工程文件链接:
链接:https://pan.baidu.com/s/1RXp9lw2ZqyglQSwKnw7Siw?pwd=6666
提取码:6666
工程里面有很多例子都是看B站视频手打的(这次代码只用到了YJSPI文件夹下面的.C和.H文件其余可以忽略),只是验证通讯和配置
一,概述
1.因为工作是从事PCBA测试软件开发的(上位机),之前做过的很多项目都是一个单片机完成所有功能,做了有段时间了无非就是那些东西的操控,继电器,电压,电流,PWM,串口,外部IO信号采集,无非是通道的多少,可能这次项目需要继电器多一点,其他项目需要测PWM又多一点。
搞去搞来也就那些东西,而且每次重新画板子都是因为通道和采集点或者继电器不够,这次就打算直接把这些常用功能模块化,一个单片机控制继电器就只控制继电器,测电压的单片机就只测电压,但是有个问题就是他们之间怎么样联系起来,起初想啊是IIC,因为方便线少。后边想了下用IIC挂载多了可能会有影响,我也指不定要挂载多少个设备.所以打算用SPI线多点就是不影响.
后边我会把这次做好的项目模块上传现在这个只是实验,验证可行性.
(如果有大佬有更好的方法欢迎指正 有奖励)
二.实验开始
接线参照下图我用了三片STM32C6T6
主机通过CSS线来选择要和哪一个单片机通讯,从机SCK,MOSI,MISO全部并到主机对应点去,就收数据时候,从机需要判断CSS线是否为低电平来确定是否接收主机发送的数据。调试的时候通过串口把数据打印出来
原理就这样.
三,步骤
1.主机SPI(初始化GPIO,配置硬件SPI,从机CSS用到的引脚默认拉高)
2.从机SPI(初始化GPIO,配置硬件SPI,从机轮询判断端口是否为低电平)
3.示波器查看主机发,从机收
4.示波器查看从机发主机收(因为从机无法主动发所以主机需要先发再收)
五,主机SPI代码
主机硬件SPI(GPIO)初始化:
//CSS:PA0,PA1
//SCK:PA5
//MOSI:PA7
//MOSI:PA6
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//主机控制CSS线,所以设置输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主机
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, 1);
GPIO_WriteBit(GPIOA, GPIO_Pin_1, 1);
MySPI_W_SS(1);
}
主机SPI(读写):
//CSS线控制
void MySPI_W_SS(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)BitValue);
}
void MySPI_Start(void)
{
MySPI_W_SS(0);
}
void MySPI_Stop(void)
{
MySPI_W_SS(1);
}
//读
uint8_t MySPI_SlaveReceive(void)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}
//写
void MySPI_SlaveSend(uint8_t data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data);
}
主机SPI Main运行代码
由于实验我就只发送两个字节过去,然后再读取从机返回给我的数据
MySPI_Init();
while (1)
{
MySPI_Start();
MySPI_SlaveSend(0XA8);
MySPI_SlaveSend(0XA9);
uint8_t re = MySPI_SlaveReceive();
MySPI_Stop();
printf("%d\r\n",re);
}
六,从机SPI代码
从机机硬件SPI(GPIO)初始化:
先来一个错位示范:
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
// CSS
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//这里需要读取CSS电平,所以配置浮空
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//SCK, MISO, MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;//从机
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
这个才是正确的从机SPI的GPIO配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
// CSS
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//MISO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//SCK
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
从机SPI(读写):
//从机只需要读写就可以了其余不需要
uint8_t MySPI_SlaveReceive(void)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}
void MySPI_SlaveSend(uint8_t data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data);
}
从机SPI Main运行代码
简单点读取主机发送的再,返回给主机0X88,0X8A
MySPI_Init();
while (1)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET)
{
uint8_t receivedByte = MySPI_SlaveReceive();
uint8_t receivedByte2 = MySPI_SlaveReceive();
printf("%d%d\r\n",receivedByte,receivedByte2);
MySPI_SlaveSend(0X88);
MySPI_SlaveSend(0X8A);
}
}
七,实际效果:
本来想把几个波形都显示出来的 可惜我逻辑分析仪在Win11用不了
主机发从机收
从机发送主机收
MISO和MOSI波形
分割线>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
在最近调试中发现 SPI主从通讯的时候字节位老是错位,例如我主机发送1,2,3,4从机收到后把1,2,3,4返回到主机,主机通过串口打印出收到的数据。
实际测试中发现 串口打印的数据是
第一次:0,1,2,3
第二次:4,1,2,3
第三次:4,1,2,3
往后N次都是这样的.....(可我发送的明明就是1,2,3,4)
就因为第一个数据位错位后导致后边数据全部错位而且无法纠正(不管你怎么样调都是错位的)
我网上查找了很多资料,说什么前面加个读取,还说什么主机和从机的GPIO配置一样,差别就是在于收发数据,等等都在胡说八道,感觉挺容易误导别人。
1.这里澄清一点 SPI主机和从机的GPIO配置肯定不同
2.我看了很多别人发的不知道从哪里来的,基本主机SPI配置和从机SPI的GPIO配置一模一样,我也不清楚这样也能通讯上妈蛋奇怪.
3.来下面直接看手册,主从配置方法不仅不一样而且区别大了去了。(我自己上边代码都是错误的,我就不修正了)
4.如果GPIO主从配置正确,那么下载好程序到各自单片机上面,先复位从机在复位主机(这点很重要)
这是我修改后的发送打印
、