Еще в студенческие годы мне пришлось весьма тесно обобщатся с микроконтроллерами, тогда это были 8-битные 8051 и AVR. Сейчас, захотев вернутся этому занятию, перевел свой взгляд на весьма широкое семейство контроллеров STM32. О них немало написано на просторах Сети, тем не менее я изъявил желание написать небольшой цикл статей о работе с STMками. Знакомство с ними я хотел бы начать, как говорят, с полного 0. Для экспериментов мною была приобретена простая и дешевая (3$) отладочная плата Maple Mini. Используемый в ней контроллер STM32F103CB обладает весьма внушительным букетом возможностей. (особенно в сравнении с решениями в своей ценовой категории). Подробно можно почитать в сети, и конечно же, в даташите.. Родная Ардуиноподобная среда разработки мне сразу не пришлаcь по вкусу (на вкус и цвет, как говорят...). Из всего изобилия разнообразных сред разработки я остановил свой взгляд на mikroC for ARM компании mikroelektronika. Когда-то я сталкивался с их компиляторами(для 8081), мне весьма понравилось. Хоть и не без косяков, но решил попробовать.
Плата имеет такой вид, все очень просто и лаконично:
Принципиальная схема платы тоже проста, но все самое необходимое здесь есть:
Программатором выбран китайский клон ST-LINK2 (3$), тем не менее он отлично работает с mikroС
Ссылка на демо-версию mikroC. Ограничение демо-версии: максимум 4KB бинарного кода. Не много, но для ознакомления вполне достаточно. С установкой приложения проблем возникнуть не должно, единственное нужно драйвера на ST-LINK2 поставить перед запуском инсталлятора mikroC.
После запуска и создания проекта нас приветствует окно приложения:
Первым делом после выбора типа используемого микроконтроллера необходимо настроить свойства нашего проекта. Конфигурация проекта mikroC вызывается сочетанием клавиш Shift-Ctrl-E (Project — Edit Project). Именно в этом окне настраиваются все прелести, связанные с непростой внутренней организацией системы тактирования STM32 микроконтроллеров. Вообще я советую хотя бы вкратце ознакомится с Reference manual на данное семейство микроконтроллеров. К нему мы будем неоднократно возвращаться.
Блок-схема системы тактирования из даташита 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 (HCLK not divided)
- 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)
Теперь можем сохранить настройки для нашего МК. Все готово для написания 1 программы. Как всегда поморгаем светодиодом (подключен к ножке PB1):
Для установки выходов GPIO порта на выход в microC есть функция
GPIO_Digital_Output(&GPIOх_BASE, _GPIO_PINMASK_ALL);// Настройка порта на выход
она включает тактирование блока GPIOх и прописывает значения в конфиграционный регистр. Данные которые записываем в порт заносим в регистр GPIOх_ODR.
GPIOх_ODR = ; // Региcтр записи в порт
Компилятор позволяет получить доступ к конкретному биту регистра или переменной. Для этого номер бита (начиная с 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 имеем такой сигнал:
Если нам необходимо считать состояние порта, то используем регистр 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 часть подошла к концу. Во второй части я постараюсь Вас познакомить с реализацией ШИМ модуляции, работой с таймерами и функцией подавления дребезга контактов на кнопке.
Автор: kselltrum
PLL multiplication factor — коэффициент умножения PLL. На входе у нас частота кварца 8 МГц, при коэффициенте 11 имеем частоту 72 МГц для SYSCLOCK (PLL input clock х 11) ??
Может коэффициент должен быть 9? 9×8=72….
или я ошибаюсь????
Всё правильно, 11 в 8-миричной системе, в 10-тиричной получается 9. :D