0

I want to be short. I got one Pico yesterday and I spent the evening yesterday making a blink.c (from Raspberry site) work on mi Pico through Linux and I made it work.

Now I want to make myblink.c which is a blink.c in Low Level programming (registers and all of that). But the last time I did a low level programming was 5 years ago on a MSP430, and I can't remember the basics anymore. Could someone please help me? Sorry. How can I make this low level programming in C compile a uf2 file which makes my raspberry pi pico blink his LED which is on the 25 pin? It compile, but the Pico doesnt do nothing. Obviously this code is not correct, what I can change to make it work? Thank you.

Below are myblink.c and CMakeLists.txt:

#include <stdio.h>
#include <stdlib.h>

#define SIO_BASE        0xd0000000
#define GPIO_IN         0xd0000004
#define GPIO_HI_IN      0xd0000008
#define GPIO_OUT        0xd0000010
#define GPIO_OUT_SET    0xd0000014
#define GPIO_OUT_CLR    0xd0000018
#define GPIO_OUT_XOR    0xd000001c
#define GPIO_OE         0xd0000020
#define GPIO_OE_SET     0xd0000024
#define GPIO_OE_CLR     0xd0000028
#define GPIO_OE_XOR     0xd000002c
#define GPIO_HI_OUT     0xd0000030

typedef unsigned int uint;

void write32(uint dst, uint val){
    uint dst_u = (uint)dst;
    dst_u = val;  
    return;
}

uint read32(uint src){
    uint src_u = (uint)src;
    return src;
}

int main(){
    uint gpoes = read32(GPIO_OE);
    gpoes |= (1<<25);
    write32(GPIO_OE, gpoes);

    //int i=0;

    while(1){
        //turn on pin 0
        write32(GPIO_OUT, 1<<25);
        //delay
        //while (i < 0x80000){
        //    i++;    
        //}
        //turn off pin 0
        //write32(GPIO_OUT_CLR, 1<<25);
        //delay
        //while (i < 0x80000){
        //    i++;    
        //}
    }        
}
cmake_minimum_required(VERSION 3.12)

# PUll in PICO SDK (must be before project)
include(pico_sdk_import.cmake)

project(myblink C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})

# Initialize the SDK
pico_sdk_init()

add_executable(myblink
    myblink.c
    )

# Pull in our pico_stdlib which pulls in common
target_link_libraries(myblink pico_stdlib)

# create map/bin/hex file etc.
pico_add_extra_outputs(myblink)
  • [Why is "Can someone help me?" not an actual question?](https://meta.stackoverflow.com/questions/284236/why-is-can-someone-help-me-not-an-actual-question). Please take the [tour](https://stackoverflow.com/tour) and review [How to ask](https://stackoverflow.com/help/how-to-ask). "Help me" is not a specific question. Does the code you have shown not compile? Does it crash? Does it get wrong results in some way? etc. Please describe one specific problem and ask one specific question. – kaylum Sep 22 '21 at 22:23
  • Done. Sorry, new here. – Matheus Costa Sep 22 '21 at 22:31
  • Shouldn't the read and write function use pointers? – Packa Sep 22 '21 at 22:34
  • I really don't know, as I said, I forgot the basics T-T. I'm gonna try tho. – Matheus Costa Sep 22 '21 at 22:35
  • Some style concerns: don't use home-brewed crap types like `typedef unsigned int uint;`. Use the standard types from `stdint.h`. In your case most likely `uint32_t`. These types should always be used in (embedded) C programming. – Lundin Sep 23 '21 at 08:33
  • Btw have you made sure that you can drive the LED directly from a GPIO pin? You have to check the datasheet about source/sink capabilities of the pin, then see how much current the LED is designed to draw. Typically they are between 5 to 20mA but not all GPIO pins can source/sink that much current. – Lundin Sep 23 '21 at 08:53
  • Lundin, thank you for your comment. In the datasheet the GP025 is the pin that contains the LED that is in the board. The led is not on a protoboard or in a separated board. Thank you for the stdint.h reccomendation, I will study and apply this method. – Matheus Costa Sep 23 '21 at 11:06
  • When you build the original blink.c example and made the blink.uf2 file there might have been a firmware.dis file, which gives a disassembly of the program. Maybe you'll see stuff in there that's missing from your version. I would have thought we need to setup the clocks and power settings and busses and port access before hitting the I/O register, but maybe not. – aMike Sep 24 '21 at 01:57
  • Chase down the source for `gpio_init` (called in the original blink.c) and `gpio_set_function` (called by gpio_init). You need to tell the RP2040 what function that pin has (I/O, not uart, spi, xip, ...). It's in `pico-sdk/src/rp2_common/hardware_gpio`. Do what they do to initialize more registers to be able to use the I/O port. – aMike Sep 24 '21 at 02:23

3 Answers3

1

Here is how it will execute in assembler:

void write32(uint dst, uint val){
    uint dst_u = (uint)dst; //a register gets the value from stack pop (dst) 
    dst_u = val; // that same register gets overwritten from stack pop (val)
    return; // ret and nothing was achieved by this function
}

If the reason for this function was to change the value of where GPIO_OUT is pointing with val then you have to pass it by reference:

void write32(uint &dst, uint val){
    dst = val;
}
XenoiS
  • 116
  • 3
0

I haven't used this specific target, but generally in case you have no pre-defined register map with all those registers already defined, then you can define them yourself by following the advise here: How to access a hardware register from firmware?

We can then fix your code like this:

// #include <stdio.h> avoid stdio.h on microcontrollers
#include <stdint.h>

#define SIO_BASE (*(volatile uint32_t*)0xd0000000u)
// ... and so on

// typedef unsigned int uint; get rid of home-made garage standard types

// replace "magic numbers" with meaningful names, "PORTX_PIN25" or whatever it might be called in hw
#define MEANINGFUL_NAME_HERE (1u << 25) 

int main(){
    GPIO_OE |= MEANINGFUL_NAME_HERE;

    while(1){
        GPIO_OUT |= MEANINGFUL_NAME_HERE;

        // write an actually working busy-delay loop by including volatile:
        for(volatile int i=0; i<0x80000; i++)
        {}

        GPIO_OUT_CLR |= MEANINGFUL_NAME_HERE;
        // I'm assuming GPIO_OUT &= ~MEANINGFUL_NAME_HERE; would work too

        for(volatile int i=0; i<0x80000; i++)
        {}
    }        
}

Probably you could also just do a bit toggle: GPIO_OUT_CLR ^= ... (bitwise XOR). Then you just need 1 delay loop.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thank you, I'm gonna test your solution in a few hours because I'm not in home at the moment. Then I will comment back. – Matheus Costa Sep 23 '21 at 11:02
  • It doesn't work. The Pico does nothing after compiling :( The LED doesn't turn on. – Matheus Costa Sep 23 '21 at 20:11
  • @MatheusCosta So maybe the problem is elsewhere. Maybe there are other data direction or routing registers that need to be set. Or maybe it's a hardware issue. Are the LEDs active high or active low? Did you check current rating of the GPIO pin like I advised in a comment? – Lundin Sep 24 '21 at 07:57
-1

Try correcting your read and write functions.

Here is what the read function should look like:

uint read32(addr){
    uint* base = (uint*)addr;
    uint value = *base;
    return value; }

Here is what the write function should look like:

void write32(void *dst, uint val){
    uint* dst_u = (uint*)dst;
    *dst_u = val;
    return;
}

After all, I don't know much about the Rpi pico but this seems to be one of the problems in your code.

Packa
  • 173
  • 14
  • Still doesn't blink, so there must be something more to be rewritten, thank you! – Matheus Costa Sep 22 '21 at 22:53
  • does the light stay on or is it off? – Packa Sep 22 '21 at 22:55
  • the led 25 is off, and after compilation it keeps off. The LED is in the board. Sorry, I said it wrong: the objective is to make it turn on before I rewrite the code to make it turn off, and then blink – Matheus Costa Sep 22 '21 at 22:56
  • 1
    This is just random bad advise out of the blue. `uint read32(addr)` isn't valid C. Using `void*` is a very bad idea in most cases, particularly when not called for. `return;` in functions returning `void` suggests that the programmer has no idea how functions work in C. And so on. – Lundin Sep 23 '21 at 08:37
  • @Lundin I never said that this was the only solution. And just for personal information, can you justify why it is a bad idea to be using `void*`. Thanks for your honest feedback though. – Packa Sep 23 '21 at 11:03
  • Thank you for your comment, I will learn about avoiding using void. – Matheus Costa Sep 23 '21 at 11:04
  • First of all these are CPU registers so `uint*` or `void*` is already completely wrong, because it needs to be a volatile qualified pointer of the appropriate size, in this case `volatile uint32_t*`. Then the functions here don't fill any purpose but to lag down register access, so they should simply be removed. As for why `void*` is bad, it's because it almost only exists in old style type-generic programming in C. Modern C has better alternatives in many cases. And you shouldn't be doing type-generic programming in a microcontroller program anyway since they should be deterministic. – Lundin Sep 23 '21 at 11:27