文章目录
SPI接口
Serial Peripheral interface
串行外围设备接口
一种高速、全双工、同步的通信主线
芯片管脚只占用四根线 节约芯片管脚 PCB布局节省空间
主要应用 EEPROM FLASH 实时时钟 AD转换器 数字信号处理器 数字信号解码器之间
概要图:
4条线通信:
MISO : 主设备数据输入 从设备数据输入
MOSI : 主设备数据输出 从设备数据输入
SCLK : 时钟信号,主设备产生
CS : 设备片选信号,主设备控制
工作原理
① 硬件上为4根线
② 主机和从机都有一个串行移位寄存器 主机通过向它SPI串行寄存器写入一个字节来发起一次传输
③ 串行移位寄存器通过MOSI信号线将字节传送给从机,从机通过MISO信号线返回。交换主从机内容
④ 外设的写操作读操作是同步完成的
如果只进行写操作,主机忽略接收的字节
若主机要读取从机 的一个字节必须发送一个空字节来引发从机传输
特点
==== 3线全双工同步运输
==== 8或16位传输帧格式选择
==== 主或从操作
==== 支持多主模式
==== 8个主模式波特率预分频系数
==== 从模式频率
==== 主模式和从模式快速通信
==== NSS管理
==== 可编程的时钟极性和相位
==== 可编程的数据顺序 MSB(Most Significant Bit 最高有效位)在前或LSB(Least Significant Bit最低有效位)在前
==== 可触发中断的专用接收发送装置
==== SPI总线忙状态标志
==== 支持可靠通信的硬件CRC
--------- 发送模式下,CRC可以被作为最后一字节发送
--------- 全双工模式下对接收到的最后一个字节自动进行CRC校验-
==== 可触发中断的主模式故障 过载及CRC错误标志
==== 支持DMA功能的缓冲器 产生发送和接收请求
从器件选择引脚管理
SPI_CR1寄存器的SSM位设置硬件管理或软件管理
SSM=1 软件管理
SSM=0 硬件管理
SSM=0 SSOE=1 NSS输出使能
SSM=0 SSOE=0 NSS输出禁止
时钟信号的相位和极性
SPI_CR寄存器 的 CPOL位 控制空闲状态 的电平
与CPHA(时钟相位)可以组合成四种可能的时序关系
CPOL位值0/1 SCK引脚在空闲状态保持 低/高电平
数据帧格式
设置SPI_CR1寄存器中LSBFIRST位,输出数据位时可设置MSB LSB的优先级
设置DFF位 每个数据帧可以是8位或者16位 发送接收都有效
状态标志
发送缓冲器空闲标志TXE 标志为1 缓冲器为空
接收缓冲器非空RXNE 标志为1
忙标志BSY 表明SPI通信层的状态
SPI相关引脚复用
SPI常用寄存器
SPI 控制寄存器1/2(SPI_CR1/2)
SPI 状态寄存器 (SPI_SR)
SPI 数据寄存器(SPI_DR)
SPI_I2S配置寄存器(SPI_I2S_CFGR)
SPI_I2S预分频寄存器 (SPI_I2SPR)
SPI相关库函数
void SPI_I2S_DeInit(SPI_TypeDef* SPIx);
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
void I2S_FullDuplexConfig(SPI_TypeDef* I2Sxext, I2S_InitTypeDef* I2S_InitStruct);
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
void SPI_CalculateCRC(SPI_TypeDef* SPIx, FunctionalState NewState);
void SPI_TransmitCRC(SPI_TypeDef* SPIx);
uint16_t SPI_GetCRC(SPI_TypeDef* SPIx, uint8_t SPI_CRC);
uint16_t SPI_GetCRCPolynomial(SPI_TypeDef* SPIx);
void SPI_DataSizeConfig(SPI_TypeDef* SPIx, uint16_t SPI_DataSize);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
SPI配置的参数
typedef struct
{
uint16_t SPI_Direction; //传送方向
uint16_t SPI_Mode; //模式
uint16_t SPI_DataSize; //数据大小
uint16_t SPI_CPOL;
uint16_t SPI_CPHA; //极性和相位
uint16_t SPI_NSS; //硬件或软件的片选
uint16_t SPI_BaudRatePrescaler; //波特率与预分频系数
uint16_t SPI_FirstBit; //MSB LSB
uint16_t SPI_CRCPolynomial; //CRC校验
}SPI_InitTypeDef;
W25Q128
作为SPI的flash
16M 分为 256 个块 每块大小64K 字节 每块16个扇区 每扇区4K个字节
W25Q128的最小擦除单位为一个扇区即4K个字节
实验程序
程序配置过程
① 使能SPIx和IO口时钟
RCC_AHBxPeriphClockCmd()/RCC_APBxPeriphClockCmd();
② 初始化IO口复用功能
void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct);
③ 设置引脚复用映射
GPIO_PinAFConfig();
④初始化SPIx,设置其工作模式
void SPI_Init(SPI_TypeDef* SPIx,SPI_InitTypeDef* SPI_InitStruct);
⑤使能SPIx
void SPI_Cmd(SPI_TypeDef*SPIx,FunctionalState NewState);
⑥SPI传输数据
void SPI_I2S_SendData(SPI_TypeDef*SPIx,uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
⑦查看SPI传输状态
SPI_I2S_GetFlagStatus(SPI2,SPI_I2s_FLAG_RXNE);
库函数
初始化
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
//GPIOFB3,4,5初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为SPI1
//SPI口初始化
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//¸´复位SPI1
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或双向的数据模式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //工作模式 主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据大小 发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟第二个跳变沿数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS) 管脚还是软件(SSI)管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频值256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输由MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI1, ENABLE); //使能SPI外设
SPI1_ReadWriteByte(0xff);//启动传输
}
SPI速度设置
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
SPI1->CR1&=0XFFC7;//位3~5清零 设置波特率
SPI1->CR1|=SPI_BaudRatePrescaler; //设置SPI1速度
SPI_Cmd(SPI1,ENABLE); //使能SPI1
}
SPI1读写字节
u8 SPI1_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte数据
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完毕
return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
}
W25Qxx初始化
void W25QXX_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//GPIOB使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//GPIOG使能
//GPIOB14
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;//PG7
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7输出1 防止NRF干扰SPI FLASH 通信
W25QXX_CS=1; //SPI FLASH不选中
SPI1_Init(); //初始化SPI
SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //设置为21M时钟
W25QXX_TYPE=W25QXX_ReadID(); //读取FLASH ID
}
主函数
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "spi.h"
#include "w25qxx.h"
#include "key.h"
//要写入W25Q16的字符串数组
const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};
#define SIZE sizeof(TEXT_Buffer)
int main(void)
{
u8 key;
u16 i=0;
u8 datatemp[SIZE];
u32 FLASH_SIZE;
u16 id = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
LCD_Init();
KEY_Init();
W25QXX_Init();
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"SPI TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2014/5/6");
LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read");
while(1)
{
id = W25QXX_ReadID();
if (id == W25Q128 || id == NM25Q128)
break;
LCD_ShowString(30,150,200,16,16,"W25Q128 Check Failed!");
delay_ms(500);
LCD_ShowString(30,150,200,16,16,"Please Check! ");
delay_ms(500);
LED0=!LED0; //DS0闪烁
}
LCD_ShowString(30,150,200,16,16,"W25Q128 Ready!");
FLASH_SIZE=16*1024*1024; //FLASH 大小16
POINT_COLOR=BLUE;
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES)//KEY1按下 写入
{
LCD_Fill(0,170,239,319,WHITE);//清除半屏
LCD_ShowString(30,170,200,16,16,"Start Write W25Q128....");
W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE); //从倒数第100个地址处开始 写入SIZE长度的数据
LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!"); //提示传送完成
}
if(key==KEY0_PRES)//KEY0按下 读取字符串显示
{
LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");
W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE); //从倒数第100个地址处开始写入SIZE长度的数据
LCD_ShowString(30,170,200,16,16,"The Data Readed Is: "); //提示传送完成
LCD_ShowString(30,190,200,16,16,datatemp); //显示读到的字符串
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系统正在运行
i=0;
}
}
}