#ifndef _GXHT30_CH32_HW_I2C_H #define _GXHT30_CH32_HW_I2C_H #include #include #include "ch32fun.h" #include "ch32v20xhw.h" // I2C Configuration #define GXHT30_I2C_CLKRATE 400000 #define GXHT30_I2C_PRERATE 2000000 #define GXHT30_I2C_TIMEOUT_MAX 250000 // GXHT30 I2C Addresses #define GXHT30_I2C_ADDR_DEFAULT 0x44 #define GXHT30_I2C_ADDR_ALT 0x45 // Commands #define GXHT30_CMD_MEAS_MSB 0x2C #define GXHT30_CMD_MEAS_LSB 0x06 #define GXHT30_CMD_SOFT_RESET_MSB 0x30 #define GXHT30_CMD_SOFT_RESET_LSB 0xA2 #define GXHT30_CMD_HEATER_ON_MSB 0x30 #define GXHT30_CMD_HEATER_ON_LSB 0x6D #define GXHT30_CMD_HEATER_OFF_MSB 0x30 #define GXHT30_CMD_HEATER_OFF_LSB 0x66 #define GXHT30_CMD_STATUS_MSB 0xF3 #define GXHT30_CMD_STATUS_LSB 0x2D #define GXHT30_CMD_CLEAR_STATUS_MSB 0x30 #define GXHT30_CMD_CLEAR_STATUS_LSB 0x41 // I2C Event Masks #define GXHT30_I2C_EVT_MASTER_MODE_SELECT \ ((uint32_t)0x00030001) // BUSY, MSL, SB #define GXHT30_I2C_EVT_MASTER_TRANSMITTER_MODE \ ((uint32_t)0x00070082) // BUSY, MSL, ADDR, TXE, TRA #define GXHT30_I2C_EVT_MASTER_RECEIVER_MODE \ ((uint32_t)0x00030002) // BUSY, MSL, ADDR #define GXHT30_I2C_EVT_MASTER_BYTE_TRANSMITTED \ ((uint32_t)0x00070084) // TRA, BUSY, MSL, TXE, BTF #define GXHT30_I2C_EVT_MASTER_BYTE_RECEIVED \ ((uint32_t)0x00030040) // BUSY, MSL, RXNE // Sensor Data Structure typedef struct { float temperature; // Temperature in Celsius float humidity; // Relative humidity in % uint8_t error; // Last error code } GXHT30_Data; // Status Register Structure typedef struct { uint8_t alert_pending; // Bit 15: Alert status uint8_t heater_on; // Bit 13: Heater status uint8_t humidity_alert; // Bit 11: Humidity tracking alert uint8_t temperature_alert; // Bit 10: Temperature tracking alert uint8_t reset_detected; // Bit 4: System reset detected uint8_t command_status; // Bit 1: Last command execution status uint8_t crc_status; // Bit 0: Write data CRC checksum status uint16_t raw_status; // Raw 16-bit status value } GXHT30_Status; // Error Codes enum GXHT30_Error { GXHT30_OK = 0, GXHT30_ERR_TIMEOUT, GXHT30_ERR_CRC, GXHT30_ERR_I2C, GXHT30_ERR_BUSY }; static inline uint8_t _gxht30_i2c_check_event(uint32_t event_mask) { uint32_t status = I2C1->STAR1 | (I2C1->STAR2 << 16); return (status & event_mask) == event_mask; } static inline uint8_t _gxht30_wait_event(uint32_t event_mask) { int32_t timeout = GXHT30_I2C_TIMEOUT_MAX; while (!_gxht30_i2c_check_event(event_mask) && (timeout-- > 0)); return timeout > 0 ? GXHT30_OK : GXHT30_ERR_TIMEOUT; } static inline uint8_t _gxht30_wait_flag(uint32_t flag) { int32_t timeout = GXHT30_I2C_TIMEOUT_MAX; while (!(I2C1->STAR1 & flag) && (timeout-- > 0)); return timeout > 0 ? GXHT30_OK : GXHT30_ERR_TIMEOUT; } static inline uint8_t _gxht30_i2c_start(uint8_t addr, uint8_t direction) { // wait until bus is not busy int32_t timeout = GXHT30_I2C_TIMEOUT_MAX; while ((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout-- > 0)); if (timeout <= 0) return GXHT30_ERR_TIMEOUT; // gen START I2C1->CTLR1 |= I2C_CTLR1_START; if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_MODE_SELECT) != GXHT30_OK) return GXHT30_ERR_TIMEOUT; // send addr I2C1->DATAR = (addr << 1) | direction; uint32_t event = (direction == 0) ? GXHT30_I2C_EVT_MASTER_TRANSMITTER_MODE : GXHT30_I2C_EVT_MASTER_RECEIVER_MODE; return _gxht30_wait_event(event); } static inline uint8_t _gxht30_i2c_write_byte(uint8_t data) { I2C1->DATAR = data; return _gxht30_wait_flag(I2C_STAR1_TXE); } static inline uint8_t _gxht30_i2c_read(uint8_t* buffer, uint8_t length) { for (uint8_t i = 0; i < length; i++) { if (i == length - 1) { I2C1->CTLR1 &= ~I2C_CTLR1_ACK; // NACK last byte } if (_gxht30_wait_flag(I2C_STAR1_RXNE) != GXHT30_OK) { I2C1->CTLR1 |= I2C_CTLR1_ACK; return GXHT30_ERR_TIMEOUT; } buffer[i] = I2C1->DATAR; } I2C1->CTLR1 |= I2C_CTLR1_ACK; // re-enable ACK return GXHT30_OK; } static inline bool _gxht30_crc8_check(uint8_t msb, uint8_t lsb, uint8_t crc) { uint8_t calc_crc = 0xFF; uint8_t data[2] = {msb, lsb}; for (uint8_t byte = 0; byte < 2; byte++) { calc_crc ^= data[byte]; for (uint8_t i = 0; i < 8; i++) { calc_crc = (calc_crc & 0x80) ? (calc_crc << 1) ^ 0x31 : (calc_crc << 1); } } return calc_crc == crc; } // init I2C hw static inline void gxht30_i2c_init(void) { uint16_t tempreg; RCC->APB2PCENR |= RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO; RCC->APB1PCENR |= RCC_APB1Periph_I2C1; // PB6 (SCL) and PB7 (SDA) GPIOB->CFGLR &= ~(0xff << (4 * 6)); GPIOB->CFGLR |= ((GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 6)) | ((GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF) << (4 * 7)); // rst I2C1 RCC->APB1PRSTR |= RCC_APB1Periph_I2C1; RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1; // i2c frequency tempreg = I2C1->CTLR2; tempreg &= ~I2C_CTLR2_FREQ; tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK / GXHT30_I2C_PRERATE) & I2C_CTLR2_FREQ; I2C1->CTLR2 = tempreg; // Fast Mode 400kHz tempreg = (FUNCONF_SYSTEM_CORE_CLOCK / (3 * GXHT30_I2C_CLKRATE)) & I2C_CKCFGR_CCR; tempreg |= I2C_CKCFGR_FS; I2C1->CKCFGR = tempreg; // en I2C and ACK I2C1->CTLR1 |= I2C_CTLR1_PE | I2C_CTLR1_ACK; } static inline uint8_t gxht30_send_command(uint8_t addr, uint8_t cmd_msb, uint8_t cmd_lsb) { uint8_t err; if ((err = _gxht30_i2c_start(addr, 0)) != GXHT30_OK) return err; if ((err = _gxht30_i2c_write_byte(cmd_msb)) != GXHT30_OK) return err; if ((err = _gxht30_i2c_write_byte(cmd_lsb)) != GXHT30_OK) return err; if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_BYTE_TRANSMITTED) != GXHT30_OK) return GXHT30_ERR_TIMEOUT; I2C1->CTLR1 |= I2C_CTLR1_STOP; return GXHT30_OK; } // read temp and humidity static inline uint8_t gxht30_read_data(uint8_t addr, GXHT30_Data* data) { uint8_t rx_data[6]; uint8_t err; if ((err = gxht30_send_command(addr, GXHT30_CMD_MEAS_MSB, GXHT30_CMD_MEAS_LSB)) != GXHT30_OK) { data->error = err; return err; } // read data if ((err = _gxht30_i2c_start(addr, 1)) != GXHT30_OK) { data->error = err; return err; } if ((err = _gxht30_i2c_read(rx_data, 6)) != GXHT30_OK) { data->error = err; return err; } I2C1->CTLR1 |= I2C_CTLR1_STOP; // verify crc if (!_gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2]) || !_gxht30_crc8_check(rx_data[3], rx_data[4], rx_data[5])) { data->error = GXHT30_ERR_CRC; return GXHT30_ERR_CRC; } // calc values uint16_t temp_raw = (rx_data[0] << 8) | rx_data[1]; uint16_t hum_raw = (rx_data[3] << 8) | rx_data[4]; data->temperature = (float)temp_raw * 0.00267033f - 45.0f; data->humidity = (float)hum_raw * 0.0015259f; data->error = GXHT30_OK; return GXHT30_OK; } static inline uint8_t gxht30_read_status(uint8_t addr, GXHT30_Status* status) { uint8_t rx_data[3]; uint8_t err; if ((err = _gxht30_i2c_start(addr, 0)) != GXHT30_OK) return err; if ((err = _gxht30_i2c_write_byte(GXHT30_CMD_STATUS_MSB)) != GXHT30_OK) return err; if ((err = _gxht30_i2c_write_byte(GXHT30_CMD_STATUS_LSB)) != GXHT30_OK) return err; if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_BYTE_TRANSMITTED) != GXHT30_OK) return GXHT30_ERR_TIMEOUT; I2C1->CTLR1 |= I2C_CTLR1_START; if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_MODE_SELECT) != GXHT30_OK) return GXHT30_ERR_TIMEOUT; I2C1->DATAR = (addr << 1) | 0x01; if (_gxht30_wait_event(GXHT30_I2C_EVT_MASTER_RECEIVER_MODE) != GXHT30_OK) return GXHT30_ERR_TIMEOUT; if ((err = _gxht30_i2c_read(rx_data, 3)) != GXHT30_OK) return err; I2C1->CTLR1 |= I2C_CTLR1_STOP; if (!_gxht30_crc8_check(rx_data[0], rx_data[1], rx_data[2])) return GXHT30_ERR_CRC; uint16_t raw = (rx_data[0] << 8) | rx_data[1]; status->raw_status = raw; status->alert_pending = (raw >> 15) & 0x01; status->heater_on = (raw >> 13) & 0x01; status->humidity_alert = (raw >> 11) & 0x01; status->temperature_alert = (raw >> 10) & 0x01; status->reset_detected = (raw >> 4) & 0x01; status->command_status = (raw >> 1) & 0x01; status->crc_status = raw & 0x01; return GXHT30_OK; } static inline uint8_t gxht30_soft_reset(uint8_t addr) { return gxht30_send_command(addr, GXHT30_CMD_SOFT_RESET_MSB, GXHT30_CMD_SOFT_RESET_LSB); } static inline uint8_t gxht30_heater_on(uint8_t addr) { return gxht30_send_command(addr, GXHT30_CMD_HEATER_ON_MSB, GXHT30_CMD_HEATER_ON_LSB); } static inline uint8_t gxht30_heater_off(uint8_t addr) { return gxht30_send_command(addr, GXHT30_CMD_HEATER_OFF_MSB, GXHT30_CMD_HEATER_OFF_LSB); } static inline uint8_t gxht30_clear_status(uint8_t addr) { return gxht30_send_command(addr, GXHT30_CMD_CLEAR_STATUS_MSB, GXHT30_CMD_CLEAR_STATUS_LSB); } #endif // _GXHT30_CH32_HW_I2C_H