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


    首先我们要知道,驱动的两大利器:电路图(通过电路图去寻找寄存器)和芯片手册

    一、寄存器的介绍

    芯片手册第六章的89页,GPIO有41个寄存器,所有访问都是32位的。Description是寄存器的功能描述。GPFSEL0(寄存器名) GPIO Function Select 0(功能选择:输入或输出);GPSET0 (寄存器名) GPIO Pin Output Set 0(将IO口置0);GPSET1(寄存器名) GPIO Pin Output Set 1(将IO口置1);GPCLR0(寄存器名) GPIO Pin Output Clear 0 (清0)下图的地址是:总线地址(并不是真正的物理地址)
    在这里插入图片描述

    GPFSEL0是pin0 ~ pin9的配置寄存器,GPFSEL1是pin10 ~ pin19的配置寄存器,以此类推,GPFSEL5就是pin50~pin53的配置寄存器。

    字段名 描述 用法
    GPFSEL0 GPIO Function select 0,功能选择输出/输入 以引脚9举例:000 = GPIO Pin 9 is an input,001 = GPIO Pin 9 is an output
    GPSET0 GPIO Pin output Set 0,输出0 0 = No effect ,1 = Set GPIO pin n
    GPSET1 GPIO Pin output set 1,输出1 0 = No effect ,1 = Set GPIO pin n
    GPCLR0 GPIO Pin output clear 0,清0 0 = No effect ,1 = Clear GPIO pin n

    在上面的文档里已经说的很清楚了,000是引脚输入,而001则是引脚输出,在这里要注意每个寄存器都是32位的

    • FSELn表示GPIOn,下图给出第九个引脚的功能选择示例,对寄存器的29-27进行配置,进而设置相应的功能。根据图片下方的register
      0表示0~9使用的是register 0(即GPFSEL0)这个寄存器。

    在这里插入图片描述

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/28a4ca08cba847d68c3e4c11b5f7615

    • 输出集寄存器用于设置GPIO管脚。SET{n}字段定义,分别对GPIO引脚进行设置,将“0”写入字段没有作用。如果GPIO管脚为在输入(默认情况下)中使用,那么SET{n}字段中的值将被忽略。然而,如果引脚随后被定义为输出,那么位将被设置根据上次的设置/清除操作。分离集和明确功能取消对读-修改-写操作的需要。GPSETn寄存器为了使IO口设置为1,set4位设置第四个引脚,也就是寄存器的第四位。
      在这里插入图片描述
    • 输出清除寄存器用于清除GPIO管脚。CLR{n}字段定义要清除各自的GPIO引脚,向字段写入“0”没有作用。如果在输入(默认),然后在CLR{n}字段的值是忽略了。然而,如果引脚随后被定义为输出,那么位将被定义为输出根据上次的设置/清除操作进行设置。分隔集与清函数消除了读-修改-写操作的需要。GPCLRn是清零功能寄存器。
      在这里插入图片描述
      在这里插入图片描述

    把pin4引脚配置为输出引脚:
    FSEL4 14-12 001 我们把4引脚的14-12配置成001 GPIO Pin 4 is an output
    详细操作:
    只需要将GPFSL0这个寄存器的14~12位设置为001就可以了。只需要将0x6(对应的2进制是110)左移12位·然后取反再与上GPFSL0就可以将13、14这两位配置为0,然后再将0x6(对应2进制110)左移12位,然后或上GPFSL0即可将12位置1。

    特别提示:进行取反后再进行按位与操作是为了不影响其他引脚

    配置pin4引脚为输出引脚 bit 12-14 配置成001

    31 30 ······14 13 12 11 10 9 8 7 6 5 4 3 2 1 
    0  0  ······0  0  1  0  0  0 0 0 0 0 0 0 0 0 
    
     //配置pin4引脚为输出引脚      bit 12-14  配置成001  
      *GPFSEL0 &= ~(0x6 <<12); // 把bit13 、bit14置为0  
     //0x6是110  <<12左移12位 ~取反 &按位与
      *GPFSEL0 |= (0x1 <<12); //把12置为1   |按位或
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    忘了按位与和按位或的点这里

    代码实现:

      *GPFSEL0 &=~(0x6 <<12); // 把13 、14置为0
      *GPFSEL0 |= (0x1 <<12); //把12置为1
    
    • 1
    • 2
    • 注意:我们配置的底层引脚对应得是BCM 寄存器第0组位FESL0–9, 这个就是在寄存器GPFSEL0里,寄存器已经分好组了
      寄存器第1组位FSEL10–19,这个在寄存器GPFSEL1里

    在这里插入图片描述

    更多的引脚对应的寄存器可以去树莓派官网进行查看
    树莓派引脚查看官网
    在这里插入图片描述
    在上图中我们可以点击对应的引脚编号,就可以查看到对应的引脚的相关的信息

    二、寄存器的地址问题

    我们在编写驱动程序的时候,IO口空间的起始地址是0x3f00 0000,加上GPIO的偏移量0x200 0000,所以GPIO的物理地址应该是0x3f20 0000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,映射到虚拟地址上。
    在这里插入图片描述
    上图的尾部偏移是对的,根据GPIO的物理地址0x3f20 0000可以知道:

    GPFSEL0 0x3f20 0000 //IO口的初始的物理地址,而并不是手册里面的那个总线地址
    GPSET0 0x3f20 001c  //地址通过查找芯片手册里面的对应的GPSET0 的总线地址的后两位决定是1c
    GPCLR0 0x3f20 0028 //地址是查找GPCLR0在芯片手册里的总线地址确定的28,所以地址后两位是28
    
    • 1
    • 2
    • 3
    • 在原来框架的基础上,添加寄存器的定义
    volatile unsigned int* GPFSEL0 = NULL;
    volatile unsigned int* GPSET0  = NULL;
    volatile unsigned int* GPCLR0  = NULL;
    
    • 1
    • 2
    • 3

    完成以上代码需要搞清楚的几点

    1. 弄清楚寄存器的分组
      GPFSEL0是pin0 ~ pin9的配置寄存器,GPFSEL1是pin10 ~ pin19的配置寄存器,以此类推,GPFSEL5就是pin50~pin53的配置寄存器。这个由查阅芯片手册可以得知

    2. volatile关键字的使用(笔试可能会考)

    • 在此处的作用:防止编译器优化(可能是省略,也可能是更改)这些寄存器地址变量&#

  • 相关阅读:
    JavaEE——No.1 线程安全问题
    【算法与数据结构】--前言
    【Vue脚手架项目的结构】
    积加(跨境ERP)与金蝶云星空单据集成对接
    Verilog 时序逻辑 UDP
    STM32大小端模式测试
    [Linux]进程间通信--管道
    AWK语言第二版 2.3转换
    Dopamine-PEG-NH2,NH2-PEG-DOP,氨基聚乙二醇多巴胺,材料改性用科研试剂
    【微机接口】可编程定时器/计数器8254
  • 原文地址:https://blog.csdn.net/weixin_51976284/article/details/126892237