[AT32F423] 【AT-START-F423测评】+NES模拟器

[复制链接]
1647|1
 楼主| 夜声 发表于 2023-11-21 23:44 | 显示全部楼层 |阅读模式
本帖最后由 夜声 于 2023-11-21 23:46 编辑

一、前言
很幸运的参与本次AT32F423开发板的测评,计划首先按自己的风格新建个工程,实现基本外设SPI,UART,LED等外设,然后基于SPI实现2.8寸的LCDTFT,LCD正常显示后实现NES模拟器。
二、NES简介
    NES是“Nintendo Entertainment System”的缩写,它是任天堂公司在1980年代推出的一款家用游戏机。在日本,它被称为Famicom(Family Computer)。NES于1983年在日本首次推出,随后于1985年在北美地区发行,成为当时家庭游戏市场的主导力量。
这款游戏机因其丰富的游戏库、经典的游戏系列以及在家庭游戏市场的影响力而备受推崇。它引入了许多经典游戏,如《马里奥兄弟》(Super Mario Bros.)、《塞尔达传说》(The Legend of Zelda)、《魂斗罗》(Contra)和《魔界村》(Castlevania)等,这些游戏至今仍然被认为是游戏史上的经典之作。
本次就基于AT32F423VCT6实现这部分代码。大概说一下这个芯片的资源。
     AT32F423系列超值型ARM®Cortex®-M4F微控制器,高达150MHz的CPU运算速度与内建的单精度浮点运算单元(FPU)、数字信号处理器(DSP),多达256KB闪存存储器(Flash)及48KB随机存取存储器(SRAM),而系统存储器(20KB)除可作启动加载程序(Bootloader)外,也可一次性配置成一般用户程序和数据区,达到256+20KB的最大空间使用。片上丰富的外设资源,用以加强连接性,集成XMC接口(拓展PSRAM,NOR存储器,或8080/6800模式并行LCD)、1个OTG控制器(设备模式支持无晶振Xtal-less)、2组CAN总线、8个UART、3个SPI/I²S(可组合全双工模式)、3个I²C、1个16位高级定时器、8个16位通用定时器、1个32位通用定时器、2个16位基本定时器。1个采样率高达5.33Msps的12位24通道高速ADC与2个12位DAC,为支持混合信号控制提供更高的性价比。
三、移植准备
3.1新建工程
首先下载相关资料,函数库,以及一些示例,板级支持包
494d4a39724368c0a9036a2f247298f3
新建自己的工程目录,将函数库,M4头文件,板级文件拷贝过来
dd197499c1a3c14a73db6f65c05c8f71
打开keil,新建工程,添加源码以及头文件路径
374b4120b2176efc69a3047544722869
3.2 SPI驱动
这里的SPI只用于LCD的驱动,配置为主机模式,实现8位数据发送即可。
  1. void spi1_init(void)

  2. {

  3. gpio_init_typegpio_initstructure;

  4. spi_init_typespi_init_struct;

  5. crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);

  6. crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);

  7. gpio_default_para_init(&gpio_initstructure);

  8. /* spi1 sck pin */

  9. gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

  10. gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

  11. gpio_initstructure.gpio_pull = GPIO_PULL_UP;

  12. gpio_initstructure.gpio_mode = GPIO_MODE_MUX;

  13. gpio_initstructure.gpio_pins = GPIO_PINS_5;

  14. gpio_init(GPIOA, &gpio_initstructure);

  15. gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_5);

  16. /* spi1 mosi pin */

  17. gpio_initstructure.gpio_pull = GPIO_PULL_UP;

  18. gpio_initstructure.gpio_pins = GPIO_PINS_7;

  19. gpio_init(GPIOA, &gpio_initstructure);

  20. gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_5);

  21. /* RST= PA2,DC=PA3 */

  22. gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;

  23. gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;

  24. gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;

  25. gpio_initstructure.gpio_pins = GPIO_PINS_2 | GPIO_PINS_3;

  26. gpio_initstructure.gpio_pull = GPIO_PULL_UP;

  27. gpio_init(GPIOA, &gpio_initstructure);

  28. spi_default_para_init(&spi_init_struct);

  29. spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;

  30. spi_init_struct.master_slave_mode = SPI_MODE_MASTER;

  31. spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_4;

  32. spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;

  33. spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;

  34. spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;

  35. spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;

  36. spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;

  37. spi_init(SPI1, &spi_init_struct);

  38. spi_enable(SPI1, TRUE);

  39. }

  40. void spi_writebyte(uint8_t TxData)

  41. {

  42. while(spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET);

  43. spi_i2s_data_transmit(SPI1, TxData);

  44. }

3.3 LCD显示实现
上面已经实现了SPI 的驱动,以及8位发送,移植LCD的驱动代码,替换显示发送函数,以及IO定义。
写数据函数:
  1. <blockquote>void LCD_WR_DATA(u8 data)
写命令函数:
  1. void LCD_WR_REG(u8 data)
  2. {
  3.    LCD_CS_CLR;     
  4.          LCD_RS_CLR;         
  5.    spi_writebyte(data);
  6.    LCD_CS_SET;        
  7. }
写16位数据函数:
  1. void Lcd_WriteData_16Bit(u16 Data)
  2. {        
  3.    LCD_CS_CLR;
  4.    LCD_RS_SET;  
  5.    spi_writebyte(Data>>8);
  6.          spi_writebyte(Data);
  7.    LCD_CS_SET;
  8. }
LCD所用IO定义:
  1. #define LCD_CS_PIN  GPIO_PINS_1
  2. #define LCD_RST_PIN GPIO_PINS_2
  3. #define LCD_DC_PIN         GPIO_PINS_3
  4. #define gpio_x                         GPIOA

  5. //GPIOÖÃ루À­¸ß£©
  6. #define        LCD_CS_SET  gpio_x->scr = LCD_CS_PIN    //Ƭѡ¶Ë¿Ú         
  7. #define        LCD_RS_SET        gpio_x->scr = LCD_DC_PIN    //Êý¾Ý/ÃüÁî   
  8. #define        LCD_RST_SET        gpio_x->scr = LCD_RST_PIN   //¸´Î»                          

  9. //GPIO¸´Î»£¨À­µÍ£©                                                            
  10. #define        LCD_CS_CLR  gpio_x->clr = LCD_CS_PIN     //Ƭѡ¶Ë¿Ú         
  11. #define        LCD_RS_CLR        gpio_x->clr = LCD_DC_PIN     //Êý¾Ý/ÃüÁî  
  12. #define        LCD_RST_CLR        gpio_x->clr = LCD_RST_PIN    //¸´Î»        



四、移植过程
添加NES源码:
1,nes源码.jpg
添加源码路径:
2添加文档路径.jpg
nes_main主函数:
  1. void nes_main(void)
  2. {
  3.     NesHeader *neshreader = (NesHeader *) rom_file;
  4.     init6502mem( 0,         /*exp_rom*/
  5.                  0,         /*sram ÓÉ¿¨ÀàÐ;ö¶¨, Ôݲ»Ö§³Ö*/
  6.                  (&rom_file[0x10]),      /*prg_rombank, ´æ´¢Æ÷´óС ÓÉ¿¨ÀàÐ;ö¶¨*/
  7.                  neshreader->romnum
  8.                );  //³õʼ»¯6502´æ´¢Æ÷¾µÏñ
  9.     reset6502();
  10.     PPU_Init((&rom_file[0x10] + (neshreader->romnum * 0x4000)),(neshreader->romfeature & 0x01));   /*PPU_³õʼ»¯*/
  11.     //NES_JoyPadInit();
  12.     NesFrameCycle();
  13. }
PPU显示每一行:
  1. void NES_RenderLine(int y_axes)
  2. {
  3. //    static u8 flag=0;
  4.         int i, render_spr_num, spr_size, dy_axes;
  5.     PPU_Reg.R2 &= ~R2_LOST_SPR;                                         //ÉèÖÃPPU״̬¼Ä´æÆ÷R2 SPR LOSTµÄ±ê־λ
  6.     if(PPU_Reg.R1 & (R1_BG_VISIBLE | R1_SPR_VISIBLE))                   //ÈôΪ¼Ù£¬¹Ø±ÕÏÔʾ£¬Ìî0ºÚ
  7.     {
  8.         /*Çå¿ÕÏÔʾ»º´æ£¬ÔÚ´ËÉèÖõױ³¾°É«£¨´ýÈ·¶¨£©*/
  9.         for(i=0; i<(8 + 256 + 8) ; i++)                       //ÏÔÊ¾Çø 7 ~ 263  0~7 263~270 Ϊ·ÀÖ¹Òç³öÇø
  10.         {
  11.             Buffer_scanline[i] =  NES_Color_Palette[PPU_Mem.image_palette[0]];
  12.         }
  13.         spr_size = PPU_Reg.R0 & R0_SPR_SIZE ? 0x0F : 0x07;              //spr_size 8£º0~7£¬16: 0~15
  14.         /* ɨÃè±³¾°sprite²¢×ª»»³ÉÏÔʾÊý¾ÝдÈëµ½»º´æ,ÿһÐÐ×î¶àÖ»ÄÜÏÔʾ8¸öSprite*/
  15.         if(PPU_Reg.R1 & R1_SPR_VISIBLE)                                 //Èô¿ªÆôspriteÏÔʾ
  16.         {
  17.             render_spr_num=0;                                           //ÇåÁãÏÔʾ¼ÆÊýÆ÷
  18.             for(i=63; i>=0; i--)                                        //ÈôÖØµþsprites 0 ¾ßÓÐÏÔʾ×î¸ßÓÅÏȼ¶£¬ÆäÓàÓÅÏȼ¶Ë³Ðò´ÎÖ®£¬ËùÒÔ×îÏÈÏÔʾ×îµÍÓÅÏȼ¶
  19.             {
  20.                 /*ÅжÏÏÔʾ²ã£¨·Ç£© ±³¾°*/
  21.                 if(!(sprite[i].attr & SPR_BG_PRIO))
  22.                 {
  23.                     continue;    //(0=Sprite In front of BG, 1=Sprite Behind BG)
  24.                 }
  25.                 /*ÅжÏÏÔʾλÖÃ*/
  26.                 dy_axes = y_axes - (uint8)(sprite[i].y + 1);            //ÅжÏspriteÊÇ·ñÔÚµ±Ç°ÐÐÏÔʾ·¶Î§ÄÚ,sprite y (FF,00,01,...EE)(0~239)
  27.                 if(dy_axes != (dy_axes & spr_size))
  28.                 {
  29.                     continue;    //Èô²»ÔÚÔò·µ»Ø¼ÌÐøÑ­»·²éÕÒÏÂÒ»¸ö
  30.                 }
  31.                 /*Èô´æÔÚspriteÔÚµ±Ç°ÏÔʾÐÐ,ÔòתÈëÏÂÃæÏÔʾ½×¶Î*/
  32.                 render_spr_num++;                                       //ÒÑÏÔʾµÄspriteµÄÊýÄ¿+1
  33.                 if(render_spr_num > 8 )                                 //Ò»Ðг¬¹ý8¸öspreite£¬Ìø³öÑ­»·
  34.                 {
  35.                     PPU_Reg.R2 |= R2_LOST_SPR;                          //ÉèÖÃPPU״̬¼Ä´æÆ÷R2µÄ±ê־λ
  36.                     break;
  37.                 }
  38.                 if(PPU_Reg.R0 & R0_SPR_SIZE)                            //ÈôÎªÕæ£¬spriteµÄ´óС8*16
  39.                 {
  40.                     NES_RenderSprite16(&sprite[i], dy_axes);
  41.                 }
  42.                 else                                                    //ÈôΪ¼Ù£¬spriteµÄ´óС8*8
  43.                 {
  44.                     NES_RenderSprite88(&sprite[i], dy_axes);
  45.                 }
  46.             }
  47.         }
  48.         /* ɨÃè±³¾° background*/
  49.         if(PPU_Reg.R1 & R1_BG_VISIBLE)
  50.         {
  51.             NES_RenderBGLine(y_axes);                                   //ɨÃè²¢ÉèÖÃSprite #0Åöײ±êÖ¾
  52.         }
  53.         /* ɨÃèǰ¾°sprite²¢×ª»»³ÉÏÔʾÊý¾ÝдÈëµ½»º´æ,ÿһÐÐ×î¶àÖ»ÄÜÏÔʾ8¸öSprite*/
  54.         if(PPU_Reg.R1 & R1_SPR_VISIBLE)                                 //Èô¿ªÆôspriteÏÔʾ
  55.         {
  56.             render_spr_num=0;                                           //ÇåÁãÏÔʾ¼ÆÊýÆ÷
  57.             /* ÈôÖØµþsprites 0 ¾ßÓÐÏÔʾ×î¸ßÓÅÏȼ¶£¬ÆäÓàÓÅÏȼ¶Ë³Ðò´ÎÖ®£¬ËùÒÔ×îÏÈÏÔʾ×îµÍÓÅÏȼ¶
  58.              * ±¸×¢£ºÈôǰ¾°sprites ÓÅÏȼ¶µÍÓÚ±³¾°ÓÅÏȼ¶£¬ÖصþµÄÑÕÉ«£¬Ç°¾°ÓÅÏȼ¶µÍÓÚ±³¾°ÓÅÏȼ¶µÄ»°£¬Ç°¾°½«²»»áÏÔʾ(ÔÝδ´¦Àí)*/
  59.             for(i=63; i>=0; i--)
  60.             {
  61.                 /*ÅжÏÏÔʾ²ã ǰ¾°*/
  62.                 if(sprite[i].attr & SPR_BG_PRIO)
  63.                 {
  64.                     continue;    //(0=Sprite In front of BG, 1=Sprite Behind BG)
  65.                 }
  66.                 /*ÅжÏÏÔʾλÖÃ*/
  67.                 dy_axes = y_axes - ((int)sprite[i].y + 1);              //ÅжÏspriteÊÇ·ñÔÚµ±Ç°ÐÐÏÔʾ·¶Î§ÄÚ,sprite y (FF,00,01,...EE)(0~239)
  68.                 if(dy_axes != (dy_axes & spr_size))
  69.                 {
  70.                     continue;    //Èô²»ÔÚÔò·µ»Ø¼ÌÐøÑ­»·²éÕÒÏÂÒ»¸ö
  71.                 }
  72.                 /*Èô´æÔÚspriteÔÚµ±Ç°ÏÔʾÐÐ,ÔòתÈëÏÂÃæÏÔʾ½×¶Î*/
  73.                 render_spr_num++;                                       //ÒÑÏÔʾµÄspriteµÄÊýÄ¿+1
  74.                 if(render_spr_num > 8 )                                 //Ò»Ðг¬¹ý8¸öspreite£¬Ìø³öÑ­»·
  75.                 {
  76.                     PPU_Reg.R2 |= R2_LOST_SPR;                          //ÉèÖÃPPU״̬¼Ä´æÆ÷R2µÄ±ê־λ
  77.                     break;
  78.                 }
  79.                 if(PPU_Reg.R0 & R0_SPR_SIZE)                            //ÈôÎªÕæ£¬spriteµÄ´óС8*16
  80.                 {
  81.                     NES_RenderSprite16(&sprite[i], dy_axes);
  82.                 }
  83.                 else                                                    //ÈôΪ¼Ù£¬spriteµÄ´óС8*8
  84.                 {
  85.                     NES_RenderSprite88(&sprite[i], dy_axes);
  86.                 }
  87.             }
  88.         }
  89.     }
  90.     else
  91.     {
  92.         for(i=0; i<(8 + 256 + 8); i++)
  93.         {
  94.             Buffer_scanline[i] = BLACK;//Çå¿ÕÏÔʾ»º´æ,ºÚÆÁ
  95.         }
  96.     }
  97.     /*Íê³ÉɨÃ裬½«ÐÐÏÔʾ»º´æÐ´ÈëLCD*/
  98.     NES_LCD_DisplayLine(y_axes, Buffer_scanline);//Æô¶¯LCDÏÔʾһÐУ¬²éѯ»òDMA´«ËÍ
  99. }
将行缓存写入LCD:
  1. void NES_LCD_DisplayLine(int y_axes, uint16 *Disaplyline_buffer)
  2. {
  3.         u16 index;                                          
  4.         LCD_SetWindows(0,200,240,y_axes);        
  5.          LCD_WriteRAM_Prepare();
  6.         for(index = 16; index < 256; index++)
  7.         {
  8.                 Lcd_WriteData_16Bit( Buffer_scanline[index]);
  9.         }
  10. }
显示效果如下:
效果.gif


qintian0303 发表于 2023-11-24 17:20 | 显示全部楼层
这个挺有趣,刷屏效果也不错,没考虑用FSMC刷屏吗
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

89

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部