sys_timer源自timer1 的 1ms 时钟定时。sys_timer是软件定时器,注册的函数是在线程执行的,优先级依赖于注册的线程的优先级。
sys_timer在系统线程中执行, 几乎可执行所有的操作。
sys_timer允许系统进入低功耗,但是定时时间到了会唤醒系统处理任务。另外,sys_timer_add() 定时循环函数,会影响下次进低功耗的时间点,如果周期太短会影响功耗。
sys_timer单位是 ms,但是是以 10ms 步进的。如果设置5ms,结果是 等同于 10ms 的。sys_timer是以 10ms 为系统滴答的。
所以,当定时参数设置的比较小时(比如10ms),系统基本不会进入低功耗,此时测试芯片电流大约100uA以上甚至更高,正常睡眠状态电流20uA。所以使用时必须避免此类情况发生。
/**
* @brief 添加ms级系统非timeout类型定时任务。
*
* @param [in] priv 定时任务处理函数的输入参数
* @param [in] func 定时任务处理函数
* @param [in] msec 定时时间
*
* @return 定时器分配的ID
*
* @note
* 1、系统会进入低功耗,节拍不会丢失,定时结束会唤醒系统;
* 2、sys_timer由systimer线程提供时基,属于同步接口,
* 也就是说在哪个线程add的sys_timer,当定时时基到了,
* systimer线程会发事件通知对应的add线程响应(回调函数被执行);
* 3、与sys_timer_del成对使用。
*/
u16 sys_timer_add(void *priv, void (*func)(void *priv), u32 msec);
/**
* @brief 删除ms级非timeout类型定时任务。
*
* @param [in] id 要删除的任务ID
*
* @note
* 1、与sys_timer_add成对使用。
*/
void sys_timer_del(u16);
/**
* @brief 添加ms级系统timeout类型定时任务。
*
* @param [in] priv 定时任务处理函数的输入参数
* @param [in] func 定时任务处理函数
* @param [in] msec 定时时间
*
* @return 定时器分配的id号
*
* @note
* 1、系统会进入低功耗,节拍不会丢失,定时结束会唤醒系统;
* 2、sys_timeout由systimer线程提供时基,属于同步接口,
* 也就是说在哪个线程add的sys_timeout,定时时基到了
* systimer线程会发事件通知对应的add线程响应(回调函数被执行);
* 3、timeout回调只会被执行一次;
* 4、与sys_timerout_del成对使用。
*/
u16 sys_timeout_add(void *priv, void (*func)(void *priv), u32 msec);
/**
* @brief 删除ms级timeout类型定时任务。
*
* @param [in] id 要删除的任务ID
*
* @note
* 1、与sys_timerout_add成对使用。
*/
void sys_timeout_del(u16);
/**
* @brief 重置ms级定时任务定时时间。
*
* @param [in] id 要修改的任务ID
*
* @note
* 1、重置之后重新计时。
*/
void sys_timer_re_run(u16 id);
/**
* @brief 修改ms级定时任务处理函数的输入参数。
*
* @param [in] id 要修改的任务ID
* @param [in] priv 处理函数的输入参数
*/
void sys_timer_set_user_data(u16 id, void *priv);
/**
* @brief 获取ms级定时任务处理函数的输入参数的值。
*
* @param [in] id 要获取处理函数的输入参数值的任务ID
*
* @return 返回add时的私有参数
*
* @note
* 1、如果有通过sys_timer_set_user_data重新设置私有参数,
* 则返回的是设置后的私有参数。
*/
void *sys_timer_get_user_data(u16 id);
/*-----------------------------------------------------------*/
/*
* System Usec Timer
*/
int sys_timer_modify(u16 id, u32 msec);
int sys_usec_timer_add(void *priv, void (*func)(void *priv), u32 usec);
void sys_usec_timer_schedule(struct sys_cpu_timer *);
void sys_usec_timer_set(int _t, u32 usec);
void sys_usec_timer_del(int);
void sys_timer_dump_time(void);
u32 sys_timer_get_ms(void);
usr_timer是增加硬件定时器。如果优先级取1,则芯片不会进入低功耗。如果优先级取0,芯片可以进入低功耗,但此时定时周期会变(可能多加了睡眠时间)。
所以,如果用了user_timer,那就难免不准。误差是由于芯片进入低功耗造成的,这是不可避免的。
此种定时器,注册的函数是在中断函数里面回调的,不可添加延时,或耗时的操作。
user_timer单位是 ms, 是以 1ms 步进的。
/**
* @brief usr_timer定时扫描增加接口
*
* @param [in] priv 私有参数
* @param [in] func 定时扫描回调函数
* @param [in] msec 定时时间, 单位:毫秒
* @param [in] priority 优先级,范围:0/1
*
* @return 定时器分配的id号
*
* @note
* 1、usr_timer的参数priority(优先级)为1,使用该类定时器,系统无法进入低功耗;
* 2、usr_timer的参数priority(优先级)为0,使用该类定时器,系统低功耗会忽略该节拍,节拍不会丢失,但是定时周期会变;
* 3、usr_timer属于异步接口, add的时候注册的扫描函数将在硬件定时器中时基到时候被调用;
* 4、对应释放接口usr_timer_del。
*/
u16 usr_timer_add(void *priv, void (*func)(void *priv), u32 msec, u8 priority);
/**
* @brief usr_timer超时增加接口
*
* @param [in] priv 私有参数
* @param [in] func 超时回调函数
* @param [in] msec 定时时间, 单位:毫秒
* @param [in] priority 优先级,范围:0/1
*
* @return 定时器分配的id号
*
* @note
* 1、usr_timerout的参数priority(优先级)为1,使用该类定时器,系统无法进入低功耗;
* 2、usr_timerout的参数priority(优先级)为0,使用该类定时器,系统低功耗会忽略该节拍,节拍不会丢失,但是定时周期会变;
* 3、usr_timerout属于异步接口, add的时候注册的扫描函数将在硬件定时器中时基到时候被调用;
* 4、对应释放接口usr_timerout_del;
* 5、timeout回调只会被执行一次。
*/
u16 usr_timeout_add(void *priv, void (*func)(void *priv), u32 msec, u8 priority);
/**
* @brief usr_timer修改定时扫描时间接口
*
* @param [in] id usr_timer_add时分配的id号
* @param [in] msec 定时时间,单位:毫秒
*/
int usr_timer_modify(u16 id, u32 msec);
/**
* @brief usr_timerout修改超时时间接口
*
* @param [in] id usr_timerout_add时分配的id号
* @param [in] msec 定时时间,单位:毫秒
*/
int usr_timeout_modify(u16 id, u32 msec);
/**
* @brief usr_timer删除接口
*
* @param [in] id usr_timer_add时分配的id号
*
* @note
* 1、注意与usr_timer_add成对使用。
*/
void usr_timer_del(u16 id);
/**
* @brief usr_timeout删除接口
*
* @param [in] id usr_timerout_add时分配的id号
*
* @note
* 1、注意与usr_timerout_add成对使用。
*/
void usr_timeout_del(u16 id);
/**
* @brief usr_time输出调试信息
*
* @note
* 1.调试时可用;
* 2.将输出所有被add定时器的id及其时间(msec)。
*/
void usr_timer_dump(void);
注意,还有使用sys_hi_timer接口的,其实在time.h文件里已经预定义了,sys_hi_timer等于user_timer。
sys_hi_timer_add() 定时循环函数,会导致不进低功耗,直到主动删除
sys_hi_timeout_add() 定时超时执行一次函数
sys_s_hi_timer_add() 定时循环函数,不影响系统进低功耗,周期会变,建议使用此种定时器
sys_s_hi_timerout_add() 定时超时执行一次函数
#define sys_hi_timer_schedule()\
usr_timer_schedule()
#define sys_hi_timer_add(a, b, c)\
usr_timer_add(a, b, c, 1)
#define sys_hi_timeout_add(a, b, c)\
usr_timeout_add(a, b, c, 1)
#define sys_hi_timer_modify(a, b)\
usr_timer_modify(a, b)
#define sys_hi_timeout_modify(a, b)\
usr_timeout_modify(a, b)
#define sys_hi_timer_del(a)\
usr_timer_del(a)
#define sys_hi_timeout_del(a)\
usr_timeout_del(a)
#define sys_s_hi_timer_add(a, b, c)\
usr_timer_add(a, b, c, 0)
#define sys_s_hi_timerout_add(a, b, c)\
usr_timeout_add(a, b, c, 0)
#define sys_s_hi_timer_modify(a, b)\
usr_timer_modify(a, b)
#define sys_s_hi_timeout_modify(a, b)\
usr_timeout_modify(a, b)
#define sys_s_hi_timer_del(a)\
usr_timer_del(a)
#define sys_s_hi_timeout_del(a)\
usr_timeout_del(a)
在adc_api.c文件里找到void _adc_init(u32 sys_lvd_en)函数。该函数是ADC功能初始化的函数。在其末尾添加了一个定时扫描任务void adc_scan(void *priv)。原来代码如下:
void _adc_init(u32 sys_lvd_en)
{
memset(adc_queue, 0xff, sizeof(adc_queue));
memset(vbg_value_array, 0x0, sizeof(vbg_value_array));
memset(vbat_value_array, 0x0, sizeof(vbat_value_array));
JL_ADC->CON = 0;
adc_add_sample_ch(AD_CH_LDOREF);
adc_set_sample_freq(AD_CH_LDOREF, PMU_CH_SAMPLE_FREQ);
adc_add_sample_ch(AD_CH_VBAT);
adc_set_sample_freq(AD_CH_VBAT, PMU_CH_SAMPLE_FREQ);
adc_add_sample_ch(AD_CH_DTEMP);
adc_set_sample_freq(AD_CH_DTEMP, PMU_CH_SAMPLE_FREQ);
u32 sum_ad = 0;
adc_sample(AD_CH_LDOREF);
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i ++) {
sum_ad += adc_wait_pnd();
}
sum_ad /= PMU_CH_VALUE_ARRAY_SIZE;
adc_value_push(vbg_value_array, sum_ad);
printf("vbg_adc_value = %d\n", adc_value_avg(vbg_value_array));
sum_ad = 0;
adc_sample(AD_CH_VBAT);
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i ++) {
sum_ad += adc_wait_pnd();
}
sum_ad /= PMU_CH_VALUE_ARRAY_SIZE;
adc_value_push(vbat_value_array, sum_ad);
printf("vbat_adc_value = %d\n", adc_value_avg(vbat_value_array));
printf("vbat = %d mv\n", adc_get_voltage(AD_CH_VBAT) * 4);
sum_ad = 0;
adc_sample(AD_CH_DTEMP);
for (int i = 0; i < PMU_CH_VALUE_ARRAY_SIZE; i ++) {
sum_ad += adc_wait_pnd();
}
sum_ad /= PMU_CH_VALUE_ARRAY_SIZE;
dtemp_voltage = adc_value_to_voltage(adc_value_avg(vbg_value_array), sum_ad);
printf("dtemp_adc_value = %d\n", sum_ad);
printf("dtemp = %d mv\n", dtemp_voltage);
request_irq(IRQ_SARADC_IDX, 0, adc_isr, 0);
usr_timer_add(NULL, adc_scan, 5, 0); //2ms
/* sys_timer_add(NULL, adc_scan, 10); //2ms */
/* void adc_test(); */
/* usr_timer_add(NULL, adc_test, 1000, 0); //2ms */
/* extern void wdt_close(); */
/* wdt_close(); */
/* */
/* while(1); */
}
上面函数里第50行:
usr_timer_add(NULL, adc_scan, 5, 0);//每隔5ms切换一次ADC扫描通道
把该句usr_timer注释掉,然后使用下面一句的sys_timer定时代替
sys_timer_add(NULL, adc_scan, 10);
此时测试你会发现系统根本无法进入低功耗状态,电流也在0.1ma-2ma左右。原因就是定时周期太短了,系统来不及进入低功耗,只好一直工作。