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