Миландр

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

Часовой пояс: UTC+03:00




Начать новую тему  Ответить на тему  [ 21 сообщение ]  На страницу 1 2 »
Автор Сообщение
 Заголовок сообщения: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-июл-28 13:16 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
Здравствуйте. Помогите разобраться с DMA. Пытаюсь разобраться на примере замера напряжения на отладочной плате.
uint16_t ADC_Buffer [U_ADC_BUFFER_SIZE]; // Буфер для хранения результатов АЦП

/**
	* Функция инициализации модуля АЦП
	*/
void init_ADC()
{
	RST_CLK_PCLKcmd(RST_CLK_PCLK_ADC, ENABLE);
	ADC_DeInit();
	ADC_StructInit(&ADC);
	
	/* Конфигурация АЦП общая. */
  ADC.ADC_SynchronousMode      = ADC_SyncMode_Independent; // Независимый запуск АЦП
  ADC.ADC_StartDelay           = 0;	// Задрежка между запусками АЦП1 и АЦП2
  ADC.ADC_IntVRefConversion    = ADC_VREF_CONVERSION_Disable;      // Запретить преобразования для канала VREF (внутренней опоры)
  ADC.ADC_IntVRefTrimming      = 1;   // Подстройка источника напряжения VREF
	ADC_Init(&ADC);
	
	/* Конфигурация АЦП2. */
  ADCx_StructInit (&ADC2);															
  ADC2.ADC_ClockSource      = ADC_CLOCK_SOURCE_CPU;	// Тактировать АЦП той же частотой, что и ядро МК 
  ADC2.ADC_SamplingMode     = ADC_SAMPLING_MODE_CICLIC_CONV;	// Режим циклического преобразования (несколько раз подряд)
  ADC2.ADC_ChannelSwitching = ADC_CH_SWITCHING_Disable;	// Режим переключения каналов (запретить)
  ADC2.ADC_ChannelNumber    = ADC_CH_ADC2; // Выбранный канал АЦП
  ADC2.ADC_Channels         = 0;		// Выбранные каналы АЦП с последовательным опросом (не выбраны) 
  ADC2.ADC_LowLevel         = 0;		// Нижний уровень
  ADC2.ADC_HighLevel        = 0;		// Верхний уровень	
  ADC2.ADC_VRefSource       = ADC_VREF_SOURCE_INTERNAL;	// Вид источника опроного напряжения (внутренний)
  ADC2.ADC_IntVRefSource    = ADC_INT_VREF_SOURCE_INEXACT;  // Вид внутреннего источника опроного напряжения (не точный)
  ADC2.ADC_Prescaler        = ADC_CLK_div_512;	// Предделитель частоты тактирования АЦП (512)
  ADC2.ADC_DelayGo          = 7;	// Задержка между запусками АЦП (максимальная)   	
  ADC2_Init (&ADC2);
	
	/* Разрешить работу АЦП2. */
  ADC2_Cmd (ENABLE);

  /* Разрешить прерывания от DMA. */
  NVIC_EnableIRQ (DMA_IRQn); 
}

/**
	* Конфигурирование DMA (Direct Memory Access - прямой доступ к памяти).
	*/
void DMA_Config(void)
{
	// DMA вообще и в частности. http://habrahabr.ru/post/228531/  
  // DMA и все что с ним связано. http://forum.milandr.ru/viewtopic.php?f=33&t=234&hilit=DMA+DMA+ADC&sid=888ddcc861bafe357880b779547a2fd9&start=15 
	
  // Разрешить тактирование DMA 
	// Для корректной работы DMA необходимо разрешить тактирование SSP1 и SSP2
  // Если этого не сделать, то всё время будет возникать прерывание от DMA.	
  RST_CLK_PCLKcmd (RST_CLK_PCLK_DMA | RST_CLK_PCLK_SSP1 | RST_CLK_PCLK_SSP2, ENABLE);  
	
  // Запретить все прерывания, в том числе от SSP1 и SSP2.
  NVIC->ICPR[0] = 0xFFFFFFFF;
  NVIC->ICER[0] = 0xFFFFFFFF;	
	
  // Сбросить все настройки DMA
  DMA_DeInit();
  DMA_StructInit (&DMA_Channel_InitStructure);	
	
  DMA_InitStructure.DMA_SourceBaseAddr = (uint32_t)(&(MDR_ADC->ADC2_RESULT));	// Базовый адрес регистра результата преобразования АЦП1, откуда будем читать 
  DMA_InitStructure.DMA_DestBaseAddr = (uint32_t) &ADC_Buffer; // Базовый адрес в памяти, куда будем писать результаты работы АЦП
  DMA_InitStructure.DMA_CycleSize = U_ADC_BUFFER_SIZE;  // Размер буфера
  DMA_InitStructure.DMA_SourceIncSize = DMA_SourceIncNo;   // Запретить автоувеличение адреса для периферии
  DMA_InitStructure.DMA_DestIncSize = DMA_DestIncHalfword;  // Разрешить автоувеличение адреса для памяти на 2
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;     // Передаем данные по 2 байта (полслова)  
  DMA_InitStructure.DMA_NumContinuous = DMA_Transfers_1;  // Сколько раз повоторяем совокупность циклов - 1 раз
  DMA_InitStructure.DMA_SourceProtCtrl = DMA_SourcePrivileged;  // Режим защиты источника
  DMA_InitStructure.DMA_DestProtCtrl = DMA_DestPrivileged; // Режим защиты приемника
  DMA_InitStructure.DMA_Mode = DMA_Mode_Basic;  // Режим работы DMA - базовый
  
  // Задать структуру канала
  DMA_Channel_InitStructure.DMA_PriCtrlData = &DMA_InitStructure;
  DMA_Channel_InitStructure.DMA_Priority = DMA_Priority_Default;
  DMA_Channel_InitStructure.DMA_UseBurst = DMA_BurstClear;
  DMA_Channel_InitStructure.DMA_SelectDataStructure = DMA_CTRL_DATA_PRIMARY;
  
	// Инициализировать канал DMA 
  DMA_Init (DMA_Channel_ADC2, &DMA_Channel_InitStructure);							  

  // Снять запрет на запросы и одиночные запросы к DMA  
  MDR_DMA->CHNL_REQ_MASK_CLR = 1 << DMA_Channel_ADC2;
  MDR_DMA->CHNL_USEBURST_CLR = 1 << DMA_Channel_ADC2;

  // Разрешить работу DMA
  DMA_Cmd (DMA_Channel_ADC2, ENABLE);

  // Задать приоритет аппаратного прерывания от DMA
  NVIC_SetPriority (DMA_IRQn, 1); 	  
	
}
ADC_Buffer заполняется данными один раз. Далее не могу понять как периодически обновлять этот буфер. Пробовал с помощью таймера или с помощью прерываний от ДМА вызывать что-то подобное:
//  Высший приоритет
  MDR_DMA->CHNL_PRIORITY_SET |= 1 << DMA_Channel_ADC2;
	
	/* Настройка управляющей структуры. */
  DMA_ControlTable[DMA_Channel_ADC2].DMA_SourceEndAddr = (uint32_t)&MDR_ADC->ADC2_RESULT;
  DMA_ControlTable[DMA_Channel_ADC2].DMA_DestEndAddr = (uint32_t)ADC_Buffer[U_ADC_BUFFER_SIZE - 1];
  DMA_ControlTable[DMA_Channel_ADC2].DMA_Control = DMA_DestIncNo | DMA_DestIncHalfword |
																									 DMA_MemoryDataSize_HalfWord |
                                                   DMA_Mode_AutoRequest |
                                                   DMA_Transfers_1024 |
                                                   ((U_ADC_BUFFER_SIZE - 1) << 4);

	DMA_Cmd(DMA_Channel_ADC2, ENABLE);; // Разрешаем работу канала DMA_Channel_ADC2.
  DMA_Request(DMA_Channel_ADC2);  // Запускаем цикл ДМА.
	
	// Получение указателя на поле Control канала
  ptrControltable = (uint32_t *) &DMA_ControlTable[DMA_Channel_ADC2].DMA_Control;
	tmpval_old = (*ptrControltable);
	
	
	tmpval = (*ptrControltable) & 0x7;
	while(tmpval == 0){}
  // Выключение канала DMA
  DMA_Cmd(DMA_Channel_ADC2, DISABLE); // MDR_DMA->CHNL_ENABLE_CLR = (1 << DMA_Channel);*/
Но либо процесс виснет в цикле, либо буфер не обновляется. Работаю с контроллером пару месяцев. До этого с МК дел не имел.


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-июл-28 16:47 
Не в сети
Support
Аватара пользователя

Зарегистрирован: 2018-авг-10 23:14
Сообщения: 530
Организация: АО «ПКК Миландр»
Откуда: Зеленоград
Eliot456 писал(а): *
Здравствуйте. Помогите разобраться с DMA. Пытаюсь разобраться на примере замера напряжения на отладочной плате.
uint16_t ADC_Buffer [U_ADC_BUFFER_SIZE]; // Буфер для хранения результатов АЦП

/**
	* Функция инициализации модуля АЦП
	*/
void init_ADC()
{
	RST_CLK_PCLKcmd(RST_CLK_PCLK_ADC, ENABLE);
	ADC_DeInit();
	ADC_StructInit(&ADC);
	
	/* Конфигурация АЦП общая. */
  ADC.ADC_SynchronousMode      = ADC_SyncMode_Independent; // Независимый запуск АЦП
  ADC.ADC_StartDelay           = 0;	// Задрежка между запусками АЦП1 и АЦП2
  ADC.ADC_IntVRefConversion    = ADC_VREF_CONVERSION_Disable;      // Запретить преобразования для канала VREF (внутренней опоры)
  ADC.ADC_IntVRefTrimming      = 1;   // Подстройка источника напряжения VREF
	ADC_Init(&ADC);
	
	/* Конфигурация АЦП2. */
  ADCx_StructInit (&ADC2);															
  ADC2.ADC_ClockSource      = ADC_CLOCK_SOURCE_CPU;	// Тактировать АЦП той же частотой, что и ядро МК 
  ADC2.ADC_SamplingMode     = ADC_SAMPLING_MODE_CICLIC_CONV;	// Режим циклического преобразования (несколько раз подряд)
  ADC2.ADC_ChannelSwitching = ADC_CH_SWITCHING_Disable;	// Режим переключения каналов (запретить)
  ADC2.ADC_ChannelNumber    = ADC_CH_ADC2; // Выбранный канал АЦП
  ADC2.ADC_Channels         = 0;		// Выбранные каналы АЦП с последовательным опросом (не выбраны) 
  ADC2.ADC_LowLevel         = 0;		// Нижний уровень
  ADC2.ADC_HighLevel        = 0;		// Верхний уровень	
  ADC2.ADC_VRefSource       = ADC_VREF_SOURCE_INTERNAL;	// Вид источника опроного напряжения (внутренний)
  ADC2.ADC_IntVRefSource    = ADC_INT_VREF_SOURCE_INEXACT;  // Вид внутреннего источника опроного напряжения (не точный)
  ADC2.ADC_Prescaler        = ADC_CLK_div_512;	// Предделитель частоты тактирования АЦП (512)
  ADC2.ADC_DelayGo          = 7;	// Задержка между запусками АЦП (максимальная)   	
  ADC2_Init (&ADC2);
	
	/* Разрешить работу АЦП2. */
  ADC2_Cmd (ENABLE);

  /* Разрешить прерывания от DMA. */
  NVIC_EnableIRQ (DMA_IRQn); 
}

/**
	* Конфигурирование DMA (Direct Memory Access - прямой доступ к памяти).
	*/
void DMA_Config(void)
{
	// DMA вообще и в частности. http://habrahabr.ru/post/228531/  
  // DMA и все что с ним связано. http://forum.milandr.ru/viewtopic.php?f=33&t=234&hilit=DMA+DMA+ADC&sid=888ddcc861bafe357880b779547a2fd9&start=15 
	
  // Разрешить тактирование DMA 
	// Для корректной работы DMA необходимо разрешить тактирование SSP1 и SSP2
  // Если этого не сделать, то всё время будет возникать прерывание от DMA.	
  RST_CLK_PCLKcmd (RST_CLK_PCLK_DMA | RST_CLK_PCLK_SSP1 | RST_CLK_PCLK_SSP2, ENABLE);  
	
  // Запретить все прерывания, в том числе от SSP1 и SSP2.
  NVIC->ICPR[0] = 0xFFFFFFFF;
  NVIC->ICER[0] = 0xFFFFFFFF;	
	
  // Сбросить все настройки DMA
  DMA_DeInit();
  DMA_StructInit (&DMA_Channel_InitStructure);	
	
  DMA_InitStructure.DMA_SourceBaseAddr = (uint32_t)(&(MDR_ADC->ADC2_RESULT));	// Базовый адрес регистра результата преобразования АЦП1, откуда будем читать 
  DMA_InitStructure.DMA_DestBaseAddr = (uint32_t) &ADC_Buffer; // Базовый адрес в памяти, куда будем писать результаты работы АЦП
  DMA_InitStructure.DMA_CycleSize = U_ADC_BUFFER_SIZE;  // Размер буфера
  DMA_InitStructure.DMA_SourceIncSize = DMA_SourceIncNo;   // Запретить автоувеличение адреса для периферии
  DMA_InitStructure.DMA_DestIncSize = DMA_DestIncHalfword;  // Разрешить автоувеличение адреса для памяти на 2
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;     // Передаем данные по 2 байта (полслова)  
  DMA_InitStructure.DMA_NumContinuous = DMA_Transfers_1;  // Сколько раз повоторяем совокупность циклов - 1 раз
  DMA_InitStructure.DMA_SourceProtCtrl = DMA_SourcePrivileged;  // Режим защиты источника
  DMA_InitStructure.DMA_DestProtCtrl = DMA_DestPrivileged; // Режим защиты приемника
  DMA_InitStructure.DMA_Mode = DMA_Mode_Basic;  // Режим работы DMA - базовый
  
  // Задать структуру канала
  DMA_Channel_InitStructure.DMA_PriCtrlData = &DMA_InitStructure;
  DMA_Channel_InitStructure.DMA_Priority = DMA_Priority_Default;
  DMA_Channel_InitStructure.DMA_UseBurst = DMA_BurstClear;
  DMA_Channel_InitStructure.DMA_SelectDataStructure = DMA_CTRL_DATA_PRIMARY;
  
	// Инициализировать канал DMA 
  DMA_Init (DMA_Channel_ADC2, &DMA_Channel_InitStructure);							  

  // Снять запрет на запросы и одиночные запросы к DMA  
  MDR_DMA->CHNL_REQ_MASK_CLR = 1 << DMA_Channel_ADC2;
  MDR_DMA->CHNL_USEBURST_CLR = 1 << DMA_Channel_ADC2;

  // Разрешить работу DMA
  DMA_Cmd (DMA_Channel_ADC2, ENABLE);

  // Задать приоритет аппаратного прерывания от DMA
  NVIC_SetPriority (DMA_IRQn, 1); 	  
	
}
ADC_Buffer заполняется данными один раз. Далее не могу понять как периодически обновлять этот буфер. Пробовал с помощью таймера или с помощью прерываний от ДМА вызывать что-то подобное:
//  Высший приоритет
  MDR_DMA->CHNL_PRIORITY_SET |= 1 << DMA_Channel_ADC2;
	
	/* Настройка управляющей структуры. */
  DMA_ControlTable[DMA_Channel_ADC2].DMA_SourceEndAddr = (uint32_t)&MDR_ADC->ADC2_RESULT;
  DMA_ControlTable[DMA_Channel_ADC2].DMA_DestEndAddr = (uint32_t)ADC_Buffer[U_ADC_BUFFER_SIZE - 1];
  DMA_ControlTable[DMA_Channel_ADC2].DMA_Control = DMA_DestIncNo | DMA_DestIncHalfword |
																									 DMA_MemoryDataSize_HalfWord |
                                                   DMA_Mode_AutoRequest |
                                                   DMA_Transfers_1024 |
                                                   ((U_ADC_BUFFER_SIZE - 1) << 4);

	DMA_Cmd(DMA_Channel_ADC2, ENABLE);; // Разрешаем работу канала DMA_Channel_ADC2.
  DMA_Request(DMA_Channel_ADC2);  // Запускаем цикл ДМА.
	
	// Получение указателя на поле Control канала
  ptrControltable = (uint32_t *) &DMA_ControlTable[DMA_Channel_ADC2].DMA_Control;
	tmpval_old = (*ptrControltable);
	
	
	tmpval = (*ptrControltable) & 0x7;
	while(tmpval == 0){}
  // Выключение канала DMA
  DMA_Cmd(DMA_Channel_ADC2, DISABLE); // MDR_DMA->CHNL_ENABLE_CLR = (1 << DMA_Channel);*/
Но либо процесс виснет в цикле, либо буфер не обновляется. Работаю с контроллером пару месяцев. До этого с МК дел не имел.
Здравствуйте!

Вопросов к инициализации блоков ADC и DMA в коде нет, настройка произведена базово и корректно - насколько понимаю, по рекомендациям из книги Благодарова А.В. и Владимирова Л.Л. "Программирование микроконтроллеров: методическое пособие на основе отечественных микросхем семейства 1986ВЕ9х разработки и производства компании "Миландр". Обратите, пожалуйста, внимание на ключевую фазу, по которой контроллер DMA начинает вести обмен - это значение логической единицы в регистре CHNL_ENABLE_SET у необходимого канала (при условии, что структуры в программе настроены и расположены в памяти корректно). Если единица установлена (в библиотеке SPL единица у необходимого канала устанавливается в функции void DMA_Cmd(uint8_t DMA_Channel, FunctionalState NewState)), то при получении блоком DMA запроса (если работаем с АЦП, то это событие окончания преобразования внутри блока преобразователя), он начинает выполнять транзакции по объему в соответствии с настройками внутри структуры по количеству циклов передачи данных (бесконечный обмен не предусмотрен), после чего остановится. Аппаратно после этого события формируется вызов прерывания (в случае с АЦП удобно использовать для обработки результатов). Теперь, чтобы запустить DMA вновь, необходимо просто снова установить значение для требуемого канала в регистре CHNL_ENABLE_SET: где это делать, разработчик решает самостоятельно - важно отметить, что в обработчике или в любом другом месте кода до активации регистра CHNL_ENABLE_SET важно переинициализировать управляющую структуру, ибо в ходе предыдущих транзакций в памяти она обязательно изменится.

Код на базе Вашего с бесконечной работой DMA (активация канала ADC2 происходит после прерывания в цикле while(1)) под спойлером - обратите, пожалуйста, внимание, что в прерывании учитывается также требование по переинициализации структуры.

Также Вам будет полезна общая статья по работе с DMA, которая опубликована на информационном портале отдела технической поддержки компании Миландр.
Открыть код
#include <MDR32Fx.h>
#include "MDR32F9Qx_config.h"
#include "MDR32F9Qx_port.h"
#include "MDR32F9Qx_rst_clk.h"
#include "MDR32F9Qx_dma.h"
#include "MDR32F9Qx_adc.h"

#define U_ADC_BUFFER_SIZE 256

void DMA_Config(void);
void init_ADC();

uint16_t ADC_Buffer [U_ADC_BUFFER_SIZE]; 
DMA_ChannelInitTypeDef DMA_InitStructure;
DMA_CtrlDataInitTypeDef DMA_Channel_InitStructure;

ADC_InitTypeDef ADC;
ADCx_InitTypeDef ADC2;

int main(void)
{
	init_ADC();
	DMA_Config();

	while(1)
	{
		DMA_Cmd (DMA_Channel_ADC2, ENABLE);
	}
}

void init_ADC()
{
  RST_CLK_PCLKcmd(RST_CLK_PCLK_ADC, ENABLE);
	
  ADC_DeInit();
  ADC_StructInit(&ADC);
	
  ADC.ADC_SynchronousMode      = ADC_SyncMode_Independent; 
  ADC.ADC_StartDelay           = 0;	
  ADC.ADC_IntVRefConversion    = ADC_VREF_CONVERSION_Disable;    
  ADC.ADC_IntVRefTrimming      = 1;   
  ADC_Init(&ADC);
	
  ADCx_StructInit (&ADC2);															
  ADC2.ADC_ClockSource      = ADC_CLOCK_SOURCE_CPU;	
  ADC2.ADC_SamplingMode     = ADC_SAMPLING_MODE_CICLIC_CONV;	
  ADC2.ADC_ChannelSwitching = ADC_CH_SWITCHING_Disable;	
  ADC2.ADC_ChannelNumber    = ADC_CH_ADC2;
  ADC2.ADC_Channels         = 0;	
  ADC2.ADC_LowLevel         = 0;		
  ADC2.ADC_HighLevel        = 0;			
  ADC2.ADC_VRefSource       = ADC_VREF_SOURCE_INTERNAL;	
  ADC2.ADC_IntVRefSource    = ADC_INT_VREF_SOURCE_INEXACT;  
  ADC2.ADC_Prescaler        = ADC_CLK_div_512;	
  ADC2.ADC_DelayGo          = 7;	 	
  ADC2_Init (&ADC2);
  ADC2_Cmd (ENABLE);
}

void DMA_Config(void)
{
  RST_CLK_PCLKcmd (RST_CLK_PCLK_DMA | RST_CLK_PCLK_SSP1 | RST_CLK_PCLK_SSP2, ENABLE);  
	
  NVIC->ICPR[0] = 0xFFFFFFFF;
  NVIC->ICER[0] = 0xFFFFFFFF;	
	
  DMA_DeInit();
  DMA_StructInit (&DMA_InitStructure);	
	
  DMA_Channel_InitStructure.DMA_SourceBaseAddr = (uint32_t)(&(MDR_ADC->ADC2_RESULT));
  DMA_Channel_InitStructure.DMA_DestBaseAddr = (uint32_t) &ADC_Buffer; 
//	DMA_Channel_InitStructure.DMA_DestBaseAddr = 0x20001000;
  DMA_Channel_InitStructure.DMA_CycleSize = U_ADC_BUFFER_SIZE;  
  DMA_Channel_InitStructure.DMA_SourceIncSize = DMA_SourceIncNo;   
  DMA_Channel_InitStructure.DMA_DestIncSize = DMA_DestIncHalfword;  
  DMA_Channel_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;     
  DMA_Channel_InitStructure.DMA_NumContinuous = DMA_Transfers_1;  
  DMA_Channel_InitStructure.DMA_SourceProtCtrl = DMA_SourcePrivileged; 
  DMA_Channel_InitStructure.DMA_DestProtCtrl = DMA_DestPrivileged; 
  DMA_Channel_InitStructure.DMA_Mode = DMA_Mode_Basic;  
  
  DMA_InitStructure.DMA_PriCtrlData = &DMA_Channel_InitStructure;
  DMA_InitStructure.DMA_Priority = DMA_Priority_Default;
  DMA_InitStructure.DMA_UseBurst = DMA_BurstClear;
  DMA_InitStructure.DMA_SelectDataStructure = DMA_CTRL_DATA_PRIMARY;
  
  DMA_Init (DMA_Channel_ADC2, &DMA_InitStructure);							  

  MDR_DMA->CHNL_REQ_MASK_CLR = 1 << DMA_Channel_ADC2;
  MDR_DMA->CHNL_USEBURST_CLR = 1 << DMA_Channel_ADC2;

  DMA_Cmd (DMA_Channel_ADC2, ENABLE);

  NVIC_EnableIRQ(DMA_IRQn);
  NVIC_SetPriority (DMA_IRQn, 1); 	  
	
}

void DMA_IRQHandler(void)
{
DMA_Channel_InitStructure.DMA_CycleSize = U_ADC_BUFFER_SIZE;
DMA_Init (DMA_Channel_ADC2, &DMA_InitStructure);
DMA_Cmd (DMA_Channel_ADC2, DISABLE);

}
Закрыть

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

Информационный портал отдела технической поддержки support.milandr.ru


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-июл-29 11:44 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
Можете в упрощенной форме пояснить в чем разница между:
DMA_SourceEndAddr, DMA_DestEndAddr управляющей структуры и
DMA_SourceBaseAddr, DMA_DestBaseAddr структуры для инициализации.
Не могу сообразить как в управляющей структуре заполнить DMA_SourceEndAddr, DMA_DestEndAddr


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-июл-29 12:42 
Не в сети
Support
Аватара пользователя

Зарегистрирован: 2018-авг-10 23:14
Сообщения: 530
Организация: АО «ПКК Миландр»
Откуда: Зеленоград
Eliot456 писал(а): *
Можете в упрощенной форме пояснить в чем разница между:
DMA_SourceEndAddr, DMA_DestEndAddr управляющей структуры и
DMA_SourceBaseAddr, DMA_DestBaseAddr структуры для инициализации.
Не могу сообразить как в управляющей структуре заполнить DMA_SourceEndAddr, DMA_DestEndAddr
Здравствуйте!

Обратите, пожалуйста, внимание, что изначально в контроллере DMA на уровне архитектуры в составе структуры управления имеются только указатели конца данных источника/приёмника, всё, что в библиотеках именуется как DMA_SourceBaseAddr и DMA_DestBaseAddr - есть ничто иное как производные, которые на основании размерности и объема отправляемых/получаемых данных в отдельных функциях вычисляют значения DMA_SourceEndAddr и DMA_DestEndAddr согласно алгоритму из подраздела "Вычисление адреса" раздела DMA в спецификации на семейство микроконтроллеров 1986ВЕ9х (см. логику работы можно также в MDR32F9Qx_dma.c).

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

Информационный портал отдела технической поддержки support.milandr.ru


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-июл-29 13:42 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
Спасибо за ответы.


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-июл-29 13:58 
Не в сети
Support
Аватара пользователя

Зарегистрирован: 2018-авг-10 23:14
Сообщения: 530
Организация: АО «ПКК Миландр»
Откуда: Зеленоград
Eliot456 писал(а): *
Спасибо за ответы.
Спасибо Вам за обращение. Подскажите, пожалуйста, я также дополнительно видел от Вас вопрос (был удалён) по отсчётам АЦП - в этом направлении удалось разобраться? В случае, если требуются какие-либо дополнительные пояснения, то обращайтесь.

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

Информационный портал отдела технической поддержки support.milandr.ru


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 07:00 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
Lampadov писал(а): *
Eliot456 писал(а): *
Спасибо за ответы.
Спасибо Вам за обращение. Подскажите, пожалуйста, я также дополнительно видел от Вас вопрос (был удалён) по отсчётам АЦП - в этом направлении удалось разобраться? В случае, если требуются какие-либо дополнительные пояснения, то обращайтесь.
С АЦП разобрался. У меня еще вопрос возник по ходу дела.
Если делать так:
while(1){
	DMA_Cmd (DMA_Channel_ADC2, ENABLE);
}
или так:
void SysTick_Handler (void)
{
	DMA_IRT_Counter++;
	
	if(DMA_IRT_Counter > 5000){
		DMA_Cmd (DMA_Channel_ADC2, ENABLE);
		DMA_IRT_Counter = 0;
	}
}
то все отрабатывает. буфер ADC_Buffer обновляется новыми данными.
Если сделать так:
bool isWriteResultADC ;
void SysTick_Handler (void)
{
	DMA_IRT_Counter++;
	
	if(DMA_IRT_Counter > 5000){
		isWriteResultADC = true;
		DMA_IRT_Counter = 0;
	}
}

while(1){
	/* Замер напряжения раз в 5 секунд. */
	if(isWriteResultADC){
		DMA_Cmd (DMA_Channel_ADC2, ENABLE);
	}
}

/**
	* Функция прерывания ДМА.
	*/
void DMA_IRQHandler (void)
{
	counterItem++;

	/* Настройка управляющей структуры ДМА, после предыдущего цикла. */
	DMA_ControlTable[DMA_Channel_ADC2].DMA_Control = DMA_DestIncHalfword | DMA_DestPrivileged | 
 DMA_MemoryDataSize_HalfWord | DMA_SourcePrivileged |
DMA_Mode_Basic | DMA_SourceIncNo |
DMA_Transfers_1 | ((U_ADC_BUFFER_SIZE - 1) << 4);
	
	/* Запретить дальнейшую работу канала DMA c ADC. */
	DMA_Cmd (DMA_Channel_ADC2, DISABLE);
	
	/* После пяти циклов ДМА, расчитываем среднее напряжение на аккумуляторе. */
	if(counterItem >= 5){
		Akb_Meassuring(ADC_Buffer, &Modbus);
		counterItem = 0;
		isWriteResultADC = false;
	}

}
То обновления буффера ADC_Buffer не происходит. Казалось бы разницы особой нет, по какому событию запускать DMA_Cmd (DMA_Channel_ADC2, ENABLE), но почему то по условию с флагом это не работает. Не подскажете в чем может быть проблема?


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 07:49 
Не в сети

Зарегистрирован: 2017-июн-05 14:53
Сообщения: 24
Организация: ОАП АПЗ
Проблема в том, что для прерываний по DMA нельзя поставить маску чтоб не было прерываний по каждой транзакции а не только по окончанию всех. Прерывание по каждой это слишком быстро.
Я решил это так:
1) циклограмма идёт по прерываниям от таймера.
2) Настраиваю DMA на приём n замеров от ADC и чтоб он разместил в буфере.
3) Запускаю ADC на последовательное переключение каналов, которые мне надо, можно многократно.
4) ADC и DMA перемалывает всё это и в буфере результат. После того, как заданное число n замеров закончится, DMA остановится.
5) По таймеру когда всё это уже закончится через несколько микросекунд обрабатываю буфер.


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 08:07 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
dOb писал(а): *
Проблема в том, что для прерываний по DMA нельзя поставить маску чтоб не было прерываний по каждой транзакции а не только по окончанию всех. Прерывание по каждой это слишком быстро.
Я решил это так:
1) циклограмма идёт по прерываниям от таймера.
2) Настраиваю DMA на приём n замеров от ADC и чтоб он разместил в буфере.
3) Запускаю ADC на последовательное переключение каналов, которые мне надо, можно многократно.
4) ADC и DMA перемалывает всё это и в буфере результат. После того, как заданное число n замеров закончится, DMA остановится.
5) По таймеру когда всё это уже закончится через несколько микросекунд обрабатываю буфер.
То есть грубо говоря, то что у меня в DMA_IRQHandler перенести в отдельную функцию и вызывать ее потом в прерывании SysTick_Handler ? Я правильно понял мысль?


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 08:55 
Не в сети

Зарегистрирован: 2017-июн-05 14:53
Сообщения: 24
Организация: ОАП АПЗ
Если у вас задача измерять медленно меняющуюся величину напряжения на аккумуляторе, то DMA вообще не нужен. Лучше обрабатывайте прерывания от ADC после каждого преобразования. Вам же не нужно гнаться за микросекундами.


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 09:26 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
dOb писал(а): *
Если у вас задача измерять медленно меняющуюся величину напряжения на аккумуляторе, то DMA вообще не нужен. Лучше обрабатывайте прерывания от ADC после каждого преобразования. Вам же не нужно гнаться за микросекундами.
Будет больше 10 аккумуляторов. Для замера напряжения на одном нужно будет сделать порядка 10 - 20 замеров для расчета среднего значения, т. е. это 10 - 20 прерываний от АЦП. Только из-за этого решил воспользоваться DMA. Плюс к этому прерывания от таймера, прерывания UART


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 10:29 
Не в сети

Зарегистрирован: 2018-дек-17 15:20
Сообщения: 172
Организация: частное лицо
Eliot456 писал(а): *
Будет больше 10 аккумуляторов. Для замера напряжения на одном нужно будет сделать порядка 10 - 20 замеров для расчета среднего значения, т. е. это 10 - 20 прерываний от АЦП. Только из-за этого решил воспользоваться DMA. Плюс к этому прерывания от таймера, прерывания UART
Я также советую настроить ADC на выдачу прерываний и на перебор каналов ADC.
В каждом прерывании результатом будет номер канала и значение ADC.
Усреднение можно производить прямо в обработчике прерываний от ADC.
Не нужно боятся прерываний от таймера и UART. Контроллеры 1986ВЕ9х выполняют код быстрее чем может показаться


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 10:36 
Не в сети

Зарегистрирован: 2018-мар-18 15:49
Сообщения: 273
Организация: StartMilandr.ru
В качестве примера, есть проект использования Ping-pong режима DMA для вычисления среднего значения с АЦП.
код: https://github.com/StartMilandr/MDR_Pac ... ong/main.c
комментарий: https://github.com/StartMilandr/MDR_Pac ... C_PingPong
Функции не из официального пака, но для понимания общей логики думаю это не принципиально.

АЦП можно настроить на перебор 10-ти каналов, если усреднение по 10 значений, то цикл DMA настраивается на 10 * 10 = 100 передач.
После этих 100 передач возникает прерывание DMA, по нему выставляется флаг для обсчета усреднений в основном цикле.
АЦП с DMA во время рассчета могут копить данные во второй массив. Но если быстродействие не позволит, то можно и без пинг понга.
Таймер не понял зачем нужен.

Обработчик прерывания желательно делать максимально коротким, иначе можно пропустить следующее прерывание или не дать другим прерываниям отработать.
- АЦП измеряет значение каждые ~28 тактов ADC_Clock. ADC_Clock max = 14МГц
- Вход-выход в прерывание в Cortex-M3 вроде по 6 (min) / 12 (max) тактов CPU_Clock. CPU_Clock max = 80МГц
Не так уж много времени на обработку в предельном случае.


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 11:24 
Не в сети

Зарегистрирован: 2021-апр-29 13:09
Сообщения: 12
Организация: Частное лицо
StartMilandr писал(а): *
В качестве примера, есть проект использования Ping-pong режима DMA для вычисления среднего значения с АЦП.
код: https://github.com/StartMilandr/MDR_Pac ... ong/main.c
комментарий: https://github.com/StartMilandr/MDR_Pac ... C_PingPong
Функции не из официального пака, но для понимания общей логики думаю это не принципиально.

АЦП можно настроить на перебор 10-ти каналов, если усреднение по 10 значений, то цикл DMA настраивается на 10 * 10 = 100 передач.
После этих 100 передач возникает прерывание DMA, по нему выставляется флаг для обсчета усреднений в основном цикле.
АЦП с DMA во время рассчета могут копить данные во второй массив. Но если быстродействие не позволит, то можно и без пинг понга.
Таймер не понял зачем нужен.

Обработчик прерывания желательно делать максимально коротким, иначе можно пропустить следующее прерывание или не дать другим прерываниям отработать.
- АЦП измеряет значение каждые ~28 тактов ADC_Clock. ADC_Clock max = 14МГц
- Вход-выход в прерывание в Cortex-M3 вроде по 6 (min) / 12 (max) тактов CPU_Clock. CPU_Clock max = 80МГц
Не так уж много времени на обработку в предельном случае.
Большое спасибо. Как-то ДМА сложно дается, уже неделю медитирую над этим модулем. Но благодаря форуму, вроде несколько продвинулся.

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


Вернуться к началу
 Заголовок сообщения: Re: АЦП через ДМА на 1986ВЕ9
СообщениеДобавлено: 2021-авг-02 11:51 
Не в сети
Support
Аватара пользователя

Зарегистрирован: 2018-авг-10 23:14
Сообщения: 530
Организация: АО «ПКК Миландр»
Откуда: Зеленоград
Eliot456 писал(а): *
StartMilandr писал(а): *
В качестве примера, есть проект использования Ping-pong режима DMA для вычисления среднего значения с АЦП.
код: https://github.com/StartMilandr/MDR_Pac ... ong/main.c
комментарий: https://github.com/StartMilandr/MDR_Pac ... C_PingPong
Функции не из официального пака, но для понимания общей логики думаю это не принципиально.

АЦП можно настроить на перебор 10-ти каналов, если усреднение по 10 значений, то цикл DMA настраивается на 10 * 10 = 100 передач.
После этих 100 передач возникает прерывание DMA, по нему выставляется флаг для обсчета усреднений в основном цикле.
АЦП с DMA во время рассчета могут копить данные во второй массив. Но если быстродействие не позволит, то можно и без пинг понга.
Таймер не понял зачем нужен.

Обработчик прерывания желательно делать максимально коротким, иначе можно пропустить следующее прерывание или не дать другим прерываниям отработать.
- АЦП измеряет значение каждые ~28 тактов ADC_Clock. ADC_Clock max = 14МГц
- Вход-выход в прерывание в Cortex-M3 вроде по 6 (min) / 12 (max) тактов CPU_Clock. CPU_Clock max = 80МГц
Не так уж много времени на обработку в предельном случае.
Большое спасибо. Как-то ДМА сложно дается, уже неделю медитирую над этим модулем. Но благодаря форуму, вроде несколько продвинулся.

P. S. Таймер нужен для периодичности измерений. Так как информация будет предоставляться по запросу с мастера, не вижу смысла постоянно измерять напряжение.
Здравствуйте!

Поддерживаю участников форума с их предложениями в пользу решения Вашей задачи, тем не менее, в пользу Вашего кода, опубликованного с утра, стоит сказать, что его работа не будет выполняться правильно, поскольку когда в прерывании по системному таймеру для параметра isWriteResultADC выставится значение true, на выходе из этого прерывания микроконтроллер не успеет отправиться в бесконечный цикл while(1) для проверки условия выставления данного параметра в значение true и запуска новых транзакций по DMA, он зайдет вновь в прерывание от DMA и обнулит параметр - таким образом новые преобразования никогда не начнутся. Дело всё в том, что для микроконтроллеров из семейства 1986ВЕ9х имеет место ошибка 0007 "Немаскируемый запрос передачи DMA от контроллера АЦП" из файла errata, вследствие которой контроллер DMA будет взводить сигнал dma_done (прерывание от DMA), тем самым вызывая обработку прерывания от DMA, даже когда контроллер не настроен на работу. Необходимо учитывать эту ошибку при разработке собственного ПО, парировать при помощи построения алгоритма обработки АЦП через прерывания DMA, либо через передачи DMA (подробнее в файле errata на семейство микроконтроллеров 1986ВЕ9х). Спасибо.

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

Информационный портал отдела технической поддержки support.milandr.ru


Вернуться к началу
Показать сообщения за:  Поле сортировки  
Начать новую тему  Ответить на тему  [ 21 сообщение ]  На страницу 1 2 »

Часовой пояс: UTC+03:00


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

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость


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

Перейти: 

cron
Создано на основе phpBB® Forum Software © phpBB Limited
Русская поддержка phpBB