通过串口工具输入命令,操作LED灯的点亮与熄灭
要求:
1)分部实现注册字符设备驱动
2)自动创建设备节点
3)通过结构体对led灯地址进行映射
4)次设备号完成私有数据传参
代码实现:
1、头文件代码的编写:
①对GPIO寄存器进行结构体的封装
②对寄存器的地址进行宏定义
③通过枚举给LED灯进行赋值

2、编写功能代码:
open函数:
通过inode结构体获取次设备号,并通过私有数据进行传参
入口函数:
①分布实现字符设备驱动
a:分配字符设备驱动(struct cdev *cdev_alloc(void))
b:完成设备驱动的初始化(void cdev_init(struct cdev *cdev, const struct file_operations *fops))
c:申请设备号(静态指定:int register_chrdev_region(dev_t from, unsigned count, const char *name);动态申请:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name))
d:注册字符设备驱动(int cdev_add(struct cdev *p, dev_t dev, unsigned count))
②完成LED灯相应寄存器的初始化
③自动创建设备节点(class_create提交目录信息,device_create提交设备节点信息)
出口函数:
a:销毁设备节点信息
b:释放驱动
c:释放设备号
d:注销字符设备驱动
许可证

附:实现代码
头文件:
#ifndef __MYLED_H__
#define __MYLED_H_typedef struct{volatile unsigned int MODER;volatile unsigned int OTYPER;volatile unsigned int OSPEEDR;volatile unsigned int PUPDR;volatile unsigned int IDR;volatile unsigned int ODR;
}gpio_t;#define GPIOE_ADDR 0x50006000
#define GPIOF_ADDR 0x50007000
#define RCC_ADDR 0x50000A28typedef enum{LED1,LED2,LED3,
}leds_t;#endif
makefile代码:
ARCH ?= x86
FILE ?= led
ARM:=arm
X86:=x86
ifeq ($(ARCH),$(ARM))
KERNEDIR:=/home/ubuntu/linux-5.10.61
endif
ifeq ($(ARCH),$(X86))
KERNEDIR:=/lib/modules/$(shell uname -r)/build
endif
PWD:=$(shell pwd)
KBUILD_EXTRA_SYMBOLS:=/home/ubuntu/ww/driver/01_linux/03_sym/01_demo/Module.symvers
all:make -C $(KERNEDIR) M=$(PWD) modules
clean:make -C $(KERNEDIR) M=$(PWD) clean
obj-m:=$(FILE).o
功能代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include"./led.h"
#define GNAME "mydev"volatile gpio_t* VIRT_GPIOE;
volatile gpio_t* VIRT_GPIOF;
volatile unsigned int* VIRT_RCC;unsigned int major =0;
char kbuf[128]={0};
struct cdev* cdev;
struct class * cls;
struct device *devic;
dev_t dev1;
int minor = 0;
unsigned count=3;int mydev_open(struct inode *inode, struct file *file)
{int min;printk("%s:%s:%d",__FILE__,__func__,__LINE__);min=MINOR(inode->i_rdev);file->private_data=(void*)min;return 0;
}
ssize_t mydev_read(struct file *file, char __user *ubuf, size_t size, loff_t * loff)
{printk("%s:%s:%d",__FILE__,__func__,__LINE__);return 0;
}
ssize_t mydev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{int ret;int min;//printk("%s:%s:%d",__FILE__,__func__,__LINE__);if(size > sizeof(kbuf)) size = sizeof(kbuf);ret = copy_from_user(kbuf,ubuf,size);if(ret){printk("copy from user is error\n");return -EIO;}//printk("copy from user kbuf = %s\n",kbuf);min=(int)file->private_data;switch(min){case 0:if(kbuf[0]=='1'){VIRT_GPIOE->ODR |= (0x1<<10);}else if(kbuf[0]=='0'){VIRT_GPIOE->ODR &= (~(0x1<<10));}break;case 2:if(kbuf[0]=='1'){VIRT_GPIOE->ODR |= (0x1<<8);}else if(kbuf[0]=='0'){VIRT_GPIOE->ODR &= (~(0x1<<8));}break;case 1:if(kbuf[0]=='1'){VIRT_GPIOF->ODR |= (0x1<<10);}else if(kbuf[0]=='0'){VIRT_GPIOF->ODR &= (~(0x1<<10));}break;}return 0;
}int mydev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d",__FILE__,__func__,__LINE__);return 0;
}
struct file_operations fops={.open=mydev_open,.read=mydev_read,.write=mydev_write,.release=mydev_close,
};
static int __init myled_init(void)
{int i;int ret;//分配字符设备驱动cdev=cdev_alloc();if(NULL==cdev){printk("cdev alloc error\n");goto ERR1;}//设备驱动初始化cdev_init(cdev,&fops);//申请设备号if(major>0){ret=register_chrdev_region(MKDEV(major,minor),count,GNAME);if(ret!=0){printk("register chrdev region error\n");ret = -ENOMEM;goto ERR2;}}else{ret=alloc_chrdev_region(&dev1,0,count,GNAME);if(ret!=0){printk("alloc chrdev region error\n");ret = -ENOMEM;goto ERR2;}major = MAJOR(dev1);minor = MINOR(dev1);}//驱动的注册ret = cdev_add(cdev,MKDEV(major,minor),count);if(ret!=0){printk("cdev add error\n");ret = -EIO;goto ERR3;}//寄存器初始化VIRT_RCC = ioremap(RCC_ADDR,4);if(NULL == VIRT_RCC){printk("VIRT_RCC error\n");return -ENXIO;}VIRT_GPIOE = ioremap(GPIOE_ADDR,sizeof(gpio_t));if(NULL == VIRT_GPIOE){printk("VIRT_GPIOE error\n");return -ENXIO;}VIRT_GPIOF = ioremap(GPIOF_ADDR,sizeof(gpio_t));if(NULL == VIRT_GPIOF){printk("VIRT_GPIOF error\n");return -ENXIO;}*VIRT_RCC |= (0x3<<4);VIRT_GPIOE->MODER &= (~(0x3<<20));VIRT_GPIOE->MODER |= (0x1<<20);VIRT_GPIOE->ODR &= (~(0x1<<10));VIRT_GPIOE->MODER &= (~(0x3<<16));VIRT_GPIOE->MODER |= (0x1<<16);VIRT_GPIOE->ODR &= (~(0x1<<8));VIRT_GPIOF->MODER &= (~(0x3<<20));VIRT_GPIOF->MODER |= (0x1<<20);VIRT_GPIOF->ODR &= (~(0x1<<10));//自动创建设备节点cls = class_create(THIS_MODULE,GNAME);if(IS_ERR(cls)){ret = PTR_ERR(cls);goto ERR4;}for(i=0;i=0;i--){device_destroy(cls,MKDEV(major,i));}class_destroy(cls);
ERR4:cdev_del(cdev);
ERR3:unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:kfree(cdev);
ERR1:return -EIO;}static void __exit myled_exit(void)
{int i;//销毁设备节点信息for(i=0;i
点灯测试现象:
1.在串口工具进行输入:
echo 1 > /dev/myled0 ---->led1灯点亮
echo 0 > /dev/myled0 ---->led1灯熄灭
echo 1 > /dev/myled1 ---->led1灯点亮
echo 0 > /dev/myled1 ---->led1灯熄灭
echo 1 > /dev/myled2 ---->led1灯点亮
echo 0 > /dev/myled2 ---->led1灯熄灭

点灯实验现象
串口点灯实验现象