AVR C按下按钮按下延迟时编程两个功能

时间:2014-10-12 13:35:29

标签: c embedded avr atmega

我是AVR编程的新手。 我有一个ATMEGA8,想要做这样的事情:

如果按下按钮,LED应该打开和关闭10次。作品。 但只要按下按钮,压电就会发出声音。 问题是我无法同时执行这两项功能。

闪烁LED功能

 int i;
 void led(void) 
 {
     for (i = 0; i < 10; i++)
     {
         PORTB |= (1 << PB0);  //LED on
         _delay_ms(250);       //wait 250ms

         PORTB &= ~(1 << PB0); //LED off
         _delay_ms(250);       //wait 250ms
     }
 }

主要代码:

while (1)
{
    if (!(PINB & (1<<PB7)) )
    {
        PORTB |= (1 << PB1); // Piezo on
        led();
    }
    else
    {
        PORTB &= ~(1 << PB1); // Piezo off
    }
}

即使我按下按钮一小段时间,压电仍然保持开启,直到闪烁LED功能结束。

抱歉英语能力差。我希望你能理解我的问题,也许你可以帮助我。

1 个答案:

答案 0 :(得分:3)

实现目标的最简单方法可能就是在按钮循环中轮询LED循环中的按钮。然而,作为一般解决方案,它具有较差的内聚力,并且不能扩展到更复杂的应用程序。

实现并发的通用方法,无需借助中断或多任务调度程序,即使用状态机进行LED控制。

状态机不是调用led()并要求它在返回之前完成整个闪存序列,而是简单地确定按钮是否被按下以及是时候改变LED状态然后返回立即。它会跟踪时间和状态,但不会在一次通话中执行完整的LED序列。

statemachine

这是在下面实现的,但请注意,时间是粗略的,并使用您已经使用的延迟功能实现 - 因为我无法分辨您可以使用的其他服务。如果任何处理需要花费任何重要时间,这可能会影响闪光时间。它依赖于每10ms调用一次LED状态机,它只是对呼叫进行计数。对状态机来说最好使用一个独立的时钟源(例如标准库clock()函数),那么如果它被非周期性地调用也没关系 - 而不是计算调用次数,它会切换状态的实际时间过去了。

请注意使用static变量来维护状态。 static在函数调用之间保持其值。

#define TICK 10            // milliseconds 
#define FLASH_ON_TICKS 25  // 250ms
#define FLASH_OFF_TICKS 25 // 250ms
#define FLASH_COUNT 10

static void ledUpdate( int start_flashing ) ;

int main( void )
{
  for(;;)
  {
    // Perform loop on each tick (10ms)
    // Assumes loop processing time is not significant! 
    _delay_ms( TICK ) ;

    if (!(PINB & (1<<PB7)) )
    {
      PORTB |= (1 << PB1); // Piezo on
      ledUpdate( 1 ) ;  
    }
    else
    {
      PORTB &= ~(1 << PB1); // Piezo off
      ledUpdate( 0 ) ;  
    }
  }

  return 0 ;
}

void ledUpdate( int start_flashing )
{
  static enum
  {
    LED_IDLE,
    LED_FLASH_ON,
    LED_FLASH_OFF

  } led_state = LED_IDLE ;

  static int led_tick = 0 ;
  static int flash_count = 0 ;

  switch( led_state )
  {
    case LED_IDLE :
    {
      if( start_flashing )
      {
        led_state = LED_FLASH_ON ;
        PORTB |= (1 << PB0);  //LED on
      }
    }
    break ;

    case LED_FLASH_ON :
    {
      led_tick++ ;
      flash_count++ ;
      if( led_tick >= FLASH_ON_TICKS )
      {
        led_state = LED_FLASH_OFF ;
        led_tick = 0 ;
        PORTB &= ~(1 << PB0); //LED off
      }
    }
    break ;

    case LED_FLASH_OFF :
    {
      if( flash_count >= FLASH_COUNT )
      {
        led_state = LED_IDLE ;
      }
      else
      {
        led_tick++ ;
        if( led_tick >= FLASH_ON_TICKS )
        {
          led_state = LED_FLASH_ON ;
          led_tick = 0 ;
          PORTB |= (1 << PB0);  //LED on
        }
    }
    break ;
  }
}

请注意,按钮状态仅影响LED,如果它尚未闪烁,即使释放按钮,也会完成10个循环。如果您希望在释放按钮时停止闪烁,则必须在start_flashingLED_FLASH_OFF状态下测试LED_FLASH_ON,并提前返回LED IDLE。例如:

statemachine2

该方法可以很容易地适应在定时器中断时运行LED状态机。而不是ledUpdate()将按钮状态作为参数,这可以通过共享变量传递给中断处理程序。其余的状态机代码将保持不变。然后主循环将在按钮关闭时简单地设置共享变量。

就个人而言,我主张分离和封装压电控制,按钮轮询和LED控制,使主循环看起来像:

int main()
{
    for(;;)
    {
        int button_state = getButtonState() ;
        upodatePiezo( button_state ) ;
        updateLed( button_state ) ;
    }
}

那将有纵梁凝聚力和松散耦合。软件设计中的两个有用目的是实现代码的可维护性和可重用性。