【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十五章 Pinctrl和GPIO子系统实验

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263


第五十五章 Pinctrl和GPIO子系统实验

本章导读

本章节将以实践课的方式讲解LED驱动例程。

55.1章节对实验需求进行分析

55.2章节修改设备树文件

55.3章节编写驱动文件

55.4章节编写测试APP

55.5章节运行测试,实现了控制LED亮灭

本章内容对应视频讲解链接(在线观看):

pinctl和gpio子系统 ( https://www.bilibili.com/video/BV1Vy4y1B7ta?p=30

pinctl和gpio子系统 ( https://www.bilibili.com/video/BV1Vy4y1B7ta?p=31

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\012-Pinctrl和GPIO子系统实验”路径下。

55.1 需求分析

通过第54章的学习,我们已经明白什么是pinctrl子系统和GPIO子系统,pinctrl子系统就是设置引脚的复用关系和电气属性的,GPIO子系统就是当pinctrl子系统将复用关系设置为GPIO以后,我们使用GPIO子系统来操作我们的GPIO。那么本章节我们来用pinctrl子系统和GPIO子系统来控制我们的引脚。第55章我们学习了在设备树下的平台总线模型,当匹配成功以后会在设备树文件中拿资源,然后我们再进行相关的操作。

本章节我们要编写pinctrl子系统和GPIO子系统,我们可以在以前代码的基础上进行编写,框架并没有变,我们也是让驱动和设备进行匹配,匹配成功之后将相关的操作,唯一变的地方是之前使用的寄存器操作我们的GPIO,现在我们换成了GPIO子系统提供的API函数来操作我们的GPIO,比原来的方法更先进了一些。

 我们以iTOP-IMX8MM开发板为例来控制LED,本章节内容会将以前学习的驱动理论融会贯通,学以致用。LED 设备注册的流程:

  • 硬件原理图分析,确定控制LED的GPIO信息,参考42.2硬件分析章节,蜂鸣器复用GPIO11_IO13
  • 根据GPIO信息在设备树文件中添加pinctrl信息
  • 在设备树文件中创建蜂鸣器的节点,并加入GPIO信息,修改完设备树编译烧写新的设备树
  • 编写LED设备驱动,加载驱动模块
  • 编写应用程序测试LED

55.2 修改设备树文件

我们修改设备树文件/home/topeet/linux/linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dtsi,修改test节点如下图所示:

	test:test{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "test";
		//reg = <4 0x30200000 4 0x30200004>;
		pinctrl-names = "default";
		pinctrl-0 = <&led_gpio>;
		gpios = <&gpio1 13 0>;

	};
&test{
	compatible = "test1234";
	status = "okay";
};

 

在&pinctrl里面追加pinctrl_beep内容,如下图所示: 

修改完后保存文件,参考开发板的使用手册编译源码,然后重新烧写编译好的镜像。

55.3 LED驱动程序编写

这里我们以iTOP-IMX8MM开发板为例。我们在Ubuntu的/home/topeet/imx8mm/12下新建driver文件,如下图所示。 

/*
 * @Author: topeet
 * @Description: LED驱动
 */

#include <linux/init.h>            //初始化头文件
#include <linux/module.h>          //最基本的文件,支持动态添加和卸载模块。
#include <linux/platform_device.h> //platform平台设备相关的头文件
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/miscdevice.h> //包含了miscdevice结构的定义及相关的操作函数。
#include <linux/fs.h>         //文件系统头文件,定义文件表结构(file,buffer_head,m_inode等)
#include <linux/uaccess.h>    //包含了copy_to_user、copy_from_user等内核访问用户进程内存地址的函数定义。
#include <linux/io.h>         //包含了ioremap、iowrite等内核访问IO内存等函数的定义。
#include <linux/gpio.h>
#include <linux/of_gpio.h>
int size;
int led_gpio = 0;
int ret = 0;
u32 out_values[2] = {0};
const char *str;
struct device_node *test_device_node;
struct property *test_node_property;

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
    printk("misc_read\n ");
    return 0;
}
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
    /*应用程序传入数据到内核空间,然后控制led的逻辑,在此添加*/

    // kbuf保存的是从应用层读取到的数据
    char kbuf[64] = {0};
    // copy_from_user 从应用层传递数据给内核层
    if (copy_from_user(kbuf, ubuf, size) != 0)
    {
        // copy_from_user 传递失败打印
        printk("copy_from_user error \n "); 
        return -1;
    }
    //打印传递进内核的数据
    printk("kbuf is %d\n ", kbuf[0]); 
    //如果传递进的数据是1,则led亮
    if (kbuf[0] == 1)
    {
        gpio_set_value(led_gpio, 1);
    }
    //如果传递进的数据是0,则led不亮
    else if (kbuf[0] == 0)
        gpio_set_value(led_gpio, 0);
    return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
    printk("hello misc_relaease bye bye \n ");
    return 0;
}
int misc_open(struct inode *inode, struct file *file)
{
    printk("hello misc_open\n ");
    return 0;
}
//文件操作集
struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write,
};
//杂项设备结构体
struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "hello_misc",
    .fops = &misc_fops,
};
/**
 * @description: platform 驱动的 probe 函数,当驱动与设备匹配以后此函数就会执行
 * @param {*}pdev : platform 设备
 * @return {*}0,成功;其他负值,失败
 */
int led_probe(struct platform_device *pdev)
{
    printk("led_probe\n");
    //获得设备节点
    test_device_node = of_find_node_by_path("/test");
    if (test_device_node == NULL)
    {
        printk("of_find_node_by_path is error \n");
        return -1;
    }
    /*******我们要使用我们的GPIO就要从设备树来获取**********/

    /*  of_get_named_gpio函数获取 GPIO 编号,
    因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,
    此函数会将设备树中类似<&gpio1 13 1>的属性信息转换为对应的 GPIO 编号 */
    led_gpio = of_get_named_gpio(test_device_node, "gpios", 0);
    if (led_gpio < 0)
    {
        printk("of_get_named_gpio is error \n");
        return -1;
    }
    printk("led_gpio is %d \n", led_gpio);
    //申请一个 GPIO 管脚
    ret = gpio_request(led_gpio, "led");
    if (ret < 0)
    {
        printk("gpio_request is error \n");
        return -1;
    }
    //设置某个GPIO为输出,并且设置默认输出值
    gpio_direction_output(led_gpio, 1);
    //注册杂项设备
    ret = misc_register(&misc_dev);
    if (ret < 0)
    {
        printk("misc registe is error \n");
    }
    printk("misc registe is succeed \n");
    return 0;
}

int led_remove(struct platform_device *pdev)
{
    gpio_free(led_gpio);
    printk("led_remove\n");
    return 0;
}
const struct platform_device_id led_idtable = {
    .name = "led_test",
};
const struct of_device_id of_match_table_test[] = {
    {.compatible = "test1234"},
    {},
};
struct platform_driver led_driver = {
    //第三步 在led_driver结构体中完成了led_probe和led_remove
    .probe = led_probe,
    .remove = led_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "led_test",
        .of_match_table = of_match_table_test 
    },
    //第四步 .id_table的优先级要比driver.name的优先级要高,优先与.id_table进行匹配
    .id_table = &led_idtable
};

static int led_driver_init(void)
{
    //第一步 我们看驱动文件要从init函数开始看
    int ret = 0;
    //第二步 在init函数里面注册了平台设备驱动platform_driver
    ret = platform_driver_register(&led_driver);
    if (ret < 0)
    {
        printk("platform_driver_register error \n");
    }
    printk("platform_driver_register ok \n");

    return 0;
}
static void led_driver_exit(void)
{
    printk("gooodbye! \n");
    misc_deregister(&misc_dev);
    platform_driver_unregister(&led_driver);
}
module_init(led_driver_init);
module_exit(led_driver_exit);

MODULE_LICENSE("GPL");

编译驱动代码为驱动模块,如下图所示:


 

55.4 编写测试APP

编写测试APP程序,完整代码如下所示:

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdlib.h>

int main(int argc,char *argv[])

{

    int fd;

    char buf[64] = {0};//定义buf缓存

    //打开设备节点

    fd = open("/dev/hello_misc",O_RDWR);

    if(fd < 0)

    {

        //打开设备节点失败

        perror("open error \n"); 

        return fd;

    }

    // atoi()将字符串转为整型,这里将第一个参数转化为整型后,存放在buf[0]中

    buf[0] = atoi(argv[1]);

    //把缓冲区数据写入文件中

    write(fd,buf,sizeof(buf));  

    printf("buf is %d\n",buf[0]); 

    close(fd);

    return 0;

}

我们将app.c文件拷贝到Ubuntu的/home/topeet/imx8mm/12目录下,输入以下命令编译app.c

 

55.5 运行测试

我们将55.2章节新编译的镜像烧写进开发板以后,启动开发板。

我们来查看一下添加的设备树节点,请参考本手册51.1查看设备树节点方法章节,如下图所示:

我们进入共享目录,加载驱动模块,如下图所示:

insmod driver.ko

 

我们再运行应用程序,输入命令“./app 1”LED亮;输入命令“./app 0”LED灭,如下图所示:

./app 0

./app 1

 

至此,我们已经学会了在设备树中使用pinctrl子系统和GPIO子系统。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/870048.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

神经网络拟合任何函数的数学原理

神经网络拟合任何函数的数学原理 神经网络拟合能力的数学表达 神经网络之所以理论上可以拟合任何函数&#xff0c;其核心在于其多层的结构和非线性的激活函数。通过增加层数和神经元数量&#xff0c;神经网络可以逼近复杂的函数关系。这一过程可以通过万能近似定理&#xff08…

Vue中的diff算法

文章目录 diff算法是什么比较方式源码分析patchpatchVnodeupdateChildren小结Vue3中diff算法优化diff算法是什么 diff算法是一种通过同层的树节点进行比较的高效算法 其有两个特点: 比较只会在同层级进行,不会跨层级比较在dff比较的过程中,循环从两边向中间比较(首位交叉…

24年第三届钉钉杯大学生大数据挑战赛浅析

需要完整资料&#xff0c;请关注WX&#xff1a;“小何数模”&#xff01; 本次钉钉杯大数据挑战赛的赛题已正式出炉&#xff0c;无论是赛题难度还是认可度&#xff0c;该比赛都是仅次于数模国赛的独一档&#xff0c;可以用于国赛前的练手训练。考虑到大家解题实属不易&#xf…

LiRouter V3.0无人机自主精细化巡检 LiStation V3.0输电线路巡检数障处理分系统 软件下载License使用

PeacePower LiRouter 输电线路无人机自主巡检航线规划系统&#xff08;本文档中简称 “LiRouter”&#xff09;&#xff0c;是基于高精度三维点云数据&#xff0c;在少量人工干预下为输电线路无人 机自主精细化巡检自动生成并输出航线的专业软件工具。 凭借在输电线路无人机智能…

04-数据库MySQL

一、项目要求 二、项目过程介绍 1、新建数据库 2、新建表 3、处理表 1.修改student 表中年龄(sage)字段属性&#xff0c;数据类型由int 改变为smallint 2.为Course表中Cno 课程号字段设置索引,并查看索引 3.为SC表建立按学号(sno)和课程号(cno)组合的升序的主键索引&#xf…

Vue使用FullCalendar实现日历/周历/月历

Vue使用FullCalendar实现日历/周历/月历 需求背景&#xff1a;项目上遇到新需求&#xff0c;要求实现工单以日/周/月历形式展示。而且要求不同工单根据状态显示不同颜色&#xff0c;一个工单内部&#xff0c;需要以不同颜色显示三个阶段。 效果图 日历 周历 月历 安装插件…

【分布式锁】Redission实现分布式锁

接着上一节&#xff0c;我们遇到了超卖的问题&#xff0c;并通过Redis实现分布式锁&#xff0c;进行了解决。本节 我将换一种方式实现分布式锁。 前提&#xff1a; nginx、redis、nacos 模块1&#xff1a; provider-and-consumer 端口 8023 模块2 rabbitmq-consumer 端口 8021 …

推荐几款支持AI剪辑并可使用个人视频素材的软件!

最强AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量 其实现在大部分的AI视频剪辑工具都可以实现一键成片&#xff0c;这里给你分享6款可以使用自己的素材实现AI剪辑的工具及其操作方法&#xff01; 一、剪映 剪映…

微信小程序之计算器

在日常生活中&#xff0c;计算器是人们广泛使用的工具&#xff0c;可以帮助我们快速且方便地计算金额、成本、利润等。下面将会讲解如何开发一个“计算器”微信小程序。 一、开发思路 1、界面和功能 “计算器”微信小程序的页面效果如图所示 在计算器中可以进行整数和小数的…

光猫设置桥接 路由器pppoe拨号 设置正常访问光猫 (openwrt)

网络信息展示 光猫桥接很简单吧&#xff0c;就不说了。先来列出修改前的网络接口和网络信息。 光猫192.168.1.1&#xff0c;openwrt 10.0.0.0/8 初始配置 需要记录的信息&#xff1a;WAN的网络设备&#xff08;eth1&#xff09;&#xff0c;光猫的IP&#xff08;192.168.1.1&am…

Qt中在pro中实现一些宏定义

在pro文件中利用 DEFINES 定义一些宏定义供工程整体使用。&#xff08;和在cpp/h文件文件中定义使用有点类似&#xff09;可以利用pro的中的宏定义实现一些全局的判断 pro中实现 #自定义一个变量 DEFINES "PI\"3.1415926\"" #自定义宏 DEFINES "T…

模拟电子技术-实验四 二极管电路仿真

实验四 二极管电路仿真 一&#xff0e;实验类型 验证性实验 二&#xff0e;实验目的 1、验证二极管的单向导电性 2、验证二极管的稳压特性。 三&#xff0e;实验原理 二极管的单向导电性&#xff1a; 四、实验内容 1、二极管参数测试仿真实验 1&#xff09;仪表仿真…

【MetaGPT系列】【MetaGPT完全实践宝典——多智能体实践】

目录 前言一、智能体1-1、Agent概述1-2、Agent与ChatGPT的区别 二、多智能体框架MetaGPT2-1、安装&配置2-2、使用已有的Agent&#xff08;ProductManager&#xff09;2-3、多智能体系统介绍2-4、多智能体案例分析2-4-1、构建智能体团队2-4-2、动作/行为 定义2-4-3、角色/智…

力扣141环形链表问题|快慢指针算法详细推理,判断链表是否有环|龟兔赛跑算法

做题链接 目录 前言&#xff1a; 一、算法推导&#xff1a; 1.假设有环并且一定会相遇&#xff0c;那么一定是在环内相遇&#xff0c;且是快指针追上慢指针。 2.有环就一定会相遇吗&#xff1f;快指针是每次跳两步&#xff0c;有没有可能把慢指针跳过去&#xff1f; 3.那一定…

算法第十五天:leetcode19.删除链表的倒数第N个节点

一、删除链表的倒数第N个节点的题目描述与链接 19.删除链表的倒数第N个节点的链接如下表所示&#xff0c;您可直接复制下面网址进入力扣学习&#xff0c;在观看下面的内容之前您一定要先做一遍哦&#xff0c;以便让我印象更深刻&#xff01;&#xff01;!https://leetcode.cn/p…

学习记录——day16 操作受限的线性表 链式栈

操作受限的线性表 1、在之前的内容&#xff0c;无论是顺序表还是链表&#xff0c;都是详细处理的线性表&#xff0c;既可以在端点处进行操作也 可以在中间位置操作 2、现实生活中&#xff0c;有很多并不需要在中间进行操作的序列&#xff0c;只在端点处进行操…

安宝特方案|解放双手,解决死角,AR带来质量监督新体验

AR质量监督 解放双手&#xff0c;解决死角 在当今制造业快速发展的背景下&#xff0c;质量监督成为确保产品高质量和完善的管理制度的关键环节。然而&#xff0c;传统的质量监督方式存在诸多挑战&#xff0c;如人工操作带来的效率低下、查岗不及时、摄像头死角等问题。 为了解…

wpf中轮询显示图片

本文的需求是&#xff0c;在一个文件夹中&#xff0c;放一堆图片的集合&#xff0c;然后在wpf程序中&#xff0c;按照定时的方式&#xff0c;循序显示照片。 全部代码 1.声明一个PictureInfo类 namespace WpfApp1 {public class PictureInfo{public string? FileName { get; …

OpenAI发布GPT-4 Mini的深度分析及中国大模型的弯道超车机会

引言 在OpenAI封禁中国IP访问其API后&#xff0c;紧接着推出了GPT-4 Mini&#xff0c;这是一个引发广泛关注和讨论的新举措。此举不仅让人们质疑OpenAI的战略方向&#xff0c;更引发了对中国大模型是否能弯道超车的讨论。本文将详细分析GPT-4 Mini的特点、市场影响及中国大模型…

Golang | Leetcode Golang题解之第275题H指数II

题目&#xff1a; 题解&#xff1a; func hIndex(citations []int) int {n : len(citations)return n - sort.Search(n, func(x int) bool { return citations[x] > n-x }) }