我正在尝试进行数据传输控制。 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/