I am trying to get the USART working correctly on a Mega2560. What is happening is every once in a while I only receive the first two characters from a string sent from the terminal (Eclipse Serial Monitor) or one character is missing from the entire string. I have tried checking for Frame, Baud and other errors to no avail. I am using the cPlusPlus Library for std::string and std::vector, I did try using C strings (char array) but still had the issue so I don't think that this is causing any problems.
Eclipse is setup to add a newline ('\n') to the string when sending and I wait for that character before executing any code on the string, but still have the issue. I originally started with the Arduino Serial library but had the same problem, even worse sometimes, that is why I opted to use the AVR USART.
I am not sure if this is an issue but I do have 2 timers (4 and 5) running to control other aspects of my program, could these be contributing to the issue? I did try removing these and still got the same result maybe I have to add another command (sei, cli) or set something somewhere? I hope I am not missing something obvious, below is the relevant code.
Settings.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]);
}
}
Main
#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);
}
}
}
EDIT
Thanks to Rev1.0 and tofro I finally got my code working correctly. Indeed the Timers were causing some conflicts and moving the USART into a ISR worked beautifully. I was also able to get rid of one of the timers and simply move the operations into the main loop. One question I do have is about a small delay I have in the main loop, is this performing the same action as sleep() in std::stream? I know that you shouldn't have a delay in the main loop unless you specifically want the program to wait but during my testing adding the delay seemed to perfect the USART Rx. Below is the updated code....
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);
}
Main
#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);
}
}
}