视频讲解:使用SPI_DMA读外部Flash+TFT_DMA双缓冲对TFT刷屏,不占CPU时间 
STC32G数据手册的 29.12.3章节 
//测试工作频率为35MHz  
/*************  功能说明    ************** 使用SPI的DMA方式对外挂的串行FLASH进行读取数据,并将数据存储在XDATA的缓冲区中,然后使用LCM的DMA方式将该缓冲区的数据写入到TFT彩屏。 整个过程采样双缓冲Ping-Pang模式: 1、SPI_DMA从FLASH读取数据到缓冲区1 2、上一步的SPI_DMA完成后,启动LCM_DMA将缓冲区1的数据送彩屏,同时SPI_DMA从FLASH读取数据到缓冲区2 3、上一步的SPI_DMA完成后,启动LCM_DMA将缓冲区2的数据送彩屏,同时SPI_DMA从FLASH读取数据到缓冲区 4、重复步骤2和步骤3 本测试代码在实验箱9.4B上测试通过。使用DMA中断加双缓冲可极大提高CPU效率,实测数据如下: ******************************************/ //#include "stc8h.h" #include "stc32g.h"//头文件见下载软件 #include "stdio.h" #define       FOSC                           35000000UL                                   //系统工作频率 #define       BAUD                          (65536- (FOSC/115200+2)/4)                                                                                                                 //加2操作是为了让Keil编译器                                                                                                                 //自动实现四舍五入运算 #define       T2S                               (65536- FOSC*2/12/128) #define       SCREENCX                320                                                  //TFT彩屏的宽度(横向像素) #define       SCREENCY                240                                                  //TFT彩屏的高度(纵向像素) #define       IMG1_ADDR              0x00000000                                    //第1幅图片在FLASH中的起始地址 #define       IMG2_ADDR              0x00025800                                    //第2幅图片在FLASH中的起始地址 #define       IMG3_ADDR              0x0004b000                                    //第3幅图片在FLASH中的起始地址 #define       HIBYTE(w)                 (BYTE)(((WORD)(w))>> 8) #define       LOBYTE(w)                 (BYTE)(w) #define       RGB565(r, g, b)           ((((r) & 0x1f) << 11) |(((g)& 0x3f) << 5) | ((b) & 0x1f)) #define       WHITE                        RGB565(31,63, 31)                       //白色 #define       BLACK                        RGB565(0,0, 0)                             //黑色 #define       GRAY                           RGB565(16,32, 16)                       //灰色 #define       RED                             RGB565(31,0, 0)                           //红色 #define       GREEN                       RGB565(0,63, 0)                           //绿色 #define       BLUE                           RGB565(0,0, 31)                           //蓝色 #define       CYAN                           RGB565(0,63, 31)                         //青色 #define       MAGENTA                 RGB565(31,0, 31)                         //紫色 #define       YELLOW                     RGB565(31,63, 0)                         //黄色 #define       DMA_BUFSIZE         (3*1024)                                         //DMA缓冲区大小 //320*240的彩屏一屏的图片数据为150KB //DMA缓冲区设置为3K比较好 typedef       bit                                 BOOL; typedef       unsigned char              BYTE; typedef       unsigned int                 WORD; typedef       unsigned long              DWORD; sbit SPI_SS                  =       P2^2; sbit SPI_MOSI            =       P2^3; sbit SPI_MISO            =       P2^4; sbit SPI_SCLK            =       P2^5; sbit LCM_CS               =       P3^4; sbit LCM_RST             =       P4^3; sbit LCM_RS               =       P4^5; sbit LCM_RD              =       P4^4; sbit LCM_WR              =       P4^2; #define LCM_DB                 P6 #define       LCM_WRDB(d)          LCM_WR= 0;\                                                         LCM_DB= (d);\                                                         _nop_();\                                                         LCM_WR= 1 void delay_ms(WORD n); void delay_us(WORD n); void sys_init(); void lcm_init(); void lcm_write_cmd(BYTE   cmd); void lcm_write_dat(BYTE     dat); void lcm_write_dat2(WORD dat); void lcm_set_window(int x0, int x1,int  y0, int y1); void lcm_clear_screen(); void lcm_show_next_image(); BYTE xdata buffer1[DMA_BUFSIZE];                                              //定义缓冲区 BYTE xdata buffer2[DMA_BUFSIZE];                                              //注意:如果需要使用DMA发送数据, //则缓冲区必须定义在xdata区域内 BYTE image; BYTE stage; BOOL f2s;                                                                                              //2秒标志位 void main() {          sys_init();                                                                                      //系统初始化          lcm_init();          f2s    = 1;          while(1)          {                    if(f2s)                    {                             f2s    = 0;                             lcm_show_next_image();                                           //每2秒自动显示下一幅图片                    }          } } void tm0_isr() interrupt 1 {          f2s    = 1;                                                                                       //设置2秒标志 } void common_isr(void) interrupt  13 {          if(DMA_LCM_STA & 0x01)          {                    DMA_LCM_STA= 0;                    if(stage >= 2L*SCREENCY*SCREENCX/DMA_BUFSIZE)                    {                             LCM_CS= 1;                    }          }          if(DMA_SPI_STA & 0x01)          {                    DMA_SPI_STA= 0;                    if(!(stage & 1))                    {                             DMA_LCM_TXAL= (BYTE)&buffer1;                   //设置DMA缓冲区起始地址                             DMA_LCM_TXAH= (WORD)&buffer1 >> 8;                             DMA_LCM_CR= 0xa1;                                           //启动DMA开始发送数据                    }                    else                    {                             DMA_LCM_TXAL= (BYTE)&buffer2;                   //设置DMA缓冲区起始地址                             DMA_LCM_TXAH= (WORD)&buffer2 >> 8;                             DMA_LCM_CR= 0xa1;                                           //启动DMA开始发送数据                    }                    if(stage < 2L*SCREENCY*SCREENCX/DMA_BUFSIZE)                    {                             if(!(stage & 1))                             {                                      DMA_SPI_RXAL= (BYTE)&buffer2;           //设置DMA缓冲区起始地址                                      DMA_SPI_RXAH= (WORD)&buffer2 >> 8;                                      DMA_SPI_CR= 0xc1;                                     //启动DMA开始接收数据                             }                             else                             {                                      DMA_SPI_RXAL= (BYTE)&buffer1;           //设置DMA缓冲区起始地址                                      DMA_SPI_RXAH= (WORD)&buffer1 >> 8;                                      DMA_SPI_CR= 0xc1;                                     //启动DMA开始接收数据                             }                             stage++;                    }                    else                    {                             SPI_SS= 1;                    }          } } void delay_ms(WORD n) {          while(n--)                    delay_us(1000); } void delay_us(WORD n) {          while(n--)                                                                                     //每个循环24个时钟          {                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();                    _nop_();          } } void sys_init() {          WTST= 0x00;          CKCON= 0x00;          EAXFR= 1;          P0M0= 0x00; P0M1 = 0x00;          P1M0= 0x00; P1M1 = 0x00;          P2M0= 0x00; P2M1 = 0x00;          P3M0= 0x10; P3M1 = 0x00;          P4M0= 0x3c; P4M1 = 0x00;          P5M0= 0x00; P5M1 = 0x00;          P6M0= 0xff; P6M1 = 0x00;          P7M0= 0x00; P7M1 = 0x00;          TM0PS= 127;                                                                              //设置定时器0时钟预分频系统为128(127+1)          TMOD= 0x00;          T0x12= 0;          TL0= T2S;                                                                                   //设置定时器0的定时周期为2秒          TH0= T2S >> 8;          TR0= 1;          ET0= 1;          EA= 1;          SPI_S0= 1;                                                                                   //P2.2(SS_2)/P2.3(MOSI_2)/P2.4(MISO_2)/P2.5(SCLK_2)          SPI_S1= 0;          SPI_SS= 1;          SPCTL= 0xd0;                                                                             //初始化SPI模块(主模式,CPHA=CPOL=0)          SPIF= 1;          f3s= 0;          image= 0; } void lcm_init() {          static BYTE code INIT[]      =                                                      //TFT彩屏初始化命令          {                    4,      0xcf,        0x00,0xc1, 0x30,                    5,      0xed,       0x64,0x03, 0x12, 0x81,                    4,      0xe8,       0x85,0x10, 0x7a,                    6,      0xcb,       0x39,0x2c, 0x00, 0x34, 0x02,                    2,      0xf7,        0x20,                    3,      0xea,       0x00,0x00,                    2,      0xc0,       0x1b,                    2,      0xc1,       0x01,                    3,      0xc5,       0x30,0x30,                    2,      0xc7,       0xb7,                    2,      0x36,       0x28,                    2,      0x3a,       0x55,                    3,      0xb1,       0x00,0x1a,                    3,      0xb6,       0x0a,0xa2,                    2,      0xf2,        0x00,                    2,      0x26,       0x01,                    16,    0xe0,       0x0f,0x2a, 0x28, 0x08, 0x0e, 0x08, 0x54,                              0xa9,       0x43, 0x0a, 0x0f, 0x00, 0x00, 0x00, 0x00,                    16,    0xe1,       0x00,0x15, 0x17, 0x07, 0x11, 0x06, 0x2b,                             0x56,       0x3c, 0x05, 0x10, 0x0f, 0x3f, 0x3f, 0x0f,                    5,      0x2b,       0x00,0x00, HIBYTE(SCREENCY-1), LOBYTE(SCREENCY-1),                    5,      0x2a,       0x00,0x00, HIBYTE(SCREENCX-1), LOBYTE(SCREENCX-1),                    1,      0x11,                     0          };          BYTE i, j;          LCM_DB= 0xff;          LCM_RS= 1;          LCM_CS= 1;          LCM_WR= 1;          LCM_RD= 1;          LCM_RST= 0;                                                                             //复位TFT彩屏          delay_ms(50);          LCM_RST= 1;          delay_ms(50);          i= 0;          while(INIT)          {                    j= INIT[i++] - 1;                    lcm_write_cmd(INIT[i++]);                    while(j--)                    {                             lcm_write_dat(INIT[i++]);                    }          }          lcm_clear_screen();          lcm_write_cmd(0x29);                                                                  //打开显示 } void lcm_write_cmd(BYTE cmd)                                                          //写命令到彩屏 {          LCM_RS= 0;          LCM_CS= 0;          LCM_WRDB(cmd);          LCM_CS= 1; } void lcm_write_dat(BYTE dat)                                                              //写8位数据到彩屏 {          LCM_RS= 1;          LCM_CS= 0;          LCM_WRDB(dat);          LCM_CS= 1; } void lcm_write_dat2(WORD dat)                                                          //写16位数据到彩屏 {          LCM_RS= 1;          LCM_CS= 0;          LCM_WRDB(dat>> 8);          LCM_WRDB(dat);          LCM_CS= 1; } BYTE spi_shift(BYTE dat)                                                                    //使用SPI读写FLASH数据 {          SPIF= 1;          SPDAT= dat;          while(!SPIF);          returnSPDAT; } void lcm_set_window(int x0, int x1,int y0, int y1)                               //在TFT彩屏中定义窗口大小 {          lcm_write_cmd(0x2a);          lcm_write_dat2(x0);          lcm_write_dat2(x1);          lcm_write_cmd(0x2b);          lcm_write_dat2(y0);          lcm_write_dat2(y1); } void lcm_clear_screen()                                                                         //清屏(使用白色填充全屏) {          int i, j;          lcm_set_window(0,SCREENCX - 1, 0, SCREENCY - 1);          lcm_write_cmd(0x2c);                                                                   //设置写显示数据命令          LCM_RS= 1;          LCM_CS= 0;          for(i=SCREENCY; i; i--)          {                    for(j=SCREENCX; j; j--)                    {                             LCM_WRDB(WHITE>> 8);                             LCM_WRDB(WHITE);                    }          }          LCM_CS= 1; } void lcm_show_next_image() {          DWORD addr;          switch(image++)                                                                          //获取图片在Flash中的起始地址          {          default:      image = 1;          case0:       addr = IMG1_ADDR; break;          case1:       addr = IMG2_ADDR; break;          case2:       addr = IMG3_ADDR; break;          }          SPI_SS= 0;          spi_shift(0x03);                                                                             //发送读取FLASH数据命令          spi_shift((BYTE)(addr>> 16));                                                   //设置目标地址          spi_shift((BYTE)(addr>> 8));          spi_shift((BYTE)(addr));          lcm_set_window(0,SCREENCX - 1, 0, SCREENCY - 1);          lcm_write_cmd(0x2c);                                                                   //设置写显示数据命令          LCM_RS= 1;          LCM_CS= 0;          LCMIFCFG= 0x04;                                                                    //设置LCM接口为8位数据位,I8080接口,数据口为P6          LCMIFCFG2= 0x09;                                                                  //配置LCM时序          LCMIFSTA= 0x00;                                                                     //清除LCM状态          LCMIFCR= 0x80;                                                                       //使能LCM接口          DMA_LCM_CFG= 0x80;                                                           //使能LCM发送DMA功能,使能DMA中断          DMA_LCM_STA= 0x00;                                                             //清除DMA状态          DMA_LCM_AMT= (DMA_BUFSIZE - 1);                              //设置DMA传输数据长度          DMA_LCM_AMTH= (DMA_BUFSIZE - 1) >> 8;          DMA_SPI_CFG= 0xa0;                                                             //使能SPI接收DMA功能,使能DMA中断          DMA_SPI_STA= 0x00;                                                               //清除DMA状态          DMA_SPI_AMT= (DMA_BUFSIZE - 1);                                 //设置DMA传输数据长度          DMA_SPI_AMTH= (DMA_BUFSIZE - 1) >> 8;          DMA_SPI_CFG2= 0x00;                                                           //DMA传输过程中不控制SS脚          DMA_SPI_RXAL= (BYTE)&buffer1;                                       //设置DMA缓冲区起始地址          DMA_SPI_RXAH= (WORD)&buffer1 >> 8;          DMA_SPI_CR= 0xc1;                                                                 //启动DMA开始接收数据          stage= 0; } //文件:ISR.ASM //中断号大于31的中断,需要进行中断入口地址重映射处理                             CSEG     AT       018BH                                            ;SPIDMA_VECTOR                             JMP                    SPIDMA_ISR                             CSEG     AT       01D3H                                            ;LCMDMA_VECTOR                             JMP                    LCMDMA_ISR SPIDMA_ISR: LCMDMA_ISR:                             JMP                    006BH                             END  
 
 
 |