SPI 总线从接口允许 JN5169 与外围设备之间进行高速同步数据传输。 JN5169 作为 SPI 总线上的从设备运行,连接到 SPI 总线的外部设备作为主机。 引脚与 SPI 总线主接口不同,如下表所示:
SPI 总线采用简单的移位寄存器数据传输方案,SPISSEL 用作低电平有效选择控制。 数据以先进先出的方式移出和移入有源设备,从而允许 SPI 总线设备同时发送和接收数据。 主机输出从机输入或主机输入从机输出数据传输是相对于外部主机产生的时钟信号 SPISCLK 的。
SPI总线从机包括以下功能:
#define MSB FALSE //spi从最高位开始传输
#define LSB TRUE //spi从最低位开始传输
uint8 TxBuff[10]; //发送缓冲区
uint8 RxBuff[10]; //接收缓冲区
void vCbSpiSlaveInt(uint32 u32Device, uint32 u32ItemBitmap)
{
uint8 recv;
if(E_AHI_DEVICE_SPIS == u32Device){
//SPI从机中断
if((u32ItemBitmap & (1 << 0)) == E_AHI_SPIS_INT_RX_FIRST_MASK){
vPrintf("数据已在接收FIFO中接收,以前是空的\n");
}
if((u32ItemBitmap & (1 << 1)) == E_AHI_SPIS_INT_TX_LAST_MASK){
vPrintf("发送FIFO中的最后一个剩余字节已发送,缓冲区为空\n");
}
if((u32ItemBitmap & (1 << 2)) == E_AHI_SPIS_INT_RX_CLIMB_MASK){
//这个中断提示应用从接收缓冲区中读取数据
//等待一个读阈值中断或读超时中断,当出现其中一个中断时,将调用用户定义的回调函数来处理中断
//函数 u8AHI_SpiSlaveRxReadByte()在这个回调函数中被调用。
vPrintf("接收FIFO的填充级别已超过配置的阈值级别\n");
recv = u8AHI_SpiSlaveRxReadByte();
vPrintf("recv1 = %x\n", recv);
recv = u8AHI_SpiSlaveRxReadByte();
vPrintf("recv2 = %x\n", recv);
}
if((u32ItemBitmap & (1 << 3)) == E_AHI_SPIS_INT_TX_FALL_MASK){
//等待一个写阈值中断来提示写入发送 FIFO
//当出现这个中断时,将调用用户定义的回调函数来处理中断,vAHI_SpiSlaveTxWriteByte()在这个回调函数中被调用。
//这个中断提示应用写数据到发送缓冲区
//写入的字节数不应超出缓冲区大小减去缓冲区写阈值的结果。
vPrintf("发送FIFO的填充水平已降至配置的阈值水平以下\n");
vAHI_SpiSlaveTxWriteByte(0x99);
}
if((u32ItemBitmap & (1 << 4)) == E_AHI_SPIS_INT_RX_OVER_MASK){
vPrintf("数据已接收但接收FIFO已满或繁忙(因此数据被丢弃)\n");
}
if((u32ItemBitmap & (1 << 5)) == E_AHI_SPIS_INT_TX_OVER_MASK){
vPrintf("发送FIFO已写入但已满\n");
}
if((u32ItemBitmap & (1 << 6)) == E_AHI_SPIS_INT_RX_UNDER_MASK){
vPrintf("接收FIFO已读取但为空\n");
}
if((u32ItemBitmap & (1 << 7)) == E_AHI_SPIS_INT_TX_UNDER_MASK){
vPrintf("尝试发送但发送FIFO为空或未准备好(因此通过SPI总线发送了0x00)\n");
}
if((u32ItemBitmap & (1 << 8)) == E_AHI_SPIS_INT_RX_TIMEOUT_MASK){
//等待一个读阈值中断或读超时中断,当出现其中一个中断时,将调用用户定义的回调函数来处理中断
//函数 u8AHI_SpiSlaveRxReadByte()在这个回调函数中被调用。
vPrintf("发生接收超时(在此期间未接收到其他数据)\n");
recv = u8AHI_SpiSlaveRxReadByte();
vPrintf("recv3 = %x\n", recv);
}
}
}
PUBLIC void vSPI_Slave_Init()
{
bAHI_SpiSlaveEnable(FALSE, //SPISMISO : DIO13, SPISMOSI : DIO12
MSB, //高位开始传输
TxBuff, //发送缓冲区
sizeof(TxBuff), //发送缓冲区大小
1, //发送缓冲区的填充阈值,以字节为单位(0到255),这个阈值提示当发送缓冲区数据大小这个阈值时,从机应当向主机写数据
RxBuff, //接收缓冲区
sizeof(RxBuff), //接收缓冲区大小
1, //接收缓冲区的填充阈值,以字节为单位(0到255),这个阈值提示当接收缓冲区数据大小这个阈值时,应当向接收缓冲区读取数据
100, //接收超时时间,以微秒为单位(0到4095)
0x1FF //中断使能位,这里开启9个中断
);
vAHI_SpiSlaveRegisterCallback(vCbSpiSlaveInt);//注册中断回调函数
//向发送缓冲区写入原始数据。
//写入的字节数必须不能超过缓冲区的大小。
//默认情况下,如果发送 FIFO 为空且传输由远程 SPI 主机启动,则 SPI 从机将发送数据字节 0x00。
//由于设置发送缓冲区的填充阈值为1,所以每到主机向从机读取一次数据,从机向主机发送一个发送缓冲区的数据,直至发送缓冲区为空(先进先出)
vAHI_SpiSlaveTxWriteByte(0x56);
vAHI_SpiSlaveTxWriteByte(0x57);
vAHI_SpiSlaveTxWriteByte(0x58);
vAHI_SpiSlaveTxWriteByte(0x59);
vAHI_SpiSlaveTxWriteByte(0x60);
}
PUBLIC void AppColdStart(void)
{
vAHI_WatchdogStop();
(void) u32AHI_Init();
vSPI_Slave_Init();
vUartInit();
vAHI_DelayXms(500);
vPrintf("System init!\n");
while (1) {
}
}
PUBLIC void AppWarmStart(void)
{
AppColdStart();
}
效果图
#define MSB FALSE //spi从最高位开始传输
#define LSB TRUE //spi从最低位开始传输
uint8 TxBuff[10];
uint8 RxBuff[10];
PUBLIC void vSPI_Slave_Init()
{
bAHI_SpiSlaveEnable(FALSE, //SPISMISO : DIO13, SPISMOSI : DIO12
MSB, //高位开始传输
TxBuff, //发送缓冲区
sizeof(TxBuff), //发送缓冲区大小
1, //发送缓冲区的填充阈值,以字节为单位(0到255),这个阈值提示当发送缓冲区数据大小这个阈值时,从机应当向主机写数据
RxBuff, //接收缓冲区
sizeof(RxBuff), //接收缓冲区大小
1, //接收缓冲区的填充阈值,以字节为单位(0到255),这个阈值提示当接收缓冲区数据大小这个阈值时,应当向接收缓冲区读取数据
100, //接收超时时间,以微秒为单位(0到4095)
0x00 //中断使能位,这里关闭全部中断
);
//向发送缓冲区写入原始数据。
//写入的字节数必须不能超过缓冲区的大小。
//默认情况下,如果发送 FIFO 为空且传输由远程 SPI 主机启动,则 SPI 从机将发送数据字节 0x00。
//由于设置发送缓冲区的填充阈值为1,所以每到主机向从机读取一次数据,从机向主机发送一个发送缓冲区的数据,直至发送缓冲区为空(先进先出)
vAHI_SpiSlaveTxWriteByte(0x56);
vAHI_SpiSlaveTxWriteByte(0x57);
vAHI_SpiSlaveTxWriteByte(0x58);
vAHI_SpiSlaveTxWriteByte(0x59);
vAHI_SpiSlaveTxWriteByte(0x60);
}
PUBLIC void AppColdStart(void)
{
uint8 sta, len, recv;
vAHI_WatchdogStop();
(void) u32AHI_Init();
vSPI_Slave_Init();
vUartInit();
vAHI_DelayXms(500);
vPrintf("System init!\n");
//实际运行时把串口打印注释掉
while (1) {
sta = u8AHI_SpiSlaveStatus();
if((sta & 0x01) == E_AHI_SPIS_STAT_RX_AVAIL_MASK){
//接收缓冲区不为空
len = u8AHI_SpiSlaveRxFillLevel();
//vPrintf("接收缓冲区填充级别:%d\n", len);
if((sta & 0x04) == E_AHI_SPIS_STAT_RX_ABOVE_MASK){
//接收缓冲区填充级别高于阈值
//vPrintf("接收FIFO的填充级别已超过配置的阈值级别\n");
recv = u8AHI_SpiSlaveRxReadByte();
vPrintf("recv1 = %x\n", recv);
}
else{
//vPrintf("接收FIFO的填充水平已降至配置的阈值水平以下\n");
}
}
else{
//vPrintf("接收缓冲区为空\n");
}
if((sta & 0x02) == E_AHI_SPIS_STAT_TX_PENDING_MASK){
//发送缓冲区不为空
len = u8AHI_SpiSlaveTxFillLevel();
//vPrintf("发送缓冲区填充级别:%d\n", len);
if((sta & 0x08) == E_AHI_SPIS_STAT_TX_ABOVE_MASK){
//发送缓冲区填充级别高于阈值
//vPrintf("发送缓冲区填充级别高于阈值\n");
}
else{
//vPrintf("发送FIFO的填充水平已降至配置的阈值水平以下\n");
vAHI_SpiSlaveTxWriteByte(0x99);
}
}
else{
//vPrintf("发送缓冲区为空\n");
vAHI_SpiSlaveTxWriteByte(0x99);
}
}
}
PUBLIC void AppWarmStart(void)
{
AppColdStart();
}
效果图
#define SPI_S0 0x04
#define SPI_S1 0x08
#define SPIF 0x80 //SPSTAT.7
#define WCOL 0x40 //SPSTAT.6
#define SSIG 0x80 //SPCTL.7
#define SPEN 0x40 //SPCTL.6
#define DORD 0x20 //SPCTL.5
#define MSTR 0x10 //SPCTL.4
#define CPOL 0x08 //SPCTL.3
#define CPHA 0x04 //SPCTL.2
#define SPDHH 0x00 //CPU_CLK/4
#define SPDH 0x01 //CPU_CLK/16
#define SPDL 0x02 //CPU_CLK/64
#define SPDLL 0x03 //CPU_CLK/128
sbit SS = P1 ^ 2; //SPI_1的SS脚
void InitSPI_1(void)
{
ACC = P_SW1; //切换到第一组SPI
ACC &= ~(SPI_S0 | SPI_S1); //SPI_S0=0 SPI_S1=0
P_SW1 = ACC; //(P1.2/SS, P1.3/MOSI, P1.4/MISO, P1.5/SCLK)
// ACC = P_SW1; //可用于测试U7,U7使用的是第二组SPI控制Flash
// ACC &= ~(SPI_S0 | SPI_S1); //SPI_S0=1 SPI_S1=0
// ACC |= SPI_S0; //(P2.4/SS_2, P2.3/MOSI_2, P2.2/MISO_2, P2.1/SCLK_2)
// P_SW1 = ACC;
// ACC = P_SW1; //切换到第三组SPI
// ACC &= ~(SPI_S0 | SPI_S1); //SPI_S0=0 SPI_S1=1
// ACC |= SPI_S1; //(P5.4/SS_3, P4.0/MOSI_3, P4.1/MISO_3, P4.3/SCLK_3)
// P_SW1 = ACC;
SPDAT = 0; //初始化SPI数据
SPSTAT = SPIF | WCOL; //清除SPI状态位
SPCTL = SPEN | MSTR | SSIG | SPDHH; //主机模式
}
uchar SPISwap(uchar dat)
{
SPDAT = dat; //触发SPI发送数据
while((SPSTAT & SPIF) != SPIF); //等待发送完成
SPSTAT = SPIF | WCOL; //清除SPI状态位
return SPDAT; //返回SPI数据
}
uchar spi_send_byte(uchar dat)
{
uchar recv = 0;
SS = 0;
recv = SPISwap(dat);
SS = 1;
return recv;
}
void main(void)
{
InitSPI_1();
Init_Uart();
EA = 1;
Delay500ms();
Delay500ms();
printf("System init\r\n");
while(1) {
printf("recv = %x\r\n", spi_send_byte(0x56));
Delay500ms();
Delay500ms();
Delay500ms();
Delay500ms();
}
}