STM32初学笔记 - P2
评论 2 热度 1297
这里用的是野火霸道的板子,搭载了STM32F103ZET6. 目前所学知识尚且较为简单,这里的笔记仅仅记录一下学习过程中的代码,方便以后回头复习以及复制。主要参考资料——《零死角玩转 STM32F103—霸道_V2 开发板》,视频——150集-野火F103霸道/指南者视频教程-中级篇。
硬件IIC读写EEPROM
原理参考:IIC完整讲解
STM32 BUG参考:
所用EEPROM型号为AT24C02,有32页,每页8字节,需8位地址寻址。EEPROM部分的原理图如下所示。图中只有一个从机(即EEPROM),当实际电路中有多个从机时,SCL和SDA两根线上不需在R218、R219的基础上再加入上拉电阻。
宏部分
#define EEPROM_I2Cx I2C1
#define EEPROM_I2C_ClockCmd RCC_APB1PeriphClockCmd
#define EEPROM_I2C_CLK RCC_APB1Periph_I2C1
#define EEPROM_I2C_GPIO_ClockCmd RCC_APB2PeriphClockCmd
#define EEPROM_I2C_GPIO_CLK RCC_APB2Periph_GPIOB
#define EEPROM_I2C_SCL_PORT GPIOB
#define EEPROM_I2C_SCL_PIN GPIO_Pin_6
#define EEPROM_I2C_SDA_PORT GPIOB
#define EEPROM_I2C_SDA_PIN GPIO_Pin_7
#define I2C_Speed 400000 // <= 400kbit
#define I2Cx_OWN_ADDR1 0x00 // STM32设备本身的地址
#define I2C_PageSize 8 // 根据AT24C01/02手册调整
#define EEPROM_ADDR 0xA0 // EEPROM设备8位地址
#define I2C_TIMEOUT_LOOP 0x2000 // 超时设置
核心函数
static uint32_t I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
/**
* @brief 初始化I2C EEPROM
*
*/
void I2C_EEPROM_Init(void){
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
EEPROM_I2C_ClockCmd(EEPROM_I2C_CLK, ENABLE);
EEPROM_I2C_GPIO_ClockCmd(EEPROM_I2C_GPIO_CLK, ENABLE);
// GPIO初始化
// SCL
GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SCL_PIN; // SCL用于发送同步时钟脉冲信号
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
GPIO_Init(EEPROM_I2C_SCL_PORT, &GPIO_InitStruct);
// SDA
GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SDA_PIN;
GPIO_Init(EEPROM_I2C_SDA_PORT, &GPIO_InitStruct);
// I2C初始化
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // SCL低电平占空比
I2C_InitStruct.I2C_OwnAddress1 = I2Cx_OWN_ADDR1;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = I2C_Speed;
I2C_Init(EEPROM_I2Cx, &I2C_InitStruct);
I2C_Cmd(EEPROM_I2Cx, ENABLE);
}
/**
* @brief 超时自动执行函数
*
* @return (uint8_t) 1
*/
static uint8_t I2C_TIMEOUT_Callback(void){
printf("I2C TIMEOUT!\n");
return 1;
}
/**
* @brief 向EEPROM写入一个字节数据
*
* @param addr 写入地址
* @param data 写入数据
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Byte_Write(u16 addr, u8 data){
// 括号表示从机信号,A表示ACK,N表示NACK
// S - addr - w - (A) - w_addr - (A) - data - (A) - P
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, addr);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, data);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR){
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 0;
}
/**
* @brief 写入数组数据到EEPROM
*
* @param addr 写入data的起始地址,使用 page write 要求addr % 8 == 0,与count无关。
* @param data 数组地址
* @param count 所用芯片要求count <= 8
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Page_Write(u16 addr, u8 *data, u8 count){
// S - addr - w - (A) - w_addr - (A) - data - (A) - data - (A) - ... - data - (A) - P
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, addr);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
while (count){
I2C_SendData(EEPROM_I2Cx, *data);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR){
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
count--;
data++;
}
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 0;
}
/**
* @brief 读取EEPROM
*
* @param addr 读取地址
* @param data 接收数据所用数组
* @param count 接收数据量
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Read(u16 addr, u8 *data, u16 count){
// S - addr - w - (A) - w_addr - (A) - \n
// S - addr - r - (A) - (data) - N - P
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR){ // EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_SendData(EEPROM_I2Cx, addr);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR){ // EV8
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
// 二次起始信号
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR){ // EV5
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Receiver);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR){ //EV6
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
while(count){
if (count == 1){ // 待读取的最后一个
I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE); // 产生NACK信号
}
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR){ // EV7
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
*data = I2C_ReceiveData(EEPROM_I2Cx);
data++;
count--;
}
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
I2C_AcknowledgeConfig(EEPROM_I2Cx, ENABLE); // 再设置回去,自动产生ACK信号
return 0;
}
/**
* @brief 等待EEPROM内部写入完成。
* @return uint8_t 是否发生错误,1-错误 0-正常
*/
uint8_t EEPROM_Wait_For_Writing(void){
do{
I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
I2C_VAR_TIMEOUT = I2C_TIMEOUT_LOOP;
while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_SB) == RESET){ // EV5
// while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT) == ERROR); // 卡死
if ((I2C_VAR_TIMEOUT--) == 0){
return I2C_TIMEOUT_Callback();
}
}
I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDR, I2C_Direction_Transmitter);
}while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_ADDR) == RESET); // EV6
// while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR); // 卡死
I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
return 0;
}
主函数
int main(void){
u32 i = 0;
u8 readData[10] = {0};
u8 writeData[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x02};
USART_Config();
I2C_EEPROM_Init();
printf("Writing...\n");
// EEPROM_Byte_Write(11, 0xff);
// EEPROM_WAIT_Done();
EEPROM_Page_Write(0, writeData, 8);
EEPROM_Wait_For_Writing();
EEPROM_Read(0, readData, 8);
printf("Reading...\n");
for (i = 0; i < 8; i++){
printf("%x ", readData[i]);
}
while(1);
}
SPI读写FLASH
SPI与IIC较为类似。所用FLASH为W25Q64。FLASH部分的原理图如下。
宏部分
// Basic
#define FLASH_SPIx SPI1
#define FLASH_SPI_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_CLK RCC_APB2Periph_SPI1
// CS(Chip select)/SS/NSS (GPIO)
#define FLASH_SPI_CS_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_CS_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_CS_PORT GPIOA
#define FLASH_SPI_CS_PIN GPIO_Pin_4
// SCK
#define FLASH_SPI_SCK_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_SCK_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_SCK_PORT GPIOA
#define FLASH_SPI_SCK_PIN GPIO_Pin_5
// MISO
#define FLASH_SPI_MISO_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_MISO_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MISO_PORT GPIOA
#define FLASH_SPI_MISO_PIN GPIO_Pin_6
// MOSI
#define FLASH_SPI_MOSI_ClockCmd RCC_APB2PeriphClockCmd
#define FLASH_SPI_MOSI_CLK RCC_APB2Periph_GPIOA
#define FLASH_SPI_MOSI_PORT GPIOA
#define FLASH_SPI_MOSI_PIN GPIO_Pin_7
#define FLASH_SPI_S GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
#define FLASH_SPI_P GPIO_SetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
#define SPI_FLAG_TIMEOUT 0x1000
#define FLASH_SPI_PageSize 256
// FLASH W25X CMD
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
#define Dummy_Byte 0xFF
#define W25X_FLAG_BSY 0x01 // BUSY 位
核心部分
static uint32_t SPI_TIMEOUT;
void FLASH_SPI_Init(void){
SPI_InitTypeDef SPI_struct;
GPIO_InitTypeDef GPIO_struct;
FLASH_SPI_ClockCmd(FLASH_SPI_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_CS_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_SCK_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_MISO_CLK, ENABLE);
FLASH_SPI_CS_ClockCmd(FLASH_SPI_MOSI_CLK, ENABLE);
// CS config
GPIO_struct.GPIO_Pin = FLASH_SPI_CS_PIN;
GPIO_struct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_struct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_struct);
// SCK
GPIO_struct.GPIO_Pin = FLASH_SPI_SCK_PIN;
GPIO_struct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_struct);
// MOSI
GPIO_struct.GPIO_Pin = FLASH_SPI_MOSI_PIN;
GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_struct);
// MISO
GPIO_struct.GPIO_Pin = FLASH_SPI_MISO_PIN;
GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_struct);
FLASH_SPI_P;
SPI_struct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_struct.SPI_Mode = SPI_Mode_Master;
SPI_struct.SPI_DataSize = SPI_DataSize_8b;
SPI_struct.SPI_CPOL = SPI_CPOL_High; // clock polarity 极性. 空闲时SCK为高电平
SPI_struct.SPI_CPHA = SPI_CPHA_2Edge; // clock phase 相位. 数据捕获于第二个时钟沿
SPI_struct.SPI_NSS = SPI_NSS_Soft; // NSS引脚软件控制
SPI_struct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; // SPI时钟2分频
SPI_struct.SPI_FirstBit = SPI_FirstBit_MSB; // MSB先行 数据高位先发送
SPI_struct.SPI_CRCPolynomial = 7; // CRC校验成员值 若要启用,还要用SPI_TransmitCRC和SPI_CalculateCRC函数来启用发送CRC功能
SPI_Init(FLASH_SPIx, &SPI_struct);
SPI_Cmd(FLASH_SPIx, ENABLE);
}
static uint8_t SPI_TIMEOUT_Callback(u8 code){
printf("SPI_ERROR, Code: %d\n", code);
return 1;
}
static void FLASH_ERROR_LOG(char* info){
printf("FLASH Error log: %s\n", info);
}
/**
* @brief 使用SPI发送1byte数据
*
* @param data uint8_t
* @return uint8_t 接收到的数据
*/
uint8_t FLASH_SPI_Send_Byte(u8 data){
SPI_TIMEOUT = SPI_FLAG_TIMEOUT;
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET){
if ((SPI_TIMEOUT--) == 0) return SPI_TIMEOUT_Callback(0);
}
SPI_I2S_SendData(FLASH_SPIx, data);
SPI_TIMEOUT = SPI_FLAG_TIMEOUT;
while(SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET){
if ((SPI_TIMEOUT--) == 0) return SPI_TIMEOUT_Callback(1);
}
return SPI_I2S_ReceiveData(FLASH_SPIx);
}
/**
* @brief 读取一个字节的数据
*
* @return uint8_t
*/
uint8_t FLASH_SPI_Read_Byte(void){
// STM32必须要发送数据才能使时钟变化,从而接收数据
return FLASH_SPI_Send_Byte(Dummy_Byte);
}
void FLASH_SPI_WriteEnable(void){
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_WriteEnable);
FLASH_SPI_P;
}
/**
* @brief 读取FLASH的ID
*
* @return uint32_t
*/
uint32_t FLASH_SPI_ReadID(void){
u32 ID = 0;
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_JedecDeviceID);
ID = FLASH_SPI_Send_Byte(Dummy_Byte); // 00 00 00 AA
ID <<= 8; // 00 00 00 AA << 8 -> 00 00 AA 00
ID |= FLASH_SPI_Send_Byte(Dummy_Byte); // 00 00 AA 00 | 00 00 00 BB
ID <<= 8; // 00 00 AA BB << 8 -> 00 AA BB 00
ID |= FLASH_SPI_Send_Byte(Dummy_Byte); // 00 AA BB 00 | 00 00 00 CC
FLASH_SPI_P;
return ID; // 00 AA BB CC
}
/**
* @brief 等待FLASH写入完成
*
*/
void FLASH_SPI_Wait_For_Writing(void){
u8 Flash_status = 0;
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_ReadStatusReg);
do{
Flash_status = FLASH_SPI_Send_Byte(Dummy_Byte);
}while((Flash_status & W25X_FLAG_BSY) == SET);
FLASH_SPI_P;
}
/**
* @brief 擦除扇区
*
* @param SectorAddr 扇区地址 (需对齐到4KB)
*/
void FLASH_SPI_SectorErase(u32 SectorAddr){
FLASH_SPI_WriteEnable();
FLASH_SPI_Wait_For_Writing();
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_SectorErase);
// 从高位到低位发送擦除扇区地址
FLASH_SPI_Send_Byte((SectorAddr & 0xFF0000) >> 16);
FLASH_SPI_Send_Byte((SectorAddr & 0xFF00) >> 8);
FLASH_SPI_Send_Byte(SectorAddr & 0xFF);
FLASH_SPI_P;
FLASH_SPI_Wait_For_Writing();
}
/**
* @brief 按页写入数据 (调用前应先擦除扇区)
*
* @param pBuffer 数据指针
* @param WriteAddr 地址
* @param ByteCount 长度 不可超过页大小
*/
void FLASH_SPI_Page_Write(u8* pBuffer, u32 WriteAddr, u16 ByteCount){
FLASH_SPI_WriteEnable();
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_PageProgram);
FLASH_SPI_Send_Byte((WriteAddr & 0xFF0000) >> 16);
FLASH_SPI_Send_Byte((WriteAddr & 0xFF00) >> 8);
FLASH_SPI_Send_Byte(WriteAddr & 0xFF);
if (ByteCount > FLASH_SPI_PageSize){ // 写入256个字节限制
ByteCount = FLASH_SPI_PageSize;
FLASH_ERROR_LOG("FUNC: FLASH_SPI_Page_Write: ByteCount > FLASH_SPI_PageSize !");
}
while (ByteCount--){
FLASH_SPI_Send_Byte(*pBuffer);
pBuffer++;
}
FLASH_SPI_P;
FLASH_SPI_Wait_For_Writing();
}
/**
* @brief 不定量数据写入 (调用前应先擦除扇区)
*
* @param pBuffer 数据指针
* @param WriteAddr 地址
* @param ByteCount 长度 (无限制)
*/
void FLASH_SPI_Buffer_Write(u8* pBuffer, u32 WriteAddr, u16 ByteCount){
u8 NPages = 0, Nsingle = 0, Addr = 0, count = 0, tmp = 0;
Addr = WriteAddr % FLASH_SPI_PageSize;
count = FLASH_SPI_PageSize - Addr;
NPages = ByteCount / FLASH_SPI_PageSize;
Nsingle = ByteCount % FLASH_SPI_PageSize;
if (Addr == 0){ // 地址对齐
if (NPages == 0){ // 单页
FLASH_SPI_Page_Write(pBuffer, WriteAddr, ByteCount);
}else{ // 多页写入
while (NPages--){
FLASH_SPI_Page_Write(pBuffer, WriteAddr, FLASH_SPI_PageSize);
WriteAddr += FLASH_SPI_PageSize;
pBuffer += FLASH_SPI_PageSize;
}
}
}else{ // 不对齐
if (NPages == 0){
if (Nsingle > count){
tmp = Nsingle - count;
FLASH_SPI_Page_Write(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
FLASH_SPI_Page_Write(pBuffer, WriteAddr, tmp);
}else{
FLASH_SPI_Page_Write(pBuffer, WriteAddr, ByteCount);
}
}else{
ByteCount -= count;
NPages = ByteCount / FLASH_SPI_PageSize;
Nsingle = ByteCount % FLASH_SPI_PageSize;
FLASH_SPI_Page_Write(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
while (NPages--){
FLASH_SPI_Page_Write(pBuffer, WriteAddr, FLASH_SPI_PageSize);
WriteAddr += FLASH_SPI_PageSize;
pBuffer += FLASH_SPI_PageSize;
}
if (Nsingle != 0){ // 写完多余的页
FLASH_SPI_Page_Write(pBuffer, WriteAddr, Nsingle);
}
}
}
}
/**
* @brief 读取FLASH
*
* @param pBuffer 存储数据指针
* @param ReadAddr 读取地址
* @param ByteCount 读取长度
*/
void FLASH_SPI_Buffer_Read(u8* pBuffer, u32 ReadAddr, u16 ByteCount){
FLASH_SPI_S;
FLASH_SPI_Send_Byte(W25X_ReadData);
FLASH_SPI_Send_Byte((ReadAddr & 0xFF0000) >> 16);
FLASH_SPI_Send_Byte((ReadAddr & 0xFF00) >> 8);
FLASH_SPI_Send_Byte(ReadAddr & 0xFF);
while (ByteCount--){
*pBuffer = FLASH_SPI_Send_Byte(Dummy_Byte);
pBuffer++;
}
FLASH_SPI_P;
}
主函数
int main(void){
u32 FLASH_ID;
u8 TX_Buffer[64] = "Hello world.";
u8 RX_Buffer[64] = {0};
USART_Config();
printf("start...\n");
FLASH_SPI_Init();
FLASH_ID = FLASH_SPI_ReadID();
printf("Device ID: %x\n", FLASH_ID);
if (FLASH_ID == 0xEF4017){
printf("w25Q64 Detected.\n");
FLASH_SPI_SectorErase(0);
FLASH_SPI_Buffer_Write(TX_Buffer, 0, 13);
printf("Write: %s\n", TX_Buffer);
FLASH_SPI_Buffer_Read(RX_Buffer, 0, 13);
printf("Read: %s\n", RX_Buffer);
}else{
printf("Error !\n");
}
printf("end...\n");
}
注:根据本FLASH芯片手册知,每个扇区大小为4kB,所以扇区0的地址为0,扇区1的地址为4096,以此类推。
wu先生 Windows 7 · Google Chrome
技术文,我不懂◔ ‸◔?
#249 2022-02-25 21:35 回复
Luuljh Windows 10 · FireFox
我主要发来自己复习的o( ̄▽ ̄)d
#250 2022-03-09 20:17 回复