Миландр

Ключевым подразделением нашей компании является Центр Проектирования интегральных микросхем
Текущее время: 2018-сен-21 08:32

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 104 ]  На страницу Пред.  1 ... 3, 4, 5, 6, 7
Автор Сообщение
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2017-май-12 17:05 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 157
Откуда: ПКК "Миландр"
Да, я имел ввиду в таймерном прерывании брать конфигурацию АЦП из кольцевого буфера меньшей размерности и прописывать регистр. Это конечно нагружает ядро, в отличие от DMA, но не значительно.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2017-май-26 13:07 
Не в сети

Зарегистрирован: 2009-июл-21 14:13
Сообщения: 1141
Откуда: Тула
Vasiliy писал(а):
По поводу проверки DMA:
2 - При работе с регистрами периферийных устройств, есть некоторые нюансы реализации. Все-таки это не ячейки памяти, а устройства в адресном пространстве. Пересылка байта в регистр ЦАП происходит некорректно, поскольку разработчики не закладывались на такой вариант. Вот если взять отдельный 12-битный ЦАП возникнет ли идея писать туда байтами, старшими, младшими? Поэтому рекомендация - при работе с регистрами периферийных устройств обращаться к ним с соразмерной разрядностью.

В случае с ЦАП посредством DMA работают 16-битные и 32-битные пересылки.

Спасибо, за обращение, мы внесем в спецификацию уточнения про работу DMA с внешними регистрами. К сожалению, данный аспект сейчас не раскрыт.

Было бы здорово добавить в модуль ЦАП ещё один регистр, восьмибитный DACx_DATA8, для записи восьмибитных данных в старшие разряды ЦАП - это полностью решило бы все вопросы. Можно взять моё предложение для реализаций ЦАП в следующих проектах МК.

_________________
сочувствующий…


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2017-дек-29 22:30 
Не в сети
Аватара пользователя

Зарегистрирован: 2013-июн-21 15:27
Сообщения: 91
Откуда: Новосибирск
Цитата:
Если dma_sreq[C] установлен в 1, то контроллер устанавливает dma_done[C] в 1 при условии что dma_waitonreq[C] в 1 и chnl_useburst_set[C] в состоянии 0. Это позволяет контроллеру показать центральному процессору запрос готовности, даже если канал выключен (запрещен).

imho это грубейшая ошибка разработки! Если канал ПДП запрещён, то контроллер не только не должен, но и НЕ ИМЕЕТ ПРАВА сообщать процессору о запросе по этому каналу ТАКИМ ОБРАЗОМ! Он должен всего лишь выставить флаг запроса по этому каналу в WAITONREQ_STATUS, и когда (или если) процессору станет интересно, что происходит в выключенных каналах, он может сравнить содержимое WAITONREQ_STATUS и CHNL_ENABLE_SET и, если сочтёт необходимым, разрешить соответствующий канал.
А представьте себе, что будет, если так же реализовать логику NVIC: прерывание по какому-то IRQ запрещено, но по этому IRQ появляется запрос и чтобы сообщить об этом процессору, NVIC вызывает прерывание. Ну бред же ж!
А вот в регистре STATUS хорошо бы ввести поля "Номер канала" и "Ошибка", чтобы в обработчике прерывания от DMA можно было понять причину прерывания. Свободного места для этого там вполне достаточно.

_________________
Странник


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июн-13 22:46 
Не в сети
Аватара пользователя

Зарегистрирован: 2011-авг-21 18:55
Сообщения: 253
У кого-нибудь есть вменяемый код с прерыванием DMA и работой c блоком ADC?

В нижеприведённом коде по прерыванию таймера запускаются ADC1 (для измерения каналов ADC_Ch0-ADC_Ch2 пачками по ADC_BufSize измерений) и ADC2 (для измерения каналов ADC_Ch3-ADC_Ch5 пачками по ADC_BufSize измерений). В прерывании DMA происходит перезапуск измерений по очередным каналам. Проблема в том, что программа не может попасть в основной цикл в файле main.c и выполнить процедуру MeasProc (), как я понял из-за постоянного вызова прерывания DMA, хотя в DMA_Setup всё настроено с учётом аппаратных ошибок и т.п. Помогает лишь запрещение прерывания DMA (закомментированные строки в DMA_IRQHandler) и разрешение прерывания DMA (закомментированные строка в Timer1_IRQHandler).

Вопрос, что ещё не учтено и как правильно решить вышеуказанную задачу??

Открыть
Код:
adc.h

#define ADC_ChSize            6
#define ADC_BufSize            10

#define ADC1_ChBegin         0
#define ADC1_ChEnd            3
#define ADC2_ChBegin         3
#define ADC2_ChEnd            6

adc.c

unsigned short ADC_Buf[ADC_ChSize][ADC_BufSize];

unsigned char ADC1_ChNum, ADC2_ChNum;

bool ADC1_Ready = false, ADC2_Ready = false;

void ADC_Setup (void)
{
   MDR_RST_CLK->ADC_MCO_CLOCK_b.ADC_CLK_EN = false;
   
   MDR_RST_CLK->PER_CLOCK_b.PORTD = true;
   
   MDR_PORTD->ANALOG &=
      ~((1 << ADC_Ch0) | (1 << ADC_Ch1) | (1 << ADC_Ch2) |
      (1 << ADC_Ch3) | (1 << ADC_Ch4) | (1 << ADC_Ch5));
   
   MDR_PORTD->PULL &=
      ~((1 << ADC_Ch0) | (1 << (ADC_Ch0+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch1) | (1 << (ADC_Ch1+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch2) | (1 << (ADC_Ch2+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch3) | (1 << (ADC_Ch3+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch4) | (1 << (ADC_Ch4+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch5) | (1 << (ADC_Ch5+PORT_PULL_UP_Pos)));
   
   MDR_RST_CLK->PER_CLOCK_b.ADC = true;
   
   MDR_ADC->ADC1_CFG =
      (1 << ADC1_CFG_REG_ADON_Pos) |
      (0 << ADC1_CFG_REG_GO_Pos) |
      (0 << ADC1_CFG_REG_CLKS_Pos) |
      (0 << ADC1_CFG_REG_SAMPLE_Pos) |
      (ADC_Ch0 << ADC1_CFG_REG_CHS_Pos) |
      (0 << ADC1_CFG_REG_CHCH_Pos) |
      (0 << ADC1_CFG_REG_RNGC_Pos) |
      (0 << ADC1_CFG_M_REF_Pos) |
      (ADC_CFG_REG_DIVCLK_HCLK_DIV_8 << ADC1_CFG_REG_DIVCLK_Pos) |
      (0 << ADC1_CFG_SYNC_CONVER_Pos) |
      (0 << ADC1_CFG_TS_EN_Pos) |
      (0 << ADC1_CFG_TS_BUF_EN_Pos) |
      (0 << ADC1_CFG_SEL_TS_Pos) |
      (0 << ADC1_CFG_SEL_VREF_Pos) |
      (0 << ADC1_CFG_TR_Pos) |
      (ADC1_CFG_DELAY_GO_8_CPU_CLK << ADC1_CFG_DELAY_GO_Pos) |
      (ADC1_CFG_DELAY_ADC_1_CPU_CLK << ADC1_CFG_DELAY_ADC_Pos);
      
   MDR_ADC->ADC2_CFG =
      (1 << ADC2_CFG_REG_ADON_Pos) |
      (0 << ADC2_CFG_REG_GO_Pos) |
      (0 << ADC2_CFG_REG_CLKS_Pos) |
      (0 << ADC2_CFG_REG_SAMPLE_Pos) |
      (ADC_Ch3 << ADC2_CFG_REG_CHS_Pos) |
      (0 << ADC2_CFG_REG_CHCH_Pos) |
      (0 << ADC2_CFG_REG_RNGC_Pos) |
      (0 << ADC2_CFG_M_REF_Pos) |
      (ADC_CFG_REG_DIVCLK_HCLK_DIV_8 << ADC2_CFG_REG_DIVCLK_Pos) |
      (0 << ADC2_CFG_ADC1_OP_Pos) |
      (0 << ADC2_CFG_ADC2_OP_Pos) |
      (ADC2_CFG_DELAY_GO_0_CPU_CLK << ADC2_CFG_DELAY_GO_Pos);
}

dma.h

#define PRI_DMA_CH_ADC_CFG_CTRL \
   ((DMA_CH_CTRL_CYCLE_CTRL_BASIC << DMA_CH_CTRL_CYCLE_CTRL_Pos) | \
   (0 << DMA_CH_CTRL_NEXT_USEBURST_Pos) | \
   ((ADC_BufSize-1) << DMA_CH_CTRL_N_MINUS_1_Pos) | \
   (DMA_CH_CTRL_R_POWER_1_TRANS << DMA_CH_CTRL_R_POWER_Pos) | \
   (0 << DMA_CH_CTRL_SRC_PROT_CTRL_Pos) | \
   (0 << DMA_CH_CTRL_DST_PROT_CTRL_Pos) | \
   (DMA_CH_CTRL_SIZE_2_BYTES << DMA_CH_CTRL_SRC_SIZE_Pos) | \
   (DMA_CH_CTRL_INC_NO << DMA_CH_CTRL_SRC_INC_Pos) | \
   (DMA_CH_CTRL_SIZE_2_BYTES << DMA_CH_CTRL_DST_SIZE_Pos) | \
   (DMA_CH_CTRL_INC_2_BYTES << DMA_CH_CTRL_DST_INC_Pos))

dma.c

__align(DMA_CH_CFG_ALIGN) DMA_CH_CFG_TypeDef PriDMA_ChCfg[DMA_CH_TABLE_SIZE];

void DMA_IRQHandler (void)
{
//   if (ADC1_Ready && ADC2_Ready)
//   {
//      NVIC_DisableIRQ (DMA_IRQn);
//      NVIC_ClearPendingIRQ (DMA_IRQn);
//      
//      return;
//   }
   
   if (PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].CTRL_b.N_MINUS_1 == 0)
   {      
      MDR_ADC->ADC1_CFG_b.CFG_REG_SAMPLE = false;
      
      ADC1_ChNum++;
      
      if (ADC1_ChNum == ADC1_ChEnd)
      {
         ADC1_ChNum = ADC1_ChBegin;
         
         ADC1_Ready = true;
      }
      
      MDR_ADC->ADC1_CFG_b.CFG_REG_CHS = ADC1_ChNum;
      
      PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC1_ChNum][ADC_BufSize-1]);
      PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
      
      if (ADC1_Ready == false)
      {
         MDR_DMA->CHNL_ENABLE_SET_b.ADC1_EC = true;
         MDR_ADC->ADC1_CFG_b.CFG_REG_SAMPLE = true;
      }
   }
   
   if (PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].CTRL_b.N_MINUS_1 == 0)
   {
      MDR_ADC->ADC2_CFG_b.CFG_REG_SAMPLE = false;
      
      ADC2_ChNum++;
      
      if (ADC2_ChNum == ADC2_ChEnd)
      {
         ADC2_ChNum = ADC2_ChBegin;
         
         ADC2_Ready = true;
      }
      
      MDR_ADC->ADC2_CFG_b.CFG_REG_CHS = ADC2_ChNum;
      
      PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC2_ChNum][ADC_BufSize-1]);
      PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
      
      if (ADC2_Ready == false)
      {
         MDR_DMA->CHNL_ENABLE_SET_b.ADC2_EC = true;
         MDR_ADC->ADC2_CFG_b.CFG_REG_SAMPLE = true;
      }
   }
}

void DMA_Setup (void)
{
   MDR_RST_CLK->PER_CLOCK |=
      ((RST_CLK_PER_CLOCK_PCLK_EN_DMA |
      RST_CLK_PER_CLOCK_PCLK_EN_SPI1 |
      RST_CLK_PER_CLOCK_PCLK_EN_SPI2));
   
   NVIC_ClearPendingIRQ (DMA_IRQn);
   
   MDR_RST_CLK->PER_CLOCK &=
      ~(RST_CLK_PER_CLOCK_PCLK_EN_SPI1 |
      RST_CLK_PER_CLOCK_PCLK_EN_SPI2);
   
   MDR_DMA->CFG =
      (0 << DMA_CFG_MASTER_ENABLE_Pos) |
      (0 << DMA_CFG_CHNL_PROT_CTRL_Pos);
   
  MDR_DMA->CHNL_REQ_MASK_SET = 0xFFFFFFFF;
   
   MDR_DMA->CHNL_SW_REQUEST = 0x00000000;
   
  MDR_DMA->CHNL_PRI_ALT_CLR = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
  MDR_DMA->CHNL_PRIORITY_CLR = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
   
   MDR_DMA->CHNL_ENABLE_CLR = 0xFFFFFFFF;
   
   MDR_DMA->CHNL_REQ_MASK_CLR = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
   MDR_DMA->CHNL_USEBURST_CLR = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
   
  MDR_DMA->ERR_CLR_b.ERR_CLR = true;
   
   ADC1_ChNum = ADC1_ChBegin;

   PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].SRC_END_ADDR = (unsigned int)(&(MDR_ADC->ADC1_RESULT));
   PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC1_ChNum][ADC_BufSize-1]);
   PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
      
   ADC2_ChNum = ADC2_ChBegin;
   
   PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].SRC_END_ADDR = (unsigned int)(&(MDR_ADC->ADC2_RESULT));
   PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC2_ChNum][ADC_BufSize-1]);
   PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
   
   MDR_DMA->CTRL_BASE_PTR = (unsigned int)&PriDMA_ChCfg;

   MDR_DMA->CFG_b.MASTER_ENABLE = true;
}

timer.c

void Timer1_IRQHandler (void)
{
   if (MDR_TIMER1->STATUS_b.CNT_ARR_EVENT)
   {
      MDR_TIMER1->STATUS_b.CNT_ARR_EVENT = false;
      
      if ((ADC1_Ready == false) && (ADC2_Ready == false))
      {
//         NVIC_EnableIRQ (DMA_IRQn);
         
         MDR_DMA->CHNL_ENABLE_SET = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
         
         MDR_ADC->ADC1_CFG_b.CFG_REG_SAMPLE = true;
         MDR_ADC->ADC2_CFG_b.CFG_REG_SAMPLE = true;
      }
   }
}

main.c

int main (void)
{
   ...
   NVIC_SetPriority (Timer1_IRQn, 0);
   NVIC_SetPriority (DMA_IRQn, 1);
   ...
   DMA_Setup ();
   ADC_Setup ();
   TimerSetup ();
   ...
   NVIC_EnableIRQ (Timer1_IRQn);
   NVIC_EnableIRQ (DMA_IRQn);
   
   MDR_TIMER1->CNTRL_b.CNT_EN = true;
   
   while (true)
   {
      if (ADC1_Ready && ADC2_Ready)
      {
         MeasProc ();
         
         ADC1_Ready = false;
         ADC2_Ready = false;
      }
   }
}
Закрыть

_________________
Hack the Planet!


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июн-14 11:10 
Не в сети

Зарегистрирован: 2010-сен-21 12:57
Сообщения: 666
Откуда: г. Санкт-Петербург
ответил ЛС


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июн-14 12:27 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 157
Откуда: ПКК "Миландр"
Не знаю насколько подходит к задаче, но набросал примере попроще (Во вложении).

Из особенностей:
1 - Нет отдельного регистра отключающего запросы от АЦП к DMA. Как например в SSP. Поэтому запросы к DMA отключил через остановку непрерывных измерений - выкл бит Cfg_Reg_Sample.
2 - Пока не вычитанное слово лежит в MDR_ADC->ADC1_RESULT запрос к DMA будет генерироваться, поэтому необходимо опустошить регистр результата после отключения преобразований. (Но вероятно может случиться так, что АЦП домеряет результат когда мы уже опустошим Result.)
3 - На сколько понял из описания бит Go выставляется автоматически при работе в режиме sample, поэтому его тоже стоит погасить.

Код:
void DMA_IRQHandler(void)
{
  //  Останавливаем непрерывное измерение АЦП
  MDR_ADC->ADC1_CFG &= ~ADC1_CFG_REG_SAMPLE;
  MDR_ADC->ADC1_CFG &= ~ADC1_CFG_REG_GO;
  //  Вычитываем данные, чтобы снять запрос sreq к DMA 
  ADC1_GetResult();
 
  completedIRQ = 1;
 
  NVIC_ClearPendingIRQ (DMA_IRQn);
}


При запуске следующего цикла DMA необходимо восстановить управляющее слово в канале DMA и взвести бит в регистре Enable - DMA_Cmd(DMA_CHANNEL, ENABLE);
После этого запустить следующий цикл измерения АЦП - бит ADC1_CFG_REG_SAMPLE.

ОФФТОП:
Некоторая дополнительная информация про DMA доступна тут - https://startmilandr.ru/doku.php/prog:dma:dma_intro
Там же есть некоторые примеры.


Вложения:
Комментарий к файлу: Пример DMA c ADC на 1986ВЕ92У
DMA_ADC.zip [74.48 КБ]
Скачиваний: 35

_________________
Отдел технической поддержки support@milandr.ru
Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июн-14 15:04 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 73
Можете и мои примеры (проекты lab3_4 и lab3_5) глянуть: https://cloud.mail.ru/public/BV1X/iS1hAif1e
Описание здесь: https://edu.milandr.ru/upload/iblock/cb ... 8be0e8.pdf
Вроде, всё ровно работает.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июн-14 21:41 
Не в сети
Аватара пользователя

Зарегистрирован: 2011-авг-21 18:55
Сообщения: 253
Всем Большое Спасибо за Информацию!! Сегодня сам тоже в ходе экспериментов получил вроде работающий вариант - ключевой момент - работа с регистрами MDR_DMA->CHNL_USEBURST :
Открыть
Код:
adc.h

#define ADC_ChSize            6
#define ADC_BufSize            10

#define ADC1_ChBegin         0
#define ADC1_ChEnd            3
#define ADC2_ChBegin         3
#define ADC2_ChEnd            6

adc.c

unsigned short ADC_Buf[ADC_ChSize][ADC_BufSize];

unsigned char ADC1_ChNum, ADC2_ChNum;

bool ADC1_Ready = false, ADC2_Ready = false;

void ADC_Setup (void)
{
   MDR_RST_CLK->ADC_MCO_CLOCK_b.ADC_CLK_EN = false;
   
   MDR_RST_CLK->PER_CLOCK_b.PORTD = true;
   
   MDR_PORTD->ANALOG &=
      ~((1 << ADC_Ch0) | (1 << ADC_Ch1) | (1 << ADC_Ch2) |
      (1 << ADC_Ch3) | (1 << ADC_Ch4) | (1 << ADC_Ch5));
   
   MDR_PORTD->PULL &=
      ~((1 << ADC_Ch0) | (1 << (ADC_Ch0+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch1) | (1 << (ADC_Ch1+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch2) | (1 << (ADC_Ch2+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch3) | (1 << (ADC_Ch3+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch4) | (1 << (ADC_Ch4+PORT_PULL_UP_Pos)) |
      (1 << ADC_Ch5) | (1 << (ADC_Ch5+PORT_PULL_UP_Pos)));
   
   MDR_RST_CLK->PER_CLOCK_b.ADC = true;
   
   MDR_ADC->ADC1_CFG =
      (1 << ADC1_CFG_REG_ADON_Pos) |
      (0 << ADC1_CFG_REG_GO_Pos) |
      (0 << ADC1_CFG_REG_CLKS_Pos) |
      (0 << ADC1_CFG_REG_SAMPLE_Pos) |
      (ADC_Ch0 << ADC1_CFG_REG_CHS_Pos) |
      (0 << ADC1_CFG_REG_CHCH_Pos) |
      (0 << ADC1_CFG_REG_RNGC_Pos) |
      (0 << ADC1_CFG_M_REF_Pos) |
      (ADC_CFG_REG_DIVCLK_HCLK_DIV_8 << ADC1_CFG_REG_DIVCLK_Pos) |
      (0 << ADC1_CFG_SYNC_CONVER_Pos) |
      (0 << ADC1_CFG_TS_EN_Pos) |
      (0 << ADC1_CFG_TS_BUF_EN_Pos) |
      (0 << ADC1_CFG_SEL_TS_Pos) |
      (0 << ADC1_CFG_SEL_VREF_Pos) |
      (0 << ADC1_CFG_TR_Pos) |
      (ADC1_CFG_DELAY_GO_8_CPU_CLK << ADC1_CFG_DELAY_GO_Pos) |
      (ADC1_CFG_DELAY_ADC_1_CPU_CLK << ADC1_CFG_DELAY_ADC_Pos);
      
   MDR_ADC->ADC2_CFG =
      (1 << ADC2_CFG_REG_ADON_Pos) |
      (0 << ADC2_CFG_REG_GO_Pos) |
      (0 << ADC2_CFG_REG_CLKS_Pos) |
      (0 << ADC2_CFG_REG_SAMPLE_Pos) |
      (ADC_Ch3 << ADC2_CFG_REG_CHS_Pos) |
      (0 << ADC2_CFG_REG_CHCH_Pos) |
      (0 << ADC2_CFG_REG_RNGC_Pos) |
      (0 << ADC2_CFG_M_REF_Pos) |
      (ADC_CFG_REG_DIVCLK_HCLK_DIV_8 << ADC2_CFG_REG_DIVCLK_Pos) |
      (0 << ADC2_CFG_ADC1_OP_Pos) |
      (0 << ADC2_CFG_ADC2_OP_Pos) |
      (ADC2_CFG_DELAY_GO_0_CPU_CLK << ADC2_CFG_DELAY_GO_Pos);
}

dma.h

#define PRI_DMA_CH_ADC_CFG_CTRL \
   ((DMA_CH_CTRL_CYCLE_CTRL_BASIC << DMA_CH_CTRL_CYCLE_CTRL_Pos) | \
   (0 << DMA_CH_CTRL_NEXT_USEBURST_Pos) | \
   ((ADC_BufSize-1) << DMA_CH_CTRL_N_MINUS_1_Pos) | \
   (DMA_CH_CTRL_R_POWER_1_TRANS << DMA_CH_CTRL_R_POWER_Pos) | \
   (0 << DMA_CH_CTRL_SRC_PROT_CTRL_Pos) | \
   (0 << DMA_CH_CTRL_DST_PROT_CTRL_Pos) | \
   (DMA_CH_CTRL_SIZE_2_BYTES << DMA_CH_CTRL_SRC_SIZE_Pos) | \
   (DMA_CH_CTRL_INC_NO << DMA_CH_CTRL_SRC_INC_Pos) | \
   (DMA_CH_CTRL_SIZE_2_BYTES << DMA_CH_CTRL_DST_SIZE_Pos) | \
   (DMA_CH_CTRL_INC_2_BYTES << DMA_CH_CTRL_DST_INC_Pos))

dma.c

__align(DMA_CH_CFG_ALIGN) DMA_CH_CFG_TypeDef PriDMA_ChCfg[DMA_CH_TABLE_SIZE];

void DMA_IRQHandler (void)
{
   if (PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].CTRL_b.N_MINUS_1 == 0)
   {
      MDR_ADC->ADC1_CFG_b.CFG_REG_SAMPLE = false;
      
      ADC1_ChNum++;
      
      if (ADC1_ChNum == ADC1_ChEnd)
      {
         MDR_DMA->CHNL_USEBURST_SET_b.ADC1_EC = true;
         
         ADC1_ChNum = ADC1_ChBegin;
         
         ADC1_Ready = true;
      }
      
      MDR_ADC->ADC1_CFG_b.CFG_REG_CHS = ADC1_ChNum;
      
      PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC1_ChNum][ADC_BufSize-1]);
      PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
      
      if (ADC1_Ready == false)
      {
         MDR_DMA->CHNL_ENABLE_SET_b.ADC1_EC = true;
         MDR_ADC->ADC1_CFG_b.CFG_REG_SAMPLE = true;
      }
   }
   
   if (PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].CTRL_b.N_MINUS_1 == 0)
   {
      MDR_ADC->ADC2_CFG_b.CFG_REG_SAMPLE = false;
      
      ADC2_ChNum++;
      
      if (ADC2_ChNum == ADC2_ChEnd)
      {
         MDR_DMA->CHNL_USEBURST_SET_b.ADC2_EC = true;
         
         ADC2_ChNum = ADC2_ChBegin;
         
         ADC2_Ready = true;
      }
      
      MDR_ADC->ADC2_CFG_b.CFG_REG_CHS = ADC2_ChNum;
      
      PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC2_ChNum][ADC_BufSize-1]);
      PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
      
      if (ADC2_Ready == false)
      {
         MDR_DMA->CHNL_ENABLE_SET_b.ADC2_EC = true;
         MDR_ADC->ADC2_CFG_b.CFG_REG_SAMPLE = true;
      }
   }
}

void DMA_Setup (void)
{
   memset (PriDMA_ChCfg, 0, sizeof(PriDMA_ChCfg));

   MDR_RST_CLK->PER_CLOCK |=
      ((RST_CLK_PER_CLOCK_PCLK_EN_DMA |
      RST_CLK_PER_CLOCK_PCLK_EN_SPI1 |
      RST_CLK_PER_CLOCK_PCLK_EN_SPI2));
   
   NVIC_ClearPendingIRQ (DMA_IRQn);
   
   MDR_RST_CLK->PER_CLOCK &=
      ~(RST_CLK_PER_CLOCK_PCLK_EN_SPI1 |
      RST_CLK_PER_CLOCK_PCLK_EN_SPI2);
   
   MDR_DMA->CFG =
      (0 << DMA_CFG_MASTER_ENABLE_Pos) |
      (0 << DMA_CFG_CHNL_PROT_CTRL_Pos);
   
   MDR_DMA->CHNL_REQ_MASK_SET = 0xFFFFFFFF;
   MDR_DMA->CHNL_ENABLE_CLR = 0xFFFFFFFF;
   MDR_DMA->CHNL_USEBURST_SET = 0xFFFFFFFF;
   MDR_DMA->CHNL_PRI_ALT_CLR = 0xFFFFFFFF;
   MDR_DMA->CHNL_PRIORITY_CLR = 0xFFFFFFFF;
   MDR_DMA->CHNL_SW_REQUEST = 0x00000000;
   
   MDR_DMA->CHNL_REQ_MASK_CLR = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
   
     MDR_DMA->ERR_CLR_b.ERR_CLR = true;
   
   ADC1_ChNum = ADC1_ChBegin;

   PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].SRC_END_ADDR = (unsigned int)(&(MDR_ADC->ADC1_RESULT));
   PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC1_ChNum][ADC_BufSize-1]);
   PriDMA_ChCfg[DMA_CH_ADC1_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
      
   ADC2_ChNum = ADC2_ChBegin;
   
   PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].SRC_END_ADDR = (unsigned int)(&(MDR_ADC->ADC2_RESULT));
   PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].DST_END_ADDR = (unsigned int)(&ADC_Buf[ADC2_ChNum][ADC_BufSize-1]);
   PriDMA_ChCfg[DMA_CH_ADC2_EC_Pos].CTRL = PRI_DMA_CH_ADC_CFG_CTRL;
   
   MDR_DMA->CTRL_BASE_PTR = (unsigned int)&PriDMA_ChCfg;

   MDR_DMA->CFG_b.MASTER_ENABLE = true;
}

timer.c

void Timer1_IRQHandler (void)
{
   if (MDR_TIMER1->STATUS_b.CNT_ARR_EVENT)
   {
      MDR_TIMER1->STATUS_b.CNT_ARR_EVENT = false;
      
      if ((ADC1_Ready == false) && (ADC2_Ready == false))
      {
         NVIC_ClearPendingIRQ (DMA_IRQn);
         
         MDR_DMA->CHNL_USEBURST_CLR = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
         MDR_DMA->CHNL_ENABLE_SET = DMA_CH_ADC1_EC | DMA_CH_ADC2_EC;
         
         MDR_ADC->ADC1_CFG_b.CFG_REG_SAMPLE = true;
         MDR_ADC->ADC2_CFG_b.CFG_REG_SAMPLE = true;
      }
   }
}

main.c

int main (void)
{
   ...
   NVIC_SetPriority (Timer1_IRQn, 0);
   NVIC_SetPriority (DMA_IRQn, 1);
   ...
   DMA_Setup ();
   ADC_Setup ();
   TimerSetup ();
   ...
   NVIC_EnableIRQ (Timer1_IRQn);
   NVIC_EnableIRQ (DMA_IRQn);
   
   MDR_TIMER1->CNTRL_b.CNT_EN = true;
   
   while (true)
   {
      if (ADC1_Ready && ADC2_Ready)
      {
         MeasProc ();
         
         ADC1_Ready = false;
         ADC2_Ready = false;
      }
   }
}
Закрыть

_________________
Hack the Planet!


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июл-12 22:19 
Не в сети
Аватара пользователя

Зарегистрирован: 2011-авг-21 18:55
Сообщения: 253
Для приёма нескольких байт с помощью UART+DMA, как известно, необходимо знать количество принимаемых байтов. Но может возникунть ситуация, когда на линии RX UART могут быть помехи или на приёмник поступит меньше или больше байт, чем надо. Как правильно организовать приём данных с защитой от данных ситуаций, в частности в STM32 https://community.st.com/thread/42689-e ... x-on-stm32 для этих целей можно использовать прерывание по состоянию IDLE (лог. 1) линии RX в течение времени, большего чем период передачи одного байта, но в 1986ВЕ9х нет такого прерывания, только прерывание по состоянию BREAK (лог. 0) линии RX и прерывание по таймауту приёма (длительность таймаута 4 байта)??

_________________
Hack the Planet!


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июл-13 10:52 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 73
R Max Не очень понял, это был вопрос или просто размышления на тему :-) ?
Конкретно на 1986ВЕ9х эту задачу не решал, но на STM-ках не раз приходилось делать. Поэтому изложу некоторые мысли.
Проблема при приеме по UART через DMA заключается не столько в выявлении факта потери связи посреди приема пакета, сколько в определении момента начала приема пакета. Т.е., надо узнать, когда к нам поступил первый байт пакета. Если этот момент удастся определить, то всегда можно поставить аппаратный или программный таймер, отсчитывающий время, необходимое на прием полного пакета (с небольшим запасом, разумеется). Если таймер сработал, то пакет считается испорченным.
В чем сложность определения момента приема первого байта? Если мы настроем UART RX на работу с DMA, то нельзя будет использовать прерывания по UART RX, и никто не скажет нам, что первый байт принят. В STM-ках я решал эту проблему очень просто: использовал для приема сразу два UART: UART1, основной, работал с DMA, а UART2, вспомогательный,- с прерываниями. Как только приходил первый байт пакета, срабатывало прерывание по UART2 RX, в обработчике которого запускался таймер. Одновременно UART1 начинал работать с DMA. По окончанию приема пакета возникало прерывание от канала DMA, в обработчике которого таймер останавливался.
Если посреди приема пакета возникал обрыв связи, то срабатывал таймер, процесс приема прерывался и всё возвращалось в исходное состояние. При этом протокол обмена должен быть построен так, чтобы паузы между передачей двух пакетов мастером немного превышали время передачи одного пакета. Это даст гарантию, что слейв правильно примет пакеты. Иначе, если слейв после включения попадет на середину пакета, последующие пакеты также будут испорчены.
Если в вашем случае не жалко двух UARTов, то можно пойти по этому пути. При этом для обоих UART надо будет обязательно отключить аппаратные буферы для RX.
Есть и другие варианты, но они более сложные, в том числе и на стороне мастера.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июл-13 12:49 
Не в сети

Зарегистрирован: 2009-июл-21 14:13
Сообщения: 1141
Откуда: Тула
R Max писал(а):
...stm32 для этих целей можно использовать прерывание по состоянию IDLE (лог. 1) линии RX в течение времени, большего чем период передачи одного байта, но в 1986ВЕ9х нет такого прерывания, только прерывание по состоянию BREAK (лог. 0) линии RX и прерывание по таймауту приёма (длительность таймаута 4 байта)??

Это компорт, и это плохо.
Кажется, все решения здесь - это костыли одного или другого вида...
При их разработке приходится исходить строго из задачи. Если ожидаемая посылка не лезет в приёмный fifo (16 байт), тогда можно попробовать настроить ДМА на кол-во байт, немного большее чем максимальный размер посылки (таким образом просто накапливать принятые данные), а начинать их обрабатывать по сигналу таймаута (соответственно, таймаут должен быть оговорен и выдерживаться всеми абонентами, аналогично модбас). Разумеется протокол обмена должен подразумевать возможность найти начало передаваемого пакета и оценить целостность принятых данных.

_________________
сочувствующий…


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июл-13 13:01 
Не в сети

Зарегистрирован: 2009-июл-21 14:13
Сообщения: 1141
Откуда: Тула
andelie писал(а):
Если в вашем случае не жалко двух UARTов, то можно пойти по этому пути. При этом для обоих UART надо будет обязательно отключить аппаратные буферы для RX.
Есть и другие варианты, но они более сложные, в том числе и на стороне мастера.

Кажется, можно сэкономить: перед началом приёма разрешить сигнал прерывания ЦПУ по приёму байта, сходить в прерывание и там 1. запрерить это прерывание, 2. запустить таймер.
Тут есть подводный камень: прерывание появится и скоро снимется, т.к. байт будет считан контроллером ДМА, так что нужно повысить приоритет этого прерывания. Можно поэкспериментировать на эту тему.
Из СП следует, что возможно можно разрешить только блочные транзакции, но по функционалу битов конфигурации не ясно как... страница 378 СП
Цитата:
Сигналы блочного и одноэлементного обмена данными не являются взаимно
исключающими, они могут быть инициированы одновременно. Например, в случае, если
заполнение данными буфера приемника превышает пороговое значение, формируется как
сигнал запроса одноэлементного обмена, так и сигнал запроса блочного обмена данными. В
случае, если количество данных в буфере приема меньше порогового значения формируется
только запрос одноэлементного обмена. Это бывает полезно в ситуациях, при которых объем
данных меньше размера блока. Пусть, например, нужно принять 19 символов, а порог
заполнения буфера FIFO установлен равным четырем. Тогда контроллер DMA осуществит
четыре передачи блоков по четыре символа, а оставшиеся три символа передаст в ходе трех
одноэлементных обменов.
Примечание – Для оставшихся трех символов контроллер UART не может
инициировать процедуру блочного обмена.

_________________
сочувствующий…


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июл-13 14:38 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 73
Цитата:
Кажется, можно сэкономить: перед началом приёма разрешить сигнал прерывания ЦПУ по приёму байта, сходить в прерывание и там 1. запретить это прерывание, 2.

А вот это, как раз-таки, и не получится сделать. Если DMA работает по какому-то событию, то о прерывании по этому же событию можно забыть: оно полностью отжирается контроллером DMA. Это я проверял в нескольких разных камнях и для разных событий. Конкретно, для STM32L152 проверял с UART RX. Поэтому и огород с двумя UARTами стал городить (однако работает надежно, и одним из трех UART не жалко было пожертвовать: все равно порожняком лежит :)).
Аппаратный буфер UART совместно с DMA RX применять никак нельзя, иначе часть байт просто потеряем. Главное, приоритет каналу DMA задать наивысший и не применять блочные передачи ни по одному каналу DMA.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: DMA и все что с ним связано
СообщениеДобавлено: 2018-июл-16 12:11 
Не в сети

Зарегистрирован: 2010-авг-30 19:12
Сообщения: 395
еще вариант - если таймауты относительно длинные - контролировать регистр channal_cfg из управляющей структуры канала. Там есть поле n_minus_1. Из описания
Цитата:
Контроллер обновит это поле перед тем, как произвести процесс арбитража. Это позволяет контроллеру хранить количество оставшихся передач DMA до завершения цикла DMA

Таким образом можно видеть есть очередная транзакция (или несколько) или нет.
Соответственно приемный буфер должен быть с запасом (или кольцевой).

Это в теории. На практике не проверялось. Не требует прерываний и дополнительных УАРТов.

_________________
О сколько нам открытий чудных
Готовит просвященья дух,
И опыт - сын ошибок трудных ... (Пушкин)

Пергаменты не утоляют жажду ("Фауст",Гете)


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 104 ]  На страницу Пред.  1 ... 3, 4, 5, 6, 7

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: Bing [Bot], Google [Bot] и гости: 3


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB