uClinux驱动编写心得(uClinux2.4+S3C44B0X) 下面的内容绝大部分不是我写的,我是根据网上前辈高手的教程一步一步来做的。但是,在编译的过程中发现资料还是有点错误和关键点的遗漏。昨晚加上今天一整天,回忆编译的过程,稍微有点理解编译的方法了。晚上改了半个小时,搞定,驱动能用了,虽然仅仅是点个led灯,但是修正编译时的错误就像侦探破案一样,成功后心情真爽啊!我把我修正后的文章重新发出来,让更多人能看到。ps今天又搜索了一整天,还是没找到更多驱动相关的资料,希望大家能够提供下。 前期准备: 既然要编写uClinux驱动,就要在操作系统和硬件之间打交道,我用的板子是经典的S3C44B0X,驱动教程基于uClinux2.4版本,2.6版本不太一样,还没有找到资料。 1) 需要掌握针对uClinux的打补丁、裁剪、编译全过程,自己可以编译一个完整的uClinux系统下载到开发板的flash中并正常工作。这方面的教程网上更多,我就下了数个版本。 2) 需要熟悉S3C44B0X的硬件资源,本例子很简单,要想驱动其他东西就复杂咯。 一共要编写2个C文件,一个是led.c,属于驱动文件,另外一个是ledtest.c属于用户程序,还有一个led.h头文件。 /////////////////////led.h文件内容/////////////////////////// /****************************************Copyright (c)************************************************** ** FREE **--------------File Info------------------------------- ** File Name: led.h ** Last modified Date: 2008-6-3 ** Last Version: 1.0 ** Descriptions: User Configurable File **---------------------------------------------------- ** Created By: ** Created date: 2008-6-3 ** Version: 1.0 ** Descriptions: First version **------------------------------------------------- ** Modified by:MAMAJINCO ** Modified date:2006-9-9 ** Version:1.0 ** Descriptions:在此忠心感谢ZLG的模版 我的高质量编程意识起源于此 *****************************************************/ //防止重复包含此文件而设置的宏 #ifndef __CONFIG_H #define __CONFIG_H //包含必要的头文件 #include <linux/config.h> #include <linux/module.h> //模块必须包含的头文件 #include <linux/kernel.h> /* printk()函数,用于向内核输出信息 */ #include <linux/fs.h> /* 很重要 其中包含了如file_opration等结构 此结构用于文件层接口 */ #include <linux/errno.h> /* 错误代码*/ #include <asm/uaccess.h> #include <linux/types.h> #include <linux/mm.h> #include <asm/arch/s3c44b0x.h> /********************************/ /* 应用程序配置 */ /********************************/ //以下根据需要改动 //定义主设备号 设备名称 #define LED_MAJOR_NR 231 //231~239 240~255都可以 #define DEVICE_NAME "led" /* name for messaging */ #define SET_LED_OFF 0 #define SET_LED_ON 1 #endif /************************End Of File *********************************************************/ ////////////////////led.c文件内容//////////////////////////////////// /*************Copyright (c)************************** ** FREE **--------------File Info----------------------------------------- ** File Name: led.c ** Last modified Date: ** Last Version: 1.0 ** Descriptions: User Configurable File **---------------------------------------------------- ** Created By: ZLG CHENMINGJI ** Created date: 2006-9-9 ** Version: 1.0 ** Descriptions: First version **-------------------------------------------------------- ** Modified by:MAMAJINCO ** Modified date: ** Version:1.0 ** Descriptions:在此忠心感谢ZLG的模版 我的高质量编程意识起源于此 ***********************************************************/ #include "led.h"//包含驱动头文件 /************************************************************ function announce ******************************************************/ //以下是关键函数的声明 static int led_open(struct inode *inode, struct file *filp); //打开设备时用的 linux把设备当作文件管理 设备最好在用的时候再打开 尽量不要提前 static int led_release(struct inode *inode, struct file *filp); static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param); //控制函数 这里用于控制LED亮与灭 int led_init(void);//注册时用的 注意 模块注册的越早越好 void led_cleanup(void);//卸载时用的 /************************************************************* ** "全局和静态变量在这里定义" ** global variables and static variables define here /****************************************************************/ static struct file_operations LED_fops = /* 前面基础部分提到的重要结构体了*/ { owner: THIS_MODULE, #if 0/*注意:#if 0-#endif里的代码是不编译的,但这里为了驱动模板讲解上的完整性仍然加上了*/ llseek: gpio_llseek, read: gpio_read, write: gpio_write, #endif ioctl: led_ioctl,//控制函数 open: led_open, //打开函数,打开文件时做初始化的 release: led_release,//释放函数,关闭时调用 }; /************************************************************** ** Function name: led_open ** Descriptions: open device ** Input:inode: information of device ** filp: pointer of file ** Output 0: OK ** other: not OK ** Created by: Chenmingji ** Created Date: 2006-9-9 **------------------------------------------------------------- ** Modified by:mamajinco ** Modified Date: 2006-9-9 **-------------------------------------------------------------- **************************************************************/ static int led_open(struct inode *inode, struct file *filp) { /*初始化放在OPEN里*/ (*(volatile unsigned *)S3C44B0X_PCONC) &= 0xffffffc0; (*(volatile unsigned *)S3C44B0X_PCONC) |= 0xffffffd5; /*GPIO C口0~2 设置为输出*/ (*(volatile unsigned *)S3C44B0X_PUPC) &= 0xffffffc0;/*GPIO C口0~2 设置为上拉*/ MOD_INC_USE_COUNT; return 0; } /********************************************************* ** Function name: led_release ** Descriptions: release device ** Input:inode: information of device ** filp: pointer of file ** Output 0: OK ** other: not OK ** Created by: Chenmingji ** Created Date: 2006-9-9 **----------------------------------------------------- ** Modified by: mamajinco ** Modified Date: 2006-9-9 **-------------------------------------------------------- ****************************************************/ static int led_release(struct inode *inode, struct file *filp) { MOD_DEC_USE_COUNT; return(0); } /**************************************************** ** Function name: led_ioctl ** Descriptions: IO control function ** Input:inode: information of device ** filp: pointer of file ** cmd: command ** arg: additive parameter ** Output 0: OK ** other: not OK ** Created by: Chenmingji ** Created Date: 2006-9-9 **------------------------------------------------------- ** Modified by: mamajinco ** Modified Date: 2006-9-9 **---------------------------------------------------------- ***********************************************************/ static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { if( arg > 2 )//判断IO是否属于0-2 return -1; switch(cmd) { case 0://把ARG传来的IO口编号转换成寄存器数据,把相应IO口置低 (*(volatile unsigned *)S3C44B0X_PDATC) |= 0x1 << arg; break; case 1://把ARG传来的IO口编号转换成寄存器数据,把相应IO口置高 (*(volatile unsigned *)S3C44B0X_PDATC) &= 0x0 << arg; break; default: return -1; break; } return 0; } /************************************************** ** Function name: led_init ** Descriptions: init driver ** Input:none ** Output 0: OK ** other: not OK ** Created by: Chenmingji ** Created Date: 2006-9-9 **------------------------------------------------ ** Modified by: mamajinco ** Modified Date: 2006-9-9 **----------------------------------------------------- *************************************************/ int led_init(void) { int result; result = register_chrdev(231,"led", &LED_fops); /*关键语句 用于注册 ** 注意!这是传统的注册方法 在2.4以上的linux版本中 加入了devfs设备文件系统 使注册更容易 但为了与大部分资料相**同 大家看的方便 这里仍然使用老方法*/ if (result < 0) //用于异常检测 { printk(KERN_ERR DEVICE_NAME ": Unable to get major %d\n", LED_MAJOR_NR ); //printk用于向内核输出信息 return(result); } printk(KERN_INFO DEVICE_NAME ": init OK\n"); return(0); } /************************************************************* ** Function name: led_cleanup ** Descriptions: exit driver ** Input:none ** Output none ** Created by: Chenmingji ** Created Date: 2006-9-9 **-------------------------------------------------------- ** Modified by: mamajinco ** Modified Date: 2006-9-9 **----------------------------------------------------- **************************************************/ void led_cleanup(void) { unregister_chrdev(231, "led"); //与register_chrdev配对使用 用于清楚驱动 } /*********************************************************** ** End Of File ****************************************************/ ///////////////////最后是ledtest.c文件内容/////////////////////// /*********************Copyright (c)************************** ** FREE **--------------File Info---------------------------- ** File Name: led.c ** Last modified Date: 2006-9-9 ** Last Version: 1.0 ** Descriptions: User Configurable File **------------------------------------------------------- ** Created By: ZLG CHENMINGJI ** Created date: 2006-9-9 ** Version: 1.0 ** Descriptions: First version **-------------------------------------------------------- ** Modified by:MAMAJINCO ** Modified date:2006-9-9 ** Version:1.0 ** Descriptions:在此忠心感谢ZLG的模版 我的高质量编程意识起源于此 ********************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> void delay(int delay)//延时用函数 { int i; for(;delay>0;delay--) { for(i=0 ; i < 5000 ; i ++); } } int main() { int fd1; int j; fd1= open("/dev/led" , O_RDWR);/*打开设备,就象打开文件一样简单*/ if(fd1 == -1)/*异常处理*/ { printf ( "file can not be open" ); return -1; } for (j =0 ; j< 10 ; j ++)/*重复10次*/ { ioctl(fd1 , 1 , 0);/*GPC0上LED亮*/ delay(1000); ioctl(fd1 , 0 , 0);/*GPC0上LED灭*/ ioctl(fd1 , 1 , 1);/*GPC1上LED亮*/ delay(1000); ioctl(fd1 , 0 , 1);/*GPC1上LED灭*/ ioctl(fd1 , 1 , 2);/*GPC2上LED亮*/ delay(1000); ioctl(fd1 , 0 , 2);/*GPC2上LED灭*/ delay(1000); } close (fd1);/*关闭设备(文件)*/ return 0; } /*********************************************************** ** End Of File ****************************************************/ ////////////////////////////////////////////////// 现在有了led.h led.c ledtest.c三个文件了,正式开始吧 PART A 修改驱动 1) 把led.h和led.c 丢到目录uClinux-dist/linux-2.4.x/drivers/char中 2) 修改文件 ===============START============== uClinux-dist/linux-2.4.x/drivers/char/Makefile ---------------------------------------------- obj-$(CONFIG_C5471_WDT) += wdt_c5471.o之后加 obj-$(CONFIG_LED) += led.o ================END============ 3) 修改文件 =================START=========== uClinux-dist/linux-2.4.x/drivers/char/Config.in ----------------------------------------- if [ "$CONFIG_CPU_S3C44B0X" = "y" ]; then bool 'Samsung S3C44B0X serial ports support' CONFIG_SERIAL_S3C44B0X之后加 bool 'Test LED Driver' CONFIG_LED ================END================= 4) 修改文件 =================START============= uClinux-dist/linux-2.4.x/drivers/char/mem.c ----------------------------------------- 开头的地方扎堆加 #ifdef CONFIG_LED extern void led_init(void); #endif int __init chr_dev_init(void)之后加 #ifdef CONFIG_LED led_init(); #endif ================END============== 5) 继续修改文件 =================START============ uClinux-dist/vendors/Samakmsung/44B0/Makefile ----------------------------------------- ttypc,c,3,12 ttypd,c,3,13 ttype,c,3,14 ttypf,c,3,15\之后加 \ led,c,231,0 \ ================END============== PART B 修改用户程序 1) 把ledtest.c丢入自己建立的目录uClinux-dist/user/ledtest 2) 修改文件 ================START============ uClinux-dist/user/Makefile ----------------------------------------- 扎堆加个下面 dir_$(CONFIG_USER_LEDTEST) += ledtest =================END=============== 3) 修改文件 ================START============ uClinux-dist/config/Configure.help ----------------------------------------- 扎堆加个下面 CONFIG_USER_LEDTEST Test the LED driver =================END============= 4) 修改文件,就是在最后加上一段 ================START================ uClinux-dist/config/Configure.in ----------------------------------------- ############################## mainmenu_option next_comment comment 'LED driver test ' bool 'LEDtest' CONFIG_USER_LEDTEST endmenu ############################### =================END============= PART C 编译 1) make menuconfig 要选中Kernel和User那两个选项,在kernel中的能找到“Test LED Driver”选项,勾上,在user中能找到“LEDtest”,也要勾上,保存退出。 2) make clean 3) make lib_only 4) make user_only 5) make romfs 6) make image 7) make 我的led.c刚开始有不少问题,make会提示是多少行有问题,然后再回去改,还蛮不错的 好了,生成bin文件了 PART D 烧录 只是我的板子的地址,但基本上差不多,在uboot中操作 1) loadb 0x0c008000 2) 发送bin文件 3) erase 0x50000 0x1fffff 4) cp 0x0c008000 0x50000 3370d //3370d根据自己载入文件的大小除以4再加2,我是cdc2e/4+2的 5) save 6) reset 如果没问题,应该能看到uClinux的界面了,下面在板子中运行 1) cd /dev 2)ls 看见里面有个LED了吧? 3) cd /proc 4)cat devices 看见驱动列表吧? led 231也应该在里面 5)ledtest 在任何地方执行这个语句 就可以看到板子上的灯亮了。 |
__________________________ 阿虚的电子小屋 http://hi.baidu.com/aokikyon |