2.6. SPI
2.6.1. Overview
AC792N有3组通用SPI控制器。
支持STD、1WIRE、DUAL和QUAD模式(其中SPI2不支持DUAL和QUAD模式)。
支持主机和从机模式。
支持DMA收发。
输出时钟可配,由时钟源 LSB频率和分频器决定。
支持配置采样边沿 、更新边沿、CLK信号线空闲电平配置。
SPI的信号线支持crossbar功能,能够映射到任意普通IO。
apps/common/example/peripheral/spi
,可使用hello_demo工程测试,使用时在app_config.h
中使能相应例程的宏开关(比如USE_SPI_MASTER_TEST_DEMO
)
。2.6.2. SPI参数配置
2.6.2.1. 1.配置结构体
spi_platform_data
结构体
struct spi_platform_data {
spi_mode_t mode; ///< 模式
u8 irq; ///< 中断号,每个SPI为固定值
u8 priority; ///< 中断优先级
u8 cpu_id; ///< 中断注册所在cpu
u8 hd_level; ///< IO强驱等级
u8 uf_ie_enable; ///< dma fifo UnderFlow irq enable(for debug)
u8 of_ie_enable; ///< dma fifo OverFlow irq enable(for debug)
u32 clk; ///< 波特率配置
u32 sync_wait_timeout; ///< 同步等待超时时间,单位ms
spi_role_t role; ///< 主、从机配置
spi_attr_t attr; ///< clk/updata/sample的电平/边沿配置
spi_io_t io; ///< 使用的引脚配置
JL_SPI_TypeDef *reg; ///< 使用的寄存器组
};
模式
mode
模式 |
说明 |
---|---|
SPI_STD_MODE |
标准模式,双向,DO作输出,DI作输入 |
SPI_1WIRE_MODE |
单线模式,单向,仅DO作输入/输出 |
SPI_DUAL_MODE |
双线模式,单向,DO和DI作输入/输出 |
SPI_QUAD_MODE |
四线模式,单向,DO、DI、D2、D3作输入/输出 |
IO强驱档位
hd_level
支持0~3档强驱,强驱档位电流见GPIO相关章节。CLK/DO/DI/D2/D3固定使用相同的档位hd_level(CS脚固定为0档强驱)。
输出时钟频率
clk
clk参数仅主机模式有效(因为芯片SPI作从机时时钟由主机提供)。
时钟频率由
SPI的时钟源LSB
和分频系数div
决定。计算公式clk = LSB / (div + 1)
,其中div系数范围为0~255整数。如果LSB = 48M,那么SPI的可调时钟范围为[48000K / 256, 48000K]
->[187.5K, 48000K]
。时钟频率无法设置任意值。由于分频系数div被限制为0-255的整数,导致输出时钟频率clk只能是基准频率LSB除以1至256的整数所得的离散值集合,无法覆盖这些特定值之间的任意频率。比如,LSB = 48M时,设置clk = 25M,实际输出48M;设置clk = 20M,实际输出24M。
主从机
role
SPI_MODE_MASTER——主机模式
SPI_MODE_SLAVE——从机模式
采样边沿、更新边沿和CLK空闲电平
attr
采样边沿
,指SPI作为数据接收方
,在时钟信号的相应边沿进行采样。更新边沿
,指SPI作为数据发送方
,在时钟信号的相应边沿进行输出。CLK空闲电平
,指SPI作为主机
,启动数据收/发前后的空闲状态时的CLK线的电平。
参数 |
说明 |
---|---|
SPI_SCLK_H_UPH_SMPL |
CLK空闲电平高,上升沿更新,下降沿采样 |
SPI_SCLK_H_UPL_SMPH |
CLK空闲电平高,下降沿更新,上升沿采样 |
SPI_SCLK_H_UPH_SMPH |
CLK空闲电平高,上升沿更新,上升沿采样 |
SPI_SCLK_H_UPL_SMPL |
CLK空闲电平高,下降沿更新,下降沿采样 |
SPI_SCLK_L_UPH_SMPL |
CLK空闲电平低,上升沿更新,下降沿采样 |
SPI_SCLK_L_UPL_SMPH |
CLK空闲电平低,下降沿更新,上升沿采样 |
SPI_SCLK_L_UPH_SMPH |
CLK空闲电平低,上升沿更新,上升沿采样 |
SPI_SCLK_L_UPL_SMPL |
CLK空闲电平低,下降沿更新,下降沿采样 |
正常不要去使用采样和更新边沿相同的配置,比如SPI_SCLK_H_UPH_SMPH。
同步收发超时时间
sync_wait_timeout
设置默认的等待
同步收/发
结束的超时时间。SPI打开后可通过IOCTL_SPI_SET_SYNC_TIMEOUT
修改。
结构体配置示例
// board_develop_AC792XX.h
#define TCFG_SPI1_ENABLE 1
#define TCFG_SPI1_CS_IO -1
#define TCFG_SPI1_CLK_IO IO_PORTA_04
#define TCFG_SPI1_DO_IO IO_PORTA_05
#define TCFG_SPI1_DI_IO IO_PORTA_06
#define TCFG_SPI1_D2_IO -1 // IO_PORTA_07
#define TCFG_SPI1_D3_IO -1 // IO_PORTA_08
#define TCFG_SPI1_BAUDRATE 10000000
#define TCFG_SPI1_HD_LEVEL 0
#define TCFG_SPI1_MODE SPI_STD_MODE
#define TCFG_SPI1_ATTR SPI_SCLK_L_UPL_SMPH
#define TCFG_SPI1_ROLE SPI_MODE_MASTER
// board_develop.c
SPI1_PLATFORM_DATA_BEGIN(spi1_data)
.io = {
.cs_pin = TCFG_SPI1_CS_IO,
.di_pin = TCFG_SPI1_DI_IO,
.do_pin = TCFG_SPI1_DO_IO,
.clk_pin = TCFG_SPI1_CLK_IO,
.d2_pin = TCFG_SPI1_D2_IO,
.d3_pin = TCFG_SPI1_D3_IO,
},
.clk = TCFG_SPI1_BAUDRATE,
.mode = TCFG_SPI1_MODE,
.priority = 6,
.cpu_id = 1,
.hd_level = TCFG_SPI1_HD_LEVEL,
.attr = TCFG_SPI1_ATTR,
.role = TCFG_SPI1_ROLE,
SPI1_PLATFORM_DATA_END()
2.6.2.2. 2.注册设备
在REGISTER_DEVICES(device_table) = { };
中添加注册项。
REGISTER_DEVICES(device_table) = {
{ "spi1", &spi_dev_ops, (void *)&spi1_data },
};
2.6.3. SPI驱动使用
1.打开设备
dev_open()
打开设备。如果open设备时,传入了spi_platform_data
类型的配置参数,那么SPI驱动将按照传入参数的配置进行初始化。如果传入的参数是NULL
,则按照Board中REGISTER_DEVICES()
注册的配置进行初始化。spi_platform_data
类型变量的地址。dev_open()
多次。struct spi_platform_data spi1_data; ///< 自定义配置
void *hdl = dev_open("spi1", (u32)&spi1_data); ///< 传入配置参数,则按传入的参数初始化
void *hdl = dev_open("spi1", NULL); ///< 传入NULL,则按Board中注册的配置初始化
2.关闭设备
dev_close()
关闭设备。dev_open()
打开n次,关闭时则需要执行dev_close()
n次才能关闭。dev_close(hdl);
3.接收数据
dev_read(hdl, buf, cnt)
读取SPI收到的数据。该接口将会把SPI接收到的数据放入指定的buf
中,接收cnt
个数据。默认同步接收数据,可配置异步接收数据(异步接收时,下次read时会等待上次read结束)。同步接收数据时,可以配置是否释放cpu。SPI作从机时,可配置超时时间。IOCTL_SPI_SET_USE_SEM
、IOCTL_SPI_SET_TRIGGER_WAIT_SEM_TIME_THRE
、IOCTL_SPI_SET_ASYNC_RECV
、IOCTL_SPI_9BIT_EN
。u8 recv_buff[64];
dev_read(hdl, recv_buf, sizeof(recv_buf);
4.发送数据
dev_write(hdl, buf, cnt)
发送数据。该接口会将指定
buf
中的cnt
个数据发送出去。默认同步发送数据,可配置异步发送数据(异步接收时,下次write时会等待上次write结束)。同步发送数据时,可以配置是否释放cpu。SPI作从机时,可配置超时时间。IOCTL_SPI_SET_USE_SEM
、IOCTL_SPI_SET_TRIGGER_WAIT_SEM_TIME_THRE
、IOCTL_SPI_SET_ASYNC_SEND
、IOCTL_SPI_9BIT_EN
。u8 send_buf[] = {0x00, 0x11, 0x22, 0x33};
dev_write(hdl, send_buf, sizeof(send_buf));
2.6.4. SPI IOCTL命令
ioctl函数原型:
/**
* #brief dev_ioctl:用于对设备进行控制和参数的修改
* @param device 设备句柄
* @param cmd 设备控制命令
* @param arg 控制参数
* @return 0:正常执行完成 -EINVAL(-22):找不到该命令
*/
int dev_ioctl(void *device, int cmd, u32 arg);
SPI设备的IOCTL命令:
#define SPI_MAGIC 'S'
#define IOCTL_SPI_SET_CS_STATUS _IOW(SPI_MAGIC, 1, u8) ///< 设置cs状态
#define IOCTL_SPI_SET_CS_PORT_FUNC _IOW(SPI_MAGIC, 2, void (*)(u8)) ///< 设置cs函数
#define IOCTL_SPI_SEND_BYTE _IOW(SPI_MAGIC, 3, u8) ///< 发送一个字节
#define IOCTL_SPI_READ_BYTE _IOR(SPI_MAGIC, 4, u8 *) ///< 读取一个字节
#define IOCTL_SPI_SEND_CMD_2BIT_MODE _IOW(SPI_MAGIC, 5, spi_2bit_data_t *) ///< 以2bit模式发命令
#define IOCTL_SPI_SET_USE_SEM _IOW(SPI_MAGIC, 6, u8) ///< 设置是否使用信号量,0:使用 非0:不使用
#define IOCTL_SPI_SET_TRIGGER_WAIT_SEM_TIME_THRE _IOW(SPI_MAGIC, 7, u32) ///< 设置TX使用信号量的时间阈值
#define IOCTL_SPI_SET_IRQ_FUNC _IOW(SPI_MAGIC, 8, void (*)(void)) ///< 设置中断函数
#define IOCTL_SPI_SET_ASYNC_SEND _IOW(SPI_MAGIC, 9, u8) ///< 设置是否同步方式发包
#define IOCTL_SPI_WAIT_ASYNC_SEND_END _IO(SPI_MAGIC, 10) ///< 等待非同步发送结束
#define IOCTL_SPI_SET_ASYNC_RECV _IOW(SPI_MAGIC, 11, u8) ///< 设置是否同步方式发包
#define IOCTL_SPI_WAIT_ASYNC_RECV_END _IO(SPI_MAGIC, 12) ///< 等待非同步发送结束
#define IOCTL_SPI_SET_SYNC_TIMEOUT _IOW(SPI_MAGIC, 13,u32) ///< 设置同步等待超时时间,单位ms
#define IOCTL_SPI_FDX_TR_DATA _IOR(SPI_MAGIC, 14,spi_fdx_data_t *) ///< 全双工收发数据,仅STD模式支持
#define IOCTL_SPI_SET_IRQ_EVENT_CB _IOW(SPI_MAGIC, 15,void (*)(spi_irq_event_t)) ///< 设置中断事件回调函数
#define IOCTL_SPI_SET_BLOCK_DATA_CFG _IOW(SPI_MAGIC, 16,spi_block_data_cfg_t *) ///< 设置block data的配置
#define IOCTL_SPI_READ_BLOCK_DATA _IOR(SPI_MAGIC, 17,int *) ///< 读取block data
#define IOCTL_SPI_FREE_BLOCK_DATA _IO(SPI_MAGIC, 18) ///< 释放一个block data
#define IOCTL_SPI_SET_BAUDRATE _IOW(SPI_MAGIC, 19,u32) ///< 设置波特率
#define IOCTL_SPI_9BIT_EN _IOW(SPI_MAGIC, 20,u8) ///< 9BIT模式开/关,0:关 非0:开,推屏用
#define IOCTL_SPI_9BIT_DATA _IOW(SPI_MAGIC, 21,u8) ///< 9BIT数据,0:低 非0:高,推屏用
1.IOCTL_SPI_SET_CS_STATUS
IOCTL_SPI_SET_CS_PORT_FUNC
配置了CS函数,不管是否配置了CS的IO,都由配置的CS函数来改变状态。CS
IO和CS函数都未配置,该命令直接退出。dev_ioctl(hdl, IOCTL_SPI_SET_CS_STATUS, 0); ///< CS IO输出低电平
2.IOCTL_SPI_SET_CS_PORT_FUNC
void spi_cs_func(u8 status)
{
if (status) {
// Todo
} else {
// Todo
}
}
dev_ioctl(hdl, IOCTL_SPI_SET_CS_PORT_FUNC, (u32)spi_cs_func);
3.IOCTL_SPI_SEND_BYTE
dev_ioctl(hdl, IOCTL_SPI_SEND_BYTE, 0x11);///< 发送0x11
4.IOCTL_SPI_READ_BYTE
u8 rx_data;
dev_ioctl(hdl, IOCTL_SPI_READ_BYTE, (u32)&rx_data);
5.IOCTL_SPI_SEND_CMD_2BIT_MODE
SPI_1WIRE_MODE
外的模式时,可以使用该命令临时切换成SPI_DUAL_MODE
发送数据,发送完成后自动恢复回原模式。spi_2bit_data_t
结构的数据的地址。static u8 data[SPI_LEN] ALIGNE(32);
static spi_2bit_data_t spi_2data;
spi_2data.len = SPI_LEN;
spi_2data.buf = data;
dev_ioctl(spi_test_hdl, IOCTL_SPI_SEND_CMD_2BIT_MODE, (u32)&spi_2data);
6.IOCTL_SPI_SET_USE_SEM
while
还是信号量
方式(即是否释放CPU)来等待SPI模块收/发结束。dev_ioctl(hdl, IOCTL_SPI_SET_USE_SEM, 1);///< 使用信号量方式等待
7.IOCTL_SPI_SET_TRIGGER_WAIT_SEM_TIME_THRE
us
。IOCTL_SPI_SET_USE_SEM
开启使用信号量,才可以使用该功能。dev_ioctl(hdl, IOCTL_SPI_SET_TRIGGER_WAIT_SEM_TIME_THRE, 10);
8.IOCTL_SPI_SET_IRQ_FUNC
static ___interrupt void spi_irq_func(void) sec(.volatile_ram_code)
{
// Todo
}
dev_ioctl(hdl, IOCTL_SPI_SET_IRQ_FUNC, spi_irq_func);
9.IOCTL_SPI_SET_ASYNC_SEND
dev_ioctl(hdl, IOCTL_SPI_SET_ASYNC_SEND, 1); ///< 使用异步方式发送数据
10.IOCTL_SPI_WAIT_ASYNC_SEND_END
IOCTL_SPI_SET_ASYNC_SEND
开启异步发送数据时,该命令才有效。dev_ioctl(hdl, IOCTL_SPI_WAIT_ASYNC_SEND_END, 0);
11.IOCTL_SPI_SET_ASYNC_RECV
dev_ioctl(hdl, IOCTL_SPI_SET_ASYNC_RECV, 1);///< 使用异步方式接收数据
12.IOCTL_SPI_WAIT_ASYNC_RECV_END
IOCTL_SPI_SET_ASYNC_RECV
开启异步接收数据时,该命令才有效。dev_ioctl(hdl, UART_SET_BAUDRATE, 115200);
13.IOCTL_SPI_SET_SYNC_TIMEOUT
dev_ioctl(hdl, IOCTL_SPI_SET_SYNC_TIMEOUT, 1000);
14.IOCTL_SPI_FDX_TR_DATA
spi_fdx_data_t
结构的数据地址。非DMA
收发。#define BUF_SIZE 64
spi_fdx_data_t fdx_data;
fdx_data.send_buf = malloc(BUF_SIZE);
fdx_data.recv_buf = malloc(BUF_SIZE);
fdx_data.len = BUF_SIZE;
dev_ioctl(hdl, IOCTL_SPI_FDX_TR_DATA, (u32)&fdx_data);
15.IOCTL_SPI_SET_IRQ_EVENT_CB
spi_irq_event_t
类型,返回值为void
类型。void spi_irq_cb(spi_irq_event_t event)
{
if (event == SPI_IRQ_EVENT_TX) printf("I am SPI_IRQ_EVENT_TX\n");
else if (event == SPI_IRQ_EVENT_RX) printf("I am SPI_IRQ_EVENT_RX\n");
else printf("I am %d\n", event);
}
dev_ioctl(hdl, IOCTL_SPI_SET_IRQ_EVENT_CB , (u32)&spi_irq_cb);
typedef enum {
SPI_IRQ_EVENT_NULL, ///< 0.NULL
SPI_IRQ_EVENT_TX, ///< 1.send
SPI_IRQ_EVENT_RX, ///< 2.receive
SPI_IRQ_EVENT_UF, ///< 3.UnderFlow
SPI_IRQ_EVENT_OF, ///< 4.OverFlow
SPI_IRQ_EVENT_BLOCK_RECV, ///< 5.block receive data
} spi_irq_event_t;
Index |
事件 |
描述 |
---|---|---|
0 |
SPI_IRQ_EVENT_NULL |
无 |
1 |
SPI_IRQ_EVENT_TX |
SPI发送数据结束 |
2 |
SPI_IRQ_EVENT_RX |
SPI接收数据结束 |
3 |
SPI_IRQ_EVENT_UF |
SPI作从 机发送,主机请求数据超过从 机发送的数据量时触发UF中断 |
4 |
SPI_IRQ_EVENT_OF |
SPI作从 机接收,主机发送数据超过从 机接收的数据量时触发OF中断 |
5 |
SPI_IRQ_EVENT_BLOCK_RECV |
块接收( 作从机接收SPI摄像头时使用) |
16.IOCTL_SPI_SET_BLOCK_DATA_CFG
spi_block_data_cfg_t
结构的数据地址。17.IOCTL_SPI_READ_BLOCK_DATA
18.IOCTL_SPI_FREE_BLOCK_DATA
19.IOCTL_SPI_SET_BAUDRATE
LSB频率
和分频系数
限制。20.IOCTL_SPI_9BIT_EN
SPI_1WIRE_MODE
支持9BIT模式。21.IOCTL_SPI_9BIT_DATA
SPI_1WIRE_MODE
支持9BIT模式。2.6.5. 常见问题说明
2.6.6. API参考
SPI dev_ioctl funciton selest
-
SPI_MAGIC
-
IOCTL_SPI_SET_CS_STATUS
设置cs状态
-
IOCTL_SPI_SET_CS_PORT_FUNC
设置cs函数
-
IOCTL_SPI_SEND_BYTE
发送一个字节
-
IOCTL_SPI_READ_BYTE
读取一个字节
-
IOCTL_SPI_SEND_CMD_2BIT_MODE
以2bit模式发命令
-
IOCTL_SPI_SET_USE_SEM
设置是否使用信号量,0:使用 非0:不使用
-
IOCTL_SPI_SET_TRIGGER_WAIT_SEM_TIME_THRE
设置TX使用信号量的时间阈值
-
IOCTL_SPI_SET_IRQ_FUNC
设置中断函数
-
IOCTL_SPI_SET_ASYNC_SEND
设置是否同步方式发包
-
IOCTL_SPI_WAIT_ASYNC_SEND_END
等待非同步发送结束
-
IOCTL_SPI_SET_ASYNC_RECV
设置是否同步方式发包
-
IOCTL_SPI_WAIT_ASYNC_RECV_END
等待非同步发送结束
-
IOCTL_SPI_SET_SYNC_TIMEOUT
设置同步等待超时时间,单位ms
-
IOCTL_SPI_FDX_TR_DATA
全双工收发数据,仅STD模式支持
-
IOCTL_SPI_SET_IRQ_EVENT_CB
设置中断事件回调函数
-
IOCTL_SPI_SET_BLOCK_DATA_CFG
设置block data的配置
-
IOCTL_SPI_READ_BLOCK_DATA
读取block data
-
IOCTL_SPI_FREE_BLOCK_DATA
释放一个block data
-
IOCTL_SPI_SET_BAUDRATE
设置波特率
SPI master or slave mode configuration parameters
SPI clk/updata/sample level and edge configuration parameters
SPI mode configuration parameters
SPI IRQ event configuration parameters
Defines
-
SPI_MAX_SIZE
(2^31 - 1)//由于该宏可能赋值给有符合变量,因此使用2^31 - 1
-
struct spi_io_t
-
struct spi_2bit_data_t
-
struct spi_block_data_cfg_t
-
struct spi_fdx_data_t
-
struct spi_platform_data
Public Members
-
spi_mode_t mode
模式
-
u8 irq
中断号,每个SPI为固定值
-
u8 priority
中断优先级
-
u8 cpu_id
中断注册所在cpu
-
u8 hd_level
IO强驱等级
-
u8 uf_ie_enable
dma fifo UnderFlow irq enable(for debug)
-
u8 of_ie_enable
dma fifo OverFlow irq enable(for debug)
-
u32 clk
波特率配置
-
u32 sync_wait_timeout
同步等待超时时间,单位ms
-
spi_role_t role
主、从机配置
-
spi_attr_t attr
clk/updata/sample的电平/边沿配置
-
JL_SPI_TypeDef *reg
使用的寄存器组
-
spi_mode_t mode