1

I am attempting to write an API for interaction with the GPIO pins in a bare metal environment in C. However , sometimes when I attempt to turn on one of the GPIO pins it turns multiple on. Here is the API :

#include "gpio.h"

void gpio_setOutput(unsigned int gpioNum) {
  //Check if the value is valid
  if(gpioNum > 53) {
    return;
  }

  //The pointer to the section of memory that contains the function selector
  unsigned int* gpio_FunctionPointer = 0;

  //Figure out which 4 bytes of memory is the correct one for the gpioNum
  if(gpioNum < 10) {
    gpio_FunctionPointer = GPFSEL0;
  } else if(gpioNum < 20) {
    gpio_FunctionPointer = GPFSEL1;
  } else if(gpioNum < 30) {
    gpio_FunctionPointer = GPFSEL2;
  } else if(gpioNum < 40) {
    gpio_FunctionPointer = GPFSEL3;
  } else if(gpioNum < 50) {
    gpio_FunctionPointer = GPFSEL4;
  } else if(gpioNum < 60) {
    gpio_FunctionPointer = GPFSEL5;
  } else {
    return;
  }

  //Get the exact location of the 3 bits for the particular gpioNum
  unsigned int memPart = (gpioNum%10)*3;
  //Get the 4 bytes of memory at the function memory
  unsigned int bytes = *gpio_FunctionPointer;
  //Calaculate the 4 bytes to clear the current function setting of gpioNum
  unsigned int clearFlag = (0b111 << memPart);
  //calculate the 4 bytes in order to set the function to output for gpioNum
  unsigned int outputFlag = (0xFFFFFFF9 << memPart) | (0xFFFFFFF9 >> (32 - memPart));
  //Clear the function
  bytes = bytes | clearFlag;
  //Set the function
  bytes = bytes & outputFlag;
  //Put the 4 bytes back
  *gpio_FunctionPointer = bytes;

}
void gpio_outputSet(unsigned int gpioNum) {
  //Check if the value is valid
  if(gpioNum > 53) {
    return;
  }
  unsigned int* gpio_SetPointer = 0;
  unsigned int memPart = 0;

  //Figure out which part of the memory relates to gpioNum
  if(gpioNum < 32) {
    gpio_SetPointer = GPSET0;
    memPart = gpioNum;
  } else {
    gpio_SetPointer = GPSET1;
    memPart = gpioNum - 32;
  }
  //Get the relivant bytes
  unsigned int bytes = *gpio_SetPointer;
  //Calculate the flag for the specific gpioNum
  unsigned int setFlag = 0b1 << memPart;

  //Add it to bytes
  bytes = bytes | setFlag;

  //Set the relevnat memory.
  *gpio_SetPointer = bytes;



}
void gpio_outputClr(unsigned int gpioNum) {
  //Check if the value is valid
  if(gpioNum > 53) {
    return;
  }
  unsigned int* gpio_SetPointer = 0;
  unsigned int memPart = 0;

  //Figure out which part of the memory relates to gpioNum
  if(gpioNum < 32) {
    gpio_SetPointer = GPCLR0;
    memPart = gpioNum;
  } else {
    gpio_SetPointer = GPCLR1;
    memPart = gpioNum - 32;
  }
  //Get the relivant bytes
  unsigned int bytes = *gpio_SetPointer;
  //Calculate the flag for the specific gpioNum
  unsigned int setFlag = 0b1 << memPart;

  //Add it to bytes
  bytes = bytes | setFlag;

  //Set the relevnat memory.
  *gpio_SetPointer = bytes;

}

The code should set the memory as specified in the bcm2835 arm peripherals manual so that the gpio function is set to output and the gpio pins are turned on and off (by setting the Clr or Set memory to 1) , however , it appears to be turing on multiple pins with one call of the function and I am unable to determine why.

When running this code on my own computer the bytes seem to be correctly set. So I assume there is something I need to do in addition to what this code is doing in order to interact with the GPIO. But I do not know what. What is wrong with my code that is causing multiple GPIO pins to come on when my code is only set to turn on one?

The full code can be found here

7
  • I'm not a bare metal guy, but I think you should indicate how you are assessing a pin's state as "on", especially if it is a pin you haven't previously configured.
    – goldilocks
    Apr 25, 2015 at 17:19
  • @goldilocks I am assessing if a pin is on by connecting it to an LED.
    – Ethan
    Apr 25, 2015 at 17:34
  • So if you have not specifically configured it in anyway, why would you assume it should be off, or that its state should be predictable or consistent? Or are you saying you've actually set the pin as an off output, but then it comes on?
    – goldilocks
    Apr 25, 2015 at 17:43
  • @goldilocks In another piece of the code I am making calls to the functions specified in here. These calls should in theory turn the LEDs on (by first calling gpio_setOutput and then gpio_outputSet and gpio_outputClr based upon if I want to turn the LED's on or off). When I call gpio_outputSet(7) , gpio pins 2,7,25 and 24 come on when only gpio pin 7 should come on. (The LEDS connected to them that is).
    – Ethan
    Apr 25, 2015 at 17:54
  • 1
    Oh. I have pre defined them before hand in some of my tests. But the bcm2835 arm peripherals manual said the should be off in the first place anyway.
    – Ethan
    Apr 25, 2015 at 18:52

1 Answer 1

2

Not sure what you are doing wrong.

For comparison the following works.

gpioReg is a uint32_t pointer to the base of the gpio peripheral.

#define GPSET0 7
#define GPSET1 8

#define GPCLR0 10
#define GPCLR1 11

#define GPLEV0 13
#define GPLEV1 14

#define GPPUD     37
#define GPPUDCLK0 38
#define GPPUDCLK1 39

#define PI_BANK (gpio>>5)

#define PI_BIT  (1<<(gpio&0x1F))

/* gpio modes. */

#define PI_INPUT  0
#define PI_OUTPUT 1
#define PI_ALT0   4
#define PI_ALT1   5
#define PI_ALT2   6
#define PI_ALT3   7
#define PI_ALT4   3
#define PI_ALT5   2

void gpioSetMode(unsigned gpio, unsigned mode)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
}

int gpioGetMode(unsigned gpio)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   return (*(gpioReg + reg) >> shift) & 7;
}

/* Values for pull-ups/downs off, pull-down and pull-up. */

#define PI_PUD_OFF  0
#define PI_PUD_DOWN 1
#define PI_PUD_UP   2

void gpioSetPullUpDown(unsigned gpio, unsigned pud)
{
   *(gpioReg + GPPUD) = pud;

   usleep(20);

   *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT;

   usleep(20);

   *(gpioReg + GPPUD) = 0;

   *(gpioReg + GPPUDCLK0 + PI_BANK) = 0;
}

int gpioRead(unsigned gpio)
{
   if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1;
   else                                         return 0;
}

void gpioWrite(unsigned gpio, unsigned level)
{
   if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
   else            *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
}
3
  • Thank you. This works well. I will attempt to compare it with my code in order to deduce the problem.
    – Ethan
    Apr 25, 2015 at 18:40
  • After comparing the code. I have found the problem. When setting the Clr and Set memory for the output. I am doing an OR function with what is already there and not setting the bytes directly like you are.
    – Ethan
    Apr 25, 2015 at 18:50
  • It's not clear why that doesn't work. The register is R/W. However the set and clear registers are meant to be used in the way I did in the posted code. It's safer, as you don't run the risk of overwriting another programs gpio settings.
    – joan
    Apr 25, 2015 at 19:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.