interrupt or not interrupt uart

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

interrupt or not interrupt uart

Jump to solution
6,340 Views
Microfelix
Contributor IV

 

 

hello
I was studying the possibility of receiving data from the UART port.
Now I was asking a question:

MQX seen that there are tasks, so you can run a loop in each task to wait for receivingdata, you must use an interrupt for the UART?

 

Maurizio Felicioni

0 Kudos
1 Solution
2,331 Views
DavidS
NXP Employee
NXP Employee

Hi Maurizio,

I don't think the issue is to have the UART in POLLED or INTERRUPT mode but more of a system architecture conversation of how you want the two task to access the recieved data.

In general only on task will be able to receive the UART data.  Your code needs to either use a synchronization method (semaphore or mutex or glabal variable) or possible one task that will recieve the UART data and then determine who it is for and send that data to a task using message or global variable.

To see how a semaphore can be used for synchronization look at the mqx/examples/hello2 project.  It is outputing data to UART and not receiving but the concept is what I want you to see.

There is a lightweight message queue example that might be helpful too in mqx/examples/lwmsgq .

Hope this helps.

Regards,

David

View solution in original post

0 Kudos
25 Replies
2,281 Views
OldNick
Contributor IV

Subtle...I missed that, assuming that it would only hit the num-=i on the first time through

 

Suppose I'd better go back and put a "blush" smiley on the last post.

 

Thanks

0 Kudos
2,281 Views
OldNick
Contributor IV

Just for completeness, here is the eventual triple-buffered rs-232 task.

Seems to work OK.

 

A pity there wasn't an example - would have saved this thread.

 

#define BUFFSIZ 512
#define POLLBUFFSIZE 128

char rxbuff[BUFFSIZ];  // receive message buffer.  Note only one message at a time allowed.

/*--------------------------------------------------------------------------
MQX Serial Port Listener.  Very simple
--- */
void rs232_task
   (
      uint_32 dummy
   )
{
static int rxidx = 0;//index into global message buffer
uint_32 n,i;
char inBuff[POLLBUFFSIZE];
char tmp;
   while(1){//sit here forever
      _time_delay(10);//polling rate = 10mS - at 115200 could produce 100+ chars?
     n = _io_read (stdin, inBuff, POLLBUFFSIZE);
       for(i=0;n>0;i++,n--){
         tmp = inBuff[i];
         tx_byte(tmp);// echo debug
         if (CR==tmp){//if CR
            rxbuff[rxidx]=0;  //replace CR with 0
            rxidx=0;          //reset buffer pointer
            comm_handler();   //parse command
         }
         else{
            rxbuff[rxidx]=inBuff[i];
            if (++rxidx>BUFFSIZ) rxidx = BUFFSIZ;//stop overrun.
         }
      }
   }
}

If anybody finds that useful, feel free to borrow it.

0 Kudos
2,281 Views
MarkP_
Contributor V

Hi,

one thing I have noticed in real communication: There may be partial message or multiple messages

in buffer when io_read() returns.

Example code below handles those cases.

(Not sure if code is bugfree, wrote with Notepad as an example).

~Mark

 

static int rxidx = 0;//index into global message buffer
uint_32 n;
char inBuff[POLLBUFFSIZE];
uint_32 inCount=0;
uint_16 msgLen;
char *cp;
char tmp;

 while(1) //sit here forever
 {
   _time_delay(10);//polling rate = 10mS - at 115200 could produce 100+ chars?
   n = _io_read(stdin, inBuff+inCount, POLLBUFFSIZE-inCount-1);
   if (n > 0) // Chars received
   {
     inCount += n; // Update char count
     do {
       inBuff[inCount] = '\0'; // End null for strchr
       if ((cp = strchr(inBuff, CR) == NULL) // CR received?
       {
         if (inCount >= POLLBUFFSIZE-1) // No
         {
           //Too long line without CR
           inCount = 0; // Flush buffer
         }
       }
       else // CR received
       {
         msgLen = cp-inBuff;
         comm_handler(inBuff, msgLen);    //parse command
         memmove(inBuff, cp+1, msgLen+1); // remove handled message
         inCount -= msgLen+1;
       }
     } while (CP != NULL && inCount > 0); // Still data in buffer?
   }
}

 

0 Kudos
2,281 Views
ETCTim
Contributor I

I have an additional question...

Is it possible to use the MQX interrupt implemenation, and read the status registers at them same time?

I need to know what the status register was for each byte so that I can detect a break condition.

It is important to know where the break condition is.

Is it possible to do this?

 

Thanks,

Tim

 

0 Kudos
2,281 Views
MarkP_
Contributor V

Hi,
Not sure if this kind of solution works:
Save at RX break interrupt the tail index to the new variable RX_BREAK_INDEX.
At read function the RX_BREAK_INDEX is checked.
New code between #if 1 ... #endif below.
Looked also Kinetis datasheet, not sure if the use of UART_S2_LBKDIF_MASK need
some initialization.

file serinprv.h:
----------------
   _mqx_uint             INPUT_LOW_WATER_MARK;
#if 1//BREAK
   _mqx_int              RX_BREAK_INDEX; // -1=not set, other value is head index
#endif
} IO_SERIAL_INT_DEVICE_STRUCT, _PTR_ IO_SERIAL_INT_DEVICE_STRUCT_PTR;


file serl_int_kuart.c:
----------------------
void _kuart_int_err_isr()
...
    if(stat & UART_S1_FE_MASK) {
       ++sci_info_ptr->RX_FRAMING_ERRORS;
    }
#if 1//BREAK
    stat = sci_ptr->S2;
    if ((stat & UART_S2_LBKDIF_MASK) != 0) {
        sci_ptr->S2 = UART_S2_LBKDIF_MASK; // Clear bit
        /* Set current index to break index if not yet set */
        if (int_io_dev_ptr->RX_BREAK_INDEX == -1) {
            int_io_dev_ptr->RX_BREAK_INDEX = int_io_dev_ptr->IN_QUEUE->TAIL;
        }
    }
#endif
}  /* Endbody */


file serl_int.c:
----------------
_mqx_int _io_serial_int_open()
...
   int_io_dev_ptr->COUNT = 1;
#if 1//BREAK
   int_io_dev_ptr->RX_BREAK_INDEX = -1;
#endif
   return(result);
} /* Endbody */


_mqx_int _io_serial_int_read()
...
   while ( i ) {

      in_queue = int_io_dev_ptr->IN_QUEUE;
      _int_disable();
#if 1//BREAK
      if (int_io_dev_ptr->RX_BREAK_INDEX != -1) { // Break received?
        if (int_io_dev_ptr->RX_BREAK_INDEX == in_queue->HEAD) { // Now at break location?
          if (i == num) { // Characters copied?
            num = -99;    // NO, RETURN BREAK OCCURENCE
            int_io_dev_ptr->RX_BREAK_INDEX = -1; // Remove break mark
            _int_enable();
            break;
          }
          else   // Some characters copied, return them. Break is returned at next call
          {
            num -= i;
            _int_enable();
            break;
          }
        }
      }
#endif
      if(flags & IO_SERIAL_NON_BLOCKING) {
          if (_CHARQ_SIZE(in_queue) == 0) {

BR,
Mark


0 Kudos
2,332 Views
DavidS
NXP Employee
NXP Employee

Hi Maurizio,

I don't think the issue is to have the UART in POLLED or INTERRUPT mode but more of a system architecture conversation of how you want the two task to access the recieved data.

In general only on task will be able to receive the UART data.  Your code needs to either use a synchronization method (semaphore or mutex or glabal variable) or possible one task that will recieve the UART data and then determine who it is for and send that data to a task using message or global variable.

To see how a semaphore can be used for synchronization look at the mqx/examples/hello2 project.  It is outputing data to UART and not receiving but the concept is what I want you to see.

There is a lightweight message queue example that might be helpful too in mqx/examples/lwmsgq .

Hope this helps.

Regards,

David

0 Kudos
2,285 Views
OldNick
Contributor IV

Hmmm. 

In a bare-metal system I would have an interrupt handler for the UART, which stores the incoming chars in a buffer, and sets one or more flags.

The flag can be polled whenever the application is ready to cope with the incoming data.

What nice bit of example code shows me how a task can wait for incoming data on the UART?  I have looked for this in the MQX examples but haven''t found it yet.

Thanks

0 Kudos
2,285 Views
MarkP_
Contributor V

You need to define ITTYx (means interrupt):

#define BSPCFG_ENABLE_TTYD       0
#define BSPCFG_ENABLE_ITTYD      1

 

Change default IO-channel in twrk60n512.h to ittyd:

 

#if BSPCFG_ENABLE_ITTYD
        //#define BSP_DEFAULT_IO_CHANNEL                      "ttyf:"    /* OSJTAG-COM   polled mode   */
        #define BSP_DEFAULT_IO_CHANNEL                        "ittyd:"    /* RS 232 port */
        #define BSP_DEFAULT_IO_CHANNEL_DEFINED          
    #else
        #define BSP_DEFAULT_IO_CHANNEL                        NULL
    #endif
#endif

 

Open uart:

  MQX_FILE_PTR uart_device; 

  uart_device = _io_fopen("ittyd:",  (char _PTR_)(IO_SERIAL_RAW_IO|IO_SERIAL_NON_BLOCKING));

Write&read data:

count = _io_write( uart_device, data, len );

readCount = _io_read ( uart_device, data, bufSize);

 

Change queue-size if larger than 64 needed in user_config.h:

#define BSPCFG_SCI3_QUEUE_SIZE 256 /* sci3=ittyd */

 

0 Kudos
2,285 Views
OldNick
Contributor IV

Thanks Mark,

 

which bit of that is sitting waiting for incoming characters? (I suspect fread...), and what is the task doing in the meantime?

 

Also, does the device need a mutex for TX use, or is that built into the fwrite?

0 Kudos
2,285 Views
DavidS
NXP Employee
NXP Employee

Hi OldNick,

Here is one way.

 

   pointer fh_ptr;

   uint_32 result;

 

   fh_ptr = (pointer)fopen("ittyc:", BSP_DEFAULT_IO_OPEN_MODE);

   if (fh_ptr == NULL) {
      printf("cannot open file: ittyc\n");  //print to uart0
   } else {
      printf("\nittyc: Device Handler opened successfully\n");  //print to uart0
      fflush(stdout);
   }

   if(result = fstatus(fh_ptr))  // check uart  to see if char is pending
   {
     printf("Character waiting in UART1\n");   //print to uart0
     read(fh_ptr,(pointer)&Z,1); // read 1 char to uart1
     printf("Your character in UART1 was %c\n", Z); //print to uart0
   }
   else
     printf("No character waiting in UART1\n");  //print to uart0

 

Hope this helps.

Regards,

David

0 Kudos
2,285 Views
OldNick
Contributor IV

DavidS

Sorry to be a bit slow on the uptake here.  Your example code looks like "check once then exit".

 

I don't think 

while(1){

   if(result = fstatus(fh_ptr)) {

      handlecharacter();

   }

}

                  is very elegant.

Anyway, I am reading chapter 5 of MQXIOUG, and not seeing anything that enables a task to "wait on RX"

Nor is fstatus documented in that manual, or anywhere else that I can find in the docs directory.

 

I reckon there must be something simple in there somewhere?  A simple UART example that waits on incoming chars and echoes them back to the terminal would be a great help.

0 Kudos
2,285 Views
DavidS
NXP Employee
NXP Employee

Hi OldNick,

Yes my example checked once (i.e. it is a non-blocking method).  It could easily be implemented in a loop as well.

When you do a read with a polling driver, then it will wait for a charater to be entered.

ex: read(fh_ptr,(pointer)bptr++,1); // read 1 char where fh_ptr is returned from fopen(), bptr is pointer to a character buffer, and 1 indicates you are waiting for one charater only.

 

The fstatus() gets mapped to io_fstatus() from fio.h and is in io_fstatus.c.  Sorry that it isn't in UG or RM.  We are trying to document everything but not quite there yet.

 

Interrupt drivers will read the input data as it arrives, place it in a buffer, set a flag so if a task was blocked waiting for data it will awaken.

 

Hope this helps.

Regards,

David

 

0 Kudos
2,285 Views
OldNick
Contributor IV

Ok guys.  this is how I have set it up.

in userconfig.h

#define BSPCFG_ENABLE_ITTYA      1//use interrupt version...

 

in MyBoardk40x256.h

#if BSPCFG_ENABLE_ITTYA
    #define BSP_DEFAULT_IO_CHANNEL                          "ittya:"   
        #define BSP_DEFAULT_IO_CHANNEL_DEFINED          
...

 #define BSP_DEFAULT_IO_OPEN_MODE                          (pointer) ( IO_SERIAL_RAW_IO |IO_SERIAL_NON_BLOCKING)

 

the tasklist is

const TASK_TEMPLATE_STRUCT  MQX_template_list[] =
{
   /* Task Index,   Function,   Stack,  Priority, Name,     Attributes,          Param, Time Slice */
    { HELLO_TASK,   hello_task, 1500,   8,        "hello",  MQX_AUTO_START_TASK, 0,     0 },
    { COMMS_TASK,   rs232_task, 1000,   8,        "rs232",  MQX_AUTO_START_TASK, 0,     0 },
    { 0 }
};

And the rs232-stask is

/* --------------------------------------------------------------------------
MQX task.  Very simple
--- */
void rs232_task
   (
      uint_32 dummy
   )
{
static int rxcount = 0;
uint_32 st;
char in;
   while(1){//sit here forever
     st = status();  //waiting for chars
     if (st){
       in = getchar();//add char to rxbuff
       tx_byte(in);// echo debug
       if (CR==in){//if CR
         rxbuff[rxcount]=0; //replace CR with 0
         comm_handler();     //parse command
         rxcount=0;          //reset rxcount
       }
       else{
         rxbuff[rxcount]=in;
         if (++rxcount>BUFFSIZ) rxcount = BUFFSIZ;//stop overrun.
       }
     }
   }
}
This works quite nicely...except that the hello_task never runs.

:robotsad:

Like I said, I need this task to sleep pending an incoming character.

 

Help?

0 Kudos
2,285 Views
MarkP_
Contributor V

Hi,

when using nonblocking interrupt method the getchar doesn't block, instead it returns IO_EOF.

_mqx_int _io_fgetc:

...

   if ((*dev_ptr->IO_READ)(file_ptr, &tmp, 1) == 1) {
      return((_mqx_int)((uchar)tmp));
   } else {
      return(IO_EOF);
   } /* Endif */


You need to add task suspend/wait when this IO_EOF is received.

 

Serial read function:

_mqx_int _io_serial_int_read:

 ...

   while ( i ) {
      in_queue = int_io_dev_ptr->IN_QUEUE;
      _int_disable();
      if(flags & IO_SERIAL_NON_BLOCKING) {
          if (_CHARQ_SIZE(in_queue) == 0) {
              num -= i;
              _int_enable();
              break;
          } /* Endif */
      } else {
          while (_CHARQ_SIZE(in_queue) == 0) {
             _taskq_suspend(int_io_dev_ptr->IN_WAITING_TASKS);
          } /* Endwhile */ 
      } /* Endif */
      _CHARQ_DEQUEUE(in_queue,c);

 

 

0 Kudos
2,285 Views
OldNick
Contributor IV

Hmmm.  Take a while to get my head around that.

 

I would rather take a step backward and re-frame the original question.

 

What is the simplest way to get a task to wake up when a character is received on the RS232-port?

 

e.g. for a task to go to sleep for a while, I call "time_delay()

 

So to make a task wait for RX-chars, I call ???

 

Polling the UART takes processor time.  But the interrupt driver surely ought to be capable of waking a task?

 

Thanks again for all the advice.

0 Kudos
2,285 Views
MarkP_
Contributor V

Not sure, but I think MQX is designed for that purpose by not defining IO_SERIAL_NON_BLOCKING.

But what happens if the baudrate is e.g. 115200, the waiting task is wakened up quite often when receiving data.

I've made in my earlier (non MQX) projects that receive buffer is polled in 10 or 20 ms period.

 

I've made the polling in MQX in this way:

readCount = _io_read (uart_device, dataBuffer, bufSize);

~Mark

0 Kudos
2,285 Views
OldNick
Contributor IV

Yep...interrupts about every 90us.

that was going to be my next question...how to move the task-wake from a per-character basis.  (Since the driver has a buffer anyway.)

 

Looks like I am going to be doing the good old 10mS poll->double-buffer->process shuffle (Always assuming I don't get buffer overruns because some other task is munching away and won't give me the processor back.)

 

Seems crazy that all this stuff gets done for USB and Ethernet stacks, but there is nothing for the ubiquitous UART.  (115,200 bits per second is too fast for them, I suppose.:robotwink:)

 

Do you happen to know what the context-switching time for MQX is, on (say) a Kinetis at 100MHz?

0 Kudos
2,285 Views
DavidS
NXP Employee
NXP Employee

Hi OldNick,

With the UART/SCI in interrupt mode, there is a 64byte buffer defined in the twrk60n512.h in the BSP.

/* TTYA and ITTYA buffer size */
#ifndef BSPCFG_SCI0_QUEUE_SIZE
    #define BSPCFG_SCI0_QUEUE_SIZE             64
#endif

It really is only used with the interrupt mode.  As a character is received, the interrupt handler will add it to the buffer.  If there is a task blocked and waiting for a character, the RTOS will place it in the ready queue and when it is the highest priority task ready to run it will run.  Should the task have to wait for awhile as other higher priority tasks are running, the UART/SCI interrupt keeps adding to the buffer.

Hope this helps.

Regards,

David

 


 

0 Kudos
2,285 Views
OldNick
Contributor IV

DavidS,

You wrote

If there is a task blocked and waiting for a character,

 

I know this sounds silly, but that is the one thing I cannot see how to set up.

 

Please explain, if I am using the non-blocking interrupt driver,which API layer function call will block the task until one or more characters are available in the ring buffer?

 

As you saw, I tried status() and getchar() and that was not succesful.

=================

MarkP

I assume that _io_read is non-blocking, and returns 0 if there is nothing in the buffer?  Should I have been able to find that out by myself?  (Sorry)

0 Kudos
2,285 Views
MarkP_
Contributor V

Hi TIm,

The MQX IO-Drivers User's Guide describes the _io_read operation. If no characters received, it returns 0.

My first trial with interrupt-mode halted the task, then debugging the code I found the parameter IO_SERIAL_NON_BLOCKING.

In MQX BSP-project there are peripheral drivers source folder (don't remember the exact name because sitting now at my home PC). Studying those polled mode and interrupt mode source files helps to understand the functionality.

~Mark

 

0 Kudos