This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 45f7ecdebea boards/gd-xd-w515-eval: fix the pin map from the board
schematic
45f7ecdebea is described below
commit 45f7ecdebead0fc1eda71de630ea3f75b30eb951
Author: Jorge Guzman <[email protected]>
AuthorDate: Fri Oct 23 21:15:27 2026 -0300
boards/gd-xd-w515-eval: fix the pin map from the board schematic
The board schematic showed that part of the pin map found by probing
the board was wrong.
Board:
- LEDs are PC5, PC4, PB0 and PB1. PC3 is the CHRG_EN input of the
GD30BC2416 and is now driven high at boot.
- The LCD backlight is PC14 (active high).
- The heater coil is PB7, active low behind a P-MOSFET; /dev/gpio0
inverts it so 1 still means on.
- The charger IRQ reaches PB2 through a PNP transistor: rising edge.
- The ISET resistor is 30 kOhm: the charge current is 333 mA.
gd30bc2416:
- Without a battery the charger acknowledges only in time windows:
retry a NACK for up to 1 s.
- The start of a charge raises no IRQ: after an IRQ with the USB
present, poll the state for up to 2 s.
gd32w51x:
- ADC: the W51x has no DRES field. Remove it and
GD32W51X_ADC_RESOLUTION; the old code set interrupt enable bits.
- tzinit: explain why SCFG must be cleared.
Documentation: new pin table, analog inputs, charger notes, the leds
example, and flash with "program" instead of gdb "load".
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Jorge Guzman <[email protected]>
---
.../arm/gd32w51x/boards/gd-xd-w515-eval/index.rst | 63 +++--
arch/arm/src/gd32w51x/Kconfig | 7 -
arch/arm/src/gd32w51x/gd32w51x_adc.c | 14 +-
arch/arm/src/gd32w51x/gd32w51x_tzinit.c | 7 +
arch/arm/src/gd32w51x/hardware/gd32w51x_adc.h | 8 +-
.../arm/gd32w51x/gd-xd-w515-eval/include/board.h | 46 ++--
.../gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h | 7 +-
.../arm/gd32w51x/gd-xd-w515-eval/src/gd32_boot.c | 4 +
.../gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c | 13 +-
.../arm/gd32w51x/gd-xd-w515-eval/src/gd32_gpio.c | 6 +-
boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_lcd.c | 8 +-
.../gd32w51x/gd-xd-w515-eval/src/gd32_userleds.c | 2 +-
drivers/power/battery/gd30bc2416.c | 267 +++++++++++++++------
13 files changed, 301 insertions(+), 151 deletions(-)
diff --git
a/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst
b/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst
index 2c002caf992..b3558ee1d9b 100644
--- a/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst
+++ b/Documentation/platforms/arm/gd32w51x/boards/gd-xd-w515-eval/index.rst
@@ -20,9 +20,10 @@ Features
========
- GD32W515PIQ6 MCU, 25 MHz crystal, no 32 kHz crystal
-- 0.96" 160x80 IPS LCD (ST7735S, SPI) with PWM backlight
+- 0.96" 160x80 IPS LCD (ST7735S, SPI) with a switched backlight
- GSL6157 capacitive fingerprint sensor (SPI)
-- Capacitive touch key (TSI), vibration motor, four LEDs
+- Capacitive touch key (TSI), vibration motor, heater coil, microphone,
+ four LEDs
- GD25Q128E 16 MB QSPI NOR flash (U6)
- GD30BC2416 battery charger, GD30LD1002 LDO, LiPo connector
- On-board GD-Link (CMSIS-DAP) on the mini-USB connector
@@ -34,9 +35,8 @@ libraries only support 26 MHz and 40 MHz crystals.
Pin mapping
===========
-No schematic of this board is public. The pins below were read back from
-the GPIO setup of the factory firmware and checked on the hardware where
-noted.
+The pins below follow the board schematic (GD-xD-W515-EVAL V1.0,
+GigaDevice, 2023-05).
============== ==================================== ==========================
Function Pins Notes
@@ -44,25 +44,32 @@ Function Pins Notes
Console PB10 (TX), PB11 (RX), AF7 USART2, to the CH340E
LCD (SPI0) PA11 (SCK), PA9 (MOSI), AF0 Write only
LCD control PA10 (D/C), PB13 (CS), PC6 (RESET)
-LCD backlight PB0 On with the display
+LCD backlight PC14 (active high) On with the display
Motor PA0, TIMER1_CH0 (AF1) /dev/pwm0 channel 1
-LEDs PB1, PC4, PC5, PC3 LED1..LED4, active high
+LEDs PC5, PC4, PB0, PB1 LED1..LED4, active high
I2C0 PB15 (SCL, AF4), PA8 (SDA, AF5) GD30BC2416 charger
Touch key PB12 (sampling), PB14 (channel) TSI, AF4, /dev/buttons
-Heater PC14 /dev/gpio0 (output)
+Heater coil PB7 (COIL_EN, active low) /dev/gpio0 (output)
Fingerprint PC7 (SCK), PB6 (MISO), PB9 (MOSI) SPI1, AF5 (no driver yet)
Fingerprint PB8 (CS), PB5 (SHUTDOWN) Outputs
Fingerprint PA12 (IRQ) /dev/gpio2 (interrupt)
-USB detect PC1 (high with USB power) /dev/gpio1 (interrupt)
+USB detect PC1 (micro-USB VBUS / 2) /dev/gpio1 (interrupt)
Charger IRQ PB2 GD30BC2416 driver (*)
+Charger enable PC3 (CHRG_EN, active high) Held high
Analog inputs PA1, PA2, PA3, PC2 ADC channels 1/2/3/6 (*)
+GD-Link VBUS PC0 (mini-USB VBUS / 2) ADC channel 4 (unused)
+Microphone PA15 (MIC_OUT) Digital input
QSPI flash PA4 (SCK), PA5 (CS) GD25Q128E, AF3
QSPI flash PA6, PA7, PB3, PB4 IO0..IO3, AF3
============== ==================================== ==========================
-(*) The analog inputs are configured by the factory firmware, but none of
-them follows the battery voltage: the battery is only measured by the
-charger. PB2 is ``/dev/gpio3`` when the charger driver is disabled.
+(*) The analog inputs are the NTC thermistors of the heater coil (PA1),
+of the battery (PA2) and of the USB connector (PA3), each a 10 kOhm NTC
+with a 10 kOhm pull-up to 3.3 V, and the battery current (PC2): an
+INA181A1 (gain 20) on a 20 mOhm shunt with a 1.8 V offset, so
+``I = (V - 1.8) / 0.4`` A, negative while the battery charges. The battery
+voltage is only measured by the charger. PB2 is ``/dev/gpio3`` when the
+charger driver is disabled.
Fingerprint sensor
==================
@@ -96,17 +103,23 @@ The GD30BC2416 charger is on I2C0. It has no register
address byte: each
and fault registers.
- ``BATIOC_GET_VOLTAGE``: the battery voltage, in mV, from the charger ADC.
- ``BATIOC_VOLTAGE``: the charge voltage (4100, 4200, 4300, 4350 or 4400 mV).
-- ``BATIOC_CURRENT``: 100/75/50/25 % of the ISET current (unknown on this
- board, the 1.5 A maximum of the charger is assumed).
+- ``BATIOC_CURRENT``: 100/75/50/25 % of the ISET current, 333 mA on this
+ board (R34 = 30 kOhm).
- ``BATIOC_OPERATE``: ``BATIO_OPRTN_CHARGE`` and ``BATIO_OPRTN_BOOST``.
The charger powers up with the charge disabled, and returns to its reset
configuration when the USB power is removed. The driver keeps the requested
charge voltage, charge current and enables, and writes them again on the
IRQ of the charger (PB2, pulsed on each event) and before each status read;
-the IRQ also wakes up ``poll()`` on ``/dev/batt0``. Enable
-``CONFIG_GD30BC2416_CHARGE`` to start the charge at boot. PC1
-(``/dev/gpio1``) follows the USB power.
+the IRQ also wakes up ``poll()`` on ``/dev/batt0``. The start of the
+charge raises no IRQ, so after an IRQ with the USB power present the driver
+polls the state for up to 2 s. Enable ``CONFIG_GD30BC2416_CHARGE`` to start
+the charge at boot. PC1 (``/dev/gpio1``) follows the USB power, and the
+board holds CHRG_EN (PC3) high.
+
+Without a battery, powered by the USB only, the charger answers on I2C in
+time windows (about 300 ms every 880 ms): the driver retries a NACKed
+transfer for up to 1 s.
.. warning:: The charger is for a single Li-ion/LiPo cell. Do not connect
a primary cell (Li-SOCl2, for instance) with the charge enabled.
@@ -136,6 +149,10 @@ secure alias when TrustZone is enabled, so program the
image there::
openocd -f target/openocd_gdlink_gd32w51x.cfg -c init -c "reset halt" \
-c "program nuttx.bin 0x0c000000 verify" -c "reset run" -c shutdown
+Do not use the ``load`` command of gdb: it writes the ELF image at
+0x08000000, where the flash driver has no bank, and leaves an inconsistent
+image. Program the binary as above, then attach gdb without ``load``.
+
.. note:: With TrustZone enabled, the secure debug port reads the
non-secure flash pages as zero. Back up the factory firmware only after
all the pages are marked secure.
@@ -191,10 +208,16 @@ The IPS panel needs the color inversion and the BGR order
of the ST7735S
26/1 pixel offset (``CONFIG_LCD_ST7735_XOFFSET``/``YOFFSET``) for its
80x160 window.
-Light the four LEDs, then switch them off::
+Blink the four LEDs (PC5, PC4, PB0, PB1) in sequence::
- nsh> printf \x0000000f > /dev/userleds
- nsh> printf \x00000000 > /dev/userleds
+ nsh> leds
+ leds_main: Starting the led_daemon
+ leds_main: led_daemon started
+ led_daemon (pid# 6): Running
+ led_daemon: Opening /dev/userleds
+ led_daemon: Supported LEDs 0x0f
+ led_daemon: LED set 0x01
+ led_daemon: LED set 0x02
Run the vibration motor for 2 seconds::
diff --git a/arch/arm/src/gd32w51x/Kconfig b/arch/arm/src/gd32w51x/Kconfig
index d1ff4294cfa..0614fbf3ed3 100644
--- a/arch/arm/src/gd32w51x/Kconfig
+++ b/arch/arm/src/gd32w51x/Kconfig
@@ -273,13 +273,6 @@ endmenu # I2C Configuration
menu "ADC Configuration"
depends on GD32W51X_ADC
-config GD32W51X_ADC_RESOLUTION
- int "ADC resolution (bits)"
- default 12
- range 6 12
- ---help---
- Valid values are 6, 8, 10 and 12.
-
config GD32W51X_ADC_CLOCK
int "ADC clock (Hz)"
default 11250000
diff --git a/arch/arm/src/gd32w51x/gd32w51x_adc.c
b/arch/arm/src/gd32w51x/gd32w51x_adc.c
index ef35abe16be..ff129fc9721 100644
--- a/arch/arm/src/gd32w51x/gd32w51x_adc.c
+++ b/arch/arm/src/gd32w51x/gd32w51x_adc.c
@@ -70,12 +70,6 @@
# define GD32_ADC_CLKDIV ADC_CCTL_ADCCK_PCLK2_DIV8
#endif
-/* Conversion resolution: 12 bits by default */
-
-#ifndef CONFIG_GD32W51X_ADC_RESOLUTION
-# define CONFIG_GD32W51X_ADC_RESOLUTION ADC_CTL0_DRES_12B
-#endif
-
/* Sampling time used for every channel */
#ifndef CONFIG_GD32W51X_ADC_SAMPLETIME
@@ -377,12 +371,12 @@ static void adc_reset(struct adc_dev_s *dev)
modifyreg32(GD32W51X_ADC_CCTL, ADC_CCTL_ADCCK_MASK, GD32_ADC_CLKDIV);
- /* Scan mode and the requested resolution. The EOC flag is raised at the
- * end of each conversion of the sequence, not only at the end of the
- * sequence.
+ /* Scan mode (the conversions are always 12-bit wide). The EOC flag is
+ * raised at the end of each conversion of the sequence, not only at the
+ * end of the sequence.
*/
- regval = ADC_CTL0_SM | CONFIG_GD32W51X_ADC_RESOLUTION;
+ regval = ADC_CTL0_SM;
putreg32(regval, GD32W51X_ADC_CTL0);
/* Right aligned data, EOC set after each conversion, no external trigger
diff --git a/arch/arm/src/gd32w51x/gd32w51x_tzinit.c
b/arch/arm/src/gd32w51x/gd32w51x_tzinit.c
index 5f1f2fbd15c..80f57d5bd87 100644
--- a/arch/arm/src/gd32w51x/gd32w51x_tzinit.c
+++ b/arch/arm/src/gd32w51x/gd32w51x_tzinit.c
@@ -71,6 +71,13 @@
* non-secure accesses (RCU_SECP_CFG = 0); only the GPIO pins need to be
* released.
*
+ * The secure core drives secure pins normally, but the non-secure
+ * peripherals cannot use them: the ADC reads zero on every secure input
+ * and the alternate functions of the non-secure peripherals do not reach
+ * them. A reset of a GPIO port through the RCU (RCU_AHB1RST) brings its
+ * SCFG register back to 0xffff, so a driver that resets a port must
+ * release its pins again.
+ *
* The SRAM blocks stay secure (TZBMPC): the core accesses them in
* secure state.
*
diff --git a/arch/arm/src/gd32w51x/hardware/gd32w51x_adc.h
b/arch/arm/src/gd32w51x/hardware/gd32w51x_adc.h
index e281caee4c4..4c85bcc5e64 100644
--- a/arch/arm/src/gd32w51x/hardware/gd32w51x_adc.h
+++ b/arch/arm/src/gd32w51x/hardware/gd32w51x_adc.h
@@ -37,7 +37,7 @@
* external/internal channels (0..11). Channel 9 is the temperature sensor
* and channel 10 is the internal reference voltage, both enabled with the
* TSVREN bit of ADC_CCTL. Channel 11 is VBAT / 4, enabled with VBATEN.
- * The conversion resolution is selectable between 12, 10, 8 and 6 bits.
+ * The conversions are always 12-bit wide (there is no DRES field).
*
* The routine (regular) sequence holds up to 9 conversions: ranks 0..5 in
* ADC_RSQ2 and ranks 6..8 in ADC_RSQ1. The inserted sequence holds up to
@@ -130,12 +130,6 @@
#define ADC_CTL0_DISNUM_MASK (7 << ADC_CTL0_DISNUM_SHIFT)
#define ADC_CTL0_IWDEN (1 << 22) /* Inserted watchdog enable */
#define ADC_CTL0_RWDEN (1 << 23) /* Routine watchdog enable */
-#define ADC_CTL0_DRES_SHIFT (24) /* Data resolution */
-#define ADC_CTL0_DRES_MASK (3 << ADC_CTL0_DRES_SHIFT)
-# define ADC_CTL0_DRES_12B (0 << ADC_CTL0_DRES_SHIFT)
-# define ADC_CTL0_DRES_10B (1 << ADC_CTL0_DRES_SHIFT)
-# define ADC_CTL0_DRES_8B (2 << ADC_CTL0_DRES_SHIFT)
-# define ADC_CTL0_DRES_6B (3 << ADC_CTL0_DRES_SHIFT)
#define ADC_CTL0_ROVFIE (1 << 26) /* Overflow int enable */
/* Control register 1 (CTL1) */
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/include/board.h
b/boards/arm/gd32w51x/gd-xd-w515-eval/include/board.h
index c921811f977..2479eedce7a 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/include/board.h
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/include/board.h
@@ -36,13 +36,12 @@
#define BOARD_IRC32K_FREQUENCY 32000 /* 32 kHz RC (FWDGT, RTC) */
#define BOARD_SYSCLK_FREQUENCY 180000000
-/* Pin assignments. There is no public schematic of this board: the pins
- * below were read back from the GPIO setup calls of the factory firmware
- * and checked on the hardware where noted (see the board documentation).
+/* Pin assignments, from the board schematic (GD-xD-W515-EVAL V1.0,
+ * GigaDevice, 2023-05) and checked on the hardware.
*/
-/* LEDs: LED1..LED4, push-pull outputs, active high (the factory firmware
- * drives them low at boot and high to light them).
+/* LEDs: LED1..LED4 of the board schematic, push-pull outputs, active
+ * high. LED3 (PB0) is also TIMER2_CH1.
*/
#define BOARD_NLEDS 4
@@ -54,20 +53,28 @@
#define GPIO_LED1 (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
- GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_B | \
- GPIO_CFG_PIN_1)
+ GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_C | \
+ GPIO_CFG_PIN_5)
#define GPIO_LED2 (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_C | \
GPIO_CFG_PIN_4)
#define GPIO_LED3 (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
- GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_C | \
- GPIO_CFG_PIN_5)
-
+ GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_B | \
+ GPIO_CFG_PIN_0)
#define GPIO_LED4 (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
- GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_C | \
+ GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_B | \
+ GPIO_CFG_PIN_1)
+
+/* CHRG_EN of the GD30BC2416 charger (PC3, active high). It is held high:
+ * the charge is controlled through the I2C registers of the charger.
+ */
+
+#define GPIO_CHRG_EN (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
+ GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
+ GPIO_CFG_OUTPUT_SET | GPIO_CFG_PORT_C | \
GPIO_CFG_PIN_3)
#define BOARD_LED1_BIT (1 << BOARD_LED1)
@@ -145,23 +152,22 @@
#define BOARD_TSI_SAMPLE_PIN 1
#define BOARD_TSI_KEY_PIN 3
-/* PWM: TIMER1_CH0 on PA0 drives the vibration motor. The LCD backlight
- * (PB0, also TIMER2_CH1) is switched on as a plain GPIO with the display.
- */
+/* PWM: TIMER1_CH0 on PA0 drives the vibration motor */
#define GPIO_TIMER1_CH0OUT GPIO_TIMER1_CH0OUT_1
/* GPIOs exposed through /dev/gpioN (CONFIG_DEV_GPIO):
*
- * /dev/gpio0 PC14 output "Heater" high-power port (MOSFET Q1)
+ * /dev/gpio0 PB7 output Heater coil (COIL_EN, P-MOSFET), active
+ * low on the pin: write 1 to switch it on
* /dev/gpio1 PC1 interrupt USB power detection (high with USB)
* /dev/gpio2 PA12 interrupt IRQ of the GSL6157 fingerprint sensor
* /dev/gpio3 PB2 interrupt IRQ of the GD30BC2416 battery charger,
* only without its driver (CONFIG_GD30BC2416)
*
- * The interrupts trigger on both edges. The charger pulses its IRQ for
- * 8 ms on each event (USB plugged or unplugged, charge end, fault); PB2
- * idles low on this board.
+ * The interrupts trigger on both edges. The charger pulses its IRQ low
+ * for 8 ms on each event (USB plugged or unplugged, charge end, fault);
+ * through the PNP Q3, PB2 idles low and goes high during the pulse.
*/
#define BOARD_NGPIOOUT 1
@@ -173,8 +179,8 @@
#define GPIO_HEATER (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
- GPIO_CFG_OUTPUT_RESET | GPIO_CFG_PORT_C | \
- GPIO_CFG_PIN_14)
+ GPIO_CFG_OUTPUT_SET | GPIO_CFG_PORT_B | \
+ GPIO_CFG_PIN_7)
#define GPIO_CHARGER_IRQ (GPIO_CFG_MODE_INPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PORT_B | GPIO_CFG_PIN_2)
#define GPIO_USB_DETECT (GPIO_CFG_MODE_INPUT | GPIO_CFG_PUPD_NONE | \
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
index 7e116b357ea..1c17b32c8e1 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd-xd-w515-eval.h
@@ -34,13 +34,12 @@
* Pre-processor Definitions
****************************************************************************/
-/* GD30BC2416 battery charger on I2C0. The charge current set by its ISET
- * resistor is not known (no schematic): the 1.5 A maximum of the charger
- * is assumed, so that BATIOC_CURRENT never selects more than requested.
+/* GD30BC2416 battery charger on I2C0. The charge current is set by its
+ * ISET resistor, R34 = 30 kOhm: I = 10 / R(kOhm) A = 333 mA.
*/
#define GD32_CHARGER_I2C_FREQUENCY 100000
-#define GD32_CHARGER_ISET_CURRENT 1500
+#define GD32_CHARGER_ISET_CURRENT 333
/****************************************************************************
* Public Function Prototypes
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_boot.c
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_boot.c
index 975a79b430a..d97e72751a2 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_boot.c
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_boot.c
@@ -30,6 +30,7 @@
#include <arch/board/board.h>
#include "gd32w51x.h"
+#include "gd32w51x_gpio.h"
#include "gd-xd-w515-eval.h"
/****************************************************************************
@@ -49,6 +50,9 @@
void gd32w51x_boardinitialize(void)
{
+ /* Keep the charger enabled (CHRG_EN high) */
+
+ gd32_gpio_config(GPIO_CHRG_EN);
}
/****************************************************************************
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
index 7042a19cb8d..991912ccd08 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_bringup.c
@@ -107,8 +107,9 @@ static const struct gd30bc2416_config_s g_charger_config =
* Name: gd32_charger_irq_attach
*
* Description:
- * Attach the IRQ of the GD30BC2416 (PB2). The 8 ms pulse is caught on
- * both edges, whatever its polarity.
+ * Attach the IRQ of the GD30BC2416 (PB2). The 8 ms low pulse of the
+ * charger turns the PNP Q3 on, so PB2 (pulled down by R41) goes high:
+ * the rising edge marks the event.
*
****************************************************************************/
@@ -116,9 +117,7 @@ static int gd32_charger_irq_attach(
FAR const struct gd30bc2416_config_s *config,
xcpt_t isr, FAR void *arg)
{
- bool enable = isr != NULL;
-
- return gd32_gpio_setevent(GPIO_CHARGER_IRQ, enable, enable, false,
+ return gd32_gpio_setevent(GPIO_CHARGER_IRQ, isr != NULL, false, false,
isr, arg);
}
#endif
@@ -153,7 +152,9 @@ int gd32_bringup(void)
struct battery_charger_dev_s *charger;
#endif
#ifdef CONFIG_GD32W51X_ADC
- /* The analog inputs set up by the factory firmware */
+ /* The analog inputs: NTC_COIL (PA1), NTC_Battery (PA2), NTC_USB (PA3)
+ * and the battery current (PC2, ADC_I_VBAT)
+ */
static const uint8_t chanlist[] =
{
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_gpio.c
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_gpio.c
index a21d8ea6289..1073b51931b 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_gpio.c
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_gpio.c
@@ -84,7 +84,7 @@ static const struct gpio_operations_s gpint_ops =
.go_enable = gpint_enable,
};
-/* Outputs: the "Heater" high-power port */
+/* Outputs: the heater coil, switched by a P-MOSFET (active low) */
static const uint32_t g_gpiooutputs[BOARD_NGPIOOUT] =
{
@@ -119,7 +119,7 @@ static int gpout_read(struct gpio_dev_s *dev, bool *value)
DEBUGASSERT(gd32gpio->id < BOARD_NGPIOOUT && value != NULL);
- *value = gd32_gpio_read(g_gpiooutputs[gd32gpio->id]);
+ *value = !gd32_gpio_read(g_gpiooutputs[gd32gpio->id]);
return OK;
}
@@ -129,7 +129,7 @@ static int gpout_write(struct gpio_dev_s *dev, bool value)
DEBUGASSERT(gd32gpio->id < BOARD_NGPIOOUT);
- gd32_gpio_write(g_gpiooutputs[gd32gpio->id], value);
+ gd32_gpio_write(g_gpiooutputs[gd32gpio->id], !value);
return OK;
}
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_lcd.c
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_lcd.c
index e9abb0714f7..e64a1d8ca44 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_lcd.c
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_lcd.c
@@ -45,12 +45,14 @@
* Pre-processor Definitions
****************************************************************************/
-/* The backlight (PB0) is switched fully on with the display */
+/* The backlight (PC14, active high) is switched fully on with the
+ * display
+ */
#define GPIO_LCD_BL (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_2MHZ | \
- GPIO_CFG_OUTPUT_SET | GPIO_CFG_PORT_B | \
- GPIO_CFG_PIN_0)
+ GPIO_CFG_OUTPUT_SET | GPIO_CFG_PORT_C | \
+ GPIO_CFG_PIN_14)
/****************************************************************************
* Private Data
diff --git a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_userleds.c
b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_userleds.c
index d28e97a310b..c0b039e33c8 100644
--- a/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_userleds.c
+++ b/boards/arm/gd32w51x/gd-xd-w515-eval/src/gd32_userleds.c
@@ -41,7 +41,7 @@
* Private Data
****************************************************************************/
-/* The four LEDs (PB1, PC4, PC5, PC3) are active HIGH: driving the pin high
+/* The four LEDs (PC5, PC4, PB0, PB1) are active HIGH: driving the pin high
* lights the LED.
*/
diff --git a/drivers/power/battery/gd30bc2416.c
b/drivers/power/battery/gd30bc2416.c
index 20da79f36fc..310db810cb6 100644
--- a/drivers/power/battery/gd30bc2416.c
+++ b/drivers/power/battery/gd30bc2416.c
@@ -47,6 +47,7 @@
#include <assert.h>
#include <debug.h>
+#include <nuttx/clock.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/signal.h>
@@ -68,6 +69,21 @@
#define GD30BC2416_ADC_DELAY_US 1000
+/* Without a battery, powered by the USB only, the charger answers on I2C
+ * for about 300 ms every 880 ms and NACKs in between: a NACKed transfer is
+ * retried every 50 ms for up to 1 s.
+ */
+
+#define GD30BC2416_RETRY_US 50000
+#define GD30BC2416_RETRY_MAX 20
+
+/* The start of the charge is not signalled by an IRQ: after an IRQ with
+ * the USB power present, the state is polled every 200 ms for up to 2 s.
+ */
+
+#define GD30BC2416_POLL_DELAY MSEC2TICK(200)
+#define GD30BC2416_POLL_MAX 10
+
/* Below this voltage no battery is considered to be connected */
#define GD30BC2416_VBAT_PRESENT_MV 2500
@@ -108,6 +124,9 @@ struct gd30bc2416_dev_s
uint16_t cfg3; /* Requested CFG3 VCCCHT field */
#ifdef CONFIG_SCHED_HPWORK
struct work_s work; /* IRQ bottom half */
+ struct work_s pollwork; /* State polling after an IRQ */
+ int state; /* Last state reported */
+ int npolls; /* Remaining state polls */
#endif
};
@@ -124,7 +143,12 @@ static int gd30bc2416_putreg16(FAR struct gd30bc2416_dev_s
*priv,
static int gd30bc2416_modifyreg16(FAR struct gd30bc2416_dev_s *priv,
uint8_t regaddr, uint16_t clearbits,
uint16_t setbits);
+static int gd30bc2416_transfer(FAR struct gd30bc2416_dev_s *priv,
+ uint8_t regaddr, FAR uint8_t *buffer,
+ bool read);
static int gd30bc2416_restore(FAR struct gd30bc2416_dev_s *priv);
+static int gd30bc2416_getstate(FAR struct gd30bc2416_dev_s *priv,
+ FAR int *status, FAR bool *usb);
static int gd30bc2416_getvbat(FAR struct gd30bc2416_dev_s *priv,
FAR int *millivolts);
@@ -174,6 +198,56 @@ static const struct battery_charger_operations_s
g_gd30bc2416ops =
* Private Functions
****************************************************************************/
+/****************************************************************************
+ * Name: gd30bc2416_transfer
+ *
+ * Description:
+ * Read or write the two bytes of a register. The register address is
+ * part of the slave address, so this is a single message. A NACK is
+ * retried: without a battery the charger only answers in time windows.
+ *
+ ****************************************************************************/
+
+static int gd30bc2416_transfer(FAR struct gd30bc2416_dev_s *priv,
+ uint8_t regaddr, FAR uint8_t *buffer,
+ bool read)
+{
+ struct i2c_config_s config;
+ int retries;
+ int ret;
+
+ config.frequency = priv->config->frequency;
+ config.address = GD30BC2416_I2C_ADDR(regaddr);
+ config.addrlen = 7;
+
+ for (retries = 0; ; retries++)
+ {
+ if (read)
+ {
+ ret = i2c_read(priv->i2c, &config, buffer, 2);
+ }
+ else
+ {
+ ret = i2c_write(priv->i2c, &config, buffer, 2);
+ }
+
+ if (ret != -ENXIO || retries >= GD30BC2416_RETRY_MAX)
+ {
+ break;
+ }
+
+ nxsig_usleep(GD30BC2416_RETRY_US);
+ }
+
+ if (ret < 0)
+ {
+ baterr("ERROR: %s of register 0x%02x failed: %d\n",
+ read ? "read" : "write", regaddr, ret);
+ }
+
+ return ret;
+}
+
/****************************************************************************
* Name: gd30bc2416_getreg16
*
@@ -186,19 +260,12 @@ static const struct battery_charger_operations_s
g_gd30bc2416ops =
static int gd30bc2416_getreg16(FAR struct gd30bc2416_dev_s *priv,
uint8_t regaddr, FAR uint16_t *regval)
{
- struct i2c_config_s config;
uint8_t buffer[2];
int ret;
- config.frequency = priv->config->frequency;
- config.address = GD30BC2416_I2C_ADDR(regaddr);
- config.addrlen = 7;
-
- ret = i2c_read(priv->i2c, &config, buffer, sizeof(buffer));
+ ret = gd30bc2416_transfer(priv, regaddr, buffer, true);
if (ret < 0)
{
- baterr("ERROR: i2c_read of register 0x%02x failed: %d\n",
- regaddr, ret);
return ret;
}
@@ -218,27 +285,14 @@ static int gd30bc2416_getreg16(FAR struct
gd30bc2416_dev_s *priv,
static int gd30bc2416_putreg16(FAR struct gd30bc2416_dev_s *priv,
uint8_t regaddr, uint16_t regval)
{
- struct i2c_config_s config;
uint8_t buffer[2];
- int ret;
-
- config.frequency = priv->config->frequency;
- config.address = GD30BC2416_I2C_ADDR(regaddr);
- config.addrlen = 7;
buffer[0] = regval >> 8;
buffer[1] = regval & 0xff;
batinfo("REG 0x%02x <- 0x%04x\n", regaddr, regval);
- ret = i2c_write(priv->i2c, &config, buffer, sizeof(buffer));
- if (ret < 0)
- {
- baterr("ERROR: i2c_write of register 0x%02x failed: %d\n",
- regaddr, ret);
- }
-
- return ret;
+ return gd30bc2416_transfer(priv, regaddr, buffer, false);
}
/****************************************************************************
@@ -359,7 +413,111 @@ static int gd30bc2416_getvbat(FAR struct gd30bc2416_dev_s
*priv,
return OK;
}
+/****************************************************************************
+ * Name: gd30bc2416_getstate
+ *
+ * Description:
+ * Read the battery state from the fault and status registers, and
+ * whether the USB power is present.
+ *
+ ****************************************************************************/
+
+static int gd30bc2416_getstate(FAR struct gd30bc2416_dev_s *priv,
+ FAR int *status, FAR bool *usb)
+{
+ uint16_t fault;
+ uint16_t status1;
+ uint16_t status2;
+ int ret;
+
+ *status = BATTERY_UNKNOWN;
+ *usb = false;
+
+ ret = gd30bc2416_getreg16(priv, GD30BC2416_REG_FAULT, &fault);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = gd30bc2416_getreg16(priv, GD30BC2416_REG_STATUS1, &status1);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = gd30bc2416_getreg16(priv, GD30BC2416_REG_STATUS2, &status2);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ *usb = (status1 & GD30BC2416_STATUS1_USB_PLUG) != 0;
+
+ if ((fault & ~GD30BC2416_FAULT_I2C_ACK) != 0)
+ {
+ *status = BATTERY_FAULT;
+ }
+ else if ((status1 & GD30BC2416_STATUS1_CH_END) != 0)
+ {
+ *status = BATTERY_FULL;
+ }
+ else if (*usb)
+ {
+ *status = (status2 & GD30BC2416_STATUS2_CH_EN) != 0 ?
+ BATTERY_CHARGING : BATTERY_IDLE;
+ }
+ else
+ {
+ *status = BATTERY_DISCHARGING;
+ }
+
+ return OK;
+}
+
#ifdef CONFIG_SCHED_HPWORK
+/****************************************************************************
+ * Name: gd30bc2416_pollworker
+ *
+ * Description:
+ * Poll the state after an IRQ until the charge starts, and notify the
+ * upper half when the state changes.
+ *
+ ****************************************************************************/
+
+static void gd30bc2416_pollworker(FAR void *arg)
+{
+ FAR struct gd30bc2416_dev_s *priv = arg;
+ int state;
+ bool usb;
+ int ret;
+
+ ret = nxmutex_lock(&priv->dev.batlock);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ ret = gd30bc2416_getstate(priv, &state, &usb);
+ nxmutex_unlock(&priv->dev.batlock);
+
+ if (ret < 0)
+ {
+ return;
+ }
+
+ if (state != priv->state)
+ {
+ priv->state = state;
+ battery_charger_changed(&priv->dev, BATTERY_STATE_CHANGED);
+ }
+
+ if (usb && state == BATTERY_IDLE && --priv->npolls > 0)
+ {
+ work_queue(HPWORK, &priv->pollwork, gd30bc2416_pollworker, priv,
+ GD30BC2416_POLL_DELAY);
+ }
+}
+
/****************************************************************************
* Name: gd30bc2416_worker
*
@@ -372,8 +530,7 @@ static int gd30bc2416_getvbat(FAR struct gd30bc2416_dev_s
*priv,
static void gd30bc2416_worker(FAR void *arg)
{
FAR struct gd30bc2416_dev_s *priv = arg;
- uint16_t fault = 0;
- uint16_t status1 = 0;
+ bool usb = false;
int ret;
ret = nxmutex_lock(&priv->dev.batlock);
@@ -382,19 +539,27 @@ static void gd30bc2416_worker(FAR void *arg)
return;
}
- gd30bc2416_getreg16(priv, GD30BC2416_REG_FAULT, &fault);
- gd30bc2416_getreg16(priv, GD30BC2416_REG_STATUS1, &status1);
- batinfo("IRQ: FAULT 0x%04x STATUS1 0x%04x\n", fault, status1);
-
ret = gd30bc2416_restore(priv);
- nxmutex_unlock(&priv->dev.batlock);
-
if (ret < 0)
{
baterr("ERROR: Failed to restore the configuration: %d\n", ret);
}
+ gd30bc2416_getstate(priv, &priv->state, &usb);
+ batinfo("IRQ: state %d USB %d\n", priv->state, usb);
+ nxmutex_unlock(&priv->dev.batlock);
+
battery_charger_changed(&priv->dev, GD30BC2416_IRQ_EVENTS);
+
+ /* The charge starts after the IRQ of the USB plug: follow the state */
+
+ if (usb && priv->state == BATTERY_IDLE &&
+ (priv->ctrl & GD30BC2416_CTRL_CH_EN) != 0)
+ {
+ priv->npolls = GD30BC2416_POLL_MAX;
+ work_queue(HPWORK, &priv->pollwork, gd30bc2416_pollworker, priv,
+ GD30BC2416_POLL_DELAY);
+ }
}
/****************************************************************************
@@ -428,9 +593,7 @@ static int gd30bc2416_state(FAR struct
battery_charger_dev_s *dev,
FAR int *status)
{
FAR struct gd30bc2416_dev_s *priv = (FAR struct gd30bc2416_dev_s *)dev;
- uint16_t fault;
- uint16_t status1;
- uint16_t status2;
+ bool usb;
int ret;
*status = BATTERY_UNKNOWN;
@@ -441,43 +604,7 @@ static int gd30bc2416_state(FAR struct
battery_charger_dev_s *dev,
return ret;
}
- ret = gd30bc2416_getreg16(priv, GD30BC2416_REG_FAULT, &fault);
- if (ret < 0)
- {
- return ret;
- }
-
- ret = gd30bc2416_getreg16(priv, GD30BC2416_REG_STATUS1, &status1);
- if (ret < 0)
- {
- return ret;
- }
-
- ret = gd30bc2416_getreg16(priv, GD30BC2416_REG_STATUS2, &status2);
- if (ret < 0)
- {
- return ret;
- }
-
- if ((fault & ~GD30BC2416_FAULT_I2C_ACK) != 0)
- {
- *status = BATTERY_FAULT;
- }
- else if ((status1 & GD30BC2416_STATUS1_CH_END) != 0)
- {
- *status = BATTERY_FULL;
- }
- else if ((status1 & GD30BC2416_STATUS1_USB_PLUG) != 0)
- {
- *status = (status2 & GD30BC2416_STATUS2_CH_EN) != 0 ?
- BATTERY_CHARGING : BATTERY_IDLE;
- }
- else
- {
- *status = BATTERY_DISCHARGING;
- }
-
- return OK;
+ return gd30bc2416_getstate(priv, status, &usb);
}
/****************************************************************************