超时概念
程序运行中,操作后需要等待某个状态,正常返回,但实际未按预期状态出现,此时应该超时异常返回,不然会造成死循环。
实现原理
超时计数时间轴如下:
0到N为微秒计数,N到M为毫秒计数。计数从0开始,直到M表示超时异常返回,中间均为正常返回。
基础:
以vxwork系统为例,taskDelay(0)可为微秒级延时(cpu任务优先级调度);taskDelay(1)可为毫秒级延时(cpu释放一个时钟片)。
对于一个超时检测来说,超时计数粒度越小,返回越及时,保证运行速度,但会加重cpu负担;相反超时计数粒度越大,返回越慢,运行速度慢,但会释放更多的cpu。
目标:
a、保证程序正常运行时调整超时计数在微秒计数(0 -N)内,达到运行速度;
b、保证程序异常运行时调整超时计数在毫秒计数(N-M)内,达到cpu释放。
实现:
1、初始化超时M,为一个大于0的较小的值,最好按实际测试情况给定。毫秒计数(N-M)固定不变。
2、正常运行时,正常返回,若此时超时计数用到了毫秒计数(N-M),自动按步长L加到M上,如此多次调整后,超时计数最终落在微秒计数(0 -N)内。
3、异常运行时,超时返回,此时必定落在M点上,为了减小cpu的消耗,N和M同时按步长L减小,让超时计数尽可能落在N-M内。
示例:
可用在i2c、spi、eth等总线轮询等待情况。
/*
* Function to ensure that the previous transaction was completed before
* initiating a new transaction. It can also be used in polling mode to
* check status of completion of a command
* 注意: 此超时机制会自动调整超时次数。
* 1,超时次数过短的话会自动增加超时次数,保证读取速率。
* 2,总线异常的话会调整超时次数到最小值,控制cpu占用率。
*/
#define I2C_REG_R_TIMES_MIN 500
unsigned int i2c_reg_r_times = 1000;/* 测试发现大约为1200,可自动向上调整修正 */
unsigned int i2c_reg_r_times_ajust_step = 10;
static int iproc_i2c_startbusy_wait(unsigned char bus)
{
unsigned int reg_val;
unsigned int check_times = 0;
unsigned int check_times_now = i2c_reg_r_times;
/* 末尾用10ms */
unsigned int check_times_10ms = check_times_now - 3;
do{
/* 检查ok */
reg_val = iproc_i2c_reg_read(bus, CCB_SMB_MSTRCMD_REG);
if(0 == (reg_val & CCB_SMB_MSTRSTARTBUSYCMD_MASK)){
/* 1,用到10ms延时,说明微秒级延时不够,需增加超时次数 */
if(check_times > check_times_10ms){
i2c_reg_r_times += i2c_reg_r_times_ajust_step;
}
return 0;
}
/* 10ms延时 */
if(check_times > check_times_10ms){
taskDelay(1);
}
/* 微秒级延时 */
else{
taskDelay(0);
}
}while(check_times++ < check_times_now);
/* 2,超时,说明总线异常,大幅减少超时次数 */
if(i2c_reg_r_times > I2C_REG_R_TIMES_MIN){
i2c_reg_r_times -= i2c_reg_r_times_ajust_step * 4;
}
return -ETIMEDOUT;
}