|
我们介绍过了 ADC模数转换实验, 知道 ADC 内部有一个通道连接着芯片 的温度传感器,这次就来学** STM32F1 的内部温度传感器。要实现的功能是:通过芯片内部温度传感器读取温度,并将读取的温度数据打印出去, D1 指示灯闪烁提示系统正常运行。学习时可以参考ADC 模数转换实验或者参考《STM32F10x 中文参考手册》-11 模数转换器(ADC)-11.10 章节,特别是寄存器介绍部分。 / ?4 E4 `' K7 ~- J. t3 G
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
复制代码 # o! q/ s! c+ a- t" C( I3 U' L' P
公式中:V25=Vsense 在 25 度时的数值(典型值为:1.43V)。 Avg_Slope=温度与 Vsense 曲线的平均斜率(单位为 mv/℃或 uv/℃)(典型值为4.3mV/℃)。通过上面公式,我们就能非常方便的计算出当前内部温度传感器测试的温度。
. \6 N# A% v8 N: S2 m6 ] ^
内部温度传感器配置步骤 接下来我们介绍下如何使用库函数对内部温度传感器进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(ADC 相关库函数在stm32f10x_adc.c和 stm32f10x_adc.h 文件中) (1)初始化 ADC1_IN16相关参数,开启内度温度传感器 ADC1_IN16 的初始化步骤与上一章介绍 AD 模数转换实验一样,这里我们只需要开启内部温度传感器即可,调用的库函数为: - ADC_TempSensorVrefintCmd(ENABLE);//打开 ADC 内部温度传感器
复制代码 ! {6 D6 I+ z) y" [
(2)读取 ADC1_IN16 AD值,将其转换为对应温度
! q! }/ P( s2 |6 G 上一步配置好后, 我们就可以读取温度传感器的电压值, 根据温度计算公式,可以求出对应电压值的温度,具体方法与上一篇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 文件,同时还要包含对应的头文件路径。 这里我们分析几个重要函数,其他部分程序大家可以打开工程查看。 内部温度传感器初始化函数
0 N# U6 |8 F9 [' J+ R 要使用内部温度传感器,我们必须先对它进行配置。初始化代码如下: - /****************************************************************1 r5 R& @" u4 v2 i. A' r* k
- * 函 数 名 : ADC_Temp_Init
5 v% K d/ ?# i2 n. X, A2 u - * 函数功能 : ADC_Temp 初始化函数
6 h6 c. r0 N6 a% T" w0 a - * 输 入 : 无
7 e- o2 P! p! @, ? - * 输 出 : 无
! e$ H2 y1 y+ N; Z9 D - *****************************************************************/* B2 c# q7 ~3 C: F1 y
- void ADC_Temp_Init(void). F1 h: m! m/ S% S1 e9 L% L
- {6 E; i" H+ l |4 b: O% u4 K
- ADC_InitTypeDef ADC_InitStructure;
& G% W* U; k0 n5 E4 i - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);1 j6 S" L" U. S8 s1 D
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 分 频 因 子 6 时 钟 为72M/6=12MHz
( J8 d8 r: m6 R s n: T" L" } - ADC_TempSensorVrefintCmd(ENABLE);//打开 ADC 内部温度传感器4 C/ Z2 ~& J" C( D$ }2 d% l
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式:ADC1 和 ADC2 工作在独立模式
: H- d: |( ?: f6 U - ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式5 M% e% U7 C) ]. j% X. m/ |
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
) ]. T0 P( b* m' l - ADC_InitStructure.ADC_ExternalTrigConv =3 b: i3 i6 g, Y9 Y' [, G7 p
- ADC_ExternalTrigConv_None;//禁止触发检测,使用软件触发- G( M/ C ~5 Y# @; l
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐: `. l- p- b( ~" g# x- u3 B
- ADC_InitStructure.ADC_NbrOfChannel = 1; //1 个转换在规则序列中 也就是只转换规则序列1
8 }8 t5 q7 U8 ]( A* c - ADC_Init(ADC1, &ADC_InitStructure);//ADC 初始化
/ q; a6 n& V% P - ADC_Cmd(ADC1, ENABLE);//开启 AD 转换器# ~9 ]- z' E4 b5 B
- ADC_ResetCalibration(ADC1);//重置指定的 ADC 的校准寄存器# _6 k* `1 H& J6 g6 @
- while(ADC_GetResetCalibrationStatus(ADC1));//获取 ADC 重置校准寄存器的状态1 Y: [! A: Y; G; B+ n
- ADC_StartCalibration(ADC1);//开始指定 ADC 的校准状态
4 K/ G. Y2 j, q! K2 j+ Y3 v - while(ADC_GetCalibrationStatus(ADC1));//获取指定 ADC的校准程序$ ?7 a# h! Z2 Z1 E2 }3 }
- ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的ADC的软件转换启动功能2 E1 h. q6 v7 ?9 T$ z+ c
- }
复制代码
$ x% S/ `5 q. h2 ^
该 函 数 功 能 很 简 单 , 初 始 化 ADC1_IN16 通 道 , 并 且 调 用ADC_TempSensorVrefintCmd 函数开启内部温度传感器, 初始化过程与ADC模数转换实验几乎一模一样。 温度读取函数 当初始化内部温度传感器后,就可以读取温度值,代码如下: - /****************************************************************
7 q$ W% y1 k( {8 ` - * 函 数 名 : Get_Temperture% Q; h" Y' ]# L3 t
- * 函数功能 : 获取温度值4 O/ |0 B& R$ N0 ^# L$ w
- * 输 入 : 无
. @. z1 v. t! c" t$ t0 K- n: i - * 输 出 : 温度值(扩大了 100 倍,单位:℃)
. Y* Q# b% ~4 n5 y2 [ - *****************************************************************/. a7 @! b! E( `* ^' u2 l- k
- int Get_Temperture(void). i# r3 M0 [* }
- {
* q: x: t+ R" H- x1 X - u32 adc_value;
+ L2 V4 s: ~) o3 j. l Y- w - int temp;
; V l _( ~ |/ V - double temperture;$ t' ]: ^1 F* F% m
- adc_value=Get_ADC_Temp_Value(ADC_Channel_16,10); //读取通道 16内部温度传感器通道,10次取平均8 O! c C8 ^3 s$ G/ ~ e2 _
- temperture=(float)adc_value*(3.3/4096); //电压值
! ~7 g5 t+ f7 R" O - temperture=(1.43-temperture)/0.0043+25; //转换为温度值* |+ J' W/ F: r) e. _: r8 U
- temp=temperture*100; //扩大 100 倍.
* O- ~' d- ]. C% B3 q - return temp;
% `/ Z) C( D0 |- h# T% s3 r' y - }
复制代码
/ G& l3 U3 e) i! Z
温度读取函数代码比较简单,首先读取 ADC1_IN16 通道的AD 值,然后将其转换为电压值,根据温度计算公式就可以得到对应的温度值,最后将其放大100倍作为函数值返回,温度值有正负,所以返回值类型为 int。 * L. ^+ Y* ]# T/ ~1 y! z1 H
主函数 编写好内部温度传感器初始化和温度读取函数后, 接下来就可以编写主函数了,代码如下: - /****************************************************************/ b: G/ Y" ]% p
- * 函 数 名 : main" o1 t3 u3 }3 ~% H3 x
- * 函数功能 : 主函数
& }) s9 }! \8 M Y+ R4 F5 [5 F - * 输 入 : 无
) Y0 u5 e/ E% B J+ P" }" s - * 输 出 : 无
+ r+ k9 V4 U! ` z1 K0 t - *****************************************************************/
7 `2 ^% C& _# C$ V6 ~ Z7 r6 ^ - int main()
; }0 I' O$ E% n - {& H* {; y- f+ L( V+ _8 p' f( |
- u8 i=0;
$ a' z, c5 O; `' {; d; z; H - int temp=0;+ c* s% T! K4 a$ a( T+ M6 g+ M7 U
- SysTick_Init(72);
1 h( g. {1 V& d$ v8 n, c- G - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
( Q$ F! u8 @7 w1 m8 L! _ - LED_Init();( V* o" ^+ w( b0 z2 E
- USART1_Init(9600);) P# g( e0 S8 H3 u% S9 p1 d, O, f( q: ?
- ADC_Temp_Init();) X2 U$ t6 N( `+ p# q' i9 R
- while(1)
; |+ L: |- f. i2 V* U$ }0 C - {/ s; S3 h7 k: O) a
- i++;$ k! j! p+ k8 u# a! N: F, D8 M
- if(i%20==0)
\* V# F- ?! [0 { - { X" W/ V5 a9 l' A. ^
- led1=!led1;: t4 @. U, Z7 z" S
- }
% D! [. R8 ]* e0 r5 h9 ~! F% ? - if(i%50==0)' @) c' i ]0 t" g* O
- {( \+ P2 N- ~( F2 g5 p" f# W
- temp=Get_Temperture();
c3 V0 K- O2 ?" S. G4 } - if(temp<0)
8 L# o, B( r9 y) l' N - {. p/ l Y* U. x
- temp=-temp;
$ y8 I4 |3 K8 f% v% U - printf("内部温度检测值为:-");
0 ]" V6 z* O2 h; @5 W# L2 @ - }% S8 h7 d6 ^" ^/ g# ~0 L5 ]3 D
- else& p) @% v4 ]- h/ k& {
- {/ D5 p6 G+ B7 ]; W0 v' N; A
- printf("内部温度检测值为:+");
1 u0 o( M. K3 m# _; i& W - }
: R. b H4 x) S2 w! w& n3 | - printf("%.2f°C\r\n",(float)temp/100);
^ u9 P& } T" | - }
0 Z4 Z9 b& G2 [" O% a' L - delay_ms(10); d8 v# k( S1 X
- }
1 z: m7 }: L$ t4 g. d - }
复制代码4 O* O1 |9 u0 M0 t& _& O* h
主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括SysTick 系统时钟,中断分组,LED 初始化等。然后调用我们前面编写的 ADC_Temp_Init 函数,最后进入 while 循环,间隔 500ms读取一次温度,判断读取的温度是正温度还是负温度,最后打印温度数据,在输出温度数据时,要记得除以 100,因为读取的温度值是放大了 100 倍的。D1 指示灯会间隔200ms闪烁,提示系统正常运行。 将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。串口不断打印读取的温度数据,如果想在串口调试助手上看到输出信息,可以打开“串口调试助手”,首先勾选下标号 1 DTR 框,然后再取消勾选。这是因为此串口助手启动时会把系统复位住,通过 DTR 状态切换下即可。然后设置好波特率等参数后,串口助手上即会收到 printf 发送过来的信息。(串口助手上先勾选下标号 1 DTR 框,然后再取消勾选)如下图所示:
' Z. [. p5 f: J7 `: h 注:由于芯片工作会发热,所以内部温度传感器检测的温度通常会高于实际温度,这也是不使用芯片内部温度传感器来检测环境温度的原因。 $ L2 E1 b& R' ]; a& g1 z4 y* y7 _
|