Linux 对于 gpio 口常用操作,提供了一套很便捷的驱动 API, 开发者不需要自己去映射寄存器,只需要调用内核提供的标准 API 函数即可完成对 gpio 口的方向配置,电平设置,状态读取,转换为外部中断编号等操作。
(1) 读取 gpio 电平状态:int gpio_get_value(unsigned int gpio);
返回值: 成功:0/1 失败:<0
(2) 设置gpio电 平状态:void gpio_set_value(unsigned int gpio, int value);
(3)申请gpio:int gpio_request(unsigned gpio, const char *label);
返回值: 成功:0 失败:<0
(4)释放gpio: void gpio_free(unsigned gpio);
(5)配置gpio输入方向:int gpio_direction_input(unsigned gpio);
返回值: 成功: 0 失败:<0
(6)配置gpio输出方向:int gpio_direction_output(unsigned gpio, int value);
返回值: 成功: 0 失败:<0
(7)申请一个gpio:int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
返回值: 成功: 失败:<0
(8) 申请一组gpio: int gpio_request_array(const struct gpio *array, size_t num);
返回值: 成功: 失败:<0
(9) 释放一组gpio:void gpio_free_array(const struct gpio *array, size_t num);
(10)gpio 编号转换外部中断编号:int gpio_to_irq(unsigned gpio);
返回值: 成功:>=0 失败:<0
(11)外部中断编号转为gpio:int irq_to_gpio(unsigned irq);
返回值: 成功:>=0 失败:<0
1.模块初始化函数: 申请杂项设备、 GPIO。将GPIO置为输出方向,输出高电平,让 LED 处于灭状态。
2.模块的卸载函数: 释放 GPIO与注销杂项设备。
3.文件操作方法的 write 函数: 控制 gpio 电平输出。
4.文件操作方法的 read 函数: 打印“ hello!” 。
功能:绿灯亮10s—打印“ hello!”(根据开发板原理图:绿灯io口为:GPIOB 7)
(1)gpio.c
#include
#include
#include
#include
#include
#include
#include
#define LED_GREEN GPIOB(7)
#define KBUF_SIZE (1024)
#define MIN(x, y) ((x)<(y)?(x):(y))
char kbuf[KBUF_SIZE]; //存储用户写入的数据。
char *rbuf="hello!"; //用户读取的数据
int gpio_open (struct inode *node, struct file *fp)
{
return 0;
}
int gpio_release (struct inode *node, struct file *fp)
{
return 0;
}
ssize_t gpio_read (struct file *fp, char __user *ubuff, size_t size, loff_t *offset)
{
int ret;
ret=copy_to_user(ubuff, rbuf, strlen(rbuf));
if(ret != 0) {
pr_err("copy_to_user error\r\n");
return -EINVAL;
}
return strlen(rbuf);
}
ssize_t gpio_write(struct file *fp, const char __user *ubuf, size_t size, loff_t *offset)
{
int ret;
memset(kbuf, 0, sizeof(kbuf));
ret = copy_from_user(kbuf, ubuf, MIN(KBUF_SIZE, size));
if(ret != 0) {
pr_err("copy_from_user error\r\n");
return -EINVAL;
}
if( strstr(kbuf, "LED_GREEN_ON") ) gpio_set_value(LED_GREEN, 0);
else if(strstr(kbuf, "LED_GREEN_OFF")) gpio_set_value(LED_GREEN, 1);
return MIN(KBUF_SIZE, size); //返回写入的字节数
}
const struct file_operations gpio_fops={
.owner=THIS_MODULE, //文件操作集的拥有者是 本模块。
.read= gpio_read,
.write= gpio_write,
.open= gpio_open,
.release= gpio_release,
};
struct miscdevice mygpio={
.minor=MISC_DYNAMIC_MINOR, //系统自动分配次设备号
.name ="my_gpio",
.fops=&gpio_fops,
};
static int __init gpio_init(void)
{
int ret;
ret=misc_register(&mygpio);
if(ret!=0){
pr_err("misc_register error");
return -EINVAL; //返回错误编码
}
ret = gpio_request(LED_GREEN, "LED_GREEN");
if(ret != 0) {
pr_err("gpio_request error\r\n");
return -1;
}
//配置gpio的方向、无效电平
gpio_direction_output(LED_GREEN, 1);
return 0;
}
static void __exit gpio_exit(void)
{
gpio_free(LED_GREEN); //释放一个gpio
misc_deregister(&mygpio); //从内核注销一个杂项设备
}
module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");
(2)app.c
#include
#include
#include
#include
#include
#include
int main(int argc, char **arg)
{
int fd;
char data[64];
fd = open("/dev/my_gpio", O_RDWR);
if(fd < 0) {
perror("open error");
return -1;
}
write(fd, "LED_GREEN_ON", sizeof("LED_GREEN_ON"));
sleep(10);
write(fd, "LED_GREEN_OFF", sizeof("LED_GREEN_OFF"));
read(fd,data,strlen(data));
printf("%s\n",data);
close(fd);
return 0;
}
(3)Makefile
#多个c文件生成一个ko文件。
obj-m +=gpio.o #把.c文件的名字加.o写到这里
# KDIR 内核源码路径,根据自己需要设置
KDIR:=/home/qjl/work/lichee/linux-3.10
all:
#ARCH: 指当前编译的驱动模块的架构
#CROSS_COMPILE:指明交叉编译器的前缀
#-C: 指定去$(KDIR)目录下执行Makefile
#M:告知Makefile,需要的编译文件在哪
#modules: 这个规则是用于编译驱动模块的
@make ARCH=arm64 CROSS_COMPILE=aarch64-linux- -C $(KDIR) M=$(PWD) modules
@rm -fr .tmp_versions *.o *.mod.o *.mod.c *.bak *.symvers *.markers *.unsigned *.order *~ .*.*.cmd .*.*.*.cmd
clean:
@make ARCH=arm64 CROSS_COMPILE=aarch64-linux- -C $(KDIR) M=$(PWD) modules clean
@rm -rf *.ko
实现功能:绿灯亮5s—红灯亮5s—蜂鸣器响1s。
(1)gpio.c
#include
#include
#include
#include
#include
#include
#include
#define LED_GREEN GPIOB(7)
#define BEEP GPIOG(13)
#define LED_RED GPIOB(6)
#define KBUF_SIZE (1024)
#define MIN(x, y) ((x)<(y)?(x):(y))
char kbuf[KBUF_SIZE];
int gpio_open (struct inode *node, struct file *fp)
{
return 0;
}
int gpio_release (struct inode *node, struct file *fp)
{
return 0;
}
ssize_t gpio_read (struct file *fp, char __user *ubuff, size_t size, loff_t *offset)
{
return 0;
}
ssize_t gpio_write(struct file *fp, const char __user *ubuf, size_t size, loff_t *offset)
{
int ret;
memset(kbuf, 0, sizeof(kbuf));
ret = copy_from_user(kbuf, ubuf, MIN(KBUF_SIZE, size));
if(ret != 0) {
pr_err("copy_from_user error\r\n");
return -EINVAL;
}
//原理图中,beep高电平响,led低电平亮
if( strstr(kbuf, "LED_GREEN_ON") ) gpio_set_value(LED_GREEN, 0);
else if(strstr(kbuf, "LED_GREEN_OFF")) gpio_set_value(LED_GREEN, 1);
else if(strstr(kbuf, "BEEP_ON")) gpio_set_value(BEEP, 1);
else if(strstr(kbuf, "BEEP_OFF")) gpio_set_value(BEEP, 0);
else if(strstr(kbuf, "LED_RED_ON")) gpio_set_value(LED_RED, 0);
else if(strstr(kbuf, "LED_RED_OFF")) gpio_set_value(LED_RED, 1);
return MIN(KBUF_SIZE, size); //返回写入的字节数
}
const struct file_operations gpio_fops={
.owner=THIS_MODULE, //文件操作集的拥有者是 本模块。
.read= gpio_read,
.write= gpio_write,
.open= gpio_open,
.release= gpio_release,
};
struct miscdevice mygpio={
.minor=MISC_DYNAMIC_MINOR, //系统自动分配次设备号
.name ="my_gpio",
.fops=&gpio_fops,
};
//定义一组gpio
const struct gpio gpio_array[] = {
{LED_RED, GPIOF_OUT_INIT_HIGH, "LED_RED"}, //gpio端口、初始电平值、端口描述(任意)
{LED_GREEN, GPIOF_OUT_INIT_HIGH, "LED_GREEN"},
{BEEP, GPIOF_OUT_INIT_LOW, "BEEP"},
};
static int __init gpio_init(void)
{
int ret;
ret=misc_register(&mygpio);
if(ret!=0){
pr_err("misc_register error");
return -EINVAL; //返回错误编码
}
//先释放一次,不然申请会失败,因为有的gpio被其他的驱动申请过了
gpio_free_array(gpio_array, sizeof(gpio_array)/sizeof(gpio_array[0]));
//申请一组gpio
ret = gpio_request_array(gpio_array,sizeof(gpio_array)/sizeof(gpio_array[0]));
if(ret != 0) {
pr_err("gpio_request_array error\r\n");
goto error; //如果失败跳到error去。
}
return 0;
error:
//某一步出错后,要把他前面申请的资源释放掉,不然驱动再次安装时就会失败
misc_deregister(&mygpio);
return -1;
}
static void __exit gpio_exit(void)
{
gpio_free_array(gpio_array, sizeof(gpio_array)/sizeof(gpio_array[0]));
misc_deregister(&mygpio); //从内核注销一个杂项设备
}
module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");
(2)app.c
#include
#include
#include
#include
#include
#include
int main(int argc, char **arg)
{
int fd;
fd = open("/dev/my_gpio", O_RDWR);
if(fd < 0) {
perror("open error");
return -1;
}
write(fd, "LED_GREEN_ON", sizeof("LED_GREEN_ON"));
sleep(5);
write(fd, "LED_GREEN_OFF", sizeof("LED_GREEN_OFF"));
write(fd, "LED_RED_ON", sizeof("LED_RED_ON"));
sleep(5);
write(fd, "LED_RED_OFF", sizeof("LED_RED_OFF"));
write(fd, " BEEP_ON", sizeof(" BEEP_ON"));
sleep(1);
write(fd, " BEEP_OFF", sizeof(" BEEP_OFF"));
close(fd);
return 0;
}
(3)Makefile
文件与示例一相同。