Thử bo mạch

Lab: điều khiển LED bằng kernel module

Pi Zero 2 W · GPIO17ioremap · readl · writel

Đề bài

LED nối GPIO17 (chân 11) qua trở 330 Ω xuống GND. Viết module led_kmod: ioremap vùng GPIO 0x3F200000, đặt GPIO17 làm output, tạo /dev/led — ghi 1 thì LED sáng, 0 thì tắt, đọc ra mức hiện tại. Khi gỡ module phải tắt LED và iounmap.

chưa nạp moduleFSEL17 = 000chân LOW 0 V
Nạp / gỡ module
Điều khiển /dev/led
Quan sát

Thanh ghi GPIO

base 0x3F200000
GPFSEL10x3F200004 · R/W0x00000000
00000000000000000000000000000000
bit 23:21 = 000 → GPIO17 là INPUT
GPSET00x3F20001C · W0x00000000
00000000000000000000000000000000
bit 17 = 0 · ghi 1 → chân lên mức cao · đọc ra 0
GPCLR00x3F200028 · W0x00000000
00000000000000000000000000000000
bit 17 = 0 · ghi 1 → chân xuống mức thấp · đọc ra 0
GPLEV00x3F200034 · R0x00000000
00000000000000000000000000000000
bit 17 = 0 · mức thật trên chân GPIO0–31

GPIO17 nằm ở bit 23:21 của GPFSEL1 (000 input, 001 output) và là bit 17 của GPSET0, GPCLR0, GPLEV0. Chân chưa là output thì ghi GPSET0 không làm LED sáng.

1// led_kmod.c – điều khiển LED ở GPIO17 qua thanh ghi (Pi Zero 2 W)
2#include <linux/module.h>
3#include <linux/miscdevice.h>
4#include <linux/fs.h>
5#include <linux/io.h>
6#include <linux/bits.h>
7#include <linux/uaccess.h>
8
9#define GPIO_PHYS 0x3F200000 /* 0x3F000000 + 0x200000 */
10#define GPIO_SIZE 0x100
11#define GPFSEL1 0x04
12#define GPSET0 0x1C
13#define GPCLR0 0x28
14#define GPLEV0 0x34
15#define LED_PIN 17
16
17static void __iomem *gpio;
18
19static ssize_t led_write(struct file *f, const char __user *buf,
20 size_t n, loff_t *off)
21{
22 char c;
23 if (!n || get_user(c, buf))
24 return -EFAULT;
25 if (c == '1')
26 writel(BIT(LED_PIN), gpio + GPSET0);
27 else if (c == '0')
28 writel(BIT(LED_PIN), gpio + GPCLR0);
29 return n;
30}
31
32static ssize_t led_read(struct file *f, char __user *buf,
33 size_t n, loff_t *off)
34{
35 char s[2] = { '0', '\n' };
36 if (readl(gpio + GPLEV0) & BIT(LED_PIN))
37 s[0] = '1';
38 return simple_read_from_buffer(buf, n, off, s, 2);
39}
40
41static const struct file_operations led_fops = {
42 .owner = THIS_MODULE,
43 .write = led_write,
44 .read = led_read,
45};
46static struct miscdevice led_dev = {
47 .minor = MISC_DYNAMIC_MINOR,
48 .name = "led", /* → /dev/led */
49 .fops = &led_fops,
50};
51
52static int __init led_init(void)
53{
54 u32 v;
55 gpio = ioremap(GPIO_PHYS, GPIO_SIZE);
56 if (!gpio)
57 return -ENOMEM;
58 v = readl(gpio + GPFSEL1); /* GPIO17: bit 23:21 */
59 v &= ~(7 << 21);
60 v |= (1 << 21); /* 001 = output */
61 writel(v, gpio + GPFSEL1);
62 return misc_register(&led_dev);
63}
64
65static void __exit led_exit(void)
66{
67 misc_deregister(&led_dev);
68 writel(BIT(LED_PIN), gpio + GPCLR0); /* tắt LED */
69 writel(readl(gpio + GPFSEL1) & ~(7 << 21), gpio + GPFSEL1);
70 iounmap(gpio);
71}
72
73module_init(led_init);
74module_exit(led_exit);
75MODULE_LICENSE("GPL");

Terminal · dmesg

0x00000000
Chưa nạp module. Bấm “insmod” để bắt đầu.