查看: 1798|回复: 2

[原创] 【正点原子I.MX6U-ALPHA开发板】+⑥Linux下的LED驱动

[复制链接]
  • TA的每日心情
    奋斗
    昨天 09:30
  • 签到天数: 2088 天

    连续签到: 170 天

    [LV.Master]伴坛终老

    发表于 2020-3-2 00:20:02 | 显示全部楼层 |阅读模式
    分享到:
        前面我们已经使用裸板程序实现了对板卡上的LED进行开关操作,Linux下去驱动LED与裸板程序控制LED还是有区别的。Linux系统中一切设备皆文件,而任何外设驱动,最终都是要配置相应的硬件寄存器。LED灯驱动最终也是对I.MX6ULL的IO口进行配置。与裸机实验不同的是,在Linux下编写驱动要符合Linux的驱动框架。由于LED是连接到I.MX6ULL的GPIO1_IO03这个引脚上,因此需要先了解一下GPIO与主控I.MX6ULL的通信原理。
        Linux内核启动的时候会初始化内存管理单元,完成设置内存映射后,CPU访问的都是虚拟地址,比如I.MX6ULL的GPIO1_IO03引脚的复用寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03的地址为0X020E0068。如果没有开启内存管理单元则可直接向0X020E0068这个寄存器地址写入数据就可以配置GPIO1_IO03的复用功能。在Linux开发中,一般会启用内存管理机制,因此就不能直接向0X020E0068这个地址写入数据了。因此必须通过0X020E0068这个物理地址得到在Linux系统里面对应的虚拟地址,因而用到ioremap和iounmap这两个函数。
        ioremap函数:ioremap函数用于获取指定物理地址空间对应的虚拟地址空间,定义在arch/arm/include/asm/io.h文件中,定义如下:
    #define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE)
    void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,unsigned int mtype)
    {
       return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0));
    }


    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    昨天 09:30
  • 签到天数: 2088 天

    连续签到: 170 天

    [LV.Master]伴坛终老

     楼主| 发表于 2020-3-8 23:18:24 | 显示全部楼层
        大家周末好,今天接着上次的小测验讲起,iounmap函数:卸载驱动的时候使用,主要是实现释放掉ioremap函数所做的映射,iounmap函数原型void iounmap (volatile void __iomem *addr);参数addr是要取消映射的虚拟地址空间首地址。简单的描述一下,使用ioremap函数将寄存器的物理地址映射到虚拟地址以后,就可以直接通过指针访问这些地址,使用iounmap函数则取消物理地址与虚拟地址的映射关系。
        在Linux下一般不会采用内存映射的做法去操作内存,而是通过一组函数来对映射后的内存进行读写操作。
    读操作函数有:
    u8 readb(const volatile void __iomem *addr)
    u16 readw(const volatile void __iomem *addr)
    u32 readl(const volatile void __iomem *addr)
    写操作函数有:
    void writeb(u8 value, volatile void __iomem *addr)
    void writew(u16 value, volatile void __iomem *addr)
    void writel(u32 value, volatile void __iomem *addr)
        硬件原理图如前面帖子中提到的一样,具体如下图所示
    LED硬件原理图①.png LED硬件原理图②.png
          部分代码分享如下:
    1. #include <linux/types.h>
    2. #include <linux/kernel.h>
    3. #include <linux/delay.h>
    4. #include <linux/ide.h>
    5. #include <linux/init.h>
    6. #include <linux/module.h>
    7. #include <linux/errno.h>
    8. #include <linux/gpio.h>
    9. #include <asm/mach/map.h>
    10. #include <asm/uaccess.h>
    11. #include <asm/io.h>

    12. #define LED_MAJOR                200                /* 主设备号 */
    13. #define LED_NAME                "led"         /* 设备名字 */

    14. #define LEDOFF         0                                /* 关灯 */
    15. #define LEDON         1                                /* 开灯 */

    16. /* 寄存器物理地址 */
    17. #define CCM_CCGR1_BASE                                (0X020C406C)       
    18. #define SW_MUX_GPIO1_IO03_BASE                (0X020E0068)
    19. #define SW_PAD_GPIO1_IO03_BASE                (0X020E02F4)
    20. #define GPIO1_DR_BASE                                (0X0209C000)
    21. #define GPIO1_GDIR_BASE                                (0X0209C004)

    22. /* 映射后的寄存器虚拟地址指针 */
    23. static void __iomem *IMX6U_CCM_CCGR1;
    24. static void __iomem *SW_MUX_GPIO1_IO03;
    25. static void __iomem *SW_PAD_GPIO1_IO03;
    26. static void __iomem *GPIO1_DR;
    27. static void __iomem *GPIO1_GDIR;

    28. /*
    29. * @description                : LED打开/关闭
    30. * @param - sta         : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
    31. * @return                         : 无
    32. */
    33. void led_switch(u8 sta)
    34. {
    35.         u32 val = 0;
    36.         if(sta == LEDON) {
    37.                 val = readl(GPIO1_DR);
    38.                 val &= ~(1 << 3);       
    39.                 writel(val, GPIO1_DR);
    40.         }else if(sta == LEDOFF) {
    41.                 val = readl(GPIO1_DR);
    42.                 val|= (1 << 3);       
    43.                 writel(val, GPIO1_DR);
    44.         }       
    45. }

    46. /*
    47. * @description                : 打开设备
    48. * @param - inode         : 传递给驱动的inode
    49. * @param - filp         : 设备文件,file结构体有个叫做private_data的成员变量
    50. *                                           一般在open的时候将private_data指向设备结构体。
    51. * @return                         : 0 成功;其他 失败
    52. */
    53. static int led_open(struct inode *inode, struct file *filp)
    54. {
    55.         return 0;
    56. }

    57. /*
    58. * @description                : 从设备读取数据
    59. * @param - filp         : 要打开的设备文件(文件描述符)
    60. * @param - buf         : 返回给用户空间的数据缓冲区
    61. * @param - cnt         : 要读取的数据长度
    62. * @param - offt         : 相对于文件首地址的偏移
    63. * @return                         : 读取的字节数,如果为负值,表示读取失败
    64. */
    65. static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
    66. {
    67.         return 0;
    68. }

    69. /*
    70. * @description                : 向设备写数据
    71. * @param - filp         : 设备文件,表示打开的文件描述符
    72. * @param - buf         : 要写给设备写入的数据
    73. * @param - cnt         : 要写入的数据长度
    74. * @param - offt         : 相对于文件首地址的偏移
    75. * @return                         : 写入的字节数,如果为负值,表示写入失败
    76. */
    77. static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
    78. {
    79.         int retvalue;
    80.         unsigned char databuf[1];
    81.         unsigned char ledstat;

    82.         retvalue = copy_from_user(databuf, buf, cnt);
    83.         if(retvalue < 0) {
    84.                 printk("kernel write failed!\r\n");
    85.                 return -EFAULT;
    86.         }

    87.         ledstat = databuf[0];                /* 获取状态值 */

    88.         if(ledstat == LEDON) {       
    89.                 led_switch(LEDON);                /* 打开LED灯 */
    90.         } else if(ledstat == LEDOFF) {
    91.                 led_switch(LEDOFF);        /* 关闭LED灯 */
    92.         }
    93.         return 0;
    94. }

    95. /*
    96. * @description                : 关闭/释放设备
    97. * @param - filp         : 要关闭的设备文件(文件描述符)
    98. * @return                         : 0 成功;其他 失败
    99. */
    100. static int led_release(struct inode *inode, struct file *filp)
    101. {
    102.         return 0;
    103. }

    104. /* 设备操作函数 */
    105. static struct file_operations led_fops = {
    106.         .owner = THIS_MODULE,
    107.         .open = led_open,
    108.         .read = led_read,
    109.         .write = led_write,
    110.         .release =         led_release,
    111. };

    112. /*
    113. * @description        : 驱动出口函数
    114. * @param                 : 无
    115. * @return                 : 无
    116. */
    117. static int __init led_init(void)
    118. {
    119.         int retvalue = 0;
    120.         u32 val = 0;

    121.         /* 初始化LED */
    122.         /* 1、寄存器地址映射 */
    123.           IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    124.         SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    125.           SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    126.         GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    127.         GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    128.         /* 2、使能GPIO1时钟 */
    129.         val = readl(IMX6U_CCM_CCGR1);
    130.         val &= ~(3 << 26);        /* 清楚以前的设置 */
    131.         val |= (3 << 26);        /* 设置新值 */
    132.         writel(val, IMX6U_CCM_CCGR1);

    133.         /* 3、设置GPIO1_IO03的复用功能,将其复用为
    134.          *    GPIO1_IO03,最后设置IO属性。
    135.          */
    136.         writel(5, SW_MUX_GPIO1_IO03);
    137.        
    138.         /*寄存器SW_PAD_GPIO1_IO03设置IO属性
    139.          *bit 16:0 HYS关闭
    140.          *bit [15:14]: 00 默认下拉
    141.      *bit [13]: 0 kepper功能
    142.      *bit [12]: 1 pull/keeper使能
    143.      *bit [11]: 0 关闭开路输出
    144.      *bit [7:6]: 10 速度100Mhz
    145.      *bit [5:3]: 110 R0/6驱动能力
    146.      *bit [0]: 0 低转换率
    147.          */
    148.         writel(0x10B0, SW_PAD_GPIO1_IO03);

    149.         /* 4、设置GPIO1_IO03为输出功能 */
    150.         val = readl(GPIO1_GDIR);
    151.         val &= ~(1 << 3);        /* 清除以前的设置 */
    152.         val |= (1 << 3);        /* 设置为输出 */
    153.         writel(val, GPIO1_GDIR);

    154.         /* 5、默认关闭LED */
    155.         val = readl(GPIO1_DR);
    156.         val |= (1 << 3);       
    157.         writel(val, GPIO1_DR);

    158.         /* 6、注册字符设备驱动 */
    159.         retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
    160.         if(retvalue < 0){
    161.                 printk("register chrdev failed!\r\n");
    162.                 return -EIO;
    163.         }
    164.         return 0;
    165. }

    166. /*
    167. * @description        : 驱动出口函数
    168. * @param                 : 无
    169. * @return                 : 无
    170. */
    171. static void __exit led_exit(void)
    172. {
    173.         /* 取消映射 */
    174.         iounmap(IMX6U_CCM_CCGR1);
    175.         iounmap(SW_MUX_GPIO1_IO03);
    176.         iounmap(SW_PAD_GPIO1_IO03);
    177.         iounmap(GPIO1_DR);
    178.         iounmap(GPIO1_GDIR);

    179.         /* 注销字符设备驱动 */
    180.         unregister_chrdev(LED_MAJOR, LED_NAME);
    181. }

    182. module_init(led_init);
    183. module_exit(led_exit);
    184. MODULE_LICENSE("GPL");
    185. MODULE_AUTHOR("Argent");
    复制代码
    1. #include "stdio.h"
    2. #include "unistd.h"
    3. #include "sys/types.h"
    4. #include "sys/stat.h"
    5. #include "fcntl.h"
    6. #include "stdlib.h"
    7. #include "string.h"

    8. #define LEDOFF         0
    9. #define LEDON         1

    10. /*
    11. * @description                : main主程序
    12. * @param - argc         : argv数组元素个数
    13. * @param - argv         : 具体参数
    14. * @return                         : 0 成功;其他 失败
    15. */
    16. int main(int argc, char *argv[])
    17. {
    18.         int fd, retvalue;
    19.         char *filename;
    20.         unsigned char databuf[1];
    21.        
    22.         if(argc != 3){
    23.                 printf("Error Usage!\r\n");
    24.                 return -1;
    25.         }

    26.         filename = argv[1];

    27.         /* 打开led驱动 */
    28.         fd = open(filename, O_RDWR);
    29.         if(fd < 0){
    30.                 printf("file %s open failed!\r\n", argv[1]);
    31.                 return -1;
    32.         }

    33.         databuf[0] = atoi(argv[2]);        /* 要执行的操作:打开或关闭 */

    34.         /* 向/dev/led文件写入数据 */
    35.         retvalue = write(fd, databuf, sizeof(databuf));
    36.         if(retvalue < 0){
    37.                 printf("LED Control Failed!\r\n");
    38.                 close(fd);
    39.                 return -1;
    40.         }

    41.         retvalue = close(fd); /* 关闭文件 */
    42.         if(retvalue < 0){
    43.                 printf("file %s close failed!\r\n", argv[1]);
    44.                 return -1;
    45.         }
    46.         return 0;
    47. }
    复制代码
    1. KERNELDIR := /home/argent/workplace/System/linux-imx-rel_imx_4.1.15_2.1.0_ga
    2. CURRENT_PATH := $(shell pwd)

    3. obj-m := led.o

    4. build: kernel_modules

    5. kernel_modules:
    6.         $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

    7. clean:
    8.         $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
    复制代码


    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-26 01:43 , Processed in 0.116741 second(s), 20 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.