- •Медицинские микропроцессорные системы
- •Анисимов а.А.
- •Isbn © сПбГэту «лэти», 2019 введение
- •1. Общая структура микроконтроллеров avr
- •2. Программирование микроконтроллеров на языке ассемблер
- •3. Работа с отладочной платой attiny104-xnano
- •4. Устройство портов ввода-вывода
- •5. Таймеры-счётчики
- •6. Широтно-импульсная модуляция
- •7. Использование аналого-цифрового преобразователя
- •8. Передача данных по uart
- •9. Последовательный интерфейс spi
- •Приложение 1. Основные команды языка assembler для микроконтроллера attiny104
- •Список литературы
- •Оглавление
- •Медицинские микропроцессорные системы
- •197376, С.-Петербург, ул. Проф. Попова, 5
5. Таймеры-счётчики
В предыдущей главе мы рассмотрели способ организации временных задержек с помощью пустых циклов. Совершенно очевидно, что для организации точных временных интервалов этот способ не подходит, к тому же он целиком загружает микроконтроллер, мешая реализации других функций. Так что нам не обойтись без некоторого таймера, который бы отсчитывал время независимо от работы микроконтроллерного ядра и позволял в любое время пользоваться результатами счёта. В рассматриваемой в данном курсе линейке микроконтроллеров функцию такого счетчика как раз и выполняет таймер-счётчик, входящий в состав стандартной периферии любого восьмибитного микроконтроллера семейства AVR. Их количество может варьироваться от одного (как в случае младшей линейке Tiny) до 6 (в старшей линейке, Atmega256, например).
В целом, во всех более старших моделях микроконтроллеров присутствуют, как минимум, два таймера/счетчика — T0 и T1. Таймер T0 обычно имеет минимальный набор функций, зависящий от модели микроконтроллера. В одних моделях он может использоваться только для отсчета и измерения временных интервалов или как счетчик внешних событий. В других же к этим функциям добавляется возможность генерации сигналов с широтно-импульсной модуляцией (ШИМ) фиксированной разрядности, а также возможность работать в асинхронном режиме в качестве часов реального времени.
Таймер T1 также может использоваться для отсчета временных интервалов и как счетчик внешних событий. Кроме того, он может выполнять запоминание своего состояния по внешнему сигналу. Как и таймер T0, он может работать в качестве широтно-импульсного модулятора, но уже переменной разрядности и к тому же многоканального. Таймер T2 полностью аналогичен таймеру T0. При наличии в микроконтроллере обоих таймеров, один из них может работать в асинхронном режиме, а другой — в качестве счетчика внешних событий. Таймер T3 по функциональным возможностям идентичен таймеру T1. В составе всех микроконтроллеров семейства имеется также сторожевой таймер (Watch Dog Timer), являющийся непременным атрибутом всех современных микроконтроллеров. Этот таймер позволяет избежать несанкционированного зацикливания программы, возникающего по тем или иным причинам.
В состав микроконтроллера Attiny104 на нашей отладочной плате входит всего один шестнадцатибитный таймер, поэтому все дальнейшие разъяснения будут проводиться на его примере.
Источники тактовой частоты.
Для работы любого микроконтроллера необходимо обеспечить его стабильным источником тактовых импульсов. В случае микроконтроллеров фирмы Atmel возможно несколько вариантов.
1) Первый – воспользоваться внутренним генератором на RC-цепочке. Этот вариант самый простой - не требует дополнительных внешних компонентов, но и особой точности от такого генератора ждать не стоит, его частота может меняться в зависимости от внешних условий в пределах 3-5 процентов. Для приложений, не требующих точного измерения временных интервалов, этого вполне достаточно. Для тех случаев, когда важна каждая микросекунда, стоит обратиться ко второму варианту.
2) В качестве источника тактовых импульсов может выступать внешний кварцевый резонатор (или керамический, для любителей экзотики). Максимальная поддерживаемая частота – 16МГц (здесь уместно вспомнить о том, что энергопотребление микроконтроллера возрастает с увеличением рабочей частоты, причём значительно). Для подключения внешнего кварцевого резонатора используются специальные выводы – XTAL1 и XTAL2, также для подключения необходимо использовать дополнительные конденсаторы емкостью в 15-30 пФ, в зависимости от частоты (хотя существуют изощренные трёхвыводные модели резонаторов с уже встроенными конденсаторами, что весьма удобно, но купить их, особенно в розницу, не так просто). Пример подключения кварцевого резонатора приведен на рисунке 5.1.
Рис. 5.1. Различные схемы тактирования микроконтроллера (часть 1)
Столь неровное значение частоты кварца (11.76 МГц) на рисунке объясняется просто – оно необходимо для реализации передачи данных по последовательному интерфейсу UART на высоких скоростях (об этом будет рассказано подробнее в соответствующем разделе).
3) Вариант, нетребовательный к точности задаваемой системной частоты – воспользоваться вместо резонатора обычной RC-цепочкой, при этом для её подключения используются те же самые выводы, что и в предыдущем случае. Тактовая частота, которую даёт такая RC-цепочка, приблизительно описывается следующим выражением:
Емкость конденсатора С должна быть не ниже 22пФ. Также, с помощью фьюз-бита CKOPT мы можем подключить между выводом XTAL1 и землёй внутренний конденсатор емкостью 36 пФ, что позволяет избавиться от внешнего конденсатора. Более подробно информация о тактировании МК с помощью RC-цепочки описана в фирменной документации.
4) Последний вариант – использование внешнего генератора. Здесь всё предельно просто, подключаем выход генератора к выводу XTAL1. Подобный способ тактирования микроконтроллера обеспечивает максимальную точность, единственный недостаток – подобные генераторы имеют достаточно высокую стоимость.
5) Для некоторых приложений (например, часов реального времени) нам может понадобиться низкочастотный кварц на 32,768 кГц. Почему значение именно такое? Очевидно, что шестнадцатибитный таймер может считать до 65536 тиков, а частота часового кварца, как нетрудно подсчитать, как раз составляет половину этого значения. Получается, что с помощью такого кварца можно с высокой точностью отсчитывать секундные интервалы. Для подключения часового кварца используются отдельные выводы TOSC1 и TOSC2, причём использование дополнительных внешних конденсаторов не требуется (рис. 5.2).
Рис. 5.2. Различные схемы тактирования микроконтроллера (часть 2)
Теперь рассмотрим регистры, определяющие работу таймера. Основными управляющими регистрами являются регистры TCCRxn (Timer/Counter Control Register, для микроконтроллера AtTiny104 предусмотрено 3 управляющих регистра – TCCR0A, TCCR0B и TCCR0C, которые определяет режим работы таймера и значение коэффициента предделителя.
Возникает вопрос, что такое предделитель? Тактовые импульсы нашего микроконтроллера (при работе от внешнего кварца или внутренней RC-цепочки, например) перед тем, как они попадут в счётный регистр таймера, можно пропустить через предделитель с фиксированными значениями - 8, 32, 64, 128, 256 и 1024. Где это может пригодиться? Вспомним часовой кварц с частотой в 32,768 кГц. Если эту частоту пропустить через делитель с коэффициентом 128 и подать на восьмибитный таймер, то точно раз в секунду будет происходить прерывание таймера по переполнению (32768/128=256), что позволяет быстро и эффективно организовать часы реального времени.
Значение предделителя можно изменить в регистре TCCR0B, за него отвечают три бита: CS02:0 (Clock Select), которые как раз и определяют коэффициент деления тактовых импульсов микроконтроллера. Здесь возможны следующие варианты (таблица 5.1).
Таблица 5.1. Задание предделителя таймера
CS02 |
CS01 |
CS01 |
Описание |
0 |
0 |
0 |
нет источника импульсов (Таймер остановлен) |
0 |
0 |
1 |
clkIO/1 (No prescaling) |
0 |
1 |
0 |
clkIO/8 (From prescaler) |
0 |
1 |
1 |
clkIO/64 (From prescaler) |
1 |
0 |
0 |
clkIO/256 (From prescaler) |
1 |
0 |
1 |
clkIO/1024 (From prescaler) |
1 |
1 |
0 |
External clock source on T0. Clock on falling edge. |
1 |
1 |
1 |
External clock source on T0. Clock on rising edge. |
Режим работы таймера T0 определяется состоянием четырех битов WGM01…WGMx03 регистра TCCR0A и TCCR0B. Зависимость режима работы таймера от состояния этих разрядов показана в таблице 5.2.
Таблица 5.2. Режимы работы таймера
№ |
WGM0 [3:0] |
Режим работы таймера |
TOP |
Обновление бита OCR0x |
Установка прерыв. TOV0 |
0 |
0000 |
Normal |
0xFFFF |
Immediate |
MAX |
1 |
0001 |
PWM, Phase Correct, 8-bit |
0x00FF |
TOP |
BOTTOM |
2 |
0010 |
PWM, Phase Correct, 9-bit |
0x01FF |
TOP |
BOTTOM |
3 |
0011 |
PWM, Phase Correct, 10-bit |
0x03FF |
TOP |
BOTTOM |
4 |
0100 |
CTC (Clear Timer on Compare) |
OCR0A |
Immediate |
MAX |
5 |
0101 |
Fast PWM, 8-bit |
0x00FF |
TOP |
TOP |
6 |
0110 |
Fast PWM, 9-bit |
0x01FF |
TOP |
TOP |
7 |
0111 |
Fast PWM, 10-bit |
0x03FF |
TOP |
TOP |
8 |
1000 |
PWM, Phase and Frequency Correct |
ICR0 |
BOTTOM |
BOTTOM |
9 |
1001 |
PWM, Phase and Frequency Correct |
OCR0A |
BOTTOM |
BOTTOM |
10 |
1010 |
PWM, Phase Correct |
ICR0 |
TOP |
BOTTOM |
11 |
1011 |
PWM, Phase Correct |
OCR0A |
TOP |
BOTTOM |
12 |
1100 |
CTC (Clear Timer on Compare) |
ICR0 |
Immediate |
MAX |
13 |
1101 |
Reserved |
- |
- |
- |
14 |
1110 |
Fast PWM |
ICR0 |
TOP |
TOP |
15 |
1111 |
Fast PWM |
OCR0A |
TOP |
TOP |
Режим Normal
Это наиболее простой режим работы таймеров, он же единственный режим в младших моделях семейства. В этом режиме счетный регистр функционирует как обычный суммирующий счетчик. По каждому импульсу тактового сигнала осуществляется инкремент счетного регистра. При переходе через значение $FF возникает переполнение, и счет продолжается со значения $00. В том же такте сигнала, в котором обнуляется регистр TCNTn, устанавливается в «1» флаг переполнения счетного регистра TOVn (Timer Overflow).
При равенстве счетного регистра и регистра сравнения устанавливается флаг прерывания OCFn и, если разряд OCIEn регистра TIMSK установлен в «1», генерируется соответствующее прерывание. Наряду с установкой флага при равенстве счетного регистра и регистра сравнения может изменяться состояние вывода OC0x микроконтроллера. Каким образом оно будет изменяться, определяется разрядами COM0x1и COM0x1 регистра TCCR0A в соответствии с таблицей 5.3.
Таблица 5.3. Регистры сравнения.
COM0x1 |
COM0x0 |
Описание |
0 |
0 |
Таймер Tn отключен от вывода OCn |
0 |
1 |
Состояние вывода меняется на противоположное |
1 |
0 |
Вывод сбрасывается в «0» |
1 |
1 |
Вывод устанавливается в «1» |
Режим CTC (сброс при совпадении)
В этом режиме счетный регистр функционирует как обычный суммирующий счетчик, инкремент которого осуществляется по каждому импульсу тактового сигнала на входе таймера. Однако максимально возможное значение счетного регистра и, следовательно, разрешающая способность счетчика определяется регистром сравнения OCR0A и OCR0B. После достижения значения, записанного в регистре сравнения, счет продолжается со значения 0x0000. В том же такте сигнала, в котором обнуляется счетный регистр, устанавливается флаг прерывания TOV0 регистра TIFR0. Временные диаграммы для этого режима работы таймера/счетчика приведены на рисунке 5.3.
Рис. 5.3. Временные диаграммы для режима CTC
При достижении счетчиком максимального значения устанавливается флаг OCF0x регистра TIFR. Одновременно с установкой флага может изменяться состояние вывода OC0x микроконтроллера. Поведение выводов определяется разрядами COM0х1 и COM0х0 регистра TCCR0A, и полностью совпадает с табличными данными, приведёнными для предыдущего режима работы таймера.
Режим Fast PWM
Режим Fast PWM («Быстродействующий ШИМ») позволяет генерировать высокочастотный сигнал с широтно-импульсной модуляцией. В связи с высокой частотой генерируемого сигнала данный режим с успехом может использоваться в таких приложениях, как регулирование мощности, выпрямление, цифроаналоговое преобразование и другие. Более подробно данный режим работы таймера (вместе с режимом Phase Corect PWM) будет рассмотрен в отдельном разделе.
Счётный регистр TCNT0 – именно в нём накапливаются тактовые импульсы. Счетный регистр может быть как восьмибитным, так и шестнадцатибитным, как в случае нашего микроконтроллера AtTiny104. В последнем случае он делится на два регистра – младший (TCNT0L) и старший (TCNT0H). И здесь возникает одна небольшая проблема. В счётные регистры в любой момент времени можно как записывать нужные численные значения, так и считывать их. Но так как регистр состоит из двух половинок, эти операции невозможно провести за одну команду – а при чтении/записи отдельных половинок может измениться значение одной из них. Чтобы избежать этого, был предложен специальный алгоритм.
Запись в старший регистр (TCNTxH) ведется вначале в регистр TEMP. Это служебный регистр, и для пользователя он недоступен. Затем в регистр TCNTxL записывается младший байт. В этот момент в реальный TCNTxH заносится ранее записанное (автоматически) в регистр TEMP значение. Получается, что два байта, старший и младший, записываются одновременно, при этом порядок записи изменять нельзя – сначала записываем старший байт, потом младший, и никак иначе. Также рекомендуется перед записью запретить прерывания.
CLI // Запрещаем прерывания
OUT TCNT1H, R16 //Старший байт записан в регистр TEMP
OUT TCNT1L,R17 // Запись сразу в обе половинки
SEI // Разрешаем прерывания
При чтении регистра TCNTx операции выполняются в обратном порядке – сначала читаем младший регистр, потом старший. Эти правила чтения/записи распространяются и на все остальные шестнадцатибитные регистры, состоящие из двух частей.
Прерывания таймера.
У каждого аппаратного события есть прерывание, и таймер не является исключением. Как только происходит переполнение счетного регистра или ещё какое-нибудь событие, сразу же происходит вызов прерывания.
За прерывания от таймеров отвечают регистры TIMSK (Timer/Counter Interrupt Mask) и TIFR (Timer/Counter Interrupt Flag Register).
TIMSK – это регистр масок, биты, находящиеся в нем, локально разрешают прерывания. Если бит установлен, значит конкретное прерывание разрешено. Если бит сброшен в ноль, значит данное прерывание запрещено. По умолчанию после системного сброса все биты регистра TIMSK сброшены в ноль. Описание разрядов регистра TIMSK0 для используемого контроллера приведено в таблице ниже, их всего четыре:
Таблица 5.4. Прерывания таймера.
Название |
Описание источника прерывания |
ICIE0 |
Флаг по событию «Захват» таймера |
OCIE0A |
Флаг по событию «Совпадение A» таймера |
OCIE0B |
Флаг по событию «Совпадение B» таймера |
TOIE0 |
Флаг по переполнению таймера |
Наиболее интересующее нас на данный момент прерывание – TOIE0, то есть прерывание по переполнению таймера. Задавая коэффициент предделителя таймера и используя данное прерывание, мы можем с достаточно большой точность отсчитывать необходимые нам временные интервалы.
Регистр TIFR – это непосредственно флаговый регистр. Когда какое-то прерывание срабатывает, устанавливается соответствующий ему флаг (то есть каждому из четырех прерываний регистра TIMSK0 соответствует свой собственный флаг регистра TIFR0). Этот флаг сбрасывается на аппаратном уровне, когда программа совершает переход по вектору прерываний. Если прерывания запрещены, то флаг так и будет стоять до тех пор, пока прерывания не разрешат, и программа не уйдет на их обработку.
алее приведён пример простой программы, использующей прерывание по переполнению таймера для мигания светодиодом с заданной задержкой.
// Лабораторная работа №2. Использование таймера.
//******************************************************//
// Подключаем внешние модули и макросы
//***********************************************************//
// Задаём переменные, определяем имена используемых регистров
.def Count = R17
.def Temp = R18 // Рабочая переменная
//***********************************************************//
// вектор прерываний
ORG 0x000
RJMP RESET // Reset Handler
ORG 0x001
RETI // IRQ0 Handler
ORG 0x002
RETI //PCINT0 Handler
ORG 0x003
RETI // PCINT1 Handler
ORG 0x004
RETI // Timer/Counter0 Capture Handler
ORG 0x005
RJMP Tim_OVF // Timer/Counter0 Overflow Handler
ORG 0x006
RETI // Timer/Counter0 Compare Match A Handler
ORG 0x007
RETI // Timer/Counter0 Compare Match B Handler
ORG 0x008
RETI // Analog Comparator Handler
ORG 0x009
RETI // Watchdog Timer Handler
ORG 0x00A
RETI // Voltage Level Monitor Handler
ORG 0x00B
RETI // ADC Conversion Handler
ORG 0x00C
RETI // USART0 Rx Start Handler
ORG 0x00D
RETI // USART0 Rx Complete Handler
ORG 0x00E
RETI // USART0 Data Register Empty Handler
ORG 0x00F
RETI // USART0 Tx Complete Handler
ORG INT_VECTORS_SIZE // Конец таблицы прерываний
//***********************************************************//
// Обработка прерываний
Tim_OVF: // Прерывание по переполнению таймера
INC Count
// Если счётчик нечётный, пропускаем следующую команду
SBRS Count, 0
LDI Temp, 0b00100000 // Иначе устанавливаем бит 6
// Если счётчик чётный, пропускаем следующую команду
SBRC Count, 0
LDI Temp, 0b00000000 // Иначе сбрасываем бит 6
OUT PortA, Temp
RETI // Выходим из прерывания
*****************************************************//
RESET:
// Инициализация стека
LDI R16, Low(RAMEND)
OUT SPL, R16
LDI R16, High(RAMEND)
OUT SPH, R16
//Инициализация периферии
//*********************************************//
LDI R16, 0xD8
OUT CCP, R16
LDI R16, 0x00
OUT CLKPSR, R16
LDI R16, 0b00100000
OUT DDRA, R16
OUT PORTA, R16
CLR Count
CLR Temp // Очищаем регистры
LDI Temp, (1<<CS00) | (1<<CS01) // Задаем предделитель
OUT TCCR0B, Temp
// разрешаем прерывание по переполнению таймера
LDI Temp, (1<<TOIE0)
OUT TIMSK0, Temp
SEI // Разрешаем глобальные прерывания
//***********************************************************//
// Основной цикл программы
Main:
// Бесконечный цикл, вся работа выполняется в прерывании
RJMP Main //***********************************************************//
Программу для работы с прерываниями по таймеру на языке Си необходимо разработать самостоятельно, пользуясь полученными ранее знаниями.
