ElasticBottle
Posts: 6
Joined: Tue Jul 02, 2019 8:30 am

Help WIth GPIO C module

Tue Jul 02, 2019 8:46 am

Hi guys,

I am new to Raspberry Pi, and have been trying to mess around with it in C.

I am running this on a bare-metal Pi, following along this page: http://cs107e.github.io/assignments/assign2/#submit

Just to be clear, I am not a student there, just a regular kid trying to learn operating systems using their material. Otherwise, I would have approached the staff there for help instead.

With that clarified, below's a bit more on my specific problem:

Everything is ran on a rpi 1 model A+

I am trying to write a module.

The gpio_write function that sets the GPIO pin module to be SET(on) or CLR(off) depending on the value 1 or 0.

The gpio_read module then returns whether a particular pin has been most recently SET or CLR by reading the LEV register.

Code: Select all

#define GPIO_CLR0 ((unsigned int *) 0x20200028)

#define GPIO_SET0 ((unsigned int *) 0x2020001c)

#define GPIO_LEV0 ((volatile unsigned int *) 0x20200034)

#define SET (int) 1
#define BYTEDIFF 4


void gpio_write(unsigned int pin, unsigned int value) {
    if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) { return;}
    int* setting_loc;
    if (value) {
        setting_loc = GPIO_SET0 + (BYTEDIFF * (pin / 32));
    }
    else {
        setting_loc = GPIO_CLR0 + (BYTEDIFF * (pin / 32));
    }
    *setting_loc = (SET << (pin % 32));
}

unsigned int gpio_read(unsigned int pin) {
    if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) { 
        return GPIO_INVALID_REQUEST; 
    }
    volatile unsigned int* loc = GPIO_LEV0 + (BYTEDIFF * (pin / 32));
    int is_set = (SET << (pin % 32)) & *loc;
    return is_set >> (pin % 32);
}
In order to ensure that everything is working, I created a test module with a test function, but the assert keeps failing. Am I doing something wrong?

Assert is a module that blink the Pi's red light when failed, and shows green when it passes. It was provided in the starter file https://github.com/cs107e/lab2/blob/mas ... g/assert.h so I doubt it's wrong

Code: Select all

// Tests for the SET and CLR register in gpio module
// Does so by looping over all GPIO pins except 14, 15, 37, and 42
// Sets each pin to be high, asserts, and then sets it to low and asserts
void test_gpio_read_write_function() {
    gpio_write(55, 1);
    assert(gpio_read(55) == GPIO_INVALID_REQUEST);
     
    for (int i = GPIO_PIN0; i <= GPIO_PIN_LAST; i ++) {
        if (i == GPIO_TX || i == GPIO_RX || i == GPIO_PIN35 || i == GPIO_PIN47){
            continue;
        } 
        gpio_write(i, 1);
        assert(gpio_read(i) == 1);
        gpio_write(i, 0);
        assert(gpio_read(i) == 0);
    }
}

User avatar
joan
Posts: 14197
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Help WIth GPIO C module

Tue Jul 02, 2019 9:14 am

Could you confirm you are using bare metal, i.e. you are not running this program under Linux?

User avatar
mahjongg
Forum Moderator
Forum Moderator
Posts: 12125
Joined: Sun Mar 11, 2012 12:19 am
Location: South Holland, The Netherlands

Re: Help WIth GPIO C module

Tue Jul 02, 2019 11:29 am

Doesn't seem to be a "beginners" subject, and the assignment seems to be to do some bare metal programming, so moving this to bare metal.

LdB
Posts: 1210
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help WIth GPIO C module

Tue Jul 02, 2019 2:42 pm

hmmm I seem to only remember the Pi1 having 54 GPIO

I am very dubious of this .. check how many GPIO a Pi1 has and even if it exists does it even go to the real world?

Code: Select all

gpio_write(55, 1); 

ElasticBottle
Posts: 6
Joined: Tue Jul 02, 2019 8:30 am

Re: Help WIth GPIO C module

Tue Jul 02, 2019 3:49 pm

mahjongg wrote: Doesn't seem to be a "beginners" subject, and the assignment seems to be to do some bare metal programming, so moving this to bare metal.
Alright, thank you so much for the re-categorization! I apologize for my lack of experience here.
joan wrote: Could you confirm you are using bare metal, i.e. you are not running this program under Linux?
Yes I'm using bare metal! Not sure how to confirm it though, I compile using the GCC -ffreestanding flag if that helps. And if it's any more help, I have also pushed on assembly code that worked before
LdB wrote: hmmm I seem to only remember the Pi1 having 54 GPIO

I am very dubious of this .. check how many GPIO a Pi1 has and even if it exists does it even go to the real world?

Code: Select all

gpio_write(55, 1); 


Yes! You're right, attempting to set 55 is to make sure that my program handles dubious request properly. In case of

Code: Select all

 gpio_write(...)
function, it simply returns without doing any setting or clearing, the read function returns an invalid request

LdB
Posts: 1210
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help WIth GPIO C module

Tue Jul 02, 2019 6:29 pm

This is is a really weird test

Code: Select all

if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST)
pin is unsigned so it can't go negative and the GPIO start from zero so it seems to be nonsense.

Now to what you problem is ... C POINTER ARITHMETIC

What exactly do you think this does?

Code: Select all

volatile unsigned int* loc = GPIO_LEV0 + (BYTEDIFF * (pin / 32));
Give you the tip the pointer doesn't equal what you think it does ... try removing the BYTEDIFF

Code: Select all

volatile unsigned int* loc = GPIO_LEV0 +  (pin / 32);
GPIO_LEV0 is a pointer when you add to it already multiplies by the size of the thing it points to as always happens in C

ElasticBottle
Posts: 6
Joined: Tue Jul 02, 2019 8:30 am

Re: Help WIth GPIO C module

Wed Jul 03, 2019 4:09 am

LdB wrote:
Tue Jul 02, 2019 6:29 pm
This is is a really weird test

Code: Select all

if (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST)
pin is unsigned so it can't go negative and the GPIO start from zero so it seems to be nonsense.
I see, so does simply changing it to:

Code: Select all

 if (pin > GPIO_PIN_LAST)
make the check better? as just a form of safeguard against typo
LdB wrote:
Tue Jul 02, 2019 6:29 pm
Now to what you problem is ... C POINTER ARITHMETIC

What exactly do you think this does?

Code: Select all

volatile unsigned int* loc = GPIO_LEV0 + (BYTEDIFF * (pin / 32));
Give you the tip the pointer doesn't equal what you think it does ... try removing the BYTEDIFF

Code: Select all

volatile unsigned int* loc = GPIO_LEV0 +  (pin / 32);
GPIO_LEV0 is a pointer when you add to it already multiplies by the size of the thing it points to as always happens in C
THank You!! That was my mistake due to my poor understanding of C pointer arithmetic. But this clarifies it for me!

At the same time, I still can't seem to pass this portion of the code on my test:

Code: Select all

for (int i = GPIO_PIN0; i <= GPIO_PIN_LAST; i ++) {
        if (i == GPIO_TX || i == GPIO_RX || i == GPIO_PIN35 || i == GPIO_PIN47){
            continue;
        } 
        gpio_write(i, 1);
        assert(gpio_read(i) == 1);
        gpio_write(i, 0);
        assert(gpio_read(i) == 0);
 }
Just to check my understanding, writing 1 to the SET register for a pin will result in its LEV register to become 1too, is that correct?

Similarly, writing 1 to the CLR register for a pin will result in 0 in the LEV register for that pin.

LdB
Posts: 1210
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help WIth GPIO C module

Wed Jul 03, 2019 6:55 am

I would assume so but your write and read have same C pointer bug did you fix them?
Basically anywhere you used this "BYTEDIFF" is bugged

Yes this makes more sense

Code: Select all

if (pin > GPIO_PIN_LAST)

ElasticBottle
Posts: 6
Joined: Tue Jul 02, 2019 8:30 am

Re: Help WIth GPIO C module

Wed Jul 03, 2019 7:10 am

LdB wrote:
Wed Jul 03, 2019 6:55 am
I would assume so but your write and read have same C pointer bug did you fix them?
Basically anywhere you used this "BYTEDIFF" is bugged

Yes this makes more sense

Code: Select all

if (pin > GPIO_PIN_LAST)
Yeap!

my current code looks like this

Code: Select all

#define GPIO_CLR0 ((unsigned int *) 0x20200028)

#define GPIO_SET0 ((unsigned int *) 0x2020001c)

#define GPIO_LEV0 ((volatile unsigned int *) 0x20200034)

#define SET (int) 1

void gpio_write(unsigned int pin, unsigned int value) {
    if (pin > GPIO_PIN_LAST) { return;}
    unsigned int* setting_loc;
    if (value) {
        setting_loc = GPIO_SET0 + (pin / 32);
    }
    else {
        setting_loc = GPIO_CLR0 + (pin / 32);
    }
    *setting_loc = (SET << (pin % 32));
}

unsigned int gpio_read(unsigned int pin) {
    if (pin > GPIO_PIN_LAST) { 
        return GPIO_INVALID_REQUEST; 
    }
    volatile unsigned int* loc = GPIO_LEV0 + (pin / 32);
    unsigned int is_set = (SET << (pin % 32)) & *loc;
    return is_set >> (pin % 32);
}

LdB
Posts: 1210
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help WIth GPIO C module

Wed Jul 03, 2019 12:53 pm

Looks right now ... mine aren't much different

Code: Select all

bool gpio_output (unsigned int gpio, bool on) 
{
	if (gpio < MAX_GPIO_NUM) 		// Check GPIO pin number valid
	{
		volatile uint32_t* p;
		uint32_t bit = 1 << (gpio % 32);		// Create mask bit
		p = (on) ? &GPIO->GPSET[0] : &GPIO->GPCLR[0];	// Set pointer depending on on/off state
		p[gpio / 32] = bit;		// Output bit to register number	
		return true;			// Return true
	}
	return false;		// Return false
}

Code: Select all

bool gpio_input (unsigned int gpio) 
{
	if (gpio < MAX_GPIO_NUM)		// Check GPIO pin number valid
	{
		uint32_t bit = 1 << (gpio % 32);		// Create mask bit
		uint32_t mem = GPIO->GPLEV[gpio / 32];	// Read port level
		if (mem & bit) return true;	// Return true if bit set
	}
	return false;	// Return false
}

ElasticBottle
Posts: 6
Joined: Tue Jul 02, 2019 8:30 am

Re: Help WIth GPIO C module

Fri Jul 05, 2019 6:14 pm

LdB wrote:
Wed Jul 03, 2019 12:53 pm
Looks right now ... mine aren't much different

Code: Select all

bool gpio_output (unsigned int gpio, bool on) 
{
	if (gpio < MAX_GPIO_NUM) 		// Check GPIO pin number valid
	{
		volatile uint32_t* p;
		uint32_t bit = 1 << (gpio % 32);		// Create mask bit
		p = (on) ? &GPIO->GPSET[0] : &GPIO->GPCLR[0];	// Set pointer depending on on/off state
		p[gpio / 32] = bit;		// Output bit to register number	
		return true;			// Return true
	}
	return false;		// Return false
}

Code: Select all

bool gpio_input (unsigned int gpio) 
{
	if (gpio < MAX_GPIO_NUM)		// Check GPIO pin number valid
	{
		uint32_t bit = 1 << (gpio % 32);		// Create mask bit
		uint32_t mem = GPIO->GPLEV[gpio / 32];	// Read port level
		if (mem & bit) return true;	// Return true if bit set
	}
	return false;	// Return false
}
Thank you so much! And sorry for the delayed response! I'm currently serving national Service so I don't get to check back when I'm in the jungle.

Will give this a go when I reach home sometime over the weekends and post my results then.

Thanks so much again for your help LdB!

ElasticBottle
Posts: 6
Joined: Tue Jul 02, 2019 8:30 am

Re: Help WIth GPIO C module

Mon Jul 08, 2019 9:31 am

LdB wrote: Looks right now ... mine aren't much different

Code: Select all

bool gpio_output (unsigned int gpio, bool on) 
{
	if (gpio < MAX_GPIO_NUM) 		// Check GPIO pin number valid
	{
		volatile uint32_t* p;
		uint32_t bit = 1 << (gpio % 32);		// Create mask bit
		p = (on) ? &GPIO->GPSET[0] : &GPIO->GPCLR[0];	// Set pointer depending on on/off state
		p[gpio / 32] = bit;		// Output bit to register number	
		return true;			// Return true
	}
	return false;		// Return false
}

Code: Select all

bool gpio_input (unsigned int gpio) 
{
	if (gpio < MAX_GPIO_NUM)		// Check GPIO pin number valid
	{
		uint32_t bit = 1 << (gpio % 32);		// Create mask bit
		uint32_t mem = GPIO->GPLEV[gpio / 32];	// Read port level
		if (mem & bit) return true;	// Return true if bit set
	}
	return false;	// Return false
}
Finally got the chance to try to code out and it worked!

Turns out, there seems to be some dead gpio pins on board my rpi :(

Thanks so much for your time and help LdB! And sorry for troubling you!

Return to “Bare metal, Assembly language”