I know in the last post I said I was going to work on the hardware, but it seems like I got sucked into one too many problems on the software side. However, the hard works has paid off, as I have successful ported over the QP, a quasi-RTOS framework, over to the PSoC4 processor.
So what does this mean? Well, in the grand scheme of things, not very much. The ported software is merely a framework on which the application layer will be written. Nevertheless, the capabilities of the application layer is now greatly magnified because I now have a way to deterministically control my programs. I can rely on the framework for all of my timing needs and it will guarantee, within 1 ms or so accuracy, that critical code will execute. The reason for going through such a sadomasochistic exercise is so that the application layer’s performance is much more stable, and to obtain finer control over the application’s timing requirements.
I won’t go over every detail of how to port over the framework, but I’ll include some of the highlights. Perhaps this will encourage others to use framework stacks rather than just writing on the bare metal software layer.
A Quick Real-time Preemption Rundown
The best feature for any RTOS is preemptive interrupts. If you are shopping for an RTOS, don’t bother with systems that don’t have the preemption feature. For those of you who do not write a lot of firmware, preemption is the act of interrupting a less important task if a higher priority task requires attention. The description of this feature might sound trivial, but it is far from trivial to implement. Explaining the reasons behind this is beyond the scope of this blog post, so I will leave that the professionals (look up books on Amazon, go any embedded conference, etc).
Here’s a simplified rundown of a how an RTOS with preemption typically works: an RTOS allows the user to create time slices, as well as queues of things that needs servicing. When an interrupt occurs, a subsystem is telling the RTOS that it needs attention. An event is generated and placed in the event queue. This event will get serviced in the next available time slice. However, the events in the queue are not serviced in the order that they are received. The events have a certain priority associated with them, and the ones with the higher priority will eat up the time slice first. In this manner, the important tasks will get processed right away, and the non-so-important tasks will get the left over time in the time slice. If there are no event left but still time, the system can go to sleep and get woken up at the start of the next time slice. If there is no more time but still events left to process, the framework will keep on processing events without going to sleep.
Taking Advantage of SysTick
Arm Cortex processors are pretty good for RTOS. They have a feature called SysTick, which allows porting between Arm Cortex processors very easy. Without SysTick, one would need to initialize a timer module to implement the time slices.
One the PSoC4, this was fairly straightforward, with the exception of loading the interrupt vectors for the SysTick interrupt. It took me a long time to find an example. Below is the initialization code for SysTick that I used for the PSoC4 port in case anyone else had trouble.
//interrupt priority #define SYSTICK_INTERRUPT_VECTOR_NUMBER 15u /* Cortex-M0 hard vector */ //timing controls #define BSP_PRIMARY_OSC_HZ 24000000U /* the system tick rate [Hz] */ #define BSP_TICKS_PER_SEC 1000UL //1 ms ticks //call in startup to init systick void QF_onStartup(void) { //link ISR vector CyIntSetSysVector(SYSTICK_INTERRUPT_VECTOR_NUMBER, SysTick_Handler); //system tick, use constants in bsp.h SysTick_Config(BSP_PRIMARY_OSC_HZ / BSP_TICKS_PER_SEC); //set priority for systick interupt NVIC_SetPriority(SysTick_IRQn, SYSTICK_PRIO); //enable interrupts CyGlobalIntEnable; }
The code will generate an interrupt every 1 ms for a 24 MHz system clock. A handler will be called after every 1 ms interval. This is where you can place all the input polling functions. This is quite a convenient feature. In contrast, on other processors that I have used, the time slices would use up a timer module. Since the timer module is typically processor specific, porting code on these systems are painful.
That’s it for now. I have a neet little program running on the Pioneer Kit, and I will add some code and a video on the next blog post.