Перші кроки з STM32 і компілятором mikroC для архітектури ARM — Частина 1

Ще в студентські роки мені довелося досить тісно обобщатся з мікроконтролерами, тоді це були 8-бітні 8051 і AVR. Зараз, захотівши повернуться цього заняття, перевів свій погляд на досить широке сімейство контролерів STM32. Про них написано чимало на просторах Мережі, тим не менше я виявив бажання написати невеликий цикл статей про роботу з ЅТМками. Знайомство з ними я хотів би почати, як кажуть, з повного 0. Для експериментів мною була придбана проста і дешева (3$) налагоджувальна плата Maple Mini. Використовуваний в ній контролер STM32F103CB володіє досить значним букетом можливостей. (особливо в порівнянні з рішеннями у своїй ціновій категорії). Докладно можна почитати в мережі, і звичайно ж, даташите.. Рідна Ардуиноподобная середовище розробки мені відразу не припала до смаку (на смак і колір, як кажуть...). З усього розмаїття різноманітних середовищ розробки я зупинив свій погляд на mikroC for ARM компанії mikroelektronika. Коли я стикався з їх компіляторами(для 8081), мені дуже сподобалося. Хоч і не без косяків, але вирішив спробувати.
Плата має такий вигляд, все дуже просто і лаконічно:
image
Принципова схема плати теж проста, але все найнеобхідніше тут є:
image
Програматором обраний китайський клон ST-LINK2 (3$), тим не менш він відмінно працює з мікгоС
image
Посилання демо-версію mikroC. Обмеження демо-версії: максимум 4KB бінарного коду. Не багато, але для ознайомлення цілком достатньо. З встановленням програми проблем виникнути не повинно, єдине потрібно драйвера на ST-LINK2 поставити перед запуском інсталятора mikroC.
Після запуску і створення проекту нас вітає вікно програми:
image
Першим ділом після вибору типу використаного мікроконтролера необхідно налаштувати властивості нашого проекту. Конфігурація проекту mikroC викликається комбінацією клавіш Shift-Ctrl-E (Project — Edit Project). Саме в цьому вікні налаштовуються всі принади, пов'язані з непростою внутрішньою організацією системи тактирования STM32 мікроконтролерів. Взагалі я раджу хоча б коротко ознайомитися з Reference manual на дане сімейство мікроконтролерів. До нього ми будемо неодноразово повертатися.
image
Блок-схема системи тактирования з даташита STM32F103
В даному вікні задається конфігурація регістрів RCC_CR і RCC_CGGR
  • Internal high-speed clock enable — Включення або відключення вбудованого генератора 8МГц(HSI) (oscillator OFF)
  • External high-speed clock enable — Включення або відключення вбудованого генератора 8МГц(HSE) (oscillator ON)
  • External high-speed clock enable — Можливість підключення до входу OSC генератора тактових імпульсів замість кварцу. Ми налаштовуємо на використання кварцу (HSE oscillator not bypassed)
  • Clock seсurity system enable — Включення з вбудованим в контролер систем захисту від зникнення синхросигналу; поки не використовуємо (Clock detector OFF)
  • PLL Enable — Включення/відключення модуля множення частоти (PLL ON)
  • System clock switch — Вибір тактового сигналу SYSCLOCK: PLL, зовнішній чи внутрішній генератор. Ми використовуємо PLL. Саме на частоті HSE помноженого на коефіцієнт PLL і працює ядро нашого контролера (PLL selected as system clock)
  • Set and cleared by software to control the division factor of the AHB clock — Установка переддільника SYSCLOCK для шини AHB, яка обслуговує периферійні модулі МК; поки відключаємо переддільник *(SYSCLOCK not divided)
  • APB low-speed prescaller APB1 — дільник частоти для низькошвидкісної периферії МК, наприклад шини I2C, максимальна частота роботи:36 МГц (HCLK divided by 2)
  • APB high speed prescaller APB 2 — дільник частоти для високошвидкісної периферії МК, наприклад портів вводу-виводу, таймери і т. д. (HCLK not devided)
  • ADC prescaller — переддільник для модуля АЦП щодо APB 2 (PCLK2 divided by 2)
  • PLL entry clock source — джерело тактового сигналу на вхід PLL, на вибір або 1/2 вбудованого RC генератора або зовнішній генератор, який пройшов через PREDIV1; саме його і використовуємо (Clock from PREDIV1 selected as the PLL input clock)
  • HSE divider for PLL entry — налаштування цього самого PREDIV1; поки не використовуємо (HSE clock not divided)
  • PLL multiplication factor — коефіцієнт множення PLL. На вході у нас частота кварцу 8 МГц, при коефіцієнті 11 маємо частоту 72 МГц для SYSCLOCK (PLL input clock х 11)
  • USB prescaller — частота шини USB. USB специфікації працює на частоті 48 МГц, вибираємо переддільник 1,5 (PLL clock divided by 1.5)
MSU clock frequency вибираємо частоту SYSCLOCK — 72МГц (72.000000)
image
Тепер можемо зберегти налаштування для нашого МК. Все готово для написання 1 програми. Як завжди поморгаем світлодіодом (підключений до ніжці PB1):
Для установки виходів GPIO порту на вихід в microC є функція
GPIO_Digital_Output(&GPIOх_BASE, _GPIO_PINMASK_ALL);// Налаштування порту на вихід

вона включає тактування блоку GPIOх і прописує значення в конфиграционный регістр. Дані які записуємо в порт заносимо в регістр GPIOх_ODR.
GPIOх_ODR = ; // Регістр запису в порт

Компілятор дозволяє отримати доступ до конкретного біта регістра або змінною. Для цього номер біта (починаючи з 0) пишемо після назви регістра через точку
REGx.by; // Доступ до окремого (y) біт регістра (х)

Для формування затримок використовуємо вбудовану функцію Delay_ms() (або Delay_us()) компілятора. Ось наша перша програма:
void main() 
{
GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1); //Робимо PB1 виходом
GPIOb_ODR.b1 = 0; //Записуємо в регістр GPIOb_ODR в 15 біт = 0

while(1) // Нескінченний цикл
{
GPIOb_ODR.b1=~GPIOb_ODR.b1; //Інверсія біта 15 
Delay_ms(500); //Затримка 500 мс
}
}

Щоб одна команда ініціалізації застосовувалося відразу до декількох ніжок порту пишемо _GPIO_PINMASKn через оператор "або", наприклад:
GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1 | _GPIO_PINMASK_7); //PB1 і PB7 налаштовані на вихід
GPIO_Digital_Output(&GPIOa_BASE, _GPIO_PINMASK_ALL ); //Всі ноги PA налаштовані на вихід

Тепер спробуємо вивести меандр на один з виводів МК, перемикаючи стан виводу порту PB15 з інтервалом 5 мс. :
void main() 
{
GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_15);
GPIOb_ODR.b15 = 0;

while(1)
{
GPIOb_ODR.b15=~GPIOb_ODR.b15;
Delay_ms(5); // Затримка 5 мс.(Імпульсу 10 мс, частота 100 Гц)
}
} 

На виведенні PB15 маємо такий сигнал:
image
Якщо нам необхідно вважати стан порту, то використовуємо регістр GPIOх_IDR, попередньо налаштувавши порт на вхід за допомогою функції GPIO_Digital_Input (*port, pin_mask). На нашій платі є кнопка, підключена до висновку порту PB8. Наступна програма запалює миготливий світлодіод на виведенні PB1 при натиснутій кнопці.
void main()
{
GPIO_Digital_Output(&GPIOb_BASE, _GPIO_PINMASK_1);
GPIO_Digital_Input(&GPIOb_BASE, _GPIO_PINMASK_8); // Налаштовуємо висновок PB8 на вхід
GPIOb_ODR.b1 = 0;

while(1)
{
if (GPIOb_IDR.b8) //Якщо кнопка натиснута 8 біт регістра GPIOb_IDR дорівнює 1
{
GPIOb_ODR.b1=~GPIOb_ODR.b1;
Delay_ms(500); //Затримка 500 мс
}
else
{
GPIOb_ODR.b1 = 0; //Якщо кнопку відпустили, погасити світлодіод
}
}
}

На цьому 1 частина підійшла до кінця. У другій частині я спробую познайомити Вас з реалізацією ШІМ модуляції, роботою з таймерами і функцією придушення брязкоту контактів на кнопці.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.