树莓派2 gpio led blink实验
2015-11-27 22:44
295 查看
树莓派2使用的是bcm2836,树莓派1使用的是bcm2835,据说bcm2836除了CPU使用的是cortex-a7,外设与bcm2835基本相同,因为官方没有提供bcm2836的外设参考手册,所以只能使用bcm2835的外设参考手册,BCM2835-ARM-Peripherals.pdf
手册中第5页的地址映射图如下:
左侧的是bcm2835中的GPU的虚拟地址空间,中间的是在没有启用mmu时ARM的物理地址空间,右侧是的ARM开启mmu时的虚拟地址空间映射。
对于我们的裸机led实验,我们只关心中间的物理地址空间。所以bcm2835的外设被映射到0x20000000开始的物理地址。但是在树莓派2中这个外设起始地址变为0x3f000000。
有人在uboot的补丁中发现了这个地址。继续查看手册的第90页,可以得知gpio寄存器被映射到0x7e200000,这个GPU的虚拟地址,根据上图可以换算为0x20200000,也就是相对外设偏移0x200000,由此推测出在树莓派中gpio的起始地址为0x3f200000。
啰嗦了这么多,我们终于可以写下第一条语句了
手册的89页说bcm2835中共有54个pin,90页的表6.1说明了gpio的41个寄存器的细节,包括被映射到的地址,以及功能等。根据此表我们可以写代码如下:
我们把gpio的寄存器映射到的这块内存当成一个unsigned int类型的数组,GPIO_BASE是数组的起始地址,因为每个寄存器都是32位的,即4个字节,所以我们可以通过寄存器的索引来访问寄存器。
前6个寄存器是功能选择寄存器,每个pin可以配置成2^3==8种不同的功能,如0b000表示输入,0b001表示输出,因为每个pin都占用3位,所以一个32位的寄存器可以容纳10个pin的配置信息,因为有54个pin,所有需要6个功能选择寄存器。
我们通过函数gpio_pin_fsel来配置相应pin的功能
第7第8个寄存器加起来一共64位,每一位对应一个pin,对应位为1,表示相应pin输出高电平
我们通过函数gpio_pin_high来使相应pin输出高电平,对于led就是点亮
第10第11个寄存器加起来一共64位,每一位对应一个pin,对应位为1,表示相应pin输出低电平。
我们通过函数gpio_pin_low来使相应pin输出低电平,对于led就是熄灭
仔细查看表6.1的寄存器功能,可以写出如下的实现代码
主程序代码如下:
pin 47是树莓派2上那个绿色的led
还有一个细节是要让c语言在裸机上运行,我们必须要设置stack pointer。
通过一个简短的汇编代码 start.S来完成:
通过命令
-g是防止编译器将我们的delay函数优化掉
将 bootcode.bin start.elf kernel.img 拷贝到sd卡中插入树莓派2,通电后,可以看到绿色led不停的闪烁了
note:
本文主要参考Brian的文章,非常感谢Brian
gnu cross toolchain: https://launchpad.net/gcc-arm-embedded/+download
手册中第5页的地址映射图如下:
左侧的是bcm2835中的GPU的虚拟地址空间,中间的是在没有启用mmu时ARM的物理地址空间,右侧是的ARM开启mmu时的虚拟地址空间映射。
对于我们的裸机led实验,我们只关心中间的物理地址空间。所以bcm2835的外设被映射到0x20000000开始的物理地址。但是在树莓派2中这个外设起始地址变为0x3f000000。
有人在uboot的补丁中发现了这个地址。继续查看手册的第90页,可以得知gpio寄存器被映射到0x7e200000,这个GPU的虚拟地址,根据上图可以换算为0x20200000,也就是相对外设偏移0x200000,由此推测出在树莓派中gpio的起始地址为0x3f200000。
啰嗦了这么多,我们终于可以写下第一条语句了
#define GPIO_BASE 0x3f200000u
手册的89页说bcm2835中共有54个pin,90页的表6.1说明了gpio的41个寄存器的细节,包括被映射到的地址,以及功能等。根据此表我们可以写代码如下:
/* gpio register index */ enum gpio_reg_index { GPIO_GPFSEL0 = 0, GPIO_GPFSEL1 = 1, GPIO_GPFSEL2 = 2, GPIO_GPFSEL3 = 3, GPIO_GPFSEL4 = 4, GPIO_GPFSEL5 = 5, GPIO_GPSET0 = 7, GPIO_GPSET1 = 8, GPIO_GPCLR0 = 10, GPIO_GPCLR1 = 11, GPIO_GPLEV0 = 13, GPIO_GPLEV1 = 14, GPIO_GPEDS0 = 16, GPIO_GPEDS1 = 17, GPIO_GPREN0 = 19, GPIO_GPREN1 = 20, GPIO_GPFEN0 = 22, GPIO_GPFEN1 = 23, GPIO_GPHEN0 = 25, GPIO_GPHEN1 = 26, GPIO_GPLEN0 = 28, GPIO_GPLEN1 = 29, GPIO_GPAREN0 = 31, GPIO_GPAREN1 = 32, GPIO_GPAFEN0 = 34, GPIO_GPAFEN1 = 35, GPIO_GPPUD = 37, GPIO_GPPUDCLK0 = 38, GPIO_GPPUDCLK1 = 39, }; enum gpio_fsel { GPIO_FSEL_INPUT = 0x0, GPIO_FSEL_OUTPUT = 0x1, GPIO_FSEL_ALT0 = 0x4, GPIO_FSEL_ALT1 = 0x5, GPIO_FSEL_ALT2 = 0x6, GPIO_FSEL_ALT3 = 0x7, GPIO_FSEL_ALT4 = 0x3, GPIO_FSEL_ALT5 = 0x2, }; extern void gpio_pin_fsel(u32 pin, enum gpio_fsel value); extern void gpio_pin_high(u32 pin); extern void gpio_pin_low(u32 pin);
我们把gpio的寄存器映射到的这块内存当成一个unsigned int类型的数组,GPIO_BASE是数组的起始地址,因为每个寄存器都是32位的,即4个字节,所以我们可以通过寄存器的索引来访问寄存器。
前6个寄存器是功能选择寄存器,每个pin可以配置成2^3==8种不同的功能,如0b000表示输入,0b001表示输出,因为每个pin都占用3位,所以一个32位的寄存器可以容纳10个pin的配置信息,因为有54个pin,所有需要6个功能选择寄存器。
我们通过函数gpio_pin_fsel来配置相应pin的功能
第7第8个寄存器加起来一共64位,每一位对应一个pin,对应位为1,表示相应pin输出高电平
我们通过函数gpio_pin_high来使相应pin输出高电平,对于led就是点亮
第10第11个寄存器加起来一共64位,每一位对应一个pin,对应位为1,表示相应pin输出低电平。
我们通过函数gpio_pin_low来使相应pin输出低电平,对于led就是熄灭
仔细查看表6.1的寄存器功能,可以写出如下的实现代码
void gpio_pin_fsel(u32 pin, enum gpio_fsel value) { volatile u32 *base = (u32 *)GPIO_BASE; base[GPIO_GPFSEL0+pin/10] |= (value << pin%10*3); } void gpio_pin_high(u32 pin) { volatile u32 *base = (u32 *)GPIO_BASE; base[GPIO_GPSET0+pin/32] |= (1 << pin%32); } void gpio_pin_low(u32 pin) { volatile u32 *base = (u32 *)GPIO_BASE; base[GPIO_GPCLR0+pin/32] |= (1 << pin%32); }
主程序代码如下:
#include <gpio.h> void delay(); int main(int argc, char *argv[]) { gpio_pin_fsel(47, GPIO_FSEL_OUTPUT); while (1) { delay(); gpio_pin_high(47); delay(); gpio_pin_low(47); } return 0; } void delay() { for (int i = 0; i < 500000; ++i) ; }
pin 47是树莓派2上那个绿色的led
还有一个细节是要让c语言在裸机上运行,我们必须要设置stack pointer。
通过一个简短的汇编代码 start.S来完成:
.text .global _start _start: mov sp, #0x8000 bl main
通过命令
rm-none-eabi-gcc -std=gnu99 -I. -mfpu=neon-vfpv4 -mfloat-abi=hard -march=armv7-a -mtune=cortex-a7 -nostartfiles -g start.S main.c gpio.c -o kernel.elf arm-none-eabi-objcopy kernel.elf -O binary kernel.img
-g是防止编译器将我们的delay函数优化掉
将 bootcode.bin start.elf kernel.img 拷贝到sd卡中插入树莓派2,通电后,可以看到绿色led不停的闪烁了
note:
本文主要参考Brian的文章,非常感谢Brian
gnu cross toolchain: https://launchpad.net/gcc-arm-embedded/+download
相关文章推荐
- 如何编写测试计划
- Oracle11g 数据库导入导出之中文乱码问题的解决
- windows下php5.4安装imagick
- 互联网协议入门(一)
- 接口和抽象类的区别
- Apache的配置
- 神之门V8(1):解密handle<T>
- 第二个Sprint冲刺第五天(燃尽图)
- SpringMVC访问静态资源的三种方式
- 自己写的一个定时器 timer
- java线程同步的5种方式(转载)
- HDU 2337 Escape from Enemy Territory(BFS+二分优化)
- JAVA8永久代
- 控制器View的显示
- style中display:none与visible:hidder的区别 以及disabled与readonly的区别
- 程序启动原理
- 什么是线程同步
- leetcode笔记:Combination Sum III
- 窗口显示流程
- 什么是P问题、NP问题和NPC问题