基于STM32片内信号的ADC应用演示案例

发布者:平安心境最新更新时间:2024-08-09 来源: elecfans关键字:STM32  ADC 手机看文章 扫描二维码
随时随地手机看文章

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

55e3b76c-b1ce-11ee-8b88-92fbcf53809c.png

55eed25a-b1ce-11ee-8b88-92fbcf53809c.png

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

5601ae20-b1ce-11ee-8b88-92fbcf53809c.png

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

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

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

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

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

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

56178920-b1ce-11ee-8b88-92fbcf53809c.png

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

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

56327942-b1ce-11ee-8b88-92fbcf53809c.png

再看看ADC的基本配置:

564d2ba2-b1ce-11ee-8b88-92fbcf53809c.png

LPUART的基本配置:

567b7d90-b1ce-11ee-8b88-92fbcf53809c.png

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

568bdb0e-b1ce-11ee-8b88-92fbcf53809c.png

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

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

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

569b3356-b1ce-11ee-8b88-92fbcf53809c.png

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

56b32d8a-b1ce-11ee-8b88-92fbcf53809c.png

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

55eed25a-b1ce-11ee-8b88-92fbcf53809c.png

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

56d1ca4c-b1ce-11ee-8b88-92fbcf53809c.png

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


#define TX_Timeout (9999)



#define TS_CAL1_ADDR  (0x1FFF75A8)  //用于计算温度传感器数据

#define TS_CAL2_ADDR  (0x1FFF75CA) //用于计算温度传感器数据



#define size1 (40)



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



uint16_t ts_c30,ts_c110;



uint16_t ADCResult[3],convCNT;



volatile  uint32_t Completed,EndofCon_Flag;



floatVBATVolt;//存放BBAT电压最终结果

floatVRefint;//存放Vrefint电压最终结果

floatTemperature;//存放片外温度℃最终结果



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 */

  SystemClock_Config();



/* USER CODE BEGIN SysInit */



/* USER CODE END SysInit */



/* Initialize all configured peripherals */

  MX_GPIO_Init();

  MX_DMA_Init();

  MX_ADC1_Init();

  MX_LPUART1_UART_Init();

  MX_TIM1_Init();

/* USER CODE BEGIN 2 */



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



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



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



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



  HAL_TIM_PWM_Start(&htim1,  TIM_CHANNEL_1);





/* USER CODE END 2 */



/* Infinite loop */

/* USER CODE BEGIN WHILE */

while (1)

  {

/* USER CODE END WHILE */



/* USER CODE BEGIN 3 */



if (EndofCon_Flag!=0)

  {

    VBATVolt=(ADCResult[0]/4095.)* 3.3 * 3.;  



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



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



      EndofCon_Flag=0;



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



HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,GPIO_PIN_RESET);//forauxiliarytest



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



while(Completed==0){}

      Completed =0;




//HAL_UART_Transmit(&hlpuart1, (uint8_t *)InVol ,sizeof(InVol), TX_Timeout);

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



while(Completed==0)  {}

      Completed =0;





//HAL_UART_Transmit(&hlpuart1, (uint8_t *)BatVol ,sizeof(BatVol), TX_Timeout);

      HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)BatVol ,sizeof(BatVol));


while(Completed==0){}

      Completed =0;



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



   }    



  }

/* USER CODE END 3 */

}



//ADCEOC 中断处理函数

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)

{



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



  convCNT++;



if(convCNT==3)  



{

    convCNT=0;



    EndofCon_Flag=0xff;



sprintf(WDVol,'Internal PN Temperature: %5.3f  

',Temperature);  



sprintf(InVol,'Internal Reference Volt: %5.3f  

',VRefint);  



sprintf(BatVol,'Current Battery Volt:   %5.3f  


',VBATVolt);



}



}



//UART DMA传输完成处理函数



void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

{



  Completed=0xff;

}


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

56e29926-b1ce-11ee-8b88-92fbcf53809c.png

56ef71be-b1ce-11ee-8b88-92fbcf53809c.png

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

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

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

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

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


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



HAL_UART_Transmit_DMA(&hlpuart1, (uint8_t *)BatVol ,sizeof(BatVol))


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

5704c6c2-b1ce-11ee-8b88-92fbcf53809c.png

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

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

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

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

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

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

57198de6-b1ce-11ee-8b88-92fbcf53809c.png

好,今天的分享就到这里


关键字:STM32  ADC 引用地址:基于STM32片内信号的ADC应用演示案例

上一篇:stm32头文件和源文件的作用
下一篇:基于STM32的小功率逆变器解决方案

推荐阅读最新更新时间:2024-11-04 10:26

STM32基础8--通用定时器(PWM控制LED)
从STM32基础7--通用定时器,STM32通用定时器可以产生PWM波形,从而控制LED亮度。下面我们更加详细的了解一下,PWM对LED的控制。 PWM控制LED硬件电路 在下面的原理图中,可以看到LED0与LED1连接到STMF407的PF9与PF10引脚上。在电路中,当PF9,PF10分别为低电平的时候,LED0与LED分别导通,也就是这个电路中,PWM周期的低电平周期,LED会被导通。 PWM周期与LED闪烁 在STM32F407中,当我们使用PWM1模式时,进行如下的设置。 当我们使用逻辑分析仪(LA)抓取,可以得到如波形,也就是说Pulse设置的为低电平的时间长度。当我们下载程序进开发板时,发现LED
[单片机]
<font color='red'>STM32</font>基础8--通用定时器(PWM控制LED)
stm32 CAN过滤器组
在互联型产品中, CAN1和CAN2分享28个过滤器组 其它STM32F103xx系列产品中有14个过滤器组 位宽设置 四种配置方式: 1个32位的屏蔽位模式 2个32位的标识符列表模式,可以过滤2个标识符id 2个16位的屏蔽位模式 4个16位的标识符列表模式,可以过滤4个标准标识符id 扩展标识符必须选择32位宽 标识符列表模式 在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同 CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器组0(FM1R)
[单片机]
<font color='red'>stm32</font> CAN过滤器组
STM32用STLINK烧写外置FLASH遇到的问题
由于项目需要大量的图片字库还有音频文件,所以外挂了NOR flash和NAND flash,需要用到烧写算法STLDR(就是包含几段在SRAM里面运行的代码),调试的时候遇到了几个问题,都是大意造成的,所以写出来记录一下 首先烧写用到PC端软件是STM32 STLINK Utility,在安装目录下附带了一些常用的flash的烧写算法,但没有我用到的那种,所以只能参考ST-LINK Utility UM手册在…\ST-LINK Utility\ExternalLoader目录下的工程模板上修改,修改需要用到对FLASH的初始化、读写、擦除函数,这个要提前调试好,填到对应的函数内就可以了,后面由上位机自己调用 问题来了,主要
[单片机]
STM32开发笔记5: miniUART的使用方法
单片机型号:STM32F407 本文介绍miniUART的使用方法,miniUART是一组轻量型代码,可以用于完成串口接收数据功能的快速实现。其可正确对2包数据按照时间间隔进行分割,使用简单,代码可靠。 可以按照下列步骤使用mininUART. 1、打开config.h文件,按照下图所示的内容代码进行设置,设置为1表示启用miniUART。 2、打开miniUART_config.h文件,对miniUART进行配置,如下图所示。MINIUART_PERIOD是指两包数据之间的最小时间间隔,说的通俗一些如果串口接收到的2个字节之间的时间间隔小于MINIUART_PERIOD的数值,则认为该2个字节
[单片机]
<font color='red'>STM32</font>开发笔记5: miniUART的使用方法
STM32再学习——时钟初始化
STM32F系列微处理器,或者说是Cortex-M3内核的MCU内,都集成了一个叫PLL的东西。PLL就是锁相回路或锁相环(PhaseLockedLoop),用来统一整合时脉讯号,使内存能正确的存取资料。PLL用于振荡器中的反馈技术,将外部的输入信号与内部的振荡信号同步,锁相环路的基本方框图如下图所示。一句话,PLL用来控制STM32F的时钟频率的。总而言之,STM32F系列MCU使用了这个东西,而我们在MCU上电之后,也就要对其正确的初始化,这样,我们才能得到我们需要的时钟配置。 本文引用地址:http://www.eepw.com.cn/article/182408.htm 在ST公司的外设固件库的示例里,对于工
[单片机]
<font color='red'>STM32</font>再学习——时钟初始化
基于STM32和CPLD的等精度测频设计
在电子工程、资源勘探、仪器仪表等相关应用中,频率测量是电子测量技术中最基本最常见的测量之一,频率计也是工程技术人员必不可少的测量工具。但是,传统的频率测量方法在实际应用中有较大的局限性,基于传统测频原理的频率计的测量精度将随被测信号频率的变化而变化,传统的直接测频法其测量精度将随被测信号频率的降低而降低,测周法的测量精度将随被测信号频率的升高而降低。本文中提出一种基于ARM与CPLD宽频带的数字频率计的设计,以微控器STM32作为核心控制芯片,利用CPLD可编程逻辑器件,实现闸门测量技术的等精度测频。 本设计的技术指标: 测频范围:1Hz~200MHz,分辨率为0.1Hz,测频相对误差百万分之一。 周期测量:信号测量范围
[工业控制]
基于<font color='red'>STM32</font>和CPLD的等精度测频设计
STM32 FSMC LCD 液晶的驱动—ILI9320
原来老早知道 STM32 具有 带4个片选的静态存储器控制器。支持CF卡、SRAM、PSRAM、NOR和NAND存储器 并行LCD接口,兼容8080/6800模式 这个其实就是FSMC 在这之前我一直使用IO口模拟8080时序感觉操作简单速度也很不错,而且ST官方上的FSMC的说明文档看得实在很晕找不到重点一直没试过FSMC。最近有机会尝试驱动驱动一块2.4的ILI9320由于要接线为了省力气直接使用了 FSMC的接法,顺便整理下写点东西出来。 我想使用12864液晶可能是每个会单片机的基本功了通常用个P0口发送8Bit数据在用一些控制线产生时钟信号,12864使用6800通信方式而小的彩色FTF 或CSTN屏流行8080通信
[单片机]
stm32 CAN总线例子
利用stm32实现了1个简单的CAN功能,使用了队列缓存 can.c 文件 #include includes.h #define GPIO_CAN GPIOB #define RCC_APB2Periph_GPIO_CAN RCC_APB2Periph_GPIOB #define GPIO_Pin_RX GPIO_Pin_8 #define GPIO_Pin_TX GPIO_Pin_9 #define GPIO_Remap_CAN GPIO_Remap1_CAN1 #define MAX_MAIL_NUM 3 static u8 CAN_msg_num ; //
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved