МК может содержать несколько шинных преобразователей, например, AHB-APB1 и AHB-APB2. С помощью периферийных шин APB (Advanced peripherial bus)


Қазақстан РеспубликасыныңМинистерство
Білім және ғылымобразования и науки
министрлігіРеспублики Казахстан
Д. Серікбаев атындағы
ШҚМТУ ВКГТУ им. Д. Серикбаева
УТВЕРЖДАЮ
декан ФИТЭ
Г.Х.Мухамедиев___________________2012 г.
ПРОГРАММИРОВАНИЕ МИКРОПРОЦЕССОРНЫХ СИСТЕМ
Конспект лекций
Специальность: 6М071600 «Приборостроение»
Форма обучения: очная
Курс: 1
Семестр: 2
Кол-во кредитов: 3
Кол-во часов:
Лекции: 15
Лабораторные работы: 30
СРСП: 45
СРС: 45
Экзамен: 2семестр
Өскемен
Усть-Каменогорск
2014
Тема 1 Цифровое чтение – запись двоичной информации
Лекция 1. Введение
1.1 Основные понятия
Условно все программное обеспечение компьютера подразделяется на два класса – системное программное обеспечение (СПО) и прикладное программное обеспечение (ППО).
Многие авторы определяют «Системное программное обеспечение» как комплекс программ, которые обеспечивают управление компонентами компьютера, такими как процессор, оперативная и долговременная память, шинные формирователи, таймеры, сетевые адаптеры, и т.д. При этом СПО выступает как «межслойный интерфейс», с одной стороны которого аппаратура, а с другой — приложения пользователя. Обычно структура СПО представляется рисунком типа рисунок 1.

Рисунок 1.1 Структура системного программного обеспечения компьютера
Деление структуры СПО на базовое и сервисное показывает, что есть минимальный (фиксированный) набор программного обеспечения, обеспечивающего работу компьютера и, как правило, поставляемого при продаже компьютера. И есть сервисное программное обеспечение – набор программ и программных комплексов, которые расширяют возможности базового программного обеспечения и организуют более удобную среду работы пользователя.
При этом сервисное программное обеспечение постоянно расширяется, включая дополнительные возможности улучшающие среду работы пользователей.
«Системное программное обеспечение включает в себя все программы, работающие c аппаратной конфигурацией (т.е. с «железом»). Поэтому главной задачей системного программного обеспечения является управление аппаратными компонентами компьютера (оперативной памятью, процессором, принтером, сканером, мультимедиа адаптером, клавиатурой, мышкой и пр.)
Иногда под понятием «системное программное обеспечение персонального компьютера» подразумевают операционную систему, что не совсем верно. Операционная система является лишь частью системного программного обеспечения, но не единственной его частью и не полным аналогом системного ПО.»
Многие авторы определяют еще одну составляющую СПО – специальное программное обеспечение,
1.2 Состав базового программного обеспечения
В состав базового программного обеспечения входят операционная система и операционные оболочки.
Операционная система – комплекс системных программ, расширяющий возможности вычислительной системы, а также обеспечивающий управление её ресурсами, загрузку и выполнение прикладных программ, взаимодействие с пользователями. В большинстве вычислительных систем операционные системы являются основной, наиболее важной (а иногда единственной) частью системного программного обеспечения.
Операционные оболочки - специальные программы, предназначенные для общения пользователя с командами операционной системы. Операционные оболочки имеют текстовый и графический варианты интерфейса конечного пользователя. Например, наиболее известной текстовой оболочкой операционных систем Windows является оболочка Far.
Необходимо отметить, что материал базового программного обеспечения изучался в дисциплинах «Системное программирование», «Операционные системы» бакалавров.
1.3 Состав сервисное программное обеспечение
Расширением базового программного обеспечения компьютера является набор сервисных, дополнительно устанавливаемых программ, которые можно классифицировать по функциональному признаку следующим:
– программы диагностики работоспособности компьютера;
– антивирусные программы, обеспечивающие защиту компьютера, обнаружение и восстановление зараженных файлов;
– программы обслуживания дисков, обеспечивающие проверку качества поверхности магнитного диска, контроль сохранности файловой системы на логическом и физическом уровнях, сжатие дисков, создание страховых копий дисков, резервирование данных на внешних носителях и др.;
– программы архивирования данных, которые обеспечивают процесс сжатия информации в файлах с целью уменьшения объема памяти для ее хранения;
– программы обслуживания сети и т.д.
Эти программы часто называются утилитами.
Утилиты – это программы, служащие для выполнения вспомогательных операций обработки данных или обслуживания компьютеров. Например, утилиты используются для мониторинга показателей датчиков и производительности оборудования (мониторинга температур процессора или видеоадаптера), управления параметрами оборудования (ограничение максимальной скорости вращения CD-привода; изменение скорости вращения вентиляторов), контроля показателей (проверка ссылочной целостности; правильности записи данных), расширения возможностей (форматирование или переразметка диска с сохранением данных, удаление без возможности восстановления).
«Утилиты (англ. utility или tool) — программы, предназначенные для решения узкого круга вспомогательных задач. Иногда утилиты относят к классу сервисного программного обеспечения.»
Необходимо отметить, что сервисный состав программного обеспечения постоянно расширяется и модернизируется – появляются новые антивирусные программы, новые архиваторы и т.д.
Часть материала сервисного программного обеспечения изучался в дисциплинах «Интернет технологии», «Защита информации», «Компьютерные системы» бакалавров.
1.4 Специальное программное обеспечение
Обычно специальное программное обеспечение разрабатывается как приложение для нестандартных информационных или управляющих систем.
«К специальному программному обеспечению информационных и управляющих систем относятся
программы (системы) управления базами данных;
программы управления языком интерфейса информационных систем;
программы сбора и предварительной обработки информации (в информационно-измерительных системах, например, бортовые системы).
ПО этого класса часто оказывается скрытым в составе драйверов оборудования или поставляется в виде библиотек функционального расширения языков программирования.
Поэтому часто такое ПО относят к системному программному обеспечению.»
В дисциплине «Системное программное обеспечение» для магистрантов будут рассмотрены задачи программирования нестандартных устройств, подключаемых к компьютеру. Необходимость рассмотрения этой задачи системного программирования объясняется тем, что персональные компьютеры находят широкое использование в различных системах автоматики для сбора, хранения и обработки информации как при управлении отдельными объектами, так и в составе сложных систем управления технологическими процессами.
В настоящее время практически единственными универсальными разъемами для подключения внешних устройств к компьютеру является USB разъемы (их можно использовать очень много). Поэтому, мы будим изучать управление внешними устройствами через USB порт с использованием отладочной платы STM32L-Discovery и установленным на ней микроконтроллером STM32L152RBT6.
Все программные разработки для микроконтроллера мы будем выполнять в интегрированной среде KeiluVision 4 на языке C (разработок на языке C# пока нет), а проекты для ПК мы будем выполнять в среде Visual Studio.NET на языке C#.
При изучении в университете основ программирования в дисциплине «Алгоритмизация и программирование» вопросы программирования, связанные с побитной обработкой информации, которая широко используется в программировании микроконтроллеров, практически не рассматривались. Поэтому в первой теме дисциплины «Системное программное обеспечение» мы рассмотрим эти основы программирования на языке C#.
Тема 1 Цифровое чтение – запись двоичной информации
Лекция 1. Введение
1.1 Основные понятия
Условно все программное обеспечение компьютера подразделяется на два класса – системное программное обеспечение (СПО) и прикладное программное обеспечение (ППО).
Многие авторы определяют «Системное программное обеспечение» как комплекс программ, которые обеспечивают управление компонентами компьютера, такими как процессор, оперативная и долговременная память, шинные формирователи, таймеры, сетевые адаптеры, и т.д. При этом СПО выступает как «межслойный интерфейс», с одной стороны которого аппаратура, а с другой — приложения пользователя. Обычно структура СПО представляется рисунком типа рисунок 1.

Рисунок 1.1 Структура системного программного обеспечения компьютера
Деление структуры СПО на базовое и сервисное показывает, что есть минимальный (фиксированный) набор программного обеспечения, обеспечивающего работу компьютера и, как правило, поставляемого при продаже компьютера. И есть сервисное программное обеспечение – набор программ и программных комплексов, которые расширяют возможности базового программного обеспечения и организуют более удобную среду работы пользователя.
При этом сервисное программное обеспечение постоянно расширяется, включая дополнительные возможности улучшающие среду работы пользователей.
«Системное программное обеспечение включает в себя все программы, работающие c аппаратной конфигурацией (т.е. с «железом»). Поэтому главной задачей системного программного обеспечения является управление аппаратными компонентами компьютера (оперативной памятью, процессором, принтером, сканером, мультимедиа адаптером, клавиатурой, мышкой и пр.)
Иногда под понятием «системное программное обеспечение персонального компьютера» подразумевают операционную систему, что не совсем верно. Операционная система является лишь частью системного программного обеспечения, но не единственной его частью и не полным аналогом системного ПО.»
Многие авторы определяют еще одну составляющую СПО – специальное программное обеспечение,
1.2 Состав базового программного обеспечения
В состав базового программного обеспечения входят операционная система и операционные оболочки.
Операционная система – комплекс системных программ, расширяющий возможности вычислительной системы, а также обеспечивающий управление её ресурсами, загрузку и выполнение прикладных программ, взаимодействие с пользователями. В большинстве вычислительных систем операционные системы являются основной, наиболее важной (а иногда единственной) частью системного программного обеспечения.
Операционные оболочки - специальные программы, предназначенные для общения пользователя с командами операционной системы. Операционные оболочки имеют текстовый и графический варианты интерфейса конечного пользователя. Например, наиболее известной текстовой оболочкой операционных систем Windows является оболочка Far.
Необходимо отметить, что материал базового программного обеспечения изучался в дисциплинах «Системное программирование», «Операционные системы» бакалавров.
1.3 Состав сервисное программное обеспечение
Расширением базового программного обеспечения компьютера является набор сервисных, дополнительно устанавливаемых программ, которые можно классифицировать по функциональному признаку следующим:
– программы диагностики работоспособности компьютера;
– антивирусные программы, обеспечивающие защиту компьютера, обнаружение и восстановление зараженных файлов;
– программы обслуживания дисков, обеспечивающие проверку качества поверхности магнитного диска, контроль сохранности файловой системы на логическом и физическом уровнях, сжатие дисков, создание страховых копий дисков, резервирование данных на внешних носителях и др.;
– программы архивирования данных, которые обеспечивают процесс сжатия информации в файлах с целью уменьшения объема памяти для ее хранения;
– программы обслуживания сети и т.д.
Эти программы часто называются утилитами.
Утилиты – это программы, служащие для выполнения вспомогательных операций обработки данных или обслуживания компьютеров. Например, утилиты используются для мониторинга показателей датчиков и производительности оборудования (мониторинга температур процессора или видеоадаптера), управления параметрами оборудования (ограничение максимальной скорости вращения CD-привода; изменение скорости вращения вентиляторов), контроля показателей (проверка ссылочной целостности; правильности записи данных), расширения возможностей (форматирование или переразметка диска с сохранением данных, удаление без возможности восстановления).
«Утилиты (англ. utility или tool) — программы, предназначенные для решения узкого круга вспомогательных задач. Иногда утилиты относят к классу сервисного программного обеспечения.»
Необходимо отметить, что сервисный состав программного обеспечения постоянно расширяется и модернизируется – появляются новые антивирусные программы, новые архиваторы и т.д.
Часть материала сервисного программного обеспечения изучался в дисциплинах «Интернет технологии», «Защита информации», «Компьютерные системы» бакалавров.
1.4 Специальное программное обеспечение
Обычно специальное программное обеспечение разрабатывается как приложение для нестандартных информационных или управляющих систем.
«К специальному программному обеспечению информационных и управляющих систем относятся
программы (системы) управления базами данных;
программы управления языком интерфейса информационных систем;
программы сбора и предварительной обработки информации (в информационно-измерительных системах, например, бортовые системы).
ПО этого класса часто оказывается скрытым в составе драйверов оборудования или поставляется в виде библиотек функционального расширения языков программирования.
Поэтому часто такое ПО относят к системному программному обеспечению.»
В дисциплине «Системное программное обеспечение» для магистрантов будут рассмотрены задачи программирования нестандартных устройств, подключаемых к компьютеру. Необходимость рассмотрения этой задачи системного программирования объясняется тем, что персональные компьютеры находят широкое использование в различных системах автоматики для сбора, хранения и обработки информации как при управлении отдельными объектами, так и в составе сложных систем управления технологическими процессами.
В настоящее время практически единственными универсальными разъемами для подключения внешних устройств к компьютеру является USB разъемы (их можно использовать очень много). Поэтому, мы будим изучать управление внешними устройствами через USB порт с использованием отладочной платы STM32L-Discovery и установленным на ней микроконтроллером STM32L152RBT6.
Все программные разработки для микроконтроллера мы будем выполнять в интегрированной среде KeiluVision 4 на языке C (разработок на языке C# пока нет), а проекты для ПК мы будем выполнять в среде Visual Studio.NET на языке C#.
При изучении в университете основ программирования в дисциплине «Алгоритмизация и программирование» вопросы программирования, связанные с побитной обработкой информации, которая широко используется в программировании микроконтроллеров, практически не рассматривались. Поэтому в первой теме дисциплины «Системное программное обеспечение» мы рассмотрим эти основы программирования на языке C#.
Тема 1 Цифровое чтение – запись двоичной информации
Лекция 3 Архитектура микроконтроллеров семействаSTM32
3.1 Обобщенная архитектура микроконтроллеров STM32
Все микроконтроллеры (МК) семействаSTM32 (компании STMicroelectronics) выполнены на основе 32 разрядных микропроцессоров CortexM3. В настоящий момент известны три типа микропроцессоров Cortex это: CortexA для «высокопроизводительных применений», CortexR «для реально-временных применений» и CortexM для «чувствительных к стоимости и для микроконтроллерных применений». Микропроцессор CortexM3 «специально разработан для применений, где необходимы развитые системные ресурсы и, при этом, малое энергопотребление».
Обобщенная архитектура микроконтроллеров STM32 изображена на рисунке 3.1.

Рисунок 3.1 – Обобщенная архитектура микроконтроллеров STM32
Микропроцессор имеет три шины:
I-bus, через эту шину инструкций к нему подключается Флэш-память;
D-bus, через эту шину данных он может обмениваться информацией со всеми устройствами МК;
System-bus, через эту системную шину микропроцессор обменивается сигналами со всеми устройствами МК, осуществляя синхронизацию их работы.
Шина инструкций подключена к Флэш-памяти напрямую, а шина данных и системная шина соединена со специальной матрицей высокоскоростных шин AHB (Advanced high-performance bus).
МК может содержать несколько шинных преобразователей, например, AHB-APB1 и AHB-APB2. С помощью периферийных шин APB (Advanced peripherial bus) осуществляется связь микропроцессора с периферийными устройствами МК.
Внутреннее статическое оперативное запоминающее устройство (ОЗУ) и блок прямого доступа к памяти (ПДП) подключены напрямую к матрице шин AHB.
В некоторых литературных источниках приводится расширенная (максимально возможная) архитектура МК STM32, которая изображена на рисунке 3.2.

Рисунок 3.2 – Архитектура микроконтроллеров STM32
3.2 Назначение основных устройств МК STM32
Рассмотрим назначение устройств МК STM32 (подробное назначение и работа некоторых устройств архитектуры МК STM32 будет рассмотрено в следующих лекциях):
ЦПУ – центральное процессорное устройство или 32-битное RISC ЦПУ CORTEX M3;
Интерфейсы JTAG/SW – эти интерфейсы используются для подключения отладочной системы CoreSight микроконтроллеров STM32. Отладочные инструментальные средства могут использовать для подключения стандартный 5-выводной интерфейс JTAG или 2-проводной интерфейс Serial Wire. Отладочная система отвечает за выполнение функций управления исполнением программы и трассировки. Ее дополнительное преимущество заключается в возможности оставаться в работе, даже когда МК STM32 находится в экономичном режиме;
КВВП – контроллер вложенных векторизованных прерываний (КВВП) является стандартным блоком ядра Cortex. Это означает, что у любого Cortex-микроконтроллера будет присутствовать одна и та же структура прерываний, независимо от его производителя. Контроллер поддерживает одно немаскируемое прерывание и еще до 240 внешних линий прерывания, которые можно подключить к пользовательским УВВ. В ядре Cortex поддерживается еще 15 дополнительных источников прерываний, использующихся для обработки внутренних исключительных ситуаций ядра Cortex;
Таймер Systic – в ядро Cortex входит 24-битный вычитающий счетчик с функциями автоматической перезагрузки и генерации прерывания. Он называется таймером SysTick и предназначен для использования в качестве стандартного таймера во всех МК STM32. Таймер SysTick может использоваться для формирования шкалы времени в ОСРВ или для генерации периодических прерываний для обработки запланированных задач. С помощью регистра управления и статуса таймера SysTick, который расположен в области системных ресурсов процессора Cortex-M3, пользователь может выбрать источник синхронизации таймера;
ПДП 3…11 каналов – передача данных между УВВ и внутренним статическим ОЗУ может осуществляться, как при участии ЦПУ Cortex, так и автоматически под управлением встроенного блока прямого доступа к памяти (ПДП). Блок ПДП МК STM32 имеет семь раздельно настраиваемых каналов, позволяющие автоматически передавать данных из памяти в память, из УВВ в память, из памяти в УВВ и из УВВ в УВВ. Передача память-память выполняется с максимально-возможным для канала ПДП быстродействием. Если же в передаче данных участвует УВВ, то работа блока ПДП синхронизируется УВВ. Передача данных будет происходить по запросу УВВ в любом из направлений;
6х16 бит ШИМ – Для управления электродвигателем обычно используется 1 таймер блока таймеров. Этот таймер дополнен рядом аппаратных узлов, предназначенных для управления электродвигателем. В каждом из трех каналов этого таймера предусмотрены два противофазных выхода. Таким образом, он представляет собой 6-канальный ШИМ-блок. Поскольку данный блок предназначен для управления трехфазным электродвигателем, у него имеется возможность программирования в каждом канале паузы неперекрытия и общий вход экстренного отключения. Кроме того, в дополнение к интерфейсу энкодера здесь предусмотрен интерфейс подключения датчиков Холла;
до 16 внешних прерываний – этот блок позволяет обрабатывать до 16 внешних прерываний, поступающих на соответствующие линии ввода-вывода МК;
до 80 линий ввода-вывода – двунаправленные линии ввода-вывода разделены на 5 портов по 16 линий ввода-вывода в каждой. Данные порты называются A…E и совместимы с напряжением 5В. Многие из внешних выводов могут выполнять альтернативную функцию линии ввода-вывода встроенного УВВ, например, модуля УСАПП или I2C. Кроме того, 16 входных линий встроенного блока внешних прерываний могут быть подключены к любой из линий портов ввода-вывода. Основное назначение линий ввода-вывода МК это обмен информацией с внешними устройствами;
1 х SPI, 2 х SPI/ I2C, 0/1 x SPI – для организации быстродействующей связи с интегральными схемами у МК STM32 имеется два модуля SPI, предназначенных для полнодуплексной передачи данных на частоте до 18МГц. Важно обратить внимание, что один модуль SPI подключен высокоскоростной шине УВВ APB2, которая может работать на частоте до 72МГц. Второй модуль связан с более низкоскоростной шиной APB1, максимальное быстродействие которой ограничивается частотой 36 МГц;
1 х УСАПП/LIN и/ф смарт-карт/lrDA управления модемом, 1/2/4 х УСАПП/LIN и/ф смарт-карт/lrDA управления модемом – несмотря на то, что порты последовательной связи уже практически не используются в ПК, они все еще остаются популярными во многих встраиваемых применениях для организации простого интерфейса последовательной связи. Его высокая популярность обусловлена свойственной ему надежностью работы и простотой использования. В МК STM32 интегрируется до 3 модулей УСАПП, каждый из которых поддерживает несколько расширенных режимов работы, позволяющие использовать МК в самых современных коммуникационных применениях. Все три УСАПП способны передавать данные на скорости до 4.5 Мбит/сек. Каждый из них также полностью программируется, в т.ч. размер передаваемых данных (8 или 9 бит), передача бита паритета или стоп-бита, а также скорость передачи. Один УСАПП подключен к шине APB2, которая способна синхронизироваться частотой до 72 МГц. а остальные связаны с 36-мегагерцевой шиной APB1;
32-512 кбайт флэш-памяти – встроенная в МК STM32 Flash память состоит из трех областей. Первая из них - основная Flash память, предназначенная для хранения кода программы. Данная память является 64-битной. Это необходимо для повышения быстродействия считывания инструкций в буфер предварительной выборки. Для выполнения программирования и стирания данная Flash память разделена на 4 тыс. страниц. Память характеризуется износостойкостью 10 тысяч циклов перезаписи и 30-летним хранением данных при температуре 85°С. Износостойкость Flash памяти большинства других микроконтроллеров приводится для 25°C. Это означает, что МК STM32 оснащены превосходной Flash памятью. Помимо основной памяти программ, также имеется две области меньшего размера: большой информационный блок и малый информационный блок. Большой информационный блок занимает 2 кбайт Flash памяти и предназначен для хранения запрограммированной производителем программы загрузчика, которая использует для передачи кода программы последовательный интерфейс УСАПП 1. Малый информационный блок состоит из шести конфигурационных байт. Они предназначены для определения свойств сброса МК STM32 и управления защитой памяти;
6-16 кбайт СОЗУ – статическое оперативное запоминающее устройство в основном используется совместно с блоком ПДП для обмена информацией между узлами МК;
20 байтовых регистров с резервным питанием – это обычные 10 ячеек памяти, которые можно использовать для хранения критической информации, когда МК STM32 находится в дежурном режиме работы и основное питание отключено.
Интерфейс внешней памяти – интерфейс для подключения дополнительной внешней памяти;
Интерфейс фотосенсора – интерфейс для подключения фотосенсора;
Управление синхронизацией – управляющий блок МК;
Матрица/арбитр высокоскоростных шин ARM Lite(Макс. 72 МГц) – матрица высокоскоростных шин, которую обычно называют AHB (Advanced high-performance bus). Режим работы этой матрицей задается двоичным кодом – арбитром;
Шинные преобразователи – преобразователи сигналов шины AHB в сигналы шин УВВ (устройств ввода-вывода) или APB (Advanced peripherial bus) с помощью которых осуществляется связь микропроцессора с периферийными устройствами МК;
2/3/5 х 16-битных таймера – у МК STM32 имеется четыре блока таймеров. Таймер 1 - расширенный таймер, предназначенный для управления электродвигателем. Остальные таймеры являются таймерами общего назначения. Все таймеры выполнены по общей архитектуре, а расширенный таймер отличается лишь добавлением специальных аппаратных блоков. Таймеры широко используются для организации программных прерываний работы МК;
2 сторожевых таймера – в МК STM32 входят два отдельных сторожевых таймера. Независимый сторожевой таймер полностью отделен от основной системы МК STM32. Он расположен в домене с резервированием питания и синхронизируется встроенным низкочастотным генератором (LSI). Его основное назначение выявление перебоев в работе основного внешнего генератора и безопасное переключение на работу внутреннего RC-генератора частотой 8МГц. Оконный сторожевой таймер является частью основной системы МК STM32 и связан с сигналом синхронизации первой шины УВВ, что позволяет быть уверенным не только в факте выполнения кода программы и во временных характеристиках его выполнения. Оба сторожевых таймера поддерживают возможность раздельного включения/отключения и могут использоваться одновременно.
2 х ЦАП – 2 цифро-аналоговых преобразователя, например, для преобразования цифрового сигнала в аналоговый сигнал при воспроизведении звука;
2 х 12-битных АЦП 16 каналов/1МГц – в зависимости от модели, в микроконтроллеры STM32 может быть встроено один или два аналогово-цифровых преобразователя. АЦП питаются отдельным напряжением, которое в зависимости типа корпуса может находиться в пределах 2.4…3.6В. Источник опорного напряжения (ИОН) АЦП соединен либо внутренне с напряжением питания АЦП, либо со специальными внешними выводами. АЦП характеризуется 12-битной разрешающей способностью и частотой преобразования 1МГц. У него имеется до 18 мультиплексированных каналов, 16 из которых можно использовать для измерения внешних сигналов. Оставшиеся два канала связаны со встроенным датчиком температуры и внутренним ИОН;
Датчик температуры – датчик измерения температуры окружающей среды;
Стабилизатор напряжения 1.8В, POR, PDR, PVD – управляемый стабилизатор напряжения, имеющий несколько режимов работы (с целью экономии питания) в зависимости от работы МК;
Несколько кварцевых генераторов – используются для синхронизации работы МК;
ФАПЧ – обычно МК STM32 синхронизируется блоком ФАПЧ и HSE-генератором частотой 72 МГц. Блок ФАПЧ (фазовой авто-подстройки частоты) широко используется для выбора наиболее экономичного режима работы МК;
Часы реального времени/AWU – входящие в МК STM32 часы реального времени представляют собой 32-битный счетчик, оптимизированный под счет секунд при синхронизации частотой 32.768 кГц. Во время настройки системы синхронизации, в качестве источника синхронизации часов реального времени можно выбрать внутренний низкочастотный генератор, внешний низкочастотный генератор или внешний высокочастотный генератор;1 x USB 2.0FS – модуль интерфейса USB, входящий в состав некоторых МК STM32, является полноскоростным (12 Мбит/сек) и предназначен для взаимодействия с хост-интерфейсом USB, как например, используемым в ПК. Данный модуль полностью реализует физический уровень и уровень передачи данных, в т.ч. проверка ошибок в пакетах и повторная передача. Интерфейс USB-устройства также поддерживает возможности приостановки и возобновления, необходимые для снижения потребляемой мощности;
1 x hxCAN 2.0B – входящий в МК STM32 CAN-контроллер является полнофункциональным CAN-узлом. Он отвечает требованиям предъявляемым к активным и пассивным устройствам CAB 2.0A и 2.0B и поддерживает передачу данных на скорости не более 1 Мбит/сек. CAN-контроллер оснащен также дополнительными возможностями для организации детерминистической передачи данных по специальному CAN-протоколу передачи в реальном времени TTCAN. После активизации функции TTCAN будет поддерживаться автоматическая повторная передача сообщений и автоматическая вставка в CAN-пакет двух дополнительных байт с зафиксированным моментом времени передачи сообщения. Все эти возможности необходимы в системах управления через CAN-интерфейс в масштабе реального времени;
1/2 x I2C – lля связи с интегральными схемами у МК STM32 имеется еще один специальный интерфейс - I2C. Интерфейс I2C может работать в ведущем или подчиненном режиме и поддерживает возможность арбитра шины, что необходимо в мультимастерных системах. Интерфейс I2C поддерживает оба скоростных режима шины: стандартный со скоростями до 100 кГц и быстродействующий со скоростями до 400 кГц. Модуль I2C использует 7- и 10-битные режимы адресации. Модуль полностью реализует протокол передачи данных по шине и требует для управления только необходимой протоколу передачи информации.
3.3 Семейство основных МК STM32
Существует очень много модификаций МК STM32, которые отличаются составами периферийных устройств, объемами памяти, частотой работы микропроцессора и допустимыми условиями работы и т.д. Обычно семейство МК STM32 представляются рисунком подобным 3.3, на котором отображена зависимость объема Флэш-памяти МК и количеством ног (pincount) платы микроконтроллера.
Мы будем работать с МК STM32L152RB, который содержит (в соответствии с рисунком 3.3) 128 KbФлэш-памяти и 64 ножки платы МК.

Рисунок 3.3 – Распределение семейства МК STM32 в зависимости от объема Флэш-памяти и количества ножек платы МК.
Между МК семейства STM32 принято говорить о pin-to-pin совместимость. «Pin-to-pin совместимость» означает, что для одного размера корпуса все сигналы сохраняются на тех же самых вводах/выводах для разных вариантов микроконтроллеров семейства. Pin-to-pin совместимость внутри семейств STM32 представлена на рисунке 3.4.

Рисунок 3.4 Pin-to-pin совместимость внутри семейств STM32
«Внутри серий STM32F-1, STM32F-2 и STM32L pin-to-pin совместимость полная. Если мы говорим, например, про серию STM32F-1, то речь идет о более чем 90 микросхемах, разделенных на пять семейств по применению, с полной pin-to-pin совместимостью. Хорошая pin-to-pin совместимость между STM32F-1 и STM32L, но для более новой линейки STM32F-2 потребуется больше работы.»Далее статья «Роман Попов (КОМПЭЛ)» из интернета.
«STM32. Урок 1. Выбор отладочной платы
В последнее время микроконтроллеры от ST Microelectronics на основе ядра Cortex начали активно набирать популярность, как среди профессиональных, так и среди начинающих разработчиков устройств на микроконтроллерах. Причин тому несколько:- невысокая цена по сравнению с конкурентами;- большое количество встроенных интерфейсов;- простота в программировании и высокая надежность.Но при этом есть и один важный недостаток – все микроконтроллеры STM не выпускаются в DIP корпусах, что зачастую просто отпугивает новичков, ведь изготовить плату с дорожками менее 0,3мм в домашних условиях проблематично. Такое положение дел привело к появлению на рынке огромного количества отладочных плат, как от ST Microelectronics (Discovery), так и платы, выпущенные сторонними фирмами (Olimex, Pinboard). Мой выбор пал на Discovery по трем причинам:- относительно невысокая цена (плату можно купить от 300р.);- хорошее качество исполнения (хотя некоторые претензии к разводке есть, но они не столь существенны);- много исходников и примеров выложено на сайте производителя;- наличие встроенного программатора (вам не придется покупать его отдельно).Цель первого урока – помочь начинающему разработчику выбрать отладочную плату, а в дальнейшем - научить основам программирования.Итак, поехали.
STM32F0DISCOVERY

Данная плата была выпущена в феврале 2012 года с целью привлечения разработчиков, ранее использовавшие 8-ми битные микроконтроллеры, тем самым заняв эту нишу. Ничего плохого, как и хорошего, о ней сказать не могу. Обычная плата, недорогая, для начала подходит отлично. Имеет следующие характеристики:- микроконтроллер: STM32F051R8T6 (Cortex M0, 48МГц, flash 64Кб, RAM 8Кб);- встроенный ST-link/V2, который можно использовать отдельно от платы;- питание от USB или от внешнего источника 3/5В;- 4 светодиода и 2 кнопки;- интерфейсы: USART, SPI, I2C, HDMI;- таймеры 16 и 32 бит;- все выводы выведены на две однорядных гребенки.Собственно такая плата уже достаточно устарела, и брать ее рекомендуется только для самого начала обучения.
STM32VLDISCOVERY

Отличается от предыдущей платы только процессором STM32F100RBT6B (Cortex M3, 24МГц, flash 128Кб, RAM 8Кб) и разводкой гребенок периферии. Так же как и выше рассмотренная подходит для начинающих разработчиков. Больше о ней сказать и нечего.
STM32LDISCOVERY

STM32LDISCOVERY достойный результат эволюции предыдущей платы. Вот что в ней есть интересного:- микроконтроллер STM32L152RBT6 (Cortex M3, 32МГц, flash 128Кб, RAM 8Кб, EEPROM 4Кб)- интерфейсы: USB, USART, SPI, I2C;- 8 таймеров;- 24-канальный 12-битный АЦП;-12-битный ЦАП;- часы реального времени;- контроллер LCD 8х40- встроенный ST-link/V2.На плате установлены:- LCD дисплей 24х8;- 4 светодиода;- 2 кнопки;- сенсорная клавиатура;- 2 однорядные гребенки со свободными выводами.О USB хочется сказать отдельно: контроллер поддерживает USB 2.0 full speed, режимы host и device, что нечасто встретишь у МК такого класса.По сути плата оптимальный вариант для работы с ядром Cortex-M3, так что можно смело брать, благо цена невысокая.
STM32F3DISCOVERY

STM32F3DISCOVERY относится к следующему поколению отладочных плат от STM и обладает следующими характеристиками:- микроконтроллер STM32F303VCT6 (Cortex M4, 72МГц, flash 256Кб, RAM 48Кб)- часы реального времени;- встроенный ST-link/V2;- 13 таймеров;- 12 канальный DMA контроллер;- 4 АЦП;- 4 операционных усилителя;- интерфейсы: CAN, USB 2.0, USART/UART, SPI, I2C;- 87 линий GPIO.На плате размещена следующая периферия:- пользовательский USB-порт;- 3-х осевой акселерометр и 3-х осевой геомагнитный сенсор в одном корпусе;- 3-х осевой гироскоп;- 10 светодиодов;- 2 кнопки;- 2 двухрядные гребенки.Очень интересная плата, много возможностей для опытов. В целом мнение о ней осталось хорошее, но ее заточенность под отслеживание физического состояния и положения сильно сокращают простор для экспериментов, хотя легко можно самому сделать плату расширения.
STM32F4DISCOVERY

С этой платой мне довелось работать больше всего, да и понравилась она мне больше остальных – сказалась ее всесторонняя направленность.Вот что она из себя представляет:- микроконтроллер STM32F407VGT6 (Cortex M4, 168МГц, flash 1Мб, RAM 192Кб)- встроенный ST-link/V2;- таймеры;- DMA контроллер;- АЦП/ЦАП;- интерфейсы: CAN, USB 2.0, USART/UART, SPI, I2C, GPIO;На плате размещена следующая периферия:- пользовательский USB-порт;- 3-х осевой акселерометр;- 8 светодиодов;- 2 кнопки;- 2 двухрядные гребенки;- аудио ЦАП с усилителем класса D;- всенаправленный цифровой микрофон.
Как я писал выше, эта плата стала для меня основной, порадовала способность работы со звуком, акселерометр.
Дальнейшие уроки будут основываться именно на этой плате.
Резюме.Если вы решили начать работать с отладочными платами STM, то советую брать именно STM32F4DISCOVERY, на мой взгляд, у нее наибольший функционал. Дальнейшие статьи будут основываться именно на работе с ней. В ближайшее время будут написаны статьи на следующие темы:- работа с GPIO, таймерами, прерываниями и т.п.;- работа с интерфейсами UART, SPI, I2C и 1-wire на примерах реальных устройств, таких как дисплеи, GPS и GSM модули, датчики температуры, связь с компьютером по RS-232 и многое другое.Конечная цель этого цикла – создание платы расширения для STM32F4. Все прошивки и схемы будут общедоступны. »3.4 Состав МК STM32L152RBT6.
Для практической работы с микроконтроллерами мы, в отличие от автора предыдущей статьи, выбрали микроконтроллер STM32L152RBT6 (на платформе STM32L-DISCOVERY). Платформа STM32L-DISCOVERY представляет собой 64-контактную плату с набором устройств, на которой могут располагаться различные микросхемы микроконтроллеров.
Выбор МК STM32L152RBT6 для учебных целей дисциплины обусловлен богатым набором внешних устройств, которые входят в состав этого МК.Перечислим эти устройства:
- пять портов ввода-вывода (ПВВ) общего назначения,
- контроллер внешних прерываний,
- встроенный программатор Флэш-памяти,
- аналогово-цифровые преобразователи,
- цифро-аналоговые преобразователи,
- датчик температуры,
- LCD дисплей,
- таймеры общего назначения,
- расширенный таймер и часов реального времени с энергонезависимыми (за счет резервирования питания) регистрами и входом обнаружения вмешательства.
Обобщенная архитектура микроконтроллеров на платформе STM32L-DISCOVERY представлена на рисунке 3.6.

Рисунок 3.6 – Архитектура микроконтроллеров на платформе STM32L-DISCOVERY

Расположение элементов на плате платформы STM32L-DISCOVERY микроконтроллера изображено на рисунках 3.7 и 3.8.

Рисунок 3.7 – Верхнее расположение элементов
С микроконтроллером STM32L152RBT6 прилагается демонстрационное программное обеспечение, подробную дополнительную информацию о котором можно получить на www.st.com/stm32l-discovery.
Перед запуском (подачей питания с помощью USB порта) рекомендуется проверить перемычки JP1 и CN3 – они должны быть установлены (on – на JP1 и две установленные перемычки на CN3).
После подачи питания запускается демонстрационное программное обеспечение. Работа программы сопровождается «миганием» светодиодов LED3 и LED4, которые «показывают» выполняемую программой функцию. После «приветствие», производится измерение напряжения на МК (LED3 и LED4 мигают поочередно). Значение измеренного напряжения отображается на экране LCD и составляет 2.97V-2.98V.
После нажатия кнопки USER программа переходит к демонстрации работы сенсорной клавиатуры (на экране LCD высвечивается символ % и светится только LED3 – зеленый). Необходимо поочередно прикасаться пальцем к сенсорным кнопкам «по возрастанию» - и наблюдать изменения в показаниях экрана LCD. Наигравшись, нажмите опять кнопку USER и переходите к следующей функции работы с сенсорной клавиатурой (светиться только LED4 – синий). Нажатие сенсорных кнопок сопровождается изменениями на экране LCD.
Последовательно нажимая кнопку USER (при выключенных LED3 и LED4) можно просмотреть на экране LCD потребляемый ток в различных режимах работы МК STM32L152RBT6. После очередного нажатия кнопки USER демонстрационная программа запускается повторно и т.д.
Рисунок 3.8 – Нижнее расположение элементов
Расшифровка обозначений МК STM32L152RBT6 приведена на рисунке 3.9.

Рисунок 3.9 – Расшифровка обозначений микроконтроллера.
Тема 1 Цифровое чтение – запись двоичной информации
Лекция 4. Программирование портов ввода-вывода МК STM32
4.1 Элементы управления портами ввода-вывода
Связь микроконтроллера STM32L152RB с компьютером осуществляется через разъем USB, а с внешними устройствами – через линии ввода – вывода, которые обозначаются PA0 - PA15, PB0 –PB15 и PC0 – PC15, при этом возможна связь и с USB портом компьютера как некоторым внешним устройством.
Каждая линия ввода – вывода может выполнять функцию линии ввода или вывода общего назначения или альтернативную функцию. Это стало возможно из-за особенности схемной реализации этих линий, которая приведена на рисунке 4.1.


Рисунок 4.1 - Схема линии ввода-вывода
Таким образом, каждая линия ввода – вывода может быть использована для цифрового чтения или записи двоичной информации или для ввода или вывода «альтернативной функции», например, для аналогового ввода или вывода. Настройка линии на конкретное использование осуществляется с помощью специальных регистров.
С целью повышения помехоустойчивости все входные буферы содержат в своём составе триггеры Шмидта. Часть выводов STM32, снабженных защитными диодами, соединёнными с общей шиной и шиной питания, помечены в datasheet как FT (5V tolerant) - совместимые с напряжением 5 вольт.
МК STM32L152RB имеет пять 16-ти разрядных регистров ввода-вывода (GPIO – general-purpose inputs/outputs), которые чаще называют портами ввода-вывода. Для связи с внешними устройствами первые три регистра МК (GPIOA, GPIOB и GPIOC) могут соединяться (как на ввод, так и на вывод) с тремя группами линий ввода-вывода по 16 двоичных разрядов, которые обозначаются РА0-РА15, РВ0-РВ15 и РС0-РС15 (см. рисунок платы МК).
Каждая линия ввода – вывода может выполнять функцию линии ввода или вывода общего назначения или альтернативную функцию и перед началом работы МК некоторые линии ввода-вывода (участвующие в работе МК) необходимо настроить, т.е. определить их поведение при работе МК.
В общем, управление портами МК STM32 осуществляется при помощи наборов из десяти 32-разрядных регистров: 
     четыре 32-разрядных управляющих (конфигурационных) регистра (GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR и GPIOx_PUPDR) (здесь и далее x-имя порта);
32-разрядный регистр GPIOx_IDR входной регистр данных для чтения физического состояния линий ввода-вывода порта;
32-разрядный регистр GPIOx_ODR выходной регистр данных для записи данных непосредственно в порт;
32-разрядный регистр GPIOx_BSRR регистр набора/сброса битов порта;
32-разрядный регистр GPIOx_LCKR регистр блокировки конфигурации выводов
два 32-разрядных альтернативных функциональных регистра отбора (GPIOx_AFRH и GPIOx_AFRL).
Как мы уже отмечали каждая линия ввода – вывода может выполнять функцию линии ввода или вывода общего назначения – т.е. передавать или принимать бит данных. Изучение процесса программирования портов ввода-вывода МК STM32L152RB начнем с программирования портов на прием и передачу данных, представленных комбинацией 0 и 1 – цифровое чтение – запись двоичной информации. Программирование портов означает запись комбинаций 0 и 1 в некоторые разряды некоторых регистров портов. При этом многие управляющие регистры не участвуют в процессе программирования и остаются в исходном состоянии (по умолчанию) после подачи питания на микроконтроллер.
Рассмотрим процесс программирования портов B и C на примере чисто учебной задачи. Предположим, что необходимо смоделировать упрощенный процесс передачи данных через порты микроконтроллера. «Отправитель» выдает массив из 10 наборов 0 и 1 на выводы PB4-PB7 (полубайты некоторых данных, например, некоторых команд). «Получатель» принимает наборы данных на выводах PC0-PC3 и записывает их в свой массив, обрабатывает полученную информацию и отправляет ее на выводы PB4-PB7 для визуального подтверждения приема данных. Выводы PB6 и PB7 подключены к светодиодам. Это позволяет визуально контролировать изменения значений на этих линиях микроконтроллера. Связь между выводами PB4-PB7 и PC0-PC3 необходимо организовать с помощью соединительных шнуров.

4.2 Управляющие регистры порта ввода-вывода
Изучение процесса программирования портов ввода-вывода начнем с изучения управляющих регистров порта.
Каждый порт GPIOx имеет четыре 32-разрядных управляющих (конфигурационных) регистра (GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR и GPIOx_PUPDR), которые конфигурируют сигналы на 16 линиях (пинах) ввода-вывода.
4.2.1 Регистр GPIOx_MODER (GPIO port mode register)
Для указания режимов работы порта используется регистр GPIOx_MODER (GPIO port mode register) смотрите рисунок 4.2. Все разряды регистра сгруппированы попарно в группы MODERy[1:0], где y номер разряда (пина) соответствующего порта.

Рисунок 4.2 – Регистр GPIOx_MODER
Биты 2y:2y+1 MODERy [1:0] изменяемые биты (y = 0.. 15). Эти биты пишутся программно, чтобы установить (сконфигурировать) режим направления ввода-вывода.
00: Ввод (исходное состояние).
01: Универсальный режим вывода.
10: Альтернативный функциональный режим.
11: Аналоговый режим.
Например, если необходимо настроить в порту B пины 4 – 7 на вывод, а в порту C пины 0 – 3 на ввод, то в регистр GPIOB->MODER необходимо записать 01 в группы с 4 по 7, а в регистр GPIOB->MODER необходимо записать 00 в группы с 0 по 3:
GPIOB-> MODER |= (GPIO_MODER_MODER4_1 | GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1 );
или
GPIOB->MODER = 0x5500;
/* 0x5500=0101 0101 0000 0000 */
В операции присваивания в регистр MODER для порта GPIOB будет записан результат поразрядного логического сложения содержимого регистра GPIOB->MODER с единицами в разрядах 5 – 8. Использование поразрядного логического сложения позволяет записать единицы в разряды 5 – 8 регистра GPIOB->MODER независимо от того, что там находилось.
В регистр MODER для порта GPIOC в разряды 0-3 необходимо записать 0, например:
GPIOC->MODER &= ~ (GPIO_MODER_MODER0 | GPIO_MODER_MODER1 | GPIO_MODER_MODER2 | GPIO_MODER_MODER3);
В операции присваивания запись ~ означает, что используется инвертированное содержимое перечисленных разрядов – т.е. 1 заменяются 0, а 0 – 1.
Логическое поразрядное умножение инвертированных перечисленных разрядов с текущим значение регистра GPIOC-> MODER позволяет «обнулить» нужные разряды регистра GPIOA-> MODER независимо от того, что там находилось.
«Обнулить» содержимое регистра можно записью в регистр всех нулей, например:
GPIOC->MODER = 0x0;
4.2.2 Регистр GPIOx_OTYPER (GPIO port output type register)
Регистр GPIOx_OTYPER определяет тип выходного сигнала на линиях ввода-вывода. Он изображен на рисунке 4.3.
Порты микроконтроллера необходимо перевести в режим push-pull (двухтактный). Для этого необходимо в регистре GPIOx_OTYPER (GPIO port output type register) установить 1 в разряды, отвечающие за пины. По смыслу необходимо настраивать только линии порта, работающего на выход, но во многих публикациях приводятся примеры настроек с помощью регистра GPIOx_OTYPER и для портов, работающих на ввод. Проверим, повлияет эта настройка для нашего порта C, разряды которого должны работать на ввод.

Рисунок 4.3 – Регистр GPIOx_OTYPER
Биты 31:16 зарезервированы и должны находиться в сброшенном состоянии.
Биты 15:0 OTy [1:0] изменяемые (конфигурационные) биты (y = 0.. 15). Эти биты пишутся программно, чтобы сконфигурировать выходной тип порта ввода-вывода.
0: Вывод двухтактный (исходное состояние).
1: Вывод с открытым стоком.
Для нашего примера необходимо установить пины PB4 – PB7 и PC0 – PC3 в исходное (нулевое) состояние. Например, для регистра GPIOB_OTYPER необходимо записать следующее выражение:
GPIOB->OTYPER &=~ (GPIO_OTYPER_ODR_4 |
GPIO_OTYPER_ODR_5 | GPIO_OTYPER_ODR_6 |
GPIO_OTYPER_ODR_7);
или
GPIOA->OTYPER &= ~0x00F0;
/* 0x00F0=0000 0000 1111 0000 */
Для регистра GPIOC можно оставить исходное состояние или подтвердить его аналогичными действия, например:
GPIOC->OTYPER &=~ (GPIO_OTYPER_ODR_0 |
GPIO_OTYPER_ODR_1 | GPIO_OTYPER_ODR_2 |
GPIO_OTYPER_ODR_3);
или
GPIOC->OTYPER &= ~0x000F;
/* 0x000F=0000 0000 0000 1111 */
4.2.3 Регистр GPIOx_OSPEEDR (GPIO port output speed register)
Для указания частоты вывода информации в порт используется регистр GPIOx_OSPEEDR (GPIO port output speed register смотрите рисунок 4.4). В регистрах GPIOx_OSPEEDR все разряды сгруппированы попарно в группы OSPEEDRy[1:0], где y номер пина соответствующего порта.

Рисунок 4.4 – Регистр GPIOx_OSPEEDRБиты 2y:2y+1 OSPEEDRy [1:0] изменяемые (конфигурационные) биты (y = 0.. 15). Эти биты пишутся программно, чтобы сконфигурировать выходную скорость ввода-вывода.
00: Очень низкая скорость на 400 кГц.
01: Низкая скорость на 2 МГц.
10: Средняя скорость на 10 МГц.
11: Высокая скорость на 40 МГц на 50 pF (50 МГц выводят максимальную скорость на 30 pF).
Для настройки соответствующего пина порта, например, на частоту 400 кГц, в соответствующую группу GPIOx_OSPEEDR необходимо установить значение 00 (значение по умолчанию).
Для нашего примера оставляем значения регистров GPIOx_OSPEEDR по умолчанию.

4.2.4 Регистр GPIOx_PUPDR (GPIO port pullup/ pull-down register)
На выходе линий ввода-вывода (пинах) микроконтроллера установлены два управляемых регистр резистора Pull-up и Pull-down с возможностью подключения (подтягивания) к шине питания или нулевой шине – смотрите рисунок 4.5.

Рисунок 4.5 – «Подтягивающие» резисторы пиновТеперь разберемся чем отличается push-pull от открытого коллектора. В режиме push-pull нога всегда находится в одном из двух состояний: На ней всегда либо земля, либо полное напряжение питания. В режиме открытого коллектора: Земля или ничего, нога просто как-бы зависает в воздухе ни к чему не подключенная внутри контроллера.
Для подключения «подтягивающих» резисторов Pull-up, Pull-down к выбранным линиям ввода-вывода, используется регистр GPIOx_PUPDR (GPIO port pullup/ pull-down register – смотрите рисунок 4.6).
Работа с ЖК индикатором на отладочной плате STM32L-Discovery
Рисунок 4.6 – Регистр GPIOx_PUPDR
Биты 2y:2y+1 PUPDRy [1:0] изменяемые (конфигурационные) биты (y = 0.. 15) регистра. Эти биты пишутся программно, чтобы скорректировать напряжение ввода-вывода или отключить влияние резисторов.
00: Отключено.
01: Напряжение повышено.
10: Напряжение понижено.
11: Зарезервированный вариант.
В нашем примере необходимо отключить влияние «подтягивающих» резисторов на пины портов B и C поэтому оставляем значение регистров GPIOx_PUPDR по умолчанию.
4.3 Регистры ввода, вывода данных порта
Для чтения физического состояния линий ввода-вывода порта используется 32-разрядный регистр GPIOx_IDR (GPIOx_IDR - input data register) – смотрите рисунок 4.7.

Рисунок 4.7 - Регистр GPIOx_IDR(GPIOx_IDR - input data register)
Биты 31:16 Зарезервированы и должны находиться в сброшенном состоянии.
Биты 15:0 IDRy [15:0]: входные данные порта (y = 0.. 15). Эти биты порта доступны только для чтения. Они содержат значение соответствующего порта ввода-вывода.
В нашем задании необходимо «читать» содержимое регистра GPIOC->IDR и записывать его в массив, например:
masic[i] = GPIOC->IDR;
Для записи некоторого значения в порт используется 32-разрядный регистр GPIOx_ODR (GPIOx_ODR - output data register) – смотрите рисунок 4.8.

Рисунок 4.8 - Регистр GPIOx_ODR (GPIOx_ODR - output data register)
Биты 31:16 Зарезервированы и должны находиться в сброшенном состоянии.
Биты 15:0 ODRy [15:0]: выходные данные порта (y = 0.. 15). Эти биты могут быть считаны и записаны программно. Запись информации осуществляется с помощью операции присваивания.
В нашем задании необходимо «записывать» передавать содержимое массива в регистр GPIOB->ODR и соответственно на линии ввода-вывода:
GPIOB->ODR = masib[i];
4.4 Остальные регистры управления портами МК
Остальные регистры управления портами микроконтроллера STM32L152RBT6 не используются в программе решения нашей задачи и будут рассмотрены подробно в других темах. В этой лекции будет кратко рассмотрено их назначение.

4.4.1 Регистр GPIOx_BSRR (GPIO port bit set/reset register)
Регистр GPIOx_BSRR регистр набора/сброса битов порта, изображенный на рисунке 4.9, предназначен для изменения значений отдельных битов регистра GPIOx_ODR соответствующего порта.

Рисунок 4.9 - Регистр GPIOx_ BSRR (GPIO port bit set/reset register)
Каждому биту GPIOx_ODR соответствуют два бита 32-разрядного регистр GPIOx_ BSRR – BRy и BSy, где y – номер соответствующего разряда регистра GPIOx_ODR. Для записи единицы в регистр GPIOx_ODR применяют константы GPIO_BSRR_BSy, а для записи нуля – GPIO_BSRR_BRy. Например, запись:
GPIOC-> BSRR = GPIO_BSRR_BS3 | GPIO_BSRR_BR1;
позволяет установить единицу в 3 разряд и ноль в 1 разряд регистра GPIOC_ODR.
Значение 0 в регистре GPIOx_ BSRR никак не влияет на значение регистра GPIOx_ODR. Поэтому запись и сброс единиц разрядов регистра GPIOx_ODR осуществляется отдельными битами регистра GPIOx_ BSRR.
В литературе встречается термин «атомарного» доступа к битам GPIOC_ODR с возможностью их модификации.
Применение регистров GPIOx_ BSRR позволяет обходить «блокировки» конфигурации выводов, которые определяются регистром
GPIOx_LCKR.
4.4.2 Регистр GPIOx_LCKR (GPIO port configuration lock register)
32-разрядный регистр GPIOx_LCKR регистр защиты (блокировки) конфигурации выводов от изменений настроек изображен на рисунке 4.10

Рисунок 4.10 - Регистр GPIOx_ LCKR (GPIO port configuration lock register)
Регистры GPIOx_ LCKR используются для защиты от изменений настроек портов в микроконтроллерах отладочной плате STM32L-Discovery.
Биты 31:17 Зарезервированные и должны быть в нулевом состоянии.
Бит 16 LCKK [16] – ключ Lock. Если в бит LCKK записан ноль, то разрешены изменения в конфигурации регистров порта. Если в бит LCKK записана единица, то изменения в конфигурации регистров порта запрещены. Если применена защита конфигурации порта, то следующее изменение конфигурации возможно только после сброса микроконтроллера.
Биты 15:0 используются, чтобы заблокировать конфигурацию GPIO в соответствующих битах.
Остальные два 32-разрядных альтернативных функциональных регистра отбора (GPIOx_AFRH и GPIOx_AFRL) будут рассмотрены в теме LCD.
4.5 Программная реализация учебного задания
Исходный код программы управления микроконтроллером имеет следующий вид:
#include "stm32l1xx.h"
int T=100000;
int i=0;
uint16_t masib[10] = {0x40,0xb0,0x70,0xa0,0x60,0x80,0x50,0xa0,0x40,0x90};
uint16_t masic[10];
void delay(int time);
int main()
{
RCC->AHBENR |= (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN);
// GPIOC->MODER |= (GPIO_MODER_MODER0_1 | GPIO_MODER_MODER1_1 | GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1);
// GPIOB->MODER |= (GPIO_MODER_MODER4_0 | GPIO_MODER_MODER5_0 | GPIO_MODER_MODER6_0 | GPIO_MODER_MODER7_0);
GPIOC->MODER = 0x0;
GPIOB->MODER = 0x5500;
GPIOC->OTYPER &=~(GPIO_OTYPER_ODR_0 | GPIO_OTYPER_ODR_1 | GPIO_OTYPER_ODR_2 | GPIO_OTYPER_ODR_3);
GPIOB->OTYPER &=~(GPIO_OTYPER_ODR_4 | GPIO_OTYPER_ODR_5 | GPIO_OTYPER_ODR_6 | GPIO_OTYPER_ODR_7);

while(1)
{
for(i=0;i<10;i++)
{
delay(T);
GPIOB->ODR = masib[i];
masic[i] = GPIOC->IDR;
delay(T);
masic[i] = masic[i]<<4;
delay(T);
}
for(i=0;i<20;i++) delay(T);
for(i=0;i<10;i++)
{
delay(T);
delay(T);
GPIOB->ODR = masic[i];
delay(T);
delay(T);
}
for(i=0;i<20;i++)delay(T);
}
}
void delay(int time)
{
for( ; time != 0; time--)
__NOP();
return;
}
Комментарии к программе:
в первом цикле for(i=0;i<10;i++) содержимое исходного массива целых чисел masib[i] передается из МК на линии порта B – GPIOB->ODR = masib[i]; Передачу данных на линии порта B контролируем визуально по поочередному миганию светодиодов LED3 и LED4, подключенных к выводам PB6 и PB7;
одновременно это содержимое по соединительным шнурам с линий PB4 – PB7 порта B поступает на входы PC0 - PC3 порта C, «читается» регистром GPIOC->IDR и переписывается в массив целых чисел masic[i] – masic[i] = GPIOC->IDR;
содержимое массива masic[i] «обрабатывается». В качестве обработки чисел массива выполняется сдвиг содержимого каждой переменной массива на 4 разряда влево – мы перемещаем числа под разряды 4 – 7 массива;
выполняем длительную задержку программы for(i=0;i<20;i++) delay(T); Эта пауза нам нужна для фиксации окончания процесса передачи и приема данных через линии МК; во втором цикле for(i=0;i<10;i++) (цикл визуального контроля полученных данных) выполняем вывод массива целых чисел masic[i] на линии порта B – GPIOB->ODR = masic[i]; Количество задержек во втором цикле 4 – на одну больше, чем в первом цикле for. Это позволяет визуально контролировать поочередное мигание светодиодов на линиях PB6 и PB7, но с меньшей частотой мигания.
Для лучшего восприятия в программе многократно используются задержки с помощью функции delay(T);.
Запись
RCC->AHBENR |= (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN);
в начале программы позволяет «подключить» к шине AHB регистры GPIOA, GPIOB и GPIOC. Без этого «подключения» регистры работать не будут.
Этапы разработки программы в интегрированной среде разработки (IDE) Keil 4 и прошивка программы в память МК подробно рассмотрены в методических указаниях этой темы.
Тема 1 Цифровое чтение – запись двоичной информации
Лекция_5 «Прерывание»
5.1 Введение
Работа с внешними устройствами МК предполагает некоторый обмен данными, который может быть организован несколькими методами, например, бесконтрольная передача данных от МК к внешнему устройству (вывод информации на ЖКИ). Иногда обмен данными предполагает всевозможные проверки внешних устройств на «готовность» выполнить прием или передачу данных. Чаще обмен данными основан на прерываниях – временной приостановки выполнения основной программы для выполнения работ по обслуживанию внешних устройств. Обычно внешние устройства могут работать в фоновом режиме, например, ЦАП могут выполнять преобразования (цифрового значения в аналоговое значение) автономно, но результаты преобразований необходимо передавать основной программе. Для этого внешние устройства должны «уметь» информировать МК (основную программу), что внешнее устройство нуждается в обслуживании. Информирование основной программы о готовности к обмену внешнего устройства реализуется с помощью механизма прерывания.
Кто программировал в системе Windows. тот знает, что любое действие в окне программы – нажатие кнопки, перемещения курсора мышки и т.д. можно «связать» с обработчиком прерывания и написать программный код как реакцию программы на соответствующее действие.
Аналогично для МК необходимо «связать» требование внешнего устройства на обслуживание с некоторым «обработчиком» и написать программный код этого обработчика.
В качестве «источников» прерывания часто используются таймеры МК – переполнение таймера есть требование (прерывание) на обслуживание. Для организации прерывания необходимо «настроить» соответствующий таймер (задать, как часто он будет «переполнятся») и написать код обработчика прерывания.
Прерывание может быть организовано с помощью специальной функции типа таймер и некоторой глобальной переменной, которая постоянно опрашивается в бесконечном цикле работы МК. Как только наша функция «установит» глобальную переменную в нужное значение, то происходит срабатывание обработчика прерывания в бесконечном цикле работы МК и «сброс» глобальной переменной. Так как работа нашей функции обычно настроена на фиксированную частоту, то легко задать нужную частоту работы обработчика прерывания. Обычно подобные системы прерывания используются для задания частоты преобразования АЦП или ЦАП очень медленных процессов.
5.2 Использование таймера для организации прерывания
В качестве примера использования в программе прерывания от таймера рассмотрим статью автор ChipSpace из интернета, в которой в свободной и доступной форме рассматривает использование прерывание от таймера (ссылаясь на другую статью) для организации мигания светодиодами. Приводим полный текст этой статьи:
Окт222012
 
Решил я подержать за вымя набирающие популярность процессоры на ядре ARM, а именно STM32L152RB, который стоит на отладочной плате STM32L Discovery.Скажу сразу – это сложнее чем возиться с Arduino, но в целом не так сложно как я думал. К моему удивлению, перемешенному с разочарованием документации для начинающих по этой плате очень мало. Все статьи посвящены более старой STM32VL Discovery, построенной на STM32F10x, которая отличается от моей STM32L1xx. Итак, для начала почитал замечательный мануал от камрада Di Halt, пишет он конечно хорошо и доходчиво, но проблема в том, что как он сам признается – про ARM он пишет не для новичков, а для товарищей уже вкуривших AVR или PIC. Arduino хоть и построен на AVR, но все же штука сильно упрощенная и я себя к “вкурившим AVR” причислить не могу. Тем ни менее, установить по выше указанной статье Keil 4.6 и даже прошить через него мою STM32L-DISCOVERY мне удалось. Для этого я загрузил в Keil проект Demo (находится в папке Keil\ARM\Boards\ST\STM32L-Discovery) и залил его в плату. То что все прошло успешно можно увидеть по версии тестовой прошивки (показывается если нажать и удержать на несколько секунд синюю кнопку), была V1.1.1, стала V2.0.0.А вот дальше начались затыки. Код мигания светодиодом описанный в той статье у меня не скомпилировался, пошла ругня на имена регистров. Ну что же, придется разбираться подумал я и полез качать главную доку по моему камню, а именно Reference manual для серий STM32L151xx, STM32L152xx и STM32L162xx. Увидев, что документик всего на 822 страницы я понял, что тут за 5 минут не разберешься, но мы не привыкли отступать! Итак, компилятор ругался в первую очередь наRCC->APB2ENR |= RCC_APB2ENR_IOPAEN;В комментарии сказано, что этой командой мы подаем такты на порт, без нее с портом ничего сделать не получится, т.к. все что можно из периферии по умолчанию выключено – энергетический кризис, чо. Покурив мануал, заменил данную строчку наRCC->AHBENR |= RCC_AHBENR_GPIOBEN;Что это значит – как я понимаю на STM32F10x порты висят на шине APB2 (Advanced Peripheral Bus №2) и соответственно там их и включают, а вот в моём STM32L1xx они висят на шине AHB (Advanced High-performance Bus) и включать их мне надо именно на ней. А вот как включать это дело вкуса, можно в мануале посмотреть биты, в которые надо записывать нужные значения и записать их непосредственно, а можно открыть stm32l1xx.h (который я скопировал в свой проект из проекта Demo) и посмотреть там имя нужного дефайна. Собственно, по имени дефайна и по комментариям его и выбираешь, а потом, если очень хочется, можно перевести его в двоичное значение и сравнить с мануалом. Конечно, с непривычки это все выглядит мрачно и непонятно, но когда вкуриваешься немного глубже начинаешь видеть тут глубоко продуманную логику. Остановлюсь на этом подробней. Для того, чтобы конфигурировать микроконтроллер используются управляющие регистры, записываем в какой-то бит такого регистра нужное значение и готово. Как тумблер включили. Проблема только, что тумблеров этих как в кабине Боинга. Но у нас есть мануал, в котором все эти регистры описаны и указано что куда писать или читать. Из него можно понять, что порты GPIO находятся на шине AHB и соответственно рулятся с помощью управляющих регистров этой же шины, а регистры, кстати, носят весьма логичные названия, например AHBENR – AHB Enabler Register , теперь если залезть в файл stm32l1xx.h, то можно увидеть секцию озаглавленную Bit definition for RCC_AHBENR register, а в ней узреть дефайн RCC_AHBENR_GPIOBEN с коментом «GPIO port B clock enable» хотя и без комментария можно понять, что GPIOBEN обозначает GPIO – речь о протах ввода/вывода, B – порт B, EN – ENable Почему порт B? Потому что светодиоды висят именно на нем, а конкретно на выводах PB6 И PB7 (это написано на плате возле самих светодиодов). Ну и два слова про конструкцию |= на случай, если читатель не знаком с битовой логикой языка С, эти два символа означают, что к тому что справа от них (в нашем случае это RCC_AHBENR_GPIOBEN, который представляет из себя дефайн числа 0×00000002 или в двоичном виде 10) нужно применить операцию логического ИЛИ с тем что слева от них и результат занести опять таки в то, что слева. Что это значит на практике? Что второй бит регистра RCC_AHBENR гарантировано примет значение единицы, при этом значения остальных битов не изменятся. Почему? Потому что логическое сложение ИЛИ чего угодно с единицей даст на выходе единицу, то есть там где были единицы там они и останутся, а где были ноли они же и останутся (кроме, конечно, того бита в который мы подаем единицу). Если последнее предложение не понятно, то лучше сначала прочитать вот это. Хватит о ерунде пойдем дальше. А дальше нужно сконфигурировать порт на выход и не просто выход а, а Push-Pull. Для STM32VL Discovery в статье по которой я учусь (ссылка выше) автор делает это командамиGPIOA->CRL &= ~GPIO_CRL_CNF3; // Обнулили биты CNF3 т.к. после старта они настроены как (01)// режим Open Drain. А нам надо Push-Pull (00)GPIOA->CRL |= GPIO_CRL_MODE3_0; // Настроили порт на выход (для простоты возьмем тот факт,// что GPIOA-CRL-MODE после сброса равны (00). Но в реальной программе// эти моменты надо проверять и делать полную инициализацию порта.// Согласно таблице 18 из RMУ нас это все тоже не работает и даже не компилится. Лезем в мануал и выясняем, что регистры у нас другие. Для того, чтобы сконфигурировать порт на выход, нужно использовать регистр GPIOx_MODER (где x это буква порта, в нашем случае это B). Тут опять таки все логично MODER это регистр режима (MODE — режим, R — регистр). Нам нужно установить выводы 6 и 7 на выход, для этого нужно записать логическую единицу в биты 12 и 14, а в биты 13 и 15 наоборот — ноли. Вы спросите – а почему так странно, биты портов 6 и 7, а биты регистра 12 и 14? Дело в том, что на каждый порт в регистре отводится по два бита, а для того чтобы порт работал как выход нужно записать в эти два бита двоичное значение 01. Это всё можно узнать из мануала на камень, но гораздо проще полезть все в тот же stm32l1xx.h, найти там раздел отмеченный комментарием «Bit definition for GPIO_MODER register» а в нем найти два дефайна GPIO_MODER_MODER6_0 и GPIO_MODER_MODER7_0 которые установят нужные нам биты в правильные значения. Но этим мы только сконфигурируем порт на выход, а еще нужно бы выбрать режим в котором он будет работать, нам нужен push-pull, а не open-drain, но т.к. push-pull это и так значение по умолчанию, то можно не париться, а если хочется попариться, но для гарантии нужно в регистре GPIOB_ OTYPER биты 6 и 7 изменить на 0 (кстати в реальной программе это надо делать обязательно, мало ли что там по дефолту стоит). Кстати, оценили имя регистра? O – Output, TYPE – Тип, R – регистр, то есть регистр определяющий тип выхода порта. На всякий еще упомяну, что есть регистр GPIOx_OSPEEDR, который, как можно понять из названия, определяет выходную скорость порта (от 400кГц до 50МГц), то есть, как я понимаю, максимальную частоту с которой мы этот порт можем писать значения. Ну нам для моргания светодиодом и 400кГц многовато, так что тут ничего менять не будем.С конфигурацией покончили, теперь надо бы записать в порты единицы, чтобы светодиоды зажглись. Делается это через регистры GPIOB_ODR (очевидно Output Data Register, для входа используется IDR – Input Data Register), просто пишем в нужные биты единички. Пошерстив файл stm32l1xx.h это можно сделать гламурно через дефайны GPIO_ODR_ODR_7 и GPIO_ODR_ODR_6 вот что получилось:#include "stm32l1xx.h"void InitAll(void){RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // включили тактирование на портБGPIOB->MODER |=GPIO_MODER_MODER7_0; //порт B вывод 7 сконфигурили как выходGPIOB->MODER |=GPIO_MODER_MODER6_0; //порт B вывод 6 сконфигурили как выходGPIOB->ODR |= GPIO_ODR_ODR_7; //порт B вывод 7 записали 1GPIOB->ODR |= GPIO_ODR_ODR_6; //порт B вывод 6 записали 1return;}int main(void) {InitAll();while(1){}}Если все настроено как в статье товарища Di Halt, то запустится симуляция, но нам интересней реальный свет и поэтому для начала зайдем в свойства проекта (Alt + F7) и в вкладке Debug вместо Use Simulator Выберем Use и в выпадающем окне St-Link Debugger (это дебагер, который встроен в нашу плату). Теперь компилим (F7) и заливаем (Ctrl + F5), откроется окно дебага, там безусловно много всего интересного, но нам для начала надо узнать горят ли наши светики, поэтому жмем сразу Run (F5) и наслаждаемся синим и зеленым светом наших светодиодов. На этом месте можно выпить чая, оправится и закурить. Самое сложное уже позади, дальше будет только легче.А дальше собственно хотелось бы этими самыми светодиодами помигать. Для этого читаем статью про использование базовых таймеров в STM32, но беда в том, что и эта статья для треклятой STM32VL Discovery и для моей новенькой STM32L приведенный код не работает. Ну что ж придется опять лезть в мануал. Автор статьи использует Standard Peripherals Library (про которую можно почитать тут) так что не удивляйтесь по поводу странных функции в той статье, я ее не использую, там в общем и без нее ничего сложного. Для начала, как и в случае с портами ввода/вывода, надо пустить тактирование на таймер (я решил использовать шестой, т.к. из мануала понял, что простые таймеры это 6 и 7, остальные с ненужными мне пока примочками). Несложный поиск по ставшему уже родным мануалу дает нам следующее: таймер 6 расположен на шине APB1 (в STM32VL Discovery кстати на ней же, хоть тут совпадение) и активируется через регистр RCC_APB1ENR в который нужно записать дефайн RCC_APB1ENR_TIM6EN любезно предоставленный все тем же файлом stm32l1xx.hДалее нужно разрешить прерывания от таймера, чтобы когда он переполнится, сработало прерывание и светодиоды сменили свое состояние. Про прерывания можно почитать тут.Делается это через регистр TIM6_DIER который расшифровывается так: TIM6 это собственно таймер6, а DIE это не «УМРИ!» а всего лишь DMA/Interrupt Enable, R как обычно RegisterTIM6->DIER |= TIM_DIER_UIE;UIE означает Update Interrupt EnableТеперь автор статьи советует установить делитель таймера на 24000. Это означает, что в таймер будет попадать только каждый 24000 тик. Выбрано это число так: раз у нас плата работает на частоте 24МГц, то записывая каждый 24000ый такт таймер будет считать одну тысячную секунды, соответственно когда значение таймера дойдет до тысячи пройдет одна секунда!TIM6->PSC = 24000 - 1; // Настраиваем делитель что таймер тикал 1000 раз в секундуTIM6->ARR = 1000 ; // Чтоб прерывание случалось раз в секундуНу и собственно запускаем таймер:TIM6->CR1 |= TIM_CR1_CEN; // Начать отсчёт!Что порадовало — эти операции я целиком скопировал из статьи – регистры называются одинаково для обоих плат. А вот с назначением прерывания есть небольшая разница, у автора для в STM32VL Discovery это:NVIC_EnableIRQ(TIM6_DAC_IRQn);А у нас для в STM32L Discovery:NVIC_EnableIRQ(TIM6_IRQn); //Разрешение TIM6_IRQn прерыванияА вот содержимое самой функции осталось то же, только имя изменим и номера светодиодовvoid TIM6_IRQHandler(void){TIM6->SR &= ~TIM_SR_UIF; //Сбрасываем флаг UIFGPIOB->ODR^=(GPIO_ODR_ODR_6 | GPIO_ODR_ODR_7); //Инвертируем состояние светодиодов}Ну вот и все, итого программа выглядит вот так:#include "stm32l1xx.h"void InitAll(void){RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // включили тактирование на портБRCC->APB1ENR |= RCC_APB1ENR_TIM6EN; //включили таймер6GPIOB->MODER |=GPIO_MODER_MODER7_0; //порт B вывод 7 сконфигурили как выходGPIOB->MODER |=GPIO_MODER_MODER6_0; //порт B вывод 6 сконфигурили как выходGPIOB->ODR |= GPIO_ODR_ODR_7; //порт B вывод 7 записали 1GPIOB->ODR |= GPIO_ODR_ODR_6; //порт B вывод 6 записали 1TIM6->DIER |= TIM_DIER_UIE; //разрешаем прерывание от таймераTIM6->PSC = 24000-1; // Настраиваем делитель что таймер тикал 1000 раз в секундуTIM6->ARR = 1000 ; // Чтоб прерывание случалось раз в секундуTIM6->CR1 |= TIM_CR1_CEN; // Начать отсчёт!NVIC_EnableIRQ(TIM6_IRQn); //Разрешение TIM6_IRQn прерыванияreturn;}
int main(void) {InitAll();while(1){}}void TIM6_IRQHandler(void){TIM6->SR &= ~TIM_SR_UIF; //Сбрасываем флаг UIFGPIOB->ODR^=(GPIO_ODR_ODR_6 | GPIO_ODR_ODR_7); //Инвертируем состояние светодиодов}компилим, заливаем и… что-то не то, вместо мигания раз в секунду светодиоды меняют свое состояние раз примерно в 12 секунд… Явно дело в несовпадении частоты, поэтому для начала надо разобраться как же выставляется частота контроллера, описано это вот в этой статье, но я хоть и прочитал статью внимательно толком все равно не смог выставить частоту в 24МГц, пришлось пойти другим путем – погуглить на какой частоте изначально работает моя плата. Как выяснилось в отличии от STM32VL Discovery, которая действительно работает на 24МГц, моя STM32L Discovery поставляется вообще без внешнего кварца (теперь понятно почему у меня не получилось настроить его на частоту 24МГц) и работает от внутреннего источника MSI, который тикает «всего то» на частоте 2,097МГц. Теперь становится ясно, почему светодиоды мигают не раз в секунду, а раз в 12 секунд. Подправим нашу программу с учетом вновь поступивших инструкций, а именно уменьшим делитель таймера до 2000:TIM6->PSC = 2000-1;Теперь каждый двухтысячный тик будет записываться в таймер, а поскольку в секунду таких тиков приходит 2097000 то при достижении 1000 тиков пройдет примерно секунда. Примерно, потому что делим мы не на 2097, а на 2000 для ровного счета так сказать. Но на глаз эту разницу мы все равно не заметим, а внутренний резонатор не такой точный как внешний, так что все равно мегаточности добиться не получится. Перекомпиливаем, заливаем, проверяем – работает! Ну вот и все, микроконтроллерный Hello, World! Мы написали, теперь можно переходить к более сложным проектам.Ссылки:проект для keil
СВЕДЕНИЯ О ПРЕПОДАВАТЕЛЕ И КОНТАКТНАЯ ИНФОРМАЦИЯ

Приложенные файлы

  • docx 34028918
    Размер файла: 3 MB Загрузок: 0

Добавить комментарий