香橙派(orangePiZero2)二:驱动开发,编写代码控制IO口的电平高低

本文介绍了如何查阅芯片手册以获取GPIO信息,以及如何编写Linux驱动程序pin5driver.c进行IO操作,包括设置GPIO为输入输出模式,通过ioremap映射物理地址并实现读写控制。最后展示了驱动的调试过程和应用测试结果。
摘要由CSDN通过智能技术生成

一、查看芯片手册

去官网下载芯片手册,查看手册。

1、从芯片手册可以看到,GPIO的基地址(0x0300B000)

2、从芯片手册上可以看到 PC 的偏移量(0x0048)

        GPIOx 的地址就是 GPIO基地址 + GPIOx的偏移量,所以 GPIOPC 的地址为: 0x0300B000 + 0x0048 = 0x0300B048 。

3、找到我们需要修改控制的端口信息:

4、通过手册,去了解如何去操作GPIO的数据位:

        如果将端口配置为输入或者输出,那么引脚的状态就与相应的位相同。读取的位值是由软件设置的值。

        如果将端口配置为功能引脚,那么将会读取未定义的值。

二、编写代码进行IO操作

编写驱动代码 pin5driver.c :

#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 device 声明
#include <linux/uaccess.h>          //copy_from_user的头文件
#include <linux/types.h>            //设备号dev_t类型声明
#include <asm/io.h>                 //ioremap iounmap的头文件


static struct class *pin5_class;
static struct device *pin5_class_dev;

static dev_t devno;                //设备号
static int major =231;             //主设备号
static int minor =0;               //次设备号
static char *module_name="pin5";   //模块名

//volatile 作用:不会因为编译器的优化而省略,且要求每次直接读取
volatile unsigned int* GPIOBASE =  NULL;
volatile unsigned int* GPIOPC   =  NULL;
volatile unsigned int* GPIODAT  =  NULL;


        
//ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
static ssize_t pin5_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
        printk("pin5_read\n");
        return 0;
}

static int pin5_open(struct inode *inode,struct file *file)
{

        printk("pin5_open\n");      //内核打印函数,和printf类似

        //把bit22~bit20 配置成001 ,为输出模式
        *GPIOPC &= ~(0x6 << 20);   //把bit22和bit21配置为0
        *GPIOPC |= (0x1 << 20);    //把bit20配置为1

        return 0;
}

static ssize_t pin5_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
        char user_cmd;

        printk("pin5_write\n");

        //获取用户空间write的值
        copy_from_user(&user_cmd,buf,count);

        //根据值来操控io口,就是往相关的IO输出高电平或者低电平
        if(user_cmd == '1'){
                *GPIODAT |= 0x01 << 5;
                printk("pin5_set\n");
        }else if(user_cmd == '0'){
                *GPIODAT &= ~(0x01 << 5);
                printk("pin5_reset\n");
        }else{
                printk("undo\n");
        }
        
        return 0;
}

static struct file_operations pin5_fops = {
        .owner = THIS_MODULE,
        .open  = pin5_open,
        .write = pin5_write,
        .read  = pin5_read,
};

static int  pin5_drv_init(void)
{
        int ret;
    
        printk("insmod driver pin5 success...\n");    
    
        devno = MKDEV(major,minor);             //创建设备号
        ret   = register_chrdev(major, module_name,&pin5_fops);         //注册驱动 告诉内核 把这个驱动加入到>内核链表当中

        pin5_class=class_create(THIS_MODULE,"myfirstdemo");             //让代码在dev自动生成设备
        pin5_class_dev =device_create(pin5_class,NULL,devno,NULL,module_name);          //创建设备文件


        /***********************************************************
        * (1)0x300B000、0x0300B048、0x0300B058 这些均为 物理地址
        * (2)需要通过 ioremap 函数 将 物理地址 映射为 虚拟地址
        * (3)把IO口的寄存器 映射成 普通的内存单元 进行访问
        ************************************************************/

        /************************映射虚拟地址************************/
        
        //GPIO基地址
        GPIOBASE =  (volatile unsigned int *)ioremap(0x0300B000,4);
        
        //GPIOPC地址
        GPIOPC   =  (volatile unsigned int *)ioremap(0x0300B048,4);
        
        //GPIO数据地址
        GPIODAT  =  (volatile unsigned int *)ioremap(0x0300B058,4);
        
        /***********************************************************/
    
        return 0;
}

static void pin5_drv_exit(void)
{
        //解除映射
        iounmap(GPIOBASE);
        iounmap(GPIOPC);
        iounmap(GPIODAT);

        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");
MODULE_AUTHOR("SHUN-GE");

三、驱动代码调试

1、首先,我们可以看到我们写好的驱动代码:

2、修改Makefile文件,添加如下内容:

3、然后我们需要回到内核的源码目录下,进行模块的编译,即 orangepi-build-main/kernel/orange-pi-4.9-sun50iw9 这个路径下:

输入以下命令,编译成模块:

make modules ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu-

编译通过如下所示:

4、将生成的 pin5driver.ko 模块 ,拷贝到开发板中:

5、在开发板中加载驱动模块:

输入以下命令即可:

sudo insmod pin5driver.ko

6、查看一下是否生成了pin5这个设备驱动:

输入以下命令:

ls /dev/pin5 -l

结果如下所示:

7、修改pin5驱动的用户权限:

输入如下命令:

sudo chmod 666 /dev/pin5

查看结果:

8、写一个用于测试的应用程序:

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

int main()
{
    int fd;
    int cmd;
    fd = open("/dev/pin5",O_RDWR);
    if(fd < 0){
        printf("open fail\n");
    }else {
        printf("open success\n");
    }
    printf("input command: 1/0\n");
    printf("1: set pin5 high\n");
    printf("0: set pin5 low\n");
    scanf("%d",&cmd);
    if(cmd == 1){
        fd = write(fd,"1",1);
        printf("%d=cmd \n",cmd);
    }else if(cmd == 0){
        fd = write(fd,"0",1);
        printf("%d=cmd \n",cmd);
    }
}

7、测试结果:

(1)PC5 为 OUT 模式,输出电平为1:

(2)PC5 为 OUT 模式,输出电平为0:

(3)用 dmesg 命令查看内核层 的 驱动程序打印的信息:

以上,就完成了IO口的驱动开发。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值