我正在使用KEIL RTX RTOS,它使用先发制人的循环调度程序。我有一个LCD用于显示数据,一些任务可以访问这个LCD(还有一些其他任务),这些任务需要固定的时间来处理LCD(例如,第一任务处理LCD用于显示它的数据50秒和50秒后,第二任务处理并显示它的数据10秒)。我知道我必须使用互斥锁来管理对LCD的访问。但是我不知道我必须如何管理固定时间?LCD任务的优先级最低,如果没有任何其他任务要执行,这些任务将被执行以显示消息
答案 0 :(得分:1)
我会首先尝试回答您的问题,但之后我会提出另一种设计供您考虑。
你应该使用一个计时器,以便在实际时间内建立任何基础,特别是相对较长的时期,例如以秒为单位测量的时间。创建两个Timer对象,一个具有50秒周期,另一个具有10秒周期。两个计时器都应该是一次性的。还要创建两个Signal对象,一个用于指示50秒周期已过期,另一个用于指示10秒周期已过期。两个Timer对象在到期时分别调用不同的回调函数。 50秒回调函数应设置50秒到期信号,然后启动10秒定时器。 10秒回调函数应设置10秒到期信号,然后重新启动50秒定时器。定时器将来回乒乓,交替设置两个信号。
现在您的资源使用任务应定期检查适当的到期信号。当任务观察到信号被设置时,它可以放弃资源并清除信号。另一个任务与另一个Signal做同样的事情。这样,这两个任务就知道何时放弃资源并允许其他任务获取资源。
使您的设计困扰我的一件事是您有两个保护资源的同步机制。互斥是一种同步机制。当任务绑定以异步方式使用资源时(即,在随机时间),可以使用互斥锁来同步这些用法并确保在任何给定时间只有一个任务正在使用该资源。如果您已经有另一种同步机制,那么您可能不需要互斥锁。换句话说,如果您的任务具有使用资源的不同时隙,那么它们已经是同步的。如果他们不打算在随机时间尝试使用该资源,那么您可能不需要互斥锁。
您的设计似乎也很复杂,我想知道这种替代设计是否会更简单。
考虑制作一个负责LCD显示界面的单一任务。此LDC任务是与显示器接口的唯一任务。您的数据任务将在生成要显示的数据时向LCD任务发送消息。 LCD任务将等待这些消息并在数据到达时适当显示。您可以使用Message Queue或Mail Queue作为此消息服务,具体取决于数据的复杂程度。现在,您不需要用于LCD显示器的互斥锁,因为只有一个任务使用它。你是否仍然需要这个设计的50/10第二次分裂?我不确定,因为我不知道该要求的来源是什么。
答案 1 :(得分:1)
然后让多个线程访问由互斥锁仲裁的单个资源,让单个线程处理资源会更简单。
在这种情况下,我建议使用显示管理器线程,其他线程可以向显示管理器注册,提供可能指向显示缓冲区的指针,以及所需的显示周期。然后,显示管理器简单地遍历每个注册的线程,在切换到下一个之前,在所需的时间段内显示其缓冲区。
例如(伪代码):
static struct
{
const char* frame_buffer ;
int display_seconds ;
OS_MUTEX mutex ;
} display_registry[MAX_DISPLAY_THREADS] = {0,0} ;
void displayLock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_lock( display_registry[handle].mutex ) ;
}
}
void displayUnock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_unlock( display_registry[handle].mutex ) ;
}
}
void lcd_thread
{
int display = 0 ;
for(;;)
{
int t = 0 ;
while( t < display_registry[display].display_seconds &&
display_registry[display].frame_buffer != 0 )
{
displayLock( display ) ;
render_display( display_registry[display].frame_buffer ) ;
displayUnlock( display ) ;
delay( ONE_SECOND ) ;
}
display = (display + 1) % MAX_DISPLAY_THREADS ;
}
}
int displayRegister( const char* frame_buffer, int display_seconds )
{
for( int i = MAX_DISPLAY_THREADS - 1;
frame_buffer[i] != 0 &&
i >= 0; i-- )
{
// do nothing
}
if( i >= 0 )
{
display_registry[i].display_seconds = display_seconds ;
display_registry[i].frame_buffer = frame_buffer ;
}
return i ; // handle for de-registering/locking
}
void displayDeregister( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
display_registry[handle].frame_buffer = 0 ;
}
}
请注意,互斥锁不是锁定LCD资源,而是锁定共享内存帧缓冲区资源。
然后,其他线程只是将数据放置在它们自己的帧缓冲区中,完全与该数据的显示异步,如下所示:
displayLock( my_display_handle ) ;
update_display_buffer() ;
displayUnlock( my_display_handle ) ;
答案 2 :(得分:0)
如前面的答案中所述,首先为LCD显示器执行单个任务,然后使用计时器事件来跟踪时间片。
最后,在计时器事件处理程序中生成任务(在时间片之后调用)。
如果您不了解收益率,则收益率是任务放弃执行以使调度程序转移到下一个任务的一种方式。