/** * usart_interface.c * * Implements a command line interface for various features on the board. * * Copyright (c) 2022 Bigscreen, Inc. */ #include #include #include "usart_interface.h" TaskHandle_t xUsartTaskHandle; uint8_t usart_in_buffer[USART_BUFFER_SIZE]; // Rx Buffer loaded by interrupt volatile uint32_t usart_in_place; uint8_t usart_received_buffer[USART_BUFFER_SIZE]; // "Safe" rx buffer, copied from interrupt, // won't have interference from IRQ triggering while being used uint32_t usart_received_place; uint8_t usart_out_buffer[USART_BUFFER_SIZE]; // Tx buffer uint32_t usart_out_place; Data_Packet_T received_packet; uint8_t error_flags; static uint8_t usart_check_command(void); void usart_interface_init(void) { // Clear the buffers memset(usart_in_buffer, 0, USART_BUFFER_SIZE); memset(usart_out_buffer, 0, USART_BUFFER_SIZE); usart_in_place = 0; usart_out_place = 0; error_flags = 0; // Ensure that nearby pins don't cause us to think there's a UART // byte coming in, enable the pad pullup ioport_set_pin_mode(PIN_UART_RX, IOPORT_MODE_PULLUP); // Initialize the receiver interrupt NVIC_EnableIRQ(CONSOLE_UART_ID); NVIC_SetPriority(CONSOLE_UART_ID, USART_INTERRUPT_PRIO); // trigger whenever a byte is waiting in the receiver CONSOLE_UART->US_IER = US_IER_RXRDY; if(xTaskCreate(usart_task, "USART", TASK_USART_STACK_SIZE, NULL, TASK_USART_PRIORITY, &xUsartTaskHandle) != pdPASS) { printf("Failed to create usart task\r\n"); } } void usart_task(void *pvParameters) { UNUSED(pvParameters); // xUsartTaskHandle = xTaskGetCurrentTaskHandle(); while(1) { vTaskDelay(1); // Check if we have received a command packet if(pdTRUE == usart_check_command()) { // Perform the requested action UI_process_command(&received_packet); if(pdPASS == UI_build_packet(usart_out_buffer, USART_BUFFER_SIZE, UI_get_response())) { usart_out_place = (UI_get_response()->Length) + USART_DATA_OFFSET; for(uint8_t j = 0; j < usart_out_place; j++) { usart_putchar(CONSOLE_UART, usart_out_buffer[j]); } usart_out_place = 0; } } } } static uint8_t usart_check_command(void) { uint32_t bytes_to_copy; // Read the number of bytes already in the queue, placed there by interrupt // This should be done with IRQs disabled to ensure no RTOS context switches or // further interrupts occur irqflags_t flags = cpu_irq_save(); if((usart_in_place + usart_received_place) < USART_BUFFER_SIZE) { bytes_to_copy = usart_in_place; } else { // fill as much of the remaining buffer as possible bytes_to_copy = usart_in_place - ((usart_in_place + usart_received_place) - USART_BUFFER_SIZE); error_flags |= ERROR_FLAG_OVERRUN; } memcpy(&(usart_received_buffer[usart_received_place]), usart_in_buffer, bytes_to_copy); usart_received_place += bytes_to_copy; usart_in_place = 0; cpu_irq_restore(flags); // Check the buffer for a valid comms packet uint8_t* start_byte = UI_check_packet(usart_received_buffer, usart_received_place, &received_packet); if(NULL == start_byte) { // There was no start byte in the whole buffer. // We can safely delete the buffer and start fresh. usart_received_place = 0; } else { uint32_t start_place = ((uint32_t)start_byte) - ((uint32_t)usart_received_buffer); uint32_t end_place = start_place + USART_DATA_OFFSET + received_packet.Length; if(received_packet.ErrorCode == UI_ERR_SUCCESS) { // shift the remainder of the received buffer down, deleting this received packet // memmove won't do anything if length (3rd argument) is zero, no need to check if the two "place"s are equal memmove(usart_received_buffer, &(usart_received_buffer[end_place]), usart_received_place - end_place); usart_received_place -= end_place; return pdTRUE; } else { // So there's a start byte, but an incomplete or invalid packet. // Let's find out why. switch(received_packet.ErrorCode) { case UI_ERR_NO_PACKET: // We get here if a start byte exists but there aren't enough data // bytes in the buffer for even a zero-data packet. // Can remove extraneous bytes from before the start byte to keep the // buffer from overfilling. if(start_place <= USART_BUFFER_SIZE) { memmove(usart_received_buffer, start_byte, usart_received_place - start_place); usart_received_place -= start_place; } break; case UI_ERR_BAD_START_SEQ: case UI_ERR_BAD_TYPE: case UI_ERR_INVALID_LENGTH: default: // Any of these represent invalid packets. Can safely delete from // the beginning of the buffer up to and including the start byte in the buffer. end_place = start_place + 1; if(end_place <= USART_BUFFER_SIZE) { memmove(usart_received_buffer, &(usart_received_buffer[end_place]), usart_received_place - end_place); usart_received_place -= end_place; } else { // Packet length extends beyond valid buffer region, somehow. // Just delete the whole buffer usart_received_place = 0; } break; } } } return pdFALSE; } #if BOARD == USER_EXT_BOARD // Console is on USART3 (PA3/PA4) void FLEXCOM3_Handler(void) { #else // Console is on USART7 (PA27,28) void FLEXCOM7_Handler(void) { #endif // Check for RXRDY interrupt if(((CONSOLE_UART->US_IMR) & (US_IMR_RXRDY)) != 0) { if(((CONSOLE_UART->US_CSR) & (US_CSR_RXRDY)) != 0) { // New character received, add it to the holding buffer if(usart_in_place < USART_BUFFER_SIZE) { usart_in_buffer[usart_in_place++] = CONSOLE_UART->US_RHR; } // If there's an overrun, notify the application else { error_flags |= ERROR_FLAG_OVERRUN; // Overwrite the last byte in the buffer. We need to read // the USART rx holding register otherwise we will be stuck // in this interrupt usart_in_buffer[USART_BUFFER_SIZE-1] = CONSOLE_UART->US_RHR; } } } }