看门狗和电压控制不工作微控制器 [AVR128DB48 Curiosity Nano]

WatchDog and Voltage control did not work microcontroller[AVR128DB48 Curiosity Nano]

您好,我正在使用 Watchdog 来控制 LED 灯。微控制器通过电缆连接到笔记本电脑。微控制器的输入是 5V。现在在引脚上有一个引脚 PB2 5V 直接连接到输入。如果我从 PB2 移除 5V 母对公电线,我想这样做,LED 将关闭。当我用 PB2 再次插入时,灯会亮起,然后看门狗每 4 秒调用一次并熄灭红灯

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#define WDTO_4S 8


void fun_red(){
        PORTB.IN=0x04; // PB2 as input
        PORTB.DIR=0x09; // PB0 and PB3 output
        
    
    PORTB.OUTCLR=0x00; // Green
    PORTB.OUTSET=0x01;
        
        
        wdt_enable(WDTO_4S);
        do 
        {
            if (PORTB.IN)
            {
                PORTB.OUTCLR=0x00; // red
            }
            else{
                PORTB.OUTSET=0x08;
            }
            

        
        } while(1);
        wdt_reset();

}

int main(void)
{
    fun_red();
}

好的,所以在编辑中您似乎希望使用 PB2 作为输入来控制 PB3 上的 LED - 对吗?

你不应该考虑电压,这些是数字的 I/O 它们有一个高状态和一个低状态以及两者之间的阈值。您的输入会在任何高于 0.8 伏的情况下变高。但是,如果您启用了内部上拉,通过断开电线来移除输入电压将没有任何效果,如果没有启用,它将浮动并且可能不会触发逻辑 0(零)。

您要么需要一个外部下拉电阻,要么启用内部上拉并反转逻辑 - 即输入低时 LED 亮起,输入高时 LED 熄灭。然后,不要将 PB2 连接到 5V,而是将其连接到 GND。由于内部上拉,移除 GND 连接将导致逻辑​​状态变为 1。此外,这更安全,因为它避免了对输入施加过大电压并损坏处理器的任何风险。

鉴于您明显缺乏电子知识,我还想问一下您是如何连接 LED 的。你应该有一个限流串联电阻,并以正确的方式连接 LED。

目前尚不清楚看门狗的目的是什么。它没有任何用处,但如果启用它,则必须维护它以防止处理器重置。看门狗的目的是在软件正常停止时重置处理器 运行 通过在正常的软件执行路径中定期重置处理器,因此如果正常路径停止执行,系统将重新启动。

首先阅读本入门应用笔记:Getting Started with GPIO

首先,你误解了GPIO寄存器的作用。 PORTB.IN 读取配置为输入的 PORTB 引脚的 输入状态 。它是 只读的 所以:

PORTB.IN=0x04; // PB2 as input

没有效果,当然不会将引脚配置为输入。这是由 DDR 完成的,所以:

PORTB.DIR=0x09; // PB0 and PB3 output

将 PB0 和 PB3 设置为输出,任何不是输出的都是输入 - 因此 PB2 是输入,因为这条线。

现在,如果您按照我的建议使用 PB2 的内部上拉电阻,则必须在 PIN2CTRL 寄存器中启用它:

PORTB.PIN2CTRL = 0x08 ; // PULLUPEN

顺便说一句,所有这些位都定义了符号,因此您应该能够编写:

PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;

这也使得注释变得不必要。

寄存器OUTSETOUTCLR分别设置(逻辑1/高)和清除(逻辑0/低)。 set/clear 的引脚由传入的 位掩码 决定。因此:

PORTB.OUTCLR=0x00; // Green

什么都不做,它将 no pins 设置为低状态。关闭红色 LED(我假设是 PB3)并打开绿色 LED(PB0):

PORTB.OUTCLR = PIN3_bm ;
PORTB.OUTSET = PIN0_bm ;

现在,在这种情况下,您对 PORTB.IN 的测试就好像它是布尔值一样有效,因为您只有一个输入。但是如果你有多个输入,那么它就不会区分,并且在任何情况下都将整数表达式当作布尔值来使用是一个坏习惯。您应该明确测试 PB2 的状态:

    // If PB2 is low (GND wire connected)
    if( (PORTB.IN & PIN2_bm) == 0 )
    {
        PORTB.OUTSET = PIN3_bm ; // red on
    }
    else
    {
        PORTB.OUTCLR = PIN3_bm ; // red off
    }

看门狗定时器的作用是在软件无法正常运行时重置处理器。您设置了超时时间,然后您需要在代码中定期重置它以防止重置。这不是您用于延迟的一般用途。为此,您将使用硬件计时器。不幸的是,它变得有点复杂;对于您的开发板 运行 在 24MHz,16 位 TIMER1 的最大定时器周期约为 2.8 秒。为了获得更大的灵活性,您通常会实现一个定时器中断来计算 smaller 周期的数量并计算定时器重新加载的次数。例如:

volatile unsigned tick = 0 ;
ISR (TIMER1_OVF_vect)    // Timer1 ISR
{
    tick++ ;
}

void tickStart()
{
    
    TCCR1B = (1<<CS11) // Prescaler 24MHz / 8
    TCNT1 = 3000 ;   // 1 ms at 24MHz/8 
    TCCR1A = 0x00;
    TIMSK = (1 << TOIE1) ;   // Enable timer1 overflow interrupt(TOIE1)
    sei();        // Enable global interrupts
}

unsigned getTick()
{
    unsigned t = 0 ;
    do
    {
        t = tick ;
    } while( tick != t ) ;

    return t ;
}

然后在监控 P2 的同时延迟 4 秒,你可以这样做:

// Wait 4 seconds or until PB2 disconnected
unsigned start = getTick() ;
while( getTick() - start < 4000 && 
       (PORTB.IN & PIN2_bm) != 0 ) 
{
    // waiting
} 

最后 评论您的代码。它会让你明白你正在尝试做什么,当你 post 一个问题时,它会告诉其他人你正在尝试做什么,它会让你的问题更简单,因为解释与代码,因此不仅清楚您希望代码做什么,而且清楚您认为它是如何做到的。

综上所述,以下比较合理:

void fun_red( void )
{ 
    // Initialise I/O 
    PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
    PORTB.DIR = PIN0_bm | PIN3_bm ;
    
    PORTB.OUTSET = PIN0_bm ; // Green on

    for(;;) 
    {
        // Red off
        PORTB.OUTCLR = PIN3_bm ;
        
        // Wait for PB2 to be connected (to GND)
        while( (PORTB.IN & PIN2_bm) == 0 )
        {
            // waiting
        }
        
        // Red on
        PORTB.OUTSET = PIN3_bm ;
        
        // Wait 4 seconds or until PB2 disconnected
        unsigned start = getTick() ;
        while( getTick() - start < 4000 && 
               (PORTB.IN & PIN2_bm) != 0 ) 
        {
            // waiting
        } 
        
        // Wait for PB2 to be reconnected
        while( (PORTB.IN & PIN2_bm) != 0)
        {
            // waiting
        }
    }
}

int main( void )
{
    tickStart() ;
    fun_red() ;
}

请记住,在此将 PB2 连接到 GND(0 伏)而不是 5V/Vcc。问题中的要求并不完全清楚,但这将做什么(未经测试 - 我没有硬件)是:

  • 当PB2断开时,LED会熄灭,
  • 当 PB2 连接时,LED 将点亮 4 秒或直到 PB2 重新连接。

虽然这不是它的预期目的并且对于一般用途来说并不是真正有用,但因为这个要求非常简单,所以看门狗定时器可以按如下方式实现所需的行为:

void fun_red()
{ 
    // Initialise I/O 
    PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
    PORTB.DIR = PIN0_bm | PIN3_bm ;
    
    // Green on
    PORTB.OUTSET = PIN0_bm ;
    
    // Red off
    PORTB.OUTCLR = PIN3_bm ;
     
    // Enable watchdog
    wdt_enable(WDTO_4S);
    
    for(;;)
    {
        // Maintain watchdog while waiting for 
        // PB2 to be connected (to GND)
        while( (PORTB.IN & PIN2_bm) == 0 )
        {
            wdt_reset() ;
        }
        
        // Red on
        PORTB.OUTSET = PIN3_bm ;
        
        // Maintain watchdog while waiting for 
        // PB2 to be disconnected (from GND)
        while( (PORTB.IN & PIN2_bm) != 0)
        {
            wdt_reset() ;
        }

        // Red off
        PORTB.OUTCLR = PIN3_bm ;

        // Wait for PB2 to be reconnected (to GND)
        // without maintaining watchdog.  Will reset after 4 seconds
        // if not reconnected.
        while( (PORTB.IN & PIN2_bm) != 0)
        {
            // do nothing
        }
    }
}

这里PB2断开后,watchdog不维护,所以4秒后会reset,然后重启程序,等待PB2连接上。我想我应该 class 这样的代码作为“肮脏的把戏”,而不是被认为是正常或特别有用的东西。