(Фрагмент курса аппаратного программирования от Юрия Панчула)

Часть 5. Программирование на голом железе и зачатие операционной системы

(почти готово)

  1. Лаба - знакомство с платой chipKit32 / PIC32 / MIPS и программирование её на С с помощью Arduino-подобного GUI. Кнопочки, лампочки, дисплейчик, IO Shield.
  2. Введение в ассемблер на примере MIPS
  3. Лаба - загрузка в память программок на ассемблере
  4. Концепция простейшей многозадачной операционной системы
  5. Лаба - своя игрушечная многозадачная ОС, которая бутится, ставит обработку прерывания по таймеру, в котором переключает задачи по схеме 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 (просмотреть).

Пример 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 (просмотреть).

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 (просмотреть).

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);
    }
}

Хотелось бы иметь возможность запускать на одном процессоре несколько задач, но чтобы они оставались независимыми и не мешали друг другу. Для этого нам понадобятся два новых понятия:

  1. Контекст выполнения
  2. Прерывания

Контекстом выполнения называется содержимое регистров процессора и стека выполняемой задачи. Ход программы полностью определяется контекстом выполнения. Если бы мы смогли в какой-то момент остановить процессор, запомнить куда-нибудь значения всех регистров (а указатель стека тоже находится в регистре), установить новые значения для регистров и пустить выполнение дальше, мы получили бы совсем другую задачу. Такое действие называется переключением контекста.

Если переключать контекст достаточно быстро, например 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 (просмотреть).

Задание для продвинутых студентов: обобщить пример для произвольного количества задач.

Задание для особо продвинутых студентов: реализовать механизм взаимодействия задач. На выбор: семафоры, мьютексы, рандеву, мониторы, передача сообщений.

 
proj/mips/lab5.txt · Последние изменения: 2012/05/08 21:42 vak
 
Copyright (C) 1996-2013 Serge Vakulenko
serge@vak.ru