我们的目标是在驱动代码中直接操作某一个GPIO引脚的数据寄存器和方向寄存器,然后点亮led灯。
1 在TinkerBoard 开发板 上执行:
sudo cat /proc/iomem | grep gpio
有以下输出:
ff720000-ff7200ff : /pinctrl/gpio0@ff720000
ff730000-ff7300ff : /pinctrl/gpio1@ff730000
ff780000-ff7800ff : /pinctrl/gpio2@ff780000
ff788000-ff7880ff : /pinctrl/gpio3@ff788000
ff790000-ff7900ff : /pinctrl/gpio4@ff790000
显示五组GPIO的物理起始地址。
rk3399的每一组GPIO的编号按照顺序为A0-A7, B0-B7, C0-C7, D0-D7。
另外查看rk3399 手册 第20章 ,得知每一组数据寄存器和方向寄存器偏移分别为
GPIO_SWPORTA_DR : 0x0000
GPIO_SWPORTA_DDR : 0x0004
Led正极接TinkerBoard物理21号引脚。
Led负极接TinkerBoard的任意一个地引脚。
查看上图得知物理21号引脚是 GPIO_1_A7 ,从本文开头我们得知GPIO1 的起始物理地址为 0xff730000,
则数据寄存器的地址就是:0xff730000 + 0x0000
方向寄存器的地址就是:0xff730000 + 0x0004,
我们现在是要将Led点亮,那就要设置方向寄存器的第8位为1(从A0,A1一直到A7),数据寄存器的第八位为1,
以下是代码:
unsigned int __iomem *data = ioremap(GPIO1_SWPORTA_DR, 4);
unsigned int __iomem *direction = ioremap(GPIO1_SWPORTA_DDR, 4);
unsigned int a = readl(direction);
unsigned int d = readl(data);
writel(readl(direction) | (GPIO_1_A7 << 7), direction);
writel(readl(data) | (GPIO_1_A7 << 7), data);
注意设置寄存器的值的方法是先把寄存器的值取出来,然后把这个值的第8位设置为1,然后再写进去。
以下是全部代码,关于交叉编译,可以看之前文章.
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/init.h> /* Needed for the macros */
#include <linux/kernel.h> /* Needed for pr_info() */
#include <linux/module.h> /* Needed by all modules */
//sudo mknod /dev/mycdev001 c 403 0
/**
*
* need to get major number from /proc/devices
* then
* sudo mknod /dev/t01 c 238 0
*
*
*
*
sudo cat /proc/iomem
ff720000-ff7200ff : /pinctrl/gpio0@ff720000
ff730000-ff7300ff : /pinctrl/gpio1@ff730000
ff780000-ff7800ff : /pinctrl/gpio2@ff780000
ff788000-ff7880ff : /pinctrl/gpio3@ff788000
ff790000-ff7900ff : /pinctrl/gpio4@ff790000
RK3399 has 5 groups of GPIO banks: GPIO0~GPIO4, and each group is distinguished by numbers A0~A7, B0~B7, C0~C7, D0~D7.
地址:
GPIO0 : FF72_0000
GPIO1 : FF73_0000
GPIO2 : FF78_0000
GPIO3 : FF78_8000
GPIO4 : FF79_0000
we use GPIO_1_A7 pin
Offset
GPIO_SWPORTA_DR : 0x0000
GPIO_SWPORTA_DDR : 0x0004
*
*/
#define MY_MAJOR 499
#define GPIO_1_BASE 0xFF730000
#define GPIO1_SWPORTA_DR GPIO_1_BASE
#define GPIO1_SWPORTA_DDR 0xFF730004
#define LOCATION 7
#define MY_MAX_MINORS 2
#define DEVICE_NAME "device name 499"
struct my_device_data {
struct cdev cdev;
char *desc;
};
struct my_device_data devs[MY_MAX_MINORS];
static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
pr_info("my_ioctl,cmd : %d\n",cmd);
switch (cmd) {
// on
case 0: {
unsigned int __iomem *data = ioremap(GPIO1_SWPORTA_DR, 4);
unsigned int __iomem *direction = ioremap(GPIO1_SWPORTA_DDR, 4);
writel(readl(direction) | (0x1 << LOCATION), direction);
writel(readl(data) | (0x1 << LOCATION), data);
break;
}
// off
case 1: {
unsigned int __iomem *data = ioremap(GPIO1_SWPORTA_DR, 4);
unsigned int __iomem *direction = ioremap(GPIO1_SWPORTA_DDR, 4);
writel(readl(direction) & ( ~(0x1 << LOCATION)), direction);
writel(readl(data) & (~(0x1 << LOCATION)), data);
break;
}
}
return 0;
}
const struct file_operations my_fops = { .owner = THIS_MODULE,
.unlocked_ioctl = my_ioctl
};
static dev_t mydev;
static __init int my_init(void) {
int err;
int i;
pr_info("a3 init_module\n");
err = alloc_chrdev_region(&mydev, 0, MY_MAX_MINORS, DEVICE_NAME);
if (err != 0) {
return err;
}
for (i = 0; i < MY_MAX_MINORS; i++) {
/* initialize devs[i] fields */
cdev_init(&devs[i].cdev, &my_fops);
cdev_add(&devs[i].cdev, MKDEV(MAJOR(mydev), i), 1);
}
return 0;
}
static void __exit my_exit(void) {
int i;
pr_info("a3 cleanup_module\n");
for (i = 0; i < MY_MAX_MINORS; i++) {
/* release devs[i] fields */
cdev_del(&devs[i].cdev);
}
unregister_chrdev_region(mydev, MY_MAX_MINORS);
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
MODULE_DESCRIPTION("A sample c driver");
下面是测试程序:
#include <stdint.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <poll.h>
void* ioctl01(void *ptr) {
int fd = open("/dev/t01", O_RDWR, O_NONBLOCK);
while(1){
//led on
ioctl(fd, 0, 100);
sleep(2);
//led off
ioctl(fd, 1, 100);
sleep(2);
}
close(fd);
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, ioctl01, NULL);
sleep(2);
pthread_join(thread, NULL);
printf("main exit\n");
return EXIT_SUCCESS;
}
欢迎评论。