<b>一、 </b><b>dm6446 gpio</b><b>的介紹</b>
說到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字元裝置來移植。
<b>二、gpio</b><b>源碼移植分析</b>
/* 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中斷函數。 <b>三、修改核心配置檔案</b> 在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

<b>四、gpio</b><b>應用程式源碼</b>
源碼添加: /* 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 *~ <b>五、檔案系統節點添加</b> 檔案系統裡,/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 等等,有平台的朋友可以試試。 <b>六、總結</b>
本人拿一個比較簡單的裝置驅動移植的例子來講解,目的讓大家了解davinci dm6446系統架構。由于時間倉促,以上可能會有不完善的地方,還請各位網友指點。davinci dm6446 開發攻略到本章節,基本上一個完整的dm6446系統的架構基本介紹完了,感謝各位網友的支援,dm6446 開發攻略的文章也接近尾聲。由于很長一段時間忙着給購買我們産品的客戶搭建開發環境、codec環境,開發驅動,調試3g産品等等,是以更新部落格的速度放慢下來,畢竟客戶的要求才是最重要的。同時也趕在51cto 5周年紀念日到來之前,結束dm6446開發攻略的主體文章,算是告一個段落,以便為51cto 5周年紀念寫篇感想文章做好鋪墊,畢竟來這個圈子也快一年了,有些東西總是需要總結的。