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

简介: 最简单的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");


目录
相关文章
|
人工智能 物联网 Linux
手把手之如何在嵌入式Linux上运行QT应用程序(以百问网imx6ull开发板为例)
手把手之如何在嵌入式Linux上运行QT应用程序(以百问网imx6ull开发板为例)
1246 0
|
存储 网络协议 API
NBIOT-BC28模块程序操作接口编写(基于STM32F103ZET6)
NBIOT-BC28模块程序操作接口编写(基于STM32F103ZET6)
383 0
|
存储 算法 芯片
IMX6ULL的I2C驱动详细分析
IMX6ULL的I2C驱动详细分析
287 0
IMX6ULL的I2C驱动详细分析
|
存储 编解码 Ubuntu
uboot移植到IMX6ULL平台详细过程(上)
uboot移植到IMX6ULL平台详细过程(上)
451 0
|
编解码 Shell Linux
uboot移植到IMX6ULL平台详细过程(下)
uboot移植到IMX6ULL平台详细过程(下)
451 0
|
芯片
具体单板的按键驱动程序(查询方式)-以IMX6ull单板为例子
具体单板的按键驱动程序(查询方式)-以IMX6ull单板为例子
102 0
|
Linux 芯片
LED驱动程序--可拓展的LED驱动程序
LED驱动程序--可拓展的LED驱动程序
122 0
|
Linux 开发工具 git
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树
342 1
嵌入式linux/鸿蒙开发板(IMX6ULL)开发(三十二)LED模板驱动程序的改造:设备树
|
定位技术 芯片
ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)(上)
ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)
286 1
ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)(上)
|
芯片 数据格式
ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)(下)
ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)
320 1
ARM架构与编程(基于I.MX6ULL): 串口UART编程(七)(下)
下一篇
DataWorks