STM32F4系列MCU内置有温度传感器,用于采集片内温度,其绑定在ADC1的IN18通道,CubeMX中显示为”Temperature sensor channel”。
ADC配置
ADC的结构体hadc1配置如下:
hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.ScanConvMode = DISABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion = 1; hadc1.Init.DMAContinuousRequests = DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;ClockPrescaler:预分频系数,默认为PCLK2 / 4,此例为90 / 4 = 22.5MHzResolution:ADC分辨率,可选有 12 位、10 位、8 位和 6 位。分辨率越高,转换数据精度越高,转换时间也越长;反之分辨率越低,转换数据精度越低,转换时间也越 短。ScanConvMode:是否使用扫描。多通道启用,单通道禁用。ContinuousConvMode:启用为自动连续转换,禁用为单次转换。如果设置为单次转换,那么每次转换后都要人工介入才能开启新的转换DiscontinuousConvMode:配置是否使用不连续的采样模式。该参数只有在ScanConvMode启用,且ContinuousConvMode关闭的情况下才有效ExternalTrigConv:外部触发方式选择。若为软件触发则外部触发会关闭。ExternalTrigConvEdge:外部触发极性选择(禁止触发检测、上升沿检测、下降沿检测或均可)DataAlign:数据对齐方式,默认右对齐NbrOfConversion:常规转换通道数目,范围1~16DMAContinuousRequests:指定DMA请求是否以一次性模式执行(达到转换次数时DMA传输停止)或在连续模式下执行(DMA传输无限制,无论转换数量如何)。在连续模式下执行时,DMA必须配置在循环(Circular)模式,否则DMA传输缓冲区被填满时将发生溢出。EOCSelection:指定转换结束后是否产生EOS中断或事件参数
STM32F429的ADC总转换时间可以通过下式计算:
对于每个ADC通道,采样时间可以通过ADC_SMPR1(用于通道10-18)或ADC_SMPR2(用于通道0-9)寄存器的相应位来配置。例如,通道0的采样时间是通过ADC_SMPR2的SMPR2[2:0]位设置。采样时间也可在adc.c中用代码定义,通常默认为3个ADC周期(SMP=000),可获得最快的采样速度:
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ sConfig.Channel = ADC_CHANNEL_TEMPSENSOR; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;12个周期由ADC输入时钟ADC_CLK决定,其由APB2( 即PCLK2)分频产生,在ClockPrescaler中可修改。
若采样时间为3个ADC周期,则ADC转换总时间为15个ADC周期,APB2时钟频率为90MHz,PCLK2/4分频后为22.5MHz,那么转换时间就是0.6667us。
DMA配置
启用DMA2,ADC1转换后的原始数据存储到16位缓冲区Rx_Buf中。由于ADC分辨率为12位,即采回的数据为12位,正好能够被16位缓冲区存下,便于后续观察和管理内存中的采样值。如果用32位缓冲区,一个区域会存两个采样值,不方便查看和后续的转换。
采样
ADC直接采样会存在跳变或抖动,通常需要进行一定的软件滤波,最常见的方法是简单平均法。
/* @ brief:ADC采样并取平均 @ param:times:平均次数 @ return:计算得到的平均值*/uint16_t adc1_get_average_value(uint8_t times){ uint16_t temp_value = 0; uint8_t t;
/* ADC 配置在单次模式,每次采样都需要手动重启ADC服务 */ HAL_ADC_Start_DMA(&hadc1, (uint32_t *) Rx_Buf, Rx_Buf_Size); //强制转换Rx_Buf为32位以满足形参限制
/* 采样值取平均 */ for (t = 0; t < times; t++) { temp_value = temp_value + HAL_ADC_GetValue(&hadc1); HAL_Delay(5); }
/* 返回平均值 */ return temp_value / times;}因分辨率为12位,ADC每次采样返回的数值即为12位无符号整型(十进制0~4095)。DMA将会传输每一次的采样值至缓冲区,为了能够更好的查看每一次返回的数值,该例程事先将缓冲区定义成了uint16_t类型,这样缓冲区数组的每一个单元正好能够存下一个采样值。在调用ADC采样函数时,只需要将其声明为uint32_t类型指针,这样DMA会按4个字节(32位)来传输数据。DMA本身并不关心传输的采样值到底是几位的,他只会读取4个字节的数据然后传递至内存,因此,每两个 uint16_t 数据(即 2 个采样值)会合并成一个 uint32_t,在内存中形成连续的数据块。
以此类推,如果采样值为8位(uint8_t),那么DMA每次将会打包4个采样值为一个uint32_t数值传回同样定义为uint8_t的缓冲区然后解包,缓冲区中的每个单元都显示一个采样值。
然后进行温度的读取。温度传感器换算公式为(单位为摄氏度):
式中参数取典型值,有, 采集温度:
/* @ brief:采集温度 @ param:NULL @ return:温度值(short)*/double read_temperature(void){ uint16_t adc_value; double temperature; //返回温度为双精度值
adc_value = adc1_get_average_value(10);
temperature = ((float)adc_value * 3.3 / 4096.0 - 0.76) / 0.0025 + 25.0;
return temperature;}main.c:
ic_temp = read_temperature();
uint8_t txbuf[15]; sprintf(txbuf, "temp is: %.2f\n", ic_temp); //转换为字符串
HAL_UART_Transmit(&huart1, (uint8_t *)txbuf, strlen(txbuf), 0xFFFF); //串口轮询发送温度值结果

