C语言中的状态机(ATmega)

C语言中的状态机(ATmega),c,state,atmega,C,State,Atmega,我正在尝试做一个数据传输控制。带有上拉电阻器的按钮连接到ATmega644P(从)的引脚A3。我想在按下按钮时发送一些数据,只要总线是空闲的。出于测试目的,我使用另一个ATmega644P(主机)将数据发送到从机,这使得总线繁忙 这就是我试图做的:当按下按钮时,启动计时器并等待300毫秒。在此期间,如果它接收到一些数据,则转到总线非空闲状态。如果总线空闲时计时器达到300ms,则发送数据。它在总线空闲时工作得很好,但有时,它在总线繁忙时发送数据 #include <stdlib.h>

我正在尝试做一个数据传输控制。带有上拉电阻器的按钮连接到ATmega644P(从)的引脚A3。我想在按下按钮时发送一些数据,只要总线是空闲的。出于测试目的,我使用另一个ATmega644P(主机)将数据发送到从机,这使得总线繁忙

这就是我试图做的:当按下按钮时,启动计时器并等待300毫秒。在此期间,如果它接收到一些数据,则转到总线非空闲状态。如果总线空闲时计时器达到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;
    }
}
#包括
#包括
#包括
#包括
#包括“uart.h”
#包括“公共汽车免费检查.h”
#包括“规格h”
#定义F_CPU 4000000UL
#定义UART_波特率19200
枚举状态{
闲散的,
启动计时器,
等等,
巴士检查,
发送数据,
公共汽车不是免费的
};
枚举状态状态=空闲状态;
内部主(空)
{
DDRA=0x00;//输入端口。
DDRB=0xFF;//输出端口B。
DDRC=0xFF;//输出端口C。
DDRD=0b11111010;//端口D输入/输出。
PORTA=0x00;
端口B=0x00;
端口C=0x00;

TCCR1B=(1我没有得到预期的功能,但我可以看到两个bug

1)无用代码

for(int i=0; i>3; i++)  // Try three times before canceling
{
  state=bus_check_s; // Go back and check again
  PORTB=0x00; // Led is off
  bus_free=0; // Bus is not free
}

state=bus_not_free_s; // After 3 attempts, go to bus_not_free_s and warn the user
bus_free=0; // Bus is not free
它可以简化为

state=bus_not_free_s;
PORTB=0x00;
bus_free=0; // Bus is not free
2)复制和粘贴错误(如果您真想停止计时器)


TCCR1B |=(0这里有几个问题。其中之一是您的状态机有点关闭

从空闲状态开始,只有按下按钮时才会离开。这是一个机械按钮吗?如果是这样,它需要去抖动。这意味着,基本上,等待它的状态变得稳定,然后确定是否按下了按钮。来自按钮的虚假输入可能会导致你在空闲状态中进出,混淆g你和你的程序

你也有点滥用计时器。如果你不想发送东西,你不需要打开计时器,所以当你完成后就把它关掉。在等待超时的时候,你也在一遍又一遍地设置计时器。没有必要。我很确定计时器功能中的逻辑是关闭的。很难说你想做什么

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


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

我已将等待中的if条件更改为:

case wait_s:
// rec_char=uart_getc();
                if ((unsigned char) rec_char)   // 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
                    state=bus_not_free_s; // Go to bus_not_free state
                }
案例等待:
//rec_char=uart_getc();
if((unsigned char)rec_char)//在此等待期间,如果在任何时候有数据传输正在进行,则转到总线非空闲状态。
{

TCCR1B&=~((1谢谢,这对我帮助很大。我注意到我的状态机真的关闭了。现在我做了一些更改。我正在模拟中尝试这一点,这就是为什么我没有使用任何去盎司算法。这就是我试图做的:当按下按钮时,启动计时器并等待300毫秒。在此期间,如果它收到一些数据,请转到bus_not_free状态。如果在总线空闲时计时器达到300ms,则发送数据。总线空闲时它工作得很好,但有时在总线繁忙时它发送数据。我将更改我的问题并在那里编写新代码。
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12))
case wait_s:
// rec_char=uart_getc();
                if ((unsigned char) rec_char)   // 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
                    state=bus_not_free_s; // Go to bus_not_free state
                }