bsp: k230: add spi driver

Requirement: The BSP for the k230 platform in the RT-Thread repository does not yet have an spi driver.

Solution: Provide spi driver for the k230 platform in the RT-Thread repository.

- Supports SPI0(OSPI) controller with 1/2/4/8 data lines.
- Supports SPI1(QSPI0) and SPI2(QSPI1) controllers with 1/2/4 data lines.
- Implements DMA-based transfers for OSPI, QSPI, and DSPI modes.
- Falls back to standard IRQ-driven transfers for legacy SPI mode (single line).
- Updates documentation in bsp/README.md

Signed-off-by: ChuanN-sudo <fjchuanil@gmail.com>
This commit is contained in:
Chuan
2025-11-10 23:12:29 +08:00
committed by R b b666
parent ee20484759
commit ae2a5758bc
10 changed files with 1221 additions and 5 deletions

View File

@@ -760,9 +760,9 @@ This document is based on the RT-Thread mainline repository and categorizes the
#### 🟢 K230 (RT-Smart)
| BSP Name | GPIO | UART | I2C | RTC | ADC | PWM | SDIO | HWTimer | WDT |
|----------|------|------|-----|-----|-----|-----|------|---------|-----|
| [k230](k230) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| BSP Name | GPIO | UART | I2C | RTC | ADC | PWM | SDIO | HWTimer | WDT | SPI |
|----------|------|------|-----|-----|-----|-----|------|---------|-----|-----|
| [k230](k230) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
#### 🟢 Xuantie (RT-Smart)

View File

@@ -1,6 +1,12 @@
scons.args: &scons
scons_arg:
- '--strict'
devices.spi:
<<: *scons
kconfig:
- CONFIG_RT_USING_SPI=y
- CONFIG_BSP_USING_SPI=y
- CONFIG_BSP_USING_SPI0=y
devices.i2c:
<<: *scons
kconfig:

View File

@@ -534,8 +534,6 @@ CONFIG_RT_USING_ADT_REF=y
# CONFIG_RT_USING_RT_LINK is not set
# end of Utilities
# CONFIG_RT_USING_VBUS is not set
#
# Memory management
#
@@ -943,6 +941,7 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_R_RHEALSTONE is not set
# CONFIG_PKG_USING_HEARTBEAT is not set
# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set
# CONFIG_PKG_USING_CHERRYECAT is not set
# end of system packages
#
@@ -1100,6 +1099,12 @@ CONFIG_RT_USING_VDSO=y
# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set
# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set
# end of GD32 Drivers
#
# HPMicro SDK
#
# CONFIG_PKG_USING_HPM_SDK is not set
# end of HPMicro SDK
# end of HAL & SDK Drivers
#
@@ -1619,6 +1624,7 @@ CONFIG_PKG_ZLIB_VER="latest"
#
# Drivers Configuration
#
# CONFIG_BSP_USING_SPI is not set
# CONFIG_BSP_USING_I2C is not set
# CONFIG_BSP_USING_RTC is not set
# CONFIG_BSP_USING_ADC is not set

View File

@@ -1,4 +1,31 @@
menu "Drivers Configuration"
menuconfig BSP_USING_SPI
bool "Enable SPI"
select RT_USING_SPI
select RT_USING_QSPI
default n
if BSP_USING_SPI
config BSP_USING_SPI0
bool "Enable SPI0"
help
Support 1, 2, 4 and 8 lines, Max clock frequency is 200 Mhz.
default n
config BSP_USING_SPI1
bool "Enable SPI1"
help
Support 1, 2, and 4 lines, Max clock frequency is 100 Mhz.
default n
config BSP_USING_SPI2
bool "Enable SPI2"
help
Support 1, 2, and 4 lines, Max clock frequency is 100 Mhz.
default n
endif
menuconfig BSP_USING_I2C
bool "Enable I2C"
select RT_USING_I2C

View File

@@ -0,0 +1,11 @@
# RT-Thread building script for SPI component
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd]
group = DefineGroup('SPI', src, depend = ['BSP_USING_SPI'], CPPPATH = CPPPATH)
Return('group')

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __DRV_SPI_H__
#define __DRV_SPI_H__
#include <stdint.h>
#include <stdbool.h>
#define SSIC_HAS_DMA 2
#define SSIC_AXI_BLW 8
#define SSIC_TX_ABW 256
#define SSIC_RX_ABW 256
#define IRQN_SPI0 146
#define IRQN_SPI1 155
#define IRQN_SPI2 164
#ifndef L1_CACHE_BYTES
#define L1_CACHE_BYTES 64
#endif
#define CACHE_ALIGN_TOP(x) (((x) + L1_CACHE_BYTES - 1) & ~(L1_CACHE_BYTES - 1))
#define CACHE_ALIGN_BOTTOM(x) ((x) & ~(L1_CACHE_BYTES - 1))
#define BIT(n) (1UL << (n))
enum
{
SSI_TXE = 0,
SSI_TXO,
SSI_RXF,
SSI_RXO,
SSI_TXU,
SSI_RXU,
SSI_MST,
SSI_DONE,
SSI_AXIE,
};
/* SPI mode */
enum
{
SPI_FRF_STD_SPI,
SPI_FRF_DUAL_SPI,
SPI_FRF_QUAD_SPI,
SPI_FRF_OCT_SPI,
};
/* SPI transmit mode */
enum
{
SPI_TMOD_TR,
SPI_TMOD_TO,
SPI_TMOD_RO,
SPI_TMOD_EPROMREAD,
};
/* Qspi register */
typedef struct
{
/* SPI Control Register 0 (0x00)*/
volatile uint32_t ctrlr0;
/* SPI Control Register 1 (0x04)*/
volatile uint32_t ctrlr1;
/* SPI Enable Register (0x08)*/
volatile uint32_t ssienr;
/* SPI Microwire Control Register (0x0c)*/
volatile uint32_t mwcr;
/* SPI Slave Enable Register (0x10)*/
volatile uint32_t ser;
/* SPI Baud Rate Select (0x14)*/
volatile uint32_t baudr;
/* SPI Transmit FIFO Threshold Level (0x18)*/
volatile uint32_t txftlr;
/* SPI Receive FIFO Threshold Level (0x1c)*/
volatile uint32_t rxftlr;
/* SPI Transmit FIFO Level Register (0x20)*/
volatile uint32_t txflr;
/* SPI Receive FIFO Level Register (0x24)*/
volatile uint32_t rxflr;
/* SPI Status Register (0x28)*/
volatile uint32_t sr;
/* SPI Interrupt Mask Register (0x2c)*/
volatile uint32_t imr;
/* SPI Interrupt Status Register (0x30)*/
volatile uint32_t isr;
/* SPI Raw Interrupt Status Register (0x34)*/
volatile uint32_t risr;
/* SPI Transmit FIFO Underflow Interrupt Clear Register (0x38)*/
volatile uint32_t txeicr;
/* SPI Receive FIFO Overflow Interrupt Clear Register (0x3c)*/
volatile uint32_t rxoicr;
/* SPI Receive FIFO Underflow Interrupt Clear Register (0x40)*/
volatile uint32_t rxuicr;
/* SPI Multi-Master Interrupt Clear Register (0x44)*/
volatile uint32_t msticr;
/* SPI Interrupt Clear Register (0x48)*/
volatile uint32_t icr;
/* SPI DMA Control Register (0x4c)*/
volatile uint32_t dmacr;
#if SSIC_HAS_DMA == 1
/* SPI DMA Transmit Data Level (0x50)*/
volatile uint32_t dmatdlr;
/* SPI DMA Receive Data Level (0x54)*/
volatile uint32_t dmardlr;
#elif SSIC_HAS_DMA == 2
/* SPI Destination Burst Length (0x50)*/
volatile uint32_t axiawlen;
/* SPI Source Burst Length (0x54)*/
volatile uint32_t axiarlen;
#else
uint32_t resv0[2];
#endif
/* SPI Identification Register (0x58)*/
volatile const uint32_t idr;
/* SPI DWC_ssi component version (0x5c)*/
volatile uint32_t ssic_version_id;
/* SPI Data Register 0-36 (0x60 -- 0xec)*/
volatile uint32_t dr[36];
/* SPI RX Sample Delay Register (0xf0)*/
volatile uint32_t rx_sample_delay;
/* SPI SPI Control Register (0xf4)*/
volatile uint32_t spi_ctrlr0;
/* SPI Transmit Drive Edge Register (0xf8)*/
volatile uint32_t ddr_drive_edge;
/* SPI XIP Mode bits (0xfc)*/
volatile uint32_t xip_mode_bits;
/* SPI XIP INCR transfer opcode (0x100)*/
volatile uint32_t xip_incr_inst;
/* SPI XIP WRAP transfer opcode (0x104)*/
volatile uint32_t xip_wrap_inst;
#if SSIC_CONCURRENT_XIP_EN
/* SPI XIP Control Register (0x108)*/
volatile uint32_t xip_ctrl;
/* SPI XIP Slave Enable Register (0x10c)*/
volatile uint32_t xip_ser;
/* SPI XIP Receive FIFO Overflow Interrupt Clear Register (0x110)*/
volatile uint32_t xrxoicr;
/* SPI XIP time out register for continuous transfers (0x114)*/
volatile uint32_t xip_cnt_time_out;
/* not support dyn ws (0x118)*/
uint32_t resv1[1];
/* SPI Transmit Error Interrupt Clear Register (0x11c)*/
volatile uint32_t spitecr;
#else
uint32_t resv1[6];
#endif
#if SSIC_HAS_DMA == 2
/* SPI Device Register (0x120)*/
volatile uint32_t spidr;
/* SPI Device Address Register (0x124)*/
volatile uint32_t spiar;
/* AXI Address Register 0 (0x128)*/
volatile uint32_t axiar0;
/* AXI Address Register 1 (0x12c)*/
volatile uint32_t axiar1;
/* AXI Master Error Interrupt Clear Register (0x130)*/
volatile uint32_t axiecr;
/* Transfer Done Clear Interrupt Clear Register (0x134)*/
volatile uint32_t donecr;
#endif
/* This register will not be used and is reserved. (0x138 ~ 0x13c)*/
uint32_t resv3[2];
#if SSIC_XIP_WRITE_REG_EN
/* XIP_WRITE_INCR_INST - XIP Write INCR transfer opcode (0x140)*/
volatile uint32_t xip_write_incr_inst;
/* XIP_WRITE_WRAP_INST - XIP Write WRAP transfer opcode (0x144)*/
volatile uint32_t xip_write_wrap_inst;
/* XIP_WRITE_CTRL - XIP Write Control Register (0x148)*/
volatile uint32_t xip_write_ctrl;
#else
uint32_t resv4[3];
#endif
// volatile uint32_t endian;
} __attribute__((packed, aligned(4))) k230_spi_reg_t;
#endif

View File

@@ -33,6 +33,9 @@ if GetDepend('BSP_UTEST_DRIVERS'):
if GetDepend('BSP_USING_I2C'):
src += ['test_i2c.c']
if GetDepend('BSP_USING_SPI'):
src += ['test_spi.c']
group = DefineGroup('utestcases', src, depend = [''])
Return('group')

View File

@@ -0,0 +1,222 @@
/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2006-2025 RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <rtdbg.h>
#include <utest.h>
#include "drv_spi.h"
#include <string.h>
#include "drv_pinctrl.h"
#include "drv_gpio.h"
/*
* 测试 SPI0 在标准SPI模式下的数据发送功能
*
* 功能说明:
* - 查找名为 "spi0" 的SPI总线设备
* - 挂载SPI设备到总线
* - 配置SPI设备参数
* - 模式标准SPI模式0 (RT_SPI_MODE_0)
* - 数据位8位
* - 最大频率1MHz
* - 数据线宽度1标准SPI
* - 准备测试数据(递增序列);
* - 创建SPI消息并发送16字节数据
* - 发送完成接收从机的16字节数据
* - 发送完成后卸载SPI设备。
*
* 硬件说明:
* - 本测试基于 K230 平台;
* - 测试SPI0(OSPI)的标准SPI模式TX和RX功能,使用硬件CS
* - 对应的引脚配置为:
* - CS: GPIO14
* - CLK: GPIO15
* - D0: GPIO16
* - D1: GPIO17
* - 需要连接SPI从设备如SPI调试器等来验证数据传输本测试文件使用一块stm32制作的SPI调试器
* - 如果没有实际从设备可以使用逻辑分析仪或示波器观察SPI波形(但是只能验证TX功能,接收到会是16bit的0xff)
*/
#define SPI0_BUS_NAME "spi0"
#define SPI0_DEV_NAME0 "spi00"
#define TEST_DATA_LENGTH 16
#define SPI0_CS_PIN 14
#define SPI0_CLK_PIN 15
#define SPI0_D0_PIN 16
#define SPI0_D1_PIN 17
#define SPI0_CS_PIN_AF IOMUX_FUNC2
#define SPI0_CLK_PIN_AF IOMUX_FUNC2
#define SPI0_D0_PIN_AF IOMUX_FUNC2
#define SPI0_D1_PIN_AF IOMUX_FUNC2
static void spi_gpio_init(void)
{
LOG_I("SPI demo: initializing SPI0 GPIO...");
k230_pinctrl_set_function(SPI0_CS_PIN, SPI0_CS_PIN_AF);
k230_pinctrl_set_function(SPI0_CLK_PIN, SPI0_CLK_PIN_AF);
k230_pinctrl_set_function(SPI0_D0_PIN, SPI0_D0_PIN_AF);
k230_pinctrl_set_function(SPI0_D1_PIN, SPI0_D1_PIN_AF);
k230_pinctrl_set_oe(SPI0_CS_PIN, 1);
k230_pinctrl_set_oe(SPI0_CLK_PIN, 1);
k230_pinctrl_set_oe(SPI0_D0_PIN, 1);
k230_pinctrl_set_oe(SPI0_D1_PIN, 1);
k230_pinctrl_set_ie(SPI0_CS_PIN, 1);
k230_pinctrl_set_ie(SPI0_CLK_PIN, 1);
k230_pinctrl_set_ie(SPI0_D0_PIN, 1);
k230_pinctrl_set_ie(SPI0_D1_PIN, 1);
}
static void spi_device_demo(void)
{
struct rt_qspi_device *qspi_dev;
LOG_I("Using rt_qspi_device to transmit");
rt_err_t ret;
uint8_t tx_data[TEST_DATA_LENGTH];
uint8_t rx_data[TEST_DATA_LENGTH];
for (int i = 0; i < TEST_DATA_LENGTH; i++)
{
tx_data[i] = i;
}
rt_memset(rx_data, 0, sizeof(rx_data));
/* Find QSPI Bus */
struct rt_spi_bus *spi_bus = (struct rt_spi_bus *)rt_device_find(SPI0_BUS_NAME);
if (!spi_bus)
{
LOG_E("Failed to find SPI bus: %s", SPI0_BUS_NAME);
return;
}
LOG_I("Success to find SPI bus: %s", SPI0_BUS_NAME);
qspi_dev = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device));
if (!qspi_dev)
{
LOG_E("Failed to allocate SPI device memory");
return;
}
LOG_I("Success to allocate QSPI device memory");
/* Attach SPI Device */
ret = rt_spi_bus_attach_device(&(qspi_dev->parent), SPI0_DEV_NAME0, SPI0_BUS_NAME, RT_NULL);
if (ret != RT_EOK)
{
LOG_E("Failed to attach SPI device: %d", ret);
rt_free(qspi_dev);
return;
}
LOG_I("SPI device attached successfully");
/* SPI Device Config*/
struct rt_qspi_configuration qspi_cfg;
qspi_cfg.parent.mode = RT_SPI_MODE_0 | RT_SPI_MSB;
qspi_cfg.parent.data_width = 8;
qspi_cfg.parent.max_hz = 1000000;
qspi_cfg.parent.reserved = 0;
qspi_cfg.qspi_dl_width = 1;
qspi_cfg.medium_size = 0;
qspi_cfg.ddr_mode = 0;
ret = rt_qspi_configure(qspi_dev, &qspi_cfg);
if (ret != RT_EOK)
{
LOG_E("SPI configuration failed: %d", ret);
rt_free(qspi_dev);
return;
}
LOG_I("SPI configuration: Standard SPI, mode=0, data_width=8, max_hz=%d, data_lines=%d",
qspi_cfg.parent.max_hz, qspi_cfg.qspi_dl_width);
LOG_I("Sending test data (length=%d):", TEST_DATA_LENGTH);
for (int i = 0; i < TEST_DATA_LENGTH; i++)
{
rt_kprintf("%02X ", tx_data[i]);
}
rt_kprintf("\n");
/* Create SPI Message */
struct rt_qspi_message msg;
rt_memset(&msg, 0, sizeof(msg));
/*Using Standard SPI*/
msg.instruction.content = 0;
msg.instruction.qspi_lines = 1;
msg.address.content = 0;
msg.address.size = 0;
msg.address.qspi_lines = 1;
msg.qspi_data_lines = 1;
msg.dummy_cycles = 0;
/* SPI Message Config */
msg.parent.send_buf = tx_data;
msg.parent.recv_buf = rx_data;
msg.parent.length = TEST_DATA_LENGTH;
msg.parent.cs_take = 1;
msg.parent.cs_release = 1;
msg.parent.next = RT_NULL;
/* Transfer Data */
ret = rt_qspi_transfer_message(qspi_dev, &msg);
if (ret != TEST_DATA_LENGTH)
{
LOG_E("SPI transfer failed, returned: %d", ret);
}
uassert_int_equal(ret, TEST_DATA_LENGTH);
LOG_I("SPI TX demo: sent %d bytes successfully", ret);
LOG_I("Received data from slave (length=%d):", TEST_DATA_LENGTH);
for (int i = 0; i < TEST_DATA_LENGTH; i++)
{
rt_kprintf("%02X ", rx_data[i]);
}
rt_kprintf("\n");
/* Detach SPI Device */
ret = rt_spi_bus_detach_device(&(qspi_dev->parent));
uassert_int_equal(ret, RT_EOK);
rt_free(qspi_dev);
}
static void testcase(void)
{
UTEST_UNIT_RUN(spi_gpio_init);
UTEST_UNIT_RUN(spi_device_demo);
}
static rt_err_t utest_tc_init(void)
{
LOG_I("SPI test case initialization");
return RT_EOK;
}
static rt_err_t utest_tc_cleanup(void)
{
LOG_I("SPI test case cleanup");
return RT_EOK;
}
UTEST_TC_EXPORT(testcase, "bsp.k230.drivers.spi", utest_tc_init, utest_tc_cleanup, 10);

View File

@@ -503,6 +503,10 @@
/* GD32 Drivers */
/* end of GD32 Drivers */
/* HPMicro SDK */
/* end of HPMicro SDK */
/* end of HAL & SDK Drivers */
/* sensors drivers */