FPGA强化-TFT_LCD液晶屏驱动设计与验证

13. TFT_LCD液晶屏驱动设计与验证

在上面的章节我们通过多个实验工程学习了VGA的驱动控制及VGA图片的显示,相信读者对所学知识已经掌握。VGA显示固然不错,但在一些便携设备上要想显示相关数据或图片,VGA显示器庞大的体积不能满足要求,这时我们使用更为小巧的TFT-LCD显示屏来取代VGA显示器。

在本章节,我们将带领读者学习TFT-LCD显示屏的基本知识和相关概念,掌握TFT-LCD显示屏的显示时序。根据所学知识设计一个TFT-LCD液晶显示屏的显示控制器,在TFT-LCD液晶显示屏上显示多色彩条。

1024程序员节快乐,如果对您有帮助,感谢点赞、收藏、关注。

13.1. 理论学习

13.1.1. tft-lcd简介

在介绍TFT-LCD之前先来说明一下LCD。液晶显示器,简称LCD(Liquid Crystal Display),相对于上一代CRT显示器(阴极射线管显示器),LCD显示器具有功耗低、体积小、承载的信息量大及不伤眼的优点,因而它成为了现在的主流电子显示设备,其中包括电视、电脑显示器、手机屏幕及各种嵌入式设备的显示器。图 42‑1是液晶电视与CRT电视的外观对比,很明显液晶电视更薄,"时尚"是液晶电视给人的第一印象,而CRT 电视则感觉很"笨重"。

图 42‑1 液晶电视以CRT电视

液晶是一种介于固体和液体之间的特殊物质,它是一种有机化合物,常态下呈液态,但是它的分子排列却和固体晶体一样非常规则,因此取名液晶。如果给液晶施加电场,会改变它的分子排列,从而改变光线的传播方向,配合偏振光片,它就具有控制光线透过率的作用,再配合彩色滤光片,改变加给液晶电压大小,就能改变某一颜色透光量 的多少,图 42‑2中的就是绿色显示结构。利用这种原理,做出可控红、绿、蓝光输出强度的显示结构,把三种显示结构组成一个显示单位,通过控制红绿蓝的强度,可以使该单位混合输出不同的色彩,这样的一个显示单位就是我们所说的像素。

图 42‑2 液晶屏的绿色显示结构

注意液晶本身是不发光的,所以需要有一个背光灯提供光源,光线经过一系列处理过程才到输出,所以输出的光线强度是要比光源的强度低很多的,比较浪费能源(当然,比CRT显示器还是节能多了)。而且这些处理过程会导致显示方向比较窄,也就是它的视角较小,从侧面看屏幕会看不清它的显示内容。另外,输出的色彩变换时,液晶 分子转动也需要消耗一定的时间,导致屏幕的响应速度低。

常见的 LCD**按物理结构分为四种:**扭曲向列型(TN-Twisted Nema TI c)、超扭曲向列型(STN-Super TN)、双层超扭曲向列型(DSTN-Dual Scan Tortuosity Nomograph)、薄膜晶体管型(TFT-Thin Film Transistor),我们将要介绍的TFT- LCD就是其中的一种。TN-LCD、STN-LCD和DSYN-LCD的基本显示原理都相同,只是液晶分子的扭曲角度不同而已。而TFT-LCD则采用与TN系列LCD截然不同的显示方式。

TFT-LCD全称Thin Film Transistor-Liquid Crystal Display,译为薄膜晶体管液晶显示器。其中TFT就是Thin Film Transistor的简称,指的是薄膜晶体管(矩阵),可以"主动的"对屏幕上的各个独立的像素进行控制,这也就是所谓的主动矩阵TFT(active matrix TFT)的来历。图像产生的基本原理很简单:显示屏由许多可以发出任意颜色的光线的像素组成,只要控制各个像素显示相应的颜色就能达到目的了。在TFT LCD中一般采用背光技术,为了能精确地控制每一个像素的颜色和亮度就需要在每一个像素之后安装一个类似百叶窗的半导体开关,以此做到完全的单独的控制一个像素点,液晶材料被夹在TFT玻璃层和颜色过滤层之间,通过改变刺激液晶的电压值就可以控制最后出现的光线强度与色彩。

TFT-LCD技术是微电子技术与液晶显示器技术巧妙结合的一种技术。人们将在硅片上进行微电子精细加工的技术,移植到在大面积玻璃上进行TFT阵列的加工,再将该阵列基板与另一片带彩色滤色膜的基板,利用已成熟的LCD技术,形成一个液晶盒,将两种技术相结合,再经过后工序如偏光片贴覆等过程,最后形成液晶显示器 。

13.1.2. RGB接口TFT-LCD时序

对于RGB接口TFT-LCD显示屏,它的图像显示的同步模式有两种,分别为HV同步模式、DE同步模式。不同的同步模式对应不同的时序。

13.1.2.1. HV同步模式

HV同步模式下,图像的显示只需要行同步信号(hsync)和场同步信号(vsync)来确定显示时序。此时,RGB接口的TFT-LCD液晶显示屏的显示时序和VGA时序标准类似。

  1. RGB接口TFT-LCD显示屏行同步时序,具体见图 42‑3。

图 42‑3 行同步时序图

如图 42‑3所示,图中RGB代表图像信息,HSync表示行同步信号,HSync自上一个上升沿起到下一个上升沿止为一个完整周期,我们称之为行扫描周期,一个完整的行扫描周期,包含4部分:同步、后沿、有效图像、前沿,基本单位为pixel,即一个像素时钟周期。

在一个完整的行扫描周期中,RGB图像信息在HSync行同步信号的同步下完成一行图像的显示,RGB图像信息在有效图像阶段,图像信息有效,其他阶段图像信息无效;HSync行同步信号在同步阶段,维持高电平,其他阶段均保持低电平,在下一个行扫描周期的同步阶段,HSync行扫描信号拉高,其他阶段拉低,周而复始 。

  1. RGB接口TFT-LCD显示屏场同步时序,具体见图 42‑4。

图 42‑4 场同步时序图

如图 42‑4所示,图中RGB代表图像信息,VSync表示场同步信号,VSync自上一个上升沿起到下一个上升沿止为一个完整周期,我们称之为场扫描周期,一个完整的行扫描周期,包含4部分:同步、后沿、有效图像、前沿,基本单位为一个完整的行扫描周期。

在一个完整的行扫描周期中,RGB图像信息在VSync行同步信号的同步下完成一帧图像的显示,RGB图像信息在有效图像阶段,图像信息有效,其他阶段图像信息无效;VSync行同步信号在同步阶段,维持高电平,其他阶段均保持低电平,在下一个行扫描周期的同步阶段,VSync行扫描信号拉高,其他阶段拉低,周而复始 。

将行、场同步时序相结合,构成RGB接口TFT-LCD时序图,具体见图 42‑5。

图 42‑5 RGB接口TFT-LCD时序图

图中的红色区域表示在一个完整的行扫描周期中,RGB图像信息只在此区域有效;黄色区域表示在一个完整的场扫描周期中,RGB图像信息只在此区域有效,两者相交的橙色区域,就是RGB接口TFT-LCD显示屏的图像显示区域。

13.1.2.2. DE同步模式

DE同步模式下,图像的显示只需要数据使能信号确定显示时序,不需要行场同步信号。DE同步模式下的TFT图像显示时序图如下。

图 42‑6 DE同步模式下TFT图像显示时序

由图可知,当数据使能信号为高电平时,表示TFT显示屏扫描到了有效显示区域,此时输入到TFT显示屏的图像信息能够显示出来;当数据使能信号为低电平时,表示TFT显示屏未扫描到有效显示区域。

对于两种不同的同步模式, DE同步模式一般使用在大尺寸屏幕,小尺寸屏幕多使用HV同步模式。HV同步模式地出现早于DE同步模式,当今的大部分显示屏均支持HV和DE两种同步模式。

13.1.3. RGB接口TFT-LCD分辨率

不同的分辨率的TFT-LCD显示屏在时序上是相似的,只是存在一些参数上的差异,下面列举了部分分辨率的时序参数,刷新频率均为60Hz,具体见表格 42‑1。

表格 42‑1 TFT-LCD显示屏时序参数

|---------------|-----------------------------------|------------------------------------------|------------------------------|---------------------------|---------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------|
| 分辨 |率 | | 时 | ** | * | **(M Hz) | * 同 | 场同 步信 | 步 号时 | 序(像 | 素)* | | * | | 时 | (行 | 数)** | | | | | | | | | |
| | | 同 | 步 | | 后 | 沿 | | 有 | * 图 | 沿 像** | | 前 | * | *行扫 | | ** * | **后 ** | | | **有 图 | 沿 像 | | **前 * *场 * | | * | 描周 | | 期** | |
| 480*272 | 9 | 41 | 2 | 480 | 2 | 525 | 10 | 2 | 272 | 2 | 286 |
| 800*480 | 33.3 | 128 | 88 | 800 | 40 | 1056 | 2 | 33 | 480 | 10 | 525 |
| 800*600 | 40 | 128 | 88 | 800 | 40 | 1056 | 4 | 23 | 600 | 1 | 628 |

表格中相关参数的含义与时钟频率的计算方法,读者可参考36.2.4 VGA显示模式小节,在此不再赘述。

13.2. 实战演练

13.2.1. 实验目标

设计编写RGB接口TFT-LCD液晶显示屏驱动,在4.3寸(480*272) TFT-LCD显示屏上横向依次显示等宽多色彩条,显示颜色自左向右依次为红、橙、黄、绿、青、蓝、紫、黑、白、灰,图像像素格式为RGB565,帧率为60Hz。

注:本章节后文中涉及到的相关参数均与4.3寸(480*272) TFT-LCD显示屏的的相关参数相对应,事先告知,后续不再声明。

13.2.2. 硬件资源

征途Pro开发板TFT_LCD接口部分原理图,如图 42‑7所示。

图 42‑7 TFT_LCD部分原理图

由图可知,TFT_LCD液晶屏与VGA相同,均使用RGB565的图像格式,位宽为16bit,输出信号还包括行同步信号hsync和场同步信号vsync;与VGA不同的是,输出信号还有时钟信号、使能信号和背光控制信号;此外还有用于触摸控制的4路信号,本实验中并未涉及,不再介绍。

13.2.3. 程序设计

注:在本实验工程中,输出信号中包含HV同步模式下需要的行、场同步信号(hsync、vsync)和DE同步模式下的tft_de信号,各信号正确输出。读者若想要使用HV同步模式进行图像显示,可在代码中注释掉tft_de信号;若想要使用DE同步模式进行图像显示,可带代码中注释掉行、场同步信号。

13.2.3.1. 整体设计

本实验工程的整体框图如下,具体见图 42‑8。

图 42‑8 TFT彩条显示工程整体框图

由上图可知,本实验工程包括4个模块,各模块简介,具体见表格 42‑2。

表格 42‑2 TFT彩条显示工程模块简介

模块名称 功能描述
tft_colorbar 顶层模块
clk_gen 时钟生成模块
tft_ctrl TFT显示时序控制模块
tft_pic 图像数据生成模块

结合图 42‑8和表格 42‑2,我们来说一下TFT彩条显示工程的工作流程。

  1. 系统上电后,板卡传入系统时钟(sys_clk)和复位信号(sys_rst_n)到顶层模块;

  2. 系统时钟直接传入时钟生成模块(clk_gen),分频产生TFT显示屏工作时钟(tft_clk_9m),作为图像数据生成模块(tft_pic)和VGA时序控制模块(tft_ctrl)的工作时钟;

  3. 图像数据生成模块以TFT显示时序控制模块传入的像素点坐标(pix_x,pix_y)为约束条件,生成待显示彩条图像的色彩信息(pix_data);

(4) 图像数据生成模块生成的彩条图像色彩信息传入TFT时序控制模块,在模块内部使用使能信号滤除掉非图像显示有效区域的图像数据,产生RGB色彩信息(rgb_tft),在行、场同步信号(hsync、vsync)或数据使能信号(tft_de)的同步作用下,将RGB色彩信息扫描显示到TFT显示屏,显示出彩条图像。

本小节以全局视角,对整个实验工程进行了概括,对各子功能模块做了简单介绍,简要说明了实验工程的工作流程,相信读者对实验工程有了整体了解。在后文中,我们将采取分述的方式,从设计、实现、仿真验证等方面,对各子功能模块进行详细介绍。

13.2.3.2. 时钟生成模块

由上文可知,本次实验工程中,TFT显示屏为4.3寸,分辨率为480*272,帧率为60,时钟频率为9MHz,而板卡晶振传入时钟频率为50MHz。时钟生成模块的作用就是将50MHz晶振时钟分频为9MHz的TFT显示屏工作时钟。

本模块调用IP核实现时钟分频。时钟生成模块框图,具体见图 42‑9。

图 42‑9 时钟生成模块框图

由图可知,本模块包括2路输入信号和2路输出信号。对模块输入输出信号的功能描述,具体见表格 42‑3。

表格 42‑3 时钟生成模块输入输出信号功能描述

信号 位宽 类型 功能描述
areset 1Bit Input 复位信号,高电平有效
inclk0 1Bit Input 50MHz晶振时钟输入
c0 1Bit Output 输出分频后9MHzTFT显示屏工作时钟
locked 1Bit Output 输出稳定时钟信号的指示信号

因为本模块是调用内部IP核生成,无需进行波形图绘制和仿真验证。

13.2.3.3. TFT显示时序控制模块

本小节中我们开始TFT_LCD显示时序控制模块(tft_ctrl)的介绍,接下来我们会通过模块框图、波形图绘制、代码编写、仿真分析这几个部分,对本模块的设计、实现、仿真验证过程做一下详细介绍。

模块框图

TFT_LCD显示时序控制模块,作用是驱动TFT_LCD显示屏,将输入模块的彩条图形像素点信息,按照TFT_LCD显示时序扫描显示到TFT_LCD显示屏上。模块框图,具体见图 42‑10。

图 42‑10 VGA时序控制模块框图

由图 42‑10可知,TFT_LCD时序控制模块包含3路输入、8路输出,共11路信号,输入输出信号简介,具体见表格 42‑4。

表格 42‑4 VGA时序控制模块输入输出信号信号功能描述

信号 位宽 类型 功能描述
tft_clk_9m 1Bit Input 工作时钟,频率9MHz
sys_rst_n 1Bit Input 复位信号,低电平有效
pi_data 16Bit Input 彩条图像像素点色彩信息
pix_x 10Bit Output TFT显示有效显示区域像素点X轴坐标
pix_y 10Bit Output TFT显示有效显示区域像素点Y轴坐标
hsync 1Bit Output 行同步信号
vsync 1Bit Output 场同步信号
rgb_tft 16Bit Output RGB图像色彩信息
tft_bl 1Bit Output TFT显示屏背光信号
tft_clk 1Bit Output TFT显示屏时钟信号
tft_de 1Bit Output TFT显示屏使能信号

输入信号中,时钟信号tft_clk_9m,频率为9MHz,为模块工作时钟,由分频模块产生并输入;复位信号sys_rst_n为顶层模块的rst_n信号输入,低电平有效;pix_data为彩条图像像素点色彩信息,由图像数据生成模块产生并传入,在TFT_LCD显示器有效图像显示区域赋值给信号RGB图像色彩 信息(rgb_tft)。

输出信号(pix_x,pix_y)为TFT_LCD有效显示区域像素点坐标,由TFT_LCD时序控制模块生并传入图像数据生成模块;hsync、vsync为TFT_LCD行、场同步信号 ,通过TFT_LCD接口传输给TFT_LCD显示屏;rgb_tft为显示器要显示的图像色彩信息,传输给TFT_LCD显示器;tft_bl为TFT显示屏背光信号;tft_clk为TFT显示屏工作时钟;tft_de为TFT显示使能信号。

波形图绘制

在模块框图部分,我们对TFT显示时序控制模块的具体功能做了说明,对输入输出信号做了简单介绍,那么如何利用模块输入信号实现模块功能,输出我们想要得到的数据信号呢?在波形图绘制部分,我们会通过绘制波形图,对各信号做详细讲解,带领读者学习掌握模块功能的实现方法。

TFT显示时序控制模块参考波形图,具体见图 42‑11。

图 42‑11 TFT时序控制模块参考波形图

图 42‑11是我们最终绘制生成的TFT时序控制模块参考波形图,下面我们分部分讲解一下波形图绘制的具体思路。

**第一部分:**行同步信号(hsync)、场同步信号(vsync)的波形绘制思路

TFT显示屏想要正确显示图像,行、场同步信号必不可少,前面小节我们对行、场同步信号的时序进行了详细讲解,由时序图可知,行同步信号为周期性信号,信号变化周期为完整的行扫描周期,信号在同步阶段保持高电平,在其他阶段保持低电平,那么如何实现行同步信号的周期性变化呢?

我们想到了前文学过的计数器,因为一个完整行扫描周期为525个像素时钟周期(480*272@60),我们可以利用计数器以像素时钟周期进行计数,每一个像素时钟周期自加1,计数范围为0-524,共计数525次,与完整行扫描周期数相吻合。只要在行同步阶段(计数范围0-40)赋值hsync信号为高电平,其他阶 段为低电平,就可以实现符合时序要求的行同步信号hsync。根据此设计思路,声明并绘制行扫描周期计数器cnt_h、行同步信号hsync信号波形如下:

图 42‑12 cnt_h、hsync信号波形图

同理,参考行同步信号波形的绘制思路,我们可以进行场同步信号波形的绘制,不过读者要注意的是,场扫描周期单位不是像素时钟周期,而是完整的行扫描周期, 所以要添加场扫描周期计数器对行扫描周期进行计数,声明并绘制场扫描周期计数器cnt_v、场同步信号vsync信号波形如下:

图 42‑13 cnt_v、vsync信号波形图

**第二部分:**图像显示有效信号(rgb_valid)波形绘制思路

由上文可知,TFT显示屏只有在有效的显示区域内送入图像数据,图像才会被正确显示,那么在什么时候可以送入图像数据呢?

我们可以声明一个有效信号,在图像有效显示区域赋值高电平,在非图像有效显示区域赋值低电平,以此信号为约束条件,控制图像信号的正确输入,定义此信号为图像显示有效信号(rgb_valid)。

信号已经声明,那么问题来了,如何控制其电平变化,实现预期波形呢?

这里我们可以利用上一部分声明的cnt_h、cnt_v两个计数器,以其为约束条件,当两个计数器计数到图像有效显示区域时,rgb_valid赋值高电平,否则赋值低电平。绘制图像显示有效信号(rgb_valid)波形如下:

图 42‑14 rgb_valid信号波形图

**第三部分:**图像信息请求信号(pix_data_req)、TFT显示屏有效显示区域像素点坐标(pix_x,pix_y)波形绘制思路

为了提高模块复用性,我们将图像数据生成功能独立出来,设计为图像数据生成模块tft_pic,虽然模块复用性提高,但这样就产生一个问题,怎样保证pix_data传输的图像数据与TFT显示屏时序相吻合呢?

结合之前学习的知识,我们知道只有在TFT显示屏有效显示区域,pix_data传输的图像数据才会传输给TFT显示屏,那么我们可以只在TFT显示屏有效显示区域对pix_data进行赋值,如何实现这一想法呢?

我们可以使用cnt_h、cnt_v信号来确定TFT显示屏有效显示区域,将有效显示区域使用坐标法表示,针对不同坐标点对pix_data进行赋值,所以我们声明TFT显示屏有效显示区域像素点坐标(pix_x,pix_y)。

上面两个问题解决了,新的问题又来了,TFT显示屏有效显示区域为480*272,如何使像素点坐标(pix_x,pix_y)实现(0,0) -- (480,272)的坐标计数?

读者可能会想到使用已经声明的图像显示有效信号(rgb_valid),但在此处不能使用该信号。

因为本次实验是TFT显示屏多色彩条的显示,图像数据生成模块tft_pic需要以坐标(pix_x,pix_y)为约束条件对pix_data信号进行赋值,只能使用时序逻辑的赋值方式,那么pix_data的赋值时刻会滞后条件满足时刻一个时钟周期,显示图像会出现问题。

为了解决这一问题,我们需要声明新的图像数据请求信号pix_data_req,该信号要超前图像显示有效信号(rgb_valid)一个时钟周期,以抵消pix_data时序逻辑赋值带来的问题。

综上所述,我们需要声明图像信息请求信号pix_data_req、TFT显示屏有效显示区域像素点坐标(pix_x,pix_y)这三路信号来解决之前提到的若干问题。对于pix_data_req信号的电平控制可参考rgb_valid信号的控制方式,以cnt_h、cnt_v信号为约束条件;坐标(pix_x, pix_y)则以新声明的pix_data_req信号为约束条件控制生成,三路信号绘制波形图如下:

图 42‑15 pix_data_req、 pix_x、pix_y信号波形图

**第四部分:**RGB色彩信息(rgb_tft)波形绘制思路

这一部分就比较简单了,TFT显示屏图像显示是在行、场同步信号的作用下,将图像色彩信息以扫描显示的方式显示出来,所以RGB色彩信息必不可少,只要在有效显示区域写入正确图像数据即可。信号rgb_tft绘制波形如下:

图 42‑16 rgb_tft信号波形图

第五部分: TFT显示数据使能信号(tft_de)波形绘制思路

数据使能信号为DE同步模式下的图像显示同步信号,只在有效图像显示区域为高电平,其他时刻为低电平。tft_de信号的波形变化和rgb_valid信号相同,所以tft_de信号可由rgb_valid信号使用组合逻辑进行赋值。数据使能信号(tft_de)波形如下图。

图 42‑17 tft_de信号波形图

第六部分: TFT显示屏工作时钟(tft_clk)、TFT显示屏背光信号(tft_bl)波形绘制思路

TFT显示屏与VGA显示器不同,TFT显示屏的正常工作离不开时钟信号,而且输入TFT显示屏的时钟信号,要与行场信号或数据使能信号的同步时钟相同,否者会出现图像显示的错误。

TFT显示屏的背光信号作用是控制显示屏背光,为高电平时打开显示器背光,低电平时关闭背光,在本实验工程使用复位信号sys_rst_n信号为背光信号赋值。

上述两信号波形图如下。

图 42‑18 tft_clk、tft_bl信号波形图

各信号波形绘制思路讲解完毕,将所有信号整合后,就是本小节开始部分展示的模块波形图。

本设计思路只做参考,并非唯一方法,读者可利用所学知识,按照自己思路进行设计。

代码编写

参照绘制波形图,编写模块参考代码。模块参考代码,具体见代码清单 42‑1。

代码清单 42‑1 TFT时序控制模块参考代码(tft_ctrl.v)

|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | module tft_ctrl ( input wire tft_clk_9m , //输入时钟,频率9MHz input wire sys_rst_n , //系统复位,低电平有效 input wire [15:0] pix_data , //待显示数据 output wire [9:0] pix_x , //输出有效显示区域像素点X轴坐标 output wire [9:0] pix_y , //输出有效显示区域像素点Y轴坐标 output wire [15:0] rgb_tft , //TFT显示数据 output wire hsync , //TFT行同步信号 output wire vsync , //TFT场同步信号 output wire tft_clk , //TFT像素时钟 output wire tft_de , //TFT数据使能 output wire tft_bl //TFT背光信号 ); //// //\* Parameter and Internal Signal \// //// //parameter define parameter H_SYNC = 10'd41 , //行同步 H_BACK = 10'd2 , //行时序后沿 H_VALID = 10'd480 , //行有效数据 H_FRONT = 10'd2 , //行时序前沿 H_TOTAL = 10'd525 ; //行扫描周期 parameter V_SYNC = 10'd10 , //场同步 V_BACK = 10'd2 , //场时序后沿 V_VALID = 10'd272 , //场有效数据 V_FRONT = 10'd2 , //场时序前沿 V_TOTAL = 10'd286 ; //场扫描周期 //wire define wire rgb_valid ; //VGA有效显示区域 wire pix_data_req ; //像素点色彩信息请求信号 //reg define reg [9:0] cnt_h ; //行扫描计数器 reg [9:0] cnt_v ; //场扫描计数器 //// //\* Main Code \// //// //tft_clk,tft_de,tft_bl:TFT像素时钟、数据使能、背光信号 assign tft_clk = tft_clk_9m ; assign tft_de = rgb_valid ; assign tft_bl = sys_rst_n ; //cnt_h:行同步信号计数器 always@(posedge tft_clk_9m or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_h <= 10'd0 ; else if(cnt_h == H_TOTAL - 1'd1) cnt_h <= 10'd0 ; else cnt_h <= cnt_h + 1'd1 ; //hsync:行同步信号 assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ; //cnt_v:场同步信号计数器 always@(posedge tft_clk_9m or negedge sys_rst_n) if(sys_rst_n == 1'b0) cnt_v <= 10'd0 ; else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL-1'd1)) cnt_v <= 10'd0 ; else if(cnt_h == H_TOTAL - 1'd1) cnt_v <= cnt_v + 1'd1 ; else cnt_v <= cnt_v ; //vsync:场同步信号 assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0 ; //rgb_valid:VGA有效显示区域 assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK) && (cnt_h < H_SYNC + H_BACK + H_VALID)) &&((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_VALID))) ? 1'b1 : 1'b0; //pix_data_req:像素点色彩信息请求信号,超前rgb_valid信号一个时钟周期 assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK - 1'b1) && (cnt_h < H_SYNC + H_BACK + H_VALID - 1'b1)) &&((cnt_v >= V_SYNC + V_BACK) && (cnt_v < V_SYNC + V_BACK + V_VALID))) ? 1'b1 : 1'b0; //pix_x,pix_y:VGA有效显示区域像素点坐标 assign pix_x = (pix_data_req == 1'b1) ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'h3ff; assign pix_y = (pix_data_req == 1'b1) ? (cnt_v - (V_SYNC + V_BACK )) : 10'h3ff; //rgb_tft:输出像素点色彩信息 assign rgb_tft = (rgb_valid == 1'b1) ? pix_data : 16'b0 ; endmodule |

模块参考代码是参照绘制波形图进行编写的,在波形图绘制小节已经对模块各信号有了详细的说明,本小节不再过多叙述。

仿真代码编写

编写仿真代码,对参考代码进行仿真验证。仿真参考代码,具体见代码清单 41‑2。

代码清单 42‑2 TFT显示时序控制模块仿真参考代码(tb_tft_ctrl.v)

|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | \`timescale 1ns/1ns module tb_tft_ctrl(); //// //\* Parameter and Internal Signal \// //// //wire define wire locked ; wire rst_n ; wire tft_clk_9m ; //reg define reg sys_clk ; reg sys_rst_n ; reg [15:0] pix_data ; //// //\* Clk And Rst \// //// //sys_clk,sys_rst_n初始赋值 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; #200 sys_rst_n <= 1'b1; end //sys_clk:产生时钟 always #10 sys_clk = ~sys_clk; //rst_n:VGA模块复位信号 assign rst_n = (sys_rst_n & locked); //pix_data:输入像素点色彩信息 always@(posedge tft_clk_9m or negedge rst_n) if(rst_n == 1'b0) pix_data <= 16'h0; else pix_data <= 16'hffff; //// //\* Instantiation \// //// //------------- clk_gen_inst ------------- clk_gen clk_gen_inst ( .areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit .inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit .c0 (tft_clk_9m ), //输出TFT工作时钟,频率9MHz,1bit .locked (locked ) //输出pll locked信号,1bit ); //------------- tft_ctrl_inst ------------- tft_ctrl tft_ctrl_inst ( .tft_clk_9m (tft_clk_9m), //输入时钟,频率9MHz .sys_rst_n (rst_n ), //系统复位,低电平有效 .pix_data (pix_data ), //待显示数据 .pix_x (pix_x ), //输出TFT有效显示区域像素点X轴坐标 .pix_y (pix_y ), //输出TFT有效显示区域像素点Y轴坐标 .rgb_tft (rgb_tft ), //TFT显示数据 .hsync (hsync ), //TFT行同步信号 .vsync (vsync ), //TFT场同步信号 .tft_clk (tft_clk ), //TFT像素时钟 .tft_de (tft_de ), //TFT数据使能 .tft_bl (tft_bl ) //TFT背光信号 ); endmodule |

仿真波形分析

配置好仿真文件,使用ModelSim对参考代码进行仿真,仿真结果如下。

图 42‑19 TFT时序控制模块整体仿真波形图

图 42‑19为模块传输一帧图像的仿真波形图,因为信号线过于密集,不便讲解说明,我们将列出各信号局部截图进行讲解。

图 42‑20 局部仿真波形图(一)

图 42‑21 局部仿真波形图(二)

图 42‑22 局部仿真波形图(三)

由图 42‑20、图 42‑21、图 42‑22可知,行扫描周期计数器cnt_h在计数范围0-524循环计数,计数周期为像素时钟周期;场扫描周期计数器在计数范围0-285循环计数,计数周期为完整的行扫描周期。

图 42‑23 局部仿真波形图(四)

图 42‑24 局部仿真波形图(五)

图 42‑25 局部仿真波形图(六)

由图 42‑23、图 42‑24、图 42‑25可知,rgb_valid信号只有在图像显示有效区域保持高电平,其他区域为低电平;pix_data_req信号超前rgb_valid信号一个时钟周期;pix_x信号在图像显示有效区域循环计数,计数周期为像素时钟周期,计数范围0-479,计数480次,与图 像行显示有效区域参数一致;rgb信号在rgb_valid信号有效时,被赋值为pix_data,rgb_valid信号无效时,赋值为0;输出TFT数据使能信号tft_de与rgb_valid波形保持一致。

图 42‑26 局部仿真波形图(七)

图 42‑27 局部仿真波形图(八)

由图 42‑26、图 42‑27可知,pix_y信号在图像显示有效区域循环计数,计数周期为完整的pix_x计数周期,计数范围0-271,计数272次。与图像显示场有效区域参数一致。

图 42‑28 局部仿真波形图(九)

图 42‑29 局部仿真波形图(十)

图 42‑30 局部仿真波形图(十一)

图 42‑31 局部仿真波形图(十二)

由图 42‑28、图 42‑29、图 42‑30、图 42‑31可知,行同步信号只有在行同步阶段保持高电平,其他阶段均保持低电平;场同步信号只有在场同步阶段保持高电平,其他阶段均保持低电平。

图 42‑32 局部仿真波形图(十三)

由图 42‑32可以看出,TFT显示屏工作时钟tft_clk与模块系统时钟tft_clk_9m保持一致;TFT显示屏背光信号tft_bl与复位信号sys_rst_n保持一致。

由仿真波形图可知,各信号仿真波形与绘制波形一致,模块通过仿真验证。

13.2.3.4. 图像数据生成模块

本小节中我们开始图像数据生成模块(tft_pic)的介绍,接下来我们会通过模块框图、波形图绘制、代码编写、仿真分析这几个部分,对本模块的设计、实现、仿真验证过程做一下详细介绍。

模块框图

图像数据生成模块,设计本模块的目的是,以TFT显示时序控制模块传入的图像有效显示区域像素点坐标(pix_x,pix_y)为约束条件,产生TFT彩条图像像素点色彩信息并回传给TFT显示时序控制模块。模块框图,具体见图 42‑33。

图 42‑33 图像数据生成模块框图

由图 42‑33可知,图像数据生成模块包含4路输入、1路输出,共5路信号,输入输出信号简介,具体见表格 42‑5。

表格 42‑5 图像数据生成模块输入输出端口功能描述

信号 位宽 类型 功能描述
tft_clk_9m 1Bit Input 工作时钟,频率9MHz
sys_rst_n 1Bit Input 复位信号,低电平有效
pix_x 10Bit Input 有效显示区域像素点X轴坐标
pix_y 10Bit Input 有效显示区域像素点Y轴坐标
pix_data 16Bit Output 彩条图像像素点色彩信息

输入信号中,时钟信号tft_clk_9m,频率为9MHz,为TFT显示屏工作时钟,由分频模块产生并输入;复位信号sys_rst_n为顶层模块的rst_n信号输入,低电平有效;(pix_x,pix_y)为TFT显示屏有效显示区域像素点坐标,由TFT时序控制模块生并输入。

输出信号pix_data为彩条图像像素点色彩信息,在TFT显示屏有效显示区域像素点坐标(pix_x,pix_y)约束下生成,传输到TFT时序控制模块。

波形图绘制

在模块框图部分,我们介绍了图像数据生成模块的具体功能,对输入输出信号做了简单介绍,那么如何利用模块输入信号实现模块功能,输出我们想要得到的数据信号呢?在波形图绘制部分,我们会通过绘制波形图,并对各信号做详细讲解,带领读者学习掌握模块功能的实现方法。

图像数据生成模块波形图,具体见图 42‑34。

图 42‑34 图像数据生成模块波形图

本模块设计较为简单,根据输入像素点坐标(pix_x,pix_y),在有效显示区域,将pix_x计数范围十等分,在不同的计数部分给pix_data赋值对应的色彩信息,因为采用时序逻辑的赋值方式,pix_data滞后pix_x、pix_y信号一个时钟周期。

代码编写

模块波形图绘制完毕后,参照绘制波形图进行参考代码的编写。模块参考代码,具体见代码清单 42‑3。

代码清单 42‑3 图像数据生成模块参考代码(tft_pic.v)

|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | module tft_pic ( input wire tft_clk_9m , //输入工作时钟,频率9MHz input wire sys_rst_n , //输入复位信号,低电平有效 input wire [9:0] pix_x , //输入有效显示区域像素点X轴坐标 input wire [9:0] pix_y , //输入有效显示区域像素点Y轴坐标 output reg [15:0] pix_data //输出像素点色彩信息 ); //// //\* Parameter and Internal Signal \// //// parameter H_VALID = 10'd480 , //行有效数据 V_VALID = 10'd272 ; //场有效数据 parameter RED = 16'hF800, //红色 ORANGE = 16'hFC00, //橙色 YELLOW = 16'hFFE0, //黄色 GREEN = 16'h07E0, //绿色 CYAN = 16'h07FF, //青色 BLUE = 16'h001F, //蓝色 PURPPLE = 16'hF81F, //紫色 BLACK = 16'h0000, //黑色 WHITE = 16'hFFFF, //白色 GRAY = 16'hD69A; //灰色 //// //\* Main Code \// //// //pix_data:输出像素点色彩信息,根据当前像素点坐标指定当前像素点颜色数据 always@(posedge tft_clk_9m or negedge sys_rst_n) if(sys_rst_n == 1'b0) pix_data <= 16'd0; else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1)) pix_data <= RED; else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2)) pix_data <= ORANGE; else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3)) pix_data <= YELLOW; else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4)) pix_data <= GREEN; else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5)) pix_data <= CYAN; else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6)) pix_data <= BLUE; else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7)) pix_data <= PURPPLE; else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8)) pix_data <= BLACK; else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9)) pix_data <= WHITE; else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID)) pix_data <= GRAY; else pix_data <= BLACK; endmodule |

模块参考代码是参照绘制波形图进行编写的,在波形图绘制小节已经对模块各信号有了详细的说明,对各信号介绍不再过多叙述。

本模块不再单独仿真,在后文直接对实验工程整体进行仿真,届时再对本模块信号波形进行分析。

13.2.3.5. 顶层模块

代码编写

实验工程的各子功能模块均已讲解完毕,在本小节对顶层模块做一下介绍。tft_colorbar顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接。代码编写较为容易,无需波形图的绘制。顶层参考代码,具体见代码清单 42‑4。

代码清单 42‑4 顶层模块参考代码(tft_colorbar.v)

|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | module tft_colorbar ( input wire sys_clk , //输入工作时钟,频率50MHz input wire sys_rst_n , //输入复位信号,低电平有效 output wire [15:0] rgb_tft , //输出像素信息 output wire hsync , //输出行同步信号 output wire vsync , //输出场同步信号 output wire tft_clk , //输出TFT时钟信号 output wire tft_de , //输出TFT使能信号 output wire tft_bl //输出背光信号 ); //// //\* Parameter and Internal Signal \// //// //wire define wire tft_clk_9m ; //TFT工作时钟,频率9MHz wire locked ; //PLL locked信号 wire rst_n ; //TFT模块复位信号 wire [9:0] pix_x ; //TFT有效显示区域X轴坐标 wire [9:0] pix_y ; //TFT有效显示区域Y轴坐标 wire [15:0] pix_data ; //TFT像素点色彩信息 //rst_n:TFT模块复位信号 assign rst_n = (sys_rst_n & locked); //// //\* Instantiation \// //// //------------- clk_gen_inst ------------- clk_gen clk_gen_inst ( .areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit .inclk0 (sys_clk ), //输入50MHz晶振时钟,1bit .c0 (tft_clk_9m ), //输出TFT工作时钟,频率9MHz,1bit .locked (locked ) //输出pll locked信号,1bit ); //------------- tft_ctrl_inst ------------- tft_ctrl tft_ctrl_inst ( .tft_clk_9m (tft_clk_9m), //输入时钟,频率9MHz .sys_rst_n (rst_n ), //系统复位,低电平有效 .pix_data (pix_data ), //待显示数据 .pix_x (pix_x ), //输出TFT有效显示区域像素点X轴坐标 .pix_y (pix_y ), //输出TFT有效显示区域像素点Y轴坐标 .rgb_tft (rgb_tft ), //TFT显示数据 .hsync (hsync ), //TFT行同步信号 .vsync (vsync ), //TFT场同步信号 .tft_clk (tft_clk ), //TFT像素时钟 .tft_de (tft_de ), //TFT数据使能 .tft_bl (tft_bl ) //TFT背光信号 ); //------------- tft_pic_inst ------------- tft_pic tft_pic_inst ( .tft_clk_9m (tft_clk_9m), //输入工作时钟,频率9MHz .sys_rst_n (rst_n ), //输入复位信号,低电平有效 .pix_x (pix_x ), //输入TFT有效显示区域像素点X轴坐标 .pix_y (pix_y ), //输入TFT有效显示区域像素点Y轴坐标 .pix_data (pix_data ) //输出像素点色彩信息 ); endmodule |

顶层模块参考代码理解起来较为简单,在此不再过多叙述。

RTL视图

实验工程通过仿真验证后,使用Quartus软件对实验工程进行编译,编译完成后,我们查看一下RTL视图, RTL视图展示信息与顶层模块框图一致,各信号正确连接,具体见图 42‑35。

图 42‑35 RTL视图

13.2.3.6. 仿真验证

仿真代码编写

顶层模块仿真参考代码介绍完毕,开始对顶层模块进行仿真,对顶层模块的仿真就是对实验工程的整体仿真。顶层模块仿真参考代码,具体见代码清单 42‑5。

代码清单 42‑5 顶层模块仿真参考代码(tb_tft_colorbar.v)

|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | \`timescale 1ns/1ns module tb_tft_colorbar(); //// //\* Parameter and Internal Signal \// //// //wire define wire hsync ; wire [15:0] rgb_tft ; wire vsync ; wire tft_clk ; wire tft_de ; wire tft_bl ; //reg define reg sys_clk ; reg sys_rst_n ; //// //\* Clk And Rst \// //// //sys_clk,rst_n初始赋值 initial begin sys_clk = 1'b1; sys_rst_n <= 1'b0; #200 sys_rst_n <= 1'b1; end //clk:产生时钟 always #10 sys_clk = ~sys_clk ; //// //\* Instantiation \// //// //------------- tft_colorbar_inst ------------- tft_colorbar tft_colorbar_inst ( .sys_clk (sys_clk ), //输入工作时钟,频率50MHz .sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效 .rgb_tft (rgb_tft ), //输出像素信息 .hsync (hsync ), //输出行同步信号 .vsync (vsync ), //输出场同步信号 .tft_clk (tft_clk ), //输出TFT时钟信号 .tft_de (tft_de ), //输出TFT使能信号 .tft_bl (tft_bl ) //输出背光信号 ); endmodule |

顶层模块仿真参考代码内部实例化各子功能模块,连接各子功能模块对应信号,模拟产生50MHz时钟信号和复位信号,理解较为容易,不再讲解。

仿真波形分析

使用ModelSim软件对代码进行仿真,tft_ctrl模块已经通过赋值验证,clk_gen为调用IP核,无需仿真,在顶层模块的仿真波形分析,我们只查看tft_pic模块的相关信号,仿真结果如下。

图 42‑36 tft_pic模块仿真波形图

由图 42‑36可知,pix_x信号在图像有效显示区域的完整计数周期被均分为十等份,pix_data在pix_x不同的计数范围内赋值不同的颜色信息。

13.3. 上板验证

13.3.1. 引脚约束

仿真验证通过后,准备上板验证,上板验证之前先要进行引脚约束。工程中各输入输出信号与开发板引脚对应关系如表格 42‑6所示。

表格 42‑6 引脚分配表

信号名 信号类型 对应引脚 备注
sys_clk Input E1 输入系统时钟
sys_rst_n Input M15 复位信号
tft_clk Output L2 时钟信号
tft_de Output K1 使能信号
tft_bl Output L3 背光信号
hsync Output L1 行同步信号
vsync Output K2 场同步信号
rgb_tft [15] Output G1 RGB色彩信息(红)
rgb_tft [14] Output F5 RGB色彩信息(红)
rgb_tft [13] Output F3 RGB色彩信息(红)
rgb_tft [12] Output F2 RGB色彩信息(红)
rgb_tft [11] Output F1 RGB色彩信息(红)
rgb_tft [10] Output E5 RGB色彩信息(绿)
rgb_tft [9] Output D4 RGB色彩信息(绿)
rgb_tft [8] Output J6 RGB色彩信息(绿)
rgb_tft [7] Output K6 RGB色彩信息(绿)
rgb_tft [6] Output L6 RGB色彩信息(绿)
rgb_tft [5] Output J2 RGB色彩信息(绿)
rgb_tft [4] Output J1 RGB色彩信息(蓝)
rgb_tft [3] Output L4 RGB色彩信息(蓝)
rgb_tft [2] Output K5 RGB色彩信息(蓝)
rgb_tft [1] Output G5 RGB色彩信息(蓝)
rgb_tft [0] Output G2 RGB色彩信息(蓝)

下面进行管脚分配,管脚的分配方法在前面章节已有所讲解,在此就不再过多叙述,管脚的分配如下图 42‑37所示。

图 42‑37 管脚分配

13.3.1.1. 结果验证

如图 42‑38所示,开发板连接12V直流电源、USB-Blaster下载器JTAG端口以及TFT_LCD液晶屏。线路正确连接后,打开开关为板卡上电。

图 42‑38 程序下载连线图

如图 42‑39所示,使用"Programmer"为开发板下载程序。

图 42‑39 程序下载图

程序下载完成后,如图 42‑40所示,TFT_LCD液晶屏正确显示彩条,和预期实验效果一致。

图 42‑40 TFT_LCD液晶屏彩条显示效果图

13.4. 章末总结

到这里,本章节讲解完毕,通过实验,相信读者对于TFT液晶屏显示的基本知识和显示时序已经深刻理解,望读者多加练习,掌握TFT显示的相关知识。

13.5. 拓展训练

1、修改代码,在不同的同步模式下实现TFT液晶屏的多色彩条显示。

2、本实验工程实现的是TFT液晶屏纵向彩条显示,读者尝试修改代码实现TFT液晶屏横向彩条显示。

相关推荐
鱼儿也有烦恼7 小时前
快速学完 LeetCode top 1~50 [特殊字符]
java·算法·leetcode·1024程序员节
井队Tell7 小时前
打造高清3D虚拟世界|零基础学习Unity HDRP高清渲染管线(第六天)
1024程序员节
安卓开发者7 小时前
Docker 安装 MongoDB 完整指南:从入门到实战
1024程序员节
爱怪笑的小杰杰7 小时前
Cesium中的倒立四棱锥:从几何结构到交互式3D可视化
javascript·3d·arcgis·1024程序员节
Brookty7 小时前
【算法】前缀和(二)使用
java·学习·算法·前缀和·动态规划·1024程序员节
兜兜风d'7 小时前
基于 Spring Boot + RabbitMQ 实现应用通信
spring boot·rabbitmq·java-rabbitmq·1024程序员节
小范同学_7 小时前
Spring集成WebSocket
java·spring boot·websocket·spring·1024程序员节
进击的圆儿7 小时前
网络编程实战02·从零搭建Epoll服务器
1024程序员节
计算衎7 小时前
Jenkins上实现CI集成软件信息Teams群通知案例实现。
python·jenkins·1024程序员节·microsoft azure·teams消息群通知·微软 graph api