linux驱动学习之对树莓派IO操作代码编写

linux地址的概念

地址总线:是由CPU 或有DMA 能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/地方的实体位址。一个 16位元 宽度的位址总线到达 2 的 16 次方 = 65536 = 64 KB 的内存位址;但现在很多计算机内存已经大于4G(windows XP x32位系统最大只能识别3.29G,所以要使用4G以上大内存就要用windows x64位系统)。

物理地址:是指处理器芯片发出,来进行地址空间寻址的地址,它与处理器地址引脚上发出的电信号相对应。

虚拟地址:程序所操作地址都是虚拟地址,虚拟地址是硬件MMU与软件内存管理结合的产物。由物理地址映射而来。

树莓派IO口寄存器

使用命令可知树莓派的cpu是BCM2835

cat /proc/cpuinfo

芯片手册中的GPIO pin的编号,对应的是管脚表的BCM
在这里插入图片描述

GPFSEL 0-5寄存器

功能:GPIO Function Select 功能选择
数量:6个
GPFSEL 0-4:负责50个GPIO pins
GPFSEL 5:负责GPIO PIN50 -53
在这里插入图片描述

GPSET 0-1寄存器

功能:设置高电平,赋值1时开启寄存器,0不作用。复位后为0
数量:2个
在这里插入图片描述

GPCLR 0-1寄存器

功能:给输出置零,赋值1时开启寄存器,0不作用。复位后为0
数量:2个
在这里插入图片描述
其他的寄存器也是差不多这样,这里就不一 一介绍了。详细可看芯片手册

编程所操作的虚拟地址

树莓派官网给出的GPIO口的物理地址
我们发现 GPFSEL0寄存器ⅤC CPU总线地址是0x7E200000,相对基址偏移0x00200000那么ARM物理地址也是偏移这么多,其ARM 物理地址是0x3f200000=0x3f000000+0x00200000。同理, GPSET0的ARM物理地址是0x320001C。我们在嵌入式 Linux中,可以使mmap函数或者 loremap函数将这两个ARM物理地址映射成虚拟地址,就等同于直接操作GPIO硬件地址了。

代码编写

驱动代码

#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 *pin26_class;
static struct device *pin26_class_dev;

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

volatile unsigned int* GPFSEL2 = NULL;
volatile unsigned int* GPSET0 = NULL;
volatile unsigned int* GPCLR0 = NULL;

//led_open函数
static int pin26_open(struct inode *inode,struct file *file)
{


        printk("pin26_open\n");  //内核的打印函数和printf类似
        printk("da dui zhang hen shuai\n");
        *GPFSEL2 &= ~(0x110 << 18);
        *GPFSEL2 |= (0x01 << 18);
        return 0;


}

//led_write函数
static ssize_t pin26_write(struct file *file1,const char __user *buf,size_t count, loff_t *ppos)
{       char cmd;
        printk("pin26_write\n");
copy_from_user(&cmd,buf,count);
        if(cmd == '1')
                *GPSET0 |= (0x01 << 26);
        if(cmd == '0')
                *GPCLR0 |= (0x01 << 26);

    return 0;
}

static struct file_operations pin26_fops = {    //这种初始化方式只在linux可用,keil中就不行

    .owner = THIS_MODULE,
    .open  = pin26_open,
    .write = pin26_write,
};

int __init pin26_drv_init(void)
{
    int ret;

    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin26_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    pin26_class=class_create(THIS_MODULE,"myfirstdemo");
    pin26_class_dev =device_create(pin26_class,NULL,devno,NULL,module_name);  //创建设备文件

        GPFSEL2=(volatile unsigned int*)ioremap(0x3F200008,4);
        GPSET0=(volatile unsigned int*)ioremap(0x3F20001C,4);
        GPCLR0=(volatile unsigned int*)ioremap(0x3F200028,4);

    return 0;
}

上层代码

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

void main(){

        int fd;
        char cmd;
        fd=open("/dev/pin26",O_RDWR);
        if(fd < 0){
                printf("open failed\n");
                perror("why");
        }
        printf("请输入0/1:电平0/1\n");
        scanf("%c",&cmd);
        if(cmd == '1' | cmd == '0' ){
                write(fd,&cmd,1);
                printf("cmd:%c\n",cmd);
        }
        else
                printf("undo\n");
}
~

主要函数介绍:

ioremap

取消映射iounmap

功能:将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问.

参数
   void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
   入口: phys_addr:要映射的起始的IO地址;
   size:要映射的空间的大小;
   flags:要映射的IO空间的和权限有关的标志;
返回值:映射后的虚拟地址

copy_from_user

copy_to_user,是一种数据程序。 作用:从内核区中读取数据到用户区

功能:用于将用户空间的数据传送到内核空间。
原型:unsigned long copy_from_user(void * to, const void __user * from, unsigned long n)
   第一个参数to:是内核空间的数据目标地址指针,
   第二个参数from:是用户空间的数据源地址指针,
   第三个参数n:是数据的长度。
返回值:如果数据拷贝成功,则返回;否则,返回没有拷贝成功的数据字节数

运行结果

输入0时
在这里插入图片描述
输入1时
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值