ANO匿名飞控分析(2)— 任务调度


一、简介

  • 匿名飞控的任务调度还是比较简单的,没有操作系统什么的。
  • 可以大概分成两块:
    (1)利用systick定时器实现的定时执行任务,这部分都是各种逻辑,执行速度很快
    (2)借助Icm20602输出的1ms脉冲实现的外部中断任务。这里主要执行传感器数据获取,执行速度比较慢,但是可以打断第一部分的各种任务。经过咨询匿名客服,这里使用传感器1ms脉冲的原因是:用传感器自己提供的时钟会让检测更准确

二、代码分析

(1)1ms中断部分

//这部分代码位于 Drv_icm20602.c------------------------------------------------------------------------------
/*Icm20602引起的1ms中断*/
void Drv_Icm20602IrqHandler(void)
{
	//清除中断标记
	GPIOIntClear(ICM20602_READY_PORT, ICM20602_READY_PIN);
	//执行中断函数
	//Drv_Icm20602_Read();
	//利用icm的1ms中断做1ms任务
	INT_1ms_Task();
}

/*配置PB2为上升沿外部中断,中断优先级为7(最低),捕获Icm20602输出的1ms周期脉冲*/
void Drv_Icm20602ReadyPinInit(void)
{
	ROM_SysCtlPeripheralEnable(ICM_READYPIN_SYSCTL);
	ROM_GPIODirModeSet(ICM20602_READY_PORT, ICM20602_READY_PIN, GPIO_DIR_MODE_IN);
	ROM_GPIOPinTypeGPIOInput(ICM20602_READY_PORT, ICM20602_READY_PIN);
	ROM_GPIOPadConfigSet(ICM20602_READY_PORT,ICM20602_READY_PIN,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPD);
	//上升沿中断
	ROM_GPIOIntTypeSet(ICM20602_READY_PORT, ICM20602_READY_PIN , GPIO_RISING_EDGE);
	//GPIO注册中断
	GPIOIntRegister(ICM20602_READY_PORT, Drv_Icm20602IrqHandler);
	//使能中断
	GPIOIntEnable(ICM20602_READY_PORT, ICM20602_READY_PIN);
	//设置中断优先级
	ROM_IntPrioritySet(ICM20602_READY_INT_PORT, USER_INT7);
}

//这部分代码位于Ano_Scheduler.c-------------------------------------------------------------------------------
#define CIRCLE_NUM 20
static u8 lt0_run_flag;
static u8 circle_cnt[2];
/* Drv_Icm20602IrqHandler调用的1ms任务 */
void INT_1ms_Task()
{	
//	if(fc_sta.start_ok == 0) return;
	
	//标记1ms执行,注意这个标志触发了Loop_Task_0
	lt0_run_flag ++;	
	//灯光驱动
	LED_1ms_DRV();

	
	circle_cnt[0] ++;
	circle_cnt[0] %= CIRCLE_NUM;
	if(!circle_cnt[0])
	{
		//20ms的周期,没有用到		
	}
}

/* lt0_run_flag触发的1ms任务,包含所有传感器读取(具体调用地点见下文)  */
static void Loop_Task_0()//1ms执行一次
{
	/*传感器数据读取*/
	Fc_Sensor_Get();
	/*惯性传感器数据准备*/
	Sensor_Data_Prepare(1);
	
	/*姿态解算更新*/
	IMU_Update_Task(1);
	
	/*获取WC_Z加速度*/
	WCZ_Acc_Get_Task();
	WCXY_Acc_Get_Task();
	
	/*飞行状态任务*/
	Flight_State_Task(1,CH_N);
	
	/*开关状态任务*/
	Swtich_State_Task(1);
	

	/*数传数据交换*/
	ANO_DT_Data_Exchange();	
}

(2)systick定时器调度部分

//这部分代码位于Ano_Scheduler.h-------------------------------------------------------------------------------
typedef struct
{
	void(*task_func)(u32 dT_us);	//任务函数指针
//	u16 rate_hz;
	u32 interval_ticks;				//任务执行周期(us)
	u32 last_run;					//上次执行任务的时间
}sched_task_t;

//这部分代码位于Ano_Scheduler.c-------------------------------------------------------------------------------

//系统任务配置,创建不同执行频率的“线程”
static sched_task_t sched_tasks[] = 
{
 	//任务n,        周期us, 上次时间us
	{Loop_Task_1 ,  2000,  0 },		//姿态角速度环控制 & 电机输出控制	
	{Loop_Task_2 ,  6000,  0 },		//获取姿态欧拉角 & 姿态角度环控制
//	{Loop_Task_2 ,  2500,  0 },
//	{Loop_Task_3 ,  2500,  0 },
//	{Loop_Task_4 ,  2500,  0 },
	{Loop_Task_5 ,  11000, 0 },		//遥控器数据处理 & 飞行模式设置任务 & 高度数据融合任务 & 高度速度环控制 & 高度环控制 & 光流数据分析 & 灯光控制
//	{Loop_Task_6 ,  9090,  0 },
//	{Loop_Task_7 ,  9090,  0 },
	{Loop_Task_8 , 20000,  0 },		//罗盘数据处理任务 & 程序指令控制 & UWB数据计算 & 位置速度环控制
	{Loop_Task_9 , 50000,  0 },		//电压相关任务 & 陀螺仪恒温控制 & 延时存储任务
//	{Loop_Task_10,100000,  0 },
};

//根据数组长度,判断线程数量
#define TASK_NUM (sizeof(sched_tasks)/sizeof(sched_task_t))

//Main_Task在main.c的while循环中轮询
u8 Main_Task(void)
{
	uint8_t index = 0;
	
	//查询1ms任务是否需要执行
	if(lt0_run_flag!=0)
	{
		lt0_run_flag--;
		Loop_Task_0();	//注意这里触发1ms中断任务
	}
	
	//循环判断其他所有线程任务,是否应该执行
	uint32_t time_now,delta_time_us;
	for(index=0;index < TASK_NUM;index++)
	{
		//获取系统当前时间,单位US
		 time_now = GetSysRunTimeUs();//SysTick_GetTick();
		//进行判断,如果当前时间减去上一次执行的时间,大于等于该线程的执行周期,则执行线程
		if(time_now - sched_tasks[index].last_run >= sched_tasks[index].interval_ticks)
		{
			delta_time_us = (u32)(time_now - sched_tasks[index].last_run);

			//更新线程的执行时间,用于下一次判断
			sched_tasks[index].last_run = time_now;
			//执行线程函数,使用的是函数指针
			sched_tasks[index].task_func(delta_time_us);
		}	 
	}
	
	return 0;
}
  • 可以看到这里用了一个sched_task_t型的数组,存储了若干不同执行频率的“线程”,利用systick定时器判断执行时机。如果有两个任务赶在同一个时间执行,会根据任务在数组中的位置从小到大依次执行。
  • 最后再看一下systick定时器读取当前时间的实现方法
//以下程序位于 Drv_Bsp.c----------------------------------------------------------------------
static uint64_t SysRunTimeMs = 0;

void SysTick_Init(void )
{
	ROM_SysTickPeriodSet(ROM_SysCtlClockGet()/1000);
	ROM_SysTickIntEnable();
	ROM_SysTickEnable();
}
void SysTick_Handler(void)
{
	SysRunTimeMs++;
}
uint32_t GetSysRunTimeMs(void)
{
	return SysRunTimeMs;
}
uint32_t GetSysRunTimeUs(void)
{
	return SysRunTimeMs*1000 + (SysTick->LOAD - SysTick->VAL) * 1000 / SysTick->LOAD;
}
  • 8
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值