嵌入式linux与物联网进阶之路五:嵌入式驱动方式点亮led

程序诗人 程序诗人     2022-12-01     287

关键词:

简化的驱动框架

话说前面章节讲到了如何利用嵌入式驱动开发的方式进行驱动开发。由于其学习路线相比于裸机开发来说,上手难度稍微大一些,而且代码量也相对来说较多,所以对刚上手的人来说是颇有难度的。本章节,我们将以一个类似于Hello World点灯的例子,来讲解在linux下如何进行内核驱动的开发。

工欲善其事,必先利其器,开始之前,我们需要先将驱动开发用到的主体框架搭建一下,这里由于上节讲过,我这里直接贴上来:

#include <linux/init.h>  
#include <linux/module.h>
#include <linux/device.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/string.h>

#define LED_MAJOR 200
#define LED_NAME  "LED"

static int led_open(struct inode *inode, struct file *filep)
    printk("GPIO init \\n");
    return 0;


static int led_write(struct file *filep, const char __user *buf, size_t count, loff_t *ppos)
    return count;


static int led_release(struct inode *inode, struct file *filep)
    printk("Release !! \\n");
    return 0;



static const struct file_operations led_fops = 
    .owner   = THIS_MODULE,
    .open    = led_open,
    .write   = led_write,
    .release = led_release,
;

static int __init led_init(void)

    printk("led control device init success! \\r\\n");

    return 0;


static void __exit led_exit(void)
    printk(" led_exit \\r\\n");


module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("CXSR");

可以看到,整个驱动开发框架无非是如下几个东西,首先是module_init和module_exit函数,之后就是file_operations结构体,结构体中有对文件的读,写,打开,关闭等等,我们只需要把我们用到的操作去实现就行了。由于这部分整体比较固化,所以这里我不再赘述了。

由于在荔枝派中,我选择了A1口作为我的控制口,所以我先尝试利用GPIO手动控制了一下,整体控制流程如下:

1、 进入sys/class/gpio目录下
2、 执行echo 1 > export命令,可以看出来创建了gpio1的文件夹
3、 进入gpio1文件夹,cat direction查看其值为in,通过vi direction,将其值改为out后, wq保存。
4、 运行 echo 1 > value 命令,则可以设置A1口高电平,设置echo 0 > value 命令,则可以设置A1口低电平。

通过如上步骤,我们发现,我们可以控制板子上的LED灯的亮灭了。之所以能这么控制,是因为我们编译生成的根文件中,包含了对GPIO的支持,所以使得我们很容易的进行控制。这里的内容,之后我们会用到,先继续。

LED驱动编码

接下来,我们就开始针对刚才的驱动框架模板,来慢慢的填写我们的内容吧。

#include <linux/init.h>  
#include <linux/module.h>
#include <linux/device.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/string.h>

#define LED_MAJOR 200
#define LED_NAME  "LED"

/*led引脚,这里是A1脚*/
#define LED_PIN 1
static int gpio_status;

/*用户态-内核态交互缓冲区*/
static char recv_msg[20];

/*class名称, 会创建到/sys/class/目录下 */
static const char *CLASS_NAME = "led_control_class";

/*led分类,由此分类可以创建出设备*/
static struct class *led_control_class = NULL;

/*led设备,由此可以实现led控制*/
static struct device *led_control_device = NULL;

/*gpio初始设置*/
static void gpio_config(void)
    if(!gpio_is_valid(LED_PIN))
        printk("Error wrong gpio number !!\\n");
        return;
    
    gpio_request(LED_PIN, "led_ctr");
    /*设置引脚为输出状态*/
    gpio_direction_output(LED_PIN,1);
    /*初始置为高电平*/
    gpio_set_value(LED_PIN,1);
    gpio_status = 1;


/*gpio关闭设置*/ static void gpio_deconfig(void) gpio_free(LED_PIN);
/*初始灯*/ static int led_open(struct inode *inode, struct file *filep) printk("GPIO init \\n"); gpio_config(); return 0;
/*为灯的缓冲区进行数据交换*/ static int led_write(struct file *filep, const char __user *buf, size_t count, loff_t *ppos) int cnt = _copy_from_user(recv_msg, buf, count); if(0 == cnt)
/*on命令, 则开灯*/ if(0 == memcmp(recv_msg, "on", 2)) printk("LED on! \\n"); gpio_set_value(LED_PIN, 1); gpio_status = 1;
/*off命令,则关灯*/
else printk("LED off! \\n"); gpio_set_value(LED_PIN, 0); gpio_status = 0; else printk("ERROR occur when writing!!\\n"); return -EIO; return count;
/*灯设备关闭*/ static int led_release(struct inode *inode, struct file *filep) printk("Release !! \\n"); gpio_deconfig(); return 0; /*文件操作结构体*/ static const struct file_operations led_fops = .owner = THIS_MODULE, .open = led_open, .write = led_write, .release = led_release, ;
/*设备初始化*/ static int __init led_init(void) int ret = 0; /* 1. 注册设备 */ ret = register_chrdev(LED_MAJOR, LED_NAME,&led_fops); if(ret <0) printk("register fail!\\r\\n"); return -EIO; printk("register success, major number is %d \\r\\n",ret);
/* 2. 创建设备分类 */ led_control_class = class_create(THIS_MODULE, CLASS_NAME); if(IS_ERR(led_control_class)) unregister_chrdev(LED_MAJOR, LED_NAME); return -EIO;    
/* 3. 创建设备 */ led_control_device = device_create(led_control_class, NULL, MKDEV(LED_MAJOR,0), NULL,LED_NAME); if(IS_ERR(led_control_device)) class_destroy(led_control_class); unregister_chrdev(LED_MAJOR, LED_NAME); return -EIO; printk("led control device init success! \\r\\n"); return 0;
/*设备注销*/ static void __exit led_exit(void) printk(" led_exit \\r\\n"); device_destroy(led_control_class, MKDEV(LED_MAJOR,0)); class_unregister(led_control_class); class_destroy(led_control_class); unregister_chrdev(LED_MAJOR,LED_NAME); module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("CXSR");

  

由于代码部分,我做了详尽的注释,所以这里不再过多的解释了。这里唯一需要注意的几点就是:

1. 用户态和内核态的数据交换,是需要通过copy_from_user或者copy_to_user来进行。

2. 设备初始化,需要先利用register_chrdev来注册字节设备,之后才能进行设备的创建操作。

3. insmod和rmmod命令的执行,分别对应led_init函数和led_exit函数。

Makefile制作

既然代码写好了,我们这里就需要来编译生成ko文件才行。我们来手写一个Makefile文件。

KERNELDIR := /home/scy/linux-mi/linux-f1c100s-480272lcd-test/

CURRENT_PATH := $(shell pwd)

obj-m := led1.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

可以看到,整个文件内容比较少,其中KERNELDIR代表linux内核源码的目录,CURRENT_PATH则代表当前路径,kernel_modules则代表我们mak命令要执行的内容,需要注意的是,如果你的板子是ARM架构的,这里要加上ARCH=arm,如果你在编译内核的时候,有用到自己的编译交叉链,那么这里也需要指定一下CROSS_COMPILE,否则可能会因为内核编译方式不一样,导致生成的ko文件,不能被板子上的系统所加载。

执行make命令,可以看到如下熟悉的输出。

 

之后,我们就可以看到led1.ko文件被生成了。

 开发板上跑起来

由于ko文件已经被生成,所以这里我们拿下tf卡,然后通过如下的命令,将我们的led1.ko,拷贝到media目录中去:

 

嵌入式linux与物联网进阶之路三:根文件系统制作

承接前篇,我们的linux内核终于制作好了,也顺利的加载起来了,但是由于没有根文件系统,所以说加载到最后,是无法进入系统的。而本节内容则是讲解如何来制作根文件系统的。BuildRoot创建根文件系统由于BuildRoot工具可以构... 查看详情

嵌入式操作系统与物联网演进之路

...术的重要组成部分,回顾其发展,其中不得不提的必然是嵌入式系统。传统的嵌入式系统与互联网的发展衍生出物联网,而在如今的物联网热潮之下,嵌入式系统也面临着全新的机遇与挑战。那么,两者的碰撞融合究竟会带来怎... 查看详情

如何成为高手?嵌入式开发进阶之路...

...达大家好,我是小麦,今天给大家分享一下看看嵌入式要掌握哪些技能。1嵌入式人才要求嵌入式行业需要什么样的技术人才?仔细观察各种招聘的岗位要求,无非是两方面:1、通用要求比如什么学历,多... 查看详情

[架构之路-35]:目标系统-系统软件-linuxos内核模块与内核设备驱动程序,一切皆文件,linux虚拟文件系统与统一设备模型(代码片段)

目录前言:第1章嵌入式系统概述1.1什么是嵌入式系统1.2嵌入式系统的架构第2章内核驱动程序架构 2.1什么驱动程序2.2驱动程序的作用2.3Linux下的内核硬件设备驱动程序第3章Linux驱动工程师学习路径3.1成长之路与必备的知识3.2C... 查看详情

linux内核---嵌入式linux内核的五个子系统

转自:https://blog.csdn.net/qq_27522735/article/details/63251168Linux内核主要由进程调度(SCHED)、内存管理(MM)、虚拟文件系统(VFS)、网络接口(NET)和进程间通信(IPC)5个子系统组成,如图1所示。 图1Linux内核的组成部分与关系&nb... 查看详情

[架构之路-28]:目标系统-系统软件-linuxos内核功能架构图解内核构建内核启动流程(代码片段)

目录第1章Linux概述1.1嵌入式系统简介1.2嵌入式系统的架构1.3什么是操作系统OS与主流操作系统1.4 什么是嵌入式操作系统1.5Linux操作系统1.6Linux操作系统的特点与好处1.7 Linux系统工具链完整1.8Linux内核第2章Linux内核为应用程序提供... 查看详情

[架构之路-28]:目标系统-系统软件-linuxos内核功能架构图解内核构建内核启动流程(代码片段)

目录第1章Linux概述1.1嵌入式系统简介1.2嵌入式系统的架构1.3什么是操作系统OS与主流操作系统1.4 什么是嵌入式操作系统1.5Linux操作系统1.6Linux操作系统的特点与好处1.7 Linux系统工具链完整1.8Linux内核第2章Linux内核为应用程序提供... 查看详情

嵌入式开发工程师学习线路图

一、核心学习课程  1、应用软件基础      Linux基础      C语言  2、底层驱动程序开发      ARM编程(ARM体系结构、ARM汇编、开发板常见的外设裸机编程)      Linux驱动开发二、Android扩展  ... 查看详情

嵌入式为什么没有嵌入式软件架构师?

【高级嵌入式软件工程师进阶之路】:版权归原作者所有。如涉及作品版权问题,请与我联系删除此处嵌入式特指基于linux平台,单片机和其他rtos不在讨论范围~我从事嵌入式软件开发有6,7个年头,bsp、驱动... 查看详情

嵌入式linux课程设计

参考技术AⅠ跪求ARM嵌入式linux系统开发详解(珍藏版)pdf珍藏版啊。。Ⅱ学嵌入式linux需要先学什么刚入门的时候,淘宝买一块cortexm3开发板即可入手,通过项目,你需要了解:任务调度、进程间通信、内存管理、设备驱动、文... 查看详情

入门物联网,嵌入式是关键

入门物联网,嵌入式是关键在2019年,我国人力资源社会保障部、市场监管总局、统计局正式向社会发布了13个新职业,其中就包括了物联网工程技术人员、人工智能工程技术人员、大数据工程技术人员等等。那么对于想要成为物... 查看详情

linux驱动开发重点关注内容--摘自《嵌入式linux驱动模板精讲与项目实践》

本文摘自本人拙著《嵌入式Linux驱动模板精讲与项目实践》初步看起来Linux设备驱动开发涉及内容非常多,而须要实现驱动的设备千差万别。事实上做一段时间驱动之后回首看来主要就是下面几点:(1)对驱动进行分类。先归纳... 查看详情

嵌入式复习(代码片段)

...器、gdb调试器、make工程管理器、Bootloader、文件系统以及嵌入式Linux应用程序、嵌入式Linux驱动程序、Linux进程间通信以及Linux下的SOCKET通信等知识的不完全叙述,要求学生从题后给出的供选择的答案中选择合适的答案,补足这些... 查看详情

嵌入式linux第二部分-裸机开发/系统移植/驱动开发/内核开发

本部分主要专注构建从0到1的嵌入式Linux学习知识体系。主要涉及Linux环境配置,嵌入式Linux裸机开发,Linux文件系统及系统移植,驱动开发等部分。目前持续更新中,更新时间:2022年11月27日【嵌入式Linux】裸机开发篇LinuxC语言及M... 查看详情

嵌入式linux第二部分-裸机开发/系统移植/驱动开发/内核开发

本部分主要专注构建从0到1的嵌入式Linux学习知识体系。主要涉及Linux环境配置,嵌入式Linux裸机开发,Linux文件系统及系统移植,驱动开发等部分。目前持续更新中,更新时间:2022年11月13日【嵌入式Linux】裸机开发篇LinuxC语言及M... 查看详情

tuyaos开发教程-嵌入式开发套件

0.前言​智能时代的到来,让物与人的关系、物与物的关系更加密切。但是物联网行业种类复杂、接入环节冗长、设备规模以及数据量庞大等,都成为了物联网发展的难点。为此,涂鸦推出了一系列应用于IoT领域的开... 查看详情

字符设备驱动程序

一、     实验目的和要求1.    学习嵌入式Linux的GPIO的使用方式;2.    学习嵌入式Linux的Arduino接口库;3.    学习使用面包板搭简单的外部电路;4.    学习Linux设备驱动程序的开发过程;5.    学习在... 查看详情

物联网的全栈开发之路

本文由嵌入式企鹅圈原创团队成员-华南师范大学物联网创新中心黄鑫执笔。      两年前我还一直专注在android系统定制领域的研究,对物联网关注比较少,尽管我个人对智能家居的概念和场景体验也比较熟... 查看详情