关灯
请选择 进入手机版 | 继续访问电脑版
米联客uisrc 首页 课程 XIINX-ZYNQ 2|ZYNQ入门 查看内容
0

S02-CH35 基于μGUI的触摸屏GUI界面设计

摘要: 本例程通过开源的uGUI v0.3库设计了1个GUI界面,为该GUI界面设计了5个窗口,为每个窗口设计了标题、按键、文本、图片logo等元素,并给每个按键设定了相应的功能,如窗口间切换、LED灯控制、屏幕亮度调节等,并在1个 ...

软件版本:VIVADO2017.4

操作系统:WIN10 64bit

硬件平台:适用米联客 ZYNQ系列开发板

米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!!

35.1 概述

      很多工程师都在ZYNQ上做过LINUX相关的应用和开发,在ZYNQ运行LINUX操作系统,可以实现非常细致的GUI图形界面。用户可以通过鼠标、键盘等外部设备与操作系统通过GUI界面进行人机交互,与平时使用电脑的体验相当。

     但同时,也有很用户只涉及ZYNQ的裸机开发,那么在无操作系统支持的情况下,是否可以在裸机环境中构建一个GUI图形界面呢?

     答案是肯定的。本例程通过利用开源GUI库μGUI,在ZYNQ裸机环境下创建一组简单的GUI界面,并通过外部液晶触摸屏实现与ZYNQ的人机交互。本例程所涉及的应用知识点如下:

  • LCD触摸屏的使用原理
  • 移植开源μGUI库,利用API函数搭建一组GUI图形界面
  • 通过VDMA、AXI-S Video Out、VTC等IP实现GUI图形界面的显示
  • 通过定时器中断实现GUI界面的周期性刷新
  • 通过PS实现动态配置MMCM/PLL
  • 通过PS动态配置AXI PWM调节液晶屏亮度
  • 通过AXI GPIO检查触摸屏的中断信号
  • 通过I2C读取触摸屏的触摸信息
  • 通过PS设置Video Timing Controller显示分辨率
  • 设计GUI界面的动态变化机制,将触摸信息反馈至GUI界面后,GUI产生动态变化,形成人机交互。

     本章提供两个测试工程,分别是LCD屏的RGB接口uGUI工程和LVDS接口的uGUI工程。用户可使用RGB或LVDS中的任意接口实现基于μGUI的触摸屏GUI界面设计的功能。

35.2 基本原理

     本例程通过开源的uGUI v0.3库设计了1个GUI界面,为该GUI界面设计了5个窗口,为每个窗口设计了标题、按键、文本、图片logo等元素,并给每个按键设定了相应的功能,如窗口间切换、LED灯控制、屏幕亮度调节等,并在1个窗口中设计了绘图功能。然后,将设计的GUI窗口通过触摸屏显示。用户通过按下触摸屏中所显示的窗口对应的按键位置,便可以实现该按键所设定的相应功能,从而实现人机交互。

35.3 LCD 触摸屏

      LCD触摸屏由LCD液晶屏和触摸屏两部分组成,其中LCD液晶屏用于画面显示,触摸屏用于实现触摸控制。本例程中所使用的是微雪公司的7寸LCD电容触摸屏,如下图所示。

35.3.1 液晶屏

      液晶屏为1024×600分辨率,这个分辨率不常用,在网上很难找到统一的标准。在该液晶屏HJ070NA-13A的手册中可以找到此液晶屏在该分辨率下的时序要求,如下图所示。

      本例程采用了51.2MHz像素时钟所对应的参数设置。液晶屏的驱动方法与显示器类似,通过时钟,行、场同步信号,数据有效信号来完成,此处不作赘述。

      要使液晶屏正常显示,背光源使能信号DISP要拉高,通过调节PWM的占空比可以改变背光源的亮度。该液晶屏的PWM为负极性,即低电平占空比越高,背光源越亮,PWM信号的频率范围为100Hz~200KHz。

35.3.2 触摸屏

      触摸屏为电容屏,采用了FT5206作为主控芯片,支持5点触控。触摸屏与ZYNQ的接口如下图所示。

35.3.3 触摸屏唤醒

      在触摸屏与ZYNQ的接口中,WAKE信号为低电平有效,ZYNQ通过拉低WAKE信号若干毫秒,再将其拉高来唤醒FT5206芯片,当触摸屏正常工作时WAKE信号应恒为高电平。如下图所示。

35.3.4 触摸中断

      INT信号也为低电平有效。当手指触摸电容屏时,INT信号会以固定频率(默认为60Hz)脉冲信号的形式输出,当手指离开电容屏,INT信号重新恢复高电平,如下图所示。

35.3.5 触摸信息获取

       每当INT变为低电平时,ZYNQ就立即通过I2C接口读出FT5206芯片内部记录的触摸坐标信息,完成一次触摸响应。

      FT5206芯片I2C接口作为slave从设备,最高速率为400KHz,I2C地址为0x38。这里补充说明一点,关于I2C地址芯片datasheet中作了如下图所示的描述,笔者在搜集到的关于FT5206的资料中均未能找到关于I2CCON register的说明。后来,通过网络搜索发现使用过该系列芯片的记录中,所提到的I2C地址均为0x38,笔者通过尝试得到了验证。

      FT5206芯片通过一系列寄存器记录与触摸相关的信息。相关寄存器如下图所示。


    其中,TD_STATUS寄存器记录了触摸点数,如下图所示。

  记录触摸坐标、触摸动作的寄存器如下图所示。

    ZYNQ通过I2C读取这些寄存器值分为两个步骤。首先,发送需要连续读取的起始寄存器地址,如下图所示。

    然后,连续读取若干个地址连续的寄存器值,如下图所示。

35.3.6 LCD触摸屏接口

     微雪公司的7寸LCD电容触摸屏具有两种接口,一种是24位RGB888接口,另外一种是单路8bit LVDS接口,触摸控制通过I2C实现。

     LCD背面接口图:

这里需要注意:微雪公司的7寸LCD电容触摸屏的两种接口不能同时使用。默认状态使用RGB接口,如果要使用LVDS接口,需要调节电阻。

使用RGB接口:R24位置焊接10K电阻,R25不焊接元件(默认状态)。

使用LVDS接口:R25位置焊接10K电阻,R24不焊接元件。

35.3.6.1 RGB接口

LCD触摸屏RGB接口定义如下图所示。其中,1~35为液晶屏接口,37~40为触摸屏接口。

35.3.6.2 LVDS接口

LCD触摸屏LVDS接口(仅介绍使用的引脚)如下图所示。其中,12~15为触摸屏接口,其他为液晶屏接口。

35.4 μGUI概述

      μGUI是一个开源的GUI图形界面设计库,包含了窗口(window),按键(button),文本框(textbox),图片(image)等4种元素。用户可以根据它们所对应的API函数自由发挥,对这些元素的数量、颜色、大小、位置、功能等参数进行定义,实现简易的GUI界面设计。

      μGUI具有一个特色,提供了专门的API函数来支持外部的二维坐标(X, Y)输入设备,例如,触摸屏。用户所创建的GUI界面可以通过这些二维坐标获取外界物体输入的交互信息(比如触摸具有某个特定功能的按键),以此为基础实现人机交互。

    该库一共包含ugui.c和ugui.h两个文件。关于μGUI库更为详细的介绍以及相应API函数的使用可参考其使用手册。读者可以访问其所在网站www.embeddedlightning.com,可以下载源文件、使用手册以及example project。

35.4.1 μGUI库移植

      由于μGUI是一个跨平台(DSP、ARM、MCU、单片机等)的C语言库,在将μGUI的ugui.c和ugui.h文件移植到ZYNQ的ARM中,并通过SDK进行程序设计和编译之前,需要完成2个重要工作:

35.4.1.1 添加单像素点像素值设置函数

      1个GUI界面其实就是1幅图像,1幅图像由很多个像素点组成。要设计1个GUI界面,就需要设置其中每个像素点的像素值。简单的说,就是要给GUI界面对应图像所在存储区域的各个地址赋值。但针对不同的平台,不同的应用,GUI界面的存储方式会存在不同,因此像素值设置的方式会有区别。出于跨平台的目的,μGUI给出了这个函数的原型声明,提供了开放接口,由用户自行设计这个函数来完成GUI界面各像素点值的设置,μGUI中所有与界面设计相关的API函数都将调用这个函数。本例程在gui_window.c中所设计的函数如下:

                     voidPixelSet(UG_S16 x, UG_S16 y, UG_COLOR c)

                      {

                      u32iPixelAddr;

                      iPixelAddr = y * GUI_WIDTH  + x;

                      BufferPtr[0][iPixelAddr] = c;

                      }

      其中x, y对应像素点的二维坐标(X,Y),c为该像素点所需设置的像素值,位宽32bit。由于本例程中GUI界面位于DDR中连续的内存区域,通过GUI界面的指针BufferPtr [0]以及x, y便可计算出像素点的地址。

35.4.1.2 调整各种变量类型定义

      在不同的嵌入式平台中,对于各种变量类型的定义可能会存在区别,例如,int型变量在不同的平台中可能是64位、32位或者16位的。因此,需要在ugui.h中调整各变量类型的定义。ugui.h中默认的变量定义如下:

                    typedefuint8_tUG_U8;

                    typedefint8_tUG_S8;

                    typedefuint16_tUG_U16;

                    typedefint16_tUG_S16;

                    typedefuint32_tUG_U32;

                    typedefint32_tUG_S32;

      上述变量定义与ZYNQ平台编译器一致,因此无需修改,只需在ugui.h文件中将#include "system.h"改为#include "stdint.h"即可。

35.4.1.3 GUI窗口

      窗口是μGUI中GUI界面必需的基本组成,不可缺少,没有窗口就无法创建GUI界面。而文本框、按键、图片等对象则是可选项,在GUI界面中可有可无。μGUI中的窗口包含了文本框、按键、图片3种基本对象,每个窗口都可以拥有若干个多种对象。简而言之,1个窗口可以包含若干个对象,而1个GUI界面可以包含若干个窗口,它们之间的关系如下图所示。

35.4.1.4 GUI界面刷新

      作为GUI界面,应该具有动态变化的特性。例如,不同窗口间切换、文本框内容变化、按键位置调整等。μGUI提供了一个函数UG_Update()来实现整个GUI界面的刷新。当用户通过μGUI库中某些API函数改变当前窗口中某些对象的特性后(例如,文字、颜色、大小),就必须尽快调用UG_Update()来确保这些更改被刷新到GUI界面上。若不调用UG_Update(),则GUI界面将永远不会发生任何改变。

      因此,在使用μGUI库进行GUI界面设计时,为了保证其动态变化的特性,用户必须要周期性的调用UG_Update()函数,调用频率的高低可根据实际要求来确定。

35.4.1.5 人机交互

      GUI界面的动态变化特性,其中很大一部分都源自于人机交互,上面提到了μGUI库支持外部的二维坐标(X, Y)输入设备,由外部输入的这些二维坐标就是GUI界面所需要的人机交互信息。例如,当设备与触摸屏连接,用户触碰了触摸屏的某个区域,触摸屏将这个区域的中心二维坐标反馈给设备,设备再将坐标输入GUI界面。

      那如何将人机交互与GUI界面的动态特性相关联呢?μGUI库提供了UG_TouchUpdate()函数,用于向GUI界面输入当前窗口中外部输入的二维坐标信息。通过该函数,GUI界面可判断出该坐标对应当前窗口中具体哪个对象,例如,某个按键。那当知道了某个对象被触摸后,是否应该作出,或者具体如何作出动态变化来响应这个输入坐标呢?这完全由用户自己来进行定义。μGUI库为每个窗口提供了窗口回调函数来完成这个任务,并给出了回调函数的原型声明(详情参考μGUI使用手册),函数的具体内容完全由用户自己设计。

      窗口回调函数在UG_Update()中被调用,回调函数获取输入二维坐标对应的对象信息,用户可以据此自由发挥,设计出相应的动态变化效果。因此,GUI的动态变化最终将由GUI界面刷新函数UG_Update()来实现。这也说明了为何必须周期性的调用UG_Update()来确保GUI界面的动态变化特性。

35.4.2颜色空间

      在μGUI库中,GUI界面里每个像素点的像素值都是32位的无符号整型变量,采用ARGB888颜色空间,如下图所示。然而,最新的版本μGUI v0.3只使用了其中的RGB888部分。因此,对于用户来说,GUI界面就是一幅24位的RGB888彩色图像,高8位完全忽略。

      在μGUI中,定义了上百种 RGB888的颜色种类,用户可根据自己的喜好为GUI界面中的窗口、按键、文本等对象选择相应的颜色进行设计。

35.4.3字体大小

      μGUI库中定义了十几种大小不同的字体,用于GUI界面中窗口、文本框、按键等对象的文本显示。

35.5 PL逻辑框架

      本例程的PL部分设计原理如下图所示。

31.5.1 PS设置

    PS部分的设置下图所示。

  • 使能M_GP0、S_HP0,PL到PS的中断接口。
  • 使能I2C0接口,并将其通过EMIO方式引出。I2C0接口与触摸屏连接,用于读取触摸屏的触摸坐标信息。
  • 使能FCLK_CLK0,时钟频率设置为100MHz。


35.5.2 GUI界面显示

      本例程中GUI界面的显示通过AXI VDMA、AXI4-S to Video Out、Video Timing Controller三个IP核完成。显示分辨率为1024*600@60Hz。

35.5.2.1 AXI VDMA设置

  • AXI VDMA与AXI DMA使用对比

       在本例程中,AXI DMA并不适用。原因如下:

       当使用AXI DMA时,PS每次向PL发送GUI界面图像都需要通过调用函数配置PL的AXI DMA才能完成。在AXI DMA发送完成中断函数中发起下一次图像传输的方式来实现连续的图像发送。同时,在本例程中,GUI界面的刷新过程是在定时器中断函数中完成,当触摸屏以60Hz显示时,1幅图像的显示时间为17.67ms。若GUI界面刷新的时间超过触摸屏中1幅图像显示的时间时,AXI DMA图像发送完成中断将不能被立即响应执行,会导致图像显示发生中断。当GUI界面刷新完成CPU退出定时器中断后,AXI DMA图像发送才能被继续执行,重新恢复GUI界面的显示,这样在每次GUI界面刷新的时候就会产生“跳屏”的现象。总而言之,定时器中断函数的执行时间会影响AXI DMA发送中断函数的执行。笔者已使用AXI DMA验证了这个现象。

      而VDMA具有free run的特点,GUI界面图像的发送可由PL的VDMA独自完成,无需PS参与,因此,定时器中断中GUI界面刷新与图像显示不会产生任何冲突,可以保证GUI界面显示的连续性。因此,在本例中使用VDMA更合适。

  • AXI VDMA设置

       在本例程中,只存在PS的DDR向PL的图像传输,即Memory Map to Stream(MM2S)方向。因此,只需使能读通道,可关闭写通道来节省逻辑资源。由于GUI界面只对应DDR中的同一幅图像,只需1个图像缓存,所以将frame buffer设置为1即可。另外,将中断输出与PS连接。AXI VDMA的设置如下图所示。


35.5.2.2  Video Timing Controller

      为了方便PS动态配置Video Timing Controller的显示分辨率,使能AXI-lite接口,其默认的分辨率可不用进行设置。Video Timing Controller的设置如下图所示。

35.5.2.3 AXI4-Sream to Video Out

      本例程中,输出至触摸屏的GUI图像为RGB888格式,AXI4-Sreamto Video Out设置如下图所示。需要引出RGB数据data、行同步信号hs、场同步信号vs以及数据有效信号de与LCD触摸屏连接。

35.5.2.4 AXI-Sream Subset Converter

      AXI VDMA与AXI4-Stream to Video Out之间通过AXI Stream接口连接,由于AXI VDMA的M_AXIS_MM2S接口的数据为32位,且含有4为的tkeep信号。而AXI4-Stream to Video Out的video_in接口的数据为24位,且无tkeep信号。为了更好的将两个信号之间存在差异的Stream接口连接,通过AXI-Sream Subset Converter完成差异信号的重映射。设置如下图所示。


35.5.3 AXI PWM

      本例程中,PL部分通过使用digilent公司AXI PWM的IP核来实现PWM信号的输出,用于驱动触摸屏的背光源。该ip位于user_src文件夹下,IP的路径设置如下图所示。

      很多触摸屏的背光源都是由PWM信号驱动的,通过改变PWM信号的占空比可以调节触摸屏的亮度。AXI PWM通过AXI总线与PS连接,PS通过AXI4-Lite对其进行配置和控制,并可动态调节输出PWM信号的占空比。AXI PWM的设置如下图所示,只需输出1路PWM信号,由于使用的触摸屏所需的PWM信号为负极性,即低电平占空比越大,屏幕越亮,因此将极性设为负。输出的PWM信号与触摸屏连接。

35.5.4 AXI GPIO

      本例程中,PL部分通过AXI GPIO实现1个触摸屏触摸中断信号输入,2个按键信号输入,5个LED控制信号输出,1个液晶屏背光源使能信号输出,共计9个GPIO端口,定义如下。

  • GPIO[0],触摸屏中断信号输入
  • GPIO[1],按键信号输入,MZ7X系列对应SW1
  • GPIO[2],按键信号输入,MZ7X系列对应SW2
  • GPIO[3]~GPIO[7],LED1~LED5控制信号输出,这里只使用MZ7X的LD1~LD4,LD5不作控制。
  • GPIO[8],液晶屏背光源控制信号输出

     每当输入的触摸中断信号或按键信号发生一次改变,AXI GPIO则会向PS触发一次中断。AXI GPIO的设置如下图所示。使能中断接口,将其与PS连接。

35.5.5 Clocking Wizard

      本例程中,Clocking Wizard用于提供LCD触摸屏进行GUI显示的参考时钟,该时钟被Video Timing Controller以及AXI4-Sreamto Video Out两个IP核使用,同时引出至顶层模块。将Clocking Wizard设置为MMCM(因为MMCM内部倍频系数和CLKOUT0分频系数可以为小数,比PLL更易获得所需的频率),并使能AXI-lite动态重配置接口,将动态重配置接口通过AXI总线与PS连接,方便PS随时对MMCM或PLL输出的时钟频率进行重配置,从而避免PL部分的重新编译。

      Clocking Wizard设置如下图所示。注意将input clock的时钟源设置为Global buffer,因为输入时钟来自PS的FCLK_CLK0,该时钟也被其他IP所使用,在进入MMCM之前已经位于全局时钟网络BUFG。


       输出时钟频率可设置也可不作设置,为了避免误解,在这里设置为所LCD显示屏需要的51.2MHz。若用户使用其他触摸屏,需要更高的分辨率,建议将clk_out1设置一个大于等于最高分辨率所对应时钟频率的值,这样可以确保时序约束的可靠性。

35.5.6 IO口

35.5.6.1 RGB接口IO

下面内容针对于RGB接口测试工程

1、 PL IO信号定义

本例程除了PS部分的固定IO口之外,PL部分引出的IO口如下图所示。

信号定义如下表所示。

PL部分IO口定义表

2、LCD时钟信号输出

      Clocking Wizard引出的时钟信号lcd_clk,需要经过ODDR产生1个反相的时钟lcd_clk_o与LCD触摸屏连接。目的在于使输出时钟lcd_clk_o的边沿位于LCD其他控制、数据信号窗口的中心位置,使建立和保持时间最大化。

3、LCD RGB信号映射

      AXI4- Sreamto Video Out输出的24位像素点数据lcd_data与LCD触摸屏RGB信号的映射关系如下所示。

4、LCD控制信号

     LCD液晶屏的背光源使能信号与AXI GPIO的最高位连接,使PS可以控制背光源的开关。触摸屏唤醒信号置为1。

35.5.6.2 LVDS接口IO

下面内容针对于LVDS接口测试工程

1、PL IO信号定义

本例程除了PS部分的固定IO口之外,PL部分引出的IO口如下图所示。

信号定义如下表所示。

PL部分IO口定义表

2、时钟及数据信号

下面这个模块的功能是将RGB信号转换为LVDS信号。

3、LCD控制信号

      触摸屏唤醒信号置为1。

35.6 PS程序设计

    PS部分程序所有的源文件如下图所示。

35.6.1 main函数

main函数主要完成如下功能:

  • 初始化Clocking Wizard,并重新设置其输出时钟
  • 初始化中断控制器及系统中断
  • 初始化并配置定时器及其中断
  • 初始化并配置AXI GPIO及其中断
  • 初始化并配置I2C接口
  • 初始化并配置Video Timing Controller,设置显示分辨率
  • 初始化并配置AXI VDMA及其中断
  • 初始化并配置AXI PWM,设置输出PWM信号频率及占空比
  • 启动定时器工作
  • 创建GUI界面
  • 启动AXI VDMA工作
  • 打开背光源,启动AXI PWM输出PWM信号点亮触摸屏
  • 进入空循环,等待触摸屏中断,根据触摸坐标信息进行相应响应,GUI界面产生相应变化

35.6.2 时钟重配置

      在PL中,Clocking Wizard使能了动态重配置功能,因此PS可以通过其AXI-lite接口动态重配置MMCM或者PLL,来改变其时钟的输出频率。

驱动程序

      Clocking Wizard的驱动程序由clk_wizard_config.c和clk_wizard_config.h组成。

   本例程中,使用了MMCM,MMCM的重配置通过Clk_Wiz_Reconfig()函数完成,该函数参考自SDK中clk_wiz_v1_1的example:xclk_wiz_intr_example.c。由于SDK关于Clocking Wizard的驱动还未完善,且MMCM/PLL内部的寄存器信息未有官方文档可参考,所以Clk_Wiz_Reconfig()不在此做具体分析。MMCM的基本结构如下图所示。

       MMCM内部的压控振荡器VCO频率与MMCM的输出时钟频率的公式如下:

       ZYNQ 7010/7015/7020内部MMCM的VCO频率范围为如下图所示。

      动态配置MMCM其实就是通过PS改变其内部倍频系数M,输入分频系数D,以及输出分频系数O的值来实现。在clk_wizard_config.h有4个重要的宏定义,如下:

                      #define VCO_FREQ 600

                      #define DYNAMIC_INPUT_FREQ 100

                      #define DYNAMIC_OUTPUT_FREQ 51.2

                      #define CLK_FRAC_EN 1

   其中VCO_FREQ表示MMCM工作时的VCO频率为600MHz,DYNAMIC_INPUT_FREQ表示MMCM输入的时钟频率为100MHz, DYNAMIC_OUTPUT_FREQ表示MMCM输出的时钟频率为51.2MHz,CLK_FRAC_EN表示允许CLKOUT0的分频系数为1/8精度的小数。       

       Clk_Wiz_Reconfig()函数根据这4个宏定义的值对MMCM的M、D、O的值进行配置,并通过Wait_For_Lock()判断各环节的输出时钟是否LOCK。用户也可以根据实际需求在允许的范围内调节VCO的频率。

35.6.3 PWM信号输出

驱动程序

       AXI PWM的驱动程序由pwm_config.c和pwm_config.h组成。

       本例程所使用的液晶屏建议输入PWM信号频率为100Hz~200K Hz。在main函数中通过PWM_Init()函数设置初始输出PWM信号的周期和占空比。

PWM_Init函数:

  • 通过PWM_Set_Period函数设置PWM信号周期
  • 通过PWM_Set_Duty函数设置PWM信号低电平的占空比

       上述函数都是以时钟周期数来设置。在pwm_config.h包含了PWM信号周期和占空比的时钟周期数宏定义。如下:

                                      #define PERIOD_CLOCK_NUM        409600  

                                     #define DUTY_CLOCK_NUM          204800

       由于本例程中,PL部分AXI PWM的参考时钟频率为100MHz,一个时钟周期就是10ns。因此,PWM_Init所设置的PWM信号的周期为4.096ms,占空比为50%,频率约为250Hz。

       为了对PWM信号的占空比进行动态调节达到改变液晶屏背光源亮度的目的,又设计了PWM_increase_duty()和PWM_decrease_duty()函数用于增大和减小PWM信号中低电平的占空比。

        最后,在main函数中调用PWM_Enable()使能PWM信号输出。

35.6.4 GPIO输入输出

        AXI GPIO的驱动程序由gpiopl_intr.c和gpiopl_intr.h组成。

     在main函数调用Gpiopl_init()函数初始化AXI GPIO,设置9个GPIO的方向,其中GPIO[0]~GPIO[2]为输入,GPIO[3]~GPIO[8]为输出。并将GPIO[3]~GPIO[8]的输出都置为0。每个GPIO的宏定义在gpiopl_intr.h中,如下所示。

                              #define TOUCH_INTR_MASK         0x00000001

                              #define BUTTON0_INTR_MASK       0x00000002

                              #define BUTTON1_INTR_MASK       0x00000004

                              #define LED1_MASK               0x00000008

                              #define LED2_MASK               0x00000010

                              #define LED3_MASK               0x00000020

                              #define LED4_MASK               0x00000040

                              #define LED5_MASK               0x00000080

                              #defineLCD_BL_EN_MASK          0x00000100

    通过Gpiopl_Setup_Intr_System()初始化并使能AXI GPIO的输入中断,GPIO[0]~GPIO[2]的任意1个信号的输入发生1次改变将触发1次中断,GpioplIntrHandler()为GPIO的中断函数。

      GpioplIntrHandler()函数:

  • 判断GPIO[0]输入的触摸屏中断信号是否为0,若为0,则调用I2C_write()和I2C_read()函数通过I2C接口读出触摸屏的触摸信息,并触摸屏中断标志位touch_flag置1,该信号将在定时器中断中使用。
  • 判断GPIO[1]输入的SW3按键信号是否为0,若为0,将GPIO[8]信号拉低,打开液晶屏背光源。
  • 判断GPIO[2]输入的SW4按键信号是否为0,若为0,将GPIO[8]信号拉高,关闭液晶屏背光源。

      最后,在main函数中将GPIO[8]置为1,开启液晶屏的背光源。

      GPIO[3]~GPIO[7]信号的输出在gui_window.c中进行控制,用于在GUI界面中控制开发包中的LED灯。

35.6.5 I2C读取触摸信息

       PS的I2C接口的驱动程序由iic_intr.c和iic_intr.h组成。

       在本例程中,PS的I2C接口不能工作于中断模式,其原因在于,通过I2C接口读写触摸屏是在GPIO的中断函数GpioplIntrHandler()中执行,此时若I2C接口产生中断,该中断将不能在GPIO中断中被嵌套响应,无法完成读写操作。因此,I2C接口只能工作于轮询Poll模式。

       触摸屏中FT5206控制芯片的I2C地址和I2C时钟频率设置如iic_intr.h宏定义所示。I2C地址为0x38,I2C时钟频率设为400KHz,为FT5206的最高值。

       #define IIC_SLAVE_ADDR 0x38

       #define IIC_SCLK_RATE 400000

     在main函数中调用Iic_init()函数初始化PS的I2C接口,设置I2C时钟频率。当触摸屏中断信号拉低触发GPIO产生中断后,在GPIO中断函数GpioplIntrHandler()中首先调用I2C_write()向FT5206芯片发送需要读取的寄存器首地址。在本例程中,需要从地址为0x02的寄存器开始读,因此I2C_write()发送的首地址为0x02。然后,调用I2C_read()函数从FT5206连续读取29个寄存器的值,每个寄存器的值为1个字节,一共读取29个字节的触摸信息。

       I2C_write()和I2C_read()函数均以轮询模式控制I2C接口。

35.6.6 GUI界面显示

     GUI界面显示在PL部分由AXI VDMA,Video Timing Controller,AXI4- Sreamto Video Out三个IP协同完成。其中AXI VDMA和Video Timing Controller需要PS进行设置,AXI VDMA完成PS端DDR3中GUI界面的读取,Video Timing Controller产生通过AXI4- Sreamto Video Out进行GUI界面显示的控制时序。

35.6.6.1 AXI VDMA

AXI VDMA的驱动程序由vdma_config.c和vdma_config.h组成。

      在本例程中,AXI VDMA仅有MM2S方向(PS DDR到PL)的读通道工作。另外,需要让AXI VDMA工作于free run模式,因此,只使能错误中断。

在main函数中调用Vdma_Init()函数对AXI VDMA进行初始化,在Vdma_Init()中调用ReadSetup()对MM2S读通道的参数进行设置。VDMA读通道的相关设置由vdma_config.h中的宏定义所决定,如下所示。

                          #define IMAGE_WIDTH        GUI_WIDTH

                          #define IMAGE_HEIGHT     GUI_HEIGHT

                          #define BYTES_PER_PIXEL 4

                          #define NUMBER_OF_READ_FRAMES  1

                          #define MEM_BASE_ADDR      0x10000000

                          #defineBUFFER0_BASE     (MEM_BASE_ADDR)

  • IMAGE_WIDTH,图像宽度,1024
  • IMAGE_HEIGHT,图像高度,600
  • BYTES_PER_PIXEL,每个像素点的字节数为4
  • NUMBER_OF_READ_FRAMES,图像缓存数量,实际只需使用1个缓存
  • BUFFER0_BASE,图像缓存的首地址,0x10000000

        然后,在main函数中调用Vdma_Setup_Intr_System()函数,配置使能VDMA读通道的错误中断,ReadErrorCallBack()为中断函数。

        最后,调用Vdma_Start()函数,启动AXI VDMA的读通道开始工作。

35.6.6.2 显示时序设置

        Video Timing Controller的驱动程序由vtc_config.c和vtc_config.h组成。

        在本例程中,液晶屏使用了1024×600@60Hz显示分辨率,使用的时序参数如下:

  • 行总像素数:1344
  • 行有效像素数:1024
  • 行同步前肩像素数:24
  • 行同步信号像素数:136
  • 行同步后肩像素数:160
  • 场总行数:635
  • 场有效行数:600
  • 场同步前肩行数:8
  • 场同步信号行数:4
  • 场同步后肩行数:23

        在main函数中调用Vtc_init()函数对Video Timing Controller进行初始化,将上述的显示时序参数写入其中,然后使能其产生控制时序信号。

35.6.7 定时器

       PS定时器的驱动程序由timer_intr.c和timer_intr.h组成。

       在本例程中,PS定时器用于周期性产生中断来实现GUI界面的刷新,以固定的频率的调用UG_Update()函数实现GUI的动态特性。

       首先,在main函数中调用Timer_init()函数对定时器进行初始化,其中断周期由以下宏定义决定,周期为20ms,即GUI的刷新频率为50Hz,刷新频率越高,GUI的动态变化越快,用户可以根据需求进行调整。

#define TIMER_LOAD_VALUE    XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ / 100

        然后,在main函数中调用Timer_Setup_Intr_System()函数使能定时器的中,将中断函数设置为TimerIntrHandler()。

        TimerIntrHandler()函数的过程如下:

  • 关闭定时器中断,因为定时器中断函数的执行时间可能会超过定时器设置的中断周期,如20ms。
  • 若触摸屏中断标志位touch_flag为1,则对GPIO中断函数中读出的29字节触摸信息进行判断,若第一个触摸点状态为接触(contact),则读出并计算出第一个触摸点的x,y坐标信息,通过UG_TouchUpdate()函数将该触摸坐标反馈至GUI界面,表示GUI上的该点被触摸。若第一个触摸点状态为抬起(put up),则将无效坐标(-1,-1)通过UG_TouchUpdate()反馈至GUI界面,表示GUI上的无任何点被触摸。目前μGUI库的按键触摸功能只支持单点触摸,因此无法同时设计出两个按键同时被触摸的效果。
  • 若当前的窗口为window 4,则为5点绘图窗口。此时,读出5个触摸点的坐标信息,以每个触摸点为圆心,沿每个手指移动轨迹不断画圆。5个触摸点的圆形图案颜色按顺序依次为:红、黄、蓝、绿、橙。
  • 调用UG_Update()刷新GUI界面,让触摸使GUI产生的动态变化更新至对应的图像中。
  • 将变化后最新的GUI界面对应的图像通过Xil_DCacheFlushRange()函数刷进DDR中,从而保证VDMA将最新的GUI界面读出,并显示在触摸屏上。
  • 重新使能定时器中断。

最后,在main函数里调用Timer_start()函数启动定时器工作。

35.6.8 GUI界面设计

    GUI界面的驱动程序由gui_window.c、gui_window.h、image.h、ugui.c和ugui.h组成,其中ugui.c和ugui.h来自μGUI库。

    在本例程中,一共设计了5个窗口,对应window1~window5。同时,也为每个窗口设计了对应的回调函数window_1_callback()~window_5_callback()。

    在main函数中,调用gui_create()函数对GUI进行初始化,并创建所有的窗口及窗口所包含的所有对象。

     gui_create()函数的流程如下:

  • 调用UG_Init()函数初始化GUI
  • 调用UG_FillScreen()函数设置所有GUI窗口的背景颜色
  • 调用create_window1()~create_window5()函数依次创建5个窗口
  • 调用UG_WindowShow()函数设置GUI显示的第一个窗口为window1
  • 调用UG_WaitForUpdate()函数等待定时器中断到来,刷新并显示GUI界面

    GUI界面设计以窗口为基础进行,每个界面对应一个窗口,但所有的窗口都对应同一片内存区域。μGUI库中有很多API函数,这里只介绍本例程所涉及的API函数,其余的API可参考μGUI使用手册。5个窗口的设计在create_window1()~create_window5()函数中完成。

35.6.8.1 GUI初始化

     通过UG_Init()函数设置GUI界面的长宽分辨率,并绑定在4.1.1节所述用户自定义的像素值设置函数PixelSet()。通过UG_FillScreen()函数将所有GUI窗口的背景设置为浅灰色。

35.6.8.2 窗口1设计

      窗口1为GUI的欢迎界面。如下图所示。

      窗口1的设计在create_window1()函数中完成,流程如下:

1)窗口创建

  • 调用UG_WindowCreate()函数绑定窗口1的回调函数为window_1_callback(),设置窗口可包含对象的最大个数为15。
  • 调用UG_WindowSetTitleText()函数设置窗口1的标题内容。
  • 调用UG_WindowSetTitleTextFont()函数设置标题字体的大小。

2)按键创建

  • 调用UG_ButtonCreate()函数创建按键0,并设置按键0的坐标范围
  • 调用UG_ButtonSetStyle()函数设置按键0的模式为3D,且按下后背景和字体的颜色会跳变
  • 调用UG_ButtonSetAlternateForeColor函数设置按键0按下后改变的字体颜色
  • 调用UG_ButtonSetAlternateBackColor函数设置按键0按下后改变的按键背景颜色
  • 调用UG_ButtonSetFont函数设置按键0的字体大小
  • 调用UG_ButtonSetText函数设置按键0显示的内容

3)文本框创建

  • 文本框0
  • 调用UG_TextboxCreate ()函数创建文本框0,并设置文本框0的坐标范围。
  • 调用UG_TextboxSetFont()函数设置文本框0内容的字体大小。
  • 调用UG_TextboxSetText()函数设置文本框0的内容。
  • 调用UG_TextboxSetAlignment()函数设置文本框0中的内容在文本框中的对齐方式。
  • 文本框1
  • 调用UG_TextboxSetForeColor()函数设置文本框1内容的字体颜色
  • 调用UG_TextboxSetHSpace()函数设置本文框1中每个字符之间的横向距离
  • 其余函数调用与文本框0同理

4)图片创建

     目前,最新版的μGUI v0.3仅支持在GUI界面中添加RGB565格式的BMP图像。由于本设计中所需添加的两个米联logo均为RGB888格式,为了让μGUI能支持24位RGB888的图片,需要对ugui.c文件中的UG_DrawBMP()函数进行修改,如下所示。其中通过阴影部分为添加的代码。

             voidUG_DrawBMP(UG_S16xp, UG_S16yp, UG_BMP* bmp )

            {

           UG_S16x,y,xs;

           UG_U8r,g,b;

           UG_U16* p;

          /*add by osrc 2017.2.18*/

UG_U32* p1;

UG_U16tmp;

/*add by osrc 2017.2.18*/

UG_U32 tmp1;

UG_COLOR c;


if ( bmp->p == NULL ) return;


/* Only support 16 BPP so far */

if ( bmp->bpp == BMP_BPP_16 )

   {

      p = (UG_U16*)bmp->p;

   }

/*add support for 32 BPP, by osrc 2017.2.18*/

elseif(bmp->bpp == BMP_BPP_32)

{

 p1 = (UG_U32*)bmp->p;

}

else

   {

return;

   }


xs = xp;

for(y=0;y<bmp->height;y++)

   {

xp = xs;

for(x=0;x<bmp->width;x++)

      {     

/*add support for RGB888, by osrc 2017.2.18*/

if(bmp->colors == BMP_RGB565)

{

tmp = *p++;

/* Convert RGB565 to RGB888 */

            r = (tmp>>11)&0x1F;

            r<<=3;

            g = (tmp>>5)&0x3F;

            g<<=2;

            b = (tmp)&0x1F;

            b<<=3;

            c = ((UG_COLOR)r<<16) | ((UG_COLOR)g<<8) | (UG_COLOR)b;

}

else

{

tmp1 = *p1++;

c = tmp1;

}

UG_DrawPixel(xp++ , yp , c );

      }

yp++;

   }

}

  • 图片0

    添加的图片0如下所示,为米联的logo。图片各像素点的值包含在image.h的logo1_bmp数组中。

    定义该图片在GUI中的结构体,如下所示。图片的指针logo1_bmp,为图片的长、宽均为65个像素,每个像素点占32bit,图片格式为RGB888。

constUG_BMP logo1 =

{

   (void*)logo1_bmp,

   65,

   65,

   BMP_BPP_32,

   BMP_RGB888

};

  • 调用UG_ImageCreate函数在窗口1中创建该图片对象,并设置图片的坐标位置。
  • 调用UG_ImageSetBMP函数将上述的logo图像与创建的图片对象绑定。
  • 图片1

    添加的图片1如下所示,为米联客的logo。图片各像素点的值包含在image.h的logo2_bmp数组中。

     定义该图片在GUI中的结构体,如下所示。图片的指针logo2_bmp,为图片的长位259像素,宽为105个像素,每个像素点占32bit,图片格式为RGB888。

                             constUG_BMP logo2 =

                              {

                              (void*)logo2_bmp,

                              259,

                              105,

                              BMP_BPP_32,

                              BMP_RGB888

                              };

      图片对象创建添加方式与图片0相同。

5)回调函数

      1个窗口的回调函数当该窗口中的按键的触摸状态发生改变时,会在UG_Update()函数中被调用。在窗口1的回调函数window_1_callback()中设置了按键0的功能,当在触摸屏中按下start application now按键便会使window_1_callback()被调用,通过UG_WindowShow函数,让GUI界面刷新后切换到窗口2。在按下按键的同时,可以观察到按键0字体和背景颜色产生的变化。

35.6.8.3 窗口2设计

      窗口2为LED灯控制界面,如下图所示。在该窗口中,用户可以通过触摸LED1~LED5按键来控制开发板上的标识为LD1~LD5的LED灯。

      窗口2的设计在create_window2()函数中完成,窗口2中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。

1)回调函数

      窗口2的回调函数window_2_callback()中设置了7个按键0~6的功能,其中LED1~LED5对应5个LED灯的控制功能,通过控制GPIO[3]~GPIO[7]的输出来实现LED灯的开关。当LED灯亮时,对应按键的背景颜色为红色,当LED熄灭时,对应按键的背景颜色还原为绿色。(注意miz701n只有LED1~LED4受控制,按键LED5实际无控制LD5的作用)另外,按下Previous Page按键让GUI界面切换至窗口1,按下Next Page按键让GUI界面切换至窗口3。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。

35.6.8.4 窗口3设计

      窗口3为液晶屏亮度调节界面,如下图所示。在该窗口中,用户可以通过触摸加、减按键来调节液晶屏背光源的亮度。

      窗口3的设计在create_window3()函数中完成,窗口3中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。

1)回调函数

      窗口3的回调函数window_3_callback()中设置了4个按键0~3的功能,其中“-”按键控制PWM信号低电平的占空比减小,从而降低液晶屏亮度;“+”按键控制PWM信号低电平的占空比增大,从而提高液晶屏亮度。另外,按下“Previous Page”按键让GUI界面切换至窗口2,按下“Next Page”按键让GUI界面切换至窗口4。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。

35.6.8.5 窗口4设计

      窗口4为绘图界面,如下图所示。在该窗口中,可实现5点触控,用户可以通过5个手指同时触摸平面来进行简单画图。

      绘图时,以每个手指触摸点为圆心,沿每个手指移动轨迹不断画圆。5个触摸点的圆形图案颜色按顺序依次为:红、黄、蓝、绿、橙。由于GUI界面存在一定刷新的时间间隔,因此绘图轨迹中间会出现间断的现象,如下图所示。这里用户自行改进来实现真实的绘图效果。

      窗口4的设计在create_window4()函数中完成,窗口4中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。

1)回调函数

     窗口4的回调函数window_4_callback()中设置了3个按键0~2的功能,其中“Retry”按键用于清除当前窗口中的绘图结果,使用户可以重新进行绘图。另外,按下“Previous Page”按键让GUI界面切换至窗口3,按下“Next Page”按键让GUI界面切换至窗口5。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。

35.6.8.6 窗口5设计

      窗口5为退出界面,如下图所示。在该窗口中,用户可以通过触摸“YES”、“NO”按键来选择是否退出回到窗口1的欢迎界面。由于窗口5的尺寸小于其他窗口,因此当GUI界面从窗口4切换到窗口5时,可以出两个窗口叠加的效果,并且当窗口5出现时,窗口4的标题栏会由蓝变灰。

      窗口5的设计在create_window5()函数中完成,由于窗口5的尺寸小于1024×600,因此需要调用UG_WindowResize()函数重新设置窗口5的尺寸大小及位置。窗口5中的标题、按键、文本框的创建流程与create_window1()原理相同,详细可参阅工程代码,此处不作赘述。

1)回调函数

     窗口5的回调函数window_5_callback()中设置了2个按键0~1的功能,按下“YES”按键让GUI界面切换至窗口1,按下“NO”按键让GUI界面切换至窗口4。在按下按键的同时,可以观察到各按键字体和背景颜色产生的变化。

35.7注意事项

35.7.1 更改GUI分辨率

若用户要接其他分辨率的触摸屏,需要更改触摸屏显示分辨率时,需要更改3个地方。

  • main函数中Vtc_init()函数中的分辨率参数
  • 更改gui_window.h中的2个宏定义

         #define GUI_HEIGHT    600

         #define GUI_WIDTH     1024

  • 更改clk_wizard_config.h中的1个宏定义,将其改为所需要的时钟频率,单位为MHz

         #define DYNAMIC_OUTPUT_FREQ 51.2

35.7.2 RGB和LVDS接口可调电阻

         微雪公司的7寸LCD电容触摸屏具有两种接口,一种是24位RGB888接口,另外一种是单路8bit LVDS接口,触摸控制通过I2C实现。

         LCD背面接口图:

这里需要注意:微雪公司的7寸LCD电容触摸屏的两种接口不能同时使用。默认状态使用RGB接口,如果要使用LVDS接口,需要调节电阻。

使用RGB接口:R24位置焊接10K电阻,R25不焊接元件(默认状态)。

使用LVDS接口:R25位置焊接10K电阻,R24不焊接元件。

35.7.3 提供测试例程FEP接口电压

      本章分别提供了RGB接口和LVDS接口的触摸屏测试例程。LCD触摸屏通过转接板连接开发板的FEP接口,开发板的FEP接口电压可调,通过核心板的可调电阻可以将FEP接口电压调整为1V8、2V5、3V3,开发板默认FEP接口电压是3V3。

      RGB接口的测试例程,FPGA开发板FEP接口默认输出电压是3V3。LVDS接口的测试例程,FPGA开发板FEP接口的输出电压是2V5。

35.7.4 测试连接

      由于本例程所使用的LCD触摸屏接口为FPC,而开发板并不具有FPC接口。因此,需要设计一个转接板将触摸屏与开发板的40pin NEP接口连接。需要注意的是,在该触摸屏内部,I2C信号线均未接上拉电阻。因此,在转接板中需要将两个I2C信号各通过1个几KΩ的电阻上拉接至3.3V,否则I2C接口将无法正常使用。

      LCD触控屏与开发板连接如下,这里需要注意,RGB接口使用的转接板和LVDS接口转接板不同,请以实际使用的接口选择转接板:

附录

附件1:摄像头

摄像头参数对照表

摄像头尺寸版图:

摄像头接口定义:

附件2:VGA时序标准

1280x720@60HZ 时序参数

640x480@60HZ 时序参数

附件3:如何添加驱动库(SDK库移植)

      在使用过程中,我们要在SDK工程中使用某个IP,而这个IP的驱动库不能在SDK中自动生成,如果直接运行程序,会报错,这时,我们需要将这个IP的驱动添加到工程中。下面说明一下添加方法,SDK的移植方法通用,这里仅作方法讲解。需要注意:当工程路径变化时,移植的SDK库的路径需要重新添加,否则会报错。

      举例:

      如果在vivado 2015.4的SDK中还没有Clocking Wizard的驱动函数库,缺少可以利用的API函数。而vivado 2015.4的SDK中包含了Clocking Wizard的驱动库。因此,为了便于开发,借用2015.4的驱动库进行设计。

      首先,在SDK 2015.4的安装目录(如:C:\Xilinx\SDK\2015.4\data\embeddedsw\XilinxProcessorIPLib\drivers)下找到Clocking Wizard的驱动库文件夹clk_wiz_v1_1,如下图所示。(这个驱动库也可以是其他地方移植的)

       将文件夹clk_wiz_v1_1复制到工程目录下的sdk_repo(自己创建的文件夹)的bsp文件夹中。然后在sdk中设置sdk_repo文件夹的路径,如下图所示。

然后更改工程bsp中的驱动函数设置,将clk_wiz_0的驱动改为clk_wiz 1.1版本,如下图所示。

重新生成bsp后,在bsp的libsrc下就会出现clk_wiz_v1_1的驱动,如下图所示。

驱动库添加进来,重新编译,就不会报错了。


路过

雷人

握手

鲜花

鸡蛋

说点什么...

已有0条评论

最新评论...

本文作者
2019-9-9 16:28
  • 6
    粉丝
  • 1685
    阅读
  • 0
    回复

关注米联客

扫描关注,了解最新资讯

联系人:汤经理
电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼201B
热门评论
排行榜

关注我们:微信公众号

官方微信

官方微信

客服热线:

0519-80699907

公司地址:常州溧阳市天目云谷3号楼北楼2楼

运营中心:常州溧阳市天目云谷3号楼北楼2楼

邮编:213300 Email:270682667#qq.com

Copyright   ©2019-2026  米联客uisrc内容版权归©UISRC.COM技术支持:UISRC.COM  备案号:苏ICP备19046771号