推送软件spi模块

This commit is contained in:
MacRsh
2023-01-28 02:23:30 +08:00
parent 4ae19a302f
commit a31a1e8bd3
4 changed files with 595 additions and 0 deletions

View File

@@ -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
## 感谢各位对本仓库的贡献!

View File

@@ -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 */

View File

@@ -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_ */

View File

@@ -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_ */