-
Notifications
You must be signed in to change notification settings - Fork 944
Add isr example with the pio #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 1 commit
9b91754
e65d58f
688f6d7
65e984f
0ff4e62
5ca0758
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
add_executable(isr) | ||
|
||
pico_generate_pio_header(isr ${CMAKE_CURRENT_LIST_DIR}/simply_isr.pio) | ||
|
||
target_sources(isr PRIVATE main.c) | ||
|
||
pico_enable_stdio_usb(isr 1) | ||
pico_enable_stdio_uart(isr 0) | ||
|
||
target_link_libraries(isr PRIVATE | ||
pico_stdlib | ||
hardware_pio | ||
) | ||
|
||
pico_add_extra_outputs(isr) | ||
|
||
# add url via pico_set_program_url | ||
example_auto_set_url(isr) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/** | ||
* Copyright 2022 (c) Daniel Garcia-Briseno | ||
* | ||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the | ||
* following conditions are met: | ||
* | ||
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following | ||
* disclaimer. | ||
* | ||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following | ||
* disclaimer in the documentation and/or other materials provided with the distribution. | ||
* | ||
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products | ||
* derived from this software without specific prior written permission. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include "pico/stdlib.h" | ||
|
||
#include "simply_isr.pio.h" | ||
// the sdk doesn't give us functions for figuring out which | ||
// isr source we're using, so we have to do it manually. | ||
#include "hardware/regs/pio.h" | ||
|
||
/** | ||
* These defines represent each state machine. | ||
* The value is the bit in the IRQ register that | ||
* will be set by each state machine thanks to "irq wait 0 rel" | ||
*/ | ||
#define PIO_SM_0_IRQ 0b0001 | ||
#define PIO_SM_1_IRQ 0b0010 | ||
#define PIO_SM_2_IRQ 0b0100 | ||
#define PIO_SM_3_IRQ 0b1000 | ||
|
||
/** | ||
* This variable will shadow the IRQ flags set by the PIO state machines. | ||
* Typically you do not want to do work in ISRs because the main thread | ||
* has more important things to do. Because of that, when we get the ISR | ||
* I'm simply going to copy the state machine that fired the ISR into | ||
* this variable. | ||
* | ||
* Variable is volatile so that it doesn't get cached in a CPU register | ||
* in the main thread. Without this it's possible that you never see | ||
* irq_flags get set even though the ISR is firing all the time. | ||
* | ||
* Of course, you can really do whatever you want in the ISR, it's up to you. | ||
*/ | ||
volatile uint32_t irq_flags = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably makes no difference, but I wonder if a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. u8 would be more efficient here. I had a u32 because originally I had that register read function which returned a u32 even though I only cared about the lower 8 bits. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to use u8 |
||
|
||
/** | ||
* Reads a hardware register | ||
* | ||
* @note See datasheet and/or pico-sdk/src/rp2040/include/hardware/regs | ||
* for base addresses and offsets | ||
* @param[in] bank The base address of the register set usually <MODULE>_BASE | ||
* @param[in] offset The register offset, usually <REG_NAME>_OFFSET | ||
*/ | ||
uint32_t read_register(uint32_t bank, uint32_t offset) { | ||
return *((volatile io_rw_32*)(bank + offset)); | ||
} | ||
|
||
/** | ||
* Writes a hardware register | ||
* | ||
* @note See datasheet and/or pico-sdk/src/rp2040/include/hardware/regs | ||
* for base addresses and offsets | ||
* @param[in] bank The base address of the register set usually <MODULE>_BASE | ||
* @param[in] offset The register offset, usually <REG_NAME>_OFFSET | ||
* @param[in] value The value to write to the register | ||
*/ | ||
void write_register(uint32_t bank, uint32_t offset, uint32_t value) { | ||
*((volatile io_rw_32*)(bank + offset)) = value; | ||
} | ||
|
||
/** | ||
* This function is called when the IRQ is fired by the state machine. | ||
* @note See enable_pio_isrs for how to register this function to be called | ||
*/ | ||
void simply_isr_handler() { | ||
// Read the IRQ register to get the IRQ flags from the state machine | ||
// This tells me which state machine sent the IRQ | ||
irq_flags = read_register(PIO0_BASE, PIO_IRQ_OFFSET); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like you can also get this from https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2040/hardware_structs/include/hardware/structs/pio.h#L127-L130 which might be "less lowlevel" than defining your own Or there's also https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_pio/include/hardware/pio.h#L799-L822 which is probably even better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I didn't notice the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to use pio_interrupt_get. |
||
|
||
// IRQ_OFFSET is write 1 to clear, so by writing back the | ||
// value, we're acknowledging that we've serviced the interrupt. | ||
write_register(PIO0_BASE, PIO_IRQ_OFFSET, irq_flags); | ||
} | ||
|
||
/** | ||
* Lets the pico know that we want it to notify us of the PIO ISRs. | ||
* @note in simply_isr.pio we enable irq0. This tells the state machine | ||
* to send the ISRs to the core, we still need to tell the core | ||
* to send them to our program. | ||
*/ | ||
void enable_pio_isrs() { | ||
// Set the function that will be called when the PIO IRQ comes in. | ||
irq_set_exclusive_handler(PIO0_IRQ_0, simply_isr_handler); | ||
|
||
// Once that function is set, we can go ahead and allow the interrupts | ||
// to come in. You want to set the function before enabling the interrupt | ||
// just in case. The docs say if an IRQ comes in and there's no handler | ||
// then it will work like a breakpoint, which seems bad. | ||
irq_set_enabled(PIO0_IRQ_0, true); | ||
} | ||
|
||
/** | ||
* Loads simply_isr pio program into PIO memory | ||
*/ | ||
void load_pio_programs() { | ||
PIO pio = pio0; | ||
|
||
// Load the program into PIO memory | ||
uint offset = pio_add_program(pio, &simply_isr_program); | ||
dangarbri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Load the program to run in each state machine. | ||
// They are allowed to run the same program in memory. | ||
simply_isr_program_init(pio, 0, offset); | ||
simply_isr_program_init(pio, 1, offset); | ||
simply_isr_program_init(pio, 2, offset); | ||
simply_isr_program_init(pio, 3, offset); | ||
} | ||
|
||
/** | ||
* Writes to the tx fifo of the given state machine. | ||
* This will make the simply_isr program send an ISR to us! | ||
*/ | ||
void trigger_isr(int sm) { | ||
printf("Triggering ISR from state machine %d\n", sm); | ||
pio_sm_put_blocking(pio0, sm, 1); | ||
// ISR will fire from the pio right here thanks to above function. | ||
|
||
// Print the irq we expect based on the given state machine | ||
printf("Expected IRQ flags: 0x%08X\n", (1 << sm)); | ||
printf("Actual IRQ Flags: 0x%08X\n", irq_flags); | ||
|
||
// Here you could do work for the isr depending on which one it is. | ||
// Something like | ||
if (irq_flags & PIO_SM_0_IRQ) { | ||
// handle_sm0_irq(); | ||
} | ||
if (irq_flags & PIO_SM_1_IRQ) { | ||
// handle_sm1_irq(); | ||
} | ||
if (irq_flags & PIO_SM_2_IRQ) { | ||
// handle_sm2_irq(); | ||
} | ||
if (irq_flags & PIO_SM_3_IRQ) { | ||
// handle_sm3_irq(); | ||
} | ||
|
||
// clear irq flags now. | ||
irq_flags = 0; | ||
} | ||
|
||
int main() { | ||
// Init stdio | ||
stdio_init_all(); | ||
|
||
// Load simply_isr into memory | ||
load_pio_programs(); | ||
|
||
// Enable IRQs to respond to simply_isr | ||
enable_pio_isrs(); | ||
|
||
// simply_isr is programmed to fire an ISR when we write | ||
// to their tx fifo. So let's do that now. | ||
while (true) { | ||
// Fire state machine 0 | ||
trigger_isr(0); | ||
sleep_ms(1000); | ||
|
||
// Fire state machine 1 | ||
trigger_isr(1); | ||
sleep_ms(1000); | ||
|
||
// Fire state machine 2 | ||
trigger_isr(2); | ||
sleep_ms(1000); | ||
|
||
// Fire state machine 3 | ||
trigger_isr(3); | ||
sleep_ms(1000); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
; Copyright (c) 2022 Daniel Garcia-Briseno | ||
; | ||
; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the | ||
; following conditions are met: | ||
; | ||
; 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following | ||
; disclaimer. | ||
; | ||
; 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following | ||
; disclaimer in the documentation and/or other materials provided with the distribution. | ||
; | ||
; 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products | ||
; derived from this software without specific prior written permission. | ||
; | ||
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
; DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
.program simply_isr | ||
|
||
; Stall on OSR. Wait for software to send a signal to continue | ||
; by writing to the OSR | ||
out x, 1 | ||
|
||
; Do work here | ||
nop | ||
|
||
; Notify the software via irq that some work has been done | ||
; irq wait means to wait for software to acknowledge the irq before | ||
; continuing. rel means let the irq be relative to the state machine. | ||
; by using "0 rel" on all state machines, software will see a different | ||
; interrupt source for each state machine. Technically state machines | ||
; can set any irq source, so in order to know where the irq is coming from | ||
; it's best to set 0 rel | ||
irq wait 0 rel | ||
|
||
% c-sdk { | ||
/** | ||
* Initializer for the fake isr program program | ||
* @param[in] pio the PIO instance to use | ||
* @param[in] sm state machine to use for the PIO instance | ||
* @param[in] offset Offset into PIO memory to place the program into | ||
*/ | ||
static inline void simply_isr_program_init(PIO pio, uint sm, uint offset) { | ||
// Enable the IRQ source | ||
// The reason for doing interrupt0 + sm: | ||
// IRQ sources are enabled per irq flag. Since the irq flag being set depends on the state | ||
// machine because of the "0 rel", we want to make sure we're enabling the correct interrupt | ||
// source for the state machine the program is loaded into. | ||
pio_set_irq0_source_enabled(pio, (pis_interrupt0 + sm), true); | ||
// Make sure the interrupt starts cleared. It should already be cleared, so this should | ||
// basically be a no-op. I call it defensive programming. | ||
pio_interrupt_clear(pio, sm); | ||
|
||
// Build the configuration for the state machine | ||
pio_sm_config config = simply_isr_program_get_default_config(offset); | ||
|
||
// Set up autopull to pull the TX Fifo into the OSR | ||
// This is what actually makes the "out" instruction wait | ||
// for input from software. | ||
// params are (config, shift_right (ignored here), autopull (true), pull threshold (1 bit)) | ||
sm_config_set_out_shift(&config, true, true, 1); | ||
|
||
// Load the config and execute the state machine | ||
pio_sm_init(pio, sm, offset, &config); | ||
pio_sm_set_enabled(pio, sm, true); | ||
} | ||
%} |
Uh oh!
There was an error while loading. Please reload this page.