A basic SCI Loopback program is written in C to transmit and receive a message with the same SCI peripheral. on the TMS5701224 Hercules Launchpad for Safety Microcontrollers. This involved writing FreeRTOS tasks to perform the SCI Transmission and Receive functionalities.
The SCI Driver and FreeRTOS Port files were generated from skeleton code from the UBC Orbit Satellite Design Team. The source code is written using a Hardware Abstraction Layer in (sys_main.c
).
Code Composer Studio (CCS) is an Eclipse based IDE to develop applications for Texas Instruments (TI) embedded processors.
We will be using the ARM compiler included with CCS to create our executable.
CCS allows us to use the XDS 110 entry level debug probe to step through the code after flashing the executable file to the microcontroller. This allows us to view the contents of the registers during runtime and determine if our SCI Loopback is working as designed.
We set a breakpoint in the Interrupt Service Routine (i.e. ISR) for SCI RX interrupt events. We break code execution upon reading a byte from the Receive Data Buffer (i.e. SCIRD), as shown below.
The purpose of the SCI Loopback is to transmit and receive the same data with the same peripheral. This allows us to test other functionality during data transfers. We are particularly interested in testing mutual exclusion with semaphores.
Viewing the contents of the Receive Data Buffer (i.e. SCIRD) when the program hits the breakpoint above shows us that the byte from the Transmit Data Buffer (i.e. SCITD) is received as intended.
Here is a sample of the CCS register displays during runtime.
. | Register Contents |
---|---|
1 | |
2 | |
3 |
The TMS570LS1224 Technical Reference Manual specifies that the SCI Data Buffers contain 8 bits of data. Due to this, we must copy 1 byte of data (i.e. 1 character) from the SCIRD register to the variable
static uint8_t rxBuff[LENGTH_BUFF];
at a time.
We will be using the FreeRTOS Kernel to periodically schedule and run the SCI TX and SCI RX tasks. We must use Static APIs to refrain from dynamic allocation in our microcontroller.
Task Creation for the SCI TX and RX functionality is nearly identical. These must be created before starting the scheduler included with FreeRTOS, as shown below.
/* Create SCI TX Task. */
xTaskCreateStatic(
sciTxTask, //
"SCI TX Task", // Task Name
DEFAULT_STACK_SIZE, // Stack Size for Task
NULL, // Parameter Pointers
DEFAULT_TASK_PRIORITY, // Task Priority
txStackBuffer, // Stack
&txTaskBuffer // Task Control Block
);
/* Create SCI RX Task. */
xTaskCreateStatic(
sciRxTask, //
"SCI RX Task", // Task Name
DEFAULT_STACK_SIZE, // Stack Size for Task
NULL, // Parameter Pointers
DEFAULT_TASK_PRIORITY, // Task Priority
rxStackBuffer, // Stack
&rxTaskBuffer // Task Control Block
);
Note : The stack buffer is defined as an array of type uint32_t
and size DEFAULT_STACK_SIZE
. The task buffer for the Task Control Block (i.e. TCB) is defined as type StaticTask_t
.
We use semaphores as a signalling mechanism that the buffer static uint8_t rxBuff[LENGTH_BUFF];
contains a complete message. This allows us to determine when the SCI RX task should run.
We create the semaphore before creating the tasks or starting the scheduler, by calling sciRxSem = xSemaphoreCreateBinaryStatic(&sciRxSemBuffer);
.
We pass a pointer to a semaphore buffer of type StaticSemaphore_t
as a parameter and assign the return value to a semaphore handle variable.
We give the semaphore every time a complete message is received and processed from the SCI peripheral. This is done in the ISR.
We try to periodically take the semaphore by calling xSemaphoreTake(sciRxSem, waitTime);
. In the meantime, the SCI RX task is in a Blocked state and other threads in the Ready state are run.
The relationship described between the tasks is analogous to a producer-consumer design. In our case, the SCI TX task and ISR for SCI RX interrupt events act as producers. They add data to the shared message buffer.
This data is used by the SCI RX task, which acts as a consumer task.
The SCI RX events are handled by our callback function for SCI interrupts. We must use the interrupt-safe version of the FreeRTOS API by calling xSemaphoreGiveFromISR(sciRxSem, &xTaskWoken);
. These API functions never block code execution.
We pass a pointer to a BaseType_t
variable to see if a task is woken by increasing the semaphore count. If a task is woken, it is called immediately upon completion of the ISR. This variable is once again passed at the end of the callback function as we yield operation to the scheduler by calling portYIELD_FROM_ISR(xTaskWoken);
.
To periodically generate a message and send it to the SCI peripheral, we call the FreeRTOS API vTaskDelayUntil(...);
to keep the SCI TX task in a Blocked state for the duration of MSG_DEADLINE
.
/* Next Message is Sent After MSG_DEADLINE Delay. */
vTaskDelayUntil(
&xLastWakeTime, // Time At Which Task Was Last Unblocked
MSG_DEADLINE // Cycle Time Period
);
If the message isn't generated and transmitted by MSG_DEADLINE
, the semaphore is not available to be taken by the SCI RX task. This is highly problematic as ABORT("RX Task Missed a Deadline!\r\n");
is called and code execution is effectively placed in an infinite loop.
If the SCI TX task runs in the correct period, it generates an output to the serial terminal as shown below.
This SCI Loopback program was written as a lab for a Firmware Bootcamp for the UBC Orbit Satellite Design Team.