Загрузил Victor Riabtsovski

Alyamkin D I Anuchin A S Drozdov A V i d

реклама
УДК 621.398
В 858
Утверждено учебным управлением МЭИ (ТУ) в качестве учебного пособия
для студентов и слушателей факультета повышения квалификации
специалистов промышленности и преподавателей вузов
Подготовлено на кафедре автоматизированного электропривода
в учебно-научно-консультационном центре «Texas Instruments-МЭИ»
Рецензенты:
докт. техн. наук, проф. А.Б. Красовский (МГТУ им. Н.Э. Баумана);
канд. техн. наук, доц. Т.В. Ремизевич (МЭИ) (ТУ)
Авторы:
А.С. Анучин, Д.И. Алямкин, А.В. Дроздов, В.Ф. Козаченко, А.С. Тарасов
Встраиваемые высокопроизводительные цифровые системы управления.
В 858 Практический курс разработки и отладки программного обеспечения сигнальных
микроконтроллеров TMS320x28xxx в интегрированной среде Code Composer
Studio: учеб. пособие / А.С. Анучин, Д.И. Алямкин, А.В. Дроздов и др.; под
общ. ред. В.Ф. Козаченко. — М.: Издательский дом МЭИ, 2010. — 270 с.
ISBN 978-5-383-00471-5
Курс практического освоения технологии разработки и отладки программного обеспечения
высокопроизводительных систем управления на базе цифровых сигнальных микроконтроллеров
со встроенной специализированной периферией на языке высокого уровня С/С++ в интегрированной компьютерной среде разработки Code Composer Studio. Раcсчитан на самостоятельную работу
или работу в лаборатории со стандартными средствами фирмы Texas Instruments, такими как оцеTM
ночные платы eZdsp F2812/28335. Может быть использован для обучения студентов и слушателей курсов повышения квалификации специалистов промышленности и преподавателей вузов.
Ориентирован на разработчиков встраиваемых систем управления комплектных электроприводов, силовых преобразователей и систем питания. Предназначен для студентов, обучающихся
по специальностям: «Электропривод и автоматика ПУ и ТК»; «Электрический транспорт»; «Электрооборудование автономных объектов»; «Промышленная электроника».
ISBN 978-5-383-00471-5
© Московский энергетический институт
(технический университет), 2010
ПРЕДИСЛОВИЕ
Последние годы характеризуются бурным развитием цифровых технологий во всех областях техники. Микроконтроллеры стали адаптированными
под преимущественную область применения за счет интеграции на кристалл
большого числа специальных периферийных устройств, способных автономно решать типовые задачи приложения с минимальными затратами
ресурсов центрального процессора. Специализированные микроконтроллеры
для применения в электроприводе, энергетике и силовой электронике получили общепринятые названия Motor Control (управление двигателями),
Motion Control (управление движениями), Power Control (управление мощностью). Усложнение алгоритмов цифрового управления электрооборудованием потребовало перехода к более производительным архитектурам построения центрального процессора — к специализированным сигнальным
микроконтроллерам высокой производительности (вплоть до 300 млн оп/с)
с большим объемом встроенной флэш-памяти программ (до 128 К слов),
памяти данных (до 34 К слов), с рядом интегрированных на кристалл периферийных устройств (аналого-цифровые преобразователи, менеджеры событий, квадратурные декодеры, контроллеры различных интерфейсов и т.п.),
возможностью программирования и отладки непосредственно на языке
высокого уровня С/С++.
Разработка современных систем встроенного управления комплектными
электроприводами, силовыми преобразователями и источниками питания
требует знания основ модульного проектирования и отладки программного
обеспечения (ПО) на Ассемблере и языке высокого уровня С/С++ с использованием интегрированных компьютерных систем разработки, допускающих отладку в реальном времени, в том числе непосредственно в изделии.
Роль языка С/С++ возрастает из-за сложности системы команд современных
микроконтроллеров, на изучение которой тратится значительное время.
Использование языка С/С++ с большим количеством уже существующих
стандартных и прикладных библиотек позволяет значительно ускорить разработку, сократить время выхода новых изделий на рынок. Это особенно
важно в условиях современной России, пытающейся активно развивать
энергосберегающие, обрабатывающие и перерабатывающие технологии,
машиностроение.
Настоящее учебное пособие представляет собой практический курс основ
программирования встроенных систем управления на языке высокого
уровня С/С++, ориентированное на подготовку и переподготовку специалистов в области современного электрооборудования и силовой электроники.
Курс базируется на одних из лучших в отрасли специализированных сигнальных микроконтроллерах TMS320x28xxx фирмы Texas Instruments, явля3
ющейся мировым лидером в области сигнальных процессоров (более 50 %
всего мирового рынка). Он построен таким образом, что позволяет в течение
ограниченного времени (до 100 ч самостоятельной работы или работы в
лаборатории) получить начальные навыки программирования на языке
С/С++ в интегрированной среде разработки и отладки программного обеспечения Code Composer Studio (CCS) с учетом особенностей архитектуры,
встроенной памяти и периферии современных микроконтроллеров, принципов организации системы прерываний.
Студенты и слушатели курсов переподготовки получают навыки программной реализации типовых задач управления, таких как цифровые регуляторы и фильтры, блоки преобразования координат, навыки создания и
отладки полноценных структур подчиненного регулирования координат приводов постоянного тока и векторного управления приводами переменного
тока. Осваивают способы подключения специальных библиотек (таких как
IQmath), обеспечивающих эффективную реализацию систем управления
реального времени.
Изучение собственно языка С/С++ не является главной целью книги (для
этого существует много хороших учебников) — оно ведется на фоне изучения архитектуры специализированных микроконтроллеров, принципов
работы и функциональных возможностей встроенных периферийных устройств, методов их программирования, современной технологии модульной
разработки и отладки проектов, в которых часть задач может быть реализована на Ассемблере, а часть — на языке С/С++, методов отладки проектов в
реальном времени с развитыми встроенными возможностями визуализации
динамических процессов вплоть до построения графиков, фазовых портретов и гармонического анализа выходных сигналов. Все перечисленные вопросы относятся к теоретической части и рассматриваются постепенно в процессе освоения студентами и слушателями курсов технологии разработки
программ в среде CCS. Это первая книга на русском языке, которая отвечает
на вопрос: как разрабатывать программное обеспечение для специализированных сигнальных микроконтроллеров Motor Control, являясь поэтапным
руководством для начинающих.
Курс состоит из девяти глав, каждая из которых представляет собой отдельное исследование. Теоретический материал чередуется с контрольными вопросами и практической самостоятельной работой на оборудовании.
В книге использованы богатый международный опыт и методика проведения фирмой Texas Instruments семинаров по семейству микроконтроллеров
‘C2000, опыт обучения студентов по курсу «Микропроцессорные системы
управления» на кафедре Автоматизированного электропривода МЭИ, опыт
переподготовки специалистов промышленности по курсу «Проектирование
и эксплуатация систем прямого микроконтроллерного управления двигателями и технологическим оборудованием» в Учебно-научно-консультационном центре «Texas Instruments — МЭИ», многолетний опыт разработки
заказных цифровых систем управления для отечественных серий преобразователей частоты и комплектных электроприводов фирмы «НПФ Вектор».
4
В книге применяются следующие соглашения по умолчанию:
• все вновь вводимые термины из области процессорной техники выделены курсивом;
• ключевые слова и директивы Ассемблера и языка С++ выделены жирным шрифтом;
• наиболее важные моменты, на которые следует обратить особое внимание, подчеркнуты.
Главы 1, 2 написаны В.Ф. Козаченко, главы 3, 4 — Д.И. Алямкиным,
главы 5, 7 — А.С. Тарасовым, глава 6 — А.С. Анучиным, главы 8, 9 —
А.С. Анучиным, А.С. Тарасовым, А.В. Дроздовым. Концепция построения
курса и общая редакция — В.Ф. Козаченко.
Авторы благодарят студентов 5-го и 6-го курсов кафедры АЭП МЭИ,
которые в порядке лабораторного практикума изучили материал книги и сделали ряд ценных замечаний, особенно М.М. Лашкевича, который тщательно
оттестировал большинство приведенных в книге примеров программ. Примеры программ можно скопировать с официального сайта кафедры АЭП
www.aep.mpei.ac.ru.
Авторы
5
Глава
1
УСТРОЙСТВО И ФУНКЦИОНАЛЬНЫЕ ВОЗМОЖНОСТИ
TM
F2812
ОЦЕНОЧНОЙ1 ПЛАТЫ eZdsp
ДЛЯ РАЗРАБОТКИ И ОТЛАДКИ ПРОГРАММНОГО
ОБЕСПЕЧЕНИЯ В ИНТЕГРИРОВАННОЙ СРЕДЕ
CODE COMPOSER STUDIO. ВВЕДЕНИЕ В СРЕДУ CCS.
ТЕХНОЛОГИЯ РАСПРЕДЕЛЕНИЯ ПАМЯТИ
1.1. ОЦЕНОЧНАЯ ПЛАТА eZdspTM F2812
Устройство, назначение и основные технические
характеристики оценочной платы
TM
Оценочная (отладочная) плата eZdsp
F2812 представляет собой автономное процессорное устройство, созданное для быстрого освоения аппаратных и программных возможностей новейших высокопроизводительных
сигнальных микроконтроллеров семейства ‘C2000 фирмы Texas Instruments
(TI). Микроконтроллеры ‘C2000 являются специализированными устройствами, архитектура и встроенная периферия которых максимально адаптированы для эффективного решения задач прямого цифрового управления
двигателями и силовыми преобразователями энергии, решения задач комплексной автоматизации производства.
Оценочная плата — это также удобное аппаратное средство, позволяющее быстро изучить архитектуру современных сигнальных процессоров,
систему их команд, устройство и режимы работы встроенных периферийных
устройств. Она является базовым аппаратным средством для выполнения
всех работ курса.
Плата поставляется вместе с интегрированной средой Code Composer
Studio (CCS) для компьютерной разработки и отладки программного обеспечения на Ассемблере и на языке высокого уровня С/С++. В дословном переводе интегрированная среда CCS называется «Студией Композитора Кодов»,
так как предоставляет широчайший спектр самых современных возможностей по модульной разработке и отладке программного обеспечения сигнальных микроконтроллеров, на порядок облегчая труд программиста.
Несмотря на то что плата eZdspTM F2812 предназначена в первую очередь для обучения студентов и специалистов промышленности, она может
применяться в проектных и конструкторских организациях для предварительной оценки возможностей реализации любого реального проекта на
6
базе цифровых сигнальных процессоров TMS320F2812. Плата поставляется
с микроконтроллером TMS320F2812, работающим на максимальной частоте
(150 МГц), имеющим максимальный объем встроенной памяти и богатый
набор встроенных периферийных устройств. Разъемы расширения позволяют подключить к плате любую дополнительную периферию в соответствии со спецификой конкретного приложения. При этом для конкретного
приложения оцениваются не только необходимая производительность центрального процессора, нужный объем памяти программ и данных, но и необходимость использования дополнительных периферийных устройств. Такой
подход существенно сокращает сроки проектирования новых микропроцессорных устройств управления.
Плата eZdspTM F2812 снабжается драйвером сопряжения с интегрированной средой Code Composer Studio через параллельный порт персонального
компьютера. На ней также дополнительно устанавливается стандартный
разъем для подключения по интерфейсу JTAG внутрисхемного эмулятора и
выполнения «профессиональной» отладки (без ограничений).
Архитектура и система команд новейших микроконтроллеров Texas
Instruments ‘C283xx с двумя параллельно работающими конвейерами для
вычислений с фиксированной и плавающей точками (начало поставок 2008),
ориентированных на самые сложные задачи управления, полностью совместима с архитектурой центрального процессора ‘C281x, установленного на
TM
плате eZdsp F2812. Совместимость поддерживается также для микроконтроллеров ‘C280x, ориентированных на управление силовыми источниками
питания. Таким образом, оценочная плата является хорошей начальной
базой для всех специалистов, занятых разработкой современных систем
встроенного цифрового управления двигателями, источниками вторичного
питания, системами комплексной автоматизации производства.
Основные технические характеристики платы:
• центральный процессор — TMS320F2812;
• скорость выполнения операций — 150 млн оп/с (150 MIPS);
• объем встроенной на кристалл памяти данных, оперативное запоминающее устройство — ОЗУ (RAM) — 18К 16-разрядных слов;
• объем встроенной на кристалл памяти программ, флэш-памяти — 128К
16-разрядных слов;
• объем внешней по отношению к микроконтроллеру (расширенной) статической памяти СОЗУ (SRAM), расположенной на плате, — 64К 16-разрядных слова;
• частота тактового генератора 30 МГц. Коэффициент внутреннего умножения тактовой частоты — 5;
• разъемы расширения для ввода аналоговых сигналов и ввода/вывода
цифровых сигналов, в том числе для сопряжения со встроенными периферийными устройствами;
• встроенный контроллер JTAG-интерфейса в соответствии со стандартом IEEE 1149.1 для подключения внутрисхемных эмуляторов;
7
• встроенный на плату внутрисхемный эмулятор JTAG-интерфейса IEEE
1149.1 с выходом на параллельный порт персонального компьютера;
TM
F2812 для работы с интегрирован• драйвер оценочной платы eZdsp
ным пакетом TI Code Composer Studio для разработки и отладки программного обеспечения;
• питание от одного внешнего источника питания +5 В. Преобразователь
переменного тока 220 В в постоянный ток напряжением +5 В (сетевой адаптер) входит в комплект поставки.
TM
F2812 показана на
Функциональная схема оценочной платы eZdsp
рис. 1.1, а ее конструкция — на рис. 1.2. Главный интерфейс платы — это
JTAG, через него обеспечивается как загрузка, так и отладка программного
обеспечения. Имеются также разъемы расширения для подключения внешних устройств пользователя.
Оценочная плата имеет габариты 13,5×7,6 см и выполнена по многослойной технологии.
Для подключения питания +5 В служит разъем P6. Максимальный потребляемый от сетевого адаптера ток не должен превышать 500 мА. Если при
подключении к плате дополнительных устройств сопряжения потребляемый
ток может превысить это значение, то сетевой адаптер должен быть заменен
более мощным.
Оценочная плата имеет минимально необходимый набор оборудования,
достаточный для разработки и отладки программного обеспечения с использованием интегрированной среды Code Composer Studio. Для работы с пери-
Тактовый
генератор
JTAG
Статическое
ОЗУ
64К*16
XZCS6AND7n
Микроконтроллер
TMS320F2812
XTAL1/OSCIN
Дискретные
порты
ввода/
вывода
Рис. 1.1. Блок-схема оценочной платы eZdsp
8
Порт расширения
аналоговых входов
Аналого-цифровой
преобразователь
Порт расширения
цифрового
ввода/вывода
Контроллер
JTAG-интерфейс/
параллельный порт
JTAG
внешний
Параллельный
порт
eZdspТМ F2812
TM
F2812
Рис. 1.2. Конструкция оценочной платы eZdsp
TM
F2812
ферией обязательно потребуется подключение к плате дополнительных устройств, в том числе преобразователей уровней сигналов и специальных
средств защиты входов процессора от недопустимых напряжений.
На плате eZdspTM F2812 установлены два планарных зеленых светоизлучающих диода. Первый из них DS1 индицирует наличие на плате напряжения питания 5 В, а второй DS2 — состояние битового выхода процессора XF,
который может программно модифицироваться. Вы можете управлять этим
светодиодом по своему усмотрению для индикации хода выполнения программы.
Для удобства подключения цифрового осциллографа на плате имеются
две тестовые точки — цифровой TP1 и аналоговой TP2 земли, которыми
будем пользоваться при исследовании работы периферийных устройств.
Практическая работа
1. Найдите на оценочной плате: центральный процессор, расширенную
статическую память, контроллер JTAG-интерфейса в параллельный порт.
2. Определите число слоев печатной платы.
3. Найдите разъем для подключения питания, разъем для подключения
компьютера, разъем для подключения внутрисхемного эмулятора.
4. Найдите светодиоды DS1 (питание) и DS2 (вывод XF микроконтроллера).
5. Найдите тестовые точки цифровой TP1 и аналоговой TP2 земли.
9
Спецификация разъемов оценочной платы eZdspTM F2812
Оценочная плата имеет пять соединителей для расширения ее функциональных возможностей с помощью дополнительно подключаемых устройств. Первый вывод каждого соединителя имеет квадратную позолоченную окантовку, в отличие от остальных выводов, имеющих круглую
окантовку. Большинство соединителей представляет собой поле отверстийконтактов, в которые вставляется первичная часть разъема и пропаивается с
обратной стороны платы. Эта операция должна выполняться квалифицированным монтажником. Модуль расширения пользователя подключается к
ответной части одного или нескольких разъемов.
В процессе выполнения практических работ будете неоднократно обращаться к приведенной справочной информации для того, чтобы определить,
куда выведены те или иные сигналы микроконтроллера (например, для
осциллографирования). Назначение соединителей представлено в табл. 1.1.
Табл и ц а 1.1
Назначение соединителей в оценочной плате eZdspTM F2812
Соединитель
Назначение
P1
JTAG-интерфейс для отладки с помощью стандартного внутрисхемного эмулятора
P2
Разъем расширения памяти и периферии
P3
Параллельный порт контроллера JTAG-интерфейса для отладки от компьютера
через стандартный параллельный порт
P4/P8/P7
Интерфейс сопряжения со встроенными периферийными устройствами
P5/P9
Интерфейс аналогового ввода/вывода
P6
Разъем подключения источника питания
JTAG-интерфейс, P1
Отладочная плата, изображенная на рис. 1.3, имеет 14-выводной JTAGинтерфейс P1 для подключения внутрисхемного эмулятора и выполнения
профессиональной отладки программного обеспечения без каких-либо ограничений. Спецификация сигналов на разъеме — стандартная для всех сигнальных процессоров Texas Instruments и приведена в табл. 1.2. Отсутствующий шестой вывод выполняет функцию ключа.
P1
13
11
9
7
14
12
10
8
5
3
1
1
4
2
2
JTAG
Рис. 1.3. Расположение выводов JTAG-интерфейса (вид платы сверху)
10
Табл и ц а 1.2
Спецификация выводов JTAG-интерфейса (разъем P1)
Вывод
13
11
9
7
5
3
1
Сигнал
EMU0
TCK
TCK-RET
TDO
PD (+5V)
TDI
TMS
Вывод
14
12
10
8
6
4
2
Сигнал
EMU1
GND
GND
GND
Нет вывода
GND
TRST-
Интерфейс расширения памяти и периферии, P2
Интерфейс имеет 60 выводов, показанных на рис. 1.4, назначение которых приведено в табл. 1.3.
На разъем P2 выведены сигналы 16-разрядной двунаправленной шины данных (XD0-XD15), 19-разрядной однонаправленной шины адреса (XA0-XA18),
шины управления (XWE, XRD# и др.), шины питания и цифровой земли,
сигналы выборки кристаллов в зонах 0 и 1 внешней памяти (XZCS0AND1#),
а также зоне 2 (XZCS2#). Это позволяет подключить к микроконтроллеру
дополнительную память или периферийные устройства.
P2
2
1
4
3
6
5
8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 41 43 45 47 49 51 54 56 58 60
7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 42 44 46 48 50 52 53 55 57 59
Рис. 1.4. Расположение выводов на разъеме расширения P2 (вид платы сверху)
Та бл и ца 1.3
Спецификация сигналов разъема расширения P2
Вывод
1
2
3
4
5
6
7
8
9
10
Сигнал
+5V
+5V
XD0
XD1
XD2
XD3
XD4
XD5
XD6
XD7
Вывод
11
12
13
14
15
16
17
18
19
20
Сигнал
XD8
XD9
XD13
XD14
XD15
XA0
XA1
XD10 XD11 XD12
Вывод
21
22
23
24
25
26
27
28
29
30
Сигнал
XA2
XA3
XA4
XA5
XA6
XA7
XA8
XA9
XA10
XA11
Вывод
31
32
33
34
35
36
37
38
39
40
Сигнал XA12
Вывод
41
XA13
42
XA14 XA15 GND
43
44
45
GND
46
Сигнал XR#W 10K Pullup XWE XRD# +3.3V XNMI/INT13
Вывод
51
Сигнал GND
XZCS0AND1# XZCS2# XREADY
10K
47
48
49
50
XRS#/RS#
Не подкл.
GND
GGND
58
59
60
52
53
54
55
56
57
GND
A16
A17
A18
XHOLD#
XHOLDA#
Не подкл. Не подкл. Не подкл.
11
Параллельный порт/JTAG-интерфейс, P3
TM
Оценочная плата eZdsp
F2812 имеет обычный параллельный порт для
подключения к персональному компьютеру (стандарты ECP, EPP и SPP8 двунаправленной передачи данных). Со стороны оценочной платы параллельный интерфейс преобразуется в JTAG-интерфейс, обеспечивая доступ к процессору со стороны компьютера при отладке. Со стороны компьютера
необходимо установить соответствующий драйвер, который будет позволять
интегрированной среде Code Composer Studio загружать и отлаживать программы в реальном времени, используя параллельный интерфейс. Драйвер
поставляется вместе с отладочной платой.
Интерфейс встроенных периферийных устройств, P4/P8/P7
Интерфейс обеспечивает сопряжение внешних устройств со встроенными
на кристалл микроконтроллера периферийными устройствами. Схема расположения выводов на разъемах P4, P8, P7 представлена на рис. 1.5.
Назначение сигналов на разъемах P4, P8 показано в табл. 1.4. На разъем
P8 выведены сигналы наиболее часто используемых периферийных устройств:
• двух модулей полного сравнения менеджеров событий А и В, обеспечивающих управление силовыми ключами двух трехфазных мостовых инверторов напряжения (PWM1—PWM6 и PWM7—PWM12), в том числе в
режиме широтно-импульсной модуляции базовых векторов;
• двух дополнительных ШИМ-генераторов (модулей сравнения), реализованных на базе таймеров 1 и 2 (T1PWM/T1CMP и T2PWM/T2CMP);
• модуля аппаратной блокировки ШИМ-сигналов, формируемых менеджерами событий А и В по сигналам внешних аварий (T1CTRIP/PDPINTA# и
T3CTRIP/PDPINTB#);
• трехканального модуля захвата внешних событий, например, для
сопряжения с потенциальными датчиками положения на элементах Холла
(CAP1, CAP2, CAP3) и «квадратурного» декодирования, например, сигналов
с импульсных датчиков положения (QEP1, QEP2, QEPI1);
• таймера/счетчика в режиме внешнего тактирования (TCLKINA,
TDIRA);
• синхронного периферийного интерфейса (SPISIMOA, SPISOMIA,
SPICLKA, SPISTEA), предназначенного для расширения дискретного
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
P4
2
1
4
3
6
5
8
7
10
9
12
11
14
13
16
15
18
17
20
19
22
21
24
23
26
25
28
27
30
29
32
31
34
33
36
35
38
37
40
39
P8
1
2
3
4
5
6
7
8
9
10
Рис. 1.5. Расположение выводов разъемов P4, P8, P7 (вид сверху)
12
P7
ввода/вывода, организации межпроцессорных коммуникаций, подключения
дополнительных периферийных устройств (ЦАП, АЦП и т.д.);
• последовательного коммуникационного интерфейса A (SCITXDA,
SCIRXDA) для поддержки стандартных последовательных каналов связи
(RS-232, RS-422, RS-485);
• контроллера промышленной шины CAN (CANTXA, CANRXA), предназначенного для организации локальных промышленных сетей на базе
CAN-интерфейса, построения распределенных мультимикропроцессорных
систем, расширения ввода/вывода.
Кроме того, на этот разъем выведен сигнал XCLKOUT внутреннего тактового генератора процессора, позволяющий не только убедиться в работоспособности платы eZdsp
TM
F2812, но и тактировать внешние устройства.
Табли ц а 1 . 4
Спецификация сигналов на разъемах P4 и P8
P4
Вывод
P4
Сигнал
P8
Вывод
P8
Сигнал
P8
Вывод
P8
Сигнал
1
+5 V
1
+5 V
2
+5 V
2
XINT2/ADCSOC
3
SCITXDA
4
SCIRXDA
3
MCLKXA
5
XINT1#/XBIO#
6
CAP1/QEP1
4
MCLKRA
7
CAP2/QEP2
8
CAP3/QEPI1
5
MFSXA
9
PWM1
10
PWM2
6
MFSRA
11
PWM3
12
PWM4
7
MDXA
13
PWM5
14
PWM6
8
MDRA
15
T1PWM/T1CMP
16
T2PWM/T2CMP
9
NC
17
TDIRA
18
TCLKINA
10
GND
19
GND
20
GND
11
CAP5/QEP4
21
NC
22
XINT1N/XBIO#
12
CAP6/QEPI2
23
SPISIMOA
24
SPISOMIA
13
T3PWM/T3CMP
25
SPICLKA
26
SPISTEA
14
T4PWM/T4CMP
27
CANTXA
28
CANRXA
15
TDIRB
29
XCLKOUT
30
PWM7
16
TCLKINB
31
PWM8
32
PWM9
17
XF/XPLLDIS#
33
PWM10
34
PWM11
18
SCITXDB
35
PWM12
36
CAP4/QEP3
19
SCIRXDB
37
T1CTRIP/PDPINTA#
38
T3CTRIP/PDPINTB#
20
GND
39
GND
40
GND
13
На разъем P4 выведены сигналы менее часто используемых периферийных устройств:
• многоканального последовательного буферизированного порта
(MCLKXA, MCLKRA, MFSXA, MFSRA, MDXA, MDRA), обеспечивающего
как синхронную, так и асинхронную передачу и предназначенного для расширения ввода/вывода и организации межпроцессорных коммуникаций;
• модуля захвата второго менеджера событий B (CAP5/QEP4,
CAP6/QEPI2);
• второго последовательного коммуникационного интерфейса B
(SCITXDB, SCIRXDB);
• дополнительных ШИМ-генераторов (модулей сравнения), реализованных на базе таймеров 3 и 4 (T3PWM/T3CMP и T4PWM/T4CMP);
• второго таймера/счетчика в режиме внешнего тактирования
(TCLKINB, TDIRB).
Кроме того, на этот разъем выведен сигнал XF — битового флага, управляемого непосредственно программой пользователя.
На разъем P7 (табл. 1.5) выведен ряд специальных входов менеджеров
событий A и B, которые могут использоваться в качестве сигналов блокировки выходов ШИМ-генераторов: либо при приёме сигнала аварии в силовом преобразователе, либо при приёме сигнала превышения выходным
током заданного порогового значения (функция автоматического токоограничения). При этом сигналы C1TRIP#, C2TRIP#, C3TRIP# могут блокировать
соответственно стойки 1, 2 и 3 первого инвертора, а сигналы С4TRIP#,
C5TRIP#, C6TRIP# — стойки 4, 5, 6 второго инвертора.
Возможен также независимый приём сигналов аварий или блокировки
ШИМ для всех четырех дополнительных каналов ШИМ-генератора, реализованных на базе таймеров T1, T2, T3, T4 — сигналы T1CTRIP—T4CTRIP.
Это обеспечивает идентификацию аварии вплоть до конкретного силового
ключа.
Кроме того, имеется возможность сборки по «монтажному ИЛИ» всех
аварийных сигналов в один сигнал PDPINTA# или PDPINTB# и одновременной блокировки сразу всех ШИМ-выходов менеджера событий А или B соответственно.
Табл и ц а 1.5
Спецификация сигналов на разъеме P7
Вывод
1
2
3
4
5
Сигнал
С1TRIP#
C2TRIP#
C3TRIP#
T2CTRIP#/EVASOC#
C4TRIP#
Вывод
6
7
8
9
10
Сигнал
C5TRIP#
C6TRIP#
T4CTRIP#/EVBSOC#
NC
GND
14
Аналоговый интерфейс, P5/P9
Для сопряжения с источниками аналоговых сигналов используется
30-выводной аналоговый интерфейс. Схема расположения выводов на разъемах P5/P9 представлена на рис. 1.6.
Встроенный в микроконтроллеры аналого-цифровой преобразователь
(ADC) является 12-разрядным последовательным АЦП с устройством
выборки-хранения и двумя входными 8-канальными мультиплексорами А и
В, входы которых выведены на разъемы P9 (ADCINA0-7) и P5 (ADCINB0-7),
представленные в табл. 1.6.
Общее число аналоговых входов — 16. Формат ввода аналоговых сигналов
0—3 В. Дополнительно на разъем выведены сигналы встроенных в аналогоцифровой преобразователь опорных источников питания ADCREFP (2 В),
ADCREFM (1 В) и сигналы аналоговой земли (GND). Все входы АЦП являются «голыми» — они не содержат ни входных фильтров низкой частоты для
защиты от помех, ни схем преобразования уровней, ни схем защиты от переполюсовки и превышения допустимого входного напряжения. Соблюдайте
осторожность при подключениях!
ANALOG
P5
1
2
3
4
5
6
7
8
9
10
2
4
6
8
10
12
14
16
18
20
1
3
5
7
9
11
13
15
17
19
P9
Рис. 1.6. Схема расположения выводов аналогового интерфейса (вид сверху)
Табли ц а 1 . 6
Спецификация сигналов на разъемах P5, P9
P5
Вывод
Сигнал
P9
Вывод
Сигнал
P9
Вывод
Сигнал
1
ADCINB0
1
GND
2
ADCINA0
2
ADCINB1
3
GND
4
ADCINA1
3
ADCINB2
5
GND
6
ADCINA2
4
ADCINB3
7
GND
8
ADCINA3
5
ADCINB4
9
GND
10
ADCINA4
6
ADCINB5
11
GND
12
ADCINA5
7
ADCINB6
13
GND
14
ADCINA6
8
ADCINB7
15
GND
16
ADCINA7
9
ADCREFM
17
GND
18
VREFLO
10
ADCREFP
19
GND
20
NC
15
Контрольные вопросы
1. К каким точкам на плате можно подключить осциллограф, чтобы проверить, работает ли центральный процессор, а именно генерируется ли выходная тактовая частота?
2. К каким точкам на плате нужно подключить осциллограф, чтобы проверить правильность формирования программой пользователя выходного битового сигнала XF?
3. Можно ли подключить к оценочной плате переменный резистор и сигнал с его
движка подать на вход АЦП? В каком диапазоне напряжений сможете менять уровень
входного аналогового сигнала?
Режимы работы оценочной платы eZdspTM F2812
На плате имеется семь переключателей, которые позволяют установить
нужный режим работы устройства. Все переключатели для удобства пользователя расположены в одном компактном месте, схема их размещения представлена на рис. 1.7.
Переключатели JP1 и JP2 являются основными. Они позволят задать
режим работы центрального процессора с точки зрения использования
памяти, а также разрешить программирование встроенной флэш-памяти.
Переключатель JP9 рекомендуется использовать только квалифицированным
пользователям:
JP1 Режим работы процессора:
1-2 Режим микропроцессора (используется внешняя память);
2-3 Режим микроконтроллера (используется внутренняя встроенная
память).
По умолчанию используется режим микроконтроллера.
JP2 Напряжение программирования флэш-памяти:
1-2 Подается (всегда в этой позиции — вместо переключателя перемычка);
2-3 Не подается.
JP9 Запрещение фазосдвигающей логики — умножителя частоты (PLL)
1-2 Работа PLL разрешена (положение по умолчанию);
2-3 Работа PLL запрещена.
JP7 – Boot Mode 3
1
2
JP1- XMP/MC#
3
1
JP8 – Boot Mode 2
1
2
3
1
JP9 – PLL Disable
1
2
2
3
JP11 – Boot Mode 1
2
3
JP12 – Boot Mode 0
3
1
2
3
JP2 – Flash Power Supply
1
2
3
Рис. 1.7. Схема расположения переключателей и их положение по умолчанию
16
Сигнал (PLL) защелкивается при сбросе системы и далее может использоваться как битовый выход XF. Фазосдвигающая логика обеспечивает
умножение частоты внешнего тактового генератора на программно заданный
коэффициент масштабирования.
Четыре других переключателя JP7, JP8, JP11 и JP12 задают режим работы
процессора при включении питания — режим начальной загрузки.
Микроконтроллеры ‘C28 имеют встроенное, запрограммированное на
заводе-изготовителе ПЗУ, в котором находится программа начальной
загрузки процессора (boot-loading software). Эта программа автоматически
выполняется сразу после сброса процессора (включения питания) и тестирует состояние нескольких выводов процессора (битовых портов
ввода/вывода GPIO), чтобы определить, какой режим начальной загрузки
аппаратно установил пользователь. Состояние этих выводов связано с
положением переключателей JP7, JP8, JP11, JP12 на оценочной плате, что
позволяет задать один из нескольких возможных режимов начальной
загрузки. Выбор режима представлен в табл. 1.7.
Табл и ц а 1.7
Выбор режима начальной загрузки переключателями JP7, JP8, JP11, JP12
Аппаратно-заданная конфигурация выводов
Режим начальной
загрузки
GPIOF4
GPIOF12
JP7
BOOT3
(SCITXDA)
JP8
BOOT2
(MDXA)
Запуск предварительно загруженной про- Установлен
граммы во флэш-памяти: FLASH. Переход
по адресу 0x3F 7FF6. В этом месте до сброса
процессора должна быть записана команда
перехода на программу пользователя
GPIOF3
GPIOF2
JP12
JP11
BOOT0
BOOT1
(SPISTEA) (SPICLK)
Х
Х
Х
Х
Х
Вызов загрузчика по последовательному
синхронному интерфейсу SPI: SPI_BOOT.
Программа загружается из внешнего последовательного ПЗУ (SPI EEPROM)
Снят
Установлен
Вызов загрузчика по последовательному асинхронному интерфейсу SCI-A: SCI_BOOT.
Программа загружается по коммуникационному каналу связи, например, от ПК по интерфейсу RS-232
Снят
Снят
Установлен Установлен
Запуск программы, ранее загруженной во
встроенное ОЗУ (банк H0): H0. Передача
управления по адресу 0х3F 8000
Снят
Снят
Установлен
Снят
Запуск программы, записанной в однократно-программируемое ПЗУ: OTP. Передача управления по адресу 0x3D 7800
Снят
Снят
Снят
Установлен
Вызов программы загрузчика по параллельному порту Port B: PARALLEL_BOOT
Снят
Снят
Снят
Снят
17
Так, пользователь может задать режим выполнения программы, уже записанной ранее во флэш-память (режим FLASH), например, с помощью внутрисхемного эмулятора по интерфейсу JTAG. В этом случае собственно
загрузка программы не производится и управление сразу передается программе пользователя. Можно задать и режим выполнения программы, ранее
загруженной в оперативную память микроконтроллера (режим H0). Эта
опция используется по умолчанию при выполнении практических работ.
Имеются и другие варианты, в частности можно передать управление загрузчику программы из компьютера в микроконтроллер по последовательному
коммуникационному интерфейсу RS-232 (режим SCI_BOOT).
Дополнительно загрузочное ПЗУ микроконтроллера содержит таблицы
стандартных функций, таких как SIN, COS, предназначенные для ускорения
математических вычислений.
Практическая работа
1. Проверьте состояние всех переключателей на оценочной плате. Убедитесь, что оно соответствует положению по умолчанию.
Контрольные вопросы
1. Какой режим начальной загрузки выберете для «перепрошивки» рабочей программы во флэш-память микропроцессорной системы управления преобразователем частоты после отладки в ОЗУ, если система управления имеет выход на интерфейс RS-232, а
внутрисхемный эмулятор недоступен?
2. Понадобится ли для этой операции специальное программное обеспечение на компьютере?
3. В какое положение необходимо будет установить переключатели режима управления начальной загрузкой после завершения «перепрошивки», чтобы запустить модифицированную рабочую программу в реальном времени?
1.2. ОРГАНИЗАЦИЯ ПАМЯТИ.
КАРТА ПАМЯТИ ОЦЕНОЧНОЙ ПЛАТЫ eZdspTM F2812
Память и карта памяти оценочной платы
Основной объем программной памяти и памяти данных расположен на
кристалле микроконтроллера:
• 128 К слов программной флэш-памяти;
• два блока ОЗУ однократного доступа (SARAM) по 4К слова каждый;
• один блок ОЗУ однократного доступа (SARAM) объемом 8К слов;
• два блока ОЗУ однократного доступа (SARAM) по 1К слову каждый.
Таким образом, общий объем встроенной на кристалл процессора памяти
программ составляет 128К слов, а памяти данных — 18К слов.
Дополнительно на плате установлена внешняя статическая память (статическое ОЗУ — SRAM) объемом 64К слова, т.е. память данных аппаратно расширена до 82К слов, что позволяет разрабатывать сложные проекты.
18
Табл и ц а 1.8
Карта памяти оценочной платы eZdsp
Начальный
адрес
блока
Встроенная на кристалл память
On-Chip Memory
Память данных
Data Space
TM
F2812
Внешняя память (расширенная)
External Memory (XINTF)
Начальный
Память программ Память данных Память программ адрес
блока
Prog Space
Data Space
Prog Space
0x00
6000
Перифер.фрейм 1
Peripheral Frame 1 (4К×16,
Protected) Защищен
0х00
6000
0x00
7000
Перифер.фрейм 2
Peripheral Frame 2 (4К×16,
Protected) Защищен
0x00
8000
Банк L0 статического ОЗУ L0 — SARAM
(4К×16, Secure Block) Секретный блок
0x00
9000
Банк L1 статического ОЗУ L1 — SARAM
(4К×16, Secure Block) Секретный блок
Зарезервировано
Зарезервировано
0x00
A000
Зона 2 расширенной памяти
XINTF Zone 2 (0,5М×16, XZCS2/)
0х08
0000
Зарезервировано
Зона 6 расширенной памяти
0x10
XINTF Zone 6 (0,5М×16, XZCS6AND7/) 0000
0x18
0000
0x3D
7800
Однократно-программируемое или масочнопрограммируемое ПЗУ OTP (ROM) (1К×16,
Secure Block) Секретный блок
0x3D
7C00
Зарезервировано (1К×16)
0x3D
8000
Флэш-память или ПЗУ Flash (ROM) (128К×16, Зарезервировано
Secure Block) Секретный блок
0x3F
7FF8
128-битовый код секретности (Password)
0x3F
8000
Банк H0 статического ОЗУ H0 — SARAM
(8К×16)
0x3F
A000
Зарезервировано
0x3F
F000
Загрузочное ПЗУ Boot ROM (4К×16)
Разрешено, если MP/(MC#)=0
0x3F
FFFF
Таблица векторов прерываний в загрузочном
ПЗУ BROM Vector — ROM (32×32)
Разрешена, если VMAP=1, MP/(MC#)=0,
ENPIE=0
Зона 7 расширенной памяти XINTF
Zone 7 (16К×16, XZCS6AND7/)
Разрешена, если MP/(MC#)=1
0x3F
C000
Таблица векторов прерываний в расширенной памяти XINTF Vector —
RAM (32×32) Разрешена, если
VMAP=1, MP/(MC#)=1, ENPIE=0
0x3F
FFFF
19
Окончание табл. 1.8
Начальный
адрес
блока
Встроенная на кристалл память
On-Chip Memory
Память данных
Data Space
Начальный
адрес
Память программ Память данных Память программ
блока
Prog Space
Data Space
Prog Space
0x00
0000
Таблица векторов прерываний M0-Vector —
RAM (32×32) Разрешено, если VMAP=0
0x00
0040
Банк М0 статического ОЗУ M0 — SARAM
(1К×16)
0x00
0400
Банк М1 статического ОЗУ M1 — SARAM
(1К×16)
0x00
0800
Перифер. фрейм 0
Peripheral Frame 0
(2К×16)
0х00
0D00
Таблица векторов периф.
прерываний PIE Vector —
Зарезервировано
RAM (256×16)
Разрешено, если
VMAP=1, ENPIE=1
0x00
0E00
Зарезервировано
0х00
2000
Зарезервировано
Внешняя память (расширенная)
External Memory (XINTF)
0x00
0000
Зарезервировано
Зона 0 расширенной памяти XINTF
Zone 0 (8К×16, XZCS0AND1/)
0x00
2000
Зона 1 расширенной памяти XINTF
Zone 1 (8К×16, XZCS0AND1/)
Защищена (Protected)
0x00
4000
Программа для отладки может быть загружена и выполнена как в оперативном запоминающем устройстве, так и во встроенной флэш-памяти. В
процессе выполнения практических работ рекомендуется использовать
только встроенное ОЗУ во избежание «непреднамеренного запароливания»
процессора. При этом плата должна аппаратно конфигурироваться в режим
работы без начальной загрузки (non boot-loader mode), когда управление
автоматически передается рабочей программе пользователя, расположенной
в оперативном запоминающем устройстве.
Замечания
1. Размеры блоков памяти фиксированы и не могут изменяться пользователем.
2. Области памяти, помеченные как резервные, зарезервированы для
будущих применений. Приложения пользователя не должны обращаться к
этим областям памяти.
20
Порядок доступа к памяти
Вся память делится на внутреннюю (встроенную на кристалл микроконтроллера) и внешнюю (расширенную) память. При обращении к памяти реализуется логика единого непрерывного адресного пространства, в котором
одну часть адресов занимает встроенная память, а другую — расширенная.
В том случае, когда адресные зоны областей встроенной и расширенной
памяти перекрываются (например, зона 0x3F F000 — 0x3F FFFF), для идентификации конкретной области памяти используется текущее состояние
входа MP/MC# микроконтроллера. В режиме микропроцессора (MP/MC# = 1)
обеспечивается доступ к внешней памяти, а в режиме микроконтроллера
(MP/MC# = 0) — к внутренней памяти. Управление состоянием входа
MP/MC# реализуется с помощью переключателя JP1. Например, области
загрузочного постоянного запоминающего устройства (ПЗУ) и внешней
памяти в зоне 7 не могут использоваться одновременно. В режиме микроконтроллера (MP/MC#=0) обращение будет автоматически производиться к
встроенному загрузочному ПЗУ, а в режиме микропроцессора (MP/MC#=1) —
к зоне 7 внешней памяти.
С одной стороны, в микроконтроллерах ‘C28xx память унифицирована —
она может использоваться (за одним исключением) и как память данных, и
как память программ. Так, в ОЗУ обычно находятся переменные пользователя (память данных), а в ПЗУ или во флэш-памяти — программы пользователя. Однако оперативное запоминающее устройство можно использовать и
в качестве кодовой памяти, загружая в него программу, подлежащую
отладке, и передавая управление этой программе. Эта операция выполняется
в ходе практических работ. С другой стороны, в процессе загрузки флэшпамяти программа «загрузчик» рассматривает флэш-память как память данных, обеспечивая пересылку новых данных — выполняет программирование
флэш-памяти. Сама программа «загрузчик» при этом будет располагаться в
кодовом ОЗУ процессора, и никаких специальных устройств — программаторов для этой операции не требуется.
Как видно из табл. 1.8, исключениями являются области памяти данных,
отведенные под регистры встроенных периферийных устройств. Регистры
периферийных устройств отображены на память данных — каждый из них
имеет свой адрес в памяти данных, по которому к нему можно обратиться.
Эти области памяти называются периферийными фреймами 1, 2 и 3 и не
могут использоваться в качестве памяти программ.
Унификация памяти существенно облегчает программирование, особенно
на языке высокого уровня, так как пользователь может наиболее рационально разделить память на память программ и данных в соответствии с конкретной задачей. Разделение выполняется с помощью компоновщика.
Термины «защищен» (периферийные фреймы 1 и 2) и «защищена» (зона
расширенной памяти 1) относятся к конвейеру команд и означают, что аппаратно исключается ситуация, когда данные из периферийного регистра/
памяти считываются текущей командой еще до того, как предыдущая
команда смогла их модифицировать. На практике это означает, что операции
с регистрами периферийных устройств памятью будут выполняться в строгом соответствии с порядком, заданным программой пользователя.
21
Флэш-память и однократно-программируемое
постоянное запоминающее устройство
Область флэш-памяти, представленная в табл. 1.9, разбита на отдельные
сектора по 8 или по 16К слов каждый. Последние восемь слов флэш-памяти
отведены для программирования кода секретности (8×16=128 бит). Если
код секретности используется, то в область памяти 0x3F 7F80 — 0x3F 7FF5
должны быть записаны нули (ключ задания режима секретности). Два слова
непосредственно перед кодом секретности предназначены для программирования команды передачи управления на точку входа в программу, которая
может быть либо собственно программой пользователя, либо процедурой
начальной загрузки процессора, расположенной во флэш-памяти.
Табл и ц а 1.9
Таблица адресов секторов флэш-памяти
Диапазон
адресов
Сектор программ и данных, объем
0x3D 8000
0x3D 9FFF
Сектор J, 8К×16
0x3D A000
0x3D BFFF
Сектор I, 8К×16
0x3D C000
0x3D FFFF
Сектор H, 16К×16
0x3E 0000
0x3E 3FFF
Сектор G, 16К×16
0x3E 4000
0x3E 7FFF
Сектор F, 16К×16
0x3E 8000
0x3E BFFF
Сектор E, 16К×16
0x3E C000
0x3E FFFF
Сектор D, 16К×16
0x3F 0000
0x3F 3FFF
Сектор C, 16К×16
0x3F 4000
0x3F 5FFF
Сектор B, 8К×16
0x3F 6000
Сектор A, 8К×16
0x3F 7F80
0x3F 7FF5
Эта область памяти должна быть запрограммирована нулями (0x0000), если используется Модуль Секретности Кода (Code Security Module), в противном случае может
содержать любой код
0x3F 7FF6
0x3F 7FF7
Точка входа загрузчика во флэш-памяти (или ПЗУ)
Boot-to-Flash (or ROM) Entry Point
Здесь должна быть запрограммирована команда перехода на программу загрузчика
(branch instruction)
0x3F 7FF8
0x3F 7FFF
Код секретности (128 бит) — Security Password
Биты кода секретности не должны быть все нулевыми
22
В процессорах ‘F2811, ‘F2812 общий объем флэш-памяти равен 128К словам: четыре сектора 8К×16 и шесть секторов 16К×16. В процессорах ‘F2810
— 64К слова: два банка по 8К×8 и три банка по 16К×16.
Все микроконтроллеры семейства ‘28x дополнительно имеют однократно-программируемое ПЗУ объемом 1К×16, расположенное в диапазоне
адресов 0x3D 7800 — 0x3D 7BFF. Операция записи в это ПЗУ может быть
выполнена только один раз.
Пользователь имеет возможность индивидуально стирать, программировать и верифицировать любой сектор флэш-памяти, оставляя информацию
в других секторах нетронутой. Однако нельзя использовать какой-либо сектор флэш-памяти или однократно-программируемое ПЗУ для работы с другим сектором флэш-памяти. Это означает, что программа загрузки флэшпамяти должна выполняться исключительно из кодового ОЗУ.
Флэш-память и однократно-программируемая память отображены одновременно и на память программ, и на память данных. Поэтому их можно
использовать для хранения как программного кода, так и данных, например
таблиц функций и коэффициентов.
В процессорах ‘C28 число тактов ожидания для работы с флэш-памятью и
однократно-программируемым ПЗУ может программироваться в приложении пользователя. Эффективная производительность флэш-памяти может
быть повышена за счет разрешения работы специального конвейера флэшпамяти. Это можно сделать в регистре опций флэш-памяти (flash option
register). Точное значение производительности процессора при работе с
флэш-памятью зависит от конкретной задачи, а также от числа установленных
тактов ожидания и режима работы конвейера флэш-памяти. Режим конвейерного доступа не используется при работе с однократно-программируемым ПЗУ.
Все микроконтроллеры семейства ‘28xx выпускаются в двух исполнениях:
‘F28xx с флэш-памятью и ‘C28xx с масочно-программируемым на заводеизготовителе ПЗУ того же объема. В процессе разработки новых изделий и в
мелкосерийном производстве используются микроконтроллеры ‘F28xx, а в
крупносерийном производстве — ‘C28xx (с уже не требующей отладки программой, «прошитой» в ПЗУ).
Оперативное запоминающее устройство
однократного доступа SARAM (банки M0, M1, L0, L1, H0)
Все микроконтроллеры семейства ‘x28xx имеют два банка ОЗУ однократного доступа М0 и М1 объемом по 1К слову каждый. Банк М0 — общего
назначения, а банк М1 обычно используется в качестве стека. Указатель
стека после сброса процессора автоматически устанавливается на начало
банка М1 (на адрес 0x00 0400).
Все микроконтроллеры ‘x281x имеют дополнительно 16К слов ОЗУ однократного доступа, которое состоит из трех банков L0, L1, H0 (4К + 4К + 8К).
Каждый из них имеет независимые аппаратные средства минимизации
задержек на конвейере для обеспечения заявленной высокой производительности процессора (150 млн оп/с).
23
Все банки оперативного запоминающего устройства, подобно всем другим блокам памяти в процессорах ‘C28, одновременно отображены и на
память данных, и на память программ. Таким образом, программист может
использовать оперативную память как для загрузки и выполнения программного кода, так и для хранения данных.
Расширенная память
Микроконтроллер TMS320F2812 автоматически формирует сигналы
выборки кристалла при обращении к расширенной памяти или периферии —
сигналы селектирования внешних устройств (CS#). Каждый сигнал соответствует определенной зоне адресов внешней расширенной памяти/периферии (XZ). Интерфейс внешней памяти поддерживает пять независимых зон
внешней памяти. Одна из них имеет свой собственный сигнал выборки
кристалла, а четыре другие — попарно общие сигналы «чип-селекта». Для
каждой зоны пользователь может запрограммировать свое собственное
число тактов ожидания (wait states), а также режим работы с сигналом готовности памяти или без него. Это облегчает организацию интерфейса с внешней памятью. Расширение периферии через XINTF поддерживается только
при отображении периферийных устройств на внешнюю память.
На плате eZdspTM F2812 один из сигналов выборки кристалла
XZCS6AND7# уже подключен к внешнему ОЗУ, а два других сигнала
(ZXCS0AND1# и XZCS2#) выведены на разъем расширения. Это позволяет
при необходимости еще больше расширить объем используемой памяти или
периферии.
При доступе к внешней статической памяти, установленной на плате,
микроконтроллер автоматически формирует строб XZCS6AND7#. Общий
объем этой зоны памяти 0,5М слова, начиная с адреса 0x10 0000 по адрес
0x18 0000. Реально используется только 64К слова в диапазоне адресов от
0x10 0000 до 0x10 FFFF.
Размещение таблицы векторов прерываний
Место размещения таблицы векторов прерываний в памяти микропроцессорной системы определяется в процедуре инициализации системы. Значение имеет режим работы, установленный аппаратно MP/MC# (микропроцессора или микроконтроллера), а также состояние двух битовых флагов VMAP
и ENPIE, устанавливаемых программно. Первый флаг VMAP задает размещение таблицы векторов либо в начальной, либо в конечной области памяти.
Второй флаг ENPIE разрешает работу с таблицей векторов периферийных
прерываний, расположенной в памяти данных и приведенной в табл. 1.10.
При активном использовании периферийных устройств обычно выбирается
второй вариант размещения (PIE Vector), когда для каждого запроса прерывания периферийного устройства выделяется место в памяти данных под
свой собственный вектор перехода на подпрограмму обслуживания запроса
прерывания периферийного устройства.
24
Табли ц а 1 . 1 0
Способы размещения в памяти таблицы векторов прерываний
Таблица
векторов
прерываний
Объем и место размещения
Условие выбора
М0 Vector
32×32-разрядных слова в начальной области VMAP = 0
ОЗУ 00 0000 — 00 003F (банк М0)
PIE Vector
256×16-разрядных слов в области ОЗУ пери- VMAP = 0, ENPIE = 1
ферийных устройств 00 0D00 — 00 0DFF
BROM Vector
32×32-разрядных слова в конечной области VMAP = 1, ENPIE = 0, MP/MC# = 0
загрузочного ПЗУ 3F FFC0 — 3F FFFF
(в режиме микроконтроллера)
XINTF Vector
32×32-разрядных слова в конечной области VMAP = 1, ENPIE = 0, MP/MC# = 1
(в режиме микропроцессора)
внешнего ОЗУ 3F FFC0 — 3F FFFF
Контрольные вопросы
1. Необходимо подключить к оценочной плате eZdspTM F2812 дополнительную внешнюю память объемом 512К слов. Достаточно ли выведенных на разъем P2 разрядов
адресной шины XA0—XA18 для прямой адресации каждой ячейки внешней расширенной памяти?
2. Какой сигнал выборки кристалла внешней памяти XZCS0AND1# или ZXCS2# следует использовать для этой цели?
3. Микросхема внешней памяти имеет следующие управляющие сигналы — выборки
кристалла CS#, разрешения записи WE#, разрешения вывода данных OE#. Какие сигналы управления на разъеме P2 можно использовать для подключения этой микросхемы?
TM
4. Нарисуйте схему подключения микросхемы внешней памяти к eZdsp
F2812.
1.3. ВВЕДЕНИЕ В ИНТЕГРИРОВАННУЮ СРЕДУ РАЗРАБОТКИ
И ОТЛАДКИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
CODE COMPOSER STUDIO
Основы модульной разработки
программного обеспечения
Современная концепция разработки и отладки программного обеспечения
микропроцессорных систем управления предполагает модульный подход,
когда любая сложная задача разбивается на ряд программных модулей, разрабатываемых и отлаживаемых одновременно несколькими программистами, с
последующим объединением наработок в один общий проект. При этом
каждый файл, содержащий программный код (программный модуль), может
быть написан независимо от других файлов и протестирован (отлажен).
Программные модули могут быть написаны на языке Ассемблер конкретного процессора (исходный файл с расширением .asm) или на языке высокого уровня С/С++ (исходный файл с расширением .c).
Преимущество программирования на Ассемблере заключается в возможности максимального использования всех достоинств архитектуры и системы
25
команд процессора, минимизации времени выполнения рабочей программы и
объема требуемой памяти. Например, в семействе микроконтроллеров ‘C28
имеются: специальные способы адресации кольцевых буферов, позволяющие предельно эффективно решать задачи цифровой фильтрации; битовый
процессор для эффективной работы с периферией, реализации логических
контроллеров и дискретных автоматов. Недостатки программирования на
Ассемблере — сложность языка, зависимость от конкретного типа процессора, большая трудоемкость и длительность разработки программного продукта, необходимость освоения нового языка при переходе на новый тип
процессора.
Основное преимущество программирования на языке высокого уровня
состоит в универсальности языка — его аппаратной независимости от типа
процессора. Функцию адаптации к конкретному процессору выполняет программа компилятор. Главный недостаток — более высокие требования к
ресурсам процессора как по производительности, так и по объему памяти
программ/данных. В связи с ростом производительности современных микроконтроллеров, увеличением объема встроенной памяти, развитием двухъядерных архитектур центрального процессора с аппаратной поддержкой
вычислений с фиксированной и плавающей точкой, специальных средств
оптимизации выходного кода этот недостаток постепенно нивелируется.
Поэтому в настоящем курсе основное внимание уделено технологии разработки и отладки программ на языке высокого уровня С/С++.
Традиционные технологии разработки программного обеспечения предполагают использование для написания и редактирования исходных файлов
программ любого текстового редактора, который позволяет сохранять
информацию в стандартном ASCII-коде (без символов форматирования), а
также ряда специальных программ: транслятора с языка Ассемблер, компилятора с языка С/С++, компоновщика, библиотекаря, загрузчика, отладчика
и др. Вызов специальных программ производится поочередно в командной
строке с указанием входных обрабатываемых файлов и опций, задающих
режим работы программы.
Объединение всех перечисленных программ, включая текстовый редактор, в одну интегрированную среду разработки позволяет значительно
упростить и облегчить труд программиста. На рис. 1.8 показаны как отдельные компоненты, входящие в интегрированную среду разработки Code
Composer Studio (далее CCS), так и типовые процессы, связанные с основными этапами разработки и отладки программ:
1) во встроенном редакторе ведется подготовка текстов исходных программных модулей на языках Ассемблер и С/С++, создаются пользовательские библиотеки часто применяемых определений и макросов (макробиблиотеки);
2) исходные файлы программных модулей на языке Ассемблер (.asm)
обрабатываются транслятором с языка Ассемблер с генерацией выходных
файлов в перемещаемом объектном коде (.obj), а также файлов листингов
(.lst). Файлы листингов содержат программы в машинных кодах и списки
ошибок, например неверных инструкций;
26
Текстовый
редактор
Исходный файл
на СИ (.с)
Библиотека
макросов и
описаний на С
Исходный файл
на Ассемблере
(.asm)
Компилятор
С/С++
Файл
на Ассемблере
(.asm)
Библиотека
макросов
на Ассемблере
Транслятор
с Ассемблера
Перемещаемый
объектный
файл (.obj)
Файл
управления
компоновкой
(lnk.cmd)
Стандартные
объектные
библиотеки
Объектные
библиотеки
пользователя
Компоновщик
Абсолютный
объектный
файл(.out)
Файлы
ввода данных
Файл
листинга (.lst)
Загрузчик/
Отладчик
Файл карты
загрузки (.map)
Code
Composer
Studio
Средства
графического
отображения
Файлы
вывода данных
Симулятор
SIM
Аппаратная часть
Оценочная
плата
eZdspTM
Плата
разработчика
EVM
Внутрисхемный
эмулятор
XDS
Контроллер
пользователя
DSP Board
Рис. 1.8. Разработка программного обеспечения в среде Code Composer Studio
27
3) исходные файлы программных модулей на языке С/С++ (.c) обрабатываются компилятором с языка С/С++. Создаются промежуточные выходные
файлы на Ассемблере (.asm), которые в свою очередь обрабатываются транслятором с Ассемблера с созданием выходных объектных файлов в перемещаемом объектном коде (.obj). Компилятор имеет встроенные средства оптимизации выходного кода;
4) специальная утилита внутреннего листинга связывает исходную программу на С/С++ с выходной программой на Ассемблере, чтобы разработчик
мог понять, как они коррелируют друг с другом, и при необходимости
выполнить «ручную оптимизацию»;
5) для стандартизации процесса разработки программного обеспечения
используется «Общий формат объектных файлов» Common Object File
Format (COFF) как для программ, написанных на Ассемблере, так и для программ, написанных на С/С++. Такой подход позволяет разрабатывать часть
программных модулей проекта (критичных ко времени выполнения и объему требуемой памяти) на Ассемблере, а часть — на С/С++;
6) вышеописанный процесс может повторяться для любого числа исходных модулей;
7) ряд уже отлаженных программных модулей в перемещаемом объектном коде может быть объединен в пользовательскую библиотеку объектных
модулей, содержащую набор часто вызываемых пользовательских функций;
8) могут использоваться также стандартные объектные библиотеки,
поставляемые производителями систем разработки ПО, например, для
вычислений в формате с плавающей точкой;
9) множество программных модулей в перемещаемом объектном коде
(.obj), в том числе нужные файлы из объектных библиотек, объединяются в
одну выходную программу в абсолютном объектном коде (.out) с помощью
компоновщика;
10) компоновщик эффективно распределяет ресурсы памяти, доступные
в конкретном микропроцессорном устройстве, для каждого модуля проекта.
При этом используется специальный командный файл управления компоновкой (lnk.cmd), который не только описывает структуру имеющихся ресурсов
памяти программ и данных на целевой плате (банки памяти), но и задает
правила размещения в этих банках различных секций программных модулей;
11) выходом процесса компоновки является абсолютный объектный файл
(.out), который может быть загружен и выполнен на целевой плате с DSPпроцессором в реальном масштабе времени. Дополнительно может генерироваться файл с картой загрузки (.map), в котором содержится информация о
том, в какие банки памяти и конкретно по каким адресам размещены все секции проекта;
12) в состав интегрированной среды CCS входит также загрузчик и
отладчик программ, с помощью которого можно выполнить рабочую программу по шагам, с точками останова или в реальном времени;
13) дополнительные возможности включают в себя: средства наблюдения
за состоянием регистров процессора и переменных в памяти; модификации
значений переменных «на лету»; утилиту подключения файлов ввода и
28
вывода данных для тестирования программ; встроенные средства графического отображения и анализа информации, например получения фазового
портрета или частотного спектра сигнала;
14) еще более мощные возможности могут быть добавлены самим программистом с использованием специальной технологии расширения функций
интегрированной среды CCS;
15) гарантируется аппаратная совместимость со всеми оценочными (типа
eZdsp) и более мощными отладочными (EVM) платами, выпускаемыми фирмой Texas Instruments в помощь разработчикам, а также возможность
отладки с любыми платами, созданными пользователями, через стандартный внутрисхемный эмулятор XDS (главное средство профессиональной
отладки);
16) в состав CCS входит также программно-логическая модель процессора
(симулятор), допускающая загрузку и отладку программ без использования
каких-либо дополнительных аппаратных средств — только с использованием компьютера. Это позволяет отлаживать часть программных модулей
удаленно, в том числе в домашних условиях.
Таким образом, интегрированная среда CCS поддерживает написание
модульного программного кода и позволяет работать над проектом нескольким программистам одновременно, сокращая срок разработки приложения.
Отладка и обновление кода становятся быстрее. Новые проекты могут быть
разработаны с меньшими сроками, так как в них могут использоваться уже
отлаженные модули из предыдущих проектов. При использовании С/С++ код
становится аппаратно-независимым, что позволяет программисту модуля не
думать о распределении памяти. Эта работа будет сделана на этапе компоновки проекта. При изменении любого из модулей производится новая
сборка проекта и вопросы распределения памяти решаются заново, чтобы
исключить вероятность возникновения конфликтов.
Секционирование программных модулей на Ассемблере
Любой программный модуль состоит из секций — отдельных частей,
предназначенных для размещения однотипной информации. В исходном
модуле на Ассемблере с помощью приведенных ниже директив Ассемблера
можно создавать секции трех типов (по умолчанию):
1) .text — кодовая секция — для размещения выполняемого программного кода;
2) .data — секция данных — для размещения констант и таблиц констант,
т.е. инициализированных данных;
3) .bss — секция переменных — для резервирования места в оперативной
памяти под неинициализированные переменные.
Дополнительно разрешается создавать произвольное число пользовательских секций (поименованных пользователем) трех базовых типов «.data»,
«.text», «.bss».
29
Таким образом, существует всего два класса секций:
1) инициализируемые секции (Initialized sections): содержат код «.text»
или фиксированные (заранее определенные пользователем данные) «.data».
Пользовательская секция такого класса создается с помощью директивы
Ассемблера «.sect»;
2) неинициализируемые секции (Uninitialized sections): лишь резервируют место в памяти для размещения неинициализированных данных «.bss».
Пользовательская секция такого класса создается с помощью директивы
Ассемблера «.usect».
Все секции по способу доступа к данным со стороны процессора (по способу адресации в программе на Ассемблере) делятся на два класса:
• отображаемые на память программ (Страница 0 — Page 0);
• отображаемые на память данных (Страница 1 — Page 1).
Если секция по способу доступа к данным относится к отображаемой на
память программ, то это еще не означает, что она фактически размещается в
ПЗУ или флэш-памяти. В процессе отладки программного обеспечения ПЗУ
может быть размещено в одном из банков кодового ОЗУ и только после
завершения отладки «прошито» во флэш-память. Это возможно за счет унификации памяти в микроконтроллерах ‘C28x, когда одна и та же физическая
область памяти может работать и как память данных, и как память программ.
Инициализируемые секции всегда отображаются на память программ, а
неинициализируемые — на память данных. Секции, отображаемые на
память данных, могут фактически размещаться только в оперативном запоминающем устройстве.
Секции, перемещаемые независимо друг от друга, являются перемещаемыми. Все секции одного и того же типа из разных программных модулей
после компоновки объединяются вместе и занимают одну непрерывную
область в конкретном банке памяти целевого устройства. Секции каждого
типа могут размещаться отдельно от других. Это одна из главных функций
компоновщика, которая называется локализацией, размещением (рис. 1.9).
Обычно секции .text и .data после объединения размещаются во флэш-памяти
или ПЗУ (на этапе наладки в кодовом ОЗУ), а секции переменных .bss —
в оперативной памяти (ОЗУ).
Так как большинство микропроцессорных систем имеют несколько различных типов памяти, секционирование помогает более эффективно использовать память на целевой плате, в том числе перераспределять память в процессе работы над проектом. Например, необходимо определить
пользовательскую секцию initial, которая будет содержать все процедуры
инициализации вашего устройства, и разместить ее в области памяти программ (во флэш-память) или определить пользовательскую секцию vector,
содержащую таблицу векторов прерываний, и разместить ее вначале в
памяти данных, а после завершения отладки — во флэш-памяти. Заводские
настройки можно сохранить в однократно-программируемом ПЗУ, как показано на рис. 1.9, или во флэш-памяти.
30
Объектный
файл 1
Секция
.text
Секция
.data
Память целевой платы
Секция
.bss
Память
программ
(флэш-память)
Объектный
файл 2
Память
программ
(ЭППЗУ)
Секция
.text
Память
данных
(ОЗУ)
Секция
.data
Секция
.bss
Рис. 1.9. Объединение однотипных секций двух объектных файлов
Секционирование программных модулей на языке С/С++
Рассмотрим, например, простейшую программу, написанную на языке
С/С++:
int x = 2;
int y = 7;
void main (void)
{
long z;
z = x + y;
}
31
Комментарии для начинающих:
1) любая программа на языке С/С++ состоит из одной или более функций.
Главная, или основная, функция программы называется main (). Имя главной
функции не может быть изменено программистом и всегда должно быть
main (). Круглые скобки после имени main указывают на то, что имя main
принадлежит функции;
2) тип void (пустой тип) перед именем главной функции указывает на то,
что главная функция не возвращает никакого значения;
3) тип void (пустой тип) вместо списка параметров в объявлении главной
функции main (void) означает, что главная функция не принимает аргументов;
4) тело любой функции, в том числе главной функции main, состоит из
составного оператора { }, причем открывающая фигурная скобка { соответствует началу составного оператора (begin), а закрывающая фигурная
скобка } — концу составного оператора (end);
5) в теле любой функции могут содержаться два блока: описания переменных и выполнения;
6) блок описания переменных включает операторы описания типов переменных. Они имеют следующий синтаксис:
<Тип переменной> <Имя_переменной_1, Имя_переменной_2 [,…]>;
В примере, приведенном на с. 31 оператор описания типа переменной
имеет вид:
long z;
Он описывает переменную z как длинное целое число (типа long);
7) блок выполнения в программе содержит только оператор присваивания: z = x + y;
8) переменные, описанные в самом начале программы перед главной функцией main(), являются глобальными переменными, а переменные, описанные в теле функции, — локальными переменными. Локальные переменные
обычно размещаются в стеке;
9) в операторах объявления типов переменных допускается «попутная»
инициализация переменных. Она выполняется с помощью обычных операторов присваивания:
int x = 2;
int y = 7;
Эти два оператора определяют переменные x и y как целые числа со знаком в дополнительном коде (типа int) и инициализируют их значениями 2 и 7
соответственно.
С помощью языка С/С++ допускается одновременное описание нескольких переменных одного и того же типа одним оператором:
int x =2, y = 7;
Не рекомендуется в одном операторе описания типов переменных смешивать инициализированные и неинициализированные переменные.
Проанализировав эту программу, можно заметить, что она одновременно
содержит код и разные типы данных, которые могут быть как глобальными,
так и локальными переменными. В языке С/С++ секции определяются
неявно, т.е., в отличие от Ассемблера, специальных директив объявления
32
секций нет. Это упрощает программирование, так как функцию создания
нужных секций автоматически берет на себя компилятор. Однако механизмом размещения секций в памяти целевого устройства по-прежнему управляет пользователь. Из этого правила есть одно исключение — имеются специальные директивы препроцессора (pragma), которые используются для
явного объявления секций данных, выделяемых под переменные типа
«структура» для резервирования памяти данных под регистры периферийных устройств (рис. 1.10).
Для размещения программного кода в С/С++ используется кодовая секция
.text (как на Ассемблере), для размещения локальных переменных — секция
стека .stack, для размещения глобальных переменных — секция глобальных
переменных .ebss, а для размещения констант, предназначенных для начальной инициализации переменных, — секция констант инициализации .cinit
(см. рис. 1.10).
Кодовые секции должны отображаться на память программ, а секции с
глобальными и локальными переменными — на память данных. Название
каждой секции, как и в Ассемблере, начинается с точки:
• секция глобальных переменных .ebss;
• секция констант для инициализации глобальных переменных .cinit;
• секция локальных переменных, расположенных в стеке, .stack;
• кодовая секция с выполняемыми инструкциями процессора .text.
Как и переменные на языке С/С++, секции могут быть инициализированными и неинициализированными. Список секций, которые могут создаваться
компилятором С/С++, с краткими комментариями назначения секций, представлен в табл. 1.11.
Секция
констант для
инициализации
переменных Init Vals
.cinit
Секция
глобальных
переменных
Global Vars
.ebss
int x = 2;
int y = 7;
void main (void)
{
long z;
z = x + y;
}
Секция
локальных
переменных
Local Vars
.stack
Кодовая секция .text
Рис. 1.10. Секции в программе на С/С++
33
Табли ц а 1.11
Секции, создаваемые компилятором языка С/С++
Имя
секции
Отображается
на страницу
памяти
Описание назначения
Возможное
место
размещения
Инициализированные секции
.text
Исполняемый программный код
Программ
Page 0
ПЗУ или ОЗУ
(при отладке)
.cinit
Константы для начальной инициализации глобаль- Программ
ных и статических переменных
Page 0
ПЗУ или ОЗУ
(при отладке)
.pinit
Таблицы инициализации глобальных конструкто- Программ
ров языка С++
Page 0
ПЗУ или ОЗУ
(при отладке)
.switch
Таблицы векторов переходов для операторов пере- Программ
ключения switch
Page 0
(для опции —mt)
Данных
Page 1
ПЗУ или ОЗУ
.econst
Переменные и массивы, жестко установленные как Данных
константы, например const int k = 3;
Page 1
ПЗУ или ОЗУ
(при отладке)
.ebss
Глобальные и статические переменные
Данных
Page 1
ОЗУ
.stack
Область системного стека, локальные переменные
Данных
Page 1
(Младшие 64К)
ОЗУ
Неинициализированные секции
.esysmem Системная память для функций динамического рас- Данных
пределения памяти
Page 1
ОЗУ
С точки зрения языка С/С++, а именно способа доступа к данным, секции
могут отображаться либо на память программ (Страница 0 — Page 0), либо
на память данных (Страница 1 — Page 1). Так, секция исполняемого программного кода .text всегда отображается на память программ. Однако при
отладке программы, что и будем делать, ее целесообразно разместить в кодовом ОЗУ (с доступом, как к программной памяти) и только после завершения
отладки — в настоящем ПЗУ или флэш-памяти. Поэтому в качестве возможного места размещения секции .text в табл. 1.11 указано ПЗУ или ОЗУ.
Секция глобальных и статических переменных .ebss должна отображаться на память данных (Страница 1 — Page 1). Она предназначена для
резервирования места под переменные, которые могут быть как предварительно проинициализированными, так и нет. Фактическим местом размещения секции может быть только оперативная память. Каждая переменная описывается на С/С++ с помощью оператора объявления типа переменной,
чтобы зарезервировать память, необходимую для размещения в ней значения
переменной. Обычно переменным не присваивается какое-либо значение.
Изменить это значение может только работающая программа.
Для того чтобы проинициализировать глобальные или статические переменные начальными значениями, т.е. выполнить предустановку переменных, применяется следующая технология: во время запуска программы
34
(Startup time) специальная процедура загрузчика С-программы (C boot
routine) копирует данные из секции инициализации глобальных переменных
.cinit (которая обычно расположена в постоянной памяти) и сохраняет их в
секции .ebss. Далее управление передается программе пользователя.
Естественно, что при отладке, так же как и секция .text, секция .cinit с
таблицей констант инициализации переменных может загружаться в кодовое
ОЗУ.
Секция констант .econst содержит жестко установленные константы,
значения которых не будут меняться, в том числе строковые константы. С
точки зрения С/С++ это всего лишь переменные, поэтому секция .econst
должна отображаться на память данных (Страница 1, Page 1), как и секция
.ebss. Фактическим местом размещения секции будет ПЗУ или ОЗУ (при
отладке).
Префикс «е» перед именем секции .bss, а также перед именами других
секций (.const — econst; sysmem — esysmem) означает расширенную за пределы 64К слов область памяти, которая используется в микроконтроллерах
‘C28x.
Одной из важнейших, при программировании на С/С++, является секция
системного стека .stack, которая используется не только для сохранения
адресов возврата из подпрограмм и процедур обслуживания прерываний, но
и в качестве места размещения локальных переменных, определенных только
внутри конкретной функции. Кроме того, при обращении к любой функции
через стек передаются параметры (аргументы функции) и возвращаются
результаты. Секция стека всегда отображается на память данных и фактически располагается в ОЗУ.
Секция системной памяти .esysmem используется специальной функцией языка far malloc для динамического распределения памяти. Если эта
функция не используется, то секция .esysmem остается пустой — ее размер
будет равен 0.
Таким образом:
• секции .text, .cinit и .switch должны отображаться на память программ
(Страница 0, Page 0). Их фактическое расположение в готовом изделии —
ПЗУ или флэш-память, а при отладке — кодовое ОЗУ;
• секция .econst должна отображаться на память данных (Страница 1,
Page1) и размещаться в готовом изделии в ПЗУ или флэш-памяти, а при
отладке — в кодовом ОЗУ;
• секции .ebss, .stack, и .esysmem должны отображаться на память данных (Страница 1, Page 1) и размещаться исключительно в оперативной
памяти (ОЗУ).
Один из возможных вариантов окончательного размещения секций
С-программы в памяти микроконтроллера ‘C28x (после завершения отладки)
показан на рис. 1.11. Секции .cinit и .text размещаются во флэш-памяти, секция
переменных .ebss — в банке M0 встроенного ОЗУ, а секция стека .stack —
в банке M1 встроенного ОЗУ.
35
Объединенные
секции всех
объектных
файлов
Память
целевой платы
Секция
.ebss
0x00 0000
0x00 0400
Секция
.stack
Секция
.cinit
0x3D 8000
Банк M0
M0SARAM
0x400(1К слово)
Банк М1
M1SARAM
0x400 (1К слово)
Флэш-память
FLASH
0X20000 (128К слов)
Секция
.text
Рис. 1.11. Один из вариантов размещения секций СИ-программы в памяти ‘C28x
Замечания
1. Объекты, которые декларируются в языке СИ как удаленные (far), или
объекты, предназначенные для использования в расширенной памяти (large
memory), должны размещаться в секциях .ebss/.econst. Напротив, близкие
объекты (near objects) могут размещаться в секциях .bss/.const. За размещением объектов по секциям следит сам компоновщик проекта.
2. Окружение реального времени языка С/С++ поддерживает динамическое распределение памяти (system heap — «кучи» системы) в начальной
области памяти данных с помощью функции malloc и динамическое распределение памяти в любой области памяти данных с помощью функции
far_malloc. Для реализации этих задач служат секции .sysmem и .esysmem.
1.4. ТЕХНОЛОГИЯ РАСПРЕДЕЛЕНИЯ
РЕСУРСОВ ПАМЯТИ ЦЕЛЕВОЙ ПЛАТЫ
С ПОМОЩЬЮ ФАЙЛА УПРАВЛЕНИЯ КОМПОНОВКОЙ
Компоновщик выполняет две основные функции, имеющие отношение к
секциям. Первая функция использует входные секции в перемещаемом объектном коде как компонуемые блоки и объединяет входные секции одного и
того же типа (если в процессе компоновки участвуют больше чем один файл)
в одну выходную секцию в перемещаемом объектном коде с тем же именем.
36
С помощью второй функции компоновщик заменяет относительные адреса
абсолютными, выполняя размещение объединенных секций в памяти устройства с учетом двух факторов:
1) карты памяти конкретной микропроцессорной системы;
2) указаний программиста по распределению секций в памяти целевого
устройства.
Командный файл компоновщика (.cmd)
Для автоматического управления процессом компоновки в интегрированной среде CCS на этапе сборки проекта (build) или пересборки проекта
(rebuild) после модификации (редактирования) одного или нескольких файлов проекта используется командный файл управления компоновкой (.cmd),
который создается программистом.
В этом файле:
• с помощью директивы MEMORY специфицируются различные
области памяти, физически присутствующие на целевой плате (встроенное
ОЗУ однократного доступа SARAM, встроенная флэш-память, расширенная
память и т.п.);
• с помощью директивы SECTIONS даются указания по распределению
секций программы по физически имеющимся областям памяти, описанным
директивой MEMORY.
Описание карты памяти целевой платы
С помощью директивы MEMORY описывается конфигурация памяти
целевой микропроцессорной системы. Необходимо задать имена отдельным
областям памяти, указать стартовые адреса и длины этих областей памяти.
Формат описания любой области памяти следующий:
Name: origin = 0x????, length = 0x????
Здесь:
Name
Имя физически существующей в микропроцессорной системе области памяти. Желательно, чтобы имя точно отображало специфику области памяти, например FLASH —
флэш-память программ, M0SARAM — банк М0 ОЗУ однократного доступа
origin
Начальный адрес области памяти в шестнадцатеричной системе счисления
length
Длина области памяти в шестнадцатеричной системе счисления
Например, в микроконтроллерах ‘C28 встроенная флэш-память начинается с адреса 0x3D8000 и имеет объем 128К слов, что соответствует длине
0х20000 (131072 в десятичной системе счисления). Если присвоить этой
области памяти имя FLASH, то ее описание примет вид:
MEMORY
{
FLASH: origin = 0x3D8000, length = 0x20000
}
37
Любая целевая плата содержит несколько различных областей памяти
(банков памяти), каждая из которых должна иметь свое имя. В этом случае
внутри составного оператора, между скобками { }, должны располагаться
несколько операторов определения областей памяти. Так, в микроконтроллерах ‘C28 описание двух банков ОЗУ однократного доступа M0 и M1 объемом
1К слово каждый может выглядеть следующим образом:
MEMORY
{
M0SARAM: origin = 0x000000, length = 0x0400
M1SARAM: origin = 0x000400, length = 0x0400
}
Уже отмечалось, что каждая секция программы на Ассемблере или на
С/С++ должна быть отображена либо на память программ (Page 0), либо на
память данных (Page 1). Поэтому в директиве MEMORY сначала описываются все банки памяти, которые входят в программную память, а затем те,
которые входят в память данных. Напоминаем, что кодовое ОЗУ на этапе
отладки может быть отнесено к памяти программ. Таким образом, полное
описание карты памяти имеет формат:
MEMORY
{
PAGE 0:/* Память программ */
FLASH:origin = 0x3D8000, length = 0x20000
PAGE 1:/* Память данных */
M0SARAM: origin = 0x000000, length = 0x0400
M1SARAM: origin = 0x000400, length = 0x0400
}
Дополнительные рекомендации:
1) обязательно указывайте после имени страницы памяти и имени сегмента памяти двоеточие;
2) используйте комментарии, как это принято в языке С/С++: начало комментария /*, конец комментария */. Использование комментариев с оператором // недопустимо;
3) располагайте вложенные записи «лесенкой», что сделает описание
областей памяти целевой платы более наглядным.
Размещение секций
Директива SECTIONS определяет, в какие физические области памяти
целевой платы будут размещены те или иные секции программы. Например,
для размещения секций .text и .cinit во флэш-память, секции .ebss в банк M0
встроенного ОЗУ, а секции .stack — в банк M1 встроенного ОЗУ нужно дать
следующие указания компоновщику:
38
SECTIONS
{
.text:>
.ebss:>
.cinit:>
.stack:>
FLASH
M0SARAM
FLASH
M1SARAM
PAGE
PAGE
PAGE
PAGE
0
1
0
1
}
Окончательно командный файл управления компоновкой будет иметь вид
MEMORY
{
PAGE 0:/* Память программ */
FLASH: origin = 0x3D8000, length = 0x20000
PAGE 1:/* Память данных */
M0SARAM: origin = 0x000000, length = 0x0400
M1SARAM: origin = 0x000400, length = 0x0400
}
SECTIONS
{
.text:>FLASH
PAGE
0
.ebss:>M0SARAM PAGE 1
.cinit:>FLASH
PAGE
0
.stack:>M1SARAM PAGE 1
}
Компоновщик соберет вместе все секции .text исполняемого программного кода из всех файлов проекта и разместит выходной исполняемый код
во флэш-памяти на странице PAGE 0 (в памяти программ). Так же он поступает и с секциями инициализации переменных .cinit. Напротив, выходная
объединенная секция переменных .ebss будет размещена в банке M0 памяти
данных, а секция стека .stack — в банке M1 памяти данных.
Практическая работа
1. Изучите внимательно карту памяти микроконтроллера ‘F2812, представленную в табл. 1.8. Считая, что объема встроенной в микроконтроллер
памяти достаточно для решения ваших задач (расширенная память не
используется), составьте описание карты памяти, необходимое для создания
файла управления компоновкой. Предложите свои имена для названия
отдельных областей памяти, определите начальные адреса и длины областей
памяти. Заполните шаблон, приведенный ниже:
39
MEMORY
{
PAGE __:
_____:
_______:
_____:
_____:
_____:
}
/* Память программ */
origin = ________, length
/* Память данных */
origin = ________, length
origin = ________, length
origin = ________, length
= ________
= ________
= ________
= ________
2. Какую область памяти Вы отвели под память программ? Почему?
3. При выполнении практических работ рекомендуется использовать банк
H0 ОЗУ для загрузки выполняемого программного кода. Размер этой области
памяти 8К слов вполне достаточен для размещения и отладки самых сложных программ. При этом флэш-память не используется во избежание возможных проблем, связанных с недостаточной квалификацией обучающихся.
Как описать карту памяти для этого случая? Сделайте это.
4. А теперь — самое сложное. Попытайтесь распределить секции программы
между отдельными областями памяти микроконтроллера (см. табл. 1.11).
5. Создайте командный файл управления компоновкой (lnk.cmd) в любом
текстовом редакторе.
Контрольные вопросы
1. Каково назначение отдельных банков встроенной памяти микроконтроллера?
2. Чем отличается секционирование программы на Ассемблере от секционирования
на языке высокого уровня С/С++?
3. Зачем нужен общий формат объектных файлов, создаваемый Ассемблером и
С/С++?
4. Может ли секция, отображенная на страницу Page 0 (память программ), реально
размещаться в ОЗУ?
5. Может ли секция, отображенная на страницу Page 1 (память данных), реально размещаться во флэш-памяти?
40
Глава
2
СОЗДАНИЕ И ОТЛАДКА ПРОСТЫХ ПРОГРАММ
НА ЯЗЫКЕ ВЫСОКОГО УРОВНЯ С/С++
В СРЕДЕ CODE COMPOSER STUDIO
2.1. ПРОЕКТ. СОЗДАНИЕ НОВОГО ПРОЕКТА
TM
В лаборатории можно работать с драйвером оценочной платы eZdsp
F2812 и загружать программы в память микроконтроллера, отлаживая их
непосредственно в целевой плате. Можно работать и с драйвером симулятора. В последнем случае понадобится только компьютер с установленной
программой CCS. В верхней строчке CCS, приведенной на рис. 2.1, располагаются разделы меню от меню Файлов (Files) до меню Помощи (Help).
Слева и под основным меню находятся панели инструментов. Будем постепенно в процессе выполнения практических работ осваивать возможности
интегрированной среды CCS.
Рис. 2.1. Общий вид окна интегрированной среды CCS при запуске
41
Проект — это набор всех файлов, требуемых для создания исполняемого
выходного файла (.out), который может быть загружен и выполнен на целевой плате. В состав проекта могут входить:
исходные файлы на Ассемблере и на языке С/С++;
файлы с описанием макросов на Ассемблере и на языке С/С++, так называемые подключаемые файлы;
файлы в перемещаемом объектном коде, входящие в стандартные библиотеки или библиотеки пользователя;
командные файлы управления компоновкой.
Важнейшей особенностью проекта является то, что отсутствует необходимость в копировании файлов из разных мест в один каталог. Проект содержит лишь ссылки на фактическое расположение файлов. Это позволяет
любому числу пользователей из любого числа проектов ссылаться на одни и
те же библиотечные файлы, файлы управления компоновкой, не засоряя компьютер множеством копий одних и тех же модулей.
Работа в среде CCS начинается либо с создания нового проекта путем
выбора пункта меню Project → New, либо с открытия уже существующего
проекта выбором пункта меню Project → Open (рис. 2.2). Работая над проектом, можно постепенно добавлять файлы в проект (подключать файлы к
проекту) с помощью команды Project → Add files to Project. Пользуясь тем же
меню, в конце работы можно сохранить проект Project → Save и закрыть его
Project → Close. Как обычно, при закрытии проекта можно сохранить наработки или отказаться от сохранения, если накопленная информация не нужна.
Создание нового проекта по команде Project → New выполняется с помощью диалогового окна, изображенного на рис. 2.3, которое позволяет ввести
имя проекта в поле Project Name и задать директорию размещения проекта
в поле Location. Как и во многих других приложениях под Windows, кнопка
«…» позволят вызвать на экран окно «Обзора папок», в котором можно
выбрать нужную директорию из имеющихся или создать новый каталог.
Если указанные в диалоговом окне каталоги и подкаталоги не существуют,
то они будут автоматически созданы. Это одно из удобств, которое предоставляет интегрированная среда.
При вводе имени проекта одновременно в соответствии с вводимым именем меняется название подкаталога последнего уровня, в котором будет размещен проект. Таким образом, имя проекта будет строго соответствовать
названию каталога для размещения файлов проекта.
Рис. 2.2. Команда, с которой начинается работа над проектом
42
В диалоговом окне создания нового проекта есть две дополнительные
опции: тип проекта Project Type и целевой микроконтроллер Target. Первая
опция имеет два возможных значения:
• Executable (.out) — создание выходного исполняемого файла в абсолютном объектном коде. Применяется для загрузки и отладки выходного
файла проекта в целевом устройстве;
• Library (.lib) — создание выходного библиотечного файла в перемещаемом объектном коде. Применяется для объединения в библиотеку пользователя нескольких уже отлаженных файлов типовых функций. При выборе
этого значения CCS, по существу, работает в качестве программы «библиотекаря», интегрируя его возможности.
Вторая опция может иметь три значения, в зависимости от типа целевого
микроконтроллера из семейства ‘C2000, для которого выполняется разработка программного обеспечения: TMS320C24XX, TMS320C27XX,
TMS320C28XX. Воспользуемся последним значением.
После закрытия диалогового окна создадим файл с расширением .pjt,
который будет содержать все параметры компилятора с языка высокого
уровня С/С++, Ассемблера и компоновщика для процесса сборки проекта.
Вначале эти параметры устанавливаются по умолчанию, а затем в процессе
работы над проектом могут редактироваться. Как уже упоминалось, названия каталога проекта и собственно файла проекта будут одинаковыми. В
примере, представленном на рис. 2.3, каталог LAB2 будет содержать файл
проекта LAB2.pjt.
При создании нового проекта внутри каталога проекта будет создана
директория Debug (Отладка), в которую в последующем будут записываться
непосредственно исполняемые файлы, полученные в процессе трансляции и
компоновки. Как только новый проект будет создан, появляются как бы виртуальные папки, которые могут заполняться в процессе работы над проектом, в том числе папка с исходными файлами (Source), с библиотечными
файлами (Libraries), с подключаемыми файлами (Include) и т.п., приведенные на рис. 2.4.
Рис. 2.3. Диалоговое окно создания нового проекта
Рис. 2.4. Начальное содержимое
каталога проекта
43
Виртуальной папка называется потому, что она может не содержать файлов, а лишь ссылки на их фактическое место расположения. Вначале эти
папки пусты, о чем свидетельствует отсутствие слева от имени папки значка
«+». Если этот значок есть, то папка имеет какое-то содержимое. Щелкнув на
значке «+» кнопкой мыши, можете раскрыть содержимое виртуальной
папки.
Подключить файл к проекту не означает физически скопировать его в
один из каталогов проекта. Как увидите, это касается не только библиотечных файлов, но и исходных файлов. Конечно, можно создать исходный файл
непосредственно в каталоге Source. Однако, чтобы среда CCS «увидела»
этот файл, его все равно придется подключить к проекту с помощью
команды Project → Add files to Project.
Практическая работа
1. Создайте новый проект для выполнения практической работы № 2.
Выберите в качестве названия проекта имя LAB2 и разместите проект в
каталоге C:\C28X\LABS\LAB2.
2. Проанализируйте содержимое созданного каталога LAB2 и подкаталогов, например, с помощью стандартной программы Windows «Проводник».
3. Сохраните проект. Закройте CCS.
4. Вновь запустите среду CCS. Откройте уже созданный проект.
2.2. СОЗДАНИЕ И РЕДАКТИРОВАНИЕ ФАЙЛА
ИСХОДНОЙ ПРОГРАММЫ НА ЯЗЫКЕ С/С++
Исходные файлы могут создаваться в любом удобном для пользователя
текстовом редакторе, способном генерировать выходные файлы в формате
ASCII без дополнительных символов форматирования. Это требование является обязательным, так как исходные файлы будут обрабатываться транслятором с языка Ассемблер или компилятором С/С++. Однако для работы с
исходными файлами лучше всего подходит встроенный в интегрированную
среду CCS редактор, который можно вызвать командой меню File → New →
→ Source File (Файл → Новый → Исходный файл), представленной на рис. 2.5.
Для быстрого вызова встроенного редактора можно воспользоваться
также горячей клавишей <Ctrl+N>. Приемы работы во встроенном редакторе
не отличаются от общеизвестных. Дополнительно встроенный редактор производит автоматическое цветовое выделение фрагментов текста в зависи-
Рис. 2.5. Вызов встроенного редактора для создания файла с исходной программой
44
Рис. 2.6. Первая программа на С/С++
мости от их функционального назначения, что очень удобно в процессе
редактирования и отладки программ. Работая в среде CCS, постепенно оцените все преимущества встроенного редактора, в том числе возможности
автогенерации имен периферийных регистров и битовых полей по первым
набранным буквам — возможности автопродолжения или автонабора.
После завершения работы по созданию или редактированию исходного
файла его необходимо сохранить на диске. Можно воспользоваться стандартными командами меню «Файл»: File → Save (Сохранить) или File → Save
As… (Сохранить как…), а также панелью инструментов CCS (пиктограммой дискеты). В общем случае исходные файлы могут размещаться в любом
каталоге, однако более удобно, если они сохраняются в каталоге конкретного
проекта, для которого и создаются.
Практическая работа
1. Вызовите встроенный текстовый редактор.
2. В появившемся окне Untitled1, пользуясь стандартными приемами
редактирования, введите текст исходной программы на языке С/С++ (см. гл. 1).
3. Сохраните работу в каталоге LAB2. Убедитесь в том, что программа
выглядит так, как показано на рис. 2.6.
4. Дайте объяснения цветовому выделению фрагментов текста, которые
выполняет встроенный текстовый редактор.
2.3. СОЗДАНИЕ И РЕДАКТИРОВАНИЕ
ФАЙЛА УПРАВЛЕНИЯ КОМПОНОВКОЙ
В гл. 1 подробно рассмотрели все аспекты создания файла управления
компоновкой (lnk.cmd) для конкретного целевого устройства. В учебных
целях целесообразно размещать программный код исключительно во встроенной оперативной памяти микроконтроллера, без использования флэшпамяти. Для этого в наибольшей степени подходит банк H0 встроенного ОЗУ
45
как имеющий достаточно большую емкость (8К слов). Таким образом, секции .text и .cinit должны отображаться на страницу 0 памяти программ
(Page 0) и размещаться в кодовом ОЗУ H0SARAM.
Секции .ebss и .stack должны обязательно отображаться на страницу 1
памяти данных (Page 1). При этом секция .ebss для относительно небольших
программ с малым числом переменных может размещаться в банке M1
SARAM, а секция .stack — в банке M0, или наоборот. Последний вариант
может оказаться предпочтительней, так как указатель стека после сброса
процессора автоматически устанавливается на начало банка M1.
Для создания и редактирования файла управления компоновкой можно
использовать встроенный в среду CCS редактор. По умолчанию имя этого
файла может быть lnk, а расширение — .cmd. Можно также связать имя
командного файла с именем проекта, например назвать его lab2.cmd.
Практическая работа
1. Создайте командный файл компоновщика lab2.cmd, пользуясь возможностями встроенного текстового редактора. Один из возможных вариантов
файла показан на рис. 2.7.
2. Сохраните файл в каталоге проекта LAB2.
Рис. 2.7. Файл управления компоновкой для отладки программ в кодовом ОЗУ
46
Замечание
В общем случае среди секций, отображаемых на память программ, может
быть и секция .reset. Она является частью библиотеки реального времени
выполнения rts2800_ml.lib. Если после описания размещения этой секции
напечатать TYPE=DSECT, то компоновщик будет игнорировать ее —
не будет размещать.
2.4. ПОДКЛЮЧЕНИЕ ФАЙЛОВ К ПРОЕКТУ
Подключение файлов к проекту производится с помощью команды меню
Project → Add Files to Project… (добавить файл к проекту). Пользуясь
встроенным редактором CCS, можно создать любое число файлов на языке
С/С++ или Ассемблере, а также большое число файлов управления компоновкой под различные конфигурации вашего оборудования. Однако создать
файл и подключить его к проекту — это разные вещи. Только после подключения файла к проекту его можно обрабатывать в среде CCS.
Подключение файла к проекту производится с помощью диалогового
окна команды, изображенного на рис. 2.8, которое содержит стандартные
средства перемещения по каталогам. Если знаете, где находится файл и
какое имя он имеет, то можете сразу ввести эти данные. Выделив нужный
файл кнопкой мыши, по команде Открыть его можно добавить в проект.
Для облегчения поиска подключаемых файлов служит опция Тип файлов,
где можно выбрать один из следующих вариантов, приведенных на рис. 2.9.
Как видно из выпадающего меню, к проекту можно подключать:
исходные файлы на языке С (*.c) или на объектно-ориентированном
языке С++ (*.cpp);
исходные файлы на языке Ассемблер (*.a);
Рис. 2.8. Диалоговое окно команды подключения файлов к проекту
47
библиотечные файлы в перемещаемом объектном коде (*.l) или отдельные, уже оттранслированные ранее, объектные файлы (*.o);
командные файлы управления компоновкой (*.cmd), а также ряд дополнительных файлов, в частности файлов данных, используемых при отладке
(*.dat).
При работе с программой на языке С потребуется подключение библиотеки функций реального времени выполнения, обычно находящейся в каталоге C:\ti\c2000\cgtools\lib\rts2800_ml.lib. С ее помощью выполняются
предварительная инициализация процессора, загрузка начальных значений
переменных (предустановка), а также ряд других функций.
Как только какой-то файл подключается к проекту, слева от списка виртуальных каталогов проекта, напротив соответствующей папки, появится знак
«+». Так, при добавлении исходного файла знак «+» появится слева от папки
Source, а при добавлении библиотечного файла — слева от папки Libraries.
Щелкнув на виртуальном каталоге мышкой, увидите подключенные к
проекту файлы. Если ошиблись и подключили не тот файл, установите курсор на имя этого файла и вызовите правой кнопкой мыши контекстно-зависимое меню. В этом меню есть команда Remote from Project (удалить из
проекта). Выбрав ее, отключите файл.
Если после операции подключения файлов к проекту щелкнуть мышкой
на значке «+» слева от Project в окне проекта, то появится дерево проекта,
содержащее все подключенные к проекту файлы. На рис. 2.10 показано
дерево проекта после подключения к проекту исходного файла с программой
на языке С lab2.c, командного файла управления компоновкой LAB2.cmd и
библиотеки реального времени выполнения rts2800_ml.lib.
Дерево проекта очень удобно для быстрого редактирования исходных
файлов. Можно всего лишь дважды щелкнуть мышкой на имени этого файла
и сразу попасть в режим редактирования или, щелкнув один раз, вызвать
контекстно-зависимое меню, а затем из него — текстовый редактор.
Рис. 2.9. Опции выбора типа подключаемого файла
48
Рис. 2.10. Дерево проекта после подключения файлов
Практическая работа
1. Добавьте к проекту LAB2 созданный исходный файл с программой на
языке С lab2.c. Убедитесь, что файл появился в дереве проекта. Отключите
его от проекта с помощью контекстно-зависимого меню и затем подключите
вновь.
2. Добавьте в проект файл управления компоновкой LAB2.cmd. Имя
этого файла появилось в дереве каталогов и файлов проекта. Теперь имеете
возможность открывать и редактировать с помощью встроенного текстового
редактора уже два файла lab2.c и LAB2.cmd. Проверьте эти возможности.
3. Добавьте к проекту библиотеку функций реального времени выполнения, находящуюся в каталоге “C:\ti\c2000\cgtools\lib\rts2800_ml.lib”.
2.5. УСТАНОВКА ОПЦИЙ КОМПИЛЯТОРА И КОМПОНОВЩИКА
Опции задают режим работы компилятора, Ассемблера и компоновщика.
Они управляют процессами компиляции, трансляции и компоновки и устанавливаются исходя из имеющихся ресурсов памяти и производительности
центрального процессора в целевой микропроцессорной системе. Когда
создается новый проект, Code Composer Studio формирует два возможных
набора опций, называемых конфигурациями: одна конфигурация Debug —
Отладочная, а вторая Release — Конечная (ее можно рассматривать как
Оптимизированную). Конфигурации можно переключать. Выбор конкретной конфигурации производится селектором выбора текущей конфигурации, находящимся в верхней левой части экрана интегрированной среды
CCS, изображенным на рис. 2.11.
Можно воспользоваться также командой меню Project → Configurations..
(Проект → Конфигурации..) и переключить текущую конфигурацию опций
в открывшемся окне, представленном на рис. 2.12.
Рис. 2.11. Селектор выбора текущей конфигурации опций
Рис. 2.12. Окно выбора текущей конфигурации
49
Чтобы облегчить процесс настройки опций сборки проекта, CCS предлагает графический пользовательский интерфейс настройки опций. На
начальном этапе работы нас вполне удовлетворят установки по умолчанию.
Сейчас исследуем некоторые из них. Если выбрать в меню Project → Build
Options… (Проект → Опции сборки…), то на экране появится окно, приведенное на рис. 2.13, с набором опций компилятора (Compiler) по умолчанию
для категории Basic (Основные).
Как видно, для отладочной конфигурации Debug предполагается:
работа центрального процессора в режиме истинного ‘C28xx с использованием всех преимуществ системы команд этого процессора (процессор
может работать и в других режимах, в частности совместимости с ‘C24xx и
‘C27xx);
полная символьная отладка;
отсутствие каких-либо методов оптимизации программного кода.
В верхней части окна показано, как выглядит установленный список
опций в командной строке вызова компилятора. К счастью, этот набор вводится автоматически самой интегрированной средой CCS и ручного
набора не требуется.
Переключимся на закладку опций компоновщика (Linker), изображенных
на рис. 2.14. Видно, что по умолчанию Code Composer Studio будет создавать
два типа выходных файлов .out и .map. Файл с расширением .out содержит
исполняемый код, загружаемый в DSP-микроконтроллер, а файл .map —
отчет компоновщика по распределению и использованию памяти в целевой
плате. Оба выходных файла будут размещаться в подкаталоге Debug текущего проекта. Опция модели автоинициализации (Autoinit Model) установлена в состояние Run-time Autoinitialization (автоинициализация в реальном масштабе времени).
Одной из важнейших опций является опция размера стека (Stack Size).
Значение 0x200 в шестнадцатеричной системе счисления соответствует
Рис. 2.13. Опции компилятора по умолчанию в режиме отладки
50
Рис. 2.14. Опции компоновщика по умолчанию
емкости стека в 512 слов. Этого достаточно для большинства простых проектов. Можно, например, увеличить размер стека вдвое, а затем закрыть окно
опций сборки проекта, нажав клавишу ОК. Сделанные изменения будут
сохранены.
Практическая работа
1. Исследуйте различные категории опций компилятора, но пока не
меняйте их.
2. В опциях компоновщика увеличьте вдвое размер стека до 1 К слова.
Сохраните настройки.
2.6. СБОРКА И ЗАГРУЗКА ПРОЕКТА
Четыре кнопки в центральной части горизонтальной панели инструментов,
приведенные на рис. 2.15, отвечают за процесс компиляции и сборки проекта.
Их название и назначение приведены в табл. 2.1.
Компиляция или трансляция отдельного исходного файла (клавиша 1)
выполняется тогда, когда этот файл только создан и необходимо убедиться,
что в файле отсутствуют синтаксические ошибки, например, неверно записанные команды на языке Ассемблер, неправильные директивы, неправильные операторы языка С/С++.
Рис. 2.15. Кнопки управления процессом компиляции и сборки проекта
51
Табл и ц а 2.1
Назначение клавиш управления компиляцией и сборкой проекта
Кнопка
Название
Описание
1
Compile File
Компилировать или ассемблировать текущий открытый файл
(исходный)
2
Incremental Build Компилировать или ассемблировать только файлы, изменившиеся
с момента предыдущей работы над проектом, а затем компоновать
3
Rebuild All
Перекомпилировать или переассемблировать все файлы проекта,
а затем скомпоновать
4
Stop Build
Остановить генерацию кода (любой из вышеперечисленных
процессов, если он запущен)
В процессе компиляции могут выдаваться не только ошибки, но и предупреждения. Предупреждения — это не фатальные ошибки. Например, при
компиляции файла с программой lab2.c в нижнем окне Build (Сборка) будет
отображен процесс компиляции:
указана командная строка вызова компилятора cl2000 со всеми текущими
опциями;
выведено предупреждение о том, что переменная z была установлена, но
далее нигде не используется;
приведен общий итог компиляции — 0 ошибок, 1 предупреждение,
0 замечаний (рис. 2.16).
Все сообщения об ошибках и предупреждениях выводятся красным цветом.
Как видите, ошибок в программе нет, но есть предупреждение о том, что
вычисленная переменная не используется. Это соответствует истине, так как
в нашей простой программе производится лишь вычисление локальной
переменной z внутри главной функции main. Если получите сообщение об
ошибке, «прокрутите окно сборки проекта», пока не увидите, выделенное
красным цветом сообщение. Двойным щелчком мыши по сообщению можно
сразу перейти к строке исходного текста, в которой содержится ошибка.
При этом курсор будет автоматически установлен на соответствующую
строку — можно делать исправления.
Если этап трансляции и объединения нескольких файлов проекта уже
пройден и необходимо добавить к проекту еще один исходный файл, то
выполняется команда последовательной сборки проекта (клавиша 2). Наконец, можно не думать о том, в каком состоянии находятся все исходные
файлы проекта и просто перекомпилировать весь проект (клавиша 3). Это
Рис. 2.16. Окно сборки проекта Build
52
Рис. 2.17. Установка опции автоматической загрузки программы после сборки
самый надежный способ учесть все изменения, которые смогли внести по
мере работы над проектом в любых исходных файлах.
Последняя клавиша с символом x прерывает начатый процесс сборки.
Следующий этап работы над проектом состоит в загрузке выходного кода
программы (файла с расширением .out) в память целевой платы. Для этой
цели в меню File предусмотрена команда Load Program… (Загрузка программы…), приведенная на рис. 2.17.
Code Composer Studio может осуществлять и автоматическую загрузку
программы после сборки в память микроконтроллера. Найдите в меню пункт
Option → Customize… (Опции → Настройки), а затем выберете закладку
Program Load Options (Опции загрузки программы) и установите
«галочку» напротив позиции Load Program After Build (Загрузка программы после сборки), нажмите OK (см. рис. 2.17). В этом случае сразу
после сборки проекта и создания выходного исполняемого файла он будет
автоматически загружаться в целевую плату.
Практическая работа
1. Выполните компиляцию исходного файла lab2.c. Убедитесь в том, что
ошибок нет.
53
Рис. 2.18. Сообщение об ошибке компиляции
2. Сознательно внесите ошибку в программу на языке С. Например, опустите символ «;» в конце оператора присваивания. Повторяя процесс компиляции, получите сообщение о том, что символ ‘;’ в конце оператора отсутствует (expected a «;») (рис. 2.18).
3. Исправьте ошибку. Перекомпилируйте программу.
4. Нажмите кнопку Build All (Собрать все) и посмотрите, как компилятор и компоновщик выполняются в окне сборки. Проверьте наличие ошибок.
Поэкспериментируйте. Например, в командном файле в описании кодового
ОЗУ замените цифру 0 на букву О (часто встречающаяся опечатка):
HOSARAM. Выполните сборку. Проанализируйте полученные сообщения об
ошибках.
5. Исправьте ошибку. На будущее учтите, что одна ошибка может вызывать сразу несколько сообщений об ошибках во время сборки, что и произошло в данном случае.
6. Выполните новую сборку проекта (теперь ошибки должны отсутствовать).
Если установлена опция автоматической загрузки выходного исполняемого файла в память микроконтроллера после завершения сборки проекта,
то процесс загрузки будет сопровождаться автоматическим открытием окна
дизассемблера, приведенного на рис. 2.19, в котором отображаются машинный код программы пользователя и соответствующие ему команды на языке
Ассемблер. Начальный адрес размещения программы пользователя соответствует имени основной программы main и началу кодовой секции .text.
При этом счетчик команд автоматически установится на метку _c_int00
начала процедуры инициализации. Текущее положение счетчика команд в
окне дизассемблера отображается зеленой стрелкой слева от адреса соответствующей ячейки памяти.
Для того чтобы информация в окне дизассемблера отображалась правильно, необходимо в меню GEL → Addressing Mode → C28x_Mode
выбрать режим адресации истинного ‘C28x — микроконтроллера, как показано на рис. 2.20. В противном случае, например когда случайно установлен
режим адресации ‘С27х, коды ‘C28x будут интерпретироваться неверно. При
этом некоторые инструкции могут вообще не распознаваться и заменяться
выделенной красным цветом директивой .word с указанием неизвестного
кода операции.
Дадим короткий предварительный комментарий о назначении процедуры
инициализации. Она предназначена:
для инициализации собственно микроконтроллера, т.е. установки режима
его работы;
54
Рис. 2.19. Окно дизассемблера сразу после загрузки исполняемого файла в память
Рис. 2.20. Задание режима адресации для правильного отображения команд
для инициализации переменных, которые должны иметь определенные
начальные значения — для предустановки значений переменных. Например,
первая ассемблерная команда в процедуре инициализации MOV @SP,
#0x0000 выполняет загрузку указателя стека процессора SP нулевым значением. Это правильно, так как задали расположение сегмента стека .stack в
банке M0 ОЗУ, начиная с нулевого адреса. Вторая команда SPM 0 задает
начальный режим сдвига произведения в аппаратном умножителе микроконтроллера (нет никакого сдвига). Третья команда SETC OBJMODE устанавливает объектный режим работы микроконтроллера (истинный ‘C28xx), а
четвертая команда CLRC AMODE — режим адресации (истинного ‘C28xx).
Далее следует еще ряд команд инициализации микроконтроллера, в частности команда установки начального значения указателя текущей страницы памяти данных MOVW DP, #0x0000.
Процедура инициализации генерируется автоматически с помощью библиотеки реального времени выполнения, и пользовать может вообще не
иметь представления о том, что там конкретно делается. Главное, чтобы процессор и память были правильно подготовлены к решению задачи пользователя. Если пишете программу исключительно на Ассемблере, то можете
сами проинициализировать процессор в своей исходной программе и подключение библиотеки реального времени выполнения не понадобится.
55
Контрольные вопросы
1. К какой области памяти микроконтроллера относится начальный адрес 0x3F 8000?
Правильно ли размещен программный код нашей программы main?
2. Правильно ли выполнена инициализация указателя стека?
2.7. АНАЛИЗ ФАЙЛА С КАРТОЙ ЗАГРУЗКИ
Файл карты загрузки .map содержит информацию о фактическом месте
размещения различных секций программы в памяти микропроцессорной
системы, а также информацию о символических именах, используемых в
программе, и их значениях.
В начале карты загрузки указывается имя выходного объектного файла с
исполняемой программой .out, включая каталог его размещения. Приводятся
символическое имя точки входа в программу и ее адрес:
OUTPUT FILE NAME:
<./Debug/LAB2.out>
ENTRY POINT SYMBOL: "_c_int00" address: 003f800a
Видно, что точкой входа в программу является адрес размещения программы инициализации “_c_int00”.
Далее приводится информация о текущей конфигурации памяти целевого устройства: список блоков памяти, входящих в память программ (PAGE
0) и память данных (PAGE 1) с указанием начальных адресов блоков, длин
блоков и их фактической загрузки (колонка used — «используется»):
MEMORY CONFIGURATION
name
origin
length
used
attr
fill
---------------------- -------- -------- -------- ---- -------PAGE 0: H0SARAM
003f8000 00002000 000000c7 RWIX
PAGE 1: M0SARAM
00000000 00000400 00000200 RWIX
M1SARAM
00000400 00000400 00000086 RWIX
Выдается карта фактического размещения секций программы в памяти
устройства с указанием страницы памяти программ (Page 0) или памяти данных (Page 1), начального адреса и длины соответствующей секции:
56
SECTION ALLOCATION MAP
output
section
attributes/
page origin
length
input sections
-------- ---- ---------- ---------- ---------------.text
.cinit
0
0
003f8000
000000a4
003f8000
0000000a
lab2.obj (.text)
003f800a
00000046
rts2800_ml.lib : boot.obj (.text)
003f8050
0000004b
: exit.obj (.text)
003f809b
00000009
: _lock.obj (.text)
003f80a4
00000021
003f80a4
00000008 lab2.obj (.cinit)
003f80ac
0000000e rts2800_ml.lib
: exit.obj (.cinit)
003f80ba
0000000a
: _lock.obj (.cinit)
003f80c4
00000001
003f80c6
00000002
--HOLE-- [fill = 0]
.reset
0
003f80c6
00000002
.data
1
00000000
00000000
UNINITIALIZED
.bss
1
00000000
00000000
UNINITIALIZED
rts2800_ml.lib : boot.obj (.reset)
.stack
1
00000000
00000200
UNINITIALIZED
.ebss
1
00000400
00000086
UNINITIALIZED
00000400
00000080
rts2800_ml.lib : exit.obj (.ebss)
00000480
00000004
00000484
00000002
: _lock.obj (.ebss)
lab2.obj (.ebss)
Обратите внимание на колонку под названием атрибуты (attributes), где
указано, из каких исходных секций собрана результирующая секция. Видно,
что в кодовую секцию .text входят четыре отдельные секции, из которых
только одна (первая) относится к исходной программе lab2.obj (.text), а
остальные три — являются «обслуживающими» и автоматически формируются компилятором с использованием библиотеки реального времени
выполнения rts2800_ml.lib, например секция начальной загрузки boot.obj
(.text). Удобно, что для каждой секции указываются ее начальный адрес размещения в памяти и длина. Таким образом, получаете информацию о месте
размещения «обслуживающих» программ и можете при желании изучить их
код.
Из общей длины кодовой секции.text 0a4h (164 слова) только 10 слов
(0ah) занимает код нашей программы, остальное — код «обслуживающих»
программ. Аналогичная ситуация и в секции инициализации констант .cinit:
из 33 слов (21h), отведенных в памяти программ для этой секции, только
восемь слов относятся к секции инициализации собственно программы
пользователя lab2.obj (.cinit), а остальные — к секциям инициализации констант для «обслуживающих» программ.
57
Видно, что секция данных .ebss содержит только два слова, зарезервированных собственно программой пользователя — секция lab2.obj (.ebss),
остальные 84h (132) слова относятся к секциям данных «обслуживающих»
программ.
В конце файла карты загрузки .map приводится список всех глобальных
имен, отсортированный по именам:
GLOBAL SYMBOLS: SORTED ALPHABETICALLY BY Name
address name
-------- ---00000000 .bss
00000000 .data
003f8000 .text
…
00000200 __STACK_SIZE
…
003f8000 _main
00000485 _x
00000484 _y
…
а также список глобальных имен, отсортированных по адресам:
GLOBAL SYMBOLS: SORTED BY Symbol Address
address name
-------- ---00000000 ___data__
…
00000200 __STACK_SIZE
…
00000484 _y
00000485 _x
003f8000 .text
003f8000 _main
…
Переменные нашей программы (x, y) размещаются в ОЗУ по адресам 485h
и 484h соответственно.
Практическая работа
1. Средствами операционной системы, например, с помощью «Проводника» исследуйте содержимое каталога LAB2. Есть ли там автоматически
созданный подкаталог Debug (Отладка)? Содержатся ли в нем следующие
файлы: LAB2.obj — выходной объектный файл в перемещаемом объектном
58
коде, созданный компилятором; LAB2.out — исполняемый выходной файл в
неперемещаемом объектном коде, созданный компоновщиком; LAB2.map —
файл с картой загрузки?
2. Проанализируйте содержимое файла с картой загрузки. Правильно ли
распределена память микроконтроллера? Соответствует ли это распределение содержимому командного файла lab2.cmd?
3. Распределены ли секции программы в соответствии с указаниями в
командном файле lab2.cmd?
2.8. ИСПОЛЬЗОВАНИЕ ОКОН ОТЛАДОЧНОЙ СРЕДЫ CCS
Для того чтобы сразу после загрузки перейти к основной программе на
языке С/С++ main(), в меню Debug нужно выбрать команду Go Main (Переход на главную программу). При этом автоматически будет выполнена процедура начальной инициализации, созданная в процессе сборки проекта с
помощью библиотеки реального времени выполнения rts2800_ml.lib (рис. 2.21).
Обычно во время отладки программы требуется следить за состоянием глобальных и локальных переменных. Для этого в среде Code Composer Studio
существует несколько способов. Рассмотрим лишь два наиболее простых:
окно памяти (memory window) и окна наблюдения (watch window) —
окна наблюдаемых переменных.
Чтобы открыть окно содержимого памяти (memory window), которое
часто называют окном «дампа» памяти, нужно воспользоваться командой
меню View → Memory (Просмотр → Память). Появится диалоговое окно
этой команды, приведенное на рис. 2.22, которое позволит в явной форме
задать начальный адрес интересующей пользователя области памяти или
задать область памяти неявно, как содержащую интересующую нас переменную. В последнем случае для задания адреса нужно воспользоваться определенным в программе символическим именем переменной. Например, для
Рис. 2.21. Окно исходной программы на языке С/С++
59
отображения в окне «дампа» памяти содержимого локальной переменной z
из программы lab2.c необходимо в поле адреса (Address:) ввести &z, что на
языке С/С++ означает адрес переменной z (а не ее значение). Помните, что
Code Composer Studio отличает символы, написанные в верхнем и нижнем
регистрах. Если записать в поле адреса &z, то получим сообщение об
ошибке, так как переменная z не определена (имя z в процессорах ‘C28 носит
специальный битовый флаг нулевого результата текущей операции).
В поле Format (Формат) можно задать желаемый стиль отображения
данных, например: Hex — C Style (шестнадцатеричные данные в формате
языка С/С++) или Hex — TI Style (шестнадцатеричные данные в формате
фирмы Texas Instruments, стандартном для микропроцессорной техники).
Если необходимо интерпретировать данные в памяти как 16-разрядные
целые числа со знаком в дополнительном коде, то выберите формат 16-Bit
Signed Int. Имеется возможность отображать содержимое памяти и в стандартном формате числа с плавающей точкой (Use IEEE Float). При этом
подразумевается, что под каждое число такого типа выделены две последовательные ячейки памяти (длинное слово). На рис. 2.23 показаны различные
форматы отображения содержимого области памяти с интересующей нас
локальной переменной z.
В диалоговом окне опций «дампа» памяти в поле Page (Страница) указывается нужная страница памяти (память программ или память данных). Так
как переменная z расположена в ОЗУ, то указывается страница памяти данных (Data). Нажав кнопку OK, увидите, что окно «дампа» памяти открылось
и начальный адрес указывает на переменную z.
Рис. 2.22. Диалоговое окно опций «дампа»
памяти
60
Рис. 2.23. Варианты отображения содержимого памяти
В нашем примере переменная z является локальной и расположена в стеке, а
ее адрес совпадает с дном стека. Напомним, что сегмент стека разместили в банке
M0 ОЗУ, начиная с адреса 0x0000. Стек в
процессорах ‘C28xx увеличивается в
направлении от младших адресов к старшим.
В процессе отладки программы можно
быстро изменять содержимое памяти по Рис. 2.24. Диалоговое окно редактировалюбому адресу, используя двойной щел- ния памяти
чок мыши на содержимом нужной ячейки
памяти. При этом открывается диалоговое окно редактирования памяти,
изображенное на рис. 2.24, которое позволяет модернизировать значение не
только в текущей ячейке, но и в других последовательно расположенных за
ней (вверх или вниз) ячейках памяти.
Отредактированное значение автоматически выделяется в окне «дампа»
памяти красным цветом как напоминание программисту о том, что данные
были изменены.
Для удобства интерпретации данных можно менять текущий формат
отображения данных, например от шестнадцатеричной формы представления переходить к десятичной. Для этого необходимо, находясь в окне
«дампа» памяти (когда окно активно), щелкнуть правой кнопкой мыши для
вызова контекстно-зависимого меню и выбрать команду Properties (Свойства). Появится уже знакомое окно Memory Window Options (Опций окна
памяти), в котором из выпадающего меню следует выбрать желаемый формат представления данных. Таким образом, в процессе отладки нет необходимости пользоваться дополнительными средствами, например калькулятором, — все операции по переводу чисел из одной системы счисления в
другую будет выполнять интегрированная среда CCS.
Практическая работа
1. Выполните переход на главную программу main(). Убедитесь в том, что
окно исходной программы на С/С++ открылось и программа готова к
отладке.
2. Откройте окно «дампа» памяти данных для наблюдения за значением
переменной z. Поэкспериментируйте с различными форматами представления данных, пользуясь контекстно-зависимым меню, прокомментируйте
полученные результаты.
3. Попробуйте модифицировать значения в отдельных ячейках памяти
данных и в определенной непрерывной области.
4. Закройте окно «дампа» памяти и откройте его вновь, указав в качестве
начального адреса области памяти текущее содержимое указателя стека SP.
Объясните, почему в этом случае символ & перед SP не ставится? Какая
область памяти отображается в окне? Почему?
61
Следующим типом окна, которое широко используется при отладке программ на языке С/С++, является окно наблюдаемых переменных (watch
window), изображенное на рис. 2.25. Для того чтобы отобразить в таком окне
значение локальной переменной z, нужно выбрать пункт меню View Watch
Window (Просмотр → Окно наблюдения) и щелкнуть мышкой на закладке
Watch Locals (Наблюдаемые локальные переменные).
Окно Watch Window будет по умолчанию содержать локальные переменные для текущей выполняемой функции. Напомним, что все локальные переменные располагаются в стеке. В языке С/С++ при вызове любой функции
автоматически выделяется место в стеке под локальные переменные этой
функции. После передачи результатов в вызывающую функцию место в
стеке автоматически высвобождается.
Текущее значение локальной переменной можно изменять и в окне
наблюдаемых переменных, что значительно удобнее, чем в окне «дампа»
памяти. Для этого нужно щелкнуть кнопкой мыши в столбце Value (Значение) в строке с именем переменной z для активизации режима редактирования, ввести новое значение переменной и завершить ввод нажатием клавиши
Enter. Можно также изменить формат отображения данных. Для этого
достаточно щелкнуть мышью в позиции Radix (Основание системы счисления) и выбрать требуемую форму представления, приведенную на
рис. 2.26: шестнадцатеричное (hex), десятичное (dec), двоичное (bin) или
восьмеричное число (oct), символ (char), число в формате с плавающей точкой (float), в научном формате высокой точности (scientific), число без знака
(undigned).
Вторая закладка Watch1 в окне наблюдаемых переменных Watch Window
предназначена для добавления глобальных переменных (см. рис. 2.25). Если
выбрать эту закладку и щелкнуть левой кнопкой мыши в пустом контейнере
в колонке Name (Имя), то откроется поле ввода, в котором можно будет
набрать имя интересующей нас переменной, завершив ввод имени нажатием
клавиши Enter. Операцию добавления глобальных переменных можно многократно повторять. Знак & перед символическим именем глобальной переменной не ставится. В колонке Value (Значение) выводятся текущие значе-
Рис. 2.25. Окно наблюдаемых переменных в момент открытия
62
Рис. 2.26. Возможные
форматы представления
данных в окне наблюдаемых переменных
Рис. 2.27. Окно наблюдаемых глобальных переменных
ния переменных, в колонке Type (Тип) — тип глобальных переменных, а в
колонке Radix (Основание системы счисления) — формат представления
данных (рис. 2.27).
Практическая работа
1. Откройте окно локальных переменных Watch Locals. Модифицируйте
значение локальной переменной z. Убедитесь в том, что адекватные изменения происходят и в окне «дампа» памяти.
2. Добавьте в окно наблюдаемых переменных глобальные переменные x и
y. Соответствуют ли их текущие значения начальным значениям, указанным
в программе?
3. Откройте второе окно Watch 1 «дампа» памяти, указав в качестве
начального адреса &y. Проверьте, что значения наблюдаемых переменных в
окне Watch Window такие же, как в окне «дампа» памяти Memory Window,
приведенном на рис. 2.28. Опробуйте механизм редактирования значений
глобальных переменных. Убедитесь, что изменение значения переменной в
одном из окон автоматически приводит к изменению его значения в другом
окне, а модифицированные значения переменных подсвечиваются красным
цветом.
4. Исследуйте файл с картой загрузки lab2.map и убедитесь, что адреса
размещения глобальных переменных x и y действительно 0x0485 и 0x0484.
5. В какой области встроенного ОЗУ компоновщик разместил глобальные
переменные?
Рис. 2.28. Второе окно «дампа» памяти с глобальными переменными x и y
63
2.9. ВЫПОЛНЕНИЕ ПРОГРАММЫ ПО ШАГАМ
Выполнение функции main() по шагам осуществляется с помощью горячей клавиши F8 или кнопки Single Step на вертикальной панели инструментов. Желтая стрелка в окне исходной программы на С/С++ указывает на
текущий активный оператор. Информация в окнах памяти и наблюдаемых
переменных автоматически обновляется, что позволяет проконтролировать
ход выполнения программы и дать заключение о ее работоспособности.
2.10. ЯЗЫКИ С/C++ И АССЕМБЛЕР
Рис. 2.29. Задание режима одновременного
отображения кодов на С/С++ и Ассемблере
64
Компилятор с языка С/С++ создает
промежуточный выходной файл на
языке Ассемблер. После трансляции
ассемблерного файла генерируется
выходной исполняемый файл в машинном коде (.out). Результаты трансляции
сохраняются в файле листинга (.lst).
Этот файл можно проанализировать,
поскольку он является текстовым.
Кроме того, интегрированная среда
CCS предоставляет уникальную возможность одновременного отображения в окне исходного кода программы
на языке С/С++ соответствующего ему
кода программы на языке Ассемблер.
Это очень удобно для изучающих архитектуру процессора и его систему
команд параллельно с освоением языка
высокого уровня С/С++, а также для
квалифицированных
пользователей,
которые в состоянии самостоятельно
выполнить оптимизацию, заменив
неоптимизированные участки кода
оптимизированными. Выберем в меню
View (Просмотр) команду Mixed
Source/ASM (Смешанный исходный
код на С/С++ и Ассемблере), как
показано на рис. 2.29. Окно исходного
кода С-программы преобразится так,
как приведено на рис. 2.30.
Видно, что код основной программы main размещается, начиная с
адреса 3F8000, т.е. в начале банка H0
Рис. 2.30. Смешанное представление кода исходной программы на языке С/С++ и на Ассемблере
встроенного ОЗУ, отведенного нами под память программ. Для того чтобы
понять ассемблерный код, сгенерированный компилятором, необходимо
знать унифицированный принцип выделения места под локальные переменные и порядок доступа к ним:
• место под локальные переменные называется фреймом локальных переменных и всегда резервируется в стеке в начале выполнения любой функции, в том числе функции main;
• резервирование выполняется за счет инкрементирования указателя
стека SP на величину, равную длине фрейма локальных переменных в количестве слов памяти, выделенных под эти переменные. При этом указатель
стека всегда будет показывать на «первую свободную ячейку памяти», не
относящуюся к фрейму локальных переменных;
• доступ к любой локальной переменной внутри фрейма производится с
помощью стековой базовоиндексной предекрементной адресации —
SP[index], где функцию базы выполняет указатель стека SP, а индекс — задается явно;
• перед выходом из функции место в стеке, выделенное под локальные
переменные, высвобождается. Это делается с помощью декрементирования
указателя стека SP на величину, равную длине фрейма локальных переменных.
Дадим покомандный комментарий к программе на Ассемблере.
1. В программе используется одна локальная переменная z типа long. Поэтому фрейм локальных переменных имеет длину 2 (два слова). Чтобы выделить место под локальную переменную, указатель стека SP нужно дважды
инкрементировать:
65
ADDB SP, #2 ;Сложение содержимого указателя стека
;с байтовой константой 2
;Константа перед сложением расширяется
;слева нулями до слова
Для последующего доступа к переменной z компилятор должен будет
использовать адресацию типа –SP [2].
2. Из анализа карты загрузки программы .map следует, что глобальные переменные располагаются в памяти данных по адресам: x → 0485h; y → 484h.
Для обращения к ним с использованием прямой адресации указатель текущей страницы памяти DP должен быть проинициализирован значением:
484h/40h = 12h (размер страницы памяти данных в процессорах ‘C28 равен
64 словам или 40h). Таким образом, переменные x и y находятся на странице
памяти номер 12h (на 18-й) и имеют относительные адреса: x → 5; y → 4.
Для инициализации указателя текущей страницы DP используется команда
непосредственной загрузки 16-разрядного слова в регистр DP:
MOVW DP,#012H ;DP(15:0) = 16-битовой константе
3. Необходимо выполнить сложение целых чисел со знаком в дополнительном коде x + y. Причем по умолчанию компилятор будет делать это в
рамках 16-разрядной арифметики, так как переменные x и y имеют тип
16-разрядное целое (int). Это означает, что автоматического контроля переполнения при сложении и перехода к 32-разрядной арифметике не предполагается! Сложение будет производиться исключительно в рамках младшего
слова аккумулятора. Для этой операции установка флага SXM не имеет значения. Однако дальше результат сложения должен быть присвоен 32-разрядной переменной z, т.е. потребуется преобразование числа из 16-разрядного
знакового формата в 32-разрядный знаковый. Именно для этой цели устанавливается флаг SXM — режим автоматического расширения знакового разряда:
SETC SXM
;Режим расширения знакового разряда
4. Загружаем первое слово y в младшую часть аккумулятора, используя
прямую страничную адресацию:
MOV
AL,@4
5. Добавляем второе слово x (исключительно 16-разрядное сложение,
перенос «С» в старшую часть аккумулятора AH не записывается):
ADD
AL,@5
6. Выполняем загрузку 16-разрядного числа со знаком из младшей части
аккумулятора AL в 32-разрядный аккумулятор АСС с автоматическим рас66
ширением знакового разряда (именно для этого понадобилось установить
флаг SXM):
МОV ACC,@AL<<5
7. Полученное 32-разрядное число сохраняем в стеке по месту расположения локальной переменной z:
MOVL *-SP[2],ACC
8. Ликвидируем фрейм локальных переменных, т.е. восстанавливаем
начальное значение указателя стека:
SUBB SP,#2
9. Выполняем команду выхода из процедуры:
LRETR
Практическая работа
1. Исследуйте работу программы в пошаговом режиме при различных
наборах входных переменных: (x, y) = (2, 5); (32766, 1); (32766, 2); (–1, –2);
(–32767, –1); (–32767, –2).
2. Объясните результат. К сожалению, его нельзя предсказать, глядя на
программу, написанную на языке С/С++. Для сохранения результата специально выбрали 32-разрядную переменную, чтобы учесть возможные переполнения.
3. Подумайте, как же быть, если необходим 32-разрядный результат с учетом возможных переполнений.
Контрольные вопросы
1. Чем отличаются локальные переменные от глобальных и где они размещаются?
2. Зачем необходимо подключать к проекту библиотеку реального времени?
67
Глава
3
ЗНАКОМСТВО С ЯЗЫКОМ ПРОГРАММИРОВАНИЯ СИ
ДЛЯ МИКРОКОНТРОЛЛЕРОВ TMS320x28xx.
ОСВОЕНИЕ МЕТОДОВ ОТЛАДКИ ПРОГРАММ
В СРЕДЕ CODE COMPOSER STUDIO
3.1. КРАТКИЕ ОБЩИЕ СВЕДЕНИЯ
О ЯЗЫКЕ ПРОГРАММИРОВАНИЯ Си
Язык программирования С (стандартная версия) и С++ (с возможностями
объектно-ориентированного программирования) в настоящее время является
одним из базовых языков программирования высокого уровня и применяется
при решении наиболее сложных задач, в том числе требующих качественного управления большим числом периферийных устройств в реальном времени. Не случайно он стал базовым и для разработки программного обеспечения высокопроизводительных сигнальных микроконтроллеров фирмы
Texas Instruments. Далее вместо названия двух версий языка С/С++ будем
использовать одно общее имя «Си».
Язык Си для ‘C28xx полностью соответствует международному стандарту С99, и для его качественного изучения необходимо пользоваться соответствующей литературой. Транслятор Си для ‘C28xx имеет некоторые особенности, связанные с архитектурой центрального процессора, а также с
методикой доступа к периферийным регистрам процессора. Осваивая технологию разработки и отладки программного обеспечения на языке Си в среде
Code Composer Studio, будем, естественно, обращать внимание на эти особенности.
Как уже известно, содержать главную функцию main() должна любая программа на языке Си, пример которой приведен ниже:
Лист. 3.1. Пример простой программы на языке Си
int i; /* описание переменной типа целое со знаком */
void main(void) /* вызов главной функции программы */
{
i++; /* текст основной программы: */
/* инкрементируем переменную i */
}
68
В этом простом примере переменная i типа целое (int) всего лишь один
раз инкрементируется. Для обозначения операции инкрементирования в
языке Си применяются два подряд набранных символа «плюс» ++, а для
обозначения операции декрементирования — два подряд набранных знака
«минус» – –. Таким образом, оператор i++; эквивалентен оператору i = i + 1;.
В общем случае программа на языке Си состоит из одной или более функций. Каждая функция создается для решения определенной подзадачи и
может быть вызвана из главной функции. Эти функции могут описываться
как в текущем файле, так и в каком-либо другом файле. Кроме того, ряд
наиболее важных функций может быть предварительно разработан, отлажен
и включен в библиотеку стандартных функций. Библиотеки стандартных
функций обычно поставляются разработчиками компилятора Си или разработчиками микроконтроллера, а пользовательские библиотеки создаются
программистами для решения своих типовых задач.
Функциям можно давать любые имена по усмотрению разработчика, но
main — это особое имя — главная, основная функция. Выполнение любой
программы всегда начинается с главной функции. Каждая программа на
языке Си должна содержать эту функцию. Из главной программы можно
вызывать другие функции, описанные в текущей программе, а также библиотечные функции.
Круглые скобки, следующие за именем функции, содержат список аргументов функции. Если функция не имеет аргументов, как в примере выше, то
скобки все равно нужно указывать (). Все операторы, входящие в тело функции, заключаются в фигурные скобки { }. Обращение (вызов) функции осуществляется указанием ее имени, вслед за которым следует заключенный в
круглые скобки список аргументов.
В языке Си предполагается, что все переменные должны быть обязательно описаны до момента их использования. Обычно это делается в начале
функции до первого выполняемого оператора. Если забудете вставить описание какой-либо переменной, то получите диагностическое сообщение об
ошибке в процессе трансляции программы.
Для ввода комментария в языке Си используются либо два подряд расположенных символа косой черты // (если комментарий не переходит на другую строку), либо специальные символы начала /* и конца комментария */.
Все операторы программы на языке Си заканчиваются точкой с запятой.
Мы постепенно будем изучать базовые возможности языка Си.
Имена переменных и констант
Имена переменных в Си составляются из букв и цифр. Первый символ
должен быть обязательно буквой. Символ подчеркивания «_» также считается буквой. Он часто используется в качестве разделителя для составных
имен переменных. Компилятор различает одни и те же имена, набранные
прописными и строчными буквами. Традиционная практика при программировании на Си — использовать строчные буквы для имен переменных, а прописные — для символических имен констант. Ключевые слова, такие как if,
69
else, int, float и т.д., зарезервированы и не могут использоваться в качестве
имен переменных. Ключевые слова записываются строчными буквами.
Основная рекомендация при выборе имен переменных — использовать
имена, отражающие назначение (смысл) переменных в программном модуле.
Типы данных
В языке Си имеется несколько стандартных типов данных: int — целое
число; float — вещественное число в формате с плавающей точкой одинарной
точности; char — символ. Имеется также ряд квалификаторов стандартных типов: short (короткое), long (длинное), unsigned (беззнаковое),
signed (знаковое). Квалификаторы типа short и long указывают на различные
размеры переменных, а квалификаторы signed и unsigned — на формат
представления — число со знаком в дополнительном коде или число без
знака соответственно.
Перечень скалярных типов данных, поддерживаемых процессором
TMS320x28xx, с указанием длины и способа представления переменной
каждого типа, а также диапазона возможного изменения значений переменных, представлен в табл. 3.1.
Табл и ц а 3.1
Типы данных языка Си, поддерживаемые компилятором ‘C28xx
Тип
Длина
Представление
char, signed char
символьный, знаковый символьный
unsigned char
беззнаковый символьный
short
короткий
unsigned short
беззнаковый короткий
int, signed int
целый, знаковый целый
unsigned int
беззнаковый целый
long, signed long
длинный, знаковый длинный
unsigned long
беззнаковый длинный
enum
перечисление, целый
float
вещественный
double
вещественный двойной точности
long double
вещественный длинный двойной
точности
pointers
указатель
far pointers
длинный указатель
16 бит
Код ASCII
16 бит
Код ASCII
70
16 бит
16 бит
16 бит
16 бит
32 бита
32 бита
16 бит
32 бита
Диапазон
Минимум
Максимум
–32768
32767
0
65535
2s complement
–32768
32767
Дополнительный код
Binary
0
65535
Двоичный код
2s complement
–32768
32767
Дополнительный код
Binary
0
65535
Двоичный код
2s complement
–2 147 483 648 2 147 483 647
Дополнительный код
Binary
0
4 294 967 295
Двоичный код
2s complement
–32768
32767
Дополнительный код
Формат IEEE 32 бита 1,17549435e–38 3,4028235e+38
32 бита Формат IEEE 32 бита 1,17549435e–38 3,4028235e+38
32 бита Формат IEEE 32 бита 1,17549435e–38 3,4028235e+38
16 бит
22 бита
Binary
Двоичный код
Binary
Двоичный код
0
0xFFFF
0
0x3FFFFF
Примеры описаний переменных с различными квалификаторами типа
приведены ниже:
short int a;
long int b;
unsigned int result;
Переменная а имеет тип «короткое целое», переменная b — длинное
целое, а переменная result — беззнаковое целое.
Слово int в таких ситуациях может быть опущено, что обычно и делается.
Количество битов, отводимых под эти объекты, зависит от типа центрального процессора, в частности от длины операндов, обрабатываемых в АЛУ,
и, естественно, от реализации компилятора. В нашем случае все переменные
типа int являются 16-разрядными.
Замечания
1. В языке Си для процессоров TMS320x28xx тип короткого целого short
signed int эквивалентен типу int с точки зрения объема памяти, требуемого
для размещения переменной, под такие переменные, как и под переменные
типа int, отводится одно слово.
2. Процессоры ‘C28x имеют 32-разрядное АЛУ, оптимизированное для
работы с 32-разрядными словами в формате с фиксированной точкой. Это
обеспечивает достаточную точность при решении задач управления реального времени, поэтому вещественные типы двойной (double) и прецизионной точности (long double) не отличаются по формату представления от
переменных типа float. Они существуют для обеспечения совместимости со
стандартом языка Си.
3. Операции с 8-разрядными словами (байтами) выполняются как частный случай 16-разрядных операций. При необходимости можно использовать ассемблерные вставки в программах на Си, написанные с применением
мощной системы команд процессоров ‘C28x по работе непосредственно с
байтами.
4. Так как процессоры TMS320x28xx имеют АЛУ, оптимизированное для
работы с 16-разрядными и 32-разрядными операндами, а также 16-разрядную встроенную и внешнюю память, то единицей представления информации для них является не байт (8 разрядов), как для многих других процессоров, а слово (16 разрядов). В стандарте языка Си (ANSI C) определена
функция sizeof, которая должна возвращать число байтов, необходимое для
размещения в памяти переменной данного типа. В нашем случае эта функция будет возвращать не число байтов, а число слов. Применяя ее для переменных типа char или int, получите 1 — одно слово (см. табл. 3.1).
71
Форматы представления констант
В языке Си существует специальная система обозначений для двоичных,
восьмеричных и шестнадцатеричных констант: окончание b в константе указывает на двоичную константу, лидирующий 0 (нуль) указывает на восьмеричную константу, а последовательность из расположенных впереди символов 0x или 0Х — на шестнадцатеричную константу. Например, десятичное
число 31 можно записать как 00011111b в двоичной системе счисления, как
037 в восьмеричной системе счисления и как 0x1F — в шестнадцатеричной.
Операторы описания переменных
Описание переменной выполняется посредством указания ее типа с помощью специально зарезервированного ключевого слова (см. табл. 3.1) и следующего за ним символического имени переменной. Тип переменной может
дополнительно иметь квалификатор типа. Оператор описания переменной,
как и любой другой оператор в языке Си, заканчивается точкой с запятой.
Например, для описания переменной i в качестве целого 16-разрядного
числа в дополнительном коде (int) необходимо ввести:
int i; /* описание переменной типа целое со знаком */
Как уже известно, если оператор описания переменной расположен до
главной функции main(), то переменная относится к так называемым глобальным переменным и под нее резервируется нужное число ячеек в
памяти данных. Если оператор описания переменной находится в теле функции, то данная переменная является локальной и размещается в стеке.
Место в памяти под нее выделяется только в процессе вызова функции.
Оператор описания нескольких переменных состоит из спецификатора
типа и следующего за ним списка переменных, в котором через запятую
перечисляются все имена переменных, например:
int x, y, z;
Переменные могут быть описаны несколькими операторами. Так, вместо
предыдущей одной строки можно использовать три:
int x;
int y;
int z;
Такая запись занимает больше места, но она удобна для добавления индивидуального комментария к каждому описанию переменной и для последующих модификаций программы.
Переменным могут быть присвоены начальные значения непосредственно внутри описаний их типа. Такие переменные называют инициализированными. В этом случае за именем переменной следуют знак равенства и
константа инициализации, например:
72
int a=5;
int b=10;
Допускается групповая инициализация переменных в одном описании,
например:
int a=b=c=d=5;
Язык Си допускает также частичную инициализацию переменных в
одном и том же описании, однако это является плохим стилем программирования:
int a, b, c=d=5;
Если переменная является внешней (определенной в другом программном
модуле) или статической, то инициализация проводится только один раз —
до начала выполнения программы. Термин статическая переменная эквивалентен термину глобальная переменная. Такие переменные определяются
вне функций, под них резервируется часть статической памяти данных, поэтому они существуют все время, пока выполняется программа. Инициализация таких переменных выполняется с применением библиотеки реального
времени выполнения и фактически скрыта от пользователя.
Все переменные, определенные внутри функций, по умолчанию считаются локальными. Они располагаются в стеке. Инициализируемым явно
локальным переменным начальные значения присваиваются при каждом
обращении к функции, в которой они описаны. Все прочие переменные, не
инициализируемые явно, в начале выполнения программы имеют неопределенные значения (т.е. содержат «мусор»).
Внешние и статические переменные по умолчанию инициализируются
нулем, но тем не менее их явная инициализация является признаком хорошего стиля программирования.
Если хотите, чтобы переменная, определенная внутри функции, относилась к статической переменной, то в ее объявлении укажите специальный
префикс static, который является зарезервированным ключевым словом. В
этом случае переменная будет существовать столько же, сколько и программа. Как объявить строку символов с именем name длиной 30 элементов,
показано ниже:
static char name [30];
Основные арифметические операции
Основные арифметические операции с примерами использования представлены в табл. 3.2.
Типовые операции сложения, вычитания, умножения и деления +, –, *, /
работают так же, как и в большинстве других языков программирования,
например в Паскале. В качестве операндов арифметических операций могут
выступать не только переменные и константы, но и любые выражения.
73
Табл и ц а 3.2
Основные арифметические операции
Операция
Описание
Пример
–
Унарный минус
x = –y;
+
Сложение — сумма значений
i = j + 2;
–
Вычитание — разность значений
i = j – 7;
*
Умножение — произведение значений
a = b * c;
/
Деление — частное от деления
%
Остаток от деления — деление по модулю
a = b/c;
++
Инкрементирование — увеличение на 1
i = ++j;
––
Декрементирование — уменьшение на 1
i = – –j;
a = b % c;
Если операция деления / применяется к выражениям целого или символьного типа, то остаток от деления отбрасывается. Это действие называется
«усечением». Например, результатом деления 5/2 является число 2, а результатом операции получения остатка 5%2 — число 1. Операция % называется
также операцией деления по модулю. Если операция деления выполняется
над числами с плавающей точкой, то результат будет числом с плавающей
точкой. Если один из операндов будет целым числом, а второй — числом с
плавающей точкой, то перед операцией деления все операнды будут автоматически преобразованы к формату числа с плавающей точкой.
При вычислении выражений самый высокий уровень старшинства имеет
операция унарного минуса, затем идут операции умножения и деления и,
наконец, операции сложения и вычитания. Операции одного уровня старшинства (умножения, деления или сложения, вычитания) выполняются последовательно слева направо. Порядок вычислений можно задать с помощью
круглых скобок. Явное указание порядка вычисления выражения с помощью
круглых скобок считается хорошим стилем программирования, так как это
улучшает «читаемость» выражения и позволяет не задумываться о приоритетах операций.
Арифметические операции группируются слева направо. Порядок выполнения ассоциативных и коммутативных операций типа + (–) не фиксируется.
Компилятор может перегруппировывать даже заключенные в круглые скобки
выражения, связанные с такими операциями. Таким образом, а + (b + c)
может быть вычислено как (a + b) + c. Это редко приводит к какому-либо
расхождению, но если необходимо обеспечить строго определенный порядок
вычислений, то нужно использовать явные промежуточные переменные или
расставить скобки () в выражении в соответствующем порядке.
Действия, предпринимаемые при положительном и отрицательном переполнении (выходе за максимально или минимально допустимое значение
переменной данного типа), зависят от архитектуры центрального процессора, технологии обработки данных в АЛУ и установленного при инициализации режима работы процессора. Так, для микроконтроллеров ‘C28xx
74
может быть установлен режим насыщения, когда превышение максимального значения 16-разрядного или 32-разрядного числа со знаком в дополнительном коде будет вызывать автоматическое ограничение результата максимально или минимально возможным значением переменной данного типа.
Полный контроль переполнений реализуется при программировании на
Ассемблере. В языке Си для микроконтроллеров TMS320x28xx переполнение не учитывается, как уже выяснилось в гл. 2 при выполнении работы № 2.
Поэтому разработчик программы на Си должен исключить выход переменных за пределы допустимого формата. Это один из существенных недостатков языка Си, который может быть устранен за счет использования ассемблерных вставок или написания наиболее важных функций на Ассемблере.
Особенности операций инкремента и декремента
Операции инкрементирования ++ и декрементирования – – предназначены
для увеличения и уменьшения значений переменных на 1. Оператор ++ добавляет 1 к значению операнда, а оператор — вычитает 1 из значения операнда.
Особенность операций ++ и – – состоит в том, что их можно использовать
и как префиксные операторы (помещая соответствующий знак операции
перед переменной, например ++n), и как постфиксные операторы (помещая
соответствующий знак операции после переменной n++). В обоих случаях n
увеличивается на 1. Но выражение ++n увеличивает n до того, как значение
n будет использовано, а n++ — после использования значения переменной.
В первом случае речь идет о «преинкрементировании» или «предекрементировании», а во втором случае — о «постинкрементировании» или «постдекрементировании».
Операции инкрементирования и декрементирования имеют самый высокий уровень старшинства. Более высокий приоритет имеют только круглые
скобки. Поэтому выражение x*y++ означает (x)*(y++). Так как выполняется
операция постинкрементирования значения y, то результатом данного выражения будет произведение значения переменной x на переменную y, а побочным результатом — инкрементирование y после вычисления выражения.
Операции инкрементирования и декрементирования текущего содержимого указателей выполняются особым образом. Главное отличие состоит в
том, что значение указателя получает приращение не на единицу, а на число,
соответствующее размеру данных в памяти, на которые ссылается указатель
sizeof(*ptr). Так, при работе с обычными целыми числами, занимающими в
памяти одно слово, указатель инкрементируется/декрементируется на единицу, а при работе с длинными целыми числами (long) — на два. Это позволяет с помощью операции инкрементирования/декрементирования указателей автоматически перемещаться к очередным элементам массива.
Приведем несколько примеров:
Лист. 3.2. Пример постинкрементирования
int n = 5;
x = n++;
75
В примере лист. 3.2 переменной x сначала будет присвоено текущее значение переменной n (т.е. 5), а затем значение переменной n увеличится на
единицу (операция постинкрементирования). Таким образом, переменная x
будет равна пяти, а переменная n — шести.
Лист. 3.3. Пример преинкрементирования
int n = 5;
x = ++n;
В примере лист. 3.3 используется операция преинкрементирования значения переменной. Поэтому сначала значение n увеличивается на единицу, а
затем новое значение n присваивается переменной x. Таким образом, обе
переменные x и n будут равны шести.
Контрольные вопросы
1. Объясните термины «преинкрементирование», «постдекрементирование».
2. Подумайте и скажите, чему будут равны переменные x, y и z после выполнения
кода лист 3.4.
Лист. 3.4. Пример программы
int z;
int x = 5;
int y = 4;
void main(void){z = x++ - --y;}
Логические операции
В языке Си следует различать два вида логических операций:
логические операции, которые выполняются над булевыми переменными
с получением булевого результата;
побитовые логические операции, которые могут выполняться одновременно над всеми битами слова с получением результата также в виде слова.
Список логических операций первого типа приведен в табл. 3.3.
В логических выражениях можно использовать любое число условий,
связанных между собой логическими операциями. Таких операций три:
Логическое И (&&), Логическое ИЛИ (||) и Логическое НЕ (!).
Табл и ц а 3.3
Логические операции
76
Операция
Описание
Пример
&&
И
с = a && b;
||
Или
с = a || b;
!
Не, отрицание
с = a ! b;
Выражения, связанные операциями && и ||, вычисляются слева направо,
причем их рассмотрение прекращается сразу же, как только становится ясно,
будет ли результат истиной или ложью:
Лист. 3.5. Пример использования логических операций
x || y //ИЛИ. Логическое выражение y вычисляется
//только тогда, когда x=false
x && y //И.Логическое выражение y вычисляется
//только тогда, когда x=true
Старшинство операции && выше, чем ||, и обе они младше операций
отношения и равенства.
Унарная операция отрицания ! преобразует ненулевой или истинный операнд (true) в 0 (false), а нулевой или ложный операнд (false) в 1 (true).
Рассмотренные логические операции применяются, главным образом,
при вычислении логических выражений, которые в свою очередь применяются в условных операторах и операторах организации циклов.
Поразрядные (побитовые) операции
В отличие от многих других языков программирования, в Си определен
полный набор поразрядных логических операций. Эти операции применяются
над 16-разрядными словами типа int (целое) или unsigned int (беззнаковое
целое).
Наиболее часто побитовые логические операции используются для
сброса, установки или инвертирования одного бита или группы битов в
слове. При этом логическая операция выполняется с непосредственным операндом — маской. Подобные операции применяются для работы с регистрами встроенных периферийных устройств микроконтроллеров, в том числе
для инициализации этих устройств и управления ими в реальном времени.
При выполнении арифметических операций над числами в разных форматах широко используются операции сдвига влево или вправо на заданное
число зарядов. При этом число разрядов сдвига указывается через пробел
сразу после знака соответствующей операции << или >>. Такой сдвиг является логическим — на освободившиеся места записываются логические
нули, а «вытесняемые» за пределы слова разряды — теряются.
Табл и ц а 3.4
Поразрядные операции
Операция
Описание
Пример
&
И
a & b;
|
Или
a | b;
^
Исключающее ИЛИ
a ^ b;
~
Не
a ~ b;
>>
Сдвиг вправо
a >> b;
<<
Сдвиг влево
a << b;
77
Следует быть внимательным и отличать побитовые логические операции
& и | от логических операций над булевыми переменными && и ||. Например, пусть имеем две переменные типа int: a=1 (0001b) и b=2 (0010b).
Результат операции побитового «И» a&b даст нулевое значение, в то время
как результат вычисления логического выражения a&&b окажется равным
единице. Это объясняется тем, что с точки зрения языка Си любое число,
отличное от 0 в логическом выражении, рассматривается как истина (true).
Примеры использования поразрядных операций приведены в Лист. 3.6.
Лист. 3.6. Примеры использования поразрядных операций
int x,y,z;
...
x = y & 0xFF; //выделение младшего байта слова
z = y >> 8;
//выделение старшего байта слова
Операции отношения и сравнения
Операциями отношения пользуются в логических выражениях, в частности, для организации условных ветвлений в программах. Операции типа
больше >, больше или равно >=, меньше < и меньше или равно <= имеют
одинаковое старшинство. Непосредственно за ними по уровню старшинства
следуют операции равенства == и неравенства !=, которые тоже имеют одинаковое старшинство. Операции отношения младше арифметических операций, так что выражение a < b – 1 рассматривается как выражение a < (b – 1).
Читатели, которые уже познакомились с системой команд микроконтроллеров ‘C28xx, обратили внимание на то, что в самом процессоре операции
сравнения для чисел без знака и для чисел со знаком отличаются. Для чисел
со знаком существует операция «больше», а для чисел без знака «выше». В
языке Си такого различия нет. Поэтому предполагается, что сравниваться
могут только выражения одинакового типа, например int или unsigned int. В
том случае, если сравниваются выражения разного типа — компилятор
выдает предупреждение об ошибке.
Табл и ц а 3.5
Операции отношения и сравнения
78
Операция
Описание
Пример
>
Больше чем
a > b;
>=
Больше или равно
a >= b;
<
Меньше чем
a < b;
<=
Меньше или равно
a <= b;
==
Равно
a == b;
!=
Не равно
a != b;
Оператор присваивания
В языке Си символом оператора присваивания является знак =. В одном
операторе присваивания можно присвоить одно и то же значение многим
переменным. Для этого используется оператор множественного присваивания, например:
x=z=y=0;
В программах на Си широко используются операторы составного присваивания: вначале над переменной выполняется указанная перед символом =
арифметическая или логическая операция, после чего результат операции
сохраняется в переменной. Составное присваивание выполняется в режиме:
«Чтение старого значения переменной — Модификация — Запись нового
значения». При этом оператор присваивания становится более компактным.
Например, операция увеличения значения переменной x на пять может
выглядеть следующим образом:
x=x+5;
а при использовании оператора составного присваивания:
x+=5;
Варианты составных операций присваивания приведены в табл. 3.6
Обратите внимание, что операции поразрядной конъюнкции И, дизъюнкции ИЛИ и Исключающего ИЛИ задаются с помощью символов &, | и ^
соответственно. В приведенных примерах для четырех младших битов переменной y производятся: очистка, установка и побитовое инвертирование
соответствующих разрядов.
Табл и ц а 3.6
Операции присваивания
Операция
=
Описание
Присваивание
Пример
x = y;
*=
Умножение с присваиванием
x *=y; (x = x * y;)
/=
Деление с присваиванием
x /= y; (x = x / y;)
%=
Остаток от деления с присваиванием
x %= y; (x = x % y;)
+=
Сложение с присваиванием
x += y; (x = x + y;)
–=
Вычитание с присваиванием
x – = y; (x = x – y;)
<<=
Сдвиг влево на заданное число разрядов с присваиванием
x <<= 2; (x = x << 2;)
>>=
Сдвиг вправо на заданное число разрядов с присваиванием
x >>= 5; (x = x >> 5;)
&=
Поразрядное И с присваиванием
|=
Поразрядное ИЛИ с присваиванием
y |= 0xFFF0 (y = y | 0xFFF0)
^=
Поразрядное Исключающее ИЛИ с присваиванием
y ^= 0xFFF0 (y = y ^ 0xFFF0)
y &= 0xFFF0 (y = y & 0xFFF0)
79
3.2. ИСПОЛЬЗОВАНИЕ СТАНДАРТНЫХ БИБЛИОТЕК
Стандарт языка Си предполагает использование ряда стандартных библиотек. Они предназначены для: вычисления математических функций,
таких как синус, косинус и др.; организации ввода/вывода данных; эффективной обработки строк символов; динамического распределения памяти и
т.п. Для каждой из таких библиотек вместе с компилятором поставляются
стандартные заголовочные файлы, в которых определяются все необходимые для использования библиотеки типы переменных, макросы и функции.
Для того чтобы использовать библиотечные функции, необходимо с помощью
директивы препроцессора #include включить в текст исходной программы
заголовочный файл соответствующей библиотеки. Например, для подключения библиотеки математических функций нужно выполнить директиву:
#include <math.h>
После этого станет возможным доступ к функциям этой библиотеки,
например к функции вычисления косинуса:
#include <math.h>
float x, y;
…
x = cos(y);
Существуют два варианта использования директивы #include:
#include <имя_файла>
#include “имя_файла”
При использовании угловых скобок в задании имени файла подключаемый файл ищется в одном из стандартных каталогов, причем текущий каталог проекта с исходными файлами не просматривается. При использовании
имени файла, заключенного в кавычки, наоборот, поиск производится в текущем каталоге проекта с файлами на исходном языке. Сами библиотечные
функции хранятся в скомпилированном виде в перемещаемом объектном
коде и подключаются к программе только на этапе компоновки.
Для отладки программного обеспечения, разрабатываемого для персональных компьютеров, широко используется стандартная библиотека
ввода/вывода stdio. Она обеспечивает все нужные пользователю функции
для работы с файлами и устройствами ввода/вывода (клавиатурой и дисплеем), что позволяет разрабатывать интерактивные программы с контролем
хода их выполнения на экране дисплея и c сохранением результатов в файлах.
Технология отладки программного обеспечения для встроенных систем
управления заметно отличается от технологии отладки программ на языке
Си для персональных компьютеров. Это отличие состоит в том, что возможности ввода/вывода данных для встроенных систем ограничены: нет подключенной к плате процессора стандартной клавиатуры и дисплея. Тем
не менее интегрированная среда CCS через специальный отладочный
80
JTAG-интерфейс обеспечивает практически те же возможности ввода/
вывода данных, а порой и существенно большие. Мы уже познакомились с
окнами памяти и окнами наблюдаемых переменных, используемых в среде
CCS для отладки проектов.
Оказывается, интегрированная среда CCS позволяет дополнительно
использовать и стандартные функции ввода/вывода, освоенные большинством программистов на языке Си. Для этого необходимо лишь подключить
заголовочный файл библиотеки:
#include <stdio.h>
Естественно, что в среде CCS функции, входящие в библиотеку, эмулируются. Например, вывод данных производится не на весь экран компьютера, а
в специальное, выделенное для этой цели окно.
В общем случае кроме заголовочного файла необходимо подключить к
проекту и саму библиотеку, из которой на стадии компоновки будут извлекаться объектные модули в перемещаемом формате. Некоторые наиболее
важные библиотеки, такие как stdio, уже включены разработчиками компилятора в библиотеку реального времени выполнения rts2800_ml.lib. Поэтому достаточно подключить только соответствующий заголовочный файл и
библиотеку реального времени выполнения.
Практическая работа
1. Найдите в каталоге include заголовочный файл библиотеки стандартного ввода/вывода stdio.h и попытайтесь проанализировать его содержимое.
2. Есть ли в списке функций ввода/вывода функции для работы с файлами: открыть — fopen, закрыть — fclose, переименовать — rename, удалить — remove и т.д.?
3. Функции ввода/вывода символов и строк, например чтения символа со
стандартного устройства ввода — getchar?
4. Функции форматируемого ввода/вывода, например printf и scanf?
3.3. ФУНКЦИЯ ФОРМАТИРОВАННОГО ВЫВОДА PRINTF()
Покажем возможности использования библиотечных функций ввода/
вывода на примере функции printf() — форматируемого вывода на экран
значений переменных и символьных строк.
В простейшем случае функция используется для вывода на дисплей текстовых сообщений, сигнализирующих о выполнении определенной задачи,
возникновении ошибки в расчетах и т.п. При этом аргументом функции
является символьная строка, заключенная в двойные кавычки, например:
printf(“Вас приветствует интегрированная среда CCS!”);
Когда управление будет передано этой функции, то через канал связи
целевой платы с компьютером ему будет передана строка символов, являю81
щаяся аргументом функции. Далее уже интегрированная среда CCS выведет
в окно Stdout запрограммированное сообщение, результаты выполнения
которого представлены на рис. 3.1. Сообщение выводится в то же окно, что и
результаты трансляции и сборки проекта Build, но на другую закладку
Stdout.
Если хотите, чтобы вывод следующего сообщения начался с новой
строки, то введите в конце текущего сообщения символ перевода строки \n:
printf(“Вас приветствует интегрированная среда CCS!\n”);
Если этого не сделать, то курсор останется на месте, и следующее сообщение будет продолжать предыдущее. Последовательность символов \n
является так называемой управляющей последовательностью, которая применяется для ввода специальных символов, в данном случае символа перевода строки Enter. Любая управляющая последовательность символов начинается со знака \, например \t — символ табуляции.
Специальный символ процента %, включенный в текстовую строку, сигнализирует функции printf() о том, что, начиная с текущей позиции, необходимо напечатать значение переменной, имя которой указано через запятую
сразу после текстового сообщения. Следующий за символом % символ,
например, d является спецификатором преобразования и указывает на то, в
каком виде должно быть выведено значение переменной: в данном случае в
формате десятичного числа со знаком:
printf(“result = %d\n”, result); /* Вывод результата в
окно CCS.*/
Таким образом, функция printf() позволяет печатать любые текстовые
сообщения, в частности названия переменных, сопровождая их значениями
переменных в нужном формате. Комбинация символов %d является указанием места в строке, начиная с которого следует выводить значение переменной. Возможны различные варианты спецификации преобразования значения переменной:
%d — аргумент преобразуется в формат десятичного числа со знаком;
%o — аргумент преобразуется в формат беззнакового восьмеричного
числа (без лидирующего нуля);
Рис. 3.1. Результат выполнения функции printf()
82
%x — аргумент преобразуется в формат беззнакового шестнадцатеричного числа (без лидирующих 0x);
%u — аргумент преобразуется в формат беззнакового десятичного числа;
%c — аргумент рассматривается как отдельный символ;
%s — аргумент является строкой: символы строки печатаются до тех пор,
пока не будет достигнут нулевой символ или не будет напечатано количество
символов, указанное в спецификации точности;
%e — аргумент, рассматриваемый как переменная типа float, преобразуется в десятичную форму в виде экспоненциальной записи
[–]M;NNNNNNE[+–]XX, где число символов после десятичной точки N
определяется указанной точностью. Точность по умолчанию равна 6;
%f — аргумент, рассматриваемый как переменная типа float, преобразуется в десятичную форму в виде [–]MMM.NNNNN, где число символов
после десятичной точки N определяется указанной точностью. Точность по
умолчанию равна 6.
Между символом % и символом преобразования могут находиться так
называемые модификаторы преобразования:
• знак «минус», который указывает на необходимость выравнивания значения преобразованного аргумента по левому краю его поля. Если этот знак
не указывается, значение автоматически выравнивается по правому краю
поля;
• строка цифр, задающая минимальную ширину поля для вывода значения. Если преобразованный аргумент имеет меньше символов, чем указанная ширина поля, то он будет дополнен слева (или справа, если было указано
выравнивание по левому краю с помощью знака –) заполняющими символами до этой ширины. Заполняющим символом обычно является пробел, а
если ширина поля указывается с лидирующим нулем, то этим символом
будет нуль (лидирующий нуль в данном случае не означает восьмеричного
формата представления числа);
• точка, которая отделяет ширину поля от следующей строки цифр,
используемой для задания точности (для типов данных с плавающей точкой);
• точность (строка цифр), которая указывает на максимальное число символов, выводимых справа от десятичной точки для переменных типа float;
• модификатор длиной l, который указывает, что соответствующий элемент данных имеет тип long, а не int.
Если идущий за символом % символ не является символом преобразования, то печатается сам этот символ. Следовательно, символ % можно напечатать, указав % %.
Приведем простые примеры. Предположим, что на экран необходимо
вывести текущие значения трех целочисленных переменных i, j, k в виде
обычного десятичного числа и выполнить перевод строки:
printf(“i= %d; j= %d; k= %d;\n”, i, j, k);
Обратите внимание, что формат обращения к функции следующий:
printf(“Управляющая строка”, аргумент1, аргумент2, и
т.д.);
83
При этом управляющая строка представляет собой строку символов в
кавычках — фразу, которая показывает, как и в каком месте должны быть
напечатаны перечисленные вслед за управляющей строкой аргументы.
В качестве аргументов могут использоваться константы, переменные или
выражения. В последнем случае значения выражений вычисляются непосредственно перед выводом на экран. Функция printf() использует управляющую строку для определения числа последующих аргументов. Их число
должно строго соответствовать числу вхождений символов % в управляющую строку. Если количество аргументов окажется недостаточным или они
будут иметь несоответствующие типы, то будет выдано сообщение об
ошибке.
Еще один пример. Предположим, что переменная x является переменной
с плавающей точкой и на экран необходимо вывести ее значение в десятичной форме записи в поле размером 10 символов, включая десятичную
точку, и точностью числом десятичных цифр справа от десятичной точки,
равной 4:
printf(“х= %10.4f \n”,x);
В заключение напомним, что использовать функцию форматированного
вывода можно только после подключения заголовочного файла стандартной
библиотеки ввода/вывода (лист. 3.7).
Лист. 3.7. Демонстрация использования библиотечной функции
/* Подключение заголовочного файла */
/* стандартной библиотеки ввода-вывода. */
/* С помощью директивы препроцессора include. */
#include <stdio.h>
/* Основная программа. */
main() /* Главная функция программы. */
{ /* Вывод строки в окно CCS. */
printf(“Вас приветствует интегрированная среда CCS!\n”);
}
3.4. НЕКОТОРЫЕ РЕКОМЕНДАЦИИ ПО ОТЛАДКЕ ПРОГРАММ
Предположим, что Вы написали простую программу суммирования двух
целых чисел (например, лист. 3.8) и хотите исследовать правильность выполнения в языке Си операции суммирования на любых наборах аргументов.
Лист. 3.8. Программа суммирования двух целых чисел
/* Описание (объявление) переменных */
int a; /* Описание первого слагаемого */
int b; /* Описание второго слагаемого */
int result; /* Описание результата (суммы) */
84
Окончание лист. 3.8
/* Основная программа */
main() /* Главная функция программы */
{ /* Текст программы — подсчет суммы двух чисел */
a = 10;
b = 5;
result = a + b; /* Подсчет суммы двух чисел */
}
Отладку интересующего нас фрагмента программы удобно выполнять в
бесконечном цикле:
while(1) /* Бесконечный цикл. */
{
result = a + b; /* Расчет суммы двух чисел. */
}
При этом, открыв окно содержимого памяти или окно наблюдаемых переменных, можно менять исходные значения аргументов и в реальном времени
наблюдать за результатом расчета. Результат можно вывести или в окно
наблюдаемых переменных, или в окно Stdout с использованием функции
printf().
Для организации бесконечного цикла применяют оператор while с условием, равным истине (1).
3.5. ПОДКЛЮЧЕНИЕ СТАНДАРТНОЙ БИБЛИОТЕКИ
ВВОДА/ВЫВОДА. МОДЕРНИЗАЦИЯ ФАЙЛА
УПРАВЛЕНИЯ КОМПОНОВКОЙ
Практическая работа
1. Выполняя предыдущую практическую работу (см. с. 81), Вы обратили
внимание на существование каталога, специально предназначенного для размещения проектов пользователей c:\ti\myprojects. В этом каталоге будут
размещаться и Ваши проекты. Создайте подкаталог с именем \username по
нескольким первым буквам вашей фамилии, например \petrov. Для каждой
новой работы организуйте отдельный подкаталог. Например, для текущей
работы создайте подкаталог \lab3. Напоминаем, что формирование каталогов
можно выполнить и автоматически в процессе создания нового проекта (п. 3).
2. Запустите Code Composer Studio. В открывшемся окне менеджера
параллельной отладки выберите либо режим симулятора F2812 Device
Simulator/CPU (программно-логической модели), либо режим эмулятора
F2812 PP Emulator/CPU_1, если целевая отладочная плата eZdsp28 подключена к параллельному порту компьютера и питание подано (рис. 3.2).
3. Создайте новый проект lab3.pjt с использованием команд меню
Project → New в каталоге ваших проектов.
85
Рис. 3.2. Окно менеджера параллельной отладки
4. Создайте исходную программу на языке Си по образцу, приведенному
на лист. 3.3. Подключите заголовочный файл стандартной библиотеки
ввода/вывода и добавьте функцию вывода в окно Stdout текущего значения
переменной result. Воспользуйтесь встроенным редактором интегрированной среды CCS, выбрав команду меню File → New → Source File (Новый
исходный файл) или нажав горячую клавишу Ctrl+N. Ваша программа
должна выглядеть, как программа, приведенная на рис. 3.3. Сохраните программу в каталоге \lab3.
5. Добавьте исходный Си-файл к новому проекту с помощью меню
Project → Add Files to Project… (Добавить файл к проекту). Убедитесь, что
слева от списка виртуальных каталогов проекта напротив папки Source появился знак +. Щелкните по нему мышкой. Убедитесь, что подключили нужный файл. Если ошиблись и подключили не тот файл, установите курсор на
имени этого файла и вызовите правой кнопкой мыши контекстно-зависимое
меню. Командой Remove from Project удалите ненужный файл из проекта.
Повторите операцию подключения.
Рис. 3.3. Программа суммирования двух чисел
86
6. Выполните процедуру подключения
к проекту командного файла управления
компоновкой Linker Command File
(*.cmd), созданного в предыдущей практической работе lnk.cmd (из соответствующего каталога без операции копирования), а также файла библиотеки реального
времени выполнения rts2800_ml.lib.
7. Откройте список содержимого вашего
проекта, щелкая на значки + рядом с
Project, lab3.pjt, Libraries и Source. Убедитесь, что проект содержит все необходимые файлы (рис. 3.4).
Замечание
Если на экране CCS отсутствует окно
менеджера проекта, выберите команду
меню View → Project. Если в данный
момент в окне менеджера проекта
выбрана иконка Bookmarks (Закладки),
щелкните на иконке File внизу плана проекта.
Рис. 3.4. Окно «дерева» проекта
8. Оттранслируйте созданную программу lab3.c. Если написали программу без ошибок, то никаких сообщений об ошибках или предупреждений
получить не должны. В противном случае отредактируйте программу и повторите трансляцию.
9. Выполните трансляцию и компоновку (сборку) всего проекта Project
→ Rebuild All. Действовать можно либо через меню, либо нажав кнопку
Rebuild All на панели инструментов. Как видите, в окне сборки появилось
сразу несколько сообщений об ошибках (рис. 3.5).
Проанализируем сообщения об ошибках.
Первое сообщение свидетельствует о том, что компоновщик не смог разместить в банке памяти M1SARAM объемом 1024 слова (0400h) секцию
переменных .ebss, реальный размер которой составляет 2404 слова (0964h).
Рис. 3.5. Результаты сборки проекта в окне build
87
Для нормальной работы функции форматированного ввода/вывода потребовался дополнительный объем памяти более 2К слов.
Во втором сообщении указывается, что для размещения динамической
памяти данных (кучи) также не хватило места: секция .system размером
0400h не поместилась в банке статического ОЗУ M0SARAM, выделенном
под стек.
Третье сообщение является следствием первых двух и свидетельствует о
том, что выходной исполняемых файл lab3.out не был создан.
Дело в том, что подключили стандартную библиотечную функцию и не
учли, что такое подключение потребует определенных ресурсов памяти.
Придется изменить командный файл компоновщика. Лучше создать
новый файл с учетом возникших дополнительных потребностей в памяти.
Для ускорения работы можно воспользоваться примером файла, рекомендуемого фирмой TI для целей отладки программ в среде CCS (рис. 3.6). Переименуем этот файл в lab3.cmd и снабдим комментариями на русском языке.
Как видите, мы существенно расширили объем памяти данных за счет
включения двух последовательно расположенных банков L0 и L1 встроенного ОЗУ объемом по 4К слова каждый. Общий объем дополнительной
памяти в объединенном блоке L0L1RAM составляет 8К слов.
Все секции данных для размещения глобальных и статических переменных .ebss, констант .econst, а также динамически распределяемой памяти
.esystem будем размещать в этом новом блоке памяти L0L1RAM.
Контрольные вопросы
1. Объясните назначение всех секций, описанных в командном файле.
2. Почему секция .cinit должна размещаться в памяти программ?
3. В какую секцию должны попасть переменные a, b, result нашей программы?
Практическая работа
1. Отключите от проекта файл lab2.cmd и подключите к проекту файл
lab3.cmd. Выполните перекомпиляцию и сборку проекта Rebuild All. Убедитесь, что в окне Build теперь отсутствуют сообщения об ошибках. Это окно
после автоматической загрузки программы в память целевой платы имеет
две закладки: Build и Sdtout. Вторая закладка имитирует терминал пользователя, на который будут выводиться все сообщения из вашей программы по
команде форматированного вывода printf().
2. Убедитесь в том, что в результате трансляции создан выходной исполняемый файл lab3.out, а также файл с картой загрузки lab3.map. Место расположения этих файлов по умолчанию — директория \Debug.
3. Откройте файл lab3.map непосредственно из среды CCS (File → Open)
и выпишите адреса размещения переменных a, b, result в памяти данных
(они нам понадобятся в процессе отладки).
4. На основе информации в файле lab3.map проанализируйте распределение секций нашей программы по банкам памяти микроконтроллера. Соответствует ли оно командному файлу? Сколько дополнительного места в кодовой
памяти и памяти данных потребовалось для «обслуживающих» программ?
88
а)
б)
Рис. 3.6. Структура командного файла компоновщика:
а, б — распределение памяти и секций
3.6. ВЫПОЛНЕНИЕ ПРОГРАММЫ В РЕЖИМЕ ПРОГОНА
Как уже известно, если установлена опция автоматической загрузки программы сразу после сборки проекта Load Program After Build, то программа будет загружена в память целевой платы и автоматически откроется
окно дизассемблера. При этом счетчик команд установится на начало секции
инициализации _с_init_00.
Если автоматическая загрузка не предусмотрена, то можно выполнить
«ручную» загрузку по команде File → Load Program. Загружать следует
89
файл с исполняемым кодом lab3.out, который находится в каталоге:
c:\ti\myprojects\username\lab3\Debug\.
Для выполнения программы инициализации процессора, установки значений глобальных констант и инициализированных переменных применяется команда Debug → Go Main — Выполнить до главной программы
main().
Теперь можно выполнить программу или в пошаговом режиме, как уже
делали, или в режиме прогона. Запуск программы в режим прогона производится командой меню Debug → Run (Отладка → Выполнить) или щелчком
мыши на кнопке Run, расположенной на панели управления. На кнопке
изображена пиктограмма «бегущего человечка».
Для остановки процесса выполнения программы пользователя используется команда меню Debug → Halt (Отладка → Останов) или соответствующая клавиша на панели управления.
Практическая работа
1. Загрузите и выполните программу в пошаговом режиме. Откройте окно
наблюдаемых переменных и следите за ходом процесса.
2. Выполните программу в режиме прогона. Обновляйте значения переменных a и b, контролируйте результат result в окне наблюдаемых переменных и в окне эмулятора терминала Stdout.
3. Модернизируйте программу для одновременного вывода на терминал
не только результата операции, но и значений операндов. Поэкспериментируйте с различными форматами вывода.
3.7. ВЫПОЛНЕНИЕ ПРОГРАММЫ С ТОЧКАМИ ОСТАНОВА
Интегрированная среда Code Composer Studio имеет широкий арсенал
средств отладки, среди которых особое место занимают точки останова.
Точка останова — это специально помеченный оператор программы, дойдя
до которого выполнение программы в режиме прогона автоматически прекращается. Находясь в режиме останова процессора, исследуйте состояние
любых переменных, а затем, убедившись, что нужный фрагмент программы
выполнен правильно, продолжите ее выполнение до следующей точки останова и т.д.
Задать точку останова можно двумя способами. Первый — через команду
меню Debug → Breakpoints… (Отладка → Точки останова…). В появившемся окне Break/Probe Points (Точки останова/Пробные точки) можно
выбрать тип точки останова, например, Break at Location (Останов по
месту) или Break at Location if expression is TRUE (Останов по месту,
если заданное выражение ИСТИНА) и ввести ее параметры (адрес размещения или адрес и условие останова).
Второй способ установки точки останова заключается в двойном щелчке
левой кнопкой мыши в соответствующей строке исходной программы на
самой левой серой полосе окна, которая используется для индикации точек
90
Рис. 3.7. Вид программы с двумя точками останова
останова и текущего положения счетчика команд. Напротив соответствующего оператора появится красная точка, которая и будет означать точку останова — рис. 3.7. Повторный двойной щелчок мыши используется для удаления текущей точки останова.
Если установить точки останова напротив команд result = a + b и printf()
в окне исходной программы и запустить программу в режиме прогона Run,
то она будет останавливаться после каждого из двух операторов, включенных в бесконечный цикл. После останова можно будет модифицировать значения исходных операндов a и b в окне Watch и продолжить выполнение
программы до момента получения результата в окне Stdout и т.д.
Практическая работа
1. Откройте окно наблюдаемых переменных Watch, как Вы это делали в
гл. 2. Добавьте в него переменные a, b и result.
2. Попробуйте установить точку останова, пользуясь меню CCS.
3. Исследуйте второй способ установки и снятия точек останова с помощью кнопки мыши.
4. Проверьте правильность выполнения программы на различных наборах исходных операндов.
5. Модернизируйте программу для исследования других математических
операций, описанных в табл. 3.2. Задайте аргументы функции printf() таким
образом, чтобы на экране одновременно отобразились значения входных
переменных и значения результатов нескольких математических операций,
например, чтобы вывод на экран выглядел следующим образом:
a = 35; b = 4; a + b = 39; a – b = 31; aæb = 140.
91
3.8. ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ ПО ИЗУЧЕНИЮ
СИСТЕМЫ КОМАНД МИКРОКОНТРОЛЛЕРА ‘C28x
Уже упоминали о том, что Ассемблер микроконтроллера ‘C28x обладает
мощными возможностями, изучив которые можно писать максимально
эффективные программы. Среда CCS предоставляет Вам ряд сервисных
функций, которые облегчают самостоятельное изучение системы команд.
Среди этих функций: одновременное отображение текста исходной программы на языках Си и Ассемблер; пошаговое выполнение исходной программы на языках Си или Ассемблер; выдача контекстно-зависимой справочной информации по командам процессора; автоматическое отображение
текущего содержимого важнейших регистров процессора непосредственно в
окне исходной программы, отображение значений меток в командах переходов и т.п.
Практическая работа
1. Упростите исходную программу, исключив из нее функцию форматированного вывода, сохраните программу под другим именем, например
lab3_0.c (рис. 3.8).
2. Отсоедините от проекта файл lab3.c и подключите файл lab3_0.c.
Выполните перекомпиляцию и сборку проекта. Выполните процедуру инициализации (Go Main).
3. Установите режим отображения в окне исходной программы смешанного кода по команде View → Mixed Source/ASM (рис. 3.9).
Рис. 3.8. Программа lab3_0.c
92
Рис. 3.9. Программа lab3_0.c в CCS в режиме MIX MODE
Переключение режима Source Mode/
Mixed Mode можно выполнить и из контекстно-зависимого меню, находясь в
окне исходной программы.
Текущее положение счетчика команд
для программы на языке Си отображается
стрелкой желтого цвета, а для программы
на языке Ассемблер — зеленого цвета.
Стрелки такого же цвета изображены и на
клавишах пошагового выполнения SourceSingle Step (Один шаг в программе на
Си) и Assembly-Single Step (Один шаг в
программе на Ассемблере).
Если щелкнуть клавишей мыши по
любой ассемблерной инструкции в окне
смешанного представления исходного
кода (именно по коду инструкции, а не по
адресу размещения команды или по полю
операнда[ов]), а затем нажать клавишу
помощи F1, то можно получить так называемую контекстно-зависимую подсказку. На экран компьютера будет выдан
список всех команд процессора, имеющих аналогичную мнемонику, но с разными возможными вариантами задания
операнда[ов]. Например, для команды
ADD получим (рис. 3.10):
Рис. 3.10. Список всех команд, имеющих
мнемонику ADD
93
Теперь можно выбрать конкретную команду, руководствуясь содержимым
поля операндов. Команда ADD AL, @6 относится к классу команд, работающих с 16-разрядным аккумулятором AX. Это обозначение является общим
для обозначения как младшего слова аккумулятора AL, так и старшего слова
AH. Символическое обозначение loc16 применяется для обозначения любого
операнда в памяти данных, адресуемого с применением разрешенного в процессоре способа адресации. В нашем случае это будет прямая страничная
адресация. Итак, выбираем первую команду ADD AX, loc16 и получаем
справку по ее применению (см. рис. 3.10).
Вся справочная информация по командам процессора построена однотипно:
• даются возможные варианты синтаксиса команды; приводится оптокод
команды, причем символ A является битовым адресом старшего или младшего слова аккумулятора, а символы LLLL LLLL задают фактически используемый в команде способ адресации операнда; указывается, в каком объектном режиме работы процессора команда может быть использована (символ
X предназначен для любого режима работы); отображается возможность
включения команды в цикл повторения RPT; приводится длительность
выполнения команды в числе циклов CYC;
• описываются операнды команды;
• приводится краткое описание действий, выполняемых командой;
• дается информация о модернизации командой флагов процессора;
• указывается возможность включения команды в цикл повторения;
• приводится простой пример использования команды с комментариями.
Ясно, что наша команда выполняет сложение содержимого 16-разрядного
аккумулятора AL с содержимым прямоадресуемой ячейки памяти данных по
адресу, смещенному относительно начала текущей страницы данных на 6
(короткий адрес операнда на странице), и записывает результат сложения
обратно в аккумулятор AL. По результатам операции сложения вырабатываются признаки отрицательного N и нулевого результата Z, переноса С и
переполнения V. Флаг С используется для анализа переполнений при работе
с числами без знака, а флаг V — для анализа переполнений при работе с числами со знаком в дополнительном коде.
Отладочная среда CCS позволяет быстро вывести на экран текущее значение регистров процессора без необходимости открывания специального
окна содержимого регистров Registers. Для этого, находясь в окне с исходным кодом программы, достаточно лишь установить курсор на символическом имени регистра процессора, например AL. Его текущее содержимое
будет немедленно выведено на экран в прямоугольной рамке. К сожалению,
эта великолепная возможность не предусмотрена для переменных в памяти.
Более того, установив курсор на метку L1 в команде безусловной передачи управления SB L1, UNC, получите информацию о фактическом значении метки L1: L1=0x003F8006. Это означает, что команда короткого безусловного относительного перехода вернет управление по адресу
0x003F8006.
94
Таким образом, интегрированная среда CCS предоставляет разработчику
программного обеспечения великолепные возможности по изучению системы команд процессора и отладке программ как на языке высокого уровня
Си, так и на Ассемблере.
Практическая работа
1. Проанализируйте ассемблерные команды, встречающиеся в программе.
Имейте в виду, что все наши переменные являются глобальными и размещены компоновщиком по следующим адресам: _a: 8086h; _b: 8084h;
_result: 8085h. Для доступа к ним применяется прямая страничная адресация. Каждая страница имеет длину 64 слова (40h). Таким образом, переменные размещаются на странице памяти с номером 8084h div 40h = 0202h.
Именно это значение используется для инициализации указателя текущей
страницы данных DP. Короткий прямой адрес переменной _а на текущей
странице памяти равен 6, переменной _b: 4, а переменной _result: 5.
2. Правильно ли мы дали комментарий к ассемблерным командам — лист. 3.7.
3. Откройте окна содержимого регистров ядра процессора View →
→ Registers → Core и статусных регистров View → Registers → Status,
окно наблюдаемых переменных. Выполните программу по шагам, наблюдая
за содержимым аккумулятора, указателя текущей страницы DP, флагов
результатов операций: Z, N, C, V, а также собственно переменных a, b, result.
Объясните результаты для различных наборов переменных, например, указанных в табл. 3.7.
Лист. 3.9. Комментарий к программе на Ассемблере
3F8000
main:
3F8000 761F
MOVW DP,#0x0202
;Инициализация указателя текущей
;страницы памяти данных DP.
3F8002 2806
MOV @6,#0x000A
;Инициализация операнда а числом 10.
3F8004 2804
MOV @4,#0x0005
;Инициализация операнда b числом 5.
3F8006
L1:
3F8006 9204
MOV AL,@4
;Загрузить в аккумулятор операнд b.
3F8007 9406
ADD AL,@6 ; Сложить с операндом а.
3F8008 9605
MOV @5,AL
;Сохранить результат по адресу result.
3F8009 6FFD
SB
L1,UNC; Повторить вычисление.
Табл и ц а 3.7
Наборы переменных для тестирования программы
a
+2
+32767
–3
–32768
+32767
b
+15
+2
–78
–2
–50
95
4. Убедитесь в том, что среда CCS позволяет быстро получить информацию о текущем состоянии регистров процессора и значениях меток в командах передачи управления.
5. Исследуйте программу, которую Вы написали для изучения других
арифметических операций в табл. 3.2 с точки зрения реализации ее на
Ассемблере. Выполните отладку.
6. Поэкспериментируйте с различными вариантами записи операторов
присваивания, модернизировав исходную программу (см. табл. 3.3).
Контрольные вопросы
1. В чем преимущества и недостатки рассмотренных в этой работе способов отладки
программного обеспечения с использованием стандартной библиотеки ввода/вывода?
2. Сравните методы отладки программы в пошаговом режиме и с точками останова.
3. Какие действия будут выполнены следующим оператором на языке Си: x += y; ?
4. Как получить информацию о текущем содержимом регистров процессора XAR1,
SP? Укажите минимум две возможности.
96
Глава
4
БАЗОВЫЕ ВОЗМОЖНОСТИ ЯЗЫКА
ПРОГРАММИРОВАНИЯ СИ
4.1. ОПЕРАТОРЫ ВЕТВЛЕНИЯ
Условный оператор if-else
Конструкция if-else используется для принятия решения по ветвлению
программы (лист. 4.1).
Лист. 4.1. Конструкция if-else
if (выражение)
оператор 1;
else
оператор 2;
Сначала вычисляется выражение, стоящее в скобках после слова if: если
оно истинно, то выполняется оператор 1, в противном случае — оператор 2.
Вторая часть условного оператора, начинающаяся с зарезервированного
слова else, может опускаться. Это так называемая сокращенная запись условного оператора — оператор 1 выполняется только при истинности выражения. В противном случае управление просто передается следующему оператору программы.
Допускается вложение условных операторов друг в друга. При этом оператор else относится к ближайшему оператору if (лист. 4.2).
Лист. 4.2. Вложение условных операторов
if (n>0)
if (a>b)
z=a;
else
z=b;
По умолчанию, часть условного оператора else относится к внутреннему
оператору if. Внешний оператор if имеет сокращенную форму записи. Если
требуется иная интерпретация, необходимо должным образом расставить
фигурные скобки, как показано в лист. 4.3.
97
Лист. 4.3. Пример оператора ветвления
if (n>0)
{
if (a>b)
z=a;
}
else
z=b;
Здесь внешний условный оператор имеет полную форму, а внутренний
оператор — сокращенную.
Реализация механизма многоступенчатого принятия решения приведена в
лист. 4.4.
Лист. 4.4. Структура if-else
if (выражение)
оператор 1
else if (выражение)
оператор 2
else if (выражение)
оператор 3
else if (выражение)
оператор 4
else
оператор 5
Выражения вычисляются по порядку. Как только одно из выражений окажется истинным, выполняется соответствующий ему оператор. На этом последовательность проверок завершится.
Оператор переключения switch
Оператор switch (см. лист. 4.5) используется для реализации множественного выбора. Вычисляется значение выражения, указанного в скобках
вслед за ключевым словом switch. Полученное значение последовательно
сравнивается с константами во всех вариантах case. Управление передается
тому набору операторов, который помечен соответствующей константой.
Лист. 4.5. Оператор переключения
switch (выражение)
{
case константное выражение 1: операторы
case константное выражение 2: операторы
case константное выражение 3: операторы
default: операторы
}
98
Каждый вариант case может помечаться целой или символьной константой или константным выражением. Не допускается использование одинаковых константных выражений. Константные выражения не должны содержать ни переменных, ни вызовов функций, так как вычисляются на этапе
компиляции программы.
Если значение выражения не соответствует ни одной константе в вариантах case, то выполняются операторы, следующие за словом default, т.е. операторы по умолчанию. Это необязательная часть оператора-переключателя
switch, которая может отсутствовать.
Если значение выражения не соответствует ни одной константе, то никаких действий не выполняется и управление передается следующему оператору программы.
Оператор переключения имеет одну очень важную особенность: ключевое слово case вместе с константой выполняет всего лишь функцию метки,
на которую передается управление для некоторого варианта значения выражения. Поэтому будут выполняться не только операторы для текущего варианта case, но и для всех последующих вариантов. Для выхода из переключателя после выполнения необходимого оператора case служит специальный
оператор прерывания break, который необходимо поставить в конце выполнения последовательности операторов case, как показано в примере лист. 4.6.
Лист. 4.6. Оператор переключения с прерыванием
switch (c)
{
case 0: case 1: case 2:
операторы
break;
case 3: case 4: case 5:
операторы
break;
case 6: case 7: case 8:
операторы
break;
default:
операторы по умолчанию
break;
}
С одной стороны, это позволяет реализовывать множественный выбор. С
другой стороны, если необходимо прервать выполнение оператора case при
срабатывании какого-либо из условий, то вслед за нужной последовательностью операторов необходимо использовать специальный оператор прерывания break, как показано в примере лист. 4.6. Инструкция break вызывает
немедленный выход из конструкции с оператором switch.
99
Контрольные вопросы
1. В программе объявлена переменная y типа int. Может ли по значению этой переменной работать оператор переключения: switch (y)?
2. В операторе switch (ch) в качестве метки использовано символическое имя переменной y (case y:). Допускается ли такая запись? Почему?
3. С помощью оператора переключения реализуйте алгоритм вывода на экран названия месяца по его номеру. В случае, если передается номер несуществующего месяца
(например, 13), должно выводиться сообщение: нет такого месяца. Для вывода на экран
используйте функцию стандартной библиотеки ввода/вывода printf(), изученную в предыдущей работе.
4.2. ОПЕРАТОРЫ ОРГАНИЗАЦИИ ЦИКЛОВ
Цикл while
С операторами организации циклов уже познакомились в предыдущей
работе и даже отладили фрагмент программы, включенный в бесконечный цикл.
С помощью циклов организуют повторяющиеся вычисления. Одним из
наиболее часто встречающихся является оператор цикла while — повторять
инструкции, расположенные за оператором while до тех пор, пока значение
выражения, приведенного в скобках, остается истинным (лист. 4.7).
Лист. 4.7. Оператор цикла while
while (выражение)
{
операторы
}
Как только при очередном вычислении выражение станет ложным, цикл
завершится и управление будет передано следующему оператору за оператором while. Такой цикл называется циклом с предварительной проверкой
условия выхода из цикла. В примере лист. 4.8 выход из цикла будет осуществлен, когда a станет больше b.
Лист. 4.8. Пример с оператором цикла while
a = 0;
b = 10;
while (a <= b)
{
a++;
/* инкрементировать а */
printf("счетчик: a = %d\n", a);
}
Значение выражения в цикле while вычисляется предварительно, т.е. до
выполнения операторов тела цикла. Следовательно, если это значение окажется ложным с самого начала, то операторы в теле цикла вообще не будут
выполнены.
100
Цикл do while
Оператор цикла do while аналогичен оператору цикла while, за исключением того, что условие выхода из цикла проверяется не вначале, а в конце
группы операторов, входящих в тело цикла. Это цикл с постпроверкой условия выхода из цикла (лист. 4.9).
Лист. 4.9. Оператор цикла do while
do
операторы
while (выражение);
В отличие от цикла while, в цикле do while операторы тела цикла выполняются по крайней мере один раз. Далее следует вычисление выражения.
Если оно истинно, то операторы выполняются вновь. В противном случае
управление передается следующему оператору. В примере программы
лист. 4.10 на экран будет выведено значение счетчика, равное 21, так как
условие выхода из цикла проверяется после выполнения тела цикла.
Лист. 4.10. Пример с оператором цикла do while
a = 10;
b = 20;
do
{
/* инкрементировать а */
a++;
printf("счетчик: a = %d\n", a);
}
while (a <= b);
Цикл for
При организации любого цикла, тело которого должно быть выполнено
заданное число раз, обязательно необходимы следующие три действия:
1) инициализация счетчика числа циклов;
2) сравнение текущего значения счетчика числа циклов с некоторым граничным значением для определения момента завершения цикла;
3) инкрементирование (или декрементирование) счетчика числа циклов
при каждом выполнении тела цикла.
В циклах while заботу о сравнении берет на себя условное выражение.
Приращение текущего состояния счетчика числа циклов реализуется либо в
теле цикла отдельной командой (см. лист. 4.10), либо с помощью «попутной»
операции инкрементирования/декрементирования непосредственно при
вычислении условного выражения, приведенного ниже:
Лист. 4.11. Пример с оператором цикла while
while (count++ <= n)
101
Единственная операция, которая оказывается при этом вне цикла, — операция инициализации счетчика числа циклов. Необходимо всегда помнить
об этом. Если не выполнить инициализацию счетчика числа циклов, то
результаты выполнения программы будут непредсказуемыми.
Оператор цикла for позволяет существенно упростить организацию циклов,
так как собирает все три действия, необходимых для организации циклов в
одном месте. Пример использования этого оператора представлен ниже:
Лист. 4.12. Пример с оператором цикла for
for (count=0; count <= n; count++)
оператор;
В круглых скобках оператора for должны находиться три выражения, разделенных символом точки с запятой ;.
Первое выражение служит для инициализации счетчика числа циклов.
Оно выполняется только один раз в начале цикла. В нашем примере счетчику числа циклов count присваивается нулевое значение.
Второе выражение — условное выражение для проверки условия выхода
из цикла. Когда это условие становится ложным, обеспечивается автоматический выход из цикла. В нашем примере цикл будет выполняться до тех
пор, пока значение счетчика будет меньше или равно n.
Третье выражение вычисляется в конце каждого цикла. Обычно служит
для инкрементирования/декрементирования счетчика числа циклов.
Вслед за заголовком цикла for следует простой или составной оператор,
описывающий тело цикла. Общий синтаксис оператора цикла for представлен ниже:
Лист. 4.13. Оператор цикла for
for (выражение 1; выражение 2; выражение 3)
{
операторы;
}
Любое из трех выражений в цикле for может отсутствовать, но точку с
запятой опускать нельзя. При отсутствии выражения 1 и выражения 3 считается, что их просто нет в конструкции цикла. При отсутствии выражения
2 предполагается, что его значение всегда истинно. Например, запись, приведенная в лист. 4.14, представляет собой «бесконечный» цикл.
Лист. 4.14. Бесконечный цикл с оператором for
for (;;)
{
операторы;
}
102
Контрольные вопросы
1. Начальное значение переменных целого типа x = 2, y = 1, k = 0, n = 9. Чему будет
равно значение переменной y после выполнения следующего цикла:
while (k<n)
{ y = y*x;
k++;
}
2. Какая информация будет выведена на экран в окно Stdout при выполнении следующего цикла:
count = 1;
do
printf(“Значение счетчика count = %d\n”, count);
while (++count<=10)
3. Замените циклы циклами с оператором for. Результат должен быть таким же.
4.3. РАБОТА С УКАЗАТЕЛЯМИ
Указатель — это специальный регистр центрального процессора или
ячейка памяти, который (ая) содержит адрес некоторого объекта (переменной). С помощью указателя обеспечивается «косвенный» доступ к объекту.
Большинство операций на Ассемблере поддерживает косвенную адресацию
операндов как наиболее эффективную. С этой целью в состав центрального
процессора микроконтроллеров ‘C28x введено так называемое вспомогательное АЛУ, которое обеспечивает широкий спектр операций с содержимым регистров-указателей XAR1-7 (пред-, постинкрементирование и декрементирование и др.). Регистры-указатели XARi являются 32-разрядными и
обеспечивают доступ ко всей расширенной памяти данных микроконтроллера.
Предположим, что х — переменная, например, типа int, а рх — указатель
на эту переменную. Для того чтобы получить адрес фактического расположения переменной в памяти, используется специальная операция &: она
выдает адрес объекта по его символическому имени (лист. 4.15).
Лист. 4.15. Операция возвращение адреса
рх = &х;
Таким образом, приведенная выше запись означает, что указатель на переменную x, имеющий символическое имя px, проинициализирован адресом
фактического расположения переменной x в памяти, т.е. «указывает» на
переменную x.
103
Операция получения адреса & применима только к переменным и элементам массива; конструкции вида «адрес выражения», например &(х – 1), и
«адрес константы», например &3, являются бессмысленными. Нельзя также
получить адрес регистровой переменной.
Унарная операция * представляет собой операцию доступа по указателю.
Если px — это указатель, то запись *px означает «содержимое области
памяти, адресуемой указателем px». Таким образом, оператор присваивания
обеспечивает загрузку переменной y значением из области памяти, адрес
которой находится в переменной px (лист. 4.16). Обращаем ваше внимание
на то, что все операнды с косвенной адресацией на языке Ассемблер также
начинаются с предшествующего символа косвенной адресации *.
Лист. 4.16. Операция обращения к переменной по указателю
y = *рх;
Проследите, что происходит в результате выполнения следующих двух
операторов присваивания, показанных в лист. 4.17. Вначале инициализируется указатель px адресом переменной x, а затем выполняется обращение к
данным по этому указателю, т.е. фактически извлекается значение переменной x.
Лист. 4.17. Пример работы с указателем
рх = &х;
y = *рх; // Эквивалентно y = x;
Указатели должны быть описаны до момента их использования, аналогично всем другим переменным. Описание указателя выполняется следующим образом: указывается тип переменной, на которую будет ссылаться указатель, и далее приводится символическое имя указателя с предшествующим
символом *, представленным в лист. 4.18. В этом примере переменные рх,
py, pz являются указателями на переменные типа int.
Лист. 4.18. Объявление переменных типа указатель
int х,y,z;
int *рх,*py,*pz;
Смысл определения выше: переменные, на которые ссылаются указатели
px, py, pz, — целого типа int. Например, операция косвенного доступа по
указателю *рх может появиться в любом контексте, где может понадобиться
текущее значение х. Так, оператор ниже (лист. 4.19) присваивает y значение
на единицу большее, чем значение х.
Лист. 4.19. Пример работы с указателем
y = *рх + 1;
104
Ссылки на указатели могут появляться и в левой части операторов присваивания. Так, первый оператор, приведенный в лист. 4.20, обеспечивает
присваивание переменной x значения, равного нулю. Второе выражение
выполняет по существу инкрементирование переменной х. Такое же действие выполняется и с помощью оператора в третьей строке.
Лист. 4.20. Примеры операций с указателем
*рх = 0;
*рх +=1;
(*рх)++;
Круглые скобки в последнем примере необходимы, если хотим инкрементировать именно переменную, которая адресуется указателем. Напротив,
если хотим добраться до текущего элемента массива, а затем «переместить»
указатель на следующий элемент массива, необходимо использовать запись,
показанную в лист. 4.21.
Лист. 4.21. Пример работы с указателем
y=*px++;
Переменной y будет присвоено значение по текущему указателю, а затем
значение указателя будет инкрементировано для доступа к следующему элементу массива, т.е. будет выполнено постинкрементирование указателя.
Обратите внимание, что при объявленном указателе типа int указатель будет
автоматически инкрементироваться на 1, а при указателе типа long — на 2.
Указатели являются переменными, поэтому с ними можно обращаться,
как и с любыми другими переменными. Так, значение одного указателя
можно присваивать другому (лист. 4.22).
Лист. 4.22. Пример работы с указателем
py = px;
Контрольные вопросы
1. Указатели должны быть не только объявлены, но и обязательно проинициализированы перед использованием. Что произойдет, если не выполнить инициализацию?
2. При описании типа указателя инициализация указателя может быть попутной.
Выполняется в двух вариантах: 1) с помощью обычного оператора присваивания;
2) посредством указания адреса переменной в скобках сразу после имени указателя.
Каким операторам эквивалентны следующие два оператора?
int *pa = &a;
int *pb (&b);
105
4.4. УКАЗАТЕЛИ И МАССИВЫ
В языке Cи существует сильная взаимосвязь между указателями и массивами. Любую операцию, которую можно выполнить с помощью индексов
массивов, можно выполнить и с помощью указателей. Вариант с указателями оказывается обычно более быстрым. Этот вариант выбирается и компилятором и опытным программистом, который пишет программу на языке
Ассемблер. Для начинающих программистов, изучающих программирование на языке Си, работа с указателями кажется более сложной, чем работа с
массивами. С приходом опыта точка зрения меняется.
Массив объявляется с помощью указания типа элементов массива, символического имени массива и длины массива, заключенной в квадратные
скобки (лист. 4.23).
Лист. 4.23. Объявление массива
int inp_buffer[100]; /* массив входных данных */
Массив целочисленных переменных типа int состоит из 100 элементов, к
каждому из которых можно обратиться по символическому имени массива
inp_buffer (по существу, базовому адресу массива) и индексу, указанному в
квадратных
скобках,
например
inp_buffer[0],
inp_buffer[1],...,
inp_buffer[99]. Последний допустимый индекс будет на 1 меньше общего
числа элементов массива, указанного в объявлении. Обращение к i-му элементу массива возможно с помощью выражения inp_buffer[i].
Объявим указатель input типа int для доступа к элементам массива, как
показано в лист. 4.24. Инициализация указателя может быть выполнена
начальным адресом массива любым из приведенных ниже способов:
Лист. 4.24. Инициализация указателя
int *input;
/*указатель для доступа к элементам массива */
input = inp_buffer; /*базовый адрес массива */
input = &inp_buffer[0];
/*адрес первого элемента массива */
Существует очень тесное соответствие между индексацией и арифметикой указателей. В действительности компилятор преобразует ссылку на массив в указатель на начало массива.
После того как указатель на массив определен, к элементам массива возможен доступ с помощью указателя. Так, оператор, приведенный в лист. 4.25,
выполняет копирование первого элемента массива inp_buffer[0] в x.
Лист. 4.25. Доступ к элементу через указатель
x = *input;
106
Для доступа к i-му элементу массива можно воспользоваться выражением
inp_buffer[i] или указателем *(input+i). Последнее выражение имеет следующий смысл: доступ к переменной по указателю input, предварительно увеличенному на смещение, пропорциональное номеру элемента i. Для элементов типа int смещение будет равно i, а для элементов типа long — 2*i.
Будьте внимательны. Положение скобок при работе с указателями имеет
большое значение (лист. 4.26).
Лист. 4.26. Пример работы с указателем
x = (*input)+2;
// Добавить к первому элементу массива число 2
x = *(input+2);
// Загрузить третий элемент массива в переменную x
Имеется существенное различие между именем массива и указателем.
Указатель является переменной, так что операции типа input = inp_buffer и
input++ имеют смысл. Однако имя массива является всего лишь константой,
а не переменной. Поэтому конструкции типа inp_buffer=input или
inp_buffer++ использовать нельзя.
Практическая работа
1. Создайте новый проект lab4.pjt. Подключите к проекту командный
файл компоновщика из предыдущей работы lab3.cmd, а также файл библиотеки реального времени выполнения rts2800_ml.lib. Подключите к проекту в
качестве исходного файла файл lab4_a.c. Он содержит пример простой программы на языке Си в которой объявляются два массива целых чисел по 10
элементов каждый, и выполняется копирование элементов из массива источника в массив приемник (рис. 4.1). Массив передатчик предварительно инициализируется, массив приемник — нет. Исследуйте программу. Найдите
операторы описания массивов и объясните, что означает инициализировать
массив списком?
2. Выполните компиляцию и компоновку программы. Убедитесь в отсутствии ошибок.
3. Как изменится оператор описания массива источника, если все константы задать не в десятичной системе счисления, а в шестнадцатеричной?
Внесите соответствующие изменения в программу. Перекомпилируйте ее.
4. Попробуйте проинициализировать только часть элементов массива
источника. При этом все оставшиеся элементы должны быть автоматически
проинициализированы нулями. Убедитесь, что этот механизм работает.
5. Выполните отладку программы в пошаговом режиме и в режиме с точкой останова (можно на последнем операторе). Откройте для этого два окна
дампов памяти: для наблюдения за содержимым массива приемника и массива источника. Проследите за процессом копирования данных.
107
Рис. 4.1. Программа копирования массива с использованием цикла for
Контрольные вопросы
1. Объясните синтаксис оператора for. Почему логическое условие в операторе цикла
записано как i < 10, а не, например, так: i <= 10? Какой индекс будет иметь последний
скопированный элемент массива? Почему?
2. Какие действия выполняются в последнем операторе программы? Замените этот
оператор на более наглядный.
Среда Сode Composer Studio допускает работу с массивами и в окнах
Watch, что значительно удобнее, чем использование окон дампов памяти.
При этом вначале в позиции Name оба массива отображаются как упакованные и в поле Value выводятся начальные адреса размещения массивов в
памяти. В поле Type указывается размер массива. Поле Radix используется
обычным образом — здесь указывается формат вывода данных (система
счисления). Стоит щелкнуть мышкой по значку + рядом с именем массива,
как массив «раскроется» и можно не только исследовать каждый его элемент по отдельности, но и при необходимости модифицировать его значение
(рис. 4.2).
Практическая работа
1. Выполните отладку программы с использованием окна Watch.
2. Удалите из проекта файл lab4_a.c и подключите вместо него файл
lab4_b.c, представленный на рис. 4.3. Выполните отладку новой программы.
108
Рис. 4.2. Окно просмотра переменных
Рис. 4.3. Программа копирования массива с помощью цикла while
Сравните две программы с точки зрения простоты организации цикла. Какой
программе Вы отдадите предпочтение? Почему?
3. Модифицируйте программу для копирования длинных чисел со знаком
типа long. Выполните отладку.
4. Замените файл lab4_b.c в проекте на файл lab4_с.c, демонстрирующий
работу с указателями (рис. 4.4). Выполните трансляцию и компоновку.
5. Обратите внимание на то, как объявлены указатели на массив приемник и на массив передатчик, как выполнена инициализация указателей.
Выполните инициализацию процессора и переменных по команде Go Main.
Исследуйте дампы памяти и докажите, что указатели на самом деле проинициализированы правильно.
6. Попробуйте выполнить начальную инициализацию указателей не
попутно, как это сделано в программе, а явно, с помощью отдельных операторов. Убедитесь, что ваши изменения дают те же результаты.
109
Рис. 4.4. Копирование массива с использованием указателей
7. Выполните отладку программы в пошаговом режиме. Следите за указателями ptr_to и ptr_from и одновременно за содержимым массивов приемника и передатчика.
Контрольные вопросы
1. Какие значения примут указатели ptr_to и ptr_from в конце цикла?
2. Как работает последний оператор программы? К какому элементу массива на
самом деле прибавляется число 5? Проверьте свои соображения.
3. Перепишите программу с использованием цикла for.
4.5. ФУНКЦИИ В ЯЗЫКЕ СИ
Материал, представленный ниже, является лишь кратким введением в
технологию создания и использования функций на языке Си.
В языке Cи функции эквивалентны функциям и процедурам в языке Паскаль или подпрограммам на языке Ассемблер. Функции предоставляют
удобный способ заключения некоторой части программы в «черный ящик»,
который в дальнейшем можно использовать, не интересуясь его внутренним
содержимым. Если функции организованы должным образом, то можно
игнорировать то, как делается работа, — достаточно знание того, что делается.
110
Язык Cи разработан таким образом, чтобы сделать использование функций легким, удобным и эффективным. Функции позволяют разбить большие
вычислительные задачи на маленькие подзадачи и использовать в работе то,
что уже сделано другими, а не начинать каждый раз с пустого места. Мы уже
применяли некоторые внешние функции, включенные в стандартные объектные библиотеки, например printf(). Прежде чем использовать такую функцию, ее необходимо обязательно объявить, что мы и делали в подключаемых
заголовочных файлах. Кроме того, к проекту должна быть подключена соответствующая объектная библиотека. Пользователь может разрабатывать и
создавать свои собственные библиотеки наиболее часто используемых функций — пользовательские библиотеки.
В любом программном модуле можно использовать как внешние функции,
так и внутренние. Первые могут находиться либо в библиотеках, либо в других программных модулях и должны быть обязательно объявлены в текущем
модуле. Внутренние функции должны быть описаны в текущем программном модуле. Любую функцию можно вызвать только в том случае, если она
описана. Для определения функций используется следующий шаблон:
Лист. 4.27. Объявление функции
тип_результата имя_функции(список_аргументов)
{
операторы_тела_функции
}
Тип результата определяет тип возвращаемого функцией значения. Функции могут и не возвращать никакого значения. В этом случае в качестве
типа результата указывается тип void (пустой).
Список аргументов содержит типы и имена формальных параметров,
указанных через запятую (если они существуют). Если в функцию не передаются никакие параметры, то в списке аргументов указывается тип void.
Далее в операторных скобках {} описывается тело функции, т.е. последовательность выполняемых ею действий. В качестве последнего оператора функции обычно используется оператор возврата в основную программу
return (лист. 4.28).
Лист. 4.28. Пример объявления функции
long Sum (int x, int y)
{
return ((long)x+(long)y);
}
Как видно из примера, в лист. 4.28 определена функция Sum сложения
двух переменных типа int. Из определения видно, что функция возвращает
значение типа long. Значение, вычисленное функцией, возвращается в вызывающую ее программу с помощью оператора return, который имеет синтаксис, показанный ниже:
111
Лист. 4.29. Оператор return
return выражение;
Если функция не возвращает никакого значения, то оператор return может
вызываться без выражения либо вообще не использоваться (лист. 4.30).
Лист. 4.30. Пример оператора return
return;
В выражении для определения возвращаемого значения использованы
две операции явного преобразования типов (long) x и (long) y, которые позволяют еще до операции сложения преобразовать входные 16-разрядные
переменные типа int в выходные 32-разрядные типа long (см. лист. 4.28).
После того как функция определена, ее можно использовать в программе.
Вызов функции производится по ее имени, а вместо формальных параметров
указываются фактические параметры.
Существуют два способа передачи параметров в функцию. Первый называется передачей параметров по значению, а второй — по ссылке.
Примером первого способа является вызов функции в лист. 4.31. Параметры a и b вначале вычисляются, а затем записываются в стек для использования в теле функции Sum. Параметры, переданные по значению, нельзя
изменить в теле самой функции.
Лист. 4.31. Пример вызова функции
void main(void)
{
int a = 2;
int b = 10;
long result;
result = Sum(a,b);
}
Если функция должна изменить значения аргументов, то выбирается второй способ передачи параметров — с помощью передачи указателей на
переменные. В этом случае функция будет иметь доступ к аргументам и при
необходимости может изменить их. Например, после вычисления суммы
переменных a и b можно обнулить их значения (лист. 4.32).
Лист. 4.32. Пример описания функции с аргументом типа указатель
long Sum(int *x, int *y)
{
long temp_sum = (long)(*x) + (long)(*y);
*x = 0;
*y = 0;
return temp_sum;
}
112
Как видно из определения функции, теперь в нее передаются не два значения типа int, а два указателя на переменные типа int. В теле функции вначале вычисляем значение локальной переменной temp_sum, а затем обнуляем значения аргументов. Очевидно, что и вызов функции будет теперь
выглядеть иначе:
Лист. 4.33. Пример вызова функции с аргументом типа указатель
void main(void)
{
int a = 2;
int b = 10;
long result;
result = Sum(&a,&b);
}
Теперь вызывающая программа передает в функцию адреса переменных
a и b. Напомним, что операция & выдает адрес переменной, следовательно,
&a является значением указателя на переменную a.
Указатели в качестве аргументов обычно используются в функциях, которые должны возвращать более одного значения. Можно сказать, что Sum
возвращает три значения: результат суммирования и две обнуленные переменные.
Практическая работа
1. Подключите к проекту файл lab4_e.c с программой, выполняющей суммирование элементов двух массивов, размеры которых известны (рис. 4.5).
Обратите внимание на то, что функция суммирования элементов массива
Summa_Array произвольной длины описана до основной программы main()
и имеет два формальных параметра:
массив, подлежащий обработке int array[];
размер массива int m.
Запись int array[] означает, что в функцию передаются не значения всех
элементов массива, а лишь указатель на начало массива. Вторым параметром в функцию передается длина массива. Функция возвращает в основную
программу сумму элементов массива типа long, подсчитанную с учетом возможных переполнений. Для последовательного суммирования элементов
двух массивов функция вызывается из основной программы дважды с разными значениями фактических параметров. При этом имена массивов x и y,
используемые в качестве фактических параметров, содержат начальные
адреса массивов и являются по существу значениями указателей на массивы.
2. Выполните отладку программы. Проследите за тем, как управление
передается функции, как выполняется возврат в основную программу.
113
Рис. 4.5. Пример использования функции
Задайте несколько разных наборов переменных, в частности таких, при которых результат намного превысит формат слова. Например, проинициализируйте первый массив числами (+20 000), а второй массив числами (–30 000).
Правильно ли работает наша программа?
3. Замените исходный файл в проекте на модернизированный файл
lab4_f.c (рис. 4.6). Новая функция суммирования элементов массива отличается только тем, что в качестве формального параметра явно задан указатель
на массив. Вызов функции практически не отличается от предыдущего, так
как значения указателей ptr_x и ptr_y равны x и y соответственно. Выполните отладку программы на разных наборах данных.
4. Сравните ассемблерный код, который генерируется транслятором с
языка Си для двух рассмотренных программ. Есть ли отличия?
Контрольные вопросы
1. Должен ли тип фактического параметра функции обязательно соответствовать
типу формального параметра?
2. Какие действия выполняются оператором *pa++ = *pb++; ?
3. Напишите функцию нахождения минимального элемента массива, максимального
элемента.
114
Рис. 4.6. Работа функций через указатели
4. Разработайте функцию сортировки массива целочисленных элементов по нарастанию значений. Можете руководствоваться, например, таким алгоритмом:
1) найти минимальный элемент массива;
2) поменять местами найденный элемент с первым элементом массива;
3) сделать то же самое (два предыдущих шага), но начиная уже со второго элемента
массива;
4) выполнить эти действия (n – 1) раз, где n — число элементов массива.
115
Глава
5
ТЕХНОЛОГИЯ РАБОТЫ С ПЕРИФЕРИЙНЫМИ
УСТРОЙСТВАМИ МИКРОКОНТРОЛЛЕРОВ
СЕМЕЙСТВА C28xx НА ЯЗЫКЕ СИ. РАБОТА
С ПОРТАМИ ДИСКРЕТНОГО ВВОДА-ВЫВОДА,
ТАЙМЕРАМИ, КОНТРОЛЛЕРОМ ПРЕРЫВАНИЙ
5.1. СПОСОБЫ ДОСТУПА К РЕГИСТРАМ
ВСТРОЕННЫХ ПЕРИФЕРИЙНЫХ УСТРОЙСТВ
МИКРОКОНТРОЛЛЕРА НА ЯЗЫКЕ СИ
На кристалл микроконтроллеров ‘C28xx интегрировано большое число
периферийных устройств, начиная от менеджера событий и аналого-цифрового преобразователя и кончая портами последовательного асинхронного и
синхронного ввода/вывода. Работа с ними выполняется с помощью соответствующих регистров, которые отображены на память данных микроконтроллера. Соответствующие области памяти данных называются периферийными фреймами. Термин отображены на память данных следует понимать
так, что некоторые ячейки внутренней памяти данных микроконтроллера
жестко отведены производителем под регистры периферийных устройств и
не могут использоваться как память данных общего назначения (для размещения переменных пользователя).
В микроконтроллерах ‘C28x нет специальных команд ввода/вывода данных типа IN/OUT. Любая команда процессора, которая обращается к памяти
данных по записи, может записать данные в регистр ПУ, и, напротив, любая
команда, которая обращается к памяти данных по чтению, может считать
данные из регистра ПУ. Непременным условием является лишь соответствие
адреса операнда адресу регистра периферийного устройства.
Суть настройки периферии (инициализации) заключается в установке
значений определенных регистров, определяющих нужный режим работы
ПУ. В простейшем случае это сводится к записи шестнадцатеричных данных
в конкретные ячейки памяти данных, выделенные под регистры ПУ, или к
выполнению побитовых логических операций между текущим содержимым
регистра ПУ и маской. Напомним, что операция «Логического И» с маской
позволяет сбросить нужные биты регистра, операция «Логического ИЛИ» —
установить нужные биты, а операция «Исключающее ИЛИ» — проинвертировать.
116
Соответствующие логические операции легко выполняются на языке
Ассемблер с использованием так называемых атомарных команд «Чтение—
Модификация—Запись», которые за один цикл выполняют операцию получения данных из регистра ПУ, модификации данных (посредством выполнения
логической операции с маской) и записи результата обратно в регистр ПУ.
Аналогичные действия можно выполнить и на языке высокого уровня Си.
При этом возможны два различных подхода.
Рассмотрим суть первого подхода. Например, регистр управления аналого-цифровым преобразователем (АЦП) ADCTRL1 находится в памяти
данных микроконтроллера по адресу 0x00007100. Для удобства работы с
ним посредством директивы препроцессора #define определяется соответствующий макрос ADCTRL1 (лист. 5.1). По существу, этой директивой
определяется указатель на переменную типа unsigned int по имени
ADCTRL1, который одновременно инициализируется значением адреса
регистра управления в памяти данных (0x00007100).
Дополнительный модификатор типа volatile запрещает компилятору
выполнять оптимизацию доступа к переменной по этому указателю. Дело в
том, что из некоторых регистров ПУ может потребоваться последовательная
многократная операция чтения в одну и ту же переменную. Если спецификатор типа volatile опустить, то компилятор может «подумать», что это описка
программиста, и исключить один из, «как ему кажется», ненужных операторов из программы. Спецификатор volatile применяется к объектам, значения
которых могут меняться вне зависимости от кода программы, например, по
прерыванию или в процессе работы периферийного устройства, — говорят,
что такие переменные могут меняться «на лету».
Теперь работа с регистрами управления АЦП ADCTRL1 и ADCTRL2
может осуществляться через объявленные указатели: первым оператором в
лист. 5.1 производится запись данных (слова 0x1234) в регистр ПУ, а вторым
оператором — инвертирование 14-го бита регистра с помощью сложного
оператора присваивания. Этот оператор производит вначале считывание
текущего содержимого регистра, затем — операцию «Логического ИЛИ» с
маской 0x4000 и, наконец, операцию записи результата маскирования
обратно в регистр ПУ.
Лист. 5.1. Традиционный доступ
#define ADCTRL1(volatile unsigned int *)0x00007100
#define ADCTRL2(volatile unsigned int *)0x00007101
//Прим. volatile-запрет оптимизации доступа к переменной
void main(void)
{
*ADCTRL1 = 0x1234; // Запись регистра целиком
*ADCTRL2 |= 0x4000;
// Установка 14 бита. Прим. |= операция ИЛИ
}
Рассмотрим пример генерации ассемблерного кода компилятором Си при
работе с таймером общего назначения CPU Timer 0, приведенного в табл. 5.1.
117
Табл и ц а 5.1
Традиционный доступ к регистрам ПУ
Исходный Си-код
Ассемблерный код,
созданный компилятором Си
// Остановить CPU Timer0
*TIMER0TCR |= 0x0010;
MOV
ORB
MOV
// Загрузить новое 32-разрядное
// значение периода
*TIMER0TPR32 = 0x00010000;
MOVL XAR4, #3072
MOVL XAR5, #65536
MOVL *+XAR4[0], XAR5
// Запустить CPU Timer0
*TIMER0TCR &= 0xFFEF;
MOV
AND
MOV
Недостатки:
Код плохо читается без комментариев.
Битовые маски должен определять сам программист.
AL, *(0:0x0c04)
AL, #0x10
*(0:0x0c04), AL
AL, *(0:0x0c04)
AL, #0xffef
*(0:0x0c04), AL
9 команд, 9 тактов
Короткий комментарий к ассемблерному тексту:
1) содержимое периферийного регистра косвенно (по значению указателя,
заданного непосредственным операндом в команде) считывается в младшее
слово аккумулятора AL. При этом значение указателя автоматически расширяется нулями от слова к длинному слову для доступа ко всей расширенной
памяти микроконтроллера. Выполняется операция «Логического ИЛИ»
содержимого AL с маской. Полученный результат записывается косвенно в
память данных по тому же указателю (в регистр ПУ);
2) адрес регистра периода загружается в регистр-указатель XAR4 как
непосредственный операнд. Значение для инициализации регистра периода
загружается в регистр XAR5, выполняющий функцию регистра общего
назначения. Содержимое XAR5 косвенно копируется по адресу регистра
периода (базово-индексная адресация с преиндексированием и значением
индекса, равным 0);
3) операции, аналогичные п. 1.
Видно, что традиционный подход доступа к регистрам ПУ (с использованием директивы препроцессора #define) имеет как преимущества, так и
недостатки.
Преимущества традиционного подхода:
• описание периферийных регистров достаточно простое;
• нет необходимости внесения изменений в файлы управления компоновкой;
• имя переменной точно соответствует имени регистра;
• имя переменной короткое.
Недостатки традиционного подхода:
• требует создания индивидуальных масок для манипулирования отдельными разрядами и битовыми полями регистров. Эта задача возлагается на
программиста;
118
• нельзя отображать значения отдельных битовых полей регистров ПУ в
окнах переменных Watch Window среды Code Composer Studio — требуется
специально готовить эти поля для вывода с помощью логических функций.
Затрудняется отладка программ;
• генерируется универсальный, но не очень эффективный код. Как видно
из табл. 5.1, атомарные команды «Чтения—Модификации—Записи» микроконтроллеров ‘C28xx компилятором не используются;
• трудно запоминать и набирать названия регистров.
Второй подход организации доступа к регистрам периферийных устройств (структурный) базируется на следующих основных принципах:
• регистры каждого встроенного периферийного устройства одного и
того же типа (менеджера событий А и В, синхронных периферийных портов А, В, C, D и т.д.) структурированы одинаковым образом самим разработчиком микроконтроллеров и располагаются в памяти данных одинаково, но с
разных начальных адресов. Назовем такую совокупность ячеек памяти данных регистровым файлом периферийного устройства;
• тип регистрового файла для каждого типа периферийного устройства
одинаков и может быть описан в виде унифицированной структуры данных
ПУ данного типа, содержащей как регистры периферийного устройства
целиком, так и отдельные битовые поля или биты этих регистров. Как известно,
в языке Си можно создавать переменные типа «структура» с помощью оператора struct с описанием полей структуры и их типов;
• после того, как унифицированная структура данных регистрового
файла типового периферийного устройства описана, можно создать любое
число экземпляров переменных, имеющих этот тип, например: для двух
менеджеров событий; для двух синхронных периферийных портов; для
одного АЦП. Естественно, что переменные создаются только для тех устройств, которые будут использованы в проекте;
• все обычные глобальные переменные, создаваемые программистом на
языке Си, по умолчанию попадают в секцию .ebss и размещаются компоновщиком в одном из банков встроенного ОЗУ общего назначения. В нашем
случае каждая переменная, описывающая отдельное периферийное устройство, должна быть строго привязана к месту фактического расположения
регистрового файла в памяти данных микроконтроллера. Поэтому перед
созданием таких переменных обязательно объявляется специальная секция
данных для хранения регистрового файла конкретного периферийного устройства. Имя этой секции данных жестко связывается с именем переменной
(структуры) соответствующего регистрового файла. Для этой цели используется специальная директива препроцессора #pragma;
• так как теперь в программе появились нестандартные секции данных,
необходимо указать компоновщику конкретное место их размещения, т.е.
привязать секции данных регистров периферийных устройств к фактическим областям в памяти данных, где они и находятся на самом деле — потребуется модификация файла управления компоновкой .cmd. После этого каждый регистр периферийного устройства получит свой собственный адрес,
119
знать который уже не обязательно. К регистрам можно будет обращаться по
символическим именам.
Кажется, что объем работы, который должен выполнить программист для
реализации структурного подхода доступа к периферии, очень велик. На
самом деле, фирма Texas Instruments поставляет уже готовую библиотеку
файлов для работы с периферией для всех типов выпускаемых микроконтроллеров. Нужно лишь научиться пользоваться этими материалами. В процессе выполнения настоящей работы Вы приобретете необходимые навыки.
Итак, если подготовительная работа выполнена, то обращение к любому
регистру периферийного устройства или битовому полю выполняется как к
обычному элементу переменной типа «структура» (лист. 5.2).
Лист. 5.2. Доступ к регистрам с использованием структурного подхода
void main(void)
{
AdcRegs.ADCTRL1.all = 0x1234;
//Запись регистра целиком
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1;
//Установка 14-ого бита
}
Посмотрим, какой ассемблерный код будет теперь создавать компилятор
(табл. 5.2).
Краткий комментарий к ассемблерному тексту:
1) компилятор автоматически инициализирует указатель текущей страницы памяти данных DP для доступа к регистровому файлу нужного периферийного устройства. Генерируется команда «Логическое ИЛИ» с маской
для установки нужного бита регистра, относящаяся к быстрым атомарным
командам типа «Чтение—Модификация—Запись», выполняемым за один
такт;
Табл и ц а 5.2
Доступ к регистрам ПУ с использованием структурного подхода
Исходный Си-код
// Остановить CPU Timer0
CpuTimer0Regs.TCR.bit.TSS = 1;
MOVW
OR
// Загрузить новое 32-разрядное
// значение периода
CpuTimer0Regs.PRD.all = 0x00010000;
MOVL XAR4, #0x010000
MOVL @2, XAR4
// Запустить CPU Timer0
CpuTimer0Regs.TCR.bit.TSS = 0;
AND
Код легко читается без комментариев.
Битовые маски уже встроены в структуры.
120
Ассемблерный код,
созданный компилятором Си
DP, #0030
@4, #0x0010
@4, #0xffef
5 команд, 5 тактов
2) производится предварительная загрузка регистра XAR4 длинной константой с последующим копированием содержимого регистра XAR4 по
короткому прямому адресу регистра периода;
3) нужный бит регистра управления таймером очищается с помощью атомарной команды «Логического И» с маской.
Как видите, компилятор создает максимально эффективный машинный
код как по объему памяти, так и по времени выполнения.
Преимущества структурного подхода:
• просто работать с индивидуальными разрядами и битовыми полями
исключительно по символическим именам;
• все необходимые маски генерирует сам компилятор;
• в окне Watch Window среды CCS может быть отображено текущее
содержимое как регистра целиком, так и каждого бита или битового поля в
отдельности. Возможна модификация значений битовых полей в процессе
отладки.
Наиболее эффективный генерируемый код — создаваемый с использованием прямой страничной адресации данных и быстрых атомарных инструкций.
Недостатки структурного подхода:
• трудно запомнить имена структур, однако в Code Composer Studio есть
встроенная система помощи Code Maestro, которая по первым буквам имени
выдает необходимые подсказки;
• больше приходится печатать, но можно использовать встроенные
средства авторасширения имен по первым набранным символам (помогает
Code Maestro);
• чтобы эффективно работать, необходимо один раз разобраться с системой использования готовых файлов, поставляемых Texas Instruments.
Более подробно особенности структурного подхода к программированию
периферии на языке Си рассмотрим ниже.
5.2. СТРУКТУРА ЗАГОЛОВОЧНОГО ФАЙЛА
ПЕРИФЕРИЙНОГО УСТРОЙСТВА
Мы уже отмечали, что микроконтроллер TMS320F2812, на котором базируется плата eZdspTM, обладает большим количеством встроенных периферийных устройств: ADC, CAN, SPI, SCI, McBSP и др.
Каждое из них должно иметь свой регистровый файл, описанный в виде
структуры. Описания типовых структур регистровых файлов хранятся в
соответствующих заголовочных файлах. Число заголовочных файлов равно
числу типов периферийных устройств, входящих в состав микроконтроллера.
Откроем в качестве примера проект lab5 и рассмотрим более подробно
его содержимое, исходная программа которого представлена на рис. 5.1. В
состав проекта входит основная программа main.c с двумя подключенными
файлами. Один из них является специальным заголовочным файлом описания периферии ‘C28xx — DSP28_Device.h.
121
Рис. 5.1. Окно исходной программы текущего проекта
Рис. 5.2. Список заголовочных файлов проекта
122
Проанализируем более подробно
состав файлов проекта в окне менеджера проекта. Раскрыв папку Include,
увидим заголовочные файлы, включенные в проект, а раскрыв папку
Source — исходные файлы. По умолчанию предполагается, что мы
можем работать с любыми периферийными устройствами микроконтроллера, поэтому к проекту подключены заголовочные файлы всех
возможных периферийных устройств, приведенные на рис. 5.2.
Обратите внимание на то, что
имя заголовочного файла любого
периферийного устройства состоит
из символического имени процессора DSP28, после которого через
символ подчеркивания указано символическое имя периферийного устройства данного типа, например:
Ev — для менеджера событий;
Sci — для последовательного коммуникационного порта; Spi — для
синхронного периферийного интерфейса; ECan — для расширенного
CAN-контроллера и т.п.
Регистровый файл каждого периферийного устройства описывается в
виде структуры на Си, причем отдельные регистры указываются в структуре
в порядке их фактического расположения в памяти данных, начиная с младшего адреса. Пустые места также описываются с помощью неиспользуемых
резервных переменных. Обычно в качестве типа элементов структуры указывается тип Uint16 (переопределенный тип unsigned int) или тип Uint32
(переопределенный тип unsigned long). В первом случае регистр 16-разрядный, во втором — 32-разрядный.
Если какой-либо регистр периферийного устройства имеет битовые поля,
то он обязательно описывается в виде структуры битовых полей. В примере
в лист. 5.3 такое описание дано для регистра управления АЦП ADCTRL1.
Лист. 5.3. Объявление структуры битового поля
/* Определение структуры битовых полей регистра управления АЦП */
struct ADCTRL1_BITS { // Описание бит
Uint16 rsvd1:4;
// 3:0 Зарезервированы
Uint16 SEQ_CASC:1; // 4 Каскадный режим секвенсора
Uint16 rsvd2:1;
// 5 Зарезервировано
Uint16 CONT_RUN:1; // 6 Циклическое преобразование
Uint16 CPS:1; //7 Делитель тактовой частоты ядра АЦП
Uint16 ACQ_PS:4; // 11:8 Время выборки
Uint16 SUSMOD:2; // 13:12 Режим работы с эмулятором
Uint16 RESET:1;
// 14 Перезапуск АЦП
Uint16 rsvd3:1;
// 15 Зарезервировано
};
/*Uint16 — новый тип, который определен как unsigned
int в заголовочном файле */
/* Определение регистра управления в виде объединения
двух полей all и bit для доступа к регистру целиком и
каждому битовому полю */
union ADCTRL1_REG {
Uint16all;
StructADCTRL1_BITSbit;
};
// Создание переменной AdcRegs типа структура
// регистрового файла struct ADC_REGS
extern volatile struct ADC_REGS AdcRegs;
Структура битовых полей имеет имя ADCTRL1_BITS и содержит описание всех битовых полей регистра в последовательности:
тип данных, размещаемых в битовом поле (обычно Uint16);
имя битового поля, точно совпадающее с именем, используемым в технической документации на периферию микроконтроллера;
символ двоеточия, после которого указано число битов в поле.
123
Видно, что младшие 4 бита регистра зарезервированы под будущие применения, следующий бит имеет имя SEQ_CASC и управляет режимом каскадирования секвенсора АЦП.
Описание каждого члена структуры заканчивается точкой с запятой. Список всех элементов структуры (всех битовых полей) заключается в фигурные
скобки { }. Для удобства программиста в поле комментария указываются
номера битов в периферийном регистре, соответствующие данному битовому полю, начиная с младшего бита, а также назначение битового поля.
Для того чтобы к регистру периферийного устройства можно было обратиться двумя способами: как ко всему 16-разрядному слову, так и к каждому
битовому полю регистра в отдельности, — используется описание регистра
в виде объединения (union), представляющего собой частный случай структуры, все поля которой располагаются по одному и тому же адресу. Объединение позволяет фактически наложить друг на друга несколько объектов.
В нашем примере объединение имеет имя регистра управления АЦП
ADCTRL1_REG и содержит два элемента: под именем all типа Uint16 и под
именем bit типа описанной выше структуры битовых полей Struct
ADCTRL1_BITS.
Такой прием позволяет по имени регистра, расширенному после точки (.)
(операция выбора элемента структуры на Си) словом all, обращаться к
регистру целиком, а словом bit — к любому битовому полю (лист. 5.4).
Лист. 5.4. Доступ к регистрам через структуру
#include “DSP281x_Device.h”
void InitAdc(void)
{
/* Перезапуск модуля АЦП (обращение к отдельному битовому полю регистра)*/
AdcRegs.ADCTRL1.bit.RESET = 1;
/* Задание режима работы АЦП (обращение целиком ко
всему регистру управления)*/
AdcRegs.ADCTRL1.all = 0x0710;
};
Рассмотренная технология описания регистровых файлов позволяет унифицировать обращения к регистрам на Си (лист. 5.5).
Лист. 5.5. Правила наименования структур
PeripheralName.RegisterName.all // Доступ к 16 или
// 32-разрядному регистру целиком
PeripheralName.RegisterName.bit.FieldName // Доступ к
//определенному битовому полю
124
Имена
всех
регистровых
файлов
периферийных
устройств
PeripheralName определены Texas Instruments и могут быть найдены в заголовочных файлах DSP28xx. Они представляют собой комбинацию заглавных
и строчных букв, например CpuTimer0Regs для регистрового файла Таймера 0 центрального процессора. Имена отдельных регистров периферийного устройства RegisterName такие же, как в описании на периферию микроконтроллера, всегда набранные заглавными буквами, например TPR
(Регистр периода таймера). Имена отдельных битовых полей периферийных
регистров FieldName тоже полностью соответствуют документации на микроконтроллер и набираются заглавными буквами, например RESET (Сброс
АЦП).
5.3. РАЗМЕЩЕНИЕ СТРУКТУР
РЕГИСТРОВЫХ ФАЙЛОВ В ПАМЯТИ ДАННЫХ
Вернитесь в конец лист. 5.3. Здесь создана глобальная переменная по
имени AdcRegs, которая имеет тип структуры регистрового файла АЦП
struct ADC_REGS. Мы уже говорили о том, что такая переменная должна
обязательно попасть в специальную секцию данных, предназначенную
исключительно для хранения этого конкретного регистрового файла. Поэтому перед созданием такой переменной должна быть выполнена директива,
которая устанавливает связь между именем переменной (в нашем случае
AdcRegs) и именем предназначенной для ее размещения секции данных (в
нашем случае AdcRegsFile). Для этой цели применяется специальная директива препроцессора #pragma DATA_SECTION, приведенная в лист. 5.6. Эту
директиву можно найти в файле описания глобальных переменных
DSP281x_GlobalVariableDefs.c.
Лист. 5.6. Объявление новой секции
#pragma DATA_SECTION(AdcRegs,"AdcRegsFile");
Смысл указанной директивы компилятору следующий: когда встретишь
переменную по имени AdcRegs, размести ее в специальной секции данных
DATA_SECTION по имени AdcRegsFile.
Теперь осталось определить область памяти микроконтроллера, в которой
фактически находятся регистры АЦП. Для этого в командном файле управления компоновкой с помощью директивы MEMORY нужно задать начальный адрес банка регистров АЦП и его длину (см. гл. 1). Назовем этот банк
ADC и из технической документации на микроконтроллер определим
начальный адрес банка 0x007100 и его длину 0x20. Разместим банк ADC в
памяти данных PAGE 1, пример которой представлен в лист. 5.7.
Естественно, необходимо дать указания компоновщику, в какой банк
памяти размещать нашу нестандартную секцию данных по имени
AdcRegsFile. Эта операция выполняется с помощью уже знакомой Вам
директивы SECTIONS:
125
Лист. 5.7. Пример инициализации памяти
MEMORY {
PAGE 1:
...
ADC:origin=0x007100, length=0x000020
... }
SECTIONS {...
AdcRegsFile:>ADCPAGE=1
... }
5.4. ФАЙЛ ОПРЕДЕЛЕНИЯ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ
Подключение заголовочных файлов осуществляется с помощью специальной директивы препроцессора #include. Общим файлом, содержащим
ссылки на файлы с описаниями структур регистров отдельных периферийных устройств, является файл DSP28_Device.h. Он подключается к программе, как показано в лист. 5.8 (см. также рис. 5.1):
Лист. 5.8. Подключение заголовочного файла
#include “DSP28_ Device.h”
Помимо подключения всех файлов с описаниями периферии, этот файл
содержит определения дополнительных типов, масок прерываний и макросов. Например, в начале файла определены значения символических имен
TRUE (Истина) и FALSE (Ложь). Определены некоторые макросы, которые
по существу представляют собой ассемблерные команды. Так, макровызов
EINT будет автоматически заменяться ассемблерной вставкой asm (clrc
INTM) — очистить флаг INTM процессора, т.е. разрешить прерывания.
Файл DSP28_Device.h является главным подключаемым заголовочным
файлом. Если включить ссылку на этот файл в программе, то все.h-файлы,
описывающие периферийные устройства, будут добавлены автоматически.
Практическая работа
1. Найдите и исследуйте главный подключаемый заголовочный файл.
2. Откройте файл с описанием регистров АЦП. Сколько всего регистров
содержит этот файл, каково их назначение? Есть ли среди регистров АЦП
регистр результата аналого-цифрового преобразования? Как производится
доступ к этому регистру? Возможен ли побитовый доступ? Почему?
3. На примере регистрового файла АЦП проследите всю последовательность объявления структур — от структур битовых полей регистров до
структуры регистрового файла АЦП целиком. Где создается конкретный
экземпляр этой структуры?
4. Найдите файл, в котором объявляется специальная секция данных для
АЦП.
126
5.5. ПРОГРАММИРОВАНИЕ ПЕРИФЕРИИ ‘C28xx
С ИСПОЛЬЗОВАНИЕМ ЗАГОЛОВОЧНЫХ ФАЙЛОВ
Устройство дискретных портов ввода/вывода
Битовые порты дискретного ввода/вывода микроконтроллера объединены
в группы по 16 линий и образуют 16-разрядные регистры ввода/вывода
общего назначения GPIOx, которые обозначаются буквами A, B, C, D, E, F, G, …,
соответственно. Число таких регистров зависит от типа микроконтроллера,
например в ‘C2810/12 их всего 6 (A, B, D, E, F, G). Регистр С зарезервирован.
Каждому биту регистра ввода/вывода с номером от 0 до 15 соответствует отдельный вывод микроконтроллера, например GPIOA3.
Большинство выводов микроконтроллера мультиплицированы и могут
работать в одном из двух режимов: 1) в качестве битового порта
ввода/вывода общего назначения; 2) в режиме выполнения специальной функции по обслуживанию одного из встроенных периферийных устройств.
Например, упомянутый вывод GPIOA3 может быть выводом многоканального ШИМ-генератора PWM4.
Упрощенная схема организации битового порта ввода/вывода в микроконтроллерах ‘C28xx представлена на рис. 5.3. Основные принципы его работы
приведены ниже:
• при сбросе процессора XRS=0, в том числе при включении питания
вывод микроконтроллера автоматически переводится в высокоимпедансное
состояние (Z-состояние) с большим входным сопротивлением. Это состояние соответствует настройке порта на ввод данных по умолчанию;
• вывод микроконтроллера можно программно сконфигурировать для
обслуживания встроенного периферийного устройства (правая часть рис. 5.3)
или для дискретного ввода/вывода информации общего назначения (левая
часть рис. 5.3). Конфигурирование выполняется с помощью одного из регистров управления мультиплексорами портов ввода/вывода GPxMUX, где x —
символическое имя порта (A, B, …). Запись в регистр мультиплексора 1
обеспечивает «периферийный ввод/вывод», а запись 0 — «ввод/вывод
общего назначения». Например, чтобы вывод микроконтроллера GPIOA3
стал работать в качестве порта ввода/вывода общего назначения в бит 3
регистра управления мультиплексорами порта А GPAMUX3 необходимо
записать 0;
• если вывод сконфигурирован для работы с периферийным устройством, оно будет автоматически управлять его состоянием. В противном случае программист должен задать либо режим ввода данных, либо режим
вывода. Эта операция выполняется посредством установки (для порта
вывода) или сброса (для порта ввода) соответствующего бита в регистре
управления направлением передачи данных GPxDIR, где x — имя порта;
• для качественной защиты дискретных входов от помех используется
специальный цифровой фильтр, принцип работы которого иллюстрирует
рис. 5.4. Состояние входа Input to Qual опрашивается периодически на
127
Бит (ы) регистров
GPxDAT/SET/CLEAR/TOGGLE
Цифровой
ввод/вывод
Бит
регистра
GPxDIR
Бит
регистра
GPxMUX
Регистр
GPxQUAL
0
Периферийный ввод/вывод
1
Управление
Z-состоянием
0
MUX
1
MUX
Входной
фильтр
SYSCLKOUT
Разрешение
Z-состояния
1
XRS
Внутренняя
привязка
к логическому
0 или 1
Ножка
процессора
Рис. 5.3. Устройство битового порта ввода/вывода общего назначения
Input
to Qual
1
1
0
0
0
0
0
Sampling Window
0
0
1
0
0
0
1
1
1
1
1
1
1
1
1
QUALPRD
Input
from Qual
Рис. 5.4. Принцип цифровой фильтрации входных дискретных сигналов
заданной пользователем частоте — частоте квалификации (оценки) входного
сигнала. Выходной сигнал квалификатора Output from Qual устанавливается только по шести одинаковым входным выборкам подряд, в противном случае он не меняется (девиация сигнала рассматривается как помеха).
Регистры управления «квалификаторами» GPxQUAL входных сигналов позволяют для всех 16 линий порта x одновременно задать нужный период
выборки QUALPRD входного сигнала — от 1 до 510 тактов ЦПУ.
128
Для каждого из портов A, B, … предусмотрены еще несколько регистров,
обеспечивающих удобный ввод или вывод данных:
GPxDAT — регистры данных для считывания входной информации из
порта после цифровой фильтрации, а также для вывода данных в те битовые
порты, которые сконфигурированы на вывод;
GPxSET — регистры установки. Запись 1 устанавливает высокий потенциал на выходе соответствующего порта. Работают только на запись;
GPxCLEAR — регистры сброса. Запись 1 устанавливает низкий потенциал на выходе соответствующего. Работают только на запись;
GPxTOGGLE — регистры переключения состояния. Запись 1 переключает потенциал на выходе соответствующего порта на противоположный.
Работают только на запись.
Практическая работа
1. Найдите в проекте и исследуйте подключаемый заголовочный файл,
который описывает структуру регистрового файла, обслуживающего дискретные порты ввода/вывода.
2. Далее будем учиться управлять светодиодами, установленными на оценочной плате. Вернитесь к гл. 1 и определите, как подключены светодиоды?
К каким конкретно выводам микроконтроллера?
3. Убедитесь, что 14-й бит порта F GPIOF14 описан как предназначенный для реализации специальной функции — прямого управления битовым
портом по состоянию флага XF процессора, а также может работать в качестве порта ввода/вывода общего назначения.
Управление светодиодным индикатором
На отладочной плате eZdspTM28 установлены два светодиода. Один из
них через ограничивающее ток сопротивление подключен к шине питания и
сигнализирует о наличии питания (программно не управляемый). Другой
светодиод через буферный усилитель подключен к битовому выходу XF микроконтроллера (порт GPIOF14).
Управлять светодиодом можно двумя способами:
1) сконфигурировать выход GPIOF14 как порт вывода общего назначения. Настроить регистр направления передачи данных на вывод — записать
«1» в 14-й разряд регистра GPFDIR. Установить 14-й разряд регистра
GPFDAT в «1», что соответствует включению светодиода, или в «0», что
соответствует его выключению;
2) сконфигурировать выход как XF (спецфункция). Установить статусный
бит XF в «1», что соответствует включению светодиода, или сбросить XF в
«0», что соответствует выключению светодиода. Статусный бит XF устанавливается ассемблерной командой SETC XF, а сбрасывается командой
CLRC XF.
129
Практическая работа
1. Откройте файл main.c и изучите его структуру. Программа будет
написана внутри бесконечного цикла while(1) в функции main. В окне
исходной программы на языке Си введите строку Gpio и нажмите клавишу Tab. Появится подсказка доступных объектов, как показано на
рис. 5.5, а.
2. Выберите GpioMuxRegs и поставьте точку. Появится список доступных регистров (см. рис. 5.5, б). Нас интересует регистр порта F —
GPFMUX. Нажмите еще раз точку, и Вам будет предложен список полей
регистра — bit и all. Если выберете all, то можно будет задать значение
регистра целиком, если — bit, то будет предоставлен выбор битовых полей
этого регистра. Выберите bit.XF_GPIOF14 и приравняйте его значение к
«1» — управление будет осуществляться по флагу XF (задана специальная
функция). Таким образом можно работать со всей периферией, не помня
названий регистров. Использование технологии CODE MAESTRO значительно упростит работу.
Важнейшие регистры процессора, изменение состояния которых возможно только при инициализации, а случайная запись в них должна быть
полностью исключена, являются защищенными от непреднамеренной
записи. К таким регистрам относятся и регистры мультиплексоров портов
ввода/вывода. Для того чтобы снять защиту от записи, используется макрос
EALLOW, который расширяется одноименной ассемблерной командой, а
для того что поставить ее после выполнения процедуры инициализации —
макрос EDIS (лист. 5.9).
а)
б)
Рис. 5.5. Code Maestro помогает набирать программу
130
Лист. 5.9. Пример программы управления светодиодом
// бесконечный цикл программы
while(1)
{
// введите ваш код здесь
l_counter++;
//разрешаем запись в регистр
EALLOW;
//используем вывод XF как связанный с периферией
GpioMuxRegs.GPFMUX.bit.XF_GPIOF14 = 1;
// запрещаем запись в регистр
EDIS;
// вставка асемблерной команды — установить xf
asm(" setc xf");
// вставка асемблерной команды — сбросить xf
asm(" clrc xf");
}
Как делать ассемблерные вставки в программе на языке Си, мы уже рассматривали. Для этой цели используется зарезервированное слово asm,
после которого в круглых скобках и двойных кавычках можно указывать
любую ассемблерную команду. В примере, приведенном в лист. 5.9, применяли команду установить флаг XF и вслед за ней команду сбросить флаг XF.
Конечно, при выполнении программы в реальном времени переключения
будут производиться с очень высокой скоростью. Однако если будем выполнять программу по шагам, то увидим изменения состояния светодиодного
индикатора.
Практическая работа
1. Откомпилируйте программу и загрузите ее в микроконтроллер. Выполните команду Debug Go Main для перехода в начало функции main. Выполняйте программу по шагам, используя команду Step Into (клавиша F8).
Наблюдайте за состоянием светодиода.
2. Вы уже заметили, что процедура инициализации порта ввода/вывода
оказалась внутри цикла while и выполняется многократно. Это не ошибка,
но «плохой стиль программирования» — инициализация должна выполняться только один раз. Модернизируйте программу — выполните инициализацию порта до начала цикла. Исследуйте работу программы. Убедитесь,
что ничего не изменилось.
3. Остановите программу. Откройте окно статусных регистров процессора и меняйте состояние флага XF в этом окне. Объясните, почему при
неработающей программе состояние выхода порта изменяется?
4. Теперь более сложное задание. Настройте линию микроконтроллера
GPIOF14 в режим порта вывода общего назначения с помощью регистра
131
управления мультиплексором GPFMUX. Задайте направление передачи данных с помощью регистра GPFDIR. Попробуйте управлять выходом XF через
регистры: GPFDAT; GPFSET, GPFCLEAR; GPFTOGGLE. Составьте три
различные программы и выполните их отладку.
Контрольные вопросы
1. Как установить скважность включения светодиода в вашей программе 25 и 12,5 %?
2. Можно ли регулировать яркость свечения светодиода? Напишите программу, которая меняла бы значение яркости горения светодиода в диапазоне от 0 до 100 % (задается
отдельной переменной) по усмотрению пользователя.
5.6. ОСНОВЫ РАБОТЫ С ТАЙМЕРАМИ ОБЩЕГО НАЗНАЧЕНИЯ
Менеджер событий является одним из важнейших периферийных устройств микроконтроллера и обеспечивает целый ряд функций, которые
используются в системах цифрового управления двигателями и системами
вторичного питания: от прямого управления отдельным силовым ключом в
режиме широтно-импульсной модуляции (ШИМ) до согласованного управления всеми ключами инвертора или активного выпрямителя. В рамках данной главы рассмотрим основы управления таймерами общего назначения
GP Timer, входящими в состав менеджеров событий. Таймеры 1 и 2 входят в
менеджер событий EVA, а таймеры 3 и 4 — в менеджер событий EVB. Таймеры могут работать в нескольких режимах, в том числе: модуля сравнения
процессора событий и ШИМ-генератора.
Они могут программироваться для выполнения операций с тактированием как от внутреннего сигнала тактирования высокоскоростных периферийных устройств (HSPCLK), так и от внешнего сигнала. В последнем случае таймер может выполнять также функцию счетчика внешних событий.
С каждым таймером связаны несколько регистров: период таймера —
TxPR; счетчик таймера — TxCNT; сравнение — TxCMPR; управление
режимами работы — TxCON, где x — номер таймера. Для того чтобы включить таймер, необходимо произвести следующий минимум операций с его
регистрами:
установить режим счета таймера: вверх (нереверсивный или суммирующий счетчик); вверх/вниз (реверсивный счетчик);
установить значение периода таймера в регистре TxPR;
установить флаги, разрешающие таймеру работать независимо от режима
работы процессора и режима эмуляции (в автономном режиме);
установить флаг разрешения работы таймера.
Приведенный перечень операций касается работы только счетчика таймера. Для выполнения на базе таймера функции сравнения или генерации
ШИМ-сигналов потребуются дополнительные действия. Наиболее часто,
например, для генерации центрированных ШИМ-сигналов, используется
режим счета таймера вверх/вниз, когда состояние счетчика таймера увеличивается на 1 с каждым импульсом входной тактовой частоты, пока не достиг132
нет заданного значения периода таймера. Затем направление счета автоматически меняется на обратное. При достижении счетчиком таймера нулевого
состояния период счета заканчивается и направление счета вновь меняется
на противоположное (рис. 5.6).
Видно, что таймер запускается программно с помощью установки бита
разрешения работы в регистре управления TxCON.TENABLE и меняет
свое состояние по переднему фронту каждого импульса тактирования
HSPCLK. Период таймера в режиме реверсивного счета пропорционален
двукратному значению в регистре периода:
TTIMER = 2æTxPRæTHSPCLK,
(5.1)
где THSPCLK — период внутреннего сигнала тактирования высокоскоростных периферийных устройств.
Важнейшим преимуществом структурного подхода к описанию регистров
периферийных устройств является возможность отображения в окне наблюдаемых переменных текущего содержимого регистров и отдельных битовых
полей, а также возможность изменения состояния регистров и битовых
полей непосредственно в окне наблюдаемых переменных. Таким образом,
находясь в среде CCS, можно последовательно проинициализировать и
включить какое-либо периферийное устройство, а затем наблюдать за его
работой, фиксируя изменение состояния периферийных регистров.
Рассмотрим последовательность действий, которую необходимо выполнить для включения таймера 1. Прежде всего нужно разрешить тактирование первого менеджера событий EVA от блока тактирования высокоскоростных периферийных устройств микроконтроллера HSPCLK. Для этого
можно:
отобразить в окне Watch Window (рис. 5.7) содержимое структуры
SysCtrlRegs — регистров управления системой;
выбрать регистр управления тактированием периферийных устройств
PCLKCR;
открыть битовые поля этого регистра;
Период счета таймера
TxPR = 4
4
3
2
1
TxCNT
0
3
3
2
2
1
1
0
TxCON.TENABLE
HSPCLK
Рис. 5.6. Таймер общего назначения в режиме счета вверх/вниз
133
Рис. 5.7. Включение тактирования первого менеджера событий
установить бит разрешения тактирования менеджера событий EVA —
EVAENCLK. Тем самым на первый менеджер событий, в том числе и на
таймеры, входящие в его состав, будет подана высокая тактовая частота.
Обратите внимание, что аналогичным образом можно включить тактирование любого другого периферийного устройства: АЦП, последовательных
периферийных и коммуникационных портов и т.п. Конкретное значение тактовой частоты HSCLK будет зависеть от коэффициента деления системной
тактовой частоты SYSCLK, установленного в регистре управления делителем HISPCP. По умолчанию, после включения процессора или сброса питания устанавливается коэффициент деления, равный 2.
С менеджером событий EVA связана структура периферийных регистров
EvaRegs, приведенная на рис. 5.8.
Содержимое регистра T1CNT отображает текущее состояние счетчика
таймера 1. В регистр T1PR должно быть записано значение требуемого
периода таймера (5.1). Регистр управления таймером T1CON имеет
несколько битовых полей, в частности поле TMODE, задающее один из
четырех возможных режимов работы таймера:
00
Останов и фиксация состояния таймера
01
Непрерывный счет вверх/вниз (реверсивный счетчик)
10
Непрерывный счет вверх (нереверсивный счетчик)
11
Направленный счет с внешним заданием направления счета
134
Рис. 5.8. Задание режима работы таймера 1
Битовые флаги FREE и SOFT регистра T1CON определяют режим
работы таймера в процессе отладки с использованием внутрисхемного эмулятора (когда процесс выполнения программы прерывается, например, для
просмотра текущего содержимого регистров):
00
Немедленный останов таймера
01
Останов таймера сразу после завершения текущего периода
10
Продолжение работы таймера (автономный режим)
11
Продолжение работы таймера (автономный режим)
Битовый флаг TENABLE разрешает работу таймера. Его можно рассматривать как флаг программного запуска таймера. Разумеется, для правильного
программирования любого периферийного устройства необходимо предварительно изучить его устройство — понять назначение каждого регистра и
каждого битового поля. Так, в нашем случае битовое поле TPS отвечает за
коэффициент предварительного деления тактовой частоты, поступающей
на вход таймера. При значении 000 коэффициент деления равен 1, а при значении 111 — 128. При каждом инкрементировании кода в этом поле коэффициент деления удваивается.
Поле TCLKS10 регистра T1CON определяет источник тактовых импульсов:
00 — блок тактирования высокоскоростных устройств HSPCLK; 01 — внешний сигнал тактирования, поступающий на вход TCLKIN; 10 — резерв;
11 — сигнал тактирования с модуля «квадратурного декодирования» QEP.
135
Остальные битовые поля служат для управления таймером в режиме
канала сравнения и генерации ШИМ-сигналов (в этой работе не используются).
5.7. ГРАФИЧЕСКИЕ ОКНА
Среда Code Composer Studio позволяет не только наблюдать текущие значения переменных в окнах наблюдаемых переменных, но и выводить на
экран различные типы графиков, например, в функции времени. Для этой
цели предназначена команда меню View → Graph Time/Frequency (Просмотр → График → Время/Частота). При выполнении этой команды CCS
выведет на экран диалоговое окно графика, в котором Вы должны сделать
необходимые настройки, — рис. 5.9. Важнейшей из них является стартовый
адрес переменной Start Address. Он задается так же, как и в окнах дампов
памяти, — с помощью операции получения адреса «&» от символического
имени переменной. На рис. 5.8 показано, как вывести на экран график текущего содержимого счетчика таймера 1.
Как уже знаем, программу можно выполнить в режиме прогона, в пошаговом режиме или в режиме с точками останова. Имеется еще один очень важный режим, заметно облегчающий отладку, — режим Animate (Анимация).
Рис. 5.9. Окно настройки графика
136
Его можно запустить с помощью кнопки
. В этом режиме программа
будет выполняться в режиме прогона до ближайшей точки останова. В
момент останова значения всех наблюдаемых переменных (в том числе в
графических окнах) будут считаны из микроконтроллера в компьютер и
экран среды CCS обновится. После этого выполнение программы автоматически продолжится в режиме прогона до новой точки останова. Таким образом,
обновление информации производится от одной точки останова к другой.
Практическая работа
1. Сделайте необходимые установки для инициализации Таймера 1 и для
его программного запуска. Установите значение в регистре периода 50 000 и
максимальный коэффициент деления входной тактовой частоты. Рассчитайте значение периода таймера в секундах.
2. Наблюдайте за изменением состояния таймера в окне Watch. Соответствует ли период таймера расчетному периоду?
3. Установите точку останова в программе main с бесконечным циклом и
запустите ее на исполнение с помощью кнопки Animate (Анимация).
Откройте графическое окно для отображения текущего состояния счетчика
таймера в функции времени. Поэкспериментируйте с настройками.
4. Перенастройте таймер 1 для работы с тем же периодом, но в режиме
нереверсивного счета. Объясните полученную осциллограмму.
5.8. ВВЕДЕНИЕ В СИСТЕМУ ПРЕРЫВАНИЙ
МИКРОКОНТРОЛЛЕРОВ ‘C28xx
Встроенный контроллер прерываний
Микроконтроллеры оптимизированы для работы с большим числом как
внешних, так и внутренних прерываний, поступающих от встроенных периферийных устройств. На уровне контроллера прерываний центрального
процессора поддерживаются один немаскируемый запрос прерывания NMI
и 16 маскируемых запросов прерывания (INT1—INT14, RTOSINT и
DLOGINT). Двенадцать линий запросов прерываний центрального процессора INT1 — INT12 предназначены для приема запросов прерываний от
встроенных периферийных устройств (рис. 5.10). Если запрос прерывания
поступает по одной из этих линий, то соответствующий флаг в регистре прерываний, ждущих обслуживания IFR, устанавливается. Если обработка
соответствующего прерывания центрального процессора разрешена в регистре разрешения прерываний IER, а также разрешена обработка всех маскируемых прерываний (флаг INTM сброшен), то запрос прерывания начинает
обрабатываться.
Все периферийные и внешние запросы прерываний объединяются в
группы по восемь запросов с помощью блока расширения периферийных
прерываний (PIE — Peripheral Interrupt Expansion) и мультиплицируются
137
IFR(12:1)
IER(12:1)
INTM
INT1
INT2
1
CPU
MUX
0
INT11
INT12
(Флаг)
Глобальное
разрешение
(Разрешение)
INTx.1
INTx.2
INTx.3
INTx
INTx.4
MUX
INTx.5
Периферийные или
внешние прерывания
INTx.6
INTx.7
INTx.8
(Разрешение)
(Флаг)
PIEIER(8:1)
PIEIFR(8:1)
Рис. 5.10. Мультиплицирование прерываний блоком PIE
на одну из 12 линий запросов прерываний центрального процессора. Таким
образом, общее число запросов, обрабатываемых контроллером прерываний,
достигает девяносто шести.
Каждое периферийное прерывание выставляет флаг ждущего прерывания
PIEIFRx.y по определенному событию в этом периферийном устройстве,
например, по завершению периода таймера. Если соответствующее периферийное прерывание разрешено битом PIEIERx.y, то запрос прерывания поступает на одну из линий INTx.
В табл. 5.3 показано распределение запросов прерываний от различных
устройств по группам. Например, все четыре запроса прерывания от таймера 1
входят в одну группу и мультиплицируются на линию INT2 запроса прерывания центрального процессора. Запрос прерывания, имеющий меньший
номер, имеет более высокий приоритет обслуживания. Так, любое прерывание в группе INT1 имеет более высокий приоритет по сравнению с любым
прерыванием в других группах, а прерывание PDPINTA — наивысший приоритет внутри группы INT1.
Уникальной особенностью контроллера прерываний ‘C28xx является возможность задания одной таблицы векторов прерываний (PIE vector table)
для всех возможных периферийных прерываний. У каждого из 96 прерываний имеется свой собственный 32-разрядный вектор прерывания (начальный адрес размещения процедуры обслуживания прерывания — interrupt
service routine ISR), который может модифицироваться пользователем, так
как находится в специально выделенной области ОЗУ (см. гл. 1). Эта операция называется инициализацией таблицы векторов прерываний. Допуска138
ется динамическое переконфигурирование таблицы векторов прерываний в
процессе работы программы.
Контроллер прерываний идентифицирует запрос периферийного прерывания и автоматически передает управление по соответствующему вектору —
адресу процедуры обслуживания прерывания (выполняется косвенная передача управления в процедуру). При этом в стеке автоматически сохраняется контекст (содержимое важнейших регистров процессора). На операцию перехода и сохранения контекста тратится всего девять циклов ЦП.
Никакой дополнительной программной идентификации источника запроса
прерывания не требуется. При выходе из процедуры обслуживания прерывания контекст автоматически восстанавливается. Таким образом, одной из
важнейших характеристик контроллера прерываний ‘C28xx являются его
предельное быстродействие и встроенная автоматизация входа и выхода из
подпрограмм обслуживания прерывания.
Табл и ц а 5.3
Таблица прерываний
Прерывания
процессора
Прерывания блока расширения периферийных прерываний
INTx.1
INTx.2
INTx.3
INTx.4
INTx.5
INTx.6
INTx.7
INTx.8
INT1
PDPINTA
(EV-A)
PDPINTB
(EV-B)
Резерв
XINT1
XINT2
ADCINT
(ADC)
TINT0
WAKEINT
(TIMER0) (LPM/WD)
INT2
CMP1INT
(EV-A)
CMP2INT
(EV-A)
CMP3INT
(EV-A)
T1PINT
(EV-A)
T1CINT
(EV-A)
T1UFINT
(EV-A)
T1OFINT Резерв
(EV-A)
INT3
T2PINT
(EV-A)
T2CINT
(EV-A)
T2UFINT
(EV-A)
T2OFINT
(EV-A)
CAPINT1
(EV-A)
CAPINT2
(EV-A)
CAPINT3 Резерв
(EV-A)
INT4
CMP4INT
(EV-B)
CMP5INT
(EV-B)
CMP6INT
(EV-B)
T3PINT
(EV-B)
T3CINT
(EV-B)
T3UFINT
(EV-B)
T3OFINT Резерв
(EV-B)
INT5
T4PINT
(EV-B)
T4CINT
(EV-B)
T4UFINT
(EV-B)
T4OFINT
(EV-B)
CAPINT4
(EV-B)
CAPINT5
(EV-B)
CAPINT6 Резерв
(EV-B)
INT6
SPIRXINTA
(SPI)
SPITXINTA Резерв
(SPI)
Резерв
MRINT
(McBSP)
MXINT
(McBSP)
Резерв
Резерв
INT7
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
INT8
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
INT9
SCIRXINTA SCITXINTA SCIRXINTB SCITXINTB ECAN0INT ECAN1INT Резерв
(SCI-A)
(SCI-A)
(SCI-B)
(SCI-B)
(CAN)
(CAN)
Резерв
INT10 Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
INT11 Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
INT12 Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
Резерв
139
Порядок обслуживания прерываний
1. Если какое-либо событие в периферийном устройстве генерирует
запрос прерывания, то в одном из регистров этого устройства выставляется
флаг запроса прерывания interrupt flag (IF). Это прерывание может быть
разрешено или запрещено непосредственно внутри периферийного устройства с помощью соответствующего бита разрешения прерывания IE. Для
каждого запроса прерывания (каждого события) существуют свои флаги IF и
IE. Например, для таймера можно установить разрешение генерации
запроса прерывания по периоду и запретить генерацию запроса прерывания
по переполнению вверх и вниз. Если событие произошло и генерация
запроса прерывания разрешена, то периферийное устройство выставляет
запрос прерывания в контроллер периферийных прерываний PIE (см.
рис. 5.10).
2. Если прерывание запрещено на уровне периферийного устройства, то
флаг запроса прерывания остается установленным до тех пор, пока не будет
очищен программным путем. Если после того, как запрос прерывания в
периферийном устройстве возник (флаг IF установился), разрешить это прерывание, то будет сформирован запрос прерывания в контроллер периферийных прерываний PIE.
3. Флаг запроса прерывания IF, выставленный в регистре соответствующего периферийного устройства, должен быть очищен вручную (программным путем) в процедуре обслуживания прерывания.
4. Для каждого источника мультиплицированного прерывания в каждой
из 12 групп имеется соответствующий флаг ждущего запроса прерывания
(PIEIFRx.y) и флаг разрешения этого периферийного прерывания
(PIEIERx.y). Дополнительно для каждой группы периферийных прерываний (INT1 — INT12) имеется свой собственный флаг разрешения группы
прерываний PIEACKx. Активное значение этого флага «логический 0». В
конце процедуры обслуживания периферийного прерывания флаг PIEACKx
должен программно очищаться, чтобы разрешить последующую обработку
прерываний группы.
5. Если на вход контроллера периферийных прерываний поступает запрос
прерывания, то соответствующий флаг «ждущего» запроса прерывания
(PIEIFRx.y) устанавливается. Если соответствующее периферийное прерывание разрешено и бит (PIEIERx.y) установлен, то производится тестирование бита разрешения прерываний для всей группы периферийных прерываний PIEACKx. Активное значение этого флага низкое. При разрешении
прерываний от группы PIEACKx = 0 контроллер периферийных прерываний
посылает запрос прерывания центральному процессору по линии INTx.
Если PIEACKx = 1 (нет разрешения группы), то контроллер прерываний
будет ждать до тех пор, пока программа не сбросит этот флаг, после чего
сформирует запрос прерывания ЦПУ.
6. Если одновременно несколько периферийных прерываний в группе
ждут обслуживания и для каждого из них установлены флаги «ждущего»
прерывания PIEIFRx.y и разрешения прерывания PIEIERx.y, то будет
140
обслуживаться прерывание с наивысшим приоритетом, т.е. имеющее меньший номер в табл. 5.3. Так, для 1-й группы наивысшим приоритетом обладает прерывание PDPINTA (внешнее по аварии в силовых ключах). Поэтому
если одновременно с ним ждет обслуживания прерывание от АЦП
(ADCINT), то будет обслуживаться прерывание PDPINTA как имеющее
более высокий приоритет.
7. Как только запрос прерывания выставляется на одну из линий запроса
прерывания центрального процессора INTx, то разрешение прерываний от
группы периферийных прерываний, выставившей запрос, аппаратно снимается: PIEACKx = 1. Прием запросов прерываний от этой группы блокируется.
8. Если запрос прерывания поступает на одну из линий запроса прерываний центрального процессора INTx, то выставляется соответствующий флаг
«ждущего» запроса прерывания IFR. Это прерывание будет обрабатываться
только в том случае, когда прерывание по соответствующей линии INTx разрешено в регистре разрешения прерываний центрального процессора IER, а
также при условии, что флаг глобального маскирования прерываний сброшен INTM = 0.
9. Перед обработкой прерывания автоматически выполняются следующие действия:
• очищаются биты IFRx (флаг ждущего глобального прерывания) и IERx
(флаг разрешения глобального прерывания);
• устанавливается флаг INTM (маскирование всеx прерываний до
момента завершения обслуживания текущего прерывания);
• очищается флаг EALLOW (разрешается доступ по записи к защищенным регистрам);
• «смывается» конвейер команд;
• адрес возврата в фоновую программу сохраняется в стеке;
• выполняется автоматическое сохранение контекста в стеке.
10. Автоматически извлекается вектор нужной процедуры обслуживания прерывания из таблицы векторов прерываний на основе информации из
контроллера периферийных прерываний о запросе, вызвавшем прерывание
(PIEIERx и PIEIFRx). Флаг PIEIFRx автоматически аппаратно очищается.
Выполняется косвенный переход на начало процедуры обслуживания прерывания.
11. Выполняются все необходимые действия по обслуживанию прерывания.
12. Перед выходом из процедуры обслуживания прерывания:
• программно очищается флаг ждущего прерывания IF в соответствующем регистре периферийного устройства (см. п. 1);
• разрешаются периферийные прерывания от группы, вызвавшей прерывание, с помощью программной очистки флага PIEACKx (см. п. 7);
• переразрешаются все маскируемые прерывания посредством сброса
флага INTM.
13. Выполняется возврат из подпрограммы в фоновую программу, в процессе которого автоматически восстанавливается контекст.
141
Пример программирования прерывания от таймера
Предположим, что таймер 1 должен генерировать периодические прерывания через определенный интервал времени, т.е. выполнять роль интервального таймера. Эта задача возникает при отсчете определенных временных интервалов (электронные реле времени), при реализации операционных
систем реального времени, при задании нужного интервала квантования аналоговых сигналов и т.п.
Один из возможных вариантов программы, реализующей эту функцию,
представлен в лист. 5.11. Листинг содержит достаточно подробные комментарии, что позволит понять механизм использования шаблонов для описания
таблицы векторов прерываний, последовательность необходимых действий
при инициализации системы прерываний, в процедуре обслуживания прерывания и в так называемой фоновой программе. Мы будем использовать прерывание по отрицательному переполнению таймера T1UFINT, которое возникает в момент обнуления счетчика таймера, т.е. в конце каждого периода.
Счетчик числа вызовов обработчика прерывания инкрементируется при каждом вызове.
Замечание
В процессе выхода из процедуры обслуживания прерывания и автоматического восстановления контекста будет восстановлено и содержимое статусных регистров. Поэтому сбрасывать флаг INTM программно не обязательно.
Практическая работа
1. Удалите файл main.c из проекта и добавьте файл main_a.c. Для этого
перейдите в меню Project → Add Files to Project… и в появившемся окне из
папки lab5\src выберите файл main_a.c. Откройте его в среде Code
Composer.
2. Откомпилируйте программу и загрузите ее в память микроконтроллера.
Установите точку останова, как показано на рис. 5.11. Напомним, что это
можно сделать, дважды щелкнув мышью на сером фоне слева от нужной
строки.
Рис. 5.11. Установка точки останова в программе
142
3. Добавьте переменную isr_ticker в окно просмотра. Для этого переместите курсор на ее имя и нажмите правую кнопку мыши. Во всплывающем
контекстно-зависимом меню выберите Add to Watch Window.
4. Запустите программу в режиме Animate и последите за изменением
значения переменной.
5. Изучите содержимое файла main_a.c. Соответствует ли эта программа
описанному в теоретической части алгоритму обслуживания прерываний?
Попробуйте понять, как работают готовые функции, поставляемые Texas
Instruments, такие как InitSysCtrl(); InitPieCtrl(); InitPieVectTable().
6. Измените процедуру обслуживания прерывания от таймера 1, например исключите команду разрешения обработки всех маскируемых прерываний перед выходом из процедуры. Объясните последствия.
Контрольные вопросы
1. Какие прерывания называются маскируемыми?
2. Что такое прерывание центрального процессора и периферийное прерывание?
Зачем нужен модуль расширения периферийных прерываний?
3. Можно ли считать, что контроллер прерываний имеет фиксированный приоритет
обслуживания прерываний? Чем определяется этот приоритет?
4. Какие флаги аппаратно сбрасываются при переходе к процедуре обслуживания
прерывания? Зачем это делается?
5. Можно ли внутри процедуры обслуживания текущего прерывания разрешить
обслуживание другого (более приоритетного)? Что нужно сделать для этого?
Практическая работа
1. Рассчитайте значение регистра периода таймера 1 для получения частоты прерываний 5 кГц. Модернизируйте программу, чтобы таймер генерировал второе прерывание по достижению в счетчике таймера значения, равного значению в регистре периода.
2. Дополните программу второй процедурой обслуживания прерываний
по периоду таймера. Установите в обработчике процедуры обслуживания
прерывания персональный счетчик ее вызовов. Убедитесь в том, что оба
обработчика прерываний вызываются с одинаковой частотой.
3. Можно ли получить частоту генерации прерываний 1 кГц? Это будет
соответствовать «кванту» времени в 1 мс. Модернизируйте программу для
отсчета любой произвольной выдержки времени, заданной в числе [мс] отдельной переменной. После отсчета выдержки времени состояние линии XF микроконтроллера должно изменяться на противоположное и «реле времени»
должно запускаться вновь. Отладьте эту программу в режиме с точками
останова.
5.9. ОТЛАДКА ПРОГРАММ В РЕАЛЬНОМ ВРЕМЕНИ
Особенностью процессоров серии C28xx является возможность отладки
программы без прерывания процесса ее выполнения в реальном времени. Эта
возможность особенно полезна, когда необходимо отслеживать изменение
143
переменных, менять их значения и при
этом остановка программы недопустима. Так, при работе ШИМ-генератора, управляющего силовыми ключами инвертора, остановка программы
по событию Breakpoint (точка останова) приведет к фиксации текущего
состояния ключей инвертора, что
может вызвать аварию, например превышение максимально допустимого
тока в двигателе.
Работа в реальном времени позволяет существенно упростить и ускорить процесс отладки систем встроенного управления оборудованием.
Для перехода в режим реального
времени нужно выбрать в CCS
команду меню Debug → Real Time
Mode (Отладка → Режим реального
времени), приведенную на рис. 5.12.
Появится предупреждение о переходе в режим реального времени, и о
том, что использование точек останова запрещено. Нужно убедиться, что
точки останова исключены из проРис. 5.12. Выбор режима реального времени
граммы, и ответить «Да» (рис. 5.13).
Далее можно запустить программу
на выполнение.
На элементах, требующих постоянного наблюдения (например, в графических окнах), необходимо выбрать в контекстном меню режим Continuous
Refresh (Непрерывное обновление), представленный на рис. 5.14.
Для регулирования скорости обновления данных существует команда
CCS View → Real-Time Refresh Options (Просмотр → Опции обновления
Рис. 5.13. Предупреждение системы об ограничении возможностей отладки программы в
режиме реального времени
144
Рис. 5.15. Установка опций обновления в
режиме реального времени
Рис. 5.14. Выбор режима непрерывного
обновления информации
в режиме реального времени), изображенная на рис. 5.15. Здесь Вы можете
задать интервал обновления данных в количестве [мс] (Refresh every __ ms),
а также установить галочку Global Continuous Refresh (Непрерывное
обновление всех глобальных переменных).
Таким образом, в каждом микроконтроллере производителем заложена
как бы операционная система реального времени, которая работает по прерываниям от отладочного JTAG-интерфейса. Тем самым пользователь получает уникальную возможность со стороны компьютера, подключенного к
контроллеру через JTAG-интерфейс, наблюдать за ходом выполнения программы, не останавливая ни центральный процессор, ни задействованные в
программе периферийные устройства. Необходимо лишь помнить о том, что
возможности отладочного интерфейса JTAG по скорости доступа к данным
ограничены.
Предупреждение: перед перезагрузкой программы режим реального времени рекомендуется отключать.
Практическая работа
1. Включите режим реального времени, как описано выше. Запустите
первую программу без точек останова с разрешением постоянного обновления содержимого окна наблюдения Watch Window. Объясните отличия в
выполнении программы.
2. Исследуйте в реальном времени программу, изменяющую состояние
светодиода через каждую секунду (см. п. 3 предыдущего задания). Соответствуют ли временные интервалы расчетным значениям?
145
Контрольные вопросы
1. Что такое режим реального времени выполнения?
2. Почему в этом режиме при отладке программы необходимо отказаться от точек
останова?
3. Можно ли в режиме реального времени наблюдать за состоянием счетчика таймера? Как быть, если дискретность обновления данных через JTAG в реальном времени
окажется недостаточной?
146
Глава
6
РАБОТА С БИБЛИОТЕКОЙ IQmath
6.1. ПРЕДСТАВЛЕНИЕ ЧИСЕЛ В МИКРОКОНТРОЛЛЕРЕ
Двоичные числа
Двоичная система счисления используется в микропроцессорах, так как
каждый ее значащий разряд имеет всего два возможных состояния, что легко
реализуется на транзисторной логике. Двоичная система, как и десятичная,
является позиционной системой счисления. Каждый разряд имеет вес, определяющийся его позицией в числе. Основание двоичной системы счисления — 2,
поэтому весовые коэффициенты являются степенями двойки. Для целой
части числа (слева от двоичной точки) располагаются весовые коэффициенты 1, 2, 4, 8 и т.д., а справа, в области дробной части числа, 0,5; 0,25; 0,125
и т.д.
Для перевода числа из двоичной системы в десятичную необходимо
умножить каждый разряд двоичного числа на соответствующий ему весовой
коэффициент и сложить произведения:
011012 = 0æ16 + 1æ8 + 1æ4 + 0æ2 + 1æ1 = 8 + 4 + 1 = 13.
(6.1)
Аналогичным способом производится перевод дробных и вещественных
чисел:
(6.2)
111,012 = 1æ4 + 1æ2 + 1æ1 + 0æ0,5 + 1æ0,25 = 7,25.
Дополнительный код
Числа в процессорной технике могут быть как беззнаковыми, так и знаковыми. Для представления знаковых чисел используется дополнительный код:
такое представление отрицательного числа, когда его сумма с соответствующим положительным числом дает нуль в рамках принятого формата числа
(дополняет положительное число до нуля). При этом старший разряд числа
рассматривается как знаковый: 0 — число положительное, 1 — отрицательное.
При получении десятичного эквивалента числа в дополнительном коде (–1)
умножается на двоичное значение старшего (знакового) разряда и его вес:
011012 = –0æ16 + 1æ8 + 1æ4 + 0æ2 + 1æ1 = 13;
111,012 = –1æ4 + 1æ2 + 1æ1 + 0æ0,5 + 1æ0,25 = –0,75.
Выражение (6.1) не изменилось, так как старший знаковый разряд оказался равен 0.
147
Получение дополнительного кода
Дополнительный код числа можно получить, воспользовавшись одним из
приведенных ниже способов:
• вычесть число из нуля (полученный результат той же разрядности
будет дополнением до нуля);
• инвертировать число и прибавить к нему единицу;
• оставить все нули с правой стороны числа неизменными, оставить
неизменной первую единицу, остальные разряды проинвертировать.
Рассмотрим примеры перевода чисел, приведенные на рис. 6.1.
Независимо от способа вычисления получается одинаковый результат.
Наиболее простым и не требующим расчетов является третий способ. В программных реализациях используется первый или второй способ как поддерживаемые системой команд процессора.
Расширение знакового разряда
при преобразовании формата
В микроконтроллерах семейства ‘C28xx АЛУ и большинство регистров
являются 32-разрядными, в то время как память данных 16-разрядная. При
этом поддерживаются как 16-разрядные, так и 32-разрядные вычисления.
Для того чтобы загрузить знаковое 16-разрядное число в 32-разрядный
регистр, требуется провести специальное преобразование формата, которое
называют расширением знакового разряда. При работе с числами без знака
преобразование выполняется простым расширением числа нулями.
В целях уменьшения объема иллюстративного материала для примера
рассмотрим 4-разрядные числа и 8-разрядные регистры.
В первом примере, показанном на рис. 6.2, выполняется загрузка в регистр
беззнакового числа 12. При этом все старшие разряды заполняются нулями.
–
0 0 0 0 0
0 1 1 0 1
1 0 0 1 1
–
0 0 0, 0 0
1 1 1, 0 1
0 0 0, 1 1
1 1 1, 0 1
0 1 1 0 1
Инверсия
Инверсия
0 0 0, 1 0
+
0 0 0, 0 1
0 0 0, 1 1
1 0 0 1 0
+
0 0 0 0 1
1 0 0 1 1
1 1 1, 0 1
0 1 1 0 1
Инверсия
1 0 0 1 1
Копирование до первой
единицы включительно
Инверсия
Копирование до первой
единицы включительно
0 0 0, 1 1
Рис. 6.1. Вычисление дополнительного кода числа
148
Данные 1 1 0 0
12
Регистр 0 0 0 0 1 1 0 0
12
Рис. 6.2. Загрузка беззнаковых чисел
Данные 1 1 0 0 = –23 + 22 = –4
Загрузка и расширение
знакового разряда
Регистр 1 1 1 1 1 1 0 0 = –27 + 26 + 25 + 24 + 23 + 22 =
= –128 + 64 + 32 + 16 + 8 + 4 =
= –4
Рис. 6.3. Загрузка знаковых чисел с расширением знакового разряда
Если программист считает, что данные в предыдущем примере представляют собой число со знаком в дополнительном коде, т.е. 11002 = –4, то для
преобразования этого числа из 4-разрядного в 8-разрядный формат потребуется операция расширения знакового разряда, приведенная на рис. 6.3.
Число загружается без изменений в младшую часть регистра, а в старшую
часть регистра копируется значение знакового разряда.
При программировании на Ассемблере режим расширения знакового разряда включается установкой флага SXM в статусном регистре процессора
(ST0) с помощью специальной команды Ассемблера SETC SXM (для задания режима работы со знаковыми числами) и сбрасывается командой CLRC
SXM (для задания режима работы с беззнаковыми числами). После такой
инициализации преобразование 16-разрядных чисел в 32-разрядные производится автоматически в процессе загрузки числа в аккумулятор или при
выполнении арифметических операций над содержимым 32-разрядного
аккумулятора и 16-разрядной ячейки памяти данных.
На языке Си операция преобразования формата должна быть определена
явно, в противном случае нужное действие выполняется с операндами в
исходном формате и возможно усечение результата (получение недостоверных данных).
Контрольные вопросы
1. Переведите в десятичную систему счисления следующие числа без знака:
110001011,11012; 110010,1101112; 0110,1002; 0,111111112.
2. Переведите в десятичную систему счисления следующие числа со знаком в дополнительном коде:
110001011,11012; 110010,1101112; 0110,1002; 1,111111112.
3. Получите тремя возможными способами дополнительный код для следующих чисел:
110001011,11012, 110010,1101112, 0110,1002; 1,111111112.
4. Зависит ли механизм преобразования чисел из 16-разрядного формата в 32-разрядный от того, является ли преобразуемое число целым, дробным или вещественным?
5. Какой диапазон чисел со знаком можно представить в формате 8.24?
149
6.2. ОСОБЕННОСТИ РАБОТЫ С ДВОИЧНЫМИ ЧИСЛАМИ
Проблемы двоичных вычислений
Для иллюстрации проблем, которые могут возникать при выполнении
арифметических операций с двоичными числами, воспользуемся примером
умножения 4-разрядных чисел, приведенным на рис. 6.4.
Предположим, что вычисляется потребляемая электрическая мощность
посредством умножения значения тока (4 А) на значение напряжения (3 В).
Получается мощность, равная 12 Вт. Для анализа выполненной операции
умножения зададимся следующими вопросами:
• что представляют собой два исходных числа и какой ожидается результат?
• как сохранить результат в памяти? Совпадают ли форматы исходных
чисел и формат результата умножения (произведения)?
Допустим, что исходные данные представляют собой 4-разрядные целые
числа со знаком. Диапазон их возможного изменения от –8 до +7. При умножении результат умещается в восьми разрядах. Диапазон возможных значений результата операции умножения от –56 до +64, диапазон 8-разрядного
выходного формата произведения от –128 до +127 (см. рис. 6.4).
Сохранить результат без потери знаков можно в две 4-разрядные ячейки
памяти. Сохранять только младшую часть нельзя, так как она может не вместить весь результат умножения. Уже в данном конкретном примере ответ
равен 12, что выходит из диапазона допустимых значений знаковых 4-разрядных чисел, и, будучи сохраненной, младшая часть результата будет в
дальнейших вычислениях восприниматься числом –4, что совершенно недопустимо.
Если, как было предложено, сохранять обе части числа (старшую и младшую) в 4-разрядной памяти, то при дальнейших операциях умножения
результата на какое-либо другое число разрядность ответа будет расти и
будет требоваться еще больше памяти для его хранения и больше операций
для обработки и дальнейших вычислений.
Отсюда видно, что величины в физических размерностях крайне
неудобно использовать в расчетах, так как это ведет к постоянному увеличению разрядности вычислений. Поэтому необходимо выбрать другой, более
удобный способ представления данных в микропроцессоре.
0
0
0
0
Аккумулятор 0
0
0
0
0
0
Память
0
0
0
0
0
0
* 0
0 0
0 1
0 0
0 0
0 1
1
0
1
0
0
0
1
0
1
0
0
0
0
0
0
1
0
0
0
0
0
?
Рис. 6.4. Двоичное умножение
150
4
* 3
1 2
Относительные единицы. Форматы
представления чисел в относительных единицах
Для расчета математических моделей, описания алгоритмов системы
управления, измерений электрических и механических величин и цифровой
фильтрации удобно использовать систему относительных единиц. Относительные единицы получаются делением реальной физической величины на
некоторое базовое значение. Базовые значения можно выбирать по-разному,
однако чаще всего за базовое число принимают номинальное значение физической величины.
Если принять за базовый ток номинальный ток двигателя, а за базовое
напряжение — номинальное напряжение двигателя, то в результате перемножения тока на напряжение в относительных единицах будет получаться
мощность, тоже представленная в относительных единицах. Преимущество
такого подхода состоит в том, что всякий раз система работает с числами
вполне определенного диапазона значений. Например, ток в преобразовательной технике редко бывает в несколько раз больше номинального (то же
касается напряжения и мощности).
Рассмотрим пример: необходимо рассчитать относительное значение
мощности при заданных относительных величинах тока и напряжения.
Пусть ток будет равен 1,25, а напряжение 0,75 номинальных значений. Выберем формат 2.2 для представления тока и напряжения, где два разряда будут
отведены под целую часть числа и два разряда — под дробную (рис. 6.5).
Вычисления производятся, как показано в левой части рис. 6.5. Полученный точный результат P* = U*I* = 1,25æ0,75 = 0,9375 представлен в формате
4.4 с четырьмя целыми и четырьмя дробными разрядами. Общее правило
состоит в том, что при умножении вещественных чисел, представленных в
форматах с фиксированной точкой (m.n) и (p.q), включая знаковый разряд в
целой части, получаем произведение в формате (m+p).(n+q).
Если договориться о том, что произведение сохраняется в исходном формате множителей, т.е. в формате 2.2, то необходимо отбросить по два разряда
с каждой стороны (см. рис. 6.5). Сразу возникает вопрос: можно ли так
делать? Будет ли результат иметь ту же точность, что и исходные операнды?
Если известно, что в конкретной системе мощность не может быть
больше номинальной (базовой) в 2 раза, то предложенный формат результата
0
0
0
0
Аккумулятор 0
0
0
0
0
0
0
0
0
0
0
0
* 0
0 0
0 1
0 0
0 0
0, 1
1,0
0,1
1 0
0 1
0 0
0 0
1 1
Память 0 0 , 1 1
1
1
1
0
0
0
1
1,25
* 0,75
0,9375
0
0
0
0
Z
0,75
0
0
0
Y
Z
0
0
Y
Y
Z
X
* X
0 E
Y Y
Y Y
Y E
Z, R
X, X
X, X
E E
Y E
E 0
0 0
E E
E
E
E
0
0
0
E
Z Z, R E
Рис. 6.5. Вычисление мощности
151
подходит. Иначе необходимо увеличить число разрядов целой части множителей, например выбрать для них формат 3.2 или 4.2.
Второе обстоятельство, вызывающее сомнение в правильности расчетов,
связано с отсечением половины разрядов в дробной части. Действительно,
точное значение результата из 0,9375 превратилось в 0,75.
Для определения погрешности этой операции необходимо рассмотреть
процесс умножения, изображенный в правой части рис. 6.5. Здесь символом
«X» обозначены разряды исходных чисел, символ «E» обозначает младшие
разряды исходных чисел, в которые заложена ошибка округления. При умножении ошибки «E» на любое число «X» или «E» получается ошибка «E», что
видно по первой строчке частичных произведений. При умножении «X» на
«X» получается достоверный разряд частичного произведения «Y». При сложении частичных произведений, в котором участвуют «Y», получаются разряды достоверного ответа «Z». Первый разряд, в сумму которого попали
ошибки «E», можно отнести к округленному, но не достоверному разряду
«R», а остальные разряды будут содержать слишком большие ошибки и не
будут достоверными вовсе. Получается, что дробная часть результата, сохраненная в памяти, содержит только округленный разряд и разряд ошибки
округления, так что точность ответа в результате отсечения половины дробных разрядов не ухудшилась.
Рассмотренный пример оперирует с числами малой разрядности. В реальных системах управления на базе микроконтроллеров ‘C28xx используются,
как минимум, 16-разрядные вещественные числа, например, с четырьмя знаками в целой части. Такие числа имеют формат 4.12. Так как для представления целой части числа, включая знаковый разряд, выделяются четыре разряда, то реализуется восьмикратная перегрузочная способность по любой из
переменных, представленных в относительных единицах (относительно
базового значения). Если точности 12 двоичных разрядов после запятой
недостаточно, то можно использовать 32-разрядный формат, например 4.28.
Таким образом, если разрабатываете программное обеспечение системы
управления, то не следует работать с физическими величинами — переходите к относительным единицам и выбирайте формат представления переменных с фиксированной точкой, например 4.28 или 8.24, который, с одной
стороны, обеспечит нужную перегрузочную способность по переменным
вашего проекта, а с другой — нужную точность вычислений.
Система команд микроконтроллеров ‘C28xx оптимизирована для работы
с числами в формате с фиксированной точкой. Более того, есть средства
автоматического преобразования формата произведения к исходному формату множителей. Например, при умножении дробных чисел (перегрузочная
способность переменной равна 1) в формате 1.31 результат в формате 2.62
автоматически, сначала за счет сдвига влево на один разряд приводится к
формату 1.63, а затем за счет отбрасывания младших 32 неточных разрядов —
к исходному формату множителей 1.31.
Мощная система команд микроконтроллеров ‘C28xx послужила предпосылкой к созданию специальной библиотеки вычислений с числами в фиксированном формате (i.q), которая получила название IQmath. Эта библиотека
152
поддерживает вычисления с вещественными переменными, представленными в одинаковом формате (с одним и тем же числом двоичных разрядов
после точки q) и позволяет менять формат чисел при необходимости.
Библиотека IQmath максимально приближена к пользователям, занимающимся разработкой систем управления реального времени, где скорость
вычислений и точность имеют определяющее значение. Ее использование
предполагает представление переменных в относительных единицах, что
позволяет разрабатывать одно и то же программное обеспечение для объектов различной мощности, например для всей серии преобразователей частоты или серии комплектных электроприводов.
Контрольные вопросы
1. В каком формате будет результат умножения 16-разрядных целых чисел без знака
(исходный формат 16.0)? 32-разрядных целых чисел без знака (исходный формат 32.0)?
2. Докажите на примерах, что при умножении дробных чисел без знака в формате
(0.16) точность произведения остается на уровне точности множителей.
3. По какой из переменных для вентильного моментного электропривода должна
быть установлена максимальная перегрузочная способность (по току, скорости, моменту,
ускорению)? Какая именно? Какой при этом будет формат представления переменных?
6.3. ОСНОВЫ РАБОТЫ С БИБЛИОТЕКОЙ IQmath
И СО СТАНДАРТНОЙ БИБЛИОТЕКОЙ ПОДДЕРЖКИ
ОПЕРАЦИЙ С ПЛАВАЮЩЕЙ ТОЧКОЙ
Процесс разработки системы управления, как правило, начинается с
создания ее математического описания. Для проверки качества регулирования на начальном этапе применяют моделирование в одном из математических пакетов (например, MatLab), т.е. система управления вместе с моделью
обсчитывается с переменными в формате с плавающей точкой, которые не
имеют ограничений по динамическому диапазону (могут быть представлены
практически любые по абсолютному значению величины). После того как
доказана работоспособность созданных алгоритмов, можно произвести
перевод системы в относительные единицы и, выбрав необходимый формат
представления переменных с фиксированной точкой (i.q), написать программу для микроконтроллера.
Программа системы управления может быть разработана и для переменных в формате с плавающей точкой, однако все вычисления в этом формате
поддерживаются системой команд на аппаратном уровне (два параллельно
работающих АЛУ с фиксированной и плавающей точкой) только в новейших
микроконтроллерах семейства ‘C2833x, которые начали производиться
только с 2008 г.
В оценочной плате eZdsp2812 установлен микроконтроллер
TMS320F2812, который не поддерживает аппаратно операции с плавающей
точкой. Поэтому все программы с использованием переменных в формате с
плавающей точкой производят вычисления, прибегая к длинным процедурам
153
умножения, деления, сложения и вычитания, скорость выполнения которых
в десятки раз ниже операций целочисленного умножения (или умножения
над числами с фиксированной точкой).
Поддержка плавающей точки в языке Си осуществляется посредством
стандартной библиотеки реального времени выполнения rts2800_ml.lib,
а вычислений с фиксированной точкой в произвольном формате (i.q) —
посредством библиотеки IQmath.
Числа с плавающей точкой
Числа в формате с плавающей точкой представляются в ‘C28xx в 32-разрядном виде, соответствующем международному стандарту IEEE Std. 754
Single Precision Floating-Point (числа в формате с плавающей точкой однократной точности). Для определения переменной в формате с плавающей
точкой используется стандартный тип float. Типы double и long double не
поддерживаются, хотя компилятор не выдает сообщений об ошибке. Они
будут откомпилированы как тип float. Формат числа типа float показан на
рис. 6.6.
Как видно, число с плавающей точкой имеет один знаковый разряд,
восемь разрядов для представления так называемой смещенной экспоненты
e = exp + 127 и 23 разряда для представления мантиссы (дробной части числа 1.f).
При этом единица в мантиссе явно не присутствует, но подразумевается.
Формат числа с плавающей точкой перекрывает большой диапазон значений
–38
до 3,4028235æ1038) и допускает также представление
(от 1,920929æ10
нуля, бесконечности и «Не Числа» (NaN).
Использование этого формата при вычислениях требует значительно
больших ресурсов процессорного времени, чем при использовании форматов с фиксированной точкой. К недостатку формата float следует отнести
также и то, что при кажущейся высочайшей точности в реальных задачах,
31 30
23 22
0
s e e e e e e e e f f f f f f f f f f f f f f f f f f f f f f f
1 знаковый разряд
if
8-разрядная экспонента
23-разрядная мантисса
((e = 255)and ( f ≠ 0 )) then v = NaN ;
s
= 0 )) then v = ⎡(−1) ⎤ ⋅ ∞;
⎣
⎦
s
e −127
⎡
⎤
⎤⎦ ⋅ (1. f );
if (0 < e < 255 ) then v = (−1) ⋅ ⎡⎣ 2
⎣
⎦
if
((e = 255)and ( f
s
≠ 0 )) then v = ⎡(−1) ⎤ ⋅ ⎣⎡ 2e−126 ⎦⎤ ⋅ (0. f );
⎣
⎦
s
⎡
⎤
if ((e = 0 )and ( f = 0 )) then v = (−1) ⋅ 0.
⎣
⎦
if
((e = 0 )and ( f
Рис. 6.6. Формат числа с плавающей точкой и правила представления
154
тем не менее, могут возникать проблемы при операциях с большими и
малыми числами. Приведем типичный пример.
Необходимо для системы управления преобразователем частоты реализовать интегратор выходной частоты инвертора напряжения в угол вектора
напряжения, а затем вычислить синус и косинус этого угла, структурная
схема преобразования которого приведена на рис. 6.7.
Как показано на схеме, задание частоты преобразуется в угловую частоту,
интегрируется в угол и вычисляются синус и косинус этого угла.
При решении задачи в микроконтроллере все непрерывные уравнения
должны быть преобразованы к разностным уравнениям с учетом интервала
дискретизации (квантования) по времени. В нашем случае задача должна
решаться на каждом периоде широтно-импульсной модуляции ключей
инвертора TШИМ. Обычно эта частота равна 5—10 кГц. Примем для определенности, что частота квантования задается интервальным таймером 1 и
равна TШИМ = 5 кГц.
Переведем непрерывное уравнение интегратора в дискретную область:
Y ( p )- 1------------= ;
X(p) p
(6.3)
yp = x;
(6.4)
yk – yk – 1
yp = ------------------------- ;
T ШИМ
(6.5)
yk – yk – 1
------------------------- = x k ;
T ШИМ
(6.6)
yk = yk – 1 + TШИМ xk.
(6.7)
Выражая входное воздействие интегратора xk через частоту f и коэффициент 2π и имея в виду, что выходом интегратора является угловое положение
вектора напряжения θk, получаем:
θk = θk – 1 + 2πTШИМ f.
f
t
2π
ω0
1
—
p
θ
(6.8)
SIN
COS
sin(θ)
cos(θ)
Рис. 6.7. Структурная схема преобразования частоты в угол и синус и косинус угла
155
Таким образом, на каждом периоде ШИМ (с частотой 5000 раз в секунду)
должна вызываться процедура увеличения текущего углового положения
вектора напряжения на некоторую величину, зависящую от частоты f. После
определения нового значения угла должен выполняться расчет синусов и
косинусов угла.
На лист. 6.1 представлена программа, которая состоит из основной (фоновой)
программы и процедуры обслуживания прерывания по периоду таймера 1,
вызываемой с частотой TШИМ = 5 кГц (см. гл. 5). Фоновая программа выполняет инициализацию переменных и содержит пустой (бесконечный) цикл.
Все вычисления выполняются в процедуре обслуживания прерывания.
Лист. 6.1. Преобразование задания частоты в угол и синус и косинус угла
#include <math.h>
...
float x,y,teta,pi,f,Tpwm;
interrupt void t1pr_isr(void);
void main(void)
{...
teta=0;
f=50;
Tpwm=1.0/5000;
pi=3.1415926535897932384626433832795;
for(;;)
{
}
}
interrupt void t1pr_isr(void)
{
teta+=2*pi*f*Tpwm;
x=sin(teta);
y=cos(teta);
}
Напомним, как можно отлаживать программы с прерываниями. После
запуска программы установим точку останова в процедуре обслуживания
прерывания, представленную на рис. 6.8.
Рис. 6.8. Точка останова в процедуре обслуживания прерывания
156
Для наблюдения за периодически меняющейся переменной откроем графическое окно, приведенное на рис. 6.9. На экране появится окно настроек
осциллографа, поля которого нужно проинициализировать, например, в
соответствии с рис. 6.10. В качестве стартового адреса зададим адрес переменной x. В качестве типа переменной укажем 32-bit IEEE floating point
(число в формате с плавающей точкой). После подтверждения настроек и
запуска программы в режиме Анимация с помощью кнопки
программа
начнет выполняться, останавливаясь в точке останова, обновляя содержимое
окна осциллографа и окна наблюдения переменных и запускаясь снова. График переменной x будет изменяться по синусоидальному закону, изображенному на рис. 6.11.
Обратите внимание, что вывод точек на график производится «как бы с
частотой 5000 Гц» (при каждом обращении к процедуре обслуживания прерывания), а частота выходного сигнала задана равной 50 Гц. Таким образом,
на одном периоде сигнала выводится ровно 100 значений переменной x. На
самом деле режим «Анимации» не является режимом реального времени.
Процессор прерывается в точке останова для передачи информации с оценочной платы в компьютер. Если этот процесс займет больше времени, чем
период ШИМ, то один или несколько периодов ШИМ могут быть пропущенными. Поэтому на графике по оси времени указано не абсолютное время, а
номер выборки данных.
В программе допущена грубая ошибка, которая незаметна в процессе
прогона, пока угол θ еще мал. Изменив его на большее значение, например
102900 радиан, можно убедиться, что функции синуса и косинуса неправильно вычисляются при больших значениях угла (рис. 6.12).
Рис. 6.9. Меню вызова окна построения осциллограмм
157
Рис. 6.10. Настройки окна осциллографирования
Рис. 6.11. Результат работы программы
Если теперь значение угла разделить на 2πf, то окажется, что система
управления такого электропривода проработает всего 327 с и «развалится»,
что абсолютно недопустимо.
Как видите, высокая точность вычислений с числами в формате с плавающей точкой еще не является гарантией устойчивости вычислительного алгоритма.
158
Рис. 6.12. Неверный расчет функции синуса «большого угла», нарушение в работе интегратора
Практическая работа
1. Выполните отладку рассмотренной программы.
2. Проверьте, будет ли «разваливаться» функция вычисления косинуса
так же, как и функция синуса. Для того чтобы понять причины этого
эффекта, рассмотрите исходный файл библиотеки стандартных функций
C:\ti\c2000\lib\cgtool\lib\rts.src. Найдите в нем объявление и реализацию
функции синуса, рассмотрите внимательно комментарии разработчиков библиотеки. Вам станет понятно, что функции синуса и косинуса точно работают только в определенном диапазоне изменения аргумента, а при выходе
из него точность расчетов не гарантируется.
3. Изменяя значение угла θ, установите, при каком его значении прекратится интегрирование. В чем причина остановки интегратора? Можно ли
объяснить причину остановки тем, что начиная с определенного большого
значения угла прибавление маленькой добавки из-за неизбежного округления результата в формате с плавающей точкой перестает сказываться?
4. Можно ли модернизировать программу выше, чтобы не происходила
потеря точности вычислений, сохраняя тип переменных float? Сделайте это.
Выполните отладку модернизированной программы.
159
Числа с фиксированной точкой
и операции над ними в библиотеке IQmath
Библиотека IQmath позволяет работать с 32-разрядными числами с фиксированной точкой. Число разбивается на целую и дробную части. Название
формата (i.q) определяется количеством разрядов целой части числа до десятичной точки, включая знаковый разряд, и количеством разрядов дробной
части числа. Например, число с 8 целыми и 24 дробными разрядами имеет
формат 8.24, представленный на рис. 6.13. Можно использовать и другое
обозначение формата — Q24.
Большая разрядность числа в формате Q24 позволяет иметь высокую точность и широкий диапазон значений: от –128 до +127,99999994.
Если система управления работает в относительных единицах, то большая перегрузочная способность дает возможность практически без предварительной переработки переходить от исходных уравнений, написанных для
формата с плавающей точкой, к уравнениям, написанным с использованием
функций библиотеки IQmath.
В качестве примера рассмотрим типовую операцию d = aæb + c, выполняемую
над числами формата Q24 без использования библиотеки IQmath (лист. 6.2).
Лист. 6.2. Классическая реализация IQ вычислений на языке Си
int32 D, A, B, C;
// числа в формате Q24
D = (int32)((int64)A*(int64)B)>>24)+С;
Данный фрагмент программы определяет числа в формате Q24 как целые.
Только программист обязан помнить, что это числа с фиксированной точкой,
и знать ее положение. Процессор работает с этими числами, как с целыми.
Для умножения двух 32-разрядных чисел их необходимо предварительно
преобразовать к 64-разрядному формату, так как формат операндов в языке
Си определяет формат результата умножения. Если этого не сделать, как
было показано ранее, то можно получить неправильный, усеченный результат.
После умножения получается 64-разрядное число в формате 16.48 с 16
целыми и 48 дробными разрядами. Его следует привести к формату числа С,
сдвинув вправо на 24 разряда и уничтожив 24 лишних разряда, которые, как
было показано, не являются достоверными (рис. 6.14). После сдвига результат умножения переводится в 32-разрядный формат (отсекается старшая
часть) и к нему добавляется значение переменной С.
31 30
s i
24 23
i
i
i
i
i
1 знаковый разряд
i
f
0
f
f
f
f
f
f
f
f f
7 разрядов целой части
f
f
f
f
f
f
f f
f
f
f
24 разряда дробной части
Рис. 6.13. Формат числа Q24
160
f
f
f
I8,Q24
A
I8,Q24
B
+
I8,Q24
C
I8,Q24
I8,Q24
D
*
I16,Q48
>>24
ssssssssssssssssssssssssI16,Q24
I8,Q24
Рис. 6.14. Работа алгоритма имитации IQmath средствами языка Си
В IQmath для определения формата переменных используется тип _iq.
Операции сложения и вычитания не претерпевают изменений, однако Си (в
отличие от Си++) не поддерживает перегрузки функций, поэтому умножение, деление и другие операции требуют вызова специальных функций
_IQmpy, _IQdiv и т.д. Для приведенного примера программа для IQmath
должна быть написана, как показано в лист. 6.3.
Лист. 6.3. Пример вычислений с использованием IQmath
_iq a,b,c,d;
d=_IQmpy(a,b)+c;
Данная запись значительно проще для восприятия. Она не содержит операций сдвигов, заложенных в функцию _IQmpy на стадии создания библиотеки ее разработчиками. Код, который будет исполнять микроконтроллер,
показан на рис. 6.15.
Алгоритм расчета несколько отличается от расчета, приведенного на
рис. 6.15. Он адаптирован к возможностям ядра микроконтроллеров семейства ‘C28хх и выполняется всего за семь тактов.
Рис. 6.15. Программа с функцией IQmath и ее эквивалент в машинном коде
161
Вначале регистр умножителя XT загружается значением переменной a с
использованием прямой страничной адресации (адрес 12 на текущей странице памяти). Затем производятся операции умножения содержимого регистра XT на значение переменной b (адрес 4 на текущей странице памяти).
Две операции умножения служат для получения младшей и старшей части
64-разрядного произведения, которые записываются в регистры P и ACC
соответственно.
Следующая команда выполняет сдвиг сдвоенного 64-разрядного регистра
(ACC:P) влево на восемь разрядов, в результате чего в старшей части (в
аккумуляторе — ACC) оказывается результат умножения, приведенный к
формату с восемью целыми и 24 дробными разрядами. К нему добавляется
значение переменной c (адрес 18 на текущей странице памяти), затем результат сохраняется в переменную d по адресу 14 на текущей странице памяти.
Алгоритм расчета показан на рис. 6.16.
Лист. 6.4. Использование библиотеки IQmath в Си++
iq a, b, c, d; // данные в формате Q24
iq operator * (const iq &a, const iq &b)
{
return(((int64)a*(int64)b)>>24);
}
void main(void)
{
d = a * b + c;
}
Здесь описывается функция-операция: вслед за типом возвращаемого
результата iq указывается ключевое слово operator, за которым следует знак
ACC:P
I16,Q48
I8,Q24
a
I8,Q24
b
+
I8,Q24
c
I8,Q24
I8,Q24
d
<<8
*
I8,Q48 00000000
I8,Q24
ACC
Рис. 6.16. Вычисления с использованием функции библиотеки IQmath
162
переопределяемой операции *, после которого в круглых скобках указан список параметров с их типами. Тело переопределяемой операции описывается,
как обычно, между фигурных скобок { }.
Приведенный код является лишь примером. На самом деле в заголовочном файле IQmathLibCPP.h содержится более сложное определение с вызовом функции _IQmpy(a,b). Таким образом, в языке Си++, объявив вещественные переменные типа iq с фиксированной точкой, дальше можно
пользоваться обычными операторами, такими как «*», «/» и т.д. Они автоматически будут замещаться соответствующими функциями библиотеки
IQmath.
Список функций, содержащихся в библиотеке IQmath, приведен в табл. 6.1
в сравнении с функциями стандартной библиотеки поддержки вычислений с
плавающей точкой.
Табл и ц а 6.1
Основные функции библиотеки IQmath
Операция
Плавающая точка
IQmath Си
IQmath Си++
Определение типа
float a,b;
_iq a,b;
iq a,b;
Определение константы
a = 1.2345
a = _IQ(1.2345)
a = IQ(1.2345)
Умножение
a*b
_IQmpy(a,b)
a*b
Деление
a/b
_IQdiv(a,b)
a/b
Сложение
a+b
a+b
a+b
Вычитание
a–b
a–b
a–b
Булевы
>, >=, <, <=, ==,
!=, &&, ||
>, >=, <, <=, ==,
!=, &&, ||
>, >=, <, <=, ==,
!=, &&, ||
Синус
sin(a)
_IQsin(a)
IQsin(a)
Косинус
cos(a)
_IQcos(a)
IQcos(a)
Синус (угол в относительных единицах)
sin(a*2pi)
_IQsinPU(a)
IQsinPU(a)
Косинус (угол в относительных
единицах)
cos(a*2pi)
_IQcosPU(a)
IQcosPU(a)
Арктангенс
atan(a)
_IQatan(a)
IQatan(a)
Угол координаты
atan2(a,b)
_IQatan2(a,b)
IQatan2(a,b)
Угол координаты в относительных
единицах
atan2(a,b)/2pi
_IQatan2PU(a,b)
IQatan2PU(a,b)
Амплитуда
sqrt(a*a+b*b)
_IQmag(a,b)
IQmag(a,b)
Квадратный корень
sqrt(a)
_IQsqrt(a)
IQsqrt(a)
Обратное значение квадратного
корня
1/sqrt(a)
_IQisqrt(a)
IQisqrt(a)
Насыщение
if (a>pos) a=pos;
if (a<neg) a=neg;
_IQsat(a,pos,neg)
IQsat(a,pos,neg)
163
Контрольные вопросы
1. Как Вы понимаете функцию синус угла в относительных единицах?
2. Какие функции необходимо использовать при переходе от декартовой системы
координат к полярной?
3. Как выполнить обратный переход от полярной системы координат к декартовой?
Пример разработки и отладки программы инерционного
фильтра с использованием библиотеки IQmath
Рассмотрим пример реализации программного модуля цифрового фильтра с простейшей передаточной функцией инерционного звена:
1
Y(p)
W ( p ) = ------------ = -------------------- ;
X ( p ) Tф p + 1
(6.9)
Tф yp + y = x,
(6.10)
где Tф — постоянная времени фильтра.
Для преобразования уравнения фильтра из непрерывной области в дискретную введем интервал дискретизации по времени h. Тогда производная в
уравнении (6.12) может быть представлена в виде разности первого порядка:
yk – yk – 1
- + yk = xk ;
T ф -----------------------(6.11)
h
T
T ф⎞
⎛ 1 + -----ф-⎞ y = ⎛ ----+ xk ;
(6.12)
- y
⎝
h⎠ k ⎝ h⎠ k–1
T
-----фh
1
y k = ----------------- y k – 1 + ----------------- x k .
Tф
Tф
1 + -----1 + -----h
h
Упростив выражение, получим:
Tф
h
-y
y k = ---------------+ ----------------- x .
Tф + h k – 1 Tф + h k
(6.13)
(6.14)
Если теперь в уравнении выделить коэффициенты при yk – 1 и xk, то оно
преобразуется к виду:
yk = ky1yk – 1 + kx0xk,
(6.15)
Tф
h
где k y1 = ----------------- , а k x0 = ----------------- .
Tф + h
Tф + h
Для определения типа данных, используемых инерционным фильтром, и
описания прототипа функций, выполняющих инициализацию фильтра и
обработку фильтром входных данных, создадим файл filter.h, который приведен ниже:
164
Лист. 6.5. Заголовочный файл модуля инерционного фильтра filter.h
#ifndef filter_h
#define filter_h
typedef struct { _iq x;
_iq y;
_iq kx0;
_iq ky1;
_iq Th;
void (*Init)();
void (*Execute)();
} FILTER_DATA;
#define FILTER_DEFAULTS { 0,0,0,0,\
_IQ(10),\
(void (*)(void))Filter_Init, \
(void (*)(void))Filter_Execute,\
}
void Filter_Init(FILTER_DATA *);
void Filter_Execute(FILTER_DATA *);
#endif
В представленном файле определен новый тип переменной с именем
FILTER_DATA на основе структуры, состоящей из входной величины xk
(переменная x), выходной величины yk (переменная y), коэффициентов kx0 и
T
ky1, параметра Th, который определяет отношение -----ф- , и двух указателей на
h
функции инициализации и расчета фильтра. В структуре величина yk – 1,
которая используется при расчете yk, отсутствует. Это связано с тем, что рассчитанное на предыдущем такте работы фильтра выходное значение yk для
нового такта расчета является как раз предыдущим значением выходной
величины yk – 1. Поэтому достаточно иметь только одну переменную, значение которой обновляется в процессе расчета.
Члены структуры void (*Init) (void) и void (*Execute) (void) являются
указателями на функции, где:
void — тип возвращаемого функцией значения;
Init и Execute — имена указателей на функцию инициализации фильтра и
собственно процедуру фильтрации;
(void) — описание передаваемых в функцию переменных (в данном случае список параметров пуст, так как в функцию ничего не передается).
После объявления типа структуры данных фильтра определяются значения
по умолчанию, которые следует использовать для автоматической инициализации данных типа FILTER_DATA. По умолчанию в FILTER_DEFAULTS
прописаны нулевые значения для входа, выхода и коэффициентов фильтра,
значение 10 для коэффициента фильтрации, а также адреса функций инициализации и расчета фильтра, синтаксис которых рассмотрим подробнее на
примере функции инициализации:
165
(void (*) (void)) Filter_Init — преобразование функции Filter_Init к указателю, объявленному в структуре. Здесь void (*) (void) является безымянным указателем (обратите внимание на сходство с указателем, объявленным
в структуре).
В конце файла описываются прототипы функций, в которые передаются
указатели на структуру FILTER_DATA.
Строго говоря, преобразование одного типа функции к другому не вполне
корректно, но используется в Code Composer Studio, поскольку в момент
объявления типа структуры FILTER_DATA невозможно объявить тип передаваемых переменных в функции, которые являются членами этого же типа
(проще говоря, нельзя использовать имя типа внутри своего же объявления).
Главный файл модуля фильтра filter.c (см. лист. 6.6) содержит обращение
к заголовочным файлам библиотеки IQmath и filter.h. Затем следует процедура инициализации, с помощью которой рассчитываются коэффициенты
ky1, kx0 и основная функция фильтра.
Лист. 6.6. Главный файл модуля инерционного фильтра filter.c
#include "IQmathLib.h"
#include "filter.h"
void Filter_Init(FILTER_DATA *filter)
{
filter->ky1=_IQdiv(filter->Th,(filter->Th+_IQ(1)));
filter->kx0=_IQdiv(_IQ(1),(filter->Th+_IQ(1)));
}
void Filter_Execute(FILTER_DATA *filter)
{
filter->y=_IQmpy(filter->kx0,filter->x)+
_IQmpy(filter->ky1,filter->y);
}
Оператор «–>» используется для обращения к членам структуры по ее
указателю (не путать с оператором «.», который используется непосредственно с именем структуры).
Готовую функцию подключите к проекту, добавив в проект файл filter.c.
Пример обращения к функции из main.c показан в лист. 6.7.
Вызов фильтра следует делать не из фоновой программы, а из процедуры
обслуживания прерывания интервального таймера, задающего период дискретизации, чтобы была точно определена постоянная интегрирования. В
данном случае помещение основной процедуры фильтрации в фоновую программу сделано в целях упрощения.
В начале программы подключаются заголовочные файлы. Определяется
структура данных фильтра sample типа FILTER_DATA, который сразу инициализируется значениями по умолчанию. В главной функции программы
main можно изменить значения по умолчанию, если это требуется, и выполнить процедуру инициализации. В цикле во входную переменную sample.x
записывается входное воздействие, а после вызова процедуры фильтра из
sample.y извлекается результат фильтрации.
166
Лист. 6.7. Вызов фильтра из главной программы main.c
#include "IQmathLib.h"
#include "filter.h"
FILTER_DATA sample=FILTER_DEFAULTS;
_iq x,y;
void main(void)
{
sample.Th=_IQ(10);
sample.Init(sample);
for(;;)
{
sample.x=x;
sample.Execute(sample);
y=sample.y;
}
}
Предлагаемая методика описания функции цифрового фильтра на первый
взгляд кажется избыточно сложной, однако она настоятельно рекомендуется
программистам ввиду следующих преимуществ:
• в одном проекте можно объявить сколько угодно экземпляров фильтра
для работы с различными входными переменными: токами фаз двигателя,
сигналом задания частоты, сигналом обратной связи по давлению и температуре и т.п.;
• цифровой фильтр для каждой из переменных может иметь свой собственный период квантования по времени и свою собственную постоянную
времени. Например, постоянная времени фильтра для токовых сигналов
может быть равной 0,01 с, а для сигналов давления и температуры — 5—10 с.
При этом нет необходимости изменять основные функции фильтрации.
Достаточно только правильно выполнить инициализацию соответствующей
структуры;
• описанный подход строго соответствует концепции модульности программного обеспечения, когда для описания любого числа однотипных задач
(процессов) требуется всего лишь один программный модуль, который
может многократно вызываться с различными значениями параметров.
В качестве входного сигнала для проверки работы цифрового инерционного фильтра удобно использовать функцию синуса с наложенными на нее
высокочастотными составляющими помех, например:
x = sin(t) + 0,2sin(45t) + 0,2sin(65t).
(6.16)
Время t может имитироваться переменной, значение которой увеличивается на некоторую константу в каждом цикле фоновой программы. Добавим
в программу переменную t типа _iq и запишем выражение для расчета входной переменной фильтра x (лист. 6.8).
167
Лист. 6.8. Расчет входного зашумленного сигнала
t+=_IQ(0.05);
x=_IQsin(t)+_IQmpy(_IQsin (_IQmpy(_IQ(45),t)),_IQ(0.2))
+_IQmpy(_IQsin (_IQmpy(_IQ(65),t)),_IQ(0.2));
Если запустить программу, а в качестве наблюдаемой переменной в окно
осциллографа вывести x, то можно наблюдать синусоиду с наложением
шума, однако приблизительно один раз за 40 периодов основной гармоники
наблюдается разрыв функции, представленный на рис. 6.17.
Это происходит из-за переполнения формата переменной t, которая не
может хранить значения, превышающие +127,99999994.
При достижении максимального значения происходит двоичное переполнение переменной t — она становится отрицательной величиной со значением, равным –128. Если рассчитать значение синуса для углов +128 и
–128 радиан, то они будут равны +0,788 и –0,788 соответственно, что и образует разрыв в функции, изображенный на рис. 6.17.
Причина кроется в том, что периодичность изменения t не соответствует
периодичности функции sin(t). Чтобы периодичность совпадала, используют
углы, представленные не в радианах, а в относительных единицах. Тогда
Рис. 6.17. Разрыв в функции синуса входной переменной x
168
одному обороту или 2π в радианах соответствует единица, т.е. синус берется
не от угла, а от оборота. Это очень удобно тем, что в формате Q24 переменная t может хранить 256 целых оборотов (значение t меняется от
+127,99999994 до –128). Так как число оборотов во всем диапазоне значений
t целое, то и функция синуса от числа оборотов будет непрерывной.
Зная об этой проблеме, разработчики библиотеки IQmath предусмотрели
целый ряд тригонометрических функций для работы с углами в относительных единицах: _IQsinPU, _IQcosPU и др. Полный перечень этих функций приведен в табл. 6.1.
Так как в дальнейшем предполагается выводить результаты работы фильтра в окно осциллографа с анализом спектра с помощью преобразования
Фурье, то будет удобно, если один период синуса основной гармоники будет
сформирован количеством точек, равным степени двойки. Поэтому новое
приращение для переменной t будет равно 1/256 (лист. 6.9).
Лист. 6.9. Основная часть цикла тестирования функции фильтра
t+=_IQ(0.00390625);
x=_IQsinPU(t)
+_IQmpy(_IQsinPU(_IQmpy(_IQ(45),t)),_IQ(0.2))
+_IQmpy(_IQsinPU(_IQmpy(_IQ(65),t)),_IQ(0.2));
sample.x=x;
sample.Execute(sample);
y=sample.y;
Для получения сразу двух графиков (входа и выхода фильтра) в окне
осциллографа следует вызвать окно свойств осциллографа и изменить параметр Display Type, как показано на рис. 6.18, а, а затем указать адрес второго
осциллографируемого параметра — рис. 6.18, б.
После запуска программы верхний луч осциллографа отображает график
изменения входного сигнала фильтра, а нижний — значение выходного
отфильтрованного сигнала (рис. 6.19).
Чтобы оценить степень фильтрации сигнала, можно воспользоваться
инструментом быстрого преобразования Фурье (БПФ), предоставляемым
средой Code Composer Studio. Для этого необходимо создать еще два окна
осциллографа для входного и выходного сигналов.
Настройки окон следует выполнить, как показано на рис. 6.20, выбрать
тип осциллограммы FFT Magnitude (амплитудное БПФ), тип сигнала Real
(действительный сигнал). Количество выборок для преобразования установлено равным 256, что соответствует одному периоду синусоиды основной
гармоники входного сигнала.
Следующая важная настройка касается формата входных чисел. Встроенное в Code Composer преобразование Фурье не совершает операцию деления
интеграла гармонических составляющих на количество выборок, поэтому
отображаемое значение амплитуды будет больше реального в 128 раз (половина от общего количества точек). Чтобы амплитуда соответствовала дей169
а)
б)
Рис. 6.18. Настройка осциллографа на два канала
ствительной, необходимо скорректировать формат входных данных, увели7
чив количество дробных разрядов на семь (2 = 128 — половина емкости
буфера преобразования), и установить формат Q31 (Q24 + 7). Чтобы по горизонтальной оси были отложены гармоники относительно основной, следует
установить параметр частоты сбора информации равный 256 Гц, что соответствует количеству выборок в буфере БПФ.
170
Рис. 6.19. Результаты цифровой фильтрации:
входной и выходной сигналы
Результат работы БПФ показан на рис. 6.21. Подробно исследовать сигнал
можно, устанавливая с помощью мыши курсор (Data Cursor) в те точки графика, где амплитуды отличны от нуля. В исходном сигнале есть: первая гармоника с амплитудой, равной 1; 45-я гармоника с амплитудой, равной 0,2;
65-я гармоника с амплитудой, равной 0,2. Это полностью соответствует описанию входного сигнала в программе, приведенной на лист. 6.9. В выходном
отфильтрованном сигнале присутствуют первая гармоника с амплитудой
0,9684, 45-я гармоника с амплитудой 0,0181 и 65-я гармоника с амплитудой
0,0133. Результаты опыта показывают, что простой инерционный фильтр
эффективно справляется с высокими гармониками, однако вносит амплитудные и фазовые искажения в основной сигнал.
Практическая работа
1. Исследуйте входной сигнал для переменной t, представленной в радианах. Убедитесь в разрывности переменной x.
2. Перейдите к относительным единицам. Отладьте программу.
171
Рис. 6.20. Настройки окна быстрого преобразования Фурье
3. Освойте технологию анализа частотного спектра входного и выходного
сигнала фильтра с использованием преобразования Фурье. Оцените фазовые
и амплитудные искажения, вносимые фильтром.
4. Измените постоянную времени фильтра, например, вдвое. Объясните
полученные результаты.
5. Создайте второй экземпляр данных инерционного фильтра, соедините
два фильтра последовательно, чтобы их работа соответствовала двойному
инерционному фильтру. Исследуйте работу фильтра с помощью средств
осциллографирования и БПФ. Сравните степень фильтрации для двух исследованных вариантов фильтров.
6. Создайте второй источник зашумленного сигнала с другими параметрами помех. Осуществите его фильтрацию. Сколько входных сигналов
может обрабатываться одним модулем фильтра в одной программе?
172
Рис. 6.21. Результата работы БПФ
7. Представьте
уравнение
двойного
инерционного
фильтра
1
W ( p ) = ---------------------------- в дискретной области. Напишите модуль программного
2
( Tф p + 1 )
обеспечения такого фильтра. Сравните его работу с работой двух последовательно соединенных инерционных фильтров. Какой вариант программы
выполняется быстрее?
6.4. ИЗМЕНЕНИЕ ФОРМАТА ЧИСЕЛ
ПРИ РАБОТЕ С БИБЛИОТЕКОЙ IQmath
По умолчанию библиотека IQmath настроена на работу с числами формата Q24, однако если для конкретной задачи не хватает точности или динамического диапазона, то формат чисел может быть изменен как для всего
проекта, так и для отдельных его частей.
В файле IQmathLib.h, подключаемом ко всем файлам, использующим
IQmath, содержится описание константы GLOBAL_Q (число дробных разрядов), которая может быть изменена по усмотрению программиста проекта
в диапазоне от 1 до 30. Все функции в программе будут использовать именно
это значение Q. Эта константа задает формат представления чисел по умолчанию, обрабатываемых функциями библиотеки IQmath. Для некоторых
типовых значений константы диапазоны изменения чисел и точность их
представления приведены в табл. 6.2.
173
Табл и ц а 6.2
Диапазоны представления и точности чисел в Q-форматах
Значение
GLOBAL_Q
Вес младшего разряда
максимальное
минимальное
28
+7,999 999 996
–8,000 000 000
0,000 000 004
24
+127,999 999 94
–128,000 000 00
0,000 000 06
20
+2047,999 999
–2048,000 000
0,000 001
Лист. 6.10. Принудительное указание формата Q-чисел
#include "IQmathLib.h"
_iq23 a, b, c;
void main(void)
{
a=_IQ23(1.1);
b=_IQ23(1.2);
c=_IQ23mpy(a,b);
}
Если по каким-либо причинам программисту необходимо изменить формат, то это можно сделать в отдельном фрагменте программы, как показано в
лист. 6.10. Следует помнить, что формат переменных внутри одной строки
должен быть одинаковый. В библиотеке IQmath предусмотрены функции
преобразования форматов, которые приведены в табл. 6.3.
Табл и ц а 6.3
Функции преобразования форматов в библиотеке IQmath
Операция
Плавающая точка
IQmath Си
IQmath Си++
Из iq в iqN
a
_IQtoIQN(a)
IQtoIQN(a)
Из iqN в iq
a
_IQNtoIQ(a)
IQNtoIQ(a)
Целая часть
(long)a
_IQint(a)
IQint(a)
Дробная часть
a-(long)a
_IQfrac(a)
IQfrac(a)
Умножение на целое число
a*(float)b
_IQmpyI32(a,b)
IQmpyI32(a,b)
Целая часть при умножении
на целое число
(long)(a*(float)b)
_IQmpyI32int(a,b)
IQmpyI32int(a,b)
Дробная часть при умножении a-(long)(a*(float)b)
на целое число
_IQmpyI32frac(a,b)
IQmpyI32frac(a,b)
Из iq во float
_IQtoF(a)
IQtoF(a)
174
a
Практическая работа
1. Измените значение константы GLOBAL_Q, чтобы проект инерционного
фильтра, рассмотренный выше, работал с переменными в формате Q28. Сказывается ли повышение точности представления переменных на работе фильтра?
2. Измените программу цифрового инерционного фильтра, чтобы он имел
2000-кратную перегрузочную способность по входному воздействию. Как
изменятся строки вызова процедуры фильтрации и передачи фильтру параметров в функции main? Как сказывается уменьшение точности представления переменных на работе фильтра?
3. Проанализируйте код перевода числа формата GLOBAL_Q в формат
переменных модуля цифрового фильтра. Объясните суть производимых преобразований.
6.5. СРАВНЕНИЕ ПРОИЗВОДИТЕЛЬНОСТИ
ПРИ РАБОТЕ С ЧИСЛАМИ В РАЗЛИЧНЫХ ФОРМАТАХ
Сравнение 32-разрядных и 16-разрядыных вычислений
Микроконтроллеры ‘C28xx оптимизированы как для 16-разрядных, так и
для 32-разрядных вычислений. В ряде случаев возникает соблазн использовать 16-разрядные вычисления для того, чтобы уменьшить размер программного кода и увеличить скорость его выполнения. Разумеется, это можно
делать, но только в том случае, когда точности вычислений достаточно. Библиотекой IQmath 16-разрядная арифметика с фиксированной точкой не поддерживается. Поэтому пользователь должен создавать свою собственную
библиотеку вычислений с вещественными 16-разрядными числами. Затраты
времени на разработку такой библиотеки могут быть неоправданными, особенно в том случае, когда требуются форматы с различным положением
десятичной точки.
На DSP-микроконтроллерах семейства ‘С28хх 32-разрядные операции c
вещественными числами в форматах с фиксированной точкой выполняются
почти так же эффективно, как 16-разрядные операции, однако при существенно более высокой точности. Именно поэтому библиотека IQmath поддерживает 32-разрядные вычисления. Большой объем встроенной памяти данных в микроконтроллерах ‘C28xx, а также возможность ее расширения с
помощью внешних БИС статического ОЗУ практически полностью снимают
проблему экономии памяти данных под переменные проекта.
Практика показывает, что рационально использовать целочисленные
16-разрядные вычисления, особенно при работе с периферией. Однако
вычисления с вещественными 16-разрядными числами нецелесообразны.
Проще и точнее использовать для подобных задач 32-разрядные вычисления,
поддерживаемые библиотекой IQmath. При этом значительно сокращается
время разработки и отладки программ, время до выхода готового изделия на
рынок. Мы рекомендуем этот подход для реализации цифровых регуляторов
и фильтров, наблюдателей состояния, модулей преобразования координат в
системах векторного управления и т.п.
175
Сравнение производительности вычислений
с плавающей точкой и фиксированной точкой
с помощью встроенных в Code Composer Studio средств
Библиотека IQmath сложна в использовании (по сравнению с плавающей
точкой), имеет малый диапазон значений величин, что требует постоянной
проверки исходных уравнений на возможность переполнения формата.
Насколько действительно велика необходимость ее применения в задачах
реального времени?
Чтобы ответить на этот вопрос, необходимо измерить производительность программы, выполняющей одинаковые действия для плавающей точки
и фиксированной точки, поддерживаемой библиотекой IQmath. Например,
сравним выполнение двух тестовых задач: решение линейного уравнения и
непрерывную развертку угла с вычислением его косинуса и синуса, каждая
из которых реализована в формате с плавающей точкой и в формате с фиксированной точкой библиотеки IQmath.
Программа, представленная на лист. 6.11, содержит фрагменты для плавающей (все переменные, начинающиеся с буквы f) и фиксированной точек.
В среде CCS имеются специальные средства оценки числа тактов микропроцессора, затраченных на выполнение определенного программного кода.
Чтобы воспользоваться ими, необходимо включить счетчик числа тактов
микроконтроллера, выбрав пункт меню Profiler/Enable Clock (Профайлер/Разрешить подсчет тактов), а затем вывести содержимое счетчика
числа тактов на экран компьютера с помощью пункта меню Profiler/View
Clock (Профайлер/Просмотр числа тактов). Профайлером называется
комплекс специальных средств отладочной среды CCS, позволяющих оценить время выполнения некоторого фрагмента программы в числе тактов
процессора.
Дальше необходимо установить точку останова внутри цикла, запустив
программу на выполнение в режиме «Анимация» с помощью кнопки
,и
создать два окна осциллографа для наблюдения за текущими значениями
переменных fx и x, изображенных на рис. 6.22. Как видно из рисунка, оба
фрагмента программы с вычислениями в формате с плавающей (верхний
график) и фиксированной (нижний график) точками работают идентично.
Лист. 6.11. Программа для измерения производительности вычислений
#include <math.h>
#include "IQmathLib.h"
_iq x,y,teta,pi,f,Tpwm;
float fx,fy,fteta,fpi,ff,fTpwm;
_iq a,b,c,d;
float fa,fb,fc,fd;
176
void main(void)
{
fteta=0;
teta=0;
ff=50;
f=_IQ(50);
fTpwm=1.0/5000;
Tpwm=_IQ(0.0002);
fpi=3.1415926535897932384626433832795;
pi=_IQ(3.1415926535897932384626433832795);
a=_IQ(2);
b=_IQ(2.5);
c=_IQ(3.3);
fa=2;
fb=2.5;
fc=3.3;
fd=fa*fb+fc;
d=_IQmpy(a,b)+c;
for(;;)
{
fteta+=2*fpi*ff*fTpwm;
fx=sin(fteta);
fy=cos(fteta);
teta+=_IQmpy(f,Tpwm);
x=_IQsinPU(teta);
y=_IQcosPU(teta);
}
}
Рис. 6.22. Работа тестовой программы измерения производительности
177
а)
б)
в)
Рис. 6.23. Счетчик числа тактов
Измерение производительности выполняется для конкретных строк программы. Перезагрузим программу и выполним ее до строки, содержащей
вычисление переменной fd. После этого двойным нажатием левой клавиши
мыши обнулим счетчик числа тактов микроконтроллера (рис. 6.23, а).
Выполним строку программы с кодом в формате с плавающей точкой. Счетчик числа тактов покажет значение, соответствующее длительности выполнения этого фрагмента программы (рис. 6.23, б). Вновь обнулим счетчик
числа тактов и выполним еще одну строку программы с вычислениями в
формате с фиксированной точкой. Количество тактов на выполнение данной
операции показано на рис. 6.23, в. Данный пример доказывает более чем
30-кратное превосходство по скорости вычислений технологии, заложенной
в библиотеку IQmath, над стандартными вычислениями с плавающей точкой
для микроконтроллеров ‘C28xx.
Выполнение трех последующих строчек кода в теле цикла займет 5823
такта для вычислений с плавающей точкой и 100 тактов для вычислений в
формате с фиксированной точкой с использованием библиотеки IQmath. Разница в скорости вычислений увеличилась до 58 раз. Таким образом, при
использовании библиотеки IQmath выигрыш в производительности может
достигать нескольких десятков.
Практическая работа
1. Выполните описанные выше действия по оценке производительности
различных фрагментов тестовой программы.
2. Произведите измерение времени выполнения функции цифрового
инерционного фильтра, написанного с использованием библиотеки IQmath.
3. Исправьте функцию фильтра для работы с переменными в формате с
плавающей точкой. Измерьте время выполнения модернизированной функции несколько раз при различных значениях входных данных. Усредните
результат. Сравните производительность обеих программ.
4. Рассчитайте, какой процент от периода ШИМ занимает фильтрация
токов двух фаз каждым из вариантов фильтра, если за период ШИМ измерение тока и вызов процедуры фильтрации производятся 4 раза по каждому
току, а частота ШИМ составляет 5 кГц. Можно ли вообще использовать в
таких задачах вычисления с плавающей точкой?
178
Глава
7
ЭФФЕКТИВНЫЕ СПОСОБЫ
ЦИФРОВОЙ ФИЛЬТРАЦИИ СИГНАЛОВ
Данная глава посвящена изучению структуры цифровых фильтров высокого порядка. Знакомству с методами адресации кольцевых буферов и специальными командами умножения с накоплением, методами реализации фильтров. Освоению правил оформления модулей на языке Ассемблер и
технологии передачи параметров из Си-программы в программу на Ассемблере, возврата результатов. Освоению стандартных методов расчета параметров цифровых фильтров в пакете моделирования MatLab. Исследованию
работы 16-разрядных и 32-разрядных фильтров с конечной и бесконечной
импульсной характеристикой.
7.1. ЦИФРОВОЙ 16-РАЗРЯДНЫЙ ФИЛЬТР
С КОНЕЧНОЙ ИМПУЛЬСНОЙ ХАРАКТЕРИСТИКОЙ
Структура КИХ-фильтра
В общем случае цифровой фильтр с конечной импульсной характеристикой (КИХ-фильтр, в зарубежной терминологии — FIR-фильтр) описывается
следующим разностным уравнением:
y(n) =
N
∑ hk x ( n – k )
k=0
(7.1)
или с использованием дискретного преобразования Лапласса передаточной
функцией:
H(z) =
N
∑ hk z
k=0
–k
.
(7.2)
Импульсная передаточная функция КИХ-фильтра идентична коэффициентам фильтра:
⎧ h , 0 < n < N;
h(n) = ⎨ n
⎩ 0, otherwise.
(7.3)
179
Блок-схема фильтра (потоковая диаграмма сигналов) представлена на
рис. 7.1 и соответствует приведенным разностным уравнениям и дискретной
передаточной функции. Реализация такого цифрового фильтра в виде многосекционной линии задержки по существу канонизирована в цифровой обработке сигналов (является общепринятым стандартом). Число элементов
задержки в линии задержки равно порядку фильтра.
Выходное управляющее воздействие (результат работы фильтра) представляет собой сумму произведений величин, сохраненных в линии
задержки, на соответствующие коэффициенты. Заметьте, что число коэффициентов в фильтре всегда на единицу больше, чем порядок фильтра. Это связано с тем, что текущая выборка x(n), которая представляет собой входное
воздействие, всегда участвует в процессе вычислений.
Входные и выходные решетчатые функции показаны на рис. 7.2. Здесь x0,
x1 и т.д. — это последовательные выборки входной переменной фильтра.
Если фильтр имеет, например, пятый порядок, то будет всего шесть коэффициентов фильтра h0, h1, h2, h3, h4, h5, которые участвуют в расчете выходного
воздействия:
y(n) = h0x(n) + h1x(n – 1) + h2x(n – 2) + h3x(n – 3) + h4x(n – 4) + h5x(n – 5). (7.4)
Если предположить, что буфер выборок вначале пуст, то выходное значение y0 будет рассчитываться только на основе входной выборки x0, выходное
значение y1 уже на основе двух выборок x0 и x1 и т.д. Начиная с шестой
выборки x5, все предыдущие выборки будут участвовать в расчете выходной
переменной фильтра, т.е. буфер выборок окажется полностью заполненным.
Рис. 7.1. Блок-схема фильтра с конечной импульсной характеристикой
180
Рис. 7.2. Входные и выходные решетчатые функции
Специальные команды умножения с накоплением
для реализации цифровых фильтров
В микроконтроллерах ‘C28xx предусмотрены специальные способы адресации и специальные команды для эффективного решения задач цифровой
фильтрации. Речь идет, прежде всего, о сохранении входных выборок данных в так называемых кольцевых буферах в памяти данных и о применении
команд умножения с накоплением, в которых один из операндов команды
умножения является выборкой данных в кольцевом буфере, а другой — константой в кодовой памяти или памяти данных. Такая команда может быть
выполнена в цикле повторения с числом повторений, равным порядку фильтра, и автоматическим накоплением всех частных произведений.
Команды умножения с накоплением оптимизированы для работы с вещественными числами со знаком в формате с фиксированной точкой и могут
быть 16-разрядными и 32-разрядными. В последнем случае предусматриваются две различные команды умножения с накоплением, позволяющие последовательно вычислить старшую и младшую части 64-разрядного результата.
Как правило, в цифровых фильтрах все переменные (выборки) и коэффициенты представляются в относительных единицах, т.е. в форматах 1.15 или
1.31. Это не только обеспечивает высокую точность расчетов, но и позволяет
181
Рис. 7.3. Принцип реализации вычислений одновременно по двум выборкам
избежать переполнений в процессе выполнения операций накопления произведений. С этой целью обычно устанавливается автоматический сдвиг частного произведения вправо на нужное число разрядов.
Принцип работы одной из самых мощных команд двойного умножения с
накоплением DMAC, оперирующей 16-разрядными выборками и коэффициентами, показан на рис. 7.3. Команда обеспечивает реализацию вычислений
одновременно по двум «ответвлениям» диаграммы фильтра, т.е. по двум
выборкам x0, x1, последовательно адресуемым регистром-указателем XAR6,
и двум коэффициентам h0, h1, последовательно адресуемым регистромуказателем XAR7. Результат первой операции умножения накапливается в
32-разрядном аккумуляторе A, а второй операции — в 32-разрядном регистре произведения P. Команда может использоваться либо для решения одновременно двух разных задач фильтрации, либо для ускорения решения одной
задачи фильтрации (как продемонстрировано на рис. 7.3). Обе операции
умножения и накопления выполняются за один цикл процессора.
В мнемонике команды используются следующие обозначения:
• (ACC:P) — для задания двух 32-разрядных приемников результатов
операций умножения с накоплением;
• *XAR6 %++ — для специальной косвенной адресации двух первых
множителей (выборок), содержащихся в кольцевом буфере. После каждого
доступа текущее содержимое указателя XAR6 автоинкрементируется;
• *XAR7++ — для обычной косвенной адресации двух вторых множителей (коэффициентов фильтра), расположенных в кодовой памяти. После каждого доступа текущее содержимое указателя XAR7 автоинкрементируется.
Кольцевые буфера
В системе команд микроконтроллеров ‘C28xx поддерживается несколько
способов адресации кольцевых буферов. Один из них реализуется с помощью так называемой «бит-реверсной адресации» — косвенной адресации с
изменением направления распространения бита переноса. Он используется
во всех сигнальных процессорах Texas Instruments и является своеобразной
«визитной карточкой» процессоров этой фирмы. Некоторая сложность и
182
«ненаглядность» метода заставили разработчиков фирмы предложить другие, более очевидные по технологии использования методы адресации. В
качестве примера рассмотрим один из них, представленный на рис. 7.4.
Буфер выборок адресуется с использованием специального способа адресации через регистр-указатель XAR6. Признаком такой особой адресации
является символ %, стоящий после имени регистра-указателя: *XAR6 %++.
Указатель, по достижении адреса последней ячейки буфера (верхней границы
буфера) автоматически переходит на первую ячейку буфера. При декрементировании указателя контроль нижней границы кольцевого буфера отсутствует. Это связано с тем, что в большинстве алгоритмов можно обойтись
без реверса направления прохода кольцевого буфера.
Предполагается, что под кольцевой буфер выделяется область памяти
данных, выровненная по числу 256, т.е., начальный адрес кольцевого буфера
может быть любым с восемью младшими битами, равными нулю. Конечный
адрес кольцевого буфера отличается от начального величиной смещения,
заданной в регистре AR1(7:0), точнее в младшем байте регистра AR1. Таким
образом, длина кольцевого буфера ограничивается 256 словами. Применительно к фильтру с конечной импульсной характеристикой объем кольцевого
буфера позволяет реализовывать фильтры вплоть до 255-го порядка включительно, что достаточно для большинства приложений.
Начальное состояние
кольцевого буфера
после прохода
Загрузка
новой выборки
x[n]
Состояние
кольцевого буфера
после загрузки выборки
XAR6
00000000
Начальный
адрес
Старшая часть
указателя адреса
кольцевого буфера
AR6(31:8)
не меняется
адрес
XAR6
XAR6
x[n – 3]
x[n – 2]
x[n – 1]
x[n]
x[n – N]
x[n – (N – 1)]
x[n – (N – 2)]
x[n – 4]
x[n – 3]
x[n – 2]
x[n – 1]
x[n]
x[n – N]
x[n – (N – 1)]
x[n – 8]
x[n – 7]
x[n – 6]
x[n – 5]
x[n – 4]
x[n – 9]
x[n – 8]
x[n – 7]
x[n – 6]
x[n – 5]
AR1(7:0)
Конечный
адрес
Рис. 7.4. Кольцевой буфер *AR6%++
183
Удобно хранить в кольцевом буфере выборки в обратном порядке — начиная с самой «старой» выборки. В таком же порядке нужно записать и коэффициенты фильтра в кодовую память: hN, hN – 1, hN – 2, …, h2, h1, h0, т.е. начиная
с коэффициента при самой «старой» выборке. Вычисления производятся,
начиная с самых «старых» значений выборок, в направлении «новых» значений с помощью команды умножения с накоплением MAC.
В этом случае перед запуском очередной процедуры фильтрации (нового
прохода фильтра) указатель кольцевого буфера XAR6 будет показывать на
«самую старую» выборку входной переменной x[n – N] (см. рис. 7.4). Она
должна быть заменена «новым» входным значением. Запись входной переменной x[n] нужно выполнить по текущему указателю XAR6 c поставтоинкрементированием, используя адресацию кольцевых буферов. При этом указатель будет автоматически показывать на «новое» самое «старое» значение:
для предыдущего прогона фильтра это переменная x[n – (N – 1)], а для очередного прогона фильтра — переменная x[n – N] (см. правую часть рис. 7.4).
Операция умножения с накоплением сопровождается перемещением указателя XAR7 с автопостинкрементированием по буферу коэффициентов
фильтра в программной памяти и указателя XAR6 с автопостинкрементированием и автоматическим контролем верхней границы кольцевого буфера, а
также возвратом к началу буфера при пересечении границы.
После вычисления выходной переменной фильтра указатель буфера коэффициентов будет показывать на следующий после расположения таблицы
коэффициентов адрес («неизвестно куда») и должен быть обязательно переинициализирован (при очередном вызове). Указатель кольцевого буфера вернется на исходную позицию, т.е. будет опять показывать на «самую старую»
выборку, которая и должна быть заменена новым входным значением.
Если в кольцевом буфере расположены 16-разрядные слова, то их общее
количество будет на единицу больше порядка КИХ-фильтра, т.е. N + 1. При
этом в индексный регистр кольцевого буфера AR1(7:0) следует записать
число, равное порядку фильтра N. Например, фильтр пятого порядка имеет
шесть выборок данных, расположенных по адресам 0, 1, 2, 3, 4, 5. Максимально допустимое смещение адреса кольцевого буфера равно пяти, т.е.
порядку фильтра. Если в кольцевом буфере расположены 32-разрядные
слова, то последняя ячейка кольцевого буфера будет иметь адрес 2*N, который и должен быть загружен в индексный регистр AR1(7:0). Например, для
фильтра пятого порядка с 32-разрядными коэффициентами индексный
регистр кольцевого буфера загружается числом 5æ2 = 10.
Алгоритм расчета 16-разрядного КИХ-фильтра
Рассмотрим алгоритм расчета КИХ-фильтра с использованием команды
двойного умножения с накоплением, приведенный на рис. 7.5. При этом
одновременно извлекаются две выборки входных переменных и два коэффициента, упакованных в 32-разрядные слова. После извлечения данных указатели
автопостинкрементируются (двукратно). Упаковка коэффициентов фильтра в
32-разрядные слова должна быть строго согласована с упаковкой выборок.
184
Линейный буфер
коэффициентов
XAR7
h5
h4
h2
h1
h3
h0
Кольцевой буфер
выборок
XAR6
+*
x[n – 5]
x[n – 2]
x[n – 4]
x[n – 1]
x[n – 3]
x[n]
+*
+
а)
Линейный буфер
коэффициентов
XAR7
h5
h4
h2
h1
h3
h0
Кольцевой буфер
выборок
XAR6
x[n – 2]
x[n – 4]
x[n – 1]
x[n – 3]
x[n]
б)
Линейный буфер
коэффициентов
XAR7
x[n – 5]
h5
h4
h2
h1
h3
h0
Кольцевой буфер
выборок
XAR6
x[n – 2]
x[n – 4]
x[n – 1]
x[n – 3]
x[n]
в)
Линейный буфер
коэффициентов
XAR7
h5
h4
h2
h1
h3
h0
Кольцевой буфер
выборок
XAR6
x[n – 3]
x[n]
x[n – 5]
x[n – 2]
x[n – 4]
x[n – 1]
г)
Рис. 7.5. Работа команды умножения с накоплением:
а, б — состояние до и после расчета; в — замена «самой старой» выборки; г — получение новой
выборки и инкрементирование указателя кольцевого буфера
Продемонстрируем механизм согласования на примере фильтра пятого
порядка. Расположим младшие коэффициенты и младшие выборки в младших словах 32-разрядных слов, а старшие коэффициенты и старшие
выборки — в старших словах 32-разрядных слов. В процедуре расчета
выходного значения производится одновременное накопление произведений
младших выборок на младшие коэффициенты и старших выборок на старшие коэффициенты с последующим суммированием обоих результатов:
⎫
⎪
⎪
2
y ( n ) = h 5 x ( n – 5 ) + h 4 x ( n – 4 ) + h 3 x ( n – 3 ); ⎬
⎪
⎪
1
2
y ( n ) = y ( n ) + y ( n ).
⎭
1
y ( n ) = h 2 x ( n – 2 ) + h 1 x ( n – 1 ) + h 0 x ( n );
(7.5)
185
Обе операции умножения с накоплением должны производиться с учетом
установленного режима сдвига произведения (PM). Последняя операция
суммирования осуществляется без сдвига. После завершения расчета указатель XAR7 на таблицу коэффициентов фильтра должен быть переинициализирован. Указатель кольцевого буфера XAR6 вернется в исходную позицию.
Старшее слово 32-разрядного кольцевого буфера будет содержать самую старую
выборку x[n – 5], которая замещается младшим словом — выборкой x[n – 2].
На освободившееся место необходимо записать новую выборку x[n]. Эта
запись должна сопровождаться инкрементированием указателя кольцевого
буфера. Таким образом, указатель кольцевого буфера будет по-прежнему
показывать на слово с «самой старой» выборкой (с точки зрения нового прогона фильтра). Можно начинать новый расчет.
Если порядок фильтра четный, то число коэффициентов будет нечетным.
Самый простой способ работы с таким фильтром состоит в том, чтобы
искусственно повысить порядок фильтра на единицу и сделать самый старший коэффициент равным 0. Например, для фильтра четвертого порядка
можно выполнить расчет на базе фильтра пятого порядка с коэффициентом
h5 = 0. Никаких других изменений не требуется.
Установка индексного регистра кольцевого буфера AR1[7:0] должна быть
выполнена, как для буфера с 32-разрядными выборками. Общее число 16разрядных слов в буфере N + 1. Адрес последнего 16-разрядного слова на
единицу меньше, т.е. N. Адрес последнего двойного слова еще на единицу
меньше, т.е. N – 1.
Рассмотренные принципы организации вычислений графически проиллюстрированы на рис. 7.5.
Диаграмма последовательной загрузки выборок в предварительно очищенный кольцевой буфер фильтра показана на рис. 7.6.
Прежде чем привести ассемблерный текст функции, выполняющей расчет КИХ-фильтра, необходимо описать соглашения по умолчанию, установленные разработчиками транслятора с языка высокого уровня С/С++ для взаимодействия программ на языке С/С++ и модулей, написанных на
Ассемблере.
Контрольные вопросы
1. Найдите в справочнике по системе команд обычную (не сдвоенную) команду
умножения с накоплением MAC. Опишите алгоритм ее выполнения.
2. Сравните производительность команд MAC и DMAC. Есть ли смысл при реализации КИХ-фильтров пользоваться более сложной командой DMAC?
3. Какую ассемблерную команду выберите для реализации КИХ-фильтра, работающего с 32-разрядными коэффициентами и выборками в формате 1.31? Нужна ли в этом
случае младшая часть частичных произведений?
186
Рис. 7.6. Диаграмма последовательной загрузки выборок
187
7.2. ИНТЕРФЕЙС С ПРОГРАММОЙ НА АССЕМБЛЕРЕ,
ВЫЗЫВАЕМОЙ ИЗ СИ-ПРОГРАММЫ
Соглашения по умолчанию
Интерфейс с функцией, написанной на языке Ассемблер, может быть правильным только в том случае, если строго следовать соглашениям по вызову
функций, а также соглашениям по использованию регистров центрального
процессора. В этом случае из программы на С/С++ гарантируется:
доступ к переменным, определенным в программе на Ассемблере, и
вызов функций, реализованных на Ассемблере;
доступ к переменным и функциям, объявленным в программе на C/C++,
из программы на Ассемблере.
Следуйте строго рекомендациям, приведенным ниже:
• все функции, где бы они ни были написаны: в программе на языке
С/С++ или на языке Ассемблер — должны выполнять установленные соглашения;
• модификация специально выделенных регистров центрального процессора, обслуживающих процесс обмена между программными модулями,
должна быть заблокирована. К этим специальным регистрам относятся:
регистры-указатели XAR1, XAR2, XAR3 и указатель стека SP;
• если указатель стека используется нормальным образом (в соответствии с принятыми соглашениями), то специально заботиться о нем нет
необходимости. Ассемблерная функция свободна в использовании стека.
Она может записывать в стек любое число переменных, но должна следить
за тем, чтобы до возврата из процедуры все записанные переменные были
извлечены из стека. Это и есть обязательное условие корректной работы с
указателем стека SP. Например, если по каким-либо причинам ассемблерной
функции понадобится регистр-указатель XAR1 (в частности, для хранения
смещения верхней границы кольцевого буфера), то его текущее содержимое
до выполнения тела функции необходимо сохранить в стеке, а перед выходом из функции — извлечь из стека;
• любые другие регистры процессора, кроме специально зарезервированных XAR1, XAR2, XAR3, SP, могут свободно использоваться без необходимости сохранения их содержимого;
• если вызываете Си-функцию из программы на языке Ассемблер, то
загрузите аргументы в предназначенные для этого регистры и запишите в
стек оставшиеся аргументы в соответствии с тем, как описано в пункте
«Вызов функции» (см. с. 189). Те же самые соглашения работают и при
вызове функции на языке Си;
• так как при вызове функций на языке Си по умолчанию используется
специальный регистр адреса возврата счетчика команд RPC, то и в процедурах на Ассемблере должны использоваться команды вызова и возврата,
работающие с этим регистром: LCR и LRETR;
188
• переменные типа long и float должны сохраняться в памяти следующим образом: младшее значащее слово по младшему адресу, старшее — по
старшему адресу;
• структуры возвращаются так, как описано на с. 191 в п. 5 «Как отвечает
вызванная функция»;
• модуль на языке Ассемблер не должен использовать секцию .cinit ни
для каких других задач, кроме инициализации глобальных переменных. Нарушение таблицы инициализации посредством записи в секцию .cinit другой
информации может вызвать непредсказуемые последствия;
• компилятор С/C++ автоматически добавляет к каждому идентификатору предшествующий ему символ подчеркивания (_). Таким образом,
если хотите обратиться из модуля на языке Ассемблер к какому-либо объекту на языке Си, то должны перед именем этого объекта указать символ
подчеркивания. Например, если на языке С/C++ объект имеет имя x, то из
программы на Ассемблере к нему следует обратиться по имени _x. Это сделано для того, чтобы в программах на Ассемблере и С/С++ можно было
пользоваться одинаковыми идентификаторами без возникновения конфликта
имен;
• любой объект или функция, которые описаны в программе на языке
Ассемблер и которые должны быть доступны или вызываться из программы
на С/С++ должны быть объявлены как глобальные с помощью специальных
директив Ассемблера .global или .def. Эти директивы объявляют соответствующие символические имена «публично доступными», что позволяет компоновщику разрешать все ссылки на них из программы на языке C/C++;
• соответственно доступ из ассемблерной программы к функции на
языке С/С++ или какому-либо объекту должен сопровождаться объявлением
имени этой функции или объекта как внешнего имени с помощью директив
.global или .ref. В этом случае при трансляции ассемблерного файла будет
создана неразрешенная внешняя ссылка, разрешение которой выполнит уже
компоновщик (т.е. назначит фактический адрес).
Вызов функции
Если вызывающая функция производит вызов другой функции, то она
выполняет следующую последовательность действий:
1) содержимое некоторых регистров центрального процессора, которые
содержат значения переменных, не используемых в вызываемой функции,
однако необходимых для последующих расчетов после возврата результата
из вызываемой функции, должно быть сохранено в стеке, если вызываемая
функция может изменить значения этих регистров;
2) если вызываемая функция возвращает структуру, то вызывающая
функция выделяет место в памяти под эту структуру и передает адрес этого
места в вызываемую функцию в качестве первого аргумента. Обратите особое внимание на это правило, так как оно является базовым при создании
библиотечных функций на Ассемблере;
189
3) аргументы, передаваемые вызываемой функции, размещаются в регистрах и, если это необходимо, в стеке. При размещении аргументов в регистрах используется следующая схема:
а) если имеется несколько 32-разрядных аргументов (типа long или
float), то первый аргумент помещается в 32-разрядный аккумулятор
ACC(AH/AL). Все другие 32-разрядные аргументы или указатели функций размещаются в стеке в обратном порядке;
б) аргументы типа указателей (Pointer arguments) размещаются в
регистрах XAR4 и XAR5. Все другие указатели размещаются в стеке;
в) остающиеся 16-разрядные аргументы размещаются в следующей
последовательности, если это возможно: AL, AH, XAR4, XAR5;
4) некоторые оставшиеся аргументы, которые не были размещены в
регистрах, размещаются в стеке в обратном порядке. Таким образом, самый
левый аргумент будет записан в стек самым последним. Все 32-разрядные
аргументы выравниваются в стеке по четным адресам;
5) аргументы типа структуры передаются адресами этих структур. Вызываемая функция должна сделать локальную копию этой структуры.
Для функций, которые объявлены с пропусками, т.е. функций, которые
вызываются с варьируемым числом аргументов, соглашения несколько
модифицируются. Последний, явно задекларированный аргумент размещается в стек так, что его адрес в стеке может использоваться как ссылка для
доступа к другим незадекларированным аргументам.
Пример вызова функции, который показывает, где размещаются аргументы, представлен ниже:
func1 (int a, int b, long c);
XAR4 XAR5 AH/AL
func1 (long a, int b, long c);
AH/AL XAR4 stack
vararg (int a, int b, int c,...)
AL AH stack
Вызванная функция возвращает управление вызывающей функции по
команде «длинного» возврата из подпрограммы LRETR. В счетчик команд
записывается значение из регистра RPC (это и есть адрес возврата), далее из
стека в регистр RPC восстанавливается предыдущий адрес возврата.
Как отвечает вызванная функция
Вызванная функция выполняет следующую последовательность действий:
1) если вызванная функция модифицирует регистры XAR1, XAR2 или
XAR3, то она должна сохранить их значения, так как вызывающая функция
предполагает, что значения в этих регистрах не должны меняться. Значения
во всех других регистрах могут быть модифицированы без сохранения их
содержимого;
2) вызванная функция резервирует достаточно места в стеке для размещения всех своих локальных переменных, а также аргументов функции. Это
размещение выполняется в самом начале тела функции добавлением константы к текущему значению указателя стека;
190
3) если вызванная функция ожидает аргумент типа структуры, то она
получит взамен указатель на эту структуру. Если записи в структуру делаются из вызванной функции, то должно быть зарезервировано место в стеке
под локальную копию этой структуры и структура должна быть скопирована
с помощью указателя на структуру, переданного в функцию. Если записи в
структуру не делаются, то доступ к структуре из вызванной функции может
быть выполнен косвенно через аргумент-указатель на структуру. Необходимо позаботиться о том, чтобы функция, которая принимает структуру в
качестве аргумента, была правильным образом описана как с точки зрения,
где она вызывается (так, чтобы в качестве аргумента был передан адрес
структуры), так и с точки зрения, где она декларируется (так, чтобы функция
выполнила копирование структуры в локальную копию структуры);
4) далее вызванная функция выполняет собственно код функции;
5) затем вызванная функция возвращает результат. Он помещается в один
из регистров процессора в соответствии со следующими соглашениями:
• 16-разрядное целое значение — в младшее слово аккумулятора AL;
• 32-разрядное целое значение — в аккумулятор ACC;
• 16- или 22-разрядный указатель в регистр XAR4.
Если функция возвращает структуру, то вызывающая функция выделяет
место в памяти под эту структуру и передает в качестве параметра адрес
области возврата через регистр XAR4. Для возврата структуры вызванная
функция копирует структуру в блок памяти, указатель на который передан с
помощью специального аргумента. В этом случае вызывающая функция
должна фактически сообщить вызываемой функции, куда вернуть структуру.
Например, в операторе s = f(x) s является структурой и функция f должна
вернуть структуру. На самом деле вызывающая функция должна передать в
качестве параметра в вызываемую функцию адрес структуры, т.е. вызов должен быть оформлен следующим образом f(&s,x). Функция f в процессе
выполнения скопирует возвращаемую структуру данных непосредственно в
s, выполняя автоматическое присваивание. Если вызывающая функция не
будет использовать возвращенное значение структуры, то в качестве первого
аргумента должно быть передано нулевое значение адреса. Это предписывает вызываемой функции не копировать возвращаемую структуру. Следует
позаботиться о том, чтобы правильно объявить функцию, которая возвращает структуру, как с точки зрения, откуда она вызывается (так, чтобы был
передан дополнительный аргумент), так и с точки зрения, где она описывается (чтобы функция «знала» о необходимости копирования результата);
6) вызванная функция высвобождает ранее зарезервированный локальный фрейм в стеке, вычитая из значения указателя стека SP ту же самую
величину, которая была добавлена при резервировании локального фрейма;
7) вызванная функция восстанавливает значения во всех регистрах, сохраненные на первом шаге (XAR1, XAR2 или XAR3).
Пример, представленный на лист. 7.1, иллюстрирует, как функция на языке
Си по имени main вызывает ассемблерную функцию по имени asmfunc. Эта
функция asmfunc принимает свой единственный параметр (аргумент), добавляет к нему константу 5, сохраняет полученное в глобальной переменной Си,
названной gvar, и возвращает результат в вызывающую функцию.
191
В этом примере объявление extern предписывает компилятору использовать соглашение по использованию имен, принятое для программ на Си, т.е.
все глобальные Си-имена будут иметь то же самое имя с дополнительным
предшествующим символом подчеркивания. Таким образом, когда компилятор пытается разрешить ссылку на глобальное имя asmfunc, он предполагает, что программист на Ассемблере дал подпрограмме точно такое же имя,
но с символом подчеркивания и описал его в качестве «общедоступного»
(глобального имени) директивой .global _asmfunc.
Лист. 7.1. Фрагмент программы на языке Си
extern int asmfunc(int a);/* Внешняя функция asmfunc,
получающая один целый параметр a и возвращающая целый
результат. */
int gvar = 0;/* Глобальная переменная gvar,
инициализируемая 0 */
}
void main(){
int i = 5;/* Локальная переменная i,
инициализируемая 5-ой */
i = asmfunc(i);/* Вызов ассемблерной функции с
передачей ей значения i. */
}
Лист. 7.2. Фрагмент программы на языке Ассемблер
/* Фрагмент программы на языке Ассемблер: */
.global _gvar
;Переменная _gvar декларируется
;как внешняя.
.global _asmfunc ;Имя функции _asmfunc декларируется
;как глобальное.
_asmfunc:
MOVZ DP, #_gvar ;Указатель текущей страницы
;на глобальную переменную _gvar.
ADDB AL, #5
;Добавить 8-разрядное целое со знаком
;(+5) к переданному через аккумулятор
;значению аргумента i.
MOV
@_gvar, AL ;Сохранить в глобальной переменной
;_gvar.
LRETR
;Возврат из подпрограммы.
Параметр i передается в процедуру через регистр AL.
Фактически ассемблерная функция делает значительно больше, чем это
можно понять, глядя на программу, составленную на языке Си. Она берет
переданный ей через регистр AL аргумент типа целое и добавляет к нему
константу (+5). Результат остается в AL в качестве возвращаемого параметра. Однако функция еще и записывает его в глобальную переменную
gvar, что абсолютно не отражено в исходом коде на Си.
192
7.3. ИССЛЕДОВАНИЕ РАБОТЫ МОДУЛЯ ФИЛЬТРА
С КОНЕЧНОЙ ИМПУЛЬСНОЙ ХАРАКТЕРИСТИКОЙ
Мы подготовили проект lab7 со всеми необходимыми файлами для исследования работы фильтра с конечной импульсной характеристикой и даже
сохранили рабочее пространство проекта (окружение проекта) в файле
lab7\debug\fir16.wks. Остается открыть этот файл, скомпилировать проект и
загрузить его в микроконтроллер. После запуска программы в режиме
ANIMATE в нижнем графическом окне увидите сигнал задания для фильтра, а в верхнем графическом окне — результат работы фильтра (рис. 7.7).
Попытаемся разобраться в структуре и содержании файлов проекта.
Откроем заголовочный файл fir.h. В нем содержится описание нового типа
переменной структуры цифрового фильтра с конечной импульсной характеристикой FIR16 (лист. 7.3).
Рис. 7.7. Рабочая область программы в CCS
193
Лист. 7.3. Содержание заголовочного файла fir.h
typedef struct {
long *coeff_ptr;
/* Указатель на коэффициенты
фильтра. */
long *dbuffer_ptr;
/* Указатель на буфер выборок. */
int cbindex;
/* Верхняя граница кольцевого буфера. */
int order;
/* Порядок фильтра. */
int input;
/* Входное значение (новая
выборка). */
int output;
/* Выходное значение фильтра. */
void (*init)(void *); /* Указатель на функцию инициализации. */
void (*calc)(void *); /* Указатель на функцию
обработки */
} FIR16;
Для создания нескольких экземпляров модулей цифровой фильтрации
необходимо просто объявить нужное число переменных типа FIR16. Таким
образом, в одной и той же программе могут работать сразу несколько фильтров разного порядка, с разными настройками (коэффициентами), каждый из
которых будет иметь свой собственный кольцевой буфер выборок и таблицу
коэффициентов. При этом алгоритм работы всех фильтров унифицируется.
По существу обработчик является общим. Каждый раз он будет работать с
теми параметрами, которые переданы ему при вызове.
Дальше описывается так называемый инициализатор структуры модуля фильтра FIR16_DEFAULTS, который обеспечивает инициализацию значений переменных типа FIR16 и настраивает указатели на нужные функции (лист. 7.4). Вслед за
этим приводится описание прототипов ассемблерных функций (лист. 7.5).
Лист. 7.4. Инициализатор структуры модуля фильтра
#define FIR16_DEFAULTS { (long *)NULL, \
(long *)NULL, \
0, \
50, \
0,\
0,\
(void (*)(void *))FIR16_init,\
(void (*)(void *))FIR16_calc}
Лист. 7.5. Прототипы ассемблерных функций
void FIR16_init(void *);
void FIR16_calc(void *);
В файле содержится также несколько инициализаторов коэффициентов
фильтров для наиболее часто применяемых фильтров: низкочастотного,
высокочастотного и полосового.
194
Для экспериментов предлагаем использовать фильтры 50-го порядка,
у которых число коэффициентов равно 51. Так как алгоритм фильтрации,
реализованный с помощью команды двойного умножения с накоплением,
требует объединения коэффициентов в длинные слова, то порядок фильтра
искусственно увеличивается на единицу и число слов с коэффициентами
оказывается равным (51 + 1)/2 = 26.
Помните, что каждый из коэффициентов представлен в формате 1.15
(в относительных единицах). Два коэффициента объединены в одно длинное
слово и используются в инициализаторе. Как рассчитать коэффициенты цифрового фильтра с помощью специальных компьютерных программ, если задана
частота дискретизации фильтра, его тип и полоса пропускания, показано ниже.
Лист. 7.6. Инициализатор коэффициентов фильтра
#define FIR16_LPF50 {\
1292,4261126,4654326,5440731,6751414,8455303,10749008,\
13501458,16712654,20382597,24445752,28902122,33620635,38601293,\
43713025,48890297,54002037,59048247,63832319,68288718,\
72351908,75956353,79036519,81461331,83230791,84279361}
Инициализатор коэффициентов низкочастотного фильтра с полосой пропускания 100 Гц и частотой дискретизации 10 кГц приведен в лист. 7.6. Амплитудно-частотная (АЧХ) и фазочастотная (ФЧХ) характеристики такого
фильтра представлены на рис. 7.8.
Теперь рассмотрим файл fir.c, в котором используется модуль низкочастотного фильтра 50-го порядка LPF, коэффициенты которого рассчитаны и находятся в заголовочном файле fir.h. Фильтр обеспечивает частоту среза 100 Гц.
Рис. 7.8. АЧХ и ФЧХ фильтра низкой частоты
195
Лист. 7.7. Пример работы с КИХ-фильтром
/*======================================
* Работа 7.
* Пример использования фильтра FIR16.
*=====================================*/
#include "IQMathLib.h"
#include "fir.h"
#define FIR_ORDER 50 /*Порядок фильтра. */
// Создание экземпляра модуля цифровой фильтрации fir
FIR16 fir= FIR16_DEFAULTS;
/* Определение буфера выборок для фильтра 50-го порядка и
размещение в секции "firldb". */
#pragma DATA_SECTION(dbuffer,"firldb");
long dbuffer[(FIR_ORDER+2)/2];
/* Определение массива постоянных коэффициентов
и его инициализация. */
long coeff[(FIR_ORDER+2)/2]= FIR16_LPF50;
/* Определяем переменные.*/
float SigInFreq = 100.0; // Частота входного сигнала, Гц.
float FiltPer = 0.0001; // Время дискретизации фильтра, с.
long l_counter = 0; // Счетчик для контроля работы программы.
int sin_value = 0; // Значение синуса.
int sin_filter = 0; // Значения отфильтрованного сигнала.
/* Начало основной программы. */
void main()
{ /* Инициализация модуля фильтра. */
fir.order=FIR_ORDER; // Настраиваем порядок фильтра.
fir.dbuffer_ptr= dbuffer;
//Передаем адрес буфера выборок.
fir.coeff_ptr=coeff;
//Передаем адрес массива коэффициентов.
fir.init(&fir); //Вызываем функцию инициализации.
/*Основной цикл программы.*/
while(1)
{
l_counter++; // Инкрементируем счетчик.
// Определение шага дискретизации угла.
Teta+= _IQ16mpy(_IQ16(SigInFreq),_IQ16(FiltPer));
// Т.к. значение sin имеет формат 16.16, то
// входной сигнал приводим к формату 1.15.
sin_value =_IQ16sinPU(Teta)>>1;
fir.input = sin_value;
//Передаем новое значение на вход фильтра
fir.calc(&fir); //Вызываем функцию подсчета.
sin_filter = fir.output;
//Передаем значение в программу.
}
}
196
Практическая работа
1. Самостоятельно получите осциллограммы работы фильтра низкой частоты, представленные на рис. 7.9.
а)
б)
в)
Рис. 7.9. Работа фильтра:
а—в — на частоте 50, 100 и 200 Гц соответственно
197
2. В каких единицах измеряется время на осциллограммах? Соответствует ли период входного и выходного сигнала заданной частоте?
3. Убедитесь, что амплитуда и угол отставания отфильтрованного сигнала
от входного соответствуют расчетным АЧХ и ФЧХ фильтра (см. рис. 7.8).
4. Измените код программы таким образом, чтобы получить входной
синусоидальный сигнал с шумом. Исследуйте степень подавления шума на
разных частотах.
7.4. ИСПОЛЬЗОВАНИЕ ПАКЕТА MatLab
ДЛЯ РАСЧЕТА КОЭФФИЦИЕНТОВ ФИЛЬТРА
Существует большое разнообразие цифровых фильтров (низкой частоты,
высокой частоты, полосовые и т.п.). Для удобства пользователей, применяющих библиотеку цифровой фильтрации в своих приложениях, при участии
фирмы Texas Instruments разработаны специальные приложения к пакету
MatLab, позволяющие автоматизировать процесс расчета коэффициентов
для фильтров с заданными выходными характеристиками. Одно из таких
приложений — ezfir16_rus.m.
Для работы с программой запустите приложение MatLab, измените текущую рабочую папку на c:\ti\user\lab7\matlab\ezFIR и загрузите проект
ezfir16_rus.
Программа предложит ввести необходимые данные и условия для расчета
фильтра:
1) порядок фильтра;
2) тип фильтра;
3) вариант фильтра;
4) частоту дискретизации (выборки);
5) частоту среза или полосу частот;
6) название файла для сохранения рассчитанных коэффициентов (необходимо указывать расширение для текстового файла, например *.txt).
Интерфейс программы показан на рис. 7.10. Программа генерирует файл
из коэффициентов для программного модуля фильтра, готовый к применению, а также АЧХ и ФЧХ фильтра.
Практическая работа
1. Выполните генерацию коэффициентов фильтра низкой частоты 50-го
порядка с полосой пропускания 100 Гц и частотой дискретизации 10 кГц.
2. Выполните генерацию коэффициентов высокочастотного фильтра (пропускает только высокие частоты) 50-го порядка с частотой дискретизации
20 кГц и частотой среза 4000 Гц. Объясните вид АЧХ и ФЧХ фильтра, приведенный на рис. 7.11.
198
Рис. 7.10. Интерфейс программы расчета коэффициентов фильтра в среде MatLab
Рис. 7.11. АЧХ и ФЧХ высокочастотного фильтра
3. Рассчитайте параметры полосового фильтра с полосой пропускания
4000—6000 Гц, работающего с частотой дискретизации 20 кГц. Объясните
вид АЧХ и ФЧХ этого фильтра, представленный на рис. 7.12, а.
4. Рассчитайте параметры полосового фильтра задержки («режекторного»)
исключающего пропускание сигнала в диапазоне частот 4000—6000 Гц.
Объясните АЧХ и ФЧХ фильтра, изображенные на рис. 7.12, б.
199
а)
б)
Рис. 7.12. АЧХ и ФЧХ полосового фильтра:
а — с полосой пропускания; б — с полосой задержки
5. Выберите цифровой фильтр, который позволит подавить электромагнитную помеху на частоте ШИМ 5 кГц во входном сигнале с датчика тока
фазы двигателя, максимальная частота которого не превышает 500 Гц.
Отладьте программу цифровой фильтрации.
Контрольные вопросы
1. Приведите конкретные примеры из области привода и силовой электроники необходимости использования различных типов цифровых фильтров.
2. Можно ли одновременно в приложении использовать несколько фильтров низкой
частоты с различной полосой пропускания? Будут ли они иметь общую программу-обработчик? Общий буфер выборок?
3. Почему буфер выборок располагается в специально выделенной для этого секции
данных, а все остальные переменные — в общей памяти данных?
200
7.5. ФИЛЬТРЫ 16- И 32-РАЗРЯДНЫЕ
С БЕСКОНЕЧНОЙ ИМПУЛЬСНОЙ ХАРАКТЕРИСТИКОЙ
Общая информация о рациональной структуре фильтра.
Биквадратные секции
В общем случае фильтр с бесконечной импульсной характеристикой IIR,
представленный на рис. 7.13, описывается следующим разностным уравнением:
y(n) = –
N
N
bk x ( n – k )
∑ a k y ( n – k ) + k∑
k=0
=0
(7.6)
или эквивалентной передаточной функцией:
N
∑ bk z
k=0
–k
-.
H ( z ) = ---------------------N
–k
a
z
∑ k
(7.7)
k=0
Проблема реализации подобных фильтров на базе арифметики конечной
точности заставляет разрабатывать различные структуры фильтров. Методы
прямой реализации фильтров IIR чрезвычайно чувствительны к квантованию параметров и в общем случае не рекомендуются для использования в
практических приложениях. Можно показать, что разделение передаточной
функции на секции малого порядка с последующим параллельным соединением секций или их каскадированием значительно снижает чувствительность к квантованию коэффициентов фильтра. Таким образом, рекомендуется использовать каскадную конфигурацию из секций второго порядка
(Second order section — SOS). Такие секции известны как биквадратные секции (Biquads). В соответствии с этим фильтры получили название биквадратных фильтров. На следующей блок-схеме показана каскадная реализация IIR-фильтра четвертого порядка на основе двух биквадратных секций.
x(n)
d1(n)
+
b01
+
y1(n)
d2(n)
+
b11
–a12
y(n)
b12
d2(n – 1)
d1(n – 1)
z–1
z–1
–a21
+
z–1
z–1
–a11
b02
b21
d1(n – 2)
–a22
b22
d2 (n – 2)
Рис. 7.13. Блок-схема фильтра с бесконечной импульсной характеристикой
201
Рассмотрим принцип реализации биквадратного фильтра более подробно
на примере одной секции второго порядка.
Разностное уравнение для БИХ-фильтра второго порядка имеет вид
y(n) = b0x(n) + b1x(n – 1) + b2x(n – 2) – a1y(n – 1) – a2 y(n – 2).
(7.8)
Выполняя дискретное преобразование Лапласса, получаем следующую
дискретную передаточную функцию:
–1
–2
y [ z ] b0 + b1 z + b2 z
H [ z ] = ---------- = ----------------------------------------------- .
–1
–2
x[z]
1 + a1 z + a2 z
(7.9)
Этой передаточной функции соответствует прямая структура БИХ-фильтра второго порядка, показанная на рис. 7.14, а.
Структура содержит четыре элемента запаздывания на такт квантования
и, как уже указывалось, при фиксированной разрядной сетке для представления коэффициентов может не обеспечивать требуемую точность расчетов.
Представим дискретную передаточную функцию фильтра в виде произведения двух передаточных функций:
–1
–2
y [ z ] b0 + b1 z + b2 z
H [ z ] = ---------- = ----------------------------------------------- =
–1
–2
x[z]
1 + a1 z + a2 z
⎛
⎞
–1
–2
1
= H 1 [ z ]H 2 [ z ] = ⎜ ---------------------------------------------⎟ b 0 + b 1 z + b 2 z
.
–
1
–
2
⎝1 + a z + a z ⎠
(
1
x(n)
b0
2
y(n)
+
z–1
z–1
–a1
b1
x(n – 1)
z–1
y(n – 1)
z–1
–a2
b2
x(n – 2)
)
y(n – 2)
а)
x(n)
d(n)
+
b0
z–1
d(n – 1)
–a1
z–1
–a2
d(n – 2)
b1
b2
б)
Рис. 7.14. Структура БИХ-фильтра:
а, б — второго и четвертого порядков
202
+
y(n)
(7.10)
Первая передаточная функция соответствует рекурсивному фильтру (РФ),
описываемому разностным уравнением:
d(n) = x(n) – a1d(n – 1) – a2d(n – 2),
(7.11)
а вторая передаточная функция — нерекурсивному фильтру (НРФ), описываемому разностным уравнением:
y(n) = b0d(n) + b1d(n – 1) + b2d(n – 2).
(7.12)
В соответствии с этими двумя разностными уравнениями фильтр может
иметь каноническую структуру, содержащую только два элемента задержки
(вместо четырех).
Как видно, структура фильтра четвертого порядка, представленная на
рис. 7.14, б, состоит из двух каскадно-включенных фильтров второго
порядка. За счет каскадирования подобных структур можно реализовать
биквадратный фильтр любого порядка. Коэффициенты имеют двойной
индекс. Первое число относится к номеру коэффициента внутри биквадратной секции, а второе — к номеру секции.
Коэффициенты биквадратных секций каскадного фильтра могут быть
сгенерированы в пакете MatLab на основе требуемой спецификации фильтра (полосы пропускания, частоты среза и т.п.). Несмотря на то что общий
коэффициент усиления входного сигнала не может превысить единицу,
коэффициенты усиления соседних узлов в биквадратных секциях могут
существенно различаться, что определяется исключительно требуемыми
параметрами фильтра. Пользователь должен позаботиться об ограничении
коэффициентов усиления соседних узлов до единицы во избежание возможных переполнений.
Первым шагом является идентификация коэффициентов усиления каждой
биквадратной секции относительно входного сигнала, а затем масштабирование входа каждой подсекции в целях исключения переполнений. Масштабирование входного сигнала секции эквивалентно масштабированию коэффициента b в предыдущей секции.
Специалистами TI разработано приложение на MatLab, позволяющее
проектировать IIR-фильтры без переполнений на основе сформулированного
алгоритма. Отмасштабированные коэффициенты биквадратных секций (SOS),
коэффициент масштабирования входа (Input Scaling Factor), Q-формат,
используемый для представления коэффициентов, и число биквадратных
секций сохраняются в файле, который применяется для инициализации
фильтра IIR5BIQ16 и IIR5BIQ32. Коэффициент масштабирования входа
ограничивает коэффициент передачи первого узла первой биквадратной секции единицей. Пользователь может использовать приложения eziir16 и
eziir32 для генерации коэффициентов фильтра.
Схема расположения коэффициентов биквадратного фильтра в памяти
программ и буфера задержки в памяти данных представлена на рис. 7.15.
Данные для каждой секции располагаются в виде последовательного мас203
Буфер 16-разрядных
коэффициентов
coeff_ptr
a21
Буфер задержки
(16-разрядный)
Младший адрес
a11
dk(n – 1)
dk(n – 2)
b21
b11
d2(n – 1)
b01
d2(n – 2)
a22
a12
d1(n – 1)
dbuffer_ptr
b22
d1(n – 2)
b12
b02
a2k
a1k
b2k
b1k
b0k
Старший адрес
Рис. 7.15. Схема расположения коэффициентов БИХ-фильтра в памяти
сива коэффициентов a2k, a1k, b2k, b1k, b0k, где k — номер биквадратной секции. Каждая секция имеет в буфере выборок две ячейки для хранения переменных dk(n – 1) и dk(n – 2). После выборки этих переменных в процессе
расчета фильтра должны выполняться операции копирования: dk(n – 1) →
→ dk(n – 2); dk(n) → dk(n – 1).
Отличие 32-разрядного фильтра от 16-разрядного состоит только в разрядности коэффициентов и переменных. Коэффициенты и переменные
буфера выборок являются длинными словами. Младшее слово располагается
по меньшему адресу.
204
Исследование модуля фильтра
с бесконечной импульсной характеристикой (IIR)
Модуль реализует каскадный биквадратный фильтр с бесконечной
импульсной характеристикой (IIR filter) на основе 16- и 32-разрядной линии
задержки.
Откройте рабочее пространство lab7/debug/iir.wks. Скомпилируйте проект и загрузите его в микроконтроллер (рис. 7.16).
Рис. 7.16. Рабочая область программы в CCS
В заголовочном файле iir.h описана структура объектов IIR5BIQ16 и
IIR5BIQ32:
Лист. 7.8. Содержание заголовочного файла iir.h
//Определение структуры модуля фильтра IIR5BIQ32.
typedef struct {
void (*init)(void *); //Указатель на функцию инициализации.
void (*calc)(void *); //Указатель на адрес вызова функции.
long *coeff_ptr;
//Указатель на коэффициенты фильтра.
long *dbuffer_ptr;
//Указатель на буфер задержки.
int nbiq;
// Число биквадратных секций фильтра Q0.
int input;
// Последняя входная выборка данных Q15.
long isf;
// Коэффициент масштабирования входа.
205
Окончание лист. 7.8
int qfmat;
/* Q формат коэффициентов Q0. */
int output;
/* Выход фильтра Q14. */
}IIR5BIQ16;
//Определение структуры модуля фильтра IIR5BIQ32.
typedef struct {
void (*init)(void *); //Указатель на функцию инициализации.
void (*calc)(void *); //Указатель на адрес вызова функции.
long *coeff_ptr;
// Указатель на коэффициенты фильтра.
long *dbuffer_ptr;
// Указатель на буфер задержки.
int nbiq;
/* Число биквадратных секций фильтра Q0. */
int input;
/* Последняя входная выборка данных Q15. */
long isf;
/* Коэффициент масштабирования входа. */
long output32; /* Выход фильтра в формате Q30. */
int output16;
/* Выход фильтра в формате Q14. */
int qfmat;
/* Q формат коэффициентов Q0. */
}IIR5BIQ32;
Определение модуля создается как специальный тип данных. Это удобно
для организации интерфейса с модулем фильтра IIR16. Для того чтобы
создать множество экземпляров модуля, необходимо просто объявить нужное число переменных типа IIR5BIQ16. Таким образом, в одной и той же
программе могут работать сразу несколько программ фильтрации.
Описание констант приведено ниже:
Лист. 7.9. Инициализатор структуры модулей фильтров
#define IIR5BIQ16_DEFAULTS { (void (*)(void
*))IIR5BIQ16_init,\
(void (*)(void *))IIR5BIQ16_calc,\
(int *)NULL, \
(int *)NULL, \
0, \
0, \
0, \
0, \
0}
#define IIR5BIQ32_DEFAULTS { (void (*)(void
*))IIR5BIQ32_init,\
(void (*)(void *))IIR5BIQ32_calc, \
(long *)NULL, \
(long *)NULL, \
0, \
0, \
0, \
0, \
0, \
0}
206
IIR5BIQ16_DEFAULTS и IIR5BIQ32_DEFAULTS — структуры символьных констант для инициализации модулей IIR5BIQ16 è IIR5BIQ32.
Описываются прототипы функций для инициализации фильтра IIR и для
вычисления выходного значения фильтра:
Лист. 7.10. Прототипы ассемблерных функций
//Прототипы для функций
void IIR5BIQ16_calc(void *);
void IIR5BIQ32_calc(void *);
void IIR5BIQ16_init(IIR5BIQ16 *);
void IIR5BIQ32_init(IIR5BIQ32 *);
Приводятся структуры с рассчитанными коэффициентами для низкочастотного, высокочастотного и полосового 16- и 32-разрядных фильтров. Пример такой структуры для низкочастотного фильтра показан ниже:
Лист. 7.11. Инициализатор коэффициентов фильтра
/* Коэффициенты для низкочастотного 16-разрядного
фильтра */
#define IIR16_LPF_COEFF {-14150,30370,4469,8937,4469}
#define IIR16_LPF_ISF150
#define IIR16_LPF_NBIQ1
#define IIR16_LPF_QFMAT14
Рассмотрим структуру основного файла iir.c, изображенную ниже:
Лист. 7.12. Пример работы с БИХ-фильтром
#include "iir.h"
#include "IQmathlib.h"
/* Создание экземпляра модуля IIR5BIQD16 */
IIR5BIQ16 iir=IIR5BIQ16_DEFAULTS;
/* Определение буфера задержек для каскадного IIR фильтра с 6 биквадратными секцями и размещение его (буфера)
в секции "iirfilt". */
#pragma DATA_SECTION(dbuffer,"iirfilt");
int dbuffer[2*IIR16_LPF_NBIQ];
/* Определение массива коэффициэнтов. */
const int coeff[5*IIR16_LPF_NBIQ]=IIR16_LPF_COEFF;
/* Определение переменных.*/
float SigInFreq = 100.0; //Частота входного сигнала, Гц.
float FiltPer = 0.0001; //Период дискретизации фильтра, с.
_iq Teta =0; //Электрический угол в относительных единицах
long l_counter = 0; //Счетчик для контроля работы программы
int sin_value = 0; // Значение синуса входного сигнала
207
Окончание лист. 7.12
int sin_filter = 0; //Значение выходного отфильтрованного сигнала.
/* Начало основной программы. */
void main(void)
{/* Инициализация модуля фильтра. */
iir.dbuffer_ptr=dbuffer;
iir.coeff_ptr=(int *)coeff;
iir.qfmat=IIR16_LPF_QFMAT;
iir.nbiq=IIR16_LPF_NBIQ;
iir.isf=IIR16_LPF_ISF;
iir.init(&iir);
/* Основной цикл программы. */
while(1)
{
l_counter++; // Инкрементируем счетчик.
//Определение приращения электрического угла
Teta+= _IQ16mpy(_IQ16(SigInFreq),_IQ16(FiltPer));
//Так как выходное значение функции sin имеет
//формат 16.16, приводим входной сигнал к формату 1.15
sin_value =_IQ16sinPU(Teta)>>1;
iir.input = sin_value;
iir.calc(&iir);
sin_filter = iir.output;
}
} /* Конец программы. */
Обратите внимание на то, что у 32-разрядного фильтра две выходные
переменные 16- и 32-разрядная: output16 и output32.
Практическая работа
1. Исследуйте приложение MatLab для генерации коэффициентов IIRфильтров.
2. Пользуясь описанной в предыдущей главе технологией, убедитесь в
правильности работы IIR-фильтра низкой частоты.
Контрольные вопросы
1. К какому из типов фильтров (КИХ, БИХ) можно отнести простейшие ПИ-, ПИД-,
ПИИ-регуляторы?
2. Какова последовательность работы с библиотечными функциями цифровой фильтрации, написанными на Ассемблере?
208
Глава
8
ТЕХНОЛОГИЯ РАЗРАБОТКИ И ОТЛАДКИ
СИСТЕМЫ ЦИФРОВОГО УПРАВЛЕНИЯ
ДВИГАТЕЛЕМ ПОСТОЯННОГО ТОКА
8.1. АППАРАТНАЯ СТРУКТУРА СИСТЕМЫ УПРАВЛЕНИЯ
Аппаратная структура системы управления двигателем постоянного тока
(ДПТ) с независимым возбуждением, построенная на базе классического
мостового шестиключевого инвертора напряжения на IGBT-транзисторах
или интеллектуальных силовых модулях представлена на рис. 8.1. Две
стойки моста используются для реализации мостового четырехключевого
реверсивного широтно-импульсного преобразователя напряжения для управления по цепи якоря (далее ПЯ) и одна стойка (нижний силовой ключ и верхний обратный диод) — для реализации нереверсивного ШИМ-преобразователя для управления двигателем по цепи возбуждения (далее ПВ). Таким
Udc
le_зад
ПИ
Силовой
трехфазный
инвертор
Модуль
ШИМ
la_зад
ПИ
le
la
ДПТ
Рис. 8.1. Аппаратная структура системы управления
209
образом, на базе классического инвертора напряжения создается сразу два
преобразователя для двухзонной (с ослаблением поля и без) четырехквадрантной системы управления ДПТ, которая применяется обычно в станочных электроприводах.
Система управления реализуется на сигнальном микроконтроллере и
обеспечивает формирование ШИМ-сигналов управления четырьмя ключами
ПЯ и одним ключом ПВ на базе многоканального ШИМ-генератора менеджера событий. Несущая частота ШИМ высокая — до 5—10 кГц. Обратные
связи по току якоря (далее индекс «а») и току возбуждения (далее индекс
«е») реализуются с помощью гальванически развязанных датчиков тока типа
«ЛЕМ», выходные сигналы которых вводятся в контроллер через АЦП. Регуляторы токов программно реализованные.
На схеме не показаны датчик скорости, в качестве которого можно
использовать тахогенератор или импульсный датчик положения, и регулятор
скорости. В первом случае для ввода сигнала с тахогенератора используется
АЦП, а во втором — «квадратурный декодер» менеджера событий. Регулятор скорости, как и регуляторы токов, реализуется программно.
Все расчеты в системе управления должны выполняться за один период
ШИМ, который является интервалом дискретизации. Модель объекта управления может иметь тот же самый интервал квантования. В данной главе в
целях упрощения системы к объекту управления отнесены кроме двигателя
и силовых преобразователей также датчики токов и скорости.
8.2. МАТЕМАТИЧЕСКАЯ МОДЕЛЬ ДВИГАТЕЛЯ
ПОСТОЯННОГО ТОКА В ОТНОСИТЕЛЬНЫХ ЕДИНИЦАХ
Математическое описание двигателя постоянного тока в физических
координатах, без учета насыщения в цепи возбуждения, при управлении
напряжением по цепи возбуждения и по цепи якоря хорошо известно:
di
U в = R в i в + L в ------в- ;
dt
(8.1)
di
U я = R я i я + L я ------я- + ( kΦ )ω ;
dt
(8.2)
------- ;
M – M с = J Σ dω
dt
(8.3)
M = (kΦ)iя;
(8.4)
( kΦ ) ном
kΦ = --------------------i .
i в ном в
(8.5)
Первое уравнение описывает контур возбуждения машины, второе —
цепь якоря, третье — является уравнением движения, четвертое — определяет связь между током якоря и электромагнитным моментом машины,
210
а пятое — между потоком и током возбуждения. В общем случае последнее
уравнение может быть нелинейным и задавать так называемую кривую
намагничивания машины. Модель двигателя постоянного тока имеет
несколько параметров:
Rв — активное сопротивление цепи возбуждения;
Lв — индуктивность цепи возбуждения;
Rя — активное сопротивление цепи якоря;
Lя — индуктивность цепи якоря;
(kΦ)ном — номинальное значение коэффициента передачи между током
якоря iя и электромагнитным моментом M, между электродвижущей силой E
и скоростью ω;
JΣ — суммарный момент инерции двигателя и присоединенных масс;
Mс — момент статической нагрузки.
Смоделируем работу двигателя непосредственно в сигнальном микроконтроллере. Для повышения скорости вычислений откажемся от использования арифметики с плавающей точкой в пользу арифметики с фиксированной
точкой, поддерживаемой библиотекой IQMath. При этом обязательным требованием является переход от физических единиц к относительным.
Удобно за базовые величины принять такие, которые характеризуют
работу двигателя в номинальном режиме. Так как кратности превышения
номинальных переменных машины (тока якоря, тока возбуждения, момента,
скорости) не превышают 5—7, то для представления переменных можно
использовать формат 4.28, обеспечивающий наибольшую точность расчетов.
Формат 8.24 позволяет получить несколько больший и гарантированно
достаточный динамический диапазон представления переменных без существенного ухудшения точности. В качестве базового времени удобно выбрать
единицу физического времени — 1 с. При этом все процессы в модели будут
протекать в том же масштабе времени, что и в реальном двигателе.
Итак, за базовые значения переменных в цепи возбуждения принимаются:
Uв баз = Uв ном;
(8.6)
iв баз = iв ном;
(8.7)
tбаз = Tбаз = 1 с.
(8.8)
Если выбрать базовые значения сопротивления и индуктивности в контуре возбуждения через уже определенные базовые величины:
U в баз
R в баз = -------------- ;
i в баз
(8.9)
U в баз T баз
L в баз = ------------------------- ,
i в баз
(8.10)
211
то уравнение цепи возбуждения в относительных единицах не будет отличаться от соответствующего уравнения в физических единицах, за исключением символа «*», характеризующего относительные переменные:
*
Uв
=
* *
Rв iв
+
*
* di в
L в ------- ,
*
(8.11a)
dt
или
*
Uв
=
*⎛ *
Rв ⎜ iв
⎝
+
*
* di в⎞
T в -------⎟
*
dt ⎠
,
(8.11б)
*
где R в — безразмерное значение активного сопротивления цепи возбужде*
ния; L в — безразмерное значение индуктивности цепи возбуждения;
*
*
Tв
Lв
= ------ — безразмерная постоянная времени контура возбуждения, чис*
Rв
ленно совпадающая со значением реальной постоянной времени.
Принимая для цепи якоря за базовые значения электрических переменных (напряжения и тока) их номинальные значения, а за базовое значение
скорости — скорость идеального холостого хода двигателя при номинальном
напряжении на якоре и номинальном токе возбуждения, получаем похожее
уравнение, отличающееся от своего аналога в физической области только
символами «*» безразмерных переменных и параметров:
откуда
212
Uя баз = Uя ном;
(8.12)
iя баз = iя ном;
(8.13)
tбаз = Tбаз = 1 с;
(8.14)
U я баз
R я баз = -------------- ;
i я баз
(8.15)
U я баз T баз
L я баз = ------------------------- ;
i я баз
(8.16)
(kΦ)баз = (k)ном;
(8.17)
U я баз
ω баз = ω 0 ном = ------------------- ,
( kΦ ) баз
(8.18)
*
Uя
=
* *
Rя iя
+
*
* di я
L я ------*
*
*
+ ( kΦ ) ω ,
(8.19)
dt
или
*
Uя
=
*⎛ *
Rя ⎜ iя
⎝
+
*
* di я⎞
T я -------⎟
*
dt ⎠
*
*
+ ( kΦ ) ω ,
(8.20)
*
где R я — безразмерное значение активного сопротивления цепи якоря;
*
Lя
= ------ — безраз— безразмерное значение индуктивности цепи якоря;
*
Rя
мерная постоянная времени якоря, численно равная электромагнитной постоянной времени двигателя в физических единицах.
Примем за базовое значение электромагнитного момента двигателя его
номинальное значение при номинальном токе якоря и номинальном токе возбуждения:
*
Lя
*
Tя
Mбаз = Mном = (kΦ)номiя ном.
(8.21)
В теории электропривода инерционность механических процессов учитывается так называемой электромеханической постоянной времени. Ее значение равно времени разгона двигателя без нагрузки от нуля до скорости
идеального холостого хода при номинальном значении электромагнитного
момента:
J Σ ω баз
T м = ----------------- .
M баз
(8.22)
С учетом этих соотношений уравнение механического равновесия двигателя в относительных единицах примет вид
*
* dω
*
*
M – M c = T м ---------- ,
*
dt
(8.23)
Tм
*
где T м = ---------- — безразмерная электромеханическая постоянная времени.
T баз
Два последних уравнения, связывающих между собой ток с моментом и
поток с током возбуждения, в относительных единицах примут еще более
простой вид:
* *
*
M = ( kΦ ) i я ;
*
*
( kΦ ) = i в .
(8.24)
(8.25)
213
Таким образом, двигатель постоянного тока с управлением по цепи якоря
и возбуждения может быть описан системой дифференциальных уравнений
третьего порядка:
*
Uв
*
Uя
=
*⎛ *
Rв ⎜ iв
=
⎝
*⎛ *
Rя ⎜ iя
⎝
+
+
*
* di в⎞
T в -------⎟
*
;
dt ⎠
*
* di я⎞
T я -------⎟
*
dt ⎠
*
(8.26)
*
+ iв ω ;
(8.27)
*
* dω
*
* *
i в i я – M c = T м ---------- .
*
dt
(8.28)
Обратите внимание на то, что динамика двигателя полностью определя*
ется соотношением постоянных времени: возбуждения T в , электромагнит*
*
ной T я и электромеханической T м . Более того, численные значения безразмерных постоянных времени равны соответствующим физическим
значениям этих постоянных. Это очень удобно при отладке и настройке системы управления. Выбранная система базовых величин действительно упрощает цифровую реализацию модели ДПТ, однако не лишена недостатков.
Необходимо помнить, что базовые значения токов и напряжений, сопротивлений и индуктивностей в цепях якоря и возбуждения различаются. Вследствие этого, например, базовые значения мощностей по цепям якоря и возбуждения различны. Для расчета полной мощности, потребляемой
двигателем, потребуется корректирующий коэффициент.
Структурная схема двигателя постоянного тока в относительных единицах
представлена на рис. 8.2. Соответствующие передаточные функции двигателя
будут использованы далее при синтезе регуляторов системы управления.
Uв*
1/Rв*
Tв*p
iв*
+1
ω*
Uя*
x
E*
+
–
iв*
1/Rя*
Tя*p + 1
iя*
M*
x
+
1
–
ω*
Tм*p
Mс*
Рис. 8.2. Структурная схема двигателя постоянного тока в относительных единицах
214
Контрольные вопросы
1. Получите передаточную функцию ДПТ по управлению по цепи якоря, считая, что
ток в контуре возбуждения установлен и равен номинальному.
2. Отношением каких двух постоянных времени будет определяться качество переходных процессов в двигателе? Сформулируйте условие апериодического процесса,
колебательного.
8.3. ДИСКРЕТНАЯ МОДЕЛЬ ДВИГАТЕЛЯ ПОСТОЯННОГО ТОКА
Как уже отмечали, модель двигателя будет рассчитываться в микроконтроллере на интервале дискретизации Tдиск, равном периоду ШИМ. Для перехода от непрерывных уравнений к разностным представим полученную в
(8.26)—(8.28) систему уравнений в форме задачи Коши:
*
di в
1- i * ;
1 U * – ----------- = -----------*
* в
* * в
dt
Tв
Rв Tв
(8.29)
di я
1 ( U * – i * ω * ) – ----1- i *
------- = -----------;
в
я
*
* *
* я
dt
Rя Tя
Tя
(8.30)
*
*
*
dω 1 * *
--------= ------ ( i в i я – M с ) .
*
*
dt
Tм
(8.31)
Заменив непрерывные переменные дискретными, а производные левыми
разностями:
*
*
*
dx
x [k] – x [k – 1]
-------- ⇒ -------------------------------------------- ,
*
*
T диск
dt
получим:
*
*
iв [ k ] – iв [ k – 1 ]
*
1
1 *
------------------------------------------ = -----------U в [ k ] – ------ i в [ k ] ;
*
*
* *
T диск
Rв Tв
Tв
*
(8.32)
*
iя [ k ] – iя [ k – 1 ]
*
1
1 *
------------------------------------------ = ------------i [k] ;
( U я [ k ] – i *в [ k ]ω * [ k ] ) – ----*
* *
* я
T диск
Rя Tя
Tя
*
(8.33)
*
ω [k] – ω [k – 1]
1- ( i * [ k ]i * [ k ] – M * )
---------------------------------------------- = ----с .
я
*
* в
T диск
Tм
(8.34)
215
После выделения значений переменных на k-м интервале квантования
через значения переменных на предыдущем (k – 1)-м интервале квантования,
система разностных уравнений примет вид:
*
*
*
iв [ k ] = K1 Uв [ k ] + K2 iв [ k – 1 ] ;
*
*
*
*
*
i я [ k ] = K 3 ( U я [ k ] – i в [ k ]ω [ k ] ) + K 4 i я [ k – 1 ] ;
*
*
*
*
*
ω [ k ] = K 5 ( i в [ k ]i я [ k ] – M с ) + ω [ k – 1 ] ,
(8.35)
(8.36)
(8.37)
где значения пяти коэффициентов, описывающих поведение двигателя в
динамике, равны:
*
T диск
K 1 = --------------------------------------- ;
*
*
*
R в ( T в + T диск )
(8.38)
*
Tв
K 2 = -------------------------- ;
*
*
T в + T диск
(8.39)
*
T диск
K 3 = --------------------------------------- ;
*
*
*
R я ( T я + T диск )
(8.40)
*
Tя
K 4 = -------------------------- ;
*
*
T я + T диск
(8.41)
*
T диск
K 5 = ------------- .
*
Tм
(8.42)
Как видите, дискретная модель ДПТ представляет собой систему разностных уравнений третьего порядка, реализация которой в микроконтроллере
требует использования, главным образом, операций умножения с накоплением. Коэффициенты K1—K5 вычисляются один раз в процессе инициализации системы по пяти параметрам двигателя и относительному значению
интервала квантования.
8.4. ПЕРЕДАТОЧНЫЕ ФУНКЦИИ СИЛОВЫХ ПРЕОБРАЗОВАТЕЛЕЙ
Для упрощения работы реальные силовые преобразователи моделируются
вместе с двигателем. При этом реализуются только полученные статические
выходные характеристики преобразователей, представленные на рис. 8.3—
8.5. Динамические особенности преобразователей, связанные с эквивалент216
Uвых ПЯ
+Ud*c я
+Udc
0
0,5
U *вых ПЯ
+1 U *
y ПЯ
–1
1 γ
–Udc
–Ud*c я
а)
б)
Рис. 8.3. Выходные характеристики преобразователя в цепи якоря
Uy*ПЯ
+Ud*c я
Ud*c я
Tп*p + 1
U *вых ПЯ
–Ud*c я
Рис. 8.4. Блок-схема преобразователя в цепи якоря
Uy*ПВ
+Ud*c в
Ud*c в
Tп*p + 1
U *вых ПВ
–Ud*c в
Рис. 8.5. Блок-схема преобразователя в цепи возбуждения
ным запаздыванием на интервал дискретизации, автоматически учитываются в результате выполнения расчетов на интервале квантования. Полные
модели преобразователей используются дальше для синтеза регуляторов.
Силовой преобразователь в цепи якоря
Преобразователь в цепи якоря (ПЯ) построен по классической мостовой
схеме инвертора напряжения и является реверсивным. Для получения передаточной функции ПЯ примем следующие допущения:
1) преобразователь в цепи якоря работает во втором импульсном режиме
(+U, –U) на фиксированной частоте ШИМ. Прямой сигнал управления подается одновременно на левый верхний и правый нижний силовой ключи, а
инверсный сигнал — одновременно на левый нижний и правый верхний
ключи. Для управления инвертором достаточно одного канала ШИМ-генератора с комплиментарными выходами;
2) ШИМ-генератор микроконтроллера обеспечивает автоматическую
защиту силовых ключей каждой стойки инвертора по «мертвому времени».
217
Значение величины «мертвого времени» на два порядка меньше периода
ШИМ и может не учитываться в математическом описании инвертора;
3) ШИМ-генератор автоматически перезагружается новой уставкой
скважности ШИМ γ в начале очередного периода ШИМ. Так как перезагрузка из теневого регистра скважности в рабочий регистр скважности производится автоматически аппаратно в начале очередного периода ШИМ, то
преобразователь имеет эквивалентное запаздывание по управлению на один
период ШИМ, т.е. на интервал дискретизации Tдиск.;
4) в первом приближении звено запаздывания можно заменить инерционным звеном с такой же постоянной времени TПЯ = Tдиск;
5) входное напряжение ПЯ будем считать постоянным и равным напряжению в звене постоянного тока Udc.
Выходная характеристика преобразователя в цепи якоря — зависимость
выходного напряжения от скважности управляющего ШИМ-сигнала показана на рис. 8.3, a. Аналитически она описывается соотношением.
Uвых ПЯ = Udc(–1 + 2γ).
Значение величины скважности γ меняется в диапазоне от 0 до 1. Введем
в рассмотрение входное управляющее воздействие преобразователя, связанное со скважностью соотношением:
*
U y ПЯ = ( – 1 + 2γ ) .
С учетом этого и после перехода к относительным единицам выходная
характеристика преобразователя в цепи якоря, приведенная на рис. 8.3, б,
примет вид:
*
*
*
U вых ПЯ = U dc я U y ПЯ ,
U dc
*
где U dc я = -------------- .
U я баз
Таким образом, коэффициент передачи преобразователя в цепи якоря
равен безразмерному значению напряжения в звене постоянного тока по
отношению к базовому напряжению цепи якоря. Итоговая передаточная функция ПЯ показана на рис. 8.4. Силовой преобразователь представляется
*
инерционным звеном с коэффициентом усиления U dc я и постоянной вре*
*
мени T п , равной интервалу дискретизации T диск . На выходе инерционного
*
звена имеется звено ограничения сигнала на уровне ± U dc я .
Силовой преобразователь в цепи возбуждения
Отличительной особенностью этого преобразователя является его нереверсивность. Он построен на базе одного силового ключа, управляемого
широтно-импульсным сигналом с регулируемой скважностью и одного
218
обратного диода. Преобразователь работает в первом импульсном режиме
(+U, 0). Выходная характеристика преобразователя имеет вид
Uвых ПВ = Udcγ.
Входное управляющее воздействие преобразователя в относительных
единицах равно скважности ШИМ-сигнала:
*
U y ПВ = γ .
С учетом базового значения напряжения в цепи возбуждения выходная
характеристика ПВ в относительных единицах примет вид
*
*
*
U вых ПВ = U dc в U y ПВ ,
U dc
*
где U dc в = -------------- .
U в баз
*
Так как эквивалентная инерционность преобразователя T п такая же, как и
для преобразователя в цепи якоря, передаточная характеристика ПВ отличается
от передаточной характеристики ПЯ только значением коэффициента передачи
и уставкой блока ограничения выходного сигнала, изображенного на рис. 8.5.
8.5. СИНТЕЗ ЦИФРОВОЙ СИСТЕМЫ ПОДЧИНЕННОГО
РЕГУЛИРОВАНИЯ КООРДИНАТ ЭЛЕКТРОПРИВОДА
Один из наиболее часто применяемых на практике методов управления
электроприводами основан на подчиненном регулировании координат, когда
последовательно оптимизируются все контуры регулирования, начиная с
самого внутреннего: вначале контур тока, затем контур скорости и, наконец,
контур положения. При этом передаточные функции регуляторов выбираются таким образом, чтобы замкнутые контуры имели требуемые показатели
качества переходных процессов при максимальном быстродействии. Это
обеспечивается посредством «компенсации» всех инерционностей в системе, за исключением так называемых «некомпенсируемых» малых постоянных времени. Разумеется, «компенсация» возможна только в том случае,
когда имеются соответствующие запасы по напряжению, т.е. форсировка.
Задача двухзонного управления ДПТ, в том числе при ослаблении поля,
является достаточно сложной. Ограничимся в процессе выполнения работы
синтезом только регуляторов тока якоря и скорости, предполагая, что ток в
цепи возбуждения уже установлен на номинальном уровне.
Синтез регулятора тока
Синтез регулятора тока будем производить при заторможенном двигателе,
когда ЭДС равна нулю. В этом случае объект управления представляет собой
последовательно соединенные передаточные функции силового преобразователя ПЯ и собственно цепи якоря двигателя постоянного тока, контур которого приведен на рис. 8.6. Внутренняя обратная связь по ЭДС отсутствует.
219
ПЯ
i *я зад
∆i *я
–
РТ
Uy*ПЯ
+Ud*c я
Ud*c я
Tп*p + 1
1/Rя*
i *я
Tя*p + 1
–Ud*c я
i *я
Uя*
Рис. 8.6. Контур тока
В качестве «некомпенсируемой» постоянной времени выберем самую
малую постоянную времени — постоянную времени преобразователя
*
*
T µ = T п . Желаемая передаточная функция разомкнутого контура регулирования определяется по формуле:
жел
1
W раз т = --------------------------------------------- ,
*
*
aт Tп p ( Tп p + 1 )
(8.43)
где aт — кратность постоянной интегрирования в контуре тока по сравнению
с «некомпенсируемой» постоянной времени. От значения этой переменной
будет зависеть качество переходных процессов отработки задания тока. Так,
при aт = 2 обеспечивается настройка на технический оптимум, когда перерегулирование по току составляет 4,3 %, а время отработки задания тока —
*
4,7 T µ . При больших значениях aт переходный процесс становится апериодическим, при меньших значениях — колебательным с более значительным
перерегулированием.
С учетом передаточной функции объекта регулирования
*
*
U dc я
1 ⁄ Rя
- -----------------------W о.р т = -----------------------*
*
( Tп p + 1 ) ( Tя p + 1 )
(8.44)
передаточная функция регулятора тока определяется по формуле
жел
*
*
W раз т
Rя Tя p + 1
- ------------------- .
W р.т = --------------- = -----------------*
W о.р т a U *
т dc я T п p
(8.45)
Получили пропорционально-интегральный регулятор тока
1
W р.т = k п р.т + ---------------*
T и р.т p
со следующими коэффициентами:
220
(8.46)
* *
Rя Tя
k п р.т = ------------------------- ;
*
*
a т U dc я T п
*
*
T и р.т
(8.47)
*
a т U dc я T п
= ------------------------- .
*
Rя
(8.48)
Программная реализация регулятора тока предполагает переход от передаточной функции регулятора к соответствующему разностному уравнению.
Запишем вначале уравнение ПИ-регулятора в непрерывной области
*
*
U y ПЯ
=
*
k п р.т Δi я
* *
1 t
+ ------------- ∫ Δi я dt ,
*
T и р.т 0
(8.49)
или после операции дифференцирования:
*
*
dU y ПЯ
dΔi
*
1
------------------ = k п р.т -----------я + ------------ Δi я .
*
*
*
dt
dt
T и р.т
(8.50)
Выполним дискретизацию этого уравнения, заменив производные
левыми разностями:
*
*
*
*
U y ПЯ [ k ] – U y ПЯ [ k – 1 ]
Δi я [ k ] – Δi я [ k – 1 ]
*
1
--------------------------------------------------------------- = k п р.т ------------------------------------------------- + ------------- Δi я [ k ] , (8.51)
*
*
*
T диск
T и р.т
T диск
после преобразований получим:
*
*
U y ПЯ [ k ]
=
*
U y ПЯ [ k
T диск⎞ *
⎛
*
– 1 ] + ⎜ k п р.т + -------------⎟ Δi я [ k ] – k п р.т Δi я [ k – 1 ] . (8.52)
*
⎝
T и р.т⎠
В соответствии с этим уравнением на каждом периоде ШИМ по текущему
и предыдущему значениям ошибки воспроизведения тока будет рассчитываться новое значение управляющего воздействия и подаваться на вход силового преобразователя в цепи якоря.
Контрольные вопросы
1. Будет ли возникать динамическая ошибка по току при разгоне двигателя, когда
ЭДС станет отлична от нуля?
2. Почему при синтезе регулятора тока мы приняли коэффициент обратной связи по
току равным единице. Допустимо ли это? Будет ли это соответствовать системе с реальным датчиком, сигнал которого вводится в АЦП?
221
Синтез регулятора скорости
Если произвести настройку контура тока с коэффициентом aт ≥ 2, то передаточная функция замкнутого контура тока может быть с высокой точностью
представлена в виде
1
W зам т = ------------------------- .
*
aт Tп p + 1
(8.53)
В теории электропривода доказывается, что для синтеза регулятора скорости можно в первом приближении пренебречь обратной связью по ЭДС
двигателя и представить контур скорости в виде, изображенном на рис. 8.7.
Если выбрать в качестве «некомпенсированной» постоянной времени
*
эквивалентную постоянную времени замкнутого контура тока a т T п , то желаемая передаточная функция контура скорости запишется следующим образом:
жел
1
W раз с = ----------------------------------------------------------- .
*
*
a с ( a т T п p )( a т T п p + 1 )
(8.54)
С учетом передаточной функции объекта регулирования в контуре скорости
1
1
W ор с = ------------------------- ---------*
*
aт Tп p + 1 Tм p
(8.55)
передаточная функция регулятора скорости определяется по формуле
жел
*
Tм
W раз с
W р.с = --------------- = ----------------- = k п р.с .
*
W ор с
aс aт Tп
(8.56)
Таким образом, регулятор скорости является чисто пропорциональным.
На его выходе целесообразно установить звено ограничения, которое будет
ограничивать задание тока якоря в динамических процессах на допустимом
*
уровне — на уровне заданного пускового тока i я пуск .
ω*зад
∆ω*
ω*
–
+i *я пуск
РС
i *я зад
1
атTп*p + 1
–i *я пуск
Рис. 8.7. Контур скорости
222
M*
1
Tм*p
ω*
8.6. ПОСТРОЕНИЕ ПРОЕКТА ШАГ ЗА ШАГОМ
Общая структура управления
двигателем постоянного тока
Ток в контуре возбуждения и ток цепи якоря регулируются с использованием стандартного шестиключевого инвертора напряжений.
В этом параграфе основное внимание уделено непосредственно системе
управления. Информация о программировании периферийных устройств,
АЦП и ШИМ может быть найдена на сайте www.ti.kom.
Практическая работа
1. Запустите CodeComposer.
2. Из папки myprojects\lab8\Debug\ загрузите рабочее пространство
(workspace) main.wks. Разверните в менеджере проекта папку source и выберете файл main.c. Откроется окно, показанное на рис. 8.8.
Все строки программы имеют комментарии, по которым можно понять
значение кода. Изучите этот файл.
Справа находятся два графических окна, в которых будут отображаться
наблюдаемые переменные.
Рис. 8.8. Рабочая область программы в CCS
223
Лист. 8.1. Структура файла main.c
//файл main.c
//подключаем заголовочные файлы//
//библиотека работы с фиксированной точкой
#include "IQmathLib.h"
//описание регистров и периферийных устройств
//микроконтроллера
#include "DSP28_Device.h"
//описание переменных и функций главного модуля
#include "main.h"
// определение параметров ДПТ
#include "parameter.h"
// определение уровня построения проекта
#include "build.h"
// прототип функции описанной в этом файле
// основное прерывание ШИМ
interrupt void EvaTimer1(void);
// Глобальные переменные используемые в этой системе
float U_e_ref = 1; /* Задание напряжения обмотки возбуждения (о.е) */
float U_a_ref=1; //Задание напряжения обмотки якоря (о.е)
float Mc_ref=0; //Задание момента сопротивления (о.е)
float Ia_ref=1; //Задание тока якоря для ПИ регулятора (о.е)
float speed_ref = 0.5; /* Задание скорости для регулятора скорости (о.е) */
float T = SAMPLING_PERIOD; /* Период дискредизации объявлен в parameter.h */
int isr_ticker = 0; /*счетчик прерываний ШИМ*/
// создание и инициализация модели ДПТ с НВ
BDCM bdcm =BDCM_DEFAULTS;
/* создание объекта пересчета параметров двигателя
в константы для модели ДПТ с НВ*/
BDCM_CONST bdcm_const = BDCM_CONST_DEFAULTS;
/*создание объекта управления ШИМ выходами*/
PWMGEN pwm =PWMGEN_DEFAULTS;
/* создание ПИ регуляторов*/
PI_REG pi_ia = PI_REG_DEFAULTS;
PI_REG pi_spd = PI_REG_DEFAULTS;
//НАЧАЛО ВЫПОЛНЕНИЯ ПРГРАММЫ
void main(void)
{//Инициализация регистров управления микроконтроллером,
//PLL,WatchDog
//Все часы устанавливаются в номинальное состояние
//Функция находиться в файле DSP28_SysCtrl.c
InitSysCtrl();
224
Продолжение лист 8.1
//Отключение и очистка всех прерываний CPU:
DINT;
IER = 0x0000;
IFR = 0x0000;
//Инициализация регистров управления прерываниями
//Функция находится в файле DSP28_PieCtrl.c
InitPieCtrl();
//Инициализация таблицы векторов прерывания в известные
//состояния
//Функция находится в файле DSP28_PieVect.c
//Таблица векторов прерывания заполняется указателями
//описанными в DSP28_DefaultIsr.c.
InitPieVectTable();
// Инициализация 1 таймера менеджера событий А
// (EVA Timer 1):
// Устанвка регистра управления
EvaRegs.GPTCONA.all = 0;
// Разрешение прерывания по событию Underflow
EvaRegs.EVAIMRA.bit.T1UFINT = 1;
EvaRegs.EVAIFRA.bit.T1UFINT = 1;
// Переопределение прерываний.
EALLOW;// Требуется разрешение на запись EALLOW
PieVectTable.T1UFINT = &EvaTimer1;
EDIS; // Запрещаем запись в защищенные регистры
// Разрешаем группу 2 (PIE group 2) прерывание 6 для T1UFINT
PieCtrlRegs.PIEIER2.all = M_INT6;
// Разрешаем CPU INT2 для T1UFINT:
IER |= M_INT2;
// Разрешаем глобальные прерывания для высокоприоритетных
// прерываний реального времени
EINT; // Разрешаем глобальное прерывание INTM
ERTM; /*Разрешаем глобальное прерывание реального времени DBGM*/
/* Инициализируем модуль ШИМ */
/* Частота прерываний для центрированной ШИМ = 5 kHz
при тактовой 150MГц (0x3A98 = 15000) */
pwm.n_period = 0x3a98;
//инициализируем модуль ШИМ
pwm.init(&pwm);
/* Инициализируем модуль подсчета констант */
bdcm_const.Re = R_EX;
bdcm_const.Le = L_EX;
bdcm_const.Ue_base = BASE_VOLTAGE_EX;
bdcm_const.Ie_base = BASE_CURRENT_EX;
225
Окончание лист 8.1
bdcm_const.Ra = R_A;bdcm_const.La = L_A;
bdcm_const.Ua_base = BASE_VOLTAGE_A;
bdcm_const.Ia_base = BASE_CURRENT_A;
bdcm_const.T_base=1;
bdcm_const.Ts = T;
bdcm_const.J=J_NOM;
bdcm_const.k_fi_nom = K_FI_NOM;
bdcm_const.calc(&bdcm_const);
/* Инициализируем константы */
bdcm.K1 = _IQ(bdcm_const.K1);
bdcm.K2 = _IQ(bdcm_const.K2);
bdcm.K3 = _IQ(bdcm_const.K3);
bdcm.K4 = _IQ(bdcm_const.K4);
bdcm.K5 = _IQ(bdcm_const.K5);
bdcm.Mc_pu = _IQ(0);
/* Инициализируем регулятор для контура тока якоря Ia */
pi_ia.Kp_reg3 = _IQ(0.567);
pi_ia.Ki_reg3 = _IQ(0.05);
pi_ia.Kd_reg3 = _IQ(0);
pi_ia.pid_out_max = _IQ(1);
pi_ia.pid_out_min = _IQ(-1);
/* Инициализируем регулятор для контура скорости */
pi_spd.Kp_reg3 = _IQ(10);
pi_spd.Ki_reg3 = _IQ(0.1);
pi_spd.Kd_reg3 = _IQ(0);
pi_spd.pid_out_max = _IQ(2);
pi_spd.pid_out_min = _IQ(-2);
// Пустой замкнутый цикл фоновой программы
for(;;);
}
3. Откройте файл build.h. В этом файле определяется переменная
BUILD_LEVEL, по значению которой определяется уровень построения
проекта. Удобство заключается в том, что если нужно рассмотреть работу
части системы, то нет необходимости переписывать весь проект, а достаточно лишь выбрать необходимый участок кода, скомпилировать его и запустить. В данной работе существует шесть уровней, каждый из которых поможет понять работу системы в целом.
Лист. 8.2. Структура файла build.h.c
#ifndef BUILDLEVEL
/* Список этапов построения проекта */
#define LEVEL1 1 /* тестирование прерывания ШИМ*/
#define LEVEL2 2 /* тест обмоти возбуждения */
226
Окончание лист 8.2
прямой пуск двигателя */
настройка регулятора тока ДПТ */
настройка регулятора скорости */
настройка регулятора скорости */
#define LEVEL3 3 /*
#define LEVEL4 4 /*
#define LEVEL5 5 /*
#define LEVEL6 6 /*
#define ALWAYS_RUN
/* Установка этапа проекта*/
#define BUILDLEVEL LEVEL5
#endif /* BUILDLEVEL */
Уровень LEVEL1
Код, выполняемый на первом этапе, содержит всего одну команду nop.
Самое важное, что хотим увидеть — это прерывание от таймера. В нем впоследствии будет считаться модель, а пока там только одна команда. Следует
обратить внимание на ее синтаксис. С помощью команды asm(« ») можно
поместить ассемблерную команду в код. В нашем случае это команда nop.
Для инициализации прерывания использовали стандартный объект
PWMGEN pwm и при инициализации записали в него значения
PWMGEN_DEFAULTS. Для настройки таймера и конфигурации прерывания
вызвали функцию инициализации pwm.init(&pwm), которая находится в
файле f28xpwm.c.
Лист. 8.3. Структура модуля ШИМ
void F28X_EV1_PWM_Init(PWMGEN *p)
{
EvaRegs.T1PR = p->n_period;
/* Init Timer 1 period Register */
EvaRegs.T1CON.all = PWM_INIT_STATE;
/* Symmetrical Operation */
EvaRegs.DBTCONA.all = DBTCON_INIT_STATE;
EvaRegs.ACTRA.all = ACTR_INIT_STATE;
EvaRegs.COMCONA.all = 0x8200;
EvaRegs.CMPR1 = p->n_period;
EvaRegs.CMPR2 = p->n_period;
EvaRegs.CMPR3 = p->n_period;
EALLOW; /* Enable EALLOW */
GpioMuxRegs.GPAMUX.all |= 0x003F;
EDIS; /* Disable EALLOW */
}
Символы PWM_INIT_STATE,
DBTCON_INIT_STATE
и
ACTR_INIT_STATE объявлены в файле f29xpwm.h, пример объявления
которого приведен ниже:
227
Лист. 8.4. Инициализатор модуля ШИМ
#define PWM_INIT_STATE (SOFT_STOP_FLAG + \
TIMER_CONT_UPDN + \
TIMER_CLK_PRESCALE_X_1 + \
TIMER_ENABLE_BY_OWN + \
TIMER_ENABLE)
Маски соответствующих битов описаны в файле f28xbmsk.h. Часть файла
приведена ниже:
Лист. 8.5. Маски
#define FREE_RUN_FLAG 0x8000
#define SOFT_STOP_FLAG 0x4000
#define TIMER_STOP 0x0000
#define TIMER_CONT_UPDN 0x0800
#define TIMER_CONT_UP 0x1000
#define TIMER_DIR_UPDN 0x1800
// и т.д.
При запуске программы функция pwm.init(&pwm) выполнится, и получим готовый к работе модуль ШИМ. На периоде необходимо будет подсчитать значение выходного напряжения, передать его в соответствующие поля
объекта pwm и вызвать функцию pwm.update(&pwm).
В файле build.h установите BUILD_LEVEL LEVEL1 и скомпилируйте
проект.
Лист. 8.6. Первый уровень компиляции
………
//*********************************************
// Пустой замкнутый цикл фоновой программы
for(;;);
}
interrupt void EvaTimer1(void)
{
isr_ticker++;
/* ***************** LEVEL1 ***************** */
#if (BUILDLEVEL==LEVEL1)
asm("nop"); // пустая команда
#endif /* (BUILDLEVEL==LEVEL1) */
………
Загрузите программу в плату и установите точку останова на команде
nop. Запустите программу на выполнение с использованием команды
ANIMATE. Вы увидите, что в окне просмотра изменяется переменная
isr_ticker — счетчик прерываний ШИМ.
228
Итак, получили прерывания ШИМ с частотой 5 кГц, внутри которого
будет считаться система управления ДПТ. Также в этой части задаются
управляющие воздействия и производится управление ключами инвертора.
Прерывание от ШИМ не соответствует частоте 5кГц. Это объясняется тем,
что при попадании на точку останова в режиме ANIMATE происходит останов программы на некоторое время. Это не всегда допустимо, поэтому для
дальнейшей работы нужно изучить работу процессора в реальном времени.
Существует также фоновая программа, объявленная в функции MAIN() и
выполняющаяся все свободное время процессора. В ней могут располагаться
не критичные к времени обработки функции, например функции коммуникаций.
Уровень LEVEL2
Приступим к исследованию системы управления двигателя постоянного
тока, для описания которой выбраны базовые величины, позволяющие рассчитывать модель двигателя в относительных единицах без использования
арифметики с плавающей точкой.
Скомпилируйте проект с использованием BUILD_LEVEL LEVEL2.
Установите точку останова, на процедуре подсчета модели (строка
bdcm.calc(&bdcm)) запустите программу в режиме ANIMATE.
На данном этапе протестируем работу контура возбуждения. В свойствах
верхнего графика, представленного на рис. 8.9, установите Start Address
Рис. 8.9. Настройки окна осциллографирования
229
&bdcm.i_e_pu — отображать ток в обмотке возбуждения. На вкладке окно
просмотра E_CONTROL измените U_e_ref (задание напряжения на обмотку
возбуждения) на 0,5 и увидите переходный процесс тока. Поэкспериментируйте с различными значениями.
Запустите программу в режиме ANIMATE. Остановите выполнение программы.
Рассмотрим, как реализован на языке Си контур возбуждения. Переход к
разностной форме описан в § 8.3. Здесь лишь коснемся особенностей программирования. В менеджере проекта выберите файл bdcm.c. В функции
bdcm_calc (BDCM *v) реализована модель ДПТ с НВ в относительных единицах. Найдите контур возбуждения. Это звено представляет собой инерционный фильтр, математически полученный следующим образом:
Ik = K1Uk + K2I[k – 1].
На языке Си с использованием арифметики с «виртуальной» плавающей
точкой IQmath это выглядит так:
Лист. 8.7. Расчет тока возбуждения
v->i_e_pu=_IQmpy(v->K1,v->u_e_pu)+_IQmpy(v->K2,v>i_e_pu);
Программа модели двигателя показана в лист. 8.8.
Рассмотрим, как передаются значения в функцию bdcm_calc. В описании
функции ей передается адрес объекта типа BDCM. В нашем случае это указатель v. Обращаться к полям объекта можно с помощью оператора ->, после
выставления которого система сама предложит список доступных полей.
Лист. 8.8. Расчет математической модели ДПТ
/*===================================================
Функция подсчета модели ДПТ с НВ Файл bdcm.c
===================================================*/
#include "..\include\IQmathLib.h"
#include "..\include\bdcm.h"
void bdcm_calc(BDCM *v)
{
_iq u_emf_pu;
// считаем обмотку возбуждения
v->i_e_pu=_IQmpy(v->K1,v->u_e_pu)+_IQmpy(v->K2,v>i_e_pu);
// считаем ток якоря
u_emf_pu=v->u_a_pu-_IQmpy(v->i_e_pu,v->W_pu);
v->i_a_pu=_IQmpy(v->K3,u_emf_pu)+_IQmpy(v->K4,v>i_a_pu);
//считаем электромагнитный момент
v->M_pu=_IQmpy(v->i_e_pu,v->i_a_pu);
//считаем скорость
v->W_pu=_IQmpy(v->K5,(v->M_pu-v->Mc_pu))+v->W_pu;
}
230
Лист. 8.9. Структура математической модели ДПТ
/* Файл bdcm.h */
typedef struct {
_iq u_e_pu; /* Вход: напряжение возбуждение (о.е) */
_iq u_a_pu; /* Вход: напряжение на якоре (о.е) */
_iq Mc_pu; /* Вход: момент сопротивления (о.е) */
_iq i_e_pu; /* Выход: ток обмотки возбуждения (о.е) */
_iq i_a_pu; /* Выход: ток якоря (о.е) */
_iq M_pu;
/* Выход: момент (о.е) */
_iq W_pu;
/* Выход: электрическая частота (о.е) */
_iq K1;
/* Параметр: */
_iq K2;
/* Параметр: */
_iq K3;
/* Параметр: */
_iq K4;
/* Параметр: */
_iq K5;
/* Параметр: */
_iq Ts;
/* Параметр: время дискретизации*/
void (*calc)(); /* Указатель на функцию счета*/
} BDCM;
/*--------------------------------------------------Инициализация по умолчанию для обекта BDCM
---------------------------------------------------*/
#define BDCM_DEFAULTS { 0,0,0, \
0,0,0,0,0, \
0,0,0,0,\
(void (*)(long))bdcm_calc }
/*--------------------------------------------------прототип функции в файле BDCM.C
---------------------------------------------------*/
void bdcm_calc(BDCM_handle);
С точки зрения команд процессора вызов происходит следующим образом. В регистр XAR7 записывается адрес вызываемой функции. В регистр
XAR4 записывается адрес структуры, с которой необходимо произвести операцию вычисления. После вызова эти значения сохраняются в стек, и работа
с полями структуры происходит указанием их непосредственного адреса.
Существует возможность передачи в функцию непосредственно структуры вместо указателя на нее, но это существенно снижает быстродействие,
так как требуется время на копирование и восстановление объекта.
Уровень LEVEL3
Запустим модель ДПТ напрямую включением на номинальное напряжение. Для этого установим BUILD_LEVEL LEVEL3 и откомпилируем программу. Установите точку останова на строке bdcm.calc(&bdcm). Загрузите
программу и запустите ее, используя режим ANIMATE. Так как ожидаем
231
увидеть большой бросок момента, то установите значение maximum X value
на свойствах нижнего графика на 50 единиц (рис. 8.10).
На графике, приведенном на рис. 8.11, увидите зависимость момента от
скорости — это динамическая характеристика ДПТ. Поэкспериментируйте с
заданиями напряжения на обмотке якоря U_a_ref на вкладке A_CONTROL и
с напряжением обмотки возбуждения U_e_ref на вкладке E_CONTROL.
Рис. 8.10. Настройка осциллографа на двухкоординатное отображение
Рис. 8.11. Прямой пуск ДПТ
232
Изучите код на языке Си для модели ДПТ — файл bdcm.c. Использование
библиотеки IQmath существенно упрощает проведение математических операций. Полученный код мало отличается от кода для операций с плавающей
точкой. Для оценки ассемблерного кода используйте команду mixed mode\
source mode в контекстном меню. В смешанном режиме каждой строке Си
соответствует набор ассемблерных команд. Используя пошаговую отладку
(клавиша F8), можно последовательно выполнять ассемблерные команды.
Уровень LEVEL4
Для ограничения пускового тока и поддержания постоянного момента
введем контур тока якоря. Как известно, настройка контура тока может производиться при зафиксированном якоре. Для этого отключим обмотку возбуждения, подав на нее напряжение U_e_ref = 0.
Скомпилируем программу с уровнем LEVEL4. Установите точку останова
на строке bdcm.calc(&bdcm); и загрузите программу. В окне верхнего графика отобразите ток якоря bdcm.i_a_pu. Для этого измените start address на
&bdcm.i_a_pu. Изменяя задание тока Ia_ref на вкладке A_CONTROL, можно
наблюдать переходный процесс в контуре тока, представленный на рис. 8.12.
Перейдите на вкладку PID_IA_REG и проведите настройку регулятора
(рис. 8.13). В окне наблюдения нет возможности задавать переменные в фор-
Рис. 8.12. Настройка контура тока
233
Рис. 8.13. Настройка ПИ-регулятора контура тока
мате Q24, поэтому для перевода можно воспользоваться калькулятором.
Изменяйте Kp_reg3 — пропорциональный коэффициент и Ki_reg3 — интегральный. После настройки переведите значения в формат Q24 и запишите их
значения в блок инициализации.
ВНИМАНИЕ! Для дальнейшей работы потребуется переключить процессор в режим реального времени и убрать все точки останова.
После настройки регулятора можно включить возбуждение и посмотреть
на пуск двигателя, приведенный на рис. 8.14. На скорости холостого хода
Рис. 8.14. Пуск ДПТ с ограничением тока
234
регулятор тока попадет в насыщение. Дальнейшее увеличение скорости требует ослабления поля. Измените напряжение на обмотке возбуждения
U_e_ref = 0,5 и наблюдайте процесс.
Уровень LEVEL5
Для обеспечения жесткости механической характеристики и регулирования момента введем контур скорости. ПИ-регулятор скорости располагается
на вкладке PID_SPD_REG. Настройте регулятор на технический оптимум.
Исследуйте режимы пуска, реверса, ослабления поля.
Лист. 8.10. Расчет двухконтурной системы
#if (BUILDLEVEL==LEVEL5)
//двухконтурная система управления
//контур скорости
pi_spd.ref = _IQ(speed_ref);
pi_spd.fdb = bdcm.W_pu;
pi_spd.calc(&pi_spd);
//контур тока
pi_ia.ref = pi_spd.out;
pi_ia.fdb = bdcm.i_a_pu;
pi_ia.calc(&pi_ia);
//считаем модель ДПТ
bdcm.u_e_pu =_IQ(U_e_ref);
bdcm.u_a_pu =pi_ia.out;
bdcm.Mc_pu =_IQ(Mc_ref);
bdcm.calc(&bdcm);
#endif /* (BUILDLEVEL==LEVEL5) */
235
Глава
9
ИССЛЕДОВАНИЕ СИСТЕМЫ ВЕКТОРНОГО
УПРАВЛЕНИЯ АСИНХРОННЫМ ДВИГАТЕЛЕМ
9.1. СТРУКТУРА АППАРАТНОЙ ЧАСТИ СИСТЕМЫ ПЧ—АД
Аппаратная структура системы преобразователь частоты — асинхронный
двигатель (ПЧ—АД) с короткозамкнутым ротором (КЗР), построенная на
базе классического мостового шестиключевого инвертора напряжения на
IGBT-транзисторах или интеллектуальных силовых модулях, представлена
на рис. 9.1.
Система управления
(TMS320x2xxx)
Сбор информации с датчиков
Управление инвертором
tº
tº
VT1
tº
VD1
VT3
VD3
VT5
VD5
VD7
ДН
VT2 VD2
VT4 VD4
ДТ
VT6 VD6
VT7
ДТ
B
C
Rторм.
A
АД
ИДП
ТГ
Рис. 9.1. Структура системы ПЧ—АД
236
Система управления реализуется на сигнальном микроконтроллере и
обеспечивает формирование ШИМ-сигналов управления шестью ключами
на базе многоканального ШИМ-генератора менеджера событий с высокой
несущей частотой — до 5—10 кГц. Обратные связи по току фазы A и B и
напряжению на звене постоянного тока (ЗПТ) реализуются с помощью датчиков электрических величин (например, на элементах Холла компенсационного типа), выходные сигналы которых вводятся в контроллер через АЦП.
Ток в фазе С измерять не требуется, так как его можно вычислить по первому закону Кирхгофа из уравнения
ia + ib + ic = 0.
(9.1)
Информация о скорости двигателя заводится с тахогенератора или с квадратурного импульсного датчика положения ротора.
Все расчеты в системе управления должны выполняться за один период
ШИМ, который является интервалом дискретизации системы управления с
периодом TШИМ. Работоспособность системы управления в отсутствие
реального преобразователя частоты и двигателя будем отрабатывать на математической модели асинхронного двигателя, запущенной на этом же контроллере. Математическая модель объекта управления (асинхронного двигателя
с короткозамкнутым ротором) будет иметь тот же самый интервал квантования.
9.2. МАТЕМАТИЧЕСКАЯ МОДЕЛЬ АСИНХРОННОГО ДВИГАТЕЛЯ
Асинхронный двигатель удобно описывать уравнениями двухфазной
обобщенной машины. Статор двигателя представлен двумя ортогонально
расположенными обмотками в стационарных координатах α, β с активным
сопротивлением Rs и индуктивностью Ls, которая в свою очередь разделяется на индуктивность рассеяния статора Lsσ и взаимную индуктивность Lm,
связывающую статорную и роторную обмотки. Роторные обмотки имеют
активное сопротивление Rr и полную индуктивность Lr и в системе роторных координат d, q описываются уравнениями:
dΨ sα ⎫
,
u s α = i sα R s + ------------dt ⎪⎪
dΨ s β ⎪
u s β = i sβ R s + ------------, ⎪
dt ⎪
⎬
dΨ rd ⎪
u r d = i r d R r + ------------- , ⎪
dt
⎪
dΨ rq ⎪
u r q = i r q R r + ------------- , ⎪
⎭
dt
(9.2)
где urd = urq = 0, так как рассматриваем асинхронный двигатель с короткозамкнутым ротором. Здесь и далее все роторные величины являются приведенными к статору.
237
Если роторные уравнения представить не во вращающихся со скоростью
ротора координатах, а в стационарных — α, β, воспользовавшись координатными преобразованиями d, q → α, β:
α = d cos θ – q sin θ, ⎫
⎬
β = d sin θ + q cos θ, ⎭
(9.3)
то уравнения АД запишутся в виде:
⎫
⎪
⎪
⎪
⎪
⎪
⎬
dΨ rα
⎪
0 = i rα R r + ------------- + Ψ r β ω, ⎪
dt
⎪
⎪
dΨ rβ
0 = i rβ R r + ------------- – Ψ rα ω, ⎪
⎭
dt
dΨ sα
u sα = i s α R s + ------------,
dt
dΨ sβ
u sβ = i s β R s + ------------,
dt
(9.4)
где ω — электрическая скорость вращения ротора. Необходимо добавить
уравнения связи токов и потокосцеплений:
Ψ sα = L s i sα + L m i rα , ⎫
⎪
Ψ sβ = L s i sβ + L m i rβ , ⎪
(9.5)
⎬
Ψ rα = L r i rα + L m i sα , ⎪
⎪
Ψ rβ = L r i rβ + L m i sβ , ⎭
и уравнение электромагнитного электрического момента двухфазной
машины:
M⊥ = Ψsαisβ – Ψsβisα.
(9.6)
Чтобы связать электромагнитный электрический момент двухфазной
машины с реальным моментом для уравнения движения, необходимо ввести
коэффициенты: преобразования 3/2, учитывающий переход к трехфазной
машине, и pp — число пар полюсов как коэффициент редукции между электрическими и механическими параметрами. Тогда недостающие уравнения
запишем в виде
3
M мех = --- p p ( Ψ sα i s β – Ψ sβ i sα ), ⎫
2
⎪
(9.7)
⎬
dω мех
⎪
M мех – M c = J --------------- ,
⎭
dt
где J — момент инерции двигателя с механизмом; ωмех — механическая скорость вращения вала двигателя.
Данные уравнения определяют поведение АД в физических единицах,
однако при создании систем управления удобно переходить к относитель238
ным единицам, так как в этом случае система легко представляется математически в микроконтроллерах, оперирующих с числами с фиксированной
точкой, поскольку диапазон изменения параметров не сильно зависит от
мощности конкретной системы.
Выбор систем относительных единиц производят по-разному в зависимости от типа двигателя. Для асинхронного двигателя в мировой практике
устоялась система относительных единиц, связанная с амплитудными значениями фазных величин. Она имеет следующие основные преимущества:
• все сопротивления и индуктивности в двухфазной и трехфазной системах совпадают;
• совпадают значения фазных токов и напряжений двухфазной и трехфазной систем;
• мощность или момент легко переводятся от двухфазной системы в
трехфазную и обратно через коэффициент 3/2.
Запишем выражения для базовых величин. Базовое напряжение равно
амплитуде фазного напряжения двигателя:
U б = U ф.ном 2 .
(9.8)
Базовое значение тока вычисляется по формуле
I б = I ф.ном 2 .
(9.9)
Базовая электрическая скорость равна синхронной круговой частоте:
ωб = 2πfном.
(9.10)
Из указанных величин можно рассчитать производные базовые величины:
сопротивление
Uб
(9.11)
R б = ------- ;
Iб
индуктивность
Rб
(9.12)
L б = ------ ;
ωб
потокосцепление
Uб
(9.13)
Ψ б = ------- ;
ωб
момент
Uб Iб
(9.14)
M б = Ψ б I б = ------------ ;
ωб
мощность
Pб = U б Iб.
(9.15)
Переведем последовательно уравнения асинхронного двигателя в относительные единицы. Для (9.4) необходимо разделить обе части уравнения на
U б = I бRб = Ψ бω б:
239
⎫
⎪
⎪
⎪
⎪
⎪
⎪
(9.16)
⎬
i rα R r
dΨ rα
Ψr β ω ⎪
0 = ------------- + -------------------- + -------------- , ⎪
I б R б Ψ б ω б dt Ψ б ω б ⎪
⎪
i rβ R r
dΨ rβ
Ψ rα ω ⎪
0 = ------------- + -------------------- – --------------- , ⎪
I б R б Ψ б ω б dt Ψ б ω б ⎭
а потом заменить переменные в физических единицах на переменные в относительных:
u sα i sα R s
dΨ sα
------- = ------------- + -------------------,
Uб
I б R б Ψ б ω б dt
u sβ i sβ R s
dΨ sβ
-------- = ------------- + -------------------,
Uб
I б R б Ψ б ω б dt
⎫
⎪
⎪
⎪
⎪
*
dΨ sβ
⎪
* *
*
1
u sβ = i sβ R s + ------ ------------- ,
⎪
ω б dt
⎪
(9.17)
⎬
*
⎪
*
*
*
*
1 dΨ r α
0 = i rα R r + ------ ------------+ Ψ rβ ω , ⎪
⎪
ω б dt
⎪
*
⎪
*
* *
* ⎪
1 dΨ r β
0 = i rβ R r + ------ ------------- – Ψ rα ω . ⎪
ω б dt
⎭
В уравнениях (9.5) следует разделить обе части на базовое потокосцепление Ψб = LбIб, тогда уравнения можно записать в виде
*
*
*
*
1 dΨ
sα
------------,
u sα = i sα R s + -----ω б dt
Ψ sα L s i sα L m i rα ⎫
---------- = ------------- + -------------- , ⎪
Ψб
Lб Iб
Lб Iб ⎪
⎪
Ψ sβ L s i sβ L m i rβ ⎪
---------- = ------------ + -------------- , ⎪
Ψб
Lб Iб
Lб Iб ⎪
⎬
Ψ rα L r i rα L m i sα ⎪
---------- = ------------- + -------------- , ⎪
Ψб
Lб Iб
Lб Iб ⎪
⎪
Ψ rβ L r i rβ L m i sβ ⎪
--------- = ------------ + -------------- , ⎪
Ψб
Lб Iб
Lб Iб ⎭
где можно произвести замену переменных на относительные:
240
(9.18)
*
* *
* *
Ψ s α = L s i sα + L m i rα , ⎫
⎪
⎪
* *
* *
*
Ψ s β = L s i sβ + L m i rβ , ⎪
⎬
* *
* *
*
Ψ r α = L r i rα + L m i sα , ⎪
⎪
⎪
* *
* *
*
Ψ r β = L r i rβ + L m i sβ . ⎭
(9.19)
Ψ s α i sβ – Ψ sβ i s α
M
-,
-------⊥- = ----------------------------------------Ψб Iб
Mб
(9.20)
Уравнение момента:
что даст:
*
*
*
*
*
M ⊥ = Ψ sα i sβ – Ψ sβ i sα .
(9.21)
Уравнения механики (9.7) запишем через электрические величины. Для
этого сначала определим понятие базовой механической скорости, равной
скорости идеального холостого хода:
2πf ном
ω мех.б = ----------------- .
pp
Электрическая и механическая скорости связаны выражением
ω = ppωмех.
(9.22)
(9.23)
Если обе части разделить на б, то получится выражение:
ω мех
ω
------ = p p ----------,
ωб
ωб
(9.24)
ωб
а так как ω б.мех = ------ , то выражение упрощается до:
pp
*
*
ω = ω мех ,
(9.25)
т.е. в относительных величинах электрическая скорость равна механической.
Это логично, так как 100 % номинальной электрической скорости всегда
будет соответствовать 100 % номинальной механической.
Преобразуем уравнение второго закона Ньютона, заменив все механические величины электрическими:
dω мех
M мех = J --------------;
dt
3
1 dω
--- p p M ⊥ = J ----- ------- .
p p dt
2
(9.26)
241
Полученное выражение можно записать так:
------- ,
M ⊥ = j dω
dt
(9.27)
2J
где j = --- ----- — электрический момент инерции, т.е. момент инерции меха3 p2
p
низма, приведенный к электрической скорости двухфазной машины.
Если теперь заменить физические электрические момент и скорость на
момент и скорость в относительных единицах, получится выражение:
*
*
dω
M ⊥ M б = jω б ---------- .
dt
(9.28)
2
Разделив обе части уравнения на ω б , получим выражение
*
*M
1 dω
M ⊥ ------б- = j ------ ---------- ,
2
ω б dt
ωб
(9.29)
Mб
где ------- = J б — базовое значение электрического момента инерции, тогда
2
ωб
2
2J ω
j = --- ----- ------б- ,
3 p2 Mб
*
(9.30)
p
а результирующее выражение второго закона Ньютона в относительных единицах для электрических величин примет вид
*
*
* 1 dω
M ⊥ = j ------ ---------- .
ω б dt
(9.31)
Во всех уравнениях перед операцией дифференцирования встречается
1
коэффициент ------ = T б , являющийся базовым временем. Он представлен в
ωб
физических единицах и является согласующим между величинами, представленными в относительных единицах, и физическим временем — dt, в
котором записаны приведенные уравнения.
Для того чтобы решать уравнения (9.17), (9.19), (9.21) и (9.31) в микроконтроллере и имитировать тем самым работу асинхронного двигателя для
создания вокруг работающей модели системы управления двигателем, необходимо записать эти уравнения в форме Коши, когда все производные переносятся в левую часть:
242
⎫
⎪
⎪
⎪
*
⎪
dΨ s β
* *
*
⎪
------------- = ω б ( u sβ – i sβ R s ) ,
⎪
dt
(9.32)
⎬
*
⎪
dΨ r α
*
*
*
*
------------- = ω б ( – i rα R r – Ψ rβ ω ) , ⎪
⎪
dt
⎪
*
⎪
dΨ r β
*
*
* *
------------- = ω б ( – i r β R r + Ψ rα ω ) . ⎪
⎭
dt
По известным потокосцеплениям после преобразования системы (9.19)
производится расчет токов по известным потокосцеплениям:
⎫
Lr
Lm
- Ψ rα , ⎪
- Ψ sα + -----------------------i sα = – -----------------------2
2
⎪
Lm – Ls Lr
Lm – Ls Lr
⎪
⎪
Lm
Lr
i sβ = – ------------------------- Ψ sβ + ------------------------- Ψ rβ , ⎪
⎪
2
2
Lm – Ls Lr
Lm – Ls Lr
⎪
(9.33)
⎬
Lm
Ls
⎪
i rα = – ------------------------- Ψ rα + ------------------------- Ψ sα , ⎪
2
2
⎪
Lm – Ls Lr
Lm – Ls Lr
⎪
⎪
Lm
Ls
i rβ = – ------------------------- Ψ rβ + ------------------------- Ψ sβ . ⎪
2
2
⎪
Lm – Ls Lr
Lm – Ls Lr
⎭
Вычисляется момент двигателя (9.21) и решается уравнение механики:
*
dΨ s α
*
*
*
------------- = ω б ( u sα – i sα R s ) ,
dt
*
*
M
dω --------(9.34)
= ω б -------⊥- .
*
dt
j
Полученные уравнения можно решать, заменив dt на Δt = TШИМ, тогда
для любого периода ШИМ (интервала квантования для разрабатываемой
системы управления) можно рассчитать изменения переменных состояния
Δx
*k
, а новое их значение рассчитать следующим образом:
⎫
*k
*k
*k – 1
*k
*k *k
Ψ sα = Ψ s α
+ ΔΨ sα ( u sα, i sα, Ψ sα ) , ⎪
⎪
*k *k *k
*k – 1
*k
*k
+ ΔΨ sβ ( u s β, i sβ, Ψ sβ ) , ⎪
Ψ sβ = Ψ s β
⎪
*k ⎪
*k *k
*k
*k – 1
*k
Ψ rα = Ψ r α
+ ΔΨ rα ( ω , i r α, Ψ r β ) , ⎬
⎪
*k
*k – 1
*k
*k
*k *k
⎪
Ψ rβ = Ψ r β
+ ΔΨ rβ ( ω , i rβ, Ψ sα ) , ⎪
⎪
*k
*k
*k – 1
*k *
⎪
ω = ω
+ Δω ( j , M ⊥ ) .
⎭
(9.35)
243
Texas Instruments предлагает воспользоваться готовыми моделями двигателя, которые можно загрузить с их сайта в Интернете www.ti.com. aci1 —
модель асинхронного двигателя, работающая по описанным выше выражениям. По входным напряжениям и моменту нагрузки позволяет рассчитать
поведение реального двигателя — его скорость, положение вала, токи статора. Для более точного решения дифференциальных уравнений АД в
модуле ACI используется метод «предиктора-корректора», суть которого
заключается в предварительном расчете переменных состояния асинхронного двигателя по уравнениям (9.35) и последующей их коррекцией, позволяющей снизить влияние погрешности расчетов, возникающих из-за квантования по времени.
В системе дифференциальных уравнений АД в модуле ACI используются
коэффициенты K1—K10, которые учитывают время дискретизации системы
и преобразуют параметры АД из абсолютных значений в относительные.
Расчет этих коэффициентов представлен ниже:
K1 = Ts α ;
K2 = Ts ωδ ;
ψ
K 4 = T s αβ -----δ- ;
I
δ
ψδ ωδ
K 5 = T s β -------------;
I
δ
Uδ
1 - -----K 7 = T s --------;
σL s I δ
I
K 3 = T s αL m -----δ- ;
ψ
δ
Lm ψδ Iδ
--- p -----K8 = 3
- ----------- ;
2 Lr Mδ
K6 = Ts γ ;
--- ;
K9 = Ts B
J
T
--- -----δ- .
K 10 = T s p
J ωδ
Блок ACI и его входные и выходные данные представлены на рис. 9.2 и в
табл. 9.1.
pu
Ualpha
Ubeta
LoadTorque
pu
pu
pu
pu
pu
ACI
pu
pu
pu
Q0
Ialpha
Ibeta
PsiAlpha
PsiBeta
Torque
Wr
WrRpm
Рис. 9.2. Входные и выходные данные модуля ACI
244
Табл и ц а 9.1
Данные модуля ACI модели АД с короткозамкнутым ротором
Параметр
Параметр АД
Параметр АД в программе
Напряжение статора:
по оси α
по оси β
Момент нагрузки (сопротивление)
*
u sα
*
u sβ
*
Mc
Ualpha
Ubeta
LoadTorque
Ток статора:
*
по оси α
i sα
Ialpha
по оси β
*
i sβ
Ibeta
Потокосцепление ротора:
по оси α
по оси β
Момент двигателя
Скорость ротора двигателя
*
Ψ rα
*
Ψ rβ
*
M⊥
*
ω
PsiAlpha
PsiBeta
Torque
Wr
Кроме того, требуется задать в качестве параметров сопротивления,
индуктивности, число пар полюсов, момент инерции и значения всех базовых величин.
9.3. ПРИНЦИПЫ ЧАСТОТНОГО РЕГУЛИРОВАНИЯ
В современных преобразователях частоты системы управления строятся
на контроллерах, программно реализующих различные способы управления.
Законы управления являются интеллектуальной собственностью фирм—
производителей преобразовательной техники и в каждом конкретном случае
реализуются по-разному. Детали реализации не разглашаются. В настоящее
время устоялось четыре основных способа частотного регулирования, каждый из которых может быть реализован во множестве вариантов.
Различают скалярное управление, векторное управление по потокосцеплению ротора, частотно-токовое управление и прямое управление моментом.
Скалярное управление реализуется во всех современных преобразователях
как базовая структура системы управления. В этом случае на двигатель подается напряжение заданной частоты, при этом преобразователь не регулирует
значения токов фаз или момент двигателя в динамике. Существуют скалярные системы управления, которые имеют положительную связь по току фазы
(с компенсацией скольжения) или замкнутые по скорости, что в статике при
плавном изменении момента нагрузки позволяет получать характеристики,
подобные имеющимся у систем векторного управления асинхронного двигателя.
В системах векторного управления (СВУ), построенного на базе известной системы «Трансвектор» [1], разработанной фирмой Siemens, система
245
управления вычисляет положение потокосцепления ротора двигателя и контролирует в статоре две составляющие тока: одну для поддержания потокосцепления ротора, а другую для создания момента. В этом режиме АД становится похож на машину постоянного тока, для которого можно, применяя
методы последовательной коррекции и подчиненного регулирования,
создать контур момента и контур скорости. Существуют датчиковые и бездатчиковые СВУ. Здесь и далее эти термины определяют наличие или отсутствие датчика положения вала двигателя или тахогенератора. Вычисление
положения потокосцепления ротора системой управления проще, когда
известна скорость или угловое положение ротора, но его можно вычислить и
ограничившись показаниями токов фаз и фазных напряжений (так работают
бездатчиковые системы). Однако при существенном снижении частоты
уменьшается фазное напряжение и ЭДС двигателя, и при частоте ниже 5 Гц
система управления уже не может вычислить все необходимые ей параметры. Систему управления размыкают и переходят к частотно-токовому
управлению.
На малых частотах в бездатчиковых системах применяют частотно-токовое регулирование, когда с помощью инвертора напряжения формируют в
фазах двигателя не напряжение, а ток. Значение тока контролируют посредством обратных связей. Характеристики АД при питании от источника тока
(инвертора напряжения с обратными связями по току) получаются жесткими, двигатель нельзя перегрузить по току увеличением нагрузки, так как
независимо от скольжения ток двигателя поддерживается постоянным. В
частотно-токовом режиме бездатчиковые системы работают на частотах
ниже 5 Гц, а при увеличении скорости, когда ЭДС двигателя становится различимой, снова переходят к структуре векторного бездатчикового управления.
Особняком стоит метод прямого управления моментом, который активно
противопоставляется векторному управлению фирмой ABB. В отличие от
СВУ прямое управление момента работает со статорными уравнениями двигателя. По модели двигателя каждый раз рассчитывается требуемое состояние вектора напряжения статора, необходимое для поддержания заданного
момента.
Скалярное управление
Уравнение электрического равновесия для одной фазы можно записать
следующим образом:
dΨ sα
u sα = i sα R s + ------------- .
dt
Если пренебречь падением напряжения на активном сопротивлении фазы
статора, то потокосцепление изменяется по синусоидальному закону:
U max
Ψ sα = – -----------cos ω 0эл t .
ω 0эл
246
Из полученного выражения видно, что амплитуда потокосцепления фазы
прямо пропорциональна напряжению и обратно пропорциональна частоте,
поэтому для поддержания постоянства потока требуется изменять напряжение пропорционально частоте. Такой закон регулирования напряжения в
функции частоты называют U/f = const.
При уменьшении скорости доля iR-составляющей увеличивается по отношению к прикладываемому напряжению, что приводит к существенному уменьшению потока на малых частотах и соответственно уменьшению момента. Для
обеспечения перегрузочной способности по моменту, близкой к номинальной во
всем диапазоне скоростей, применяют iR-компенсацию, при которой имеет
место завышение напряжения относительно закона U/f = const (рис. 9.3, а).
В области малых частот напряжение питания увеличивают по сравнению
с линейным законом. В области высоких частот напряжение питания доходит
до номинального значения. Увеличивать его больше номинального нельзя,
так как на этот уровень напряжения рассчитывается изоляция двигателя.
Преобразователи частоты со скалярным управлением дают возможность
обслуживающему персоналу задавать параметры закона изменения напряжения в функции частоты: определять значения начального и конечного напряжения, наклон и прогиб кривой напряжения, а возможно и задание кривой по
точкам (рис. 9.3, б и в).
Настройка кривой напряжения в функции частоты производится для конкретного вида нагрузки, с изменением вида нагрузки режим работы двигателя может оказаться далеким до оптимального — может произойти насыщение магнитной цепи, критический момент оказаться недостаточным для
преодоления момента нагрузки или произойти повышение потерь в результате увеличившегося скольжения. Многие преобразователи поддерживают
функции автоматической перенастройки кривой напряжения от частоты,
адаптируя закон для конкретной нагрузки. В зависимости от момента на валу
двигателя они изменяют напряжение двигателя в целях обеспечения требуемой перегрузочной способности, а также могут изменять частоту напряжения статора для обеспечения более жестких механических характеристик
двигателя. Такое управление называется «с компенсацией скольжения». При
быстро меняющихся факторах скалярное управление не обеспечивает требуемого качества регулирования. В этих случаях применяют системы векторного управления и системы прямого управления моментом.
U
Uном
U
= const
f
fном
а)
U
Uном
U
Un
Umin
U2
U1
fном f
f
б)
f1
f2
fn
f
в)
Рис. 9.3. Законы регулирования в преобразователях частоты со скалярной системой управления
247
Векторное управление
В развитии теории векторного управления большую роль сыграла разработка фирмой Siemens системы «Трансвектор». Эта система построена на
прямом измерении вектора потока в воздушном зазоре датчиками Холла. С
помощью математических преобразований, используя измеренный поток и
токи статора, вычисляют потокосцепление ротора и его угловое положение.
Контроль тока статора ведется по двум составляющим, одна из которых
задает потокосцепление, а другая — момент.
Для описания асинхронного двигателя используются уравнения в ортогональной системе координат x, y, вращающихся со скоростью ωk:
⎫
⎪
⎪
⎪
dΨ sy
⎪
u s y = i sy R s + ------------- + ω k Ψ sx ,
⎪
dt
⎪
dΨ r x
⎪
(9.36)
0 = i r x R r + ------------- – ( ω k – ω )Ψ ry , ⎬
dt
⎪
⎪
dΨ r y
0 = i r x R r + ------------- + ( ω k – ω )Ψ r x , ⎪
⎪
dt
⎪
Lm
⎪
M ⊥ = ------- ( Ψ rx i sy – Ψ ry i s x ),
⎪
Lr
⎭
где ωk и ω — электрические скорости вращения координатной системы x, y и
ротора двигателя.
Координаты x, y следует расположить так, чтобы ось x была сонаправлена
с вектором потокосцепления ротора (при этом Ψry = 0), а регулирование следует производить при Ψr = const, тогда уравнения роторной цепи упростятся:
dΨ sx
u s x = i sx R s + ------------- – ω k Ψ sy ,
dt
0 = ir x Rr ,
⎫
(9.37)
⎬
0 = i r y R r + ( ω k – ω )Ψ rx . ⎭
Для анализа полученного результата следует записать выражения потокосцеплений ротора асинхронного двигателя:
Ψ r x = L r i rx + L m i sx , ⎫
(9.38)
⎬
Ψ r y = L r i ry + L m i sy . ⎭
Согласно (9.37) irx = 0, при этом с учетом Ψry = 0 выражения (9.38) упрощаются:
Ψr x = Lm is x , ⎫
⎪
(9.39)
Lm
⎬
i ry = – ------- i sy . ⎪
Lr
⎭
248
Ток статора по оси x однозначно определяет потокосцепление. Тогда уравнение момента двигателя при Ψry = 0 примет вид
Lm
M = ------Ψ i .
L r rx s y
(9.40)
Сформулируем основной принцип векторного управления: с помощью
тока статора по оси x необходимо установить поле ротора, а по оси y задавать ток, определяющий момент, развиваемый двигателем. Результирующая
структура СВУ показана на рис. 9.4.
Структурная схема содержит регуляторы потокосцепления, момента (тока
статора по оси y) и тока статора по оси x; блок координатных преобразований из x, y, связанных с потокосцеплением ротора, в неподвижные координаты α, β, связанные со статором; инвертор, работающий с заданиями напряжений в координатах α, β; АД; измерительную систему; наблюдатель,
который по измеренным значениям электрических, механических и термических величин производит вычисление амплитуды Ψ r и положения θ Ψ
r
потокосцепления ротора АД; блоки координатных и фазных преобразований.
Блок фазных преобразований позволяет по измеренным токам двух фаз
определить токи двухфазной эквивалентной машины в стационарных координатах α, β. Блоки координатных преобразований осуществляют перерасчет ортогональных составляющих вектора тока из одних координатных осей
в другие.
В рамках данной лабораторной будет исследоваться упрощенная структура, изображенная на рис. 9.5, из которой будет исключен регулятор потока,
а регулятор момента представлен регулятором тока по оси y.
Рис. 9.4. Структура системы векторного управления
249
зад
зад
зад
зад
Рис. 9.5. Упрощенная структура системы векторного управления
Получим уравнения и структуру простейшего наблюдателя положения
потокосцепления ротора по измеренным токам статора и скорости вращения
ротора. Уравнения роторной цепи в координатах α, β описываются следующим образом:
⎫
dΨ rα
0 = i rα R r + ------------- + Ψ r β ω, ⎪
⎪
dt
(9.41)
⎬
dΨ rβ
⎪
0 = i rβ R r + ------------- – Ψ rα ω. ⎪
dt
⎭
Запишем ток ротора через потокосцепление ротора и ток статора:
Ψ rα L m
⎫
i rα = ---------- – ------ is α , ⎪
Lr
Lr
⎪
(9.42)
⎬
Ψ rβ L m
⎪
i rβ = ---------- – ------- i s β . ⎪
Lr
Lr
⎭
Подставляя (9.42) в (9.41), находим математическое описание наблюдателя потокосцепления ротора в следующем виде:
⎫
dΨ rα
T r ------------+ Ψ rα = L m i sα – T r ωΨ rβ , ⎪
⎪
dt
⎬
dΨ rβ
⎪
T r ------------- + Ψ rβ = L m i sβ + T r ωΨ rα . ⎪
dt
⎭
250
(9.43)
Рис. 9.6. Структура наблюдателя потокосцепления ротора в датчиковой системе векторного
управления
Согласно (9.43) на рис. 9.6 приведена структура наблюдателя потокосцепления ротора.
9.4. СТРУКТУРА ПРОЕКТА. РАБОЧАЯ ОБЛАСТЬ ПРОЕКТА
В данной работе исследуется датчиковый вариант системы векторного
управления (СВУ) асинхронным двигателем. Так как некоторые процессы
меняются быстрее, чем CCS успевает передавать и отображать их (например, ток статора, положение), будем использовать специальные модули для
быстрого сбора и отображения данных. В проекте используются два модуля:
специальный буфер для хранения данных и модуль вывода данных на ножки
микроконтроллера в виде ШИМ:
• быстро меняющиеся данные собираются в специальный программный
модуль — DATALOG, и могут быть быстро визуализированы пользователем;
• в случае использования платформы на базе eZdspTM TMS320x281x,
которая имеет два менеджера событий (EVMA, EVMB), возможно применение модуля PWMDAC, который использует таймер № 3 второго менеджера
событий (EVMB), и данные, переданные в этот модуль, могут быть выведены на соответствующие выходы микроконтроллера в виде ШИМ с частотой
30 кГц для наблюдения с помощью осциллографа.
Программа состоит из двух основных потоков. В фоновом потоке происходит инициализация программных модулей, периферийных модулей, а
также конфигурация и разрешение прерывания от таймера 1. После этого
фоновый поток попадает в бесконечный цикл и не выполняет никаких действий. Запущенное прерывание от первого таймера содержит несколько программных шаблонов, которые можно подключать с использованием команд
условной компиляции. В файле build.h определяется уровень компиляции
проекта.
251
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
@
M@
@
@
@
L@
@
@
@
@
@
@
@
@
L@
@
@
@
@
ᆳ
L@
@
@
@
@
@
ᆳ
@
@
@
N@
@
@@
@
@
@
@
@
@
@
@
ᆳ
FileView.
@
@
@
@
@
N@
N@
@
@
@
,
@
@
@
@
ᆳ
@
@
@
@
@
@
ᆳ
@
@
@
@
@
@
@
L@
watch window @
@
N@
@
@
@
@
@
@
@
L@
@
@
@
@
@
@
@
@
@
@
@
L@
@
@
@
@
L@
@
@
@
@
L@
@
@
@
@
@
@
@
N@
@
@
@
_ CCS2x.wks.
«F28xx XDS510USB
TMS320F2812 eZdsp
N@
@
@
@
@
@
@
L@
@
@
@
@
@
@
@
@
@
ᆳ
ᆳ
3sim_ RXQ@
«sdgo2812eZdsp»
L@
@
(*.pjt)
ᆱᄏ@
@
@
@
@
@
@ @
Emulator (Spectrum Digital)>>
@
@
@
@
@
@
.. \aci3_3sim_281x\cIQmath\build.
@
@
@
N@
@
@
@
@
@
@
@
IQmath
@
@
workspace (*.wks)
@
@
@
@
@
@
@
@
@
@
@
ᆳ
N@
@
L@
build.h.
@
@
@
@
ᆳ
N@
N@
9.1. @
@
@
#define BUILDLEVEL LEVELl
@
@
@
Project @
@
@
@
@
@
Main».
«Rebuild
@
@
@
@
@
252
ᆳ
@
@
@
@
@
@
«Debug -> Go
@
@
@
Run
@
@
@
@
Debug
@
N@
@
L@
@
@
@
@
1.
2.
@
@
@
@
@
@
eZdsp.
@
@
ᄏ@
L@
@
@
N@
@
@
@
@
_@
@
L@
@
@
@
@
@
@
@
@
N@ @
_@
@
9.5. ИССЛЕДОВАНИЕ МОДЕЛИ АСИНХРОННОГО ДВИГАТЕЛЯ
В РЕЖИМЕ СКАЛЯРНОГО УПРАВЛЕНИЯ
Предложенная для исследования программа содержит флаг разрешения
работы системы — EnableFlag. Пока этот флаг не будет установлен, программа не сможет преодолеть приведенный в лист. 9.2 участок кода. Для
запуска расчета системы необходимо установить значение EnableFlag=1,
что можно сделать, используя окно «watch window».
Лист. 9.2. Программная ловушка — флаг на разрешение работы
while (EnableFlag==FALSE)
{
BackTicker++;
}
На первом уровне компиляции исследуется поведение модели асинхронного двигателя, структура системы которой показана на рис. 9.7. Вы можете
задавать скорость вращения вектора напряжения и значение его компонент в
координатах d и q.
Все модули фазных и координатных преобразований разработаны в осях
(d,q), что в действительности означает вращающуюся систему координат
и это никак не связано с общепринятой терминологией. Асинхронный двигатель в данной работе регулируется в осях (x,y), а от реальной системы
координат (d,q) отличается только фазовым сдвигом.
В скалярной системе управления основным регулируемым параметром
является скорость поля статора ω0эл, которая определяет скорость вращения
ротора ωr машины. Скорость поля ω0эл пропорциональна частоте напряжения fs, питающего обмотку статора. Поэтому для изменения скорости поля
необходимо изменить fs. При этом следует заботиться об амплитуде напряжения: при уменьшении fs для сохранения магнитного потока на некотором,
например, номинальном уровне следует изменять Es ≈ Us так, чтобы
Us /fs = const.
UdRef
Ds
I_PARK
SpeedRef
RAMPGEN
UqRef Qs
Alpha Ualpha
Beta
Angle
Ubeta
LoadTorque
ACI
Ibeta
Ialpha
PsiBeta
PsiAlpha
Wr
Torque
Рис. 9.7. Структура проекта на первом уровне компиляции
253
Для реализации данного закона управления используется модуль vhzprof
Freq
VHz_PROF
VoltOut
Поскольку система управления реализована в осях (x, y), то задание по
оси y равно нулю Uy = 0, а по оси x равно амплитуде питающего напряжения
и определяется по формуле
Ux = Umax = VoltOut,
где VoltOut — выход модуля vhzprof.
Поскольку дискретная математическая модель асинхронного двигателя
реализована в неподвижных осях (α, β), необходимо преобразовать систему
в осях (x, y) в систему в осях (α, β). Для данного преобразования служит
модуль Ipark (обратное преобразование координат).
Данный модуль позволяет переходить из вращающейся системы векторов, например (x, y), с заданным углом в неподвижную, например (α, β).
Ds
Alpha
I_PARK
Qs
Angle
Beta
Задавая компоненты двух векторов напряжений по осям x и y и угол поворота системы относительно стационарной системы координат α и β, получаем два вектора напряжения alfa и beta, которые определяют положение
системы (x, y). Причем напряжение по оси y устанавливаем в нуль, поскольку
ось x системы сонаправлена с потокосцеплением ротора, а напряжение по
оси x устанавливаем постоянной величиной, значение которой определяется
по заданию скорости двигателя исходя из закона управления. Таким образом,
векторы напряжения alfa и beta определяют амплитуду и положение вектора
напряжения по оси x в данный момент времени и передаются в модель двигателя в качестве задания.
Текущее положение вектора напряжения по оси x определяется заданием
угла положения. Если угол изменяется по линейному закону, то вектор
напряжения по оси x вращается относительно неподвижной системы координат, причем скорость изменения угла определяет скорость вращения вектора
напряжения. Вспомним, что вектор напряжения сонаправлен с потокосцеплением ротора и определяет его положение в данный момент времени, поэтому скорость изменения угла определяет скорость ротора АД. Поэтому скорость изменения угла положения вектора напряжения по оси x определяется
заданием скорости вращения ротора.
254
Для задания угла и скорости его изменения используется модуль rg1
(RAMPGEN) — модуль опорного пилообразного сигнала с задаваемой частотой. Задание скорости SpeedRef определяет частоту генерируемого сигнала, который поступает в модуль Ipark в качестве угла вектора напряжения.
Gain
Offset
RAMP_
GEN
Out
Freq
Далее векторы напряжения alfa и beta, полученные из модуля Ipark,
передаются в модуль aci1 — модель асинхронного двигателя. По входным
напряжениям и моменту нагрузки можно рассчитать поведение реального
двигателя — его скорость, положение вала, токи статора. Для расчета параметров в системе координат (α, β) в модуле ACI используется метод «предиктора-корректора» для решения дифференциальных уравнений.
Чтобы исследовать работу модели двигателя, следует переключить
вкладку окна просмотра переменных на BUILD1. Для того чтобы можно
было наблюдать поведение двигателя на графиках в режиме реального времени, необходимо включить этот режим.
Для этого нужно снять все точки останова, в меню debug выбрать
команду reset CPU, загрузить программу в микроконтроллер, затем выбрать
команду из меню «Debug –> Real time mode». Появится диалоговое окно с
предупреждением «Do you want to allow realtime mode switching?: Can’t
enter realtime mode unless debug events are enabled. Bit 1 of ST1 must be 0».
Необходимо нажать подтверждение «Yes».
После того как выбран режим реального времени, запустите программу
командой Run из меню Debug или используя кнопку быстрого запуска. Для
автоматического обновления графиков в контекстном меню графиков выберите «Continues refresh».
В данной работе все расчеты произведены с использованием библиотеки
IQmath. Все физические величины, такие как напряжение, потокосцепление, положение вектора потокосцепления, скорость и момент, приведены в
относительных единицах.
Номинальные параметры двигателя задаются в файле parameter.h
Теперь, когда вычисления запущены, можно начинать исследование
модели асинхронного двигателя. Задавая величину вектора напряжения и его
частоту, имеем возможность имитировать скалярное управление. Вы можете
управлять моделью двигателя через окно просмотра переменных, где:
Speed_ref — задание скорости;
VdTesting — тестовое задание напряжения по оси d (x);
VqTesting — тестовое задание напряжения по оси q (y).
255
Лист. 9.3. Определение номинальных параметров двигателя
// Define the Induction motor parameters
#define RS 1.723
// Stator resistance (ohm)
#define RR 2.011
// Rotor resistance (ohm)
#define LS 0.166619 // Stator inductance (H)
#define LR 0.168964 // Rotor inductance (H)
#define LM 0.159232 // Rotor inductance (H)
#define P 4
// Number of poles
// Define the mechanical parameters
#define BB 0.0001 //Damping coefficient (N.m.sec/rad)
#define JJ 0.1 //Moment of inertia of rotor mass (kg.m^2)
#define TL 0
// Load torque (N.m)
// Define the base quantites
#define BASE_VOLTAGE 325.269
// Base peak phase voltage (volt)
#define BASE_CURRENT 5 // Base peak phase current (amp)
#define BASE_TORQUE 7.35105194 // Base torque (N.m)
#define BASE_FLUX 0.79616
// Base flux linkage (volt.sec/rad)
#define BASE_FREQ 50 // Base electrical frequency (Hz)
Установите нулевое задание скорости. Задайте напряжение по оси Ud = 0,05;
Uq = 0 — помните, что все переменные рассчитываются в относительных
величинах, задав число 0,05 о.е., вы задали 5 % номинального напряжения
двигателя, что в режиме, когда скорость равна нулю, является достаточным.
На графиках можно наблюдать изменение токов статора.
Окно графиков будет автоматически обновляться каждый раз, когда
интерфейс JTAG способен запросить данные. Буфер может быть захвачен
CCS в любой момент времени, поэтому очевидно появление разорванных
участков. Для работы осциллографа (изучите его работу самостоятельно) в
продолжительном режиме установите значение dlog.mode_reset = 2, для
остановки или однократной записи используйте dlog.mode_reset = 1.
Период прерывания установлен таким образом, что все необходимые
вычисления укладываются в один период. Параметры АД, базовые значения,
механические параметры и время дискретизации (частота обсчета модели
двигателя) можно удобно изменять через заголовочный файл parameter.h.
Общая для всех символическая константа для работы с библиотекой «виртуальной» плавающей точки IQmath GLOBAL_Q установлена в файле IQmathLib.h
и равна 24. Таким образом, все переменные в проекте используют формат 8.24.
Задайте напряжение по оси d = 0,1 и скорости — 0,5. Наблюдаете на графиках изменение скорости вала двигателя и момента на валу.
Обратите внимание на то, что качественного наблюдения момента двигателя не получается.
График момента, изображенный на рис. 9.8, содержит разрывные значения. Это связано с тем, что график скорости и момента рисуется по точкам, а
не через единый запрос буфера значений, часть точек теряется и не отображается на графике.
256
Рис. 9.8. Изменение напряжения, тока, скорости и момента
Контрольные вопросы
1. Объясните увеличение тока при смене задания скорости.
2. Объясните работу осциллографа. Почему график момента содержит не все расчетные точки?
3. Как, используя данные осциллографа, построить механическую характеристику?
9.6. ИССЛЕДОВАНИЕ ЧАСТОТНО-ТОКОВОЙ СИСТЕМЫ
УПРАВЛЕНИЯ АСИНХРОННОГО ДВИГАТЕЛЯ.
НАСТРОЙКА ПИ-РЕГУЛЯТОРА ТОКА
В предыдущем пункте исследовали поведение модели двигателя при скаляром управлении. Полученная в результате исследования механическая
характеристика представлена на рис. 9.9.
График механической характеристики построен с использованием
средств «View–>Graph–>Constellation» с соответствующими настройками
(рис. 9.10).
257
Рис. 9.9. Механическая характеристика двигателя
Рис. 9.10. Настройки графика для построения механической характеристики
258
IdRef Ref
SpeedRef
Fdb
PID_REG3
Out
Ds
I_PARK
Ref
Fdb
Ref
PID_REG3
Out
PID_REG3
Fdb
Out Qs
Angle
Alpha Ualpha
Beta
Ubeta
LoadTorque
ACI
Ibeta
Ialpha
PsiBeta
PsiAlpha
Wr
IDs
IQs
Torque
CUR_
MOD
Theta
Wr
Ds
Angle
PARK
Alpha
Qs
Beta
Рис. 9.11. Общая структурная схема системы векторного управления
Для перехода к изучению векторной системы управления обратимся к
описанию принципов этой системы.
Известно, что при поддержании потокосцепления ротора постоянным,
можно управлять моментом машины, задавая ортогональную компоненту
тока статора. При реализации этого принципа используют систему, ориентированную по вектору потокосцепления ротора. Для этого используют регулятора тока — один по оси d формирует задание тока так, чтобы потокосцепление ротора оставалось неизменным, и другой по оси q — для формирования
моментообразующего тока статора. Для задания ограничения момента и поддержания скорости используют ПИ-регулятор скорости. Общая структурная
схема представлена на рис. 9.11.
Разомкнув систему по углу и заданию токов, можно производить
настройку ПИД-регуляторов тока.
Практическая работа
Скомпилируйте программу с уровнем компиляции 2. На данном этапе
структурная схема будет соответствовать разомкнутой по скорости и положению. Задачей текущего этапа является настройка ПИ-регуляторов тока.
259
Лист. 9.4. Определение второго уровня построения проекта
#define BUILDLEVEL LEVEL2
Изучите код программы. Обратите внимание на то, что по флагу
EnableFlag запускается осциллограф. Это позволит синхронизировать
момент подачи напряжения на статор двигателя с моментом начала записи
осциллографа.
Изменяйте значение флага разрешения работы с 0 на 1 и наблюдайте
переходный процесс в контуре тока, изображенном на рис. 9.12.
Настраивать ПИД-регулятор тока можно с помощью окна редактирования
переменных WatchWindow (рис. 9.13).
Рис. 9.12. Переходный процесс в контуре тока
Рис. 9.13. Процесс настройки ПИД-регуляторов тока
260
Лист. 9.5. Пример части программы второго уровня построения проекта
if (EnableFlag!=EnableFlag_prev)
{
dlog.graph_ptr1=(type_data *)dlog.dl_buffer1_adr;
dlog.graph_ptr2=(type_data *)dlog.dl_buffer2_adr;
dlog.graph_ptr3=(type_data *)dlog.dl_buffer3_adr;
dlog.graph_ptr4=(type_data *)dlog.dl_buffer4_adr;
}
if (EnableFlag==0)
{
pid1_id.Ui=0;
pid1_iq.Ui=0;
aci1.Ualpha = 0;
aci1.Ubeta = 0;
}
else
{
aci1.Ualpha = ipark1.Alpha;
aci1.Ubeta = ipark1.Beta;
}
Контрольные вопросы
1. Настройте регуляторы на технический оптимум.
2. Исследуйте работу системы, задавая различные значения скорости. Для этого
переведите режим осциллографа в «продолжительный» и в режиме реального времени
наблюдайте изменения тока, скорости, момента. Постройте механическую характеристику получившейся системы, подобную изображенной на рис. 9.14.
Рис. 9.14. Механическая характеристика при частотно-токовом управлении
261
9.7. МОДУЛЬ ОПРЕДЕЛЕНИЯ ПОЛОЖЕНИЯ
ВЕКТОРА ПОТОКОСЦЕПЛЕНИЯ РОТОРА
Для перехода от разомкнутой системы к системе векторного управления с
ориентацией по вектору потокосцепления ротора добавим модуль
CUR_MOD. Этот модуль вычисляет положение вектора потокосцепления
ротора. Структура и математическое описание модуля представлены в каталоге C:\tidcs\DMC\c28\v32x\lib\doc\cur_mod.pdf.
IQs
IDs
pu
pu CUR_MOD pu
Theta
Wr
pu
Скомпилируйте программу с третьим уровнем компиляции и загрузите в
микроконтроллер. Переключите окно просмотра на вкладку BUILD 3. Запустите расчет в режиме реального времени.
Система сконфигурирована так, что после разрешения работы программы
задание тока по оси d становится равным 0,5 о.е., а по оси q равным 0. Для
того чтобы привести двигатель во вращение, необходимо задать моментообразующую компоненту тока статора — ток по оси q. Задайте значение, равное 0,1. Наблюдайте разгон двигателя с постоянством момента. Для реверса
двигателя задайте отрицательное значение тока. Получаемые графики
должны иметь вид, приведенный на рис. 9.15.
Обратите внимание на то, что при достижении определенной скорости
двигатель перестает разгоняться. Это связано с тем, что исчерпаны все
запасы по напряжению и регуляторы токов насыщены. Для того чтобы двигатель мог разгоняться дальше, необходимо уменьшить поле ротора. Для
того чтобы ослабить поле ротора, уменьшите значение задания тока d.
Контрольные вопросы
1. Изучите код программы. Определите, на каких графиках отображаются ток статора, угол положения вектора потокосцепления ротора, скорость двигателя и момент.
2. Получите механическую характеристику системы при различных токах d и q.
3. Исследуйте поведение регуляторов тока в зависимости от скорости.
9.8. ИНТЕГРАЦИЯ ПИ-РЕГУЛЯТОРА СКОРОСТИ.
ИССЛЕДОВАНИЕ СИСТЕМЫ ВЕКТОРНОГО
УПРАВЛЕНИЯ АСИНХРОННЫМ ДВИГАТЕЛЕМ
Для получения полноценной системы векторного управления нам необходимо добавить регулятор скорости. Ограничение регулятора скорости задает
предельное значение задания тока в системе векторного управления, таким
образом ограничивая электромагнитный момент двигателя.
262
Рис. 9.15. Работа системы векторного управления
Скомпилируйте программу с четвертым уровнем и загрузите ее в микроконтроллер. Система полностью готова к работе. Переключитесь в режим
реального времени и запустите программу на выполнение.
263
Рис. 9.16. Переходные процессы в режиме векторного управления (ток, угол вектора потокосцепления ротора, момент, скорость)
Определите, какие графические окна связаны с токами статора, положением поля ротора, заданием скорости, текущей скоростью.
Задавая различные задания скорости, наблюдайте переходный процесс по
скорости (рис. 9.16).
Для изучения системы обратитесь к исходному коду. Убедитесь, что программный код соответствует предложенной структурной схеме.
Контрольные вопросы
1. Измените настройки регулятора скорости так, чтобы переходный процесс по скорости стал колебательным, а затем попытайтесь настроить регулятор на технический
оптимум.
2. Постройте механическую характеристику исследуемой системы векторного управления АД.
264
Задания для самостоятельного изучения
1. Изучите механическую характеристику системы векторного управления. Как по нескольким точкам, для нескольких заданий тока d и q построить
граничную и предельную характеристики двигателя?
2. Предложите альтернативные способы расчета положения вектора потокосцепления ротора, например в стационарных осях.
3. Подумайте, как должна быть устроена система векторного управления
для реализации бездатчикового варианта?
265
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
1. Анучин А.С., Козаченко В.Ф. Архитектура, система команд, технология проектирования
и отладки специализированных сигнальных микроконтроллеров для управления двигателями: Лабораторный практикум. М.: Издательство МЭИ, 2001. 96 с.
2. Анучин А.С., Козаченко В.Ф. Архитектура и программирование DSP-микроконтроллеров TMS320x24xx для управления двигателями в среде Code Composer: Лабораторный
практикум. М.: Издательство МЭИ, 2003. 96 с.
3. Ильинский Н.Ф., Козаченко В.Ф. Общий курс электропривода: учебник для вузов. М.:
Энергоатомиздат, 1992. 544 с.
4. Козаченко В.Ф. Микроконтроллеры: руководство по применению 16-разрядных микроконтроллеров Intel MCS-196/296 во встроенных системах управления. М.: ЭКОМ, 1997.
688 с.
5. Брайан Керниган, Деннис Ритчи. Язык программирования Си. М.: Невский Диалект,
2000. 352 с.
6. TMS320F2810, TMS320F2812 Digital Signal Processors. Data Manual. Literature Numger:
SPRS174H. Texas Instruments Inc., 2003. 168c.
7. TMS320C28x Optimizing C/C++ Compiler User’s Guide. Preliminary. Literature Numger:
SPRU514. Texas Instruments Inc., 1997. 351 c.
8. An Easy Way of Creating a C-callable Assembly Function for the TMS320C28x DSP. Literature
Numger: SPRA806. Texas Instruments Inc., 2001. 16 c.
9. Filter Library. Module user’s Guide C28x. Foundation Software. Texas Instruments Inc., 2002.
23 c.
10. Clarke & Park. Transforms on the TMS320C2xx. Application Report. Literature Numger:
BRPA048. Texas Instruments Inc., 1996.
11. Digital Motor Control. Software Library. Digital Control Systems (DCS) Group. Literature
Numger: SPRU485. Texas Instruments Inc., 2001.
12. Уэйт М., Прата С., Мартин Д. Язык Си. Руководство для начинающих: пер. с англ. М.:
Мир, 1988. 512 с.
13. Болски М.И. Язык программирования Си: Справочник: пер. с англ. М.: Радио и связь,
1988. 96 с.
266
ОГЛАВЛЕНИЕ
Предисловие . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
Глава 1. Устройство и функциональные возможности оценочной
платы eZdspTM F2812 для разработки и отладки программного обеспечения
в интегрированной среде Code Composer Studio. Введение в среду CCS.
Технология распределения памяти . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.1. Оценочная плата eZdsp
TM
F2812 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TM
1.2. Организация памяти. Карта памяти оценочной платы eZdsp F2812 . . . . . . . . .
1.3. Введение в интегрированную среду разработки и отладки программного
обеспечения Code Composer Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4. Технология распределения ресурсов памяти целевой платы с помощью файла
управления компоновкой. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 2. Создание и отладка простых программ на языке высокого уровня С/С++
в среде Code Composer Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1. Проект. Создание нового проекта. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2. Создание и редактирование файла исходной программы на языке С/С++ . . . . . .
2.3. Создание и редактирование файла управления компоновкой . . . . . . . . . . . . . . . .
2.4. Подключение файлов к проекту . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5. Установка опций компилятора и компоновщика . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6. Сборка и загрузка проекта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.7. Анализ файла с картой загрузки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.8. Использование окон отладочной среды CCS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.9. Выполнение программы по шагам . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.10. Языки С/C++ и Ассемблер. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 3. Знакомство с языком программирования Си для микроконтроллеров
TMS320x28xx. Освоение методов отладки программ в среде Code Composer Studio .
3.1. Краткие общие сведения о языке программирования Си . . . . . . . . . . . . . . . . . . . .
3.2. Использование стандартных библиотек. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3. Функция форматированного вывода printf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4. Некоторые рекомендации по отладке программ . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5. Подключение стандартной библиотеки ввода/вывода. Модернизация файла
управления компоновкой. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.6. Выполнение программы в режиме прогона . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.7. Выполнение программы с точками останова. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.8. Дополнительные возможности по изучению системы команд
микроконтроллера ‘C28x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
18
25
36
41
41
44
45
47
49
51
56
59
64
64
68
68
80
81
84
85
89
90
92
267
Глава 4. Базовые возможности языка программирования Си . . . . . . . . . . . . . . . . . . . .
4.1. Операторы ветвления. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2. Операторы организации циклов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3. Работа с указателями . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4. Указатели и массивы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.5. Функции в языке Си . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 5. Технология работы с периферийными устройствами микроконтроллеров
семейства C28xx на языке Си. Работа с портами дискретного ввода/вывода,
таймерами, контроллером прерываний . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1. Способы доступа к регистрам встроенных периферийных устройств
микроконтроллера на языке Си. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2. Структура заголовочного файла периферийного устройства . . . . . . . . . . . . . . . . .
5.3. Размещение структур регистровых файлов в памяти данных . . . . . . . . . . . . . . . .
5.4. Файл определения глобальных переменных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.5. Программирование периферии ‘C28xx с использованием заголовочных файлов
5.6. Основы работы с таймерами общего назначения . . . . . . . . . . . . . . . . . . . . . . . . . .
5.7. Графические окна . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.8. Введение в систему прерываний микроконтроллеров ‘C28xx . . . . . . . . . . . . . . . .
5.9. Отладка программ в реальном времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
97
97
100
103
106
110
116
116
121
125
126
127
132
136
137
143
Глава 6. Работа с библиотекой IQmath. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.1. Представление чисел в микроконтроллере . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2. Особенности работы с двоичными числами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3. Основы работы с библиотекой IQmath и со стандартной библиотекой поддержки
операций с плавающей точкой . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4. Изменение формата чисел при работе с библиотекой IQmath . . . . . . . . . . . . . . . .
6.5. Сравнение производительности при работе с числами в различных форматах . .
147
147
150
Глава 7. Эффективные способы цифровой фильтрации сигналов . . . . . . . . . . . . . . . .
7.1. Цифровой 16-разрядный фильтр с конечной импульсной характеристикой . . . . .
7.2. Интерфейс с программой на Ассемблере, вызываемой из Си-программы . . . . . .
7.3. Исследование работы модуля фильтра с конечной импульсной характеристикой
7.4. Использование пакета MatLab для расчета коэффициентов фильтра . . . . . . . . . .
7.5. Фильтры 16- и 32-разрядные с бесконечной импульсной характеристикой . . . . .
179
179
188
193
198
201
Глава 8. Технология разработки и отладки системы цифрового управления
двигателем постоянного тока . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.1. Аппаратная структура системы управления . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2. Математическая модель двигателя постоянного тока в относительных единицах
8.3. Дискретная модель двигателя постоянного тока . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.4. Передаточные функции силовых преобразователей . . . . . . . . . . . . . . . . . . . . . . . .
8.5. Синтез цифровой системы подчиненного регулирования координат
электропривода . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.6. Построение проекта шаг за шагом . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
268
153
173
175
209
209
210
215
216
219
223
Глава 9. Исследование системы векторного управления асинхронным двигателем . 236
9.1. Структура аппаратной части системы ПЧ—АД . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
9.2. Математическая модель асинхронного двигателя . . . . . . . . . . . . . . . . . . . . . . . . . . 237
9.3. Принципы частотного регулирования . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
9.4. Структура проекта. Рабочая область проекта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
9.5. Исследование модели асинхронного двигателя в режиме скалярного управления 253
9.6. Исследование частотно-токовой системы управления асинхронного двигателя.
Настройка ПИ-регулятора тока. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
9.7. Модель определения положения вектора потокосцепления ротора . . . . . . . . . . . . 262
9.8. Интеграция ПИ-регулятора скорости. Исследование системы векторного
управления асинхронным двигателем . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Библиографический список . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
266
269
Учебное издание
Анучин Алексей Сергеевич, Алямкин Дмитрий Иванович,
Дроздов Андрей Владимирович, Козаченко Владимир Филиппович,
Тарасов Алексей Сергеевич
ВСТРАИВАЕМЫЕ ВЫСОКОПРОИЗВОДИТЕЛЬНЫЕ
ЦИФРОВЫЕ СИСТЕМЫ УПРАВЛЕНИЯ
Практический курс разработки и отладки программного обеспечения
сигнальных микроконтроллеров TMS320x28xxx
в интегрированной среде Code Composer Studio
Учебное пособие
по курсу
«Микропроцессорные системы управления»
для студентов, обучающихся по направлениям «Электротехника, электромеханика
и электротехнологии», «Промышленная электроника», «Роботы и манипуляторы», для слушателей
курсов повышения квалификации специалистов промышленности и преподавателей вузов
Редактор О.М. Горина
Художественный редактор А.Ю. Землеруб
Технический редактор Т.А. Дворецкова
Корректоры Е.П. Севостьянова, В.В. Сомова
Компьютерная верстка Л.Н. Тыгиной
Подписано в печать с оригинала-макета 7.11.10
Бумага офсетная
Гарнитура Таймс
Усл. печ. л. 22,1
Усл. кр.-отт. 23,1
Тираж 400 экз.
Заказ №
Формат 70×100/16
Печать офсетная
Уч.-изд.л. 18,9
ЗАО «Издательский дом МЭИ», 111250, Москва, Красноказарменная ул., 14
тел./факс: (495)361-16-81, адрес в Интернете: http://www.mpei-publishers.ru,
электронная почта: publish@mpei.ru
Отпечатано в ООО «Галлея-Принт», 111024, Москва, ул. 5-я Кабельная, д. 2Б
Скачать