RK3288_Android7.1写一个GPIO驱动控制LED灯亮灭

GPIO是驱动当中最常用的资源,操作GPIO是最基本的操作,本节通过操作GPIO实现对LED灯的亮灭操作,驱动是在rk3288 android7.1 kernel4.4当中实现的。驱动实现解析dts的gpio,创建设备节点,创建工作队列实现闪灯效果(一秒切换一种颜色【蓝色和红色交替】)。

一、首先查看原理图,确定控制led的是哪个gpio
在这里插入图片描述

在这里插入图片描述

从原理图上可以看到,连接到主控的gpio是GPIO5_B2,所有我们只需要拉高或者拉低就可以控制LED灯了。拉高GPIO5_B2可以使三极管Q10导通,这样LED D31就亮了;拉低GPIO5_B2可以使三极管Q9断开,使三极管Q8导通,这样LED D32就亮了。

总结一下就是GPIO5_B2拉高亮红灯,拉低亮绿灯[开发板上焊接的是蓝灯]。驱动通过拉高拉低gpio控制led。

二、代码实现部分
1、dts部分,添加gpio的资源描述
文件:arch/arm/boot/dts/rk3288-evb-android-rk808-mipi.dts

 led_ctrl {
	 status = "okay";
     compatible = "rk3288,led_ctrl";
     gpio_led = <&gpio5 10 GPIO_ACTIVE_HIGH>; //GPIO5_B2

    };

2、驱动部分。
文件:drivers/Makefile,在该文件添加编译对应驱动文件夹语句:

obj-y   += led_ctrl/

文件:drivers/led_ctrl/Makefile(添加Makefile文件和编译语句)

obj-y += led_ctrl.o

文件:drivers/led_ctrl/led_ctrl.c

#include <dt-bindings/gpio/gpio.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/err.h> 


static int gpio_led; //定义一个GPIO变量,用gpio_led表示


static ssize_t gpio_led_show(struct device *dev,
			struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", gpio_get_value(gpio_led)); //获取GPIO的电平,1为高电平,0为低电平
}


static ssize_t gpio_led_store(struct device *dev, struct device_attribute *attr,
						const char *buf, size_t size)
{
	int val;
	int ret;

	ret = kstrtoint(buf, 10, &val); //输入一个字符然后转换成10进制整形数

        if (ret) {
		printk("%s: kstrtoint error return %d\n", __func__, ret);
		return -1;
        }

	if (val== 1) { //如果echo 1 到节点则调用

		printk("-czd-: _%s_ :gpio pull up\n", __func__);
		gpio_direction_output(gpio_led, val); //设置GPIO电平为高电平

	} else if (val == 0) { //如果echo 0 到节点则调用

		printk("-czd-: _%s_ :gpio pull down\n", __func__);
		gpio_direction_output(gpio_led, val); //设置GPIO电平为低电平

	} else {

		printk("I only support 0 or 1 to ctrl led\n");

	}
	return size;
}

static DEVICE_ATTR(gpio_led, 0664, gpio_led_show, gpio_led_store); //调用DEVICE_ATTR创建设备节点

static int led_ctrl_probe(struct platform_device *pdev) //compatible的属性和dts的compatible一致,就会调用probe函数
{

	struct device_node *led_ctrl_node = pdev->dev.of_node;
	enum of_gpio_flags flags;
	int gpio_value;

	printk("[%d] enter %s start..\n", __LINE__, __func__); //printk打印,kernel一般调用这个函数打印log

	gpio_led = of_get_named_gpio_flags(led_ctrl_node, "gpio_led", 0, &flags); //解析dts的gpio
	printk("gpio_led is %d --\n", gpio_led);

	gpio_value = (flags == GPIO_ACTIVE_HIGH)? 1:0;

	if (!gpio_is_valid(gpio_led)) { //判断GPIO是否合法能用
		printk("gpio_led: %d is invalid\n", gpio_led);
		return -ENODEV;
	}

	if (gpio_request(gpio_led, "gpio_led")) { //申请GPIO口资源
		printk("gpio_led: %d request failed!\n", gpio_led);
		gpio_free(gpio_led); //如果申请失败,要释放GPIO的占用
		return -ENODEV;
	}

	gpio_direction_output(gpio_led, !gpio_value); //设置GPIO初始电平为低电平
	printk("gpio_led pin level is %d\n", !gpio_value); //这里gpio_value取反!gpio_value,是低电平

	device_create_file(&pdev->dev, &dev_attr_gpio_led);

	printk("[%d]: ___%s___ sucess!\n", __LINE__, __func__);

	return 0;
}

static int led_ctrl_remove(struct platform_device *pdv)
{
	printk("___%s___\n", __func__);
	return 0;
}

static struct of_device_id led_ctrl_match_table[] = {
	{ .compatible = "led_ctrl",},
	{},
	};

static struct platform_driver led_ctrl_driver = {

	.driver = {
		.name = "led_ctrl",
		.owner = THIS_MODULE,
		.of_match_table = led_ctrl_match_table,
	},
	.probe = led_ctrl_probe,
	.remove = led_ctrl_remove,
	};

static int led_ctrl_init(void)
{
	printk("#led_ctrl#: ___%s___\n", __func__);
	platform_driver_register(&led_ctrl_driver);
	return 0;
}

static void led_ctrl_exit(void)
{
	printk("#led_ctrl#: ___%s___,\n", __func__);
	cancel_delayed_work_sync(&gpioled_event);  //取消延时工作队列
	platform_driver_unregister(&led_ctrl_driver);

}

module_init(led_ctrl_init); //模块加载函数
module_exit(led_ctrl_exit); //模块卸载函数

MODULE_AUTHOR("czd,214241976@qq.com");
MODULE_DESCRIPTION("Driver for control sysled");
MODULE_LICENSE("GPL");  //许可声明, 描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到kernel tainted的警告


以上就是创建设备节点,然后通过节点操作【read/write】GPIO的上下电实现led的亮灭的简单实现代码,串口或者adb工具操作对应节点文件gpio_led控制gpio电平:

rk3288:/ # su
rk3288:/ # echo 0 > sys/devices/platform/led_ctrl/gpio_led //往节点写入0,亮蓝灯
rk3288:/ # cat sys/devices/platform/led_ctrl/gpio_led // cat查看一下gpio电平                          
0
rk3288:/ # echo 1 > sys/devices/platform/led_ctrl/gpio_led //往节点写入1,亮红灯                    
rk3288:/ # cat sys/devices/platform/led_ctrl/gpio_led // cat查看一下gpio电平                         
1
rk3288:/ #

注意:在安卓开发过程需要给节点的权限:
可以创建个脚本(*.sh文件)运行,然后在脚本里添加节点的权限:
例如apk/system/bin/lowmem_manage.sh文件中添加以下语句:
chmod 666 sys/devices/platform/led_ctrl/gpio_led

或者在init*.rc文件当中添加赋权限的语句。

为了实现闪灯效果,我这里使用了工作队列来实现,具体逻辑:创建一个工作队列,然后在工作队列去操作GPIO的上下电,并且在工作队列函数中加入定时器,时间一到,又重新开始执行工作队列里面的操作,如此循环,实现led闪灯效果。

#include <dt-bindings/gpio/gpio.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/err.h> 
#include <linux/workqueue.h> //添加需要用到的头文件


static int gpio_led; //定义一个GPIO变量,用gpio_led表示
static int rb_switch_value = 0;//定义一个GPIO电平变量值

static struct delayed_work gpioled_event; //定义一个delay_work

static void gpioled_event_workq(struct work_struct *work)  //定义你要延时执行的工作队列函数
{

	printk("#%s#: led color blue or red,rb_switch_value=%d\n", __func__, rb_switch_value);
	gpio_direction_output(gpio_led, rb_switch_value); //设置GPIO电平
	rb_switch_value = !rb_switch_value; //设置rb_switch_value取反,以此实现高低电平切换设置
	schedule_delayed_work(&gpioled_event, msecs_to_jiffies(1000)); //添加这句之后每隔1秒执行一次
}

static ssize_t gpio_led_show(struct device *dev,
			struct device_attribute *attr, char *buf)
{
	return sprintf(buf, "%d\n", gpio_get_value(gpio_led)); //获取GPIO的电平,1为高电平,0为低电平
}


static ssize_t gpio_led_store(struct device *dev, struct device_attribute *attr,
						const char *buf, size_t size)
{
	int val;
	int ret;

	ret = kstrtoint(buf, 10, &val); //输入一个字符然后转换成10进制整形数

        if (ret) {
		printk("%s: kstrtoint error return %d\n", __func__, ret);
		return -1;
        }

	if (val== 1) { //如果echo 1 到节点则调用

		printk("-czd-: _%s_ :gpio pull up\n", __func__);
		gpio_direction_output(gpio_led, val); //设置GPIO电平为高电平

	} else if (val == 0) { //如果echo 0 到节点则调用

		printk("-czd-: _%s_ :gpio pull down\n", __func__);
		gpio_direction_output(gpio_led, val); //设置GPIO电平为低电平

	} else {

		printk("I only support 0 or 1 to ctrl led\n");

	}
	return size;
}

static DEVICE_ATTR(gpio_led, 0664, gpio_led_show, gpio_led_store); //调用DEVICE_ATTR创建设备节点

static int led_ctrl_probe(struct platform_device *pdev) //compatible的属性和dts的compatible一致,就会调用probe函数
{

	struct device_node *led_ctrl_node = pdev->dev.of_node;
	enum of_gpio_flags flags;
	int gpio_value;

	printk("[%d] enter %s start..\n", __LINE__, __func__); //printk打印,kernel一般调用这个函数打印log

	gpio_led = of_get_named_gpio_flags(led_ctrl_node, "gpio_led", 0, &flags); //解析dts的gpio
	printk("gpio_led is %d --\n", gpio_led);

	gpio_value = (flags == GPIO_ACTIVE_HIGH)? 1:0;

	if (!gpio_is_valid(gpio_led)) { //判断GPIO是否合法能用
		printk("gpio_led: %d is invalid\n", gpio_led);
		return -ENODEV;
	}

	if (gpio_request(gpio_led, "gpio_led")) { //申请GPIO口资源
		printk("gpio_led: %d request failed!\n", gpio_led);
		gpio_free(gpio_led); //如果申请失败,要释放GPIO的占用
		return -ENODEV;
	}

	gpio_direction_output(gpio_led, !gpio_value); //设置GPIO初始电平为低电平
	printk("gpio_led pin level is %d\n", !gpio_value); //这里gpio_value取反!gpio_value,是低电平

	device_create_file(&pdev->dev, &dev_attr_gpio_led);

	INIT_DELAYED_WORK(&gpioled_event, gpioled_event_workq); //初始化工作队列
	schedule_delayed_work(&gpioled_event, msecs_to_jiffies(2000)); //添加到延时工作队列,这里延时2秒

	printk("[%d]: ___%s___ sucess!\n", __LINE__, __func__);

	return 0;
}

static int led_ctrl_remove(struct platform_device *pdv)
{
	printk("___%s___\n", __func__);
	return 0;
}

static struct of_device_id led_ctrl_match_table[] = {
	{ .compatible = "led_ctrl",},
	{},
	};

static struct platform_driver led_ctrl_driver = {

	.driver = {
		.name = "led_ctrl",
		.owner = THIS_MODULE,
		.of_match_table = led_ctrl_match_table,
	},
	.probe = led_ctrl_probe,
	.remove = led_ctrl_remove,
	};

static int led_ctrl_init(void)
{
	printk("#led_ctrl#: ___%s___\n", __func__);
	platform_driver_register(&led_ctrl_driver);
	return 0;
}

static void led_ctrl_exit(void)
{
	printk("#led_ctrl#: ___%s___,\n", __func__);
	cancel_delayed_work_sync(&gpioled_event);  //取消延时工作队列
	platform_driver_unregister(&led_ctrl_driver);

}

module_init(led_ctrl_init); //模块加载函数
module_exit(led_ctrl_exit); //模块卸载函数

MODULE_AUTHOR("czd,214241976@qq.com");
MODULE_DESCRIPTION("Driver for control sysled");
MODULE_LICENSE("GPL");  //许可声明, 描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到kernel tainted的警告
  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个使用STM32F401微控制器的按键带锁存地控制LED灯亮的示例代码: ```c #include "stm32f4xx_ll_bus.h" #include "stm32f4xx_ll_gpio.h" void GPIO_Init(void); void EXTI_Init(void); void Delay(uint32_t delay); int main(void) { GPIO_Init(); EXTI_Init(); while (1) { // 主循环代码 } } void GPIO_Init(void) { // 使能GPIOA和GPIOC时钟 LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA); LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC); // 配置GPIOA的引脚为输入模式(按键) LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT); LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_UP); // 配置GPIOC的引脚为输出模式(LED灯) LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_13, LL_GPIO_MODE_OUTPUT); } void EXTI_Init(void) { // 使能SYSCFG时钟 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); // 配置外部中断线0和对应的GPIO引脚 LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0); // 配置外部中断线0的触发方式为上升沿触发 LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_0); // 使能外部中断线0 LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_0); // 使能中断向量 NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0)); NVIC_EnableIRQ(EXTI0_IRQn); } void Delay(uint32_t delay) { for (uint32_t i = 0; i < delay; i++); } void EXTI0_IRQHandler(void) { // 清除中断标志位 LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); // 检测按键状态 if (LL_GPIO_IsInputPinSet(GPIOA, LL_GPIO_PIN_0)) { // 按键被按下,切换LED灯状态 LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_13); // 延时一段时间,避免按键抖动 Delay(100000); } } ``` 在上述示例代码中,我们使用GPIOA的Pin 0引脚作为输入引脚,用于连接按键。使用GPIOC的Pin 13引脚作为输出引脚,用于连接LED灯。我们使用外部中断来检测按键的状态变化,并在按键被按下时切换LED灯的状态。 在 `EXTI_Init()` 函数中,我们首先使能了SYSCFG时钟,并配置了外部中断线0和对应的GPIO引脚。然后,我们将外部中断线0的触发方式配置为上升沿触发,并使能了外部中断线0。最后,我们设置了中断优先级,并使能了对应的中断。 在 `EXTI0_IRQHandler()` 中断服务函数中,我们首先清除了中断标志位。然后,我们检测按键的状态,如果按键被按下,我们切换LED灯的状态,并延时一段时间(这里使用简单的循环延时)以避免按键抖动。 请注意,上述示例中的参数值仅供参考,实际应用中需要根据具体需求进行适当的修改和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

零意@

您的打赏将是我继续创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值