Davinci DM6446開發攻略——LINUX GPIO驅動源碼移植
一、 DM6446 GPIO的介紹
說到LINUX 驅動移植,沒有移植過的朋友,或剛剛進入LINUX領域的朋友,最好去看看《LINUX 設備驅動程序》第三版,有個理論或感性的認識。該版本是基於2.6.10的基礎上描述的,經典讀物,網上有電子版,但是建議花幾十元買本書是值得的。
GPIO是嵌入式係統最簡單、最常用的資源了,比如點亮LED,控製蜂鳴器,輸出高低電平,檢測按鍵,等等。GPIO分輸入和輸出,在Montavista linux-2.6.18中,有關GPIO的最底層的寄存器驅動,是在linux-2.6.18_pro500\arch\arm\mach-davinci目錄下的gpio.c,這個是寄存器級的驅動,搞過單片機MCU的朋友應該比較熟悉寄存器級的驅動。根據DM6446的芯片DATASHEET,DM6446的GPIO分為3組BANK,BANK01組包括GPIO0~GPIO31,BANK23組包括GPIO32~GPIO63,BANK45組包括GPIO64~GPIO70,由於硬件資源的原因,DM6446並不是GPIO管腳就是純粹的GPIO腳,GPIO管腳和其他一些標準接口複用相同的引腳,比如SPI和GPIO複用,I2C和GPIO複用等,到底是使用GPIO還是其他接口,在初始化的時候,都需要對PINMUX0和PINMUX1兩個寄存器進行設置(見DM6446的芯片DATASHEET第3章),而軟件設置則在Montavista linux-2.6.18_pro500\arch\arm\mach-davinci目錄下mux_cfg.c和對應的mux.h裏。本人這裏使用GPIO10、GPIO12、GPIO28,分別對應控製BUZZER、LED1、LED0,所以不需要對mux_cfg.c和mux.h進行修改。我們把這些GPIO應用歸入linux字符設備來移植。
二、GPIO源碼移植分析
/* drivers/char/davinci_dm644x_gpios.c*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#define DEVICE_NAME "dm644x_gpios" /*定義設備驅動的名字,或設備節點名稱*/
#define GPIO_MAJOR 199 /*使用 cat /proc/devices查看不要和存在的char節點重複*/
/*my app gpio define*/
#define DM644X_GPIO_BUZZER 10 /*GPIO10*/
#define DM644X_GPIO_LED1 12 /*GPIO10*/
#define DM644X_GPIO_LED0 28 /*GPIO10*/
static int davinci_dm644x_gpio_open(struct inode *inode, struct file *file)
{
return 0;/*該函數可以什麼都不做,也可以加入類似初始化的設置*/
}
static int davinci_dm644x_gpio_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) /*cmd 表示應用程序傳入的led動作,是on 還是off*/
{
case 0: //gpio = 0
if(0==arg) /*arg由自己硬件電路決定使用那些GPIO*/
{
gpio_direction_output(DM644X_GPIO_LED0, 0);/*調用TI linux-2.6.18寄存器驅動*/
}
else if(1==arg)
{
gpio_direction_output(DM644X_GPIO_LED1, 0);
}
else if(2==arg)
{
gpio_direction_output(DM644X_GPIO_BUZZER, 0);
}
else
{
return -EINVAL;
}
break;
case 1: //gpio = 1
if(0==arg)
{
gpio_direction_output(DM644X_GPIO_LED0, 1);
}
else if(1==arg)
{
gpio_direction_output(DM644X_GPIO_LED1, 1);
}
else if(2==arg)
{
gpio_direction_output(DM644X_GPIO_BUZZER, 1);
}
else
{
return -EINVAL;
}
break;
default:
return -EINVAL;
}
}
/*定義驅動設備文件API,在linux係統當中,任何設備都可以當做文件的方式操作,這一點和單片機和MCU有很大差別*/
static const struct file_operations davinci_dm644x_gpio_fileops = {
.owner = THIS_MODULE,
.open = davinci_dm644x_gpio_open,
.ioctl = davinci_dm644x_gpio_ioctl,
};
static int __init davinci_dm644x_gpio_init(void) /*內核初始化會調用該函數*/
{
int ret;
gpio_direction_output(DM644X_GPIO_LED0, 1); //led0 is on
udelay(1);
gpio_direction_output(DM644X_GPIO_LED1, 1); //led1 is on
udelay(1);
gpio_direction_output(DM644X_GPIO_BUZZER, 1); //BUZZER is on
mdelay(500); /*初始化時,buzzer 發出聲音500ms*/
gpio_direction_output(DM644X_GPIO_BUZZER, 1); //BUZZER is off
ret = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &davinci_dm644x_gpio_fileops);
if(ret < 0)
{
printk(DEVICE_NAME " register falid!\n");
return ret;
}
printk (DEVICE_NAME" initialized\n");
return ret;
}
static void __exit davinci_dm644x_gpio_exit(void)
{
unregister_chrdev(GPIO_MAJOR, DEVICE_NAME);
}
module_init(davinci_dm644x_gpio_init);
module_exit(davinci_dm644x_gpio_exit);
MODULE_AUTHOR("xxx <>");
MODULE_DESCRIPTION("Davinci DM644x gpio driver");
MODULE_LICENSE("GPL");
這個驅動源碼是一種比較老的驅動移植,即register_chrdev(GPIO_MAJOR, DEVICE_NAME, &davinci_dm644x_gpio_fileops),靜態分配設備節點,適合linux-2.4.x和linux-2.6.10前的版本,當然也可以在2.6.18及以後的版本使用,現在新的版本char字符設備的移植可以參考davinci_resizer.c、davinci_previewer.c等文件。
上麵的初始化函數調用udelay和msdelay,udelay一般適用於一個比較小的delay,如果你填的數大於2000,係統會認為你這個是一個錯誤的delay函數,因此如果需要2ms以上的delay需要使用mdelay函數。
由於這些delay函數本質上都是忙等待,對於長時間的忙等待意味這無謂的耗費著cpu的資源,因此對於毫秒級的延時,內核提供了msleep,ssleep等函數,這些函數將使得調用它的進程睡眠參數指定的時間。
寄存器級的驅動gpio_direction_output()函數是定義在linux-2.6.18_pro500/arch/arm/mach-davinci/下的gpio.c裏,裏邊還有gpio_direction_input()和GPIO中斷函數。
三、修改內核配置文件
在linux-2.6.18_pro500/drivers/char目錄下,
修改Kconfig文件,在menu "Character devices"下麵,加入
config DAVINCI_DM644X_GPIOS
tristate "Davinci DM644x GPIO GPIOs"
depends on ARCH_DAVINCI
help
This option enables support for LEDs and Buzzer connected to GPIO lines
on Ti Davinci DM644x CPUs, such as the DM6446。
修改Makefile文件,在128行
obj-$(CONFIG_DAVINCI_DM646X_TSIF) += tsif_control.o tsif_data.o下麵,加入:
obj-$( DAVINCI_DM644X_GPIOS) += davinci_dm644x_gpios.o
修改linux-2.6.18 內核menu配置
選上“Character devices”裏的“Davinci DM644x GPIOs”,保存修改後的配置,然後make uImage,對內核的編譯;
四、GPIO應用程序源碼
源碼添加:
/* dm644x_gpio_test.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
/* ./dm644x_gpio_test 0 1 */ //led0 on
/* ./dm644x_gpio_test 0 0 */ //led0 off
/* ./dm644x_gpio_test 1 1 */ //led1 on
/* ./dm644x_gpio_test 1 0 */ //led1 off
/* ./dm644x_gpio_test 2 1 */ // buzzer on
/* ./dm644x_gpio_test 2 0 */ // buzzer off
int main(int argc, char **argv)
{
int on;
int gpio_number;
int fd;
// argv[0]== dm644x_gpio_test
// argv[1]== gpio_number
// argv[1]== on
if (argc != 3 || sscanf(argv[1], "%d", &gpio_number) != 1 || sscanf(argv[2],"%d", &on) != 1 ||on < 0 || on > 1 || gpio _number < 0 || gpio _number > 3)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\t dm644x_gpio_test gpio_number on|off\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, "\t gpio_number from 0 to 2\n");
fprintf(stderr, "\t on 1 off 0\n");
exit(1);
}
fd = open("/dev/dm644x_gpios", 0);
if (fd < 0)
{
perror("open device /dev/dm644x_gpios");
exit(1);
}
ioctl(fd, on, gpio_number);
close(fd);
return 0;
}
Makefile添加:
#application makefile for dm644x gpio test
CROSSCOMPILE = arm_v5t_le-
CC=$(CROSSCOMPILE)gcc
LD=$(CROSSCOMPILE)ld
OBJCOPY=$(CROSSCOMPILE)objcopy
OBJDUMP=$(CROSSCOMPILE)objdump
INCLUDE = /home/user/linux-2.6.18_pro500/include/*指向你的內核include*/
all: dm644x_gpio_test
dm644x_gpio_test: dm644x_gpio_test.c
$(CROSSCOMPILE)gcc -Wall -O2 dm644x_gpio_test.c -I $(INCLUDE) -o dm644x_gpio_test
$(CROSSCOMPILE)strip dm644x_gpio_test
cp -f dm644x_gpio_test /home/user/nfs/target/opt/app/
clean:
@rm -vf dm644x_gpio_test *.o *~
五、文件係統節點添加
文件係統裏,/etc/init.d/rcS文件
# Run /etc/rc.d/rc.local if it exists
[ -x /etc/rc.d/rc.local ] && /etc/rc.d/rc.local
前麵加mknod /dev/dm644x_gpios c 199 0或在shell命令行下執行mknod /dev/dm644x_gpios c 199 0;這就是靜態分配設備節點的做法。
運行係統,進入shell命令下,
#cd / opt/app/
#./ dm644x_gpio_test 0 1可以控製點亮LED0
等等,有平台的朋友可以試試。
六、總結
本人拿一個比較簡單的設備驅動移植的例子來講解,目的讓大家理解davinci dm6446係統架構。由於時間倉促,以上可能會有不完善的地方,還請各位網友指點。Davinci dm6446 開發攻略到本章節,基本上一個完整的DM6446係統的框架基本介紹完了,感謝各位網友的支持,dm6446 開發攻略的文章也接近尾聲。由於很長一段時間忙著給購買我們產品的客戶搭建開發環境、codec環境,開發驅動,調試3G產品等等,所以更新博客的速度放慢下來,畢竟客戶的要求才是最重要的。同時也趕在51CTO 5周年紀念日到來之前,結束DM6446開發攻略的主體文章,算是告一個段落,以便為51CTO 5周年紀念寫篇感想文章做好鋪墊,畢竟來這個圈子也快一年了,有些東西總是需要總結的。
最後更新:2017-04-03 16:48:40