我正在尝试让 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 中的数据将丢失/覆盖。您的情况可能会发生这种情况。它通常发生在两种情况下:
如果使用 RX ISR 处理 RX 数据,则必须确保其他 ISR 不会阻止代码执行。这就是 ISR 应包含尽可能少的代码的一般原因。
如果通过轮询 RXcflags来处理 RX 数据(如您的情况),您还必须确保“主循环”中没有任何内容阻止代码执行太长时间。
使用 ISR 而不是轮询 RXcflags是首选变体。
关于arduino - AVR USART通讯问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40390573/