浦江一水 发表于 2024-10-31 22:14:03

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-10-31 22:14:42

本帖最后由 浦江一水 于 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模式
      重新编译运行, 来观察比较两种模式的显示效果。

工程文件包在此,供有兴趣的坛友下载参考和指正。




浦江一水 发表于 2024-10-31 22:15:04

本学习实验课题的移植到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-2 09:37:53

本帖最后由 浦江一水 于 2024-11-3 15:25 编辑

感谢 autopccopy 版主鲜花鼓励。
感谢 神农鼎管理员鲜花鼓励。
两位都5朵鲜花支持,太给力、厚爱了。

ercircle 发表于 2024-11-18 00:26:33

浦江一水 发表于 2024-10-31 22:14
第二步,采用LCM模式的来了.

梳理一下,学习实验的思路:

请教下,这个ISR.asm文件是不是没生效呢。

===因为代码中用的是interrupt 13
       而不是interrupt DMA_LCM_VECTOR

浦江一水 发表于 2024-11-18 09:33:58

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-18 09:33:58

本帖最后由 浦江一水 于 2024-11-26 07:26 编辑


无意间重复了。

浦江一水 发表于 2024-11-18 09:35:51

本帖最后由 浦江一水 于 2024-11-26 07:26 编辑


谢谢。

ercircle 发表于 2024-11-18 10:51:28

感谢回复,那应该是对中断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。
屏还没到手,等到了验证下对不对。

浦江一水 发表于 2024-11-18 12:27:34

ercircle 发表于 2024-11-18 10:51
感谢回复,那应该是对中断DMA_LCM_VECTOR的寻址是芯片内部的事,流程上:
1.用户代码LCMIFCFG= LCMIFIE...

好像在哪里见到过,直接用DMA_LCM_VECTOR的,或可省去ISR.asm汇编文件参与。
不过这个转换手段也给人编程思路方面有一点启示,有点意思,也不算啥麻烦事。
目前看到的很多代码都在这样用ISR.asm,包括官方例程。

页: [1] 2
查看完整版本: AI8051U_V1.2实验箱,实验驱动TFT彩屏 | 最终程序见3楼的 DMA 驱动