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

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

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

Сообщение kapusta1986 »

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

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

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

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

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

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

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

Сообщение Z_h_e »

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

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

Сообщение Neekeetos »

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

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

Сообщение 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);
}

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

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

Сообщение kapusta1986 »

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


А как это сделать?

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

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

включил

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

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

Сообщение 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 КБ) 441 скачивание
видно что для того чтобы работал сс1 нужно включить TIM3_TRGO, а на самом таймере выбрать выход триггера чтобы он дублировал сс1.
Информация по RLC mini находится >тут<
fagci
Родился
Сообщения: 12
Зарегистрирован: Пн фев 29, 2016 22:04:54
Откуда: Новосибирск
Контактная информация:

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

Сообщение fagci »

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

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

Сообщение kapusta1986 »

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

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

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

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

Сообщение fagci »

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

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

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