High frequency streaming

Firmware/software/electronics/mechanics
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

Hi,

Thanks for your input.

I have managed to put the sampling working with a TIMx timer, specifically TIM6 by adapting the example I mentioned and everything is more or less working. I'll now add DMA to make it run smoothly in case of higher sampling frequencies.
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

Hi everyone,

In the past week I have been working on DMA and it's not going that well... Is there anyone here that knows how to do it properly? I'll explain my problem...

I want to read an analog value and that is working, I don't think that it is implemented the most efficient way, but it works for now. After this I want to use DMA to read 19 values, one at the time, from the ADC1 and then send an IRQ to handle the data.

This is the code for setting up the DMA:

Code: Select all

#define DMA_Str DMA2_Stream4
#define DMA_IRQ DMA2_Stream4_IRQn

void DMA_init(volatile uint16_t *ADCConvertedValue)
{
  DMA_InitTypeDef       DMA_InitStructure;
  DMA_StructInit(&DMA_InitStructure);

  //==Configure DMA2 - Stream 4
  DMA_DeInit(DMA_Str);  //Set DMA registers to default values
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //Source address
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) ADCConvertedValue; //Destination address
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = 19; //Buffer size
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //source size - 16bit
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // destination size = 16b
  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(DMA_Str, &DMA_InitStructure); //Initialize the DMA

  DMA_Cmd(DMA_Str, ENABLE); //Enable the DMA2 - Stream 4
  DMA_ITConfig(DMA_Str, DMA_IT_TC, ENABLE);

  //Enable DMA1 channel IRQ Channel
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = DMA_IRQ;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
This is the code related to ADC1 and DMA:

Code: Select all

ADC_RegularChannelConfig(ADC_NUM, ADC_CHANNEL, 1, ADC_SampleTime_144Cycles);
ADC_DMACmd(ADC_NUM, ENABLE); //Enable ADC1 DMA
ADC_DMARequestAfterLastTransferCmd(ADC_NUM, ENABLE);
ADC_Cmd(ADC_NUM, ENABLE);
ADC_start();
And to handle the data:

Code: Select all

void DMA2_Stream4_IRQHandler(void)
{
  if (DMA_GetITStatus(DMA2_Stream4, DMA_IT_TC)) {
    // My code
    DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TC);
  }
}
When I flash this into the Crazyflie it crashes (the 2 lights stay blue and doesn't do the normal boot led sequence). I have been looking around and when I change the DMA_Channel to DMA_Channel1 for example it doesn't crash, but I have to use that because of the mapping.

Thanks in advance.
tobias
Bitcraze
Posts: 2339
Joined: Mon Jan 28, 2013 7:17 pm
Location: Sweden

Re: High frequency streaming

Post by tobias »

Hi,

Without doing any detailed analysis it looks OK to me and the resources does not seem occupied by something else. Could it be that the code ends up in an endless looping interrupt? If you have the code at github I can do a quick SWD debug of it.
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

Hi,

Sorry it took me so long. I wasn't working on github but I am now. If you have the time please take a look at the micdeck files here. I think the timer and the ADC can be connected some other way, I still haven't figured it out, but that part I think is working only when I turn on DMA the Crazyflie crashes.

Thank you

Edit: I added the python script I'm using to unpack and display the data to a new repository

Edit2: I just realized that I spent a bunch of time on the internet looking how to work with timers and DMA when I could just have looked into ledring12 files... And I realized that I'm not setting the .usedGpio and .usedPeriph well.
tobias
Bitcraze
Posts: 2339
Joined: Mon Jan 28, 2013 7:17 pm
Location: Sweden

Re: High frequency streaming

Post by tobias »

I quickly tested you code and as I suspected it gets stuck in an endless interrupt, namely the DMA2 interrupt. Something more (flag) needs to be cleared or interrupt inactivated. The half transfer flag was set I could see.

I see that you use the timer interrupt to trigger the DMA. You can set this up directly so the timer will trigger the ADC and the ADC will trigger the DMA. You can then use the half transfer and transfer complete interrupt with a circular buffer to constantly get ADC values and minimize CPU load.

Hope the above helps!
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

Hi,

Thank you for the tips. I had some stuff wrong.

Firstly I forgot to change the RCC_AHB1Periph_DMA2 to enable, but worst than that was an error in clearing the interruption bit. I had DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TC); instead of DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TCIF4); and since those are unsigned ints the compiler didn't complained.

Regarding the ADC and the timer, I finally took the time to see how to do it properly and it wasn't that difficult. I'm now using the TIM3 since I couldn't use TIM6. After this the Crazyflie started crashing randomly (2 red leds on). I thought it was because I wasn't implementing the last step (using the half transfer) but after implementing that the crashes continue, some times it takes 1 sec sometimes 10 sec.

I'll continue to push on this and I'll let you know how it turned out. I guess STM32F4 has a thing called double buffering which is similar to use HT and TC, if I can't find the source of the crashes I'll give this a try.

Thanks again!
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

Hi again,

I found out why the Crazyflie was crashing, the priority for the DMA NVIC IRQ was too high. I guess there was some functionality of the FreeRTOS that didn't had the time to run.

So I have been looking into optimization since I'm already having problems with what I think is CPU load and I found out that the function I have been using to send packets (crtpSendPacket) uses a function (xQueueSend) which "...must not be called from an interrupt service routine." and queues the packets by copy and not by reference. Due to this I created a new crtpSendPacketISR function which uses xQueueSendFromISR instead, even if this funtion queues packets by copy too.

Code: Select all

int crtpSendPacketISR(CRTPPacket *p)
  {
    ASSERT(p);
    ASSERT(p->size <= CRTP_MAX_DATA_SIZE);

    return xQueueSendFromISR(txQueue, p, 0);
  }
It works, but does this sounds ok to you? Do you have any other suggestion on how to make the sending of packets more efficient, maybe creating a function which queues the packets by reference?

Thank you

Edit:
This is my DMA IRQ.

Code: Select all

void packsData(uint8_t section) {
  p.data[0] = packetCount;
  packetCount++;
  ptr = 1;
  byteHalf = 0;
  i = SAMPLES_PER_PACKET * section;
  end = SAMPLES_PER_PACKET * (section + 1);
  do{
      sample = ADCConvertedValue[i];
      if(byteHalf){
	  p.data[ptr] |= (uint8_t)(sample >> 8);
	  ptr++;
	  p.data[ptr] = (uint8_t)(sample);
	  ptr++;
	  byteHalf = 0;
      }else{
	  p.data[ptr] = (uint8_t)(sample >> 4);
	  ptr++;
	  p.data[ptr] = (uint8_t)(sample << 4);
	  byteHalf = 1;
      }
      i++;
  }while(i != end);
  crtpSendPacketISR(&p);
  memset(&(p.data[1]), 0, DATA_BYTES);
}
tobias
Bitcraze
Posts: 2339
Joined: Mon Jan 28, 2013 7:17 pm
Location: Sweden

Re: High frequency streaming

Post by tobias »

Good findings!

It might be efficient enough now but I think you can make it much more efficient by letting the ADC and DMA do all the work. You could setup a 60 byte buffer and have the DMA work in circular mode and use HT and TC interrupts to just send the buffer that is not in use for the moment. The DMA and ADC can be setup to handle the 12bit and put it in words so you don't have to do that either.
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

I'm already doing that. That function is called in the DMA interrupt:

Code: Select all

void DMA2_Stream4_IRQHandler(void)
{
  if (DMA_GetITStatus(DMA2_Stream4, DMA_IT_HTIF4)) {
    packsData(0);
    DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_HTIF4);
  }
  if (DMA_GetITStatus(DMA2_Stream4, DMA_IT_TCIF4)) {
    packsData(1);
    DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TCIF4);
  }
}
Sorry I forgot to mention.
david
Beginner
Posts: 22
Joined: Wed Aug 30, 2017 2:10 pm

Re: High frequency streaming

Post by david »

Btw I'm experiencing something I believe is some kind of interference from the packet sending. Did you ever seen something like this on readings from the adc? For some reason this noise almost disappears when the ground of the Crazyflie is connected to my oscilloscope...
noise.png
Post Reply