Several years ago, when single-chip microcontrollers were relatively simple systems, designing software was basically nothing but direct reference to microcontroller peripheral systems. It was being made then by the use of background/foreground programs written in C language or in assembler.Modern microcontrollers are far more complicated than the systems one could come across a couple of years ago. These are equipped in vast memory, extensive and complex communication interface or graphic systems making possible to attach colour LCD display. Common access to the Internet of the modern end users of electronic devices as well as their attachment to colour displays enforces embedded software creators to take different approach.Due to the fact that development of embedded software in the traditional way consumes both time and resources and taking into consideration the short life of product as well as a necessity to launch it onto the market fast, it is unacceptable nowadays. In order to meet present market expectations one should use the internal libraries while developing software for microcontrollers. Since it significantly simplifies the programme structure, using operational system intended for microcontrollers may be beneficial as well.
Almost a decade ago, when the operating systems were not as popular as they are today, in Electronics in Practice, I presented a series of my own articles about operating system ISIX. Much has changed since then. Constant development of hardware, libraries, tool software and ISIX system itself caused new system to develop new, III version which does not have much in common with the initial version but assumptions only. Tool software such as editors as well as compilators significantly developed, so that the information included in the last cycle became outdated.For the above reasons I will present basic course consisting examples of use new ISIX system version in the magazine. Since I received some signs from readers throughout last series of articles, it made me prepare all the examples performed within this course for popular and cheap ST default sets, with no necessity to solder or buying programmers etc. What we need is STM32-DISCOVERY, USB cable which would enable to connect the set to computer plus Serial USB adapter enabling attaching microcontroller serial port to PC computer.The adapter will be needed to the set consisting STM32F411-DISCO with integrating programmer STLINK in 2.0 version.The remaining sets equipped in STLINK2-1 programmer do not require attaching external converter.
The following sets will be used in the examples:
- STM411-DISCO + serial adapter on USB in standard 3,3V
The first set will serve as the basic one to be used in performing basic examples, while in more advanced examples (presenting graphic libraries or net connections) 2)&3) sets would be used.
Use of operating system for the embedded software is far more beneficial compared to classical approach which is based on main loop and interrupt procedures:
- Functional blocks may be divided for tasks – that enables the code to be better organized without the necessity of developing state machines
- Multitasking makes possible to perform various independent tasks pseudo concurrently
- Interrupt procedures, due to the possibility of tasks division may be reduced to essential minimum while all the additional activities would be redirected to separate threads. As a consequence the system reacts much faster to interrupt requests
- The mechanisms of inter process communication (IPC) make possible to transfer information between threads easily
- Integrated mechanisms and API allow us to focus on designing application instead of managing processes, developing communication mechanisms and state machines with every project from scratch what significantly saves the time which would be devoted for developing application
Just to remind you, the main motivation behind ISIX development was its simplicity and modular structure in order to ensure using it both in microcontrollers with 4kB RAM memory as well as in big systems with 512KB memory and more. The system has also dense and simple API to ensure that user does not have to make an effort to learn the complex system service as well as wonder which mechanism he should use.The minimum system requirements needed to start III version of ISIX operating system are the following:
ARM Cortex M0/M3/M4/M7
ISIX system design
Operating system has a modular design which allows to use only some parts of the system, as required. The modules had been divided into components, which can be used independently.
Kernel of operating system ISIX-RTOS
Building system scripts WAF specific for ISIX
Energy meter operation library
Main energy library
FAT filesystem library
Mini library GUI managing LCD displays
GSM modem library
Peripheral systems and initialization universal library
TCP/IP stack (LWIP)
SSL/TLS stack (EmbeddedTLS)
USB host and DEVICE USB stack
ISIX system is based on memory image design FLASH microcontroller and is being prepared on the basis of tool purposed to design projects called WAF https://www.waf.io/
As compared to GNU-MAKE which had been used in previous versions of the system, this tool has several advantages:
It is fully portable and works both in Linux, Windows and OS-X systems without the necessity of making tricks in the scripts
It makes parallel design possible and it includes the standard rules for developing applications for microcontrollers
It supports automatic cache for the objects without necessity to build the code from scratch. It allows to save some time.
Owing to Python language it allows to design extensions and scripts easily. Based on meta – data included in configuration file XML, consisting information about other microcontroller resources, linker scripts can be generated automatically
By the use of the templates (glob) which significantly facilitate designing scripts and managing them, it allows for smart source files finding
As in GNU-MAKE case particular designing scripts were included in Makefile file, in case of WAF tools the files describing the design process are called wscript and since these are valid with Python language in terms of syntax, we do not have to learn another programming language.
The process of building application by the use of this system is based on three steps:
- project configuration by waf configure command, where we can pass additional configuration parametres to the project by the use of right arguments eg. frequency of quartz crystal or choice of additional functionalities
- project design by the use of waf or waf build command
- downloading project to the targeted plate by the use of waf program command.
The kernel of ISIX system
The kernel of the ISIX system had been written in C language, with GNU extension in C11 dialect and its source code is in libsix catalogue. Despite the fact that the basic API uses C language, all the libraries own additional classes enabling use API system in C++ language.
API of ISIX system had been divided into following function modules:
We can specify here system calls responsible for managing memory, managing processes (tasks), communication and inter process synchronisation, interrupt service and managing cache memory of processors as well as functions being in charge of the whole system control.
Unfortunately microcontrollers with Cortex M core do not have the unit of managing memory and some systems have simplified system of memory protection MPU only, thus operating system and kernel share mutual address space. On one hand, since it enables inter process communication this is beneficial. On the other hand, this is a disadvantage because the process which works incorrectly may damage both core of the system as well as other processes. ISIX operating system can use functionality of MPU unit and if such unit is available, it protects kernel and processes before overwriting pile of data as well as prevents access to forbidden parts of code.API of managing memory is used to allocation of memory which is on the heap and may be used both by controllers as well as user’s processes due to sharing of the address space.
System calls related to memory service are presented here:
- isix::alloc Allocates n byte size memory area
- isix::free Releases allocated memory area
- isix::realloc Changes size of allocated earlier memory area
- isix::heap_stats Returns statistics on free memory and fragmentation
- isix::mempool_create Develops new N block pool of memory with fixed size
- isix::mempool_destroy Removes pool of memory blocks by releasing resources
- isix::mempool_alloc Allocates memory block from the pool
- isix::mempool_free Returns memory block to the pool
alloc/free/realloc functions are intended for allocation and releasing memory on the heap.
The way these functions work is nondeterministic, thus they should not be used in interrupt procedures. Managing heap area in ISIX system is carried out by innovative algorithm TSLF (Two Levels Segregated Fit Memory) dedicated for operating systems in real time, which compared to classical algorithm of free blocks list provides more deterministic time of reply and significantly decreases time of fragmentation. This is important due to the limited resources and missing MMU unit.The functions of memory allocation mentioned above are being used by the standard library of C language so in order to ensure compatibility rather standard functions malloc/free and new/delete should be used.Mempool_family task is to allocate and to release permanent memory blocks and calling this family is separate pool.
Allocation of permanent memory blocks is particularly useful in controllers, which transfer data by packages of constant size e.g. ethernet frame or USB frame.
As mempool_alloc, and mempool_free functions express computational complexity class O(1), these can be used in procedures of interrupt service.
Managing processes in ISIX is being performed by the following basic set of functions.
- ostask_t Handle identifying process/task
- isix::task_create Creates new process/task
- isix::task_kill Cancels process/task by releasing assets
- isix::task_change_prio Changes default priority of task/process
- isix::task_get_priority Gets default priority of task/process
- isix::task_get_task_inherited_priority Gets real (inherited) priority of task/process
- isix::task_self Gets handle of current task/process
- isix::task_suspend Powers down defined process/task until resume function calling
- isix::task_resume Reawakens defined process/task powered down by suspend calling
- isix::task_wait_for Powers down task until another task is finished
- isix::wait_ms Powers down thread for given period of time
- isix::yield Expatriates current process by starting planner
- isix::free_stack_space Returns volume of free memory of stack for given task/process
- isix::get_task_state Returns state of the process
- isix::task_ref Returns new reference for the task/process
- isix::task_unref Removes reference from the task/process
Functions responsible for managing processes had been divided into a few smaller groups.
The callings responsible for creating and deleting tasks make the first group.
Once creating the process we have to provide the following as operands: indicator to function performing given thread, operand submitted to the thread while its start, volume assigned to pile and priority of thread. As the processor does not have unit of managing memory, the volume of the pile must be known already at the stage of task creation. All the processes work on the common address space and there is no possibility to map additional memory to the thread space on demand, as it happens usually in the systems with MMU.
Next group of calling enables managing thread priorities.
When it comes to managing priorities it should be mentioned that ISIX system has by default 16 thread priorities in disposal, in range 0-15 whereas 0 is the numeric value for the highest priority and 15 symbolizes the lowest priority.
CONFIG_ISIX_NUMBER_OF_PRIORITIES. The number of available plot priorities may be changed in the system during compilation through change of definition CONFIG_ISIX_NUMBER_OF_PRIORITIES.
ISIX has mechanism of inheriting priorities which prevents their prevention.
Additional get_task_inherited_priority() function allows to find out with what priority given process had been graded. However get_task_priority() function ensures downloading assigned by default priority to the process/task.
Functions of process management, which enable shutting processes down, restarting tasks as well as shutting processes down until another process is finished.
The group of diagnostic callings makes possible to check amount of space left on the pile as well as the condition of the process at a particular moment. In ISIX every single task may take the following states:
- READY – process ready to do and awaiting for grading
- RUNNING – process being executed
- CREATED – process had been created, but it is not being performed yet
- SLEEPING – process is stand by and waiting for wakeup
- WTSEM – process waiting for semaphore
- ZOMBIE – process zombie, awaiting for destroying and assets release
- WTEVT – process waiting for the incident
- SUSPEND – stand by process by task_suspend
- WTMTX – process waiting for mutex
- WTCOND – process waiting for conditional variable
- WTEXIT – process waiting for another process to be finished
The current state of the thread is used by scoring algorithm in order to qualify tasks to perform. It can be also used as a diagnostic test while starting software.
It is worth to mention here about C++ interface made to develop new task processes. This interface enables to develop code of the task both from basic function, lambda function as well as any method being a compound of class. Creating new thread (task), using this mechanism directly in class constructor may look as follows:
: m_thr( isix::thread_create(std::bind(&base_task_tests::thread,std::ref(*this))))
In the above mentioned example, in the class constructor plot is created, which will constitute class method task_testscalled thread.
The block of system callings IPC responsible for synchronisation and data exchange between processes as well interrupt procedures is significant system interface. In ISIX we dispose few popular synchronisation mechanisms
Semaphores are the basic mechanisms enabling synchronisation of access to resources where given resource is divided through a limited number of users. By the use of semaphores application may control the maximum number of open files. Typical semaphore is implemented as an integer type number, set values 0 to maximum value. It can be, in particular, semaphore of binary type with values from 0 and 1 range. Semaphore associated with given resource is initially set for value of available resources given type. The process willing to refer to given resource must check the value of the semaphore connected with this resource. Positive value means that given resource is available.The process reduces semaphore value before it starts using given resources and 0 value means that are no free resources and the process must wait for resource release by another process occupying resource. Once resource is released, the semaphore value is being increased and the system notifies awaiting process.In ISIX it is possible to create counting semaphores with maximum value defined as well as binary semaphore be setting upper limit for semaphore equal 1
Semaphores related API in ISIX look as follows:
- isix::ossem_t Data type defining handle for the semaphore
- isix::sem_create_limited creates new semaphore with initial value N and maximum value M
- isix::sem_create Creates new semaphore with initial value N
- isix::sem_wait Reduces semaphore’s counter and presumably puts the task to Sleep while waiting for the semaphore
- isix::sem_signal Picks semaphore by waking awaiting task or by increasing semaphore’s counter
- isix::sem_signal_isr Picks semaphore from interrupt level by waking awaiting task up or by increasing semaphore’s counter
- isix::sem_reset Sets semaphore on requested value waking up all the tasks
- isix::sem_getval Returns the current semaphore’s value
- isix::sem_destroy Cancels semaphore and releases resources
There is a set of functions enabling work with semaphores.
At ISR interrupt procedures level semaphores may be used to notification of processes/tasks waiting for some incidents from peripheral circuits. It allows to redirect more time consuming tasks from interrupt procedures to tasks/processes significantly increasing responsiveness of operating system. In order to get notifications from interrupt procedures using special functions with sufix_isr is recommended.
MUTEX originates from english term mutually exclusive. It is a special binary semaphore type, equipped in mechanism preventing priority inversion. It can be unlocked only by the process which put the lock on it. Error appears whenever another process tries to unlock it. Protection of given resource is the perfect situation when it comes to use mutexes, eg. when some operation performed by one process/task cannot be interrupted by another process. Inversion of priorities is not beneficial, as in given moment it performs different task than the one that should be performed according to algorithm scheduling rules. Priorities inheritance which is about increasing priorities of awaiting tasks to the highest priority from all the priorities awaiting for these resources is used as a countermeasure. There are separate, mutex related API in ISIX:
- osmtx_t Type of data defining handle for mutex
- isix::mutex_create Creates new mutex
- isix::mutex_lock Locks the mutex
- isix::mutex_unlock Unlocks mutex
- isix::mutex_unlock_all Unlocks mutex waking all the waiting processes up
- isix::mutex_destroy Removes mutex releasing resources
The complete set of functions needed to work with mutexes is here in disposal. There’s also mutex_unlock_all calling which releases a mutex, but it also awakens not only one but all the processes waiting for resources. As one can notice function with sufix _isr is missing in the description and it is because functions connected with semaphores should not be called from interrupt procedures. In terms of interrupts only semaphores may be used. It is because the recalculation and inheritance algorithm is complex computationally and is not appropriate to call for in from interrupt context.
FIFO queues are mechanisms convenient to interprocess communication and may be used to transfer data between processes.The messages queue in ISIX system aims to transfer messages of fixed-size and while this queue is formed it is required to define its maximum size. The way the queue works is presented below:
Picture below presents maximum 7 elements size queue, which consists of 5 elements. Thread#1 records particular elements from queue by the system call fifo_read(). Each reading function call causes returning piece of data to read or shutting the process down until the thread#1 will send new data to read.Thread#1 recording data may enter into standby mode, if there’s no space in the queue, until thread#2 reads data and free up space in the queue.FIFO queue service is performed by the following API system:
- osfifo_t Data handle type representing FIFO queue
- isix::fifo_create Creates new queue with maximum M size N elements
- isix::fifo_create_ex Creates new queue with maximum M size N elements
- isix::fifo_write Writes file to FIFO queue
- isix::fifo_write_isr Writes file to FIFO within interrupt
- isix::fifo_read Writes filefrom FIFO queue
- isix::fifo_read_isr Writes file from FIFO queue
- isix::fifo_count Returns number of elements placed in queue
- isix::fifo_destroy Removes FIFO queue and releases occupied resources
Functions with_isr sufix are unblocking and may be recalled within the interrupt. If there is no free space or data to read, the error code informing about such situation is returned, instead of shutting down while waiting for a piece of data or free space.
FIFO queues are one way objects, thus we must use separate queues in order to perform two ways communication.
It is worth to mention that all the tasks work in mutual address space, so by the use of queues we can transfer indicators to different areas of memory.
Bit events form mechanism which one can more often find in embedded operating systems than in the ones of general use. The events enable waiting for specific event or group of events based on setting of the relevant bit or group of bits in one word. In ISIX system the event stores bit mask in 31 bits long variable. Therefore one process/ task may wait for 31 events at the same time.The way the mechanism works is presented here.
Task#3 recalls event_wait() function, with argument 0x3, what in consequence makes this task shut down until bits with 0 and 1 are set.
Due to the fact that two bits are reset to zero, task#3 is shut down. After performing some procedure task#1 sets bit with 0 serial number by event_set() calling, informing that some procedure had been performed.Similarly after performing requested action, task#2 sets bit with 1 serial number informing about ending of certain procedure. At this moment bit event is set with value 0x03 what meets criterion of task#3 restart, therefore after these conditions are fulfilled, operating system with restart task#3 by ending system calling event_wait().It is worth mentioning here, that event_wait() function may wait both for setting all required bits, as well as 1 bit from requested group, depending on wait_for_all bool type parameter. The ISIX system has also additional API, which enables connection of given bit in given event with random FIFO queue, what can be used for waiting for reading data by one process from several queues. By the use of this mechanism we can obtain functionality similar to use of poll/epoll system callings in the systems compatible with the POSIX standard.
- osevent_t Type representing bit events object
- isix::event_create Creates new object of bit events
- isix::event_destroy Removes bit events object by releasing resources occupied by the object
- isix::event_sync Waits for the group of bits defined by wait_for and then atomically set group of bits
- isix::event_wait Waits for the group of bits or any bit
- isix::event_clear Resets group of bits
- isix::event_clear_isr Resets group of bits within interrupt
- isix::event_set Sets group of bits, waking up tasks after await condition is fulfilled
- isix::event_set_isr Sets group of bits within interrupt, waking up tasks after await condition is fulfilled
- isix::event_get Retrieves value from group of bits
- isix::event_get_isr Retrieves value from group of bits within interrupt
- isix::ifo_event_connect Connects chosen FIFO queue with chosen bit of events
- isix::fifo_event_disconnect Disconnects chosen FIFO queue with chosen bit of events
Similarly as in previous group of callings, functions with suffix isr may be used in interrupt procedures, however others cannot be used within interrupt.
It is worth to mention about event_sync, which is waiting in atomic way for the group of bits bits_to_wait and next sets group of bits to value bits_to_set, what might be useful in more advanced synchronisation problems.
Interrupt and cache memory management
Interrupt and processor’s cache memory is important for device drivers. There were not any callings intended for that purpose in primary ISIX system version, so that managing interrupt controller was performed by external libraries for given microcontroller. Along with the introduction of cache memory to CORTEX-M7 processors, internal API was introduced to ISIX system servicing interrupt controller and managing cache memory useful while writing unified device drivers.
System callings intended to interrupt controller administration can be divided into two groups. Generic functions form one group, while functions specific for given controller form the other. In Cortex-M core case the functions will be specific for interrupt controller NVIC (Nested Vectorized Interrupt Controller).
API intended to general interrupt management enables unlocking and locking of chosen interrupt channel or global turning on/off of all interrupts. Description below:
- void isix::irq_enable(); Turns global interrupts on
void isix::irq_disable(); Turns global interrupts off
- unsigned isix::irq_save(void); Turns interrupts off and records the current state of interrupts
- void isix::irq_restore( unsigned mask ); Recreates current state of global interrupts
- void isix::request_irq( int irqno ); Turns chosen interrupt line on
- void isix::free_irq( int irqno ); Turns chosen interrupt line off
- bool isix_get_irq_enabled( int irqno ); Checks current state of chosen interrupt line
- bool isix::get_irq_pending( int irqno ); Checks if chosen interrupt request is active
- void isix::set_irq_pending( int irqno ); Sets chosen interrupt request flag
- void isix::clear_irq_pending( int irqno ); Cancels chosen interrupt request flag
The first group comprises callings responsible for global turning off or turning the interrupt system on. We also have the possibility of atomic writing of the previous state of global interrupts, in local variable both with its turning off.There’s also an opportunity to recreate the previously written state of global interrupts.Second group contains functions which enable switching on or switching off of chosen interrupt line, checking state of interrupt request, setting and resetting of chosen line of interrupt request. it is required to provide serial number, of chosen line, which is specific to the platform. Specific for the platform API managing interrupts enables setting different functions of interrupt system. In the case of microcontrollers with Cortex-M3/4/7, core set of specific functions for NVIC controller is presented below:
- void isix::mask_irq_priority( isix_irq_prio_t priority ); Masks interrupts with priority lower than required
- isix::irq_raw_prio_t isix::mask_irq_save_priority( isix_irq_prio_t new_prio ); Masks interrupts with priority lower than Required and records previous state
- isix::mask_irq_restore_priority( isix_irq_raw_prio_t prio ) Recreates previous state of interrupts masking
- void isix::umask_irq_priority(void) Turns interrupt mask off by unblocking interrupts
- void isix::set_irq_priority( int irqno, isix_irq_prio_t priority ); Sets interrupts priority <prio,subprio>
- void isix::set_raw_irq_priority( int irqno, isix_irq_raw_prio_t prio ); Sets interrupts priority by using interrupts of Internal type
- isix_irq_raw_prio_t isix::irq_priority_to_raw_priority( isix_irq_prio_t prio ); Converts priority of interrupts <prio,subprio> to internal type
- bool isix::get_active_irq( int irqno ); Checks if chosen interrupt is active
- void isix::generate_software_interrupt( int irqno ); Generates program interrupt
- void isix::event_irq_pending( bool en ); Sets controller in a way to make given interrupt cause internal signal EV for the processor
- void isix::set_irq_priority_group( enum isix_cortexm_prigroup prigroup ); Sets number of bits which will be used as priority and number of bits which will be used as interrupt subpriority
- void isix::set_irq_vectors_base( const void *vectptr ); Sets base address of interrupt vector
Functions enabling switching off (masking) interrupts but only to the value of priority are passed as argument. Likewise in basic functions, we can mask interrupts while maintaining previous state of mask or without it. We also have the possibility of setting priorities of particular interrupts by function set_irq_priority() There is also the possibility of setting request flags of interrupts or possibility of defining how many bites from priority’s numerical value will be used as priority and how many as sub-priority of interrupts. More details can be found in technical documentation of Cortex-M core.
Managing processor’s CACHE memory
The most technically advanced and the biggest microcontrollers working with hundred MHz frequency may be equipped in additional memory cache, which significantly accelerates access to data located in slower memory FLASH or RAM. This mechanism is known especially for bigger microprocessors. ARM-CortexM had been introduced once efficient Cortex-M7 core arrived, which, instead of being attached to the bus system,AHB is attached to AXI bus system known from application processors Cortex-A.
Flow chart of processor’s Cache memory is presented on the picture
Cortex-M7 core has 4KB cache memory of first level (L1), separately for code and separately for data. Cache memory is usually transparent for software, however, while developing drivers using DMA, necessary synchronisation of cache memory with main memory is needed. Similar case occurs for multiprocessor work, but such configurations are not popular among microcontrollers. By the time Cortex-M7 appeared, ISIX system introduced API intended for managing cache memory. If we use microcontroller with Cortex-M0/M3/M4 core which does not have cache memory, the managing cache related functions are empty and do not perform any actions. Regardless we use core fitted with cache memory or not, in device drivers using DMA controller, it is always recommended to use them in order to obtain code working on random core. API intended to cache memory management is presented in the table:
- void isix::icache_enable( bool yes ); Turns on or turns off cache memory code
- void isix::dcache_enable( bool yes ); Turns on or turns off cache memory data
- void isix::inval_dcache( void ); Cancels the whole cache memory data
- void isix::inval_icache( void ); Cancels the whole cache memory code
- void isix::clean_dcache( void ); Clears the whole cache memory data
- void isix::clean_inval_dcache( void ); Cancels and clears the whole cache memory data
- void isix::inval_dcache_by_addr( void *addr, size_t dsize ); Cancels chosen part of cache memory data
- void isix::clean_dcache_by_addr( void *addr, size_t dsize ); Clears chosen part of cache memory data
- void isix::clean_inval_dcache_by_addr( void *addr, size_t dsize ); Cancels and clears chosen part of memory data.
API includes basic callings enabling switching memory I-Cache on and off (code cache memory). In D-Cache memory we have possibility to cancel or clear memory cache lines, which indicate specified physical addresses assigned by the initial address and area size, without the necessity of removing the whole. It is worth to mention, what is the difference between operation of cancellation and clearing cache memory. Cancellation operation cancels data located in cache memory and treats these data as if they were not there. Clearing operation assigns cache memory content to main memory.Operation of cancellation is applied e.g. after DMA transmission, when memory area is changed beyond processor’s control. Clearing operation should be applied before DMA transmission in order to obtain reliable data read by DMA controller from the memory.
Management and system diagnosis
Managing system functions such as starting system, its shutting down and restart are separate group of loosely connected system callings, reading time, time of processor’s use etc. Managing function set in ISIX is following:
- ostick_t isix::get_jiffies(void); Returns number of ms that elapsed since the system started
- osutick_t isix::get_ujiffies(void) Returns number of microseconds that have elapsed since the system started
- bool isix_timer_elapsed( ostick_t t1, ostick_t timeout ) Returns true value, when required time elapsed since t1
- void isix::start_scheduler(void); Starts ISIX operating system’s work, function should be called with main() and this is the last function called before system starts
- void isix::shutdown_scheduler(void); Ends operating system’s work, at this moment function start_scheduler() ends its work
- void isix::reboot(void) Restarts the system and starts the program again
- void isix::init( unsigned long core_freq ); Initializes operating system taking as argument the current frequency of the core, should be called before main () and start_scheduler()
- osprio_t isix::get_min_priority(void); Returns minimal priority of thread/tasks available for operating system
- bool isix::is_scheduler_active(void) Returns true value when operating system works
- void isix::enter_critical(void); Locks scheduler enabling expropriation of currently performed task.
- void isix::exit_critical(void); Unlocks scheduler enabling expropriation of currently performed task
First group of callings are functions related to managing time that elapses and enable reading milliseconds or microseconds that elapsed since operating system start. We also have a function that allows to determine if the time between two callings elapsed.isix::init function should be called before main() function from starting code CRT0, soon after code initializing global variables and global constructors.start_scheduler() function starts operating system work and should be called from main() function.This function never ends, unless isix:shudown_scheduler() function will be called out so operating system would be shut down. This method may be used in order to put microcontroller into deep sleep e.g. in response to pressing “Soft Power” button. Next callings are to control scheduler and allow to block or unblock planner’s algorithm, which can be useful while writing some device controllers.
Kernel of the system, discussed earlier, is only a base to manage processes/tasks, memory, or processor but the libraries performing additional functionality such as net management or USB stack.Considering limited space of this article, only the most important libraries and their functions will be discussed.The most crucial library being base of whole program work is lib periph.This library is responsible for initialization of microcontroller. It includes also linker scripts defining map of microcontroller memory and default interrupt vector for all peripheral systems. Functionality, that would allow additional layer of abstraction on stdperiph libraries originating from system producer. The library consists of devices controllers intended for ISIX system, e.g. I2C, SPI, GPIO which manage basic peripheral systems.Library libusb ensures layer of abstraction on USB protocoles and enables writing USB device controllers both for devices such as DEVICE, or HOST, regardless of equipment used. libtcpip library being port of known and appreciated LWIP stack, ensures basic functionality and access to the net. Moreover libtls makes possible encrypting data according to TLS/SSL protocol. If we want to attach GSM modem to microcontroller, we can use libgsm library. This library, except sharing PPP protocol ensures full management of voice connections, text messages or access to module address book, by the use of convenient API C++11/14/17.libfsfat library is also interesting functionality, which, along with SD card controllers, working in compatibility mode with SPI as well as SDIO mode enables access to SD cards formatted in FAT system.If we are interested in developing three-phase electric energy counter measuring active and passive, given out and taken in power, we can use convenient libenergymeter library as this library has basic API in C++17 language.