Chinaunix首页 | 论坛 | 博客
  • 博客访问: 555381
  • 博文数量: 127
  • 博客积分: 1169
  • 博客等级: 少尉
  • 技术积分: 1298
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-16 14:29
个人简介

空白

文章分类

全部博文(127)

分类: 嵌入式

2017-03-29 11:23:11

TINY4412调试LED

LED一般由CPU GPIO口控制,将GPIO口设置为输出口,再控制其输出高低电平即可控制灯亮、灯灭。


一、看原理图确定LED接入GPIO




原理图分析结果:
通过分析原理LED最终接入了CPU GPX3_2脚,GPX3_2输出低电平是LED灯灭,输出高电平时LED灯亮。
所有我们需要将GPX3_2先设置为输出引脚,在控制其输出高低电平,就可以控制灯灭、灯亮了。

二、CPU芯片手册找GPIO寄存器信息

在4412 CPU datasheet中查找GPX3,获得GPX3CONGPX3DAT寄存器的信息如下:
1、GPX3CON寄存器:


从CPU datasheet可以得到,GPX3CON的地址为0x11000C60, GPX3CON_2在GPX3CON地址的11-8位,将GPX3CON的11-8位设置为0x01即为输出功能。
代码如下:
/* 从gpx3con的物理地址得到gpx3con, gpx3dat的虚拟地址 */
gpx3con = (volatile unsigned long *)ioremap(0x11000C60, 16);
gpx3dat = gpx3con + 1;

/* 将GPX3CON[11:8]设置为输出引脚0x01,11,10,9位设置为低,8设置为高 ====> [11:10:9:8] ===> 0001 */
*gpx3con &= ~(0x0F << (4 * 2));           /* 将第11,10,9,8位全部置为0 */
*gpx3con |= (0x01 << (4 * 2));              /* 将第8位置为1 */

2、GPX3DAT寄存器:

GPX3DAT的地址为0x11000C64,8位大小,将GPX3CON指定的pin脚设置为0,1即可指定输出低,高电平。
比如:GPX3CON的11-8个bit对应的是GPX3CON[2],那么GPX3DAT的第2个bit对应的 0,1就是控制GPX3CON[2]输出低、高电平了。
代码如下:
/* 将GPX3DAT的第2位设置为输出低电平0灭灯 */
*gpx3dat &= ~(0x01 << 2);
/* 将GPX3DAT的第2位设置为输出高电平1亮灯 */
*gpx3dat |= (0x01 << 2);

三、写LED驱动
1、字符串设备驱动file_operations结构体
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open  = led_drv_open,
    .write = led_drv_write,
};
.open对应文件系统open系统调用接口;
.wirte对应文件系统write系统调用接口;

2、驱动入口函数
驱动入口函数根据字符设备驱动框架申请设备号,创建/dev/led0,添加字符设备,寄存器物理地址映射工作,内容如下:
  1. /* 驱动入口函数 */
  2. static int led_drv_init(void)
  3. {
  4.     int retval;
  5.     dev_t devid;
  6.     
  7.     /* 分配设备编号(devid) */
  8.     if (major)
  9.     {
  10.         devid = MKDEV(major, 0);
  11.         retval = register_chrdev_region(devid, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
  12.     }
  13.     else
  14.     {
  15.          /* 动态分配主设备号 */
  16.          retval = alloc_chrdev_region(&devid, 0, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
  17.          major = MAJOR(devid);
  18.     }

  19.     if (retval)
  20.     {
  21.          printk(KERN_ERR "Unable to register minors for led\n");
  22.          return retval;
  23.     }

  24.     /* 由内核自动创建/dev/led0设备 */
  25.     leddrv_class = class_create(THIS_MODULE, "led");
  26.     device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led0"); /* /dev/led0 */

  27.     cdev_init(&led_cdev, &led_fops);
  28.     cdev_add(&led_cdev, devid, LED_DEV_MAX);


  29.     /* 用来将I/O内存资源的物理地址映射到内核虚地址空间(3GB-4GB)中 */
  30.     /* 0x11000C60: 由芯片手册获得,GPX3CON物理地址 */
  31.     gpx3con = (volatile unsigned long *)ioremap(0x11000C60, 16);
  32.     gpx3dat = gpx3con + 1;

  33.     printk("LED driver create device ------> /dev/led0 suucess!\n");
  34.     
  35.     return 0;
  36. }
3、驱动open函数
  1. /* 驱动open函数 */
  2. static int led_drv_open(struct inode *inode, struct file *file)
  3. {
  4.     //printk("led_drv_open\n");
  5.     
  6.     //1. 设置GPX3CON为输出引脚,输出低电平是灯灭,输出高电平是灯亮
  7.     //将GPX3CON[11:8]设置为输出引脚0x01,11,10,9位设置为低,8设置为高 ====> [11:10:9:8] ===> 0001
  8.     *gpx3con &= ~(0x0F << (4 * 2)); /* 将第11,10,9,8位全部置为0 */
  9.     *gpx3con |= (0x01 << (4 * 2)); /* 将第8位置为1 */
  10.     
  11.     return 0;
  12. }
4、启动write函数

  1. /* 驱动write函数 */
  2. static int led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  3. {
  4.     int val = 0;
  5.     
  6.     //printk("led_drv_write\n");
  7.     
  8.     /* 从应用层拷贝数据 */
  9.     if (copy_from_user(&val, buf, count) != 0)
  10.     {
  11.         return -EFAULT;
  12.     }
  13.     
  14.     /* 灭灯 */
  15.     if (val == 0)
  16.     {
  17.         /* 将GPX3DAT的第2位设置为输出低电平0灭灯 */
  18.      *gpx3dat &= ~(0x01 << 2);
  19.     }
  20.     /* 亮灯 */
  21.     else
  22.     {
  23.      /* 将GPX3DAT的第2位设置为输出高电平1亮灯 */
  24.      *gpx3dat |= (0x01 << 2);
  25.     }
  26.     
  27.     return 0;
  28. }
5、驱动模块出口函数
  1. static void led_drv_exit(void)
  2. {    
  3.     dev_t devid = MKDEV(major, 0);
  4.     
  5.     device_destroy(leddrv_class, devid);
  6.     class_destroy(leddrv_class);

  7.     cdev_del(&led_cdev);

  8.     unregister_chrdev_region(devid, LED_DEV_MAX);

  9.     iounmap(gpx3con);

  10.     printk("LED driver destory device ------> /dev/led0 suucess!\n");
  11. }
完整驱动代码如下:
  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/interrupt.h>
  4. #include <linux/slab.h>
  5. #include <linux/errno.h>
  6. #include <linux/miscdevice.h>
  7. #include <linux/pci.h>
  8. #include <linux/wait.h>
  9. #include <linux/init.h>
  10. #include <linux/fs.h>
  11. #include <linux/cdev.h>

  12. #include <asm/io.h>
  13. #include <asm/uaccess.h>

  14. /* 设备数量1个 */
  15. #define LED_DEV_MAX 1

  16. /* 主设备号 */
  17. static int major;

  18. /* 内核字符设备结构体 */
  19. static struct cdev led_cdev;

  20. static struct class *leddrv_class;

  21. volatile unsigned long *gpx3con = NULL;
  22. volatile unsigned long *gpx3dat = NULL;

  23. /* 驱动open函数 */
  24. static int led_drv_open(struct inode *inode, struct file *file)
  25. {
  26.     //printk("led_drv_open\n");
  27.     
  28.     //1. 设置GPX3CON为输出引脚,输出低电平是灯灭,输出高电平是灯亮
  29.     //将GPX3CON[11:8]设置为输出引脚0x01,11,10,9位设置为低,8设置为高 ====> [11:10:9:8] ===> 0001
  30.     *gpx3con &= ~(0x0F << (4 * 2)); /* 将第11,10,9,8位全部置为0 */
  31.     *gpx3con |= (0x01 << (4 * 2)); /* 将第8位置为1 */
  32.     
  33.     return 0;
  34. }

  35. /* 驱动write函数 */
  36. static int led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
  37. {
  38.     int val = 0;
  39.     
  40.     //printk("led_drv_write\n");
  41.     
  42.     /* 从应用层拷贝数据 */
  43.     if (copy_from_user(&val, buf, count) != 0)
  44.     {
  45.         return -EFAULT;
  46.     }
  47.     
  48.     /* 灭灯 */
  49.     if (val == 0)
  50.     {
  51.         /* 将GPX3DAT的第2位设置为输出低电平0灭灯 */
  52.      *gpx3dat &= ~(0x01 << 2);
  53.     }
  54.     /* 亮灯 */
  55.     else
  56.     {
  57.      /* 将GPX3DAT的第2位设置为输出高电平1亮灯 */
  58.      *gpx3dat |= (0x01 << 2);
  59.     }
  60.     
  61.     return 0;
  62. }

  63. static struct file_operations led_fops = {
  64.     .owner = THIS_MODULE,
  65.     .open = led_drv_open,
  66.     .write = led_drv_write,
  67. };

  68. /* 驱动入口函数 */
  69. static int led_drv_init(void)
  70. {
  71.     int retval;
  72.     dev_t devid;
  73.     
  74.     /* 分配设备编号(devid) */
  75.     if (major) {
  76.      devid = MKDEV(major, 0);
  77.      retval = register_chrdev_region(devid, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
  78.     } else {
  79.         /* 动态分配主设备号 */
  80.         retval = alloc_chrdev_region(&devid, 0, LED_DEV_MAX, "led"); /* (major, 0)对应hello_fops,(major, 1~255)都不对应hello_fops */
  81.         major = MAJOR(devid);
  82.     }

  83.     if (retval) {
  84.         printk(KERN_ERR "Unable to register minors for led\n");
  85.         return retval;
  86.     }

  87.     /* 由内核自动创建/dev/led0设备 */
  88.     leddrv_class = class_create(THIS_MODULE, "led");
  89.     device_create(leddrv_class, NULL, MKDEV(major, 0), NULL, "led0"); /* /dev/led0 */

  90.     cdev_init(&led_cdev, &led_fops);
  91.     cdev_add(&led_cdev, devid, LED_DEV_MAX);

  92.     /* 用来将I/O内存资源的物理地址映射到内核虚地址空间(3GB-4GB)中 */
  93.     /* 0x11000C60: 由芯片手册获得,GPX3CON物理地址 */
  94.     gpx3con = (volatile unsigned long *)ioremap(0x11000C60, 16);
  95.     gpx3dat = gpx3con + 1;

  96.     printk("LED driver create device ------> /dev/led0 suucess!\n");
  97.     
  98.     return 0;
  99. }

  100. static void led_drv_exit(void)
  101. {    
  102.     dev_t devid = MKDEV(major, 0);
  103.     
  104.     device_destroy(leddrv_class, devid);
  105.     class_destroy(leddrv_class);

  106.     cdev_del(&led_cdev);

  107.     unregister_chrdev_region(devid, LED_DEV_MAX);

  108.     iounmap(gpx3con);

  109.     printk("LED driver destory device ------> /dev/led0 suucess!\n");
  110. }

  111. module_init(led_drv_init);
  112. module_exit(led_drv_exit);

  113. MODULE_LICENSE("GPL");
7、Makefile:
  1. KERN_DIR = /code/tiny4412/linux-3.5

  2. all:
  3.     make -C $(KERN_DIR) M=`pwd` modules

  4. clean:
  5.     make -C $(KERN_DIR) M=`pwd` modules clean
  6.     rm -rf modules.order

  7. obj-m    += led.o
8、编译模块
在驱动代码目录执行make命令,编译生成led.ko
# make

四、写LED驱动测试程序

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6.    
  7. int main(int argc, char **argv)
  8. {
  9.     if (argc != 2)
  10.     {
  11.         printf("usage: led_test <0(off)|1(on)> \n");
  12.         return -1;
  13.     }

  14.     int fd = open("/dev/led0", O_RDWR);
  15.     if (fd < 0)
  16.     {
  17.         printf("open /dev/led0 fail. \r\n");
  18.         return -1;
  19.     }

  20.     int val = 0;

  21.     if (strncmp(argv[1], "0", 1) == 0)
  22.     {
  23.         val = 0;
  24.     }
  25.     else
  26.     {
  27.         val = 1;
  28.     }

  29.     write(fd, &val, sizeof(val));

  30.     close(fd);

  31.     return 0;
  32. }
编译驱动测试程序:
arm-linux-gcc led_test.c -o led_test

五、测试
1、将led.ko、led_test文件tftp到设备,安装led.ko
# insmod led.ko
2、查看/dev目录下是否存在led0设备文件
# ls -l /dev/led0
3、运行led_test
# ./led_test 0           //熄灭led
# ./led_test 1           //点亮led
阅读(1627) | 评论(0) | 转发(0) |
0

上一篇:Shell自定义变量

下一篇:sqlite移植

给主人留下些什么吧!~~