【立创开发板】立创开发板NES游戏机 - 嘉立创EDA开源硬件平台

编辑器版本 ×
标准版 Standard

1、简单易用,可快速上手

2、流畅支持300个器件或1000个焊盘以下的设计规模

3、支持简单的电路仿真

4、面向学生、老师、创客

专业版 professional

1、全新的交互和界面

2、流畅支持超过3w器件或10w焊盘的设计规模,支持面板和外壳设计

3、更严谨的设计约束,更规范的流程

4、面向企业、更专业的用户

专业版 【立创开发板】立创开发板NES游戏机

简介:基于立创梁山派GD32F4高性能开发板制作自己的游戏机,这游戏机移植NES模拟器,我们可以很好地学习LCD屏的移植,SD卡的文件系统的使用,简单UI界面制作,音乐输出的功能,等等。

开源协议: GPL 3.0

(未经作者授权,禁止转载)

创建时间: 2023-03-22 20:23:49
更新时间: 2023-05-25 21:52:06
描述

一、项目简介

  基于立创梁山派GD32F4高性能开发板制作自己的游戏机,这游戏机移植NES模拟器,目前一般NES游戏都可以玩,目前有上百款游戏下载。在这项目当中,我们可以很好地学习LCD屏的移植,SD卡的文件系统的使用,简单UI界面制作,音乐输出的功能,等等。在这项目当中我们可以回忆童年时光。

二、板载功能资源

PCBT

(图1 PCB正面)

PCBB

(图2 PCB反面)

 

三、重点学习内容:

1、文件系统,Fats读写操作(SD卡与Flash 的文件系统)。

2EXMCLCD驱动(8080LCD接口)

3、移植NES的显示240*240改成480*480

4、液晶屏触摸控制及玩游戏

5DAC音乐输出

6、文字库使

7SDRAM的内存管理

8Bmp图片文件读取存储显示

 

四、电路原理图介绍:

 

1、电源管理部分:

POWER

3TYPE-C 充电接口,二极管可以防反灌输入。

POWER

(图4)电源管理模块

 图4是充电与升压管理部分。U10为充电IC,充电电流最大1AU9为升压ICME2159)一款比较优秀的锂电池升压IC2.5V-4.2V宽电压转换效率可以达到90%

如下图5(官方手册测试参数):

(图5)ME2159 性能图

图4中CHG1CHG2为充电IC的正在充电、充电完成状态信号。R27电阻是插入充电电源后停止升压,程序要拨充电电源后延时重启否则白屏。右侧R16R19电池电压采样电流,计算电池电压公式:

faValue = adcValue*(3.368/4096)*fadcoffset;

        A.    favalue:电池电压

        B.     adcValueADC读取的值

        C.     fadcoffset:矫正值

 

2、语音放大电路:

(图6)DAC输出信号放大电路

图6中改变R10与R8 的比例可以实现改变音频放大的倍数,可以让喇叭声音变大。

 

3、游戏控制遥杆

(图7)兼容两款遥杆的电路

实物展示:

图8

型号:RKJXV1220001

立创商城购买:RKJXV1220001_(ALPSALPINE(阿尔卑斯阿尔派))RKJXV1220001中文资料_价格_PDF手册-立创电子商城 (szlcsc.com)

图9

型号:无

淘宝购买:https://item.taobao.com/item.htm?spm=a1z09.2.0.0.38cf2e8dqWGxGP&id=642782754225&_u=jak0o1p43a3

 

五、系统框架

图10、系统图

 

1、显示屏选用:购买的立创`梁山派-屏幕扩展板 10件套(详情请点击以下)

立创`梁山派-屏幕扩展板_(立创开发板)立创`梁山派-屏幕扩展板中文资料_价格_PDF手册-立创电子商城 (szlcsc.com)

2、梁山派开发板:立创·梁山派 GD32F470ZGT6开发板 五件套(详情请点击以下)

立创·梁山派 GD32F470ZGT6开发板_(立创开发板)立创·梁山派 GD32F470ZGT6开发板中文资料_价格_PDF手册-立创电子商城 (szlcsc.com)

3、组合后(接好后):

这是完整接好的样子。

这是开机后的样子。

这个是游戏界面

 

 

 

 

六、程序重点描述

 

1、EXMC驱动TFT-LCD与SDRAM共用:

          初次调试,EXMC的LCD驱动时发现SDRAM存储异常,运行游戏失败,LCD显示正常,如果屏蔽LCD驱动,SDRAM就正常运行游戏,因为游戏文件要加载到SDRAM上运行。问题就是SDRAM和TFT-LCD共用就程序异常跑飞,最终解决问题,优化了SDRAM的时序延时。

代码如下:

ErrStatus exmc_synchronous_dynamic_ram_init(uint32_t sdram_device)
{
    exmc_sdram_parameter_struct        sdram_init_struct;
    exmc_sdram_timing_parameter_struct  sdram_timing_init_struct;
    exmc_sdram_command_parameter_struct     sdram_command_init_struct;

    uint32_t command_content = 0, bank_select;
    uint32_t timeout = SDRAM_TIMEOUT;

    /* enable EXMC clock*/
    rcu_periph_clock_enable(RCU_EXMC);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOD);
    rcu_periph_clock_enable(RCU_GPIOE);
    rcu_periph_clock_enable(RCU_GPIOF);
    rcu_periph_clock_enable(RCU_GPIOG);
    rcu_periph_clock_enable(RCU_GPIOH);

    /* common GPIO configuration */
    /* SDNWE(PC0),SDNE0(PC2),SDCKE0(PC3) pin configuration */ 
    gpio_af_set(GPIOC, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);
    gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);

    /* D2(PD0),D3(PD1),D13(PD8),D14(PD9),D15(PD10),D0(PD14),D1(PD15) pin configuration */
    gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_8 | GPIO_PIN_9 |
                                   GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_8 | GPIO_PIN_9 |
                                                         GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_8 | GPIO_PIN_9 |
                                                                     GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15);

    /* NBL0(PE0),NBL1(PE1),D4(PE7),D5(PE8),D6(PE9),D7(PE10),D8(PE11),D9(PE12),D10(PE13),D11(PE14),D12(PE15) pin configuration */
    gpio_af_set(GPIOE, GPIO_AF_12, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_7  | GPIO_PIN_8 |
                                   GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |
                                   GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_7  | GPIO_PIN_8 |
                                                         GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |
                                                         GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_7  | GPIO_PIN_8 |
                                                                     GPIO_PIN_9  | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 |
                                                                     GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);

    /* A0(PF0),A1(PF1),A2(PF2),A3(PF3),A4(PF4),A5(PF5),NRAS(PF11),A6(PF12),A7(PF13),A8(PF14),A9(PF15) pin configuration */
    gpio_af_set(GPIOF, GPIO_AF_12, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_2  | GPIO_PIN_3  |
                                   GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_11 | GPIO_PIN_12 |
                                   GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_2  | GPIO_PIN_3  |
                                                         GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_11 | GPIO_PIN_12 |
                                                         GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0  | GPIO_PIN_1  | GPIO_PIN_2  | GPIO_PIN_3  |
                                                                     GPIO_PIN_4  | GPIO_PIN_5  | GPIO_PIN_11 | GPIO_PIN_12 |
                                                                     GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);

    /* A10(PG0),A11(PG1),A12(PG2),A14(PG4),A15(PG5),SDCLK(PG8),NCAS(PG15) pin configuration */
    gpio_af_set(GPIOG, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | 
                                   GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15);
    gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | 
                                                         GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15);
    gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | 
                                                                     GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15);

    /* specify which SDRAM to read and write */
    if(EXMC_SDRAM_DEVICE0 == sdram_device){
        bank_select = EXMC_SDRAM_DEVICE0_SELECT;
    }else{
        bank_select = EXMC_SDRAM_DEVICE1_SELECT;
    }

    /* EXMC SDRAM device initialization sequence --------------------------------*/
    /* Step 1 : configure SDRAM timing registers --------------------------------*/
    /* LMRD: 2 clock cycles */
    sdram_timing_init_struct.load_mode_register_delay = 2;
    /* XSRD: min = 75ns */
    sdram_timing_init_struct.exit_selfrefresh_delay = 9;  
    /* RASD: min=44ns , max=120k (ns) */
    sdram_timing_init_struct.row_address_select_delay = 5;  
    /* ARFD: min=66ns */
    sdram_timing_init_struct.auto_refresh_delay = 8;  
    /* WRD:  min=1 Clock cycles +7.5ns */
    sdram_timing_init_struct.write_recovery_delay = 3;
    /* RPD:  min=20ns */
    sdram_timing_init_struct.row_precharge_delay = 3; 
    /* RCD:  min=20ns */
    sdram_timing_init_struct.row_to_column_delay = 3; 

    /* step 2 : configure SDRAM control registers ---------------------------------*/
    sdram_init_struct.sdram_device = sdram_device;
    sdram_init_struct.column_address_width = EXMC_SDRAM_COW_ADDRESS_9;
    sdram_init_struct.row_address_width = EXMC_SDRAM_ROW_ADDRESS_13;
    sdram_init_struct.data_width = EXMC_SDRAM_DATABUS_WIDTH_16B;
    sdram_init_struct.internal_bank_number = EXMC_SDRAM_4_INTER_BANK;
    sdram_init_struct.cas_latency = EXMC_CAS_LATENCY_3_SDCLK;  
    sdram_init_struct.write_protection = DISABLE;
    sdram_init_struct.sdclock_config = EXMC_SDCLK_PERIODS_2_HCLK;  
    sdram_init_struct.brust_read_switch = ENABLE;
    sdram_init_struct.pipeline_read_delay = EXMC_PIPELINE_DELAY_2_HCLK;
    sdram_init_struct.timing  = &sdram_timing_init_struct;
    /* EXMC SDRAM bank initialization */
    exmc_sdram_init(&sdram_init_struct);

    /* step 3 : configure CKE high command---------------------------------------*/
    sdram_command_init_struct.command = EXMC_SDRAM_CLOCK_ENABLE;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_2_SDCLK;
    sdram_command_init_struct.mode_register_content = 0;
    /* wait until the SDRAM controller is ready */ 
    while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)){
        timeout--;
    }
    if(0 == timeout){
        return ERROR;
    }    
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 4 : insert 10ms delay----------------------------------------------*/
    delay_1ms(10);

    /* step 5 : configure precharge all command----------------------------------*/
    sdram_command_init_struct.command = EXMC_SDRAM_PRECHARGE_ALL;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_1_SDCLK;
    sdram_command_init_struct.mode_register_content = 0;
    /* wait until the SDRAM controller is ready */
    timeout = SDRAM_TIMEOUT; 
    while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)){
        timeout--;
    }
    if(0 == timeout){
        return ERROR;
    }    
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 6 : configure Auto-Refresh command-----------------------------------*/
    sdram_command_init_struct.command = EXMC_SDRAM_AUTO_REFRESH;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_8_SDCLK;
    sdram_command_init_struct.mode_register_content = 0;
    /* wait until the SDRAM controller is ready */ 
    timeout = SDRAM_TIMEOUT; 
    while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)){
        timeout--;
    }
    if(0 == timeout){
        return ERROR;
    }    
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 7 : configure load mode register command-----------------------------*/
    /* program mode register */
    command_content = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1        |
                                SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL   |
                                SDRAM_MODEREG_CAS_LATENCY_3           |
                                SDRAM_MODEREG_OPERATING_MODE_STANDARD |
                                SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

    sdram_command_init_struct.command = EXMC_SDRAM_LOAD_MODE_REGISTER;
    sdram_command_init_struct.bank_select = bank_select;
    sdram_command_init_struct.auto_refresh_number = EXMC_SDRAM_AUTO_REFLESH_2_SDCLK;
    sdram_command_init_struct.mode_register_content = command_content;

    /* wait until the SDRAM controller is ready */ 
    timeout = SDRAM_TIMEOUT; 
    while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)){
        timeout--;
    }
    if(0 == timeout){
        return ERROR;
    }    
    /* send the command */
    exmc_sdram_command_config(&sdram_command_init_struct);

    /* step 8 : set the auto-refresh rate counter--------------------------------*/
    /* 64ms, 8192-cycle refresh, 64ms/8192=7.81us */
    /* SDCLK_Freq = SYS_Freq/2 */
    /* (7.81 us * SDCLK_Freq) - 20 */
    exmc_sdram_refresh_count_set(761); 

    /* wait until the SDRAM controller is ready */ 
    timeout = SDRAM_TIMEOUT; 
    while((exmc_flag_get(sdram_device, EXMC_SDRAM_FLAG_NREADY) != RESET) && (timeout > 0)){
        timeout--;
    }
    if(0 == timeout){
        return ERROR;
    }
    return SUCCESS;    
}

   修改完代码后,程序正常运行游戏。

 

2、TFT-LCD显示图片修改:

 

原代码是这样的:

void LCD_ShowPicture1(uint16_t x,uint16_t y,uint16_t length,uint16_t width,const uint8_t *pic)
{
uint16_t i,j;
uint32_t k=0;
LCD_Address_Set(x,y,x+length-1,y+width-1);
    
    LCD_DC_Set();//写数据
    LCD_CS_Clr();
for(i=0;i<length;i++)
{
for(j=0;j<width;j++)
{
            while(RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_TBE));
            spi_i2s_data_transmit(SPI4, *pic++);
            while(RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_TBE));
            spi_i2s_data_transmit(SPI4, *pic++);
}
}
    while(RESET == spi_i2s_flag_get(SPI4, SPI_FLAG_RBNE));
    while(SET == spi_i2s_flag_get(SPI4, SPI_STAT_TRANS));
    LCD_CS_Set();
    
    while(spi_i2s_flag_get(SPI4, SPI_FLAG_RBNE)) i=spi_i2s_data_receive(SPI4);
}

 

源代码显示图片是直接使用SPI对显示屏写入数据,没有直接调用LCD驱动的方法,若是不留意就程序一开机会挂在这里

8080显示驱动 修改为:

void LCD_ShowPicture1(uint16_t x,uint16_t y,uint16_t length,uint16_t width,const uint8_t *pic)
{
    uint16_t i,j;
    uint32_t k=0;
 
     uint16_t pdat;
 
LCD_Address_Set(x,y,x+length-1,y+width-1);
    
for(i=0;i<length;i++)
{
for(j=0;j<width;j++)
{
pdat = pic[k*2];
pdat = pdat<<8|pic[k*2+1];
 
LCD_WR_DATAs(pdat);
 
k++;
 
}
      }   
}

 

    字库更新:(需要的字库文件:UNIGBK.BIN,GBK12.BIN,GBK16.BIN,GBK24.BIN)我在正点原子找了字库文件,拿来用,发现出现以下的显示问题:

      

找到问题所在:text.c   --->   Show_Font 函数如下:

//************************************************************************

void Show_Font(u16 x,u16 y,u8 *font,uint16_t fc,uint16_t bc,u8 size,u8 mode)
{
u8 temp,t,t1;
u16 y0=y;
u8 dzk[72];   
u8 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数 
if(size!=12&&size!=16&&size!=24)return;//不支持的size
Get_HzMat(font,dzk,size);//得到相应大小的点阵数据 
for(t=0;t<csize;t++)
{      
temp=dzk[t];      //得到点阵数据                          
for(t1=0;t1<8;t1++)
{
          if(temp&0x80) LCD_DrawPoint(x,y,fc);
            else if(mode==0) LCD_DrawPoint(x,y,bc);
temp<<=1;
y++;
if((y-y0)==size)
{
y=y0;
x++;
break;
}
}   
}  
}

//***************************************************************************

把函数里的X和Y改改就可以了,改过如下:

//**********************************************************

 
void Show_Font(u16 x,u16 y,u8 *font,uint16_t fc,uint16_t bc,u8 size,u8 mode)
{
u8 temp,t,t1;
// u16 y0=y;
u16 x0=x;
u8 dzk[72];   
u8 csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数 
if(size!=12&&size!=16&&size!=24)return;//不支持的size
Get_HzMat(font,dzk,size);//得到相应大小的点阵数据 
for(t=0;t<csize;t++)
{      
temp=dzk[t];//得到点阵数据                          
for(t1=0;t1<8;t1++)
{
            if(temp&0x80) LCD_DrawPoint(x,y,fc);
            else if(mode==0) LCD_DrawPoint(x,y,bc);
temp<<=1;
x++;
if((x-x0)==size)
{
  x=x0;
  y++;
  break;
}
}   
     }
}
 
 
 

//**************************************************  

其次游戏的显示驱动:240*240分辨率改成480*480分辨率,不需要增加显存的情况下:

nes_ppu.c  ---->    void scanline_draw(int  LineNo)函数

 

//***********************************************************************************

extern uint8_t nes_xoff; //显示在x轴方向的偏移量(实际显示宽度=256-2*nes_xoff)

void scanline_draw(int LineNo)

{

 

uint16 i; 

uint16_t sx,ex;

do_scanline_and_draw(ppu->dummy_buffer);

 

  #if 0

  sx=nes_xoff+8;

  ex=256+8-nes_xoff;

 

  //printf("LineNo:%d sx:%d ex:%d\r\n",LineNo,sx,ex);

  LCD_Address_Set(LineNo,sx,LineNo,ex);//设置显示范围 横着屏幕

  for(i=sx;i<ex;i++){

    //LCD_DrawPoint(LineNo,i,NES_Palette[ppu->dummy_buffer[i]]);

    //printf("i:%d color:%d \r\n",i,NES_Palette[ppu->dummy_buffer[i]]);

    LCD_WR_DATA(NES_Palette[ppu->dummy_buffer[i]]);

  }

  #else

  sx = 17;

  ex = 240+17;

    

    

    LCD_Address_Set(0,(LineNo*2)+40,480,(LineNo*2)+40);//设置显示范围 横着屏显示

   

for(i=sx;i<ex;i++){

 

LCD_WR_DATAs(NES_Palette[ppu->dummy_buffer[i]]);

LCD_WR_DATAs(NES_Palette[ppu->dummy_buffer[i]]);

 

}

 

 

LCD_Address_Set(0,(LineNo*2+1)+40,480,(LineNo*2+1)+40);//设置显示范围 横着屏显示

    

//LCD_DC_Set();//写数据

    // LCD_CS_Clr();

    

 

for(i=sx;i<ex;i++){

 

LCD_WR_DATAs(NES_Palette[ppu->dummy_buffer[i]]);

LCD_WR_DATAs(NES_Palette[ppu->dummy_buffer[i]]);

 

}

}

 

函数里红色部分:大概原理就是x坐标数据写入两次,Y坐标的一行写两行,等于 一个点放大加1倍,240*240的内存显示放大成 480*480,节省RAM显存。

 

游戏显示部分好了.

 

 

 

3、讲讲Bmp文件显示:

bmp的函数代码复制于教材的源码改的,

重要文件:file_opera.c      bmp_opera.c

修改了屏蔽了这两个程序里大多数的LCD驱动代码,只留下bmp文件处理解析的内容。

fatsbmp文件,bmp文件信息的主意格式结构体(bmp_opera.h):

//BMP文件主要信息结构体

typedef struct

{

//1. 文件信息头

uint16  tbfType;//开头2个字节,文件类型,必须是0x4D42

uint32  tbfSize;//文件大小,字节为单位

uint32_tbfOffset;    //数据起始偏移地址,单位:字节  

 

//2. 位图信息头

uint32  tbiSize;//信息头字节数,一般就是40

uint32 tbiWidth;//BMP图宽度,像素

uint32 tbiHeight;//BMP图高度,像素

uint16 tbiBitCount;//每个像素的比特数

} BMPFileInfoDef;

 

bmp文件在SD卡内存储,文件信息数据排列顺序就根据这个结构图排列:

//调用以下函数读取BMP文件信息, 文件信息保存到指针BmpFileInfo指向的变量

uint8_t ReadBmpFileInfo(uint8_t *Filename, BMPFileInfoDef *BmpFileInfo)

{

FIL  file;

FRESULT res=f_open(&file, (char *)Filename, FA_READ);         //fats 读取bmp文件信息

if(res != FR_OK)

{

f_close(&file);

return 0;     //失败返回0

}

 

ReadBmpHeader(&file, BmpFileInfo);//读取文件头信息

 

f_close(&file);        //成功后关闭清除file句柄

return 1;   //成功返回1

}

 

然后,可以用void    ShowBmpFileInfo(const BMPFileInfoDef *BmpFileInfo)  把bmp文件的信息通过printf打印出来

 

再然后,bmp文件图片数据显示到LCD上,图片是24位RGB888, 一个像素3个字节,排列顺序BGR,
BMP图片从下开始往上逐行存储, 在电脑上对图片进行了上下翻转,所以直接顺序读取,

需要转换为RGB565表示的像素点数据,才能画到LCD上

 

 

边读边显示的函数:

void DrawBmp(const uint16_t  PosX, const uint16_t PosY,

FIL *file, BMPFileInfoDef *fileInfo)

 

在LCD上绘制整个BMP图片,返回值为0表示绘图成功
uint8_t    DrawBmpFile(const uint16_t  PosX, const uint16_t PosY, uint8_t *Filename)

 

以上是Bmp图片直接显示LCD屏上,后面我自己改了个直接读数据保存在SDRAM里的

uint32_t BmpRead(uint16_t *buf ,FIL *file, BMPFileInfoDef *fileInfo)


//读BMP图片把数据写buffer里,返回值为0表示绘图成功
uint8_t    ReadBmpFile(uint16_t *buf ,  uint8_t *Filename,BMPFileInfoDef    *fileInfo)

 

后面调用程序,使用方法(NES_GUI.c里):

先分配图片内存:

 

然后读取bmp数据到RAM里:

这些函数把图片显示内容,读到RAM里,再显示

LCD_DrawBmp 因为LCD屏定了是自上往下显示的,bmp文件的内容是自下往上的,所以改了这个函数。

 

 

 

 

设计图
原理图
1 /
PCB
1 /
未生成预览图,请在编辑器重新保存一次
工程成员
侵权投诉
相关工程
换一批
加载中...
添加到专辑 ×

加载中...

温馨提示 ×

是否需要添加此工程到专辑?

温馨提示
动态内容涉嫌违规
内容:
  • 153 6159 2675

服务时间

周一至周五 9:00~18:00
  • 技术支持

support
  • 开源平台公众号

MP