Перейти к содержанию
    

Здравствуйте!
Имеется отладочная плата с STM32F303. Задача: измерять импульсный сигнал длинной 100 мкс и частотой 1000 Гц и выводить измерения на ПК.

Запускаю два таймера. Первый имитирует измеряемый сигнал.

Спойлер
void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 7;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 1000;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 100;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.BreakFilter = 0;
  sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
  sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
  sBreakDeadTimeConfig.Break2Filter = 0;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */
  HAL_TIM_MspPostInit(&htim1);

}

 

Второй "толкает" АЦП

Спойлер
void MX_TIM3_Init(void)
{

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};


  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 7;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 100;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

 

Измеряю через внутренний 12-битный АЦП с использованием ДМА и запуском по таймеру.

Спойлер
void MX_ADC2_Init(void)
{

  ADC_ChannelConfTypeDef sConfig = {0};

  /** Common config
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc2.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DMAContinuousRequests = DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.SamplingTime = ADC_SAMPLETIME_4CYCLES_5;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

/* ADC2 DMA Init */
    /* ADC2 Init */
    hdma_adc2.Instance = DMA2_Channel1;
    hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc2.Init.Mode = DMA_CIRCULAR;
    hdma_adc2.Init.Priority = DMA_PRIORITY_MEDIUM;

 

и сохраняю измерения в массив. Затем этот массив через ДМА передаю ЮАРТУ и вывожу на ПК.

Спойлер
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */

/* USER CODE BEGIN PV */
char trans_str[100];
volatile uint16_t dark1[10];
volatile uint8_t flag = 0;
/* USER CODE END PV */

/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) // вызов коллбэка после измерения АЦП
{
    if(hadc->Instance == ADC2)
    {
      flag = 1; // поднятие флага по коллбэку
	  HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8);
    }
}
/* USER CODE END 0 */

int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_DMA_Init();
  MX_ADC2_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	
	HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED); // калибровка АЦП
	HAL_TIM_PWM_Start (&htim1, TIM_CHANNEL_1); // запуск импульсного сигнала
	
	HAL_ADC_Start_DMA(&hadc2, (uint32_t *)dark1, 10); // запуск АЦП
	HAL_TIM_Base_Start(&htim3); // запуск триггера
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(flag == 1)
		{
			flag = 0; // опускаем флаг
		
			HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8); // переключение СД для проверки работы АЦП
			
			HAL_ADC_Stop_DMA(&hadc2); // остановка АЦП для работы ЮАРТА
			
			
		}
		else if (flag == 0)
		{
			// формируем строку из массива
			snprintf(trans_str, 61, "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d;\n", dark1[0], dark1[1], dark1[2], dark1[3], dark1[4], dark1[5], dark1[6], dark1[7], dark1[8], dark1[9] );
			HAL_UART_Transmit_DMA(&huart1, (uint8_t*)trans_str, strlen(trans_str)); // передача строки на ПК
			
			HAL_ADC_Start_DMA(&hadc2, (uint32_t*)dark1, 10); // запуск АЦП
			
		}


    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

На выходе получаю поток невнятных чисел. Помогите разобраться в чем ошибка.

 

22.PNG

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 minutes ago, NefoRalt said:

длинной 100 мкс и частотой 1000 Гц

Это как? Изобразите графически, чтобы не было разночтения.

23 minutes ago, NefoRalt said:

Задача: измерять

Измерять что? Пиковое значение? Среднеквадратическое? Спектр?

24 minutes ago, NefoRalt said:

Помогите разобраться в чем ошибка.

Добавьте ещё схему перед входом АЦП. Обычно там должен быть ФНЧ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

57 минут назад, haker_fox сказал:

Это как? Изобразите графически, чтобы не было разночтения.

22.thumb.PNG.918ec36e2d13d616253d0c887863e8db.PNG

59 минут назад, haker_fox сказал:

Измерять что? Пиковое значение? Среднеквадратическое? Спектр?

За длительность одного импульса (100 мкс) сделать 10 измерений напряжения -> заполнить массив из 10 элементов 

 

1 час назад, haker_fox сказал:

Добавьте ещё схему перед входом АЦП. Обычно там должен быть ФНЧ.

Ничего нет, выход сигнала напрямую соединен с АЦП

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 minutes ago, NefoRalt said:

Ничего нет, выход сигнала напрямую соединен с АЦП

А надо бы антиалиасинговый фильтр.

1 hour ago, NefoRalt said:

Первый имитирует измеряемый сигнал.

Осциллографом убедились, что сигнал действительно присутствует на ножке АЦП с таймера?

1 hour ago, NefoRalt said:

Затем этот массив через ДМА передаю ЮАРТУ и вывожу на ПК.

Попробуйте сначала посмотреть его содержимое через отладчик. Это несколько сократит путь данных от таймера до ваших глаз. Исключится два звена с потенциальной ошибой: настройка ПДП и настройка УАСПП.

P.S. Я не использую разные библиотеки драйверов для работы с периферией. Пишу их самостоятельно, глядя в документацию. В Вашем случае причин может быть масса: ошибка в драйверах (посмотрите содержимое регистров периферии после инициализации и сравните с документацией), неправильное понимание работы периферии и т.п. Можно добавить прерыване по завершению преобразования АЦП и отладчиком посмотреть коды преобразования, убедиться в их правильности. Затем, включить прерывание по завершениею транзакции ПДП, посмотреть, что скопировано в буфер. И т.д. Т.е. идти итерационно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/17/2022 at 10:44 AM, NefoRalt said:

Запускаю два таймера. Первый имитирует измеряемый сигнал.

Второй "толкает" АЦП

 

На выходе получаю поток невнятных чисел. Помогите разобраться в чем ошибка.

У вас эти таймеры как-то синхронизированы между собой ?

Если между ними синхронизации нет, то измерения будут попадать в произвольную точку имитируемого сигнала и, как следствие, на выходе АЦП 

поток случайных чисел.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 часа назад, haker_fox сказал:

Осциллографом убедились, что сигнал действительно присутствует на ножке АЦП с таймера?

Да. сигнал есть

23 часа назад, haker_fox сказал:

Попробуйте сначала посмотреть его содержимое через отладчик. Это несколько сократит путь данных от таймера до ваших глаз. Исключится два звена с потенциальной ошибой: настройка ПДП и настройка УАСПП.

Проверял через отладчик. Данные, сохраняемые в массив, и данные, выводимые на ПК одинаковы. АЦП и передача работают корректно. 

22 часа назад, dimka76 сказал:

У вас эти таймеры как-то синхронизированы между собой ?

Спасибо за подсказку. Добавил третий таймер, запускающий первые два. Первый запускается нормально, а вот со вторым непонятки. АЦП все равно косо измеряет. 

И я не понимаю, то ли таймер косо запускается, то ли запуск АЦП криво закодил

Изменено пользователем NefoRalt

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 минуты назад, NefoRalt сказал:

И я не понимаю, то ли таймер косо запускается, то ли запуск АЦП криво закодил

Махайте ногами через модули сравнения (compare) обоих таймеров и смотрите осциллографом/лог. анализатором.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/18/2022 at 11:51 AM, NefoRalt said:

Спасибо за подсказку. Добавил третий таймер, запускающий первые два. Первый запускается нормально, а вот со вторым непонятки. АЦП все равно косо измеряет. 

Включите прерывание АЦП, а в его обработчике дергайте любой ножкой.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 часов назад, Сергей Борщ сказал:

Махайте ногами через модули сравнения (compare) обоих таймеров и смотрите осциллографом/лог. анализатором.

 

19 часов назад, dimka76 сказал:

Включите прерывание АЦП, а в его обработчике дергайте любой ножкой.

Сделал переключение пина после измерения АЦП:

Спойлер
/* USER CODE BEGIN 0 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC2)
    {
      flag = 1; // поднимаем флаг
	  HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8); // включаем пин
    }
}
/* USER CODE END 0 */

  while (1)
  {
		if(flag == 1)
		{
			flag = 0; // опускаем флаг
		
			HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8); // выключаем пин
			
			
			HAL_ADC_Stop_DMA(&hadc2);
			snprintf(trans_str, 61, "%d, %d, %d, %d, %d, %d, %d, %d, %d, %d;\n", dark1[0], dark1[1], dark1[2], dark1[3], dark1[4], dark1[5], dark1[6], dark1[7], dark1[8], dark1[9] );
			HAL_UART_Transmit_DMA(&huart1, (uint8_t*)trans_str, strlen(trans_str));
			
			HAL_ADC_Start_DMA(&hadc2, (uint32_t*)dark1, 10);
			
			
		}
  }

 

Так это выглядит на осциллографе: синий - переключаемый пин, желтый - измеряемый сигнал. По идее, синий должен всегда идти вместо с желтым, но это не так и я не понимаю почему.

NewFile0.jpg.5b52473adb6107a0ea96b1096ba5f88c.jpgNewFile0.jpg.8a4ec6fe18b84a7e91535844402c9f67.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/19/2022 at 8:44 AM, NefoRalt said:

Сделал переключение пина после измерения АЦП:

Toggle - это переключение - изменение состояния.

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

А в main этот пин вообще не трогать.

On 8/19/2022 at 8:44 AM, NefoRalt said:

Так это выглядит на осциллографе: синий - переключаемый пин, желтый - измеряемый сигнал. По идее, синий должен всегда идти вместо с желтым, но это не так и я не понимаю почему.

NewFile0.jpg.8a4ec6fe18b84a7e91535844402c9f67.jpg

Т.е. вы хотите сказать, что у вас измерение происходит не на каждом желтом импульсе ?

Это уже вам надо искать ошибки в программе. 

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

Вам лучше делать запуск АЦП не по таймеру, а по вашему измеряемому сигналу. Т.е. завести измеряемый сигнал не только на вход АЦП, а еще

и на вход внешнего прерывания, и вот по этому прерыванию должен происходить запуск АЦП.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, dimka76 said:

Т.е. завести измеряемый сигнал не только на вход АЦП, а еще

и на вход внешнего прерывания

Гм.. в реальной жизни импульс же может быть далеко "не импульсом": скорость нарастания/спада фронтов, помехи. Автору придётся городить схему, подобную схеме внешнего запуска у осциллографа или вольтметра. Хотя, это и правильно. В протвном случае, наличие импульса придётся искать полностью программным способом.

 

Автор) Вам бы взять лабораторный генератор вместо таймера и иметь настоящий реальный сигнал со всеми присущими ему недостатками. Заодно и не нужно будет лишнюю работу выполнять по запуску второго таймера.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 8/19/2022 at 10:24 AM, haker_fox said:

В противном случае, наличие импульса придётся искать полностью программным способом.

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

Так что, синхронизация с измеряемым сигналом может оказаться более выгодной.

Но это все гадание на кофейной гуще, т.к. мы не знает всех исходных данных.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

35 minutes ago, dimka76 said:

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

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

35 minutes ago, dimka76 said:

Но это все гадание на кофейной гуще, т.к. мы не знает всех исходных данных.

Это верно!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 19.08.2022 в 13:12, dimka76 сказал:

Вам лучше делать запуск АЦП не по таймеру, а по вашему измеряемому сигналу. Т.е. завести измеряемый сигнал не только на вход АЦП, а еще

и на вход внешнего прерывания, и вот по этому прерыванию должен происходить запуск АЦП.

Поставил запуск АЦП по измеряемому сигналу, а также добавил переключение ножки МК по прерыванию ДМА.

Действительно, картина стала яснее. АЦП измеряет чаще чем, чем это было видно по предыдущему коду. 

NewFile0.jpg.7ff00db3a5533430233f109bbda5e3fd.jpg

В 19.08.2022 в 14:24, haker_fox сказал:

Автор) Вам бы взять лабораторный генератор вместо таймера и иметь настоящий реальный сигнал со всеми присущими ему недостатками. Заодно и не нужно будет лишнюю работу выполнять по запуску второго таймера.

 

В 19.08.2022 в 15:30, dimka76 сказал:

Но это все гадание на кофейной гуще, т.к. мы не знает всех исходных данных.

Вопрос сейчас стоит не в синхронизации таймеров, а в том, как заставить АЦП измерять чаще. 

NewFile0.jpg.354b75ed06362e0f6b0dedfa13cb826a.jpg

По хорошему, надо, чтобы АЦП (синий сигнал), измерял столько же раз, сколько раз появляется импульс (желтый сигнал). Или даже в два раза чаще.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, NefoRalt said:

измерял столько же раз, сколько раз появляется импульс

Непонятно. Раз у Вас АЦП запускается теперь по измерительному сигналу, почему это происходит только раз в 10 мс? Преобразование одного канала (даже нескольких) происходит гораздо быстрее... Куда тратится это время, на что?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...