From 95b08e26ae1c37ef29c5be30851b2d27336c7ef8 Mon Sep 17 00:00:00 2001
From: Kizarm <mrazik@volny.cz>
Date: Wed, 16 Oct 2024 19:34:04 +0200
Subject: [PATCH] usart rx timeout management

---
 V203/usb/ch32v203/system.cpp | 24 ++++++++++++++++++++++--
 V203/usb/ch32v203/system.h   |  4 +++-
 V203/usb/usart/main.cpp      | 26 +++++++++++++++++++-------
 3 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/V203/usb/ch32v203/system.cpp b/V203/usb/ch32v203/system.cpp
index 0806171..d94ffbe 100644
--- a/V203/usb/ch32v203/system.cpp
+++ b/V203/usb/ch32v203/system.cpp
@@ -126,6 +126,7 @@ void SystemCoreClockUpdate (void) {
 }
 
 static uint32_t p_us = 0u;
+static bool     timeout;
 void delay_init () {
     p_us = SystemCoreClock / 8000000;
 }
@@ -143,5 +144,24 @@ void delay_us (const unsigned dly) {
     while((SysTick.SR & (1u << 0)) != (1u << 0));
     SysTick.CTLR.B.STE = RESET;
 }
-
-
+void set_timeout_us (const uint32_t time) {
+    SysTick.CTLR.B.STE = RESET;
+    timeout = false;
+    const uint32_t i = (uint32_t) time * p_us;
+    SysTick.SR &= ~(1 << 0);
+    SysTick.CMPLR = i;
+    SysTick.CTLR.modify([](SysTick_Type::CTLR_DEF & r) -> uint32_t {
+      r.B.MODE = SET;
+      r.B.INIT = SET;
+      return r.R;
+    });
+    SysTick.CTLR.B.STE = SET;
+}
+bool is_timeout () {
+    if (SysTick.SR & (1u << 0)) {
+      SysTick.CTLR.B.STE = RESET;
+      timeout = true;
+    } else {
+    }
+    return timeout;
+}
diff --git a/V203/usb/ch32v203/system.h b/V203/usb/ch32v203/system.h
index fe6c6ce..9a57c76 100644
--- a/V203/usb/ch32v203/system.h
+++ b/V203/usb/ch32v203/system.h
@@ -85,7 +85,9 @@ extern "C" {
   extern void SystemCoreClockUpdate (void);
   extern void SystemInit(void);
   extern void delay_init ();
-  extern void delay_us   (const unsigned dly);
+  extern void delay_us       (const unsigned dly);
+  extern void set_timeout_us (const uint32_t time);
+  extern bool is_timeout ();
 };
 
 #endif // SYSTEM_H
diff --git a/V203/usb/usart/main.cpp b/V203/usb/usart/main.cpp
index 59d7cfb..f940f1d 100644
--- a/V203/usb/usart/main.cpp
+++ b/V203/usb/usart/main.cpp
@@ -1,6 +1,9 @@
 #include "cdc_class.h"
 #include "usart.h"
 #include "mirror.h"
+
+static constexpr unsigned Timeout = 5000; // us
+
 /** Převodník USB_CDC - USART
  * Datový formát je pevný - 8 bit, 1 stop bit, bez parity.
  * Změnu po USB by šlo dost jednoduše dodělat, ale nepovažoval jsem to za nutné.
@@ -37,16 +40,24 @@ public:
   /* Vlastní odeslání paketu na USB_CDC
    * */
   void pass () {
-    /* Je to velmi zjednodušeno, odešle se 32 bytový paket, přímo.
-     * Pro test to stačí (testováno pro 1 MBd, full duplex), ale jinak by
-     * zde musela být kontrola přetečení, odeslání, časování timeout atd.
-     * Nechci to zaplevelit nečitelnými hovadinami jako ten číňan.
+    /* Je to dost zjednodušeno, odešle se 64 bytový paket,
+     * nebo po 5 ms to co zůstalo v bufferu.
      * */
-    if (index >= 32) { // 32 je vhodný kompromis mezi 1 a 64
-      BaseLayer::Up (buffer, index);
-      index = 0;
+    if ((is_timeout() and index) or (index >= 64)) {
+      block(buffer, index);
     }
   }
+  protected:
+    void block (const char * data, const uint32_t len) {
+      unsigned ofs = 0, rem = len;
+      while (rem) {
+        const unsigned n = BaseLayer::Up(data + ofs, rem);
+        rem -= n;
+        ofs += n;
+      }
+      index = 0;
+      set_timeout_us(Timeout);
+    }
 };
 static cdc_class cdc;
 static Usart     usart;
@@ -57,6 +68,7 @@ int main () {
   top += cdc;
   top -= mid += usart;
   cdc.attach(usart);
+  set_timeout_us(Timeout);
   for (;;) {
     mid.pass();
   }