Changeset - 104a7b37e488
[Not reviewed]
default
0 6 0
ethanzonca@CL-SEC241-08.cedarville.edu - 12 years ago 2012-11-28 23:45:20
ethanzonca@CL-SEC241-08.cedarville.edu
Rewrote the serial parser to use a circular buffer and interrupt-based system, which is much more robust. Untested, serial interrupt not firing at the moment.
6 files changed with 168 insertions and 65 deletions:
0 comments (0 inline, 0 general)
master/master/config.h
Show inline comments
 
@@ -22,14 +22,13 @@
 
 
 
// --------------------------------------------------------------------------
 
// Error Codes config (led.c)
 
// Error Codes config (led.c, used throughout code)
 
// --------------------------------------------------------------------------
 
 
// SD Card
 
#define ERROR_SD_INIT 2
 
#define ERROR_SD_PARTITION 3
 
 
 
// --------------------------------------------------------------------------
 
// Slave Sensors config (slavesensors.c)
 
// --------------------------------------------------------------------------
 
@@ -45,6 +44,19 @@
 
 
 
// --------------------------------------------------------------------------
 
// Command Parser config (serparser.c)
 
// --------------------------------------------------------------------------
 
 
// Maximum payload size of command
 
#define MAX_PAYLOAD_LEN 16
 
 
// Circular serial buffer size. Must be at least MAX_CMD_LEN + 5
 
#define BUFFER_SIZE 32 
 
 
// Public broadcast address
 
#define BROADCAST_ADDR 0 
 
 
// --------------------------------------------------------------------------
 
// USART config (serial.c)
 
// --------------------------------------------------------------------------
 
master/master/lib/serial.c
Show inline comments
 
@@ -14,12 +14,19 @@
 
#include "../config.h"
 
#include <avr/io.h>
 
 
// NOTE: USART ISRs for charachter reception are included in serparser.c
 
 
void serial0_setup() {
 
	PORTD &= ~(1<<PD0);
 
	PORTD |= (1<<PD1);
 
	
 
	/* Set baud rate */
 
	UBRR0H = (unsigned char)(USART0_BAUD_PRESCALE>>8);
 
	UBRR0L = (unsigned char)USART0_BAUD_PRESCALE;
 
	
 
	UCSR0A |= (1<<RXC0);
 
	/* Enable receiver and transmitter */
 
	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
 
	UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(RXCIE0); // Enable rx/tx/rx interrupt
 
	/* Set frame format: 8data, 1stop bit */
 
	UCSR0C = (3<<UCSZ00); // - 1 stop bit
 
}
master/master/lib/serparser.c
Show inline comments
 
@@ -10,104 +10,157 @@
 
 *
 
 */
 
 
// ************* Macros ***************
 
#define SERIAL_RX_HASBYTES UCSR0A & _BV(RXC0)
 
#define MAX_CMD_LEN 16
 
#define BROADCAST_ADDR 0 //public address
 
 
#include <avr/io.h>
 
#include "../config.h"
 
#include "serial.h"
 
//#define DEBUG
 
#include "serparser.h"
 
 
 
// Circular buffer for incoming data
 
uint8_t buffer[BUFFER_SIZE];
 
 
// ************* Command Definitions ***************
 
// Location of parser in the buffer
 
uint8_t bufferParsePosition = 0;
 
 
// Location of receive byte interrupt in the buffer
 
volatile uint16_t bufferDataPosition = 0;
 
 
// Incoming command buffer
 
uint8_t buffer[MAX_CMD_LEN+2];
 
// Parser state
 
uint8_t parserState = STATE_RESET;
 
uint8_t lastParserState = STATE_RESET;
 
 
// Current buffer location
 
uint8_t bufferPosition = 0;
 
// Length of current payload data (and checksum)
 
int dataLength = 0;
 
 
//current length of the data transmission (and checksum)
 
int dataLength;
 
// Data and checksum of most recent transmission
 
char receivedPayload[MAX_PAYLOAD_LEN];
 
 
// Payload type ID of the sensor of most recent transmission
 
char receivedDataType = 0;
 
 
//data (and checksum) of most recent transmission
 
char data[16];
 
// Checksum to be calculated and then compared to the received checksum
 
char checksumCalc = 0;
 
 
//ID of the sensor of most recent transmission
 
char sensorID;
 
// Could inline if program space available
 
static void setParserState(uint8_t state)
 
{
 
	lastParserState = parserState;
 
	parserState = state;
 
 
//checksum to be calculated and then compared to the received checksum
 
char checksumCalc;
 
	// If resetting, clear vars
 
	if(state == STATE_RESET) 
 
	{
 
		dataLength = 0;
 
		checksumCalc = 0;
 
	}
 
 
/* Process incoming serial data */
 
// TODO: Maybe make this suck up the whole buffer if there is more to be had. That would make this much more efficient.
 
// we could parse while(SERIAL_RX_HASBYTES && notOverTimeout), don't have it parse for more than 100ms straight or something...
 
	// Every time we change state, we have parsed a byte
 
	bufferParsePosition = (bufferParsePosition + 1) % BUFFER_SIZE;
 
}
 
 
// Receive data on USART
 
ISR(USART0_RX_vect) 
 
{
 
	buffer[bufferDataPosition % BUFFER_SIZE] = UDR0;
 
	bufferDataPosition = (bufferDataPosition + 1) % BUFFER_SIZE;
 
}
 
 
 
// Parse data from circular buffer
 
int serparser_parse(void)
 
{
 
 
	char byte;
 
 
	if(SERIAL_RX_HASBYTES) // Only process data if buffer has a byte
 
	// Process first command (if any) on the circular buffer
 
	while(bufferDataPosition != bufferParsePosition)
 
	{
 
		
 
		// Pull byte off of the buffer
 
		byte = serial0_readChar();
 
		buffer[bufferPosition] = byte;
 
		byte = buffer[bufferParsePosition];
 
		
 
		// byte checking/validation
 
		if(bufferPosition == 0)
 
		// Reset 
 
		if(parserState == STATE_RESET)
 
		{
 
			if(byte == '[') // If this is a start byte, we're OK. Keep reading.
 
			if(byte == '[') // Start of frame; keep parsing
 
			{
 
				bufferPosition++;
 
				serial0_sendString("PARSER: start ok\r\n");
 
				setParserState(STATE_GETID);
 
			}
 
			else // Not a start byte that we're expecting. Reset to initial state.
 
			else // Not start of frame, reset
 
			{
 
				bufferPosition = 0;
 
				serial0_sendString("PARSER: not start, reset\r\n");
 
				setParserState(STATE_RESET);
 
				return PARSERESULT_NODATA; // no valid start bit; better luck next time. run the function the next time around the main loop.
 
			}
 
		}
 
		else if(bufferPosition == 1)
 
		
 
		// Get destination module ID
 
		else if(parserState == STATE_GETID)
 
		{
 
			if(byte == MODULE_ID) //this transmission is intended for the master; continue parsing
 
			if(byte == MODULE_ID) // Message intended for this module; keep parsing
 
			{
 
				bufferPosition++;
 
				serial0_sendString("SER: dest ok\r\n");
 
				checksumCalc += byte;
 
				dataLength = 0; //reset dataLength here to protect from bad inputs
 
				setParserState(STATE_GETDATATYPE);
 
			}
 
			else //this transmission is intended for another module; stop parsing and reset
 
			else // Transmission is intended for another module; reset
 
			{
 
				bufferPosition = 0;
 
				serial0_sendString("SER: bad dest, reset\r\n");
 
				setParserState(STATE_RESET);
 
			}
 
		}
 
		else if(bufferPosition == 2)
 
		
 
		// Get payload type ID
 
		else if(parserState == STATE_GETDATATYPE)
 
		{
 
			sensorID = byte; //store the type of data receiving
 
			bufferPosition++;
 
			serial0_sendString("SER: type ok\r\n");
 
			receivedDataType = byte; // Store the type of data receiving
 
			checksumCalc += byte;
 
			setParserState(STATE_GETDATA);
 
		}
 
		else if (bufferPosition == MAX_CMD_LEN) //command is too long and bad data has been recieved; reset
 
		
 
		// Get payload data
 
		else if(parserState == STATE_GETDATA)
 
		{		
 
			if (byte == ']') // End of frame
 
		{
 
			bufferPosition = 0;
 
				serial0_sendString("SER: eof ok\r\n");
 
				if(bufferParsePosition == bufferDataPosition) 
 
				{
 
					// We are at the end of the line. No more data to parse.
 
					setParserState(STATE_RESET);
 
					return PARSERESULT_PARSEOK;
 
		}
 
		else
 
		{
 
			if (byte == ']') //this is the end of the transmission
 
			{
 
				bufferPosition = 0;
 
				return 2; // got whole msg
 
					setParserState(STATE_RESET);
 
					// we could choose to keep parsing now, or parse the next message next loop around (better idea).
 
					// return now so we hit it the next time around
 
					return PARSERESULT_PARSEOK;
 
				}
 
			}
 
			else //still receiving data
 
			else // Still receiving data
 
			{
 
				data[dataLength] = byte;
 
				serial0_sendString("SER: data ok\r\n");
 
				receivedPayload[dataLength] = byte;
 
				dataLength++;
 
				bufferPosition++;
 
				checksumCalc += byte;
 
				
 
				// Data buffer overrun protection
 
				if(dataLength > MAX_PAYLOAD_LEN) {
 
					serial0_sendString("SER: buf overflow, reset\r\n");
 
					setParserState(STATE_RESET);
 
					return PARSERESULT_FAIL;
 
				}
 
				else {
 
					// Set state. MUST call even though state is maintained to update parse position
 
					setParserState(STATE_GETDATA);
 
					return PARSERESULT_STILLPARSING;
 
				}
 
				
 
			}
 
 
		}
 
	}
 
	else
 
	{
 
		return 0; // serial data not available
 
	}
 
	return PARSERESULT_NODATA;
 
	
 
}
 
\ No newline at end of file
master/master/lib/serparser.h
Show inline comments
 
@@ -14,6 +14,23 @@
 
#ifndef SERPARSER_H_
 
#define SERPARSER_H_
 
 
enum parseResults
 
{
 
	PARSERESULT_FAIL = 0,
 
	PARSERESULT_NODATA,
 
	PARSERESULT_STILLPARSING,
 
	PARSERESULT_PARSEOK,
 
};
 
 
// Parser states
 
enum parseStates
 
{
 
	STATE_RESET = 0,
 
	STATE_GETID,
 
	STATE_GETDATATYPE,
 
	STATE_GETDATA
 
};
 
 
// Prototypes
 
int serparser_parse(void);
 
master/master/lib/slavesensors.c
Show inline comments
 
@@ -55,16 +55,20 @@ void slavesensors_setup()
 
	slaves[7] = SLAVE7_SENSORS;	
 
}
 
 
bool slavesensors_process() 
 
bool slavesensors_process(uint8_t parseResult) 
 
{
 
	// Periodic: Every Iteration
 
		
 
	// maybe we should do this in an ISR on byte received. only problem is that this could interrupt things,
 
	// but we only care about interruption during logging and such. don't log when parsing a message?
 
	receptionFinished = serparser_parse();
 
	
 
	// not anymore:
 
	//receptionFinished = serparser_parse();
 
	// now we need some way to know if a transmission finished. Probably set a flag in main from the
 
	// return that is "hasUnhandledTransmission = true"
 
		
 
	// Finished reception of a message (one sensor data value). If not finished, send out command to get the next one
 
	if(receptionFinished == 2)
 
	if(parseResult == PARSERESULT_PARSEOK)
 
	{
 
		bool weFinishedTHeLastSlaveSensor = false; // ex.
 
		// if we finished the final reception for this slave, set gettingValues = false;
 
@@ -84,6 +88,7 @@ bool slavesensors_process()
 
			return true; // Keep going...
 
		}
 
	}
 
	// TODO: Handle errors. If we got a parse fail, then we probably need to retransmit the message. etc.
 
	else 
 
	{
 
		return true; // Keep going...
master/master/master.c
Show inline comments
 
@@ -29,6 +29,7 @@
 
#include "lib/sd/sd_raw_config.h"
 
#include "lib/looptime.h"
 
#include "lib/slavesensors.h"
 
#include "lib/serparser.h"
 

	
 
void micro_setup() 
 
{
 
@@ -49,11 +50,12 @@ int main(void)
 
	logger_setup();
 
	afsk_setup();
 

	
 
	serial0_sendString("\r\n\r\n---------------------------------\r\nHAB Controller 1.0 - Initialized!\r\n---------------------------------\r\n\r\n");
 
	serial0_sendString("\r\n\r\n---------------------------------\r\n");
 
	serial0_sendString("HAB Controller 1.0 - Initialized!\r\n");
 
	serial0_sendString("---------------------------------\r\n\r\n");
 
	
 
	led_on(POWER);
 
	
 
	
 
	// Buffer for string operations
 
	char logbuf[32];
 
	const char* logBufPtr = logbuf;
 
@@ -65,6 +67,10 @@ int main(void)
 
	// If we are currently processing sensor data
 
	bool isProcessing = false;
 
	
 
	// Result of last parser run
 
	int parseResult = PARSERESULT_NODATA;
 
	bool haveDataToHandle = false;
 
	
 
	// Write CSV header to SD card
 
	logger_log("ProgramTime,LastAprsBroadcast,LastLog\n");
 
	
 
@@ -105,10 +111,13 @@ int main(void)
 
			lastAprsBroadcast = time_millis();
 
		}			
 
		
 
		// keep calling processSensors until it is finished
 
		parseResult = serparser_parse();
 
		
 
		// TODO: If we receive a command, we should still process it even if we aren't isProcessing, right?
 
		// Maybe we should isolate the received command handling ("Execution") and the issuing of requests.
 
		if(isProcessing) 
 
		{
 
			isProcessing = slavesensors_process();
 
			isProcessing = slavesensors_process(parseResult);
 
		}
 
		wdt_reset();
 
    }
0 comments (0 inline, 0 general)