Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
8 years, 7 months ago.
inconsistent hanging on Serial.attach when placed inside class (on NUCLEO L073RZ)
I'm integrating a GPS that communicates over serial UART. I have a test case working perfectly, using RawSerial library and Tx/Rx IRQ interrupts. But when I integrate this code into it's own class within a larger program, the program will SOMETIMES hang when it gets to the serial.attach functions. I say "sometimes" because about 1/3rd of the time, the program actually executes properly and moves beyond the attach functions.
Actual program has many more modules and a few interrupts, but I have been able to isolate the issue to the following stripped down code (uses mbed OS 5):
main.cpp of malfunctioning code
#include "mbed.h"
#include "ORG4400.h"
#include <string.h>
int initGPS = 0;
ORG4400 gps(PB_3, PB_4, true); // tx:D3->PB_3, rx:D5->PB_4
/***** Inititalization Methods *****/
void initSensors() {
printf("----- Initializing Sensors -----\n\n");
printf("Initializing GPS...\n");
gps.begin();
printf("GPS initialized\n\n");
initGPS = 1;
printf("----- Finished Initializing Sensors -----\n\n");
}
/***** End IMU Wrapper Functions *****/
int main() {
initSensors();
while(1) {
/*if (initGPS) {
int attempts = 0;
while (attempts < 10){
//std::string *gpsJSON;
std::string msg = gps.readMSG();
if (msg != "") {
//printf("received valid NMEA message:\n%s\n\n", msg.c_str());
attempts = 10;
if (commWIFI.transmitJSON(msg) == 1) printf("message sent successfully\n\n");
else printf("message not sent successfully\n\n");
}
else {
attempts++;
}
}
}*/
wait(2);
}
}
GPS class library file of malfunctioning code
#include "ORG4400.h"
#include "mbed.h"
#include <string.h>
ORG4400::ORG4400(PinName tx, PinName rx, bool debug) : _serial(tx,rx)
{
_serial.baud(4800);
_debug = debug;
if (_debug) printf("in ORG4400 constructor\n");
tx_in=0;
tx_out=0;
rx_in=0;
rx_out=0;
status = GPS_NOT_READY;
if (_debug) printf("before attach\n");
_serial.attach(callback(this, &ORG4400::Rx_interrupt), RawSerial::RxIrq);
_serial.attach(callback(this, &ORG4400::Tx_interrupt), RawSerial::TxIrq);
//_serial.attach(this, &ORG4400::Rx_interrupt, RawSerial::RxIrq); Deprecated in OS 5 -- have tried, still hangs
//_serial.attach(this, &ORG4400::Tx_interrupt, RawSerial::RxIrq); Deprecated in OS 5 -- have tried, still hangs
if (_debug) printf("after attach\n");
}
gps_mode_t ORG4400::begin()
{
if (_debug) printf("in ORG4400 begin\n");
int rx_i=0;
while (1) {
read_line();
sscanf(rx_line,"%c",&rx_i);
if (rx_line[0] == '$') {
//if (_parser.parse(rx_line)) {
if (_debug) printf("Valid NMEA line\n");
status = GPS_FULL_PWR;
return status;
}
else {
//add a timeout here
}
}
}
// Read a line from the large rx buffer from rx interrupt routine
void ORG4400::read_line() {
int i;
i = 0;
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(USART4_5_IRQn);
// Loop reading rx buffer characters until end of line character
while ((i==0) || (rx_line[i-1] != '\r')) {
// Wait if buffer empty
if (rx_in == rx_out) {
// End Critical Section - need to allow rx interrupt to get new characters for buffer
NVIC_EnableIRQ(USART4_5_IRQn);
while (rx_in == rx_out) {
}
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(USART4_5_IRQn);
}
rx_line[i] = rx_buffer[rx_out];
i++;
rx_out = (rx_out + 1) % buffer_size;
}
// End Critical Section
NVIC_EnableIRQ(USART4_5_IRQn);
rx_line[i-1] = 0;
return;
}
// Interupt Routine to read in data from serial port
void ORG4400::Rx_interrupt() {
// Loop just in case more than one character is in UART's receive FIFO buffer
// Stop if buffer full
while ((_serial.readable()) && (((rx_in + 1) % buffer_size) != rx_out)) {
rx_buffer[rx_in] = _serial.getc();
rx_in = (rx_in + 1) % buffer_size;
}
return;
}
// Interupt Routine to write out data to serial port
void ORG4400::Tx_interrupt() {
// Loop to fill more than one character in UART's transmit FIFO buffer
// Stop if buffer empty
while ((_serial.writeable()) && (tx_in != tx_out)) {
_serial.putc(tx_buffer[tx_out]);
tx_out = (tx_out + 1) % buffer_size;
}
return;
}
GPS module library header file of malfunctioning code
#ifndef __SP_ORG4400_H__
#define __SP_ORG4400_H__
#include "stdint.h"
#include "mbed.h"
#include <string.h>
typedef enum
{
GPS_FULL_PWR,
GPS_STANDBY,
GPS_HIBERNATE,
GPS_ATP,
GPS_PTF,
GPS_APM,
GPS_MPM,
GPS_NOT_READY,
//...
} gps_mode_t;
class ORG4400
{
public:
gps_mode_t status;
ORG4400(PinName tx, PinName rx, bool debug);
gps_mode_t begin();
private:
RawSerial _serial;
bool _debug;
static const int buffer_size = 255; // Circular buffers for serial TX and RX data - used by interrupt routines
char volatile tx_buffer[buffer_size+1];
char volatile rx_buffer[buffer_size+1];
int volatile tx_in;
int volatile tx_out;
int volatile rx_in;
int volatile rx_out;
// Line buffers for sprintf and sscanf
char tx_line[80];
char rx_line[80];
void Tx_interrupt();
void Rx_interrupt();
void send_line();
void read_line();
};
#endif // __SP_ORG4400_H__ //
The test case that is working perfectly, with practically the same code, is:
#include "mbed.h"
#include <string.h>
RawSerial device(PB_3, PB_4); // tx, rx
void Tx_interrupt();
void Rx_interrupt();
void send_line();
void read_line();
const int buffer_size = 255;
char tx_buffer[buffer_size+1];
char rx_buffer[buffer_size+1];
int volatile tx_in=0;
int volatile tx_out=0;
int volatile rx_in=0;
int volatile rx_out=0;
char tx_line[80];
char rx_line[80];
int main() {
int i=0;
int rx_i=0;
device.baud(4800);
device.attach(&Rx_interrupt, RawSerial::RxIrq);
device.attach(&Tx_interrupt, Serial::TxIrq);
while (1) {
read_line();
sscanf(rx_line,"%c",&rx_i);
printf("received '%s'\n",rx_line);
}
}
// Copy tx line buffer to large tx buffer for tx interrupt routine
void send_line() {
int i;
char temp_char;
bool empty;
i = 0;
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(USART4_5_IRQn);
empty = (tx_in == tx_out);
while ((i==0) || (tx_line[i-1] != '\n')) {
// Wait if buffer full
if (((tx_in + 1) % buffer_size) == tx_out) {
// End Critical Section - need to let interrupt routine empty buffer by sending
NVIC_EnableIRQ(USART4_5_IRQn);
while (((tx_in + 1) % buffer_size) == tx_out) {
}
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(USART4_5_IRQn);
}
tx_buffer[tx_in] = tx_line[i];
i++;
tx_in = (tx_in + 1) % buffer_size;
}
if (device.writeable() && (empty)) {
temp_char = tx_buffer[tx_out];
tx_out = (tx_out + 1) % buffer_size;
// Send first character to start tx interrupts, if stopped
device.putc(temp_char);
}
// End Critical Section
NVIC_EnableIRQ(USART4_5_IRQn);
return;
}
// Read a line from the large rx buffer from rx interrupt routine
void read_line() {
int i;
i = 0;
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(USART4_5_IRQn);
// Loop reading rx buffer characters until end of line character
while ((i==0) || (rx_line[i-1] != '\r')) {
// Wait if buffer empty
if (rx_in == rx_out) {
// End Critical Section - need to allow rx interrupt to get new characters for buffer
NVIC_EnableIRQ(USART4_5_IRQn);
while (rx_in == rx_out) {
}
// Start Critical Section - don't interrupt while changing global buffer variables
NVIC_DisableIRQ(USART4_5_IRQn);
}
rx_line[i] = rx_buffer[rx_out];
i++;
rx_out = (rx_out + 1) % buffer_size;
}
// End Critical Section
NVIC_EnableIRQ(USART4_5_IRQn);
rx_line[i-1] = 0;
return;
}
// Interupt Routine to read in data from serial port
void Rx_interrupt() {
// Loop just in case more than one character is in UART's receive FIFO buffer
// Stop if buffer full
while ((device.readable()) && (((rx_in + 1) % buffer_size) != rx_out)) {
rx_buffer[rx_in] = device.getc();
rx_in = (rx_in + 1) % buffer_size;
}
return;
}
// Interupt Routine to write out data to serial port
void Tx_interrupt() {
// Loop to fill more than one character in UART's transmit FIFO buffer
// Stop if buffer empty
while ((device.writeable()) && (tx_in != tx_out)) {
device.putc(tx_buffer[tx_out]);
tx_out = (tx_out + 1) % buffer_size;
}
return;
}
The main difference is that the code is packaged into its own class and member variables/functions. I have played with declaring the important member variables as globals (to mimic the test case), have tried changing
I've also tried calling _serial.getc after the while loop in interrupts, before return is called, in case the serial device is not writeable/readable upon first entering the interrupt. I did this because I've read that serial interrupts might loop infinitely until at least one getc/putc is called (see this thread for one reference: https://developer.mbed.org/questions/167/Controller-hangs-once-a-serial-interrupt/)
I have read of a similar issue on some boards, running mbed OS 2, and someone suggested disabling the UART before attach and re-enabling after attach (see this thread for reference: https://developer.mbed.org/questions/77509/Embed-hanged-when-use-Serial-attachfun-m/). I have not had luck successfully disabling and re-enabling in the test case, as I believe it's performed differently in OS 5. For instance tried:
__USART5_FORCE_RESET(); __USART5_RELEASE_RESET(); __USART5_CLK_DISABLE(); .... attach functions .... __USART5_CLK_ENABLE();
but this caused even the test case to run improperly (obviously doing something wrong there). Any suggestions extremely appreciated.