Процессор
Процессор — относительно простое устройство. Суть его работы состоит в том, чтобы выполнять по порядку перечень команд записанных в памяти. Для начала расскажу из чего он состоит, а потом постепенно перейду к самим командам. Итак, процессор состоит из (упрошенная модель):
Блок управления отвечает за распознание команд, и выполнение соответствующих им действий.
АЛУ отвечает за арифметические и логические действия. Сложить два числа сложности не представляет, на это уйдет пару тактов. А вот с умножением дела обстоят намного сложней (а еще, если эти числа с плавающей точкой).
С тактовым генератором тоже все понятно — его задача просто генерировать импульсы высокой частоты.
Регистры, пожалуй, самая трудная тема. Регистр — это единица памяти процессора. Самая быстраz, но и самая меньшая по объему. В процессоре могут располагаться регистры с объемами от 8 до 128 бит. Регистры бывают общего и специального назначения. В регистры общего назначения процессор складывает промежуточные значения вычислений, нужные адреса памяти. Регистры частного назначения используются для слежения за стеком, установки флагов. В микроконтроллерах могут быть регистры для взаимодействия с внутренними датчиками.
Естественно схема вышеописанного процессора сильно упрошена. И в реальном ЦП есть ещё: кэш, блоки операций с плавающей точкой, конвейерные модули (для параллельного выполнения некоторых команд). Начиная с серии i3, интел туда уже впихивает видео карты.
Регистры и команды процессора
Хотя перечень инструкций не велик (в переменную типа char должен вместится). Сложность их очень варьируется. От одного такта, до пары сотен. Какую информацию несут о себе инструкции процессора? Ну, например: перенеси-ка вот это значение 0x01FA1254 в регистра AH; сложи-ка значение из регистра BX c AX; достань, пожалуйста, значение из памяти по адресу 0xF145A1FC и запиши его в регистр BL. Естественно когда речь идет о памяти, то скорость падает в разы.
Пару слов про кэш. Кэш, это вторая по величине скорости память. Она вшита в сам процессор. И нужна для быстрого обращения к часта используемым местам в ОЗУ. При обращении к какому-либо участку памяти, сначала идёт прогон по кэшу. Если в кэше былом обнаружено совпадение, от туда достается значение и записывается в регистр. Если нет ЦП обращается к памяти, а в кэш записывает значение и адрес участка памяти на который был запрос.
Таймеры и счетчики
В контексте про микроконтроллеры Cypress, таймер является синонимом счетчика. Процессор ничего не знает о внешнем времени. Но он знает свою тактовую частоту (а точнее вы её знаете). Исходя из этого, наше устройство уже можно привязать к реальному времени. Допустим, частота тактового генератора 100Гц, и счетчик работает на этой же частоте. Тогда за каждые 100 подсчетов, будет проходить ровно одна секунда. По окончании подсчетов можно вывести сигнал, на какой-нибудь из выходов МК. Можно увеличить внутренний счетчик в коде. Зависит от поставленной задачи. Величина, указывающая на то, сколько импульсов должно прийти до обновления состояния счетчика, называется периодом счетчика или таймера. Кстати данные о количестве пришедших импульсов тоже хранятся в регистре. Он называется счетным регистром. От величины этого регистра зависит разрядность таймера. Если счетный регистр является 8-ми битным, то таймер может считать импульсы до 256 раз. И является 8-ми битным.
Таймеры бывают общего назначения и сторожевые таймеры. Сторожевые таймеры предназначены для аварийного перезапуска МК, или для экономии электроэнергии (чтобы будить процессор).
Глобальные прерывания
Но, если рассуждать как описано выше, то процессор будет работать в строго определённом порядке. По строго оговоренному алгоритму. В таком случае от него было бы мало толку, потому что он должен как то реагировать на внешние воздействия. К примеру, нажатия клавиш, появление входного сигнала, и т.д. Потому и придумали прерывания процессора. Суть в самом слове — прерывать. Прерывание это событие, при котором происходит приостановка основной программы и переход на выполнение другого участка кода.
Пример 5. Clock
Задачи: Написать секундомер, с возможностью программного перезапуска.
В этой статье пример написан «с нуля» и не тянет за собой никаких ссылок из прошлых занятий. Потому открываем дизайнер, нажимаем «создать новый проект», выбираем директорию и имя, нажимаем «Готово». Из пользовательских модулей нам понадобиться Counter32 (32х битный счетчик) который находиться в папке Counters и LCD из папки «Misc Digitals». Для Counter32 поле Enable выставляем High. Для LCD поле LCDPort лучше сразу поставить на Port_2.
Теперь нужно выбрать рабочую частоту и период для нашего счётчика. Всё это необходимо подобрать таким образом, чтобы результирующая частота получилась 1Гц. Выберем для счетчика делитель частоты VC3. VC3Divider выбираем 150. VC3Source выбираем VC1. VC1 выставляем на 16. Напомню что все эти действия выполняются в дизайнере, в закладке Global Resources. Зная, что частота тактового генератора 24Е6 и делитель частоты VC3 можно получить: 24 000 000 / 16 * 150 = 10 000. Это и есть рабочая частота нашего счётчика. Теперь, если установить период равный этой частоте, получим результат срабатывания счетчика 1 раз в секунду. Из всего этого получился следующий код:
#include // part specific constants and macros
#include «PSoCAPI.h» // PSoC API definitions for all User Modules
#include «stdlib.h»
DWORD count;
int sec;
char time[256];
void main(void)
{
M8C_EnableGInt ; // Uncomment this line to enable Global Interrupts
LCD_Start();
LCD_Position(0,0);
LCD_PrCString(«Time in sec:»);
Counter32_WritePeriod((10000-1)/2);
Counter32_EnableInt();
Counter32_Start();
while(1)
{
Counter32_ReadCounter(&count);
if(count == 0)
{
sec++;
itoa(time,sec,10);
LCD_Position(1,0);
LCD_PrString(time);
while(!count)
Counter32_ReadCounter(&count);
}
}
}
Последние две строчки являются достаточно хитрыми, а нужны в связи с тем, что Counter32 обновляется частотой 1000Гц, а процессор работает на частоте 24МГц/8. В связи с этим вполне реальна ситуация когда проц выполняет уже следующую итерацию цикла while(1), а Counter32 ещё не обновил своё значение.
Технический мануал рекомендует заполнять все поля настроек элемента, даже если они не используются. По этому приведу их краткое описание.
Скрин всех настроек:
Глобальные настройки проекта:
Собираем проект, зашиваем прошивку, смотрим результат:
До 18 секунд телефон с микроконтроллером синхронизировались хорошо. Но потом начинают появляться отклонения от времени. Это связано с тем, что МК работает на предельной частоте 24МГц.
Далее, чтобы отреагировать на нажатие кнопки, нам надо выставить настройки для лапы МК, которая будет ловить прерывание. Я выбрал Port_0_1. И выставил для неё следующие настройки:
Самое важное здесь, это Interrupt. Поле отвечающие за тип срабатывания прерывания.
В коде нужно добавить
#pragma interrupt_handler GPIO_ISR
указатель на функцию, которая будет выполняться при срабатывание прерывания.
void GPIO_ISR(void)
{
sec = 0;
}
Сама функция. Видно, что она обнуляет значение времени.
Сразу в начале функции main()
PRT0DM0 &= ~0x02;
PRT0DM1 &= ~0x02;
PRT0DM2 &= ~0x02;
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);
Дополнительная настройка портов и включение прерываний.
Ещё один ОЧЕНЬ важный момент!!! Который походу упустили из виду инженеры Cypress. Для успешной работы прерываний нам придётся подправить ещё один файлик проекта вручную. Файл находится в директории (ИмяПроекта)/(ИмяПроекта)/boot.tpl. К примеру:
Если вы выбрали Port_0_1 и имя функции GPIO_ISR. То примерно на строке 142 заменить с:
org 1Ch ;GPIO Interrupt Vector
`@INTERRUPT_7`
reti
На:
org 1Ch ;GPIO Interrupt Vector
ljmp _GPIO_ISR
reti
Это косяк Си’шного компилятора, при переводе имён функций
Не забываем подключить кнопку:
Ну и наконец, можно компилировать и зашивать прошивку!
Задержка появилась из-за того, что вывод строки осуществляется лишь по прохождении периода таймера.
Прикрепленные файлы:
- Clock.zip (311 Кб)