Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pio_ppm - RC PPM protocol example works on GPIO 3 #414

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if (NOT PICO_NO_HARDWARE)
add_subdirectory(onewire)
add_subdirectory(pio_blink)
add_subdirectory(pwm)
add_subdirectory(ppm)
add_subdirectory(quadrature_encoder)
add_subdirectory(spi)
add_subdirectory(squarewave)
Expand Down
11 changes: 11 additions & 0 deletions pio/ppm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
add_executable(pio_ppm)

pico_generate_pio_header(pio_ppm ${CMAKE_CURRENT_LIST_DIR}/ppm.pio)

target_sources(pio_ppm PRIVATE ppm.c)

target_link_libraries(pio_ppm PRIVATE pico_stdlib hardware_pio hardware_clocks)
pico_add_extra_outputs(pio_ppm)

# add url via pico_set_program_url
example_auto_set_url(pio_ppm)
42 changes: 42 additions & 0 deletions pio/ppm/ppm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

/*
* 8-channel Radio Control PPM example
* Output on GPIO3
*

* synchro | ch0 value 1-2ms | ch1 value 1-2ms |...| ch8 value 1-2ms |
* >=5ms _______ _______ _______ _______
* _______...__| 0.5ms |_____________| 0.5ms |___________|...| 0.5ms |____________| 0.5ms |_...
*
*/
#include <stdio.h>

#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "hardware/pio.h"
#include "ppm.pio.h"

void set_value_and_log(uint channel, uint value_usec) {
printf("Channel %d is set to value %5.3f ms\r\n", channel, (float)value_usec / 1000.0f);
ppm_set_value(channel, value_usec);

}
int main() {
setup_default_uart();
uint pin = 3;
ppm_program_init(pio0, pin);
while (1) {
set_value_and_log(1, 1100);
set_value_and_log(8, 1800);
sleep_ms(1000);
set_value_and_log(1, 1700);
set_value_and_log(8, 1200);
sleep_ms(1000);
}
}

83 changes: 83 additions & 0 deletions pio/ppm/ppm.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;

.program ppm
.side_set 1

set pindirs, 1 side 0; Set pin to output
.wrap_target
out x, 16 side 0
delay0:
jmp x-- delay0 side 0
out x, 16 side 1
delay1:
jmp x-- delay1 side 1
.wrap

% c-sdk {
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "ppm.pio.h"


#define PPM_CHANNELS (8)
struct {
volatile PIO pio;
// 16 MSB - strobe length in microsec
// 16 LSB - gap length in microsec
volatile uint32_t ch_values[PPM_CHANNELS + 1];
volatile uint value_idx;
} ppm_data;

void ppm_handler() {
while (!pio_sm_is_tx_fifo_full(ppm_data.pio, 0)) {
pio_sm_put(ppm_data.pio, 0, ppm_data.ch_values[ppm_data.value_idx]);
ppm_data.value_idx = (ppm_data.value_idx + 1) % (PPM_CHANNELS + 1);
}
}

// 500 microseconds strobe + gap = value (microseconds)
#define BUILD_CH_VALUE(value_us) (((500 - 1) << 16) + (value_us - 500 - 1))

static inline void ppm_program_init(PIO pio, uint pin) {
ppm_data.pio = pio == NULL ? pio0 : pio;
ppm_data.ch_values[0] = BUILD_CH_VALUE(5000);
elmot marked this conversation as resolved.
Show resolved Hide resolved
// Neutral position (1500us) by default
for (int i = 1; i <= PPM_CHANNELS; ++i)
ppm_data.ch_values[i] = BUILD_CH_VALUE(1500);
ppm_data.value_idx = 0;
uint offset = pio_add_program(ppm_data.pio, &ppm_program);
pio_gpio_init(ppm_data.pio, pin);
pio_sm_set_consecutive_pindirs(pio, 0, pin, 1, true);
pio_sm_config pio_conf = ppm_program_get_default_config(offset);
sm_config_set_sideset_pins(&pio_conf, pin);
sm_config_set_set_pins(&pio_conf, 0, 1);
sm_config_set_out_shift(&pio_conf, true, true, 0);
sm_config_set_fifo_join(&pio_conf, PIO_FIFO_JOIN_TX);
pio_sm_init(ppm_data.pio, 0, offset, &pio_conf);
pio_sm_set_clkdiv(ppm_data.pio, 0, (float) clock_get_hz(clk_sys) / 1000000); //1MHz
pio_sm_clear_fifos(ppm_data.pio, 0);
pio_sm_restart(ppm_data.pio, 0);

pio_set_irq0_source_enabled(ppm_data.pio, PIO_INTR_SM0_TXNFULL_LSB, true);
irq_set_exclusive_handler(PIO0_IRQ_0, ppm_handler);
irq_set_enabled(PIO0_IRQ_0, true);
pio_sm_set_enabled(ppm_data.pio, 0, true);
}

/** Sets channel value
*
* @channel_number RC channel number [1..PPM_CHANNELS]
* @value_usec channel vaue [1000, 2000]
*/
static inline void ppm_set_value(uint channel_number, uint value_usec) {
valid_params_if(PIO, channel_number <= PPM_CHANNELS);
valid_params_if(PIO, channel_number >= 1);
valid_params_if(PIO, value_usec <= 2000);
valid_params_if(PIO, value_usec >= 1000);
ppm_data.ch_values[channel_number] = BUILD_CH_VALUE(value_usec);
}
%}