树莓派2 gpio led blink实验

来源:互联网 发布:python 打包发布 编辑:程序博客网 时间:2024/06/05 11:27

树莓派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。

啰嗦了这么多,我们终于可以写下第一条语句了

#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.elfarm-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


0 0
原创粉丝点击