Skip to content
Snippets Groups Projects
Commit 66f28133 authored by da1l6's avatar da1l6
Browse files

common/transport: Make receive interrupt driven to allow receive while the main loop is busy.

parent 4631e16a
No related branches found
No related tags found
No related merge requests found
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#define USART_NAME USART #define USART_NAME USART
#undef USART_USE_AS_STDOUT #undef USART_USE_AS_STDOUT
#define BAUD 2400 #define BAUD 9600
// #define USART_USE_AS_STDOUT //Debugging only! // #define USART_USE_AS_STDOUT //Debugging only!
void USART_recv_interrupt(void); void USART_recv_interrupt(void);
#define USART_NUMBER TRANSPORT_USART_NUMBER #define USART_NUMBER TRANSPORT_USART_NUMBER
...@@ -26,18 +26,82 @@ void USART_recv_interrupt(void); ...@@ -26,18 +26,82 @@ void USART_recv_interrupt(void);
interrupt_msg_handler_t interrupt_msg_table[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; interrupt_msg_handler_t interrupt_msg_table[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
volatile uint8_t msg_start_received = 0; #define RECV_IDLE 0x00
#define RECV_START 0x01
uint16_t transport_receive_char(uint16_t timeout); #define RECV_BODY 0x02
#define RECV_CHECKSUM 0x03
#define RECV_FINISHED 0x04
#define RECV_ERROR 0x0A
#define RECV_ESCAPE_FLAG 0x40
#define RECV_INTERRUPT_FLAG 0x80
#define RECV_SET_STATE(s) {msg_recv_state = (msg_recv_state & (RECV_ESCAPE_FLAG | RECV_INTERRUPT_FLAG)) | s;}
#define RECV_STATE() (msg_recv_state & ~(RECV_ESCAPE_FLAG | RECV_INTERRUPT_FLAG))
volatile uint8_t msg_recv_state = RECV_IDLE;
volatile uint8_t msg_recv_pos = 0;
volatile uint8_t msg_recv_body_length = 0;
volatile uint16_t msg_recv_checksum = 0;
#define RECV_MAX_LENGTH 255
volatile uint8_t msg_recv_buffer[RECV_MAX_LENGTH];
uint8_t transport_unescape(uint8_t c);
void USART_recv_interrupt(void){ void USART_recv_interrupt(void){
uint16_t ret = transport_receive_char(400); uint8_t c = USART_read_char();
// printf("RXI %04x\n", ret); if (msg_recv_state & RECV_INTERRUPT_FLAG){
// lcd_write_char('R'); if (c != INTERRUPT_NONE && interrupt_msg_table[INTERRUPT_NUM(c)]){
if (ret == RET_MSG_START){ interrupt_msg_table[INTERRUPT_NUM(c)]();
msg_start_received = 1; }
// printf("Start\n"); msg_recv_state &= ~RECV_INTERRUPT_FLAG;
USART_disable_rx_interrupt(); } else if (c == SC_ESCAPE){
msg_recv_state |= RECV_ESCAPE_FLAG;
} else if (c == SC_INTERRUPT){
msg_recv_state |= RECV_INTERRUPT_FLAG;
} else if (c == SC_MSG_START){
RECV_SET_STATE(RECV_START);
msg_recv_pos = 0;
} else if (c == SC_MSG_END){
msg_recv_body_length = msg_recv_pos;
RECV_SET_STATE(RECV_CHECKSUM);
msg_recv_pos = 0;
} else {
if (msg_recv_state & RECV_ESCAPE_FLAG){
c = transport_unescape(c);
msg_recv_state &= ~RECV_ESCAPE_FLAG;
}
if (RECV_STATE() == RECV_START){
msg_recv_pos = 0;
RECV_SET_STATE(RECV_BODY);
}
if (RECV_STATE() == RECV_BODY){
if (msg_recv_pos < RECV_MAX_LENGTH){
msg_recv_buffer[msg_recv_pos] = c;
msg_recv_pos++;
}
} else if (RECV_STATE() == RECV_CHECKSUM){
if (msg_recv_pos == 0) msg_recv_checksum = c;
if (msg_recv_pos == 1) {
msg_recv_checksum |= c << 8;
RECV_SET_STATE(RECV_FINISHED);
}
msg_recv_pos++;
}
}
}
uint8_t transport_unescape(uint8_t c){
if (c == EC_2B){
return 0x2B;
} else if (c == EC_D4){
return 0xD4;
} else if (c == EC_5C){
return 0x5C;
} else if (c == EC_AA){
return 0xAA;
} else {
return c;
} }
} }
...@@ -89,12 +153,15 @@ void transport_send_message(uint8_t* body, uint8_t length){ ...@@ -89,12 +153,15 @@ void transport_send_message(uint8_t* body, uint8_t length){
for (uint8_t i = 0; i < length; i++){ for (uint8_t i = 0; i < length; i++){
transport_send_char(body[i]); transport_send_char(body[i]);
// printf(">%02x>", body[i]);
checksum = _crc16_update(checksum, body[i]); checksum = _crc16_update(checksum, body[i]);
} }
USART_transmit_char(SC_MSG_END); USART_transmit_char(SC_MSG_END);
transport_send_char(checksum & 0xFF); transport_send_char(checksum & 0xFF);
transport_send_char(checksum >> 8); transport_send_char(checksum >> 8);
USART_flush();
} }
void transport_send_interrupt_message(uint8_t interrupt){ void transport_send_interrupt_message(uint8_t interrupt){
...@@ -107,177 +174,31 @@ void transport_send_interrupt_message(uint8_t interrupt){ ...@@ -107,177 +174,31 @@ void transport_send_interrupt_message(uint8_t interrupt){
} }
uint8_t transport_data_available(void){ uint8_t transport_data_available(void){
return msg_start_received; return msg_recv_state == RECV_FINISHED;
}
uint16_t transport_handle_interrupt_msg(uint16_t timeout){
uint8_t num_interrupts = 1;
uint16_t ret = SC_INTERRUPT;
// while (ret == SC_INTERRUPT){
// ret = USART_read_char_timeout(timeout);
// if (ret == ERR_TIMEOUT) return ERR_TIMEOUT;
// if (ret == SC_INTERRUPT){
// if (num_interrupts < 255){
// num_interrupts++;
// } else { //more than 255 nested interrupt msgs?! somebody is fucking with us.
// return TRANSPORT_ERR_NESTED_INTERRUPTS;
// }
// }
// }
//
// printf("Int");
for (uint8_t i = 0; i < num_interrupts; i++){
ret = USART_read_char_timeout(timeout);
if (ret == ERR_TIMEOUT) return ERR_TIMEOUT;
if (ret != INTERRUPT_NONE && interrupt_msg_table[INTERRUPT_NUM(ret)]){
interrupt_msg_table[INTERRUPT_NUM(ret)]();
}
}
return 0;
} }
uint16_t transport_receive_char(uint16_t timeout){ uint16_t transport_receive_message(uint8_t* buffer, uint8_t length, uint16_t timeout){
uint16_t ret;
uint8_t c;
while (1){ while (RECV_STATE() != RECV_FINISHED && timeout > 0){
ret = USART_read_char_timeout(timeout); timeout--;
if (ret == ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT; _delay_us(100);
c = ret & 0xFF;
if (c == SC_ESCAPE){
while(1){
ret = USART_read_char_timeout(timeout);
if (ret == ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
c = ret & 0xFF;
if (c == SC_INTERRUPT) {
ret = transport_handle_interrupt_msg(timeout);
if (ret == ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
if (ret == TRANSPORT_ERR_NESTED_INTERRUPTS) return TRANSPORT_ERR_NESTED_INTERRUPTS;
//loop again, to read the next char (the one that was about to be escaped, when this interrupt came in)
} else {
break;
}
}
if (c == EC_2B){
return 0x2B;
} else if (c == EC_D4){
return 0xD4;
} else if (c == EC_5C){
return 0x5C;
} else if (c == EC_AA){
return 0xAA;
} else {
//keep unknown escape char unchanged //TODO: Handle this properly? Error?
return c;
}
} else if (c == SC_MSG_END){
return RET_MSG_END;
} else if (c == SC_MSG_START){
return RET_MSG_START;
} else if (c == SC_INTERRUPT){
ret = transport_handle_interrupt_msg(timeout);
if (ret == ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
if (ret == TRANSPORT_ERR_NESTED_INTERRUPTS) return TRANSPORT_ERR_NESTED_INTERRUPTS;
//we have nothing to return, so loop again to read the next char.
} else { //literal char
return c;
}
} }
}
uint16_t _transport_receive_message(uint8_t* buffer, uint8_t length, uint16_t timeout){
uint16_t ret = 0;
uint8_t c = 0;
uint8_t recv_length = 0;
uint16_t real_checksum = 0xFFFF;
uint16_t expected_checksum;
uint8_t has_body = 1;
//Wait for message start char (if the interrupt didn't pick it up for us already) if (RECV_STATE() != RECV_FINISHED){
if (!msg_start_received){ return TRANSPORT_ERR_TIMEOUT;
while (1){ } else if (msg_recv_body_length > length) {
ret = transport_receive_char(timeout); RECV_SET_STATE(RECV_IDLE);
if (ret == TRANSPORT_ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT; return TRANSPORT_ERR_MESSAGE_TOO_LONG;
if (ret == RET_MSG_START) break;
}
} else { } else {
msg_start_received = 0; uint16_t checksum = 0xFFFF;
} for (uint8_t i = 0; i < msg_recv_body_length; i++){
checksum = _crc16_update(checksum, msg_recv_buffer[i]);
//The start char can be repeated multiple times, wait for something else buffer[i] = msg_recv_buffer[i];
while (1){
ret = transport_receive_char(timeout);
if (ret == TRANSPORT_ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
if (ret == TRANSPORT_ERR_NESTED_INTERRUPTS) return TRANSPORT_ERR_NESTED_INTERRUPTS;
if (ret == RET_MSG_END) {has_body = 0; break;}; //end after start -> empty message
if (ret != RET_MSG_START) break;
}
if (has_body){
c = ret & 0xFF;
//Receive the message body
while (1){
if (recv_length < length){ //write byte to the buffer (if not full)
buffer[recv_length] = c;
recv_length++;
} else {
return TRANSPORT_ERR_MESSAGE_TOO_LONG;
}
real_checksum = _crc16_update(real_checksum, c); //checksum byte
//Receive next char
ret = transport_receive_char(timeout);
if (ret == TRANSPORT_ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
if (ret == TRANSPORT_ERR_NESTED_INTERRUPTS) return TRANSPORT_ERR_NESTED_INTERRUPTS;
if (ret == RET_MSG_START) return TRANSPORT_ERR_MESSAGE_ABORT; //no start char expected, this is an error (truncated message?)
if (ret == RET_MSG_END) break; //end char (literal) received
c = ret & 0xFF;
} }
RECV_SET_STATE(RECV_IDLE);
if (checksum != msg_recv_checksum) return TRANSPORT_ERR_CHECKSUM_FAIL;
return msg_recv_body_length;
} }
//checksum (low byte)
ret = transport_receive_char(timeout);
if (ret == TRANSPORT_ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
if (ret == TRANSPORT_ERR_NESTED_INTERRUPTS) return TRANSPORT_ERR_NESTED_INTERRUPTS;
if (ret == RET_MSG_START) return TRANSPORT_ERR_MESSAGE_ABORT; //no start char expected, this is an error (truncated message?)
if (ret == RET_MSG_END) return TRANSPORT_ERR_UNEXPCETED_END; //doesnt belong here, don't know how to handle that
expected_checksum = ret & 0xFF;
//checksum (high byte)
ret = transport_receive_char(timeout);
if (ret == TRANSPORT_ERR_TIMEOUT) return TRANSPORT_ERR_TIMEOUT;
if (ret == TRANSPORT_ERR_NESTED_INTERRUPTS) return TRANSPORT_ERR_NESTED_INTERRUPTS;
if (ret == RET_MSG_START) return TRANSPORT_ERR_MESSAGE_ABORT; //no start char expected, this is an error (truncated message?)
if (ret == RET_MSG_END) return TRANSPORT_ERR_UNEXPCETED_END; //doesnt belong here, don't know how to handle that
expected_checksum |= (ret & 0xFF) << 8;
if (expected_checksum != real_checksum){
// lcd_clear();
// lcd_home();
// for (uint8_t i = 0; i < recv_length; i++){
// lcd_write_byte_hex(buffer[i]);
// }
// _delay_ms(10000);
return TRANSPORT_ERR_CHECKSUM_FAIL;
}
return recv_length;
} }
uint16_t transport_receive_message(uint8_t* buffer, uint8_t length, uint16_t timeout){
USART_disable_rx_interrupt(); //Disable RX interrupt, we are going to poll rx from here now.
uint16_t ret = _transport_receive_message(buffer,length, timeout);
USART_enable_rx_interrupt(); //Enable RX interrupt again.
return ret;
}
\ No newline at end of file
...@@ -8,10 +8,7 @@ ...@@ -8,10 +8,7 @@
#define TRANSPORT_ERR_TIMEOUT 0xFFFF #define TRANSPORT_ERR_TIMEOUT 0xFFFF
#define TRANSPORT_ERR_CHECKSUM_FAIL 0xFFEE #define TRANSPORT_ERR_CHECKSUM_FAIL 0xFFEE
#define TRANSPORT_ERR_MESSAGE_ABORT 0xFFDD
#define TRANSPORT_ERR_MESSAGE_TOO_LONG 0xFFCC #define TRANSPORT_ERR_MESSAGE_TOO_LONG 0xFFCC
#define TRANSPORT_ERR_UNEXPCETED_END 0xFFBB
#define TRANSPORT_ERR_NESTED_INTERRUPTS 0xFFAA
#define TRANSPORT_IS_ERROR(ret) (((ret) & 0xFF00) != 0) #define TRANSPORT_IS_ERROR(ret) (((ret) & 0xFF00) != 0)
typedef void (*interrupt_msg_handler_t)(void); typedef void (*interrupt_msg_handler_t)(void);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment