STM32的ADC转换仍是很强大的,它具备多个通道选择,这里我就不细说,不了解的能够自行百度,这里只是选取单通道,实现ADC转换。在文章开始以前,我说一下数据左对齐跟右对齐的差异,之前一直糊里糊涂的,记录下来以避免之后本身忘记。12位二进制最大值为 0x0FFF 左对齐操做后的结果是 0xFFF0,右对齐后仍是0x0FFF。反过来看 ,若寄存器里左对齐的数据值X (至关于实际数据*16,因此左对齐转换的值要/16才是实际的值),则X>>4才是实际的数据。而右对齐,则是数据保持不变,采集到多少就多少。至因而按左对齐保存到寄存器仍是按照右对齐,就看你的配置里如何选了。html
好了,下面就开始说明怎么用STM32CUBEMX实现ADC单通道转换吧。函数
利用中断模式ui
一、配置ADC引脚spa
二、开定时跟串口,定时器用来定时打开ADC转换,这样能够达到1S内控制ADC转换次数的目的,不过有个限制,这里样子控制ADC转换次数的话,若是采样次数多,配置ADC采样速度时必定要够 快,正常配置ADC的采样频率能够经过改变其采样速度来设置的,这里我是为了方便处理,就直接用定时器去开启了;而串口则是打印转换后的电压用的。3d
三、配置时钟code
四、配置ADC设置htm
`blog
五、开启中断模式get
六、串口配置默认便可回调函数
七、定时器配置,定时器配置的是进入定时器中断的频率,定时时间能够根据这个频率换算出来,这里定时器的频率 = 72M / 72 /1000 =1000Hz,因此定时时间为 T = 1S/f = 1S/1000 = 1ms,因此我这里配置定时为1ms。
八、基本配置咱们完成了,如今咱们生成工程用KEIL5打开
九、打开工程,咱们如今进入代码部分
这里咱们只须要重写定时器中断回调函数跟,ADC转换回调中断函数便可。在main文件里添加这下面这两个函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器中断回调 { HAL_ADC_Start_IT(&hadc1); //定时器中断里面开启ADC中断转换,1ms开启一次采集 } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) //ADC转换完成回调 { HAL_ADC_Stop_IT(&hadc1); //关闭ADC HAL_TIM_Base_Stop_IT(&htim3); //关闭定时器 AD_Value=HAL_ADC_GetValue(&hadc1); //获取ADC转换的值 Value_1=(float)(AD_Value*3.3/4096); //ADC换算,这里参考电压3.3V,12位的ADC满量程为2^12=4096,转换出来的单位是V printf("%.4f\r\n",Value_2[j-10000]); //串口打印信息 HAL_TIM_Base_Start_IT(&htim3); //开启定时器 }
到这里就完成单通道ADC中断转换的全部步骤啦,经过串口助手实测转换结果偏差为0.0008v。
至于串口查看信息打印输出重定向能够看我这篇文章:http://www.javashuo.com/article/p-xgbapsoq-kz.html
不使用中断模式
不使用中断模式的状况下跟使用中断的相似的,首先配置的过程当中不须要开启中断,至于定时器开不开看我的须要,想利用定时器定时采集的能够开,不想的不用开,其余的配置同样。生成代码后,在main文件的main函数中的while循环里添加下面代码:
/* USER CODE BEGIN 3 */ for(char n=0;n<22;n++) { //取22个值作滤波用 HAL_ADC_Start(&hadc2); HAL_ADC_PollForConversion(&hadc2, 10); //等待转换完成,第二个参数表示超时时间,单位ms if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc2), HAL_ADC_STATE_REG_EOC)) { Value[n]=HAL_ADC_GetValue(&hadc2); AD_Value += Value[n]; } } max=Value[0]; min=Value[0]; for(char n=0;n<22;n++)//取最大值、最小值 { max=(Value[n]<max)?max:Value[n]; min=(min<Value[n])?min:Value[n]; } printf("PC0 ADC : %.4f \r\n",(float)((AD_Value -max-min)/20)*(3.1/4096)); AD_tr=(float)((AD_Value -max-min)/20)*(3.1/4096); //这里我作了个去掉最大最小值后,取均值的软件滤波 AD_Value=0;
这里面的一些变量就大家本身去定义了,我就不列出来了,实测偏差在0.001v之内。
补充注意事项:
一、ADC初始化后要进行校准,使用下面函数校准,能够放在ADC初始化函数后面校准
HAL_ADCEx_Calibration_Start(&hadc2); //AD校准
二、传入ADC的电压不能够超过3.3V,就是不能够超过你的参考电压,否则结果不许,还有可能烧坏ADC引脚
使用DMA模式【转:http://www.stm32cube.com/article/37】
再次写写stm32cubemx中AD采集的问题,此次不用while里面的查询,也不用中断采样了,直接用DMA
先说下用DMA的好处:不管是中断采样仍是查询采样,都须要在主程序中占用好多时间出来,嗯,你能够这样理解
那种采样都须要调用HAL_ADC_GetValue()这个函数,,,就是要取得转换后的值,中断还好点,要是查询的话,有可能会丢失数据啊. 用dma就能够避免了
DMA用的事总线时间,无线cpu干预,额,这种说法貌似有点问题.管它呢
在AD转换结束的时候自动链接你准备存取的变量的地址,数据一步到位.额,省了多少事..
使用stm32cubemx对AD的配置
而后对她的DMA配置,并开启DMA的中断
而后生成代码吧
打开main.c文件,在这个地方添加代码
/[i] USER CODE BEGIN 0 [/i]/ __IO uint16_t uhADCxConvertedValue = 0; /[i] USER CODE END 0 [/i]/
在main()函数里添加
/[i] USER CODE BEGIN 2 [/i]/ HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&uhADCxConvertedValue, 1); /[i] USER CODE END 2 [/i]/
意思是开启dma传输,传送一个字的数据到uhADCxConvertedValue这个变量里面
而后再文件的末尾处添加
/[i] USER CODE BEGIN 4 [/i]/ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle) { /[i] Turn LED1 on: Transfer process is correct [/i]/ // BSP_LED_On(LED1); HAL_GPIO_WritePin (GPIOF,GPIO_PIN_6,GPIO_PIN_SET ); } /[i] USER CODE END 4 [/i]/
意思是AD转换完成调用这个函数,函数里使能led
也许,你会问,为毛是HAL_ADC_ConvCpltCallback()这个函数啊,这个函数不是当开启AD的中断的时候才调用的吗?
嗯,对,这个函数是这样的,可是你仔细去分析下开启AD的DMA中断函数里面,就会发现这个函数也在啊
以下图.进入HAL_ADC_Start_DMA函数里面,看到
在进入到图中的ADC_DMAConvCplt函数里面看到
OK,疑问解决,之后用到AD就能够直接调用这个CALL了,不要纠结了.