78-WS2812-Library (STM32F4)

78-WS2812-Library (STM32F4)

//--------------------------------------------------------------
// File     : stm32_ub_ws2812.h
//--------------------------------------------------------------

//--------------------------------------------------------------
#ifndef __STM32F4_UB_WS2812_H
#define __STM32F4_UB_WS2812_H

//--------------------------------------------------------------
// Includes
//--------------------------------------------------------------
#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_tim.h"
#include "stm32f4xx_dma.h"
#include "misc.h"

//--------------------------------------------------------------
// Anzahl der WS2812-LEDs (1...n) in den einzelnen Ketten 
// CPU -> LED_1 -> LED_2 -> LED_3 -> LED_n
//
// falls ein Channel nicht benutzt wird => laenge auf 0 setzen
//--------------------------------------------------------------
#define  WS2812_LED_CH1_ANZ    5  // [CH1 an PC6] LED-Kette mit 5 LEDs
#define  WS2812_LED_CH2_ANZ    0  // [CH2 an PB5] CH2 wird nicht benutzt
#define  WS2812_LED_CH3_ANZ    0  // [CH3 an PB0] CH3 wird nicht benutzt
#define  WS2812_LED_CH4_ANZ    0  // [CH4 an PB1] CH4 wird nicht benutzt

#define  WS2812_LED_MAX_ANZ    5  // anzahl der LEDs in der laengsten Kette

//--------------------------------------------------------------
// check der laengenangaben
//--------------------------------------------------------------
#if WS2812_LED_CH1_ANZ>WS2812_LED_MAX_ANZ
#error wrong len
#endif
#if WS2812_LED_CH2_ANZ>WS2812_LED_MAX_ANZ
#error wrong len
#endif
#if WS2812_LED_CH3_ANZ>WS2812_LED_MAX_ANZ
#error wrong len
#endif
#if WS2812_LED_CH4_ANZ>WS2812_LED_MAX_ANZ
#error wrong len
#endif

#if (WS2812_LED_MAX_ANZ>WS2812_LED_CH1_ANZ) && (WS2812_LED_MAX_ANZ>WS2812_LED_CH2_ANZ)
#if (WS2812_LED_MAX_ANZ>WS2812_LED_CH3_ANZ) && (WS2812_LED_MAX_ANZ>WS2812_LED_CH4_ANZ)
#error wrong len
#endif
#endif

//--------------------------------------------------------------
// benutzer Timer fuer das Daten-Signal => TIM3
//--------------------------------------------------------------
#define  WS2812_TIM_CLOCK     RCC_APB1Periph_TIM3
#define  WS2812_TIM           TIM3
#define  WS2812_TIM_AF        GPIO_AF_TIM3
#define  WS2812_TIM_CH1       1 
#define  WS2812_TIM_CCR_REG1  TIM3->CCR1
#define  WS2812_TIM_DMA_TRG1  TIM_DMA_CC1
#define  WS2812_TIM_CH2       2 
#define  WS2812_TIM_CCR_REG2  TIM3->CCR2
#define  WS2812_TIM_DMA_TRG2  TIM_DMA_CC2
#define  WS2812_TIM_CH3       3 
#define  WS2812_TIM_CCR_REG3  TIM3->CCR3
#define  WS2812_TIM_DMA_TRG3  TIM_DMA_CC3
#define  WS2812_TIM_CH4       4 
#define  WS2812_TIM_CCR_REG4  TIM3->CCR4
#define  WS2812_TIM_DMA_TRG4  TIM_DMA_CC4

//--------------------------------------------------------------
// GPIO-Pins (CH1...CH4) fuer Data-OUT 
//
// moegliche Pinbelegungen (bei TIM3)
//   CH1 : [PA6, PB4, PC6]
//   CH2 : [PA7, PB5, PC7]
//   CH3 : [PB0, PC8]
//   CH4 : [PB1, PC9] 
//--------------------------------------------------------------
#define  WS2812_CH1_CLOCK    RCC_AHB1Periph_GPIOC
#define  WS2812_CH1_PORT     GPIOC
#define  WS2812_CH1_PIN      GPIO_Pin_6 
#define  WS2812_CH1_SOURCE   GPIO_PinSource6 

#define  WS2812_CH2_CLOCK    RCC_AHB1Periph_GPIOB
#define  WS2812_CH2_PORT     GPIOB
#define  WS2812_CH2_PIN      GPIO_Pin_5 
#define  WS2812_CH2_SOURCE   GPIO_PinSource5

#define  WS2812_CH3_CLOCK    RCC_AHB1Periph_GPIOB
#define  WS2812_CH3_PORT     GPIOB
#define  WS2812_CH3_PIN      GPIO_Pin_0 
#define  WS2812_CH3_SOURCE   GPIO_PinSource0

#define  WS2812_CH4_CLOCK    RCC_AHB1Periph_GPIOB
#define  WS2812_CH4_PORT     GPIOB
#define  WS2812_CH4_PIN      GPIO_Pin_1 
#define  WS2812_CH4_SOURCE   GPIO_PinSource1

//--------------------------------------------------------------
// benutzer DMA
//   => TIM3-CC1 => DMA1, Channel5, Stream4
//   => TIM3-CC2 => DMA1, Channel5, Stream5
//   => TIM3-CC3 => DMA1, Channel5, Stream7
//   => TIM3-CC4 => DMA1, Channel5, Stream2
// (siehe Seite 216+217 vom Referenz Manual)
//--------------------------------------------------------------
#define  WS2812_DMA_CLOCK         RCC_AHB1Periph_DMA1

#define  WS2812_DMA_CH1_STREAM    DMA1_Stream4
#define  WS2812_DMA_CH1_CHANNEL   DMA_Channel_5

#define  WS2812_DMA_CH2_STREAM    DMA1_Stream5
#define  WS2812_DMA_CH2_CHANNEL   DMA_Channel_5

#define  WS2812_DMA_CH3_STREAM    DMA1_Stream7
#define  WS2812_DMA_CH3_CHANNEL   DMA_Channel_5

#define  WS2812_DMA_CH4_STREAM    DMA1_Stream2
#define  WS2812_DMA_CH4_CHANNEL   DMA_Channel_5

//--------------------------------------------------------------
// Transfer-Complete Interrupt
//   CC1 => DMA1, Stream4
//   CC2 => DMA1, Stream5
//   CC3 => DMA1, Stream7
//   CC4 => DMA1, Stream2
//--------------------------------------------------------------
#define  WS2812_DMA_CH1_IRQn      DMA1_Stream4_IRQn
#define  WS2812_DMA_CH1_ISR       DMA1_Stream4_IRQHandler
#define  WS2812_DMA_CH1_IRQ_FLAG  DMA_IT_TCIF4

#define  WS2812_DMA_CH2_IRQn      DMA1_Stream5_IRQn
#define  WS2812_DMA_CH2_ISR       DMA1_Stream5_IRQHandler
#define  WS2812_DMA_CH2_IRQ_FLAG  DMA_IT_TCIF5

#define  WS2812_DMA_CH3_IRQn      DMA1_Stream7_IRQn
#define  WS2812_DMA_CH3_ISR       DMA1_Stream7_IRQHandler
#define  WS2812_DMA_CH3_IRQ_FLAG  DMA_IT_TCIF7

#define  WS2812_DMA_CH4_IRQn      DMA1_Stream2_IRQn
#define  WS2812_DMA_CH4_ISR       DMA1_Stream2_IRQHandler
#define  WS2812_DMA_CH4_IRQ_FLAG  DMA_IT_TCIF2

//--------------------------------------------------------------
// RGB LED Farbdefinition (3 x 8bit)
//--------------------------------------------------------------
typedef struct
{
  uint8_t red;    // 0...255 (als PWM-Wert)
  uint8_t green;  // 0...255 (als PWM-Wert)
  uint8_t blue;   // 0...255 (als PWM-Wert)
} WS2812_RGB_t;

//--------------------------------------------------------------
// HSV LED Farbdefinition
//--------------------------------------------------------------
typedef struct
{
  uint16_t h;     // 0...359 (in Grad, 0=R, 120=G, 240=B)
  uint8_t s;      // 0...100 (in Prozent)
  uint8_t v;      // 0...100 (in Prozent)
} WS2812_HSV_t;

//--------------------------------------------------------------
// Globale Buffer fuer die Farben (als RGB-Wert)
//--------------------------------------------------------------
#if WS2812_LED_CH1_ANZ>0
WS2812_RGB_t WS2812_LED_BUF_CH1[ WS2812_LED_CH1_ANZ ];
#endif

#if WS2812_LED_CH2_ANZ>0
WS2812_RGB_t WS2812_LED_BUF_CH2[WS2812_LED_CH2_ANZ];
#endif

#if WS2812_LED_CH3_ANZ>0
WS2812_RGB_t WS2812_LED_BUF_CH3[WS2812_LED_CH3_ANZ];
#endif

#if WS2812_LED_CH4_ANZ>0
WS2812_RGB_t WS2812_LED_BUF_CH4[WS2812_LED_CH4_ANZ];
#endif

//--------------------------------------------------------------
// standard Farben (R,G,B)
//--------------------------------------------------------------
#define  WS2812_RGB_COL_OFF      (WS2812_RGB_t) {0x00,0x00,0x00}

#define  WS2812_RGB_COL_BLUE     (WS2812_RGB_t) {0x00,0x00,0xFF}
#define  WS2812_RGB_COL_GREEN    (WS2812_RGB_t) {0x00,0xFF,0x00}
#define  WS2812_RGB_COL_RED      (WS2812_RGB_t) {0xFF,0x00,0x00}
#define  WS2812_RGB_COL_WHITE    (WS2812_RGB_t) {0xFF,0xFF,0xFF}

#define  WS2812_RGB_COL_CYAN     (WS2812_RGB_t) {0x00,0xFF,0xFF}
#define  WS2812_RGB_COL_MAGENTA  (WS2812_RGB_t) {0xFF,0x00,0xFF}
#define  WS2812_RGB_COL_YELLOW   (WS2812_RGB_t) {0xFF,0xFF,0x00}

//--------------------------------------------------------------
// standard Farben (H,S,V)
//--------------------------------------------------------------
#define  WS2812_HSV_COL_OFF      (WS2812_HSV_t) {0,  0,  0}

#define  WS2812_HSV_COL_BLUE     (WS2812_HSV_t) {240,100,100}
#define  WS2812_HSV_COL_GREEN    (WS2812_HSV_t) {120,100,100}
#define  WS2812_HSV_COL_RED      (WS2812_HSV_t) {0,  100,100}

#define  WS2812_HSV_COL_CYAN     (WS2812_HSV_t) {180,100,100}
#define  WS2812_HSV_COL_MAGENTA  (WS2812_HSV_t) {300,100,100}
#define  WS2812_HSV_COL_YELLOW   (WS2812_HSV_t) {60, 100,100}

//--------------------------------------------------------------
// WS2812 Timing : (1.25us = 800 kHz)
//   logische-0 => HI:0.35us , LO:0.90us
//   logische-1 =  HI:0.90us , LO:0.35us
//
// WS23812 Bit-Format : (8G8R8B)
//   24bit pro LED  (30us pro LED)
//    8bit pro Farbe (MSB first)
//    Farbreihenfolge : GRB
//      
//   nach jedem Frame von n-LEDs kommt eine Pause von >= 50us
//
// Grundfrequenz (TIM3) = 2*APB1 (APB1=42MHz) => TIM_CLK=84MHz
// periode   : 0 bis 0xFFFF
// prescale  : 0 bis 0xFFFF
//
// PWM-Frq = TIM_CLK/(periode+1)/(vorteiler+1)
//-------------------------------------------------------------- 
#define  WS2812_TIM_PRESCALE    0  // F_T3  = 84 MHz (11.9ns)
#define  WS2812_TIM_PERIODE   104  // F_PWM = 80 kHz (1.25us)

#define  WS2812_LO_TIME        29  // 29 * 11.9ns = 0.34us
#define  WS2812_HI_TIME        76  // 76 * 11.9ns = 0.90us

//--------------------------------------------------------------
// defines vom WS2812 (nicht abaendern)
//--------------------------------------------------------------
#define  WS2812_BIT_PER_LED    24  // 3*8bit pro LED
#define  WS2812_PAUSE_ANZ       2  // fuer Pause (2*30us)

#define  WS2812_TIMER_BUF_LEN1   (WS2812_LED_CH1_ANZ+WS2812_PAUSE_ANZ)*WS2812_BIT_PER_LED
#define  WS2812_TIMER_BUF_LEN2   (WS2812_LED_CH2_ANZ+WS2812_PAUSE_ANZ)*WS2812_BIT_PER_LED
#define  WS2812_TIMER_BUF_LEN3   (WS2812_LED_CH3_ANZ+WS2812_PAUSE_ANZ)*WS2812_BIT_PER_LED
#define  WS2812_TIMER_BUF_LEN4   (WS2812_LED_CH4_ANZ+WS2812_PAUSE_ANZ)*WS2812_BIT_PER_LED
#define  WS2812_TIMER_BUF_LEN    (WS2812_LED_MAX_ANZ+WS2812_PAUSE_ANZ)*WS2812_BIT_PER_LED

//--------------------------------------------------------------
// Globale Funktionen
//--------------------------------------------------------------
void UB_WS2812_Init( void );
void UB_WS2812_SetChannel( uint8_t ch );
void UB_WS2812_Refresh( void );
void UB_WS2812_RGB_2_HSV( WS2812_HSV_t hsv_col, WS2812_RGB_t *rgb_col );
void UB_WS2812_One_Led_RGB( uint32_t nr, WS2812_RGB_t rgb_col, uint8_t refresh );
void UB_WS2812_All_Led_RGB( WS2812_RGB_t rgb_col, uint8_t refresh );
void UB_WS2812_One_Led_HSV( uint32_t nr, WS2812_HSV_t hsv_col, uint8_t refresh );
void UB_WS2812_All_Led_HSV( WS2812_HSV_t hsv_col, uint8_t refresh );
void UB_WS2812_Shift_Left( void );
void UB_WS2812_Shift_Right( void );
void UB_WS2812_Rotate_Left( void );
void UB_WS2812_Rotate_Right( void );

//--------------------------------------------------------------
#endif // __STM32F4_UB_WS2812_H
//--------------------------------------------------------------
// File     : stm32_ub_ws2812.c
// Datum    : 25.04.2014
// Version  : 1.2
// Autor    : UB
// EMail    : mc-4u(@)t-online.de
// Web      : www.mikrocontroller-4u.de
// CPU      : STM32F4
// IDE      : CooCox CoIDE 1.7.4
// GCC      : 4.7 2012q4
// Module   : GPIO, TIM, DMA, MISC
// Funktion : RGB-LED mit WS2812-Chip (LED Vcc = 3,3V !!)
//            (einzelne LED oder bis zu 4 Ketten von n-LEDs)
//
// Hinweis  : es koennen bis zu 4 LED-Ketten betrieben werden
//            die Anzahl der LEDs pro Kette und der benutzte
//            GPIO-Pin muss im H-File eingestellt werden
//
// CH1 = PC6 (LED-Kette mit 5 LEDs)
// CH2 = PB5 (nicht aktiv)
// CH3 = PB0 (nicht aktiv)
// CH4 = PB1 (nicht aktiv)
//--------------------------------------------------------------

//--------------------------------------------------------------
// Includes
//--------------------------------------------------------------
#include "stm32_ub_ws2812.h"

//--------------------------------------------------------------
// Globale interne Variabeln
//--------------------------------------------------------------
uint32_t ws2812_dma_status;
uint16_t WS2812_TIMER_BUF[ WS2812_TIMER_BUF_LEN ];
uint8_t ws2812_channel;
WS2812_RGB_t *ws2812_ptr;
uint32_t ws2812_maxanz;

//--------------------------------------------------------------
// interne Funktionen
//--------------------------------------------------------------
void p_WS2812_clearAll( void );
void p_WS2812_calcTimerBuf( void );
void p_WS2812_InitIO( void );
void p_WS2812_InitTIM( void );
void p_WS2812_InitDMA( void );
void p_WS2812_InitNVIC( void );
void p_WS2812_DMA_Start( void );

//--------------------------------------------------------------
// init aller WS2812-Ketten
// alle LEDs ausschalten
// (aktiv ist die Kette mit der niedrigsten Channel-Nr)
//--------------------------------------------------------------
void UB_WS2812_Init( void )
{
  uint32_t n;
  
  // init aller Variabeln
  ws2812_dma_status = 0;
  ws2812_channel = 0;
  ws2812_maxanz = 0;
  
  // init vom Timer Array
  for ( n = 0; n < WS2812_TIMER_BUF_LEN; n++ )
  {
    WS2812_TIMER_BUF[ n ] = 0; // 0 => fuer Pausenzeit
  }
  
  // init der LED Arrays aller Ketten
#if WS2812_LED_CH4_ANZ>0
  for(n=0;n<WS2812_LED_CH4_ANZ;n++)
  { 
    WS2812_LED_BUF_CH4[n]=WS2812_RGB_COL_OFF;
  }
  ws2812_channel=4;
#endif
#if WS2812_LED_CH3_ANZ>0
  for(n=0;n<WS2812_LED_CH3_ANZ;n++)
  { 
    WS2812_LED_BUF_CH3[n]=WS2812_RGB_COL_OFF;
  }
  ws2812_channel=3;
#endif
#if WS2812_LED_CH2_ANZ>0
  for(n=0;n<WS2812_LED_CH2_ANZ;n++)
  { 
    WS2812_LED_BUF_CH2[n]=WS2812_RGB_COL_OFF;
  }
  ws2812_channel=2;
#endif
#if WS2812_LED_CH1_ANZ>0
  for(n=0;n<WS2812_LED_CH1_ANZ;n++)
  { 
    WS2812_LED_BUF_CH1[n]=WS2812_RGB_COL_OFF;
  }
  ws2812_channel=1;
#endif
  
  if ( ws2812_channel == 0 )
    return;
  
  // aktive WS2812-Kette auswaehlen
  UB_WS2812_SetChannel( ws2812_channel );
  
  // init vom GPIO
  p_WS2812_InitIO( );
  // init vom Timer
  p_WS2812_InitTIM( );
  // init vom NVIC
  p_WS2812_InitNVIC( );
  // init vom DMA
  p_WS2812_InitDMA( );
  
  // alle WS2812-Ketten loeschen
  p_WS2812_clearAll( );
}

//--------------------------------------------------------------
// auswahl welche WS2812-Kette aktiv sein soll
// (LED Anzahl der Kette muss >0 sein)
// ch = [1...4]
//--------------------------------------------------------------
void UB_WS2812_SetChannel( uint8_t ch )
{
#if WS2812_LED_CH1_ANZ>0
  if(ch==1)
  { 
    ws2812_channel=1;
    ws2812_ptr=&WS2812_LED_BUF_CH1[0];
    ws2812_maxanz=WS2812_LED_CH1_ANZ;
  }
#endif
#if WS2812_LED_CH2_ANZ>0
  if(ch==2)
  { 
    ws2812_channel=2;
    ws2812_ptr=&WS2812_LED_BUF_CH2[0];
    ws2812_maxanz=WS2812_LED_CH2_ANZ;
  }
#endif
#if WS2812_LED_CH3_ANZ>0
  if(ch==3)
  { 
    ws2812_channel=3;
    ws2812_ptr=&WS2812_LED_BUF_CH3[0];
    ws2812_maxanz=WS2812_LED_CH3_ANZ;
  }
#endif
#if WS2812_LED_CH4_ANZ>0
  if(ch==4)
  { 
    ws2812_channel=4;
    ws2812_ptr=&WS2812_LED_BUF_CH4[0];
    ws2812_maxanz=WS2812_LED_CH4_ANZ;
  }
#endif
}

//--------------------------------------------------------------
// Refresh der aktiven WS2812-Kette
// (update aller LEDs)
// Die RGB-Farbwerte der LEDs muss im Array "WS2812_LED_BUF[n]" stehen
// n = [0...WS2812_LED_ANZ-1]
//--------------------------------------------------------------
void UB_WS2812_Refresh( void )
{
  if ( ws2812_channel == 0 )
    return;
  
  // warte bis DMA-Transfer fertig
  while ( ws2812_dma_status != 0 )
    ;
  
  // Timer Werte berrechnen
  p_WS2812_calcTimerBuf( );
  
  // DMA starten
  p_WS2812_DMA_Start( );
}

//--------------------------------------------------------------
// wandelt einen HSV-Farbwert in einen RGB-Farbwert um
// (Funktion von UlrichRadig.de)
//--------------------------------------------------------------
void UB_WS2812_RGB_2_HSV( WS2812_HSV_t hsv_col, WS2812_RGB_t *rgb_col )
{
  uint8_t diff;
  
  // Grenzwerte
  if ( hsv_col.h > 359 )
    hsv_col.h = 359;
  if ( hsv_col.s > 100 )
    hsv_col.s = 100;
  if ( hsv_col.v > 100 )
    hsv_col.v = 100;
  
  if ( hsv_col.h < 61 )
  {
    rgb_col->red = 255;
    rgb_col->green = ( 425 * hsv_col.h ) / 100;
    rgb_col->blue = 0;
  }
  else if ( hsv_col.h < 121 )
  {
    rgb_col->red = 255 - ( ( 425 * ( hsv_col.h - 60 ) ) / 100 );
    rgb_col->green = 255;
    rgb_col->blue = 0;
  }
  else if ( hsv_col.h < 181 )
  {
    rgb_col->red = 0;
    rgb_col->green = 255;
    rgb_col->blue = ( 425 * ( hsv_col.h - 120 ) ) / 100;
  }
  else if ( hsv_col.h < 241 )
  {
    rgb_col->red = 0;
    rgb_col->green = 255 - ( ( 425 * ( hsv_col.h - 180 ) ) / 100 );
    rgb_col->blue = 255;
  }
  else if ( hsv_col.h < 301 )
  {
    rgb_col->red = ( 425 * ( hsv_col.h - 240 ) ) / 100;
    rgb_col->green = 0;
    rgb_col->blue = 255;
  }
  else
  {
    rgb_col->red = 255;
    rgb_col->green = 0;
    rgb_col->blue = 255 - ( ( 425 * ( hsv_col.h - 300 ) ) / 100 );
  }
  
  hsv_col.s = 100 - hsv_col.s;
  diff = ( ( 255 - rgb_col->red ) * hsv_col.s ) / 100;
  rgb_col->red = rgb_col->red + diff;
  diff = ( ( 255 - rgb_col->green ) * hsv_col.s ) / 100;
  rgb_col->green = rgb_col->green + diff;
  diff = ( ( 255 - rgb_col->blue ) * hsv_col.s ) / 100;
  rgb_col->blue = rgb_col->blue + diff;
  
  rgb_col->red = ( rgb_col->red * hsv_col.v ) / 100;
  rgb_col->green = ( rgb_col->green * hsv_col.v ) / 100;
  rgb_col->blue = ( rgb_col->blue * hsv_col.v ) / 100;
}

//--------------------------------------------------------------
// eine LED der aktiven WS2812-Kette auf eine Farbe setzen (RGB)
// nr      : [0...WS2812_LED_ANZ-1]
// rgb_col : Farbwert (in RGB)
// refresh : 0=ohne refresh, 1=mit refresh
//--------------------------------------------------------------
void UB_WS2812_One_Led_RGB( uint32_t nr, WS2812_RGB_t rgb_col, uint8_t refresh )
{
  if ( ws2812_channel == 0 )
    return;
  
  if ( nr < ws2812_maxanz )
  {
    ws2812_ptr[ nr ] = rgb_col;
    
    if ( refresh == 1 )
      UB_WS2812_Refresh( );
  }
}

//--------------------------------------------------------------
// alle LEDs der aktiven WS2812-Kette auf eine Farbe setzen (RGB)
// rgb_col : Farbwert (in RGB)
// refresh : 0=ohne refresh, 1=mit refresh
//--------------------------------------------------------------
void UB_WS2812_All_Led_RGB( WS2812_RGB_t rgb_col, uint8_t refresh )
{
  uint32_t n;
  
  if ( ws2812_channel == 0 )
    return;
  
  for ( n = 0; n < ws2812_maxanz; n++ )
  {
    ws2812_ptr[ n ] = rgb_col;
  }
  if ( refresh == 1 )
    UB_WS2812_Refresh( );
}

//--------------------------------------------------------------
// eine LED der aktiven WS2812-Kette auf eine Farbe setzen (HSV)
// nr      : [0...WS2812_LED_ANZ-1]
// hsv_col : Farbwert (in HSV)
// refresh : 0=ohne refresh, 1=mit refresh
//--------------------------------------------------------------
void UB_WS2812_One_Led_HSV( uint32_t nr, WS2812_HSV_t hsv_col, uint8_t refresh )
{
  WS2812_RGB_t rgb_col;
  
  if ( ws2812_channel == 0 )
    return;
  
  if ( nr < ws2812_maxanz )
  {
    // farbe in RGB umwandeln
    UB_WS2812_RGB_2_HSV( hsv_col, &rgb_col );
    ws2812_ptr[ nr ] = rgb_col;
    
    if ( refresh == 1 )
      UB_WS2812_Refresh( );
  }
}

//--------------------------------------------------------------
// alle LEDs der aktiven WS2812-Kette auf eine Farbe setzen (HSV)
// hsv_col : Farbwert (in HSV)
// refresh : 0=ohne refresh, 1=mit refresh
//--------------------------------------------------------------
void UB_WS2812_All_Led_HSV( WS2812_HSV_t hsv_col, uint8_t refresh )
{
  uint32_t n;
  WS2812_RGB_t rgb_col;
  
  if ( ws2812_channel == 0 )
    return;
  
  // farbe in RGB umwandeln
  UB_WS2812_RGB_2_HSV( hsv_col, &rgb_col );
  for ( n = 0; n < ws2812_maxanz; n++ )
  {
    ws2812_ptr[ n ] = rgb_col;
  }
  if ( refresh == 1 )
    UB_WS2812_Refresh( );
}

//--------------------------------------------------------------
// alle LEDs der aktiven WS2812-Kette eine position nach links schieben
// letzte LED wird ausgeschaltet
//--------------------------------------------------------------
void UB_WS2812_Shift_Left( void )
{
  uint32_t n;
  
  if ( ws2812_channel == 0 )
    return;
  
  if ( ws2812_maxanz > 1 )
  {
    for ( n = 1; n < ws2812_maxanz; n++ )
    {
      ws2812_ptr[ n - 1 ] = ws2812_ptr[ n ];
    }
    ws2812_ptr[ n - 1 ] = WS2812_RGB_COL_OFF;
    
    UB_WS2812_Refresh( );
  }
}

//--------------------------------------------------------------
// alle LEDs der aktiven WS2812-Kette eine position nach rechts schieben
// erste LED wird ausgeschaltet
//--------------------------------------------------------------
void UB_WS2812_Shift_Right( void )
{
  uint32_t n;
  
  if ( ws2812_channel == 0 )
    return;
  
  if ( ws2812_maxanz > 1 )
  {
    for ( n = ws2812_maxanz - 1; n > 0; n-- )
    {
      ws2812_ptr[ n ] = ws2812_ptr[ n - 1 ];
    }
    ws2812_ptr[ n ] = WS2812_RGB_COL_OFF;
    
    UB_WS2812_Refresh( );
  }
}

//--------------------------------------------------------------
// alle LEDs der aktiven WS2812-Kette eine position nach links rotieren
// letzte LED bekommt den Farbwert der ersten LED
//--------------------------------------------------------------
void UB_WS2812_Rotate_Left( void )
{
  uint32_t n;
  WS2812_RGB_t d;
  
  if ( ws2812_channel == 0 )
    return;
  
  if ( ws2812_maxanz > 1 )
  {
    d = ws2812_ptr[ 0 ];
    for ( n = 1; n < ws2812_maxanz; n++ )
    {
      ws2812_ptr[ n - 1 ] = ws2812_ptr[ n ];
    }
    ws2812_ptr[ n - 1 ] = d;
    
    UB_WS2812_Refresh( );
  }
}

//--------------------------------------------------------------
// alle LEDs der aktiven WS2812-Kette eine position nach rechts rotieren
// erste LED bekommt den Farbwert der letzten LED
//--------------------------------------------------------------
void UB_WS2812_Rotate_Right( void )
{
  uint32_t n;
  WS2812_RGB_t d;
  
  if ( ws2812_channel == 0 )
    return;
  
  if ( ws2812_maxanz > 1 )
  {
    d = ws2812_ptr[ ws2812_maxanz - 1 ];
    for ( n = ws2812_maxanz - 1; n > 0; n-- )
    {
      ws2812_ptr[ n ] = ws2812_ptr[ n - 1 ];
    }
    ws2812_ptr[ n ] = d;
    
    UB_WS2812_Refresh( );
  }
}

//--------------------------------------------------------------
// interne Funktion
// loescht alle WS2812-Ketten
//--------------------------------------------------------------
void p_WS2812_clearAll( void )
{
  //-------------------------
  // einmal DMA starten
  // (ohne Signal, Dauer LoPegel)
  //-------------------------
  if ( WS2812_LED_CH4_ANZ > 0 )
  {
    // auf Kanal 4 schalten
    UB_WS2812_SetChannel( 4 );
    // warte bis DMA-Transfer fertig
    while ( ws2812_dma_status != 0 )
      ;
    // DMA starten
    p_WS2812_DMA_Start( );
  }
  if ( WS2812_LED_CH3_ANZ > 0 )
  {
    // auf Kanal 3 schalten
    UB_WS2812_SetChannel( 3 );
    // warte bis DMA-Transfer fertig
    while ( ws2812_dma_status != 0 )
      ;
    // DMA starten
    p_WS2812_DMA_Start( );
  }
  if ( WS2812_LED_CH2_ANZ > 0 )
  {
    // auf Kanal 2 schalten
    UB_WS2812_SetChannel( 2 );
    // warte bis DMA-Transfer fertig
    while ( ws2812_dma_status != 0 )
      ;
    // DMA starten
    p_WS2812_DMA_Start( );
  }
  if ( WS2812_LED_CH1_ANZ > 0 )
  {
    // auf Kanal 1 schalten
    UB_WS2812_SetChannel( 1 );
    // warte bis DMA-Transfer fertig
    while ( ws2812_dma_status != 0 )
      ;
    // DMA starten
    p_WS2812_DMA_Start( );
  }
  
  //-------------------------
  // alle LEDs ausschalten
  //-------------------------
  if ( WS2812_LED_CH4_ANZ > 0 )
  {
    // auf Kanal 4 schalten
    UB_WS2812_SetChannel( 4 );
    UB_WS2812_All_Led_RGB( WS2812_RGB_COL_OFF, 1 );
  }
  if ( WS2812_LED_CH3_ANZ > 0 )
  {
    // auf Kanal 3 schalten
    UB_WS2812_SetChannel( 3 );
    UB_WS2812_All_Led_RGB( WS2812_RGB_COL_OFF, 1 );
  }
  if ( WS2812_LED_CH2_ANZ > 0 )
  {
    // auf Kanal 2 schalten
    UB_WS2812_SetChannel( 2 );
    UB_WS2812_All_Led_RGB( WS2812_RGB_COL_OFF, 1 );
  }
  if ( WS2812_LED_CH1_ANZ > 0 )
  {
    // auf Kanal 1 schalten
    UB_WS2812_SetChannel( 1 );
    UB_WS2812_All_Led_RGB( WS2812_RGB_COL_OFF, 1 );
  }
}

//--------------------------------------------------------------
// interne Funktion
// errechnet aus den RGB-Farbwerten der aktiven LEDs
// die notwendigen PWM-Werte fuer die Datenleitung
//--------------------------------------------------------------
void p_WS2812_calcTimerBuf( void )
{
  uint32_t n;
  uint32_t pos;
  WS2812_RGB_t led;
  
  if ( ws2812_channel == 0 )
    return;
  
  pos = 0;
  // timingzeiten fuer alle LEDs setzen
  for ( n = 0; n < ws2812_maxanz; n++ )
  {
    led = ws2812_ptr[ n ];
    
    // Col:Green , Bit:7..0
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x80 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x40 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x20 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x10 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x08 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x04 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x02 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.green & 0x01 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    
    // Col:Red , Bit:7..0
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x80 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x40 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x20 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x10 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x08 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x04 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x02 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.red & 0x01 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    
    // Col:Blue , Bit:7..0
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x80 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x40 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x20 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x10 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x08 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x04 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x02 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
    WS2812_TIMER_BUF[ pos ] = WS2812_LO_TIME;
    if ( ( led.blue & 0x01 ) != 0 )
      WS2812_TIMER_BUF[ pos ] = WS2812_HI_TIME;
    pos++;
  }
  
  // nach den Farbinformationen eine Pausenzeit anh鋘gen (2*30ms)
  for ( n = 0; n < WS2812_PAUSE_ANZ * WS2812_BIT_PER_LED; n++ )
  {
    WS2812_TIMER_BUF[ pos ] = 0;  // 0 => fuer Pausenzeit
    pos++;
  }
}

//--------------------------------------------------------------
// interne Funktion
// DMA und Timer starten
// (gestoppt wird per Transfer-Complete-Interrupt)
//--------------------------------------------------------------
void p_WS2812_DMA_Start( void )
{
  if ( ws2812_channel == 0 )
    return;
  
  // status auf "busy" setzen
  ws2812_dma_status = 1;
  // init vom DMA
  p_WS2812_InitDMA( );
  if ( ws2812_channel == 1 )
  {
    // enable vom Transfer-Complete Interrupt
    DMA_ITConfig( WS2812_DMA_CH1_STREAM, DMA_IT_TC, ENABLE );
    // DMA enable
    DMA_Cmd( WS2812_DMA_CH1_STREAM, ENABLE );
  }
  else if ( ws2812_channel == 2 )
  {
    // enable vom Transfer-Complete Interrupt
    DMA_ITConfig( WS2812_DMA_CH2_STREAM, DMA_IT_TC, ENABLE );
    // DMA enable
    DMA_Cmd( WS2812_DMA_CH2_STREAM, ENABLE );
  }
  else if ( ws2812_channel == 3 )
  {
    // enable vom Transfer-Complete Interrupt
    DMA_ITConfig( WS2812_DMA_CH3_STREAM, DMA_IT_TC, ENABLE );
    // DMA enable
    DMA_Cmd( WS2812_DMA_CH3_STREAM, ENABLE );
  }
  else if ( ws2812_channel == 4 )
  {
    // enable vom Transfer-Complete Interrupt
    DMA_ITConfig( WS2812_DMA_CH4_STREAM, DMA_IT_TC, ENABLE );
    // DMA enable
    DMA_Cmd( WS2812_DMA_CH4_STREAM, ENABLE );
  }
  // Timer enable
  TIM_Cmd( WS2812_TIM, ENABLE );
}

//--------------------------------------------------------------
// interne Funktion
// init vom GPIO Pin
//--------------------------------------------------------------
void p_WS2812_InitIO( void )
{
  GPIO_InitTypeDef GPIO_InitStructure;
  
#if WS2812_LED_CH1_ANZ>0 
  // Clock Enable
  RCC_AHB1PeriphClockCmd(WS2812_CH1_CLOCK, ENABLE);

  // Config des Pins als Digital-Ausgang
  GPIO_InitStructure.GPIO_Pin = WS2812_CH1_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(WS2812_CH1_PORT, &GPIO_InitStructure);

  // Lo-Pegel ausgeben
  WS2812_CH1_PORT->BSRRH = WS2812_CH1_PIN;

  // Alternative-Funktion mit dem IO-Pin verbinden
  GPIO_PinAFConfig(WS2812_CH1_PORT, WS2812_CH1_SOURCE, WS2812_TIM_AF);
#endif
  
#if WS2812_LED_CH2_ANZ>0 
  // Clock Enable
  RCC_AHB1PeriphClockCmd(WS2812_CH2_CLOCK, ENABLE);

  // Config des Pins als Digital-Ausgang
  GPIO_InitStructure.GPIO_Pin = WS2812_CH2_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(WS2812_CH2_PORT, &GPIO_InitStructure);

  // Lo-Pegel ausgeben
  WS2812_CH2_PORT->BSRRH = WS2812_CH2_PIN;

  // Alternative-Funktion mit dem IO-Pin verbinden
  GPIO_PinAFConfig(WS2812_CH2_PORT, WS2812_CH2_SOURCE, WS2812_TIM_AF);
#endif
  
#if WS2812_LED_CH3_ANZ>0 
  // Clock Enable
  RCC_AHB1PeriphClockCmd(WS2812_CH3_CLOCK, ENABLE);

  // Config des Pins als Digital-Ausgang
  GPIO_InitStructure.GPIO_Pin = WS2812_CH3_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(WS2812_CH3_PORT, &GPIO_InitStructure);

  // Lo-Pegel ausgeben
  WS2812_CH3_PORT->BSRRH = WS2812_CH3_PIN;

  // Alternative-Funktion mit dem IO-Pin verbinden
  GPIO_PinAFConfig(WS2812_CH3_PORT, WS2812_CH3_SOURCE, WS2812_TIM_AF);
#endif
  
#if WS2812_LED_CH4_ANZ>0 
  // Clock Enable
  RCC_AHB1PeriphClockCmd(WS2812_CH4_CLOCK, ENABLE);

  // Config des Pins als Digital-Ausgang
  GPIO_InitStructure.GPIO_Pin = WS2812_CH4_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(WS2812_CH4_PORT, &GPIO_InitStructure);

  // Lo-Pegel ausgeben
  WS2812_CH4_PORT->BSRRH = WS2812_CH4_PIN;

  // Alternative-Funktion mit dem IO-Pin verbinden
  GPIO_PinAFConfig(WS2812_CH4_PORT, WS2812_CH4_SOURCE, WS2812_TIM_AF);
#endif
}

//--------------------------------------------------------------
// interne Funktion
// init vom Timer
//--------------------------------------------------------------
void p_WS2812_InitTIM( void )
{
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_OCInitTypeDef TIM_OCInitStructure;
  
  // Clock enable (TIM)
  RCC_APB1PeriphClockCmd( WS2812_TIM_CLOCK, ENABLE );
  
  // Clock Enable (DMA)
  RCC_AHB1PeriphClockCmd( WS2812_DMA_CLOCK, ENABLE );
  
  // Timer init
  TIM_TimeBaseStructure.TIM_Period = WS2812_TIM_PERIODE;
  TIM_TimeBaseStructure.TIM_Prescaler = WS2812_TIM_PRESCALE;
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit( WS2812_TIM, &TIM_TimeBaseStructure );
  
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  
#if WS2812_LED_CH1_ANZ>0
  TIM_OC1Init(WS2812_TIM, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(WS2812_TIM, TIM_OCPreload_Enable);
#endif
#if WS2812_LED_CH2_ANZ>0
  TIM_OC2Init(WS2812_TIM, &TIM_OCInitStructure);
  TIM_OC2PreloadConfig(WS2812_TIM, TIM_OCPreload_Enable);
#endif
#if WS2812_LED_CH3_ANZ>0
  TIM_OC3Init(WS2812_TIM, &TIM_OCInitStructure);
  TIM_OC3PreloadConfig(WS2812_TIM, TIM_OCPreload_Enable);
#endif
#if WS2812_LED_CH4_ANZ>0
  TIM_OC4Init(WS2812_TIM, &TIM_OCInitStructure);
  TIM_OC4PreloadConfig(WS2812_TIM, TIM_OCPreload_Enable);
#endif
  
  // Timer enable
  TIM_ARRPreloadConfig( WS2812_TIM, ENABLE );
}

//--------------------------------------------------------------
// interne Funktion
// init vom DMA
//--------------------------------------------------------------
void p_WS2812_InitDMA( void )
{
  DMA_InitTypeDef DMA_InitStructure;
  
  if ( ws2812_channel == 0 )
    return;
  
  // DMA init
  if ( ws2812_channel == 1 )
  {
    DMA_Cmd( WS2812_DMA_CH1_STREAM, DISABLE );
    DMA_DeInit( WS2812_DMA_CH1_STREAM );
    DMA_InitStructure.DMA_Channel = WS2812_DMA_CH1_CHANNEL;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &WS2812_TIM_CCR_REG1;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) WS2812_TIMER_BUF;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = WS2812_TIMER_BUF_LEN1;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16bit
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init( WS2812_DMA_CH1_STREAM, &DMA_InitStructure );
  }
  else if ( ws2812_channel == 2 )
  {
    DMA_Cmd( WS2812_DMA_CH2_STREAM, DISABLE );
    DMA_DeInit( WS2812_DMA_CH2_STREAM );
    DMA_InitStructure.DMA_Channel = WS2812_DMA_CH2_CHANNEL;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &WS2812_TIM_CCR_REG2;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) WS2812_TIMER_BUF;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = WS2812_TIMER_BUF_LEN2;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16bit
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init( WS2812_DMA_CH2_STREAM, &DMA_InitStructure );
  }
  else if ( ws2812_channel == 3 )
  {
    DMA_Cmd( WS2812_DMA_CH3_STREAM, DISABLE );
    DMA_DeInit( WS2812_DMA_CH3_STREAM );
    DMA_InitStructure.DMA_Channel = WS2812_DMA_CH3_CHANNEL;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &WS2812_TIM_CCR_REG3;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) WS2812_TIMER_BUF;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = WS2812_TIMER_BUF_LEN3;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16bit
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init( WS2812_DMA_CH3_STREAM, &DMA_InitStructure );
  }
  else if ( ws2812_channel == 4 )
  {
    DMA_Cmd( WS2812_DMA_CH4_STREAM, DISABLE );
    DMA_DeInit( WS2812_DMA_CH4_STREAM );
    DMA_InitStructure.DMA_Channel = WS2812_DMA_CH4_CHANNEL;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &WS2812_TIM_CCR_REG4;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) WS2812_TIMER_BUF;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = WS2812_TIMER_BUF_LEN4;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16bit
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init( WS2812_DMA_CH4_STREAM, &DMA_InitStructure );
  }
}

//--------------------------------------------------------------
// interne Funktion
// init vom NVIC
//--------------------------------------------------------------
void p_WS2812_InitNVIC( void )
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
#if WS2812_LED_CH1_ANZ>0
  TIM_DMACmd(WS2812_TIM, WS2812_TIM_DMA_TRG1, ENABLE);
#endif
#if WS2812_LED_CH2_ANZ>0
  TIM_DMACmd(WS2812_TIM, WS2812_TIM_DMA_TRG2, ENABLE);
#endif
#if WS2812_LED_CH3_ANZ>0
  TIM_DMACmd(WS2812_TIM, WS2812_TIM_DMA_TRG3, ENABLE);
#endif
#if WS2812_LED_CH4_ANZ>0
  TIM_DMACmd(WS2812_TIM, WS2812_TIM_DMA_TRG4, ENABLE);
#endif
  
#if WS2812_LED_CH1_ANZ>0
  NVIC_InitStructure.NVIC_IRQChannel = WS2812_DMA_CH1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
#endif
#if WS2812_LED_CH2_ANZ>0
  NVIC_InitStructure.NVIC_IRQChannel = WS2812_DMA_CH2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
#endif
#if WS2812_LED_CH3_ANZ>0
  NVIC_InitStructure.NVIC_IRQChannel = WS2812_DMA_CH3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
#endif
#if WS2812_LED_CH4_ANZ>0
  NVIC_InitStructure.NVIC_IRQChannel = WS2812_DMA_CH4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
#endif
}

//--------------------------------------------------------------
// interne Funktion
// ISR vom DMA (CH1)
// (wird aufgerufen, wenn alle Daten uebertragen wurden)
//--------------------------------------------------------------
void WS2812_DMA_CH1_ISR( void )
{
  // Test auf Transfer-Complete Interrupt Flag
  if ( DMA_GetITStatus( WS2812_DMA_CH1_STREAM, WS2812_DMA_CH1_IRQ_FLAG ) )
  {
    // Flag zuruecksetzen
    DMA_ClearITPendingBit( WS2812_DMA_CH1_STREAM, WS2812_DMA_CH1_IRQ_FLAG );
    
    // Timer disable
    TIM_Cmd( WS2812_TIM, DISABLE );
    // DMA disable
    DMA_Cmd( WS2812_DMA_CH1_STREAM, DISABLE );
    
    // status auf "ready" setzen
    ws2812_dma_status = 0;
  }
}

//--------------------------------------------------------------
// interne Funktion
// ISR vom DMA (CH2)
// (wird aufgerufen, wenn alle Daten uebertragen wurden)
//--------------------------------------------------------------
void WS2812_DMA_CH2_ISR( void )
{
  // Test auf Transfer-Complete Interrupt Flag
  if ( DMA_GetITStatus( WS2812_DMA_CH2_STREAM, WS2812_DMA_CH2_IRQ_FLAG ) )
  {
    // Flag zuruecksetzen
    DMA_ClearITPendingBit( WS2812_DMA_CH2_STREAM, WS2812_DMA_CH2_IRQ_FLAG );
    
    // Timer disable
    TIM_Cmd( WS2812_TIM, DISABLE );
    // DMA disable
    DMA_Cmd( WS2812_DMA_CH2_STREAM, DISABLE );
    
    // status auf "ready" setzen
    ws2812_dma_status = 0;
  }
}

//--------------------------------------------------------------
// interne Funktion
// ISR vom DMA (CH3)
// (wird aufgerufen, wenn alle Daten uebertragen wurden)
//--------------------------------------------------------------
void WS2812_DMA_CH3_ISR( void )
{
  // Test auf Transfer-Complete Interrupt Flag
  if ( DMA_GetITStatus( WS2812_DMA_CH3_STREAM, WS2812_DMA_CH3_IRQ_FLAG ) )
  {
    // Flag zuruecksetzen
    DMA_ClearITPendingBit( WS2812_DMA_CH3_STREAM, WS2812_DMA_CH3_IRQ_FLAG );
    
    // Timer disable
    TIM_Cmd( WS2812_TIM, DISABLE );
    // DMA disable
    DMA_Cmd( WS2812_DMA_CH3_STREAM, DISABLE );
    
    // status auf "ready" setzen
    ws2812_dma_status = 0;
  }
}

//--------------------------------------------------------------
// interne Funktion
// ISR vom DMA (CH4)
// (wird aufgerufen, wenn alle Daten uebertragen wurden)
//--------------------------------------------------------------
void WS2812_DMA_CH4_ISR( void )
{
  // Test auf Transfer-Complete Interrupt Flag
  if ( DMA_GetITStatus( WS2812_DMA_CH4_STREAM, WS2812_DMA_CH4_IRQ_FLAG ) )
  {
    // Flag zuruecksetzen
    DMA_ClearITPendingBit( WS2812_DMA_CH4_STREAM, WS2812_DMA_CH4_IRQ_FLAG );
    
    // Timer disable
    TIM_Cmd( WS2812_TIM, DISABLE );
    // DMA disable
    DMA_Cmd( WS2812_DMA_CH4_STREAM, DISABLE );
    
    // status auf "ready" setzen
    ws2812_dma_status = 0;
  }
}

 

posted @ 2015-08-27 00:29  IAmAProgrammer  阅读(1021)  评论(0编辑  收藏  举报