STM32 进阶SPI案例1:软件模拟SPI读写FLASH

需求描述

基于寄存器操作,使用软件模拟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();
}
相关推荐
end_SJ4 小时前
初学stm32 --- flash模仿eeprom
stm32·单片机·嵌入式硬件
LS_learner6 小时前
K210视觉识别模块
嵌入式硬件
Uitwaaien547 小时前
单片机数码管动态显示
单片机·嵌入式硬件
末时清10 小时前
电机驱动-标准库和HAL库
单片机·嵌入式硬件
电鱼智能的电小鱼10 小时前
基于SAIL-RK3576核心板的AI边缘计算网关设计方案——智慧家庭新突破
linux·人工智能·嵌入式硬件·边缘计算
张一西10 小时前
ARM学习(42)CortexM3/M4 MPU配置
stm32·arm·mpu·nxp·arm架构·cortexm3·cortexm4
嵌入式-老费11 小时前
基于海思soc的智能产品开发(高、中、低soc、以及和fpga的搭配)
嵌入式硬件
BuiderCodes13 小时前
STM32 中 GPIO 的八种工作模式介绍
stm32·单片机·嵌入式硬件
Ronin-Lotus15 小时前
嵌入式硬件篇---PWM&电机&舵机
c语言·stm32·单片机·嵌入式硬件·学习·51单片机·硬件工程
Tony小周1 天前
HC32L136K8TA单片机输出互为反相双路PWM
单片机·嵌入式硬件