有空了,先先来个简单的u-boot上实现LED控制
软件环境: 笔记本一台,安装WindowsXP sp3
XP下软件:Source Insight 3.5;SecureCRT;VMware Workstation7.0
虚拟机中安装ubuntu10.04
ubuntu中软件:Vim;编译工具链 arm-none-eabi- 版本4.7.2
硬件环境:cubieboard,淘宝自带的串口线和电源线
microSD卡一只,读卡器一个
准备工作:U-boot实现SD卡启动,请参考另一篇文章 http://blog.csdn.net/andy_wsj/article/details/8515197
CB的原理图
A10用户手册
准备妥当,开工
这应该是很简单的工作,并非不熟悉GPIO的使用,只是对u-boot不熟悉而已。
在XP下用Source Insight 3.5建立u-boot代码查阅工程,可以迅速的定位一段代码在U-boot中的位置,快速查找自己不熟悉的调用关系。
SecureCRT就是超级终端,由于虚拟机连接USB转串口常常出这样那样的问题,我就直接在XP下使用SecureCRT代替minicom了,XP还需要安装一个USB转串口的驱动,这些都很简单。网上搜索一下,很多例子。
既然是控制LED,直接就在u-boot代码查阅工程中搜索"LED"或"led",出来一大片结果,逐个观察,最后可以定位通用的LED控制代码在文件:
/u-boot-sunxi-sunxi/drivers/misc/status_led.c阅读这个文件,头文件包含status_led.h,这个头文件等下再看看
调用的函数有__led_init,__led_toggle,__led_set,搜索这几个函数,定位在文件:
/u-boot-sunxi-sunxi/drivers/misc/gpio_led.c
于是我认为,如果需要实现LED控制,则将这两个文件修改成适合cubieboard即可。
1、编译细节处理
以上两个文件需要编译到u-boot里面去,看一下目录/u-boot-sunxi-sunxi/drivers/misc/的Makefile文件内容:
.......include $(TOPDIR)/config.mk
LIB := $(obj)libmisc.o
COBJS-$(CONFIG_ALI152X) += ali512x.o
COBJS-$(CONFIG_DS4510) += ds4510.o
COBJS-$(CONFIG_FSL_LAW) += fsl_law.o
COBJS-$(CONFIG_GPIO_LED) += gpio_led.o
COBJS-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o
COBJS-$(CONFIG_NS87308) += ns87308.o
COBJS-$(CONFIG_PDSP188x) += pdsp188x.o
COBJS-$(CONFIG_STATUS_LED) += status_led.o
COBJS-$(CONFIG_TWL4030_LED) += twl4030_led.o
COBJS-$(CONFIG_PMIC) += pmic_core.o
COBJS-$(CONFIG_DIALOG_PMIC) += pmic_dialog.o
.......
可以看出,编译这两个文件需要定义两个宏
CONFIG_GPIO_LED
CONFIG_STATUS_LED
2、函数调用细节
仔细阅读gpio_led.c代码,调用了gpio_direction_output,gpio_set_value,gpio_get_value三个函数,在工程内搜索一下,很容易找到相关文件就是
/u-boot-sunxi-sunxi/arch/arm/cpu/armv7/sunxi/gpio.c
这几个函数都已经实现了,因此只要调用合适即可。再来看看status_led.c内函数的调用情况,要实现LED闪烁,只要调用status_led_tick函数
搜索status_led_tick函数,可以看见其他平台的实现,都是在定时器中断中调用而手上的u-boot没有实现定时器中断,因此需要自己写一部分代码,这个留到下一次学习中断时进行。
如果不使用中断,最好的莫过于使用命令来控制了。到目录/u-boot-sunxi-sunxi/common看一下,
发现已经存在一个文件cmd_led.c,打开看看头文件和宏定义,如果想直接使用这个文件,再看看目录下的Makefile,那么需要定义
CONFIG_CMD_LED
CONFIG_BOARD_SPECIFIC_LED
CONFIG_BOARD_SPECIFIC_LED这个宏的意思是板上特别的LED,也许是区别那些标准LED例如键盘的LED,
不管怎样,决定使用CONFIG_BOARD_SPECIFIC_LED,这个宏在status_led.h内也会使用,定义了这个宏,
status_led.h内就定义了几个函数原型。
如此一来,在/u-boot-sunxi-sunxi/include/configs/sunxi-common.h最后加上下面这几行:
/*LED*/
#define CONFIG_CMD_LED //使用LED命令,即编译cmd_led.c
#define CONFIG_BOARD_SPECIFIC_LED //板载特别LED
#define CONFIG_GPIO_LED //LED的IO操作,即编译../driver/misc/gpio_led.c
#define CONFIG_STATUS_LED //LED的状态控制操作,即编译../driver/misc/status_led.c,
实际上./driver/misc/status_led.c这个文件没有使用到,
但是我计划在下一步打开定时器中断时使用这个文件内的函数,因此先编译进来。
这样编译会报错,因为还有一些LED相关的宏没有定义,定义在何处呢?
经过观察,我发现其他开发板都定义在status_led.h内,因此我也打算这样做
进入status_led.h,发现正好使用了CONFIG_BOARD_SPECIFIC_LED这个宏做条件编译
偷一下懒,status_led.h文件内如下定义之:
#elif defined(CONFIG_BOARD_SPECIFIC_LED)
#define STATUS_LED_BIT ((7 << 5) + 20) /*PH20, gpio group 7*/
#define STATUS_LED_PERIOD (CONFIG_SYS_HZ / 10)
#define STATUS_LED_STATE STATUS_LED_BLINKING
#define STATUS_LED_BIT1 ((7 << 5) + 21) /*PH21*/
#define STATUS_LED_PERIOD1 (CONFIG_SYS_HZ / 10)
#define STATUS_LED_STATE1 STATUS_LED_BLINKING
/* led_id_t is unsigned long mask */
typedef unsigned long led_id_t;
extern void __led_toggle (led_id_t mask);
extern void __led_init (led_id_t mask, int state);
extern void __led_set (led_id_t mask, int state);
解释一下这个宏:
#define STATUS_LED_BIT ((7 << 5) + 20) /*PH20, gpio group 7*/ 7左移5表示:7乘以32
看看原理图,LED画在PH20和PH21上,同过IO控制三极管导通,进而控制LED状态
这时候需要看看A10的用户手册,“Port Controller”那一节,若要获得PH的控制寄存器首地址,需要 n*0x24 + 0x00
而PH是第7组IO,0x24就是十进制32,因此需要 7 << 5
加20表示是第PH20,如果是PH21就加21,当使用这个宏的时候,代码是反过来解析使用的,
阅读/u-boot-sunxi-sunxi/arch/arm/cpu/armv7/sunxi/gpio.c,就可以看到这个数据使用的方法了。
编译,按SD卡启动方式写入SD卡,上电之后输入命令
led 0 on
led 0 off
led 1 on
led 1 off
led all toggle
发现没有反应,这曾然我十分迷惑,不停的去A10用户手册找原因,以为是某个寄存器设置错误
最后发现是初始化没有调用,因此在文件cmd_led.c里面还需要调用一下初始化,在函数 do_led 中加入红色部分代码即可
加入之后再执行上述命令,就可以控制两个LED了
LED的控制很简单,但是从这一个例子,可以学会在U-boot中对所有GPIO的功能设置方法
其他的东西,比如nand模块的使用,跟LED方法一样
接下来,将定时器中断模块加入u-boot,使用上面这些代码,就可以实现跑马灯效果啦