在内核模块中启用定时器,定时 1s,让 led1 一秒亮,一秒灭
#include
#include
#include
#include
#include
#include
#include
#include
struct device_node *dnode;
struct gpio_desc *gpiono;
struct timer_list mytimer; //定义定时器对象
//定义定时器处理函数
void mytimer_func(struct timer_list *timer)
{
static int status = 0;
status = !status;
gpiod_set_value(gpiono, status);
//再次启用定时器
mod_timer(timer, jiffies + HZ);
}
static int __init mycdev_init(void)
{
//解析 led 灯的设备树节点
dnode = of_find_node_by_path("/myleds");
if (dnode == NULL) {
printk("解析设备树节点失败\n");
return -ENXIO;
}
printk("解析设备树节点成功\n");
//根据设备树节点获取 led 灯的 gpio 编号并向内核注册
gpiono = gpiod_get_from_of_node(dnode, "led1", 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(gpiono)) {
printk("申请gpio失败\n");
return -PTR_ERR(gpiono);
}
//初始化定时器对象
mytimer.expires = jiffies + HZ;
timer_setup(&mytimer, mytimer_func, 0);
//将定时器对象注册进内核
add_timer(&mytimer);
return 0;
}
static void __exit mycdev_exit(void)
{
del_timer(&mytimer);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
基于 gpio 子系统完成 LED 灯驱动的注册,应用程序测试
#include
#include
#include
#include
#include
#include
#include
#include "head.h"
struct device_node *dnode;
struct gpio_desc *led[3];
int major;
struct class *cls;
struct device *dev;
void toggle_led(int i)
{
static int value[3] = { 0, 0, 0 };
value[i] = !value[i];
gpiod_set_value(led[i], value[i]);
}
static int mycdev_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int mycdev_release(struct inode *inode, struct file *filp)
{
return 0;
}
long mycdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case LED_TOGGLE:
toggle_led(arg);
break;
}
return 0;
}
struct file_operations fops = {
.open = mycdev_open,
.unlocked_ioctl = mycdev_ioctl,
.release = mycdev_release,
};
static int __init mycdev_init(void)
{
int i;
char str[5];
// 字符设备驱动注册
major = register_chrdev(0, "mychrdev", &fops);
if (major < 0) {
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n", major);
// 向上提交目录
cls = class_create(THIS_MODULE, "mychrdev");
if (IS_ERR(cls)) {
printk("向上提交目录失败\n");
return -PTR_ERR(cls);
}
// 向上提交设备节点信息
// 为三盏灯创建三个设备文件
for (i = 0; i < 3; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL,
"mychrdev%d", i);
if (IS_ERR(dev)) {
printk("向上提交设备节点信息失败\n");
return -PTR_ERR(dev);
}
}
//解析led灯的设备树节点
dnode = of_find_node_by_path("/myleds");
if (dnode == NULL) {
printk("解析设备树节点失败\n");
return -ENXIO;
}
printk("解析设备树节点成功\n");
//根据设备树节点解析 led123 gpio结构体并向内核注册
for (i = 0; i < 3; i++) {
sprintf(str, "led%d", i + 1);
led[i] = gpiod_get_from_of_node(dnode, str, 0, GPIOD_OUT_LOW,
NULL);
if (IS_ERR(led[i])) {
printk("申请gpio失败\n");
return -PTR_ERR(led[i]);
}
}
return 0;
}
static void __exit mycdev_exit(void)
{
int i;
//灭灯
for (i = 0; i < 3; i++)
gpiod_set_value(led[i], 0);
//注销gpio信息
for (i = 0; i < 3; i++)
gpiod_put(led[i]);
for (i = 0; i < 3; i++)
device_destroy(cls, MKDEV(major, i));
class_destroy(cls);
unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
用户空间测试文件:
#include
#include
#include
#include
#include
#include "head.h"
int main(int argc, char const *argv[])
{
int a, fd[3];
char str[15];
for (a = 0; a < 3; a++) {
sprintf(str, "/dev/mychrdev%d", a);
fd[a] = open("/dev/mychrdev0", O_RDWR);
if (fd[a] < 0) {
printf("打开设备文件失败\n");
exit(-1);
}
}
while (1) {
printf("请选择要开关的灯\n");
printf("0(LED1) 1(LED2) 2(LED3)>");
scanf("%d", &a);
ioctl(fd[a], LED_TOGGLE, a);
}
for (a = 0; a < 3; a++)
close(fd[a]);
return 0;
}