使用GPIO子系统编写LED灯驱动
驱动程序
#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>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include "my3led.h"int major;
char kbuf[128] = {0};
struct class *cls;
struct device *dev;struct gpio_desc *gpiono1; // led1设备号
struct gpio_desc *gpiono2; // led2设备号
struct gpio_desc *gpiono3; // led3设备号
struct device_node *dnode; // 保存解析到的设备树节点地址// 创建功能码
#define LED_ON _IOW('l', 1, int)
#define LED_OFF _IOW('l', 0, int)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 *ubuf, size_t size, loff_t *lof)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);unsigned long ret;// 向用户空间读取拷贝if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小size = sizeof(kbuf);ret = copy_to_user(ubuf, kbuf, size);if (ret) // 拷贝失败{printk("copy_to_user filed\n");return ret;}return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{unsigned long ret;// 从用户空间读取数据if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小size = sizeof(kbuf);ret = copy_from_user(kbuf, ubuf, size);if (ret) // 拷贝失败{printk("copy_to_user filed\n");return ret;}return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{switch (cmd){case LED_ON:switch (arg){case 1:gpiod_set_value(gpiono1, 1);break;case 2:gpiod_set_value(gpiono2, 1);break;case 3:gpiod_set_value(gpiono3, 1);break;}break;case LED_OFF:switch (arg){case 1:gpiod_set_value(gpiono1, 0);break;case 2:gpiod_set_value(gpiono2, 0);break;case 3:gpiod_set_value(gpiono3, 0);break;}break;}return 0;
}int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}// 定义操作方法结构体变量并赋值
struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.unlocked_ioctl = mycdev_ioctl,.release = mycdev_close,
};static int __init mycdev_init(void)
{// 字符设备驱动注册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);}printk("向上提交目录成功\n");// 向上提交设备节点信息int i; // 向上提交三次设备节点信息for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点失败\n");return -PTR_ERR(dev);}}printk("向上提交设备节点成功\n");// 解析LED灯设备树节点dnode = of_find_node_by_path("/leds");if (dnode == NULL){printk("解析设备树节点失败\n");return -ENXIO;}printk("解析设备树节点成功\n");// 解析LED1的gpio编号gpiono1 = gpiod_get_from_of_node(dnode, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);if (gpiono1 == NULL){printk("解析gpio编号失败\n");return -ENXIO;}printk("解析gpio编号成功\n");// 解析LED2的gpio编号gpiono2 = gpiod_get_from_of_node(dnode, "led2-gpio", 0, GPIOD_OUT_LOW, NULL);if (gpiono2 == NULL){printk("解析gpio编号失败\n");return -ENXIO;}printk("解析gpio编号成功\n");// 解析LED3的gpio编号gpiono3 = gpiod_get_from_of_node(dnode, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);if (gpiono3 == NULL){printk("解析gpio编号失败\n");return -ENXIO;}printk("解析gpio编号成功\n");return 0;
}
static void __exit mycdev_exit(void)
{// 销毁设备节点信息device_destroy(cls, MKDEV(major, 0));// 销毁设备节点信息int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 释放gpio编号gpiod_put(gpiono1);gpiod_put(gpiono2);gpiod_put(gpiono3);// 销毁目录class_destroy(cls);// 注销字符设备驱动unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
应用程序:
#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 "my3led.h"//创建功能码
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)int main(int argc, char const *argv[])
{int a,b;int fd=open("/dev/myled0",O_RDWR);if(fd<0){printf("打开设备文件失败\n");exit(-1);}while(1){//从终端读取printf("请输入要实现的功能\n");printf("0(关灯) 1(开灯)\n");printf("请输入>");scanf("%d",&a);printf("请输入要控制的灯\n");printf("1(LED1) 2(LED2) 3(LED3)\n");printf("请输入>");scanf("%d",&b);switch(a){case 1:ioctl(fd,LED_ON,b);break;case 0:ioctl(fd,LED_OFF,b);break;}}close(fd);return 0;
}
头文件
#ifndef __HEAD_H__
#define __HEAD_H__
typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR 0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR 0X50000A28//构建功能码
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)
#endif
运行效果图:
注册三个按键的中断---代码
#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>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
/*myirq{compatible = "hqyj,myirq";interrupt-parent = <&gpiof>;interrupts=<9 0>,<7 0>,<8 0>;
};
*/struct device_node *dnode; // 保存解析到的设备树节点地址
unsigned int irqno1;
unsigned int irqno2;
unsigned int irqno3;
// 中断处理函数按键1
irqreturn_t myirq_handler1(int irq, void *dev)
{printk("key1_intc\n");return IRQ_HANDLED;
}
//按键2
irqreturn_t myirq_handler2(int irq, void *dev)
{printk("key2_intc\n");return IRQ_HANDLED;
}
//按键3
irqreturn_t myirq_handler3(int irq, void *dev)
{printk("key3_intc\n");return IRQ_HANDLED;
}static int __init mycdev_init(void)
{// 解析按键的设备树节点dnode = of_find_compatible_node(NULL, NULL, "hqyj,myirq");if (dnode == NULL){printk("解析设备树节点失败\n");return -ENXIO;}printk("解析设备树节点成功\n");// 解析按键1的软中断号irqno1 = irq_of_parse_and_map(dnode, 0);if (!irqno1){printk("解析软中断号失败\n");return -ENXIO;}printk("解析软中断号成功%d\n", irqno1);// 解析按键2的软中断号irqno2 = irq_of_parse_and_map(dnode, 1);if (!irqno2){printk("解析软中断号失败\n");return -ENXIO;}printk("解析软中断号成功%d\n", irqno2);// 解析按键3的软中断号irqno3 = irq_of_parse_and_map(dnode, 2);if (!irqno3){printk("解析软中断号失败\n");return -ENXIO;}printk("解析软中断号成功%d\n", irqno3);// 注册按键1中断int ret1 = request_irq(irqno1, myirq_handler1, IRQF_TRIGGER_FALLING, "key1", (void *)1);if (ret1){printk("1中断注册失败\n");return ret1;}printk("中断注册成功\n");// 注册按键2中断int ret2 = request_irq(irqno2, myirq_handler2, IRQF_TRIGGER_FALLING, "key2", (void *)2);if (ret2){printk("2中断注册失败\n");return ret2;}printk("中断注册成功\n");// 注册按键3中断int ret3 = request_irq(irqno3, myirq_handler3, IRQF_TRIGGER_FALLING, "key3", (void *)3);if (ret3){printk("3中断注册失败\n");return ret3;}printk("中断注册成功\n");return 0;
}
static void __exit mycdev_exit(void)
{free_irq(irqno1, (void *)1);free_irq(irqno2, (void *)2);free_irq(irqno3, (void *)3);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
三个按键按下后效果图: