神农鼎 发表于 2023-5-19 21:33:15

视频讲解:使用SPI_DMA读外部Flash+TFT_DMA双缓冲对TFT刷屏,不占CPU时间

视频讲解:使用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读取数据到缓冲区12、上一步的SPI_DMA完成后,启动LCM_DMA将缓冲区1的数据送彩屏,同时SPI_DMA从FLASH读取数据到缓冲区23、上一步的SPI_DMA完成后,启动LCM_DMA将缓冲区2的数据送彩屏,同时SPI_DMA从FLASH读取数据到缓冲区4、重复步骤2和步骤3本测试代码在实验箱9.4B上测试通过。使用DMA中断加双缓冲可极大提高CPU效率,实测数据如下:
DMA缓冲区大小中断周期中断代码执行时间DMA中断处理程序的CPU占有率
3K7.5ms1.6us0.021%
2K5.0ms1.6us0.032%
1K2.49ms1.6us0.064%
5121.25ms1.6us0.128%
256610us1.6us0.262%
128310us1.6us0.516%
******************************************///#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= 1void 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,inty0, int y1);void lcm_clear_screen();void lcm_show_next_image();BYTE xdata buffer1;                                              //定义缓冲区BYTE xdata buffer2;                                              //注意:如果需要使用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) interrupt13{         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 - 1;                   lcm_write_cmd(INIT);                   while(j--)                   {                            lcm_write_dat(INIT);                   }         }         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_ISRSPIDMA_ISR:LCMDMA_ISR:                            JMP                  006BH                            END


芯LYS 发表于 2023-7-22 10:22:27

视频讲解:使用SPI_DMA读外部Flash+TFT_DMA双缓冲对TFT刷屏,不占CPU时间

https://www.stcaimcu.com/forum.p ... 3184&extra=page%3D1

STC32G12K128实验箱中的演示程序,第79个参考程序



深圳国芯人工智能有限公司-实验箱 (stcai.com)


神农鼎 发表于 2023-10-11 10:06:41

USB / CAN 专题免费教学会议通知:
【USB 原理及实战,16课时】10月/9号, 10月/11号; 10/16, 10/18;
【CAN 原理及实战,8课时】12月/18号, 12月/20号;
线上视频授课:周一下午/周三下午,14:00 ~ 17:00;
腾讯会议号:885-5858-2739; (安装腾讯会议软件后,输入会议号即可)
参会学习立即【免费+包邮送】USB核心功能实验板
参会学习立即【免费+包邮送】CAN核心功能实验板,模拟的CAN收发器您自己补上
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=4526&extra=&page=1
请帮忙转发给可能需要:从0开始了解USB和CAN的 同学/同事/老师/研发人员

@辉-无名 发表于 2023-12-20 22:19:41

SPI+DMA到底怎么用,我现在需要读取和写入分开,在CS拉低时先写入3个字节,再读取2个字节,再写入90个字节,再读取2个字节,然后把拉高,硬件SPI都可以实现,但是SPI+DMA就不行了,DMA不能单独写,或者单独读取吗?

神农鼎 发表于 2023-12-24 17:27:28

DMA 是连续 【1个字节 ~ N个字节】


视频教学《STC32位8051单片机原理及应用-STC32G12K128》,何宾教授主讲 - 软件库函数/原理图库/PCB库/最小包装 - 国芯论坛-STC全球32位8051爱好者互助交流社区 - STC全球32位8051爱好者互助交流社区 (stcaimcu.com)

QQ624353765 发表于 2024-1-20 18:35:15

@辉-无名 发表于 2023-12-20 22:19
SPI+DMA到底怎么用,我现在需要读取和写入分开,在CS拉低时先写入3个字节,再读取2个字节,再写入90个字节 ...

我也是发送地址+读取数据不能直接DMA实现,只能分开,发完地址CS拉高后在拉低才能接收数据,这不在加代码量吗
页: [1]
查看完整版本: 视频讲解:使用SPI_DMA读外部Flash+TFT_DMA双缓冲对TFT刷屏,不占CPU时间