各总线协议汇总(二)- SPI
二、SPI(Serial Peripheral Interface)
(一)、SPI接口
SPI和IIC一样也是有模拟和硬件两种方式(模拟方式都是根据相应的时序图来写)。
SCLK:时钟信号,由主机发出(master)
MISO:主机输入从机输出(master input slave output)
MOSI:主机输出从机输入(master output slave input)
CS:片选,从设备使能信号(chip selection)
(二)、SPI工作模式
SPI四种工作模式:
CPOL:时钟极性选择(clock polarity),决定SPI空闲(idle)时的极性
0–> 低电平 1–>高电平
CPHA:时钟相位选择(clock phase),决定SPI在第几条边沿采样
0–> 在SCLK第一条边沿采样 1–> 在SCK第二个条边沿采样
(三)、SPI程序
1、硬件SPI
#define SPI_SCLK_PORT GPIOA
#define SPI_MISO_PORT GPIOA
#define SPI_MOSI_PORT GPIOA
#define SPI_CS_PORT GPIOA
#define SPI_SCLK_PIN GPIO_Pin_5
#define SPI_MISO_PIN GPIO_Pin_6
#define SPI_MOSI_PIN GPIO_Pin_7
#define SPI_CS_PIN GPIO_Pin_4
#define SPI_CS_ENABLE GPIO_ResetBits(SPI_CS_PORT,SPI_CS_PIN)
#define SPI_CS_DISENABLE GPIO_SetBits(SPI_CS_PORT,SPI_CS_PIN)
/*******************************************************
* FunctionName: SPI_GPIO_Config
* Function: Configure SPI_GPIO
* Arguments: None
* Return: None
* Author seanOY
********************************************************/
//SPI引脚设置
void SPI_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能引脚时钟
//SCLK configuration
GPIO_InitStructure.GPIO_Pin = SPI_SCLK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SPI_SCLK_PORT, &GPIO_InitStructure);
//MISO configuration
GPIO_InitStructure.GPIO_Pin = SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SPI_MISO_PORT, &GPIO_InitStructure);
//MOSI configuration
GPIO_InitStructure.GPIO_Pin = SPI_MOSI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SPI_MOSI_PORT, &GPIO_InitStructure);
//CS configuration
GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SPI_CS_PORT, &GPIO_InitStructure);
}
/*******************************************************
* FunctionName: SPI_Configuration
* Function: Configure SPI
* Arguments: None
* Return: None
* Author seanOY
********************************************************/
//SPI配置函数
void SPI_Configuration(void)
{
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //预分频值
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个边沿
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟在idle为高电平
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC校验计算的多项式的数据位长度
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //帧格式为8位
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
//设置最先传输的字节为最高有效位 (the Most Significant Bit)
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件控制NSS信号
SPI_Init(SPI1,&SPI_InitStructure); //初始化SPI1
SPI_Cmd(SPI1, ENABLE); //使能SPI1
}
//SPI是环形结构,有读就有写
/*******************************************************
* FunctionName: SPI_Writebyte
* Function: Write a byte
* Arguments: data
* Return: None
* Author seanOY
********************************************************/
void SPI_Writebyte(unsigned char data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPI1);
}
/*******************************************************
* FunctionName: SPI_Readbyte
* Function: Read a byte
* Arguments: data
* Return: None
* Author seanOY
********************************************************/
unsigned char SPI_Readbyte(unsigned char data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}
读的时候要注意一个问题,因为从模式是没法提供时钟的,所以主模式下必须要在接收的同时提供时钟。办法就是发送一个字节来实现,因为还是上面说的,发送一个字节就意味着收到一个字节,代码和写完全一样,只要把读出来的字节保存即可。
2、模拟SPI(软件SPI)
#define SPI_Delay() DelayUs ( 200 )
//这里的DelayUs是用systick定时的,我就不将这些程序打出来了
static void SPI_Config ( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
//SCLK configuration
RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//MISO configuration
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//MOSI configuration
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//CS configuration
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void SPI_SendByte ( uint8_t byte )
{
uint8_t counter;
for(counter=0;counter<8;counter++)
{
if ( byte & 0x80 )
MOSI_1 ();
else
MOSI_0 ();
SPI_Delay();
SCK_0 ();
SPI_Delay();
SCK_1();
SPI_Delay();
byte <<= 1;
}
}
uint8_t SPI_ReadByte ( void )
{
uint8_t counter;
uint8_t SPI_Data;
for(counter=0;counter<8;counter++)
{
SPI_Data <<= 1;
SCK_0 ();
SPI_Delay();
if ( GPIO_ReadInputDataBit ( GPIOA, GPIO_Pin_6 ) == 1)
SPI_Data |= 0x01;
SPI_Delay();
SCK_1 ();
SPI_Delay();
}
return SPI_Data;
}