Chinaunix首页 | 论坛 | 博客
  • 博客访问: 178765
  • 博文数量: 35
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 305
  • 用 户 组: 普通用户
  • 注册时间: 2016-02-01 12:35
个人简介

不断超越自己,将更强大!

文章分类

全部博文(35)

文章存档

2022年(1)

2017年(5)

2016年(29)

我的朋友

分类: 嵌入式

2016-02-29 22:25:54

       最近移植成功了基于AT91SAM9261EK的UBI根文件系统,移植了ftp服务器功能,因此,打算开始驱动的学习,首先写个最简单的基于Linux 的LED驱动,根据网上的例子,修改一下。然后成功运行!方法很重要,开发环境很重要。我这里使用的是Linux 2.6.32的内核。

(1)编写嵌入式Linux LED驱动,控制开发板上的两个LED灯。低电平亮,高电平灭。Atmel AT91SAM9261 PA13 PA14两个GPIO控制。
(2)编写驱动程序(生成模块)与用户程序(执行文件)。
(3)文件系统是可写的,也就是可以建目录与文件,移植了简单的ftp服务器(我这里使用stupidftp)。这样的话,生成的模块与用户程序,直接通过ftp复制到开发板上,由串口超级终端(我使用CRT)来加载模块,执行用户程序,操作运行。

          LED驱动程序:修改网上成功的例子:


点击(此处)折叠或打开

  1. //LED_TEST.c
  2. #include <linux/string.h>
  3. #include <linux/cdev.h>
  4. #include <linux/fs.h>
  5. #include <mach/gpio.h> //包含管脚操作的相关函数
  6. #include <linux/device.h> //包含创建设备文件的相关函数


  7. #define DEVICE_NAME "SAM9261-LED_TEST"
  8. static int LED_Major = 0;


  9. struct cdev cdev;


  10. /** 应用程序执行 ioctl(fd, cmd, arg)时的第 2 个参数 **/
  11. #define LED_OFF 0
  12. #define LED_ON 1


  13. static unsigned long led_table [] =
  14. {
  15.     AT91_PIN_PA13, /**led_1**/
  16.     AT91_PIN_PA14, /**led_2**/
  17. };

  18. 一:LED驱动程序编写
  19. /*应用程序对设备文件/dev/leds 执行 open()时,
  20. *就会调用SAM9261_ledtest_open */
  21. static int SAM9261_ledtest_open(struct inode *inode, struct file *file)
  22. {
  23.     printk("SAM9261-ledtest Driver Open Called!\n");
  24.     return 0;
  25. }


  26. static long SAM9261_ledtest_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  27. {
  28.     if((cmd != 1 && cmd != 0) || (arg != 1 && arg != 0))
  29.     return -1;


  30.     switch(cmd)
  31.     {
  32.         case LED_ON:
  33. if(arg)
  34. {
  35. at91_set_gpio_value(led_table[arg], 0);
  36. }
  37. else
  38. {
  39. at91_set_gpio_value(led_table[arg], 0);
  40. }
  41. break;
  42.         case LED_OFF:
  43. if(arg)
  44. {
  45. at91_set_gpio_value(led_table[arg], 1);
  46. }
  47. else
  48. {
  49. at91_set_gpio_value(led_table[arg], 1);
  50. }
  51. break;
  52.         default:
  53. return -EINVAL;
  54.     }
  55.     return 0;
  56. }


  57. static int SAM9261_ledtest_release(struct inode *inode, struct file *file)
  58. {
  59.     printk("SAM9261_LED Driver Release Called!\n");
  60.     return 0;
  61. }


  62. /*这个结构是字符设备驱动程序的核心
  63.   *当应用程序操作设备文件时调用的 open、read等函数,
  64.   *最终会调用这个结构中指定的对应函数 */
  65. static struct file_operations SAM9261_ledtest_fops =
  66. {
  67.     .owner      = THIS_MODULE,
  68.     .open = SAM9261_ledtest_open,
  69.     .release = SAM9261_ledtest_release,
  70.     .unlocked_ioctl = SAM9261_ledtest_ioctl,
  71. };


  72. static struct class *SAM9261_ledtest_class = NULL;


  73. /**模块的初始化函数**/
  74. static int __init SAM9261_ledtest_init(void)
  75. {
  76. int result,err;
  77. dev_t devno = MKDEV(LED_Major, 0);
  78.     
  79. /**采用自动分配主设备号**/
  80.   if (LED_Major)
  81.   {
  82. result = register_chrdev_region(devno, 1, DEVICE_NAME);
  83. printk("Got the Major number by register_chrdev_region !\n ");
  84. }
  85.   else
  86.   {
  87. result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
  88. LED_Major=MAJOR(devno);
  89. printk("Got the Major number by alloc_chrdev_region !\n");
  90.   }
  91.   
  92.   if (result < 0)
  93.   {
  94.      printk(DEVICE_NAME " can't register major number\n");
  95.    return result;
  96.     }
  97.     
  98.     printk("register SAM9261_ledtest Driver OK! Major = %d\n", LED_Major);
  99.     
  100.     /**初始化cdev结构**/
  101.     cdev_init(&cdev,&SAM9261_ledtest_fops);
  102.     cdev.owner=THIS_MODULE;
  103.     cdev.ops=&SAM9261_ledtest_fops;
  104.     
  105. /**注册字符设备**/
  106.   err=cdev_add(&cdev, MKDEV(LED_Major, 0), 1);
  107.   if (err)
  108.   {
  109. printk("error %d adding led \n ", err);
  110. goto fail_cdev_add;
  111. }
  112.         /**自动创建设备文件**/
  113. SAM9261_ledtest_class = class_create(THIS_MODULE, DEVICE_NAME);
  114. if(IS_ERR(SAM9261_ledtest_class))
  115. {
  116. printk("Err: failed in SAM9261_ledtest class. \n");
  117. goto fail_create_class;
  118. }
  119. device_create(SAM9261_ledtest_class, NULL, MKDEV(LED_Major, 0), NULL, DEVICE_NAME);


  120. /**初始化PA13,PA14 PA23引脚**/
  121. at91_set_gpio_output(AT91_PIN_PA13, 1);
  122. at91_set_gpio_output(AT91_PIN_PA14, 1);
  123. at91_set_gpio_output(AT91_PIN_PA23, 1);

  124. at91_set_deglitch(AT91_PIN_PA13, 1);
  125. at91_set_deglitch(AT91_PIN_PA14, 1);
  126. at91_set_deglitch(AT91_PIN_PA23, 1);

  127. printk(DEVICE_NAME " initialized\n");

  128. return 0;
  129.     
  130. fail_create_class:
  131. cdev_del(&cdev);
  132. fail_cdev_add:
  133. unregister_chrdev_region(devno, 1);


  134. return -1;
  135. }


  136. /**模块的撤销函数**/
  137. static void __exit SAM9261_ledtest_exit(void)
  138. {
  139. printk("SAM9261 LED DRIVER MODULE EXIT\n");
  140. device_destroy(SAM9261_ledtest_class, MKDEV(LED_Major, 0));
  141. class_destroy(SAM9261_ledtest_class);
  142. cdev_del(&cdev);
  143. unregister_chrdev(LED_Major, DEVICE_NAME);
  144. }


  145. /**指定驱动程序的初始化函数和卸载函数**/
  146. module_init(SAM9261_ledtest_init);
  147. module_exit(SAM9261_ledtest_exit);


  148. MODULE_LICENSE("GPL");
  149. MODULE_AUTHOR("none");
  150. MODULE_DESCRIPTION("SAM9261_LEDTEST drivers");
  151. MODULE_ALIAS("LED Driver module.");



*******************
二:驱动程序Makefile编写
     驱动程序的Makefile文件:我这里不放在Linux 文件里(一般简单的直接放在Linux下面Drivers/char/目录下,然后修改Kconfig)
由于编译成模块,可以加载(insmod)与卸载(rmmod),没有必要再改动内核或是编译选项为:模块M。当然,内核要支持insmod 与rmmod命令。这好像应该是文件系统制作时:busybox的命令!!
     因此,内核与根文件系统制作好了就不要去改了,只要把模块与用户程序通过ftp放到开发板的目录下:我这里为:/mnt/nandflash 自己建的一个目录,设置一下权限,为可以读写。
    LED_TEST.c的Makefile文件如下:


点击(此处)折叠或打开

  1. obj-m:=LED_TEST.o
  2. CURRENT_PATH:=$(shell pwd)
  3. ARM_LINUX_KERNEL:=/home/AT91/Kernel/linux-2.6.32.2
  4. all:
  5. $(MAKE) -C $(ARM_LINUX_KERNEL) SUBDIRS=$(CURRENT_PATH) modules
  6. clean:
  7. rm -rf *.cmd *.o *.ko *.mod.c *.symvers *.order



          注意:虽然不放在Linux 如:linux-2.6.32.2的目录下,但编译驱动还是需要使用这个目录的,即开发板烧写的内核的目录。因为驱动与Linux内核文件有关,使用它的相关头文件与开发板硬件相关的底层文件。我这里驱动文件放在一个单独的目录里,然后引用内核的目录。

三:编译驱动的命令为:
在驱动文件目录(有LED_TEST.c Makefile),
执行shell:
# make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-

生成LED_Test.ko模块(加载就可以直接使用)

用户程序LED_APP.c的编写:


点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/ioctl.h>

  5. #define LED_DEV "/dev/SAM9261-LED_TEST"


  6. int main(int argc, char **argv)
  7. {
  8.     int fd, ret, led_num, led_status;
  9.     if (argc!=3 || sscanf(argv[1],"%d", &led_num)!=1
  10.         || sscanf(argv[2],"%d", &led_status)!=1)
  11.     {
  12.         printf("\r\nPlease input correct parameters !\r\n\n");
  13.         printf("usage:\r\n%s \r\n", argv[0]);
  14.     printf("\r\nOptions:\r\n");
  15.         printf(" led_num\t- 1 for red led, 0 for blue led.\r\n led_status\t- 1 for ON, 0 for OFF.\r\n\n");
  16.         exit(1);
  17.     }

  18.     if((led_status!=1 && led_status!=0) || (led_num!=0 && led_num!=1))
  19.     {
  20.          printf("\r\nError: The parameter value must be '0' or '1' !\r\n");
  21.          printf("\r\nPlease try again !!! !\r\n\n");
  22.          exit(1);
  23.     }

  24.     fd = open(LED_DEV, 0);
  25.     if (fd < 0)
  26.     {
  27.         printf("\r\nFail to open device '%s'!\r\n\n", LED_DEV);
  28.         exit(1);
  29.     }

  30.     ret = ioctl(fd, led_status, led_num);
  31.     if(ret < 0)
  32.     {
  33.         printf("\r\nFail calling ioctl !\r\n\n");    
  34.     }

  35.     close(fd);

  36.     return 0;
  37. }

用户程序的:Makefile


点击(此处)折叠或打开

  1. all:
  2.     arm-none-linux-gnueabi-gcc LED_TEST_APP.c -o LED_APP
  3. clean:
  4.     rm -rf *.o LED_APP
        执行编译:# make

(在Makefile里,已经指定了arm-none-linux-gnueabi-gcc 的交叉编译,因此直接make就执行了交叉编译)

就可以生成LED_APP可执行的文件,可以chmod 777 LED_APP设置一下执行权限。

         然后把LED_APP LED_TEST.ko 通过ftp复制到开发板。如果你的开发板不支持ftp,则需要跟文件系统一起烧到开发板。

如放在/mnt/nandflash里面,
则进入/mnt/nandflash。

在shell里执行:
#insmod LED_TEST.ko
//会提示是否加载模块成功!!
# ./LED_APP 0 1         //(第一个LED 亮)
# ./LED_APP 0 0         //(第一个LED灭)
#./LED_APP 1 1         //(第二个LED亮)
#./LED_APP 1 0        //( 第二个LED灭)
(输入时不要输入#,那是shell提示符!./表示当前目录,如果放在bin下面,可以直接执行LED_APP,因为bin这种目录,就像windows下面设置好了环境变量,可以直接执行,不用输入全路径。如果是其他的目录,需要输入全路径,当前目录下,直接使用./即可。)


卸载的命令:
rmmod LED_TEST.ko

好了,执行成功!




阅读(1985) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~