【电子DIY】DIY行车记录仪为游戏机之主控软件开发(6)flash读写驱动

代码见:https://github.com/qinyunti/py32f403-nes.git

一.前言

前面我们实现了SPI接口,并读到了ID,现在就开始实现FLASH的读写接口,为后面文件系统移植做准备。

二.驱动

flash.h中我们定义数据结构

typedef uint32_t (*flash_spi_io)(uint8_t* tx_buffer, uint8_t* rx_buffer, uint32_t len, int flag);

/**

* \struct flash_dev_st

* FLASH结构.

*/

typedef struct

{

        flash_spi_io spi_trans;  /**< spi传输接口 */

        void* buffer;            /**< sector缓存  */

        uint32_t sector_size;    /**< sector大小  */

        uint32_t sector_bits;    /**< sector大小 位数  */       

        uint32_t page_size;      /**< page大小  */

        uint32_t page_bits;      /**< page大小 位数  */       

} flash_dev_st;

有用户实现spi传输接口,提供buffer,指定其他参数,这样flash.c和flash.h完全可移植,无需任何修改。

Flash.c的实现,完全参照FLASH的芯片手册来进行。

主要实现以下命令

#define FLASH_CMD_WEL 0x06

#define FLASH_CMD_PAGEPROGRAM 0x02

#define FLASH_CMD_READ 0x03

#define FLASH_CMD_ERASESECTOR 0x20

#define FLASH_CMD_READSR1 0x05

#define FLASH_CMD_READSR2 0x35

#define FLASH_CMD_READSR3 0x15

#define FLASH_CMD_WRITESR1 0x01

#define FLASH_CMD_WRITESR2 0x31

#define FLASH_CMD_WRITESR3 0x11

2.1写使能

发一个字节0x06即可

int flash_write_enable(flash_dev_st* dev)

{

        uint8_t cmd = FLASH_CMD_WEL;

        dev->spi_trans(&cmd, 0, 1, 1);

        return 0;

}

213820sjg1didddvkkdlkx

2.2读状态寄存器

发一个字节命令,然后读一字节状态

int flash_read_sr1(flash_dev_st* dev, uint8_t* sr)

{

        uint8_t cmd[2] = {FLASH_CMD_READSR1,0xFF};

        uint8_t rx[2];

        dev->spi_trans(cmd, rx, 2, 1);

        *sr = rx[1];

        return 0;

}

int flash_read_sr2(flash_dev_st* dev, uint8_t* sr)

{

        uint8_t cmd[2] = {FLASH_CMD_READSR2,0xFF};

        uint8_t rx[2];

        dev->spi_trans(cmd, rx, 2, 1);

        *sr = rx[1];

        return 0;

}

int flash_read_sr3(flash_dev_st* dev, uint8_t* sr)

{

        uint8_t cmd[2] = {FLASH_CMD_READSR3,0xFF};

        uint8_t rx[2];

        dev->spi_trans(cmd, rx, 2, 1);

        *sr = rx[1];

        return 0;

}

213820canungoyurnwnovh

2.3写状态寄存器

发送一字节命令和一字节数据

int flash_write_sr1(flash_dev_st* dev, uint8_t val)

{

        uint8_t cmd[2] = {FLASH_CMD_WRITESR1,0};

        cmd[1] = val;

        dev->spi_trans(cmd, 0, 2, 1);

        return 0;

}

int flash_write_sr2(flash_dev_st* dev, uint8_t val)

{

        uint8_t cmd[2] = {FLASH_CMD_WRITESR2,0};

        cmd[1] = val;

        dev->spi_trans(cmd, 0, 2, 1);

        return 0;

}

int flash_write_sr3(flash_dev_st* dev, uint8_t val)

{

        uint8_t cmd[2] = {FLASH_CMD_WRITESR3,0};

        cmd[1] = val;

        dev->spi_trans(cmd, 0, 2, 1);

        return 0;

}

213820nc6v7v7vd6vw73vx

2.4等待完成

#define FLASH_SR1_BUSY  (1 << 0)

int flash_wait_busy(flash_dev_st* dev)

{

        uint8_t sr = 0;

        do{

                flash_read_sr1(dev, &sr);

        } while(FLASH_SR1_BUSY & sr);

        return 0;

}

读状态寄存器1,等待其bit0变为0

213821tj11seytaeetazb1

2.5Sector擦除

发送1字节命令+3字节地址

int flash_erase_sector(flash_dev_st* dev, uint32_t addr)

{

        uint8_t cmd[4];

        flash_write_enable(dev);

  cmd[0] = FLASH_CMD_ERASESECTOR;

        cmd[1] = (uint8_t)(addr >> 16 & 0xFF);

        cmd[2] = (uint8_t)(addr >> 8  & 0xFF);

        cmd[3] = (uint8_t)(addr >> 0  & 0xFF);

        dev->spi_trans(cmd, 0, 4, 1);

        flash_wait_busy(dev);

        return 0;

}

213821agvpxhqxop3gg886

2.6Page编程

先要写使能

然后发1字节命令+3字节地址,此时不拉高CS

然后发数据

最后等待完成

int flash_pageprogram(flash_dev_st* dev, uint8_t* buffer, uint32_t addr, uint32_t len)

{

        uint8_t cmd[4];

  cmd[0] = FLASH_CMD_PAGEPROGRAM;

        cmd[1] = (uint8_t)(addr >> 16 & 0xFF);

        cmd[2] = (uint8_t)(addr >> 8  & 0xFF);

        cmd[3] = (uint8_t)(addr >> 0  & 0xFF);

        flash_write_enable(dev);

        dev->spi_trans(cmd, 0, 4, 0);

        dev->spi_trans(buffer, 0, len, 1);

        flash_wait_busy(dev);

        return 0;

}


213821oppbxaagcgp9qxcx

2.7读数据

先发送1字节命令+3字节地址,此时不拉高CS

然后读数据

uint32_t flash_read(flash_dev_st* dev, uint8_t* buffer, uint32_t addr, uint32_t len)

{

    uint8_t cmd[4];

    cmd[0] = FLASH_CMD_READ;

    cmd[1] = (uint8_t)(addr >> 16 & 0xFF);

    cmd[2] = (uint8_t)(addr >> 8  & 0xFF);

    cmd[3] = (uint8_t)(addr >> 0  & 0xFF);

                dev->spi_trans(cmd, 0, 4, 0);

                dev->spi_trans(0, buffer, len, 1);

                return 0;

}

213822lyu0yysi990urryy

2.8写数据

实现任意地址的任意长度写。

按照sector为单位,如果待写入数据不是sector地址和大小对齐,则先读出来然后修改,最后写入。写入前自动擦除。

uint32_t flash_write(flash_dev_st* dev, uint8_t* buffer, uint32_t addr, uint32_t len)

{

          /**

           *   |                     |                    |                  |                        |

           *             |                                                                   |

           *             <                           len                                     >      

     *             addr

           *   <sec_head >                                                    <  sec_tail  >

           *   start_addr

     */

          uint32_t sec_head;

          uint32_t sec_tail = 0;

          uint32_t sec_mid_num;

          uint32_t start_addr;

          uint32_t end_addr;


          uint32_t fill;


          start_addr = addr & (~(dev->sector_size-1));

          end_addr = (addr+len) & (~(dev->sector_size-1));

          sec_head = addr & (dev->sector_size-1);   

          if((end_addr != start_addr) || (sec_head == 0)){

                        sec_tail = (addr + len) & (dev->sector_size-1);

                }

                sec_mid_num = (end_addr - start_addr) >> dev->sector_bits;

                if((sec_head != 0) || (sec_tail != 0)){

                        if(sec_mid_num > 1){

                          sec_mid_num--;

                        }else{

                                sec_mid_num = 0;

                        }

                }

                /* head */

          if(sec_head > 0)

    {

                        flash_read(dev,dev->buffer, start_addr, dev->sector_size);

                        fill =( len > (dev->sector_size-sec_head)) ? (dev->sector_size-sec_head) : len;

                        memcpy(dev->buffer+sec_head,buffer,len);

                        flash_erase_sector(dev,start_addr);

      for (uint32_t j=0; j<16; j++) {

                                flash_pageprogram(dev,dev->buffer + (j<<dev->page_bits),start_addr + (j<<dev->page_bits), dev->page_size);  

      }

                        buffer += fill;

                        start_addr += dev->sector_size;

                }

          /* mid */

                for(uint32_t i=0; i<sec_mid_num; i++)

    {

                        flash_erase_sector(dev,start_addr);

      for (uint32_t j=0; j<16; j++) {

                                flash_pageprogram(dev, buffer + (j<<dev->page_bits), start_addr + (j<<dev->page_bits), dev->page_size);  

      }

                        buffer += dev->sector_size;

                        start_addr += dev->sector_size;

                }

          /* tail */

          if(sec_tail > 0)

    {

                        flash_read(dev,dev->buffer+sec_tail, start_addr+sec_tail, dev->sector_size - sec_tail);

                        memcpy(dev->buffer,buffer,sec_tail);

                        flash_erase_sector(dev,start_addr);

      for (uint32_t j=0; j<16; j++) {

                                flash_pageprogram(dev, dev->buffer + (j<<dev->page_bits),start_addr + (j<<dev->page_bits), dev->page_size);  

      }

                }

                return len;

}

三.测试

封装一个flash_itf.c/h

实现FLASH读写,屏蔽实例相关信息。

flash_itf.h

#ifndef FLASH_ITF_H

#define FLASH_ITF_H

#ifdef __cplusplus

extern "C" {

#endif


#include <stdint.h>

/**

* \fn flash_itf_init

* 初始化

*/

int flash_itf_init(void);

/**

* \fn flash_itf_deinit

* 反初始化

*/

int flash_itf_deinit(void);

/**

* \fn flash_itf_read

* 读数据

* \param[in] addr 读开始地址

* \param[out] buffer 存储读出的数据

* \param[in] len 待读出的长度

* \retval 返回实际读出的数据长度

*/

uint32_t flash_itf_read(uint8_t* buffer, uint32_t addr, uint32_t len);

/**

* \fn flash_itf_write

* 写数据

* \param[in] addr 写开始地址

* \param[out] buffer 存储待写的数据

* \param[in] len 待写入的长度

* \retval 返回实际写入的数据长度

*/

uint32_t flash_itf_write(uint8_t* buffer, uint32_t addr, uint32_t len);

#ifdef __cplusplus

}

#endif

#endif

flash_itf.c

#include "flash_itf.h"

#include "flash.h"

#include "spi.h"

#include "FreeRTOS.h"

static flash_dev_st s_flash;

static uint32_t spi_io(uint8_t* tx_buffer, uint8_t* rx_buffer, uint32_t len, int flag)

{

        return spi_trans(0, 0, tx_buffer, rx_buffer, len, flag);

}

int flash_itf_init(void)

{

        s_flash.spi_trans = spi_io;

        s_flash.page_bits = 8;

        s_flash.page_size = 256;

        s_flash.sector_bits = 12;

        s_flash.sector_size = 4096;

        s_flash.buffer = pvPortMalloc(4096);

        return 0;

}

int flash_itf_deinit(void)

{

        vPortFree(s_flash.buffer);

        return 0;

}

uint32_t flash_itf_read(uint8_t* buffer, uint32_t addr, uint32_t len)

{

        return flash_read(&s_flash, buffer, addr, len);

}

uint32_t flash_itf_write(uint8_t* buffer, uint32_t addr, uint32_t len)

{

        return flash_write(&s_flash, buffer, addr, len);

}

Shell_func.c中

#include "flash_itf.h"

申明两个函数

static void printflashfunc(uint8_t* param);

static void writeflashfunc(uint8_t* param);

g_shell_cmd_list_ast中添加两行,添加两个命令

  { (uint8_t*)"printflash",   printflashfunc,   (uint8_t*)"printflash addr[hex] len"},

  { (uint8_t*)"writeflash",   writeflashfunc,   (uint8_t*)"writeflash addr[hex] hexstr"},        

实现如下

static void printflashfunc(uint8_t* param)

{

  uint8_t buffer[16];

  uint32_t addr;

  uint32_t len;

  if(2 == sscanf((const char*)param, "%*s %x %d", &addr, &len))

  {

    uint32_t toread;

    uint32_t read = 0;

    while(read < len)

    {

      toread = ((len-read) > sizeof(buffer)) ? sizeof(buffer) : (len-read);

                        flash_itf_read(buffer, addr+read, toread);

      read += toread;

      for(uint32_t i=0; i<toread ;i++)

      {

        xprintf("%02x ",buffer);

      }

      xprintf("\r\n");

    }

  }

}

static void writeflashfunc(uint8_t* param)

{

  uint8_t buffer[32+1];

  uint8_t hex[16];


  uint32_t addr;

  uint32_t len;

  if(2 == sscanf((const char*)param, "%*s %x %s", &addr, buffer))

  {

                len = str2hex((const char*)buffer, hex, strlen((char*)buffer));

                if(len>0)

    {

                        flash_itf_write(hex,addr,len);

                }

  }

}

Main.c中初始化

        flash_itf_init();

运行看到新添加的命令

213822nkhnh0527xhr6qqh

测试读写,写入0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99

回读看到正确

213822mdhdum0ynmmtlldu

四.总结

现在我们基于SPI驱动,根据FLASH的手册,实现了FLASH的读写接口。 FLASH驱动完全可移植可复用。 并增加了shell指令进行读写测试。后面就可以移植文件系统了。