您的位置:首页 > 新闻 > 热点要闻 > 永久个人自助建站_公共营养师报考条件_公司官网怎么制作_中国纪检监察报

永久个人自助建站_公共营养师报考条件_公司官网怎么制作_中国纪检监察报

2024/12/23 9:54:05 来源:https://blog.csdn.net/qq_42190402/article/details/144212513  浏览:    关键词:永久个人自助建站_公共营养师报考条件_公司官网怎么制作_中国纪检监察报
永久个人自助建站_公共营养师报考条件_公司官网怎么制作_中国纪检监察报

FLASH模仿EEPROM

        STM32本身没有自带EEPROM,但是自带了FLASH存储器。

        STM32F103ZET6自带 1M字节的FLASH空间,和 128K+64K的SRAM空间。

        STM32F4 的 SPI 功能很强大,SPI 时钟最高可以到 37.5Mhz,支持 DMA,可以配置为 SPI协议或者 I2S 协议(支持全双工 I2S)。

SPI简介

        SPI,串行外围设备接口。主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。

SPI内部结构简明图

SPI 接口一般使用 4 条线通信:

        MISO 主入从出。

        MOSI 主出从入。

        SCLK 时钟信号,由主设备产生。

        CS 从设备片选信号,由主设备控制。

        主机和从机都有一个移位寄存器,主机通过向自己的SPI串行寄存器写入一个字节来发起一次传输。寄存器通过MOSI信号线将字节传送给从机,从机将自己移位寄存器的内容通过MISO信号线返回给主机。这样,两个移位寄存器的内容被交换。

        外设的写操作和读操作是同步完成的,如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输

        SPI总线有四种工作方式,通过配置时钟极性(CPOL)相位(CPHA)实现。CPOL决定时钟空闲状态电平,CPHA选择数据传输的采样时刻。主从设备需保持时钟配置一致。

SPI1主模式配置步骤

1)配置相关引脚的复用功能,使能 SPI1 时钟。

        启用SPI1需两步:

        首先使能SPI1时钟,通过APB2ENR的第12位设置;

        然后将PB3、4、5配置为复用输出AF5,分别对应SCK、MISO、MOSI,CS由软件管理。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能 SPI1 时钟

        复用 PB3,PB4,PB5 为 SPI1 引脚的方法为:

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

        同时我们设置相应的引脚模式为复用功能模式:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能

2)初始化 SPI1,设置 SPI1 工作模式等。

        通过SPI1_CR1寄存器配置SPI1:

        设为主机模式

        数据格式8位

        设置SCK时钟极性采样方式(CPOL/CPHA),

        并调整时钟频率至最大37.5MHz,

        同时确定数据格式(MSB或LSB在前)。

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);typedef struct
{uint16_t SPI_Direction;//通信方式,半双工、全双工uint16_t SPI_Mode;     //设置主从模式。例如主机模式SPI_Mode_Masteruint16_t SPI_DataSize; //8/16位帧格式选择。8位传输则SPI_DataSize_8b。uint16_t SPI_CPOL;     //时钟极性。决定同步时钟空闲状态的电平。uint16_t SPI_CPHA;     //采样方式。在串行同步时钟的第几个跳变沿采集信号。SPI_CPHA_2Edge位为第2个跳变沿。uint16_t SPI_NSS;      //设置NSS信号由硬件控制还是软件控制。SPI_NSS_Soft软件控制uint16_t SPI_BaudRatePrescaler; //波特率预分频值。有2到256分频共八个值可选。//初始化的时候我们选择 256 分频值//SPI_BaudRatePrescaler_256,传输速度为 84M/256=328.125KHz。
//数据传输顺序是MSB在前还是LSB在前。MSB为最高有效位,LSB最低有效位。uint16_t SPI_FirstBit;         uint16_t SPI_CRCPolynomial; //CRC校验多项式,大于1即可。
}SPI_InitTypeDef;

使能SPI1

SPI_Cmd(SPI1, ENABLE); //使能 SPI1 外设

SPI 传输数据

//往SPIx数据寄存器写入数据,实现发送
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
//从SPI的数据寄存器读数据
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

查看 SPI 传输状态

        SPI传输过程中经常要判断数据是否传输完成发送区是否为空等等状态。

//检查SPI1的接收缓冲区非空
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);

W25Q128简介

        W25Q128将 16M的容量分为 256个块,每个块64K字节

        一个块又分为 16个扇区,每个扇区 4K字节

        最小擦除单位为一个扇区,也就是 4K字节。

        W25Q128支持不超过存储范围的任意地址开始读取数据,在发送24位地址之后程序就可以开始循环读数据,其地址会自动增加

//读取 SPI FLASH 
//在指定地址开始读取指定长度的数据
//NumByteToRead:要读取的字节数(最大 65535)
void W25QXX_Read(u8* pBuffer,      //数据存储区  u32 ReadAddr,     //开始读取的地址(24bit)u16 NumByteToRead) 
{ u16 i; W25QXX_CS=0; //使能器件 SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令 SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送 24bit 地址 SPI1_ReadWriteByte((u8)((ReadAddr)>>8)); SPI1_ReadWriteByte((u8)ReadAddr); for(i=0;i<NumByteToRead;i++){ pBuffer[i]=SPI1_ReadWriteByte(0XFF); //循环读数,主机发送空字节 }W25QXX_CS=1; 
}

        正点原子提供的FLASH写入函数支持在 W25Q128的任意地址写入不超过容量的任意长度。

         /4096获取扇区地址

        %4096获取在扇区内的偏移

        4096-扇区偏移 = 扇区剩余空间大小。

        在SPI Flash写入时,需判断扇区是否需擦除,即检查目标区域是否全为0xFF

        因Flash写入前需擦除(置位为1),擦除后扇区全为0xFF。若区域非全0xFF,则需擦除;否则可直接写入,因未擦除位写0无效。

//写 SPI FLASH 
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区 WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大 65535) 
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite) 
{ u32 secpos;u16 secoff; u16 secremain; u16 i; u8 * W25QXX_BUF; W25QXX_BUF=W25QXX_BUFFER; secpos=WriteAddr/4096;//扇区地址 secoff=WriteAddr%4096;//在扇区内的偏移secremain=4096-secoff;//扇区剩余空间大小//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用if(NumByteToWrite<=secremain) //写入的字节数小于扇区剩余字节secremain=NumByteToWrite; //不大于 4096 个字节while(1) {W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容for(i=0;i<secremain;i++)//校验数据,遍历整个扇区,判断是否都是0xFF{if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除}if(i<secremain)//需要擦除{W25QXX_Erase_Sector(secpos);//擦除这个扇区for(i=0;i<secremain;i++) //复制{W25QXX_BUF[i+secoff]=pBuffer[i]; }W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区}else {W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//已擦除的,直接写}if(NumByteToWrite==secremain) break;//写入结束了else//写入未结束{secpos++; //扇区地址增 1secoff=0; //偏移位置为 0 pBuffer+=secremain; //指针偏移WriteAddr+=secremain; //写地址偏移 NumByteToWrite-=secremain; //字节数递减//如果要写入的字节数if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完else secremain=NumByteToWrite; //下一个扇区可以写完了}}; 
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com