Hello,
I'd like to write some reusabel code module to handle e.g. UART ports. Therefore I'd like to create a variable number (based on some kind of define...) of threads dealing with UART port 1,2,3 ... since it is possible to declare CMSIS-RTOS threads with a variable number of instances it could look like this:
#define UART_PORT_NUM 3 osThreadDef(uart_thread, osPriorityNormal, UART_PORT_NUM, 0)
my problem: If I need any kind of inter-thread communication (ITC) object, like mutexes or mail queues, for each of this threads I don't know how to declare them, since osMailQDef, osMutexDef and so on, are not designed with a "variable" number of instances...
Any suggestions?
Here is 1 exammple - very specific to Keil's CMSIS RTOS implementation.
namespace OsRTX { // uint32_t os_mutex_cb_##name[4] = { 0 }; becomes.. struct Mutex { uint32_t _private_[4]; }; } class OsMutex { private: OsRTX::Mutex m_rtxMutex; osMutexId m_MutexId; void Create() { osMutexDef_t temp; // Note: not all osDef_t items can be temporary temp.mutex = &m_rtxMutex; m_MutexId = osMutecCreate( (const osMutexDef_t *) &temp); } public: OsMutex() { Create(); } // Initialized at construction! // Note: not all os items can be initialized // before the OS is running. ~OsMutex() {} osStatus Wait(uint32_t millis) { return osMutexWait(m_MutexId, millis); } osStatus Release() { return osMutexRelease(m_MutexId); } }; // 2 Instances of Mutex Objects OsMutex Uart1Mutex; osMutex Uart2Mutex; // Usage status = Uart1Mutex.Wait(100); status = Uart1Mutex.Release();
You can do something like this
class Uart : public UartBase { UART* m_uart; OsMutex m_mutex; public: Uart(UART* uart) : m_uart(uart) {} ~Uart() {} osStatus Acquire(uint32_t millis) { return m_mutex.Wait(millis); } osStatus Release() }; Uart GPS_Uart(UART1); Uart GSM_Uart(UART2); Uart Debug_Uart(UART3);
Combining MANY items you can
Class GenericObjectUsingTaskAndUart { static const uint32_t mbxitems = 10; UartBase *m_uart; OsThread A1; OsThread A2; // Usually the object IS-A thread, but if you want multiple threads // tied together, you would have the class contain them... OsMbx<mbxitems,DataType_t> InQueue// Mailbox with 10 entries of type DataType_t public: GenericObjectUsingTaskAndUart(UartBase *uart) : m_uart(uart) {} ~GenericObjectUsingTaskAndUart() {} osStatus InitTask() { // Init and start threads, other stuff, that must happen // After OS is running. Could break into InitTask and Start } OsMbxBase<DataType_t> *GetInQueue() { return &InQueue; } }; // We now have an object that contains what it needs and knows about then, but is not // specifically tied to something GenericObjectUsingTaskAndUart Gen1(UART1); GenericObjectUsingTaskAndUart Gen2(UART2); GenericObjectUsingTaskAndUart Gen3(UART3);
You may create objects for every os item type (mailbox,message,timer,thread) They can be generically combined in any way imagined (for good or bad)
This is just an example for thought. I am not suggesting that it is a way anyone should use it. Just posting it as an actual answer the original question. I am certainly not requesting any kind of critique.
Ok, that's what I need, thank you! I have to adapt it (I forgot to mention, that I am using C, the mentioned C++ thread was just to demonstrate, that there is a need for such an implementation) I was also thinking about avoiding the typical osXyzDef() macros and to do all the stuff manually. The only question, which you also have mentioned in your comment: Is it really save/recommendable to create the osXyzDef_t structures (e.g. osMutexDef_t) temporary? The macros are creating them as const, so I thought, the kernel needs it that way.
See my example for just supplying a struct to the tread, informing it what queues etc to associate with.
It is not at all safe to just make them temporary.
I had a similar need for reusable code involving mutexes. The way the macros are defined in cmsis_os.h make that difficult, but after some study it appears all that's needed is a control block of uint32_t[4] and a temporary pointer.
The osMutexDef(x) goes away, as does osMutex(x). I've replaced those with:
typedef struct _osMutexCb { uint32_t cb[4]; // this is the actual control block void *pCb; // pointer to the control block. Only needed during osMutexCreate } osMutexCb_t;
You should be able to allocate space for that statically or dynamically, as many of them as you need.
I've created a wrapper for the create. This could also be done as a macro or inline, but I have some portability issues with the latter.
osMutexId osMutexCreate2(osMutexCb_t *MutexCb) { MutexCb->pCb = (void *) MutexCb->cb; return osMutexCreate((const osMutexDef_t *) &MutexCb->pCb); }
The other mutex functions only use the osMutexId (which is just a pointer to the control block, so why couldn't osMutexCreate just use it in the first place?). Duh.
That's an awfully lot of monkey motion for a relatively simple thing. It's also a redundant level of indirection where we're sending osMutexCreate a pointer to a pointer. In the standard cmsis syntax using osMutexDef macro, that pointer space is only used once for the create. I tested this using an auto variable, demonstrating it does not need to be retained.
The old Keil programmers would have never permanently wasted those 4 bytes, or the redundant level of indirection. They were careful of CPU clocks and space efficiency.
I've seen some claims posted here that the standard osMutexDef() and osMutex() macros make code more readable. Nonsense. Wrapping a simple block of storage in a macro that references a typedef is less readable, not more. It took several hours to conclude that all you needed was 16 bytes of storage and send osMutexCreate a pointer to a pointer to that storage and you're finished.
I'm at a loss to understand why Keil created these macros that make it difficult to write reusable code. The only reasonable answer might be that programmers will be less likely to "lose" control blocks by deallocating them from dynamic memory, or some other accident.
Ok. But they could have easily protected dumb programmers by retaining the control block within OS static memory and allocating it during mutex create. Yes, that would mean a fixed number of mutexes would be allowed, but that's no different than the static memory allocated for the stack, or the fixed number of threads, etc.
The bottom line is cmsis should have been similar to other OSes, and allow programmers to control their own mutexes and other OS objects. This would make portability and re-learning issues easier. I'm sure whomever designed this thing believes they were doing someone a favor. Sorry, no. It made things much more difficult, and in the end I've just rewritten a chunk of cmsis_os.h to let me do what I need to do. No doubt I'll rewrite more of it. That's time I've wasted merely because the convoluted multi-layer syntax in cmsis_os_h wouldn't allow me to do what is easily done with other OSes.