1 添加LED设备树结点
1.1 分析LED灯的原理图
找出LED灯引脚对应的GPIO控制器,而在内核源码中设备树文件已经将GPIO控制节点编写完成,我们只需要添加一下LED灯的结点以及对应的属性
eg:
pinctrl: pin-controller@50002000 {
#address-cells = <1>;//指定子节点用来描述地址的u32个数为1
#size-cells = <1>;//指定子节点用来描述地址长度的u32个数为1
compatible = "st,stm32mp157-pinctrl";//厂商信息
ranges = <0 0x50002000 0xa400>;//用来指定子节点的父地址 和地址大小
gpiof: gpio@50007000 {
gpio-controller;//空属性,起到标识作用,当前节点为GPIO控制器
#gpio-cells = <2>;//在别的节点中引用当前节点时需要2个u32类型的数据进行描述
reg = <0x5000 0x400>;//当前节点的地址信息
clocks = <&rcc GPIOF>;//GPIOF总线使能
st,bank-name = "GPIOF";//指定控制器名字
status = "disabled";//当前控制器状态
//okay:工作状态 //disabled:不工作状态
};
};
1.2 要添加的LED结点
myleds{
led1=<&gpioe 10 0>;//&gpioe表示使用的是那一组gpio控制器 10表示用的是这一组的第几个管脚 0表示默认状态 ,在这里引用gpio控制器节点时用的u32个数由gpio控制器节点中的#gpio-cells属性决定
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
};
2 调用GPIO子系统API进行LED控制操作
在设备树节点中解析出GPIO对象,并向内核申请使用GPIO编号。在内核空间使用unlocked_ioctl操作方法来实现控制LED灯的亮灭
/*
* Filename: /home/ubuntu/vscode/day2_/char_dev/mycdev.c
* Path: /home/ubuntu/vscode/day2_/char_dev
* Created Date: Tuesday, June 13th 2023, 1:39:34 pm
* Author: xinyue
*
* Copyright (c) 2023 Your Company
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include "led.h"
#include <linux/timer.h>
//主设备号
int major = 0;
struct gpio_desc *gpionum1, *gpionum2, *gpionum3; //描述GPIO结构体
struct timer_list mytimer; //定时器对象
char kbuf[1024] = {0};
struct class* cls; //向上提交目录信息结构体指针
struct device* dev; //向上提交设备结点结构体指针
struct device_node* dnode;
int timer_init(void);
//定义虚拟地址
unsigned int* vir_motor_rcc;
int mycdev_open (struct inode * inode, struct file * file)
{
printk("%s:%s:%d\n", __FUNCTION__, __FILE__, __LINE__);
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
//逻辑控制
switch(cmd)
{
case LED1_ON:
gpiod_set_value(gpionum1, 1);
break;
case LED1_OFF:
gpiod_set_value(gpionum1, 0);
break;
case LED2_ON:
gpiod_set_value(gpionum2, 1);
break;
case LED2_OFF:
gpiod_set_value(gpionum2, 0);
break;
case LED3_ON:
gpiod_set_value(gpionum3, 1);
break;
case LED3_OFF:
gpiod_set_value(gpionum3, 0);
break;
case LED_WHILE:
timer_init();//启动
break;
case LED_STOP:
del_timer(&mytimer);//删除
break;
default:
break;
}
return 0;
}
int mycdev_close (struct inode * inode, struct file * file)
{
printk("%s:%s:%d\n", __FUNCTION__, __FILE__, __LINE__);
return 0;
}
struct file_operations fops = {
.open=mycdev_open,
.unlocked_ioctl=mycdev_ioctl,
.release=mycdev_close
};
int dev_create(void)
{
//向上提交目录信息
cls = class_create(THIS_MODULE, "mycdev");
if(IS_ERR(cls)) //返回值指向错误码区
{
printk("class_create failed\n");
return -PTR_ERR(cls);
}
printk("class_create succeeded\n");
//向上提交设备结点信息
dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "mycleds");
if(IS_ERR(dev))
{
printk("device_create failed\n");
return -PTR_ERR(dev);
}
printk("device_create succeeded\n");
return 0;
}
//解析设备树结点,找出GPIO编号
int find_gpiox(void)
{
//找出设备树结点首地址
dnode = of_find_node_by_name(NULL, "myleds");
if(dnode == NULL)
{
printk("of_find_node_by_name failed\n");
return -1;
}
//根据结点找到对应的GPIO编号,并申请使用编号,初始化为低电平输出模式
gpionum1 = gpiod_get_from_of_node(dnode, "led1", 0, GPIOD_OUT_LOW, NULL);//led1
if(IS_ERR(gpionum1))
{
printk("gpiod_get_from_of_node failed\n");
return -PTR_ERR(gpionum1);
}
gpionum2 = gpiod_get_from_of_node(dnode, "led2", 0, GPIOD_OUT_LOW, NULL);//led2
if(IS_ERR(gpionum2))
{
printk("gpiod_get_from_of_node failed\n");
return -PTR_ERR(gpionum2);
}
gpionum3 = gpiod_get_from_of_node(dnode, "led3", 0, GPIOD_OUT_LOW, NULL);//led3
if(IS_ERR(gpionum3))
{
printk("gpiod_get_from_of_node failed\n");
return -PTR_ERR(gpionum3);
}
return 0;
}
int flags = 0;
//定时器处理函数
void mytimer_func(struct timer_list *timer)
{
if(0 == flags)
{
gpiod_set_value(gpionum3, 0);
gpiod_set_value(gpionum1, 1);
flags = 1;
}
else if(1 == flags)
{
gpiod_set_value(gpionum1, 0);
gpiod_set_value(gpionum2, 1);
flags = 2;
}
else if(2 == flags)
{
gpiod_set_value(gpionum2, 0);
gpiod_set_value(gpionum3, 1);
flags = 0;
}
mod_timer(&mytimer, jiffies + HZ); //重启定时器
}
//初始化一个定时器
int timer_init(void)
{
mytimer.expires = jiffies + HZ;
//初始化定时器
timer_setup(&mytimer, mytimer_func, 0);
//将定时器注册进内核并启用
add_timer(&mytimer);
return 0;
}
static int __init mycdev_init(void)
{
major = register_chrdev(0, "mycleds", &fops);
if(0 == major)
{
printk("%s\n", "mycdev register failed");
return -1;
}
printk("%s:%s:%d major=%d\n", __FUNCTION__, __FILE__, __LINE__, major);
dev_create(); //向上提交信息(创建设备结点)
find_gpiox(); //初始化GPIO引脚
return 0;
}
static void __exit mycdev_exit(void)
{
//注销定时器
del_timer(&mytimer);
//释放申请的编号
gpiod_put(gpionum1);
gpiod_put(gpionum2);
gpiod_put(gpionum3);
//销毁设备节点
device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
//注销设备驱动
unregister_chrdev(major, "mycleds");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
在用户空间使用ioctl函数配合内核空间的操作方法unlocked_ioctl来实现LED灯的控制
/*
* Filename: /home/ubuntu/vscode/day7_/test.c
* Path: /home/ubuntu/vscode/day7_
* Created Date: Tuesday, June 20th 2023, 2:30:34 pm
* Author: xinyue
*
* Copyright (c) 2023 Your Company
*/
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include <sys/ioctl.h>
#include"led.h"
int main(int argc, char const *argv[])
{
int a,b;
char buf[128]={0};
int fd=open("/dev/mycleds",O_RDWR);
if(fd<0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while(1)
{
printf("请输入:11(led1开灯) 10(led1关灯) 1(流水灯启动) 0(流水灯停止)>>>");
scanf("%d",&a);
switch(a)
{
case 11:
ioctl(fd,LED1_ON);//开灯
break;
case 10:
ioctl(fd,LED1_OFF);//关灯
break;
case 21:
ioctl(fd,LED2_ON);//开灯
break;
case 20:
ioctl(fd,LED2_OFF);//关灯
break;
case 31:
ioctl(fd,LED3_ON);//开灯
break;
case 30:
ioctl(fd,LED3_OFF);//关灯
break;
case 1:
ioctl(fd,LED_WHILE);//流水灯启动
break;
case 0:
ioctl(fd,LED_STOP);//流水灯停止
break;
default:
printf("输入有误,请重新输入>>>\n");
break;
}
}
close(fd);
return 0;
}
头文件
/*
* Filename: /home/ubuntu/vscode/day7_/led.h
* Path: /home/ubuntu/vscode/day7_
* Created Date: Tuesday, June 20th 2023, 2:04:59 pm
* Author: xinyue
*
* Copyright (c) 2023 Your Company
*/
#ifndef __LED_H__
#define __LED_H__
#define LED1_ON _IO('l', 11)
#define LED1_OFF _IO('l', 10)
#define LED2_ON _IO('l', 21)
#define LED2_OFF _IO('l', 20)
#define LED3_ON _IO('l', 31)
#define LED3_OFF _IO('l', 30)
#define LED_WHILE _IO('l', 1)
#define LED_STOP _IO('l', 0)
#endif