Am3358增加Uboot的logo显示 & 增加Uboot自定义命令控制LCD

本文的内容如下:

一. 简单介绍一下AM335x的时钟域的概念,然后讲解如何配置LCDC的时钟

二. 讲解LCDC的寄存器的内容和如何根据所选的LCD屏的特性进行时序上的配置

三. 介绍用bmpToRaster tools 将任意图片生成十六进制的数据数组

四. 在新版本的processor SDK v3.0中,在uboot阶段实现增加LCD 的logo显示

五. 基于第四点内容,增加Uboot的命令,效果是在Uboot进入命令模式,使用自定义的命令控制LCD屏的开、关和复位

AM335x 在Uboot 增加自定义的命令控制LCD 功能
--Eggsy Pang
本文的内容如下:
一. 简单介绍一下AM335x 的时钟域的概念,然后讲解如何配置LCDC 的时钟
二. 讲解LCDC 的寄存器的内容和如何根据所选的LCD 屏的特性进行时序上的配置
三. 介绍用tool 将任意图片生成十六进制的数据数组
四. 在新版本的processor SDK v3.0 中,在uboot 阶段如何实现增加LCD logo 的显示
五. 基于第四点内容,增加Uboot 的命令,效果是在Uboot 进入命令模式,使用自定
义的命令控制LCD 屏的开、关和复位
如果对AM335x 的LCD 时序以及LCD 配置流程清楚的读者,可以跳过一二三章的内容,直接阅
读四五章LCD 在Linux 的Uboot 阶段的配置流程。
一.时钟域的配置 & LCD 时钟源的选择
首先介绍一下时钟域的概念。时钟域的作用是为了更好地管理各个外设模块,让它们能够独
立的控制又能够互相的沟通与合作。说的有点正经,简单来说就是,时钟域就相当于由很多开关
组成,时钟的总开关,模块唤醒开关,睡眠开关等等,比如不用的模块就把对应的开关关闭,但
又不影响其他模块的正常工作,比如需要唤醒某个模块,那就开启唤醒开关,这样做是为了达到
低功耗、灵活使能、独立控制的效果。
举个例子吧,比如LCD 控制器和DDR 这两个模块,DDR 存放着LCD 要显示的图片数据,但是
在某个时刻我们不需要LCD 显示,为了低功耗,我们会把LCD 的功能关掉,但是DDR 模块依然需
要正常工作,保留数据,这时候时钟域就发挥作用了,仅仅把LCD 的时钟关闭,DDR 的时钟依然
存在。上面所说的模块时钟叫做Function Clock,主要是为模块正常工作提供时钟源。那DDR 和
LCD 怎么沟通呢,也就是说如何把DDR 保存的图片数据传给LCD 控制器呢?无非就是DDR 往相应
的buffer 填数据,LCD 控制器从buffer 里取数据,很明显,这个过程需要clock 来进行,这个
一.时钟域的配
置 & LCD 时钟源
的选择
二.LCD 控制寄存
器的配置(时序、
LCD 工作模式、
DMA)
三. 一副图片如
何生成一个十六
进制的数组
四.在Uboot 里
增加LCD 显示功

五.通过添加
Uboot 的自定义命
令来控制LCD
clock也是时钟域提供的,这种clock就叫做interface clock,主要功能是为模块内部沟通提供时钟源。
那么如何配置LCD的时钟域呢?也就是说怎么找对应的时钟开关呢?看下图:
(摘取AM335x Technical Reference Manual)
很清晰的看到,蓝色圈圈,也即LCD控制器所在地方,它挂在L3s的L4 PER上,换句话说,L3的时钟域包含了L3s的时钟域,L3s的时钟域又包含L4 PER(又名L4LS)的时钟域,L4 PER的时钟域包含了LCD控制器的时钟域,所以我们要使能LCD控制器的模块和它的时钟,首先要使能L3的clock和模块,再使能L3s的模块和clock,然后使能L4 PER的clock和模块,最后为LCD控制器选择clock。就像一层层地往下打开开关,一层一层的往下使能,是不是很像打开一个多重文件夹一样,那么如何使能呢?很简单,就是在相应的寄存器的某个位上写“1”。这些寄存器大部分位于PRCM的CM_PER Registers上,Technical Reference Manual的第八章有详细的介绍,个人觉得这一章节内容相当重要。下面通过LCD时钟初始化程序进行剖析:
void LCDModuleClkConfig(void)
{
// HWREG(y)=x表示往地址为y的寄存器写的值是x
//软件唤醒L3的clock
HWREG(SOC_PRCM_REGS + CM_PER_L3_CLKSTCTRL) |= CM_PER_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP;
//软件唤醒L3s的clock
HWREG(SOC_PRCM_REGS + CM_PER_L3S_CLKSTCTRL) |= CM_PER_L3S_CLKSTCTRL_CLKTRCTRL_SW_WKUP;
//使能L3模块
HWREG(SOC_PRCM_REGS + CM_PER_L3_CLKCTRL) |= CM_PER_L3_CLKCTRL_MODULEMODE_ENABLE;
//软件唤醒L4LS的clock
HWREG(SOC_PRCM_REGS + CM_PER_L4LS_CLKSTCTRL) |= CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL_SW_WKUP;
//使能L4LS模块
HWREG(SOC_PRCM_REGS + CM_PER_L4LS_CLKCTRL) |= CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE;
//此处选择DISP PLL CLKOUTM2 作为LCDC PIXEL的时钟源,LCDC PIXEL的时钟源有三种可选,接下来
//会详细说明
HWREG(SOC_CM_DPLL_REGS + CM_DPLL_CLKSEL_LCDC_PIXEL_CLK) = CM_DPLL_CLKSEL_LCDC_PIXEL_CLK_CLKSEL_SEL1;
//使能LCDC模块
HWREG(SOC_PRCM_REGS + CM_PER_LCDC_CLKCTRL) |= CM_PER_LCDC_CLKCTRL_MODULEMODE_ENABLE;
//前面的L3s L3 L4 L4LS LCDC相关的时钟域唤醒后,while等待这些时钟全部打开
while(!(HWREG(SOC_PRCM_REGS + CM_PER_L3S_CLKSTCTRL) &
CM_PER_L3S_CLKSTCTRL_CLKACTIVITY_L3S_GCLK));
while(!(HWREG(SOC_PRCM_REGS + CM_PER_L3_CLKSTCTRL) &
CM_PER_L3_CLKSTCTRL_CLKACTIVITY_L3_GCLK));
while(!(HWREG(SOC_PRCM_REGS + CM_PER_OCPWP_L3_CLKSTCTRL) &
(CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L3_GCLK |
CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L4_GCLK)));
while(!(HWREG(SOC_PRCM_REGS + CM_PER_L4LS_CLKSTCTRL) &
(CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_L4LS_GCLK |
CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_LCDC_GCLK)));
}
对上述LCDModuleClkConfig函数的红色部分进行说明
我们的AM335x对LCD_CLK的设置,是比较灵活的。因为LCDC的clock有三种时钟源可以选择,如下图所示:
首先看第一幅图的右半边部分PRCM,可以看到可选LCDC_CLK的三种时钟源分别是:
1. PER_CLKOUTM2
基于Starterware的LCD显示例程,就是使用的PER_CLKOUTM2作为时钟源,该时钟为192MHz(其实PER_CLKOUTM2时钟也是可以灵活配置的,但是不推荐,因为它是属于时钟数比较核心的时钟,如果这个一改变,其他的外设也要跟着变,影响比较大,所以对于该路时钟老老实实用192MHz就好,这方面描述可以参考TRM的Table 8-24. Per PLL Typical Frequencies(MHz) ,所以这给很多人的直观印象就是我们的LCD_CLK只能从192MHz分频,事实上还是可以有更多选择的。
2. CORE_CLKOUTM5
这路时钟是250MHz的(类似于PER_CLKOUTM2,也可更改配置但不推荐,参考TRM的Table 8-22. Core PLL Typical Frequencies (MHz)
3. 从Display PLL倍频后获取
这时请看第一幅图的左边Display PLL部分,一般Display PLL会选择外部晶振(比如24MHz晶振) 作为它的时钟源(Resource Clock),然后进行相关的配置,实现倍频,倍频公式:
LCD_CLK= Resource Clock * DPLL_Mult / (N +1) /M2
其中DPLL_Mult、N的值由CM_CLKSEL_DPLL_DISP寄存器决定,如下图所示。M2默认为1。
所以Clock的配置选择是不是很灵活呢。那么如何设置去选取这三个不同的时钟源呢?很简单,在CM_DPLL寄存器(0x44E0_0500)偏移量为34h的CLKSEL_LCDC_PIXEL_CLK寄存器中,1-0位就是对这个时钟的选择配置。可参考上述程序 LCDModuleClkConfig函数的红色部分。
二.LCD控制寄存器的配置(时序、LCD工作模式、DMA)
获取LCDC_CLK后,要和LCD屏沟通,还需要配置
piexl、HSYNC和VSYNC时钟,这三者时钟都带来自于
LCDC_CLK这个时钟源。如右图所示。
那么在哪里进行配置呢?
答案是在LCD控制器的寄存器(地址是0x4830_E000)
上进行配置,如右下图所示,除可配置时钟外
还可以配置DMA、中断等。
配置流程:
1. 首先查找所需要配置的LCD屏的datasheet
2. 从中获取重要参数:piexl时钟的大小,
行宽,列宽,Hsw Hbp hfp vsw Vbp Vsw
来自于上图PRCM的LCD_CLK
摘取某一个LCD屏的datesheet的内容作为配置例子,如下图所示:
下面简单介绍一下这些参数代表什么意思。
首先先介绍TFT-LCD系统的构成。它主要由三部分构成:TFT-LCD控制器、TFT-LCD驱动器和TFT-LCD屏。TFT-LCD控制器就集成在我们的AM335x中的,而购买的LCD屏一般都包含剩下的TFT-LCD驱动器和TFT-LCD屏这两个部分。
我们都知道,一般的TFT-LCD的显示刷新频率是60Hz,也就是每秒钟显示60帧画面。而我们视频播放帧率是23~30fps,我们一般不会利用LCD控制器每秒送60帧图像的RGB信息给LCD,而是每秒钟送20~30帧的RGB信息给LCD,让其显示。其实,我们是通过LCD控制器把RGB数据送给LCD驱动器,LCD驱动器就把它放到缓存中,然后以60fps的速度送给LCD屏显示。可能有人会问,视频帧率有多大,LCD就显示多快,不行吗?不行,由于液晶分子有一种特性,就是不能够一直固定在某一个电压不变,不然时间久了,液晶分子就遭到了破坏。所以要以一定的频率(通常是60Hz)不停的刷新LCD屏,然后在帧消隐的时间里,变换显示电压极性,达到保护液晶的目的。
对于PAL制式的CRT显示器,帧频是25Hz,场频是50Hz,这时,视频帧率和显示器刷新率是相同的。而我们在嵌入式当中使用TFT-LCD显示器,视频帧率一般是不等于显示器帧率的。所以,LCD控制器时序和LCD驱动器时序(LCD显示扫描时序)虽然表示方法大体相同,但有实质上的区别,LCD控制器时序控制着视频帧率;LCD驱动器时序控制着显示器刷新率。这是大家在学习LCD的时候最容易混淆的地方。
好,如果还对LCD存在疑惑之处,欢迎一起讨论。下面对上述的重要参数进行见解。见下图。其中VSYNC是帧同步信号,VSYNC每发出1个脉冲,都意味着新的1屏视频数据开始发送。而HSYNC为线同步信号,每个HSYNC脉冲都表明新的1扫描线视频数据开始发送。而VDEN则用来标明视频数据有效,VCLK是像数时钟,一个VCLK会带来一位RGB。并且在帧同步以及线同步的头尾都必须留有消隐时间,例如对于VSYNC来说消隐时间是(VSPW+1)+(VBPD+1)+ (VFPD+1);HSYNC亦类同。对于LCD的时序分析,如下图所示:
其中:
VSPW:Vertical sync pulse width,也就是VSYNC处于高电平时的线(HSYNC)的数目 VFPD:Vertical front porch delay VBPD:Vertical back porch delay
HSPW:Hertical sync pulse width,也就是HSYNC处于高电平时的VCLK(像素CLOCK)的数目 HFPD:Hertical front porch delay HBPD:Hertical back porch delay
ok,接着上述的配置流程
3. 最后一个步骤就是对AM335x的LCD控制器的寄存器进行相关操作,让LCDC能够配合LCD屏的特性进行时序上的沟通,通过程序来分析其流程:
上面主要列举一下配置LCDC寄存器常用的函数接口,你可以理解为是API,其实这些API里面很简单,仅仅是对LCDC寄存器的不同偏移量的地方进行相关的赋值,若想知道详细内容,从源代码进行学习(待会我会发布LCD驱动的源代码),结合TRM的第十三章一起分析,效果更佳。
static void SetUpLCD(void)
{
/* 使能相关时钟域,在前面已经分析该函数 */
LCDModuleClkConfig();
/* 相应的引脚分配,可以使用TI的工具Pin mux进行配置,快、准、可视化 */
LCDPinMuxSetup();
/*前面只是使能相关时钟域,还剩下最后一个时钟开关需要打开,该开关控制在LCDC寄存器 */
RasterClocksEnable(SOC_LCDC_0_REGS);
/* 配置pclk等上述所说的重要参数前,需要先关闭LCDC的功能 */
RasterDisable(SOC_LCDC_0_REGS);
/* 配置像素时钟pclk =30M,写到第二参数,第三个参数是LCDC_CLK,这里选择100M */
RasterClkConfig(SOC_LCDC_0_REGS, 30000000, 100000000);
/* 配置LCD控制器的 DMA,此处选择FIFO的阈值是8个字节,DMA双通道乒乓模式,详细请看13章内容 */
RasterDMAConfig(SOC_LCDC_0_REGS, RASTER_DOUBLE_FRAME_BUFFER,
RASTER_BURST_SIZE_16, RASTER_FIFO_THRESHOLD_8,
RASTER_BIG_ENDIAN_DISABLE);
/* 配置LCD的模式:TFT or STN , 彩色 0r 黑白等 */
RasterModeConfig(SOC_LCDC_0_REGS, RASTER_DISPLAY_MODE_TFT_UNPACKED,
RASTER_PALETTE_DATA, RASTER_COLOR, RASTER_RIGHT_ALIGNED);
/* 配置LCDC的CLOCK工作极性,也即帧信号或像素时钟是高电平有效还是低电平有效 ,同步信号是上升沿有效还是下降沿有效*/
RasterTiming2Configure(SOC_LCDC_0_REGS, RASTER_FRAME_CLOCK_LOW |
RASTER_LINE_CLOCK_LOW | RASTER_PIXEL_CLOCK_HIGH|
RASTER_SYNC_EDGE_RISING|RASTER_SYNC_CTRL_ACTIVE|
RASTER_AC_BIAS_HIGH , 0, 255);
/* 配置LCD屏关于水平方向的参数,如上图摘取某LCD datasheet上有写:800个水平像素点,HSPW =48 hfp=40,hbp=40 */
RasterHparamConfig(SOC_LCDC_0_REGS, 800, 48, 40, 40);
/* 配置LCD屏关于垂直方向的参数,如上图摘取某LCD datasheet上有写:480个垂直像素点,VSPW =3,Vfp=13,Vbp=29 */
RasterVparamConfig(SOC_LCDC_0_REGS, 480, 3, 13, 29);
RasterFIFODMADelayConfig(SOC_LCDC_0_REGS, 128);
}
完成上述步骤,基本上已经完成了90%的配置流程,接下来就是,把你想显示的图片数据往LCDC的DMA上扔,然后使能LCD控制器,就可以显示了。那么怎么往DMA扔数据呢?
操作如下:
三. 一副图片如何生成一个十六进制的数组
这里有个小插曲:简单介绍一下如何通过任意一副照片转变成一个图片数据数组image[]。步骤如下:
1. 下载AM335X_StarterWare_02_00_00_07_Setup.exe,链接为:
http://software-dl.ti.com/dsps/dsps_public_sw/am_bu/starterware/latest/index_FDS.html
下载后并安装在自定义目录中。StarterWare里面有很多Tool,还包含了大量的裸机代码。
2. 若安装的是window版本,则找到安装后的目录,把/tools/bmpToRaster拷贝到linux系统下,若是Linux版本,则跳过该步骤。
3. 在BitmapReader.h头文件里屏蔽 #define COMPRESS
4. 在终端进入bmpToRaster目录下,执行make,成功后将会生成a.out
5. 选择一个24位的位图,格式是bmp(若其他格式,请先通过图片工具进行转化),图片的size=800*480(这里只是举个例子,根据你要驱动的屏幕的大小进行裁剪)
6. 将图片名称.bmp拷贝到a.out目录下
7. 终端运行命令:./a.out 800 480 ./图片名称.bmp ./image.h 24 BGR
命令参数解析:800 480 和24 BGR的意思是生成适合LCD屏800*480的图片数据,并且数据格式是888RGB格式,该图片数据以数组形式存在image.h文件里。图片名称.bmp和 image.h,这两个文件的名称可自定义。生成a.out文件后,以后若还需将照片转化成数组形式,只需要执行第五个、第六个和第七个步骤即可。
/* DMA工作在乒乓球模式,双通道传送,DMA0和DMA 1会轮流传送image的数据给LCD显示 ,每个DMA通道每次传送8个字节,image是由图片数据组成的数组*/
RasterDMAFBConfig(SOC_LCDC_0_REGS, (unsigned int)image,
(unsigned int)image4 + sizeof(image) - 2, 0); //DMA0
RasterDMAFBConfig(SOC_LCDC_0_REGS, (unsigned int)image,
(unsigned int)image + sizeof(image) - 2, 1); //DMA1
/* 使能LCDC功能 */
RasterEnable(SOC_LCDC_0_REGS);
四.在Uboot里增加LCD显示功能
Ok ,接下来是重点了,讨论如何在Uboot里面初始化并使能LCD控制器,并让LCD屏显示logo。而且有趣的是,可以进入Uboot的命令控制状态,输入自定义的命令,控制LCD显示或者关闭。
步骤如下:
1. 下载我提供的LCD的drive——文件夹名“lcd”,将其放到u-boot/drivers文件夹里,lcd文件夹里面内容如下图所示。比较重要的是文件rasterDisplay.c。 其他一些.h的头文件里主要是一些宏定义,描述相关的寄存器地址,以下表格有详细说明。
RasterDisplay.c主要函数
int Lcd_Init(void) //其实就是执行上面所说的蓝框的函数
//初始化的有时钟域、LCD控制器的时序和LCDC的DMA模式
void Lcd_reset(void) //软件复位LCD控制器,LCDC寄存器的值恢复为复位的状态
void Lcd_off(void) //关闭LCDC的功能
void Lcd_on(void) //打开LCDC的功能
2. 定义相关宏
rasterDisplay.c
对LCDC初始化,其实里面内容就是我上面介绍的一些配置函数
lcd.c
初始化LCDC所有的引脚功能以及配置LCDC的时钟域
raster.c / raster.c h
其实里面的内容就是我上面所说的配置LCDC的“API”函数
hw_control_Am335x.h
描述Control module寄存器(配置引脚)
hw_cm_per.h
描述时钟域的寄存器
hw_cm_dpll.h
描述与外设的时钟源选择有关的寄存器
image.h
照片生成的数据的头文件,里面是数组image1[]
Makefile
内容 :obj-$(CONFIG_LCD_UBOOT) += rasterDisplay.o lcd.o raster.o
里面就这一句话,将LCDC初始化函数的所在的C文件 编译成相应的.o文件,如果有define CONFIG_LCD_UBOOT ,则该步骤会在make uboot时候自动完成
上面表格所说makefile的内容,需要定义define CONFIG_LCD_UBOOT,相关C文件才会被编译,那么如何增加这个宏定义呢?打开am335x_evm.h (/u-boot /include/configs),然后添加一句话# define CONFIG_LCD_UBOOT,如图所示:
3. 配置Display PLL
如果还记得上面的内容,我们已经选择Display PLL的输出CLKOUT作为LCD_CLK的时钟源。而我提供的lcd drive里面还没实现Display PLL配置,考虑到lcd的时钟源的选择灵活性,所以不在lcd drive里面实现。接下来就是讨论如何怎么在Uboot里面配置Display PLL。
根据公式:LCD_CLK= Resource Clock * DPLL_Mult / (N +1) /M2,配置Display PLL也就是配置DPLL_Mult、N和 M2。此处Resource Clock使用的是24M晶振(蓝椭圆)作为Display PLL的时钟源输入。步骤分为ABCD四个步骤:
A. 打开文件Clock_am33xx.c (uboot\arch\arm\cpu\armv7\am33xx),按照下面位置进行添加(红色标明为新增加,请看紫色注释说明,帮助理解)
#ifndef CONFIG_SPL_BUILD
# define CONFIG_TIMESTAMP
# define CONFIG_LZO
# define CONFIG_LCD_UBOOT
#endif
B. 打开Clock.h (uboot\arch\arm\include\asm\arch-am33xx) ,添加两条语句:
C. 打开Clock.c (uboot\arch\arm\cpu\armv7\am33xx),按照下面位置进行添加(红色标明为新增加,请看紫色注释说明,帮助理解)
const struct dpll_params dpll_per = {
960, OSC-1, 5, -1, -1, -1, -1};// 找到这个位置之后进行添加
// 声明dpll_regs结构体 为 dpll_dis_regs,
//将与display PLL相关的寄存器地址 赋值给该结构体的参数:
const struct dpll_regs dpll_dis_regs = {
.cm_clkmode_dpll = CM_WKUP + 0x98,
.cm_idlest_dpll = CM_WKUP + 0x48,
.cm_clksel_dpll = CM_WKUP + 0x54,
.cm_div_m2_dpll = CM_WKUP + 0xA4,
};
// 声明dpll_params结构体为 dpll_dis,取名倍频结构体,对其赋值:
//DPLL_Mult =100 ;N= OSC-1;M2=1, 后面四个-1代表参数无效
//所以根据公式LCD_CLK=100M (OSC=24M为晶振频率)
const struct dpll_params dpll_dis = { 100, OSC-1, 1, -1, -1, -1, -1};
//返回 指向dpll_dis(倍频结构体)的指针
const struct dpll_params *get_dpll_dis_params(void)
{
return &dpll_dis;
}
extern const struct dpll_regs dpll_dis_regs;
const struct dpll_params *get_dpll_dis_params(void);
static void setup_dplls(void)
{
。。。。。
params = get_dpll_ddr_params();
do_setup_dpll(&dpll_ddr_regs, params);
//指向dpll_dis的指针赋值给params
params = get_dpll_dis_params();
//根据A步骤声明的寄存器结构体dpll_dis_regs和倍频结构体指针作为形参
//传参后,该函数会对寄存器进行赋值,达到倍频的效果
//执行成功后,输出LCD_CLK=100M
do_setup_dpll(&dpll_dis_regs, params);
}
D. 打开 Board.c (board\ti\am335x),按照下面位置进行添加(黄色标明为新增加,请看橙色注释说明,帮助理解)
#if !defined(CONFIG_SPL_BUILD)
void lcdbacklight(int gpio, char *name, int val) //该函数使能LCD背光引脚
{
int ret;
ret = gpio_request(gpio, name);
if (ret < 0) {
printf("%s: Unable to request %s\n", __func__, name);
return;
}
ret = gpio_direction_output(gpio, 0);
if (ret < 0) {
printf("%s: Unable to set %s as output\n", __func__, name);
goto err_free_gpio;
}
gpio_set_value(gpio, val);
return;
err_free_gpio:
gpio_free(gpio);
}
void lcdbacklight_off(int gpio) //关闭lcd背光并且关闭LCDC的功能
{
gpio_set_value(gpio,0);
Lcd_off(); //来自lcd drive
return;
}
void lcdbacklight_on(int gpio) //打开lcd背光并且打开LCDC的功能
{
#if !defined(CONFIG_SPL_BUILD)
gpio_set_value(gpio,1);
Lcd_on(); //来自lcd drive
#endif
return;
}
void board_lcd_reset(int gpio) //关闭lcd背光并且软件复位LCDC
{
gpio_set_value(gpio,0);
Lcd_reset();
return;
}#endif
int board_init(void)
{ ……..
#if !defined(CONFIG_SPL_BUILD)
lcdbacklight(7, "lcdbacklight", 1); //使能lcd背光,引脚是GPIO0_7
Lcd_Init();
#endif
………}
E. 打开 Board.h (board\ti\am335x),按照下面位置进行添加
五.通过添加Uboot的自定义命令来控制LCD
最后一部分是讨论怎么添加Uboot的自定义命令,控制LCD。步骤如下:
1. 在u-boot/cmd文件里,添加lcd_cmd.c文件,该文件会提供给大家。
细心的读者可以发现上面D步骤所定义的函数void lcdbacklight_off(int gpio)、void lcdbacklight_on(int gpio))和board_lcd_reset(int gpio)还没有被调用。
其实这些函数将会在lcd_cmd.c文件命令执行函数do_lcd中调用。
static int do_lcd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int cmd;
/* Validate arguments */
if ((argc < 1) || (argc > 2))
return CMD_RET_USAGE;
cmd = get_lcd_cmd(argv[1]); //获取命令类型
if (cmd < 0) {
return CMD_RET_USAGE;
}
#ifdef CONFIG_CMDLINE
if (cmd==0){
printf("lcd off\n");
lcdbacklight_off(7);
}
else if (cmd==1){
printf("lcd on\n");
lcdbacklight_on(7);
}
else if (cmd==2){
printf("lcd reset\n");
board_lcd_reset(7);
}
return 0;
#else
return 1;
#endif
}
int Lcd_Init(void);
void lcdbacklight(int gpio, char *name, int val);
void lcdbacklight_off(int gpio);
void lcdbacklight_on(int gpio);
void board_lcd_reset(int gpio);
2. 打开u-boot/cmd/Makefile文件,在如下位置添加一句obj-y += lcd_cmd.o ,确保lcd_cmd.c文件被编译,如图:
3. 打开u-bootxx/include/config/am335x_evm.h,增加红色部分,执行boot引导kernel的时候,最后把LCDC复位,防止在引导kernel时出现问题,因为kernel也会对LCDC进行初始化。
验证效果:
上面的所有的步骤都完成后,就可以执行make指令,编译Uboot,生成MLO和U-boot.img,copy到SD卡运行,或者通过其他方式加载到板子上,或者通过其他方式bootload。加载u-boot.img后,可以发现此时屏幕已经亮起来并显示logo。
进入uboot命令,输入ULCD –h命令可以看到一些命令帮助,此时先输入ULCD off可以看到屏幕关闭,logo没了,输入ULCD on又可以看到屏幕重新显示logo。
ifndef CONFIG_SPL_BUILD
# core command
obj-y += boot.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
obj-y += help.o
obj-y += lcd_cmd.o
#define CONFIG_BOOTCOMMAND \
"run findfdt; " \
"run init_console; " \
"run envboot; " \
"run RESET_LCD; " \
"run distro_bootcmd"
"RESET_LCD=" \
"ULCD reset\0" \
六.常见问题
温馨提醒:如果Uboot的LCDC_CLK选择PER_CLKOUTM2,而不是选择Display PLL,在kernel加载过程中会出现串口打印乱码、I2C时序错误等的现象。
这是因为kernel配置Per PLL是一个逆过程,它首先根据LCD的pixel clock(假设等于30M)
默认LCDC对时钟源LCDC_CLK进行的是二分频,所以LCDC_CLK=60M,因为Uboot选择PER_CLKOUTM2作为它的时钟源,在kernel会默认此选择,所以PER_CLKOUTM2=LCDC_CLK=60M,以及kerne将PER_CLKOUTM2配置为60M,而其他一些外设默认选择时钟源为PER_CLKOUTM2,且值=192M,但实际上PER_CLKOUTM2=60M,所以时钟源错了,那可想而知其他的外设时钟,如UART、I2C等肯定会出现时序的错乱。

PDF:链接地址

https://e2echina.ti.com/cfs-file.ashx/__key/telligent-evolution-components-attachments/13-121-00-00-00-01-20-79/AM335x-_2857_Uboot_9E58A052EA819A5B494E84767D54E44EA7633652_LCD_9F52FD80_.pdf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值