通过串口工具输入命令,操作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=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灯熄灭
点灯实验现象
串口点灯实验现象