【原创】STM32U5系列与STM32G4系列MCU内部温度传感器精准读取指南

1. 前言

        这段时间在使用STM32U5系列的单片机,想通过内部的ADC来获取单片机内部的温度,在网上搜了一圈,发现大多是STM32F1、F4系列的温度读取教程,STM32U5系类的和STM32G4系列的方法较少,有大致的介绍但不全面,因此综合现有的教程,写一个详细一点的教程来实现正确温度值的获取。

2. 读取方式

2.1 手册获取

        通过查询STM32U5系列或STM32G4系列的参考手册(Reference Manuel)可以找到详细的读取流程,此处使用的是STM32U585,查询STM32U5的手册RM0456:

        手册可以直接百度或者ST官网搜RM0456找到,或者从CubeMX建立好工程后,从Help -> Docs选项卡中找到这个芯片相关的所有文档:

        点开即可找到手册位置

2.2 查看温度获取流程

        手册33.4.33中详细介绍了温度的获取流程

        读取的步骤如下:

        翻译总结如下:

1. 选择温度传感器对应的ADC通道(并设置合适的采样时间)

2. 配置合适的采样时间

3. 设置ADC12_CCR寄存的VSENSESEL位,将温度传感器从power down模式唤醒

4. 启动ADC转换

5. 待转换完毕,读取ADC转换结果

6. 根据公式计算真实温度值

        转换公式如下:

转换公式中我们需要从MCU内部地址获取两个温度下的校准值,即TS_CAL1和TS_CAL2,再代入公式计算。

        在表中可以找到两个校准值变量的对应的存储位置,表中我们可以看到两个校准值给的条件是14-bit的ADC模式下且参考电压为3V的条件下计算出的校准值,因此要得到准确的温度值需要根据自己ADC的采样配置(位数)和参考电压值来换算:

        简单解释一下这两个参数值,两个系数值都为16bit,TS_CAL1为使用14bit的ADC在参考电压为3V的情况下,外部温度为30°的时候获取的校准系数;TS_CAL2即为同样条件下温度为130°时候的校准值。

3. CubeMX配置ADC

        为了简明扼要就不一步一步的选单片机,配置时钟树这些了,直接配置好一个可以正常运行的CubeMX工程后,打开CubeMX开始配置ADC。

        这里选择ADC1,其他通道根据需要配置,主要是选择这个②处的温度传感器通道,勾选后进行参数设置:

        勾选完成后即可生成工程,生成的工程参数如下,不需要额外的更改:

void MX_ADC1_Init(void)
{
  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.GainCompensation = 0;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
  /* USER CODE END ADC1_Init 2 */
}

4. 代码编写

        完成基本配置后我们需要找到温度传感器对应的ADC通道,从手册中我们可以找到STM32U5的Vsense对应的通道号为19

        不过我们也可以通过HAL库中的宏定义来直接读取,而不关心具体的通道号:

        我们配置的ADC为单次转换,因此我们定义一个函数来方便读取任一通道的ADC值,函数中的ADC配置可以放在ADC1的初始化中,但为了方便切换单次读取通道,我将其放在读取函数中。其中打开CCR寄存器部分可以要也可以不要,测试发现打不打开都可以正确读取到温度值,可能是给MCU休眠后唤醒使用?暂不清楚,若有清楚的可以评论区讲解下,代码如下:

static uint32_t adc_value_get(uint32_t iChan)
{
  #define ADC_OBJ &hadc1
  
  ADC_ChannelConfTypeDef sConfig = {
  .Channel      = iChan,
  .Rank         = ADC_REGULAR_RANK_1,
  .SamplingTime = ADC_SAMPLETIME_814CYCLES,
  .SingleDiff   = ADC_SINGLE_ENDED,
  .OffsetNumber = ADC_OFFSET_NONE,
  .Offset       = 0
  };	

  if (HAL_ADC_ConfigChannel(ADC_OBJ, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }	
   
  // 打开CCR寄存器唤醒温度传感器
  LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_TEMPSENSOR);
  
  if (HAL_ADC_Start(ADC_OBJ) != HAL_OK)
  {
    /* ADC conversion start error */
    Error_Handler();
  }
  
  // adc timeout set 10ms
  if (HAL_ADC_PollForConversion(ADC_OBJ, 10) != HAL_OK)
  {
    /* ADC conversion start error */
	Error_Handler();
  }

  if (HAL_ADC_Stop(ADC_OBJ) != HAL_OK)
  {
  	/* ADC conversion stop error */
  	Error_Handler();
  } 	
	
  return HAL_ADC_GetValue(ADC_OBJ);
}

        定义好这个读取函数后我们在主函数的循环前定义一些必要的变量,并在使用ADC前对其进行一次校准,两个校准系数为16bit的值因此直接从地址中读取即可,但我使用的评估板是3.3V的参考电压,因此需要进行一下转换,若你使用的参考电压值是其他值,将3.3改成对应值即可,但要注意不要删除浮点数后的 [ f ] ,不然最后计算出来的值会和整数除法混和导致最终的结果不对,代码如下:

  /* 定义两个校准系数的存储地址和校准系数的对应温度值 */
  #define TS_CAL1_ADDR (0x0BFA0710)      
  #define TS_CAL2_ADDR (0x0BFA0742)
  #define TS_CAL1_TEMP (30.0f)           // 30 TEMP
  #define TS_CAL2_TEMP (130.0f)          // 130 TEMP
  
  uint32_t ADC_Value = 0;
  float ftemp, TS_CAL[2];

  /* 从地址中读取真实的校准系数 */
  TS_CAL[0] = (float)*(__IO uint16_t *)TS_CAL1_ADDR;
  TS_CAL[1] = (float)*(__IO uint16_t *)TS_CAL2_ADDR;
 
  /* 转换校准系数为3.3V的条件下的值 */
  TS_CAL[0] = TS_CAL[0] * 3.0f / 3.3f;
  TS_CAL[1] = TS_CAL[1] * 3.0f / 3.3f;

  /* 校准ADC1 */
  if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK)
  {
    /* Calibration Error */
    Error_Handler();
  }

        完成设置后即可在主循环中调用函数,直接读取ADC_CHANNEL_TEMPSENSOR通道的数据,也可以读取通道19,并根据公式计算处对应的温度值,验证温度数据。此处有一个注意点,我们使用的ADC1配置的是12bit,而校准系数给的是14bit,因此需要将12bit的采样值转换成14bit(左移两位),算出来的结果才对,若配置成其他的值对应转换即可,代码如下:

while (1)
{
    // 获取温度采样原始值
    ADC_Value = adc_value_get(ADC_CHANNEL_TEMPSENSOR);
    ADC_Value <<= 2;    // 将12bit ADC采样值转换成14bit

    // 根据公式计算温度值
    ftemp = (TS_CAL2_TEMP - TS_CAL1_TEMP)/(TS_CAL[1] - TS_CAL[0]) * (ADC_Value - TS_CAL[0]) + TS_CAL1_TEMP;
    
    // 分别打印原始值 电压值 和温度值
    LOG_INFO("ADC:%d  Vol:%.4f  Temp:%.4f \n", ADC_Value, ADC_Value*3.3f/(16384-1), ftemp);
    HAL_Delay(500);
}

        最终的打印采样结果如下:

5. 总结

总结下来,获取准确的温度值的注意点如下:

  1. 1. 选择正确的通道,若不知道直接使用 ADC_CHANNEL_TEMPSENSOR
  2. 2. 转换参考电压值
  3. 3. 转换自己adc的位数为14bit,或者将校准系数转换为adc对应的位数即可
  4. 4. 浮点除法和整数除法不可混淆

        其中这一系列的MCU(如STM32G474)的温度计算方式都可以使用同样的流程,其区别仅在于校准系数的地址不同和温度传感器对应的ADC通道号不同。

        内容若有纰漏,评论区欢迎讨论指正。

作者:HongShion

物联沃分享整理
物联沃-IOTWORD物联网 » 【原创】STM32U5系列与STM32G4系列MCU内部温度传感器精准读取指南

发表回复