压力传感器
壓力傳感器
壓力傳感器是最常用的一種傳感器,其應(yīng)用范圍有各種工業(yè)互通環(huán)境,涉及航空,航天,軍工,石化,電力等。按照不同的測試,壓力類型可分表壓傳感器,差壓傳感器,絕壓傳感器。
表壓傳感器 ——> 相對于環(huán)境壓力
 差壓傳感器 ——> 兩個壓力間差值
 絕壓傳感器 ——> 相對于絕對壓力
絕對壓力是指立體承受的實際壓力是以真空狀態(tài)為起點計算的壓力均勻垂直作用,在物體表面上的力稱為壓力液體所受壓力的大小與受力面積之比叫做壓強
 計算公式為:
 
新型的壓力傳感器特點:體積小,質(zhì)量輕,在眾多領(lǐng)域中被廣泛使用。
 實現(xiàn)原理
 壓力變化 —> 阻值變化 —> 阻值電壓/電流 —> 采集數(shù)據(jù)
“惠斯通電橋” 它是能精確測量電阻變化的電路
 
 “惠斯通電橋的四分之一橋”在實際情況下一般將R4變?yōu)閼?yīng)變電阻,其他三個電阻值相等
 
 在公式中V0、VEX和R都是已知量,可以計算出來△R的值
那么如何將電阻變化
 與壓力產(chǎn)生的應(yīng)變(ε)建立聯(lián)系呢?
 我們用到另外一個公式
 GF的值是由生產(chǎn)商提供的,可以通過輸出電壓求出應(yīng)變值
“惠斯通電橋的二分之一橋”
 
不同之處:
 有兩個應(yīng)變未知電阻
通過簡化可得出
 
 出現(xiàn)了兩個未知量△R2和△R1,所以方程所以方程中出現(xiàn)了兩個未知量△R2和△R1,而一個方程不能求出兩個未知量的值
 可根據(jù)傳感器受到F力的作用下,隨之而改變的應(yīng)變電阻R3會收縮 -vε,應(yīng)變電阻R4拉伸的應(yīng)變值為ε,公式中 V是固定值稱為“泊松比”。
 
 根據(jù)以上公式,可以將△R2用△R1表示出來,就能通過輸出電壓計算出應(yīng)變值
還有一種全橋應(yīng)變電阻方式,這三種都用各自的有缺點可根據(jù)實際情況采用哪種方式。
在實際中電橋中數(shù)值是不為“0”的,需要調(diào)零處理
 
 輸出的電壓為毫伏級,單位是mV/V。
 假設(shè)傳感器參數(shù)為下圖所示,它滿載輸出的電壓為20mV。
 
 單片機檢測這么小的電壓值,需要經(jīng)過放大處理或用專用的集成芯片
ADC 模數(shù)轉(zhuǎn)換部分
 上面剛說了,通過檢測傳感器的電壓,就可以計算出重量,所以就需要一個模擬電壓采集電路,看上圖可以知道,S+和 S-實際上是一個電橋的輸出。假設(shè),壓力傳感器的輸出靈敏度為 1mV/V,即表示若激勵電壓是 5V,則輸出范圍±5mV。這個輸出電壓通常是幾毫伏,不僅非常微小,而且這還是一對差分信號。比如 S+對地電壓是 2.51V,S-對地電壓是 2.50V,那么 S+和 S-之間的電壓差就是 0.01V,這個 0.01V 的信號就叫差分信號,他們的共模電壓就是 2.50V。在檢測電壓時,可以先檢測一下 S+的對地電壓,再檢測一下 S-的對地電壓,然后再做一個減法運算就行了,這樣需要采集兩次電壓,不僅增加了運算量,而且每次采集都是帶誤差的,而且這里的有效信號僅僅是那個 0.01V 的差。所以,我們就需要一個差分 ADC 芯片,例如:CS1237。這個芯片的輸入信號可以是一個差分信號,也就是那個 0.01V 的電壓,而且內(nèi)部還帶有一個放大器,可以把這個 0.01V 的信號放大 128 倍。由于是差分輸入的芯片,所以需要特別注意一下它能夠承受的共模電壓大小,CS1237 可以承受最大共模電壓是芯片的電源電壓。
 還是假設(shè) S+和 S-之間的電壓差是 0.01V,那么可以經(jīng)過 128 倍放大,就變成了 1.28V。使用上圖所示的接線方法,使用 3.3V 的供電,由于是差分輸入的結(jié)構(gòu),所以 CS1237 可以接受的信號是±3.7mV。這樣有一個好處,就是當(dāng)傳感器安裝時候,不用擔(dān)心受力方向了,不管如何安裝,只要經(jīng)過校準(zhǔn),都可以檢測到有效的重量。
電壓轉(zhuǎn)重量的算法程序
 通上電之后,秤盤上面不放任何東西,此時傳感器會輸出一個電壓,不要管具體是多少,因為每一個批次的傳感器都會有微小差異,擰螺絲的力道不一樣這個電壓也不會一樣。無論如何,此時能夠采集到一個數(shù)值,想象一下,這是數(shù)軸上的一個點 A。這個時候,再在秤盤上面放一個 500 克的砝碼,不管電壓是往正方向走還是往負方向走,反正采集到的電壓肯定會偏離 A 點一段距離的,記錄這個點位 B。那么用 B-A(兩個數(shù)值是帶正負號的計算)得到的數(shù)值就是在這個電子秤中,500克的重量對應(yīng)的那一段。此時可以計算一下稱重系數(shù) C=500/(B-A)。數(shù)值 C 是一個小數(shù)表示 CS1237 采集的數(shù)字,每一個數(shù)字對應(yīng)的重量,比如可能是 C=0.01,那么如果單片機再讀取到一個數(shù)據(jù)是 D,D-A 如果等于 1000,那么秤盤上放的這個物品就是 10 克。(具體程序可參考配套例程)
程序部分
CS1237.c
//作者網(wǎng)址: WWW.JIXIN.PRO #include "CS1237.h" //20bit ADC#define ADC_Bit 20 //ADC有效位數(shù),帶符號位 最高24位 #define SCK_1 SCLK = 1 #define SCK_0 SCLK = 0 #define DAT_1 DOUT = 1 #define DAT_0 DOUT = 0 #define NOP_5() _nop_();_nop_();_nop_() #define NOP30() NOP_5();NOP_5();NOP_5();NOP_5();NOP_5();NOP_5(); #define NOP40() NOP_5();NOP_5();NOP_5();NOP_5();NOP_5();NOP_5();NOP_5();NOP_5(); #define One_CLK SCK_1;NOP40();SCK_0;NOP40(); #define CS_CON 0X0C //芯片地配置 內(nèi)部REF 輸出10HZ PGA=128 通道A 0X0C #define Lv_Bo 0.00001 //濾波系數(shù) 小于1sbit DOUT = P3^7;//數(shù)據(jù)對應(yīng)IO口 sbit SCLK = P3^5;//時鐘對應(yīng)IO口 static long AD_Res_Last=0;//上一輪的ADC數(shù)值保存//延時500US 5.5296MHZ void delay_500us(volatile unsigned char a) { volatile unsigned char i,j,b;for(b=0;b<a;b++){i = 3;j = 137;do{while (--j);} while (--i);} } //CS1237進入低功耗模式 void CS1237_Power_Down(void) {SCLK = 1;delay_500us(100);SCLK = 1;SCLK = 1;delay_500us(100); } //配置CS1237芯片 void Con_CS1237(void) {unsigned char i;unsigned char dat;unsigned char count_i=0;//溢出計時器dat = CS_CON;// 0100 1000SCK_0;//時鐘拉低while(DOUT)//芯片準(zhǔn)備好數(shù)據(jù)輸出 時鐘已經(jīng)為0,數(shù)據(jù)也需要等CS1237全部拉低為0才算都準(zhǔn)備好{delay_500us(10);count_i++;if(count_i > 150){SCK_1;DAT_1;return;//超時,則直接退出程序}}for(i=0;i<29;i++)// 1 - 29{One_CLK;}SCK_1;NOP30();DAT_1;SCK_0;NOP30();//30SCK_1;NOP30();DAT_1;SCK_0;NOP30();//31SCK_1;NOP30();DAT_0;SCK_0;NOP30();//32SCK_1;NOP30();DAT_0;SCK_0;NOP30();//33SCK_1;NOP30();DAT_1;SCK_0;NOP30();//34SCK_1;NOP30();DAT_0;SCK_0;NOP30();//35SCK_1;NOP30();DAT_1;SCK_0;NOP30();//36One_CLK;//37 寫入了0x65for(i=0;i<8;i++)// 38 - 45個脈沖了,寫8位數(shù)據(jù){SCK_1;NOP40();if(dat&0x80)DAT_1;elseDAT_0;dat <<= 1;SCK_0;NOP40();}One_CLK;//46個脈沖拉高數(shù)據(jù)引腳 } //讀取芯片的配置數(shù)據(jù) unsigned char Read_CON(void) {unsigned char i;unsigned char dat=0;//讀取到的數(shù)據(jù)unsigned char count_i=0;//溢出計時器unsigned char k=0,j=0;//中間變量SCK_0;//時鐘拉低while(DOUT)//芯片準(zhǔn)備好數(shù)據(jù)輸出 時鐘已經(jīng)為0,數(shù)據(jù)也需要等CS1237全部拉低為0才算都準(zhǔn)備好{delay_500us(10);count_i++;if(count_i > 150){SCK_1;DAT_1;return 1;//超時,則直接退出程序}}for(i=0;i<29;i++)// 1 - 29{One_CLK;}SCK_1;NOP30();DAT_1;SCK_0;NOP30();//30SCK_1;NOP30();DAT_0;SCK_0;NOP30();//31SCK_1;NOP30();DAT_1;SCK_0;NOP30();//32SCK_1;NOP30();DAT_0;SCK_0;NOP30();//33SCK_1;NOP30();DAT_1;SCK_0;NOP30();//34SCK_1;NOP30();DAT_1;SCK_0;NOP30();//35SCK_1;NOP30();DAT_0;SCK_0;NOP30();//36DAT_1;One_CLK;//37 寫入了0x56dat=0;for(i=0;i<8;i++)// 38 - 45個脈沖了,讀取數(shù)據(jù){One_CLK;dat <<= 1;if(DOUT)dat++;}One_CLK;//46個脈沖拉高數(shù)據(jù)引腳return dat; } //讀取ADC數(shù)據(jù),返回的是一個有符號數(shù)據(jù) long Read_CS1237(void) {unsigned char i;long dat=0;//讀取到的數(shù)據(jù)unsigned char count_i=0;//溢出計時器DOUT = 1;//端口鎖存1,51必備SCK_0;//時鐘拉低while(DOUT)//芯片準(zhǔn)備好數(shù)據(jù)輸出 時鐘已經(jīng)為0,數(shù)據(jù)也需要等CS1237拉低為0才算都準(zhǔn)備好{delay_500us(10);count_i++;if(count_i > 300){SCK_1;DAT_1;return 0;//超時,則直接退出程序}}DOUT = 1;//端口鎖存1,51必備dat=0;for(i=0;i<24;i++)//獲取24位有效轉(zhuǎn)換{SCK_1;NOP40();dat <<= 1;if(DOUT)dat ++;SCK_0;NOP40(); }for(i=0;i<3;i++)//一共輸入27個脈沖{One_CLK;}DAT_1;//先根據(jù)宏定義里面的有效位,丟棄一些數(shù)據(jù)i = 24 - ADC_Bit;//i表示將要丟棄的位數(shù)dat >>= i;//丟棄多余的位數(shù)return dat; } //初始化ADC相關(guān)參數(shù) void Init_CS1237(void) {Con_CS1237();//配置CS1237if(Read_CON() != CS_CON)//如果讀取的ADC配置出錯,則重啟IAP_CONTR = 0x20;AD_Res_Last = Read_CS1237();AD_Res_Last = Read_CS1237();AD_Res_Last = Read_CS1237(); } //數(shù)字一階濾波器 濾波系數(shù)A,小于1。上一次數(shù)值B,本次數(shù)值C out = b*A + C*(1-A) //下面這個程序負責(zé)讀取出最終ADC數(shù)據(jù) long Read_18Bit_AD(void) {float out,c;out = AD_Res_Last;c = Read_CS1237();if(c!=0) // 讀到正確數(shù)據(jù){out = out*Lv_Bo + c*(1-Lv_Bo);AD_Res_Last = out;//把這次的計算結(jié)果放到全局變量里面保護}return AD_Res_Last; } //main.c
//作者網(wǎng)址: WWW.JIXIN.PRO //內(nèi)部時鐘 5.5296MHZ //電池供電可用,實時顯示重量,單位g //KEY1 校準(zhǔn)時第一步空稱 ; KEY2 砝碼校準(zhǔn)500G砝碼 ;KEY3 常規(guī)去皮功能 #include "STC15W.h" #include "intrins.h" #include "Uart_1.h" #include "OLED_IIC.h" #include "CS1237.h" #include "T4_Key.h" #include "ADC.h" #include "IAP_EEPROM.h"#define Tare EEROM_20Long[0] //校準(zhǔn)用,皮重 #define BGA_EEPROM EEROM_20Long[1] //在5V供電下,校準(zhǔn)的內(nèi)部BGA參數(shù) #define First_ON EEROM_20Long[2] //第一次開機標(biāo)志位 如果不是88則表示第一次開機,用于批量生產(chǎn) #define Weight_500g EEROM_20Long[3] //500G標(biāo)定時候的ADC數(shù)值extern bit Key_1,Key_2,Key_3;//三個按鍵的狀態(tài),全局變量 1表示按下 ,每次用過之后需要手動置零 extern unsigned int Battery_Now;//計算出來的當(dāng)前電池電壓 long EEROM_20Long[20];//用于掉電保存的數(shù)值,每次燒寫過后可能會歸零 unsigned char Work_Count=0;//放在定時器里面,每50ms進行一次顯示,稱重。 unsigned int Low_Power = 0;//放到定時器里面,進行關(guān)機檢測,30S重量沒有變化后進入低功耗模式 static float Weight_Coe=0.00000;//全局變量,稱重時參考的重量系數(shù) static float Tare_Coe=0.00000;//全局變量,皮重的重量,放大1000倍之后的數(shù)值 static unsigned int Weight_30S_1,Weight_30S_2;//30S讀取一次重量,然后比較,如果兩次相同則進入低功耗 static unsigned char Power_Down_F=0;//單片機掉電標(biāo)志位,用于掉電重啟后初始化所有設(shè)備//獲取電池電壓,50次平均值 unsigned int Get_Bat(void); //讀取CS1237,去皮后的重量,精確到g unsigned int Get_Weight(void); //根據(jù)校準(zhǔn)的數(shù)值,計算出稱重系數(shù) void Get_Weight_Coe(void);void main(void) {unsigned int Main_Loop=0;//在主函數(shù)里面用的,循環(huán)時候控制循環(huán)次數(shù)的變量P0M1=0;P0M0=0;P1M1=0;P1M0=0;P2M1=0;P2M0=0;P3M1=0;P3M0=0;P4M1=0;P4M0=0;P5M1=0;P5M0=0;//上電初始化所有IO口為普通IOInit_Uart1();//初始化串口1,9600bpsOLED_Init(); //OLED初始化Init_T4();//初始化T4,用于按鍵檢測Init_CS1237();//初始化CS1237Init_ADC();//初始化ADCEA = 1;//打開單片機全局中斷Re_20_Long(0XD3B8,EEROM_20Long);//讀取所有的掉電保存數(shù)據(jù)到內(nèi)存里Delay1ms(10);if(First_ON != 88)//如果檢測到第一次開機情況,表示需要校準(zhǔn),用于批量生產(chǎn)時候{LED2 = 0;//燈亮First_ON = 88;Main_Loop = 10;while(Main_Loop--)Tare = Read_18Bit_AD();//讀取出CS1237的數(shù)據(jù),5V情況下Main_Loop = 10;while(Main_Loop--)BGA_EEPROM = Get_BGA();//在5V供電情況下,保存BGA參數(shù)Wr_20_Long(0XD3B8,EEROM_20Long);//保存數(shù)據(jù)到EEPROMLED2 = 1;//燈滅}elseGet_Weight_Coe();//依據(jù)EEPROM內(nèi)容,計算稱重系數(shù)Send_Data1(Read_CON());//發(fā)送CS1237配置信息到串口//---------------------------------------------------------------//LED2 = 0;//燈亮Main_Loop = 10;//開機自動讀取一次當(dāng)前皮重。while(Main_Loop--)//連續(xù)讀取10次,是因為CS1237讀取程序里有軟件濾波器,這樣做更接近真實值Tare_Coe = Read_18Bit_AD();//讀取出皮重的ADC數(shù)據(jù)Tare_Coe *= Weight_Coe;LED2 = 1;//關(guān)燈//--------------------------------------------------------------// while(1){if(Key_1)//去皮后的值保存到EEPROM里面,必須在5V環(huán)境下 校準(zhǔn)專用{Key_1 = 0;LED2 = 0;//燈亮Main_Loop = 10;while(Main_Loop--)Tare = Read_18Bit_AD();//讀取出CS1237的數(shù)據(jù),5V情況下Main_Loop = 10;while(Main_Loop--)BGA_EEPROM = Get_BGA();//在5V供電情況下,保存BGA參數(shù)Wr_20_Long(0XD3B8,EEROM_20Long);//保存數(shù)據(jù)到EEPROMLED2 = 1;//關(guān)燈}if(Key_2)//放上一個500g砝碼,用于校準(zhǔn)誤差,必須在5V供電環(huán)境下{Key_2 = 0;LED2 = 0;//燈亮Main_Loop = 10;while(Main_Loop--)Weight_500g = Read_18Bit_AD();//讀取出500G的數(shù)據(jù)Weight_Coe = Weight_500g - Tare;//除去皮重的ADC數(shù)值Weight_Coe = 500000 / Weight_Coe;//放大1000倍的斜率Tare_Coe = Weight_Coe * Tare;//皮重的重量,放大1000倍之后的Wr_20_Long(0XD3B8,EEROM_20Long);//保存數(shù)據(jù)到EEPROMLED2 = 1;} if(Key_3)//正常的去皮重,不保存到EEPROM里面{Key_3 = 0;LED2 = 0;Main_Loop = 10;while(Main_Loop--)Tare_Coe = Read_18Bit_AD();//讀取出皮重的ADC數(shù)據(jù)Tare_Coe *= Weight_Coe;LED2 = 1;}//定時器控制的子程序,每150ms調(diào)用一次if(Work_Count == 4){OLED_ShowNum(7*9,2,Get_Bat(),4,16);//顯示電池電壓Weight_30S_1 = Get_Weight();//每一次稱重,都要更新一下用于低功耗的數(shù)據(jù)OLED_ShowNum(7*8,4,Weight_30S_1,4,16);//OLED顯示重量Work_Count = 0;if(Weight_30S_1 == Weight_30S_2){Low_Power ++;}else{Weight_30S_2 = Weight_30S_1;Low_Power = 0;}}//if(Low_Power > 150){CS1237_Power_Down();//CS1237進入低功耗模式OLED_Power_Down();//OLED進入低功耗模式INT_CLKO |= 0X10;//使能INT2中斷,主要用于喚醒單片機//所有IO口設(shè)置為高阻輸入P0M1=0;P0M0=0;P1M1=0;P1M0=0;P2M1=0;P2M0=0;P3M1=0;P3M0=0;P4M1=0;P4M0=0;P5M1=0;P5M0=0;P0 = 0xff;P1 = 0xff;P2 = 0xff;P3 = 0xff;P4 = 0xff;P5 = 0xff;Power_Down_F = 0;PCON |= 0X02;//單片機進入停機模式 while(1){if(Power_Down_F)//IAP_CONTR = 0x20;}}} } //獲取電池電壓,50次平均值 unsigned int Get_Bat(void) {unsigned char i=50;unsigned long dat=0;while(i--){Get_Vol();//主要是為了獲取電池電壓dat += Battery_Now;}dat /= 50;dat /= 100;//特意忽略電壓的最后兩位 表示以V為單位的電壓保留一位小數(shù) 比如3800mV,即3.8Vdat *= 100;return dat; } //讀取CS1237,去皮后的重量,精確到g unsigned int Get_Weight(void) {float dat;unsigned long dat2;dat = Read_18Bit_AD();dat *= Weight_Coe;//計算出當(dāng)前重量,毛重,1000倍放大的dat -= Tare_Coe;//減去皮重的重量if(dat<0)dat = 0;dat2 = dat;dat2 /= 100;//準(zhǔn)備四舍五入,因為放大了100倍,所以現(xiàn)在保留了小數(shù)點后一位if((dat2 % 10) > 5){dat2 /= 10;dat2 += 1;}else{dat2 /= 10;}return dat2; } //根據(jù)校準(zhǔn)的數(shù)值,計算出稱重系數(shù) void Get_Weight_Coe(void) {Weight_Coe = Weight_500g - Tare;//除去皮重的ADC數(shù)值Weight_Coe = 500000 / Weight_Coe;//放大1000倍的斜率Tare_Coe = Weight_Coe * Tare;//皮重的重量,放大1000倍之后的 } // //外部中斷入口,主要用于掉電喚醒 void EX_Int2(void) interrupt 10 //INT2 {Power_Down_F = 1 ; }//總結(jié)
 
                            
                        - 上一篇: 频谱分析仪的工作原理
- 下一篇: R4S 玩转docker(一)
