Showing preview only (403K chars total). Download the full file or copy to clipboard to get everything.
Repository: GitJer/Some_RPI-Pico_stuff
Branch: main
Commit: 0041b97507cc
Files: 162
Total size: 363.6 KB
Directory structure:
gitextract_myyc33ud/
├── Button-debouncer/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── button_debounce.cpp
│ ├── button_debounce.h
│ ├── button_debounce.pio
│ ├── main.cpp
│ └── micropython_integrated/
│ ├── README.md
│ ├── button_debounce.cpp
│ ├── button_debounce.h
│ ├── button_debounce.pio.h
│ ├── debounce.cpp
│ ├── debouncemodule.c
│ ├── debouncemodule.h
│ └── micropython.cmake
├── CMakeLists.txt
├── HCSR04/
│ ├── CMakeLists.txt
│ ├── HCSR04.cpp
│ ├── HCSR04.pio
│ └── README.md
├── LICENSE
├── Limited_1_wire/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── onewire.cpp
│ └── onewire.pio
├── PwmIn/
│ ├── CMakeLists.txt
│ ├── PwmIn.cpp
│ ├── PwmIn.pio
│ ├── PwmIn_4pins/
│ │ ├── CMakeLists.txt
│ │ ├── PWM4.cpp
│ │ ├── PwmIn.cpp
│ │ ├── PwmIn.h
│ │ └── PwmIn.pio
│ └── README.md
├── README.md
├── Rotary_encoder/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── pio_rotary_encoder.cpp
│ └── pio_rotary_encoder.pio
├── Rotational_shift_ISR/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── rotational_shift_ISR.cpp
│ └── rotational_shift_ISR.pio
├── SBUS/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── SBUS.cpp
│ ├── SBUS.pio
│ └── gpio_invert/
│ ├── CMakeLists.txt
│ └── SBUS.cpp
├── Two_sm_one_disabled/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_sm_one_disabled.cpp
│ └── two_sm_one_disabled.pio
├── Two_sm_one_disabled_with_irq/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_sm_one_disabled_with_irq.cpp
│ └── two_sm_one_disabled_with_irq.pio
├── Two_sm_simple/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_sm_simple.cpp
│ └── two_sm_simple.pio
├── Value_communication_between_two_sm_via_pins/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── value_communication_between_two_sm_via_pins.cpp
│ └── value_communication_between_two_sm_via_pins.pio
├── Z80/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── Z80.c
│ └── Z80.pio
├── blow_out_a_LED/
│ ├── CMakeLists.txt
│ ├── README.md
│ └── blow_led.cpp
├── button_matrix_4x4/
│ ├── 4x4_button_matrix.cpp
│ ├── 4x4_button_matrix.pio
│ ├── CMakeLists.txt
│ └── README.md
├── count_pulses_with_pause/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── count_pulses_with_pause.cpp
│ └── count_pulses_with_pause.pio
├── example_auto_set_url.cmake
├── handy_bits_and_pieces/
│ └── README.md
├── ledpanel/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── ledpanel.c
│ ├── ledpanel.h
│ ├── ledpanel.pio
│ └── ledpanel_worker.c
├── multiplication/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── multiplier.cpp
│ └── multiplier.pio
├── pico_sdk_import.cmake
├── sm_to_dma_to_buffer/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── sm_to_dma_to_buffer.cpp
│ └── sm_to_dma_to_buffer.pio
├── sm_to_dma_to_sm_to_dma_to_buffer/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── sm_to_dma_to_sm_to_dma_to_buffer.cpp
│ └── sm_to_dma_to_sm_to_dma_to_buffer.pio
├── state_machine_emulator/
│ ├── README.md
│ ├── config.py
│ ├── emulation.py
│ ├── examples/
│ │ ├── button_debounce/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── in_shift/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── irq_set_and_clear/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── ledpanel/
│ │ │ ├── c_program
│ │ │ ├── ledpanel.pio.h
│ │ │ └── pin_program
│ │ ├── multiplication/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── push_pull_auto/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── push_pull.pio.h
│ │ ├── rotational_shift/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── side_step/
│ │ │ ├── README.md
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── square_wave/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ └── stepper/
│ │ ├── c_program
│ │ ├── pin_program
│ │ └── pio_program.pio.h
│ ├── interface/
│ │ ├── __init__.py
│ │ ├── _interface_item.py
│ │ ├── _left_frame.py
│ │ ├── _mid_frame.py
│ │ ├── _output_frame.py
│ │ ├── _right_frame.py
│ │ ├── _toolbar.py
│ │ └── _tooltips.py
│ ├── main.py
│ └── state_machine/
│ ├── __init__.py
│ ├── _do_sideset.py
│ ├── _execute_instructions.py
│ ├── _push_pull.py
│ ├── _set_all_GPIO.py
│ └── _time_step.py
├── subroutines/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── subroutine.cpp
│ └── subroutine.pio
├── two_pio_programs_one_file/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_p_one_f.cpp
│ ├── two_p_one_f.pio
│ └── two_p_one_f.pio.h
└── ws2812_led_strip_120/
├── CMakeLists.txt
├── README.md
├── ws2812_led_strip_120.c
└── ws2812_led_strip_120.pio
================================================
FILE CONTENTS
================================================
================================================
FILE: Button-debouncer/CMakeLists.txt
================================================
add_executable(pio_button_debounce)
pico_generate_pio_header(pio_button_debounce ${CMAKE_CURRENT_LIST_DIR}/button_debounce.pio)
target_sources(pio_button_debounce PRIVATE button_debounce.cpp main.cpp)
target_link_libraries(pio_button_debounce PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(pio_button_debounce)
# add url via pico_set_program_url
example_auto_set_url(pio_button_debounce)
================================================
FILE: Button-debouncer/README.md
================================================
# Button debouncer using the Raspberry Pico PIO
## Update
The new version allows the use of all 8 PIO state machines, and thus debounce up to 8 gpio.
It also allows setting of the debounce time between 0.5 to 30 ms.
## Original text
When using a GPIO to read noisy input, such as a mechanical button, it may happen that the signal read by the microcontroller rapidly switches back and forth, which may lead to false detection of several button presses where only one was intended. To prevent this, some hardware solutions exist as well as software solutions. This project is a software debouncer that makes sure that only after the input signal has stabilized, the code will read the new value. The downside of debouncers is that they usually cost some processing time to function. For Arduino a simple debouncer can be found [here](https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce).
The nice thing about the [Raspberry Pico](https://www.raspberrypi.org/documentation/pico/getting-started) is that it has 8 programmable IO (PIO) blocks that work independent of the main cores. I wanted to try my hand at programming the PIO and decided to do something I hadn't already seen as an example: a debouncer. The debouncer runs on one of the state machines of a PIO instance (there are two PIO instances and each has 4 state machines.)
The c++ code contains a class that starts the PIO code and lets the user code read the debounced pin state. The PIO code is as follows:
jmp pin isone ; executed only once: is the pin currently 0 or 1?
iszero:
wait 1 pin 0 ; the pin is 0, wait for it to become 1
set x 31 ; prepare to test the pin for 31 times
checkzero:
jmp pin stillone ; check if the pin is still 1
jmp iszero ; if the pin has returned to 0, start over
stillone:
jmp x-- checkzero ; decrease the time to wait, or the pin has definitively become 1
isone:
wait 0 pin 0 ; the pin is 1, wait for it to become 0
set x 31 ; prepare to test the pin for 31 times
checkone:
jmp pin isone ; if the pin has returned to 1, start over
jmp x-- checkone ; decrease the time to wait
jmp iszero ; the pin has definitively become 0
## Explanation of PIO code
* Start with the assumption that the pin is in a steady state.
If it is currently 1, then go to 'isone'; if it is currently 0, then go to 'iszero'
* The code after 'isone' works as follows:
* Since the pin is 1 wait for a change to 0
* If that happens, set 31 into the x scratch register
* This is the amount of 'time' the debouncer will wait before switching over. The actual amount of time is also dependent on the clock divisor, and the fact that two jmp statements are executed for a test. Edit: in the updated version the debounce time can be set in ms.
* The program keeps checking if the input changes back to 1; and if so, start over at 'isone'
* If the input does not change back, complete the loop of counting down from 31
* If the x scratch register becomes 0, the signal has definitively switched to 0; start from 'iszero'
* The branch of 'iszero' works similarly, but is structured a little bit differently because the `jmp pin` statement jumps on 1, not 0
There is one more important aspect to consider: the user code needs to read the debounced pin. So, somehow information from the PIO state machine has to go to the user code. I have considered several options:
* Use the FIFO much like the [uart_rx](https://github.com/raspberrypi/pico-examples/tree/master/pio/uart_rx) example code
* Use the FIFO, but from the PIO code make it empty (IN NULL) for a 0 and fill it with something to indicate a 1, and test the FIFO content with pio_sm_get_rx_fifo_level, or pio_sm_is_rx_fifo_empty
* Use an interrupt, e.g. PIO0_IRQ_0 for a 0, and PIO0_IRQ_1 for a 1
* Use an interesting approach where no explicit communication between the user code and PIO code is used at all!
The option I chose is based on the fact that the user code can know where the PIO state machine program counter is during execution via `pio_sm_get_pc`. The debouncer code has a clear split between the part where the debounced value is 0 and the part where it is 1: If (offset+1 <= pc < offset+isone) the value is 0, if (pc >= offset+isone) the value is 1, where offset is the starting point of the program in the PIO memory, and pc is the program counter.
This approach keeps the code very small, and reading the debounced state is very quick.
## Does it actually work
Yes, it does. To show that the debouncer works, I have used another microcontroller to generate a repeating signal that bounces up and down a couple of times. In the figure below I have used a slightly different PIO code that sets a GPIO to 0 or 1 depending on the debounced state.

The blue line is the raw input signal. It bounces up and down a couple of times between point (a) and (b). The yellow line is the debounced pin state. It clearly follows the blue line, but skips over the bounces.
If the debouncing time is chosen too small the yellow line simply follows the blue line including the bounces, but with a slight delay.
================================================
FILE: Button-debouncer/button_debounce.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "button_debounce.pio.h"
#include "button_debounce.h"
// indicate if errors and warnings should print a message
// comment out for no messages
#define PRINT_ERRORS
// indicator that something is not used or not set
#define UNUSED -10
/*
* class that debounces gpio using the PIO state machines.
* up to 8 gpios can be debounced at any one time.
* the debounce time for each gpio can be set individually, default it is set a 10ms.
*/
Debounce::Debounce(void)
{
// indicate that currently there are no gpios debounced
for (int i = 0; i < 32; i++)
{
gpio_debounced[i] = UNUSED;
pio_debounced[i] = (PIO)NULL;
sm_debounced[i] = UNUSED;
offset[i] = UNUSED;
// conf[i] = (pio_sm_config) 0;
}
num_of_debounced = 0;
pio0_already_set = UNUSED;
pio1_already_set = UNUSED;
}
/*
* Request to debounce the gpio
* @param gpio: the gpio that needs to be debounced
* the value must be [0, 28] excluding 23, 24 and 25.
*/
int Debounce::debounce_gpio(uint gpio)
{
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
// check that the gpio is unused
if (gpio_debounced[gpio] != UNUSED)
{
#ifdef PRINT_ERRORS
printf("debounce warning: gpio is already debounced\n");
#endif
return -1;
}
// check if there are still sm available (there are 8, but other programs could also be using sm, which is checked later)
if (num_of_debounced == 8)
{
#ifdef PRINT_ERRORS
printf("debounce error: max 8 gpios can be debounced (no state machine available)\n");
#endif
return -1;
}
// Find a pio and sm:
// start with trying to use pio0
PIO pio = pio0;
// claim a state machine, no panic if non is available
uint sm = pio_claim_unused_sm(pio, false);
// check if this is a valid sm
if (sm == -1)
{
// pio0 did not deliver a sm, try pio1
pio = pio1;
// claim a state machine, no panic if non is available
sm = pio_claim_unused_sm(pio, false);
// check if this is a valid sm
if (sm == -1)
{
// also no sm from pio1, return an error
#ifdef PRINT_ERRORS
printf("debounce error: no state machine available\n");
#endif
return -1;
}
}
pio_debounced[gpio] = pio;
sm_debounced[gpio] = sm;
gpio_debounced[gpio] = gpio;
num_of_debounced += 1;
// check if the pio program has already been loaded:
if ((pio_debounced[gpio] == pio0) && (pio0_already_set != UNUSED))
{
offset[gpio] = offset[pio0_already_set];
conf[gpio] = conf[pio0_already_set];
}
else if ((pio_debounced[gpio] == pio1) && (pio1_already_set != UNUSED))
{
offset[gpio] = offset[pio1_already_set];
conf[gpio] = conf[pio1_already_set];
}
else
{
// load the pio program into the pio memory
offset[gpio] = pio_add_program(pio_debounced[gpio], &button_debounce_program);
// make a sm config
conf[gpio] = button_debounce_program_get_default_config(offset[gpio]);
// set the initial clkdiv to 10ms
sm_config_set_clkdiv(&conf[gpio], 10.);
if (pio_debounced[gpio] == pio0)
pio0_already_set = gpio;
else
pio1_already_set = gpio;
}
// set the 'wait' gpios
sm_config_set_in_pins(&conf[gpio], gpio); // for WAIT, IN
// set the 'jmp' gpios
sm_config_set_jmp_pin(&conf[gpio], gpio); // for JMP
// init the pio sm with the config
pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);
// enable the sm
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);
return 0;
};
/*
* Request to debounce the gpio
* @param gpio: the gpio that needs to be debounced
* the value must be a uint in the range [0, 28] excluding 23, 24 and 25.
* @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]
*/
int Debounce::set_debounce_time(uint gpio, float debounce_time)
{
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
if (debounce_time < 0.5)
{
#ifdef PRINT_ERRORS
printf("debounce error: debounce time must be > 0 ms\n");
#endif
return -1;
}
if (debounce_time > 30)
{
#ifdef PRINT_ERRORS
printf("debounce error: the maximum is 30 ms, if you need longer debounce times: instructions are in the code\n");
#endif
return -1;
}
/*
calculate clkdiv based on debounce time:
Note: the resulting debounce time will not be very precise, but probably within 5 to 10%
In the pio code it becomes clear that the debounce time is about 31*2 = 62 instructions.
The time in seconds when a clkdiv is applied then becomes: clkdiv * 62 / 125000000
In microseconds this is: clkdiv * 62/125, which is about half the clkdiv value.
Conversely: the clkdiv for a given debounce_time in miliseconds is: 2 * 1000 * debounce_time
The minimum clkdiv value is 1, the corresponding debounce time is about 500 microseconds
The maximum clkdiv value is 65535, the corresponding debounce time is about 33 milliseconds
If a longer debounce time is required, the pio code must be adapted to add some pauses. This is
indicated in the pio code.
*/
// stop the sm
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);
// calculate the clkdiv (see explanation above)
float clkdiv = 2. * debounce_time * 1000.;
// check that the clkdiv has a valid value
if (clkdiv < 1.0)
clkdiv = 1.0;
else if (clkdiv > 65535.)
clkdiv = 65535.;
// set the clkdiv for both pio
sm_config_set_clkdiv(&conf[gpio], clkdiv);
// do the init of the pio/sm
pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);
// enable the sm
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);
return 0;
};
/*
* Read the current value of the debounced the gpio
* @param gpio: the gpio whose value (low, high) is read
* the gpio must have previously been debounced using debounce_gpio()
*/
int Debounce::read(uint gpio)
{
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
// check that this gpio is indeed being debounced
if (gpio_debounced[gpio] == UNUSED)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio is not debounced\n");
#endif
return -1;
}
// read the program counter
uint pc = pio_sm_get_pc(pio_debounced[gpio], sm_debounced[gpio]);
// if it is at or beyond the "wait 0 pin 0" it has value 1, else 0
// in the pio code a public define called 'border' is set at that position
if (pc >= (offset[gpio] + button_debounce_border))
return 1;
else
return 0;
};
/*
* undebounce a previously debounced gpio
* @param gpio: the gpio that is no longer going to be debounced
* the gpio must have previously been debounced using debounce_gpio()
*/
int Debounce::undebounce_gpio(uint gpio)
{
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
// check that this gpio is indeed being debounced
if (gpio_debounced[gpio] == UNUSED)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio is not debounced\n");
#endif
return -1;
}
// disable the pio
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);
// save pio, sm and offset to - if possible - unclaim the sm and remove the program from pio memory
PIO pio_used = pio_debounced[gpio];
uint sm_used = sm_debounced[gpio];
int offset_used = offset[gpio];
// indicate that the gpio is not debounced
gpio_debounced[gpio] = UNUSED;
pio_debounced[gpio] = (PIO)NULL;
sm_debounced[gpio] = UNUSED;
offset[gpio] = UNUSED;
// unclaim the sm
pio_sm_unclaim(pio_used, sm_used);
// if this is the last gpio of a pio: remove the program, set pioX_already_set to UNUSED
int i;
for (i = 0; i < 32; i++)
{
// check if the pio is still in use (i.e. one of the sm belongs to this pio)
if (pio_debounced[i] == pio_used)
break;
}
// if i==32 it means that no other debounced gpio uses this pio
if (i == 32)
{
// remove the program
pio_remove_program(pio_used, &button_debounce_program, offset_used);
// indicate that the pio (either pio0 or pio1) is not set
if (pio_used == pio0)
pio0_already_set = UNUSED;
else
pio1_already_set = UNUSED;
}
// there is one less gpio being debounced
num_of_debounced--;
return 0;
}
================================================
FILE: Button-debouncer/button_debounce.h
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "button_debounce.pio.h"
/*
* class that debounces gpio using the PIO state machines.
* up to 8 gpios can be debounced at any one time.
* the debounce time for each gpio can be set individually, default it is set a 10ms.
*/
class Debounce
{
public:
/*
* constructor to debounce gpios
*/
Debounce(void);
/*
* Request to debounce the gpio
* @param gpio: the gpio that needs to be debounced
* the value must be [0, 28] excluding 23, 24 and 25.
*/
int debounce_gpio(uint gpio);
/*
* set the debounce time for a gpio
* @param gpio: the gpio for which the debounce time will be set
* the gpio must have previously been debounced using debounce_gpio()
* @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]
*/
int set_debounce_time(uint gpio, float debounce_time);
/*
* Read the current value of the debounced the gpio
* @param gpio: the gpio whose value (low, high) is read
* the gpio must have previously been debounced using debounce_gpio()
*/
int read(uint gpio);
/*
* undebounce (rebounce?) a previously debounced gpio
* @param gpio: the gpio that is no longer going to be debounced
* the gpio must have previously been debounced using debounce_gpio()
*/
int undebounce_gpio(uint gpio);
private:
// the number of instantiated buttons to debounce
uint num_of_debounced = 0;
// Note: yes, the below way of doing things is somewhat wastefull, but handy
// for each gpio indicate if it is debounced
int gpio_debounced[32];
// for each gpio the PIO used to debounce
PIO pio_debounced[32];
// for each gpio the sm used to debounce
int sm_debounced[32];
// for each gpio the location of the pio program in the memory
int offset[32];
// for each gpio the configurations of the pio sm
pio_sm_config conf[32];
// flags to indicate the pio has already been set (and by which gpio)
int pio0_already_set, pio1_already_set;
};
================================================
FILE: Button-debouncer/button_debounce.pio
================================================
; Debounce a gpio
; Explanation:
; - start with the assumption that the gpio is in a steady state.
; If it is currently 1, then go to 'isone'; if it is currently 0, then go to 'iszero'
; - the branch of 'isone' works as follows:
; wait for a change to 0
; if that happens, set 31 into the x scratch register
; this is the amount of 'time' the debouncer will wait before switching over
; the actual amount of time is also dependent on the clock divisor
; the program keeps checking if the input changes back to 1, if so, start over at 'isone'
; if the input does not change back, complete the loop of counting down from 31
; if the x scratch register becomes 0, the signal has definitively switched to 0:
; start from 'iszero'
; - the branch of 'iszero' works similarly, but note that a jmp pin statement always jumps on 1, not 0
; - if (offset+1 <= pc < offset+isone) the value is 0, if (pc >= offset+isone) the value is 1
; - The border between 0 and 1 in the code is taken as 'isone' which is made public as 'button_debounce_border'
.program button_debounce
jmp pin isone ; executed only once: is the gpio currently 0 or 1?
iszero:
wait 1 pin 0 ; the gpio is 0, wait for it to become 1
set x 31 ; prepare to test the gpio for 31 * 2 clock cycles
checkzero:
; nop [31] ; possible location to add some pauses if longer debounce times are needed
; Note: also insert a pause right after 'checkone' below
jmp pin stillone; check if the gpio is still 1
jmp iszero ; if the gpio has returned to 0, start over
stillone:
jmp x-- checkzero; the decrease the time to wait, or decide it has definitively become 1
isone:
wait 0 pin 0 ; the gpio is 1, wait for it to become 0
set x 31 ; prepare to test the gpio for 31 * 2 clock cycles
checkone:
; nop [31] ; possible location to add some pauses if longer debounce times are needed
; Note: also insert a pause right after 'checkzero' above
jmp pin isone ; if the gpio has returned to 1, start over
jmp x-- checkone; decrease the time to wait
jmp iszero ; the gpio has definitively become 0
; the c-code must know where the border between 0 and 1 is in the code:
.define public border isone
================================================
FILE: Button-debouncer/main.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "button_debounce.h"
/*
This code shows how to use the button debouncer that uses the PIO state machines.
It shows all functionality:
Instantiate the debouncer, e.g.: Debounce debouncer;
Request to debounce the gpio, e.g. gpio 3: debouncer.debounce_gpio(3)
set the debounce time for a gpio, e.g. set to 1ms: debouncer.set_debounce_time(3, 1);
Read the current value of the debounced the gpio, e.g. gpio 3: int v = debouncer.read(3);
undebounce (rebounce?) a previously debounced gpio, e.g. gpio 3: debouncer.undebounce_gpio(3);
This example code first debounces gpio 3 to 10, then in an infinite loop reads the current
value of the debounced gpios. During this loop one by one the gpios are undebounced and then
one by one debounced again.
*/
int main()
{
// necessary for printf
stdio_init_all();
printf("Start of debounce example\n");
// instantiate the debouncer
Debounce debouncer;
// debounce 8 gpio
debouncer.debounce_gpio(3);
debouncer.debounce_gpio(4);
debouncer.debounce_gpio(5);
debouncer.debounce_gpio(6);
debouncer.debounce_gpio(7);
debouncer.debounce_gpio(8);
debouncer.debounce_gpio(9);
debouncer.debounce_gpio(10);
// set different debounce times
// Note: an external puls generator that can vary the puls widts and an logic analyser
// was used during testing to verify that this indeed works.
// debouncer.set_debounce_time(3, 1);
// debouncer.set_debounce_time(4, 2);
// debouncer.set_debounce_time(5, 3);
// debouncer.set_debounce_time(6, 4);
// debouncer.set_debounce_time(7, 5);
// debouncer.set_debounce_time(8, 6);
// debouncer.set_debounce_time(9, 7);
// debouncer.set_debounce_time(10, 8);
// infinite loop that continues to show the debounced value of the gpios
// in the first part of the loop the debounced gpios are one by one UNdebounced (rebounced?)
// in the second part of the loop the gpios are again all debounced (redebounced?)
int sm;
while (true)
{
tight_loop_contents();
// loop that reads and prints the current values of the debounced gpios (or print an X if not debounced)
// one by one the gpios are undebounced
for (int stop_debounce = 3; stop_debounce <= 10; stop_debounce++)
{
printf("\nValue:\t");
for (int gpio = 3; gpio <= 10; gpio++)
{
int v = debouncer.read(gpio);
if (v != -1)
printf("%d\t", v);
else
printf("X\t");
}
sleep_ms(250);
// one by one UNdebounce the gpios
debouncer.undebounce_gpio(stop_debounce);
}
// loop that reads and prints the current values of the debounced gpios (or print an X if not debounced)
// one by one the gpios are debounced again
for (int debounce_again = 3; debounce_again <= 10; debounce_again++)
{
printf("\nValue:\t");
for (int gpio = 3; gpio <= 10; gpio++)
{
int v = debouncer.read(gpio);
if (v != -1)
printf("%d\t", v);
else
printf("X\t");
}
sleep_ms(250);
// one by one debounce the gpios
debouncer.debounce_gpio(debounce_again);
}
}
}
================================================
FILE: Button-debouncer/micropython_integrated/README.md
================================================
# Button debouncer using the Raspberry Pico PIO integrated into MicroPython
## Note
This is a version of the button debounce program that has to be compiled into MicroPython.
This is not what I want, but this worked and I'm still working on a version that is an external .mpy file that can be imported.
## How-to compile this code into MicroPython
This is a how-to for integrating cpp programs such as the button_debounce class into MicroPython.
Starting point:
- there is a working micropython environment for the RPI Pico
- there is a working cpp program (in this case: button_debounce)
Go to the rp2 port in the micropython directory:
```
cd /YOUR_PATH/micropython/ports/rp2
```
Test that compiling it produces a working micropython binary:
```
make clean
make
ls -l build-PICO/
```
The file 'firmware.uf2' should be present (and just built)
The module to be integrated in micropython will be located in the examples directory
```
cd /YOUR_PATH/micropython/examples/usercmodule
mkdir button_debounce
cd button_debounce
```
**The next text isn't needed if you use the files in this github repository! Skip to 'Add to overall cmake file for user-c-modules'**
Copy the cpp files to this directory. Do not forget to include the generated .pio.h file if needed:
```
cp /PATH_TO_ORIGINAL_CPP_CODE/button_debounce.* .
```
Now make the following files:
```
touch debounce.cpp
touch debouncemodule.h
touch debouncemodule.c
touch micropython.cmake
```
The content of these files is:
debounce.cpp:
```
extern "C" {
#include <debouncemodule.h>
#include "button_debounce.h"
Debounce debounce;
mp_obj_t debounce_gpio(mp_obj_t mp_gpio) {
const auto gpio = mp_obj_get_int(mp_gpio);
return mp_obj_new_int(debounce.debounce_gpio(gpio));
}
mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time) {
const auto gpio = mp_obj_get_int(mp_gpio);
const auto debounce_time = mp_obj_get_float(mp_debounce_time);
return mp_obj_new_int(debounce.set_debounce_time(gpio, debounce_time));
}
mp_obj_t read(mp_obj_t mp_gpio) {
const auto gpio = mp_obj_get_int(mp_gpio);
return mp_obj_new_int(debounce.read(gpio));
}
mp_obj_t undebounce_gpio(mp_obj_t mp_gpio) {
const auto gpio = mp_obj_get_int(mp_gpio);
return mp_obj_new_int(debounce.undebounce_gpio(gpio));
}
}
```
debouncemodule.h:
```
// Include MicroPython API.
#include "py/runtime.h"
// Declare the function we'll make available in Python as cppexample.cppfunc().
extern mp_obj_t debounce_gpio(mp_obj_t mp_gpio);
extern mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time);
extern mp_obj_t read(mp_obj_t mp_gpio);
extern mp_obj_t undebounce_gpio(mp_obj_t mp_gpio);
```
debouncemodule.c:
```
#include <debouncemodule.h>
// Define a Python reference to the function we'll make available.
// See example.cpp for the definition.
STATIC MP_DEFINE_CONST_FUN_OBJ_1(debounce_gpio_obj, debounce_gpio);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_debounce_time_obj, set_debounce_time);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(read_obj, read);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(undebounce_gpio_obj, undebounce_gpio);
// Define all properties of the module.
// Table entries are key/value pairs of the attribute name (a string)
// and the MicroPython object reference.
// All identifiers and strings are written as MP_QSTR_xxx and will be
// optimized to word-sized integers by the build system (interned strings).
STATIC const mp_rom_map_elem_t debounce_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_debounce) },
{ MP_ROM_QSTR(MP_QSTR_debounce_gpio), MP_ROM_PTR(&debounce_gpio_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_debounce_time), MP_ROM_PTR(&set_debounce_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&read_obj) },
{ MP_ROM_QSTR(MP_QSTR_undebounce_gpio), MP_ROM_PTR(&undebounce_gpio_obj) },
};
STATIC MP_DEFINE_CONST_DICT(debounce_module_globals, debounce_module_globals_table);
// Define module object.
const mp_obj_module_t debounce_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&debounce_module_globals,
};
// Register the module to make it available in Python.
// Note: the "1" in the third argument means this module is always enabled.
// This "1" can be optionally replaced with a macro like MODULE_CPPEXAMPLE_ENABLED
// which can then be used to conditionally enable this module.
MP_REGISTER_MODULE(MP_QSTR_debounce, debounce_user_cmodule, 1);
```
micropython.cmake:
```
add_library(usermod_debounce INTERFACE)
target_sources(usermod_debounce INTERFACE
${CMAKE_CURRENT_LIST_DIR}/button_debounce.cpp
${CMAKE_CURRENT_LIST_DIR}/debounce.cpp
${CMAKE_CURRENT_LIST_DIR}/debouncemodule.c
)
target_include_directories(usermod_debounce INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(usermod INTERFACE usermod_debounce)
```
## Add to overall cmake file for user-c-modules
In the directory with usercmodules add the button_debounce to the micropython.cmake file (you can use any editor, I used vi):
```
vi /YOUR_PATH/micropython/examples/usercmodule/micropython.cmake
```
This file now looks like:
```
# This top-level micropython.cmake is responsible for listing
# the individual modules we want to include.
# Paths are absolute, and ${CMAKE_CURRENT_LIST_DIR} can be
# used to prefix subdirectories.
# Add the C example.
# include(${CMAKE_CURRENT_LIST_DIR}/cexample/micropython.cmake)
# Add the CPP example.
# include(${CMAKE_CURRENT_LIST_DIR}/cppexample/micropython.cmake)
# Add the module.
include(${CMAKE_CURRENT_LIST_DIR}/button_debounce/micropython.cmake)
```
## Compiling
Go to the rp2 port in the micropython directory and make with the option to include the user_c_modules:
```
cd /YOUR_PATH/micropython/ports/rp2
make USER_C_MODULES=../../examples/usercmodule/micropython.cmake
```
This should result in a firmware.uf2 that includes the button_debounce module.
## Running
The firmware must be uploaded to the pico (turn off, button pressed, turn on, file explorer pops up, move firmware.uf2 into it).
Thonny can be used to interact with micropython.
I made the following script and uploaded it to the pico:
```
import debounce
import time
debounce.start()
debounce.debounce_gpio(15)
while True:
print(debounce.read(15))
time.sleep(0.1)
```
Note the 'debounce.start'. This is due to a problem I haven't solved yet: micropython loads the module with 'import debounce' but at a restart of a script using it, it doesn't run the constructor of the Debounce class again, therefore not initializing the variables. Thus, I've made a function 'start' to explicitly initialize the class variables.
================================================
FILE: Button-debouncer/micropython_integrated/button_debounce.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "button_debounce.pio.h"
#include "button_debounce.h"
// FIXME: micropython doesn't let the printf go through, so might as well not print
// indicate if errors and warnings should print a message
// comment out for no messages
// #define PRINT_ERRORS
// indicator that something is not used or not set
#define UNUSED -10
/*
* class that debounces gpio using the PIO state machines.
* up to 8 gpios can be debounced at any one time.
* the debounce time for each gpio can be set individually, default it is set a 10ms.
*/
// FIXME: micropython caused problems with the constructor, so now I've an
// empty constructor and the user needs to explicitly start the debounce
// with 'start'
Debounce::Debounce(void)
{}
/*
* start must be used to initialize the variables
*/
void Debounce::start(void)
{
// indicate that currently there are no gpios debounced
for (int i = 0; i < 32; i++)
{
gpio_debounced[i] = UNUSED;
pio_debounced[i] = (PIO)NULL;
sm_debounced[i] = UNUSED;
offset[i] = UNUSED;
// conf[i] = (pio_sm_config) 0;
}
num_of_debounced = 0;
pio0_already_set = UNUSED;
pio1_already_set = UNUSED;
has_started = true;
}
/*
* Request to debounce the gpio
* @param gpio: the gpio that needs to be debounced
* the value must be [0, 28] excluding 23, 24 and 25.
*/
int Debounce::debounce_gpio(uint gpio)
{
if (has_started==false)
{
#ifdef PRINT_ERRORS
printf("debounce error: the module needs to be started first with <module name>.start()\n");
#endif
return -1;
}
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
// check that the gpio is unused
if (gpio_debounced[gpio] != UNUSED)
{
#ifdef PRINT_ERRORS
printf("debounce warning: gpio is already debounced\n");
#endif
return -1;
}
// check if there are still sm available (there are 8, but other programs could also be using sm, which is checked later)
if (num_of_debounced == 8)
{
#ifdef PRINT_ERRORS
printf("debounce error: max 8 gpios can be debounced (no state machine available)\n");
#endif
return -1;
}
// Find a pio and sm:
// start with trying to use pio0
PIO pio = pio0;
// claim a state machine, no panic if non is available
int sm = pio_claim_unused_sm(pio, false);
// check if this is a valid sm
if (sm == -1)
{
// pio0 did not deliver a sm, try pio1
pio = pio1;
// claim a state machine, no panic if non is available
sm = pio_claim_unused_sm(pio, false);
// check if this is a valid sm
if (sm == -1)
{
// also no sm from pio1, return an error
#ifdef PRINT_ERRORS
printf("debounce error: no state machine available\n");
#endif
return -1;
}
}
pio_debounced[gpio] = pio;
sm_debounced[gpio] = sm;
gpio_debounced[gpio] = gpio;
num_of_debounced += 1;
// check if the pio program has already been loaded:
if ((pio_debounced[gpio] == pio0) && (pio0_already_set != UNUSED))
{
offset[gpio] = offset[pio0_already_set];
conf[gpio] = conf[pio0_already_set];
}
else if ((pio_debounced[gpio] == pio1) && (pio1_already_set != UNUSED))
{
offset[gpio] = offset[pio1_already_set];
conf[gpio] = conf[pio1_already_set];
}
else
{
// load the pio program into the pio memory
offset[gpio] = pio_add_program(pio_debounced[gpio], &button_debounce_program);
// make a sm config
conf[gpio] = button_debounce_program_get_default_config(offset[gpio]);
// set the initial clkdiv to 10ms
sm_config_set_clkdiv(&conf[gpio], 10.);
if (pio_debounced[gpio] == pio0)
pio0_already_set = gpio;
else
pio1_already_set = gpio;
}
// set the 'wait' gpios
sm_config_set_in_pins(&conf[gpio], gpio); // for WAIT, IN
// set the 'jmp' gpios
sm_config_set_jmp_pin(&conf[gpio], gpio); // for JMP
// init the pio sm with the config
pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);
// enable the sm
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);
return 0;
};
/*
* Request to debounce the gpio
* @param gpio: the gpio that needs to be debounced
* the value must be a uint in the range [0, 28] excluding 23, 24 and 25.
* @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]
*/
int Debounce::set_debounce_time(uint gpio, float debounce_time)
{
// check that the module has been started
if (has_started==false)
{
#ifdef PRINT_ERRORS
printf("debounce error: the module needs to be started first with <module name>.start()\n");
#endif
return -1;
}
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
if (debounce_time < 0.5)
{
#ifdef PRINT_ERRORS
printf("debounce error: debounce time must be > 0 ms\n");
#endif
return -1;
}
if (debounce_time > 30)
{
#ifdef PRINT_ERRORS
printf("debounce error: the maximum is 30 ms, if you need longer debounce times: instructions are in the code\n");
#endif
return -1;
}
/*
calculate clkdiv based on debounce time:
Note: the resulting debounce time will not be very precise, but probably within 5 to 10%
In the pio code it becomes clear that the debounce time is about 31*2 = 62 instructions.
The time in seconds when a clkdiv is applied then becomes: clkdiv * 62 / 125000000
In microseconds this is: clkdiv * 62/125, which is about half the clkdiv value.
Conversely: the clkdiv for a given debounce_time in miliseconds is: 2 * 1000 * debounce_time
The minimum clkdiv value is 1, the corresponding debounce time is about 500 microseconds
The maximum clkdiv value is 65535, the corresponding debounce time is about 33 milliseconds
If a longer debounce time is required, the pio code must be adapted to add some pauses. This is
indicated in the pio code.
*/
// stop the sm
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);
// calculate the clkdiv (see explanation above)
float clkdiv = 2. * debounce_time * 1000.;
// check that the clkdiv has a valid value
if (clkdiv < 1.0)
clkdiv = 1.0;
else if (clkdiv > 65535.)
clkdiv = 65535.;
// set the clkdiv for both pio
sm_config_set_clkdiv(&conf[gpio], clkdiv);
// do the init of the pio/sm
pio_sm_init(pio_debounced[gpio], sm_debounced[gpio], offset[gpio], &conf[gpio]);
// enable the sm
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], true);
return 0;
};
/*
* Read the current value of the debounced the gpio
* @param gpio: the gpio whose value (low, high) is read
* the gpio must have previously been debounced using debounce_gpio()
*/
int Debounce::read(uint gpio)
{
// check that the module has been started
if (has_started==false)
{
#ifdef PRINT_ERRORS
printf("debounce error: the module needs to be started first with <module name>.start()\n");
#endif
return -1;
}
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
// check that this gpio is indeed being debounced
if (gpio_debounced[gpio] == UNUSED)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio is not debounced\n");
#endif
return -1;
}
// read the program counter
int pc = pio_sm_get_pc(pio_debounced[gpio], sm_debounced[gpio]);
// if it is at or beyond the "wait 0 pin 0" it has value 1, else 0
// in the pio code a public define called 'border' is set at that position
if (pc >= (offset[gpio] + button_debounce_border))
return 1;
else
return 0;
};
/*
* undebounce a previously debounced gpio
* @param gpio: the gpio that is no longer going to be debounced
* the gpio must have previously been debounced using debounce_gpio()
*/
int Debounce::undebounce_gpio(uint gpio)
{
// check that the module has been started
if (has_started==false) {
#ifdef PRINT_ERRORS
printf("debounce error: the module needs to be started first with <module name>.start()\n");
#endif
return -1;
}
// check if the gpio is valid
if ((gpio > 28) || gpio == 23 || gpio == 24 || gpio == 25)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio should be 0 to 28 excluding 23, 24 and 25\n");
#endif
return -1;
}
// check that this gpio is indeed being debounced
if (gpio_debounced[gpio] == UNUSED)
{
#ifdef PRINT_ERRORS
printf("debounce error: gpio is not debounced\n");
#endif
return -1;
}
// disable the pio
pio_sm_set_enabled(pio_debounced[gpio], sm_debounced[gpio], false);
// save pio, sm and offset to - if possible - unclaim the sm and remove the program from pio memory
PIO pio_used = pio_debounced[gpio];
uint sm_used = sm_debounced[gpio];
int offset_used = offset[gpio];
// indicate that the gpio is not debounced
gpio_debounced[gpio] = UNUSED;
pio_debounced[gpio] = (PIO)NULL;
sm_debounced[gpio] = UNUSED;
offset[gpio] = UNUSED;
// unclaim the sm
pio_sm_unclaim(pio_used, sm_used);
// if this is the last gpio of a pio: remove the program, set pioX_already_set to UNUSED
int i;
for (i = 0; i < 32; i++)
{
// check if the pio is still in use (i.e. one of the sm belongs to this pio)
if (pio_debounced[i] == pio_used)
break;
}
// if i==32 it means that no other debounced gpio uses this pio
if (i == 32)
{
// remove the program
pio_remove_program(pio_used, &button_debounce_program, offset_used);
// indicate that the pio (either pio0 or pio1) is not set
if (pio_used == pio0)
pio0_already_set = UNUSED;
else
pio1_already_set = UNUSED;
}
// there is one less gpio being debounced
num_of_debounced--;
return 0;
}
================================================
FILE: Button-debouncer/micropython_integrated/button_debounce.h
================================================
//#include "py/runtime.h"
#include "button_debounce.pio.h"
/*
* class that debounces gpio using the PIO state machines.
* up to 8 gpios can be debounced at any one time.
* the debounce time for each gpio can be set individually, default it is set a 10ms.
*/
class Debounce
{
public:
/*
* constructor to debounce gpios
*/
Debounce(void);
/*
* start the debounce class
* FIXME: This is due to the way micropython starts a module (when
* restarting it doesn't run the constructor again)
*/
void start(void);
/*
* Request to debounce the gpio
* @param gpio: the gpio that needs to be debounced
* the value must be [0, 28] excluding 23, 24 and 25.
*/
int debounce_gpio(uint gpio);
/*
* set the debounce time for a gpio
* @param gpio: the gpio for which the debounce time will be set
* the gpio must have previously been debounced using debounce_gpio()
* @param debounce_time: the float debounce_time in milliseconds in the range [0.5, 30.]
*/
int set_debounce_time(uint gpio, float debounce_time);
/*
* Read the current value of the debounced the gpio
* @param gpio: the gpio whose value (low, high) is read
* the gpio must have previously been debounced using debounce_gpio()
*/
int read(uint gpio);
/*
* undebounce (rebounce?) a previously debounced gpio
* @param gpio: the gpio that is no longer going to be debounced
* the gpio must have previously been debounced using debounce_gpio()
*/
int undebounce_gpio(uint gpio);
private:
// indicate that the instantiated class has been started
// FIXME: this due to the micropython problem with starting a module and not running the constructor again
bool has_started = false;
// the number of instantiated buttons to debounce
uint num_of_debounced = 0;
// Note: yes, the below way of doing things is somewhat wastefull, but handy
// for each gpio indicate if it is debounced
int gpio_debounced[32];
// for each gpio the PIO used to debounce
PIO pio_debounced[32];
// for each gpio the sm used to debounce
int sm_debounced[32];
// for each gpio the location of the pio program in the memory
int offset[32];
// for each gpio the configurations of the pio sm
pio_sm_config conf[32];
// flags to indicate the pio has already been set (and by which gpio)
int pio0_already_set, pio1_already_set;
};
================================================
FILE: Button-debouncer/micropython_integrated/button_debounce.pio.h
================================================
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// --------------- //
// button_debounce //
// --------------- //
#define button_debounce_wrap_target 0
#define button_debounce_wrap 10
#define button_debounce_border 6
static const uint16_t button_debounce_program_instructions[] = {
// .wrap_target
0x00c6, // 0: jmp pin, 6
0x20a0, // 1: wait 1 pin, 0
0xe03f, // 2: set x, 31
0x00c5, // 3: jmp pin, 5
0x0001, // 4: jmp 1
0x0043, // 5: jmp x--, 3
0x2020, // 6: wait 0 pin, 0
0xe03f, // 7: set x, 31
0x00c6, // 8: jmp pin, 6
0x0048, // 9: jmp x--, 8
0x0001, // 10: jmp 1
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program button_debounce_program = {
.instructions = button_debounce_program_instructions,
.length = 11,
.origin = -1,
};
static inline pio_sm_config button_debounce_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + button_debounce_wrap_target, offset + button_debounce_wrap);
return c;
}
#endif
================================================
FILE: Button-debouncer/micropython_integrated/debounce.cpp
================================================
extern "C" {
#include <debouncemodule.h>
#include "button_debounce.h"
#include <stdio.h>
#include "py/obj.h"
Debounce debounce;
// FIXME: this is due to micropython not running the constructor again at re-importing the module
mp_obj_t start() {
// mp_printf(&mp_plat_print, "start\n");
debounce.start();
return mp_obj_new_int(1);
}
mp_obj_t debounce_gpio(mp_obj_t mp_gpio) {
const auto gpio = mp_obj_get_int(mp_gpio);
// mp_printf(&mp_plat_print, "debounce_gpio\n");
return mp_obj_new_int(debounce.debounce_gpio(gpio));
}
mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time) {
const auto gpio = mp_obj_get_int(mp_gpio);
const auto debounce_time = mp_obj_get_float(mp_debounce_time);
// mp_printf(&mp_plat_print, "set_debounce_time\n");
return mp_obj_new_int(debounce.set_debounce_time(gpio, debounce_time));
}
mp_obj_t read(mp_obj_t mp_gpio) {
const auto gpio = mp_obj_get_int(mp_gpio);
// mp_printf(&mp_plat_print, "read\n");
return mp_obj_new_int(debounce.read(gpio));
}
mp_obj_t undebounce_gpio(mp_obj_t mp_gpio) {
const auto gpio = mp_obj_get_int(mp_gpio);
// mp_printf(&mp_plat_print, "undebounce_gpio\n");
return mp_obj_new_int(debounce.undebounce_gpio(gpio));
}
}
================================================
FILE: Button-debouncer/micropython_integrated/debouncemodule.c
================================================
#include <debouncemodule.h>
// Define a Python reference to the function we'll make available.
// See example.cpp for the definition.
// FIXME: start shouldn't be necessary (see other files)
STATIC MP_DEFINE_CONST_FUN_OBJ_0(start_obj, start);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(debounce_gpio_obj, debounce_gpio);
STATIC MP_DEFINE_CONST_FUN_OBJ_2(set_debounce_time_obj, set_debounce_time);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(read_obj, read);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(undebounce_gpio_obj, undebounce_gpio);
// Define all properties of the module.
// Table entries are key/value pairs of the attribute name (a string)
// and the MicroPython object reference.
// All identifiers and strings are written as MP_QSTR_xxx and will be
// optimized to word-sized integers by the build system (interned strings).
STATIC const mp_rom_map_elem_t debounce_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_debounce) },
// FIXME: start shouldn't be necessary (see other files)
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&start_obj) },
{ MP_ROM_QSTR(MP_QSTR_debounce_gpio), MP_ROM_PTR(&debounce_gpio_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_debounce_time), MP_ROM_PTR(&set_debounce_time_obj) },
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&read_obj) },
{ MP_ROM_QSTR(MP_QSTR_undebounce_gpio), MP_ROM_PTR(&undebounce_gpio_obj) },
};
STATIC MP_DEFINE_CONST_DICT(debounce_module_globals, debounce_module_globals_table);
// Define module object.
const mp_obj_module_t debounce_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&debounce_module_globals,
};
// Register the module to make it available in Python.
// Note: the "1" in the third argument means this module is always enabled.
// This "1" can be optionally replaced with a macro like MODULE_CPPEXAMPLE_ENABLED
// which can then be used to conditionally enable this module.
MP_REGISTER_MODULE(MP_QSTR_debounce, debounce_user_cmodule, 1);
================================================
FILE: Button-debouncer/micropython_integrated/debouncemodule.h
================================================
// Include MicroPython API.
#include "py/runtime.h"
// Declare the function we'll make available in Python as cppexample.cppfunc().
// FIXME: start shouldn't be necessary (see other files)
extern mp_obj_t start();
extern mp_obj_t debounce_gpio(mp_obj_t mp_gpio);
extern mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time);
extern mp_obj_t read(mp_obj_t mp_gpio);
extern mp_obj_t undebounce_gpio(mp_obj_t mp_gpio);
================================================
FILE: Button-debouncer/micropython_integrated/micropython.cmake
================================================
add_library(usermod_debounce INTERFACE)
target_sources(usermod_debounce INTERFACE
${CMAKE_CURRENT_LIST_DIR}/button_debounce.cpp
${CMAKE_CURRENT_LIST_DIR}/debounce.cpp
${CMAKE_CURRENT_LIST_DIR}/debouncemodule.c
)
target_include_directories(usermod_debounce INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(usermod INTERFACE usermod_debounce)
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.12)
# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)
project(pico_examples 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()
include(example_auto_set_url.cmake)
add_subdirectory(blow_out_a_LED)
add_subdirectory(Button-debouncer)
add_subdirectory(button_matrix_4x4)
add_subdirectory(count_pulses_with_pause)
add_subdirectory(HCSR04)
add_subdirectory(ledpanel)
add_subdirectory(Limited_1_wire)
add_subdirectory(multiplication)
add_subdirectory(PwmIn)
add_subdirectory(Rotary_encoder)
add_subdirectory(Rotational_shift_ISR)
add_subdirectory(SBUS)
add_subdirectory(sm_to_dma_to_buffer)
add_subdirectory(sm_to_dma_to_sm_to_dma_to_buffer)
add_subdirectory(subroutines)
add_subdirectory(two_pio_programs_one_file)
add_subdirectory(Two_sm_one_disabled)
add_subdirectory(Two_sm_one_disabled_with_irq)
add_subdirectory(Two_sm_simple)
add_subdirectory(Value_communication_between_two_sm_via_pins)
add_subdirectory(ws2812_led_strip_120)
================================================
FILE: HCSR04/CMakeLists.txt
================================================
add_executable(HCSR04)
pico_generate_pio_header(HCSR04 ${CMAKE_CURRENT_LIST_DIR}/HCSR04.pio)
target_sources(HCSR04 PRIVATE HCSR04.cpp)
target_link_libraries(HCSR04 PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(HCSR04)
# add url via pico_set_program_url
example_auto_set_url(HCSR04)
================================================
FILE: HCSR04/HCSR04.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "HCSR04.pio.h"
// class that sets up and reads the HCSR04
// The HCSR04 works by giving it a 10 us pulse on its Trigger pin
// The distance to an object is represented by the length of the pulse on its Echo pin
class HCSR04
{
public:
// constructor
// input = pin connected to the 'Echo' pin of the HCSR04.
// ! NOTE: USE A VOLTAGE DIVIDER FOR THE INPUT (i.e. the Echo pin of the HCSR04)
// to go from 5V (which is needed by the HCSR04 module) to 3.3V
// output = pin connected to the 'Trig' pin of the HCSR04.
HCSR04(uint input, uint output)
{
// pio 0 is used
pio = pio0;
// state machine 0
sm = 0;
// configure the used pins
pio_gpio_init(pio, input);
pio_gpio_init(pio, output);
// load the pio program into the pio memory
uint offset = pio_add_program(pio, &HCSR04_program);
// make a sm config
pio_sm_config c = HCSR04_program_get_default_config(offset);
// set the 'in' pins, also used for 'wait'
sm_config_set_in_pins(&c, input);
// set the 'jmp' pin
sm_config_set_jmp_pin(&c, input);
// set the output pin to output
pio_sm_set_consecutive_pindirs(pio, sm, output, 1, true);
// set the 'set' pins
sm_config_set_set_pins(&c, output, 1);
// set shift direction
sm_config_set_in_shift(&c, false, false, 0);
// init the pio sm with the config
pio_sm_init(pio, sm, offset, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
}
// read the distance to an object in cm (0 cm means invalid measurement)
float read(void)
{
// value is used to read from the sm RX FIFO
uint32_t clock_cycles = 0;
// clear the FIFO: do a new measurement
pio_sm_clear_fifos(pio, sm);
// give the sm some time to do a measurement and place it in the FIFO
sleep_ms(100);
// check that the FIFO isn't empty
if (pio_sm_is_rx_fifo_empty(pio, sm))
{
return 0;
}
// read one data item from the FIFO
// Note: every test for the end of the echo puls takes 2 pio clock ticks,
// but changes the 'timer' by only one
clock_cycles = 2 * pio_sm_get(pio, sm);
// using
// - the time for 1 pio clock tick (1/125000000 s)
// - speed of sound in air is about 340 m/s
// - the sound travels from the HCSR04 to the object and back (twice the distance)
// we can calculate the distance in cm by multiplying with 0.000136
float cm = (float)clock_cycles * 0.000136;
return (cm);
}
private:
// the pio instance
PIO pio;
// the state machine
uint sm;
};
int main()
{
// needed for printf
stdio_init_all();
// the instance of the HCSR04 (Echo pin = 14, Trig pin = 15)
HCSR04 my_HCSR04(14, 15);
// infinite loop to print distance measurements
while (true)
{
// read the distance sensor and print the result
float cm = my_HCSR04.read();
printf("cm = %f\n", cm);
sleep_ms(100);
}
}
================================================
FILE: HCSR04/HCSR04.pio
================================================
; Description of the used algorithm
;
; start:
;
; Give a puls to the HCSR04 Trig pin to start the measurement
; According to the datasheet the pulse should be 10us long.
; Assume the Trig pin is currently 0, set it to 1, wait 10 us, set it to 0
; A length of 10 us, with a clock of 125MHz this is 1250 ticks, or binary: 10011100010.
; Assuming that the 10 us doesn't have to be very precise, round it down to 10011000000.
; The delay can be achieved by:
;
; set x 19 ; set x to 10011 (and clear the higher bits)
; mov ISR x ; copy x to ISR
; in NULL 6 ; shift in 6 more 0 bits
; mov x ISR ; move the ISR to x (which now contains 10011000000)
; delay1:
; jmp x-- delay1 ; count down to 0: a delay of (about) 10 us
;
; This results in a delay of about 10us.
;
; The distance to the object is encoded in the length of the pulse on the Echo pin
; Read the Echo pin (USE A VOLTAGE DIVIDER) wait until the input pulse becomes high
; Set the value 0xFFFFFFFF in x; this is the start of the 'timer'. This can be achieved by
; mov x ~NULL
;
; Now the value in x is decremented in a loop and each time the Echo pin is tested.
; If the Echo pin is 0, the value (0xFFFFFFFF - x) represents the length of the echo pulse.
; Note: each decrement of x and a test of the Echo pin is 2 pio clock cycles.
; Push the bit-inversed value of x, i.e. (0xFFFFFFFF - x) into the Rx FIFO
;
; According to the HCSR04 datasheet, we have to wait for 60ms before starting another measurement
; Use the same trick as above to create a delay. But now with values:
; 7500000 clock cycles = 11100100111000011100000, round down to 11100 + 18 * 0
;
; Go back to start
.program HCSR04
.wrap_target
; give a puls to the HCSR04 Trigger pin
set pins 1 ; set the trigger to 1
; delay for 10 us (the length of the trigger pulse)
set x 19 ; set x to 10011 (and clear the higher bits)
mov ISR x ; copy x to ISR
in NULL 6 ; shift in 6 more 0 bits
mov x ISR ; move the ISR to x (which now contains 10011000000)
delay1:
jmp x-- delay1 ; count down to 0: a delay of (about) 10 us
set pins 0 ; make the trigger 0 again, completing the trigger pulse
;
wait 1 pin 0 ; wait for the echo pin to rise
; start a counting loop to measure the length of the echo pulse
mov x ~NULL ; start with the value 0xFFFFFFFF
timer:
jmp x-- test ; count down
jmp timerstop ; timer has reached 0, stop count down
test:
jmp pin timer ; test if the echo pin is still 1, if so, continue counting down
timerstop: ; echo pulse is over (or timer has reached 0)
mov ISR ~x ; move the bit-inversed value in x to the ISR
push noblock ; push the ISR into the Rx FIFO
; delay for 60ms (advice from datasheet to prevent triggering on echos)
set x 28 ; set x to 11100
mov ISR x ; copy x to ISR
in NULL 18 ; shift in 18 more bits
mov x ISR ; move the ISR to x
delay2:
jmp x-- delay2 ; delay (about) 60 ms
.wrap ; start over
================================================
FILE: HCSR04/README.md
================================================
# HC-SR04 using the PIO code
The HC-SR04 is an ultrasonic distance measurement unit ([datasheet](https://cdn.sparkfun.com/datasheets/Sensors/Proximity/HCSR04.pdf)). Basically, it works by sending a puls to the Trig pin, and measuring the pulse on the Echo pin. The length of the Echo pulse represents the distance to an object in front of the device.
The HC-SR04 uses 5V while the Pico uses 3V3. Thus the Echo pin requires a [resistive divider](https://hackaday.com/2016/12/05/taking-it-to-another-level-making-3-3v-and-5v-logic-communicate-with-level-shifters/) to be save.
## The algorithm
The pio code to read the HC-SR04 has the following steps:
* Give a pulse on the Trig pin of the HC-SR04 to start the measurement. The datasheet indicates that the length of this pulse should be 10 us
* Measure the length of the pulse on the Echo pin
* Wait 60 ms before making a new measurement to be sure that no echos from previous measurements are found
Setting and reading pins are (if you've done it before) straight forward, but making and reading pulses of specific length weren't for me.
Making the HC-SR04 algorithm work involves two types of timing:
* a delay to make the trigger pulse
* measuring the length of the echo pulse
* a delay to ensure no echos of a previous measurement are received
The PIO doesn't seem to have timers, but each instruction takes one pio clock cycle, and these can be used to measure time.
## Delay in PIO
According to the datasheet, the trigger pulse should be 10 us. The pio clock runs at 125 MHz, so, the pulse should be 1250 cycles long. Here it is assumed that the clock divider is set to 1. If this has a different value, some of the timing calculations below will change. Creating the required delay can be achieved in the following way:
* 1250 cycles in binary is 10011100010
* assuming the pulse width doesn't need to be very precise, this can be rounded down to 10011000000
* this can be split into the 5 most significant digits (10011) and 6 additional 0 bits
* place this number in the x register in the following way (note that for this to work correctly, the shift direction needs to be set with`sm_config_set_in_shift(&c, false, false, 0);`):
``` pio
set x 19 ; set x to 10011 (and clear the higher bits)
mov ISR x ; copy x to ISR
in NULL 6 ; shift in 6 more 0 bits
mov x ISR ; move the ISR to x (which now contains 10011000000)
```
* Count down to 0 in a tight loop using:
``` pio
delay1:
jmp x-- delay1
```
This results in a delay of almost 10 us.
This same approach can be used to create a clock-cycle precise delay for any number between 0x00000000 and 0xFFFFFFFF. This may involve setting x and shifting it into the ISR, 5 bits at a time, several times. If the timing doesn't have to be precise, rounding down (or up) after the 5 most significant bits and shifting in further 0's, as done above, can save instructions.
The second delay that is needed is 60 ms. The same trick as above is used to create this delay. But now with the following values:
7500000 clock cycles are needed. In binary this is 11100100111000011100000. This is rounded down to 11100 + 18 * 0.
## Measuring duration of a pulse
To measure the duration of some event, in this case the length of the Echo pulse which represents the distance to an object, can be done by starting the x (or y) scratch register with 0xFFFFFFFF and counting down, testing for the stop criterion each iteration.
The counting down loop with test for the stop criterion looks like this:
```pio
timer:
jmp x-- test ; count down
jmp timerstop ; timer has reached 0, stop count down
test:
jmp pin timer ; test if the echo pin is still 1, if so, continue counting down
timerstop: ; echo pulse is over (or timer has reached 0)
mov ISR ~x ; move the bit-inverted value of x to the ISR
push noblock ; push the ISR into the RX FIFO
```
If x isn't 0, the statement `jmp x-- test` jumps to testing for the stop criterion (the Echo pin going low). If the stop criterion is true, or if the x register is 0 when arriving at the jmp statement, the bit-inverted value of x is moved to the ISR, which makes it positive, and is then pushed to the Rx FIFO.
## Calculation of distance
In the C/C++ code the value in the x scratch register is received. Each loop has two instructions (`jmp` with decrement, and `jmp` testing the pin) the amount of pio clock cycles becomes `2 * pio_sm_get(pio, sm)`.
For the calculation of the distance three more things are needed:
* one pio clock cycle takes 1 / 125 MHz = 0.000000008 s
* The speed of sound in air is about 340 m/s = 34000 cm/s
* The sound travels from the HC-SR04 to the object and back (twice the distance)
If those are factored in, the code to calculate the distance in cm becomes:
``` c
clock_cycles = 2*pio_sm_get(pio, sm);
cm = (float)clock_cycles * 0.000136;
```
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 GitJer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Limited_1_wire/CMakeLists.txt
================================================
add_executable(onewire)
pico_generate_pio_header(onewire ${CMAKE_CURRENT_LIST_DIR}/onewire.pio)
target_sources(onewire PRIVATE onewire.cpp)
target_link_libraries(onewire PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(onewire)
# add url via pico_set_program_url
example_auto_set_url(onewire)
================================================
FILE: Limited_1_wire/README.md
================================================
# 1-wire protocol for one device
This code is a pio implementation of the [1-wire protocol](https://en.wikipedia.org/wiki/1-Wire). The C++ implementation is rather limited: it is meant for only one device (the DS18B20 temperature sensor) and uses the default settings (12-bit conversion).
To make this I have used the [datasheet](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) as well as the implementation of [Paul Stoffregen](https://github.com/PaulStoffregen/OneWire) and [this pio implementation](https://www.raspberrypi.org/forums/viewtopic.php?t=304511#p1851030).
One of the interesting elements is that it uses the same pin for set, out, sideset, out, and in! (I should have included a 'jmp pin' somewhere. :)
================================================
FILE: Limited_1_wire/onewire.cpp
================================================
/*
This code uses pio code to read a single DS18B20 temperature sensor.
It is not suited to read more than one sensor: it does not use the parts
of the 1-wire protocol that allowes more than one sensor.
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "onewire.pio.h"
// the pin to which the sensor is connected.
// Note that an external pullup resistor of 4.7k is needed on this pin
#define OW_PIN 15
// ---------------------------------------------------------------------------------
// The 1-Wire CRC scheme is described in Maxim Application Note 27:
// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products"
//
// Dow-CRC using polynomial X^8 + X^5 + X^4 + X^0
// Tiny 2x16 entry CRC table created by Arjen Lentz
// See http://lentz.com.au/blog/calculating-crc-with-a-tiny-32-entry-lookup-table
//
// Copied from https://github.com/PaulStoffregen/OneWire/blob/master/OneWire.cpp
static const uint8_t dscrc2x16_table[] = {
0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83,
0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41,
0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,
0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74};
// ---------------------------------------------------------------------------------
class OneWire
{
public:
OneWire(uint onewire_pin)
{
pio = pio0;
// state machine 0
sm = 0;
// configure the used pins
pio_gpio_init(pio, OW_PIN);
// load the pio programs into the pio memory
offset_wait = pio_add_program(pio, &onewire_wait_program);
offset_reset = pio_add_program(pio, &onewire_reset_program);
offset_write_byte = pio_add_program(pio, &onewire_write_byte_program);
offset_read_byte = pio_add_program(pio, &onewire_read_byte_program);
// make a sm config
pio_sm_config c = pio_get_default_sm_config();
// set the set pin
sm_config_set_set_pins(&c, OW_PIN, 1);
// set the out pin
sm_config_set_out_pins(&c, OW_PIN, 1);
// configure side set to be 1 pin, optional (hence 2 bits) and controls pindirs
sm_config_set_sideset(&c, 2, true, true);
sm_config_set_sideset_pins(&c, OW_PIN);
// set the in pin
sm_config_set_in_pins(&c, OW_PIN);
// set shift to right: bits shifted by 'in' are ordered as least
// significant bit (LSB) first, no autopush/pull
sm_config_set_in_shift(&c, true, false, 0);
sm_config_set_out_shift(&c, true, false, 0);
// one clock cycle is 10 us
sm_config_set_clkdiv(&c, 1250);
// init the pio sm with the config, start with the wait program
pio_sm_init(pio, sm, offset_wait, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
}
int reset()
{
// start the reset program and check if a sensor (worker) responds
pio_sm_exec(pio, sm, offset_reset);
// read the return value: 0 means there are at least one workers
int no_workders_detected = pio_sm_get_blocking(pio, sm);
if (no_workders_detected == 1)
// indeed: no workers detected
return -1;
return 1;
}
int reset_and_check()
{
// this method checks if there are workers (i.e. DS18B20 sensors)
// and then - assuming only one is present - checks if it can read
// this sensor properly by asking for its unique ID and doing a CRC.
// start the reset program
pio_sm_exec(pio, sm, offset_reset);
// read the return value: 0 means there are (at least one) workers
int no_workders_detected = pio_sm_get_blocking(pio, sm);
if (no_workders_detected == 1)
// indeed: no workers detected
return -1;
else
{
// for stability reasons give it some time
sleep_ms(10);
// send the command to get the address of the worker
send_byte(0x33);
// for stability reasons give it some time
sleep_ms(10);
// read the results
read_bytes(8);
// the eighth byte is the crc
if (crc8(results, 7) != results[7])
{
printf("crc of ROM is wrong!!!!!!!!!!!\n");
return -1;
}
return 1;
}
}
void send_byte(uint32_t to_send)
{
// put the byte in the TxFIFO
pio_sm_put(pio, sm, to_send);
// start the sm program that writes a byte
pio_sm_exec(pio, sm, offset_write_byte);
}
uint32_t read_byte()
{
// start the sm program that reads a byte
pio_sm_exec(pio, sm, offset_read_byte);
// wait for the result
return pio_sm_get_blocking(pio, sm) >> 24;
}
void read_bytes(int n)
{
// read n bytes from the sensor
for (int i = 0; i < n; i++)
results[i] = (uint8_t)read_byte() & 0xFF;
}
float read_temperature()
{
// put the sensor in a known state
int workers = reset();
if (workers < 0)
return -1;
// for stability reasons give it some time
sleep_ms(10);
// address the family (not one specific sensor)
send_byte(0xCC);
// for stability reasons give it some time
sleep_ms(5);
// ask for a temperature conversion
send_byte(0x44);
// a temperature conversion at 12 bit resolution takes 750ms
sleep_ms(800);
// put the sensor in a known state
workers = reset();
if (workers < 0)
return -1;
// for stability reasons give it some time
sleep_ms(10);
// address the family (not one specific sensor)
send_byte(0xCC);
// for stability reasons give it some time
sleep_ms(5);
// ask for the results
send_byte(0xBE);
// for stability reasons give it some time
sleep_ms(10);
// read the results
read_bytes(9);
// the ninth byte is the crc
if (crc8(results, 8) != results[8])
{
printf("crc of data is wrong!!!!!!!!!!!\n");
return -1;
}
// convert the temperature results to a float
// the bits in results[0] indicate temperature as follows:
// bit 7 to 0: 2^3, 2^2, 2^1, 2^0, 2^-1, 2^-2, 2^-3, 2^-4
uint8_t Tlow = results[0];
float T = 0;
for (int bit = 0; bit < 8; bit++)
if ((Tlow & 1 << bit) > 0)
T += 1 << bit;
// above counting started as if 2^-4 is 1, so correct by dividing by 16
T /= 16.;
// the bits in results[1] indicate temperature as follows:
// bit 7 to 0: S, S, S, S, S, 2^6, 2^5, 2^4; where S = sign
// note that negative numbers start at -128
uint8_t Thigh = results[1];
for (int bit = 0; bit < 3; bit++)
if ((Thigh & 1 << bit) > 0)
T += 1 << (bit + 4);
// check the sign using bit 3
if ((Thigh & 1 << 3) > 0)
T = -128.+T;
return T;
}
// ---------------------------------------------------------------------------------
// Copied (and adapted) from https://github.com/PaulStoffregen/OneWire/blob/master/OneWire.cpp
uint8_t crc8(const uint8_t *addr, uint8_t len)
{
uint8_t crc = 0;
while (len--)
{
crc = *addr++ ^ crc; // just re-using crc as intermediate
crc = dscrc2x16_table[(crc & 0x0f)] ^
dscrc2x16_table[16 + ((crc >> 4) & 0x0f)];
}
return crc;
}
// ---------------------------------------------------------------------------------
private:
// the pio instance
PIO pio;
// the state machine
uint sm;
// the offsets (origins of the PIO programs)
uint offset_wait;
uint offset_reset;
uint offset_write_byte;
uint offset_read_byte;
// the result of reading the sensor
uint8_t results[9];
};
int main()
{
// needed for printf
stdio_init_all();
// the instance of the OneWire
OneWire DS18B20(OW_PIN);
// reset and determine if there are workers
int workers = DS18B20.reset_and_check();
if (workers > 0)
while (true)
printf("Temperature = %f\n", DS18B20.read_temperature());
else
while (true)
;
}
================================================
FILE: Limited_1_wire/onewire.pio
================================================
;
; It is assumed that an external pull up resistor (4.7k) is present on the pin with the OneWire sensor
;
; ------------------------------------------------------
; WAIT
; When starting the sm, it should do nothing.
.program onewire_wait
onewire_wait:
jmp onewire_wait
; ------------------------------------------------------
; RESET
.program onewire_reset
; side-set controls the direction
.side_set 1 opt pindirs
onewire_reset:
; side_set to input and wait until the line goes high, just in case it isn't already 1
wait 1 PIN 0 side 0
; side_set to output and pull line low and delay for > 480 us
set PINS 0 [7] side 1
; wait > 480 us (at 10us per step), here use 490 us
set x 4 [5]
reset_wait_loop:
jmp x-- reset_wait_loop [6]
; side_set to input, delay for 80us
nop [7] side 0
; read the value of the line (1 = no slaves, 0 = at least one slave)
in PINS 1
push
; wait until the line goes high after the slaves release the line
wait 1 PIN 0
; end by doing nothing in a loop
reset_stop:
jmp reset_stop
; ------------------------------------------------------
; WRITE BYTE
.program onewire_write_byte
.side_set 1 opt pindirs
; get the byte to send
pull
; set counter to 8 bits
set x 7
write_byte_loop:
; master pulls low for 10 us
set PINS 0 side 1
; write whatever bit is shifted out of the OSR, keep it for 40us
out PINS 1 [3]
; the last part (10 us) must be high TODO: jmp also takes 10 us (would not be needed)
set PINS 1
; do 8 bits in total
jmp x-- write_byte_loop
; end by doing nothing in a loop
write_byte_stop:
jmp x-- write_byte_stop
; ------------------------------------------------------
; READ BYTE
.program onewire_read_byte
.side_set 1 opt pindirs
; set counter to 8 bits
set x 7
; clear the ISR
mov ISR NULL
read_byte_loop:
; master pulls low for 10 us
set PINS 0 side 1
; set master to read (instruction takes 10 us)
nop side 0
; sample the line and shift right into the ISR (LSB is read first)
in PINS 1
; there is still some time to wait: about 50 us
nop [4]
; do 8 bits in total
jmp x-- read_byte_loop
; push it in the RxFIFO
push
; end by doing nothing in a loop
read_byte_stop:
jmp read_byte_stop
================================================
FILE: PwmIn/CMakeLists.txt
================================================
add_executable(PwmIn)
pico_generate_pio_header(PwmIn ${CMAKE_CURRENT_LIST_DIR}/PwmIn.pio)
target_sources(PwmIn PRIVATE PwmIn.cpp)
target_link_libraries(PwmIn PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(PwmIn)
# add url via pico_set_program_url
example_auto_set_url(PwmIn)
================================================
FILE: PwmIn/PwmIn.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "PwmIn.pio.h"
// class that sets up and reads PWM pulses: PwmIn. It has three functions:
// read_period (in seconds)
// read_pulsewidth (in seconds)
// read_dutycycle (between 0 and 1)
// read pulsewidth, period, and calculate the dutycycle
class PwmIn
{
public:
// constructor
// input = pin that receives the PWM pulses.
PwmIn(uint input)
{
// pio 0 is used
pio = pio0;
// state machine 0
sm = 0;
// configure the used pins
pio_gpio_init(pio, input);
// load the pio program into the pio memory
uint offset = pio_add_program(pio, &PwmIn_program);
// make a sm config
pio_sm_config c = PwmIn_program_get_default_config(offset);
// set the 'jmp' pin
sm_config_set_jmp_pin(&c, input);
// set the 'wait' pin (uses 'in' pins)
sm_config_set_in_pins(&c, input);
// set shift direction
sm_config_set_in_shift(&c, false, false, 0);
// init the pio sm with the config
pio_sm_init(pio, sm, offset, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
}
// read_period (in seconds)
float read_period(void)
{
read();
// one clock cycle is 1/125000000 seconds
return (period * 0.000000008);
}
// read_pulsewidth (in seconds)
float read_pulsewidth(void)
{
read();
// one clock cycle is 1/125000000 seconds
return (pulsewidth * 0.000000008);
}
// read_dutycycle (between 0 and 1)
float read_dutycycle(void)
{
read();
return ((float)pulsewidth / (float)period);
}
// read pulsewidth and period for one pulse
void read_PWM(float *readings)
{
read();
*(readings + 0) = (float)pulsewidth * 0.000000008;
*(readings + 1) = (float)period * 0.000000008;
*(readings + 2) = ((float)pulsewidth / (float)period);
}
private:
// read the period and pulsewidth
void read(void)
{
// clear the FIFO: do a new measurement
pio_sm_clear_fifos(pio, sm);
// wait for the FIFO to contain two data items: pulsewidth and period
while (pio_sm_get_rx_fifo_level(pio, sm) < 2)
;
// read pulse width from the FIFO
pulsewidth = pio_sm_get(pio, sm);
// read period from the FIFO
period = pio_sm_get(pio, sm) + pulsewidth;
// the measurements are taken with 2 clock cycles per timer tick
pulsewidth = 2 * pulsewidth;
// calculate the period in clock cycles:
period = 2 * period;
}
// the pio instance
PIO pio;
// the state machine
uint sm;
// data about the PWM input measured in pio clock cycles
uint32_t pulsewidth, period;
};
int main()
{
// needed for printf
stdio_init_all();
// the instance of the PwmIn
PwmIn my_PwmIn(14);
// the array with the results
float pwm_reading[3];
// infinite loop to print PWM measurements
while (true)
{
my_PwmIn.read_PWM(pwm_reading);
if (pwm_reading[0] >= 0.)
{
printf("pw=%.8f \tp=%.8f \tdc=%.8f\n", pwm_reading[0], pwm_reading[1], pwm_reading[2]);
}
sleep_ms(100);
}
}
================================================
FILE: PwmIn/PwmIn.pio
================================================
.program PwmIn
; algorithm:
; loop:
; reset y, the 'timer' for the pulsewidth (high period)
; reset x, the 'timer' for the low period. (high + low = period)
; wait for a 0, then wait for a 1: this is the rising edge
; loop:
; decrement y timer
; test for falling edge
; y timer value is the pulse width (actually, (0xFFFFFFFF - y)*2/125MHz is the pulse width)
; loop:
; test for rising edge
; decrement x timer
; x timer is the low period (actually, (0xFFFFFFFF - x)*2/125MHz is the low period)
; push both y and x to the Rx FIFO
.wrap_target
start:
mov y ~NULL ; start with the value 0xFFFFFFFF
mov x ~NULL ; start with the value 0xFFFFFFFF
wait 0 pin 0 ; wait for a 0
wait 1 pin 0 ; wait for a 1, now we really have the rising edge
timer_hp: ; loop for high period
jmp y-- test ; count down for pulse width
jmp start ; timer has reached 0, stop count down of pulse, restart
test:
jmp pin timer_hp ; test if the pin is still 1, if so, continue counting down
timer_lp: ; loop for low period
jmp pin timerstop ; if the pin has become 1, the period is over, stop count down
jmp x-- timer_lp ; if not: count down
jmp start ; timer has reached 0, stop count down of low period, restart
timerstop:
mov ISR ~y ; move the value ~y to the ISR: the high period (pulsewidth) (0xFFFFFFFF-y)
push noblock ; push the ISR into the Rx FIFO
mov ISR ~x ; move the value ~x to the ISR: the low period (0xFFFFFFFF-x)
push noblock ; push the ISR into the Rx FIFO
.wrap
================================================
FILE: PwmIn/PwmIn_4pins/CMakeLists.txt
================================================
add_executable(PWM4)
pico_generate_pio_header(PWM4 ${CMAKE_CURRENT_LIST_DIR}/PwmIn.pio)
target_sources(PWM4 PRIVATE PWM4.cpp PwmIn.cpp)
target_link_libraries(PWM4 PRIVATE
pico_stdlib
hardware_pio
hardware_pwm
hardware_gpio
)
pico_add_extra_outputs(PWM4)
================================================
FILE: PwmIn/PwmIn_4pins/PWM4.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/time.h"
#include "PwmIn.h"
#define NUM_OF_PINS 4
int main()
{
// needed for printf
stdio_init_all();
printf("PwmIn on 4 pins\n");
// set PwmIn
uint pin_list[NUM_OF_PINS] = {14, 15, 18, 19};
PwmIn my_PwmIn(pin_list, NUM_OF_PINS);
while (true)
{
// adviced empty (for now) function of sdk
tight_loop_contents();
// translate pwm of input to output
float PW_0 = my_PwmIn.read_PW(0);
float PW_1 = my_PwmIn.read_PW(1);
float PW_2 = my_PwmIn.read_PW(2);
float PW_3 = my_PwmIn.read_PW(3);
printf("PW_0=%f PW_1=%f PW_2=%f PW_3=%f\n", PW_0, PW_1, PW_2, PW_3);
sleep_ms(100);
}
}
================================================
FILE: PwmIn/PwmIn_4pins/PwmIn.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/irq.h"
#include "PwmIn.h"
#include "PwmIn.pio.h"
// class that reads PWM pulses from up to 4 pins
PwmIn::PwmIn(uint *pin_list, uint num_of_pins)
{
_num_of_pins = num_of_pins;
// pio 0 is used
pio = pio0;
// load the pio program into the pio memory
uint offset = pio_add_program(pio, &PwmIn_program);
// start num_of_pins state machines
for (int i = 0; i < num_of_pins; i++)
{
// prepare state machine i
pulsewidth[i] = 0;
period[i] = 0;
// configure the used pins (pull down, controlled by PIO)
gpio_pull_down(pin_list[i]);
pio_gpio_init(pio, pin_list[i]);
// make a sm config
pio_sm_config c = PwmIn_program_get_default_config(offset);
// set the 'jmp' pin
sm_config_set_jmp_pin(&c, pin_list[i]);
// set the 'wait' pin (uses 'in' pins)
sm_config_set_in_pins(&c, pin_list[i]);
// set shift direction
sm_config_set_in_shift(&c, false, false, 0);
// init the pio sm with the config
pio_sm_init(pio, i, offset, &c);
// enable the sm
pio_sm_set_enabled(pio, i, true);
}
// set the IRQ handler
irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq_handler);
// enable the IRQ
irq_set_enabled(PIO0_IRQ_0, true);
// allow irqs from the low 4 state machines
pio0_hw->inte0 = PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM1_BITS | PIO_IRQ0_INTE_SM2_BITS | PIO_IRQ0_INTE_SM3_BITS ;
};
// read the period and pulsewidth
void PwmIn::read_PWM(float *readings, uint pin)
{
if (pin < _num_of_pins)
{
// determine whole period
period[pin] += pulsewidth[pin];
// the measurements are taken with 2 clock cycles per timer tick
// hence, it is 2*0.000000008
*(readings + 0) = (float)pulsewidth[pin] * 2 * 0.000000008;
*(readings + 1) = (float)period[pin] * 2 * 0.000000008;
*(readings + 2) = ((float)pulsewidth[pin] / (float)period[pin]);
pulsewidth[pin] = 0;
period[pin] = 0;
}
};
// read only the duty cycle
float PwmIn::read_DC(uint pin)
{
return ((float)pulsewidth[pin] / (float)period[pin]);
}
// read only the period
float PwmIn::read_P(uint pin)
{
// the measurements are taken with 2 clock cycles per timer tick
// hence, it is 2*0.000000008
return ((float)period[pin] * 0.000000016);
}
float PwmIn::read_PW(uint pin)
{
// the measurements are taken with 2 clock cycles per timer tick
// hence, it is 2*0.000000008
return ((float)pulsewidth[pin] * 0.000000016);
};
uint32_t PwmIn::pulsewidth[4];
uint32_t PwmIn::period[4];
PIO PwmIn::pio;
================================================
FILE: PwmIn/PwmIn_4pins/PwmIn.h
================================================
#ifndef PwmIn_H
#define PwmIn_H
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "PwmIn.h"
#include "PwmIn.pio.h"
// class that reads PWM pulses on max 4 pins
class PwmIn
{
public:
// constructor
PwmIn(uint *pin_list, uint num_of_pin);
// read pulsewidth and period for one pulse
void read_PWM(float *readings, uint pin);
// read only the pulsewidth
float read_PW(uint pin);
// read only the duty cycle
float read_DC(uint pin);
// read only the period
float read_P(uint pin);
private:
// set the irq handler
static void pio_irq_handler()
{
int state_machine = -1;
// check which IRQ was raised:
for (int i = 0; i < 4; i++)
{
if (pio0_hw->irq & 1<<i)
{
// clear interrupt
pio0_hw->irq = 1 << i;
// read pulse width from the FIFO
pulsewidth[i] = pio_sm_get(pio, i);
// read low period from the FIFO
period[i] = pio_sm_get(pio, i);
// clear interrupt
pio0_hw->irq = 1 << i;
}
}
}
// the pio instance
static PIO pio;
// the pins and number of pins
uint _num_of_pins;
// data about the PWM input measured in pio clock cycles
static uint32_t pulsewidth[4], period[4];
};
#endif
================================================
FILE: PwmIn/PwmIn_4pins/PwmIn.pio
================================================
.program PwmIn
; algorithm:
; loop:
; reset y, the 'timer' for the pulsewidth (high period)
; reset x, the 'timer' for the low period. (high + low = period)
; wait for a 0, then wait for a 1: this is the rising edge
; loop:
; decrement y timer
; test for falling edge
; y timer value is the pulse width (actually, (0xFFFFFFFF - x)*2/125MHz is the pulse width)
; loop:
; test for rising edge
; decrement x timer
; x timer is the low period (actually, (0xFFFFFFFF - x)*2/125MHz is the low period)
; push both y and x to the Rx FIFO
; notify via relative IRQ
.wrap_target
start:
mov y ~NULL ; start with the value 0xFFFFFFFF
mov x ~NULL ; start with the value 0xFFFFFFFF
wait 0 pin 0 ; wait for a 0
wait 1 pin 0 ; wait for a 1, now we really have the rising edge
timer_hp: ; loop for high period
jmp y-- test ; count down for pulse width
jmp start ; timer has reached 0, stop count down of pulse, restart
test:
jmp pin timer_hp ; test if the pin is still 1, if so, continue counting down
timer_lp: ; loop for low period
jmp pin timerstop ; if the pin has become 1, the period is over, stop count down
jmp x-- timer_lp ; if not: count down
jmp start ; timer has reached 0, stop count down of low period, restart
timerstop:
mov ISR ~y ; move the value ~y to the ISR: the high period (pulsewidth) (0xFFFFFFFF-x)
push noblock ; push the ISR into the Rx FIFO
mov ISR ~x ; move the value ~x to the ISR: the low period (0xFFFFFFFF-x)
push noblock ; push the ISR into the Rx FIFO
irq 0 rel ; notify the c-program via IRQ
.wrap
================================================
FILE: PwmIn/README.md
================================================
# PWM input using the Raspberry Pi Pico PIO
# UPDATE:
There was a problem with getting the PwmIn to read more than one pin. So, I've made an update. This update can read pwm signals from up to 4 pins (you could probably go up to 8 if pio1 is also used). It uses (relative) irq in the pio code to signal the c-code that new data is available. See the directory PwmIn_4pins.
# ORIGINAL:
Most microcontrollers have hardware to produce Pulse Width Modulation (PWM) signals. But sometimes it is useful to be able to read PWM signals and determine the period, pulse width and duty cycle.
(Warning: questionable statement) Although the RP2040 seems to be capable of measuring pulse width and period, this takes more CPU cycles than letting a PIO state machine take care of it.
Based on the method to measure pulses with PIO code as described [here](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/HCSR04), a PWM Input can be made.
## Algorithm
In pseudo-code the algorithm is as follows:
```
loop:
reset y, the 'timer' for the pulsewidth (high period)
reset x, the 'timer' for the low period. (high + low = period)
wait for a 0, then wait for a 1: this is the rising edge
loop:
decrement y timer
test for falling edge
y timer value is the pulse width (actually, (0xFFFFFFFF - y)*2/125MHz is the pulse width)
loop:
test for rising edge
decrement x timer
x timer is the low period (actually, (0xFFFFFFFF - x)*2/125MHz is the low period)
push both y and x to the Rx FIFO
```
================================================
FILE: README.md
================================================
# Content
This repository contains bits and pieces that I made while trying to figure out how the Raspberry Pi Pico state machines and the Programmable Input/Output (PIO) work.
[Here](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/handy_bits_and_pieces) I have written down the reusable 'tricks' I use in several of the projects listed below.
The following projects are contained in this repository:
## State machine emulator
The problem with the state machines is that debuggers do not give the insight I need when writing code for a sm. I typically write some code, upload it to the pico and find that it doesn't do what I want it to do. Instead of guessing what I do wrong, I would like to see the values of e.g. the registers when the sm is executing. So, I made an [emulator](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/state_machine_emulator).
## Two independently running state machines
[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Two_sm_simple) is just an example of two state machines running independently. Nothing special about it, but I had to do it.
## Two independently running state machines, one gets disabled temporarily
[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Two_sm_one_disabled) is an example of two state machines running independently, but one gets disabled temporarily.
## Two independently running state machines, synchronized via irq, one gets disabled temporarily
[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Two_sm_one_disabled_with_irq) is an example of two state machines synchronized via setting and clearing an irq, one gets disabled temporarily.
## State machine writes into a buffer via DMA
[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/sm_to_dma_to_buffer) is an example of a state machine using DMA (Direct Memory Access) to write into a buffer.
## State Machine -> DMA -> State Machine -> DMA -> Buffer
[This](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/sm_to_dma_to_sm_to_dma_to_buffer) is an example where one state machine writes via DMA to another state machine whose output is put into a buffer via another DMA channel.
## Communicating values between state machines
The [RP2040 Datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) states that "State machines can not communicate data". Or can they ... Yes they can, in several ways, [including via GPIO pins](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Value_communication_between_two_sm_via_pins).
## Use the ISR for rotational shifting
Normally if the ISR shifts via the `IN` instruction, the bits that come out of the ISR go to cyber space, never to be heard from again. Sometimes it is handy to have rotational shifting. [Right shifting works fine, but left shifting needs some trickery](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Rotational_shift_ISR).
## 4x4 button matrix using PIO code
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/button_matrix_4x4) reads a 4x4 button matrix using PIO code for the Raspberry Pico and returns the button pressed.
## Button debouncer using PIO code
When using a GPIO to read noisy input, such as a mechanical button, a [software debouncer](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Button-debouncer) makes sure that only after the input signal has stabilized, the code will read the new value.
## PWM input using PIO code
Most microcontrollers have hardware to produce Pulse Width Modulation (PWM) signals. But sometimes it is useful to be able to [read PWM signals](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/PwmIn) and determine the period, pulse width and duty cycle.
## Rotary encoder using PIO code
[This software](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Rotary_encoder) reads an optical rotary encoder with very clean signals on its output using PIO code.
## HC-SR04 using the PIO code
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/HCSR04) reads the HC-SR04, an ultrasonic distance measurement unit.
## Ws2812 led strip with 120 pixels
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/ws2812_led_strip_120) is my take on how to control a ws2812 led strip with 120 pixels
## multiply two numbers
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/multiplication) multiplies two numbers.
## Two pio programs in one file
I wanted to see how I could use two pio programs in one file and use them from within the c/c++ program, see [here.](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/two_pio_programs_one_file)
## 1-wire protocol for one device
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/Limited_1_wire) is a pio implementation of the 1-wire protocol.
## Blow out a(n) LED
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/blow_out_a_LED) is a remake of the wonderfull little thingy made by Paul Dietz: blow on a LED to make it go out! Really!
## Counting pulses in a pulse train separated by a pause
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/count_pulses_with_pause) can be used for protocols where the data is encoded by a number of pulses in a pulse train followed by a pause.
E.g. the LMT01 temperature sensor uses this, [see](https://www.reddit.com/r/raspberrypipico/comments/nis1ew/made_a_pulse_counter_for_the_lmt01_temperature/).
## Subroutines in pioasm
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/subroutines) shows that subroutines in pioasm can be a thing and can - in some cases - be used to do more with the limited memory space than is possible with just writing the code in one program.
## Read the SBUS protocol with (and without!) pio code
The SBUS protocol is typically used in Radio Controlled cars, drones, etc. If you want to read this protocol from a RC receiver in order to manipulate the data before setting motors and servos, you can use [this code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/SBUS).
## LED panel using PIO state machine and Direct Memory Access
[This code](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/ledpanel) shows how a pio state machine can drive led panels. It is made for
two 64x64 led panels connected to form a 64 row x 128 column panel. There are 16 brightness levels for each Red, Green and Blue of each pixel, allowing many
colors. In its present form it updates the panel at about 144 Hz at standard clock settings (=125MHz.)
================================================
FILE: Rotary_encoder/CMakeLists.txt
================================================
add_executable(pio_rotary_encoder)
pico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_rotary_encoder.pio)
target_sources(pio_rotary_encoder PRIVATE pio_rotary_encoder.cpp)
target_link_libraries(pio_rotary_encoder PRIVATE
pico_stdlib
hardware_pio
)
pico_enable_stdio_usb(pio_rotary_encoder 0)
pico_enable_stdio_uart(pio_rotary_encoder 1)
pico_add_extra_outputs(pio_rotary_encoder)
# add url via pico_set_program_url
example_auto_set_url(pio_rotary_encoder)
================================================
FILE: Rotary_encoder/README.md
================================================
# Rotary encoder for Raspberry Pi Pico using PIO code
This software reads a rotary encoder with the Raspberry Pi Pico using PIO code.
The rotary encoder that is used is an optical encoder with very clean signals on its output, called A and B, so debouncing of these signals is unnecessary.
<img src="rotary_encoder.jpg" width="300">
The specific encoder I use gives 600 pulses per 360 degrees rotation. For each pulse, 4 signal changes are measured, see the next figure from [wikipedia](https://en.wikipedia.org/wiki/Rotary_encoder#/media/File:Quadrature_Diagram.svg), so in total 2400 transitions are made for a full rotation.
<img src="Quadrature_Diagram.png" width="300">
I have also tried a cheap simple rotary encoder, but only when turning very slowly does the result make any sense.
## Other code
This isn't the first code to read a rotary encoder using the PIO, see e.g. [pimoroni-pico](https://github.com/pimoroni/pimoroni-pico/blob/encoder-pio/drivers/encoder-pio/encoder.pio), which does timing and rudimentary debouncing. And it lets you choose non-consecutive pins.
But this code uses interrupts in the PIO code to signal rotations to the C++ code. And maybe this can best be considered as an exercise on how to make something useful with:
- 19 `jmp` instructions (one hidden in a `mov exec`)
- 3 `in` instructions
- 2 `irq` instructions
- 1 `out` instruction
- 1 `mov` instruction (well ... technically 2)
But seriously, I wanted to play with a jump table in PIO code. This means setting the `.origin` to make sure the jump table is at a fixed position in instruction memory (0), and setting the initial program counter with `pio_sm_init(pio, sm, 16, &c)` to start at instruction location 16, i.e. after the jump table. It also means that the `mov exec` instruction is used to make a jump to the jump table.
## Explanation of PIO code
The first 15 addresses are a list of jumps, forming a jump table, that is later used to raise either an IRQ that signals a clockwise rotation or an IRQ to signal a counter clockwise rotation. See below.
The important steps are explained in the figure below. There the program line number, the contents of the Output Shift Register (OSR) and the Input Shift Register (ISR) are shown.
The program starts at line 16, which is only used as an initialization of the program: the two pins (whose values are denoted as A and B) of the rotary encoder are read into the ISR. The two rightmost bits are now AB. The other bits in the ISR do not play a role at this moment and are denoted as 'x'.
The next step, line 17, is the actual start of the program: the content of the ISR is copied to the OSR. The two bits AB are to be used as the previous values, and are therefore denoted as A' and B'.
Line 18 clears the ISR because the zero valued bits are needed later. Then, in two steps, the ISR gets shifted in the old values, A'B' from the OSR, and reads in two new readings of the rotary encoder (AB).
Now, at line 20, the 16 left most bits of the ISR contain: 000000000000A'B'AB.
According to the [datasheet of the rp2040](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) an unconditional jmp instruction without delay or side-set is, apart from the address to jump to, all zeros. And that is exactly what is now contained in the ISR: a jmp instruction to address 0A'B'AB. This is always an address in the first 16 program addresses (0-15). The PIO has an instruction to interpret the content of the ISR as an instruction and execute it: on line 20 the `mov exec ISR` does exactly this.
<img src="code_explanation.png" width="800">
## Jump table
The first 16 instructions form a jump table. The addresses represent the 12 legal transitions and 4 error transitions that can be found in the output of the rotary encoder. As described above, the address that is jumped to is represented by the two old values and the two current values of the output: A'B'AB. The 16 transitions are:
```
A'B'A B = meaning; action
--------------------------------------------------------------------------------------
0 0 0 0 = transition from 00 to 00 = no change in reading; do a jump to line 17
0 0 0 1 = transition from 00 to 01 = clockwise rotation; do a jump to CW
0 0 1 0 = transition from 00 to 10 = counter clockwise rotation; do a jump to CCW
0 0 1 1 = transition from 00 to 11 = error; do a jump to line 17
0 1 0 0 = transition from 01 to 00 = counter clockwise rotation; do a jump to CCW
0 1 0 1 = transition from 01 to 01 = no change in reading; do a jump to line 17
0 1 1 0 = transition from 01 to 10 = error; do a jump to line 17
0 1 1 1 = transition from 01 to 11 = clockwise rotation; do a jump to CW
1 0 0 0 = transition from 10 to 00 = clockwise rotation; do a jump to CW
1 0 0 1 = transition from 10 to 01 = error; do a jump to line 17
1 0 1 0 = transition from 10 to 10 = no change in reading; do a jump to line 17
1 0 1 1 = transition from 10 to 11 = counter clockwise rotation; do a jump to CCW
1 1 0 0 = transition from 11 to 00 = error; do a jump to line 17
1 1 0 1 = transition from 11 to 01 = counter clockwise rotation; do a jump to CCW
1 1 1 0 = transition from 11 to 10 = clockwise rotation; do a jump to CW
1 1 1 1 = transition from 11 to 11 = no change in reading; do a jump to line 17
```
The jump to `CW` is a piece of code that sets `irq 0` and then jumps to line 17, the jump to `CCW` sets `irq 1` and then jumps to line 17.
In the C++ code, the `irq 0` causes a counter called `rotation` to be increased, the `irq 1` causes it to be decreased.
================================================
FILE: Rotary_encoder/pio_rotary_encoder.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/irq.h"
#include "pio_rotary_encoder.pio.h"
// class to read the rotation of the rotary encoder
class RotaryEncoder
{
public:
// constructor
// rotary_encoder_A is the pin for the A of the rotary encoder.
// The B of the rotary encoder has to be connected to the next GPIO.
RotaryEncoder(uint rotary_encoder_A)
{
uint8_t rotary_encoder_B = rotary_encoder_A + 1;
// pio 0 is used
PIO pio = pio0;
// state machine 0
uint8_t sm = 0;
// configure the used pins as input with pull up
pio_gpio_init(pio, rotary_encoder_A);
gpio_set_pulls(rotary_encoder_A, true, false);
pio_gpio_init(pio, rotary_encoder_B);
gpio_set_pulls(rotary_encoder_B, true, false);
// load the pio program into the pio memory
uint offset = pio_add_program(pio, &pio_rotary_encoder_program);
// make a sm config
pio_sm_config c = pio_rotary_encoder_program_get_default_config(offset);
// set the 'in' pins
sm_config_set_in_pins(&c, rotary_encoder_A);
// set shift to left: bits shifted by 'in' enter at the least
// significant bit (LSB), no autopush
sm_config_set_in_shift(&c, false, false, 0);
// set the IRQ handler
irq_set_exclusive_handler(PIO0_IRQ_0, pio_irq_handler);
// enable the IRQ
irq_set_enabled(PIO0_IRQ_0, true);
pio0_hw->inte0 = PIO_IRQ0_INTE_SM0_BITS | PIO_IRQ0_INTE_SM1_BITS;
// init the sm.
// Note: the program starts after the jump table -> initial_pc = 16
pio_sm_init(pio, sm, 16, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
}
// set the current rotation to a specific value
void set_rotation(int _rotation)
{
rotation = _rotation;
}
// get the current rotation
int get_rotation(void)
{
return rotation;
}
private:
static void pio_irq_handler()
{
// test if irq 0 was raised
if (pio0_hw->irq & 1)
{
rotation = rotation - 1;
}
// test if irq 1 was raised
if (pio0_hw->irq & 2)
{
rotation = rotation + 1;
}
// clear both interrupts
pio0_hw->irq = 3;
}
// the pio instance
PIO pio;
// the state machine
uint sm;
// the current location of rotation
static int rotation;
};
// Initialize static member of class Rotary_encoder
int RotaryEncoder::rotation = 0;
int main()
{
// needed for printf
stdio_init_all();
// the A of the rotary encoder is connected to GPIO 16, B to GPIO 17
RotaryEncoder my_encoder(16);
// initialize the rotatry encoder rotation as 0
my_encoder.set_rotation(0);
// infinite loop to print the current rotation
while (true)
{
printf("rotation=%d\n", my_encoder.get_rotation());
}
}
================================================
FILE: Rotary_encoder/pio_rotary_encoder.pio
================================================
.program pio_rotary_encoder
.wrap_target
.origin 0 ; The jump table has to start at 0
; it contains the correct jumps for each of the 16
; combination of 4 bits formed by A'B'AB
; A = current reading of pin_A of the rotary encoder
; A' = previous reading of pin_A of the rotary encoder
; B = current reading of pin_B of the rotary encoder
; B' = previous reading of pin_B of the rotary encoder
jmp read ; 0000 = from 00 to 00 = no change in reading
jmp CW ; 0001 = from 00 to 01 = clockwise rotation
jmp CCW ; 0010 = from 00 to 10 = counter clockwise rotation
jmp read ; 0011 = from 00 to 11 = error
jmp CCW ; 0100 = from 01 to 00 = counter clockwise rotation
jmp read ; 0101 = from 01 to 01 = no change in reading
jmp read ; 0110 = from 01 to 10 = error
jmp CW ; 0111 = from 01 to 11 = clockwise rotation
jmp CW ; 1000 = from 10 to 00 = clockwise rotation
jmp read ; 1001 = from 10 to 01 = error
jmp read ; 1010 = from 10 to 10 = no change in reading
jmp CCW ; 1011 = from 10 to 11 = counter clockwise rotation
jmp read ; 1100 = from 11 to 00 = error
jmp CCW ; 1101 = from 11 to 01 = counter clockwise rotation
jmp CW ; 1110 = from 11 to 10 = clockwise rotation
jmp read ; 1111 = from 11 to 11 = no change in reading
pc_start: ; this is the entry point for the program
in pins 2 ; read the current values of A and B and use
; them to initialize the previous values (A'B')
read:
mov OSR ISR ; the OSR is (after the next instruction) used to shift
; the two bits with the previous values into the ISR
out ISR 2 ; shift the previous value into the ISR. This also sets
; all other bits in the ISR to 0
in pins 2 ; shift the current value into the ISR
; the 16 LSB of the ISR now contain 000000000000A'B'AB
; this represents a jmp instruction to the address A'B'AB
mov exec ISR ; do the jmp encoded in the ISR
CW: ; a clockwise rotation was detected
irq 0 ; signal a clockwise rotation via an IRQ
jmp read ; jump to reading the current values of A and B
CCW: ; a counter clockwise rotation was detected
irq 1 ; signal a counter clockwise rotation via an IRQ
; jmp read ; jump to reading the current values of A and B.
; the jmp isn't needed because of the .wrap, and the first
; statement of the program happens to be a jmp read
.wrap
================================================
FILE: Rotational_shift_ISR/CMakeLists.txt
================================================
add_executable(rotational_shift_ISR)
pico_generate_pio_header(rotational_shift_ISR ${CMAKE_CURRENT_LIST_DIR}/rotational_shift_ISR.pio)
target_sources(rotational_shift_ISR PRIVATE rotational_shift_ISR.cpp)
target_link_libraries(rotational_shift_ISR PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(rotational_shift_ISR)
# add url via pico_set_program_url
example_auto_set_url(rotational_shift_ISR)
================================================
FILE: Rotational_shift_ISR/README.md
================================================
# Use the ISR for rotational shifting
Normally if the ISR shifts via the `IN` instruction, the bits that come out of the ISR go to cyber space, never to be heard from again. Sometimes it is handy to have rotational shifting. The [figure below](https://en.wikipedia.org/wiki/Circular_shift#Applications) shows a rotational right shift of one bit. Note that the Least Significant Bit (LSB) shifts to the Most Significant Bit (MSB).

In PIO code the ISR can be used to shift bits in the direction given by the c-command `sm_config_set_in_shift(&c, direction, false, 0)` where direction=true means shift to the right, and direction=false means shift to the left. The `IN` instruction has an option to shift bits into the ISR from the ISR itself via `IN ISR <Bit count>` instruction. This opens up the possibility of making it shift rotationally. The algorithm used by `IN ISR <Bit count>` is as follows:
shift the ISR for `<Bit count>` bits in the direction specified by `sm_config_set_in_shift`, and shift in the LSBs of the original ISR value.
## Right shifting
For right shifting this works just fine, it basically comes down to every bit that would normally go to cyber space is saved and shifted in as the MSB. In the figure below the two steps are shown for eight bits (the ISR has 32) and the instruction `IN ISR 2`:

On the top the ISR has the bit value 00110010, in the middle it has shifted two bits, and on the bottom the two LSBs of the original ISR are shifted in the same order as the MSBs (these are the same as the two bits shifted out in the blue circle). This results in a bit value 10001100, which is exactly a two bits rotational shift of the ISR.
## Left shifting
For left-shifting, however, this does not work well, consider the following example:

The ISR at the start has a bit value of 01010010, and after shifting to the left, the two LSBs of the original ISR are shifted in on the left. These are not the two bits that were shifted out (blue circle)! So, no rotational shifting to the left.
It is possible to obtain true rotational left shifting. The trick is that the direction of shifting must be set to right shifting: `sm_config_set_in_shift(&c, true, false, 0)` and the following PIO code can then be used to left shift the ISR for two bits (other `<Bit counts>` are of course also possible):
```
mov ISR :: ISR
in ISR 2
mov ISR :: ISR
```
In the above PIO code, the `::` symbols reverses the bit order in the ISR, effectively turning the correctly working right shift into a left-shift.
================================================
FILE: Rotational_shift_ISR/rotational_shift_ISR.cpp
================================================
#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "rotational_shift_ISR.pio.h"
// handy function to print the value of 'number' in bits
void printBits(uint32_t number)
{ // go through each of the bits, starting with the Most Significant Bit (MSB)
for (int i = 31; i >= 0; i--)
{
// test of that bit is set, print "1" if set, or "0" if not
if ((number & (1 << i)) > 0)
{
printf("1");
}
else
{
printf("0");
}
}
}
int main()
{
// needed for printf
stdio_init_all();
// pio 0 is used
PIO pio = pio0;
// state machine 0 and 1
uint sm = 0;
// load the sm0 program into the pio memory
uint offset = pio_add_program(pio, &rotational_shift_ISR_program);
// make a sm config
pio_sm_config c = rotational_shift_ISR_program_get_default_config(offset);
// set shift direction
// NOTE: IT IS SET TO SHIFT RIGHT! EVEN THOUGH THE PIO CODE SHIFTS RIGHT AND LEFT
sm_config_set_in_shift(&c, true, false, 0);
// init the pio sm with the config
pio_sm_init(pio, sm, offset, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
while (true)
{
// push a random number into the Tx FIFO
pio_sm_put(pio, sm, rand());
// read it back (first wait until something is written into the Rx FIFO)
while (pio_sm_is_rx_fifo_empty(pio, sm))
;
printf("S)\t");
printBits(pio_sm_get(pio, sm));
printf("\n");
// let the PIO code do 32 shifts to the right
for (int i = 1; i <= 32; i++)
{
while (pio_sm_is_rx_fifo_empty(pio, sm))
;
printf("R%d)\t", i);
printBits(pio_sm_get(pio, sm));
printf("\n");
}
// let the PIO code do 32 shifts to the left
for (int i = 1; i <= 32; i++)
{
while (pio_sm_is_rx_fifo_empty(pio, sm))
;
printf("L%d)\t", i);
printBits(pio_sm_get(pio, sm));
printf("\n");
}
printf("----------------------------------------------------------\n");
}
}
================================================
FILE: Rotational_shift_ISR/rotational_shift_ISR.pio
================================================
/*
I was playing with the instruction `IN ISR <Bit count>` and wanted to make
a rotational shifter. For right-shifting this works fine, but for left-shifting
two instructions `mov ISR :: ISR` are required.
*/
.program rotational_shift_ISR
.wrap_target
pull block ; get a value to shift around
mov ISR OSR ; put it in the ISR
mov y ISR ; save the ISR
push block ; push the starting value back to the user program
mov ISR y ; restore the ISR
set x 31 ; prepare to do a full rotation to the right
loop_right:
in ISR 1 ; shift to right
mov y ISR ; save the ISR
push block ; the ISR is cleared
mov ISR y ; restore the ISR
jmp x-- loop_right
set x 31 ; prepare to do a full rotation to the left
loop_left:
mov ISR :: ISR ; reverse bit order in ISR
in ISR 1 ; shift right (but after the next reverse its actually left)
mov ISR :: ISR ; reverse bit order in ISR
mov y ISR ; save the ISR
push block ; the ISR is cleared
mov ISR y ; restore the ISR
jmp x-- loop_left
.wrap
================================================
FILE: SBUS/CMakeLists.txt
================================================
add_executable(SBUS)
pico_generate_pio_header(SBUS ${CMAKE_CURRENT_LIST_DIR}/SBUS.pio)
target_sources(SBUS PRIVATE SBUS.cpp)
target_link_libraries(SBUS PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(SBUS)
================================================
FILE: SBUS/README.md
================================================
# Read the SBUS protocol with (and without!) pio code
The SBUS protocol is typically used in Radio Controlled cars, drones, etc.
If you want to read this protocol from a RC receiver in order to manipulate the data before setting motors and servos, you can use this code.
The SBUS protocol is basically an uart Rx with inverted input, 100000 baud rate, a parity bit, and two stop bits, see [here](https://github.com/bolderflight/sbus).
The basis for the PIO code is the RPI pico example for [pio rx](https://github.com/raspberrypi/pico-examples/blob/master/pio/uart_rx/uart_rx.pio).
The parsing of the received data is done following to [this](https://platformio.org/lib/show/5622/Bolder%20Flight%20Systems%20SBUS).
It even does parity checking in the pio code!
## Update
Since the SBUS protocol is "normal" uart I originally started looking at using the hardware uart, but couldn't find an invert option. Turns out you have to invert the GPIO used for input or output. The updated code that doesn't use pio but uart hardware is [here](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/SBUS/gpio_invert).
================================================
FILE: SBUS/SBUS.cpp
================================================
/*
Read SBUS protocol, typically used in Radio Controlled cars, drones, etc.
SBUS data packets consists of 25 8-bit bytes starting with 0x0F and ending with two 0x00
The signal is inverted, has an even parity bit and two stop-bits,
see: https://github.com/bolderflight/sbus
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "SBUS.pio.h"
// The baud rate for the SBUS protocol is 100000
#define SERIAL_BAUD 100000
// The RC receiver is attached to pin 5
#define PIO_RX_PIN 5
// the max amount of data in a SBUS data packet
#define MAX_DATA_ITEMS 25
// function to decode the received SBUS data to actual steering commands
void decode(uint8_t *data)
{
uint16_t channels[16];
// see https://platformio.org/lib/show/5622/Bolder%20Flight%20Systems%20SBUS
channels[0] = (uint16_t)((data[0] | data[1] << 8) & 0x07FF);
channels[1] = (uint16_t)((data[1] >> 3 | data[2] << 5) & 0x07FF);
channels[2] = (uint16_t)((data[2] >> 6 | data[3] << 2 | data[4] << 10) & 0x07FF);
channels[3] = (uint16_t)((data[4] >> 1 | data[5] << 7) & 0x07FF);
channels[4] = (uint16_t)((data[5] >> 4 | data[6] << 4) & 0x07FF);
channels[5] = (uint16_t)((data[6] >> 7 | data[7] << 1 | data[8] << 9) & 0x07FF);
channels[6] = (uint16_t)((data[8] >> 2 | data[9] << 6) & 0x07FF);
channels[7] = (uint16_t)((data[9] >> 5 | data[10] << 3) & 0x07FF);
channels[8] = (uint16_t)((data[11] | data[12] << 8) & 0x07FF);
channels[9] = (uint16_t)((data[12] >> 3 | data[13] << 5) & 0x07FF);
channels[10] = (uint16_t)((data[13] >> 6 | data[14] << 2 | data[15] << 10) & 0x07FF);
channels[11] = (uint16_t)((data[15] >> 1 | data[16] << 7) & 0x07FF);
channels[12] = (uint16_t)((data[16] >> 4 | data[17] << 4) & 0x07FF);
channels[13] = (uint16_t)((data[17] >> 7 | data[18] << 1 | data[19] << 9) & 0x07FF);
channels[14] = (uint16_t)((data[19] >> 2 | data[20] << 6) & 0x07FF);
channels[15] = (uint16_t)((data[20] >> 5 | data[21] << 3) & 0x07FF);
for (int i = 0; i < 16; i++)
{
printf("%d \t", channels[i]);
}
printf("\n");
}
// Main function
int main()
{
// needed for printf
stdio_init_all();
// Set up the state machine to receive RC SBUS data
PIO pio = pio0;
uint sm = 0;
uint offset = pio_add_program(pio, &sbus_program);
pio_sm_config c = sbus_program_get_default_config(offset);
// configure the pin to receive the SBUS data
pio_sm_set_consecutive_pindirs(pio, sm, PIO_RX_PIN, 1, false);
pio_gpio_init(pio, PIO_RX_PIN);
gpio_pull_down(PIO_RX_PIN);
sm_config_set_in_pins(&c, PIO_RX_PIN); // for WAIT, IN
sm_config_set_jmp_pin(&c, PIO_RX_PIN); // for JMP
// Shift to right, autopull disabled
sm_config_set_in_shift(&c, true, false, 32);
// Shift to left, autopull disabled
sm_config_set_out_shift(&c, false, false, 32);
// Deeper FIFO as we're not doing any TX
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// SM transmits 1 bit per 8 execution cycles.
float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_BAUD);
sm_config_set_clkdiv(&c, div);
// init and enable the sm
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
uint8_t index = 0;
uint8_t data[MAX_DATA_ITEMS];
// continuously get the SBUS data from the pio and decode it
while (true)
{
// Note:
// Although there is sufficient time for receiving and decoding because the clkdiv
// and join (see above), too much printing will cause data loss
while (pio_sm_is_rx_fifo_empty(pio, sm))
tight_loop_contents();
uint8_t data_item = pio_sm_get(pio, sm) >> 24;
// search for 0x0f: the start marker
if (data_item == 0x0f)
index = 0;
else if (index < MAX_DATA_ITEMS)
{
// test if the first end marker is read
if (data_item == 0)
{
// read the second end marker
while (pio_sm_is_rx_fifo_empty(pio, sm))
tight_loop_contents();
data_item = pio_sm_get(pio, sm) >> 24;
// the second end marker should also be 0
if (data_item != 0)
printf("Error, second end marker not found\n");
else
// if all data is received, decode it
if (index == 22)
decode(data);
}
else
// if not start or end marker, add it to the received data
data[index++] = data_item;
}
else
printf("error\n");
}
}
================================================
FILE: SBUS/SBUS.pio
================================================
.program sbus
; Start with the situation that the number of ones received is zero (an even number)
; Read the 8 data bits and the parity bit (so, 9 bits in total)
; The even parity bit has to make the total number of ones odd!
; The code starts in the top half (the wrong parity part); if a one is observed it switches
; to the bottom half (the correct parity part). If again a one is observed, it switches back to the top half.
; Etc, switching back and forth every time a one is observed.
; the bits arrive LSB first, thus right-shift it into the ISR
.wrap_target
start:
wait 1 pin 0 ; Stall until start bit is asserted
set x, 8 [10] ; read 8 data bits plus the parity bit. The parity bit should make a odd total of ones.
; delay 12 cycles incl wait, set (8 cycles for the start bit, then 4 to get halfway the first data bit)
even_ones:
in pins, 1 ; Shift data bit into ISR
mov OSR ISR ; test if it is a one or zero: copy it to the OSR and left-shift one bit out
out y 1
jmp !y even_ones_1 ; jump if y == 0, the number of ones doesn't change
jmp odd_ones_2 ; the number of ones did change! Go to the bottom part (correct parity)
even_ones_1:
jmp x-- even_ones [3] ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles
; no need to wait for the stop bits, just stop already, the parity is wrong
jmp end_and_restart
even_ones_2: ; the only reason this is here is the [2] compared to [3] a couple of lines back
; because after the "jmp !y" an extra jmp is made, which costs 1 cycle
jmp x-- even_ones [2] ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles
; no need to wait for the stop bits, just stop already, the parity is wrong
jmp end_and_restart
;---------------------------------------------------------
; If the reading of 9 bits ends above: the parity is wrong
;
; If the reading of 9 bits ends below: the parity is correct
;---------------------------------------------------------
odd_ones:
in pins, 1 ; Shift data bit into ISR
mov OSR ISR ; copy it to the OSR to get that bit into the y register
out y 1 ;
jmp !y odd_ones_1 ; jump if y == 0, the number of ones doesn't change
jmp even_ones_2 ; the number of ones did change! Go to the top part (wrong parity)
odd_ones_1:
jmp x-- odd_ones [3] ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles
; all data has been read, no need to wait for the stop bits. The parity is correct
correct:
mov OSR ISR ; the parity bit is still in ISR, remove it: copy to OSR, left-shift the parity bit out
out NULL 1
mov ISR ~OSR ; invert the data in the OSR and copy it to the ISR
push ; push the ISR to the Rx FIFO
end_and_restart:
wait 0 pin 0 ; wait for line to return to idle state.
jmp start ; Don't push data if we didn't see correct framing.
odd_ones_2:
jmp x-- odd_ones [2] ; Loop 9 times (8 data bits + the parity bit), each loop iteration is 8 cycles
jmp correct ; all data has been read, no need to wait for the stop bits. The parity is correct
================================================
FILE: SBUS/gpio_invert/CMakeLists.txt
================================================
add_executable(SBUS)
target_sources(SBUS PRIVATE SBUS.cpp)
target_link_libraries(SBUS PRIVATE
pico_stdlib
hardware_uart
)
pico_add_extra_outputs(SBUS)
================================================
FILE: SBUS/gpio_invert/SBUS.cpp
================================================
/*
Read SBUS protocol, typically used in Radio Controlled cars, drones, etc.
SBUS data packets consists of 25 8-bit bytes starting with 0x0F and ending with two 0x00
The signal is inverted, has an even parity bit and two stop-bits,
see: https://github.com/bolderflight/sbus
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
// the max amount of data in a SBUS data packet (including start marker 0x0F and end marker 0x00)
#define MAX_DATA_ITEMS 25
// the baud rate for the SBUS protocol is 100000
#define SERIAL_BAUD 100000
// the RC receiver is attached to pin 5
#define RX_PIN 5
// the UART output (not used)
// #define TX_PIN 4
// the uart (there are two: uart0 and uart1, pin5 belongs to uart1)
#define UART_ID uart1
// number of data bits; SBUS has 8
#define DATA_BITS 8
// number of stop bits; SBUS has 2
#define STOP_BITS 2
// parity setting; SBUS has even parity
#define PARITY UART_PARITY_EVEN
// function to decode the received SBUS data to actual steering commands
void decode(uint8_t *data)
{
// the SBUS data encodes 16 channels
uint16_t channels[16];
// see https://platformio.org/lib/show/5622/Bolder%20Flight%20Systems%20SBUS
channels[0] = (uint16_t)((data[0] | data[1] << 8) & 0x07FF);
channels[1] = (uint16_t)((data[1] >> 3 | data[2] << 5) & 0x07FF);
channels[2] = (uint16_t)((data[2] >> 6 | data[3] << 2 | data[4] << 10) & 0x07FF);
channels[3] = (uint16_t)((data[4] >> 1 | data[5] << 7) & 0x07FF);
channels[4] = (uint16_t)((data[5] >> 4 | data[6] << 4) & 0x07FF);
channels[5] = (uint16_t)((data[6] >> 7 | data[7] << 1 | data[8] << 9) & 0x07FF);
channels[6] = (uint16_t)((data[8] >> 2 | data[9] << 6) & 0x07FF);
channels[7] = (uint16_t)((data[9] >> 5 | data[10] << 3) & 0x07FF);
channels[8] = (uint16_t)((data[11] | data[12] << 8) & 0x07FF);
channels[9] = (uint16_t)((data[12] >> 3 | data[13] << 5) & 0x07FF);
channels[10] = (uint16_t)((data[13] >> 6 | data[14] << 2 | data[15] << 10) & 0x07FF);
channels[11] = (uint16_t)((data[15] >> 1 | data[16] << 7) & 0x07FF);
channels[12] = (uint16_t)((data[16] >> 4 | data[17] << 4) & 0x07FF);
channels[13] = (uint16_t)((data[17] >> 7 | data[18] << 1 | data[19] << 9) & 0x07FF);
channels[14] = (uint16_t)((data[19] >> 2 | data[20] << 6) & 0x07FF);
channels[15] = (uint16_t)((data[20] >> 5 | data[21] << 3) & 0x07FF);
for (int i = 0; i < 16; i++)
{
printf("%d \t", channels[i]);
}
printf("\n");
}
// Main function
int main()
{
// needed for printf
stdio_init_all();
// initialize the uart
uart_init(UART_ID, SERIAL_BAUD);
// set the gpio function to uart
gpio_set_function(RX_PIN, GPIO_FUNC_UART);
// set data bits, stop_bits and the parity of the uart
uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);
// set the input gpio pin to inverted
gpio_set_inover(RX_PIN, GPIO_OVERRIDE_INVERT);
// index of read SBUS data item
uint8_t index = 0;
// array to store read SBUS data
uint8_t data[MAX_DATA_ITEMS];
// continuously get the SBUS data from the pio and decode it
while (true)
{ // read a SBUS data item
uint8_t data_item = (uint8_t)uart_getc(UART_ID);
// store it
data[index++] = data_item;
// check for the start markter
if (data_item == 0x0F)
{
// if start marker has been found: decode the existing SBUS data
decode(data);
// start over
index = 0;
}
}
}
================================================
FILE: Two_sm_one_disabled/CMakeLists.txt
================================================
add_executable(two_sm_one_disabled)
pico_generate_pio_header(two_sm_one_disabled ${CMAKE_CURRENT_LIST_DIR}/two_sm_one_disabled.pio)
target_sources(two_sm_one_disabled PRIVATE two_sm_one_disabled.cpp)
target_link_libraries(two_sm_one_disabled PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(two_sm_one_disabled)
# add url via pico_set_program_url
example_auto_set_url(two_sm_one_disabled)
================================================
FILE: Two_sm_one_disabled/README.md
================================================
# Two independently running state machines, one gets disabled temporarily
This is an example of two state machines (sm) running independently, but one, sm1, gets disabled temporarily.
The sm0 repeatedly counts up from 0 to 0xFFFFFFFF (actually, it counts down!), the other repeatedly counts down from 31. They both send their current values to the c-program via their Rx FIFOs.
In the c-program, sm1 gets disabled for a number of iterations. The output is somewhat interesting:
```
0 = 293088 1 = 31 ;
0 = 293089 1 = 30 ;
0 = 293090 1 = 29 ;
0 = 293091 1 = 28 <--- ; Here the sm1 is disabled, but it still has 4 words in the Rx FIFO
0 = 293092 1 = 27 <--- ;
0 = 293093 1 = 26 <--- ;
0 = 293094 1 = 25 <--- ; Last valid Rx FIFO data
0 = 293095 1 = 28 <--- ; Here the Rx FIFO of sm1 is empty. Oddly, we get 28, not 25
0 = 293096 1 = 28 <--- ;
0 = 293097 1 = 28 <--- ;
0 = 293098 1 = 28 <--- ;
0 = 293099 1 = 28 <--- ;
0 = 293100 1 = 28 <--- ;
0 = 293101 1 = 24 ; The sm is started again, and as expected continues where it was stopped
0 = 293102 1 = 23 ;
0 = 293103 1 = 22 ;
0 = 293104 1 = 21 ;
```
While sm0 keeps counting up, sm1 is disabled. At that moment there are still 4 data items in the FIFO. After these 4 have been read and printed, reading the FIFO keeps giving data (28, the entry at the time of disabling sm1). I know you should always check whether there is still valid data in the FIFO before reading it, but hey now we know what happens.
================================================
FILE: Two_sm_one_disabled/two_sm_one_disabled.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "two_sm_one_disabled.pio.h"
int main()
{
// needed for printf
stdio_init_all();
// pio 0 is used
PIO pio = pio0;
// state machine 0 and 1
uint sm0 = 0;
uint sm1 = 1;
// load the sm0 program into the pio memory
uint offset0 = pio_add_program(pio, &tester0_program);
// load the sm1 program into the pio memory
uint offset1 = pio_add_program(pio, &tester1_program);
// make a sm config
pio_sm_config c0 = tester0_program_get_default_config(offset0);
pio_sm_config c1 = tester1_program_get_default_config(offset1);
// init the pio sm0 with the config
pio_sm_init(pio, sm0, offset0, &c0);
pio_sm_init(pio, sm1, offset1, &c1);
// enable the sm
pio_sm_set_enabled(pio, sm0, true);
pio_sm_set_enabled(pio, sm1, true);
// infinite loop. But after 100 times normal printf, sm1 is disabled for 10 iterations and then enabled again
int i = 0;
while (true)
{
i++;
if (i < 100)
{
// normal printing 100 times
printf("0 = %d \t\t1 = %d\n", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));
}
else if (i < 110)
{
// sm1 is disabled, printing for 10 times
pio_sm_set_enabled(pio, sm1, false);
printf("0 = %d \t\t1 = %d <---\n", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));
}
else
{
// enable sm1 again, and repeat counting
pio_sm_set_enabled(pio, sm1, true);
i = 0;
}
}
}
================================================
FILE: Two_sm_one_disabled/two_sm_one_disabled.pio
================================================
.program tester0
start0:
mov x ~NULL ; start with 0xFFFFFFFF
push_it0:
mov ISR ~x ; push the value into the Rx FIFO
; (make it look like counting up)
push block
jmp x-- push_it0 ; count down
jmp start0
.program tester1
start1:
set x 31 ; start with 31
push_it1:
mov ISR x ; push the x value into the Rx FIFO
push block
jmp x-- push_it1 ; count down
jmp start1
================================================
FILE: Two_sm_one_disabled_with_irq/CMakeLists.txt
================================================
add_executable(two_sm_one_disabled_with_irq)
pico_generate_pio_header(two_sm_one_disabled_with_irq ${CMAKE_CURRENT_LIST_DIR}/two_sm_one_disabled_with_irq.pio)
target_sources(two_sm_one_disabled_with_irq PRIVATE two_sm_one_disabled_with_irq.cpp)
target_link_libraries(two_sm_one_disabled_with_irq PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(two_sm_one_disabled_with_irq)
# add url via pico_set_program_url
example_auto_set_url(two_sm_one_disabled_with_irq)
================================================
FILE: Two_sm_one_disabled_with_irq/README.md
================================================
# Two independently running state machines, synchronized via irq, one is temporarily disabled
This is an example of two state machines (sm) synchronizing their execution via setting and clearing an irq. At some point sm1 gets disabled. Because they are synchronized, sm0 also stops.
One sm repeatedly counts up from 0 to 0xFFFFFFFF (actually, it counts down!), the other repeatedly counts down from 31. They both send their current values to the c-program via their Rx FIFOs.
```
0 = 198 1 = 24 ;
0 = 199 1 = 23 ;
0 = 200 1 = 22 ;
0 = 201 1 = 21 <--- ; The sm1 is disabled
0 = 202 1 = 20 <--- ; The sm0 stops producing output (irq wait)
0 = 199 1 = 19 <--- ;
0 = 199 1 = 18 <--- ; Last valid sm1 Rx FIFO data
0 = 199 1 = 21 <--- ;
0 = 199 1 = 21 <--- ;
0 = 199 1 = 21 <--- ;
0 = 199 1 = 21 <--- ;
0 = 199 1 = 21 <--- ;
0 = 199 1 = 21 <--- ;
0 = 203 1 = 17 ; The sm1 started again, sm0 follows
0 = 204 1 = 16 ;
0 = 205 1 = 15 ;
```
================================================
FILE: Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "two_sm_one_disabled_with_irq.pio.h"
int main()
{
// needed for printf
stdio_init_all();
// pio 0 is used
PIO pio = pio0;
// state machine 0 and 1
uint sm0 = 0;
uint sm1 = 1;
// load the sm0 program into the pio memory
uint offset0 = pio_add_program(pio, &tester0_program);
// load the sm1 program into the pio memory
uint offset1 = pio_add_program(pio, &tester1_program);
// make a sm config
pio_sm_config c0 = tester0_program_get_default_config(offset0);
pio_sm_config c1 = tester1_program_get_default_config(offset1);
// init the pio sm0 with the config
pio_sm_init(pio, sm0, offset0, &c0);
pio_sm_init(pio, sm1, offset1, &c1);
// enable the sm
pio_sm_set_enabled(pio, sm0, true);
pio_sm_set_enabled(pio, sm1, true);
// infinite loop. But after 100 times normal printf, sm1 is disabled for 10 iterations and then enabled again
int i = 0;
while (true)
{
i++;
if (i < 100)
{
// normal printing 100 times
printf("0 = %d \t\t1 = %d\n", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));
}
else if (i < 110)
{
// sm1 is disabled, printing for 10 times
pio_sm_set_enabled(pio, sm1, false);
printf("0 = %d \t\t1 = %d <---\n", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));
}
else
{
// enable sm1 again, and repeat counting
pio_sm_set_enabled(pio, sm1, true);
i = 0;
}
}
}
================================================
FILE: Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.pio
================================================
.program tester0
start0:
mov x ~NULL ; start with 0xFFFFFFFF
push_it0:
mov ISR ~x ; push the value into the Rx FIFO (make it look like counting up)
push block
irq wait 0 ; set irq 0 and wait for it to clear
jmp x-- push_it0 ; count down
jmp start0
.program tester1
start1:
set x 31 ; start with 31
push_it1:
mov ISR x ; push the value into the Rx FIFO
push block
irq clear 0 ; clear irq 0
jmp x-- push_it1 ; count down
jmp start1
================================================
FILE: Two_sm_simple/CMakeLists.txt
================================================
add_executable(two_sm_simple)
pico_generate_pio_header(two_sm_simple ${CMAKE_CURRENT_LIST_DIR}/two_sm_simple.pio)
target_sources(two_sm_simple PRIVATE two_sm_simple.cpp)
target_link_libraries(two_sm_simple PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(two_sm_simple)
# add url via pico_set_program_url
example_auto_set_url(two_sm_simple)
================================================
FILE: Two_sm_simple/README.md
================================================
# Two independently running state machines
This is just an example of two state machines (sm) running independently. Nothing special about it, but I had to do it.
One sm repeatedly counts up from 0 to 0xFFFFFFFF (actually, it counts down!), the other repeatedly counts down from 31. They both send their current values to the c-program via their Rx FIFOs.
================================================
FILE: Two_sm_simple/two_sm_simple.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "two_sm_simple.pio.h"
int main()
{
// needed for printf
stdio_init_all();
// pio 0 is used
PIO pio = pio0;
// state machine 0 and 1
uint sm0 = 0;
uint sm1 = 1;
// load the sm0 program into the pio memory
uint offset0 = pio_add_program(pio, &tester0_program);
// load the sm1 program into the pio memory
uint offset1 = pio_add_program(pio, &tester1_program);
// make a sm config
pio_sm_config c0 = tester0_program_get_default_config(offset0);
pio_sm_config c1 = tester1_program_get_default_config(offset1);
// init the pio sm0 with the config
pio_sm_init(pio, sm0, offset0, &c0);
pio_sm_init(pio, sm1, offset1, &c1);
// enable the sm
pio_sm_set_enabled(pio, sm0, true);
pio_sm_set_enabled(pio, sm1, true);
while (true)
{
printf("0 = %d \t\t1 = %d\n", pio_sm_get(pio, sm0), pio_sm_get(pio, sm1));
}
}
================================================
FILE: Two_sm_simple/two_sm_simple.pio
================================================
.program tester0
start0:
mov x ~NULL ; start at 0xFFFFFFFF and count down
push_it0:
mov ISR ~x ; make it appear that it counts up
push block ; push the current value into the Rx FIFO
jmp x-- push_it0
jmp start0
.program tester1
start1:
set x 31 ; start at 31
push_it1:
mov ISR x
push block ; push the current value into the Rx FIFO
jmp x-- push_it1
jmp start1
================================================
FILE: Value_communication_between_two_sm_via_pins/CMakeLists.txt
================================================
add_executable(value_communication_between_two_sm_via_pins)
pico_generate_pio_header(value_communication_between_two_sm_via_pins ${CMAKE_CURRENT_LIST_DIR}/value_communication_between_two_sm_via_pins.pio)
target_sources(value_communication_between_two_sm_via_pins PRIVATE value_communication_between_two_sm_via_pins.cpp)
target_link_libraries(value_communication_between_two_sm_via_pins PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(value_communication_between_two_sm_via_pins)
# add url via pico_set_program_url
example_auto_set_url(value_communication_between_two_sm_via_pins)
================================================
FILE: Value_communication_between_two_sm_via_pins/README.md
================================================
# Sending values between state machines
The [RP2040 Datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) states that "State machines can not communicate data". Or can they ...
Of course they can, but not directly from within the state machines (sm). They can communicate via their FIFOs if the c program reads the Rx FIFO of one sm and puts it into the Tx FIFO of the other sm. They can even do this via DMA, which is [another example](https://github.com/GitJer/Some_RPI-Pico_stuff/tree/main/sm_to_dma_to_sm_to_dma_to_buffer) in this repository.
But they can also communicate using the GPIO pins.
In this example, one sm sets a GPIO pin to a value, and the other reads that pin to obtain the value. They use an irq to make sure that they work synchronized. In this example only one GPIO pin is used, but they could of course use more pins to communicate more bits at one time.
================================================
FILE: Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "value_communication_between_two_sm_via_pins.pio.h"
int main()
{
// needed for printf
stdio_init_all();
// pio 0 is used
PIO pio = pio0;
// state machine 0 and 1
uint sm0 = 0;
uint sm1 = 1;
// use pin 15 for both output of sm0 and input to sm1
uint pin = 15;
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm0, pin, 1, true);
// These pins are used by sm0 and sm1.
// As it turns out, the pin needs to be set once: to output!
// even though sm1 uses them as input.
// load the sm0 program into the pio memory
uint offset0 = pio_add_program(pio, &tester0_program);
// load the sm1 program into the pio memory
uint offset1 = pio_add_program(pio, &tester1_program);
// make a sm config
pio_sm_config c0 = tester0_program_get_default_config(offset0);
pio_sm_config c1 = tester1_program_get_default_config(offset1);
// set shift direction
sm_config_set_in_shift(&c0, false, false, 0);
// set shift direction
sm_config_set_in_shift(&c1, false, false, 0);
// sm0 produces output using 'set', sm1 reads it using 'in'
// set the 'set' pins for sm0
sm_config_set_set_pins(&c0, pin, 1);
// set the 'in' pins for sm1
sm_config_set_in_pins(&c1, pin);
// init the pio sm0 with config c0
pio_sm_init(pio, sm0, offset0, &c0);
// init the pio sm1 with config c1
pio_sm_init(pio, sm1, offset1, &c1);
// enable the state machines
pio_sm_set_enabled(pio, sm0, true);
pio_sm_set_enabled(pio, sm1, true);
// end with infinite loop of printing what is send by sm0 and received by sm1
while (true)
{
printf("value send by sm0 = %d\n", pio_sm_get(pio, sm0));
printf("value received by sm1 = %d\n", pio_sm_get(pio, sm1));
}
}
================================================
FILE: Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.pio
================================================
.program tester0
.wrap_target
set pins 0 ; set output on pin to 0 (this is read by tester1, see below)
mov ISR NULL ; signal this to the c++ program via the Rx FIFO
push block
irq wait 0 ; wait for the other sm to read the value of the pin
set pins 1 ; the same as above, but now for the value 1
set x 1 ; push 1 into the ISR and then Rx FIFO
mov ISR x
push block
irq wait 0
.wrap
.program tester1
.wrap_target
wait irq 0 ; wait for the other sm to set a value on the pin
in pins 1 ; read the value
push block ; signal this to the c++ program via the FIFO
irq clear 0 ; clear the irq
.wrap
================================================
FILE: Z80/CMakeLists.txt
================================================
add_executable(Z80)
pico_generate_pio_header(Z80 ${CMAKE_CURRENT_LIST_DIR}/Z80.pio)
target_sources(Z80 PRIVATE Z80.c)
target_link_libraries(Z80 PRIVATE
pico_stdlib
hardware_pio
hardware_irq
hardware_vreg
)
================================================
FILE: Z80/README.md
================================================
# Z80 read and write from bus
This is a WIP.
It uses pio code to read and write to a Z80 bus.
The information needed to write this code was provided by [siriokds](https://github.com/siriokds).
================================================
FILE: Z80/Z80.c
================================================
#include "stdio.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/vreg.h"
// the .pio.h file also defines (in this order): D0 (8 bits), A0 (8 bits), RW, WR, DIR, OE
#include "Z80.pio.h"
// PIO and state machine
PIO pio;
uint sm_rd;
uint sm_wr;
uint offset_rd;
uint offset_wr;
// Configure the pio state machine
void configure_pio_sm()
{
// pio 0 is used
pio = pio0;
// state machine 0 is used.
sm_rd = 0;
sm_wr = 1;
// load the sm_rd program into the pio memory
offset_rd = pio_add_program(pio, &Z80_read_program);
// load the sm program into the pio memory
offset_wr = pio_add_program(pio, &Z80_write_program);
// make a sm_rd config
pio_sm_config smc_rd = Z80_read_program_get_default_config(offset_rd);
// make a sm_wr config
pio_sm_config smc_wr = Z80_write_program_get_default_config(offset_wr);
// function select: this allows pio to set output on a gpio
for (int i = D0; i <= OE; i++)
pio_gpio_init(pio, i);
// TODO: for testing purposes, set data and address lines to 0 via pull down, normally this would be set externally
for (int i = D0; i < RD; i++)
gpio_set_pulls(i, false, true);
// set initial pindirs: D0 - D7 are (also) output
pio_sm_set_consecutive_pindirs(pio, sm_rd, D0, 8, true);
pio_sm_set_consecutive_pindirs(pio, sm_wr, D0, 8, true);
// set initial pindirs: A0 - A7 are input
pio_sm_set_consecutive_pindirs(pio, sm_rd, A0, 8, false);
pio_sm_set_consecutive_pindirs(pio, sm_wr, A0, 8, false);
// set initial pindirs: RD, WR are input
pio_sm_set_consecutive_pindirs(pio, sm_rd, RD, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm_wr, RD, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm_rd, WR, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm_wr, WR, 1, false);
// set initial pindirs: DIR and OE are output
pio_sm_set_consecutive_pindirs(pio, sm_rd, DIR, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm_wr, DIR, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm_rd, OE, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm_wr, OE, 1, true);
// pio 'in' pins: inputs start at the first data bit (D0)
sm_config_set_in_pins(&smc_rd, D0);
// pio 'out' pins: data D0-D7 can also be output
sm_config_set_out_pins(&smc_rd, D0, 8);
// pio 'set' pins: DIR (LSB) and OE (MSB)
sm_config_set_set_pins(&smc_rd, DIR, 2);
// Reading from RxFIFO: Shift to left, autopull disabled
sm_config_set_in_shift(&smc_rd, false, false, 32);
// Writing to TxFIFO: Shift to right, autopull disabled
sm_config_set_out_shift(&smc_rd, true, false, 32);
// pio 'in' pins: inputs start at the first data bit (D0)
sm_config_set_in_pins(&smc_wr, D0);
// pio 'out' pins: data D0-D7 can also be output
sm_config_set_out_pins(&smc_wr, D0, 8);
// pio 'set' pins: DIR (LSB) and OE (MSB)
sm_config_set_set_pins(&smc_wr, DIR, 2);
// Reading from RxFIFO: Shift to left, autopull disabled
sm_config_set_in_shift(&smc_wr, false, false, 32);
// Writing to TxFIFO: Shift to right, autopull disabled
sm_config_set_out_shift(&smc_wr, true, false, 32);
// set clock to about 4Mhz TODO: set correct frequency
// sm_config_set_clkdiv(&smc, 31);
// sm_config_set_clkdiv(&smc_rd, 16);
// sm_config_set_clkdiv(&smc_wr, 16);
// init the pio sm with the config
pio_sm_init(pio, sm_rd, offset_rd, &smc_rd);
pio_sm_init(pio, sm_wr, offset_wr, &smc_wr);
// enable the sm
pio_sm_set_enabled(pio, sm_rd, true);
pio_sm_set_enabled(pio, sm_wr, true);
}
// in the current version the address bus is only 8 bits
// the first half is ROM, the second half is RAM
uint8_t ROM[128];
uint8_t RAM[128];
int main()
{
// set the voltage a bit higher than default
vreg_set_voltage(0b1100); // 1.15v
// overclock to 270MHz
set_sys_clock_khz(270000, true);
// fill the ROM and RAM with numbers
uint8_t num;
for (num=0; num<128; num++) {
ROM[num] = num;
RAM[num] = 128+num;
}
// needed for printf
stdio_init_all();
// initialize the state machine
configure_pio_sm();
// print the gpio assignments
printf("D0=%d\n", D0);
printf("A0=%d\n", A0);
printf("RD=%d\n", RD);
printf("WR=%d\n", WR);
printf("DIR=%d\n", DIR);
printf("OE=%d\n", OE);
// a counter to simulate the data in memory (or after some calculations)
uint16_t counter = 0;
// the received data from pio is a combination of the data and the address
uint32_t addr_data;
// the data part
uint8_t data;
// the address part
uint16_t address;
// used for checking whether the sm has returned to the default state
uint8_t current_pc;
// start in the default situation
// Note: if this is in reality set by an external system (e.g. via pullup/down resistors, ths can be removed)
pio_sm_exec(pio, sm_rd, offset_rd + Z80_read_offset_set_default);
pio_sm_exec(pio, sm_wr, offset_wr + Z80_write_offset_set_default);
uint bitoffs;
const uint32_t mask = PIO_FLEVEL_RX0_BITS >> PIO_FLEVEL_RX0_LSB;
while (1)
{
// test if there is something in RxFIFO of sm0 (a read)
bitoffs = PIO_FLEVEL_RX0_LSB + sm_rd * (PIO_FLEVEL_RX1_LSB - PIO_FLEVEL_RX0_LSB);
if ((pio->flevel >> bitoffs) & mask > 0)
{
// get the data from the RxFIFO
addr_data = pio->rxf[sm_rd];
// the lowest 8 bits are the data and the next 8 bits are the address
// data = addr_data & 0xFF;
address = addr_data >> 8;
// determine if it is ROM or RAM, then get the value from ROM or RAM
if (address<128) {
data = ROM[address];
printf("read ROM address %d results in %d\n", address, data);
} else {
data = RAM[address-128];
printf("read RAM address %d results in %d\n", address-128, data);
}
// send the data to the pio for writing it to the data bits
pio->txf[sm_rd] = data;
}
// test if there is something in RxFIFO of sm1 (a write)
bitoffs = PIO_FLEVEL_RX0_LSB + sm_wr * (PIO_FLEVEL_RX1_LSB - PIO_FLEVEL_RX0_LSB);
if ((pio->flevel >> bitoffs) & mask > 0)
{
// get the data from the RxFIFO
addr_data = pio->rxf[sm_wr];
data = addr_data & 0xFF;
address = addr_data >> 8;
if (address<128) {
printf("ROM not writable\n");
} else {
RAM[address-128] = data;
printf("Wrote %d to RAM address %d\n", data, address-128);
}
}
}
}
================================================
FILE: Z80/Z80.pio
================================================
; This pio programm allows a Z80 bus to:
; - read from the RPI pico by providing an address (read_data)
; - write a value to the RPI pico to a memory address (write_data)
; set the pin assignment:
; The starting point of the 8 bit Data D0 - D7
.define PUBLIC D0 2
; The starting point of the 16 bit Address A0 - A15
.define PUBLIC A0 (D0 + 8)
; Read bus flag
.define PUBLIC RD (A0 + 8 + 0)
; Write bus flag
.define PUBLIC WR (A0 + 8 + 1)
; Direction of level shifter
.define PUBLIC DIR (A0 + 8 + 2)
; Output enable of level shifter
.define PUBLIC OE (A0 + 8 + 3)
.program Z80_read
public set_default:
; set pins: MSB = OE, LSB = DIR, so 0b10 is OE=1 and DIR=0
; set the default: OE=1 and DIR=0
set pins 0b10
; wait for RD to become 0
wait 0 GPIO RD
; read the data (8 bits) and addres (8 bits)
in pins 16
; push the data and address to the RxFIFO
push
; get the new data to set as output
pull block
; set the data bits on the bus
out pins 8
; set OE=0 and DIR=0
set pins 0b00
; wait for RD to become 1, then wait 3 cycles
wait 1 GPIO RD [3]
; go back to the default state
jmp set_default
.program Z80_write
public set_default:
; set pins: MSB = OE, LSB = DIR, so 0b10 is OE=1 and DIR=0
; set the default: OE=1 and DIR=0
set pins 0b10
; wait for WR to become 0
wait 0 GPIO WR
; set OE=0 and DIR=0
set pins 0b00
; read the data (8 bits) and addres (8 bits)
in pins 16
; push the data and address to the RxFIFO
push
; wait for WR to become 1, then wait 3 cycles
wait 1 GPIO WR [3]
; go back to the default state
jmp set_default
================================================
FILE: blow_out_a_LED/CMakeLists.txt
================================================
add_executable(blow_led)
target_sources(blow_led PRIVATE blow_led.cpp)
target_link_libraries(blow_led PRIVATE
pico_stdlib
hardware_adc
hardware_dma
)
================================================
FILE: blow_out_a_LED/README.md
================================================
# Blow out a(n) LED
This is remake of the wonderful little thingy made by Paul Dietz. See this [Hackaday article](https://hackaday.com/2018/08/21/an-led-you-can-blow-out-with-no-added-sensor/) and the [github code](https://github.com/paulhdietz/LEDSensors/blob/master/_07_BlowOutLED/_07_BlowOutLED.ino).
It lights a LED and when you blow on it, it switches off for one second, then lights again. Imagine blowing out a LED!
From the hackaday article:
"Turning the LED on warms it up and blowing on it cools it off, causing measurable
changes in the voltage drop across the device. The change isn’t much — only a
handful of millivolts — but the effect is consistent and can be measured. "
Where his code is rather simple and elegant, mine is convoluted.
Why? Because I wanted to play with the DMA sniffer!
The DMA sniffer allows summing of all values that pass through the DMA.
So, there is no need to sum the values yourself afterwards.
The code does the following:
- it turns the LED on for at least 1s to warm it up
- it starts a repeat timer to call "repeating_timer_callback"
- in that callback the adc is read NUM_ADC_SAMPLES times through dma,
the samples are summed by the dma sniffer
- if the read summed value deviates sufficiently from the average, a flag is set to switch the LED off
- an array of obtained summed values is kept up to date to determine the average of the sums
- in the main loop the flag is checked and when set the LED goes off for 1s and then 1s on to warm it up
================================================
FILE: blow_out_a_LED/blow_led.cpp
================================================
/*
This is remake of the wonderful little thingy made by Paul Dietz. See:
https://hackaday.com/2018/08/21/an-led-you-can-blow-out-with-no-added-sensor/
https://github.com/paulhdietz/LEDSensors/blob/master/_07_BlowOutLED/_07_BlowOutLED.ino
From the hackaday article:
"Turning the LED on warms it up and blowing on it cools it off, causing measurable
changes in the voltage drop across the device. The change isn’t much — only a
handful of millivolts — but the effect is consistent and can be measured. "
Where his code is rather simple and elegant, mine is convoluted.
Why? Because I wanted to play with the DMA sniffer!
The DMA sniffer allows summing of all values that pass through the DMA.
So, there is no need to sum the values yourself afterwards.
The code does the following:
- it turns the LED on for at least 1s to warm it up
- it starts a repeat timer to call "repeating_timer_callback"
- in that callback the adc is read NUM_ADC_SAMPLES times through dma,
the samples are summed by the dma sniffer
- an array of the obtained sums is kept up to date to determine the average of the sums
- if the read summed value deviates sufficiently from the average, a flag is set to switch the LED off
- in the main loop the flag is checked and when set the LED goes off for 1s and then 1s on to warm it up
*/
#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#include "hardware/dma.h"
// LED Connections: PLUS pin - resistor - ADC pin - LED - GND
#define PLUS 22
// pin for ADC goes between resistor and LED
#define MEASURE 26
// Number of adc samples to take. Only used in dma_channel_configure
#define NUM_ADC_SAMPLES 512
// Number of samples to determine the average
#define NUM_AV_SAMPLES 64
// Minimum jump for blow out
// depends on the resistor value and type of LED
#define BLOWN 400
// the array with NUM_ADC_SAMPLES summed adc samples
float summed_adc_values[NUM_AV_SAMPLES];
// keeps track of where in the summed_adc_values array the new value is to be written
uint8_t sample_num;
// dma channel number
uint dma_chan;
// the total of the summed_adc_values array
float total;
// flag to indicate it the led should be turned off
bool light_out;
// the variable into which the dma channel dumps all adc readings!
uint32_t temp;
// the callback function that is called automatically (see add_repeating_timer_ms below)
bool repeating_timer_callback(struct repeating_timer *t)
{
// start the sniff sum at 0
dma_hw->sniff_data = 0;
// start the dma_channel. NOTE: it doesn't write to a buffer, it writes to a single variable
// the samples are summed by the sniffer functionality!
dma_channel_set_write_addr(dma_chan, &temp, true);
// wait for it to finish
dma_channel_wait_for_finish_blocking(dma_chan);
// check whether the deviations of the current sample deviates more than BLOWN from the average
// the value of BLOWN depends on many things, e.g. resistor, type of LED, NUM_AV_SAMPLES, and NUM_ADC_SAMPLES
// the print statement can be used to determine which BLOWN value will work for you
// printf("%f\n", (total / 64.) - summed_adc_values[sample_num]);
if (((total / 64.) - dma_hw->sniff_data) > BLOWN)
light_out = true;
// save the summed adc values in the summed_adc_values array
// total is the sum of all elements in that array
total -= summed_adc_values[sample_num];
summed_adc_values[sample_num] = dma_hw->sniff_data;
total += summed_adc_values[sample_num];
// make the array a ring
if (++sample_num == NUM_AV_SAMPLES)
sample_num = 0;
return true;
}
int main()
{
// needed for printf
stdio_init_all();
// switch on the LED and let it warm up
gpio_init(PLUS);
gpio_set_dir(PLUS, GPIO_OUT);
gpio_put(PLUS, 1);
sleep_ms(1000);
// initialize some variables
light_out = false;
sample_num = 0;
total = 0;
for (int i = 0; i < NUM_AV_SAMPLES; i++)
summed_adc_values[i] = 0;
// ========== ADC setup ===================
adc_init();
adc_fifo_setup(
true, // Write each completed conversion to the sample FIFO
true, // Enable DMA data request (DREQ)
1, // DREQ (and IRQ) asserted when at least 1 sample present
false, // disable ERR bit
false // Do not shift each sample to 8 bits when pushing to FIFO, i.e. keep it 12 bit resolution
);
// Divisor of 0 -> full speed.
adc_set_clkdiv(0);
// start the adc
adc_run(true);
// ========== DMA setup ===================
// Set up the DMA to start transferring data as soon as it appears in FIFO
dma_chan = dma_claim_unused_channel(true);
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
// data size is 32 bits (it should be at least 12)
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32);
// Reading from AND WRITING to a constant address
channel_config_set_read_increment(&cfg, false);
channel_config_set_write_increment(&cfg, false);
// Pace transfers based on availability of ADC samples
channel_config_set_dreq(&cfg, DREQ_ADC);
// configure sniff to automatically sum (mode=0xf) all the data
dma_sniffer_enable(dma_chan, 0xf, true);
// enable sniff
channel_config_set_sniff_enable(&cfg, true);
// configure the channel
dma_channel_configure(dma_chan, &cfg,
NULL, // dst
&adc_hw->fifo, // src
NUM_ADC_SAMPLES, // transfer count
false // start immediately
);
// ===========TIMER setup =================
struct repeating_timer timer;
add_repeating_timer_ms(10, repeating_timer_callback, NULL, &timer);
// ========================================
while (true)
{
if (light_out)
{ // switch the LED off
gpio_put(PLUS, 0);
sleep_ms(1000);
// switch it back on and let it warm up
gpio_put(PLUS, 1);
sleep_ms(1000);
// clear flag
light_out = false;
}
}
}
================================================
FILE: button_matrix_4x4/4x4_button_matrix.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "4x4_button_matrix.pio.h"
// class that sets up and reads the 4x4 button matrix
class button_matrix_4x4
{
public:
// constructor
// base_input is the starting gpio for the 4 input pins
// base_output is the starting gpio for the 4 output pins
button_matrix_4x4(uint base_input, uint base_output)
{
// pio 0 is used
pio = pio0;
// state machine 0
sm = 0;
// configure the used pins
for (int i = 0; i < 4; i++)
{
// output pins
pio_gpio_init(pio, base_output + i);
// input pins with pull down
pio_gpio_init(pio, base_input + i);
gpio_pull_down(base_input + i);
}
// load the pio program into the pio memory
uint offset = pio_add_program(pio, &button_matrix_program);
// make a sm config
pio_sm_config c = button_matrix_program_get_default_config(offset);
// set the 'in' pins
sm_config_set_in_pins(&c, base_input);
// set the 4 output pins to output
pio_sm_set_consecutive_pindirs(pio, sm, base_output, 4, true);
// set the 'set' pins
sm_config_set_set_pins(&c, base_output, 4);
// set shift such that bits shifted by 'in' end up in the lower 16 bits
sm_config_set_in_shift(&c, 0, 0, 0);
// init the pio sm with the config
pio_sm_init(pio, sm, offset, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
}
// read the 4x4 matrix
int read(void)
{
// value is used to read from the fifo
uint32_t value = 0;
// clear the FIFO, we only want a currently pressed key
pio_sm_clear_fifos(pio, sm);
// give the sm some time to fill the FIFO if a key is being pressed
sleep_ms(1);
// check that the FIFO isn't empty
if (pio_sm_is_rx_fifo_empty(pio, sm))
{
return -1;
}
// read one data item from the FIFO
value = pio_sm_get(pio, sm);
// translate from a bit position in value to a key number from 0 to 15.
for (int i = 0; i < 16; i++)
{
// test a bit to see if it is set
if ((value & (0x1 << i)) != 0)
{
//the bit is set -> return 'i' as the key number
return i;
}
}
return -1;
}
private:
// the pio instance
PIO pio;
// the state machine
uint sm;
};
int main()
{
// needed for printf
stdio_init_all();
// the instance of the button matrix: input gpio: 10, 11, 12, and 13, output gpio: 18, 19, 20, and 21
button_matrix_4x4 my_matrix(10, 18);
// infinite loop to print pressed keys
while (true)
{
// read the matrix of buttons
int key = my_matrix.read();
if (key >= 0)
{ // a key was pressed: print its number
printf("key pressed = %d\n", key);
}
sleep_ms(1000);
}
}
================================================
FILE: button_matrix_4x4/4x4_button_matrix.pio
================================================
; in the c-program the 'set' pins are the 4 output pins to the 4x4 button matrix
; in the c-program the 'in' pins are the 4 input pins from the 4x4 button matrix, these are pulled_down
.program button_matrix
start:
set pins 1 [31] ; set 0001 on the 4 output pins (activate first row) and wait for the signal to stabilize
in pins 4 ; shift the input pins into the ISR
set pins 2 [31] ; set 0010 on the 4 output pins (activate second row) and wait for the signal to stabilize
in pins 4 ; shift the input pins into the ISR
set pins 4 [31] ; set 0100 on the 4 output pins (activate third row) and wait for the signal to stabilize
in pins 4 ; shift the input pins into the ISR
set pins 8 [31] ; set 1000 on the 4 output pins (activate fourth row) and wait for the signal to stabilize
in pins 4 ; shift the input pins into the ISR
mov x ISR ; copy the ISR into the x scratch register
jmp !x start ; if the x contains 0, no key was pressed, start over
push noblock ; a key was pressed, push the ISR into the RX FIFO
jmp start ; start over
================================================
FILE: button_matrix_4x4/CMakeLists.txt
================================================
add_executable(4x4_button_matrix)
pico_generate_pio_header(4x4_button_matrix ${CMAKE_CURRENT_LIST_DIR}/4x4_button_matrix.pio)
target_sources(4x4_button_matrix PRIVATE 4x4_button_matrix.cpp)
target_link_libraries(4x4_button_matrix PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(4x4_button_matrix)
# add url via pico_set_program_url
example_auto_set_url(4x4_button_matrix)
================================================
FILE: button_matrix_4x4/README.md
================================================
# 4x4 button matrix
This code reads a 4x4 button matrix using PIO code for the Raspberry Pico. In the image below a simple 4x4 button matrix is shown. Four of its pins are connected to the rows (from the PIO code perspective these are outputs) and the other 4 are connected to its columns (used as pulled down inputs).

The PIO code alternatingly sets one row to HIGH and the others to LOW. The four column pins are read by the 'in pins 4' instruction. If no button is pressed, all columns read LOW, i.e. '0000' is shifted into the ISR. If a button is pressed in the row that is HIGH, the corresponding column is also read as HIGH.
If, after all 4 rows have had their turn of being set to HIGH, the ISR contains only zeros, no key was pressed. If it isn't zero, a button was pressed (at least one), and the ISR is pushed into the state machine's RX FIFO.
In the C/C++ code, when reading the RX FIFO it first needs to be cleared in order to not read old key presses. The consequence is that the state machine needs a little time (1 ms in the code) to fill the FIFO if a key is being pressed. If the FIFO is still empty, no key is pressed. If the FIFO does contain a value, the key is available as a bit in some position in the lower 16 bits. This is translated into a key value from 0 to 15.
================================================
FILE: count_pulses_with_pause/CMakeLists.txt
================================================
add_executable(count_pulses_with_pause)
pico_generate_pio_header(count_pulses_with_pause ${CMAKE_CURRENT_LIST_DIR}/count_pulses_with_pause.pio)
target_sources(count_pulses_with_pause PRIVATE count_pulses_with_pause.cpp)
target_link_libraries(count_pulses_with_pause PRIVATE
pico_stdlib
hardware_pio
)
pico_add_extra_outputs(count_pulses_with_pause)
# add url via pico_set_program_url
# example_auto_set_url(count_pulses_with_pause)
================================================
FILE: count_pulses_with_pause/README.md
================================================
# Counting pulses in a pulse train separated by a pause
This class can be used for protocols where the data is encoded by a number of pulses in a pulse train followed by a pause.
E.g. the LMT01 temperature sensor uses this, [see](https://www.reddit.com/r/raspberrypipico/comments/nis1ew/made_a_pulse_counter_for_the_lmt01_temperature/).
================================================
FILE: count_pulses_with_pause/count_pulses_with_pause.cpp
================================================
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "count_pulses_with_pause.pio.h"
/*
This class can be used for protocols where the data is encoded by a number of pulses in a pulse train followed by a pause.
E.g. the LMT01 temperature sensor uses this (see https://www.reddit.com/r/raspberrypipico/comments/nis1ew/made_a_pulse_counter_for_the_lmt01_temperature/)
The class itself only starts the state machine and, when called, read_pulses() gives the data the state machine has put in the Rx FIFO
*/
class count_pulses_with_pause
{
public:
// input = pin that receives the pulses.
count_pulses_with_pause(uint input)
{
// pio 0 is used
pio = pio0;
// state machine 0
sm = 0;
// configure the used pin
pio_gpio_init(pio, input);
// load the pio program into the pio memory
uint offset = pio_add_program(pio, &count_pulses_with_pause_program);
// make a sm config
pio_sm_config c = count_pulses_with_pause_program_get_default_config(offset);
// set the 'jmp' pin
sm_config_set_jmp_pin(&c, input);
// set the 'wait' pin (uses 'in' pins)
sm_config_set_in_pins(&c, input);
// set shift direction
sm_config_set_in_shift(&c, false, false, 0);
// init the pio sm with the config
pio_sm_init(pio, sm, offset, &c);
// enable the sm
pio_sm_set_enabled(pio, sm, true);
}
// read the number of pulses in a pulse train
uint32_t read_pulses(void)
{
// clear the FIFO: do a new measurement
pio_sm_clear_fifos(pio, sm);
// wait for the FIFO to contain a data item
while (pio_sm_get_rx_fifo_level(pio, sm) < 1)
;
// read the Rx FIFO and return the value: the number of pulses measured
return (pio_sm_get(pio, sm));
}
private:
// the pio instance
PIO pio;
// the state machine
uint sm;
};
int main()
{
// needed for printf
stdio_init_all();
// the instance of the count_pulses_with_pause. Note the input pin is 28 in this example
count_pulses_with_pause my_count_pulses_with_pause(28);
// infinite loop to print pulse measurements
while (true)
{
int num_pulses = my_count_pulses_with_pause.read_pulses();
printf("number of pulses = %zu\n", num_pulses);
sleep_ms(100);
}
}
================================================
FILE: count_pulses_with_pause/count_pulses_with_pause.pio
================================================
.program count_pulses_with_pause
; algorithm:
; This PIO program counts the number of pulses in a pulse train.
; In the settings used below, two pulse trains must be separated by at least 40ms
;
; There are two counters:
; One counts the number of pulses observed in a pulse train (the pulse_counter, placed in the y-register)
; One counts how much time has passed after a pulse (the time_counter, placed in the x-register)
;
; If the time_counter has measured 40ms it is assumed the pulse train has ended.
; Then pulse_counter is placed in the Rx FIFO and the pio program starts over.
;
; 'Measuring' the 40ms is done by counting how many instructions have been executed
; in the loop that measures if a pulse is present on the input pin.
; The RPI pico is assumed to run at 125MHz, so 40ms is 5000000 instruction steps.
; The test loop is 2 instructions (jmp PIN and jmp x--), so the time_counter must be 2500000.
; Since the 40ms doesn't need to be precise, this can be approximated by placing 19 in the x and shifting in 17 zeros.
; 2500000 in binary is 1001100010010110100000, which is approximately 10011 with 17 zeros, 10011 is 19 in decimal.
start:
; set pulse_counter to 0 (counting negatively!)
mov y ~NULL
start_time_counter:
; set time_counter to 10011 << 17
set x 19
mov ISR x
in NULL 17
mov x ISR
test:
; test if there is a pulse (a 1 on the pin)
jmp PIN during_pulse
; no pulse is currently observed: decrement time_counter and test again
jmp x-- test
; time_counter has reached 0: a pause has happened
; place the pulse counter in the Rx FIFO
mov ISR ~y
push
; start over
jmp start
during_pulse:
; a pulse is in progress, wait till the pulse is over
wait 0 PIN 0
; increase the number of pulses observed
jmp y-- during_pulse2
; assume y never reaches 0
during_pulse2:
; restart the time counter
jmp start_time_counter
================================================
FILE: example_auto_set_url.cmake
================================================
set(PICO_EXAMPLE_URL_BASE "https://github.com/raspberrypi/pico-examples/tree/HEAD")
macro(example_auto_set_url TARGET)
file(RELATIVE_PATH URL_REL_PATH "${PICO_EXAMPLES_PATH}" "${CMAKE_CURRENT_LIST_DIR}")
pico_set_program_url(${TARGET} "${PICO_EXAMPLE_URL_BASE}/${URL_REL_PATH}")
endmacro()
================================================
FILE: handy_bits_and_pieces/README.md
================================================
# Handy bits and pieces of PIO code
On this page I list some of the things that I found out while making the projects.
If you know of better ways of doing things, or if you have additional 'tricks', please let me know!
## Setting the x (or y) scratch registers to 0xFFFFFFFF
The x register can be used for counting, but PIO code only has one instruction (`jmp x-- label`) that can actually subtract one from the value in x.
Setting the x (or y) registers to a value from 0 to 31 can be done with the `set` command. That command has 5 bits for a number.
For counters it is handy to have much higher values to start with. One approach is to start the x with the highest value available in 32 bits: 0xFFFFFFFF and count down.
This can be achieved with the instruction:
```
mov x ~NULL
```
If the C-program needs a number counting down from 0xFFFFFFFF to 0, you can use:
```
start:
mov x ~NULL ; set the x register to 0xFFFFFFFF
push_it:
mov ISR x ; copy x to the ISR
push block ; wait for the C-program to read it from the Rx FIFO
jmp x-- push_it ; decrement x and loop if not zero
jmp start ; x = 0 -> start over
```
## Counting up instead of down
As stated above, PIO only has one instruction to count down: `jmp x-- label` (which can also use y). Counting up isn't possible. However, bitwise inverting the value in x results in its complement. Thus we can start a loop at 0xFFFFFFFF and count down, but use the inverted value in x, which results in counting from 0 to 0xFFFFFFFF.
If the C-program needs a number counting up from 0 to 0xFFFFFFFF, you can use:
```
start:
mov x ~NULL ; set the x register to 0xFFFFFFFF
push_it:
mov ISR ~x ; copy its complement to the ISR
push block ; wait for the C-program to read it from the Rx FIFO
jmp x-- push_it ; decrement x and loop if not zero
jmp start ; x = 0 -> start over
```
Note the tilde '~' before the x in the line `mov ISR ~x`.
## Delay
If you need to have a small delay in PIO code the delay_value can be set to a number of cycles between 0 and 31. If, however, a (much) larger delay is required, the following can be used:
A counter in the x (or y) register can be started at the required number of delay cycles, and then counted down in a tight loop. Note that you need to subtract the amount of cycles that setting up the counter takes.
This same approach can be used to create a clock-cycle precise delay for any number between 0x00000000 and 0xFFFFFFFF. This can be done by setting x and shifting it into the ISR, 5 bits at a time, several times. For example, a delay loop of 682 instructions is needed (in binary: 1010101010), this can be achieved in the following way:
```
set x 21 ; in binary 10101, the first 5 bits of 1010101010
mov ISR x ; copy x into the ISR
set x 10 ; in binary 01010, the 5 least significant bits of 1010101010
in x 5 ; shift the least significant 5 bits of x into the ISR
mov x ISR ; place the ISR in x as the start value of the counter
delay_loop: ; the actual delay loop
jmp x-- delay_loop
```
Note that setting up the starting value in the x register took 5 instructions, so in total this will result in a delay of 682+5 = 687 clock cycles. Also note that left-shifting is used.
If the timing doesn't have to be precise, rounding down (or up) after the 5 most significant bits and shifting in further 0's can save instructions.
For example if a delay of approximately 7500000 clock cycles is needed (in binary 11100100111000011100000), using only the first 5 most significant digits and setting the rest (18 bits) to 0, may suffice. This results in:
```
set x 28 ; set x to 11100
mov ISR x ; copy x into the ISR
in NULL 18 ; shift in 18 more 0 bits
mov x ISR ; move the ISR to x
delay_loop: ; the delay loop
jmp x-- delay_loop
```
## Measuring 'time'
To measure the duration of some event, can be done by starting the x (or y) scratch register with 0xFFFFFFFF and counting down, testing for the stop criterion each iteration.
The counting down loop with test for the stop criterion looks like this:
```pio
mov x ~NULL ; start with 0xFFFFFFFF
timer:
jmp x-- test ; count down, and jump to testing
jmp timerstop ; timer has reached 0, stop count down
test:
jmp pin timer ; <---- insert the test criterion here
timerstop: ; The test criterion was met (or timer has reached 0)
mov ISR ~x ; move the bit inverted value of x to the ISR
push noblock ; push the ISR into the RX FIFO
```
The `jmp x-- test` tests if x is zero, and if not decrements it and jumps to testing for the stop criterion (here the `jmp` pin going low). If the criterion fails, a jmp is made to `timer`. If the criterion is met, or if the x register becomes 0, the bit-inverted value of x is moved to the ISR, and is then pushed to the Rx FIFO.
Here the counting down and the test itself only take two instructions (`jmp x-- test` and `jmp pin timer`). Therefore the resolution of measuring the time with this code is 2 * pio clock tick, which for the standard clock is `2 / 125 MHz = 0.000000016` seconds. If the test takes more than one instruction, the resolution becomes lower.
## Change shifting direction in PIO code
When configuring a sm from c-code, you can configure the shift direction of the ISR:
for left shifting:
```
sm_config_set_in_shift(&c, false, false, 0);
```
for right shifting:
```
sm_config_set_in_shift(&c, true, false, 0);
```
If you need to change the shifting direction in PIO code, you can use:
```
mov ISR :: ISR
in ISR 1
mov ISR :: ISR
```
The `::` symbol reverses the bit order. In this example, shifting is only for one bit, but other bit counts are also possible: `in ISR Bit_count`, with Bit_count between 1 and 32, 32 is encoded as 00000.
================================================
FILE: ledpanel/CMakeLists.txt
================================================
add_executable(ledpanel)
pico_generate_pio_header(ledpanel ${CMAKE_CURRENT_LIST_DIR}/ledpanel.pio)
target_sources(ledpanel PRIVATE ledpanel.c ledpanel_worker.c)
target_link_libraries(ledpanel PRIVATE
pico_stdlib
hardware_pio
hardware_dma
hardware_irq
hardware_interp
pico_multicore
)
================================================
FILE: ledpanel/README.md
================================================
# LED panel using PIO state machine and Direct Memory Access
This code shows how a pio state machine can drive led panels. It is made for
two 64x64 led panels connected to form a 64 row x 128 column panel. There are 16
brightness levels for each Red, Green and Blue of each pixel, allowing many
colors. In its present form it updates the panel at about 144 Hz at standard
clock settings (=125MHz.)
The main function has code for three test patterns.
There are many, many concepts that require explanation, and I do a poor
job of it in the code and header file.
Some of the things that I would like to (but do not) explain in detail here are:
* it uses two cores: core 0 generates images, core 1 transcodes it into a data format suitable for
sending it to the PIO state machine (sm).
* after transcoding, the sm continuously sends the data to the ledpanel. It uses Direct Memory Access (DMA) for this.
* double buffering is used for generating images (but not for the transcoded data structure!)
* care has been taken that every generated image is displayed.
* the images have 4 bits for each color (green, blue, red), it uses a sort of Pulse Width Modulation (PWM) to get different brightness levels for the colors and thus each pixel can have 16 * 16 * 16 = 4096 colors. See [here](https://www.galliumstudio.com/2020/04/07/stm32-episode-3-color-tricks-with-led-panels/) for an explanation of the PWM approach.
* there is also an overall brightness setting. It takes values from 0 (dimmest) to 7 (brightest).
* brightness ultimately comes down to how long a delay loop is in the sm code.
* the file 'ledpanel.c' contains the code for core 0. It mostly consists of some simple (and simplistic) functions to make the images. In the main function three example functions are given.
* the file 'ledpanel_worker.c' contains the code for core 1: transcoding and controlling the sm via DMA.
* the sm outputs the address and color bits to the ledpanel and executes the delay loop that determines the brightness.
* I used the interpolator hardware! This must be one of the few examples that uses it. And I do not use it for what it was intended for: it just reorders some bits, see the (somewhat vague) explanation in 'ledpanel_worker.c'.
This image shows the first of the three example animations in the main function: a skewed rainbow that shifts position with time:

## Some words about the timing of the various functions
In the figure below the data of three logical analyzer channels are shown (many thanks to [Saleae](https://www.saleae.com/)) for the first example in the main function (the skewed rainbow):
The top line shows when the DMA interrupt is busy. This interrupt restarts the DMA channel such that the sm can send data to the ledpanel. The restarting of the DMA takes very little time, so only a peak is visible in the figure. The important thing is the time between those peaks (the 1 in the figure). At the highest brightness level that I use, the peaks are 6.93 ms apart, this translates into a display update frequency of 144 Hz.
The bottom line is the process generating the images on core 0. It is clear that generating images is the slowest process, see 3 in the figure. Generating the rainbow image costs more than 55ms.
The channel in the middle is the transcoding function on core 1. It takes almost 7ms to transcode an image, see 2 in the figure.

In the figure below the same channels are shown for the second example in the main function: red and blue gradients overlapping for 8 overall brightness levels. At the lowest brightness level the updating of the ledpanel (top channel)
takes 590 us, at 1 in the figure, while one brightness level higher takes 640 us (at 2 in the figure). At the highest brightness level it is 6.93 ms.
Generating the figure takes almost 6 ms, the pulse at 4 in the figure. Note that in the code there is a 1 second pause between generating images at different brightness levels.

In the figure below the timing for the third example in the main function (three moving lines) is shown. Generating a new image takes little time: 1.15 ms, see 1 in the figure. The transcoding, 2 in the figure, is continuously busy. The code has been made such that all images are displayed: core 0 has to wait for core 1 to finish transcoding.

================================================
FILE: ledpanel/ledpanel.c
================================================
#include "stdio.h"
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "hardware/interp.h"
#include "ledpanel.pio.h"
#include "pico/multicore.h"
#include "ledpanel.h"
// Overall brightness: a number from 0 (dim) to 7 (brightest)
uint overall_brightness = 7;
// the image to be displayed (a pointer) and the two actual variables for double buffering
uint currently_drawing = 1;
uint (*image)[num_of_displays * columns_of_display];
uint image1[rows_of_display][num_of_displays * columns_of_display];
uint image2[rows_of_display][num_of_displays * columns_of_display];
// 0 = no image ready or processing finished
// 1 = producer (core 0, in this file) has finished producing image 1
// 2 = producer (core 0)) has finished producing image 2
uint image_ready = 0;
uint image_processing = 0;
/******************************************************************************
* some simple routines to draw the image for the test patterns
*****************************************************************************/
// set the color + brightness for a pixel
void set_pixel(uint row, uint column, uint c, uint brightness)
{
// leave the other two colors untouched to allow mixing
if (c == R) // Red: do not shift
image[row][column] |= brightness;
else if (c == G) // Green: shift over 8 bits
image[row][column] |= brightness << 8;
else // Blue: shift 4 bits
image[row][column] |= brightness << 4;
}
// set the individual colors + brightnesses for a pixel
void set_pixel_c(uint row, uint column, uint R_brightness, uint G_brightness, uint B_brightness)
{
image[row][column] = R_brightness | G_brightness << 8 | B_brightness << 4;
}
// draw a horizontal line
void row_line(uint row, uint column_start, uint column_end, uint c, uint brightness)
{
for (uint column = column_start; column < column_end; column++)
set_pixel(row, column, c, brightness);
}
// draw a vertical line
void column_line(uint column, uint row_start, uint row_end, uint c, uint brightness)
{
for (uint row = row_start; row < row_end; row++)
set_pixel(row, column, c, brightness);
}
// make an empty image
void clear_image()
{
for (uint x = 0; x < rows_of_display; x++)
for (uint y = 0; y < num_of_displays * columns_of_display; y++)
image[x][y] = 0;
}
// color wheel (from Adafruit strand test)
// Input a value 0 to 255 to get a color value.
// The colors returned are a transition r - g - b - back to r.
void wheel(uint wheel_pos, uint *RC, uint *GC, uint *BC)
{
if (wheel_pos < 85)
{
*RC = wheel_pos * 3;
*GC = 255 - wheel_pos * 3;
*BC = 0;
}
else if (wheel_pos < 170)
{
wheel_pos -= 85;
*BC = wheel_pos * 3;
*RC = 255 - wheel_pos * 3;
*GC = 0;
}
else
{
wheel_pos -= 170;
*GC = wheel_pos * 3;
*BC = 255 - wheel_pos * 3;
*RC = 0;
}
}
// switch image buffers to be drawn (dubbelbuffering)
// However, wait if the process on core1 is still busy with a buffer
void switch_buffer()
{
image_ready = currently_drawing;
if (currently_drawing == 1)
{
while (image_processing == 2)
;
image = image2;
currently_drawing = 2;
}
else
{
while (image_processing == 1)
;
image = image1;
currently_drawing = 1;
}
}
/******************************************************************************
* the main function: initializes everything and starts a loop of test patterns
*****************************************************************************/
int main()
{
// start the dubbel buffer with image1
image = image1;
currently_drawing = 1;
// TODO: remove (only for testing purposes)
gpio_init(timing_pin_build_image);
gpio_set_dir(timing_pin_build_image, GPIO_OUT);
// needed for printf
stdio_init_all();
// start with an empty image
clear_image();
// Start core 1. Core 1 continuously transcodes the image
// to a format that can be used with the pio code on the
// state machine..
multicore_launch_core1(core1_worker);
// show the test patterns forever
while (true)
{
// /*
//
// Test pattern 3: make the rainbow pattern that shifts with time
//
for (int current_t = 0; current_t < 255; current_t++)
{
gpio_put(timing_pin_build_image, 1); // TODO: remove (only for testing purposes)
uint r, g, b;
// make a rainbow pattern
for (uint row = 0; row < rows_of_display; row++)
for (uint column = 0; column < num_of_displays * columns_of_display; column++)
{
wheel((current_t + row + column) % 256, &r, &g, &b);
// Note: green is a bit too strong on my panels -> lower the multiplication factor
set_pixel_c(row, column, (int)(r * 0.0627), (int)(g * 0.04), (int)(b * 0.0627));
}
gpio_put(timing_pin_build_image, 0); // TODO: remove (only for testing purposes)
// signal to core 1 that the image is ready, and switch image buffer
switch_buffer();
}
// */
// /*
//
// Test pattern 2: draw red and blue planes with 16 different intensity levels and change the overall brightness
//
// change the overall brightness
for (uint b = 0; b <= 7; b++)
{
gpio_put(timing_pin_build_image, 1); // TODO: remove (only for testing purposes)
clear_image();
uint prev_fraction = 0;
uint next_fraction = 0;
// Red color planes with brightness in 16 levels (4 bits)
for (uint fraction = 1; fraction <= 16; fraction++)
{
next_fraction = (int)((float)fraction / 16. * (num_of_displays * columns_of_display));
for (uint current_y = prev_fraction; current_y < next_fraction; current_y++)
column_line(current_y, 0, rows_of_display, R, fraction - 1);
prev_fraction = next_fraction;
}
prev_fraction = 0;
// Blue color planes with brightness in 16 levels (4 bits)
for (uint fraction = 1; fraction <= 16; fraction++)
{
next_fraction = (int)((float)fraction / 16. * rows_of_display);
for (uint current_x = prev_fraction; current_x < next_fraction; current_x++)
row_line(current_x, 0, num_of_displays * columns_of_display, B, fraction - 1);
prev_fraction = next_fraction;
}
overall_brightness = b;
gpio_put(timing_pin_build_image, 0); // TODO: remove (only for testing purposes)
// signal to core 1 that the image is ready, and switch image buffer
switch_buffer();
// pause 1 seconds
sleep_ms(1000);
}
// */
// /*
//
// Test pattern 1: moving lines
//
// repeat this test pattern a number of times
for (int count = 0; count < 5; count++)
{
for (uint current_t = 0; current_t < num_of_displays * columns_of_display; current_t++)
{
gpio_put(timing_pin_build_image, 1); // TODO: remove (only for testing purposes)
uint current_x = current_t % rows_of_display;
uint current_y = current_t;
// prepare an empty image
clear_image();
// draw red, green and blue lines at high brightness
column_line(current_y, 0, rows_of_display, R, 15);
column_line((current_y + 64) % (num_of_displays * columns_of_display), 0, rows_of_display, G, 15);
row_line(current_x, 0, num_of_displays * columns_of_display, B, 15);
gpio_put(timing_pin_build_image, 0); // TODO: remove (only for testing purposes)
// signal to core 1 that the image is ready, and switch image buffer
switch_buffer();
}
}
// */
}
}
======================================
gitextract_myyc33ud/
├── Button-debouncer/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── button_debounce.cpp
│ ├── button_debounce.h
│ ├── button_debounce.pio
│ ├── main.cpp
│ └── micropython_integrated/
│ ├── README.md
│ ├── button_debounce.cpp
│ ├── button_debounce.h
│ ├── button_debounce.pio.h
│ ├── debounce.cpp
│ ├── debouncemodule.c
│ ├── debouncemodule.h
│ └── micropython.cmake
├── CMakeLists.txt
├── HCSR04/
│ ├── CMakeLists.txt
│ ├── HCSR04.cpp
│ ├── HCSR04.pio
│ └── README.md
├── LICENSE
├── Limited_1_wire/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── onewire.cpp
│ └── onewire.pio
├── PwmIn/
│ ├── CMakeLists.txt
│ ├── PwmIn.cpp
│ ├── PwmIn.pio
│ ├── PwmIn_4pins/
│ │ ├── CMakeLists.txt
│ │ ├── PWM4.cpp
│ │ ├── PwmIn.cpp
│ │ ├── PwmIn.h
│ │ └── PwmIn.pio
│ └── README.md
├── README.md
├── Rotary_encoder/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── pio_rotary_encoder.cpp
│ └── pio_rotary_encoder.pio
├── Rotational_shift_ISR/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── rotational_shift_ISR.cpp
│ └── rotational_shift_ISR.pio
├── SBUS/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── SBUS.cpp
│ ├── SBUS.pio
│ └── gpio_invert/
│ ├── CMakeLists.txt
│ └── SBUS.cpp
├── Two_sm_one_disabled/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_sm_one_disabled.cpp
│ └── two_sm_one_disabled.pio
├── Two_sm_one_disabled_with_irq/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_sm_one_disabled_with_irq.cpp
│ └── two_sm_one_disabled_with_irq.pio
├── Two_sm_simple/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_sm_simple.cpp
│ └── two_sm_simple.pio
├── Value_communication_between_two_sm_via_pins/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── value_communication_between_two_sm_via_pins.cpp
│ └── value_communication_between_two_sm_via_pins.pio
├── Z80/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── Z80.c
│ └── Z80.pio
├── blow_out_a_LED/
│ ├── CMakeLists.txt
│ ├── README.md
│ └── blow_led.cpp
├── button_matrix_4x4/
│ ├── 4x4_button_matrix.cpp
│ ├── 4x4_button_matrix.pio
│ ├── CMakeLists.txt
│ └── README.md
├── count_pulses_with_pause/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── count_pulses_with_pause.cpp
│ └── count_pulses_with_pause.pio
├── example_auto_set_url.cmake
├── handy_bits_and_pieces/
│ └── README.md
├── ledpanel/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── ledpanel.c
│ ├── ledpanel.h
│ ├── ledpanel.pio
│ └── ledpanel_worker.c
├── multiplication/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── multiplier.cpp
│ └── multiplier.pio
├── pico_sdk_import.cmake
├── sm_to_dma_to_buffer/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── sm_to_dma_to_buffer.cpp
│ └── sm_to_dma_to_buffer.pio
├── sm_to_dma_to_sm_to_dma_to_buffer/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── sm_to_dma_to_sm_to_dma_to_buffer.cpp
│ └── sm_to_dma_to_sm_to_dma_to_buffer.pio
├── state_machine_emulator/
│ ├── README.md
│ ├── config.py
│ ├── emulation.py
│ ├── examples/
│ │ ├── button_debounce/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── in_shift/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── irq_set_and_clear/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── ledpanel/
│ │ │ ├── c_program
│ │ │ ├── ledpanel.pio.h
│ │ │ └── pin_program
│ │ ├── multiplication/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── push_pull_auto/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── push_pull.pio.h
│ │ ├── rotational_shift/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── side_step/
│ │ │ ├── README.md
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ ├── square_wave/
│ │ │ ├── c_program
│ │ │ ├── pin_program
│ │ │ └── pio_program.pio.h
│ │ └── stepper/
│ │ ├── c_program
│ │ ├── pin_program
│ │ └── pio_program.pio.h
│ ├── interface/
│ │ ├── __init__.py
│ │ ├── _interface_item.py
│ │ ├── _left_frame.py
│ │ ├── _mid_frame.py
│ │ ├── _output_frame.py
│ │ ├── _right_frame.py
│ │ ├── _toolbar.py
│ │ └── _tooltips.py
│ ├── main.py
│ └── state_machine/
│ ├── __init__.py
│ ├── _do_sideset.py
│ ├── _execute_instructions.py
│ ├── _push_pull.py
│ ├── _set_all_GPIO.py
│ └── _time_step.py
├── subroutines/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── subroutine.cpp
│ └── subroutine.pio
├── two_pio_programs_one_file/
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── two_p_one_f.cpp
│ ├── two_p_one_f.pio
│ └── two_p_one_f.pio.h
└── ws2812_led_strip_120/
├── CMakeLists.txt
├── README.md
├── ws2812_led_strip_120.c
└── ws2812_led_strip_120.pio
SYMBOL INDEX (194 symbols across 57 files)
FILE: Button-debouncer/button_debounce.h
function class (line 15) | class Debounce
FILE: Button-debouncer/main.cpp
function main (line 21) | int main()
FILE: Button-debouncer/micropython_integrated/button_debounce.h
function class (line 10) | class Debounce
FILE: Button-debouncer/micropython_integrated/button_debounce.pio.h
type pio_program (line 37) | struct pio_program
function pio_sm_config (line 43) | static inline pio_sm_config button_debounce_program_get_default_config(u...
FILE: Button-debouncer/micropython_integrated/debounce.cpp
function mp_obj_t (line 10) | mp_obj_t start() {
function mp_obj_t (line 16) | mp_obj_t debounce_gpio(mp_obj_t mp_gpio) {
function mp_obj_t (line 22) | mp_obj_t set_debounce_time(mp_obj_t mp_gpio, mp_obj_t mp_debounce_time) {
function mp_obj_t (line 29) | mp_obj_t read(mp_obj_t mp_gpio) {
function mp_obj_t (line 35) | mp_obj_t undebounce_gpio(mp_obj_t mp_gpio) {
FILE: HCSR04/HCSR04.cpp
class HCSR04 (line 11) | class HCSR04
method HCSR04 (line 19) | HCSR04(uint input, uint output)
method read (line 49) | float read(void)
function main (line 82) | int main()
FILE: Limited_1_wire/onewire.cpp
class OneWire (line 36) | class OneWire
method OneWire (line 39) | OneWire(uint onewire_pin)
method reset (line 75) | int reset()
method reset_and_check (line 87) | int reset_and_check()
method send_byte (line 120) | void send_byte(uint32_t to_send)
method read_byte (line 128) | uint32_t read_byte()
method read_bytes (line 136) | void read_bytes(int n)
method read_temperature (line 143) | float read_temperature()
method crc8 (line 206) | uint8_t crc8(const uint8_t *addr, uint8_t len)
function main (line 233) | int main()
FILE: PwmIn/PwmIn.cpp
class PwmIn (line 14) | class PwmIn
method PwmIn (line 19) | PwmIn(uint input)
method read_period (line 44) | float read_period(void)
method read_pulsewidth (line 52) | float read_pulsewidth(void)
method read_dutycycle (line 60) | float read_dutycycle(void)
method read_PWM (line 67) | void read_PWM(float *readings)
method read (line 77) | void read(void)
function main (line 101) | int main()
FILE: PwmIn/PwmIn_4pins/PWM4.cpp
function main (line 10) | int main()
FILE: PwmIn/PwmIn_4pins/PwmIn.h
function class (line 12) | class PwmIn
FILE: Rotary_encoder/pio_rotary_encoder.cpp
class RotaryEncoder (line 10) | class RotaryEncoder
method RotaryEncoder (line 16) | RotaryEncoder(uint rotary_encoder_A)
method set_rotation (line 50) | void set_rotation(int _rotation)
method get_rotation (line 56) | int get_rotation(void)
method pio_irq_handler (line 62) | static void pio_irq_handler()
function main (line 89) | int main()
FILE: Rotational_shift_ISR/rotational_shift_ISR.cpp
function printBits (line 11) | void printBits(uint32_t number)
function main (line 27) | int main()
FILE: SBUS/SBUS.cpp
function decode (line 23) | void decode(uint8_t *data)
function main (line 51) | int main()
FILE: SBUS/gpio_invert/SBUS.cpp
function decode (line 30) | void decode(uint8_t *data)
function main (line 59) | int main()
FILE: Two_sm_one_disabled/two_sm_one_disabled.cpp
function main (line 8) | int main()
FILE: Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.cpp
function main (line 8) | int main()
FILE: Two_sm_simple/two_sm_simple.cpp
function main (line 8) | int main()
FILE: Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.cpp
function main (line 9) | int main()
FILE: Z80/Z80.c
function configure_pio_sm (line 15) | void configure_pio_sm()
function main (line 94) | int main()
FILE: blow_out_a_LED/blow_led.cpp
function repeating_timer_callback (line 61) | bool repeating_timer_callback(struct repeating_timer *t)
function main (line 91) | int main()
FILE: button_matrix_4x4/4x4_button_matrix.cpp
class button_matrix_4x4 (line 9) | class button_matrix_4x4
method button_matrix_4x4 (line 15) | button_matrix_4x4(uint base_input, uint base_output)
method read (line 49) | int read(void)
function main (line 84) | int main()
FILE: count_pulses_with_pause/count_pulses_with_pause.cpp
class count_pulses_with_pause (line 16) | class count_pulses_with_pause
method count_pulses_with_pause (line 20) | count_pulses_with_pause(uint input)
method read_pulses (line 45) | uint32_t read_pulses(void)
function main (line 63) | int main()
FILE: ledpanel/ledpanel.c
function set_pixel (line 32) | void set_pixel(uint row, uint column, uint c, uint brightness)
function set_pixel_c (line 44) | void set_pixel_c(uint row, uint column, uint R_brightness, uint G_bright...
function row_line (line 50) | void row_line(uint row, uint column_start, uint column_end, uint c, uint...
function column_line (line 57) | void column_line(uint column, uint row_start, uint row_end, uint c, uint...
function clear_image (line 64) | void clear_image()
function wheel (line 74) | void wheel(uint wheel_pos, uint *RC, uint *GC, uint *BC)
function switch_buffer (line 100) | void switch_buffer()
function main (line 123) | int main()
FILE: ledpanel/ledpanel_worker.c
function encode_image (line 39) | void encode_image()
function configure_pio_sm (line 171) | void configure_pio_sm()
function dma_handler (line 202) | void dma_handler()
function configure_dma (line 215) | void configure_dma()
function core1_worker (line 241) | void core1_worker()
FILE: multiplication/multiplier.cpp
function pio_mul (line 13) | void pio_mul(int a, int b)
function main (line 20) | int main()
FILE: sm_to_dma_to_buffer/sm_to_dma_to_buffer.cpp
function main (line 9) | int main()
FILE: sm_to_dma_to_sm_to_dma_to_buffer/sm_to_dma_to_sm_to_dma_to_buffer.cpp
function main (line 22) | int main()
FILE: state_machine_emulator/emulation.py
class emulation (line 4) | class emulation:
method __init__ (line 7) | def __init__(self, state_machine, pin_program, c_program):
method emulate (line 25) | def emulate(self, number_of_steps):
method bit_string (line 54) | def bit_string(self, value):
method execute_pin_and_c_program (line 59) | def execute_pin_and_c_program(self):
FILE: state_machine_emulator/examples/button_debounce/pio_program.pio.h
type pio_program (line 33) | struct pio_program
function pio_sm_config (line 39) | static inline pio_sm_config button_debounce_program_get_default_config(u...
FILE: state_machine_emulator/examples/in_shift/pio_program.pio.h
type pio_program (line 25) | struct pio_program
function pio_sm_config (line 31) | static inline pio_sm_config tester_program_get_default_config(uint offse...
FILE: state_machine_emulator/examples/irq_set_and_clear/pio_program.pio.h
type pio_program (line 40) | struct pio_program
function pio_sm_config (line 46) | static inline pio_sm_config tester_program_get_default_config(uint offset)
FILE: state_machine_emulator/examples/ledpanel/ledpanel.pio.h
type pio_program (line 35) | struct pio_program
function pio_sm_config (line 41) | static inline pio_sm_config ledpanel_program_get_default_config(uint off...
FILE: state_machine_emulator/examples/multiplication/pio_program.pio.h
type pio_program (line 45) | struct pio_program
function pio_sm_config (line 52) | static inline pio_sm_config tester_program_get_default_config(uint offset)
FILE: state_machine_emulator/examples/push_pull_auto/push_pull.pio.h
type pio_program (line 26) | struct pio_program
function pio_sm_config (line 32) | static inline pio_sm_config push_pull_program_get_default_config(uint of...
FILE: state_machine_emulator/examples/rotational_shift/pio_program.pio.h
type pio_program (line 41) | struct pio_program
function pio_sm_config (line 47) | static inline pio_sm_config rotational_shift_ISR_program_get_default_con...
FILE: state_machine_emulator/examples/side_step/pio_program.pio.h
type pio_program (line 27) | struct pio_program
function pio_sm_config (line 33) | static inline pio_sm_config tester_program_get_default_config(uint offse...
FILE: state_machine_emulator/examples/square_wave/pio_program.pio.h
type pio_program (line 26) | struct pio_program
function pio_sm_config (line 32) | static inline pio_sm_config squarewave_program_get_default_config(uint o...
FILE: state_machine_emulator/examples/stepper/pio_program.pio.h
type pio_program (line 37) | struct pio_program
function pio_sm_config (line 43) | static inline pio_sm_config tester_program_get_default_config(uint offset)
FILE: state_machine_emulator/interface/__init__.py
class Emulator_Interface (line 4) | class Emulator_Interface(Thread):
method __init__ (line 14) | def __init__(self, program_definitions, pin_program, c_program, emulat...
method check (line 51) | def check(self):
method __del__ (line 55) | def __del__(self):
method get_reload_flag (line 64) | def get_reload_flag(self):
method update_display (line 67) | def update_display(self):
FILE: state_machine_emulator/interface/_interface_item.py
class Interface_Item (line 8) | class Interface_Item:
method __init__ (line 9) | def __init__(self, frame, display_name, row, col, width):
method update (line 28) | def update(self, clock):
class Var_Bits_32 (line 50) | class Var_Bits_32(Interface_Item):
method __init__ (line 51) | def __init__(self, display_name, var_name, frame, row, col, var, var_i...
method value_string (line 57) | def value_string(self, clock):
class Pin_Settings_32 (line 66) | class Pin_Settings_32(Interface_Item):
method __init__ (line 67) | def __init__(self, display_name, base_name, count_name, frame, row, co...
method value_string (line 74) | def value_string(self, clock):
class Var_List_IRQ (line 91) | class Var_List_IRQ(Interface_Item):
method __init__ (line 92) | def __init__(self, display_name, frame, row, col, var, var_index):
method value_string (line 97) | def value_string(self, clock):
class Var_List (line 103) | class Var_List(Interface_Item):
method __init__ (line 104) | def __init__(self, display_name, frame, row, col, var, var_index):
method value_string (line 109) | def value_string(self, clock):
class Interface_Item_Listbox_Bits (line 116) | class Interface_Item_Listbox_Bits:
method __init__ (line 117) | def __init__(self, display_name, var_name, frame, row, col, var, clock):
method update (line 131) | def update(self, clock):
method value_string (line 137) | def value_string(self, index, clock):
class Interface_Item_Listbox_Time (line 145) | class Interface_Item_Listbox_Time:
method __init__ (line 146) | def __init__(self, display_name, frame, row, col, var):
method update (line 156) | def update(self):
method value_string (line 162) | def value_string(self, index):
FILE: state_machine_emulator/interface/_left_frame.py
function build_left_frame (line 4) | def build_left_frame(self):
function update_left_frame (line 32) | def update_left_frame(self):
FILE: state_machine_emulator/interface/_mid_frame.py
function build_mid_frame (line 4) | def build_mid_frame(self):
function update_mid_frame (line 143) | def update_mid_frame(self):
FILE: state_machine_emulator/interface/_output_frame.py
function build_output_frame (line 4) | def build_output_frame(self):
function update_output_frame (line 32) | def update_output_frame(self):
FILE: state_machine_emulator/interface/_right_frame.py
function build_right_frame (line 4) | def build_right_frame(self):
function update_right_frame (line 29) | def update_right_frame(self):
FILE: state_machine_emulator/interface/_toolbar.py
function enable_disable_buttons (line 7) | def enable_disable_buttons(self):
function reload_callback (line 30) | def reload_callback(self, event):
function restart_callback (line 36) | def restart_callback(self, event):
function quit_callback (line 43) | def quit_callback(self, event):
function step_50_back_callback (line 48) | def step_50_back_callback(self):
function step_10_back_callback (line 58) | def step_10_back_callback(self, event):
function step_back_callback (line 68) | def step_back_callback(self, event):
function step_callback (line 75) | def step_callback(self, event):
function step_10_callback (line 82) | def step_10_callback(self, event):
function step_50_callback (line 92) | def step_50_callback(self):
function build_toolbar (line 102) | def build_toolbar(self):
FILE: state_machine_emulator/interface/_tooltips.py
class CreateToolTip (line 5) | class CreateToolTip(object):
method __init__ (line 9) | def __init__(self, widget, text='widget info'):
method enter (line 20) | def enter(self, event=None):
method leave (line 23) | def leave(self, event=None):
method schedule (line 27) | def schedule(self):
method unschedule (line 31) | def unschedule(self):
method showtip (line 37) | def showtip(self, event=None):
method hidetip (line 52) | def hidetip(self):
FILE: state_machine_emulator/main.py
function process_file_pio_h (line 26) | def process_file_pio_h(filename, c_program):
function process_file_pin_program (line 85) | def process_file_pin_program(filename, pin_program):
function process_file_c_program (line 110) | def process_file_c_program(filename, c_program):
function print_usage (line 145) | def print_usage():
FILE: state_machine_emulator/state_machine/__init__.py
class state_machine (line 3) | class state_machine:
method __init__ (line 13) | def __init__(self, program_definitions):
FILE: state_machine_emulator/state_machine/_do_sideset.py
function do_sideset (line 1) | def do_sideset(self, instruction_delay_sideset):
FILE: state_machine_emulator/state_machine/_execute_instructions.py
function execute_instruction (line 1) | def execute_instruction(self, instruction):
function execute_jmp (line 47) | def execute_jmp(self, instruction):
function execute_wait (line 90) | def execute_wait(self, instruction):
function execute_in (line 124) | def execute_in(self, instruction):
function execute_out (line 192) | def execute_out(self, instruction):
function execute_push (line 274) | def execute_push(self, instruction):
function execute_pull (line 302) | def execute_pull(self, instruction):
function execute_mov (line 332) | def execute_mov(self, instruction):
function execute_irq (line 419) | def execute_irq(self, instruction):
function execute_set (line 453) | def execute_set(self, instruction):
FILE: state_machine_emulator/state_machine/_push_pull.py
function push_to_RxFIFO (line 1) | def push_to_RxFIFO(self):
function pull_from_TxFIFO (line 12) | def pull_from_TxFIFO(self):
FILE: state_machine_emulator/state_machine/_set_all_GPIO.py
function set_all_GPIO (line 1) | def set_all_GPIO(self):
FILE: state_machine_emulator/state_machine/_time_step.py
function time_step (line 1) | def time_step(self):
FILE: subroutines/subroutine.cpp
function main (line 13) | int main()
FILE: two_pio_programs_one_file/two_p_one_f.cpp
function main (line 15) | int main()
FILE: two_pio_programs_one_file/two_p_one_f.pio.h
type pio_program (line 28) | struct pio_program
function pio_sm_config (line 34) | static inline pio_sm_config two_p_one_f_1_program_get_default_config(uin...
type pio_program (line 59) | struct pio_program
function pio_sm_config (line 65) | static inline pio_sm_config two_p_one_f_2_program_get_default_config(uin...
FILE: ws2812_led_strip_120/ws2812_led_strip_120.c
function put_pixel (line 9) | static inline void put_pixel(uint32_t pixel_grb)
function urgb_u32 (line 14) | static inline uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b)
function all_red (line 21) | void all_red()
function all_green (line 27) | void all_green()
function all_blue (line 33) | void all_blue()
function white (line 39) | void white(int level)
function main (line 47) | int main()
Condensed preview — 162 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (395K chars).
[
{
"path": "Button-debouncer/CMakeLists.txt",
"chars": 430,
"preview": "add_executable(pio_button_debounce)\n\npico_generate_pio_header(pio_button_debounce ${CMAKE_CURRENT_LIST_DIR}/button_debou"
},
{
"path": "Button-debouncer/README.md",
"chars": 5250,
"preview": "# Button debouncer using the Raspberry Pico PIO \n\n## Update \n\nThe new version allows the use of all 8 PIO state machines"
},
{
"path": "Button-debouncer/button_debounce.cpp",
"chars": 9540,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"button_de"
},
{
"path": "Button-debouncer/button_debounce.h",
"chars": 2219,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"button_de"
},
{
"path": "Button-debouncer/button_debounce.pio",
"chars": 2317,
"preview": "; Debounce a gpio\n\n\n; Explanation:\n; - start with the assumption that the gpio is in a steady state. \n; If it is curre"
},
{
"path": "Button-debouncer/main.cpp",
"chars": 3474,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"button_debounce.h\"\n\n/*\n This code shows how to use the button de"
},
{
"path": "Button-debouncer/micropython_integrated/README.md",
"chars": 6622,
"preview": "# Button debouncer using the Raspberry Pico PIO integrated into MicroPython\n\n## Note\nThis is a version of the button deb"
},
{
"path": "Button-debouncer/micropython_integrated/button_debounce.cpp",
"chars": 10798,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n\n#include \"button_de"
},
{
"path": "Button-debouncer/micropython_integrated/button_debounce.h",
"chars": 2545,
"preview": "//#include \"py/runtime.h\"\n\n#include \"button_debounce.pio.h\"\n\n/* \n * class that debounces gpio using the PIO state machin"
},
{
"path": "Button-debouncer/micropython_integrated/button_debounce.pio.h",
"chars": 1572,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "Button-debouncer/micropython_integrated/debounce.cpp",
"chars": 1262,
"preview": "extern \"C\" {\n#include <debouncemodule.h>\n#include \"button_debounce.h\"\n#include <stdio.h>\n#include \"py/obj.h\"\n\nDebounce d"
},
{
"path": "Button-debouncer/micropython_integrated/debouncemodule.c",
"chars": 1959,
"preview": "#include <debouncemodule.h>\n\n// Define a Python reference to the function we'll make available.\n// See example.cpp for t"
},
{
"path": "Button-debouncer/micropython_integrated/debouncemodule.h",
"chars": 435,
"preview": "// Include MicroPython API.\n#include \"py/runtime.h\"\n\n// Declare the function we'll make available in Python as cppexampl"
},
{
"path": "Button-debouncer/micropython_integrated/micropython.cmake",
"chars": 372,
"preview": "add_library(usermod_debounce INTERFACE)\n\ntarget_sources(usermod_debounce INTERFACE\n ${CMAKE_CURRENT_LIST_DIR}/button_"
},
{
"path": "CMakeLists.txt",
"chars": 1080,
"preview": "cmake_minimum_required(VERSION 3.12)\n\n# Pull in SDK (must be before project)\ninclude(pico_sdk_import.cmake)\n\nproject(pic"
},
{
"path": "HCSR04/CMakeLists.txt",
"chars": 325,
"preview": "add_executable(HCSR04)\n\npico_generate_pio_header(HCSR04 ${CMAKE_CURRENT_LIST_DIR}/HCSR04.pio)\n\ntarget_sources(HCSR04 PRI"
},
{
"path": "HCSR04/HCSR04.cpp",
"chars": 3243,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"HCSR04.pio.h\"\n\n// class that sets up a"
},
{
"path": "HCSR04/HCSR04.pio",
"chars": 3319,
"preview": "\n; Description of the used algorithm\n;\n; start:\n;\n; Give a puls to the HCSR04 Trig pin to start the measurement\n; "
},
{
"path": "HCSR04/README.md",
"chars": 4917,
"preview": "# HC-SR04 using the PIO code\nThe HC-SR04 is an ultrasonic distance measurement unit ([datasheet](https://cdn.sparkfun.co"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2021 GitJer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "Limited_1_wire/CMakeLists.txt",
"chars": 333,
"preview": "add_executable(onewire)\n\npico_generate_pio_header(onewire ${CMAKE_CURRENT_LIST_DIR}/onewire.pio)\n\ntarget_sources(onewire"
},
{
"path": "Limited_1_wire/README.md",
"chars": 733,
"preview": "# 1-wire protocol for one device \n\nThis code is a pio implementation of the [1-wire protocol](https://en.wikipedia.org/w"
},
{
"path": "Limited_1_wire/onewire.cpp",
"chars": 8457,
"preview": "/*\n\nThis code uses pio code to read a single DS18B20 temperature sensor.\nIt is not suited to read more than one sensor: "
},
{
"path": "Limited_1_wire/onewire.pio",
"chars": 2467,
"preview": "\n;\n; It is assumed that an external pull up resistor (4.7k) is present on the pin with the OneWire sensor\n;\n; ----------"
},
{
"path": "PwmIn/CMakeLists.txt",
"chars": 314,
"preview": "add_executable(PwmIn)\n\npico_generate_pio_header(PwmIn ${CMAKE_CURRENT_LIST_DIR}/PwmIn.pio)\n\ntarget_sources(PwmIn PRIVATE"
},
{
"path": "PwmIn/PwmIn.cpp",
"chars": 3336,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"PwmIn.pio.h\"\n\n// class that sets up an"
},
{
"path": "PwmIn/PwmIn.pio",
"chars": 1684,
"preview": "\n.program PwmIn\n\n; algorithm:\n\n; loop:\n; reset y, the 'timer' for the pulsewidth (high period)\n; reset x, the 'tim"
},
{
"path": "PwmIn/PwmIn_4pins/CMakeLists.txt",
"chars": 302,
"preview": "add_executable(PWM4)\n\npico_generate_pio_header(PWM4 ${CMAKE_CURRENT_LIST_DIR}/PwmIn.pio)\n\ntarget_sources(PWM4 PRIVATE PW"
},
{
"path": "PwmIn/PwmIn_4pins/PWM4.cpp",
"chars": 745,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"pico/time.h\"\n\n#include \"PwmIn.h\"\n\n#define NUM_OF_PINS 4\n\nint main"
},
{
"path": "PwmIn/PwmIn_4pins/PwmIn.cpp",
"chars": 2729,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/irq.h\"\n\n#include \"PwmIn.h\"\n#in"
},
{
"path": "PwmIn/PwmIn_4pins/PwmIn.h",
"chars": 1390,
"preview": "#ifndef PwmIn_H\n#define PwmIn_H\n\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"PwmIn."
},
{
"path": "PwmIn/PwmIn_4pins/PwmIn.pio",
"chars": 1768,
"preview": "\n.program PwmIn\n\n; algorithm:\n\n; loop:\n; reset y, the 'timer' for the pulsewidth (high period)\n; reset x, the 'tim"
},
{
"path": "PwmIn/README.md",
"chars": 1596,
"preview": "# PWM input using the Raspberry Pi Pico PIO \n\n# UPDATE:\nThere was a problem with getting the PwmIn to read more than one"
},
{
"path": "README.md",
"chars": 6559,
"preview": "# Content\n\nThis repository contains bits and pieces that I made while trying to figure out how the Raspberry Pi Pico sta"
},
{
"path": "Rotary_encoder/CMakeLists.txt",
"chars": 511,
"preview": "add_executable(pio_rotary_encoder)\n\npico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_rotary_enc"
},
{
"path": "Rotary_encoder/README.md",
"chars": 5750,
"preview": "# Rotary encoder for Raspberry Pi Pico using PIO code\n\nThis software reads a rotary encoder with the Raspberry Pi Pico u"
},
{
"path": "Rotary_encoder/pio_rotary_encoder.cpp",
"chars": 2988,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/irq.h\"\n\n#include \"pio_rotary_e"
},
{
"path": "Rotary_encoder/pio_rotary_encoder.pio",
"chars": 2712,
"preview": "\n.program pio_rotary_encoder\n.wrap_target\n.origin 0 ; The jump table has to start at 0\n ; it cont"
},
{
"path": "Rotational_shift_ISR/CMakeLists.txt",
"chars": 437,
"preview": "add_executable(rotational_shift_ISR)\n\npico_generate_pio_header(rotational_shift_ISR ${CMAKE_CURRENT_LIST_DIR}/rotational"
},
{
"path": "Rotational_shift_ISR/README.md",
"chars": 2592,
"preview": "# Use the ISR for rotational shifting\nNormally if the ISR shifts via the `IN` instruction, the bits that come out of the"
},
{
"path": "Rotational_shift_ISR/rotational_shift_ISR.cpp",
"chars": 2225,
"preview": "\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"rotational_shift_"
},
{
"path": "Rotational_shift_ISR/rotational_shift_ISR.pio",
"chars": 1183,
"preview": "/*\n I was playing with the instruction `IN ISR <Bit count>` and wanted to make\n a rotational shifter. For right-sh"
},
{
"path": "SBUS/CMakeLists.txt",
"chars": 246,
"preview": "add_executable(SBUS)\n\npico_generate_pio_header(SBUS ${CMAKE_CURRENT_LIST_DIR}/SBUS.pio)\n\ntarget_sources(SBUS PRIVATE SBU"
},
{
"path": "SBUS/README.md",
"chars": 1114,
"preview": "# Read the SBUS protocol with (and without!) pio code\n\nThe SBUS protocol is typically used in Radio Controlled cars, dro"
},
{
"path": "SBUS/SBUS.cpp",
"chars": 4708,
"preview": "/*\n Read SBUS protocol, typically used in Radio Controlled cars, drones, etc.\n SBUS data packets consists of 25 8-"
},
{
"path": "SBUS/SBUS.pio",
"chars": 3371,
"preview": ".program sbus\n\n; Start with the situation that the number of ones received is zero (an even number)\n; Read the 8 data bi"
},
{
"path": "SBUS/gpio_invert/CMakeLists.txt",
"chars": 180,
"preview": "add_executable(SBUS)\n\ntarget_sources(SBUS PRIVATE SBUS.cpp)\n\ntarget_link_libraries(SBUS PRIVATE\n pico_stdlib\n "
},
{
"path": "SBUS/gpio_invert/SBUS.cpp",
"chars": 3545,
"preview": "/*\n Read SBUS protocol, typically used in Radio Controlled cars, drones, etc.\n SBUS data packets consists of 25 8-"
},
{
"path": "Two_sm_one_disabled/CMakeLists.txt",
"chars": 429,
"preview": "add_executable(two_sm_one_disabled)\n\npico_generate_pio_header(two_sm_one_disabled ${CMAKE_CURRENT_LIST_DIR}/two_sm_one_d"
},
{
"path": "Two_sm_one_disabled/README.md",
"chars": 1658,
"preview": "# Two independently running state machines, one gets disabled temporarily\n\nThis is an example of two state machines (sm)"
},
{
"path": "Two_sm_one_disabled/two_sm_one_disabled.cpp",
"chars": 1611,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_sm_one_disabled.pio.h\"\n\nint main()"
},
{
"path": "Two_sm_one_disabled/two_sm_one_disabled.pio",
"chars": 477,
"preview": "\n.program tester0\n\nstart0:\n mov x ~NULL ; start with 0xFFFFFFFF\npush_it0:\n mov ISR ~x ; push the "
},
{
"path": "Two_sm_one_disabled_with_irq/CMakeLists.txt",
"chars": 501,
"preview": "add_executable(two_sm_one_disabled_with_irq)\n\npico_generate_pio_header(two_sm_one_disabled_with_irq ${CMAKE_CURRENT_LIST"
},
{
"path": "Two_sm_one_disabled_with_irq/README.md",
"chars": 1156,
"preview": "# Two independently running state machines, synchronized via irq, one is temporarily disabled\n\nThis is an example of two"
},
{
"path": "Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.cpp",
"chars": 1620,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_sm_one_disabled_with_irq.pio.h\"\n\ni"
},
{
"path": "Two_sm_one_disabled_with_irq/two_sm_one_disabled_with_irq.pio",
"chars": 548,
"preview": "\n.program tester0\n\nstart0:\n mov x ~NULL ; start with 0xFFFFFFFF\npush_it0:\n mov ISR ~x ; push the "
},
{
"path": "Two_sm_simple/CMakeLists.txt",
"chars": 381,
"preview": "add_executable(two_sm_simple)\n\npico_generate_pio_header(two_sm_simple ${CMAKE_CURRENT_LIST_DIR}/two_sm_simple.pio)\n\ntarg"
},
{
"path": "Two_sm_simple/README.md",
"chars": 358,
"preview": "# Two independently running state machines \n\nThis is just an example of two state machines (sm) running independently. N"
},
{
"path": "Two_sm_simple/two_sm_simple.cpp",
"chars": 982,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_sm_simple.pio.h\"\n\nint main()\n{\n "
},
{
"path": "Two_sm_simple/two_sm_simple.pio",
"chars": 454,
"preview": "\n.program tester0\n\nstart0:\n mov x ~NULL ; start at 0xFFFFFFFF and count down\npush_it0:\n mov ISR ~x "
},
{
"path": "Value_communication_between_two_sm_via_pins/CMakeLists.txt",
"chars": 621,
"preview": "add_executable(value_communication_between_two_sm_via_pins)\n\npico_generate_pio_header(value_communication_between_two_sm"
},
{
"path": "Value_communication_between_two_sm_via_pins/README.md",
"chars": 904,
"preview": "# Sending values between state machines \n\nThe [RP2040 Datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datash"
},
{
"path": "Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.cpp",
"chars": 1881,
"preview": "\n#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"value_communication_between_two_sm_vi"
},
{
"path": "Value_communication_between_two_sm_via_pins/value_communication_between_two_sm_via_pins.pio",
"chars": 708,
"preview": "\n.program tester0\n\n.wrap_target\n set pins 0 ; set output on pin to 0 (this is read by tester1, see below)\n mo"
},
{
"path": "Z80/CMakeLists.txt",
"chars": 251,
"preview": "add_executable(Z80)\n\npico_generate_pio_header(Z80 ${CMAKE_CURRENT_LIST_DIR}/Z80.pio)\n\ntarget_sources(Z80 PRIVATE Z80.c)\n"
},
{
"path": "Z80/README.md",
"chars": 196,
"preview": "# Z80 read and write from bus \n\nThis is a WIP.\n\nIt uses pio code to read and write to a Z80 bus.\n\nThe information needed"
},
{
"path": "Z80/Z80.c",
"chars": 6753,
"preview": "#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/vreg.h\"\n// the .pio.h file also"
},
{
"path": "Z80/Z80.pio",
"chars": 1700,
"preview": "; This pio programm allows a Z80 bus to:\n; - read from the RPI pico by providing an address (read_data)\n; - write a valu"
},
{
"path": "blow_out_a_LED/CMakeLists.txt",
"chars": 194,
"preview": "add_executable(blow_led)\n\ntarget_sources(blow_led PRIVATE blow_led.cpp)\n\ntarget_link_libraries(blow_led PRIVATE\n "
},
{
"path": "blow_out_a_LED/README.md",
"chars": 1502,
"preview": "# Blow out a(n) LED\nThis is remake of the wonderful little thingy made by Paul Dietz. See this [Hackaday article](https:"
},
{
"path": "blow_out_a_LED/blow_led.cpp",
"chars": 6178,
"preview": "/*\n\nThis is remake of the wonderful little thingy made by Paul Dietz. See:\nhttps://hackaday.com/2018/08/21/an-led-you-ca"
},
{
"path": "button_matrix_4x4/4x4_button_matrix.cpp",
"chars": 3072,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"4x4_button_matrix.pio.h\"\n\n// class tha"
},
{
"path": "button_matrix_4x4/4x4_button_matrix.pio",
"chars": 1136,
"preview": "; in the c-program the 'set' pins are the 4 output pins to the 4x4 button matrix\n; in the c-program the 'in' pins are th"
},
{
"path": "button_matrix_4x4/CMakeLists.txt",
"chars": 413,
"preview": "add_executable(4x4_button_matrix)\n\npico_generate_pio_header(4x4_button_matrix ${CMAKE_CURRENT_LIST_DIR}/4x4_button_matri"
},
{
"path": "button_matrix_4x4/README.md",
"chars": 1323,
"preview": "# 4x4 button matrix\n\nThis code reads a 4x4 button matrix using PIO code for the Raspberry Pico. In the image below a sim"
},
{
"path": "count_pulses_with_pause/CMakeLists.txt",
"chars": 460,
"preview": "add_executable(count_pulses_with_pause)\n\npico_generate_pio_header(count_pulses_with_pause ${CMAKE_CURRENT_LIST_DIR}/coun"
},
{
"path": "count_pulses_with_pause/README.md",
"chars": 340,
"preview": "# Counting pulses in a pulse train separated by a pause \n\nThis class can be used for protocols where the data is encoded"
},
{
"path": "count_pulses_with_pause/count_pulses_with_pause.cpp",
"chars": 2441,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/irq.h\"\n#include \"hardware/pio.h\"\n\n#include \"count_pulses"
},
{
"path": "count_pulses_with_pause/count_pulses_with_pause.pio",
"chars": 1989,
"preview": "\n.program count_pulses_with_pause\n\n; algorithm:\n\n; This PIO program counts the number of pulses in a pulse train.\n; In t"
},
{
"path": "example_auto_set_url.cmake",
"chars": 297,
"preview": "set(PICO_EXAMPLE_URL_BASE \"https://github.com/raspberrypi/pico-examples/tree/HEAD\")\nmacro(example_auto_set_url TARGET)\n "
},
{
"path": "handy_bits_and_pieces/README.md",
"chars": 5948,
"preview": "# Handy bits and pieces of PIO code \n\nOn this page I list some of the things that I found out while making the projects."
},
{
"path": "ledpanel/CMakeLists.txt",
"chars": 343,
"preview": "add_executable(ledpanel)\n\npico_generate_pio_header(ledpanel ${CMAKE_CURRENT_LIST_DIR}/ledpanel.pio)\n\ntarget_sources(ledp"
},
{
"path": "ledpanel/README.md",
"chars": 4393,
"preview": "# LED panel using PIO state machine and Direct Memory Access\n\nThis code shows how a pio state machine can drive led pane"
},
{
"path": "ledpanel/ledpanel.c",
"chars": 8283,
"preview": "\n#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/irq."
},
{
"path": "ledpanel/ledpanel.h",
"chars": 6362,
"preview": "#ifndef LEDPANELH\n#define LEDPANELH\n\n/*\n\nThis code shows how a pio state machine can work with led panels. It is made fo"
},
{
"path": "ledpanel/ledpanel.pio",
"chars": 1820,
"preview": ".program ledpanel\n ; side_set PINS: \n ; MSB: blank (0=screen on, 1=screen off)\n ; cl"
},
{
"path": "ledpanel/ledpanel_worker.c",
"chars": 12422,
"preview": "#include \"stdio.h\"\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n#include \"hardware/irq.h"
},
{
"path": "multiplication/CMakeLists.txt",
"chars": 357,
"preview": "add_executable(multiplier)\n\npico_generate_pio_header(multiplier ${CMAKE_CURRENT_LIST_DIR}/multiplier.pio)\n\ntarget_source"
},
{
"path": "multiplication/README.md",
"chars": 560,
"preview": "# multiply two numbers \n\nIn the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) there"
},
{
"path": "multiplication/multiplier.cpp",
"chars": 1100,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"multiplier.pio.h\"\n\n// the pio instance"
},
{
"path": "multiplication/multiplier.pio",
"chars": 1761,
"preview": "\n.program multiplier\n\n; overall approach:\n ; get the two numbers to be multiplied, called m1 and m2\n ; m2 is saved"
},
{
"path": "pico_sdk_import.cmake",
"chars": 2763,
"preview": "# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake\n\n# This can be dropped into an external project to he"
},
{
"path": "sm_to_dma_to_buffer/CMakeLists.txt",
"chars": 450,
"preview": "add_executable(sm_to_dma_to_buffer)\n\npico_generate_pio_header(sm_to_dma_to_buffer ${CMAKE_CURRENT_LIST_DIR}/sm_to_dma_to"
},
{
"path": "sm_to_dma_to_buffer/README.md",
"chars": 295,
"preview": "# State machine writes into a buffer via DMA\n\nThis is an example of a state machine (sm) using DMA (Direct Memory Access"
},
{
"path": "sm_to_dma_to_buffer/sm_to_dma_to_buffer.cpp",
"chars": 2212,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/dma.h\"\n\n#include \"sm_to_dma_to"
},
{
"path": "sm_to_dma_to_buffer/sm_to_dma_to_buffer.pio",
"chars": 278,
"preview": "\n.program sm_to_dma_to_buffer\n\nstart0:\n mov x ~NULL ; start with 0xFFFFFFFF\npush_it0:\n mov ISR ~x "
},
{
"path": "sm_to_dma_to_sm_to_dma_to_buffer/CMakeLists.txt",
"chars": 554,
"preview": "add_executable(sm_to_dma_to_sm_to_dma_to_buffer)\n\npico_generate_pio_header(sm_to_dma_to_sm_to_dma_to_buffer ${CMAKE_CURR"
},
{
"path": "sm_to_dma_to_sm_to_dma_to_buffer/README.md",
"chars": 1652,
"preview": "# State Machine -> DMA -> State Machine -> DMA -> Buffer\nThis is an example where one state machine writes via DMA to an"
},
{
"path": "sm_to_dma_to_sm_to_dma_to_buffer/sm_to_dma_to_sm_to_dma_to_buffer.cpp",
"chars": 4937,
"preview": "/*\n\nThe goal of this program is to show how DMA can be used to transfer data from one state machine to the next and from"
},
{
"path": "sm_to_dma_to_sm_to_dma_to_buffer/sm_to_dma_to_sm_to_dma_to_buffer.pio",
"chars": 1245,
"preview": "// Note: \n// program tester1 is 4 instructions (intentionally not using .wrap) while program tester0 is only 3 instructi"
},
{
"path": "state_machine_emulator/README.md",
"chars": 14535,
"preview": "# Emulator of a Raspberry Pi Pico PIO state machine\n\n## What does it do\nThis emulator allows you to step through the wor"
},
{
"path": "state_machine_emulator/config.py",
"chars": 70,
"preview": "SAVE_TEST_DATA = False\n\nEMULATION_STEPS = 500\nSTATEMACHINE_NUMBER = 0\n"
},
{
"path": "state_machine_emulator/emulation.py",
"chars": 11952,
"preview": "from copy import deepcopy\n\n\nclass emulation:\n \"\"\" This class controls the emulation of the sm of a RP2040 \"\"\"\n\n de"
},
{
"path": "state_machine_emulator/examples/button_debounce/c_program",
"chars": 1057,
"preview": "# timestamp, command, arguments of command\n0, in_base, 0\n0, jmp_pin, 0\n5, get_pc\n10, get_pc\n15, get_pc\n20, get_pc\n21, ge"
},
{
"path": "state_machine_emulator/examples/button_debounce/pin_program",
"chars": 524,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/button_debounce/pio_program.pio.h",
"chars": 1276,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/in_shift/c_program",
"chars": 118,
"preview": "# timestamp, command, arguments of command\n0, in_base, 0\n0, in_shift_right, 0\n9, in_base, 10\n9, in_shift_right, 1\n"
},
{
"path": "state_machine_emulator/examples/in_shift/pin_program",
"chars": 677,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/in_shift/pio_program.pio.h",
"chars": 897,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/irq_set_and_clear/c_program",
"chars": 130,
"preview": "# timestamp, command, arguments of command\n10, irq, 0\n12, irq, 1\n14, irq, 2\n16, irq, 3\n18, irq, 4\n20, irq, 5\n22, irq, 6\n"
},
{
"path": "state_machine_emulator/examples/irq_set_and_clear/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/irq_set_and_clear/pio_program.pio.h",
"chars": 1386,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/ledpanel/c_program",
"chars": 661,
"preview": "# timestamp, command [, argument of command]\n0, sideset_base, 13\n0, out_shift_right, True\n0, out_shift_autopull, True\n0,"
},
{
"path": "state_machine_emulator/examples/ledpanel/ledpanel.pio.h",
"chars": 1509,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/ledpanel/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/multiplication/c_program",
"chars": 101,
"preview": "# timestamp, command, arguments of command\n0, put, 5\n0, put, 3\n55, get\n56, put, 7\n56, put, 8\n225, get"
},
{
"path": "state_machine_emulator/examples/multiplication/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/multiplication/pio_program.pio.h",
"chars": 1620,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/push_pull_auto/c_program",
"chars": 485,
"preview": "0, out_shift_right, True\n0, out_shift_autopull, True\n0, pull_threshold, 10\n0, in_shift_right, False\n0, in_shift_autopush"
},
{
"path": "state_machine_emulator/examples/push_pull_auto/pin_program",
"chars": 10,
"preview": "0, all, 0\n"
},
{
"path": "state_machine_emulator/examples/push_pull_auto/push_pull.pio.h",
"chars": 984,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/rotational_shift/c_program",
"chars": 701,
"preview": "# timestamp, command, arguments of command\n0, put, 1234567890\n10, get\n15, get\n20, get\n25, get\n30, get\n35, get\n40, get\n45"
},
{
"path": "state_machine_emulator/examples/rotational_shift/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/rotational_shift/pio_program.pio.h",
"chars": 1608,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/side_step/README.md",
"chars": 216,
"preview": "# The sidestep also works with two GPIO as output:\n\n## pio program:\n```\n.program tester\n \n.side_set 2 opt\n\n.wrap_target\n"
},
{
"path": "state_machine_emulator/examples/side_step/c_program",
"chars": 61,
"preview": "# timestamp, command, arguments of command\n0, sideset_base, 4"
},
{
"path": "state_machine_emulator/examples/side_step/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/side_step/pio_program.pio.h",
"chars": 1052,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/square_wave/c_program",
"chars": 105,
"preview": "# timestamp, command, arguments of command (typically pio, sm, arguments)\n0, set_base, 0\n0, set_count, 1\n"
},
{
"path": "state_machine_emulator/examples/square_wave/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/square_wave/pio_program.pio.h",
"chars": 1089,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "state_machine_emulator/examples/stepper/c_program",
"chars": 127,
"preview": "# timestamp, command, arguments of command\n0, out_base, 0\n0, out_count, 4\n0, put, 100\n0, put, 2216789025\n0, out_shift_ri"
},
{
"path": "state_machine_emulator/examples/stepper/pin_program",
"chars": 261,
"preview": "# timestamp, pinnumber, state\n# timestamp 0 is the first clock\n# pinnumber = all means all GPIOs\n# pinnumber = GPIOx mea"
},
{
"path": "state_machine_emulator/examples/stepper/pio_program.pio.h",
"chars": 1311,
"preview": "// This example is taken from:\n// https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4."
},
{
"path": "state_machine_emulator/interface/__init__.py",
"chars": 2993,
"preview": "from tkinter import Tk\nfrom threading import Thread\n\nclass Emulator_Interface(Thread):\n \"\"\" This class builds the GUI"
},
{
"path": "state_machine_emulator/interface/_interface_item.py",
"chars": 7465,
"preview": "from tkinter import Text, Label, Listbox\nfrom interface._tooltips import CreateToolTip\n\n\"\"\"\nAll the classes here are an "
},
{
"path": "state_machine_emulator/interface/_left_frame.py",
"chars": 2303,
"preview": "from tkinter import Frame\n\n\ndef build_left_frame(self):\n \"\"\" build the panel on the left with the c-program, pin_prog"
},
{
"path": "state_machine_emulator/interface/_mid_frame.py",
"chars": 9937,
"preview": "from tkinter import Frame, Label, Text\nfrom config import STATEMACHINE_NUMBER\n\ndef build_mid_frame(self):\n \"\"\" build "
},
{
"path": "state_machine_emulator/interface/_output_frame.py",
"chars": 1885,
"preview": "from tkinter import Frame, Label, Listbox, Text\n\n\ndef build_output_frame(self):\n \"\"\" build the frame with the output "
},
{
"path": "state_machine_emulator/interface/_right_frame.py",
"chars": 1503,
"preview": "from tkinter import Frame, Label, Listbox\n\n\ndef build_right_frame(self):\n \"\"\" build the panel with the pio program \"\""
},
{
"path": "state_machine_emulator/interface/_toolbar.py",
"chars": 5798,
"preview": "from tkinter import Frame, Button\n\n\"\"\"\n build the toolbar with associated call back functions and key bindings\n\"\"\"\n\nd"
},
{
"path": "state_machine_emulator/interface/_tooltips.py",
"chars": 1757,
"preview": "# thanks to crxguy52 and Stevoisiak:\n# https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter\n"
},
{
"path": "state_machine_emulator/main.py",
"chars": 10719,
"preview": "\"\"\"\nTODO:\n- check the 'status' with 'set_N' and 'status_sel' with the actual hardware!\n Wait! not just the status, bu"
},
{
"path": "state_machine_emulator/state_machine/__init__.py",
"chars": 4987,
"preview": "from config import STATEMACHINE_NUMBER\n\nclass state_machine:\n \"\"\" this class emulates a state machine (sm) \"\"\"\n\n f"
},
{
"path": "state_machine_emulator/state_machine/_do_sideset.py",
"chars": 1780,
"preview": "def do_sideset(self, instruction_delay_sideset):\n \"\"\" handle sideset \"\"\"\n # TODO: is 3.5.1. fully covered? Especia"
},
{
"path": "state_machine_emulator/state_machine/_execute_instructions.py",
"chars": 19536,
"preview": "def execute_instruction(self, instruction):\n \"\"\" Execute the PIO instruction \"\"\"\n # get the three bits that encode"
},
{
"path": "state_machine_emulator/state_machine/_push_pull.py",
"chars": 1068,
"preview": "def push_to_RxFIFO(self):\n \"\"\" push data to the RxFIFO (it has already been checked that there is space!) \"\"\"\n # p"
},
{
"path": "state_machine_emulator/state_machine/_set_all_GPIO.py",
"chars": 2578,
"preview": "def set_all_GPIO(self):\n \"\"\" sets the GPIO according to the changes in the time step; there is a specific priority or"
},
{
"path": "state_machine_emulator/state_machine/_time_step.py",
"chars": 2513,
"preview": "def time_step(self):\n \"\"\" emulate one time step \"\"\"\n # prepare for warning messages\n self.sm_warning_messages ="
},
{
"path": "subroutines/CMakeLists.txt",
"chars": 286,
"preview": "add_executable(subroutine)\n\npico_generate_pio_header(subroutine ${CMAKE_CURRENT_LIST_DIR}/subroutine.pio)\n\ntarget_source"
},
{
"path": "subroutines/README.md",
"chars": 3419,
"preview": "# Subroutines in PIO code\n\n## What? Subroutines? There's no such thing in pioasm\nThat is true, but you can make somethin"
},
{
"path": "subroutines/subroutine.cpp",
"chars": 1303,
"preview": "/*\n This is the C SDK code needed to start the accompanying pio code.\n It only defines a pin (for out and set) as "
},
{
"path": "subroutines/subroutine.pio",
"chars": 1989,
"preview": "; This is a proof of principle of using subroutines in PIO code.\n\n.program subroutine\n ; START THE PROGRAM AT ADDRESS"
},
{
"path": "two_pio_programs_one_file/CMakeLists.txt",
"chars": 365,
"preview": "add_executable(two_p_one_f)\n\npico_generate_pio_header(two_p_one_f ${CMAKE_CURRENT_LIST_DIR}/two_p_one_f.pio)\n\ntarget_sou"
},
{
"path": "two_pio_programs_one_file/README.md",
"chars": 859,
"preview": "This example contains two pio programs.\nIt was actually part of getting the 1-wire protocol working, and that requires a"
},
{
"path": "two_pio_programs_one_file/two_p_one_f.cpp",
"chars": 1655,
"preview": "#include <stdio.h>\n\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n\n#include \"two_p_one_f.pio.h\"\n\n#define TEST_PIN 1"
},
{
"path": "two_pio_programs_one_file/two_p_one_f.pio",
"chars": 1453,
"preview": "\n;\n; This file contains two pio programs.\n; It is assumed that an external pull up resistor is present on the pin used b"
},
{
"path": "two_pio_programs_one_file/two_p_one_f.pio.h",
"chars": 2122,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "ws2812_led_strip_120/CMakeLists.txt",
"chars": 435,
"preview": "add_executable(ws2812_led_strip_120)\n\npico_generate_pio_header(ws2812_led_strip_120 ${CMAKE_CURRENT_LIST_DIR}/ws2812_led"
},
{
"path": "ws2812_led_strip_120/README.md",
"chars": 1426,
"preview": "# Ws2812 led strip with 120 pixels \n\nThis is my take on how to control a ws2812 led strip with 120 pixels. It differs fr"
},
{
"path": "ws2812_led_strip_120/ws2812_led_strip_120.c",
"chars": 4336,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n#include \"pico/stdlib.h\"\n#include \"hardware/pio.h\"\n#include \"ha"
},
{
"path": "ws2812_led_strip_120/ws2812_led_strip_120.pio",
"chars": 2739,
"preview": "; This code sends pixel color data for ws2812 leds for a led strip of 120 leds.\n; Note: the data send from the c-code mu"
}
]
About this extraction
This page contains the full source code of the GitJer/Some_RPI-Pico_stuff GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 162 files (363.6 KB), approximately 105.2k tokens, and a symbol index with 194 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.