понеділок, 15 жовтня 2012 р.

Огляд STM32 (ARM Cortex-M від STMicroelectronics)

STM32VLDiscovery
Планував я написати наступний пост про роботу з атмелівським апартним TWI (I2C), але поки доступу до AVR не маю, зате маю STM32VLDiscovery, тому поговоримо поки про ARM-мікроконтролери від STMicroelectronics

ARM -- архітектура популярна (згідно вікіпедії, найбільш поширені 32-бітні процесори). Детально про неї не писатиму, принаймні не зараз, згадаю лише кілька важливих для подальшого розуміння фактів: 

По-перше, фірма-розробник, ARM Holding, процесорів не випускає. Вона розробляє лише архітектуру і систему команд та ліцензує її іншим фірмам. Виробники можуть виготовляти процесори або мікроконтролери (System on Chip), що базуються на ядрі ARM, доповнених різними периферійними компонентами, пам'яттю, сопроцесорами, тощо, або розробляти власні архітектур на базі системи команд та архітектурних рішень ARM. Тому під абревіатурою ARM можуть ховатися зовсім різні процесори! Звичайно, у всіх пристроях сімейства багато спільного, але все рівно завжди варто конкретизувати, про який саме ARM мова.

По-друге, існує багато "версій" ядра ARM, які грубо можна поділити на три групи:
  • ARM Cortex-A -- "звичайні" процесори, їх, наприклад, можна зустріти в ноутбуках і планшетах
  • ARM Cortex-R -- процесори для систем реального часу
  • ARM Cortex-M -- мікроконтролери на базі архітектури ARM
Надалі мова йтиме лише про "мікроконтролерні" варіанти ARM, Cortex-M. Важливо усвідомлювати, що хоча в світі мікроконтролерів вони виділяються великою потужністю, однак все ж це саме мікроконтролери і "звичайним" процесорам ARM серії A вони дуже і дуже поступаються -- це просто пристрої різного класу, як мопед і мікроавтобус. Крім того, так як для мікроконтролерів критичним є розмір програм, "Кортекси" підтримують лише "урізані" системи команд -- thumb (виключно 16-бітні команди) і thumb-2 (як 16-бітні, так і 32-бітні). Досягнуто це урізання та "запаковування" у 16 біт зменшенням кількості дозволених комбінацій операндів, перетворення частини операндів на неявні (з ніжністю згадуємо різноманітні div-и та mul-и x86-х), тощо.


Абревіатури

Перш ніж перейти до основного матеріалу статті, для зовсім початківців, наведу деякі часто вживані абревіатури, які використовуватимуться в подальшому:

Апаратура

Щоб не потонути в "зоології" (багато фірм випускають ARM Cortex-M0/M0+/M1/M3/M4 у різних варіантах, кожен з яких має ряд опціональних "фіч"), розглянемо один конкретний приклад. Фірма STMicroelectronics випускає свої ARM-и під іменем STM32 і для них, за доволі низькими цінами, продає відладочні/тестові плати (evaluation boards). Ось, наприклад, такі, як на фото вище -- STM32VLDISCOVERY.  Вони містять контролер, фірмовий програматор для нього та трішки периферії для проби -- світлодіоди, кнопки, LCD-екрани, тощо. Для роботи з ними доступний як вільний (Open Source) так і просто безкоштовний (з тими чи іншими обмеженнями) софт. В певному сенсі, плати ці схожі на славнозвісні Ардуїно, навіть існує порт Arduini IDE для них -- Leaflabs Maple IDE (призначене для Leaflab-івського клона ардуїно, тільки з STM32 процесором, але чудово працює і з "фірмовими" відладочними платами). Іноді, враховуючи помітно більшу потужність, говорять, що оті "Discovery"-плати є перспективними конкурентами Arduino, але як на мене вони служать не стільки конкурентом, скільки приємним доповненням. Мінусом є доволі строгі ліцензійні вимоги -- їх не можна використовувати у приладах "на продаж" чи для промислового використання.

Надалі в основному розглядатимемо згадану STM32VLDISCOVERY. Вона обладнана мікроконтролером STM32F100RB, архітектури ARM Cortex M3. Для повноти викладу, згадаю, що він належить до так-званого "medium-density value line" сімейства, де medium-density вказує на об'єм Flash-пам'яті (між  64 і 128Кб), а value line на частоту (24МГц). Детальніше див. "STM32F10x Standard Peripherals Library supported devices and toolchains".  Система команд M3 включає всі команди набору thumb, всі thumb-2, апаратне множення (яке виконується за один такт), апаратне ділення, арифметику з насиченням, реалізовано опціональний  bit-banding (адресацію окремих бітів). Детальніше можна почитати тут. Його "досьє":
  • Робоча напруга - 3.3В! (Тобто, логічна одиничка відповідає напрузі +3.3В.) Частина виводів -- 5В tolerant
  • Частота: 24МГц
  • 128кб флеш-пам'яті (пам'яті програм)
  • 8кб оперативної пам'яті
  • Характерна для багатьох мікроконтролерів EEPROM-пам'ять відсутня, замість неї може використовуватися пам'ять програм (деталі див., наприклад, тут: "Flash память STM32")
  • Багато-багато всілякої периферії: DMA, таймери, ADC, DAC, розрахунок CRC, тощо
  • Багато комунікаційних інтерфейсів: I2C, SPI, UART, Consumer electronics control (CEC)
Крім контролера на платі є: 
  • Програматор ST-Link, який дозволяє завантаження та відладку програм по USB (потрібен кабель із mini-B USB конектором) у контролер плати і може використовуватися незалежно програматором -- для програмування інших мікроконтролерів серії STM! (Переключення здійснюється джампером CN3 на платі, доволі приємний подарунок від фірми)
  • Два світлодіоди, зелений та синій, доступні для програмістів (не рахуючи червоних діодів на "програматорській" частині) -- під'єднані до до PC9 і PC8 відповідно (Port C, піни 9 і 8)
  • Кнопка, під'єднана до PA0 та кнопка RESET
  • Роз'єм, JP1, включивши замість якого амперметр, можна заміряти споживання контролера (якщо джампера немає, живлення на контролер не подається)
  • Піни +5В і +3.3В для живлення сторонньої апаратури (але зрозуміло, багато з них брати не можна! -- згорять контролери живлення)
Детальніше про її конструкцію, включаючи схеми, можна почитати тут. Купити можна, наприклад, тут, ціна на момент написання - трішки менше 140 грн. (Відгук про той магазин див. тут, за словом "Космодром". З того часу робив ще одне замовлення, знову жодних нарікань чи вартих уваги зауважень. Так що рекомендую.)

"Додаткова література":

Апаратні засоби та їх програмування

"Біблією" будь-якого апаратного засобу є його Datasheet (специфікації) та, якщо існує, Reference Manual. Для  STM32F100x їх декілька -- по перше, це "мануал" на ядро,  ARMv7-M від ARM Holdings, по-друге довідник від STMicroelectronics, "STM32F100xx advanced ARM-based 32-bit MCUs" і даташіт по особливостях STM32F100. (В мережі можна знайти любительський переклад російською).

Так як читати даташіти справа нудна, ознайомитися із архітектурою і принципами роботи можна за допомогою "Insider's Guide STM32" від Hitex. Існує його переклад російською.

Взагалі, про те, як працювати із STM32 та платами серії DISCOVERY, як і про Ардуїно, не писав хіба лінивий, тому нижче обмежуся лише коротким оглядом із багатьма посиланнями. Для тих, кому стане нудно читати послідовно -- можна почати з розділу "Перша програма", а тоді повернутися сюди.

CMSIS i SPL (Standard Peripherals Library)

Ці дві абревіатури дуже важливі ;-)

CMSIS -- стандартна бібліотека для всіх Cortex-ів, основа її розроблена ARM Holdings, допилюється виробниками мікроконтролерів під їх периферію. Зокрема, у ній визначено всі регістри (тому немає потреби вручну задавати їх адреси в пам'яті -- див. наступний розділ), різноманітні константи, бітмаски і т.д. Популярний огляд CMSIS див. у Ді Халта: "ARM. Учебный Курс. Keil + CMSIS. Создание проекта".

Standard Peropherals Library -- бібліотека від STMicroelectronics для їхніх STM32. Енкапсулює роботу з окремими регістрами, замість безпосереднього звертання до них, спеціальні структури заповнюються потрібними значеннями і передаються функціям, що виконують всю роботу. Звучить мутно, але коли дійде справа до прикладів, стане зрозуміліше :-). Див. опис у вікі, статтю від Ді Халта: "Работа с STM32F10x Standard Peripherals Library", та офіційну документацію. Знайти її можна, наприклад, у архіві з бібліотекою. Там же є багато-багато прикладів використання. Неофіційна онлайн версія тут.

Обидві бібліотеки постачаються у вигляді джерельних кодів (sources), так як багато простіше їх скомпілювати під конкретний контролер, ніж тримати цілий зоопарк можливих скомпільованих варіантів. Взагалі, у світі мікроконтролерів це -- загальна практика. Часто, для зручності, файли бібліотек копіюються в папку проекту. Деякі IDE, зокрема CooCox coIDE, про яку мова нижче, навіть автоматизують цей процес. Скачати офіційну копію бібліотеки для нашої плати можна тут, (посилання з її сторінки), але багато які IDE містять її в комплекті.

Взагалі, якщо десь є приклад коду такого виду:

RCC->APB2ENR |= RCC_APB2Periph_GPIOC;

маємо використання лише CMSIS, а якщо такого:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

то SPL. Ді Халт рекомендує завжди починати працювати, обмежуючись CMSIS, і лише розібравшись, для спрощення життя, починати використовувати SPL. Абсолютно з ним згоден в цьому! Хоча, сам іноді роблю навпаки, лінь -- страшна сила...

Карта пам'яті

Почнемо з карти пам'яті -- якщо без знання системи команд можна все рівно доволі впевнено програмувати на C/C++, розуміти що і як адресується, все ж варто в будь-якому випадку.
(с) Hitex, "Insider's Guide STM32"
Перші 0.5Гб адресного простору виділено для коду. Так як типовий об'єм пам'яті менший за 1Мб, (у нас взагалі 128Кб), то простір для росту є :). Перші 2Кб, за допомогою зовнішніх пінів BOOT0 і BOOT1, можуть відображати власне вміст Flash (BOOT0=0), фабричний bootloader, який вміє отримувати програму по USART1 і "прошивати" її -- записувати у програмну пам'ять (BOOT0=1, BOOT1=0), або (коли BOOT0=1, BOOT1=1) системну оперативну пам'ять (SRAM). Тобто, щоб прошити контролер, слід встановити BOOT0=1, BOOT1=0; перевантажити контролер і передати йому (згідно описаного в даташіті протоколу) прошивку. Якщо відбувається відладка програми, щоб не перепрошивати пам'ять раз за разом, можна завантажувати її в SRAM, і встановивши BOOT0=1, BOOT1=1, запускати її безпосередньо звідти. Див. також "STM32 Bootloader" від MyController.

Далі слідує 0.5Гб для оперативної пам'яті (SRAM). Так як її взагалі лічені кілобайти/десятки кілобайт, місця більш ніж достатньо, тому частину його виділено під bit-banding. Кожне слово із адресою між 0x22000000 і 0x23FFFFFF відповідає одному біту першого мегабайта діапазону від 0x20000000 до 0x20100000. Тобто, встановлюючи слово (нагадуємо, 32-бітне), з області bit-banding, в 1 чи 0, ми встановлюємо чи очищаємо відповідний біт із першого мегабайта. (Знайти адресу відповідного слова просто, хай це буде домашнім завданням, або, якщо зовсім лінь -- див. згадуваний вище "Insider's Guide STM32", в моєму виданні -- сторінка 22). Процесор вміє виконувати код, який знаходиться в оперативній пам'яті (SRAM), але відбуватися це буде повільніше, ніж для коду з Flash.

Наступних 0.5Гб виділено під регістри периферії, вставленої виробником в мікроконтролер. Перший мегабайт так само доступний побітно, (як і SRAM).

Фактично зі всією оперативною пам'яттю і всіма регістрами периферії можна працювати побітно -- вони займають явно менше місця, ніж 1Мб.

Наступні 2Гб виділено, один для зовнішньої пам'яті  та один для зовнішніх пристроїв.

Нарешті, останніх 0.5Гб використовуються периферією ядра Cortex-M3. Адреси всіх регістрів ядра співпадають для всіх процесорів сімейства Cortex.

Переривання

Cortex-и мають дуже розвинуту систему переривань, якою завідує NVIC -- Nested Vector Interrupt Controller. Описувати тут її не буду, хоча це і важливо. За деталями -- див.:
Зацитую хіба текст з останнього посилання:

Что нужно знать о прерываниях:
  1. Они все независимо включаются/выключаются
  2. Имеют приоритет
  3. Могут быть вызваны программно
  4. Если для прерывания нет обработчика, а оно возникло, то будет вызван обработчик по умолчанию

Шини і тактові генератори

Різноманітних шин у контролера багато, а система тактування складна і розгорнута. Зроблено все це для досягнення більшої ефективності, як у швидкодії, так і у енергоспоживанні. Однак, в результаті, колупатися у тому всьому доволі складно.

(с) RM0041 Reference manual: STM32F100xx advanced ARM-based 32-bit MCUs.
Перший же сюрприз, який чекає на початківця -- після ввімкнення контролера більшість периферії відключена від тактового генератора. Хочете хоча б "посмикати" ніжки -- затактуйте спочатку відповідний порт. Хочете скористатися таймером? Аналогічно. Ну, ви зрозуміли :-) Навіть буфер предвиборки Flash доводиться вмикати окремо (це своєрідний "кеш програм", без нього швидкодія помітно паде).

Всі шини об'єднано у так-звану матрицю шин:
  • ICode -- шина, якою контролер завантажує код з Flash
  • DCode -- завантаження даних із Flash та інтерфейс відлагодження
  • System -- з'єднує системну шину ядра з матрицею шин, здійснює арбітраж доступу між ядром і DMA
  • DMA -- з'єднує шину DMA, AHB, з матрицею
У свою чергу, AHB,  через спеціалізований міст, синхронно під'єднана до двох шин, високошвидкісної APB2 і "повільної" APB1, на яких знаходиться вся вбудована периферія. Як видно зі схеми, частина таймерів, USART1, SPI1, ADC1 та порти вводу-виводу сидять на швидкісній APB2; DAC1/2, I2Cx, USART2/3, SPI2, решта таймерів -- на повільній APB1. Важливо, що вмикається тактування та задається частота для цих шин незалежно.

Тактування блоків теж реалізовано "складно і багатогранно":

Тактування блоків контролера, (с) Hitex, "Insider's Guide STM32"
Тактуватися контролер може як від внутрішнього генератора (HSI -- High Speed Internal, працює без зовнішніх схем, але не дуже точний) так і від зовнішнього (HSE -- High Speed External, може використовувати як сигнал від зовнішнього генератора, так і просто зовнішній кварцовий резонатор). Існує розвинута система керування режимами тактування, реакцією на збої зовнішніх джерел тактового сигналу (з можливістю продовження роботи від внутрішнього!), але в деталі тут не вдаватимуся, якщо цікаво чи є така потреба -- див. додаткову літературу нижче. Окремо тактується USB (так як частота завжди повинна бути 48МГц), але STM32F100 USB не підтримує, тому в деталі теж поки не вдаватимуся. Окремі джерела тактового сигналу використовуються RTC - Real Time Clock-Ом, годинником тобто, та IWDG -- Independent (незалежним) Watchdog. RTC може тактуватися як від HSE, з діленням на 128, так і від внутрішнього чи зовнішнього низькочастотного генератора (LSI/LSE -- Low Speed Internal/External); IWDG -- лише від LSI.

Далі частота "потрапляє" на PLL -- Phase Locked Loop, який, фактично, є помножувачем однієї із частот: HSI/2, HSE, HSE/2, на коефіцієнт від 2 до 16. Керування ним - див. додаткову літературу. Лише слід пам'ятати -- змінювати його налаштування можна тільки поки він вимкнений, після ввімкнення потрібен певний час для виходу на робочий режим (вийшовши, виставляє спеціальний біт, вміє генерувати переривання).

На виході PLL маємо системну частоту, SYSCLK, від якої тактуються всі решта елементи контролера. Кожен із них має свій подільник -- див. схему вище, завдяки чому можна гнучко підбирати частоту роботи всіх блоків.
Додаткова література:

Semihosting

ARM-и мають цікаву "фішку", semihosting -- можливість використовувати у коді майже-звичайні функції сімейства printf, вивід яких можна бачити у консолі відладчика. Іноді дуже зручно! Працює воно за допомогою інтерфейсу із звучною назвою Angel.

Кілька слів про деталі можна прочитати тут: "LPCXpresso Урок 6. Semihosting. Использование printf в отладке". Цінна додаткова інформація є в цій статті: "QEMU ARM semihosting". Ну і куди ж без офіційної документації: "ARM Software Development Toolkit User Guide: Angel".


GPIO -- порти вводу-виводу

Менш-більш звичні, але без них нікуди :-). Імена портів -- GPIOx, де x -- A, B, C, D, E, F або G. (STM32F100 на нашій платі має лише A, B, C -- він у корпусі LQFP-64). Кожен порт об'єднує 16 ніжок -- пінів. Мають на диво багато режимів:
  • Вхід Hi-Z, високоомний
  • Вхід з підтяжкою вверх
  • Вхід з підтяжкою вниз
  • Аналоговий вхід (для ADC)
  • Вихід з відкритим колектором (див. опис на вікі та частину статті "STM32: Урок 4 - GPIO" про Open-drain)
  • Вихід push-pull 
  • Альтернативна функція -- push-pull
  • Альтернативна функція -- open-drain
Альтернативні функції -- це використання піна для всіляких там ADC/USART/I2C/тощо. При цьому, контролер керуватиме ніжкою автоматично. (Зверніть увагу -- мало вибрати альтернативну функцію для піна, слід ще правильно задати його режив -- push-pull чи open-drain.)

В загальних рисах, які альтернативні функції де живуть, можна побачити на рисунку нижче, а детально, із відповідністю пінів контролера пінам плати, описано в таблиці "Extension Connections" даташіта STM32VLDISCOVERY.

Виробник розповсюджує також корисну програмку для візуального конфігурування пінів -- MicroXplorer.

Сенсу переписувати написане багато ким про роботу з GPIO немає, тому просто даю посилання. Не забуваємо, що нульовим пунктом у всіх таких списках стоять даташіти.

Таймери

Таймерів у пристрою, який розглядаємо, багато, і вони круті. Їх можна поділити на наступні класи:
  1. SysTick timer -- простий 24-бітний таймер, який є частиною ядра Cortex
  2. Базові таймери (basic timers), TIM6 і TIM7 для STM32F100
  3. Таймери загального використання (General-purpose timers) -- TIM2, TIM3, TIM4, TIM15, TIM16, TIM17, які, в залежності від набору можливостей, діляться на групи -- див таблицю нижче. 
  4. Advanced-control timer лише один, TIM1. Він один вміє все, що вміють таймери загального використання.
Крім того, ще є два Watchdog-а, сторожові таймери: Window watchdog і Independent watchdog, які теж, в принципі, можна використовувати як таймери. Всі перераховані, здається, вміють "заморожуватися" у режимі відлагодження.

Перш ніж перейти до розгляду конкретних таймерів, подивимося на матрицю можливостей TIMx:

(c) "STM32F100x4 STM32F100x6 STM32F100x8 STM32F100xB" Datasheet

SysTick timer 

24-бітний лічильник,який зменшується ("декрементивний") та вміє автоперезавантажуватися (що це значить -- трішки нижче). Може тактуватися або системною тактовою частотою SYSCLK, або 1/8 від неї, SYSCLK/8. Регістри для роботи з цим таймером наступні:
  • SysTick Current Value Register, VAL -- поточне значення лічильника. Коли досягає нуля, генерується переривання. 
  • SysTick Reload Value Register, LOAD -- значення, яке завантажуватиметься в Current Value Register після того, як він занулився (ось і автоперезавантаження).
  • SysTick Control and Status Register, CTRL -- регістр керування та статусу. Використовуються наступні біти: COUNTFLAG (16) -- 1, якщо лічильник досягав нуля з того часу, як його останній раз читали, очищається в нуль, коли його читають або коли очищається лічильник VAL; CLKSOURCE (2) -- зовнішнє (0) або внутрішнє (1) джерело тактового сигналу. TICKINT (1) -- якщо 1, дозволено генерацію переривання, ENABLE (0) -- якщо 1, таймер "дозволено" і він відраховує "тіки".
  • SysTick Calibration Value Register, CALIB -- регістр калібрування, може використовуватися, щоб добитися однакових інтервалів між перериваннями, згенерованими SysTick на різних мікроконтролерах сімейства.
CMSIS має структуру для роботи із ним, SysTick, описану в cmsis.h (лише фрагменти, що стосуються таймера):

typedef struct
{
  __IO uint32_t CTRL;  

  /*!< Offset: 0x00  SysTick Control and Status Register */
  __IO uint32_t LOAD;                         

  /*!< Offset: 0x04  SysTick Reload Value Register       */
  __IO uint32_t VAL;                          

  /*!< Offset: 0x08  SysTick Current Value Register      */
  __I  uint32_t CALIB;                        

  /*!< Offset: 0x0C  SysTick Calibration Register        */
} SysTick_Type;


#define SCS_BASE (0xE000E000)                              
/*!< System Control Space Base Address */
#define SysTick_BASE (CS_BASE +  0x0010)                      
/*!< SysTick Base Address  

#define SysTick ((SysTick_Type *)       SysTick_BASE)     
/*!< SysTick configuration struct      */

Цей таймер -- хороший приклад того, як організована CMSIS. Регістри таймера відображаються один за одним у пам'ять, за базовою адресою, яка розраховується як сума базової адреси системної області (див. вище карту пам'яті) та зміщення у ній області регістрів SysTick.

Крім того, для зручності, CMSIS надає функцію SysTick_Config(uint32_t ticks), яка ініціалізує таймер, задає частоту генерації переривань (знаючи системну частоту, можна її привести, хоча і не дуже точно, до одиниць реального часу) та запускає відлік:

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            

  /* Перевіряємо, чи значення не завелике -- таймер 24-бітний
  * SysTick_LOAD_RELOAD_Msk -- 24 двійкові одинички */

  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      

  /* Ініціалізуємо регістр перезавантаження */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  

  /* Встановлюємо пріоритет переривання, засобами NVIC */
  SysTick->VAL   = 0;                                          

  /* Задаємо початкове значення лічильника */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    

  /* Дозволяємо переривання та запускаємо таймер */
  return (0);                                                  

}

Зрозуміло, що для досягнення бажаної затримки (в секундах), слід встановити:

 ticks = системна частота (в герцах) * час (в секундах)

Тільки не забуваємо, що таймер лише 24-бітний, тому, наприклад, для STM32VLDISCOVERY, з частотою 24МГц, більше 0.7 секунди (8*0.7, якщо тактувати на 1/8 частоти) відрахувати він не зможе. Це обмеження легко обійти програмно -- використати свій лічильник, який змінюватиметься в процедурі обробки переривання.

Головне призначення таймера -- побудова диспетчера операційної системи, але іноді зручно мати аж 24-бітний таймер.

Сподіваюся, мені пробачать такий, непропорційно великий щодо решти компонент мікроконтролера, опис цього таймера, але він -- чи не найпростіший периферійний пристрій, і можна зручно проілюструвати загальні принципи роботи, не тонучи в багатьох бітах десятків регістрів.

Детальніше, традиційно, див. літературу:
  • Опис роботи з SysTimer, містить приклади: "STM32. Системный таймер SysTick."
  • Опис таймера -- розділ "The SYSTICK Timer" глави 8 "The NVIC and Interrupt Control" у "The Defi nitive Guide to the ARM Cortex-M3" від Joseph Yiu. (Можна знайти на просторах Інтернету, або пишіть мені)

Базові таймери (basic timers)

Їх два, TIM6 і TIM7 (в STM32F100), з наступними можливостями:
  • 16-бітний лічильник з автоперезавантаженням,
  • 16-бітний подільник частоти, (на нього ділиться частота, з якою затактовано таймер, див. відповідний розділ вище)
  • 16-бітний регістр періоду, (що задає величину, за якої лічильник перевантажиться),
  • схеми синхронізації DAC,
  • генерація переривань,
  • генерація запитів DMA.
 Знаходяться вони на шині APB1, перед використанням слід ввімкнути їх тактування.

Детальніше див:

"Просунуті" таймери -- загального використання і Advanced-control

Ну, це взагалі не таймери, а монстри. Вони вміють (загального використання -- у різних комбінаціях, див. таблицю вище, advanced-contorol -- все зразу) наступне:
  • Все те, що і базові таймери -- 16-бітні лічильник, 16-бітний подільник частоти, 16-бітний регістр автоперезавантаження, генерація переривань та запитів DMA
  • TIM2/3/4 вміють рахувати вверх та вниз
  • До 4 каналів захоплення/порівняння сигналів, які можуть служити для: захоплення сигналу, (зокрема захоплення PWM), порівняння виводу, генерація PWM.
  • Захоплення сигналу -- із вказаного каналу захоплюються імпульси і поточне значення таймера зберігається в регістрі TIM_CCRx. Можна налаштувати генерацію переривань чи запит DMA на прихід імпульсу, можна ловити фронт, спад сигналу чи і те і те, можна генерувати переривання over-capture, якщо з часу попереднього імпульсу регістр TIM_CCRx не було прочитано, можна налаштувати фільтр, який вказує, після котрої зміни сигналу, (від 0 до 15), слід вважати, що він таки прийшов (зручно для боротьби із дрижанням); можна налаштовувати подільник частоти, ловлячи кожен 2/4/8 сигнал. Захоплення PWM -- хитрий режим захоплення, коли один канал ловить фронти і скидує лічильник, інший -- ловить спади, таким чином фіксуючи, відповідно, період і заповнення сигналу (duty cycle). 
  • Порівняння виводу по досягненню вказаного значення лічильника, переключає пін у 0, 1, або змінює поточне значення на протилежне.
  • Генерація PWM!
  • Виділений режим читання енкодера та роботи із датчиками Холла.
  • Лічильник повторів -- генерація переривання не кожного переповнення, а лише раз на вказану кількість (від 0 до 255).
  • Комплементарні виводи (такі, що мають протилежні логічні рівні), із можливістю програмування dead-time -- затримки між переключенням їх рівнів,
  • Синхронізація -- таймери можна об'єднувати, як для синхронізації, щоб одна і та ж подія синхронно запускала кілька таймерів, так і в ланцюжки, коли переривання по переповненню одного таймера генерує одну "лічильну" подію іншого. 
  • Вхід BRK для зупинки таймера, чи переведення його у заданий стан.
  • Фіксація параметрів таймера, без можливості їх зміни до перевантаження контролера.
Які з перерахованих компонент присутні у яких таймерах -- див. таблицю вище, а деталі роботи з ними можна прочитати тут:

Додатково можна подивитися загальні статті: "AVR. Учебный курс. Использование ШИМ (PWM)" від Ді Халта, на прикладі AVR та "ШИМ - Широтно-Импульсная Модуляция" від Robocraft. 

ADC

Без Аналогово-Цифрового перетворювача, (АЦП, він же ADC) у аналоговому світі складно. Тому і STM32 ним обладнані. Детально я з ними поки не розбирався, тому обмежуся лише посиланнями:

DAC

Зворотній до ADC пристрій. Як ерзац-DAC можна використовувати PWM, особливо із конденсатором великої ємності для згладжування, але виділений пристрій все ж зручніше. Крім того, DAC від STM32 вміє генерувати сигнал трикутної форми та шум.

I2C

Про шину I2C я вже писав, і ще до цієї теми повернуся не раз, тому теж тільки список посилань:

SPI

Serial Peripheral Interface Bus -- SPI,  ще одна популярна послідовна шина. Синхронна, повнодуплексна, шустріша від I2C, зате допускає лише один пристрій на шині (якщо не рахувати chip select  -- можливість ввімкнути один конкретний пристрій із декількох присутніх на шині, вимкнувши всі інші). Крім даташітів (а краще -- перед ними :) можна глянути сюди:

USART

Куди ж без нього, хоча дідусю вже багато-багато рочків:

DMA

Зручна річ: сказав контролеру звідки брати, куди класти, і забув -- периферія із пам'яттю самі розберуться. Думаю, колись розгляну його детально. А поки декілька посилань для затравки:

Список джерел інформації

Для зручності, наведу сайти, на сторінки яких посилаюся вище, та деякі додаткові джерела в одному місці: 

 

Програмне забезпечення

Щоб скористатися контролером, крім власне нього, із необхідним "обвісом", слід мати ще різноманітні засоби розробки. Коротко пройдемося по них, (з надією повернутися до деяких в майбутніх постах).

Компілятори та IDE

Як не дивно, документація на STM32VLDiscovery рекомендує лише три комерційних пакети:
  • Keil MDK-ARM, і ARM Development Studio 5. Враховуючи, що ARM купила Keil, наводжу разом. Keil постачає свою IDE та свій компілятор, існує безкоштовна версія пакету, Lite, із обмеженням на величину коду в 32Кб. ARM DS базується на Eclipse, може працювати як зі своїм компілятором, так і з gcc. Обоє містять відладник, симулятор, і ряд корисних бібліотек та інструментів. Не працював із ними, тому детальніше сказати не можу.
  • Embedded Workbench for ARM (EWARM) від IAR Systems. Власний компілятор, і якщо не плутаю, власне IDE. Існує безкоштовна версія із обмеженням 32Кб.
  • Atollic TrueSTUDIO for ARM. Базується на Eclipse і включає gcc. Існує безкоштовна версія, із тим же обмеженням в 32Кб (8Кб для Cortex-M0 і M1).
Ще декілька варіантів, із коментарем "хіба лінивий не зробив ще IDE для ARM", перераховано тут.

Однак, як зрозуміло із написаного вище, для ARM/ARM Cortex існує підтримка в gcc, який, у свою чергу, дружить із Eclipse, тому можна сподіватися на існування відкритих, Open Source, засобів. Сподівання аж ніяк не марні:
  • "GNU Tools for ARM Embedded Processors" -- компілятор gcc. Зокрема, скомпільовані для Windows версії.
  • Маючи трішки часу, досвіду та бажання попрацювати, гарне середовище можна зібрати самостійно, все із тих же інгредієнтів -- arm-gcc та Eclipse. Як це зробити, описано в статті "STM32: Урок 1 - Настраиваем IDE" від Robocraft.
  • Дуже хороша, особливо для початківців, оболонка CoIDE від CooCox. Недоліком є відсутність нормальної підтримки C++, відсутність версії під Linux та глибоке-глибоке закопування можливостей налаштування (якими славиться Eclipse), що іноді сильно ускладнює життя (наприклад, повернення тієї ж підтримки C++ :). Китайська розробка.
Про CoIDE буде окремий пост, а зараз колекція посилань про роботу із IDE:
Також, хоча воно трішки не в тему, але так як ніяк не можу звикнути, а практично всі згадані IDE -- замасковані Eclipse, хай буде тут: шоткати Eclipse.
Важливе зауваження -- різні оболонки по різному генерують допоміжний код ініціалізації, який виконуватиметься до main. Тому можуть бути сюрпризи -- наприклад, одні оболонки вмикатимуть по замовчуванню якусь периферію, інші -- ні, тоді та ж програма буде успішно працювати в перших і не працюватиме у других. (Наприклад, моя CoIDE створює файл <project_path>/cmsis_boot/startup/startup_stm32f10x_md_vl.c, який, цитуючи офіційний опис:  Set the initial SP, set the vector table entries with the exceptions ISR address, initialize data and bss, setup the microcontroller system, call the application's entry point.  After Reset the Cortex-M3 processor is in Thread mode, priority is Privileged, and the Stack is set to Main. Де під "setup the microcontroller system" мається на увазі виклик функції SystemInit з CMSIS, котра у свою чергу "Initialize the Embedded Flash Interface, the PLL and update the SystemCoreClock variable".)

Також, слід пам'ятати, що типовий у світі мікроконтролерів спосіб підключити бібліотеки -- скопіювати їх у папку проекту, і компілювати разом із власним кодом. Це простіше, ніж тягнути із середовищем безліч комбінацій скомпільованих бібліотек, а сумарний їх розмір і так невеликий, через обмежену пам'ять контролера. Одні оболонки такий процес автоматизують (як вже згадана CoIDE), інші ні (і тоді в інструкціях пише щось типу "скопіюйте cmsis.c i cmsis.h у папку вашого проекту").

Допоміжні засоби

Крім компілятора, оболонки та засобів для прошивки і відлагодження (вони явно не згадувалися, але малися на увазі в попередньому розділі), існує ряд корисних програм, які сильно полегшують життя розробнику. Назву декілька із них:
  • STM Studio run-time variables monitoring and visualization tool, від STMicroelectronics. Інструмент, за допомогою якого можна слідкувати за змінними у мікроконтролері під час його (менш-більш звичайної) роботи. Працює з використанням стандартних засобів відлагоження, типу ST-Link. Безкоштовна. Стаття про роботу із нею: "STM Studio", від EasySTM32.
  • MicroXplorer, MCU graphical configuration tool, від STMicroelectronics. Засіб конфігурування (та підбору оптимальних комбінацій) пінів мікроконтролерів, в залежності від моделі та периферії, із якою планується працювати. Враховуючи громіздкість та "рясність" периферії та велику кількість ніжок контролерів, дуже корисна річ! Дві статті по роботі із ним, хоча програма достатньо інтуїтивна: "Работа с MicroXplorer", від EasySTM32,  "STM32. MicroXplorer", від ChipSpace.
  • CooCox CoSmart -- за ідеологіє (та й за виглядом), сильно нагадує MicroXplorer, однак ніби надає більше можливостей. На жаль, випробувати поки не вдалося -- підтримки STM32 ще немає, обіцяли на серпень, так що чекаємо.
  • CooCox CoAssistant -- інтерактивний довідник по регістрам. Можна користуватися он-лайн!

Операційні системи

Як не дивно, навіть на таких крихітках, як мікроконтролери, є ніша для операційних систем. Детальніше про деякі з них в майбутніх постах, а поки короткий список сумісних із STM32:
  • ChibiOS/RT. Портабельна, компактна, багатозадачна OS реального часу. Підтримує ряд сімейств мікроконтролерів з архітектурами ARM7/ARM Cortex-Mx/STM8/MSP430/AVR та інші (див. "Supported Architectures"). Підтримує багатозадачність із витісненням, засоби роботи з багатопоточністю, статична по природі (що важливо для мікроконтролерів), але може працювати і динамічно виділяючи ресурси, Hardware Abstraction Layer (HAL) для великого набору периферії (див., правда, Features Matrix, але все рівно, для STM32F1xx підтримується найбільший набір периферії). Open Source -- GPL, GPL with Linking Exception. Є можливість покупки комерційної ліцензії. 
  • CooCox CoOS. Крихітна, (мінімальне ядро - 974 байт), багатозадачна OS реального часу для ARM Cortex M. Підтримує багатозадачність із витісненням, засоби роботи з багатопоточністю, вміє розпізнавати переповнення стеку. Open Source -- BSD. Розробляється китайцями :-)
  • Free RTOS. Дуже популярна і відома. Портабельна, підтримує три десятки архітектур, багатозадачна OS реального часу. Підтримує як кооперативну багатозадачність, так і багатозадачність із витісненням, а також гібридні варіанти. Засоби для роботи з багатозадачністю, програмні таймери, розпізнавання переповнення стеку. Ліцензія -- GPL with Linking Exception, але включає "апаратуро-специфічні" файли із своїми ліцензіями.  Існує декілька модифікацій -- OpenRTOS для комерційного використання, та SafeRTOS, підвищеної надійності.
Додатково можна глянути у різноманітні списки подібних операційних систем:

Інше

Їх забагато, щоб тут перераховувати, але на просторах Інтернету можна знайти дуже багато корисних бібліотек (різної якості та з різними ліцензіями, звичайно) для ARM Cortex взагалі, і  STM32 зокрема. Важливо, що, якщо потрібно терміново працювати з файловою системою FAT чи дисплеєм на контролері HD44780, ймовірно, писати їх підтримку самостійно, або, принаймні, писати її з нуля, не доведеться.

Крім того, для гурманів, можна спробувати програмувати контролер за допомогою Lua (проект eLua) або Scheme (Armpit Scheme).

Перша програма

Щоб мінімально проілюструвати описане вище (по великому рахунку -- описане за посиланнями вище), розглянемо "Hello world" світу мікроконтролерів -- мигання світлодіодами. Нагадаю, що STM32VLDiscovery має два доступних програмісту світлодіоди, під'єднані до PС8 і PC9.

Спершу обмежимося засобами CMSIS:
/* Файл із різноманітними оголошеннями -- іменами регістрів,
 * бітмасками, адресами відображення в пам'яті, тощо */
#include <stm32f10x.h>

int main()
{
  /* Вмикаємо тактування порту C
   * в регістрі APB2ENR підсистеми
   * RCC (Reset and Clock Control):
   * встановлюємо біт RCC_APB2ENR_IOPCEN
   *  */
  RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

  /* Піни 0-7 порта керуються регістром CRL,
   * 8-15 -- CRH */
  /* Режим: вивід, Push-Pull, біти CNF8 -- нульові, очищаємо*/
  GPIOC->CRH &= ~GPIO_CRH_CNF8;
  GPIOC->CRH &= ~GPIO_CRH_CNF9;
  /* Частота оновлення -- 2МГц, біти MODE - 10b */
  /* Очищаємо MODE0 для PC8 */
  GPIOC->CRH |= GPIO_CRH_MODE8_0;
  /* Встановлюємо MODE1 для PC8 */
  GPIOC->CRH &= ~GPIO_CRH_MODE8_1;
  /* Очищаємо MODE0 для PC9 */
  GPIOC->CRH |= GPIO_CRH_MODE9_0;
  /* Встановлюємо MODE1 для PC9 */
  GPIOC->CRH &= ~GPIO_CRH_MODE9_1;

  /* Встановлюємо 1 на пінах 8 і 9 -- засвічуємо діоди */
  GPIOC->BSRR = GPIO_BSRR_BS8;
  GPIOC->BSRR = GPIO_BSRR_BS9;

  /* Зависаємо */
  while (1)
  {
      __NOP();
  }
}

А тепер повторимо те ж, користуючись SPL:
/* Файл із різноманітними оголошеннями -- іменами регістрів,
 * бітмасками, адресами відображення в пам'яті, тощо */
#include <stm32f10x.h>
/* Функції керування тактуванням
 * (підсистема RCC -- Reset and Clock Control) 
 * Частина SPL */
#include <stm32f10x_rcc.h>
/* Функції роботи з GPIO, частина SPL */
#include <stm32f10x_gpio.h>

int main()
{
  /* Вмикаємо тактування порту C */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

  /* Готуємо структуру опису порта: */
  GPIO_InitTypeDef LEDS_gpio_conf;
  /* Режим: вивід, Push-Pull */
  LEDS_gpio_conf.GPIO_Mode = GPIO_Mode_Out_PP;
  /* Піни: 8 і 9 (там у нас світлодіоди) */
  LEDS_gpio_conf.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
  /* Частота оновлення: 2МГц (більше і не треба) */
  LEDS_gpio_conf.GPIO_Speed = GPIO_Speed_2MHz;

  /* Ініціалізуємо порт C згідно заданої конфігурації */
  GPIO_Init(GPIOC, &LEDS_gpio_conf);

  /* Встановлюємо 1 на пінах 8 і 9 -- засвічуємо діоди */
  GPIO_SetBits(GPIOC, GPIO_Pin_8 | GPIO_Pin_9);

  /* Зависаємо */
  while (1)
  {
      __NOP();
  }
}

Взагалі, на таких маленьких прикладах важко зрозуміти переваги та недоліки кожного із способів, але запідозрити, в чому відмінність, вже можна.


На цьому -- поки все, дякую за увагу і все таке :=)

2 коментарі:

  1. дякую за хорошу підбірку матеріалів, сам вже перечитав частину вище вказаного)

    ВідповістиВидалити
  2. Гарна річ.

    Зара вивчаю SAMD20.

    ВідповістиВидалити