Добавить в корзинуПозвонить
Найти в Дзене

Разработка для чипов серии JL JieLi AC696N – Подробное объяснение таймеров: различия и выбор между sys_timer и usr_timer

Введение
Таймеры кажутся простыми, но их неправильное использование либо увеличивает энергопотребление, либо приводит к неточностям синхронизации – и отладка может быть весьма болезненной. SDK для JL JieLi AC696N предоставляет два набора таймеров: sys_timer и usr_timer. При первом знакомстве легко запутаться – когда какой использовать? Почему мой таймер начинает работать неточно после перехода в сон? Можно ли выполнять работу в callback-функции? Со всеми этими вопросами я столкнулся при отладке решений с низким энергопотреблением на плате разработки AC696N. Вот краткое изложение различий и логики выбора, чтобы можно было напрямую обращаться к нему при написании кода. Информация о чипе ЧипРазрядностьРежим захватаРеверсивный выводIOMAPAC696X32Вверх/ВнизЕстьПоддерживается I. Системный таймер (sys_timer) – "Программный таймер"
Особенности: Управляется потоком systimer, синхронный интерфейс. Callback-функция выполняется в том же потоке, где был добавлен таймер.
Низкое энергопотребление: С

Введение
Таймеры кажутся простыми, но их неправильное использование либо увеличивает энергопотребление, либо приводит к неточностям синхронизации – и отладка может быть весьма болезненной. SDK для JL JieLi AC696N предоставляет два набора таймеров: sys_timer и usr_timer. При первом знакомстве легко запутаться – когда какой использовать? Почему мой таймер начинает работать неточно после перехода в сон? Можно ли выполнять работу в callback-функции? Со всеми этими вопросами я столкнулся при отладке решений с низким энергопотреблением на плате разработки AC696N. Вот краткое изложение различий и логики выбора, чтобы можно было напрямую обращаться к нему при написании кода.

Информация о чипе

ЧипРазрядностьРежим захватаРеверсивный выводIOMAPAC696X32Вверх/ВнизЕстьПоддерживается

-2

I. Системный таймер (sys_timer) – "Программный таймер"
Особенности: Управляется потоком systimer, синхронный интерфейс. Callback-функция выполняется в том же потоке, где был добавлен таймер.
Низкое энергопотребление: Система может спать; таймер разбудит систему по истечении времени, тики не теряются.
Применение: Подходит для задач, требующих периодического выполнения и допускающих небольшие задержки.
Интерфейсы: sys_timer_add, sys_timer_del, sys_timeout_add (однократный)

II. Пользовательский таймер (usr_timer) – "Аппаратный таймер"
Особенности: Управляется аппаратным таймером, асинхронный интерфейс, callback-функция выполняется в контексте прерывания.
Влияние приоритета:

  • priority=1: Система не может переходить в режим низкого энергопотребления, синхронизация точная
-3

  • priority=0: Система может спать, но период таймера может увеличиваться из-за сна
    Применение: Подходит для задач, требующих точной синхронизации и быстрого реагирования, или однократных задержек.
    Интерфейсы: usr_timer_add, usr_timer_del, usr_timeout_add

III. Применение таймеров

1) Функция синхронизации
Пример на основе файла led7_timer.c для AC696:

static void timer2_isr()
{
//local_irq_disable(); // Включить, когда требуется极高 точность синхронизации
TIMER_CON |= BIT(14);
// Сброс флага прерывания
// Пользовательский код
//......
//local_irq_enable(); // Включить, когда требуется точность синхронизации
}

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);
//Наивысший приоритет 7, включить при необходимости точности
TIMER_CON = (index << 4) | BIT(0) | BIT(3);

printf("PRD : 0x%x / %d", TIMER_PRD, clk_get("timer"));

return 0;
}

2) Функция захвата
• Пример кода

// На основе программы для AC696. Некоторые выводы могут не поддерживать эту функцию – сначала протестируйте, потом проектируйте плату!!!!!!
#define TIMER5 7
// см. 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;
// Время от предыдущего прерывания до текущего, можно использовать для измерения
CATCH_TIME_REG->CNT = 0;
num++;
// Счетчик
}

void timer_100ms()
{
printf("num == %d\n",num);
num = 0;
}

void timer_catch_init()
{
printf("timer_catch_init\n");
// Конфигурация ввода-вывода
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);
// Конфигурация тактирования
SFR(JL_IOMAP->CON0, 5, 3, CATCH_TIMER);
// TIMER5:7 в irflt.h
CATCH_TIME_REG->CON = 0;
CATCH_TIME_REG->CON |= (0b10 << 2);
// Выбор источника тактового сигнала: кварц 24MHz
CATCH_TIME_REG->CON |= (0b0001 << 4);
// Делитель частоты 4
// Установка периода, начального значения, режима таймера
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);
// Периодическое чтение счетчика прерываний
sys_timer_add(NULL, timer_ms, 1);
// Генерация сигнала для захвата прерывания (неточно, не ровно 1мс)
}

Руководство по выбору

  • Требуется низкое энергопотребление и нет высоких требований к реальному времени → Используйте sys_timer
  • Требуется точная синхронизация или быстрое реагирование, энергопотребление не критично → Используйте usr_timer (priority=1)
  • Требуется однократная задержка → Используйте sys_timeout_add или usr_timeout_add

Резюме
Проще говоря, в обычном коде отдавайте предпочтение sys_timer – он не влияет на сон, энергоэффективен и покрывает большинство сценариев. Если требуется точность до миллисекунды или callback-функция должна обрабатывать срочные задачи, используйте usr_timer, но помните: при priority=1 система не может спать, что увеличивает энергопотребление – не оставляйте его включенным надолго в готовом продукте. Однократные задержки поддерживаются обеими системами – используйте ту, которая удобнее. Рекомендую опробовать оба типа таймеров на плате разработки AC696N, вывести через последовательный порт информацию о потоке callback-функции и поведении при сне – так вы интуитивно поймете разницу, и в дальнейшем выбор не будет вызывать затруднений.