RTOS 应用程序嵌入式开发人员面临着几个共同的挑战。让我们检查这些挑战并讨论一些潜在的解决方案。
挑战 #1 – 选择任务优先级
事实证明,有几种不同的方法来选择任务优先级。首先,有最短的响应时间。在这种方法中,开发人员检查每个任务的响应时间要求,并将最高优先级分配给具有最短响应时间要求的任务。
其次,有最短作业优先方法。在这种方法中,开发人员检查任务的执行时间,执行时间最短的任务是最高优先级(显然其次是下一个最高优先级,依此类推)。
最后,还有一种在实时嵌入式系统中最常用的方法,即最短周期优先或更常用的“速率单调调度(RMS)”。在这种方法中,周期最短的任务优先级最高。
遵循 RMS 将使你完成 95% 的任务,然后通常会有一个奇怪的任务,或者是非周期性的,需要优先级分配。这些非周期性任务可以分配一个最坏情况周期,也可以根据它们的重要性、执行时间或是否需要在另一个可能需要其数据的任务之前运行来分配它们。(请记住,任务优先级没有正确或错误的答案,只有可能比其他系统运行得更好或更高效的系统)。
挑战#2 — 用数据流图看大图
嵌入式开发人员在实施他们的 RTOS 应用程序时并没有真正了解数据是从哪里产生的、去往何处以及如何到达那里的,这会导致软件有点像糟乱的代码,并且随着更多应用程序的部署,经常需要不断地返工。尽量减少这种返工并了解整个应用程序的方法是开发一个简单的数据流图。该图包含几个关键组件:
- 数据生产者
- 数据消费者
- 数据传输机制
- 存储机制
- 任务协调机制
拥有此数据流图可以回答有关应用程序设计的许多问题,并避免将大量时间浪费在返工或调试上。
挑战 #3 – 正确保护共享内存
互斥锁用于保护共享内存资源,但在实现中,经常有嵌入式开发人员将互斥锁与受保护的数据分开创建。虽然乍一看这似乎没问题,但问题是如果互斥锁是独立于数据结构创建的,并且有人去使用该数据结构,他们可能不会意识到它是一个共享和受保护的资源。(是的,文档、设计和许多其他东西应该使这一点显而易见,但如果单独声明它很容易被忽视)。
解决方案是将共享内存视为一个对象,并将互斥锁作为共享内存数据结构的一部分。例如,共享内存可能有来自湿度、温度和电流传感器的数据。我们通常可以如下声明数据的结构:
typedef struct
{
uint16_t Humidity;
uint16_t Temperature;
uint16_t Current;
}SensorData_t;
同样,单独声明的互斥锁可能会使数据共享变得不那么明显。相反,我们可以定义如下结构:
typedef struct
{
mutex_t SensorDataMutex;
uint16_t Humidity;
uint16_t Temperature;
uint16_t Current;
}SensorData_t
现在,每当开发人员查看数据结构、尝试执行自动完成等操作时,都会提醒他们这是受保护的数据。当他们看到它被保护时,它应该提醒他们在访问数据之前,他们需要获取互斥锁。
开发人员经常忘记,仅仅因为创建互斥锁是为了保护数据,并不能保证互斥锁将用于访问数据。(这也是为什么将数据结构视为一个对象并创建限制对数据资源的访问和控制的函数很有用,这些数据资源在应用程序级别抽象出互斥锁)。
实时操作系统可以简化嵌入式系统的时间和资源管理。但是,RTOS 确实增加了系统的复杂性,可能会产生影响开发计划和代码质量的意想不到的挑战。在今天的文章中,我们研究了嵌入式开发人员经常遇到的几个常见挑战,通过遵循一些最佳实践可以很容易地克服这些挑战。