嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序

嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序

  • IMX6ULL-QEMU的 LED驱动程序
    • 7.5 IMX6ULL-QEMU的 LED驱动程序
      • 7.5.1 看原理图确定引脚及操作方法
      • 7.5.2 所涉及的寄存器操作
      • 7.5.3 写程序
      • 7.5.4 上机实验
      • 7.5.5 课后作业

IMX6ULL-QEMU的 LED驱动程序

嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第1张图片

7.5 IMX6ULL-QEMU的 LED驱动程序

使用 QEMU模拟的硬件,它的硬件资源可以随意扩展。 在 IMX6ULL QEMU 虚拟开发板上,我们为它设计了 4个 LED。

7.5.1 看原理图确定引脚及操作方法

嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第2张图片
从上图可知,这 4个 LED 用到了 GPIO5_3、GPIO1_3、GPIO1_5、GPIO1_6 共 4个引脚。 在芯片手册里,这些引脚的名字是:GPIO5_IO03、GPIO1_IO03、GPIO1_IO05、GPIO1_IO06。可以根据名字搜到对应的寄存器。 当这些引脚输出低电平时,对应的 LED被点亮;输出高电平时,LED熄灭。

7.5.2 所涉及的寄存器操作

嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第3张图片
步骤 1:
使能 GPIO1、GPIO5
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第4张图片
设置 b[31:30]、b[27:26]就可以使能 GPIO5、GPIO1,设置为什么值呢?
注意:在 imx6ullrm.pdf中,CCM_CCGR1的 b[31:30]是保留位;我以前写程序时错用了 imx6ul(不是imx6ull)的手册,导致程序中额外操作了这些保留位。不去设置 b[31:30],GPIO5也是默认使能的。 看下图,设置为 0b11:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第5张图片
① 00:该 GPIO模块全程被关闭
② 01:该 GPIO模块在 CPU run mode情况下是使能的;在 WAIT或 STOP模式下,关闭
③ 10:保留
④ 11:该 GPIO模块全程使能

步骤 2:
设置 GPIO5_IO03、GPIO1_IO03、GPIO1_IO05、GPIO1_IO06为 GPIO模式
① 对于 GPIO5_IO03,设置如下寄存器:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第6张图片
② 对于 GPIO1_IO03,设置如下寄存器:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第7张图片

③ 对于 GPIO1_IO05,设置如下寄存器:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第8张图片
④ 对于 GPIO1_IO06,设置如下寄存器:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第9张图片

步骤 3:
设置 GPIO5_IO03、GPIO1_IO03、GPIO1_IO05、GPIO1_IO06为输出引脚,设置其输出电平 寄存器地址为:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第10张图片

设置方向寄存器,把引脚设置为输出引脚:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第11张图片
设置数据寄存器,设置引脚的输出电平:
嵌入式Linux应用开发-第七章-IMX6ULL-QEMU的LED驱动程序_第12张图片

7.5.3 写程序

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

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\02_led_drv\       
02_led_drv_for_boards\xxxxxx_imx6ull-qemu_src_bin 

硬件相关的文件是 board_xxxxxx_imx6ull-qemu.c,其他文件跟 LED框架驱动程序完全一样。
涉及的寄存器挺多,一个一个去执行 ioremap效率太低。 先定义结构体,然后对结构体指针进行 ioremap,这些结构体在。 对于 IOMUXC,可以如下定义:

struct iomux {  
volatile unsigned int unnames[23];  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO01;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO02;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO05;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO06;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO07;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO08;  
volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO09; 

}; 
struct iomux  *iomux = ioremap(0x20e0000,  sizeof(struct iomux)); 

对于 GPIO,可以如下定义:

struct imx6ull_gpio {  
volatile unsigned int dr;  
volatile unsigned int gdir;  
volatile unsigned int psr;  
volatile unsigned int icr1;  
volatile unsigned int icr2;  
volatile unsigned int imr;  
volatile unsigned int isr;  
volatile unsigned int edge_sel;
 }; 

struct imx6ull_gpio *gpio1 = ioremap(0x209C000,  sizeof(struct imx6ull_gpio)); 
struct imx6ull_gpio *gpio5 = ioremap(0x20AC000,  sizeof(struct imx6ull_gpio)); 

开始详细分析 board_xxxxxx_imx6ull-qemu.c。 它首先构造了一个 led_operations结构体,用来表示 LED的硬件操作:

176 static struct led_operations board_demo_led_opr = { 
177     .num  = 4, 
178     .init = board_demo_led_init, 
179     .ctl  = board_demo_led_ctl, 
180 }; 
181 

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

57 static int board_demo_led_init (int which) /* 初始化 LED, which-哪个 LED */ 
58 { 
59     if (!CCM_CCGR1) 
60     { 
61         CCM_CCGR1 = ioremap(0x20C406C, 4); 
62         IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4); 
63 
64         iomux = ioremap(0x20e0000, sizeof(struct iomux)); 
65         gpio1 = ioremap(0x209C000, sizeof(struct imx6ull_gpio)); 
66         gpio5 = ioremap(0x20AC000, sizeof(struct imx6ull_gpio)); 
67     } 
68 
69     if (which == 0) 
70     { 
71         /* 1. enable GPIO5 
72          * CG15, b[31:30] = 0b11 
73          */ 
74         *CCM_CCGR1 |= (3<<30); 
75 
76         /* 2. set GPIO5_IO03 as GPIO 
77          * MUX_MODE, b[3:0] = 0b101 
78          */ 
79         *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = 5; 
80 
81         /* 3. set GPIO5_IO03 as output 
82          * GPIO5 GDIR, b[3] = 0b1 
83          */ 
84         gpio5->gdir |= (1<<3); 
85     } 
86     else if(which == 1) 
87     { 
88         /* 1. enable GPIO1 
89          * CG13, b[27:26] = 0b11 
90          */ 
91         *CCM_CCGR1 |= (3<<26); 
92 
93         /* 2. set GPIO1_IO03 as GPIO 
94          * MUX_MODE, b[3:0] = 0b101 
95          */ 
96         iomux->IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 = 5; 
97 
98         /* 3. set GPIO1_IO03 as output 
99          * GPIO1 GDIR, b[3] = 0b1 
100          */ 
101         gpio1->gdir |= (1<<3); 
102     } 
103     else if(which == 2) 
104     { 
105         /* 1. enable GPIO1 
106          * CG13, b[27:26] = 0b11 
107          */ 
108         *CCM_CCGR1 |= (3<<26); 
109 
110         /* 2. set GPIO1_IO05 as GPIO 
111          * MUX_MODE, b[3:0] = 0b101 
112          */ 
113         iomux->IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO05 = 5; 
114 
115         /* 3. set GPIO1_IO05 as output 
116          * GPIO1 GDIR, b[5] = 0b1 
117          */ 
118         gpio1->gdir |= (1<<5); 
119     } 
120     else if(which == 3) 
121     { 
122         /* 1. enable GPIO1 
123          * CG13, b[27:26] = 0b11 
124          */ 
125         *CCM_CCGR1 |= (3<<26); 
126 
127         /* 2. set GPIO1_IO06 as GPIO 
128          * MUX_MODE, b[3:0] = 0b101 
129          */ 
130         iomux->IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO06 = 5; 
131 
132         /* 3. set GPIO1_IO06 as output 
133          * GPIO1 GDIR, b[6] = 0b1 
134          */ 
135         gpio1->gdir |= (1<<6); 
136     } 
137 
138     //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which); 
139     return 0; 
140 } 
141 

led_operations结构体中有 ctl函数指针,它指向 board_demo_led_ctl函数,在里面将会根据参数设置 LED引脚的输出电平:
142 static int board_demo_led_ctl (int which, char status) /* 控制 LED, which-哪个 LED, status:1-亮,0-灭 */

143 { 
144     //printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off"); 
145     if (which == 0) 
146     { 
147         if (status)  /* on : output 0 */ 
148             gpio5->dr &= ~(1<<3); 
149         else         /* on : output 1 */ 
150             gpio5->dr |= (1<<3); 
151     } 
152     else if (which == 1) 
153     { 
154         if (status)  /* on : output 0 */ 
155             gpio1->dr &= ~(1<<3); 
156         else         /* on : output 1 */ 
157             gpio1->dr |= (1<<3); 
158     } 
159     else if (which == 2) 
160     { 
161         if (status)  /* on : output 0 */ 
162             gpio1->dr &= ~(1<<5); 
163         else         /* on : output 1 */ 
164             gpio1->dr |= (1<<5); 
165     } 
166     else if (which == 3) 
167     { 
168         if (status)  /* on : output 0 */ 
169             gpio1->dr &= ~(1<<6); 
170         else         /* on : output 1 */ 
171             gpio1->dr |= (1<<6); 
172     } 
173     return 0; 
174 } 
175 

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

182 struct led_operations *get_board_led_opr(void) 
183 { 
184     return &board_demo_led_opr; 
185 } 
186 

7.5.4 上机实验

先启动 IMX6ULL QEMU模拟器,挂载 NFS文件系统。
运行 QEMU时, QEMU内部为主机虚拟出一个网卡, IP为 10.0.2.2, IMX6ULL有一个网卡, IP为 10.0.2.15, 它连接到主机的虚拟网卡。 这样 IMX6ULL就可以通过 10.0.2.2去访问 Ubuntu了。

然后执行以下命令安装驱动、执行测试程序:

# insmod  xxxxxx_led.ko 
# ./ledtest  /dev/xxxxxx_led0  on 
# ./ledtest  /dev/xxxxxx_led0  off 

7.5.5 课后作业

a. 在驱动里有 ioremap,什么时候执行 iounmap?请完善程序
b. 驱动程序中有太多的 if判断,请优化程序减少 if的使用

你可能感兴趣的:(Linux,ARM,MCU,MCU,C51,linux,运维,服务器,c++,c语言)