单纯测试I2C写,使用OLED更好,测试直观可视。这里我选择GXHT30作为测试对象,是因为处理过程中有写、读的处理。写的测试可以参考官方例程,而读取需要做一些摸索,毕竟是要通过直接操作寄存器的方式来实现的,有哪些寄存器需要被设置、设置成什么值,没有官方例程可以参考。
电路连接上,PB12作SDA用,PB13作SCL用。I2C外设的通讯速率为400KHz。
以下是主程序代码:
1、main.c
#include "CH58x_common.h"
#include "GXHT30.h"
/**
* @brief float 转字符串(保留 1 位小数,支持负数)
* @param val 浮点值
* @param buf 缓冲区,至少 12 字节
*/
void float_to_str(float val, char *buf)
{
int16_t v = (int16_t)(val * 10);
if (v < 0) {
buf[0] = '-';
v = -v;
sprintf(buf + 1, "%d.%d", v / 10, v % 10);
} else {
sprintf(buf, "%d.%d", v / 10, v % 10);
}
}
uint8_t TxData[2] = {0x2C, 0x06};
int main(void)
{
uint8_t i=0, addr;
uint8_t err;
GXHT30_Data_t d;
char str_t[12], str_h[12];
/* 系统初始化 */
SetSysClock(CLK_SOURCE_PLL_60MHz);
mDelaymS(500); // 上电延时,稳定供电
// 串口用引脚
GPIOA_SetBits(GPIO_Pin_9);
GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
UART1_DefInit();
PRINT("GXHT30 I2C test\r\n");
GXHT30_Init(); // 初始化GXHT30
PRINT("GXHT30 init done\r\n");
while (1) {
err = GXHT30_ReadData(&d); // 获取温湿度数据
if (err == 0) {
//PRINT("Temp = %d C, Humi = %d %%RH\r\n", (int)d.temperature, (int)d.humidity);
float_to_str(d.temperature, str_t);
float_to_str(d.humidity, str_h);
PRINT("Temp = %s C, Humi = %s %%RH\r\n", str_t, str_h);
} else {
PRINT("read error = %d\r\n", err);
}
mDelaymS(1000);
}
}2、gxht.h(处理代码页也写这个头文件中了)
#ifndef __GXHT30_H
#define __GXHT30_H
#ifdef __cplusplus
extern "C" {
#endif
#include "CH58x_common.h"
/* ============== 引脚配置(可按需修改) ============== */
/* 默认:SDA -> PB14 SCL -> PB15 */
#define GXHT30_SDA_PIN GPIO_Pin_12
#define GXHT30_SCL_PIN GPIO_Pin_13
/* ================================================== */
#define GXHT30_ADDR_7BIT 0x44 /* ADDR 引脚接 GND;接 VCC 改为 0x45 */
#define GXHT30_ADDR_W (GXHT30_ADDR_7BIT << 1)
#define GXHT30_ADDR_R ((GXHT30_ADDR_7BIT << 1) | 1)
/* 测量命令:单次测量,高重复性 */
#define GXHT30_CMD_HIGH 0x2C06
#define GXHT30_CMD_MED 0x2C0D
#define GXHT30_CMD_LOW 0x2C10
#define GXHT30_CMD_RESET 0x30A2
/* 错误码定义 */
#define GXHT30_OK 0
#define GXHT30_ERR_START 1 /* 起始条件失败 */
#define GXHT30_ERR_ADDR_W 2 /* 写地址无应答 */
#define GXHT30_ERR_TXE 3 /* 发送数据超时 */
#define GXHT30_ERR_BTF 4 /* 字节发送完成超时 */
#define GXHT30_ERR_ADDR_R 5 /* 读地址无应答 */
#define GXHT30_ERR_RXNE 6 /* 接收数据超时 */
#define GXHT30_ERR_CRC_T 7 /* 温度 CRC 错误 */
#define GXHT30_ERR_CRC_H 8 /* 湿度 CRC 错误 */
#define GXHT30_I2C_TIMEOUT 100000UL /* 循环次数阈值,按主频调整 */
// 温湿度数据结构体
typedef struct {
float temperature; /* ℃ */
float humidity; /* %RH */
} GXHT30_Data_t;
void GXHT30_Init(void);
uint8_t GXHT30_SoftReset(void);
uint8_t GXHT30_ReadData(GXHT30_Data_t *data); /* 0 成功,非 0 失败 */
/* ---------------- 引脚配置 ---------------- */
#define GXHT30_SDA_PIN GPIO_Pin_12
#define GXHT30_SCL_PIN GPIO_Pin_13
#define GXHT30_ADDR_7BIT 0x44
/* ---------------- 硬件 I2C 底层 ---------------- */
void GXHT30_Init(void) {
/* 1. 配置引脚为上拉输入(硬件 I2C 自动开漏) */
GPIOB_ModeCfg(GXHT30_SDA_PIN | GXHT30_SCL_PIN, GPIO_ModeIN_PU);
/* 2. 初始化 I2C 外设:400kHz,7位地址,使能应答 */
I2C_Init(I2C_Mode_I2C, 400000, I2C_DutyCycle_16_9, I2C_Ack_Enable, I2C_AckAddr_7bit, 0x00);
while(I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET);
/* 3. 等待总线空闲 */
while (I2C_GetFlagStatus(I2C_FLAG_BUSY) != RESET);
mDelaymS(5); /* 等待传感器上电稳定 */
GXHT30_SoftReset();
}
/* ---------------- CRC8 校验(多项式 0x31,初值 0xFF) ---------------- */
static uint8_t gxht30_crc8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0xFF, i, b;
while (len--) {
crc ^= *data++;
for (b = 0; b < 8; b++) {
if (crc & 0x80)
crc = (uint8_t)((crc << 1) ^ 0x31);
else
crc = (uint8_t)(crc << 1);
}
}
return crc;
}
/* ---------------- GXHT30 功能函数 ---------------- */
/**
* @brief 发送16位指令
* @param
* @return 0 成功;0以外 错误
*/
uint8_t GXHT30_SendCmd(uint16_t cmd) {
uint32_t timeout;
/* 1. 起始条件 */
I2C_GenerateSTART(ENABLE);
timeout = GXHT30_I2C_TIMEOUT;
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE); /* 收尾,释放总线 */
return GXHT30_ERR_START;
}
}
/* 2. 写地址 */
I2C_Send7bitAddress(GXHT30_ADDR_W, I2C_Direction_Transmitter);
timeout = GXHT30_I2C_TIMEOUT;
while (!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_ADDR_W;
}
}
/* 3. 发送命令高字节 */
timeout = GXHT30_I2C_TIMEOUT;
while (I2C_GetFlagStatus(I2C_FLAG_TXE) == RESET) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_TXE;
}
}
I2C_SendData((uint8_t)(cmd >> 8));
/* 4. 发送命令低字节 */
timeout = GXHT30_I2C_TIMEOUT;
while (I2C_GetFlagStatus(I2C_FLAG_TXE) == RESET) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_TXE;
}
}
I2C_SendData((uint8_t)(cmd & 0xFF));
/* 5. 等最后一个字节发完(含 ACK 位) */
timeout = GXHT30_I2C_TIMEOUT;
while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_BTF;
}
}
I2C_GenerateSTOP(ENABLE);
return GXHT30_OK;
}
/**
* @brief 软复位GXHT30
* @param
* @return 0 成功;0以外 错误
*/
uint8_t GXHT30_SoftReset(void) {
return GXHT30_SendCmd((uint16_t)GXHT30_CMD_RESET);
}
/**
* @brief 单次测量读取温湿度
* @param data 输出
* @return 0 成功;1 命令无 ACK;2 读无 ACK;3/4 CRC 错误
*/
uint8_t GXHT30_ReadData(GXHT30_Data_t *data) {
uint8_t buf[6], i, ret;
uint16_t raw_t, raw_h;
uint32_t timeout;
/* ================= 第一段:发送测量命令 ================= */
ret = GXHT30_SendCmd((uint16_t)GXHT30_CMD_HIGH);
/* 6. 等待转换(高重复性最长 15.5ms,给 20ms 余量) */
mDelaymS(20);
/* ================= 第二段:读取 6 字节 ================= */
/* 1. 起始条件 */
I2C_GenerateSTART(ENABLE);
timeout = GXHT30_I2C_TIMEOUT;
while (!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_START;
}
}
/* 2. 读地址 */
I2C_Send7bitAddress(GXHT30_ADDR_R, I2C_Direction_Receiver);
timeout = GXHT30_I2C_TIMEOUT;
while (!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) {
if (--timeout == 0) {
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_ADDR_R;
}
}
/* 3. 连续接收 6 字节 */
for (i = 0; i < 6; i++) {
timeout = GXHT30_I2C_TIMEOUT;
while (I2C_GetFlagStatus(I2C_FLAG_RXNE) == RESET) {
if (--timeout == 0) {
I2C_AcknowledgeConfig(ENABLE); /* 恢复 ACK */
I2C_GenerateSTOP(ENABLE);
return GXHT30_ERR_RXNE;
}
}
buf[i] = I2C_ReceiveData();
/* 第 5 字节读完(下标 4)后关闭 ACK,让第 6 字节收到 NACK */
if (i == 4) {
I2C_AcknowledgeConfig(DISABLE);
}
}
I2C_GenerateSTOP(ENABLE);
I2C_AcknowledgeConfig(ENABLE); /* 恢复 ACK,保证下次通信正常 */
/* ================= 数据校验与换算 ================= */
if (gxht30_crc8(&buf[0], 2) != buf[2]) return GXHT30_ERR_CRC_T;
if (gxht30_crc8(&buf[3], 2) != buf[5]) return GXHT30_ERR_CRC_H;
raw_t = ((uint16_t)buf[0] << 8) | buf[1];
raw_h = ((uint16_t)buf[3] << 8) | buf[4];
data->temperature = -45.0f + 175.0f * (float)raw_t / 65535.0f;
data->humidity = 100.0f * (float)raw_h / 65535.0f;
return GXHT30_OK;
}
#ifdef __cplusplus
}
#endif
#endif3、官方提供的SDK(CH58x_i2c.c)中的必要函数
/*********************************************************************
* @fn I2C_GenerateSTART
*
* @brief Generates I2Cx communication START condition.
*
* @param NewState - ENABLE or DISABLE.
*
* @return none
*/
void I2C_GenerateSTART(FunctionalState NewState)
{
if(NewState != DISABLE)
R16_I2C_CTRL1 |= RB_I2C_START;
else
R16_I2C_CTRL1 &= ~RB_I2C_START;
}
/*********************************************************************
* @fn I2C_GenerateSTOP
*
* @brief Generates I2Cx communication STOP condition.
*
* @param NewState - ENABLE or DISABLE.
*
* @return none
*/
void I2C_GenerateSTOP(FunctionalState NewState)
{
if(NewState != DISABLE)
R16_I2C_CTRL1 |= RB_I2C_STOP;
else
R16_I2C_CTRL1 &= ~RB_I2C_STOP;
}
/*********************************************************************
* @fn I2C_CheckEvent
*
* @brief Checks whether the last I2Cx Event is equal to the one passed as parameter.
*
* @param I2C_EVENT - specifies the event to be checked.
* I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED : EV1.
* I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED : EV1.
* I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED : EV1.
* I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED : EV1.
* I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED : EV1.
* I2C_EVENT_SLAVE_BYTE_RECEIVED : EV2.
* (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF) : EV2.
* (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL) : EV2.
* I2C_EVENT_SLAVE_BYTE_TRANSMITTED : EV3.
* (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF) : EV3.
* (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL) : EV3.
* I2C_EVENT_SLAVE_ACK_FAILURE : EV3_2.
* I2C_EVENT_SLAVE_STOP_DETECTED : EV4.
* I2C_EVENT_MASTER_MODE_SELECT : EV5.
* I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : EV6.
* I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED : EV6.
* I2C_EVENT_MASTER_BYTE_RECEIVED : EV7.
* I2C_EVENT_MASTER_BYTE_TRANSMITTING : EV8.
* I2C_EVENT_MASTER_BYTE_TRANSMITTED : EV8_2.
* I2C_EVENT_MASTER_MODE_ADDRESS10 : EV9.
*
* @return 1 - SUCCESS or 0 - ERROR.
*/
uint8_t I2C_CheckEvent(uint32_t I2C_EVENT)
{
uint32_t lastevent = 0;
uint32_t flag1 = 0, flag2 = 0;
uint8_t status = 0;
flag1 = R16_I2C_STAR1;
flag2 = R16_I2C_STAR2;
flag2 = flag2 << 16;
lastevent = (flag1 | flag2) & FLAG_Mask;
if((lastevent & I2C_EVENT) == I2C_EVENT)
{
status = !0;
}
else
{
status = 0;
}
return status;
}
/*********************************************************************
* @fn I2C_Send7bitAddress
*
* @brief Transmits the address byte to select the slave device.
*
* @param Address - specifies the slave address which will be transmitted.
* @param I2C_Direction - specifies whether the I2C device will be a Transmitter or a Receiver.
* I2C_Direction_Transmitter - Transmitter mode.
* I2C_Direction_Receiver - Receiver mode.
*
* @return none
*/
void I2C_Send7bitAddress(uint8_t Address, uint8_t I2C_Direction)
{
if(I2C_Direction != I2C_Direction_Transmitter)
Address |= OADDR1_ADD0_Set;
else
Address &= OADDR1_ADD0_Reset;
R16_I2C_DATAR = Address;
}
/*********************************************************************
* @fn I2C_GetFlagStatus
*
* @brief Checks whether the last I2Cx Event is equal to the one passed as parameter.
*
* @param I2C_FLAG - specifies the flag to check.
* I2C_FLAG_DUALF - Dual flag (Slave mode).
* I2C_FLAG_SMBHOST - SMBus host header (Slave mode).
* I2C_FLAG_SMBDEFAULT - SMBus default header (Slave mode).
* I2C_FLAG_GENCALL - General call header flag (Slave mode).
* I2C_FLAG_TRA - Transmitter/Receiver flag.
* I2C_FLAG_BUSY - Bus busy flag.
* I2C_FLAG_MSL - Master/Slave flag.
* I2C_FLAG_SMBALERT - SMBus Alert flag.
* I2C_FLAG_TIMEOUT - Timeout or Tlow error flag.
* I2C_FLAG_PECERR - PEC error in reception flag.
* I2C_FLAG_OVR - Overrun/Underrun flag (Slave mode).
* I2C_FLAG_AF - Acknowledge failure flag.
* I2C_FLAG_ARLO - Arbitration lost flag (Master mode).
* I2C_FLAG_BERR - Bus error flag.
* I2C_FLAG_TXE - Data register empty flag (Transmitter).
* I2C_FLAG_RXNE - Data register not empty (Receiver) flag.
* I2C_FLAG_STOPF - Stop detection flag (Slave mode).
* I2C_FLAG_ADD10 - 10-bit header sent flag (Master mode).
* I2C_FLAG_BTF - Byte transfer finished flag.
* I2C_FLAG_ADDR - Address sent flag (Master mode) "ADSL"
* Address matched flag (Slave mode)"ENDA".
* I2C_FLAG_SB - Start bit flag (Master mode).
*
* @return FlagStatus - SET or RESET.
*/
FlagStatus I2C_GetFlagStatus(uint32_t I2C_FLAG)
{
FlagStatus bitstatus = RESET;
__IO uint32_t i2creg = 0, i2cxbase = 0;
i2cxbase = (uint32_t)BA_I2C;
i2creg = I2C_FLAG >> 28;
I2C_FLAG &= FLAG_Mask;
if(i2creg != 0)
{
i2cxbase += 0x14;
}
else
{
I2C_FLAG = (uint32_t)(I2C_FLAG >> 16);
i2cxbase += 0x18;
}
if(((*(__IO uint32_t *)i2cxbase) & I2C_FLAG) != (uint32_t)RESET)
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
return bitstatus;
}
/*********************************************************************
* @fn I2C_AcknowledgeConfig
*
* @brief Enables or disables the specified I2C acknowledge feature.
*
* @param NewState - ENABLE or DISABLE.
*
* @return none
*/
void I2C_AcknowledgeConfig(FunctionalState NewState)
{
if(NewState != DISABLE)
R16_I2C_CTRL1 |= RB_I2C_ACK;
else
R16_I2C_CTRL1 &= ~RB_I2C_ACK;
}
/*********************************************************************
* @fn I2C_ReceiveData
*
* @brief Returns the most recent received data by the I2Cx peripheral.
*
* @return The value of the received data.
*/
uint8_t I2C_ReceiveData(void)
{
return (uint8_t)R16_I2C_DATAR;
}调试结果:

我要赚赏金
