你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32的ADC片内信号经验分享

[复制链接]
攻城狮Melo 发布时间:2024-1-18 19:00
微信图片_20240118190031.png



很多STM32芯片里往往内置了专用的ADC通道,比方用来测量Vrefint,VBAT的分压或温度传感器的输出电压信号。不同系列所内置的模拟信号通道可能有差异。这里以STM32G4系列为例,它内置了对应于Vrefint,VBAT的三分之一分压和温度传感器的输出电压的专用模拟通道。

微信图片_20240118190028.png


微信图片_20240118185948.png


下面的示例就是针对上述3个通道进行ADC,并测量相关电压和片内温度,最终得到3个结果,分别是VRefint电压,VBAT的电压,片内温度。

微信图片_20240118190017.png


实现过程是这样的,大体分四步:【有点点麻雀虽小五脏俱全的味道图片】

1、TIMER1 更新事件触发ADC的转换;

2、CPU基于EOC中断获取ADC结果;

3、对ADC结果进行换算,得到电压值和温度值存放在特定内存位置;

4、基于DMA传输通过UART将最终结果在串口终端显示;

其中,TIMER1的CH1输出PWM波形,其更新事件做ADC的转换启动信号。每次的TIMER更新事件触发ADC,3个通道扫描方式转换。这里的UART使用片内LPUART,使用它主要是考虑它跟板载虚拟串口直接相连,没有其它特别用意。

微信图片_20240118190012.png


我使用STM32G474Nucleo板来进行下面实验。其中VDD=3.3v,VBAT与VDD相连。另外,ADC模块的参考电压也是3.3v.

使用CubeMx图形化工具进行配置,先看TIMER配置:

微信图片_20240118190009.png


再看看ADC的基本配置:

微信图片_20240118190005.png


LPUART的基本配置:

微信图片_20240118190002.png


因为要使用ADC中断和UART的DMA传输,记得做ADC的中断响应使能配置和LPUART的DMA配置,这里只使用UART的TX DMA功能。

微信图片_20240118185959.png


使用CubeMx主要配置主要是上面这些。

在组织用户代码前,先简单介绍下片内温度传感器的内容。该温度传感器针对不同温度有不同电压输出,其输出电压跟温度呈线性关系。ST公司针对片内温度传感器在两个特定温度【30℃和110℃或30℃和130℃】、基于特定参考电压【3v或3.3v,不同系列以数据手册为准】生成了1组校准值并存放于片内特定FLASH位置。

STM32G4系列的校准值是在参考电压为3v,30℃和110℃条件下的两个值,在数据手册里还给出了校准值的片内存放地址。

微信图片_20240118185956.png


针对这个温度传感器的使用,ST公司在参考手册里还给出了计算公式。其实,有无这个公式无所谓,我们不难自行推理出来。【TS_DATA代表某时刻测得的传感器输出电压对应的转换值,TS_CAL1/TS_CAL2分别表示在30℃和110℃条件下基于传感器输出电压的转换值。】

微信图片_20240118185953.png


另外,前面提过,ST公司在手册里给出了温度传感器的两个温度下的校准值,但要注意生成校准值的ADC模块所用参考电压跟我们实际应用时AD模块所用的参考基准电压可能不一致。如果不一致,就必须将ADC值换算成同一基准参考电压条件下的数据。目前在ST手册里也特别强调这点了。我把上面一副图再贴一遍于此【见黄色语句提醒】。



关于这点,我们也不难理解。同一待测信号、同一ADC模块在不同基准参考电压下转换值往往是不一样的。见下面示意图加以理解。

微信图片_20240118185944.png


完成各项配置后,创建软件工程。添加必需的用户代码:

  1. #define TX_Timeout (9999)

  2. #define TS_CAL1_ADDR  (0x1FFF75A8)  //用于计算温度传感器数据
  3. #define TS_CAL2_ADDR  (0x1FFF75CA) //用于计算温度传感器数据

  4. #define size1 (40)

  5. char WDVol[size1],BatVol[size1],InVol[size1];

  6. uint16_t ts_c30,ts_c110;

  7. uint16_t ADCResult[3],convCNT;

  8. volatile  uint32_t Completed,EndofCon_Flag;

  9. float    VBATVolt;//存放BBAT电压最终结果
  10. float    VRefint;  //存放Vrefint电压最终结果
  11. float    Temperature;//存放片内温度℃最终结果

  12. int main(void)
  13. {
  14. /* USER CODE BEGIN 1 */

  15. /* USER CODE END 1 */

  16. /* MCU Configuration--------------------------------------------------------*/

  17. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  18.   HAL_Init();

  19. /* USER CODE BEGIN Init */

  20. /* USER CODE END Init */

  21. /* Configure the system clock */
  22.   SystemClock_Config();

  23. /* USER CODE BEGIN SysInit */

  24. /* USER CODE END SysInit */

  25. /* Initialize all configured peripherals */
  26.   MX_GPIO_Init();
  27.   MX_DMA_Init();
  28.   MX_ADC1_Init();
  29.   MX_LPUART1_UART_Init();
  30.   MX_TIM1_Init();
  31. /* USER CODE BEGIN 2 */

  32.    ts_c30 = *(uint16_t *)(TS_CAL1_ADDR); //读取30℃时的ADC校准值

  33.    ts_c110 =  *(uint16_t *)(TS_CAL2_ADDR);//读取110℃时的ADC校准值

  34.   HAL_ADCEx_Calibration_Start(&hadc1 , ADC_SINGLE_ENDED);//ADC校准

  35.   HAL_ADC_Start_IT(&hadc1);//启动ADC并开启转换中断

  36.   HAL_TIM_PWM_Start(&htim1,  TIM_CHANNEL_1);


  37. /* USER CODE END 2 */

  38. /* Infinite loop */
  39. /* USER CODE BEGIN WHILE */
  40. while (1)
  41.   {
  42. /* USER CODE END WHILE */

  43. /* USER CODE BEGIN 3 */

  44. if (EndofCon_Flag!=0)
  45.   {
  46.     VBATVolt=(ADCResult[0]/4095.)* 3.3 * 3.;  

  47.     VRefint=(ADCResult[1]/4095.) * 3.3;  

  48.     Temperature = 30.+ (88.*(ADCResult[2]-((ts_c30/1.1))))/(ts_c110 - ts_c30);

  49.       EndofCon_Flag=0;

  50. //HAL_UART_Transmit(&hlpuart1, (uint8_t *)WDVol ,sizeof(WDVol), TX_Timeout);

  51.       HAL_GPIO_WritePin( GPIOC,GPIO_PIN_3,GPIO_PIN_RESET);  //for auxiliary test

  52.       HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)WDVol ,sizeof(WDVol));

  53. while(Completed==0){}
  54.       Completed =0;


  55. //HAL_UART_Transmit(&hlpuart1, (uint8_t *)InVol ,sizeof(InVol), TX_Timeout);
  56.       HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)InVol ,sizeof(InVol));

  57. while(Completed==0)  {}
  58.       Completed =0;


  59. //HAL_UART_Transmit(&hlpuart1, (uint8_t *)BatVol ,sizeof(BatVol), TX_Timeout);
  60.       HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)BatVol ,sizeof(BatVol));

  61. while(Completed==0){}
  62.       Completed =0;

  63.       HAL_GPIO_WritePin( GPIOC,GPIO_PIN_3,GPIO_PIN_SET);  //for auxiliary test

  64.    }   

  65.   }
  66. /* USER CODE END 3 */
  67. }

  68. //ADC EOC 中断回调处理函数
  69. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
  70. {

  71.   ADCResult[convCNT]=HAL_ADC_GetValue(&hadc1); //获取转换结果并存入数组

  72.   convCNT++;

  73. if(convCNT==3)  

  74. {
  75.     convCNT=0;

  76.     EndofCon_Flag=0xff;

  77. sprintf(WDVol,"Internal PN Temperature: %5.3f  \r\n",Temperature);  

  78. sprintf(InVol,"Internal Reference Volt: %5.3f  \r\n",VRefint);  

  79. sprintf(BatVol,"Current Battery Volt:   %5.3f  \r\n\r\n",VBATVolt);

  80. }

  81. }

  82. //UART DMA 传输完成中断回调函数

  83. void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
  84. {

  85.   Completed=0xff;
  86. }
复制代码

基于上面的配置和测试代码,我们就可以看到最终的结果了。定时器周期性地触发ADC,每得到3个ADC结果就进行数据处理,然后通过UART以DMA方式传输到串口终端。注意VBat电压是测量结果再乘以3得到的。

微信图片_20240118185941.png


微信图片_20240118185938.png


针对上面的应用演示,最后给几点相关应用提醒:

1、针对温度传感器做测量时,校准时使用的参考电压与实际应用不一致时要做换算,换算成相同参考电压的数据后再做计算。这点前面也提过了。

2、使用TIMER的TRGO触发ADC,如果选择类似比较事件、更新事件来触发ADC时,此时ADC对触发极性的选择是无效的,或者说ADC的转换仅依赖于触发事件时间点。如果是选择TIMER的Ocref信号作为触发源,此时ADC的硬件触发的极性选择是有效的,可以是上沿或下沿触发,甚至是双沿触发。这时就得根据需要选择合适的触发沿。【可以进一步阅读本公众号文章《STM32定时器触发ADC的时序话题》】

3、这里使用UART的DMA传输依次显示三个结果于串口终端,三个启动UART DMA传输的函数须保留适当时间间隔,即等上次传输完成后再启动下一次传输,因为这里每次传输使用的是同一DMA通道。否则没法全部正常输出。比如若把上面3次UART DMA传输的代码改成下面这样子:

  1. HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)WDVol ,sizeof(WDVol));

  2. HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)InVol ,sizeof(InVol));

  3.   HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)BatVol ,sizeof(BatVol))
复制代码

这时输出结果会变成下面的情形,总是只能看到一个结果的输出,即第一次启动的DMA传输结果。



如果想省事点,直接在相邻2次DMA传输间加上合适延时也行。我这里根据DMA传输完成事件来决定执行下一次发送。在DMA传输完成中断里设置Completed变量为非0值表示当前一轮DMA传输完成。

4、对于那些在中断和主程序里都会被访问的变量,记得将它们冠以volatile。

下图的三路波形是我调试时辅助使用的。

第一路表示计数器的计数变化,显然是单向向上计数模式。

第二路是TIMER1通道1的PWM输出波形。

第三路是我每次基于DMA实现UART发送时拉高拉低的波形。平常管脚电平为高,在实现DMA传输过程中拉低。

微信图片_20240118185918.png


如有侵权请联系删除
转载自: 茶话MCU



微信图片_20240118185921.png
收藏 评论0 发布时间:2024-1-18 19:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版