AVR USART 通信问题
AVR USART Communication problems
我正在尝试让 USART 在 Mega2560 上正常工作。发生的事情是每隔一段时间我只收到从终端(Eclipse 串行监视器)发送的字符串中的前两个字符,或者整个字符串中缺少一个字符。我已尝试检查帧、波特率和其他错误,但均无济于事。我正在为 std::string 和 std::vector 使用 cPlusPlus 库,我确实尝试使用 C 字符串(字符数组)但仍然有问题,所以我认为这不会导致任何问题。
Eclipse 设置为在发送时向字符串添加一个换行符 ('\n'),我在对字符串执行任何代码之前等待该字符,但问题仍然存在。我最初是从 Arduino 串行库开始的,但遇到了同样的问题,有时甚至更糟,这就是我选择使用 AVR USART 的原因。
我不确定这是否是个问题,但我确实有 2 个计时器(4 和 5)运行 控制我程序的其他方面,这些是否会导致问题?我确实尝试删除这些但仍然得到相同的结果也许我必须添加另一个命令(sei,cli)或在某处设置一些东西?我希望我没有遗漏一些明显的东西,下面是相关代码。
Settings.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);
}
}
}
我将 post 作为答案,因为评论太长了:
我没有看到任何明显的问题,尤其是当您说您从定时器 ISR 中删除了用于测试的代码时。
但是,请考虑硬件 UART 使用两字节 FIFO。如果控制器在未读出 UDR 寄存器的情况下接收到更多的两个字节,则 FIFO 中的数据将为 lost/overwritten。这可能发生在你的情况下。它通常发生在两种情况下:
如果使用 RX ISR 处理 RX 数据,则必须确保其他 ISR 未阻止代码执行。这就是 ISR 应包含尽可能少的代码的一般原因。
如果 RX 数据是通过轮询 RXC 标志来处理的(就像您的情况一样),您还必须确保 "main loop" 中的任何内容都不会长时间阻塞代码执行。
使用 ISR 而不是轮询 RXC 标志是首选变体。
我正在尝试让 USART 在 Mega2560 上正常工作。发生的事情是每隔一段时间我只收到从终端(Eclipse 串行监视器)发送的字符串中的前两个字符,或者整个字符串中缺少一个字符。我已尝试检查帧、波特率和其他错误,但均无济于事。我正在为 std::string 和 std::vector 使用 cPlusPlus 库,我确实尝试使用 C 字符串(字符数组)但仍然有问题,所以我认为这不会导致任何问题。
Eclipse 设置为在发送时向字符串添加一个换行符 ('\n'),我在对字符串执行任何代码之前等待该字符,但问题仍然存在。我最初是从 Arduino 串行库开始的,但遇到了同样的问题,有时甚至更糟,这就是我选择使用 AVR USART 的原因。
我不确定这是否是个问题,但我确实有 2 个计时器(4 和 5)运行 控制我程序的其他方面,这些是否会导致问题?我确实尝试删除这些但仍然得到相同的结果也许我必须添加另一个命令(sei,cli)或在某处设置一些东西?我希望我没有遗漏一些明显的东西,下面是相关代码。
Settings.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);
}
}
}
我将 post 作为答案,因为评论太长了:
我没有看到任何明显的问题,尤其是当您说您从定时器 ISR 中删除了用于测试的代码时。
但是,请考虑硬件 UART 使用两字节 FIFO。如果控制器在未读出 UDR 寄存器的情况下接收到更多的两个字节,则 FIFO 中的数据将为 lost/overwritten。这可能发生在你的情况下。它通常发生在两种情况下:
如果使用 RX ISR 处理 RX 数据,则必须确保其他 ISR 未阻止代码执行。这就是 ISR 应包含尽可能少的代码的一般原因。
如果 RX 数据是通过轮询 RXC 标志来处理的(就像您的情况一样),您还必须确保 "main loop" 中的任何内容都不会长时间阻塞代码执行。
使用 ISR 而不是轮询 RXC 标志是首选变体。