gxht30
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -41,6 +41,7 @@
|
||||
"string": "c",
|
||||
"atomic": "c",
|
||||
"__bit_reference": "c",
|
||||
"err.h": "c"
|
||||
"err.h": "c",
|
||||
"httpd.h": "c"
|
||||
}
|
||||
}
|
||||
295
inc/gxht30_hw_i2c.h
Normal file
295
inc/gxht30_hw_i2c.h
Normal file
@@ -0,0 +1,295 @@
|
||||
#ifndef _GXHT30_CH32_HW_I2C_H
|
||||
#define _GXHT30_CH32_HW_I2C_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
72
main.c
72
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) {
|
||||
|
||||
Reference in New Issue
Block a user