AnsweredAssumed Answered

DMA only sending one byte and not whole buffer

Question asked by fireout on Oct 21, 2015
Latest reply on Oct 22, 2015 by waclawek.jan
STM32f30x
The aim is to trigger the DMA from a timer. The DMA should then send one row of data from my buffer to the SPI. The DMA should then trigger an interrupt which points DMA to the next row in the buffer. It should now wait until it is triggered again from the timer before sending.
Currently this all works but for one thing. It only sends 1 byte to the SPI and then waits for the timer trigger before sending the next.  On a scope I can see that if I change the timer speed then the inter byte gap coming out of SPI changes.

#include "stm32f30x_dma.h"
#include "stm32f30x_gpio.h"
#include "stm32f30x_rcc.h"
#include "stm32f30x_spi.h"
#include "stm32f30x_tim.h"
#include "stm32f30x_misc.h"
#include "stm32f30x_syscfg.h"
 
 
#define X_OFFSET 20
 
#define ROW 200
#define COL 32
 
 
#define START1 24
#define END1    312
 
 
uint8_t MemBuf[ROW][COL];
uint8_t RowCount = 0;
 
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_OCInitTypeDef  TIM_OCInitStructure;
NVIC_InitTypeDef   NVIC_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef  DMA_InitStructure;
 
 
 
//-----------------------------------------------------------------
 
void Init(void)
{
 
    uint16_t PrescalerValue = 0;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);//GPIOB clock enable
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //TIM4 clock enable
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1 clock enable
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//SPI2 clock enable
 
 
     
//------------------
//GPIO Setup    - output for MOSI
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15 ; // we want to configure AF pins on port B
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;       // we want the pins to be alternate functions
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;  // this sets the GPIO modules clock speed
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;     // this sets the pin type to push / pull (as opposed to open drain)
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;   // this sets the pullup / pulldown resistors
    GPIO_Init(GPIOB, &GPIO_InitStruct);
 
 
//------------------
//TIM4 setup
  PrescalerValue = (uint16_t) (SystemCoreClock / 2000000) - 1;//Compute the prescaler value
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);// Time Base configuration
    TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = X_OFFSET + 80;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
 
//setup the output compare
    TIM_OCStructInit(&TIM_OCInitStructure);
 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
    TIM_OCInitStructure.TIM_Pulse = X_OFFSET;
    TIM_OC1Init(TIM4, &TIM_OCInitStructure);
 
  TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//??
  TIM_ARRPreloadConfig(TIM4, ENABLE); //??
 
  //Master mode - trigger DMA
    TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_OC1Ref);
 
 
 
//------------------
 
 
    TIM_DMACmd(TIM4,TIM_DMA_CC1, ENABLE ); // Enable TIM1_CC1 DMA Requests
 
//------------------
//Interrupt setup
 
//Enable DMA1 channel IRQ Channel
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
//------------------
//DMA1 setup
//Transfer Data to SPI2 from RAM buffer
//Trigger from TIM4 OC1
 
    DMA_Cmd(DMA1_Channel1, DISABLE);
    DMA_DeInit(DMA1_Channel1);//reset DMA1 channe1 to default values;
 
    DMA_StructInit(&DMA_InitStructure);
     
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//channel will be used for memory to SPI transfer
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//setting normal mode (non circular)
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;//medium priority
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//source and destination data size half word=16bit
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//automatic memory increment enable.
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//Location assigned to peripheral register will be destination
    DMA_InitStructure.DMA_BufferSize = COL;// / 2;//amount of data to be transfered - divide by 2 because we using 16bit
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;//source and destination start addresses
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&MemBuf;
 
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);//send values to DMA registers
 
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);// Enable DMA1 Channel Transfer Complete interrupt
 
 
//------------------
//SPI setup
    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft ;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
 
    SPI_Init(SPI2, &SPI_InitStructure);
 
    SPI_I2S_DMACmd (SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
 
 
 
//------------------
//Connect output to alternate function - AF2
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5); //MOSI on SPI2
//------------------
 
    SPI_Cmd(SPI2, ENABLE);
    TIM_Cmd(TIM4, ENABLE);
    DMA_Cmd(DMA1_Channel1, ENABLE);
}
 
//-----------------------------------------------------------------
void DMA1_Channel1_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC1))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1 | DMA1_IT_GL1);
        DMA_Cmd(DMA1_Channel1, DISABLE);
        RowCount++;
        if(RowCount >= ROW)
        {
            DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)MemBuf;//end of buffer reached - start at beginning
            RowCount = 0;
 
        }
        else
        {
            DMA_InitStructure.DMA_MemoryBaseAddr += COL;//move to next row in buffer
        }
        DMA_Init(DMA1_Channel1, &DMA_InitStructure);//send values to DMA registers
        DMA_Cmd(DMA1_Channel1, ENABLE);
 
    }
    else
    {
        DMA_ClearITPendingBit(DMA1_IT_GL1);
    }
}
//-----------------------------------------------------------------
int main(void)
{
 
    Init();
    for (uint8_t var = 0; var < 20; ++var)
    {
        MemBuf[0][var] = 0xaa;//put some stuff in one row of the buffer
    }
 
    while(1)
    {
        //put your code here
    }
}

Outcomes