stm32之SPI

1.SPI是一种高速全双工串行同步通信总线,在软件设计过程中,使用SPI总线必须有一个主机主要是指在数据传输时, 空闲时就不必强调主机和从机。 SPI 可以有多主机模式。

(1)接口
  MOSI:数据线, Master Output Slave Input 主机输出, 从机输入
  MISO:数据线, Master Input Slave Output 主机输入, 从机输出
  /SS: 从器件使能信号, 由主器件控制,是片选线,一般不用
  4 线 SPI 接口: SCLK MOSI MISO CS
  3 线 SPI 接口: SCLK MOSI MISO 

 2.SPI接口如何连接

  CPOL=0, 串行同步时钟的空闲状态为低电平
    CPOL=1, 串行同步时钟的空闲状态为 高电平。

 时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。
      CPHA=0, 在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;
   CPHA=1, 在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。 

3.SPI特征

● 3 线全双工同步传输
● 带或不带第三根双向数据线的双线单工同步传输
● 8 或 16 位传输帧格式选择
● 主或从操作
● 支持多主模式
● 8 个主模式波特率预分频系数(最大为 fPCLK/2)
● 从模式频率 (最大为 fPCLK/2)
● 主模式和从模式的快速通信
● 主模式和从模式下均可以由软件或硬件进行 NSS 管理: 主/从操作模式的动态改变
● 可编程的时钟极性和相位
● 可编程的数据顺序, MSB 在前或 LSB 在前
● 可触发中断的专用发送和接收标志
● SPI 总线忙状态标志
● 支持可靠通信的硬件 CRC
─ 在发送模式下, CRC 值可以被作为最后一个字节发送
─ 在全双工模式中对接收到的最后一个字节自动进行 CRC 校验
● 可触发中断的主模式故障、 过载以及 CRC 错误标志
● 支持 DMA 功能的 1 字节发送和接收缓冲器: 产生发送和接受请求
4.SPI结构框图通常 SPI 通过 4 个引脚与外部器件相连:

● MISO: 主设备输入/从设备输出引脚。 该引脚在从模式下发送数据, 在主模式下接收数据。
● MOSI: 主设备输出/从设备输入引脚。 该引脚在主模式下发送数据, 在从模式下接收数据。
● SCK: 串口时钟, 作为主设备的输出, 从设备的输入
● NSS: 从设备选择。 这是一个可选的引脚, 用来选择主/从设备。 它的功能是用来作为“ 片选引脚” , 让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。 
.NSS管理
NSS输入又分为硬件输入和软件控制输入两种模式:
软件模式:
  对于SPI主机来说, 需要设置SPI_CR1寄存器的SSM为1和SSI位为1,SSM为1是为了使能软件管理。 NSS有内部和外部引脚。 这时候, 外部引脚留作他用( 可以用来作为GPIO驱动从设备的片选信号) 。
 内部 NSS引脚电平则通过SPI_CRL寄存器的SSI位来驱动。 SSI位为1是为了使NSS内电平为高电平。
要保持MSTR和SPE位为1, 也就是说要保持主机模式,只有NSS接到高电平信号时, 这两位才能保持置1.也就是说对于STM32的SPI, 要保持为主机状态, 内部输入的NSS电平必须为高。
  对于SPI 从机来说:
如果从机选择STM32的一个SPI, 譬如主机选为SPI1, 从机选为SPI2, 则要按照以下操作手册说, NSS引脚在完成字节传输之前必须连接到一个低电平信号。 在软件模式下, 则需要设置SPI_CR1寄存器的SSM为1( 软件管理使能) 和SSI位为0 

硬件模式:
  对于主机, 我们的NSS可以直接接到高电平, 对于从机, NSS接低就可以。 
4.如何配置 SPI
A. 从模式配置:
  1) 设置 DFF 位以定义数据帧格式为 8 位或 16 位。
  2) 选择 CPOL 和 CPHA 位来定义数据传输和串行时钟之间的相位关系(见图 212)。 为保证正确的数据传输, 从设备和主设备的 CPOL 和 CPHA 位必须配置成相同的方式。
  3) 帧格式(SPI_CR1 寄存器中的 LSBFIRST 位定义的” MSB 在前” 还是” LSB 在前” )必须与主设备相同
  4) 硬件模式下(参考从选择(NSS)脚管理部分), 在完整的数据帧(8 位或 16 位)传输过程中,NSS 引脚必须为低电平。 在 NSS 软件模式下, 设置 SPI_CR1 寄存器中的 SSM 位并清除 SSI 位。
  5) 清除 MSTR 位、 设置 SPE 位(SPI_CR1 寄存器), 使相应引脚工作于 SPI 模式下。在这个配置中, MOSI 引脚是数据输入, MISO 引脚是数据输出。
B. 主模式配置
  1) 通过 SPI_CR1 寄存器的 BR[2:0]位定义串行时钟波特率。
  2) 选择 CPOL 和 CPHA 位, 定义数据传输和串行时钟间的相位关系(见图 212)。
  3) 设置 DFF 位来定义 8 位或 16 位数据帧格式。
  4) 配置 SPI_CR1 寄存器的 LSBFIRST 位定义帧格式。
  5) 如果需要 NSS 引脚工作在输入模式, 硬件模式下, 在整个数据帧传输期间应把 NSS脚连接到高电平; 在软件模式下, 需设置 SPI_CR1 寄存器的 SSM 位和 SSI 位。 如果 NSS引脚工作在输出模式, 则只需设置 SSOE 位。

  6) 必须设置 MSTR 位和 SPE 位(只当 NSS 脚被连到高电平, 这些位才能保持置位)。在这个配置中, MOSI 引脚是数据输出, 而 MISO 引脚是数据输入。

  1 #include "main.h"
  2 /*********************
  3 函数名称:SPI_Init
  4 函数功能:SPI初始化
  5 函数参数:无
  6 函数返回值:无
  7 备注:配置spi为主模式
  8       CS--PB0---通用推挽输出
  9             MISO--PA6---主机输入--浮空输入
 10             MOSI--PA7---主机输出--复用推挽输出
 11             SCLK--PA5---主机输出--复用推挽输出
 12 **********************/
 13 void SPI_Config(void)
 14 {
 15 #if reg_progream
 16     //1.时钟使能PA PB SPI1
 17     RCC->APB2ENR |=(1<<2)|(1<<3)|(1<<12);
 18     //2. CS  PB0  通用推挽输出
 19     GPIOB->CRL &=~(0xf<<0);
 20     GPIOB->CRL|=(3<<0);
 21     //MISO  PA6  浮空输入
 22     GPIOA->CRL &=~(0xfff<<20);
 23     GPIOA->CRL |=(4<<24);
 24     //SCLK  PA5  主机输出--复用推挽输出
 25     GPIOA->CRL |=(0xb<<20);
 26     //MOSI  PA7  主机输出--复用推挽输出
 27     GPIOA->CRL |=(0xb<<28);
 28 /***********SPI配置***************/
 29     //串行波特率--/2
 30     SPI1->CR1 &=~(0x7<<3);
 31     //CPOL -1  CPHA -1
 32     SPI1->CR1 |=(1<<0);
 33     SPI1->CR1 |=(1<<1);
 34     //使用八位数据帧格式
 35   SPI1->CR1 &=~(1<<11);
 36     //高位在前
 37     SPI1->CR1 &=~(1<<7);
 38     //先开启LSB,SSI=1,启用软件从设备管理
 39     SPI1->CR1 |=(1<<8);
 40     SPI1->CR1 |=(1<<9);
 41     //SSOE
 42     SPI1->CR2 |=(1<<2);
 43     //配置为主设备
 44     SPI1->CR1 |=(1<<2);
 45     //SPI使能
 46     SPI1->CR1 |=(1<<6);
 47     //双线模式
 48     SPI1->CR1 &=~(1<<15);
 49     //全双工
 50     SPI1->CR1 &=~(1<<10);
 51     //
 52 #else
 53         GPIO_InitTypeDef GPIO_InitStruct;
 54     SPI_InitTypeDef  SPI_InitStruct;
 55     //使能时钟  PA  PB  SPI1
 56     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_SPI1,ENABLE);
 57     //CS     PB0  通用推挽输出
 58     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
 59     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
 60     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
 61     GPIO_Init(GPIOB,&GPIO_InitStruct);
 62     //MISO   PA6  浮空输入
 63     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 64     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
 65     GPIO_Init(GPIOA,&GPIO_InitStruct);
 66     //MOSI   PA7  复用推挽输出    SCLK   PA5  复用推挽输出
 67     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
 68     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
 69     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
 70     GPIO_Init(GPIOA,&GPIO_InitStruct);
 71     
 72     SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//串行时钟波特率 fclk/2
 73     SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;//时钟极性:高
 74     SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;//第2个跳边沿采样
 75     SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;//数据帧:8位
 76     SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//高位在前
 77     SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//软件管理
 78     SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线双向全双工
 79     SPI_InitStruct.SPI_Mode = SPI_Mode_Master;//主机
 80 //    SPI_InitStruct.SPI_CRCPolynomial = 7;//CRC多项式复位值
 81     SPI_Init(SPI1,&SPI_InitStruct);
 82     SPI_Cmd(SPI1,ENABLE);//开启SPI
 83     
 84 #endif
 85 }
 86 //接收
 87 uint8_t SPI_ReadWrite(u8 data)
 88 {
 89 #if  reg_progream
 90     //发送数据  
 91     while(!(SPI3->SR &(1<<1)));//为1,为空
 92     SPI1->DR = data;
 93     //等待接收到数据
 94     while((SPI1->SR &(1<<0))==0);
 95     //保存数据
 96     return SPI1->DR;  //数据寄存器
 97 #else
 98     while((SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE))==RESET)
 99     SPI_I2S_SendData(SPI1,data);
100     while((SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE))==RESET)
101     return SPI_I2S_ReceiveData(SPI1);
102 #endif
103 }
SPI程序配置



原文地址:https://www.cnblogs.com/juan-4-14/p/12734816.html