需求描述
基于寄存器操作,使用软件模拟SPI协议,完成读写FLASH。
硬件电路设计
寄存器代码书写
main.c
#include "usart1.h"
#include "string.h"
#include <stdio.h>
#include "m24c02.h"
#include "soft_spi.h"
#include "W25Q32.h"
int main(void){Usart1_Init();W25Q32_Init();uint8_t buffer[1001] = {0};W25Q32_EraseSetor(0x00,0x00);W25Q32_PageWrite(0x00,0x00,0x00,0x00,"abcde",5);W25Q32_ReadData(0x00,0x00,0x00,0x00,buffer,1000);printf("%s\n",buffer);while (1){}}
soft_spi.h
#ifndef __SOFT_SPI_H__
#define __SOFT_SPI_H__
#include "stm32f10x.h"
#define SCK_HIGH (GPIOA->ODR |= GPIO_ODR_ODR5)
#define SCK_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR5)
#define MOSI_HIGH (GPIOA->ODR |= GPIO_ODR_ODR7)
#define MOSI_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR7)
#define CS_HIGH (GPIOA->ODR |= GPIO_ODR_ODR13)
#define CS_LOW (GPIOA->ODR &= ~GPIO_ODR_ODR13)
#define MISO_READ (GPIOA->IDR & GPIO_IDR_IDR6)
void SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t byte);
#endif /* __SOFT_SPI_H__ */
soft_spi.c
#include "soft_spi.h"
void SPI_Init(void)
{// 1. GPIOA// 1.1 先放时钟RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// new * 模式0SCK_LOW;
// 1.2 PA5 时钟 通用推挽GPIOA->CRL &= ~GPIO_CRL_CNF5;GPIOA->CRL |= GPIO_CRL_MODE5;// 1.3 PA6 输入信号 浮空输入GPIOA->CRL &= ~GPIO_CRL_CNF6_1;GPIOA->CRL |= GPIO_CRL_CNF6_0;GPIOA->CRL &= ~GPIO_CRL_MODE6;// 1.4 PA7 数据输出 通用推挽GPIOA->CRL &= ~GPIO_CRL_CNF7;GPIOA->CRL |= GPIO_CRL_MODE7;
// 2. GPIOC// 2.1 放GPIOC的时钟RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;// new * 片选拉高 表示 不通讯CS_HIGH;
// 2.2. PC13 片选使能信号 通用推挽GPIOC->CRH &= ~GPIO_CRH_CNF13;GPIOC->CRH |= GPIO_CRH_MODE13;
}
void SPI_Start(void)
{CS_LOW;
}
void SPI_Stop(void)
{CS_HIGH;
}
// mode0 上升沿的时候读数据 下降沿的时候发数据
uint8_t SPI_SwapByte(uint8_t byte)
{
uint8_t receive_byte = 0;for (uint8_t i = 0; i < 8; i++){// for循环里面 是不是处理的bit// 1. 上升沿之前 先把MO的数据偷偷放好if (byte & 0x80){MOSI_HIGH;}else{MOSI_LOW;}// 2. 一个bit发完之后 左移一下byte <<= 1;// 3. 拉高一下 创造一个上升沿SCK_HIGH;receive_byte <<= 1;if (MISO_READ){receive_byte |= 1;}
// 4. 拉低一下 让时钟进入空闲 再偷着读数据SCK_LOW;// !! 此处违反了协议 因为协议要求 高电平时数据交换}
return receive_byte;
}
W25Q32.h
#ifndef __W25Q32_H__
#define __W25Q32_H__
#include "soft_spi.h"
void W25Q32_Init(void);
void W25Q32_JEDECID(uint8_t * mfid,uint8_t * memType,uint8_t * cap);
void W25Q32_WriteEnable(void);
void W25Q32_WriteDisable(void);
uint8_t w25Q32_ReadStatus(void);
void W25Q32_PageWrite(uint8_t block_addr,uint8_t sector_addr,uint8_t page_addr,uint8_t page_inner_addr,uint8_t * pData,uint8_t len
);
void W25Q32_EraseSetor(uint8_t block_addr,uint8_t sector_addr
);
void W25Q32_ReadData(uint8_t block_addr,uint8_t sector_addr,uint8_t page_addr,uint8_t page_inner_addr,uint8_t * pData,uint8_t len
);
#endif /* __W25Q32_H__ */
W25Q32.c
#include "W25Q32.h"
#define DUMMY 0xff
void W25Q32_Init(void){SPI_Init();
}
void W25Q32_JEDECID(uint8_t * mfid,uint8_t * memType,uint8_t * cap){SPI_Start();SPI_SwapByte(0x9f);// 制造商ID* mfid = SPI_SwapByte(DUMMY);// 存储器类型* memType = SPI_SwapByte(DUMMY);// 容量* cap = SPI_SwapByte(DUMMY);SPI_Stop();
}
void W25Q32_WriteEnable(void){SPI_Start();SPI_SwapByte(0x06);SPI_Stop();
}
uint8_t w25Q32_ReadStatus(void){SPI_Start();SPI_SwapByte(0x05);uint8_t status = SPI_SwapByte (DUMMY);SPI_Stop();return status;
}
void W25Q32_WriteDisable(void){SPI_Start();SPI_SwapByte(0x04);SPI_Stop();
}
void W25Q32_WaitBusyFinish(void){SPI_Start();SPI_SwapByte(0x05);while (1){uint8_t status = SPI_SwapByte(DUMMY);if ((status & 0x01) == 0){SPI_Stop();return;}}
}
void W25Q32_PageWrite(uint8_t block_addr,uint8_t sector_addr,uint8_t page_addr,uint8_t page_inner_addr,uint8_t * pData,uint8_t len
)
{//先判断忙不忙W25Q32_WaitBusyFinish();//再打开写使能W25Q32_WriteEnable();
SPI_Start();//1.页写的指令是0x02SPI_SwapByte(0x02);//2.先发24位地址的高8位SPI_SwapByte(block_addr);//3.再发中间8位(需要将4位的段地址 和4位的页地址合并)SPI_SwapByte(sector_addr << 4 | (page_addr & 0x0f));//4.最后低8位地址,业内地址不需要合并SPI_SwapByte(page_addr);//5.有多少数据发多少数据for (uint8_t i = 0; i < len; i++){SPI_SwapByte(pData[i]);}//6. 结束通讯SPI_Stop();
}
void W25Q32_EraseSetor(uint8_t block_addr,uint8_t sector_addr
){W25Q32_WaitBusyFinish();W25Q32_WriteEnable();
SPI_Start();SPI_SwapByte(0x20);SPI_SwapByte(block_addr);SPI_SwapByte(sector_addr << 4);SPI_SwapByte(0x00);SPI_Stop();
}
void W25Q32_ReadData(uint8_t block_addr,uint8_t sector_addr,uint8_t page_addr,uint8_t page_inner_addr,uint8_t * pData,uint8_t len
){W25Q32_WaitBusyFinish();
SPI_Start();SPI_SwapByte(0x03);
//2.先发24位地址的高8位SPI_SwapByte(block_addr);//3.再发中间8位(需要将4位的段地址 和4位的页地址合并)SPI_SwapByte(sector_addr << 4 | (page_addr & 0x0f));//4.最后低8位地址,业内地址不需要合并SPI_SwapByte(page_addr);
for (uint8_t i = 0; i < len; i++){pData[i] = SPI_SwapByte(DUMMY);}SPI_Stop();
}