{"id":22101151,"url":"https://github.com/rmja/drone-stm32f4-hal","last_synced_at":"2025-08-21T07:34:32.759Z","repository":{"id":96149004,"uuid":"317285252","full_name":"rmja/drone-stm32f4-hal","owner":"rmja","description":"Drone OS hardware abstraction layer for STM32F4 micro-controllers","archived":false,"fork":false,"pushed_at":"2022-11-30T10:35:30.000Z","size":401,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-25T04:39:14.692Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rmja.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-30T16:41:06.000Z","updated_at":"2025-04-06T11:13:28.000Z","dependencies_parsed_at":"2023-03-29T03:18:03.556Z","dependency_job_id":null,"html_url":"https://github.com/rmja/drone-stm32f4-hal","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rmja/drone-stm32f4-hal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmja%2Fdrone-stm32f4-hal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmja%2Fdrone-stm32f4-hal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmja%2Fdrone-stm32f4-hal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmja%2Fdrone-stm32f4-hal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rmja","download_url":"https://codeload.github.com/rmja/drone-stm32f4-hal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmja%2Fdrone-stm32f4-hal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271445000,"owners_count":24760947,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-21T02:00:08.990Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-01T05:23:46.050Z","updated_at":"2025-08-21T07:34:32.752Z","avatar_url":"https://github.com/rmja.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"![maintenance](https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg)\n\n# drone-stm32f4-hal\n\n[Drone OS] hardware abstraction layer (HAL) for STM32F4 micro-controllers.\n\n## Usage\n\nAdd the crate to your `Cargo.toml` dependencies:\n\n```toml\n[dependencies]\ndrone-stm32f4-hal = { git = \"https://github.com/rmja/drone-stm32f4-hal\", features = [\"...\"] }\n```\n\nA feature maps to a peripheral driver. There are the following low-level drivers:\n* `rcc` Type safe clock configuration model, and a rcc, pwr, and flash driver.\n* `gpio` Type safe pin-setup configuration primitives.\n* `dma` Dma primitives for uniform configuration across other, dma-dependent drivers.\n\nThere are the following more high-level drivers:\n\n* `exti` An external pin-interrupt driver.\n* `spi` Dma driven, future based spi driver.\n* `uart` Dma driven, future based uart driver.\n* `fmc` External SDRAM driver.\n\n## RCC\nA necessary but often complicated task when starting a new embedded application is to correctly configure the various clocks within the mcu. The `rcc` feature contains two parts:\n1. A clock tree configuration _model_, and\n2. a set of _drivers_ that effectuate the model on boot.\n\nThe following is the model configuration example from the [uart example app](https://github.com/rmja/drone-stm32f4-hal/blob/master/examples/uart/src/consts.rs):\n\n```rust\nuse drone_stm32f4_hal::rcc::clktree::*;\n\npub const HSECLK: HseClk = HseClk::new(8_000_000);\npub const PLLSRC_HSECLK: PllSrcMuxSignal = PllSrcMuxSignal::Hse(HSECLK);\npub const PLL: Pll = PLLSRC_HSECLK.to_pllsrc(8).to_pll(360, 2, 8);\npub const SYSCLK_PLL: SysClkMuxSignal = SysClkMuxSignal::Pll(PLL.p);\npub const SYSCLK: SysClk = SYSCLK_PLL.to_sysclk();\npub const HCLK: HClk = SYSCLK.to_hclk(1);\npub const PCLK1: PClk1 = HCLK.to_pclk1(4);\npub const PCLK2: PClk2 = HCLK.to_pclk2(2);\n```\n\nThe model desribes the following configuration:\n* The mcu board is equipped with an 8MHz crystal wired to the mcu, and we want to use it as the high-speed oscillator (HSE).\n* The HSE should be used as signal source for the internal PLL.\n* The PLL has two output signals: `PLL_P` and `PLL_Q`. With the HSE signal source, configure the pll outputs with the parameters `m`, `n`, `p`, and `q` as follows:\n  * `PLL_P = ((HSECLK / 8) * 360) / 2 = 180MHz`\n  * `PLL_Q = ((HSECLK / 8) * 360) / 8 = 45MHz`\n* Use the PLL's `PLL_P` clock signal as source for the sysclk.\n* Define `SYSCLK` to be equal to the selected sysclk signal. This means that this model will run the mcu at 180MHz.\n* Let the AHB bus, memory, dma, etc. sourced with `HCLK=SYSCLK/1`, i.e. run at the same frequency with a prescaler of 1.\n* Let the low speed peripheral bus run at `PCLK1=HCLK/4=45MHz`.\n* Let the high speed peripheral bus run at `PCLK2=HCLK/2=90MHz`.\n\nNote that the configuration is type safe and that parameters are verified at compile time, ensuring that only valid parameters are passed to the RCC configuration registers, and that it is guaranteed that the mcu is not overclocked.\n\nThe model is applied as follows ([again from the uart example app](https://github.com/rmja/drone-stm32f4-hal/blob/master/examples/uart/src/tasks/root.rs)):\n```rust\nuse drone_stm32f4_hal::rcc::{\n  periph_flash,\n  periph_pwr,\n  periph_rcc,\n  traits::*,\n  Flash, Pwr, Rcc, RccSetup,\n};\n\nthr.rcc.enable_int(); \n\nlet rcc_setup = RccSetup {\n  rcc: periph_rcc!(reg),\n  rcc_int: thr.rcc,\n};\nlet rcc = Rcc::init(rcc_setup);\nlet pwr = Pwr::with_enabled_clock(periph_pwr!(reg));\nlet flash = Flash::new(periph_flash!(reg));\n\nlet hseclk = rcc.stabilize(consts::HSECLK).await;\nlet pll = rcc.select(consts::PLLSRC_HSECLK, hseclk).stabilize(consts::PLL).await;\nlet hclk = rcc.configure(consts::HCLK);\nlet pclk1 = rcc.configure(consts::PCLK1);\nlet pclk2 = rcc.configure(consts::PCLK2);\npwr.enable_overdrive();\nflash.set_latency(consts::HCLK.get_wait_states(VoltageRange::HighVoltage));\nswo::flush();\nswo::update_prescaler(consts::HCLK.f() / log::baud_rate!() - 1);\nrcc.select(consts::SYSCLK_PLL, pll.p());\n```\n\nA lot is going on here, so one thing at a time. We first enable rcc interupt in the NVIC and then we initialize the `rcc`, `pwr`, and `flash` drivers from their respective [peripheral mappings](https://book.drone-os.com/periph.html).\n\nWhen the drivers are ready, we can start applying our model:\nWe start by _stabilizing_ the `HSE` clock, meaning that 1) we configure it according to the `HSECLK` model configuration, and 2) we start it. Stabilizing a clock is an asynchronous operation, so we need to await its completion. The stabilization returns a `ConfiguredClk\u003cHseClk\u003e` where `ConfiguredClk\u003cClk\u003e` is a zero-overhead indicator that tells us that the clock is ready to use (recall that `consts::HSECLK` is of type `HseClk`).\n\nMoving on, we can now select the `HSE` clock signal as the source for our PLL. The `select()` method requires a `ConfiguredClk\u003cClk\u003e` so we are guaranteed that the source clock has actually stabilized prior to selecting it as input. Only after that has happened are we able to stablizie the PLL and wait for it to become ready. After this, we can easily configure the peripheral clocks (This could have been done at any time, so there is no need to inforce any guarantees on prior stabilization of clocks).\n\nThe next couple of lines enables over-drive (available in e.g. stm32f429) for high-speed operation, sets the correct flash latency for the mcu in the specified voltage range, configures the swo for [logging](https://book.drone-os.com/bluepill-blink/full-speed.html). Lastly we are ready to select the PLL's `PLL_P` output as the source for the sysclk, effectively setting the desired 180MHz mcu speed.\n\n## GPIO\nThe `gpio` feature includes a set of types that makes it easy and safe to configure ports and their respective pins.\nConsider the following example that configures pin `A5` into alternate-function mode, with push/pull type, and for high speed operation.\n\n```rust\nuse drone_stm32f4_hal::gpio::{prelude::*, GpioHead};\n\nlet gpio_a = GpioHead::with_enabled_clock(periph_gpio_a_head!(reg));\nlet pin_sck = gpio_a.pin(periph_gpio_a5!(reg))\n  .into_alternate()\n  .into_pushpull()\n  .with_speed(GpioPinSpeed::VeryHighSpeed);\n```\nThe `pin_sck` has type `GpioPin\u003cGpioA5, AlternateMode\u003cAf\u003e, PushPullType, NoPull\u003e` where `Af` is any of the alternate function marker types `PinAf0,...,PinAf15`.\nIt is not needed to explicitly specify the alternate function,\nas it is included in the function prototypes for any drivers that uses a pin, and so its type information flows \"backwards\" into the configuration of the pin.\n\nThe clock for `gpio_a` is enabled when it is constructed as the clock is required when configuring the gpio.\nThe clock should be explicitly disabled if so desired. This is unsafe, as special care should be taken if operating with disabled gpio clocks:\n\n```rust\nunsafe {\n  port_a.disable_clock(); // Explicitly disable clock - use with care!\n}\n```\n\n## DMA\nThe `dma` driver is simple, but includes type safety features for other drivers.\nConsider the following lines of code from the [uart example](./examples/uart/src/thr/root.rs):\n\n```rust\nlet dma1 = DmaCfg::with_enabled_clock(periph_dma1!(reg));\nlet rx_dma = dma1.ch(DmaChSetup::new(periph_dma1_ch5!(reg), thr.dma_1_ch_5));\n```\n`rx_dma` has type `DmaChCfg\u003cDma1Ch5, DmaStCh, DmaInt\u003e` where `Dma1Ch5` means \"DMA1 _stream_ 5\".\nST has changed the nomenclature for newer mcu's, and [Drone OS] uses the new naming scheme.\nThe F4 series operate with _streams_ and _channels_.\nWe call the channels within a stream a \"stream channel\" to avoid confusion.\n\n`DmaStCh` may be any of `DmaStCh0,...,DmaStCh7` as there 8 possible stream channels within a stream.\nAs for the alternate function mode in the gpio driver,\nthe actual stream channel is not explicitly specified,\nas this information flows back into the type of `rx_dma` when the variable is actually used.\n\n## EXTI\nThe exti driver is similar to the [smartoris-exti] driver,\nbut with type safety from the gpio pin configuration ensuring that interrupts for a given pin is configured on the correct exti peripheral.\nThere is a working [Drone OS] example application in the [examples folder](./examples/exti/):\n\n\n```rust\nthr.exti_2.enable_int();\n\nlet syscfg = Syscfg::with_enabled_clock(periph_syscfg!(reg));\nlet exti = ExtiDrv::new(periph_exti2!(reg), thr.exti_2, \u0026syscfg).into_rising_edge();\n\nlet gpio = GpioHead::with_enabled_clock(periph_gpio_i_head!(reg));\nlet pin = gpio.pin(periph_gpio_i2!(reg)).into_input().into_pushpull().into_pulldown();\n\nlet line = exti.line(\u0026pin);\n\nlet stream = line.create_saturating_stream();\nexti.listen();\n\nwhile let Some(tick) = stream.next().await {\n  // Rising edge was triggered\n}\n```\n\nThe exti interrupt is first enabled in the nvic. The exti driver is then initialized, after which the pin is configured as input.\nThe exti driver provides the `line()` function returning an `ExtiLine` struct, from which one can create a stream of events.\nWe start to listen when the stream is ready to consume the interrupts.\n\n## SPI\nThe spi driver provides future based spi transfers using dma.\nThere is a working [Drone OS] example application in the [examples folder](./examples/spi/).\nThe driver is initially setup like the following:\n\n```rust\nthr.spi_1.enable_int();\nthr.dma_2_ch_2.enable_int();\nthr.dma_2_ch_3.enable_int();\n\nlet gpio_a = GpioHead::with_enabled_clock(periph_gpio_a_head!(reg));\nlet pin_sck = gpio_a.pin(periph_gpio_a5!(reg))\n  .into_alternate()\n  .into_pushpull()\n  .with_speed(GpioPinSpeed::VeryHighSpeed);\nlet pin_miso = gpio_a.pin(periph_gpio_a6!(reg))\n  .into_alternate()\n  .into_pushpull()\n  .with_speed(GpioPinSpeed::VeryHighSpeed);\nlet pin_mosi = gpio_a.pin(periph_gpio_a7!(reg))\n  .into_alternate()\n  .into_pushpull()\n  .with_speed(GpioPinSpeed::VeryHighSpeed);\n\nlet dma2 = DmaCfg::with_enabled_clock(periph_dma2!(reg));\nlet miso_dma = dma2.ch(DmaChSetup::new(periph_dma2_ch2!(reg), thr.dma_2_ch_2));\nlet mosi_dma = dma2.ch(DmaChSetup::new(periph_dma2_ch3!(reg), thr.dma_2_ch_3));\n\nlet pins = SpiPins::default().sck(pin_sck).miso(pin_miso).mosi(pin_mosi);\nlet setup = SpiSetup::new(\n    periph_spi1!(reg),\n    thr.spi_1,\n    pins,\n    pclk2,\n    BaudRate::Max(7_700_000),\n);\nlet spi = SpiDrv::init(setup).into_master(miso_dma, mosi_dma);\n```\n\nThe spi interrupt is first enabled in the nvic before the spi pins are configured.\nThe base driver does not handle chip selection, and it is up to the user to correctly select the desired chip before transferring on the spi bus (see below).\nThe dma and its channels matching the used spi peripheral are then configured.\nA `SpiSetup` structure containing all the parameters for the driver are created with the spi pins to verify that the pins actually map to the spi peripheral. The peripheral clock is also specified together witht the maximum allowed baud rate.\n\n### Chip Selection\nChip selection is not an integrated part of the spi driver, but a small chip controller shim is included in the driver.\nIt handles simple select/deselect like the following:\n\n```rust\nuse drone_stm32f4_hal::spi::chipctrl::*;\n\nlet pin_cs = gpio_b.pin(periph_gpio_b7!(reg))\n  .into_output()\n  .with_speed(GpioPinSpeed::HighSpeed);\n\nlet mut chip = SpiChip::new_deselected(pin_cs);\nlet selection = spi_master.select(\u0026mut chip);\n// Do some communication...\ndrop(selection); // drop() deselects chip.\n```\n\nIt extends the spi master driver with the `select()` method which returns a guard that deselects the chip when dropped.\n\n### Communication\n\nThe communication can be done using the three methods `write()`, `read()`, and `xfer()`:\n\n```rust\nlet selection = spi_master.select(\u0026mut chip);\nlet tx_buf = [1, 2, 3, 4].as_ref();\nlet mut rx_buf = [0;4];\nspi_master.write(tx_buf).root_wait();\nspi_master.read(\u0026mut rx_buf).root_wait();\nspi_master.xfer(tx_buf, \u0026mut rx_buf).root_wait();\ndrop(selection); // drop() deselects chip.\n```\n\nThe `write()` function simply writes the buffer and discards all bytes received during the write,\nthe `read()` method emits `0` on the spi bus to \"clock out\" the selected chip. The received bytes are written to the provided buffer.\n`xfer()` performs a full duplex transfer (the two buffer slices must have the same size). \n\n\n## UART\nThe uart driver uses any of the stm32 uart periperals together with their corresponding dma rx/tx streams to achieve asynchronous read and write operations with minimal cpu overhead.\n\nThere is a working [Drone OS] example application in the [examples folder](./examples/uart/).\nThe driver is initially setup like the following:\n\n```rust\nthr.usart_2.enable_int();\nthr.dma_1_ch_5.enable_int();\nthr.dma_1_ch_6.enable_int();\n\nlet gpio_a = GpioHead::with_enabled_clock(periph_gpio_a_head!(reg));\nlet pin_tx = gpio_a.pin(periph_gpio_a2!(reg))\n  .into_alternate()\n  .into_pushpull()\n  .with_speed(GpioPinSpeed::VeryHighSpeed);\nlet pin_rx = GpioPin::from(periph_gpio_a3!(reg))\n  .into_alternate()\n  .into_pushpull()\n  .with_speed(GpioPinSpeed::VeryHighSpeed);\n\nlet dma1 = DmaCfg::with_enabled_clock(periph_dma1!(reg));\nlet rx_dma = dma1.ch(DmaChSetup::new(periph_dma1_ch5!(reg), thr.dma_1_ch_5));\nlet tx_dma = dma1.ch(DmaChSetup::new(periph_dma1_ch6!(reg), thr.dma_1_ch_6));\n\nlet uart_pins = UartPins::default().tx(pin_tx).rx(pin_rx);\n\nlet setup = UartSetup::init(periph_usart2!(reg), thr.usart_2, pclk1);\nlet uart_drv = UartDrv::init(setup);\n```\n\nThe usart interrupt is first enabled in the nvic.\nThe rx/tx pins are then configured together with the dma's.\nA `uart_pins` variable is created that is used later when initializing the rx/tx operation.\nIt should be noted that it is the assigned `uart_pins` that ultimately decides the alternate function of the two pins.\nThe usart peripheral, the usart thread, and the configured periheral clock to the `UartSetup` initialization function, creating a uart `setup` structure. The default setup is `9600/8N1`.\nAny other setting can be specified on the public properties of `setup` (the `setup` variable must be declared `mut` in this case).\nThe setup is finally passed to the driver initialization function.\n\nThe rx and tx operation of the driver are completely separated, and each of them needs further initiation before use.\n\n### TX Operation\nCompleting the setup for tx operation looks like this:\n```rust\nlet tx_setup = DmaChSetup::init(periph_dma1_ch6!(reg), thr.dma_1_ch_6);\nlet mut tx_drv = uart_drv.into_tx(tx_dma, \u0026uart_pins);\n```\n\nWith `tx_drv` we are now finally able to do some communication.\nThe uart is not started at this time.\nFor this we must invoke the `start()` function which returns a guard that stops the transmitter when dropped.\nWe can start writing to the uart after it is started:\n\n```rust\nlet mut tx = tx_drv.start();\ntx.write(b\"Hello World!\\n\").await;\ntx.write(b\"Drone OS is awesome!\\n\").await;\ntx.flush().await;\ndrop(tx);\n```\n\nThe driver supports multiple subsequent calls to the `write()` function which is an asynchronous call that returns a future that completes _when the dma controller is ready to receive more bytes to be written_.\nIn this way it is possible to fully saturate the uart even for fast baud rates, without any \"spacings\" due to a software design with multiple `write()` calls.\nIn the example this have the effect that the `\\n` character after the Hello World! write is completely adjacent to the `D` when expecting the uart tx line.\nAs a consequence of this design: When `write()` completes this does not mean that the data have actually been sent.\nFor this we use `flush()` which, when returned tells that all data are completely transmitted at which time it is safe to stop the uart.\n\n### RX Operation\nThe rx part of the driver is initialized like the following:\n\n```rust\nlet rx_setup = DmaChSetup::init(periph_dma1_ch5!(reg), thr.dma_1_ch_5);\nlet mut rx_drv = uart_drv.into_rx(rx_setup, \u0026uart_pins);\n```\n\nAgain, as for the tx part, the driver is not yet ready to receive. For this we need to start the receiver:\n\n```rust\nlet rx_ring_buf = vec![0; 256].into_boxed_slice();\nlet mut rx = rx_drv.start(rx_ring_buf);\n\nloop {\n  let mut buf = [0; 16];\n  match rx.read(\u0026mut buf).await {\n    Ok(n) =\u003e {\n      // Data is available in the slice \u0026buf[..n].\n    }\n    Err(e) =\u003e {\n      // The ring buffer has overflowed.\n    }\n  };\n}\n\ndrop(rx);\n```\n\nThe driver is started with the ring buffer `rx_ring_buf`. This buffer is internally assigned to the dma controller, and must be sufficiently large to store received bytes before they are consumed using the `read()` function by the user. `read()` returns a future with the number of bytes read. This number may be smaller than the size of the provided read buffer. It returns immediately if there is _any_ data readily available in the ring buffer, or in the future _as soon as there is data available_. More specifically for `n = read(\u0026buf).await?`, we have `1 \u003c= n \u003c= len(buf)`.\nThis is not using busy waiting on data to become available, but is achieved internally by registering a [Drone OS] [fiber](https://book.drone-os.com/fibers.html) that completes when any data becomes available in the dma controller and therefore the ring buffer.\n\nThe `read()` method may return an error if `read()` is not called fast enough, in which case it can happen that the ring buffer has overflowed since the last call to `read()`.\n\n### TX and RX Operation\nThe two previous examples have shown tx-only and rx-only operation. One can split the driver into both a tx and rx driver as follows:\n\n```rust\nlet tx_setup = DmaChSetup::init(periph_dma1_ch6!(reg), thr.dma_1_ch_6);\nlet rx_setup = DmaChSetup::init(periph_dma1_ch5!(reg), thr.dma_1_ch_5);\nlet (mut tx_drv, mut rx_drv) = uart_drv.into_trx(tx_setup, rx_setup, \u0026uart_pins);\n```\n\n## FMC\nThe `fmc` feature provides an sdram driver. The driver ensures that all required pins are configured correctly, after which one can obtain a `\u0026mut [T]` slice of the address mapped memory.\nThere is an [example app](./examples/fmc/src/tasks/root.rs) where the majority of the configuration is setting up pins into the alternate function mode.\nAfter that, the initialization and use of the driver is straight forward:\n\n```rust\nlet sdram_pins = FmcSdRamPins::default()\n  .sdclk(gpio_g.pin(periph_gpio_g8!(reg)).into_alternate().with_speed(GpioPinSpeed::HighSpeed))\n  ...;\nlet address_pins = FmcSdRamAddressPins::default()\n  .a0(gpio_f.pin(periph_gpio_f0!(reg)).into_alternate().with_speed(GpioPinSpeed::HighSpeed))\n  ...;\nlet data_pins = FmcSdRamDataPins::default()\n  .d0(gpio_d.pin(periph_gpio_d14!(reg)).into_alternate().with_speed(GpioPinSpeed::HighSpeed))\n  ...;\nlet bank_pins = FmcSdRamBankPins::default()\n  .ba0(gpio_g.pin(periph_gpio_g4!(reg)).into_alternate().with_speed(GpioPinSpeed::HighSpeed))\n  ...;\nlet mask_pins = FmcSdRamByteMaskPins::default()\n  .nbl0(gpio_e.pin(periph_gpio_e0!(reg)).into_alternate().with_speed(GpioPinSpeed::HighSpeed))\n  ...;\n\nlet fmc = FmcDrv::init_sdram(SdRamSetup::new_bank2(periph_fmc!(reg), consts::SDRAM_CFG, hclk), sdram_pins, address_pins, data_pins, bank_pins, mask_pins);\nlet ram = fmc.bank2_slice::\u003cusize\u003e();\n\nfor i in 0..ram.len() {\n    ram[i] = i;\n}\n\nfor i in 0..ram.len() {\n    assert_eq!(i, ram[i], \"SDRAM sanity check error!\");\n}\n```\nThe driver ensures that the correct number of pins is mapped corresponding to the sdram `consts::SDRAM_CFG` [configuration parameters](./examples/fmc/src/consts.rs),\nand that they are set into alternate function mode.\n\n## Supported Devices\n\n| stm32_mcu |\n|-----------|\n| stm32f401 |\n| stm32f405 |\n| stm32f407 |\n| stm32f410 |\n| stm32f411 |\n| stm32f412 |\n| stm32f413 |\n| stm32f427 |\n| stm32f429 |\n| stm32f446 |\n| stm32f469 |\n\n## References\n\n* [STM32F429 PM0090 reference manual](https://www.st.com/resource/en/reference_manual/dm00031020.pdf)\n* [stm32f4-hal](https://github.com/stm32-rs/stm32f4xx-hal)\n* [smartoris-exti]\n\n[Drone OS]: https://www.drone-os.com/\n[smartoris-exti]: https://github.com/smartoris/smartoris-exti\n\n## License\n\nLicensed under either of\n\n * Apache License, Version 2.0\n   ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n * MIT license\n   ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\n## Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frmja%2Fdrone-stm32f4-hal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frmja%2Fdrone-stm32f4-hal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frmja%2Fdrone-stm32f4-hal/lists"}