- •Оглавление
- •Предисловие
- •Почему я написал книгу?
- •Для кого эта книга?
- •Как использовать эту книгу?
- •Как организована книга?
- •Об авторе
- •Ошибки и предложения
- •Поддержка книги
- •Как помочь автору
- •Отказ от авторского права
- •Благодарность за участие
- •Перевод
- •Благодарности
- •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
Универсальные асинхронные последовательные средства связи |
209 |
Все платы Nucleo-64 спроектированы таким образом, что USART2 целевого микроконтроллера связан с интерфейсом ST-LINK3. Когда мы устанавливаем драйверы ST-LINK, также устанавливается дополнительный драйвер для виртуального COM-порта (Virtual COM Port, VCP): он позволяет нам получить доступ к USART2 целевого микроконтроллера через интерфейс USB без использования специального конвертера TTL/USB. Используя программу эмуляции терминала, мы можем обмениваться сообщениями и данными с нашей Nucleo.
CubeHAL разделяет API для управления интерфейсами UART и USART. Все функции и дескрипторы, используемые для обработки USART, начинаются с префикса HAL_USART и содержатся в файлах stm32xxx_hal_usart.{c,h}, а связанные с управлением UART начинаются с префикса HAL_UART и содержатся в файлах stm32xxx_hal_uart.{c,h}. Поскольку оба модуля концептуально идентичны, а UART является наиболее распространенной формой последовательного соединения между различными модулями, в данной книге будут рассмотрены только функции модуля HAL_UART.
8.2. Инициализация UART
Как и все периферийные устройства STM32, даже USART4 отображается в области периферийной памяти, которая начинается с 0x4000 0000. CubeHAL абстрагируется от действующего отображения в памяти каждого USART для какого-либо микроконтроллера STM32 благодаря дескриптору USART_TypeDef5. Например, мы можем просто использовать макрос USART2 для ссылки на второе периферийное устройство USART, предоставляемое всеми микроконтроллерами STM32 с корпусом LQFP64.
Однако все функции HAL, связанные с управлением UART, спроектированы таким образом, чтобы они принимали в качестве первого параметра экземпляр структуры Си UART_HandleTypeDef, которая определена следующим образом:
typedef struct { |
|
USART_TypeDef |
*Instance; |
UART_InitTypeDef |
Init; |
UART_AdvFeatureInitTypeDef |
AdvancedInit; |
uint8_t |
*pTxBuffPtr; |
uint16_t |
TxXferSize; |
uint16_t |
TxXferCount; |
uint8_t |
*pRxBuffPtr; |
uint16_t |
RxXferSize; |
uint16_t |
RxXferCount; |
DMA_HandleTypeDef |
*hdmatx; |
DMA_HandleTypeDef |
*hdmarx; |
HAL_LockTypeDef |
Lock; |
__IO HAL_UART_StateTypeDef |
State; |
__IO HAL_UART_ErrorTypeDef |
ErrorCode; |
} UART_HandleTypeDef; |
|
/* Базовый адрес регистров UART |
*/ |
/* Параметры UART-связи |
*/ |
/* Параметры инициализации |
|
продвинутых функций UART |
*/ |
/* Указатель на буфер Tx-передачи UART |
*/ |
/* Размер Tx-передачи UART |
*/ |
/* Счетчик Tx-передачи UART |
*/ |
/* Указатель на буфер Rx-передачи UART |
*/ |
/* Размер Rx-передачи UART |
*/ |
/* Счетчик Rx-передачи UART |
*/ |
/* Параметры дескриптора DMA для Tx UART */ /* Параметры дескриптора DMA для Rx UART */
/* Блокировка объекта UART |
*/ |
/* Состояние работы UART |
*/ |
/* Код ошибки UART |
*/ |
3Обратите внимание, что это утверждение может быть неверным, если вы используете плату Nucleo-32 или Nucleo-144. Проверьте документацию ST для получения дополнительной информации об этом.
4Начиная с данного параграфа, термины USART и UART используются взаимозаменяемо, если не указано иное.
5Анализ полей этой структуры Си выходит за рамки данной книги.
Универсальные асинхронные последовательные средства связи |
210 |
Давайте более подробно рассмотрим наиболее важные поля данной структуры.
•Instance (экземпляр): является указателем на дескриптор USART, который мы со-
бираемся использовать. Например, USART2 – это дескриптор UART, связанный с интерфейсом ST-LINK каждой платы Nucleo.
•Init: является экземпляром структуры Си UART_InitTypeDef, использующейся для
конфигурации интерфейса UART. Мы рассмотрим ее более подробно в ближайшее время.
•AdvancedInit: это поле используется для конфигурации более сложных функций
UART, таких как автоматическое обнаружение скорости передачи данных (BaudRate) и замена выводов TX/RX. Некоторые HAL не предоставляют это дополнительное поле. Это происходит потому, что интерфейсы USART не одинаковы у всех микроконтроллеров STM32. Это важный аспект, который необходимо учитывать при выборе подходящего микроконтроллера для вашего приложения. Анализ этого поля выходит за рамки данной книги.
•pTxBuffPtr и pRxBuffPtr: эти поля указывают на буферы передачи и приема соот-
ветственно. Они используются в качестве источника для передачи байтов TxXferSizebytes по UART и для получения RxXferSize при сконфигурированном UART в полнодуплексном режиме (Full Duplex Mode). Поля TxXferCount и RxXferCount используются HAL для внутреннего учета количества переданных и полученных байтов.
•Lock: это поле используется внутри HAL для блокировки одновременного доступа к интерфейсам UART.
Как сказано выше, поле Lock используется для управления одновременным доступом почти во всех процедурах HAL. Если вы посмотрите на код HAL, вы увидите несколько вариантов использования макроса __HAL_LOCK(), который расширяется следующим образом:
#define __HAL_LOCK(__HANDLE__) |
\ |
do{ |
\ |
if((__HANDLE__)->Lock == HAL_LOCKED) |
\ |
{ |
\ |
return HAL_BUSY; |
\ |
} |
\ |
else |
\ |
{ |
\ |
(__HANDLE__)->Lock = HAL_LOCKED; |
\ |
} |
\ |
}while (0) |
|
Неясно, почему инженеры ST решили позаботиться об одновременном доступе к процедурам HAL. Вероятно, они решили использовать потокобезопасный (thread safe) подход, освобождая разработчика приложений от ответственности за управление множественным доступом к одному и тому же аппаратному интерфейсу в случае нескольких потоков, работающих в одном приложении.
Однако это вызывает раздражающий побочный эффект для всех пользователей HAL: даже если мое приложение не выполняет одновременный доступ к одному и тому же периферийному устройству, мой код будет плохо оптимизирован из-за множества проверок состояния поля Lock. Более того, такой способ блокировки по своей сути является небезопасным потоком, потому что нет критической секции, используемой для предотвращения условий гонки (race
Универсальные асинхронные последовательные средства связи |
211 |
conditions) в случае, если более привилегированная ISR прерывает исполняемый код. Наконец, если мое приложение использует ОСРВ, гораздо лучше использовать собственные примитивы блокировки используемой ОС (такие как семафоры и мьютексы, которые не только атомарны, но и правильно управляют планированием задач, избегая циклы активного ожидания (busy waiting)) для обработки одновременных обращений, без необходимости проверять наличие специального возвращаемого значения (HAL_BUSY) функций HAL.
Многие разработчики не одобряют данный способ блокировки периферийных устройств с момента первого выпуска HAL. Инженеры ST недавно объявили, что они активно работают над лучшим решением.
Все действия по конфигурации UART выполняются с использованием экземпляра структуры Си UART_InitTypeDef, которая определена следующим образом:
typedef struct { uint32_t BaudRate; uint32_t WordLength; uint32_t StopBits; uint32_t Parity; uint32_t Mode; uint32_t HwFlowCtl; uint32_t OverSampling;
} UART_InitTypeDef;
•BaudRate: этот параметр относится к скорости соединения, выраженной в битах в
секунду. Несмотря на то что параметр может принимать произвольное значение, обычно скорость передачи данных BaudRate выбирается из списка известных и стандартных значений. Это связано с тем, что она является функцией периферийного тактового сигнала, связанного с USART (который ответвляется от основных тактовых сигналов HSI или HSE через сложную цепочку множителей PLL в некоторых микроконтроллерах STM32), и не все BaudRate могут быть легко достигнуты без введения погрешностей скорости передачи выборки битов и, следовательно, ошибок обмена данными. Таблица 2 показывает список общепринятых BaudRate и вычисленные соответствующие погрешности скорости передачи выборки (%Error) для микроконтроллера STM32F030. Всегда обращайтесь к справочному руководству для вашего микроконтроллера, чтобы узнать, какая из периферийных тактовых частот наилучшим образом соответствует необходимой BaudRate на имеющемся микроконтроллере STM32.
•WordLength: она (длина слова) задает количество битов данных, переданных или
полученных в кадре данных. Это поле может принимать значение
UART_WORDLENGTH_8B или UART_WORDLENGTH_9B, что означает, что мы можем переда-
вать данные по UART пакетами, содержащими 8 или 9 битов. Это число не включает передаваемые служебные биты, такие как биты начала и конца передачи пакета.
•StopBits: в этом поле задается количество передаваемых стоп-битов. Оно может
принимать значение UART_STOPBITS_1 или UART_STOPBITS_2, что означает, что мы можем использовать один или два стоп-бита, чтобы сигнализировать об окончании кадра передаваемых данных.
Универсальные асинхронные последовательные средства связи |
212 |
Таблица 2: Расчет погрешности для запрограммированных скоростей передачи данных на 48 МГц
вобоих случаях передискретизации в 16 или в 8 отсчетов на бит данных
•Parity: это поле задает режим проверки четности и может принимать значения
из Таблицы 3. Учтите, что при включенной проверке четности вычисленная четность вставляется в позицию MSB передаваемых данных (9-й бит, когда длина слова установлена в 9 бит данных; 8-й бит, когда длина слова установлена на 8 бит данных). Проверка четности – это очень простая форма проверки ошибок. Она бывает двух видов: на нечетное (odd) или на четное (even) число единиц. Чтобы получить бит четности, все биты данных суммируются, и проверка четности по сумме определяет, установлен этот бит или нет. Например, предполагая, что установлена проверка четности на четное число единиц, то складывая их в байте данных, таком как 0b01011101, имеющем нечетное число 1 (5), бит четности будет установлен в 1. И наоборот, если был установлен режим проверки четности на нечетное число единиц, бит четности будет равен 0. Проверка четности является необязательной и не очень часто применяется. Она может быть полезна для передачи по зашумленным средам, при этом она также немного замедлит передачу данных и требует, чтобы отправитель и получатель реализовали обработку ошибок (обычно полученные данные, которые дают сбой, должны быть повторно отправлены). Когда возникает ошибка при проверке четности (parity error), все микроконтроллеры STM32 генерируют определенное прерывание, как мы увидим далее.
•Mode: это поле определяет, включен ли режим RX или TX или отключен. Оно мо-
жет принимать одно из значений из таблицы 4.
Универсальные асинхронные последовательные средства связи |
213 |
•HwFlowCtl: Это поле определяет, включен ли аппаратный режим управления по-
током RS2326 или отключен. Этот параметр может принимать одно из значений из таблицы 5.
Таблица 3: Доступные режимы проверки четности для UART-соединения
Режим проверки четности |
Описание |
|
UART_PARITY_NONE |
|
Проверка четности отключена |
UART_PARITY_EVEN |
|
Бит четности устанавливается в 1, если число битов, равных 1, |
|
является нечетным |
|
|
|
|
UART_PARITY_ODD |
|
Бит четности устанавливается в 1, если число битов, равных 1, |
|
является четным |
|
|
|
|
|
Таблица 4: Доступные режимы UART |
|
Режим работы UART |
Описание |
|
UART_MODE_RX |
UART сконфигурирован только в режиме приема |
|
UART_MODE_TX |
UART сконфигурирован только в режиме передачи |
|
UART_MODE_TX_RX |
UART сконфигурирован на работу в режиме и приема, и передачи |
|
Таблица 5: Доступные режимы управления потоком для UART-соединения |
||
Режим управления потоком |
Описание |
|
UART_HWCONTROL_NONE |
|
Аппаратное управление потоком отключено |
UART_HWCONTROL_RTS |
|
Линия «Запрос на передачу» (RTS) включена |
UART_HWCONTROL_CTS |
|
Линия «Готовность к передаче» (CTS) включена |
UART_HWCONTROL_RTS_CTS |
|
Обе линии RTS и CTS включены |
•OverSampling: когда UART получает кадр данных от удаленного узла (remote peer),
он отбирает сигналы для вычисления числа 1 и 0, составляющих сообщение.
Передискретизация или избыточная дискретизация (Oversampling) – это метод от-
бора сигнала с частотой дискретизации, значительно превышающей частоту Найквиста. Приемник реализует различные конфигурируемые пользователем методы передискретизации (за исключением синхронного режима) для восстановления данных, различая действительные входящие данные и шум. Это позволяет найти компромисс между максимальной скоростью связи и помехоустойчивостью. Поле OverSampling может принимать значение UART_OVERSAMPLING_16 для выполнения 16 отсчетов на каждый бит кадра данных или UART_OVERSAMPLING_8 для выполнения 8 отсчетов. В таблице 2 показан расчет погрешностей передачи для запрограммированных скоростей передачи данных при 48 МГц в микроконтроллере STM32F030 в случаях передискретизации в 16 или в 8 отсчетов на бит данных.
Теперь самое время начать писать немного кода. Давайте посмотрим, как сконфигурировать USART2 в микроконтроллере, оснащающим нашу Nucleo, для обмена сообщениями через интерфейс ST-LINK.
int main(void) { UART_HandleTypeDef huart2;
6 Это поле используется только для включения управления потоком RS232. Чтобы включить управление потоком RS485, HAL предоставляет специальную функцию HAL_RS485Ex_Init(), определенную в файле
stm32xxxx_hal_uart_ex.c.
Универсальные асинхронные последовательные средства связи |
214 |
/* Инициализация HAL */
HAL_Init();
/* Конфигурирование системного тактового сигнала */
SystemClock_Config();
/* Конфигурирование USART2 */ huart2.Instance = USART2; huart2.Init.BaudRate = 38400; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&huart2);
...
}
Первым шагом является конфигурация периферийного устройства USART2. Здесь мы используем следующую конфигурацию: 38400, N, 1. То есть BaudRate, равную 38400 бит/с, без проверки на четность и только один стоп-бит. Затем мы отключаем все формы аппаратного управления потоком и выбираем самую высокую частоту передискретизации, то есть 16 тактовых тиков на каждый передаваемый бит. Вызов функции HAL_UART_Init() гарантирует, что HAL инициализирует USART2 в соответствии с заданными параметрами.
Однако приведенного выше кода по-прежнему недостаточно для обмена сообщениями через виртуальный COM-порт Nucleo. Не забывайте, что каждое периферийное устройство, предназначенное для обмена данными с внешним миром, должно быть надлежащим образом связано с соответствующими GPIO, то есть мы должны сконфигурировать выводы TX и RX USART2. Рассматривая схемы Nucleo, мы увидим, что выводами TX и RX USART2 являются PA2 и PA3 соответственно. Более того, мы уже видели в Главе 4, что HAL спроектирован таким образом, что функция HAL_UART_Init() автоматически вызывает HAL_UART_MspInit() (см. рисунок 19 в Главе 4) для правильной инициализации вводов/выводов: наша обязанность – написать тело этой функции в нашем коде приложения, которая будет автоматически вызываться HAL.
Обязательно ли определять данную функцию?
Ответ: нет. Это просто практика, применяемая HAL и кодом, автоматически генерируемым CubeMX. HAL_UART_MspInit() и соответствующая функция
HAL_UART_MspDeInit(), которая вызывается функцией HAL_UART_DeInit(), объяв-
ляются внутри HAL следующим образом:
__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart);
Атрибут функции __weak представляет собой способ GCC объявить символы (в данном случае, имя функции) со слабой областью видимости, которую мы будем перезаписывать, если другие символы с таким же именем будет иметь глобальную область видимости (то есть без атрибута __weak), определяясь в
Универсальные асинхронные последовательные средства связи |
215 |
другом месте приложения (то есть в другом перемещаемом файле). Компоновщик автоматически заменит вызов функции HAL_UART_MspInit(), определенной внутри HAL, если мы реализуем его в нашем коде приложения.
Код ниже показывает, как правильно написать код для функции HAL_UART_MspInit().
void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct;
if(huart ->Instance == USART2) {
/* Разрешение тактирования периферии */
__HAL_RCC_USART2_CLK_ENABLE();
/**Конфигурация GPIO USART2
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = USART_TX_Pin|USART_RX_Pin; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_USART2; /* ПРЕДУПРЕЖДЕНИЕ: зависит от конкретного микроконтроллера STM32 */
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
Как видите, функция разработана так, что она является общей для каждого USART, используемого внутри приложения. Оператор if дисциплинирует код инициализации для используемого USART (в нашем случае, USART2). Оставшаяся часть кода конфигу-
рирует выводы PA2 и PA3. Пожалуйста, обратите внимание, что альтернативная функция может отличаться в зависимости от микроконтроллера, оснащающего вашу Nucleo. Обратитесь к примерам книги, чтобы увидеть правильный код инициализации для вашей Nucleo.
После конфигурации интерфейса USART2 мы можем начать обмен сообщениями с нашим ПК.
Обратите внимание, что представленный ранее код не может быть достаточным для правильной инициализации периферийного устройства USART для нескольких микроконтроллеров STM32. Некоторые микроконтроллеры STM32, такие как STM32F334R8, позволяют разработчику выбирать источник тактового сигнала для используемого периферийного устройства (например, USART2 в микроконтроллере STM32F334R8 может опционально тактироваться от SYSCLK, HSI, LSE или PCLK1). Настоятельно рекомендуется использовать CubeMX при первой конфигурации периферийных устройств для вашего микроконтроллера и тщательно проверять сгенерированный код в поисках подобных исключений. В противном случае техническое описание является единственным источником данной информации.