GPFSEL0 GPIO Function Select 0: 功能选择 输入/输出
GPSET0 GPIO Pin Output Set 0 : 输出0
GPSET1 GPIO Pin Output Set 1 : 输出1
0 = No effect
1 = Set GPIO pin n
GPCLR0 GPIO Pin Output Clear 0: 清零
0 = No effect
1 = Clear GPIO pin n
GPCLR1 GPIO Pin Output Clear 1 :清1
每个寄存器都是32位的
GPFSEL0 0x3f200000
GPSET0 0x3f20001c
GPCLR0 0x3f200028
void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);
ioremap宏定义在asm/io.h内:
#define ioremap(cookie,size) __ioremap(cookie,size,0)
phys_addr:要映射的起始的IO地址
size:要映射的空间的大小
flags:要映射的IO空间和权限有关的标志
该函数返回映射后的内核虚拟地址(3G-4G). 接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源
#include // file_operations声明
#include // module_init module_exit声明
#include // __init __exit 宏定义声明
#include // class device声明
#include // copy_from_user的头文件
#include // 设备号 dev_t 类型声明
#include // ioremap iounmap 的头文件
static struct class *pin5_class;
static struct device *pin5_class_dev;
static dev_t devno; // 设备号
static int major = 233; // 主设备号
static int minor = 1; //次设备号
static char *module_name = "pin5"; //模块名
volatile unsigned int* GPFSEL0 = NULL; // volatile不会因编译器的优化而省略,每次直接读值
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
static int pin5_read (struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
printk("pin5_read\n"); // 内核的打印函数
return 0;
}
//pin5_open函数
static int pin5_open(struct inode *inode, struct file *file)
{
printk("pin5_open\n"); // 内核的打印函数,和printf类似
// 配置pin5引脚为输出引脚 ,需要把 bit 15~17 配置成001
*GPFSEL0 &= ~(0x6 << 15); // 0x6 0110 左移15位,取反后为1001,把bit 16、17配置成 0
*GPFSEL0 |= (0x1 << 15); // 把 第 15 位bit 配置成 1, 15~17为001输出 / 000输入,其中一工为0~31位
return 0;
}
//open_write函数
static ssize_t pin5_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int cmd;
printk("pin5_write\n");
// 获取上层write函数的值
copy_from_user(&cmd,buf,count); // 从上层获取函数的值第一个参数是一个char类型的指针const char __user *buf,用int也行
// 根据值来设定操作 io 口,高电频还是低电频
printk("get value\n");
if(cmd == 1)
{
*GPSET0 |= 0x1 << 5; // 把第 0 组 第5引脚置为 1
}
else if(cmd == 0)
{
*GPCLR0 |= 0x1 << 5; // 第 5位置1 让清零寄存器把第 5 引脚清零
}
else
{
printk("undo\n");
}
return 0;
}
static struct file_operations pin5_fops = {
.owner = THIS_MODULE,
.open = pin5_open,
.write = pin5_write,
.read = pin5_read,
};
int __init pin5_drv_init(void) //真实驱动入口
{
int ret;
printk("insmod driver pin5 success\n");
devno = MKDEV(major,minor); //2. 创建设备号
ret = register_chrdev(major , module_name, &pin5_fops); //3.注册驱动,告诉内核,把这个驱动加入到内核的链表中
pin5_class = class_create( THIS_MODULE, "myfirstdemo" ); // 让代码在dev自动生成设备
pin5_class_dev = device_create( pin5_class , NULL , devno , NULL ,module_name ); //创建设备文件
GPFSEL0 = ( volatile unsigned int *)ioremap(0x3f200000,4);// 第一个参数为物理地址,第二个参数为映射的大小
GPSET0 = ( volatile unsigned int *)ioremap(0x3f20001C,4);// 一般寄存器32位,4个字节
GPCLR0 = ( volatile unsigned int *)ioremap(0x3f200028,4);// 把物理地址 转换为 虚拟地址
//ioremap由于返回值是void*型需要强制转换 void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags)
return 0;
}
void __exit pin5_drv_exit(void)
{
iounmap(GPFSEL0); //iounmap函数用于取消ioremap()所做的映射
iounmap(GPSET0);
iounmap(GPCLR0);
device_destroy(pin5_class,devno);
class_destroy(pin5_class);
unregister_chrdev( major, module_name);// 卸载驱动
}
module_init(pin5_drv_init); // 驱动入口,驱动安装的时候,会调用这个宏
module_exit(pin5_drv_exit);
MODULE_LICENSE("GPL v2");
#include
#include
#include
#include
int main()
{
int fd;
int usecmd;
int data;
fd = open("/dev/pin5",O_RDWR);
if(fd < 0)
{
printf("open file failed\n");
perror("why : ");
}
else
{
printf("open success\n");
}
printf("请输入你的指令 : 1/0 , 1: pin5 HIGH , 0: pin5 LOW\n");
scanf("%d",&usecmd);
if(usecmd == 1)
{
data = 1;
printf("data = %d\n",data);
}
else if(usecmd == 0)
{
data = 0;
printf("data = %d\n",data);
}
fd = write(fd, &data,1);
}
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
scp pin5dirve.ko pi@192.168.3.43:/home/pi
arm-linux-gnueabihf-gcc pin5test.c -o pin5test
scp pin5test pi@192.168.3.43:/home/pi
sudo insmod pin5drive.ko // 安装驱动,要超级权限
lsmod // 可查看驱动是否安装
sudo chmod 666 /dev/pin5 // 666 为可读可写可操作权限
./pin5test
gpio readall