一、SPI简介
1、英文全称:Serial Peripheral interface
2、中文全称:串行外设接口
3、特点:全双工、同步、传输速率快
4、传输方式:
CLK:时钟线
MOSI:主机输出,从机输入
MISO:从机输入,从机输出
CS:片选,选择通信的从机
(输出引脚配置为推挽输出模式,输入引脚配置为浮空或上拉输入模式)
5、SPI数据交换
在CLK高电平(上升沿)输出数据,CLK低电平(下降沿)读取数据
6、相关缩写
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:
CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;
(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位
CPHA=0,表示第一个边沿:
①CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
②CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
CPHA=1,表示第二个边沿:
CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
(3) SCK=SCLK=SPI的时钟
7、STM32标准外设库SPI_InitTypeDef的定义
typedef struct
{
uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
uint16_t SPI_Mode; // 设置SPI 的主从模式
uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项
uint16_t SPI_CPOL; // 设置时钟极性
uint16_t SPI_CPHA; // 设置时钟相位
uint16_t SPI_NSS; //设置NSS 信号由硬件(NSS管脚)还是软件控制
uint16_t SPI_BaudRatePrescaler; //设置SPI 波特率预分频值
uint16_t SPI_FirstBit; //设置数据传输顺序是MSB 位在前还是LSB 位在前
uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可
}SPI_InitTypeDef;
8、代码
一、软件模拟
c文件
#include "stm32f10x.h" // Device header
#include "SoftWare_SPI.h"
void SPI_W_CS(uint8_t BitValue)
{
GPIO_WriteBit(SPI_CS_Port, SPI_CS_Pin, (BitAction)BitValue);
}
void SPI_W_CLK(uint8_t BitValue)
{
GPIO_WriteBit(SPI_CLK_Port, SPI_CLK_Pin, (BitAction)BitValue);
}
uint8_t SPI_R_MISO(void)
{
return GPIO_ReadInputDataBit(SPI_MISO_Port, SPI_MISO_Pin);
}
void MySPI_W_MOSI(uint8_t BitValue)
{
GPIO_WriteBit(SPI_MOSI_Port, SPI_MOSI_Pin, (BitAction)BitValue);
}
void SW_SPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = SPI_CS_Pin | SPI_CLK_Pin | SPI_MOSI_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = SPI_MISO_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_W_CS(1);
SPI_W_CLK(0);
}
void SPI_Start(void)
{
SPI_W_CS(0);
}
void SPI_Stop(void)
{
SPI_W_CS(1);
}
uint8_t SPI_SwapByte(uint8_t ByteSend)
{
uint8_t i, ByteReceive = 0x00;
for (i = 0; i < 8; i ++)
{
MySPI_W_MOSI(ByteSend & (0x80 >> i));
SPI_W_CLK(1);
if (SPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
SPI_W_CLK(0);
}
return ByteReceive;
}
h文件
#ifndef __SoftWare_SPI__H__
#define __SoftWare_SPI__H__
#define SPI_CS_Port GPIOA //CS引脚选择
#define SPI_CS_Pin GPIO_Pin_4
#define SPI_CLK_Port GPIOA //CLK引脚选择
#define SPI_CLK_Pin GPIO_Pin_5
#define SPI_MISO_Port GPIOA //MISO引脚选择
#define SPI_MISO_Pin GPIO_Pin_6
#define SPI_MOSI_Port GPIOA //MOSI引脚选择
#define SPI_MOSI_Pin GPIO_Pin_7
void SPI_W_CS(uint8_t BitValue);
void SPI_W_CLK(uint8_t BitValue);
uint8_t SPI_R_MISO(void);
void MySPI_W_MOSI(uint8_t BitValue);
void SW_SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t ByteSend);
#endif
二、SPI外设
c文件
#include "stm32f10x.h" // Device header
#include "HardWare_SPI.h"
void SW_SPI_W_CS(uint8_t BitValue)
{
GPIO_WriteBit(SPI_CS_Port, SPI_CS_Pin, (BitAction)BitValue);
}
void HW_SPI_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;//从机选择引脚,软件模拟
GPIO_InitStructure.GPIO_Pin = SPI_CS_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //外设控制的输出,使用复用推挽输出
GPIO_InitStructure.GPIO_Pin = SPI_CLK_Pin | SPI_MOSI_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //主机输入,上拉输入模式
GPIO_InitStructure.GPIO_Pin = SPI_MISO_Pin;
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_DataSize = SPI_DataSize_8b;
SPI_InitSTructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitSTructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitSTructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitSTructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitSTructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitSTructure.SPI_NSS = SPI_NSS_Hard;
SPI_InitSTructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitSTructure);
SPI_Cmd(SPI1, ENABLE);
SW_SPI_W_CS(1);//默认不选中从机
}
void HW_SPI_Start(void)
{
SW_SPI_W_CS(0);
}
void HW_SPI_Stop(void)
{
SW_SPI_W_CS(1);
}
uint8_t HW_SPI_SwapByte(uint8_t ByteSend)
{
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//判断TXE(发送寄存器)是否为空,为空置一
SPI_I2S_SendData(SPI1, ByteSend);//写入数据到TDR
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//等待RXNE为1,表示收到一个字节,表示发送时序完成
//要接收必须先发送
return SPI_I2S_ReceiveData(SPI1);//读取RDR接收的数据
}
h文件
#ifndef __HardWare_SPI__H__
#define __HardWare_SPI__H__
#define SPI_CS_Port GPIOA //CS引脚选择
#define SPI_CS_Pin GPIO_Pin_4
#define SPI_CLK_Port GPIOA //CLK引脚选择
#define SPI_CLK_Pin GPIO_Pin_5
#define SPI_MISO_Port GPIOA //MISO引脚选择
#define SPI_MISO_Pin GPIO_Pin_6
#define SPI_MOSI_Port GPIOA //MOSI引脚选择
#define SPI_MOSI_Pin GPIO_Pin_7
void SW_SPI_W_CS(uint8_t BitValue);
void HW_SPI_Init(void);
void HW_SPI_Start(void);
void HW_SPI_Stop(void);
uint8_t HW_SPI_SwapByte(uint8_t ByteSend);
#endif