標準 I2C:SCL 高電平時 SDA 由高→低產生 START。
SHT11:SDA 先拉低,SCL 產生一個時鐘脈沖(高→低→高),SDA 再拉高。這要求精確控制 GPIO 時序。
#define IIC_SCL RC0
#define IIC_SDA RC1
#define IIC_SCL_DIR TRISC0
#define IIC_SDA_DIR TRISC1
#define INPUT 1
#define OUTPUT 0
#define SCL_HIGH() IIC_SCL_DIR = INPUT // 釋放總線,上拉電阻拉高
#define SCL_LOW() do { IIC_SCL_DIR = OUTPUT; IIC_SCL = 0; } while(0)
#define SDA_HIGH() IIC_SDA_DIR = INPUT
#define SDA_LOW() do { IIC_SDA_DIR = OUTPUT; IIC_SDA = 0; } while(0)
關鍵技巧:利用三態輸出特性,輸入模式時由外部上拉電阻拉高(邏輯 1),輸出模式驅動低電平(邏輯 0),實現開漏輸出效果。
void SHT_Start(void) {
SCL_HIGH(); SDA_HIGH(); delay_us(5);
SDA_LOW(); delay_us(5);
SCL_LOW(); delay_us(5);
SCL_HIGH(); delay_us(5); // SHT11 要求的額外時鐘脈沖
SDA_HIGH(); delay_us(5);
SCL_LOW();
}
void SHT_WriteByte(unsigned char dat) {
for (int i = 0; i < 8; i++) {
if (dat & 0x80) SDA_HIGH(); else SDA_LOW();
dat <<= 1;
SCL_LOW(); delay_us(5);
SCL_HIGH(); delay_us(5); // 上升沿發送數據
}
SCL_LOW();
SDA_HIGH(); delay_us(5); // 釋放總線準備讀取 ACK
SCL_HIGH(); delay_us(5); // 主機產生第 9 個時鐘讀取 ACK
// 可在此檢測 IIC_SDA 是否為 0(從機應答)
SCL_LOW();
SDA_HIGH();
}
unsigned char SHT_ReadByte(unsigned char ack) {
unsigned char dat = 0;
SDA_HIGH(); // 釋放總線
for (int i = 0; i < 8; i++) {
dat <<= 1;
SCL_HIGH(); delay_us(5);
if (IIC_SDA) dat |= 1; // 上升沿讀取數據
SCL_LOW(); delay_us(5);
}
// 發送應答
if (ack) SDA_LOW(); else SDA_HIGH();
SCL_LOW(); delay_us(5);
SCL_HIGH(); delay_us(5);
SCL_LOW();
SDA_HIGH();
return dat;
}
void SHT_Stop(void) {
SDA_LOW(); delay_us(5);
SCL_HIGH(); delay_us(5);
SDA_HIGH(); delay_us(5); // SCL 高時 SDA 低→高
SCL_LOW();
}
delay_us(5) 滿足 SHT11 最小 4.7μs 時鐘週期,實際應根據 MCU 主頻校準cli()/sei()),防止時序被打亂GPIO 模擬 I2C 是嵌入式工程師必備技能。通過精確控制 SCL/SDA 時序,可驅動任何兼容 I2C 協議的設備,甚至適配非標準時序器件。關鍵在於理解開漏輸出機制、掌握時序圖參數、合理設置延時與中斷保護。