]> begriffs open source - cmsis-freertos/blob - Demo/Safer_Interrupts_M33F_NXP_LPC55S69_MCUXpresso/Application/main.c
Update README.md - branch main is now the base branch
[cmsis-freertos] / Demo / Safer_Interrupts_M33F_NXP_LPC55S69_MCUXpresso / Application / main.c
1 /*
2  * FreeRTOS V202111.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * https://www.FreeRTOS.org
23  * https://github.com/FreeRTOS
24  *
25  */
26
27 /*
28  * This demo demonstrates the best practice of deferring majority of the
29  * interrupt processing work from IRQ handlers to an unprivileged task. IRQ
30  * handlers execute in privileged mode and therefore, have access to all the
31  * memories. Keeping the IRQ handlers small and deferring most of the work to
32  * unprivileged task provides better security.
33  *
34  * This demo creates a queue, known as interrupt queue. IRQ handlers post
35  * requests to this queue which are later served by an unprivileged interrupt
36  * handler task.
37  *
38  *                   +-------------------------+
39  *        +--------->+    Interrupt Queue      +------------+
40  *        |          +-------------------------+            |
41  *        |                                                 | Fetch and Serve
42  *   Post |                                                 |
43  *        |                                                 v
44  *        |                                          +------+--------+
45  * +------+--------+                                 |               |
46  * |  IRQ Handler  |                                 |               |
47  * |  (Privileged) |                                 |   Interrupt   |
48  * +---------------+                                 |    Handler    |
49  *                                                   |     Task      |
50  *                                                   | (Unprivileged)|
51  *                                                   |               |
52  *                                                   |               |
53  *                                                   +---------------+
54  *
55  * This demo show-cases the following 2 demos:
56  * 1. LED Demo -  The LED demo creates a task which periodically toggles the on
57  *                board RGB LED. Whenever user presses the USER button, the
58  *                user button pressed IRQ handler changes the color of the LED.
59  *                The user button pressed IRQ handler posts a request to the
60  *                interrupt queue and task of changing the LED color is deferred
61  *                to the unprivileged handler.
62  * 2. UART Demo - The UART demo creates a task which prints a command menu on
63  *                the serial console and then waits for the user input. Whenever
64  *                user enters any input on the serial console to run a command,
65  *                the UART received IRQ handler handles it and prints the
66  *                appropriate response on the serial console. The UART received
67  *                IRQ handler posts a request to the interrupt queue and task of
68  *                handling the command and providing the response is deferred
69  *                to the unprivileged handler.
70  */
71
72 /* FreeRTOS kernel includes. */
73 #include "FreeRTOS.h"
74 #include "task.h"
75 #include "queue.h"
76
77 /* BSP includes. */
78 #include "board.h"
79 #include "clock_config.h"
80 #include "fsl_debug_console.h"
81 #include "fsl_device_registers.h"
82 #include "fsl_inputmux.h"
83 #include "fsl_power.h"
84 #include "fsl_pint.h"
85 #include "fsl_usart.h"
86 #include "pin_mux.h"
87
88 /* Demo includes. */
89 #include "led_demo.h"
90 #include "uart_demo.h"
91 #include "interrupt_handler_task.h"
92
93 /**
94  * @brief Length of the interrupt queue.
95  */
96 #define USER_IRQ_QUEUE_LEN          8
97 #define USER_IRQ_QUEUE_BUF_SIZE     ( sizeof( UserIrqRequest_t ) * USER_IRQ_QUEUE_LEN )
98
99 /**
100  * @brief UART_MEM_START to (UART_MEM_START + UART_MEM_LEN) cover the range
101  * used by USART_WriteBlocking().
102  */
103 #define UART_MEM_START              ( USART0 )
104 #define UART_MEM_LEN                ( 3648 )
105
106 /**
107  * @brief GPIO_MEM_START to (GPIO_MEM_START + GPIO_MEM_LEN) covers the range
108  * used for setting and clearing GPIO pins.
109  */
110 #define GPIO_MEM_START              ( 0x4008E200u )
111 #define GPIO_MEM_LEN                ( 256 )
112
113 /**
114  * @brief Handle and storage for the interrupt queue shared between IRQ handlers
115  * and the interrupt handler task.
116  */
117 static QueueHandle_t xUserIrqQueueHandle;
118 static StaticQueue_t xStaticQueue;
119 static uint8_t ucQueueBuffer[ USER_IRQ_QUEUE_BUF_SIZE ];
120 /*-----------------------------------------------------------*/
121
122 /**
123  * @brief Initialize hardware.
124  */
125 static void prvInitializeHardware( void );
126
127 /**
128  * @brief Initialize user button pressed interrupt.
129  */
130 static void prvInitializeUserButtonInterrupt( void );
131
132 /**
133  * @brief Initialize UART received interrupt.
134  */
135 static void prvInitializeUartReceivedInterrupt( void );
136
137 /**
138  * @brief Handler for pin interrupt when USER button is pressed.
139  *
140  * The handler enqueues a user IRQ request for the IRQ to be further processed
141  * in the unprivileged interrupt handler.
142  */
143 static void userButtonPressedHandler( pint_pin_int_t xInterruptType, uint32_t ulMatchStatus );
144
145 /**
146  * @brief Handler for UART interrupt when UART data is received.
147  *
148  * The handler enqueues a user IRQ request for the IRQ to be further processed
149  * in the unprivileged interrupt handler.
150  */
151 void FLEXCOMM0_IRQHandler( void );
152
153 /**
154  * @brief Create the demo tasks.
155  */
156 static void prvCreateDemoTasks( void );
157 /*-----------------------------------------------------------*/
158
159 static void prvInitializeHardware( void )
160 {
161     /* Set BOD VBAT level to 1.65V. */
162     POWER_SetBodVbatLevel( kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false );
163
164     /* Attach main clock divide to FLEXCOMM0 (debug console). */
165     CLOCK_AttachClk( BOARD_DEBUG_UART_CLK_ATTACH );
166
167     /* Board initialization. */
168     BOARD_InitBootPins();
169     BOARD_InitBootClocks();
170     BOARD_InitDebugConsole();
171 }
172 /*-----------------------------------------------------------*/
173
174 static void prvInitializeUserButtonInterrupt( void )
175 {
176     /* Connect trigger sources to PINT. */
177     INPUTMUX_Init( INPUTMUX );
178     INPUTMUX_AttachSignal( INPUTMUX, kPINT_PinInt0, kINPUTMUX_GpioPort1Pin9ToPintsel );
179
180     /* Turnoff clock to inputmux to save power. Clock is only needed to make changes. */
181     INPUTMUX_Deinit( INPUTMUX );
182
183     /* Initialize PINT. */
184     PINT_Init( PINT );
185
186     /* Setup Pin Interrupt 0 for rising edge. */
187     PINT_PinInterruptConfig( PINT, kPINT_PinInt0, kPINT_PinIntEnableRiseEdge, userButtonPressedHandler );
188
189     /* Enable callback for PINT0 by Index. */
190     PINT_EnableCallbackByIndex( PINT, kPINT_PinInt0 );
191 }
192 /*-----------------------------------------------------------*/
193
194 static void prvInitializeUartReceivedInterrupt( void )
195 {
196     usart_config_t config;
197     USART_GetDefaultConfig( &( config ) );
198     config.enableTx = true;
199     config.enableRx = true;
200     USART_Init( USART0, &( config ), CLOCK_GetFlexCommClkFreq( 0U ) );
201
202     /* Enable RX interrupt. */
203     USART_EnableInterrupts( USART0, kUSART_RxLevelInterruptEnable | kUSART_RxErrorInterruptEnable );
204     EnableIRQ( FLEXCOMM0_IRQn );
205 }
206 /*-----------------------------------------------------------*/
207
208 static void userButtonPressedHandler( pint_pin_int_t xInterruptType, uint32_t ulMatchStatus )
209 {
210     UserIrqRequest_t xIrqRequest;
211     BaseType_t xHigherPriorityTaskWoken;
212
213     /* Silence warnings about unused variables. */
214     ( void ) xInterruptType;
215     ( void ) ulMatchStatus;
216
217     /* We have not woken a task at the start of the ISR. */
218     xHigherPriorityTaskWoken = pdFALSE;
219
220     /* Enqueue a request to user IRQ queue to be processed in the unprivileged
221      * interrupt handler. */
222     xIrqRequest.xHandlerFunction = vButtonPressedIRQHandler;
223     xIrqRequest.ulData = 0; /* Not used. */
224     xQueueSendFromISR( xUserIrqQueueHandle, &( xIrqRequest ), &( xHigherPriorityTaskWoken ) );
225
226     /* Posting the above request might have unblocked the interrupt handler task.
227      * Make sure to return to the interrupt handler task in case it was not already
228      * running. */
229     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
230 }
231 /*-----------------------------------------------------------*/
232
233 /* Override weak definition of FLEXCOMM0_IRQHandler */
234 void FLEXCOMM0_IRQHandler(void)
235 {
236     UserIrqRequest_t xIrqRequest;
237     BaseType_t xHigherPriorityTaskWoken;
238
239     /* We have not woken a task at the start of the ISR. */
240     xHigherPriorityTaskWoken = pdFALSE;
241
242     /* If new data arrived. */
243     if( ( kUSART_RxFifoNotEmptyFlag | kUSART_RxError ) & USART_GetStatusFlags( USART0 ) )
244     {
245         /* Enqueue a request to user IRQ queue to be processed in the unprivileged
246          * interrupt handler. */
247         xIrqRequest.xHandlerFunction = vUartDataReceivedIRQHandler;
248         xIrqRequest.ulData = ( uint32_t ) USART_ReadByte( USART0 );
249         xQueueSendFromISR( xUserIrqQueueHandle, &( xIrqRequest ), &( xHigherPriorityTaskWoken ) );
250     }
251
252     /* Posting the above request might have unblocked the interrupt handler task.
253      * Make sure to return to the interrupt handler task in case it was not already
254      * running. */
255     portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
256 }
257 /*-----------------------------------------------------------*/
258
259 static void prvCreateDemoTasks( void )
260 {
261     static StackType_t xInterruptHandlerTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
262     static StackType_t xLedDemoTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
263     static StackType_t xUartDemoTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
264
265     extern TaskHandle_t xUartDemoTaskHandle;
266     extern uint32_t __user_irq_shared_memory_start__[];
267     extern uint32_t __user_irq_shared_memory_end__[];
268     uint32_t ulSharedMemoryLength = ( uint32_t )__user_irq_shared_memory_end__ - ( uint32_t )__user_irq_shared_memory_start__ + 1;
269
270     /* The interrupt handler task needs access to UART memory region too as we
271      * write the response to UART from the interrupt handler in the UART demo. */
272     TaskParameters_t xInterruptHandlerTaskParameters =
273     {
274         .pvTaskCode     = vInterruptHandlerTask,
275         .pcName         = "InterruptHandlerTask",
276         .usStackDepth   = configMINIMAL_STACK_SIZE,
277         .pvParameters   = xUserIrqQueueHandle,
278         .uxPriority     = configMAX_PRIORITIES - 1, /* Run the interrupt handler task at the highest priority. */
279         .puxStackBuffer = xInterruptHandlerTaskStack,
280         .xRegions       =
281         {
282             { __user_irq_shared_memory_start__, ulSharedMemoryLength, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
283             { ( void * ) UART_MEM_START,        UART_MEM_LEN,         tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
284             { 0,                                0,                    0                                                      },
285         }
286     };
287     TaskParameters_t xLedDemoTaskParameters =
288     {
289         .pvTaskCode     = vLedDemoTask,
290         .pcName         = "LedDemoTask",
291         .usStackDepth   = configMINIMAL_STACK_SIZE,
292         .pvParameters   = NULL,
293         .uxPriority     = tskIDLE_PRIORITY,
294         .puxStackBuffer = xLedDemoTaskStack,
295         .xRegions       =
296         {
297             { __user_irq_shared_memory_start__, ulSharedMemoryLength, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
298             { ( void * ) GPIO_MEM_START,        GPIO_MEM_LEN,         tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
299             { 0,                                0,                    0                                                      },
300         }
301     };
302     TaskParameters_t xUartDemoTaskParameters =
303     {
304         .pvTaskCode     = vUartDemoTask,
305         .pcName         = "UartDemoTask",
306         .usStackDepth   = configMINIMAL_STACK_SIZE,
307         .pvParameters   = NULL,
308         .uxPriority     = tskIDLE_PRIORITY,
309         .puxStackBuffer = xUartDemoTaskStack,
310         .xRegions       =
311         {
312             { __user_irq_shared_memory_start__, ulSharedMemoryLength, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
313             { ( void * ) UART_MEM_START,        UART_MEM_LEN,         tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
314             { 0,                                0,                    0                                                      },
315         }
316     };
317
318     xTaskCreateRestricted( &( xInterruptHandlerTaskParameters ), NULL );
319     xTaskCreateRestricted( &( xLedDemoTaskParameters ), NULL );
320     xTaskCreateRestricted( &( xUartDemoTaskParameters ), &( xUartDemoTaskHandle ) );
321 }
322 /*-----------------------------------------------------------*/
323
324 /**
325  * @brief Entry point.
326  */
327 int main( void )
328 {
329     /* Initialize board hardware. */
330     prvInitializeHardware();
331
332     /* Create the interrupt queue for deferring work from ISRs to the
333      * unprivileged interrupt handler task. */
334     xUserIrqQueueHandle = xQueueCreateStatic( USER_IRQ_QUEUE_LEN,
335                                               sizeof( UserIrqRequest_t ),
336                                               ucQueueBuffer,
337                                               &( xStaticQueue ) );
338
339     prvCreateDemoTasks();
340     prvInitializeUserButtonInterrupt();
341     prvInitializeUartReceivedInterrupt();
342
343     vTaskStartScheduler();
344
345     /* Should never reach here. */
346     for( ; ; );
347 }
348 /*-----------------------------------------------------------*/
349
350 /* Stack overflow hook. */
351 void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
352 {
353     /* Force an assert. */
354     configASSERT( pcTaskName == 0 );
355 }
356 /*-----------------------------------------------------------*/
357
358 /* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an
359  * implementation of vApplicationGetIdleTaskMemory() to provide the memory that
360  * is used by the Idle task. */
361 void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
362                                     StackType_t ** ppxIdleTaskStackBuffer,
363                                     uint32_t * pulIdleTaskStackSize )
364 {
365     /* If the buffers to be provided to the Idle task are declared inside this
366      * function then they must be declared static - otherwise they will be
367      * allocated on the stack and so not exists after this function exits. */
368     static StaticTask_t xIdleTaskTCB;
369     static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
370
371     /* Pass out a pointer to the StaticTask_t structure in which the Idle
372      * task's state will be stored. */
373     *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
374
375     /* Pass out the array that will be used as the Idle task's stack. */
376     *ppxIdleTaskStackBuffer = uxIdleTaskStack;
377
378     /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
379      * Note that, as the array is necessarily of type StackType_t,
380      * configMINIMAL_STACK_SIZE is specified in words, not bytes. */
381     *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
382 }
383 /*-----------------------------------------------------------*/
384
385 /* configUSE_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
386  * application must provide an implementation of vApplicationGetTimerTaskMemory()
387  * to provide the memory that is used by the Timer service task. */
388 void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
389                                      StackType_t ** ppxTimerTaskStackBuffer,
390                                      uint32_t * pulTimerTaskStackSize )
391 {
392     /* If the buffers to be provided to the Timer task are declared inside this
393      * function then they must be declared static - otherwise they will be
394      * allocated on the stack and so not exists after this function exits. */
395     static StaticTask_t xTimerTaskTCB;
396     static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ] __attribute__( ( aligned( 32 ) ) );
397
398     /* Pass out a pointer to the StaticTask_t structure in which the Timer
399      * task's state will be stored. */
400     *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
401
402     /* Pass out the array that will be used as the Timer task's stack. */
403     *ppxTimerTaskStackBuffer = uxTimerTaskStack;
404
405     /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
406      * Note that, as the array is necessarily of type StackType_t,
407      * configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
408     *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
409 }
410 /*-----------------------------------------------------------*/