我试图让一个LED连接到覆盆子pi上的gpio引脚18时遇到问题。我用C测试了我的设置,并确认这不是问题,但我的汇编代码就是问题。
这是我到目前为止的代码:
template<typename T, void (T::*M)() = &T::f>
void g() {
T t;
(t.*M)();
}
我设法使用GPIO 47引脚。但是,当我将其更改为使用引脚18时,这就是我遇到问题的地方。在此先感谢您的帮助,非常感谢!
答案 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上。