開(kāi)發(fā)環(huán)境:
主機(jī):Ubuntu12.04
開(kāi)發(fā)板:RT5350
Openwrt:Openwrt15.05
1 硬件原理
圖1
由于發(fā)光二級(jí)管單向?qū)щ娞匦裕粗挥性谡螂妷海?a target="_blank">二極管的正極接正,負(fù)極接負(fù))下才能導(dǎo)通發(fā)光。如圖所示,如果 GPIO 輸出高電平,LED 就會(huì)被點(diǎn)亮,如果 GPIO 輸出低電平,LED 就會(huì)熄滅。對(duì)于我們的驅(qū)動(dòng)開(kāi)發(fā),無(wú)論是單片機(jī)、還是 ARM、或者是我們的 MIPS,核心思想,都是讀寫某個(gè)地址,即操作某個(gè)寄存器。
2 寄存器介紹
RT5350 一共有 28 個(gè) GPIO 管腳,這 28 個(gè) GPIO,除了 GPIO0,其他全部是與其他功能引腳復(fù)用的。
表1
GPIO1、GPIO2 與 I2C 復(fù)用。
表2
GPIO3~6 與 SPI 復(fù)用。
表3
GPIO7~14,與 UARTF 即串口 2 復(fù)用。
這些復(fù)用關(guān)系,都可以通過(guò)查閱 RT5350 的芯片手冊(cè)得到。
而這些復(fù)用功能, 我們可以通過(guò) GPIOMODE 寄存器來(lái)進(jìn)行選擇, 通過(guò)查手冊(cè)得知,GPIOMODE寄存器的地址為 0x10000060。
表4
GPIOMODE 寄存器 bit0 位用于選擇 GPIO1、GPIO2 對(duì)應(yīng)的引腳是用于 IIC 總線,還是用于GPIO。
GPIOMODE 寄存器 bit1 位用于選擇 GPIO3~6 對(duì)應(yīng)的引腳用于 SPI 總線,還是用于 GPIO。
GPIOMODE 寄存器 bit2~4 位用于選擇 UARTF 對(duì)應(yīng)的引腳工作于哪個(gè)模塊,具體定義如下。
表5
從該表格可以看出,UARTF 對(duì)應(yīng)的引腳,可以工作于 UARTF、PCM、I2S、GPIO 四種模式,將 GPIOMODE 寄存器 bit24 位設(shè)置為相應(yīng)的值,就能讓這些引腳工作于相應(yīng)的模式,比如將GPIOMODE 寄存器 bit24 位的值設(shè)置為 7,則讓相應(yīng)的引腳工于 GPIO 模式。
當(dāng)將相應(yīng)的引腳設(shè)置為 GPIO 以后,我們接下來(lái)就需要操作該 GPIO 了,操作 GPIO 不外乎就是設(shè)置 GPIO 是輸入還是輸出、讓其輸出高電平還是低電平、讀取其電平狀態(tài)。不管是哪種操作,都有對(duì)應(yīng)的寄存器。
表6
GPIO21_00_DIR 寄存器,用于設(shè)置 GPIO0~21 的方向,當(dāng)相應(yīng)的位被設(shè)置為 1,則表示相應(yīng)的 GPIO 管腳被設(shè)置為了輸出,如果被設(shè)置為了 0,則相應(yīng)的 GPIO 引腳就被設(shè)置為了輸入。
表7
當(dāng)相應(yīng) GPIO 引腳被設(shè)置為輸出時(shí),設(shè)置 GPIO21_00_DATA 寄存器的相應(yīng)位為 1,則讓該GPIO 引腳輸出了高電平,如果設(shè)置 GPIO21_00_DATA 寄存器的相應(yīng)位為 0,則讓該 GPIO 引腳輸出了低電平。
當(dāng)相應(yīng) GPIO 引腳被設(shè)置為輸入時(shí),則通過(guò)讀取 GPIO21_00_DATA 寄存器時(shí),就能讀取相應(yīng)的 GPIO 引腳的狀態(tài)。
關(guān)于更多的 GPIO 操作的寄存器介紹,請(qǐng)自行查閱手冊(cè)。
3 編寫驅(qū)動(dòng)程序
我們通過(guò)前面章節(jié)的學(xué)習(xí),掌握了驅(qū)動(dòng)程序的框架,接下來(lái)我們就來(lái)寫一個(gè)驅(qū)動(dòng)程序,實(shí)現(xiàn)操作 GPIO25、GPIO26 兩個(gè) GPIO 引腳。具體驅(qū)動(dòng)實(shí)現(xiàn)如下。
#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 輸出高電平,同時(shí)熄滅 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:// 點(diǎn)亮 LED1
*GPIO27_22_DATA |= (1< 3);
break;
case MYLEDS_LED1_OFF: // 熄滅 LED1
*GPIO27_22_DATA &= ~(1< 3);
break;
case MYLEDS_LED2_ON:// 點(diǎn)亮 LED2
*GPIO27_22_DATA |= (1