Hi All,
I've tried a concept that (with minimal testing) seems to work pretty well for both transmitting and receiving data on a serial port. This way uses two seperate tasks, one for transmit and one for receive, with both tasks blocking (and not using processor cycles) when not actually servicing data.
First, create a globally accessable serial device descriptor and one task for serialTX and one task for serialRX. The main task inits the serial device (interrupt mode, blocking) and creates the two serial tasks.
TASK_TEMPLATE_STRUCT MQX_template_list[] = { /* Task number, Entry point, Stack, Pri, String, Auto? */ {MAIN_TASK, Main_task, 3000, 11, "main", MQX_AUTO_START_TASK}, {SERIALTX_TASK, MAIN_SerialTxTask, 3000, 10, "setx", 0}, {SERIALRX_TASK, MAIN_SerialRxTask, 3000, 9, "serx", 0}, {0, 0, 0, 0, 0, 0}};//Declare a global FileDescriptor for access to the serial portFILE_PTR serial_fd;//Declare some global vars to config portuint_32 baud=57600;uint_32 flags=IO_SERIAL_RAW_IO;void Main_task(uint_32 initial_data) { _task_id tiSerialTxTaskID; _task_id tiSerialRxTaskID; //Init file descriptor to access serial port(9) serial_fd = fopen("ittyj:", IO_SERIAL_RAW_IO); //Open in blocking mode ioctl(serial_fd, IO_IOCTL_SERIAL_SET_BAUD, &baud); //Set baud rate //Create the task to transmit serial data tiSerialTxTaskID = _task_create_blocked(0, SERIALTX_TASK, 0); if(tiSerialTxTaskID == MQX_NULL_TASK_ID) _time_delay(1); //Create the task to receive serial data tiSerialRxTaskID = _task_create(0, SERIALRX_TASK, 0); if(tiSerialRxTaskID == MQX_NULL_TASK_ID) _time_delay(1); //While serial task is waiting for data we can run this task and do whatever//---------------------------------------------------------------------------- while(1) { //Do whatever needs doing... } fclose(serial_fd); _mqx_exit(0); }
The serial receive task calls read() (blocking) in an endless loop like this:
void MAIN_SerialRxTask(uint_32 initial_data) { uint_32 iCount = 0; unsigned char data_buffer[2048]; while(1) { //Function blocks so MQX can allow other tasks to run iCount = read(serial_fd, data_buffer, 1); if(iCount == IO_ERROR) break; //Write the recvd data back out the same port write(serial_fd, data_buffer, iCount); } return; }
The transmit task calls write() to transmit the data in the TxBuffer then blocks itself again in an endless loop:
void MAIN_SerialTxTask(uint_32 initial_data) { while(1) { //Write contents of TxBuffer write(serial_fd, TxBuffer, iTxBufferCount); //Return to blocking state again when finished transmitting TxBuffer _task_block(); } return; }
Now in order to transmit, any function can load the TxBuffer with data and unblock the transmit task:
//Load TxBuffer....//Unblock serialTx task_task_ready( _task_get_td(tiSerialTxTaskID) );
Obviously, care has to be taken to avoid conflicts when accessing the transmit and receive buffers from multiple locations (that code not shown here). But it seems like the concept of a single common serial device descriptor, that is accessed by both a transmit and receive tasks could work? The advantage of this method is that neither task is unblocking and using processor cycles unless it actually has something to do.
I suppose a variation on this theme would be to have the Txtask block when finished transmitting and unblock on a lwevent. Then, whoever wants to send data could load the TxBuffer and trigger the lwevent that wakes up the serial transmit task.
Any thoughts, cautions, observations, etc on this concept would be greatly appreciated as I'm very new to the whole MQX / RTOS thing. Thanks!!
~Tim