树莓派寄存器阅读

驱动开发的两大利器就是开发手册和电路图,但是树莓派在开发手册中就已经将相应的寄存器已经写出来了,所以不需要看电路图,只看开发手册就可以进行开发。

进行IO口的驱动,所以在开发手册中找到对应的IO篇章,进行开发
有的芯片手册给的是物理地址,bcm2835给的是总线地址

GPFSEL0 GPIO Function Select 0: 功能选择 输入/输出

GPSET0 GPIO Pin Output Set 0 : 设置输出为1(set就是置1的意思)
0 = No effect
1 = Set GPIO pin n

GPCLR0 GPIO Pin Output Clear 0: 清0
0 = No effect
1 = Clear GPIO pin n

每个寄存器都是32位

编写操控io的代码

关于树莓派的cpu型号:

使用pinout

使用cat /proc/cpuinfo

查阅资料:

查阅资料发现树莓派3b的CPU型号就是BCM2709,也就是pinout中的BCM2827

要先确定IO空间的起始地址(物理地址)

ps:树莓派3B的是cpu型号为bcm2709.

0x3F00 0000就是IO外围设备的起始物理地址

我们需要确定GPFSEL0的物理地址,就要找出偏移量


可以得到物理地址:0x3F00 0000+(0x7E20 0000- 0x7E00 0000)=0x3F20 0000

GPFSEL0的地址为:0x3F20 0000;
GPSET0的地址为: 0x3F20 001C;
GPCLR0的地址为: 0x3F20 0028;

我们得到的是物理地址是不可以直接操作的,我们需要转化成虚拟地址,通过调用函数:__ioremap

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);ioremap宏定义在asm/io.h内:#define ioremap(cookie,size)           __ioremap(cookie,size,0)

参数:
phys_addr:要映射的起始的IO地址

size:要映射的空间的大小

flags:要映射的IO空间和权限有关的标志

驱动代码编写

基于驱动框架的代码进行修改

sed -ir ‘s/hello/pin4/g’ pin4_drv.c (把pin4_drv.c里面所有的hello替换成pin4)

sed -ir ‘s/关键字/目标关键字/g’ 文件

先定义寄存器的地址

错误示范:

这样直接定义是不对的,linux中代码访问的是虚拟地址,所以我们需要在入口函数把物理地址转化为虚拟地址

寄存器地址先初始化为NULL

volatile的作用:防止编译器对代码优化,变量值是直接从变量地址中读取和存储的

驱动加载的时候在入口函数对寄存器地址赋值


在open的时候配置pin4为输出引脚

在write函数获取用户层的数据,根据这个数据来操作io口输出高电平或者低电平

copy_from_user(void *to, const void __user *from, unsigned long n)
//从用户空间拷贝数据到内核空间
*to    :将数据拷贝到内核的地址
*from  :需要拷贝数据的地址
n      :拷贝数据的长度(字节)
也就是将form地址中的数据拷贝到to地址中去,拷贝长度是n

并且在出口函数解除寄存器的地址映射(防止风险)

完整代码

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的头文件volatile unsigned int* GPFSEL0=NULL;      //volatile不会因编译器的优化而省略,每次直接读值
volatile unsigned int* GPSET0=NULL;
volatile unsigned int* GPCLR0=NULL;static struct class *pin4_drv_class;
static struct device *pin4_drv_class_dev;/*1.确定主设备号*/
static dev_t devno;                //设备号
static int major = 231;                    //主设备号
static int minor = 0;                      //次设备号
static char *module_name = "pin4_drv";   //模块名
/*3.实现对应的 drv_open/drv_read/drv_write 等函数*/static ssize_t pin4_drv_read (struct file * file, char __user * buf, size_t size, loff_t * offset){printk("pin4_drv_read\n");//printk打印在内核中的信息    return 0;
}static ssize_t pin4_drv_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){char cmd;printk("pin4_drv_write\n");copy_from_user(&cmd,buf,size);printk("get is %c\n",cmd);if(cmd=='1'){printk("set 1\n");*GPSET0 |= 0x1 << 4;}else if(cmd=='0'){printk("set 0\n");*GPCLR0 |= 0x1 << 4;}else{printk("cmd undo\n");}return 0;
}static int pin4_drv_open (struct inode * node, struct file * file){printk("pin4_drv_open\n");//配置pin4引脚为输出引脚,bit 12-14 配置成001*GPFSEL0 &=~(0x6<<12);       //0x6  0110左移12位    取反后1001与运算 结果为把bit 13,14配置成0*GPFSEL0 |=(0x1<<12);        //把第12位配置成1        14~12位001输出000为输入 其中一共0~31位 return 0;
}/*2.定义自己的 file_operations 结构体*/
static const struct file_operations pin4_drv = {.owner          = THIS_MODULE,.open           = pin4_drv_open,.write          = pin4_drv_write,.read           = pin4_drv_read,};
/*4.实现入口函数,安装驱动程序时,就会去调用这个入口函数*/
int __init pin4_drv_init(void)   //真实驱动入口
{int ret;devno = MKDEV(major, minor);  //创建设备号ret   = register_chrdev(major, module_name, &pin4_drv);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中pin4_drv_class=class_create(THIS_MODULE, "pin4 class");         //创建类,用代码在dev自动生成设备pin4_drv_class_dev=device_create(pin4_drv_class, NULL, devno, NULL, module_name);  //创建设备文件GPFSEL0=(volatile unsigned int *)ioremap(0x3F200000,4);   //第一个参数真正的物理地址,第二个参数映射的大小  一个寄存器32位有4个字节GPSET0=(volatile unsigned int*)ioremap(0x3F20001C,4);   //由于ioremap返回值是void*型需要强制转换 GPCLR0=(volatile unsigned int*)ioremap(0x3F200028,4);    //物理地址转换成虚拟地址printk("insmod drive pin4 success\n");return 0;
}/*5.实现出口函数,卸载驱动程序时,就会去调用这个入口函数*/
void __exit pin4_drv_exit(void)
{//和入口函数的顺序相反iounmap(GPFSEL0);       //iounmap函数用于取消ioremap()所做的映射iounmap(GPSET0);iounmap(GPCLR0);device_destroy(pin4_drv_class, devno);class_destroy(pin4_drv_class);unregister_chrdev(major, module_name);  //卸载驱动}
/*其他完善:GPL协议 入口加载*/
module_init(pin4_drv_init);  //入口,内核加载该驱动的时候,这个宏被使用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

编译阶段

  1. 把写好的驱动文件拷贝到源码树目录的 /drivers/char 目录下

  2. 修改Makefile(为了在编译的时候生成.ko文件)

  3. 编译驱动代码(切回到源码树目录进行编译)
    ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules


测试阶段

  1. 把驱动文件拷贝到树莓派,并且安装驱动

    安装驱动

    驱动加载成功

  2. 编写测试程序拷贝到树莓派
    测试程序:

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(){int fd;char cmd;fd=open("/dev/pin4_drv",O_RDWR);if(fd==-1){printf("open failed\n");perror("reason");}else{printf("open success\n");}printf("imput 1 or 0\n1:pin4 set 1\n0:pin4 set 0\n");scanf("%c",&cmd);if(cmd=='1'){write(fd,"1",1);}else if (cmd=='0'){write(fd,"0",1);}else{printf("imput error\n");}return 0;
}

在lUbuntu交叉编译后把程序拷贝到树莓派,然后在树莓派运行


测试结果:用dmesg查看内核打印信息

查看io口状态:

linux-通过BCM2835芯片手册进行IO操控的代码编程相关推荐

  1. 11.树莓派博通BCM2835芯片手册导读与IO口驱动代码调试和测试

    11.树莓派博通BCM2835芯片手册导读与IO口驱动代码调试和测试 硬件地址的相关概念 总线地址 32位的操作系统 ,cpu最多只能访问2^32bit,即只能访问4G的内存 64位的操作系统 ,cp ...

  2. 树莓派博通BCM2835芯片手册导读及io口驱动代码的实现

    树莓派博通BCM2835芯片手册导读及io口驱动代码的实现 树莓派寄存器的介绍 寄存器地址问题 驱动代码的实现 在linux中生成树莓派所需要的的程序及实现现象 一.树莓派寄存器的介绍 GPFSEL0 ...

  3. 树莓派高阶课程7:树莓派博通BCM2835芯片手册导读

    树莓派博通BCM2835芯片手册导读 驱动的两大利器: 电路图:通过电路图找到寄存器 芯片手册:进行编写 1.树莓派寄存器的介绍: GPFSEL0 GPIO Function Select 0: // ...

  4. 树莓派——8、树莓派博通BCM2835芯片手册导读

    (122条消息) 树莓派高阶课程7:树莓派博通BCM2835芯片手册导读_哒宰的自我修养的博客-CSDN博客 博通BCM2835芯片手册导读 驱动的两大利器: 电路图:通过电路图找到寄存器 芯片手册: ...

  5. 基于博通bcm2835芯片手册进行简单的树莓派引脚驱动

    目录 1.配置寄存器 2.设置寄存器的地址 3.编写驱动代码 4.编写应用层代码 5.编译 6.测试 1.配置寄存器 我们要进行树莓派引脚的驱动就要对树莓派的引脚进行一些配置,比如我想把树莓派的某个 ...

  6. 树莓派IO口驱动代码的编写、微机总线地址、物理地址、虚拟地址、BCM2835芯片手册

    地址总线: 百度百科解释: 地址总线 (Address Bus:又称:位址总线) 属于一种电脑总线 (一部份),是由CPU 或有DMA 能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/ ...

  7. 树莓派高级开发之树莓派博通BCM2835芯片手册导读与及“相关IO口驱动代码的编写”

    首先我们要知道,驱动的两大利器:电路图(通过电路图去寻找寄存器)和芯片手册 一.寄存器的介绍 芯片手册第六章的89页,GPIO有41个寄存器,所有访问都是32位的.Description是寄存器的功能 ...

  8. 树莓派博通BCM2835芯片手册导读

    驱动两大利器︰电路图(通过电路图找寄存器)和芯片手册. 寄存器 芯片手册第六章P89∶ 字段名 描述 用法 GPFSEL0 GPIO Function select 0,功能选择输出/输入 以引脚9举 ...

  9. 树莓派 博通BCM2835芯片手册

    手册提取链接 链接:https://pan.baidu.com/s/1fdmIBNn1Pr1j3-ercNhKJg 提取码:8y1b 驱动的两大利器: 1.电路图:通过电路图找到寄存器 2.芯片手册 ...

  10. 树莓派学习笔记(十五)博通BCM2835芯片手册导读

    树莓派3b的CPU型号为:BCM2835(ARM-cotexA53架构) CPU型号为2440.2410(ARM9架构) 编写驱动需要电路图(作用通过电路图找到寄存器).芯片手册 树莓派官网:http ...

最新文章

  1. 《深度学习:Java语言实现》一一2.6小结
  2. 宏平均macro average
  3. 揭秘:倒卖火车票的惊人黑幕全过程!
  4. blog微服务架构代码_聊聊微服务架构
  5. pytorch保存模型pth_Day159:模型的保存与加载
  6. SpringCloud:Zuul 路由访问(基本使用、路由功能、过滤访问、服务降级)
  7. ios 刷新遮罩遮罩_在Adobe XD中进行遮罩的3种方法
  8. 安卓学习笔记43:初试开源框架Volley
  9. java大数据组件HBase
  10. 回收二手木料,是一个利润比较大的项目
  11. Codeforces Edu Round 68 (Rated for Div. 2)
  12. hudson构建配置
  13. UI实战教程之切图标注篇(UI必备)
  14. 浏览器通过域名查找IP地址的过程
  15. LibOpenCM3(二) 项目模板 Makefile分析
  16. 计算机综合症怎么治,哪些运动可以用来治疗“电脑综合症”
  17. 傲慢与偏见之 - 因果倒置的锦上添花
  18. 鼠标滚动:mousewheel事件在Firefox采用DOMMouseScroll事件
  19. “量子XX”,是怎么被玩坏的?
  20. 用一分钟理解console的这个原理,多留几根黑发~

热门文章

  1. python字符画绘制代码_python图片转字符画代码是什么
  2. 带电检测必要性_GIS的概念和定期局部放电检测的重要性
  3. 虚拟机下搭建一个dns服务器,虚拟机下DNS服务器配置.doc
  4. 5.1(电脑技能经验) 美图秀秀批量处理图片大小,针对CSDN博客上传图片要求,小于2M,图片大小设计712*400
  5. 重装服务器系统鼠标键盘用不了,win7重装系统后鼠标键盘不能用怎么办
  6. 6轴并联机器人示教器
  7. hp打印机装不上服务器系统,win10安装不了惠普打印机驱动怎么办
  8. RGB888和RGB565颜色对照表
  9. win7 sp1简体中文升级补丁包(64位)
  10. 软件测试 中静态测试与动态测试的区别