Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 56 additions & 31 deletions pio/pio_blink/blink.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,78 +11,103 @@
#include "hardware/clocks.h"
#include "blink.pio.h"

/**
* This example demonstrates using PIO to flash four LEDs at different frequencies.
* On RP2040 and RP2350A those GPIOs are all in the same "PIO range" of 0 - 31 (so a single PIO is used).
* On RP2350B the first two GPIOs are in the "lower PIO range" of 0 - 31 and the
* next two LEDs are in the "higher PIO range" of 16 - 47 (so two PIOs are used).
*/

void blink_pin_forever(PIO pio, uint sm, uint offset, uint pin, uint freq);

// by default flash leds on gpios 3-4
// By default flash LEDs on GPIOs 3 and 4
#ifndef PIO_BLINK_LED1_GPIO
#define PIO_BLINK_LED1_GPIO 3
#define PIO_BLINK_LED2_GPIO (PIO_BLINK_LED1_GPIO + 1)
#endif

// and flash leds on gpios 5-6
// or if the device supports more than 32 gpios, flash leds on 32-33
// and also flash LEDs on GPIOs 5 and 6
// Or if the device has more than 32 gpios, also flash LEDs on GPIOs 32 and 33
#ifndef PIO_BLINK_LED3_GPIO
#if NUM_BANK0_GPIOS <= 32
#define PIO_BLINK_LED3_GPIO 5
#else
#define PIO_BLINK_LED3_GPIO 32
#endif
#define PIO_BLINK_LED4_GPIO (PIO_BLINK_LED3_GPIO + 1)
#endif

#define PIO_BLINK_LED1_FREQUENCY 4
#define PIO_BLINK_LED2_FREQUENCY 3
#define PIO_BLINK_LED3_FREQUENCY 2
#define PIO_BLINK_LED4_FREQUENCY 1

int main() {
setup_default_uart();

assert(PIO_BLINK_LED1_GPIO < 31);
assert(PIO_BLINK_LED3_GPIO < 31 || PIO_BLINK_LED3_GPIO >= 32);

// LED1 and LED2 are both expected to be in the "lower" range of PIO-addressable GPIOs
assert(PIO_BLINK_LED2_GPIO == PIO_BLINK_LED1_GPIO + 1);
assert((PIO_BLINK_LED1_GPIO < 32) && (PIO_BLINK_LED2_GPIO < 32));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

arguably, these asserts should be unnecessary - the code should be able to work whatever value is used.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it depends on the focus of the example - is it showing "here's how you flash two GPIOs in one PIO-range and two GPIOs in a different range" (which I think was the original intention?), or is it showing "here's totally generic (but more complicated) code for flashing any four GPIOs in any combination" ?
Perhaps I ought to add a comment highlighting the intention of the example? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I ought to add a comment highlighting the intention of the example?

Done.

// check LED3 and LED4 are both in the same range of PIO-addressable GPIOs
assert(PIO_BLINK_LED4_GPIO == PIO_BLINK_LED3_GPIO + 1);
assert(((PIO_BLINK_LED3_GPIO < 32) && (PIO_BLINK_LED4_GPIO < 32)) || ((PIO_BLINK_LED3_GPIO >= 16) && (PIO_BLINK_LED3_GPIO < 48) && (PIO_BLINK_LED4_GPIO >= 16) && (PIO_BLINK_LED4_GPIO < 48)));

// LED1 and LED2 are both controlled by the program loaded into pio[0] at offset[0]
// LED3 and LED4 are both controlled by the program loaded into pio[1] at offset[1] (which might be the same as pio[0] and offset[0] if LED3 and LED4 are both on GPIOs < 32)
// LED1 is controlled by sm[0]
// LED2 is controlled by sm[1]
// LED3 is controlled by sm[2]
// LED4 is controlled by sm[3]
PIO pio[2];
uint sm[2];
uint sm[4];
uint offset[2];

// Find a free pio and state machine and add the program
// Find a free PIO and state machine and add the program
bool rc = pio_claim_free_sm_and_add_program_for_gpio_range(&blink_program, &pio[0], &sm[0], &offset[0], PIO_BLINK_LED1_GPIO, 2, true);
hard_assert(rc);
printf("Loaded program at %u on pio %u\n", offset[0], PIO_NUM(pio[0]));

// Start led1 flashing
blink_pin_forever(pio[0], sm[0], offset[0], PIO_BLINK_LED1_GPIO, 4);
// Start LED1 flashing
blink_pin_forever(pio[0], sm[0], offset[0], PIO_BLINK_LED1_GPIO, PIO_BLINK_LED1_FREQUENCY);

// Claim the next state machine and start led2 flashing
pio_sm_claim(pio[0], sm[0] + 1);
blink_pin_forever(pio[0], sm[0] + 1, offset[0], PIO_BLINK_LED1_GPIO + 1, 3);
// Claim the next unused state machine and start LED2 flashing
sm[1] = pio_claim_unused_sm(pio[0], true);
blink_pin_forever(pio[0], sm[1], offset[0], PIO_BLINK_LED2_GPIO, PIO_BLINK_LED2_FREQUENCY);

if (PIO_BLINK_LED3_GPIO >= 32) {
// Find a free pio and state machine and add the program
rc = pio_claim_free_sm_and_add_program_for_gpio_range(&blink_program, &pio[1], &sm[1], &offset[1], PIO_BLINK_LED3_GPIO, 2, true);
if ((PIO_BLINK_LED3_GPIO >= 32) || (PIO_BLINK_LED4_GPIO >= 32)) {
// Find a free PIO and state machine and add the program
rc = pio_claim_free_sm_and_add_program_for_gpio_range(&blink_program, &pio[1], &sm[2], &offset[1], PIO_BLINK_LED3_GPIO, 2, true);
printf("Loaded program at %u on pio %u\n", offset[1], PIO_NUM(pio[1]));
} else {
// no need to load the program again
rc = true;
pio[1] = pio[0];
sm[1] = sm[0] + 2;
offset[1] = offset[0];
pio_sm_claim(pio[1], sm[1]);
sm[2] = pio_claim_unused_sm(pio[1], true);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should have a pio_claim_free_sm_and_add_for_gpio_range function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a @kilograham question to me! 😆
(Although I guess that potential function might be useful for @poetaster in raspberrypi/pico-sdk#2030 )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we should make the existing function clever enough to know not to add the same program to the same PIO twice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we should make the existing function clever enough to know not to add the same program to the same PIO twice.

Don't know if we can do that with the current API, as I think the PIO instruction memory is write-only?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be able to stash an array of pio_program_t structures. Worth seeing if it makes the API better.

}
hard_assert(rc);

// Start led3 flashing
blink_pin_forever(pio[1], sm[1], offset[1], PIO_BLINK_LED3_GPIO, 2);
// Start LED3 flashing
blink_pin_forever(pio[1], sm[2], offset[1], PIO_BLINK_LED3_GPIO, PIO_BLINK_LED3_FREQUENCY);

// Claim the next unused state machine and start LED4 flashing
sm[3] = pio_claim_unused_sm(pio[1], true);
blink_pin_forever(pio[1], sm[3], offset[1], PIO_BLINK_LED4_GPIO, PIO_BLINK_LED4_FREQUENCY);

// Claim the next state machine and start led4 flashing
pio_sm_claim(pio[1], sm[1] + 1);
blink_pin_forever(pio[1], sm[1] + 1, offset[1], PIO_BLINK_LED3_GPIO + 1, 1);
printf("All LEDs should be flashing\n");

// free up pio resources
pio_sm_unclaim(pio[1], sm[1] + 1);
if (PIO_BLINK_LED3_GPIO >= 32) {
pio_remove_program_and_unclaim_sm(&blink_program, pio[1], sm[1], offset[1]);
// free up PIO resources
pio_sm_unclaim(pio[1], sm[3]);
if ((PIO_BLINK_LED3_GPIO >= 32) || (PIO_BLINK_LED4_GPIO >= 32)) {
pio_remove_program_and_unclaim_sm(&blink_program, pio[1], sm[2], offset[1]);
} else {
pio_sm_unclaim(pio[1], sm[1]);
pio_sm_unclaim(pio[1], sm[2]);
}
pio_sm_unclaim(pio[0], sm[0] + 1);
pio_sm_unclaim(pio[0], sm[1]);
pio_remove_program_and_unclaim_sm(&blink_program, pio[0], sm[0], offset[0]);

// the program exits but the pio keeps running!
printf("All leds should be flashing\n");
// the program exits but the PIO keeps running!
printf("All LEDs should continue to flash\n");
}

void blink_pin_forever(PIO pio, uint sm, uint offset, uint pin, uint freq) {
Expand Down