c - C 语言状态机 (ATmega)

标签 c state atmega

我正在尝试进行数据传输控制。 ATmega644P(Slave)的PINA3上连接有一个带有上拉电阻的按钮。我想在按下按钮时发送一些数据,只要总线空闲。出于测试目的,我使用另一台 ATmega644P(Master)向 Slave 发送数据,这使得总线繁忙。

这就是我想做的:按下按钮时,启动计时器并等待 300 毫秒。在此期间,如果接收到一些数据,则进入bus_not_free状态。如果总线空闲时定时器达到 300ms,则发送数据。当总线空闲时它工作得很好,但有时,它会在总线繁忙时发送数据。

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "uart.h"
#include "bus_free_check.h"
#include "specs.h"

#define F_CPU 4000000UL
#define UART_BAUD_RATE      19200

enum states{
    idle_s,
    start_timer_s,
    wait_s,
    bus_check_s,
    send_data_s,
    bus_not_free_s
};

enum states state=idle_s;

int main(void)
{
    DDRA = 0x00;    //  PORTA is input. 
    DDRB = 0xFF;    //  PORTB is output.
    DDRC = 0xFF;    //  PORTC is output.
    DDRD = 0b11111010;  //  PORTD input/output.

    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;

    TCCR1B = (1 << WGM12);
    TIMSK1 = (1 << OCIE1A);

    OCR1A = 1171;   //  300ms

    uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );

    sei();

    while(1)
    {   
        switch(state)
        {
            case idle_s:
                bus_free=0;
                PORTC=0x01;             

                if (bit_is_clear(PINA,3)) // If the button is pressed
                {
                    while(bit_is_clear(PINA,3)); // Wait until it is unpressed
                    {
                        state=start_timer_s; // Start the timer                         
                    }
                } 
                else
                {
                    state=idle_s; // Wait in the idle_s until the button is pressed
                }

                break;

            case start_timer_s:
                TCCR1B |= (1 << CS10) | (1<< CS12); // Start the timer
                state=wait_s; // Go to wait state and wait for 300ms

                break;

            case wait_s:
                if (bit_is_clear(PIND,0))   // During this waiting, if at any time there is a data transfer going on, go to bus_not_free state.
                {
                    TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // Stop the timer
                    TCNT1=0; // Reset the counter
                    bus_free=0;
                    state=bus_not_free_s; // Go to bus_not_free state
                }

                break;

            case send_data_s:
                send_target_seri_no(); // Bus is free, you can send the data
                TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // After sending the data, stop the timer.
                TCNT1=0; // And reset the counter.
                state=idle_s; // Go to idle_s and get ready

                break;

            case bus_not_free_s:
                uart_puts("Bus is not free \r"); // Bus is not free, can't send the data
                state=idle_s; // Go to idle state, and don't give up hope

                break;

            default:
                uart_puts("Fatal Error \r");
                break;
        }               
    }
}

ISR(TIMER1_COMPA_vect) // If the timer reaches 300ms, that means the bus is free
{
    bus_free=1;
    if(state==wait_s) // When the timer hits 300ms, if it is still in wait_s state, send the data.
    {
        state=send_data_s;
    }
}

谢谢。

最佳答案

您遇到了几个问题。其中之一是你的状态机有点不对劲。

从空闲状态开始,只有按下按钮才能离开它。这是机械按钮吗?如果是这样,则需要进行去抖处理。这意味着,基本上,等待其状态变得稳定,然后确定是否发生了按钮按下。来自按钮的虚假输入可能会导致您突然进入和退出空闲状态,从而使您和您的程序感到困惑。

你也有点滥用计时器了。如果您不尝试发送某些内容,则不需要打开计时器,因此在完成发送后将其关闭。在等待超时的同时,您还一遍又一遍地设置计时器。不必要。我很确定计时器函数中的逻辑已关闭。很难说出你想做什么。

总线检查状态是最大的问题。它的作用太多了。当退出空闲状态时,您只需要启动一次计时器。因此将其分离到另一个状态。然后,检查状态所做的就是在总线空闲时转移到发送状态。您的“三次尝试”循环没有执行任何操作:它不会等待任何更改。

陷入“总线不空闲”状态的原因是这样的:假设总线输入为0(不忙)且bus_free=0。它如何将bus_free设置为1?在这种情况下,您的检查时间函数将关闭计时器,因此 ISR 永远不会触发更改bus_free。

关于c - C 语言状态机 (ATmega),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23459835/

相关文章:

javascript - 如何在react.js中跨子组件持久保存数据或状态?

c - GCC 中的汇编代码未在 Atmel Studio 中针对 AVR 环境进行编译

c - Atmega168 I2C 作为主机

c - 在 *nix 系统上的 C 中,使用 $PATH 或 "which"找到二进制文件的绝对路径?

Flutter GetIt 插件 - GetIt 中未注册类型 xxx

将2个字节转换成12位数字?

javascript - 从 firebase 进行身份验证后,我想在状态中存储名称、电子邮件

microcontroller - 为什么要在 AVR 编程中校准振荡器

c - C 中的文件问题

C- fprintf 两个变量在同一行