ELADCMSecondEditionChapterFivePartXV
来自百问网嵌入式Linux wiki
Zhouyuebiao(讨论 | 贡献)2019年12月16日 (一) 14:45的版本 (Create EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFivePartXV page)
__NOTITLE__
目录
具体单板的按键驱动程序(查询方式)
AM335X的按键驱动程序(查询方式)
先看原理图确定引脚及操作方法
再看芯片手册确定寄存器及操作方法
- 步骤2:把GPIO1_25对应的引脚设置为GPIO模式
- 要用哪一个寄存器来把GPIO1_25对应的引脚设置为GPIO模式?
编程
程序框架
- 使用GIT下载所有源码后,本节源码位于如下目录:
01_all_series_quickstart\04_快速入门(正式开始)\
02_嵌入式Linux驱动开发基础知识\source\
04_button_drv\02_button_drv_for_boards\01_button_drv_for_am335x
硬件相关的代码
- 主要看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
测试
- 安装驱动程序之后执行测试程序,观察它的返回值(执行测试程序的同时操作按键):
# insmod button_drv.ko
# insmod board_am335x.ko
# ./button_test /dev/100ask_button0
课后作业
- ① 修改board_am335x.c,增加更多按键
- ② 修改button_test.c,使用按键来点灯
RK3288的按键驱动程序(查询方式)
先看原理图确定引脚及操作方法
- Firefly的RK3288开发板上没有按键,我们为它制作的扩展板上有1个按键。在扩展板原理图rk3288_extend_v12_0715.pdf中可以看到按键,如下:
- 平时按键电平为高,按下按键后电平为低。
- 按键引脚为GPIO7_B1。
再看芯片手册确定寄存器及操作方法
- 步骤2:把GPIO7_B1对应的引脚设置为GPIO模式
编程
程序框架
- 使用GIT下载所有源码后,本节源码位于如下目录:
01_all_series_quickstart\04_快速入门(正式开始)\
02_嵌入式Linux驱动开发基础知识\source\
04_button_drv\02_button_drv_for_boards\02_button_drv_for_rk3288
硬件相关的代码
- 主要看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 }
测试
- 安装驱动程序之后执行测试程序,观察它的返回值(执行测试程序的同时操作按键):
# insmod button_drv.ko
# insmod board_rk3288.ko
# ./button_test /dev/100ask_button0
课后作业
- ① 修改button_test.c,使用按键来点灯
RK3399的按键驱动程序(查询方式)
先看原理图确定引脚及操作方法
- Firefly的RK3399开发板上没有按键,我们为它制作的扩展板上有3个按键。在扩展板原理图rk3399_extend_v12_0709final.pdf中可以看到按键,如下:
- 平时按键电平为高,按下按键后电平为低。
- 按键引脚为GPIO0_B1、GPIO0_B2、GPIO0_B4。
- 本视频中,只操作一个按键:GPIO0_B1。
再看芯片手册确定寄存器及操作方法
- 步骤2:把GPIO0_B1对应的引脚设置为GPIO模式
- 步骤3:设置GPIO0内部寄存器,把GPIO0_B1设置为输入引脚,读数据寄存器
编程
程序框架
- 使用GIT下载所有源码后,本节源码位于如下目录:
01_all_series_quickstart\04_快速入门(正式开始)\
02_嵌入式Linux驱动开发基础知识\source\
04_button_drv\02_button_drv_for_boards\03_button_drv_for_rk3399
硬件相关的代码
- 主要看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 }
测试
- 安装驱动程序之后执行测试程序,观察它的返回值(执行测试程序的同时操作按键):
# insmod button_drv.ko
# insmod board_rk3399.ko
# ./button_test /dev/100ask_button0
课后作业
- ① 修改board_rk3399.c,增加更多按键
- ② 修改button_test.c,使用按键来点灯
百问网IMX6ULL-QEMU的按键驱动程序(查询方式)
- 使用QEMU模拟的硬件,它的硬件资源可以随意扩展。
- 在IMX6ULL QEMU 虚拟开发板上,我们为它设计了2个 按键。在QEMU的GUI上有4个按键,右边的2个留待以后用于电源管理。
再看芯片手册确定寄存器及操作方法
编程
程序框架
- 使用GIT下载所有源码后,本节源码位于如下目录:
01_all_series_quickstart\04_快速入门(正式开始)\
02_嵌入式Linux驱动开发基础知识\source\
04_button_drv\02_button_drv_for_boards\04_button_drv_for_100ask_imx6ull-qemu
硬件相关的代码
- 主要看board_100ask_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 }
测试
- 先启动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_100ask_imx6ull-qemu.ko
# ./button_test /dev/100ask_button0
# ./button_test /dev/100ask_button1
课后作业
- ① 修改button_test.c,使用按键来点灯