diff --git a/arduino/vx8_gps_16mhz/vx8_gps_16mhz.ino b/arduino/vx8_gps_16mhz/vx8_gps_16mhz.ino index a7884a6..1bde8a0 100644 --- a/arduino/vx8_gps_16mhz/vx8_gps_16mhz.ino +++ b/arduino/vx8_gps_16mhz/vx8_gps_16mhz.ino @@ -1,12 +1,17 @@ /* - vx8_gps.ino - - Created on: 16 Jan 2016 - Author: 4Z7DTF - Repository: https://github.com/4z7dtf/vx8_gps - Decription: Arduino code of GPS module for Yaesu VX-8DR/DE transceivers. - Verified on 28 March 2016 with Arduino Nano 16MHz/5V and Arduino 1.6.8 IDE. -*/ + * vx8_gps.ino + * + * Created on: 16 Jan 2016 + * Author: Dmitry Melnichansky 4Z7DTF + * Repository: https://github.com/4z7dtf/vx8_gps + * Decription: Arduino code of GPS module for Yaesu VX-8DR/DE transceivers. + * + * 2016-01-16 The program created. + * 2016-03-28 Verified with Arduino Nano 16MHz/5V and Arduino 1.6.8 IDE. + * 2016-04-01 USART Buffer Empty interrupt used for TX. TX routines removed + * completely from the main loop. + * Verified with Arduino Nano at 16MHz and Arduino 1.6.8 IDE. + */ /* No fix: @@ -60,10 +65,10 @@ #define ALL_OFF 0B00000011 /* Different sources state that maximum sentence length is 80 characters - plus CR and LF. Actual Yaesu FGPS-2 GPS output shows that this standard - is ignored and GGA message reaches 86 symbols. That's why the buffer sizes - are limited to 90 characters instead of 82. -*/ + * plus CR and LF. Actual Yaesu FGPS-2 GPS output shows that this standard + * is ignored and GGA message reaches 86 symbols. That's why the buffer sizes + * are limited to 90 characters instead of 82. + */ #define BUFFER_SIZE 90 #define COMMA ',' #define DOLLAR '$' @@ -78,7 +83,7 @@ enum rx_states READY = 0x00, /* Default state, ready to receive. Changes if $ is received. */ RX_MESSAGE = 0x01, /* Receiving the message between the $ and * delimiters. */ RX_CHECKSUM = 0x02, /* Receiving the checksum. Changes if \r\n is received. */ - COPY_TO_TX = 0x03, /* Default state, ready to receive. Changes if $ is received. */ + START_TX = 0x03, /* Default state, ready to receive. Changes if $ is received. */ }; uint8_t state; /* Current system state. */ @@ -87,24 +92,23 @@ enum nmea_commands NONE, GGA, RMC, ZDA }; uint8_t rx_command; /* NMEA command being received. */ -uint8_t rx_field; /* Current field of NMEA command. */ +uint8_t rx_field_num; /* Current field of NMEA command. */ uint8_t rx_field_size; /* Current field size. */ char rx_buffer[BUFFER_SIZE]; /* Buffer for the received message. */ uint8_t rx_buf_pos; -bool rx_byte_ready; /* Received byte ready flag. */ -uint8_t rx_byte; /* Received byte. */ +volatile uint8_t rx_byte; /* Received byte. */ +uint8_t tbp_byte; /* Byte to process. */ uint8_t calc_checksum; /* Calculated checksum of the received message. Calculated on the fly. */ uint8_t rx_checksum; /* Checksum of the received NMEA sentence. */ -/* A lookup table for converting numerical values to hexadecimal digits. */ +/* Lookup table for converting numerical values to hexadecimal digits. */ char hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /* TX variables */ char tx_buffer[BUFFER_SIZE]; /* Buffer for the message to be sent. */ uint8_t tx_buf_pos; -bool tx_not_empty; /* TX has a message to send. Set to false when tx_buffer is empty. */ -bool tx_rts; /* TX ready to send. Set to true when RX is ready to send next byte. */ +volatile bool tx_has_data; /* TX has a message to send. Set to false when tx_buffer is empty. */ void setup(void) { @@ -112,81 +116,67 @@ void setup(void) PORTD &= ALL_OFF; usart_init(); reset_tx(); - tx_rts = true; reset_rx(); + rx_byte = NO_DATA; state = READY; sei(); } void loop(void) { - /* Next byte is sent if there is one and TX is ready to send. - If null terminator or end of buffer is reached, TX is reset. - */ - if (tx_rts && tx_not_empty) + while (1) { - if (tx_buf_pos < BUFFER_SIZE && tx_buffer[tx_buf_pos] != NO_DATA) - { - UDR0 = tx_buffer[tx_buf_pos]; - tx_buf_pos++; - tx_rts = false; - } - else + /* RX routine */ + if(rx_byte) { - reset_tx(); + tbp_byte = rx_byte; + rx_byte = NO_DATA; } - } - /* RX routine */ - switch (state) - { + switch (state) + { case READY: /* READY: The system is ready to receive and remains is this state - until $ character is received. - */ - if (rx_byte_ready) + * until $ character is received. + */ + if (tbp_byte == DOLLAR) { - rx_byte_ready = false; - if (rx_byte == DOLLAR) - { - rx_buffer[rx_buf_pos] = rx_byte; - rx_buf_pos++; - state = RX_MESSAGE; - } + rx_buffer[rx_buf_pos] = tbp_byte; + rx_buf_pos++; + state = RX_MESSAGE; } + tbp_byte = NO_DATA; break; case RX_MESSAGE: /* RX_MESSAGE: The system receives the message between $ and * - delimiters. NMEA sentence checksum is calculated on the fly. - Comma character marks end of field. Each time it is - received, the field is verified and changed to VX-8 specific - format if required. Changing the fields on the fly results - in getting a new VX-8 compatible message at the end of reception. - NMEA sentences with empty time fields are discarded. The system - stops receiving current sentence and returns to READY. This is - done to prevent sending false time to VX-8. Other empty fields - are filled with default values, in most cases zeros. That's why - VX-8 shows zeros in coordinate fields when GPS fix isn't acquired - or is lost. - When * character is received the state changes to RX_CHECKSUM. - */ - if (rx_byte_ready) + * delimiters. NMEA sentence checksum is calculated on the fly. + * Comma character marks end of field. Each time it is + * received, the field is verified and changed to VX-8 specific + * format if required. Changing the fields on the fly results + * in getting a new VX-8 compatible message at the end of reception. + * NMEA sentences with empty time fields are discarded. The system + * stops receiving current sentence and returns to READY. This is + * done to prevent sending false time to VX-8. Other empty fields + * are filled with default values, in most cases zeros. That's why + * VX-8 shows zeros in coordinate fields when GPS fix isn't acquired + * or is lost. + * When * character is received the state changes to RX_CHECKSUM. + */ + if (tbp_byte) { - rx_byte_ready = false; - /* If received character is $ or the buffer is overflown, - reset and return to READY state. - */ - if (rx_byte == DOLLAR || rx_buf_pos >= BUFFER_SIZE) + * reset and return to READY state. + */ + if (tbp_byte == DOLLAR || rx_buf_pos >= BUFFER_SIZE) { reset_rx(); break; } /* Comma and marks end of field, asterisk marks end of message - which is also end of the last field. - */ - if (rx_byte == COMMA || rx_byte == ASTERISK) + * which is also end of the last field. + */ + if (tbp_byte == COMMA || tbp_byte == ASTERISK) { bool field_valid = process_field(); if (!field_valid) @@ -194,7 +184,7 @@ void loop(void) reset_rx(); break; } - rx_field++; + rx_field_num++; rx_field_size = 0; } else @@ -202,45 +192,44 @@ void loop(void) rx_field_size++; } - rx_buffer[rx_buf_pos] = rx_byte; + rx_buffer[rx_buf_pos] = tbp_byte; rx_buf_pos++; /* If end of message, change state to RX_CHECKSUM - without affecting the calculated checksum. - */ - if (rx_byte == ASTERISK) + * without affecting the calculated checksum. + */ + if (tbp_byte == ASTERISK) { state = RX_CHECKSUM; } else { - calc_checksum ^= rx_byte; + calc_checksum ^= tbp_byte; } + + tbp_byte = NO_DATA; } break; case RX_CHECKSUM: /* RX_CHECKSUM: The system receives the checksum (first two bytes - after *). After CR (\r) character is received, the system - compares it to the calculated value. If two values match, - a new checksum is calculated and added to the message. - Upon receiving the LF (\n) character which marks end of sentence - system state changes to COPY_TO_TX. - */ - if (rx_byte_ready) + * after *). After CR (\r) character is received, the system + * compares it to the calculated value. If two values match, + * a new checksum is calculated and added to the message. + * Upon receiving the LF (\n) character which marks end of sentence + * system state changes to START_TX. + */ + if (tbp_byte) { - rx_byte_ready = false; - /* If received character is $ or * or the buffer is overflown, - reset and return to READY state. - */ - if (rx_byte == DOLLAR || rx_byte == ASTERISK - || rx_buf_pos >= BUFFER_SIZE) + * reset and return to READY state. + */ + if (tbp_byte == DOLLAR || tbp_byte == ASTERISK || rx_buf_pos >= BUFFER_SIZE) { reset_rx(); break; } /* CR (\r) is received after the last character of checksum. */ - else if (rx_byte == CR) + else if (tbp_byte == CR) { /* If match, calculate a new one, else reset. */ if (rx_checksum == calc_checksum) @@ -250,12 +239,11 @@ void loop(void) { checksum ^= rx_buffer[i]; } - rx_buffer[rx_buf_pos] = - hex_chars[(checksum & 0xF0) >> 4]; + rx_buffer[rx_buf_pos] = hex_chars[(checksum & 0xF0) >> 4]; rx_buf_pos++; rx_buffer[rx_buf_pos] = hex_chars[checksum & 0x0F]; rx_buf_pos++; - rx_buffer[rx_buf_pos] = rx_byte; + rx_buffer[rx_buf_pos] = tbp_byte; rx_buf_pos++; } else @@ -264,309 +252,315 @@ void loop(void) } } /* LF (\n) is the last symbol of NMEA message. */ - else if (rx_byte == LF) + else if (tbp_byte == LF) { - rx_buffer[rx_buf_pos] = rx_byte; + rx_buffer[rx_buf_pos] = tbp_byte; rx_buf_pos++; - state = COPY_TO_TX; + state = START_TX; } /* Characters 0-9 and A-F are converted to numbers and added to checksum. - Digit symbols have values 0x30-0x39. Capital letters start from 0x41. - If the received byte is a letter (val. 0x4X) we substract 0x07 - to convert the value to 0x3A-0x3F. Bitwise AND with 0x0F converts - the value to 0x00-0x0F. - Previous value of the received checksum is rotated 4 bits left. If the first - byte of the checksum was received, the value is 0x00 and isn't affected. - If the second byte is received, the value 0x0X becomes 0xX0 leaving a - place for the second digit. - */ + * Digit symbols have values 0x30-0x39. Capital letters start from 0x41. + * If the received byte is a letter (val. 0x4X) we subtract 0x07 + * to convert the value to 0x3A-0x3F. Bitwise AND with 0x0F converts + * the value to 0x00-0x0F. + * Previous value of the received checksum is rotated 4 bits left. If the first + * byte of the checksum was received, the value is 0x00 and isn't affected. + * If the second byte is received, the value 0x0X becomes 0xX0 leaving a + * place for the second digit. + */ else { - uint8_t val = rx_byte; + uint8_t val = tbp_byte; if (val & 0x40) val -= 0x07; val &= 0x0F; rx_checksum <<= 4; rx_checksum |= val; } + + tbp_byte = NO_DATA; } break; - case COPY_TO_TX: - /* COPY_TO_TX: The received and reformatted message is transferred - to TX buffer. If the buffer isn't empty, the system remains in - this state until the previous message is sent. After sending - the message to TX system moves to READY state. - */ - - /* The new message is sent to TX only if it has no message to send. */ - if (!tx_not_empty) + case START_TX: + /* START_TX: The received and reformatted message is transferred + * to TX buffer. If the buffer isn't empty, the system remains in + * this state until the previous message is sent. After sending + * the message to TX system moves to READY state. + */ + if (!tx_has_data) { + reset_tx(); copy_msg_to_tx_buf(); - tx_buf_pos = 0; - tx_not_empty = true; + tx_has_data = true; + UCSR0B |= (1 << UDRIE0); /* Enable buffer empty interrupt */ reset_rx(); + PORTD &= ALL_OFF; /* Turn all the LEDs off. */ } - PORTD &= ALL_OFF; /* Turn all the leds off. */ break; + } + /* End RX routine */ } - /* End RX routine */ } /* - Function: process_field - ----------------------- - Determines message type from the 0th field and changes the length - of last received field if required. - - returns: True if the field was valid, false if the message has to be discarded - due to current field's value. -*/ + * Function: process_field + * ----------------------- + * Determines message type from the 0th field and changes the length + * of last received field if required. + * + * returns: True if the field was valid, false if the message has to be discarded + * due to current field's value. + */ bool process_field(void) { bool res = true; switch (rx_command) { - case NONE: /* Determine command type */ - if (rx_buffer[3] == 'G' && rx_buffer[4] == 'G' && rx_buffer[5] == 'A') + case NONE: /* Determine command type */ + if (rx_buffer[3] == 'G' && rx_buffer[4] == 'G' && rx_buffer[5] == 'A') + { + rx_command = GGA; + break; + } + if (rx_buffer[3] == 'R' && rx_buffer[4] == 'M' && rx_buffer[5] == 'C') + { + rx_command = RMC; + break; + } + if (rx_buffer[3] == 'Z' && rx_buffer[4] == 'D' && rx_buffer[5] == 'A') + { + rx_command = ZDA; + break; + } + res = false; + break; + + case GGA: + /* NEO-6M vs. Yaesu VX-8 + * NEO: $GPGGA,094053.00,3204.41475,N,03445.96499,E,1,09,1.12,28.7,M,17.5,M,,*69 + * VX8: $GPGGA,095142.196,4957.5953,N,00811.9616,E,0,00,99.9,00234.7,M,0047.9,M,000.0,0000*42 + * Dif: $GPGGA,094053.00_,3204.4147X,N,03445.9649X,E,1,09,_1.1X,___28.7,M,__17.5,M,___._,____*69 + * Fields: 0 1 2 3 4 5 6 7 8 9 A B C D E + */ + switch (rx_field_num) + { + case 0x00: + /* Added for switch() optimization purposes. */ + break; + case 0x01: + /* If time field is empty all the message is discarded. */ + if (rx_field_size == 0) { - rx_command = GGA; + PORTD |= GGA_RED; /* Turn the red LED on. */ + res = false; break; } - if (rx_buffer[3] == 'R' && rx_buffer[4] == 'M' && rx_buffer[5] == 'C') + /* Time field is fixed to 10 characters: hhmmss.sss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 6, 3); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 10; + break; + case 0x02: + if (rx_field_size == 0) + PORTD |= GGA_RED; /* Red LED on. */ + else + PORTD |= GGA_GREEN; /* Green LED on. */ + /* Latitude field is fixed to 9 characters: ddmm.ssss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 4); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 9; + break; + case 0x03: + /* Latitude N/S field is set to N if empty. */ + if (rx_field_size == 0) { - rx_command = RMC; - break; + rx_buffer[rx_buf_pos] = 'N'; + rx_buf_pos++; } - if (rx_buffer[3] == 'Z' && rx_buffer[4] == 'D' && rx_buffer[5] == 'A') + break; + case 0x04: + /* Longitude field is fixed to 10 characters: dddmm.ssss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 5, 4); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 10; + break; + case 0x05: + /* Longitude E/W field is set to E if empty. */ + if (rx_field_size == 0) { - rx_command = ZDA; - break; + rx_buffer[rx_buf_pos] = 'E'; + rx_buf_pos++; } - res = false; break; - - case GGA: - /* NEO-6M vs. Yaesu VX-8 - NEO: $GPGGA,094053.00,3204.41475,N,03445.96499,E,1,09,1.12,28.7,M,17.5,M,,*69 - VX8: $GPGGA,095142.196,4957.5953,N,00811.9616,E,0,00,99.9,00234.7,M,0047.9,M,000.0,0000*42 - Dif: $GPGGA,094053.00_,3204.4147X,N,03445.9649X,E,1,09,_1.1X,___28.7,M,__17.5,M,___._,____*69 - Fields: 0 1 2 3 4 5 6 7 8 9 A B C D E - */ - switch (rx_field) + case 0x06: + /* Field 6 doesn't require modification. */ + break; + case 0x07: + /* Number of satellites is integer fixed to 2 characters. */ + fix_int_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 2); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 2; + break; + case 0x08: + /* Horizontal dilution of position field is fixed to 4 characters: xx.x. + * In the case of NEO-U-6 it means that one character after the decimal point + * will be truncated. */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 2, 1); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 4; + break; + case 0x09: + /* Altitude above mean sea is fixed to 7 characters: aaaaa.a */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 5, 1); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 7; + break; + case 0x0A: + /* Altitude units are set to M (meters) if this field is empty. */ + if (rx_field_size == 0) { - case 0x01: - /* If time field is empty all the message is discarded. */ - if (rx_field_size == 0) - { - PORTD |= GGA_RED; /* Turn the red LED on. */ - res = false; - break; - } - /* Time field is fixed to 10 characters: hhmmss.sss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 6, 3); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 10; - break; - case 0x02: - if (rx_field_size == 0) - PORTD |= GGA_RED; /* Red LED on. */ - else - PORTD |= GGA_GREEN; /* Green LED on. */ - /* Latitude field is fixed to 9 characters: ddmm.ssss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 4); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 9; - break; - case 0x03: - /* Latitude N/S field is set to N if empty. */ - if (rx_field_size == 0) - { - rx_buffer[rx_buf_pos] = 'N'; - rx_buf_pos++; - } - break; - case 0x04: - /* Longitude field is fixed to 10 characters: dddmm.ssss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 5, 4); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 10; - break; - case 0x05: - /* Longitude E/W field is set to E if empty. */ - if (rx_field_size == 0) - { - rx_buffer[rx_buf_pos] = 'E'; - rx_buf_pos++; - } - break; - case 0x06: - /* Field 6 doesn't require modification. */ - break; - case 0x07: - /* Number of satellites is integer fixed to 2 characters. */ - fix_int_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 2); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 2; - break; - case 0x08: - /* Horizontal dilution of position field is fixed to 4 characters: xx.x. - In the case of NEO-U-6 it means that one character after the decimal point - will be truncated. */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 2, 1); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 4; - break; - case 0x09: - /* Altitude above mean sea is fixed to 7 characters: aaaaa.a */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 5, 1); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 7; - break; - case 0x0A: - /* Altitude units are set to M (meters) if this field is empty. */ - if (rx_field_size == 0) - { - rx_buffer[rx_buf_pos] = 'M'; - rx_buf_pos++; - } - break; - case 0x0B: - /* Height of geoid field is fixed to 6 characters: ddd.mm */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 1); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 6; - break; - case 0x0C: - /* Altitude units are set to M (meters) if this field is empty. */ - if (rx_field_size == 0) - { - rx_buffer[rx_buf_pos] = 'M'; - rx_buf_pos++; - } - break; - case 0x0D: - /* Time since last DGPS update field is fixed to 5 characters: ddd.m */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 3, 1); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 5; - break; - case 0x0E: - /* DGPS station ID number is integer fixed to 4 characters. */ - fix_int_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 4; - break; + rx_buffer[rx_buf_pos] = 'M'; + rx_buf_pos++; + } + break; + case 0x0B: + /* Height of geoid field is fixed to 6 characters: ddd.mm */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 1); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 6; + break; + case 0x0C: + /* Altitude units are set to M (meters) if this field is empty. */ + if (rx_field_size == 0) + { + rx_buffer[rx_buf_pos] = 'M'; + rx_buf_pos++; } break; + case 0x0D: + /* Time since last DGPS update field is fixed to 5 characters: ddd.m */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 3, 1); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 5; + break; + case 0x0E: + /* DGPS station ID number is integer fixed to 4 characters. */ + fix_int_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 4; + break; + } + break; /* End case GGA */ - case RMC: - /* NEO-6M vs. Yaesu VX-8 - NEO: $GPRMC,094054.00,A,3204.41446,N,03445.96604,E,3.876,110.45,231215,,,A*62 - VX8: $GPRMC,095142.196,V,4957.5953,N,00811.9616,E,9999.99,999.99,080810,,*2C - Dif: $GPRMC,094054.00_,A,3204.4144X,N,03445.9660X,E,___3.87X,110.45,231215,,XX*62 - Fields: 0 1 2 3 4 5 6 7 8 9 - */ - switch (rx_field) + case RMC: + /* NEO-6M vs. Yaesu VX-8 + * NEO: $GPRMC,094054.00,A,3204.41446,N,03445.96604,E,3.876,110.45,231215,,,A*62 + * VX8: $GPRMC,095142.196,V,4957.5953,N,00811.9616,E,9999.99,999.99,080810,,*2C + * Dif: $GPRMC,094054.00_,A,3204.4144X,N,03445.9660X,E,___3.87X,110.45,231215,,XX*62 + * Fields: 0 1 2 3 4 5 6 7 8 9 + */ + switch (rx_field_num) + { + case 0x00: + /* Added for switch() optimization purposes. */ + break; + case 0x01: + /* If time field is empty all the message is discarded. */ + if (rx_field_size == 0) { - case 0x00: - break; - case 0x01: - /* If time field is empty all the message is discarded. */ - if (rx_field_size == 0) - { - PORTD |= RMC_RED; /* Turn the red LED on. */ - res = false; - break; - } - /* Time field is fixed to 10 characters: hhmmss.sss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 6, 3); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 10; - break; - case 0x02: - break; - case 0x03: - if (rx_field_size == 0) - PORTD |= RMC_RED; /* Red LED on. */ - else - PORTD |= RMC_GREEN; /* Green LED on. */ - /* Latitude field is fixed to 9 characters: ddmm.ssss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 4); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 9; - break; - case 0x04: - /* Latitude N/S field is set to N if empty. */ - if (rx_field_size == 0) - { - rx_buffer[rx_buf_pos] = 'N'; - rx_buf_pos++; - } - break; - case 0x05: - /* Longitude field is fixed to 10 characters: dddmm.ssss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 5, 4); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 10; - break; - case 0x06: - /* Longitude E/W field is set to E if empty. */ - if (rx_field_size == 0) - { - rx_buffer[rx_buf_pos] = 'E'; - rx_buf_pos++; - } - break; - case 0x07: - /* Speed field is fixed to 7 characters: ssss.ss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 2); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 7; - break; - case 0x08: - /* Track angle field is fixed to 6 characters: ddd.mm */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 3, 2); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 6; - break; + PORTD |= RMC_RED; /* Turn the red LED on. */ + res = false; + break; + } + /* Time field is fixed to 10 characters: hhmmss.sss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 6, 3); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 10; + break; + case 0x02: + break; + case 0x03: + if (rx_field_size == 0) + PORTD |= RMC_RED; /* Red LED on. */ + else + PORTD |= RMC_GREEN; /* Green LED on. */ + /* Latitude field is fixed to 9 characters: ddmm.ssss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 4); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 9; + break; + case 0x04: + /* Latitude N/S field is set to N if empty. */ + if (rx_field_size == 0) + { + rx_buffer[rx_buf_pos] = 'N'; + rx_buf_pos++; + } + break; + case 0x05: + /* Longitude field is fixed to 10 characters: dddmm.ssss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 5, 4); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 10; + break; + case 0x06: + /* Longitude E/W field is set to E if empty. */ + if (rx_field_size == 0) + { + rx_buffer[rx_buf_pos] = 'E'; + rx_buf_pos++; } break; + case 0x07: + /* Speed field is fixed to 7 characters: ssss.ss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 4, 2); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 7; + break; + case 0x08: + /* Track angle field is fixed to 6 characters: ddd.mm */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 3, 2); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 6; + break; + } + break; /* End case RMC */ - case ZDA: - /* NEO-6M vs. Yaesu VX-8 - NEO: $GPZDA,142615.00,26,12,2015,,*52 - VX8: $GPZDA,095143.196,08,08,2010,,*51 - Dif: $GPZDA,142615.00_,26,12,2015,,*52 - Fields: 0 1 2 3 4 - */ - if (rx_field == 1) + case ZDA: + /* NEO-6M vs. Yaesu VX-8 + * NEO: $GPZDA,142615.00,26,12,2015,,*52 + * VX8: $GPZDA,095143.196,08,08,2010,,*51 + * Dif: $GPZDA,142615.00_,26,12,2015,,*52 + * Fields: 0 1 2 3 4 + */ + if (rx_field_num == 1) + { + /* If time field is empty all the message is discarded. */ + if (rx_field_size == 0) { - /* If time field is empty all the message is discarded. */ - if (rx_field_size == 0) - { - res = false; - break; - } - /* Time field is fixed to 10 characters: hhmmss.sss */ - fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 6, 3); - rx_buf_pos -= rx_field_size; - rx_buf_pos += 10; + res = false; + break; } - break; + /* Time field is fixed to 10 characters: hhmmss.sss */ + fix_decimal_field_len(&rx_buffer[rx_buf_pos - rx_field_size], 6, 3); + rx_buf_pos -= rx_field_size; + rx_buf_pos += 10; + } + break; } return res; } /* - Function: reset_tx - ------------------ - Resets transmit buffer to ready-to-send state. - - returns: none -*/ + * Function: reset_tx + * ------------------ + * Resets transmit buffer to ready-to-send state. + * + * returns: none + */ void reset_tx(void) { for (uint8_t i = 0; i < BUFFER_SIZE; i++) @@ -574,16 +568,16 @@ void reset_tx(void) tx_buffer[i] = NO_DATA; } tx_buf_pos = 0; - tx_not_empty = false; + tx_has_data = false; } /* - Function: reset_rx - ------------------ - Resets receive buffer and sets system state to READY. - - returns: none -*/ + * Function: reset_rx + * ------------------ + * Resets receive buffer and sets system state to READY. + * + * returns: none + */ void reset_rx(void) { for (uint8_t i = 0; i < BUFFER_SIZE; i++) @@ -594,18 +588,19 @@ void reset_rx(void) calc_checksum = 0x00; rx_checksum = 0x00; rx_command = NONE; - rx_field = 0; + rx_field_num = 0; rx_field_size = 0; + tbp_byte = NO_DATA; state = READY; } /* - Function: copy_msg_to_tx_buf - ---------------------------- - Copies contents of RX buffer to TX buffer. - - returns: none -*/ + * Function: copy_msg_to_tx_buf + * ---------------------------- + * Copies contents of RX buffer to TX buffer. + * + * returns: none + */ void copy_msg_to_tx_buf(void) { for (uint8_t i = 0; i < BUFFER_SIZE; i++) @@ -616,12 +611,12 @@ void copy_msg_to_tx_buf(void) /* UART routines */ /* - Function: usart_init - -------------------- - Initializes UART, enables TX, RX and interrupts. - - returns: none -*/ + * Function: usart_init + * -------------------- + * Initializes UART, enables TX, RX and interrupts. + * + * returns: none + */ void usart_init(void) { /* Set baud rate */ @@ -629,23 +624,45 @@ void usart_init(void) UBRR0L = (uint8_t) UBRR_VALUE; /* Set frame format to 8 data bits, no parity, 1 stop bit */ UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); - /* Enable reception and RX complete interrupt */ + /* Enable reception and transmission */ + UCSR0B |= (1 << RXEN0) | (1 << TXEN0); + /* Enable RX Complete interrupt */ UCSR0B |= (1 << RXEN0) | (1 << RXCIE0); - /* Enable transmission TX comlete interrupt */ - UCSR0B |= (1 << TXEN0) | (1 << TXCIE0); } -/* RX Complete interrupt service routine */ +/* + * Function: ISR(USART_RX_vect) + * ---------------------------- + * RX Complete interrupt service routine. + * + * returns: none + */ ISR(USART_RX_vect) { rx_byte = UDR0; - rx_byte_ready = true; } -/* UART Data register empty service routine */ -ISR(USART_TX_vect) +/* + * Function: ISR(USART_UDRE_vect) + * ------------------------------ + * UART Data Register Empty service routine. Sends next byte to buffer + * if end of sentence (null terminator) isn't reached. Otherwise clears + * tx_has_data flag and disables UART Data Register Empty interrupt. + * + * returns: none + */ +ISR(USART_UDRE_vect) { - tx_rts = true; + if (tx_buffer[tx_buf_pos] != NO_DATA) + { + UDR0 = tx_buffer[tx_buf_pos]; + tx_buf_pos++; + } + else + { + UCSR0B &= ~(1 << UDRIE0); /* Disable UDR0 empty interrupt */ + tx_has_data = false; + } } // ============================================================================ diff --git a/binaries/vx8_gps.hex b/binaries/vx8_gps.hex index 4351510..fb0df4c 100644 --- a/binaries/vx8_gps.hex +++ b/binaries/vx8_gps.hex @@ -2,151 +2,150 @@ :100010000C9467000C9467000C9467000C946700C4 :100020000C9467000C9467000C9467000C946700B4 :100030000C9467000C9467000C9467000C946700A4 -:100040000C941F040C9467000C94BC000C94670083 -:100050000C94CF000C9467000C9467000C9467001C -:100060000C9467000C94670016021C022402C8025C -:1000700030020F033C024C025A0264027202640214 -:100080007C028D02AA020F03B0022402C8023002D1 -:10009000D202E30211241FBECFEFD8E0DEBFCDBFF6 -:1000A00011E0A0E0B1E0E8E5F9E002C005900D92B2 +:100040000C9418040C9467000C94BD000C94CD0023 +:100050000C9467000C9467000C9467000C94670084 +:100060000C9467000C9467002C0232023A02DE0204 +:10007000460225035202620270027A0288027A0264 +:100080009202A302C0022503C6023A02DE02460221 +:10009000E802F90211241FBECFEFD8E0DEBFCDBFCA +:1000A00011E0A0E0B1E0EAE4F9E002C005900D92B1 :1000B000A031B107D9F721E0A0E1B1E001C01D9264 -:1000C000A93DB207E1F70E9410040C94AA040C9415 -:1000D0000000E3E1F1E0119281E0ED36F807D9F795 -:1000E00010921201109211010895E2E7F1E01192CD -:1000F00081E0EC3CF807D9F71092710110926E0183 -:1001000010926D011092CE011092CD011092CC018F -:100110001092CF010895E2E7F1E0A3E1B1E081910F -:100120008D9381E0EC3CF807D1F708951092C5005B -:1001300087E68093C400E2ECF0E0808186608083F3 -:10014000E1ECF0E080818069808380818864808335 -:1001500008958AB18C6F8AB98BB183708BB90E9474 -:1001600096000E94690081E0809310010E94750052 -:100170001092CF01789408951F920F920FB60F92AC -:1001800011248F938091C60080936F0181E080934A -:1001900070018F910F900FBE0F901F9018951F92B6 -:1001A0000F920FB60F9211248F9381E0809310016C -:1001B0008F910F900FBE0F901F9018959C01F90121 -:1001C0002F5F3F4F90819111FACF9E2F981B892F5F -:1001D0000895FC01908180E09E3241F0992331F036 -:1001E0008F5FDF01A80FB11D9C91F6CF0895FC0130 -:1001F000208130E080E090E0222379F0992319F00B -:100200008F5F91E004C091E02E3209F090E03F5FF3 -:10021000DF01A30FB11D2C91EFCF0895242F261BD2 -:1002200030E050E347FD14C0E42FFF27E7FDF095D1 -:10023000DC01AE0FBF1FE217F3073CF0E21BF30B2C -:10024000E80FF91F60816C9301C05C934150EACFC5 -:100250000895CF93DF93641BA0E050E070E02A2F55 -:10026000332727FD30954217530754F0EC01C20F96 -:10027000D31FFE01E60FF71FE081E883AF5FEFCFEA -:10028000DF91CF91089550E070E3262F332727FDAB -:100290003095FC01241735072CF4E20FF31F70830F -:1002A0006F5FF3CFE40FF51F108208950F931F9334 -:1002B000CF93DF931F92CDB7DEB78C0169830E9485 -:1002C000DE00682F4981841789F0C801641738F46B -:1002D0000F90DF91CF911F910F910C940E010F9011 -:1002E000DF91CF911F910F910C9429010F90DF9115 -:1002F000CF911F910F910895CF92DF92FF920F93AC -:100300001F93CF93DF931F92CDB7DEB76C01F62E0C -:10031000042F0E94DE00682F811105C08EE2F601D5 -:100320008083118261E0C60169830E94E9006981CE -:100330008F1571F0162F1F0D181B412F8F1520F4EC -:10034000C6010E940E0103C0C6010E942901612F4F -:10035000C60169830E94F70069818017A1F0462FCA -:10036000400F481B801758F4C6010F90DF91CF91C2 -:100370001F910F91FF90DF90CF900C944301F601F5 -:10038000E40FF11D10820F90DF91CF911F910F911B -:10039000FF90DF90CF9008958091CE018130C9F118 -:1003A00038F0823009F4C9C0833009F41DC137C167 -:1003B00080917501873471F480917601873409F05A -:1003C00030C180917701813409F02BC181E08093A5 -:1003D000CE010895823561F4809176018D3409F063 -:1003E00020C180917701833409F01BC182E00EC0E7 -:1003F0008A3509F016C180917601843409F011C163 -:1004000080917701813409F00CC183E08093CE01A3 -:1004100006C18091CD0190E0FC013197EE30F105ED -:1004200008F0FDC0EC5CFF4F0C94A4042091CC01BB -:100430002111E2C05D9AF5C08091CC01811102C00A -:100440005D9A95C05F9A93C08091CC018111E7C0FD -:1004500080917101E82FF0E0EE58FE4F9EE43FC01E -:100460008091CC018111DBC080917101E82FF0E017 -:10047000EE58FE4F95E433C08091710190E02091D9 -:10048000CC01821B910962E08E589E4F0E9456015A -:10049000809171018E5FBEC08091710190E02091CA -:1004A000CC01821B910941E062E08E589E4F0E9470 -:1004B0007C013FC08091710190E02091CC01821BB2 -:1004C000910941E065E077C08091CC018111A7C01E -:1004D00080917101E82FF0E0EE58FE4F9DE490838B -:1004E0008F5F9BC08091710190E02091CC01821BB5 -:1004F000910941E064E070C08091710190E0209129 -:10050000CC01821B910941E063E08E589E4F0E940E -:100510007C01809171018B5F7DC08091710190E0C1 -:100520002091CC01821B910964E08E589E4F0E945D -:100530005601809171018C5F6DC08091CD0190E07A -:10054000FC013197E830F10508F069C0EE5BFF4F20 -:100550000C94A4042091CC0121114EC05A9A61C080 -:100560008091CC01811102C05A9A01C05C9A80919D -:10057000710190E02091CC01821B910944E064E07C -:100580008E589E4F0E947C0180917101875F42C00E -:100590008091710190E02091CC01821B910944E08F -:1005A00065E031C08091710190E02091CC01821B07 -:1005B000910942E064E08E589E4F0E947C01809138 -:1005C0007101895F27C08091710190E02091CC0179 -:1005D000821B910942E063E08E589E4F0E947C018D -:1005E000809171018A5F16C08091CD018130B9F48C -:1005F0002091CC012223A9F08091710190E0821B0F -:10060000910943E066E08E589E4F0E947C018091E4 -:100610007101865F9091CC01891B8093710181E00B -:10062000089580E00895CF93DF938091100188238F -:10063000D1F0809111018823B1F0E0911201EA35E7 -:1006400080F4F0E0ED5EFE4F8081882351F08093CE -:10065000C600809112018F5F8093120110921001E9 -:1006600002C00E9469008091CF01813001F138F011 -:10067000823009F45AC0833009F4BEC0CDC08091E5 -:100680007001882309F4C8C01092700190916F0125 -:10069000943209F0C1C080917101E82FF0E0EE586A -:1006A000FE4F90838F5F8093710181E097C08091AE -:1006B0007001882309F4B0C01092700180916F011D -:1006C000843221F0909171019A3520F0DF91CF9121 -:1006D0000C9475008C3211F08A3261F40E94CC01C6 -:1006E0008823A1F38091CD018F5F8093CD0110927B -:1006F000CC0105C08091CC018F5F8093CC018091AB -:10070000710190916F01E82FF0E0EE58FE4F908359 -:100710008F5F809371019A3211F482E05FC0809103 -:100720006E01892780936E0177C0809170018823C4 -:1007300009F472C01092700180916F01843231F21D -:100740008A3221F2609171016A3508F0BFCF8D3095 -:10075000D1F590916D0180916E019813B7CF71E042 -:10076000A0E0862F90E0AC0141505109272F30E0E6 -:10077000241735073CF4F901EE58FE4F2081A227DB -:100780007F5FF4CFEC01CE58DE4FEA2FE295EF7099 -:10079000F0E0E050FF4F20812883E1E0E60FF0E039 -:1007A000EE58FE4FAF70B0E0A050BF4F8C918083E9 -:1007B000E2E0E60FF0E0EE58FE4F8DE080836D5FE3 -:1007C0006093710129C08A3061F4E62FF0E0EE58A1 -:1007D000FE4F80836F5F6093710183E08093CF0150 -:1007E0001BC086FD87508F7090916D019295907F10 -:1007F000892B80936D0110C080911101811109C076 -:100800000E948B001092120181E0809311010E94DE -:1008100075008BB183708BB9DF91CF9108950895E6 -:100820000E9469040E940F040E94A900C0E0D0E069 -:100830000E9413032097E1F30E940000F9CF1F925A -:100840000F920FB60F9211242F933F938F939F9384 -:10085000AF93BF938091D1019091D201A091D30128 -:10086000B091D4013091D00123E0230F2D3720F433 -:100870000196A11DB11D05C026E8230F0296A11DFA -:10088000B11D2093D0018093D1019093D201A09308 -:10089000D301B093D4018091D5019091D601A0915C -:1008A000D701B091D8010196A11DB11D8093D5014A -:1008B0009093D601A093D701B093D801BF91AF9187 -:1008C0009F918F913F912F910F900FBE0F901F908E -:1008D0001895789484B5826084BD84B5816084BDA8 -:1008E00085B5826085BD85B5816085BDEEE6F0E0A9 -:1008F000808181608083E1E8F0E010828081826005 -:100900008083808181608083E0E8F0E08081816085 -:100910008083E1EBF0E0808184608083E0EBF0E0B5 -:10092000808181608083EAE7F0E080818460808359 -:100930008081826080838081816080838081806803 -:1009400080831092C1000895EE0FFF1F0590F4916F -:08095000E02D0994F894FFCF9B -:1009580030313233343536373839414243444546ED +:1000C000A83DB207E1F70E9409040C94A3040C9424 +:1000D0000000E2E1F1E0119281E0EC36F807D9F797 +:1000E00010921101109210010895E1E7F1E01192D0 +:1000F00081E0EB3CF807D9F71092700110926D0186 +:1001000010926C011092CD011092CC011092CB0193 +:1001100010926E011092CE010895E1E7F1E0A2E1A4 +:10012000B1E081918D9381E0EB3CF807D1F7089520 +:100130001092C50087E68093C400E2ECF0E0808175 +:1001400086608083E1ECF0E080818861808380813B +:100150008069808308958AB18C6F8AB98BB183706E +:100160008BB90E9498000E9469000E94750010924D +:100170006F011092CE01789408951F920F920FB6DE +:100180000F9211248F938091C60080936F018F91FD +:100190000F900FBE0F901F9018951F920F920FB6E1 +:1001A0000F9211248F93EF93FF93E0911101F0E0F0 +:1001B000EE5EFE4F8081882341F08093C6008091DF +:1001C00011018F5F8093110107C08091C1008F7D65 +:1001D0008093C10010921001FF91EF918F910F90C9 +:1001E0000FBE0F901F9018959C01F9012F5F3F4F94 +:1001F00090819111FACF9E2F981B892F0895FC01B1 +:10020000908180E09E3241F0992331F08F5FDF01D1 +:10021000A80FB11D9C91F6CF0895FC01208130E01C +:1002200080E090E0222379F0992319F08F5F91E02C +:1002300004C091E02E3209F090E03F5FDF01A30F90 +:10024000B11D2C91EFCF0895242F261B30E050E3F1 +:1002500047FD14C0E42FFF27E7FDF095DC01AE0F4A +:10026000BF1FE217F3073CF0E21BF30BE80FF91F87 +:1002700060816C9301C05C934150EACF0895CF93A5 +:10028000DF93641BA0E050E070E02A2F332727FDA6 +:1002900030954217530754F0EC01C20FD31FFE01F3 +:1002A000E60FF71FE081E883AF5FEFCFDF91CF91DB +:1002B000089550E070E3262F332727FD3095FC0189 +:1002C000241735072CF4E20FF31F70836F5FF3CF11 +:1002D000E40FF51F108208950F931F93CF93DF93C0 +:1002E0001F92CDB7DEB78C0169830E94F400682F9E +:1002F0004981841789F0C801641738F40F90DF91A1 +:10030000CF911F910F910C9424010F90DF91CF9109 +:100310001F910F910C943F010F90DF91CF911F918E +:100320000F910895CF92DF92FF920F931F93CF9377 +:10033000DF931F92CDB7DEB76C01F62E042F0E941B +:10034000F400682F811105C08EE2F60180831182CE +:1003500061E0C60169830E94FF0069818F1571F019 +:10036000162F1F0D181B412F8F1520F4C6010E9458 +:10037000240103C0C6010E943F01612FC6016983A9 +:100380000E940D0169818017A1F0462F400F481B84 +:10039000801758F4C6010F90DF91CF911F910F91F4 +:1003A000FF90DF90CF900C945901F601E40FF11DFE +:1003B00010820F90DF91CF911F910F91FF90DF90EE +:1003C000CF9008958091CD018130C9F138F082300D +:1003D00009F4C9C0833009F41DC137C1809174018B +:1003E000873471F480917501873409F030C18091B0 +:1003F0007601813409F02BC181E08093CD0108950D +:10040000823561F4809175018D3409F020C18091AD +:100410007601833409F01BC182E00EC08A3509F0F1 +:1004200016C180917501843409F011C18091760163 +:10043000813409F00CC183E08093CD0106C1809125 +:10044000CC0190E0FC013197EE30F10508F0FDC0E1 +:10045000EC5CFF4F0C949D042091CB012111E2C074 +:100460005D9AF5C08091CB01811102C05D9A95C063 +:100470005F9A93C08091CB018111E7C08091700198 +:10048000E82FF0E0EF58FE4F9EE43FC08091CB0193 +:100490008111DBC080917001E82FF0E0EF58FE4F32 +:1004A00095E433C08091700190E02091CB01821BD4 +:1004B000910962E08F589E4F0E946C0180917001FB +:1004C0008E5FBEC08091700190E02091CB01821BB5 +:1004D000910941E062E08F589E4F0E9492013FC017 +:1004E0008091700190E02091CB01821B910941E045 +:1004F00065E077C08091CB018111A7C08091700128 +:10050000E82FF0E0EF58FE4F9DE490838F5F9BC093 +:100510008091700190E02091CB01821B910941E014 +:1005200064E070C08091700190E02091CB01821B4B +:10053000910941E063E08F589E4F0E9492018091A3 +:1005400070018B5F7DC08091700190E02091CB01A4 +:10055000821B910964E08F589E4F0E946C0180912C +:1005600070018C5F6DC08091CC0190E0FC013197EF +:10057000E830F10508F069C0EE5BFF4F0C949D0474 +:100580002091CB0121114EC05A9A61C08091CB01BC +:10059000811102C05A9A01C05C9A8091700190E06A +:1005A0002091CB01821B910944E064E08F589E4F5B +:1005B0000E94920180917001875F42C0809170011A +:1005C00090E02091CB01821B910944E065E031C0AD +:1005D0008091700190E02091CB01821B910942E053 +:1005E00064E08F589E4F0E94920180917001895F54 +:1005F00027C08091700190E02091CB01821B91096E +:1006000042E063E08F589E4F0E94920180917001FA +:100610008A5F16C08091CC018130B9F42091CB0162 +:100620002223A9F08091700190E0821B910943E0A0 +:1006300066E08F589E4F0E94920180917001865F04 +:100640009091CB01891B8093700181E0089580E037 +:100650000895D2E0C1E013E00DE080916F0188239E +:1006600031F080916F0180936E0110926F01809143 +:10067000CE018130D1F038F0823009F44FC08330A0 +:1006800009F4B1C0EACF90916E01943209F0A8C08C +:1006900080917001E82FF0E0EF58FE4F90838F5F5C +:1006A00080937001C093CE019BC080916E0188231E +:1006B000A1F2843221F0909170019A3518F00E94D5 +:1006C0007500CBCF8C3211F08A3261F40E94E201C6 +:1006D0008823A9F38091CC018F5F8093CC01109285 +:1006E000CB0105C08091CB018F5F8093CB018091BE +:1006F000700190916E01E82FF0E0EF58FE4F90836B +:100700008F5F809370019A3219F4D093CE0168C044 +:1007100080916D01892780936D0162C080916E0187 +:10072000882309F49ACF843251F28A3241F26091DF +:1007300070016A3520F68D30E1F590916C01809161 +:100740006D01981333C071E0A0E0862F90E0AC01FA +:1007500041505109272F30E0241735073CF4F901A7 +:10076000EF58FE4F2081A2277F5FF4CF8F589E4F16 +:10077000EA2FE295EF70F0E0E050FF4F2081FC019E +:100780002083E1E0E60FF0E0EF58FE4FAF70B0E0FD +:10079000A050BF4F8C918083E2E0E60FF0E0EF586D +:1007A000FE4F00836D5F609370011AC00E94750058 +:1007B00017C08A3059F4E62FF0E0EF58FE4F8083DF +:1007C0006F5F609370011093CE010AC086FD875061 +:1007D0008F7090916C019295907F892B80936C0122 +:1007E00010926E013ACF80911001811136CF0E9494 +:1007F00069000E948D00C09310018091C100806249 +:100800008093C1000E9475008BB183708BB925CF96 +:1008100008950E9462040E9408040E94AB00C0E098 +:10082000D0E00E9429032097E1F30E940000F9CF55 +:100830001F920F920FB60F9211242F933F938F9315 +:100840009F93AF93BF938091D0019091D101A091DC +:10085000D201B091D3013091CF0123E0230F2D3786 +:1008600020F40196A11DB11D05C026E8230F0296B4 +:10087000A11DB11D2093CF018093D0019093D10190 +:10088000A093D201B093D3018091D4019091D5016E +:10089000A091D601B091D7010196A11DB11D809301 +:1008A000D4019093D501A093D601B093D701BF9105 +:1008B000AF919F918F913F912F910F900FBE0F900D +:1008C0001F901895789484B5826084BD84B581604A +:1008D00084BD85B5826085BD85B5816085BDEEE648 +:1008E000F0E0808181608083E1E8F0E01082808127 +:1008F00082608083808181608083E0E8F0E0808195 +:1009000081608083E1EBF0E0808184608083E0EBB4 +:10091000F0E0808181608083EAE7F0E0808184609C +:1009200080838081826080838081816080838081F8 +:10093000806880831092C1000895EE0FFF1F05901C +:0A094000F491E02D0994F894FFCF24 +:10094A0030313233343536373839414243444546FB :00000001FF diff --git a/src/main.c b/src/main.c index 3030e8c..a743e48 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,17 @@ /* * vx8_gps.c * - * Created on: 03 gen 2016 - * Author: 4Z7DTF + * Created on: 03 Jan 2016 + * Author: Dmitry Melnichansky 4Z7DTF + * Repository: https://github.com/4z7dtf/vx8_gps + * + * 2016-01-03 The program created. + * 2016-03-28 Stable version which uses TX Complete interrupt. Tested with + * Arduino Nano running at 16MHz and with a stand-alone ATmega328P + * running at 2MHz. + * 2016-04-01 USART Buffer Empty interrupt used for TX. TX routines removed + * completely from the main loop. Tested with Arduino Nano at 16MHz + * and with a stand-alone ATmega328P running at 2MHz. */ /* @@ -52,12 +61,12 @@ void usart_init(void); #define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) /* Led output pin deifinitions. - * All the leds are connectd to PORTD as follows: - * pin 7: green - GGA sentence valid - * pin 5: red - GGA sentence invalid - * pin 4: green - RMC sentence valid - * pin 2: red - RMC sentence invalid - */ + All the leds are connectd to PORTD as follows: + pin 7: green - GGA sentence valid + pin 5: red - GGA sentence invalid + pin 4: green - RMC sentence valid + pin 2: red - RMC sentence invalid +*/ #define GGA_GREEN 0B10000000 #define GGA_RED 0B00100000 #define RMC_GREEN 0B00010000 @@ -83,7 +92,7 @@ enum rx_states READY = 0x00, /* Default state, ready to receive. Changes if $ is received. */ RX_MESSAGE = 0x01, /* Receiving the message between the $ and * delimiters. */ RX_CHECKSUM = 0x02, /* Receiving the checksum. Changes if \r\n is received. */ - COPY_TO_TX = 0x03, /* Default state, ready to receive. Changes if $ is received. */ + START_TX = 0x03, /* Default state, ready to receive. Changes if $ is received. */ }; uint8_t state; /* Current system state. */ @@ -92,24 +101,23 @@ enum nmea_commands NONE, GGA, RMC, ZDA }; uint8_t rx_command; /* NMEA command being received. */ -uint8_t rx_field; /* Current field of NMEA command. */ +uint8_t rx_field_num; /* Current field of NMEA command. */ uint8_t rx_field_size; /* Current field size. */ char rx_buffer[BUFFER_SIZE]; /* Buffer for the received message. */ uint8_t rx_buf_pos; -bool rx_byte_ready; /* Received byte ready flag. */ -uint8_t rx_byte; /* Received byte. */ +volatile uint8_t rx_byte; /* Received byte. */ +uint8_t tbp_byte; /* Byte to process. */ uint8_t calc_checksum; /* Calculated checksum of the received message. Calculated on the fly. */ uint8_t rx_checksum; /* Checksum of the received NMEA sentence. */ -/* A lookup table for converting numerical values to hexadecimal digits. */ +/* Lookup table for converting numerical values to hexadecimal digits. */ char hex_chars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /* TX variables */ char tx_buffer[BUFFER_SIZE]; /* Buffer for the message to be sent. */ uint8_t tx_buf_pos; -bool tx_not_empty; /* TX has a message to send. Set to false when tx_buffer is empty. */ -bool tx_rts; /* TX ready to send. Set to true when RX is ready to send next byte. */ +volatile bool tx_has_data; /* TX has a message to send. Set to false when tx_buffer is empty. */ int main(void) { @@ -118,48 +126,34 @@ int main(void) PORTD &= ALL_OFF; usart_init(); reset_tx(); - tx_rts = true; reset_rx(); + rx_byte = NO_DATA; state = READY; sei(); /* Main loop */ while (1) { - /* Next byte is sent if there is one and TX is ready to send. - * If null terminator or end of buffer is reached, TX is reset. - */ - if (tx_rts && tx_not_empty) + /* RX routine */ + if(rx_byte) { - if (tx_buf_pos < BUFFER_SIZE && tx_buffer[tx_buf_pos] != NO_DATA) - { - UDR0 = tx_buffer[tx_buf_pos]; - tx_buf_pos++; - tx_rts = false; - } - else - { - reset_tx(); - } + tbp_byte = rx_byte; + rx_byte = NO_DATA; } - /* RX routine */ switch (state) { case READY: /* READY: The system is ready to receive and remains is this state * until $ character is received. */ - if (rx_byte_ready) + if (tbp_byte == DOLLAR) { - rx_byte_ready = false; - if (rx_byte == DOLLAR) - { - rx_buffer[rx_buf_pos] = rx_byte; - rx_buf_pos++; - state = RX_MESSAGE; - } + rx_buffer[rx_buf_pos] = tbp_byte; + rx_buf_pos++; + state = RX_MESSAGE; } + tbp_byte = NO_DATA; break; case RX_MESSAGE: /* RX_MESSAGE: The system receives the message between $ and * @@ -176,14 +170,12 @@ int main(void) * or is lost. * When * character is received the state changes to RX_CHECKSUM. */ - if (rx_byte_ready) + if (tbp_byte) { - rx_byte_ready = false; - /* If received character is $ or the buffer is overflown, * reset and return to READY state. */ - if (rx_byte == DOLLAR || rx_buf_pos >= BUFFER_SIZE) + if (tbp_byte == DOLLAR || rx_buf_pos >= BUFFER_SIZE) { reset_rx(); break; @@ -192,7 +184,7 @@ int main(void) /* Comma and marks end of field, asterisk marks end of message * which is also end of the last field. */ - if (rx_byte == COMMA || rx_byte == ASTERISK) + if (tbp_byte == COMMA || tbp_byte == ASTERISK) { bool field_valid = process_field(); if (!field_valid) @@ -200,7 +192,7 @@ int main(void) reset_rx(); break; } - rx_field++; + rx_field_num++; rx_field_size = 0; } else @@ -208,20 +200,22 @@ int main(void) rx_field_size++; } - rx_buffer[rx_buf_pos] = rx_byte; + rx_buffer[rx_buf_pos] = tbp_byte; rx_buf_pos++; /* If end of message, change state to RX_CHECKSUM * without affecting the calculated checksum. */ - if (rx_byte == ASTERISK) + if (tbp_byte == ASTERISK) { state = RX_CHECKSUM; } else { - calc_checksum ^= rx_byte; + calc_checksum ^= tbp_byte; } + + tbp_byte = NO_DATA; } break; case RX_CHECKSUM: @@ -230,23 +224,20 @@ int main(void) * compares it to the calculated value. If two values match, * a new checksum is calculated and added to the message. * Upon receiving the LF (\n) character which marks end of sentence - * system state changes to COPY_TO_TX. + * system state changes to START_TX. */ - if (rx_byte_ready) + if (tbp_byte) { - rx_byte_ready = false; - /* If received character is $ or * or the buffer is overflown, * reset and return to READY state. */ - if (rx_byte == DOLLAR || rx_byte == ASTERISK - || rx_buf_pos >= BUFFER_SIZE) + if (tbp_byte == DOLLAR || tbp_byte == ASTERISK || rx_buf_pos >= BUFFER_SIZE) { reset_rx(); break; } /* CR (\r) is received after the last character of checksum. */ - else if (rx_byte == CR) + else if (tbp_byte == CR) { /* If match, calculate a new one, else reset. */ if (rx_checksum == calc_checksum) @@ -256,12 +247,11 @@ int main(void) { checksum ^= rx_buffer[i]; } - rx_buffer[rx_buf_pos] = - hex_chars[(checksum & 0xF0) >> 4]; + rx_buffer[rx_buf_pos] = hex_chars[(checksum & 0xF0) >> 4]; rx_buf_pos++; rx_buffer[rx_buf_pos] = hex_chars[checksum & 0x0F]; rx_buf_pos++; - rx_buffer[rx_buf_pos] = rx_byte; + rx_buffer[rx_buf_pos] = tbp_byte; rx_buf_pos++; } else @@ -270,15 +260,15 @@ int main(void) } } /* LF (\n) is the last symbol of NMEA message. */ - else if (rx_byte == LF) + else if (tbp_byte == LF) { - rx_buffer[rx_buf_pos] = rx_byte; + rx_buffer[rx_buf_pos] = tbp_byte; rx_buf_pos++; - state = COPY_TO_TX; + state = START_TX; } /* Characters 0-9 and A-F are converted to numbers and added to checksum. * Digit symbols have values 0x30-0x39. Capital letters start from 0x41. - * If the received byte is a letter (val. 0x4X) we substract 0x07 + * If the received byte is a letter (val. 0x4X) we subtract 0x07 * to convert the value to 0x3A-0x3F. Bitwise AND with 0x0F converts * the value to 0x00-0x0F. * Previous value of the received checksum is rotated 4 bits left. If the first @@ -288,31 +278,32 @@ int main(void) */ else { - uint8_t val = rx_byte; + uint8_t val = tbp_byte; if (val & 0x40) val -= 0x07; val &= 0x0F; rx_checksum <<= 4; rx_checksum |= val; } + + tbp_byte = NO_DATA; } break; - case COPY_TO_TX: - /* COPY_TO_TX: The received and reformatted message is transferred + case START_TX: + /* START_TX: The received and reformatted message is transferred * to TX buffer. If the buffer isn't empty, the system remains in * this state until the previous message is sent. After sending * the message to TX system moves to READY state. */ - - /* The new message is sent to TX only if it has no message to send. */ - if (!tx_not_empty) + if (!tx_has_data) { + reset_tx(); copy_msg_to_tx_buf(); - tx_buf_pos = 0; - tx_not_empty = true; + tx_has_data = true; + UCSR0B |= (1 << UDRIE0); /* Enable buffer empty interrupt */ reset_rx(); + PORTD &= ALL_OFF; /* Turn all the LEDs off. */ } - PORTD &= ALL_OFF; /* Turn all the leds off. */ break; } /* End RX routine */ @@ -362,8 +353,11 @@ bool process_field(void) * Dif: $GPGGA,094053.00_,3204.4147X,N,03445.9649X,E,1,09,_1.1X,___28.7,M,__17.5,M,___._,____*69 * Fields: 0 1 2 3 4 5 6 7 8 9 A B C D E */ - switch (rx_field) + switch (rx_field_num) { + case 0x00: + /* Added for switch() optimization purposes. */ + break; case 0x01: /* If time field is empty all the message is discarded. */ if (rx_field_size == 0) @@ -477,9 +471,10 @@ bool process_field(void) * Dif: $GPRMC,094054.00_,A,3204.4144X,N,03445.9660X,E,___3.87X,110.45,231215,,XX*62 * Fields: 0 1 2 3 4 5 6 7 8 9 */ - switch (rx_field) + switch (rx_field_num) { case 0x00: + /* Added for switch() optimization purposes. */ break; case 0x01: /* If time field is empty all the message is discarded. */ @@ -551,7 +546,7 @@ bool process_field(void) * Dif: $GPZDA,142615.00_,26,12,2015,,*52 * Fields: 0 1 2 3 4 */ - if (rx_field == 1) + if (rx_field_num == 1) { /* If time field is empty all the message is discarded. */ if (rx_field_size == 0) @@ -583,7 +578,7 @@ void reset_tx(void) tx_buffer[i] = NO_DATA; } tx_buf_pos = 0; - tx_not_empty = false; + tx_has_data = false; } /* @@ -603,8 +598,9 @@ void reset_rx(void) calc_checksum = 0x00; rx_checksum = 0x00; rx_command = NONE; - rx_field = 0; + rx_field_num = 0; rx_field_size = 0; + tbp_byte = NO_DATA; state = READY; } @@ -638,21 +634,44 @@ void usart_init(void) UBRR0L = (uint8_t) UBRR_VALUE; /* Set frame format to 8 data bits, no parity, 1 stop bit */ UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); - /* Enable reception and RC complete interrupt */ + /* Enable reception and transmission */ + UCSR0B |= (1 << RXEN0) | (1 << TXEN0); + /* Enable RX Complete interrupt */ UCSR0B |= (1 << RXEN0) | (1 << RXCIE0); - /* Enable transmission and UDR0 empty interrupt */ - UCSR0B |= (1 << TXEN0) | (1 << UDRIE0); } -/* RX Complete interrupt service routine */ + +/* + * Function: ISR(USART_RX_vect) + * ---------------------------- + * RX Complete interrupt service routine. + * + * returns: none + */ ISR(USART_RX_vect) { rx_byte = UDR0; - rx_byte_ready = true; } -/* UART Data register empty service routine */ +/* + * Function: ISR(USART_UDRE_vect) + * ------------------------------ + * UART Data Register Empty service routine. Sends next byte to buffer + * if end of sentence (null terminator) isn't reached. Otherwise clears + * tx_has_data flag and disables UART Data Register Empty interrupt. + * + * returns: none + */ ISR(USART_UDRE_vect) { - tx_rts = true; + if (tx_buffer[tx_buf_pos] != NO_DATA) + { + UDR0 = tx_buffer[tx_buf_pos]; + tx_buf_pos++; + } + else + { + UCSR0B &= ~(1 << UDRIE0); /* Disable UDR0 empty interrupt */ + tx_has_data = false; + } }