• 嵌入式Linux应用开发-第七章-野火-正点原子IMX6ULL的LED驱动程序


    野火IMX6ULL的LED驱动程序

    在这里插入图片描述

    7.4 野火/正点原子 IMX6ULL的 LED驱动程序

    野火、正点原子用的内核版本是 4.1.15,
    我们用的内核版本是 linux 4.9.88, 都是 4.x版本,在学习上没有任何差别。 你拿到板子后,可以使用他们出厂的系统, 也可以根据我们提供的高级用户手册更改为我们的系统。

    7.4.1 原理图

    7.4.1.1 野火 fire_imx6ull-pro开发板

    LED原理图如下,它使用 GPIO5_IO03,引脚输出低电平时 LED被点亮,输出高电平时 LED被熄灭:
    在这里插入图片描述

    7.4.1.2 正点原子 Atk_imx6ull-alpha开发板

    LED原理图如下,它使用 GPIO1_IO03,引脚输出低电平时 LED被点亮,输出高电平时 LED被熄灭:
    在这里插入图片描述

    7.4.2 所涉及的寄存器操作

    GPIO模块图如下:
    在这里插入图片描述
    代码中对硬件的操作截图如下,截图便于对比,后面有文字便于复制:
    在这里插入图片描述

    7.4.2.1 野火 fire_imx6ull-pro开发板

    步骤 1:使能 GPIO5
    在这里插入图片描述
    设置 b[31:30]就可以使能 GPIO5,设置为什么值呢?
    注意:
    在 imx6ullrm.pdf中,CCM_CCGR1的 b[31:30]是保留位;我以前写程序时错用了 imx6ul(不是imx6ull)的手册,导致程序中额外操作了这些保留位。不去设置 b[31:30],GPIO5也是默认使能的。 看下图,设置为 0b11:
    在这里插入图片描述
    ① 00:该 GPIO模块全程被关闭
    ② 01:该 GPIO模块在 CPU run mode情况下是使能的;在 WAIT或 STOP模式下,关闭
    ③ 10:保留
    ④ 11:该 GPIO模块全程使能

    /* GPIO5_IO03 */ 
    /* a. 使能 GPIO5  
    * set CCM to enable GPIO5  
     * CCM_CCGR1[CG15] 0x20C406C  
     *  bit[31:30] = 0b11  
     * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    步骤 2:设置 GPIO5_IO03为 GPIO模式
    设置如下寄存器:
    在这里插入图片描述

    /* b. 设置 GPIO5_IO03用于 GPIO  
    * set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3  
    *   to configure GPIO5_IO03 as GPIO  
    *  IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 0x2290014  
    *  bit[3:0] = 0b0101 alt5  
    * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    步骤 3:设置 GPIO5_IO03为输出引脚,
    设置其输出电平 寄存器地址为:
    在这里插入图片描述
    设置数据寄存器,设置引脚的输出电平:
    在这里插入图片描述

    /* c. 设置 GPIO5_IO03作为 output引脚  
    * set GPIO5_GDIR to configure GPIO5_IO03 as output  
    *  GPIO5_GDIR  0x020AC000 + 0x4  
    *  bit[3] = 0b1  
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    /* d. 设置 GPIO5_DR输出低电平  
    * set GPIO5_DR to configure GPIO5_IO03 output 0  
    * GPIO5_DR 0x020AC000 + 0  
    * bit[3] = 0b0  
    * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    /* e. 设置 GPIO5_IO3输出高电平  
    * set GPIO5_DR to configure GPIO5_IO03 output 1  
    *  GPIO5_DR 0x020AC000 + 0  
    *  bit[3] = 0b1  
    * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    7.4.2.2 正点原子 Atk_imx6ull-alpha开发板

    **步骤 1:**使能 GPIO1
    在这里插入图片描述
    设置 b[27:26]就可以使能 GPIO1,设置为什么值呢? 看下图,设置为 0b11:
    在这里插入图片描述
    ① 00:该 GPIO模块全程被关闭
    ② 01:该 GPIO模块在 CPU run mode情况下是使能的;在 WAIT或 STOP模式下,关闭
    ③ 10:保留
    ④ 11:该 GPIO模块全程使能

    /* GPIO1_IO03 */ 
    /* a. 使能 GPIO1  
    * set CCM to enable GPIO1  
    * * CCM_CCGR1[CG13] 0x20C406C  
    * * bit[27:26] = 0b11  
    * */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    步骤 2:
    设置 GPIO1_IO03为 GPIO模式 设置如下寄存器:
    在这里插入图片描述

    /* b. 设置 GPIO1_IO03用于 GPIO  
    * set IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03  
    *   to configure GPIO1_IO03 as GPIO  
    *  IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03  0x20E0068  
    *  bit[3:0] = 0b0101 alt5  
    */ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    步骤 3:
    设置 GPIO1_IO03为输出引脚,设置其输出电平 寄存器地址为:
    在这里插入图片描述
    设置方向寄存器,把引脚设置为输出引脚:

    在这里插入图片描述
    设置数据寄存器,设置引脚的输出电平:
    在这里插入图片描述

    /* c. 设置 GPIO1_IO03作为 output引脚  
    * set GPIO1_GDIR to configure GPIO1_IO03 as output  
    * GPIO1_GDIR  0x0209C000 + 0x4  
    *  bit[3] = 0b1 
    * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    /* d. 设置 GPIO1_DR输出低电平  
    * set GPIO1_DR to configure GPIO1_IO03 output 0  
    *  GPIO1_DR 0x0209C000 + 0  
    *  bit[3] = 0b0  
    * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    /* e. 设置 GPIO1_IO03输出高电平  
    * set GPIO1_DR to configure GPIO1_IO03 output 1  
    *  GPIO1_DR 0x0209C000 + 0  
    *  bit[3] = 0b1  
    * / 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.4.3 写程序

    7.4.3.1 野火 fire_imx6ull-pro开发板

    使用 GIT下载所有源码后,本节源码位于如下目录:

    01_all_series_quickstart\ 
    05_嵌入式 Linux驱动开发基础知识\source\02_led_drv\       
    02_led_drv_for_boards\fire_imx6ull-pro_src_bin 
    
    • 1
    • 2
    • 3

    硬件相关的文件是 board_fire_imx6ull-pro.c,其他文件跟 LED框架驱动程序完全一样。 它首先构造了一个 led_operations结构体,用来表示 LED的硬件操作:

    100 static struct led_operations board_demo_led_opr = { 
    101     .num  = 1, 
    102     .init = board_demo_led_init, 
    103     .ctl  = board_demo_led_ctl, 
    104 }; 
    105 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    led_operations结构体中有 init函数指针,它指向 board_demo_led_init函数,在里面将会初始化LED引脚:使能、设置为 GPIO模式、设置为输出引脚。
    值得关注的是第 35~38行,对于寄存器要先使用 ioremap得到它的虚拟地址,以后使用虚拟地址访问寄存器:

    21 static volatile unsigned int *CCM_CCGR1                              ; 
    22 static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3; 
    23 static volatile unsigned int *GPIO5_GDIR                             ; 
    24 static volatile unsigned int *GPIO5_DR                               ; 
    25 
    26 static int board_demo_led_init (int which) /* 初始化 LED, which-哪个 LED */     
    27 { 
    28     unsigned int val; 
    29 
    30     //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which); 
    31     if (which == 0) 
    32     { 
    33         if (!CCM_CCGR1) 
    34         { 
    35             CCM_CCGR1  = ioremap(0x20C406C, 4); 
    36         IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4); 
    37             GPIO5_GDIR  = ioremap(0x020AC000 + 0x4, 4); 
    38             GPIO5_DR    = ioremap(0x020AC000 + 0, 4); 
    39         } 
    40 
    41         /* GPIO5_IO03 */ 
    42         /* a. 使能 GPIO5 
    43          * set CCM to enable GPIO5 
    44          * CCM_CCGR1[CG15] 0x20C406C 
    45          * bit[31:30] = 0b11 
    46          */ 
    47         *CCM_CCGR1 |= (3<<30); 
    48 
    49         /* b. 设置 GPIO5_IO03用于 GPIO 
    50          * set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 
    51          *      to configure GPIO5_IO03 as GPIO 
    52          * IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3  0x2290014 
    53          * bit[3:0] = 0b0101 alt5 
    54          */ 
    55         val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3; 
    56         val &= ~(0xf); 
    57         val |= (5); 
    58         *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val; 
    59 
    60 
    61         /* b. 设置 GPIO5_IO03作为 output引脚 
    62          * set GPIO5_GDIR to configure GPIO5_IO03 as output 
    63          * GPIO5_GDIR  0x020AC000 + 0x4 
    64          * bit[3] = 0b1 
    65          */ 
    66         *GPIO5_GDIR |= (1<<3); 
    67     } 
    68 
    69     return 0; 
    70 } 
    71 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    led_operations结构体中有 ctl函数指针,它指向 board_demo_led_ctl函数,在里面将会根据参数设置 LED引脚的输出电平:

    72 static int board_demo_led_ctl (int which, char status) /* 控制 LED, which-哪个 LED, status:1-亮,0-灭 */ 
    73 { 
    74     //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off"); 
    75     if (which == 0) 
    76     { 
    77         if (status) /* on: output 0*/ 
    78         { 
    79             /* d. 设置 GPIO5_DR输出低电平 
    80              * set GPIO5_DR to configure GPIO5_IO03 output 0 
    81              * GPIO5_DR 0x020AC000 + 0 
    82              * bit[3] = 0b0 
    83              */ 
    84             *GPIO5_DR &= ~(1<<3); 
    85         } 
    86         else  /* off: output 1*/ 
    87         { 
    88             /* e. 设置 GPIO5_IO3输出高电平 
    89              * set GPIO5_DR to configure GPIO5_IO03 output 1 
    90              * GPIO5_DR 0x020AC000 + 0 
    91              * bit[3] = 0b1 
    92              */ 
    93             *GPIO5_DR |= (1<<3); 
    94         } 
    95 
    96     } 
    97     return 0; 
    98 } 
    99 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    下面的 get_board_led_opr函数供上层调用,给上层提供 led_operations结构体:

    106 struct led_operations *get_board_led_opr(void) 
    107 { 
    108     return &board_demo_led_opr; 
    109 } 
    110 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.4.3.2 正点原子 Atk_imx6ull-alpha开发板
    使用 GIT下载所有源码后,本节源码位于如下目录:

     01_all_series_quickstart\ 
     05_嵌入式 Linux驱动开发基础知识\source\02_led_drv\       
     02_led_drv_for_boards\atk_imx6ull-alpha_src_bin 
     
    
    • 1
    • 2
    • 3
    • 4

    硬件相关的文件是 board_atk_imx6ull-alpha.c,其他文件跟 LED框架驱动程序完全一样。 它首先构造了一个 led_operations结构体,用来表示 LED的硬件操作:

    100 static struct led_operations board_demo_led_opr = { 
    101     .num  = 1, 
    102     .init = board_demo_led_init, 
    103     .ctl  = board_demo_led_ctl, 
    104 }; 
    105 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    led_operations结构体中有 init函数指针,它指向 board_demo_led_init函数,在里面将会初始化LED引脚:使能、设置为 GPIO模式、设置为输出引脚。
    值得关注的是第 35~38行,对于寄存器要先使用 ioremap得到它的虚拟地址,以后使用虚拟地址访问寄存器:

    26 static int board_demo_led_init (int which) /* 初始化 LED, which-哪个 LED */ 
    27 { 
    28     unsigned int val; 
    29
    30     //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which); 
    31     if (which == 0) 
    32     { 
    33         if (!CCM_CCGR1) 
    34         { 
    35             CCM_CCGR1 = ioremap(0x20C406C, 4); 
    36             IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = ioremap(0x20E0068, 4); 
    37             GPIO1_GDIR = ioremap(0x0209C000 + 0x4, 4); 
    38             GPIO1_DR  = ioremap(0x0209C000 + 0, 4); 
    39         } 
    40 
    41         /* GPIO1_IO03 */ 
    42         /* a. 使能 GPIO1 
    43          * set CCM to enable GPIO1 
    44          * CCM_CCGR1[CG13] 0x20C406C 
    45          * bit[27:26] = 0b11 
    46          */ 
    47         *CCM_CCGR1 |= (3<<26); 
    48 
    49         /* b. 设置 GPIO1_IO03用于 GPIO 
    50          * set IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 
    51          *      to configure GPIO1_IO03 as GPIO 
    52          * IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03  0x20E0068 
    53          * bit[3:0] = 0b0101 alt5 
    54          */ 
    55         val = *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03; 
    56         val &= ~(0xf); 
    57         val |= (5); 
    58         *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = val; 
    59 
    60 
    61         /* c. 设置 GPIO1_IO03作为 output引脚 
    62          * set GPIO1_GDIR to configure GPIO1_IO03 as output 
    63          * GPIO1_GDIR  0x0209C000 + 0x4 
    64          * bit[3] = 0b1 65          */ 
    66         *GPIO1_GDIR |= (1<<3); 
    67     } 
    68 
    69     return 0; 
    70 } 
    71 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    led_operations结构体中有 ctl函数指针,它指向 board_demo_led_ctl函数,在里面将会根据参数设置 LED引脚的输出电平:

    72 static int board_demo_led_ctl (int which, char status) /* 控制 LED, which-哪个 LED, status:1-亮,0-灭 */ 
    73 { 
    74     //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off"); 
    75     if (which == 0) 
    76     { 
    77         if (status) /* on: output 0*/ 
    78         { 
    79             /* d. 设置 GPIO1_DR输出低电平 
    80              * set GPIO1_DR to configure GPIO1_IO03 output 0 
    81              * GPIO1_DR 0x0209C000 + 0 
    82              * bit[3] = 0b0 
    83              */ 
    84             *GPIO1_DR &= ~(1<<3); 
    85         } 
    86         else  /* off: output 1*/ 
    87         { 
    88             /* e. 设置 GPIO1_IO03输出高电平 
    89              * set GPIO1_DR to configure GPIO1_IO03 output 1 
    90              * GPIO1_DR 0x0209C000 + 0 
    91              * bit[3] = 0b1 
    92              */
    93             *GPIO1_DR |= (1<<3); 
    94         } 
    95 
    96     } 
    97     return 0; 
    98 } 
    99 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    下面的 get_board_led_opr函数供上层调用,给上层提供 led_operations结构体:

    106 struct led_operations *get_board_led_opr(void) 
    107 { 
    108     return &board_demo_led_opr; 
    109 } 
    110 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.4.4 上机实验

    首先设置工具链,然后修改驱动程序 Makefile指定内核源码路径,就可以编译驱动程序和测试程序了。 启动开发板,挂载 NFS文件系统,这样就可以访问到 Ubuntu中的文件。 最后,就可以在开发板上进行下列测试。

    7.4.4.1 野火 fire_imx6ull-pro开发板

    注意:如果要使用板子自带的系统,关闭原有 LED驱动的方法是类似的,也是进入开发板/sys/class/leds/目录,对于每一个 LED在该目录下都有一个子目录,假设某个子目录名为 XXX,则执行如下命令:

    # echo none  >  /sys/class/leds/XXX/trigger 
    
    • 1

    使用我们的系统时,按如下操作。
    要先禁止内核中原来的 LED驱动,把“heatbeat”功能关闭,执行以下命令即可:

    # echo none > /sys/class/leds/cpu/trigger 
    
    • 1

    这样就可以使用我们的驱动程序做实验了:

    # insmod  xxxxxx_led.ko 
    #./ledtest  /dev/xxxxxx_led0  on 
    #./ledtest  /dev/xxxxxx_led0  off 
    
    • 1
    • 2
    • 3

    如果想恢复原来的心跳功能,可以执行:

    # echo heartbeat > /sys/class/leds/cpu/trigger  
    
    • 1
    7.4.4.2 正点原子 Atk_imx6ull-alpha开发板

    注意:如果要使用板子自带的系统,关闭原有 LED驱动的方法是类似的,也是进入开发板/sys/class/leds/目录,对于每一个 LED在该目录下都有一个子目录,假设某个子目录名为 XXX,则执行如下命令:

     # echo none  >  /sys/class/leds/XXX/trigger 
    
    • 1

    使用我们的系统时,按如下操作。 要先禁止内核中原来的 LED驱动,把“heatbeat”功能关闭,执行以下命令即可:

     # echo none > /sys/class/leds/sys-led/trigger 
    
    • 1

    这样就可以使用我们的驱动程序做实验了:

     # insmod  xxxxxx_led.ko 
     # ./ledtest  /dev/xxxxxx_led0  on 
     # ./ledtest  /dev/xxxxxx_led0  off 
    
    • 1
    • 2
    • 3

    如果想恢复原来的心跳功能,可以执行:

     # echo heartbeat > /sys/class/leds/sys-led/trigger 
    
    • 1

    7.4.5 课后作业

    a. 在驱动里有 ioremap,什么时候执行 iounmap?请完善程序
    b. 视频里我们只实现了点一个 LED,开发板上也只有一个 LED, 所以,请修改代码操作蜂鸣器。

  • 相关阅读:
    PHP-面向服务器端的Web编程- print语句——直接打印内容,等效于echo “ 文本“
    Spring Bean如何定义继承呢?
    C语言——二周目——数据在内存中的存储
    我的创作纪念日
    HTTP协议概览
    华纳云:Linux每天自动备份mysql数据库怎么实现
    android jni通过反射打印java类的所有方法
    教你轻松解决win系统ucrtbased.dll丢失的问题,亲测有效!
    SpringSecurity框架【详解】
    【控制】自适应控制基本概念
  • 原文地址:https://blog.csdn.net/kingpower2018/article/details/133318011