我是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功能结束。
抱歉英语能力差。我希望你能理解我的问题,也许你可以帮助我。
答案 0 :(得分:3)
实现目标的最简单方法可能就是在按钮循环中轮询LED循环中的按钮。然而,作为一般解决方案,它具有较差的内聚力,并且不能扩展到更复杂的应用程序。
实现并发的通用方法,无需借助中断或多任务调度程序,即使用状态机进行LED控制。
状态机不是调用led()
并要求它在返回之前完成整个闪存序列,而是简单地确定按钮是否被按下以及是时候改变LED状态然后返回立即。它会跟踪时间和状态,但不会在一次通话中执行完整的LED序列。
这是在下面实现的,但请注意,时间是粗略的,并使用您已经使用的延迟功能实现 - 因为我无法分辨您可以使用的其他服务。如果任何处理需要花费任何重要时间,这可能会影响闪光时间。它依赖于每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_flashing
和LED_FLASH_OFF
状态下测试LED_FLASH_ON
,并提前返回LED IDLE
。例如:
该方法可以很容易地适应在定时器中断时运行LED状态机。而不是ledUpdate()
将按钮状态作为参数,这可以通过共享变量传递给中断处理程序。其余的状态机代码将保持不变。然后主循环将在按钮关闭时简单地设置共享变量。
就个人而言,我主张分离和封装压电控制,按钮轮询和LED控制,使主循环看起来像:
int main()
{
for(;;)
{
int button_state = getButtonState() ;
upodatePiezo( button_state ) ;
updateLed( button_state ) ;
}
}
那将有纵梁凝聚力和松散耦合。软件设计中的两个有用目的是实现代码的可维护性和可重用性。