目的:STM32F103RCT6作为主机、STM32F103C8T6作为从机,实现主机发送12个字节从机回12个字节
第一步:了解SPI口的接线方式,查找了相关资料,描述如下
SPI 接口一般使用 4 条线通信:
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。
即双机的接线方式是:MISO-MISO、MOSI-MOSI、SCLK-SCLK、CS-CS
还有就是不要忘了主机和从机要共地
第二步:查看STM32 对SPI进行使用的相关程序示例,和SPI工作原理
其中涉及到比较重要的就是移位寄存器、时钟极性(CPOL)、时钟相位(CPHA)
CPOL=0,串行同步时钟的空闲状态为低电平
CPOL=1,串行同步时钟的空闲状态为高电平
CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样
CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样
然后SPI 主机和与从机时钟相位和极性要设置成一样
第三步:上机写程序
主机:使用SPI1
SPI初始化部分配置:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //PA4-CS 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //PA6-MISO 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; //PA5-SCK PA7-MOSI 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
主机和从机的引脚配置是不同的
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //主机配置,从机配置一样
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //主机配置,从机配置一样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //主机nss信号由软件管理,而从机则要由硬件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //速度 72/4=18M
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
主循环里用个按键控制发送12个字节
SPI_Buffer1[12]={0xAA,0x14,0x13,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
while(1)
{
if(key==KEY0_PRES) //KEY0 按下
{
for(i1=0;i1<12;i1++)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4); //拉低允许通讯
j=SPI1_ReadWriteByte(SPI_Buffer1[i1]);
SPI_rxBuffer[i1] = j;
GPIO_SetBits(GPIOA, GPIO_Pin_4); //拉高禁止通讯
delay_us(10);
}
}
}
u8 SPI1_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI1, TxData);
retry=0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI1);
}
从机:使用SPI2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PB12-CS
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; // PB14-MISO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15; //PB13-SCK PB15-MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //从机模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 与主机一样
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//与主机一样
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; //从机nss由硬件管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //速度 72/4=18M
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
/* 配置中断 */
NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_Init(SPI2, &SPI_InitStructure);
SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE); //使能中断接收
SPI_Cmd(SPI2,ENABLE);
中断接收函数
从机发送的数据SPI_TxBuf[12]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12};
void SPI2_IRQHandler(void)
{
/*接收中断 */
if(SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_RXNE) == SET)
{
for(SPI_Rxcnt= 0;SPI_Rxcnt < 12;SPI_Rxcnt++)
{
SPI_I2S_ClearITPendingBit( SPI2 , SPI_I2S_IT_RXNE );
/*接收到数据 */
while(SPI_I2S_GetFlagStatus(SPI2 , SPI_I2S_FLAG_RXNE)==RESET );
SPI_RxBuf[SPI_Rxcnt] = SPI_I2S_ReceiveData(SPI2);
if(SPI_RxBuf[0]==0xAA) SPI_I2S_SendData(SPI2, SPI_TxBuf[SPI_TX_cnt++]);
}
}
}
uint8_t SPI_SendByte(uint8_t data)
{
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, data);
// while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
// return SPI_I2S_ReceiveData(SPI2); //已开启了中断接收
}
测试结果:
按下按键,主机发{0xAA,0x14,0x13,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
从机收到{0xAA,0x14,0x13,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};//数据正确
主机收到{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11};//多了一个0,少了一个0x12
按第二次按键,主机发{0xAA,0x14,0x13,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
从机收到{0xAA,0x14,0x13,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};//数据正确
主机收到{0x12,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11};//收到的第一个字节变成了0x12
也就是开机上电的第一次通讯,主机会先收到一个0,第二次之后就没有了
暂时不知道哪里出了问题