• 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;
      }
    }
  • 相关阅读:
    初识python: 斐波拉契数(生成器获取)
    初识python:斐波拉契数(列表获取)
    初识python: 递归函数
    初识python:多线程
    初识python 之 取101到200之前的所有素数
    初识python 之 兔子生崽(小练习)
    初识python 之 MongoDB 基本操作
    初识python 之 mysql数据库基本操作
    MongoDB 安装及制作成windows服务
    初识python: 回调函数
  • 原文地址:https://www.cnblogs.com/shangdawei/p/4762103.html
Copyright © 2020-2023  润新知