AnsweredAssumed Answered

STM32F100RB DMA+Timer PPM Stream[SOLVED]

Question asked by damian on Jun 21, 2015
Latest reply on May 9, 2017 by Kosekar.Himanshu
I am currently trying to generate a ppm stream for a RC transmitter using the DMA and timer on a STM32F100RB but am having a few difficulties getting it working. The code I have is based on a similar problem solved here. 

#include <stm32f10x.h>
#include <stm32f10x_adc.h>
#include <stm32f10x_dma.h>
#include <stm32f10x_exti.h>
#include <stm32f10x_gpio.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_spi.h>
#include <stm32f10x_tim.h>
#include <misc.h>
  
#define TIM2_CCR1   0x4000004C  // Not CCR but DMAR pointing to CCR
#define ADC1_DR     0x4001244C
  
#define PPM_ELEMENTS 18
#define ADC_ELEMENTS 9
  
u16 PPM_Buffer[PPM_ELEMENTS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21998};
u16 ADC_Buffer[ADC_ELEMENTS];
  
void SysClkInit(void);
void ADCInit(void);
void DMAInit(void);
void TIM2Init(void);
  
int main(void)
{
    SysClkInit();
    ADCInit();
    TIM2Init();
    DMAInit();
  
  
  
  
    // if (pressed trim at power on) calibrate mode
  
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  
    while(1)
    {
  
    }
}
  
void SysClkInit()
{
    RCC_DeInit();    // Reset RCC to default
  
    RCC_HSEConfig(RCC_HSE_ON);    // Enable HSE
  
    ErrorStatus HSEStartUpStatus = RCC_WaitForHSEStartUp(); // Wait for HSE to stabilise
  
    if (HSEStartUpStatus == SUCCESS)    // If we are stable
    {
        RCC_HCLKConfig(RCC_SYSCLK_Div1);    // HCLK = SYSCLK/1
        RCC_PCLK2Config(RCC_HCLK_Div1);     // PCLK2 = HCLK/1
        RCC_PCLK1Config(RCC_HCLK_Div1);     // PCLK1 = HCLK/1
  
        // PLLCLK = (8MHzHSE/2) * 6 = 24 MHz
        RCC_PREDIV1Config(RCC_PREDIV1_Source_HSE, RCC_PREDIV1_Div2);
        RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_6);
  
        RCC_PLLCmd(ENABLE);     // Enable the PLL
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);    // Wait for it to be ready
  
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);    // Use PLL as SYSCLK
  
        while (RCC_GetSYSCLKSource() != 0x08);    // Wait for switch over to happen
    }
}
  
void ADCInit(void)
{
    //Enable ADC1, GPIOA, and GPIOB
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1  |
                           RCC_APB2Periph_GPIOA |
                           RCC_APB2Periph_GPIOB |
                           RCC_APB2Periph_AFIO, ENABLE);
  
    GPIO_InitTypeDef GPIO_InitStructure; //Variable used to setup the GPIO pins
  
    //Configure ADC pins (PA0 -> Channel 1 to PA7 -> Channel 8) as analog inputs
    GPIO_StructInit(&GPIO_InitStructure); // Reset init structure, if not it can cause issues...
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |
                                  GPIO_Pin_1 |
                                  GPIO_Pin_2 |
                                  GPIO_Pin_3 |
                                  GPIO_Pin_4 |
                                  GPIO_Pin_5 |
                                  GPIO_Pin_6 |
                                  GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOB, &GPIO_InitStructure);      // init batt pin
  
    ADC_InitTypeDef ADC_InitStructure;
    //ADC1 configuration
  
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    //We will convert multiple channels
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    //select single conversion mode
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    //select no external triggering
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    //right 12-bit data alignment in ADC data register
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    //9 channels conversion
    ADC_InitStructure.ADC_NbrOfChannel = 9;
    //load structure values to control and status registers
    ADC_Init(ADC1, &ADC_InitStructure);
  
    //configure each channel
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 7, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_41Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_41Cycles5);
  
    //Enable ADC1
    ADC_Cmd(ADC1, ENABLE);
  
    //enable DMA for ADC
    ADC_DMACmd(ADC1, ENABLE);
  
    //Enable ADC1 reset calibration register
    ADC_ResetCalibration(ADC1);
    //Check the end of ADC1 reset calibration register
    while(ADC_GetResetCalibrationStatus(ADC1));
    //Start ADC1 calibration
    ADC_StartCalibration(ADC1);
    //Check the end of ADC1 calibration
    while(ADC_GetCalibrationStatus(ADC1));
}
  
void DMAInit(void)
{
    //enable DMA1 clock
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  
    //create DMA structure
    DMA_InitTypeDef  DMA_InitStructure;
  
    //reset DMA1 channe1 to default values;
    DMA_DeInit(DMA1_Channel1);
    //channel will be used for memory to memory transfer
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    //setting normal mode (non circular)
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    //medium priority
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    //source and destination data size word=32bit
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    //automatic memory destination increment enable.
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    //source address increment disable
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    //Location assigned to peripheral register will be source
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    //chunk of data to be transfered
    DMA_InitStructure.DMA_BufferSize = ADC_ELEMENTS;                            // 8 channels + Batt sense
    //source and destination start addresses
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Buffer;
    //send values to DMA registers
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  
    // Enable DMA1 Channel Transfer Complete interrupt
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Channel1, ENABLE); //Enable the DMA1 - Channel1
  
    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = PPM_ELEMENTS;                            // 8 channels + 9 space + end
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM2_CCR1;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)PPM_Buffer;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
  
    // turning DMA on
    DMA_Cmd(DMA1_Channel5, ENABLE);
  
    NVIC_InitTypeDef NVIC_InitStructure;
    //Enable DMA1 channel1 IRQ Channel */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
/*
    //Enable DMA1 channel5 IRQ Channel
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);*/
}
  
void TIM2Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure ;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
  
    TIM_TimeBaseStructure.TIM_Period = 22000 - 1;
    TIM_TimeBaseStructure.TIM_Prescaler = 24 - 1;    // 24 MHz / 24 (1 MHz tick)
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
  
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);
  
    TIM_DMAConfig(TIM2, TIM_DMABase_CCR1, TIM_DMABurstLength_1Transfer);
    // "connecting" DMA and TIM2
    TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);
  
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
  
    // turning on TIM3 and PWM outputs
    TIM_Cmd(TIM2, ENABLE);
}
  
void DMA1_Channel1_IRQHandler(void) // Convert ADC to mS
{
    int i;
  
    for (i = 0; i < PPM_ELEMENTS - 1; ++i)
    {
        if (i == 0)
        {
            PPM_Buffer[i] = 400;
        }
        else if (i % 2 == 1)
        {
            PPM_Buffer[i] = PPM_Buffer[i - 1] + ADC_Buffer[(i - 1) / 2]/4 + 1000;
        }
        else
        {
            PPM_Buffer[i] = PPM_Buffer[i - 1] + 400;
        }
    }
  
    DMA_ClearFlag(DMA1_FLAG_TC1 | DMA1_FLAG_GL1 | DMA1_FLAG_HT1);
  
}

The output should look like the attached picture.

Any ideas on where I am going wrong?

Attachments

Outcomes