Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
wedgen
PostPosted: Mar 04, 2012 - 11:36 PM
Rookie


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!
 
 View user's profile Send private message  
Reply with quote Back to top
mikech
PostPosted: Mar 05, 2012 - 09:01 AM
Hangaround


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 ?
 
 View user's profile Send private message  
Reply with quote Back to top
wedgen
PostPosted: Mar 05, 2012 - 10:00 AM
Rookie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
daffniles
PostPosted: Mar 07, 2012 - 01:16 AM
Hangaround


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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
wedgen
PostPosted: Mar 07, 2012 - 08:46 AM
Rookie


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.
 
 View user's profile Send private message  
Reply with quote Back to top
danicampora
PostPosted: Mar 07, 2012 - 10:17 AM
Hangaround


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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
daffniles
PostPosted: Mar 07, 2012 - 07:36 PM
Hangaround


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.
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
wedgen
PostPosted: Mar 08, 2012 - 05:27 AM
Rookie


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!
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits