roboforum.ru

Технический форум по робототехнике.

STM8L (ADC -> DMA) проблема синхронизации

STM8L (ADC -> DMA) проблема синхронизации

executer » 10 апр 2014, 16:29

Сразу извиняюсь за параллельную тему, но на easyelectronics что то тихо в ветке.

Здравствуйте, уткнулся в проблему, не знаю куда копать. Первый проект на STM, и идеи кончились.
Задача: измерение напряжения на 4х аналоговых входах. 100х4 раз в секунду достаточно.
Проблема: Из-за опроса нескольких входов использую DMA. Данные идут, но отсутствует однозначность
Канал№х -> Ячейка[x].
Как бы отсутствие синхронизации в работе ADC и DMA.
Крайний вариант решения: Отключить DMA и запускать ADC однократно, по-канально. Каждый раз переконфигурируя на следующий канал и ожидая прерывания готовности результата. Т.е. симулировать DMA программно.

Плата Discovery со стекляшкой.
Код: Выделить всёРазвернуть
uint16_t recv_array[6];
...........................................................................

  // Adjust system clock prescaler
  CLK_CKDIVR = 0; // no system clock prescaler (16 MHz)
 
  // Configure timer 1
********** не важно, думаю. ********************

  // Configure timer 4
********** не важно, думаю. ********************

 
  // Configure DMA
  CLK->PCKENR2 |= CLK_PCKENR2_DMA1; //Тактирование
 
  DMA1_Channel0->CNBTR = sizeof(recv_array)-1; //Размер буфера
 
  DMA1_Channel0->CPARH = (ADC1_BASE+4)>>8; //Адрес регистра АЦП (старший байт)
  DMA1_Channel0->CPARL = (uint8_t)(ADC1_BASE+4); //Младший

  DMA1_Channel0->CM0ARH = (uint8_t)((uint16_t)(recv_array)>>8); //Адрес буфера
  DMA1_Channel0->CM0ARL = (uint8_t)(recv_array); 

  DMA1_Channel0->CSPR |= DMA_CSPR_16BM | DMA_CSPR_PL;  //Режим работы с 16и битными числами. | HighPriority
  DMA1_Channel0->CCR |= DMA_CCR_IDM | DMA_CCR_CE | DMA_CCR_TCIE | DMA_CCR_ARM; //Включаем канал и разрешаем прерывание
  DMA1->GCSR |= DMA_GCSR_GE; //Включаем DMA 

 
  // Coinfigure ADC
  CLK->PCKENR2 |= CLK_PCKENR2_ADC1; //Тактирование

  ADC1->CR1 |= ADC_CR1_ADON | ADC_CR1_CONT; //Пинаем АЦП, чтобы он проснулся + Continious
  ADC1->CR2 |= (uint8_t)0x02; // 16 cycles per samle period
  ADC1->CR3 |= (uint8_t)0x02; // 16 cycles per samle period
   
  ADC1->TRIGR[0] |= ADC_TRIGR1_TSON | (1<<0); //Подаем питание на датчик
  ADC1->TRIGR[2] |= (1<<2) | (1<<1) | (1<<0); // Disable triggers on inputs
  ADC1->TRIGR[3] |= (1<<7); // Disable triggers on inputs
  ADC1->SQR[0] |= (1<<5) | (1<<0); //Measure temperatire + ch24
  ADC1->SQR[2] |= (1<<2) | (1<<1) | (1<<0); //Measure ext channels 10,9,8
  ADC1->SQR[3] |= (1<<7); //Measure ext channel 7
 
  SomeDelay(); // let sensor to startup
 

  // Enable interrupts
  asm("RIM");
  ADC1->CR1 |= ADC_CR1_START; //Поехали!   
 
 
  while (1) //В цикле будем мигать светодиодом
  {
       
    PC_ODR_bit.ODR7 = 1; //Переключаем пин в высокий уровень - светодиод горит
    SomeDelay();  //Задержка в 0.5 сек   
    PC_ODR_bit.ODR7 = 0; //Пин в низкий уровень - светик тухнет
    SomeDelay();
  };
 
  return 0;
}

ISR(TIM1_OVF, TIM1_OVR_UIF_vector)
{
TIM1_SR1_bit.UIF = 0; //Сброс флага прерывания
};

ISR(TIM4_OVF, TIM4_UIF_vector)
{
TIM4_SR1_bit.UIF = 0; //Сброс флага прерывания
};

ISR(ADC_IRQ, COMP_EF1_vector)
{
  //Забираем результат
x=x; //ТУТ СТОИТ BREAKPOINT
//TIM4_SR1_bit.UIF = 0; //Сброс флага прерывания
};

ISR(DMA_IRQ, DMA1_CH0_TC_vector) //INTERRUPT_HANDLER(DMA_IRQ, 2)
{
DMA1_Channel0->CSPR &= ~DMA_CSPR_TCIF;
}



Прерывание вызывается только вот это. Все аналоговые входа на напряжении питания, измеряется только температурный датчик. При этом в "recv_array" находится вот что (последовательные breakpoint-ы):
Код: Выделить всёРазвернуть
4095  4095  4095  712_  712_  4095  ...
4095  4095  4095  4095  708_  4095  ...
4095  4095  4095  4095  4095  4095  ...
711_  4095  4095  4095  709_  4095  ...
4095  4095  4095  4095  4095  708_  ...
4095  4095  706_  708_  4095  4095  ...
"_" - это просто для форматирования в колонки добавил

А ожидались 7хх в одну строчку.
Подскажите, пожалуйста, куда копать. Перфекционист не разрешает симулировать DMA программно ((. Пока что.

ПС: Да, проверял что будет если снимать DMA_CCR_ARM, ADC_CR1_CONT, делать массив больше/меньше количества опрашиваемых каналов - все одно: входа один раз оцифровываются, данные в буфере, прерывания не вызываются и все...

Добавлено спустя 17 секунд:
Вчера весь вечер провертел, медитировал на регистры ADC и DMA, ясность не появилась.

Менял время выборки АЦП, картина не поменялась - значит все таки только ADC пинает DMA. Только както странно.

Пробовал выключить CONT в ADC и тыцать START в прерывании ADC_CR1, но он тогда опрашивает только первый по порядку канал (

В режиме отладки, нажав паузу, могу ли я по состоянию регистров какнибудь понять какой канал ADC сейчас оцифровывается? Чтобы хоть понять что он делает?

Еще совсем непонятно, в референце написано, что если не взвести CONT=1 и запустить ADC с несколькими выбранными каналами, то он оцифрует всю очередь (если выключить DMA - то останется результат только последнего канала), и после обработки последнего канала вызовет прерывание EOC. А у меня оцифровывается первый канал и ничего не происходит дальше. ЧЯДНТ?

Re: STM8L (ADC -> DMA) проблема синхронизации

dccharacter » 10 апр 2014, 19:16

А кто у тебя ДМА дергает? ADC или таймер? В смысле кто является источником DMA запроса?

Добавлено спустя 15 минут 1 секунду:
Не вижу следующих конфигураций:
1. Маппинга АДЦ на нулевой канал ДМА
2. Включение кругового режима (CIRC)
3. Включения режима ДМА Периферия->Память

Добавлено спустя 6 минут 34 секунды:
Посмотри на этот бит попристальнее: MINCDEC

Добавлено спустя 1 минуту 4 секунды:
И вообще на этот регистр. Такое чувство, что там сплошные грабли.

Re: STM8L (ADC -> DMA) проблема синхронизации

executer » 11 апр 2014, 01:25

DMA дергает ADC в циклическом режиме. DMA складывает результаты ADC в буферок. Канал 1 в [0], Канал 2 в [1] и т.д. Это моя цель.

По замечаниям:
1. Он по-дефолту замаплен на DMA0. Законфигил принудительно - ничего не поменялось.
2. CIRC включен (там в IAR биты иногда по-своему называются. CIRC = DMA_CCR_ARM "Autorelaod mode")
3. Это тоже в дефолте так, и буфер таки заполняется измерениями.

MINCDEC настроен на инкримент и в пошаговом режиме я вижу что буфер наполняется значениями от [0] до последней ячейки.

Причесаный код:

Код: Выделить всёРазвернуть
uint16_t result;   
uint16_t recv_array[32];

int main( void )
{
  GPIOC->DDR |= (1<<6); //Настраиваем 7й пин порта C на выход
  GPIOC->CR1 |= (1<<6); //Переключаем его в режим push-pull (это когда он может выдавать
 
  GPIOE->DDR |= (1<<6);
  GPIOE->CR1 |= (1<<6);
  GPIOE->DDR |= (1<<5);
  GPIOE->CR1 |= (1<<5);
  // Startup state
  GPIOE->ODR |= (1<<7);
  GPIOE->ODR &= ~(1<<6);

  // Adjust system clock prescaler
  CLK->CKDIVR = 0; // no system clock prescaler (16 MHz)
 
  // Configure DMA
  CLK->PCKENR2 |= CLK_PCKENR2_DMA1; //Тактирование
 
  DMA1_Channel0->CPARH = (ADC1_BASE+4)>>8; //Адрес регистра АЦП (старший байт)
  DMA1_Channel0->CPARL = (uint8_t)(ADC1_BASE+4); //Младший

  DMA1_Channel0->CM0ARH = (uint8_t)((uint16_t)(recv_array)>>8); //Адрес буфера
  DMA1_Channel0->CM0ARL = (uint8_t)(recv_array); 

  DMA1_Channel0->CNBTR = sizeof(recv_array)/2;//-1; //Размер буфера (in WORDS)
 
  DMA1_Channel0->CSPR = DMA_CSPR_16BM | DMA_CSPR_PL;  //Режим работы с 16и битными числами. | HighPriority
  DMA1_Channel0->CCR = DMA_CCR_CE | DMA_CCR_TCIE | DMA_CCR_IDM | DMA_CCR_ARM; //Включаем канал и разрешаем прерывание | increment | AutoReload
 
  SYSCFG->RMPCR1 = 0x0C; // TIM -> DMA.Ch4; ADC -> DMA.Ch0
 
  DMA1->GCSR |= DMA_GCSR_GE; //Включаем DMA 

 
  // Coinfigure ADC
  CLK->PCKENR2 |= CLK_PCKENR2_ADC1; //Тактирование

  ADC1->CR1 |= ADC_CR1_ADON | ADC_CR1_CONT;//  | ADC_CR1_OVERIE;//| ADC_CR1_EOCIE; //Пинаем АЦП, чтобы он проснулся + Continious
  ADC1->CR2 |= ADC_CR2_PRESC | (uint8_t)0x07; // 02; // low speed | 16 cycles per samle period
  ADC1->CR3 |= (uint8_t)0x07<<5; // 16 cycles per samle period
   
  ADC1->TRIGR[0] = 0x00;
  ADC1->TRIGR[2] = 0x00;
  ADC1->TRIGR[3] = 0x00;
  ADC1->SQR[0] = 0x00;
  ADC1->SQR[2] = 0x00;
  ADC1->SQR[3] |= 0xFF;(1<<7); //Measure ext channel 7
 

  // Enable interrupts
  asm("RIM");
  ADC1->CR1 |= ADC_CR1_START; //Поехали!   
 
  while (1)
  {
    result=result; // Dummy actions
    result=result;
  };
 
  return 0;
}

ISR(DMA_IRQ, DMA1_CH0_TC_vector) //INTERRUPT_HANDLER(DMA_IRQ, 2)
{
  DMA1_Channel0->CSPR &= ~DMA_CSPR_TCIF;
}


Добавлено спустя 4 минуты 12 секунд:
Маймуна веришвило, кажись оно работало. Если убрать все брекпоинты и запустить его на выполнение, то можно сколь угодно долго ждать, потом нажать Break - вуоля, буфер забит правильно. Дальше запустить и Break - все поехало..... По ходу это просто пауза-запуск контролера/ядра(?) все сбивали. ЙО МАЙО!!!

Re: STM8L (ADC -> DMA) проблема синхронизации

Aseris » 11 апр 2014, 11:48

executer писал(а):DMA дергает ADC
Так нельзя, нужно чтобы наоборот... DMA шустрее ADC..

Добавлено спустя 7 минут 17 секунд:
Хм... хотя из фразы изначальной не поймеш кто кого дергает..

Вобщем инициатор транзакции ADC должен быть.

Re: STM8L (ADC -> DMA) проблема синхронизации

executer » 11 апр 2014, 12:13

Да, ADC подготовил результат и тыцнул DMA чтобы тот забирал. Наоборот даже не получится сконфигурировать.
Странно, что во всех статьях с описанием быстрого старта с камнем нет примечания что режим отладки вносит свои коррективы в работу. Но это, видимо, актуально только для более-менее автономных узлов, как DMA.


Rambler\'s Top100 Mail.ru counter