打印
[单片机芯片]

【CH32F207VCT6】开发例程+ 03添加任务调度

[复制链接]
489|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 聪聪哥哥 于 2025-7-10 10:55 编辑

在C语言里,任务调度指的是对多个任务(也被叫做线程或者进程)的执行顺序进行管理,以此来达成高效利用系统资源的目的。下面为你详细介绍任务调度的相关概念和实现方法。
一:任务调度的概念思想:
抢占式调度:在这种调度方式下,操作系统能够依据任务优先级,强行暂停当前正在执行的任务,转而执行其他任务。
非抢占式调度:采用这种调度方式时,任务只有在主动放弃 CPU 控制权的情况下,其他任务才有机会执行。
实时调度:实时调度的核心是保证任务能在严格的时间限制内完成,它又可以细分为硬实时调度和软实时调度。
优先级调度:优先级调度会为每个任务分配一个优先级,系统会优先执行优先级较高的任务。

二:任务调度的关键要点
上下文切换:在任务切换时,需要保存当前任务的状态(例如寄存器值),并恢复下一个任务的状态。
临界区保护:对于共享资源,要使用互斥锁、信号量等机制来避免竞态条件。
任务同步:可以通过信号量、事件标志组等方式实现任务间的同步。
堆栈管理:每个任务都有自己独立的堆栈,必须确保堆栈大小足够,防止溢出。

常用的任务调用有Free Rtos,OS,RT-threard等等实时操作系统专为嵌入式系统设计,提供了强大的任务调度功能,这里和大家分享一个简单任务调度器。创建任务队列。然后按照一定时间间隔来处理任务。
这里我是用CH32的通用定时器来实现任务的调度。
三:CH32通用定时器的基本概念分享:
   通用定时器模块包含四个 16 位可自动重装的定时器(TIM2、TIM3、TIM4和 TIM5),用于测量脉冲宽度或者产生特定频率的脉冲、PWM 波等。可用于自动化控制、电源等领域。
3.1通用定时器的主要特征包括:
16 位自动重装计数器,支持增计数模式,减计数模式和增减计数模式
16 位预分频器,分频系数从 1~65536 之间动态可调
支持四路独立的比较捕获通道
每路比较捕获通道支持多种工作模式,比如:输入捕获、输出比较、PWM 生成和单脉冲输出
支持外部信号控制定时器
支持在多种模式下使用 DMA
支持增量式编码,定时器之间的级联和同步
3.2、通用 定时器的内部构造:
3.2 几个重要的寄存器介绍:
TIMx_CTLR1:控制寄存器1,配置计数方向、分频等
TIMx_PSC:预分频器,决定定时器时钟频率
TIMx_ATRLR:自动重装载寄存器,决定计数周期
TIMx_SWEVG:软件事件生成寄存器
TIMx_INTFR:中断标志寄存器
熟练的掌握上述寄存器操作,可以方便我们快速开发代码工程。
四:通用定时器的编写流程如下:
1:使能定时器2时钟。
2:初始化定时器,配置ARR,PSC,中断时间1ms。
3:开启定时器2中断,配置NVIC中断优先级。
4:使能定时器2,定时器开始功能。
5: 编写中断服务函数。
这里定时器的中断频率计算公式如下:Tout(中断触发时间)=(ARR+1)(PSC+1)/定时器的时钟频率

五:程序流程图如下所示:

六:程序代码如下:
6.1 初始化通用定时器2
void TIM2_INT_Init( u16 arr, u16 psc)
{

        NVIC_InitTypeDef NVIC_InitStructure={0};
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure={0};

        RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

        TIM_TimeBaseInitStructure.TIM_Period = arr;
        TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
        TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
        TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 50;
        TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);

        TIM_ClearITPendingBit( TIM2, TIM_IT_Update );

        NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
        NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;
        NVIC_Init(&NVIC_InitStructure);

        TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

        TIM_Cmd( TIM2, ENABLE );
}
6.2 编写定时器2的中断处理函数:
void TIM2_IRQHandler(void)
{
   if(TIM_GetITStatus(TIM2, TIM_IT_Update)==SET)
  {
    Task_Marks_Handler_Callback();
  }
   TIM_ClearITPendingBit( TIM2, TIM_IT_Update );
}
6.3 定义任务函数如下所示:在定时器2中添加任务标记函数
//========================================================================
// 函数: TASK_COMPONENTS
// 描述: 添加任务列表
// 参数: None.
// 返回: None.
// 版本: V1.0, 2025-07-09
//========================================================================
static TASK_COMPONENTS Task_Comps[]=
{
  {0, 1000,1000, task_1000ms},                          /* task 1 Period: 1000ms */
  {0, 500,500, task_500ms},                          /* task 1 Period: 500ms *
};
这里我添加了两个时间任务,分为是500ms 和 1000ms的任务函数
6.4 编写任务标记回调函数
//========================================================================
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2025-07-09
//========================================================================
void Task_Marks_Handler_Callback(void)
{
        char i;
        for(i=0; i<Tasks_Max; i++)
        {
                if(Task_Comps[i].TIMCount)    /* If the time is not 0 */
                {
                        Task_Comps[i].TIMCount--;  /* Time counter decrement */
                        if(Task_Comps[i].TIMCount == 0)  /* If time arrives */
                        {
                                /*Resume the timer value and try again */
                                Task_Comps[i].TIMCount = Task_Comps[i].TRITime;  
                                Task_Comps[i].Run = 1;    /* The task can be run */
                        }
                }
        }
}
6.5 编写任务处理回调函数
//========================================================================
// 函数: Task_Pro_Handler_Callback
// 描述: 任务处理回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2025-07-09
//========================================================================
void Task_Pro_Handler_Callback(void)
{
        char i;
        for(i=0; i<Tasks_Max; i++)
        {
                if(Task_Comps[i].Run) /* If task can be run */
                {
                        Task_Comps[i].Run = 0;    /* Flag clear 0 */
                        Task_Comps[i].TaskHook();  /* Run task */
                }
        }
}
6.6 分别在定时器2中断中添加任务标记函数,在主函数中添加任务处理回调函数
  while( 1 )
   {
      Task_Pro_Handler_Callback();
   };
6.7  为了方便演示效果,这里两个任务如下所示:
//========================================================================
// 函数: void task_1000ms(void)
// 描述: 1000ms 任务.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2025-07-09
//========================================================================
void task_1000ms(void)
{
        printf("21论坛测评CH32F207\r\n");
}
//========================================================================
// 函数: void task_500ms(void)
// 描述: 500 任务.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2025-07-09
//========================================================================
void task_500ms(void)
{
   if(bLEDFlag == 0)
   {
       GPIO_WriteBit( GPIOA, GPIO_Pin_0, Bit_SET  );
       bLEDFlag =1 ;
   }
  else
  {
       bLEDFlag = 0 ;
       GPIO_WriteBit( GPIOA, GPIO_Pin_0, Bit_RESET  );
  }
}
七:测试现象如下所示:
7.1 串口接收数据如下:使用串口接收工具查看,串口1发送的数据。


7.2 实物测试图如下所示:


7.3 工程代码如下:
02DWIN_USART.zip (6.11 MB)

8:调试心得
使用CH32的通用定时器完成任务调度的功能,使用定时器2的1ms中断更新速度,所以要求每个任务的扫描时间不能小于1ms,要是更新响应速度,需要更新定时器2的中断时间即可。


使用特权

评论回复
沙发
时光迷宫| | 2025-7-12 13:24 | 只看该作者
这个就是任务来回切换然后计时?前后台系统还是两个中断来回切换?

使用特权

评论回复
板凳
NebulaHaven| | 2025-7-12 22:27 | 只看该作者
就是前后台系统来回切换,或者两个中断来回切换测时间对吗

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

75

主题

206

帖子

1

粉丝