В этой части мы разберемся с порами ввода-вывода GPIO микроконтроллера STM32F103C8 и напишем «Hello, World!» с мигающим светодиодом, а так же научимся читать состояние выводов микроконтроллера и использовать встроенный подтягивающий резистор. Предыдущая статья здесь, все статьи цикла можно посмотреть тут: https://dimoon.ru/category/obuchalka/stm32f1.
Введение
General-purpose input/output (GPIO) — важный компонент любого микроконтроллера, с помощью которого он взаимодействует с окружающим миром. В микроконтроллерах STM32 порты именуются буквами A, B, C и так далее: GPIOA, GPIOB, GPIOC... Каждый GPIO имеет 16 линий ввода/вывода, причем каждая линия может быть настроена независимо от других. Вот варианты настройки:
- Input floating — вход с отключенными подтягивающими резисторами
- Input pull-up — вход с подтяжкой к логической единице
- Input-pull-down — вход с подтяжкой к логическому нулю
- Analog — аналоговый вход (например, для АЦП)
- Output open-drain — выход с открытым коллектором (записали 1 — выход в высокоимпедансном состоянии, записали 0 — выход прижат внутренним транзистором к земле)
- Output push-pull — выход «тяни-толкай» (записали 1 — на выходе лог. 1, записали 0 — на выходе лог. 0)
- Alternate function push-pull — альтернативная функция в режиме «тяни-толкай»
- Alternate function open-drain — альтернативная функция в режиме открытого коллектора
Дам несколько пояснений по поводу режимов.
Режим Analog. Внутри микроконтроллера есть аналогово-цифровые преобразователи, которые, как известно, должны иметь аналоговые входы. Так вот, в режиме Analog ножка микроконтроллера подключается к аналоговому входу АЦП внутри микроконтроллера. Кроме того, отключается вся цифровая обвязка этой ножки для уменьшения цифрового шума и энергопотребления.
Alternate function. В этом режиме ножкой микроконтроллера управляет внутренняя цифровая периферия, например, модуль USART.
Регистры GPIO
Давайте рассмотрим регистры портов GPIO.
Port configuration register low (GPIOx_CRL)
Рис. 1. Регистр CRL
Это конфигурационный регистр для выводов порта с номерами от 0 до 7. Каждому выводу предоставлено 4-ре бита конфигурации: 2 бита MODEy и 2 бита CNFy.
MODEy[1:0]: Режим ножки порта, вход или выход. В режиме выхода нужно выбрать максимальную частоту переключения данной ножки, насколько понял это является оптимизацией энергопотребления порта.
- 00: Вход (значение после сброса)
- 01: Выход, максимальная частота 10 MHz.
- 10: Выход, максимальная частота 2 MHz.
- 11: Выход, максимальная частота 50 MHz.
CNFy[1:0]: Конфигурация режима.
В режиме входа (MODEy[1:0]=00):
- 00: Analog mode — аналоговый режим (подключен к АЦП или ЦАП-у)
- 01: Floating input — вход с отключенными подтягивающими резисторами (значение после сброса)
- 10: Input with pull-up / pull-down — вход с подтяжкой вверх или вниз
- 11: Reserved — не используется
В режиме выхода (MODEy[1:0]>00):
- 00: General purpose output push-pull — выход в режиме тяни/толкай
- 01: General purpose output Open-drain — выход с открытым коллектором
- 10: Alternate function output Push-pull — выход альтернативной функции режиме тяни/толкай
- 11: Alternate function output Open-drain — выход альтернативной функции с открытым коллектором
Port configuration register high (GPIOx_CRH)
Рис. 2. Регистр CRH
Это конфигурационный регистр для выводов порта с номерами от 8 до 15. Тут все то же, что и для регистра GPIOx_CRL.
Port input data register (GPIOx_IDR)
Рис. 3. Регистр IDR
IDRy: в этих битах содержится входное значение соответствующего порта ввода-вывода.
Port output data register (GPIOx_ODR)
Рис. 4. Регистр ODR
ODRy: выходные данные порта.
Port bit set/reset register (GPIOx_BSRR)
Рис. 5. Регистр BSRR
С помощью этого регистра можно сбросить или установить любой бит регистра ODR без операций чтение-модификация-запись.
BRy: Сбросить бит у регистра ODR порта ввода-вывода (y= 0 .. 15)
- 0: не оказывает влияние на соответствующий бит ODRx
- 1: Сбрасывает в ноль соответствующий бит ODRx
BSy: Установить бит у регистра ODR порта ввода-вывода (y= 0 .. 15)
- 0: не оказывает влияние на соответствующий бит ODRx
- 1: Устанавливает в единицу соответствующий бит ODRx
Port bit reset register (GPIOx_BRR)
Рис. 6. Регистр BRR
С помощью этого регистра можно сбросить любой бит регистра ODR без операций чтение-модификация-запись.
BRy: Сбросить бит у регистра ODR порта ввода-вывода (y= 0 .. 15)
- 0: не оказывает влияние на соответствующий бит ODRx
- 1: Сбрасывает в ноль соответствующий бит ODRx
Port configuration lock register (GPIOx_LCKR)
Рис. 7. Регистр LCKR
Этот регистр используется для блокировки конфигурационных битов порта после записи корректной последовательности в 16 бит (LCKK) регистра. Значения битов [15:0] используется для блокировки конфигурации GPIO. Во время блокирующей последовательности в LCKK значения LCKR [15: 0] не должны меняться. Когда блокирующая последовательность была записана, конфигурация выбранных портов ввода/вывода может быть изменена только после сброса микроконтроллера. Каждый LCKy бит блокирует возможность изменения четырех битов конфигурации порта (CRL, CRH).
LCKK[16]: Ключ блокировки.
- 0: Блокировка конфигурации порта не активна.
- 1: Блокировка конфигурации порта активна. GPIOx_LCKR заблокирован до следующего сброса микроконтроллера.
Для блокировки необходимо выполнить следующую последовательность:
- Записать 1
- Записать 0
- Записать 1
- Прочитать 0
- Прочитать 1 (эта операция чтения не является обязательной, а всего лишь подтверждает успешность установки блокировки)
LCKy: Эти биты могут быть прочитаны и записаны, но запись можно произвести только если бит LCKK равен нулю.
- 0: Конфигурация пина номер y не заблокирована
- 1: Конфигурация пина номер y заблокирована
Настройка порта GPIO
Итак, с регистрами разобрались, настало время практики. Все примеры в этой статье для микроконтроллера STM32F103C8. В моем распоряжении есть вот такая отладочная плата:
На ней установлен кварцевый резонатор на 8 МГц и светодиод на порту PB12. Вот с помощью этого светодиода мы и устроим Hello, World! 😉
Задача ясна: настраиваем PB12 на выход в режиме push-pull и с помощью регистра ODR дергаем 12-й пин порта GPIOB туда-сюда! Но мы забыли об одной маленько детали: RCC. Дело в том, что по-умолчанию после сброса микроконтроллера все периферийные модули отключены от источника тактового сигнала, в том числе и GPIO. А подать тактирование можно с помощью регистров RCC. В 3-ей части я про это говорил. Для начала нужно определить, к какой шине у нас подключен GPIOB. Открываем даташит на микроконтроллер, ищем вот эту таблицу:
Рис. 8. Таблица шин и периферийных устройств
GPIOB у нас подключен к шине APB2. Идем в Reference manual, открываем раздел про RCC, переходим к пункту 7.3.7 APB2 peripheral clock enable register (RCC_APB2ENR). С помощью этого регистра можно подать тактовый сигнал на устройства шины APB2:
Рис. 9. Регистр RCC_APB2ENR
В регистре RCC_APB2ENR много флагов для разной периферии, в том числе и для нашего GPIOB, флаг называется IOPBEN. Перед началом инициализации PB12 нам надо установить этот бит в единицу.
Поехали программировать! За основу возьмем проект из 2-й части: https://github.com/DiMoonElec/stm32f103c8_empty_project. Создадим функцию инициализации порта:
void PortInit(void) { }
Первым делом включаем тактирование порта GPIOB:
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //Включаем тактирование порта GPIOB
Далее настройка порта. Нам нужен пин PB12. Его конфигурационные биты находятся в регистре CRH:
GPIOB->CRH &= ~(GPIO_CRH_MODE12 | GPIO_CRH_CNF12); //для начала все сбрасываем в ноль //MODE: выход с максимальной частотой 2 МГц //CNF: режим push-pull GPIOB->CRH |= (0x02 << GPIO_CRH_MODE12_Pos) | (0x00 << GPIO_CRH_CNF12_Pos);
Все 😉 Настройка завершена! Вот полный код функции:
void PortInit(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //Включаем тактирование порта GPIOB GPIOB->CRH &= ~(GPIO_CRH_MODE12 | GPIO_CRH_CNF12); //для начала все сбрасываем в ноль //MODE: выход с максимальной частотой 2 МГц //CNF: режим push-pull GPIOB->CRH |= (0x02 << GPIO_CRH_MODE12_Pos) | (0x00 << GPIO_CRH_CNF12_Pos); }
Теперь управление. Для этого нам понадобится регистр ODR (рис. 4). Вот функция установки высокого уровня на PB12:
void PortSetHi(void) { GPIOB->ODR |= (1<<12); }
А вот низкого:
void PortSetLow(void) { GPIOB->ODR &= ~(1<<12); }
Ни чего сложного, почти как на AVR-ках! Однако, у нас все же более серьезный микроконтроллер, и у него есть некоторые фишки. Выражения вида GPIOB->ODR |= (1<<12) являются операциями чтение-модификация-запись. Это долго, и имеет побочные эффекты при одновременном обращении к одному и тому же регистру из разных участков программы (например, из прерывания и основного потока программы). Это называется нарушение атомарности операции. Но в STM32 можно сделать вот так:
void PortSetHi(void) { GPIOB->BSRR = (1<<12); } void PortSetLow(void) { GPIOB->BRR = (1<<12); }
Конструкции (1<<12) превращаются в битовую маску 0x1000 на этапе компиляции, и у нас получается только одна операция записи в регистр BSRR или BRR (см. рис. 5, 6).
Ни и простой main() для проверки:
void main() { int i; PortInit(); for(;;) { PortSetHi(); for(i=0; i<0x40000; i++) ; PortSetLow(); for(i=0; i<0x40000; i++) ; } }
Все, светодиод, подключенный к BP12 мигает! «Hello, World!» готов!
Давайте теперь настроим какой-нибудь вывод порта, например PB15, на вход с подтяжкой к питанию. При подключении PB15 к минусу, у нас будет зажигаться светодиод. Задача ясна, преступаем к реализации. В PortInit() добавим пару строк:
/// Настраиваем PB15 на вход с подтяжкой к питанию /// GPIOB->CRH &= ~(GPIO_CRH_MODE15 | GPIO_CRH_CNF15); //MODE: вход, оставляем в нуле //CNF: вход с pull-up / pull-down GPIOB->CRH |= (0x00 << GPIO_CRH_MODE15_Pos) | (0x02 << GPIO_CRH_CNF15_Pos); GPIOB->ODR |= (1<<15); //Включаем подтяжку вверх
Тут все почти то же самое, как и при настройке PB12, только другой пин и параметры MODE/CNF. А вот последняя строчка требует пояснений. Дело в том, что регистр ODR имеет двойное назначение. Если пин настроен на выход, то соответствующий бит в ODR отвечает за значение логического уровня на этом пине. Но если пин настроен на вход, причем CNF=10 (Input with pull-up / pull-down), то соответствующий бит в ODR управляет подтягивающими резисторами этого пина. К стати, в Reference manual есть вот такая картинка:
Рис. 10. Таблица конфигурации порта
Функция PortInit() приобретает такой вид:
void PortInit(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //Включаем тактирование порта GPIOB /// Настраиваем PB12 на выход /// GPIOB->CRH &= ~(GPIO_CRH_MODE12 | GPIO_CRH_CNF12); //для начала все сбрасываем в ноль //MODE: выход с максимальной частотой 2 МГц //CNF: режим push-pull GPIOB->CRH |= (0x02 << GPIO_CRH_MODE12_Pos) | (0x00 << GPIO_CRH_CNF12_Pos); /// Настраиваем PB15 на вход с подтяжкой к питанию /// GPIOB->CRH &= ~(GPIO_CRH_MODE15 | GPIO_CRH_CNF15); //MODE: вход, оставляем в нуле //CNF: вход с pull-up / pull-down GPIOB->CRH |= (0x00 << GPIO_CRH_MODE15_Pos) | (0x02 << GPIO_CRH_CNF15_Pos); GPIOB->ODR |= (1<<15); //Включаем подтяжку вверх }
Перейдем к чтению состояния PB15. Для этого существует регистр IDR (рис. 3):
int ReadPort(void) { if(GPIOB->IDR & (1<<15)) return 1; return 0; }
Думаю тут все понятно: если 15 бит в регистре IDR равен 1, то возвращаем единицу, а иначе — ноль.
В этом случае main() будет выглядеть вот так:
void main() { PortInit(); for(;;) { if(ReadPort()) PortSetHi(); else PortSetLow(); } }
На отладочной плате с микроконтроллером stm32f103c8 светодиод зажигается низким уровнем на выводе PB12. Это значит, что при замыкании PB15 на землю, будет прочитано нулевое значение на этом выводе и установлен ноль на PP12, и светодиод зажжется. Если PB15 отключить от земли, то внутренняя подтяжка прижмет вывод к плюсу питания микроконтроллера, на PB15 функция ReadPort() прочтет логическую единицу и на выводе PB12 возникнет лог. 1, что приведет к погасанию светодиода.
На этом все, продолжение следует! 😉 Продолжение.
А почему мы не используем регистр BSRR для записи нуля а пользуемся BBR? Из тех же соображений что и ODR. И что будет если в него в двух позициях записать 1 и на установку 1 и на установку 0
Можно и BBR, разницы ни какой нет. Можно использовать и ODR, но в этом случае нужно выполнять операцию типа Чтение-Модификация-Запись, а для BSRR и BBR только запись — это быстрее и нет возможности возникновения нарушения атомарности операции
Что будет, если в BSRR записать единицы на установку и сброс одного и того же пина GPIO я без понятия, надо попробовать)) В документации такого вроде не попадалось
Нашел в даташите: If both BSx and BRx are set, BSx has priority.
Здравствуйте. у меня вопрос — в проекте для задержки между вкл. и выкл. светодиода используется цикл for.а как обстоят дела в стм32 насчет служебных ф-ий типа Delay_ms() как в GCC для 8-ми битных АВРок?
Сам использую компилятор от IAR, насколько знаю, для ARM-ов ни чего подобного нет, хотя для тех же AVR-ок есть функция __delay_cycles(). В реальных проектах если надо генерить сигнал с периодом больше 1ms и с не очень большой точностью поддержания периода, то реализую это на программных таймерах и конечных автоматах, если периоды меньше и/или нужна большая точность — то аппаратные таймеры. Если же нужно выдать короткий управляющий импульс для какой-то микры на плате — то использую задержку на for, что-то типа for(i=0; i<5; i++) {asm("nop");}, asm("nop") для того, чтоб оптимизатор не выкинул пустой цикл.
Нашел на просторах инета такой интересный код
Никаких таймеров, все быстро и компактно. Только у меня в include надо core_cm3.h вместо stm32f10x_tim.h. Использую STM32CubeIDE
и функцию delta желательно переписать с учетом переполнения счетчика, чтобы не получить непонятные глюки на граничных значениях
Здравствуйте. еще вопросик — например я затактировался от HSE через PLL x9. в итоге частота 72 Мгц. Далее например подключаю GPIOB : RCC->APB2ENR|=RCC_APB2ENR_IOPBEN; .Но ведь выход порта В я могу настроить только на макс. 50Мгц , Значит ли это что и делитель АРВ2 в этом случае настроить как минимум на HCLK/2 поскольку частота на этой шине 72 Мгц?
То, что настраивается в регистрах GPIO как максимальная скорость работы, как ни странно, не имеет ни какого отношения к тактированию. Этими битами настраивается максимальный потребляемый ток данного пина. Пример: если настроить пин на максимальную частоту 2 МГц, но при этом подать 72 МГц, то сигнал на этом пине будет больше похож на синус, чем на меандр. Если же настроить на 50 МГц, то в этом случае на выходе будет уже что-то более-менее похожее на меандр
Где-то обсуждали (ссылаясь на доки), что частота переключения пинов в STMках отражает на самом деле скорость нарастания/спада фронта, а значение частоты — это максимальная частота, при которой сумма времени нарастания и спада не более двух третей от стабильного уровня импульса. Быстрое переключение выводов может оказывать электромагнитное влияние на работу самого микроконтроллера. Поэтому, если нет необходимости в высоких частотах, предлагается устанавливать опцию более низкой частоты (т.е. более медленные изменения уровня сиглала на ножках) в целях более высокой помехоустойчивости. При этом, если повышенная частота нужна, то такая опция такая есть.
Да, именно так
Оставлю это здесь в качестве напоминалки. ))
Понадобилось организовать параллельный интерфейс между двумя МК — одним махом байт передавать без последовательных заморочек.
Самое быстрое решение — BSRR. Готовим маску и засылаем его туда.
Пример — грузим байт в порт А биты с 0 по 7:
uint8_t data = 0xAA;
uint32t mask = ((data) | (~data<BSRR = mask;
Пример — грузим байт туда же, но в биты 4-11, поскольку первые четыре заняты под АЦП
uint8_t data = 0xAA;
uint32t mask = ((data<<4) | (~data<BSRR = mask;
В принципе, никто не мешает сделать так:
mask=0;
if(data&0x01) mask|=GPIO_BSRR_BS4; else mask|=GPIO_BSRR_BR4;
if(data&0x02) mask|=GPIO_BSRR_BS5; else mask|=GPIO_BSRR_BR5;
if(data&0x04) mask|=GPIO_BSRR_BS6; else mask|=GPIO_BSRR_BR6;
if(data&0x08) mask|=GPIO_BSRR_BS7; else mask|=GPIO_BSRR_BR7;
if(data&0x10) mask|=GPIO_BSRR_BS8; else mask|=GPIO_BSRR_BR8;
if(data&0x20) mask|=GPIO_BSRR_BS9; else mask|=GPIO_BSRR_BR9;
if(data&0x40) mask|=GPIO_BSRR_BS10; else mask|=GPIO_BSRR_BR10;
if(data&0x80) mask|=GPIO_BSRR_BS11; else mask|=GPIO_BSRR_BR11;
GPIOA->BSRR=mask;
Если бы не два но:
1. Скорость выполнения — первый вариант выполняется 31 тик процессора, второй — 146 — первый в четыре раза быстрее
2. Размер кода (неоптимизированного) 36 байт и 328 — в 9 раз меньше
упс! порезались < &
Пример 1.
Пример 2.
Здравствуйте!
Написал программку для опроса 2 датчиков и 1 кнопки.
PB11 — термодатчик DS18B20
PB12 — датчик температуры и влажности DHT22
PB3 — кнопка
Если помимо опроса датчиков идёт опрос кнопки, то датчик влажности показывает влажность 0, температуру при этом показывает верно. А если закомментировать опрос кнопки, то влажность отображается корректно.
Почему такая проблема ? Что я делаю не так ?
Здраствуйте! Не получается запустить хотя б Hello word. Использовал предыдущую программу, добавил код данного урока до подключения. Строки int main() закоментировал, с ними не компилируется.
Проблему решил, на моей отладочной плате светодиод подключен к PC13. программу переписал все работает, спасибо за уроки!
Здравствуйте. У меня среда отладки CubeIDE. Столкнулся с проблемой. Купленная ранее (в 2019) Blue Pill переходит в отладку со стандартным ST-Link GBD server. А вроде бы такая же Blue Pill, но купленная в 2020 году не переходит в режим отладки с ошибкой, что мк не опознан. При использовании ST-Link GBD OpenOCD получаю сообщение что сервер ждет код ядра STM32VL_CORE_ID 0x1ba01477, а получает STM32L_CORE_ID 0x2ba01477. Не подскажете где можно найти описание этих ядер и понять в чем разница между ними? На ST-Link Utility обе таблетки программируются без ошибок. В интернете по кодам ядер инфы не нашел. Может плохо искал. Помогите разобраться в этом вопросе.
С КубIDE не знаком, могу только предположить, что на отладочной плате микроконтроллер является китайской копией, и в IDE что-то происходит «не туда»
Это китайская копия контроллера, сам с таким сталкивался. Можно пофиксить, заменив STM32VL_CORE_ID 0x1ba01477 на 0x2ba01477 в
\xpack-openocd-0.10.0-14\scripts\target\stm32f1x.cfg
После этого заработало. Только при оригинале придется вернуть обратно.
Вопрс?
GPIOB->BSRR = GPIO_BSRR_BR9; // ((uint32_t)0x02000000)
GPIOB->BSRR = GPIO_BSRR_BS9;
// ((uint32_t)0x00000200)
GPIOB->BRR = GPIO_BRR_BR9;
// ((uint16_t)0x0200)
GPIOB->BRR = GPIO_BSRR_BS9;
// ((uint32_t)0x00000200)
GPIOB->BSRR = (1 <BSRR &= ~GPIO_ODR_ODR9; // ((uint16_t)0x0200)
Чем лучше пользоваться выставлять 0 или 1 ????
(GPIOB->ODR ) медленно, и боится прерываний
BSRR = BSRR BS
BSRR = BSRR BR
BRR = BSRR BS
BRR = BRR BR
Так как правильно ??? Ломаю голову
Разобрался
BSRR — BR — 0 на выводе
BSRR — BS — 1 на выводе
—————————————
BRR — (только BR) 1 на выводе
—————————————
ODR — 0.7 мГц ногодрыг (пока прочитает — запишет — понятно) но ???
BSRR — BS — 1 на выводе 1,38 мГц ногодрыг
BRR — BR — 1 на выводе 1,7 мГц ногодрыг
почему??
Может и не прав,но формат GPIOB->BRR = (1<BRR |= (1<<12);с BSRR тоже,или компилятор меня понимает правильно через дефайны?
Вордпресс поломал форматирование кода, напишите мне на почту этот вопрос
Скажите, пожалуйста: уже долго мучаюсь и не могу запустить прогу. Все компилится, загружается, но светодиод все равно не моргает. Причем если использовать сгенеренный проект на CubeMX LL, то все работает, а вот без использования CubeMX не работает. Как будет контроллер как-то залочен. Только что поставил IAR, скопировал код RCC и под светодиод на этой страничке, поменяв его под 13 вывод порта C. И тоже самое, все скомпилилось, загрузилось, светодиод не моргает. Плата blue pill на stm32f103c8.
Пришлите мне на почту код, посмотрю
Здравствуйте!
Прошу, расскажите, как устроить прерывание от перепада на выводе.
http://dimoon.ru/obuchalka/stm32f1/programmirovanie-stm32-chast-15-vneshnie-preryivaniya-exti.html
Вот тут писал про это
DiMoon, помоги хоть что-то запустить на плате с stm32f103vdh6. Вообще у меня цель сделать обмен по usb. Но до этого так далеко. Я даже не могу мигнуть светодиодом. Сначала я делал прошивку в CubeIDE. Просто выбрал модель, подключил HSE и обозначи пин PE15 как output. Добавил 2 строки кода в main HAL_Toggle…. и HAL_Delay и прошил. Ничего не мигает.
На похожей плате я выпаял stm32 и прозвонил пяточки под bga100 pin. Светодиод весит на PE15.
Теперь я нашел твою обучалку. Скачал iar и сделал как ты.
Вот код main.c
ff
2 письма
#include «stm32f1xx.h»
int ClockInit(void)
{
__IO int StartUpCounter;
RCC->CR |= (1<CR & (1< 0x1000)
{
RCC->CR &= ~(1<CFGR |= (0x07<<RCC_CFGR_PLLMULL_Pos)
| (0x01<CR |= (1<CR & (1< 0x1000)
{
RCC->CR &= ~(1<CR &= ~(1<ACR |= (0x02<CFGR |= (0x00<<RCC_CFGR_PPRE2_Pos)
| (0x04<<RCC_CFGR_PPRE1_Pos)
| (0x00<CFGR |= (0x02<CFGR & RCC_CFGR_SWS_Msk) != (0x02<CR &= ~(1<APB2ENR |= RCC_APB2ENR_IOPEEN;
GPIOE->CRH &= ~(GPIO_CRH_MODE15 | GPIO_CRH_CNF15);
GPIOE->CRH |= (0x02 << GPIO_CRH_MODE15_Pos) | (0x00 <ODR |= (1<ODR &= ~(1<<15);
}
void main()
{
int i;
ClockInit();
PortInit();
for(;;)
{
PortSetHi();
for(i=0; i<0x40000; i++)
;
PortSetLow();
for(i=0; i<0x40000; i++)
;
}
}
Прошил. Светодиод молчит.
Где собака порылась? Не понимаю.
1. Заходим на страницу этого МК, читаем немного описания:
https://www.st.com/en/microcontrollers-microprocessors/stm32f103vd.html#overview
Из текста понимаем, что этот МК относится к high-density (классификация дуратская, да)
2. Качаем референс мануал:
https://www.st.com/resource/en/reference_manual/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
3. Читаем раздел 7.3.7 APB2 peripheral clock enable register (RCC_APB2ENR), включаем тактирование порта Е. Для этого установить в 1 бит IOPEEN.
4. Инициализируем PE15. Смотрим 9.2.2 Port configuration register high (GPIOx_CRH) (x=A..G),
MODE15=0b01, CNF15=0b00
5. Смотрим раздел 9.2.4 Port output data register (GPIOx_ODR) (x=A..G). Бит ODR15 устанавливаем в 1, диод должен загореться, ODR15 в ноль – гаснуть (или наоборот, зависит от реализации на плате)
Обязательно к ознакомлению видео https://youtu.be/-IiUGOK7k9U
Советую понатыкать в отладчике, как в видео, а потом уже код писать, так разберешься более детально 😉 УДАЧИ!!!!
Привет.
Понатыкал в дебагере. Поигрался с регисторами. Результат отрицательный. Не горит.
Вот мои действия:
Текст кода — просто цикл. Нажал ctrl-d. В регистр IOPEEN записал 1, Mode15 1, Cnf 0. А потом в ODR15 1.
Я не делал никакую инициализацию Clock от HSE. Все от HSI.
Я не понимаю в чем причина. Плата рабочая, если записать оригинальную прошивку, то работает, мигает.
Светодиод висит на PE15 через резистор 100-110 Om.
Ошибиться не могу с пятоком. Т.к. уже 100 раз сравнивал. Ноги HSE и HSL совпадают с топ view.
Сдаемся?
А разрешение тактирования порта в RCC делал? Сначала нужно включить тактирование порта в RCC, а потом уже выполнять его настройку и тд
Ну, да. В регистр IOPEEN записал 1.
Вышли мне на почту скрин дебаггера с регистрами RCC, где настраивал, и регистры GPIOE. Название отладочной платы тоже кинь туда, по свободе посмотрю. Сто пудов там где-то мелкая ошибка, на которую не обратили внимание
Привет.
Я уже хотел сдаться, но сегодня вспомнил, что точно такая же плата после повреждения водой. Выпаял stm32 и прозвонил пяточки. Хм. Оказалось светодиод на PD2 здесь висит. Меня сбило, что прошивка для них одинаковая. Значит как-то программно это обошли.
Вообщем теперь я умею мигать регисторами. Ура.
Спасибо за помощь.
Теперь мне надо как-то подключить два кварца и затактировать usb. И в цикле выводить в терминал участок памяти stm32.
Если возникнут вопросы, то поможите советом?