【树莓派学习笔记】九、C语言寄存器操作控制GPIO
目录
- CPU型号确定
- 寄存器的地址问题
- GPIO寄存器
- GPFESLn
- GPSETn
- GPCLRn
- 重要函数
- mmap函数
- munmap函数
- 点灯程序
平台:树莓派3B
版本: 2021-05-07-raspios-buster-armhf
CPU型号确定
由
pinout
命令可知,所用的板子Soc型号为BCM2837
寄存器的地址问题
本节内容修改自虚拟地址/物理地址——virtual address(memory)/physical address: 树莓派 mmap example —— 风竹夜
由于官方只公开了BCM2835的芯片手册(Raspberry Pi Documentation),而我们用的板子Soc为BCM2837,因此由手册得到的地址是不准确的。但两个芯片外设上区别不大,手册仍有一定的参考价值。
手册第5页描绘了BCM2835的地址映射情况
其中要区分文档中的三个地址,Bus Address、Physical Address、Virtual Address
在树莓派中,借助ARM内部的MMU,CPU外设物理地址映射成了虚拟地址。
这里的 Virtual Address 和 Physical Address 是通过 ARM MMU 来实现映射的(先不管cache等其他因素)。主板上外设的实际地址是 Physical Address,所以要访问 GPIO 寄存器,也就是访问 Physical Address 中从 0x20000000 开始的某处的地址, 那么就需要在代码中访问 Virtual Address 中从 0xF2000000 开始的某处的地址,由于该虚拟地址在高地址内存,因此只能在内核的代码中才能够访问到。
而在树莓派3B中我们可以通过
sudo cat /proc/iomem
命令获取物理地址分配情况
由图可知,树莓派3B GPIO 的物理起始地址为0x3f200000
GPIO寄存器
从手册第89页开始详细地描述了GPIO外设。
第90-91页的表格标明了和GPIO相关的寄存器的地址。
其中GPFESLn(选择引脚功能)、GPSETn(设置引脚输出高电平)和GPCLRn(设置引脚输出低电平)是控制引脚输出电平需要用到的寄存器。
虽然树莓派3B的Soc换成了BCM2837,GPIO外设的基地址有变,但手册上的偏移地址还是有参考价值的。
其中GPFESL0的偏移地址为0x00;GPSET0的偏移地址为0x1C;GPCLRn的偏移地址为0x28
GPFESLn
由手册知
GPFESL0控制GPIO Pin 0~9;
GPFESL1控制GPIO Pin 10~19;
GPFESL2控制GPIO Pin 20~29;
GPFESL3控制GPIO Pin 30~39;
GPFESL4控制GPIO Pin 40~49;
GPFESL5控制GPIO Pin 50~59;
3位为间隔,000 为输入,001为输出
GPSETn
由手册知
GPSET0控制GPIO pin 0~31
GPSET1控制GPIO pin 32~53
GPCLRn
由手册知
GPCLR0控制GPIO pin 0~31
GPCLR1控制GPIO pin 32~53
重要函数
mmap函数
mmap将一个文件或者其它对象映射进内存。mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。
length:映射区的长度。//长度单位是 以字节为单位,不足一内存页按一内存页处理。
prot:期望的内存保护标志,不能与文件的打开模式冲突。
PROT_EXEC //页内容可以被执行;
PROT_READ //页内容可以被读取;
PROT_WRITE //页可以被写入;
PROT_NONE //页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。
fd:有效的文件描述词。一般是由open()函数返回。
offset:被映射对象内容的起点。
返回值:成功:被映射区的指针。失败:MAP_FAILED[其值为(void *)-1]。
munmap函数
munmap()用来取消参数start所指的映射内存起始地址
int munmap(void *start,size_t length);
start:映射区的开始地址
length:欲取消的内存大小
返回值:成功:0;失败:-1。
点灯程序
在合适的地方编写main.c文件
nano main.c
#include <stdio.h>
#include <sys/mman.h> //mmap、munmap函数的定义
#include <fcntl.h> //open函数的定义
#include <unistd.h> //close函数的定义
#include <stdint.h> //uint8_t、uint32_t等类型的定义
#include <unistd.h> //sleep函数的定义#define GPIO_BASE_Physical_Address 0x3f200000#define GPFSEL0_Offs 0x00
#define GPSET0_Offs 0x1C
#define GPCLR0_Offs 0x28 int main(int argc, char *argv[])
{int fd;uint8_t Pin = 3;fd = open("/dev/gpiomem", O_RDWR);if (fd == -1) //只有root用户才能读取/dev/mem, 故本实验选择读取/dev/gpiomem,以实现普通用户控制GPIO{printf("open Error!\n");return -1;}void *GPIO_BASE = mmap(0, sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE, MAP_SHARED , fd, GPIO_BASE_Physical_Address);close(fd);if(GPIO_BASE == MAP_FAILED){printf("mmap Error!\n");return -1;}volatile uint32_t * GPFSEL0 = (uint32_t *)(GPIO_BASE + GPFSEL0_Offs);volatile uint32_t * GPSET0 = (uint32_t *)(GPIO_BASE + GPSET0_Offs);volatile uint32_t * GPCLR0 = (uint32_t *)(GPIO_BASE + GPCLR0_Offs);*GPFSEL0 = (*GPFSEL0 & ~((uint32_t)7 << (3 * Pin))) | ((uint32_t)1) << (3 * Pin); //设置Pin 3为输出模式for(uint8_t i = 0; i < 10; ++i) //反转Pin 3 i次{*GPCLR0 = ((uint32_t)1) << Pin; sleep(1);*GPSET0 = ((uint32_t)1) << Pin; sleep(1);}*GPFSEL0 = *GPFSEL0 & ~((uint32_t)7 << (3 * Pin)); //设置Pin 3为输入模式if(munmap(GPIO_BASE, sysconf(_SC_PAGESIZE)) == -1){printf("munmap Error!\n");return -1;}GPIO_BASE = MAP_FAILED;GPFSEL0 = MAP_FAILED;GPSET0 = MAP_FAILED;GPCLR0 = MAP_FAILED;return 0;
}
其中sysconf(_SC_PAGESIZE)为页的长度,相关知识见yaaangmin大佬的视频:深入理解计算机系统20:内存 - 多级页表、虚拟地址、物理地址
编写Makefile文件
nano Makefile
注意Makefile里的缩进为Tab而不是空格
main: main.ogcc -o main main.o
main.o: main.cgcc -c main.c
clean:rm *.orm main
clear:rm *.orm main
编译并测试
make
./main
可见Pin 3 脚所连LED闪烁(Pin 3脚已事先接好LED和限流电阻下拉至GND)
此编号对应
gpio readall
命令下的BCM编号
【树莓派学习笔记】九、C语言寄存器操作控制GPIO相关推荐
- 树莓派学习笔记——GPIO功能学习
0.前言 树莓派现在越来越火,网上树莓派的资料也越来越多.树莓派源自英国,国外嵌入式开源领域具有良好的分享精神,树莓派各种集成库也层出不穷,下面推荐几个. [ python GPIO ] [开发语言] ...
- 树莓派学习笔记 ——I2C PCF8574 SysFs方式
1.前言 树莓派的GPIO端口数量有限,可通过IO扩展芯片增加GPIO的数量,使得树莓派可以适应更多的应用.PCF8574为一款通过I2C总线扩展IO的芯片,单个PCF8574可扩展8个IO,一个I2 ...
- 树莓派学习笔记——Linux I2C驱动说明
1.前言 [linux内核说明] 通常情况下,I2C设备由内核驱动控制,但是某些情况下I2C设备也可由用户空间控制.如果在用户空间控制I2C设备,需要访问/dev目录中所提供的接口,在使用I2C设备之 ...
- at24c16如何划分出多个读写区_AVR学习笔记九、基于AT24C16的数据存储实验
Ema{@AVR 学习笔记九.基于 AT24C16 的数据存储实验 ------- 基于 LT_Mini_M16 9.1 用 I/O 口模拟 I2C 总线实现 AT24C16 的读写 9.1.1 .实 ...
- 树莓派学习笔记(5):成功实现NAS家庭服务器(流媒体播放、文件共享及下载机)
一.家庭服务器实现的主要功能 http://www.cnblogs.com/xiaowuyi/p/4051238.html 1.流媒体播放服务:利用DLNA实现电视.手机.电脑播放其上面的媒体文件. ...
- 树莓派学习笔记——获取树莓派CPU温度
0 前言 本文通过文件操作读取树莓派CPU温度,在linux系统中任何设备的操作都被抽象成为文件读写,通过读取/sys/class/thermal/thermal_zone0/temp文件中的内容便获 ...
- 树莓派学习笔记——wiringPi简介、安装和管脚说明
1.WiringPi简介 WiringPi是应用于树莓派平台的GPIO控制库函数,WiringPi遵守GUN Lv3.wiringPi使用C或者C++开发并且可以被其他语言包转,例如python.ru ...
- 树莓派学习笔记(一)
树莓派学习笔记 笔记来自B站UP主[树小悉]的树莓派系列视频的听课笔记,通俗易懂,风趣幽默,适合新手入门,强烈推荐!!! 关机命令 sudo poweroff 关闭电源 sodo shutdown - ...
- IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法...
IOS学习笔记(九)之UIAlertView(警告视图)和UIActionSheet(操作表视图)基本概念和使用方法 Author:hmjiangqq Email:jiangqqlmj@163.com ...
最新文章
- 计算机系统的分类补充完整,数据库系统原(理B)13春A卷.doc
- GetOpenFileName 选择文件夹的解决方法
- 全球及中国梦幻体育软件和平台行业十四五规模展望及应用价值分析报告2021-2027年
- leetcode 191. 位1的个数(移位操作)
- springframework报错_应对报错信息的必杀技!
- potplayer播放器的下载与截图配置
- 去哪儿网:报复性滑雪来了 机票预订量超去年
- 在设计四人抢答器中灯全亮_数字电子技术课程设计报告(四人抢答器).doc
- 战地一的服务器在哪个文件夹,战地1怎么加入服务器 战地1加入服务器方法
- Linux的syscall源码,Linux内核跟踪之syscall tracer
- Inferred type 'S' for type parameter 'S' is not within its bound;
- iphone型号表_iPhone12各版本有什么区别 苹果12四款机型配置表一览
- 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)
- BuuCTF_crypto(2021.10.8新-->旧)
- python爬取网易云音乐飙升榜音乐_python爬取网易云音乐热歌榜 python爬取网易云音乐热歌榜实例代码...
- 杰理-手表-AC701-watch-添加表盘
- 小米miui查看连接过已保存的密码方法
- 技术栈中的爱马仕?Facebook发布全新JavaScript引擎:Hermes
- 有了他,和编程大牛面基不在话下
- 零费用学习网络营销,小伙伴们惊呆了
热门文章
- java获取classpath以外的路径
- wndows系统命令总结
- 双绞线施工质量的检测方法
- 两个日期相差月份 java_Java获取两个指定日期之间的所有月份
- 代码 抠图_3 行 Python 代码 5 秒抠图的 AI 神器,根本无需 PS,附教程
- css 动画使用_如何在CSS中使用动画
- android开发入门_Android开发入门
- 2019 css 框架_宣布CSS 2019调查状态
- 文档对象模型dom_什么是文档对象模型,以及为什么应该知道如何使用它。
- 简·雅各布斯指数第二部分:测试