1- I2C協(xié)議介紹
(1)I2C協(xié)議簡介
I2C總線是Philips公司在八十年代初推出的一種串行、半雙工的總線,主要用于近距離、低速的芯片之間的通信。
12C Bus(IIC, Inter-Integrated Circuit Bus)是由一根數(shù)據(jù)線SDA用于收發(fā)數(shù)據(jù),一根時鐘線SCL用于通信雙方時鐘的同步,利用上拉電阻將它們拉成高電平(表示總線空閑),其典型的電壓準位為+3.3V或+5V,具有電路簡單、連接線少、控制簡單、通信速率高等優(yōu)點。
I2C總線是一種主從結(jié)構(gòu)(Master/Slave)總線, I2C總線上的每一個設(shè)備都可以作為主設(shè)備或者從設(shè)備,但一個總線上一般只有一個主設(shè)備,可以帶多個從設(shè)備。其中主設(shè)備用來產(chǎn)生允許傳輸?shù)臅r鐘信號,并初始化總線的數(shù)據(jù)傳輸,所以主設(shè)備通常是CPU,而從設(shè)備只能被動響應(yīng)主設(shè)備發(fā)起的通信請求,所以各種12C接口芯片將作為從設(shè)備使用。
(2)I2C從設(shè)備地址
因為一個12C總線上可以有多個從設(shè)備,這樣主設(shè)備需要通過地址來確定與哪個器件進行通信。I2C總線上每個從設(shè)備都有一個唯一的7bit地址物理識別,這個地址固化在芯片內(nèi)部,并可以從芯片datasheet上找到。因為12C地址全0為廣播地址,所以12C總線理論上最多能帶2^7-1=127個從設(shè)備。
其中I2C的從器件地址(我只用到了7位的)的組成如下:
1byte = 7bit地址 + 1bit讀寫標志
注:1bit讀寫標志中,0-發(fā)送數(shù)據(jù)(寫),1-請求數(shù)據(jù)(讀)
有些時候一個總線上可能需要掛多個同一芯片,這樣有些芯片還需要引出一個或幾個引腳,由開發(fā)板設(shè)計電路來決定其具體地址,從而讓不同芯片具有不同的7bit物理地址。如下圖:
如果ADDR連VSS,則其7bit地址為0x44
如果ADDR連VDD,則其7bit地址為0x45
2- I2C通信時序
在12C總線上傳送的每一位數(shù)據(jù)都由一個同步時鐘脈沖相對應(yīng),即在SCL串行時鐘的配合下,數(shù)據(jù)在SDA上從高位向低位依次串行傳送每一位的數(shù)據(jù)。下面是12C通信的時序圖:
(1)起始位
12C總線在空閑時SDA和SCL都處于高電平狀態(tài)(由上拉電阻拉成高電平),當主設(shè)備要開始一次12C通信時就發(fā)送一個START(S)信號,這個起始位就可以告訴所有12C從機, “我”要開始進行12C通信了;當要結(jié)束一次12C通信時,則發(fā)送一個STOP§信號結(jié)束本次通信。
START(S):當SCL保持高電平時候,SDA出現(xiàn)下降沿,產(chǎn)生一個起始位,注意SCL一定要在高電平。
STOP( P ):當SCL保持高電平時候,SDA出現(xiàn)上升沿,產(chǎn)生一個停止位,注意SCL一定要在高電平。
(2)讀寫地址
主機在發(fā)送START信號之后,第2個時序應(yīng)該立刻給出要通信的目標從機物理地址。此外,I2C總線是一種能夠?qū)崿F(xiàn)半雙工通信的同步串行通信協(xié)議,站在主設(shè)備的角度來看應(yīng)該具有讀/寫從設(shè)備的功能。
這時候12C的讀寫地址除了7bit物理地址以外,還有1bit用來標識讀/寫方向位。這樣12C的從設(shè)備讀寫地址通常是一個字節(jié),其中高7bit是上面描述的物理地址,最低位用來表示讀寫方向(0為寫操作, 1為讀操作)
(3)I2C應(yīng)答信號
主機往12C總線上傳輸器件地址,所有的從機接收到這個地址后與自己的地址相比較若相同則發(fā)出一個應(yīng)答ACK(Acknowledge)信號,主機收到這個應(yīng)答信號后通訊連接建立成功,若未收到應(yīng)答信號則表示尋址失敗。
此外,主/從機在之后的數(shù)據(jù)通信中,數(shù)據(jù)接收方(可能是主機也可能是從機)收到傳輸?shù)囊粋€字節(jié)數(shù)據(jù)后,需要給出響應(yīng),此時處在第九個時鐘,發(fā)送端釋放SDA線控制權(quán),將SDA電平拉高,由接收方控制。
- 若希望繼續(xù),則給出“應(yīng)答(ACK, Acknowledge)”信號,即SDA為低電平
- 若不希望繼續(xù),則給出“非應(yīng)答(NACK,Not Acknowledge) ”信號,即SDA為高電平
(4)數(shù)據(jù)位發(fā)送與接收
主機在收到從機的應(yīng)答信號之后,開始給從機發(fā)送數(shù)據(jù)。SDA數(shù)據(jù)線上的每個字節(jié)必須是8位,每次傳輸?shù)淖止?jié)數(shù)量沒有限制,每個字節(jié)發(fā)送完成之后,從機必須跟一個應(yīng)答信號。
12C總線通信時數(shù)據(jù)位傳輸采用MSB(最高位優(yōu)先)方式發(fā)送,其中高電平表示數(shù)據(jù)位1,低電平表示數(shù)據(jù)位0。
當傳輸?shù)臄?shù)據(jù)位需要改變時(如上一個位發(fā)送的是1,下一個位要發(fā)送0),必須發(fā)生在SCL為低電平期間。另外在傳輸過程中, SDA上的數(shù)據(jù)位在SCL高電平期間必須保持穩(wěn)定不變。
假設(shè)SCL在高電平,想一下是不是就會觸發(fā)起始位或者終止位。想一想起始信號與停止信號是怎么發(fā)送的就會明白為什么SCl一定要在高電平才能改變SDA。
3- I2C協(xié)議主機收發(fā)數(shù)據(jù)流程
(1)主機發(fā)送數(shù)據(jù)
- 主機在檢測到總線為“空閑狀態(tài)”(即 SDA、SCL 線均為高電平)時,發(fā)送一個啟動信號“S”,開始一次通信的開始;
- 主機接著發(fā)送一個從設(shè)備地址,它由7bit物理地址和1bit讀寫控制位R/w組成(此時R/W=0);
- 相對應(yīng)的從機收到命令字節(jié)后向主機回饋應(yīng)答信號 ACK(ACK=0);
- 主機收到從機的應(yīng)答信號后開始發(fā)送第一個字節(jié)的數(shù)據(jù);
- 從機收到數(shù)據(jù)后返回一個應(yīng)答信號 ACK;
- 主機收到應(yīng)答信號后再發(fā)送下一個數(shù)據(jù)字節(jié);
- 當主機發(fā)送最后一個數(shù)據(jù)字節(jié)并收到從機的 ACK 后,通過向從機發(fā)送一個停止信號P結(jié)束本次通信并釋放總線。從機收到P信號后也退出與主機之間的通信。
注意:
- 主機通過發(fā)送地址碼與對應(yīng)的從機建立了通信關(guān)系,而掛接在總線上的其它從機雖然同時也收到了地址碼,但因為與其自身的地址不相符合,因此提前退出與主機的通信;
- 主機的一次發(fā)送通信,其發(fā)送的數(shù)據(jù)數(shù)量不受限制。主機是通過 P 信號通知發(fā)送的結(jié)束,從機收到 P 信號后退出本次通信;
- 主機的每一次發(fā)送后都是通過從機的 ACK 信號了解從機的接收狀況,如果應(yīng)答錯誤則重發(fā)。
(2)主機接收數(shù)據(jù)
- 主機發(fā)送起始信號后,接著發(fā)送地址字節(jié)(其中R/W=1);
- 對應(yīng)的從機收到地址字節(jié)后,返回一個應(yīng)答信號并向主機發(fā)送數(shù)據(jù);
- 主機收到數(shù)據(jù)后向從機反饋一個應(yīng)答信號ACK;
- 從機收到應(yīng)答信號后再向主機發(fā)送下一個數(shù)據(jù);
- 當主機完成接收數(shù)據(jù)后,向從機發(fā)送一個NAK,從機收到非應(yīng)答信號后便停止發(fā)送;
- 主機發(fā)送非應(yīng)答信號后,再發(fā)送一個停止信號,釋放總線結(jié)束通信。
注意:
- 主機所接收數(shù)據(jù)的數(shù)量是由主機自身決定,當發(fā)送“非應(yīng)答信號NAK”時從機便結(jié)束傳送并釋放總線。
- 非應(yīng)答信號的兩個作用:前一個數(shù)據(jù)接收成功,停止從機的再次發(fā)送。
4- SHT30傳感器介紹
(1)SHT30簡介
SHT30數(shù)字溫濕度傳感器采用業(yè)內(nèi)知名的瑞士Sensirion公司推出的新一代SHT30溫濕度傳感器芯片,它能夠提供極高的可靠性和出色的長期穩(wěn)定性,具有功耗低、反應(yīng)快、抗干擾能力強等優(yōu)點。IIC通訊,兼容3.3V/5V,可以非常容易的集成到智能樓宇、天氣站、倉庫存儲、養(yǎng)殖、孵化等應(yīng)用場景中,其中小米的溫濕度傳感器使用的也是SHT30。
- 高精度,內(nèi)部自動校準,數(shù)字輸出
- 低功耗、響應(yīng)速度快、抗干擾能力強
- 兼容3.3V/5V控制器
(2)SHT30工作原理
SHT30 芯片有八個引腳,利用12C進行數(shù)據(jù)傳輸,具有兩個可選地址,寬電源電壓從2.4V到5.5V。下面是引腳說明:
下面是小熊座NB-loT開發(fā)板上SHT30傳感器的原理圖,其中12C接口連到了MCU的PB13和PB14兩個引腳上,這兩個引腳可以設(shè)置為12C2模式工作。
5- HAL庫中I2C發(fā)送接收數(shù)據(jù)函數(shù)
開始實現(xiàn)HAL庫實現(xiàn)I2C對SHT30溫濕度采樣我們首先了解一下HAL庫中的兩個函數(shù)。
(1)HAL_I2C_Master_Transmit()
(1)函數(shù)原型:
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
(2)函數(shù)功能:IIC發(fā)送數(shù)據(jù),主機需要將數(shù)據(jù)通過IIC發(fā)送過去
(3)參數(shù)介紹:
- *hi2c 設(shè)置使用的是那個IIC
- DevAddress 寫入的地址,設(shè)置寫入數(shù)據(jù)的地址
- *pData 需要寫入的數(shù)據(jù)
- Size 要發(fā)送的字節(jié)數(shù)
- Timeout 最大傳輸時間,超過傳輸時間將自動退出傳輸函數(shù)
(4) 使用到的函數(shù)參數(shù)講解(舉例子):
HAL_I2C_Master_Transmit(&hi2c2, SHT30_ADDR_WR, (uint8_t*)buf, 2, 0xFFFF);
- &hi2c2:我們使用的是:hi2c2,傳地址&hi2c2
- SHT30_ADDR_WR:我們宏定義了寫的地址,傳寫的地址
c #define SHT30_ADDR_WR (SHT30_ADDR<<1)
- (uint8_t*)buf:我們將需要傳的數(shù)據(jù)保存在buf中
- 2:傳2個字節(jié),16個位
- 0xFFFF超時:oxFFFF(4 294 967 295也就是無符號整型所能表示的最大值)
(2)HAL_I2C_Master_Receive()
(1)函數(shù)原型:
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
(2)函數(shù)功能:IIC接收數(shù)據(jù),從機發(fā)送給主機,主機需要將數(shù)據(jù)通過IIC接收
(3)函數(shù)參數(shù)和HAL_I2C_Master_Transmit()大概是一樣的,只是取到的數(shù)據(jù)保存在pData中。
(4)使用到的函數(shù)參數(shù)講解(舉例子):
HAL_I2C_Master_Receive(&hi2c2, SHT30_ADDR_RD, buf, SHT30_DATA_SIZE, 0xFFFF);
- &hi2c2:我們使用的是:hi2c2,傳地址&hi2c2
- SHT30_ADDR_RD:我們宏定義了讀的地址,傳讀的地址
c #define SHT30_ADDR_RD ((SHT30_ADDR<<1) | 0x01)
- buf:我們將獲取到的數(shù)據(jù)保存在buf中
- SHT30_DATA_SIZE:宏定義,6個字節(jié)
c #define SHT30_DATA_SIZE 6
- 0xFFFF超時:oxFFFF(4 294 967 295也就是無符號整型所能表示的最大值)
6- SHT30溫濕度采樣
(1)配置
先配置SHT30連接的I2C管腳PB13 和PB14為I2C模式,此時因為I2C功能并沒有使能,管腳狀態(tài)為黃色。接下來再在Connectivity里選擇12C2并配置為I2C模式,這是12C功能配置完成。配置好之后按Ctrl+S將會自動生成I2C總線初始化代碼。
(2)創(chuàng)建sht30.c
/*
* sht30.c
*
* Created on: Nov 3, 2022
* Author: Administrator
*/
#include <stdio.h>
#include "stm32l4xx_hal.h"
#include "i2c.h"
#include "sht30.h"
#define CONFIG_SHT30_DEBUG
#ifdef CONGIF_SHT30_DEBUG
#define sht30_print(format, args...) printf(format, ##args)
#else
#define sht30_print(format, args...) do{} while(0)
#endif
static int sht30_send_cmd(SHT30_CMD cmd)
{
uint8_t buf[2];
buf[0] = cmd >> 8;
buf[1] = cmd & 0xFF;
return HAL_I2C_Master_Transmit(&hi2c2, SHT30_ADDR_WR, (uint8_t*)buf, 2, 0xFFFF);
}
static void sht30_soft_reset(void)
{
sht30_send_cmd(SOFT_RESET_CMD);
HAL_Delay(1);
}
static int sht30_single_shot_measurement(uint8_t *buf, uint8_t buf_size)
{
uint16_t cmd = HIGH_ENABLED_CMD;
uint8_t rv;
if( !buf || buf_size < SHT30_DATA_SIZE )
{
sht30_print("%s(): Invalid input argument\n", __func__);
return -1;
}
rv = sht30_send_cmd(cmd);
if( rv )
{
sht30_print("ERROR: HST30 send messurement command failure, rv = &d\n", rv);
sht30_soft_reset();
return -2;
}
rv = HAL_I2C_Master_Receive(&hi2c2, SHT30_ADDR_RD, buf, SHT30_DATA_SIZE, 0xFFFF);
if(rv)
{
sht30_print("ERROR: SHT30 read measurement result failure, rv = %d\n", rv);
return -3;
}
return 0;
}
static uint8_t sht30_crc8(const uint8_t *data, int len)
{
const uint8_t POLYNOMIAL = 0x31;
uint8_t crc = 0xFF;
int i,j;
for (i=0; i<len; ++i)
{
crc ^= *data++;
for (j=0; j<8; j++)
{
crc = (crc & 0x80)? (crc << 1) ^ POLYNOMIAL:(crc << 1);
}
}
return crc;
}
int SHT30_SampleData(float *temperature, float *humidity)
{
uint8_t buf[SHT30_DATA_SIZE];
int rv;
uint16_t temp;
uint16_t humd;
uint16_t crc;
if(!temperature || !humidity)
{
sht30_print("%s(): Invalid input argument\n", __func__);
return -1;
}
rv = sht30_single_shot_measurement(buf, SHT30_DATA_SIZE);
if(rv)
{
sht30_print("SHT30 Single Short measurement failure, rv=%d\n", rv);
return -2;
}
#ifdef CONFIG_SHT30_DEBUG
{
int i;
sht30_print("SHT30 get %d bytes sample data: \n", SHT30_DATA_SIZE);
for(i=0; i<SHT30_DATA_SIZE; i++)
{
sht30_print("0x%02x ", buf[i]);
}
sht30_print("\n");
}
#endif
crc = sht30_crc8(buf, 2);
sht30_print("SHT30 temperature Cal_CRC: [%02x] EXP_CRC: [%02x]\n", crc, buf[2]);
if(crc != buf[2])
{
sht30_print("SHT30 measurement temperature got CRC error\n");
return -3;
}
crc = sht30_crc8(&buf[3], 2);
sht30_print("SHT30 humidity Cal_CRC: [%02x] EXP_CRC: [%02x]\n", crc, buf[5]);
if(crc != buf[5])
{
sht30_print("SHT30 messurement temperature got CRC error\n");
return -4;
}
temp = (buf[0]<<8) | buf[1];
humd = (buf[3]<<8) | buf[4];
*temperature = -45 + 175*((float)temp/65535);
*humidity = 100 * ((float)humd/65535);
return 0;
}
(3)創(chuàng)建sht30.h
/*
* sht30.h
*
* Created on: Nov 3, 2022
* Author: Administrator
*/
#ifndef INC_SHT30_H_
#define INC_SHT30_H_
#include "stm32l4xx_hal.h"
#define SHT30_ADDR 0x44
#define SHT30_ADDR_WR (SHT30_ADDR<<1)
#define SHT30_ADDR_RD ((SHT30_ADDR<<1) | 0x01)
#define SHT30_DATA_SIZE 6
typedef enum
{
SOFT_RESET_CMD = 0x30A2,
HIGH_ENABLED_CMD = 0x2C06,
MEDIUM_ENABLED_CMD =0x2C0D,
LOW_ENSABLED_CMD = 0x2C10,
HIGH_DISABLED_CMD = 0x2400,
MEDIUM_DISABLED_CMD = 0X240B,
LOW_DISABLED_CMD = 0x2416,
HIGH_0_5_CMD = 0x2032,
MEDIUM_0_5_CMD = 0x2024,
LOW_0_5_CMD = 0x202F,
HIGH_1_CMD = 0x2130,
MEDIUM_1_CMD = 0x2126,
LOW_1_CMD = 0x212D,
HIGH_2_CMD = 0x2236,
MEDIUM_2_CMD = 0x2220,
LOW_2_CMD = 0x222B,
HIGH_4_CMD = 0x2334,
MEDIUM_4_CMD = 0x2322,
LOW_4_CMD = 0x2329,
HIGH_10_CMD = 0x2737,
MEDIUM_10_CMD = 0x2721,
LOW_10_CMD = 0x272A,
}SHT30_CMD;
extern int SHT30_SampleData(float *temperature, float *humidity);
#endif /* INC_SHT30_H_ */
(4)修改main.c
printf()函數(shù)實現(xiàn)的代碼前期博客中有,就不多說了。文章來源:http://www.zghlxwxcb.cn/news/detail-423601.html
/* USER CODE BEGIN Includes */
#include "sht30.h"
#include "string.h"
/* USER CODE END Includes */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static int report_tempRH_json(void);
/* USER CODE END 0 */
printf ("The sht30 starts to obtain temperature and humidity data.\r\n");
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
report_tempRH_json();
HAL_Delay(3000);
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
int report_tempRH_json(void)
{
char buf[128];
float temperature, humidity;
if(SHT30_SampleData(&temperature, &humidity) < 0)
{
printf("ERROR: SHT30 Sample data failure\n");
return -1;
}
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), "{\"Temperature\":\"%.2f\", \"Humidity\":\"%.2f\"}", temperature, humidity);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 0xFFFF);
return 0;
}
/* USER CODE END 4 */
7- 結(jié)果呈現(xiàn)
對著開發(fā)板上的溫濕度傳感器哈氣,就會看見變化,說明成功。文章來源地址http://www.zghlxwxcb.cn/news/detail-423601.html
到了這里,關(guān)于I2C協(xié)議介紹以及HAL庫實現(xiàn)I2C對SHT30溫濕度采樣的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!