这一篇就比较简单了,直接上核心代码,也就是大家要修改的地方,不修改这里,直接拿过去是无法编译通过的

//PPU 将行缓存,写入LCD
//NES游戏的分辨率为256*240.
void NES_LCD_DisplayLine(int y_axes, u16 *Disaplyline_buffer)
{u16 index;LCD_tft.set_FastHLine_XY(32, y_axes, 256);      //定位开始显示坐标和要绘制的数据长度for (index = 8; index < 264; index++)//原始数据16       256{LCD_tft.Write_data(Buffer_scanline[index]); //定位开始显示坐标}LCD_tft.disable_tft_write();
}

这一段代码位于ppu.c代码的最后一部分

这是一个绘制一行图像的函数

可以看到这个函数有两个传入的参数

int y_axes:要绘制的第y行

u16 Disaplyline_buffer:要绘制颜色数据所在的数组,颜色数据格式为RGB565

其中

LCD_tft.set_FastHLine_XY(32, y_axes, 256);      //定位开始显示坐标和要绘制的数据长度

这个函数是要你们自己写的,直接移植会报错

其中:

32位要绘制的列坐标,移位屏幕使用的是320*240的,所以写入32可以使图像位于中间的位置,

int y_axes:要绘制的第y行

256:即要绘制数据的长度了

这是定位开始显示坐标和要绘制的数据长度,即在开始绘制一行的颜色时,先定位要开始绘制的坐标,以及要绘制的数据长度(绘制数据长度跟实际情况而定,不一定需要);

*************************************************************************************************************

  for (index = 8; index < 264; index++){LCD_tft.Write_data(Buffer_scanline[index]); //开始写入颜色数据} 

设置完光标位置之后就开始绘制数据了,图像数据存在于Buffer_scanline[8-263]之中

其中

LCD_tft.Write_data(Buffer_scanline[index]); //开始写入颜色数据

这个函数是要你们自己写的,直接移植会报错 ,

继续往下看,还有一个

 LCD_tft.disable_tft_write();

这个函数对于你们并不一定需要,我使用的是TFT-ESPI的库文件,所以需要编写结束绘制一行的函数,同样的直接复制这一个也会导致编译错误,因此想移植的朋友,注意看哦

最后还有一点需要注意,在拿到ppu.c文件后

  elsefor (i = 8; i < 264; i++)Buffer_scanline[i] = TFT_BLACK; //清空显示缓存,黑屏//原始数据  8    264//完成扫描,将行显示缓存写入LCD*/NES_LCD_DisplayLine(y_axes, Buffer_scanline); //启动LCD显示一行,查询或DMA传送
}//PPU 将行缓存,写入LCD
//NES游戏的分辨率为256*240.
void NES_LCD_DisplayLine(int y_axes, u16 *Disaplyline_buffer)
{u16 index;LCD_tft.set_FastHLine_XY(32, y_axes, 256);      //定位开始显示坐标和要绘制的数据长度for (index = 8; index < 264; index++)//原始数据16       256{LCD_tft.Write_data(Buffer_scanline[index]); //定位开始显示坐标}LCD_tft.disable_tft_write();
}

注意看第三行

 Buffer_scanline[i] = TFT_BLACK; //清空显示缓存,黑屏

这里的TFT_BLACK需要替换为你们显示设备,显示为黑色的代码

至此关于ppu.c文件的介绍就此完结

然后返回nes_main.c文件(不清楚的可以返回第二篇)

 printf("\r\n  开始申请内存\n");res = nes_mem_creat(); //申请内存if (res == 0) //申请成功了.则运行游戏{printf("\r\n  开始申请成功\n");printf("\r\n  初始化按键\n");printf("\r\n  初始化屏幕\n");printf("\r\n  初始化ppu\n");printf("\r\n  初始化joypad\n");printf("\r\n  初始化6502\n");printf("\r\n  无限循环执行游戏\n");printf("\r\n  按下退出游戏键\n");}else   printf("\r\n  开始申请失败\n");nes_mem_delete();//释放内存return res;
}

将上面的代码改为:

 printf("\r\n  开始申请内存\n");res = nes_mem_creat(); //申请内存if (res == 0) //申请成功了.则运行游戏{printf("\r\n  开始申请成功\n");printf("\r\n  初始化6502\n");LCD_tft_init();  //初始化显示设备PPU_Init(((u8 *)&rom_file[offset + 0x10] + (neshreader->romnum * 0x4000)),(neshreader->romfeature & 0x01)); //PPU_初始化printf("\r\n  初始化按键\n");printf("\r\n  初始化joypad\n");printf("\r\n  无限循环执行游戏\n");printf("\r\n  按下退出游戏键\n");}else   printf("\r\n  开始申请失败\n");nes_mem_delete();//释放内存return res;
}

即添加初始化显示设备和ppu初始化

然后对下面这段代码,具体代码可以去第二篇看


//PPU使用u8 *NameTable;           //2K的变量u16  *Buffer_scanline;   //行显示缓存,上下标越界最大为7,显示区 7 ~ 263  0~7 263~270 为防止溢出区
//CPU使用u8 *ram6502;             //RAM  2K字节,由malloc申请
portMUX_TYPE isr_nes_creat = portMUX_INITIALIZER_UNLOCKED;//开辟nes运行所需的RAM.
//返回值:0,成功;
//    其他,错误代码.
u8 nes_mem_creat(void)
{portENTER_CRITICAL_ISR(&isr_nes_creat);ram6502 = (u8 *)malloc(2048); //申请2K内存if (ram6502 == NULL)return 1;       //申请失败NameTable = (u8 *)malloc(2048); //申请2K内存if (NameTable == NULL)return 2;Buffer_scanline = (u16 *)malloc((8 + 256 + 8) * 2);if (Buffer_scanline == NULL)return 3;portEXIT_CRITICAL_ISR(&isr_nes_creat);return 0;
}

删掉

//PPU使用u8 *NameTable;            //2K的变量u16  *Buffer_scanline;   //行显示缓存,上下标越界最大为7,显示区 7 ~ 263  0~7 263~270 为防止溢出区

同时打开.h文件,可以看到

#include <stdio.h>
#include <string.h>
#include "type.h"
#include "nes_rom.h"

在头文件中加入如下代码

#include "PPU.h"

这两句话,然后进行编辑,看到编译通过即可,继续学习可以看第四篇,按键的移植,下面贴出此篇的完整代码分两段贴出

先贴出.h文件的代码,再贴出.c的代码,复制的时候注意一下即可

其中#include “type”为数据类型文件,不清楚的和一去看第一篇

#ifndef _PPU_H_
#define _PPU_H_
#include "type.h"
#include "LCDDisplay.h"
//assumes WORD = 16bit, BYTE = 8bit!!
typedef unsigned short WORD;
typedef unsigned char  BYTE;//sprite 存储器
typedef struct
{u8 spr_ram[256];   //精灵存储器,存储卡通数据 u8 spr_addrcnt;       //sprite 地址计数器
}Spr_MemType;typedef struct
{u8 y;      //垂直Y轴坐标(垂直位置-1)【Vertical Position-1 (FFh,00h..EEh=Scanline 0..239, EFh..FEh=Not displayed)】u8    t_num;  //title 号,(R1.5 )0:(8*8: [7:0]title号码) , 1:(8*16: [7:1]title号码(0~127) ,[0] pattern 0 或1)u8    attr;   //显示属性 [7]垂直翻转,[6]水平饭庄,[5]显示在背景上(下),[4:2]未使用,[1:0]sprite palette(0~3颜色索引表)u8   x;      //水平x轴坐标
}SpriteType;//PPU 存储器 map
typedef struct
{    u16 PPU_addrcnt;       //PPU地址计数器高八位,第一次写入, PPU地址计数器低八位,第一次写入*/u8  PPU_readtemp;      //读取操作缓冲 //PPU 内存映像 64KB寻址 16KB($0000 ~ &3FFF)物理内存,后面的为镜像    u8 *patterntable0;     //$0000 ~ $0FFF 图案表0             u8 *patterntable1;         //$1000 ~ $1FFF 图案表1                 u8 *name_table[4];         //$2000 ~ $23BF 命名表0(32x30块)   //$23C0 ~ $23FF 属性表0)            //$2400 ~ $27BF 命名表1(32x30块)  //$27C0 ~ $27FF 属性表1        //$2800 ~ $2BBF 命名表2(32x30块)  //$2BC0 ~ $2BFF 属性表2()        //$2C00 ~ $2FBF 命名表3(32x30块)) //$2FC0 ~ $2FFF 属性表3         u8 image_palette[16];  //$3F00 ~ $3F0F 背景调色板#1,颜色索引,使用颜色值(RG565) u8 sprite_palette[16];   //$3F00 ~ $3F0F 精灵调色板#1,颜色索引,使用颜色值(RG565)
}PPU_MemType;//PPU 寄存器
typedef struct{u8 NES_R0;    //$2000u8 NES_R1;   //$2001u8 NES_R2;   //$2002
//  u8 NES_R3;   //$2003
//  u8 NES_R4;   //$2004u8 NES_R5;   //$2005
//  u8 NES_R6;   //$2006
//  u8 NES_R7;   //$2007
}PPU_RegType;//PPU  标志        //sprite 属性标志
#define SPR_VFLIP       0x80
#define SPR_HFLIP       0x40
#define SPR_BG_PRIO     0x20    //(0=Sprite In front of BG, 1=Sprite Behind BG)//R0
#define R0_VB_NMI_EN    0x80
#define R0_SPR_SIZE     0x20    //(0=8x8, 1=8x16)
#define BG_PATTERN_ADDR 0x10
#define SPR_PATTERN_ADDR 0x08
#define PPU_ADDRINCR    0x04    //bit2 (0: +1) (1: +32)
#define R0_NAME_TABLE   0x03//R1
#define R1_SPR_VISIBLE  0x10
#define R1_BG_VISIBLE   0x08
#define R1_SPR_LEFT8    0x04
#define R1_BG_LEFT8     0x02
#define R1_DISPMODE     0x01//R2
#define R2_VBlank_Flag  0x80
#define R2_SPR0_HIT     0x40
#define R2_LOST_SPR     0x20//与显示行有关
#define NES_DISP_WIDTH                  256   //每一行扫描像素宽度
#define CLOCKS_PER_SCANLINE             113   //每一行扫描线,CPU时钟113.66//扫描行号,共扫描262行(0~261)
#define SCAN_LINE_START_NUM             0
#define SCAN_LINE_DISPALY_START_NUM     21
#define SCAN_LINE_DISPALY_END_NUM       261
#define SCAN_LINE_END_NUM               262//背景显示 name table 表
#define NAME_TABLE_H_MASK   1
#define NAME_TABLE_V_MASK   2///外部引用 scanline显示变量
extern int PPU_scanline;
extern u8 SpriteHitFlag;//外部引用 存储器数据声明
extern Spr_MemType Spr_Mem;
extern PPU_RegType PPU_Reg;
extern PPU_MemType PPU_Mem;
extern SpriteType  * sprite;//函数声明
void  PPU_Init(u8* vromaddr, u8  ScreenMirrorType);//PPU 存储器与寄存器函数组
void PPU_MemWrite(u8 value);
u8   PPU_MemRead(void);
void PPU_RegWrite(u16 addr, u8 byte);
u8   PPU_RegRead(u16 addr);
u8   PPU_NameTablesRead(void);
void PPU_NameTablesWrite(u8 value);
void NES_GetSpr0HitFlag(int y_axes);
void NES_RenderSprite88(SpriteType *sprptr, int dy_axes);
void NES_RenderSprite16(SpriteType *sprptr, int dy_axes);
void NES_RenderLine(int y_axes);
void NES_LCD_DisplayLine(int y_axes, u16 *Disaplyline_buffer);
void NES_LCD_BG_DisplayLine(u16 color);
#endif 
#include "PPU.h"//变量声明
//存储器相关
u8 *NameTable; //2K的变量PPU_RegType PPU_Reg;
PPU_MemType PPU_Mem;
Spr_MemType Spr_Mem;SpriteType *sprite = (SpriteType *)&Spr_Mem.spr_ram[0]; //指向第一个sprite 0 的位置// 显示相关
u8 SpriteHitFlag, PPU_Latch_Flag; //sprite #0 显示碰撞点所在扫描行号, 背景位移¥2005写入标志
int PPU_scanline;                 //当前扫描行
u16 *Buffer_scanline;             //[8 + 256 + 8];    //行显示缓存,上下标越界最大为7,显示区 7 ~ 263  0~7 263~270 为防止溢出区u8 PPU_BG_VScrlOrg, PPU_BG_HScrlOrg;
//u8 PPU_BG_VScrlOrg_Pre, PPU_BG_HScrlOrg_Pre;
//u8 PPU_BG_NameTableNum;                       //当前背景命名表号
//u16 PPU_AddrTemp;//NES 调色板 颜色表(RGB565)
const u16 NES_Color_Palette[64] ={//颜色索引地址->RGB值 -> RGB565(16bit)/*  0x00 -> 0x75, 0x75, 0x75 */ 0x73AE,/*    0x01 -> 0x27, 0x1B, 0x8F */ 0x20D1,/*    0x02 -> 0x00, 0x00, 0xAB */ 0x0015,/*    0x03 -> 0x47, 0x00, 0x9F */ 0x4013,/*    0x04 -> 0x8F, 0x00, 0x77 */ 0x880E,/*    0x05 -> 0xAB, 0x00, 0x13 */ 0xA802,/*    0x06 -> 0xA7, 0x00, 0x00 */ 0xA000,/*    0x07 -> 0x7F, 0x0B, 0x00 */ 0x7840,/*    0x08 -> 0x43, 0x2F, 0x00 */ 0x4160,/*    0x09 -> 0x00, 0x47, 0x00 */ 0x0220,/*    0x0A -> 0x00, 0x51, 0x00 */ 0x0280,/*    0x0B -> 0x00, 0x3F, 0x17 */ 0x01E2,/*    0x0C -> 0x1B, 0x3F, 0x5F */ 0x19EB,/*    0x0D -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x0E -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x0F -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x10 -> 0xBC, 0xBC, 0xBC */ 0xBDF7,/*    0x11 -> 0x00, 0x73, 0xEF */ 0x039D,/*    0x12 -> 0x23, 0x3B, 0xEF */ 0x21DD,/*    0x13 -> 0x83, 0x00, 0xF3 */ 0x801E,/*    0x14 -> 0xBF, 0x00, 0xBF */ 0xB817,/*    0x15 -> 0xE7, 0x00, 0x5B */ 0xE00B,/*    0x16 -> 0xDB, 0x2B, 0x00 */ 0xD940,/*    0x17 -> 0xCB, 0x4F, 0x0F */ 0xCA61,/*    0x18 -> 0x8B, 0x73, 0x00 */ 0x8B80,/*    0x19 -> 0x00, 0x97, 0x00 */ 0x04A0,/*    0x1A -> 0x00, 0xAB, 0x00 */ 0x0540,/*    0x1B -> 0x00, 0x93, 0x3B */ 0x0487,/*    0x1C -> 0x00, 0x83, 0x8B */ 0x0411,/*    0x1D -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x1E -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x1F -> 0x00, 0x00, 00x0 */ 0x0000,/*    0x20 -> 0xFF, 0xFF, 0xFF */ 0xFFFF,/*    0x21 -> 0x3F, 0xBF, 0xFF */ 0x3DFF,/*    0x22 -> 0x5F, 0x97, 0xFF */ 0x5CBF,/*    0x23 -> 0xA7, 0x8B, 0xFD */ 0xA45F,/*    0x24 -> 0xF7, 0x7B, 0xFF */ 0xF3DF,/*    0x25 -> 0xFF, 0x77, 0xB7 */ 0xFBB6,/*    0x26 -> 0xFF, 0x77, 0x63 */ 0xFBAC,/*    0x27 -> 0xFF, 0x9B, 0x3B */ 0xFCC7,/*    0x28 -> 0xF3, 0xBF, 0x3F */ 0xF5E7,/*    0x29 -> 0x83, 0xD3, 0x13 */ 0x8682,/*    0x2A -> 0x4F, 0xDF, 0x4B */ 0x4EE9,/*    0x2B -> 0x58, 0xF8, 0x98 */ 0x5FD3,/*    0x2C -> 0x00, 0xEB, 0xDB */ 0x075B,/*    0x2D -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x2E -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x2F -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x30 -> 0xFF, 0xFF, 0xFF */ 0xFFFF,/*    0x31 -> 0xAB, 0xE7, 0xFF */ 0xAF3F,/*    0x32 -> 0xC7, 0xD7, 0xFF */ 0xC6BF,/*    0x33 -> 0xD7, 0xCB, 0xFF */ 0xD65F,/*    0x34 -> 0xFF, 0xC7, 0xFF */ 0xFE3F,/*    0x35 -> 0xFF, 0xC7, 0xDB */ 0xFE3B,/*    0x36 -> 0xFF, 0xBF, 0xB3 */ 0xFDF6,/*    0x37 -> 0xFF, 0xDB, 0xAB */ 0xFED5,/*    0x38 -> 0xFF, 0xE7, 0xA3 */ 0xFF34,/*    0x39 -> 0xE3, 0xFF, 0xA3 */ 0xE7F4,/*    0x3A -> 0xAB, 0xF3, 0xBF */ 0xAF97,/*    0x3B -> 0xB3, 0xFF, 0xCF */ 0xB7F9,/*    0x3C -> 0x9F, 0xFF, 0xF3 */ 0x9FFE,/*    0x3D -> 0x00, 0x00, 0x00 */ 0x0000,/*    0x3E -> 0x00, 0x00,0x 00 */ 0x0000,/*    0x3F -> 0x00, 0x00, 0x00 */ 0x0000};
void gui_memset(void *add, int value, int size_num)
{int i;for (i = 0; i < size_num; i++){*(char *)add = value;printf("%*d", 4, *(char *)add);add = (char *)add + 1;}printf("\n");
}
// PPU_RegType PPU_Reg;
// PPU_MemType PPU_Mem;
// Spr_MemType  Spr_Mem;//PPU 初始化
void PPU_Init(u8 *patterntableptr, //Pattern table 地址u8 ScreenMirrorType  //屏幕镜像类型
)
{gui_memset(&PPU_Mem, 1, sizeof(PPU_Mem)); //清零存储器gui_memset(&Spr_Mem, 2, sizeof(Spr_Mem));gui_memset(&PPU_Reg, 3, sizeof(PPU_Reg));PPU_Mem.patterntable0 = patterntableptr;PPU_Mem.patterntable1 = patterntableptr + 0x1000;if (ScreenMirrorType == 0) //水平镜像{PPU_Mem.name_table[0] = &NameTable[0];PPU_Mem.name_table[1] = &NameTable[0];PPU_Mem.name_table[2] = &NameTable[1024];PPU_Mem.name_table[3] = &NameTable[1024];}else //垂直镜像{PPU_Mem.name_table[0] = &NameTable[0];PPU_Mem.name_table[1] = &NameTable[1024];PPU_Mem.name_table[2] = &NameTable[0];PPU_Mem.name_table[3] = &NameTable[1024];}SpriteHitFlag = PPU_Latch_Flag = 0;//   PPU_BG_VScrlOrg = PPU_BG_VScrlOrg_Pre = 0;//  PPU_BG_HScrlOrg = PPU_BG_HScrlOrg_Pre = 0;PPU_BG_VScrlOrg = 0;PPU_BG_HScrlOrg = 0;//    PPU_BG_NameTableNum = 0;// PPU_AddrTemp = 0;PPU_scanline = 0;
}///
//PPU 存储器与寄存器函数组
/////读PPU name table 数据
u8 PPU_NameTablesRead(void)
{u16 addrtemp = PPU_Mem.PPU_addrcnt & 0xFFF;if (addrtemp > 0xC00)return PPU_Mem.name_table[3][addrtemp - 0xC00]; //nametable3if (addrtemp > 0x800)return PPU_Mem.name_table[2][addrtemp - 0x800]; //nametable2if (addrtemp > 0x400)return PPU_Mem.name_table[1][addrtemp - 0x400]; //nametable1elsereturn PPU_Mem.name_table[0][addrtemp]; //nametable0
}//写PPU name table 数据
void PPU_NameTablesWrite(u8 value)
{u16 addrtemp = PPU_Mem.PPU_addrcnt & 0xFFF;if (addrtemp > 0xC00){PPU_Mem.name_table[3][addrtemp - 0xC00] = value; //nametable3return;}if (addrtemp > 0x800){PPU_Mem.name_table[2][addrtemp - 0x800] = value; //nametable2return;}if (addrtemp > 0x400){PPU_Mem.name_table[1][addrtemp - 0x400] = value; //nametable1return;}else{PPU_Mem.name_table[0][addrtemp] = value; //nametable0return;}
}//写PPU存储器
void PPU_MemWrite(u8 value)
{switch (PPU_Mem.PPU_addrcnt & 0xF000){case 0x0000: //$0000 ~ $0FFF  只读 - 与卡带有关//PPU_Mem.patterntable0[PPU_Mem.PPU_addrcnt] = value;break;case 0x1000: //$1000 ~ $1FFF  只读 - 与卡带有关//PPU_Mem.patterntable1[PPU_Mem.PPU_addrcnt & 0x0FFF] = value;break;case 0x2000: //$2000 ~ $2FFFPPU_NameTablesWrite(value);break;case 0x3000://$3000 ~ $3EFF    -- $2000 ~ $2EFF的镜像//$3F00 ~ $3F0F  image  palette//$3F10 ~ $3F1F   sprite paletteif ((PPU_Mem.PPU_addrcnt & 0x1F) > 0x0F){PPU_Mem.sprite_palette[(PPU_Mem.PPU_addrcnt & 0xF)] = value; //精灵颜色索引值表if ((PPU_Mem.PPU_addrcnt & 3) == 0)                          //对应位置为透明色的景象{PPU_Mem.sprite_palette[0] = PPU_Mem.image_palette[0] = value;PPU_Mem.sprite_palette[4] = PPU_Mem.image_palette[4] = value;PPU_Mem.sprite_palette[8] = PPU_Mem.image_palette[8] = value;PPU_Mem.sprite_palette[12] = PPU_Mem.image_palette[12] = value;}}elsePPU_Mem.image_palette[(PPU_Mem.PPU_addrcnt & 0xF)] = value; //背景颜色索引值表//PPU_NameTablesWrite(value);//name table镜像,一般不执行到此处break;default://printf("写入PPU地址大于$4000 %X", PPU_Mem.PPU_addrcnt);//或PPU_Mem.PPU_addrcnt & 0x3FFFbreak;}//读写后,地址计数器增加,根据$2002 [bit2] 0:+1  1: +32。PPU_Reg.NES_R0 &PPU_ADDRINCR ? PPU_Mem.PPU_addrcnt += 32 : PPU_Mem.PPU_addrcnt++;
}//读PPU存储器
u8 PPU_MemRead(void)
{//由于硬件原因,NES PPU每次读取返回的是缓冲值,为时机读取地址减1;u8 temp;temp = PPU_Mem.PPU_readtemp; //保存缓冲值,作为返回值switch (PPU_Mem.PPU_addrcnt & 0xF000){case 0x0000:                                                         //$0000 ~ $0FFFPPU_Mem.PPU_readtemp = PPU_Mem.patterntable0[PPU_Mem.PPU_addrcnt]; //读取地址指定值到缓冲break;case 0x1000:                                                                  //$1000 ~ $1FFFPPU_Mem.PPU_readtemp = PPU_Mem.patterntable1[PPU_Mem.PPU_addrcnt & 0x0FFF]; //读取地址指定值到缓冲break;case 0x2000:                                   //$2000 ~ $2FFFPPU_Mem.PPU_readtemp = PPU_NameTablesRead(); //读取地址指定值到缓冲break;case 0x3000://$3000 ~ $3EFF -- $2000 ~ $2EFF的镜像//$3F00 ~ $3F0F image  palette//$3F10 ~ $3F1F    sprite paletteif (PPU_Mem.PPU_addrcnt >= 0x3F10){temp = PPU_Mem.sprite_palette[(PPU_Mem.PPU_addrcnt & 0xF)]; //PPU 读取缓冲不适用 palette 调色板,直接返回break;}if (PPU_Mem.PPU_addrcnt >= 0x3F00){temp = PPU_Mem.image_palette[(PPU_Mem.PPU_addrcnt & 0xF)]; //PPU 读取缓冲不适用 palette 调色板,直接返回break;}//temp = PPU_NameTablesRead();//name tables 镜像,一般不执行到此处break;default:temp = 0;//printf("读取PPU地址大于$4000 %X", PPU_Mem.PPU_addrcnt);}//读写后,地址计数器增加,根据$2002 [bit2] 0:+1  1: +32。PPU_Reg.NES_R0 &PPU_ADDRINCR ? PPU_Mem.PPU_addrcnt += 32 : PPU_Mem.PPU_addrcnt++;return temp;
}//写PPU寄存器
void PPU_RegWrite(u16 RX, u8 value)
{switch (RX){//$2000case 0:PPU_Reg.NES_R0 = value;//           printf("\r\n PPU r0: %x", value);// Account for Loopy's scrolling discoveries  参考InfoNes//           PPU_AddrTemp = ( PPU_AddrTemp & 0xF3FF ) | ( ( ( (u16)value ) & 0x0003 ) << 10 );//          PPU_BG_NameTableNum = PPU_Reg.NES_R0 & R0_NAME_TABLE;//$2001break;case 1:PPU_Reg.NES_R1 = value;//$2003/break;case 3: //Sprite Memory Address, 8位地址计数器Spr_Mem.spr_addrcnt = value;//$2004break;case 4: //Sprite Memory Data ,每次存取 sprite ram 地址计数器spr_addrcnt自动加1Spr_Mem.spr_ram[Spr_Mem.spr_addrcnt++] = value;//$2005break;case 5: //PPU_Reg.R5 = value;if (PPU_Latch_Flag){                                              //真1:垂直scroll数据PPU_BG_VScrlOrg = (value > 239) ? 0 : value; //原始数据239//地址缓冲值变化,参考infones//PPU_AddrTemp = ( PPU_AddrTemp & 0xFC1F ) | ((((u16)value) & 0xF8 ) << 2);//PPU_AddrTemp = ( PPU_AddrTemp & 0x8FFF ) | ((((u16)value) & 0x07 ) << 12);}else //假0:水平scroll数据{PPU_BG_HScrlOrg = value;//Added : more Loopy Stuff    参考Infones//PPU_AddrTemp = ( PPU_AddrTemp & 0xFFE0 ) | ((((u16)value) & 0xF8 ) >> 3 );}PPU_Latch_Flag ^= 1;//$2006break;case 6://if(PPU_Latch_Flag){        //1//PPU_Mem.PPU_addrcnt = (PPU_Mem.PPU_addrcnt << 8) + value; //PPU 存储器地址计数器,先写高8位,后写低8位///* Low *///PPU_AddrTemp = ( PPU_AddrTemp & 0xFF00 ) | (((u16)value ) & 0x00FF);//PPU_Mem.PPU_addrcnt = PPU_AddrTemp;//PPU_BG_VScrlOrg = (u8)(PPU_Mem.PPU_addrcnt & 0x001F );//PPU_BG_HScrlOrg = (u8)((PPU_Mem.PPU_addrcnt& 0x03E0 ) >> 5 );//}else{                 //0///* High */// PPU_AddrTemp = (PPU_AddrTemp & 0x00FF)|((((u8)value) & 0x003F ) << 8 );//}//PPU_Latch_Flag ^= 1;PPU_Mem.PPU_addrcnt = (PPU_Mem.PPU_addrcnt << 8) + value; //PPU 存储器地址计数器,先写高8位,后写低8位PPU_Latch_Flag ^= 1;//$2007break;case 7: //写 PPU Memory DataPPU_MemWrite(value);break;default://printf("\r\nPPU 写入地址错误 %d", RX);break;}
}//读PPU寄存器
u8 PPU_RegRead(u16 RX)
{u8 temp;switch (RX){case 0:temp = PPU_Reg.NES_R0; //$2000 RWbreak;case 1:temp = PPU_Reg.NES_R1; //$2001 RWbreak;case 2:temp = PPU_Reg.NES_R2;PPU_Reg.NES_R2 &= ~(R0_VB_NMI_EN);//读取$2002,还原PPU地址计数器写入时标志//同样对于$2005 $2006写入状态控制标志PPU_Latch_Flag = 0;// Make a Nametable 0 in V-Blankif ((PPU_scanline > 20 && PPU_scanline < 262) && !(PPU_Reg.NES_R0 & R0_VB_NMI_EN)){                                   //原始数据20                    262PPU_Reg.NES_R0 &= ~R0_NAME_TABLE; //选择 name table #0//PPU_BG_NameTableNum = 0;                    //name table 号 #0}break;;     //$2002 Rcase 4: //读 Sprite Memory Datatemp = Spr_Mem.spr_ram[Spr_Mem.spr_addrcnt++];break;case 7: //读 PPU Memory Datatemp = PPU_MemRead();break;default://printf("\r\nPPU 读取地址错误 %d", RX);return RX;}return temp;
}///
//PPU 显示函数组
/////查找sprite #0碰撞标志
void NES_GetSpr0HitFlag(int y_axes)
{int i, y_scroll, dy_axes, dx_axes;u8 y_TitleLine, x_TitleLine;u8 spr_size, Spr0_Data, temp;u8 nNameTable, BG_TitlePatNum;u8 BG_Data0, BG_Data1, BG_Data;u16 title_addr;u8 *BG_Patterntable;u8 *Spr_Patterntable;//判断sprite #0 显示区域是否在当前行spr_size = PPU_Reg.NES_R0 & R0_SPR_SIZE ? 0x0F : 0x07; //spr_size 8:0~7,16: 0~15dy_axes = y_axes - (u8)(sprite[0].y + 1);              //判断sprite#0 是否在当前行显示范围内,0坐标实际值为FFif (dy_axes != (dy_axes & spr_size))return;//取得sprite显示位置的背景显示数据//nNameTable = PPU_BG_NameTableNum;            //取得当前屏幕的name table 号nNameTable = PPU_Reg.NES_R0 & R0_NAME_TABLE;BG_Patterntable = PPU_Reg.NES_R0 & BG_PATTERN_ADDR ? PPU_Mem.patterntable1 : PPU_Mem.patterntable0; //背景pattern首地址y_scroll = y_axes + PPU_BG_VScrlOrg;                                                                //Scorll 位移后背景显示行的Y坐标,四屏[00]、[01]、[10]、[11],每屏显示(0+x_scroll_org,y_scroll_org)if (y_scroll > 239)                                                                                 //原始数据239{y_scroll -= 240;                 //原始数据240nNameTable ^= NAME_TABLE_V_MASK; //垂直超出屏幕,切换name table表}y_TitleLine = y_scroll >> 3;                        //title 行号 0~29(y轴坐标除8)x_TitleLine = (PPU_BG_HScrlOrg + sprite[0].x) >> 3; //title 列号 0~31(y轴坐标除8)dy_axes = y_scroll & 0x07;                          //title显示y轴偏移像素 除8的余数dx_axes = PPU_BG_HScrlOrg & 0x07;                   //title显示x轴偏移像素 除8的余数if (x_TitleLine > 31)nNameTable ^= NAME_TABLE_H_MASK;                                                 //x轴跨屏显示BG_TitlePatNum = PPU_Mem.name_table[nNameTable][(y_TitleLine << 5) + x_TitleLine]; //y_TitleLine * 32 + x_TitleLine,从name表中取得sprite显示位置的背景的title号的BG_Data0 = BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes];                       //背景显示数据0BG_Data0 |= BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes + 8];if ((x_TitleLine + 1) > 31)nNameTable ^= NAME_TABLE_H_MASK;                                                     //x轴跨屏显示BG_TitlePatNum = PPU_Mem.name_table[nNameTable][(y_TitleLine << 5) + x_TitleLine + 1]; //从name表中下一个背景显示的title号的BG_Data1 = BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes];                           //背景显示数据1BG_Data1 |= BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes + 8];BG_Data = (BG_Data0 << dx_axes) | (BG_Data1 >> dx_axes); //背景与Sprite #0 位置相同的当前显示行的显示数据//取得sprite #0 显示数据if (sprite[0].attr & SPR_VFLIP)dy_axes = spr_size - dy_axes;   //若垂直翻转if (PPU_Reg.NES_R2 & R0_SPR_SIZE) //8*16  若为真,sprite的大小8*16{                                 //取得所在title Pattern首地址Spr_Patterntable = (sprite[0].t_num & 0x01) ? PPU_Mem.patterntable1 : PPU_Mem.patterntable0;title_addr = (sprite[0].t_num & 0XFE) << 4; //*16,原地址已*2Spr0_Data = Spr_Patterntable[title_addr + dy_axes];Spr0_Data |= Spr_Patterntable[title_addr + dy_axes + 8];}else //8*8{    //取得sprite #0 所在title Pattern首地址Spr_Patterntable = (PPU_Reg.NES_R0 & SPR_PATTERN_ADDR) ? PPU_Mem.patterntable1 : PPU_Mem.patterntable0;title_addr = sprite[0].t_num << 4; //*16Spr0_Data = Spr_Patterntable[title_addr + dy_axes];Spr0_Data |= Spr_Patterntable[title_addr + dy_axes + 8];}if (sprite[0].attr & SPR_HFLIP) //若水平翻转, 翻转高低位数据{temp = 0;for (i = 0; i < 8; i++){temp |= (Spr0_Data >> i) & 1;temp <<= i;}Spr0_Data = temp;}if (Spr0_Data & BG_Data){//printf("\r\nSprite #0 Hit!");SpriteHitFlag = 1;}
}//显示一行背景,若与sprite碰撞,设置碰撞标志
void NES_RenderBGLine(int y_axes)
{int i, y_scroll, /*x_scroll,*/ dy_axes, dx_axes;int Buffer_LineCnt, y_TitleLine, x_TitleLine;u8 H_byte, L_byte, BG_color_num, BG_attr_value;u8 nNameTable, BG_TitlePatNum;u8 *BG_Patterntable;//nNameTable = PPU_BG_NameTableNum;         //取得当前屏幕的name table 号nNameTable = PPU_Reg.NES_R0 & R0_NAME_TABLE;//printf("\r\n name table num: %x", nNameTable);BG_Patterntable = PPU_Reg.NES_R0 & BG_PATTERN_ADDR ? PPU_Mem.patterntable1 : PPU_Mem.patterntable0; //背景pattern首地址y_scroll = y_axes + PPU_BG_VScrlOrg;                                                                //Scorll 位移后显示行的Y坐标if (y_scroll > 239)                                                                                 //垂直超出屏幕,切换name table表{y_scroll -= 240;nNameTable ^= NAME_TABLE_V_MASK;}y_TitleLine = y_scroll >> 3; //title 行号 0~29(y轴坐标除8)dy_axes = y_scroll & 0x07;   //除8的余数//x_scroll =     PPU_BG_HScrlOrg_Pre;dx_axes = PPU_BG_HScrlOrg & 0x07; //x轴偏移像素//先显示一行的左边部分,从第一个像素开始扫描Buffer_LineCnt = 8 - dx_axes; //缓存写入位置(0~ 255),8是显示起始点//x_TitleLine ~ 31 列像素显示(8bit一列)for (x_TitleLine = PPU_BG_HScrlOrg >> 3; x_TitleLine < 32; x_TitleLine++) //从左数第一个显示title单元开始{//printf("\r\n%d %d %d",y_axes, y_TitleLine, x_TitleLine);BG_TitlePatNum = PPU_Mem.name_table[nNameTable][(y_TitleLine << 5) + x_TitleLine]; //y_TitleLine * 32,当前显示的title号的L_byte = BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes];                         //BG_TitlePatNum * 16 + dy_xaesH_byte = BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes + 8];//属性表 中查找 高两位颜色索引值                      除4去余数再乘8                除4BG_attr_value = PPU_Mem.name_table[nNameTable][960 + ((y_TitleLine >> 2) << 3) + (x_TitleLine >> 2)]; //title对应的属性表8bit值//(title对应的高两位)(y title bit2  右移 1位)(运算)或 (x title bit2)所得值 [000][010][100][110] 0 2 4 6为对应的attr 8bit[0:1][2:3][4:5][6:7] 中的高两位颜色值BG_attr_value = ((BG_attr_value >> (((y_TitleLine & 2) << 1) | (x_TitleLine & 2))) & 3) << 2;//x列每次扫描8像素显示for (i = 7; i >= 0; i--) //先写左边像素的颜色{//[1:0]低两位颜色索引值BG_color_num = BG_attr_value;BG_color_num |= (L_byte >> i) & 1;BG_color_num |= ((H_byte >> i) & 1) << 1;if (BG_color_num & 3)Buffer_scanline[Buffer_LineCnt] = NES_Color_Palette[PPU_Mem.image_palette[BG_color_num]]; //如果低两位为0,则为透明色,不写入Buffer_LineCnt++;}}//显示一行右边部分, 切换name table表nNameTable ^= NAME_TABLE_H_MASK;//Buffer_LineCnt -= dx_axes;//右边0 ~ PPU_BG_HScrlOrg_Pre >> 3for (x_TitleLine = 0; x_TitleLine <= (PPU_BG_HScrlOrg >> 3); x_TitleLine++){BG_TitlePatNum = PPU_Mem.name_table[nNameTable][(y_TitleLine << 5) + x_TitleLine]; //y_TitleLine * 32,当前显示的title号的L_byte = BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes];H_byte = BG_Patterntable[(BG_TitlePatNum << 4) + dy_axes + 8];BG_attr_value = PPU_Mem.name_table[nNameTable][960 + ((y_TitleLine >> 2) << 3) + (x_TitleLine >> 2)]; //title对应的属性表8bit值BG_attr_value = ((BG_attr_value >> (((y_TitleLine & 2) << 1) | (x_TitleLine & 2))) & 3) << 2;         //索引颜色[4:3]for (i = 7; i >= 0; i--){BG_color_num = BG_attr_value;BG_color_num |= (L_byte >> i) & 1;BG_color_num |= ((H_byte >> i) & 1) << 1;if (BG_color_num & 3)Buffer_scanline[Buffer_LineCnt] = NES_Color_Palette[PPU_Mem.image_palette[BG_color_num]];Buffer_LineCnt++;}}
}//显示一个sprite的title 88
void NES_RenderSprPattern(SpriteType *sprptr, u8 *Spr_Patterntable, u16 title_addr, u8 dy_axes)
{int i, dx_axes;u8 Spr_color_num, H_byte, L_byte;if ((PPU_Reg.NES_R1 & R1_SPR_LEFT8 == 0) && sprptr->x < 8) //禁止左8列像素显示{dx_axes = 8 - sprptr->x;if (dx_axes == 0)return;}elsedx_axes = 0;if (sprptr->attr & SPR_VFLIP) //若垂直翻转{dy_axes = 7 - dy_axes; //sprite 8*8显示dy_axes行}L_byte = Spr_Patterntable[title_addr + dy_axes];H_byte = Spr_Patterntable[title_addr + dy_axes + 8];if (sprptr->attr & SPR_HFLIP) //若水平翻转{for (i = 7; i >= dx_axes; i--) //先写右边 颜色数据{Spr_color_num = (L_byte >> i) & 1;         //bit0Spr_color_num |= ((H_byte >> i) & 1) << 1; //bit1if (Spr_color_num == 0)continue;Spr_color_num |= (sprptr->attr & 0x03) << 2;                                                   //bit23Buffer_scanline[sprptr->x + i + 8] = NES_Color_Palette[PPU_Mem.sprite_palette[Spr_color_num]]; //偏移8}}else{for (i = 7; i >= dx_axes; i--) //先写右边 颜色数据{Spr_color_num = (L_byte >> (7 - i)) & 1;         //bit0Spr_color_num |= ((H_byte >> (7 - i)) & 1) << 1; //bit1if (Spr_color_num == 0)continue;Spr_color_num |= (sprptr->attr & 0x03) << 2;                                                   //bit23Buffer_scanline[sprptr->x + i + 8] = NES_Color_Palette[PPU_Mem.sprite_palette[Spr_color_num]]; //写入颜色值到缓存}}
}// sprite 8*8 显示数据扫描
void NES_RenderSprite88(SpriteType *sprptr, int dy_axes)
{u8 *Spr_Patterntable;//取得所在title Pattern首地址Spr_Patterntable = (PPU_Reg.NES_R0 & SPR_PATTERN_ADDR) ? PPU_Mem.patterntable1 : PPU_Mem.patterntable0;NES_RenderSprPattern(sprptr, Spr_Patterntable, sprptr->t_num << 4, (u8)dy_axes);
}//sprite 8*16 显示数据扫描
void NES_RenderSprite16(SpriteType *sprptr, int dy_axes)
{if (sprptr->t_num & 0x01){if (dy_axes < 8)                                                                                 //sprite  title 奇数号NES_RenderSprPattern(sprptr, PPU_Mem.patterntable1, (sprptr->t_num & 0xFE) << 4, (u8)dy_axes); //上8*8elseNES_RenderSprPattern(sprptr, PPU_Mem.patterntable1, sprptr->t_num << 4, (u8)dy_axes & 7); //下8*8}else{if (dy_axes < 8)                                                                        //sprite  title 偶数号NES_RenderSprPattern(sprptr, PPU_Mem.patterntable0, sprptr->t_num << 4, (u8)dy_axes); //上8*8elseNES_RenderSprPattern(sprptr, PPU_Mem.patterntable0, (sprptr->t_num | 1) << 4, (u8)dy_axes & 7); //下8*8}
}void NES_RenderLine(int y_axes)
{int i, render_spr_num, spr_size, dy_axes;//MMC5 VROM switch -- VROM存储器切换//MapperRenderScreen( 1 );PPU_Reg.NES_R2 &= ~R2_LOST_SPR; //设置PPU状态寄存器R2 SPR LOST的标志位//PPU_BG_VScrlOrg_Pre = PPU_BG_VScrlOrg;                            //背景 垂直 scroll//PPU_BG_HScrlOrg_Pre = PPU_BG_HScrlOrg;                         //背景 水平 scrollif (PPU_Reg.NES_R1 & (R1_BG_VISIBLE | R1_SPR_VISIBLE)) //若为假,关闭显示,填0黑{//清空显示缓存,在此设置底背景色(待确定)//原始数据7      256for (i = 8; i < 264; i++) //显示区 7 ~ 263  0~7 263~270 为防止溢出区{Buffer_scanline[i] = NES_Color_Palette[PPU_Mem.image_palette[0]];}spr_size = PPU_Reg.NES_R0 & R0_SPR_SIZE ? 0x0F : 0x07; //spr_size 8:0~7,16: 0~15//扫描背景sprite并转换成显示数据写入到缓存,每一行最多只能显示8个Spriteif (PPU_Reg.NES_R1 & R1_SPR_VISIBLE) //若开启sprite显示{render_spr_num = 0; //清零显示计数器for (i = 63; i >= 0; i--){ //若重叠sprites 0 具有显示最高优先级,其余优先级顺序次之,所以最先显示最低优先级//判断显示层(非) 背景if (!(sprite[i].attr & SPR_BG_PRIO))continue; //(0=Sprite In front of BG, 1=Sprite Behind BG)//判断显示位置dy_axes = y_axes - (u8)(sprite[i].y + 1); //判断sprite是否在当前行显示范围内,sprite y (FF,00,01,...EE)(0~239)if (dy_axes != (dy_axes & spr_size))continue; //若不在则返回继续循环查找下一个//若存在sprite在当前显示行,则转入下面显示阶段render_spr_num++;       //已显示的sprite的数目+1if (render_spr_num > 8) //一行超过8个spreite,跳出循环{PPU_Reg.NES_R2 |= R2_LOST_SPR; //设置PPU状态寄存器R2的标志位break;}if (PPU_Reg.NES_R0 & R0_SPR_SIZE)NES_RenderSprite16(&sprite[i], dy_axes); //若为真,sprite的大小8*16elseNES_RenderSprite88(&sprite[i], dy_axes); //若为假,sprite的大小8*8}}//扫描背景 backgroundif (PPU_Reg.NES_R1 & R1_BG_VISIBLE)NES_RenderBGLine(y_axes); //扫描并设置Sprite #0碰撞标志//扫描前景sprite并转换成显示数据写入到缓存,每一行最多只能显示8个Sprite*if (PPU_Reg.NES_R1 & R1_SPR_VISIBLE) //若开启sprite显示{render_spr_num = 0; //清零显示计数器//若重叠sprites 0 具有显示最高优先级,其余优先级顺序次之,所以最先显示最低优先级//备注:若前景sprites 优先级低于背景优先级,重叠的颜色,前景优先级低于背景优先级的话,前景将不会显示(暂未处理)*/for (i = 63; i >= 0; i--){//判断显示层 前景if (sprite[i].attr & SPR_BG_PRIO)continue; //(0=Sprite In front of BG, 1=Sprite Behind BG)//判断显示位置dy_axes = y_axes - ((int)sprite[i].y + 1); //判断sprite是否在当前行显示范围内,sprite y (FF,00,01,...EE)(0~239)if (dy_axes != (dy_axes & spr_size))continue; //若不在则返回继续循环查找下一个//若存在sprite在当前显示行,则转入下面显示阶段render_spr_num++;       //已显示的sprite的数目+1if (render_spr_num > 8) //一行超过8个spreite,跳出循环{PPU_Reg.NES_R2 |= R2_LOST_SPR; //设置PPU状态寄存器R2的标志位break;}if (PPU_Reg.NES_R0 & R0_SPR_SIZE)NES_RenderSprite16(&sprite[i], dy_axes); //若为真,sprite的大小8*16elseNES_RenderSprite88(&sprite[i], dy_axes); //若为假,sprite的大小8*8}}}elsefor (i = 8; i < 264; i++)Buffer_scanline[i] = TFT_BLACK; //清空显示缓存,黑屏//原始数据  8   264//完成扫描,将行显示缓存写入LCD*/NES_LCD_DisplayLine(y_axes, Buffer_scanline); //启动LCD显示一行,查询或DMA传送
}//PPU 将行缓存,写入LCD
//NES游戏的分辨率为256*240.     但因为 NTSC 所以分辨率剩下 256x224。
void NES_LCD_DisplayLine(int y_axes, u16 *Disaplyline_buffer)
{u16 index;LCD_tft.set_FastHLine_XY(32, y_axes, 256);      //定位开始显示坐标和要绘制的数据长度for (index = 8; index < 264; index++)//原始数据16       256{LCD_tft.Write_data(Buffer_scanline[index]); //定位开始显示坐标}LCD_tft.disable_tft_write();
}

第三篇:手把手教你移植任天堂,没有声音、无需外置SD卡、可使用独立按键也可使用外置手柄,本人使用的芯片为ESP32,移植到STM32均可使用。(本篇主要介绍ppu.c文件,即教你如何移植屏幕)相关推荐

  1. tiny4412 uboot 2020.10版本移植(四)——uboot修改支持sd卡、eMMC引导内核及其他一些杂项设置

    本文在<tiny4412 uboot 2020.10版本移植(三)--uboot初步启动> 的基础上继续向tiny4412 uboot 2020.10版添加功能. 主要有三块内容:1. D ...

  2. 使用vscode + gcc进行 STM32 单片机开发(三)DMA读写SD卡,移植FATFS文件系统

    背景 在本系列的前两篇文章( 使用vscode + gcc进行 STM32 单片机开发(一)编译及调试 使用vscode + gcc进行 STM32 单片机开发(二)gcc环境 移植rtthread) ...

  3. Android6.0 MountService和vold详解(三) vold SD卡、otg

    既上面两篇博客,继续分析vold.对外置SD卡和OTG的分析: 一.process_config函数 上一篇我们再main函数中分析了VolumeManager的start函数,这次我们接下来分析pr ...

  4. android vold,Android6.0 MountService和vold详解(三) vold SD卡、otg

    既上面两篇博客,继续分析vold.对外置SD卡和OTG的分析: 一.process_config函数 上一篇我们再main函数中分析了VolumeManager的start函数,这次我们接下来分析pr ...

  5. 基于SD卡的FatFs文件系统(FatFs移植到STM32)

    平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线 工程介绍:主要文件在USER组中,bsp_sdio_sdcard.c,bsp_sdio_sdcard.h和main.c, ...

  6. stm32f769 寄存器配置SD卡---移植fatfs

    昨天开始在上周实现的SD卡读写基础上移植fatfs文件系统,开始不是很顺利,之前没有搞过,完全按照f767的例程移植的,但是在加载SD卡时一直是失败的,很郁闷,折腾了一天,结果还不理想,睡了个好觉,思 ...

  7. GD32F4xx SD卡读写及FATFS移植记录

      最近调试了一下GD32F450Z 的SDIO接口,读写micro SD卡并移植了FATFS调试过程记录如下,调试时使用的是16GB Kingston TF 卡. 说一下思路:分3步实现 1.从资料 ...

  8. 芯片的SD/MMC控制器以及SD卡介绍

    1.MMC.SD卡.eMMC介绍 1.1.三者关联 (1)最早出现的是MMC卡,卡片式结构,按照MMC协议设计.(相较于NandFlash芯片来说,MMC卡有2个优势:第一是卡片化,便于拆装:第二是统 ...

  9. 【FatFs】基于STM32 SD卡移植FatFs文件系统

    相关文章 <[SDIO]SDIO.SD卡.FatFs文件系统相关文章索引> 1.前言 FatFs是一个通用的FAT/exFAT文件系统模块,用于小型嵌入式系统.它完全是由 ANSI C 语 ...

最新文章

  1. JZOJ5922. 【NOIP2018模拟10.23】sequence
  2. python爬取csdn排名积分等信息
  3. 向上造型和向下造型_动漫美少年漫画造型-仰视、俯视、情感表现
  4. django 学习个人总结 之many_to_one
  5. Unity3D插件之DoTween
  6. 三星电脑计算机主板图,三星S10E+拆解图文教程
  7. 机器学习模型太慢?来看看英特尔(R) 扩展加速 ⛵
  8. 如何理解IT、OT、CT?
  9. HDU4699Editor
  10. python opencv 读取mov文件
  11. 第二章 SQL命令参考-REASSIGN OWNED
  12. Altium Designer中关于PCB及原理绘制那些高级玩意总结
  13. 自动驾驶汽车如何有助于可持续移动规划?
  14. 流程引擎规则引擎_规则引擎的优势
  15. DATA GUARD代码(部分)
  16. 判断vector中是否存在某元素的多种方法
  17. Win11服务Windows Update禁用后自动开启怎么办
  18. CSDN实训 - 通过Java修改游戏存档
  19. The certificate issuer‘s certificate has expired. Check your system date and time.“
  20. QQ截图无法截取右键菜单等内容解决方案

热门文章

  1. 我为什么不再推荐RxJava
  2. DC-DC电源模块输出先放大电容还是小电容
  3. python绘制有误差线的折线图
  4. oracle imp lrm 00101,LRM-00101: unknown parameter name解决办法
  5. 我要的就是简单的幸福
  6. 在学校玩计算机被发现的检讨书,高中生玩手机被发现检讨书
  7. 微信小程序合成海报_利用微信小程序中Canvas API来合成海报生成组件封装
  8. Java调用PDFBox打印自定义纸张PDF
  9. 安卓手机APP压力monkey测试
  10. 浅谈聚合支付的分类及发展前景