This posting will talk about RTOS & FreeRTOS and guide how to port it to W7500.
RTOS
A real-time operating system or RTOS, abbreviation of Read Time Operating System, is literally an operating system for a real time application programming. It especially designed to focus on managing CPU time which is one of operating systems. RTOS make programmers possible to control higher number of priority processes. Priority of operation program might be higher than priority of system program. By minimizing critical region of system code, it can mange handling an application program request on time.
Main point of RTOS is how well you can maintain the duration time of handling application program task. The range of process time fluctuation is called Jitter. It is separated as hard and soft RTOS and former one has narrow Jitter than the latter one.
Link : https://ko.wikipedia.org/wiki/%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C
FreeRTOS
FreeRTOS is one of open source RTOS that is designed to have all-in-one, simplicity, portability, conciseness. Price of core source code of operating system is under 4,000 and most of codes are built with C language which makes it very portable. It is now ported to 8 bit, 16 bit, 32 bit process such as 8051, AVR, PIC18, H8, MSP430, HCS12, PIC24, ARM Cortex-M3, ARM7, ARM9, AVR32, ColdFire, x86.
FreeRTOS core provides following functions.
- Mutlitasking
- Select between Preemptive & Non-preemptive
- No limit on Task #
- Possible to create task in a dynamical way
- No limit on Priority #
- Possible to assign same priority with more than 2 task (Round Robin Scheduling (RR) in this situation)
- Co-routine
- Message queue
- Semaphore
- Memory Management
FreeRTOS uses modified GPL license policy. A difference compare to the former GPL license is that the work operating by linking with FreeRTOS has no obligation to open in public. The advantage of it is that it is possible when exclusive rights of the work is need for the industrial purposes.
Link : http://www.ubinos.org/mediawiki/index.php/FreeRTOS_%EC%86%8C%EA%B0%9C
Download Free RTOS
Go to the FreeRTOS to download info, document, and source code related to FreeRTO
Folder Structure
Download FreeRTOS to check Free-RTOS-Plus folder and FreeRTOS folder. Visit http://www.freertos.org/a00017.html for the further information.
FreeRTOS folder consists of Demo and Source folders and Demo folder has example demo project exists on the basis of FreeRTOS kernel.
Source folder has FreeRTOS Kenel implemented files and Portable folder has process and IDE FreeRTOS Port files.
This posting will use port.c and portmacro.h in the portable/RVDS/ARM_CM0 folder to operate FreeRTOS using one of CortexM0 series called W7500. Additionally, Add files in the portable/MemMang folder to the poject for Memoney Management of FreeRTOS. Visit http://www.freertos.org/a00111.html for the further information about Memory Management.
Porting a Free RTOS on WIZwiki-W7500ECO
I will explain how to port Free RTOS using WIZwiki-W7500ECO board which is one of W7500 platforms. Examples are about Blinking LED1 of WIZwiki-W7500ECO board every 20ms.
Step 1 : Download W7500 Library
Go to the https://github.com/Wiznet/W7500, then download W7500 Library Code.
Step 2 : Create Keil Project for W7500
Create a folder in the directory of W7500-master\W7500x_Library_Examples\Projects\Peripheral_Examples for the project, then copy a proper file. In this posting, files in the GPIO\Blink_LED were copied into OS\FreeRTOS after making an OS\FreeRTOS folder. Originally it is general way to make a new project, but this method was used in order to use set values of the project made by WIZnet to create the project.
Open _GPIO_Blink_LED.uvproj of OS\FreeRTOS\MDK folder to check an example of turing On/Off the GPIO of W7500 seen as the following picture.
[How to set Include Path]Since this example’s GPIO/Blink_LED and folder Depth is same, set value do not need to be changed. However, if folder Depth is different from original projects, compile doe not work. In this case, you are able to fix the probrem by setting Include Path using following method.
Select [Project]-[Options for Target], then set directory of following folder in [C/C++] tab’s Include Paths
- \Libraries\CMSIS\Device\WIZnet\W7500\Include
- \Libraries\W7500x_stdPeriph_Driver\inc
- \Libraries\CMSIS\Include
- ..\
Step 3 : Porting FreeRTOS
Copy downloaded FreeRTOS file from the Step 1 in the OS\FreeRTOS\ folder from Step 2 seen as following picture, then delete files other than files seen in the following picture.
After completing the above processes, make a FreeRTOS group on Keil project and add following files.
- OS\FreeRTOS\FreeRTOS\croutine.c
- OS\FreeRTOS\FreeRTOS\list.c
- OS\FreeRTOS\FreeRTOS\queue.c
- OS\FreeRTOS\FreeRTOS\tasks.c
- OS\FreeRTOS\FreeRTOS\timers.c
- OS\FreeRTOS\FreeRTOS\portable\MemMang\heap_2.c
- OS\FreeRTOS\FreeRTOS\portable\RVDS\ARM_CM0\port.c
Create FreeRTOSConfig.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H /*———————————————————– * Application specific definitions. * * These definitions should be adjusted for your particular hardware and * application requirements. * * THESE PARAMETERS ARE DESCRIBED WITHIN THE ‘CONFIGURATION’ SECTION OF THE * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. * * See http://www.freertos.org/a00110.html. *———————————————————-*/ /* Prevent C code being included by the IAR assembler. */ #ifndef __IASMARM__ #include <stdint.h> extern uint32_t SystemCoreClock; #endif #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( SystemCoreClock ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) #define configMAX_PRIORITIES ( 5 ) #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 10 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 16 ) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1 /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) /* Software timer definitions. */ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY ( 2 ) #define configTIMER_QUEUE_LENGTH 5 #define configTIMER_TASK_STACK_DEPTH ( 80 ) /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_eTaskGetState 1 /* Normal assert() semantics without relying on the provision of an assert.h header file. */ #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS standard names – or at least those used in the unmodified vector table. */ #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler /* Bump up the priority of recmuCONTROLLING_TASK_PRIORITY to prevent false positive errors being reported considering the priority of other tasks in the system. */ #define recmuCONTROLLING_TASK_PRIORITY ( configMAX_PRIORITIES – 1 ) #endif |
Create blink_led_task.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | /* Kernel includes. */ #include “FreeRTOS.h” #include “task.h” #include “queue.h” #include “W7500x_gpio.h” /* Priorities at which the tasks are created. */ #define mainQUEUE_RECEIVE_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) #define mainQUEUE_SEND_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) /* The rate at which data is sent to the queue. The 200ms value is converted to ticks using the portTICK_PERIOD_MS constant. */ #define mainQUEUE_SEND_FREQUENCY_MS ( 200 / portTICK_PERIOD_MS ) /* The number of items the queue can hold. This is 1 as the receive task will remove items as they are added, meaning the send task should always find the queue empty. */ #define mainQUEUE_LENGTH ( 1 ) /* Values passed to the two tasks just to check the task parameter functionality. */ #define mainQUEUE_SEND_PARAMETER ( 0x1111UL ) #define mainQUEUE_RECEIVE_PARAMETER ( 0x22UL ) /* The number of the LED that is toggled. */ #define mainLED_TO_TOGGLE ( 0 ) /* * The tasks as described in the comments at the top of this file. */ static void prvQueueReceiveTask( void *pvParameters ); static void prvQueueSendTask( void *pvParameters ); /* * Called by main() to create the simply blinky style application if * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 1. */ void main_blinky( void ); void vParTestToggleLED( unsigned long ulLED ); /* * The hardware only has a single LED. Simply toggle it. */ extern void vMainToggleLED( void ); /* The queue used by both tasks. */ static QueueHandle_t xQueue = NULL; void main_blinky( void ) { /* Create the queue. */ xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) ); if( xQueue != NULL ) { /* Start the two tasks as described in the comments at the top of this file. */ xTaskCreate( prvQueueReceiveTask, /* The function that implements the task. */ “Rx”, /* The text name assigned to the task – for debug only as it is not used by the kernel. */ configMINIMAL_STACK_SIZE, /* The size of the stack to allocate to the task. */ ( void * ) mainQUEUE_RECEIVE_PARAMETER, /* The parameter passed to the task – just to check the functionality. */ mainQUEUE_RECEIVE_TASK_PRIORITY, /* The priority assigned to the task. */ NULL ); /* The task handle is not required, so NULL is passed. */ xTaskCreate( prvQueueSendTask, “TX”, configMINIMAL_STACK_SIZE, ( void * ) mainQUEUE_SEND_PARAMETER, mainQUEUE_SEND_TASK_PRIORITY, NULL ); /* Start the tasks and timer running. */ vTaskStartScheduler(); } /* If all is well, the scheduler will now be running, and the following line will never be reached. If the following line does execute, then there was insufficient FreeRTOS heap memory available for the idle and/or timer tasks to be created. See the memory management section on the FreeRTOS web site for more details. */ for( ;; ); } static void prvQueueSendTask( void *pvParameters ) { TickType_t xNextWakeTime; const unsigned long ulValueToSend = 100UL; /* Check the task parameter is as expected. */ configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_SEND_PARAMETER ); /* Initialise xNextWakeTime – this only needs to be done once. */ xNextWakeTime = xTaskGetTickCount(); for( ;; ) { /* Place this task in the blocked state until it is time to run again. The block time is specified in ticks, the constant used converts ticks to ms. While in the Blocked state this task will not consume any CPU time. */ vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS ); /* Send to the queue – causing the queue receive task to unblock and toggle the LED. 0 is used as the block time so the sending operation will not block – it shouldn’t need to block as the queue should always be empty at this point in the code. */ xQueueSend( xQueue, &ulValueToSend, 0U ); } } static void prvQueueReceiveTask( void *pvParameters ) { unsigned long ulReceivedValue; /* Check the task parameter is as expected. */ configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_RECEIVE_PARAMETER ); for( ;; ) { /* Wait until something arrives in the queue – this task will block indefinitely provided INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. */ xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY ); /* To get here something must have been received from the queue, but is it the expected value? If it is, toggle the LED. */ if( ulReceivedValue == 100UL ) { vParTestToggleLED( mainLED_TO_TOGGLE ); ulReceivedValue = 0U; } } } void vParTestToggleLED( unsigned long ulLED ) { if( GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == Bit_SET ) GPIO_ResetBits(GPIOA, GPIO_Pin_1); else GPIO_SetBits(GPIOA, GPIO_Pin_1); } |
Modified main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | /** ****************************************************************************** * @file Uart/Printf/main.c * @author IOP Team * @version V1.0.0 * @date 01-May-2015 * @brief Main program body ****************************************************************************** * @attention * * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE * TIME. AS A RESULT, WIZnet SHALL NOT BE HELD LIABLE FOR ANY * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. * * <h2><center>© COPYRIGHT 2015 WIZnet Co.,Ltd.</center></h2> ****************************************************************************** */ /* Includes ——————————————————————*/ #include <stdio.h> #include “W7500x_uart.h” #include “W7500x_gpio.h” #include “FreeRTOS.h” #include “task.h” #include “semphr.h” #include “queue.h” extern void main_blinky( void ); /* * Initialise the LED ports, and create a timer that periodically toggles an LED * just to provide a visual indication that the program is running. */ int main() { GPIO_InitTypeDef GPIO_InitDef; SystemInit(); SystemCoreClockUpdate(); GPIO_InitDef.GPIO_Pin = ( GPIO_Pin_1 | GPIO_Pin_2) ; // Set to Pin_1 (LED1) & Pin_2 (LED2) GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT; // Set to Mode Output GPIO_Init(GPIOA, &GPIO_InitDef); PAD_AFConfig(PAD_PA,(GPIO_Pin_1|GPIO_Pin_2), PAD_AF1); // PAD Config – LED used 2nd Function main_blinky(); for(;;); } void vApplicationMallocFailedHook( void ) { /* vApplicationMallocFailedHook() will only be called if configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h. It is a hook function that will get called if a call to pvPortMalloc() fails. pvPortMalloc() is called internally by the kernel whenever a task, queue, timer or semaphore is created. It is also called by various parts of the demo application. If heap_1.c or heap_2.c are used, then the size of the heap available to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can be used to query the size of free heap space that remains (although it does not provide information on how the remaining heap might be fragmented). */ taskDISABLE_INTERRUPTS(); for( ;; ); } /*———————————————————–*/ void vApplicationIdleHook( void ) { /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle task. It is essential that code added to this hook function never attempts to block in any way (for example, call xQueueReceive() with a block time specified, or call vTaskDelay()). If the application makes use of the vTaskDelete() API function (as this demo application does) then it is also important that vApplicationIdleHook() is permitted to return to its calling function, because it is the responsibility of the idle task to clean up memory allocated by the kernel to any task that has since been deleted. */ } /*———————————————————–*/ void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName ) { ( void ) pcTaskName; ( void ) pxTask; /* Run time stack overflow checking is performed if configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is called if a stack overflow is detected. */ taskDISABLE_INTERRUPTS(); for( ;; ); } /*———————————————————–*/ void vApplicationTickHook( void ) { /* This function will be called by each tick interrupt if configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h. User code can be added here, but the tick hook is called from an interrupt context, so code must not attempt to block, and only the interrupt safe FreeRTOS API functions can be used (those that end in FromISR()). The code in this tick hook implementation is for demonstration only – it has no real purpose. It just gives a semaphore every 50ms. The semaphore unblocks a task that then toggles an LED. Additionally, the call to vQueueSetAccessQueueSetFromISR() is part of the “standard demo tasks” functionality. */ /* The semaphore and associated task are not created when the simple blinky demo is used. */ } /*———————————————————–*/ |
Fixed Error
1 2 | Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and w7500x_it.o). Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and w7500x_it.o). |
Above Error occured due to the duplication of PendSV_Handler and SysTick_Handler formula. To solve the problem, W7500x_it.c is changed to following one.
1 2 3 4 | __weak void PendSV_Handler(void) {} __weak void SysTick_Handler(void) {} |
Step 4 : Running an example
After RTOS porting, check if example code is working fine.
출처 : Life4IoT
COMMENTS