之前使用时间任务调度的方式,完成任务调度处理。今天和大家分享另外一种任务调度的方法。
一:消息队列的基础知识:
1、消息队列只 是一种先进先出的队列型数据结构口,实际上是系统内核中的一个内部链表。消息被顺序插入队列中,其中发送进程将消息添加到队列未尾,接受进程从队列头读取消息。
2、多个进程可同时向一个消息队列发送消息,也可以同时从一个消息队列中接收消息。发送进程把消息发送到队列尾部,接受进程从消息队列头部读取消息,消息一旦被读出就从队列中删除。在主程序中进行处理任务,删除任务。若不删除任务,会导致任务堆栈的溢出,从而导致程序的进入硬件错误。
二:结构:
1:消息队列中消息本身由消息类型和消息数据组成,通常使用如下结构:
typedef struct MESSAGE0 //消息结构体
{
int iWParm; //消息参数
int iLParm;
int Mnumber; //消息值,取值1-254
}MESSAGE;
1) iWparm 指定了消息类型,为正整数。
引入消息类型之后,消息队列在逻辑上由一个消息链表转化为多个消息链表。发送进程仍然无条件把消息写入队列的尾部,但接收进程却可以有选择地读取某个特定类型的消息中最接近队列头的一个,即使该消息不在队列头。相应消息一旦被读取,就从队列中删除,其它消息维持不变。
2)iLParm指定了消息的数据。我们可以定义任意的数据类型甚至包括结构来描述消息数据。
2:消息队列的创建:
定义消息队列的深度和当前指针的个数
MESSAGE ArrayMessage[MES_MAX]; //消息队列最大64
static char CurrentMesgPoint; //当前需要操作的消息指针
3:具体的创建任务如下所示:
/***************************************************************************************
SendMessage()
发送消息到消息队列
Mnumber:消息值
WParm:消息参数1
LParm:消息参数2
****************************************************************************************/
void SendMessage(char Mnumber,int WParm,int LParm)
{
char i;
CurrentMesgPoint++; //这样处理的原因是避免在SendMessage函数重入导致的消息被改写的BUG
if(CurrentMesgPoint >= MES_MAX)
CurrentMesgPoint = 0;
i = CurrentMesgPoint;
if(ArrayMessage[i].Mnumber == 0)
{
ArrayMessage[i].Mnumber = Mnumber;
ArrayMessage[i].iWParm = WParm;
ArrayMessage[i].iLParm = LParm;
return;
}
}
4:删除任务:
/***************************************************************************************
DeleteMessage()
从消息队列中删除消息
MPoint:消息队列数组下标
***************************************************************************************/
void DeleteMessage(char MPoint)
{
ArrayMessage[MPoint].iWParm = 0;
ArrayMessage[MPoint].iLParm = 0;
ArrayMessage[MPoint].Mnumber=0; //避免函数重载出现BUG,对Mnumber的改写放在最后面
}
5:在定时器中添加不同间隔的消息队列:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM6)
{
Time6point++ ;
if(Time6point %100 == 0)
SendMessage(MW_TIMERINT,1,0); //发出TIM 100ms中断消息
if(Time6point %200 == 0)
SendMessage(MW_TIMERINT,2,0); //发出TIM 200ms中断消息
if(Time6point %500 == 0)
SendMessage(MW_TIMERINT,5,0); //发出TIM 500ms中断消息
if(Time6point %1000 == 0)
{
Time6point = 0 ;
SendMessage(MW_TIMERINT,10,0); //发出TIM 1000ms中断消息
}
}
}
时间任务的处理函数如下所示:
void System_Tim_Driver(char Tim_Intev)
{
switch(Tim_Intev)
{
case 1:
break;
case 2: HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4);
break;
case 5: printf("STM32F103VETEST! by queue test!!\n");//500ms
break;
case 10:
break ;
default:break;
}
}
主程序中添加任务处理,删除的函数如下所示:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
mspoint ++;
if(mspoint>=MES_MAX) mspoint = 0; //消息循环查讯
if(ArrayMessage[mspoint].Mnumber !=0)
{
switch(ArrayMessage[mspoint].Mnumber)
{
case MW_TIMERINT:
System_Tim_Driver(ArrayMessage[mspoint].iWParm);
break;
default:
break;
}
DeleteMessage(mspoint); //删除已经处理的消息
}
}
实物验证如下所示:
后记:
1、采用消息队列方式比之前采用时间任务调度具有更多的灵活性,通信的进程不但没有时间上的要求,也不需要进行同步处理。只需要根据消息触发的顺序,进行执行,从响应时效性上更加的灵活。
2、消息队列是一种先进先出的队列型数据结构;
3、消息队列将输出的信息进行了打包处理,可以保证以消息为单位进行接收;为了防止消息队列的溢出,可以将任务队列加大,从而使实现任务深度的增加,防止任务的丢失。
全部评论