树莓派4b的基地址为0xfe00 0000,因此基地址加上偏移地址得到物理地址为0xfe20 000 而树莓派3b的基地址为0x3f00 0000,此处容易混淆
操作I/O口输入、输出0和输入、输出1,根据芯片手册,需要操作三个寄存器 功能选择寄存器:GPFSEL0 选择I/O口及I/O口功能 输出设置寄存器:GPSET0 选择I/O口输出1 输出清除寄存器:GPCLR0 选择I/O口输出0
功能选择寄存器(GPFSELn) 由芯片手册得知,GPFSELn (n= 0时控制引脚09,n=1时控制引脚1019,以此类推)为功能选择寄存器,功能选择寄存器用于定义通用 I/O 引脚的操作,有32位,每3位为1一组进行配置一个引脚,如果想要配置引脚4为输出,则应该让GPFSELn = 为GPFSEL0且配置bit 14-12 为001,其他位保持不变。
输出设置寄存器(GPSETn) 由芯片手册得知,GPFSETn (n= 0时控制引脚031,n=1时控制引脚3257)为输出设置寄存器,输出设置寄存器用于定义通用 I/O 引脚的操作,有32位,每1位配置一个引脚,如果想要配置引脚4输出1,则应该让GPFSETn = 为GPFSET0且配置bit 4为1,其他位保持不变。
输出清除寄存器(GPCLRn) 由芯片手册得知,GPCLRn (n= 0时控制引脚031,n=1时控制引脚3257)为输出设置寄存器,输出设置寄存器用于定义通用 I/O 引脚的操作,有32位,每1位配置一个引脚,如果想要配置引脚4输出1,则应该让GPCLRn = 为GPCLR0且配置bit 4为1,其他位保持不变。
volatile关键字 防止寄存器变量被编译器优化,要求每次直接从寄存器读值
上层代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main()
{
int fd;
int cmd;
int data;
fd = open("/dev/pin4",O_RDWR);
if(fd < 0)
{
printf("open failed\n");
}else{
printf("open success\n");
}
printf("please input cmd:1/0\n1:set pin4 high\n0:set pin4 low\n");
scanf("%d",&cmd);
if(cmd == 1)
{
data = 1;
}
else
{
data = 0;
}
printf("data = %d\n",data);
fd = write(fd,&data,4);
return 0;
}
完整驱动代码
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>
static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno;
static int major =231;
static int minor =0;
static char *module_name="pin4";
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;
static int pin4_read (struct file *file,char __user *buf,size_t size,loff_t *ppos)
{
printk("pin4_read\n");
return 0;
}
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n");
*GPFSEL0 &= ~(0x06 << 12);
*GPFSEL0 |= (0x06 << 12);
return 0;
}
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
int usercmd;
printk("pin4_write\n");
copy_from_user(&usercmd,buf,count);
if(usercmd == 1)
{
printk("set 1\n");
*GPSET0 |= (0x01 << 4);
}
else if (usercmd == 0)
{
printk("set 0\n");
*GPCLR0 |= (0x01 << 4);
}
else
{
printk("no\n");
}
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
.read = pin4_read,
};
int __init pin4_drv_init(void)
{
int ret;
devno = MKDEV(major,minor);
ret = register_chrdev(major, module_name,&pin4_fops);
pin4_class=class_create(THIS_MODULE,"myfirstdemo");
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);
GPFSEL0 = (volatile unsigned int*)ioremap(0xfe200000,4);
GPSET0 = (volatile unsigned int*)ioremap(0xfe20001c,4);
GPCLR0 = (volatile unsigned int*)ioremap(0xfe200028,4);
return 0;
}
void __exit pin4_drv_exit(void)
{
iounmap(GPFSEL0);
iounmap(GPSET0);
iounmap(GPCLR0);
device_destroy(pin4_class,devno);
class_destroy(pin4_class);
unregister_chrdev(major, module_name);
}
module_init(pin4_drv_init);
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
测试结果: 打开成功 dmesg查看内核打印信息 查看pin4的状态,可看出已设置成输出模式,低电平状态 查看pin4的状态,可看出已设置成输出模式,高电平状态 驱动代码测试完毕
|