OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6729|回复: 0

《MiniPRO H750开发指南》第三十二章 内部温度传感器实验

[复制链接]

1140

主题

1152

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4896
金钱
4896
注册时间
2019-5-8
在线时间
1248 小时
发表于 2023-1-13 10:24:18 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-1-12 12:17 编辑

第三十二章 内部温度传感器实验

1)实验平台:正点原子MiniPro STM32H750开发板

2) 章节摘自【正点原子】MiniPro STM32H750 开发指南_V1.1


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h750_minipro.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)MiniPro STM32H750技术交流QQ群:170313895

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章,我们将介绍STM32H750的内部温度传感器并使用它来读取温度值,然后在LCD模块上显示出来。
本章分为如下几个小节:
32.1 内部温度传感器简介
32.2 硬件设计
32.3 程序设计
32.4 下载验证

32.1 内部温度传感器简介
STM32H750有一个内部的温度传感器,可以用来测量CPU及周围的温度(TA)。对于STM32H7系列来说,该温度传感器在内部和ADC3_INP18输入通道相连接,此通道把传感器输出的电压转换成数字值。STM32H750的内部温度传感器支持的温度范围为:-40~125度。精度为±3℃左右。

STM32H750内部温度传感器的使用很简单,只要设置一下内部ADC,并激活其内部温度传感器通道就差不多了。关于ADC的设置,我们在上一章已经进行了详细的介绍,这里就不再多说。接下来我们介绍一下和温度传感器设置相关的两个地方。

第一个地方,我们要使用STM32H750的内部温度传感器,必须先激活ADC的内部通道,这里通过ADC3_COMMON_CCR的VSENSEEN位(bit23)设置。设置该位为1则启用内部温度传感器。

第二个地方,STM32H750的内部温度传感器固定的连接在ADC3的通道18上,所以,我们在设置好ADC3之后只要读取通道18的值,就是温度传感器返回来的电压值了。根据这个值,我们就可以计算出当前温度。计算公式如下:
image001.png

上式中:
TS_CAL1 是温度传感器在30℃时的校准值,固定保存在芯片内部的:0X1FF1 E820 ~ 0X1FF1 E821这两个地址(16位)。
TS_CAL2 是温度传感器在110℃时的校准值,固定保存在芯片内部的:0X1FF1 E840 ~ 0X1FF1 E841这两个地址(16位)。
TS_DATA:ADC3通道18读取到的当前温度传感器转换值。

利用以上公式,我们就可以方便的计算出当前温度传感器的温度了。

32.2 硬件设计
1. 例程功能
通过ADC3的通道18读取STM32H7内部温度传感器的电压值,并将其转换为温度值,显示在TFTLCD屏上。LED0闪烁用于提示程序正在运行。

2. 硬件资源
1)RGB灯
     RED : LED0 - PB4
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
4)ADC3 通道18
5)内部温度传感器

32.3 程序设计
32.3.1 ADC的HAL库驱动
本实验用到的ADC的HAL库API函数前面都介绍过,具体调用情况请看程序解析部分。下面介绍读取内部温度传感器ADC值的配置步骤。

读取内部温度传感器ADC值配置步骤
1)开启ADC时钟
通过__HAL_RCC_ADC3_CLK_ENABLE函数开启ADC3的时钟。

2)设置ADC3,开启内部温度传感器
调用HAL_ADC_Init函数来设置ADC3时钟分频系数、分辨率、模式、扫描方式、对齐方式等信息。
注意:该函数会调用:HAL_ADC_MspInit回调函数来完成对ADC底层的初始化,包括:ADC3时钟使能、ADC3时钟源的选择等。

3)配置ADC通道并启动AD转换器
调用HAL_ADC_ConfigChannel()函数配置ADC3通道18,根据需求设置通道、序列、采样时间和校准配置单端输入模式或差分输入模式等。然后通过HAL_ADC_Start函数启动AD转换器。

4)读取ADC值,计算温度
这里选择查询方式读取,在读取ADC值之前需要调用HAL_ADC_PollForConversion等待上一次转换结束。然后就可以通过HAL_ADC_GetValue来读取ADC值。最后根据上面介绍的公式计算出温度传感器的温度值。

32.3.2 程序流程图
image004.png
图32.3.2.1 内部温度传感器实验程序流程图


32.3.3 程序解析
1. adc3驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。ADC3驱动源码包括两个文件:adc3.c和adc3.h。
adc3.h头文件只有一个宏定义和一些函数的声明,该宏定义如下:
  1. #define ADC3_TEMPSENSOR_CHX ADC_CHANNEL_TEMPSENSOR
复制代码
ADC_CHANNEL_TEMPSENSOR就是ADC3通道18连接内部温度传感器的通道18宏定义。我们在定义为ADC3_TEMPSENSOR_CHX,可以让大家更容易理解这个宏定义的含义。
下面我们直接介绍adc3.c的程序,首先是ADC3初始化函数,其定义如下:
  1. /**
  2. *@brief ADC3初始化函数
  3. * @note 本函数专用于支持ADC3, 和ADC1/2区分开来, 方便使用
  4. * 我们使用16位精度, ADC采样时钟=32M, 转换时间为:采样周期 + 8.5个ADC周期
  5. * 设置最大采样周期: 810.5, 则转换时间 = 819个ADC周期 = 25.6us
  6. *@param 无
  7. *@retval 无
  8. */
  9. void adc3_init(void)
  10. {
  11. g_adc3_handle.Instance = ADC3; /* 选择哪个ADC */
  12. /* 输入时钟2分频,即adc_ker_ck=per_ck/2=32Mhz*/
  13. g_adc3_handle.Init.ClockPrescaler=ADC_CLOCK_ASYNC_DIV2;
  14. g_adc3_handle.Init.Resolution = ADC_RESOLUTION_16B; /* 16位模式 */
  15. g_adc3_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; /* 非扫描模式 */
  16. g_adc3_handle.Init.EOCSelection =ADC_EOC_SINGLE_CONV; /* 关闭EOC中断 */
  17. g_adc3_handle.Init.LowPowerAutoWait= DISABLE; /* 自动低功耗关闭 */
  18. g_adc3_handle.Init.ContinuousConvMode= DISABLE; /* 关闭连续转换模式 */
  19. g_adc3_handle.Init.NbrOfConversion= 1; /* 赋值范围是1~16,本实验用到1个通道 */
  20. /* 禁止常规转换组不连续采样模式 */
  21. g_adc3_handle.Init.DiscontinuousConvMode= DISABLE;
  22. /* 配置不连续采样模式的通道数,禁止常规转换组不连续采样模式后,此参数忽略 */
  23. g_adc3_handle.Init.NbrOfDiscConversion= 0;
  24. g_adc3_handle.Init.ExternalTrigConv= ADC_SOFTWARE_START; /* 软件触发 */
  25. /* 采用软件触发的话,此位忽略 */
  26. g_adc3_handle.Init.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_NONE;
  27. /* 常规通道的数据仅仅保存在DR寄存器里面 */
  28. g_adc3_handle.Init.ConversionDataManagement=ADC_CONVERSIONDATA_DR;
  29. /* 有新的数据后直接覆盖掉旧数据 */
  30. g_adc3_handle.Init.Overrun =ADC_OVR_DATA_OVERWRITTEN;
  31. /* 设置ADC转换结果的左移位数 */
  32. g_adc3_handle.Init.LeftBitShift =ADC_LEFTBITSHIFT_NONE;
  33. g_adc3_handle.Init.OversamplingMode= DISABLE; /* 关闭过采样 */
  34. HAL_ADC_Init(&g_adc3_handle); /* 初始化 */
  35. HAL_ADCEx_Calibration_Start(&g_adc3_handle, ADC_CALIB_OFFSET,
  36. ADC_SINGLE_ENDED); /* ADC校准 */
  37. }
复制代码
该函数主要调用了两个HAL库函数,HAL_ADC_Init函数配置了选择哪个ADC、数据对齐方式、是否使用扫描模式等参数,HAL_ADCEx_Calibration_Start函数用于校准ADC。另外HAL_ADC_Init函数会调用它的MSP回调函数HAL_ADC_MspInit,该函数用来存放使能ADC和配置选择ADC时钟源等代码,其定义如下:
  1. /**
  2. *@brief       ADC底层驱动,引脚配置,时钟使能
  3.                  此函数会被HAL_ADC_Init()调用
  4. *@param       hadc:ADC句柄
  5. *@retval      无
  6. */
  7. voidHAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
  8. {
  9.    RCC_PeriphCLKInitTypeDef rcc_periph_clk_struct = {0};
  10.    __HAL_RCC_ADC3_CLK_ENABLE();            /* 使能ADC3时钟 */
  11.    rcc_periph_clk_struct.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  12.    rcc_periph_clk_struct.AdcClockSelection = RCC_ADCCLKSOURCE_CLKP;
  13.    HAL_RCCEx_PeriphCLKConfig(&rcc_periph_clk_struct);
  14. }
复制代码
下面是获得ADC转换后的结果函数,其定义如下:
  1. /**
  2. *@brief       获得ADC转换后的结果
  3. *@param       ch: 通道号, ADC_CHANNEL_0~ADC_CHANNEL_19
  4. *@retval      无
  5. */
  6. uint32_t adc3_get_result(ADC_HandleTypeDef adc_handle, uint32_t ch)
  7. {
  8.    ADC_ChannelConfTypeDef adc_ch_conf = {0};
  9.    adc_ch_conf.Channel = ch;                                    /* 通道 */
  10.    adc_ch_conf.Rank =ADC_REGULAR_RANK_1;                    /* 序列 */
  11.    adc_ch_conf.SamplingTime = ADC_SAMPLETIME_810CYCLES_5; /* 采样时间 */
  12.    adc_ch_conf.SingleDiff = ADC_SINGLE_ENDED;               /* 单边采集 */
  13.    adc_ch_conf.OffsetNumber = ADC_OFFSET_NONE;              /* 不使用偏移量的通道 */
  14.    adc_ch_conf.Offset=0;                                        /* 偏移量为0 */
  15.    HAL_ADC_ConfigChannel(&adc_handle, &adc_ch_conf);      /* 通道配置 */
  16.    HAL_ADC_Start(&adc_handle);                                /* 开启ADC */
  17.    HAL_ADC_PollForConversion(&adc_handle, 10);            /* 轮询转换 */
  18.     return HAL_ADC_GetValue(&adc_handle);   /* 返回最近一次ADC规则组的转换结果 */
  19. }
复制代码
该函数先调用HAL_ADC_ConfigChannel函数设置通道的转换序列和采样时间等,再调用HAL_ADC_Start函数开启ADC,接着调用HAL_ADC_PollForConversion函数等待转换完成,最后调用HAL_ADC_GetValue函数获取转换后的当前结果。
下面介绍的是获取ADC某通道的转换多次后的平均值函数,函数定义如下:
  1. /**
  2. *@brief       获取通道ch的转换值,取times次,然后平均
  3. *@param       ch      : 通道号, 0~19
  4. *@param       times   : 获取次数
  5. *@retval      通道ch的times次转换结果平均值
  6. */
  7. uint32_t adc3_get_result_average(ADC_HandleTypeDef adc_handle,
  8. uint32_t ch, uint8_t times)
  9. {
  10.     uint32_t temp_val = 0;
  11.     uint8_t t;
  12.     for (t = 0; t < times; t++)   /* 获取times次数据 */
  13.     {
  14.        temp_val += adc3_get_result(adc_handle, ch);
  15.        delay_ms(5);
  16.     }
  17.     return temp_val / times;      /* 返回平均值 */
  18. }
复制代码
该函数用于多次获取ADC值,累加再取平均值,以提高准确度。
最后一个函数是获取内部温度传感器温度值函数,函数定义如下:
  1. /**
  2. *@brief       获取内部温度传感器温度值
  3. *@param       无
  4. *@retval      温度值(扩大了100倍,单位:℃.)
  5. */
  6. shortadc3_get_temperature(void)
  7. {
  8.     uint32_t adcx;
  9.     short result;
  10.     double temperature;
  11.     float temp = 0;
  12.     uint16_t ts_cal1, ts_cal2;
  13.    
  14.    ts_cal1 = *(volatile uint16_t *)(0X1FF1E820);          /* 获取TS_CAL1 */
  15.    ts_cal2 = *(volatile uint16_t *)(0X1FF1E840);          /* 获取TS_CAL2 */
  16.    temp = (float)((110.0 - 30.0) / (ts_cal2 - ts_cal1));/* 获取比例因子 */
  17. /* 读取内部温度传感器通道,10次取平均 */
  18.    adcx =adc3_get_result_average(adc3_handle, ADC3_TEMPSENSOR_CHX, 10);
  19.    temperature = (float)(temp * (adcx - ts_cal1) + 30); /* 计算温度 */
  20.    result = temperature *= 100;                             /* 扩大100倍. */
  21.     return result;
  22. }
复制代码
该函数根据ADC3通道18采集温度传感器返回来的电压值代入32.1小节介绍的公式来计算出当前温度值。

2. main.c代码
在main.c里面编写如下代码:
  1. int main(void)
  2. {
  3.     short temp;
  4.    sys_cache_enable();                        /* 打开L1-Cache */
  5.    HAL_Init();                                  /* 初始化HAL库 */
  6.    sys_stm32_clock_init(240, 2, 2, 4);     /* 设置时钟, 480Mhz */
  7.    delay_init(480);                            /* 延时初始化 */
  8.    usart_init(115200);                        /* 串口初始化为115200 */
  9.    mpu_memory_protection();                  /* 保护相关存储区域 */
  10.    led_init();                                  /* 初始化LED */
  11.    lcd_init();                                  /* 初始化LCD */
  12.    adc3_init();                                /* 初始化ADC3(使能内部温度传感器) */
  13.    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  14.    lcd_show_string(30, 70, 200, 16, 16, "TemperatureTEST", RED);
  15.    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  16.    lcd_show_string(30, 120, 200, 16, 16, "TEMPERATE:00.00C", BLUE);
  17.     while (1)
  18.     {
  19.        temp =adc3_get_temperature(); /* 得到温度值 */
  20.        if (temp < 0)
  21.        {
  22.            temp = -temp;
  23.            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, "-", BLUE);/* 显示负号 */
  24.        }
  25.        else
  26.        {
  27.            lcd_show_string(30 + 10 * 8, 120, 16, 16, 16, " ", BLUE); /* 无符号 */
  28.        }
  29. /* 显示整数部分 */
  30.        lcd_show_xnum(30 + 11 * 8, 120, temp / 100, 2, 16, 0, BLUE);
  31. /* 显示小数部分 */
  32.        lcd_show_xnum(30 + 14 * 8, 120, temp % 100, 2, 16, 0X80, BLUE);
  33.       
  34.        LED0_TOGGLE();  /* LED0闪烁,提示程序运行 */
  35.        delay_ms(250);
  36.     }
  37. }
复制代码
该部分的代码逻辑很简单,先是得到温度值,再根据温度值判断正负值,来显示温度符号,再显示整数和小数部分。

32.4 下载验证
将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示的内容如图32.4.1所示:
   image005.png    
图32.4.1 内部温度传感器实验测试图

大家可以看看你的温度值与实际是否相符合(因为芯片会发热,所以一般会比实际温度偏高)
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-24 20:08

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表