开发环境:
主机:Ubuntu12.04
开发板:RT5350
Openwrt:Openwrt15.05
1 硬件原理
图1
由于发光二级管单向导电特性,即只有在正向电压(二极管的正极接正,负极接负)下才能导通发光。如图所示,如果 GPIO 输出高电平,LED 就会被点亮,如果 GPIO 输出低电平,LED 就会熄灭。对于我们的驱动开发,无论是单片机、还是 ARM、或者是我们的 MIPS,核心思想,都是读写某个地址,即操作某个寄存器。
2 寄存器介绍
RT5350 一共有 28 个 GPIO 管脚,这 28 个 GPIO,除了 GPIO0,其他全部是与其他功能引脚复用的。
表1
GPIO1、GPIO2 与 I2C 复用。
表2
GPIO3~6 与 SPI 复用。
表3
GPIO7~14,与 UARTF 即串口 2 复用。
这些复用关系,都可以通过查阅 RT5350 的芯片手册得到。
而这些复用功能, 我们可以通过 GPIOMODE 寄存器来进行选择, 通过查手册得知,GPIOMODE寄存器的地址为 0x10000060。
表4
GPIOMODE 寄存器 bit0 位用于选择 GPIO1、GPIO2 对应的引脚是用于 IIC 总线,还是用于GPIO。
GPIOMODE 寄存器 bit1 位用于选择 GPIO3~6 对应的引脚用于 SPI 总线,还是用于 GPIO。
GPIOMODE 寄存器 bit2~4 位用于选择 UARTF 对应的引脚工作于哪个模块,具体定义如下。
表5
从该表格可以看出,UARTF 对应的引脚,可以工作于 UARTF、PCM、I2S、GPIO 四种模式,将 GPIOMODE 寄存器 bit24 位设置为相应的值,就能让这些引脚工作于相应的模式,比如将GPIOMODE 寄存器 bit24 位的值设置为 7,则让相应的引脚工于 GPIO 模式。
当将相应的引脚设置为 GPIO 以后,我们接下来就需要操作该 GPIO 了,操作 GPIO 不外乎就是设置 GPIO 是输入还是输出、让其输出高电平还是低电平、读取其电平状态。不管是哪种操作,都有对应的寄存器。
表6
GPIO21_00_DIR 寄存器,用于设置 GPIO0~21 的方向,当相应的位被设置为 1,则表示相应的 GPIO 管脚被设置为了输出,如果被设置为了 0,则相应的 GPIO 引脚就被设置为了输入。
表7
当相应 GPIO 引脚被设置为输出时,设置 GPIO21_00_DATA 寄存器的相应位为 1,则让该GPIO 引脚输出了高电平,如果设置 GPIO21_00_DATA 寄存器的相应位为 0,则让该 GPIO 引脚输出了低电平。
当相应 GPIO 引脚被设置为输入时,则通过读取 GPIO21_00_DATA 寄存器时,就能读取相应的 GPIO 引脚的状态。
关于更多的 GPIO 操作的寄存器介绍,请自行查阅手册。
3 编写驱动程序
我们通过前面章节的学习,掌握了驱动程序的框架,接下来我们就来写一个驱动程序,实现操作 GPIO25、GPIO26 两个 GPIO 引脚。具体驱动实现如下。
#include < linux/mm.h >
#include < linux/miscdevice.h >
#include < linux/slab.h >
#include < linux/vmalloc.h >
#include < linux/mman.h >
#include < linux/random.h >
#include < linux/init.h >
#include < linux/raw.h >
#include < linux/tty.h >
#include < linux/capability.h >
#include < linux/ptrace.h >
#include < linux/device.h >
#include < linux/highmem.h >
#include < linux/crash_dump.h >
#include < linux/backing-dev.h >
#include < linux/bootmem.h >
#include < linux/splice.h >
#include < linux/pfn.h >
#include < linux/export.h >
#include < linux/io.h >
#include < linux/aio.h >
#include < linux/kernel.h >
#include < linux/module.h >
#include < asm/uaccess.h >
#define MYLEDS_LED1_ON 0
#define MYLEDS_LED1_OFF 1
#define MYLEDS_LED2_ON 2
#define MYLEDS_LED2_OFF 3
volatile unsigned long *GPIOMODE;
volatile unsigned long *GPIO27_22_DIR;
volatile unsigned long *GPIO27_22_DATA;
static struct class *myleds_class;
static int myleds_open(struct inode *inode, struct file *file)
{
/* 让 GPIO#25、GPIO#26 输出高电平,同时熄灭 LED1、LED2 */
*GPIO27_22_DATA &= ~((1< < 3)|(1< < 4));
return 0;
}
static long myleds_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case MYLEDS_LED1_ON:// 点亮 LED1
*GPIO27_22_DATA |= (1< < 3);
break;
case MYLEDS_LED1_OFF: // 熄灭 LED1
*GPIO27_22_DATA &= ~(1< < 3);
break;
case MYLEDS_LED2_ON:// 点亮 LED2
*GPIO27_22_DATA |= (1