ADC DMA

Рассмотрим на конкретном примере работу ADC DMA в микроконтроллерах STM32. Настраивать периферию будем с помощью CubeMX, писать код в CubeIDE. Ну и конечно использовать ADC HAL. Решим следующую задачу: необходимо вычислить RMS сигнала с частотой, скажем 512 Гц. Так же у нас есть сигнал начала измерения. У ADC задействовано 2 канала, на 1 заведен наш сигнал частотой 512 Гц, на второй сигнал с датчика.
Как уже понятно из заголовка статьи читать ADC будем с помощью DMA. Использовать будем микроконтроллер STM32F446RET.

Настройка ADC и DMA в CubeMX

Для начала нам необходимо настроить ADC и DMA в CubeMX для правильной работы в дальнейшем.

Настройка ADC в CubeMX

Вкладка с настройками ADC в CubeMX выглядит следующим образом:

Настройка ADC в CubeMX

Как видим у нас выбраны 2 канала ADC1: IN10 и IN11. Теперь рассмотрим каждую установку:

  • Mode — режим работы ADC. Если задействован только один блок ADC, то в настройках только 1 режим Independent mode.
  • Clock Prescaler — делитель частоты тактирования ADC. Здесь все просто, чем меньше делитель, тем больше частота.
  • Resolution — здесь настраиваем разрядность ADC. Можно выбрать от 6 до 12 бит. Чем выше нужна точность, тем больше выставляем разрядность.
  • Data Alignment — Выравнивание результата преобразования в регистре данных. Справа налево или наоборот.
  • Scan Conversion Mode — режим сканирования. Так как у нас 2 канала ADC, включаем режим сканирования.
  • Сontinuous Conversion Mode — активировав данный режим, ADC , будет работать в циклическом режиме. Это значит, что после завершения преобразования двух каналов, будет автоматически стартовать новое преобразование. Также наш DMA будет настроен на режим Circular, по этому этот пункт необходимо обязательно включить. Иначе мы получим только одно значение.
  • Discontinuous Conversion Mode — Противоположное значение предыдущему пункту. Его конечно отключаем.
  • DMA Continuous Requests — активирует непрерывное обращение к DMA. Работает в связке с настройкой DMA Circular и Сontinuous Conversion Mode.
  • End Of Conversion Selection — настраиваем, когда поднимется флаг завершения преобразования, после измерения каждого канала или по завершению преобразования на всех каналах. Нам необходим второй вариант.
  • Number Of Conversion — количество каналов для преобразования. У нас 2 регулярных канала.
  • External Trigger Conversion Sourse — внешний тригер старта преобразования. У нас старт будет производится программно. Можно настроить и по таймеру.
  • External Trigger Conversion Edge — автоматически активируется значение None, если будет программный старт.
  • Rank — здесь настраиваем очередность чтения каналов ADC. Опрос происходит не по номеру канала, а по номеру Rank.
  • Chanel — присваиваем ранку номер канала.
  • Sampling Time — время сэмплирования данного канала.

Настройка ADC DMA в CubeMX

После настройки ADC переходим на вкладку DMA Settings.

Настройка ADC DMA в STM32

Нажав на кнопку Add, выбираем ADC1. Далее выбираем режим Circular, это значит DMA будет работать непрерывно в циклическом режиме, пока пользователь не остановит работу. Так же необходимо настроить размер данных (Data Width), выбираем Word (32 бита).

Пример кода ADC с DMA

Теперь поговорим про написание кода, для решения нашей задачи. Решать ее будем так:запишем в массив значения ADC за один период, а затем посчитаем среднеквадратичное значение.

Посчитаем сколько мы сможем сделать измерений за один период с учетом наших настроек ADC.

Время на одно преобразование будет равно сумме преобразования на каждый канал плюс время на преобразование 12 бит, а так же 12,5 циклов на общие нужды (по даташиту).

56+56+12 + 12.5 = 136.5

Так как, наш ADC1 размещен на шине APB2 с частотой 45 МГц. С учетом делителя на 2 получаем 22,5МГц.

Разделив 136,5 на 22500000 получим значение 6.066 us (микросекунд). Это значит что одно преобразование двух каналов ADC равно этому времени.

Теперь посчитаем сколько сможем сделать преобразований за один период 512 герц.

1953,125/6,06 = 322,29 (322)

Получаем 322 измерения за период. Так как у нас 2 канала нам нужен массив на 644 значения. Ниже представлен пример программного кода:

#define ADC_REFERENCE_VOLTAGE                3.102
#define ADC_MAX                              0xFFF

uint32_t AllCounterMesh = 644;
uint32_t ArrayOfADC_DMA[644]  = {0,};

//на эту ножку подключен сигнал начала преобразования
if(GPIO_Pin == GPIO_PIN_11)
	{
		if (start_meshure!=0)
		{
			if ((adcFlag == false)&&(counterADC==0))
			{
                                // Стартуем ADC DMA
	   		        HAL_ADC_Start_DMA(&hadc1, ArrayOfADC_DMA, AllCounterMesh);
                                // Устанавливаем флаг начала преобразования
				adcFlag = true;
			}
			counterADC++;   // счетчик периодов
                        // Period равен 1, но можно указать больше, тогда нужно увеличить массив с данными с учетом количества периодов
			if (counterADC > Period)
			{
				counterADC = 0;
				HAL_ADC_Stop_DMA(&hadc1);
				
				//рассчитываем напряжения
				float sqr_Un = 0;
				float sqr_In = 0;
				for (uint32_t i = 0; i < AllCounterMesh - 2; i+=2)
				{
					sqr_In   = sqr_In + powf(ArrayOfADC_DMA[i],2.0);    //chanel 10
					sqr_Un   = sqr_Un + powf(ArrayOfADC_DMA[i+1],2.0);  //chanel 11
				}
				adcDataIn = sqrtf(sqr_In/(CounterMesh)); //CounterMesh = 322
				adcDataUn = sqrtf(sqr_Un/(CounterMesh));
				sqr_In = 0;
				sqr_Un = 0;
				adcUn = ADC_REFERENCE_VOLTAGE * adcDataUn/ADC_MAX;
				adcIn = (ADC_REFERENCE_VOLTAGE * adcDataIn/ADC_MAX);
				adcFlag = false;
			}
		}
	}
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
4 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Сергей
Сергей
2 месяцев назад

Автор, поясните откуда в коде взялась переменная adcDataUp, и что такое ADC_REFERENCE_VOLTAGE???

Сергей
Сергей
2 месяцев назад

Доброго времени суток. Еще один вопрос и ваш совет. Задача измерить переменное напряжение частотой 50 Гц. Мой вариант алгоритма — запускаю таймер частотой 20мс (50Гц), который в свое время толкает АЦП, который делает 200 замеров напряжения канала за это время опроса и по прерыванию отдает все это через ДМА в память, где проводятся вычисления RMS. В итоге у меня выходной результат на мониторе прыгает, хотя напряжение не меняется. Где ошибка??