树莓派IO操控代码编程

我们在编写驱动程序的时候,IO空间的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,映射到虚拟地址上。
特别注意,BCM2708 和BCM2709 IO起始地址不同,BCM2708是0x20000000,BCM2709是0x3f000000,这是造成大部分人驱动出现“段错误”的原因。树莓派3B的CPU为BCM2709。

驱动代码:

#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 count,loff_t *ppos)
{
        printk("pin4_read\n");				//内核打印函数,和printf类似
        return 0;
}

static int pin4_open(struct inode *inode,struct file *file)
{
        printk("pin4_open\n");
        //配置pin4引脚为输出引脚,bit为12-14配置成001
        *GPFSEL0 &= ~(0x6 << 12);		//把13,14 位配置成00
        *GPFSEL0 |= (0x1 << 12);			//把12 位配置成1
        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);		//获取上层write函数的值
		//根据值来操作IO口,高电平或低电平
        if(userCmd == 1) {
                printk("set 1\n");
                *GPSET0 |= 0x1 << 4;			//高电平
        } else if(userCmd == 0) {
                printk("set 0\n");
                *GPCLR0 |= 0x1 << 4;			//低电平
        } else {
                printk("undo\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;
        printk("insmod driver pin4 success\n");
        devno = MKDEV(major,minor);					//创建设备号
        ret = register_chrdev(major,module_name,&pin4_fops);//注册驱动,告诉内核,把这个驱动加入到内核的链表中
        pin4_class = class_create(THIS_MODULE,"myfirstdemo");		//让代码在dev下自动生成设备
        pin4_class_dev = device_create(pin4_class,NULL,devno,NULL,module_name);		//创建设备文件

// void _iomem *ioremap(resource_size_t res_cookie,size_t size){
//      return arch_ioremap_caller(res_cookie,size,MT_DEVICE,_builtin_return_address(0));
// }


GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);			//将一个IO地址空间映射到内核的虚拟地址空间上去
GPSET0 = (volatile unsigned int *)ioremap(0x3f20001C,4);
GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4);


        return 0;
}

void __exit pin4_drv_exit(void)
{

// void iounmap(volatile void _iomem *addr)
// {
// }

        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");

测试代码:

#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");
                perror("reson:");

        } else {
                printf("open success\n");
        }
        printf("input commnd : 1/0 \n1:set pin4 high \n0:set pin4 low\n");
        scanf("%d",&cmd);
        if(cmd == 1) {
                data = 1;
        }
        if(cmd == 0) {
                data = 0;
        }
        printf("data = %d\n",data);
        fd = write(fd,&data,1);
        close(fd);
        return 0;
}

在虚拟机分别编译,将模块、测试程序拷贝至树莓派,插入模块后运行测试程序。

sudo insmod xxx.ko //向内核加载模块
sudo chmod 666 /dev/pin4
./pin4test

volatile //确保本条指令不会因编译器的优化而省略;且要求每次直接读值
lsmod //显示已经载入系统的模块
sudo rmmod xxx //卸载驱动模块

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值