二、小试牛刀-利用驱动操作GPIO点灯

二、小试牛刀-利用驱动操作GPIO点灯

(1)IMX6ULL 的 GPIO 模块结构

IMX6ULL的GPIO一共有5组,每组引脚最多有32个。

  • GPIO1 :GPIO1_IO0~GPIO1_IO31;

  • GPIO2 :GPIO2_IO0~GPIO2_IO21;

  • GPIO3 :GPIO3_IO0~GPIO3_IO28;

  • GPIO4 :GPIO4_IO0~GPIO4_IO28;

  • GPIO5 :GPIO5_IO0~GPIO5_IO11;

GPIO 的控制涉及 4 大寄存器:CCM、IOMUXC、GPIO 。

  • CCM 用于设置是否向 GPIO 模块提供时钟 ,通过CCM_CCGR 寄 存器来设置。

  • IOMUXC:引脚的模式(Mode、功能),,IOMUX通过 2 个寄存器用来设置

    IOMUXC_SW_MUX_CTL_PAD<PADNAME> :Mux pad xxx,选择某个 pad 的功能

    IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>:Mux grp xxx,选择某组引脚的功能一个引脚对应一个寄存器

    对于操作GPIO点灯只需关注三个寄存器

    GPIOx_GDIR:设置引脚方向,每位对应一个引脚,1-output,0-input

    GPIOx_GDIR:设置输出引脚的电平,每位对应一个引脚,1-高电平,0-低电平

    GPIOx_PSR:读取引脚的电平,每位对应一个引脚,1-高电平,0-低电

  • 具体操作

读引脚

  1. 设置 CCM_CCGRx 寄存器中某位使能对应的 GPIO 模块

  2. 设置 IOMUX 来选择引脚用于 GPIO

  3. 设置 GPIOx_GDIR 中某位为 0,把该引脚设置为输入功能

  4. 读 GPIOx_DR 或 GPIOx_PSR 得到某位的值(读 GPIOx_DR 返回的是 GPIOx_PSR 的值

写引脚

  1. 设置 CCM_CCGRx 寄存器中某位使能对应的 GPIO 模块 默认是使能的

  2. 设置 IOMUX 来选择引脚用于 GPIO

  3. 设置 GPIOx_GDIR 中某位为 1,把该引脚设置为输出功能

  4. 写 GPIOx_DR 某位的值

需要注意的是,你可以设置该引脚的 loopback 功能,这样就可以从 GPIOx_PSR 中读到引脚的有实电平;你从 GPIOx_DR 中读回的只是上次设置的值,它并不能反应引脚的真实电平,比如可能因为硬件故障导致该引脚跟地短路了,你通过设置 GPIOx_DR 让它输出高电平并不会起效果。

(2)驱动程序

  • 驱动程序分为上下两层:leddrv.c、board.c

    将LED操作抽象为结构体

 1 #ifndef _LED_OPR_H
 2 #define _LED_OPR_H
 3  4  5 struct led_operations
 6 {
 7     int num;
 8     int (*init)(int which);
 9     int (*ctl)(int which,char status);
10 };
11 12 struct led_operations *get_board_led_opr(void);//函数声明注册单板GPIO
13 14 #endif

在芯片手册中确定的寄存器地址被称为物理地址,在 Linux 内核中无法直接使用,需要使用内核提供的 ioremap 把物理地址映射为虚拟地址,使用虚拟地址

virt_addr  = ioremap(phys_addr, size);

把物理地址 phys_addr 开始的一段空间(大小为 size),映射为虚拟地址;返回值是该段虚拟地址的首地址。

 1 static volatile unsigned int *CCM_CCGR1                              ;//时钟
 2 static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;//引脚
 3 static volatile unsigned int *GPIO5_GDIR                             ;//输入输出
 4 static volatile unsigned int *GPIO5_DR                               ;//高低电平
 5 
 6 static int board_led_init(int which)
 7 {
 8     unsigned int val;
 9 10     if(which==0)
11     {
12 13         if(!CCM_CCGR1)
14         {
15             CCM_CCGR1                              = ioremap(0x20C406C, 4);//4字节
16             IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4);
17             GPIO5_GDIR                              = ioremap(0x020AC000 + 0x4, 4);
18             GPIO5_DR                                = ioremap(0x020AC000 + 0, 4);
19         }
20 21         *CCM_CCGR1 |= (3<<30);//GPIO5时钟
22 23         val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;//配置引脚功能
24         val &= ~(0xf);
25         val |= (5);
26         *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val;
27 28         *GPIO5_GDIR |= (1<<3);//输出高电平
29     }
30     return 0;
31 }
32 33 
34 static int board_led_ctl(int which,char status)
35 {
36     if(which==0)
37     {
38         if(status)
39         {
40             *GPIO5_DR&=(1<<3);
41         }
42         else
43         {
44             *GPIO5_DR|=(1<<3);
45         }
46     }
47 48     return 0;
49 }
50 51 struct led_operations board_led_opr=
52 {
53     .num=1,
54     .init=board_led_init,
55     .ctl=board_led_ctl,
56 };
57 58 struct led_operations *get_board_led_opr(void)
59 {
60     return &board_led_opr;
61 }
  1 #include <linux/module.h>
  2   3 #include <linux/fs.h>
  4 #include <linux/errno.h>
  5 #include <linux/miscdevice.h>
  6 #include <linux/kernel.h>
  7 #include <linux/major.h>
  8 #include <linux/mutex.h>
  9 #include <linux/proc_fs.h>
 10 #include <linux/seq_file.h>
 11 #include <linux/stat.h>
 12 #include <linux/init.h>
 13 #include <linux/device.h>
 14 #include <linux/tty.h>
 15 #include <linux/kmod.h>
 16 #include <linux/gfp.h>
 17  18 #include "led_opr.h"
 19  20 static int major=0;
 21 static struct class *led_class;
 22 static struct led_operations *p_led_opr;
 23  24 static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
 25 {
 26     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 27     return 0;
 28 }
 29  30 /* write(fd, &val, 1); */
 31 static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
 32 {
 33     int err;
 34     char status;
 35     struct inode *inode = file_inode(file);
 36     int minor = iminor(inode);//提取次设备号
 37     
 38     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 39     err = copy_from_user(&status, buf, 1);
 40  41     /* 根据次设备号和status控制LED */
 42     p_led_opr->ctl(minor, status);
 43     
 44     return 1;
 45 }
 46  47 static int led_drv_open (struct inode *node, struct file *file)
 48 {
 49     int minor = iminor(node);
 50     
 51     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 52     /* 根据次设备号初始化LED */
 53     p_led_opr->init(minor);
 54     
 55     return 0;
 56 }
 57  58 static int led_drv_close (struct inode *node, struct file *file)
 59 {
 60     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 61     return 0;
 62 }
 63  64  65 static struct file_operations led_drv = {
 66     .owner   = THIS_MODULE,
 67     .open    = led_drv_open,
 68     .read    = led_drv_read,
 69     .write   = led_drv_write,
 70     .release = led_drv_close,
 71 };
 72  73  74 static int __init led_init(void)
 75 {
 76     int err;
 77     int i;
 78  79     major=register_chrdev(0,"100ask_led",&led_drv);
 80  81     led_class=class_create(THIS_MODULE,"100ask_led_class");
 82     err=PTR_ERR(led_class);
 83     if(IS_ERR(led_class))
 84     {
 85         printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 86         unregister_chrdev(major,"100ask_led");
 87         return -1;
 88     }
 89  90     p_led_opr=get_board_led_opr();
 91  92     for(i=0;i<p_led_opr->num;i++)
 93     {
 94         device_create(led_class,NULL,MKDEV(major,i),NULL,"100ask_led%d", i);
 95     }
 96  97     return 0;
 98     
 99 }
100 101 static void __exit led_exit(void)
102 {
103     int i;
104     printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
105 106     for (i = 0; i < p_led_opr->num; i++)
107         device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */
108 109     device_destroy(led_class, MKDEV(major, 0));
110     class_destroy(led_class);
111     unregister_chrdev(major, "100ask_led");
112 }
113 114 module_init(led_init);
115 module_exit(led_exit);
116 117 MODULE_LICENSE("GPL");
 1 #include <sys/types.h>
 2 #include <sys/stat.h>
 3 #include <fcntl.h>
 4 #include <unistd.h>
 5 #include <stdio.h>
 6 #include <string.h>
 7  8 /*
 9  * ./ledtest /dev/100ask_led0 on
10  * ./ledtest /dev/100ask_led0 off
11  */
12 int main(int argc, char **argv)
13 {
14     int fd;
15     char status;
16     
17     /* 1. 判断参数 */
18     if (argc != 3) 
19     {
20         printf("Usage: %s <dev> <on | off>\n", argv[0]);
21         return -1;
22     }
23 24     /* 2. 打开文件 */
25     fd = open(argv[1], O_RDWR);
26     if (fd == -1)
27     {
28         printf("can not open file %s\n", argv[1]);
29         return -1;
30     }
31 32     /* 3. 写文件 */
33     if (0 == strcmp(argv[2], "on"))
34     {
35         status = 1;
36         write(fd, &status, 1);
37     }
38     else
39     {
40         status = 0;
41         write(fd, &status, 1);
42     }
43     
44     close(fd);
45     
46     return 0;
47 }

 

 
 



posted @ 2020-05-10 21:42  青春少年时  阅读(1038)  评论(0)    收藏  举报