cubieboard上通过U-boot点亮板载LED

有空了,先先来个简单的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  中加入红色部分代码即可
int do_led (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
        int i, match = 0;
        enum led_cmd cmd;
         static int led_init_flag = 0;

        /* Validate arguments */
        if ((argc != 3)) {
                return CMD_RET_USAGE;
        }

        cmd = get_led_cmd(argv[2]);
        if (cmd < 0) {
                return CMD_RET_USAGE;
        }

        if( led_init_flag == 0)
        {
                for(i=0; led_commands[i].mask; i++){
                        __led_init(led_commands[i].mask, 0);
                }
                led_init_flag = 1;
        }
........


加入之后再执行上述命令,就可以控制两个LED了


LED的控制很简单,但是从这一个例子,可以学会在U-boot中对所有GPIO的功能设置方法

其他的东西,比如nand模块的使用,跟LED方法一样

接下来,将定时器中断模块加入u-boot,使用上面这些代码,就可以实现跑马灯效果啦






你可能感兴趣的:(u-boot学习)