| Author |
Message |
|
|
Posted: Mar 04, 2012 - 11:36 PM |
|

Joined: Jun 14, 2011
Posts: 23
|
|
I'm writing a firmware for a project that will obviously have a USART Terminal for viewing/changing operational parameters and testing various items in the project. I'm a stickler for efficiency, using the least a mount of clock cycles and memory as possible (within reason). I'm using a UC3 (L0 for my current development, which will be ported to C2 later)
Here's my question to the community:
What have you found to be the best way to handle the terminal via the ISR while providing a clean input for the user? (IE: Backspace deletes chars properly with not repeats and overwrites)
The following code is what I have currently (which is always evolving) and I'm wondering if calling the print function within the ISR to a good use of resources or if I should try a different way. I'll post 2 versions, one with vanilla ASF and one with FreeRTOS doping (which I'm still fiddling with, thus the purpose of this post).
CUB1 is cursor back 1
serialDELIMITER is CR which is 13
BS is 127
Vanilla:
Code:
static void usart_int_handler(void)
{
int c = 0;
if(USART->csr & AVR32_USART_CSR_RXRDY_MASK)
{
usart_read_char(USART, &c);
if ((char)c == BS)
{
if(usart_rx_idx > 0) usart_rx_idx--;
print(USART, CUB1 CLEARLCR);
}
else if ((char)c == CR)
{
//When CR is detected without any input, refresh screen
if (usart_rx_idx == 0)
{
work_reg |= USART_RX_NOIN;
}
else
{
//Set bit 1 for DataRDY
work_reg |= USART_RX_DRDY;
// Reset RX Index
usart_rx_idx = 0;
}
}
else
{
usart_rx_buffer[usart_rx_idx++] = (char)c;
usart_write_char(USART, c); // echo
}
}
}
This code will set a register with various parameters depending on the char received. Such as CR tells the application that the command is finished, process; if no data before the cr, then say no data and the prompt will refresh. BS means It should delete the previous char (just back the pointer up) and move the cursor and clear the screen to the right (this is the efficient part I'm wondering, should I even bother, that's several chars that get sent out which cause the ISR to be delayed that much more).
FreeRTOS Doping (modified from demo):
Code:
static portBASE_TYPE prvUSART_ISR_NonNakedBehaviour( void )
{
/* Now we can declare the local variables. */
signed portCHAR cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
unsigned portLONG ulStatus;
volatile avr32_usart_t *usart = serialPORT_USART;
portBASE_TYPE retstatus;
/* What caused the interrupt? */
ulStatus = usart->csr & usart->imr;
if (ulStatus & AVR32_USART_CSR_TXRDY_MASK)
{
/* The interrupt was caused by the THR becoming empty. Are there any
more characters to transmit?
Because FreeRTOS is not supposed to run with nested interrupts, put all OS
calls in a critical section . */
portENTER_CRITICAL();
retstatus = xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken );
portEXIT_CRITICAL();
if (retstatus == pdTRUE)
{
/* A character was retrieved from the queue so can be sent to the
THR now. */
usart->thr = cChar;
}
else
{
/* Queue empty, nothing to send so turn off the Tx interrupt. */
usart->idr = AVR32_USART_IDR_TXRDY_MASK;
}
}
if (ulStatus & AVR32_USART_CSR_RXRDY_MASK)
{
/* The interrupt was caused by the receiver getting data. */
cChar = usart->rhr; //TODO
/* Because FreeRTOS is not supposed to run with nested interrupts, put all OS
calls in a critical section . */
portENTER_CRITICAL();
//Upon recieving a serialDELIMITER character, wake the Terminal Up
if (cChar == serialDELIMITER)
{
xTaskResumeFromISR(menuTermHandle);
}
// Is char a backspace?
else if(cChar == BS)
{
// pop off last char, but don't do anything with it
xQueueReceiveFromISR( xCharsForTx, &cChar, &xHigherPriorityTaskWoken);
//Send out cursor move left one?
}
else
{
xQueueSendFromISR(xRxedChars, &cChar, &xHigherPriorityTaskWoken);
// Echo Char
#if configSERIAL_ECHO
usart->thr = cChar;
#endif
}
portEXIT_CRITICAL();
}
/* The return value will be used by portEXIT_SWITCHING_ISR() to know if it
should perform a vTaskSwitchContext(). */
return ( xHigherPriorityTaskWoken );
}
Basically the same as previous but using FreeRTOS and Queues.
--Update--
An extension of the ISR question for those who have used FreeRTOS. For your Idle level process that handles the USART Terminal which is your method of choice: xTaskSuspend/xTaskResumeFromISR, Semaphores or long blocking times on xSerialGetChar?
Thanks for your input! |
|
|
| |
|
|
|
|
|
Posted: Mar 05, 2012 - 09:01 AM |
|

Joined: Aug 19, 2003
Posts: 396
Location: Australia
|
|
My motto is "Get it working first, then optimise".
If I cannot optimise, run the processor faster !
Well, in the vanilla version you can save several C statements by replacing the usart_read_char(,); with c=USART->rhr;
Similarly, replace usart_write_char(,); with usart->thr=c;
Is CUB1 a single byte or a multi-byte sequence ? |
|
|
| |
|
|
|
|
|
Posted: Mar 05, 2012 - 10:00 AM |
|

Joined: Jun 14, 2011
Posts: 23
|
|
CUB1 is a VT100 command, its a few bytes long.
Code:
#define CUB1 "\x1B[1D" //!< Move Curser to the Left 1 Col
Yeah that's why the FreeRTOS is putting stuff straight into the reg, save a few mjmp.
I'm setting up PDCA to transmit the large USART transfers such as the help menu and what not right now. |
|
|
| |
|
|
|
|
|
Posted: Mar 07, 2012 - 01:16 AM |
|

Joined: Mar 28, 2010
Posts: 127
Location: Palmerston North, New Zealand
|
|
Hi wedgen,
If you want absolute speed, then you should plan to use a PDCA based version (with interrupts) which does not use any cpu cycles until a complete instruction is received. An then use something like realTerm, which handles sending in bursts. This is the basic method I use (all be it using the USB as a COM port) to achieve the same thing |
|
|
| |
|
|
|
|
|
Posted: Mar 07, 2012 - 08:46 AM |
|

Joined: Jun 14, 2011
Posts: 23
|
|
| The thing with using PDCA is how to detect when the user has given a CR to complete the command. PDCA wants to fill its buffer before it interrupts. Unless there's some super secret register value that can make it throw its ISR when it gets a certain value? Or do you keep all your commands the same length? which I find difficult as different elements require different lengths of values. |
|
|
| |
|
|
|
|
|
Posted: Mar 07, 2012 - 10:17 AM |
|

Joined: Oct 10, 2007
Posts: 395
Location: Valls, Spain
|
|
You can enable the UART timeout interrupt, this way you can use the PDCA without having to use fixed length packets nor wait till the Rx buffer is full.
When the timeout interrupt is triggered, just grab the data from the Rx buffer and reload the pdca channel. |
_________________ Daniel Campora
http://www.lear.com
|
| |
|
|
|
|
|
Posted: Mar 07, 2012 - 07:36 PM |
|

Joined: Mar 28, 2010
Posts: 127
Location: Palmerston North, New Zealand
|
|
I do as dani has suggested, when there is a significant delay in sending of data (I use 10 times the baud rate frame for one byte) then I process what has arrived. If u are using a terminal emulator this may not work well, as (I think) they send each byte as soon as it is entered, but to achieve what your after I would suggest using something like realTerm (v2.0.0.57) which allows you to enter commands, and then send the bytes all at once (no backspaces). This is how I manage the units I program, and I have a large number of variable length commands that I send.
Regards. |
|
|
| |
|
|
|
|
|
Posted: Mar 08, 2012 - 05:27 AM |
|

Joined: Jun 14, 2011
Posts: 23
|
|
Hmm, the burst send/pdca timeout is definitely an option.
I was hoping to give it interoperability between any emulator/terminal application. But that's certainly something to try. I do like realTerm, but I find I always go back to putty, maybe for simplicity? *shrug*
Thanks for the suggestions! |
|
|
| |
|
|
|
|
|