【发布时间】:2017-03-16 09:25:30
【问题描述】:
我正在尝试让 USART 在 Mega2560 上正常工作。发生的事情是每隔一段时间我只收到从终端(Eclipse 串行监视器)发送的字符串中的前两个字符,或者整个字符串中缺少一个字符。我尝试检查帧、波特和其他错误无济于事。我正在为 std::string 和 std::vector 使用 cPlusPlus 库,我确实尝试使用 C 字符串(char 数组)但仍然有问题,所以我认为这不会导致任何问题。
Eclipse 设置为在发送时向字符串添加换行符 ('\n'),我在对字符串执行任何代码之前等待该字符,但仍然存在问题。我最初是从 Arduino 串行库开始的,但遇到了同样的问题,有时甚至更糟,这就是我选择使用 AVR USART 的原因。
我不确定这是否是一个问题,但我确实有 2 个计时器(4 和 5)正在运行以控制我的程序的其他方面,这些是否会导致问题?我确实尝试删除这些但仍然得到相同的结果也许我必须添加另一个命令(sei,cli)或在某处设置一些东西?我希望我没有遗漏一些明显的东西,下面是相关代码。
设置.h
const struct{
uint16_t baud = 57600;
}settings;
USARTSerial
/**
* 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。下面是更新后的代码......
USARTSerial
/**
* 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);
}
}
}
【问题讨论】:
-
代码对于 UART polling 看起来没问题。如果您的程序在相当长的时间内(超过 2 个字节时间)执行其他操作,您可能希望基于中断读取 UART。
-
@tofro 我想知道使用 ISR 是否可以解决问题。我将更改我的代码,看看是否有帮助。完成后我会回来检查...