Concurrency 用8051单片机进行频率测量

Concurrency 用8051单片机进行频率测量,concurrency,embedded,microcontroller,race-condition,8051,Concurrency,Embedded,Microcontroller,Race Condition,8051,我只想用比较器输入(下降沿)连续计算正弦信号的频率。有效目标频率约为~122 Hz,我的实现大部分时间都能正常工作,但有时它计算出的错误频率总是~61 Hz(这是不可能的,我用示波器验证了这一点) 我的实现似乎有一个弱点,可能是竞争条件或误用计时器,因为它使用并发中断服务例程并手动启动和停止计时器 我还认为该错误与约122Hz的测量频率相关,因为一个计时器溢出几乎相同: 一个定时器溢出=1/(1/8 MHz*2^16[位])=122.0703125 Hz 我使用的8051微控制器(Silicon

我只想用比较器输入(下降沿)连续计算正弦信号的频率。有效目标频率约为~122 Hz,我的实现大部分时间都能正常工作,但有时它计算出的错误频率总是~61 Hz(这是不可能的,我用示波器验证了这一点)

我的实现似乎有一个弱点,可能是竞争条件或误用计时器,因为它使用并发中断服务例程并手动启动和停止计时器

我还认为该错误与约122Hz的测量频率相关,因为一个计时器溢出几乎相同:

一个定时器溢出=1/(1/8 MHz*2^16[位])=122.0703125 Hz

我使用的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

    ...
  }  
}
//定义
#定义周期\定时器\频率8000000.0//定时器3以8MHz运行
#定义TMR3_页面0x01/*计时器3*/
#定义CP1F_向量12/*比较器1下降沿*/
#定义TF3_向量14/*定时器3重新加载定时器*/
sfr TMR3CN=0xC8;/*定时器3控制*/
sfr TMR3L=0xCC;/*定时器3低字节*/
sfr TMR3H=0xCD;/*定时器3高字节*/
//全局变量
易失性无符号整数扩展数据计时器3\u溢出\u tmp;//本期临时柜台
易失性无符号整数扩展数据计时器3_lastValue;//最后一个计时器值的快照
易失性无符号整数扩展数据计时器3_溢出;//当前溢出计数器,在主例程中使用
易失性无符号整型扩展数据tempVar;//临时变量
易失性无符号长整型扩展数据周期;//晚更新世
易失性浮点扩展数据周期_in_SI;//计算周期(秒)
易失性浮点扩展数据频率;//计算频率(赫兹)
//比较器1 ISR的优先级为“高”:EIP1=0x40
无效comp1\U下降\U isr(无效)中断CP1F\U向量
{
SFRPAGE=TMR3_页面;
TMR3CN&=0xfb;//停止计时器3
timer3_lastValue=(无符号整数)TMR3H;

timer3\u lastValue我无法指出特定的错误,但您在这段代码中遇到了一些问题

主要问题是8051不是PC,而是有史以来成为主流的最可怕的8位MCU。这意味着你应该拼命避免像32位整数和浮点这样的东西。如果你反汇编这段代码,你就会明白我的意思

这里绝对没有必要使用浮点。32位变量也可以避免。只要有可能,就应该使用
uint8\t
,并避免使用
无符号int
。您的C代码不需要知道以秒为单位的时间或以赫兹为单位的频率,只需要关心计时器周期数

您有多个争用条件错误。您的
goto
hack-in-main是一个肮脏的解决方案-相反,您应该首先防止争用条件发生。并且您在ISR之间有另一个带有
timer3\u overflow\u tmp
的争用条件
ISR和
main
之间或具有不同优先级的两个不同ISR之间共享的每个变量都必须受到竞争条件的保护。这意味着您必须确保原子访问或使用某种方式的保护机制。在这种情况下,您可能只需使用bool标志。另一种选择是更改为8位变量,并将访问该变量的代码写入内联汇编程序。通常,您不能对8位内核上的
无符号int
进行原子访问。

我无法指出特定的错误,但您在这段代码中遇到了一些问题

主要问题是8051不是PC,而是有史以来成为主流的最可怕的8位MCU。这意味着你应该拼命避免像32位整数和浮点这样的东西。如果你反汇编这段代码,你就会明白我的意思

这里绝对没有必要使用浮点。32位变量也可以避免。只要有可能,就应该使用
uint8\t
,并避免使用
无符号int
。您的C代码不需要知道以秒为单位的时间或以赫兹为单位的频率,只需要关心计时器周期数

您有多个争用条件错误。您的
goto
hack-in-main是一个肮脏的解决方案-相反,您应该首先防止争用条件发生。并且您在ISR之间有另一个带有
timer3\u overflow\u tmp
的争用条件
ISR和
main
之间或具有不同优先级的两个不同ISR之间共享的每个变量都必须受到竞争条件的保护。这意味着您必须确保原子访问或使用某种方式的保护机制。在这种情况下,您可能只需使用bool标志。另一种选择是更改为8位变量,并在内联汇编程序中写入访问该变量的代码。通常,您不能对8位内核上的
无符号int
进行原子访问。

具有慢边,就像低频正弦和输入中的滞后不足一样(默认为无),只需一点噪音,上升沿就会像下降沿一样,产生一半的频率


代码片段不包括设置滞后的
CPT1CN
设置。对于您的信号,您可能需要将其最大化,并确保信号上的峰间噪声小于30mV。

具有缓慢的边缘,就像低频正弦和输入中滞后不足一样(默认值为“无”),上升沿看起来像下降沿只需要一点噪声,频率就会降低一半

代码片段不包括设置滞后的
CPT1CN
设置。对于您的信号,您可能需要将其最大化,并确保