diff --git a/rp2040_uart/2024-03_rp2040-uart.md b/rp2040_uart/2024-03_rp2040-uart.md index bf9099baff83b1fb18482cb946e3731cd8063f02..9daf10438daded379adf44aa3a4ee1af8db9d4db 100644 --- a/rp2040_uart/2024-03_rp2040-uart.md +++ b/rp2040_uart/2024-03_rp2040-uart.md @@ -12,6 +12,8 @@ So, as for reasonable goals for today, I should basically just try to throw-and- Well actually, fk it, I will use the online pioasm for the time being... +### UART PIO TX'ing + 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... This should be simple: we have output is @@ -31,4 +33,10 @@ So we should be able to to clk/8 : 16MBit/s, and indeed we can see things workin | 25 |  | | 30 |  | -But this is not terribly interesting: we want to see that we can catch words fast enough: the ISR on the RX side is normally where we meet our limits. \ No newline at end of file +But this is not terribly interesting: we want to see that we can catch words fast enough: the ISR on the RX side is normally where we meet our limits. + +### UART PIO RX'ing + +So, I should spin up an RX line now and see about firing an interrupt there... I'll get one chip TX'ing at a fixed rate and then start in on no. 2 + +... doing this using [the blocking example](https://github.com/raspberrypi/pico-examples/blob/master/pio/uart_rx/uart_rx.c) catches *some* bytes, but not that many (at only 1mbaud), and it's perhaps only latching when we *just* catch the byte in time, i.e. if we poll just as the last bit has arrived. \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio/screen.cpp b/rp2040_uart/code/uart_pio_rx/screen.cpp similarity index 100% rename from rp2040_uart/code/uart_pio/screen.cpp rename to rp2040_uart/code/uart_pio_rx/screen.cpp diff --git a/rp2040_uart/code/uart_pio/screen.h b/rp2040_uart/code/uart_pio_rx/screen.h similarity index 100% rename from rp2040_uart/code/uart_pio/screen.h rename to rp2040_uart/code/uart_pio_rx/screen.h diff --git a/rp2040_uart/code/uart_pio_rx/uart_pio_rx.ino b/rp2040_uart/code/uart_pio_rx/uart_pio_rx.ino new file mode 100644 index 0000000000000000000000000000000000000000..d79b4d0fa9414b985c939032a62d28a07f2d2702 --- /dev/null +++ b/rp2040_uart/code/uart_pio_rx/uart_pio_rx.ino @@ -0,0 +1,51 @@ +#include "screen.h" +#include "uart_rx.pio.h" + +// using an RP2040 XIAO +// with earle philhower core + +// "D10" - GPIO 3 +#define PIN_DEBUG 3 + +// on XIAO "RX" - GPIO 1 +#define PIN_RX 1 +#define PIO_BAUD 1000000 + +// 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_rx_program); + uart_rx_program_init(pio, sm, offset, PIN_RX, PIO_BAUD); +} + +uint32_t lastUpdate = 0; +uint32_t updateInterval = 200; + +void loop(void){ + digitalWrite(PIN_DEBUG, !digitalRead(PIN_DEBUG)); + uint8_t data = uart_rx_program_getc(pio, sm); + // 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_rx/uart_rx.pio b/rp2040_uart/code/uart_pio_rx/uart_rx.pio new file mode 100644 index 0000000000000000000000000000000000000000..4371915a927868f4c0fd0e9d036d76839fbf4a4d --- /dev/null +++ b/rp2040_uart/code/uart_pio_rx/uart_rx.pio @@ -0,0 +1,94 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + +.program uart_rx_mini + +; Minimum viable 8n1 UART receiver. Wait for the start bit, then sample 8 bits +; with the correct timing. +; IN pin 0 is mapped to the GPIO used as UART RX. +; Autopush must be enabled, with a threshold of 8. + + wait 0 pin 0 ; Wait for start bit + set x, 7 [10] ; Preload bit counter, delay until eye of first data bit +bitloop: ; Loop 8 times + in pins, 1 ; Sample data + jmp x-- bitloop [6] ; Each iteration is 8 cycles + +% c-sdk { +#include "hardware/clocks.h" +#include "hardware/gpio.h" + +static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + + pio_sm_config c = uart_rx_mini_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + // Shift to right, autopush enabled + sm_config_set_in_shift(&c, true, true, 8); + 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 * baud); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%} + +.program uart_rx + +; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and +; break conditions more gracefully. +; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. + +start: + wait 0 pin 0 ; Stall until start bit is asserted + set x, 7 [10] ; Preload bit counter, then delay until halfway through +bitloop: ; the first data bit (12 cycles incl wait, set). + in pins, 1 ; Shift data bit into ISR + jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_stop ; Check stop bit (should be high) + + irq 4 rel ; Either a framing error or a break. Set a sticky flag, + wait 1 pin 0 ; and wait for line to return to idle state. + jmp start ; Don't push data if we didn't see good framing. + +good_stop: ; No delay before returning to start; a little slack is + push ; important in case the TX clock is slightly too fast. + + +% c-sdk { +static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + + pio_sm_config c = uart_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin); // for JMP + // Shift to right, autopush disabled + sm_config_set_in_shift(&c, true, 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 * baud); + sm_config_set_clkdiv(&c, div); + + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +static inline char uart_rx_program_getc(PIO pio, uint sm) { + // 8-bit read from the uppermost byte of the FIFO, as data is left-justified + io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3; + while (pio_sm_is_rx_fifo_empty(pio, sm)) + tight_loop_contents(); + return (char)*rxfifo_shift; +} + +%} \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio_rx/uart_rx.pio.h b/rp2040_uart/code/uart_pio_rx/uart_rx.pio.h new file mode 100644 index 0000000000000000000000000000000000000000..c33c341fe28514a32233dd8dbbf7bf1c172bd833 --- /dev/null +++ b/rp2040_uart/code/uart_pio_rx/uart_rx.pio.h @@ -0,0 +1,120 @@ +// -------------------------------------------------- // +// This file is autogenerated by pioasm; do not edit! // +// -------------------------------------------------- // + +#pragma once + +#if !PICO_NO_HARDWARE +#include "hardware/pio.h" +#endif + +// ------------ // +// uart_rx_mini // +// ------------ // + +#define uart_rx_mini_wrap_target 0 +#define uart_rx_mini_wrap 3 + +static const uint16_t uart_rx_mini_program_instructions[] = { + // .wrap_target + 0x2020, // 0: wait 0 pin, 0 + 0xea27, // 1: set x, 7 [10] + 0x4001, // 2: in pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program uart_rx_mini_program = { + .instructions = uart_rx_mini_program_instructions, + .length = 4, + .origin = -1, +}; + +static inline pio_sm_config uart_rx_mini_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + uart_rx_mini_wrap_target, offset + uart_rx_mini_wrap); + return c; +} + +#include "hardware/clocks.h" +#include "hardware/gpio.h" +static inline void uart_rx_mini_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + pio_sm_config c = uart_rx_mini_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + // Shift to right, autopush enabled + sm_config_set_in_shift(&c, true, true, 8); + 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 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} + +#endif + +// ------- // +// uart_rx // +// ------- // + +#define uart_rx_wrap_target 0 +#define uart_rx_wrap 8 + +static const uint16_t uart_rx_program_instructions[] = { + // .wrap_target + 0x2020, // 0: wait 0 pin, 0 + 0xea27, // 1: set x, 7 [10] + 0x4001, // 2: in pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + 0x00c8, // 4: jmp pin, 8 + 0xc014, // 5: irq nowait 4 rel + 0x20a0, // 6: wait 1 pin, 0 + 0x0000, // 7: jmp 0 + 0x8020, // 8: push block + // .wrap +}; + +#if !PICO_NO_HARDWARE +static const struct pio_program uart_rx_program = { + .instructions = uart_rx_program_instructions, + .length = 9, + .origin = -1, +}; + +static inline pio_sm_config uart_rx_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + uart_rx_wrap_target, offset + uart_rx_wrap); + return c; +} + +static inline void uart_rx_program_init(PIO pio, uint sm, uint offset, uint pin, uint baud) { + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false); + pio_gpio_init(pio, pin); + gpio_pull_up(pin); + pio_sm_config c = uart_rx_program_get_default_config(offset); + sm_config_set_in_pins(&c, pin); // for WAIT, IN + sm_config_set_jmp_pin(&c, pin); // for JMP + // Shift to right, autopush disabled + sm_config_set_in_shift(&c, true, 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 * baud); + sm_config_set_clkdiv(&c, div); + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +static inline char uart_rx_program_getc(PIO pio, uint sm) { + // 8-bit read from the uppermost byte of the FIFO, as data is left-justified + io_rw_8 *rxfifo_shift = (io_rw_8*)&pio->rxf[sm] + 3; + while (pio_sm_is_rx_fifo_empty(pio, sm)) + tight_loop_contents(); + return (char)*rxfifo_shift; +} + +#endif + \ No newline at end of file diff --git a/rp2040_uart/code/uart_pio_tx/screen.cpp b/rp2040_uart/code/uart_pio_tx/screen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45f33a49e0baa49ed6a53d6334f04f65afcb17b1 --- /dev/null +++ b/rp2040_uart/code/uart_pio_tx/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_tx/screen.h b/rp2040_uart/code/uart_pio_tx/screen.h new file mode 100644 index 0000000000000000000000000000000000000000..91fb08f0bd738345349813ee51aec5739694a9f9 --- /dev/null +++ b/rp2040_uart/code/uart_pio_tx/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_tx/uart_pio_tx.ino similarity index 95% rename from rp2040_uart/code/uart_pio/uart_pio.ino rename to rp2040_uart/code/uart_pio_tx/uart_pio_tx.ino index 12becd59659c683577ac741cf05c6222bde05a44..cf3e913337e31597d6740b6de5e11ee2cb02b54d 100644 --- a/rp2040_uart/code/uart_pio/uart_pio.ino +++ b/rp2040_uart/code/uart_pio_tx/uart_pio_tx.ino @@ -9,7 +9,7 @@ // on XIAO "TX" - GPIO 0 #define PIN_TX 0 -#define PIO_BAUD 30000000 +#define PIO_BAUD 1000000 // the PIO, and statemachine ? PIO pio = pio0; @@ -38,7 +38,7 @@ void loop(void){ digitalWrite(PIN_DEBUG, HIGH); // blocking tx-put: uart_tx_program_putc(pio, sm, 85); - delayMicroseconds(100); + delayMicroseconds(30); digitalWrite(PIN_DEBUG, LOW); // ... if(lastUpdate + updateInterval < millis()){ diff --git a/rp2040_uart/code/uart_pio/uart_tx.pio b/rp2040_uart/code/uart_pio_tx/uart_tx.pio similarity index 100% rename from rp2040_uart/code/uart_pio/uart_tx.pio rename to rp2040_uart/code/uart_pio_tx/uart_tx.pio diff --git a/rp2040_uart/code/uart_pio/uart_tx.pio.h b/rp2040_uart/code/uart_pio_tx/uart_tx.pio.h similarity index 100% rename from rp2040_uart/code/uart_pio/uart_tx.pio.h rename to rp2040_uart/code/uart_pio_tx/uart_tx.pio.h