以前在X寶上買過一個稱重放大器,180+大洋。原理基本上就是把橋式拉力傳感器輸出的mV級信號放大到5V供單片機讀取。連接實驗電路的時候很完美,能
實現重量的轉換,但是實際組裝后卻發現這種A/A模塊受到的干擾太嚴重了,包括電源的干擾,導線長短的干擾,導線位置變化的干擾,無線電的干擾等等等
等……實在是惱人。
后來感覺是思路錯誤了,就不該用模擬信號來傳輸,于是決定使用A/D模塊來把重量轉換成數字信號傳輸,A/D模塊就固定在離傳感器最近的地方,將輸出的數字信號用導線傳輸給單片機,這樣能很大程度減小各種干擾。
在網上搜了一下A/D模塊,基本上有兩種廉價成品:HX711和PCF8591,它們分別是8位轉換模塊和24位轉換模塊。
PCF8591:
113403yljygeouubgbooyo.png (319.11 KB, 下載次數: 244)
下載附件
2016-1-23 00:58 上傳
功能簡介: 基于I2C接口的AD/DA轉換模塊,8位精度,四通道AD,單通道DA,電壓輸出型
最大特點: 1. 支持兩種接口類型接入目標板:排針或排座
2. 支持I2C總線級聯(通過排針、排座對接的方法,可同時使用多個I2C模塊)
典型應用: 低速AD/DA轉換
主要資源: PCF8591,I2C接口排針,I2C接口排座,可調電阻,AD輸入口,DA輸出口,地址跳線端口
HX711:
233501r6766iuo0csk0o1u.jpg (149.81 KB, 下載次數: 225)
下載附件
2016-1-23 00:58 上傳
模塊工作電壓:4.8-5.5v
典型電流 1.6mA
體積:長 2.9cm * 寬 1.7cm * 高 0.4cm
● 帶金屬屏蔽,強抗干擾,預留MCU(STC15F104)位置,可自行升級二次開發。
● 兩路可選擇差分輸入
● 片內低噪聲可編程放大器,可選增益為32、64 和128
● 片內穩壓電路可直接向外部傳感器和芯片內A/D 轉換器提供電源
● 片內時鐘振蕩器無需任何外接器件,必要時也可使用外接晶振或時鐘
● 上電自動復位電路
● 簡單的數字控制和串口通訊:所有控制由管腳輸入,芯片內寄存器無需編程
● 可選擇10Hz 或80Hz 的輸出數據速率
● 同步抑制50Hz 和60Hz 的電源干擾
● 耗電量(含穩壓電源電路):
典型工作電流:< 1.7mA, 斷電電流:< 1μA
● 工作電壓范圍:2.6 ~ 5.5V
● 工作溫度范圍:-20 ~ +85℃
114637shy565thz23nfzfx.jpg (77.27 KB, 下載次數: 229)
下載附件
2016-1-23 00:58 上傳
===============================我叫分割線=================================
我要使用的拉力傳感器為傳統的橋式傳感器,量程1kg,想要達到1g的精度,這就要求模塊輸出位數至少為10位(2^10=1024),8位的
PCF8591無法勝任,因為它的精度只有2^8=256位。所以決定使用HX711作為我的A/D模塊,而且我選的HX711模塊還帶屏蔽殼,正符合我
的要求。
先來看一下該模塊的常用接法:
114513lmwalwvc5wj1j1j5.jpg (168.8 KB, 下載次數: 208)
下載附件
2016-1-23 00:58 上傳
模擬輸入
通道 A 模擬差分輸入可直接與橋式傳感器的差分輸出相接。由于橋式傳感器輸出的信號較小,為了充分利用A/D 轉換器的輸入動態范圍,該通道的可編程增益較大,為128 或64。這些增益所對應的滿量程差分輸入電壓分別±20mV 或±40mV。
通道B 為固定的32 增益,所對應的滿量程差分輸入電壓為±80mV。通道B 應用于包括電池在內的系統參數檢測。
供電電源
數字電源(DVDD)應使用與MCU 芯片相同的的數字供電電源。HX711 芯片內的穩壓電路可同時向 A/D
轉換器和外部傳感器提供模擬電源。穩壓電源的供電電壓(VSUP)可與數字電源(DVDD)相同。穩壓電源的輸出電壓值(VAVDD)由外部分壓電阻
R1、R2 和芯片的輸出參考電壓VBG
決定(圖1),VAVDD=VBG(R1+R2)/R2。應選擇該輸出電壓比穩壓電源的輸入電壓(VSUP)低至少100mV。
如果不使用芯片內的穩壓電路,管腳VSUP應連接到DVDD 或AVDD 中電壓較高的一個管腳上。管腳VBG 上不需要外接電容,管腳VFB
應接地,管腳BASE 為無連接。時鐘選擇如果將管腳 XI 接地,HX711
將自動選擇使用內部時鐘振蕩器,并自動關閉外部時鐘輸入和晶振的相關電路。這種情況下,典型輸出數據速率為10Hz
或80Hz。如果需要準確的輸出數據速率,可將外部輸入時鐘通過一個20pF 的隔直電容連接到XI管腳上,或將晶振連接到XI 和XO
管腳上。這種情況下,芯片內的時鐘振蕩器電路會自動關
閉,晶振時鐘或外部輸入時鐘電路被采用。此時,若晶振頻率為11.0592MHz, 輸出數據速率為準確的10Hz
或80Hz。輸出數據速率與晶振頻率以上述關系按比例增加或減少。使用外部輸入時鐘時,外部時鐘信號不一定需要為方波。可將MCU
芯片的晶振輸出管腳上的時鐘信號通過20pF 的隔直電容連接到XI管腳上,作為外部時鐘輸入。外部時鐘輸入信號的幅值可低至150mV。
串口通訊
串口通訊線由管腳PD_SCK 和DOUT 組成,用來輸出數據,選擇輸入通道和增益。當數據輸出管腳DOUT 為高電平時,表明
A/D 轉換器還未準備好輸出數據,此時串口時鐘輸入信號PD_SCK 應為低電平。當DOUT 從高電平變低電平后,PD_SCK 應輸入25 至27
個不等的時鐘脈沖(圖二)。其中第一個時鐘脈沖的上升沿將讀出輸出24 位數據的最高位(MSB),直至第24 個時鐘脈沖完成,24
位輸出數據從最高位至最低位逐位輸出完成。第25至27 個時鐘脈沖用來選擇下一次A/D 轉換的輸入通道和增益,參見表三。
114746w2jwooj4cew4d99o.jpg (28.18 KB, 下載次數: 235)
下載附件
2016-1-23 00:58 上傳
PD_SCK 脈沖數輸入通道 增益
114820jxy141ixdiee34e4.jpg (63.93 KB, 下載次數: 234)
下載附件
2016-1-23 00:58 上傳
時序圖
PD_SCK 的輸入時鐘脈沖數不應少于25 或多于27,否則會造成串口通訊錯誤。當A/D 轉換器的輸入通道或增益改變時,A/D 轉換器需要4 個數據輸出周期才能穩定。DOUT 在4 個數據輸出周期后才會從高電平變低電平,輸出有效數據。
==============================我也是分割線================================
好了,看了這么多原理很多人一定已經暈了,下面來簡單的:HX711在Arduino上的使用!
經過各種搜索,僅僅在Google Project上找到了一個 phk@FreeBSD.ORG 寫的 Arduino 庫(感謝啊!),功能很完善,而且支持多版本的Arduino。
我備份在這里吧,以免以后找不到:
hx711-arduino-v0.01.tar.gz
(1.3 KB, 下載次數: 179)
2016-1-23 00:59 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
再來看一下接線:
115526uh8es1vuwu11ob4x.jpg (76 KB, 下載次數: 242)
下載附件
2016-1-23 00:58 上傳
1. VCC 可以是 2.6-5.5 中的任意值,因為我們使用的是 Arduino ,所以直接5V供電,GND 接地。
2. SCK 接 Arduino 的 Pin 9,DT 接 Pin10,這兩個接腳可以在程序中改變。
3. E+、E-、A+ 和 A- 分別接橋式傳感器的:激勵電壓正、負,輸出電壓正、負
(E+ 接紅線;E- 接黑線;A+ 接綠或藍線;A- 接白線)。
4. B+ 和 B- 接通道B的傳感器,也可以通過分壓電路接電源,用來檢測電源電壓。不用的話最好接GND,不過我試驗不接也沒問題。
在 Arduino 中打開示例代碼,可以看到非常簡單:- #include <HX711.h> // 包含庫的頭文件
- HX711 hx(9, 10); // 數據接腳定義
- void setup() {
- Serial.begin(9600);
- }
- void loop()
- {
- double sum = 0; // 為了減小誤差,一次取出10個值后求平均值。
- for (int i = 0; i < 10; i++) // 循環的越多精度越高,當然耗費的時間也越多
- sum += hx.read(); // 累加
- Serial.println(sum/10); // 求平均值進行均差
- }
復制代碼 給的示例程序非常簡單,但是我查看庫中含有很多示例沒有給出的函數:- HX711(byte sck, byte dout, byte amp = 128, double co = 1); // 定義 sck、dout 接腳,增益倍數(默認128)和修正系數(默認1)
- void set_amp(byte amp); // 改變增益倍數和對應的通道,至少調用一次 read() 后起作用
- bool is_ready(); // 返回 hx711 是否可用,在 read() 函數中會被調用
- long read(); // 返回傳感器電壓值,如果 hx711 不可用則程序會暫停在此函數
- double bias_read(); // 返回:(read() - 偏移值) * 修正系數
- void tare(int t = 10); // 將皮重添加到偏移值,影響每次 read(); 的調用
- void set_co(double co = 1); // 修改修正系數(默認為1)
- void set_offset(long offset = 0); // 修改偏移值(默認為0)
復制代碼 可以看到,HX711還可以使用四參數方式定義,同時指定增益倍數及修正系數。在程序運行中還可以隨時改變增益倍數,修正系數以及利用偏移值實現去皮重等功能,非常實用。
這里唯一需要解釋的是第一個函數,- HX711 hx(9, 10); // 這樣用說明只定義SCK和DOUT接腳,AMP默認使用A通道的128位增益,修正系數默認為1;
- HX711 hx(9, 10, 64); // 這樣用說明定義SCK和DOUT接腳,AMP使用A通道的64增益,修正系數默認為1;
- HX711 hx(9, 10, 32, 1.4); // 這樣用說明定義SCK和DOUT接腳,AMP使用B通道的32位增益,修正系數為1.4;
復制代碼 這
里有關通道和增益倍數的選擇,資料中已經提及過,A通道只有128和64位兩種增益倍數,對應滿載電壓為 20mV 和
40mV,B通道只有固定的32位增益倍數,滿載電壓為
80mV,使用時各個通道輸入電壓不要超過對應增益倍數的滿載電壓。當然,程序中額可以隨時切換增益倍數和通道,使用set_amp(amp)函數即可,
當然,amp 的值只能是 128、64或32。
再強調一句,如果增益倍數選擇32位增益,那么讀出的數據就是B通道的。
==============================我真的是分割線================================
下面寫一個具體應用示例:
我選擇的傳感器參數如下:
滿量程輸出電壓=激勵電壓x靈敏度1.0mV/V
例如:供電電壓是5V乘以靈敏度1.0mV/V=滿量程5mV
實際上我選用的這個模塊,當電源電壓是5V時,供給傳感器的供電電壓是4V,于是我的傳感器滿量程電壓為4mV。
這樣我就完全可以選擇增益倍數最高的A通道128位增益來得到最高的精度。
電子稱連接圖示:
155242qc8j9dbvb9bzvvjc.jpg (51.43 KB, 下載次數: 237)
下載附件
2016-1-23 00:58 上傳
圖中綠色的為HX711模塊,右下角為Arduino
UNO,吊臂上掛著的就是橋式傳感器(我連接的方式為懸吊式測拉力值),AD模塊與傳感器間的電線越短越好,過長的話會受到各種干擾,AD模塊與
Arduino之間的連線最好也不要超過30cm,如果必須加長的話,可以考慮使用帶電磁屏蔽的線以及信號放大器。
首先使用庫自帶的例子測試一下,可以看到懸掛上1kg砝碼以及托盤等部件后得到的值為:- 1315588.75
- 1315597.75
- 1315607.37
- 1315606.75
- 1315604.75
- 1315589.62
- 1315579.62
- 1315594.25
- 1315588.75
- 1315580.25
- 1315589.00
- 1315584.25
- 1315605.50
- 1315596.12
- 1315592.25
- 1315607.75
- 1315585.75
- 1315582.87
- 1315568.75
- 1315574.75
- 1315578.00
- 1315583.12
- 1315604.62
- 1315573.50
- 1315584.25
復制代碼 去掉一個500g砝碼后值為:- 742865.50
- 742847.87
- 742869.00
- 742879.12
- 742873.68
- 742858.81
- 742867.81
- 742843.37
- 742862.18
- 742844.87
- 742856.50
- 742834.31
- 742831.31
- 742825.12
- 742842.31
- 742821.31
- 742816.12
- 742846.00
- 742844.00
- 742826.87
- 742818.12
- 742812.87
- 742807.18
- 742835.00
復制代碼 可以粗略的計算:
1315500 - 742800 = 572700
所以修正系數大概為: 500 / 572700 = 0.00087305
那么程序就可以寫成(這時可以適當的減小些讀取速度,增加個delay,比如讓程序半分鐘一讀取):
- #include <HX711.h>
- HX711 hx(9, 10, 128, 0.00087305);
- void setup() {
- Serial.begin(9600);
- }
- void loop() {
- delay(500);
- double sum = 0;
- for (int i = 0; i < 10; i++)
- sum += hx.read();
- Serial.println(sum/10);
- }
復制代碼 編譯寫入Arduino后,將砝碼全部取下,僅保留稱體,得到結果是:- 169640.70
- 169632.59
- 169632.90
- 169640.90
- 169632.79
- 169623.59
- 169649.79
- 169610.40
- 169615.20
- 169659.40
- 169619.90
- 169624.79
- 169614.79
- 169624.29
- 169617.79
- 169629.20
- 169604.00
- 169617.50
- 169628.20
- 169589.40
- 169602.29
- 169598.40
- 169596.40
- 169604.00
- 169612.40
- 169592.20
復制代碼 說明偏移值近似為 169600,那么我們在setup中調用偏移值的函數(使用bias_read()讀取帶修正系數及偏移值的讀數,與read()對比作參考):- #include <HX711.h>
- HX711 hx(9, 10, 128, 0.00087305);
- void setup() {
- Serial.begin(9600);
- hx.set_offset(169600);
- }
- void loop() {
- delay(500);
- double sum0 = 0;
- double sum1 = 0;
- for (int i = 0; i < 10; i++) {
- sum0 += hx.read();
- sum1 += hx.bias_read();
- }
- Serial.print(sum0/10);
- Serial.print(" ");
- Serial.println(sum1/10);
- }
復制代碼 這時讀出的數據變為:
- 169615.20 0.01
- 169616.70 0.02
- 169613.40 0.02
- 169636.50 0.03
- 169620.70 0.02
- 169638.09 0.04
- 169625.29 0.02
- 169620.50 0.02
- 169612.29 0.01
- 169609.79 0.01
- 169624.40 0.02
- 169625.20 0.02
- 169630.00 0.03
- 169628.29 0.03
- 169606.29 0.00
- 169617.79 0.02
- 169637.90 0.03
- 169603.70 0.00
- 169605.29 0.01
- 169636.50 0.03
復制代碼 然后再放上500g砝碼查看一下數據:
- 742492.68 500.16
- 742499.81 500.17
- 742503.81 500.17
- 742505.18 500.18
- 742507.18 500.17
- 742499.31 500.17
- 742499.50 500.17
- 742512.37 500.18
- 742521.87 500.19
- 742497.87 500.17
- 742518.12 500.18
- 742518.81 500.18
- 742520.87 500.19
- 742520.68 500.19
- 742530.18 500.20
- 742532.87 500.20
- 742543.00 500.20
- 742540.12 500.21
- 742517.00 500.19
復制代碼 再放上一個500g砝碼試試:- 1315286.75 1000.25
- 1315304.62 1000.25
- 1315296.62 1000.25
- 1315302.25 1000.26
- 1315302.12 1000.26
- 1315294.25 1000.25
- 1315310.00 1000.26
- 1315277.37 1000.24
- 1315283.75 1000.24
- 1315280.25 1000.23
- 1315286.75 1000.24
- 1315293.25 1000.25
- 1315309.87 1000.26
- 1315299.50 1000.25
- 1315307.12 1000.26
- 1315304.50 1000.25
- 1315301.50 1000.25
- 1315296.75 1000.25
- 1315284.50 1000.23
- 1315284.37 1000.24
- 1315308.75 1000.26
- 1315291.62 1000.25
- 1315312.75 1000.26
- 1315312.25 1000.26
復制代碼 可以看到第一位小數發生了大概0.2的變化,這說明我們粗略取的修正系數還不夠精確,但是完全符合1g精度的要求。
接下來就是完善程序,增加去皮重的功能了,在Arduino上接一個按鈕,為了阻止電磁干擾發生誤判斷,我采用了常輸出高電平的按鈕,當按下按鈕的時候輸出低電平,按鈕接在 4 號口上:- #include <HX711.h>
- HX711 hx(9, 10, 128, 0.00087305);
- void setup() {
- Serial.begin(9600);
- hx.set_offset(169600);
- }
- void loop() {
- if(digitalRead(4) == LOW) hx.tare();
- double sum0 = 0;
- double sum1 = 0;
- for (int i = 0; i < 10; i++) {
- sum0 += hx.read();
- sum1 += hx.bias_read();
- }
- Serial.print(sum0/10);
- Serial.print(" ");
- Serial.println(sum1/10);
- }
復制代碼 這樣每次按下按鈕的時候就可以去皮重了。
============================不要懷疑我是分割線=============================
去皮重也實現了,可是最求完美的我發現每次掉電后都要重新設置皮重,很是麻煩,于是決定將皮重信息存到EEPROM中保存,這樣每次上電后就會自動讀取存儲的皮重信息,從0點開始稱量了!
看了一下 HX711 的庫,在去皮重的時候只需調用 hx.tare();,tare() 函數內容如下:- void HX711::tare(int t) {
- double sum = 0;
- for (int i = 0; i < t; i++) {
- sum += read();
- }
- set_offset(sum / t);
- }
復制代碼 里面又調用了 read() 和 set_offset(),read() 不用看了,就是讀取一次不帶修正系數和偏移量的傳感器數據。set_offset() 如下:- void HX711::set_offset(long offset) {
- OFFSET = offset;
- }
復制代碼 僅僅是把偏移量賦值給OFFSET,而OFFSET會在 bias_read() 中起作用:- double HX711::bias_read() {
- return (read() - OFFSET) * COEFFICIENT;
- }
復制代碼 也就是說,我們在調用 tare() 的時候如果能返回 OFFSET 值就可以存儲它用作去皮重了。
不幸的是,HX711庫并不允許我們這么做。也許有人要說了,那就調用 tare() 后 再調用一次 read() 來獲取
OFFSET唄。這樣可不行,首先調用 tare() 的時候本身就會調用 10 次 read() 并求平均值作為 OFFSET
來使用,我們再調用一次 read() 讀出的數既不是之前那個平均值,精度也不如前面的平均值高,就算再求一次 10個 數的平均值,也得不到之前那個
OFFSET了,精度會大打折扣的。
看來唯一的辦法就是自己手動改一下 HX711 的庫,讓它在調用 tare() 的時候直接返回個
OFFSET,這樣就解決了我們的需求。為了和官方庫區分,我們改一下庫的名字,將 HX711 庫文件夾復制一份改名為 HX711A ,然后將
HX711.cpp 改為 HX711A.cpp,同樣 HX711.h 改為 HX711A.h。然后打開 HX711A.cpp 和 HX711A.h,作出修改。改動如下:
HX711A.cpp:- #include <HX711.h> -> #include <HX711A.h>
- void HX711::tare(int t) { - > double HX711::tare(int t) {
- 并在 tare 函數結尾加入: return sum / t;
復制代碼 HX711A.h:
- #ifndef HX711_H -> #ifndef HX711A_H
- #define HX711_H -> #define HX711A_H
- void tare(int t = 10); -> double tare(int t = 10);
復制代碼 這樣在調用 tare() 的時候就可以返回 OFFSET 值了,我們存儲 OFFSET值就可以了。
可是新的問題又出現了,OFFSET 值是 double 類型的,而我們 Arduino 提供的 EEPROM 庫一次僅能存儲1個 char
類型數據。于是上網查資料,發現弘版有個帖子提到了多類型存儲,可是相應的庫編譯后有點龐大,還是自己解決吧,寫了個共用體實現的double類型存儲,
見我的帖子,可以在程序里加入這一部分內容。
----帖子內容引用------------------------ 有的時候我們需要將float或者double類型的數據存到EEPROM中以備下次利用,比如制作電子稱存儲皮重等信息時。
但是Arduino的EEPROM僅僅能以8bit一位存儲1024位。
在網上查了一下,貌似使用共用體的方法很火,于是寫了個代碼:
數據拆分存儲部分:- dvalue.v = 169600.00;
- unsigned char *dpointer;
- dpointer = dvalue.dchar;
- for(int i = 0; i < 8; i++) {
- EEPROM.write(i,*dpointer);
- dpointer++;
- }
復制代碼 數據讀取部分:- for(int i = 0; i < 8; i++) dvalue.dchar[ i] = EEPROM.read(i);
- Serial.println(dvalue.v);
復制代碼 完整示例代碼:- #include <EEPROM.h>
- union data {
- double v;
- unsigned char dchar[8];
- } dvalue;
- void setup() {
- Serial.begin(9600);
-
- //數據拆分
- dvalue.v = 169600.00;
- unsigned char *dpointer;
- dpointer = dvalue.dchar;
- for(int i = 0; i < 8; i++) {
- EEPROM.write(i,*dpointer);
- dpointer++;
- }
- }
- void loop()
- {
- //數據還原
- for(int i = 0; i < 8; i++) dvalue.dchar[ i] = EEPROM.read(i);
- Serial.println(dvalue.v);
- delay(1000);
- }
復制代碼 當僅僅要存取float類型時,只需要把v前面的double改為float,把循環和聲明dchar中的8改為4即可。
EEPROM庫中貌似還有個put方法可以存各種數據類型,感謝18# 蔥拌豆腐 提供
http://www.arduino.cc/en/Reference/EEPROM ----------------------------
另外,經過查詢資料,橋式傳感器受溫度影響的偏移量也不容忽視,可以在系統中加入溫度傳感器(例如DS18B20),并在計算重量的時候加入線性溫度漂移修正,這里我就不寫溫度相關代碼了,僅給出溫度漂移修正的函數供大家參考:- #include <HX711A.h>
- #include <EEPROM.h>
- HX711 hx(9, 10, 128, 0.00087305);
- int i=0;
- // 用于在 EEPROM 中儲存 double 類型數據的共用體
- unsigned char *dpointer;
- union data {
- double v;
- unsigned char dchar[8];
- } dvalue;
- void setup() {
- Serial.begin(9600);
- for(i = 0; i < 8; i++) dvalue.dchar[ i] = EEPROM.read(i); // 從 EEPROM 讀取偏移量
- hx.set_offset(dvalue.v); // 設置已讀取的偏移量
- }
- void loop() {
- if(digitalRead(4) == LOW) {
- dvalue.v = hx.tare(); // 去皮重并讀取偏移量
- hx.set_offset(dvalue.v); // 設置已讀取的偏移量
- dpointer = dvalue.dchar; // 以下程序將偏移量分解并儲存到 EEPROM
- for(i = 0; i < 8; i++) {
- EEPROM.write(i,*dpointer);
- dpointer++;
- }
- }
- double sum = 0;
- for (i = 0; i < 10; i++) {
- sum += hx.bias_read();
- }
- Serial.println(sum/10);
- //Serial.println(sum / 10 * (1 + (20 - temprature) / 10 * 0.02)); // 帶線性溫度補償的輸出,注意temprature單位為攝氏度。
- }
復制代碼 ============================我分割線又回來啦=============================
此外,程序可以增加的功能還有:
1.LCD顯示
2.鍵盤輸入及語音功能
3.標準砝碼校準功能(如500g),其實就是修正系數的自我修正功能。
這幾個功能我就不再研究了,都不難,留給各位自己發揮的空間吧
全文完 |