From a31a1e8bd31430d659edc3a11839d383631f65a3 Mon Sep 17 00:00:00 2001 From: MacRsh <1063220965@qq.com> Date: Sat, 28 Jan 2023 02:23:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E9=80=81=E8=BD=AF=E4=BB=B6spi?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- device/mr_soft_spi/README.md | 161 +++++++++++++++ device/mr_soft_spi/mr_soft_spi.c | 299 +++++++++++++++++++++++++++ device/mr_soft_spi/mr_soft_spi.h | 91 ++++++++ device/mr_soft_spi/mr_soft_spi_cfg.h | 44 ++++ 4 files changed, 595 insertions(+) create mode 100644 device/mr_soft_spi/README.md create mode 100644 device/mr_soft_spi/mr_soft_spi.c create mode 100644 device/mr_soft_spi/mr_soft_spi.h create mode 100644 device/mr_soft_spi/mr_soft_spi_cfg.h diff --git a/device/mr_soft_spi/README.md b/device/mr_soft_spi/README.md new file mode 100644 index 0000000..c4346e9 --- /dev/null +++ b/device/mr_soft_spi/README.md @@ -0,0 +1,161 @@ +`mr-library` `mr-soft-spi` +# 简介 +**mr-soft-spi** 模块为 **mr-library** 项目下的可裁剪模块,以C语言编写,可快速移植到各种平台(主要以嵌入式mcu为主)。 +**mr-soft-spi** 模块通过 **io** 模拟实现 **spi** 协议。 + + +---------- +# **mr-soft-spi** 特点 +* 资源占用低。 +- 简单易用。 +- 通用性强,移植便捷。 +* 松解耦,模块化。 + + +---------- + +# 代码目录 +**mr-soft-spi** 源代码目录结构如下图所示: +|名称|描述| +|:---|:---| +|mr_soft_spi.c|源代码| +|mr_soft_spi.h|头文件| +|mr_soft_spi_cfg.h|配置文件| + + +---------- + + +# 需适配接口 +|函数|描述| +|:---|:---| +|void (*set_clk)(mr_uint8_t level)|设置 spi-clk 电平| +|void (*set_mosi)(mr_uint8_t level)|设置 spi-mosi 电平| +|mr_uint8_t (*get_miso)(void)|读取 spi-miso 电平| +|void (*set_cs)(mr_uint8_t level)|设置 spi-cs 电平| + + +---------- + + +# 可使用接口 +|函数|描述| +|:---|:---| +|void mr_soft_spi_attach(struct mr_soft_spi *spi, struct mr_soft_spi_bus *spi_bus)|将 spi 设备挂载到 spi 总线| +|mr_err_t mr_soft_spi_transfer(struct mr_soft_spi *spi, struct mr_soft_spi_msg msg)|通过 spi 总线发送 spi 消息| + + +---------- + + +# 配置选项 +可通过修改配置文件( mr_soft_spi_cfg.h )实现功能修改 +|配置|描述| +|:---|:---| +|USING_SOFT_SPI|使用 soft-spi 功能| +|U_SOFT_SPI_BUS_CS_VALID|spi-cs 引脚仅在使用时有效| +|U_SOFT_SPI_DEBUG|使用 spi-debug 检测参数功能| +可选配置 +|配置|描述| +|:---|:---| +|mr_hw_interrupt_disable()|rtos 临界段关闭中断| +|mr_hw_interrupt_enable(EX)|rtos 临界段使能中断| + + +---------- + +# 使用流程 +1. 复制 `模块(mr_soft_spi)` 文件夹到您的工程文件。 +2. 按需修改 `配置文件(mr_soft_spi_cfg.h)`的配置. +3. 查看 `源代码(mr_soft_spi.c)` 文件中的 user specification,按说明适配接口。 +4. 将 `头文件(mr_soft_spi.h)` 引用到您的工程。 +5. 开始愉快的使用。 + + +---------- + +# 使用示例 +``` +/* -------------------- 配置 -------------------- */ + +/* 创建一条 spi 总线 */ +struct mr_soft_spi_bus spi_bus; + +/* 适配 spi 总线接口 */ +void set_clk(mr_uint8_t level) +{ + GPIO_WriteBit(GPIOA,GPIO_Pin_0,level); +} + +void set_mosi(mr_uint8_t level) +{ + GPIO_WriteBit(GPIOA,GPIO_Pin_1,level); +} + +mr_uint8_t get_miso(void) +{ + return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2); +} + +/* 配置 spi 总线 */ +spi_bus.set_clk = set_clk; +spi_bus.set_mosi = set_mosi; +spi_bus.get_miso = get_miso; +spi_bus.lock = MR_UNLOCK; +spi_bus.owner = MR_NULL; + +/* 创建一个 spi 设备 */ +struct mr_soft_spi spi_device; + +/* 适配 spi 设备接口 */ +void set_cs(mr_uint8_t level) +{ + GPIO_WriteBit(GPIOA,GPIO_Pin_3,level); +} + +/* 配置 spi 设备 */ +spi_device.mode = SPI_MODE_0; //SPI MODE 0 +spi_device.cs_active = LEVEL_LOW; //CS 引脚低电平有效 +spi_device.set_cs = set_cs; + +/* -------------------- 使用 -------------------- */ + +int main(void) +{ + /* 需要发送的数据 */ + mr_uint8_t buffer[10]={0,1,2,3,4,5,6,7,8,9}; + + /* 初始化 gpio */ + GPIO_InitTypeDef GPIO_InitStructure = {0}; + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOA, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* 挂载 spi 设备到 spi 总线 */ + mr_soft_spi_attach(&spi_device,&spi_bus); + + /* 创建 spi 消息 */ + struct mr_soft_spi_msg spi_msg; + spi_msg.send_buffer = buffer; //发送数据地址 + spi_msg.send_size = 10; //发送数据数量 + spi_msg.recv_buffer = MR_NULL; //读取数据地址 + spi_msg.recv_size = 0; //读取数据数量 + spi_msg.read_write = SPI_WR; //只读模式 + + /* 发送消息 */ + mr_soft_spi_transfer(&spi_device,spi_msg); +} + +``` + +---------- +# 贡献代码 +如果您在使用 **mr-soft-spi** 模块中遇到了 bug 或是 您有自己的想法,欢迎您提交 pr 或者联系我(email):macrsh@outlook.com + +## 感谢各位对本仓库的贡献! \ No newline at end of file diff --git a/device/mr_soft_spi/mr_soft_spi.c b/device/mr_soft_spi/mr_soft_spi.c new file mode 100644 index 0000000..a400acc --- /dev/null +++ b/device/mr_soft_spi/mr_soft_spi.c @@ -0,0 +1,299 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-27 MacRsh first version + */ + +#include "mr_soft_spi.h" + +/** user specification : + * please set the configuration file first !!! + * + * if you want to use the soft-spi library, adapt the following api: + * (must to adapt) 1. struct mr_soft_spi_bus : create the spi-bus + * (must to adapt) 2. mr_soft_spi_bus : adapt the spi-bus functions + * set_clk : set the clock line to high/low level + * set_mosi : set the mosi line to high/low level + * get_miso : get the miso line level + * (must to adapt) 3. struct mr_soft_spi : create the spi device + * (must to adapt) 4. mr_soft_spi : adapt the spi device functions + * set_cs : set the cs line to high/low level + * + * then, you can use the following api: + * (can use) mr_soft_spi_attach : attach spi device to spi-bus + * (can use) mr_soft_spi_transfer : the spi device through spi-bus to transfer the spi message + * + */ + +#if (USING_SOFT_SPI == __CONFIG_ENABLE) + +/** + * This function will attach spi device to spi-bus + * + * @param spi the spi device to be mounted + * @param spi_bus the spi-bus + * + */ +void mr_soft_spi_attach(struct mr_soft_spi *spi, struct mr_soft_spi_bus *spi_bus) +{ + /* check function args */ + MR_DEBUG_ARGS_NULL(spi,); + MR_DEBUG_ARGS_NULL(spi_bus,); + + spi->bus = spi_bus; +} + +#define mr_soft_spi_bus_delay(x) for(mr_size_t temp = x; temp--;) + +/** + * This function will transmit data through spi-bus + * + * @param spi the spi device + * @param send_buffer the send data + * + * @return the receive data + * + */ +static mr_uint8_t mr_soft_spi_bus_transmit(struct mr_soft_spi *spi, mr_uint8_t send_buffer) +{ + mr_uint8_t recv_buffer = 0; + mr_uint8_t clk_level; + mr_uint8_t i; + + if(spi->mode == 0 || spi->mode == 1) + clk_level = LEVEL_LOW; + else + clk_level = LEVEL_HIGH; + + spi->bus->set_clk(clk_level); + clk_level = ! clk_level; + + if(spi->mode % 2 == 0) + { + for(i = 8; i > 0; i --) + { + /* send */ + if(send_buffer & 0x80) + spi->bus->set_mosi(LEVEL_HIGH); + else + spi->bus->set_mosi(LEVEL_LOW); + send_buffer <<= 1; + + mr_soft_spi_bus_delay(100); + spi->bus->set_clk(clk_level); + clk_level = ! clk_level; + + /* receive */ + recv_buffer <<= 1; + recv_buffer |= spi->bus->get_miso(); + + mr_soft_spi_bus_delay(100); + spi->bus->set_clk(clk_level); + clk_level = ! clk_level; + } + } + else + { + for(i = 8; i > 0; i --) + { + spi->bus->set_clk(clk_level); + clk_level = ! clk_level; + + /* send */ + if(send_buffer & 0x80) + spi->bus->set_mosi(LEVEL_HIGH); + else + spi->bus->set_mosi(LEVEL_LOW); + send_buffer <<= 1; + + mr_soft_spi_bus_delay(100); + spi->bus->set_clk(clk_level); + clk_level = ! clk_level; + + /* receive */ + recv_buffer <<= 1; + recv_buffer |= spi->bus->get_miso(); + + mr_soft_spi_bus_delay(100); + } + } + + return recv_buffer; +} + +/** + * This function will the spi device take the spi-bus right of use + * + * @param spi the spi device + * + * @return the error code, MR_EOK on initialization successfully. + * + */ +static mr_err_t mr_soft_spi_bus_take(struct mr_soft_spi *spi) +{ + mr_uint8_t spi_bus_lock; + mr_base_t level; + + /* check function args */ + MR_DEBUG_ARGS_NULL(spi,-MR_EINVAL); + MR_DEBUG_ARGS_NULL(spi->set_cs,-MR_ERROR); + + /* check spi-bus owner */ + if(spi->bus->owner != spi) + { + /* check mutex lock */ + do{ + spi_bus_lock = spi->bus->lock; + } while(spi_bus_lock != MR_UNLOCK); + + /* rtos interrupt disable*/ + level = mr_hw_interrupt_disable(); + + /* lock mutex lock */ + spi->bus->lock = MR_LOCK; + + /* rtos interrupt enable*/ + mr_hw_interrupt_enable(level); + + /* stop spi cs */ + if(spi->bus->owner != MR_NULL) + spi->bus->owner->set_cs(!spi->bus->owner->cs_active); + + /* exchange spi-bus owner */ + spi->bus->owner = spi; + + /* start spi cs */ + spi->set_cs(spi->cs_active); + } + else + { + /* rtos interrupt disable*/ + level = mr_hw_interrupt_disable(); + + /* lock mutex lock */ + spi->bus->lock = MR_LOCK; + + /* rtos interrupt enable*/ + mr_hw_interrupt_enable(level); + +#if (U_SOFT_SPI_BUS_CS_VALID == __CONFIG_ENABLE) + /* start spi cs */ + spi->set_cs(spi->cs_active); +#endif + } + + return MR_EOK; +} + +/** + * This function will the spi device release the spi-bus right of use + * + * @param spi the spi device + * + * @return the error code, MR_EOK on initialization successfully. + * + */ +static mr_err_t mr_soft_spi_bus_release(struct mr_soft_spi *spi) +{ + /* check function args */ + MR_DEBUG_ARGS_NULL(spi,-MR_EINVAL); + MR_DEBUG_ARGS_NULL(spi->set_cs,-MR_ERROR); + + /* check spi-bus owner */ + if(spi->bus->owner == spi) + { +#if (U_SOFT_SPI_BUS_CS_VALID == __CONFIG_ENABLE) + /* stop spi cs */ + spi->set_cs(!spi->cs_active); +#endif + /* unlock mutex lock */ + spi->bus->lock = MR_UNLOCK; + return MR_EOK; + } + + return -MR_ERROR; +} + +/** + * This function will the spi device through spi-bus to transfer the spi message + * + * @param spi the spi device + * @param msg the spi message + * + * @return the error code, MR_EOK on initialization successfully. + * + */ +mr_err_t mr_soft_spi_transfer(struct mr_soft_spi *spi, struct mr_soft_spi_msg msg) +{ + mr_err_t ret; + + /* check function args */ + MR_DEBUG_ARGS_NULL(spi,-MR_EINVAL); + MR_DEBUG_ARGS_IF(msg.read_write > SPI_WR_THEN_RD,-MR_EINVAL); + + /* take spi-bus */ + ret = mr_soft_spi_bus_take(spi); + if(ret != MR_EOK) + return ret; + + if(msg.read_write == SPI_WR || msg.recv_buffer == MR_NULL) + msg.recv_size = 0; + if(msg.read_write == SPI_RD || msg.send_buffer == MR_NULL) + msg.send_size = 0; + + switch (msg.read_write) { + case SPI_RD: + /* receive */ + while (msg.recv_size) { + *msg.recv_buffer = mr_soft_spi_bus_transmit(spi,0u); + + ++msg.recv_buffer; + --msg.recv_size; + } + break; + case SPI_WR: + /* send */ + while (msg.send_size) { + mr_soft_spi_bus_transmit(spi,*msg.send_buffer); + + ++msg.send_buffer; + --msg.send_size; + } + break; + case SPI_WR_THEN_RD: + /* send */ + while (msg.send_size) { + mr_soft_spi_bus_transmit(spi,*msg.send_buffer); + + ++msg.send_buffer; + --msg.send_size; + } + /* receive */ + while (msg.recv_size) { + *msg.recv_buffer = mr_soft_spi_bus_transmit(spi,0u); + + ++msg.recv_buffer; + --msg.recv_size; + } + break; + case SPI_RDWR: + /* transmit */ + while (msg.send_size) { + *msg.recv_buffer = mr_soft_spi_bus_transmit(spi,*msg.send_buffer); + + ++msg.send_buffer; + ++msg.recv_buffer; + --msg.send_size; + } + break; + } + + /* release spi-bus */ + mr_soft_spi_bus_release(spi); + + return MR_EOK; +} + +#endif /* end of USING_SOFT_SPI */ diff --git a/device/mr_soft_spi/mr_soft_spi.h b/device/mr_soft_spi/mr_soft_spi.h new file mode 100644 index 0000000..9300325 --- /dev/null +++ b/device/mr_soft_spi/mr_soft_spi.h @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-14 MacRsh first version + */ + +#ifndef _SOFT_SPI_H_ +#define _SOFT_SPI_H_ + +#include "mr_soft_spi_cfg.h" + +#if (USING_SOFT_SPI == __CONFIG_ENABLE) + +//<<< base data type(please check that there is no conflict then change data type to typedef) >>> +#ifndef _DRIVER_DEF_H_ +typedef unsigned char mr_uint8_t; +typedef unsigned int mr_size_t; +typedef long mr_base_t; +typedef mr_base_t mr_err_t; +#define MR_NULL 0 +#define MR_EOK 0 /**< There is no error */ +#define MR_ERROR 1 /**< A generic error happens */ +#define MR_EINVAL 10 /**< Invalid argument */ +/* lock type definitions */ +#define MR_LOCK 1 /**< lock */ +#define MR_UNLOCK 0 /**< unlock */ +/* debug */ +#if (U_SOFT_SPI_DEBUG == __CONFIG_ENABLE) +#define MR_DEBUG_ARGS_NULL(args,ret) \ + if(args == MR_NULL) return ret +#define MR_DEBUG_ARGS_IF(args_if,ret) \ + if(args_if) return ret +#else +#define MR_DEBUG_ARGS_NULL(args,ret); +#define MR_DEBUG_ARGS_IF(args_if,ret); +#endif /* end of U_SOFT_SPI_DEBUG */ +#endif /* end of _DRIVER_DEF_H_ */ + +#define LEVEL_HIGH 1 +#define LEVEL_LOW 0 + +#define SPI_MODE_0 0 /* CPOL = 0, CPHA = 0 */ +#define SPI_MODE_1 1 /* CPOL = 0, CPHA = 1 */ +#define SPI_MODE_2 2 /* CPOL = 1, CPHA = 0 */ +#define SPI_MODE_3 3 /* CPOL = 1, CPHA = 1 */ + +#define SPI_WR 0 /* writing only */ +#define SPI_RD 1 /* reading only */ +#define SPI_RDWR 2 /* writing-while-reading */ +#define SPI_WR_THEN_RD 3 /* writing first, reading later */ + +struct mr_soft_spi_msg +{ + mr_uint8_t read_write; /* SOFT_SPI_WR/ SOFT_SPI_RD/ SOFT_SPI_RDWR/ SOFT_SPI_WR_THEN_RD */ + mr_uint8_t *send_buffer; + mr_size_t send_size; + mr_uint8_t *recv_buffer; + mr_size_t recv_size; +}; + +struct mr_soft_spi +{ + mr_uint8_t mode :2; + mr_uint8_t cs_active :1; + struct mr_soft_spi_bus *bus; + + void (*set_cs)(mr_uint8_t level); +}; + + +struct mr_soft_spi_bus +{ + void (*set_clk)(mr_uint8_t level); + void (*set_mosi)(mr_uint8_t level); + mr_uint8_t (*get_miso)(void); + + struct mr_soft_spi *owner; + mr_uint8_t lock; +}; + +/** + * Export soft-spi functions + */ +void mr_soft_spi_attach(struct mr_soft_spi *spi, struct mr_soft_spi_bus *spi_bus); +mr_err_t mr_soft_spi_transfer(struct mr_soft_spi *spi, struct mr_soft_spi_msg msg); + +#endif /* end of USING_SOFT_SPI */ + +#endif /* end of _SOFT_SPI_H_ */ diff --git a/device/mr_soft_spi/mr_soft_spi_cfg.h b/device/mr_soft_spi/mr_soft_spi_cfg.h new file mode 100644 index 0000000..9e70c7e --- /dev/null +++ b/device/mr_soft_spi/mr_soft_spi_cfg.h @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-01-14 MacRsh first version + */ + +#ifndef _SOFT_SPI_CFG_H_ +#define _SOFT_SPI_CFG_H_ + +/** user specification : + * ( __CONFIG_ENABLE ) it means enable function + * ( __CONFIG_DISABLE ) it means disable function + * ( //< must > ) it means this option need user complement + * ( //< const > ) it means this option unchangeable + * ( //< change able > ) it means this option can change, you could change parameter or annotation parameter + * ( //< optional > ) it means this option is optional, not essential + * ( //<<< XXXX >>> ) it means interpretation this define means + */ + +#define __CONFIG_ENABLE 1 +#define __CONFIG_DISABLE 0 + +//<---------------------------- FRAME -----------------------------------------> +//< must > +//<<< If you not using mr_device frame, please remove the following include, replace with your chip head-file >>> +//#include "device_def.h" +#include "ch32v30x.h" +//< change able > +//<<< Using soft-spi >>> +#define USING_SOFT_SPI __CONFIG_ENABLE +//<<< spi-bus cs valid only when using, default: __CONFIG_ENABLE >>> +#define U_SOFT_SPI_BUS_CS_VALID __CONFIG_ENABLE +//<<< Using debug >>> +#define U_SOFT_SPI_DEBUG __CONFIG_ENABLE + +//< optional > +//<<< rtos critical, disable interrupt, replace with rtos function, default: 0 >>> +#define mr_hw_interrupt_disable() 0 +//<<< rtos critical, enable interrupt >>> +#define mr_hw_interrupt_enable(EX); + +#endif /* end of _SOFT_SPI_CFG_H_ */