AI8051U_V1.2实验箱,实验驱动TFT彩屏 | 最终程序见3楼的 DMA 驱动
基于AI8051U_V1.2实验箱_学习实验之驱动TFT彩屏===最终程序见 3楼的 DMA 驱动
这是将原来在32G12K128_V6.92实验箱运行的实验程序移植到新的AI8051U_V1.2实验箱上来了,
虽说是旧话重提、老调重弹,其实过程并非一帆风顺。
先看看实验结果小视频吧:
使用40MHz,流畅度感觉已经不错了。
本实验使用的是模拟IO模式驱动,尚未加入LCM和DMA技术。
本人认为,这是必须先这样做的,这是最基本的方法,可以有助于理解控制指令的时序,程序可移植性较强。主要是可以先验证一下硬件是否没有问题了。
这套彩屏驱动的函数源码,已包括:使用小字库、实现中西文字符串显示、BMP小图片显示、画线、画矩形、下拉菜单制作等实用函数源码,可用于完成一般的项目需求的。
主程序就不贴出来了。基本相同于:
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=8725
工程文件包供有兴趣的坛友下载参考、指正。
本帖最后由 浦江一水 于 2024-11-3 15:22 编辑
第二步,采用LCM模式的来了.
梳理一下,学习实验的思路:
LCM-i8080/M6800 是AI8051U单片机的特为
===驱动TFT/LCD等屏的硬件接口模式.
理论上比模拟IO方式速度快, 效果好.
实验改用LCM模式采取以下一些步骤:
① 在LCD9431.C源码文件中的开头部分,加入宏定义
#define USE_LCM1 //1:使用LCM模式 0:模拟IO模式
保留原来模拟IO方式的代码, 在调试时可切换,这样可防新代码出错调试不通而回不来.
② 先搞清楚所用的显示屏是哪种类型,接口引脚定义.
查数据手册, 配置LCM相关的寄存器,
在LCD_Init()函数中,增加LCM初始化配置语句:
#if (USE_LCM==1)
LCMIFCFG = 0x80; //bit7 1:Enable Interrupt, bit1 0:8bit mode; bit0 0:8080,1:6800 //选定显示屏接口类型//允许中断
LCMIFCFG2= 0x29;//RS:P45,RD:P37,WR:P36; Setup Time=2,HOLD Time=1 //配置切换到引脚(有限固定选择,不是随意的!)
LCMIFSTA = 0x00; //状态寄存器复零
EA = 1; //开总中断 (注意:LCM模式需要用到中断服务机制,必须开)
#endif
③ 在工程文件总加入 ISR.asm 文件,其内容为:(是用汇编语言编写的)
CSEG AT 01D3H ;如果代码段地址是01D3H
LJMP 006BH ;那么就长转移跳转到地址 006BH 代码段执行...
CSEG AT 01DBH ;如果代码段地址是01DBH
LJMP 006BH ;那么也就长转移跳转到地址 006BH 代码段执行...
END
为什么要加入这个文件?是因为根据AI8051U.H中的关于中断矢量表的定义:
//Interrupt Vector中断矢量表
......
#define USER_VECTOR 13 //006BH
......
#define DMA_LCM_VECTOR 58 //01D3H
#define LCM_VECTOR 59 //01DBH
......
可见:LCM或DMA所触发的中断矢量编号已经超出标准32号, 则要强制转到保留的用户中断编号13号中来处理...
④ 由于启用的中断的原因, 也就要增加一个中断服务函数:
void LCMIF_DMA_Interrupt(void) interrupt 13
{
if(LCMIFSTA & 0x01) //如果是LCM所引发的中断, 查状态寄存器的bit0为是否为1...
{
LCMIFSTA = 0x00; //状态寄存器复零
LCD_CS=1; //LCD的片选置1, 不片选了, 也表示数据已经由硬件发出了...
}
}
⑤使用LCM模式发送的主要语句是写命令和写数据两条: (其它语句不需要改编)
写命令字节函数:
LCMIFDATL = CMD; //将命令字节写入数据寄存器的字节(低8位)
LCD_CS=0; //LCD片选上...
LCMIFCR = 0x84; //Enable interface, write command out 触发中断写命令//控制寄存器的CMD[]位=100
while(!LCD_CS); //等待中断返回标志
写数据字节函数:
LCMIFDATL = CMD; //将命令字节写入数据寄存器的字节(低8位)
LCD_CS=0; //LCD片选上...
LCMIFCR = 0x85; //Enable interface, write command out 触发中断写命令//控制寄存器的CMD[]位=101
while(!LCD_CS); //等待中断返回标志
这样,原来模拟IO方式写命令或数据时, 要考虑的时序控制IO跳变, 现在只要将数据放入寄存器,其它由硬件去控制完成发送了。
因此效率提高了。
调试时可改写宏定义
#define USE_LCM0 //1:使用LCM模式 0:模拟IO模式
重新编译运行, 来观察比较两种模式的显示效果。
工程文件包在此,供有兴趣的坛友下载参考和指正。
本学习实验课题的移植到AI8051U实验箱来运行,
经过了三个步骤,在此前的基础上又增加了DMA手段。
从视频中可见, 程序稍有小改动,增加了32*32小图标的显示, 是实验用DMA方式显示BMP图片的方法.
由于本课题除了清屏和图标显示以外,没有大量数据传输的需求,因此DMA手段仅仅用在这两个函数上。
稍稍添加注释解析,以供初学、同学者参考理解。
//清屏函数
void LCD_CLS(u8 Color)
{ u16 i,j;
LCD_SetWindow(0,0,LCD_W-1,LCD_H-1);
#if (USE_DMA==1) //若使用DMA方式
j=0;
for(i=0;i<=DMA_AMT_LEN;i++)LCD_Buf= COLOR; //先向缓存区填颜色字
DMA_TxCount = 75; //确定发送次数: 总字节数/缓存区大小=循环次数 (320*240*2) /2048=75
LCD_CS=0; //显示屏片选中,让DMA向显示屏发送数据...
DMAFlag=0; //设DMA完成标志初为0
DMA_LCM_CR = 0xA0; //(Write dat 写数据) 使能LCM_DMA / 启动发送数据模式操作 / 引发中断
while(!DMAFlag); //等待中断操作完成...
LCD_CS=1; //片选中,让DMA向显示屏发送数据...
#else
for(i=0;i<LCD_W;i++) //不使用DMA方式的原写数据循环方式
{ for (j=0;j<LCD_H;j++)
{ LCD_WR_DATA(COLOR); }
}
#endif
}
//使用DMA方式的BMP图标显示函数
#if (USE_DMA==1)
/*****************************************************************************
* 函数:void LCD_BMP_DMA(u16 X, u16 Y,u16 W,u16 H,u8 Color) //显示图片
* 参数: X起点 Y起点 W宽度 H高度 Color: 色号
* 注意:因目前缓存区LCD_Buf[]为2048字节,故仅仅支持32*32图标DMA模式显示
* 若缓存区扩为4608字节,可显示48*48图标
* 若缓存区扩为8192字节,可显示64*64图标 (算法暂留,有待完善)
******************************************************************************/
void LCD_BMP_DMA(u16 X,u16 Y,u8 W,u8 H,u8* pic)
{u16 i,Col; u8 h,l;
LCD_SetWindow(X,Y,X+W-1,Y+H-1); //设置显示窗口
for(i=0;i<W*H; i++)
{ l=pic; h=pic;//BMP图像数据:低位在前/高位在后
Col=256*h+l; //转换为: 高位在前/低位在后
LCD_Buf = Col; //先送入缓存区
}
//DMA_TxCount=W*H*2/(DMA_AMT_LEN+1); //计算发送次数:总字节数/缓存区大小=循环次数.图标(32*32*2)/2048=1
DMA_TxCount=1; //计算发送次数:总字节数/缓存区大小=循环次数. 图标(32*32*2)/2048=1
LCD_CS=0; //显示屏片选中,让DMA向显示屏发送数据...
DMAFlag=0; //设DMA完成标志初为0
DMA_LCM_CR = 0xA0; //(Write dat 写数据) 使能LCM_DMA / 启动发送数据模式操作 / 引发中断
while(!DMAFlag); //等待中断操作完成...
LCD_CS=1; //片选中,让DMA向显示屏发送数据...
LCD_SetWindow(0,0,LCD_W,LCD_H); //恢复全屏显示区域
}
#endif
这两函数的区别在于:
清屏函数在一次性初始化发送缓存区数据后, 启动DMA发送要循环75次才完成全屏一色. 每次循环不必重载缓存区数据, 所以期间不需要MCU参与.
而显示BMP函数, 如果图像数据量大于缓存的话, 即要分多次循环的话, 那么在期间要重载新数据, 则需要MCU参与, 所以先不考虑大图片, 否则就失去使用DMA的意义了.
(虽说有双缓存的手段,可开两个缓存,启用两个DMA, 交替使用, 但目前看是用于从外部Flash存储器读取数据的. 故暂不作深入考虑.)
AI8051U实验箱还有丰富的新知识点, 有待开发挖掘、学习研究.
至此,初篇移植作业暂告段落, 还有提升完善的空间, 日后再换课题: 比如图片数据放在外部QSPI_Flash中, 再做全屏图片的显示实验.
工程文件包, 供有兴趣的爱好者参考和指正:
本帖最后由 浦江一水 于 2024-11-3 15:25 编辑
感谢 autopccopy 版主鲜花鼓励。
感谢 神农鼎管理员鲜花鼓励。
两位都5朵鲜花支持,太给力、厚爱了。
浦江一水 发表于 2024-10-31 22:14
第二步,采用LCM模式的来了.
梳理一下,学习实验的思路:
请教下,这个ISR.asm文件是不是没生效呢。
===因为代码中用的是interrupt 13
而不是interrupt DMA_LCM_VECTOR
ercircle 发表于 2024-11-18 00:26
请教下,这个ISR.asm文件是不是没生效呢。
===因为代码中用的是interrupt 13
感谢这位坛友对本贴的关注、浏览和留言。
关于所提的问题, 我是这样理解的:
程序中用了: interrupt13
这是等价于 : interrupt USER_VECTOR
DMA_LCM 所触发的中断是DMA_LCM_VECTOR
根据Ai8051U.H中的中断矢量表来看:
USER_VECTOR 定义的中断序号 13 其入口地址是 006BH
DMA_LCM_VECTOR定义的中断序号 58 其入口地址是 01D3H
那么DMA_LCM触发的中断如何转到USER_VECTOR来执行中断服务程序呢?
再看:ISR.asm中的代码...
CSEG AT 01D3H
LJMP 006BH
CSEG AT 01DBH
LJMP 006BH
END
意思是:一旦中断入口到地址 01D3H那就长转移到地址 006BH 之处去执行...
也就是转移到了 interrupt13 去执行了。
这说明ISR.asm 中的代码还是起作用、有效的。
那么,为什么不直接用interrupt DMA_LCM_VECTOR呢?
因为 DMA_LCM_VECTOR 中断序号是 58,是扩展号, 超出传统的 32 的范畴,
通过ISR.asm 这是一种兼容传统8051的手段。
而 Ai8051U是新型强大的CPU,不同于传统的8051,
程序一开始就加了EAXFR = 1; //扩展寄存器(XFR)访问使能
估计还会有新的手段变化。
有待进一步学习、体会...
谢谢。
本帖最后由 浦江一水 于 2024-11-26 07:26 编辑
无意间重复了。
本帖最后由 浦江一水 于 2024-11-26 07:26 编辑
谢谢。
感谢回复,那应该是对中断DMA_LCM_VECTOR的寻址是芯片内部的事,流程上:
1.用户代码LCMIFCFG= LCMIFIE 开中断
2.芯片内部寻址DMA_LCM_VECTOR
3.汇编映射DMA_LCM_VECTOR到USER_VECTOR
4.通过USER_VECTOR跳转到interrupt USER_VECTOR对应的函数
上面是8bit,32bit使用拓展中断号之后应该直接用DMA_LCM_VECTOR可以省去汇编映射而且不占用USER_VECTOR。
屏还没到手,等到了验证下对不对。 ercircle 发表于 2024-11-18 10:51
感谢回复,那应该是对中断DMA_LCM_VECTOR的寻址是芯片内部的事,流程上:
1.用户代码LCMIFCFG= LCMIFIE...
好像在哪里见到过,直接用DMA_LCM_VECTOR的,或可省去ISR.asm汇编文件参与。
不过这个转换手段也给人编程思路方面有一点启示,有点意思,也不算啥麻烦事。
目前看到的很多代码都在这样用ISR.asm,包括官方例程。
页:
[1]
2