diff --git a/README.md b/README.md index 1c9d6923147b8b64960a6f92dcc846c359d12af7..badbb05b9a1a22d857ae48fa6dd6190b375369a9 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,5 @@ - actually-realized data rates are often smaller in practice than according to underlying specs, - see even i.e. transfer of some tens-of-megabytes onto an SD card (copying a gcode file) - we observe only ~ 20mb/sec of advertized 80mb/sec or so - otherwise, you have notes on what-the-point is in your `scratch` -- and consider linking to the ring-test page \ No newline at end of file +- and consider linking to the ring-test page +- and discuss that at the embedded limit, we often run into interrupt-request-handling-time troubles before anything else... \ No newline at end of file diff --git a/rp2040_uart/2024-03_rp2040-uart.md b/rp2040_uart/2024-03_rp2040-uart.md new file mode 100644 index 0000000000000000000000000000000000000000..613cf3ba9da1c8c9212bfdc898b70292e4384394 --- /dev/null +++ b/rp2040_uart/2024-03_rp2040-uart.md @@ -0,0 +1,15 @@ +## 2024 01 03 + +Today I'd like to check out the RP2040's PIO via the UART example, and see how fast we can sendy UART frames between two devices, as a preliminary speed test. + +So - we're wired up, we should hit the scope and then fire up some test code: display (?) uart PIO, blinky, etc ... + +The first thing [I'm learning here](https://www.eevblog.com/forum/microcontrollers/8-uarts-using-asm_pio-pio-dma-micropython-on-the-rpi-pico/) and as shown in the [pio rx](https://github.com/raspberrypi/pico-examples/tree/master/pio/uart_rx) and [pio tx](https://github.com/raspberrypi/pico-examples/blob/master/pio/uart_tx/uart_tx.pio) examples is that each state machine can only do one half of a UART... TX or RX, not both as we are accustomed to with a UART peripheral. So, since we are interested in building our little router thing, we actually would only be able to max out at 6 total UARTS there: 2x the-og-peripheral, and 4x PIOs. + +The second thing [I'm learning](https://www.instructables.com/Using-RP2040-PIO-in-Arduino-IDE-on-Windows/) is that working with PIO in C is not *that* simple; we write a PIO block `uart_tx.pio` and then use a pio-assembler to write `uart_tx.pio.h` that we can include in our sketch. There is an [online pioasm instance](https://wokwi.com/tools/pioasm). Doing this on windows is a little bit of a pain - and means that we will have two things to call before we can upload code, but not a major-major roadblock. + +So, as for reasonable goals for today, I should basically just try to throw-and-catch a block, really simple-like, to test baseline perf. + +Well actually, fk it, I will use the online pioasm for the time being... + +And we're up with a test, I will find the BAUD limit next, and check if that is affected by changes to f_cpu... \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio/screen.cpp b/rp2040_uart/code/uart_pio/screen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45f33a49e0baa49ed6a53d6334f04f65afcb17b1 --- /dev/null +++ b/rp2040_uart/code/uart_pio/screen.cpp @@ -0,0 +1,39 @@ +#include "screen.h" +#include <Adafruit_GFX.h> +#include <Adafruit_SSD1306.h> +#include <Wire.h> + +// OLED +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels + +#define X_POS 0 +#define Y_POS 0 +#define TXT_SIZE 1 + +// even for displays with i.e. "0x78" printed on the back, +// the address that works is 0x3C, IDK +#define SCREEN_ADDRESS 0x3C + +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT); + +// warning: is blocking, takes ~ 33ms ! +void displayPrint(String msg){ + display.clearDisplay(); + display.setCursor(X_POS, Y_POS); + display.print(msg); + display.display(); +} + +void displaySetup(void){ + // initialize the screen, + // oddly, SWITCHCAPVCC is the option that works even though OLED is hooked to 5V + display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS); + display.clearDisplay(); + display.display(); + display.setTextColor(SSD1306_WHITE); + display.setTextSize(TXT_SIZE); + display.setTextWrap(true); + display.dim(false); + displayPrint("bonjour..."); +} \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio/screen.h b/rp2040_uart/code/uart_pio/screen.h new file mode 100644 index 0000000000000000000000000000000000000000..91fb08f0bd738345349813ee51aec5739694a9f9 --- /dev/null +++ b/rp2040_uart/code/uart_pio/screen.h @@ -0,0 +1,9 @@ +#ifndef SCREEN_H_ +#define SCREEN_H_ + +#include <Arduino.h> + +void displaySetup(void); +void displayPrint(String msg); + +#endif \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio/uart_pio.ino b/rp2040_uart/code/uart_pio/uart_pio.ino new file mode 100644 index 0000000000000000000000000000000000000000..5e83bc668639d6eef3b32f46d35c0658c0d81d97 --- /dev/null +++ b/rp2040_uart/code/uart_pio/uart_pio.ino @@ -0,0 +1,52 @@ +#include "screen.h" +#include "uart_tx.pio.h" + +// using an RP2040 XIAO +// with earle philhower core + +// "D10" - GPIO 3 +#define PIN_DEBUG 3 + +// on XIAO "TX" - GPIO 0 +#define PIN_TX 0 +#define PIO_BAUD 115200 + +// the PIO, and statemachine ? +PIO pio = pio0; +uint sm = 0; +uint offset = 0; + +void setup(void){ + pinMode(PIN_LED_B, OUTPUT); + digitalWrite(PIN_LED_B, LOW); + + pinMode(PIN_DEBUG, OUTPUT); + digitalWrite(PIN_DEBUG, LOW); + + // the display setup + displaySetup(); + displayPrint("bonjour..."); + + offset = pio_add_program(pio, &uart_tx_program); + uart_tx_program_init(pio, sm, offset, PIN_TX, PIO_BAUD); +} + +uint32_t lastUpdate = 0; +uint32_t updateInterval = 200; + +void loop(void){ + digitalWrite(PIN_DEBUG, HIGH); + // blocking tx-put: + uart_tx_program_putc(pio, sm, 85); + digitalWrite(PIN_DEBUG, LOW); + // ... + if(lastUpdate + updateInterval < millis()){ + lastUpdate = millis(); + digitalWrite(PIN_LED_B, !digitalRead(PIN_LED_B)); + // displayPrint(spipi_print()); + // displayPrint(String(rxCount) + "\n" + + // String(rxSize) + // ); + } +} + diff --git a/rp2040_uart/code/uart_pio/uart_tx.pio b/rp2040_uart/code/uart_pio/uart_tx.pio new file mode 100644 index 0000000000000000000000000000000000000000..b6f91a89f02e181671ba5732830c9fd6f627383c --- /dev/null +++ b/rp2040_uart/code/uart_pio/uart_tx.pio @@ -0,0 +1,61 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program uart_tx +.side_set 1 opt + +; An 8n1 UART transmit program. +; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. + + pull side 1 [7] ; Assert stop bit, or stall with line in idle state + set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks +bitloop: ; This loop will run 8 times (8n1 UART) + out pins, 1 ; Shift 1 bit from OSR to the first OUT pin + jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. + + +% c-sdk { +#include "hardware/clocks.h" + +static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) { + // Tell PIO to initially drive output-high on the selected pin, then map PIO + // onto that pin with the IO muxes. + pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); + pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); + pio_gpio_init(pio, pin_tx); + + pio_sm_config c = uart_tx_program_get_default_config(offset); + + // OUT shifts to right, no autopull + sm_config_set_out_shift(&c, true, false, 32); + + // We are mapping both OUT and side-set to the same pin, because sometimes + // we need to assert user data onto the pin (with OUT) and sometimes + // assert constant values (start/stop bit) + sm_config_set_out_pins(&c, pin_tx, 1); + sm_config_set_sideset_pins(&c, pin_tx); + + // We only need TX, so get an 8-deep FIFO! + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +static inline void uart_tx_program_putc(PIO pio, uint sm, char c) { + pio_sm_put_blocking(pio, sm, (uint32_t)c); +} + +static inline void uart_tx_program_puts(PIO pio, uint sm, const char *s) { + while (*s) + uart_tx_program_putc(pio, sm, *s++); +} + +%} \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio/uart_tx.pio.h b/rp2040_uart/code/uart_pio/uart_tx.pio.h new file mode 100644 index 0000000000000000000000000000000000000000..30646d9d4d049df54e0763be4b20cb0c47cd463f --- /dev/null +++ b/rp2040_uart/code/uart_pio/uart_tx.pio.h @@ -0,0 +1,72 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------- // +// uart_tx // +// ------- // + +#define uart_tx_wrap_target 0 +#define uart_tx_wrap 3 + +static const uint16_t uart_tx_program_instructions[] = { + // .wrap_target + 0x9fa0, // 0: pull block side 1 [7] + 0xf727, // 1: set x, 7 side 0 [7] + 0x6001, // 2: out pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program uart_tx_program = { + .instructions = uart_tx_program_instructions, + .length = 4, + .origin = -1, +}; + +static inline pio_sm_config uart_tx_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + uart_tx_wrap_target, offset + uart_tx_wrap); + sm_config_set_sideset(&c, 2, true, false); + return c; +} + +#include "hardware/clocks.h" +static inline void uart_tx_program_init(PIO pio, uint sm, uint offset, uint pin_tx, uint baud) { + // Tell PIO to initially drive output-high on the selected pin, then map PIO + // onto that pin with the IO muxes. + pio_sm_set_pins_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); + pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin_tx, 1u << pin_tx); + pio_gpio_init(pio, pin_tx); + pio_sm_config c = uart_tx_program_get_default_config(offset); + // OUT shifts to right, no autopull + sm_config_set_out_shift(&c, true, false, 32); + // We are mapping both OUT and side-set to the same pin, because sometimes + // we need to assert user data onto the pin (with OUT) and sometimes + // assert constant values (start/stop bit) + sm_config_set_out_pins(&c, pin_tx, 1); + sm_config_set_sideset_pins(&c, pin_tx); + // We only need TX, so get an 8-deep FIFO! + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + // SM transmits 1 bit per 8 execution cycles. + float div = (float)clock_get_hz(clk_sys) / (8 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +static inline void uart_tx_program_putc(PIO pio, uint sm, char c) { + pio_sm_put_blocking(pio, sm, (uint32_t)c); +} +static inline void uart_tx_program_puts(PIO pio, uint sm, const char *s) { + while (*s) + uart_tx_program_putc(pio, sm, *s++); +} + +#endif