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