目录
Arduino IDE——SPI向两个slave的其中一个发送数据
PKE8720DF-C13-F10开发板SPI功能简介
PKE8720DF-C13-F10 支持摩托罗拉 SPI 串行接口操作,支持主机或从机的操作模式。提供了两个 SPI 端口:
- SPI0(高速):配置为主机或从机,最大波特率 50MHz
- SPI1(正常速度):配置为主机,最大波特率 25MHz
SPI 一共有 4 根线,分别是 MOSI(PB18 / PA12)、MISO(PB19 / PA13)、SCLK(PB20 / PA14)和 CS/SS(PB21 / PA15):
- MOSI (Master Output Slave Input):master 数据输出,slave 数据输入
- MISO (Master Input Slave Output):master 数据输入,slave 数据输出
- SCLK (Serial Clock):时钟信号,由 master 产生
- CS/SS (Chip Select / Slave Select):片选信号,由 master 控制。当一个 master 连接多个 slave 时,CS/SS是 slave 是否被 master 选中的控制信号
下图是一个 master 连接两个 slave 的示意图:
Figure 1. 一个 master 连接两个 slave 示意图
Arduino IDE——SPI向两个slave的其中一个发送数据
Arduino IDE 配置方法以及 PIN 脚对应关系已经在之前的文章中进行了详细的介绍:使用PKE8720DF-C13-F10实现Arduino example——Button_deer_vickey的博客-CSDN博客
1)所需材料
- 3 × PKE8720DF-C13-F10开发板
- 3 × USB转type-C数据线
- 1 × 面包板
- 11 × 杜邦线
2)电路连接
一块板子的 SPI1 作为 master,PB22 和 PB23 分别为 slave 1 和 slave 2 提供片选信号;另外两块板子的 SPI0 作为 slave:
- Master board 的 SPI1_MOSI (PA12),SPI1_MISO (PA13),SPI1_SCLK (PA14) 都通过面包板,分别连接到两块 Slave board 的 SPI0_MOSI (PB18),SPI0_MISO (PB19),SPI0_SCLK (PB20)
- Master board 的 CS0 (PB22) 连接到 Slave board 1 的 SPI0_SS (PB21)
- Master board 的 CS1 (PB23) 连接到 Slave board 2 的 SPI0_SS (PB21)
3)代码解析
代码通过macro SPI_IS_AS_MASTER 来区分 master 和 slave,完整的源代码文件 SPI_MultiSlave.ino 可以在下面的链接下载:
https://download.csdn.net/download/deer_vickey/88189915?spm=1001.2014.3001.5503
include files
SPI的接口定义在下面的头文件中,相关函数可以在ambd_arduino源代码中查询,github下载地址:GitHub - ambiot/ambd_arduino: AmebaD Arduino third-party package SDK
#include "device.h"
#include "pinnames.h"
#include "spi_api.h"
#include "spi_ex_api.h"
#include "gpio_api.h"
definitions and global variables
- SPI_IS_AS_MASTER 用来区分 master 和 slave,1 表示 master,0 表示 slave
- SCLK_FREQ 是时钟信号的频率
- TestBuf[] 存放 master 发送以及 slave 接收的数据,数据长度为 64
- TrDone 表示数据传送或者接收是否结束,1 表示结束
- 另外还定义了 master 和 slave 的 SPI pins
#define SPI_IS_AS_MASTER 1
#define TEST_BUF_SIZE 64
#define SCLK_FREQ 1000000
char TestBuf[TEST_BUF_SIZE];
volatile int TrDone;
#define SPI_GPIO_CS0 PB_22
#define SPI_GPIO_CS1 PB_23
#if SPI_IS_AS_MASTER
spi_t spi_master;
//SPI1
PinName SPIM_MOSI = PA_12;
PinName SPIM_MISO = PA_13;
PinName SPIM_SCLK = PA_14;
PinName SPIM_CS = PA_15;
#else
spi_t spi_slave;
//SPI0
PinName SPIS_MOSI = PB_18;
PinName SPIS_MISO = PB_19;
PinName SPIS_SCLK = PB_20;
PinName SPIS_CS = PB_21;
#endif
setup()
a. master:
- 初始化作为片选信号的 GPIO:SPI_GPIO_CS0 和 SPI_GPIO_CS1:
gpio_init(&spi_cs0, SPI_GPIO_CS0);
gpio_write(&spi_cs0, 1); //Initialize GPIO Pin to high
gpio_dir(&spi_cs0, PIN_OUTPUT); // Direction: Output
gpio_mode(&spi_cs0, PullNone); // No pull
gpio_init(&spi_cs1, SPI_GPIO_CS1);
gpio_write(&spi_cs1, 1); //Initialize GPIO Pin to high
gpio_dir(&spi_cs1, PIN_OUTPUT); // Direction: Output
gpio_mode(&spi_cs1, PullNone); // No pull
- 初始化 SPI1 (master):
spi_master.spi_idx = MBED_SPI1;
spi_init(&spi_master, SPIM_MOSI, SPIM_MISO, SPIM_SCLK, SPIM_CS);
spi_frequency(&spi_master, SCLK_FREQ);
spi_format(&spi_master, 8, (SPI_SCLK_IDLE_LOW|SPI_SCLK_TOGGLE_START) , 0);
- TestBuf[] 赋值,片选信号 SPI_GPIO_CS0 拉低,SPI_GPIO_CS1 拉高,即选择 slave 1:
for (i=0;i<TEST_BUF_SIZE;i++)
{
TestBuf[i] = i;
}
gpio_write(&spi_cs0, 0);
gpio_write(&spi_cs1, 1);
// wait Slave ready
delay(1000);
- 调用 spi_master_write_stream 函数传送 data 之后,打印出 data 的内容:
spi_irq_hook(&spi_master, (spi_irq_handler)master_tr_done_callback, (uint32_t)&spi_master);
printf("SPI Master Write Test==>\r\n");
TrDone = 0;
spi_master_write_stream(&spi_master, TestBuf, TEST_BUF_SIZE);
dump_data((const u8 *)TestBuf, TEST_BUF_SIZE, "SPI Master Write Data:");
b. slave
- 初始化 SPI0 (slave),并且将 TestBuf[] 内容清空:
spi_slave.spi_idx = MBED_SPI0;
spi_init(&spi_slave, SPIS_MOSI, SPIS_MISO, SPIS_SCLK, SPIS_CS);
spi_format(&spi_slave, 8, (SPI_SCLK_IDLE_LOW|SPI_SCLK_TOGGLE_START) , 1);
_memset(TestBuf, 0, TEST_BUF_SIZE);
- 如果当前处于 busy 状态,会打印 “Wait SPI Bus Ready...”:
while (spi_busy(&spi_slave))
{
printf("Wait SPI Bus Ready...\r\n");
delay(4000);
}
- 调用 spi_slave_read_stream 函数接收数据,接收完成后打印出接收到的内容
- 如果在 15 秒的时间内都没有接收完数据,会打印 “SPI Slave Wait Timeout” 的信息:
printf("SPI Slave Read Test ==>\r\n");
spi_irq_hook(&spi_slave, (spi_irq_handler)slave_tr_done_callback, (uint32_t)&spi_slave);
TrDone = 0;
spi_flush_rx_fifo(&spi_slave);
spi_slave_read_stream(&spi_slave, TestBuf, TEST_BUF_SIZE);
i=0;
printf("SPI Slave Wait Read Done...\r\n");
while(TrDone == 0) {
delay(100);
i++;
if (i>150) {
printf("SPI Slave Wait Timeout\r\n");
break;
}
}
dump_data((const u8 *)TestBuf, TEST_BUF_SIZE, "SPI Slave Read Data:");
4)烧录image
- Master:“Tools” --> “Port” 中选择 Master 的 COM 口,SPI_IS_AS_MASTER 定义成 1,点击 “Sketch” --> “Upload” 把 image 烧录到板子中
- Slave:“Tools” --> “Port” 中选择 Slave 的 COM 口,SPI_IS_AS_MASTER 定义成 0,点击 “Sketch” --> “Upload” 把 image 烧录到板子中
5)实验结果
先分别按下两个 Slave 的 Reset 键,再按下 Master 的 Reset 键,可以看到下面的 output。
- 第一张图的 COM31 是 Master,发送的数据为 00 01 02 03 04 ... 3d 3e 3f
- 第二张图的 COM34 是 Slave 1,接收到的数据为 00 01 02 03 04 ... 3d 3e 3f
- 第二张图的 COM36 是 Slave 2,打印出了 “SPI Slave Wait Timeout” 信息
因为把片选信号 SPI_GPIO_CS0 拉低,所以 master 选择的是 slave 1。 slave 1 成功接收到 master 发送的数据,slave 0 未接收到数据,实验结果符合预期。
- master (COM31):
- slave 1 (COM34):
- slave 2 (COM36):