嵌入式Linux应用开发-第十五章具体单板的按键驱动程序
嵌入式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,使用按键来点灯
相关文章:
嵌入式Linux应用开发-第十五章具体单板的按键驱动程序
嵌入式Linux应用开发-第十五章具体单板的按键驱动程序 第十五章 具体单板的按键驱动程序(查询方式)15.1 GPIO操作回顾15.2 AM335X的按键驱动程序(查询方式)15.2.1 先看原理图确定引脚及操作方法15.2.2 再看芯片手册确定寄存器及操作方法15.2.3 编程15.2.3.1 程序框架15.2.3.2 硬…...
MySQL体系结构和四层架构介绍
MySQL体系结构图如下: 四层介绍 1. 连接层: 它的主要功能是处理客户端与MySQL服务器之间的连接(比如Java应用程序通过JDBC连接MySQL)。当客户端应用程序连接到MySQL服务器时,连接层对用户进行身份验证、建立安全连接并管理会话状态。它还处理…...
【产品运营】如何做好B端产品规划
产品规划是基于当下掌握的多维度信息,为追求特定目的,而制定的产品资源投入计划。 产品规划是基于当下掌握的多维度信息(客户需求、市场趋势、竞争对手、竞争策略等),为追求特定目的(商业增长、客户满意等&…...
ruoyi-启动
1 springboot 版本 git 地址 ruoyi-vue-pro: 🔥 官方推荐 🔥 RuoYi-Vue 全新 Pro 版本,优化重构所有功能。基于 Spring Boot MyBatis Plus Vue & Element 实现的后台管理系统 微信小程序,支持 RBAC 动态权限、数据权限…...
select完成服务器并发
服务器 #include <myhead.h>#define PORT 4399 //端口号 #define IP "192.168.0.191"//IP地址//键盘输入事件 int keybord_events(fd_set readfds); //客户端交互事件 int cliRcvSnd_events(int , struct sockaddr_in*, fd_set *, int *); //客户端连接事件 …...
初级篇—第四章聚合函数
文章目录 聚合函数介绍聚合函数介绍COUNT函数AVG和SUM函数MIN和MAX函数 GROUP BY语法基本使用使用多个列分组WITH ROLLUP HAVING基本使用WHERE和HAVING的对比开发中的选择 SELECT的执行过程查询的结构SQL 的执行原理 练习流程函数 聚合函数介绍 聚合函数作用于一组数据&#x…...
计算机图像处理-中值滤波
非线性滤波 非线性滤波是利用原始图像跟模版之间的一种逻辑关系得到结果,常用的非线性滤波方法有中值滤波和高斯双边滤波,分别对应cv2.medianBlur(src, ksize)方法和cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])方法。 …...
Golang中的包和模块设计
Go,也被称为Golang,是一种静态类型、编译型语言,因其简洁性和对并发编程的强大支持而受到开发者们的喜爱。Go编程的一个关键方面是其包和模块系统,它允许创建可重用、可维护和高效的代码。本博客文章将深入探讨在Go中设计包和模块…...
web:[极客大挑战 2019]Upload
题目 页面显示为一个上传,猜测上传一句话木马文件 先查看源代码看一下有没有有用的信息,说明要先上传图片,先尝试上传含有一句话木马的图片 构造payload <?php eval($_POST[123]);?> 上传后页面显示为,不能包含<&…...
ICMP差错包
ICMP报文分类 Type Code 描述 查询/差错 0-Echo响应 0 Echo响应报文 查询 3-目的不可达 0 目标网络不可达报文 差错 1 目标主机不可达报文 差错 2 目标协议不可达报文 差错 3 目标端口不可达报文 差错 4 要求分段并设置DF flag标志报文 差错 5 源路由…...
算法基础课第二部分
算法基础课 第四讲 数学知识AcWing1381. 阶乘(同余,因式分解) 质数AcWing 866. 质数的判定---试除法AcWing 868. 质数的判定---埃氏筛AcWing867. 分解质因数---试除法AcWing 197. 阶乘---分解质因数---埃式筛 约数AcWing 869. 求约数---试除法AcWing 870. 约数个数-…...
【数据结构】外部排序、多路平衡归并与败者树、置换-选择排序(生成初始归并段)、最佳归并树算法
目录 1、外部排序 1.1 基本概念 1.2 方法 2、多路平衡归并与败者树 2.1 K路平衡归并 2.2 败者树 3、置换-选择排序(生成初始归并段)编辑 4、最佳归并树 4.1 理论基础编辑 4.2 构造方法 编辑 5、各种排序算法的性质 1、外部排序 1.1 基本概…...
抽象工厂模式 创建性模式之五
在看这篇文章之前,请先看看“简单工厂模式”和“工厂方法模式”这两篇博文,会更有助于理解。我们现在已经知道,简单工厂模式就是用一个简单工厂去创建多个产品,工厂方法模式是每一个具体的工厂只生产一个具体的产品,然…...
servlet如何获取PUT和DELETE请求的参数
1. servlet为何不能获取PUT和DELETE请求的参数 Servlet的规范是POST的数据需要转给request.getParameter*()方法,没有规定PUT和DELETE请求也这么做 The Servlet spec requires form data to be available for HTTP POST but not for HTTP PUT or PATCH requests. T…...
【Vue.js】使用Element中的Mock.js搭建首页导航左侧菜单---【超高级教学】
一,Mock.js 1.1 认识Mock.js Mock.js是一个用于前端开发中生成随机数据、模拟接口响应的 JavaScript 库。模拟数据的生成器,用来帮助前端调试开发、进行前后端的原型分离以及用来提高自动化测试效率 总结来说,Element中的Mock.js是一个用于…...
从技术创新到应用实践,百度智能云发起大模型平台应用开发挑战赛!
大模型已经成为未来技术发展方向的重大变革,热度之下更需去虚向实,让技术走进产业场景。在这样的背景下,百度智能云于近期发起了“百度智能云千帆大模型平台应用开发挑战赛”。 挖掘大模型落地应用 千帆大模型平台应用开发挑战赛启动 在不久前…...
简单三步 用GPT-4和Gamma自动生成PPT PDF
1. 用GPT-4 生产PPT内容 我想把下面的文章做成PPT,请你给出详细的大纲和内容 用于谋生的知识,学生主要工作是学习,成年人的工作是养家糊口,这是基本的要求,在这之上,才能有更高的追求。 不要短期期望过高…...
QT设置弹窗显示屏幕中央
Qt设置每次运行弹窗显示屏幕中央 要确保Qt应用程序中的弹出窗口每次都显示在屏幕的中央,您可以使用以下方法: 使用QMessageBox的move方法手动设置窗口位置: #include <QApplication> #include <QMessageBox> #include <QDesk…...
正点原子嵌入式linux驱动开发——STM32MP1启动详解
STM32单片机是直接将程序下载到内部 Flash中,上电以后直接运行内部 Flash中的程序。 STM32MP157内部没有供用户使用的 Flash,系统都是存放在外部 Flash里面的,比如 EMMC、NAND等,因此 STM32MP157上电以后需要从外部 Flash加载程序…...
FPGA的数字钟带校时闹钟报时功能VHDL
名称:基于FPGA的数字钟具有校时闹钟报时功能 软件:Quartus 语言:VHDL 要求: 1、计时功能:这是数字钟设计的基本功能,每秒钟更新一次,并且能在显示屏上显示当前的时间。 2、闹钟功能:如果当前的时间与闹钟设置的时…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
