完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>
GPIO
的寄存器通过
ioremap
函数转换之后,可以通过直接控制虚拟地址来控制物理地址(寄存器的实际地址),这样就实现
GPIO
的读和写以及其它任意功能。
需要的基础知识
虚拟地址和物理地址
内存管理单元概念
linux
驱动模块的加载
主要内容
GPIO
的寄存器文档详细介绍和说明
函数
ioremap
的用法
使用
ioremap
实现对
GPIO
的控制
硬件
以
LED2
(靠近蜂鸣器的
LED
)为例,介绍原理图以及
Datasheet
中对应的寄存器。
原理图
打开底板原理图
pdf
文档,如下图所示,选取
LED2
,网络标号是
KP_COL0
。
通过查找网络标号
KP_COL0
,发现
KP_COL0
接到连接器上,如下图所示。
打开核心板
pdf
文档,查找网络标号
KP_COL0
,找到
exnoys4412
芯片对应的
GPIO
,
如下图所示。
如上图所示,这个管脚对应的
GPIO
是
GPL2_0
。
打开
4412
的
Datasheet
,找到
GPIO
对应的章节,在第六章。
寄存器
在第六章中有对应的小节“
6.2 Register Description
”,在“
6.2.1 Registers Summary
”
中可以找到
GPL2
对应的寄存器,如下图所示。
如上图所示,通过阅读寄存器的描述文字,可以清楚需要控制的寄存器为
GPL2CON
(配
置寄存器),
GPL2DAT
(数据寄存器,对应输出模式,寄存器写
0
输出低电平,写
1
输出高
电平),
GPL2PUD
(上拉和下拉配置,输出需要上拉),其它几个寄存器不需要使用,默认
即可。
寄存器地址一般由基地址和偏移地址组成,通过上图中的“
Offset
”可以发现三个寄存器
GPL2CON
,
GPL2DAT
,
GPL2PUD
的偏移地址分别是
0x0100
,
0x0104
,
0x0108
exnoys4412
中
GPIO
寄存器最大是
32
位,同时也可以是
16
位或者是
8
位,这一点在
对寄存器操作的时候需要用到。通过上图中的“
Reset Value
”,可以发现三个寄存器
GPL2CON
,
GPL2DAT
,
GPL2PUD
的复位之后的值是
0x0000_0000
,
0x00
,
0x5555
,那
么这几个寄存器的值分别是
32
位,
8
位,
16
位。
寄存器
GPL2CON
在
Datasheet
中搜关键字“
GPL2CON
”,如下图所示,可以发现这个寄存器的基地址是
0x1100_0000
。那么
GPL2CON
寄存器的物理地址
phy = 0x11000000 + 0x0100
。
LED2
对应的
GPIO
是
GPL2
中的
Bit0
,那么找到
GPL2CON[0]
(就在上图的表格中,下
翻即可找到),如下图所示。
如上图所示,
GPL2CON
寄存器的
[3:0]
这低四位写
0x0
则是输入模式,
0x1
则是输出模
式。
LED2
显然需要配置为输出模式,那么
GPL2CON
寄存器的
bit[3:0]
需要写
0x1
。
寄存器
GPL2DAT
在
Datasheet
中搜关键字“
GPL2DAT
”,如下图所示,可以发现这个寄存器的基地址是
0x1100_0000
。那么
GPL2DAT
寄存器的物理地址
phy = 0x11000000 + 0x0104
。
通过上图中
Description
部分,可以看到这段英文“
When configuring as output port,
the pin state should be same as the corresponding bit.
”翻译成中文就是“
IO
被配置为
输出模式,寄存器中写
0
则输出低电平,写
1
则输出高电平”。那么
GPL2DAT
寄存器的
bit
0
位写
0
灯则灭,写
1
则亮。
寄存器
GPL2PUD
在
Datasheet
中搜关键字“
GPL2PUD
”,如下图所示,可以发现这个寄存器的基地址是
0x1100_0000
。那么
GPL2PUD
寄存器的物理地址
phy = 0x11000000 + 0x0108
。
如上图所示,先介绍“
Bit
”部分,“
[2n + 1:2n] n = 0 to7
”表示
n
可以取
0-7
,分别
对应
8
个
pin
脚,
n=0,2x0:2x0+1
,就是
bit[0
,
,
n=1,2x1:2x1+1
,就是
bit[2
,
依次类
推。
GPL2[0]
则
n=0
,需要对
bit[0,1]
进行操作。
这里有一点需要说明,如下图所示,红色框中“
Disables
”应该是
Enables
,这是三星
Datasheet
中的一个小错误。
综上所述,需要对
GPL2PUD
寄存器的
bit[0 1]
写
0x3
。
代码中所需资源小结
代码中需要寄存器的物理地址、寄存器长度、对应位的值,如下表所示。
软件部分
本节主要内容为软件知识,在介绍代码之前,会先简单介绍一下
ioremap
函数,
C
语言
中的
volatile
关键字。
ioremap
函数
物理地址和虚拟地址之间需要使用
ioremap
函数进行映射,将物理地址映射成虚拟地址
之后对虚拟地址操作就相当于对物理地址进行操作,也就是直接对寄存器进行操作。
映射函数
void *ioremap(unsigned long phys_addr, unsigned long size)
phys_addr
:要映射的起始的
IO
地址
size
:要映射的空间的大小。
取消映射函数
void iounmap(void * addr)
addr
:映射后得到的虚拟地址。
编译的小知识
-volatile
关键字
在
C
语言中,编译器在进行编译优化时,如果发现多次获取同一变量的代码之间没有对
这个变量进行操作,例如:
int j = 10
;
int call_first = j
;
……
(
大段代码,但是没有对
j
进行操作)
int call_sec = j
;
编译器会自动将
call_first
的值给
call_sec
,而不会再回头去取
j
的值。如果涉及寄存器,
那就很容易出错。
涉及到寄存器的地址,
C
语言中提供了
volatile
关键字。它能影响编译器编译的结果
,
它会
通知编译器,
volatile
定义的变量是随时可能发生变化的,与
volatile
变量有关的运算,不要
进行编译优化,以免出错。如果上面的变量使用
volatile
定义,那么每次使用它的时候必须从
j
的地址中读取,因而编译器生成的可执行码会重新从
j
的地址读取数据放在变量
call_sec
中。
代码
本节的代码需要有文档前面的知识,以及模块注册和卸载的知识。
驱动源码
代码文件
dri_ioremap.c
。基础知识前面已经介绍了很多,代码的注释也很多,就不额外
的代码分析了。
//sch:KP_COL0
→
GPL2_0
/*GPL2
→
datasheet:GPL2CON,GPL2DAT,GPL2PUD
Add = Base Address+Offset
GPL2CON = 0x1100_0000 + 0x0100 = 0x11000000 + 0x0100
s3c_gpio_cfgpin
GPL2DAT = 0x1100_0000 + 0x0104 = 0x11000000 + 0x0104
gpio_set_value
GPL2PUD = 0x1100_0000 + 0x0108 = 0x11000000 + 0x0108
*/
/*GPL2_0
→
datasheet:GPL2CON[0] GPL2DAT[0] GPL2PUD[0]
GPL2CON[0] [3:0]
→
output 0x0
GPL2DAT 0 or 1
GPL2PUD[0] 0x3
*/
/*
寄存器不一定都是
32
位的,也有
16
和
8
位的
写寄存器的时候需要注意
*/
#include
#include
#include
volatile unsigned long virt_addr, phys_addr;//
用于存放虚拟地址和物理地址
volatile unsigned long *GPL2CON, *GPL2DAT, *GPL2PUD;//
用于存放三个寄存器的地址
void gpl2_device_init(void)
{
//0x11000100 + 0x10
包括全所有的
IO
引脚寄存器地址
phys_addr = 0x11000100;
//0x11000100=GPL2CON
//
在虚拟地址空间中申请一块长度为
0x10
的连续空间
//
这样,物理地址
phys_addr
到
phys_addr+0x10
对应虚拟地址
virt_addr
到
virt_addr+0x10
virt_addr =(unsigned long)ioremap(phys_addr, 0x10);
//
指定需要操作的寄存器的地址
GPL2CON = (unsigned long *)(virt_addr + 0x00);
GPL2DAT = (unsigned long *)(virt_addr + 0x04);
GPL2PUD = (unsigned long *)(virt_addr + 0x08);
}
//led
配置函数
,
配置开发板的
GPIO
的寄存器
void gpl2_configure(void)
{
//GPL2CON
寄存器
,bit0
设置为
1
,输出模式
*GPL2CON |= 0x00000001;
//GPL2PUD
寄存器,
bit[0
:
1]
设置为
0x3,
上拉模式
*GPL2PUD |= 0x0003;
}
void gpl2_on(void) //
点亮
led
{
*GPL2DAT |= 0x01;
}
void gpl2_off(void) //
灭掉
led
{
*GPL2DAT &= 0xfe;
}
static int __init led_gpl2_init(void) //
模块初始化函数
{
printk("init n");
gpl2_device_init(); //
实现
IO
内存的映射
gpl2_configure(); //
配置
GPL2
为输出模式
gpl2_on();
printk("led gpl2 openn");
return 0;
}
static void __exit led_gpl2_exit(void) //
模块卸载函数
{
printk("exit n");
gpl2_off();
iounmap((void *)virt_addr); //
撤销映射关系
printk("led gpl2 closen");
}
module_init(led_gpl2_init);
module_exit(led_gpl2_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("dz20160510");
MODULE_VERSION("2016-05-10");
编译脚本
编译脚本
Makefile
代码如下。
#!/bin/bash
#
通知编译器我们要编译模块的哪些源码
#
这里是编译
read_gpio.c
这个文件编译成中间文件
read_gpio.o
obj-m += dri_ioremap.o
#
源码目录变量,这里用户需要根据实际情况选择路径
#
作者是将
Linux
的源码拷贝到目录
/home/topeet/android4.0
下并解压的
KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0
#
当前目录变量
PWD ?= $(shell pwd)
#make
命名默认寻找第一个目标
#make -C
就是指调用执行的路径
#$(KDIR)Linux
源码目录,作者这里指的是
/home/topeet/android4.0/iTop4412_Kernel_3.0
#$(PWD)
当前目录变量
#modules
要执行的操作
all:
make -C $(KDIR) M=$(PWD) modules
#make clean
执行的操作是删除后缀为
o
的文件
clean:
rm -rf *.o
编译运行测试
编译之后,运行
iTOP-4412
开发板最小系统,加载驱动模块,靠近
led
的灯会亮,卸载
模块则会灭。
|
|
相关推荐
|
|
· 2016-7-3 19:04:14
讲得不错,学习了。
|
|
|
|
|
|
【米尔-国产瑞芯微RK3568开发板试用体验】评测一 系统配置
807 浏览0 评论
1246 浏览0 评论
2191 浏览1 评论
如何配置Linux操作系统设备树让我的开发板可以将板子上的GPIO接口用作 I2S输出??
2510 浏览1 评论
1649 浏览0 评论
小黑屋|手机版|Archiver|德赢Vwin官网(湘ICP备2023018690号)
GMT+8, 2024-8-29 01:10, Processed in 0.860319 second(s), Total 84, Slave 66 queries .
Powered by德赢Vwin官网 网
© 2015bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号