earlephilhower / arduino-pico

Raspberry Pi Pico Arduino core, for all RP2040 and RP2350 boards
GNU Lesser General Public License v2.1
2.03k stars 421 forks source link

Can't get PIO to run on RP2350 #2383

Closed ullibak closed 1 month ago

ullibak commented 1 month ago

Hi I am trying to run the quadrature encoder example that uses PIO functionality:

github.com/raspberrypi/pico-examples/tree/master/pio/quadrature_encoder

I have a rotary encoder attached to GPIOs 0 and 1 and tested it using VS Code and the Pico SDK 2.0 and it runs as expected. To run it on Arduino, I created a new file containing an almost exact copy of the example code (with the only exception that stdio_init_all() is not used):

#include <hardware/pio.h>
#include "quadrature_encoder.pio.h"

int new_value, delta, old_value = 0;
const uint PIN_AB = 0;
PIO pio = pio0;
const uint sm = 0;

void setup() {
  //stdio_init_all();
  Serial.begin(115200);

  // we don't really need to keep the offset, as this program must be loaded at offset 0
  uint offset = pio_add_program(pio, &quadrature_encoder_program);
  quadrature_encoder_program_init(pio, sm, PIN_AB, 0);
}

void loop() {
  new_value = quadrature_encoder_get_count(pio, sm);
  delta = new_value - old_value;
  old_value = new_value;
  Serial.printf("position %8d, delta %6d\n", new_value, delta);
  sleep_ms(500);
}

I then copied the file generated by pioasm.exe (quadrature_encoder.pio.h) from the build directory of the SDK example to the directory of the .INO file. quadrature_encoder.pio.zip

The INO files compiles and uploads without errors. But the output for "position" and "delta" is always zero,

Did I overlook something or is the PIO functionality not yet implemented in arduino-pico?

Thanks!

bendelathouwer commented 1 month ago

I always thought that the pio function was only plausible trough an external convertor?

bendelathouwer commented 1 month ago

https://arduino-pico.readthedocs.io/en/latest/sdk.html the last entry is exactly what i thought

ullibak commented 1 month ago

Yes, you need pioasm.exe to convert the PIO instructions to a mixed file containing C code and assembly code. The output of this process is the file that I put into the zip file above. This is the header file "quadrature_encoder.pio.h" that is included in the .INO file in line 2. This conversion can be done by hand by calling

.\pioasm.exe quadrature_encoder.pio quadrature_encoder.pio.h

on the Windows command line. When building and compiling the Pico examples the same process is run automatically and the generated file can be found in the build directory.

From my former experience with the old Pico board (RP2040) the code from the examples that come with the Pico SDK (and the generated .h file) can be used with arduino-pico. But I was not able to do so using the new Pico 2.

earlephilhower commented 1 month ago

A quick check of SoftwareSerial shows that the PIOs run fine on the RP2350. Connect GP0 to GP2 and you'll get hello coming out from the PIO input (since Quadature Encoder probably uses input on the PIOs)

#include <SoftwareSerial.h>
SoftwareSerial s(2, 3);
void setup() {
  s.begin(115200);
  Serial1.begin(115200);
  while (1) {
    Serial1.println("hello");
    Serial.printf("x\n");
    while (s.available()) {
      Serial.printf("%c", s.read());
    }
    delay(100);
  }
}

void loop() {
}

Your example has hardcoded PIOs and SMs which isn't legal here. Look at PIOProgram to ensure you aren't trying to use a PIO that the core already is running, If it's interrupt-based, make sure it's properly enabled because the IRQs in the SDK and the core do not always start identically.

ullibak commented 1 month ago

Many thanks! Tried your code and it works as expected. Yes it could be that a SM is already used for another purpose and I did not check this in my program. PIOProgram solves this in an elegant way. As a fast experiment, I tried different SMs on pio0 and pio1 and also different GPIO pins. No success.

Reading the C++ SDK documentation again, I found that in the examples given there, the GPIOs are initialized.

So I added pio_gpio_init() for both GPIOs used. And suddenly, the quadrature encoder worked!

#include <hardware/pio.h>
#include "quadrature_encoder.pio.h"

int new_value, delta, old_value = 0;
const uint PIN_AB = 0;
PIO pio = pio0;
const uint sm = 0;

void setup() {
  Serial.begin(115200);
  pio_gpio_init(pio, PIN_AB);
  pio_gpio_init(pio, PIN_AB + 1);
  // we don't really need to keep the offset, as this program must be loaded at offset 0
  uint offset = pio_add_program(pio, &quadrature_encoder_program);
  quadrature_encoder_program_init(pio, sm, PIN_AB, 0);
}

void loop() {
  // put your main code here, to run repeatedly:
  new_value = quadrature_encoder_get_count(pio, sm);
  delta = new_value - old_value;
  old_value = new_value;
  Serial.printf("position %8d, delta %6d\n", new_value, delta);
  sleep_ms(500);
}

The only mayor difference between my Arduino code and the example code of the Pico SDK is that I do not call stdio_init_all(). But this should have nothing to do with GPIOs. Or am I wrong?

Thanks again!

earlephilhower commented 1 month ago

It definitely wouldn't set pins to PIO! 😆

That call just inits USB and printf hooks on the SDK. We do all that stuff internally, so just dropping the call is the right thing here.

Looks like with the proper IO setup you're all good, so closing for now.