作为一个倾向工控的芯片,ADC采样是一个十分重要的外设。STM32集成三个12位精度18通道的内部ADC,最高速度1微秒,结合DMA能够解放CPU进行更好的处理。
ADC接口上的其它逻辑功用包含:
●同步的采样和坚持
●穿插的采样和坚持
●单次采样
模仿看门狗功用答应十分精准地监督一路、多路或一切选中的通道,当被监督的信号超出预置的阀值时,将发生中止。
由规范定时器(TIMx)和高档操控定时器(TIM1和TIM8)发生的事情,能够别离内部级联到ADC的开端触发和注入触发,应用程序能使AD转化与时钟同步。
12位ADC是一种逐次迫临型模仿数字数字转化器。它有多达18个通道,可测量16个外部和2个内部信号源。
ADC的输入时钟不得超越14MHZ,它是由PCLK2经分频发生。
假如被ADC转化的模仿电压低于低阀值或高于高阀值,AWD模仿看门狗状况位被设置。
关于ADC采样与DMA联系,引证网上一段解说:
STM32 的长处在哪里?
除掉宣扬环节,细细剖析。
STM32 时钟不算快,72MHZ,
也不能扩展大容量的RAM FLASH,
相同没有 DSP 那样强壮的指令集。
它的优势在哪里呢?
—就在快速收集数据,快速处理上。
ARM 的特征便是便利。
这个快速收集,高性能的ADC 便是一个很好的表现,
12 位精度,最快1uS 的转化速度,一般具有2 个以上独立的ADC 操控器,
这意味着,
STM32 能够一起对多个模仿量进行快速收集,
这个特性不是一般的MCU具有的。
以上高性能的 ADC,合作相对比较块的指令集和一些特征的算法支撑,
就构成了STM32 在电机操控上的强壮特性。
好了,正题,怎末做一个简略的ADC,留意是简略的,
ADC 是个杂乱的问题,触及硬件规划,电源质量,参阅电压,信号预处理等等问题。
咱们只就如安在MCU内完结一次ADC 作评论。
谈到 ADC,咱们还要第一次引进别的一个重要的设备DMA.
DMA是什么东西呢。
一般在 8 位单片机年代,很少有这个概念。
在外置资源越来越多今后,
咱们把一个MCU内部分为主处理器和 外设两个部分。
主处理器当然是履行咱们指令的首要部分,
外设则是串口 I2C ADC 等等用来完结特定功用的设备
回想一下,8 位年代,咱们的主处理器最常干的事情是什么?
逻辑判别?不是。那才几个指令
核算算法?不是。大部分时分算法都很简略。
事实上,主处理器便是作个转移工,
把 USART 的数据接纳下来,存起来
把 ADC 的数据接纳下来,存起来
把要发送的数据,存起来,一个个的往USART 里放。
…………
为了处理这个对立,
人们想到一个方法,让外设和内存间树立一个通道,
在主处理器答应下,
让外设和内存直接读写,这样就释放了主处理器,
这个东西便是DMA。
打个比方:
一个MCU是个公司。
老板便是主处理器
职工是外设
库房便是内存
早年 库房的东西都是老板管的。
职工需求质料作业,就一个个报给老板,老板去库房里一个一个拿。
职作业好的东西,一个个给老板,老板一个个放进库房里。
老板很累,尽管老板是超人,也受不了越来越多的职工和单子。
最终老板雇了一个库房保管员,它便是DMA
他专门担任入库和出库,
只需求把出库和入库方案给老板过目
老板说 OK,就不管了。
后边的入库和出库进程,
职工只需求和这个库房保管员打交道就能够了。
——–闲话,马七时常想,让设备与设备之间开DMA,岂不更牛X
比方完结。
ADC 是个高速设备,前面说到。
并且 ADC 收集到的数据是不能直接用的。即便你再当心的规划外围电路,测的离谱的数据总会呈现。
那么一般来说,是收集一批数据,然后进行处理,这个进程便是软件滤波。
DMA用到这儿就很适宜。让ADC 高速收集,把数据填充到RAM 中,填充必定数量,比方32 个,64 个MCU再来运用。
—–多一句,也能够说,单次ADC 毫无意义。
下面咱们来详细介绍,怎么运用DMA来进行ADC 操作。
初始化函数包含两部分,DMA 初始化和ADC 初始化
咱们有多个管理员–DMA
一个管理员当然不止管一个DMA 操作。所以DMA有多个Channel
以下是程序剖析:
程序依据STM32F103VET6,库函数完结
RCC部分:(疏忽体系时钟装备)
//发动DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//发动ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO部分:(ADC引脚拜见上表)
//ADC_CH10–> PC0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模仿输入
GPIO_Init(GPIOC, &GPIO_InitStructure);
// PC2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC1装备:(两外部输入,另采样内部温度传感器)
void ADC1_Configuration(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //转化形式为独立,还有穿插等十分多样的挑选
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//接连转化敞开
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 3; //设置转化序列长度为3,三通道
ADC_Init(ADC1, &ADC_InitStructure);
//ADC内置温度传感器使能(要运用片内温度传感器,切忌要敞开它)
ADC_TempSensorVrefintCmd(ENABLE);
//惯例转化序列1:通道10
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
//惯例转化序列2:通道16(内部温度传感器),采样时刻>2.2us,(239cycles)
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 3, ADC_SampleTime_239Cycles5);
//输入参数:ADC外设,ADC通道,转化序列次序,采样时刻
// Enable ADC1
ADC_Cmd(ADC1, ENABLE);
// 敞开ADC的DMA支撑(要完结DMA功用,还需独立装备DMA通道等参数)
ADC_DMACmd(ADC1, ENABLE);
// 下面是ADC主动校准,开机后需履行一次,确保精度
// Enable ADC1 reset calibaration register
ADC_ResetCalibration(ADC1);
// Check the end of ADC1 reset calibration register
while(ADC_GetResetCalibrationStatus(ADC1));
// Start ADC1 calibaration
ADC_StartCalibration(ADC1);
// Check the end of ADC1 calibration
while(ADC_GetCalibrationStatus(ADC1));
// ADC主动校准完毕—————
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //ADC发动
}
DMA装备:(无软件滤波)
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //DMA外设地址,在头部界说
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设至内存形式
//BufferSize=2,由于ADC转化序列有2个通道
//如此设置,使序列1成果放在AD_Value[0],序列2成果放在AD_Value[1]
DMA_InitStructure.DMA_BufferSize = 3; //一次转化三个
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //承受一次后,设备地址不后移
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //承受一次后,内存地址后移
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //每次传输半字
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
//循环形式敞开,Buffer写满后,主动回到初始地址开端传输
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
//装备完结后,发动DMA通道
DMA_Cmd(DMA1_Channel1, ENABLE);
}
此DMA例程用于单次ADC转化,合作软件滤波可做如下改动:
大局声明:
vu16 AD_Value[30][3]; //AD采样值
vu16 After_filter[3]; //AD滤波后
DMA部分:(带中止滤波)
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//BufferSize=2,由于ADC转化序列有2个通道
//如此设置,使序列1成果放在AD_Value[0],序列2成果放在AD_Value[1]
DMA_InitStructure.DMA_BufferSize = 90;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
//循环形式敞开,Buffer写满后,主动回到初始地址开端传输
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
//装备完结后,发动DMA通道
DMA_Cmd(DMA1_Channel1, ENABLE);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //使能DMA传输完结中止
}
NVIC部分:
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); // Enable the DMA Interrupt
stm32f10x_it.c文件:
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)
{
filter();
DMA_ClearITPendingBit(DMA1_IT_TC1);
}
}
滤波部分:(均值滤波)
#define N 30
void filter(void)
{
intsum = 0;
u8 count,i;
for(i=0;i<2;i++)
{
for ( count=0;count
sum += AD_Value[count][i];
}
After_filter[i]=sum/N;
sum=0;
}
}
采样数据与实践电压/温度转化:
u16 GetTemp(u16 advalue)
{
u32 Vtemp_sensor;
s32 Current_Temp;
// ADC转化完毕今后,读取ADC_DR寄存器中的成果,转化温度值核算公式如下:
// V25 – VSENSE
//T(℃) = ————+ 25
// Avg_Slope
// V25:温度传感器在25℃时 的输出电压,典型值1.43 V。
//VSENSE:温度传感器的当时输出电压,与ADC_DR 寄存器中的成果ADC_ConvertedValue之间的转化联系为:
// ADC_ConvertedValue * Vdd
//VSENSE = ————————–
// Vdd_convert_value(0xFFF)
//Avg_Slope:温度传感器输出电压和温度的相关参数,典型值4.3 mV/℃。
Vtemp_sensor = advalue * 330 / 4096;
Current_Temp = (s32)(143 – Vtemp_sensor)*10000/43 + 2500;
return (s16)Current_Temp;
}
u16 GetVolt(u16 advalue)
{
return (u16)(advalue * 330 / 4096);
}
滤波部分思路为:ADC正常接连采样三个通道,由DMA进行转移,一次转移90个数据,即为1-2-3-1-2-3循环,每个通道各30次,存在 AD_Value[30][3]中,30为每通道30个数据,3为三个通道,依据二维数组存储方法此进程主动完结。而每逢一次DMA进程完毕后,触发 DMA完结中止,进入滤波函数将30个数据均值成一个, 存入After_filter[3]。整个进程滤波核算需求CPU参加,而在程序中采样成果值随时均为最新,极力处理程序杂乱性和CPU负载。 x=GetVolt(After_filter[0]);即可得到即时电压值。