功能:主机发送数据0x34–>从机接收数据–>通过串口将数据发送出去–>串口猎人显示0x34
必备知识点
1、SPI串行外设接口特点
高速、全双工、同步、串行
- 高速:发送数据的速度很快
- 全双工:两设备可同时双向通信(接收与发送)
- 同步:使用一个时钟源
- 串行:串行数据一位一位发(占用的外设接口少,但效率相对较低)
2、SPI四根总线及功能
NSS、SCK、MOSI、MISO
- NSS(片选线):
[1]每个从机都有自己的一条单独的总线与主机连接,此总线的作用就是为主机选择对应的从机进行传输数据,每个从机与主机之间的NSS总线互不相干。
[2]片选线默认为高电平,SPI中规定通信以NSS信号线拉低为开始,拉高为结束。(软件管理下,片选线需手动拉低/硬件片选线的拉高与拉低自动设置) - SCK(时钟线):保持同步的时钟源(32的晶振)
- MOSI:主器件输出数据,从器件输入数据(主器件默认为高,主器件默认为低)
MISO:主器件输入数据,从器件输出数据(主器件默认为高,主器件默认为低)
注意:“MOSI/MISO电平高低可能有误,但应该是这样”
1主机多从机
1主机1从机
3、物理层与协议层的一些知识
物理层:片选线(软件管理/硬件管理)
[1] SPI_NSS_Hard:硬件自动拉高拉低片选,在速率上是远比软件方式控制要高的,缺点是当STM32为主设备时,同一个SPI上面只能接一个从设备。这也就限制了SPI通信设备的数量。
[2] SPI_NSS_Soft:软件控制,GPIO控制片选拉高拉低,优点是一个SPI上面可以挂多个设备。这也是使用较多的方式。通过SPI_Cmd这个函数实现的,使能SPI,硬件自动拉低片选,取消使能SPI,硬件自动拉高片选。
[3] 注意:“一般主器件用软件管理,从器件用硬件管理”
协议层:四种通讯模式
[1] 时钟极性(CPOL):NSS总线空闲时SCK电平信号,空闲时为高电平(=1),低电平(=0)
[2] 时钟相位(CPHA):数据采样时刻,奇数边(=0),偶数边(=1)
https://www.bilibili.com/video/BV1wK411g78L/?spm_id_from=333.851.header_right.history_list.click&vd_source=c567c826b38b783f81e4cb19996400c5
https://one-piece.blog.csdn.net/article/details/119545607?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-119545607-blog-126541530.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-119545607-blog-126541530.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=1
配置步骤
主器件
- 一些初始化:结构体(GPIOA/SPI)
- 使能:SPI IO/SPI外设时钟
- 重映射:SPI(NSS/SCK/MISO/MOSI)
- SPI(NSS/SCK/MISO/MOSI)外设接口初始化
- SPI初始化;使能SPI;拉低片选
- 主器件发送数据
//主
#include "stm32f4xx.h"
#include "delay.h"
int main(void)
{
//一些初始化:结构体(GPIOA/SPI)
GPIO_InitTypeDef GPIO_InitStructure4;//GPIOA4
GPIO_InitTypeDef GPIO_InitStructure5;//GPIOA5
GPIO_InitTypeDef GPIO_InitStructure6;//GPIOA6
GPIO_InitTypeDef GPIO_InitStructure7;//GPIOA7
SPI_InitTypeDef SPI_InitStructure;
//使能:SPI IO/SPI外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能 SPI1 IO口
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能 SPI 外设时钟GPIOA
//重映射:SPI(NSS/SCK/MISO/MOSI)
GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1);//NSS,使用软件管理方法
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);//SCK
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);//MISO
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);//MOSI
//SPI(NSS/SCK/MISO/MOSI)外设接口初始化
GPIO_InitStructure4.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure4.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure4.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure4.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure4.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA , &GPIO_InitStructure4);
GPIO_InitStructure5.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure5.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure5.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure5.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure5.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA , &GPIO_InitStructure5);
GPIO_InitStructure6.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure6.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure6.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure6.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure6.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA , &GPIO_InitStructure6);
GPIO_InitStructure7.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure7.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure7.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure7.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure7.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA , &GPIO_InitStructure7);
//SPI初始化;使能SPI;**拉低片选**
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//通信方式:全双工模式;包括(半双工、全双工、串行发、串行收)
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//SPI模式:主模式;包括(主、从)
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//八位实际只有7位
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//时钟极性:空闲时高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//数据在第二个跳变沿被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//传输速度为84MHz/256=328.125KHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//大端 小端 MSB 10000001 LSB
SPI_InitStructure.SPI_CRCPolynomial = 7;//设置CRC校验多项式
SPI_Init(SPI1,&SPI_InitStructure);
SPI_Cmd(SPI1,ENABLE);//使能SPI1
//NSS 置低
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
//主器件发送数据
while(1)
{
if(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==SET)//缓冲区为空
{
SPI_I2S_SendData(SPI1,0x34);
}
// SPI_I2S_ClearFlag(SPI1, SPI_I2S_FLAG_TXE);
}
}
从器件
- 一些初始化:结构体(GPIOA/SPI/USART/NVIC)
- 使能:SPI IO/SPI外设时钟/USART
- 重映射:SPI(NSS/SCK/MISO/MOSI)、USART
- SPI(NSS/SCK/MISO/MOSI)外设接口初始化;USART外设接口初始化
- SPI初始化;使能SPI;开启SPI接收数据中断
- USART初始化;USART设置;使能USART
- NVIC配置
- 编写中断服务程序
//从
#include "stm32f4xx.h"
#include "delay.h"
int main(void)
{
//一些初始化:结构体(GPIOA/SPI/USART/NVIC)
GPIO_InitTypeDef GPIO_InitStructure5;//GPIOA5、6、7
GPIO_InitTypeDef GPIO_InitStructure;//USART
USART_InitTypeDef Usart_InitStructure;//USART
SPI_InitTypeDef SPI_InitStructure;//SPI
NVIC_InitTypeDef NVIC_InitStructure;//NVIC
//使能:SPI IO/SPI外设时钟/USART
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能 外设时钟GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//使能 USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能 外设SPI1
//重映射:SPI(NSS/SCK/MISO/MOSI)、USART
GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1);//NSS,使用软件管理方法
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);//SCK
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);//MISO
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);//MOSI
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);//usart
// GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);//usart
//SPI(NSS/SCK/MISO/MOSI)外设接口初始化;USART外设接口初始化
GPIO_InitStructure5.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
// GPIO_InitStructure5.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure5.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure5.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure5.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure5.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA , &GPIO_InitStructure5);
//SPI初始化;使能SPI;开启SPI接收数据中断
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//通信方式:全双工模式;包括(半双工、全双工、串行发、串行收)
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;//SPI模式:从模式;包括(主、从)
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//八位实际只有7位
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//时钟极性:空闲时高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//数据在第二个跳变沿被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;//从机--该处注意
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//传输速度为84MHz/256=328.125KHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//大端 小端 MSB 10000001 LSB
SPI_InitStructure.SPI_CRCPolynomial = 7;//设置CRC校验多项式
SPI_Init(SPI1,&SPI_InitStructure);
SPI_Cmd(SPI1,ENABLE);//使能SPI1
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);//开启相关中断
//USART初始化;USART设置;使能USART
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA , &GPIO_InitStructure);
//串口设置
Usart_InitStructure.USART_BaudRate = 9600;//波特率
Usart_InitStructure.USART_WordLength = USART_WordLength_8b;//八个数据位
Usart_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
Usart_InitStructure.USART_Parity = USART_Parity_No;//奇偶校验
Usart_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//失能硬件流
// Usart_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx ;//发送使能
Usart_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &Usart_InitStructure); //将以上赋完值的结构体带入库函数USART_Init进行初始化
USART_Cmd(USART1,ENABLE);//USART_CR1控制寄存器1 UE位激活USART
//NVIC配置
NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQn;//定义的初始化中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
while(1){};
}
u8 U_data,S_data;
//编写中断服务程序
void SPI1_IRQHandler(void)
{
if(SPI_I2S_GetITStatus(SPI1,SPI_I2S_IT_RXNE)!= RESET)//接收缓冲区 0缓冲区为空;1缓冲区不为空
{
S_data = SPI_I2S_ReceiveData(SPI1);
USART_SendData(USART1,S_data);
}
// SPI_I2S_ClearITPendingBit(SPI1, SPI_I2S_IT_RXNE);
}
注意:
[1] 每个步骤一定要理解后配置,不要配置错了
“之前就是重映射那里将SPI写成了SAI”导致了错误
[2] 连线一定要连对(MISO–MISO MOSI–MOSI NSS–NSS SCK–SCK)
“之前连错了MOSI–MISO,导致串口输出的数据是0xFF,因为从器件的MOSI连接的是主器件的MISO(默认为高组态,所以输出为0xFF)”