Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

stm32f103 Dual mode ADC+DMA по таймеру4

Вт ноя 22, 2022 14:28:08

Всем привет! с недавних пор решил потихоньку изучать STM32. И поставил себе задачу потренироваться в парном режиме измерения АЦП с запуском по таймеру. Частота измеряемого синусоидального сигнала 100Гц, 256 измерений на период входящего сигнала, исходил что 100Гц=10000uS, значит 10000/256 = 39,0625uS или 25600Гц.
Конечно пользуюсь пока Кубом, в дальнейшем может перейду на Кейл.
1. Настройка Таймера4
Channel4 > Output Compare No Output
Prescaler > 2187 (56000000/25600Гц=2187)
Counter Period > 1
Mode > Toggle on match
2.Настройка АЦП.
ADC1:
- ADCs_Common_Settings > Dual regular simultaneous mode only
- Выравнивание по правому краю
- Extrernal Trigger ConversionMode > Timer 4 Capture Compare 4 event
- ADC2 в таком случае подчинен ADC1.
NVIC settings: ADC1 and ADC2 global interrupts галочка
DMA settings:
- ADC1 DMA1 Channel 1
- Mode>Normal и полное Word
Вообщем в отладке я еще хоть как то могу первые значения обоих АЦП увидеть что они верные, а дальше почему то завышает(( что делаю не так?
Спойлер
Код:
/* USER CODE BEGIN PV */
char trans_str[63] = {0,}; // буфер для UART1
volatile uint16_t adc[2] = {0,}; // переменные для АЦП
volatile uint16_t adc1[NPT];
volatile uint16_t adc2[NPT];
volatile uint16_t cnt_NPT = 0;
volatile uint8_t flag = 0;
/* USER CODE END PV */

..........................................

/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
   if(hadc->Instance == ADC1 && !flag) { //Если преобразование закончено и флаг в основном цикле был сброшен
      if(cnt_NPT < NPT) {
         adc1[cnt_NPT] = adc[0]; //запомнили значения для первого канала АЦП1
         adc2[cnt_NPT] = adc[1]; //запомнили значения для второго канала АЦП2
         adc[0] = 0; //сбросили значения
         adc[1] = 0;   //сбросили значения
         cnt_NPT++;
          } else {flag = 1; cnt_NPT = 0;} //ставим флаг что результат можно забирать
       }
}
/* USER CODE END 0 */
..........................................

  /* USER CODE BEGIN 2 */
  HAL_ADCEx_Calibration_Start(&hadc1);                     //запуск автоматической самоккалибровки АЦП1
  HAL_ADCEx_Calibration_Start(&hadc2);                     //запуск автоматической самоккалибровки АЦП2
  HAL_ADC_Start(&hadc2);                              //запустить работу АЦП2
  HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)&adc, 1);     //АЦП1 в мульти-режиме
  HAL_TIM_OC_Start(&htim4, TIM_CHANNEL_4);                   //запустить рабуту таймера 4
  /* USER CODE END 2 */

  /* USER CODE BEGIN WHILE */
  while (1)
  {
     if(flag)          //Если флаг 1, значит результат измерения АЦП1 и АЦП2 готов
      {
        flag = 0;
        for (uint16_t i=0; i<NPT; ){
        snprintf(trans_str, 63, "%d  %d %d\r\n", i, adc1[i], adc2[i]);
        HAL_UART_Transmit(&huart1, (uint8_t*)trans_str, strlen(trans_str), 35);
        i++;   //инкрементируем после вывода инфы
        }
        //цикл пробежали, запускает АПЦ1 и АЦП2
        //HAL_ADC_Start(&hadc2);                              //запустить работу АЦП2
        //HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)&adc, 1);   //длина равна 1 так как в DMA указано целое «слово»
      }else{         //Если флаг 0, запускает АПЦ1 и АЦП2
         //HAL_ADC_Start(&hadc2);                              //запустить работу АЦП2
         //HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t*)&adc, 1);   //длина равна 1 так как в DMA указано целое «слово»
      }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC1_Init 1 */

  /* USER CODE END ADC1_Init 1 */

  /** Common config
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T4_CC4;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_DUALMODE_REGSIMULT;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */

  /* USER CODE END ADC1_Init 2 */

}

static void MX_ADC2_Init(void)
{

  /* USER CODE BEGIN ADC2_Init 0 */

  /* USER CODE END ADC2_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC2_Init 1 */

  /* USER CODE END ADC2_Init 1 */

  /** Common config
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.NbrOfConversion = 1;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC2_Init 2 */

  /* USER CODE END ADC2_Init 2 */

}

static void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 2187;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 1;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_OC_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */

}

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Ср ноя 23, 2022 08:59:31

"завышает" - не гибком конкретно.
Но очень вероятно, что ошибки нет и то что видите это реальность на f103

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Ср ноя 23, 2022 20:34:52

a797945 писал(а):Но очень вероятно, что ошибки нет и то что видите это реальность на f103

Неё... Пока без синусоиды, просто подал 3.3в через разные резисторы adc1 и adc2. И реальные значения ацп я знаю. Эти же значения только в первом измерении в массиве под номером [0], потом остальные 255 завышены, что в отладке смотришь массив измерений, что в уарте выводишь его. Завышены на много вместо 2700 выдаёт 3600. Потом код повторяется и тоже самое только первое измерение обоих каналов верно. Ну что постоянно калибровку делать?

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Пт ноя 25, 2022 12:20:56

камень старый в сети полно статей и примеров, что Вам мешает с ними повнимательнее ознакомится.
расписано же когда нужен ОУ, какие номиналы RC цепочки на входе ацп, ... т.е. не обеспечена аппаратная часть.
зачем счетчику для работы оставили только два такта - только шагнул и тут же сброс - зачем так?
режим канала таймера Toggle ( т.е. переворачивание) - а значит на один период adc должно быть 2 периода таймера. если соотв. ногу настроить - можно проконтролировать, что период выдается правильный.
период для измерения, сами насчитали, 39,0625uS - зачем в измерении ужиматься до 1мкс, примените 13.5 (12,5+13,5) т.е. время до следующей выборки остается (а так же может не понадобится ОУ). цифры если такт adc - 14МГц.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Пт ноя 25, 2022 15:01:30

камень старый в сети полно статей и примеров, что Вам мешает с ними повнимательнее ознакомится.
расписано же когда нужен ОУ, какие номиналы RC цепочки на входе ацп, ... т.е. не обеспечена аппаратная часть.

Поделитесь если у вас есть что в загашнике? может не совсем по тем примерам иду что нашёл! пока читаю книгу Освоение STM32. Я всегда рад пересмотреть свой подход!

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Пт ноя 25, 2022 18:08:38

сорри, но обучать я не могу - я не спец., у самого предостаточно пробелов в знаниях.
да и моим путем идти не надо - цели профессионально осваивать МК себе не ставил, староват я для такой цели.
режимом Dual regular simultaneous никогда не пользовался, кстати НАЛом то же.
часть мыслей озвученных ранее - просто из здравого смысла, про другую часть можете почитать ну, например, здесь :
https://dzen.ru/media/tdmlab/stm32-i-cu ... 055a94cb98
и в конце статьи достаточно ссылок на документы.

если что-то не получается - разбиваю задачу на более мелкие и проверяю так ли они работают, если где заткнусь ищу ответы или в черепной коробке или в статьях и документах.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб ноя 26, 2022 11:33:41

зачем счетчику для работы оставили только два такта - только шагнул и тут же сброс - зачем так?

вы об этом куске кода? правильно вас понял?
Спойлер
Код:
/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
   if(hadc->Instance == ADC1 && !flag) { //Если преобразование закончено и флаг в основном цикле был сброшен
      if(cnt_NPT < NPT) {
         adc1[cnt_NPT] = adc[0]; //запомнили значения для первого канала АЦП1
         adc2[cnt_NPT] = adc[1]; //запомнили значения для второго канала АЦП2
         adc[0] = 0; //сбросили значения
         adc[1] = 0;   //сбросили значения
         cnt_NPT++;
          } else {flag = 1; cnt_NPT = 0;} //ставим флаг что результат можно забирать
       }
}
/* USER CODE END 0 */


период для измерения, сами насчитали, 39,0625uS - зачем в измерении ужиматься до 1мкс, примените 13.5 (12,5+13,5) т.е. время до следующей выборки остается (а так же может не понадобится ОУ). цифры если такт adc - 14МГц.

одна микросекунда поставил с заделом, к примеру - реализовать ДПФ алгоритм и там быстродействие важно все же с определение фазы. Но это пока взгляд на будущее где к примеру можно применить парный режим измерения.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб ноя 26, 2022 12:35:17

это вы же ?

"вы об этом куске кода?" нет, об этом:
" htim4.Init.Prescaler = 2187;
htim4.Init.Period = 1; "
но портянку хала смотрел по "диагонали", он для меня непривычен.

используете быстрое преобразование 1,5 (12,5+1,5) - необходим буферный ОУ,
по ссылкам расписано почему и когда нужен.

натыкался вчера на коды аналогичные вашей задаче - можете взять какой-нибудь за основу и поэтапно привести к нужному вам механизму. или проанализировать отличия и исправить ваш, обращая внимание на аппаратную составляющую.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб ноя 26, 2022 12:54:27

a797945 писал(а):это вы же ?

нет пованговал за автора(извениет что влез) и привел пример, и мне тоже интересен это процесс опроса ацп в режиме dual. Для меня интересен алгоритм ДПФ и применение для него такого режима измерения ацп.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб ноя 26, 2022 13:03:25

да извиняться не за что. я переспросил - по тексту было не понятно.

даете поисковику "Dual regular simultaneous примеры", выбираете понятный вам вариант, от него и "пляшете".

я такой режим не применял, юзал fast interleaved без таймера (и понятно не на хале :) ).

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб ноя 26, 2022 21:33:57

1.htim4.Init.Prescaler = 2187;
htim4.Init.Period = 1;
................
2.натыкался вчера на коды аналогичные вашей задаче - можете взять какой-нибудь за основу и поэтапно привести к нужному вам механизму. или проанализировать отличия и исправить ваш, обращая внимание на аппаратную составляющую.

1. это я начудил конечно, перечитал по таймерам, в моем случае желаемую частоту в 25600Гц(39,0625uS) получить нужно сделать так:
Prescaler > 0
Counter Period > 2187 (56000000/25600Гц=2187)

2. в принципе так и делаю, по поводу аппаратной части тоже подтягиваю себя сейчас!

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб ноя 26, 2022 21:44:28

одна микросекунда поставил с заделом, к примеру - реализовать ДПФ алгоритм и там быстродействие важно все же с определение фазы. Но это пока взгляд на будущее где к примеру можно применить парный режим измерения.

Стесняюсь спросить, а зачем для ДПФ парный режим и какую фазу вы собрались определять с каким то там быстродействием?
Для ДПФ всего то нужно выбрать частоту дискретизации с учетом потребного подавления неиспользуемых зон Найквиста. И всё.
Фазовый спектр в ДПФ получается относительно синусно-косинусных базисов. Никакой "парности" измерений там не требуется.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс ноя 27, 2022 11:07:32

kote52, вообще перевёл Халовский юзер мануал по ф103, то что касается парного ацп в мультирежиме, там прямым текстом написано что ведомый ацп2 останавливать HAL_ADC_Stop_IT(&hadc2); а у вас HAL_ADC_Stop(&hadc2); и закоментировано.
Stop conversion and disable the ADC HAL_ADC_Stop_IT(&hadc2) (slave) using function HAL_ADC_Stop_IT()
А стартует как у вас в коде в мульти режиме с ДМА ведомый HAL_ADC_Start();.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Сб дек 03, 2022 12:38:36

Фазовый спектр в ДПФ получается относительно синусно-косинусных базисов. Никакой "парности" измерений там не требуется.

не знаю что там имел ввиду там человек, но мы с вами в одной из тем говорил как то о дпф... я поднял старую ветку, вы отозвались... у меня вопрос сейчас в чем?... тогда если не парность, т.е. получается нужно как то одновременно синхронизировать DAC и ADC, что бы минимизировать тот промежуток времени который может возникнуть между началом работы DAC и началом измерения ADC? Сэмплировать и DAC и оба ADC от одного таймера одной частоты? так получается?
И можете объяснить почему не важно "с каким то там быстродействием?" По другому сформулирую от себя, т.е. 1uS время преобразования ADС у STM32 или 13uS у Atmega, роли не сыграют?

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс дек 04, 2022 13:09:49

Основным принципом при выборе частоты дискретизации является верхняя частота спектра входного сигнала и требования к антиалиасингу.
Поскольку у ЦАПа нет времени преобразования, а есть время переходного процесса и оно зависит от перепада входного кода, то выводить в ЦАП можно по завершении преобразования АЦП и без таймера.
Только непонятно зачем весь этот перфоманс?
А если имеется обработка перед выводом, то время преобразования перестает быть значимым. Ну и потребуется семплировать ЦАП от таймера.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс дек 04, 2022 15:15:26

КРАМ писал(а):Только непонятно зачем весь этот перфоманс?
А если имеется обработка перед выводом, то время преобразования перестает быть значимым. Ну и потребуется семплировать ЦАП от таймера.

да какой тут перфоманс?)) я пытаюсь нащупать как делать правильно что изложил в начале темы - "измерения АЦП с запуском по таймеру. Частота измеряемого синусоидального сигнала 100Гц, 256 измерений на период входящего сигнала". И тут в принципе парный режим может вообще не нужен, ну хотя бы я его худо бедно покрутил и получилось. Научился сейчас запускать то таймеру и получил массив из 256 измерений на частое 25,6кГц. Единственное что мне не нравится, я сразу забираю из ДМА результат после каждого преобразовании АЦП, а можно сделать весь массив заполнился и только потом... или еще - набрать к примеру 100 таких массивом(но в этом я пока не уверен возможно ли :dont_know: ) и потом забрать из ДМА для обработки.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс дек 04, 2022 15:33:26

Единственное что мне не нравится

А зачем ДМА, если вы забираете каждое измерение?
Вообще то нужно при каждом запуске сессии ДМА, если речь идет о one shot mode, ставить нужное значение в счетчик ДМА. Или однократно, если речь идет о continuous mode.
Тогда прерывание по ДМА возникнет либо по завершении транзакций по всему массиву, либо по каждой половине - зависит от разрешений.

Частота измеряемого синусоидального сигнала 100Гц, 256 измерений на период входящего сигнала.

Что вы хотите измерить у синусоидального сигнала при такой высокой частоте дискретизации? Даже при 16 выборках за период антиалиасинговый фильтр будет очень простым и вы получите амплитуду и фазу сигнала с хорошей точностью. Если нет опасности получить паразитный сигнал на частотах 2-й и выше зон Найквиста, то достаточно и удобно иметь ровно 4 выборки на период. Вообще то хватит и БОЛЕЕ, чем двух.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс дек 04, 2022 16:15:40

А зачем ДМА, если вы забираете каждое измерение?

Пока так буду дальше двигаться.
Что вы хотите измерить у синусоидального сигнала при такой высокой частоте дискретизации? Даже при 16 выборках за период антиалиасинговый фильтр будет очень простым и вы получите амплитуду и фазу сигнала с хорошей точностью. Если нет опасности получить паразитный сигнал на частотах 2-й и выше зон Найквиста, то достаточно и удобно иметь ровно 4 выборки на период. Вообще то хватит и БОЛЕЕ, чем двух.

да ни чего не измеряю, поставил себе задачу, все опять же из первого моего сообщения, вот. Задача то более чем интересная, что мне общие примеры с ацп и дма. В такой задаче есть что покрутить и увидеть в работе на практике. Тут нет каких-то измерений чего-то или проектирования чего-то... то что раньше о ДПФ спрашивал, возможно эти знания применить в ДПФ и там обработки сигнала какого-то... но пока просто учусь. Ну вы же видете я плаваю в обычной теории и почти ни чего не знаю.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс дек 04, 2022 16:31:19

то что раньше о ДПФ спрашивал

Вот я вам в контексте ДПФ/БПФ и ответил. 100 выборок за период - это оверсемплинг. Его обычно применяют для увеличения разрядности АЦП. При сохранении разрядности в оверсемплинге нет никакого смысла и выбор частоты дискретизации производится на основании требований к антиалиасинговому фильтру ПЕРЕД АЦП. Вторая зона Найквиста (первый зеркальный диапазон частот) лежит между половиной частоты дискретизации и частотой дискретизации. Сигнал оказавшейся в этой зоне будет отражен в первую, то есть вы не сможете его отличить от сигнала в первой зоне. Поэтому нужно делать ФНЧ на входе АЦП, который подавит сигнал на половине частоты дискретизации и выше нее на величину динамического диапазона АЦП, то есть сигнал в паразитных зонах (второй и выше) будет лежать ниже шума квантования.
Однако, если качество УВХ в АЦП (так называемая апертура или полоса пропускания) позволяют, то можно работать в режиме даунсемплинга (стробоскопическом). И тогда на входе ставят ПОЛОСОВОЙ фильтр вырезающий необходимую зону Найквиста выше первой.

Re: stm32f103 Dual mode ADC+DMA по таймеру4

Вс дек 04, 2022 17:30:53

Вообще то нужно при каждом запуске сессии ДМА, если речь идет о one shot mode, ставить нужное значение в счетчик ДМА. Или однократно, если речь идет о continuous mode.
Тогда прерывание по ДМА возникнет либо по завершении транзакций по всему массиву, либо по каждой половине - зависит от разрешений.

Все вас в ДПФ уводит, оставьте его в покое... лучше вернемся к проблеме с кодом! можно как то подробнее о счетчике, куда именно поставить...
Ответить