Слайд 2
Предупреждение
Все электрические схемы, представленные здесь и далее, являются
условными!
В них могут отсутствовать важные компоненты!
Слайд 3
Цели на сегодня:
Зажечь светодиод
Погасить светодиод
Помигать светодиодом
Помигать по нажатию
кнопки (bonus level)
Слайд 4
Как зажечь светодиод?
Это зависит от того, как он
подключен.
Слайд 5
Как зажечь светодиод?
Как зажечь светодиод при таком подключении?
Нужно
на вывод МК подать низкий уровень напряжения – светодиод
загорится.
Если подать высокий уровень – светодиод погаснет.
Слайд 6
Как зажечь светодиод?
Как зажечь светодиод при таком подключении?
Очевидно, все наоборот.
Нужно на вывод МК подать высокий уровень
напряжения – светодиод загорится.
Если подать низкий уровень – светодиод погаснет.
Слайд 7
Как узнать, нажата ли кнопка?
Нужно измерить уровень напряжения
на входе.
Если кнопка нажата – то уровень на входе
будет низким.
А если не нажата?
Неопределенность. Это плохо. Что делать?
Слайд 8
Как правильно подключать кнопку
Теперь, когда кнопка не нажата,
на входе будет высокий уровень.
Это называется «подтяжка к питанию»
- Pull Up.
А зачем нужен резистор?
Чтобы не было КЗ, когда кнопка нажата.
Слайд 9
Но можно и наоборот
Это называется «подтяжка к земле»
- Pull Down.
Теперь, когда кнопка нажата, на входе
МК будет высокий уровень,
а когда не нажата - низкий
Подтяжка может быть ВНУТРИ МК!
Слайд 10
Логические уровни
Для stm32f103:
Для других устройств уровни могут быть
другими; кодирование может быть инверсным и т.д.
Слайд 11
Контакты микроконтроллера
(они же «пины», «ноги», «выводы»)
Тип:
цифровой
аналоговый
Направление:
вход
выход
Режим:
Ввод/вывод общего назначения
(GPIO)
Альтернативный
Режим входа:
С подтяжкой (вверх/вниз)
Без подтяжки (floating)
Режим выхода:
Комплементарный (Push pull)
Открытый
сток (Open drain)
Текущее состояние:
входа (только чтение)
выхода (чтение/запись)
Слайд 12
Работа с периферийными устройствами
Специальные команды ассемблера
Ввод/вывод, отображенный на
память (memory mapped IO) – регистры доступны по фиксированным
адресам.
В последнем случае у каждого периферийного устройства есть набор регистров (не путать с регистрами ЦПУ).
Каждый регистр настраивает определенную функциональность.
Каждый бит в регистре что-то означает.
Слайд 13
Что из этого нам сегодня нужно?
Чтобы зажечь светодиод
на плате discovery, нам нужна ножка в режиме комплементарного
выхода (output push-pull).
Чтобы считать состояние кнопки – вход без подтяжки (input floating).
Слайд 14
Как же всем этим управлять?
Нужно как-то выбирать все
эти режимы и состояния для каждого контакта! Как? Как
должен выглядеть API?
С помощью специальных функций, которые кто-то уже написал за нас?
Но что делают эти функции?
Слайд 15
Работа с GPIO
Контакты МК логически объединяются в группы
– «порты».
В stm32f10x в каждом порту 16 контактов.
Порты обозначаются
буквами – PORTA, PORTB, PORTC...
Контакты обозначаются числами от 0 до 15:
PC.12 – 12-й контакт в порту С.
Количество доступных контактов зависит от корпуса МК; некоторые порты могут отсутствовать целиком или частично.
Слайд 16
STM32f103RBT6
На плате discovery не доступны:
PA13, PA14, PA15;
PB3,PB4; PC14, PC15; PD0, PD1
Слайд 17
STM32 VL Discovery
Два светодиода, подключенные к земле и
МК:
PC.8
PC.9
Две кнопки:
Черная – это reset
Синяя – PA.0 – просто
кнопка с внешней подтяжкой к питанию
Слайд 18
Регистры GPIO
Регистр CRL (control low) – режим работы
пинов с 0 по 7.
Регистр CRH (control high) –
режим работы пинов с 8 по 15.
Регистр IDR (input data register) – чтение состояния входов.
Регистр ODR (output data register) – чтение и запись состояния выходов.
И еще несколько.
У каждого порта есть такой набор регистров.
Где про них читать? reference manual, глава 9.2 (стр. 166)
Слайд 19
Доступ к регистрам периферии в языке С
Через указатели
на «волшебные» структуры:
GPIOA->ODR – доступ к регистру ODR порта
А, словно это обычная глобальная переменная.
(в других МК бывают «волшебные» указатели сразу на регистры, без структур)
Слайд 20
Почему ничего не работает?!
Практически всю периферию в МК
нужно сначала включить (подать питание и тактирование).
Это нужно сделать
через регистры подсистемы тактирования. Все GPIO включаются через регистр RCC->APB2ENR (ref. man. стр. 142)
Слайд 21
Как же зажечь светодиод
Подать питание на нужный порт
регистр
RCC->AP2ENR
Настроить режим нужного контакта в нужном порту (нужен режим
output push pull)
регистр GPIOx->CRH или CRL
Вывести на контакт высокий уровень
регистр GPIOx->ODR
Слайд 22
Битовые манипуляции
Установка одного бита:
a |= 1
седьмой бит
Сброс одного бита:
a &= ~(1
бит
Инверсия одного бита:
a ^= 1<<5; // инверсия пятого бита
Слайд 23
Доступ к регистрам, отображенным на память
Допустим, адрес нужного
мне регистра - 0x4001 0800.
И регистр этот размером в
4 байта.
Как мне в него что-нибудь записать, если я пишу на С?
Например, можно создать указатель:
uint32_t * ptr = 0x40010800;
Но указателю нельзя присвоить число, будет ошибка компиляции.
Нужно сделать приведение типа:
uint32_t * ptr = (uint32_t *)0x40010800;
Теперь почти все ок, можно записать число по нужному адресу:
*ptr = 1;
Слайд 24
Доступ к регистрам, отображенным на память
А как сделать
то же самое, не создавая указатель?
(0x40010800)
(
(volatile uint32_t *)
)
*
= 1
Ужас
какой. И что, каждый раз вот так писать?
Можно и так.
Но как правило, этот ужас прячут за #define:
#define REGISTER *((volatile uint32_t *)0x40010800)
И потом можно писать так, словно REGISTER – это заранее созданная глобальная переменная:
REGISTER = 5;