通过串口工具输入命令,操作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 0x50000A28
-
- typedef 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
- {
- devic = device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
- if(IS_ERR(devic))
- {
- ret = PTR_ERR(devic);
- goto ERR5;
- }
- }
- return 0;
- ERR5:
- for(--i;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
- {
- device_destroy(cls,MKDEV(major,i));
- }
- class_destroy(cls);
- //释放驱动
- cdev_del(cdev);
- //释放设备号
- unregister_chrdev_region(MKDEV(major,minor),count);
- //注销字符设备驱动
-
- kfree(cdev);
- }
-
- module_init(myled_init);
- module_exit(myled_exit);
-
- MODULE_LICENSE("GPL");
点灯测试现象:
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灯熄灭

点灯实验现象
串口点灯实验现象
-
相关阅读:
ABAP 企业微信ASE CBC 解密算法
故障维修无忧服务:OLED透明拼接屏的专业技术支持与保修服务
机器视觉杂
数组复制(java)
运行java命令出现 Error: Invalid or corrupt jarfile XXX.jar
apollo源码启动服务,apollo源码分析
进程控制,父子进程
linux查看进程对应的线程(数)
Spring之文件上传下载,jrebel,多文件上传
自动安装系统-桌面
-
原文地址:https://blog.csdn.net/ww1106/article/details/127937189