最简单的LED驱动程序编写流程--基于IMX6ULL

驱动程序

一.查看芯片手册

根据芯片手册找到3个条件
1.根据芯片手册找到对应端口,并对相应端口组使能,而IMX6ULL使能是默认的
2.找到对应引脚的模式,设置为GPIO模式或者其他串口模式
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14 设置引脚模式的地址
3.并在GPIO模式下,设置引脚是输入模式或者是输出模式
GPIO5_GDIR地址:0x020AC0 设置输入输出模式的地址
4.对引脚的数据寄存器进行数据的写入
GPIO5_DR地址:0x020AC000设置数据的地址

二.用source insight打开liunx内核源码

参照内核驱动程序编写LED的驱动程序

三.编写LED驱动

1.设置LED入口函数和出口函数

a.入口函数
//入口函数
static int __init led_init(void)
{
	printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
	major = register_chrdev(0, "100ask_led", &led_fops);
	//ioremap 映射寄存器地址,实际地址到虚拟地址
	//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14
	
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3=ioremap(0x02290000 + 0x14, 4);
	//映射大小为4但是是一个页为4K

	//GPIO5_GDIR地址:0x020AC0
	GPIO5_GDIR = ioremap(0x020AC0,4);

	//GPIO5_DR地址:0x020AC000
	GPIO5_DR = ioremap(0x020AC000,4);

	
	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major,0),NULL,"myled");//前面两行系统就会自己创建名为myled的设备节点,就不需要手动创建
	return 0;
}
module_init(led_init);

入口函数做的事情如下:
1.调用register_chrdev函数向内核注册驱动程序,而驱动程序写在file_operations结构体的led_fops中。并分配主设备号。
2.将实际的物理地址通过ioremap函数映射为机器的虚拟地址,通过对虚拟地址指针的操作就可以操作物理地址。
3.创建两个类class_create和device_create创建这两个类的目的是使机器自动创建设备节点。
4.调用module_init函数将led_init告诉内核为led驱动程序的入口函数

b.出口函数
static void __exit led_exit(void)
{
	//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	//GPIO5_GDIR地址:0x020AC0
	iounmap(GPIO5_GDIR);
	//GPIO5_DR地址:0x020AC000
	iounmap(GPIO5_DR);
	
	class_destroy(led_class);
	device_destroy(led_class, MKDEV(major,0));
	unregister_chrdev(major, "100ask_led");
}
module_exit(led_exit);

出口函数做的事情如下:
1.对入口函数内部做的ioremap销毁
2.对绕口令函数内部做的class_create和device_create这两个类进行销毁
3.将注册函数也进行销毁
4.module_exit告诉内核led_exit为出口函数

2.编写驱动程序

static const struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};

这里声明了驱动程序里面有那些函数:led_write和;led_open函数

a.led_open函数
static int led_open(struct inode *inode, struct file *filp)
{
	//enable gpio已经默认设置
	//configure pin as gpio5_3
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;
	//configure gpio as output
	*GPIO5_GDIR |= (1<<3);
	return 0;
}

led_open函数作用是:打开引脚驱动,打开的时候证明将要用这个驱动。所有设置这个驱动的配置。也就是上面芯片手册所查到的物理地址设置成相应的值。

b.led_write函数
static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	int ret;
	// copy_from_user : get data from app
	ret = copy_from_user(&val, buf, 1);
	// to set gpio register : out 1/0
	if(val)
	{
		*GPIO5_DR &= ~(1<<3);
	}
	else
	{
		*GPIO5_DR |= (1<<3);
	}
	return 1;
}

通过用户层传来的命令向驱动引脚写数据,这里要注意用户层和核心层之间的数据通信是通过copy_from_user来通信的。然后对引脚的数据寄存器进行数据的填写。

完整代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

static int major;
static struct class *led_class;

//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14

static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;

//GPIO5_GDIR地址:0x020AC0
static volatile unsigned int *GPIO5_GDIR;

//GPIO5_DR地址:0x020AC000
static volatile unsigned int *GPIO5_DR;

static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	int ret;
	// copy_from_user : get data from app
	ret = copy_from_user(&val, buf, 1);
	// to set gpio register : out 1/0
	if(val)
	{
		*GPIO5_DR &= ~(1<<3);
	}
	else
	{
		*GPIO5_DR |= (1<<3);
	}
	return 1;
}

static int led_open(struct inode *inode, struct file *filp)
{
	//enable gpio已经默认设置
	//configure pin as gpio5_3
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;
	//configure gpio as output
	*GPIO5_GDIR |= (1<<3);
	return 0;
}

static const struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};


//入口函数
static int __init led_init(void)
{
	printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__);
	major = register_chrdev(0, "100ask_led", &led_fops);
	//ioremap 映射寄存器地址,实际地址到虚拟地址
	//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14
	
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3=ioremap(0x02290000 + 0x14, 4);
	//映射大小为4但是是一个页为4K

	//GPIO5_GDIR地址:0x020AC0
	GPIO5_GDIR = ioremap(0x020AC0,4);

	//GPIO5_DR地址:0x020AC000
	GPIO5_DR = ioremap(0x020AC000,4);

	
	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major,0),NULL,"myled");//前面两行系统就会自己创建名为myled的设备节点,就不需要手动创建
	return 0;
}
//出口函数
static void __exit led_exit(void)
{
	//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	//GPIO5_GDIR地址:0x020AC0
	iounmap(GPIO5_GDIR);
	//GPIO5_DR地址:0x020AC000
	iounmap(GPIO5_DR);
	
	class_destroy(led_class);
	device_destroy(led_class, MKDEV(major,0));
	unregister_chrdev(major, "100ask_led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单IMX6uLL LED 闪烁的驱动程序的例子: ```c #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> /* GPIO register offsets */ #define GPIO_DR_OFFSET 0x0 #define GPIO_GDIR_OFFSET 0x4 #define GPIO_PSR_OFFSET 0x8 #define GPIO_ICR1_OFFSET 0x10 #define GPIO_ICR2_OFFSET 0x14 /* LED GPIO pin number */ #define LED_GPIO_PIN 10 /* Module information */ MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("LED Blink Driver"); MODULE_LICENSE("GPL"); /* GPIO base address */ static void __iomem *gpio_base; /* Module initialization function */ static int led_blink_probe(struct platform_device *pdev) { int ret; u32 gpio_val; /* Get the GPIO base address from the device tree */ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gpio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(gpio_base)) { dev_err(&pdev->dev, "Failed to map GPIO registers\n"); return PTR_ERR(gpio_base); } /* Configure the GPIO direction */ gpio_val = readl(gpio_base + GPIO_GDIR_OFFSET); gpio_val |= (1 << LED_GPIO_PIN); writel(gpio_val, gpio_base + GPIO_GDIR_OFFSET); /* Blink the LED */ while (!kthread_should_stop()) { gpio_val = readl(gpio_base + GPIO_DR_OFFSET); gpio_val ^= (1 << LED_GPIO_PIN); writel(gpio_val, gpio_base + GPIO_DR_OFFSET); msleep(500); } return 0; } /* Module exit function */ static int led_blink_remove(struct platform_device *pdev) { return 0; } /* Device tree match table */ static const struct of_device_id led_blink_of_match[] = { { .compatible = "led-blink-gpio", }, {}, }; MODULE_DEVICE_TABLE(of, led_blink_of_match); /* Platform driver structure */ static struct platform_driver led_blink_driver = { .probe = led_blink_probe, .remove = led_blink_remove, .driver = { .name = "led-blink-gpio", .of_match_table = led_blink_of_match, }, }; /* Module initialization function */ static int __init led_blink_init(void) { /* Register the platform driver */ return platform_driver_register(&led_blink_driver); } /* Module exit function */ static void __exit led_blink_exit(void) { /* Unregister the platform driver */ platform_driver_unregister(&led_blink_driver); } /* Module entry and exit points */ module_init(led_blink_init); module_exit(led_blink_exit); ``` 这个驱动程序使用了平台设备和设备树来获取 LED GPIO 的地址和配置信息,并使用 GPIO 控制 LED 的闪烁。在这个例子中,我们使用了 GPIO1_IO10 作为 LED 的控制引脚,然后使用 `msleep()` 函数来控制闪烁的频率。注意,驱动程序中的代码仅供参考,需要针对具体的硬件进行修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值