// Include FreeRTOS headers. #include "FreeRTOSConfig.h" #include "FreeRTOS.h" #include "portmacro.h" #include "task.h" #include "timers.h" #include "queue.h" #include "stm32l432xx.h" #include "lib_ee152.h" #include //***************************************************** // Our big-hammer debugging facility. It works with macros in FreeRTOSConfig.h. // - At each 1ms SysTick interrupt, the macro traceTASK_INCREMENT_TICK() appends // the number 4 to g_debug_notes[]. // - Whenever we context switch to task_writer1() -- whether it's triggered by a // SysTick interrupt, by a task calling vTaskDelay(), or whatever -- we append // the number 1 to g_debug_notes[] // - Similarly, switch to task_writer2() appends 2, switching to // task_UART_daemon() appends 3, and switching to the idle task appends 0. // - Then, at the end of the run, task_UART_daemon() dump g_debug_notes[]: // printing out "*" for each SysTick interrupt, "1" for task_writer1, "2" for // task_writer2, "D" for the UART daemon and "I" for the idle task. //***************************************************** unsigned char g_debug_notes [MAX_DEBUG_NOTES]; unsigned g_n_debug_notes; // Each writer task writes its variable to say that its done: task_UART_daemon // monitors these globals and, when both writers are done, prints out the debug // info. static volatile int g_writer1_done=0; static volatile int g_writer2_done=0; // The global queue -- 32 bytes (not 32 ints). QueueHandle_t g_queue; #define QUEUE_SIZE 32 // Error-handling routine. Why bother with an error handler that doesn't print // its message? Well, it's hard to print an error message when we're trying to // debug the UART! Instead, we can turn on the green LED to show that there was // some kind of error (though remember we're also using the green LED to // indicate that both writers are done). // Or, we can run in the debugger. Then, either set a breakpoint on error_152() // or just manually stop the program when it hangs. Either way, the debugger // will show us that we're in error_152(), who called it, and what 'message' is. void error_152 (char *message) { //digitalWrite (D13, 1); // Turn the green LED on if desired. while(1); } //***************************************************** // This part of the file has our core routines for writing to the screen. // - The main shared data structure is a FreeRTOS queue g_queue. // - Task_UART_daemon() continually loops: if g_queue has a character in it, // then print that character using UART_write_byte_nonB(). If g_queue is // empty, then sleep for a tick and check again. // - How do characters get into the queue? The two writer tasks (which will come // later) each call serial_write_special(string). Serial_write_special() takes // a string and writes it into g_queue. Internally, serial_write_special() // keeps checking if g_queue has any space; if so, then we write one char into // g_queue; else it sleeps for a tick and tries again. // Note that serial_write_special() is a function and not a task. It doesn't // return until it has found space to write its entire string into g_queue. // But there can easily be two instance of serial_write() active, that can be // swapped in and out by the scheduler. // - Note that serial_write_special() is a special version of the usual Arduino // serial_write(), just for this lab. It's special because it works with // g_queue. // // As opposed to lab3_main_v1.c, these versions of serial_write_special() and // task_UART_daemon() don't ever put themselves to sleep with vTaskDelay(1). // Instead, they just use blocking queue writes and reads to accomplish the // same goal. // This way is simpler to code and more in the spirit of queue -- and // interestingly, has a very different end result. //***************************************************** static void serial_write_special (const char *user_buf) { // Dump into the circular buffer. for (const char *cp=user_buf; *cp; ++cp) { if (xQueueSend (g_queue,cp,5000) != pdPASS) error_152 ("Queue write failed"); } } static void task_UART_daemon (void *pvParameters) { void UART_write_byte(USART_TypeDef *USARTx, char data); vTaskSetApplicationTaskTag (NULL, (void *)3); char ch; while (!g_writer1_done || !g_writer2_done) { // Returns an error condition if the read times out. if (xQueueReceive (g_queue, &ch, 5) == pdPASS) //error_152 ("Queue read timed out"); UART_write_byte (USART2, ch); } // Both writers are done. Print the accumulated debug info and then spin. digitalWrite (D13, 1); // Turn the green LED on. UART_write_byte (USART2, '\r'); UART_write_byte (USART2, '\n'); for (int i=0; i