Измерение частоты с помощью микроконтроллера 8051 - PullRequest
0 голосов
/ 28 сентября 2018

Я просто хочу непрерывно вычислять частоту синусоидального сигнала с входом компаратора (по падающим фронтам).Эффективная целевая частота составляет около ~ 122 Гц, и моя реализация работает большую часть времени, но иногда она вычисляет неправильную частоту с всегда около ~ 61 Гц (что невозможно, я проверил это с помощью осциллографа).

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

Я также думаю, что ошибка коррелирует с измереннойчастота около ~ 122 Гц, поскольку переполнение одного таймера практически одинаково:

Переполнение одного таймера = 1 / (1/8 МГц * 2 ^ 16 [бит]) = 122,0703125 Гц

Я использую микроконтроллер 8051 (Silicon Labs C8051F121) со следующим кодом:

// defines 
#define PERIOD_TIMER_FREQ 8000000.0 // Timer 3 runs at 8MHz

#define TMR3_PAGE      0x01 /* TIMER 3 */
#define CP1F_VECTOR    12   /* comparator 1 falling edge */
#define TF3_VECTOR     14   /* timer3 reload timer   */

sfr TMR3CN        = 0xC8; /* TIMER 3 CONTROL */
sfr TMR3L         = 0xCC; /* TIMER 3 LOW BYTE */
sfr TMR3H         = 0xCD; /* TIMER 3 HIGH BYTE */

// global variables
volatile unsigned int xdata timer3_overflow_tmp; // temporary counter for the current period
volatile unsigned int xdata timer3_lastValue; // snapshot of the last timer value
volatile unsigned int xdata timer3_overflow; // current overflow counter, used in the main routine
volatile unsigned int xdata tempVar; // temporary variable
volatile unsigned long int xdata period; // the caluclated period
volatile float xdata period_in_SI; // calculated period in seconds
volatile float xdata frequency; // calculated frequency in Hertz

// Comparator 1 ISR has priority "high": EIP1 = 0x40
void comp1_falling_isr (void) interrupt CP1F_VECTOR
{
  SFRPAGE = TMR3_PAGE;
  TMR3CN &= 0xfb;      // stop timer 3

  timer3_lastValue = (unsigned int) TMR3H;
  timer3_lastValue <<= 8;
  timer3_lastValue |= (unsigned int) TMR3L;

  // check if timer 3 overflow is pending 
  if (TMR3CN & 0x80)
  {
    timer3_overflow_tmp++; // increment overflow counter
    TMR3CN &= 0x7f; // Clear over flow flag. This will also clear a pending interrupt request.
  } 

  timer3_overflow = timer3_overflow_tmp;

  // Reset all the timer 3 values to zero
  TMR3H = 0;
  TMR3L = 0;
  timer3_overflow_tmp = 0;
  TMR3CN |= 0x04;     // restart timer 3
}

// Timer 3 ISR has priority "low", which means it can be interrupted by the
// comparator ISR: EIP2 = 0x00
// Timer 3 runs at 8MHz in 16 bit auto-reload mode
void timer3_isr(void) interrupt TF3_VECTOR using 2
{      
  SFRPAGE = TMR3_PAGE;
  timer3_overflow_tmp++;
  TMR3CN &= 0x7f; // Clear over flow flag. This will also clear a pending interrupt request.
}

void main(void)
{
  for(;;)
  {
    ...

calcFrequencyLabel: // this goto label is a kind of synchronization mechanism
                    // and is used to prevent race conditions caused by the ISRs
                    // which invalidates the current copied timer values

    tempVar = timer3_lastValue;
    period  = (unsigned long int)timer3_overflow;
    period  <<= 16;
    period  |= (unsigned long int)timer3_lastValue;

    // If both values are not equal, a race condition has been occured.
    // Therefore the the current time values are invalid and needs to be dropped.
    if (tempVar != timer3_lastValue) 
      goto calcFrequencyLabel;

    // Caluclate period in seconds
    period_in_SI = (float) period / PERIOD_TIMER_FREQ;

    // Caluclate period in Hertz
    frequency = 1 / period_in_SI; // Should be always stable about ~122Hz

    ...
  }  
}

Может кто-нибудь помочь мне найти ошибку в моей реализации?

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

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

Фрагмент кода не включает настройку CPT1CN, где установлен гистерезис.Для вашего сигнала вам, вероятно, необходимо максимально увеличить его и убедиться, что пиковый шум на вашем сигнале составляет менее 30 мВ.

0 голосов
/ 28 сентября 2018

Я не могу точно указать конкретную ошибку, но у вас есть некоторые проблемы в этом коде.

Основная проблема заключается в том, что 8051 был не ПК, а самым ужасным 8-битным MCU, когда-либо ставшим мейнстримом.Это означает, что вы должны отчаянно избегать таких вещей, как 32-битные целые числа и числа с плавающей запятой.Если вы разберете этот код, вы поймете, что я имею в виду.

Нет абсолютно никаких причин, по которым вам нужно использовать здесь с плавающей точкой.И 32-битных переменных, вероятно, тоже можно избежать.Вы должны использовать uint8_t, когда это возможно, и избегать unsigned int тоже.Ваш код C не должен знать время в секундах или частоту в Гц, а просто заботиться о количестве циклов таймера.

У вас есть несколько ошибок состояния гонки.Ваш goto взлом в основном - грязное решение - вместо этого вы должны предотвратить возникновение состояния гонки.И у вас есть другое условие гонки между ISR с timer3_overflow_tmp.
Каждая переменная, совместно используемая между ISR и main, или между двумя различными ISR с разными приоритетами, должна быть защищена от условий гонки.Это означает, что вы должны либо обеспечить атомарный доступ, либо использовать какой-либо механизм защиты.В этом случае вы могли бы просто использовать "мьютекс бедняка" bool flag.Другой альтернативой является изменение 8-битной переменной и запись кода, обращающегося к ней, во встроенном ассемблере.Как правило, у вас не может быть атомарного доступа на unsigned int на 8-битном ядре.

...