Skip to content
Snippets Groups Projects
Commit 3fa39721 authored by cnlohr's avatar cnlohr
Browse files

Update ws2812b demo to be more robust.

parent 02c531f2
No related branches found
No related tags found
No related merge requests found
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
int main() __attribute__((used)); int main() __attribute__((used));
void SystemInit( void ) __attribute__((used)); void SystemInit( void ) __attribute__((used));
void _start() __attribute__((naked)) __attribute((section(".init"))) __attribute__((used)); void InterruptVector() __attribute__((naked)) __attribute((section(".init"))) __attribute__((used));
void handle_reset() __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used)); void handle_reset() __attribute__((naked)) __attribute((section(".text.handle_reset"))) __attribute__((used));
void DefaultIRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used)); void DefaultIRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute__((naked)) __attribute__((used));
...@@ -55,7 +55,7 @@ void TIM1_TRG_COM_IRQHandler( void ) __attribute__((section(".text.vector_ha ...@@ -55,7 +55,7 @@ void TIM1_TRG_COM_IRQHandler( void ) __attribute__((section(".text.vector_ha
void TIM1_CC_IRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used)); void TIM1_CC_IRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
void TIM2_IRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used)); void TIM2_IRQHandler( void ) __attribute__((section(".text.vector_handler"))) __attribute((weak,alias("DefaultIRQHandler"))) __attribute__((used));
void _start() void InterruptVector()
{ {
asm volatile( "\n\ asm volatile( "\n\
.align 2\n\ .align 2\n\
...@@ -111,29 +111,38 @@ void handle_reset() ...@@ -111,29 +111,38 @@ void handle_reset()
.option norelax\n\ .option norelax\n\
la gp, __global_pointer$\n\ la gp, __global_pointer$\n\
.option pop\n\ .option pop\n\
la sp, _eusrstack\n" ); la sp, _eusrstack\n"
// Load data section from flash to RAM // Setup the interrupt vector.
uint32_t * tempin = _data_lma;
uint32_t * tempout = _data_vma;
while( tempout != _edata )
*(tempout++) = *(tempin++);
tempout = _sbss;
while( tempout < _ebss )
*(tempout++) = 0;
asm volatile( "\n\ " li t0, 0x80\n\
li t0, 0x80\n\
csrw mstatus, t0\n\ csrw mstatus, t0\n\
li t0, 0x3\n\ li t0, 0x3\n\
csrw 0x804, t0\n\ csrw 0x804, t0\n\
la t0, _start\n\ la t0, InterruptVector\n\
ori t0, t0, 3\n\ ori t0, t0, 3\n\
csrw mtvec, t0" ); csrw mtvec, t0\n"
// Clear BSS
// Has to be in assembly otherwise it will overwrite the stack.
"la t0, _sbss\n\
la t1, _ebss\n\
bgeu t0, t1, 2f\n\
1:\n\
sw zero, (t0)\n\
addi t0, t0, 4\n\
bltu t0, t1, 1b\n\
2:" );
// Load data section from flash to RAM
uint32_t * tempin = _data_lma;
uint32_t * tempout = _data_vma;
while( tempout != _edata )
*(tempout++) = *(tempin++);
SystemInit(); SystemInit();
// set mepc to be main as the root interrupt.
asm volatile( "\n\ asm volatile( "\n\
la t0, main\n\ la t0, main\n\
csrw mepc, t0\n\ csrw mepc, t0\n\
......
# WS2812B SPI DMA example # WS2812B SPI DMA example
This example uses SPI-DMA to output WS2812B LEDs. By chunking the outputs using the center-interrupt, it is possible to double-buffer the WS2812B output data while only storing a few LEDs worth of data in memory at once. Single-file-header for SPI-DMA to output WS2812B LEDs. By chunking the outputs using the center-interrupt, it is possible to double-buffer the WS2812B output data while only storing a few LEDs worth of data in memory at once.
This outputs the LED data on the MOSI (PC6) pin of the CH32V003. This outputs the LED data on the MOSI (PC6) pin of the CH32V003.
...@@ -8,10 +8,34 @@ Additionally, this demo only uses 6% CPU while it's outputting LEDs and free whi ...@@ -8,10 +8,34 @@ Additionally, this demo only uses 6% CPU while it's outputting LEDs and free whi
The timing on the SPI bus is not terribly variable, the best I was able to find was: The timing on the SPI bus is not terribly variable, the best I was able to find was:
Ton0 = 324ns * Ton0 = 324ns
Ton1 = 990ns * Ton1 = 990ns
Toff0 = 990ns * Toff0 = 990ns
Toff1 = 324ns * Toff1 = 324ns
Treset = 68us * Treset = 68us
## Usage
If you are including this in main, simply
```c
#define WS2812DMA_IMPLEMENTATION
#include "ws2812b_dma_spi_led_driver.h"
```
You will need to implement the following two functions, as callbacks from the ISR.
```c
uint32_t CallbackWS2812BLED( int ledno );
```
You willalso need to call
```c
InitWS2812DMA();
```
Then, whenyou want to update the LEDs, call:
```c
WS2812BStart( int num_leds );
```
If you want to see if it's done sending, examine `WS2812BLEDInUse`.
...@@ -29,9 +29,6 @@ void WS2812BDMAStart( int leds ); ...@@ -29,9 +29,6 @@ void WS2812BDMAStart( int leds );
// Callbacks that you must implement. // Callbacks that you must implement.
uint32_t WS2812BLEDCallback( int ledno ); uint32_t WS2812BLEDCallback( int ledno );
extern volatile int WS2812BLEDDone;
#ifdef WS2812DMA_IMPLEMENTATION #ifdef WS2812DMA_IMPLEMENTATION
// Note first 2 LEDs of DMA Buffer are 0's as a "break" // Note first 2 LEDs of DMA Buffer are 0's as a "break"
...@@ -40,17 +37,16 @@ extern volatile int WS2812BLEDDone; ...@@ -40,17 +37,16 @@ extern volatile int WS2812BLEDDone;
// 1: Divisble by 2. // 1: Divisble by 2.
// 2: // 2:
#define DMALEDS 16 #define DMALEDS 16
#define WS2812B_RESET_PERIOD 6 #define WS2812B_RESET_PERIOD 2
#define DMA_BUFFER_LEN (((DMALEDS+1)/2)*6) #define DMA_BUFFER_LEN (((DMALEDS+1)/2)*6)
static uint16_t WS2812dmabuff[DMA_BUFFER_LEN]; static uint16_t WS2812dmabuff[DMA_BUFFER_LEN];
static int WS2812LEDs; static volatile int WS2812LEDs;
static int WS2812LEDPlace; static volatile int WS2812LEDPlace;
volatile int WS2812BLEDDone; static volatile int WS2812BLEDInUse;
// This is the code that updates a portion of the WS2812dmabuff with new data. // This is the code that updates a portion of the WS2812dmabuff with new data.
// This effectively creates the bitstream that outputs to the LEDs. // This effectively creates the bitstream that outputs to the LEDs.
static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending ) static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int tce )
{ {
const static uint16_t bitquartets[16] = { const static uint16_t bitquartets[16] = {
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110, 0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
...@@ -65,6 +61,10 @@ static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending ) ...@@ -65,6 +61,10 @@ static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending )
while( place < 0 && ptr != end ) while( place < 0 && ptr != end )
{ {
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
(*ptr++) = 0; (*ptr++) = 0;
place++; place++;
...@@ -78,22 +78,18 @@ static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending ) ...@@ -78,22 +78,18 @@ static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending )
while( ptr != end ) while( ptr != end )
(*ptr++) = 0;//0xffff; (*ptr++) = 0;//0xffff;
// If we're "totally finished" we can set the flag.
if( place == ledcount+1 )
WS2812BLEDDone = 1;
// Only safe to do this when we're on the second leg. // Only safe to do this when we're on the second leg.
if( ending ) if( tce )
{ {
if( place == ledcount ) if( place == ledcount )
{ {
// Take the DMA out of circular mode and let it expire. // Take the DMA out of circular mode and let it expire.
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular; DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
WS2812BLEDInUse = 0;
} }
place++; place++;
} }
break; break;
} }
...@@ -113,42 +109,48 @@ static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending ) ...@@ -113,42 +109,48 @@ static void WS2812FillBuffSec( uint16_t * ptr, int numhalfwords, int ending )
void DMA1_Channel3_IRQHandler( void ) __attribute__((interrupt)); void DMA1_Channel3_IRQHandler( void ) __attribute__((interrupt));
void DMA1_Channel3_IRQHandler( void ) void DMA1_Channel3_IRQHandler( void )
{ {
//GPIOD->BSHR = 1; // Turn on GPIOD0 //GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
// Backup flags. // Backup flags.
int intfr = DMA1->INTFR; volatile int intfr = DMA1->INTFR;
do
// Clear all possible flags.
DMA1->INTFCR = DMA1_IT_GL3;
if( intfr & DMA1_IT_HT3 )
{ {
// Complete (Fill in second part) // Clear all possible flags.
WS2812FillBuffSec( WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 1 ); DMA1->INTFCR = DMA1_IT_GL3;
}
if( intfr & DMA1_IT_TC3 )
{
// Halfwaay (Fill in first part)
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN / 2, 0 );
}
//GPIOD->BSHR = 1<<16; // Turn off GPIOD0 if( intfr & DMA1_IT_HT3 )
{
// Halfwaay (Fill in first part)
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN / 2, 1 );
}
if( intfr & DMA1_IT_TC3 )
{
// Complete (Fill in second part)
WS2812FillBuffSec( WS2812dmabuff + DMA_BUFFER_LEN / 2, DMA_BUFFER_LEN / 2, 0 );
}
intfr = DMA1->INTFR;
} while( intfr );
//GPIOD->BSHR = 1<<16; // Turn off GPIOD0 for profiling
} }
void WS2812BDMAStart( int leds ) void WS2812BDMAStart( int leds )
{ {
WS2812BLEDDone = 0; __disable_irq();
WS2812BLEDInUse = 1;
DMA1_Channel3->CFGR &= ~DMA_Mode_Circular;
DMA1_Channel3->CNTR = 0;
DMA1_Channel3->MADDR = (uint32_t)WS2812dmabuff;
WS2812LEDs = leds; WS2812LEDs = leds;
WS2812LEDPlace = -WS2812B_RESET_PERIOD; WS2812LEDPlace = -WS2812B_RESET_PERIOD;
WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN, 0 ); WS2812FillBuffSec( WS2812dmabuff, DMA_BUFFER_LEN, 0 );
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries. DMA1_Channel3->CNTR = DMA_BUFFER_LEN; // Number of unique uint16_t entries.
DMA1_Channel3->CFGR |= DMA_Mode_Circular;
__enable_irq();
} }
void WS2812BDMAInit( ) void WS2812BDMAInit( )
{ {
WS2812BLEDDone = 1;
// Enable DMA + Peripherals // Enable DMA + Peripherals
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1; RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1;
......
...@@ -65,22 +65,22 @@ void SystemInit(void) ...@@ -65,22 +65,22 @@ void SystemInit(void)
USART1->CTLR1 |= CTLR1_UE_Set; USART1->CTLR1 |= CTLR1_UE_Set;
} }
#define NR_LEDS 197 #define NR_LEDS 191
uint16_t phases[NR_LEDS]; uint16_t phases[NR_LEDS];
int frameno; int frameno;
int tween = 0; volatile int tween = 0;
int tweendir = 0;
// Callbacks that you must implement. // Callbacks that you must implement.
uint32_t WS2812BLEDCallback( int ledno ) uint32_t WS2812BLEDCallback( int ledno )
{ {
uint8_t index = (phases[ledno])>>8; uint8_t index = (phases[ledno])>>8;
uint8_t rs = sintable[index]>>3; uint8_t rs = sintable[index]>>3;
// uint32_t fire = huetable[(rs+190&0xff)] | (huetable[(rs+30&0xff)]<<8) | (huetable[(rs+0)]<<16); uint32_t fire = huetable[(rs+190&0xff)] | (huetable[(rs+30&0xff)]<<8) | (huetable[(rs+0)]<<16);
uint32_t ice = 0xff | ((rs)<<8) | (rs<<16); uint32_t ice = 0xff | ((rs)<<8) | (rs<<16);
return ice;
//return TweenHexColors( fire, ice, tween ); // Where "tween" is a value from 0 ... 255 // Because this chip doesn't natively support multiplies, this can be very slow.
return TweenHexColors( fire, ice, tween ); // Where "tween" is a value from 0 ... 255
} }
int main() int main()
...@@ -101,37 +101,41 @@ int main() ...@@ -101,37 +101,41 @@ int main()
for( k = 0; k < NR_LEDS; k++ ) phases[k] = k<<8; for( k = 0; k < NR_LEDS; k++ ) phases[k] = k<<8;
int tweendir = 0;
while(1) while(1)
{ {
// Wait for LEDs to totally finish. // Wait for LEDs to totally finish.
if( WS2812BLEDDone ) Delay_Ms( 12 );
while( WS2812BLEDInUse );
frameno++;
if( frameno == 1024 )
{
tweendir = 4;
}
if( frameno == 2048 )
{ {
frameno++; tweendir = -4;
frameno = 0;
if( frameno == 1024 )
{
tweendir = 4;
}
if( frameno == 2048 )
{
tweendir = -4;
frameno = 0;
}
if( tweendir )
{
tween += tweendir;
if( tween > 255 ) tween = 255;
if( tween < 0 ) tween = 0;
}
for( k = 0; k < NR_LEDS; k++ )
{
phases[k] += ((((rands[k&0xff])+0xf)<<2) + (((rands[k&0xff])+0xf)<<1))>>1;
}
WS2812BDMAStart( NR_LEDS );
} }
if( tweendir )
{
int t = tween + tweendir;
if( t > 255 ) t = 255;
if( t < 0 ) t = 0;
tween = t;
}
for( k = 0; k < NR_LEDS; k++ )
{
phases[k] += ((((rands[k&0xff])+0xf)<<2) + (((rands[k&0xff])+0xf)<<1))>>1;
}
WS2812BDMAStart( NR_LEDS );
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment