- •Оглавление
- •Предисловие
- •Почему я написал книгу?
- •Для кого эта книга?
- •Как использовать эту книгу?
- •Как организована книга?
- •Об авторе
- •Ошибки и предложения
- •Поддержка книги
- •Как помочь автору
- •Отказ от авторского права
- •Благодарность за участие
- •Перевод
- •Благодарности
- •I Введение
- •1. Введение в ассортимент микроконтроллеров STM32
- •1.1. Введение в процессоры на базе ARM
- •1.1.1. Cortex и процессоры на базе Cortex-M
- •1.1.1.10. Внедренные функции Cortex-M в ассортименте STM32
- •1.2. Введение в микроконтроллеры STM32
- •1.2.1. Преимущества ассортимента STM32….
- •1.2.2. ….И его недостатки
- •1.3. Краткий обзор подсемейств STM32
- •1.3.1. Серия F0
- •1.3.2. Серия F1
- •1.3.3. Серия F2
- •1.3.4. Серия F3
- •1.3.5. Серия F4
- •1.3.6. Серия F7
- •1.3.7. Серия H7
- •1.3.8. Серия L0
- •1.3.9. Серия L1
- •1.3.10. Серия L4
- •1.3.11. Серия L4+
- •1.3.12. Серия STM32WB
- •1.3.13. Как правильно выбрать для себя микроконтроллер?
- •1.4. Отладочная плата Nucleo
- •2. Установка инструментария
- •2.1. Почему выбирают Eclipse/GCC в качестве инструментария для STM32
- •2.1.1. Два слова о Eclipse…
- •2.2. Windows – Установка инструментария
- •2.2.1. Windows – Установка Eclipse
- •2.2.2. Windows – Установка плагинов Eclipse
- •2.2.3. Windows – Установка GCC ARM Embedded
- •2.2.4. Windows – Установка инструментов сборки
- •2.2.5. Windows – Установка OpenOCD
- •2.2.6. Windows – Установка инструментов ST и драйверов
- •2.3. Linux – Установка инструментария
- •2.3.2. Linux – Установка Java
- •2.3.3. Linux – Установка Eclipse
- •2.3.4. Linux – Установка плагинов Eclipse
- •2.3.5. Linux – Установка GCC ARM Embedded
- •2.3.6. Linux – Установка драйверов Nucleo
- •2.3.7. Linux – Установка OpenOCD
- •2.3.8. Linux – Установка инструментов ST
- •2.4. Mac – Установка инструментария
- •2.4.1. Mac – Установка Eclipse
- •2.4.2. Mac – Установка плагинов Eclipse
- •2.4.3. Mac – Установка GCC ARM Embedded
- •2.4.4. Mac – Установка драйверов Nucleo
- •2.4.5. Mac – Установка OpenOCD
- •2.4.6. Mac – Установка инструментов ST
- •3. Hello, Nucleo!
- •3.1. Прикоснитесь к Eclipse IDE
- •3.2. Создание проекта
- •3.3. Подключение Nucleo к ПК
- •3.5. Изучение сгенерированного кода
- •4. Инструмент STM32CubeMX
- •4.1. Введение в инструмент CubeMX
- •4.1.1. Представление Pinout
- •4.1.2. Представление Clock Configuration
- •4.1.3. Представление Configuration
- •4.1.4. Представление Power Consumption Calculator
- •4.2. Генерация проекта
- •4.2.1. Генерация проекта Си при помощи CubeMX
- •4.2.2. Создание проекта Eclipse
- •4.2.3. Ручное импортирование сгенерированных файлов в проект Eclipse
- •4.3. Изучение сгенерированного кода приложения
- •4.3.1. Добавим что-нибудь полезное в микропрограмму
- •4.4. Загрузка исходного кода примеров книги
- •5. Введение в отладку
- •5.1. Начало работы с OpenOCD
- •5.1.1. Запуск OpenOCD
- •5.1.2. Подключение к OpenOCD Telnet Console
- •5.1.3. Настройка Eclipse
- •5.1.4. Отладка в Eclipse
- •5.2. Полухостинг ARM
- •5.2.1. Включение полухостинга в новом проекте
- •5.2.2. Включение полуохостинга в существующем проекте
- •5.2.3. Недостатки полухостинга
- •5.2.4. Как работает полухостинг
- •II Погружение в HAL
- •6. Управление GPIO
- •6.2. Конфигурация GPIO
- •6.2.1. Режимы работы GPIO
- •6.2.2. Режим альтернативной функции GPIO
- •6.2.3. Понятие скорости GPIO
- •6.3. Управление GPIO
- •6.4. Деинициализация GPIO
- •7. Обработка прерываний
- •7.1. Контроллер NVIC
- •7.1.1. Таблица векторов в STM32
- •7.2. Разрешение прерываний
- •7.2.1. Линии запроса внешних прерываний и контроллер NVIC
- •7.2.2. Разрешение прерываний в CubeMX
- •7.3. Жизненный цикл прерываний
- •7.4. Уровни приоритета прерываний
- •7.4.1. Cortex-M0/0+
- •7.4.2. Cortex-M3/4/7
- •7.4.3. Установка уровня прерываний в CubeMX
- •7.5. Реентерабельность прерываний
- •8. Универсальные асинхронные последовательные средства связи
- •8.1. Введение в UART и USART
- •8.2. Инициализация UART
- •8.3. UART-связь в режиме опроса
- •8.3.1. Установка консоли последовательного порта в Windows
- •8.3.2. Установка консоли последовательного порта в Linux и MacOS X
- •8.4. UART-связь в режиме прерываний
- •8.5. Обработка ошибок
- •8.6. Перенаправление ввода-вывода
- •9. Управление DMA
- •9.1. Введение в DMA
- •9.1.1. Необходимость DMA и роль внутренних шин
- •9.1.2. Контроллер DMA
- •9.2. Модуль HAL_DMA
- •9.2.1. DMA_HandleTypeDef в HAL для F0/F1/F3/L0/L1/L4
- •9.2.2. DMA_HandleTypeDef в HAL для F2/F4/F7
- •9.2.3. DMA_HandleTypeDef в HAL для L0/L4
- •9.2.4. Как выполнять передачи в режиме опроса
- •9.2.5. Как выполнять передачи в режиме прерываний
- •9.2.8. Разнообразные функции модулей HAL_DMA и HAL_DMA_Ex
- •9.3. Использование CubeMX для конфигурации запросов к DMA
- •10. Схема тактирования
- •10.1. Распределение тактового сигнала
- •10.1.1. Обзор схемы тактирования STM32
- •10.1.1.1. Многочастотный внутренний RC-генератор в семействах STM32L
- •10.1.3.1. Подача тактового сигнала от высокочастотного генератора
- •10.1.3.2. Подача тактового сигнала от 32кГц генератора
- •10.2. Обзор модуля HAL_RCC
- •10.2.1. Вычисление тактовой частоты во время выполнения
- •10.2.2. Разрешение Выхода синхронизации
- •10.2.3. Разрешение Системы защиты тактирования
- •10.3. Калибровка HSI-генератора
- •11. Таймеры
- •11.1. Введение в таймеры
- •11.1.1. Категории таймеров в микроконтроллере STM32
- •11.1.2. Доступность таймеров в ассортименте STM32
- •11.2. Базовые таймеры
- •11.2.1. Использование таймеров в режиме прерываний
- •11.2.2. Использование таймеров в режиме опроса
- •11.2.3. Использование таймеров в режиме DMA
- •11.2.4. Остановка таймера
- •11.3. Таймеры общего назначения
- •11.3.1.1. Режим внешнего тактирования 2
- •11.3.1.2. Режим внешнего тактирования 1
- •11.3.2. Режимы синхронизации ведущего/ведомого таймеров
- •11.3.2.1. Разрешение прерываний, относящихся к триггерной цепи
- •11.3.2.2. Использование CubeMX для конфигурации синхронизации ведущего/ведомого устройств
- •11.3.3. Программная генерация связанных с таймером событий
- •11.3.4. Режимы отсчета
- •11.3.5. Режим захвата входного сигнала
- •11.3.5.1. Использование CubeMX для конфигурации режима захвата входного сигнала
- •11.3.6. Режим сравнения выходного сигнала
- •11.3.6.1. Использование CubeMX для конфигурации режима сравнения выходного сигнала
- •11.3.7. Генерация широтно-импульсного сигнала
- •11.3.7.1. Генерация синусоидального сигнала при помощи ШИМ
- •11.3.7.2. Использование CubeMX для конфигурации режима ШИМ
- •11.3.8. Одноимпульсный режим
- •11.3.8.1. Использование CubeMX для конфигурации одноимпульсного режима
- •11.3.9. Режим энкодера
- •11.3.9.1. Использование CubeMX для конфигурации режима энкодера
- •11.3.10.1. Режим датчика Холла
- •11.3.10.2. Комбинированный режим трехфазной ШИМ и другие функции управления двигателем
- •11.3.10.3. Вход сброса таймера и блокировка регистров таймера
- •11.3.10.4. Предварительная загрузка регистра автоперезагрузки
- •11.3.11. Отладка и таймеры
- •11.4. Системный таймер SysTick
- •12. Аналого-цифровое преобразование
- •12.1. Введение в АЦП последовательного приближения
- •12.2. Модуль HAL_ADC
- •12.2.1. Режимы преобразования
- •12.2.1.1. Режим однократного преобразования одного канала
- •12.2.1.2. Режим сканирования с однократным преобразованием
- •12.2.1.3. Режим непрерывного преобразования одного канала
- •12.2.1.4. Режим сканирования с непрерывным преобразованием
- •12.2.1.5. Режим преобразования инжектированных каналов
- •12.2.1.6. Парный режим
- •12.2.2. Выбор канала
- •12.2.3. Разрядность АЦП и скорость преобразования
- •12.2.4. Аналого-цифровые преобразования в режиме опроса
- •12.2.6. Аналого-цифровые преобразования в режиме DMA
- •12.2.6.1. Многократное преобразование одного канала в режиме DMA
- •12.2.6.3. Непрерывные преобразования в режиме DMA
- •12.2.7. Обработка ошибок
- •12.2.8. Преобразования, управляемые таймером
- •12.2.9. Преобразования, управляемые внешними событиями
- •12.2.10. Калибровка АЦП
- •12.3. Использование CubeMX для конфигурации АЦП
- •13.1. Введение в периферийное устройство ЦАП
- •13.2. Модуль HAL_DAC
- •13.2.1. Управление ЦАП вручную
- •13.2.2. Управление ЦАП в режиме DMA с использованием таймера
- •13.2.3. Генерация треугольного сигнала
- •13.2.4. Генерация шумового сигнала
- •14.1. Введение в спецификацию I²C
- •14.1.1. Протокол I²C
- •14.1.1.1. START- и STOP-условия
- •14.1.1.2. Формат байта
- •14.1.1.3. Кадр адреса
- •14.1.1.4. Биты «Подтверждено» (ACK) и «Не подтверждено» (NACK)
- •14.1.1.5. Кадры данных
- •14.1.1.6. Комбинированные транзакции
- •14.1.1.7. Удержание синхросигнала
- •14.1.2. Наличие периферийных устройств I²C в микроконтроллерах STM32
- •14.2. Модуль HAL_I2C
- •14.2.1.1. Операции I/O MEM
- •14.2.1.2. Комбинированные транзакции
- •14.3. Использование CubeMX для конфигурации периферийного устройства I²C
- •15.1. Введение в спецификацию SPI
- •15.1.1. Полярность и фаза тактового сигнала
- •15.1.2. Управление сигналом Slave Select
- •15.1.3. Режим TI периферийного устройства SPI
- •15.1.4. Наличие периферийных устройств SPI в микроконтроллерах STM32
- •15.2. Модуль HAL_SPI
- •15.2.1. Обмен сообщениями с использованием периферийного устройства SPI
- •15.2.2. Максимальная частота передачи, достижимая при использовании CubeHAL
- •15.3. Использование CubeMX для конфигурации периферийного устройства SPI
- •16. Циклический контроль избыточности
- •16.1. Введение в расчет CRC
- •16.1.1. Расчет CRC в микроконтроллерах STM32F1/F2/F4/L1
- •16.2. Модуль HAL_CRC
- •17. Независимый и оконный сторожевые таймеры
- •17.1. Независимый сторожевой таймер
- •17.1.1. Использование CubeHAL для программирования таймера IWDG
- •17.2. Системный оконный сторожевой таймер
- •17.2.1. Использование CubeHAL для программирования таймера WWDG
- •17.3. Отслеживание системного сброса, вызванного сторожевым таймером
- •17.4. Заморозка сторожевых таймеров во время сеанса отладки
- •17.5. Выбор сторожевого таймера, подходящего для вашего приложения
- •18. Часы реального времени
- •18.1. Введение в периферийное устройство RTC
- •18.2. Модуль HAL_RTC
- •18.2.1. Установка и получение текущей даты/времени
- •18.2.1.1. Правильный способ чтения значений даты/времени
- •18.2.2. Конфигурирование будильников
- •18.2.3. Блок периодического пробуждения
- •18.2.5. Калибровка RTC
- •18.2.5.1. Грубая калибровка RTC
- •18.2.5.2. Тонкая калибровка RTC
- •18.2.5.3. Обнаружение опорного тактового сигнала
- •18.3. Использование резервной SRAM
- •III Дополнительные темы
- •19. Управление питанием
- •19.1. Управление питанием в микроконтроллерах на базе Cortex-M
- •19.2. Как микроконтроллеры Cortex-M управляют рабочим и спящим режимами
- •19.2.1. Переход в/выход из спящих режимов
- •19.2.1.1. «Спящий режим по выходу»
- •19.3. Управление питанием в микроконтроллерах STM32F
- •19.3.1. Источники питания
- •19.3.2. Режимы питания
- •19.3.2.1. Рабочий режим
- •19.3.2.2. Спящий режим
- •19.3.2.3. Режим останова
- •19.3.2.4. Режим ожидания
- •19.3.2.5. Пример работы в режимах пониженного энергопотребления
- •19.4. Управление питанием в микроконтроллерах STM32L
- •19.4.1. Источники питания
- •19.4.2. Режимы питания
- •19.4.2.1. Рабочие режимы
- •19.4.2.2. Спящие режимы
- •19.4.2.2.1. Режим пакетного сбора данных
- •19.4.2.3. Режимы останова
- •19.4.2.4. Режимы ожидания
- •19.4.2.5. Режим выключенного состояния
- •19.4.3. Переходы между режимами питания
- •19.4.4. Периферийные устройства с пониженным энергопотреблением
- •19.4.4.1. LPUART
- •19.4.4.2. LPTIM
- •19.5. Инспекторы источников питания
- •19.6. Отладка в режимах пониженного энергопотребления
- •19.7. Использование калькулятора энергопотребления CubeMX
- •20. Организация памяти
- •20.1. Модель организации памяти в STM32
- •20.1.1. Основы процессов компиляции и компоновки
- •20.2.1. Исследование бинарного ELF-файла
- •20.2.2. Инициализация секций .data и .bss
- •20.2.2.1. Пара слов о секции COMMON
- •20.2.3. Секция .rodata
- •20.2.4. Области Стека и Кучи
- •20.2.5. Проверка размера Кучи и Стека на этапе компиляции
- •20.2.6. Различия с файлами скриптов инструментария
- •20.3. Как использовать CCM-память
- •20.3.1. Перемещение таблицы векторов в CCM-память
- •20.4.1. Программирование MPU с использованием CubeHAL
- •21. Управление Flash-памятью
- •21.1. Введение во Flash-память STM32
- •21.2. Модуль HAL_FLASH
- •21.2.1. Разблокировка Flash-памяти
- •21.2.2. Стирание Flash-памяти
- •21.2.3. Программирование Flash-памяти
- •21.3. Байты конфигурации
- •21.3.1. Защита от чтения Flash-памяти
- •21.4. Дополнительные памяти OTP и EEPROM
- •21.5. Задержка чтения Flash-памяти и ускоритель ART™ Accelerator
- •21.5.1. Роль TCM-памятей в микроконтроллерах STM32F7
- •22. Процесс начальной загрузки
- •22.1.1. Программное физическое перераспределение памяти
- •22.1.2. Перемещение таблицы векторов
- •22.1.3. Запуск микропрограммы из SRAM с помощью инструментария GNU MCU Eclipse
- •22.2. Встроенный загрузчик
- •22.2.1. Запуск загрузчика из встроенного программного обеспечения
- •22.2.2. Последовательность начальной загрузки в инструментарии GNU MCU Eclipse
- •22.3. Разработка пользовательского загрузчика
- •22.3.2. Как использовать инструмент flasher.py
- •23. Запуск FreeRTOS
- •23.1. Введение в концепции, лежащие в основе ОСРВ
- •23.2.1. Структура файлов с исходным кодом FreeRTOS
- •23.2.1.2. Как импортировать FreeRTOS с использованием CubeMX и CubeMXImporter
- •23.3. Управление потоками
- •23.3.1. Состояния потоков
- •23.3.2. Приоритеты потоков и алгоритмы планирования
- •23.3.3. Добровольное освобождение от управления
- •23.3.4. Холостой поток idle
- •23.4. Выделение памяти и управление ею
- •23.4.1. Модель динамического выделения памяти
- •23.4.1.1. heap_1.c
- •23.4.1.2. heap_2.c
- •23.4.1.3. heap_3.c
- •23.4.1.4. heap_4.c
- •23.4.1.5. heap_5.c
- •23.4.2. Модель статического выделения памяти
- •23.4.3. Пулы памяти
- •23.4.4. Обнаружение переполнения стека
- •23.5. Примитивы синхронизации
- •23.5.1. Очереди сообщений
- •23.5.2. Cемафоры
- •23.5.3. Сигналы потоков
- •23.6. Управление ресурсами и взаимное исключение
- •23.6.1. Мьютексы
- •23.6.2. Критические секции
- •23.6.3. Обработка прерываний совместно с ОСРВ
- •23.7. Программные таймеры
- •23.7.1. Как FreeRTOS управляет таймерами
- •23.8. Пример из практики: Управление энергосбережением с ОСРВ
- •23.8.1. Перехват холостого потока idle
- •23.8.2. Бестиковый режим во FreeRTOS
- •23.9. Возможности отладки
- •23.9.1. Макрос configASSERT()
- •23.9.2. Статистика среды выполнения и информация о состоянии потоков
- •23.10. Альтернативы FreeRTOS
- •23.10.1. ChibiOS
- •23.10.2. ОС Contiki
- •23.10.3. OpenRTOS
- •24. Продвинутые методы отладки
- •24.1. Введение в исключения отказов Cortex-M
- •24.1.1.1. Как инструментарий GNU MCU Eclipse обрабатывает исключения отказов
- •24.1.1.2. Как интерпретировать содержимое регистра LR при переходе в исключение
- •24.1.2. Исключения отказов и их анализ
- •24.2.1. Представление Expressions
- •24.2.1.1. Мониторы памяти
- •24.2.2. Точки наблюдения
- •24.2.3. Режим Instruction Stepping Mode
- •24.2.4. Keil Packs и представление Peripheral Registers
- •24.2.5. Представление Core Registers
- •24.3. Средства отладки от CubeHAL
- •24.4. Внешние отладчики
- •24.4.1. Использование SEGGER J-Link для отладчика ST-LINK
- •24.4.2. Использование интерфейса ITM и трассировка SWV
- •24.5. STM Studio
- •24.6. Одновременная отладка двух плат Nucleo
- •25. Файловая система FAT
- •25.1. Введение в библиотеку FatFs
- •25.1.1. Использование CubeMX для включения в ваши проекты библиотеки FatFs
- •25.1.2. Наиболее важные структуры и функции FatFs
- •25.1.2.1. Монтирование файловой системы
- •25.1.2.2. Открытие файлов
- •25.1.2.3. Чтение и запись файла
- •25.1.2.4. Создание и открытие каталога
- •25.1.3. Как сконфигурировать библиотеку FatFs
- •26. Разработка IoT-приложений
- •26.2. Ethernet контроллер W5500
- •26.2.1. Как использовать шилд W5500 и модуль ioLibrary_Driver
- •26.2.1.1. Конфигурирование интерфейса SPI
- •26.2.1.2. Настройка буферов сокетов и сетевого интерфейса
- •26.2.2. API-интерфейсы сокетов
- •26.2.2.1. Управление сокетами в режиме TCP
- •26.2.2.2. Управление сокетами в режиме UDP
- •26.2.3. Перенаправление ввода-вывода на сокет TCP/IP
- •26.2.4. Настройка HTTP-сервера
- •26.2.4.1. Веб-осциллограф
- •27. Начало работы над новым проектом
- •27.1. Проектирование оборудования
- •27.1.1. Послойная разводка печатной платы
- •27.1.2. Корпус микроконтроллера
- •27.1.3. Развязка выводов питания
- •27.1.4. Тактирование
- •27.1.5. Фильтрация вывода сброса RESET
- •27.1.6. Отладочный порт
- •27.1.7. Режим начальной загрузки
- •27.1.8. Обратите внимание на совместимость с выводами…
- •27.1.9. …и на выбор подходящей периферии
- •27.1.10. Роль CubeMX на этапе проектирования платы
- •27.1.11. Стратегии разводки платы
- •27.2. Разработка программного обеспечения
- •27.2.1. Генерация бинарного образа для производства
- •Приложение
- •Принудительный сброс микроконтроллера из микропрограммы
- •B. Руководство по поиску и устранению неисправностей
- •Проблемы с установкой GNU MCU Eclipse
- •Проблемы, связанные с Eclipse
- •Eclipse не может найти компилятор
- •Eclipse постоянно прерывается при выполнении каждой инструкции во время сеанса отладки
- •Пошаговая отладка очень медленная
- •Микропрограмма работает только в режиме отладки
- •Проблемы, связанные с STM32
- •Микроконтроллер не загружается корректно
- •Невозможно загрузить микропрограмму или отладить микроконтроллер
- •C. Схема выводов Nucleo
- •Nucleo-F446RE
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F411RE
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F410RB
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F401RE
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F334R8
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F303RE
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F302R8
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F103RB
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F091RC
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F072RB
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F070RB
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-F030R8
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-L476RG
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-L152RE
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-L073R8
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •Nucleo-L053R8
- •Разъемы, совместимые с Arduino
- •Morpho-разъемы
- •D. Корпусы STM32
- •LFBGA
- •LQFP
- •TFBGA
- •TSSOP
- •UFQFPN
- •UFBGA
- •VFQFP
- •WLCSP
- •E. Изменения книги
- •Выпуск 0.1 – Октябрь 2015
- •Выпуск 0.2 – 28 октября 2015
- •Выпуск 0.2.1 – 31 октября 2015
- •Выпуск 0.2.2 – 1 ноября 2015
- •Выпуск 0.3 – 12 ноября 2015
- •Выпуск 0.4 – 4 декабря 2015
- •Выпуск 0.5 – 19 декабря 2015
- •Выпуск 0.6 – 18 января 2016
- •Выпуск 0.6.1 – 20 января 2016
- •Выпуск 0.6.2 – 30 января 2016
- •Выпуск 0.7 – 8 февраля 2016
- •Выпуск 0.8 – 18 февраля 2016
- •Выпуск 0.8.1 – 23 февраля 2016
- •Выпуск 0.9 – 27 марта 2016
- •Выпуск 0.9.1 – 28 марта 2016
- •Выпуск 0.10 – 26 апреля 2016
- •Выпуск 0.11 – 27 мая 2016
- •Выпуск 0.11.1 – 3 июня 2016
- •Выпуск 0.11.2 – 24 июня 2016
- •Выпуск 0.12 – 4 июля 2016
- •Выпуск 0.13 – 18 июля 2016
- •Выпуск 0.14 – 12 августа 2016
- •Выпуск 0.15 – 13 сентября 2016
- •Выпуск 0.16 – 3 октября 2016
- •Выпуск 0.17 – 24 октября 2016
- •Выпуск 0.18 – 15 ноября 2016
- •Выпуск 0.19 – 29 ноября 2016
- •Выпуск 0.20 – 28 декабря 2016
- •Выпуск 0.21 – 29 января 2017
- •Выпуск 0.22 – 2 мая 2017
- •Выпуск 0.23 – 20 июля 2017
- •Выпуск 0.24 – 11 декабря 2017
- •Выпуск 0.25 – 3 января 2018
- •Выпуск 0.26 – 7 мая 2018
Обработка прерываний |
193 |
длиться как можно меньше, иначе другие базовые ISR могут быть надолго замаскированы, теряя важную информацию, поступающую от других периферийных устройств.
В качестве упражнения попробуйте поиграть с приоритетами прерываний и посмотреть, что произойдет, если оба прерывания имеют одинаковый приоритет.
Вы можете заметить, что прерывание часто срабатывает просто от касания провода, даже если он не подключен к земле. Почему так происходит? Существуют две основные причины, по которым прерывание срабатывает «случайным образом». Прежде всего, современные микроконтроллеры стараются минимизировать утечки энергии, связанные с использованием внутренних подтягивающих к питанию/к земле резисторов. Таким образом, значение этих резисторов выбрано достаточно высоким (около 50 кОм). Если вы поиграете с уравнением делителя напряжения, вы сможете понять, что достаточно легко перетянуть I/O к низкому или высокому уровню напряжения при высоком значении сопротивления подтягивающего резистора. Во-вторых, здесь мы не делаем адекват-
ной борьбы с дребезгом входного вывода. Борьба с дребезгом (debouncing) – это процесс минимизации эффекта дребезга контактов, производимого «нестабильными» источниками (например, механическим переключателем). Обычно борьба с дребезгом выполняется аппаратно13 или программно путем подсчета времени, прошедшего с первого изменения состояния входа: в нашем случае, если вход остается низким в течение более чем определенного периода (обычно достаточно от 100 мс до 200 мс), то мы можем сказать, что вход был действительно подключен к земле). Как мы увидим в Главе 11, можно также использовать один канал таймера, сконфигурированного для работы в режиме захвата входа, чтобы определять, когда GPIO меняет состояние. Это дает нам возможность автоматически считать, сколько времени прошло с первого события. Кроме того, каналы таймера поддерживают встроенные и программируемые аппаратные фильтры, которые позволяют нам уменьшить количество внешних компонентов для борьбы с дребезгом вводов/выводов.
7.4.2. Cortex-M3/4/7
Cortex-M3/4/7 обладают более продвинутым механизмом приоритетов прерываний, чем механизм, доступный в микроконтроллерах на базе Cortex-M0/0+. Разработчикам предоставляется более высокая степень гибкости, часто являющаяся причиной головной боли для новичков. Более того, и в документации ARM, и в документации ST, приоритет прерываний представлен немного нелогично.
В ядрах Cortex-M3/4/7 приоритет каждого прерывания определяется регистром IPR. Это 8-разрядный регистр в архитектуре ядра ARMv7-M, которая допускает до 255 различных уровней приоритета. Однако на практике микроконтроллеры STM32, реализующие эти ядра, используют только четыре старших бита данного регистра, а все остальные биты равны нулю.
13 Обычно параллельного соединения конденсатора и резистора с выводами переключателя в большинстве случаев достаточно. Например, вы можете взглянуть на схемы платы Nucleo, чтобы увидеть, как инженеры ST борются с дребезгом контактов кнопки USER, подключенной к выводу PC13.
Обработка прерываний |
194 |
Рисунок 15. Содержимое регистра IPR в микроконтроллере STM32 на базе ядра Cortex-M3/4/7
На рисунке 15 четко показано, как интерпретируется содержимое регистра IPR. Это означает, что у нас есть только шестнадцать максимальных уровней приоритета: 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0. Чем ниже это число, тем выше приоритет. То есть IRQ, имеющий приоритет, равный 0x10, имеет более высокий приоритет, чем IRQ с уровнем приоритета, равным 0xA0. Если два прерывания срабатывают в одно и то же время, то приоритет будет отдан первому. Если процессор уже обслуживает прерывание и срабатывают прерывания с более высоким приоритетом, то обработка текущего прерывания приостанавливается, и управление переходит к прерыванию с более высоким приоритетом. Когда его обработка завершается, выполнение возвращается к предыдущему прерыванию, если в это время не происходит никаких других прерываний с более высоким приоритетом.
Пока что механизм практически не отличается от Cortex-M0/0+. Сложность возникает из-за того, что регистр IPR может быть логически разделен на две части: последовательность битов, определяющих приоритет вытеснения14, и последовательность битов, определяющих субприоритет. Первый уровень приоритета управляет приоритетами вытеснения между ISR. Если ISR имеет приоритет выше, чем другая, она прервет (вытеснит) выполнение ISR с более низким приоритетом в случае ее запуска. Субприоритет определяет, какая ISR будет выполняться первой в случае множества отложенных ISR, но он не будет влиять на вытеснение ISR.
Рисунок 16: Вытеснение прерываний в случае одновременного выполнения
На рисунке 16 показан пример вытеснения прерывания. A – это IRQ с более низким приоритетом, который срабатывает в момент времени t0. ISR начинает выполнение, но IRQ B, который имеет более высокий приоритет (более низкий уровень приоритета), срабатывает в момент времени t1, и выполнение ISR прерывания A останавливается. Через некоторое время срабатывает IRQ C в момент времени t2, и ISR B останавливается, а ISR C начинает свое выполнение. Когда оно заканчивается, возобновляется выполнение ISR B до ее завершения. Когда это происходит, возобновляется выполнение ISR A. Этот
14 Что усложняет понимание приоритетов прерываний, так это то, что в официальной документации ино-
гда приоритет вытеснения (preemption priority) также называют группой приоритетов (group priority). Это приводит к большой путанице, поскольку новички склонны представлять, что эти биты определяют своего рода привилегии Списка управления доступом (Access Control List, ACL). Здесь, чтобы упростить понимание данного вопроса, мы будем говорить только об уровне приоритета вытеснения.
Обработка прерываний |
195 |
«вложенный» механизм, обусловленный приоритетами прерываний, сводит к назва-
нию контроллера NVIC, который называется Контроллером вложенных векторных преры-
ваний (Nested Vectored Interrupt Controller).
Рисунок 17: Если отложены два прерывания с одинаковым приоритетом, то сначала выполняется то, которое имеет более высокий субприоритет
На рисунке 17 показано, как субприоритет влияет на выполнение нескольких отложенных ISR. Здесь у нас есть три прерывания, все с одинаковым максимальным приоритетом. В момент времени t0 срабатывает IRQ A, и он немедленно обслуживается. В момент времени t1 срабатывает IRQ B, но, поскольку он имеет тот же уровень приоритета, что и другие IRQ, он остается в отложенном состоянии. В момент времени t2 также запускается IRQ C, но по той же причине, что и раньше, процессор переводит его в отложенное состояние. Когда завершается ISR A, IRQ C обслуживается первым, поскольку он имеет более высокий уровень субприоритета, чем B. IRQ B может обслуживаться, только когда заканчивается ISR C.
Способ логического деления битов IPR определяется регистром SCB->AIRCR (подгруппа битов регистра Блока управления системой (System Control Block, SCB)), и с самого начала важно подчеркнуть, что данный способ интерпретировать содержимое регистра IPR является глобальным для всех ISR. После того как мы определили схему деления прио-
ритетов (также называемую сгруппированными приоритетами (priority grouping) в HAL),
она становится общей для всех прерываний, используемых в системе.
Рисунок 18: Деления битов IPR между приоритетом вытеснения и субприоритетом
Обработка прерываний |
196 |
На рисунке 18 показаны все пять возможных делений регистра IPR, в то время как в таблице 2 показано максимальное количество уровней приоритета вытеснения и уровней субприоритета, которые допускает каждая схема деления.
Таблица 2: Количество доступных уровней приоритета вытеснения на основе текущей схемы
сгруппированных приоритетов
Группа приоритетов NVIC Число уровней приоритета вытеснения Число уровней субприоритета
NVIC_PRIORITYGROUP_0 |
0 |
16 |
NVIC_PRIORITYGROUP_1 |
2 |
8 |
NVIC_PRIORITYGROUP_2 |
4 |
4 |
NVIC_PRIORITYGROUP_3 |
8 |
2 |
NVIC_PRIORITYGROUP_4 |
16 |
0 |
CubeHAL предоставляет следующую функцию для назначения приоритета IRQ:
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
Библиотека HAL разработана таким образом, что PreemptPriority и SubPriority могут быть сконфигурированы номером уровня приоритета в диапазоне от 0 до 16. Значение автоматически смещается на старшие значащие биты. Это упрощает перенос кода на другой микроконтроллер с другим числом битов приоритета (по этой причине производители интегральных схем используют только левую часть регистра IPR).
Напротив, чтобы определить сгруппированные приоритеты, то есть то, как поделить ре-
гистр IPR между приоритетом вытеснения и субприоритетом, можно использовать сле-
дующую функцию:
HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
где параметр PriorityGroup является одним из макросов из столбца Группа приорите-
тов NVIC в таблице 2.
В следующем примере15 показано, как работает механизм приоритета прерываний.
Имя файла: src/main-ex3.c
59 uint8_t blink = 0;
60
61int main(void) {
62GPIO_InitTypeDef GPIO_InitStruct;
64 |
HAL_Init(); |
65 |
|
66/* Разрешение тактирования портов GPIO */
67__HAL_RCC_GPIOC_CLK_ENABLE();
68__HAL_RCC_GPIOB_CLK_ENABLE();
69__HAL_RCC_GPIOA_CLK_ENABLE();
70
71/* Конфигурирование вывода GPIO : PC13 */
72GPIO_InitStruct.Pin = GPIO_PIN_13 ;
73GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
15 Пример предназначен для работы с платой Nucleo-F401RE. Пожалуйста, обратитесь к примерам книги, если у вас другая плата Nucleo.
Обработка прерываний |
197 |
74GPIO_InitStruct.Pull = GPIO_PULLDOWN;
75HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
77/* Конфигурирование вывода GPIO : PB2 */
78GPIO_InitStruct.Pin = GPIO_PIN_2 ;
79GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
80GPIO_InitStruct.Pull = GPIO_PULLUP;
81HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
83/* Конфигурирование вывода GPIO : PA5 */
84GPIO_InitStruct.Pin = GPIO_PIN_5;
85GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
86GPIO_InitStruct.Pull = GPIO_NOPULL;
87GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
88HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
90HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0x1, 0);
91 |
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); |
92 |
|
93HAL_NVIC_SetPriority(EXTI2_IRQn, 0x0, 0);
94HAL_NVIC_EnableIRQ(EXTI2_IRQn);
95
96while(1);
97}
98
99void EXTI15_10_IRQHandler(void) {
100HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
101}
102
103void EXTI2_IRQHandler(void) {
104HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
105}
106
107void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
108if(GPIO_Pin == GPIO_PIN_13) {
109blink = 1;
110while(blink) {
111HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
112for(int i = 0; i < 1000000; i++);
113}
114}
115else {
116blink = 0;
117}
118}
Код должен быть достаточно простым для понимания, если предыдущее объяснение для вас понятно. Здесь у нас есть два IRQ, связанных с линиями 2 и 13 контроллера EXTI. Соответствующие им ISR вызывают HAL_GPIO_EXTI_IRQHandler(), который, в свою оче-
Обработка прерываний |
198 |
редь, вызывает обратный вызов HAL_GPIO_EXTI_Callback(), передав ему GPIO, участвующий в прерывании. Когда нажата пользовательская кнопка, подключенная к сигналу PC13, ISR запускает бесконечный цикл, пока глобальная переменная blink не станет >0. Этот цикл заставляет светодиод LD2 быстро мигать. Когда на выводе PB2 установлен низкий уровень (используйте схему выводов для вашей Nucleo из Приложения C, чтобы определить его расположение), срабатывает EXTI2_IRQHandler(), и это приводит к тому, что HAL_GPIO_EXTI_IRQHandler() устанавливает переменную blink равной 0. Обработчик
теперь можно закончить.
Пожалуйста, обратите внимание, что это довольно плохой способ работы с прерываниями. Блокировка микроконтроллера внутри прерывания – плохой стиль программирования, и он является корнем всего зла во встроенном программировании. К сожалению, это единственный пример, который пришел в голову автору, учитывая, что на данный момент книга все еще охватывает только несколько тем. Каждая ISR должна быть спроектирована так, чтобы длиться как можно меньше, иначе другие базовые ISR могут быть надолго замаскированы, теряя важную информацию, поступающую от других периферийных устройств.
В качестве упражнения попробуйте поиграть с приоритетами прерываний и посмотреть, что произойдет, если оба прерывания имеют одинаковый приоритет.
Вы можете заметить, что прерывание часто срабатывает просто от касания провода, даже если он не подключен к земле. Почему так происходит? Существуют две основные причины, по которым прерывание срабатывает «случайным образом». Прежде всего, современные микроконтроллеры стараются минимизировать утечки энергии, связанные с использованием внутренних подтягивающих к питанию/к земле резисторов. Таким образом, значение этих резисторов выбрано достаточно высоким (около 50 кОм). Если вы поиграете с уравнением делителя напряжения, вы сможете понять, что достаточно легко перетянуть I/O к низкому или высокому уровню напряжения при высоком значении сопротивления подтягивающего резистора. Во-вторых, здесь мы не делаем адекват-
ной борьбы с дребезгом входного вывода. Борьба с дребезгом (debouncing) – это процесс минимизации эффекта дребезга контактов, производимого «нестабильными» источниками (например, механическим переключателем). Обычно борьба с дребезгом выполняется аппаратно16 или программно путем подсчета времени, прошедшего с первого изменения состояния входа: в нашем случае, если вход остается низким в течение более чем определенного периода (обычно достаточно от 100 мс до 200 мс), то мы можем сказать, что вход был действительно подключен к земле). Как мы увидим в Главе 11, можно также использовать один канал таймера, сконфигурированного для работы в режиме захвата входа, чтобы определять, когда GPIO меняет состояние. Это дает нам возможность автоматически считать, сколько времени прошло с пер-
16 Обычно параллельного соединения конденсатора и резистора с выводами переключателя в большинстве случаев достаточно. Например, вы можете взглянуть на схемы платы Nucleo, чтобы увидеть, как инженеры ST борются с дребезгом контактов кнопки USER, подключенной к выводу PC13.
Обработка прерываний |
199 |
вого события. Кроме того, каналы таймера поддерживают встроенные и программируемые аппаратные фильтры, которые позволяют нам уменьшить количество внешних компонентов для борьбы с дребезгом вводов/выводов.
Важно отметить некоторые фундаментальные моменты. Прежде всего, в отличие от микроконтроллеров на базе Cortex-M0/0+, ядра Cortex-M3/4/7 позволяют динамически изменять приоритет прерывания, даже если оно уже разрешено. Во-вторых, необходимо соблюдать осторожность при динамическом снижении сгруппированных приоритетов. Давайте рассмотрим следующий пример. Предположим, что у нас есть три ISR с тремя убывающими приоритетами (приоритет указан в скобках): A (0x0), B (0x10), C (0x20). Предположим, что мы определили данные приоритеты, когда сгруппированные приори-
теты были равны NVIC_PRIORITYGROUP_4. Если мы снизим их до уровня NVIC_PRIORI-
TYGROUP_1, текущий уровень вытеснения будет интерпретироваться как субприоритет. Это приведет к тому, что процедуры обслуживания прерываний A, B и C будут иметь одинаковый уровень прерывания (то есть 0x0), и их невозможно будет вытеснить. Например, глядя на рисунок 20, мы можем заметить, что происходит с приоритетом ISR C, когда сгруппированные приоритеты снижаются с 4 до 1. Когда сгруппированные прио-
ритеты установлены в 4, приоритет ISR C составляет всего два уровня под равный 0 максимальный уровень приоритета (следующий наивысший уровень – 0x10, который является приоритетом B). Это означает, что C может быть вытеснен как A, так и B. Однако, если мы снизим сгруппированные приоритеты до 1, тогда приоритет C станет 0x0 (только бит 7 действует как приоритет), а оставшиеся биты интерпретируются контроллером NVIC как субприоритет. Это может привести к следующему сценарию:
1.все прерывания не смогут вытеснять друг друга;
2.если сработало прерывание C, и ЦПУ не обслуживает другое прерывание, начнется немедленное обслуживание прерывания C;
3.если ЦПУ обслуживает ISR C, и затем через короткое время срабатывают A и B, после завершения обслуживания прерывания C ЦПУ будет обслуживать A, а затем B;
4.если ЦПУ обслуживает другую ISR, и если срабатывает C, а затем через короткое время срабатывают A и B, сначала будет обслуживаться A, затем B, и затем C.
Рисунок 20: Что происходит с приоритетом ISR C при снижении сгруппированных приоритетов с
4 до 1
Прежде чем выяснить механизм приоритета прерываний, вам придется провести несколько экспериментов самостоятельно. Итак, попробуйте изменить Пример 3 так, чтобы изменение сгруппированных приоритетов приводило к одинаковому приоритету вытеснения для обоих IRQ.