工业产品好像都要写自检函数接口。
自检一般就需要对板子上的东西做一个测试调用。
比如你的板子有led,传感器,显示屏。我们就需要在这个自检时间里去检查led闪烁是否正常,传感器通信成果与否,失败了就要通过后续的显示屏显示出来。自检一般伴随一个倒计时,根据你传感器全部掉线且通信完成的时间去设置这个自检时间间隔,应该会好一些,反正就是如果你的传感器是坏的那自检结束应该显示故障或掉线等标志,如果是好的应该显示示值。如果是好的,自检完毕还没上线,这岂不是很难说的过去。
于是我们分解一下自检函数,应该办的事,首先刷一下屏吧,都有软件版本号吧,显示一下吧。流水灯该有吧,展示你的灯的好坏。然后就是去跟传感器通信,争取在自检时间结束之前通信完毕。
这个时间如何去获得呢,我们可以通过软件定时器获得。软件定时器的时间来源可以是任一个硬件定时器,硬件定时器选择一个最小时间计数值。比如1ms或1us。一般都是1ms。
于是我们就可以如下代码
// 系统自检,传感器预热、倒计时等都放在这个函数
void SysSelf (void) {
uint8_t_t pre_heat_cnt = 0,first_run = 0;
static uint16_t timecnt = 0, oled_brush = 0;
uint32_t time;
uint8_t_t led_sta = 0;
uint8_t_t buf[5] = {0};
CpuRstType = SelRest();
if (CpuRstType != RES_SOFT||close_dev==0x5AA5)
{
led_set (LED_ALL, LED_M_LOOP, systick_1ms_cnt());
while (1) {
#ifndef DIS_WDG_DBG_EN
// AnalogWDGFeed();//WDGFeed();
#endif
if (SoftTimerCheck (SYS_SELF_TIM) == 0) {
timecnt++;
SoftTimerSet (SYS_SELF_TIM, SYS_SELF_TIMOUT);
}
if (timecnt < 10) // 1秒的屏幕自检
{
lcd_disp_all();
} else if (timecnt >= 10 && ti
{
if (oled_brush == 0 ) {
lcd_clear_disp();
oled_brush = 1;
}
lcd_display_CHAR('V',1);
lcd_display_NUMBER(0,2,1);
lcd_display_NUMBER(1,3,0);
} else if ((timecnt >= 20 && timecnt < 270) && ((timecnt % 10) == 0
{
buf[0] = '-';
buf[3] = '-';
buf[1] = ((270 - timecnt) / 100) + '0';
buf[2] = ((270 - timecnt) % 100 / 10) + '0';
lcd_display_CHAR(buf[1],1);
lcd_display_CHAR(buf[2],2);
lcd_display_CHAR(buf[0],0);
lcd_display_CHAR(buf[3],3);
}
led_update_loop();
if (timecnt >= 270) // 27s 结束自检
{
timecnt = 0;
break;
}
pre_heat_cnt = timecnt/10;
// 开机自检阶段查询传感参数
if(first_run == 0)
{
first_run = 1;
sen1_self_flag = 1;
sen2_self_flg = 1;
sen1_sensor_comm();
sen2_sensor_comm();
}
if(pre_heat_cnt > 8 && first_run == 1)
{
if(Get_sen1_state() != STA_LP){
sen1_sensor_comm();
}
}
if(first_run == 1){
if(get_sen2_state() != STA_LP_){
press_sen2_comm();
}
}
}
sen1_self_flg = 0;
sen2_self_flag = 0;
led_switch(Disable);
}
clear_close_flg();
led_set (LED_ALL, LED_M_OFF, systick_1ms_cnt());
led_set (LED_RUN, LED_M_BLINK_FAST, systick_1ms_cnt());
led_update();
wakeup_Reinit();
power_state = 1;
}
大致流程是什么呢?1s的lcd全显示,看lcd是否正常,然后就是1s的版本号,然后就是流水灯。在2s后进行传感器通信,并不断的倒计时,传感通信利用有限状态机实现,在这里进行不断的调用,如果到最后一个状态了它会将状态机状态转换会第一状态,为了避免重复通信,就取消进入再次进入通信状态机。通信完成,无论传感器在不在线,都要把自检标志清零,因为在状态机里用到了这些标志。然后就是标志已经开机了。软件定时器的实现很简单,给出一个示例,利用滴答定时器来进行计数,以后再更新软件定时器的实现。
void SysTick_Handler (void) //滴答定时器中断服务函数(分为两部分,一部分是延时状态的处理,另一部分是时间片的处理,时间片内任务没有进行滴答延时所以不参与从延时列表的操作)
{
tNode *node;
tTask *task;
uint32_t status=tTaskEnterCritical(); //进入临界区
for(node=tTaskDelayedList.firstnode; node!= &(tTaskDelayedList.headNode); node=node->nextNode) //遍历延时表的所有节点
{
task=tNodeParent(node, tTask, delayNode); //根据宏定义计算该延时节点的任务节点首地址
if(--task->delayTicks==0) //如果该任务的延时计数值为0 , 表示可以加入就绪列表,
{
tTimeTaskWakeUp(task); //将该任务的延时节点从延时列表删除,并将延时状态取消
tTaskSchedRdy(task);// 把该任务加入就绪列表,位图置1
}
}
if(--(currentTask->slice)==0) //Time slice round-robin 如果该任务的时间片计数值为0 表示可以切换到该优先级的下一个任务
{
if(tListCount(&taskTable[currentTask->prio])>0) //如果该有限级任务数大于0
{
tListRemoveFirst(&taskTable[currentTask->prio]); //把该任务与链表从头部断开连接
tListAddLast(&taskTable[currentTask->prio], &(currentTask->linkNode)); //把该任务重新连接到链表尾部
currentTask->slice=TINYOS_SLICE_MAX;//重新设置任务的时间片 ,以便下一次再次减到0,设置完之后就可以开启调度,切换到下一个时间片任务
}
}
#if TINYOS_ENABLE_CPUUSAGE_STAT == 1
tickCount++;//每10ms进入一次中断查询 tick达到100次算1s 一个周期
checkCpuUsage();//进入一次滴答中断就检查一次CPU
#endif
tTaskExitCritical(status);
#if TINYOS_ENABLE_TIMER == 1
tTimerModuleTickNotify(); // 每经过10ms进入中断一次标志着一次硬定时
#endif
#if TINYOS_ENABLE_HOOKS == 1
tHooksSysTick();
#endif
tTaskSched(); //Schedule the task
}
这里跟软件定时器相关的就是tickCount,这里去计数,软件定时器的接口去哪些定时项有没有到时间,当然这还涉及一个计数值满了回滚到0的情况,要另外考虑。
个人心得体会,如有错误还请指正。