我们介绍过了 ADC模数转换实验, 知道 ADC 内部有一个通道连接着芯片 的温度传感器,这次就来学** STM32F1 的内部温度传感器。要实现的功能是:通过芯片内部温度传感器读取温度,并将读取的温度数据打印出去, D1 指示灯闪烁提示系统正常运行。学习时可以参考ADC 模数转换实验或者参考《STM32F10x 中文参考手册》-11 模数转换器(ADC)-11.10 章节,特别是寄存器介绍部分。
6 I8 @& U4 I2 a P$ c7 k
STM32F1 内部温度传感器简介 STM32F1 内部含有一个温度传感器,可用来测量 CPU 及周围的温度(TA)。此温度传感器与 ADC1 内部输入通道相连接,内部温度传感器连接通道框图如下图所示。它连接在ADC1_IN16 上。ADC1 可以将传感器输出的电压转换成数字值。STM32F1 的内部温度传感器支持的温度范围为:-40~125 度,精度为±1.5℃左右。 STM32F1 内部温度传感器的使用很简单,只要初始化下 ADC1_IN16 通道,并激活其内部温度传感器通道就差不多了。关于 ADC 的初始化,我们在上篇文章已经进行了详细的介绍,这里就不多说。接下来我们介绍一下和温度传感器设置相关的 2 个地方。 (1)要使用 STM32F1 的内部温度传感器,必须先激活 ADC 的内部通道, 这里通过 ADC_CCR 的 TSVREFE 位( bit23)设置。设置该位为 1 则启用内部温度传感器,否则关闭内部温度传感器。 (2)STM32F103ZET6的内部温度传感器固定的连接在 ADC1_IN16上, 所以,我们在设置好 ADC1 之后只要读取通道 16 的 AD 值,就知道温度传感器返回来的电压值了。根据这个值,我们就可以计算出当前温度。计算公式如下: - T(℃) ={( V25 - Vsense) /Avg_Slope}+25
复制代码
$ ]/ U+ V n8 E) a9 k& s4 x9 l- F 公式中:V25=Vsense 在 25 度时的数值(典型值为:1.43V)。 Avg_Slope=温度与 Vsense 曲线的平均斜率(单位为 mv/℃或 uv/℃)(典型值为4.3mV/℃)。通过上面公式,我们就能非常方便的计算出当前内部温度传感器测试的温度。 * c M' R/ P2 ]( q) c1 Z
内部温度传感器配置步骤 接下来我们介绍下如何使用库函数对内部温度传感器进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(ADC 相关库函数在stm32f10x_adc.c和 stm32f10x_adc.h 文件中) (1)初始化 ADC1_IN16相关参数,开启内度温度传感器 ADC1_IN16 的初始化步骤与上一章介绍 AD 模数转换实验一样,这里我们只需要开启内部温度传感器即可,调用的库函数为: - ADC_TempSensorVrefintCmd(ENABLE);//打开 ADC 内部温度传感器
复制代码
6 s% F; G3 Q5 |$ a(2)读取 ADC1_IN16 AD值,将其转换为对应温度8 ^) t/ G# U* a) y3 Q0 D( {
上一步配置好后, 我们就可以读取温度传感器的电压值, 根据温度计算公式,可以求出对应电压值的温度,具体方法与上一篇ADC文章介绍的一样。 本实验使用到硬件资源如下: (1)D1 指示灯 (2)串口 1 (3)内部温度传感器 D1 指示灯、串口 1 电路在前面章节都介绍过,这里就不多说,至于内部温度传感器它属于 STM32F1芯片内部的资源,连接的是 ADC1_IN16通道。 要实现的功能是:通过芯片内部温度传感器读取温度,并将读取的温度数据打印出去,D1 指示灯闪烁提示系统正常运行。程序框架如下: (1)初始化内部温度传感器(初始化 ADC1_IN16,开启温度传感器) (2)编写温度读取函数 (3)编写主函数 前面介绍内部温度传感器配置步骤时, 就已经讲解如何初始化内部温度传感器。下面我们打开“内部温度传感器实验”工程,在 APP工程组中可以看到添加了 adc_temp.c 文件(里面包含了内部温度传感器驱动程序),在 StdPeriph_Driver 工程组中添加了 stm32f10x_adc.c 库文件。 ADC 操作的库函数都放在stm32f10x_adc.c 和 stm32f10x_adc.h文件中,所以使用到 ADC 就必须加入 stm32f10x_adc.c 文件,同时还要包含对应的头文件路径。 这里我们分析几个重要函数,其他部分程序大家可以打开工程查看。 内部温度传感器初始化函数
* |' b: A' k+ ]7 P3 Z& ] 要使用内部温度传感器,我们必须先对它进行配置。初始化代码如下: - /****************************************************************( w1 S% |. a$ [! z( W+ p
- * 函 数 名 : ADC_Temp_Init% {. ~' ^) }0 }; b$ l& @
- * 函数功能 : ADC_Temp 初始化函数7 e0 K1 l+ g% n" O
- * 输 入 : 无
5 v W) p# p; Z - * 输 出 : 无5 M" B2 O6 e( S# x* E0 b/ F7 v
- *****************************************************************/
/ P! d3 ^" L1 D3 l - void ADC_Temp_Init(void)9 ^7 [1 p& o% R5 n+ B3 o/ B
- {
. {" f# l$ @/ \4 e - ADC_InitTypeDef ADC_InitStructure;5 U& [) ?) g. K6 A4 d7 d
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
0 b: C" G5 y# O - RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 分 频 因 子 6 时 钟 为72M/6=12MHz
8 t, r2 _0 y& e2 |/ y/ i( ~9 i* y - ADC_TempSensorVrefintCmd(ENABLE);//打开 ADC 内部温度传感器
0 d: g1 i9 r |/ F - ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式:ADC1 和 ADC2 工作在独立模式
) a- d% x/ V5 A/ q - ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式
; h4 Q& H# E- H" S3 K - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换. Q Z* d5 l1 n @+ ^ t) s1 D2 ?
- ADC_InitStructure.ADC_ExternalTrigConv =! W" C# S! v1 A z5 P% Q& a$ } t
- ADC_ExternalTrigConv_None;//禁止触发检测,使用软件触发
% ]' W( z7 O& N, _+ \, {) S' N - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐7 r- q- A" n1 u% G
- ADC_InitStructure.ADC_NbrOfChannel = 1; //1 个转换在规则序列中 也就是只转换规则序列12 H0 \8 Y M! n
- ADC_Init(ADC1, &ADC_InitStructure);//ADC 初始化
4 Y& @4 s( }! @* F: _% v - ADC_Cmd(ADC1, ENABLE);//开启 AD 转换器
4 e8 q0 v# Y0 Y+ r - ADC_ResetCalibration(ADC1);//重置指定的 ADC 的校准寄存器+ S1 v$ [! K0 l
- while(ADC_GetResetCalibrationStatus(ADC1));//获取 ADC 重置校准寄存器的状态
/ T9 p' o. i6 R8 @6 T& D0 W - ADC_StartCalibration(ADC1);//开始指定 ADC 的校准状态3 Q2 O6 |$ W/ V+ {* J9 Z
- while(ADC_GetCalibrationStatus(ADC1));//获取指定 ADC的校准程序
3 B$ }) C/ a& h* C) J# M - ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的ADC的软件转换启动功能2 ~3 W, f( h0 c* a' J
- }
复制代码4 j* t: T9 J+ i6 d5 Z
该 函 数 功 能 很 简 单 , 初 始 化 ADC1_IN16 通 道 , 并 且 调 用ADC_TempSensorVrefintCmd 函数开启内部温度传感器, 初始化过程与ADC模数转换实验几乎一模一样。 温度读取函数 当初始化内部温度传感器后,就可以读取温度值,代码如下: - /****************************************************************
- W6 v# b1 z* H% x' r - * 函 数 名 : Get_Temperture* j/ C/ h1 R6 V$ P8 q
- * 函数功能 : 获取温度值! g8 L; j" n( E! a
- * 输 入 : 无
2 F6 d' V9 G, ?% R) G( u" k - * 输 出 : 温度值(扩大了 100 倍,单位:℃)& ~4 R- E! L. j2 O
- *****************************************************************/ i) c8 X0 k* r0 g8 e
- int Get_Temperture(void)- G& P) Q4 B8 e0 H( U
- {
- H: p: r5 w0 N C - u32 adc_value;& r8 Y# U: ]; y% m3 M# a' f
- int temp;
$ R0 M# J9 H6 D7 }/ n - double temperture;3 q! F" @) b: y) H4 z {
- adc_value=Get_ADC_Temp_Value(ADC_Channel_16,10); //读取通道 16内部温度传感器通道,10次取平均- `0 V1 I: r, |1 I
- temperture=(float)adc_value*(3.3/4096); //电压值
/ l1 D7 n v: {% I! \4 ~) J - temperture=(1.43-temperture)/0.0043+25; //转换为温度值
0 O9 P8 {- d7 W1 d - temp=temperture*100; //扩大 100 倍." p# w6 h, I( e" R8 _" Z
- return temp;
9 C. } Y4 n2 o6 d/ i - }
复制代码. k# ~$ P; z* J5 O3 @0 l
温度读取函数代码比较简单,首先读取 ADC1_IN16 通道的AD 值,然后将其转换为电压值,根据温度计算公式就可以得到对应的温度值,最后将其放大100倍作为函数值返回,温度值有正负,所以返回值类型为 int。 0 I1 g; i3 W; y+ ^5 r. U
主函数 编写好内部温度传感器初始化和温度读取函数后, 接下来就可以编写主函数了,代码如下: - /****************************************************************; L" b! `2 c' [- }* e) s+ {
- * 函 数 名 : main
) j+ B. u' \) `% r4 z% Y& e& F - * 函数功能 : 主函数$ @% \* v2 a8 u, H" C# D& V1 [
- * 输 入 : 无
+ m: }, N0 D5 K& B2 q - * 输 出 : 无
% d9 M. h; K2 K" x) U$ f - *****************************************************************/5 f k3 a- R, g* @. F
- int main()
& _& N- ~' `. A2 z- w: @5 W- q8 a8 A - {5 L7 P5 [* C5 z* g
- u8 i=0;
, Y2 ]$ E, J- |! @ - int temp=0;
! ?7 y. M+ Z( J, \ - SysTick_Init(72);5 Z% Q" i+ h9 @6 v8 q$ Q! y: \
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
8 N" t1 [2 {5 t) _ - LED_Init();
8 H2 C7 F4 R, G' T" m' v4 Y - USART1_Init(9600);
* Y" T: N7 w' V0 ^ - ADC_Temp_Init();; ?0 s# A' [ t. z$ `) N
- while(1)7 m* ?2 q: H8 Z2 E) R
- {6 U( I8 Y1 n+ }" @$ {, j
- i++;
9 g5 p2 f: t/ L: x. k8 K+ o: ^' t# T - if(i%20==0)9 n: P& z [4 m5 W- |
- {0 J5 L0 f& ~- ` _
- led1=!led1;
* Q( Z' ~% B5 w9 q - }
. Q, v" Z9 X0 G+ R# }. m% {9 S5 O - if(i%50==0)5 ^1 H( c8 j) q. H$ V( R
- {
& ^0 t, @. v' ]- d, T, I7 O - temp=Get_Temperture();
3 _, B$ |7 A- A" q - if(temp<0)5 B" j. \$ _: W0 v$ k' e) Z
- {8 f* \" V# B/ w' Z/ e
- temp=-temp;
# A) D( v& ^+ p5 f3 D - printf("内部温度检测值为:-");) L6 u- O9 v5 R# O. E! j1 W% N
- }
5 ]8 A H7 Y# c( f! U - else
7 e+ k- g9 j: m4 Y J* M - {8 l. n8 O7 t' L9 w1 X
- printf("内部温度检测值为:+");4 M+ Z! s( i' N
- }& E; A. ^0 G) a) q/ ]
- printf("%.2f°C\r\n",(float)temp/100);! |, y2 D% G: j. Z# }3 U
- }
+ v4 K% v0 I0 h0 s! L1 ] - delay_ms(10);
5 P) |, T& p5 }- q - }7 i7 Y. B) Z' ^% \
- }
复制代码2 B# M( o L: Z2 l% b
主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括SysTick 系统时钟,中断分组,LED 初始化等。然后调用我们前面编写的 ADC_Temp_Init 函数,最后进入 while 循环,间隔 500ms读取一次温度,判断读取的温度是正温度还是负温度,最后打印温度数据,在输出温度数据时,要记得除以 100,因为读取的温度值是放大了 100 倍的。D1 指示灯会间隔200ms闪烁,提示系统正常运行。 将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。串口不断打印读取的温度数据,如果想在串口调试助手上看到输出信息,可以打开“串口调试助手”,首先勾选下标号 1 DTR 框,然后再取消勾选。这是因为此串口助手启动时会把系统复位住,通过 DTR 状态切换下即可。然后设置好波特率等参数后,串口助手上即会收到 printf 发送过来的信息。(串口助手上先勾选下标号 1 DTR 框,然后再取消勾选)如下图所示:
( u4 `2 v u, z" ?% N5 e8 G' T: v 注:由于芯片工作会发热,所以内部温度传感器检测的温度通常会高于实际温度,这也是不使用芯片内部温度传感器来检测环境温度的原因。 , f( r, G; u7 H% t
|