0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Openwrt开发指南 第19章 驱动开发之LED驱动程序

嵌入式大杂烩 来源:嵌入式大杂烩 作者:嵌入式大杂烩 2023-07-03 09:01 次阅读

开发环境:

主机:Ubuntu12.04

开发板:RT5350

Openwrt:Openwrt15.05

1 硬件原理

1688309450899wor3360rqz

图1

由于发光二级管单向导电特性,即只有在正向电压(二极管的正极接正,负极接负)下才能导通发光。如图所示,如果 GPIO 输出高电平,LED 就会被点亮,如果 GPIO 输出低电平,LED 就会熄灭。对于我们的驱动开发,无论是单片机、还是 ARM、或者是我们的 MIPS,核心思想,都是读写某个地址,即操作某个寄存器

2 寄存器介绍

RT5350 一共有 28 个 GPIO 管脚,这 28 个 GPIO,除了 GPIO0,其他全部是与其他功能引脚复用的。

表1

1688309451600teuqhsf0kc

GPIO1、GPIO2 与 I2C 复用。

表2

1688309451852hx8f10ostq

GPIO3~6 与 SPI 复用。

表3

1688309452118oauupjmw22

GPIO7~14,与 UARTF 即串口 2 复用。

这些复用关系,都可以通过查阅 RT5350 的芯片手册得到。

而这些复用功能, 我们可以通过 GPIOMODE 寄存器来进行选择, 通过查手册得知,GPIOMODE寄存器的地址为 0x10000060。

表4

1688309452394a7cho2pt83

GPIOMODE 寄存器 bit0 位用于选择 GPIO1、GPIO2 对应的引脚是用于 IIC 总线,还是用于GPIO。

GPIOMODE 寄存器 bit1 位用于选择 GPIO3~6 对应的引脚用于 SPI 总线,还是用于 GPIO。

GPIOMODE 寄存器 bit2~4 位用于选择 UARTF 对应的引脚工作于哪个模块,具体定义如下。

表5

16883094530699yqb8mgmpb

从该表格可以看出,UARTF 对应的引脚,可以工作于 UARTF、PCM、I2S、GPIO 四种模式,将 GPIOMODE 寄存器 bit24 位设置为相应的值,就能让这些引脚工作于相应的模式,比如将GPIOMODE 寄存器 bit24 位的值设置为 7,则让相应的引脚工于 GPIO 模式。

当将相应的引脚设置为 GPIO 以后,我们接下来就需要操作该 GPIO 了,操作 GPIO 不外乎就是设置 GPIO 是输入还是输出、让其输出高电平还是低电平、读取其电平状态。不管是哪种操作,都有对应的寄存器。

表6

1688309453462jtao5bg1g4

GPIO21_00_DIR 寄存器,用于设置 GPIO0~21 的方向,当相应的位被设置为 1,则表示相应的 GPIO 管脚被设置为了输出,如果被设置为了 0,则相应的 GPIO 引脚就被设置为了输入。

表7

1688309453878j8wuc87w5l

当相应 GPIO 引脚被设置为输出时,设置 GPIO21_00_DATA 寄存器的相应位为 1,则让该GPIO 引脚输出了高电平,如果设置 GPIO21_00_DATA 寄存器的相应位为 0,则让该 GPIO 引脚输出了低电平。

当相应 GPIO 引脚被设置为输入时,则通过读取 GPIO21_00_DATA 寄存器时,就能读取相应的 GPIO 引脚的状态。

关于更多的 GPIO 操作的寄存器介绍,请自行查阅手册。

3 编写驱动程序

我们通过前面章节的学习,掌握了驱动程序的框架,接下来我们就来写一个驱动程序,实现操作 GPIO25、GPIO26 两个 GPIO 引脚。具体驱动实现如下。

#include < linux/mm.h >
#include < linux/miscdevice.h >
#include < linux/slab.h >
#include < linux/vmalloc.h >
#include < linux/mman.h >
#include < linux/random.h >
#include < linux/init.h >
#include < linux/raw.h >
#include < linux/tty.h >
#include < linux/capability.h >
#include < linux/ptrace.h >
#include < linux/device.h >
#include < linux/highmem.h >
#include < linux/crash_dump.h >
#include < linux/backing-dev.h >
#include < linux/bootmem.h >
#include < linux/splice.h >
#include < linux/pfn.h >
#include < linux/export.h >
#include < linux/io.h >
#include < linux/aio.h >
#include < linux/kernel.h >
#include < linux/module.h >
#include < asm/uaccess.h >
#define MYLEDS_LED1_ON 0
#define MYLEDS_LED1_OFF 1
#define MYLEDS_LED2_ON 2
#define MYLEDS_LED2_OFF 3

volatile unsigned long *GPIOMODE;
volatile unsigned long *GPIO27_22_DIR;
volatile unsigned long *GPIO27_22_DATA;
static struct class *myleds_class;

static int myleds_open(struct inode *inode, struct file *file)
{
/* 让 GPIO#25、GPIO#26 输出高电平,同时熄灭 LED1、LED2 */
*GPIO27_22_DATA &= ~((1< < 3)|(1< < 4));
return 0;
}
static long myleds_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case MYLEDS_LED1_ON:// 点亮 LED1
*GPIO27_22_DATA |= (1< < 3);
break;
case MYLEDS_LED1_OFF: // 熄灭 LED1
*GPIO27_22_DATA &= ~(1< < 3);
break;
case MYLEDS_LED2_ON:// 点亮 LED2
*GPIO27_22_DATA |= (1< < 4);
break;
case MYLEDS_LED2_OFF: // 熄灭 LED2
*GPIO27_22_DATA &= ~(1< < 4);
break;
default:
break;
}
return 0;
}
/* 1.分配、设置一个 file_operations 结构体 */
static struct file_operations myleds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译
模块时自动创建的__this_module 变量 */
.open = myleds_open,
.unlocked_ioctl = myleds_unlocked_ioctl,
};
int major;
static int __init myleds_init(void)
{
/* 2.注册 */
major = register_chrdev(0, "myleds", &myleds_fops);
/* 3.自动创建设备节点 */
/* 创建类 */
myleds_class = class_create(THIS_MODULE, "myleds");
/* 类下面创建设备节点 */
device_create(myleds_class, NULL, MKDEV(major, 0), NULL, "myleds"); //
/dev/myleds
/* 4.硬件相关的操作 */
/* 映射寄存器的地址 */
GPIOMODE = (volatile unsigned long *)ioremap(0x10000060, 4);
GPIO27_22_DIR = (volatile unsigned long *)ioremap(0x10000674, 4);
GPIO27_22_DATA = (volatile unsigned long *)ioremap(0x10000670, 4);
/* 设置相应管脚用于 GPIO */
/*
** LED1 ---- GPIO#25
** LED2 ---- GPIO#26
*/
*GPIOMODE |= (0x1< < 14);
/* 将 GPIO#25、GPIO#26 设置为输出 */
*GPIO27_22_DIR = (1< < 3)|(1< < 4);
return 0;
}
static void __exit myleds_exit(void)
{
unregister_chrdev(major, "myleds");
device_destroy(myleds_class, MKDEV(major, 0));
class_destroy(myleds_class);
iounmap(GPIOMODE);
iounmap(GPIO27_22_DIR);
iounmap(GPIO27_22_DATA);
}
module_init(myleds_init);
module_exit(myleds_exit);
MODULE_LICENSE("GPL");

因为我们的开发板上跑的是 Linux 系统,因此操作某个寄存器的时候,需要将它的物理地址映射成虚拟地址,通过 ioremap()函数来进行映射,该函数的参数 1,就是对应的寄存器的物理地址,参数 2 是需要映射多大,可以理解为寄存器有多大,返回值就是该寄存器对应的虚拟地址了。

当寄存器地址映射为虚拟地址以后,然后基于前面写的驱动程序框架,就能很容易的编写出自己的 GPIO 驱动了,和操作单片机没有什么差别了。

4 编写 Makefile

驱动写好以后,自然是需要想办法来编译该驱动了,通过前面的章节的学习,我们需要给该驱动编写一个 Makefile 文件。

首先是新建一个文件夹,取名为 myleds,然后在 myleds 目录下再新建一个文件夹,取名为 src,然后将上面的驱动文件复制到 src 目录下,并且在 src 目录下新建一个 Makefile,

内容如下。

obj-m += myleds.o

然后回到 myleds 目录,再创建一个 Makefile 文件,内容如下。

# 
#Copyright (C) 2008-2019 OpenWrt.org
#
#This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=myleds
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define KernelPackage/myleds
SUBMENU:=Other modules
# DEPENDS:=@!LINUX_3_3
TITLE:=Motor driver
FILES:=$(PKG_BUILD_DIR)/myleds.ko
# AUTOLOAD:=$(call AutoLoad,30,myleds,1)
KCONFIG:=
endef
define KernelPackage/myleds/description
This is a myleds drivers
endef
MAKE_OPTS:= \\
ARCH="$(LINUX_KARCH)" \\
CROSS_COMPILE="$(TARGET_CROSS)" \\
SUBDIRS="$(PKG_BUILD_DIR)"
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C "$(LINUX_DIR)" \\
$(MAKE_OPTS) \\
modules
endef
$(eval $(call KernelPackage,myleds))

如果不清楚为什么这样编写 Makefile 文件,请查看前面章节的相关介绍。

5 编译驱动程序

接下来,我们就来配置编译驱动程序。首先将 myleds 文件夹传到 OpenWrt 源码的package/kernel 目录下。然后进入 OpenWrt 源码的顶层目录,执行 make menuconfig。

$ make menuconfig

在弹出的菜单界面里,首先进入 Kernel modules 选项。

Kernel modules  --- >
Other modules  --- >
< * > kmod-myleds

16883094542989qo0m5w9t8

图2

然后我们就能看到我们的驱动程序的选项了 kmod-myleds,将它配置成M,也可以便已进入内核。最后,退出保存。然后再执行编译命令。

make V=99

编译完成以后,就能在源码目录下的bin/ramips/packages/base目录下看到我们的内核模块驱动程序的软件包 kmod-myleds_3.18.109-1_ramips_24kec.ipk。

19.6 动态的加载和卸载内核驱动模块软件包

通过前面的努力,我们终于得到了我们自己的内核驱动模块软件包了。接下来就来使用它。首先将 kmod-myleds_3.18.109-1_ramips_24kec.ipk 软件包传到开发板上面。然后使用 opkg install 命令来安装软件包。

opkg install kmod-myleds_3.18.109-1_ramips_24kec.ipk

1688309454667y59me39sp3

使用 opkg list 命令来查看已经安装了哪些软件包。这里是否执行该命令都无所谓。接下来进入我们的/lib/modules/3.18.109目录,就能看到我们的驱动程序模块了。

cd lib/modules/3.18.109/

1688309454875gdnscpsfh4

接下来就通过 insmod 命令来装载驱动模块。

insmod myleds.ko

驱动安装成功以后,我们就能看到 GPIO 驱动对应的设备节点了。

168830945511587ihzpsapz

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • led
    led
    +关注

    关注

    237

    文章

    22455

    浏览量

    645903
  • 驱动程序
    +关注

    关注

    19

    文章

    770

    浏览量

    47248
  • 开发板
    +关注

    关注

    25

    文章

    4437

    浏览量

    94088
  • 开发环境
    +关注

    关注

    1

    文章

    203

    浏览量

    16461
  • OpenWrt
    +关注

    关注

    10

    文章

    117

    浏览量

    38881
收藏 人收藏

    评论

    相关推荐

    Openwrt开发指南 第25章 配置OpenWrt支持网络摄像头

    Openwrt开发指南 第25章 配置OpenWrt支持网络摄像头
    的头像 发表于 07-13 08:56 1598次阅读
    <b class='flag-5'>Openwrt</b><b class='flag-5'>开发指南</b> 第25章 配置<b class='flag-5'>OpenWrt</b>支持网络摄像头

    PCI驱动程序开发实例

    本文采用微软的驱动程序开发包Device Driver Kit(DDK)开发出来的PCI驱动程序通用性好,兼容性强。本文给出了基于DSP的PCI驱动
    发表于 12-30 11:10 6535次阅读
    PCI<b class='flag-5'>驱动程序</b><b class='flag-5'>开发</b>实例

    Openwrt开发指南 第15章 驱动开发之字符设备驱动程序之概念介绍

    从上到下,一个软件系统可以分为:应用程序、库、操作系统(内核)、驱动程序开发人员可以专注于自己熟悉的部分,对于相邻层,只需要了解它的接口,无需关注它的实现细节。
    的头像 发表于 06-30 09:00 2181次阅读
    <b class='flag-5'>Openwrt</b><b class='flag-5'>开发指南</b> 第15章 <b class='flag-5'>驱动</b><b class='flag-5'>开发</b>之字符设备<b class='flag-5'>驱动程序</b>之概念介绍

    Openwrt开发指南 第20章 驱动开发LED应用程序

    在前面的章节中, 我们成功的写出了我们自己的驱动程序, 并且向应用程序提供了 open、ioctl 两个接口,那么接下来我们就来编写应用程序,调用这些接口。
    的头像 发表于 07-03 09:02 2138次阅读
    <b class='flag-5'>Openwrt</b><b class='flag-5'>开发指南</b> 第20章 <b class='flag-5'>驱动</b><b class='flag-5'>开发</b>之<b class='flag-5'>LED</b>应用<b class='flag-5'>程序</b>

    Openwrt开发指南 第24章 配置开发板支持U盘

    Openwrt开发指南 第24章 配置开发板支持U盘
    的头像 发表于 07-13 08:55 1384次阅读
    <b class='flag-5'>Openwrt</b><b class='flag-5'>开发指南</b> 第24章 配置<b class='flag-5'>开发</b>板支持U盘

    CE驱动程序开发指南资料分享

    CE驱动程序开发指南资料分享
    发表于 08-03 12:03

    测试系统开发指南:理解驱动程序和直接输入输出

    测试系统开发指南:理解驱动程序和直接输入/输出
    发表于 08-21 14:53

    WINCE驱动程序开发指南

    CE驱动程序所有驱动皆为动态链接库驱动实现中可以调用所有标准的APICE驱动程序模型本机驱动程序流接口
    发表于 07-07 15:06 41次下载

    用WinDriver开发PCI设备驱动程序

    用WinDriver开发PCI设备驱动程序:给出了驱动开发工具WinDriver的主要特点,内部机制及开发步骤,并且结合PCI设备的具体特点
    发表于 06-28 19:26 51次下载

    SN3340 LED驱动IC应用开发指南

    SN3340 LED驱动IC应用开发指南:The SN3340 is a continuous mode inductive step-downconverter, designed
    发表于 01-07 17:20 31次下载

    基于VxWorks的pci设备驱动程序开发

    对嵌入式实时系统Vxw池中的设备驱动程序开发进行了简要的介绍,讨论了V s操作系统下的I/O系统和设备驱动程序、应用程序之间的关系。另外,结合作者
    发表于 08-04 17:41 34次下载
    基于VxWorks的pci设备<b class='flag-5'>驱动程序</b><b class='flag-5'>开发</b>

    Windows WDM设备驱动程序开发指南

    书主要介绍Windows 98和Windows 2000新的驱动程序模型(WDM)。描述了WDM设备驱动程序的结构、功能和开发方法.
    发表于 12-12 15:31 0次下载
    Windows WDM设备<b class='flag-5'>驱动程序</b><b class='flag-5'>开发指南</b>

    A64开发板LCD开发指南

    A64开发板LCD开发指南驱动开发指南
    发表于 06-21 17:02 0次下载

    驱动程序开发步骤

    驱动程序开发步骤 编写一个驱动程序的大致流程如下: 1)查看原理图,数据手册,了解设备的操作方法; 2)在内核中找到相近的驱动程序,作为模板来开发
    发表于 12-29 14:51 6226次阅读

    UM2298_STM32Cube BSP驱动程序开发指南

    UM2298_STM32Cube BSP驱动程序开发指南
    发表于 11-22 19:19 1次下载
    UM2298_STM32Cube BSP<b class='flag-5'>驱动程序</b><b class='flag-5'>开发指南</b>