需求描述
基于寄存器操作,使用软件模拟SPI协议,完成读写FLASH。
硬件电路设计
寄存器代码书写
main.c
cs
#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
cs
#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
cs
#include "soft_spi.h"
void SPI_Init(void)
{
// 1. GPIOA
// 1.1 先放时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// new * 模式0
SCK_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
cs
#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
cs
#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.页写的指令是0x02
SPI_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();
}