基于基本框架简单实现树莓派IO口驱动代码的编写

1. 驱动代码实现:

#include <linux/fs.h>            //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>        //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件


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避免地址因编译器的优化而省略,同时也要求每次编译时在原始内存中读值,而不是寄存器中的备份数据,提高时效性
volatile unsigned int *GPSET0  = NULL;
volatile unsigned int *GPCLR0  = NULL;


//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
        printk("pin4_open\n");  //内核的打印函数和printf类似
        //配置pin4引脚为输出引脚,将bit 14,13,12配置为001
        *GPFSEL0 &= ~(0x6 << 12);  //将bit 14,13置为00, 这里包括下一步通过与或的操作是为了不影响其他位的设置
        *GPFSEL0 |= (0x1 << 12);   //将bit 12置1

        return 0;
}

//led_read函数
static int pin4_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
        printk("pin4_read\n");

        return 0;
}

//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
        int userCmd;
        printk("pin4_write\n");
        //从上层获得write函数的值
        copy_from_user(&userCmd, buf, count);
        //根据获取到的上层写入值来设置IO口引脚高低电平
        printk("get data\n");
        if (userCmd == 1){
                *GPSET0 |= (0x1 << 4);
                printk("pin4 sets 1\n");
        }
        else if (userCmd == 0){
                *GPCLR0 |= (0x1 << 4);
                printk("pin4 sets 0\n");
        }
        else {
                printk("set faliuer\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);  //创建设备文件

        GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000, 4);  //用ioremap将物理地址转化为虚拟地址,将io口寄存器映射成普通内存单元进行访问
        GPSET0  = (volatile unsigned int *)ioremap(0x3f20001C, 4);
        GPCLR0  = (volatile unsigned int *)ioremap(0x3f200028, 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);  //,注意这个不是函数调用,它是一个宏,是加载内核的入口,内核加载驱动的时候,这个宏会被调用,即pin4_drv_init()这个函数会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

2. 上层测试代码实现:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
        int fd;
        int n_write;
        int cmd;

        fd = open("/dev/pin4", O_RDWR);
        if (fd == -1){
                printf("open /dev/pin4 failuer\n");
                perror("failuer reason");
        }
        else {
                printf("fd = %d\n", fd);
                printf("open /dev/pin4 success\n");
        }
        printf("Please input 1/0 to select high/low voltage in pin4\n");
        scanf("%d", &cmd);
        if (cmd == 1){
                printf("cmd = %d\n", cmd);
                n_write = write(fd, &cmd, 4);  //注意这里len=4不是1,因为cmd是int型占4个字节

        }
        else if (cmd == 0){
                printf("cmd = %d\n", cmd);
                n_write = write(fd, &cmd, 4);

        }
        else {
                printf("Wrong Input\n");
        }

        return 0;
}
~    

3. 实现过程:
(1)对内核进行编译:

ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 modules

在这里插入图片描述同样的将生成的pin4Driver2.ko驱动文件scp到树莓派上,树莓派上将先前的pin4Driver2驱动卸载了,即sudo rmmod pin4Driver2 ,再sudo insmod pin4Driver2.ko 加载驱动,最后也是很关键的一步,即sudo chmod 666 /dev/pin4 更改/dev/pin4访问权限后驱动才能运行成功。

(2)将上层测试文件交叉编译后scp到树莓派上,即arm-linux-gnueabihf-gcc pin4Test.c -o pin4Testscp pin4Test pi@172.20.10.6:/home/pi

(3)在树莓派上运行pin4Test,并打开树莓派gpio(gpio readall):
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述最后再通过dmesg查看内核打印信息:
在这里插入图片描述当然,我们还可以实现上层read以及其它引脚电平的配置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值