Работа счетчиков/таймеров в C8051(Silabs)

В силу профессиональной деятельности я работаю исключительно с МК Silabs, поэтому буду описывать только частные случаи с их участием. Однако общие принципы и терминология, я уверен, применима и для других процессоров этой архитектуры, да и вообще для всех.

Теперь к теме этой небольшой заметки. Уже несколько раз, описывая инициализация контроллера в проекте, я спотыкаюсь об тему таймера и его четкой работы, и начинаю лезть в даташит и примеры, перечитывать уже пройденное.
Silabs предлагает вообще-то очень удобную на мой взгляд утилиту конфигурирования МК, в которой собраны все данные о портах, осцилляторах и периферии. Так вот оказавшись в окне конфигурирования таймеров, наблюдаем такую картину (разберу пример только с Timer-0, т.к. остальные таймеры аналогичны).
Timers
Первое, что необходимо, это выбрать режим работы таймера. Первые два режима (13 и 16 битный счетчик/таймер) идентичны, отличаются лишь длинной регистров (TH и TL, 8 и 5 бит в каждом соответственно — это для 13 битного таймера, и по 8 — для 16 битного). Это типичные таймеры счетчики; в процессе счета (то есть отсчитывания тактовых импульсов, источник которых мы выбираем в разделе Clock Source) таймер инкрементируется до значения 1FFFh(это 5+8 бит для 13-битного) или FFFFh, после чего переходит в состояние 0000h и продолжает счет. В момент обнуления таймера устанавливается флаг переполнения TF (Timer Overflow Flag), который вызывает соответствующее прерывание. Если оно конечно же разрешено (Раздел Timer Interrupt). Раздел Timer Initial Value задает начальное значение регистров TH и TL.
Третий режим (уточню, на самом деле по даташиту это Mode 2, отсчет-то от 0 идет) это 8-битный таймер с перезагрузкой, длина его всего 8 бит, но теперь счетный регистр — это TL, а регистр TH при каждом переполнении подгружает туда стартовое значение. Смотрим схему.
reload
Таким образом для этого режима можно гибко настроить счетчики и прерывания на определенный временной отрезок.

Но вот мне понадобилось организовать прерывания таймера на 1 мс, чтобы навесить на него кучу всяких счетчиков и потом уже легко задавать с их помощью разные процессы и легко расставлять их по времени. Но однако процессор у меня работает на высокой частоте и 8-битного режима мне уже недостаточно. Потому что, например, при частоте 24 Мгц таймер успеет отсчитать только ~10,6 мкс до переполнения. Поэтому без зазрения совести переводим его в 16-битный режим и самостоятельно организуем автоперезагрузку в функции, обрабатывающей прерывание от таймера. Прописываем в ней в самом начале необходимые нам значения TL и TH. Я лично делаю это через константы вот так:

// Константы для реализации автоперезагрузки Timer0 на 1 мс для частоты 48.0 МГц
// поскольку Timer0 работает в Mode 1(16 битный счетчик/таймер *без перезагрузки*)
// Time Initial Value = ( 65536 - 0.001 * 48000000 = 17536 = 0x4480 )
#define TH0_35x				  0x44		 // Старший байт
#define TL0_35x				  0x80		 // Младший байт

А в функции обработки прерывания добавляю:

void inttim0( void ) interrupt 1
{
//  Перезагружаем оба 8-битных регистра таймера-0 (1 мс)
 TL0 = 0x00;
 TH0 = TH0_35x;
 TL0 = TL0_35x;
...
}


Поясню, что перво-наперво обнуляю TL0, чтобы ненароком его переполнение (таймер-то у нас продолжает работать) не двинуло на единицу верхний регистр. Ну вот люблю я перестраховаться. И собственно все, дальше таймер пошел считать с нужным смещением, чтобы обнулиться и вызвать прерывание ровно через 1мс.

Факультативно еще скажу о последнем режиме, Mode 3 (это то, что в скобочках помечено у некоторых разделов настройки). В этом хитром режиме организуются два 8-битных счетчика, а регистры TH и TL используются отдельно. Однако как вы уже догадались используют они разные сигналы синхронизации. TL0 использует необходимые биты таймера 0, и отсчитывать может как осцилляторы внутренние, так и внешние. А TH0 использует только внутреннюю частоту и использует биты таймера 1(который уже не сможет выполнять полностью свою функцию, но на него можно навесить синхронизацию UART интерфейсов).
Кстати обычно в моих МК еще есть таймеры 3 и 4, в которых реализованны 16-битные режимы с автоперезагрузкой, но это уже неинтересно.
  • +2
  • 22 сентября 2011, 14:50
  • PixRaider

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

RSS свернуть / развернуть
Статья недописана?
0
Это заметка, я не планировал ее делать объемнее. Но если есть пожелания, то могу добавить.
0
Просто как то резко обрывается.

«Перво наперво» и опа
0
Рабочий аврал прервал вдохновение.
Дописал.
0
Перенес в коллективный блог по С51
0
[offtop]
и всё-таки mcs51 мои любимые мк… всё просто и понятно… на арм«ах с их отвязанной периферией конфигурация таймера занимает времени у меня больше, чем написание самой задачи (даже при условии „галочного“ конфига в кейле)
[/offtop]
0
А нету желания запилить какой-нибудь небольшой курс «силабс для самых маленьких»? Типа как angle5a сделал для лпц we.easyelectronics.ru/angel5a/anons-urokov-po-lpcxpresso.html
Оглавление можно даже по большей части сты^Wодолжить там же, думаю angle5a не обидится :)
Курс бы нашел своих читателей :)
0
Идея и у меня такая была, но я тут давно ридонли сижу, потому что силабсами как-то никто не интересуется.
0
Ну почему же, довольно любопытные МК. Тока не самые распространенные. Например C8051F321 для нормального погроматора я не нашел там, где обычно закупаюсь. Только C8051F320 в промэлектронике, но он довольно прилично стоит (да, он мощный, но ARMы не слабее и подешевле).
0
  • avatar
  • Vga
  • 24 сентября 2011, 18:34
Поясню, что перво-наперво обнуляю TL0, чтобы ненароком его переполнение (таймер-то у нас продолжает работать) не двинуло на единицу верхний регистр.
А здесь нет временного регистра как в avr, обеспечивающего атомарное чтение и запись?
0
  • avatar
  • John
  • 24 сентября 2011, 16:40
Расчет константы для таймера можно возложить на компилятор:
#define LOBYTE(n) ((uint8_t)(n))
#define HIBYTE(n) ((uint8_t)((uint16_t)(n) >> 8))	

#define Time_Initial_Value (65536 - (uint16_t)(0.001 * 48000000))		
					
TH0 = HIBYTE(Time_Initial_Value);
TL0 = LOBYTE(Time_Initial_Value);

Еще можно значения времени и частоты задать через define.
0
  • avatar
  • John
  • 24 сентября 2011, 17:21
Отличное решение, перейму.
Да, пора уже себя приучать к макросам.
0
// Константы для реализации автоперезагрузки Timer0 на 1 мс для частоты 48.0 МГц
А какой МК используете с тактовой 48 МГц? У старших, если не ошибаюсь, есть умножитель тактовой частоты, но там вроде как кратно 24,5 МГц?
0
  • avatar
  • do_sl
  • 26 сентября 2011, 10:11
48 МГц у серииC8051F34x (внешний). А 24,5 МГц это внутренний тактовый генератор(вроде у всех МК Силаба).
0
  • avatar
  • Zov
  • 26 сентября 2011, 12:04
Использую C8051F340, у которого штатная частота внутреннего генератора — 12 МГц, соответственно умножителем дотягиваю до 48 МГц.
А на 24,5 МГц калибруется, например, F35x серия.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.