CH58x I²C Guide: Hardware I²C, Software I²C and SHT40 Examples

To read an SHT40 from a CH58x controller, establish the 3.3 V I²C bus, send the one-byte 0xFD command, finish the write transaction, wait for conversion, read six bytes, check both CRC bytes, and only then convert temperature and humidity. SHT20 requires a separate command and timing path.

Indoor ventilation monitor with enlarged controller board, separate SHT40 sensor PCB behind the vent, and a labeled SHT40 component inset
The enlarged controller board sits beside a vented sensor compartment; the SHT40 inset points to the separate sensing PCB.
Fast distinction: The SHT40 uses a 7-bit address of 0x44 for the device discussed here. The source CH58x library example passes 0x88 to its address function because that call expects the left-shifted address. Check the installed EVT API before copying that value.

1. Establish the electrical and firmware baseline

The SHT40 needs SDA and SCL pull-ups to the bus logic rail, a common ground, and a local supply capacitor. The typical circuit below shows 10 kΩ pull-ups and 100 nF decoupling; 10 kΩ is a reference-circuit value, not a guarantee at 400 kHz on a capacitive cable or a multi-board bus. Check total bus capacitance, effective pull-up resistance and rise time on the assembled board.

Sensirion SHT40 typical I2C application circuit and pseudocode
SHT40 application circuit with SDA and SCL pull-ups and a 100 nF supply capacitor. Select pull-up values for the installed bus.
What to verify before running code
ItemCH585 example / SHT40 requirementPractical check
Power and logicSHT40 VDD: 1.08–3.6 V; use pull-ups to the sensor-compatible logic rail.Measure VDD and the idle SDA/SCL high level at the sensor, not only at the MCU.
PinsThe supplied CH585 example uses PB12/SDA and PB13/SCL; its remap path uses PB20/SDA and PB21/SCL.Match the installed CH58x variant, board schematic and EVT pin-remap setting.
AddressSHT40: 0x44 as a 7-bit address; the source SDK example shifts it to 0x88 for one specific call.Confirm whether your API accepts 7-bit or already-shifted addresses.
Bus rateThe source initializes 100 kHz; standard and fast modes have different rise-time budgets.Start at 100 kHz, then inspect edges before increasing speed.
/* CH585 source-project initialization path; confirm names in your EVT. */
#define SHT_MODE 40
/* Leave USE_SOFT undefined for the source hardware-I2C path. */
/* #define USE_SOFT TRUE */
GPIOB_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13, GPIO_ModeIN_PU);
I2C_Init(I2C_Mode_I2C, 100000, I2C_DutyCycle_16_9,
         I2C_Ack_Enable, I2C_AckAddr_7bit, 0);

/* The source passes this shifted form to I2C_Send7bitAddress(). */
#define SHT40_ADDR_FOR_SOURCE_API (0x44u << 1)

External resistors remain necessary when the MCU's internal pull-ups are too weak for the chosen bus capacitance and speed. Multiple breakout boards may already contain pull-ups; their parallel combination changes the effective resistance.

2. Read one SHT40 measurement in the correct order

The SHT40 high-repeatability command is one byte, 0xFD. Its response is exactly six bytes: temperature MSB, temperature LSB, temperature CRC, humidity MSB, humidity LSB, humidity CRC. The current Sensirion SHT4x datasheet gives a maximum high-repeatability measurement duration of 8.3 ms; its quick-start example waits 10 ms. Those are sensor timing figures, not a timeout for a stalled CH58x peripheral.

SHT40 I2C readout flow: write 0xFD, stop, wait, read six bytes, stop, validate both CRC bytes, then convert
SHT40 transaction and validation order. The bus transaction ends before CRC checks and value conversion; reject the frame if either CRC fails.
  1. Write: START, address 0x44 with write direction, send 0xFD, then STOP.
  2. Wait: allow the sensor to finish conversion. If read-address polling is used instead of a fixed delay, a busy SHT40 can NACK the read header.
  3. Read: START, address 0x44 with read direction, receive six bytes, NACK the final byte, then STOP.
  4. Validate: independently check each two-byte data word against its following CRC byte. Reject the complete frame if either check fails.
  5. Convert: apply the SHT4x equations only to a valid frame. Clamp RH to 0–100% only if the application requires a display-range value; retain raw data for diagnostics.

An I²C ACK confirms that something answered at the address; it does not prove a valid SHT40 measurement. A plausible temperature also cannot substitute for CRC validation. The sensor does not stretch the clock during its measurement, and its measurement data is consumed after the first acknowledged read header. If a read fails, issue a new measurement command rather than assuming the old sample is still available. Sensirion SHT4x datasheet, §§4.1–4.6.

Source I2C waveform with START, data clocks, ninth-clock ACK and STOP
I²C timing: SDA changes while SCL is high at START and STOP; the receiver drives ACK on the ninth clock.

3. Put the protocol into CH58x firmware

The code below keeps the SHT40 command, response order, CRC and conversion separate from the installed CH58x EVT. It is a portable protocol core, not a complete CH585 firmware project. Connect its three bus callbacks to the hardware-I²C or software-I²C implementation actually used by your project. The write callback must emit STOP; the read callback must NACK the sixth byte and emit STOP. Both must return on NACK or timeout rather than waiting indefinitely for an event flag.

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/* Each adapter call must finish its own I2C transaction with STOP.
   read() must fill all six bytes, NACK byte 6 before STOP,
   and both calls need a timeout. */
typedef struct {
    bool (*write)(uint8_t address7, const uint8_t *data,
                  size_t length, uint32_t timeout_ms);
    bool (*read)(uint8_t address7, uint8_t *data,
                 size_t length, uint32_t timeout_ms);
    void (*delay_ms)(uint32_t time_ms);
} sht40_bus_t;

typedef struct { float temperature_c; float humidity_rh; } sht40_data_t;
typedef enum { SHT40_OK, SHT40_ARGUMENT, SHT40_WRITE,
               SHT40_READ, SHT40_CRC } sht40_result_t;

static uint8_t sht4x_crc8(const uint8_t data[2])
{
    uint8_t crc = 0xFF;
    for (unsigned i = 0; i < 2; ++i) {
        crc ^= data[i];
        for (unsigned bit = 0; bit < 8; ++bit)
            crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x31)
                               : (uint8_t)(crc << 1);
    }
    return crc;
}

sht40_result_t sht40_measure(const sht40_bus_t *bus, sht40_data_t *out)
{
    if (!bus || !out || !bus->write || !bus->read || !bus->delay_ms)
        return SHT40_ARGUMENT;

    const uint8_t command = 0xFD;       /* high repeatability */
    uint8_t rx[6];                      /* T MSB, T LSB, T CRC,
                                         RH MSB, RH LSB, RH CRC */
    if (!bus->write(0x44, &command, 1, 20)) return SHT40_WRITE;
    bus->delay_ms(10);                  /* 8.3 ms max in datasheet */
    if (!bus->read(0x44, rx, 6, 20))     return SHT40_READ;

    if (sht4x_crc8(&rx[0]) != rx[2] ||
        sht4x_crc8(&rx[3]) != rx[5])     return SHT40_CRC;

    uint16_t t = ((uint16_t)rx[0] << 8) | rx[1];
    uint16_t h = ((uint16_t)rx[3] << 8) | rx[4];
    out->temperature_c = -45.0f + 175.0f * t / 65535.0f;
    out->humidity_rh = -6.0f + 125.0f * h / 65535.0f;
    return SHT40_OK;
}

/* Datasheet CRC test vector: sht4x_crc8({0xBE, 0xEF}) == 0x92. */

Use CRC8(0xBE, 0xEF) = 0x92 as a unit check for the CRC routine. The SHT40 conversion equations are T = −45 + 175 × rawT / 65535 °C and RH = −6 + 125 × rawRH / 65535 %RH. Both CRC bytes must pass before either reported value is accepted. Sensirion SHT4x datasheet, Tables 7–8.

SHT40 temperature and humidity serial output captured in the supplied article
SHT40 serial output: temperature and relative-humidity readings.

In the supplied source's UART printout, the temperature and humidity lines demonstrate the output format. During your own bring-up, log the six raw bytes and the specific failure stage (write NACK, read NACK/timeout, temperature CRC or humidity CRC); that record is more diagnostic than logging converted values alone.

4. Keep the SHT20 path separate

An SHT20 can use the same two-wire physical bus, but it is not an SHT40 command-compatible device. The SHT20 uses 7-bit address 0x40 and separate temperature/humidity commands. Its hold-master mode can hold SCL low while measuring; the SHT40 instead NACKs a premature read header. At the SHT20 default resolution, the datasheet lists maximum durations of 85 ms for temperature and 29 ms for RH, so reusing the SHT40's 10 ms wait would be wrong. Sensirion SHT20 datasheet, §§5.3–5.4.

Different driver decisions for the two sensors
DecisionSHT40SHT20
7-bit address0x44 for this part0x40
Measurement command0xFD returns T and RH together0xE3 T / 0xE5 RH in hold-master mode
ResponseSix bytes: T+CRC, RH+CRCTwo data bytes and optional checksum for each measurement
Waiting behaviorRead header NACK while busy; no clock stretchingHold-master mode may hold SCL low during conversion
ConversionDivide raw values by 65535Clear the two status bits; divide by 65536
/* SHT20 conversion only; CRC and I2C handling remain separate. */
uint16_t rh_ticks = (uint16_t)(raw_rh_word & 0xFFFCu);
uint16_t t_ticks  = (uint16_t)(raw_t_word  & 0xFFFCu);
float humidity_rh = -6.0f + 125.0f * rh_ticks / 65536.0f;
float temperature_c = -46.85f + 175.72f * t_ticks / 65536.0f;

Use a separate result for each SHT20 command, as shown above. If the controller cannot support hold-master clock stretching, review the datasheet's no-hold mode timing rather than only changing the command byte. The SHT20 user register must be read before changing the heater bit so reserved bits are preserved; leave the heater off for ordinary ambient comparisons. Sensirion SHT20 datasheet, §§5.6 and 6.

SHT20 temperature and humidity serial output captured in the supplied article
SHT20 serial output: temperature and relative-humidity readings.

5. Hardware I²C or software I²C?

Laptop screen with readable CH58x SHT40 project and half of the keyboard visible beside a breadboard controller and sensor breakout
CH58x and SHT40 development workspace: check the selected driver files before choosing hardware or software I²C.

The supplied project tree contains separate SHT20, SHT40 and bit-banged I²C files. Choose one bus implementation before tracing a failure: hardware-I²C event flags and GPIO software timing fail in different ways. In the supplied example, defining USE_SOFT selects the software path, while commenting out that definition selects hardware I²C. Setting the macro to FALSE may still select software if the source uses #ifdef USE_SOFT; inspect the installed project's conditional compilation before building.

Source project file list with SHT20, SHT40 and software I2C files
The source project separates the SHT20, SHT40 and software I²C modules.
Source project SHT mode and software I2C macro definitions
Source configuration: SHT_MODE selects the sensor example; USE_SOFT selects software I²C. Confirm these names in the installed project.

For software I²C, release SDA and SCL as open-drain signals, sample SDA while SCL is high, and NACK the final SHT40 response byte. The receive excerpt below expresses the source project's byte-level logic; its GPIO helpers must supply the line release and timing behavior. Verify that the chosen pins are available on the actual CH58x package and board.

/* Source-style bit-banged read; the surrounding GPIO helpers must
   release SDA for input and enforce timing at the chosen bus speed. */
uint8_t IIC_ReadByte(uint8_t ack)
{
    uint8_t value = 0;
    IIC_SDA_H();                         /* release open-drain SDA */
    for (unsigned bit = 0; bit < 8; ++bit) {
        IIC_SCL_L(); IIC_Delay();
        IIC_SCL_H(); IIC_Delay();
        value = (uint8_t)((value << 1) | (SDA_read() ? 1u : 0u));
    }
    IIC_SCL_L();
    if (ack) IIC_SDA_L(); else IIC_SDA_H();
    IIC_Delay(); IIC_SCL_H(); IIC_Delay(); IIC_SCL_L();
    IIC_SDA_H();                         /* release SDA after ACK/NACK */
    return value;
}

Neither a software delay loop nor a hardware flag wait should be unbounded. Set a timeout around START, address ACK, transfer complete and RXNE events. If SDA stays low before START, look for a held bus or wiring fault; do not repeatedly issue START without diagnosing the line.

6. Verify the result and choose the next check

Engineer using oscilloscope probes and a logic analyzer during I2C sensor prototype development
Development setup for checking SDA/SCL waveforms and bus behavior on the target board.
What a CH58x–SHT40 bring-up result actually tells you
ObservationLikely boundaryNext check
SDA or SCL is low at idleElectrical bus problemMeasure both lines at the sensor; check shorts, pull-ups and supply.
No ACK at addressPower, pin map or address representationCheck VDD/GND, PB12/PB13 or remap, and whether the SDK expects 0x44 or 0x88.
Write ACK, read NACK immediatelyConversion may still be runningCheck write STOP, elapsed time and high-repeatability command byte 0xFD.
Six bytes arrive; CRC failsByte order, sampling or signal integrityLog all six bytes; run the 0xBEEF → 0x92 CRC test and inspect waveforms.
CRC passes; converted RH is implausibleFormula, sensor identification or placementConfirm the model, apply the matching equation and compare the sensor's air path with a reference.

Before release, run repeated reads on the selected CH58x board at the intended supply, bus speed and cable length; record NACK, timeout and CRC counts as well as converted values. Keep the MCU adapter's event sequence and ACK/NACK handling matched to the installed EVT release; the SHT40 core above does not define those library internals.

CH58x–SHT40 I²C FAQ

Should I use 0x44 or 0x88 for an SHT40 on a CH585?

The SHT40 device address is 0x44 in 7-bit notation. The cited CH58x source passes 0x88 to an API that expects the address shifted left by one bit. Check the installed EVT function's address convention before changing firmware.

Why does the SHT40 ACK the write but NACK the read?

The sensor can NACK a read header while a measurement is still running. After sending the one-byte 0xFD command, end the write with STOP and allow conversion time; Sensirion's quick-start example waits 10 ms for this high-repeatability command. Also check power, wiring and the address format if the NACK persists.

What must be checked in the six-byte SHT40 response?

The frame contains two temperature bytes and their CRC, followed by two humidity bytes and their CRC. Validate both CRC-8 bytes before converting or accepting either measurement. The CRC polynomial is 0x31 with initialization value 0xFF.

Can the same SHT40 driver read an SHT20?

No. The SHT20 uses 7-bit address 0x40, different temperature and humidity commands, different waiting behavior and different conversion equations. The physical I²C bus may be shared, but the device protocol and validation path must remain separate.

Technical references

Sensirion SHT4x datasheet · Sensirion SHT20 datasheet · CH58x I²C source example

© 2023 All Rights Reserved. www.nyfea.com Terms of Use | Privacy Policy