前言
在第十一章中,我们采集3个通道,每次采集完一个通道以后,都要在中断回调函数中读取采样结果,存在数组中,然后对数组序号加1,继续调用HAL_ADC_Start_IT(&hadc1);启动下一次采集,三个通道采集结束后,对数组序号清零。采用中断方式,只是减少了CPU等待ADC转换的时间,但是在中断回调数仍然需要读取数据,对数组序号处理,重启启动ADC转换。这些任务仍然会频繁占用CPU的时间。
本章我们采用DMA方式,ADC转换结束以后,DMA会自动将数据依次保存到数组中,而无需CPU的介入。三个通道采样结束以后,中断回调CPU直接将数组中的数据转化为实际的物理电压和温度,并打印出来。
一、功能
我们在上个工程的基础上,通过PY32Studio可视化界面,修改配置,实现芯片内部温度传感器(Temperature)、内部参考电压(Vrefint)、1/3的VCC的单次扫描ADC采样,无需外接任何硬件电路,最后通过串口printf实时打印芯片内部结温、内部参考电压、1/3的VCC。
本练习采用单次扫描转换模式,DMA,非连续。
二、硬件
通过USB转TTL模块连接PC与Startkit板, Startkit板与USB转TTL模块的连线方式如下,通过串口将信息发送到串口助手。
Startkit 开发板 | USB转TTL模块 |
Tx(PA2) | Rx |
Rx(PA3) | Tx |
GND | GND |
三、PY32Studio配置
3.1 打开上个练习中创建的工程F032_ADC_DMA
3.2 配置时钟参数,采用HSI, 主频设置为72MHz
3.3 在芯片视图中,选择PA2为UART1_Tx, 选择PA3为UART1_Rx
3.4 配置UART参数
3.4 配置ADC的参数
第一步:选择ADC的三个内部通道:
第二步:配置ADC的参数:
- Scan Conversion Mode:Enable(打开扫描模式,单次多通道采样)
- Continuous Conversion Mode:Disable(关闭连续转换,单次触发单次采样)
- Discontinuous Conversion Mode:Disable
- End of Conversion Selection: End of sequence of conversion
第三步:配置ADC的常规转换模式(Regular conversion mode)和三个通道的转换顺序:
第四步:配置注入式模式和ADC看门狗,如上图所示;
第五步:配置ADC的中断
第六步:配置ADC的DMA参数
第七步:点击生成工程
四、编写应用代码
4.1 应用代码
在PY32Studio生成的代码的基础上,添加以下代码:
在uart.c文件中,重写fputc函数,代码放在USER CODE BEGIN ExternalFunctions之后:
/* USER CODE BEGIN ExternalFunctions */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000); //阻塞式发送,超时时间设为1000(1s) return ch; } /* USER CODE END ExternalFunctions */在main.c文件中,加入下面代码,代码要放在USER CODE BEGIN PV之后:
/* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ uint32_t adcValue[3]; float VCCA3 = 0.0; float Vrefint = 0.0; uint32_t dTemperature = 0; uint8_t adcCompleteFlag = 0; //转换完成标志位,1:转换完成,0:转换未完成 /* USER CODE END PV */在main.c文件中,重写ADC转换完成的回调函数,回调函数放在USER CODE BEGIN 4之后:
/* USER CODE BEGIN 4 */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if(hadc->Instance==ADC1) { adcCompleteFlag = 1; //将ADC转换完成标志位置位,三个通道采样完成 } } /* USER CODE END 4 */在main.c文件中,修改main()函数
- 在USER CODE BEGIN 2之后,将adcCompleteFlag标志位置位;
- 在while(1)循环中,增加以下代码:
- 判断ADC转换完成标志位,如果完成:
- 将转换的结果转化为Vrefint,VCCA/3,温度值;
- 打印转换结果;
- 启动ADC转换,采用DMA方式;
- 将adcCompleteFlag标志位清零。
- 判断ADC转换完成标志位,如果完成:
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ Studio_RCC_Init(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ Studio_GPIO_Init(); Studio_DMA_Init(); Studio_UART1_Init(); Studio_ADC1_Init(); /* USER CODE BEGIN 2 */ adcCompleteFlag = 1; //将adcCompleteFlag标志位置位; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while(1) { if(adcCompleteFlag == 1) { Vrefint = adcValue[0] * 0.0008; //计算VVCA/3的电压值 = adcValue * VCCA/4096 printf("Vrefint is %.3f V. \r\n",Vrefint); VCCA3 = adcValue[1] * 0.0008; //计算Vrefint的电压值 = adcValue * VCCA/4096 printf("VCCA/3 is %.3f V. \r\n",VCCA3); dTemperature = __HAL_ADC_CALC_TEMPERATURE(TEMPSENSOR_CAL_VREFANALOG, adcValue[2], ADC_RESOLUTION_12B); //通过测量的电压计算温度 printf("Temperature is %d C.\r\n",(int)dTemperature); HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcValue, 3); //再次启动ADC采样 adcCompleteFlag = 0; //ADC转换完成标志位清零 } HAL_Delay(3000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }4.2 设置
在选项卡中,选择安装的编译器版本和Debug类型。
4.3 编译,下载
- 点击编译图标,开始编译程序;
- 编译成功后,开始下载程序;
- 下载成功后,开始调试程序;
4.4调试
点击调试图标,进入调试界面,运行程序,把adcValue、VCCA3、Vrefint、dTemperature四个变量加到Watch1窗口中。在View菜单中,选择Periodic Window Update命令,全速运行代码,观察这几个变量的值。
打开串口助手,选择正确的端口名,波特率,数据位,校验位和停止位,打开串口,开始接收数据。右面的窗口可以显示出从MCU发送过来的数据。
五、总结
本章内容,我们采用单次扫描采样的DMA方式,对ADC1内部的3个通道进行轮流采样,计算相应的电压值或温度值,并轮流打印计算结果。整个过程主要步骤如下:
- 通过PY32Studio配置时钟,UART1, ADC1模块;
- 在uart.c文件中,包括"stdio.h"头文件,重写fputc函数;
- 在main.c文件中,定义全局变量并且初始化: adcValue、VCCA3、Vrefint、dTemperature, adcCompleteFlag;
- 在while(1)循环中,判断ADC转换是否完成,如果完成:
- 计算温度值或电压值;
- 打印温度值或者电压值
printf("VCCA/3 is %.3f V. \r\n",VCCA3); - 再次启动ADC+DMA转换;
- 将转换完成标志位清零
- 在main.c文件中,重写adc转换完成回调函数,在回调函数中,将转换完成标志位置位;
- 调试代码,在串口助手中观察打印出的变量的值。