【已解决】STC8A8K64D4如何正确通过DMA方式在ILI9325显示图片?
本帖最后由 kundi 于 2021-8-6 22:19 编辑DMA方式,是一种不经过CPU的快速批量传输数据的方式。而STC官方网站上的相关例程中,关于TFT专用接口和DMA方式驱动TFT屏的,只有指定颜色写入刷屏与读取LCD的ID的程序,但是却没有通过DMA方式显示一张图片的大面积改变TFT显示区域的程序(少量字符可不考虑)。我就想参考其他程序,写一个DMA方式显示一个图片的程序。目前,STC8A8K64D4的DMA,支持XRAM与LCM之间数据的交换。
现在问题来了,下面第二个程序就是我已经写好的DMA方式显示图片的程序,发现显示的时候图片糊了,会不会是每批次写入XRAM的时间大于DMA传送的时间,由于使用了中断,XRAM内容没有更行完就被打断并传输给TFT LCD了,就会使得发送顺序混乱的错误的内容,图片就糊了?单色刷屏可能是因为每批次写入XRAM的时间小于等于DMA传送的时间而没有出错。
关键部分程序如下:
1.例程中的DMA方式刷屏的函数
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{
u16 i,j;
u16 width=ex-sx+1; //得到填充的宽度
u16 height=ey-sy+1; //高度
LCD_SetWindows(sx,sy,ex,ey);//设置显示窗口
#if USR_LCM_IF == 1
for(j=0,i=0;i<256;i++)//一次往XRAM写入256个16bit数据
{
Color = color;
}
index = 300;//总共传输300个批次,因为300*256=320*240=76800(像素)
LCD_CS=0;
LCMIF_BMM_CR = 0xa0; //Write dat
// P40 = 0;
#else//不使用DMA的传统刷频方式
// P40 = 0;
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
LCD_WR_DATA_16Bit(color); //写入数据
}
// P40 = 1;
#endif
}2.我目前根据其他程序,写的DMA方式传输数据达到显示图片的目的,就是上边问题的部分(此部分正解见2,3楼)。
void Gui_Drawbmp16(u16 x,u16 y,const unsigned char *p) //显示40*40 QQ图片
{
unsigned int i,k;
unsigned char picH,picL;
LCD_SetWindows(x,y,x+40-1,y+40-1);//窗口设置
#if USR_LCM_IF == 1//有问题的方式,图片糊了
k=0;
while(k<40)
{
for(i=0;i<40;i++)//一次写40个
{
picL=*(p+j*2); //数据低位在前
picH=*(p+j*2+1);
Color = (picH<<8)|picL;
j++;
}
k++;
index = 40;//传40批。40*40=1600像素
LCD_CS = 0;
LCMIF_BMM_CR = 0xa0; //Write dat
}
#else//这是传统的方式,显示是正常的。
for(i=0;i<40*40;i++)
{
picL=*(p+i*2); //数据低位在前
picH=*(p+i*2+1);
LCD_WR_DATA_16Bit(picH<<8|picL);
}
#endif
LCD_SetWindows(0,0,LCD_W,LCD_H);//恢复显示窗口为全屏
}3.Test——Color在大循环中调用
void Test_Color(void)
{
u16 lcd_id;
u8 buf = {0};
// P40 = 0;
LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
// P40 = 1;
while(!LCD_CS);
LCMIF_BMM_AMT = 0x01; //Exe 2(n+1) bytes
lcd_id = LCD_Read_ID();
sprintf((char *)buf,"ID:0x%x",lcd_id);
Show_Str(50,25,BLUE,YELLOW,buf,16,1);
LCMIF_BMM_AMT = 0xff; //Exe 256(n+1) bytes,DMA单次传输的个数为(255+1)*2字节
delay_ms(800);
// P40 = 0;
LCD_Fill(0,0,LCD_W,LCD_H,RED);
// P40 = 1;
delay_ms(800);
// P40 = 0;
LCD_Fill(0,0,LCD_W,LCD_H,GREEN);
// P40 = 1;
delay_ms(800);
// P40 = 0;
LCD_Fill(0,0,LCD_W,LCD_H,BLUE);
// P40 = 1;
delay_ms(800);
LCMIF_BMM_AMT = 39; //Exe 39(n+1) bytes,设置DMA单次传输的个数为(39+1)*2字节
j=0;
Gui_Drawbmp16(30,30,gImage_qq);//显示40*40图片
j=0;
delay_ms(1000);
LCMIF_BMM_AMT = 0xff; //Exe 256(n+1) bytes
}
4.DMA的中断服务程序
void LCMIF_BMM_Interrupt(void) interrupt 13
{
if(LCMIFSTA & 0x01)
{
LCMIFSTA = 0x00;
LcmFlag = 0;
}
if(LCMIF_BMM_STA & 0x01)
{
if(BmmFlag)
{
BmmFlag = 0;
LCMIF_BMM_CR = 0;
}
else
{
index--;
if(index == 0)
{
LCMIF_BMM_CR = 0;
LCD_CS=1;
// P40 = 1;
}
else
{
LCMIF_BMM_CR = 0xa0; //Write dat
}
}
LCMIF_BMM_STA = 0;
}
}更改后的结果和说明见2-3楼,这里附完整的代码文件于附件中,希望STC官方可在后续示例程序中参考一下我的成果。
本帖最后由 kundi 于 2021-8-6 15:45 编辑
基本成功了,只是最后一个被发送的像素是黑点。
程序如下:
void Gui_Drawbmp16(u16 x,u16 y,const unsigned char *p) //显示40*40 QQ图片
{
unsigned int i,k;
unsigned char picH,picL;
LCD_SetWindows(x,y,x+40-1,y+40-1);//窗口设置
#if USR_LCM_IF == 1
k=0;
index = 40;//放到外层循环的外面
while(k<40)
{
LCMIF_BMM_CFG =0x02;//使得DMA 的LCM中段允许位变0
LCMIFCFG = 0x02;//使得LCM接口的中断允许位是0,程序写入XRAM之前这两个中断要禁用,然后恢复。
for(i=0;i<40;i++)
{
picL=*(p+j*2); //数据低位在前
picH=*(p+j*2+1);
Color = (picH<<8)|picL;
j++;
}
k++;
LCD_CS = 0;
LCMIFCFG = 0x82;//恢复这2个中断的允许
LCMIF_BMM_CFG= 0x82;
LCMIF_BMM_CR = 0xa0; //Write dat
}
#else
for(i=0;i<40*40;i++)
{
picL=*(p+i*2); //数据低位在前
picH=*(p+i*2+1);
LCD_WR_DATA_16Bit(picH<<8|picL);
}
#endif
LCD_SetWindows(0,0,LCD_W,LCD_H);//恢复显示窗口为全屏
} 本帖最后由 kundi 于 2021-8-6 22:21 编辑
测试了大的图片,尺寸是160*160。就没有发现最末尾像素是黑点,看来DMA在大数据量时比较合适。
测试过程中,图片用DMA方式传输都比用传统方式快,特别是在主频升到45MHZ时(45MHZ时,如果电压偏低可能读取LCM数据会容易出错)。
此测试图片之版权归其作者所有。
代码:(调用此函数前要把LCMIF_BMM_AMT的值改为159)
最终更改后的程序代码文件在1楼。 不错,学习了。DMA都是在ARM上用,没想到STC也可以。 牛逼!学习一下! 好东西,学习了! 谢谢楼主的分享~ 45MHZ下,DMA刷屏演示,见压缩包内视频文件。
比较遗憾的是不支持SPI到LCM的DMA操作,需要在XRAM中转,速度提升有限。 LCD外设 规格是啥?
每个商家都自定义一套。。。。。有没有通用的? zzsczz 发表于 2021-11-15 18:29
LCD外设 规格是啥?
对于8/16位的并行口,STC8A8K64D4内部的LCM接口是可以设置接口模式的,包括8080/68000。
SPI接口的LCM话,就用不到这个LCM接口了,毕竟SPI也有DMA. 可以从spi flash dma 搬运到lcd吗
好像只是搬运小图片有用 320x240x2 = 153kb
51单片机无论如何也放不下一个framebuffer
页:
[1]