网站被k如何恢复,网站建设分金手指排名十四,广东富盈建设有限公司企业网站,南昌seo教程文章目录 前言介绍SPI外设SPI框图简化框图 时序主模式全双工连续传输非连续传输 软硬件波形对比硬件SPI读写W25Q64接线图代码规划代码实现 前言
本文介绍STM32中自带的SPI外设#xff0c;在大容量产品和互联型产品上#xff0c;SPI接口可以配置为支持SPI协议或者支持I2S音频… 文章目录 前言介绍SPI外设SPI框图简化框图 时序主模式全双工连续传输非连续传输 软硬件波形对比硬件SPI读写W25Q64接线图代码规划代码实现 前言
本文介绍STM32中自带的SPI外设在大容量产品和互联型产品上SPI接口可以配置为支持SPI协议或者支持I2S音频协议。SPI接口默认工作在SPI方式可以通过软件把功能从SPI模式切换到I2S模式。 在小容量和中容量产品上不支持I2S音频协议。 串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。它可用于多种用途包括使用一条双向数据线的双线单工同步传输还可使用CRC校验的可靠通信。 软件SPI读写W25Q64、SPI详解可见此专栏。 介绍SPI外设 常用8位数据帧高位先行。 外设总线APB1、APB2的频率不一样APB1 36MHz、APB2 72MHz所以SPI1与SPI2的频率也不一样。SPI1挂载于APB2上SPI2挂载于APB1上。 注意选择时钟频率时分频系数是必选的即SPI1的最大频率等于72MHz/2为36MHz。 I2S为数字音频传输协议是用于把音乐信号发送到DAC的协议。 SPI框图 此框图移位寄存器是低位先行移出到MOSI从MISO移入 。 当此SPI做从机时就走MOSI、MISO交叉的路线。 NSS用于多主机模型由多个设备连接NSS当NSS配置为输出时输出低电平这些设备都变为从机当NSS配置为输入时主机拉低NSS此SPI和其他连接NSS的设备就不能变为主机 由SPI_CR2的SSOE控制NSS输出还是输入。 波特发生器用于产生时钟。 主机从地址和数据总线写入数据到发送缓冲区然后经过移位寄存器完成数据交换当移位寄存器还没有数据时发送缓存区传入数据后会置状态寄存器的TXE为1表示发送寄存器缓冲区为空此时可以提前准备下一个数据的写入同时移位寄存器也会自动交换一个数据然后数据进入接收缓冲区此时状态寄存器的RXNE1表示接收寄存器缓冲区非空在RXNE1时要尽快把接收缓冲区的数据读出来反复执行即可完成连续的数据流。 SPI_CR1寄存器描述如下
简化框图
最主要的结构部分方便更清晰的理解实现过程。
时序
主模式全双工连续传输
传输效率高提前写入数据。
非连续传输
效率低没有提前写入比较好操作但是频率很快时由于没有提前写入数据导致的等待时间会较大的影响传输速率。 例如256分频时看不出字节间的间隙。 128分频时有较小间隙。 64分频时间隙逐渐变大。
软硬件波形对比 硬件SPI读写W25Q64
接线图 代码规划
使用ThisSPI.c来用于书写SPI基本时序单元的内容然后使用ThisW25Q64.c来基于ThisSPI.c来调用这些SPI基本时序单元来完成读写的功能以下头文件声明省略。
代码实现
ThisSPI.c
#include stm32f10x.h // Device header// 封装写入CS位的函数
void ThisSPI_W_CS(uint8_t BitValue){GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}void ThisSPI_Init(void){// 初始化引脚配置RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);// CSGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; // 推挽输出GPIO_InitStructure.GPIO_Pin GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);// CLK、MOSIGPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽输出GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);// MISOGPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);// 初始化SPI外设SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_128; // 分频系数SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 0SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 0SPI_InitStructure.SPI_CRCPolynomial 7; // CRC校验暂时不用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_Mode SPI_Mode_Master; // 主机模式SPI_InitStructure.SPI_NSS SPI_NSS_Soft; // 暂时不使用SPI_Init(SPI1,SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);ThisSPI_W_CS(1);
}
/*** brief SPI起始时序单元* param 无* retval 无*/
void ThisSPI_Start(void){ThisSPI_W_CS(0);
}
/*** brief SPI结束时序单元* param 无* retval 无*/
void ThisSPI_End(void){ThisSPI_W_CS(1);
}
/*** brief SPI交换字节* param Byte 字节内容* retval 接收的数据*/
uint8_t ThisSPI_SwapByte(uint8_t Byte){// 等待TXE1while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)RESET);// 写入发送寄存器同时会清除TXE标志SPI_I2S_SendData(SPI1,Byte);// 等待RXNE1while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)RESET);// 读入接收寄存器同时会清除RXNE标志return SPI_I2S_ReceiveData(SPI1);
}
ThisW25Q64.C
#include stm32f10x.h // Device header
#include ThisSPI.h#define JEDEC_ID 0X9F
#define Write_Enable 0X06
#define Read_Status_Register_1 0X05
#define Page_Program 0X02
#define Sector_Erase 0X20
#define Read_Data 0X03// 初始化
void ThisW25Q64_Init(void){ThisSPI_Init();}
// 参考指令集来完成SPI时序单元的拼接
/*** brief 读取产品ID* param MID 产品IDDID 设备ID* retval 无*/
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID){ThisSPI_Start();ThisSPI_SwapByte(JEDEC_ID);*MID ThisSPI_SwapByte(0XFF);*DID ThisSPI_SwapByte(0XFF);*DID 8;*DID | ThisSPI_SwapByte(0XFF);ThisSPI_End();
}
/*** brief 写使能* param 无* retval 无*/
void W25Q64_WriteEnable(void){ThisSPI_Start();ThisSPI_SwapByte(Write_Enable);ThisSPI_End();
}
/*** brief 等待写入完成通过状态寄存器1的BUSY位* param 无* retval 无*/
void W25Q64_WaitBUSY(void){uint32_t Timeout 10000;ThisSPI_Start();ThisSPI_SwapByte(Read_Status_Register_1);while((ThisSPI_SwapByte(0XFF) 0x01) 0x01){Timeout--;if(Timeout 0) break;}ThisSPI_End();
}
/*** brief 页写入* param Address页地址* param DataArr写入的数据* param Count一次写入的大小* retval 无*/
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArr,uint16_t Count){uint8_t i;W25Q64_WriteEnable();ThisSPI_Start();ThisSPI_SwapByte(Page_Program);ThisSPI_SwapByte(Address16);ThisSPI_SwapByte(Address8);ThisSPI_SwapByte(Address);for(i0;iCount;i){ThisSPI_SwapByte(DataArr[i]);}ThisSPI_End();W25Q64_WaitBUSY();
}
/*** brief 扇区擦除* param Address页地址* retval 无*/
void W25Q64_SectorErase(uint32_t Address){W25Q64_WriteEnable();ThisSPI_Start();ThisSPI_SwapByte(Sector_Erase);ThisSPI_SwapByte(Address16);ThisSPI_SwapByte(Address8);ThisSPI_SwapByte(Address);ThisSPI_End();W25Q64_WaitBUSY();
}
/*** brief 读取页内数据* param Address页地址* param DataArr用于存取读取的数据* param Count读取数据的大小* retval 无*/
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArr,uint32_t Count){uint32_t i;ThisSPI_Start();ThisSPI_SwapByte(Read_Data);ThisSPI_SwapByte(Address16);ThisSPI_SwapByte(Address8);ThisSPI_SwapByte(Address);for(i0;iCount;i){DataArr[i] ThisSPI_SwapByte(0XFF);}ThisSPI_End();
}
main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include ThisW25Q64.huint8_t MID;
uint16_t DID;
uint8_t ArrWrite[] {0x11,0x92,0x73,0x24};
uint8_t ArrRead[4];int main(void)
{OLED_Init();OLED_ShowString(1,1,MID:);OLED_ShowString(1,8,DID:);ThisW25Q64_Init();W25Q64_ReadID(MID,DID);OLED_ShowHexNum(1,5,MID,2);OLED_ShowHexNum(1,12,DID,4);OLED_ShowString(2,1,W:);OLED_ShowString(3,1,R:);W25Q64_SectorErase(0X000000);W25Q64_PageProgram(0X000000,ArrWrite,4);W25Q64_ReadData(0X000000,ArrRead,4);OLED_ShowHexNum(2,3,ArrWrite[0],2);OLED_ShowHexNum(2,5,ArrWrite[1],2);OLED_ShowHexNum(2,7,ArrWrite[2],2);OLED_ShowHexNum(2,9,ArrWrite[3],2);OLED_ShowHexNum(3,3,ArrRead[0],2);OLED_ShowHexNum(3,5,ArrRead[1],2);OLED_ShowHexNum(3,7,ArrRead[2],2);OLED_ShowHexNum(3,9,ArrRead[3],2);while (1){}
}