led点灯是最常用的也是最简单的驱动程序,在linux内核当中,应该有现成的配置和驱动,但它是通过控制/sys属性,而不是/dev属性,本文基于字符驱动,通过控制/dev实现
硬件电路
板子上有3个LED灯,只有一个用户可以控制,其余2个分别是底板和核心板上的电源指示灯,用户LED灯的原理图如下所示
![image.png](//file1.elecfans.com/web2/M00/04/1E/wKgZombKgI6ATbSQAAB_9-zXZao913.png)
即D1灯,通过GPIO85控制其的亮灭,当GPIO85输出低电平时,LED点亮,反之亦然。
配置驱动
设备树配置
在设备树中,默认配置用户led为心跳灯,如下所示
leds {
compatible = "gpio-leds";
led1 {
label = "led1";
gpios = <&gpio 85 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
default-state = "on";
};
};
对应的驱动,位于drivers/leds/leds-gpio.c,
在此将默认dts配置注释,增加如下设备树配置信息
gpio_led{
compatible = "lx,gpio-led";
label = "led";
gpios = <&gpio 85 GPIO_ACTIVE_LOW>;
status = "ok";
};
驱动代码
平台驱动
定义平台驱动,设置compatible 与设备树相同,引脚probe函数lx_led_probe
static const struct of_device_id led_ids[] = {
{ .compatible = "lx,gpio-led"},
{ }
};
struct platform_driver lx_gpio_led_driver = {
.probe = lx_led_probe,
.driver = {
.name = "gpioled",
.owner = THIS_MODULE,
.of_match_table = led_ids,
}
};
probe信息
主要3部分:解析设备树信息,字符设备cdev,class/device设备
static int lx_led_probe(struct platform_device *pdv)
{
int ret = 0;
pr_info("match successed\n");
led_device_node = of_find_node_by_path("/gpio_led");
if(led_device_node == NULL)
{
pr_err( "it get led node failed! \n");
return -1;
}
led = of_get_named_gpio(led_device_node, "gpios", 0);
pr_info("led = %d\n",led);
gpio_direction_output(led, 0);
ret = alloc_chrdev_region(&led_devno, 0, 1, "gpioled");
if(ret < 0){
pr_err("fail to alloc led_devno\n");
goto alloc_err;
}
led_chr_dev.owner = THIS_MODULE;
cdev_init(&led_chr_dev, &led_chr_dev_fops);
ret = cdev_add(&led_chr_dev, led_devno, 1);
if(ret < 0)
{
printk("fail to add cdev\n");
goto add_err;
}
class_led = class_create(THIS_MODULE, "gpioled");
device = device_create(class_led, NULL, led_devno, NULL, "gpioled");
return 0;
add_err:
unregister_chrdev_region(led_devno, 1);
pr_err("egister_chrdev error! \n");
alloc_err:
return -1;
}
读写控制
led主要通过输出0或1控制亮灭,故只需要实现write函数(或ioctl)
static ssize_t lx_led_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char value;
int error = copy_from_user(&value, buf, cnt);
if(error < 0) {
pr_err(" it get data error\n");
return -1;
}
if(value)
{
gpio_direction_output(led, 1);
}
else
{
gpio_direction_output(led, 0);
}
return 1;
}
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = lx_led_dev_open,
.write = lx_led_dev_write,
};
应用测试
int main(int argc, char **argv)
{
int fd;
int status;
fd = open(“/dev/gpioled”, O_RDWR);
if (fd == -1)
{
printf("can not open file /dev/gpioled\\\\\\\\n");
return -1;
}
do
{
printf("please input:/0/1/q\\\\\\\\n");
scanf("%d",&status);
printf("%d\\\\\\\\n",status);
if (status == 0)
{
write(fd, &status, 1);
}
else if(status== 1)
{
write(fd, &status, 1);
}
else if(status==113)
{
break;
}
else
{
printf("please error\\\\\\\\n");
}
} while(1);
close(fd);
return 0;
}
实验效果
终端控制
首先,加载ko文件
![QQ_1724587199019.png](//file1.elecfans.com/web2/M00/05/0E/wKgaombLHMmAVhT5AAA0KgZml-o206.png)
然后运行应用,分别输入1或0,控制led的状态
![QQ_1724587220227.png](//file1.elecfans.com/web2/M00/04/21/wKgZombLHOCANsFNAAA_Fbt0zac110.png)
板卡显示