【问题标题】:Toggling Blink Led Program on and off打开和关闭闪烁 LED 程序
【发布时间】:2021-03-12 22:55:38
【问题描述】:

我的目标:制作一个可以打开和关闭“闪烁 LED”程序的按钮。 PortD 只有输出 LED。 PORTBO 有输入按钮。 微控制器:Atmega328p

问题:目前,当我按下开关的按钮 case1 时,“闪烁 LED”程序启动。但我无法通过按钮关闭 LED。

注意:我是初学者,我想我可能不得不以某种方式使用中断,但不确定

#include <avr/io.h> // header file file for input output pins
#include <util/delay.h> // header file for delay.
#define DEBOUNCE_TIME 25 // time to wait while "de-bouncing" button

void init_ports_mcu()
{ /* set pin 5 of PORTB for output*/
    DDRD = 0xFF;
    PORTD = 0x00;
    PORTB |= (1 << DDB0);
}

unsigned char button_state()
{
    if( !(PINB & 0x01) )
    {
        _delay_ms( DEBOUNCE_TIME );
        if( (PINB & 0x01) ) return 1;
    }
    return 0;
}

int main( void )
{
    init_ports_mcu();
    unsigned char n_led = 1;

    while( 1 )
    {
        if( button_state() )
        {
            switch( n_led )
            {
                case 1:
                    while( 2 )
                    {
                        PORTD |= 0xFF;
                        _delay_ms( 1000 ); //delay 1 second

                        PORTD &= ~0xFF;
                        _delay_ms( 1000 );

                        if( button_state() ) // If the button is pressed break while loop
                        {
                            break;
                        }
                    }
                    break;
                case 2:
                    PORTD |= 0x00;
                    _delay_ms( 5000 ); //delay 1 second
                    n_led--; // swtiches back to case1 LED number
                    break;
            }
            //n_led ++;
        }
    }
    return 0;
}

【问题讨论】:

  • 不能说我遵循逻辑,代码格式也无济于事。但有一件事,mainunsigned char n_led = 1; 开头,而n_led 之后唯一的变化是n_led--;。我看不出switch(n_led) 怎么能到达case 2:
  • button_state() 函数是一个边缘检测器。为了使该函数返回 1,您需要在 25 毫秒的延迟时间内按下按钮。当代码在执行while(1) 循环时,大部分时间都花在了_delay(DEBOUNCE_TIME) 调用上,因此可以识别按钮按下。但是在while(2) 循环中,大部分时间都花在了_delay(1000) 调用上,因此您需要将按钮按下的时间精确到毫秒。祝你好运!赔率是 40 比 1。
  • 这是一个有趣的尝试。删除第二个_delay_ms(1000)。将DEBOUNCE_TIME 更改为1000。现在,当您按下按钮时,LED 将开始闪烁。如果在 LED 亮起时按下按钮,它会一直闪烁。但是如果在 LED 熄灭时按下按钮,它将停止闪烁。

标签: c embedded avr


【解决方案1】:

有很多问题。你的 debounce 逻辑似乎有缺陷,它说:

如果按钮没有被按下,等待20ms再看是否被按下。

最好说:

如果按钮状态发生变化,等待25ms,然后查看是否保持变化

unsigned char button_state()
{
    static unsigned last_state = (PINB & 0x01) ;

    unsigned new_state = (PINB & 0x01) ;
    if( last_state != new_state )
    {
        _delay_ms( DEBOUNCE_TIME ) ;

        if( (PINB & 0x01) == new_state ) last_state = new_state ;
    }

    return last_state ;
}

它仍然是一种相当粗糙的去抖动方法,但可能足够了。使用边沿中断和定时器中断是一种更可靠的解决方案 (example pseudocode)。

在您的主体中,您最大的问题是,当您延迟时,您没有处理其他任何事情 - 延迟是“忙等待”。毫无疑问,逻辑上还有其他缺陷,但坦率地列举它们是徒劳的。需要重新设计。

在不引入现有代码中没有的库代码或硬件资源的情况下,我建议使用延迟为 1 的状态机循环并使用计数器来跟踪时间。状态是闪烁/不闪烁LED on/LED off的子状态:

int main( void )
{
    #define FLASH_INTERVAL 1000 ;

    init_ports_mcu();
    unsigned char flashing = 0 ;
    unsigned char led_state = 0 ;
    unsigned long tick = 0 ;
    unsigned flash_time = FLASH_INTERVAL ;
    unsigned char button_down = 0 ;

    for(;;)
    {
        // Toggle indication state on button-down event
        unsigned char btn = button_state() ;
        if( !button_down && btn )
        {
            button_down = 1 ;

            // Toggle LED mode
            flashing = flashing == 0 ? 1 : 0 ;

            // Initialise the flashing starting with LED on
            flash_time = FLASH_INTERVAL ;
            led_state = flashing ;
        }
        else if( !btn )
        {
            // Button released
            button_down = 0 ;
        }

        // If flashing "ON"...
        if( flashing )
        {
            // Set the LED state
            if( led_state ) 
            {
                PORTD |= 0xFF ;
            }
            else
            {
                PORTD &= 0xFF ;
            }

            // Toggle the LED state when the flash interval has elapsed
            flash_time-- ;
            if( flash_time == 0 )
            {
                flash_time = FLASH_INTERVAL ;
                led_state = led_state ? 0 : 1 ;
            }
        }
        else // flashing off
        {
            PORTD &= ~0xFF;
        }

        // Increment tick every ms
        _delay_ms(1) ;
        tick++ ;
    }

    return 0;
}

上面的一个问题是,无论循环体执行多长时间,滴答计数间隔都会延长。在按钮按下或释放的情况下,这将包括去抖动延迟,但在这种情况下,这并不是一个特别的问题。在实时/时间关键型应用程序中需要考虑。

改进和简化可能通过定时器中断增加tick 计数而不是依赖于延迟和循环体执行时间。或者您可以使用定时器中断直接切换 LED,然后简单地打开/关闭定时器或设置一个标志来阻止它在按钮按下事件时切换 LED。

所以是的,您可以广泛使用中断和定时器硬件来实现这一点,您可以通过多种方式实现这一点,但这绝不是必要的。

【讨论】:

  • 请注意,此处提供的所有代码都没有经过测试甚至编译 - 将其视为“说明性”,您可能仍需要对其进行调试。
  • 谢谢克利福德!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-24
  • 2015-12-04
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多