(Фрагмент [[http://panchul.livejournal.com/184647.html | курса аппаратного программирования]] от Юрия Панчула) ====== Часть 5. Программирование на голом железе и зачатие операционной системы ====== (почти готово) - Лаба - знакомство с платой chipKit32 / PIC32 / MIPS и программирование её на С с помощью Arduino-подобного GUI. Кнопочки, лампочки, дисплейчик, IO Shield. - Введение в ассемблер на примере MIPS - Лаба - загрузка в память программок на ассемблере - Концепция простейшей многозадачной операционной системы - Лаба - своя игрушечная многозадачная ОС, которая бутится, ставит обработку прерывания по таймеру, в котором переключает задачи по схеме round-robin. В качестве среды программирования используется пакет MPIDE фирмы chipKIT. Это бесплатное программное обеспечение, сделанное на основе компилятора GCC и визуальной среды программирования Arduino. Его можно установить под Linux, Windows или Mac OS X. Скачать пакет можно здесь: [[https://github.com/chipKIT32/chipKIT32-MAX/downloads]] Для первых двух примеров используется визуальная среда программирования. В третьем и четвёртом примерах от студентов потребуется умение работать в режиме командной строки и пользоваться командой make. Загрузка программ на плату Uno32 (или Max32) производится через порт USB с помощью утилиты avrdude, входящей в состав MPIDE. Отдельный программатор не требуется. ===== 5.1. Знакомство с платой Uno32 ===== Лабораторная работа: знакомство с платой Uno32. Процессор PIC32 с архитектурой MIPS32. Простая среда разработки chipKit MPIDE и программирование на С. Подключение кнопочек и семисегментного индикатора с помощью макетной платы. Концепция прерывания. Необходимое оборудование: * Плата Uno32 * Адаптер PICkit 3 (начиная с раздела 5.3) * Компьютер с Linux или Windows * Кабель mini-USB * Универсальная макетная плата * Две кнопки с проводами * Семисегментный светодиодный индикатор * Девять резисторов 220 Ом с проводами Пример 1: управление двумя светодиодами, имеющимися на плате Uno32, с помощью двух внешних кнопок. Пример 2: управление семисегментным индикатором. Игра "нажми вместе". При нажатии двух кнопок на индикаторе отображается разность времени нажатия в миллисекундах. Используется аппаратное прерывание от таймера. Студентам предлагается доработать примеры в некотором направлении. Возможный список задач: * Сделать счетчик нажатий кнопок. Одна кнопка увеличивает счётчик, другая уменьшает. * Секундомер. Первая кнопка запускает отсчет. Вторая увеличивает темп в 10 или 100 раз. * "Электронный кубик" - генератор случайных чисел. === Пример 5.1.1. === Среда MPIDE. Управление двумя светодиодами, имеющимися на плате Uno32, с помощью двух внешних кнопок. TODO: нарисовать схему подключения кнопок. Требуются два резистора 10кОм для подтяжки к +3.3В. Исходные тексты: {{example5-1-1.pde}} ([[lab5-example511 |просмотреть]]). === Пример 5.1.2 === Среда MPIDE. Управление семисегментным индикатором. Игра "нажми вместе". При нажатии двух кнопок на индикаторе отображается разность времени нажатия в миллисекундах. Используется аппаратное прерывание от таймера. TODO: нарисовать примерную схему подключения 7-сегментного индикатора. Требуются восемь резисторов 220Ом для ограничения тока через светодиоды. Годится любой светодиодный индикатор с общим катодом. Можно с общим анодом, если поменять местами LATxSET и LATxCLR в функции display(). {{led-diagram-3.jpg}} ||**Контакт Uno32 Digital**||**Сегмент LED**|| || 2 || A || || 3 || B || || 4 || C || || 5 || D || || 6 || E || || 7 || F || || 8 || G || || 9 || H || Исходные тексты: {{example5-1-2.pde}} ([[lab5-example512 |просмотреть]]). ===== 5.2. Введение в ассемблер MIPS ===== Организация оперативной памяти, разбиение на слова. Понятие машинной инструкции и счётчика команд. Регистры общего назначения, номера и имена регистров. Передача параметров и возврат значения в регистрах. Регистр адреса возврата. Стек, регистр стека, место в стеке для каждой вызываемой функции (фрейм). Основные инструкции системы команд MIPS. Разбиение по функциональным группам. Псевдоинструкции LI, LA. Управление периферийными модулями: таймер, сигналы ввода-вывода. Концепция управляющих регистров, отображаемых на память. ===== 5.3. Практическая работа на ассемблере ===== Пример: управление двумя светодиодами с помощью двух внешних кнопок. То же, что в разделе 5.1, но на ассемблере. Для сборки используется утилита make (скачать {{makefile}}). В начале файла makefile следует установить путь к каталогу с установленным пакетом MPIDE, например: MPIDE_DIR = /opt/mpide-0022-linux32-20110822 Для загрузки программы в плату надо вызывать: sudo make upload Задание для продвинутых студентов: переписать на ассемблере пример 2 из раздела 5.1. === Пример 5.3 === Управление двумя светодиодами с помощью двух внешних кнопок. Тот же, что в разделе 5.1, но на ассемблере. TODO: нарисовать схему подключения кнопок. Требуются два резистора 10кОм для подтяжки к +3.3В. Исходные тексты: {{example5-3.zip}} ([[lab5-example53 |просмотреть]]). ===== 5.4. Концепция многозадачного выполнения ===== В основе многозадачности лежит стремление к простоте. В примере 5.1.1 нам приходилось обрабатывать всего два входных сигнала (кнопки). Тем не менее, основной цикл программы выглядит неочевидным. В реальных системах микроконтроллеру приходится обрабатывать десятки или даже сотни входных событий. Хороший способ справиться с этим - разбить программу на независимые части меньшего размера, выполняющиеся параллельно, не мешая друг другу. Такие части называют //задачами// (tasks). В больших операционных системах задачи, выполняющиеся в раздельных адресных пространствах, называются //процессами//. Вот как могли бы выглядеть задачи в примере 5.1.1. Намного проще, не правда ли? void task1() { for (;;) { if (нажата первая кнопка) { гасим первый светодиод; delay (150); } зажигаем первый светодиод; delay (150); } } void task2() { for (;;) { if (нажата вторая кнопка) { гасим второй светодиод; delay (150); } зажигаем второй светодиод; delay (150); } } Хотелось бы иметь возможность запускать на одном процессоре несколько задач, но чтобы они оставались независимыми и не мешали друг другу. Для этого нам понадобятся два новых понятия: - Контекст выполнения - Прерывания //Контекстом выполнения// называется содержимое регистров процессора и стека выполняемой задачи. Ход программы полностью определяется контекстом выполнения. Если бы мы смогли в какой-то момент остановить процессор, запомнить куда-нибудь значения всех регистров (а указатель стека тоже находится в регистре), установить новые значения для регистров и пустить выполнение дальше, мы получили бы совсем другую задачу. Такое действие называется //переключением контекста//. Если переключать контекст достаточно быстро, например 1000 раз в секунду, будет создаваться впечатление параллельного и одновременного выполнения всех задач. Скажем, 1 миллисекунду работает первая задача, потом 1 миллисекунду вторая, и дальше по кругу. Такой алгоритм распределения процессорного времени обычно называют циклическим (round robin). В больших операционных системах применяются и другие алгоритмы, более сложные. //Прерывания// это особый механизм, посредством которого процессор приостанавливает выполнение текущей программы, и совершает необходимые срочные действия. При этом управление передаётся на специальный фиксированный адрес, по которому расположена функция обработки прерывания. После её завершения происходит //выход из прерывания//, и выполнение программы продолжается. Для обработки прерываний в процессоре есть специальные регистры (EPC, Status) и инструкции (ERET, EI, DI). Для получения периодических прерываний в процессоре MIPS есть два 32-битных регистра: Count и Compare. Регистр Count - это счётчик, который растет с фиксированной частотой, в нашем случае 40 МГц. Когда значение Count становится равным регистру Compare, возникает прерывание с номером 0 и происходит переход по адресу 0x9d000200. Старое значение счетчика команд PC прерванной программы заносится в специальный регистр EPC. В регистре Status устанавливается бит EXL, блокирующий обработку других прерываний (если они возникнут). Дальше начинает выполняться функция обработки прерывания. В нашем примере она записывает значения всех регистров (контекст) в стек прерванной задачи, переключает регистр стека на другую задачу, восстанавливает все регистры из стека, и выполняет инструкцию ERET. Это специальная команда, которая снимает бит EXL регистра Status (этим разблокируя прерывания) и пересылает значение регистра EPC в счетчик команд PC. Продолжается выполнение новой задачи, до следующего прерывания. ===== 5.5. Пример реализации многозадачности ===== Переключение двух задач по таймеру. Исходные тексты расположены в двух файлах: * example5-5.c --- основная программа на языке Си. * timer-interrupt.S --- функция обработки прерывания на ассемблере. Сохраняет и восстанавливает контекст выполнения в стеке. Для управления таймером и переключения задач вызывает функцию на языке Си. Для сборки используется утилита make (скачать {{makefile}}). В начале файла makefile следует установить путь к каталогу с установленным пакетом MPIDE, например: MPIDE_DIR = /opt/mpide-0022-linux32-20110822 Для загрузки программы в плату надо вызывать: sudo make upload Исходные тексты: {{example5-5.zip}} ([[lab5-example55 |просмотреть]]). Задание для продвинутых студентов: обобщить пример для произвольного количества задач. Задание для особо продвинутых студентов: реализовать механизм взаимодействия задач. На выбор: семафоры, мьютексы, рандеву, мониторы, передача сообщений.