|
内部温度传感器简介测量的温度位置 内部温度传感器集成在芯片中,测量的是芯片的温度。 ( ~7 N2 E( ]: m: B4 h2 a
如何测量对应位置的温度?
; w7 U, Q( \5 d5 J
* H1 J( s4 K+ V' P& \" N4 j2 i2 \+ X 温度传感器与ADC1_CH16相连,另外ADC1_CH17是与内部参照电压VREF+相连,因此我们可以通过ADC1的第16路通道测量芯片温度实时对应的电压转换得来的数字量,也可以通过ADC1的第17路通道测量内部参照电压对应的数字量。 / Q6 q0 F; f9 \& {5 a- D" t
2 W+ J8 x. j* U7 K. U
我们知道STM32的ADC转换DATA是12Bits的,因此输入电压(小于3.3V大于0V)ADC转换为数字量的值为“大于0小于4096”。 ! ^- ]! j7 V" v! n# [4 `3 z
! S4 T9 a; m- ]$ Z8 J
我们由“T-V关系图”,“V的数字量”和“ADC量程”,可以得知“此时的温度”。
2 \( E7 O- C7 C9 e0 T4 s. o; `8 B7 h

5 S% D$ w/ h- v2 u& o
内部传感器配置注意事项. G Z# [, k( B* y% l
4 h* k9 J6 P/ s' R% n, `① 读取内部温度传感器数据的周期应大于17.1us; ② 内部温度传感器的温度测量误差约为1.5℃,因此内部温度传感器更适合于检测温度的变化,而不是测量绝对的温度。如果需要测量精确的温 度,应该使用一个外置的温度传感器。
% n7 |- G2 H7 [+ z3 p. Z4 `9 ~
- @- q0 @, i+ v* ` 内部温度传感器配置流程
; a: [: o: _5 @# I2 _$ R4 O
9 J' G! O: S/ x1 a% p所属函数位置 | 执行步骤 | ADCx的初始化函数 | 使能相应总线上(APB1或者APB2)ADCx与GPIOx的外设时钟 | 配置GPIOx口中ADCx对应的引脚为模拟输入模式(ADC外设对应的GPIO模式) | 将ADCx复位为缺省值(就是将ADCx的所有设过的属性全部重设为默认值) | 设置ADCx时钟分频 | 将ADCx的校准寄存器恢复为默认值(此时应用对应的标志位来判断此步过程是否完成,如果完成再执行下一步) | 将ADCx的校准寄存器初始化,开始校准(此时应用对应的标志位来判断此步过程是否完成,如果完成再执行下一步) | 使能ADC1_CH16对应的内部温度传感器 | 使能ADCx外设 | ADCx_CHy通道数值读取函数 | 配置ADCx_CHy(ADCx对应的y通道)的转换序号与转换周期 | 启动ADCx_CHy继续转换(此时应用对应标志位判断ADCx外设所有通道是否转换完成) | 读取ADCx_CHy通道的数字量的值 | ADCx_CHy通道读取的数据的后期处理函数 | 对n次读取的数据进行取平均值处理 | 温度与电压数字量之间的转换函数 | 用温度与电压数字量之间的对应关系进行转换 | / R) x) e% f1 @2 }0 d( {* g% k+ `9 \
内部温度传感器实验代码解析ADC初始化代码! g; q' Q' W) S
1 \) G. V& Y; z6 u& e2 P
6 X* d2 U: M+ ~3 n' I# G5 v: \' f- void ADC_InitConfig()
7 d; h1 }4 @5 G* Y7 Z$ m, t - {
$ B8 F; r& P6 F( ~, o- z - ADC_InitTypeDef ADC_InitStructure; ) c6 J$ ?( A' `, N
- " Q' j8 Z8 s6 m# t2 O7 S
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
) {$ j$ h) z& ^5 W4 }1 O' {0 h' l - ) n/ Q4 @/ ~) A& Y4 O- n i
- ADC_DeInit(ADC1); 5 k" t2 N5 H- a/ g( [
- RCC_ADCCLKConfig(RCC_PCLK2_Div6);
$ A6 B, r" @* O o9 ]' v4 y6 Y -
3 {; g) A- z$ B4 } - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
i4 j( |9 D/ r) I! S) C8 w - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Left;
0 \1 ^4 _' r) Z: Q% @8 g9 r8 ] - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; + [ a* O( R! k( d& U! c
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
7 ~2 d# m. k9 F7 j$ i - ADC_InitStructure.ADC_NbrOfChannel = 1; // 定义规则通道的长度
; N' r; I. ?& {) {1 b5 b2 Z+ o - ADC_InitStructure.ADC_ScanConvMode = DISABLE; 8 b9 Z- l6 `4 b
- ADC_Init(ADC1,&ADC_InitStructure);
* l, j7 {+ H; I2 _" C# ~# y8 Q -
1 @7 t& K! L3 Z. e) Y/ D - ADC_TempSensorVrefintCmd(ENABLE); // 使能内部温度传感器 W5 ~1 f( ^/ a9 K0 E! T
- - `4 }6 y1 i P9 N8 r0 |
- ADC_Cmd(ADC1,ENABLE); // 使能ADC1
$ u2 b& Y+ @# z: n2 ]! V) z - 9 D! w4 E1 a) v
- ADC_ResetCalibration(ADC1); // 开始ADC1的校准寄存器复位
3 P6 V" q% C, [$ ^7 ?6 L8 F$ b+ ] - while(ADC_GetResetCalibrationStatus(ADC1));
3 y3 s: x! h7 K7 ^0 J! q% [. | -
( s% ~- `2 c0 T' N. f+ _; @- h* b - ADC_StartCalibration(ADC1); // 开始ADC1的自动校准功能 , {4 h5 ?& ?2 |& x
- while(ADC_GetCalibrationStatus(ADC1)); ; Q. K! V3 t9 w, N: U
- 8 w4 k, x. `0 i. i# {% C
- delay_init(); 3 `: S) ]. F* k8 n/ P
- }
复制代码
( ^5 @4 Q, Q" I/ v我们应该注意到:ADC1_CH16连接着内部温度传感器,不用初始化具体的GPIO口引脚,只需要将寄存器的相应位使能即可:
/ S9 r4 }% h6 f4 W: v. w( B/ A$ q
; y. b& D) Y5 Q) J9 _# q; [
我们有些同学在用如下“等待”代码的时候有些疑问: " }6 p+ Z3 {1 u, A6 w& t
- % X6 }) k7 z9 u5 A0 Y1 a& O
- ADC_ResetCalibration(ADC1); // 开始ADC1的校准寄存器复位 * {7 M% F8 Q1 Z7 k( A
- while(ADC_GetResetCalibrationStatus(ADC1));
9 j0 U6 g- V$ e+ E( K. G/ s - ( d$ E% L! `6 H; \* w- j3 z" u
- ADC_StartCalibration(ADC1); // 开始ADC1的自动校准功能 % f1 j9 R' a* ~) j+ p
- while(ADC_GetCalibrationStatus(ADC1));
复制代码
/ |% h* }$ X' t$ n2 R3 y- c/ s我们用while循环为ADC外设提供执行任务所需的等待时间,但是我们一对比如下while代码,就有点懵了:
: R! X. X( B; l) [; U2 X1 r
- ( v) g1 Q, ]2 J. w) j6 D0 G% o
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
! F0 m0 E$ A5 S - ! O" }0 p6 V1 Z0 f
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
复制代码
S+ z' N+ C0 ^: D
. t3 X- j9 v# ^! Q: m( n- Z$ j
( F, v" A4 [% D3 a _在while循环中,应该“取非”?这个是个让人不解的问题。 我们此时应该看寄存器,看寄存器中位与状态的对应关系: ADC1复位校准功能与ADC1执行校准功能分别对应“ADC控制寄存器 2(ADC_CR2)”对应的如下两个位: 0 ~) C6 O# w6 e8 Z% r$ h) @
0 A' n- m2 n/ B/ S4 _7 o% k
5 ^" o& x5 _9 {% J# w
我们看到上图,可以得到如下结论: 8 S# V5 W1 U: k1 z0 U) m
功能 | 状态 | 位标志 | 检验ADC校准功能复位状态(完成/进行中) | 复位执行中 | 1(SET) | 复位完成(复位指令执行完毕) | 0(RESET) | 检验执行ADC校准功能的状态(完成/进行中) | 正在开始执行ADC校准功能 | 1(SET) | ADC校准功能执行完毕 | 0(RESET) | * @/ M& a+ \) s/ z
此外,ADC_SR状态寄存器中的标志位EOC位代表着“ADC转换工作是否结束”:
功能 | 状态 | 位标志 | 查看ADC转换是否结束 | ADC转换结束 | 1(SET) | ADC转换完成 | 0(RESET) | " Q3 q0 K& x4 k# A: X( H3 k8 d9 t
DC转换函数
( N+ g. u$ v0 J3 j7 F* C% ~! d O8 Q; Y$ |
- & C z9 a, [6 `- |- K. g0 A: d' |
- u16 ADC_GetValue() 8 P5 j: s4 b2 S* Y. K, Q* l0 V: ~5 W; B
- { ' T0 o8 [' M! |( c
- ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道3,第一个转换,采样时间为239.5周期
3 a5 V5 m0 \( |: B/ W& ]5 {& s -
. ]7 H5 P% J: t$ \/ t* E - ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 5 ^6 ~5 a) s2 \, r: \
- 2 N& U3 b0 o' n# i6 o
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束 , Y4 F8 P5 y# c2 X8 Q6 h! Z
- 1 \: R; F$ m4 c+ O* p2 p0 I9 e
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
/ G1 F: m6 Z9 u; U+ e( \% H - }
复制代码 $ u* s0 m+ z5 O5 Y9 x+ ^6 m K
4 [- d( n& l9 Z
注意:我们一定要在读取ADC通道转换值得函数中去执行: / X8 e5 q5 B' I. ]. c# N
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 . r' ~* M, G8 H+ A
- * e5 ^8 x; @+ j& W' c1 i
- while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
复制代码 , z2 i9 k/ V5 w! {. U: V4 m1 w
我们初始化时只想初始化ADCx设备的属性,而函数“ADC_SoftwareStartConvCmd”得功能是根据ADCx各个引脚的属性启动ADCx外设进行转换操作。显然,初始化时不应进行获取转换值得操作。
1 V" r2 }& _+ J6 J0 A% R
9 d( a; I* }7 y1 n0 W 多次读取取平均函数, l r- j, g) p5 A$ r7 U3 _
/ U$ v# j/ v5 q, n' x
- ! X1 M ~/ @; s5 e( Z- q$ _
- //获取通道ch的转换值 5 E4 }$ _% [- ?5 e `
- //取times次,然后平均 : E. @1 R: R' d3 z: ~) u9 `1 m
- u16 T_Get_Adc_Average(u8 ch,u8 times)
2 {" I( e, w: }4 a. r6 N; j) l - { 6 i u' Y9 i- H- k
- u32 temp_val=0;
" f: |1 `. H" F8 g# h - u8 t; 1 C5 k* R5 `9 s8 v; ?5 X
- for(t=0;t<times;t++) ! z8 @4 h' o. h# q
- { ) T0 ` O. \( i0 A- k! c( Z
- temp_val+=T_Get_Adc(ch);
: K, M" e/ `+ G+ m! v- } - delay_ms(5); z0 g6 q2 R+ ?) A; h' }" ]
- }
/ L' C- f, @) M0 \( O: ~" a - return temp_val/times; 4 U' o; I- e i& m" g3 g9 s
- }
复制代码 + }: e5 g3 I- S: m* N7 y9 \
7 v; a; ^1 D8 k: M. g. A
ADC转换的数字量转换为所需温度的函数
3 h# B% x4 R# \+ m+ S/ i2 O/ {$ ^7 i4 M$ v: t5 k
$ H- ]* n3 J1 }% W, |7 l6 k- * `! @8 V0 m2 I& u1 v. O
- //得到温度值 + H7 o/ _+ g+ Y% Z! B' a# g
- //返回值:温度值(扩大了100倍,单位:℃.) ' @9 y' V$ H. p; f8 m
- short Get_Temprate(void) //获取内部温度传感器温度值
: p+ J X& X& I( ]- [" P3 X+ `; Q - { & d c' G, q7 m6 @/ H! B
- u32 adcx; 4 i; b' z2 k. t3 G
- short result;
1 J! X: o4 p# B y - double temperate;
& X1 F' n9 E% s& R - adcx=T_Get_Adc_Average(ADC_Channel_16,20); //读取通道16,20次取平均 ' V3 s) k; C* f% _/ ~5 e/ T6 H$ @# z* `
- temperate=(float)adcx*(3.3/4096); //电压值
# v& c9 T+ }( H8 [4 k6 v2 ^9 l, c - temperate=(1.43-temperate)/0.0043+25; //转换为温度值 ; t; a- U/ X1 L! F7 M h
- result=temperate*=100; //扩大100倍. % u" y% l# O* r' I: J2 Z6 Z/ z
- return result;
$ D) X% d' P# f* F5 ~) I: {' H - }2 Z1 X. [* |8 n* N" @5 J: R
-
复制代码 & n j3 A. `* ]
$ M" s- h/ G" \/ m9 W9 T0 G k. J
/ B, n$ r$ S8 Q$ d总体代码示例TempSensor.c7 X; F L& E9 o
& s7 e+ b/ k& v' t- ) |7 @# i& o8 w2 C/ E) o
- #include "TempSensor.h"
1 c, h8 K$ b! R- S - #include "stm32f10x.h"
% Y* }' D2 X0 n- v% \$ ?' P - #include "delay.h" & |3 P& W- l5 Q' k' F
- 3 d6 {9 q# X9 s$ F' t
- void ADC_InitConfig()
, b6 [9 K1 N7 A9 j' V( q% x4 a - {
) j7 s- G5 `. t% O* a! e - ADC_InitTypeDef ADC_InitStructure;
, ^: ?& U! y0 }8 M3 @* ?( g9 c! p -
7 W5 R, W& n) b8 M6 T8 w( `3 C" I - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
& s4 X9 x: H* H1 z8 M1 f - $ h4 [1 ^3 }, M' A' `
- ADC_DeInit(ADC1);
7 m, m; T2 A5 v4 O- q! l/ m8 E - RCC_ADCCLKConfig(RCC_PCLK2_Div6);
9 g! s$ _- t, b1 R - : A5 Q- ~0 }0 `) c
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; - J! L$ M3 Z* t& j6 R4 o+ f
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Left; 1 E6 }9 Z9 e7 y6 S
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
8 C& t& t q1 k1 w$ j9 a2 ^6 { - ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ' |+ V4 r8 O6 P8 Z4 ]/ S- ^) ^
- ADC_InitStructure.ADC_NbrOfChannel = 1; // 定义规则通道的长度 h: S/ K9 J4 s4 i2 e" x
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; / n5 q6 D7 i" m/ n6 V- _
- ADC_Init(ADC1,&ADC_InitStructure); $ o. w- ?* h" @. v) b; w
-
$ j- T" S5 J: `* \5 O - ADC_TempSensorVrefintCmd(ENABLE); // 使能内部温度传感器 7 H8 Z9 U( Z) o4 n. C, S D( m
-
! a& m9 Y# ^5 K- ]7 J* p - ADC_Cmd(ADC1,ENABLE); // 使能ADC1 / e* |' g" h5 H( f" z, P( Q
- ( X+ j, {6 M0 _: c; H5 w
- ADC_ResetCalibration(ADC1); // 开始ADC1的校准寄存器复位 # b' w0 X0 [' F; E
- while(ADC_GetResetCalibrationStatus(ADC1));
5 v8 f& ^8 C9 _& j; l- j" [ - 1 H* n# r" @7 F5 x+ [/ W
- ADC_StartCalibration(ADC1); // 开始ADC1的自动校准功能 # }- x& `2 H9 B1 I/ E
- while(ADC_GetCalibrationStatus(ADC1)); " ]% R. G# [' X3 v3 E( v; ?
-
4 b0 |6 s) [) {) G$ U, |# s - delay_init(); 9 ?! b" Y7 U! P' I% @
- } : X& ~' s3 v* @1 g1 w* V( Y: E
-
( y7 C" e9 m( w - u16 ADC_GetValue() : v% O( F1 N0 G! I
- {
' N9 r1 P o/ K0 e$ j' c2 U - ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道3,第一个转换,采样时间为239.5周期 2 x* \6 `+ u8 e8 ]! R/ z
-
" M/ w a8 [) q+ R$ h e$ x - ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
. d& l {% c" [# ~. n; E# `" c -
; m0 A! P& y1 G _. u; n6 e - while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
; I( v) T' l$ A. _0 h -
" |+ u/ \% c% h6 a - return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果 , Z/ O6 ~$ y/ g
- }
. l# u5 l4 }1 w% G1 O -
* C! n8 W9 b$ M/ P - u16 ADC_GetAverageValue()
8 i: G+ e2 J% W) r$ S# J' g - { % k; h4 T1 a# o
- u8 i = 0;
7 }; y* ]0 Q4 r! ?% s - u16 temp = 0; 9 Q1 F- o3 ^* I' L9 h
- / v# c, |* [8 ^8 l- ^
- for(;i<5;i++) M, ?' h- ~: G
- { " V# J& {. L/ x/ W( C* n1 ?
- temp += ADC_GetValue(); 9 m" z) v. W6 t' h o, [
- delay_ms(5);
_3 g5 X3 ?5 F9 Y& o - } 5 V# r9 Y! X$ ~, G; F9 M; d
- return temp/5; ; L/ W7 X" ?* @% g. _- J" Z
- }
& @5 D- l9 }. e -
|% i+ h# O0 k# C1 n+ V - u16 DigitalValueConversion()
- t1 N( N( R9 N: V8 a! b, y+ p - { 3 S8 J8 C4 y' N
- float temp = ADC_GetAverageValue();
/ U3 e& @5 u4 A# h* K - return ((1.43-3.3*(temp/4096))/0.043+25)*100;
, i- Y( X7 A) I8 \! V4 f - }
复制代码 * ~# Z2 ~, h2 p9 D; f5 R0 A2 x
. V5 c1 Q. ]; \6 J5 f! o
TempSensor.h# @8 l% e0 l/ n
4 d$ O3 s# b) |9 r% x- $ G- c* p2 z* G
- #ifndef _TEMPSENSOR_H , K ?; `0 w+ f7 W( G7 z% d8 x
- #define _TEMPSENSOR_H 8 O9 B2 G' v: Y7 T0 |: g/ j: V
-
3 k, E& P# C z& S+ u! Q - #include "sys.h"
# [) f9 Z9 d" z+ Q% n* e - 9 p3 D9 _1 x- w' A
- void ADC_InitConfig(); : B/ u! V0 K/ g& ~3 d$ i
- u16 ADC_GetValue(); 5 |6 Z* e7 H6 g$ D5 i/ Y
- u16 ADC_GetAverageValue(); Y) Q; z$ S, Y# n2 ~5 A/ |& j
- u16 DigitalValueConversion();
$ q8 Q+ r. T- o' g -
5 s/ n3 T5 [" v2 W - #endif
复制代码
; ]8 V `# P& a3 f2 u1 |9 ]+ V
8 M8 W( Y" b3 v9 I# [+ `Main.c# c# d4 b! z) K5 ?& _ Z
% z, o; a. ]- J z7 @- R" C w- #include "TempSensor.h" . @3 q( d/ i$ L$ g( t; R
- #include "stm32f10x.h" ( K9 n" P: G! G5 X" {. `9 R4 h
- #include "usart.h"
1 r6 J: W$ H- F - #include "delay.h" 9 h5 n2 q; U2 L9 k
- #include "lcd.h" 6 J8 ^* o4 v/ ^2 `3 t6 ?4 y, T
- ; ^9 `0 Z+ C U% a7 K
- int main() * @1 j# X- f0 ~/ x- x4 p
- {
; k8 G. S1 e l8 P - u16 temp = 0;
8 \. p; E- \1 N - ( Q$ c( \- @) K. M
- ADC_InitConfig();
4 s) P( P1 n$ _ H4 x3 u - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); ; S+ {" I, f3 x0 R/ {+ B
- uart_init(115200);
# Q1 L' K; ?3 N1 ] - delay_init();
' E& [2 v) R2 S/ D {5 B; W7 b - LCD_Init();
. Q; V. {' v0 k5 t( y! K - LCD_Clear(BLUE); ; X& E v# L3 x* ^
- ' a* ?# j8 C, i* N, ]6 ~" N8 w
- temp = DigitalValueConversion();
% z o% p7 Q, T g# [4 V' Y6 c -
" I! b/ ]! e! M+ {4 |& c: F0 ^4 @ - POINT_COLOR=RED;//设置字体为红色
( I _! E2 A" k" k+ m: w" Z - LCD_ShowString(30,50,200,16,16,"WarShip STM32");
4 ^8 I$ j, K ?7 ?0 ^ - LCD_ShowString(30,70,200,16,16,"Temperature TEST"); ) _( V5 G& k8 z
- LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK"); 6 \4 B x8 K3 }( X; c, Y" C9 S
- LCD_ShowString(30,110,200,16,16,"2015/1/14");
3 D, `% _" V! i, x% s' O7 w' H# q - LCD_ShowString(30,140,200,16,16,"TEMPERATE: 00.00C"); 3 O/ ~0 [) ?+ D$ k$ r X
-
: Z9 q' @7 z; C1 X0 l - while(1) ' W8 y. R7 u! d( G' l; w9 q' I
- {
9 j' M9 O8 i/ L2 ]9 H9 G - temp = DigitalValueConversion(); % G6 S& N/ p3 v. N5 S' n
- LCD_ShowNum(30+11*8,140,temp/100,2,16); # L5 @7 u, N7 `/ t2 Y
- LCD_ShowNum(30+14*8,140,temp%100,2,16); . W/ y% q: Y3 J8 G* e* y$ m, Q$ y' X
- delay_ms(150); ) T/ _2 q# z0 m% s2 f% T
- } 2 O& E" u: x# ^- s9 P; P3 q+ T
- }
复制代码 ) `) L* v4 Z! Q
0 t, X7 r) _# ?! u
程序运行结果
2 z; m% n2 l5 H$ G1 C1 f$ c
$ s; C1 i F# a* c1 B, g" m
$ K. ?" h9 f; |* u/ r: s! k, x' F B o( k* O7 W5 J4 m
& c' N4 u) V ^0 _4 l! u5 ^3 l$ T |