发新帖本帖赏金 100.00元(功能说明)我要提问
返回列表
[方案相关]

基于HC32F4A0开发板的Flash读写及数码相框功能实现

[复制链接]
721|8
手机看帖
扫描二维码
随时随地手机跟帖
jinglixixi|  楼主 | 2023-2-26 10:19 | 显示全部楼层 |阅读模式
本帖最后由 jinglixixi 于 2023-2-26 16:37 编辑

#申请原创#

在HC32F4A0开发板上,配有8M字节Flash存储器W25Q64, 该存储器的ID标识为“EF16”,其页长度256字节,扇区长度为4096字节,块长度为64K字节。
使用它可以有效地扩展数据的存储容量,并可用它来构建字库或是图库,其电路如图1所示,相应的引脚连接关系见图2所示。
1.jpg
图1  W25Q64电路

2.jpg
图2  连接关系

为了便于学习和掌握它的使用方法,可通过厂家所配备的2个例程来进行,其名称为spi_write_read_flash和QSPI_Base,其中的spi_write_read_flash主要是用于解决以磁道为单位的擦除及读写问题,而QSPI_Base主要是解决多次读写的问题。
以spi_write_read_flash为例,此测试结果是由LED灯来指示,若顺利完成则以闪亮蓝色指示灯来表示,辅助则是以点亮红色指示灯来表示。

在测试过程一个比较奇特的现象是,若以J14和J19来供电,则只会见到红色指示灯被点亮;而若以J21和J25来供电,则会见到蓝色指示灯闪亮的效果,奇怪不?可千万不要因为它的神奇而勿怪了W25Q64的读写性能呦!
3.jpg
图3  点亮绿色指示灯

4.jpg
图4  点亮红色指示灯

此外,通过对主程序的观察可以发现,若是能通过校验的审核会因的设计而形成的擦写与读写操作被反复地执行,并以此过程的时间消耗来作为指示灯闪烁的延时。这样的处理其实是很不足取的,因为存储器的擦写是有一定寿命限定的,这样来消耗它是实在是一种性能的消耗与浪费。

其主程序的内容如下:
int32_t main(void)
{
    LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
    BSP_CLK_Init();
    BSP_IO_Init();
    BSP_LED_Init();
    BSP_W25QXX_Init();
    LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
    LoadData();
    for (;;) {
        (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
        (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
        VerifyData();
        ClearData();
        BSP_LED_Toggle(LED_BLUE);
    }
}

在同样效果的情况下,可改为如下的内容来实现,其内容为:
int32_t main(void)
{
    LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
    BSP_CLK_Init();
    BSP_IO_Init();
    BSP_LED_Init();
    BSP_W25QXX_Init();
    LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
    LoadData();
    (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
    (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
    (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
    VerifyData();
    ClearData();
    for (;;) {
          BSP_LED_Toggle(LED_BLUE);
          DDL_DelayMS(500);
    }
}


为了便于后续的读写测试及图像显示,可以为它添加上一个彩色的LCD显示屏,其显示分辨率为160*80像素点,其连接形式如图5所示。
该显示屏与开发板的连接关系为:
SCL---PE11
SDA---PE12
RST---PE13
DC ---PE14
CS ---PE15
BL ---VDD
5.jpg
图5  添加LCD显示

为了解其实现方法和过程,下面分成3个部分来详细地展开介绍。

在进行图像存储和再现时,是通过数组来把图像数据转存到FLASH存储器的。由于图像的数据量都比较大,因此要分批次来转存。

1)单扇区写入与图像显示
将数组中的数据转存到写入数组的函数为:
void LoadDatap(uint32_t ti)
{
    uint32_t i;
    for (i = 0UL; i < SPI_FLASH_DATA_SIZE; i++) {
        m_au8WriteData[i]  = gImage_RW[ti+i];
    }
}


相较于以前的图像显示函数LCD_ShowPicture(),需将其更改为函数LCD_ShowPicturep()。
原图像显示函数为:
void LCD_ShowPicture(u16 x1,u16 y1,u16 x2,u16 y2)
{
      int i;
      LCD_Address_Set(x1,y1,x2,y2);
      for(i=0;i<12800;i++)
      {
            LCD_WR_DATA8(gImage_RW[i*2]);
            LCD_WR_DATA8(gImage_RW[i*2+1]);
      }
}

更改后的图像显示函数为:
void LCD_ShowPicturep(void)
{
      int i;
      for(i=0;i<1024;i++)
      {
            LCD_WR_DATA8(m_au8ReadData[i*2]);
            LCD_WR_DATA8(m_au8ReadData[i*2+1]);
      }
}

以一个扇区的数据实现图像再现的主程序为:
#define SPI_FLASH_DATA_SIZE             (1024UL * 2UL)
#define W25Q64_SECTOR_SIZE            (1024UL * 4UL)
int32_t main(void)
{
    uint32_t ti=0;
    LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
    BSP_CLK_Init();
    BSP_IO_Init();
    BSP_LED_Init();
    LED_Init();
    BSP_W25QXX_Init();
    LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
    Lcd_Init();
    LCD_ShowPicture(0,0,159,79);
    DDL_DelayMS(1000);
    LCD_Clear(RED);
    DDL_DelayMS(1000);
    LCD_ShowString(20,10,"HC32F4A0 ",RED);
    LCD_Address_Set (0,0,159,79);
    SPI_FLASH_ADDR=0;
    LoadDatap(ti);
    (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
    (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
    (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
    LCD_ShowPicturep();
    SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
    ti= ti+ SPI_FLASH_DATA_SIZE;
    LoadDatap(ti);
    (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
    (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
    LCD_ShowPicturep();
    for (;;) {
         BSP_LED_Toggle(LED_BLUE);
         DDL_DelayMS(500);
    }
}

2)单幅图像的显示处理
对于一幅160*80像素点的图像来说,要占用25600个字节空间,单是一个扇区是无法存储这样一幅图像的数据,完成要使用多个扇区来完成。
那一幅160*80像素点的16位色图像要需要几个扇区呢?
用需7个扇区,即160*80*2=25600/4096=6.25。
因此在进行数据读写的过程中,既有整扇区的处理,还需有剩余零散扇区的处理。

以多个扇区来完成单幅图像显示的程序为:
     LCD_Clear(RED);
     LCD_Address_Set (0,0,159,79);
     SPI_FLASH_ADDR=0;
     for (j=0;j<6;j++) {
        LoadDatap(ti);
        (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
        (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
        LCD_ShowPicturep();
        SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
        ti= ti+ SPI_FLASH_DATA_SIZE;
        LoadDatap(ti);
        (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
        LCD_ShowPicturep();
        SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
        ti= ti+ SPI_FLASH_DATA_SIZE;
    }
    LoadDatap(ti);
    (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
    (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
    (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
    for(k=0;k<512;k++)
    {
            LCD_WR_DATA8(m_au8ReadData[k*2]);
            LCD_WR_DATA8(m_au8ReadData[k*2+1]);
    }
    for (;;) {
           BSP_LED_Toggle(LED_BLUE);
           DDL_DelayMS(500);
    }

6.jpg
图6  扇区读写的显示效果

7.jpg
图7  整幅图像再现


3)多幅图像的显示处理
以单幅图像显示为基础,要实现多幅图像的显示(数码相框功能),需要将图像数据的存储处理和读取再现功能相分离,这主要是受数组空间的影响,需分批次将一幅幅图像数据存储到FLASH存储器以构成图像库。
如何在通过读取处理将存储的多幅图像再现出来。

其主程序为:
int32_t main(void)
{
        uint32_t ti=0,j,k,a;
        uint8_t F,N=3,M;
        LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
        BSP_CLK_Init();
        BSP_IO_Init();
        BSP_LED_Init();
        LED_Init();
        BSP_W25QXX_Init();
        LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
        Lcd_Init();
        LCD_Clear(BLACK);  //RED
        LCD_ShowString(20,10,"HC32F4A0 ",RED);
        LCD_ShowString(20,40,"Mini PHOTO ",YELLOW);
        DDL_DelayMS(2000);
        LCD_ShowPicture(0,0,159,79);
        DDL_DelayMS(2000);
        F=0;     //  F=0 PLAY ; F=1 RECORDE.
        if(F) {
                      M=N*8;
                      SPI_FLASH_ADDR=M * W25Q64_SECTOR_SIZE;   // 0 8 16 24
                      goto aaa;   //RECORDE
        }
        // PLAY
        a=0;
        while(1)
        {
                SPI_FLASH_ADDR=a*8 * W25Q64_SECTOR_SIZE;
                LCD_Address_Set (0,0,159,79);
                for (j=0;j<6;j++) {
                        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
                        LCD_ShowPicturep();
                        SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
                        DDL_DelayMS(1);
                        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
                        LCD_ShowPicturep();
                        SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
                        DDL_DelayMS(1);
                }
               
               (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE/2);
               DDL_DelayMS(1);
               for(k=0;k<512;k++)
               {
                       LCD_WR_DATA8(m_au8ReadData[k*2]);
                       LCD_WR_DATA8(m_au8ReadData[k*2+1]);
                }
                DDL_DelayMS(1);
                DDL_DelayMS(2000);
                //SPI_FLASH_ADDR=a*8 * W25Q64_SECTOR_SIZE;
                // a=0  RW
                // a=1  FJ
                // a=2  HY
                // a=3  YX
                a=a+1;
                if(a>N)       //  N=3
                {
                      a=0;                //SPI_FLASH_ADDR=0;
                }
                DDL_DelayMS(500);
         }
         for (;;) {
           BSP_LED_Toggle(LED_BLUE);
           DDL_DelayMS(500);
        }
        
        aaa:  //  RECORDE
        LCD_Address_Set (0,0,159,79);
        for (j=0;j<6;j++) {
        LoadDatap(ti);
        (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
        (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
        LCD_ShowPicturep();
        DDL_DelayMS(1);
        SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
        ti= ti+ SPI_FLASH_DATA_SIZE;
        LoadDatap(ti);
        (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE);
        (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE);
        LCD_ShowPicturep();
        DDL_DelayMS(1);
        SPI_FLASH_ADDR= SPI_FLASH_ADDR+ SPI_FLASH_DATA_SIZE;
        ti= ti+ SPI_FLASH_DATA_SIZE;
    }
    LoadDatap(ti);
    (void)BSP_W25QXX_EraseSector(SPI_FLASH_ADDR);
    (void)BSP_W25QXX_Write(SPI_FLASH_ADDR, m_au8WriteData, SPI_FLASH_DATA_SIZE/2);
    (void)BSP_W25QXX_Read(SPI_FLASH_ADDR, m_au8ReadData, SPI_FLASH_DATA_SIZE/2);
    for(k=0;k<512;k++)
    {
              LCD_WR_DATA8(m_au8ReadData[k*2]);
              LCD_WR_DATA8(m_au8ReadData[k*2+1]);
    }
    for (;;) {
              BSP_LED_Toggle(LED_BLUE);
              DDL_DelayMS(500);
    }
}

在使用时,若进行浏览,则令F=1,并使用N来指定可供浏览的图片个数;若进行通讯存储,则令F=0,并以N来指定存储的位置,并将存的通讯数据存放到数组中以供读取。

8.jpg
图8 播放效果

相信通过前面的介绍,即使你所用的不再是W25Q64,也能以所介绍的思路和方法来解决它。

演示视频:


使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2023-02-27
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2023-2-27 13:45 回复TA
通过QSPI访问Flash储存器,快速存取数据,并在OLED显示屏上显示图片,整个流程介绍较为详细,实现效果较好。 
tpgf| | 2024-2-5 09:02 | 显示全部楼层
对数码相框的编写需要相关的工具吗

使用特权

评论回复
guanjiaer| | 2024-2-5 10:05 | 显示全部楼层
数码相框就是一个相框,不过它不再用放进相片的方式来展示,而是通过一个液晶的屏幕显示,它可以通过读卡器的接口从SD卡获取相片,并设置循环显示的方式,比普通的相框更灵活多变,也给现在日益使用的数码相片一个新的展示空间。

使用特权

评论回复
jinglixixi|  楼主 | 2024-2-5 15:32 | 显示全部楼层
guanjiaer 发表于 2024-2-5 10:05
数码相框就是一个相框,不过它不再用放进相片的方式来展示,而是通过一个液晶的屏幕显示,它可以通过读卡器 ...

说的到位!!!

使用特权

评论回复
八层楼| | 2024-2-5 17:55 | 显示全部楼层
数码相框是展示数码照片而非纸质照片的相框。

使用特权

评论回复
观海| | 2024-2-5 18:28 | 显示全部楼层
在实现数码相框的代码中 整幅图像的定位是定死的吗

使用特权

评论回复
heimaojingzhang| | 2024-2-5 18:59 | 显示全部楼层
可以在同一个相框内循环展示(播放)不同照片

使用特权

评论回复
keaibukelian| | 2024-2-5 19:32 | 显示全部楼层
其基本原理:外观采用普通相框的造型,把原来相框中间的照片部分换成液晶显示屏,配上电源,存储介质等,可以直接播放数码照片,使得同一个相框内可以循环播放照片,比普通相框的单一功能更有优势。

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

446

主题

2678

帖子

37

粉丝