852|2

75

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

《原子Linux驱动开发》2-编译下载运行点灯! [复制链接]

前言:

经过一系列的摸索,终于也是配置好了开发板的交叉编译环境,接下来我将把《原子Linux驱动开发》书上的LED驱动例程分享给大家

介绍:

从单片机转到linux,想点个灯就没有那么简单了,在单片机中就直接配置寄存器,如果用STM32的直接在cubemx中图形化配置,完成外设的初始化,然后调用对应控制GPIO的函数即可。但是在linux中就没有那么简单了。

在Linux中点亮一个灯的方法有很多,裸机驱动点灯,Linux下的led驱动点灯,设备树下的led点灯,pinctrl 和 gpio 子系统点灯,虽然都是点灯,但是驱动的本质还是没变,都是配置 LED 灯 所使用的 GPIO 寄存器,驱动开发方式和裸机基本没啥区别。Linux 是一个庞大而完善的系统, 尤其是驱动框架,像 GPIO 这种最基本的驱动不可能采用“原始”的裸机驱动开发方式,否则 就相当于你买了一辆车,结果每天推着车去上班。Linux 内核提供了 pinctrl 和 gpio 子系统用于 GPIO 驱动,随着学习的不断深入,最后我们将学习如何借助 pinctrl 和 gpio 子系统来简化 GPIO 驱动开发。

但在这之前,还是得老老实实的编写led驱动,即本书的第二章:嵌入式Linux LED灯驱动开发实验

阅读:

先来学习理论部分,我们跟着目录来,学习顺序依次是了解Linux下LED灯驱动原理,硬件原理图分析,实验程序编写,运行测试

1)Linux下led灯驱动原理

Linux 下的任何外设驱动,最终都是要配置相应的硬件寄存器。所以本章的 LED 灯驱动最 终也是对 I.MX6ULL 的 IO 口进行配置,与裸机实验不同的是,在 Linux 下编写驱动要符合 Linux 的驱动框架。I.MX6U-ALPHA 开发板上的 LED 连接到 I.MX6ULL 的 GPIO1_IO03 这个引脚上, 因此本章实验的重点就是编写 Linux 下 I.MX6UL 引脚控制驱动。

1.地址映射

在编写驱动之前,我们需要先简单了解一下 MMU 这个神器,MMU 全称叫做 Memory Manage Unit,也就是内存管理单元。在老版本的 Linux 中要求处理器必须有 MMU,但是现在 Linux 内核已经支持无 MMU 的处理器了。MMU 主要完成的功能如下: ①、完成虚拟空间到物理空间的映射。 ②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。 我们重点来看一下第①点,也就是虚拟空间到物理空间的映射,也叫做地址映射。首先了 解两个地址概念:虚拟地址(VA,Virtual Address)、物理地址(PA,Physcical Address)。对于 32 位 的处理器来说,虚拟地址范围是 2^32=4GB,我们的开发板上有 512MB 的 DDR3,这 512MB 的 内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间,如图 所示:

Linux 内核启动的时候会初始化 MMU,设置好内存映射,设置好以后 CPU 访问的都是虚 拟 地 址 。 比 如 I.MX6ULL 的 GPIO1_IO03 引 脚 的 复 用 寄 存 器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068。如果没有开启 MMU 的话 直接向 0X020E0068 这个寄存器地址写入数据就可以配置 GPIO1_IO03 的复用功能。现在开启 了 MMU,并且设置了内存映射,因此就不能直接向 0X020E0068 这个地址写入数据了。我们必 须得到 0X020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址,这里就涉及到了物理内 存和虚拟内存之间的转换,需要用到两个函数:ioremap 和 iounmap。ioremap 函 数 用 于 获 取 指 定 物 理 地 址 空 间 对 应 的 虚 拟 地 址 空 间 , 定义arch/arm/include/asm/io.h 文件中,定义如下:
1 #define ioremap(cookie,size) __arm_ioremap((cookie), (size), 
MT_DEVICE) 
2 
3 void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, 
unsigned int mtype) 
4 { 
5 return arch_ioremap_caller(phys_addr, size, mtype, 
__builtin_return_address(0)); 
6 }

ioremap 是个宏,有两个参数:cookie 和 size,真正起作用的是函数__arm_ioremap,此函 数有三个参数和一个返回值,这些参数和返回值的含义如下: phys_addr:要映射的物理起始地址。 size:要映射的内存空间大小。 mtype:ioremap 的类型,可以选择 MT_DEVICE、MT_DEVICE_NONSHARED、 MT_DEVICE_CACHED 和 MT_DEVICE_WC,ioremap 函数选择 MT_DEVICE。 返回值:__iomem 类型的指针,指向映射后的虚拟空间首地址。 假如我们要获取 I.MX6ULL 的 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器对应 的虚拟地址,使用如下代码即可:

#define SW_MUX_GPIO1_IO03_BASE (0X020E0068) 
static void __iomem* SW_MUX_GPIO1_IO03; 
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); 

宏 SW_MUX_GPIO1_IO03_BASE 是寄存器物理地址,SW_MUX_GPIO1_IO03 是映射后 的虚拟地址。对于 I.MX6ULL 来说一个寄存器是 4 字节(32 位)的,因此映射的内存长度为 4。 映射完成以后直接对 SW_MUX_GPIO1_IO03 进行读写操作即可。

iounmap 函数:卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射,iounmap 函数原 型如下:

void iounmap (volatile void __iomem *addr)

iounmap 只有一个参数 addr,此参数就是要取消映射的虚拟地址空间首地址。假如我们现 在要取消掉 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器的地址映射,使用如下代码 即可:

iounmap(SW_MUX_GPIO1_IO03); 

2)I/O 内存访问函数

这里说的 I/O 是输入/输出的意思,并不是我们学习单片机的时候讲的 GPIO 引脚。这里涉 及到两个概念:I/O 端口和 I/O 内存。当外部寄存器或内存映射到 IO 空间时,称为 I/O 端口。 当外部寄存器或内存映射到内存空间时,称为 I/O 内存。但是对于 ARM 来说没有 I/O 空间这个 概念,因此 ARM 体系下只有 I/O 内存(可以直接理解为内存)。使用 ioremap 函数将寄存器的物 理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议 这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。

读操作函数有如下几个:

readb、readw 和 readl 这三个函数分别对应 8bit、16bit 和 32bit 读操作,参数 addr 就是要 读取写内存地址,返回值就是读取到的数据。

写操作函数有如下几个:

writeb、writew 和 writel 这三个函数分别对应 8bit、16bit 和 32bit 写操作,参数 value 是要 写入的数值,addr 是要写入的地址。

3)硬件原理图

程序编写:
书中提到的MX6U阿尔法开发板我是没有搞来,用的是手头上这块STM32mp135dk
说一下点灯的思路
1编译:创建led文件目录编写led驱动代码文件和Makefile文件
2运行:在已经搭建好的交叉编译环境中make一下
3下载:把Ubuntu系统中编译好的程序拷贝到win主机上,在通过ssh下载到开发板usr/local目录下
4运行:通过ssh终端运行文件,灯亮
有了思路接下来我们一步一步操作就好了

按流程用到的命令大致就是终端中的几条,第一遍摸索的时候比较急也很乱就没有一一截图,这里偷下懒直接放出总步骤(没有cd到具体目录),记得要source一下环境。

led.c:

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <linux/gpio.h>

int main(int argc, char **argv)
{
	struct gpiohandle_request req;
	struct gpiohandle_data data;
	char chrdev_name[20];
	int fd, ret;

	strcpy(chrdev_name, "/dev/gpiochip9");

	/*  Open device: gpiochip9 for GPIO MCP */
	fd = open(chrdev_name, 0);
	if (fd == -1) {
		ret = -errno;
		fprintf(stderr, "Failed to open %s\n", chrdev_name);

		return ret;
	}

	/* request GPIO line: GPIO_9_15 */
	req.lineoffsets[0] = 15;
	req.flags = GPIOHANDLE_REQUEST_OUTPUT;
	memcpy(req.default_values, &data, sizeof(req.default_values));
	strcpy(req.consumer_label, "led_gpio_mcp_15");
	req.lines  = 1;

	ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
	if (ret == -1) {
		ret = -errno;
		fprintf(stderr, "Failed to issue GET LINEHANDLE IOCTL (%d)\n",
			ret);
	}
	if (close(fd) == -1)
		perror("Failed to close GPIO character device file");

	/*  Start led blinking */
	while(1) {

		data.values[0] = !data.values[0];
		ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
		if (ret == -1) {
			ret = -errno;
			fprintf(stderr, "Failed to issue %s (%d)\n",
					"GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);
		}
		sleep(1);
	}

	/*  release line */
	ret = close(req.fd);
	if (ret == -1) {
		perror("Failed to close GPIO LINEHANDLE device file");
		ret = -errno;
	}
	return ret;
}

Makefile代码:

PROG = led
SRCS = led.c
OBJS = $(SRCS:.c=.o)

CLEANFILES = $(PROG)

# Add / change option in CFLAGS if needed
# CFLAGS += <new option>

$(PROG):  $(OBJS)
	$(CC) $(CFLAGS) -o $(PROG) $(OBJS)

.c.o:
	$(CC) $(CFLAGS) -c $< -o $@

all: $(PROG)
 
 
clean:
	rm -f $(CLEANFILES) $(patsubst %.c,%.o, $(SRCS)) *~

这里我用FileZila实现win和Ubuntu直接的文件传输

MobaXterm登录ssh

 

然后在终端运行命令

su -l weston -c "/usr/local/led"

效果:

61d93b739652a1585dd285b22676cb26

红灯闪烁

其实还有更简单的st官方的GPIOLib点灯,我也玩了下,只需要在ssh终端中输入指令即可完成点灯,它是用户层的代码可以直接通过char device控制GPIOlib,从而控制GPIO,和单片机的玩法有点像。在串口终端直接输入:

gpioset -c  gpiochip9 14=1

效果:

501484d7a882069349c47e994ab7401a

绿灯点亮

led闪烁程序: led (16.68 KB, 下载次数: 0)

最新回复

谢谢分享,期待更多后续   详情 回复 发表于 2024-3-28 15:43
点赞(1) 关注

回复
举报

7608

帖子

18

TA的资源

五彩晶圆(高级)

沙发
 

谢谢分享,期待更多后续

点评

谢佬鼓励  详情 回复 发表于 2024-3-29 23:36
 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 

回复

75

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
freebsder 发表于 2024-3-28 15:43 谢谢分享,期待更多后续

谢佬鼓励

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
一个ARM7的手册,全部的语言格式和用法

一个ARM7的手册,全部的语言格式和用法

LPC1343学习笔记(连载中)--6月21日新增第十二篇

有幸拿到了EEWORLD论坛的LPC1343评估板,实在是一件意外而激励人心的事情。为感谢EEWORLD和NXP,特将学习过程与大家共同分享。也 ...

2011 TI M3 DAY资料提前放出

明天TI M3 DAY就要开始了,提前放出相关资料,感兴趣的朋友可以预习一下。 本帖最后由 jkhu 于 2011-6-19 23:19 编辑 ]

我的Beaglebone学习历程

整理一下前面发的帖子,搞个总帖,方便大家交流。1.BeagleBone 硬件性能测试 _周计划https://bbs.eeworld.com.cn/thread-324885- ...

GD32F105RBT6和 STM32F103RBT6是完全兼容的,程序也完全兼容吗?

GD32F105RBT6和 STM32F103RBT6是完全兼容的,一个引脚都不差,,,下载程序的接口也一样的,,,是不是可以直接使用原来STM32里 ...

【Silicon Labs 开发套件评测】+SPI flash(MX25R8035F)

在很多应用中,需要保存配置参数。一般都会外接一个存储器来存储,数据比较少会使用EEPROM,使用SPI flash的也比较多,存储空间 ...

【基于NUCLEO-F746ZG电机开发应用】12.参数配置-定时器TIM1配置

在伺服电机的控制过程中,使电机能够按照自己的想法转起来,一定要用到PWM输出控制,但是PWM该如何产生,频率如何控制,占空比 ...

瑞萨CPK-RA6M4 开发板测评----I2C

功能模块的硬件介绍-->I2C I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串 ...

二极管常用的8个用途

之前有个版本是7中用途 二极管是十分常用的基础元器件,本文主要介绍了二极管的一些作用,比如防反、整流、稳压、续流、检波 ...

visionfive的星光2开发板移植openwrt上篇--编译篇

本帖最后由 怀揣少年梦 于 2023-8-15 23:57 编辑 ###一、openwrt是什么? openwrt是一个常用于路由器嵌入式linux操作系统 ...

关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表