0%

LED驱动

LED驱动

GPIO模块一般结构:

a. 有多组GPIO,每组有多个GPIO

b. 使能:电源/时钟

c. 模式(Mode):引脚可用于GPIO或其他功能

d. 方向:引脚Mode设置为GPIO时,可以继续设置它是输出引脚,还是输入引脚

e. 数值:对于输出引脚,可以设置寄存器让它输出高、低电平

​ 对于输入引脚,可以读取寄存器得到引脚的当前电平

LED原理图

image-20211227203745402

image-20211227203833588

LED0对应引脚PI0

相关寄存器

image-20211227203252530

PI0

时钟寄存器

image-20211227205838761

RCC_PLL4CR地址:0x50000000 + 0x894

1
2
3
* enalbe PLL4, it is clock source for all gpio */
*RCC_PLL4CR |= (1<<0);
while ((*RCC_PLL4CR & (1<<1)) == 0);

使能GPIOI

image-20211227210036696

RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28

1
2
/* enable gpioI */
*RCC_MP_AHB4ENSETR |= (1<<8);

PI0模式设置

image-20211227210347472

GPIOI_MODER地址:0x5000A000 + 0x00

1
2
3
4
5
6
/*
* configure gpI0 as gpio
* configure gpio as output
*/
*GPIOI_MODER &= ~(3<<0);
*GPIOI_MODER |= (1<<0);

输出高低电平

image-20211227210634694

GPIOI_BSRR地址: 0x5000A000 + 0x18

驱动程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

static int major;
static struct class *led_class;

/* registers */
// RCC_PLL4CR地址:0x50000000 + 0x894
static volatile unsigned int *RCC_PLL4CR;

// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
static volatile unsigned int *RCC_MP_AHB4ENSETR;

// GPIOI_MODER 地址:0x5000A000 + 0x00
static volatile unsigned int *GPIOI_MODER;

// GPIOI_BSRR 地址: 0x5000A000 + 0x18
static volatile unsigned int *GPIOI_BSRR;

static ssize_t led_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
char val;
/* copy_from_user : get data from app */
copy_from_user(&val, buf, 1);

/* to set gpio register: out 1/0 */
if (val)
{
/* set gpa10 to let led on */
*GPIOI_BSRR = (1<<16);
}
else
{

/* set gpa10 to let led off */
*GPIOI_BSRR = (1<<0);
}
return 1;
}

static int led_open(struct inode *inode, struct file *filp)
{
/* enalbe PLL4, it is clock source for all gpio */
*RCC_PLL4CR |= (1<<0);
while ((*RCC_PLL4CR & (1<<1)) == 0);

/* enable gpioI */
*RCC_MP_AHB4ENSETR |= (1<<8);

/*
* configure gpI0 as gpio
* configure gpio as output
*/
*GPIOI_MODER &= ~(3<<0);
*GPIOI_MODER |= (1<<0);

return 0;
}

static struct file_operations led_fops = {
.owner = THIS_MODULE,
.write = led_write,
.open = led_open,
};

/* 入口函数 */
static int __init led_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

major = register_chrdev(0, "100ask_led", &led_fops);

/* ioremap(base_phy, size); */
// RCC_PLL4CR地址:0x50000000 + 0x894
RCC_PLL4CR = ioremap(0x50000000 + 0x894, 4);

// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28, 4);

// GPIOI_MODER 地址:0x5000A000 + 0x00
GPIOI_MODER = ioremap(0x5000A000 + 0x00, 4);

// GPIOI_BSRR 地址: 0x5000A000 + 0x18
GPIOI_BSRR = ioremap(0x5000A000 + 0x18, 4);

led_class = class_create(THIS_MODULE, "myled");
device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */

return 0;
}

static void __exit led_exit(void)
{
iounmap(RCC_PLL4CR);
iounmap(RCC_MP_AHB4ENSETR);
iounmap(GPIOI_MODER);
iounmap(GPIOI_BSRR);

device_destroy(led_class, MKDEV(major, 0));
class_destroy(led_class);

unregister_chrdev(major, "100ask_led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


// ledtest /dev/myled on
// ledtest /dev/myled off

int main(int argc, char **argv)
{
int fd;
char status = 0;

if (argc != 3)
{
printf("Usage: %s <dev> <on|off>\n", argv[0]);
printf(" eg: %s /dev/myled on\n", argv[0]);
printf(" eg: %s /dev/myled off\n", argv[0]);
return -1;
}
// open
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf("can not open %s\n", argv[0]);
return -1;
}

// write
if (strcmp(argv[2], "on") == 0)
{
status = 1;
}

write(fd, &status, 1);
return 0;
}

实验

Makefile文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册

KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4

all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o ledtest ledtest.c

clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f ledtest

obj-m += led_drv.o

ubuntn

1
2
3
4
5
6
7
8
9
10
book@100ask:~/01_all_series_quickstart/05_嵌入式Linux驱动开发基础知识/source/02_led_drv/00_led_drv_simple/stm32mp157$ make
make -C /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4 M=`pwd` modules
make[1]: Entering directory '/home/book/100ask_stm32mp157_pro-sdk/Linux-5.4'
CC [M] /home/book/01_all_series_quickstart/05_嵌入式Linux驱动开发基础知识/source/02_led_drv/00_led_drv_simple/stm32mp157/led_drv.o
Building modules, stage 2.
MODPOST 1 modules
LD [M] /home/book/01_all_series_quickstart/05_嵌入式Linux驱动开发基础知识/source/02_led_drv/00_led_drv_simple/stm32mp157/led_drv.ko
make[1]: Leaving directory '/home/book/100ask_stm32mp157_pro-sdk/Linux-5.4'
arm-buildroot-linux-gnueabihf-gcc -o ledtest ledtest.c
book@100ask:~/01_all_series_quickstart/05_嵌入式Linux驱动开发基础知识/source/02_led_drv/00_led_drv_simple/stm32mp157$ cp led_drv.ko ledtest /home/book/nfs_rootfs/

开发板

挂载NFS

1
busybox  mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

关闭心跳灯

1
echo none > /sys/class/leds/sys-led/trigger

挂载驱动

1
2
3
4
root@ATK-stm32mp1:/mnt# insmod led_drv.ko
[ 2844.864125] /home/book/01_all_series_quickstart/05_嵌入式Linux驱动开发基础知识/source/02_led_drv/00_led_drv_simple/stm32mp157/led_drv.c led_init 81
root@ATK-stm32mp1:/mnt# /mnt/ledtest /dev/myled on #开灯
root@ATK-stm32mp1:/mnt# /mnt/ledtest /dev/myled off #关灯