SPI外设是我们常用的外设功能之一,CSK6 SDK支持SPI外设功能,本章节通过示例介绍SPI外设的基本使用方法。
CSK6 芯片有两组SPI硬件外设,SPI0和SPI1。
CSK6 SPI驱动功能特性如下:
当CSK6 作为从模式时,单次最大能接收512字节数据,超过512字节数据时主发送端需要拆包发送。
读取spi设备的数据
int spi_read(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *rx_bufs
);
返回0表示成功,返回非0表示失败。
参数说明
字段 | 说明 |
---|---|
dev | 指向spi Device的指针 |
config | 指向spi的配置属性的指针 |
rx_bufs | 指向spi接收buf的结构指针 |
向spi设备写入数据
int spi_write(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs);
返回0表示成功,返回非0表示失败。
参数说明
字段 | 说明 |
---|---|
dev | 指向spi Device的指针 |
config | 指向spi的配置属性的指针 |
tx_bufs | 指向spi写入buf的结构指针 |
向spi设备写入/读取数据
int spi_transceive(const struct device *dev,
const struct spi_config *config,
const struct spi_buf_set *tx_bufs,
const struct spi_buf_set *rx_bufs);
这个函数可以实现同时读写功能,spi_write与spi_read都基于这个函数实现,spi_write在调用时候会将rx设置为空,而spi_read调用时会将tx设置为空。返回0表示成功,返回非0表示失败。
参数说明
字段 | 说明 |
---|---|
dev | 指向spi Device的指针 |
config | 指向spi的配置属性的指针 |
tx_bufs | 指向spi写入buf的结构指针 |
rx_bufs | 指向spi接收buf的结构指针 |
更多SPI API接口请查看zephyr官网SPI Interface。
本示例基于两个CSK6-NanoKit开发板实现SPI数据的通信,其中一个作为SPI主设备,另一设备作为从设备,实现该示例需要以下准备工作:
spi0(GPIO_A_19 sclk, GPIO_A_20 cs, GPIO_A_17 miso, GPIO_A_18 mosi)
和spi1(GPIO_B_00 sclk, GPIO_B_03 cs, GPIO_B_02 miso, GPIO_A_01 mosi)
连接,接线方式如下图示:通过Lisa命令创建项目:
lisa zep create
依次按以下目录选择完成spi sample创建:
csk → samples → driver → spi_master_slave
CONFIG_GPIO=y
# SPI外设配置
CONFIG_SPI=y
CONFIG_LOG=y
CONFIG_HEAP_MEM_POOL_SIZE=10240
# SPI接收处理超时时间
CONFIG_SPI_COMPLETION_TIMEOUT_TOLERANCE=10000
CSK6-NanoKit开发板提供了两组SPI外设。本示例使用spi0(GPIO_A_19 sclk, GPIO_A_20 cs, GPIO_A_17 miso, GPIO_A_18 mosi)
和spi1(GPIO_B_00 sclk, GPIO_B_03 cs, GPIO_B_02 miso, GPIO_A_01 mosi)
作为SPI通讯接口,因此需要在设备树中将这GPIO引脚复用为SPI功能,可通过board overlay
的方式完成,具体如下:
在app目录下增加csk6011a_nano.overlay
文件并添加如下配置:
&pinctrl{
/* SPI0引脚配置 */
pinctrl_spi0_cs_default: spi0_cs_default{
pinctrls = <SPI0_CS_N_GPIOA_20>;
};
pinctrl_spi0_miso_default: spi0_miso_default{
pinctrls = <SPI0_MISO_GPIOA_17>;
};
pinctrl_spi0_mosi_default: spi0_mosi_default{
pinctrls = <SPI0_MOSI_GPIOA_18>;
};
pinctrl_spi0_sclk_default: spi0_sclk_default{
pinctrls = <SPI0_CLK_GPIOA_19>;
};
/* SPI1引脚配置 */
pinctrl_spi1_sclk_default: spi1_sclk_default{
pinctrls = <SPI1_CLK_GPIOB_00>;
};
pinctrl_spi1_mosi_default: spi1_mosi_default{
pinctrls = <SPI1_MOSI_GPIOB_01>;
};
pinctrl_spi1_miso_default: spi1_miso_default{
pinctrls = <SPI1_MISO_GPIOB_02>;
};
pinctrl_spi1_cs_default: spi1_cs_default{
pinctrls = <SPI1_CS_N_GPIOB_03>;
};
};
/* SPI1 配置 */
&spi1{
pinctrl-0 = <&pinctrl_spi1_sclk_default &pinctrl_spi1_mosi_default &pinctrl_spi1_miso_default &pinctrl_spi1_cs_default>;
pinctrl-names = "default";
status = "okay";
};
NOTE
如果您想了解更多关于设备树的信息,请学习设备树章节。
本示例实现以下业务逻辑:
{1, 2, 3, 4, 5, 6, 7, 8, 9}
数据。{9, 8, 7, 6, 5, 4, 3, 2, 1}
数据。uint8_t master_buffer_tx[BUF_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
uint8_t master_buffer_rx[BUF_SIZE] = {};
#define MASTER_STACK_SIZE 4096
K_THREAD_STACK_DEFINE(master_stack_area, MASTER_STACK_SIZE);
struct k_thread master_thread_data;
const struct device *spi_master = NULL;
/*获取spi1设备实例*/
void spi_master_init(void)
{
spi_master = DEVICE_DT_GET(DT_NODELABEL(spi1));
if (spi_master == NULL)
{
printk("spi_master: Device is not found.\n");
return;
}
if (!device_is_ready(spi_master))
{
printk("spi_master: Device is not ready.\n");
return;
}
}
/* master线程处理 */
void master_thread(void *v1, void *v2, void *v3)
{
struct spi_config spi_cfg = {0};
const struct spi_buf tx_bufs[] = {
{
.buf = master_buffer_tx,
.len = BUF_SIZE,
},
};
const struct spi_buf rx_bufs[] = {
{
.buf = master_buffer_rx,
.len = BUF_SIZE,
},
};
const struct spi_buf_set tx = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs)};
const struct spi_buf_set rx = {
.buffers = rx_bufs,
.count = ARRAY_SIZE(rx_bufs)};
/* 设置SPI配置:8bit数据位, LSB 数据格式,master模式,时钟频率10Mhz */
spi_cfg.operation = SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_TRANSFER_LSB;
spi_cfg.frequency = 10 * 1000000UL;
while (1)
{
/*每间隔1S触发一次数据发送和接收*/
k_msleep(1000);
/*清空接收缓冲区*/
memset(master_buffer_rx, 0, BUF_SIZE);
printk("[Master]spi send data \n");
/*发送和接收数据*/
spi_transceive(spi_master, &spi_cfg, &tx, &rx);
printk("[Master]spi received data \n");
/*打印接收数据*/
for (uint8_t i = 0; i < BUF_SIZE; i++)
{
printk("0x%x", master_buffer_rx[i]);
}
printk("\n");
}
}
void main(void)
{
printk("SPI test start \n");
spi_master_init();
/* 创建线程 */
int pri = k_thread_priority_get(k_current_get());
k_thread_create(&master_thread_data, master_stack_area,
K_THREAD_STACK_SIZEOF(master_stack_area), master_thread, NULL,
NULL, NULL, pri, 0, K_NO_WAIT);
printk("SPI thread creaeted\n");
while (1)
{
k_msleep(10);
}
}
uint8_t slave_buffer_tx[BUF_SIZE] = {9, 8, 7, 6, 5, 4, 3, 2, 1};
uint8_t slave_buffer_rx[BUF_SIZE] = {};
#define SLAVE_STACK_SIZE 4096
K_THREAD_STACK_DEFINE(slave_stack_area, SLAVE_STACK_SIZE);
struct k_thread slave_thread_data;
const struct device *spi_slave = NULL;
struct k_thread slave_thread_data;
/*获取spi1设备实例*/
void spi_slave_init(void)
{
spi_slave = DEVICE_DT_GET(DT_NODELABEL(spi1));
if (spi_slave == NULL)
{
printk("spi_slave: Device is not found.\n");
return;
}
if (!device_is_ready(spi_slave))
{
printk("spi_slave: Device is not ready.\n");
return;
}
}
/* slave线程处理 */
void slave_thread(void *v1, void *v2, void *v3)
{
struct spi_config spi_cfg = {0};
const struct spi_buf tx_bufs[] = {
{
.buf = slave_buffer_tx,
.len = BUF_SIZE,
},
};
const struct spi_buf rx_bufs[] = {
{
.buf = slave_buffer_rx,
.len = BUF_SIZE,
},
};
const struct spi_buf_set tx = {
.buffers = tx_bufs,
.count = ARRAY_SIZE(tx_bufs)};
const struct spi_buf_set rx = {
.buffers = rx_bufs,
.count = ARRAY_SIZE(rx_bufs)};
/* 设置SPI配置:8bit数据位, LSB 数据格式,slave模式,时钟频率10Mhz */
spi_cfg.operation = SPI_WORD_SET(8) | SPI_OP_MODE_SLAVE | SPI_TRANSFER_LSB;
spi_cfg.frequency = 10 * 1000000UL;
while (1)
{
/*清空接收缓冲区*/
memset(slave_buffer_rx, 0, BUF_SIZE);
/*接收和发送数据*/
spi_transceive(spi_slave, &spi_cfg, &tx, &rx);
printk("[Slave]spi receive data, and send back \n");
/*打印接收数据*/
for (uint8_t i = 0; i < BUF_SIZE; i++)
{
printk("0x%x", slave_buffer_rx[i]);
}
printk("\n");
}
}
void main(void)
{
printk("SPI test start \n");
spi_slave_init();
/* 创建线程 */
int pri = k_thread_priority_get(k_current_get());
k_thread_create(&slave_thread_data, slave_stack_area,
K_THREAD_STACK_SIZEOF(slave_stack_area),
slave_thread, NULL, NULL, NULL, pri, 0, K_NO_WAIT);
printk("SPI thread creaeted\n");
while (1)
{
k_msleep(10);
}
}
TIP
本示例实现了master和slave代码逻辑,通过宏定义MASTER_MODE
来设置master和slave模式的实现逻辑,1为master模式,0为slave模式,开发者需要分别编译master和slave模式固件烧录到两个CSK6-NanoKit开发板上。
分别配置MASTER_MODE
为1和0并烧录到两个CSK6-NanoKit开发板上。
在app根目录下通过以下指令完成编译:
lisa zep build -b csk6011a_nano
CSK6-NanoKit通过USB连接PC,通过烧录指令开始烧录:
lisa zep flash
查看日志:
CSK6-NanoKit通过板载DAPlink虚拟串口连接电脑,或者将CSK6-NanoKit的日志串口A03 TX A02 RX
外接串口板并连接电脑。
slave设备接收到master设备发送的数据结果应为:
*** Booting Zephyr OS build 1ecc9604fbc0 ***
SPI test start
SPI thread creaeted
[Slave]spi receive data, and send back
0x10x20x30x40x50x60x70x80x9
[Slave]spi receive data, and send back
0x10x20x30x40x50x60x70x80x9
[Slave]spi receive data, and send back
0x10x20x30x40x50x60x70x80x9
...
master设备接收到slave设备回传的数据结果应为:
** Booting Zephyr OS build 1ecc9604fbc0 ***
SPI test start
SPI thread creaeted
[Master]spi send data
[Master]spi received data
0x90x80x70x60x50x40x30x20x1
[Master]spi send data
[Master]spi received data
0x90x80x70x60x50x40x30x20x1
[Master]spi send data
[Master]spi received data
0x90x80x70x60x50x40x30x20x1
...
逻辑分析仪数据分析
从逻辑分析仪过程数据如果可以看到,master设备MOSI数据为01~09
,MISO数据为09~01
,则符合预期。