STM32实现PT100测温系统设计报告(OLED屏显示)


  本项目设计内容涉及:传感器、嵌入式系统应用、模拟电子技术。

  课程:《智能传感技术》

  指导老师:覃园芳老师

一、任务要求

  设计PT100温度传感器的信号采集电路,使用嵌入式系统实验板采集外部的温度并显示。

要求:

  1. 范围: 20 20 20 ~ 80 ℃ 80℃ 80
  2. 精度:与标准仪器比较小于 0.3 ℃ 0.3℃ 0.3
  3. 30 30 30 50 50 50 70 ℃ 70℃ 70三级高温报警。

注:本次设计采用的主板芯片为STM32F103RCT6

二、硬件设计

1、分析所要使用的PT100温度传感器。

  结合有关PT100的资料,可得几个重要知识:

  • PT100的阻值会随温度的变化而成正比变化(温度越高阻值越大),但阻值变化很小,约等于 0.385 Ω / 度 0.385Ω/度 0.385Ω/
  • PT100的测温范围是 ﹣ 200 ℃ ﹣200℃ 200 ~ 150 ℃ 150℃ 150且在0℃时,阻值刚好等于 100 Ω 100Ω 100Ω
  • PT100的工作电流要小于 5 m A 5mA 5mA
  • PT100的阻值虽然随温度的变化而成正比变化,但在不同温度区间内其变化的速率(也就是 K 值 K值 K)不一样。

2、设计PT100驱动电路。

  结合所学经验,开始有想法:

  如果把PT100当电阻串联在电路中,并用主板上STM32F103RCT6芯片的PA1引脚的ADC功能去读取电压值并进行温度转换,可得出温度,如图1所示:图1  PT100串联电路测AD法

图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.890.005=0.54945V
  将其根据AD转换的换算公式换算成AD值大概为 0.54945 / 3.3 ∗ 4096 = 681.98 ≈ 682 0.54945/3.3*4096=681.98≈682 0.54945/3.34096=681.98682
  当温度上升一度,假设PT100的阻值刚好上升了 0.385 Ω 0.385Ω 0.385Ω,那么其分掉的电压的变动值约等于
0.385 ∗ 0.005 = 0.001925 V 0.385*0.005=0.001925V 0.3850.005=0.001925V
  将其根据AD转换的换算公式换算成AD值大概为 0.001925 / 3.3 ∗ 4096 = 2.39 ≈ 2 0.001925/3.3*4096=2.39≈2 0.001925/3.34096=2.392
   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+R4R1R2+R3R2)

  当 R 1 ∗ R 3 = R 2 ∗ R 4 R1*R3=R2*R4 R1R3=R2R4,该直流电桥会达到平衡,带入公式可得输出U2的值为 U 2 = U 1 ∗ 0 = 0 V U2=U1*0=0V U2=U10=0V  也就是该直流电桥在平衡状态下,输出电压为 0 V 0V 0V

  当 R 1 ∗ R 3 ≠ R 2 ∗ R 4 R1*R3≠R2*R4 R1R3=R2R4时,该直流电桥的平衡会被打破,带入公式可得输出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.890.005=0.54945V  也就是说R3和R4至少要分掉的电压为 5 - 0.54945 = 4.45055 V 5-0.54945=4.45055V 50.54945=4.45055V

  设 R 1 = R 2 = 109.89 Ω R1=R2=109.89Ω R1=R2=109.89Ω,在此刻, 5 V 5V 5V通电的电桥为一个并联电路,可变形为图4所示的电路:
PT100单臂电桥变形图

图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 50.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 U1U2,输出电压为 X ∗ ( U 1 - U 2 ) X*(U1-U2) X(U1U2)。则该差分放大器的差分电压放大倍数为 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=(R1R4)R2(R2R4)R3U1R2R4R2

  在实际应用中,一般使 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(U1U2)R4

  当所前边所设计的电桥与差分放大电路结合起来, U 1 - U 2 U1-U2 U1U2就为电桥输出的电势差,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(R1R4R1R2R3R2)=5(138.81000138.81001000100)=0.154867963087V0.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.15521.2921倍。

  但是在现实中, 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(R1R4R1R2R3R2)=5(131.11000131.11081000108)=0.0.2159735882V0.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.09235.8735倍。

  接下来就要计算电阻取值了,因为 35 ∗ R 1 = 35 ∗ R 2 = R 3 = R 4 35*R1=35*R2=R3=R4 35R1=35R2=R3=R4,所以可以设R1、R2的阻值为 1 ∗ X Ω 1*XΩ 1XΩ,R3、R4的阻值为 35 ∗ X Ω 35*XΩ 35XΩ,只要算出 X X X即可算出全部的电阻, X X X为该差分放大电路的输入电阻值,如图7:
35倍差分放大电路

图7 35倍差分放大电路


  现在生产的运放,一般其输入阻抗都很高,所以运放输入端电阻选择余地比较大,但又为了减少偏置电流带来的影响,降低噪声和温漂的影响,输入电阻的取值一般选择在 10 k Ω 10kΩ 10kΩ ~ 100 K Ω 100KΩ 100KΩ的区间。

  本次设计所使用的运算放大器为LM358,其内部集成结构如图7所示:
LM358集成结构

图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来达到想要的放大倍数。
PT100驱动电路

图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布局图


  焊接效果图:
PT100驱动电路V1.0(万用阻值可调实验板)-未接线

图10 PT100驱动电路V1.0(万用阻值可调实验板)-未接线


  使用杜邦线连接各个电阻,对电阻阻值进行精确调整;再用杜邦线连接整个电路,完成驱动板的硬件制作,如图11。
 PT100驱动电路V1.0(万用阻值可调实验板)-接线

图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;多排针焊接,跳线帽完成各元件的连接,可用万用表探查线路中电阻、电压、电流的变化。

  焊接布局图:
PT100驱动电路V2.0布局图

图12 PT100驱动电路V2.0布局图


  焊接效果图:
PT100驱动电路V2.0

图13 PT100驱动电路V2.0(测试板)


  较上一版本,新增电源控制模块,控制PT100驱动板的开关;并且体积明显减小,方便携带。
PT100驱动电路V2.0(测试板)

图14 PT100驱动电路V2.0(测试板)


  ③PT100驱动电路V3.0

  功能:在V2.0的基础下,双面焊接,提高电路阻值精度和电路布线简洁度;新增可调整放大倍数(30倍~35倍)的5K电位器于底部;去除测试数据用的端口(排针、跳线帽);新增独立电源接口,可单独给该驱动板供电。

  焊接布局图:略。

  焊接效果图:
PT100驱动电路V3.0

图15 PT100驱动电路V3.0

4、设计PT100驱动电路与STM32F103RC实验板的电气连接思路框图。

PT100驱动板与STM32F103RC实验板的电气连接框图

图16 PT100驱动板与STM32F103RC实验板的电气连接框图

5、了解已有蜂鸣器电路与STM32F103RCT6实验板的电路图。

已有蜂鸣器电路与STM32F103RCT6实验板的电路图

图17 已有蜂鸣器电路与STM32F103RCT6实验板的电路图

6、了解已有OLED电路与STM32F103RCT6实验板的电路图。

已有蜂鸣器电路与STM32F103RCT6实验板的电路图

图18 已有蜂鸣器电路与STM32F103RCT6实验板的电路图

7、实现STM32F103RCT6实验板外设改装。

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实验板的电气连接(实际电路连接)。

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采集的速度和滤波频次及速率。
STM32F103RCT6系统内部时钟树

图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值的读取。
ADC1初始化流程图

图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秒刷新一次。
STM32F103RCT6AD采集程序流程图

图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初始化完成。
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乘上对应区间的该斜率,则可求出对应的温度。

猜你喜欢

转载自blog.csdn.net/xingdala/article/details/117934599