From 8b13d7a7465464bfcaeaefe0320346fcab53ad13 Mon Sep 17 00:00:00 2001
From: Jake <jake.read@cba.mit.edu>
Date: Wed, 3 Jan 2024 13:35:10 -0500
Subject: [PATCH] pio rx no-irq hello world

---
 rp2040_uart/2024-03_rp2040-uart.md            |  10 +-
 .../code/{uart_pio => uart_pio_rx}/screen.cpp |   0
 .../code/{uart_pio => uart_pio_rx}/screen.h   |   0
 rp2040_uart/code/uart_pio_rx/uart_pio_rx.ino  |  51 ++++++++
 rp2040_uart/code/uart_pio_rx/uart_rx.pio      |  94 ++++++++++++++
 rp2040_uart/code/uart_pio_rx/uart_rx.pio.h    | 120 ++++++++++++++++++
 rp2040_uart/code/uart_pio_tx/screen.cpp       |  39 ++++++
 rp2040_uart/code/uart_pio_tx/screen.h         |   9 ++
 .../uart_pio_tx.ino}                          |   4 +-
 .../{uart_pio => uart_pio_tx}/uart_tx.pio     |   0
 .../{uart_pio => uart_pio_tx}/uart_tx.pio.h   |   0
 11 files changed, 324 insertions(+), 3 deletions(-)
 rename rp2040_uart/code/{uart_pio => uart_pio_rx}/screen.cpp (100%)
 rename rp2040_uart/code/{uart_pio => uart_pio_rx}/screen.h (100%)
 create mode 100644 rp2040_uart/code/uart_pio_rx/uart_pio_rx.ino
 create mode 100644 rp2040_uart/code/uart_pio_rx/uart_rx.pio
 create mode 100644 rp2040_uart/code/uart_pio_rx/uart_rx.pio.h
 create mode 100644 rp2040_uart/code/uart_pio_tx/screen.cpp
 create mode 100644 rp2040_uart/code/uart_pio_tx/screen.h
 rename rp2040_uart/code/{uart_pio/uart_pio.ino => uart_pio_tx/uart_pio_tx.ino} (95%)
 rename rp2040_uart/code/{uart_pio => uart_pio_tx}/uart_tx.pio (100%)
 rename rp2040_uart/code/{uart_pio => uart_pio_tx}/uart_tx.pio.h (100%)

diff --git a/rp2040_uart/2024-03_rp2040-uart.md b/rp2040_uart/2024-03_rp2040-uart.md
index bf9099b..9daf104 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  | ![img](images/2024-01-03_uart-tx-25mb.png)   |
 | 30  | ![img](images/2024-01-03_uart-tx-30mb.png)   |
 
-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 0000000..d79b4d0
--- /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 0000000..4371915
--- /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 0000000..c33c341
--- /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 0000000..45f33a4
--- /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 0000000..91fb08f
--- /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 12becd5..cf3e913 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
-- 
GitLab