使用ARM组件的Flash Raspberry pi LED

时间:2017-03-09 17:43:57

标签: assembly raspberry-pi arm

我试图让一个LED连接到覆盆子pi上的gpio引脚18时遇到问题。我用C测试了我的设置,并确认这不是问题,但我的汇编代码就是问题。

这是我到目前为止的代码:

template<typename T, void (T::*M)() = &T::f>
void g() {
    T t;
    (t.*M)();
}

我设法使用GPIO 47引脚。但是,当我将其更改为使用引脚18时,这就是我遇到问题的地方。在此先感谢您的帮助,非常感谢!

3 个答案:

答案 0 :(得分:4)

查看BCM2835文档 - Broadcom BCM2835 ARM外设https://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

您的代码似乎是基于Raspberry Pi,汇编语言,Bruce Smith。

我不是为什么我们要清除3位,因为文档说的是输入集。

***我确认它适用于我的RPi 2 B,引脚上有3+伏特。

目标引脚= 18 设置GPIO功能: 26-24 FSEL18 FSEL18 - 功能选择18 R / W 0 这是在GPIO备用功能选择寄存器1中 所以:0x 7E20 0004 GPFSEL1 GPIO功能选择1 32 R / W |偏移量#0x4

init_output:                        // init for OUTPUT
            ldr r3, [sp, #8]        // virt GPIO base
            add r3, r3, #0x4        // offset to GPFSEL1
            ldr r2, [r3]            // get contents of GPFSEL1
            orr r2, r2, #0b001<<18  // set 3 bits re FSEL18 output
            str r2, [r3]            // store set bits at GPFSEL1

设置输出: 31-0 SETn(n = 0..31)0 =无效1 =设置GPIO引脚n R / W 0 哪个在GPIO输出设置寄存器0中 所以:0x 7E20 001C GPSET0 GPIO引脚输出设置0 32 W | offset#0x1C == 28

set_pin:
        ldr r3, [sp, #8]            // virt GPIO base
        add r3, r3, #0x1C           // GPSET0
        ldr r2, [r3]                // get content of GPSET0
        orr r2, r2, #1<<18          // set PIN 18
        str r2,[r3]                 // set PIN 18 @ GPSET0

清除输出: 31-0 CLRn(n = 0..31)0 =无效1 =清零GPIO引脚n R / W 0 哪个是GPIO输出清除寄存器0 所以:0x 7E20 0028 GPCLR0 GPIO引脚输出清零0 32 W |偏移量#0x28 == 40

clear_pin:
        ldr r3, [sp, #8]            // virt GPIO base
        add r3, r3, #0x28           // GPCLR0
        ldr r2, [r3]                // get content of GPCLR0
        orr r2, r2, #1<<18          // set PIN 18
        str r2,[r3]                 // set PIN 18 @ GPCLR0            

答案 1 :(得分:2)

这是修订后的替代答案,在C。

InfinitelyManic澄清的解决方案在运行Stretch的Pi-0上运行良好。 &#34;设置&#34;在运行Jessie(K 4.9.35)或Openelec 8.0.4 = Kodi 17.3(内核4.9.30)的Pi-1B上运行良好,但是&#34;清除&#34;操作清除了引脚但挂了系统。

我使用从fdbdcbc和InfinitelyManic整合的汇编代码作为C程序的原型来做同样的事情。我希望C版可能更便携。它使用上述技术对引脚进行直接存储器控制(不使用wiringPi或其他库,我无法将其加载到Openelec上)。在调试中,我发现设置SET / CLR寄存器只需要写一个代表要设置的引脚的位/ clr;复制和重写寄存器导致系统挂起CLR。

我使用标准的C包含定义对使用汇编程序到C的一些代码进行了大量评论和反向工程。

下面的代码适用于运行Stretch的Pi-0(修订代码920093)和运行Jessie或Openelec的Pi-1B(修订代码000e)。

/*  gblink.c
    Procedures to set and clear Raspberry Pi GPIO pins through direct
    memory access rather than through major libraries (such as wiringPi).

    Advantages: speed and portability (requiring no special libraries).
    Disadvantage: limited capability, long-term maintenance, flexibility.

    If running on Pi-2, change ref to gpioBASE to gpioBASE2 in mmap call
    Don't know what to use if Pi-3

    With testing program.
    Compile as
        gcc -o gblink gblink.c
    run as
        sudo gblink <pin number>

    Written by hdtodd, 12 Dec 2017, based on Stack Overflow ARM 
    assembler examples by InfinitelyManic on 10 Mar 2017:
    https://stackoverflow.com/questions/42702056/
      flash-raspberry-pi-led-using-arm-assembly
    Corrected 23 Dec 2017 to write rather than rewrite SET/CLR registers.

    See also: 
      https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <time.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define memFile    "/dev/mem"            // memory device for mapping
#define memFlags   O_RDWR | O_SYNC       // flags for opening mem
#define baseGPIO   0x20200000            // GPIO_Base address for Pi 1
#define baseGPIO2  0x3F200000            // GPIO_Base address for Pi 2
#define pinMAX     32                    // max # of GPIO pins we'll support (for simplicity)
#define pinsPerSEL 10                    // 3 bits/field, 10 fields/register in GFSELn's
#define outMask    0b111                 // 3-bit field mask to clear GFSEL register
#define outEnable  0b001                 // 3-bit pattern for GFSEL registers to enable output
#define offsetSET   7                    // word-address offset to GPSET0 word to set pin (0x1c bytes)
#define offsetCLR  10                    // word-address offset to GPCLR0 word to clear pin (0x28 bytes)
typedef enum {false=0, true=~0} boolean;

static uint32_t  *vAddrGPIO;             // mapped address to physical GPIO register base
int       memFH;                         // file handle for /dev/mem 
boolean   alreadyMapped = false;
boolean   keepBlinking = true;
struct sigaction act;                    // catch CNTL-C to terminate cleanly
uint32_t  gpFSEL, fSELn;                 // offset to GPIO Function SELect register for this 
                                         //   pin & Field SEL loc in that register for this pin
boolean   gblinkDebug = false;           // enables printing debug information

// cntlCHandler() triggers end of main loop if a ^C is received on controlling terminal
void cntlCHandler(int sigType) {
  keepBlinking = false;
};

// enableGPIO maps memory to GPIO registers and sets pin 'pinNum' to be an output pin
boolean enableGPIO(int pinNum) {
  if (pinNum < 0 || pinNum > pinMAX-1) {
    printf("[enableGPIO:] Requested pin # %d out of range 0 to %d\n", pinNum, pinMAX-1);
    return false;
  };

  // if we haven't already done it, map GPIO registers to our memory space
  if ( !alreadyMapped ) {
    if ( (memFH = open(memFile,memFlags)) < 0) {
      perror("[?enableGPIO:] Can't open '/dev/mem' file; exiting.  Rerun as root?");
      exit(EXIT_FAILURE);
    }
    vAddrGPIO = (uint32_t *)mmap(NULL,getpagesize(),PROT_WRITE|PROT_READ,MAP_SHARED,memFH,baseGPIO);
    if (vAddrGPIO == MAP_FAILED) {
      perror("[?enableGPIO:] Can't map to GPIO register addresses; exiting.  Rerun as root?");
      exit(EXIT_FAILURE);
    };
    alreadyMapped = true;
  };

  // locate the register and field for this pin and set it for output
  if (gblinkDebug) 
    printf("[enableGPIO] Memory-mapped base address of GPIO register set is vAddrGPIO = %p\n", vAddrGPIO);
  gpFSEL  = pinNum/pinsPerSEL;          // offset to register for this pin
  fSELn   = 3*(pinNum%pinsPerSEL);      // location in register of 3-bit field for this pin
  if (gblinkDebug) {
    printf("[enableGPIO] GPIO pin mapped to GPFSEL%d and 3-bit FSEL field at bit %d in that register.\n", gpFSEL, fSELn);
    printf("[enableGPIO] Will use gpioSEL register at %p\n", (vAddrGPIO+gpFSEL));
  };
  *(vAddrGPIO+gpFSEL) =  ( *(vAddrGPIO+gpFSEL) & ~(outMask<<fSELn)) | (outEnable<<fSELn);
  if (gblinkDebug)
    printf("[enableGPIO] After setting, GPFSEL%d register contents = %x\n", gpFSEL, *(vAddrGPIO+gpFSEL));
  return true;
};

// sets the pin voltage high (+5v)
boolean setOutput(int pinNum) {
  if (pinNum < 0 || pinNum >= pinMAX) {
    printf("[setOutput] Requested pin # %d is outside of allowed range 0 to %d\n", pinNum, pinMAX-1);
    return false;
  };
  if (gblinkDebug)
    printf("[setOutput] Writing to pin field %d at GPIO register address %p\n", pinNum, (vAddrGPIO+offsetSET));
  *(vAddrGPIO+offsetSET) = 1<<pinNum;  // write to "set" bit
  return true;
};

// sets the pin voltage low (+0v)
boolean clrOutput(int pinNum) {
  if (pinNum < 0 || pinNum >= pinMAX) {
    printf("[clrOutput] Requested pin # %d is outside of allowed range 0 to %d\n", pinNum, pinMAX-1);
    return false;
  };
  if (gblinkDebug)
    printf("[clrOutput] Writing to pin field %d at GPIO register address %p\n", pinNum, (vAddrGPIO+offsetCLR));
  *(vAddrGPIO+offsetCLR) = 1<<pinNum; // write to "clr" bit
  return true;
};

void main(int argc, char *argv[]) {
  int pinNum;

  // validate arguments and/or provide help
  if ( (argc < 2) || (strcasecmp(argv[1],"-h")==0) || !isdigit(argv[1][0]) ) {
    printf("gblink: program to test direct memory control of GPIO pins by toggling pin voltage high<-->low\n");
    printf("        usage: gblink [ pin number, >=0, <=%d ]\n", pinMAX-1);
    printf("               where pin number is BCM2835 numbering system.  Not GPIO or Pi!\n");
    printf("        Install wiringPi and run 'gpio readall' to see pin map\n");
    printf("        Debug with 'sudo GBLINK_DEBUG=1 gblink <pin number>'\n");
    return;
  };

  // we need to run as root to open and map memory
  if (geteuid() != 0) {
    fprintf(stderr, "[?%s:] Must be run as root.  Try 'sudo %s <pin number>'\n", argv[0], argv[0]);
    exit(EXIT_FAILURE);
  };

  // do we want to provide debugging information along the way?
  if (getenv("GBLINK_DEBUG") != NULL) {
    printf("[gblink:] Debug mode enabled\n");
    gblinkDebug = true;
  };

  // OK, ready to go.  Catch ^C's to exit cleanly
  act.sa_handler = cntlCHandler;
  sigaction(SIGINT, &act, NULL);

  // which pin are we working with?
  pinNum = atoi(argv[1]);
  if (gblinkDebug)
    printf("[gblink:] Using GPIO pin %d\n", pinNum);

  // enable that pin for output
  if ( !enableGPIO(pinNum) ) {
    perror("[gblink:] Can't enable GPIO output.  ");
    exit(EXIT_FAILURE);
  };

  // until we're told to stop, just keep blinking
  while (keepBlinking) {
    if (gblinkDebug) printf("[gblink:] Turning pin ON\n");
    setOutput(pinNum);
    sleep(2);
    if (gblinkDebug) printf("[gblink:] Turning pin OFF\n");
    clrOutput(pinNum);
    sleep(5);
  };

  /* ^C terminate the loop above and drops into this cleanup code */
  printf("[gblink:] CNTL-C halt.  Clean up and exit.\n");
  if (alreadyMapped) {
    munmap(vAddrGPIO,getpagesize());
    close(memFH);
  };

  return;
};

答案 2 :(得分:2)

我相信上面的InfinitelyManic代码需要进行小的修正。

在set_pin:/ clear_pin:sections中,而不是

//        ldr r2, [r3]          // get content of GPCLR0                                                                            
//        orr r2, r2, #1<<23    // set PIN 23 to 1 ==> set pin voltage to low

我认为替换它们的单行应该是

    mov r2, #1<<23          // set PIN 23 to 1 ==> set pin voltage to low (clear)

在用

写入寄存器之前
    str r2,[r3]             // set PIN 23 @ GPCLR0 

即写入而不是复制并重写该寄存器。 (我已经将限流电阻连接到23而不是像InfinitelyManic那样使用引脚18.根据SEL和SET / CLR寄存器引用调整自己的代码。)

此更改仅设置SET / CLR寄存器中的一位(将其他位写为0),而不是复制然后重写整个寄存器(可能会再次设置其他位)。我原本以为重写一些其他代码正在管理和已经设置的位不会有害,但在所有Raspberry Pi模型中都不是这样。数据手册非常清楚:将1写入寄存器插槽会导致引脚电压置位或复位。目前尚不清楚的是,在Pi的不同型号上,该寄存器中表示的其他引脚可能连接到其他硬件,再次设置/清除它们可能会在这些设备中产生意外后果(显然)。

InfinitelyManic的代码复制并重写了寄存器,工作在Pi-0模型(修订版代码920093),我的C语言翻译(之前的帖子,随后编辑到此更改)。但是在Pi-1B(修订版代码000e)上,SET工作但CLR失败了。或者更确切地说,CLR将该引脚上的电压重置为0,但也会挂起系统,从而导致电源循环。

许多实验导致了一个声明 - 复制和重写而不仅仅是写作 - 作为问题的根源。在Pi-0上运行Stretch,K 4.9.66 +,Pi-1B运行Jessie,K 4.9.35和Pi-1B运行Openelec 8.0.4又名Kodi 17.3,K 4.9.30,测试和验证了两个版本的代码。两种形式的代码都在Pi-0上运行;复制和重写版本将Jessie和Openelec挂在了Pi-1B上。