TA的每日心情 | 开心 2018-10-12 13:33 |
---|
签到天数: 183 天 连续签到: 3 天 [LV.7]常住居民III
|
有点时间看看驱动了,就多看看了,继上篇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是灭的,由此可以得知我们的代码配置已经生效了。
方法二:
第二个方法就是直接在linux编写自己的GPIO控制函数,这样的话操作会相对比较麻烦,这样我们先分析具体代码的执行流程
首先GPIO操作要自己编写函数必须有如下步骤:
1.我们需要在编写控制函数实现,并在相关头文件中添加函数声明。
2.实现linux的寄存器地址映射,linux无法直接访问我们要操作的GPIO寄存器,所以要下弄下映射。
3.编写LED控制程序,调用我们自己实现的GPIO控制函数。
按照我们上面的步骤首先实现GPIO控制函数,在此我们要提一下imx6的datasheet中关于GPIO控制函数写函数的流程,我们自己实现函数还是要按照手册流程来:
看手册内容可以看到要三步走,通过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 函数进行了如下调用:
其中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控制功能流程和实现就比较清晰明白了
|
|