1、在内核设备树中添加设备信息:
LED1的设备树编写需要参考内核的帮助文档:
linux-5.10.61/Documentation/devicetree/bindings/gpio
在根节点内部添加led灯设备树节点
:~/linux-5.10.61/arch/arm/boot/dts $ vi stm32mp157a-fsmp1a.dts
myled.c
#include "mydev.h"
int gpiono, gpiono1, gpiono2;
struct device_node *dnode;
struct timer_list mytimer;
struct class *cls;
struct device *dev;
// 定义cdev的结构体指针变量
struct cdev *cdev;
unsigned int major = 0;
unsigned int minor = 0;
int ret, i;
char kbuf[128] = {0};
// 定时器时间到达的处理函数
void timer_led_function(struct timer_list *timer)
{
gpio_set_value(gpiono, !gpio_get_value(gpiono)); // 让灯取反
mod_timer(timer, jiffies + 5 * HZ); // 再次启动定时器
}
/****************************************************************/
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
/**************************************************************/
ssize_t mycdev_read(struct file *file, char __user *ubuf,
size_t size, loff_t *offs)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_to_user(ubuf, kbuf, size);
if (ret)
{
printk("copy data to user error\n");
return -EIO;
}
return size;
}
/**************************************************************/
ssize_t mycdev_write(struct file *file,
const char __user *ubuf, size_t size, loff_t *off)
{
int ret;
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(kbuf))
size = sizeof(kbuf);
ret = copy_from_user(kbuf, ubuf, size);
if (ret)
{
printk("copy data from user error\n");
return -EIO;
}
switch (kbuf[0])
{
case '1': // led1灯亮
// 开灯
gpio_set_value(gpiono, 1);
break;
case '2': // led2灯亮
gpio_set_value(gpiono1, 1);
break;
case '3': // led3灯亮
gpio_set_value(gpiono2, 1);
break;
case '4': // led1灯灭
gpio_set_value(gpiono, 0);
break;
case '5': // led2灯灭
gpio_set_value(gpiono1, 0);
break;
case '6': // led3灯灭
gpio_set_value(gpiono2, 0);
break;
}
return size;
}
/**************************************************************/
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
/**************************************************************/
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
/**************************************************************/
static int __init mycdev_init(void)
{
dev_t devno;
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("cdev alloc memory error\n");
ret = -ENOMEM;
goto ERR1;
}
printk("分配对象成功\n");
// 2.对象的初始化
cdev_init(cdev, &fops);
// 3.申请设备号
ret = alloc_chrdev_region(&devno, minor, 1, "myled");
if (ret)
{
printk("dynamic:alloc device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
printk("动态申请设备号成功\n");
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), 1);
if (ret)
{
printk("cdev register error\n");
goto ERR3;
}
printk("字符驱动注册成功\n");
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, "myled"); // 向上提交设备目录
if (IS_ERR(cls))
{
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
printk("向上提交设备目录成功\n");
dev = device_create(cls, NULL, MKDEV(major, minor), NULL, "myled"); // 向上提交设备节点
if (IS_ERR(dev))
{
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
printk("向上提交设备节点成功\n");
printk("字符设备驱动的注册成功\n");
// 1、分配一个定时器对象
// 2、对象初始化
mytimer.expires = jiffies + HZ; // 定时器设置时间为1s
timer_setup(&mytimer, timer_led_function, 0);
// 3、启动定时器
add_timer(&mytimer);
// 解析设备树,获得设备节点
dnode = of_find_node_by_name(NULL, "myleds");
if (dnode == NULL)
{
printk("解析设备树失败\n");
return -1;
}
printk("解析设备树成功\n");
/**************************led1************************************/
// 根据gpio的节点解析到gpio的编号
gpiono = of_get_named_gpio(dnode, "led1", 0);
if (gpiono < 0)
{
printk("解析到gpio的编号失败\n");
return -1;
}
printk("解析到gpio的编号成功\n");
// 申请gpio编号
if (gpio_request(gpiono, NULL))
{
printk("申请gpio编号失败\n");
return -1;
}
printk("申请gpio编号成功\n");
// 设置管脚为输出
gpio_direction_output(gpiono, 0);
/****************************led2**********************************/
// 根据gpio的节点解析到gpio的编号
gpiono1 = of_get_named_gpio(dnode, "led2", 0);
if (gpiono1 < 0)
{
printk("解析到gpio_led2的编号失败\n");
return -1;
}
printk("解析到gpio_led2的编号成功\n");
// 申请gpio编号
if (gpio_request(gpiono1, NULL))
{
printk("申请gpio_led2编号失败\n");
return -1;
}
printk("申请gpio_led2编号成功\n");
// 设置管脚为输出
gpio_direction_output(gpiono1, 0);
/*****************************led3*********************************/
// 根据gpio的节点解析到gpio的编号
gpiono2 = of_get_named_gpio(dnode, "led3", 0);
if (gpiono2 < 0)
{
printk("解析到gpio_led3的编号失败\n");
return -1;
}
printk("解析到gpio_led3的编号成功\n");
// 申请gpio编号
if (gpio_request(gpiono2, NULL))
{
printk("申请gpio_led3编号失败\n");
return -1;
}
printk("申请gpio_led3编号成功\n");
// 设置管脚为输出
gpio_direction_output(gpiono2, 0);
return 0;
/******************goto执行跳转语句*****************************/
ERR5:
device_destroy(cls, MKDEV(major, minor));
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major, minor), 1);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
// 1.销毁设备节点
device_destroy(cls, MKDEV(major, minor));
class_destroy(cls);
// 2.销毁字符设备驱动
cdev_del(cdev);
// 3.销毁设备号
unregister_chrdev_region(MKDEV(major, minor), 1);
// 4.释放动态申请的cdev内存
kfree(cdev);
// 删除定时器
del_timer(&mytimer);
// 灭灯
gpio_set_value(gpiono, 0);
gpio_set_value(gpiono1, 0);
gpio_set_value(gpiono2, 0);
// 释放gpio编号
gpio_free(gpiono);
gpio_free(gpiono1);
gpio_free(gpiono2);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
test.c
#include "c.h"
int count = 5;
int main(int argc, const char *argv[])
{
char buf[128] = {0};
int fd2, ret, i;
fd2 = open("/dev/myled", O_RDWR);
if (fd2 < 0)
{
printf("打开mycdev设备失败\n");
exit(-1);
}
while (1)
{
memset(buf,0,sizeof(buf));//清空buf
printf("1(1亮灯)2(2亮灯)3(3亮灯)4(1亮灭)5(2亮灭)6(3亮灭)\n");
printf("请输入控制码:>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
write(fd2,buf,sizeof(buf));
}
close(fd2);
return 0;
}