Linux驱动入门(二)操作硬件

copy from :https://blog.csdn.net/weixin_42462202/article/details/99949396

Linux驱动入门(二)操作硬件
文章目录
Linux驱动入门(二)操作硬件
一、通用做法
ioremap
iounmap
寄存器读写
二、gpiolib
gpio.h
gpiolib.c
三星平台提供的gpio-cfg
三、总结
一、通用做法
玩过单片机的朋友应该懂得如何操作寄存器

举个例子,例如现在想往寄存器地址0xFF223440写数据,在单片机编程中的做法如下

volatile unsigned int *reg = (unsigned int*)0xFF223440;
*reg = val;

在Linux中做法也是类似,只不过Linux不可以直接访问寄存器地址,而要经过映射后才能访问,下面开始介绍

ioremap
映射地址

/*
* cookie:表示要映射的地址
* size:表示要映射的地址范围
*/
ioremap(cookie,size)

例如上面要操作寄存器0xFF223440,在Linux的做法如下

volatile unsigned int *reg = ioremap(0xFF223440, 4);
*reg = val;

当然在映射前要先申请request_region(start,n,name)地址空间,不过这不是必须的

在映射完地址后,当驱动程序退出时,不再使用此地址,需要使用iounmap取消映射

iounmap
取消地址映射

/*
* cookie:映射后的地址
*/
iounmap(cookie)

例如上面的例子中,在驱动程序退出时,需要调用

iounmap(reg);

在取消映射后,如果先前有申请地址空间,那么就需要释放申请的地址空间release_region(start,n)

寄存器读写
在上面的例子中,读写寄存器通过直接访问指针,而内核中提供了一套宏定义来读写寄存器

readb(c) //读8位
readw(c) //读16位
readl(c) ///读32位

writeb(v,c) //写8位
writew(v,c) //写16位
writel(v,c) //写32位

二、gpiolib
上面介绍的是通用的做法,可以访问和设置任意的寄存器

内核中对于gpio的设置,提供了更加简便的方法-gpiolib

gpiolib是一套对gpio的操作函数,有了这些函数,就可以简化对gpio进行操作,而不需要直接通过寄存器来操作

当然gpiolib仅限于操作gpio,如果要操作非gpio相关的寄存器(例如lcd控制器,摄像头控制器等),那么还是要使用上面讲的通用方法,下面开始介绍gpiolib

gpio.h
对于每一款芯片,大多在移植的时候都有对应一个gpio.h文件

对于每一款芯片,大多在移植的时候都有对应一个gpio.h文件

以三星平台的S5PV210为例,gpio.h位于archarmmach-s5pv210includemachgpio.h

其中定义了一系列的gpio相关的宏

S5PV210_GPA0(_nr)
S5PV210_GPA1(_nr)
S5PV210_GPB(_nr)
...

​ 可以使用这些宏定义,通过gpiolib提供的函数来操作gpio

gpiolib.c
gpiolib.c位于driversgpiogpiolib.c,其中定义了一系列的gpio操作函数,gpio的操作函数需要搭配gpio.h中的宏定义使用

int gpio_request(unsigned gpio, const char *label)
void gpio_free(unsigned gpio)
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
int gpio_get_value(unsigned gpio)
void gpio_set_value(unsigned gpio, int value)
int gpio_to_irq(unsigned gpio)

gpio_request

申请gpio,在使用gpio前,应该先申请gpio,当然这并不是必须的

gpio_free

释放gpio,用于释放被申请了的gpio

gpio_direction_input

设置gpio为输入

gpio_direction_output

设置gpio为输出,并设置gpio高低电平

gpio_get_value

获取gpio的高低电平

gpio_set_value

设置gpio的高低电平

gpio_to_irq

获取gpio对应的外部中断

如果我们设置S5PV210的GPIOA0的第四个引脚为高电平,我可以这样做

/* 申请 */
gpio_request(S5PV210_GPA0(3), "m_gpio");

/* 设置 */
gpio_direction_output(S5PV210_GPA0(3), 1);

/* 不需要时释放 */
gpio_free(S5PV210_GPA0(3));

从上面的操作函数中,可以看到,gpio-lib仅支持设置gpio为输入或者输出,但要知道,有些gpio是可以复用成其他功能的(例如摄像头接口的gpio等),下面是从S5PV210的datasheet中截图的

可以看到GPE0-0不仅可以设置为Input(输入模式)和Output(输出模式),还可以复用为CAM_A_PCLK(摄像头接口A的像素时钟引脚)

那么如果没有其他的支持,我们就需要使用通用的方式来配置寄存器,为此,三星平台再提供了一套操作gpio的函数

三星平台提供的gpio-cfg
文件位于archarmplat-samsungincludeplatgpio-cfg.h

其中的操作函数分为三类,第一类配置gpio,第二类设置gpio引脚状态,第三类设置gpio的驱动强度

配置gpio

#define S3C_GPIO_INPUT (S3C_GPIO_SPECIAL(0)) //输入
#define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1)) //输出
#define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x)) //复用功能

int s3c_gpio_cfgpin(unsigned int pin, unsigned int to); //配置
unsigned s3c_gpio_getcfg(unsigned int pin); //获取配置

例如上述例子,如果要设置GPE0-0为摄像头接口A的像素时钟引脚,那么就这么配置

s3c_gpio_cfgpin(S5PV210_GPE0(0), S3C_GPIO_SFN(2));

设置引脚状态

#define S3C_GPIO_PULL_NONE ((__force s3c_gpio_pull_t)0x00) //悬空
#define S3C_GPIO_PULL_DOWN ((__force s3c_gpio_pull_t)0x01) //下拉
#define S3C_GPIO_PULL_UP ((__force s3c_gpio_pull_t)0x02) //上拉

int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull);
s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin);

设置引脚驱动强度

#define S5P_GPIO_DRVSTR_LV1 ((__force s5p_gpio_drvstr_t)0x00)
#define S5P_GPIO_DRVSTR_LV2 ((__force s5p_gpio_drvstr_t)0x01)
#define S5P_GPIO_DRVSTR_LV3 ((__force s5p_gpio_drvstr_t)0x10)
#define S5P_GPIO_DRVSTR_LV4 ((__force s5p_gpio_drvstr_t)0x11)

s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin);
s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr);

三、总结
使用通用的方法可以设置任意寄存器,只是比较麻烦
使用gpiolib可以较为方便地配置gpio,但仅限于gpio,如果需要设置非gpio,还是需要使用通用地方法
内核提供的gpiolib只能配置gpio为输入或者输出,其他功能(例如复用引脚,设置上下拉等)无法通过gpiolib设置,三星平台提供了另一套函数设置
————————————————
版权声明:本文为CSDN博主「JT同学」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42462202/article/details/99949396

Always Believe Something Beauitful Will Be Happen
原文地址:https://www.cnblogs.com/Oude/p/12455803.html