本项目设计内容涉及:传感器、嵌入式系统应用、模拟电子技术。
课程:《智能传感技术》
指导老师:覃园芳老师
一、任务要求
设计PT100温度传感器的信号采集电路,使用嵌入式系统实验板采集外部的温度并显示。
要求:
- 范围:
20
20
80
℃
80℃
- 精度:与标准仪器比较小于
0.3
℃
0.3℃
-
30
30
50
50
70
℃
70℃
注:本次设计采用的主板芯片为STM32F103RCT6
二、硬件设计
1、分析所要使用的PT100温度传感器。
结合有关PT100的资料,可得几个重要知识:
- PT100的阻值会随温度的变化而成正比变化(温度越高阻值越大),但阻值变化很小,约等于
0.385
Ω
/
度
0.385Ω/度
- PT100的测温范围是
﹣
200
℃
﹣200℃
150
℃
150℃
100
Ω
100Ω
- PT100的工作电流要小于
5
m
A
5mA
- PT100的阻值虽然随温度的变化而成正比变化,但在不同温度区间内其变化的速率(也就是
K
值
K值
2、设计PT100驱动电路。
结合所学经验,开始有想法:
如果把PT100当电阻串联在电路中,并用主板上STM32F103RCT6芯片的PA1引脚的ADC功能去读取电压值并进行温度转换,可得出温度,如图1所示:
图1 PT100串联电路测AD法
但结合资料,这种想法秒速推翻。
常温(
25
℃
25℃
25℃)水中的PT100的阻值大概在
109.89
Ω
109.89Ω
109.89Ω左右。
假设主板给PT100的供电是标准的
3.3
V
3.3V
3.3V直流电,并且通过PT100的电流也是其最大
5
m
A
5mA
5mA直流电流,那么在此刻,PT100所要分掉的电压约为
109.89
∗
0.005
=
0.54945
V
109.89*0.005=0.54945V
109.89∗0.005=0.54945V
将其根据AD转换的换算公式换算成AD值大概为
0.54945
/
3.3
∗
4096
=
681.98
≈
682
0.54945/3.3*4096=681.98≈682
0.54945/3.3∗4096=681.98≈682
当温度上升一度,假设PT100的阻值刚好上升了
0.385
Ω
0.385Ω
0.385Ω,那么其分掉的电压的变动值约等于
0.385
∗
0.005
=
0.001925
V
0.385*0.005=0.001925V
0.385∗0.005=0.001925V
将其根据AD转换的换算公式换算成AD值大概为
0.001925
/
3.3
∗
4096
=
2.39
≈
2
0.001925/3.3*4096=2.39≈2
0.001925/3.3∗4096=2.39≈2
0.5
℃
/
A
D
0.5℃/AD
0.5℃/AD ?好像还不错,但任务要求所设计的PT100电路测温得出的结果,精度要小于
0.3
℃
0.3℃
0.3℃。
0.3
℃
0.3℃
0.3℃是什么概念?换算为PT100的阻值变化,也就约为
0.1155
Ω
0.1155Ω
0.1155Ω,那么其精度下,PT100变化
0.1155
Ω
0.1155Ω
0.1155Ω阻值,要变化的电压约为,将其根据AD转换的换算公式换算成AD值大概为,连
1
1
1个AD值都不到,无法达到这个精度。
所以,这种方法被推翻。
直流电桥(微小电阻变化转微小电压变化)
找到了关键问题所在,就是PT100的随温度的变化而成正比变化太小了,只要把这种变化变大,大到很明显,就解决了问题。
电阻的阻值是没法调大的,只能通过电阻变化导致的电压变化调大,就可以解决变化小的问题。
结合所学知识,只要将微小的电阻变化转化为微小的电压变化,再将微小的电压变化转化为大的电压变化,也就是用直流电桥配上一个放大电路,就很完美地解决这个问题。
如图2为一个直流电桥,R1、R2、R3、R4为该直流电桥的桥臂,且R1、R2、R3、R4皆为固定电阻,U1为该直流电桥的供电电源,U2为该直流电桥的输出电压。
图2 直流电桥
结合所学的直流电桥知识,可得公式:
U
2
=
U
1
∗
(
R
1
R
1
+
R
4
−
R
2
R
2
+
R
3
)
U2=U1*(\frac{R1}{R1+R4} -\frac{R2}{R2+R3})
U2=U1∗(R1+R4R1−R2+R3R2)
当
R
1
∗
R
3
=
R
2
∗
R
4
R1*R3=R2*R4
R1∗R3=R2∗R4,该直流电桥会达到平衡,带入公式可得输出U2的值为
U
2
=
U
1
∗
0
=
0
V
U2=U1*0=0V
U2=U1∗0=0V 也就是该直流电桥在平衡状态下,输出电压为
0
V
0V
0V;
当
R
1
∗
R
3
≠
R
2
∗
R
4
R1*R3≠R2*R4
R1∗R3=R2∗R4时,该直流电桥的平衡会被打破,带入公式可得输出U2的值。
利用直流电桥不平衡状态下会输出电压的特性,应用不平衡电桥之一——单臂电桥,来把微小的电阻变化转化为微小的电压变化。
如图3为一个直流电桥,R1、R2、R3、R4为该直流电桥的桥臂,且R1为可变电阻,R2、R3、R4为固定电阻,且
R
3
=
R
4
R3=R4
R3=R4,U1为该直流电桥的供电电源,U2为该直流电桥的输出电压。
图3 直流单臂电桥
该单臂电桥应用于本次设计,R1为PT100,R2为与PT100接近的阻值,这样可以使得在一定(温度)情况下(PT100的阻值等于R2的阻值)电桥可以达到平衡,使得单臂电桥的输出趋近于
0
0
0。
又因为通过PT100的电流不能大于
5
m
A
5mA
5mA,不然可能会烧坏PT100,所以R3与R4的阻值虽然要取相等,但又要把电桥电流稳定到
5
m
A
5mA
5mA以下,所以R2和R3要给PT100分流。
本次设计,实验板可接出供电为
3.3
V
3.3V
3.3V和
5
V
5V
5V两种,为了提高精度,也就是提高单臂电桥的输出电压的范围,故选择5V供电。
在先前的计算中,常温(
25
℃
25℃
25℃)水中的PT100的阻值大概在
109.89
Ω
109.89Ω
109.89Ω左右。假设主板给PT100的供电是标准的
5
V
5V
5V直流电,并且通过PT100的电流也是其最大
5
m
A
5mA
5mA直流电流,那么在此刻,PT100所占电压最大为
109.89
∗
0.005
=
0.54945
V
109.89*0.005=0.54945V
109.89∗0.005=0.54945V 也就是说R3和R4至少要分掉的电压为
5
-
0.54945
=
4.45055
V
5-0.54945=4.45055V
5-0.54945=4.45055V
设
R
1
=
R
2
=
109.89
Ω
R1=R2=109.89Ω
R1=R2=109.89Ω,在此刻,
5
V
5V
5V通电的电桥为一个并联电路,可变形为图4所示的电路:
图4 PT100单臂电桥变形图
又R3和R4至少要分掉
4.45055
V
4.45055V
4.45055V的电压,所以R3和R4的阻值至少为
4.45055
/
0.005
=
0.5
V
4.45055/0.005=0.5V
4.45055/0.005=0.5V
又由于本次设计PT100用来测量
20
℃
20℃
20℃ ~
80
℃
80℃
80℃的水温,水常压下的极端液态也就是
0
℃
0℃
0℃。
重新计算PT100将分掉的电压,PT100的阻值放入
0
℃
0℃
0℃的水中,其阻值为
100
Ω
100Ω
100Ω左右。假设主板给PT100的供电是标准的
5
V
5V
5V直流电,并且通过PT100的电流也是其最大
5
m
A
5mA
5mA直流电流,那么在此刻,PT100所占电压最大为,在此刻,R3和R4又至少要分掉的电压为
5
-
0.5
=
405
V
5-0.5=405V
5-0.5=405V 所以R3和R4的阻值至少为
4.5
/
0.005
=
900
Ω
4.5/0.005=900Ω
4.5/0.005=900Ω
所以R3和R4的电阻取值,在理想状态下至少是
900
Ω
900Ω
900Ω,但现实中没有刚刚好的阻值可以挑,那就可以选择比该值大的电阻,例如
1
K
Ω
1KΩ
1KΩ,或者是比
1
K
Ω
1KΩ
1KΩ再大的,但是如果选用的电阻太大,则分压过多,依旧导致电桥的输出范围越来越少,最后使得微弱的电压变化变得更加微弱,甚至是不变化了,而且电阻的型号选用金属膜电阻,因为其体积小、噪声低、稳定性好,选它很不错,误差1%可接受。
又因为R2要与PT100趋近,所以对水的温度测量中,温度变化最大为
0
℃
0℃
0℃ ~
100
℃
100℃
100℃,所以PT100的电阻变化约为
100
Ω
100Ω
100Ω ~
139
Ω
139Ω
139Ω,因而要使得电路达到最佳的变化R2为
150
Ω
150Ω
150Ω的可变电阻最佳,又可调精度要非常高,所以要选用精密电阻。
但现实中没有刚刚好阻值的精密电阻可以挑,那就可以选择比该值大一点点的精密电阻,所以3296w不错,多圈式精密可调,例如3296w电位器201(
200
Ω
200Ω
200Ω)。
所以本次设计选用的R3和R4为
1
K
Ω
1KΩ
1KΩ,单臂电桥部分的设计完成,如图5所示。
图5 PT100单臂电桥部分
差分放大电路(放大微小的电压变化)
接下来就是放大电路部分,通过放大电路来实现把电压的微小变化扩大,使用模电所学过的差分放大电路(减法电路),可以把电桥的输出进行减法运算,并增益放大输出。
当电桥平衡,电桥输出电压电势相等,差分放大电路的减法运算的结果为
0
V
0V
0V;
当电桥的平衡被破坏,电桥输出电压电势拉开,此时差分放大电路的减法运算即可有一定的差值,放大器对其进行放大到合适的差值变动并输出,即当电桥输出最大时,经过差分放大电路,放大到我们所需的最大差值电压
0
V
0V
0V ~
3.3
V
3.3V
3.3V。如图6为一个差分放大电路:
图6 差分放大电路
△假设U1 = U2:
①因输入端U1、U2的电流趋近相等,为“虚断”特性,同相输入端为高阻态,其输入电压值仅仅取决于R2、R4分压值。同相输入端的电压可以看作成为输入端比较基准电压;
②因输入端U1、U2的电压趋近相等,为“虚短”特性,进而又推知其为反相输入端,即R1、R3串联分压电路,这是反馈电压。放大器的控制目的是使反馈电压等于基准电压;
③由R1=R2,R3=R4条件可知,放大器输出端OUT只有处于“虚地”状态,即输出端OUT为0V,才能满足反馈电压等于基准电压,这可以由此导出差分放大器的一个工作特征。
△假设U1 ≠ U2,例如U1 > U2:
①此时因同相输入端电压高于反相输入端,输出端电压往正方向变化,其R1、R3偏置电路中的电流方向为R3→R1;
②由R1、R3的阻值比例可知,R1两端电压降为
(
同
向
输
入
端
电
压
)
-
U
1
(同向输入端电压)-U1
(同向输入端电压)-U1 则R3两端电压降为
[
(
同
向
输
入
端
电
压
)
-
U
1
]
∗
(
R
3
/
R
1
)
[(同向输入端电压)-U1]*(R3/R1)
[(同向输入端电压)-U1]∗(R3/R1) 输出端电压为
(
同
向
输
入
端
电
压
)
+
[
(
同
向
输
入
端
电
压
-
U
1
)
∗
(
R
3
/
R
1
)
(同向输入端电压)+[(同向输入端电压-U1)*(R3/R1)
(同向输入端电压)+[(同向输入端电压-U1)∗(R3/R1)
③若此时的输入电压差为
U
1
-
U
2
U1-U2
U1-U2,输出电压为
X
∗
(
U
1
-
U
2
)
X*(U1-U2)
X∗(U1-U2)。则该差分放大器的差分电压放大倍数为
R
4
/
R
3
=
X
R4/R3=X
R4/R3=X 可得该差分放大电路的放大倍数是
X
X
X倍。
△假设U1 ≠ U2,例如U1 < U2:
①此时因同相输入端电压低于反相输入端,输出端电压往反方向变化,其R1、R3偏置电路中的电流方向为R3←R1;
②由R1、R3的阻值比例可知,R1两端电压变为
-
U
1
-
(
同
向
输
入
端
电
压
)
-U1-(同向输入端电压)
-U1-(同向输入端电压) 则R3两端电压变为
-
[
U
1
-
(
同
向
输
入
端
电
压
)
]
∗
(
R
3
/
R
1
)
-[U1-(同向输入端电压)]*(R3/R1)
-[U1-(同向输入端电压)]∗(R3/R1) 输出端电压为
-
(
同
向
输
入
端
电
压
)
-
(
同
向
输
入
端
电
压
-
U
1
)
∗
(
R
3
/
R
1
)
-(同向输入端电压)-(同向输入端电压-U1)*(R3/R1)
-(同向输入端电压)-(同向输入端电压-U1)∗(R3/R1) 结果是一个负电压。
所以故障维修的经验就冒出来了:
如果直接测量R1、R3串联电路的分压状态,只要R1、R3串联分压是成立的,则该差分电路就大致上就是好的,电路的电压放大倍数也由此得出;
只要测量输入电压差(R1、R2左端电压差),再测量输出端电压进行比较,则外围偏置电路的好坏,也会得出明确的结论。
所以公式就推出来了:
其输出公式为:
O
U
T
=
(
R
2
+
R
4
)
∗
R
3
∗
U
1
(
R
1
+
R
4
)
∗
R
2
−
R
4
∗
R
2
R
2
OUT=\frac{(R2+R4)*R3*U1}{(R1+R4)*R2} -\frac{R4*R2}{R2}
OUT=(R1+R4)∗R2(R2+R4)∗R3∗U1−R2R4∗R2
在实际应用中,一般使
R
1
=
R
2
R1=R2
R1=R2、
R
3
=
R
4
R3=R4
R3=R4化简电路,则
其输出公式为:
O
U
T
=
(
U
1
-
U
2
)
∗
R
4
R
1
OUT=\frac{(U1-U2)*R4}{R1}
OUT=R1(U1-U2)∗R4
当所前边所设计的电桥与差分放大电路结合起来,
U
1
-
U
2
U1-U2
U1-U2就为电桥输出的电势差,OUT输入到STM32F103RCT6实验板ADC1口,其最大可读取电压为
3.3
V
3.3V
3.3V,则电势差放大的倍数也就是
R
4
/
R
1
R4/R1
R4/R1即可进行计算了。
假设所设计的单臂电桥达到理想状态,最小电桥输出
U
2
=
0
V
U2=0V
U2=0V,最大电桥输出为
U
2
=
M
a
x
B
r
i
d
g
e
U2=Max Bridge
U2=MaxBridge
O
u
t
Out
Out。水温最高为
100
℃
100℃
100℃,最大电桥输出为PT100置于
100
℃
100℃
100℃时电桥的输出。
实测100℃时PT100的阻值约为
138.8
Ω
138.8Ω
138.8Ω,电桥所接输入电压为
5
V
5V
5V,
R
3
=
R
4
=
1
K
R3=R4=1K
R3=R4=1K,若要使得测温范围是从
0
℃
0℃
0℃ ~
100
℃
100℃
100℃,则精密电阻R2的阻值应该调为
0
℃
0℃
0℃时PT100的阻值,也就是
100
Ω
100Ω
100Ω,带入公式可得电桥的输出
U
2
=
U
1
∗
(
R
1
R
1
+
R
4
-
R
2
R
2
+
R
3
)
=
5
∗
(
138.8
138.8
+
1000
-
100
100
+
1000
)
=
0.154867963087
V
≈
0.155
V
U2=U1*(\frac{R1}{R1+R4}-\frac{R2}{R2+R3}) =5*(\frac{138.8}{138.8+1000}-\frac{100}{100+1000})=0.154867963087V≈0.155V
U2=U1∗(R1+R4R1-R2+R3R2)=5∗(138.8+1000138.8-100+1000100)=0.154867963087V≈0.155V
将其放大到ADC1最大可检测电压
3.3
V
3.3V
3.3V,所需的放大倍数为
3.3
/
0.155
≈
21.29
≈
21
3.3/0.155≈21.29≈21
3.3/0.155≈21.29≈21倍。
但是在现实中,
100
℃
100℃
100℃的开水降温速度很快,所以理想的倍数有点浪费,所以我们只测到
80
℃
80℃
80℃,常温下的冰水升温速度也很快,所以我们最小也只测到
20
℃
20℃
20℃,这就是设计条件的由来吧。
实测
80
℃
80℃
80℃时PT100的阻值约为
131.1
Ω
131.1Ω
131.1Ω,电桥所接输入电压为
5
V
5V
5V,
R
3
=
R
4
=
1
K
R3=R4=1K
R3=R4=1K若要使得测温范围是从
20
℃
20℃
20℃ ~
80
℃
80℃
80℃,则精密电阻R2的阻值应该调为
20
℃
20℃
20℃时PT100的阻值,实测约为
108
Ω
108Ω
108Ω,带入公式可得电桥的输出
U
2
=
U
1
∗
(
R
1
R
1
+
R
4
-
R
2
R
2
+
R
3
)
=
5
∗
(
131.1
131.1
+
1000
-
108
108
+
1000
)
=
0.0.2159735882
V
≈
0.092
V
U2=U1*(\frac{R1}{R1+R4}-\frac{R2}{R2+R3})=5*(\frac{131.1}{131.1+1000}-\frac{108}{108+1000})=0.0.2159735882V≈0.092V
U2=U1∗(R1+R4R1-R2+R3R2)=5∗(131.1+1000131.1-108+1000108)=0.0.2159735882V≈0.092V
将其放大到ADC1最大可检测电压
3.3
V
3.3V
3.3V,所需的放大倍数为
3.3
/
0.092
≈
35.87
≈
35
3.3/0.092≈35.87≈35
3.3/0.092≈35.87≈35倍。
接下来就要计算电阻取值了,因为
35
∗
R
1
=
35
∗
R
2
=
R
3
=
R
4
35*R1=35*R2=R3=R4
35∗R1=35∗R2=R3=R4,所以可以设R1、R2的阻值为
1
∗
X
Ω
1*XΩ
1∗XΩ,R3、R4的阻值为
35
∗
X
Ω
35*XΩ
35∗XΩ,只要算出
X
X
X即可算出全部的电阻,
X
X
X为该差分放大电路的输入电阻值,如图7:
图7 35倍差分放大电路
现在生产的运放,一般其输入阻抗都很高,所以运放输入端电阻选择余地比较大,但又为了减少偏置电流带来的影响,降低噪声和温漂的影响,输入电阻的取值一般选择在
10
k
Ω
10kΩ
10kΩ ~
100
K
Ω
100KΩ
100KΩ的区间。
本次设计所使用的运算放大器为LM358,其内部集成结构如图7所示:
图7 LM358集成结构
LM358增益高(高达
100
d
B
100dB
100dB),自带内部频率补偿,低功耗,差模输入电压范围宽(单电源
3
V
3V
3V ~
30
V
30V
30V),输出电压摆幅大(
0
V
0V
0V ~
V
C
C
VCC
VCC)。
使用R1和R2选用
10
K
Ω
10KΩ
10KΩ,保证输入稳定的同时也尽可能地保证精度,所以R3和R4选用
350
K
Ω
350KΩ
350KΩ。在现实中,有
10
K
Ω
10KΩ
10KΩ的金属膜电阻和
300
K
300K
300K的金属膜电阻考虑到实际放大倍数可能不够,因为不是理想电路,所以用电位器503(
50
K
Ω
50KΩ
50KΩ)串联
300
K
300K
300K来达到想要的放大倍数。
图8 PT100驱动电路
电路构思完成,将单臂电桥和差分放大电路合二为一,加上
0.1
u
F
0.1uF
0.1uF的电容给输入
5
V
5V
5V电压滤波,搭建成完整的PT100驱动电路,如图8所示。届时调节电路,根据测温范围调节R2至合适,根据电压放大效果调节R9、R10至合适即可。
3、电路焊接
关于电路的焊接,我焊接了三次。
-
第一次焊接的板子为:“PT100驱动电路V1.0(万用阻值可调实验板)”,其精度特别满足设计要求,与标准温度的差值不超过0.1℃,但其毕竟是实验板,不是成品板,不能作为设计成品;
-
第二次焊接为:“PT100驱动电路V2.0(测试板)”,其精度基本满足设计要求,与标准温度的差值不超过0.2℃,但毕竟是校验板,还不是成品板,不能作为设计成品;
-
第三次焊接为:“PT100驱动电路V3.0”,属于成品板,其精度基本满足设计要求,与标准温度的差值不超过0.2℃,可作为设计成品。
具体如下:
①PT100驱动电路V1.0(万用阻值可调实验板)
功能:多固定电阻和可调电位器的焊接,使得每个电阻的电阻值可精确调节,其中R3、R4精确到1K,R5、R6可精确的阻值范围为
10
K
10K
10K ~
15
K
15K
15K,R7、R8可精确的阻值范围为
250
K
250K
250K ~
350
K
350K
350K;多排针焊接,可用万用表探查线路中电阻、电压、电流的变化;双色排针,用于标注电路的整体布线。
焊接布局图:
图9 PT100驱动电路V1.0布局图
焊接效果图:
图10 PT100驱动电路V1.0(万用阻值可调实验板)-未接线
使用杜邦线连接各个电阻,对电阻阻值进行精确调整;再用杜邦线连接整个电路,完成驱动板的硬件制作,如图11。
图11 PT100驱动电路V1.0(万用阻值可调实验板)-接线
②PT100驱动电路V2.0(测试板)
功能:在V1.0所测的数据后,选用更加合适阻值电阻的焊接,其中R3、R4精确到
1
K
1K
1K,R5、R6的阻值为
10
K
10K
10K,R7、R8的阻值为
300
K
300K
300K;多排针焊接,跳线帽完成各元件的连接,可用万用表探查线路中电阻、电压、电流的变化。
焊接布局图:
图12 PT100驱动电路V2.0布局图
焊接效果图:
图13 PT100驱动电路V2.0(测试板)
较上一版本,新增电源控制模块,控制PT100驱动板的开关;并且体积明显减小,方便携带。
图14 PT100驱动电路V2.0(测试板)
③PT100驱动电路V3.0
功能:在V2.0的基础下,双面焊接,提高电路阻值精度和电路布线简洁度;新增可调整放大倍数(30倍~35倍)的5K电位器于底部;去除测试数据用的端口(排针、跳线帽);新增独立电源接口,可单独给该驱动板供电。
焊接布局图:略。
焊接效果图:
图15 PT100驱动电路V3.0
4、设计PT100驱动电路与STM32F103RC实验板的电气连接思路框图。
图16 PT100驱动板与STM32F103RC实验板的电气连接框图
5、了解已有蜂鸣器电路与STM32F103RCT6实验板的电路图。
图17 已有蜂鸣器电路与STM32F103RCT6实验板的电路图
6、了解已有OLED电路与STM32F103RCT6实验板的电路图。
图18 已有蜂鸣器电路与STM32F103RCT6实验板的电路图
7、实现STM32F103RCT6实验板外设改装。
图19 STM32F103RCT6实验板外设
表:STM32F103RCT6实验板外设平面接口
电源IO | ADC 0~3 | 其余IO | 4*4矩阵键盘 |
---|---|---|---|
(3.3V)红Pin1~Pin6 | OLED | OLED | 4*4矩阵键盘 |
(GND)黑Pin7~Pin12 | OLED | OLED | 4*4矩阵键盘 |
(5V)红Pin13~Pin18 | 串口 1~2 | 其余IO | 4*4矩阵键盘 |
8、实现PT100驱动电路V3.0与STM32F103RCT6实验板的电气连接(实际电路连接)。
图20 PT100驱动电路V3.0与STM32F103RCT6实验板的电气连接图
三、软件设计
总代码工程链接下载:PT100测温系统
1、设计PT100测温系统,完成各驱动模块总调配。
为了完成本次的设计目标,需要开始代码的构思,根据任务要求,在本次PT100测温系统的软件设计中,所需要使用的代码模块有:STM32F103RCT6自带的Time内部时钟模块、STM32F103RCT6自带的ADC模数转换模块、主板上的蜂鸣器模块、外设的OLED显示模块。
系统运行前,要先初始化将要使用的这些模块。
初始化后,让所有模块达到“备战”状态。
当PT100驱动电路的输出被实验板上的STM32F103RCT6芯片的ADC1端口(PA1)读取到的时候,芯片自带的ADC模数转换模块会启动,将在ADC1端口(PA1)所读到的电压值用12位的AD值表示出来,并将这表示出来的AD值用液晶屏显示在屏幕上,此时AD值通过计算公式算出该AD时对应的温度,并将温度也一起显示出来,当转化出来的温度达到报警的要求(30℃,50℃,70℃),蜂鸣器鸣叫,即可完成本次设计的要求。
总程序流程图如图21所示:
图21 各驱动模块总调配流程图
主程序“main.c”
//***************************************************************************************
//*** __----~~~~~~~~~~~------___
//*** . . ~~//====...... __--~ ~~
//*** -. \_|// |||\\ ~~~~~~::::... /~
//*** ___-==_ _-~o~ \/ ||| \\ _/~~-
//*** __---~~~.==~||\=_ -_--~/_-~|- |\\ \\ _/~
//*** _-~~ .=~ | \\-_ '-~7 /- / || \ /
//*** .~ .~ | \\ -_ / /- / || \ /
//*** / ____ / | \\ ~-_/ /|- _/ .|| \ /
//*** |~~ ~~|--~~~~--_ \ ~==-/ | \~--===~~ .\
//*** ' ~-| /| |-~\~~ __--~~
//*** |-~~-_/ | | ~\_ _-~ /\
//*** / \ \__ \/~ \__
//*** _--~ _/ | .-~~____--~-/ ~~==.
//*** ((->/~ '.|||' -_| ~~-/ , . _||
//*** -_ ~\ ~~---l__i__i__i--~~_/
//*** _-~-__ ~) \--______________--~~
//*** //.-~~~-~_--~- |-------~~~~~~~~
//*** //.-~~~--\
//***
//*** PT100驱动程序V4.0 (2021.05.28)
//*** By XingDala.2905469493.All Rights Reserved.
//***************************************************************************************
#include "stm32f10x.h"
#include "adc.h"
#include "OLED.h"
#include "PT100INT.h"
#include "sound.h"
#include "time.h"
int main(void)
{
ADC_init();//ADC初始化
time_init();//时钟初始化
OLED_Init();//OLED初始化
PT100_OLED();//PT100驱动程序显示初始化
sound_init();//蜂鸣器初始化
while(1)
{
PT100INT();//PT100驱动程序
}
}
2、TIME系统内部时钟模块
为流畅运行本次设计,因而选用STM32F103RCT6自带的系统时钟TIM2、TIM3、TIM4、TIM5、TIM6、TIM7。以往会使用高精度的中断函数加以控制,以达到STM32F103RCT6最极限的延时精度,但这里不要求精度,所以不需要使用内部中断来写延时。
其中,TIM2、TIM3、TIM4用来控制蜂鸣器报警系统,TIM5用来控制足以驱动并流畅运行系统各模块的延时(三种时间单位:us、ms、s),TIM6和TIM7用来控制AD采集的速度和滤波频次及速率。
图22 STM32F103RCT6系统内部时钟树
(1)初始化时钟TIM6、TIM7:
在所调用的6个系统时钟中,用来处理AD采集的两个系统时钟TIM6、TIM7是固定的速率,所以要对固定速率的系统时钟进行初始化。
(2)时钟程序:
时钟程序“time.c”
void time_init(void)//初始化
{
time6();
time7();
}
void time2(unsigned int n)//蜂鸣器响的持续时长
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef time2;
time2.TIM_CounterMode = TIM_CounterMode_Up;
time2.TIM_Period = n*10 - 1;
time2.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM2, & time2);
TIM_SetCounter(TIM2,0);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
}
void time3(unsigned int n)//蜂鸣器关的持续时长
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitTypeDef time3;
time3.TIM_CounterMode = TIM_CounterMode_Up;
time3.TIM_Period = n*10 - 1;
time3.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM3, & time3);
TIM_SetCounter(TIM3,0);
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
}
void time4(unsigned int n)//蜂鸣器音调
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseInitTypeDef time4;
time4.TIM_CounterMode = TIM_CounterMode_Up;
time4.TIM_Period = n - 1;
time4.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInit(TIM4, & time4);
TIM_SetCounter(TIM4,0);
TIM_ClearFlag(TIM4,TIM_FLAG_Update);
}
void T(unsigned int n,unsigned int x)//延时函数(us、ms、s)eg:T(152,1)表示延时125微秒,T(254,2)表示延时254毫秒
{
unsigned int j = 0,k = 0;
if(x == 1){j=72;k = 1;}//us
if(x == 2){j=7200;k = 10;}//ms
if(x == 3){j=7200;k = 10000;}//s
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
TIM_TimeBaseInitTypeDef time5;
time5.TIM_CounterMode = TIM_CounterMode_Up;
time5.TIM_Period = n*k - 1;
time5.TIM_Prescaler = j - 1;
TIM_TimeBaseInit(TIM5, & time5);
TIM_SetCounter(TIM5,0);
TIM_ClearFlag(TIM5,TIM_FLAG_Update);
TIM_Cmd(TIM5, ENABLE);
while(TIM_GetFlagStatus(TIM5,TIM_FLAG_Update)==RESET);
TIM_Cmd(TIM5, DISABLE);
}
void time6(void)//1S
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseInitTypeDef time6;
time6.TIM_CounterMode = TIM_CounterMode_Up;
time6.TIM_Period = 10000 - 1;
time6.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM6, & time6);
TIM_SetCounter(TIM6,0);
TIM_ClearFlag(TIM6,TIM_FLAG_Update);
}
void time7(void)//1ms
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
TIM_TimeBaseInitTypeDef time7;
time7.TIM_CounterMode = TIM_CounterMode_Up;
time7.TIM_Period = 10 - 1;
time7.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM7, & time7);
TIM_SetCounter(TIM7,0);
TIM_ClearFlag(TIM7,TIM_FLAG_Update);
}
时钟程序“time.h”
#ifndef _TIME_H__
#define _TIME_H__
void time_init(void);
void time2(unsigned int n);
void time3(unsigned int n);
void time4(unsigned int n);
void T(unsigned int n,unsigned int x);
void time6(void);
void time7(void);
#endif
3、AD采集模块
(1)初始化:
配置STM32F103RCT6的ADC1接口的读取模式,打开ADC1的时钟,设置为模拟输入模式,配置单通道扫描模式,配置连续转化模式,定义规则通道的外部触发方式为内部触发,设置数据对齐方式为右对齐,设置ADC模拟通道个数为1,设置被采集的通道为ADC1,使能ADC1,使能ADC1的复位校准,开始ADC1的复位,ADC1的复位和校准结束后,ADC1初始化结束,把LM358的输出接到ADC1上即可准备AD值的读取。
图23 ADC1初始化流程图
(2)AD采集程序:
由于5V电压不稳定,导致输出的AD值也会微微波动,此刻就需要用到程序滤波。
使用时钟,每隔1ms采集一次AD值,并将所采集的AD值加起来,用IN存起来;使用时钟,每隔1s输出一次IN值,并将所输出的IN值除以1000,因为在这1s内采集了1000个AD值,除以1000后,就可以得到1S内平均AD值,也就是滤波。
接着,再利用sprintf把要转换的AD数值格式化为字符串,并用OLED显示屏显示出来,由于IN值1s输出一次,使用滤波后的平均AD值1秒刷新一次。
图24 STM32F103RCT6AD采集程序流程图
AD采集程序“adc.c”
#include "stm32f10x.h"
void ADC_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1 ,ADC_Channel_1 ,1 ,ADC_SampleTime_1Cycles5);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
}
AD采集程序“adc.h”
#ifndef _ADC_H__
#define _ADC_H__
void ADC_init(void);
#endif
4、蜂鸣器响应模块
(1)初始化:
打开蜂鸣器引脚所在的时钟,设置其为推挽输出模式。
图25 蜂鸣器响应模块初始化流程图
(2)蜂鸣器响应:
TIM2控制蜂鸣器通电的时长,TIM3控制蜂鸣器断电的时长,TIM4控制蜂鸣器发声的音调(频率),以此来调出万能的警报声音。当温度达到报警温度,触发蜂鸣器响应程序,程序流程图如图22所示:
图26 蜂鸣器响应模块报警主程序流程图
蜂鸣器鸣响程序“sound.c”
#include "stm32f10x.h"
#include "sound.h"
#include "time.h"
void sound_init(void)//初始化蜂鸣器
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef FMQ;
FMQ.GPIO_Pin = GPIO_Pin_8;
FMQ.GPIO_Speed = GPIO_Speed_50MHz;
FMQ.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &FMQ);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}
void FMQ_T(void)//蜂鸣器响
{
TIM_Cmd(TIM2, ENABLE);
while(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update) == RESET)
{
TIM_Cmd(TIM4, ENABLE);
while(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update) == RESET)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}
TIM_ClearFlag(TIM4,TIM_FLAG_Update);
while(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update) == RESET)
{
GPIO_SetBits(GPIOB, GPIO_Pin_8);
}
TIM_ClearFlag(TIM4,TIM_FLAG_Update);
TIM_Cmd(TIM4, DISABLE);
}
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_Cmd(TIM2, DISABLE);
}
void FMQ_F(void)//蜂鸣器关
{
TIM_Cmd(TIM3, ENABLE);
while(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == RESET)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
TIM_Cmd(TIM3, DISABLE);
}
void sound1(void)//蜂鸣器鸣叫,节奏调节
{
time2(100);//响的持续时长
time4(100);//音调
FMQ_T();//蜂鸣器开始响一段时间
time3(100);//关的持续时长
FMQ_F();//蜂鸣器关闭一段时间
time2(100);//响的持续时长
time4(100);//音调
FMQ_T();//蜂鸣器开始响一段时间
GPIO_ResetBits(GPIOC, GPIO_Pin_13);//蜂鸣器关
}
蜂鸣器鸣响程序“sound.h”
#ifndef _SOUND_H__
#define _SOUND_H__
void sound_init(void);
void FMQ_T(void);
void FMQ_F(void);
void sound1(void);
#endif
5、OLED显示模块
(1)初始化:
打开OLED接口的时钟,推挽输出OLED两条信号线(SCL、SDA),并且一块拉高,延时200ms;关闭显示,设置时钟分频因子和震荡频率([3:0]分频因子、[7:4]震荡频率),设置驱动路数为默认0X3F(1/64),设置显示偏移默认为0,设置显示开始行 [5:0],电荷泵设置为bit2,设置内存地址模式页地址模式,段重定义设置为bit0:0,0->0;1,0->127,设置COM扫描方向为普通模式,设置COM硬件引脚配置为[5:4]配置,对比度设置为默认0X7F最亮,设置预充电周期为[3:0]、PHASE 1、[7:4]、PHASE 2,设置VCOMH 电压倍率为[6:4] 0.83*3.3,开启全局显示,设置显示方式为正常,开启显示,清屏,OLED初始化完成。
图27 OLED初始化流程图
(2)显示:
ZFC(0,0,"abc254!");//在坐标(0,0)显示字符串abc254!
ZF(0,0,'0');//在坐标(0,0)显示字符0
WZ(0,0,2,3);//在坐标(0,0)显示第3页的第2个汉字
TP(0,0,128,64,2);///在坐标(0,0)显示第二张图片,显示的图片范围为x:0~128,y:0~64
4、PT100系统总驱动模块
PT100的阻值是随着温度的变化而变化,在对各标准温度下温度AD采集的结果可以得出,AD值随着温度的升高而升高,其线性关系近乎是一条直线,但这条直线有略微的弯曲,所以需要进行大量的AD采集进行数据拟合。
本次设计计划使用100个AD采集数据,也就是100个温度区间,但一百个区间很多,不可能用计算器一个一个计算,所以这一百个区间(温度-AD)将被分别放入两个数组(一个数组放温度值,一个数组放AD值,两两对应),再在程序中用AD比对,找出此刻LM358传来的AD在哪个区间,再用固定的公式算出每个AD的温度是多少
该
区
间
温
度
变
化
/
该
区
间
A
D
变
化
=
每
个
A
D
是
多
少
温
度
该区间温度变化/该区间AD变化=每个AD是多少温度
该区间温度变化/该区间AD变化=每个AD是多少温度 再乘以此刻测出的实时AD值,算出准确的温度。
又由于供电不稳定,每次供电接口的微微位移,都会使得V供电偏大或者偏小,间接导致同温度下,LM358输出的AD与AD采集时的电压不一致,所以在AD值进入计算前,先借助标准仪表对比同温度下与AD采集时的AD偏移了多少AD,对程序中的AD进行误差加减,把微微偏移的AD曲线平移到AD采集时的曲线。
PT100测温系统程序“PT100INT.c”
#include "stm32f10x.h"
#include "adc.h"
#include "SSA.h"
#include "time.h"
#include "OLED.h"
unsigned int wucha = 0;//误差偏移修正
void PT100INT(void)
{
unsigned int Dout = 0;//清除AD数据缓存
unsigned int IN = 0;//采集AD前清零
TIM_Cmd(TIM6, ENABLE);//1s计时准备
while(TIM_GetFlagStatus(TIM6,TIM_FLAG_Update) == RESET)//开始1s计时,1s内采集1000次AD并软件滤波
{
TIM_Cmd(TIM7, ENABLE);//1ms计时准备
while(TIM_GetFlagStatus(TIM7,TIM_FLAG_Update) == RESET)//开始10ms计时,采集100次
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//AD采集准备
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//AD采集
Dout = ADC_GetConversionValue(ADC1);//AD数据缓存
}
TIM_ClearFlag(TIM7,TIM_FLAG_Update);//10ms结束,定时器清零,为下一次计时10ms做准备
TIM_Cmd(TIM7, DISABLE);//关闭10ms定时器
IN = IN + Dout;//AD累加,为滤波做准备
}
TIM_ClearFlag(TIM6,TIM_FLAG_Update);//1s结束,定时器清零,为下一次计时1s做准备
TIM_Cmd(TIM6, DISABLE);//关闭1s定时器
IN = (IN / 1000)+wucha;//滤波
PT100show_AD(IN);//AD值显示
//AD采集值,请从低温到高温排
unsigned int TEMP[]= //温度值
{
26,27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,45,
46,47,48,49,50,51,52,53,54,55,
56,57,58,59,60,61,62,63,64,65,
66,67,68,69,70,71,72,73,74,75
};
unsigned int AD[]= //AD值
{
937, 977,1017,1057,1098,1144,1101,1240,1289,1332,
1378,1423,1474,1525,1560,1610,1661,1706,1753,1786,
1847,1902,1949,1984,2048,2100,2188,2231,2275,2322,
2372,2420,2465,2511,2554,2602,2652,2701,2747,2802,
2841,2886,2937,2994,3037,3065,3139,3164,3194,3347
};
unsigned int Temp = 0;//温度值清零
if(IN < 937)//温度超过测量范围,使用最后测量范围的K值进行计算
{
Temp = IN*10*TEMP[1]/AD[1];//动态拟合比对,动态显示温度
}
else if(IN >= 937 && IN <= 3347)//温度在测量范围内,开始测温
{
unsigned int a=0;
while(Temp == 0)//算出温度后跳出
{
if(IN > AD[a])//按区间比对
{
Temp = IN*10*TEMP[a]/AD[a];//动态拟合比对,动态显示温度
}
a++;//AD采集区间切换
}
}
else if(IN > 3347)//温度超过测量范围,使用最后测量范围的K值进行计算
{
Temp = IN*10*TEMP[49]/AD[49];//动态拟合比对,动态显示温度
}
PT100show_Temp(Temp);温度显示
}
PT100测温系统程序“PT100INT.h”
#ifndef _PT100INT_H__
#define _PT100INT_H__
void PT100INT(void);
#endif
最终结果,所测试的温度与标准仪表相差
0.1
℃
0.1℃
0.1℃ ~
0.2
℃
0.2℃
0.2℃。
图28为PT100测温系统的测温显示效果:
图28 PT100测温系统显示面板
当前面板所显示的AD值每1s刷新一次;
所显示的温度值每1s刷新一次;
警报未响起时蜂鸣器状态显示“静音”,警报响起时蜂鸣器报警鸣叫,且状态显示“鸣响”,当报警声响三遍后,报警声停止,蜂鸣器报警系统进入睡眠状态,当温度继续上升超过报警温度1℃时,蜂鸣器报警系统又会进入预备触发状态,等待报警温度的再次达到报警温度值;
最底下的图案为系统运行指示灯,当系统正在(流畅)运行时,该图标是动态显示的动画状态,最右边的方形图标闪烁。
四、调试
1、电路调试
在设计电桥中,一开始是把
5
V
5V
5V接在PT100与精密电阻间,把GND接在两个
1
K
1K
1K中间,模拟软件测试没有问题,但实际电路中会出现问题,LM358的2号引脚和3号引脚不能承受过大的电压,最终导致LM358的输出为爆输出状态(标准温度下超过
3.3
V
3.3V
3.3V),但在LM358的芯片手册上提到这两个引脚所能接受的电压远远不止这么少,百思不得其解。
所以在设计中将GND接在PT100与精密电阻间,把
5
V
5V
5V接在两个
1
K
1K
1K中间,让
1
K
1K
1K去分掉电压,模拟软件测试也是没有问题,最终LM358的2号引脚和3号引脚所承受的电压降到了
0.5
V
0.5V
0.5V以下,输出也和模拟差不多,电路调试成功。
2、放大倍数调试
根据实测,实物与理论出现预料范围内的偏差(电桥输出比理论偏大,放大倍数比理论偏小),通过串联电位器(由于元件不足,没有过大的电位器),勉强把放大倍数调整到了32倍。
3、AD采集:
此时刚好测出一百个温度区间内的AD值(当温度降的慢AD变化率小的时候,选最大温度区间为
1
℃
1℃
1℃,;但当温度降得快,AD变化很大的时候,选用最小温度区间为
0.5
℃
0.5℃
0.5℃,多次高温采样),其折线图25所示。
图29 AD采集
一百个区间已出,每个区间的斜率
K
=
温
度
变
化
/
A
D
变
化
=
每
个
A
D
是
多
少
温
度
K= 温度变化/AD变化=每个AD是多少温度
K=温度变化/AD变化=每个AD是多少温度 很容易可以求出来,只要把AD乘上对应区间的该斜率,则可求出对应的温度。
版权声明:本文为CSDN博主「星达拉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xingdala/article/details/117934599
本项目设计内容涉及:传感器、嵌入式系统应用、模拟电子技术。
课程:《智能传感技术》
指导老师:覃园芳老师
一、任务要求
设计PT100温度传感器的信号采集电路,使用嵌入式系统实验板采集外部的温度并显示。
要求:
- 范围:
20
20
80
℃
80℃
- 精度:与标准仪器比较小于
0.3
℃
0.3℃
-
30
30
50
50
70
℃
70℃
注:本次设计采用的主板芯片为STM32F103RCT6
二、硬件设计
1、分析所要使用的PT100温度传感器。
结合有关PT100的资料,可得几个重要知识:
- PT100的阻值会随温度的变化而成正比变化(温度越高阻值越大),但阻值变化很小,约等于
0.385
Ω
/
度
0.385Ω/度
- PT100的测温范围是
﹣
200
℃
﹣200℃
150
℃
150℃
100
Ω
100Ω
- PT100的工作电流要小于
5
m
A
5mA
- PT100的阻值虽然随温度的变化而成正比变化,但在不同温度区间内其变化的速率(也就是
K
值
K值
2、设计PT100驱动电路。
结合所学经验,开始有想法:
如果把PT100当电阻串联在电路中,并用主板上STM32F103RCT6芯片的PA1引脚的ADC功能去读取电压值并进行温度转换,可得出温度,如图1所示:
图1 PT100串联电路测AD法
但结合资料,这种想法秒速推翻。
常温(
25
℃
25℃
25℃)水中的PT100的阻值大概在
109.89
Ω
109.89Ω
109.89Ω左右。
假设主板给PT100的供电是标准的
3.3
V
3.3V
3.3V直流电,并且通过PT100的电流也是其最大
5
m
A
5mA
5mA直流电流,那么在此刻,PT100所要分掉的电压约为
109.89
∗
0.005
=
0.54945
V
109.89*0.005=0.54945V
109.89∗0.005=0.54945V
将其根据AD转换的换算公式换算成AD值大概为
0.54945
/
3.3
∗
4096
=
681.98
≈
682
0.54945/3.3*4096=681.98≈682
0.54945/3.3∗4096=681.98≈682
当温度上升一度,假设PT100的阻值刚好上升了
0.385
Ω
0.385Ω
0.385Ω,那么其分掉的电压的变动值约等于
0.385
∗
0.005
=
0.001925
V
0.385*0.005=0.001925V
0.385∗0.005=0.001925V
将其根据AD转换的换算公式换算成AD值大概为
0.001925
/
3.3
∗
4096
=
2.39
≈
2
0.001925/3.3*4096=2.39≈2
0.001925/3.3∗4096=2.39≈2
0.5
℃
/
A
D
0.5℃/AD
0.5℃/AD ?好像还不错,但任务要求所设计的PT100电路测温得出的结果,精度要小于
0.3
℃
0.3℃
0.3℃。
0.3
℃
0.3℃
0.3℃是什么概念?换算为PT100的阻值变化,也就约为
0.1155
Ω
0.1155Ω
0.1155Ω,那么其精度下,PT100变化
0.1155
Ω
0.1155Ω
0.1155Ω阻值,要变化的电压约为,将其根据AD转换的换算公式换算成AD值大概为,连
1
1
1个AD值都不到,无法达到这个精度。
所以,这种方法被推翻。
直流电桥(微小电阻变化转微小电压变化)
找到了关键问题所在,就是PT100的随温度的变化而成正比变化太小了,只要把这种变化变大,大到很明显,就解决了问题。
电阻的阻值是没法调大的,只能通过电阻变化导致的电压变化调大,就可以解决变化小的问题。
结合所学知识,只要将微小的电阻变化转化为微小的电压变化,再将微小的电压变化转化为大的电压变化,也就是用直流电桥配上一个放大电路,就很完美地解决这个问题。
如图2为一个直流电桥,R1、R2、R3、R4为该直流电桥的桥臂,且R1、R2、R3、R4皆为固定电阻,U1为该直流电桥的供电电源,U2为该直流电桥的输出电压。
图2 直流电桥
结合所学的直流电桥知识,可得公式:
U
2
=
U
1
∗
(
R
1
R
1
+
R
4
−
R
2
R
2
+
R
3
)
U2=U1*(\frac{R1}{R1+R4} -\frac{R2}{R2+R3})
U2=U1∗(R1+R4R1−R2+R3R2)
当
R
1
∗
R
3
=
R
2
∗
R
4
R1*R3=R2*R4
R1∗R3=R2∗R4,该直流电桥会达到平衡,带入公式可得输出U2的值为
U
2
=
U
1
∗
0
=
0
V
U2=U1*0=0V
U2=U1∗0=0V 也就是该直流电桥在平衡状态下,输出电压为
0
V
0V
0V;
当
R
1
∗
R
3
≠
R
2
∗
R
4
R1*R3≠R2*R4
R1∗R3=R2∗R4时,该直流电桥的平衡会被打破,带入公式可得输出U2的值。
利用直流电桥不平衡状态下会输出电压的特性,应用不平衡电桥之一——单臂电桥,来把微小的电阻变化转化为微小的电压变化。
如图3为一个直流电桥,R1、R2、R3、R4为该直流电桥的桥臂,且R1为可变电阻,R2、R3、R4为固定电阻,且
R
3
=
R
4
R3=R4
R3=R4,U1为该直流电桥的供电电源,U2为该直流电桥的输出电压。
图3 直流单臂电桥
该单臂电桥应用于本次设计,R1为PT100,R2为与PT100接近的阻值,这样可以使得在一定(温度)情况下(PT100的阻值等于R2的阻值)电桥可以达到平衡,使得单臂电桥的输出趋近于
0
0
0。
又因为通过PT100的电流不能大于
5
m
A
5mA
5mA,不然可能会烧坏PT100,所以R3与R4的阻值虽然要取相等,但又要把电桥电流稳定到
5
m
A
5mA
5mA以下,所以R2和R3要给PT100分流。
本次设计,实验板可接出供电为
3.3
V
3.3V
3.3V和
5
V
5V
5V两种,为了提高精度,也就是提高单臂电桥的输出电压的范围,故选择5V供电。
在先前的计算中,常温(
25
℃
25℃
25℃)水中的PT100的阻值大概在
109.89
Ω
109.89Ω
109.89Ω左右。假设主板给PT100的供电是标准的
5
V
5V
5V直流电,并且通过PT100的电流也是其最大
5
m
A
5mA
5mA直流电流,那么在此刻,PT100所占电压最大为
109.89
∗
0.005
=
0.54945
V
109.89*0.005=0.54945V
109.89∗0.005=0.54945V 也就是说R3和R4至少要分掉的电压为
5
-
0.54945
=
4.45055
V
5-0.54945=4.45055V
5-0.54945=4.45055V
设
R
1
=
R
2
=
109.89
Ω
R1=R2=109.89Ω
R1=R2=109.89Ω,在此刻,
5
V
5V
5V通电的电桥为一个并联电路,可变形为图4所示的电路:
图4 PT100单臂电桥变形图
又R3和R4至少要分掉
4.45055
V
4.45055V
4.45055V的电压,所以R3和R4的阻值至少为
4.45055
/
0.005
=
0.5
V
4.45055/0.005=0.5V
4.45055/0.005=0.5V
又由于本次设计PT100用来测量
20
℃
20℃
20℃ ~
80
℃
80℃
80℃的水温,水常压下的极端液态也就是
0
℃
0℃
0℃。
重新计算PT100将分掉的电压,PT100的阻值放入
0
℃
0℃
0℃的水中,其阻值为
100
Ω
100Ω
100Ω左右。假设主板给PT100的供电是标准的
5
V
5V
5V直流电,并且通过PT100的电流也是其最大
5
m
A
5mA
5mA直流电流,那么在此刻,PT100所占电压最大为,在此刻,R3和R4又至少要分掉的电压为
5
-
0.5
=
405
V
5-0.5=405V
5-0.5=405V 所以R3和R4的阻值至少为
4.5
/
0.005
=
900
Ω
4.5/0.005=900Ω
4.5/0.005=900Ω
所以R3和R4的电阻取值,在理想状态下至少是
900
Ω
900Ω
900Ω,但现实中没有刚刚好的阻值可以挑,那就可以选择比该值大的电阻,例如
1
K
Ω
1KΩ
1KΩ,或者是比
1
K
Ω
1KΩ
1KΩ再大的,但是如果选用的电阻太大,则分压过多,依旧导致电桥的输出范围越来越少,最后使得微弱的电压变化变得更加微弱,甚至是不变化了,而且电阻的型号选用金属膜电阻,因为其体积小、噪声低、稳定性好,选它很不错,误差1%可接受。
又因为R2要与PT100趋近,所以对水的温度测量中,温度变化最大为
0
℃
0℃
0℃ ~
100
℃
100℃
100℃,所以PT100的电阻变化约为
100
Ω
100Ω
100Ω ~
139
Ω
139Ω
139Ω,因而要使得电路达到最佳的变化R2为
150
Ω
150Ω
150Ω的可变电阻最佳,又可调精度要非常高,所以要选用精密电阻。
但现实中没有刚刚好阻值的精密电阻可以挑,那就可以选择比该值大一点点的精密电阻,所以3296w不错,多圈式精密可调,例如3296w电位器201(
200
Ω
200Ω
200Ω)。
所以本次设计选用的R3和R4为
1
K
Ω
1KΩ
1KΩ,单臂电桥部分的设计完成,如图5所示。
图5 PT100单臂电桥部分
差分放大电路(放大微小的电压变化)
接下来就是放大电路部分,通过放大电路来实现把电压的微小变化扩大,使用模电所学过的差分放大电路(减法电路),可以把电桥的输出进行减法运算,并增益放大输出。
当电桥平衡,电桥输出电压电势相等,差分放大电路的减法运算的结果为
0
V
0V
0V;
当电桥的平衡被破坏,电桥输出电压电势拉开,此时差分放大电路的减法运算即可有一定的差值,放大器对其进行放大到合适的差值变动并输出,即当电桥输出最大时,经过差分放大电路,放大到我们所需的最大差值电压
0
V
0V
0V ~
3.3
V
3.3V
3.3V。如图6为一个差分放大电路:
图6 差分放大电路
△假设U1 = U2:
①因输入端U1、U2的电流趋近相等,为“虚断”特性,同相输入端为高阻态,其输入电压值仅仅取决于R2、R4分压值。同相输入端的电压可以看作成为输入端比较基准电压;
②因输入端U1、U2的电压趋近相等,为“虚短”特性,进而又推知其为反相输入端,即R1、R3串联分压电路,这是反馈电压。放大器的控制目的是使反馈电压等于基准电压;
③由R1=R2,R3=R4条件可知,放大器输出端OUT只有处于“虚地”状态,即输出端OUT为0V,才能满足反馈电压等于基准电压,这可以由此导出差分放大器的一个工作特征。
△假设U1 ≠ U2,例如U1 > U2:
①此时因同相输入端电压高于反相输入端,输出端电压往正方向变化,其R1、R3偏置电路中的电流方向为R3→R1;
②由R1、R3的阻值比例可知,R1两端电压降为
(
同
向
输
入
端
电
压
)
-
U
1
(同向输入端电压)-U1
(同向输入端电压)-U1 则R3两端电压降为
[
(
同
向
输
入
端
电
压
)
-
U
1
]
∗
(
R
3
/
R
1
)
[(同向输入端电压)-U1]*(R3/R1)
[(同向输入端电压)-U1]∗(R3/R1) 输出端电压为
(
同
向
输
入
端
电
压
)
+
[
(
同
向
输
入
端
电
压
-
U
1
)
∗
(
R
3
/
R
1
)
(同向输入端电压)+[(同向输入端电压-U1)*(R3/R1)
(同向输入端电压)+[(同向输入端电压-U1)∗(R3/R1)
③若此时的输入电压差为
U
1
-
U
2
U1-U2
U1-U2,输出电压为
X
∗
(
U
1
-
U
2
)
X*(U1-U2)
X∗(U1-U2)。则该差分放大器的差分电压放大倍数为
R
4
/
R
3
=
X
R4/R3=X
R4/R3=X 可得该差分放大电路的放大倍数是
X
X
X倍。
△假设U1 ≠ U2,例如U1 < U2:
①此时因同相输入端电压低于反相输入端,输出端电压往反方向变化,其R1、R3偏置电路中的电流方向为R3←R1;
②由R1、R3的阻值比例可知,R1两端电压变为
-
U
1
-
(
同
向
输
入
端
电
压
)
-U1-(同向输入端电压)
-U1-(同向输入端电压) 则R3两端电压变为
-
[
U
1
-
(
同
向
输
入
端
电
压
)
]
∗
(
R
3
/
R
1
)
-[U1-(同向输入端电压)]*(R3/R1)
-[U1-(同向输入端电压)]∗(R3/R1) 输出端电压为
-
(
同
向
输
入
端
电
压
)
-
(
同
向
输
入
端
电
压
-
U
1
)
∗
(
R
3
/
R
1
)
-(同向输入端电压)-(同向输入端电压-U1)*(R3/R1)
-(同向输入端电压)-(同向输入端电压-U1)∗(R3/R1) 结果是一个负电压。
所以故障维修的经验就冒出来了:
如果直接测量R1、R3串联电路的分压状态,只要R1、R3串联分压是成立的,则该差分电路就大致上就是好的,电路的电压放大倍数也由此得出;
只要测量输入电压差(R1、R2左端电压差),再测量输出端电压进行比较,则外围偏置电路的好坏,也会得出明确的结论。
所以公式就推出来了:
其输出公式为:
O
U
T
=
(
R
2
+
R
4
)
∗
R
3
∗
U
1
(
R
1
+
R
4
)
∗
R
2
−
R
4
∗
R
2
R
2
OUT=\frac{(R2+R4)*R3*U1}{(R1+R4)*R2} -\frac{R4*R2}{R2}
OUT=(R1+R4)∗R2(R2+R4)∗R3∗U1−R2R4∗R2
在实际应用中,一般使
R
1
=
R
2
R1=R2
R1=R2、
R
3
=
R
4
R3=R4
R3=R4化简电路,则
其输出公式为:
O
U
T
=
(
U
1
-
U
2
)
∗
R
4
R
1
OUT=\frac{(U1-U2)*R4}{R1}
OUT=R1(U1-U2)∗R4
当所前边所设计的电桥与差分放大电路结合起来,
U
1
-
U
2
U1-U2
U1-U2就为电桥输出的电势差,OUT输入到STM32F103RCT6实验板ADC1口,其最大可读取电压为
3.3
V
3.3V
3.3V,则电势差放大的倍数也就是
R
4
/
R
1
R4/R1
R4/R1即可进行计算了。
假设所设计的单臂电桥达到理想状态,最小电桥输出
U
2
=
0
V
U2=0V
U2=0V,最大电桥输出为
U
2
=
M
a
x
B
r
i
d
g
e
U2=Max Bridge
U2=MaxBridge
O
u
t
Out
Out。水温最高为
100
℃
100℃
100℃,最大电桥输出为PT100置于
100
℃
100℃
100℃时电桥的输出。
实测100℃时PT100的阻值约为
138.8
Ω
138.8Ω
138.8Ω,电桥所接输入电压为
5
V
5V
5V,
R
3
=
R
4
=
1
K
R3=R4=1K
R3=R4=1K,若要使得测温范围是从
0
℃
0℃
0℃ ~
100
℃
100℃
100℃,则精密电阻R2的阻值应该调为
0
℃
0℃
0℃时PT100的阻值,也就是
100
Ω
100Ω
100Ω,带入公式可得电桥的输出
U
2
=
U
1
∗
(
R
1
R
1
+
R
4
-
R
2
R
2
+
R
3
)
=
5
∗
(
138.8
138.8
+
1000
-
100
100
+
1000
)
=
0.154867963087
V
≈
0.155
V
U2=U1*(\frac{R1}{R1+R4}-\frac{R2}{R2+R3}) =5*(\frac{138.8}{138.8+1000}-\frac{100}{100+1000})=0.154867963087V≈0.155V
U2=U1∗(R1+R4R1-R2+R3R2)=5∗(138.8+1000138.8-100+1000100)=0.154867963087V≈0.155V
将其放大到ADC1最大可检测电压
3.3
V
3.3V
3.3V,所需的放大倍数为
3.3
/
0.155
≈
21.29
≈
21
3.3/0.155≈21.29≈21
3.3/0.155≈21.29≈21倍。
但是在现实中,
100
℃
100℃
100℃的开水降温速度很快,所以理想的倍数有点浪费,所以我们只测到
80
℃
80℃
80℃,常温下的冰水升温速度也很快,所以我们最小也只测到
20
℃
20℃
20℃,这就是设计条件的由来吧。
实测
80
℃
80℃
80℃时PT100的阻值约为
131.1
Ω
131.1Ω
131.1Ω,电桥所接输入电压为
5
V
5V
5V,
R
3
=
R
4
=
1
K
R3=R4=1K
R3=R4=1K若要使得测温范围是从
20
℃
20℃
20℃ ~
80
℃
80℃
80℃,则精密电阻R2的阻值应该调为
20
℃
20℃
20℃时PT100的阻值,实测约为
108
Ω
108Ω
108Ω,带入公式可得电桥的输出
U
2
=
U
1
∗
(
R
1
R
1
+
R
4
-
R
2
R
2
+
R
3
)
=
5
∗
(
131.1
131.1
+
1000
-
108
108
+
1000
)
=
0.0.2159735882
V
≈
0.092
V
U2=U1*(\frac{R1}{R1+R4}-\frac{R2}{R2+R3})=5*(\frac{131.1}{131.1+1000}-\frac{108}{108+1000})=0.0.2159735882V≈0.092V
U2=U1∗(R1+R4R1-R2+R3R2)=5∗(131.1+1000131.1-108+1000108)=0.0.2159735882V≈0.092V
将其放大到ADC1最大可检测电压
3.3
V
3.3V
3.3V,所需的放大倍数为
3.3
/
0.092
≈
35.87
≈
35
3.3/0.092≈35.87≈35
3.3/0.092≈35.87≈35倍。
接下来就要计算电阻取值了,因为
35
∗
R
1
=
35
∗
R
2
=
R
3
=
R
4
35*R1=35*R2=R3=R4
35∗R1=35∗R2=R3=R4,所以可以设R1、R2的阻值为
1
∗
X
Ω
1*XΩ
1∗XΩ,R3、R4的阻值为
35
∗
X
Ω
35*XΩ
35∗XΩ,只要算出
X
X
X即可算出全部的电阻,
X
X
X为该差分放大电路的输入电阻值,如图7:
图7 35倍差分放大电路
现在生产的运放,一般其输入阻抗都很高,所以运放输入端电阻选择余地比较大,但又为了减少偏置电流带来的影响,降低噪声和温漂的影响,输入电阻的取值一般选择在
10
k
Ω
10kΩ
10kΩ ~
100
K
Ω
100KΩ
100KΩ的区间。
本次设计所使用的运算放大器为LM358,其内部集成结构如图7所示:
图7 LM358集成结构
LM358增益高(高达
100
d
B
100dB
100dB),自带内部频率补偿,低功耗,差模输入电压范围宽(单电源
3
V
3V
3V ~
30
V
30V
30V),输出电压摆幅大(
0
V
0V
0V ~
V
C
C
VCC
VCC)。
使用R1和R2选用
10
K
Ω
10KΩ
10KΩ,保证输入稳定的同时也尽可能地保证精度,所以R3和R4选用
350
K
Ω
350KΩ
350KΩ。在现实中,有
10
K
Ω
10KΩ
10KΩ的金属膜电阻和
300
K
300K
300K的金属膜电阻考虑到实际放大倍数可能不够,因为不是理想电路,所以用电位器503(
50
K
Ω
50KΩ
50KΩ)串联
300
K
300K
300K来达到想要的放大倍数。
图8 PT100驱动电路
电路构思完成,将单臂电桥和差分放大电路合二为一,加上
0.1
u
F
0.1uF
0.1uF的电容给输入
5
V
5V
5V电压滤波,搭建成完整的PT100驱动电路,如图8所示。届时调节电路,根据测温范围调节R2至合适,根据电压放大效果调节R9、R10至合适即可。
3、电路焊接
关于电路的焊接,我焊接了三次。
-
第一次焊接的板子为:“PT100驱动电路V1.0(万用阻值可调实验板)”,其精度特别满足设计要求,与标准温度的差值不超过0.1℃,但其毕竟是实验板,不是成品板,不能作为设计成品;
-
第二次焊接为:“PT100驱动电路V2.0(测试板)”,其精度基本满足设计要求,与标准温度的差值不超过0.2℃,但毕竟是校验板,还不是成品板,不能作为设计成品;
-
第三次焊接为:“PT100驱动电路V3.0”,属于成品板,其精度基本满足设计要求,与标准温度的差值不超过0.2℃,可作为设计成品。
具体如下:
①PT100驱动电路V1.0(万用阻值可调实验板)
功能:多固定电阻和可调电位器的焊接,使得每个电阻的电阻值可精确调节,其中R3、R4精确到1K,R5、R6可精确的阻值范围为
10
K
10K
10K ~
15
K
15K
15K,R7、R8可精确的阻值范围为
250
K
250K
250K ~
350
K
350K
350K;多排针焊接,可用万用表探查线路中电阻、电压、电流的变化;双色排针,用于标注电路的整体布线。
焊接布局图:
图9 PT100驱动电路V1.0布局图
焊接效果图:
图10 PT100驱动电路V1.0(万用阻值可调实验板)-未接线
使用杜邦线连接各个电阻,对电阻阻值进行精确调整;再用杜邦线连接整个电路,完成驱动板的硬件制作,如图11。
图11 PT100驱动电路V1.0(万用阻值可调实验板)-接线
②PT100驱动电路V2.0(测试板)
功能:在V1.0所测的数据后,选用更加合适阻值电阻的焊接,其中R3、R4精确到
1
K
1K
1K,R5、R6的阻值为
10
K
10K
10K,R7、R8的阻值为
300
K
300K
300K;多排针焊接,跳线帽完成各元件的连接,可用万用表探查线路中电阻、电压、电流的变化。
焊接布局图:
图12 PT100驱动电路V2.0布局图
焊接效果图:
图13 PT100驱动电路V2.0(测试板)
较上一版本,新增电源控制模块,控制PT100驱动板的开关;并且体积明显减小,方便携带。
图14 PT100驱动电路V2.0(测试板)
③PT100驱动电路V3.0
功能:在V2.0的基础下,双面焊接,提高电路阻值精度和电路布线简洁度;新增可调整放大倍数(30倍~35倍)的5K电位器于底部;去除测试数据用的端口(排针、跳线帽);新增独立电源接口,可单独给该驱动板供电。
焊接布局图:略。
焊接效果图:
图15 PT100驱动电路V3.0
4、设计PT100驱动电路与STM32F103RC实验板的电气连接思路框图。
图16 PT100驱动板与STM32F103RC实验板的电气连接框图
5、了解已有蜂鸣器电路与STM32F103RCT6实验板的电路图。
图17 已有蜂鸣器电路与STM32F103RCT6实验板的电路图
6、了解已有OLED电路与STM32F103RCT6实验板的电路图。
图18 已有蜂鸣器电路与STM32F103RCT6实验板的电路图
7、实现STM32F103RCT6实验板外设改装。
图19 STM32F103RCT6实验板外设
表:STM32F103RCT6实验板外设平面接口
电源IO | ADC 0~3 | 其余IO | 4*4矩阵键盘 |
---|---|---|---|
(3.3V)红Pin1~Pin6 | OLED | OLED | 4*4矩阵键盘 |
(GND)黑Pin7~Pin12 | OLED | OLED | 4*4矩阵键盘 |
(5V)红Pin13~Pin18 | 串口 1~2 | 其余IO | 4*4矩阵键盘 |
8、实现PT100驱动电路V3.0与STM32F103RCT6实验板的电气连接(实际电路连接)。
图20 PT100驱动电路V3.0与STM32F103RCT6实验板的电气连接图
三、软件设计
总代码工程链接下载:PT100测温系统
1、设计PT100测温系统,完成各驱动模块总调配。
为了完成本次的设计目标,需要开始代码的构思,根据任务要求,在本次PT100测温系统的软件设计中,所需要使用的代码模块有:STM32F103RCT6自带的Time内部时钟模块、STM32F103RCT6自带的ADC模数转换模块、主板上的蜂鸣器模块、外设的OLED显示模块。
系统运行前,要先初始化将要使用的这些模块。
初始化后,让所有模块达到“备战”状态。
当PT100驱动电路的输出被实验板上的STM32F103RCT6芯片的ADC1端口(PA1)读取到的时候,芯片自带的ADC模数转换模块会启动,将在ADC1端口(PA1)所读到的电压值用12位的AD值表示出来,并将这表示出来的AD值用液晶屏显示在屏幕上,此时AD值通过计算公式算出该AD时对应的温度,并将温度也一起显示出来,当转化出来的温度达到报警的要求(30℃,50℃,70℃),蜂鸣器鸣叫,即可完成本次设计的要求。
总程序流程图如图21所示:
图21 各驱动模块总调配流程图
主程序“main.c”
//***************************************************************************************
//*** __----~~~~~~~~~~~------___
//*** . . ~~//====...... __--~ ~~
//*** -. \_|// |||\\ ~~~~~~::::... /~
//*** ___-==_ _-~o~ \/ ||| \\ _/~~-
//*** __---~~~.==~||\=_ -_--~/_-~|- |\\ \\ _/~
//*** _-~~ .=~ | \\-_ '-~7 /- / || \ /
//*** .~ .~ | \\ -_ / /- / || \ /
//*** / ____ / | \\ ~-_/ /|- _/ .|| \ /
//*** |~~ ~~|--~~~~--_ \ ~==-/ | \~--===~~ .\
//*** ' ~-| /| |-~\~~ __--~~
//*** |-~~-_/ | | ~\_ _-~ /\
//*** / \ \__ \/~ \__
//*** _--~ _/ | .-~~____--~-/ ~~==.
//*** ((->/~ '.|||' -_| ~~-/ , . _||
//*** -_ ~\ ~~---l__i__i__i--~~_/
//*** _-~-__ ~) \--______________--~~
//*** //.-~~~-~_--~- |-------~~~~~~~~
//*** //.-~~~--\
//***
//*** PT100驱动程序V4.0 (2021.05.28)
//*** By XingDala.2905469493.All Rights Reserved.
//***************************************************************************************
#include "stm32f10x.h"
#include "adc.h"
#include "OLED.h"
#include "PT100INT.h"
#include "sound.h"
#include "time.h"
int main(void)
{
ADC_init();//ADC初始化
time_init();//时钟初始化
OLED_Init();//OLED初始化
PT100_OLED();//PT100驱动程序显示初始化
sound_init();//蜂鸣器初始化
while(1)
{
PT100INT();//PT100驱动程序
}
}
2、TIME系统内部时钟模块
为流畅运行本次设计,因而选用STM32F103RCT6自带的系统时钟TIM2、TIM3、TIM4、TIM5、TIM6、TIM7。以往会使用高精度的中断函数加以控制,以达到STM32F103RCT6最极限的延时精度,但这里不要求精度,所以不需要使用内部中断来写延时。
其中,TIM2、TIM3、TIM4用来控制蜂鸣器报警系统,TIM5用来控制足以驱动并流畅运行系统各模块的延时(三种时间单位:us、ms、s),TIM6和TIM7用来控制AD采集的速度和滤波频次及速率。
图22 STM32F103RCT6系统内部时钟树
(1)初始化时钟TIM6、TIM7:
在所调用的6个系统时钟中,用来处理AD采集的两个系统时钟TIM6、TIM7是固定的速率,所以要对固定速率的系统时钟进行初始化。
(2)时钟程序:
时钟程序“time.c”
void time_init(void)//初始化
{
time6();
time7();
}
void time2(unsigned int n)//蜂鸣器响的持续时长
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef time2;
time2.TIM_CounterMode = TIM_CounterMode_Up;
time2.TIM_Period = n*10 - 1;
time2.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM2, & time2);
TIM_SetCounter(TIM2,0);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
}
void time3(unsigned int n)//蜂鸣器关的持续时长
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitTypeDef time3;
time3.TIM_CounterMode = TIM_CounterMode_Up;
time3.TIM_Period = n*10 - 1;
time3.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM3, & time3);
TIM_SetCounter(TIM3,0);
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
}
void time4(unsigned int n)//蜂鸣器音调
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseInitTypeDef time4;
time4.TIM_CounterMode = TIM_CounterMode_Up;
time4.TIM_Period = n - 1;
time4.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInit(TIM4, & time4);
TIM_SetCounter(TIM4,0);
TIM_ClearFlag(TIM4,TIM_FLAG_Update);
}
void T(unsigned int n,unsigned int x)//延时函数(us、ms、s)eg:T(152,1)表示延时125微秒,T(254,2)表示延时254毫秒
{
unsigned int j = 0,k = 0;
if(x == 1){j=72;k = 1;}//us
if(x == 2){j=7200;k = 10;}//ms
if(x == 3){j=7200;k = 10000;}//s
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
TIM_TimeBaseInitTypeDef time5;
time5.TIM_CounterMode = TIM_CounterMode_Up;
time5.TIM_Period = n*k - 1;
time5.TIM_Prescaler = j - 1;
TIM_TimeBaseInit(TIM5, & time5);
TIM_SetCounter(TIM5,0);
TIM_ClearFlag(TIM5,TIM_FLAG_Update);
TIM_Cmd(TIM5, ENABLE);
while(TIM_GetFlagStatus(TIM5,TIM_FLAG_Update)==RESET);
TIM_Cmd(TIM5, DISABLE);
}
void time6(void)//1S
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseInitTypeDef time6;
time6.TIM_CounterMode = TIM_CounterMode_Up;
time6.TIM_Period = 10000 - 1;
time6.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM6, & time6);
TIM_SetCounter(TIM6,0);
TIM_ClearFlag(TIM6,TIM_FLAG_Update);
}
void time7(void)//1ms
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
TIM_TimeBaseInitTypeDef time7;
time7.TIM_CounterMode = TIM_CounterMode_Up;
time7.TIM_Period = 10 - 1;
time7.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInit(TIM7, & time7);
TIM_SetCounter(TIM7,0);
TIM_ClearFlag(TIM7,TIM_FLAG_Update);
}
时钟程序“time.h”
#ifndef _TIME_H__
#define _TIME_H__
void time_init(void);
void time2(unsigned int n);
void time3(unsigned int n);
void time4(unsigned int n);
void T(unsigned int n,unsigned int x);
void time6(void);
void time7(void);
#endif
3、AD采集模块
(1)初始化:
配置STM32F103RCT6的ADC1接口的读取模式,打开ADC1的时钟,设置为模拟输入模式,配置单通道扫描模式,配置连续转化模式,定义规则通道的外部触发方式为内部触发,设置数据对齐方式为右对齐,设置ADC模拟通道个数为1,设置被采集的通道为ADC1,使能ADC1,使能ADC1的复位校准,开始ADC1的复位,ADC1的复位和校准结束后,ADC1初始化结束,把LM358的输出接到ADC1上即可准备AD值的读取。
图23 ADC1初始化流程图
(2)AD采集程序:
由于5V电压不稳定,导致输出的AD值也会微微波动,此刻就需要用到程序滤波。
使用时钟,每隔1ms采集一次AD值,并将所采集的AD值加起来,用IN存起来;使用时钟,每隔1s输出一次IN值,并将所输出的IN值除以1000,因为在这1s内采集了1000个AD值,除以1000后,就可以得到1S内平均AD值,也就是滤波。
接着,再利用sprintf把要转换的AD数值格式化为字符串,并用OLED显示屏显示出来,由于IN值1s输出一次,使用滤波后的平均AD值1秒刷新一次。
图24 STM32F103RCT6AD采集程序流程图
AD采集程序“adc.c”
#include "stm32f10x.h"
void ADC_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1 ,ADC_Channel_1 ,1 ,ADC_SampleTime_1Cycles5);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1) == SET);
}
AD采集程序“adc.h”
#ifndef _ADC_H__
#define _ADC_H__
void ADC_init(void);
#endif
4、蜂鸣器响应模块
(1)初始化:
打开蜂鸣器引脚所在的时钟,设置其为推挽输出模式。
图25 蜂鸣器响应模块初始化流程图
(2)蜂鸣器响应:
TIM2控制蜂鸣器通电的时长,TIM3控制蜂鸣器断电的时长,TIM4控制蜂鸣器发声的音调(频率),以此来调出万能的警报声音。当温度达到报警温度,触发蜂鸣器响应程序,程序流程图如图22所示:
图26 蜂鸣器响应模块报警主程序流程图
蜂鸣器鸣响程序“sound.c”
#include "stm32f10x.h"
#include "sound.h"
#include "time.h"
void sound_init(void)//初始化蜂鸣器
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef FMQ;
FMQ.GPIO_Pin = GPIO_Pin_8;
FMQ.GPIO_Speed = GPIO_Speed_50MHz;
FMQ.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &FMQ);
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}
void FMQ_T(void)//蜂鸣器响
{
TIM_Cmd(TIM2, ENABLE);
while(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update) == RESET)
{
TIM_Cmd(TIM4, ENABLE);
while(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update) == RESET)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}
TIM_ClearFlag(TIM4,TIM_FLAG_Update);
while(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update) == RESET)
{
GPIO_SetBits(GPIOB, GPIO_Pin_8);
}
TIM_ClearFlag(TIM4,TIM_FLAG_Update);
TIM_Cmd(TIM4, DISABLE);
}
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_Cmd(TIM2, DISABLE);
}
void FMQ_F(void)//蜂鸣器关
{
TIM_Cmd(TIM3, ENABLE);
while(TIM_GetFlagStatus(TIM3,TIM_FLAG_Update) == RESET)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_8);
}
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
TIM_Cmd(TIM3, DISABLE);
}
void sound1(void)//蜂鸣器鸣叫,节奏调节
{
time2(100);//响的持续时长
time4(100);//音调
FMQ_T();//蜂鸣器开始响一段时间
time3(100);//关的持续时长
FMQ_F();//蜂鸣器关闭一段时间
time2(100);//响的持续时长
time4(100);//音调
FMQ_T();//蜂鸣器开始响一段时间
GPIO_ResetBits(GPIOC, GPIO_Pin_13);//蜂鸣器关
}
蜂鸣器鸣响程序“sound.h”
#ifndef _SOUND_H__
#define _SOUND_H__
void sound_init(void);
void FMQ_T(void);
void FMQ_F(void);
void sound1(void);
#endif
5、OLED显示模块
(1)初始化:
打开OLED接口的时钟,推挽输出OLED两条信号线(SCL、SDA),并且一块拉高,延时200ms;关闭显示,设置时钟分频因子和震荡频率([3:0]分频因子、[7:4]震荡频率),设置驱动路数为默认0X3F(1/64),设置显示偏移默认为0,设置显示开始行 [5:0],电荷泵设置为bit2,设置内存地址模式页地址模式,段重定义设置为bit0:0,0->0;1,0->127,设置COM扫描方向为普通模式,设置COM硬件引脚配置为[5:4]配置,对比度设置为默认0X7F最亮,设置预充电周期为[3:0]、PHASE 1、[7:4]、PHASE 2,设置VCOMH 电压倍率为[6:4] 0.83*3.3,开启全局显示,设置显示方式为正常,开启显示,清屏,OLED初始化完成。
图27 OLED初始化流程图
(2)显示:
ZFC(0,0,"abc254!");//在坐标(0,0)显示字符串abc254!
ZF(0,0,'0');//在坐标(0,0)显示字符0
WZ(0,0,2,3);//在坐标(0,0)显示第3页的第2个汉字
TP(0,0,128,64,2);///在坐标(0,0)显示第二张图片,显示的图片范围为x:0~128,y:0~64
4、PT100系统总驱动模块
PT100的阻值是随着温度的变化而变化,在对各标准温度下温度AD采集的结果可以得出,AD值随着温度的升高而升高,其线性关系近乎是一条直线,但这条直线有略微的弯曲,所以需要进行大量的AD采集进行数据拟合。
本次设计计划使用100个AD采集数据,也就是100个温度区间,但一百个区间很多,不可能用计算器一个一个计算,所以这一百个区间(温度-AD)将被分别放入两个数组(一个数组放温度值,一个数组放AD值,两两对应),再在程序中用AD比对,找出此刻LM358传来的AD在哪个区间,再用固定的公式算出每个AD的温度是多少
该
区
间
温
度
变
化
/
该
区
间
A
D
变
化
=
每
个
A
D
是
多
少
温
度
该区间温度变化/该区间AD变化=每个AD是多少温度
该区间温度变化/该区间AD变化=每个AD是多少温度 再乘以此刻测出的实时AD值,算出准确的温度。
又由于供电不稳定,每次供电接口的微微位移,都会使得V供电偏大或者偏小,间接导致同温度下,LM358输出的AD与AD采集时的电压不一致,所以在AD值进入计算前,先借助标准仪表对比同温度下与AD采集时的AD偏移了多少AD,对程序中的AD进行误差加减,把微微偏移的AD曲线平移到AD采集时的曲线。
PT100测温系统程序“PT100INT.c”
#include "stm32f10x.h"
#include "adc.h"
#include "SSA.h"
#include "time.h"
#include "OLED.h"
unsigned int wucha = 0;//误差偏移修正
void PT100INT(void)
{
unsigned int Dout = 0;//清除AD数据缓存
unsigned int IN = 0;//采集AD前清零
TIM_Cmd(TIM6, ENABLE);//1s计时准备
while(TIM_GetFlagStatus(TIM6,TIM_FLAG_Update) == RESET)//开始1s计时,1s内采集1000次AD并软件滤波
{
TIM_Cmd(TIM7, ENABLE);//1ms计时准备
while(TIM_GetFlagStatus(TIM7,TIM_FLAG_Update) == RESET)//开始10ms计时,采集100次
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//AD采集准备
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//AD采集
Dout = ADC_GetConversionValue(ADC1);//AD数据缓存
}
TIM_ClearFlag(TIM7,TIM_FLAG_Update);//10ms结束,定时器清零,为下一次计时10ms做准备
TIM_Cmd(TIM7, DISABLE);//关闭10ms定时器
IN = IN + Dout;//AD累加,为滤波做准备
}
TIM_ClearFlag(TIM6,TIM_FLAG_Update);//1s结束,定时器清零,为下一次计时1s做准备
TIM_Cmd(TIM6, DISABLE);//关闭1s定时器
IN = (IN / 1000)+wucha;//滤波
PT100show_AD(IN);//AD值显示
//AD采集值,请从低温到高温排
unsigned int TEMP[]= //温度值
{
26,27,28,29,30,31,32,33,34,35,
36,37,38,39,40,41,42,43,44,45,
46,47,48,49,50,51,52,53,54,55,
56,57,58,59,60,61,62,63,64,65,
66,67,68,69,70,71,72,73,74,75
};
unsigned int AD[]= //AD值
{
937, 977,1017,1057,1098,1144,1101,1240,1289,1332,
1378,1423,1474,1525,1560,1610,1661,1706,1753,1786,
1847,1902,1949,1984,2048,2100,2188,2231,2275,2322,
2372,2420,2465,2511,2554,2602,2652,2701,2747,2802,
2841,2886,2937,2994,3037,3065,3139,3164,3194,3347
};
unsigned int Temp = 0;//温度值清零
if(IN < 937)//温度超过测量范围,使用最后测量范围的K值进行计算
{
Temp = IN*10*TEMP[1]/AD[1];//动态拟合比对,动态显示温度
}
else if(IN >= 937 && IN <= 3347)//温度在测量范围内,开始测温
{
unsigned int a=0;
while(Temp == 0)//算出温度后跳出
{
if(IN > AD[a])//按区间比对
{
Temp = IN*10*TEMP[a]/AD[a];//动态拟合比对,动态显示温度
}
a++;//AD采集区间切换
}
}
else if(IN > 3347)//温度超过测量范围,使用最后测量范围的K值进行计算
{
Temp = IN*10*TEMP[49]/AD[49];//动态拟合比对,动态显示温度
}
PT100show_Temp(Temp);温度显示
}
PT100测温系统程序“PT100INT.h”
#ifndef _PT100INT_H__
#define _PT100INT_H__
void PT100INT(void);
#endif
最终结果,所测试的温度与标准仪表相差
0.1
℃
0.1℃
0.1℃ ~
0.2
℃
0.2℃
0.2℃。
图28为PT100测温系统的测温显示效果:
图28 PT100测温系统显示面板
当前面板所显示的AD值每1s刷新一次;
所显示的温度值每1s刷新一次;
警报未响起时蜂鸣器状态显示“静音”,警报响起时蜂鸣器报警鸣叫,且状态显示“鸣响”,当报警声响三遍后,报警声停止,蜂鸣器报警系统进入睡眠状态,当温度继续上升超过报警温度1℃时,蜂鸣器报警系统又会进入预备触发状态,等待报警温度的再次达到报警温度值;
最底下的图案为系统运行指示灯,当系统正在(流畅)运行时,该图标是动态显示的动画状态,最右边的方形图标闪烁。
四、调试
1、电路调试
在设计电桥中,一开始是把
5
V
5V
5V接在PT100与精密电阻间,把GND接在两个
1
K
1K
1K中间,模拟软件测试没有问题,但实际电路中会出现问题,LM358的2号引脚和3号引脚不能承受过大的电压,最终导致LM358的输出为爆输出状态(标准温度下超过
3.3
V
3.3V
3.3V),但在LM358的芯片手册上提到这两个引脚所能接受的电压远远不止这么少,百思不得其解。
所以在设计中将GND接在PT100与精密电阻间,把
5
V
5V
5V接在两个
1
K
1K
1K中间,让
1
K
1K
1K去分掉电压,模拟软件测试也是没有问题,最终LM358的2号引脚和3号引脚所承受的电压降到了
0.5
V
0.5V
0.5V以下,输出也和模拟差不多,电路调试成功。
2、放大倍数调试
根据实测,实物与理论出现预料范围内的偏差(电桥输出比理论偏大,放大倍数比理论偏小),通过串联电位器(由于元件不足,没有过大的电位器),勉强把放大倍数调整到了32倍。
3、AD采集:
此时刚好测出一百个温度区间内的AD值(当温度降的慢AD变化率小的时候,选最大温度区间为
1
℃
1℃
1℃,;但当温度降得快,AD变化很大的时候,选用最小温度区间为
0.5
℃
0.5℃
0.5℃,多次高温采样),其折线图25所示。
图29 AD采集
一百个区间已出,每个区间的斜率
K
=
温
度
变
化
/
A
D
变
化
=
每
个
A
D
是
多
少
温
度
K= 温度变化/AD变化=每个AD是多少温度
K=温度变化/AD变化=每个AD是多少温度 很容易可以求出来,只要把AD乘上对应区间的该斜率,则可求出对应的温度。
版权声明:本文为CSDN博主「星达拉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xingdala/article/details/117934599
暂无评论