diff --git a/.vscode/settings.json b/.vscode/settings.json index 1a2508a..ac2942e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,6 +41,7 @@ "string": "c", "atomic": "c", "__bit_reference": "c", - "err.h": "c" + "err.h": "c", + "httpd.h": "c" } } \ No newline at end of file diff --git a/inc/gxht30_hw_i2c.h b/inc/gxht30_hw_i2c.h new file mode 100644 index 0000000..80c840a --- /dev/null +++ b/inc/gxht30_hw_i2c.h @@ -0,0 +1,295 @@ +#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 \ No newline at end of file diff --git a/main.c b/main.c index ad42f19..656b296 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,7 @@ #include "ch32fun.h" #include "ch32v20xhw.h" #include "ethernetif.h" +#include "gxht30_hw_i2c.h" #include "lwip/apps/httpd.h" #include "lwip/dhcp.h" #include "lwip/init.h" @@ -25,6 +26,8 @@ #define PLL_MULTIPLIER 15 #define STATS_PRINT_INTERVAL_MS 10000 +#define SENSOR_READ_INTERVAL_MS 5000 +#define STATUS_READ_INTERVAL_MS 30000 struct netif g_netif; static volatile int g_httpd_is_initialized = 0; @@ -178,10 +181,17 @@ void ethernetif_print_stats(void) { int main() { SystemInit(); set_sysclk_to_120mhz_from_hse(); + print_clock_registers(); + systick_init(); led_init(); lwip_stack_init(); + gxht30_i2c_init(); + + gxht30_soft_reset(GXHT30_I2C_ADDR_DEFAULT); + Delay_Ms(10); + uint32_t last_led_toggle_time = 0; uint32_t last_link_poll_time = 0; #if LWIP_STATS @@ -189,6 +199,14 @@ int main() { #endif int led_state = 0; + uint32_t last_sensor_read_time = 0; + uint32_t last_status_read_time = 0; + + GXHT30_Data sensor_data = {0}; + GXHT30_Status sensor_status = {0}; + + printf("GXHT30 Sensor initialized\n"); + while (1) { ethernetif_input(&g_netif); sys_check_timeouts(); @@ -205,6 +223,60 @@ int main() { } #endif + // Read sensor data periodically + if (millis() - last_sensor_read_time > SENSOR_READ_INTERVAL_MS) { + if (gxht30_read_data(GXHT30_I2C_ADDR_DEFAULT, &sensor_data) == + GXHT30_OK) { + int16_t temp_int = (int16_t)(sensor_data.temperature * 100); + int16_t hum_int = (int16_t)(sensor_data.humidity * 100); + + int16_t temp_whole = temp_int / 100; + int16_t temp_decimal = temp_int % 100; + if (temp_decimal < 0) temp_decimal = -temp_decimal; + + int16_t hum_whole = hum_int / 100; + int16_t hum_decimal = hum_int % 100; + + printf("Temperature: %d.%02d C | Humidity: %d.%02d %%\n", temp_whole, + temp_decimal, hum_whole, hum_decimal); + } else { + printf("Sensor error: %d\n", sensor_data.error); + } + last_sensor_read_time = millis(); + } + + // Read status register periodically + if (millis() - last_status_read_time > STATUS_READ_INTERVAL_MS) { + if (gxht30_read_status(GXHT30_I2C_ADDR_DEFAULT, &sensor_status) == + GXHT30_OK) { + printf("Status: "); + + if (sensor_status.alert_pending) printf("ALERT "); + if (sensor_status.heater_on) printf("HEATER_ON "); + if (sensor_status.humidity_alert) printf("HUM_ALERT "); + if (sensor_status.temperature_alert) printf("TEMP_ALERT "); + if (sensor_status.reset_detected) printf("RESET "); + if (sensor_status.command_status) printf("CMD_ERR "); + if (sensor_status.crc_status) printf("CRC_ERR "); + + if (sensor_status.raw_status == 0x0010) { + printf("OK (reset detected on startup)"); + } else if (sensor_status.raw_status == 0x0000) { + printf("OK"); + } + + printf("\n"); + + // Clear reset flag after first read if you want + // if (sensor_status.reset_detected) { + // gxht30_clear_status(GXHT30_I2C_ADDR_DEFAULT); + // } + } else { + printf("Status read error\n"); + } + last_status_read_time = millis(); + } + // uint32_t now = millis(); // if (now - last_led_toggle_time > LED_TOGGLE_INTERVAL_MS) { // if (led_state) {