From 9d71a62a71b6bf1e790adb04fb087b1482b366b3 Mon Sep 17 00:00:00 2001
From: cnlohr <lohr85@gmail.com>
Date: Wed, 2 Aug 2023 13:58:39 -0400
Subject: [PATCH]  * Add code to modify INTSYSCR to ch32v003fun  * Allow
 interrupt preemption in WS2812B driver.  * Add simple driver for ws2812b's.

---
 ch32v003fun/ch32v003fun.h              | 12 ++++
 extralibs/ws2812b_dma_spi_led_driver.h | 12 +++-
 extralibs/ws2812b_simple.h             | 88 ++++++++++++++++++++++++++
 3 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 extralibs/ws2812b_simple.h

diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h
index 3e07c24..bea67b2 100644
--- a/ch32v003fun/ch32v003fun.h
+++ b/ch32v003fun/ch32v003fun.h
@@ -4789,6 +4789,18 @@ RV_STATIC_INLINE void NVIC_SystemReset(void)
   NVIC->CFGR = NVIC_KEY3|(1<<7);
 }
 
+// For configuring INTSYSCR, for interrupt nesting + hardware stack enable.
+static inline uint32_t __get_INTSYSCR(void)
+{
+    uint32_t result;
+    asm volatile("csrr %0, 0x804": "=r"(result));
+    return (result);
+}
+
+static inline void __set_INTSYSCR( uint32_t value )
+{
+    asm volatile("csrw 0x804, %0" : : "r"(value));
+}
 
 
 /*********************************************************************
diff --git a/extralibs/ws2812b_dma_spi_led_driver.h b/extralibs/ws2812b_dma_spi_led_driver.h
index 433fff7..115fea5 100644
--- a/extralibs/ws2812b_dma_spi_led_driver.h
+++ b/extralibs/ws2812b_dma_spi_led_driver.h
@@ -3,13 +3,18 @@
    to generate outputs very efficiently. So, for now, SPI Port.  Additionally, it uses FAR less
    internal bus resources than to do the same thing with timers.
    
-   For the CH32V003 this means output will be on PORTC Pin 6
+   **For the CH32V003 this means output will be on PORTC Pin 6**
 
    Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
 
    If you are including this in main, simply 
 	#define WS2812DMA_IMPLEMENTATION
 
+   Other defines inclue:
+	#define WSRBG
+	#define WSGRB
+	#define ALLOW_INTERRUPT_NESTING
+
    You will need to implement the following two functions, as callbacks from the ISR.
 	uint32_t WS2812BLEDCallback( int ledno );
 
@@ -219,6 +224,11 @@ void WS2812BDMAInit( )
 //	NVIC_SetPriority( DMA1_Channel3_IRQn, 0<<4 ); //We don't need to tweak priority.
 	NVIC_EnableIRQ( DMA1_Channel3_IRQn );
 	DMA1_Channel3->CFGR |= DMA_CFGR1_EN;
+
+#ifdef ALLOW_INTERRUPT_NESTING
+	__set_INTSYSCR( 2 ); // Enable interrupt nesting.
+	PFIC->IPRIOR[24] = 0b10000000; // Turn on preemption for DMA1Ch3
+#endif
 }
 
 #endif
diff --git a/extralibs/ws2812b_simple.h b/extralibs/ws2812b_simple.h
new file mode 100644
index 0000000..8c569fe
--- /dev/null
+++ b/extralibs/ws2812b_simple.h
@@ -0,0 +1,88 @@
+/* Single-File-Header for using asynchronous LEDs with the CH32V003 using DMA to the SPI port.
+   I may write another version of this to use DMA to timer ports, but, the SPI port can be used
+   to generate outputs very efficiently. So, for now, SPI Port.  Additionally, it uses FAR less
+   internal bus resources than to do the same thing with timers.
+   
+   For the CH32V003 this means output will be on PORTC Pin 6
+
+   Copyright 2023 <>< Charles Lohr, under the MIT-x11 or NewBSD License, you choose!
+
+   If you are including this in main, simply 
+	#define WS2812BSIMPLE_IMPLEMENTATION
+
+   You may also want to define 
+	#define WS2812BSIMPLE_NO_IRQ_TWEAKING
+
+*/
+
+#ifndef _WS2812B_SIMPLE
+#define _WS2812B_SIMPLE
+
+#include <stdint.h>
+
+void WS2812BSimpleSend( GPIO_TypeDef * port, int pin, uint8_t * data, int len_in_bytes );
+
+#ifdef WS2812BSIMPLE_IMPLEMENTATION
+
+#include "funconfig.h"
+
+#if FUNCONF_SYSTICK_USE_HCLK != 1
+#error WS2812B Driver Requires FUNCONF_SYSTICK_USE_HCLK
+#endif
+
+void WS2812BSimpleSend( GPIO_TypeDef * port, int pin, uint8_t * data, int len_in_bytes )
+{
+	int port_id = (((intptr_t)port-(intptr_t)GPIOA)>>10);
+	RCC->APB2PCENR |= (RCC_APB2Periph_GPIOA<<port_id);  // Make sure port is enabled.
+
+	int poffset = (pin*4);
+	port->CFGLR = ( port->CFGLR & (~(0xf<<poffset))) | ((GPIO_Speed_2MHz | GPIO_CNF_OUT_PP)<<(poffset));
+
+	int maskon = 1<<pin;
+	int maskoff = 1<<(16+pin);
+
+	port->BSHR = maskoff;
+
+	uint8_t * end = data + len_in_bytes;
+	while( data != end )
+	{
+		uint8_t byte = *data;
+
+		int i;
+		for( i = 0; i < 8; i++ )
+		{
+			if( byte & 0x80 )
+			{
+				// WS2812B's need AT LEAST 625ns for a logical "1"
+				port->BSHR = maskon;
+				DelaySysTick(25);
+				port->BSHR = maskoff;
+				DelaySysTick(1);
+			}
+			else
+			{
+				// WS2812B's need BETWEEN 62.5 to about 500 ns for a logical "0"
+#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
+				__disable_irq();
+#endif
+				port->BSHR = maskon;
+				asm volatile( "nop\nnop\nnop\nnop" );
+				port->BSHR = maskoff;
+#ifndef WS2812BSIMPLE_NO_IRQ_TWEAKING
+				__enable_irq();
+#endif
+				DelaySysTick(15);
+			}
+			byte <<= 1;
+		}
+
+		data++;
+	}
+
+	port->BSHR = maskoff;
+}
+
+#endif
+
+#endif
+
-- 
GitLab