risc-v/gd32vw55x: add sdcard (SPI/FAT) example to gd32vw553k-start

Add an "sdcard" configuration that mounts an SD card over SPI0 with a FAT
filesystem. The board provides the SPI chip-select glue (gd32_spi0select,
gd32_spi0status and gd32_spi0register in a new gd32_spi.c) and gd32_bringup()
binds the slot with mmcsd_spislotinitialize() and mounts /dev/mmcsd0 on
/mnt/sd.

The card is wired to the J1 header: SCK PA2, MISO PA1, MOSI PA0, and a
software chip select on PA4. CONFIG_MMCSD_MMCSUPPORT is left off (its MMC
CMD1 probe upsets SD cards).

Signed-off-by: Jorge Guzman <jorge.gzm@gmail.com>
This commit is contained in:
Jorge Guzman 2026-07-24 14:14:21 -03:00 committed by CeDeROM
parent e33a1f9943
commit bbc0bdd40c
7 changed files with 273 additions and 5 deletions

View file

@ -256,6 +256,50 @@ duty (100 Hz, 50 % by default)::
duration for a single run. Put an LED (with a series resistor) or a scope on
PA0 to see it.
sdcard
------
NSH plus an SD card on SPI0 with a FAT filesystem. The card is registered as
``/dev/mmcsd0`` and ``gd32_bringup()`` mounts it on ``/mnt/sd``.
Wire the card (or a microSD-over-SPI breakout) to the J1 header:
======== ==========
Signal Pin
======== ==========
SCK PA2
MISO PA1
MOSI PA0
CS PA4
======== ==========
.. note::
Power the breakout from **5 V**, not 3.3 V. These breakouts carry their
own 3.3 V regulator, and the SD card draws current bursts while it powers
up; feeding 3.3 V directly leaves the card stuck in the ACMD41 init loop
(it answers CMD0/CMD8 but never leaves the idle state). A decoupling
capacitor close to the card and short leads help too.
The SPI runs its initialisation at ~625 kHz (PCLK2 / 256), slightly above the
400 kHz of the SD spec, which the vast majority of cards tolerate.
The block device shows up as ``/dev/mmcsd0`` and the bringup already mounts it
on ``/mnt/sd``, so no manual ``mount`` is needed::
nsh> ls /dev/
/dev:
console
mmcsd0
null
ttyS0
zero
nsh> mount
/mnt/sd type vfat
nsh> echo "GD32 on NuttX" > /mnt/sd/hello.txt
nsh> cat /mnt/sd/hello.txt
GD32 on NuttX
nsh> ls -l /mnt/sd
littlefs
--------

View file

@ -0,0 +1,55 @@
#
# 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_MMCSD_MMCSUPPORT 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_FS_FAT=y
CONFIG_FS_PROCFS=y
CONFIG_GD32VW55X_SPI=y
CONFIG_GD32VW55X_UART2=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_MMCSD=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_STACK_COLORATION=y
CONFIG_START_DAY=13
CONFIG_START_MONTH=7
CONFIG_START_YEAR=2026
CONFIG_SYSTEM_NSH=y
CONFIG_TASK_NAME_SIZE=12
CONFIG_UART2_RXBUFSIZE=128
CONFIG_UART2_SERIAL_CONSOLE=y
CONFIG_UART2_TXBUFSIZE=128

View file

@ -71,6 +71,15 @@
# define GPIO_SPI_SCK GPIO_SPI_SCK_1 /* PA2, AF5 */
# define GPIO_SPI_MISO GPIO_SPI_MISO_1 /* PA1, AF5 */
# define GPIO_SPI_MOSI GPIO_SPI_MOSI_1 /* PA0, AF5 */
/* SPI0 chip select for the SD card: a plain GPIO output on PA4 (J1),
* driven by gd32_spi0select(). Active low, idles high.
*/
# define GPIO_SPI0_CSPIN (GPIO_CFG_MODE_OUTPUT | GPIO_CFG_PUPD_NONE | \
GPIO_CFG_PP | GPIO_CFG_SPEED_MAX | \
GPIO_CFG_OUTPUT_SET | GPIO_CFG_PORT_A | \
GPIO_CFG_PIN_4)
#endif
/* I2C0 uses PA2 (SCL) / PA3 (SDA) on AF4, the pins broken out on the J1

View file

@ -38,4 +38,8 @@ ifeq ($(CONFIG_BOARDCTL_RESET),y)
CSRCS += gd32_reset.c
endif
ifeq ($(CONFIG_GD32VW55X_SPI),y)
CSRCS += gd32_spi.c
endif
include $(TOPDIR)/boards/Board.mk

View file

@ -37,6 +37,9 @@
#ifdef CONFIG_SPI_DRIVER
# include <nuttx/spi/spi_transfer.h>
#endif
#ifdef CONFIG_MMCSD_SPI
# include <nuttx/mmcsd.h>
#endif
#ifdef CONFIG_GD32VW55X_ADC
# include <nuttx/analog/adc.h>
#endif
@ -171,19 +174,47 @@ int gd32_bringup(void)
#endif
#ifdef CONFIG_GD32VW55X_SPI
/* The family has a single SPI instance: bus 0 */
/* The family has a single SPI instance: bus 0. Configure the chip select
* GPIOs first, then bring the bus up.
*/
gd32_spidev_initialize();
spi = gd32_spibus_initialize(0);
if (spi == NULL)
{
ferr("ERROR: failed to initialize SPI0\n");
}
#ifdef CONFIG_SPI_DRIVER
else if (spi_register(spi, 0) < 0)
else
{
ferr("ERROR: failed to register /dev/spi0\n");
}
#ifdef CONFIG_SPI_DRIVER
if (spi_register(spi, 0) < 0)
{
ferr("ERROR: failed to register /dev/spi0\n");
}
#endif
#ifdef CONFIG_MMCSD_SPI
/* Bind SPI0 to the MMC/SD SPI slot (/dev/mmcsd0) and mount it as FAT.
* The SD card is wired to SPI0 (SCK PA2, MISO PA1, MOSI PA0) with the
* chip select on PA4 -- all on the J1 header.
*/
ret = mmcsd_spislotinitialize(0, 0, spi);
if (ret < 0)
{
ferr("ERROR: failed to bind SPI0 to the MMC/SD slot: %d\n", ret);
}
else
{
ret = nx_mount("/dev/mmcsd0", "/mnt/sd", "vfat", 0, NULL);
if (ret < 0)
{
ferr("ERROR: failed to mount /mnt/sd: %d\n", ret);
}
}
#endif
}
#endif
#ifdef CONFIG_GD32VW55X_ADC

View file

@ -0,0 +1,113 @@
/****************************************************************************
* boards/risc-v/gd32vw55x/gd32vw553k-start/src/gd32_spi.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <nuttx/spi/spi.h>
#include <arch/board/board.h>
#include "gd32vw55x_gpio.h"
#include "gd32vw553k-start.h"
#ifdef CONFIG_GD32VW55X_SPI
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: gd32_spidev_initialize
*
* Description:
* Configure the SPI chip select GPIO(s). The bus itself is brought up by
* gd32_spibus_initialize().
*
****************************************************************************/
void gd32_spidev_initialize(void)
{
/* The SD card chip select on SPI0, idle (de-asserted, high) */
gd32_gpio_config(GPIO_SPI0_CSPIN);
gd32_gpio_write(GPIO_SPI0_CSPIN, true);
}
/****************************************************************************
* Name: gd32_spi0select and gd32_spi0status
*
* Description:
* The board-specific select and status methods of the SPI interface (see
* include/nuttx/spi/spi.h). Chip select is active low.
*
****************************************************************************/
void gd32_spi0select(struct spi_dev_s *dev, uint32_t devid, bool selected)
{
spiinfo("devid: %" PRIu32 " CS: %s\n", devid,
selected ? "assert" : "de-assert");
gd32_gpio_write(GPIO_SPI0_CSPIN, !selected);
}
uint8_t gd32_spi0status(struct spi_dev_s *dev, uint32_t devid)
{
uint8_t status = 0;
if (devid == SPIDEV_MMCSD(0))
{
/* There is no card-detect line wired, so assume the card is present */
status |= SPI_STATUS_PRESENT;
}
return status;
}
#ifdef CONFIG_SPI_CALLBACK
/****************************************************************************
* Name: gd32_spi0register
*
* Description:
* Register a media-change callback. This board has no card-detect line,
* so there is nothing to notify; the slot is reported present by
* gd32_spi0status().
*
****************************************************************************/
int gd32_spi0register(struct spi_dev_s *dev, spi_mediachange_t callback,
void *arg)
{
return OK;
}
#endif
#endif /* CONFIG_GD32VW55X_SPI */

View file

@ -37,4 +37,16 @@
int gd32_bringup(void);
/****************************************************************************
* Name: gd32_spidev_initialize
*
* Description:
* Configure the SPI chip select GPIO(s) used by the board.
*
****************************************************************************/
#ifdef CONFIG_GD32VW55X_SPI
void gd32_spidev_initialize(void);
#endif
#endif /* __BOARDS_RISCV_GD32VW55X_GD32VW553K_START_SRC_GD32VW553K_START_H */