首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >AVR USART通信问题

AVR USART通信问题
EN

Stack Overflow用户
提问于 2016-11-02 22:01:38
回答 1查看 1.4K关注 0票数 2

我正在努力使美援to在Mega2560上正常工作。每隔一段时间我就会从终端()发送的字符串()中只接收到前两个字符,或者整个字符串中缺少一个字符。我试过检查帧、波德和其他错误,但没有结果。我正在使用std::string和std::cPlusPlus库,我确实尝试使用C字符串(char数组),但是仍然存在问题,所以我不认为这会造成任何问题。

Eclipse的设置是在发送时向字符串添加一个换行符('\n'),我在执行字符串上的任何代码之前等待该字符,但仍然存在问题。我最初是从Arduino系列库开始的,但也遇到了同样的问题,更糟糕的是,这也是我选择使用AVR USART的原因。

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

Settings.h

代码语言:javascript
复制
const struct{
   uint16_t baud = 57600;
}settings;

USARTSerial

代码语言:javascript
复制
/**
 * 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]);
    }
}

Main

代码语言:javascript
复制
#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中的the ()执行相同的操作?我知道,除非您特别希望程序等待,否则主循环中不应该有延迟,但是在我的测试中,添加延迟似乎完善了USART。下面是更新的代码..。

USARTSerial

代码语言:javascript
复制
/**
 * 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);
}

Main

代码语言:javascript
复制
#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);
        }
    }
} 
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-11-03 07:14:20

我会把这个作为回答,因为它太长了,不能发表评论:

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

但是,考虑到硬件UART使用两个字节FIFO。如果控制器接收到更多的两个字节而不读出UDR寄存器,则FIFO中的数据将丢失/覆盖。在你的情况下可能会发生这种情况。这种情况通常发生在两种情况下:

  1. 如果使用RX处理RX数据,则必须确保其他ISR不会阻止代码执行。这就是ISR应该包含尽可能少的代码的一般原因。
  2. 如果RX数据是通过轮询RXC标志来处理的(就像在您的例子中那样),您还必须确保“主循环”中没有任何东西会阻止代码执行太长时间。

使用ISR而不是轮询RXC标志是首选的变体。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40390573

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档