risc-v/gd32vw55x: fix I2C master and add gd32vw553k-start sht3x example

The GD32VW55x I2C master (the STM32-style "v2" IP) never completed a real
transfer. Two bugs:

1. Wrong kernel clock. The protocol state machine is clocked by the I2C
   kernel clock selected in RCU_CFG1.I2C0SEL, not by the APB1 bus clock that
   only feeds the register interface. The driver left it at the APB1 default
   and computed TIMING for PCLK1 (~80 MHz); the resulting prescaled period is
   so short that the SDADEL/SCLDEL setup and hold times fall below the
   analog-filter minimum of the IP, so the master latches START but never
   drives SCL (STAT stuck with BUSY set). Route I2C0 to IRC16M (16 MHz) and
   use it as clk_freq, and never let the prescaler drop below the value that
   keeps the prescaled clock at/under 4 MHz (250 ns) so SDADEL/SCLDEL stay in
   spec. This matches the vendor BSP (IRC16M, PSC=3).

2. Transfer timeout was zero. CONFIG_GD32VW55X_I2C_TIMEOTICKS has a Kconfig
   default of 0 ("override when non-zero"), but the driver derived the timeout
   with a plain #ifndef, which never triggers because the symbol is always
   defined. The polled wait loop therefore ran a single iteration and gave
   up. Address probes (NACK on the first pass) still worked and masked it;
   only multi-byte transfers exercised the loop. Honour the "0 means derive
   from seconds/milliseconds" contract.

Also make the interrupt wait immune to the ISR completing between startmsg()
and the wait (do not clobber a posted DONE), and drop a redundant cast.

To exercise this on hardware, add an "sht3x" configuration to the
gd32vw553k-start: the nsh base plus I2C0, the i2ctool and the Sensirion SHT3x
temperature/humidity driver, registered as /dev/i2c0 and /dev/temp0. I2C0 is
routed to PA2 (SCL) / PA3 (SDA) on AF4, the pins broken out on the J1 header
(datasheet Table 2-5); when SPI is enabled it claims PA2, so I2C0 falls back
to PB0/PB1, whose SDA pin is not broken out (the periph case).

Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
This commit is contained in:
Jorge Guzman 2026-07-24 14:11:36 -03:00 committed by CeDeROM
parent f0e4f61add
commit 30735584a4
5 changed files with 212 additions and 30 deletions

View file

@ -0,0 +1,57 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_ARCH_LEDS is not set
CONFIG_ARCH="risc-v"
CONFIG_ARCH_BOARD="gd32vw553k-start"
CONFIG_ARCH_BOARD_GD32VW553K_START=y
CONFIG_ARCH_CHIP="gd32vw55x"
CONFIG_ARCH_CHIP_GD32VW553KM=y
CONFIG_ARCH_CHIP_GD32VW55X=y
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_RISCV=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARDCTL_RESET=y
CONFIG_BOARD_LOOPSPERMSEC=16000
CONFIG_BUILTIN=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_FULLOPT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_EXAMPLES_SHT3X=y
CONFIG_FS_PROCFS=y
CONFIG_GD32VW55X_I2C0=y
CONFIG_GD32VW55X_UART2=y
CONFIG_I2C_POLLED=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=3072
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=64
CONFIG_NSH_READLINE=y
CONFIG_NSH_STRERROR=y
CONFIG_RAM_SIZE=294400
CONFIG_RAM_START=0x20000200
CONFIG_RAW_BINARY=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_WAITPID=y
CONFIG_SENSORS=y
CONFIG_SENSORS_SHT3X=y
CONFIG_STACK_COLORATION=y
CONFIG_START_DAY=13
CONFIG_START_MONTH=7
CONFIG_START_YEAR=2026
CONFIG_SYSTEM_I2CTOOL=y
CONFIG_SYSTEM_NSH=y
CONFIG_TASK_NAME_SIZE=12
CONFIG_UART2_RXBUFSIZE=128
CONFIG_UART2_SERIAL_CONSOLE=y
CONFIG_UART2_TXBUFSIZE=128

View file

@ -73,9 +73,21 @@
# define GPIO_SPI_MOSI GPIO_SPI_MOSI_1 /* PA0, AF5 */
#endif
/* I2C0 uses PA2 (SCL) / PA3 (SDA) on AF4, the pins broken out on the J1
* header (datasheet Table 2-5), so a sensor such as the SHT3x can be wired
* there directly. When SPI is enabled it claims PA2 (SPI SCK), so I2C0
* falls back to PB0/PB1 (AF6); PB1 (SDA) is not broken out on J1/J2, so that
* pairing only registers the bus (the periph case).
*/
#ifdef CONFIG_GD32VW55X_I2C0
# define GPIO_I2C0_SCL GPIO_I2C0_SCL_3 /* PB0, AF6 */
# define GPIO_I2C0_SDA GPIO_I2C0_SDA_3 /* PB1, AF6 */
# ifdef CONFIG_GD32VW55X_SPI
# define GPIO_I2C0_SCL GPIO_I2C0_SCL_3 /* PB0, AF6 */
# define GPIO_I2C0_SDA GPIO_I2C0_SDA_3 /* PB1, AF6 (not on a header) */
# else
# define GPIO_I2C0_SCL GPIO_I2C0_SCL_1 /* PA2, AF4 (J1) */
# define GPIO_I2C0_SDA GPIO_I2C0_SDA_1 /* PA3, AF4 (J1) */
# endif
#endif
#ifdef CONFIG_GD32VW55X_I2C1

View file

@ -49,6 +49,9 @@
#ifdef CONFIG_USERLED_LOWER
# include <nuttx/leds/userled.h>
#endif
#ifdef CONFIG_SENSORS_SHT3X
# include <nuttx/sensors/sht3x.h>
#endif
#ifdef CONFIG_MTD_PROGMEM
# include <nuttx/mtd/mtd.h>
#endif
@ -142,13 +145,29 @@ int gd32_bringup(void)
{
ferr("ERROR: failed to initialize I2C0\n");
}
#ifdef CONFIG_I2C_DRIVER
else if (i2c_register(i2c, 0) < 0)
else
{
ferr("ERROR: failed to register /dev/i2c0\n");
gd32_i2cbus_uninitialize(i2c);
}
#ifdef CONFIG_I2C_DRIVER
/* Expose the bus as /dev/i2c0 for the i2ctool (i2c dev/get/set...) */
if (i2c_register(i2c, 0) < 0)
{
ferr("ERROR: failed to register /dev/i2c0\n");
}
#endif
#ifdef CONFIG_SENSORS_SHT3X
/* Sensirion SHT3x on I2C0 -> /dev/temp0. The default I2C address is
* 0x44 (0x45 with ADDR tied high); confirm it first with
* "i2c dev 0x44 0x45" from NSH.
*/
if (sht3x_register("/dev/temp0", i2c, 0x44) < 0)
{
ferr("ERROR: failed to register SHT3x at 0x44 on I2C0\n");
}
#endif
}
#endif
#ifdef CONFIG_GD32VW55X_SPI