cancel
Showing results for 
Search instead for 
Did you mean: 

TIM1 to control DMA peripheral-to-memory (8 ADCs)

matteo
Associate II
Posted on April 02, 2014 at 01:50

Hello,

Me again with basic questions. I am still trying to acquire from a bank of ADCs. 8 SAR ADCs in parallel on my stm32f429i-disco. From previous forum posts, and my own attempts, it appears I cannot use the HSI clock directly by putting it out on MCO1 and reading its transitions. So I am trying to learn how to use TIM1 and a peripheral-to-memory DMA. Since it's TIM1, I'll use DMA2. Because of the ADCs I have, a full read takes 16 cycles of the clock I send them (call it SCLK): - 4 leading zeros - 8 bits of data - 4 trailing zeros I generate the SCLK from the TIM1 and put it out on PA8:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
//Fill the TIM_TimeBaseInitStruct with the desired parameters
// TIM_TimeBaseInitTypeDef base;
TIM_TimeBaseStructInit(&TIM1_Settings);
TIM1_Settings.TIM_Prescaler = 0;
TIM1_Settings.TIM_CounterMode = TIM_CounterMode_Up;
TIM1_Settings.TIM_Period = 5; //AutoReload value in the TIM1->ARR register
TIM1_Settings.TIM_ClockDivision = 0;
//configure the Time Base unit with the corresponding configuration
TIM_TimeBaseInit(TIM1, &TIM1_Settings);
/* Channel 1 output configuration */
TIM_OCInitTypeDef OCbase;
TIM_OCStructInit(&OCbase);
OCbase.TIM_OCMode = TIM_OCMode_PWM2;
OCbase.TIM_OutputState = TIM_OutputState_Enable;
OCbase.TIM_Pulse = 2;
OCbase.TIM_OCPolarity = TIM_OCPolarity_Low;
OCbase.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OC1Init(TIM1, &OCbase);
//Enable TIM1 output on pin PA8
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
/* Very much needed. Enable Preload register on CCR1. */
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
//Enable Timer
 TIM_Cmd(TIM1, ENABLE);
/* TIM1 Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);

With the settings above (along with my clock settings), the SCLK clock I see on PA8 is at 77MHz, with sightly less than 50% duty cycle. I my acquisition code, I would like to start the DMA peripheral reading after the leading zeros, and synch it to the rising edge of the SCLK (read 1 bit from the 8 ADCs on each rising edge of SCLK). The ADCs start converting when I set their chip select CS to low:

//before assigning CS=0, we need to wait for the SCLK to be high, for synch reasons
while(SCLK==0);
CS_TO_LOW;
TIM1->RCR = 3; //Set the repetition value to skip 4 leading zeros
TIM1->DIER |= TIM_IT_Update; //enable update interrupt
while((TIM1->SR & TIM_IT_Update) == 0); //wait until update flag

SCLK in the code is a macro that queries the GPIOA->IDR to see if the output of PA8 is zero; CS_TO_LOW is a macro that lowers the value I output as chip select. They both work. In my mind, after the execution of the snippet above I should have skipped the four leading zeros, and be ready to have my DMA read the 8 bytes from the ADC bank. I am stumped as to how to proceed from there. Should I disable the TIM1 update interrupt, and have the DMA2 stream synch on the TIM1 clock? In that case, which one of the TIM1 ''events'' would cause the DMA2 transaction to synch with the rising edges of SCLK? (like TIM1_CH1? or what else?) I would then wait for an interrupt from the DMA2 telling me the 8 bytes have been transferred, and after 4 more cycles I would set CS to 1 thus ending a reading. OR Should I cause the TIM1 update interrupt to trigger on every period (assuming a period corresponds to the period of SCLK i see on PA8), and use that interrupt to cause a single DMA transfer (of 1 byte) from DMA2.Stream5.Channel6? I hope I provided enough information to get some advice. Best, Matteo
4 REPLIES 4
matteo
Associate II
Posted on April 02, 2014 at 02:25

By the way, this is my DMA config code

//USE registers from DMA2 and DMA2_Stream5, channel 6
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_6;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)GPIOA;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&dataBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 1; //or ACQUISITION_BUFFER_SIZE
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //do not increase the peripheral address
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Increase the target memory address
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
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(DMA2_Stream5, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream5, ENABLE);

I would think that lets me use either of the strategies described above (just have to change stream and channel number accordingly). Is that right?
Posted on April 02, 2014 at 02:43

Pretty much stuck with DMA2 based on the mem-to-mem requirement of &GPIO->IDR

If it where me I'd just burst in the entire 16 byte run, and be done.

Perhaps you can diagram the connectivity (schematic), and the waveforms (timing diagram) you need to generate?
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
matteo
Associate II
Posted on April 02, 2014 at 03:19

If it where me I'd just burst in the entire 16 byte run, and be done.

How would I go about doing that, exactly? Settings wise I mean. Would this work:

TIM1->RCR = 0;
TIM1->DIER |= TIM_IT_Update | 0x0100; 
while((DMA2->HISR&0x0800)==0); //wait for transfer complete
TIM1->DIER &= ~TIM_IT_Update; //disable update interrupt

And in the DMA initialization

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;

Since 16 bytes fills the whole FIFO, right?
matteo
Associate II
Posted on April 02, 2014 at 04:05

Here are my current DMA init settings:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_InitStructure.DMA_Channel = DMA_Channel_6;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)GPIOA;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&dataBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 16; //or ACQUISITION_BUFFER_SIZE //or number of bytes each shot
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //do not increase the peripheral address
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Increase the target memory address
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream5, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream5, ENABLE);

And in my read code:

while(SCLK==0);
CS_TO_LOW;
TIM1->RCR = 0; //repetition counter = 0
TIM1->DIER |= TIM_IT_Update | 0x0100;
while((DMA2->HISR & 0x0800) == 0);//wait for DMA transfer complete (all 16 bits)
TIM1->DIER &= ~TIM_IT_Update;
CS_TO_HIGH;

I would like the DMA to be done in 16 cycles, hence I would like to see CS remain low for as much. However: - on first transfer CS remains low for 27 cycles - on all other transfers CS remains low for 6 cycles Any guess at what might be causing this?