这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 国产MCU » 【CH582M】CH582M的I2C外设(4)-----主模式测试例(GXHT3

共1条 1/1 1 跳转至页

【CH582M】CH582M的I2C外设(4)-----主模式测试例(GXHT30)

专家
2026-10-09 20:48:36     打赏

单纯测试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
#endif


3、官方提供的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;
}

调试结果:

image.png










关键词: CH582M     I2C     GXHT30    

共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]