查看: 5508|回复: 4

[原创] RIoTboard连载六 RIoTboard配置和控制GPIO的两种方法

[复制链接]

15

主题

4

好友

903

积分

举人

Rank: 4

  • TA的每日心情
    开心
    2016-9-29 15:49
  • 签到天数: 113 天

    [LV.6]常住居民II

    爱板会员勋章 活跃会员勋章

    发表于 2014-5-10 23:00:41 |显示全部楼层
    有点时间看看驱动了,就多看看了,继上篇imx6 GPIO的主要宏定义和关键函数分析之后(以上分析也是自己查阅源代码得出,有不合适的地方请大家多指出。),我们就按照之前的分析来看看在内核的初始化中添加GPIO控制代码,实现用户LED D45的初步控制,来验证我们之前的分析是否正确。
    方法一:
    首先我们按照在linux下使用GPIO统一编号的方式来控制GPIO,实现LED点亮功能。有原理图知道D45由EIM__A25引脚控制,输出低电平LED亮。那我们就在linux下简单 添加如下代码,具体添加位置在此不多分析,只要能保证执行到就行。那我们就将代码添加在board_mx6q_riot.c中:
    static void mylinuxled_init()
    {
            int mydata;
            printk("----- my led init code-------\n");
            mydata = gpio_direction_output(RIOT_USER_LED,1);
            gpio_set_value(RIOT_USER_LED,0);
            printk("-------my led init finish!\n");
    }
    函数中直接使用linux下的gpio_direction_output和gpio_set_value两个函数来实现GPIO的输出配置和GPIO数据输出配置,在这里就用到了我们上文中提到的Linux下的GPIO统一编号问题,RIOT_USER_LED这个变量的定义就是由
    #define RIOT_USER_LED                IMX_GPIO_NR(5, 2)计算得来,实际就是linux下的GPIO-130。到这儿我们就实现了简单的LED控制代码,然后怎么执行呢,我们将这个函数添加到board_mx6q_riot.c中的mx6_riot_board_init这个板级初始化函数中,这样我们的这个LED控制函数就可以执行了。执行后我们看到串口打印如下:
    ----- my led init code-------
    WARNING: at drivers/gpio/gpiolib.c:101 gpio_ensure_requested+0x4c/0x110()
    autorequest GPIO-130
    Modules linked in:
    [<80045834>] (unwind_backtrace+0x0/0xf8) from [<80070d50>] (warn_slowpath_common+0x4c/0x64)
    [<80070d50>] (warn_slowpath_common+0x4c/0x64) from [<80070dfc>] (warn_slowpath_fmt+0x30/0x40)
    [<80070dfc>] (warn_slowpath_fmt+0x30/0x40) from [<80241fb0>] (gpio_ensure_requested+0x4c/0x110)
    [<80241fb0>] (gpio_ensure_requested+0x4c/0x110) from [<802421c0>] (gpio_direction_output+0x8c/0xf4)
    [<802421c0>] (gpio_direction_output+0x8c/0xf4) from [<8000eb18>] (mx6_riot_board_init+0x184/0x71c)
    [<8000eb18>] (mx6_riot_board_init+0x184/0x71c) from [<80009670>] (customize_machine+0x1c/0x28)
    [<80009670>] (customize_machine+0x1c/0x28) from [<800394e8>] (do_one_initcall+0x34/0x174)
    [<800394e8>] (do_one_initcall+0x34/0x174) from [<80008940>] (kernel_init+0x98/0x13c)
    [<80008940>] (kernel_init+0x98/0x13c) from [<8003faa4>] (kernel_thread_exit+0x0/0x8)
    ---[ end trace 1b75b31a2719ed1c ]---
    -------my led init finish!
    大家看到了初始化函数的打印,系统多了申请GPIO的警告,可能是有些地方还有问题,不过不影响我们的工作,暂时放一放,后续再查找问题所在。同时我们函数是配置GPIO输出低电平,这时候我们可以看到系统进入ubuntu时LED灯D45是亮起来的,而原来系统启动后只有指示系统心跳的LED D46闪烁,D45是灭的,由此可以得知我们的代码配置已经生效了。
    IMG_20140509_235303.jpg

    方法二:
    第二个方法就是直接在linux编写自己的GPIO控制函数,这样的话操作会相对比较麻烦,这样我们先分析具体代码的执行流程
    首先GPIO操作要自己编写函数必须有如下步骤:
    1.我们需要在编写控制函数实现,并在相关头文件中添加函数声明。
    2.实现linux的寄存器地址映射,linux无法直接访问我们要操作的GPIO寄存器,所以要下弄下映射。
    3.编写LED控制程序,调用我们自己实现的GPIO控制函数。
    按照我们上面的步骤首先实现GPIO控制函数,在此我们要提一下imx6的datasheet中关于GPIO控制函数写函数的流程,我们自己实现函数还是要按照手册流程来:
    4.JPG

    看手册内容可以看到要三步走,通过IOMUX配置引脚为GPIO功能,然后写GDIR方向寄存器,配置为输出,最后在想DR寄存器中写数据,输出到GPIO引脚上。
    第一步:
    既然如此那我们就按照流程实现GPIO控制函数,因为按照freescale编程规律,将GPIO操作函数都放在IOMUX-V3.c中,所以我们也将我们的函数实现放在这个文件中,具体代码如下:
    static void __iomem *myiobase;//定义iomem变量
    void set_gpio_output_val(unsigned gpiogroup,unsigned mask,unsigned val)
    {
            unsigned reg = __raw_readl(myiobase+0x4000*(gpiogroup-1)+ 0x00);
            if (val & 1)
                    reg |= mask;    /* set high */
            else
                    reg &= ~mask;   /* clear low */
            __raw_writel(reg, myiobase+0x4000*(gpiogroup-1) + 0x00);
            reg = __raw_readl(myiobase+0x4000*(gpiogroup-1) + 0x04);
            reg |= mask;            /* configure GPIO line as output */
            __raw_writel(reg, myiobase+0x4000*(gpiogroup-1) + 0x04);
    }
    具体参数意义,等调用的时候再解释。好了有了函数体,就在IOMUX-V3.H中添加个声明就可以了。这里就不粘贴了。
    第二步:实现IO的映射:
    IO的映射相对好实现,主要是将GPIO的基地址弄出来就行了。
    上面函数中我们已经声明了iomem变量myiobase,下面就是添加映射:
    编写如下函数
    void mymxc_io_v3_init(void __iomem *myio_v3_base)
    {
            myiobase = myio_v3_base;
    }
    目的是实现myiobase地址的获取,这个函数放在/arch/arm/mach-mx6/mm.c文件中的mx6_map_io函数中
    mymxc_io_v3_init(IO_ADDRESS(GPIO1_BASE_ADDR));
    其中IO_ADDRESS是个宏定义,功能是实现IMX6的外设物理地址和虚拟地址的映射,这样有了这个函数我们就可以访问到GPIO寄存器来控制GPIO了。
    第三步:
    编写LED控制程序,调用我们自己编写的GPIO控制函数实现GPIO控制。
    我们编写代码如下:
    static void mygpioled_init()
    {
            printk("----- my led init code-------\n");
            set_gpio_output_val(5,(1<<2),0);
            mdelay(1000);
            set_gpio_output_val(5,(1<<2),1);
            printk("-------my led init finish!\n");
    }
    调用set_gpio_output_val函数,实现GPIO_5_2的控制,设置输出为低电平,LED点亮,然后延时1s,设置GPIO输出高电平,LED熄灭。同时串口打印函数执行消息。将此函数放在board_init函数中,编译烧写运行就可以看到板子上的D45LED闪亮1s钟后熄灭。到现在为止我们已经自己编写程序控制GPIO了,但是现在好像还少点什么,大家都看到了我们工作过程只是用到了GPIO控制函数,但是我们多次说过freescale的具有复用功能的引脚控制首先要通过IOMUX来配置GPIO,然后才可以通过控制GPIO的寄存器来配置引脚,但是我们好像没有做这一步,就直接控制GPIO引脚了,怎么也没问题呢。其实这个问题在于,源码中已经有了EIM_A25这个引脚的IOMUX的配置函数,让我们看看是如何实现的:
    首先我们知道freescale关于GPIO的操作函数都在IOMUX-V3.C中,那么这个文件中有哪些函数呢,我们首要关注的是两个函数:
    int mxc_iomux_v3_setup_pad(iomux_v3_cfg_t pad);//配置单个引脚iomux配置函数,参数是一个iomux_v3_cfg_t
    int mxc_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t *pad_list, unsigned count);//可以配置多个引脚的iomux配置函数
    它的参数是pad_list就是需要配置的引脚列表和引脚数目,其实这个函数的实现很简单就是一个对setup_pad函数的循环调用count次实现多个配置。
    大家是不是很熟悉,又见到了iomux_v3_cfg_t 这个数据。那么初始化流程是如何的呢
    我们回到board_init函数,其中对mxc_iomux_v3_setup_multiple_pads 函数进行了如下调用:
    5.JPG

    其中mx6dl_riot_pads是个包含 我们RIoTboard上需要配置所有引脚的iomux_v3_cfg_t 数据
    而ARRAY_SIZE(mx6dl_riot_pads)就是这个结构体的大小,即包含引脚的数目,然后进行循环多次调用IOMUX配置函数配置引脚
    关于mx6dl_riot_pads这个结构体的定义可以在对应的头文件中找到,能看到LED功能引脚
    /* LED */
            MX6DL_PAD_EIM_A25__GPIO_5_2,
            MX6DL_PAD_EIM_D28__GPIO_3_28,
    相信看到这个大家就熟悉了,这个正好是通过我们上篇文章介绍的IOMUX的重要宏定义IOMUX_PAD的得来的。这样我们的GPIO控制功能流程和实现就比较清晰明白了



    回复

    使用道具 举报

    78

    主题

    21

    好友

    6227

    积分

    状元

    Rank: 6Rank: 6

  • TA的每日心情
    奋斗
    2016-7-29 22:00
  • 签到天数: 726 天

    [LV.9]以坛为家II

    爱板会员勋章 活跃会员勋章 发帖机器勋章

    发表于 2014-5-10 23:13:36 |显示全部楼层
    受益颇深!
    回复

    使用道具 举报

    12

    主题

    1

    好友

    994

    积分

    举人

    Rank: 4

  • TA的每日心情
    开心
    2016-9-12 10:26
  • 签到天数: 12 天

    [LV.3]偶尔看看II

    发表于 2014-5-19 23:04:46 |显示全部楼层
    讲的很好啊,初学LINUX 驱动,谢谢
    回复

    使用道具 举报

    67

    主题

    14

    好友

    5038

    积分

    状元

    Rank: 6Rank: 6

  • TA的每日心情
    奋斗
    2015-10-8 09:49
  • 签到天数: 430 天

    [LV.9]以坛为家II

    爱板会员勋章

    发表于 2014-6-3 15:06:27 |显示全部楼层
    写的相当专业!
    回复

    使用道具 举报

    0

    主题

    0

    好友

    5

    积分

    白丁

    Rank: 1

    该用户从未签到

    发表于 2014-6-3 16:49:22 |显示全部楼层
    写的不错!
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    关闭

    站长推荐上一条 /6 下一条


    手机版|爱板网 |网站地图  

    GMT+8, 2016-12-4 08:05 , Processed in 0.146965 second(s), 14 queries , Memcache On.

    苏公网安备 32059002001056号

    Powered by Discuz!

    回顶部