JL JieLi AC696N Series Chip Development – Timer Detailed Explanation: Differences and Selection Between sys_timer and usr_timer
Introduction
Timers seem simple, but using them in the wrong place either increases power consumption or causes timing inaccuracies—and debugging can be quite troublesome. The JL JieLi AC696N SDK provides two sets of timers: sys_timer and usr_timer. When first getting started, it's easy to get confused—when to use which? Why does my timer become inaccurate after entering sleep? Can I perform work in the callback? I encountered all these issues while debugging low-power solutions on the AC696N development board. Here's a summary of the differences and selection logic, so you can directly reference it when writing code.
Chip Product Introduction
I. System Timer (sys_timer) – "Software Timer"
Features: Managed by the systimer thread, synchronous interface. The callback executes in the same thread where the timer was added.
Low Power: System can sleep; the timer will wake the system when it expires, without losing ticks.
Use Case: Suitable for general tasks that need periodic execution and can tolerate slight delays.
Interfaces: sys_timer_add, sys_timer_del, sys_timeout_add (one-shot)
II. User Timer (usr_timer) – "Hardware Timer"
Features: Driven by hardware timer, asynchronous interface, callback executes in interrupt context.
Priority Impact:
- 🔹 priority=1: System cannot enter low power, timing is precise
- 🔹 priority=0: System can sleep, but timing period may lengthen due to sleep
- 🔹 Use Case: Suitable for timing tasks requiring precision and fast response, or one-shot timeouts.
- 🔹 Interfaces: usr_timer_add, usr_timer_del, usr_timeout_add
III. Timer Applications
1) Timing Function
Example using led7_timer.c on AC696:
static void timer2_isr()
{
//local_irq_disable(); // Enable when extremely accurate timing is needed
TIMER_CON |= BIT(14); // Clear interrupt flag
// User custom code
//......
//local_irq_enable(); // Enable when extremely accurate timing is needed
}
int led7_timer_init()
{
u32 prd_cnt;
u8 index;
printf("------------%s :%d", __func__, __LINE__);
for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
prd_cnt = TIMER_UNIT_MS * (APP_TIMER_CLK / 1000) / timer_div[index];
if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
break;
}
}
__this->index = index;
__this->prd = prd_cnt;
TIMER_CNT = 0;
TIMER_PRD = prd_cnt; //1ms
request_irq(TIMER_VETOR, 6, timer2_isr, 0); //Highest priority 7, enable when extremely accurate timing needed
TIMER_CON = (index << 4) | BIT(0) | BIT(3);
printf("PRD : 0x%x / %d", TIMER_PRD, clk_get("timer"));
return 0;
}
2) Capture Function
• Reference Code
// Based on AC696 program. Some I/O pins may not support this function—test first, then design PCB!!!!!!
#define TIMER5 7 // see irflt.h
#define CATCH_TIMER TIMER5
#define CATCH_IRQ_TIME_IDX IRQ_TIME5_IDX
#define CATCH_TIME_REG JL_TIMER5
#define CATCH_GPIO IO_PORTB_06
int num = 0;
void timer_ms()
{
static int flag = 0;
if(flag){
gpio_set_pull_down(IO_PORTA_02,0);
gpio_set_pull_up(IO_PORTA_02,0);
gpio_set_direction(IO_PORTA_02,1);
flag = 0;
}
else{
gpio_set_pull_down(IO_PORTA_02,0);
gpio_set_pull_up(IO_PORTA_02,0);
gpio_set_direction(IO_PORTA_02,0);
gpio_set_output_value(IO_PORTA_02,0);
putchar('0');
flag = 1;
}
}
__interrupt
void timer_catch_isr(void)
{
CATCH_TIME_REG->CON |= BIT(14);
u16 bCap1 = CATCH_TIME_REG->PRD; // Time from last interrupt to this one, can be used for timing
CATCH_TIME_REG->CNT = 0;
num++; // Count
}
void timer_100ms()
{
printf("num == %d\n",num);
num = 0;
}
void timer_catch_init()
{
printf("timer_catch_init\n");
// I/O configuration
gpio_irflt_in(CATCH_GPIO);
gpio_set_direction(CATCH_GPIO, 1);
gpio_set_die(CATCH_GPIO, 1);
gpio_set_pull_up(CATCH_GPIO, 1);
gpio_set_pull_down(CATCH_GPIO, 0);
// Clock configuration
SFR(JL_IOMAP->CON0, 5, 3, CATCH_TIMER); // TIMER5:7 in irflt.h
CATCH_TIME_REG->CON = 0;
CATCH_TIME_REG->CON |= (0b10 << 2); // Select crystal oscillator clock source: 24MHz
CATCH_TIME_REG->CON |= (0b0001 << 4); // Clock source divide by 4
// Set period, initial value, timer mode
CATCH_TIME_REG->PRD = OSC_Hz / (4 * 1000);
CATCH_TIME_REG->CNT = CATCH_TIME_REG->PRD;
CATCH_TIME_REG->CON |= BIT(14);
CATCH_TIME_REG->CON |= (0b11 << 0);
request_irq(CATCH_IRQ_TIME_IDX, 5, timer_catch_isr, 0);
sys_timer_add(NULL, timer_100ms, 100); // Periodically read interrupt count
sys_timer_add(NULL, timer_ms, 1); // Generate waveform for interrupt capture (not precise, not exactly 1ms)
}
Selection Guide
- 🔹 Need low power and low real-time requirements → Use sys_timer
- 🔹 Need precise timing or fast response, power consumption not the primary concern → Use usr_timer (priority=1)
- 🔹 Need one-shot timeout functionality → Use sys_timeout_add or usr_timeout_add
Summary
In simple terms, prioritize sys_timer in regular code—it doesn't affect sleep, is power-friendly, and covers most scenarios. If you need millisecond-level precision or the callback needs to handle urgent tasks, use usr_timer, but remember that when priority=1, the system cannot sleep, increasing power consumption—don't keep it running long-term in production. Both support one-shot timeouts; use whichever you prefer. I recommend trying both timers on the AC696N development board, printing the callback thread and sleep behavior via serial to intuitively understand the difference—then you won't hesitate when selecting timers.
评论
发表评论