AnsweredAssumed Answered

Trouble getting 800kHz from PWM timer and DMA

Question asked by Mahjoub.Youssif on Jun 1, 2015
Latest reply on Jun 3, 2015 by Mahjoub.Youssif
Hi guys,

I've been trying to get this code working for the past week now and i'm out of ideas. I'm trying to control the ws2812 LEDs with PWM. These LEDs have tight timings and require a single 800kHz data/clock line. I seems no matter what i do i cannot get anywhere close to 800kHz.

In this image DMA is disabled and just the timer is generating a PWM signal. not sure why its not square and the period is 60 ms instead of the 1.25ns to get 800kHz.
http://i.imgur.com/zenqjt8.png

This next image is with DMA enabled. I have no idea why the period is 3ms.
http://i.imgur.com/iDjAgwS.png

Data sheet for LEDs: https://cdn.sparkfun.com/datasheets/Components/LED/COM-12877.pdf

Code i based my code off: https://github.com/devthrash/STM32F1-workarea/blob/master/Project/WS2812/main.c

Here is my code: 

#include <stm32f10x.h>
//#include <stdio.h>
 
#define GPIO_PWM_PIN            GPIO_Pin_2
#define GPIO_USART_PIN      GPIO_Pin_9
 
#define PWM_HIGH_WIDTH      17
#define PWM_LOW_WIDTH           9
 
#define LED_COUNT                   1
 
 
/* Buffer that holds one complete DMA transmission.
 *
 * Ensure that this buffer is big enough to hold
 * all data bytes that need to be sent.
 *
 * The buffer size can be calculated as followas:
 * number of LEDs * 24 bytes + 42 bytes.
 *
 * This leaves us with a maximum string length of
 * (2^16 bytes per DMA stream - 42 bytes)/24 bytes per LED = 2728 LEDs.
 */
uint16_t ledBuff[24*LED_COUNT+42];
char usartBuff[32];
 
uint8_t rgb[5][3] = {
        {255, 0, 0},
        {0, 255, 0},
        {0, 0, 255},
        {255, 255, 255},
        {196, 227, 185}
};
 
 
 
//Prototypes
void RCC_Config(void);
void GPIO_Config(void);
void TIM_Config(void);
void USART_Config(void);
void DMA_Config(void);
void send_data(uint8_t (*color)[3], uint16_t len);
 
 
void Delay(__IO uint32_t nCount) {
  while(nCount--) {
  }
}
 
int main() {
 
    int16_t i;
    //char str[500] = {(char)ledBuff};//"TEST!!!!TEST\n\r";
     
    RCC_Config();
    GPIO_Config();
    USART_Config();
    TIM_Config();
    DMA_Config();
     
    while (1){ 
     
            send_data(&rgb[3], 1);
         
            for(i = 0; i < sizeof(usartBuff); i++) {
                while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) {}
                USART_SendData(USART1, usartBuff[i]);
            }
             
            Delay(5000L);
    }
}
 
void RCC_Config(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1 , ENABLE);      // Enable clock for GPIOA and USART1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);                                                        // Enable clock for TIM2
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                                                          // Enable clock for DMA1
}
 
void GPIO_Config(void) {
 
    GPIO_InitTypeDef GPIO_InitStruct;
     
    GPIO_InitStruct.GPIO_Pin = GPIO_PWM_PIN | GPIO_USART_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}
 
void USART_Config(void) {
     
    USART_InitTypeDef USART_InitStruct;
     
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx;
     
    USART_Init(USART1, &USART_InitStruct);
     
    USART_Cmd(USART1, ENABLE);
}
 
void TIM_Config(void) {
     
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStruct;
    TIM_OCInitTypeDef  TIM_OCInitStruct;
 
    uint16_t PrescalerValue = (uint16_t)(72000000 / 24000000) - 1;                  // PrescalerValue = 2, Clock is scaled down to 72MHz /(PrescalerValue + 1) = 762MHz/3 = 24MHz
         
    /* Time base configuration */
    TIM_TimeBaseStruct.TIM_Period = 29; // 800kHz                                       // Species the period value
    TIM_TimeBaseStruct.TIM_Prescaler = PrescalerValue;                                          // Specifies the prescaler value used to divide the TIM clock.
    TIM_TimeBaseStruct.TIM_ClockDivision = 0;                                                               // Specifies the clock division.
    TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;                                //
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
 
    /* PWM1 Mode configuration: Channel1 */
    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;                                                  // Specifies the TIM mode.
  TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;                            //
    //TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Disable;
    TIM_OCInitStruct.TIM_Pulse = 15;
  TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    //TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
  //TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;
    TIM_OC3Init(TIM2, &TIM_OCInitStruct);
    //TIM_Cmd(TIM2, ENABLE);
     
}
 
void DMA_Config(void) {
     
    DMA_InitTypeDef DMA_InitStruct;
     
    DMA_DeInit(DMA1_Channel1);                                                                                                      // Deinitialize DAM1 Channel 1 to their default reset values.
     
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR3;                              // Specifies Physical address of the peripheral in this case Timer 2 CCR1
    DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ledBuff;                                             // Specifies the buffer memory address
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;                                                             // Data transfered from memory to peripheral
    DMA_InitStruct.DMA_BufferSize = 42;                                                                                     // Specifies the buffer size
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                   // Do not incrament the peripheral address
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                    // Incrament the buffer index
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;            // Specifies the peripheral data width
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                            // Specifies the memory data width
    DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;                                                                      // Specifies the operation mode. Normal or Circular
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;                                                            // Specifies the software priority
    DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                                                                           //
     
    DMA_Init(DMA1_Channel1, &DMA_InitStruct);                                                                           // Initialize DAM1 Channel 1 to values specified in the DMA_InitStruct structure.
     
    TIM_DMACmd(TIM2, TIM_DMA_CC3, ENABLE);                                                                              // Enables TIM2's DMA request. TIM_DMA_CC1 : TIM Capture Compare 1 DMA source
 
}
 
void send_data(uint8_t (*color)[3], uint16_t len) {
     
    uint8_t i;
    uint8_t led;
    uint16_t memaddr;
    uint16_t buffersize;
     
    buffersize = (len*24)+42;           // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes
    memaddr = 0;                                    // reset buffer memory index
    led = 0;
     
    while(len)
    {  
        for (i = 0; i < 8; i++)                 // RED data
        {
            if ((color[led][0]<<i) & 0x80)  // data sent MSB first, j = 0 is MSB j = 7 is LSB
            {
                ledBuff[memaddr] = PWM_HIGH_WIDTH;  // compare value for logical 1
                usartBuff[memaddr] = '1';
            }
            else
            {
                ledBuff[memaddr] = PWM_LOW_WIDTH;   // compare value for logical 0
                usartBuff[memaddr] = '0';
            }
            memaddr++;
        }
         
        usartBuff[memaddr+1] = '-';
         
        for (i = 0; i < 8; i++)                 // GREEN data
        {
            if ((color[led][1]<<i) & 0x80)  // data sent MSB first, j = 0 is MSB j = 7 is LSB
            {
                ledBuff[memaddr] = PWM_HIGH_WIDTH;  // compare value for logical 1
                usartBuff[memaddr+2] = '1';
            }
            else
            {
                ledBuff[memaddr] = PWM_LOW_WIDTH;   // compare value for logical 0
                usartBuff[memaddr+2] = '0';
            }
            memaddr++;
        }
         
        usartBuff[memaddr+3] = '-';
         
        for (i = 0; i < 8; i++)                 // BLUE data
        {
            if ((color[led][2]<<i) & 0x80)  // data sent MSB first, j = 0 is MSB j = 7 is LSB
            {
                ledBuff[memaddr] = PWM_HIGH_WIDTH;  // compare value for logical 1
                usartBuff[memaddr+4] = '1';
            }
            else
            {
                ledBuff[memaddr] = PWM_LOW_WIDTH;   // compare value for logical 0
                usartBuff[memaddr+4] = '0';
            }
            memaddr++;
        }
         
        usartBuff[memaddr+5] = '\n';
        usartBuff[memaddr+5] = '\r';
        led++;
        len--;
    }
     
    // add needed delay at end of byte cycle, pulsewidth = 0
    while(memaddr < buffersize)
    {
        ledBuff[memaddr] = 0;
        memaddr++;
    }
     
    DMA_SetCurrDataCounter(DMA1_Channel1, buffersize);  // load number of bytes to be transferred
    DMA_Cmd(DMA1_Channel1, ENABLE);                                         // enable DMA channel 1
    TIM_Cmd(TIM2, ENABLE);                                                          // enable Timer 2
    while(!DMA_GetFlagStatus(DMA1_FLAG_TC1));                   // wait until transfer complete
    TIM_Cmd(TIM2, DISABLE);                                                         // disable Timer 2
    DMA_Cmd(DMA1_Channel1, DISABLE);                                        // disable DMA channel 1
    DMA_ClearFlag(DMA1_FLAG_TC1);                                           // clear DMA1 Channel 1 transfer complete flag
}




Outcomes