[X]关闭

[米联客-XILINX-H3_CZ08_7100] LINUX应用篇连载-03 液晶屏触控

文档创建者:LINUX课程
浏览次数:388
最后更新:2024-09-10
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 2_LINUX应用开发
本帖最后由 LINUX课程 于 2024-9-11 11:13 编辑

软件版本:vitis2021.1(vivado2021.1)
操作系统:WIN10 64bit
硬件平台:适用XILINX Z7/ZU系列FPGA
登录“米联客”FPGA社区-www.uisrc.com视频课程、答疑解惑!

1 简介
本章节实现了一个带触控的lcd显示器。实现的功能有framebuffer和lcd触控,输出画面依靠vdma进行显示,在此之上编写qt应用程序实现简单的触控功能。注意此处的触控指的是实现input子系统,而非系统界面可触控。
2 系统框图
image.jpg
3 方案介绍
本方案用到了三个驱动,分别是i2c驱动用于触控功能,vdma驱动用于画面输出,pwm驱动用于屏幕背光亮度的控制。
  • 显示屏使用了gt9157芯片,可以通过i2c来进行数据交互,需要作为input子系统来使用。
  • vdma驱动需要实现framebuffer功能。
上述驱动完成后,qt程序可以直接使用pwm的class来控制。
  • class的控制方法位于/sys/class/backlight/backlight/brightness。
  • 实现触控按钮功能。
4 方案搭建
4.1 vivado工程解析
此处不对vivado工程的搭建作详细解释,若想查看vivado搭建请移步FPGA部分课程。接下来将从Linux开发的角度分析vivado内需要注意的几个地方。
1:BD图
image.jpg
看到右侧部分的管脚:
lcd_*:LCD屏幕显示接口
lcd_pwm:背光亮度pwm接口(本demo无效)
gpio_rtl:触控的控制管脚
lcd_iic:触控芯片与Linux通信
image.jpg
2:查看寄存器地址
此处可以看到需要用到的寄存器地址,vitis导出的设备树会自动生成,所以看一下就行。
4.2 设备树解析
非常不建议初学者自己修改设备树,请使用demo中自带的设备树操作,若能理解设备树的含义与作用可自行修改。
1:vitis生成pl.dtsi
PWM_0节点需要修改,其他节点都无需修改:
通过vitis生成的设备树如下:
image.jpg
需要修改为下图:
image.jpg
该设备树对应的驱动位于uisrc-lab-xlnx/sources/kernel/drivers/pwm/tcpwm.c。相较之前,设备树的修改主要有:
  • 设备树节点名称的变更。
  • compatible的修改,此处用于驱动匹配。必须修改,否则无法正常显示。
  • 添加pwm-cells的参数。
2:添加LCD显示的节点
image.jpg
该设备树对应的驱动位于/uisrc-lab-xlnx/sources/kernel/drivers/video/fbdev/vdmafb.c。dmas需要设置一下vdma的地址,具体看上方的axi_vdma_0节点。由于本工程使用了3缓存VDMA所以park必须设置为1。
3:添加触控的节点
image.jpg
该设备树对应的驱动位于/uisrc-lab-xlnx/sources/kernel/drivers/input/touchscreen/gt9xx。直接添加在根节点下,可与zynqmp-mzux.dts 文件中的i2c0节点共存。
4:添加背光节点
image.jpg
添加在/节点下,作用是设定背光等级,依次是从暗至明。(本demo无效)
4.3 构建系统
1:从vivado到vitis生成所需文件
若不清楚如何搭建,请复习Linux基础篇内第四章的内容,此处不再赘述。若想快速验证,在本课对应的demo中有工程已编译好可直接使用。
image.jpg
boot:启动文件,使用boot文件替换boot分区,即可搭配任意文件系统运行程序。
QT:pc端的qt接收与展示波形工程,文件夹内有两个文件,一个QT工程的文件夹为QT的工程,另一个qt_play.tar.gz的文件可解压到文件系统内,待系统启动时直接运行。
soc_dts、soc_hw、soc_prj、soc_sdk:移植系统所需工程,分别为设备树、硬件描述文件、vivado工程和vitis工程。若还不熟悉这些文件,请先学习Linux基础第四章的内容。
2:拷贝文件
本方案使用的开发包版本为uisrc-lab-xlnx20220601.tar.gz,请确保正在使用的版本一致。若不清楚或不一致,请前往https://www.uisrc.com/t-3268.html下载。
以往的开发包,需要手动设置好5个文件,分别是fsbl.elf、system_wrapper.bit和kernel、uboot的设备树(设备树在demo中已经提供)。在新版本的开发包中可以无须手动改名,放置到指定路径即可。若不清楚本段的内容表述,请先重复Linux基础篇内第四章的内容,熟悉基础步骤。
image.jpg
将四个文件放置到/uisrc-lab-xlnx/boards/mz7x/ubuntu/output/files指定位置。其中fsbl.elf、system_wrapper.bit两个文件直接放在目录下,而kernel和uboot的设备树要放在对应的文件夹内。注意kernel-dts文件夹内的设备树支持include,所以可以放入多个文件,但是uboot-dts内设备树不支持include,只能支持名为zynq-mz7x.dts的设备树。
3:修改驱动文件
需要修改的驱动文件位于/home/uisrc/uisrc-lab-xlnx/sources/kernel/drivers/video/fbdev/vdmafb.c
原有驱动设置:
image.jpg
修改分辨率后:
1725965734831.jpg
注意:在做其他章节实验时,需要把此处改回原样。
4:构建所需系统
首先source环境变量,先前往以下路径/uisrc-lab-xlnx/scripts:
image.jpg
运行命令:
source mz7xcfg.sh                        用来设置环境变量
move_files.sh                                将刚才我们拷贝的文件重命名并拷贝到对应的位置
make_uboot.sh                        编译uboot
make_kernel.sh                        编译kernel
create_image.sh                        生成启动文件
紧接着插入sd卡,并连接到虚拟机内,继续输入命令:
make_parted.sh                        给sd卡分区,先输入sdb,再输入y
deploy_image.sh                        烧录系统
7100 SD卡启动,参考第一章第5节。
4:交叉编译qt
交叉编译需要使用交叉编译工具包,这个工具包是独立于系统开发包的另外一个东西,本方案使用的版本为:uisrc-lab-qt20220601.tar.gz。若没有此开发包,请前往https://www.uisrc.com/t-3268.html下载。
将uisrc-lab-qt20220601.tar.gz拷贝到虚拟机内/home/uisrc路径下,使用tar zxvf uisrc-lab-qt20220601.tar.gz命令解压,一定要确保解压在/home/uisrc路径下,否则会出现错误。
进入/uisrc-lab-qt/scripts路径,右键在此处打开终端,输入命令:
source qt_cfg.sh                        设置环境变量
image.jpg
进入/uisrc-lab-qt/applications路径,把本方案demo自带的qt demo拷贝进来,位于demo文件夹/QT/QT工程/mp_test:
image.jpg
将HelloQt中的build_arm_app.sh复制一份到mp_test中去:
image.jpg
打开刚才source好的终端,输入命令:
cd ../applications/mp_test/                cd到指定路径
./build_arm_app.sh                                编译qt工程
此文件就是编译出来的文件:
image.jpg
将编译好的文件和demo中qt工程内附带的1.jpg拷贝到/uisrc-lab-qt/applications/qt_play中:
image.jpg
修改该文件夹下,run_arm_app.sh文件的内容,使用记事本打开该文件,将最后一行的名称改为我们编译出来文件的名称:
image.jpg
将qt_play整个文件夹拷贝到/media/uisrc/rootfs/home/uisrc里,这步的目的是将做好的程序放到文件系统内,开发板上电登录后即可运行:
image.jpg
弹出sd卡:
1725965899157.jpg
7100FC 系统烧录部分参考第一章第5节。从qspi启动sd卡。
5 驱动应用分析
5.1 pwm驱动
驱动位于/uisrc-lab-xlnx/sources/kernel/drivers/pwm/tcpwm.c
1:平台驱动
  1. static struct platform_driver tcpwm_fun_device_driver = {
  2. .probe = tcpwm_fun_probe,
  3. .remove = tcpwm_fun_remove,
  4. .driver = {
  5.   .name = "tcpwm_func",
  6.   .owner = THIS_MODULE,
  7.   .of_match_table = of_match_ptr(tcpwm_fun_of_match),
  8. }};
复制代码
从平台驱动结构体,我们可以看到tcpwm_fun_probe为平台驱动注册函数,tcpwm_fun_remove为平台驱动注销函数,从tcpwm_fun_of_match可以找到驱动匹配名称,我们下节来看。
2:match函数
  1. static struct of_device_id tcpwm_fun_of_match[] = {
  2. {
  3.   .compatible = "xlnx,PWM-1.0",
  4. },
  5. {},
  6. };
复制代码
compatible字段展示了设备树匹配名称为xlnx,PWM-1.0,这也就是为什么在4.2.2.1中需要修改设备树的compatible字段。
3:平台驱动注册函数
  1. static int tcpwm_fun_probe(struct platform_device *pdev)
  2. {
  3. struct device *dev = &pdev->dev;
  4. struct msxbo_pwm_chip *pc = dev_get_platdata(dev);
  5. struct resource *res;
  6. void __iomem *base;
  7. int ret = 0;

  8. if (!pc)
  9. {
  10.   pc = devm_kzalloc(dev, sizeof(struct msxbo_pwm_chip), GFP_KERNEL);
  11.   if (!pc)
  12.    return -ENOMEM;

  13.   platform_set_drvdata(pdev, pc);
  14. }
  15. #if 1
  16. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  17. base = devm_ioremap_resource(dev, res);
  18. if (IS_ERR(base))
  19.   return PTR_ERR(base);

  20. pc->pwm_regmap = devm_regmap_init_mmio(dev, base,
  21.              &pwm_regmap_config);
  22. #endif

  23. pc->chip.dev = &pdev->dev;
  24. pc->chip.ops = &msxbo_pwm_ops;
  25. pc->chip.of_xlate = of_pwm_xlate_with_flags;
  26. pc->chip.of_pwm_n_cells = 3;
  27. pc->chip.base = -1;
  28. pc->chip.npwm = 1;

  29. ret = pwmchip_add(&pc->chip);
  30. if (ret < 0)
  31. {
  32.   dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
  33.   return ret;
  34. }

  35. regmap_write(pc->pwm_regmap, PWM_AXI_PERIOD_REG_OFFSET, PERIOD_CLOCK_NUM);
  36. pm_runtime_enable(&pdev->dev);

  37. return ret;
  38. }
复制代码
这个函数主要是用来向内核注册pwm设备。
行9~16,首先为需要注册的结构体申请一段内存空间。
行18,从设备树获取相关资源。
行19~21,申请并映射出基地址。
行23~24,映射寄存器地址。
行27~32,初始化注册pwm结构体的各项配置。
行34,注册pwm设备。
3:pwm操作集合
  1. static const struct pwm_ops msxbo_pwm_ops = {
  2. .config = msxbo_pwm_config,
  3. .enable = msxbo_pwm_enable,
  4. .disable = msxbo_pwm_disable,
  5. .owner = THIS_MODULE,
  6. };
复制代码
行2,设置pwm占空比。
行3,使能pwm。
行4,关闭pwm。
5.2 vdmafb驱动
驱动位于/uisrc-lab-xlnx/sources/kernel/drivers/video/fbdev/vdmafb.c
1:平台驱动
  1. static struct platform_driver vdmafb_driver = {
  2.     .probe = vdmafb_probe,
  3.     .remove = vdmafb_remove,
  4.     .driver = {
  5.         .name = "vdmafb_fb",
  6.         .of_match_table = vdmafb_match,
  7.     }};
复制代码
行2,驱动注册函数。
行3,驱动注销函数。
行6,设备树匹配函数。
2:设备树匹配
  1. static struct of_device_id vdmafb_match[] = {
  2.     {
  3.         .compatible = "topic,vdma-fb",
  4.     },
  5.     {},
  6. };
复制代码
对应了4.2.2中对设备树的修改。
3:驱动注册
  1. static int vdmafb_probe(struct platform_device *pdev)
  2. {
  3.         int ret = 0;
  4.         struct vdmafb_dev *fbdev;
  5.         int fbsize;

  6.         fbdev = devm_kzalloc(&pdev->dev, sizeof(*fbdev), GFP_KERNEL);
  7.         if (!fbdev)
  8.                 return -ENOMEM;

  9.         platform_set_drvdata(pdev, fbdev);

  10.         fbdev->info.fbops = &vdmafb_ops;
  11.         fbdev->info.device = &pdev->dev;
  12.         fbdev->info.par = fbdev;

  13.         fbdev->dma_template = devm_kzalloc(&pdev->dev,
  14.                                            sizeof(struct dma_interleaved_template) +
  15.                                                sizeof(struct data_chunk),
  16.                                            GFP_KERNEL);
  17.         if (!fbdev->dma_template)
  18.                 return -ENOMEM;

  19.         vdmafb_init_var(fbdev, pdev);
  20.         vdmafb_init_fix(fbdev);

  21.         /* Allocate framebuffer memory */
  22.         fbsize = fbdev->info.fix.smem_len;
  23.         fbdev->fb_virt = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbsize),
  24.                                             &fbdev->fb_phys, GFP_KERNEL);
  25.         if (!fbdev->fb_virt)
  26.         {
  27.                 dev_err(&pdev->dev,
  28.                         "Frame buffer memory allocation failed\n");
  29.                 return -ENOMEM;
  30.         }
  31.         fbdev->info.fix.smem_start = fbdev->fb_phys;
  32.         fbdev->info.screen_base = fbdev->fb_virt;
  33.         fbdev->info.pseudo_palette = fbdev->pseudo_palette;

  34.         /* Clear framebuffer */
  35.         memset_io(fbdev->fb_virt, 0, fbsize);

  36.         fbdev->dma = dma_request_chan(&pdev->dev, "axivdma");
  37.         if (IS_ERR_OR_NULL(fbdev->dma))
  38.         {
  39.                 ret = PTR_ERR(fbdev->dma);
  40.                 if ((ret != -ENOENT) || (ret != -EPROBE_DEFER))
  41.                 {
  42.                         dev_err(&pdev->dev, "Failed to allocate DMA channel (%d).\n", ret);
  43.                         return ret;
  44.                 }
  45.                 goto err_dma_free;
  46.         }

  47.         ret = of_property_read_u32(pdev->dev.of_node, "park", &fbdev->park);
  48.         if (ret < 0)
  49.         {
  50.                 fbdev->park = 1;
  51.         }

  52.         /* Setup and enable the framebuffer */
  53.         vdmafb_setupfb(fbdev);

  54.         ret = fb_alloc_cmap(&fbdev->info.cmap, 256, 0);
  55.         if (ret)
  56.         {
  57.                 dev_err(&pdev->dev, "fb_alloc_cmap failed\n");
  58.         }

  59.         /* Register framebuffer */
  60.         ret = register_framebuffer(&fbdev->info);
  61.         if (ret)
  62.         {
  63.                 dev_err(&pdev->dev, "Framebuffer registration failed\n");
  64.                 goto err_channel_free;
  65.         }

  66.         return 0;

  67. err_channel_free:
  68.         dma_release_channel(fbdev->dma);
  69. err_dma_free:
  70.         dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbsize), fbdev->fb_virt,
  71.                           fbdev->fb_phys);

  72.         return ret;
  73. }
复制代码
行7~9,申请内存空间。
行11,将fbdev保存到pdev中,防止数据丢失。
行13~15,设置fb设备的操作集合与设备等。
行17~22,申请临时dma内存空间。
行24~25,设置显示屏的参数。
行28~39,申请fb内容的内存空间。
行42,将新申请的内存空间初始化。
行44~54,向内核申请一个dma通道
行56~60,读取设备树的park字段,确定是praking模式还是circular模式。
行63,设置设置fb设备的dma相关配置。
行72~77,向内核注册fb设备。
6 方案演示
6.1 硬件连线
1:板卡部分
1725966318245.jpg
2:屏幕部分
image.jpg
6.1 程序测试
1:上电并串口登录
账户:uisrc,密码:root。
image.jpg
此时屏幕已显示开机登录命令行界面。
2:运行qt
输入命令:
cd qt_play/                                进入qt程序文件夹
sudo ./run_arm_app.sh                运行qt程序,输入密码:root
image.jpg
3:观察qt程序
1725966431690.jpg
屏幕上的加号减号可触控。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则