老公给人做网站结果网站卖假货,百度指数平台官网,wordpress 关闭自动更新,qq空间搬家wordpress前言 前不久#xff0c;我有位做测试的朋友转去做开发的工作#xff0c;面试遇到了一个问题#xff0c;他没明白#xff0c;打电话问了我。题目大概就是#xff1a;在单片机裸机开发时#xff0c;单片机要处理多个任务#xff0c;此时你的程序框架是怎样的呢#xff1f… 前言 前不久我有位做测试的朋友转去做开发的工作面试遇到了一个问题他没明白打电话问了我。题目大概就是在单片机裸机开发时单片机要处理多个任务此时你的程序框架是怎样的呢这其实是个经典面试问题我以前面试也被问过。答案一轮询系统代码结构如左右滑动查看全部代码int main(void)
{init_something();while(1){do_something1();do_something2();do_something3();}
}
这种结构大概是我们初学单片机的时候的代码结构。在没有外部事件驱动时可以较好使用。只答出了这种情况印象分估计会比较低多半凉凉。答案二前后台系统代码结构如该代码来自 《RT-Thread内核实现与应用开发实践指南》 左右滑动查看全部代码int flag1 0;
int flag2 0;
int flag3 0;int main(void)
{/* 硬件相关初始化 */HardWareInit();/* 无限循环 */for (;;) {if (flag1) {/* 处理事情 1 */DoSomething1();}if (flag2) {/* 处理事情 2 */DoSomethingg2();}if (flag3) {/* 处理事情 3 */DoSomethingg3();}}
}void ISR1(void)
{/* 置位标志位 */flag1 1;/* 如果事件处理时间很短则在中断里面处理如果事件处理时间比较长在回到后台处理 */DoSomething1();
}void ISR2(void)
{/* 置位标志位 */flag2 2;/* 如果事件处理时间很短则在中断里面处理如果事件处理时间比较长在回到后台处理 */DoSomething2();
}void ISR3(void)
{/* 置位标志位 */flag3 1;/* 如果事件处理时间很短则在中断里面处理如果事件处理时间比较长在回到后台处理 */DoSomething3();
}
此处中断称为前台main中的while循环称为后台。相比于循环系统这种方式相对可以提高外部事件的实时响应能力。可以回答出这种情况印象分大概一半以上会再细问。答案三升级版前后台系统软件定时器法以前学C语言时常常听到有人说指针是C语言的灵魂没学会指针就是没学会C语言。。后来学单片机时又听到有人说中断和定时器是单片机的灵魂没掌握中断与定时器就没学会单片机。。大佬们都那么说了那就拿定时器来搞点事情。定时器浑身都是宝本篇笔记我们来介绍使用定时器系统滴答定时器或者其它定时器来做的裸机框架。软件定时器法也有另一种说法时间片轮询法。可以回答出这种情况这场面试多半稳了。下面以STM32单片机为例看看这种方法的使用。站在巨人的肩膀上 开源项目—— MultiTimer 项目仓库地址https://github.com/0x1abin/MultiTimer1、MultiTimer 简介MultiTimer 是一个软件定时器扩展模块可无限扩展你所需的定时器任务取代传统的标志位判断方式 更优雅更便捷地管理程序的时间触发时序。2、MultiTimer 的demo左右滑动查看全部代码#include multi_timer.hstruct Timer timer1;
struct Timer timer2;void timer1_callback()
{printf(timer1 timeout!\r\n);
}void timer2_callback()
{printf(timer2 timeout!\r\n);
}int main()
{timer_init(timer1, timer1_callback, 1000, 1000); //1s looptimer_start(timer1);timer_init(timer2, timer2_callback, 50, 0); //50ms delaytimer_start(timer2);while(1) {timer_loop();}
}void HAL_SYSTICK_Callback(void)
{timer_ticks(); //1ms ticks
}
3、MultiTimer 的移植、剖析想要对MultiTimer 进行深入学习可阅读项目源码及如下这篇文章第6期 | MultiTimer一款可无限扩展的软件定时器自己动手丰衣足食 1、代码模板准备一个定时器可以是系统滴答定时器也可以是TIM定时器使用这个定时器拓展出多个软件定时器。比如我们系统中有三个任务LED翻转、温度采集、温度显示。此时我们可以使用一个硬件定时器拓展出3个软件定时器定义如下宏定义左右滑动查看全部代码#define MAX_TIMER 3 // 最大定时器个数
EXT volatile unsigned long g_Timer1[MAX_TIMER];
#define LedTimer g_Timer1[0] // LED翻转定时器
#define GetTemperatureTimer g_Timer1[1] // 温度采集定时器
#define SendToLcdTimer g_Timer1[2] // 温度显示定时器#define TIMER1_SEC (1) // 秒
#define TIMER1_MIN (TIMER1_SEC*60) // 分
在定时器初始化的时候也顺便给三个软件定时器进行初始化操作左右滑动查看全部代码/********************************************************************************************************
** 函数: TIM1_Init, 通用定时器1初始化
**------------------------------------------------------------------------------------------------------
** 参数: arr自动重装值 psc时钟预分频数
** 说明: 定时器溢出时间计算方法:Tout((arr1)*(psc1))/Ft
** 返回: void
********************************************************************************************************/
void TIM1_Init(uint16_t arr, uint16_t psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); /* 定时器TIM1初始化 */TIM_TimeBaseStructure.TIM_Period arr; TIM_TimeBaseStructure.TIM_Prescaler psc; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter0;TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); TIM_ClearFlag(TIM1,TIM_FLAG_Update );/* 中断使能 */TIM_ITConfig(TIM1,TIM_IT_Update, ENABLE ); /* 中断优先级NVIC设置 */NVIC_InitStructure.NVIC_IRQChannel TIM1_UP_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;NVIC_Init(NVIC_InitStructure); TIM_Cmd(TIM1, ENABLE); // 全局定时器初始化for(int i 0; i MAX_TIMER; i){g_Timer1[i] 0; }
}
在定时器中断中对这些软件定时器进行定时值做递减操作左右滑动查看全部代码/********************************************************************************************************
** 函数: TIM1_IRQHandler, 定时器1中断服务程序
**------------------------------------------------------------------------------------------------------
** 参数: 无
** 返回: 无
********************************************************************************************************/
void TIM1_UP_IRQHandler(void) //TIM1中断
{uint8 i;if (TIM_GetITStatus(TIM1, TIM_IT_Update) ! RESET) // 检查TIM1更新中断发生与否{//-------------------------------------------------------------------------------// 各种定时间器计时for (i 0; i MAX_TIMER; i) // 定时时间递减 if( g_Timer1[i] ) g_Timer1[i]-- ;TIM_ClearITPendingBit(TIM1, TIM_IT_Update); //清除TIMx更新中断标志 }
}
我们在各个定时任务中给这些软件定时器赋予定时值这些定时值递减到0则该任务会被触发执行比如左右滑动查看全部代码void Task_Led(void)
{//----------------------------------------------------------------// 等待定时时间if(LedTimer) return;LedTimer 1 * TIMER1_SEC;//----------------------------------------------------------------// LED任务主体LedToggle();
}void Task_GetTemperature(void)
{//----------------------------------------------------------------// 等待定时时间if(LedTimer) return;LedTimer 2 * TIMER1_SEC;//----------------------------------------------------------------// 温度采集任务主体GetTemperature();
}void Task_SendToLcd(void)
{//----------------------------------------------------------------// 等待定时时间if(LedTimer) return;LedTimer 2 * TIMER1_SEC;//----------------------------------------------------------------// 温度显示任务主体LcdDisplay();
}
如此一来每过1、2、4秒则分别触发LED翻转任务、温度采集任务、温度显示任务。这里配置的最小定时单位为1秒当然根据实际需要进行配置定时器初始化定时器初始化可以放在系统统一初始化函数里左右滑动查看全部代码/********************************************************************************************************
** 函数: SysInit, 系统上电初始化
**------------------------------------------------------------------------------------------------------
** 参数:
** 说明:
** 返回:
********************************************************************************************************/
void SysInit(void)
{CpuInit(); // 配置系统信息函数SysTickInit(); // 系统滴答定时器初始化函数UsartInit(115200); // 串口初始化函数波特率115200TIM1_Init(2000-1, 36000-1); // 定时周期1sLedInit(); // Led初始化TemperatureInit(); // 温度传感器初始化LcdInit(); // LCD初始化
}
此时我们的main函数就可以设计为int main(void)
{//----------------------------------------------------------------------------------------------- // 上电初始化函数SysInit(); //----------------------------------------------------------------------------------------------- // 主程序while (1){//----------------------------------------------------------------------------------------------- // 定时任务Task_Led();Task_GetTemperature(); Task_SendToLcd();}
}
主函数主要是进行系统上电的一些初始化操作接着是调用各定时任务函数。本demo使用定时器1来扩展出3个软件定时器如果TIM资源不够用可以换用系统滴答定时器来做。如其中时间基数可以根据实际需要进行调整。2、实践代入法套用以上模板分享我的一个实例需要思考及注意的问题是给每个任务的定时值设置多大合适这也是一些朋友有疑问的这只能是自己对自己的任务做考虑具体情况具体分析给经验值、调试调整。就如同常常有人问定义多大的数组合适在使用RTOS时每个线程的线程栈大小设置多大合适、优先级设置为多少合适这些都是需要我们自己进行思考的。有模板/轮子套用是好事但有些问题不能单单依靠模板否则有可能把自己给套进去。以上是以STM32为例的其它单片机也是可以用这样子的思想的包括51单片机。面对文首提到的面试问题若是可以提到使用软件定时器来处理进一步能清楚地表达出来再进一步能写出一些伪代码那这场面试多半是稳了。不仅仅是为了面试本文的方法是很经典的小编曾经接触的产品项目中就有用到很实用值得学习掌握。方法掌握多了实际应用的时候想用屠龙刀还是倚天剑根据实际情况选择使用即可。以上就是本次的分享如有错误欢迎指出谢谢。-END-推荐阅读 专辑|Linux文章汇总 专辑|程序人生 专辑|C语言嵌入式Linux微信扫描二维码关注我的公众号