1、本示例采用的通信方式为是SPI从机的模式0,如果需要其他模式需要对应的微调代码
2、简易流程示意图:
3、相关接口宏定义,采用直接操作寄存器节省函数调用的时间
#define SPIS_CLK_Port GPIOB
#define SPIS_CLK_Pin GPIO_Pin_0
#define SPIS_CS_Port GPIOB
#define SPIS_CS_Pin GPIO_Pin_1
#define SPIS_MOSI_Port GPIOB
#define SPIS_MOSI_Pin GPIO_Pin_2
#define SPIS_MISO_Port GPIOB
#define SPIS_MISO_Pin GPIO_Pin_3
#define SPIS_MISO_1 SPIS_MISO_Port->BSRR = SPIS_MISO_Pin; /* MOSI = 1 */
#define SPIS_MISO_0 SPIS_MISO_Port->BSRR = SPIS_MISO_Pin<<16; /* MOSI = 0 */
#define SPIS_READ_MOSI ((SPIS_MOSI_Port->IODR >> 16)&SPIS_MOSI_Pin) /* 读MOSI口线状态 */
#define SPIS_READ_CS ((SPIS_CS_Port->IODR >> 16)&SPIS_CS_Pin) /* 读CS线状态 */
#define SPIS_READ_CLK ((SPIS_CLK_Port->IODR >> 16)&SPIS_CLK_Pin) /* 读CLK线状态 */
4、中断处理代码,此时CLK及CS中断为同一个中断服务函数
void EXTI1_IRQHandler(void)
{
static uint8_t send_old = 0;
static uint8_t SEND_F = 0;
if(GPIO->INTP_TYPE_STA[SPIS_CS_EXTI_Line].INTP_STA&SPIS_CS_EXTI_Pins)//中断管脚判断,直接读取寄存器节约时间
{
if(SPIS_READ_CS) //检测到片选拉高
{
//CS拉高,结束数据收发
spis_recv_data = 0;
spis_send_data = 0;
spis_bits_offset = 0;
//关闭中断
GPIO->INTP_TYPE_STA[SPIS_CLK_EXTI_Line].INTP_TYPE &= ~(0x03 << (SPIS_CLK_EXTI_offset * 2));
}
else
{
//发送数据的的第一个bit
if(!SEND_F)
{
/*发送新数据,在模式0时,主机的第一个上升沿就会采样数据,所以在片选信号或者上一包数据通信完成之后就需要马上
*将下一帧数据的首bit发送,由于后续可能不会再进行通信,所以需要保存当前数据,在新的一轮通信中继续发送当前帧
*数据,所以这里将数据保存到send_old中,如果当前数据没有发送,后续会继续发送该数据
*/
send_old = spis_send_data = spis_send_buf[spis_send_offset++];
spis_send_offset = spis_send_offset == SPIS_FIFO_LEN?0:spis_send_offset;
}
else
{
spis_send_data = send_old;
}
if(spis_send_data&0x80)
{
SPIS_MISO_1;
}
else
{
SPIS_MISO_0;
}
spis_send_data <<= 1;
spis_bits_offset = 0;
EXTI_LineConfig(SPIS_CLK_EXTI_Line, SPIS_CLK_EXTI_Pins, EXTI_Trigger_Rising_Falling);
GPIO->INTP_TYPE_STA[SPIS_CLK_EXTI_Line].INTP_TYPE |= (0x03 << (SPIS_CLK_EXTI_offset * 2));
}
GPIOB->BSRR = GPIO_Pin_4<<16;
}
if(GPIO->INTP_TYPE_STA[SPIS_CLK_EXTI_Line].INTP_STA&SPIS_CLK_EXTI_Pins)
{
GPIOB->BSRR = GPIO_Pin_5;
if(!SPIS_READ_CS){ //检测到片选拉低
SEND_F = 1;
if(SPIS_READ_CLK)//检测到时钟上升沿的到来
{
//读取数据
if(SPIS_READ_MOSI)
{
spis_recv_data |= 0x01;
}
spis_bits_offset++;
if(spis_bits_offset == 8)//分开处理,避免上升或者下降沿的中断处理耗时
{
// GPIOB->BSRR = GPIO_Pin_5<<16;
// GPIOB->BSRR = GPIO_Pin_5;
//数据缓冲
spis_recv_buf[spis_recv_offset++] = spis_recv_data;
spis_recv_offset = spis_recv_offset == SPIS_FIFO_LEN?0:spis_recv_offset;
spis_recv_data = 0;
}
spis_recv_data <<= 1; //接收完成之后赋值0,移位不影响
}
else//检测到时钟下降沿的到来
{
if(spis_bits_offset == 8)
{
//输出下一包数据的头,便于后续字节通信的第一个时钟沿主机就能正确获取到数据,与片选信号有效后的第一步操作一样
send_old = spis_send_data = spis_send_buf[spis_send_offset++];
spis_send_offset = spis_send_offset == SPIS_FIFO_LEN?0:spis_send_offset;
//接收完成1包数据
spis_bits_offset = 0;
SEND_F = 1; //一包数据完整发送,开始新一包数据
}
else
SEND_F = 0;//新数据开始发送,后续中断后丢弃该包数据
if(spis_send_data&0x80)
{
SPIS_MISO_1;
}
else
{
SPIS_MISO_0;
}
spis_send_data <<= 1;
}
}
GPIOB->BSRR = GPIO_Pin_5<<16;
}
//printf("EXTI1_GPIO_Status %08X\n", EXTI_GetITLineStatus(EXTI_Line1));
GPIO->INTP_TYPE_STA[1].INTP_STA = 0xFFFF;
NVIC_ClearPendingIRQ(EXTI1_IRQn);
}
5、GPIO及中断初始化需要结合对于的芯片平台进行操作,这里就不过多描述
6、注意:本示例只提供了基础的中断处理部分,所有的数据收发均通过对应的缓冲区进行收发,实现从机的基础数据收发,正常应用时需要自行完善相应的软件处理逻辑