1
0
Эх сурвалжийг харах

ch569w-evt : add spi master driver, SPI0 tested (#6205)

* ch569w-evt : add spi master driver, SPI0 tested

* Update bsp/wch/risc-v/ch569w-evt/board/Kconfig

* Update bsp/wch/risc-v/ch569w-evt/board/Kconfig

Co-authored-by: Man, Jianting (Meco) <920369182@qq.com>
emuzit 2 жил өмнө
parent
commit
a881c05e58

+ 3 - 0
bsp/wch/risc-v/Libraries/ch56x_drivers/SConscript

@@ -15,6 +15,9 @@ if GetDepend('SOC_SERIES_CH569'):
 if GetDepend('RT_USING_HWTIMER'):
     src += ['ch56x_timer.c']
 
+if GetDepend('RT_USING_SPI'):
+    src += ['ch56x_spi.c']
+
 if GetDepend('RT_USING_PIN'):
     src += ['ch56x_gpio.c']
 

+ 318 - 0
bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_spi.c

@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author            Notes
+ * 2022-07-30     Emuzit            first version
+ */
+#include <rthw.h>
+#include <rtdebug.h>
+#include <drivers/spi.h>
+#include <drivers/pin.h>
+#include "ch56x_spi.h"
+#include "ch56x_sys.h"
+
+#if !defined(BSP_USING_SPI0) && !defined(BSP_USING_SPI1)
+    #error "Please define at least one SPIx"
+#endif
+
+struct spi_bus
+{
+    struct rt_spi_bus parent;
+    volatile struct spi_registers *reg_base;
+    irq_number_t irqn;
+    char *name;
+    rt_base_t sck_pin;
+    rt_base_t mosi_pin;
+    rt_base_t miso_pin;
+};
+
+#ifdef BSP_USING_SPI0
+static struct spi_bus spi_bus_0 =
+{
+    .reg_base = (struct spi_registers *)SPI0_REG_BASE,
+    .irqn = SPI0_IRQn,
+    .name = SPI0_BUS_NAME,
+    .sck_pin = SPI0_SCK_PIN,
+    .mosi_pin = SPI0_MOSI_PIN,
+    .miso_pin = SPI0_MISO_PIN,
+};
+#endif
+#ifdef BSP_USING_SPI1
+static struct spi_bus spi_bus_1 =
+{
+    .reg_base = (struct spi_registers *)SPI1_REG_BASE,
+    .irqn = SPI1_IRQn,
+    .name = SPI1_BUS_NAME,
+    .sck_pin = SPI1_SCK_PIN,
+    .mosi_pin = SPI1_MOSI_PIN,
+    .miso_pin = SPI1_MISO_PIN,
+};
+#endif
+
+static uint8_t _spi_pin_config(struct rt_spi_device *device, struct rt_spi_configuration *config)
+{
+    struct spi_bus *spi_bus = (struct spi_bus *)device->bus;
+
+    uint8_t mode;
+
+    /* RT_SPI_3WIRE means SI/SO pin shared */
+    mode = config->mode & (RT_SPI_MASTER | RT_SPI_SLAVE | RT_SPI_3WIRE);
+    if (mode == RT_SPI_MASTER)
+    {
+        mode = RB_SPI_MOSI_OE | RB_SPI_SCK_OE;
+        rt_pin_mode(spi_bus->mosi_pin, PIN_MODE_OUTPUT);
+        rt_pin_mode(spi_bus->sck_pin, PIN_MODE_OUTPUT);
+    }
+    else if (mode == RT_SPI_SLAVE)
+    {
+        mode = RB_SPI_MISO_OE | RB_SPI_MODE_SLAVE;
+        rt_pin_mode(spi_bus->miso_pin, PIN_MODE_OUTPUT);
+    }
+    else if (mode == RT_SPI_MASTER | RT_SPI_3WIRE)
+    {
+        mode = RB_SPI_2WIRE_MOD | RB_SPI_MISO_OE | RB_SPI_SCK_OE;
+        rt_pin_mode(spi_bus->miso_pin, PIN_MODE_INPUT);
+        rt_pin_mode(spi_bus->sck_pin, PIN_MODE_OUTPUT);
+    }
+    else
+    {
+        mode = RB_SPI_2WIRE_MOD | RB_SPI_MISO_OE | RB_SPI_MODE_SLAVE;
+        rt_pin_mode(spi_bus->miso_pin, PIN_MODE_INPUT);
+    }
+
+    return mode;
+}
+
+static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *config)
+{
+    volatile struct spi_registers *sxreg;
+    struct rt_spi_device *owner;
+
+    union _spi_ctrl_mod ctrl_mod;
+    uint8_t mode;
+    uint32_t Fsys;
+    uint32_t div;
+
+    rt_base_t cs_pin;
+    int inactive;
+
+    RT_ASSERT(device != RT_NULL);
+
+    /* ch56x SPI supports only 8-bit data */
+    if (config->data_width != 8)
+        return -RT_EINVAL;
+
+    ctrl_mod.reg = _spi_pin_config(device, config);
+
+    /* ch56x SPI supports only mode 0 & mode 3 */
+    mode = config->mode & (RT_SPI_CPOL | RT_SPI_CPHA);
+    if (mode == RT_SPI_MODE_0)
+        ctrl_mod.mst_sck_mod = MST_SCK_MOD_0;
+    else if (mode == RT_SPI_MODE_3)
+        ctrl_mod.mst_sck_mod = MST_SCK_MOD_3;
+    else
+        return -RT_EINVAL;
+
+    /* CLOCK_DIV is 8-bit, reject excessively low max_hz */
+    Fsys = sys_hclk_get();
+    if (config->max_hz < Fsys / 255)
+        return -RT_EINVAL;
+
+    /* minimum allowable CLOCK_DIV is 2 */
+    div = (Fsys + config->max_hz - 1) / config->max_hz;
+    if (div < 2)
+        div = 2;
+
+    sxreg = ((struct spi_bus *)device->bus)->reg_base;
+    sxreg->CLOCK_DIV = div;
+
+    mode = config->mode & (RT_SPI_MSB | RT_SPI_LSB);
+    sxreg->CTRL_CFG.reg = (mode == RT_SPI_MSB) ? 0 : RB_SPI_BIT_ORDER;
+    sxreg->INTER_EN.reg = 0;
+
+    ctrl_mod.all_clear = 1;
+    sxreg->CTRL_MOD.reg = ctrl_mod.reg;
+    ctrl_mod.all_clear = 0;
+    sxreg->CTRL_MOD.reg = ctrl_mod.reg;
+
+    mode = config->mode & (RT_SPI_MASTER | RT_SPI_SLAVE);
+    if (mode == RT_SPI_MASTER)
+    {
+        /* get bus owner before this configure */
+        owner = device->bus->owner;
+        if (owner && owner != device)
+        {
+            /* make sure predecessor's CS is deactived */
+            inactive = (owner->config.mode & RT_SPI_CS_HIGH) ? PIN_LOW : PIN_HIGH;
+            cs_pin = (rt_base_t)owner->parent.user_data;
+            rt_pin_write(cs_pin, inactive);
+        }
+        /* bus owner is maintained by upper layer, do not update here */
+
+        inactive = (config->mode & RT_SPI_CS_HIGH) ? PIN_LOW : PIN_HIGH;
+        cs_pin = (rt_base_t)device->parent.user_data;
+        rt_pin_write(cs_pin, inactive);
+        rt_pin_mode(cs_pin, PIN_MODE_OUTPUT);
+    }
+
+    /* `config` is actually `device->config` : spi_core.c */
+    //device->config = *config;
+
+    return RT_EOK;
+}
+
+static rt_uint32_t _spi_xfer_1(struct rt_spi_device *device, struct rt_spi_message *message)
+{
+    struct spi_bus *spi_bus = (struct spi_bus *)device->bus;
+    volatile struct spi_registers *sxreg = spi_bus->reg_base;
+
+    uint8_t *data;
+    uint32_t size;
+
+    rt_base_t cs_pin;
+    int cs_high;
+
+    size = message->length;
+    if (size == 0 || size > 4095)
+        return 0;
+
+    /* ch56x can't do SPI send & recv at the same time */
+    if (message->send_buf && !message->recv_buf)
+    {
+        data = (uint8_t *)message->send_buf;
+        sxreg->CTRL_MOD.fifo_dir = SPI_FIFO_DIR_OUTPUT;
+    }
+    else if (!message->send_buf && message->recv_buf)
+    {
+        data = (uint8_t *)message->recv_buf;
+        sxreg->CTRL_MOD.fifo_dir = SPI_FIFO_DIR_INPUT;
+    }
+    else
+    {
+        return 0;
+    }
+
+    /* set MISO pin direction to match xfer if shared SI/SO pin */
+    if (device->config.mode & RT_SPI_3WIRE)
+    {
+        rt_base_t mode = message->send_buf ? PIN_MODE_OUTPUT : PIN_MODE_INPUT;
+        rt_pin_mode(spi_bus->miso_pin, mode);
+    }
+
+    cs_pin = (rt_base_t)device->user_data;
+    cs_high = device->config.mode & RT_SPI_CS_HIGH;
+
+    if (message->cs_take)
+    {
+        /* take/activate CS */
+        rt_pin_write(cs_pin, cs_high ? PIN_HIGH : PIN_LOW);
+    }
+
+    sxreg->TOTAL_COUNT = size;
+
+    if (size > SPI_FIFO_SIZE)
+    {
+        sxreg->DMA_BIG = (uint32_t)data;
+        sxreg->DMA_END = (uint32_t)(data + size);
+        sxreg->CTRL_CFG.dma_enable = 1;
+        /* mark no need to read FIFO */
+        size = 0;
+    }
+    else
+    {
+        if (message->send_buf)
+        {
+            /* keep sending, won't overflow */
+            while (size)
+            {
+                sxreg->FIFO = *data++;
+                size--;
+            }
+        }
+    }
+
+    /* wait for transfer done */
+    while (sxreg->TOTAL_COUNT > 0);
+
+    /* non-DMA recv => read data from FIFO */
+    if (size > 0)
+    {
+        while (size--)
+            *data++ = sxreg->FIFO;
+    }
+
+    /* set MISO as input after xfer if shared SI/SO pin */
+    if (device->config.mode & RT_SPI_3WIRE)
+    {
+        rt_pin_mode(spi_bus->miso_pin, PIN_MODE_INPUT);
+    }
+
+    if (message->cs_release)
+    {
+        /* release/deactivate CS */
+        rt_pin_write(cs_pin, cs_high ? PIN_LOW : PIN_HIGH);
+    }
+
+    return message->length;
+}
+
+static rt_uint32_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
+{
+    uint32_t total_xsize = 0;
+    uint32_t xsize;
+
+    RT_ASSERT(device != NULL);
+    RT_ASSERT(message != NULL);
+
+    while (message != RT_NULL)
+    {
+        xsize = _spi_xfer_1(device, message);
+        if (xsize != message->length)
+            return 0;
+        total_xsize += xsize;
+        message = message->next;
+    }
+
+    return total_xsize;
+}
+
+static const struct rt_spi_ops spi_ops =
+{
+    .configure = spi_configure,
+    .xfer = spi_xfer,
+};
+
+static int rt_hw_spi_init(void)
+{
+    struct spi_bus *devices[2];
+
+    rt_err_t res, ret = RT_EOK;
+
+    int n = 0;
+
+#ifdef BSP_USING_SPI1
+    devices[n++] = &spi_bus_1;
+#endif
+#ifdef BSP_USING_SPI0
+    devices[n++] = &spi_bus_0;
+#endif
+
+    while (--n >= 0)
+    {
+        struct spi_bus *spi_bus = devices[n];
+
+        sys_clk_off_by_irqn(spi_bus->irqn, SYS_SLP_CLK_ON);
+
+        res = rt_spi_bus_register(&spi_bus->parent, spi_bus->name, &spi_ops);
+        if (res != RT_EOK)
+        {
+            ret = res;
+        }
+    };
+
+    return ret;
+}
+INIT_DEVICE_EXPORT(rt_hw_spi_init);

+ 218 - 0
bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_spi.h

@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author            Notes
+ * 2022-07-30     Emuzit            first version
+ */
+#ifndef __CH56X_SPI_H__
+#define __CH56X_SPI_H__
+
+#include "soc.h"
+#include "ch56x_gpio.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SPI0_BUS_NAME       "spi0"
+#define SPI1_BUS_NAME       "spi1"
+
+#ifndef SPI0_SCS_PIN
+#define SPI0_SCS_PIN        GET_PIN(A, 12)
+#endif
+#define SPI0_SCK_PIN        GET_PIN(A, 13)
+#define SPI0_MOSI_PIN       GET_PIN(A, 14)
+#define SPI0_MISO_PIN       GET_PIN(A, 15)
+
+#ifdef SOC_SERIES_CH569
+#ifndef SPI1_SCS_PIN
+#define SPI1_SCS_PIN        GET_PIN(B, 11)
+#endif
+#define SPI1_SCK_PIN        GET_PIN(B, 12)
+#define SPI1_MOSI_PIN       GET_PIN(B, 13)
+#define SPI1_MISO_PIN       GET_PIN(B, 14)
+
+#else
+#define SPI1_SCK_PIN        GET_PIN(A, 0)
+#define SPI1_MOSI_PIN       GET_PIN(A, 1)
+#define SPI1_MISO_PIN       GET_PIN(A, 2)
+#endif
+
+#define SPI_FIFO_SIZE       8
+
+union _spi_ctrl_mod
+{
+    uint8_t reg;
+    struct
+    {
+        uint8_t mode_slave  : 1;  // RW, SPI master/slave (0/1) mode select
+        uint8_t all_clear   : 1;  // RW, clear FIFO/count/int-flag
+        uint8_t two_wire    : 1;  // RW, 2/3-wire mode (0/1), SPI slave
+        uint8_t mst_sck_mod : 1;  // RW, mode0/mode3 (0/1) for SCK idle L/H
+        uint8_t fifo_dir    : 1;  // RW, FIFO direction is output/input (0/1)
+        uint8_t sck_oe      : 1;  // RW, SCK pin output enable
+        uint8_t mosi_oe     : 1;  // RW, MOSI pin output enable
+        uint8_t miso_oe     : 1;  // RW, MISO pin output enable
+    };
+    struct
+    {
+        uint8_t stuff_0     : 3;
+        uint8_t slv_cmd_mod : 1;  // RW, 1st byte is data/cmd (0/1), SPI slave
+        uint8_t stuff_4     : 4;
+    };
+};
+#define RB_SPI_MODE_SLAVE   0x01
+#define RB_SPI_ALL_CLEAR    0x02
+#define RB_SPI_2WIRE_MOD    0x04
+#define RB_SPI_MST_SCK_MOD  0x08
+#define RB_SPI_SLV_CMD_MOD  0x08
+#define RB_SPI_FIFO_DIR     0x10
+#define RB_SPI_SCK_OE       0x20
+#define RB_SPI_MOSI_OE      0x40
+#define RB_SPI_MISO_OE      0x80
+
+#define MST_SCK_MOD_0       0
+#define MST_SCK_MOD_3       1
+
+#define SPI_FIFO_DIR_OUTPUT 0
+#define SPI_FIFO_DIR_INPUT  1
+
+union _spi_ctrl_cfg
+{
+    uint8_t reg;
+    struct
+    {
+        uint8_t dma_enable  : 1;  // RW, enable DMA function
+        uint8_t resv_1      : 1;
+        uint8_t dma_loop    : 1;  // RW, enable DMA loop mode (0 => single)
+        uint8_t resv_3      : 1;
+        uint8_t auto_if     : 1;  // RW, enable auto clear RB_SPI_IF_BYTE_END
+        uint8_t bit_order   : 1;  // RW, data bit ordering, LSB/MSB first (0/1)
+        uint8_t resv_6      : 2;
+    };
+};
+#define RB_SPI_DMA_ENABLE   0x01
+#define RB_SPI_DMA_LOOP     0x04
+#define RB_SPI_AUTO_IF      0x10
+#define RB_SPI_BIT_ORDER    0x20
+
+#define SPI_BIT_ORDER_MSB   0
+#define SPI_BIT_ORDER_LSB   1
+
+union _spi_inter_en
+{
+    uint8_t reg;
+    struct
+    {
+        uint8_t cnt_end     : 1;  // RW, IE for all bytes transfered
+        uint8_t byte_end    : 1;  // RW, IE for single byte transfered
+        uint8_t fifo_hf     : 1;  // RW, IE for FIFO half full
+        uint8_t dma_end     : 1;  // RW, IE for end of DMA
+        uint8_t fifo_ov     : 1;  // RW, IE for FIFO full or empty
+        uint8_t resv_5      : 2;
+        uint8_t fst_byte    : 1;  // RW, IE for 1st byte received, SPI slave
+    };
+};
+#define RB_SPI_IE_CNT_END   0x01
+#define RB_SPI_IE_BYTE_END  0x02
+#define RB_SPI_IE_FIFO_HF   0x04
+#define RB_SPI_IE_DMA_END   0x08
+#define RB_SPI_IE_FIFO_OV   0x10
+#define RB_SPI_IE_FST_BYTE  0x80
+
+union _spi_run_flag
+{
+    uint8_t reg;
+    struct
+    {
+        uint8_t resv_0      : 4;
+        uint8_t slv_cmd_act : 1;  // RO, SPI slave cmd received
+        uint8_t fifo_ready  : 1;  // RO, SPI FIFO ready to transfer
+        uint8_t slv_cs_load : 1;  // RO, SPI slave is loading R8_SPIx_SLAVE_PRE
+        uint8_t slv_select  : 1;  // RO, SPI slave CS active (selected)
+    };
+};
+#define RB_SPI_SLV_CMD_ACT  0x10
+#define RB_SPI_FIFO_READY   0x20
+#define RB_SPI_SLV_CS_LOAD  0x40
+#define RB_SPI_SLV_SELECT   0x80
+
+union _spi_int_flag
+{
+    uint8_t reg;
+    struct
+    {
+        uint8_t cnt_end     : 1;  // RW1, IF for all bytes transfered
+        uint8_t byte_end    : 1;  // RW1, IF for single byte transfered
+        uint8_t fifo_hf     : 1;  // RW1, IF for FIFO half full
+        uint8_t dma_end     : 1;  // RW1, IF for end of DMA
+        uint8_t fifo_ov     : 1;  // RW1, IF for FIFO full or empty
+        uint8_t resv_5      : 1;
+        uint8_t free        : 1;  // RO, current SPI state is free
+        uint8_t fst_byte    : 1;  // RW1, IF for 1st byte received, SPI slave
+    };
+};
+#define RB_SPI_IF_CNT_END   0x01
+#define RB_SPI_IF_BYTE_END  0x02
+#define RB_SPI_IF_FIFO_HF   0x04
+#define RB_SPI_IF_DMA_END   0x08
+#define RB_SPI_IF_FIFO_OV   0x10
+#define RB_SPI_FREE         0x40
+#define RB_SPI_IF_FST_BYTE  0x80
+
+/*
+ * 0x00  R8_SPIx_CTRL_MOD:    SPI mode setting register
+ * 0x01  R8_SPIx_CTRL_CFG:    SPI configuration register
+ * 0x02  R8_SPIx_INTER_EN:    SPI interrupt enable register
+ * 0x03  R8_SPIx_CLOCK_DIV:   SPI master clock divisor, minimum 2
+ * 0x03  R8_SPIx_SLAVE_PRE:   SPI slave preset data (reset as 10h)
+ * 0x04  R8_SPIx_BUFFER:      SPI data buffer
+ * 0x05  R8_SPIx_RUN_FLAG:    SPI working state register
+ * 0x06  R8_SPIx_INT_FLAG:    SPI interrupt flags register
+ * 0x07  R8_SPIx_FIFO_COUNT:  SPI FIFO data count
+ * 0x0c  R16_SPIx_TOTAL_CNT:  SPI total data length to transfer
+ * 0x10  R8_SPIx_FIFO:        SPI FIFO
+ * 0x13  R8_SPIx_FIFO_COUNT1: SPI FIFO data count
+ * 0x14  R32_SPIx_DMA_NOW:    SPI DMA current address
+ * 0x18  R32_SPIx_DMA_BEG:    SPI DMA start address
+ * 0x1c  R32_SPIx_DMA_END:    SPI DMA end address
+ *
+ * CAVEAT: gcc (as of 8.2.0) tends to read 32-bit word for bit field test.
+ * Be careful for those with side effect for read (e.g. RBR, IIR).
+ */
+struct spi_registers
+{
+    union _spi_ctrl_mod     CTRL_MOD;
+    union _spi_ctrl_cfg     CTRL_CFG;
+    union _spi_inter_en     INTER_EN;
+    union
+    {
+        uint8_t             CLOCK_DIV;
+        uint8_t             SLAVE_PRE;
+    };
+    uint8_t                 BUFFER;
+    union _spi_run_flag     RUN_FLAG;
+    union _spi_int_flag     INT_FLAG;
+    uint8_t                 FIFO_COUNT;
+    uint32_t                resv_8;
+    uint16_t                TOTAL_COUNT;
+    uint16_t                resv_0e;
+    uint8_t                 FIFO;
+    uint8_t                 resv_11[2];
+    uint8_t                 FIFO_COUNT1;
+    uint32_t                DMA_NOW;
+    uint32_t                DMA_BIG;
+    uint32_t                DMA_END;
+};
+CHECK_STRUCT_SIZE(struct spi_registers, 0x20);
+
+rt_err_t spi_cs_pin_assign(int spi_n, rt_base_t cs_pin);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 23 - 4
bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.c

@@ -11,6 +11,8 @@
 #include <rtdebug.h>
 #include "ch56x_sys.h"
 
+static uint32_t hclk_freq;
+
 rt_inline uint8_t _slp_clk_off0_irqn_bit(uint8_t irqn)
 {
     uint8_t bitpos;
@@ -221,6 +223,8 @@ int sys_hclk_set(uint32_t freq)
         sys->CLK_CFG_CTRL.reg = clk_cfg_ctrl_wdat(clksel);
         sys_safe_access_leave(sys);
         rt_hw_interrupt_enable(level);
+        /* save to hclk_freq for quick report */
+        sys_hclk_calc();
         clksel = 0;
     }
 
@@ -228,11 +232,24 @@ int sys_hclk_set(uint32_t freq)
 }
 
 /**
- * @brief   Get current HCLK frequency.
+ * @brief   Get saved HCLK frequency.
  *
- * @return  Returns current HCLK frequency (Hz).
+ *          Valid only if HCLK is set strickly with sys_hclk_set().
+ *          Use sys_hclk_calc() otherwise.
+ *
+ * @return  Returns saved HCLK frequency (Hz, 0 if not set yet).
  */
 uint32_t sys_hclk_get(void)
+{
+    return hclk_freq;
+}
+
+/**
+ * @brief   Get current HCLK frequency, calculated from hw setting.
+ *
+ * @return  Returns current HCLK frequency (Hz).
+ */
+uint32_t sys_hclk_calc(void)
 {
     volatile struct sys_registers *sys = (void *)SYS_REG_BASE;
 
@@ -240,10 +257,12 @@ uint32_t sys_hclk_get(void)
 
     if (sys->CLK_CFG_CTRL.sel_pll == CLK_SEL_PLL_USB_480M)
     {
-        return plldiv ? 480000000 / plldiv : 30000000;
+        hclk_freq = plldiv ? 480000000 / plldiv : 30000000;
     }
     else
     {
-        return plldiv ?  30000000 / plldiv : 2000000;
+        hclk_freq = plldiv ?  30000000 / plldiv : 2000000;
     }
+
+    return hclk_freq;
 }

+ 1 - 0
bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_sys.h

@@ -382,6 +382,7 @@ struct sys_registers
 
 CHECK_STRUCT_SIZE(struct sys_registers, 0x28);
 
+uint32_t sys_hclk_calc(void);
 uint32_t sys_hclk_get(void);
 int  sys_hclk_set(uint32_t freq);
 int  sys_clk_off_by_irqn(uint8_t irqn, int off);

+ 4 - 0
bsp/wch/risc-v/Libraries/ch56x_drivers/ch56x_timer.c

@@ -14,6 +14,10 @@
 #include "ch56x_timer.h"
 #include "isr_sp.h"
 
+#if !defined(BSP_USING_TMR0) && !defined(BSP_USING_TMR1) && !defined(BSP_USING_TMR2)
+    #error "Please define at least one TMRx"
+#endif
+
 struct hwtimer_device
 {
     struct rt_hwtimer_device parent;

+ 10 - 1
bsp/wch/risc-v/ch569w-evt/.config

@@ -123,7 +123,13 @@ CONFIG_RT_USING_PIN=y
 # CONFIG_RT_USING_PM is not set
 # CONFIG_RT_USING_RTC is not set
 # CONFIG_RT_USING_SDIO is not set
-# CONFIG_RT_USING_SPI is not set
+CONFIG_RT_USING_SPI=y
+# CONFIG_RT_USING_SPI_BITOPS is not set
+# CONFIG_RT_USING_QSPI is not set
+# CONFIG_RT_USING_SPI_MSD is not set
+# CONFIG_RT_USING_SFUD is not set
+# CONFIG_RT_USING_ENC28J60 is not set
+# CONFIG_RT_USING_SPI_WIFI is not set
 CONFIG_RT_USING_WDT=y
 # CONFIG_RT_USING_AUDIO is not set
 # CONFIG_RT_USING_SENSOR is not set
@@ -654,6 +660,9 @@ CONFIG_BSP_USING_TIMER=y
 CONFIG_BSP_USING_TMR0=y
 CONFIG_BSP_USING_TMR1=y
 # CONFIG_BSP_USING_TMR2 is not set
+CONFIG_BSP_USING_SPI=y
+CONFIG_BSP_USING_SPI0=y
+# CONFIG_BSP_USING_SPI1 is not set
 
 #
 # Onboard Peripheral Drivers

+ 105 - 28
bsp/wch/risc-v/ch569w-evt/applications/main.c

@@ -8,12 +8,14 @@
  * 2022-07-15     Emuzit            first version
  * 2022-07-20     Emuzit            add watchdog test
  * 2022-07-26     Emuzit            add hwtimer test
+ * 2022-07-30     Emuzit            add spi master test
  */
 #include <rtthread.h>
 #include <rtdebug.h>
 #include <drivers/pin.h>
 #include <drivers/watchdog.h>
 #include <drivers/hwtimer.h>
+#include <drivers/spi.h>
 #include "board.h"
 
 static const rt_base_t gpio_int_pins[8] = GPIO_INT_PINS;
@@ -34,12 +36,9 @@ static const uint32_t gpint_mode[] =
 static struct rt_mailbox *gpint_mb = RT_NULL;
 static struct rt_thread  *gpint_thread = RT_NULL;
 
-static rt_base_t led0, led1;
-
 static rt_device_t wdg_dev;
 
-static struct rt_device *tmr_dev_0;
-static struct rt_device *tmr_dev_1;
+static rt_base_t led0, led1;
 
 static void gpio_int_callback(void *pin)
 {
@@ -73,31 +72,13 @@ static void gpio_int_thread(void *param)
     }
 }
 
-#ifdef RT_USING_HWTIMER
-static rt_err_t tmr_timeout_cb(rt_device_t dev, rt_size_t size)
-{
-    rt_tick_t tick = rt_tick_get();
-
-    int tmr = (dev == tmr_dev_1) ? 1 : 0;
-
-    rt_kprintf("hwtimer %d timeout callback fucntion @tick %d\n", tmr, tick);
-
-    return RT_EOK;
-}
-#endif
-
-void main(void)
+static void test_gpio_int(void)
 {
-    rt_hwtimerval_t timerval;
-    rt_hwtimer_mode_t mode;
-    rt_size_t tsize;
-
-    uint32_t seconds;
     rt_err_t res;
-
     int i;
 
-    rt_kprintf("\nCH569W-R0-1v0, HCLK: %dMHz\n\n", sys_hclk_get() / 1000000);
+    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
+    rt_pin_write(LED1_PIN, led1 = PIN_HIGH);
 
     /* Enable all gpio interrupt with various modes.
      * LED0 or GND touching can be used to trigger pin interrupt.
@@ -136,8 +117,11 @@ void main(void)
             }
         }
     }
+}
 
 #ifdef RT_USING_WDT
+static void test_watchdog(uint32_t seconds)
+{
     /* Test watchdog with 30s timeout, keepalive with gpio interrupt.
      *
      * CAVEAT: With only 8-bit WDOG_COUNT and fixed clocking at Fsys/524288,
@@ -158,9 +142,32 @@ void main(void)
     {
         rt_kprintf("WDT_TIMEOUT in %d seconds, trigger gpio interrupt to keep alive.\n\n", seconds);
     }
+}
+#else
+    #define test_watchdog(tov)  do {} while(0)
 #endif
 
 #ifdef RT_USING_HWTIMER
+static struct rt_device *tmr_dev_0;
+static struct rt_device *tmr_dev_1;
+
+static rt_err_t tmr_timeout_cb(rt_device_t dev, rt_size_t size)
+{
+    rt_tick_t tick = rt_tick_get();
+
+    int tmr = (dev == tmr_dev_1) ? 1 : 0;
+
+    rt_kprintf("hwtimer %d timeout callback fucntion @tick %d\n", tmr, tick);
+
+    return RT_EOK;
+}
+
+static void test_hwtimer(void)
+{
+    rt_hwtimerval_t timerval;
+    rt_hwtimer_mode_t mode;
+    rt_size_t tsize;
+
     /* setup two timers, ONESHOT & PERIOD each
     */
     tmr_dev_0 = rt_device_find("timer0");
@@ -210,13 +217,83 @@ void main(void)
         }
         else
         {
-            rt_kprintf("timer1 started !\n");
+            rt_kprintf("timer1 started !\n\n");
         }
     }
+}
+#else
+    #define test_hwtimer()  do {} while(0)
 #endif
 
-    rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
-    rt_pin_write(LED1_PIN, led1 = PIN_HIGH);
+#ifdef RT_USING_SPI
+static struct rt_spi_device spi_dev_w25q;
+
+static void test_spi_master(void)
+{
+    struct rt_spi_configuration cfg;
+    struct rt_spi_message msg1, msg2;
+    rt_err_t res;
+
+    uint8_t buf[16];
+
+    cfg.max_hz = 25 * 1000000;
+    cfg.data_width = 8;
+    cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB | RT_SPI_CS_HIGH;
+
+    res = rt_spi_bus_attach_device(
+          &spi_dev_w25q, W25Q32_SPI_NAME, SPI0_BUS_NAME, (void *)W25Q32_CS_PIN);
+    if (res == RT_EOK && rt_spi_configure(&spi_dev_w25q, &cfg) == RT_EOK)
+    {
+        /* cmd : Read Manufacturer / Device ID (90h) */
+        buf[0] = 0x90;
+        /* address : 0 */
+        buf[1] = buf[2] = buf[3] = 0;
+        msg1.send_buf   = buf;
+        msg1.recv_buf   = RT_NULL;
+        msg1.length     = 4;
+        msg1.cs_take    = 1;
+        msg1.cs_release = 0;
+        msg1.next       = &msg2;
+
+        msg2.send_buf   = RT_NULL;
+        msg2.recv_buf   = buf;
+        msg2.length     = 2;
+        msg2.cs_take    = 0;
+        msg2.cs_release = 1;
+        msg2.next       = RT_NULL;
+
+        rt_spi_transfer_message(&spi_dev_w25q, &msg1);
+        rt_kprintf("use rt_spi_transfer_message() read w25q ID is:%x%x\n", buf[0], buf[1]);
+
+        /* cmd : Read Data (03h)  */
+        buf[0] = 0x03;
+        /* address : 0 */
+        buf[1] = buf[2] = buf[3] = 0;
+        msg2.length = 16;
+        if (rt_spi_transfer_message(&spi_dev_w25q, &msg1) == RT_NULL)
+        {
+            rt_kprintf("rt_spi_transfer_message() 16-byte-read DMA done\n\n");
+        }
+    }
+    else
+    {
+        rt_kprintf("w25q32 attach/configure failed (%d) !\n", res);
+    }
+}
+#else
+    #define test_spi_master()  do {} while(0)
+#endif
+
+void main(void)
+{
+    uint32_t wdog_timeout = 32;
+
+    rt_kprintf("\nCH569W-R0-1v0, HCLK: %dMHz\n\n", sys_hclk_get() / 1000000);
+
+    test_gpio_int();
+    test_watchdog(wdog_timeout);
+    test_hwtimer();
+    test_spi_master();
 
     rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
     rt_pin_write(LED0_PIN, led0 = PIN_LOW);

+ 15 - 0
bsp/wch/risc-v/ch569w-evt/board/Kconfig

@@ -51,6 +51,21 @@ config BSP_USING_TIMER
         bool "using TMR2"
         default n
     endif
+
+config BSP_USING_SPI
+    bool "using on-chip spi"
+    select RT_USING_SPI
+    default n
+
+    if BSP_USING_SPI
+        config BSP_USING_SPI0
+            bool "using SPI0"
+            default n
+
+        config BSP_USING_SPI1
+            bool "using SPI1"
+            default n
+    endif
 endmenu
 
 menu "Onboard Peripheral Drivers"

+ 4 - 0
bsp/wch/risc-v/ch569w-evt/board/board.h

@@ -13,11 +13,15 @@
 #include <stdint.h>
 #include "ch56x_sys.h"
 #include "ch56x_gpio.h"
+#include "ch56x_spi.h"
 
 #define LED0_PIN            GET_PIN(B, 24)
 #define LED1_PIN            GET_PIN(B, 22)
 #define LED2_PIN            GET_PIN(B, 23)
 
+#define W25Q32_CS_PIN       GET_PIN(A, 12)
+#define W25Q32_SPI_NAME     "spi00"
+
 #define SYS_HCLK_FREQ       80000000  // 80 MHz
 
 #define RAMX_SIZE           32  // USER_MEM 00/01/1x : 32/64/96 KB

+ 3 - 0
bsp/wch/risc-v/ch569w-evt/rtconfig.h

@@ -72,6 +72,7 @@
 #define RT_SERIAL_RB_BUFSZ 64
 #define RT_USING_HWTIMER
 #define RT_USING_PIN
+#define RT_USING_SPI
 #define RT_USING_WDT
 
 /* Using USB */
@@ -183,6 +184,8 @@
 #define BSP_USING_TIMER
 #define BSP_USING_TMR0
 #define BSP_USING_TMR1
+#define BSP_USING_SPI
+#define BSP_USING_SPI0
 
 /* Onboard Peripheral Drivers */