【问题标题】:PIC16F1937 and UART : keep led ON while receivingPIC16F1937 和 UART:接收时保持 LED 亮起
【发布时间】:2019-05-06 12:03:20
【问题描述】:

我试图在 PIC 接收字符时(按下按钮时)保持 LED 亮起,默认情况下 LED 处于关闭状态,但我似乎找不到解决方案。

我用 MPLABX IDE 使用微控制器(特别是 PIC)编码学习了 3 个月,并开始了解 UART 通信。起初我尝试在收到字符时打开/关闭 LED,效果很好并且工作正常,但现在我试图在按下按钮时保持 LED 处于活动状态,但无法真正克服这个问题。我的代码在这样的中断函数中:

//#define LED RB5

void __interrupt () ISR (void)
{   
    if(RCIF) // data is received
    {
        data = RCREG; // get the value
        if (data == '1') // if value received is '1'
            LED = 1; //turn ON led
        RCIF = 0;
    }
    LED = 0; // turn OFF led
}

使用上面的代码可以让 LED 在我按住按钮时非常快速地打开/关闭,这并不是我真正想要的。
我希望有人能帮助我了解我需要做什么。谢谢!

【问题讨论】:

  • 因为“橡皮鸭调试”的简短会话应该会引导您找到问题的原因。
  • 您需要添加一个else
  • 根据我正在查看的数据表,RCIF 位是只读的。

标签: c pic uart


【解决方案1】:

LED 会快速熄灭,因为您在打开后立即关闭:

void __interrupt () ISR (void)
{   
    if(RCIF) // data is received
    {
        data = RCREG; // get the value
        if (data == '1') // if value received is '1'
            LED = 1; //turn ON led
        RCIF = 0;
    }
    LED = 0; // turn OFF led   <<=== This is executed unconditionally.
}

如果您不想每次都执行它,可以将 LED = 0; 放在 else 分支中。

可能是这样的:

void __interrupt () ISR (void)
{   
    if(RCIF) // data is received
    {
        data = RCREG; // get the value
        if (data == '1') // if value received is '1'
            LED = 1; //turn ON led
        else
            LED = 0; // turn OFF led
        RCIF = 0;
    }
}

根据您的逻辑,else 可能会放置在外部 if 块的末尾之后。

【讨论】:

  • 试过这个,但问题是每次有数据要读取时都会执行 if(RCIF){...},当我松开按钮“1”时,LED 保持亮起,因为 if( RCIF) 不再执行。此外,如果我将 else 放在外部 if(RCIF) 上,它不会做任何事情。
  • 您没有通过 UART 获得按钮释放事件吗?
  • @Gerhardh 那将是 GPIO 中断而不是 UART。考虑到他正在专门为 UART 模块检查中断标志,释放按钮似乎只是停止发送数据,我不清楚当RCIF 位被清除时他是否收到中​​断。
【解决方案2】:

接收到任何字符后,LED 会快速熄灭。 RCIF 标志仅在接收到 UART 数据时设置。因此,要关闭 LED,您应该启动一个特定毫秒的计时器,除非您的接收完成,否则请继续重新启动计时器。


void __interrupt () ISR (void)
{   
    if(RCIF) // data is received
    {
        data = RCREG; // get the value
        if (data == '1') // if value received is '1'
            LED = 1; //turn ON led
        RCIF = 0;
        start_timer_interrupt(x_ms);  //set x_ms optimum timing so as to LED ON is visable with UART Rx
        flag_rx_timer=1;
    }

    if(TIMER_OVERFLOW) //TIMER_OVERFLOW is an example keyword used here. Pleas add exact timer overflow flag
    {
        if(flag_rx_timer)  //check if timer overflow is because of timer started during UART Rx
        {
             LED = 0;
             flag_rx_timer=0;
        }
    }
}```

【讨论】:

  • 你能指定什么 start_timer_interrupt(x_ms);方法?这是使用计时器的延迟吗?
  • 你需要研究如何在PIC​​中实现定时器。参考链接-ww1.microchip.com/downloads/en/devicedoc/51682a.pdf
  • @NitinJadhav 如果你想让定时器打开和关闭 LED,它应该有自己的中断来处理溢出。真正从事真正固件开发的人不会将两者联系在一起。
【解决方案3】:

我建议你在 app.c 中为此创建一个状态机。

enumstruct 定义一些状态来保存状态并将它们放在app.h 中,在您希望使用此模块的文件中包含app.h(例如您的system_interrupt.c 文件)。

例如你可以这样做:

typedef enum{
    IDLE,
    START_RECEIVING,
    STILL_RECEIVING,
}uart_state_t;

typedef struct{
    uart_state_t current_state;
}uart_module_t

volatile uart_module_t uart_module = {0}; // initial state = IDLE, needs to be volatile because it will be updated via interrupt

然后创建一个函数来服务您的状态机。这将处理它当前所处的状态,并根据需要转换到其他状态。例如,状态机以IDLE 状态启动,但是一旦您的中断被触发并且RCIF 位被设置,那么您将转换到START_RECEIVING 状态,这将打开LED 然后转换到@987654328 @ 状态,它将轮询 RCIF 位直到被清除。看起来像这样:

void uartFSM(void){

    switch(uart_module.current_state){

        case IDLE:
        {

            break;

        }

        case START_RECEIVING:
        {

            LED = 1; // Turn LED on
            uart_module.current_state = STILL_RECEIVING; // state update

            break;

        }
        case STILL_RECEIVING:
        {

            if(!RCIF){

                // done receiving
                LED = 0; // Turn LED off
                uart_module.current_state = IDLE; // state update

            }

            break;

        }

        default:
        {
            // whoops
            break;
        }

    }

}

现在您的中断将如下所示:

void __interrupt () ISR (void)
{   
    if(RCIF) // data is received
    {

        data = RCREG; // get the value

        // if value received is '1'
        if (data == '1') uart_module.current_state = START_RECEIVING; // state update

    }

}

现在您只需要确保在APP_Tasks 中的某个位置调用uartFSM(),以便状态机得到服务。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多