stm32f10x - измерение частоты и амплитуды сигнала

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Родился
Аватара пользователя
Сообщения: 12
Зарегистрирован: Вт мар 01, 2016 12:22:22

Сообщение kapusta1986 »

Всем добрый день! Котаны и кошки! ) :beer:

Имею отладочную плату на базе камня stm32f10x (то бишь дискавери), с сайта (http://robocraft.ru/blog/ARM/739.html) - код автора заработал сразу. Получилось измерить частоту и ширину импульса. У меня вопрос, может ли стмка измерить амплитуду сигнала?

Ведь нельзя в прерывание таймера засунуть прерывание ADC ?

Отдельно запустил ADC для измерения того же самого сигнала, но он получается какой то устредненный, ровно половину от 3,3 В

Если подать постоянный сигнал, то получаю как положенно 4095 (то бишь 3.3В), а с частотой проблема.... собсвенно вот и вопрос: может ли стмка измерить амплитуду сигнала?

Спасибо! :beer:
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2708
Зарегистрирован: Сб май 14, 2011 21:16:04
Откуда: г. Чайковский

Сообщение Z_h_e »

kapusta1986 писал(а):Ведь нельзя в прерывание таймера засунуть прерывание ADC ?
Прерывание с более высоким приоритетом прервет выполнение текущего обработчика прерывания.
kapusta1986 писал(а):может ли стмка измерить амплитуду сигнала?
Программно то можно сделать. Я правда с STM еще очень мало игрался.
Изображение
Добро всегда побеждает зло. Поэтому кто победил - тот и добрый.
Реклама
Держит паяльник хвостом
Аватара пользователя
Сообщения: 993
Зарегистрирован: Пн сен 18, 2006 11:16:05
Откуда: Тула

Сообщение Neekeetos »

kapusta1986 писал(а):Ведь нельзя в прерывание таймера засунуть прерывание ADC ?
А зачем, если настроить триггер ацп на выход канала захвата таймера, то оно будет срабатывать когда пришел фронт/спад, в прерывании таймера можно будет подождать пока закончится преобразование и считать то что намерилось. Там будет еще задержка между самим фронтом входного сигнала и началом работы ацп, она примерно равна длине фильтра канала захвата плюс задержка старта ацп , еще пару тактов , а сам момент для которого сигнал будет измерен будет еще дальше отодвинут во времени - еще и на время выборки ацп, которое кстати лучше увеличить.
Информация по RLC mini находится >тут<
Контактная информация:
Родился
Аватара пользователя
Сообщения: 12
Зарегистрирован: Вт мар 01, 2016 12:22:22

Сообщение kapusta1986 »

Спасибо Neekeetos, с помощью товарища одного мне удалось собрать код

На низких частотах работает нормально. Из проблем у меня выявилось небольшое отклонение по вольтажу - 4095 значение при 2.8 В

Частоту мерит без проблем

Код: Выделить всё


#include <тут подключите библиотеки необходимые>

void init_gpio(void);
void init_timer(void);

uint16_t uint16_time_diff(uint16_t now, uint16_t before);

volatile uint16_t systick_ms = 0;
volatile uint16_t Tcapture1 = 0, Tcapture2 = 0;
volatile uint8_t T1capture_is_first = 1, T1capture_is_ready = 0;


uint32_t period;

uint32_t adcvalue;

void ADC_Configuration(void)
{
		NVIC_InitTypeDef NVIC_InitStructure;
		ADC_InitTypeDef  ADC_InitStructure;
		/* PCLK2 is the APB2 clock */
		/* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
		RCC_ADCCLKConfig(RCC_PCLK2_Div6);

		/* Enable ADC1 clock so that we can talk to it */
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
		/* Put everything back to power-on defaults */
		ADC_DeInit(ADC1);

		/* ADC1 Configuration ------------------------------------------------------*/
		/* ADC1 and ADC2 operate independently */
		ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
		/* Disable the scan conversion so we do one at a time */
		ADC_InitStructure.ADC_ScanConvMode = DISABLE;
		/* Don't do contimuous conversions - do them on demand */
		ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
		/* Start conversin by software, not an external trigger */
		ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
		/* Conversions are 12 bit - put them in the lower 12 bits of the result */
		ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
		/* Say how many channels would be used by the sequencer */
		ADC_InitStructure.ADC_NbrOfChannel = 1;

		/* Now do the setup */
		ADC_Init(ADC1, &ADC_InitStructure);
		/* Enable ADC1 */
		ADC_Cmd(ADC1, ENABLE);

		/* Enable ADC1 reset calibaration register */
		ADC_ResetCalibration(ADC1);
		/* Check the end of ADC1 reset calibration register */
		while(ADC_GetResetCalibrationStatus(ADC1));
		/* Start ADC1 calibaration */
		ADC_StartCalibration(ADC1);
		/* Check the end of ADC1 calibration */
		while(ADC_GetCalibrationStatus(ADC1));
		
		//настройка канала АЦП
		ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
		ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
	
		//настройка прерывания для ацп
		NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//0
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;				//1
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
		NVIC_EnableIRQ(ADC1_2_IRQn);
}


//прерывание измерит значение и передаст переменной adcvalue
void ADC1_2_IRQHandler(void) 
{	
	//обработали прерывание
	if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) 
	{
		ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
		adcvalue = ADC_GetConversionValue(ADC1);
	}
}


int main(void)
{
	
		static uint32_t toggle_ms = 0;
	
                //тут настройте тактирование и прочие параметры
			
		init_gpio();
		init_timer();

		ADC_Configuration();
	
		//SysTick_Config(SystemCoreClock / 1000);
	
		while (1)
		{

				 if (T1capture_is_ready)
						{
							
							NVIC_DisableIRQ(TIM3_IRQn);
							T1capture_is_ready = 0;

							/* Обрабатываем захваченный период */
							period = uint16_time_diff(Tcapture2, Tcapture1);
							// ...
							NVIC_EnableIRQ(TIM3_IRQn);
							
						}
                                               
                                           //тут уже оперируйте со значением амплитуды adcvalue
  
		}
		
}

void init_gpio(void)
{
  GPIO_InitTypeDef gpio_cfg;
  GPIO_StructInit(&gpio_cfg);

  /* Таймер TIM3, канал 1 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  gpio_cfg.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  gpio_cfg.GPIO_Pin = GPIO_Pin_6;
  GPIO_Init(GPIOA, &gpio_cfg);
	

	  //светодиоды PC9 и PC8
  gpio_cfg.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
  gpio_cfg.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOC, &gpio_cfg);
}

void init_timer(void)
{
TIM_TimeBaseInitTypeDef timer_base;
	  TIM_ICInitTypeDef timer_ic1;
		TIM_ICInitTypeDef timer_ic2;
	
		/* Подаём такты на TIM3 */
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

  /* Настраиваем предделитель так, чтобы таймер считал миллисекунды.
     На бо́льших частотах следите, чтобы предделитель не превысил
     максимальное значение uint16_t - 0xFFFF (65535) */

		TIM_TimeBaseStructInit(&timer_base);
//прескалер для измерения низких частот

		timer_base.TIM_Prescaler = 7200 - 1;
		TIM_TimeBaseInit(TIM3, &timer_base);

  /* Настраиваем захват сигнала:
   - канал: 1
   - счёт: по нарастанию
   - источник: напрямую со входа
   - делитель: отключен
   - фильтр: отключен */

		timer_ic1.TIM_Channel = TIM_Channel_1;
		timer_ic1.TIM_ICPolarity = TIM_ICPolarity_Rising;
		timer_ic1.TIM_ICSelection = TIM_ICSelection_DirectTI;
		timer_ic1.TIM_ICPrescaler = TIM_ICPSC_DIV1;
		timer_ic1.TIM_ICFilter = 0;
		TIM_ICInit(TIM3, &timer_ic1);
		
		timer_ic2.TIM_Channel = TIM_Channel_2;
		timer_ic2.TIM_ICPolarity = TIM_ICPolarity_Falling;
		timer_ic2.TIM_ICSelection = TIM_ICSelection_DirectTI;
		timer_ic2.TIM_ICPrescaler = TIM_ICPSC_DIV1;
		timer_ic2.TIM_ICFilter = 0;
		TIM_ICInit(TIM3, &timer_ic2);

  /* Разрешаем таймеру генерировать прерывание по захвату */
		TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
		TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);
	
		TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
		TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
	
		/* Включаем таймер */
		TIM_Cmd(TIM3, ENABLE);


		/* Разрешаем прерывания таймера TIM3 */
		NVIC_EnableIRQ(TIM3_IRQn);
}

void TIM3_IRQHandler(void)
{
	//если сработало прерывание по фронту
  if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
  {
    /* Даём знать, что обработали прерывание */
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);

//     /* Запоминаем предыдущее измерение и считываем текущее */
			/* время будет как разность этих двух значении */
     Tcapture1 = Tcapture2;
     Tcapture2 = TIM_GetCapture1(TIM3);

//     /* Для корректной обработки нужно минимум два измерения */
     if (!T1capture_is_first)
       T1capture_is_ready = 1;

     T1capture_is_first = 0;
		
		 //одновременно будем считать, что здесь высокий уровень сигнала, а значит можно измерить амплитуду
		 //запускаем конвертацию АЦП
		 ADC_SoftwareStartConvCmd(ADC1, ENABLE);		
		
// 												
    /* Тут как-нибудь обрабатываем событие over-capture, если провороним */
    if (TIM_GetFlagStatus(TIM3, TIM_FLAG_CC1OF) != RESET)
    {
      TIM_ClearFlag(TIM3, TIM_FLAG_CC1OF);
      // ...
    }
  }
	
}


void SysTick_Handler(void)
{
  ++systick_ms;
}

uint16_t uint16_time_diff(uint16_t now, uint16_t before)
{
  return (now >= before) ? (now - before) : (UINT16_MAX - before + now);
}

Реклама
Эиком - электронные компоненты и радиодетали
Родился
Аватара пользователя
Сообщения: 12
Зарегистрирован: Вт мар 01, 2016 12:22:22

Сообщение kapusta1986 »

А зачем, если настроить триггер ацп на выход канала захвата таймера, то оно будет срабатывать когда пришел фронт/спад,
А как это сделать?

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_CC1; //частота у меня приходит на Т3 таймер

ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);

включил

Но не фурычит... ((( что я делаю не так?
Реклама
Держит паяльник хвостом
Аватара пользователя
Сообщения: 993
Зарегистрирован: Пн сен 18, 2006 11:16:05
Откуда: Тула

Сообщение Neekeetos »

kapusta1986 писал(а):ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_CC1; //частота у меня приходит на Т3 таймер

ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
Не пользуюсь SPL именно из за этого бреда. У ацп в ф10х просто нету внешнего триггера т3-сс1, вот например картинка из мануала
adc.png
(29.83 КБ) 444 скачивания
видно что для того чтобы работал сс1 нужно включить TIM3_TRGO, а на самом таймере выбрать выход триггера чтобы он дублировал сс1.
Информация по RLC mini находится >тут<
Контактная информация:
Реклама
Родился
Сообщения: 12
Зарегистрирован: Пн фев 29, 2016 22:04:54
Откуда: Новосибирск

Сообщение fagci »

Делаю осциллограф, посмотреть можно тут
http://www.youtube.com/watch?v=5nLQ-VqMv-g
там же ссылка на исходники.
Ветка ADC, пока не сливаю, ибо сыро=)
Помучались, отладили, работает. Переписываем!
Контактная информация:
Родился
Аватара пользователя
Сообщения: 12
Зарегистрирован: Вт мар 01, 2016 12:22:22

Сообщение kapusta1986 »

Спасибо! :beer: Neekeetos , Z_h_e

Буду думать теперь, писать, пробовать, отлаживать...

fagci - исходник ADC как у Вас сделан? Вы можете без кода вкратце рассказать как у вас идут измерения в этом проекте?
Родился
Сообщения: 12
Зарегистрирован: Пн фев 29, 2016 22:04:54
Откуда: Новосибирск

Сообщение fagci »

Есть ADC, к нему прицеплен DMA с размером буфера равным ширине экрана в ландшафте. DMA кольцевой, по окончанию генерится прерывание, в котором устанавливается флаг наличия данных.
Сам ADC делает замеры по внешнему триггеру - таймеру, настроенному на частоту, которая просчитана с учётом частоты на деление.
В основном цикле проверяется, сработало ли прерывание по заполнению ДМА буфера и дальше рисуется с учётом того, что максимум 4096 по амплитуде, далее масштабируется.

Пока захвата rising/спада нет, но хочется реализовать.
Помучались, отладили, работает. Переписываем!
Контактная информация:
Ответить

Вернуться в «ARM»