arduino - AVR USART通讯问题

标签 arduino avr atmega usart

我正在尝试让 USART 在 Mega2560 上正常工作。发生的情况是,每隔一段时间,我只收到从终端(Eclipse Serial Monitor)发送的字符串的前两个字符,或者整个字符串中缺少一个字符。我尝试检查帧、波特率和其他错误,但无济于事。我正在将 cPlusPlus 库用于 std::string 和 std::vector,我确实尝试使用 C 字符串(字符数组),但仍然存在问题,所以我认为这不会导致任何问题。

Eclipse 设置为在发送时向字符串添加换行符 ('\n'),并且我在对字符串执行任何代码之前等待该字符,但仍然存在问题。我最初使用 Arduino 串行库,但遇到了同样的问题,有时甚至更糟,这就是我选择使用 AVR USART 的原因。

我不确定这是否是一个问题,但我确实有 2 个计时器(4 和 5)在运行来控制程序的其他方面,这些是否会导致该问题?我确实尝试删除这些,但仍然得到相同的结果,也许我必须添加另一个命令(sei,cli)或在某个地方设置一些东西?我希望我没有遗漏一些明显的东西,下面是相关代码。

设置.h

const struct{
   uint16_t baud = 57600;
}settings;

USART序列

/**
 * Constructor
 */
USARTSerial::USARTSerial(){
    uint8_t baud = (F_CPU / (settings.baud * 16UL)) - 1;

    /*** Serial Setup ***/
    UBRR0H = (baud >> 8);
    UBRR0L = baud;

    //Transmit and receive enable
    UCSR0B |= (1 << TXEN0) | (1 << RXEN0);
    UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);
}

/**
 * Reads a single byte from the USART
 */
unsigned char USARTSerial::ReadChar(){

    loop_until_bit_is_set(UCSR0A, RXC0);

    //Get the received char
    return UDR0;
}

/**
 * Writes a single byte to the USART.
 */
void USARTSerial::WriteChar(char data){

    //Wait until byte is ready to be written    
    while((UCSR0A & (1<<UDRE0)) == 0){}

    // Transmit data
    UDR0 = data;
}

/**
 * Writes a std::string to the USART.
 */
void USARTSerial::Write(string data){

    //Loop unit we write all the data
    for (uint16_t i = 0; i < data.length(); i++){
        this->WriteChar(data[i]);
    }
}

主要

#include "Arduino.h"
#include "avr/interrupt.h"
#include "StandardCplusplus.h"
#include "string"
#include "Settings.h"
#include "USARTSerial.h"

std::string cmdStr;   /*** String to hold incoming data ***/
USARTSerial serial;   /*** Serial Interface ***/

void setup(){
    cli();

    //Setup Timer 5
    TCCR5A = 0;
    TCCR5B = 0;
    TCNT5  = 0;
    OCR5A = 16000;
    TCCR5B |= (1 << WGM52);
    TCCR5B |= (0 << CS52) | (0 << CS51) | (1 << CS50);
    TIMSK5 |= (1 << OCIE5A);

    //Setup Timer 4
    TCCR4A = 0;
    TCCR4B = 0;
    TCNT4 = 0;
    OCR4A = 40000;
    TCCR4B |= (1 << WGM12);
    TCCR4B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
    TIMSK4 |= (1 << OCIE4A);

    serial = USARTSerial();

    //Enable the Interrupts
    sei();
}


/**
 * ISR(Timer5 Compare A)
**/  
ISR(TIMER5_COMPA_vect)
{
    //Do things...
}

/**
 * ISR(Timer4 Compare A)
**/
ISR(TIMER4_COMPA_vect) {

   //Do some really cool stuff....
}

void loop(){
    char inChar;

    if(bit_is_set(UCSR0A, RXC0)){

        inChar = serial.ReadChar();

        //Check if we have a newline
        if (inChar == '\n'){
            serial.Write(cmdStr);
            cmdStr = "";
        }
        else{
            cmdStr += toupper(inChar);
        }
    }
}

编辑


感谢 Rev1.0 和 tofro 我终于让我的代码正常工作了。事实上,定时器引起了一些冲突,并且将 USART 转移到 ISR 中效果非常好。我还能够摆脱其中一个计时器,只需将操作移至主循环即可。我确实有一个问题是关于主循环中的一个小延迟,这是否与 std::stream 中的 sleep() 执行相同的操作?我知道你不应该在主循环中有延迟,除非你特别希望程序等待,但在我的测试过程中,添加延迟似乎可以完善 USART Rx。下面是更新后的代码......

USART序列

/**
 * Constructor
 */
USARTSerial::USARTSerial(){
    uint8_t baud = (F_CPU / (settings.baud * 16UL)) - 1;

    /*** Serial Setup ***/
    UBRR0H = (baud >> 8);
    UBRR0L = baud;

    //Transmit and receive enable
    UCSR0B |= (1 << TXEN0) | (1 << RXEN0) | (1 << RXCIE0); /** Added RXCIE0 for the USART ISR **/
    UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);
}

/**
 * Reads a single byte from the USART
 */
unsigned char USARTSerial::ReadChar(){

    loop_until_bit_is_set(UCSR0A, RXC0);

    //Get the received char
    return UDR0;
}

/**
 * Writes a single byte to the USART.
 */
void USARTSerial::WriteChar(char data){

    //Wait until byte is ready to be written    
    while((UCSR0A & (1<<UDRE0)) == 0){}

    // Transmit data
    UDR0 = data;
}

/**
 * Writes a std::string to the USART.
 */
void USARTSerial::Write(string data){

    //Loop unit we write all the data
    for (uint16_t i = 0; i < data.length(); i++){
        this->WriteChar(data[i]);
    }
}

/**
 * Available
 *
 * Returns if the USART is ready for reading
 */
bool USARTSerial::Available(){
    return bit_is_set(UCSR0A, RXC0);
}

主要

#include "Arduino.h"
#include "avr/interrupt.h"
#include "StandardCplusplus.h"
#include "string"
#include "Settings.h"
#include "USARTSerial.h"

std::string cmdStr;   /*** String to hold incoming data ***/
USARTSerial serial;   /*** Serial Interface ***/

void setup(){
    cli();

    //Setup Timer 5
    TCCR5A = 0;
    TCCR5B = 0;
    TCNT5  = 0;
    OCR5A = 16000;
    TCCR5B |= (1 << WGM52);
    TCCR5B |= (0 << CS52) | (0 << CS51) | (1 << CS50);
    TIMSK5 |= (1 << OCIE5A);

    serial = USARTSerial();

    //Enable the Interrupts
    sei();
}


/**
 * ISR(Timer5 Compare A)
**/  
ISR(TIMER5_COMPA_vect)
{
    //Do things...
}

/**
 * Main Loop
**/
void loop(){
     //Do some really cool stuff....
     delay (50);
} 

/**
 * USART Interrupt
 */
ISR(USART0_RX_vect){
    char inChar;

    if(serial.Available()){

        inChar = serial.ReadChar();

        //Check if we have a newline
        if (inChar == '\n'){
            /** Run code on the recieved data ***/
            cmdStr = "";
        }
        else{
            //Convert to Uppercase 
            cmdStr += toupper(inChar);
        }
    }
} 

最佳答案

我会将其作为答案发布,因为评论太长了:

我没有看到任何明显的问题,特别是当你说你从计时器 ISR 中删除了代码进行测试时。

但是,请考虑硬件 UART 使用两字节 FIFO。如果 Controller 在未读出 UDR 寄存器的情况下接收到两个以上字节,则 FIFO 中的数据将丢失/覆盖。您的情况可能会发生这种情况。它通常发生在两种情况下:

  1. 如果使用 RX ISR 处理 RX 数据,则必须确保其他 ISR 不会阻止代码执行。这就是 ISR 应包含尽可能少的代码的一般原因。

  2. 如果通过轮询 RXcflags来处理 RX 数据(如您的情况),您还必须确保“主循环”中没有任何内容阻止代码执行太长时间。

使用 ISR 而不是轮询 RXcflags是首选变体。

关于arduino - AVR USART通讯问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40390573/

相关文章:

timer - Arduino 纳米定时器

c - AVR编程,中断处理

php - 如何使用 PHP 读取串口

c++ - Arduino 上的十六进制乘法

c - 将代码从 ATmega32 修改为 Arduino Mega - 函数 `main' 中出现错误,即使没有

c - atmega8a 熔断外部晶振 16MHz

gcc - 我可以告诉 gcc/ld 排除未使用的模块吗?

c - 初始化一个变量,同时指定存储地址 : is it possible?

c - 在 C 中实现/模拟具有 20Mhz 时钟和 1024 预分频器的 16 位计数器

c++ - 是否可以用C++编写PLC梯形图程序?