在 KEIL RTX RTOS 上执行任务的固定时间
Fixed time for executing a task on the KEIL RTX RTOS
我正在使用 KEIL RTX RTOS,它使用抢占式循环 scheduler.I 有一个 LCD 用于显示数据,一些任务可以访问这个 LCD(还有一些其他任务),这些任务需要处理 LCD 的固定时间(例如,第一个任务处理 LCD 用于显示其数据 50 秒,50 秒后,第二个任务处理并显示其数据 10 秒)。我知道我必须使用互斥锁来管理对 LCD.but 的访问 我不知道我必须如何在固定时间管理它?LCD 任务的优先级最低,如果没有任何其他任务要执行,这些任务将执行显示消息。
我会先尝试回答你的问题,然后我会提出一个替代设计供你考虑。
您应该使用 Timer 来以现实世界的时间为基础,尤其是相对较长的时间段,例如以秒为单位的时间段。创建两个 Timer 对象,一个周期为 50 秒,另一个周期为 10 秒。两个定时器都应该是一次性的。还创建两个 Signal 对象,一个指示 50 秒期限已过,另一个指示 10 秒期限已过。两个 Timer 对象在到期时各自调用不同的回调函数。 50 秒回调函数应设置 50 秒到期信号,然后启动 10 秒计时器。 10 秒回调函数应设置 10 秒到期信号,然后重新启动 50 秒计时器。计时器将来回乒乓,交替设置两个信号。
现在您的资源使用任务应定期检查适当的过期信号。当任务观察到信号已设置时,它可以放弃资源并清除信号。另一个任务对另一个信号做同样的事情。这样两个任务就知道什么时候放弃资源并允许另一个任务获得它。
关于您的设计让我困扰的一件事是您有两个同步机制来保护您的资源。互斥量是一种同步机制。当任务需要异步使用资源时(即在随机时间),可以使用互斥锁来同步这些使用并确保在任何给定时间只有一个任务正在使用该资源。如果您已经有另一种同步机制,那么您可能不需要互斥体。换句话说,如果您的任务有不同的时间段来使用资源,那么它们已经是同步的。如果他们不打算在随机时间尝试使用该资源,那么您可能不需要互斥锁。
你的设计看起来也很复杂,我想知道这个替代设计是否可以更简单。
考虑制作一个负责LCD显示界面的任务。此 LDC 任务是唯一与显示交互的任务。当您的数据任务产生要显示的数据时,它们将向 LCD 任务发送消息。 LCD 任务将简单地等待这些消息并在它们到达时适当地显示数据。根据数据的复杂程度和多样性,您可以为此消息服务使用 Message Queue 或 Mail Queue。现在您不需要 LCD 显示器的互斥体,因为只有一个任务使用它。你还需要这个设计的 50/10 秒时间间隔吗?我不确定,因为我不知道该要求的来源是什么。
与其让多个线程访问由互斥锁仲裁的单个资源,不如让单个线程处理资源更简单。
在这种情况下,我建议使用显示管理器线程,其他线程可以向显示管理器注册,可能提供指向显示缓冲区的指针和所需的显示周期。显示管理器然后简单地循环通过每个已注册的线程,在切换到下一个之前显示其缓冲区所需的时间。
例如(伪代码):
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 ) ;
前面的回答中提到,先做一个LCD显示的单一任务,然后使用定时器事件来跟踪时间片。
最后,在timer event handler中yield任务(在时间片后调用)。
如果您不了解 yield,yield 是一种让任务放弃执行以使调度程序转到下一个任务的方式。
我正在使用 KEIL RTX RTOS,它使用抢占式循环 scheduler.I 有一个 LCD 用于显示数据,一些任务可以访问这个 LCD(还有一些其他任务),这些任务需要处理 LCD 的固定时间(例如,第一个任务处理 LCD 用于显示其数据 50 秒,50 秒后,第二个任务处理并显示其数据 10 秒)。我知道我必须使用互斥锁来管理对 LCD.but 的访问 我不知道我必须如何在固定时间管理它?LCD 任务的优先级最低,如果没有任何其他任务要执行,这些任务将执行显示消息。
我会先尝试回答你的问题,然后我会提出一个替代设计供你考虑。
您应该使用 Timer 来以现实世界的时间为基础,尤其是相对较长的时间段,例如以秒为单位的时间段。创建两个 Timer 对象,一个周期为 50 秒,另一个周期为 10 秒。两个定时器都应该是一次性的。还创建两个 Signal 对象,一个指示 50 秒期限已过,另一个指示 10 秒期限已过。两个 Timer 对象在到期时各自调用不同的回调函数。 50 秒回调函数应设置 50 秒到期信号,然后启动 10 秒计时器。 10 秒回调函数应设置 10 秒到期信号,然后重新启动 50 秒计时器。计时器将来回乒乓,交替设置两个信号。
现在您的资源使用任务应定期检查适当的过期信号。当任务观察到信号已设置时,它可以放弃资源并清除信号。另一个任务对另一个信号做同样的事情。这样两个任务就知道什么时候放弃资源并允许另一个任务获得它。
关于您的设计让我困扰的一件事是您有两个同步机制来保护您的资源。互斥量是一种同步机制。当任务需要异步使用资源时(即在随机时间),可以使用互斥锁来同步这些使用并确保在任何给定时间只有一个任务正在使用该资源。如果您已经有另一种同步机制,那么您可能不需要互斥体。换句话说,如果您的任务有不同的时间段来使用资源,那么它们已经是同步的。如果他们不打算在随机时间尝试使用该资源,那么您可能不需要互斥锁。
你的设计看起来也很复杂,我想知道这个替代设计是否可以更简单。
考虑制作一个负责LCD显示界面的任务。此 LDC 任务是唯一与显示交互的任务。当您的数据任务产生要显示的数据时,它们将向 LCD 任务发送消息。 LCD 任务将简单地等待这些消息并在它们到达时适当地显示数据。根据数据的复杂程度和多样性,您可以为此消息服务使用 Message Queue 或 Mail Queue。现在您不需要 LCD 显示器的互斥体,因为只有一个任务使用它。你还需要这个设计的 50/10 秒时间间隔吗?我不确定,因为我不知道该要求的来源是什么。
与其让多个线程访问由互斥锁仲裁的单个资源,不如让单个线程处理资源更简单。
在这种情况下,我建议使用显示管理器线程,其他线程可以向显示管理器注册,可能提供指向显示缓冲区的指针和所需的显示周期。显示管理器然后简单地循环通过每个已注册的线程,在切换到下一个之前显示其缓冲区所需的时间。
例如(伪代码):
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 ) ;
前面的回答中提到,先做一个LCD显示的单一任务,然后使用定时器事件来跟踪时间片。
最后,在timer event handler中yield任务(在时间片后调用)。
如果您不了解 yield,yield 是一种让任务放弃执行以使调度程序转到下一个任务的方式。