STM32 SPI双机通讯记录总结

STM32 SPI双机通讯记录总结

目的: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,第二次之后就没有了
暂时不知道哪里出了问题

  • 1
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
针对您的问题,以下是stm32f103c8t6 SPI双机通信的步骤: 一、SPI主机配置 1. 配置SPI口为主机模式,设置SPI口的工作模式、数据大小、时钟极性、时钟相位等参数。 2. 使能SPI口,等待SPI口空闲。 二、SPI从机配置 1. 配置SPI口为从机模式,设置SPI口的工作模式、数据大小、时钟极性、时钟相位等参数。 2. 使能SPI口,等待SPI口空闲。 三、双机通信 1. 轮询+中断(低速):主机发送数据,从机接收数据,主机等待从机接收完成中断,从机接收完成后发送中断信号,主机接收中断信号后读取数据。 2. 轮询+DMA(低速):主机发送数据,从机接收数据,主机等待DMA传输完成,从机接收完成后发送DMA传输完成信号,主机接收信号后读取数据。 3. DMA+DMA(高速):主机发送数据,从机接收数据,主机使用DMA传输数据,从机使用DMA接收数据,传输完成后从机发送DMA传输完成信号,主机接收信号后读取数据。 4. 开启CRC校验(自选):在数据传输过程中,可以开启CRC校验,以保证数据传输的正确性。 四、遇到的问题 1. 高速使用时,程序卡死,或者数据出错(已解决):可能是由于时钟极性、时钟相位等参数设置不正确导致的,需要重新设置参数。 2. 数据莫名其妙乱码,主机发送正常,接收乱码等:可能是由于数据大小设置不正确导致的,需要重新设置数据大小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值