Linux驱动编程篇(三)——LED驱动(一)简单LED驱动

一、LED驱动程序的实现目标及流程图

1、打开LED
2、关闭LED

在这里插入图片描述

二、LED驱动程序的实现部分

1、内核层LED驱动程序
2、应用层LED测试程序

三、内核层LED驱动程序的编程步骤

1、添加头文件
2、确定主设备号,也可以让内核分配
3、定义自己的 file_operations 结构体
4、实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
4.1 led_drv_open函数
  4.1.1使能时钟
  4.1.2使能GPIO
  4.1.3配置IO引脚
  4.1.4配置IO引脚方向
4.2 led_drv_write函数
  4.2.1获取应用层的数据
  4.2.3GPIO输出
4.3 把 file_operations 结构体告诉内核:register_chrdev.
4.4 创建类class_create
4.5 创建设备device_create.
5、实现入口函数:安装驱动程序时,就会去调用这个入口函数,执行工作:
5.1 ioremap虚拟地址映射物理地址
5.2 把 file_operations 结构体告诉内核:register_chrdev.
5.3 创建类class_create.
5.4 创建设备device_create.
6、实现出口函数:卸载驱动程序时,就会去调用这个入口函数,执行工作:
6.1 iounmap释放虚拟地址
6.2 把 file_operations 结构体从内核注销:unregister_chrdev.
6.3 销毁类class_create.
6.4 销毁设备结点class_destroy.
7、其他完善:GPL协议、驱动作者、驱动名称

四、具体实现

1、内核层LED驱动程序
 //1、添加头文件
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/errno.h>
 #include <linux/miscdevice.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/mutex.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/stat.h>
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/tty.h>
 #include <linux/kmod.h>
 #include <linux/gfp.h>
 #include <asm/io.h>
//函数声明
static ssize_t led_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos);
static ssize_t led_drv_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos);
static int led_drv_open(struct inode *inode, struct file *filp);
static int __init led_init(void);
static void __exit led_exit(void);
 /*registers*/
// RCC_PLL4CR地址:0x50000000 + 0x894
 static volatile unsigned int *RCC_PLL4CR;
 // RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
 static volatile unsigned int *RCC_MP_AHB4ENSETR;
 // GPIOA_MODER 地址:0x50002000 + 0x00
 static volatile unsigned int *GPIOA_MODER;
 // GPIOA_BSRR 地址: 0x50002000 + 0x18
 static volatile unsigned int *GPIOA_BSRR;
 // led类
 static struct class *led_class;
 
 //2、确定主设备号,也可以让内核分配
 static int major = 0;
 
 //3、定义自己的 file_operations 结构体
 static const struct file_operations my_fops = {
 		.read		= led_drv_read,
 		.write		= led_drv_write,
 		.open 		= led_drv_open,
 		.owner = THIS_MODULE,
 };
 
 //4、实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
 static ssize_t led_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
 {
 	return 0;
 }
 static ssize_t led_drv_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos)
 {
 	char val;
 	/*1、get data from app*/
 	/*copy_from_user(void * to, const void __user * from, unsigned long n):get form app*/
 	copy_from_user(&val,buffer,1);
 	/*2、set register out 1/0*/
 	if(val){
 		/*set gpio to let led on*/
 		*GPIOA_BSRR = (1<<26);
 	}else{
 		/*set gpio to let led off*/
 		*GPIOA_BSRR = (1<<10);
 	}
 	return 1;
 }
 static int led_drv_open(struct inode *inode, struct file *filp)
 {
 	/*1、enalbe PLL4,it is clock source for all gpio*/
 	*RCC_PLL4CR|= (1<<0);
 	while((*RCC_PLL4CR & (1<<1)) == 0)
 
 	/*2、enable gpio*/
 	*RCC_MP_AHB4ENSETR |= (1<<0);
 	
 	/*3、configure gpA10 as gpio*/
 	*GPIOA_MODER &= ~(3<<20);
	/* 4、configure gpio as output*/
 	*GPIOA_MODER |= (1<<20);
 	return 0;
 }
 //5、实现入口函数:安装驱动程序时,就会去调用这个入口函数,执行工作:
 //   (1)把 file_operations 结构体告诉内核:register_chrdev. 
 //   (2)创建类class_create. 
 //   (3)创建设备device_create.
 static int __init led_init(void)
 {
 	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
 	major = register_chrdev(0,"my_led",&my_fops);
	/*虚拟地址映射物理地址*/
 	//ioremap(phys_addr_t phys_addr, size_t size)
	// RCC_PLL4CR地址:0x50000000 + 0x894
 	RCC_PLL4CR = ioremap(0x50000000 + 0x894,4);
 	// RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28
 	RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28,4);
 	// GPIOA_MODER地址:0x50000000 + 0x00
 	GPIOA_MODER = ioremap(0x50000000 + 0x00,4);
 	// GPIOA_BSRR地址:0x50000000 + 0x18
 	GPIOA_BSRR = ioremap(0x50000000 + 0x18,4);
 	led_class = class_create(THIS_MODULE, "myled");
 	/*创建/dev/myled*/
 	device_create(led_class, NULL, MKDEV(major, 0),NULL,"myled");
	return 0;
 }
 
 //6、实现出口函数:卸载驱动程序时,就会去调用这个入口函数,执行工作:
 //   (1)把 file_operations 结构体从内核注销:unregister_chrdev. 
 //   (2)销毁类class_create. 
 //   (3)销毁设备结点class_destroy.
 static void __exit led_exit(void)
 {
  	// RCC_PLL4CR地址:0x50000000 + 0x894
 	iounmap(RCC_PLL4CR);
 	// RCC_MP_AHB4ENSETR地址:0x50000000 + 0xA28
 	iounmap(RCC_MP_AHB4ENSETR);
 	// GPIOA_MODER地址:0x50000000 + 0x00
 	iounmap(GPIOA_MODER);
 	// GPIOA_BSRR地址:0x50000000 + 0x18
 	iounmap(GPIOA_BSRR);
 	//销毁类
 	class_destroy(led_class);
 	//销毁设备
 	device_destroy(led_class, MKDEV(major, 0));
 	//注销驱动
 	unregister_chrdev(major,"my_led");
 }
 //修饰入口、出口函数
 module_init(led_init);
 module_exit(led_exit);
 //7、其他完善:GPL协议、驱动作者、驱动名称
 MODULE_AUTHOR("aipolo <369480046@qq.com>");
 MODULE_DESCRIPTION("led Driver");
 MODULE_LICENSE("GPL");
2、应用层LED测试程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
//ledtest /dev/myled on
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("open %s error\n",argv[0]);
		return -1;
	}
	//write  if是on,status = 1;if是off,status = 0;
	if(strcmp(argv[2],"on") == 0){
		printf("open led\n");
		status = 1;
	}
	write(fd,&status,1);
}
3、Makefile
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/armbuildroot-linux-gnueabihf_sdk-buildroot/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_my.o

五、实验结果

1、配置环境变量

export ARCH=arm64
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/armbuildroot-linux-gnueabihf_sdk-buildroot/bin

2、装载驱动

insmod /mnt/my_led.ko

3、查看设备
在这里插入图片描述在这里插入图片描述
4、实现
在这里插入图片描述
LED开
在这里插入图片描述
LED关
在这里插入图片描述
5、卸载驱动

rmmod led_my

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值