SPI(二):主从通信

1、主从通信:主机轮询+从机中断

实现主从通信需要2块stm32单片机,一块用作主机,一块用作从机。

主机   										从机
CLK:PA5(复用推挽输出).......................	PA5(浮空输入)
NSS:PB9(软件+通用推挽输出)...................PA4(硬件输入)
MOSI:PA7(复用推挽输出).......................PA7(浮空输入)
MISO:PA6(浮空输入)...........................PA6(复用推挽输出)

实验要求:按下主机按键(按键连接PB0),主机向从机发送数据,从机接受到数据后,也向主机发送数据。

主机的代码如下
①SPI.c文件的代码如下

#include "SPI.h"

/**
 * @brief:SPI1初始化函数
 */
SPI_HandleTypeDef hspi1;        //SPI1配置结构体
void SPI_Init(void)
{
    /* SPI1的初始化 */
    __HAL_RCC_SPI1_CLK_ENABLE();                    //使能SPI1的时钟
    hspi1.Instance = SPI1;                          //选择SPI1
    hspi1.Init.Mode = SPI_MODE_MASTER;              //模式:主机模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    //双向
    hspi1.Init.DataSize  = SPI_DATASIZE_8BIT;       //1个字节
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      //时钟空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          //第一个边沿采集
    hspi1.Init.NSS = SPI_NSS_SOFT;                  //GPIO控制NSS
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,72MHz/2
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         //高位先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         //关闭TI模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭CRC校验
    HAL_SPI_Init(&hspi1);
}

/**
 * @brief:HAL_SPI_Init()调用此函数
 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
    if(hspi->Instance == SPI1)
    {
        /* IO口的初始化:SPI1的CLK = PA5,MISO = PA6,MOSI = PA7,NSS = PB9*/
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        GPIO_Init.Mode = GPIO_MODE_AF_PP;           //复用推挽输出
        GPIO_Init.Pin = GPIO_PIN_5|GPIO_PIN_7;      //PA5和PA7
        GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        GPIO_Init.Mode = GPIO_MODE_INPUT;           //输入
        GPIO_Init.Pin = GPIO_PIN_6;                 //PA6
        GPIO_Init.Pull = GPIO_NOPULL;               //浮空输入
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        __HAL_RCC_GPIOB_CLK_ENABLE();
        GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP;       //通用推挽输出
        GPIO_Init.Pin = GPIO_PIN_9;                 //PB9
        GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
        HAL_GPIO_Init(GPIOB, &GPIO_Init);
        
    }else if(hspi->Instance == SPI2)
    {
        
    }
}

②SPI.h文件的代码如下

#ifndef __SPI_H
#define __SPI_H

#include "stm32f1xx_hal.h"
#define CSS_ENABLE          HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET)//拉低NSS,开始时序
#define CSS_DISABLE         HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET)  //拉高SS,终止时序

extern SPI_HandleTypeDef hspi1;        //SPI1配置结构体

void SPI_Init(void);
#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "SPI.h"
#include "UART.h"
#include "Key.h"

uint8_t WData[5] = {1,2,33,4,5};
uint8_t RData[5];
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    SPI_Init();
    Key_GPIO_Init();
    printf("程序测试\r\n");
    
	while(1){
       switch(Key_Scan())
        {
            case 1:
            printf("按键按下了,主机向从机发送数据!\r\n");
            
            /* 发送数据 */
            CSS_ENABLE;
            HAL_SPI_TransmitReceive(&hspi1, WData, RData, 5, 1000);//发送数据
            CSS_DISABLE;
            for(uint8_t i = 0; i<sizeof(RData)/sizeof(RData[0]); i++)
            {
                printf("从机发送给主机的数据RData[%d] = %d\r\n",i,RData[i]);
            }
            break;
        }
	}	
}

从机的代码如下
①SPI.c文件的代码如下

#include "SPI.h"

/**
 * @brief:SPI1初始化函数
 */
SPI_HandleTypeDef hspi1;        //SPI1配置结构体
void SPI_Init(void)
{
    /* SPI1的初始化 */
    __HAL_RCC_SPI1_CLK_ENABLE();                    //使能SPI1的时钟
    hspi1.Instance = SPI1;                          //选择SPI1
    hspi1.Init.Mode = SPI_MODE_SLAVE;               //模式:从机模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    //双向
    hspi1.Init.DataSize  = SPI_DATASIZE_8BIT;       //1个字节
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      //时钟空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          //第一个边沿采集
    hspi1.Init.NSS = SPI_NSS_HARD_INPUT;            //NSS:硬件控制输入
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,72MHz/2
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         //高位先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         //关闭TI模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭CRC校验
    HAL_SPI_Init(&hspi1);
    
    /* 配置SPI1的NVIC */
    HAL_NVIC_SetPriority(SPI1_IRQn,3,0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
}

/**
 * @brief:HAL_SPI_Init()调用此函数
 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
    if(hspi->Instance == SPI1)
    {
        /* IO口的初始化:SPI1的CLK = PA5,MISO = PA6,MOSI = PA7,NSS = 硬件控制 */
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        GPIO_Init.Mode = GPIO_MODE_INPUT;           //输入模式
        GPIO_Init.Pin = GPIO_PIN_5|GPIO_PIN_7;      //PA5和PA7
        GPIO_Init.Pull = GPIO_NOPULL;               //浮空输入
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        GPIO_Init.Mode = GPIO_MODE_AF_PP;           //复用推挽输出
        GPIO_Init.Pin = GPIO_PIN_6;                 //PA6
        GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
    }else if(hspi->Instance == SPI2)
    {
        
    }
}

②SPI.h文件的代码如下

#ifndef __SPI_H
#define __SPI_H

#include "stm32f1xx_hal.h"
extern SPI_HandleTypeDef hspi1;        //SPI1配置结构体
void SPI_Init(void);

#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "SPI.h"
#include "UART.h"

uint8_t WData[5] = {1,2,33,4,5};
uint8_t RData[5];
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    SPI_Init();
    printf("程序测试\r\n");
    
    HAL_SPI_TransmitReceive_IT(&hspi1, WData, RData, 5);//开启接受发送数据,开启接受发送数据完成中断。

	while(1){
     
	}	
}

④stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "SPI.h" 
#include "UART.h"

/**
 * @brief:SPI1的中断服务函数
 */
void SPI1_IRQHandler(void)
{
    HAL_SPI_IRQHandler(&hspi1);//中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:SPI1收发数据完成的中断服务函数
 */
extern uint8_t RData[5];
extern uint8_t WData[5];
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
   if(hspi->Instance == SPI1)
   {
       for(uint8_t i = 0; i<sizeof(RData)/sizeof(RData[0]); i++)
       {
           printf("主机发送给从机的数据RData[%d] = %d\r\n",i,RData[i]);
       }
       HAL_SPI_TransmitReceive_IT(&hspi1, WData, RData, 5);//开启接受发送数据,开启接受发送数据完成中断。
   }else if(hspi->Instance == SPI2)
   {
       
   }
}

综上从机的NSS受主机的NSS控制,所以配置为SPI_NSS_HARD_INPUT(硬件控制输入),无需手动开关
从机不会主动的接受和发送数据,从机中HAL_SPI_TransmitReceive_IT(&hspi1, WData, RData, 5)开启了接受发送数据+完成中断。当从机接受到主机发送来的数据的同时,才会把WData的数据发送给主机。当接受完成后(同时也发送完成)会进入中断回调函HAL_SPI_TxRxCpltCallback()。

2、主从通信:主机中断+从机DMA

主机的代码如下
①SPI.c文件的代码如下

#include "SPI.h"

/**
 * @brief:SPI1初始化函数
 */
SPI_HandleTypeDef hspi1;        //SPI1配置结构体
void SPI_Init(void)
{
    /* SPI1的初始化 */
    __HAL_RCC_SPI1_CLK_ENABLE();                    //使能SPI1的时钟
    hspi1.Instance = SPI1;                          //选择SPI1
    hspi1.Init.Mode = SPI_MODE_MASTER;              //模式:主机模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    //双向
    hspi1.Init.DataSize  = SPI_DATASIZE_8BIT;       //1个字节
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      //时钟空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          //第一个边沿采集
    hspi1.Init.NSS = SPI_NSS_SOFT;                  //GPIO控制NSS
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,72MHz/2
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         //高位先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         //关闭TI模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭CRC校验
    HAL_SPI_Init(&hspi1);
    
	/* 配置SPI1的NVIC */
    HAL_NVIC_SetPriority(SPI1_IRQn,3,0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
}

/**
 * @brief:HAL_SPI_Init()调用此函数
 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
    if(hspi->Instance == SPI1)
    {
        /* IO口的初始化:SPI1的CLK = PA5,MISO = PA6,MOSI = PA7,NSS = PB9*/
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        GPIO_Init.Mode = GPIO_MODE_AF_PP;           //复用推挽输出
        GPIO_Init.Pin = GPIO_PIN_5|GPIO_PIN_7;      //PA5和PA7
        GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        GPIO_Init.Mode = GPIO_MODE_INPUT;           //输入
        GPIO_Init.Pin = GPIO_PIN_6;                 //PA6
        GPIO_Init.Pull = GPIO_NOPULL;               //浮空输入
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        __HAL_RCC_GPIOB_CLK_ENABLE();
        GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP;       //通用推挽输出
        GPIO_Init.Pin = GPIO_PIN_9;                 //PB9
        GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;      //最大输出速度:最低2MHz
        HAL_GPIO_Init(GPIOB, &GPIO_Init);
        
    }else if(hspi->Instance == SPI2)
    {
        
    }
}

②SPI.h文件的代码如下

#ifndef __SPI_H
#define __SPI_H

#include "stm32f1xx_hal.h"
#define CSS_ENABLE          HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET)//拉低NSS,开始时序
#define CSS_DISABLE         HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET)  //拉高SS,终止时序

extern SPI_HandleTypeDef hspi1;        //SPI1配置结构体

void SPI_Init(void);
#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "SPI.h"
#include "UART.h"
#include "Key.h"

uint8_t WData[5] = {1,2,33,4,5};
uint8_t RData[5];
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    SPI_Init();
    Key_GPIO_Init();
    printf("程序测试\r\n");
    
	while(1){
       switch(Key_Scan())
        {
            case 1:
            printf("按键按下了,主机向从机发送数据!\r\n");
            
            /* 发送数据 */
            CSS_ENABLE;
            HAL_SPI_TransmitReceive_IT(&hspi1, WData, RData, 5);//开启接受发送数据中断。          
            break;
        }
	}	
}

④stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "SPI.h" 
#include "UART.h"

/**
 * @brief:SPI1的中断服务函数
 */
void SPI1_IRQHandler(void)
{
    HAL_SPI_IRQHandler(&hspi1);//中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:SPI1收发数据完成的中断服务函数
 */
extern uint8_t RData[5];
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
   if(hspi->Instance == SPI1)
   {
       CSS_DISABLE;  //读取数据完成,关闭NSS
       for(uint8_t i = 0; i<sizeof(RData)/sizeof(RData[0]); i++)
       {
           printf("RData[%d] = %d\r\n",i,RData[i]);
       }
   }else if(hspi->Instance == SPI2)
   {
       
   }
}

从机的代码如下
①SPI.c文件的代码如下

#include "SPI.h"

/**
 * @brief:SPI1初始化函数
 */
SPI_HandleTypeDef hspi1;        //SPI1配置结构体
void SPI_Init(void)
{
    /* SPI1的初始化 */
    __HAL_RCC_SPI1_CLK_ENABLE();                    //使能SPI1的时钟
    hspi1.Instance = SPI1;                          //选择SPI1
    hspi1.Init.Mode = SPI_MODE_SLAVE;               //模式:从机模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    //双向
    hspi1.Init.DataSize  = SPI_DATASIZE_8BIT;       //1个字节
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      //时钟空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          //第一个边沿采集
    hspi1.Init.NSS = SPI_NSS_HARD_INPUT;            //NSS:硬件控制输入
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,72MHz/2
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         //高位先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         //关闭TI模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭CRC校验
    HAL_SPI_Init(&hspi1);
}

/**
 * @brief:HAL_SPI_Init()调用此函数
 */
DMA_HandleTypeDef hdma1_Rx;                       //SP11的DMA1的通道2配置结构体
DMA_HandleTypeDef hdma1_Tx;                       //SP11的DMA1的通道3配置结构体
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
    if(hspi->Instance == SPI1)
    {
        /* IO口的初始化:SPI1的CLK = PA5,MISO = PA6,MOSI = PA7,NSS = 硬件控制 */
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        GPIO_Init.Mode = GPIO_MODE_INPUT;           //输入模式
        GPIO_Init.Pin = GPIO_PIN_5|GPIO_PIN_7;      //PA5和PA7
        GPIO_Init.Pull = GPIO_NOPULL;               //浮空输入
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        
        GPIO_Init.Mode = GPIO_MODE_AF_PP;           //复用推挽输出
        GPIO_Init.Pin = GPIO_PIN_6;                 //PA6
        GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
        HAL_GPIO_Init(GPIOA, &GPIO_Init);

        /* 1、初始化DMA1的通道2配置 */
        __HAL_RCC_DMA1_CLK_ENABLE();                                  //使能DMA1的时钟
        hdma1_Rx.Instance = DMA1_Channel2;                            //选择DMA1的通道2
        hdma1_Rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;      //外设站点数据宽度:8位
        hdma1_Rx.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_Rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;         //内存站点数据宽度:8位
        hdma1_Rx.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_Rx.Init.Direction = DMA_PERIPH_TO_MEMORY;               //传输方向:这里选择外设---->内存
        hdma1_Rx.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_Rx.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hspi1,hdmarx,hdma1_Rx);                        //SPI1和DMA1构建链接
        HAL_DMA_Init(&hdma1_Rx);
        
        HAL_NVIC_SetPriority(DMA1_Channel2_IRQn,3,0);
        HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
        
        /* 2、初始化DMA1的通道3配置 */
        hdma1_Tx.Instance = DMA1_Channel3;                            //选择DMA1的通道3
        hdma1_Tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;      //外设站点数据宽度:8位
        hdma1_Tx.Init.PeriphInc = DMA_PINC_DISABLE;                   //外设地址是否递增:选择不自增
        hdma1_Tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;         //内存站点数据宽度:8位
        hdma1_Tx.Init.MemInc = DMA_MINC_ENABLE;                       //内存地址是否递增:选择自增
        hdma1_Tx.Init.Direction = DMA_MEMORY_TO_PERIPH;               //传输方向:这里选择内存---->外设
        hdma1_Tx.Init.Mode = DMA_CIRCULAR;                            //计数器传输模式:自动重装
        hdma1_Tx.Init.Priority = DMA_PRIORITY_MEDIUM;                 //通道1传输优先级:选择中等
        __HAL_LINKDMA(&hspi1,hdmatx,hdma1_Tx);                        //SPI1和DMA1构建链接
        HAL_DMA_Init(&hdma1_Tx);
        
        HAL_NVIC_SetPriority(DMA1_Channel3_IRQn,3,0);
        HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
        
    }else if(hspi->Instance == SPI2)
    {
        
    }
}

②SPI.h文件的代码如下

#ifndef __SPI_H
#define __SPI_H

#include "stm32f1xx_hal.h"
extern SPI_HandleTypeDef hspi1;        //SPI1配置结构体
extern DMA_HandleTypeDef hdma1_Rx;     //SP11的DMA1的通道2配置结构体
extern DMA_HandleTypeDef hdma1_Tx;     //SP11的DMA1的通道3配置结构体

void SPI_Init(void);

#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "SPI.h"
#include "UART.h"

uint8_t WData[5] = {1,2,33,4,5};
uint8_t RData[5];
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    SPI_Init();
    printf("程序测试\r\n");
    
    HAL_SPI_TransmitReceive_DMA(&hspi1, WData, RData, 5);//开启接受发送数据DMA。

	while(1){
     
	}	
}

④stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "SPI.h" 
#include "UART.h"

/**
 * @brief:DMA完成的中断服务函数
 */
void DMA1_Channel2_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_Rx);//中断服务总函数
}

void DMA1_Channel3_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&hdma1_Tx);//中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:DMA收发数据完成的中断服务函数
 */
extern uint8_t RData[5];
extern uint8_t WData[5];
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
   if(hspi->Instance == SPI1)
   {
       for(uint8_t i = 0; i<sizeof(RData)/sizeof(RData[0]); i++)
       {
           printf("主机发送给从机的数据RData[%d] = %d\r\n",i,RData[i]);
       }
       HAL_SPI_TransmitReceive_DMA(&hspi1, WData, RData, 5);//开启接受发送数据中断。
   }else if(hspi->Instance == SPI2)
   {
       
   }
}

3、主从通信:主从可交换模式

   因为主从通信下,只有主机才能主动向从机发送数据。例如A为主机,B为从机,而通信时只有A才能主动向B发送数据,而B不能主动向A发送数据。若需要B主动向A发送数据时,则需要主从身份交换,将B作为主机,A作为从机。

                  在这里插入图片描述
主从可交换模式:将2个设备都配置为主机,NSS配置为SPI_NSS_HARD_INPUT(硬件控制输入),如图:NSS引脚分别连接着对方的一个IO口。

①A作主机B作从机,则IOA输出低电平即可
      原因:B配置为主机,而NSS配置为SPI_NSS_HARD_INPUT且输入为低电平,即与B配置为主机冲突会产生一个MODF错误,会进入一个错误中断回调,再回调函数里面可配置作为从机的IO口引脚。
②B作主机A作从机,则IOB输出低电平即可
      原因:同理

①SPI.c文件的代码如下

#include "SPI.h"

/**
 * @brief:SPI1主机初始化函数
 */
SPI_HandleTypeDef hspi1;        //SPI1配置结构体
void SPI_MInit(void)
{
    /* SPI1的初始化 */
    __HAL_RCC_SPI1_CLK_ENABLE();                    //使能SPI1的时钟
    hspi1.Instance = SPI1;                          //选择SPI1
    hspi1.Init.Mode = SPI_MODE_MASTER;              //模式:主模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    //双向
    hspi1.Init.DataSize  = SPI_DATASIZE_8BIT;       //1个字节
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      //时钟空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          //第一个边沿采集
    hspi1.Init.NSS = SPI_NSS_HARD_INPUT;            //NSS:硬件输入
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,72MHz/2
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         //高位先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         //关闭TI模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭CRC校验
    HAL_SPI_Init(&hspi1);
    
    __HAL_SPI_ENABLE_IT(&hspi1, SPI_IT_ERR);        //使能错误中断
    __HAL_SPI_ENABLE(&hspi1);                       //使能SPI1
    /* 配置SPI1的NVIC */
    HAL_NVIC_SetPriority(SPI1_IRQn,3,0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
}

/**
 * @brief:SPI1从机初始化函数
 */
void SPI_SInit(void)
{
   /* 将其配置为从机 */
    __HAL_RCC_SPI1_CLK_ENABLE();                    //使能SPI1的时钟
    hspi1.Instance = SPI1;                          //选择SPI1
    hspi1.Init.Mode = SPI_MODE_SLAVE;               //模式:从模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    //双向
    hspi1.Init.DataSize  = SPI_DATASIZE_8BIT;       //1个字节
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      //时钟空闲低电平
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          //第一个边沿采集
    hspi1.Init.NSS = SPI_NSS_HARD_INPUT;            //NSS:硬件输入
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//2分频,72MHz/2
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         //高位先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         //关闭TI模式
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;//关闭CRC校验
    HAL_SPI_Init(&hspi1);
}
/**
 * @brief:HAL_SPI_Init()调用此函数
 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
    GPIO_InitTypeDef GPIO_Init;                     //IO口初始化结构体
    if(hspi->Instance == SPI1)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        /* PA3:连接对方NSS */
        GPIO_Init.Mode = GPIO_MODE_OUTPUT_PP;       //通用推挽输出
        GPIO_Init.Pin = GPIO_PIN_3;                 //PA3
        GPIO_Init.Speed = GPIO_SPEED_FREQ_LOW;      //最大输出速度:最低2MHz
        HAL_GPIO_Init(GPIOA, &GPIO_Init);
        HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);//先输出高电平
        
        if(hspi->Init.Mode == SPI_MODE_MASTER)          //为主机的引脚配置
        {
            /* IO口的初始化:SPI1的CLK = PA5,MISO = PA6,MOSI = PA7*/
            GPIO_Init.Mode = GPIO_MODE_AF_PP;           //复用推挽输出
            GPIO_Init.Pin = GPIO_PIN_5|GPIO_PIN_7;      //PA5和PA7
            GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
            HAL_GPIO_Init(GPIOA, &GPIO_Init);
            
            GPIO_Init.Mode = GPIO_MODE_INPUT;           //输入
            GPIO_Init.Pin = GPIO_PIN_6;                 //PA6
            GPIO_Init.Pull = GPIO_NOPULL;               //浮空输入
            HAL_GPIO_Init(GPIOA, &GPIO_Init);
        }
        else                                            //为从机的引脚配置
        {
            GPIO_Init.Mode = GPIO_MODE_AF_PP;           //复用推挽输出
            GPIO_Init.Pin = GPIO_PIN_6;                 //PA6
            GPIO_Init.Speed = GPIO_SPEED_FREQ_MEDIUM;   //最大输出速度:最高10MHz
            HAL_GPIO_Init(GPIOA, &GPIO_Init);
            
            GPIO_Init.Mode = GPIO_MODE_INPUT;           //输入
            GPIO_Init.Pin = GPIO_PIN_5|GPIO_PIN_7;      //PA5和PA7
            GPIO_Init.Pull = GPIO_NOPULL;               //浮空输入
            HAL_GPIO_Init(GPIOA, &GPIO_Init);
        }
    }else if(hspi->Instance == SPI2)
    {
        
    }
}

②SPI.h文件的代码如下

#ifndef __SPI_H
#define __SPI_H

#include "stm32f1xx_hal.h"
extern SPI_HandleTypeDef hspi1;        //SPI1配置结构体

void SPI_MInit(void);
void SPI_SInit(void);
#endif

③main.c文件的代码如下

#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "SPI.h"
#include "UART.h"
#include "Key.h"

uint8_t WData[5] = {1,2,33,4,5};
uint8_t RData[5];
int main(void){
    
	HAL_Init();
	HSE_RCC_Init(); 
	UART1_Init(115200);
    SPI_MInit();            //都先配置为主机
    Key_GPIO_Init();
    printf("程序测试\r\n");
    
	while(1){
       switch(Key_Scan())
        {
            case 1:
            printf("按键按下了!\r\n");
            
            /* 发送数据 */
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET); //PA3先输出低电平,把对方设置为从机
            HAL_Delay(20);                                      //等待20ms,等从机配置成功
            HAL_SPI_TransmitReceive_IT(&hspi1, WData, RData, 5);//开启接受发送数据中断。          
            break;
        }
	}	
}

④stm32f1xx_it.c文件的代码如下

#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h" 
#include "SPI.h" 
#include "UART.h"

/**
 * @brief:SPI1的中断服务函数
 */
void SPI1_IRQHandler(void)
{
    HAL_SPI_IRQHandler(&hspi1);//中断服务总函数
}

/******************* 下面的中断的回调函数 ***************************/
/**
 * @brief:SPI1收发数据完成的中断服务函数
 */
extern uint8_t RData[5];
extern uint8_t WData[5];
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
   if(hspi->Instance == SPI1)
   {
       HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);    //PA3输出高电平
       for(uint8_t i = 0; i<sizeof(RData)/sizeof(RData[0]); i++)
       {
           printf("RData[%d] = %d\r\n",i,RData[i]);
       }
       HAL_SPI_DeInit(&hspi1);                             //对SPI复位
       SPI_MInit();//将从机还原为主机
   }else if(hspi->Instance == SPI2)
   {
       
   }
}

/**
 * @brief:错误中断的回调函数
 */
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
    if(hspi->Instance == SPI1)
    {
        if(hspi->ErrorCode == HAL_SPI_ERROR_MODF)//模式错误
        {
            printf("我变成从机了!\r\n");
            HAL_SPI_DeInit(&hspi1);                             //对SPI复位
            SPI_SInit();                                        //配置为从机
            HAL_SPI_TransmitReceive_IT(&hspi1, WData, RData, 5);//开启接受发送数据中断。
        }
    }
    else if(hspi->Instance == SPI2)
    {
        
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值