Rust embedded. Stopwatch.
Rust embedded. Spi и embedded-graphics.
Rust embedded. Gpio.
Rust embedded. Сиквел.
Rust embedded.
Сегодня речь про gpio и rust. Gpio основной интерфейс для старта. Сегодня мы поморгаем светодиодами на отладочной плате stm32f429i-disco и сделаем это двумя способами.
Интерфейс ввода/вывода общего назначения (англ. general-purpose input/output, GPIO) — интерфейс для связи между компонентами компьютерной системы, к примеру микропроцессором и различными периферийными устройствами. Контакты GPIO могут выступать как в роли входа, так и в роли выхода — это, как правило, конфигурируется. GPIO контакты часто группируются в порты.
На микроконтроллерах серии STM32 порты общего назначения обозначаются как: GPIOA, GPIOB и т.д. На схеме микроконтроллера соответствующие портам выводы обозначаются PA0-PA15, PB0-PB15 и т.д. Порты 16- разрядные, у каждого порта 16 выводов. Режим и состояние каждого вывода могут быть установлены отдельно, независимо от других выводов. Каждый вывод может использоваться в режиме:
-
- Input floating – вход без подтягивающего резистора, порт никуда не подключен;
- Input pull-up – вход с подтягивающим резистором, порт подключен к питанию микроконтроллера;
- Input pull-down – вход с подтягивающим резистором, подключенным к общему сигналу (GND);
- Analog – аналоговый вход;
- Output open-drain – выход с открытым стоком. Выход или подключен к “0” или в воздухе болтается (например, для внешней подтяжки вверх);
- Output push-pull – обычный активный выход. При низком логическом уровне напряжение на выводе равно 0, при высоком – напряжение близко к напряжению питания микроконтроллера, обычно + 3 В;
- Alternate function push-pull – альтернативная функция вывода в обычном (активном) режиме;
- Alternate function open-drain – альтернативная функция вывода в режиме открытый сток;
Первый пример. Простое baremetal firmware. Baremetal – это небольшой код, инициализирующий ядро/ядра, и передающий управление пользовательской программе, которую по какой-то причине требуется запустить без нормальной ОС и без ОСРВ. На отладочной плате у нас присутствуют два пользовательских светодиода LD3 и LD2, и им соответствующие пины микроконтроллера PG13 и PG14.
cargo-features = ["default-run"] [package] authors = ["Roman Shulenkov postmaster@mcu.by"] categories = ["embedded", "no-std"] name = "stm32f429i_disco" version = "0.1.0" edition = "2018" [dependencies] cortex-m = "0.6.2" cortex-m-rt = "0.6.12" panic-halt = "0.2.0" [dependencies.stm32f4] version = "0.10.0" features = ["stm32f429", "rt"] [dependencies.stm32f4xx-hal] version = "0.7" features = ["rt", "stm32f429"]
#![deny(unsafe_code)] #![no_main] #![no_std] // Halt on panic #[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964 extern crate panic_halt; // panic handler use cortex_m; use cortex_m_rt::entry; use stm32f4xx_hal as hal; use crate::hal::{prelude::*, stm32}; #[entry] fn main() -> ! { if let (Some(dp), Some(cp)) = ( stm32::Peripherals::take(), cortex_m::peripheral::Peripherals::take(), ) { // Set up the LEDs. On the stm32f429i-disco they are connected to pin PG13 and PG14. let gpiog = dp.GPIOG.split(); let mut led3 = gpiog.pg13.into_push_pull_output(); let mut led4 = gpiog.pg14.into_push_pull_output(); // Set up the system clock. We want to run at 180MHz for this one. let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.sysclk(180.mhz()).freeze(); // Create a delay abstraction based on SysTick let mut delay = hal::delay::Delay::new(cp.SYST, clocks); loop { // On for 1s, off for 1s. led3.set_high().unwrap(); led4.set_low().unwrap(); delay.delay_ms(1000_u32); led3.set_low().unwrap(); led4.set_high().unwrap(); delay.delay_ms(1000_u32); } } loop {} }
Второй пример по функционалу полностью как первый пример, только мы воспользуемся фреймворком RTMF. RTMF – это среда параллелизма для построения систем реального времени.
Возможности RTFM:
- Задачи – единица конкуренции. Задачи могут запускаться по событию (в ответ на асинхронный стимул) или вызываться программно по желанию.
- Передача сообщений между задачами. А именно, сообщения можно передавать программным задачам в момент вызова.
- Очередь таймера. Программные задачи можно планировать на запуск в определенный момент в будущем. Это свойство можно использовать, чтобы реализовывать периодические задачи.
- Поддержка приоритетов задач, и таким образом, вытесняющей многозадачности.
- Эффективное, свободное от гонок данных разделение памяти через хорошо разграниченные критические секции на основе приоритетов.
- Выполнение без взаимной блокировки задач, гарантированное на этапе компиляции. Это более сильная гарантия, чем предоставляемая стандартной абстракцией Mutex.
- Минимальные затраты на диспетчеризацию. Диспетчер задач имеет минимальный след; основная часть работы по диспетчеризации делается аппаратно.
- Высокоэффективное использование памяти: Все задачи используют общий стек вызовов и нет сильной зависимости от динамического распределителя памяти.
- Все устройства Cortex-M полностью поддерживаются.
- Эта модель задач поддается известному анализу методом WCET (наихудшего времени исполнения) и техникам анализа диспетчеризации (хотя разработчики еще не разработали дружественных инструментов для этого).
Все программы на RTFM используют атрибут app (#[app(..)]). Этот атрибут нужно применять к const-элементам, содержащим элементы. Атрибут app имеет обязательный аргумент device, в качестве значения которому передается путь. Этот путь должен указывать на библиотеку устройства, сгенерированную с помощью svd2rust v0.14.x. Атрибут app развернется в удобную точку входа, поэтому нет необходимости использовать атрибут cortex_m_rt::entry.
init
Внутри псевдо-модуля атрибут app ожидает запуска функции инициализации, обозначенную атрибутом init. Эта функция должна иметь сигнатуру [unsafe] fn().
Эта функция инициализации будет первой частью запускаемого приложения. Функция init запустится с отключенными прерываниями и будет иметь эксклюзивный доступ к периферии Cortex-M и специфичной для устройства периферии через переменные core и device, которые внедряются в область видимости init атрибутом app. Не вся периферия Cortex-M доступна в core, потому что рантайм RTFM принимает владение частью из неё.
idle
Функция, помеченная атрибутом idle может присутствовать в псевдо-модуле опционально. Эта функция используется как специальная задача ожидания и должна иметь сигнатуру [unsafe] fn() – > !.
Когда она присутствует, рантайм запустит задачу idle после init. В отличие от init, idle запустится с включенными прерываниями и не может завершиться, поэтому будет работать бесконечно.
cargo-features = ["default-run"] [package] authors = ["Roman Shulenkov postmaster@mcu.by"] categories = ["embedded", "no-std"] name = "stm32f429i_disco" version = "0.1.0" edition = "2018" [dependencies] cortex-m = "0.6.2" cortex-m-rt = "0.6.12" panic-halt = "0.2.0" panic-semihosting = "0.5.3" cortex-m-semihosting = "0.3.5" cortex-m-rtfm = "0.5.1" [dependencies.stm32f4] version = "0.10.0" features = ["stm32f429", "rt"] [dependencies.stm32f4xx-hal] version = "0.7" features = ["rt", "stm32f429"]
#![no_main] #![no_std] use panic_semihosting as _; use rtfm::app; use cortex_m; use stm32f4xx_hal as hal; use crate::hal::{prelude::*, stm32}; use stm32f4::stm32f429; #[app(device = stm32f429)] const APP: () = { struct Resources { led3: stm32f4xx_hal::gpio::gpiog::PG13<stm32f4xx_hal::gpio::Output<stm32f4xx_hal::gpio::PushPull>>, led4: stm32f4xx_hal::gpio::gpiog::PG14<stm32f4xx_hal::gpio::Output<stm32f4xx_hal::gpio::PushPull>>, delay: stm32f4xx_hal::delay::Delay, } #[init] fn init(_cx: init::Context) -> init::LateResources{ let dp = stm32::Peripherals::take().unwrap(); let cp = cortex_m::peripheral::Peripherals::take().unwrap(); let gpiog = dp.GPIOG.split(); let led3 = gpiog.pg13.into_push_pull_output(); let led4 = gpiog.pg14.into_push_pull_output(); // Set up the system clock. We want to run at 180MHz for this one. let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.sysclk(180.mhz()).freeze(); // Create a delay abstraction based on SysTick let delay = hal::delay::Delay::new(cp.SYST, clocks); init::LateResources { led3, led4, delay, } } #[idle (resources = [led3, led4, delay])] fn idle(_cx: idle::Context) -> ! { let led3: &mut stm32f4xx_hal::gpio::gpiog::PG13<stm32f4xx_hal::gpio::Output<stm32f4xx_hal::gpio::PushPull>> = _cx.resources.led3; let led4: &mut stm32f4xx_hal::gpio::gpiog::PG14<stm32f4xx_hal::gpio::Output<stm32f4xx_hal::gpio::PushPull>> = _cx.resources.led4; let delay: &mut stm32f4xx_hal::delay::Delay = _cx.resources.delay; loop { // On for 1s, off for 1s. led3.set_high().unwrap(); led4.set_low().unwrap(); delay.delay_ms(1000_u32); led3.set_low().unwrap(); led4.set_high().unwrap(); delay.delay_ms(1000_u32); } } };