第1部分 SHT31傳感器介紹
1.1芯片簡介
SHT3X系列是由瑞士Sensirion生產的高精度溫溼度傳感器,也是Sensirion公司目前主打的溫溼度傳感器系列。現在網上常見的相關資料調試的基本上以SHT30爲主,SHT31則較少。本次開發用的是更高級的SHT35系列。
這次採用的SHT35傳感器允許寬電壓輸入,支持2.15V~5.5V。採用IIC總線通信,最高可達1MHz的通信速度。並根據ADDR引腳的接法,提供兩個可選的地址:0x44,0x45。傳感器的精度爲1.5%RH和0.1℃。傳感器最大工作範圍-40-125℃,0-100%RH。原裝芯片有8個引腳。
還有一點要注意的是SHT3x推薦的的最佳工作環境是5℃-60℃,20%RH-80%RH。當傳感器暴露在>80%RH的工作環境下超過60小時後,會出現+3%RH的偏差。
SHT3x系列提供數字接口和模擬接口兩種規格。
相關資料在文末給出。
1.2 引腳介紹
芯片總共有8個引腳
(1)SDA :IIC數據線引腳
(2)ADDR :地址引腳,可連接VSS或VDD,分別會有不同的地址。不能浮空。
(3)ALERT :報警引腳,如果使用,建議接到單片機的外部中斷。不用的話建議浮空。
(4)SCL :IIC時鐘線引腳
(5)VDD :電壓輸入引腳
(6)nRESET :復位引腳,低電平有效。如果不用,建議接到VDD。
(7)R :沒有電氣作用的“沒卵用“”引腳,連接到VSS
(8) VSS :接地
(附上官方推薦的典型應用電路)
1.3命令和模式介紹
首先看一下地址
很明顯,當傳感器ADDR引腳接VSS時,採用地址A;當傳感器ADDR引腳接VDD時,採用地址B。本次開發的傳感器 Address=0x44.
傳感器支持單次數據採集模式和週期性數據採集模式。其實單次數據採集模式下,可選時鐘延伸,而週期性數據採集默認開始數據延伸。這裏我們默認採用週期性數據採集模式。
SHT3X支持12種工作模式,分別有高,中,低三檔可選刷新率。mps=0.5,1,2…時,分別代表每兩秒採集一次數據,每秒採集一次數據,每秒採集兩次數據…
如希望設定高刷新率,每秒採集一次。那麼向傳感器寫命令0x2130即可。注意當採用mps=10時,會導致傳感器自發熱,影響測量。
工作順序爲:先發送IIC通信開始標誌Start後,寫入左移一位的地址,並將空出來的位寫0表示寫數據。當收到傳感器應答後,即可發送命令的高八位,再次等待應答,再發送餘下的低八位。然後等待ACK應答即可。
其他命令同理,大部分都是同樣的寫入模式。
1.4 重要命令及其工作流程
那麼我們看幾條重要的命令及其工作流程。
設置好工作模式後寫入此命令,可以準備好接受數據。先發送IIC通信開始標誌Start後,寫入左移一位的地址,並將空出來的位寫1表示讀數據。然後等待ACK應答即可接受數據。注意數據傳輸順序是先溫度後溼度。並且都是十六位數據。並且每個數據後都附8位的CRC校驗。在完成溼度的CRC校驗後,即可回覆NACK,傳感器將停止發送數據,釋放SDA線,以便於MCU發送Stop標誌,結束通信。
第2部分 SHT3x官方參考代碼簡介
2.1.官方給的參考code是基於STM32平臺的。文末有資料獲取方式。
由於Nordic藍牙芯片的I2C接口與STM32有稍微不同。本文將基於該參考code,通過修改,移植到nRF52832藍牙芯片上。
2.2 參考I2C 代碼詳解
本文檔包含C語言的示例代碼,用於通過與SHT3x溼度和溫度傳感器通信
I2C接口。代碼的目的是在實現SHT3x傳感器時簡化用戶的軟件編程。除了
簡單的測量溼度和溫度,代碼包含計算CRC校驗和和計算物理溼度和溫度值。這個示例代碼是爲STM32-Discovery板編寫和優化的,但它可以很容易地應用於其他微控制器而做稍微改變。
至於更詳細的官方參考code和說明,不是本文的重點,系列資料在文末有獲取方式。
第3部分 Nordic藍牙芯片 I2C(TWI)軟件設計介紹:
關於nRF52832藍牙芯片的I2C(TWI)串行總線的原理,之前的文章已有介紹。以下主要介紹軟件設計部分。
nRF52832 片內集成的 TWI(兩線串行總線)兼容 I2C 總線,帶有 EasyDMA,可與連接到同一總線的多個從機設備通訊,主要特點如下:
(1)兼容 I2C。
(2)速率:100 kbps、250 kbps 或 400 kbps。
(3)支持時鐘延伸。
(4)帶 EasyDMA。
(5)TWI 的 SCL 和 SDA 信號可以通過配置寄存器連接到任何一個GPIO,這樣可以靈活地實現器件引腳排列,並有效利用電路板空間和信號路由。
nRF52832 的 TWI 的原理框圖如下圖所示,TWI 主機通過觸發STARTTX 或STARTRX 任務啓動TWI 傳輸,通過觸發 STOP 任務停止 TWI 傳輸。TWI 主機在掛起時無法停止,因此必須在 TWI 主機恢復後觸發 STOP 任務停止 TWI。啓動 TWI 主機後,在TWI 主機停止之前,即在 LASTRX,LASTTX 或 STOPPED 事件之後,不應再次觸發 STARTTX 任務或STARTRX 任務。如果從機產生 NACK 輸入,那麼 TWI 主機將產生ERROR 事件。
3.1.nRF的I2C 函數庫應用
TWI 的應用步驟如下圖所示,首先要定義 TWI 驅動程序實例,驅動程序實例對應具體的硬件 TWI 外設(TWI0 和 TWI1),驅動程序實例決定了我們使用的是 TWI0 還是 TWI1。接着初始化配置TWI 連接的引腳和速率等參數,註冊事件句柄(非堵塞模式下),初始化後使能TWI,之後就可以使用 TWI 進行傳輸數據。
3.2定義TWI 驅動程序實例
TWI 驅動程序實例使用 nrf_drv_twi_t 結構體定義,該結構體描述了具體的 TWI 外設, 當我們定義了nrf_drv_twi_t 類型的變量並對其賦值後,該變量就對應了一個具體的硬件 TWI 外設。
定義驅動程序實例代碼如下,初始化宏 NRF_DRV_TWI_INSTANCE 的輸入參數對應TWI 外設的編號,即如果我們定義 TWI0 的驅動程序實例,TWI_INSTANCE_ID 的值設置爲 0,定義 TWI1 的驅動程序實例,TWI_INSTANCE_ID 的值設置爲 1。驅動程序實例定義後,我們即可通過該驅動程序實例訪問對應的 TWI。
3.3 初始化TWI(I2C)
TWI 初始化的庫函數是 nrf_drv_twi_init ()函數,該函數同時也配置了 TWI 是否使用阻塞模式。
(1)應用程序提供事件句柄:TWI 工作於非阻塞模式。
(2)應用程序不提供事件句柄(event_handler 設置爲 NULL):TWI 工作於阻塞模式。建議使用阻塞模式。
/** TWI初始化
* @brief TWI initialization.
*/
//I2C引腳
#define TWI_SCL_M 26 //I2C SCL
#define TWI_SDA_M 25 //I2C SDA
/* TWI instance. */
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
void twi_init (void)
{
ret_code_t err_code;
const nrf_drv_twi_config_t twi_config = {
.scl = TWI_SCL_M,
.sda = TWI_SDA_M,
.frequency = NRF_DRV_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};
err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi);
NRF_LOG_INFO("twi_master_init ok.");
NRF_LOG_FLUSH();
nrf_gpio_pin_clear(20); //ledµÆÁÁ
}
3.5 TWI數據傳輸函數
TWI 驅動程序提供了兩個單獨的函數nrf_drv_twi_tx() 函數 和 nrf_drv_twi_ rx() 函數 , 分別用於完成數據的發送和接收,函原型如下表所示。使tx 和 rx 函數時,尤其要注意從機地址 address,函數接收的是 7位地址,函數內部會自己加上讀寫位。因此如果某個 I2C 接口設備提供的是 8位地址,我們需要提取出 7位地址賦值給address,也就是去掉 8位地址的最低(R/W位)。
nRF52832藍牙芯片的所有I2C 通信都是通過這兩個函數接口。不需要自己去重寫I2C驅動函數。
3.5.1 TWI發送函數模型:nrf_drv_twi_tx()
函數原型 |
STATIC_INLINE ret_code_t nrf_drv_twi_tx (nrf_drv_twi_t const * p_instance, uint8_t address, uint8_t const * p_data, uint8_t length, bool no_stop) |
函數功能 |
向 TWI 從機發送數據,發生錯誤時將停止傳輸。 如果傳輸正在進行,則該函數返回錯誤代碼 NRF_ERROR_BUSY。 |
參 數 |
[in] p_instance:指向 TWI 驅動程序實例結構體。 [in] address:指定的從機地址(7 位 LSB)。 [in] p_data:指向傳輸數據緩存。 [in] length:發送的字節數。 [in] no_stop:如果置位,成功傳輸後總線上不會生成停止條件(允許在 下一次傳輸中重複啓動)。 |
返回值 |
NRF_SUCCESS:發送成功。 NRF_ERROR_BUSY:驅動程序尚未準備好進行新的傳輸。NRF_ERROR_INTERNAL:硬件檢測到錯誤。NRF_ERROR_INVALID_ADDR:使用了 EasyDMA,但是緩存地址沒有位於RAM 空間。 NRF_ERROR_DRV_TWI_ERR_ANACK:在輪詢模式下發送地址後收到NAC。 NRF_ERROR_DRV_TWI_ERR_DNACK:在輪詢模式下發送數據後收到 NACK。 |
3.5.2 TWI接收函數模型:nrf_drv_twi_rx()
|
返回值 |
[in] address:指定的從機地址(7 位 LSB)。 [in] p_data:指向接收數據緩存。 [in] length:讀取的字節數。 |
返回值 |
NRF_SUCCESS:發送成功。 NRF_ERROR_BUSY:驅動程序尚未準備好進行新的傳輸。NRF_ERROR_INTERNAL:硬件檢測到錯誤。NRF_ERROR_DRV_TWI_ERR_OVERRUN:接收數據未及時讀取,被新接收的數據覆蓋。 NRF_ERROR_DRV_TWI_ERR_ANACK:在輪詢模式下發送地址後收到NAC。 NRF_ERROR_DRV_TWI_ERR_DNACK:在輪詢模式下發送數據後收到 NACK。 |
3.5 I2C 設備掃描
爲什麼要掃描設備?通過掃描I2C 設備,可以確認設備是否正常工作,以及設備接入是否正常等,還可以通過打印信息,確認設備的 I2C 地址。
/* Number of possible TWI addresses. */
#define TWI_ADDRESSES 127
void iic_scan_address(void)
{
ret_code_t err_code;
uint8_t address;
uint8_t sample_data;
bool detected_device = false;
NRF_LOG_INFO("TWI scanner started.");
NRF_LOG_FLUSH();
twi_init();//twi init
for (address = 1; address <= TWI_ADDRESSES; address++)
{
err_code = nrf_drv_twi_rx(&m_twi, address, &sample_data, sizeof(sample_data));
if (err_code == NRF_SUCCESS)
{
detected_device = true;
NRF_LOG_INFO("TWI-i2c device detected at address 0x%x.", address);
}
NRF_LOG_FLUSH();
}
if (!detected_device)
{
NRF_LOG_INFO("No device was found.");
//NRF_LOG_FLUSH();
}
NRF_LOG_INFO("TWI device scan ended.");
NRF_LOG_FLUSH();
}
I2C設備掃描的Log信息:
由於我的I2C 總線上掛了兩個設備:
0x3C爲 0.96寸oled屏幕的I2C地址,
0x44爲,SHT35溫溼度傳感器的I2C地址。
第4部分 SHT3x代碼移植到Nordic藍牙芯片:
由於Nordic藍牙芯片的I2C接口與STM32有稍微不同。本文將基於官方給的STM32代碼,通過修改,移植到nRF52832藍牙芯片上。
通過掃描,我知道我的SHT35溫溼度傳感器的I2C地址爲0x44。當然也可以通過手冊得知。掃描只是爲了確認沒錯。
4.1 寫SHT35寄存器函數
這個函數非常重要,由於SHT35的命令都是16位,而nrf_drv_twi_tx()函數中,數據定義的是8位。所以關鍵點就在code中。
static uint8_t tx_buf[2];
tx_buf[0] = (uint8_t)(cmd>>8); //低8位放在buf0
tx_buf[1] = (uint8_t)(cmd & 0xFF); //高8位放在buf1
static etError SHT3x_WriteCommand(uint16_t cmd) { ret_code_t err_code; static uint8_t tx_buf[2]; tx_buf[0] = (uint8_t)(cmd>>8); //低8位 tx_buf[1] = (uint8_t)(cmd & 0xFF); //高8位 //TWI傳輸完成標誌設置爲false //m_xfer_done = false; uint8_t retry_num = 20; do{ err_code = nrf_drv_twi_tx(&m_twi, SHT35_ADDRESS, tx_buf, 2, false); APP_ERROR_CHECK(err_code); retry_num--; //等待TWI總線應答 //UNUSED_VARIABLE(err_code); } while((NRF_SUCCESS != err_code) && (0 < retry_num));
//返回寫入成功 return NO_ERROR; } |
4.2 週期測量模式
//發送命令,週期測量模式,測量頻率1Hz void SHT3X_SetPeriodicMeasurement(void) { SHT3x_WriteCommand(CMD_MEAS_PERI_2_H); NRF_LOG_INFO("cmd_meas_peri_2_h"); NRF_LOG_FLUSH(); } |
4.3 讀取溫溼度數據
//讀取溫溼度數據,函數中會校驗數據 etError SHX3X_ReadMeasurementBuffer(float* temperature, float* humidity) { etError error; ret_code_t err_code; static uint8_t bytes[6]; //寫入命令 SHT3x_WriteCommand(CMD_FETCH_DATA); //0xE000,readout measurements for periodic mode //讀出溫溼度數據 //m_xfer_done = false; //嘗試20次 uint8_t retry_num = 20; do{ err_code = nrf_drv_twi_rx(&m_twi, SHT35_ADDRESS, bytes, 6); APP_ERROR_CHECK(err_code); retry_num--; //等待TWI總線應答 //UNUSED_VARIABLE(err_code); } while((NRF_SUCCESS != err_code) && (0 < retry_num)); //while (m_xfer_done == false){}; //校驗溫溼度數據 error = SHT3X_CheckCrc(bytes, 2, bytes[2]); if(error == NO_ERROR) { *temperature = SHT3X_CalcTemperature((bytes[0] << 8) | bytes[1]); *humidity = SHT3X_CalcHumidity((bytes[3] << 8) | bytes[4]); } return NO_ERROR; } |
4.4 CRC校驗
//------------------------------CRC校驗和驗證------------------------ // Generator polynomial for CRC #define POLYNOMIAL 0x131 // P(x) = x^8 + x^5 + x^4 + 1 = 100110001
//CRC校驗 static uint8_t SHT3X_CalcCrc(uint8_t data[], uint8_t nbrOfBytes) { uint8_t bit; // bit mask uint8_t crc = 0xFF; // calculated checksum uint8_t byteCtr; // byte counter //用給定的多項式計算8位校驗和 for(byteCtr = 0; byteCtr < nbrOfBytes; byteCtr++) { crc ^= (data[byteCtr]); for(bit = 8; bit > 0; --bit) { if(crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL; else crc = (crc << 1); } }
return crc; }
//檢驗校驗是否正確
static etError SHT3X_CheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum) { uint8_t crc; // calculated checksum //計算8位校驗和 crc = SHT3X_CalcCrc(data, nbrOfBytes); //驗證校驗 if(crc != checksum) return CHECKSUM_ERROR; else return NO_ERROR; } |
4.5 溫溼度計算
//------------------------計算溫溼度--------------------------------- /** * 描述 : 計算溫度 * 入參: rawValue:讀取的溫度信息 * 返回值 : 計算出的溫度 **/ static float SHT3X_CalcTemperature(uint16_t rawValue) { //T = -45 + 175 * rawValue / (2^16-1) float temperature = 175.0f * (float)rawValue / 65535.0f - 45.0f; return temperature; } /** * 描述 : 計算溼度[%RH] * 入參: dat :讀取的溼度信息 * 返回值 : 計算出的溼度值 ***/ static float SHT3X_CalcHumidity(uint16_t rawValue) { // 計算相對溼度[%RH] // RH = rawValue / (2^16-1) * 100 float huminity = 100.0f * (float)rawValue / 65535.0f; return huminity; } |
4.6 測試溫溼度
先設置測量模式,再讀取數據。
//-----------------------溫溼度測試程序--------------------------------------- void sht35_c_test(void){
NRF_LOG_INFO("sht35_c_test start..,measure the temperature and humidity"); NRF_LOG_FLUSH();
float temperature; // ?? float humidity; // ?? [%RH]
//週期測量模式1HZ SHT3X_SetPeriodicMeasurement(); nrf_delay_ms(50); //while(true) // { SHX3X_ReadMeasurementBuffer(&temperature, &humidity); NRF_LOG_INFO("temperature:%d `C",temperature); NRF_LOG_INFO("humidity :%d RH",humidity); NRF_LOG_FLUSH(); //測量頻率設置1Hz,因此讀取間隔不能小於1s nrf_delay_ms(1200); // } } |
測試結果:
用4pin的I2C OLED屏幕顯示溫溼度:
5.結語
下一章,介紹nRF52832上點亮4pin 0.96寸I2C的OLED屏幕。
白浪介紹:
(1)關於射頻、微波、天線、無線通信、智能硬件、軟件編程、滲透安全、人工智能、區塊鏈,Java、Android、C/C++、python等綜合能力的培養提升。
(2)各種學習資料、學習軟件分享。
1.掃碼關注公衆號(Geekxiaobai)
2. 如在後臺發送“Python高級編程”“Python Graphics”或者“2003”,即可免費獲得電子書籍。僅供學習之用。
3. 掃碼關注後,查看往期內容,會有更多資料驚喜等着你來拿哦
想要更多相關學習資料,可以在公衆號留言哦。
========******=========******========******=========******==========