Конфигурация таймеров - расчет

Конфигурируя таймер, в большинстве случаев нужно посчитать предделитель и значение регистра ARR. Считается это не сложно, но тем не менее 5-10 драгоценных минут надо потратить. Кроме того, так как оба эти значения зависят от частоты, поменял частоту – пересчитывай опять.
Да и выглядит инициализация с такими магическими числами не сильно наглядно.

Вобщем мне это надоело и я написал пару макросов, которые атоматом считают значения прескейлера и ARR для 8-ми битных таймеров у которых предделитель может принимать значения, равные степеням 2-ки:

#define LOG_BASE_2(x) ((x) <= 1 ? 0 : (x) <= 2 ? 1 : (x) <= 4 ? 2 : (x) <= 8 ? 3 : (x) <= 16 ? 4 : (x) <= 32 ? 5 : (x) <= 64 ? 6 : (x) <= 128 ? 7 : (x) <= 256 ? 8 : \
  (x) <= 512 ? 9 : (x) <= 1024 ? 10 : (x) <= 2048 ? 11 : (x) <= 4096 ? 12 : (x) <= 8192 ? 13 : (x) <= 16384 ? 14 : 15)

#define TIM_PSCR(x) LOG_BASE_2((((x) * F_CPU) / 256) + 1)
#define TIM_ARR(x) ((x) * F_CPU / (1 << TIM_PSCR((x))))


Пример инициализации таймера, генерирующего прерывание раз в миллисекунду:
#define F_CPU       16    //CPU Frequency (MHz)
#define TICK_PERIOD 1000  //1000us (1ms)
…
  //Init tick timer
  TIM4_IER_UIE    = 1;  // Update interrupt enabled
  TIM4_PSCR_PSC   = TIM_PSCR(TICK_PERIOD);
  TIM4_ARR        = TIM_ARR(TICK_PERIOD);
  TIM4_CR1_CEN    = 1;  // Start timer

Хотя, возможно я изобрел велосипед…

Update: Благодаря совету уважаемого John все заметно упростилось.
Поправил макросы вверху.
  • +1
  • 30 ноября 2012, 18:13
  • Victor

Комментарии (10)

RSS свернуть / развернуть
«У вас не хватает рейтинга и силы для голосования!», а так плюс однозначно, буду юзать ваш велосипед)
только дурацкий вопрос: а F_CPU нигде не дефайнится автоматом?
+1
Откуда бы он его знал? =)
0
так потому и «дурацкий») уже снова утро, а проект всё ещё не взлетает, голова не работает совершенно, но уже отказывается доверять своим размышлениям)
0
Макрос POWER_BASE_2 можно упростить до (((x) <= 15)? (1 << (x)): (1 << 15))
+2
  • avatar
  • John
  • 30 ноября 2012, 20:55
Вот же ж блин, вот это я протупил :)
POWER_BASE_2 хронологически был написан после LOG_BASE_2, потому тупо сделал его инверсным
Спасибо!
0
Поправил статью.
На самом деле макрос POWER_BASE_2 вообще не нужне. Даже для контроля аргумента — так как он всегда вызывается для результата LOG_BASE_2, то аргумент всегда будет <= 15
0
Раз пошла такая пьянка — изначально думал вместо логарифма использовать следующий макрос, который оставляет только старший установленный бит в байте:
#define HIGHEST_BIT(x) \
  (((x) | ((x) >> 1) | (((x) | ((x) >> 1)) >> 2) | (((x) | ((x) >> 1) | (((x) | ((x) >> 1)) >> 2)) >> 4)) ^ \
  (((x) | ((x) >> 1) | (((x) | ((x) >> 1)) >> 2) | (((x) | ((x) >> 1) | (((x) | ((x) >> 1)) >> 2)) >> 4)) >> 1))

Но так как он малочитаемый и сложно масштабируемый (если поддержать 16 бит — вообще простыня получится), то оказалось проще написать логарифм
0
Можно так, пример для 32 бит:

#define FLP2_1(x) ((x) | ((x) >> 1))
#define FLP2_2(x) (FLP2_1(x) | (FLP2_1(x) >>  2))
#define FLP2_3(x) (FLP2_2(x) | (FLP2_2(x) >>  4))
#define FLP2_4(x) (FLP2_3(x) | (FLP2_3(x) >>  8))
#define FLP2_5(x) (FLP2_4(x) | (FLP2_4(x) >> 16))
#define FLP2(x)   (FLP2_5(x) ^ (FLP2_5(x) >>  1))
0
Да, так нагляднее, но все-равно понять сналету сложно.
По поводу поддержки 16 бит и более у меня была другая идея:
Если есть макрос HIGHEST_BIT_ для 8 бит, то можно так:
#define HIGHEST_BIT(x) HIGHEST_BIT_((x)>> 8) > 0 ? HIGHEST_BIT_((x) >> 8) << 8 : HIGHEST_BIT_(x)

Для 32 бит еще один в одну строчку и т.д.
0
Спасибо! Удобно!:)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.