嵌入式Linux应用开发-第十五章具体单板的按键驱动程序

news2025/4/8 9:17:01

嵌入式Linux应用开发-第十五章具体单板的按键驱动程序

  • 第十五章 具体单板的按键驱动程序(查询方式)
    • 15.1 GPIO操作回顾
    • 15.2 AM335X的按键驱动程序(查询方式)
      • 15.2.1 先看原理图确定引脚及操作方法
      • 15.2.2 再看芯片手册确定寄存器及操作方法
      • 15.2.3 编程
        • 15.2.3.1 程序框架
        • 15.2.3.2 硬件相关的代码
        • 15.2.3.3 测试
      • 15.2.4 课后作业
    • 15.3 RK3288的按键驱动程序(查询方式)
      • 15.3.1 先看原理图确定引脚及操作方法
      • 15.3.2 再看芯片手册确定寄存器及操作方法
      • 15.3.3 编程
        • 15.3.1.1 程序框架
      • 15.3.4 测试
    • 15.4 RK3399的按键驱动程序(查询方式)
      • 15.4.1 先看原理图确定引脚及操作方法
      • 15.4.2 再看芯片手册确定寄存器及操作方法
      • 15.4.3 编程
        • 15.4.3.1 程序框架
        • 15.4.3.2 硬件相关的代码
      • 15.4.4 测试
      • 15.4.5 课后作业
    • 15.5 IMX6ULL-QEMU的按键驱动程序(查询方式)
      • 15.5.1 先看原理图确定引脚及操作方法
      • 15.5.2 再看芯片手册确定寄存器及操作方法
      • 15.5.3 编程
        • 15.5.3.1 程序框架
        • 15.5.3.2 硬件相关的代码
      • 15.5.4 测试
      • 15.5.5 课后作业

第十五章 具体单板的按键驱动程序(查询方式)

在这里插入图片描述

15.1 GPIO操作回顾

参考《第四章 普适的 GPIO引脚操作方法》、《第五章 具体单板的 GPIO操作方法》。
在这里插入图片描述

15.2 AM335X的按键驱动程序(查询方式)

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

AM335X是底板+核心板的结构,打开底板原理图 xxxxxx_am335x_v12_原理图.pdf,它有 4个按键,本视频只操作一个按键,原理图如下:
在这里插入图片描述

平时按键电平为高,按下按键后电平为低。 按键引脚为 GPIO1_25。

15.2.2 再看芯片手册确定寄存器及操作方法

在这里插入图片描述

步骤 1:
使能 GPIO1模块
设置CM_PER_GPIO1_CLKCTRL寄存器的bit[18]为1,bit[1:0]为0x2,该寄存器地址为0x44E00000+0xAC。
在这里插入图片描述
步骤 2:
把 GPIO1_25对应的引脚设置为 GPIO模式
要用哪一个寄存器来把 GPIO1_25对应的引脚设置为 GPIO模式?
① 在核心板原理图 ET-som335X原理图.pdf里搜“GPIO1_25”,可以看到下图,确定 pin number为 U16:
在这里插入图片描述

② 在芯片手册 AM335x Sitara™ Processors.pdf里搜“U16”,可得下图,引脚名为 GPMC_A9,用作 GPIO时要设置为 mode 7:
在这里插入图片描述

③ 在芯片手册 AM335x_datasheet_spruh73p.pdf中搜 gpmc_a9,
在这里插入图片描述
所以,要把GPIO1_25对应的引脚设置为 GPIO模式,要设置 conf_gpmc_a9寄存器的bit[5]为1,bit[2:0]为 7,这个寄存器的地址是 0x44E10000+0x864。

步骤 3:
设置 GPIO1内部寄存器,把 GPIO1_25设置为输入引脚,读数据寄存器 GPIO_OE寄存器:地址为 0x4804C000+0x134,bit[25]设置为 1。
GPIO_DATAIN寄存器:地址为 0x4804C000+0x138,读其 bit[25]。
在这里插入图片描述
在这里插入图片描述

15.2.3 编程

15.2.3.1 程序框架

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

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
04_button_drv\02_button_drv_for_boards\01_button_drv_for_am335x 

在这里插入图片描述

15.2.3.2 硬件相关的代码

主要看 board_am335x.c,先看它的入口函数,代码如下。
第 84行向上层驱动注册一个 button_operations结构体,该结构体在第 76~80行定义。

76 static struct button_operations my_buttons_ops = { 
77     .count = 1, 
78     .init = board_am335x_button_init, 
79     .read = board_am335x_button_read, 
80 }; 
81 
82 int board_am335x_button_drv_init(void) 
83 { 
84     register_button_operations(&my_buttons_ops); 
85     return 0; 
86 } 
87

button_operations结构体中有 init函数指针,它指向 board_am335x_button_init函数,在里面将会初始化 LED引脚:使能、设置为 GPIO模式、设置为输出引脚。代码如下。

值得关注的是第 32~35行,对于寄存器要先使用 ioremap得到它的虚拟地址,以后使用虚拟地址访问寄存器。

21 static volatile unsigned int *CM_PER_GPIO1_CLKCTRL; 
22 static volatile unsigned int *conf_gpmc_a9; 
23 static volatile unsigned int *GPIO1_OE; 
24 static volatile unsigned int *GPIO1_DATAIN; 
25 
26 static void board_am335x_button_init (int which) /* 初始化 button, which-哪个 button */ 
27 { 
28     if (which == 0) 
29     { 
30         if (!CM_PER_GPIO1_CLKCTRL) 
31         { 
32             CM_PER_GPIO1_CLKCTRL = ioremap(0x44E00000 + 0xAC, 4); 
33             conf_gpmc_a9 = ioremap(0x44E10000 + 0x864, 4); 
34             GPIO1_OE = ioremap(0x4804C000 + 0x134, 4); 
35             GPIO1_DATAIN = ioremap(0x4804C000 + 0x138, 4); 
36         } 
37 
38         //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which); 
39         /* a. 使能 GPIO1 
40          * set PRCM to enalbe GPIO1 
41          * set CM_PER_GPIO1_CLKCTRL (0x44E00000 + 0xAC) 
42          * val: (1<<18) | 0x2 
43          */ 
44         *CM_PER_GPIO1_CLKCTRL = (1<<18) | 0x2; 
45 
46         /* b. 设置 GPIO1_25的功能,让它工作于 GPIO模式 
47          * set Control Module to set GPIO1_25 (U16) used as GPIO 
48          * conf_gpmc_a9 as mode 7 
49          * addr : 0x44E10000 + 0x864 
50          * bit[5]   : 1, Input enable value for the PAD 
51          * bit[2:0] : mode 7 
52          */ 
53         *conf_gpmc_a9 = (1<<5) | 7; 
54 
55         /* c. 设置 GPIO1_25的方向,让它作为输入引脚 
56          * set GPIO1's registers , to set 设置 GPIO1_25的方向'S dir (input) 
57          * GPIO_OE 
58          * addr : 0x4804C000 + 0x134 
59          * set bit 25 
60          */ 
61 
62         *GPIO1_OE |= (1<<25); 63     } 
64 
65 } 
66 

button_operations结构体中还有有 read函数指针,它指向 board_am335x_button_read函数,在里面将会读取并返回按键引脚的电平。代码如下。

67 static int board_am335x_button_read (int which) /* 读 button, which-哪个 */ 
68 { 
69     printk("%s %s line %d, button %d, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, which, *GPIO1_DATAIN); 
70     if (which == 0) 
71         return (*GPIO1_DATAIN & (1<<25)) ? 1 : 0; 
72     else 
73         return 0; 
74 } 
75 
15.2.3.3 测试

安装驱动程序之后执行测试程序,观察它的返回值(执行测试程序的同时操作按键):

# insmod button_drv.ko 
# insmod board_am335x.ko 
# ./button_test /dev/xxxxxx_button0 

15.2.4 课后作业

① 修改 board_am335x.c,增加更多按键
② 修改 button_test.c,使用按键来点灯

15.3 RK3288的按键驱动程序(查询方式)

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

Firefly的 RK3288开发板上没有按键,我们为它制作的扩展板上有 1个按键。在扩展板原理图rk3288_extend_v12_0715.pdf中可以看到按键,如下:
在这里插入图片描述

平时按键电平为高,按下按键后电平为低。 按键引脚为 GPIO7_B1。

15.3.2 再看芯片手册确定寄存器及操作方法

芯片手册为 Rockchip_RK3288_TRM_V1.2_Part1-20170321.pdf,不过我们总结如下。
在这里插入图片描述
步骤 1:
使能 GPIO7模块
设置 CRU_CLKGATE14_CON寄存器的 bit[7]为 0。 要设置 bit7,必须同时设置 bit23为 1。
该寄存器地址为 0xFF760000+0x198。
在这里插入图片描述

步骤 2:
把 GPIO7_B1对应的引脚设置为 GPIO模式
设置 GRF_GPIO7B_IOMUX寄存器的 bit[3:2]为 0b00。 要设置 bit[3:2],必须同时设置 bit[19:18]为 0b11。 该寄存器地址为 0xFF770000+0x0070。
在这里插入图片描述
步骤 3:
设置 GPIO7内部寄存器,把 GPIO7_B1设置为输入引脚,读数据寄存器
GPIO_SWPORTA_DDR方向寄存器:地址为 0xFF7E0000+ 0x0004,bit[9]设置为 0。 GPIO_EXT_PORTA外部端口寄存器:地址为 0xFF7E0000+ 0x0050,读其 bit[9]。
注意:
GPIO_A0~A7 对应 bit0bit7;GPIO_B0B7 对应 bit8~bit15;
GPIO_C0~C7 对应 bit16bit23;GPIO_D0D7 对应 bit24~bit31
在这里插入图片描述

15.3.3 编程

15.3.1.1 程序框架

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

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
04_button_drv\02_button_drv_for_boards\02_button_drv_for_rk3288 

在这里插入图片描述

15.3.1.2 硬件相关的代码
主要看 board_rk3288.c,先看它的入口函数,代码如下。

第 81行向上层驱动注册一个 button_operations结构体,该结构体在第 73~77行定义。 73 static struct button_operations

my_buttons_ops = { 
74     .count = 1, 
75     .init = board_rk3288_button_init, 
76     .read = board_rk3288_button_read, 
77 }; 
78 
79 int board_rk3288_button_drv_init(void) 
80 { 
81     register_button_operations(&my_buttons_ops); 
82     return 0; 
83 } 

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

21 static volatile unsigned int *CRU_CLKGATE14_CON; 
22 static volatile unsigned int *GRF_GPIO7B_IOMUX ; 
23 static volatile unsigned int *GPIO7_SWPORTA_DDR; 
24 static volatile unsigned int *GPIO7_EXT_PORTA ; 
25 
26 static void board_rk3288_button_init (int which) /* 初始化 button, which-哪个 button */ 
27 { 
28     if (which == 0) 
29     { 
30         if (!CRU_CLKGATE14_CON) 
31         { 
32             CRU_CLKGATE14_CON = ioremap(0xFF760000 + 0x0198, 4); 
33             GRF_GPIO7B_IOMUX  = ioremap(0xFF770000 + 0x0070, 4); 
34             GPIO7_SWPORTA_DDR = ioremap(0xFF7E0000 + 0x0004, 4); 
35             GPIO7_EXT_PORTA   = ioremap(0xFF7E0000 + 0x0050, 4); 
36         } 
37 
38         /* rk3288 GPIO7_B1 */ 
39         /* a. 使能 GPIO7 
40          * set CRU to enable GPIO7 
41          * CRU_CLKGATE14_CON 0xFF760000 + 0x198 
42          * (1<<(7+16)) | (0<<7) 
43          */ 
44         *CRU_CLKGATE14_CON = (1<<(7+16)) | (0<<7); 
45 
46         /* b. 设置 GPIO7_B1用于 GPIO 
47          * set PMU/GRF to configure GPIO7_B1 as GPIO 
48          * GRF_GPIO7B_IOMUX 0xFF770000 + 0x0070 
49          * bit[3:2] = 0b00 
50          * (3<<(2+16)) | (0<<2) 
51          */ 
52         *GRF_GPIO7B_IOMUX =(3<<(2+16)) | (0<<2); 
53 
54         /* c. 设置 GPIO7_B1作为 input引脚 
55          * set GPIO_SWPORTA_DDR to configure GPIO7_B1 as input 56          * GPIO_SWPORTA_DDR 0xFF7E0000 + 0x0004 
57          * bit[9] = 0b0 
58          */ 
59         *GPIO7_SWPORTA_DDR &= ~(1<<9); 
60     } 
61 
62 } 

button_operations结构体中还有有 read函数指针,它指向 board_rk3288_button_read函数,在里面将会读取并返回按键引脚的电平。代码如下。

64 static int board_rk3288_button_read (int which) /* 读 button, which-哪个 */ 
65 { 
66     //printk("%s %s line %d, button %d, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, which, *GPIO1_DATAIN); 
67     if (which == 0) 
68         return (*GPIO7_EXT_PORTA & (1<<9)) ? 1 : 0; 
69     else 
70         return 0; 
71 } 

15.3.4 测试

安装驱动程序之后执行测试程序,观察它的返回值(执行测试程序的同时操作按键):

# insmod button_drv.ko 
# insmod board_rk3288.ko 
# ./button_test /dev/xxxxxx_button0 

15.3.5 课后作业
① 修改 button_test.c,使用按键来点灯

15.4 RK3399的按键驱动程序(查询方式)

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

Firefly的 RK3399开发板上没有按键,我们为它制作的扩展板上有 3个按键。在扩展板原理图rk3399_extend_v12_0709final.pdf中可以看到按键,如下:
在这里插入图片描述

平时按键电平为高,按下按键后电平为低。
按键引脚为 GPIO0_B1、GPIO0_B2、GPIO0_B4。 本视频中,只操作一个按键:GPIO0_B1。

15.4.2 再看芯片手册确定寄存器及操作方法

芯片手册为 Rockchip RK3399TRM V1.3 Part1.pdf和 Rockchip RK3399TRM V1.3 Part2.pdf,不过我们总结如下。
在这里插入图片描述

步骤 1:
使能 GPIO0模块
设置 PMUCRU_CLKGATE_CON1寄存器的 bit[3]为 0。

要设置 bit3,必须同时设置 bit19为 1。 该寄存器地址为 0xFF760000+ 0x0104。
在这里插入图片描述

步骤 2
把 GPIO0_B1对应的引脚设置为 GPIO模式
设置 PMUGRF_GPIO0B_IOMUX寄存器的 bit[3:2]为 0b00。 要设置 bit[3:2],必须同时设置 bit[19:18]为 0b11。 该寄存器地址为 0xFF310000+0x0004。
在这里插入图片描述

步骤 3:
设置 GPIO0内部寄存器,把 GPIO0_B1设置为输入引脚,读数据寄存器 这些寄存器的介绍在芯片手册 Rockchip RK3399TRM V1.3 Part2.pdf中。
GPIO_SWPORTA_DDR方向寄存器:地址为 0xFF720000+ 0x0004,bit[9]设置为 0。 GPIO_EXT_PORTA外部端口寄存器:地址为 0xFF720000+ 0x0050,读其 bit[9]。

注意:
GPIO_A0~A7 对应 bit0bit7;GPIO_B0B7 对应 bit8~bit15;
GPIO_C0~C7 对应 bit16bit23;GPIO_D0D7 对应 bit24~bit31
在这里插入图片描述

15.4.3 编程

15.4.3.1 程序框架

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

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
04_button_drv\02_button_drv_for_boards\03_button_drv_for_rk3399 

在这里插入图片描述

15.4.3.2 硬件相关的代码

主要看 board_rk3399.c,先看它的入口函数,代码如下。
第 81行向上层驱动注册一个 button_operations结构体,该结构体在第 73~77行定义。

73 static struct button_operations my_buttons_ops = { 
74     .count = 1, 
75     .init = board_rk3399_button_init, 
76     .read = board_rk3399_button_read, 
77 }; 
78 
79 int board_rk3399_button_drv_init(void) 
80 { 
81     register_button_operations(&my_buttons_ops); 82     return 0; 
83 } 

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

21 static volatile unsigned int *PMUCRU_CLKGATE_CON1; 
22 static volatile unsigned int *GRF_GPIO0B_IOMUX ; 
23 static volatile unsigned int *GPIO0_SWPORTA_DDR; 
24 static volatile unsigned int *GPIO0_EXT_PORTA ; 
25 
26 static void board_rk3399_button_init (int which) /* 初始化 button, which-哪个 button */ 
27 { 
28     if (which == 0) 
29     { 
30         if (!PMUCRU_CLKGATE_CON1) 
31         { 
32             PMUCRU_CLKGATE_CON1 = ioremap(0xFF760000+ 0x0104, 4); 
33             GRF_GPIO0B_IOMUX  = ioremap(0xFF310000+0x0004, 4); 
34             GPIO0_SWPORTA_DDR = ioremap(0xFF720000 + 0x0004, 4); 
35             GPIO0_EXT_PORTA   = ioremap(0xFF720000 + 0x0050, 4); 
36         } 
37 
38         /* rk3399 GPIO0_B1 */ 
39         /* a. 使能 GPIO0 
40          * set CRU to enable GPIO0 
41          * PMUCRU_CLKGATE_CON1 0xFF760000+ 0x0104 
42          * (1<<(3+16)) | (0<<3) 
43          */ 
44         *PMUCRU_CLKGATE_CON1 = (1<<(3+16)) | (0<<3); 
45 
46         /* b. 设置 GPIO0_B1用于 GPIO 
47          * set PMU/GRF to configure GPIO0_B1 as GPIO 
48          * GRF_GPIO0B_IOMUX 0xFF310000+0x0004 
49          * bit[3:2] = 0b00 
50          * (3<<(2+16)) | (0<<2) 
51          */ 
52         *GRF_GPIO0B_IOMUX =(3<<(2+16)) | (0<<2); 
53 
54         /* c. 设置 GPIO0_B1作为 input引脚 
55          * set GPIO_SWPORTA_DDR to configure GPIO0_B1 as input 56          * GPIO_SWPORTA_DDR 0xFF720000 + 0x0004 
57          * bit[9] = 0b0 
58          */ 
59         *GPIO0_SWPORTA_DDR &= ~(1<<9); 
60     } 
61 
62 } 

button_operations结构体中还有有 read函数指针,它指向 board_rk3399_button_read函数,在里面将会读取并返回按键引脚的电平。代码如下。

64 static int board_rk3399_button_read (int which) /* 读 button, which-哪个 */ 
65 { 
66    //printk("%s %s line %d, button %d, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, which, *GPIO1_DATAIN); 
67     if (which == 0) 
68         return (*GPIO0_EXT_PORTA & (1<<9)) ? 1 : 0; 
69     else 
70         return 0; 
71 } 

15.4.4 测试

安装驱动程序之后执行测试程序,观察它的返回值(执行测试程序的同时操作按键):

 # insmod button_drv.ko 
# insmod board_rk3399.ko 
# ./button_test /dev/xxxxxx_button0 

15.4.5 课后作业

① 修改 board_rk3399.c,增加更多按键
② 修改 button_test.c,使用按键来点灯

15.5 IMX6ULL-QEMU的按键驱动程序(查询方式)

使用 QEMU模拟的硬件,它的硬件资源可以随意扩展。
在 IMX6ULL QEMU 虚拟开发板上,我们为它设计了 2个 按键。在 QEMU的 GUI上有 4个按键,右边的 2个留待以后用于电源管理。

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

在这里插入图片描述

平时按键电平为低,按下按键后电平为高。 按键引脚为 GPIO5_IO01、GPIO1_IO18。

15.5.2 再看芯片手册确定寄存器及操作方法

在这里插入图片描述
步骤 1:
使能 GPIO1、GPIO5
在这里插入图片描述

设置 b[31:30]、b[27:26]就可以使能 GPIO5、GPIO1,设置为什么值呢?
注意:在 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模块全程使能

步骤 2:
设置 GPIO5_IO01、GPIO1_IO18为 GPIO模式

① 对于 GPIO5_IO01,设置如下寄存器:
② 对于 GPIO1_IO18,设置如下寄存器:

步骤 3:
设置 GPIO5_IO01、GPIO1_IO18为输入引脚,读取引脚电平 寄存器地址为:
设置方向寄存器,把引脚设置为输出引脚:
读取引脚状态寄存器,得到引脚电平:

15.5.3 编程

15.5.3.1 程序框架

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

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
04_button_drv\02_button_drv_for_boards\04_button_drv_for_xxxxxx_imx6ull-qemu 

在这里插入图片描述

15.5.3.2 硬件相关的代码

主要看 board_xxxxxx_imx6ull-qemu.c。
涉及的寄存器挺多,一个一个去执行 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; 
 volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA; 
 volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_UART1_RX_DATA; 
 volatile unsigned int IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B; 
}; 
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)); 

看一个驱动程序,先看它的入口函数,代码如下。
第 127行向上层驱动注册一个 button_operations结构体,该结构体在第 119~123行定义。 119 static struct button_operations

my_buttons_ops = { 
120     .count = 2, 
121     .init = board_imx6ull_button_init, 
122     .read = board_imx6ull_button_read, 
123 }; 
124 
125 int board_imx6ull_button_drv_init(void) 
126 { 
127     register_button_operations(&my_buttons_ops); 
128     return 0; 
129 } 

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

50 /* enable GPIO1,GPIO5 */ 
51 static volatile unsigned int *CCM_CCGR1; 
52 
53 /* set GPIO5_IO03 as GPIO */ 
54 static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1; 
55 
56 static struct iomux *iomux; 
57 
58 static struct imx6ull_gpio *gpio1; 
59 static struct imx6ull_gpio *gpio5; 
60 
61 static void board_imx6ull_button_init (int which) /* 初始化 button, which-哪个 button */ 
62 { 
63     if (!CCM_CCGR1) 
64     { 
65         CCM_CCGR1 = ioremap(0x20C406C, 4); 
66         IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1 = ioremap(0x229000C, 4); 67 
68         iomux = ioremap(0x20e0000, sizeof(struct iomux)); 
69         gpio1 = ioremap(0x209C000, sizeof(struct imx6ull_gpio)); 
70         gpio5 = ioremap(0x20AC000, sizeof(struct imx6ull_gpio)); 
71     } 
72 
73     if (which == 0) 
74     { 
75         /* 1. enable GPIO5 
76          * CG15, b[31:30] = 0b11 
77          */ 
78         *CCM_CCGR1 |= (3<<30); 
79 
80         /* 2. set GPIO5_IO01 as GPIO 
81          * MUX_MODE, b[3:0] = 0b101 
82          */ 
83         *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1 = 5; 
84 
85         /* 3. set GPIO5_IO01 as input 
86          * GPIO5 GDIR, b[1] = 0b0 
87          */ 
88         gpio5->gdir &= ~(1<<1); 
89     } 
90     else if(which == 1) 
91     { 
92         /* 1. enable GPIO1 
93          * CG13, b[27:26] = 0b11 
94          */ 
95         *CCM_CCGR1 |= (3<<26); 
96 
97         /* 2. set GPIO1_IO18 as GPIO 
98          * MUX_MODE, b[3:0] = 0b101 
99          */ 
100         iomux->IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B = 5; 
101 
102         /* 3. set GPIO1_IO18 as input 
103          * GPIO1 GDIR, b[18] = 0b0 
104          */ 
105         gpio1->gdir &= ~(1<<18); 
106     } 107 
108 } 

button_operations结构体中还有有 read函数指针,它指向 board_imx6ull_button_read函数,在里面将会读取并返回按键引脚的电平。代码如下。

110 static int board_imx6ull_button_read (int which) /* 读 button, which-哪个 */ 
111 { 
112     //printk("%s %s line %d, button %d, 0x%x\n", __FILE__, __FUNCTION__, __LINE__, which, *GPIO1_DATAIN); 
113     if (which == 0) 
114         return (gpio5->psr & (1<<1)) ? 1 : 0; 
115     else 
116         return (gpio1->psr & (1<<18)) ? 1 : 0; 
117 } 

15.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 button_drv.ko 
# insmod board_drv.ko 
# insmod board_xxxxxx_imx6ull-qemu.ko 
# ./button_test  /dev/xxxxxx_button0 
# ./button_test  /dev/xxxxxx_button1 

15.5.5 课后作业

① 修改 button_test.c,使用按键来点灯

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1053385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

跨域问题详解:CORS问题+解决办法

跨域问题详解&#xff1a;CORS问题 1 概念&#xff1a;协议 域名 端口任意一个不同就会触发 CORS是一个W3C标准&#xff0c;全称是"跨域资源共享"&#xff08;Cross-origin resource sharing&#xff09;。 它允许浏览器向跨源服务器&#xff0c;发出XMLHttpReque…

微服务架构的黄金法则:拆分、重构、扩展

文章目录 1. 拆分&#xff08;Decompose&#xff09;1.1 单一责任原则&#xff08;Single Responsibility Principle&#xff09;1.2 松耦合&#xff08;Loose Coupling&#xff09;1.3 数据拆分 2. 重构&#xff08;Refactor&#xff09;2.1 单一代码库2.2 独立部署2.3 自动化…

CSP-J第二轮试题-2020年-1.2题

文章目录 参考&#xff1a;总结 [CSP-J2020] 优秀的拆分题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示样例 1 解释数据规模与约定 答案1答案2 [CSP-J2020] 直播获奖题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 …

OIS、EIS原理

OIS概念 光学防抖OIS&#xff0c;全称Optical Image Stabilization&#xff0c;从字面理解就是稳定的光学图像。其工作原理是基于陀螺仪检测并进行位移补偿。即通过镜头内的陀螺仪侦测手机抖动产生的微小移动&#xff0c;然后将信号传至CPU处理&#xff0c;CPU会即刻计算需要补…

小白继续深入学习C++

第1节 指针的基本概念 1、变量的地址&#xff1a; 变量是内存地址的简称&#xff0c;在C中&#xff0c;每定义一个变量&#xff0c;系统就会给变量分配一块内存&#xff0c;内存是有地址的。 C用运算符&获取变量在内存中的起始地址。 语法&#xff1a; &变…

国庆作业 day1

C语言基础考题&#xff08;40&#xff09; 选择题 20分每题2分 1、已知字母A的ASCII码为十进制数值65&#xff0c;且S为字符型&#xff0c;则执行语句SA6-3&#xff1b;后S中的值为 ( ) A.D B.68 C.不确定的值 D.C 2、若有定义语句&#xff1a;int a12;&#xff0c;则执…

Java之线程池的详细解析

1. 线程池 1.1 线程状态介绍 当线程被创建并启动以后&#xff0c;它既不是一启动就进入了执行状态&#xff0c;也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢&#xff1f;Java中的线程 状态被定义在了java.lang.Thread.Stat…

安全渗透测试基础之漏洞扫描工具之Nessus使用介绍

前置条件:Nessus工具使用前要确保工具是服务状态 systemctl start nessusd.service 启动nessus服务 systemctl status nessusd.service 查看nessus服务状态 1.配置扫描模板 2.新增高级扫描 2.1 设置日程表: 2.2设置邮件收件人(可选): 2.3主机发现: 2.

软件设计模式系列之二十一——观察者模式

1 观察者模式的定义 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象之间建立一对多的依赖关系&#xff0c;当一个对象的状态发生变化时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这个模式也被称为发布-订阅模式…

Android stdio的Gradle菜单栏无内容问题的解决方法

右边Gradle菜单栏里没有Tasks选项内容的问题 正常情况↓ 如果这个问题如果无法解决的话&#xff0c;Gradle打包就只能通过控制台输入命令来解决&#xff0c;但这无疑是把简单问题复杂化了&#xff0c;我们来看看怎么解决这个问题吧。 这里有几个方法提供&#xff0c;可以自行选…

Ipa Guard软件介绍:启动界面和功能模块全解析,保护你的iOS应用源码

ipaguard界面概览 ipaguard界面分左右2块&#xff1a;左边菜单导航栏&#xff0c;右边的功能区 左侧菜单&#xff1a;按模块分成启动界面&#xff0c;代码模块&#xff0c;文件模块&#xff0c;重签名与测试模块 右侧主功能区会随着功能变化&#xff0c;但是整体分3块&#xf…

Webpack 基础入门以及接入 CSS、Typescript、Babel

一、什么是 Webpack Webpack 是一款 JS 模块化开发的技术框架&#xff0c;其运作原理是将多个 JS 文件关联起来构成可运行的应用程序。 Webpack 拥有丰富的 plugins / loaders 插件生态圈&#xff0c;可以让 js 识别不同的语言如 .css, .scss, .sass, .json, .xml, .ts, .vue…

揭秘:机构招生电子传单制作的五个黄金法则

机构招生微传单制作一直都是让很多人在意的事情。一款好的微传单不仅可以吸引更多的学生&#xff0c;还可以省去很多招生工作的时间和精力。但是&#xff0c;很多人却不知道如何制作一款精美的微传单。下面就让我们来学习一下如何制作一款机构招生的微传单吧。 首先&#xff0c…

聊聊Go与依赖注入

如果你读过Robert C. Martin[1]的《敏捷软件开发&#xff1a;原则、模式与实践》[2](书的封皮见下图)&#xff0c;那么你一定知道经典的SOLID设计原则[3]中的“D”&#xff1a;依赖倒置原则&#xff08;Dependency Inversion Principle, DIP&#xff09;。 依赖倒置原则是面向对…

node版本问题:Error: error:0308010C:digital envelope routines::unsupported

前言 出现这个错误是因为 node.js V17及以后版本中最近发布的OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严格的限制&#xff0c;可能会对生态系统造成一些影响. 在node.js V17以前一些可以正常运行的的应用程序,但是在 V17 及以后版本可能会抛出以下异常: 我重装系…

华为云云耀云服务器L实例评测|部署在线图表和流程图绘制工具drawio

华为云云耀云服务器L实例评测&#xff5c;部署在线图表和流程图绘制工具drawio 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 优势及其应用场景1.3 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 drawio3.1 drawio 介绍3.2 Docker 环…

宝贝详情页制作的7个步骤,一看就懂

对于许多淘宝卖家来说&#xff0c;制作精美的详情页是吸引客户和提升销售的关键。然而&#xff0c;很多卖家可能会因为缺乏设计技能和经验而感到困惑。本文将为您揭示如何使用免费在线海报制作工具&#xff0c;如乔拓云&#xff0c;来制作具有吸引力的详情页&#xff0c;让您在…

基本数据类型在内存中存储形式

目录 内存分区模型 常量 与 变量 常量的定义 #define 与 const 区别 宏与const使用 修改const常量 整数类型 无符号整数 有符号整数 补码 内存分析 浮点数类型 float类型的IEEE编码 double类型的IEEE编码 基本的浮点数指令 数据类型转换分析 浮点数作为返回值…

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用

嵌入式Linux应用开发-基础知识-第十六章GPIO和Pinctrl子系统的使用 第十六章 GPIO 和 Pinctrl 子系统的使用16.1 Pinctrl 子系统重要概念16.1.1 引入16.1.2 重要概念16.1.3 示例16.1.4 代码中怎么引用pinctrl 16.2 GPIO子系统重要概念16.2.1 引入16.2.2 在设备树中指定引脚16.2…

React 入门笔记

前言 国庆值班把假期拆了个稀碎, 正好不用去看人潮人海, 趁机会赶个晚集入门一下都火这么久的 React 前端技术. 话说其实 n 年前也了解过一丢丢来着, 当时看到一上来就用 JS 写 DOM 的套路直接就给吓退了, 扭头还去看 Vue 了&#x1f923;, 现在从市场份额来看, 确实 React 还…