Arduino I2C + 溫濕度傳感器AM2321


(2015.5.17:本日志的內容有所更新,參見《使用Arduino Wire Library讀取溫濕度傳感器AM2321》。)

AM2321是廣州奧松電子生產的數字式溫濕度傳感器。雖是國產品牌,其精度也可以與國外的主流溫濕度傳感IC媲美。

  • 尺寸:11.3x7.8x4mm(長x寬x高)
  • 封裝:0.05 pitch PTH
  • 工作電壓:2.6~5V
  • 功耗:測量時0.5mA,休眠狀態10μA
  • 接口:I2C,最大速率100kbps;或單總線通訊
  • 分辨率:溫度0.1°C,相對濕度0.1%RH
  • 精度:出廠前已校正,室溫時溫度誤差+/-0.3°C,相對濕度誤差+/-3%RH(皆典型值)
  • 重復性:溫度+/-0.2°C,相對濕度+/-0.1%RH

美中不足:與國外同精度產品相比,AM2321的重復性和漂移指標偏大;功耗偏高;只能手動焊接,給產品量產帶來不便。

電路連接

AM2321支持5V工作,將電源、地、SCL、SDA四個管腳直接與UNO板子的對應管腳相連。對於Arduino UNO,I2C總線的SDA信號線對應A4管腳,SCL時鍾線對應A5管腳。之后,SCL、SDA線需要通過上拉電阻連接到5V電源,電阻值可取4.7k或10k。

 

功能調試

第一次調試花了不少時間,最終借助示波器才搞定。需留意的幾個問題:

1. I2C地址問題。雖然手冊里寫的地址是0xB8(0b10111000),但實際上器件是采用的7位地址,應該表述成0x5C(0b1011100),代碼中的地址也應寫成0x5C,否則無法通信。

2. 喚醒AM2321時的時序問題。器件不回ACK,且最后一個時鍾下降沿到發stop信號需間隔0.8~3ms。這個時序條件在Arduino的Wire庫中沒有處理的函數,因此只能將A4、A5設置成GPIO,利用bit-banging實現。shiftOut()函數可以實現字節的串行輸出,且速率剛好也是100kbps左右。[注:后面發現即使沒有這個等待時間,傳感器也能正常工作,詭異。]

3. A4、A5管腳在GPIO和硬件I2C之間的功能切換問題。在調用Wire.begin()函數之后,再使用pinMode()或digitalWrite()函數就無效了。發現在Wire.begin()函數中設置了I2C的控制寄存器TWCR,需將TWCR恢復到調用Wire.begin()前的狀態,才可以用GPIO的方式操作A4、A5。

4. 讀返回數據時的時序問題。手冊要求發送地址后,需要等待至少30μs后才能讀取數據。這個功能在Wire庫里也不支持,但直接用庫里的函數(間隔約10μs)讀取,沒有發現有通信錯誤的問題。

5. 傳感器發送數據之后,會觸發下一次溫濕度測量,測量結果供下次數據讀取。因此連續讀取兩次才能獲得當前的溫濕度值,即:第一次讀取的是上一次測量的值,第二次讀取的才是當前測量值。兩次讀取的最小間隔為2秒。

測試代碼

  1 /*
  2 Measurement of temperature and humidity using the AM2321 sensor
  3 Attention: 
  4     The protocol AM2321 used is not a standard i2c.
  5     Bit-banging is used to wake up the sensor, 
  6     and then i2c functions are used to communicate.
  7 Connection:
  8 AM2321           UNO
  9 VDD <--------->  5V
 10 GND <--------->  GND
 11 SCL <---------> SCL(A5)
 12 SDA <---------> SDA(A4)
 13 */
 14 
 15 #include <Wire.h>
 16 
 17 #define ADDRESS_AM2321 0x5C //not 0xB8
 18 #define SIGN_WRITE 0x00
 19 #define SDA_PIN A4
 20 #define SCL_PIN A5
 21 
 22 byte fuctionCode = 0;
 23 byte dataLength = 0;
 24 byte humiHigh = 0;
 25 byte humiLow = 0;
 26 byte tempHigh = 0;
 27 byte tempLow = 0;
 28 byte crcHigh = 0;
 29 byte crcLow = 0;
 30 
 31 int humidity = 0;
 32 int temperature = 0;
 33 unsigned int crcCode = 0;
 34 
 35 byte backupTWCR = 0;
 36 
 37 void setup()
 38 {
 39     Serial.begin(115200);
 40 }
 41 
 42 void loop()
 43 {
 44     //step 1. wake up the sensor
 45     SendWakeUp(); 
 46     backupTWCR = TWCR;
 47 
 48     //step 2. send command 
 49     Wire.begin();
 50     Wire.beginTransmission(ADDRESS_AM2321);
 51     Wire.write(0x03);
 52     Wire.write(0x00);
 53     Wire.write(0x04);
 54     Wire.endTransmission();
 55 
 56     delayMicroseconds(1500);
 57 
 58     //step 3. read data, and recover the TWCR register
 59     Wire.requestFrom(ADDRESS_AM2321, 8);
 60     fuctionCode = Wire.read();
 61     dataLength = Wire.read();
 62     humiHigh = Wire.read();
 63     humiLow = Wire.read();
 64     tempHigh = Wire.read();
 65     tempLow = Wire.read();
 66     crcLow = Wire.read();
 67     crcHigh = Wire.read();
 68 
 69     //get the result
 70     humidity = (humiHigh<<8) | humiLow;
 71     temperature = (tempHigh<<8) | tempLow;
 72     crcCode = (crcHigh<<8) | crcLow;
 73 
 74     Serial.print(temperature/10.0, 1);    Serial.println(" `C");
 75     Serial.print(humidity/10.0, 1);    Serial.println(" \%RH");
 76     CheckCRC();
 77 
 78     //recover the TWCR register, e.g. disable the I2C bus
 79     TWCR = backupTWCR; 
 80 
 81     delay(4000);
 82 }
 83 
 84 void SendWakeUp()
 85 {
 86     //set pinmode
 87     pinMode(SCL_PIN, OUTPUT);
 88     pinMode(SDA_PIN, OUTPUT);
 89     digitalWrite(SCL_PIN, HIGH);
 90     digitalWrite(SDA_PIN, HIGH);
 91 
 92     //issue a START condition
 93     delayMicroseconds(5);
 94     digitalWrite(SDA_PIN, LOW); 
 95     delayMicroseconds(5);
 96     digitalWrite(SCL_PIN, LOW); 
 97     delayMicroseconds(5);
 98 
 99     //send ADDRESS+W
100     shiftOut(SDA_PIN, SCL_PIN, MSBFIRST, ((ADDRESS_AM2321<<1) | SIGN_WRITE));
101 
102     //send clock for ack
103     pinMode(SDA_PIN, INPUT_PULLUP);// or INPUT mode
104     delayMicroseconds(5);
105     digitalWrite(SCL_PIN, HIGH);
106     delayMicroseconds(5);
107     digitalWrite(SCL_PIN, LOW);
108     pinMode(SDA_PIN, OUTPUT);
109     digitalWrite(SDA_PIN, LOW);
110     
111     delayMicroseconds(1000);
112 
113     //issue a STOP condition
114     digitalWrite(SCL_PIN, HIGH);
115     delayMicroseconds(5);
116     digitalWrite(SDA_PIN, HIGH);
117 }
118 
119 void CheckCRC() //from the datesheet
120 {
121     byte backValues[] = {fuctionCode, dataLength, humiHigh, \
122         humiLow, tempHigh, tempLow};
123     unsigned int crc = 0xFFFF;
124     int i;
125     int len = 6;
126     int j = 0;
127     while (len--)
128     {
129         crc ^= backValues[j];
130         j++;
131         for (i=0; i<8; i++)
132         {
133             if (crc & 0x01)
134             {
135                 crc >>= 1;
136                 crc ^= 0xA001;
137             }
138             else
139             {
140                 crc >>= 1;
141             }
142         }
143     } 
144     if (crc == crcCode)
145     {
146         Serial.println("CRC checked.");
147     }
148     else
149     {
150         Serial.println("CRC Error!");
151     }
152 }
View Code

 [注] 后面發現,第一步喚醒時即使直接用Wire庫中的beginTransmission()和endTransmission()函數即可,即使沒有手冊中要求的0.8~3ms等待時間,傳感器也能正常運行。從示波器上看,傳感器不回ACK,硬件I2C等待的時間僅10us左右,卻不影響工作。看來手冊的描述有問題。使用Wire Library標准庫來讀取AM2321,可以參照另一篇日志

參考資料

奧松官網信息 - AM2321數字溫濕度傳感器
TI - Troubleshooting I2C Bus Protocol 關於I2C調試問題處理的文檔,推薦
拆解國產奧松微小型濕度傳感器AM2321
wangdong/AM2321 - GitHub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM