在内核模块中启用定时器,定时1s,让led1 一秒亮、一面灭
#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include<linux/timer.h>
unsigned int gpiono;
struct device_node *dnode;
//分配定时器对象
struct timer_list mytimer;
//定时器处理函数
void mytimer_func(struct timer_list *timer)
{
//LED1一秒亮一秒灭
gpio_set_value(gpiono,!gpio_get_value(gpiono));
//再次启用定时器
mod_timer(timer,jiffies+HZ);
}
static int __init mycdev_init(void)
{
int ret;
//初始化定时器对象
mytimer.expires=jiffies+HZ;//设置定时一秒的阈值
timer_setup(&mytimer,mytimer_func,0);
//将定时器对象注册进内核
add_timer(&mytimer);
//解析led灯的设备树节点
dnode=of_find_node_by_path("/myleds");
if(dnode==NULL)
{
printk("解析设备树节点失败\n");
return -ENXIO;
}
printk("解析设备树节点成功\n");
//根据设备树节点解析led1gpio编号
gpiono=of_get_named_gpio(dnode,"led1",0);
if(gpiono<0)
{
printk("gpio编号解析失败\n");
}
printk("解析gpio编号成功gpiono=%d\n",gpiono);
//申请gpio编号
ret=gpio_request(gpiono,NULL);
if(ret<0)
{
printk("申请gpio编号失败\n");
return ret;
}
//led1对应的gpio的管脚为输出
gpio_direction_output(gpiono,0);
return 0;
}
static void __exit mycdev_exit(void)
{
//灭灯
gpio_set_value(gpiono,0);
//释放gpio编号
gpio_free(gpiono);
//注销定时器对象
del_timer(&mytimer);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
基于gpio子系统完成LED灯驱动的注册,应用程序测试
驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/uaccess.h>
#include<linux/io.h>
#include<linux/device.h>
#include<linux/poll.h>
#include<linux/of.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
struct class *cls;
struct device *dev;
int major;
/*
myleds{
led1=<&gpioe 10 0>;
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
};
*/
struct device_node *dnode;
unsigned int gpiono1;
unsigned int gpiono2;
unsigned int gpiono3;
char kbuf[128]={0};
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *off)
{
int ret;
//判断kbuf的大小,如果<size,把size的数值修改为kbuf的大小
if(size>sizeof(kbuf))
size=sizeof(kbuf);
ret=copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("copy_to_user filed\n");
return -EIO;//拷贝失败返回错误码
}
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *off)
{
int ret;
if(size>sizeof(kbuf))
size=sizeof(kbuf);
ret=copy_from_user(kbuf,ubuf,size);
if(ret)
{
printk("copy_from_user filed\n");
return -EIO;//拷贝失败返回错误码
}
if(kbuf[0]=='1')
{
gpio_set_value(gpiono1,!gpio_get_value(gpiono1));
}
else if(kbuf[0]=='2')
{
gpio_set_value(gpiono2,!gpio_get_value(gpiono2));
}
else if(kbuf[0]=='3')
{
gpio_set_value(gpiono3,!gpio_get_value(gpiono3));
}
return 0;
}
//定义操作方法结构体变量并初始化
struct file_operations fops=
{
.open=mycdev_open,
.read=mycdev_read,
.write=mycdev_write,
.release=mycdev_close,
};
static int __init mycdev_init(void)
{
int ret1,ret2,ret3;
int i;
// 字符设备驱动注册
major = register_chrdev(0, "myleddev", &fops);
if (major < 0)
{
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功:major=%d\n", major);
// 向上提交目录
cls = class_create(THIS_MODULE, "myleddev");
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, "myleddev%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");
//根据设备树节点解析led gpio编号
gpiono1=of_get_named_gpio(dnode,"led1",0);
if(gpiono1<0)
{
printk("gpio编号解析失败\n");
}
gpiono2=of_get_named_gpio(dnode,"led2",0);
if(gpiono2<0)
{
printk("gpio编号解析失败\n");
}
gpiono3=of_get_named_gpio(dnode,"led3",0);
if(gpiono3<0)
{
printk("gpio编号解析失败\n");
}
printk("解析gpio编号成功gpiono1=%d\n",gpiono1);
printk("解析gpio编号成功gpiono2=%d\n",gpiono2);
printk("解析gpio编号成功gpiono3=%d\n",gpiono3);
//申请gpio编号
ret1=gpio_request(gpiono1,NULL);
if(ret1<0)
{
printk("申请gpio编号失败\n");
return ret1;
}
ret2=gpio_request(gpiono2,NULL);
if(ret2<0)
{
printk("申请gpio编号失败\n");
return ret2;
}
ret3=gpio_request(gpiono3,NULL);
if(ret3<0)
{
printk("申请gpio编号失败\n");
return ret3;
}
printk("申请gpio编号成功\n");
//led对应的gpio的管脚为输出
gpio_direction_output(gpiono1,0);
gpio_direction_output(gpiono2,0);
gpio_direction_output(gpiono3,0);
gpio_set_value(gpiono1,0);
gpio_set_value(gpiono2,0);
gpio_set_value(gpiono3,0);
return 0;
}
static void __exit mycdev_exit(void)
{
int i;
//灭灯
gpio_set_value(gpiono1,0);
gpio_set_value(gpiono2,0);
gpio_set_value(gpiono3,0);
//释放gpio编号
gpio_free(gpiono1);
gpio_free(gpiono2);
gpio_free(gpiono3);
// 销毁设备节点信息
for (i = 0; i < 3; i++)
{
device_destroy(cls, MKDEV(major, i));
}
// 销毁目录信息
class_destroy(cls);
// 注销字符设备驱动
unregister_chrdev(major, "myleddev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
测试程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[128] = {0};
int fd = open("/dev/myleddev0", O_RDWR);
if (fd < 0)
{
printf("打开设备文件失败\n");
exit(-1);
}
while (1)
{
printf("请输入控制命令--1:led1的亮灭 2:led2的亮灭 3:led3的亮灭>");
fgets(buf, sizeof(buf), stdin); // 在终端读取
buf[strlen(buf) - 1] = '\0'; // 吃掉末尾的‘\n’;
write(fd, buf, sizeof(buf));
}
close(fd);
return 0;
}
实验结果