|
我们介绍过了 ADC模数转换实验, 知道 ADC 内部有一个通道连接着芯片 的温度传感器,这次就来学** STM32F1 的内部温度传感器。要实现的功能是:通过芯片内部温度传感器读取温度,并将读取的温度数据打印出去, D1 指示灯闪烁提示系统正常运行。学习时可以参考ADC 模数转换实验或者参考《STM32F10x 中文参考手册》-11 模数转换器(ADC)-11.10 章节,特别是寄存器介绍部分。
& ~8 z# H! U! J" w9 C
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
复制代码
% A4 t9 t( m/ ?7 C" D 公式中:V25=Vsense 在 25 度时的数值(典型值为:1.43V)。 Avg_Slope=温度与 Vsense 曲线的平均斜率(单位为 mv/℃或 uv/℃)(典型值为4.3mV/℃)。通过上面公式,我们就能非常方便的计算出当前内部温度传感器测试的温度。
2 j6 i7 m1 W# P1 O% g8 e
内部温度传感器配置步骤 接下来我们介绍下如何使用库函数对内部温度传感器进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(ADC 相关库函数在stm32f10x_adc.c和 stm32f10x_adc.h 文件中) (1)初始化 ADC1_IN16相关参数,开启内度温度传感器 ADC1_IN16 的初始化步骤与上一章介绍 AD 模数转换实验一样,这里我们只需要开启内部温度传感器即可,调用的库函数为: - ADC_TempSensorVrefintCmd(ENABLE);//打开 ADC 内部温度传感器
复制代码
; J0 c& c, T- g( q# ^' j8 d( n, d(2)读取 ADC1_IN16 AD值,将其转换为对应温度# W, O! S: k" v) ~$ }( ^- o
上一步配置好后, 我们就可以读取温度传感器的电压值, 根据温度计算公式,可以求出对应电压值的温度,具体方法与上一篇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 文件,同时还要包含对应的头文件路径。 这里我们分析几个重要函数,其他部分程序大家可以打开工程查看。 内部温度传感器初始化函数
$ D0 W5 B; I' }2 ~1 E6 P4 F1 I Y( I 要使用内部温度传感器,我们必须先对它进行配置。初始化代码如下: - /****************************************************************$ ^# j7 x9 \, S6 D2 J: L4 Y: ~
- * 函 数 名 : ADC_Temp_Init
1 G$ }! d8 U( P6 x) p% g, @ p( Q, W7 Q - * 函数功能 : ADC_Temp 初始化函数3 R9 M8 n) z- v# O7 D; p6 n0 ?/ r
- * 输 入 : 无1 L1 E- Y& l# A! O
- * 输 出 : 无
( C* @$ R- s1 k" K8 D6 a# g9 g5 ~ - *****************************************************************/
% m- N( \. e. I1 Z9 S - void ADC_Temp_Init(void)
! J9 d/ o3 D+ r& `9 v# b2 ]9 A - {
* a* L- o; [! f5 }. {/ W - ADC_InitTypeDef ADC_InitStructure;
; V- U j4 r. F' e" s- m G, [ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);$ F& Z3 Y6 C. e/ x( o% W; Q
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 分 频 因 子 6 时 钟 为72M/6=12MHz( J, r7 B" I1 j$ s5 v
- ADC_TempSensorVrefintCmd(ENABLE);//打开 ADC 内部温度传感器
6 K3 u( j& O$ p' l: C. t9 Y# l& e - ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC 工作模式:ADC1 和 ADC2 工作在独立模式# H, w: K/ X2 [2 ?$ s
- ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式3 L" p; G% z% s' k, M% }) f
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换6 {+ `2 s) U5 Y, g( F3 M$ m: V# L% @
- ADC_InitStructure.ADC_ExternalTrigConv =
8 N1 D: b! @ c5 v1 U0 P - ADC_ExternalTrigConv_None;//禁止触发检测,使用软件触发
/ y- u# y S9 D2 H' ] - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐* F+ S- Z* ?! f z* S
- ADC_InitStructure.ADC_NbrOfChannel = 1; //1 个转换在规则序列中 也就是只转换规则序列1
& f! N" [* \7 ^, ^ - ADC_Init(ADC1, &ADC_InitStructure);//ADC 初始化 g. ]' V/ ~; X* |+ f8 o- x
- ADC_Cmd(ADC1, ENABLE);//开启 AD 转换器, e$ p# t Y1 ~' u: e/ f! R
- ADC_ResetCalibration(ADC1);//重置指定的 ADC 的校准寄存器
J4 @2 m4 R2 `9 W - while(ADC_GetResetCalibrationStatus(ADC1));//获取 ADC 重置校准寄存器的状态
2 c* f. w' p5 L, A% |: e( U8 a - ADC_StartCalibration(ADC1);//开始指定 ADC 的校准状态
r B: O, k5 i" W - while(ADC_GetCalibrationStatus(ADC1));//获取指定 ADC的校准程序
6 F. t- D" ^7 _ - ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的ADC的软件转换启动功能
u0 Z7 F6 F5 ^0 R - }
复制代码4 s6 W* z5 V O2 g A
该 函 数 功 能 很 简 单 , 初 始 化 ADC1_IN16 通 道 , 并 且 调 用ADC_TempSensorVrefintCmd 函数开启内部温度传感器, 初始化过程与ADC模数转换实验几乎一模一样。 温度读取函数 当初始化内部温度传感器后,就可以读取温度值,代码如下: - /****************************************************************
5 z+ G; M: Z8 C. _( ?. a, s9 h. w - * 函 数 名 : Get_Temperture5 z- G% T: n) o" \$ ]0 q
- * 函数功能 : 获取温度值
5 O9 ^3 q+ x, [9 f! a! R - * 输 入 : 无3 U* @& p1 J. {! D* p5 n7 i
- * 输 出 : 温度值(扩大了 100 倍,单位:℃)
, s( g2 f+ H2 a) m" r - *****************************************************************/8 f" G* o4 V8 s. j% J, a% {6 R
- int Get_Temperture(void)+ [* F# T8 d1 e; |4 @, m" d
- {
/ d4 Q& d& p$ i+ t) P - u32 adc_value;
! N- Y% N9 g: B( N - int temp;
5 N, {; k6 Z3 E8 J( V1 B" o+ {" K - double temperture;
7 l( K! W" C! E' K4 E/ ^ - adc_value=Get_ADC_Temp_Value(ADC_Channel_16,10); //读取通道 16内部温度传感器通道,10次取平均. x! F- q/ r" f8 H o5 b+ `
- temperture=(float)adc_value*(3.3/4096); //电压值, a$ s1 ]5 e. J+ R
- temperture=(1.43-temperture)/0.0043+25; //转换为温度值
# v! v/ `/ r n8 U" K - temp=temperture*100; //扩大 100 倍.6 u; P. r" M$ t+ `2 m% m
- return temp;" b% z2 [# S9 J& b2 [1 C/ {, z
- }
复制代码- L" J8 [2 k! |* {2 R
温度读取函数代码比较简单,首先读取 ADC1_IN16 通道的AD 值,然后将其转换为电压值,根据温度计算公式就可以得到对应的温度值,最后将其放大100倍作为函数值返回,温度值有正负,所以返回值类型为 int。
2 u! P+ c( t9 h8 @# X
主函数 编写好内部温度传感器初始化和温度读取函数后, 接下来就可以编写主函数了,代码如下: - /****************************************************************& m2 V1 ?! h# u0 N0 e0 r
- * 函 数 名 : main; d5 `1 n8 i" p" P; `
- * 函数功能 : 主函数! i- v+ h# f2 r% o* G, L" {
- * 输 入 : 无$ O- D h% h& |& [2 F
- * 输 出 : 无
; C* }2 Z1 ^* k: T" J - *****************************************************************/5 g4 t4 E" |* o) f
- int main()
; I. v' k- R# m - {
, Q3 d4 F6 W! ]- m) a1 p - u8 i=0;$ @1 c- E- m3 o) X
- int temp=0;
& J1 e, p! J H1 } - SysTick_Init(72);
5 m! f: J# z" r - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组3 a" ^/ ~! k) E& _; h) B/ t, E
- LED_Init();
0 R$ c( p' G4 m4 T, L9 y" n - USART1_Init(9600);& x( B( R& Z& T( x( L6 G, L% r
- ADC_Temp_Init();1 f- @: W0 s- N
- while(1)
* u. u! _" N& i" v/ z/ R - {$ R6 U% a4 o9 R# ]
- i++;, j4 a) \$ d: j( j; {3 l7 a
- if(i%20==0), L ` ~3 Z, {, ~
- {( P. z) N, d0 M8 e$ B
- led1=!led1;% @) @3 z8 { f7 S1 v: ^4 {4 A1 |
- }
" F N, e: t% [- _: M - if(i%50==0)
/ Y9 _% y# t q1 _ - {8 S( K1 ]- m) y5 H
- temp=Get_Temperture();
6 U* e! t/ f- P: J& U; I2 O - if(temp<0)
' G* ], d; d; P( ?) P' a/ _3 p - {
( @9 x b6 s7 W+ `/ d - temp=-temp;' e" b9 R0 p* Y6 B
- printf("内部温度检测值为:-");% q2 v% b ]( f7 P
- }
: ^0 g9 z" J) s' r0 R( r - else6 ?% [9 {9 R$ `
- {7 l% \0 \! t) U& L# S1 c6 C* C
- printf("内部温度检测值为:+");5 G: |. B; L3 f+ z
- }
4 Y" D t( j/ {) ~; @5 [ - printf("%.2f°C\r\n",(float)temp/100);
" j, G: e& L8 h* {% U - }' ^- Y* t6 y0 J: K" e
- delay_ms(10);
9 d* N8 S9 L# H8 m3 } k" k1 [) B* `( P - }$ j0 P3 |) J& |( M
- }
复制代码: Y: c- B: ~, V9 \8 S( v' M
主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括SysTick 系统时钟,中断分组,LED 初始化等。然后调用我们前面编写的 ADC_Temp_Init 函数,最后进入 while 循环,间隔 500ms读取一次温度,判断读取的温度是正温度还是负温度,最后打印温度数据,在输出温度数据时,要记得除以 100,因为读取的温度值是放大了 100 倍的。D1 指示灯会间隔200ms闪烁,提示系统正常运行。 将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。串口不断打印读取的温度数据,如果想在串口调试助手上看到输出信息,可以打开“串口调试助手”,首先勾选下标号 1 DTR 框,然后再取消勾选。这是因为此串口助手启动时会把系统复位住,通过 DTR 状态切换下即可。然后设置好波特率等参数后,串口助手上即会收到 printf 发送过来的信息。(串口助手上先勾选下标号 1 DTR 框,然后再取消勾选)如下图所示:
- C$ ~. c' \8 I- i- W8 ` 注:由于芯片工作会发热,所以内部温度传感器检测的温度通常会高于实际温度,这也是不使用芯片内部温度传感器来检测环境温度的原因。 ' |+ v) C3 [2 q: @+ x
|