树莓派基于IO口编写驱动

IO口操作

GPFSEL0 GPIO Function Select 0 功能选择输出/输入 14-12是pin4的io口,001是输出 ,000是输入 GPSET0 GPIO Pin Output Set 0 输出0 GPSET1 GPIO Pin Output Set 1 输出1 GPCLR0 GPIO Pin Output Clear 0 清0

volatile的作用:防止编译器对代码优化,调用这个函数告诉编译器我不需要优化,变量值是直接从变量地址中读取和存储的

copy_from_user(void *cmd, const void __user *from, unsigned long n) //获取应用层的数据拷贝到内核

  • cmd:将数据拷贝到内核的地址

  • *from :用户的数据

  • n :拷贝数据的长度(字节)

  • 也就是将form地址中的数据拷贝到cmd地址中去,拷贝长度是n

驱动IO口输入/出代码


#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 unsigned int *GPSET0 = NULL;
volatile unsigned int *GPCLR0 = NULL;

//这三行是设置寄存器的地址,volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.

//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
        printk("pin4_open\n");  //内核的打印函数
        *GPFSEL0 &=~(0x6<<12);//6的二进制是110左移12位后,110对应的位置是14-12,取反后110变为001其它位为1,和GPFSEL0进行与运算后就实现只有14、13位改变为0
        *GPFSEL0 |=(0x1<<12);//把bit14,bit13配置成0 //把位置12变为1
        //配置pin4引脚为输出引脚,bit 12~14配置成100
        return 0;
}
//read函数
static int pin4_read(struct file *file,char __user *buf,size_t count,loff_t *ppos)
{
        printk("pin4_read\n");  //内核的打印函数
                //可以用copy_to_user()函数读取引脚
        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口,高电平或者低电平
        if (usercmd==1)
        {
                printk("set 1\n");
                *GPSET0 |=(0x1 <<4); //通常pin4用1左移4位进行或运算
        }
        else if(usercmd==0){
                printk("set 0\n");
                * GPCLR0 |=(0x1<<4);
        }
        else{
                printk("do nothing\n");
        }
        return 0;
}
static struct file_operations pin4_fops = {

        .owner = THIS_MODULE,
        .open  = pin4_open,
        .write = pin4_write,
        .read  = pin4_read,
};
//static限定这个结构体的作用,仅仅只在这个文件。
int __init pin4_drv_init(void)   //真实的驱动入口
{

        int ret;
                printk("ismod pin4 successed\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);//物理地址转换成虚拟地址,io口寄存器映射成普通内存单元进行访问
        GPSET0 =(volatile unsigned int *)ioremap(0x3f20001C,4);//ioremap函数将物理地址转换成虚拟地址,io口寄存器映射成普通内存单元进行访问
        GPCLR0 =(volatile unsigned int *)ioremap(0x3f200028,4);
        //这三行是设置寄存器的地址,volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,
                //且要求每次直接读值.ioremap函数将物理地址转换为虚拟地址,IO口寄存器映射成普通内存单元进行访问。
                //ioremap函数第一个参数输物理地址,第二个参数是


        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");                            
应用层代码:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
​
int main()
{
        int fd;
        int cmd;
​
        fd=open("/dev/pin4",O_RDWR);
        if(fd<0){
                printf("open error!\n");
                perror("reson:");
        }else{
                printf("open success\n");
        }
        printf("input cmd : 1/0 \n");
        scanf("%d",&cmd);
​
        write(fd,&cmd,4);
        
        return 0;
}

效果:

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Iverson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值