diff options
| author | jrmoulton <[email protected]> | 2025-06-10 15:47:54 -0600 |
|---|---|---|
| committer | jrmoulton <[email protected]> | 2025-06-10 15:48:36 -0600 |
| commit | cfad9798ff99d4de0571a512d156b5fe1ef1d427 (patch) | |
| tree | fc3bf670f82d139de19466cddad1e909db7f3d2e /embassy-stm32 | |
| parent | fc342915e6155dec7bafa3e135da7f37a9a07f5c (diff) | |
| parent | 6186d111a5c150946ee5b7e9e68d987a38c1a463 (diff) | |
merge new embassy changes
Diffstat (limited to 'embassy-stm32')
144 files changed, 18198 insertions, 6602 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md new file mode 100644 index 000000000..b6781905e --- /dev/null +++ b/embassy-stm32/CHANGELOG.md | |||
| @@ -0,0 +1,279 @@ | |||
| 1 | # Changelog for embassy-stm32 | ||
| 2 | |||
| 3 | All notable changes to this project will be documented in this file. | ||
| 4 | |||
| 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
| 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
| 7 | |||
| 8 | ## Unreleased | ||
| 9 | - Modify BufferedUart initialization to take pins before interrupts ([#3983](https://github.com/embassy-rs/embassy/pull/3983)) | ||
| 10 | - Added a 'single-bank' and a 'dual-bank' feature so chips with configurable flash bank setups are be supported in embassy ([#4125](https://github.com/embassy-rs/embassy/pull/4125)) | ||
| 11 | |||
| 12 | ## 0.2.0 - 2025-01-10 | ||
| 13 | |||
| 14 | Starting 2025 strong with a release packed with new, exciting good stuff! 🚀 | ||
| 15 | |||
| 16 | ### New chips | ||
| 17 | |||
| 18 | This release adds support for many newly-released STM32 chips. | ||
| 19 | |||
| 20 | - STM32H7[RS] "bootflash line" ([#2898](https://github.com/embassy-rs/embassy/pull/2898)) | ||
| 21 | - STM32U0 ([#2809](https://github.com/embassy-rs/embassy/pull/2809) [#2813](https://github.com/embassy-rs/embassy/pull/2813)) | ||
| 22 | - STM32H5[23] ([#2892](https://github.com/embassy-rs/embassy/pull/2892)) | ||
| 23 | - STM32U5[FG] ([#2892](https://github.com/embassy-rs/embassy/pull/2892)) | ||
| 24 | - STM32WBA5[045] ([#2892](https://github.com/embassy-rs/embassy/pull/2892)) | ||
| 25 | |||
| 26 | ### Simpler APIs with less generics | ||
| 27 | |||
| 28 | Many HAL APIs have been simplified thanks to reducing the amount of generic parameters. This helps with creating arrays of pins or peripherals, and for calling the same code with different pins/peripherals without incurring in code size penalties d | ||
| 29 | |||
| 30 | For GPIO, the pins have been eliminated. `Output<'_, PA4>` is now `Output<'_>`. | ||
| 31 | |||
| 32 | For peripherals, both pins and DMA channels have been eliminated. Peripherals now have a "mode" generic param that specifies whether it's capable of async operation. For example, `I2c<'_, I2C2, NoDma, NoDma>` is now `I2c<'_, Blocking>` and `I2c<'_, I2C2, DMA2_CH1, DMA2_CH2>` is now `I2c<'_, Async>`. | ||
| 33 | |||
| 34 | - Removed DMA channel generic params for UART ([#2821](https://github.com/embassy-rs/embassy/pull/2821)), I2C ([#2820](https://github.com/embassy-rs/embassy/pull/2820)), SPI ([#2819](https://github.com/embassy-rs/embassy/pull/2819)), QSPI ([#2982](https://github.com/embassy-rs/embassy/pull/2982)), OSPI ([#2941](https://github.com/embassy-rs/embassy/pull/2941)). | ||
| 35 | - Removed peripheral generic params for GPIO ([#2471](https://github.com/embassy-rs/embassy/pull/2471)), UART ([#2836](https://github.com/embassy-rs/embassy/pull/2836)), I2C ([#2974](https://github.com/embassy-rs/embassy/pull/2974)), SPI ([#2835](https://github.com/embassy-rs/embassy/pull/2835)) | ||
| 36 | - Remove generics in CAN ([#3012](https://github.com/embassy-rs/embassy/pull/3012), [#3020](https://github.com/embassy-rs/embassy/pull/3020), [#3032](https://github.com/embassy-rs/embassy/pull/3032), [#3033](https://github.com/embassy-rs/embassy/pull/3033)) | ||
| 37 | |||
| 38 | ### More complete and consistent RCC | ||
| 39 | |||
| 40 | RCC support has been vastly expanded and improved. | ||
| 41 | - The API is now consistent across all STM32 families. Previously in some families you'd configure the desired target frequencies for `sysclk` and the buses and `embassy-stm32` would try to calculate dividers and muxes to hit them as close as possible. This has proved to be intractable in the general case and hard to extend to more exotic RCC configurations. So, we have standardized on an API where the user specifies the settings for dividers and muxes directly. It's lower level but gices more control to the user, supports all edge case exotic configurations, and makes it easier to translate a configuration from the STM32CubeMX tool. ([Tracking issue](https://github.com/embassy-rs/embassy/issues/2515). [#2624](https://github.com/embassy-rs/embassy/pull/2624). F0, F1 [#2564](https://github.com/embassy-rs/embassy/pull/2564), F3 [#2560](https://github.com/embassy-rs/embassy/pull/2560), U5 [#2617](https://github.com/embassy-rs/embassy/pull/2617), [#3514](https://github.com/embassy-rs/embassy/pull/3514), [#3513](https://github.com/embassy-rs/embassy/pull/3513), G4 [#2579](https://github.com/embassy-rs/embassy/pull/2579), [#2618](https://github.com/embassy-rs/embassy/pull/2618), WBA [#2520](https://github.com/embassy-rs/embassy/pull/2520), G0, C0 ([#2656](https://github.com/embassy-rs/embassy/pull/2656)). | ||
| 42 | - Added support for configuring all per-peripheral clock muxes (CCIPRx, DCKCFGRx registers) in `config.rcc.mux`. This was previously handled in an ad-hoc way in some drivers (e.g. USB) and not at all in others (causing e.g. wrong SPI frequency) ([#2521](https://github.com/embassy-rs/embassy/pull/2521), [#2583](https://github.com/embassy-rs/embassy/pull/2583), [#2634](https://github.com/embassy-rs/embassy/pull/2634), [#2626](https://github.com/embassy-rs/embassy/pull/2626), [#2815](https://github.com/embassy-rs/embassy/pull/2815), [#2517](https://github.com/embassy-rs/embassy/pull/2517)). | ||
| 43 | - Switch to a safe configuration before configuring RCC. This helps avoid crashes when RCC has been already configured previously (for example by a bootloader). (F2, F4, F7 [#2829](https://github.com/embassy-rs/embassy/pull/2829), C0, F0, F1, F3, G0, G4, H5, H7[#3008](https://github.com/embassy-rs/embassy/pull/3008)) | ||
| 44 | - Some new nice features: | ||
| 45 | - Expose RCC enable and disable in public API. ([#2807](https://github.com/embassy-rs/embassy/pull/2807)) | ||
| 46 | - Add `unchecked-overclocking` feature that disables all asserts, allowing running RCC out of spec. ([#3574](https://github.com/embassy-rs/embassy/pull/3574)) | ||
| 47 | - Many fixes: | ||
| 48 | - Workaround H5 errata that accidentally clears RAM on backup domain reset. ([#2616](https://github.com/embassy-rs/embassy/pull/2616)) | ||
| 49 | - Reset RTC on L0 ([#2597](https://github.com/embassy-rs/embassy/pull/2597)) | ||
| 50 | - Fix H7 to use correct unit in vco clock check ([#2537](https://github.com/embassy-rs/embassy/pull/2537)) | ||
| 51 | - Fix incorrect D1CPRE max for STM32H7 RM0468 ([#2518](https://github.com/embassy-rs/embassy/pull/2518)) | ||
| 52 | - WBA's high speed external clock has to run at 32 MHz ([#2511](https://github.com/embassy-rs/embassy/pull/2511)) | ||
| 53 | - Take into account clock propagation delay to peripherals after enabling a clock. ([#2677](https://github.com/embassy-rs/embassy/pull/2677)) | ||
| 54 | - Fix crash caused by using higher MSI range as sysclk on STM32WL ([#2786](https://github.com/embassy-rs/embassy/pull/2786)) | ||
| 55 | - fix using HSI48 as SYSCLK on F0 devices with CRS ([#3652](https://github.com/embassy-rs/embassy/pull/3652)) | ||
| 56 | - compute LSE and LSI frequency for STM32L and STM32U0 series ([#3554](https://github.com/embassy-rs/embassy/pull/3554)) | ||
| 57 | - Add support for LSESYS, used to pass LSE clock to peripherals ([#3518](https://github.com/embassy-rs/embassy/pull/3518)) | ||
| 58 | - H5: LSE low drive mode is not functional ([#2738](https://github.com/embassy-rs/embassy/pull/2738)) | ||
| 59 | |||
| 60 | ### New peripheral drivers | ||
| 61 | |||
| 62 | - Dual-core support. First core initializes RCC and writes a struct into shared memory that the second core uses, ensuring no conflicts. ([#3158](https://github.com/embassy-rs/embassy/pull/3158), [#3263](https://github.com/embassy-rs/embassy/pull/3263), [#3687](https://github.com/embassy-rs/embassy/pull/3687)) | ||
| 63 | - USB Type-C/USB Power Delivery Interface (UCPD) ([#2652](https://github.com/embassy-rs/embassy/pull/2652), [#2683](https://github.com/embassy-rs/embassy/pull/2683), [#2701](https://github.com/embassy-rs/embassy/pull/2701), [#2925](https://github.com/embassy-rs/embassy/pull/2925), [#3084](https://github.com/embassy-rs/embassy/pull/3084), [#3271](https://github.com/embassy-rs/embassy/pull/3271), [#3678](https://github.com/embassy-rs/embassy/pull/3678), [#3714](https://github.com/embassy-rs/embassy/pull/3714)) | ||
| 64 | - Touch sensing controller (TSC) ([#2853](https://github.com/embassy-rs/embassy/pull/2853), [#3111](https://github.com/embassy-rs/embassy/pull/3111), [#3163](https://github.com/embassy-rs/embassy/pull/3163), [#3274](https://github.com/embassy-rs/embassy/pull/3274)) | ||
| 65 | - Display Serial Interface (DSI) [#2903](https://github.com/embassy-rs/embassy/pull/2903), ([#3082](https://github.com/embassy-rs/embassy/pull/3082)) | ||
| 66 | - LCD/TFT Display Controller (LTDC) ([#3126](https://github.com/embassy-rs/embassy/pull/3126), [#3458](https://github.com/embassy-rs/embassy/pull/3458)) | ||
| 67 | - SPDIF receiver (SPDIFRX) ([#3280](https://github.com/embassy-rs/embassy/pull/3280)) | ||
| 68 | - CORDIC math accelerator ([#2697](https://github.com/embassy-rs/embassy/pull/2697)) | ||
| 69 | - Digital Temperature Sensor (DTS) ([#3717](https://github.com/embassy-rs/embassy/pull/3717)) | ||
| 70 | - HMAC accelerator ([#2565](https://github.com/embassy-rs/embassy/pull/2565)) | ||
| 71 | - Hash accelerator ([#2528](https://github.com/embassy-rs/embassy/pull/2528)) | ||
| 72 | - Crypto accelerator ([#2619](https://github.com/embassy-rs/embassy/pull/2619), [#2691](https://github.com/embassy-rs/embassy/pull/2691)) | ||
| 73 | - Semaphore (HSEM) ([#2777](https://github.com/embassy-rs/embassy/pull/2777), [#3161](https://github.com/embassy-rs/embassy/pull/3161)) | ||
| 74 | |||
| 75 | ### Improvements to existing drivers | ||
| 76 | |||
| 77 | GPIO: | ||
| 78 | - Generate singletons only for pins that actually exist. ([#3738](https://github.com/embassy-rs/embassy/pull/3738)) | ||
| 79 | - Add `set_as_analog` to Flex ([#3017](https://github.com/embassy-rs/embassy/pull/3017)) | ||
| 80 | - Add `embedded-hal` v0.2 `InputPin` impls for `OutputOpenDrain`. ([#2716](https://github.com/embassy-rs/embassy/pull/2716)) | ||
| 81 | - Add a config option to make the VDDIO2 supply line valid ([#2737](https://github.com/embassy-rs/embassy/pull/2737)) | ||
| 82 | - Refactor AfType ([#3031](https://github.com/embassy-rs/embassy/pull/3031)) | ||
| 83 | - Gpiov1: Do not call set_speed for AFType::Input ([#2996](https://github.com/embassy-rs/embassy/pull/2996)) | ||
| 84 | |||
| 85 | UART: | ||
| 86 | - Add embedded-io impls ([#2739](https://github.com/embassy-rs/embassy/pull/2739)) | ||
| 87 | - Add support for changing baud rate ([#3512](https://github.com/embassy-rs/embassy/pull/3512)) | ||
| 88 | - Add split_ref ([#3500](https://github.com/embassy-rs/embassy/pull/3500)) | ||
| 89 | - Add data bit selection ([#3595](https://github.com/embassy-rs/embassy/pull/3595)) | ||
| 90 | - Add RX Pull configuration option ([#3415](https://github.com/embassy-rs/embassy/pull/3415)) | ||
| 91 | - Add async flush ([#3379](https://github.com/embassy-rs/embassy/pull/3379)) | ||
| 92 | - Add support for sending breaks ([#3286](https://github.com/embassy-rs/embassy/pull/3286)) | ||
| 93 | - Disconnect pins on drop ([#3006](https://github.com/embassy-rs/embassy/pull/3006)) | ||
| 94 | - Half-duplex improvements | ||
| 95 | - Add half-duplex for all USART versions ([#2833](https://github.com/embassy-rs/embassy/pull/2833)) | ||
| 96 | - configurable readback for half-duplex. ([#3679](https://github.com/embassy-rs/embassy/pull/3679)) | ||
| 97 | - Convert uart half_duplex to use user configurable IO ([#3233](https://github.com/embassy-rs/embassy/pull/3233)) | ||
| 98 | - Fix uart::flush with FIFO at Half-Duplex mode ([#2895](https://github.com/embassy-rs/embassy/pull/2895)) | ||
| 99 | - Fix Half-Duplex sequential reads and writes ([#3089](https://github.com/embassy-rs/embassy/pull/3089)) | ||
| 100 | - disable transmitter during during half-duplex flush ([#3299](https://github.com/embassy-rs/embassy/pull/3299)) | ||
| 101 | - Buffered UART improvements | ||
| 102 | - Add embedded-io ReadReady impls ([#3179](https://github.com/embassy-rs/embassy/pull/3179), [#3451](https://github.com/embassy-rs/embassy/pull/3451)) | ||
| 103 | - Add constructors for RS485 ([#3441](https://github.com/embassy-rs/embassy/pull/3441)) | ||
| 104 | - Fix RingBufferedUartRx hard-resetting DMA after initial error ([#3356](https://github.com/embassy-rs/embassy/pull/3356)) | ||
| 105 | - Don't teardown during reconfigure ([#2989](https://github.com/embassy-rs/embassy/pull/2989)) | ||
| 106 | - Wake receive task for each received byte ([#2722](https://github.com/embassy-rs/embassy/pull/2722)) | ||
| 107 | - Fix dma and idle line detection in ringbuffereduartrx ([#3319](https://github.com/embassy-rs/embassy/pull/3319)) | ||
| 108 | |||
| 109 | SPI: | ||
| 110 | - Add MISO pullup configuration option ([#2943](https://github.com/embassy-rs/embassy/pull/2943)) | ||
| 111 | - Add slew rate configuration options ([#3669](https://github.com/embassy-rs/embassy/pull/3669)) | ||
| 112 | - Fix blocking_write on nosck spi. ([#3035](https://github.com/embassy-rs/embassy/pull/3035)) | ||
| 113 | - Restrict txonly_nosck to SPIv1, it hangs in other versions. ([#3028](https://github.com/embassy-rs/embassy/pull/3028)) | ||
| 114 | - Fix non-u8 word sizes. ([#3363](https://github.com/embassy-rs/embassy/pull/3363)) | ||
| 115 | - Issue correct DMA word length when reading to prevent hang. ([#3362](https://github.com/embassy-rs/embassy/pull/3362)) | ||
| 116 | - Add proper rxonly support for spi_v3 and force tx dma stream requirements. ([#3007](https://github.com/embassy-rs/embassy/pull/3007)) | ||
| 117 | |||
| 118 | I2C: | ||
| 119 | - Implement asynchronous transactions ([#2742](https://github.com/embassy-rs/embassy/pull/2742)) | ||
| 120 | - Implement blocking transactions ([#2713](https://github.com/embassy-rs/embassy/pull/2713)) | ||
| 121 | - Disconnect pins on drop ([#3006](https://github.com/embassy-rs/embassy/pull/3006)) | ||
| 122 | - Ensure bus is free before master-write operation ([#3104](https://github.com/embassy-rs/embassy/pull/3104)) | ||
| 123 | - Add workaround for STM32 i2cv1 errata ([#2887](https://github.com/embassy-rs/embassy/pull/2887)) | ||
| 124 | - Fix disabling pullup accidentally enabling pulldown ([#3410](https://github.com/embassy-rs/embassy/pull/3410)) | ||
| 125 | |||
| 126 | Flash: | ||
| 127 | - Add L5 support ([#3423](https://github.com/embassy-rs/embassy/pull/3423)) | ||
| 128 | - Add H5 support ([#3305](https://github.com/embassy-rs/embassy/pull/3305)) | ||
| 129 | - add F2 support ([#3303](https://github.com/embassy-rs/embassy/pull/3303)) | ||
| 130 | - Add U5 support ([#2591](https://github.com/embassy-rs/embassy/pull/2591), [#2792](https://github.com/embassy-rs/embassy/pull/2792)) | ||
| 131 | - Add H50x support ([#2600](https://github.com/embassy-rs/embassy/pull/2600), [#2808](https://github.com/embassy-rs/embassy/pull/2808)) | ||
| 132 | - Fix flash erase on F3 ([#3744](https://github.com/embassy-rs/embassy/pull/3744)) | ||
| 133 | - Support G0 second flash bank ([#3711](https://github.com/embassy-rs/embassy/pull/3711)) | ||
| 134 | - F1, F3: wait for BSY flag to clear before flashing ([#3217](https://github.com/embassy-rs/embassy/pull/3217)) | ||
| 135 | - H7: enhance resilience to program sequence errors (pgserr) ([#2539](https://github.com/embassy-rs/embassy/pull/2539)) | ||
| 136 | |||
| 137 | ADC: | ||
| 138 | - Add `AnyAdcChannel` type. You can obtain it from a pin with `.degrade_adc()`. Useful for making arrays of ADC pins. ([#2985](https://github.com/embassy-rs/embassy/pull/2985)) | ||
| 139 | - Add L0 support ([#2544](https://github.com/embassy-rs/embassy/pull/2544)) | ||
| 140 | - Add U5 support ([#3688](https://github.com/embassy-rs/embassy/pull/3688)) | ||
| 141 | - Add H5 support ([#2613](https://github.com/embassy-rs/embassy/pull/2613), [#3557](https://github.com/embassy-rs/embassy/pull/3557)) | ||
| 142 | - Add G4 async support ([#3566](https://github.com/embassy-rs/embassy/pull/3566)) | ||
| 143 | - Add G4 support for calibrating differential inputs ([#3735](https://github.com/embassy-rs/embassy/pull/3735)) | ||
| 144 | - Add oversampling and differential support for G4 ([#3169](https://github.com/embassy-rs/embassy/pull/3169)) | ||
| 145 | - Add DMA support for ADC v2 ([#3116](https://github.com/embassy-rs/embassy/pull/3116)) | ||
| 146 | - Add DMA support for ADC v3 and v4 ([#3128](https://github.com/embassy-rs/embassy/pull/3128)) | ||
| 147 | - Unify naming `blocking_read` for blocking, `read` for async. ([#3148](https://github.com/embassy-rs/embassy/pull/3148)) | ||
| 148 | - Fix channel count for the STM32G4 ADCs. ([#2828](https://github.com/embassy-rs/embassy/pull/2828)) | ||
| 149 | - Fix blocking_delay_us() overflowing when sys freq is high ([#2825](https://github.com/embassy-rs/embassy/pull/2825)) | ||
| 150 | - Remove need for taking a `Delay` impl. ([#2797](https://github.com/embassy-rs/embassy/pull/2797)) | ||
| 151 | - H5: set OR.OP0 to 1 when ADCx_INP0 is selected, per RM ([#2776](https://github.com/embassy-rs/embassy/pull/2776)) | ||
| 152 | - Add oversampling support ([#3124](https://github.com/embassy-rs/embassy/pull/3124)) | ||
| 153 | - Adc averaging support for ADC v4. ([#3110](https://github.com/embassy-rs/embassy/pull/3110)) | ||
| 154 | - F2 ADC fixes ([#2513](https://github.com/embassy-rs/embassy/pull/2513)) | ||
| 155 | |||
| 156 | DAC: | ||
| 157 | - Fix new_internal not setting mode as documented ([#2886](https://github.com/embassy-rs/embassy/pull/2886)) | ||
| 158 | |||
| 159 | OPAMP: | ||
| 160 | - Add missing opamp external outputs for STM32G4 ([#3636](https://github.com/embassy-rs/embassy/pull/3636)) | ||
| 161 | - Add extra lifetime to opamp-using structs ([#3207](https://github.com/embassy-rs/embassy/pull/3207)) | ||
| 162 | - Make OpAmp usable in follower configuration for internal DAC channel ([#3021](https://github.com/embassy-rs/embassy/pull/3021)) | ||
| 163 | |||
| 164 | CAN: | ||
| 165 | - Add FDCAN support. ([#2475](https://github.com/embassy-rs/embassy/pull/2475), [#2571](https://github.com/embassy-rs/embassy/pull/2571), [#2623](https://github.com/embassy-rs/embassy/pull/2623), [#2631](https://github.com/embassy-rs/embassy/pull/2631), [#2635](https://github.com/embassy-rs/embassy/pull/2635), [#2637](https://github.com/embassy-rs/embassy/pull/2637), [#2645](https://github.com/embassy-rs/embassy/pull/2645), [#2647](https://github.com/embassy-rs/embassy/pull/2647), [#2658](https://github.com/embassy-rs/embassy/pull/2658), [#2703](https://github.com/embassy-rs/embassy/pull/2703), [#3364](https://github.com/embassy-rs/embassy/pull/3364)) | ||
| 166 | - Simplify BXCAN API, make BXCAN and FDCAN APIs consistent. ([#2760](https://github.com/embassy-rs/embassy/pull/2760), [#2693](https://github.com/embassy-rs/embassy/pull/2693), [#2744](https://github.com/embassy-rs/embassy/pull/2744)) | ||
| 167 | - Add buffered mode support ([#2588](https://github.com/embassy-rs/embassy/pull/2588)) | ||
| 168 | - Add support for modifying the receiver filters from `BufferedCan`, `CanRx`, and `BufferedCanRx` ([#3733](https://github.com/embassy-rs/embassy/pull/3733)) | ||
| 169 | - Add support for optional FIFO scheduling for outgoing frames ([#2988](https://github.com/embassy-rs/embassy/pull/2988)) | ||
| 170 | - fdcan: Properties for common runtime get/set operations ([#2840](https://github.com/embassy-rs/embassy/pull/2840)) | ||
| 171 | - fdcan: implement bus-off recovery ([#2832](https://github.com/embassy-rs/embassy/pull/2832)) | ||
| 172 | - Add BXCAN sleep/wakeup functionality ([#2854](https://github.com/embassy-rs/embassy/pull/2854)) | ||
| 173 | - Fix BXCAN hangs ([#3468](https://github.com/embassy-rs/embassy/pull/3468)) | ||
| 174 | - add RTR flag if it is remote frame ([#3421](https://github.com/embassy-rs/embassy/pull/3421)) | ||
| 175 | - Fix log storm when no CAN is connected ([#3284](https://github.com/embassy-rs/embassy/pull/3284)) | ||
| 176 | - Fix error handling ([#2850](https://github.com/embassy-rs/embassy/pull/2850)) | ||
| 177 | - Give CAN a kick when writing into TX buffer via sender. ([#2646](https://github.com/embassy-rs/embassy/pull/2646)) | ||
| 178 | - Preseve the RTR flag in messages. ([#2745](https://github.com/embassy-rs/embassy/pull/2745)) | ||
| 179 | |||
| 180 | FMC: | ||
| 181 | - Add 13bit address sdram constructors ([#3189](https://github.com/embassy-rs/embassy/pull/3189)) | ||
| 182 | |||
| 183 | xSPI: | ||
| 184 | - Add OCTOSPI support ([#2672](https://github.com/embassy-rs/embassy/pull/2672)) | ||
| 185 | - Add OCTOSPIM support ([#3102](https://github.com/embassy-rs/embassy/pull/3102)) | ||
| 186 | - Add HEXADECASPI support ([#3667](https://github.com/embassy-rs/embassy/pull/3667)) | ||
| 187 | - Add memory mapping support for QSPI ([#3725](https://github.com/embassy-rs/embassy/pull/3725)) | ||
| 188 | - Add memory mapping support for OCTOSPI ([#3456](https://github.com/embassy-rs/embassy/pull/3456)) | ||
| 189 | - Add async support for QSPI ([#3475](https://github.com/embassy-rs/embassy/pull/3475)) | ||
| 190 | - Fix QSPI synchronous read operation hangs when FIFO is not full ([#3724](https://github.com/embassy-rs/embassy/pull/3724)) | ||
| 191 | - Stick to `blocking_*` naming convention for QSPI, OSPI ([#3661](https://github.com/embassy-rs/embassy/pull/3661)) | ||
| 192 | |||
| 193 | SDMMC: | ||
| 194 | - Add `block-device-driver` impl for use with `embedded-fatfs` ([#2607](https://github.com/embassy-rs/embassy/pull/2607)) | ||
| 195 | - Allow cmd block to be passed in for sdmmc dma transfers ([#3188](https://github.com/embassy-rs/embassy/pull/3188)) | ||
| 196 | |||
| 197 | ETH: | ||
| 198 | - Fix reception of multicast packets ([#3488](https://github.com/embassy-rs/embassy/pull/3488), [#3707](https://github.com/embassy-rs/embassy/pull/3707)) | ||
| 199 | - Add support for executing custom SMI commands ([#3355](https://github.com/embassy-rs/embassy/pull/3355)) | ||
| 200 | - Add support for MII interface ([#2465](https://github.com/embassy-rs/embassy/pull/2465)) | ||
| 201 | |||
| 202 | USB: | ||
| 203 | - Assert correct clock on init. ([#2711](https://github.com/embassy-rs/embassy/pull/2711)) | ||
| 204 | - Set PWR_CR2 USV on STM32L4 ([#2605](https://github.com/embassy-rs/embassy/pull/2605)) | ||
| 205 | - USBD driver improvements: | ||
| 206 | - Add ISO endpoint support ([#3314](https://github.com/embassy-rs/embassy/pull/3314)) | ||
| 207 | - Add support for L1. ([#2452](https://github.com/embassy-rs/embassy/pull/2452)) | ||
| 208 | - set USB initialization delay to 1µs ([#3700](https://github.com/embassy-rs/embassy/pull/3700)) | ||
| 209 | - OTG driver improvements: | ||
| 210 | - Add ISO endpoint support ([#3314](https://github.com/embassy-rs/embassy/pull/3314)) | ||
| 211 | - Add support for U595, U5A5 ([#3613](https://github.com/embassy-rs/embassy/pull/3613)) | ||
| 212 | - Add support for STM32H7R/S ([#3337](https://github.com/embassy-rs/embassy/pull/3337)) | ||
| 213 | - Add support for full-speed ULPI mode ([#3281](https://github.com/embassy-rs/embassy/pull/3281)) | ||
| 214 | - Make max EP count configurable ([#2881](https://github.com/embassy-rs/embassy/pull/2881)) | ||
| 215 | - fix corruption in CONTROL OUT transfers in stm32f4. ([#3565](https://github.com/embassy-rs/embassy/pull/3565)) | ||
| 216 | - Extract Synopsys USB OTG driver to a separate crate ([#2871](https://github.com/embassy-rs/embassy/pull/2871)) | ||
| 217 | - Add critical sections to avoid USB OTG corruption Errata ([#2823](https://github.com/embassy-rs/embassy/pull/2823)) | ||
| 218 | - Fix support for OTG_HS in FS mode. ([#2805](https://github.com/embassy-rs/embassy/pull/2805)) | ||
| 219 | |||
| 220 | I2S: | ||
| 221 | - Add SPIv3 support. ([#2992](https://github.com/embassy-rs/embassy/pull/2992)) | ||
| 222 | - Add full-duplex support. ([#2992](https://github.com/embassy-rs/embassy/pull/2992)) | ||
| 223 | - Add I2S ringbuffered DMA support ([#3023](https://github.com/embassy-rs/embassy/pull/3023)) | ||
| 224 | - Fix STM32F4 I2S clock calculations ([#3716](https://github.com/embassy-rs/embassy/pull/3716)) | ||
| 225 | |||
| 226 | SAI: | ||
| 227 | - Add a function that waits for any SAI/ringbuffer write error ([#3545](https://github.com/embassy-rs/embassy/pull/3545)) | ||
| 228 | - Disallow start without an initial write ([#3541](https://github.com/embassy-rs/embassy/pull/3541)) | ||
| 229 | - Flush FIFO on init and disable ([#3538](https://github.com/embassy-rs/embassy/pull/3538)) | ||
| 230 | - Fix MCKDIV for SAI v3/v4 ([#2710](https://github.com/embassy-rs/embassy/pull/2710)) | ||
| 231 | - Pull down clock and data lines in receive mode ([#3326](https://github.com/embassy-rs/embassy/pull/3326)) | ||
| 232 | - Add function to check if SAI is muted ([#3282](https://github.com/embassy-rs/embassy/pull/3282)) | ||
| 233 | |||
| 234 | Low-power support: | ||
| 235 | - Update `embassy-executor` to v0.7. | ||
| 236 | - Add support for U0 ([#3556](https://github.com/embassy-rs/embassy/pull/3556)) | ||
| 237 | - Add support for U5 ([#3496](https://github.com/embassy-rs/embassy/pull/3496)) | ||
| 238 | - Add support for H5 ([#2877](https://github.com/embassy-rs/embassy/pull/2877)) | ||
| 239 | - Add support for L4 ([#3213](https://github.com/embassy-rs/embassy/pull/3213)) | ||
| 240 | - Fix low-power EXTI IRQ handler dropped edges ([#3404](https://github.com/embassy-rs/embassy/pull/3404)) | ||
| 241 | - Fix alarms not triggering in some cases ([#3592](https://github.com/embassy-rs/embassy/pull/3592)) | ||
| 242 | |||
| 243 | Timer: | ||
| 244 | - Add Input Capture high-level driver ([#2912](https://github.com/embassy-rs/embassy/pull/2912)) | ||
| 245 | - Add PWM Input high-level driver ([#3014](https://github.com/embassy-rs/embassy/pull/3014)) | ||
| 246 | - Add support for splitting `SimplePwm` into channels ([#3317](https://github.com/embassy-rs/embassy/pull/3317)) | ||
| 247 | - Fix `SimplePwm` not enabling output pin in some stm32 families ([#2670](https://github.com/embassy-rs/embassy/pull/2670)) | ||
| 248 | - Add LPTIM low-level driver. ([#3310](https://github.com/embassy-rs/embassy/pull/3310)) | ||
| 249 | - Low-level TIM driver improvements: | ||
| 250 | - Simplify traits, convert from trait methods to struct. ([#2728](https://github.com/embassy-rs/embassy/pull/2728)) | ||
| 251 | - Add `low_level::Timer::get_clock_frequency()` ([#2908](https://github.com/embassy-rs/embassy/pull/2908)) | ||
| 252 | - Fix 32bit timer off by one ARR error ([#2876](https://github.com/embassy-rs/embassy/pull/2876)) | ||
| 253 | - Avoid max_compare_value >= u16::MAX ([#3549](https://github.com/embassy-rs/embassy/pull/3549)) | ||
| 254 | |||
| 255 | DMA: | ||
| 256 | - Add `AnyChannel` type. Similar to `AnyPin`, it allows representing any DMA channel at runtime without needing generics. ([#2606](https://github.com/embassy-rs/embassy/pull/2606)) | ||
| 257 | , Add support for BDMA on H7 ([#2606](https://github.com/embassy-rs/embassy/pull/2606)) | ||
| 258 | - Add async `stop()` function to BDMA, DMA ([#2757](https://github.com/embassy-rs/embassy/pull/2757)) | ||
| 259 | - Add configuration option for DMA Request Priority ([#2680](https://github.com/embassy-rs/embassy/pull/2680)) | ||
| 260 | - Rewrite DMA ringbuffers ([#3336](https://github.com/embassy-rs/embassy/pull/3336)) | ||
| 261 | - Enable half transfer IRQ when constructing a ReadableDmaRingBuffer ([#3093](https://github.com/embassy-rs/embassy/pull/3093)) | ||
| 262 | - Right-align `write_immediate()` in ring buffers ([#3588](https://github.com/embassy-rs/embassy/pull/3588)) | ||
| 263 | |||
| 264 | `embassy-time` driver: | ||
| 265 | - Update to `embassy-time` v0.4, `embassy-time-driver` v0.2. ([#3593](https://github.com/embassy-rs/embassy/pull/3593)) | ||
| 266 | - Change preference order of `time-driver-any` to pick less-featureful timers first. ([#2570](https://github.com/embassy-rs/embassy/pull/2570)) | ||
| 267 | - Allow using more TIMx timers for the time driver of TIM1 ([#2570](https://github.com/embassy-rs/embassy/pull/2570), [#2614](https://github.com/embassy-rs/embassy/pull/2614)) | ||
| 268 | - Correctly gate `time` feature of embassy-embedded-hal in embassy-stm32 ([#3359](https://github.com/embassy-rs/embassy/pull/3359)) | ||
| 269 | - adds timer-driver for tim21 and tim22 (on L0) ([#2450](https://github.com/embassy-rs/embassy/pull/2450)) | ||
| 270 | |||
| 271 | WDG: | ||
| 272 | - Allow higher PSC value for iwdg_v3 ... ([#2628](https://github.com/embassy-rs/embassy/pull/2628)) | ||
| 273 | |||
| 274 | Misc: | ||
| 275 | - Allow `bind_interrupts!` to accept conditional compilation attrs ([#3444](https://github.com/embassy-rs/embassy/pull/3444)) | ||
| 276 | |||
| 277 | ## 0.1.0 - 2024-01-12 | ||
| 278 | |||
| 279 | First release. | ||
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ac9e9644c..034f51df9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "embassy-stm32" | 2 | name = "embassy-stm32" |
| 3 | version = "0.1.0" | 3 | version = "0.2.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | 5 | license = "MIT OR Apache-2.0" |
| 6 | description = "Embassy Hardware Abstraction Layer (HAL) for ST STM32 series microcontrollers" | 6 | description = "Embassy Hardware Abstraction Layer (HAL) for ST STM32 series microcontrollers" |
| @@ -19,16 +19,22 @@ flavors = [ | |||
| 19 | { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, | 19 | { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, |
| 20 | { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, | 20 | { regex_feature = "stm32f2.*", target = "thumbv7m-none-eabi" }, |
| 21 | { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, | 21 | { regex_feature = "stm32f3.*", target = "thumbv7em-none-eabi" }, |
| 22 | { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, | 22 | { regex_feature = "stm32f4[2367]..[ig]", target = "thumbv7em-none-eabi", features = ["low-power", "dual-bank"] }, |
| 23 | { regex_feature = "stm32f4.*", target = "thumbv7em-none-eabi", features = ["low-power", "single-bank"] }, | ||
| 24 | { regex_feature = "stm32f7[67]..[ig]", target = "thumbv7em-none-eabi", features = ["dual-bank"] }, | ||
| 23 | { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, | 25 | { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, |
| 24 | { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, | 26 | { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, |
| 27 | { regex_feature = "stm32g0...c", target = "thumbv6m-none-eabi", features = ["dual-bank"] }, | ||
| 25 | { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, | 28 | { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, |
| 29 | { regex_feature = "stm32g4[78].*", target = "thumbv7em-none-eabi", features = ["low-power", "dual-bank"] }, | ||
| 26 | { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, | 30 | { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] }, |
| 27 | { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] }, | 31 | { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] }, |
| 28 | { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, | 32 | { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, |
| 29 | { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] }, | 33 | { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] }, |
| 30 | { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, | 34 | { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, |
| 35 | { regex_feature = "stm32l4[pqrs].*", target = "thumbv7em-none-eabi", features = ["dual-bank"] }, | ||
| 31 | { regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" }, | 36 | { regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" }, |
| 37 | { regex_feature = "stm32l5...e", target = "thumbv8m.main-none-eabihf", features = ["low-power", "dual-bank"] }, | ||
| 32 | { regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] }, | 38 | { regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] }, |
| 33 | { regex_feature = "stm32u0.*", target = "thumbv6m-none-eabi" }, | 39 | { regex_feature = "stm32u0.*", target = "thumbv6m-none-eabi" }, |
| 34 | { regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" }, | 40 | { regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" }, |
| @@ -38,20 +44,21 @@ flavors = [ | |||
| 38 | ] | 44 | ] |
| 39 | 45 | ||
| 40 | [package.metadata.docs.rs] | 46 | [package.metadata.docs.rs] |
| 41 | features = ["defmt", "unstable-pac", "exti", "time-driver-any", "time", "stm32h755zi-cm7"] | 47 | features = ["defmt", "unstable-pac", "exti", "time-driver-any", "time", "stm32h755zi-cm7", "single-bank"] |
| 42 | rustdoc-args = ["--cfg", "docsrs"] | 48 | rustdoc-args = ["--cfg", "docsrs"] |
| 43 | 49 | ||
| 44 | [dependencies] | 50 | [dependencies] |
| 45 | embassy-sync = { version = "0.6.0", path = "../embassy-sync" } | 51 | embassy-sync = { version = "0.7.0", path = "../embassy-sync" } |
| 46 | embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } | 52 | embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true } |
| 47 | embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } | 53 | embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } |
| 54 | embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true } | ||
| 48 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } | 55 | embassy-futures = { version = "0.1.0", path = "../embassy-futures" } |
| 49 | embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } | 56 | embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } |
| 50 | embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" } | 57 | embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false } |
| 51 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } | 58 | embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } |
| 52 | embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } | 59 | embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } |
| 53 | embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } | 60 | embassy-usb-synopsys-otg = { version = "0.2.0", path = "../embassy-usb-synopsys-otg" } |
| 54 | embassy-executor = { version = "0.6.0", path = "../embassy-executor", optional = true } | 61 | embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } |
| 55 | 62 | ||
| 56 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 63 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 57 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 64 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| @@ -62,17 +69,19 @@ embedded-can = "0.4" | |||
| 62 | embedded-storage = "0.3.1" | 69 | embedded-storage = "0.3.1" |
| 63 | embedded-storage-async = { version = "0.4.1" } | 70 | embedded-storage-async = { version = "0.4.1" } |
| 64 | 71 | ||
| 72 | rand-core-06 = { package = "rand_core", version = "0.6" } | ||
| 73 | rand-core-09 = { package = "rand_core", version = "0.9" } | ||
| 65 | 74 | ||
| 66 | defmt = { version = "0.3", optional = true } | 75 | |
| 76 | defmt = { version = "1.0.1", optional = true } | ||
| 67 | log = { version = "0.4.14", optional = true } | 77 | log = { version = "0.4.14", optional = true } |
| 68 | cortex-m-rt = ">=0.6.15,<0.8" | 78 | cortex-m-rt = ">=0.6.15,<0.8" |
| 69 | cortex-m = "0.7.6" | 79 | cortex-m = "0.7.6" |
| 70 | futures-util = { version = "0.3.30", default-features = false } | 80 | futures-util = { version = "0.3.30", default-features = false } |
| 71 | rand_core = "0.6.3" | 81 | sdio-host = "0.9.0" |
| 72 | sdio-host = "0.5.0" | ||
| 73 | critical-section = "1.1" | 82 | critical-section = "1.1" |
| 74 | #stm32-metapac = { version = "15" } | 83 | #stm32-metapac = { version = "16" } |
| 75 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645" } | 84 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec" } |
| 76 | 85 | ||
| 77 | vcell = "0.1.3" | 86 | vcell = "0.1.3" |
| 78 | nb = "1.0.0" | 87 | nb = "1.0.0" |
| @@ -88,16 +97,20 @@ static_assertions = { version = "1.1" } | |||
| 88 | volatile-register = { version = "0.2.1" } | 97 | volatile-register = { version = "0.2.1" } |
| 89 | bitflags = "2.4.2" | 98 | bitflags = "2.4.2" |
| 90 | 99 | ||
| 100 | block-device-driver = { version = "0.2" } | ||
| 101 | aligned = "0.4.1" | ||
| 91 | 102 | ||
| 92 | [dev-dependencies] | 103 | [dev-dependencies] |
| 93 | critical-section = { version = "1.1", features = ["std"] } | 104 | critical-section = { version = "1.1", features = ["std"] } |
| 105 | proptest = "1.5.0" | ||
| 106 | proptest-state-machine = "0.3.0" | ||
| 94 | 107 | ||
| 95 | [build-dependencies] | 108 | [build-dependencies] |
| 96 | proc-macro2 = "1.0.36" | 109 | proc-macro2 = "1.0.36" |
| 97 | quote = "1.0.15" | 110 | quote = "1.0.15" |
| 98 | 111 | ||
| 99 | #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} | 112 | #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} |
| 100 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e0cfd165fd8fffaa0df66a35eeca83b228496645", default-features = false, features = ["metadata"] } | 113 | stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-27ef8fba3483187e852eaf3796d827259f61e8ec", default-features = false, features = ["metadata"] } |
| 101 | 114 | ||
| 102 | [features] | 115 | [features] |
| 103 | default = ["rt"] | 116 | default = ["rt"] |
| @@ -106,14 +119,24 @@ default = ["rt"] | |||
| 106 | rt = ["stm32-metapac/rt"] | 119 | rt = ["stm32-metapac/rt"] |
| 107 | 120 | ||
| 108 | ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging | 121 | ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging |
| 109 | defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async/defmt-03", "embassy-usb-driver/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] | 122 | defmt = [ |
| 123 | "dep:defmt", | ||
| 124 | "embassy-sync/defmt", | ||
| 125 | "embassy-embedded-hal/defmt", | ||
| 126 | "embassy-hal-internal/defmt", | ||
| 127 | "embedded-io-async/defmt-03", | ||
| 128 | "embassy-usb-driver/defmt", | ||
| 129 | "embassy-net-driver/defmt", | ||
| 130 | "embassy-time?/defmt", | ||
| 131 | "embassy-usb-synopsys-otg/defmt", | ||
| 132 | ] | ||
| 110 | 133 | ||
| 111 | exti = [] | 134 | exti = [] |
| 112 | low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] | 135 | low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] |
| 113 | low-power-debug-with-sleep = [] | 136 | low-power-debug-with-sleep = [] |
| 114 | 137 | ||
| 115 | ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) | 138 | ## Automatically generate `memory.x` file based on the memory map from [`stm32-metapac`](https://docs.rs/stm32-metapac/) |
| 116 | memory-x = ["stm32-metapac/memory-x"] | 139 | memory-x = [] |
| 117 | 140 | ||
| 118 | ## Use secure registers when TrustZone is enabled | 141 | ## Use secure registers when TrustZone is enabled |
| 119 | trustzone-secure = [] | 142 | trustzone-secure = [] |
| @@ -124,14 +147,18 @@ trustzone-secure = [] | |||
| 124 | ## There are no plans to make this stable. | 147 | ## There are no plans to make this stable. |
| 125 | unstable-pac = [] | 148 | unstable-pac = [] |
| 126 | 149 | ||
| 150 | ## Enable this feature to disable the overclocking check. | ||
| 151 | ## DO NOT ENABLE THIS FEATURE UNLESS YOU KNOW WHAT YOU'RE DOING. | ||
| 152 | unchecked-overclocking = [] | ||
| 153 | |||
| 127 | #! ## Time | 154 | #! ## Time |
| 128 | 155 | ||
| 129 | ## Enables additional driver features that depend on embassy-time | 156 | ## Enables additional driver features that depend on embassy-time |
| 130 | time = ["dep:embassy-time"] | 157 | time = ["dep:embassy-time", "embassy-embedded-hal/time"] |
| 131 | 158 | ||
| 132 | # Features starting with `_` are for internal use only. They're not intended | 159 | # Features starting with `_` are for internal use only. They're not intended |
| 133 | # to be enabled by other crates, and are not covered by semver guarantees. | 160 | # to be enabled by other crates, and are not covered by semver guarantees. |
| 134 | _time-driver = ["dep:embassy-time-driver", "time"] | 161 | _time-driver = ["dep:embassy-time-driver", "time", "dep:embassy-time-queue-utils"] |
| 135 | 162 | ||
| 136 | ## Use any time driver | 163 | ## Use any time driver |
| 137 | time-driver-any = ["_time-driver"] | 164 | time-driver-any = ["_time-driver"] |
| @@ -178,11 +205,17 @@ split-pc2 = ["_split-pins-enabled"] | |||
| 178 | ## Split PC3 | 205 | ## Split PC3 |
| 179 | split-pc3 = ["_split-pins-enabled"] | 206 | split-pc3 = ["_split-pins-enabled"] |
| 180 | 207 | ||
| 208 | dual-bank = [] | ||
| 209 | single-bank = [] | ||
| 210 | |||
| 181 | ## internal use only | 211 | ## internal use only |
| 182 | _split-pins-enabled = [] | 212 | _split-pins-enabled = [] |
| 183 | 213 | ||
| 184 | ## internal use only | 214 | ## internal use only |
| 185 | _dual-core = [] | 215 | _dual-core = [] |
| 216 | _core-cm0p = [] | ||
| 217 | _core-cm4 = [] | ||
| 218 | _core-cm7 = [] | ||
| 186 | 219 | ||
| 187 | #! ## Chip-selection features | 220 | #! ## Chip-selection features |
| 188 | #! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`. | 221 | #! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`. |
| @@ -1007,40 +1040,40 @@ stm32h743xg = [ "stm32-metapac/stm32h743xg" ] | |||
| 1007 | stm32h743xi = [ "stm32-metapac/stm32h743xi" ] | 1040 | stm32h743xi = [ "stm32-metapac/stm32h743xi" ] |
| 1008 | stm32h743zg = [ "stm32-metapac/stm32h743zg" ] | 1041 | stm32h743zg = [ "stm32-metapac/stm32h743zg" ] |
| 1009 | stm32h743zi = [ "stm32-metapac/stm32h743zi" ] | 1042 | stm32h743zi = [ "stm32-metapac/stm32h743zi" ] |
| 1010 | stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7", "_dual-core" ] | 1043 | stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7", "_dual-core", "_core-cm7" ] |
| 1011 | stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4", "_dual-core" ] | 1044 | stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4", "_dual-core", "_core-cm4" ] |
| 1012 | stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7", "_dual-core" ] | 1045 | stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7", "_dual-core", "_core-cm7" ] |
| 1013 | stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4", "_dual-core" ] | 1046 | stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4", "_dual-core", "_core-cm4" ] |
| 1014 | stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7", "_dual-core" ] | 1047 | stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7", "_dual-core", "_core-cm7" ] |
| 1015 | stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4", "_dual-core" ] | 1048 | stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4", "_dual-core", "_core-cm4" ] |
| 1016 | stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7", "_dual-core" ] | 1049 | stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7", "_dual-core", "_core-cm7" ] |
| 1017 | stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4", "_dual-core" ] | 1050 | stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4", "_dual-core", "_core-cm4" ] |
| 1018 | stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7", "_dual-core" ] | 1051 | stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7", "_dual-core", "_core-cm7" ] |
| 1019 | stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4", "_dual-core" ] | 1052 | stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4", "_dual-core", "_core-cm4" ] |
| 1020 | stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7", "_dual-core" ] | 1053 | stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7", "_dual-core", "_core-cm7" ] |
| 1021 | stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4", "_dual-core" ] | 1054 | stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4", "_dual-core", "_core-cm4" ] |
| 1022 | stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7", "_dual-core" ] | 1055 | stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7", "_dual-core", "_core-cm7" ] |
| 1023 | stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4", "_dual-core" ] | 1056 | stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4", "_dual-core", "_core-cm4" ] |
| 1024 | stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7", "_dual-core" ] | 1057 | stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7", "_dual-core", "_core-cm7" ] |
| 1025 | stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4", "_dual-core" ] | 1058 | stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4", "_dual-core", "_core-cm4" ] |
| 1026 | stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7", "_dual-core" ] | 1059 | stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7", "_dual-core", "_core-cm7" ] |
| 1027 | stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4", "_dual-core" ] | 1060 | stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4", "_dual-core", "_core-cm4" ] |
| 1028 | stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7", "_dual-core" ] | 1061 | stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7", "_dual-core", "_core-cm7" ] |
| 1029 | stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4", "_dual-core" ] | 1062 | stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4", "_dual-core", "_core-cm4" ] |
| 1030 | stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7", "_dual-core" ] | 1063 | stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7", "_dual-core", "_core-cm7" ] |
| 1031 | stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4", "_dual-core" ] | 1064 | stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4", "_dual-core", "_core-cm4" ] |
| 1032 | stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7", "_dual-core" ] | 1065 | stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7", "_dual-core", "_core-cm7" ] |
| 1033 | stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4", "_dual-core" ] | 1066 | stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4", "_dual-core", "_core-cm4" ] |
| 1034 | stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7", "_dual-core" ] | 1067 | stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7", "_dual-core", "_core-cm7" ] |
| 1035 | stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4", "_dual-core" ] | 1068 | stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4", "_dual-core", "_core-cm4" ] |
| 1036 | stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7", "_dual-core" ] | 1069 | stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7", "_dual-core", "_core-cm7" ] |
| 1037 | stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4", "_dual-core" ] | 1070 | stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4", "_dual-core", "_core-cm4" ] |
| 1038 | stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7", "_dual-core" ] | 1071 | stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7", "_dual-core", "_core-cm7" ] |
| 1039 | stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4", "_dual-core" ] | 1072 | stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4", "_dual-core", "_core-cm4" ] |
| 1040 | stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7", "_dual-core" ] | 1073 | stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7", "_dual-core", "_core-cm7" ] |
| 1041 | stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4", "_dual-core" ] | 1074 | stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4", "_dual-core", "_core-cm4" ] |
| 1042 | stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7", "_dual-core" ] | 1075 | stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7", "_dual-core", "_core-cm7" ] |
| 1043 | stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4", "_dual-core" ] | 1076 | stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4", "_dual-core", "_core-cm4" ] |
| 1044 | stm32h750ib = [ "stm32-metapac/stm32h750ib" ] | 1077 | stm32h750ib = [ "stm32-metapac/stm32h750ib" ] |
| 1045 | stm32h750vb = [ "stm32-metapac/stm32h750vb" ] | 1078 | stm32h750vb = [ "stm32-metapac/stm32h750vb" ] |
| 1046 | stm32h750xb = [ "stm32-metapac/stm32h750xb" ] | 1079 | stm32h750xb = [ "stm32-metapac/stm32h750xb" ] |
| @@ -1051,24 +1084,24 @@ stm32h753ii = [ "stm32-metapac/stm32h753ii" ] | |||
| 1051 | stm32h753vi = [ "stm32-metapac/stm32h753vi" ] | 1084 | stm32h753vi = [ "stm32-metapac/stm32h753vi" ] |
| 1052 | stm32h753xi = [ "stm32-metapac/stm32h753xi" ] | 1085 | stm32h753xi = [ "stm32-metapac/stm32h753xi" ] |
| 1053 | stm32h753zi = [ "stm32-metapac/stm32h753zi" ] | 1086 | stm32h753zi = [ "stm32-metapac/stm32h753zi" ] |
| 1054 | stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7", "_dual-core" ] | 1087 | stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7", "_dual-core", "_core-cm7" ] |
| 1055 | stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4", "_dual-core" ] | 1088 | stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4", "_dual-core", "_core-cm4" ] |
| 1056 | stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7", "_dual-core" ] | 1089 | stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7", "_dual-core", "_core-cm7" ] |
| 1057 | stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4", "_dual-core" ] | 1090 | stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4", "_dual-core", "_core-cm4" ] |
| 1058 | stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7", "_dual-core" ] | 1091 | stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7", "_dual-core", "_core-cm7" ] |
| 1059 | stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4", "_dual-core" ] | 1092 | stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4", "_dual-core", "_core-cm4" ] |
| 1060 | stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7", "_dual-core" ] | 1093 | stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7", "_dual-core", "_core-cm7" ] |
| 1061 | stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4", "_dual-core" ] | 1094 | stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4", "_dual-core", "_core-cm4" ] |
| 1062 | stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7", "_dual-core" ] | 1095 | stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7", "_dual-core", "_core-cm7" ] |
| 1063 | stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4", "_dual-core" ] | 1096 | stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4", "_dual-core", "_core-cm4" ] |
| 1064 | stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7", "_dual-core" ] | 1097 | stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7", "_dual-core", "_core-cm7" ] |
| 1065 | stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4", "_dual-core" ] | 1098 | stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4", "_dual-core", "_core-cm4" ] |
| 1066 | stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7", "_dual-core" ] | 1099 | stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7", "_dual-core", "_core-cm7" ] |
| 1067 | stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4", "_dual-core" ] | 1100 | stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4", "_dual-core", "_core-cm4" ] |
| 1068 | stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7", "_dual-core" ] | 1101 | stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7", "_dual-core", "_core-cm7" ] |
| 1069 | stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4", "_dual-core" ] | 1102 | stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4", "_dual-core", "_core-cm4" ] |
| 1070 | stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7", "_dual-core" ] | 1103 | stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7", "_dual-core", "_core-cm7" ] |
| 1071 | stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4", "_dual-core" ] | 1104 | stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4", "_dual-core", "_core-cm4" ] |
| 1072 | stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] | 1105 | stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] |
| 1073 | stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] | 1106 | stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] |
| 1074 | stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] | 1107 | stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] |
| @@ -1601,14 +1634,14 @@ stm32wba55he = [ "stm32-metapac/stm32wba55he" ] | |||
| 1601 | stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ] | 1634 | stm32wba55hg = [ "stm32-metapac/stm32wba55hg" ] |
| 1602 | stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ] | 1635 | stm32wba55ue = [ "stm32-metapac/stm32wba55ue" ] |
| 1603 | stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ] | 1636 | stm32wba55ug = [ "stm32-metapac/stm32wba55ug" ] |
| 1604 | stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core" ] | 1637 | stm32wl54cc-cm4 = [ "stm32-metapac/stm32wl54cc-cm4", "_dual-core", "_core-cm4" ] |
| 1605 | stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core" ] | 1638 | stm32wl54cc-cm0p = [ "stm32-metapac/stm32wl54cc-cm0p", "_dual-core", "_core-cm0p" ] |
| 1606 | stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core" ] | 1639 | stm32wl54jc-cm4 = [ "stm32-metapac/stm32wl54jc-cm4", "_dual-core", "_core-cm4" ] |
| 1607 | stm32wl54jc-cm0p = [ "stm32-metapac/stm32wl54jc-cm0p", "_dual-core" ] | 1640 | stm32wl54jc-cm0p = [ "stm32-metapac/stm32wl54jc-cm0p", "_dual-core", "_core-cm0p" ] |
| 1608 | stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4", "_dual-core" ] | 1641 | stm32wl55cc-cm4 = [ "stm32-metapac/stm32wl55cc-cm4", "_dual-core", "_core-cm4" ] |
| 1609 | stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p", "_dual-core" ] | 1642 | stm32wl55cc-cm0p = [ "stm32-metapac/stm32wl55cc-cm0p", "_dual-core", "_core-cm0p" ] |
| 1610 | stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4", "_dual-core" ] | 1643 | stm32wl55jc-cm4 = [ "stm32-metapac/stm32wl55jc-cm4", "_dual-core", "_core-cm4" ] |
| 1611 | stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p", "_dual-core" ] | 1644 | stm32wl55jc-cm0p = [ "stm32-metapac/stm32wl55jc-cm0p", "_dual-core", "_core-cm0p" ] |
| 1612 | stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] | 1645 | stm32wle4c8 = [ "stm32-metapac/stm32wle4c8" ] |
| 1613 | stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] | 1646 | stm32wle4cb = [ "stm32-metapac/stm32wle4cb" ] |
| 1614 | stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] | 1647 | stm32wle4cc = [ "stm32-metapac/stm32wle4cc" ] |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d8a7ea0e6..bb5ef53d7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -9,8 +9,8 @@ use proc_macro2::{Ident, TokenStream}; | |||
| 9 | use quote::{format_ident, quote}; | 9 | use quote::{format_ident, quote}; |
| 10 | use stm32_metapac::metadata::ir::BitOffset; | 10 | use stm32_metapac::metadata::ir::BitOffset; |
| 11 | use stm32_metapac::metadata::{ | 11 | use stm32_metapac::metadata::{ |
| 12 | MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, ALL_CHIPS, | 12 | MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, |
| 13 | ALL_PERIPHERAL_VERSIONS, METADATA, | 13 | ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | #[path = "./build_common.rs"] | 16 | #[path = "./build_common.rs"] |
| @@ -50,12 +50,63 @@ fn main() { | |||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | // ======== | 52 | // ======== |
| 53 | // Select the memory variant to use | ||
| 54 | let memory = { | ||
| 55 | let single_bank_selected = env::var("CARGO_FEATURE_SINGLE_BANK").is_ok(); | ||
| 56 | let dual_bank_selected = env::var("CARGO_FEATURE_DUAL_BANK").is_ok(); | ||
| 57 | |||
| 58 | let single_bank_memory = METADATA.memory.iter().find(|mem| { | ||
| 59 | mem.iter().any(|region| region.name.contains("BANK_1")) | ||
| 60 | && !mem.iter().any(|region| region.name.contains("BANK_2")) | ||
| 61 | }); | ||
| 62 | |||
| 63 | let dual_bank_memory = METADATA.memory.iter().find(|mem| { | ||
| 64 | mem.iter().any(|region| region.name.contains("BANK_1")) | ||
| 65 | && mem.iter().any(|region| region.name.contains("BANK_2")) | ||
| 66 | }); | ||
| 67 | |||
| 68 | cfgs.set( | ||
| 69 | "bank_setup_configurable", | ||
| 70 | single_bank_memory.is_some() && dual_bank_memory.is_some(), | ||
| 71 | ); | ||
| 72 | |||
| 73 | match (single_bank_selected, dual_bank_selected) { | ||
| 74 | (true, true) => panic!("Both 'single-bank' and 'dual-bank' features enabled"), | ||
| 75 | (true, false) => { | ||
| 76 | single_bank_memory.expect("The 'single-bank' feature is not supported on this dual bank chip") | ||
| 77 | } | ||
| 78 | (false, true) => { | ||
| 79 | dual_bank_memory.expect("The 'dual-bank' feature is not supported on this single bank chip") | ||
| 80 | } | ||
| 81 | (false, false) => { | ||
| 82 | if METADATA.memory.len() != 1 { | ||
| 83 | panic!("Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection") | ||
| 84 | } | ||
| 85 | METADATA.memory[0] | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | // ======== | ||
| 53 | // Generate singletons | 91 | // Generate singletons |
| 54 | 92 | ||
| 55 | let mut singletons: Vec<String> = Vec::new(); | 93 | let mut singletons: Vec<String> = Vec::new(); |
| 94 | |||
| 95 | // Generate one singleton per pin | ||
| 96 | for p in METADATA.pins { | ||
| 97 | singletons.push(p.name.to_string()); | ||
| 98 | } | ||
| 99 | |||
| 100 | // generate one singleton per peripheral (with many exceptions...) | ||
| 56 | for p in METADATA.peripherals { | 101 | for p in METADATA.peripherals { |
| 57 | if let Some(r) = &p.registers { | 102 | if let Some(r) = &p.registers { |
| 58 | if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" { | 103 | if r.kind == "adccommon" |
| 104 | || r.kind == "sai" | ||
| 105 | || r.kind == "ucpd" | ||
| 106 | || r.kind == "otg" | ||
| 107 | || r.kind == "octospi" | ||
| 108 | || r.kind == "xspi" | ||
| 109 | { | ||
| 59 | // TODO: should we emit this for all peripherals? if so, we will need a list of all | 110 | // TODO: should we emit this for all peripherals? if so, we will need a list of all |
| 60 | // possible peripherals across all chips, so that we can declare the configs | 111 | // possible peripherals across all chips, so that we can declare the configs |
| 61 | // (replacing the hard-coded list of `peri_*` cfgs below) | 112 | // (replacing the hard-coded list of `peri_*` cfgs below) |
| @@ -63,13 +114,8 @@ fn main() { | |||
| 63 | } | 114 | } |
| 64 | 115 | ||
| 65 | match r.kind { | 116 | match r.kind { |
| 66 | // Generate singletons per pin, not per port | 117 | // handled above |
| 67 | "gpio" => { | 118 | "gpio" => {} |
| 68 | let port_letter = p.name.strip_prefix("GPIO").unwrap(); | ||
| 69 | for pin_num in 0..16 { | ||
| 70 | singletons.push(format!("P{}{}", port_letter, pin_num)); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | 119 | ||
| 74 | // No singleton for these, the HAL handles them specially. | 120 | // No singleton for these, the HAL handles them specially. |
| 75 | "exti" => {} | 121 | "exti" => {} |
| @@ -111,6 +157,10 @@ fn main() { | |||
| 111 | "peri_sai4", | 157 | "peri_sai4", |
| 112 | "peri_ucpd1", | 158 | "peri_ucpd1", |
| 113 | "peri_ucpd2", | 159 | "peri_ucpd2", |
| 160 | "peri_usb_otg_fs", | ||
| 161 | "peri_usb_otg_hs", | ||
| 162 | "peri_octospi2", | ||
| 163 | "peri_xspi2", | ||
| 114 | ]); | 164 | ]); |
| 115 | cfgs.declare_all(&["mco", "mco1", "mco2"]); | 165 | cfgs.declare_all(&["mco", "mco1", "mco2"]); |
| 116 | 166 | ||
| @@ -192,7 +242,7 @@ fn main() { | |||
| 192 | .to_ascii_lowercase(), | 242 | .to_ascii_lowercase(), |
| 193 | ), | 243 | ), |
| 194 | Err(GetOneError::None) => None, | 244 | Err(GetOneError::None) => None, |
| 195 | Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), | 245 | Err(GetOneError::Multiple) => panic!("Multiple time-driver-xxx Cargo features enabled"), |
| 196 | }; | 246 | }; |
| 197 | 247 | ||
| 198 | let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { | 248 | let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { |
| @@ -278,8 +328,7 @@ fn main() { | |||
| 278 | // ======== | 328 | // ======== |
| 279 | // Generate FLASH regions | 329 | // Generate FLASH regions |
| 280 | let mut flash_regions = TokenStream::new(); | 330 | let mut flash_regions = TokenStream::new(); |
| 281 | let flash_memory_regions: Vec<_> = METADATA | 331 | let flash_memory_regions: Vec<_> = memory |
| 282 | .memory | ||
| 283 | .iter() | 332 | .iter() |
| 284 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) | 333 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.settings.is_some()) |
| 285 | .collect(); | 334 | .collect(); |
| @@ -291,6 +340,8 @@ fn main() { | |||
| 291 | "Bank1" | 340 | "Bank1" |
| 292 | } else if region.name.starts_with("BANK_2") { | 341 | } else if region.name.starts_with("BANK_2") { |
| 293 | "Bank2" | 342 | "Bank2" |
| 343 | } else if region.name == "OTP" { | ||
| 344 | "Otp" | ||
| 294 | } else { | 345 | } else { |
| 295 | continue; | 346 | continue; |
| 296 | } | 347 | } |
| @@ -317,7 +368,7 @@ fn main() { | |||
| 317 | let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); | 368 | let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); |
| 318 | flash_regions.extend(quote! { | 369 | flash_regions.extend(quote! { |
| 319 | #[cfg(flash)] | 370 | #[cfg(flash)] |
| 320 | pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData<MODE>); | 371 | pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData<MODE>); |
| 321 | }); | 372 | }); |
| 322 | } | 373 | } |
| 323 | 374 | ||
| @@ -349,7 +400,7 @@ fn main() { | |||
| 349 | 400 | ||
| 350 | #[cfg(flash)] | 401 | #[cfg(flash)] |
| 351 | impl<'d, MODE> FlashLayout<'d, MODE> { | 402 | impl<'d, MODE> FlashLayout<'d, MODE> { |
| 352 | pub(crate) fn new(p: embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { | 403 | pub(crate) fn new(p: embassy_hal_internal::Peri<'d, crate::peripherals::FLASH>) -> Self { |
| 353 | Self { | 404 | Self { |
| 354 | #(#inits),*, | 405 | #(#inits),*, |
| 355 | _mode: core::marker::PhantomData, | 406 | _mode: core::marker::PhantomData, |
| @@ -475,9 +526,39 @@ fn main() { | |||
| 475 | } | 526 | } |
| 476 | 527 | ||
| 477 | impl<'a> ClockGen<'a> { | 528 | impl<'a> ClockGen<'a> { |
| 529 | fn parse_mul_div(name: &str) -> (&str, Frac) { | ||
| 530 | if name == "hse_div_rtcpre" { | ||
| 531 | return (name, Frac { num: 1, denom: 1 }); | ||
| 532 | } | ||
| 533 | |||
| 534 | if let Some(i) = name.find("_div_") { | ||
| 535 | let n = &name[..i]; | ||
| 536 | let val: u32 = name[i + 5..].parse().unwrap(); | ||
| 537 | (n, Frac { num: 1, denom: val }) | ||
| 538 | } else if let Some(i) = name.find("_mul_") { | ||
| 539 | let n = &name[..i]; | ||
| 540 | let val: u32 = name[i + 5..].parse().unwrap(); | ||
| 541 | (n, Frac { num: val, denom: 1 }) | ||
| 542 | } else { | ||
| 543 | (name, Frac { num: 1, denom: 1 }) | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 478 | fn gen_clock(&mut self, peripheral: &str, name: &str) -> TokenStream { | 547 | fn gen_clock(&mut self, peripheral: &str, name: &str) -> TokenStream { |
| 479 | let clock_name = format_ident!("{}", name.to_ascii_lowercase()); | 548 | let name = name.to_ascii_lowercase(); |
| 480 | self.clock_names.insert(name.to_ascii_lowercase()); | 549 | let (name, frac) = Self::parse_mul_div(&name); |
| 550 | let clock_name = format_ident!("{}", name); | ||
| 551 | self.clock_names.insert(name.to_string()); | ||
| 552 | |||
| 553 | let mut muldiv = quote!(); | ||
| 554 | if frac.num != 1 { | ||
| 555 | let val = frac.num; | ||
| 556 | muldiv.extend(quote!(* #val)); | ||
| 557 | } | ||
| 558 | if frac.denom != 1 { | ||
| 559 | let val = frac.denom; | ||
| 560 | muldiv.extend(quote!(/ #val)); | ||
| 561 | } | ||
| 481 | quote!(unsafe { | 562 | quote!(unsafe { |
| 482 | unwrap!( | 563 | unwrap!( |
| 483 | crate::rcc::get_freqs().#clock_name.to_hertz(), | 564 | crate::rcc::get_freqs().#clock_name.to_hertz(), |
| @@ -486,6 +567,7 @@ fn main() { | |||
| 486 | #peripheral, | 567 | #peripheral, |
| 487 | #name | 568 | #name |
| 488 | ) | 569 | ) |
| 570 | #muldiv | ||
| 489 | }) | 571 | }) |
| 490 | } | 572 | } |
| 491 | 573 | ||
| @@ -527,7 +609,11 @@ fn main() { | |||
| 527 | match crate::pac::RCC.#fieldset_name().read().#field_name() { | 609 | match crate::pac::RCC.#fieldset_name().read().#field_name() { |
| 528 | #match_arms | 610 | #match_arms |
| 529 | #[allow(unreachable_patterns)] | 611 | #[allow(unreachable_patterns)] |
| 530 | _ => unreachable!(), | 612 | _ => panic!( |
| 613 | "attempted to use peripheral '{}' but its clock mux is not set to a valid \ | ||
| 614 | clock. Change 'config.rcc.mux' to another clock.", | ||
| 615 | #peripheral | ||
| 616 | ) | ||
| 531 | } | 617 | } |
| 532 | } | 618 | } |
| 533 | } | 619 | } |
| @@ -598,6 +684,8 @@ fn main() { | |||
| 598 | PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock), | 684 | PeripheralRccKernelClock::Clock(clock) => clock_gen.gen_clock(p.name, clock), |
| 599 | }; | 685 | }; |
| 600 | 686 | ||
| 687 | let bus_clock_frequency = clock_gen.gen_clock(p.name, &rcc.bus_clock); | ||
| 688 | |||
| 601 | // A refcount leak can result if the same field is shared by peripherals with different stop modes | 689 | // A refcount leak can result if the same field is shared by peripherals with different stop modes |
| 602 | // This condition should be checked in stm32-data | 690 | // This condition should be checked in stm32-data |
| 603 | let stop_mode = match rcc.stop_mode { | 691 | let stop_mode = match rcc.stop_mode { |
| @@ -611,6 +699,9 @@ fn main() { | |||
| 611 | fn frequency() -> crate::time::Hertz { | 699 | fn frequency() -> crate::time::Hertz { |
| 612 | #clock_frequency | 700 | #clock_frequency |
| 613 | } | 701 | } |
| 702 | fn bus_frequency() -> crate::time::Hertz { | ||
| 703 | #bus_clock_frequency | ||
| 704 | } | ||
| 614 | 705 | ||
| 615 | const RCC_INFO: crate::rcc::RccInfo = unsafe { | 706 | const RCC_INFO: crate::rcc::RccInfo = unsafe { |
| 616 | crate::rcc::RccInfo::new( | 707 | crate::rcc::RccInfo::new( |
| @@ -709,6 +800,16 @@ fn main() { | |||
| 709 | // Generate RCC | 800 | // Generate RCC |
| 710 | clock_gen.clock_names.insert("sys".to_string()); | 801 | clock_gen.clock_names.insert("sys".to_string()); |
| 711 | clock_gen.clock_names.insert("rtc".to_string()); | 802 | clock_gen.clock_names.insert("rtc".to_string()); |
| 803 | |||
| 804 | // STM32F4 SPI in I2S mode receives a clock input from the dedicated I2S PLL. | ||
| 805 | // For this, there is an additional clock MUX, which is not present in other | ||
| 806 | // peripherals and does not fit the current RCC structure of stm32-data. | ||
| 807 | if chip_name.starts_with("stm32f4") && !chip_name.starts_with("stm32f410") { | ||
| 808 | clock_gen.clock_names.insert("plli2s1_p".to_string()); | ||
| 809 | clock_gen.clock_names.insert("plli2s1_q".to_string()); | ||
| 810 | clock_gen.clock_names.insert("plli2s1_r".to_string()); | ||
| 811 | } | ||
| 812 | |||
| 712 | let clock_idents: Vec<_> = clock_gen.clock_names.iter().map(|n| format_ident!("{}", n)).collect(); | 813 | let clock_idents: Vec<_> = clock_gen.clock_names.iter().map(|n| format_ident!("{}", n)).collect(); |
| 713 | g.extend(quote! { | 814 | g.extend(quote! { |
| 714 | #[derive(Clone, Copy, Debug)] | 815 | #[derive(Clone, Copy, Debug)] |
| @@ -857,6 +958,7 @@ fn main() { | |||
| 857 | (("ltdc", "B7"), quote!(crate::ltdc::B7Pin)), | 958 | (("ltdc", "B7"), quote!(crate::ltdc::B7Pin)), |
| 858 | (("usb", "DP"), quote!(crate::usb::DpPin)), | 959 | (("usb", "DP"), quote!(crate::usb::DpPin)), |
| 859 | (("usb", "DM"), quote!(crate::usb::DmPin)), | 960 | (("usb", "DM"), quote!(crate::usb::DmPin)), |
| 961 | (("usb", "SOF"), quote!(crate::usb::SofPin)), | ||
| 860 | (("otg", "DP"), quote!(crate::usb::DpPin)), | 962 | (("otg", "DP"), quote!(crate::usb::DpPin)), |
| 861 | (("otg", "DM"), quote!(crate::usb::DmPin)), | 963 | (("otg", "DM"), quote!(crate::usb::DmPin)), |
| 862 | (("otg", "ULPI_CK"), quote!(crate::usb::UlpiClkPin)), | 964 | (("otg", "ULPI_CK"), quote!(crate::usb::UlpiClkPin)), |
| @@ -1015,6 +1117,9 @@ fn main() { | |||
| 1015 | (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), | 1117 | (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), |
| 1016 | (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), | 1118 | (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), |
| 1017 | (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), | 1119 | (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), |
| 1120 | (("lptim", "CH1"), quote!(crate::lptim::Channel1Pin)), | ||
| 1121 | (("lptim", "CH2"), quote!(crate::lptim::Channel2Pin)), | ||
| 1122 | (("lptim", "OUT"), quote!(crate::lptim::OutputPin)), | ||
| 1018 | (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), | 1123 | (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), |
| 1019 | (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), | 1124 | (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), |
| 1020 | (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), | 1125 | (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), |
| @@ -1024,7 +1129,7 @@ fn main() { | |||
| 1024 | (("sdmmc", "D4"), quote!(crate::sdmmc::D4Pin)), | 1129 | (("sdmmc", "D4"), quote!(crate::sdmmc::D4Pin)), |
| 1025 | (("sdmmc", "D5"), quote!(crate::sdmmc::D5Pin)), | 1130 | (("sdmmc", "D5"), quote!(crate::sdmmc::D5Pin)), |
| 1026 | (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), | 1131 | (("sdmmc", "D6"), quote!(crate::sdmmc::D6Pin)), |
| 1027 | (("sdmmc", "D6"), quote!(crate::sdmmc::D7Pin)), | 1132 | (("sdmmc", "D7"), quote!(crate::sdmmc::D7Pin)), |
| 1028 | (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), | 1133 | (("sdmmc", "D8"), quote!(crate::sdmmc::D8Pin)), |
| 1029 | (("quadspi", "BK1_IO0"), quote!(crate::qspi::BK1D0Pin)), | 1134 | (("quadspi", "BK1_IO0"), quote!(crate::qspi::BK1D0Pin)), |
| 1030 | (("quadspi", "BK1_IO1"), quote!(crate::qspi::BK1D1Pin)), | 1135 | (("quadspi", "BK1_IO1"), quote!(crate::qspi::BK1D1Pin)), |
| @@ -1049,6 +1154,117 @@ fn main() { | |||
| 1049 | (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), | 1154 | (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), |
| 1050 | (("octospi", "CLK"), quote!(crate::ospi::SckPin)), | 1155 | (("octospi", "CLK"), quote!(crate::ospi::SckPin)), |
| 1051 | (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), | 1156 | (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), |
| 1157 | (("octospim", "P1_IO0"), quote!(crate::ospi::D0Pin)), | ||
| 1158 | (("octospim", "P1_IO1"), quote!(crate::ospi::D1Pin)), | ||
| 1159 | (("octospim", "P1_IO2"), quote!(crate::ospi::D2Pin)), | ||
| 1160 | (("octospim", "P1_IO3"), quote!(crate::ospi::D3Pin)), | ||
| 1161 | (("octospim", "P1_IO4"), quote!(crate::ospi::D4Pin)), | ||
| 1162 | (("octospim", "P1_IO5"), quote!(crate::ospi::D5Pin)), | ||
| 1163 | (("octospim", "P1_IO6"), quote!(crate::ospi::D6Pin)), | ||
| 1164 | (("octospim", "P1_IO7"), quote!(crate::ospi::D7Pin)), | ||
| 1165 | (("octospim", "P1_DQS"), quote!(crate::ospi::DQSPin)), | ||
| 1166 | (("octospim", "P1_NCS"), quote!(crate::ospi::NSSPin)), | ||
| 1167 | (("octospim", "P1_CLK"), quote!(crate::ospi::SckPin)), | ||
| 1168 | (("octospim", "P1_NCLK"), quote!(crate::ospi::NckPin)), | ||
| 1169 | (("octospim", "P2_IO0"), quote!(crate::ospi::D0Pin)), | ||
| 1170 | (("octospim", "P2_IO1"), quote!(crate::ospi::D1Pin)), | ||
| 1171 | (("octospim", "P2_IO2"), quote!(crate::ospi::D2Pin)), | ||
| 1172 | (("octospim", "P2_IO3"), quote!(crate::ospi::D3Pin)), | ||
| 1173 | (("octospim", "P2_IO4"), quote!(crate::ospi::D4Pin)), | ||
| 1174 | (("octospim", "P2_IO5"), quote!(crate::ospi::D5Pin)), | ||
| 1175 | (("octospim", "P2_IO6"), quote!(crate::ospi::D6Pin)), | ||
| 1176 | (("octospim", "P2_IO7"), quote!(crate::ospi::D7Pin)), | ||
| 1177 | (("octospim", "P2_DQS"), quote!(crate::ospi::DQSPin)), | ||
| 1178 | (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)), | ||
| 1179 | (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)), | ||
| 1180 | (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)), | ||
| 1181 | (("xspi", "IO0"), quote!(crate::xspi::D0Pin)), | ||
| 1182 | (("xspi", "IO1"), quote!(crate::xspi::D1Pin)), | ||
| 1183 | (("xspi", "IO2"), quote!(crate::xspi::D2Pin)), | ||
| 1184 | (("xspi", "IO3"), quote!(crate::xspi::D3Pin)), | ||
| 1185 | (("xspi", "IO4"), quote!(crate::xspi::D4Pin)), | ||
| 1186 | (("xspi", "IO5"), quote!(crate::xspi::D5Pin)), | ||
| 1187 | (("xspi", "IO6"), quote!(crate::xspi::D6Pin)), | ||
| 1188 | (("xspi", "IO7"), quote!(crate::xspi::D7Pin)), | ||
| 1189 | (("xspi", "IO8"), quote!(crate::xspi::D8Pin)), | ||
| 1190 | (("xspi", "IO9"), quote!(crate::xspi::D9Pin)), | ||
| 1191 | (("xspi", "IO10"), quote!(crate::xspi::D10Pin)), | ||
| 1192 | (("xspi", "IO11"), quote!(crate::xspi::D11Pin)), | ||
| 1193 | (("xspi", "IO12"), quote!(crate::xspi::D12Pin)), | ||
| 1194 | (("xspi", "IO13"), quote!(crate::xspi::D13Pin)), | ||
| 1195 | (("xspi", "IO14"), quote!(crate::xspi::D14Pin)), | ||
| 1196 | (("xspi", "IO15"), quote!(crate::xspi::D15Pin)), | ||
| 1197 | (("xspi", "DQS0"), quote!(crate::xspi::DQS0Pin)), | ||
| 1198 | (("xspi", "DQS1"), quote!(crate::xspi::DQS1Pin)), | ||
| 1199 | (("xspi", "NCS1"), quote!(crate::xspi::NCSPin)), | ||
| 1200 | (("xspi", "NCS2"), quote!(crate::xspi::NCSPin)), | ||
| 1201 | (("xspi", "CLK"), quote!(crate::xspi::CLKPin)), | ||
| 1202 | (("xspi", "NCLK"), quote!(crate::xspi::NCLKPin)), | ||
| 1203 | (("xspim", "P1_IO0"), quote!(crate::xspi::D0Pin)), | ||
| 1204 | (("xspim", "P1_IO1"), quote!(crate::xspi::D1Pin)), | ||
| 1205 | (("xspim", "P1_IO2"), quote!(crate::xspi::D2Pin)), | ||
| 1206 | (("xspim", "P1_IO3"), quote!(crate::xspi::D3Pin)), | ||
| 1207 | (("xspim", "P1_IO4"), quote!(crate::xspi::D4Pin)), | ||
| 1208 | (("xspim", "P1_IO5"), quote!(crate::xspi::D5Pin)), | ||
| 1209 | (("xspim", "P1_IO6"), quote!(crate::xspi::D6Pin)), | ||
| 1210 | (("xspim", "P1_IO7"), quote!(crate::xspi::D7Pin)), | ||
| 1211 | (("xspim", "P1_IO8"), quote!(crate::xspi::D8Pin)), | ||
| 1212 | (("xspim", "P1_IO9"), quote!(crate::xspi::D9Pin)), | ||
| 1213 | (("xspim", "P1_IO10"), quote!(crate::xspi::D10Pin)), | ||
| 1214 | (("xspim", "P1_IO11"), quote!(crate::xspi::D11Pin)), | ||
| 1215 | (("xspim", "P1_IO12"), quote!(crate::xspi::D12Pin)), | ||
| 1216 | (("xspim", "P1_IO13"), quote!(crate::xspi::D13Pin)), | ||
| 1217 | (("xspim", "P1_IO14"), quote!(crate::xspi::D14Pin)), | ||
| 1218 | (("xspim", "P1_IO15"), quote!(crate::xspi::D15Pin)), | ||
| 1219 | (("xspim", "P1_DQS0"), quote!(crate::xspi::DQS0Pin)), | ||
| 1220 | (("xspim", "P1_DQS1"), quote!(crate::xspi::DQS1Pin)), | ||
| 1221 | (("xspim", "P1_NCS1"), quote!(crate::xspi::NCSPin)), | ||
| 1222 | (("xspim", "P1_NCS2"), quote!(crate::xspi::NCSPin)), | ||
| 1223 | (("xspim", "P1_CLK"), quote!(crate::xspi::CLKPin)), | ||
| 1224 | (("xspim", "P1_NCLK"), quote!(crate::xspi::NCLKPin)), | ||
| 1225 | (("xspim", "P2_IO0"), quote!(crate::xspi::D0Pin)), | ||
| 1226 | (("xspim", "P2_IO1"), quote!(crate::xspi::D1Pin)), | ||
| 1227 | (("xspim", "P2_IO2"), quote!(crate::xspi::D2Pin)), | ||
| 1228 | (("xspim", "P2_IO3"), quote!(crate::xspi::D3Pin)), | ||
| 1229 | (("xspim", "P2_IO4"), quote!(crate::xspi::D4Pin)), | ||
| 1230 | (("xspim", "P2_IO5"), quote!(crate::xspi::D5Pin)), | ||
| 1231 | (("xspim", "P2_IO6"), quote!(crate::xspi::D6Pin)), | ||
| 1232 | (("xspim", "P2_IO7"), quote!(crate::xspi::D7Pin)), | ||
| 1233 | (("xspim", "P2_IO8"), quote!(crate::xspi::D8Pin)), | ||
| 1234 | (("xspim", "P2_IO9"), quote!(crate::xspi::D9Pin)), | ||
| 1235 | (("xspim", "P2_IO10"), quote!(crate::xspi::D10Pin)), | ||
| 1236 | (("xspim", "P2_IO11"), quote!(crate::xspi::D11Pin)), | ||
| 1237 | (("xspim", "P2_IO12"), quote!(crate::xspi::D12Pin)), | ||
| 1238 | (("xspim", "P2_IO13"), quote!(crate::xspi::D13Pin)), | ||
| 1239 | (("xspim", "P2_IO14"), quote!(crate::xspi::D14Pin)), | ||
| 1240 | (("xspim", "P2_IO15"), quote!(crate::xspi::D15Pin)), | ||
| 1241 | (("xspim", "P2_DQS0"), quote!(crate::xspi::DQS0Pin)), | ||
| 1242 | (("xspim", "P2_DQS1"), quote!(crate::xspi::DQS1Pin)), | ||
| 1243 | (("xspim", "P2_NCS1"), quote!(crate::xspi::NCSPin)), | ||
| 1244 | (("xspim", "P2_NCS2"), quote!(crate::xspi::NCSPin)), | ||
| 1245 | (("xspim", "P2_CLK"), quote!(crate::xspi::CLKPin)), | ||
| 1246 | (("xspim", "P2_NCLK"), quote!(crate::xspi::NCLKPin)), | ||
| 1247 | (("hspi", "IO0"), quote!(crate::hspi::D0Pin)), | ||
| 1248 | (("hspi", "IO1"), quote!(crate::hspi::D1Pin)), | ||
| 1249 | (("hspi", "IO2"), quote!(crate::hspi::D2Pin)), | ||
| 1250 | (("hspi", "IO3"), quote!(crate::hspi::D3Pin)), | ||
| 1251 | (("hspi", "IO4"), quote!(crate::hspi::D4Pin)), | ||
| 1252 | (("hspi", "IO5"), quote!(crate::hspi::D5Pin)), | ||
| 1253 | (("hspi", "IO6"), quote!(crate::hspi::D6Pin)), | ||
| 1254 | (("hspi", "IO7"), quote!(crate::hspi::D7Pin)), | ||
| 1255 | (("hspi", "IO8"), quote!(crate::hspi::D8Pin)), | ||
| 1256 | (("hspi", "IO9"), quote!(crate::hspi::D9Pin)), | ||
| 1257 | (("hspi", "IO10"), quote!(crate::hspi::D10Pin)), | ||
| 1258 | (("hspi", "IO11"), quote!(crate::hspi::D11Pin)), | ||
| 1259 | (("hspi", "IO12"), quote!(crate::hspi::D12Pin)), | ||
| 1260 | (("hspi", "IO13"), quote!(crate::hspi::D13Pin)), | ||
| 1261 | (("hspi", "IO14"), quote!(crate::hspi::D14Pin)), | ||
| 1262 | (("hspi", "IO15"), quote!(crate::hspi::D15Pin)), | ||
| 1263 | (("hspi", "DQS0"), quote!(crate::hspi::DQS0Pin)), | ||
| 1264 | (("hspi", "DQS1"), quote!(crate::hspi::DQS1Pin)), | ||
| 1265 | (("hspi", "NCS"), quote!(crate::hspi::NSSPin)), | ||
| 1266 | (("hspi", "CLK"), quote!(crate::hspi::SckPin)), | ||
| 1267 | (("hspi", "NCLK"), quote!(crate::hspi::NckPin)), | ||
| 1052 | (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)), | 1268 | (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)), |
| 1053 | (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)), | 1269 | (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)), |
| 1054 | (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)), | 1270 | (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)), |
| @@ -1081,6 +1297,8 @@ fn main() { | |||
| 1081 | (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), | 1297 | (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), |
| 1082 | (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), | 1298 | (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), |
| 1083 | (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), | 1299 | (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), |
| 1300 | (("dac", "OUT1"), quote!(crate::dac::DacPin<Ch1>)), | ||
| 1301 | (("dac", "OUT2"), quote!(crate::dac::DacPin<Ch2>)), | ||
| 1084 | ].into(); | 1302 | ].into(); |
| 1085 | 1303 | ||
| 1086 | for p in METADATA.peripherals { | 1304 | for p in METADATA.peripherals { |
| @@ -1106,6 +1324,41 @@ fn main() { | |||
| 1106 | peri = format_ident!("{}", pin.signal.replace('_', "")); | 1324 | peri = format_ident!("{}", pin.signal.replace('_', "")); |
| 1107 | } | 1325 | } |
| 1108 | 1326 | ||
| 1327 | // OCTOSPIM is special | ||
| 1328 | if p.name == "OCTOSPIM" { | ||
| 1329 | // Some chips have OCTOSPIM but not OCTOSPI2. | ||
| 1330 | if METADATA.peripherals.iter().any(|p| p.name == "OCTOSPI2") { | ||
| 1331 | peri = format_ident!("{}", "OCTOSPI2"); | ||
| 1332 | g.extend(quote! { | ||
| 1333 | pin_trait_impl!(#tr, #peri, #pin_name, #af); | ||
| 1334 | }); | ||
| 1335 | } | ||
| 1336 | peri = format_ident!("{}", "OCTOSPI1"); | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | // XSPIM is special | ||
| 1340 | if p.name == "XSPIM" { | ||
| 1341 | if pin.signal.starts_with("P1") { | ||
| 1342 | peri = format_ident!("{}", "XSPI1"); | ||
| 1343 | } else if pin.signal.starts_with("P2") { | ||
| 1344 | peri = format_ident!("{}", "XSPI2"); | ||
| 1345 | } else { | ||
| 1346 | panic! {"malformed XSPIM pin: {:?}", pin} | ||
| 1347 | } | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | // XSPI NCS pin to CSSEL mapping | ||
| 1351 | if pin.signal.ends_with("NCS1") { | ||
| 1352 | g.extend(quote! { | ||
| 1353 | sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 0); | ||
| 1354 | }) | ||
| 1355 | } | ||
| 1356 | if pin.signal.ends_with("NCS2") { | ||
| 1357 | g.extend(quote! { | ||
| 1358 | sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 1); | ||
| 1359 | }) | ||
| 1360 | } | ||
| 1361 | |||
| 1109 | g.extend(quote! { | 1362 | g.extend(quote! { |
| 1110 | pin_trait_impl!(#tr, #peri, #pin_name, #af); | 1363 | pin_trait_impl!(#tr, #peri, #pin_name, #af); |
| 1111 | }) | 1364 | }) |
| @@ -1158,6 +1411,18 @@ fn main() { | |||
| 1158 | g.extend(quote! { | 1411 | g.extend(quote! { |
| 1159 | impl_opamp_vp_pin!( #peri, #pin_name, #ch); | 1412 | impl_opamp_vp_pin!( #peri, #pin_name, #ch); |
| 1160 | }) | 1413 | }) |
| 1414 | } else if pin.signal.starts_with("VINM") { | ||
| 1415 | // Impl NonInvertingPin for the VINM* signals ( VINM0, VINM1, etc) | ||
| 1416 | // STM32G4 | ||
| 1417 | let peri = format_ident!("{}", p.name); | ||
| 1418 | let pin_name = format_ident!("{}", pin.pin); | ||
| 1419 | let ch: Result<u8, _> = pin.signal.strip_prefix("VINM").unwrap().parse(); | ||
| 1420 | |||
| 1421 | if let Ok(ch) = ch { | ||
| 1422 | g.extend(quote! { | ||
| 1423 | impl_opamp_vn_pin!( #peri, #pin_name, #ch); | ||
| 1424 | }) | ||
| 1425 | } | ||
| 1161 | } else if pin.signal == "VOUT" { | 1426 | } else if pin.signal == "VOUT" { |
| 1162 | // Impl OutputPin for the VOUT pin | 1427 | // Impl OutputPin for the VOUT pin |
| 1163 | let peri = format_ident!("{}", p.name); | 1428 | let peri = format_ident!("{}", p.name); |
| @@ -1168,14 +1433,14 @@ fn main() { | |||
| 1168 | } | 1433 | } |
| 1169 | } | 1434 | } |
| 1170 | 1435 | ||
| 1171 | // DAC is special | 1436 | if regs.kind == "spdifrx" { |
| 1172 | if regs.kind == "dac" { | ||
| 1173 | let peri = format_ident!("{}", p.name); | 1437 | let peri = format_ident!("{}", p.name); |
| 1174 | let pin_name = format_ident!("{}", pin.pin); | 1438 | let pin_name = format_ident!("{}", pin.pin); |
| 1175 | let ch: u8 = pin.signal.strip_prefix("OUT").unwrap().parse().unwrap(); | 1439 | let af = pin.af.unwrap_or(0); |
| 1440 | let sel: u8 = pin.signal.strip_prefix("IN").unwrap().parse().unwrap(); | ||
| 1176 | 1441 | ||
| 1177 | g.extend(quote! { | 1442 | g.extend(quote! { |
| 1178 | impl_dac_pin!( #peri, #pin_name, #ch); | 1443 | impl_spdifrx_pin!( #peri, #pin_name, #af, #sel); |
| 1179 | }) | 1444 | }) |
| 1180 | } | 1445 | } |
| 1181 | } | 1446 | } |
| @@ -1185,13 +1450,12 @@ fn main() { | |||
| 1185 | // ======== | 1450 | // ======== |
| 1186 | // Generate dma_trait_impl! | 1451 | // Generate dma_trait_impl! |
| 1187 | 1452 | ||
| 1188 | let signals: HashMap<_, _> = [ | 1453 | let mut signals: HashMap<_, _> = [ |
| 1189 | // (kind, signal) => trait | 1454 | // (kind, signal) => trait |
| 1190 | (("adc", "ADC"), quote!(crate::adc::RxDma)), | 1455 | (("adc", "ADC"), quote!(crate::adc::RxDma)), |
| 1191 | (("adc", "ADC1"), quote!(crate::adc::RxDma)), | 1456 | (("adc", "ADC1"), quote!(crate::adc::RxDma)), |
| 1192 | (("adc", "ADC2"), quote!(crate::adc::RxDma)), | 1457 | (("adc", "ADC2"), quote!(crate::adc::RxDma)), |
| 1193 | (("adc", "ADC3"), quote!(crate::adc::RxDma)), | 1458 | (("adc", "ADC3"), quote!(crate::adc::RxDma)), |
| 1194 | (("adc", "ADC4"), quote!(crate::adc::RxDma)), | ||
| 1195 | (("ucpd", "RX"), quote!(crate::ucpd::RxDma)), | 1459 | (("ucpd", "RX"), quote!(crate::ucpd::RxDma)), |
| 1196 | (("ucpd", "TX"), quote!(crate::ucpd::TxDma)), | 1460 | (("ucpd", "TX"), quote!(crate::ucpd::TxDma)), |
| 1197 | (("usart", "RX"), quote!(crate::usart::RxDma)), | 1461 | (("usart", "RX"), quote!(crate::usart::RxDma)), |
| @@ -1202,6 +1466,7 @@ fn main() { | |||
| 1202 | (("sai", "B"), quote!(crate::sai::Dma<B>)), | 1466 | (("sai", "B"), quote!(crate::sai::Dma<B>)), |
| 1203 | (("spi", "RX"), quote!(crate::spi::RxDma)), | 1467 | (("spi", "RX"), quote!(crate::spi::RxDma)), |
| 1204 | (("spi", "TX"), quote!(crate::spi::TxDma)), | 1468 | (("spi", "TX"), quote!(crate::spi::TxDma)), |
| 1469 | (("spdifrx", "RX"), quote!(crate::spdifrx::Dma)), | ||
| 1205 | (("i2c", "RX"), quote!(crate::i2c::RxDma)), | 1470 | (("i2c", "RX"), quote!(crate::i2c::RxDma)), |
| 1206 | (("i2c", "TX"), quote!(crate::i2c::TxDma)), | 1471 | (("i2c", "TX"), quote!(crate::i2c::TxDma)), |
| 1207 | (("dcmi", "DCMI"), quote!(crate::dcmi::FrameDma)), | 1472 | (("dcmi", "DCMI"), quote!(crate::dcmi::FrameDma)), |
| @@ -1210,8 +1475,9 @@ fn main() { | |||
| 1210 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), | 1475 | (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), |
| 1211 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), | 1476 | (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), |
| 1212 | (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)), | 1477 | (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)), |
| 1213 | (("dac", "CH1"), quote!(crate::dac::DacDma1)), | 1478 | (("hspi", "HSPI1"), quote!(crate::hspi::HspiDma)), |
| 1214 | (("dac", "CH2"), quote!(crate::dac::DacDma2)), | 1479 | (("dac", "CH1"), quote!(crate::dac::Dma<Ch1>)), |
| 1480 | (("dac", "CH2"), quote!(crate::dac::Dma<Ch2>)), | ||
| 1215 | (("timer", "UP"), quote!(crate::timer::UpDma)), | 1481 | (("timer", "UP"), quote!(crate::timer::UpDma)), |
| 1216 | (("hash", "IN"), quote!(crate::hash::Dma)), | 1482 | (("hash", "IN"), quote!(crate::hash::Dma)), |
| 1217 | (("cryp", "IN"), quote!(crate::cryp::DmaIn)), | 1483 | (("cryp", "IN"), quote!(crate::cryp::DmaIn)), |
| @@ -1225,6 +1491,19 @@ fn main() { | |||
| 1225 | ] | 1491 | ] |
| 1226 | .into(); | 1492 | .into(); |
| 1227 | 1493 | ||
| 1494 | if chip_name.starts_with("stm32u5") { | ||
| 1495 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); | ||
| 1496 | } else { | ||
| 1497 | signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | if chip_name.starts_with("stm32g4") { | ||
| 1501 | let line_number = chip_name.chars().skip(8).next().unwrap(); | ||
| 1502 | if line_number == '3' || line_number == '4' { | ||
| 1503 | signals.insert(("adc", "ADC5"), quote!(crate::adc::RxDma)); | ||
| 1504 | } | ||
| 1505 | } | ||
| 1506 | |||
| 1228 | for p in METADATA.peripherals { | 1507 | for p in METADATA.peripherals { |
| 1229 | if let Some(regs) = &p.registers { | 1508 | if let Some(regs) = &p.registers { |
| 1230 | // FIXME: stm32u5a crash on Cordic driver | 1509 | // FIXME: stm32u5a crash on Cordic driver |
| @@ -1290,36 +1569,13 @@ fn main() { | |||
| 1290 | for e in rcc_registers.ir.enums { | 1569 | for e in rcc_registers.ir.enums { |
| 1291 | fn is_rcc_name(e: &str) -> bool { | 1570 | fn is_rcc_name(e: &str) -> bool { |
| 1292 | match e { | 1571 | match e { |
| 1293 | "Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true, | 1572 | "Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" => true, |
| 1294 | "Timpre" | "Pllrclkpre" => false, | 1573 | "Timpre" | "Pllrclkpre" => false, |
| 1295 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, | 1574 | e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, |
| 1296 | _ => false, | 1575 | _ => false, |
| 1297 | } | 1576 | } |
| 1298 | } | 1577 | } |
| 1299 | 1578 | ||
| 1300 | #[derive(Copy, Clone, Debug)] | ||
| 1301 | struct Frac { | ||
| 1302 | num: u32, | ||
| 1303 | denom: u32, | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | impl Frac { | ||
| 1307 | fn simplify(self) -> Self { | ||
| 1308 | let d = gcd(self.num, self.denom); | ||
| 1309 | Self { | ||
| 1310 | num: self.num / d, | ||
| 1311 | denom: self.denom / d, | ||
| 1312 | } | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | fn gcd(a: u32, b: u32) -> u32 { | ||
| 1317 | if b == 0 { | ||
| 1318 | return a; | ||
| 1319 | } | ||
| 1320 | gcd(b, a % b) | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | fn parse_num(n: &str) -> Result<Frac, ()> { | 1579 | fn parse_num(n: &str) -> Result<Frac, ()> { |
| 1324 | for prefix in ["DIV", "MUL"] { | 1580 | for prefix in ["DIV", "MUL"] { |
| 1325 | if let Some(n) = n.strip_prefix(prefix) { | 1581 | if let Some(n) = n.strip_prefix(prefix) { |
| @@ -1402,8 +1658,7 @@ fn main() { | |||
| 1402 | let mut pins_table: Vec<Vec<String>> = Vec::new(); | 1658 | let mut pins_table: Vec<Vec<String>> = Vec::new(); |
| 1403 | let mut adc_table: Vec<Vec<String>> = Vec::new(); | 1659 | let mut adc_table: Vec<Vec<String>> = Vec::new(); |
| 1404 | 1660 | ||
| 1405 | for m in METADATA | 1661 | for m in memory |
| 1406 | .memory | ||
| 1407 | .iter() | 1662 | .iter() |
| 1408 | .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) | 1663 | .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) |
| 1409 | { | 1664 | { |
| @@ -1419,43 +1674,42 @@ fn main() { | |||
| 1419 | let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; | 1674 | let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; |
| 1420 | let gpio_stride = 0x400; | 1675 | let gpio_stride = 0x400; |
| 1421 | 1676 | ||
| 1422 | for p in METADATA.peripherals { | 1677 | for pin in METADATA.pins { |
| 1423 | if let Some(regs) = &p.registers { | 1678 | let port_letter = pin.name.chars().nth(1).unwrap(); |
| 1424 | if regs.kind == "gpio" { | 1679 | let pname = format!("GPIO{}", port_letter); |
| 1425 | let port_letter = p.name.chars().nth(4).unwrap(); | 1680 | let p = METADATA.peripherals.iter().find(|p| p.name == pname).unwrap(); |
| 1426 | assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride); | 1681 | assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride); |
| 1427 | let port_num = (p.address as u32 - gpio_base) / gpio_stride; | 1682 | let port_num = (p.address as u32 - gpio_base) / gpio_stride; |
| 1428 | 1683 | let pin_num: u32 = pin.name[2..].parse().unwrap(); | |
| 1429 | for pin_num in 0u32..16 { | 1684 | |
| 1430 | let pin_name = format!("P{}{}", port_letter, pin_num); | 1685 | pins_table.push(vec![ |
| 1431 | 1686 | pin.name.to_string(), | |
| 1432 | pins_table.push(vec![ | 1687 | p.name.to_string(), |
| 1433 | pin_name.clone(), | 1688 | port_num.to_string(), |
| 1434 | p.name.to_string(), | 1689 | pin_num.to_string(), |
| 1435 | port_num.to_string(), | 1690 | format!("EXTI{}", pin_num), |
| 1436 | pin_num.to_string(), | 1691 | ]); |
| 1437 | format!("EXTI{}", pin_num), | 1692 | |
| 1438 | ]); | 1693 | // If we have the split pins, we need to do a little extra work: |
| 1439 | 1694 | // Add the "_C" variant to the table. The solution is not optimal, though. | |
| 1440 | // If we have the split pins, we need to do a little extra work: | 1695 | // Adding them only when the corresponding GPIOx also appears. |
| 1441 | // Add the "_C" variant to the table. The solution is not optimal, though. | 1696 | // This should avoid unintended side-effects as much as possible. |
| 1442 | // Adding them only when the corresponding GPIOx also appears. | 1697 | #[cfg(feature = "_split-pins-enabled")] |
| 1443 | // This should avoid unintended side-effects as much as possible. | 1698 | for split_feature in &split_features { |
| 1444 | #[cfg(feature = "_split-pins-enabled")] | 1699 | if split_feature.pin_name_without_c == pin.name { |
| 1445 | for split_feature in &split_features { | 1700 | pins_table.push(vec![ |
| 1446 | if split_feature.pin_name_without_c == pin_name { | 1701 | split_feature.pin_name_with_c.to_string(), |
| 1447 | pins_table.push(vec![ | 1702 | p.name.to_string(), |
| 1448 | split_feature.pin_name_with_c.to_string(), | 1703 | port_num.to_string(), |
| 1449 | p.name.to_string(), | 1704 | pin_num.to_string(), |
| 1450 | port_num.to_string(), | 1705 | format!("EXTI{}", pin_num), |
| 1451 | pin_num.to_string(), | 1706 | ]); |
| 1452 | format!("EXTI{}", pin_num), | ||
| 1453 | ]); | ||
| 1454 | } | ||
| 1455 | } | ||
| 1456 | } | ||
| 1457 | } | 1707 | } |
| 1708 | } | ||
| 1709 | } | ||
| 1458 | 1710 | ||
| 1711 | for p in METADATA.peripherals { | ||
| 1712 | if let Some(regs) = &p.registers { | ||
| 1459 | if regs.kind == "adc" { | 1713 | if regs.kind == "adc" { |
| 1460 | let adc_num = p.name.strip_prefix("ADC").unwrap(); | 1714 | let adc_num = p.name.strip_prefix("ADC").unwrap(); |
| 1461 | let mut adc_common = None; | 1715 | let mut adc_common = None; |
| @@ -1494,6 +1748,36 @@ fn main() { | |||
| 1494 | .flat_map(|p| &p.registers) | 1748 | .flat_map(|p| &p.registers) |
| 1495 | .any(|p| p.kind == "dmamux"); | 1749 | .any(|p| p.kind == "dmamux"); |
| 1496 | 1750 | ||
| 1751 | let mut dma_irqs: BTreeMap<&str, Vec<String>> = BTreeMap::new(); | ||
| 1752 | |||
| 1753 | for p in METADATA.peripherals { | ||
| 1754 | if let Some(r) = &p.registers { | ||
| 1755 | if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" || r.kind == "lpdma" { | ||
| 1756 | for irq in p.interrupts { | ||
| 1757 | let ch_name = format!("{}_{}", p.name, irq.signal); | ||
| 1758 | let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap(); | ||
| 1759 | |||
| 1760 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. | ||
| 1761 | if has_dmamux && ch.dmamux.is_none() { | ||
| 1762 | continue; | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | dma_irqs.entry(irq.interrupt).or_default().push(ch_name); | ||
| 1766 | } | ||
| 1767 | } | ||
| 1768 | } | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | #[cfg(feature = "_dual-core")] | ||
| 1772 | let mut dma_ch_to_irq: BTreeMap<&str, Vec<String>> = BTreeMap::new(); | ||
| 1773 | |||
| 1774 | #[cfg(feature = "_dual-core")] | ||
| 1775 | for (irq, channels) in &dma_irqs { | ||
| 1776 | for channel in channels { | ||
| 1777 | dma_ch_to_irq.entry(channel).or_default().push(irq.to_string()); | ||
| 1778 | } | ||
| 1779 | } | ||
| 1780 | |||
| 1497 | for (ch_idx, ch) in METADATA.dma_channels.iter().enumerate() { | 1781 | for (ch_idx, ch) in METADATA.dma_channels.iter().enumerate() { |
| 1498 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. | 1782 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. |
| 1499 | if has_dmamux && ch.dmamux.is_none() { | 1783 | if has_dmamux && ch.dmamux.is_none() { |
| @@ -1502,6 +1786,16 @@ fn main() { | |||
| 1502 | 1786 | ||
| 1503 | let name = format_ident!("{}", ch.name); | 1787 | let name = format_ident!("{}", ch.name); |
| 1504 | let idx = ch_idx as u8; | 1788 | let idx = ch_idx as u8; |
| 1789 | #[cfg(feature = "_dual-core")] | ||
| 1790 | let irq = { | ||
| 1791 | let irq_name = if let Some(x) = &dma_ch_to_irq.get(ch.name) { | ||
| 1792 | format_ident!("{}", x.get(0).unwrap()) | ||
| 1793 | } else { | ||
| 1794 | panic!("failed to find dma interrupt") | ||
| 1795 | }; | ||
| 1796 | quote!(crate::pac::Interrupt::#irq_name) | ||
| 1797 | }; | ||
| 1798 | |||
| 1505 | g.extend(quote!(dma_channel_impl!(#name, #idx);)); | 1799 | g.extend(quote!(dma_channel_impl!(#name, #idx);)); |
| 1506 | 1800 | ||
| 1507 | let dma = format_ident!("{}", ch.dma); | 1801 | let dma = format_ident!("{}", ch.dma); |
| @@ -1532,10 +1826,20 @@ fn main() { | |||
| 1532 | None => quote!(), | 1826 | None => quote!(), |
| 1533 | }; | 1827 | }; |
| 1534 | 1828 | ||
| 1829 | #[cfg(not(feature = "_dual-core"))] | ||
| 1830 | dmas.extend(quote! { | ||
| 1831 | crate::dma::ChannelInfo { | ||
| 1832 | dma: #dma_info, | ||
| 1833 | num: #ch_num, | ||
| 1834 | #dmamux | ||
| 1835 | }, | ||
| 1836 | }); | ||
| 1837 | #[cfg(feature = "_dual-core")] | ||
| 1535 | dmas.extend(quote! { | 1838 | dmas.extend(quote! { |
| 1536 | crate::dma::ChannelInfo { | 1839 | crate::dma::ChannelInfo { |
| 1537 | dma: #dma_info, | 1840 | dma: #dma_info, |
| 1538 | num: #ch_num, | 1841 | num: #ch_num, |
| 1842 | irq: #irq, | ||
| 1539 | #dmamux | 1843 | #dmamux |
| 1540 | }, | 1844 | }, |
| 1541 | }); | 1845 | }); |
| @@ -1544,26 +1848,6 @@ fn main() { | |||
| 1544 | // ======== | 1848 | // ======== |
| 1545 | // Generate DMA IRQs. | 1849 | // Generate DMA IRQs. |
| 1546 | 1850 | ||
| 1547 | let mut dma_irqs: BTreeMap<&str, Vec<String>> = BTreeMap::new(); | ||
| 1548 | |||
| 1549 | for p in METADATA.peripherals { | ||
| 1550 | if let Some(r) = &p.registers { | ||
| 1551 | if r.kind == "dma" || r.kind == "bdma" || r.kind == "gpdma" { | ||
| 1552 | for irq in p.interrupts { | ||
| 1553 | let ch_name = format!("{}_{}", p.name, irq.signal); | ||
| 1554 | let ch = METADATA.dma_channels.iter().find(|c| c.name == ch_name).unwrap(); | ||
| 1555 | |||
| 1556 | // Some H7 chips have BDMA1 hardcoded for DFSDM, ie no DMAMUX. It's unsupported, skip it. | ||
| 1557 | if has_dmamux && ch.dmamux.is_none() { | ||
| 1558 | continue; | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | dma_irqs.entry(irq.interrupt).or_default().push(ch_name); | ||
| 1562 | } | ||
| 1563 | } | ||
| 1564 | } | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | let dma_irqs: TokenStream = dma_irqs | 1851 | let dma_irqs: TokenStream = dma_irqs |
| 1568 | .iter() | 1852 | .iter() |
| 1569 | .map(|(irq, channels)| { | 1853 | .map(|(irq, channels)| { |
| @@ -1589,6 +1873,100 @@ fn main() { | |||
| 1589 | pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas]; | 1873 | pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas]; |
| 1590 | }); | 1874 | }); |
| 1591 | 1875 | ||
| 1876 | // ======== | ||
| 1877 | // Generate gpio_block() function | ||
| 1878 | |||
| 1879 | let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as usize; | ||
| 1880 | let gpio_stride = 0x400 as usize; | ||
| 1881 | |||
| 1882 | for p in METADATA.peripherals { | ||
| 1883 | if let Some(bi) = &p.registers { | ||
| 1884 | if bi.kind == "gpio" { | ||
| 1885 | assert_eq!(0, (p.address as usize - gpio_base) % gpio_stride); | ||
| 1886 | } | ||
| 1887 | } | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | g.extend(quote!( | ||
| 1891 | pub fn gpio_block(n: usize) -> crate::pac::gpio::Gpio {{ | ||
| 1892 | unsafe {{ crate::pac::gpio::Gpio::from_ptr((#gpio_base + #gpio_stride*n) as _) }} | ||
| 1893 | }} | ||
| 1894 | )); | ||
| 1895 | |||
| 1896 | // ======== | ||
| 1897 | // Generate flash constants | ||
| 1898 | |||
| 1899 | let flash_regions: Vec<&MemoryRegion> = memory | ||
| 1900 | .iter() | ||
| 1901 | .filter(|x| x.kind == MemoryRegionKind::Flash && x.name.starts_with("BANK_")) | ||
| 1902 | .collect(); | ||
| 1903 | let first_flash = flash_regions.first().unwrap(); | ||
| 1904 | let total_flash_size = flash_regions | ||
| 1905 | .iter() | ||
| 1906 | .map(|x| x.size) | ||
| 1907 | .reduce(|acc, item| acc + item) | ||
| 1908 | .unwrap(); | ||
| 1909 | let write_sizes: HashSet<_> = flash_regions | ||
| 1910 | .iter() | ||
| 1911 | .map(|r| r.settings.as_ref().unwrap().write_size) | ||
| 1912 | .collect(); | ||
| 1913 | assert_eq!(1, write_sizes.len()); | ||
| 1914 | |||
| 1915 | let flash_base = first_flash.address as usize; | ||
| 1916 | let total_flash_size = total_flash_size as usize; | ||
| 1917 | let write_size = (*write_sizes.iter().next().unwrap()) as usize; | ||
| 1918 | |||
| 1919 | g.extend(quote!( | ||
| 1920 | pub const FLASH_BASE: usize = #flash_base; | ||
| 1921 | pub const FLASH_SIZE: usize = #total_flash_size; | ||
| 1922 | pub const WRITE_SIZE: usize = #write_size; | ||
| 1923 | )); | ||
| 1924 | |||
| 1925 | // ======== | ||
| 1926 | // Generate EEPROM constants | ||
| 1927 | |||
| 1928 | cfgs.declare("eeprom"); | ||
| 1929 | |||
| 1930 | let eeprom_memory_regions: Vec<&MemoryRegion> = | ||
| 1931 | memory.iter().filter(|x| x.kind == MemoryRegionKind::Eeprom).collect(); | ||
| 1932 | |||
| 1933 | if !eeprom_memory_regions.is_empty() { | ||
| 1934 | cfgs.enable("eeprom"); | ||
| 1935 | |||
| 1936 | let mut sorted_eeprom_regions = eeprom_memory_regions.clone(); | ||
| 1937 | sorted_eeprom_regions.sort_by_key(|r| r.address); | ||
| 1938 | |||
| 1939 | let first_eeprom_address = sorted_eeprom_regions[0].address; | ||
| 1940 | let mut total_eeprom_size = 0; | ||
| 1941 | let mut current_expected_address = first_eeprom_address; | ||
| 1942 | |||
| 1943 | for region in sorted_eeprom_regions.iter() { | ||
| 1944 | if region.address != current_expected_address { | ||
| 1945 | // For STM32L0 and STM32L1, EEPROM regions (if multiple) are expected to be contiguous. | ||
| 1946 | // If they are not, this indicates an issue with the chip metadata or an unsupported configuration. | ||
| 1947 | panic!( | ||
| 1948 | "EEPROM regions for chip {} are not contiguous, which is unexpected for L0/L1 series. \ | ||
| 1949 | First region: '{}' at {:#X}. Found next non-contiguous region: '{}' at {:#X}. \ | ||
| 1950 | Please verify chip metadata. Embassy currently assumes contiguous EEPROM for these series.", | ||
| 1951 | chip_name, sorted_eeprom_regions[0].name, first_eeprom_address, region.name, region.address | ||
| 1952 | ); | ||
| 1953 | } | ||
| 1954 | total_eeprom_size += region.size; | ||
| 1955 | current_expected_address += region.size; | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | let eeprom_base_usize = first_eeprom_address as usize; | ||
| 1959 | let total_eeprom_size_usize = total_eeprom_size as usize; | ||
| 1960 | |||
| 1961 | g.extend(quote! { | ||
| 1962 | pub const EEPROM_BASE: usize = #eeprom_base_usize; | ||
| 1963 | pub const EEPROM_SIZE: usize = #total_eeprom_size_usize; | ||
| 1964 | }); | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | // ======== | ||
| 1968 | // Generate macro-tables | ||
| 1969 | |||
| 1592 | for irq in METADATA.interrupts { | 1970 | for irq in METADATA.interrupts { |
| 1593 | let name = irq.name.to_ascii_uppercase(); | 1971 | let name = irq.name.to_ascii_uppercase(); |
| 1594 | interrupts_table.push(vec![name.clone()]); | 1972 | interrupts_table.push(vec![name.clone()]); |
| @@ -1683,6 +2061,11 @@ fn main() { | |||
| 1683 | } | 2061 | } |
| 1684 | 2062 | ||
| 1685 | println!("cargo:rerun-if-changed=build.rs"); | 2063 | println!("cargo:rerun-if-changed=build.rs"); |
| 2064 | |||
| 2065 | if cfg!(feature = "memory-x") { | ||
| 2066 | gen_memory_x(memory, out_dir); | ||
| 2067 | println!("cargo:rustc-link-search={}", out_dir.display()); | ||
| 2068 | } | ||
| 1686 | } | 2069 | } |
| 1687 | 2070 | ||
| 1688 | enum GetOneError { | 2071 | enum GetOneError { |
| @@ -1768,3 +2151,97 @@ fn rustfmt(path: impl AsRef<Path>) { | |||
| 1768 | } | 2151 | } |
| 1769 | } | 2152 | } |
| 1770 | } | 2153 | } |
| 2154 | |||
| 2155 | fn gen_memory_x(memory: &[MemoryRegion], out_dir: &Path) { | ||
| 2156 | let mut memory_x = String::new(); | ||
| 2157 | |||
| 2158 | let flash = get_memory_range(memory, MemoryRegionKind::Flash); | ||
| 2159 | let ram = get_memory_range(memory, MemoryRegionKind::Ram); | ||
| 2160 | |||
| 2161 | write!(memory_x, "MEMORY\n{{\n").unwrap(); | ||
| 2162 | writeln!( | ||
| 2163 | memory_x, | ||
| 2164 | " FLASH : ORIGIN = 0x{:08x}, LENGTH = {:>4}K /* {} */", | ||
| 2165 | flash.0, | ||
| 2166 | flash.1 / 1024, | ||
| 2167 | flash.2 | ||
| 2168 | ) | ||
| 2169 | .unwrap(); | ||
| 2170 | writeln!( | ||
| 2171 | memory_x, | ||
| 2172 | " RAM : ORIGIN = 0x{:08x}, LENGTH = {:>4}K /* {} */", | ||
| 2173 | ram.0, | ||
| 2174 | ram.1 / 1024, | ||
| 2175 | ram.2 | ||
| 2176 | ) | ||
| 2177 | .unwrap(); | ||
| 2178 | write!(memory_x, "}}").unwrap(); | ||
| 2179 | |||
| 2180 | std::fs::write(out_dir.join("memory.x"), memory_x.as_bytes()).unwrap(); | ||
| 2181 | } | ||
| 2182 | |||
| 2183 | fn get_memory_range(memory: &[MemoryRegion], kind: MemoryRegionKind) -> (u32, u32, String) { | ||
| 2184 | let mut mems: Vec<_> = memory.iter().filter(|m| m.kind == kind && m.size != 0).collect(); | ||
| 2185 | mems.sort_by_key(|m| m.address); | ||
| 2186 | |||
| 2187 | let mut start = u32::MAX; | ||
| 2188 | let mut end = u32::MAX; | ||
| 2189 | let mut names = Vec::new(); | ||
| 2190 | let mut best: Option<(u32, u32, String)> = None; | ||
| 2191 | for m in mems { | ||
| 2192 | if !mem_filter(&METADATA.name, &m.name) { | ||
| 2193 | continue; | ||
| 2194 | } | ||
| 2195 | |||
| 2196 | if m.address != end { | ||
| 2197 | names = Vec::new(); | ||
| 2198 | start = m.address; | ||
| 2199 | end = m.address; | ||
| 2200 | } | ||
| 2201 | |||
| 2202 | end += m.size; | ||
| 2203 | names.push(m.name.to_string()); | ||
| 2204 | |||
| 2205 | if best.is_none() || end - start > best.as_ref().unwrap().1 { | ||
| 2206 | best = Some((start, end - start, names.join(" + "))); | ||
| 2207 | } | ||
| 2208 | } | ||
| 2209 | |||
| 2210 | best.unwrap() | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | fn mem_filter(chip: &str, region: &str) -> bool { | ||
| 2214 | // in STM32WB, SRAM2a/SRAM2b are reserved for the radio core. | ||
| 2215 | if chip.starts_with("STM32WB") | ||
| 2216 | && !chip.starts_with("STM32WBA") | ||
| 2217 | && !chip.starts_with("STM32WB0") | ||
| 2218 | && region.starts_with("SRAM2") | ||
| 2219 | { | ||
| 2220 | return false; | ||
| 2221 | } | ||
| 2222 | |||
| 2223 | true | ||
| 2224 | } | ||
| 2225 | |||
| 2226 | #[derive(Copy, Clone, Debug)] | ||
| 2227 | struct Frac { | ||
| 2228 | num: u32, | ||
| 2229 | denom: u32, | ||
| 2230 | } | ||
| 2231 | |||
| 2232 | impl Frac { | ||
| 2233 | fn simplify(self) -> Self { | ||
| 2234 | let d = gcd(self.num, self.denom); | ||
| 2235 | Self { | ||
| 2236 | num: self.num / d, | ||
| 2237 | denom: self.denom / d, | ||
| 2238 | } | ||
| 2239 | } | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | fn gcd(a: u32, b: u32) -> u32 { | ||
| 2243 | if b == 0 { | ||
| 2244 | return a; | ||
| 2245 | } | ||
| 2246 | gcd(b, a % b) | ||
| 2247 | } | ||
diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs new file mode 100644 index 000000000..936ad7413 --- /dev/null +++ b/embassy-stm32/src/adc/c0.rs | |||
| @@ -0,0 +1,467 @@ | |||
| 1 | use pac::adc::vals::Scandir; | ||
| 2 | #[allow(unused)] | ||
| 3 | use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; | ||
| 4 | use pac::adccommon::vals::Presc; | ||
| 5 | |||
| 6 | use super::{ | ||
| 7 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | ||
| 8 | }; | ||
| 9 | use crate::dma::Transfer; | ||
| 10 | use crate::time::Hertz; | ||
| 11 | use crate::{pac, rcc, Peri}; | ||
| 12 | |||
| 13 | /// Default VREF voltage used for sample conversion to millivolts. | ||
| 14 | pub const VREF_DEFAULT_MV: u32 = 3300; | ||
| 15 | /// VREF voltage used for factory calibration of VREFINTCAL register. | ||
| 16 | pub const VREF_CALIB_MV: u32 = 3300; | ||
| 17 | |||
| 18 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); | ||
| 19 | |||
| 20 | const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; | ||
| 21 | |||
| 22 | const TEMP_CHANNEL: u8 = 9; | ||
| 23 | const VREF_CHANNEL: u8 = 10; | ||
| 24 | |||
| 25 | const NUM_HW_CHANNELS: u8 = 22; | ||
| 26 | const CHSELR_SQ_SIZE: usize = 8; | ||
| 27 | const CHSELR_SQ_MAX_CHANNEL: u8 = 14; | ||
| 28 | const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; | ||
| 29 | |||
| 30 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, | ||
| 31 | // this currently cannot be modeled with stm32-data, | ||
| 32 | // so these are available from the software on all ADCs. | ||
| 33 | /// Internal voltage reference channel. | ||
| 34 | pub struct VrefInt; | ||
| 35 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 36 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 37 | fn channel(&self) -> u8 { | ||
| 38 | VREF_CHANNEL | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Internal temperature channel. | ||
| 43 | pub struct Temperature; | ||
| 44 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 45 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 46 | fn channel(&self) -> u8 { | ||
| 47 | TEMP_CHANNEL | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | #[derive(Debug)] | ||
| 52 | pub enum Prescaler { | ||
| 53 | NotDivided, | ||
| 54 | DividedBy2, | ||
| 55 | DividedBy4, | ||
| 56 | DividedBy6, | ||
| 57 | DividedBy8, | ||
| 58 | DividedBy10, | ||
| 59 | DividedBy12, | ||
| 60 | DividedBy16, | ||
| 61 | DividedBy32, | ||
| 62 | DividedBy64, | ||
| 63 | DividedBy128, | ||
| 64 | DividedBy256, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Prescaler { | ||
| 68 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 69 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 70 | match raw_prescaler { | ||
| 71 | 0 => Self::NotDivided, | ||
| 72 | 1 => Self::DividedBy2, | ||
| 73 | 2..=3 => Self::DividedBy4, | ||
| 74 | 4..=5 => Self::DividedBy6, | ||
| 75 | 6..=7 => Self::DividedBy8, | ||
| 76 | 8..=9 => Self::DividedBy10, | ||
| 77 | 10..=11 => Self::DividedBy12, | ||
| 78 | _ => unimplemented!(), | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | #[allow(unused)] | ||
| 83 | fn divisor(&self) -> u32 { | ||
| 84 | match self { | ||
| 85 | Prescaler::NotDivided => 1, | ||
| 86 | Prescaler::DividedBy2 => 2, | ||
| 87 | Prescaler::DividedBy4 => 4, | ||
| 88 | Prescaler::DividedBy6 => 6, | ||
| 89 | Prescaler::DividedBy8 => 8, | ||
| 90 | Prescaler::DividedBy10 => 10, | ||
| 91 | Prescaler::DividedBy12 => 12, | ||
| 92 | Prescaler::DividedBy16 => 16, | ||
| 93 | Prescaler::DividedBy32 => 32, | ||
| 94 | Prescaler::DividedBy64 => 64, | ||
| 95 | Prescaler::DividedBy128 => 128, | ||
| 96 | Prescaler::DividedBy256 => 256, | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | fn presc(&self) -> Presc { | ||
| 101 | match self { | ||
| 102 | Prescaler::NotDivided => Presc::DIV1, | ||
| 103 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 104 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 105 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 106 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 107 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 108 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 109 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 110 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 111 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 112 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 113 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | #[cfg(feature = "defmt")] | ||
| 119 | impl<'a> defmt::Format for Prescaler { | ||
| 120 | fn format(&self, fmt: defmt::Formatter) { | ||
| 121 | match self { | ||
| 122 | Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), | ||
| 123 | Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), | ||
| 124 | Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), | ||
| 125 | Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), | ||
| 126 | Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), | ||
| 127 | Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), | ||
| 128 | Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), | ||
| 129 | Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), | ||
| 130 | Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), | ||
| 131 | Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), | ||
| 132 | Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), | ||
| 133 | Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Number of samples used for averaging. | ||
| 139 | /// TODO: Implement hardware averaging setting. | ||
| 140 | #[allow(unused)] | ||
| 141 | pub enum Averaging { | ||
| 142 | Disabled, | ||
| 143 | Samples2, | ||
| 144 | Samples4, | ||
| 145 | Samples8, | ||
| 146 | Samples16, | ||
| 147 | Samples32, | ||
| 148 | Samples64, | ||
| 149 | Samples128, | ||
| 150 | Samples256, | ||
| 151 | Samples512, | ||
| 152 | Samples1024, | ||
| 153 | } | ||
| 154 | |||
| 155 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 156 | /// Create a new ADC driver. | ||
| 157 | pub fn new(adc: Peri<'d, T>, sample_time: SampleTime, resolution: Resolution) -> Self { | ||
| 158 | rcc::enable_and_reset::<T>(); | ||
| 159 | |||
| 160 | T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); | ||
| 161 | |||
| 162 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | ||
| 163 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | ||
| 164 | |||
| 165 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | ||
| 166 | debug!("ADC frequency set to {}", frequency); | ||
| 167 | |||
| 168 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 169 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | ||
| 170 | } | ||
| 171 | |||
| 172 | let mut s = Self { | ||
| 173 | adc, | ||
| 174 | sample_time: SampleTime::from_bits(0), | ||
| 175 | }; | ||
| 176 | |||
| 177 | s.power_up(); | ||
| 178 | |||
| 179 | s.set_resolution(resolution); | ||
| 180 | |||
| 181 | s.calibrate(); | ||
| 182 | |||
| 183 | s.enable(); | ||
| 184 | |||
| 185 | s.configure_default(); | ||
| 186 | |||
| 187 | s.set_sample_time_all_channels(sample_time); | ||
| 188 | |||
| 189 | s | ||
| 190 | } | ||
| 191 | |||
| 192 | fn power_up(&mut self) { | ||
| 193 | T::regs().cr().modify(|reg| { | ||
| 194 | reg.set_advregen(true); | ||
| 195 | }); | ||
| 196 | |||
| 197 | // "The software must wait for the ADC voltage regulator startup time." | ||
| 198 | // See datasheet for the value. | ||
| 199 | blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); | ||
| 200 | } | ||
| 201 | |||
| 202 | fn calibrate(&mut self) { | ||
| 203 | // We have to make sure AUTOFF is OFF, but keep its value after calibration. | ||
| 204 | let autoff_value = T::regs().cfgr1().read().autoff(); | ||
| 205 | T::regs().cfgr1().modify(|w| w.set_autoff(false)); | ||
| 206 | |||
| 207 | T::regs().cr().modify(|w| w.set_adcal(true)); | ||
| 208 | |||
| 209 | // "ADCAL bit stays at 1 during all the calibration sequence." | ||
| 210 | // "It is then cleared by hardware as soon the calibration completes." | ||
| 211 | while T::regs().cr().read().adcal() {} | ||
| 212 | |||
| 213 | debug!("ADC calibration value: {}.", T::regs().dr().read().data()); | ||
| 214 | |||
| 215 | T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); | ||
| 216 | } | ||
| 217 | |||
| 218 | fn enable(&mut self) { | ||
| 219 | T::regs().isr().modify(|w| w.set_adrdy(true)); | ||
| 220 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 221 | // ADRDY is "ADC ready". Wait until it will be True. | ||
| 222 | while !T::regs().isr().read().adrdy() {} | ||
| 223 | } | ||
| 224 | |||
| 225 | fn configure_default(&mut self) { | ||
| 226 | // single conversion mode, software trigger | ||
| 227 | T::regs().cfgr1().modify(|w| { | ||
| 228 | w.set_cont(false); | ||
| 229 | w.set_exten(Exten::DISABLED); | ||
| 230 | w.set_align(Align::RIGHT); | ||
| 231 | }); | ||
| 232 | } | ||
| 233 | |||
| 234 | /// Enable reading the voltage reference internal channel. | ||
| 235 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 236 | T::common_regs().ccr().modify(|reg| { | ||
| 237 | reg.set_vrefen(true); | ||
| 238 | }); | ||
| 239 | |||
| 240 | VrefInt {} | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Enable reading the temperature internal channel. | ||
| 244 | pub fn enable_temperature(&self) -> Temperature { | ||
| 245 | debug!("Ensure that sample time is set to more than temperature sensor T_start from the datasheet!"); | ||
| 246 | T::common_regs().ccr().modify(|reg| { | ||
| 247 | reg.set_tsen(true); | ||
| 248 | }); | ||
| 249 | |||
| 250 | Temperature {} | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Set the ADC sample time. | ||
| 254 | /// Shall only be called when ADC is not converting. | ||
| 255 | pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { | ||
| 256 | self.sample_time = sample_time; | ||
| 257 | |||
| 258 | // Set all channels to use SMP1 field as source. | ||
| 259 | T::regs().smpr().modify(|w| { | ||
| 260 | w.smpsel(0); | ||
| 261 | w.set_smp1(sample_time); | ||
| 262 | }); | ||
| 263 | } | ||
| 264 | |||
| 265 | /// Set the ADC resolution. | ||
| 266 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 267 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Perform a single conversion. | ||
| 271 | fn convert(&mut self) -> u16 { | ||
| 272 | // Set single conversion mode. | ||
| 273 | T::regs().cfgr1().modify(|w| w.set_cont(false)); | ||
| 274 | |||
| 275 | // Start conversion | ||
| 276 | T::regs().cr().modify(|reg| { | ||
| 277 | reg.set_adstart(true); | ||
| 278 | }); | ||
| 279 | |||
| 280 | // Waiting for End Of Conversion (EOC). | ||
| 281 | while !T::regs().isr().read().eoc() {} | ||
| 282 | |||
| 283 | T::regs().dr().read().data() as u16 | ||
| 284 | } | ||
| 285 | |||
| 286 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 287 | Self::configure_channel(channel); | ||
| 288 | T::regs().cfgr1().write(|reg| { | ||
| 289 | reg.set_chselrmod(false); | ||
| 290 | reg.set_align(Align::RIGHT); | ||
| 291 | }); | ||
| 292 | self.convert() | ||
| 293 | } | ||
| 294 | |||
| 295 | fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator<Item = &'a mut AnyAdcChannel<T>>) { | ||
| 296 | assert!( | ||
| 297 | channel_sequence.len() <= CHSELR_SQ_SIZE, | ||
| 298 | "Seqenced read set cannot be more than {} in size.", | ||
| 299 | CHSELR_SQ_SIZE | ||
| 300 | ); | ||
| 301 | let mut last_sq_set: usize = 0; | ||
| 302 | T::regs().chselr_sq().write(|w| { | ||
| 303 | for (i, channel) in channel_sequence.enumerate() { | ||
| 304 | assert!( | ||
| 305 | channel.channel() <= CHSELR_SQ_MAX_CHANNEL, | ||
| 306 | "Sequencer only support HW channels smaller than {}.", | ||
| 307 | CHSELR_SQ_MAX_CHANNEL | ||
| 308 | ); | ||
| 309 | w.set_sq(i, channel.channel()); | ||
| 310 | last_sq_set = i; | ||
| 311 | } | ||
| 312 | |||
| 313 | for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { | ||
| 314 | w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); | ||
| 315 | } | ||
| 316 | }); | ||
| 317 | |||
| 318 | Self::apply_channel_conf() | ||
| 319 | } | ||
| 320 | |||
| 321 | async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma<T>>, readings: &mut [u16]) { | ||
| 322 | // Enable overrun control, so no new DMA requests will be generated until | ||
| 323 | // previous DR values is read. | ||
| 324 | T::regs().isr().modify(|reg| { | ||
| 325 | reg.set_ovr(true); | ||
| 326 | }); | ||
| 327 | |||
| 328 | // Set continuous mode with oneshot dma. | ||
| 329 | T::regs().cfgr1().modify(|reg| { | ||
| 330 | reg.set_discen(false); | ||
| 331 | reg.set_cont(true); | ||
| 332 | reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); | ||
| 333 | reg.set_dmaen(true); | ||
| 334 | reg.set_ovrmod(Ovrmod::PRESERVE); | ||
| 335 | }); | ||
| 336 | |||
| 337 | let request = rx_dma.request(); | ||
| 338 | let transfer = unsafe { | ||
| 339 | Transfer::new_read( | ||
| 340 | rx_dma, | ||
| 341 | request, | ||
| 342 | T::regs().dr().as_ptr() as *mut u16, | ||
| 343 | readings, | ||
| 344 | Default::default(), | ||
| 345 | ) | ||
| 346 | }; | ||
| 347 | |||
| 348 | // Start conversion. | ||
| 349 | T::regs().cr().modify(|reg| { | ||
| 350 | reg.set_adstart(true); | ||
| 351 | }); | ||
| 352 | |||
| 353 | // Wait for conversion sequence to finish. | ||
| 354 | transfer.await; | ||
| 355 | |||
| 356 | // Ensure conversions are finished. | ||
| 357 | Self::cancel_conversions(); | ||
| 358 | |||
| 359 | // Reset configuration. | ||
| 360 | T::regs().cfgr1().modify(|reg| { | ||
| 361 | reg.set_cont(false); | ||
| 362 | reg.set_dmacfg(Dmacfg::from_bits(0)); | ||
| 363 | reg.set_dmaen(false); | ||
| 364 | }); | ||
| 365 | } | ||
| 366 | |||
| 367 | /// Read one or multiple ADC channels using DMA in hardware order. | ||
| 368 | /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. | ||
| 369 | /// Readings won't be in the same order as in the `set`! | ||
| 370 | /// | ||
| 371 | /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use | ||
| 372 | /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). | ||
| 373 | /// TODO(chudsaviet): externalize generic code and merge with read(). | ||
| 374 | pub async fn read_in_hw_order( | ||
| 375 | &mut self, | ||
| 376 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 377 | hw_channel_selection: u32, | ||
| 378 | scandir: Scandir, | ||
| 379 | readings: &mut [u16], | ||
| 380 | ) { | ||
| 381 | assert!( | ||
| 382 | hw_channel_selection != 0, | ||
| 383 | "Some bits in `hw_channel_selection` shall be set." | ||
| 384 | ); | ||
| 385 | assert!( | ||
| 386 | (hw_channel_selection >> NUM_HW_CHANNELS) == 0, | ||
| 387 | "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", | ||
| 388 | NUM_HW_CHANNELS | ||
| 389 | ); | ||
| 390 | // To check for correct readings slice size, we shall solve Hamming weight problem, | ||
| 391 | // which is either slow or memory consuming. | ||
| 392 | // Since we have limited resources, we don't do it here. | ||
| 393 | // Not doing this have a great potential for a bug through. | ||
| 394 | |||
| 395 | // Ensure no conversions are ongoing. | ||
| 396 | Self::cancel_conversions(); | ||
| 397 | |||
| 398 | T::regs().cfgr1().modify(|reg| { | ||
| 399 | reg.set_chselrmod(false); | ||
| 400 | reg.set_scandir(scandir); | ||
| 401 | reg.set_align(Align::RIGHT); | ||
| 402 | }); | ||
| 403 | |||
| 404 | // Set required channels for multi-convert. | ||
| 405 | unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } | ||
| 406 | |||
| 407 | Self::apply_channel_conf(); | ||
| 408 | |||
| 409 | self.dma_convert(rx_dma, readings).await | ||
| 410 | } | ||
| 411 | |||
| 412 | // Read ADC channels in specified order using DMA (CHSELRMOD = 1). | ||
| 413 | // In STM32C0, only lower 14 ADC channels can be read this way. | ||
| 414 | // For other channels, use `read_in_hw_order()` or blocking read. | ||
| 415 | pub async fn read( | ||
| 416 | &mut self, | ||
| 417 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 418 | channel_sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 419 | readings: &mut [u16], | ||
| 420 | ) { | ||
| 421 | assert!( | ||
| 422 | channel_sequence.len() != 0, | ||
| 423 | "Asynchronous read channel sequence cannot be empty." | ||
| 424 | ); | ||
| 425 | assert!( | ||
| 426 | channel_sequence.len() == readings.len(), | ||
| 427 | "Channel sequence length must be equal to readings length." | ||
| 428 | ); | ||
| 429 | |||
| 430 | // Ensure no conversions are ongoing. | ||
| 431 | Self::cancel_conversions(); | ||
| 432 | |||
| 433 | T::regs().cfgr1().modify(|reg| { | ||
| 434 | reg.set_chselrmod(true); | ||
| 435 | reg.set_align(Align::RIGHT); | ||
| 436 | }); | ||
| 437 | |||
| 438 | Self::setup_channel_sequencer(channel_sequence); | ||
| 439 | |||
| 440 | self.dma_convert(rx_dma, readings).await | ||
| 441 | } | ||
| 442 | |||
| 443 | fn configure_channel(channel: &mut impl AdcChannel<T>) { | ||
| 444 | channel.setup(); | ||
| 445 | // write() because we want all other bits to be set to 0. | ||
| 446 | T::regs() | ||
| 447 | .chselr() | ||
| 448 | .write(|w| w.set_chsel(channel.channel().into(), true)); | ||
| 449 | |||
| 450 | Self::apply_channel_conf(); | ||
| 451 | } | ||
| 452 | |||
| 453 | fn apply_channel_conf() { | ||
| 454 | // Trigger and wait for the channel selection procedure to complete. | ||
| 455 | T::regs().isr().modify(|w| w.set_ccrdy(false)); | ||
| 456 | while !T::regs().isr().read().ccrdy() {} | ||
| 457 | } | ||
| 458 | |||
| 459 | fn cancel_conversions() { | ||
| 460 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 461 | T::regs().cr().modify(|reg| { | ||
| 462 | reg.set_adstp(Adstp::STOP); | ||
| 463 | }); | ||
| 464 | while T::regs().cr().read().adstart() {} | ||
| 465 | } | ||
| 466 | } | ||
| 467 | } | ||
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index b37ec260f..3cdc9d8fb 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs | |||
| @@ -2,12 +2,12 @@ use core::future::poll_fn; | |||
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::into_ref; | ||
| 6 | |||
| 7 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 8 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; |
| 7 | use crate::interrupt::typelevel::Interrupt; | ||
| 8 | use crate::interrupt::{self}; | ||
| 9 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 10 | use crate::{interrupt, rcc, Peripheral}; | 10 | use crate::{rcc, Peri}; |
| 11 | 11 | ||
| 12 | pub const VDDA_CALIB_MV: u32 = 3300; | 12 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 13 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 13 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| @@ -22,12 +22,9 @@ pub struct InterruptHandler<T: Instance> { | |||
| 22 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 22 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 23 | unsafe fn on_interrupt() { | 23 | unsafe fn on_interrupt() { |
| 24 | if T::regs().sr().read().eoc() { | 24 | if T::regs().sr().read().eoc() { |
| 25 | T::regs().cr1().modify(|w| w.set_eocie(false)); | 25 | T::regs().cr1().modify(|w| w.set_eocie(false)); // End of Convert interrupt disable |
| 26 | } else { | 26 | T::state().waker.wake(); |
| 27 | return; | ||
| 28 | } | 27 | } |
| 29 | |||
| 30 | T::state().waker.wake(); | ||
| 31 | } | 28 | } |
| 32 | } | 29 | } |
| 33 | 30 | ||
| @@ -48,8 +45,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | |||
| 48 | } | 45 | } |
| 49 | 46 | ||
| 50 | impl<'d, T: Instance> Adc<'d, T> { | 47 | impl<'d, T: Instance> Adc<'d, T> { |
| 51 | pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self { | 48 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 52 | into_ref!(adc); | ||
| 53 | rcc::enable_and_reset::<T>(); | 49 | rcc::enable_and_reset::<T>(); |
| 54 | T::regs().cr2().modify(|reg| reg.set_adon(true)); | 50 | T::regs().cr2().modify(|reg| reg.set_adon(true)); |
| 55 | 51 | ||
| @@ -72,6 +68,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 72 | // One cycle after calibration | 68 | // One cycle after calibration |
| 73 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); | 69 | blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); |
| 74 | 70 | ||
| 71 | T::Interrupt::unpend(); | ||
| 72 | unsafe { T::Interrupt::enable() }; | ||
| 73 | |||
| 75 | Self { | 74 | Self { |
| 76 | adc, | 75 | adc, |
| 77 | sample_time: SampleTime::from_bits(0), | 76 | sample_time: SampleTime::from_bits(0), |
diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index ac88c9742..3aeb6f2c7 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs | |||
| @@ -2,13 +2,11 @@ use core::future::poll_fn; | |||
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::into_ref; | ||
| 6 | |||
| 7 | use super::blocking_delay_us; | 5 | use super::blocking_delay_us; |
| 8 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 6 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; |
| 9 | use crate::interrupt::typelevel::Interrupt; | 7 | use crate::interrupt::typelevel::Interrupt; |
| 10 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| 11 | use crate::{interrupt, rcc, Peripheral}; | 9 | use crate::{interrupt, rcc, Peri}; |
| 12 | 10 | ||
| 13 | pub const VDDA_CALIB_MV: u32 = 3300; | 11 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 14 | pub const ADC_MAX: u32 = (1 << 12) - 1; | 12 | pub const ADC_MAX: u32 = (1 << 12) - 1; |
| @@ -42,7 +40,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Vref { | |||
| 42 | impl Vref { | 40 | impl Vref { |
| 43 | /// The value that vref would be if vdda was at 3300mv | 41 | /// The value that vref would be if vdda was at 3300mv |
| 44 | pub fn value(&self) -> u16 { | 42 | pub fn value(&self) -> u16 { |
| 45 | crate::pac::VREFINTCAL.data().read().value() | 43 | crate::pac::VREFINTCAL.data().read() |
| 46 | } | 44 | } |
| 47 | } | 45 | } |
| 48 | 46 | ||
| @@ -56,13 +54,11 @@ impl<T: Instance> super::SealedAdcChannel<T> for Temperature { | |||
| 56 | 54 | ||
| 57 | impl<'d, T: Instance> Adc<'d, T> { | 55 | impl<'d, T: Instance> Adc<'d, T> { |
| 58 | pub fn new( | 56 | pub fn new( |
| 59 | adc: impl Peripheral<P = T> + 'd, | 57 | adc: Peri<'d, T>, |
| 60 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 58 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 61 | ) -> Self { | 59 | ) -> Self { |
| 62 | use crate::pac::adc::vals; | 60 | use crate::pac::adc::vals; |
| 63 | 61 | ||
| 64 | into_ref!(adc); | ||
| 65 | |||
| 66 | rcc::enable_and_reset::<T>(); | 62 | rcc::enable_and_reset::<T>(); |
| 67 | 63 | ||
| 68 | // Enable the adc regulator | 64 | // Enable the adc regulator |
diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 689c2871d..944e971bb 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs | |||
| @@ -3,14 +3,13 @@ use core::marker::PhantomData; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use embassy_futures::yield_now; | 5 | use embassy_futures::yield_now; |
| 6 | use embassy_hal_internal::into_ref; | ||
| 7 | use embassy_time::Instant; | 6 | use embassy_time::Instant; |
| 8 | 7 | ||
| 9 | use super::Resolution; | 8 | use super::Resolution; |
| 10 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; | 9 | use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; |
| 11 | use crate::interrupt::typelevel::Interrupt; | 10 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 13 | use crate::{interrupt, rcc, Peripheral}; | 12 | use crate::{interrupt, rcc, Peri}; |
| 14 | 13 | ||
| 15 | const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; | 14 | const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; |
| 16 | 15 | ||
| @@ -74,7 +73,7 @@ impl<T: Instance> super::SealedAdcChannel<T> for Vref<T> { | |||
| 74 | impl<T: Instance> Vref<T> { | 73 | impl<T: Instance> Vref<T> { |
| 75 | /// The value that vref would be if vdda was at 3000mv | 74 | /// The value that vref would be if vdda was at 3000mv |
| 76 | pub fn calibrated_value(&self) -> u16 { | 75 | pub fn calibrated_value(&self) -> u16 { |
| 77 | crate::pac::VREFINTCAL.data().read().value() | 76 | crate::pac::VREFINTCAL.data().read() |
| 78 | } | 77 | } |
| 79 | 78 | ||
| 80 | pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { | 79 | pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { |
| @@ -138,11 +137,9 @@ impl<T: Instance> Drop for Temperature<T> { | |||
| 138 | 137 | ||
| 139 | impl<'d, T: Instance> Adc<'d, T> { | 138 | impl<'d, T: Instance> Adc<'d, T> { |
| 140 | pub fn new( | 139 | pub fn new( |
| 141 | adc: impl Peripheral<P = T> + 'd, | 140 | adc: Peri<'d, T>, |
| 142 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 141 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 143 | ) -> Self { | 142 | ) -> Self { |
| 144 | into_ref!(adc); | ||
| 145 | |||
| 146 | rcc::enable_and_reset::<T>(); | 143 | rcc::enable_and_reset::<T>(); |
| 147 | 144 | ||
| 148 | //let r = T::regs(); | 145 | //let r = T::regs(); |
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index c1e584f59..1fce3085a 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -1,10 +1,17 @@ | |||
| 1 | #[allow(unused)] | 1 | #[allow(unused)] |
| 2 | #[cfg(stm32h7)] | ||
| 2 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; | 3 | use pac::adc::vals::{Adcaldif, Difsel, Exten}; |
| 4 | #[allow(unused)] | ||
| 5 | #[cfg(stm32g4)] | ||
| 6 | use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; | ||
| 3 | use pac::adccommon::vals::Presc; | 7 | use pac::adccommon::vals::Presc; |
| 8 | use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; | ||
| 4 | 9 | ||
| 5 | use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; | 10 | use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; |
| 11 | use crate::adc::SealedAdcChannel; | ||
| 12 | use crate::dma::Transfer; | ||
| 6 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 7 | use crate::{pac, rcc, Peripheral}; | 14 | use crate::{pac, rcc, Peri}; |
| 8 | 15 | ||
| 9 | /// Default VREF voltage used for sample conversion to millivolts. | 16 | /// Default VREF voltage used for sample conversion to millivolts. |
| 10 | pub const VREF_DEFAULT_MV: u32 = 3300; | 17 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -128,8 +135,7 @@ impl Prescaler { | |||
| 128 | 135 | ||
| 129 | impl<'d, T: Instance> Adc<'d, T> { | 136 | impl<'d, T: Instance> Adc<'d, T> { |
| 130 | /// Create a new ADC driver. | 137 | /// Create a new ADC driver. |
| 131 | pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self { | 138 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 132 | embassy_hal_internal::into_ref!(adc); | ||
| 133 | rcc::enable_and_reset::<T>(); | 139 | rcc::enable_and_reset::<T>(); |
| 134 | 140 | ||
| 135 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 141 | let prescaler = Prescaler::from_ker_ck(T::frequency()); |
| @@ -137,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 137 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 143 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); |
| 138 | 144 | ||
| 139 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 145 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); |
| 140 | info!("ADC frequency set to {} Hz", frequency.0); | 146 | trace!("ADC frequency set to {}", frequency); |
| 141 | 147 | ||
| 142 | if frequency > MAX_ADC_CLK_FREQ { | 148 | if frequency > MAX_ADC_CLK_FREQ { |
| 143 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 149 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); |
| @@ -165,32 +171,58 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 165 | reg.set_advregen(true); | 171 | reg.set_advregen(true); |
| 166 | }); | 172 | }); |
| 167 | 173 | ||
| 168 | blocking_delay_us(10); | 174 | blocking_delay_us(20); |
| 169 | } | 175 | } |
| 170 | 176 | ||
| 171 | fn configure_differential_inputs(&mut self) { | 177 | fn configure_differential_inputs(&mut self) { |
| 172 | T::regs().difsel().modify(|w| { | 178 | T::regs().difsel().modify(|w| { |
| 173 | for n in 0..18 { | 179 | for n in 0..18 { |
| 174 | w.set_difsel(n, Difsel::SINGLEENDED); | 180 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 175 | } | 181 | } |
| 176 | }); | 182 | }); |
| 177 | } | 183 | } |
| 178 | 184 | ||
| 179 | fn calibrate(&mut self) { | 185 | fn calibrate(&mut self) { |
| 180 | T::regs().cr().modify(|w| { | 186 | T::regs().cr().modify(|w| { |
| 181 | w.set_adcaldif(Adcaldif::SINGLEENDED); | 187 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); |
| 182 | }); | 188 | }); |
| 183 | 189 | ||
| 184 | T::regs().cr().modify(|w| w.set_adcal(true)); | 190 | T::regs().cr().modify(|w| w.set_adcal(true)); |
| 185 | 191 | ||
| 186 | while T::regs().cr().read().adcal() {} | 192 | while T::regs().cr().read().adcal() {} |
| 193 | |||
| 194 | blocking_delay_us(20); | ||
| 195 | |||
| 196 | T::regs().cr().modify(|w| { | ||
| 197 | w.set_adcaldif(Adcaldif::DIFFERENTIAL); | ||
| 198 | }); | ||
| 199 | |||
| 200 | T::regs().cr().modify(|w| w.set_adcal(true)); | ||
| 201 | |||
| 202 | while T::regs().cr().read().adcal() {} | ||
| 203 | |||
| 204 | blocking_delay_us(20); | ||
| 187 | } | 205 | } |
| 188 | 206 | ||
| 189 | fn enable(&mut self) { | 207 | fn enable(&mut self) { |
| 190 | T::regs().isr().write(|w| w.set_adrdy(true)); | 208 | // Make sure bits are off |
| 191 | T::regs().cr().modify(|w| w.set_aden(true)); | 209 | while T::regs().cr().read().addis() { |
| 192 | while !T::regs().isr().read().adrdy() {} | 210 | // spin |
| 193 | T::regs().isr().write(|w| w.set_adrdy(true)); | 211 | } |
| 212 | |||
| 213 | if !T::regs().cr().read().aden() { | ||
| 214 | // Enable ADC | ||
| 215 | T::regs().isr().modify(|reg| { | ||
| 216 | reg.set_adrdy(true); | ||
| 217 | }); | ||
| 218 | T::regs().cr().modify(|reg| { | ||
| 219 | reg.set_aden(true); | ||
| 220 | }); | ||
| 221 | |||
| 222 | while !T::regs().isr().read().adrdy() { | ||
| 223 | // spin | ||
| 224 | } | ||
| 225 | } | ||
| 194 | } | 226 | } |
| 195 | 227 | ||
| 196 | fn configure(&mut self) { | 228 | fn configure(&mut self) { |
| @@ -228,6 +260,68 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 228 | Vbat {} | 260 | Vbat {} |
| 229 | } | 261 | } |
| 230 | 262 | ||
| 263 | /// Enable differential channel. | ||
| 264 | /// Caution: | ||
| 265 | /// : When configuring the channel “i” in differential input mode, its negative input voltage VINN[i] | ||
| 266 | /// is connected to another channel. As a consequence, this channel is no longer usable in | ||
| 267 | /// single-ended mode or in differential mode and must never be configured to be converted. | ||
| 268 | /// Some channels are shared between ADC1/ADC2/ADC3/ADC4/ADC5: this can make the | ||
| 269 | /// channel on the other ADC unusable. The only exception is when ADC master and the slave | ||
| 270 | /// operate in interleaved mode. | ||
| 271 | #[cfg(stm32g4)] | ||
| 272 | pub fn set_differential_channel(&mut self, ch: usize, enable: bool) { | ||
| 273 | T::regs().cr().modify(|w| w.set_aden(false)); // disable adc | ||
| 274 | T::regs().difsel().modify(|w| { | ||
| 275 | w.set_difsel( | ||
| 276 | ch, | ||
| 277 | if enable { | ||
| 278 | Difsel::DIFFERENTIAL | ||
| 279 | } else { | ||
| 280 | Difsel::SINGLE_ENDED | ||
| 281 | }, | ||
| 282 | ); | ||
| 283 | }); | ||
| 284 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 285 | } | ||
| 286 | |||
| 287 | #[cfg(stm32g4)] | ||
| 288 | pub fn set_differential(&mut self, channel: &mut impl AdcChannel<T>, enable: bool) { | ||
| 289 | self.set_differential_channel(channel.channel() as usize, enable); | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Set oversampling shift. | ||
| 293 | #[cfg(stm32g4)] | ||
| 294 | pub fn set_oversampling_shift(&mut self, shift: u8) { | ||
| 295 | T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); | ||
| 296 | } | ||
| 297 | |||
| 298 | /// Set oversampling ratio. | ||
| 299 | #[cfg(stm32g4)] | ||
| 300 | pub fn set_oversampling_ratio(&mut self, ratio: u8) { | ||
| 301 | T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); | ||
| 302 | } | ||
| 303 | |||
| 304 | /// Enable oversampling in regular mode. | ||
| 305 | #[cfg(stm32g4)] | ||
| 306 | pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { | ||
| 307 | T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); | ||
| 308 | T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); | ||
| 309 | T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 310 | } | ||
| 311 | |||
| 312 | // Reads that are not implemented as INJECTED in "blocking_read" | ||
| 313 | // #[cfg(stm32g4)] | ||
| 314 | // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { | ||
| 315 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 316 | // } | ||
| 317 | |||
| 318 | // #[cfg(stm32g4)] | ||
| 319 | // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { | ||
| 320 | // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), | ||
| 321 | // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); | ||
| 322 | // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); | ||
| 323 | // } | ||
| 324 | |||
| 231 | /// Set the ADC sample time. | 325 | /// Set the ADC sample time. |
| 232 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | 326 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { |
| 233 | self.sample_time = sample_time; | 327 | self.sample_time = sample_time; |
| @@ -261,23 +355,146 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 261 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | 355 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { |
| 262 | channel.setup(); | 356 | channel.setup(); |
| 263 | 357 | ||
| 264 | self.read_channel(channel.channel()) | 358 | self.read_channel(channel) |
| 265 | } | 359 | } |
| 266 | 360 | ||
| 267 | fn read_channel(&mut self, channel: u8) -> u16 { | 361 | /// Read one or multiple ADC channels using DMA. |
| 362 | /// | ||
| 363 | /// `sequence` iterator and `readings` must have the same length. | ||
| 364 | /// | ||
| 365 | /// Example | ||
| 366 | /// ```rust,ignore | ||
| 367 | /// use embassy_stm32::adc::{Adc, AdcChannel} | ||
| 368 | /// | ||
| 369 | /// let mut adc = Adc::new(p.ADC1); | ||
| 370 | /// let mut adc_pin0 = p.PA0.into(); | ||
| 371 | /// let mut adc_pin1 = p.PA1.into(); | ||
| 372 | /// let mut measurements = [0u16; 2]; | ||
| 373 | /// | ||
| 374 | /// adc.read( | ||
| 375 | /// p.DMA1_CH2.reborrow(), | ||
| 376 | /// [ | ||
| 377 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | ||
| 378 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | ||
| 379 | /// ] | ||
| 380 | /// .into_iter(), | ||
| 381 | /// &mut measurements, | ||
| 382 | /// ) | ||
| 383 | /// .await; | ||
| 384 | /// defmt::info!("measurements: {}", measurements); | ||
| 385 | /// ``` | ||
| 386 | pub async fn read( | ||
| 387 | &mut self, | ||
| 388 | rx_dma: Peri<'_, impl RxDma<T>>, | ||
| 389 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | ||
| 390 | readings: &mut [u16], | ||
| 391 | ) { | ||
| 392 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 393 | assert!( | ||
| 394 | sequence.len() == readings.len(), | ||
| 395 | "Sequence length must be equal to readings length" | ||
| 396 | ); | ||
| 397 | assert!( | ||
| 398 | sequence.len() <= 16, | ||
| 399 | "Asynchronous read sequence cannot be more than 16 in length" | ||
| 400 | ); | ||
| 401 | |||
| 402 | // Ensure no conversions are ongoing and ADC is enabled. | ||
| 403 | Self::cancel_conversions(); | ||
| 404 | self.enable(); | ||
| 405 | |||
| 406 | // Set sequence length | ||
| 407 | T::regs().sqr1().modify(|w| { | ||
| 408 | w.set_l(sequence.len() as u8 - 1); | ||
| 409 | }); | ||
| 410 | |||
| 411 | // Configure channels and ranks | ||
| 412 | for (_i, (channel, sample_time)) in sequence.enumerate() { | ||
| 413 | Self::configure_channel(channel, sample_time); | ||
| 414 | |||
| 415 | match _i { | ||
| 416 | 0..=3 => { | ||
| 417 | T::regs().sqr1().modify(|w| { | ||
| 418 | w.set_sq(_i, channel.channel()); | ||
| 419 | }); | ||
| 420 | } | ||
| 421 | 4..=8 => { | ||
| 422 | T::regs().sqr2().modify(|w| { | ||
| 423 | w.set_sq(_i - 4, channel.channel()); | ||
| 424 | }); | ||
| 425 | } | ||
| 426 | 9..=13 => { | ||
| 427 | T::regs().sqr3().modify(|w| { | ||
| 428 | w.set_sq(_i - 9, channel.channel()); | ||
| 429 | }); | ||
| 430 | } | ||
| 431 | 14..=15 => { | ||
| 432 | T::regs().sqr4().modify(|w| { | ||
| 433 | w.set_sq(_i - 14, channel.channel()); | ||
| 434 | }); | ||
| 435 | } | ||
| 436 | _ => unreachable!(), | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | // Set continuous mode with oneshot dma. | ||
| 441 | // Clear overrun flag before starting transfer. | ||
| 442 | T::regs().isr().modify(|reg| { | ||
| 443 | reg.set_ovr(true); | ||
| 444 | }); | ||
| 445 | |||
| 446 | T::regs().cfgr().modify(|reg| { | ||
| 447 | reg.set_discen(false); | ||
| 448 | reg.set_cont(true); | ||
| 449 | reg.set_dmacfg(Dmacfg::ONE_SHOT); | ||
| 450 | reg.set_dmaen(Dmaen::ENABLE); | ||
| 451 | }); | ||
| 452 | |||
| 453 | let request = rx_dma.request(); | ||
| 454 | let transfer = unsafe { | ||
| 455 | Transfer::new_read( | ||
| 456 | rx_dma, | ||
| 457 | request, | ||
| 458 | T::regs().dr().as_ptr() as *mut u16, | ||
| 459 | readings, | ||
| 460 | Default::default(), | ||
| 461 | ) | ||
| 462 | }; | ||
| 463 | |||
| 464 | // Start conversion | ||
| 465 | T::regs().cr().modify(|reg| { | ||
| 466 | reg.set_adstart(true); | ||
| 467 | }); | ||
| 468 | |||
| 469 | // Wait for conversion sequence to finish. | ||
| 470 | transfer.await; | ||
| 471 | |||
| 472 | // Ensure conversions are finished. | ||
| 473 | Self::cancel_conversions(); | ||
| 474 | |||
| 475 | // Reset configuration. | ||
| 476 | T::regs().cfgr().modify(|reg| { | ||
| 477 | reg.set_cont(false); | ||
| 478 | }); | ||
| 479 | } | ||
| 480 | |||
| 481 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | ||
| 268 | // Configure channel | 482 | // Configure channel |
| 269 | Self::set_channel_sample_time(channel, self.sample_time); | 483 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| 484 | } | ||
| 270 | 485 | ||
| 486 | fn read_channel(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 487 | Self::configure_channel(channel, self.sample_time); | ||
| 271 | #[cfg(stm32h7)] | 488 | #[cfg(stm32h7)] |
| 272 | { | 489 | { |
| 273 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | 490 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); |
| 274 | T::regs() | 491 | T::regs() |
| 275 | .pcsel() | 492 | .pcsel() |
| 276 | .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); | 493 | .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); |
| 277 | } | 494 | } |
| 278 | 495 | ||
| 279 | T::regs().sqr1().write(|reg| { | 496 | T::regs().sqr1().write(|reg| { |
| 280 | reg.set_sq(0, channel); | 497 | reg.set_sq(0, channel.channel()); |
| 281 | reg.set_l(0); | 498 | reg.set_l(0); |
| 282 | }); | 499 | }); |
| 283 | 500 | ||
| @@ -292,4 +509,13 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 292 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); | 509 | T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); |
| 293 | } | 510 | } |
| 294 | } | 511 | } |
| 512 | |||
| 513 | fn cancel_conversions() { | ||
| 514 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 515 | T::regs().cr().modify(|reg| { | ||
| 516 | reg.set_adstp(Adstp::STOP); | ||
| 517 | }); | ||
| 518 | while T::regs().cr().read().adstart() {} | ||
| 519 | } | ||
| 520 | } | ||
| 295 | } | 521 | } |
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 2f36df240..f46e87f38 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -4,37 +4,46 @@ | |||
| 4 | #![allow(missing_docs)] // TODO | 4 | #![allow(missing_docs)] // TODO |
| 5 | #![cfg_attr(adc_f3_v2, allow(unused))] | 5 | #![cfg_attr(adc_f3_v2, allow(unused))] |
| 6 | 6 | ||
| 7 | #[cfg(not(adc_f3_v2))] | 7 | #[cfg(not(any(adc_f3_v2)))] |
| 8 | #[cfg_attr(adc_f1, path = "f1.rs")] | 8 | #[cfg_attr(adc_f1, path = "f1.rs")] |
| 9 | #[cfg_attr(adc_f3, path = "f3.rs")] | 9 | #[cfg_attr(adc_f3, path = "f3.rs")] |
| 10 | #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] | 10 | #[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] |
| 11 | #[cfg_attr(adc_v1, path = "v1.rs")] | 11 | #[cfg_attr(adc_v1, path = "v1.rs")] |
| 12 | #[cfg_attr(adc_l0, path = "v1.rs")] | 12 | #[cfg_attr(adc_l0, path = "v1.rs")] |
| 13 | #[cfg_attr(adc_v2, path = "v2.rs")] | 13 | #[cfg_attr(adc_v2, path = "v2.rs")] |
| 14 | #[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] | 14 | #[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0), path = "v3.rs")] |
| 15 | #[cfg_attr(adc_v4, path = "v4.rs")] | 15 | #[cfg_attr(any(adc_v4, adc_u5), path = "v4.rs")] |
| 16 | #[cfg_attr(adc_g4, path = "g4.rs")] | 16 | #[cfg_attr(adc_g4, path = "g4.rs")] |
| 17 | #[cfg_attr(adc_c0, path = "c0.rs")] | ||
| 17 | mod _version; | 18 | mod _version; |
| 18 | 19 | ||
| 19 | use core::marker::PhantomData; | 20 | use core::marker::PhantomData; |
| 20 | 21 | ||
| 21 | #[allow(unused)] | 22 | #[allow(unused)] |
| 22 | #[cfg(not(adc_f3_v2))] | 23 | #[cfg(not(any(adc_f3_v2)))] |
| 23 | pub use _version::*; | 24 | pub use _version::*; |
| 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; | ||
| 24 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] | 26 | #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] |
| 25 | use embassy_sync::waitqueue::AtomicWaker; | 27 | use embassy_sync::waitqueue::AtomicWaker; |
| 26 | 28 | ||
| 29 | #[cfg(adc_u5)] | ||
| 30 | #[path = "u5_adc4.rs"] | ||
| 31 | pub mod adc4; | ||
| 32 | |||
| 33 | pub use crate::pac::adc::vals; | ||
| 27 | #[cfg(not(any(adc_f1, adc_f3_v2)))] | 34 | #[cfg(not(any(adc_f1, adc_f3_v2)))] |
| 28 | pub use crate::pac::adc::vals::Res as Resolution; | 35 | pub use crate::pac::adc::vals::Res as Resolution; |
| 29 | pub use crate::pac::adc::vals::SampleTime; | 36 | pub use crate::pac::adc::vals::SampleTime; |
| 30 | use crate::peripherals; | 37 | use crate::peripherals; |
| 31 | 38 | ||
| 32 | dma_trait!(RxDma, Instance); | 39 | dma_trait!(RxDma, Instance); |
| 40 | #[cfg(adc_u5)] | ||
| 41 | dma_trait!(RxDma4, adc4::Instance); | ||
| 33 | 42 | ||
| 34 | /// Analog to Digital driver. | 43 | /// Analog to Digital driver. |
| 35 | pub struct Adc<'d, T: Instance> { | 44 | pub struct Adc<'d, T: Instance> { |
| 36 | #[allow(unused)] | 45 | #[allow(unused)] |
| 37 | adc: crate::PeripheralRef<'d, T>, | 46 | adc: crate::Peri<'d, T>, |
| 38 | #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] | 47 | #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] |
| 39 | sample_time: SampleTime, | 48 | sample_time: SampleTime, |
| 40 | } | 49 | } |
| @@ -64,7 +73,7 @@ trait SealedInstance { | |||
| 64 | } | 73 | } |
| 65 | 74 | ||
| 66 | pub(crate) trait SealedAdcChannel<T> { | 75 | pub(crate) trait SealedAdcChannel<T> { |
| 67 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))] | 76 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] |
| 68 | fn setup(&mut self) {} | 77 | fn setup(&mut self) {} |
| 69 | 78 | ||
| 70 | #[allow(unused)] | 79 | #[allow(unused)] |
| @@ -98,10 +107,13 @@ pub(crate) fn blocking_delay_us(us: u32) { | |||
| 98 | adc_f3_v1_1, | 107 | adc_f3_v1_1, |
| 99 | adc_g0, | 108 | adc_g0, |
| 100 | adc_u0, | 109 | adc_u0, |
| 101 | adc_h5 | 110 | adc_h5, |
| 111 | adc_h7rs, | ||
| 112 | adc_u5, | ||
| 113 | adc_c0 | ||
| 102 | )))] | 114 | )))] |
| 103 | #[allow(private_bounds)] | 115 | #[allow(private_bounds)] |
| 104 | pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { | 116 | pub trait Instance: SealedInstance + crate::PeripheralType { |
| 105 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 117 | type Interrupt: crate::interrupt::typelevel::Interrupt; |
| 106 | } | 118 | } |
| 107 | /// ADC instance. | 119 | /// ADC instance. |
| @@ -117,10 +129,13 @@ pub trait Instance: SealedInstance + crate::Peripheral<P = Self> { | |||
| 117 | adc_f3_v1_1, | 129 | adc_f3_v1_1, |
| 118 | adc_g0, | 130 | adc_g0, |
| 119 | adc_u0, | 131 | adc_u0, |
| 120 | adc_h5 | 132 | adc_h5, |
| 133 | adc_h7rs, | ||
| 134 | adc_u5, | ||
| 135 | adc_c0 | ||
| 121 | ))] | 136 | ))] |
| 122 | #[allow(private_bounds)] | 137 | #[allow(private_bounds)] |
| 123 | pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral { | 138 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { |
| 124 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 139 | type Interrupt: crate::interrupt::typelevel::Interrupt; |
| 125 | } | 140 | } |
| 126 | 141 | ||
| @@ -129,7 +144,7 @@ pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::R | |||
| 129 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { | 144 | pub trait AdcChannel<T>: SealedAdcChannel<T> + Sized { |
| 130 | #[allow(unused_mut)] | 145 | #[allow(unused_mut)] |
| 131 | fn degrade_adc(mut self) -> AnyAdcChannel<T> { | 146 | fn degrade_adc(mut self) -> AnyAdcChannel<T> { |
| 132 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))] | 147 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] |
| 133 | self.setup(); | 148 | self.setup(); |
| 134 | 149 | ||
| 135 | AnyAdcChannel { | 150 | AnyAdcChannel { |
| @@ -147,7 +162,7 @@ pub struct AnyAdcChannel<T> { | |||
| 147 | channel: u8, | 162 | channel: u8, |
| 148 | _phantom: PhantomData<T>, | 163 | _phantom: PhantomData<T>, |
| 149 | } | 164 | } |
| 150 | 165 | impl_peripheral!(AnyAdcChannel<T: Instance>); | |
| 151 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} | 166 | impl<T: Instance> AdcChannel<T> for AnyAdcChannel<T> {} |
| 152 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | 167 | impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { |
| 153 | fn channel(&self) -> u8 { | 168 | fn channel(&self) -> u8 { |
| @@ -155,6 +170,45 @@ impl<T: Instance> SealedAdcChannel<T> for AnyAdcChannel<T> { | |||
| 155 | } | 170 | } |
| 156 | } | 171 | } |
| 157 | 172 | ||
| 173 | impl<T> AnyAdcChannel<T> { | ||
| 174 | #[allow(unused)] | ||
| 175 | pub fn get_hw_channel(&self) -> u8 { | ||
| 176 | self.channel | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | #[cfg(adc_u5)] | ||
| 181 | foreach_adc!( | ||
| 182 | (ADC4, $common_inst:ident, $clock:ident) => { | ||
| 183 | impl crate::adc::adc4::SealedInstance for peripherals::ADC4 { | ||
| 184 | fn regs() -> crate::pac::adc::Adc4 { | ||
| 185 | crate::pac::ADC4 | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | impl crate::adc::adc4::Instance for peripherals::ADC4 { | ||
| 190 | type Interrupt = crate::_generated::peripheral_interrupts::ADC4::GLOBAL; | ||
| 191 | } | ||
| 192 | }; | ||
| 193 | |||
| 194 | ($inst:ident, $common_inst:ident, $clock:ident) => { | ||
| 195 | impl crate::adc::SealedInstance for peripherals::$inst { | ||
| 196 | fn regs() -> crate::pac::adc::Adc { | ||
| 197 | crate::pac::$inst | ||
| 198 | } | ||
| 199 | |||
| 200 | fn common_regs() -> crate::pac::adccommon::AdcCommon { | ||
| 201 | return crate::pac::$common_inst | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | impl crate::adc::Instance for peripherals::$inst { | ||
| 206 | type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; | ||
| 207 | } | ||
| 208 | }; | ||
| 209 | ); | ||
| 210 | |||
| 211 | #[cfg(not(adc_u5))] | ||
| 158 | foreach_adc!( | 212 | foreach_adc!( |
| 159 | ($inst:ident, $common_inst:ident, $clock:ident) => { | 213 | ($inst:ident, $common_inst:ident, $clock:ident) => { |
| 160 | impl crate::adc::SealedInstance for peripherals::$inst { | 214 | impl crate::adc::SealedInstance for peripherals::$inst { |
| @@ -182,11 +236,11 @@ foreach_adc!( | |||
| 182 | 236 | ||
| 183 | macro_rules! impl_adc_pin { | 237 | macro_rules! impl_adc_pin { |
| 184 | ($inst:ident, $pin:ident, $ch:expr) => { | 238 | ($inst:ident, $pin:ident, $ch:expr) => { |
| 185 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::peripherals::$pin {} | 239 | impl crate::adc::AdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> {} |
| 186 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::peripherals::$pin { | 240 | impl crate::adc::SealedAdcChannel<peripherals::$inst> for crate::Peri<'_, crate::peripherals::$pin> { |
| 187 | #[cfg(any(adc_v1, adc_l0, adc_v2, adc_g4, adc_v4))] | 241 | #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5))] |
| 188 | fn setup(&mut self) { | 242 | fn setup(&mut self) { |
| 189 | <Self as crate::gpio::SealedPin>::set_as_analog(self); | 243 | <crate::peripherals::$pin as crate::gpio::SealedPin>::set_as_analog(self); |
| 190 | } | 244 | } |
| 191 | 245 | ||
| 192 | fn channel(&self) -> u8 { | 246 | fn channel(&self) -> u8 { |
| @@ -204,7 +258,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 204 | match res { | 258 | match res { |
| 205 | #[cfg(adc_v4)] | 259 | #[cfg(adc_v4)] |
| 206 | Resolution::BITS16 => (1 << 16) - 1, | 260 | Resolution::BITS16 => (1 << 16) - 1, |
| 207 | #[cfg(adc_v4)] | 261 | #[cfg(any(adc_v4, adc_u5))] |
| 208 | Resolution::BITS14 => (1 << 14) - 1, | 262 | Resolution::BITS14 => (1 << 14) - 1, |
| 209 | #[cfg(adc_v4)] | 263 | #[cfg(adc_v4)] |
| 210 | Resolution::BITS14V => (1 << 14) - 1, | 264 | Resolution::BITS14V => (1 << 14) - 1, |
| @@ -213,7 +267,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { | |||
| 213 | Resolution::BITS12 => (1 << 12) - 1, | 267 | Resolution::BITS12 => (1 << 12) - 1, |
| 214 | Resolution::BITS10 => (1 << 10) - 1, | 268 | Resolution::BITS10 => (1 << 10) - 1, |
| 215 | Resolution::BITS8 => (1 << 8) - 1, | 269 | Resolution::BITS8 => (1 << 8) - 1, |
| 216 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] | 270 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] |
| 217 | Resolution::BITS6 => (1 << 6) - 1, | 271 | Resolution::BITS6 => (1 << 6) - 1, |
| 218 | #[allow(unreachable_patterns)] | 272 | #[allow(unreachable_patterns)] |
| 219 | _ => core::unreachable!(), | 273 | _ => core::unreachable!(), |
diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 3b064044e..6f69e8486 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs | |||
| @@ -2,14 +2,15 @@ use core::marker::PhantomData; | |||
| 2 | use core::mem; | 2 | use core::mem; |
| 3 | use core::sync::atomic::{compiler_fence, Ordering}; | 3 | use core::sync::atomic::{compiler_fence, Ordering}; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::{into_ref, Peripheral}; | ||
| 6 | use stm32_metapac::adc::vals::SampleTime; | 5 | use stm32_metapac::adc::vals::SampleTime; |
| 7 | 6 | ||
| 8 | use crate::adc::{Adc, AdcChannel, Instance, RxDma}; | 7 | use crate::adc::{Adc, AdcChannel, Instance, RxDma}; |
| 9 | use crate::dma::ringbuffer::OverrunError; | ||
| 10 | use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; | 8 | use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; |
| 11 | use crate::pac::adc::vals; | 9 | use crate::pac::adc::vals; |
| 12 | use crate::rcc; | 10 | use crate::{rcc, Peri}; |
| 11 | |||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub struct OverrunError; | ||
| 13 | 14 | ||
| 14 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { | 15 | fn clear_interrupt_flags(r: crate::pac::adc::Adc) { |
| 15 | r.sr().modify(|regs| { | 16 | r.sr().modify(|regs| { |
| @@ -101,13 +102,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 101 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. | 102 | /// It is critical to call `read` frequently to prevent DMA buffer overrun. |
| 102 | /// | 103 | /// |
| 103 | /// [`read`]: #method.read | 104 | /// [`read`]: #method.read |
| 104 | pub fn into_ring_buffered( | 105 | pub fn into_ring_buffered(self, dma: Peri<'d, impl RxDma<T>>, dma_buf: &'d mut [u16]) -> RingBufferedAdc<'d, T> { |
| 105 | self, | ||
| 106 | dma: impl Peripheral<P = impl RxDma<T>> + 'd, | ||
| 107 | dma_buf: &'d mut [u16], | ||
| 108 | ) -> RingBufferedAdc<'d, T> { | ||
| 109 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | 106 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); |
| 110 | into_ref!(dma); | ||
| 111 | 107 | ||
| 112 | let opts: crate::dma::TransferOptions = TransferOptions { | 108 | let opts: crate::dma::TransferOptions = TransferOptions { |
| 113 | half_transfer_ir: true, | 109 | half_transfer_ir: true, |
| @@ -203,16 +199,16 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 203 | Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), | 199 | Sequence::Four => T::regs().sqr3().modify(|w| w.set_sq(3, channel.channel())), |
| 204 | Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), | 200 | Sequence::Five => T::regs().sqr3().modify(|w| w.set_sq(4, channel.channel())), |
| 205 | Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), | 201 | Sequence::Six => T::regs().sqr3().modify(|w| w.set_sq(5, channel.channel())), |
| 206 | Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(6, channel.channel())), | 202 | Sequence::Seven => T::regs().sqr2().modify(|w| w.set_sq(0, channel.channel())), |
| 207 | Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(7, channel.channel())), | 203 | Sequence::Eight => T::regs().sqr2().modify(|w| w.set_sq(1, channel.channel())), |
| 208 | Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(8, channel.channel())), | 204 | Sequence::Nine => T::regs().sqr2().modify(|w| w.set_sq(2, channel.channel())), |
| 209 | Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(9, channel.channel())), | 205 | Sequence::Ten => T::regs().sqr2().modify(|w| w.set_sq(3, channel.channel())), |
| 210 | Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(10, channel.channel())), | 206 | Sequence::Eleven => T::regs().sqr2().modify(|w| w.set_sq(4, channel.channel())), |
| 211 | Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(11, channel.channel())), | 207 | Sequence::Twelve => T::regs().sqr2().modify(|w| w.set_sq(5, channel.channel())), |
| 212 | Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(12, channel.channel())), | 208 | Sequence::Thirteen => T::regs().sqr1().modify(|w| w.set_sq(0, channel.channel())), |
| 213 | Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(13, channel.channel())), | 209 | Sequence::Fourteen => T::regs().sqr1().modify(|w| w.set_sq(1, channel.channel())), |
| 214 | Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(14, channel.channel())), | 210 | Sequence::Fifteen => T::regs().sqr1().modify(|w| w.set_sq(2, channel.channel())), |
| 215 | Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(15, channel.channel())), | 211 | Sequence::Sixteen => T::regs().sqr1().modify(|w| w.set_sq(3, channel.channel())), |
| 216 | }; | 212 | }; |
| 217 | 213 | ||
| 218 | if !was_on { | 214 | if !was_on { |
| @@ -226,9 +222,8 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 226 | 222 | ||
| 227 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. | 223 | /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. |
| 228 | pub fn start(&mut self) -> Result<(), OverrunError> { | 224 | pub fn start(&mut self) -> Result<(), OverrunError> { |
| 229 | self.ring_buf.clear(); | ||
| 230 | |||
| 231 | self.setup_adc(); | 225 | self.setup_adc(); |
| 226 | self.ring_buf.clear(); | ||
| 232 | 227 | ||
| 233 | Ok(()) | 228 | Ok(()) |
| 234 | } | 229 | } |
| @@ -245,7 +240,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 245 | /// [`start`]: #method.start | 240 | /// [`start`]: #method.start |
| 246 | pub fn teardown_adc(&mut self) { | 241 | pub fn teardown_adc(&mut self) { |
| 247 | // Stop the DMA transfer | 242 | // Stop the DMA transfer |
| 248 | self.ring_buf.request_stop(); | 243 | self.ring_buf.request_pause(); |
| 249 | 244 | ||
| 250 | let r = T::regs(); | 245 | let r = T::regs(); |
| 251 | 246 | ||
| @@ -311,7 +306,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { | |||
| 311 | // DMA requests are issues as long as DMA=1 and data are converted. | 306 | // DMA requests are issues as long as DMA=1 and data are converted. |
| 312 | w.set_dds(vals::Dds::CONTINUOUS); | 307 | w.set_dds(vals::Dds::CONTINUOUS); |
| 313 | // EOC flag is set at the end of each conversion. | 308 | // EOC flag is set at the end of each conversion. |
| 314 | w.set_eocs(vals::Eocs::EACHCONVERSION); | 309 | w.set_eocs(vals::Eocs::EACH_CONVERSION); |
| 315 | }); | 310 | }); |
| 316 | 311 | ||
| 317 | // Begin ADC conversions | 312 | // Begin ADC conversions |
diff --git a/embassy-stm32/src/adc/u5_adc4.rs b/embassy-stm32/src/adc/u5_adc4.rs new file mode 100644 index 000000000..1dd664366 --- /dev/null +++ b/embassy-stm32/src/adc/u5_adc4.rs | |||
| @@ -0,0 +1,478 @@ | |||
| 1 | #[allow(unused)] | ||
| 2 | use pac::adc::vals::{Adc4Dmacfg, Adc4Exten, Adc4OversamplingRatio}; | ||
| 3 | |||
| 4 | use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; | ||
| 5 | use crate::dma::Transfer; | ||
| 6 | pub use crate::pac::adc::regs::Adc4Chselrmod0; | ||
| 7 | pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4SampleTime as SampleTime}; | ||
| 8 | use crate::time::Hertz; | ||
| 9 | use crate::{pac, rcc, Peri}; | ||
| 10 | |||
| 11 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | ||
| 12 | |||
| 13 | /// Default VREF voltage used for sample conversion to millivolts. | ||
| 14 | pub const VREF_DEFAULT_MV: u32 = 3300; | ||
| 15 | /// VREF voltage used for factory calibration of VREFINTCAL register. | ||
| 16 | pub const VREF_CALIB_MV: u32 = 3300; | ||
| 17 | |||
| 18 | const VREF_CHANNEL: u8 = 0; | ||
| 19 | const VCORE_CHANNEL: u8 = 12; | ||
| 20 | const TEMP_CHANNEL: u8 = 13; | ||
| 21 | const VBAT_CHANNEL: u8 = 14; | ||
| 22 | const DAC_CHANNEL: u8 = 21; | ||
| 23 | |||
| 24 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | ||
| 25 | /// Internal voltage reference channel. | ||
| 26 | pub struct VrefInt; | ||
| 27 | impl<T: Instance> AdcChannel<T> for VrefInt {} | ||
| 28 | impl<T: Instance> SealedAdcChannel<T> for VrefInt { | ||
| 29 | fn channel(&self) -> u8 { | ||
| 30 | VREF_CHANNEL | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Internal temperature channel. | ||
| 35 | pub struct Temperature; | ||
| 36 | impl<T: Instance> AdcChannel<T> for Temperature {} | ||
| 37 | impl<T: Instance> SealedAdcChannel<T> for Temperature { | ||
| 38 | fn channel(&self) -> u8 { | ||
| 39 | TEMP_CHANNEL | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Internal battery voltage channel. | ||
| 44 | pub struct Vbat; | ||
| 45 | impl<T: Instance> AdcChannel<T> for Vbat {} | ||
| 46 | impl<T: Instance> SealedAdcChannel<T> for Vbat { | ||
| 47 | fn channel(&self) -> u8 { | ||
| 48 | VBAT_CHANNEL | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Internal DAC channel. | ||
| 53 | pub struct Dac; | ||
| 54 | impl<T: Instance> AdcChannel<T> for Dac {} | ||
| 55 | impl<T: Instance> SealedAdcChannel<T> for Dac { | ||
| 56 | fn channel(&self) -> u8 { | ||
| 57 | DAC_CHANNEL | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Internal Vcore channel. | ||
| 62 | pub struct Vcore; | ||
| 63 | impl<T: Instance> AdcChannel<T> for Vcore {} | ||
| 64 | impl<T: Instance> SealedAdcChannel<T> for Vcore { | ||
| 65 | fn channel(&self) -> u8 { | ||
| 66 | VCORE_CHANNEL | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | pub enum DacChannel { | ||
| 71 | OUT1, | ||
| 72 | OUT2, | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Number of samples used for averaging. | ||
| 76 | pub enum Averaging { | ||
| 77 | Disabled, | ||
| 78 | Samples2, | ||
| 79 | Samples4, | ||
| 80 | Samples8, | ||
| 81 | Samples16, | ||
| 82 | Samples32, | ||
| 83 | Samples64, | ||
| 84 | Samples128, | ||
| 85 | Samples256, | ||
| 86 | } | ||
| 87 | |||
| 88 | pub const fn resolution_to_max_count(res: Resolution) -> u32 { | ||
| 89 | match res { | ||
| 90 | Resolution::BITS12 => (1 << 12) - 1, | ||
| 91 | Resolution::BITS10 => (1 << 10) - 1, | ||
| 92 | Resolution::BITS8 => (1 << 8) - 1, | ||
| 93 | Resolution::BITS6 => (1 << 6) - 1, | ||
| 94 | #[allow(unreachable_patterns)] | ||
| 95 | _ => core::unreachable!(), | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | // NOTE (unused): The prescaler enum closely copies the hardware capabilities, | ||
| 100 | // but high prescaling doesn't make a lot of sense in the current implementation and is ommited. | ||
| 101 | #[allow(unused)] | ||
| 102 | enum Prescaler { | ||
| 103 | NotDivided, | ||
| 104 | DividedBy2, | ||
| 105 | DividedBy4, | ||
| 106 | DividedBy6, | ||
| 107 | DividedBy8, | ||
| 108 | DividedBy10, | ||
| 109 | DividedBy12, | ||
| 110 | DividedBy16, | ||
| 111 | DividedBy32, | ||
| 112 | DividedBy64, | ||
| 113 | DividedBy128, | ||
| 114 | DividedBy256, | ||
| 115 | } | ||
| 116 | |||
| 117 | impl Prescaler { | ||
| 118 | fn from_ker_ck(frequency: Hertz) -> Self { | ||
| 119 | let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; | ||
| 120 | match raw_prescaler { | ||
| 121 | 0 => Self::NotDivided, | ||
| 122 | 1 => Self::DividedBy2, | ||
| 123 | 2..=3 => Self::DividedBy4, | ||
| 124 | 4..=5 => Self::DividedBy6, | ||
| 125 | 6..=7 => Self::DividedBy8, | ||
| 126 | 8..=9 => Self::DividedBy10, | ||
| 127 | 10..=11 => Self::DividedBy12, | ||
| 128 | _ => unimplemented!(), | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | fn divisor(&self) -> u32 { | ||
| 133 | match self { | ||
| 134 | Prescaler::NotDivided => 1, | ||
| 135 | Prescaler::DividedBy2 => 2, | ||
| 136 | Prescaler::DividedBy4 => 4, | ||
| 137 | Prescaler::DividedBy6 => 6, | ||
| 138 | Prescaler::DividedBy8 => 8, | ||
| 139 | Prescaler::DividedBy10 => 10, | ||
| 140 | Prescaler::DividedBy12 => 12, | ||
| 141 | Prescaler::DividedBy16 => 16, | ||
| 142 | Prescaler::DividedBy32 => 32, | ||
| 143 | Prescaler::DividedBy64 => 64, | ||
| 144 | Prescaler::DividedBy128 => 128, | ||
| 145 | Prescaler::DividedBy256 => 256, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | fn presc(&self) -> Presc { | ||
| 150 | match self { | ||
| 151 | Prescaler::NotDivided => Presc::DIV1, | ||
| 152 | Prescaler::DividedBy2 => Presc::DIV2, | ||
| 153 | Prescaler::DividedBy4 => Presc::DIV4, | ||
| 154 | Prescaler::DividedBy6 => Presc::DIV6, | ||
| 155 | Prescaler::DividedBy8 => Presc::DIV8, | ||
| 156 | Prescaler::DividedBy10 => Presc::DIV10, | ||
| 157 | Prescaler::DividedBy12 => Presc::DIV12, | ||
| 158 | Prescaler::DividedBy16 => Presc::DIV16, | ||
| 159 | Prescaler::DividedBy32 => Presc::DIV32, | ||
| 160 | Prescaler::DividedBy64 => Presc::DIV64, | ||
| 161 | Prescaler::DividedBy128 => Presc::DIV128, | ||
| 162 | Prescaler::DividedBy256 => Presc::DIV256, | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | pub trait SealedInstance { | ||
| 168 | #[allow(unused)] | ||
| 169 | fn regs() -> crate::pac::adc::Adc4; | ||
| 170 | } | ||
| 171 | |||
| 172 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { | ||
| 173 | type Interrupt: crate::interrupt::typelevel::Interrupt; | ||
| 174 | } | ||
| 175 | |||
| 176 | pub struct Adc4<'d, T: Instance> { | ||
| 177 | #[allow(unused)] | ||
| 178 | adc: crate::Peri<'d, T>, | ||
| 179 | } | ||
| 180 | |||
| 181 | #[derive(Debug)] | ||
| 182 | pub enum Adc4Error { | ||
| 183 | InvalidSequence, | ||
| 184 | DMAError, | ||
| 185 | } | ||
| 186 | |||
| 187 | impl<'d, T: Instance> Adc4<'d, T> { | ||
| 188 | /// Create a new ADC driver. | ||
| 189 | pub fn new(adc: Peri<'d, T>) -> Self { | ||
| 190 | rcc::enable_and_reset::<T>(); | ||
| 191 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | ||
| 192 | |||
| 193 | T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | ||
| 194 | |||
| 195 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | ||
| 196 | info!("ADC4 frequency set to {}", frequency); | ||
| 197 | |||
| 198 | if frequency > MAX_ADC_CLK_FREQ { | ||
| 199 | panic!("Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | ||
| 200 | } | ||
| 201 | |||
| 202 | let mut s = Self { adc }; | ||
| 203 | |||
| 204 | s.power_up(); | ||
| 205 | |||
| 206 | s.calibrate(); | ||
| 207 | blocking_delay_us(1); | ||
| 208 | |||
| 209 | s.enable(); | ||
| 210 | s.configure(); | ||
| 211 | |||
| 212 | s | ||
| 213 | } | ||
| 214 | |||
| 215 | fn power_up(&mut self) { | ||
| 216 | T::regs().isr().modify(|w| { | ||
| 217 | w.set_ldordy(true); | ||
| 218 | }); | ||
| 219 | T::regs().cr().modify(|w| { | ||
| 220 | w.set_advregen(true); | ||
| 221 | }); | ||
| 222 | while !T::regs().isr().read().ldordy() {} | ||
| 223 | |||
| 224 | T::regs().isr().modify(|w| { | ||
| 225 | w.set_ldordy(true); | ||
| 226 | }); | ||
| 227 | } | ||
| 228 | |||
| 229 | fn calibrate(&mut self) { | ||
| 230 | T::regs().cr().modify(|w| w.set_adcal(true)); | ||
| 231 | while T::regs().cr().read().adcal() {} | ||
| 232 | T::regs().isr().modify(|w| w.set_eocal(true)); | ||
| 233 | } | ||
| 234 | |||
| 235 | fn enable(&mut self) { | ||
| 236 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 237 | T::regs().cr().modify(|w| w.set_aden(true)); | ||
| 238 | while !T::regs().isr().read().adrdy() {} | ||
| 239 | T::regs().isr().write(|w| w.set_adrdy(true)); | ||
| 240 | } | ||
| 241 | |||
| 242 | fn configure(&mut self) { | ||
| 243 | // single conversion mode, software trigger | ||
| 244 | T::regs().cfgr1().modify(|w| { | ||
| 245 | w.set_cont(false); | ||
| 246 | w.set_discen(false); | ||
| 247 | w.set_exten(Adc4Exten::DISABLED); | ||
| 248 | w.set_chselrmod(false); | ||
| 249 | }); | ||
| 250 | |||
| 251 | // only use one channel at the moment | ||
| 252 | T::regs().smpr().modify(|w| { | ||
| 253 | for i in 0..24 { | ||
| 254 | w.set_smpsel(i, false); | ||
| 255 | } | ||
| 256 | }); | ||
| 257 | } | ||
| 258 | |||
| 259 | /// Enable reading the voltage reference internal channel. | ||
| 260 | pub fn enable_vrefint(&self) -> VrefInt { | ||
| 261 | T::regs().ccr().modify(|w| { | ||
| 262 | w.set_vrefen(true); | ||
| 263 | }); | ||
| 264 | |||
| 265 | VrefInt {} | ||
| 266 | } | ||
| 267 | |||
| 268 | /// Enable reading the temperature internal channel. | ||
| 269 | pub fn enable_temperature(&self) -> Temperature { | ||
| 270 | T::regs().ccr().modify(|w| { | ||
| 271 | w.set_vsensesel(true); | ||
| 272 | }); | ||
| 273 | |||
| 274 | Temperature {} | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Enable reading the vbat internal channel. | ||
| 278 | pub fn enable_vbat(&self) -> Vbat { | ||
| 279 | T::regs().ccr().modify(|w| { | ||
| 280 | w.set_vbaten(true); | ||
| 281 | }); | ||
| 282 | |||
| 283 | Vbat {} | ||
| 284 | } | ||
| 285 | |||
| 286 | /// Enable reading the vbat internal channel. | ||
| 287 | pub fn enable_vcore(&self) -> Vcore { | ||
| 288 | Vcore {} | ||
| 289 | } | ||
| 290 | |||
| 291 | /// Enable reading the vbat internal channel. | ||
| 292 | pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { | ||
| 293 | let mux; | ||
| 294 | match dac { | ||
| 295 | DacChannel::OUT1 => mux = false, | ||
| 296 | DacChannel::OUT2 => mux = true, | ||
| 297 | } | ||
| 298 | T::regs().or().modify(|w| w.set_chn21sel(mux)); | ||
| 299 | Dac {} | ||
| 300 | } | ||
| 301 | |||
| 302 | /// Set the ADC sample time. | ||
| 303 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 304 | T::regs().smpr().modify(|w| { | ||
| 305 | w.set_smp(0, sample_time); | ||
| 306 | }); | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Get the ADC sample time. | ||
| 310 | pub fn sample_time(&self) -> SampleTime { | ||
| 311 | T::regs().smpr().read().smp(0) | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Set the ADC resolution. | ||
| 315 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 316 | T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); | ||
| 317 | } | ||
| 318 | |||
| 319 | /// Set hardware averaging. | ||
| 320 | pub fn set_averaging(&mut self, averaging: Averaging) { | ||
| 321 | let (enable, samples, right_shift) = match averaging { | ||
| 322 | Averaging::Disabled => (false, Adc4OversamplingRatio::OVERSAMPLE2X, 0), | ||
| 323 | Averaging::Samples2 => (true, Adc4OversamplingRatio::OVERSAMPLE2X, 1), | ||
| 324 | Averaging::Samples4 => (true, Adc4OversamplingRatio::OVERSAMPLE4X, 2), | ||
| 325 | Averaging::Samples8 => (true, Adc4OversamplingRatio::OVERSAMPLE8X, 3), | ||
| 326 | Averaging::Samples16 => (true, Adc4OversamplingRatio::OVERSAMPLE16X, 4), | ||
| 327 | Averaging::Samples32 => (true, Adc4OversamplingRatio::OVERSAMPLE32X, 5), | ||
| 328 | Averaging::Samples64 => (true, Adc4OversamplingRatio::OVERSAMPLE64X, 6), | ||
| 329 | Averaging::Samples128 => (true, Adc4OversamplingRatio::OVERSAMPLE128X, 7), | ||
| 330 | Averaging::Samples256 => (true, Adc4OversamplingRatio::OVERSAMPLE256X, 8), | ||
| 331 | }; | ||
| 332 | |||
| 333 | T::regs().cfgr2().modify(|w| { | ||
| 334 | w.set_ovsr(samples); | ||
| 335 | w.set_ovss(right_shift); | ||
| 336 | w.set_ovse(enable) | ||
| 337 | }) | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Read an ADC channel. | ||
| 341 | pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 { | ||
| 342 | channel.setup(); | ||
| 343 | |||
| 344 | // Select channel | ||
| 345 | T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); | ||
| 346 | T::regs().chselrmod0().modify(|w| { | ||
| 347 | w.set_chsel(channel.channel() as usize, true); | ||
| 348 | }); | ||
| 349 | |||
| 350 | // Reset interrupts | ||
| 351 | T::regs().isr().modify(|reg| { | ||
| 352 | reg.set_eos(true); | ||
| 353 | reg.set_eoc(true); | ||
| 354 | }); | ||
| 355 | |||
| 356 | // Start conversion | ||
| 357 | T::regs().cr().modify(|reg| { | ||
| 358 | reg.set_adstart(true); | ||
| 359 | }); | ||
| 360 | |||
| 361 | while !T::regs().isr().read().eos() { | ||
| 362 | // spin | ||
| 363 | } | ||
| 364 | |||
| 365 | T::regs().dr().read().0 as u16 | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Read one or multiple ADC channels using DMA. | ||
| 369 | /// | ||
| 370 | /// `sequence` iterator and `readings` must have the same length. | ||
| 371 | /// The channels in `sequence` must be in ascending order. | ||
| 372 | /// | ||
| 373 | /// Example | ||
| 374 | /// ```rust,ignore | ||
| 375 | /// use embassy_stm32::adc::adc4; | ||
| 376 | /// use embassy_stm32::adc::AdcChannel; | ||
| 377 | /// | ||
| 378 | /// let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 379 | /// let mut adc4_pin1 = p.PC1; | ||
| 380 | /// let mut adc4_pin2 = p.PC0; | ||
| 381 | /// let mut.into()d41 = adc4_pin1.into(); | ||
| 382 | /// let mut.into()d42 = adc4_pin2.into(); | ||
| 383 | /// let mut measurements = [0u16; 2]; | ||
| 384 | /// // not that the channels must be in ascending order | ||
| 385 | /// adc4.read( | ||
| 386 | /// &mut p.GPDMA1_CH1, | ||
| 387 | /// [ | ||
| 388 | /// &mut.into()d42, | ||
| 389 | /// &mut.into()d41, | ||
| 390 | /// ] | ||
| 391 | /// .into_iter(), | ||
| 392 | /// &mut measurements, | ||
| 393 | /// ).await.unwrap(); | ||
| 394 | /// ``` | ||
| 395 | pub async fn read( | ||
| 396 | &mut self, | ||
| 397 | rx_dma: Peri<'_, impl RxDma4<T>>, | ||
| 398 | sequence: impl ExactSizeIterator<Item = &mut AnyAdcChannel<T>>, | ||
| 399 | readings: &mut [u16], | ||
| 400 | ) -> Result<(), Adc4Error> { | ||
| 401 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | ||
| 402 | assert!( | ||
| 403 | sequence.len() == readings.len(), | ||
| 404 | "Sequence length must be equal to readings length" | ||
| 405 | ); | ||
| 406 | |||
| 407 | // Ensure no conversions are ongoing | ||
| 408 | Self::cancel_conversions(); | ||
| 409 | |||
| 410 | T::regs().isr().modify(|reg| { | ||
| 411 | reg.set_ovr(true); | ||
| 412 | reg.set_eos(true); | ||
| 413 | reg.set_eoc(true); | ||
| 414 | }); | ||
| 415 | |||
| 416 | T::regs().cfgr1().modify(|reg| { | ||
| 417 | reg.set_dmaen(true); | ||
| 418 | reg.set_dmacfg(Adc4Dmacfg::ONE_SHOT); | ||
| 419 | reg.set_chselrmod(false); | ||
| 420 | }); | ||
| 421 | |||
| 422 | // Verify and activate sequence | ||
| 423 | let mut prev_channel: i16 = -1; | ||
| 424 | T::regs().chselrmod0().write_value(Adc4Chselrmod0(0_u32)); | ||
| 425 | for channel in sequence { | ||
| 426 | let channel_num = channel.channel; | ||
| 427 | if channel_num as i16 <= prev_channel { | ||
| 428 | return Err(Adc4Error::InvalidSequence); | ||
| 429 | }; | ||
| 430 | prev_channel = channel_num as i16; | ||
| 431 | |||
| 432 | T::regs().chselrmod0().modify(|w| { | ||
| 433 | w.set_chsel(channel.channel as usize, true); | ||
| 434 | }); | ||
| 435 | } | ||
| 436 | |||
| 437 | let request = rx_dma.request(); | ||
| 438 | let transfer = unsafe { | ||
| 439 | Transfer::new_read( | ||
| 440 | rx_dma, | ||
| 441 | request, | ||
| 442 | T::regs().dr().as_ptr() as *mut u16, | ||
| 443 | readings, | ||
| 444 | Default::default(), | ||
| 445 | ) | ||
| 446 | }; | ||
| 447 | |||
| 448 | // Start conversion | ||
| 449 | T::regs().cr().modify(|reg| { | ||
| 450 | reg.set_adstart(true); | ||
| 451 | }); | ||
| 452 | |||
| 453 | transfer.await; | ||
| 454 | |||
| 455 | // Ensure conversions are finished. | ||
| 456 | Self::cancel_conversions(); | ||
| 457 | |||
| 458 | // Reset configuration. | ||
| 459 | T::regs().cfgr1().modify(|reg| { | ||
| 460 | reg.set_dmaen(false); | ||
| 461 | }); | ||
| 462 | |||
| 463 | if T::regs().isr().read().ovr() { | ||
| 464 | Err(Adc4Error::DMAError) | ||
| 465 | } else { | ||
| 466 | Ok(()) | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | fn cancel_conversions() { | ||
| 471 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 472 | T::regs().cr().modify(|reg| { | ||
| 473 | reg.set_adstp(true); | ||
| 474 | }); | ||
| 475 | while T::regs().cr().read().adstart() {} | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 9bec2e13b..fb6f5b7d0 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -2,7 +2,6 @@ use core::future::poll_fn; | |||
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::into_ref; | ||
| 6 | #[cfg(adc_l0)] | 5 | #[cfg(adc_l0)] |
| 7 | use stm32_metapac::adc::vals::Ckmode; | 6 | use stm32_metapac::adc::vals::Ckmode; |
| 8 | 7 | ||
| @@ -10,7 +9,7 @@ use super::blocking_delay_us; | |||
| 10 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 9 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; |
| 11 | use crate::interrupt::typelevel::Interrupt; | 10 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::peripherals::ADC1; | 11 | use crate::peripherals::ADC1; |
| 13 | use crate::{interrupt, rcc, Peripheral}; | 12 | use crate::{interrupt, rcc, Peri}; |
| 14 | 13 | ||
| 15 | pub const VDDA_CALIB_MV: u32 = 3300; | 14 | pub const VDDA_CALIB_MV: u32 = 3300; |
| 16 | pub const VREF_INT: u32 = 1230; | 15 | pub const VREF_INT: u32 = 1230; |
| @@ -63,10 +62,9 @@ impl super::SealedAdcChannel<ADC1> for Temperature { | |||
| 63 | 62 | ||
| 64 | impl<'d, T: Instance> Adc<'d, T> { | 63 | impl<'d, T: Instance> Adc<'d, T> { |
| 65 | pub fn new( | 64 | pub fn new( |
| 66 | adc: impl Peripheral<P = T> + 'd, | 65 | adc: Peri<'d, T>, |
| 67 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 66 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 68 | ) -> Self { | 67 | ) -> Self { |
| 69 | into_ref!(adc); | ||
| 70 | rcc::enable_and_reset::<T>(); | 68 | rcc::enable_and_reset::<T>(); |
| 71 | 69 | ||
| 72 | // Delay 1μs when using HSI14 as the ADC clock. | 70 | // Delay 1μs when using HSI14 as the ADC clock. |
| @@ -160,7 +158,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 160 | channel.setup(); | 158 | channel.setup(); |
| 161 | 159 | ||
| 162 | // A.7.5 Single conversion sequence code example - Software trigger | 160 | // A.7.5 Single conversion sequence code example - Software trigger |
| 163 | T::regs().chselr().write(|reg| reg.set_chselx(ch_num as usize, true)); | 161 | T::regs().chselr().write(|reg| reg.set_chsel_x(ch_num as usize, true)); |
| 164 | 162 | ||
| 165 | self.convert().await | 163 | self.convert().await |
| 166 | } | 164 | } |
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 842a5ee6d..e94a25b24 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs | |||
| @@ -1,10 +1,8 @@ | |||
| 1 | use embassy_hal_internal::into_ref; | ||
| 2 | |||
| 3 | use super::blocking_delay_us; | 1 | use super::blocking_delay_us; |
| 4 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; | 2 | use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; |
| 5 | use crate::peripherals::ADC1; | 3 | use crate::peripherals::ADC1; |
| 6 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 7 | use crate::{rcc, Peripheral}; | 5 | use crate::{rcc, Peri}; |
| 8 | 6 | ||
| 9 | mod ringbuffered_v2; | 7 | mod ringbuffered_v2; |
| 10 | pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; | 8 | pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; |
| @@ -97,8 +95,7 @@ impl<'d, T> Adc<'d, T> | |||
| 97 | where | 95 | where |
| 98 | T: Instance, | 96 | T: Instance, |
| 99 | { | 97 | { |
| 100 | pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self { | 98 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 101 | into_ref!(adc); | ||
| 102 | rcc::enable_and_reset::<T>(); | 99 | rcc::enable_and_reset::<T>(); |
| 103 | 100 | ||
| 104 | let presc = Prescaler::from_pclk2(T::frequency()); | 101 | let presc = Prescaler::from_pclk2(T::frequency()); |
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 9441e42ff..313244e19 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs | |||
| @@ -1,12 +1,11 @@ | |||
| 1 | use cfg_if::cfg_if; | 1 | use cfg_if::cfg_if; |
| 2 | use embassy_hal_internal::into_ref; | ||
| 3 | use pac::adc::vals::Dmacfg; | 2 | use pac::adc::vals::Dmacfg; |
| 4 | 3 | ||
| 5 | use super::{ | 4 | use super::{ |
| 6 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, | 5 | blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, |
| 7 | }; | 6 | }; |
| 8 | use crate::dma::Transfer; | 7 | use crate::dma::Transfer; |
| 9 | use crate::{pac, rcc, Peripheral}; | 8 | use crate::{pac, rcc, Peri}; |
| 10 | 9 | ||
| 11 | /// Default VREF voltage used for sample conversion to millivolts. | 10 | /// Default VREF voltage used for sample conversion to millivolts. |
| 12 | pub const VREF_DEFAULT_MV: u32 = 3300; | 11 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -20,7 +19,7 @@ impl<T: Instance> SealedAdcChannel<T> for VrefInt { | |||
| 20 | cfg_if! { | 19 | cfg_if! { |
| 21 | if #[cfg(adc_g0)] { | 20 | if #[cfg(adc_g0)] { |
| 22 | let val = 13; | 21 | let val = 13; |
| 23 | } else if #[cfg(adc_h5)] { | 22 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 24 | let val = 17; | 23 | let val = 17; |
| 25 | } else if #[cfg(adc_u0)] { | 24 | } else if #[cfg(adc_u0)] { |
| 26 | let val = 12; | 25 | let val = 12; |
| @@ -39,7 +38,7 @@ impl<T: Instance> SealedAdcChannel<T> for Temperature { | |||
| 39 | cfg_if! { | 38 | cfg_if! { |
| 40 | if #[cfg(adc_g0)] { | 39 | if #[cfg(adc_g0)] { |
| 41 | let val = 12; | 40 | let val = 12; |
| 42 | } else if #[cfg(adc_h5)] { | 41 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 43 | let val = 16; | 42 | let val = 16; |
| 44 | } else if #[cfg(adc_u0)] { | 43 | } else if #[cfg(adc_u0)] { |
| 45 | let val = 11; | 44 | let val = 11; |
| @@ -58,9 +57,9 @@ impl<T: Instance> SealedAdcChannel<T> for Vbat { | |||
| 58 | cfg_if! { | 57 | cfg_if! { |
| 59 | if #[cfg(adc_g0)] { | 58 | if #[cfg(adc_g0)] { |
| 60 | let val = 14; | 59 | let val = 14; |
| 61 | } else if #[cfg(adc_h5)] { | 60 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 62 | let val = 2; | 61 | let val = 2; |
| 63 | } else if #[cfg(adc_h5)] { | 62 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 64 | let val = 13; | 63 | let val = 13; |
| 65 | } else { | 64 | } else { |
| 66 | let val = 18; | 65 | let val = 18; |
| @@ -71,7 +70,7 @@ impl<T: Instance> SealedAdcChannel<T> for Vbat { | |||
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | cfg_if! { | 72 | cfg_if! { |
| 74 | if #[cfg(adc_h5)] { | 73 | if #[cfg(any(adc_h5, adc_h7rs))] { |
| 75 | pub struct VddCore; | 74 | pub struct VddCore; |
| 76 | impl<T: Instance> AdcChannel<T> for VddCore {} | 75 | impl<T: Instance> AdcChannel<T> for VddCore {} |
| 77 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { | 76 | impl<T: Instance> super::SealedAdcChannel<T> for VddCore { |
| @@ -95,8 +94,7 @@ cfg_if! { | |||
| 95 | } | 94 | } |
| 96 | 95 | ||
| 97 | impl<'d, T: Instance> Adc<'d, T> { | 96 | impl<'d, T: Instance> Adc<'d, T> { |
| 98 | pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self { | 97 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 99 | into_ref!(adc); | ||
| 100 | rcc::enable_and_reset::<T>(); | 98 | rcc::enable_and_reset::<T>(); |
| 101 | T::regs().cr().modify(|reg| { | 99 | T::regs().cr().modify(|reg| { |
| 102 | #[cfg(not(any(adc_g0, adc_u0)))] | 100 | #[cfg(not(any(adc_g0, adc_u0)))] |
| @@ -173,7 +171,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 173 | T::regs().ccr().modify(|reg| { | 171 | T::regs().ccr().modify(|reg| { |
| 174 | reg.set_tsen(true); | 172 | reg.set_tsen(true); |
| 175 | }); | 173 | }); |
| 176 | } else if #[cfg(adc_h5)] { | 174 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 177 | T::common_regs().ccr().modify(|reg| { | 175 | T::common_regs().ccr().modify(|reg| { |
| 178 | reg.set_tsen(true); | 176 | reg.set_tsen(true); |
| 179 | }); | 177 | }); |
| @@ -193,7 +191,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 193 | T::regs().ccr().modify(|reg| { | 191 | T::regs().ccr().modify(|reg| { |
| 194 | reg.set_vbaten(true); | 192 | reg.set_vbaten(true); |
| 195 | }); | 193 | }); |
| 196 | } else if #[cfg(adc_h5)] { | 194 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 197 | T::common_regs().ccr().modify(|reg| { | 195 | T::common_regs().ccr().modify(|reg| { |
| 198 | reg.set_vbaten(true); | 196 | reg.set_vbaten(true); |
| 199 | }); | 197 | }); |
| @@ -262,6 +260,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 262 | /// | 260 | /// |
| 263 | /// `sequence` iterator and `readings` must have the same length. | 261 | /// `sequence` iterator and `readings` must have the same length. |
| 264 | /// | 262 | /// |
| 263 | /// Note: The order of values in `readings` is defined by the pin ADC | ||
| 264 | /// channel number and not the pin order in `sequence`. | ||
| 265 | /// | ||
| 265 | /// Example | 266 | /// Example |
| 266 | /// ```rust,ignore | 267 | /// ```rust,ignore |
| 267 | /// use embassy_stm32::adc::{Adc, AdcChannel} | 268 | /// use embassy_stm32::adc::{Adc, AdcChannel} |
| @@ -271,8 +272,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 271 | /// let mut adc_pin1 = p.PA1.degrade_adc(); | 272 | /// let mut adc_pin1 = p.PA1.degrade_adc(); |
| 272 | /// let mut measurements = [0u16; 2]; | 273 | /// let mut measurements = [0u16; 2]; |
| 273 | /// | 274 | /// |
| 274 | /// adc.read_async( | 275 | /// adc.read( |
| 275 | /// p.DMA1_CH2, | 276 | /// p.DMA1_CH2.reborrow(), |
| 276 | /// [ | 277 | /// [ |
| 277 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), | 278 | /// (&mut *adc_pin0, SampleTime::CYCLES160_5), |
| 278 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), | 279 | /// (&mut *adc_pin1, SampleTime::CYCLES160_5), |
| @@ -285,7 +286,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 285 | /// ``` | 286 | /// ``` |
| 286 | pub async fn read( | 287 | pub async fn read( |
| 287 | &mut self, | 288 | &mut self, |
| 288 | rx_dma: &mut impl RxDma<T>, | 289 | rx_dma: Peri<'_, impl RxDma<T>>, |
| 289 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | 290 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, |
| 290 | readings: &mut [u16], | 291 | readings: &mut [u16], |
| 291 | ) { | 292 | ) { |
| @@ -366,14 +367,14 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 366 | T::regs().cfgr().modify(|reg| { | 367 | T::regs().cfgr().modify(|reg| { |
| 367 | reg.set_discen(false); | 368 | reg.set_discen(false); |
| 368 | reg.set_cont(true); | 369 | reg.set_cont(true); |
| 369 | reg.set_dmacfg(Dmacfg::ONESHOT); | 370 | reg.set_dmacfg(Dmacfg::ONE_SHOT); |
| 370 | reg.set_dmaen(true); | 371 | reg.set_dmaen(true); |
| 371 | }); | 372 | }); |
| 372 | #[cfg(any(adc_g0, adc_u0))] | 373 | #[cfg(any(adc_g0, adc_u0))] |
| 373 | T::regs().cfgr1().modify(|reg| { | 374 | T::regs().cfgr1().modify(|reg| { |
| 374 | reg.set_discen(false); | 375 | reg.set_discen(false); |
| 375 | reg.set_cont(true); | 376 | reg.set_cont(true); |
| 376 | reg.set_dmacfg(Dmacfg::ONESHOT); | 377 | reg.set_dmacfg(Dmacfg::ONE_SHOT); |
| 377 | reg.set_dmaen(true); | 378 | reg.set_dmaen(true); |
| 378 | }); | 379 | }); |
| 379 | 380 | ||
| @@ -413,7 +414,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 413 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 414 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 414 | // RM0492, RM0481, etc. | 415 | // RM0492, RM0481, etc. |
| 415 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 416 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." |
| 416 | #[cfg(adc_h5)] | 417 | #[cfg(any(adc_h5, adc_h7rs))] |
| 417 | if channel.channel() == 0 { | 418 | if channel.channel() == 0 { |
| 418 | T::regs().or().modify(|reg| reg.set_op0(true)); | 419 | T::regs().or().modify(|reg| reg.set_op0(true)); |
| 419 | } | 420 | } |
| @@ -446,7 +447,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 446 | 447 | ||
| 447 | // RM0492, RM0481, etc. | 448 | // RM0492, RM0481, etc. |
| 448 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." | 449 | // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." |
| 449 | #[cfg(adc_h5)] | 450 | #[cfg(any(adc_h5, adc_h7rs))] |
| 450 | if channel.channel() == 0 { | 451 | if channel.channel() == 0 { |
| 451 | T::regs().or().modify(|reg| reg.set_op0(false)); | 452 | T::regs().or().modify(|reg| reg.set_op0(false)); |
| 452 | } | 453 | } |
| @@ -474,7 +475,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 474 | if #[cfg(any(adc_g0, adc_u0))] { | 475 | if #[cfg(any(adc_g0, adc_u0))] { |
| 475 | // On G0 and U6 all channels use the same sampling time. | 476 | // On G0 and U6 all channels use the same sampling time. |
| 476 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); | 477 | T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); |
| 477 | } else if #[cfg(adc_h5)] { | 478 | } else if #[cfg(any(adc_h5, adc_h7rs))] { |
| 478 | match _ch { | 479 | match _ch { |
| 479 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 480 | 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), |
| 480 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), | 481 | _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), |
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 63b5b58ea..39e0d51b9 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #[cfg(not(stm32u5))] | ||
| 2 | use pac::adc::vals::{Adcaldif, Boost}; | ||
| 1 | #[allow(unused)] | 3 | #[allow(unused)] |
| 2 | use pac::adc::vals::{Adcaldif, Adstp, Boost, Difsel, Dmngt, Exten, Pcsel}; | 4 | use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; |
| 3 | use pac::adccommon::vals::Presc; | 5 | use pac::adccommon::vals::Presc; |
| 4 | 6 | ||
| 5 | use super::{ | 7 | use super::{ |
| @@ -7,7 +9,7 @@ use super::{ | |||
| 7 | }; | 9 | }; |
| 8 | use crate::dma::Transfer; | 10 | use crate::dma::Transfer; |
| 9 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 10 | use crate::{pac, rcc, Peripheral}; | 12 | use crate::{pac, rcc, Peri}; |
| 11 | 13 | ||
| 12 | /// Default VREF voltage used for sample conversion to millivolts. | 14 | /// Default VREF voltage used for sample conversion to millivolts. |
| 13 | pub const VREF_DEFAULT_MV: u32 = 3300; | 15 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| @@ -19,6 +21,8 @@ pub const VREF_CALIB_MV: u32 = 3300; | |||
| 19 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); | 21 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); |
| 20 | #[cfg(stm32h7)] | 22 | #[cfg(stm32h7)] |
| 21 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); | 23 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); |
| 24 | #[cfg(stm32u5)] | ||
| 25 | const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); | ||
| 22 | 26 | ||
| 23 | #[cfg(stm32g4)] | 27 | #[cfg(stm32g4)] |
| 24 | const VREF_CHANNEL: u8 = 18; | 28 | const VREF_CHANNEL: u8 = 18; |
| @@ -31,8 +35,16 @@ const VREF_CHANNEL: u8 = 19; | |||
| 31 | const TEMP_CHANNEL: u8 = 18; | 35 | const TEMP_CHANNEL: u8 = 18; |
| 32 | 36 | ||
| 33 | // TODO this should be 14 for H7a/b/35 | 37 | // TODO this should be 14 for H7a/b/35 |
| 38 | #[cfg(not(stm32u5))] | ||
| 34 | const VBAT_CHANNEL: u8 = 17; | 39 | const VBAT_CHANNEL: u8 = 17; |
| 35 | 40 | ||
| 41 | #[cfg(stm32u5)] | ||
| 42 | const VREF_CHANNEL: u8 = 0; | ||
| 43 | #[cfg(stm32u5)] | ||
| 44 | const TEMP_CHANNEL: u8 = 19; | ||
| 45 | #[cfg(stm32u5)] | ||
| 46 | const VBAT_CHANNEL: u8 = 18; | ||
| 47 | |||
| 36 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs | 48 | // NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs |
| 37 | /// Internal voltage reference channel. | 49 | /// Internal voltage reference channel. |
| 38 | pub struct VrefInt; | 50 | pub struct VrefInt; |
| @@ -146,8 +158,7 @@ pub enum Averaging { | |||
| 146 | 158 | ||
| 147 | impl<'d, T: Instance> Adc<'d, T> { | 159 | impl<'d, T: Instance> Adc<'d, T> { |
| 148 | /// Create a new ADC driver. | 160 | /// Create a new ADC driver. |
| 149 | pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self { | 161 | pub fn new(adc: Peri<'d, T>) -> Self { |
| 150 | embassy_hal_internal::into_ref!(adc); | ||
| 151 | rcc::enable_and_reset::<T>(); | 162 | rcc::enable_and_reset::<T>(); |
| 152 | 163 | ||
| 153 | let prescaler = Prescaler::from_ker_ck(T::frequency()); | 164 | let prescaler = Prescaler::from_ker_ck(T::frequency()); |
| @@ -155,7 +166,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 155 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); | 166 | T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); |
| 156 | 167 | ||
| 157 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); | 168 | let frequency = Hertz(T::frequency().0 / prescaler.divisor()); |
| 158 | info!("ADC frequency set to {} Hz", frequency.0); | 169 | info!("ADC frequency set to {}", frequency); |
| 159 | 170 | ||
| 160 | if frequency > MAX_ADC_CLK_FREQ { | 171 | if frequency > MAX_ADC_CLK_FREQ { |
| 161 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); | 172 | panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); |
| @@ -202,14 +213,15 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 202 | fn configure_differential_inputs(&mut self) { | 213 | fn configure_differential_inputs(&mut self) { |
| 203 | T::regs().difsel().modify(|w| { | 214 | T::regs().difsel().modify(|w| { |
| 204 | for n in 0..20 { | 215 | for n in 0..20 { |
| 205 | w.set_difsel(n, Difsel::SINGLEENDED); | 216 | w.set_difsel(n, Difsel::SINGLE_ENDED); |
| 206 | } | 217 | } |
| 207 | }); | 218 | }); |
| 208 | } | 219 | } |
| 209 | 220 | ||
| 210 | fn calibrate(&mut self) { | 221 | fn calibrate(&mut self) { |
| 211 | T::regs().cr().modify(|w| { | 222 | T::regs().cr().modify(|w| { |
| 212 | w.set_adcaldif(Adcaldif::SINGLEENDED); | 223 | #[cfg(not(adc_u5))] |
| 224 | w.set_adcaldif(Adcaldif::SINGLE_ENDED); | ||
| 213 | w.set_adcallin(true); | 225 | w.set_adcallin(true); |
| 214 | }); | 226 | }); |
| 215 | 227 | ||
| @@ -293,7 +305,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 293 | 305 | ||
| 294 | T::regs().cfgr2().modify(|reg| { | 306 | T::regs().cfgr2().modify(|reg| { |
| 295 | reg.set_rovse(enable); | 307 | reg.set_rovse(enable); |
| 296 | reg.set_osvr(samples); | 308 | reg.set_ovsr(samples); |
| 297 | reg.set_ovss(right_shift); | 309 | reg.set_ovss(right_shift); |
| 298 | }) | 310 | }) |
| 299 | } | 311 | } |
| @@ -331,12 +343,12 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 331 | /// use embassy_stm32::adc::{Adc, AdcChannel} | 343 | /// use embassy_stm32::adc::{Adc, AdcChannel} |
| 332 | /// | 344 | /// |
| 333 | /// let mut adc = Adc::new(p.ADC1); | 345 | /// let mut adc = Adc::new(p.ADC1); |
| 334 | /// let mut adc_pin0 = p.PA0.degrade_adc(); | 346 | /// let mut adc_pin0 = p.PA0.into(); |
| 335 | /// let mut adc_pin2 = p.PA2.degrade_adc(); | 347 | /// let mut adc_pin2 = p.PA2.into(); |
| 336 | /// let mut measurements = [0u16; 2]; | 348 | /// let mut measurements = [0u16; 2]; |
| 337 | /// | 349 | /// |
| 338 | /// adc.read_async( | 350 | /// adc.read( |
| 339 | /// p.DMA2_CH0, | 351 | /// p.DMA2_CH0.reborrow(), |
| 340 | /// [ | 352 | /// [ |
| 341 | /// (&mut *adc_pin0, SampleTime::CYCLES112), | 353 | /// (&mut *adc_pin0, SampleTime::CYCLES112), |
| 342 | /// (&mut *adc_pin2, SampleTime::CYCLES112), | 354 | /// (&mut *adc_pin2, SampleTime::CYCLES112), |
| @@ -349,7 +361,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 349 | /// ``` | 361 | /// ``` |
| 350 | pub async fn read( | 362 | pub async fn read( |
| 351 | &mut self, | 363 | &mut self, |
| 352 | rx_dma: &mut impl RxDma<T>, | 364 | rx_dma: Peri<'_, impl RxDma<T>>, |
| 353 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, | 365 | sequence: impl ExactSizeIterator<Item = (&mut AnyAdcChannel<T>, SampleTime)>, |
| 354 | readings: &mut [u16], | 366 | readings: &mut [u16], |
| 355 | ) { | 367 | ) { |
| @@ -407,7 +419,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 407 | }); | 419 | }); |
| 408 | T::regs().cfgr().modify(|reg| { | 420 | T::regs().cfgr().modify(|reg| { |
| 409 | reg.set_cont(true); | 421 | reg.set_cont(true); |
| 410 | reg.set_dmngt(Dmngt::DMA_ONESHOT); | 422 | reg.set_dmngt(Dmngt::DMA_ONE_SHOT); |
| 411 | }); | 423 | }); |
| 412 | 424 | ||
| 413 | let request = rx_dma.request(); | 425 | let request = rx_dma.request(); |
| @@ -446,7 +458,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 446 | 458 | ||
| 447 | Self::set_channel_sample_time(channel, sample_time); | 459 | Self::set_channel_sample_time(channel, sample_time); |
| 448 | 460 | ||
| 449 | #[cfg(stm32h7)] | 461 | #[cfg(any(stm32h7, stm32u5))] |
| 450 | { | 462 | { |
| 451 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); | 463 | T::regs().cfgr2().modify(|w| w.set_lshift(0)); |
| 452 | T::regs() | 464 | T::regs() |
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 278c93ff4..305666d5b 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs | |||
| @@ -6,7 +6,7 @@ use core::marker::PhantomData; | |||
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::interrupt::InterruptExt; | 8 | use embassy_hal_internal::interrupt::InterruptExt; |
| 9 | use embassy_hal_internal::into_ref; | 9 | use embassy_hal_internal::PeripheralType; |
| 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 11 | use embassy_sync::channel::Channel; | 11 | use embassy_sync::channel::Channel; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| @@ -17,11 +17,11 @@ use self::registers::{Registers, RxFifo}; | |||
| 17 | pub use super::common::{BufferedCanReceiver, BufferedCanSender}; | 17 | pub use super::common::{BufferedCanReceiver, BufferedCanSender}; |
| 18 | use super::frame::{Envelope, Frame}; | 18 | use super::frame::{Envelope, Frame}; |
| 19 | use super::util; | 19 | use super::util; |
| 20 | use crate::can::enums::{BusError, TryReadError}; | 20 | use crate::can::enums::{BusError, InternalOperation, TryReadError}; |
| 21 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 21 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 22 | use crate::interrupt::typelevel::Interrupt; | 22 | use crate::interrupt::typelevel::Interrupt; |
| 23 | use crate::rcc::{self, RccPeripheral}; | 23 | use crate::rcc::{self, RccPeripheral}; |
| 24 | use crate::{interrupt, peripherals, Peripheral}; | 24 | use crate::{interrupt, peripherals, Peri}; |
| 25 | 25 | ||
| 26 | /// Interrupt handler. | 26 | /// Interrupt handler. |
| 27 | pub struct TxInterruptHandler<T: Instance> { | 27 | pub struct TxInterruptHandler<T: Instance> { |
| @@ -68,7 +68,6 @@ pub struct SceInterruptHandler<T: Instance> { | |||
| 68 | 68 | ||
| 69 | impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> { | 69 | impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> { |
| 70 | unsafe fn on_interrupt() { | 70 | unsafe fn on_interrupt() { |
| 71 | info!("sce irq"); | ||
| 72 | let msr = T::regs().msr(); | 71 | let msr = T::regs().msr(); |
| 73 | let msr_val = msr.read(); | 72 | let msr_val = msr.read(); |
| 74 | 73 | ||
| @@ -76,9 +75,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup | |||
| 76 | msr.modify(|m| m.set_slaki(true)); | 75 | msr.modify(|m| m.set_slaki(true)); |
| 77 | T::state().err_waker.wake(); | 76 | T::state().err_waker.wake(); |
| 78 | } else if msr_val.erri() { | 77 | } else if msr_val.erri() { |
| 79 | info!("Error interrupt"); | ||
| 80 | // Disable the interrupt, but don't acknowledge the error, so that it can be | 78 | // Disable the interrupt, but don't acknowledge the error, so that it can be |
| 81 | // forwarded off the the bus message consumer. If we don't provide some way for | 79 | // forwarded off the bus message consumer. If we don't provide some way for |
| 82 | // downstream code to determine that it has already provided this bus error instance | 80 | // downstream code to determine that it has already provided this bus error instance |
| 83 | // to the bus message consumer, we are doomed to re-provide a single error instance for | 81 | // to the bus message consumer, we are doomed to re-provide a single error instance for |
| 84 | // an indefinite amount of time. | 82 | // an indefinite amount of time. |
| @@ -175,16 +173,15 @@ impl<'d> Can<'d> { | |||
| 175 | /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. | 173 | /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. |
| 176 | /// You must call [Can::enable_non_blocking] to use the peripheral. | 174 | /// You must call [Can::enable_non_blocking] to use the peripheral. |
| 177 | pub fn new<T: Instance>( | 175 | pub fn new<T: Instance>( |
| 178 | _peri: impl Peripheral<P = T> + 'd, | 176 | _peri: Peri<'d, T>, |
| 179 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 177 | rx: Peri<'d, impl RxPin<T>>, |
| 180 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 178 | tx: Peri<'d, impl TxPin<T>>, |
| 181 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> | 179 | _irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>> |
| 182 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> | 180 | + interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>> |
| 183 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> | 181 | + interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>> |
| 184 | + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>> | 182 | + interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>> |
| 185 | + 'd, | 183 | + 'd, |
| 186 | ) -> Self { | 184 | ) -> Self { |
| 187 | into_ref!(_peri, rx, tx); | ||
| 188 | let info = T::info(); | 185 | let info = T::info(); |
| 189 | let regs = &T::info().regs; | 186 | let regs = &T::info().regs; |
| 190 | 187 | ||
| @@ -504,6 +501,14 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_ | |||
| 504 | pub fn reader(&self) -> BufferedCanReceiver { | 501 | pub fn reader(&self) -> BufferedCanReceiver { |
| 505 | self.rx.reader() | 502 | self.rx.reader() |
| 506 | } | 503 | } |
| 504 | |||
| 505 | /// Accesses the filter banks owned by this CAN peripheral. | ||
| 506 | /// | ||
| 507 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master | ||
| 508 | /// peripheral instead. | ||
| 509 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { | ||
| 510 | self.rx.modify_filters() | ||
| 511 | } | ||
| 507 | } | 512 | } |
| 508 | 513 | ||
| 509 | /// CAN driver, transmit half. | 514 | /// CAN driver, transmit half. |
| @@ -679,22 +684,18 @@ impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { | |||
| 679 | 684 | ||
| 680 | /// Returns a sender that can be used for sending CAN frames. | 685 | /// Returns a sender that can be used for sending CAN frames. |
| 681 | pub fn writer(&self) -> BufferedCanSender { | 686 | pub fn writer(&self) -> BufferedCanSender { |
| 687 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 682 | BufferedCanSender { | 688 | BufferedCanSender { |
| 683 | tx_buf: self.tx_buf.sender().into(), | 689 | tx_buf: self.tx_buf.sender().into(), |
| 684 | waker: self.info.tx_waker, | 690 | waker: self.info.tx_waker, |
| 691 | internal_operation: self.info.internal_operation, | ||
| 685 | } | 692 | } |
| 686 | } | 693 | } |
| 687 | } | 694 | } |
| 688 | 695 | ||
| 689 | impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { | 696 | impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { |
| 690 | fn drop(&mut self) { | 697 | fn drop(&mut self) { |
| 691 | critical_section::with(|_| { | 698 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); |
| 692 | let state = self.state as *const State; | ||
| 693 | unsafe { | ||
| 694 | let mut_state = state as *mut State; | ||
| 695 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 696 | } | ||
| 697 | }); | ||
| 698 | } | 699 | } |
| 699 | } | 700 | } |
| 700 | 701 | ||
| @@ -735,6 +736,14 @@ impl<'d> CanRx<'d> { | |||
| 735 | ) -> BufferedCanRx<'d, RX_BUF_SIZE> { | 736 | ) -> BufferedCanRx<'d, RX_BUF_SIZE> { |
| 736 | BufferedCanRx::new(self.info, self.state, self, rxb) | 737 | BufferedCanRx::new(self.info, self.state, self, rxb) |
| 737 | } | 738 | } |
| 739 | |||
| 740 | /// Accesses the filter banks owned by this CAN peripheral. | ||
| 741 | /// | ||
| 742 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master | ||
| 743 | /// peripheral instead. | ||
| 744 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { | ||
| 745 | unsafe { MasterFilters::new(self.info) } | ||
| 746 | } | ||
| 738 | } | 747 | } |
| 739 | 748 | ||
| 740 | /// User supplied buffer for RX Buffering | 749 | /// User supplied buffer for RX Buffering |
| @@ -744,16 +753,16 @@ pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result< | |||
| 744 | pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> { | 753 | pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> { |
| 745 | info: &'static Info, | 754 | info: &'static Info, |
| 746 | state: &'static State, | 755 | state: &'static State, |
| 747 | _rx: CanRx<'d>, | 756 | rx: CanRx<'d>, |
| 748 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | 757 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| 749 | } | 758 | } |
| 750 | 759 | ||
| 751 | impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | 760 | impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { |
| 752 | fn new(info: &'static Info, state: &'static State, _rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self { | 761 | fn new(info: &'static Info, state: &'static State, rx: CanRx<'d>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self { |
| 753 | BufferedCanRx { | 762 | BufferedCanRx { |
| 754 | info, | 763 | info, |
| 755 | state, | 764 | state, |
| 756 | _rx, | 765 | rx, |
| 757 | rx_buf, | 766 | rx_buf, |
| 758 | } | 767 | } |
| 759 | .setup() | 768 | .setup() |
| @@ -811,19 +820,25 @@ impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { | |||
| 811 | 820 | ||
| 812 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 821 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 813 | pub fn reader(&self) -> BufferedCanReceiver { | 822 | pub fn reader(&self) -> BufferedCanReceiver { |
| 814 | self.rx_buf.receiver().into() | 823 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); |
| 824 | BufferedCanReceiver { | ||
| 825 | rx_buf: self.rx_buf.receiver().into(), | ||
| 826 | internal_operation: self.info.internal_operation, | ||
| 827 | } | ||
| 828 | } | ||
| 829 | |||
| 830 | /// Accesses the filter banks owned by this CAN peripheral. | ||
| 831 | /// | ||
| 832 | /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master | ||
| 833 | /// peripheral instead. | ||
| 834 | pub fn modify_filters(&mut self) -> MasterFilters<'_> { | ||
| 835 | self.rx.modify_filters() | ||
| 815 | } | 836 | } |
| 816 | } | 837 | } |
| 817 | 838 | ||
| 818 | impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> { | 839 | impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> { |
| 819 | fn drop(&mut self) { | 840 | fn drop(&mut self) { |
| 820 | critical_section::with(|_| { | 841 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); |
| 821 | let state = self.state as *const State; | ||
| 822 | unsafe { | ||
| 823 | let mut_state = state as *mut State; | ||
| 824 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 825 | } | ||
| 826 | }); | ||
| 827 | } | 842 | } |
| 828 | } | 843 | } |
| 829 | 844 | ||
| @@ -895,7 +910,7 @@ impl RxMode { | |||
| 895 | RxFifo::Fifo0 => 0usize, | 910 | RxFifo::Fifo0 => 0usize, |
| 896 | RxFifo::Fifo1 => 1usize, | 911 | RxFifo::Fifo1 => 1usize, |
| 897 | }; | 912 | }; |
| 898 | T::regs().ier().write(|w| { | 913 | T::regs().ier().modify(|w| { |
| 899 | w.set_fmpie(fifo_idx, false); | 914 | w.set_fmpie(fifo_idx, false); |
| 900 | }); | 915 | }); |
| 901 | waker.wake(); | 916 | waker.wake(); |
| @@ -938,18 +953,22 @@ impl RxMode { | |||
| 938 | Self::NonBuffered(_) => { | 953 | Self::NonBuffered(_) => { |
| 939 | let registers = &info.regs; | 954 | let registers = &info.regs; |
| 940 | if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { | 955 | if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { |
| 941 | registers.0.ier().write(|w| { | 956 | registers.0.ier().modify(|w| { |
| 942 | w.set_fmpie(0, true); | 957 | w.set_fmpie(0, true); |
| 943 | }); | 958 | }); |
| 944 | Ok(msg) | 959 | Ok(msg) |
| 945 | } else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) { | 960 | } else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) { |
| 946 | registers.0.ier().write(|w| { | 961 | registers.0.ier().modify(|w| { |
| 947 | w.set_fmpie(1, true); | 962 | w.set_fmpie(1, true); |
| 948 | }); | 963 | }); |
| 949 | Ok(msg) | 964 | Ok(msg) |
| 950 | } else if let Some(err) = registers.curr_error() { | 965 | } else if let Some(err) = registers.curr_error() { |
| 951 | Err(TryReadError::BusError(err)) | 966 | Err(TryReadError::BusError(err)) |
| 952 | } else { | 967 | } else { |
| 968 | registers.0.ier().modify(|w| { | ||
| 969 | w.set_fmpie(0, true); | ||
| 970 | w.set_fmpie(1, true); | ||
| 971 | }); | ||
| 953 | Err(TryReadError::Empty) | 972 | Err(TryReadError::Empty) |
| 954 | } | 973 | } |
| 955 | } | 974 | } |
| @@ -1022,6 +1041,8 @@ pub(crate) struct State { | |||
| 1022 | pub(crate) rx_mode: RxMode, | 1041 | pub(crate) rx_mode: RxMode, |
| 1023 | pub(crate) tx_mode: TxMode, | 1042 | pub(crate) tx_mode: TxMode, |
| 1024 | pub err_waker: AtomicWaker, | 1043 | pub err_waker: AtomicWaker, |
| 1044 | receiver_instance_count: usize, | ||
| 1045 | sender_instance_count: usize, | ||
| 1025 | } | 1046 | } |
| 1026 | 1047 | ||
| 1027 | impl State { | 1048 | impl State { |
| @@ -1030,6 +1051,8 @@ impl State { | |||
| 1030 | rx_mode: RxMode::NonBuffered(AtomicWaker::new()), | 1051 | rx_mode: RxMode::NonBuffered(AtomicWaker::new()), |
| 1031 | tx_mode: TxMode::NonBuffered(AtomicWaker::new()), | 1052 | tx_mode: TxMode::NonBuffered(AtomicWaker::new()), |
| 1032 | err_waker: AtomicWaker::new(), | 1053 | err_waker: AtomicWaker::new(), |
| 1054 | receiver_instance_count: 1, | ||
| 1055 | sender_instance_count: 1, | ||
| 1033 | } | 1056 | } |
| 1034 | } | 1057 | } |
| 1035 | } | 1058 | } |
| @@ -1041,6 +1064,7 @@ pub(crate) struct Info { | |||
| 1041 | rx1_interrupt: crate::interrupt::Interrupt, | 1064 | rx1_interrupt: crate::interrupt::Interrupt, |
| 1042 | sce_interrupt: crate::interrupt::Interrupt, | 1065 | sce_interrupt: crate::interrupt::Interrupt, |
| 1043 | tx_waker: fn(), | 1066 | tx_waker: fn(), |
| 1067 | internal_operation: fn(InternalOperation), | ||
| 1044 | 1068 | ||
| 1045 | /// The total number of filter banks available to the instance. | 1069 | /// The total number of filter banks available to the instance. |
| 1046 | /// | 1070 | /// |
| @@ -1053,11 +1077,12 @@ trait SealedInstance { | |||
| 1053 | fn regs() -> crate::pac::can::Can; | 1077 | fn regs() -> crate::pac::can::Can; |
| 1054 | fn state() -> &'static State; | 1078 | fn state() -> &'static State; |
| 1055 | unsafe fn mut_state() -> &'static mut State; | 1079 | unsafe fn mut_state() -> &'static mut State; |
| 1080 | fn internal_operation(val: InternalOperation); | ||
| 1056 | } | 1081 | } |
| 1057 | 1082 | ||
| 1058 | /// CAN instance trait. | 1083 | /// CAN instance trait. |
| 1059 | #[allow(private_bounds)] | 1084 | #[allow(private_bounds)] |
| 1060 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral + 'static { | 1085 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static { |
| 1061 | /// TX interrupt for this instance. | 1086 | /// TX interrupt for this instance. |
| 1062 | type TXInterrupt: crate::interrupt::typelevel::Interrupt; | 1087 | type TXInterrupt: crate::interrupt::typelevel::Interrupt; |
| 1063 | /// RX0 interrupt for this instance. | 1088 | /// RX0 interrupt for this instance. |
| @@ -1110,6 +1135,7 @@ foreach_peripheral!( | |||
| 1110 | rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, | 1135 | rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, |
| 1111 | sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, | 1136 | sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, |
| 1112 | tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, | 1137 | tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, |
| 1138 | internal_operation: peripherals::$inst::internal_operation, | ||
| 1113 | num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, | 1139 | num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, |
| 1114 | }; | 1140 | }; |
| 1115 | &INFO | 1141 | &INFO |
| @@ -1125,6 +1151,37 @@ foreach_peripheral!( | |||
| 1125 | fn state() -> &'static State { | 1151 | fn state() -> &'static State { |
| 1126 | unsafe { peripherals::$inst::mut_state() } | 1152 | unsafe { peripherals::$inst::mut_state() } |
| 1127 | } | 1153 | } |
| 1154 | |||
| 1155 | |||
| 1156 | fn internal_operation(val: InternalOperation) { | ||
| 1157 | critical_section::with(|_| { | ||
| 1158 | //let state = self.state as *const State; | ||
| 1159 | unsafe { | ||
| 1160 | //let mut_state = state as *mut State; | ||
| 1161 | let mut_state = peripherals::$inst::mut_state(); | ||
| 1162 | match val { | ||
| 1163 | InternalOperation::NotifySenderCreated => { | ||
| 1164 | mut_state.sender_instance_count += 1; | ||
| 1165 | } | ||
| 1166 | InternalOperation::NotifySenderDestroyed => { | ||
| 1167 | mut_state.sender_instance_count -= 1; | ||
| 1168 | if ( 0 == mut_state.sender_instance_count) { | ||
| 1169 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 1170 | } | ||
| 1171 | } | ||
| 1172 | InternalOperation::NotifyReceiverCreated => { | ||
| 1173 | mut_state.receiver_instance_count += 1; | ||
| 1174 | } | ||
| 1175 | InternalOperation::NotifyReceiverDestroyed => { | ||
| 1176 | mut_state.receiver_instance_count -= 1; | ||
| 1177 | if ( 0 == mut_state.receiver_instance_count) { | ||
| 1178 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | } | ||
| 1182 | } | ||
| 1183 | }); | ||
| 1184 | } | ||
| 1128 | } | 1185 | } |
| 1129 | 1186 | ||
| 1130 | impl Instance for peripherals::$inst { | 1187 | impl Instance for peripherals::$inst { |
diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index c5de1c683..c295b0f50 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs | |||
| @@ -2,7 +2,7 @@ use core::cmp::Ordering; | |||
| 2 | use core::convert::Infallible; | 2 | use core::convert::Infallible; |
| 3 | 3 | ||
| 4 | pub use embedded_can::{ExtendedId, Id, StandardId}; | 4 | pub use embedded_can::{ExtendedId, Id, StandardId}; |
| 5 | use stm32_metapac::can::vals::Lec; | 5 | use stm32_metapac::can::vals::{Lec, Rtr}; |
| 6 | 6 | ||
| 7 | use super::{Mailbox, TransmitStatus}; | 7 | use super::{Mailbox, TransmitStatus}; |
| 8 | use crate::can::enums::BusError; | 8 | use crate::can::enums::BusError; |
| @@ -166,16 +166,16 @@ impl Registers { | |||
| 166 | return Some(BusError::BusPassive); | 166 | return Some(BusError::BusPassive); |
| 167 | } else if err.ewgf() { | 167 | } else if err.ewgf() { |
| 168 | return Some(BusError::BusWarning); | 168 | return Some(BusError::BusWarning); |
| 169 | } else if err.lec() != Lec::NOERROR { | 169 | } else if err.lec() != Lec::NO_ERROR { |
| 170 | return Some(match err.lec() { | 170 | return Some(match err.lec() { |
| 171 | Lec::STUFF => BusError::Stuff, | 171 | Lec::STUFF => BusError::Stuff, |
| 172 | Lec::FORM => BusError::Form, | 172 | Lec::FORM => BusError::Form, |
| 173 | Lec::ACK => BusError::Acknowledge, | 173 | Lec::ACK => BusError::Acknowledge, |
| 174 | Lec::BITRECESSIVE => BusError::BitRecessive, | 174 | Lec::BIT_RECESSIVE => BusError::BitRecessive, |
| 175 | Lec::BITDOMINANT => BusError::BitDominant, | 175 | Lec::BIT_DOMINANT => BusError::BitDominant, |
| 176 | Lec::CRC => BusError::Crc, | 176 | Lec::CRC => BusError::Crc, |
| 177 | Lec::CUSTOM => BusError::Software, | 177 | Lec::CUSTOM => BusError::Software, |
| 178 | Lec::NOERROR => unreachable!(), | 178 | Lec::NO_ERROR => unreachable!(), |
| 179 | }); | 179 | }); |
| 180 | } | 180 | } |
| 181 | None | 181 | None |
| @@ -299,13 +299,16 @@ impl Registers { | |||
| 299 | mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); | 299 | mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); |
| 300 | 300 | ||
| 301 | mb.tdlr() | 301 | mb.tdlr() |
| 302 | .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.data()[0..4].try_into()))); | 302 | .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.raw_data()[0..4].try_into()))); |
| 303 | mb.tdhr() | 303 | mb.tdhr() |
| 304 | .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.data()[4..8].try_into()))); | 304 | .write(|w| w.0 = u32::from_ne_bytes(unwrap!(frame.raw_data()[4..8].try_into()))); |
| 305 | let id: IdReg = frame.id().into(); | 305 | let id: IdReg = frame.id().into(); |
| 306 | mb.tir().write(|w| { | 306 | mb.tir().write(|w| { |
| 307 | w.0 = id.0; | 307 | w.0 = id.0; |
| 308 | w.set_txrq(true); | 308 | w.set_txrq(true); |
| 309 | if frame.header().rtr() { | ||
| 310 | w.set_rtr(Rtr::REMOTE); | ||
| 311 | } | ||
| 309 | }); | 312 | }); |
| 310 | } | 313 | } |
| 311 | 314 | ||
diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index a54b54f6e..386d4467c 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs | |||
| @@ -1,43 +1,43 @@ | |||
| 1 | use embassy_sync::channel::{DynamicReceiver, DynamicSender}; | 1 | use embassy_sync::channel::{SendDynamicReceiver, SendDynamicSender}; |
| 2 | 2 | ||
| 3 | use super::enums::*; | 3 | use super::enums::*; |
| 4 | use super::frame::*; | 4 | use super::frame::*; |
| 5 | 5 | ||
| 6 | pub(crate) struct ClassicBufferedRxInner { | 6 | pub(crate) struct ClassicBufferedRxInner { |
| 7 | pub rx_sender: DynamicSender<'static, Result<Envelope, BusError>>, | 7 | pub rx_sender: SendDynamicSender<'static, Result<Envelope, BusError>>, |
| 8 | } | 8 | } |
| 9 | pub(crate) struct ClassicBufferedTxInner { | 9 | pub(crate) struct ClassicBufferedTxInner { |
| 10 | pub tx_receiver: DynamicReceiver<'static, Frame>, | 10 | pub tx_receiver: SendDynamicReceiver<'static, Frame>, |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | #[cfg(any(can_fdcan_v1, can_fdcan_h7))] | 13 | #[cfg(any(can_fdcan_v1, can_fdcan_h7))] |
| 14 | 14 | ||
| 15 | pub(crate) struct FdBufferedRxInner { | 15 | pub(crate) struct FdBufferedRxInner { |
| 16 | pub rx_sender: DynamicSender<'static, Result<FdEnvelope, BusError>>, | 16 | pub rx_sender: SendDynamicSender<'static, Result<FdEnvelope, BusError>>, |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | #[cfg(any(can_fdcan_v1, can_fdcan_h7))] | 19 | #[cfg(any(can_fdcan_v1, can_fdcan_h7))] |
| 20 | pub(crate) struct FdBufferedTxInner { | 20 | pub(crate) struct FdBufferedTxInner { |
| 21 | pub tx_receiver: DynamicReceiver<'static, FdFrame>, | 21 | pub tx_receiver: SendDynamicReceiver<'static, FdFrame>, |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | /// Sender that can be used for sending CAN frames. | 24 | /// Sender that can be used for sending CAN frames. |
| 25 | #[derive(Copy, Clone)] | 25 | pub struct BufferedSender<'ch, FRAME> { |
| 26 | pub struct BufferedCanSender { | 26 | pub(crate) tx_buf: embassy_sync::channel::SendDynamicSender<'ch, FRAME>, |
| 27 | pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, Frame>, | ||
| 28 | pub(crate) waker: fn(), | 27 | pub(crate) waker: fn(), |
| 28 | pub(crate) internal_operation: fn(InternalOperation), | ||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | impl BufferedCanSender { | 31 | impl<'ch, FRAME> BufferedSender<'ch, FRAME> { |
| 32 | /// Async write frame to TX buffer. | 32 | /// Async write frame to TX buffer. |
| 33 | pub fn try_write(&mut self, frame: Frame) -> Result<(), embassy_sync::channel::TrySendError<Frame>> { | 33 | pub fn try_write(&mut self, frame: FRAME) -> Result<(), embassy_sync::channel::TrySendError<FRAME>> { |
| 34 | self.tx_buf.try_send(frame)?; | 34 | self.tx_buf.try_send(frame)?; |
| 35 | (self.waker)(); | 35 | (self.waker)(); |
| 36 | Ok(()) | 36 | Ok(()) |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | /// Async write frame to TX buffer. | 39 | /// Async write frame to TX buffer. |
| 40 | pub async fn write(&mut self, frame: Frame) { | 40 | pub async fn write(&mut self, frame: FRAME) { |
| 41 | self.tx_buf.send(frame).await; | 41 | self.tx_buf.send(frame).await; |
| 42 | (self.waker)(); | 42 | (self.waker)(); |
| 43 | } | 43 | } |
| @@ -48,5 +48,77 @@ impl BufferedCanSender { | |||
| 48 | } | 48 | } |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | impl<'ch, FRAME> Clone for BufferedSender<'ch, FRAME> { | ||
| 52 | fn clone(&self) -> Self { | ||
| 53 | (self.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 54 | Self { | ||
| 55 | tx_buf: self.tx_buf, | ||
| 56 | waker: self.waker, | ||
| 57 | internal_operation: self.internal_operation, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl<'ch, FRAME> Drop for BufferedSender<'ch, FRAME> { | ||
| 63 | fn drop(&mut self) { | ||
| 64 | (self.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Sender that can be used for sending Classic CAN frames. | ||
| 69 | pub type BufferedCanSender = BufferedSender<'static, Frame>; | ||
| 70 | |||
| 51 | /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 71 | /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 52 | pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result<Envelope, BusError>>; | 72 | pub struct BufferedReceiver<'ch, ENVELOPE> { |
| 73 | pub(crate) rx_buf: embassy_sync::channel::SendDynamicReceiver<'ch, Result<ENVELOPE, BusError>>, | ||
| 74 | pub(crate) internal_operation: fn(InternalOperation), | ||
| 75 | } | ||
| 76 | |||
| 77 | impl<'ch, ENVELOPE> BufferedReceiver<'ch, ENVELOPE> { | ||
| 78 | /// Receive the next frame. | ||
| 79 | /// | ||
| 80 | /// See [`Channel::receive()`]. | ||
| 81 | pub fn receive(&self) -> embassy_sync::channel::DynamicReceiveFuture<'_, Result<ENVELOPE, BusError>> { | ||
| 82 | self.rx_buf.receive() | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Attempt to immediately receive the next frame. | ||
| 86 | /// | ||
| 87 | /// See [`Channel::try_receive()`] | ||
| 88 | pub fn try_receive(&self) -> Result<Result<ENVELOPE, BusError>, embassy_sync::channel::TryReceiveError> { | ||
| 89 | self.rx_buf.try_receive() | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Allows a poll_fn to poll until the channel is ready to receive | ||
| 93 | /// | ||
| 94 | /// See [`Channel::poll_ready_to_receive()`] | ||
| 95 | pub fn poll_ready_to_receive(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> { | ||
| 96 | self.rx_buf.poll_ready_to_receive(cx) | ||
| 97 | } | ||
| 98 | |||
| 99 | /// Poll the channel for the next frame | ||
| 100 | /// | ||
| 101 | /// See [`Channel::poll_receive()`] | ||
| 102 | pub fn poll_receive(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<Result<ENVELOPE, BusError>> { | ||
| 103 | self.rx_buf.poll_receive(cx) | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | impl<'ch, ENVELOPE> Clone for BufferedReceiver<'ch, ENVELOPE> { | ||
| 108 | fn clone(&self) -> Self { | ||
| 109 | (self.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 110 | Self { | ||
| 111 | rx_buf: self.rx_buf, | ||
| 112 | internal_operation: self.internal_operation, | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | impl<'ch, ENVELOPE> Drop for BufferedReceiver<'ch, ENVELOPE> { | ||
| 118 | fn drop(&mut self) { | ||
| 119 | (self.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | /// A BufferedCanReceiver for Classic CAN frames. | ||
| 124 | pub type BufferedCanReceiver = BufferedReceiver<'static, Envelope>; | ||
diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index a5cca424d..97cb47640 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs | |||
| @@ -68,3 +68,17 @@ pub enum TryReadError { | |||
| 68 | /// Receive buffer is empty | 68 | /// Receive buffer is empty |
| 69 | Empty, | 69 | Empty, |
| 70 | } | 70 | } |
| 71 | |||
| 72 | /// Internal Operation | ||
| 73 | #[derive(Debug)] | ||
| 74 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 75 | pub enum InternalOperation { | ||
| 76 | /// Notify receiver created | ||
| 77 | NotifyReceiverCreated, | ||
| 78 | /// Notify receiver destroyed | ||
| 79 | NotifyReceiverDestroyed, | ||
| 80 | /// Notify sender created | ||
| 81 | NotifySenderCreated, | ||
| 82 | /// Notify sender destroyed | ||
| 83 | NotifySenderDestroyed, | ||
| 84 | } | ||
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs index 68161ca50..c6a66b469 100644 --- a/embassy-stm32/src/can/fd/config.rs +++ b/embassy-stm32/src/can/fd/config.rs | |||
| @@ -328,11 +328,15 @@ pub struct FdCanConfig { | |||
| 328 | /// | 328 | /// |
| 329 | /// Automatic retransmission is enabled by default. | 329 | /// Automatic retransmission is enabled by default. |
| 330 | pub automatic_retransmit: bool, | 330 | pub automatic_retransmit: bool, |
| 331 | /// Enabled or disables the pausing between transmissions | 331 | /// The transmit pause feature is intended for use in CAN systems where the CAN message |
| 332 | /// identifiers are permanently specified to specific values and cannot easily be changed. | ||
| 332 | /// | 333 | /// |
| 333 | /// This feature looses up burst transmissions coming from a single node and it protects against | 334 | /// These message identifiers can have a higher CAN arbitration priority than other defined |
| 334 | /// "babbling idiot" scenarios where the application program erroneously requests too many | 335 | /// messages, while in a specific application their relative arbitration priority must be inverse. |
| 335 | /// transmissions. | 336 | /// |
| 337 | /// This may lead to a case where one ECU sends a burst of CAN messages that cause | ||
| 338 | /// another ECU CAN messages to be delayed because that other messages have a lower | ||
| 339 | /// CAN arbitration priority. | ||
| 336 | pub transmit_pause: bool, | 340 | pub transmit_pause: bool, |
| 337 | /// Enabled or disables the pausing between transmissions | 341 | /// Enabled or disables the pausing between transmissions |
| 338 | /// | 342 | /// |
diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 07e3dddad..0a22f2c91 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs | |||
| @@ -71,11 +71,28 @@ impl Registers { | |||
| 71 | } | 71 | } |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | #[cfg(feature = "time")] | ||
| 75 | pub fn calc_timestamp(&self, ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 76 | let now_embassy = embassy_time::Instant::now(); | ||
| 77 | if ns_per_timer_tick == 0 { | ||
| 78 | return now_embassy; | ||
| 79 | } | ||
| 80 | let cantime = { self.regs.tscv().read().tsc() }; | ||
| 81 | let delta = cantime.overflowing_sub(ts_val).0 as u64; | ||
| 82 | let ns = ns_per_timer_tick * delta as u64; | ||
| 83 | now_embassy - embassy_time::Duration::from_nanos(ns) | ||
| 84 | } | ||
| 85 | |||
| 86 | #[cfg(not(feature = "time"))] | ||
| 87 | pub fn calc_timestamp(&self, _ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 88 | ts_val | ||
| 89 | } | ||
| 90 | |||
| 74 | pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) { | 91 | pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) { |
| 75 | let mailbox = self.tx_buffer_element(bufidx); | 92 | let mailbox = self.tx_buffer_element(bufidx); |
| 76 | mailbox.reset(); | 93 | mailbox.reset(); |
| 77 | put_tx_header(mailbox, header); | 94 | put_tx_header(mailbox, header); |
| 78 | put_tx_data(mailbox, &buffer[..header.len() as usize]); | 95 | put_tx_data(mailbox, buffer); |
| 79 | 96 | ||
| 80 | // Set <idx as Mailbox> as ready to transmit | 97 | // Set <idx as Mailbox> as ready to transmit |
| 81 | self.regs.txbar().modify(|w| w.set_ar(bufidx, true)); | 98 | self.regs.txbar().modify(|w| w.set_ar(bufidx, true)); |
| @@ -190,7 +207,7 @@ impl Registers { | |||
| 190 | DataLength::Fdcan(len) => len, | 207 | DataLength::Fdcan(len) => len, |
| 191 | DataLength::Classic(len) => len, | 208 | DataLength::Classic(len) => len, |
| 192 | }; | 209 | }; |
| 193 | if len as usize > ClassicData::MAX_DATA_LEN { | 210 | if len as usize > 8 { |
| 194 | return None; | 211 | return None; |
| 195 | } | 212 | } |
| 196 | 213 | ||
| @@ -200,7 +217,7 @@ impl Registers { | |||
| 200 | if header_reg.rtr().bit() { | 217 | if header_reg.rtr().bit() { |
| 201 | F::new_remote(id, len as usize) | 218 | F::new_remote(id, len as usize) |
| 202 | } else { | 219 | } else { |
| 203 | F::new(id, &data) | 220 | F::new(id, &data[0..(len as usize)]) |
| 204 | } | 221 | } |
| 205 | } else { | 222 | } else { |
| 206 | // Abort request failed because the frame was already sent (or being sent) on | 223 | // Abort request failed because the frame was already sent (or being sent) on |
| @@ -458,7 +475,7 @@ impl Registers { | |||
| 458 | /// [`FdCanConfig::set_transmit_pause`] | 475 | /// [`FdCanConfig::set_transmit_pause`] |
| 459 | #[inline] | 476 | #[inline] |
| 460 | pub fn set_transmit_pause(&self, enabled: bool) { | 477 | pub fn set_transmit_pause(&self, enabled: bool) { |
| 461 | self.regs.cccr().modify(|w| w.set_txp(!enabled)); | 478 | self.regs.cccr().modify(|w| w.set_txp(enabled)); |
| 462 | } | 479 | } |
| 463 | 480 | ||
| 464 | /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`] | 481 | /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`] |
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index c549313f3..97d22315a 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -4,16 +4,16 @@ use core::marker::PhantomData; | |||
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::interrupt::InterruptExt; | 6 | use embassy_hal_internal::interrupt::InterruptExt; |
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 7 | use embassy_hal_internal::PeripheralType; |
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 9 | use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; | 9 | use embassy_sync::channel::Channel; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | use crate::can::fd::peripheral::Registers; | 12 | use crate::can::fd::peripheral::Registers; |
| 13 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 13 | use crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed}; |
| 14 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 15 | use crate::rcc::{self, RccPeripheral}; | 15 | use crate::rcc::{self, RccPeripheral}; |
| 16 | use crate::{interrupt, peripherals, Peripheral}; | 16 | use crate::{interrupt, peripherals, Peri}; |
| 17 | 17 | ||
| 18 | pub(crate) mod fd; | 18 | pub(crate) mod fd; |
| 19 | 19 | ||
| @@ -52,36 +52,39 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 52 | regs.ir().write(|w| w.set_tefn(true)); | 52 | regs.ir().write(|w| w.set_tefn(true)); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | match &T::state().tx_mode { | 55 | T::info().state.lock(|s| { |
| 56 | TxMode::NonBuffered(waker) => waker.wake(), | 56 | let state = s.borrow_mut(); |
| 57 | TxMode::ClassicBuffered(buf) => { | 57 | match &state.tx_mode { |
| 58 | if !T::registers().tx_queue_is_full() { | 58 | TxMode::NonBuffered(waker) => waker.wake(), |
| 59 | match buf.tx_receiver.try_receive() { | 59 | TxMode::ClassicBuffered(buf) => { |
| 60 | Ok(frame) => { | 60 | if !T::registers().tx_queue_is_full() { |
| 61 | _ = T::registers().write(&frame); | 61 | match buf.tx_receiver.try_receive() { |
| 62 | Ok(frame) => { | ||
| 63 | _ = T::registers().write(&frame); | ||
| 64 | } | ||
| 65 | Err(_) => {} | ||
| 62 | } | 66 | } |
| 63 | Err(_) => {} | ||
| 64 | } | 67 | } |
| 65 | } | 68 | } |
| 66 | } | 69 | TxMode::FdBuffered(buf) => { |
| 67 | TxMode::FdBuffered(buf) => { | 70 | if !T::registers().tx_queue_is_full() { |
| 68 | if !T::registers().tx_queue_is_full() { | 71 | match buf.tx_receiver.try_receive() { |
| 69 | match buf.tx_receiver.try_receive() { | 72 | Ok(frame) => { |
| 70 | Ok(frame) => { | 73 | _ = T::registers().write(&frame); |
| 71 | _ = T::registers().write(&frame); | 74 | } |
| 75 | Err(_) => {} | ||
| 72 | } | 76 | } |
| 73 | Err(_) => {} | ||
| 74 | } | 77 | } |
| 75 | } | 78 | } |
| 76 | } | 79 | } |
| 77 | } | ||
| 78 | 80 | ||
| 79 | if ir.rfn(0) { | 81 | if ir.rfn(0) { |
| 80 | T::state().rx_mode.on_interrupt::<T>(0); | 82 | state.rx_mode.on_interrupt::<T>(0, state.ns_per_timer_tick); |
| 81 | } | 83 | } |
| 82 | if ir.rfn(1) { | 84 | if ir.rfn(1) { |
| 83 | T::state().rx_mode.on_interrupt::<T>(1); | 85 | state.rx_mode.on_interrupt::<T>(1, state.ns_per_timer_tick); |
| 84 | } | 86 | } |
| 87 | }); | ||
| 85 | 88 | ||
| 86 | if ir.bo() { | 89 | if ir.bo() { |
| 87 | regs.ir().write(|w| w.set_bo(true)); | 90 | regs.ir().write(|w| w.set_bo(true)); |
| @@ -165,7 +168,6 @@ pub struct CanConfigurator<'d> { | |||
| 165 | _phantom: PhantomData<&'d ()>, | 168 | _phantom: PhantomData<&'d ()>, |
| 166 | config: crate::can::fd::config::FdCanConfig, | 169 | config: crate::can::fd::config::FdCanConfig, |
| 167 | info: &'static Info, | 170 | info: &'static Info, |
| 168 | state: &'static State, | ||
| 169 | /// Reference to internals. | 171 | /// Reference to internals. |
| 170 | properties: Properties, | 172 | properties: Properties, |
| 171 | periph_clock: crate::time::Hertz, | 173 | periph_clock: crate::time::Hertz, |
| @@ -175,27 +177,30 @@ impl<'d> CanConfigurator<'d> { | |||
| 175 | /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. | 177 | /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. |
| 176 | /// You must call [Fdcan::enable_non_blocking] to use the peripheral. | 178 | /// You must call [Fdcan::enable_non_blocking] to use the peripheral. |
| 177 | pub fn new<T: Instance>( | 179 | pub fn new<T: Instance>( |
| 178 | _peri: impl Peripheral<P = T> + 'd, | 180 | _peri: Peri<'d, T>, |
| 179 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 181 | rx: Peri<'d, impl RxPin<T>>, |
| 180 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 182 | tx: Peri<'d, impl TxPin<T>>, |
| 181 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> | 183 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> |
| 182 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> | 184 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> |
| 183 | + 'd, | 185 | + 'd, |
| 184 | ) -> CanConfigurator<'d> { | 186 | ) -> CanConfigurator<'d> { |
| 185 | into_ref!(_peri, rx, tx); | ||
| 186 | |||
| 187 | rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); | 187 | rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); |
| 188 | tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 188 | tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 189 | 189 | ||
| 190 | rcc::enable_and_reset::<T>(); | 190 | rcc::enable_and_reset::<T>(); |
| 191 | 191 | ||
| 192 | let info = T::info(); | ||
| 193 | T::info().state.lock(|s| { | ||
| 194 | s.borrow_mut().tx_pin_port = Some(tx.pin_port()); | ||
| 195 | s.borrow_mut().rx_pin_port = Some(rx.pin_port()); | ||
| 196 | }); | ||
| 197 | (info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 198 | (info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 199 | |||
| 192 | let mut config = crate::can::fd::config::FdCanConfig::default(); | 200 | let mut config = crate::can::fd::config::FdCanConfig::default(); |
| 193 | config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); | 201 | config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); |
| 194 | T::registers().into_config_mode(config); | 202 | T::registers().into_config_mode(config); |
| 195 | 203 | ||
| 196 | rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); | ||
| 197 | tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 198 | |||
| 199 | unsafe { | 204 | unsafe { |
| 200 | T::IT0Interrupt::unpend(); // Not unsafe | 205 | T::IT0Interrupt::unpend(); // Not unsafe |
| 201 | T::IT0Interrupt::enable(); | 206 | T::IT0Interrupt::enable(); |
| @@ -206,8 +211,7 @@ impl<'d> CanConfigurator<'d> { | |||
| 206 | Self { | 211 | Self { |
| 207 | _phantom: PhantomData, | 212 | _phantom: PhantomData, |
| 208 | config, | 213 | config, |
| 209 | info: T::info(), | 214 | info, |
| 210 | state: T::state(), | ||
| 211 | properties: Properties::new(T::info()), | 215 | properties: Properties::new(T::info()), |
| 212 | periph_clock: T::frequency(), | 216 | periph_clock: T::frequency(), |
| 213 | } | 217 | } |
| @@ -259,19 +263,16 @@ impl<'d> CanConfigurator<'d> { | |||
| 259 | /// Start in mode. | 263 | /// Start in mode. |
| 260 | pub fn start(self, mode: OperatingMode) -> Can<'d> { | 264 | pub fn start(self, mode: OperatingMode) -> Can<'d> { |
| 261 | let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); | 265 | let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); |
| 262 | critical_section::with(|_| { | 266 | self.info.state.lock(|s| { |
| 263 | let state = self.state as *const State; | 267 | s.borrow_mut().ns_per_timer_tick = ns_per_timer_tick; |
| 264 | unsafe { | ||
| 265 | let mut_state = state as *mut State; | ||
| 266 | (*mut_state).ns_per_timer_tick = ns_per_timer_tick; | ||
| 267 | } | ||
| 268 | }); | 268 | }); |
| 269 | self.info.regs.into_mode(self.config, mode); | 269 | self.info.regs.into_mode(self.config, mode); |
| 270 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 271 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 270 | Can { | 272 | Can { |
| 271 | _phantom: PhantomData, | 273 | _phantom: PhantomData, |
| 272 | config: self.config, | 274 | config: self.config, |
| 273 | info: self.info, | 275 | info: self.info, |
| 274 | state: self.state, | ||
| 275 | _mode: mode, | 276 | _mode: mode, |
| 276 | properties: Properties::new(self.info), | 277 | properties: Properties::new(self.info), |
| 277 | } | 278 | } |
| @@ -293,12 +294,18 @@ impl<'d> CanConfigurator<'d> { | |||
| 293 | } | 294 | } |
| 294 | } | 295 | } |
| 295 | 296 | ||
| 297 | impl<'d> Drop for CanConfigurator<'d> { | ||
| 298 | fn drop(&mut self) { | ||
| 299 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 300 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 296 | /// FDCAN Instance | 304 | /// FDCAN Instance |
| 297 | pub struct Can<'d> { | 305 | pub struct Can<'d> { |
| 298 | _phantom: PhantomData<&'d ()>, | 306 | _phantom: PhantomData<&'d ()>, |
| 299 | config: crate::can::fd::config::FdCanConfig, | 307 | config: crate::can::fd::config::FdCanConfig, |
| 300 | info: &'static Info, | 308 | info: &'static Info, |
| 301 | state: &'static State, | ||
| 302 | _mode: OperatingMode, | 309 | _mode: OperatingMode, |
| 303 | properties: Properties, | 310 | properties: Properties, |
| 304 | } | 311 | } |
| @@ -312,7 +319,9 @@ impl<'d> Can<'d> { | |||
| 312 | /// Flush one of the TX mailboxes. | 319 | /// Flush one of the TX mailboxes. |
| 313 | pub async fn flush(&self, idx: usize) { | 320 | pub async fn flush(&self, idx: usize) { |
| 314 | poll_fn(|cx| { | 321 | poll_fn(|cx| { |
| 315 | self.state.tx_mode.register(cx.waker()); | 322 | self.info.state.lock(|s| { |
| 323 | s.borrow_mut().tx_mode.register(cx.waker()); | ||
| 324 | }); | ||
| 316 | 325 | ||
| 317 | if idx > 3 { | 326 | if idx > 3 { |
| 318 | panic!("Bad mailbox"); | 327 | panic!("Bad mailbox"); |
| @@ -332,12 +341,12 @@ impl<'d> Can<'d> { | |||
| 332 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 341 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 333 | /// transmitted, then tries again. | 342 | /// transmitted, then tries again. |
| 334 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { | 343 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { |
| 335 | self.state.tx_mode.write(self.info, frame).await | 344 | TxMode::write(self.info, frame).await |
| 336 | } | 345 | } |
| 337 | 346 | ||
| 338 | /// Returns the next received message frame | 347 | /// Returns the next received message frame |
| 339 | pub async fn read(&mut self) -> Result<Envelope, BusError> { | 348 | pub async fn read(&mut self) -> Result<Envelope, BusError> { |
| 340 | self.state.rx_mode.read_classic(self.info, self.state).await | 349 | RxMode::read_classic(self.info).await |
| 341 | } | 350 | } |
| 342 | 351 | ||
| 343 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 352 | /// Queues the message to be sent but exerts backpressure. If a lower-priority |
| @@ -345,40 +354,43 @@ impl<'d> Can<'d> { | |||
| 345 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 354 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 346 | /// transmitted, then tries again. | 355 | /// transmitted, then tries again. |
| 347 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { | 356 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { |
| 348 | self.state.tx_mode.write_fd(self.info, frame).await | 357 | TxMode::write_fd(self.info, frame).await |
| 349 | } | 358 | } |
| 350 | 359 | ||
| 351 | /// Returns the next received message frame | 360 | /// Returns the next received message frame |
| 352 | pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { | 361 | pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { |
| 353 | self.state.rx_mode.read_fd(self.info, self.state).await | 362 | RxMode::read_fd(self.info).await |
| 354 | } | 363 | } |
| 355 | 364 | ||
| 356 | /// Split instance into separate portions: Tx(write), Rx(read), common properties | 365 | /// Split instance into separate portions: Tx(write), Rx(read), common properties |
| 357 | pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { | 366 | pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { |
| 367 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 368 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 358 | ( | 369 | ( |
| 359 | CanTx { | 370 | CanTx { |
| 360 | _phantom: PhantomData, | 371 | _phantom: PhantomData, |
| 361 | info: self.info, | 372 | info: self.info, |
| 362 | state: self.state, | ||
| 363 | config: self.config, | 373 | config: self.config, |
| 364 | _mode: self._mode, | 374 | _mode: self._mode, |
| 365 | }, | 375 | }, |
| 366 | CanRx { | 376 | CanRx { |
| 367 | _phantom: PhantomData, | 377 | _phantom: PhantomData, |
| 368 | info: self.info, | 378 | info: self.info, |
| 369 | state: self.state, | ||
| 370 | _mode: self._mode, | 379 | _mode: self._mode, |
| 371 | }, | 380 | }, |
| 372 | self.properties, | 381 | Properties { |
| 382 | info: self.properties.info, | ||
| 383 | }, | ||
| 373 | ) | 384 | ) |
| 374 | } | 385 | } |
| 375 | /// Join split rx and tx portions back together | 386 | /// Join split rx and tx portions back together |
| 376 | pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { | 387 | pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { |
| 388 | (tx.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 389 | (tx.info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 377 | Can { | 390 | Can { |
| 378 | _phantom: PhantomData, | 391 | _phantom: PhantomData, |
| 379 | config: tx.config, | 392 | config: tx.config, |
| 380 | info: tx.info, | 393 | info: tx.info, |
| 381 | state: tx.state, | ||
| 382 | _mode: rx._mode, | 394 | _mode: rx._mode, |
| 383 | properties: Properties::new(tx.info), | 395 | properties: Properties::new(tx.info), |
| 384 | } | 396 | } |
| @@ -390,7 +402,7 @@ impl<'d> Can<'d> { | |||
| 390 | tx_buf: &'static mut TxBuf<TX_BUF_SIZE>, | 402 | tx_buf: &'static mut TxBuf<TX_BUF_SIZE>, |
| 391 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, | 403 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, |
| 392 | ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 404 | ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 393 | BufferedCan::new(self.info, self.state, self._mode, tx_buf, rxb) | 405 | BufferedCan::new(self.info, self._mode, tx_buf, rxb) |
| 394 | } | 406 | } |
| 395 | 407 | ||
| 396 | /// Return a buffered instance of driver with CAN FD support. User must supply Buffers | 408 | /// Return a buffered instance of driver with CAN FD support. User must supply Buffers |
| @@ -399,7 +411,14 @@ impl<'d> Can<'d> { | |||
| 399 | tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>, | 411 | tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>, |
| 400 | rxb: &'static mut RxFdBuf<RX_BUF_SIZE>, | 412 | rxb: &'static mut RxFdBuf<RX_BUF_SIZE>, |
| 401 | ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 413 | ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 402 | BufferedCanFd::new(self.info, self.state, self._mode, tx_buf, rxb) | 414 | BufferedCanFd::new(self.info, self._mode, tx_buf, rxb) |
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | impl<'d> Drop for Can<'d> { | ||
| 419 | fn drop(&mut self) { | ||
| 420 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 421 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 403 | } | 422 | } |
| 404 | } | 423 | } |
| 405 | 424 | ||
| @@ -413,7 +432,6 @@ pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, | |||
| 413 | pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | 432 | pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { |
| 414 | _phantom: PhantomData<&'d ()>, | 433 | _phantom: PhantomData<&'d ()>, |
| 415 | info: &'static Info, | 434 | info: &'static Info, |
| 416 | state: &'static State, | ||
| 417 | _mode: OperatingMode, | 435 | _mode: OperatingMode, |
| 418 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, | 436 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, |
| 419 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | 437 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| @@ -423,15 +441,15 @@ pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | |||
| 423 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 441 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 424 | fn new( | 442 | fn new( |
| 425 | info: &'static Info, | 443 | info: &'static Info, |
| 426 | state: &'static State, | ||
| 427 | _mode: OperatingMode, | 444 | _mode: OperatingMode, |
| 428 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, | 445 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, |
| 429 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | 446 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| 430 | ) -> Self { | 447 | ) -> Self { |
| 448 | (info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 449 | (info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 431 | BufferedCan { | 450 | BufferedCan { |
| 432 | _phantom: PhantomData, | 451 | _phantom: PhantomData, |
| 433 | info, | 452 | info, |
| 434 | state, | ||
| 435 | _mode, | 453 | _mode, |
| 436 | tx_buf, | 454 | tx_buf, |
| 437 | rx_buf, | 455 | rx_buf, |
| @@ -447,19 +465,15 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, | |||
| 447 | 465 | ||
| 448 | fn setup(self) -> Self { | 466 | fn setup(self) -> Self { |
| 449 | // We don't want interrupts being processed while we change modes. | 467 | // We don't want interrupts being processed while we change modes. |
| 450 | critical_section::with(|_| { | 468 | self.info.state.lock(|s| { |
| 451 | let rx_inner = super::common::ClassicBufferedRxInner { | 469 | let rx_inner = super::common::ClassicBufferedRxInner { |
| 452 | rx_sender: self.rx_buf.sender().into(), | 470 | rx_sender: self.rx_buf.sender().into(), |
| 453 | }; | 471 | }; |
| 454 | let tx_inner = super::common::ClassicBufferedTxInner { | 472 | let tx_inner = super::common::ClassicBufferedTxInner { |
| 455 | tx_receiver: self.tx_buf.receiver().into(), | 473 | tx_receiver: self.tx_buf.receiver().into(), |
| 456 | }; | 474 | }; |
| 457 | let state = self.state as *const State; | 475 | s.borrow_mut().rx_mode = RxMode::ClassicBuffered(rx_inner); |
| 458 | unsafe { | 476 | s.borrow_mut().tx_mode = TxMode::ClassicBuffered(tx_inner); |
| 459 | let mut_state = state as *mut State; | ||
| 460 | (*mut_state).rx_mode = RxMode::ClassicBuffered(rx_inner); | ||
| 461 | (*mut_state).tx_mode = TxMode::ClassicBuffered(tx_inner); | ||
| 462 | } | ||
| 463 | }); | 477 | }); |
| 464 | self | 478 | self |
| 465 | } | 479 | } |
| @@ -478,28 +492,28 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, | |||
| 478 | 492 | ||
| 479 | /// Returns a sender that can be used for sending CAN frames. | 493 | /// Returns a sender that can be used for sending CAN frames. |
| 480 | pub fn writer(&self) -> BufferedCanSender { | 494 | pub fn writer(&self) -> BufferedCanSender { |
| 495 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 481 | BufferedCanSender { | 496 | BufferedCanSender { |
| 482 | tx_buf: self.tx_buf.sender().into(), | 497 | tx_buf: self.tx_buf.sender().into(), |
| 483 | waker: self.info.tx_waker, | 498 | waker: self.info.tx_waker, |
| 499 | internal_operation: self.info.internal_operation, | ||
| 484 | } | 500 | } |
| 485 | } | 501 | } |
| 486 | 502 | ||
| 487 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 503 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 488 | pub fn reader(&self) -> BufferedCanReceiver { | 504 | pub fn reader(&self) -> BufferedCanReceiver { |
| 489 | self.rx_buf.receiver().into() | 505 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); |
| 506 | BufferedCanReceiver { | ||
| 507 | rx_buf: self.rx_buf.receiver().into(), | ||
| 508 | internal_operation: self.info.internal_operation, | ||
| 509 | } | ||
| 490 | } | 510 | } |
| 491 | } | 511 | } |
| 492 | 512 | ||
| 493 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 513 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 494 | fn drop(&mut self) { | 514 | fn drop(&mut self) { |
| 495 | critical_section::with(|_| { | 515 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); |
| 496 | let state = self.state as *const State; | 516 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); |
| 497 | unsafe { | ||
| 498 | let mut_state = state as *mut State; | ||
| 499 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 500 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 501 | } | ||
| 502 | }); | ||
| 503 | } | 517 | } |
| 504 | } | 518 | } |
| 505 | 519 | ||
| @@ -509,41 +523,16 @@ pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Resul | |||
| 509 | /// User supplied buffer for TX buffering | 523 | /// User supplied buffer for TX buffering |
| 510 | pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>; | 524 | pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>; |
| 511 | 525 | ||
| 512 | /// Sender that can be used for sending CAN frames. | 526 | /// Sender that can be used for sending Classic CAN frames. |
| 513 | #[derive(Copy, Clone)] | 527 | pub type BufferedFdCanSender = super::common::BufferedSender<'static, FdFrame>; |
| 514 | pub struct BufferedFdCanSender { | ||
| 515 | tx_buf: DynamicSender<'static, FdFrame>, | ||
| 516 | waker: fn(), | ||
| 517 | } | ||
| 518 | |||
| 519 | impl BufferedFdCanSender { | ||
| 520 | /// Async write frame to TX buffer. | ||
| 521 | pub fn try_write(&mut self, frame: FdFrame) -> Result<(), embassy_sync::channel::TrySendError<FdFrame>> { | ||
| 522 | self.tx_buf.try_send(frame)?; | ||
| 523 | (self.waker)(); | ||
| 524 | Ok(()) | ||
| 525 | } | ||
| 526 | |||
| 527 | /// Async write frame to TX buffer. | ||
| 528 | pub async fn write(&mut self, frame: FdFrame) { | ||
| 529 | self.tx_buf.send(frame).await; | ||
| 530 | (self.waker)(); | ||
| 531 | } | ||
| 532 | |||
| 533 | /// Allows a poll_fn to poll until the channel is ready to write | ||
| 534 | pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> { | ||
| 535 | self.tx_buf.poll_ready_to_send(cx) | ||
| 536 | } | ||
| 537 | } | ||
| 538 | 528 | ||
| 539 | /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 529 | /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 540 | pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<FdEnvelope, BusError>>; | 530 | pub type BufferedFdCanReceiver = super::common::BufferedReceiver<'static, FdEnvelope>; |
| 541 | 531 | ||
| 542 | /// Buffered FDCAN Instance | 532 | /// Buffered FDCAN Instance |
| 543 | pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | 533 | pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { |
| 544 | _phantom: PhantomData<&'d ()>, | 534 | _phantom: PhantomData<&'d ()>, |
| 545 | info: &'static Info, | 535 | info: &'static Info, |
| 546 | state: &'static State, | ||
| 547 | _mode: OperatingMode, | 536 | _mode: OperatingMode, |
| 548 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, | 537 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, |
| 549 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, | 538 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, |
| @@ -553,15 +542,15 @@ pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> | |||
| 553 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 542 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 554 | fn new( | 543 | fn new( |
| 555 | info: &'static Info, | 544 | info: &'static Info, |
| 556 | state: &'static State, | ||
| 557 | _mode: OperatingMode, | 545 | _mode: OperatingMode, |
| 558 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, | 546 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, |
| 559 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, | 547 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, |
| 560 | ) -> Self { | 548 | ) -> Self { |
| 549 | (info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 550 | (info.internal_operation)(InternalOperation::NotifyReceiverCreated); | ||
| 561 | BufferedCanFd { | 551 | BufferedCanFd { |
| 562 | _phantom: PhantomData, | 552 | _phantom: PhantomData, |
| 563 | info, | 553 | info, |
| 564 | state, | ||
| 565 | _mode, | 554 | _mode, |
| 566 | tx_buf, | 555 | tx_buf, |
| 567 | rx_buf, | 556 | rx_buf, |
| @@ -577,19 +566,15 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' | |||
| 577 | 566 | ||
| 578 | fn setup(self) -> Self { | 567 | fn setup(self) -> Self { |
| 579 | // We don't want interrupts being processed while we change modes. | 568 | // We don't want interrupts being processed while we change modes. |
| 580 | critical_section::with(|_| { | 569 | self.info.state.lock(|s| { |
| 581 | let rx_inner = super::common::FdBufferedRxInner { | 570 | let rx_inner = super::common::FdBufferedRxInner { |
| 582 | rx_sender: self.rx_buf.sender().into(), | 571 | rx_sender: self.rx_buf.sender().into(), |
| 583 | }; | 572 | }; |
| 584 | let tx_inner = super::common::FdBufferedTxInner { | 573 | let tx_inner = super::common::FdBufferedTxInner { |
| 585 | tx_receiver: self.tx_buf.receiver().into(), | 574 | tx_receiver: self.tx_buf.receiver().into(), |
| 586 | }; | 575 | }; |
| 587 | let state = self.state as *const State; | 576 | s.borrow_mut().rx_mode = RxMode::FdBuffered(rx_inner); |
| 588 | unsafe { | 577 | s.borrow_mut().tx_mode = TxMode::FdBuffered(tx_inner); |
| 589 | let mut_state = state as *mut State; | ||
| 590 | (*mut_state).rx_mode = RxMode::FdBuffered(rx_inner); | ||
| 591 | (*mut_state).tx_mode = TxMode::FdBuffered(tx_inner); | ||
| 592 | } | ||
| 593 | }); | 578 | }); |
| 594 | self | 579 | self |
| 595 | } | 580 | } |
| @@ -608,28 +593,28 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' | |||
| 608 | 593 | ||
| 609 | /// Returns a sender that can be used for sending CAN frames. | 594 | /// Returns a sender that can be used for sending CAN frames. |
| 610 | pub fn writer(&self) -> BufferedFdCanSender { | 595 | pub fn writer(&self) -> BufferedFdCanSender { |
| 596 | (self.info.internal_operation)(InternalOperation::NotifySenderCreated); | ||
| 611 | BufferedFdCanSender { | 597 | BufferedFdCanSender { |
| 612 | tx_buf: self.tx_buf.sender().into(), | 598 | tx_buf: self.tx_buf.sender().into(), |
| 613 | waker: self.info.tx_waker, | 599 | waker: self.info.tx_waker, |
| 600 | internal_operation: self.info.internal_operation, | ||
| 614 | } | 601 | } |
| 615 | } | 602 | } |
| 616 | 603 | ||
| 617 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. | 604 | /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. |
| 618 | pub fn reader(&self) -> BufferedFdCanReceiver { | 605 | pub fn reader(&self) -> BufferedFdCanReceiver { |
| 619 | self.rx_buf.receiver().into() | 606 | (self.info.internal_operation)(InternalOperation::NotifyReceiverCreated); |
| 607 | BufferedFdCanReceiver { | ||
| 608 | rx_buf: self.rx_buf.receiver().into(), | ||
| 609 | internal_operation: self.info.internal_operation, | ||
| 610 | } | ||
| 620 | } | 611 | } |
| 621 | } | 612 | } |
| 622 | 613 | ||
| 623 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { | 614 | impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { |
| 624 | fn drop(&mut self) { | 615 | fn drop(&mut self) { |
| 625 | critical_section::with(|_| { | 616 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); |
| 626 | let state = self.state as *const State; | 617 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); |
| 627 | unsafe { | ||
| 628 | let mut_state = state as *mut State; | ||
| 629 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 630 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 631 | } | ||
| 632 | }); | ||
| 633 | } | 618 | } |
| 634 | } | 619 | } |
| 635 | 620 | ||
| @@ -637,19 +622,24 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for Buffer | |||
| 637 | pub struct CanRx<'d> { | 622 | pub struct CanRx<'d> { |
| 638 | _phantom: PhantomData<&'d ()>, | 623 | _phantom: PhantomData<&'d ()>, |
| 639 | info: &'static Info, | 624 | info: &'static Info, |
| 640 | state: &'static State, | ||
| 641 | _mode: OperatingMode, | 625 | _mode: OperatingMode, |
| 642 | } | 626 | } |
| 643 | 627 | ||
| 644 | impl<'d> CanRx<'d> { | 628 | impl<'d> CanRx<'d> { |
| 645 | /// Returns the next received message frame | 629 | /// Returns the next received message frame |
| 646 | pub async fn read(&mut self) -> Result<Envelope, BusError> { | 630 | pub async fn read(&mut self) -> Result<Envelope, BusError> { |
| 647 | self.state.rx_mode.read_classic(&self.info, &self.state).await | 631 | RxMode::read_classic(&self.info).await |
| 648 | } | 632 | } |
| 649 | 633 | ||
| 650 | /// Returns the next received message frame | 634 | /// Returns the next received message frame |
| 651 | pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { | 635 | pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { |
| 652 | self.state.rx_mode.read_fd(&self.info, &self.state).await | 636 | RxMode::read_fd(&self.info).await |
| 637 | } | ||
| 638 | } | ||
| 639 | |||
| 640 | impl<'d> Drop for CanRx<'d> { | ||
| 641 | fn drop(&mut self) { | ||
| 642 | (self.info.internal_operation)(InternalOperation::NotifyReceiverDestroyed); | ||
| 653 | } | 643 | } |
| 654 | } | 644 | } |
| 655 | 645 | ||
| @@ -657,7 +647,6 @@ impl<'d> CanRx<'d> { | |||
| 657 | pub struct CanTx<'d> { | 647 | pub struct CanTx<'d> { |
| 658 | _phantom: PhantomData<&'d ()>, | 648 | _phantom: PhantomData<&'d ()>, |
| 659 | info: &'static Info, | 649 | info: &'static Info, |
| 660 | state: &'static State, | ||
| 661 | config: crate::can::fd::config::FdCanConfig, | 650 | config: crate::can::fd::config::FdCanConfig, |
| 662 | _mode: OperatingMode, | 651 | _mode: OperatingMode, |
| 663 | } | 652 | } |
| @@ -668,7 +657,7 @@ impl<'c, 'd> CanTx<'d> { | |||
| 668 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 657 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 669 | /// transmitted, then tries again. | 658 | /// transmitted, then tries again. |
| 670 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { | 659 | pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { |
| 671 | self.state.tx_mode.write(self.info, frame).await | 660 | TxMode::write(self.info, frame).await |
| 672 | } | 661 | } |
| 673 | 662 | ||
| 674 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 663 | /// Queues the message to be sent but exerts backpressure. If a lower-priority |
| @@ -676,7 +665,13 @@ impl<'c, 'd> CanTx<'d> { | |||
| 676 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 665 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 677 | /// transmitted, then tries again. | 666 | /// transmitted, then tries again. |
| 678 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { | 667 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { |
| 679 | self.state.tx_mode.write_fd(self.info, frame).await | 668 | TxMode::write_fd(self.info, frame).await |
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 672 | impl<'d> Drop for CanTx<'d> { | ||
| 673 | fn drop(&mut self) { | ||
| 674 | (self.info.internal_operation)(InternalOperation::NotifySenderDestroyed); | ||
| 680 | } | 675 | } |
| 681 | } | 676 | } |
| 682 | 677 | ||
| @@ -696,19 +691,19 @@ impl RxMode { | |||
| 696 | } | 691 | } |
| 697 | } | 692 | } |
| 698 | 693 | ||
| 699 | fn on_interrupt<T: Instance>(&self, fifonr: usize) { | 694 | fn on_interrupt<T: Instance>(&self, fifonr: usize, ns_per_timer_tick: u64) { |
| 700 | T::registers().regs.ir().write(|w| w.set_rfn(fifonr, true)); | 695 | T::registers().regs.ir().write(|w| w.set_rfn(fifonr, true)); |
| 701 | match self { | 696 | match self { |
| 702 | RxMode::NonBuffered(waker) => { | 697 | RxMode::NonBuffered(waker) => { |
| 703 | waker.wake(); | 698 | waker.wake(); |
| 704 | } | 699 | } |
| 705 | RxMode::ClassicBuffered(buf) => { | 700 | RxMode::ClassicBuffered(buf) => { |
| 706 | if let Some(result) = self.try_read::<T>() { | 701 | if let Some(result) = self.try_read::<T>(ns_per_timer_tick) { |
| 707 | let _ = buf.rx_sender.try_send(result); | 702 | let _ = buf.rx_sender.try_send(result); |
| 708 | } | 703 | } |
| 709 | } | 704 | } |
| 710 | RxMode::FdBuffered(buf) => { | 705 | RxMode::FdBuffered(buf) => { |
| 711 | if let Some(result) = self.try_read_fd::<T>() { | 706 | if let Some(result) = self.try_read_fd::<T>(ns_per_timer_tick) { |
| 712 | let _ = buf.rx_sender.try_send(result); | 707 | let _ = buf.rx_sender.try_send(result); |
| 713 | } | 708 | } |
| 714 | } | 709 | } |
| @@ -716,12 +711,12 @@ impl RxMode { | |||
| 716 | } | 711 | } |
| 717 | 712 | ||
| 718 | //async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> { | 713 | //async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> { |
| 719 | fn try_read<T: Instance>(&self) -> Option<Result<Envelope, BusError>> { | 714 | fn try_read<T: Instance>(&self, ns_per_timer_tick: u64) -> Option<Result<Envelope, BusError>> { |
| 720 | if let Some((frame, ts)) = T::registers().read(0) { | 715 | if let Some((frame, ts)) = T::registers().read(0) { |
| 721 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | 716 | let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts); |
| 722 | Some(Ok(Envelope { ts, frame })) | 717 | Some(Ok(Envelope { ts, frame })) |
| 723 | } else if let Some((frame, ts)) = T::registers().read(1) { | 718 | } else if let Some((frame, ts)) = T::registers().read(1) { |
| 724 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | 719 | let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts); |
| 725 | Some(Ok(Envelope { ts, frame })) | 720 | Some(Ok(Envelope { ts, frame })) |
| 726 | } else if let Some(err) = T::registers().curr_error() { | 721 | } else if let Some(err) = T::registers().curr_error() { |
| 727 | // TODO: this is probably wrong | 722 | // TODO: this is probably wrong |
| @@ -731,12 +726,12 @@ impl RxMode { | |||
| 731 | } | 726 | } |
| 732 | } | 727 | } |
| 733 | 728 | ||
| 734 | fn try_read_fd<T: Instance>(&self) -> Option<Result<FdEnvelope, BusError>> { | 729 | fn try_read_fd<T: Instance>(&self, ns_per_timer_tick: u64) -> Option<Result<FdEnvelope, BusError>> { |
| 735 | if let Some((frame, ts)) = T::registers().read(0) { | 730 | if let Some((frame, ts)) = T::registers().read(0) { |
| 736 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | 731 | let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts); |
| 737 | Some(Ok(FdEnvelope { ts, frame })) | 732 | Some(Ok(FdEnvelope { ts, frame })) |
| 738 | } else if let Some((frame, ts)) = T::registers().read(1) { | 733 | } else if let Some((frame, ts)) = T::registers().read(1) { |
| 739 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | 734 | let ts = T::registers().calc_timestamp(ns_per_timer_tick, ts); |
| 740 | Some(Ok(FdEnvelope { ts, frame })) | 735 | Some(Ok(FdEnvelope { ts, frame })) |
| 741 | } else if let Some(err) = T::registers().curr_error() { | 736 | } else if let Some(err) = T::registers().curr_error() { |
| 742 | // TODO: this is probably wrong | 737 | // TODO: this is probably wrong |
| @@ -746,16 +741,12 @@ impl RxMode { | |||
| 746 | } | 741 | } |
| 747 | } | 742 | } |
| 748 | 743 | ||
| 749 | fn read<F: CanHeader>( | 744 | fn read<F: CanHeader>(info: &'static Info, ns_per_timer_tick: u64) -> Option<Result<(F, Timestamp), BusError>> { |
| 750 | &self, | ||
| 751 | info: &'static Info, | ||
| 752 | state: &'static State, | ||
| 753 | ) -> Option<Result<(F, Timestamp), BusError>> { | ||
| 754 | if let Some((msg, ts)) = info.regs.read(0) { | 745 | if let Some((msg, ts)) = info.regs.read(0) { |
| 755 | let ts = info.calc_timestamp(state.ns_per_timer_tick, ts); | 746 | let ts = info.regs.calc_timestamp(ns_per_timer_tick, ts); |
| 756 | Some(Ok((msg, ts))) | 747 | Some(Ok((msg, ts))) |
| 757 | } else if let Some((msg, ts)) = info.regs.read(1) { | 748 | } else if let Some((msg, ts)) = info.regs.read(1) { |
| 758 | let ts = info.calc_timestamp(state.ns_per_timer_tick, ts); | 749 | let ts = info.regs.calc_timestamp(ns_per_timer_tick, ts); |
| 759 | Some(Ok((msg, ts))) | 750 | Some(Ok((msg, ts))) |
| 760 | } else if let Some(err) = info.regs.curr_error() { | 751 | } else if let Some(err) = info.regs.curr_error() { |
| 761 | // TODO: this is probably wrong | 752 | // TODO: this is probably wrong |
| @@ -765,16 +756,15 @@ impl RxMode { | |||
| 765 | } | 756 | } |
| 766 | } | 757 | } |
| 767 | 758 | ||
| 768 | async fn read_async<F: CanHeader>( | 759 | async fn read_async<F: CanHeader>(info: &'static Info) -> Result<(F, Timestamp), BusError> { |
| 769 | &self, | ||
| 770 | info: &'static Info, | ||
| 771 | state: &'static State, | ||
| 772 | ) -> Result<(F, Timestamp), BusError> { | ||
| 773 | //let _ = self.read::<F>(info, state); | ||
| 774 | poll_fn(move |cx| { | 760 | poll_fn(move |cx| { |
| 775 | state.err_waker.register(cx.waker()); | 761 | let ns_per_timer_tick = info.state.lock(|s| { |
| 776 | self.register(cx.waker()); | 762 | let state = s.borrow_mut(); |
| 777 | match self.read::<_>(info, state) { | 763 | state.err_waker.register(cx.waker()); |
| 764 | state.rx_mode.register(cx.waker()); | ||
| 765 | state.ns_per_timer_tick | ||
| 766 | }); | ||
| 767 | match RxMode::read::<_>(info, ns_per_timer_tick) { | ||
| 778 | Some(result) => Poll::Ready(result), | 768 | Some(result) => Poll::Ready(result), |
| 779 | None => Poll::Pending, | 769 | None => Poll::Pending, |
| 780 | } | 770 | } |
| @@ -782,15 +772,15 @@ impl RxMode { | |||
| 782 | .await | 772 | .await |
| 783 | } | 773 | } |
| 784 | 774 | ||
| 785 | async fn read_classic(&self, info: &'static Info, state: &'static State) -> Result<Envelope, BusError> { | 775 | async fn read_classic(info: &'static Info) -> Result<Envelope, BusError> { |
| 786 | match self.read_async::<_>(info, state).await { | 776 | match RxMode::read_async::<_>(info).await { |
| 787 | Ok((frame, ts)) => Ok(Envelope { ts, frame }), | 777 | Ok((frame, ts)) => Ok(Envelope { ts, frame }), |
| 788 | Err(e) => Err(e), | 778 | Err(e) => Err(e), |
| 789 | } | 779 | } |
| 790 | } | 780 | } |
| 791 | 781 | ||
| 792 | async fn read_fd(&self, info: &'static Info, state: &'static State) -> Result<FdEnvelope, BusError> { | 782 | async fn read_fd(info: &'static Info) -> Result<FdEnvelope, BusError> { |
| 793 | match self.read_async::<_>(info, state).await { | 783 | match RxMode::read_async::<_>(info).await { |
| 794 | Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }), | 784 | Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }), |
| 795 | Err(e) => Err(e), | 785 | Err(e) => Err(e), |
| 796 | } | 786 | } |
| @@ -819,9 +809,11 @@ impl TxMode { | |||
| 819 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | 809 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames |
| 820 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 810 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 821 | /// transmitted, then tries again. | 811 | /// transmitted, then tries again. |
| 822 | async fn write_generic<F: embedded_can::Frame + CanHeader>(&self, info: &'static Info, frame: &F) -> Option<F> { | 812 | async fn write_generic<F: embedded_can::Frame + CanHeader>(info: &'static Info, frame: &F) -> Option<F> { |
| 823 | poll_fn(|cx| { | 813 | poll_fn(|cx| { |
| 824 | self.register(cx.waker()); | 814 | info.state.lock(|s| { |
| 815 | s.borrow_mut().tx_mode.register(cx.waker()); | ||
| 816 | }); | ||
| 825 | 817 | ||
| 826 | if let Ok(dropped) = info.regs.write(frame) { | 818 | if let Ok(dropped) = info.regs.write(frame) { |
| 827 | return Poll::Ready(dropped); | 819 | return Poll::Ready(dropped); |
| @@ -838,16 +830,16 @@ impl TxMode { | |||
| 838 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | 830 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames |
| 839 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 831 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 840 | /// transmitted, then tries again. | 832 | /// transmitted, then tries again. |
| 841 | async fn write(&self, info: &'static Info, frame: &Frame) -> Option<Frame> { | 833 | async fn write(info: &'static Info, frame: &Frame) -> Option<Frame> { |
| 842 | self.write_generic::<_>(info, frame).await | 834 | TxMode::write_generic::<_>(info, frame).await |
| 843 | } | 835 | } |
| 844 | 836 | ||
| 845 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 837 | /// Queues the message to be sent but exerts backpressure. If a lower-priority |
| 846 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | 838 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames |
| 847 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 839 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 848 | /// transmitted, then tries again. | 840 | /// transmitted, then tries again. |
| 849 | async fn write_fd(&self, info: &'static Info, frame: &FdFrame) -> Option<FdFrame> { | 841 | async fn write_fd(info: &'static Info, frame: &FdFrame) -> Option<FdFrame> { |
| 850 | self.write_generic::<_>(info, frame).await | 842 | TxMode::write_generic::<_>(info, frame).await |
| 851 | } | 843 | } |
| 852 | } | 844 | } |
| 853 | 845 | ||
| @@ -922,6 +914,10 @@ struct State { | |||
| 922 | pub rx_mode: RxMode, | 914 | pub rx_mode: RxMode, |
| 923 | pub tx_mode: TxMode, | 915 | pub tx_mode: TxMode, |
| 924 | pub ns_per_timer_tick: u64, | 916 | pub ns_per_timer_tick: u64, |
| 917 | receiver_instance_count: usize, | ||
| 918 | sender_instance_count: usize, | ||
| 919 | tx_pin_port: Option<u8>, | ||
| 920 | rx_pin_port: Option<u8>, | ||
| 925 | 921 | ||
| 926 | pub err_waker: AtomicWaker, | 922 | pub err_waker: AtomicWaker, |
| 927 | } | 923 | } |
| @@ -933,34 +929,22 @@ impl State { | |||
| 933 | tx_mode: TxMode::NonBuffered(AtomicWaker::new()), | 929 | tx_mode: TxMode::NonBuffered(AtomicWaker::new()), |
| 934 | ns_per_timer_tick: 0, | 930 | ns_per_timer_tick: 0, |
| 935 | err_waker: AtomicWaker::new(), | 931 | err_waker: AtomicWaker::new(), |
| 932 | receiver_instance_count: 0, | ||
| 933 | sender_instance_count: 0, | ||
| 934 | tx_pin_port: None, | ||
| 935 | rx_pin_port: None, | ||
| 936 | } | 936 | } |
| 937 | } | 937 | } |
| 938 | } | 938 | } |
| 939 | 939 | ||
| 940 | type SharedState = embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, core::cell::RefCell<State>>; | ||
| 940 | struct Info { | 941 | struct Info { |
| 941 | regs: Registers, | 942 | regs: Registers, |
| 942 | interrupt0: crate::interrupt::Interrupt, | 943 | interrupt0: crate::interrupt::Interrupt, |
| 943 | _interrupt1: crate::interrupt::Interrupt, | 944 | _interrupt1: crate::interrupt::Interrupt, |
| 944 | tx_waker: fn(), | 945 | tx_waker: fn(), |
| 945 | } | 946 | internal_operation: fn(InternalOperation), |
| 946 | 947 | state: SharedState, | |
| 947 | impl Info { | ||
| 948 | #[cfg(feature = "time")] | ||
| 949 | fn calc_timestamp(&self, ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 950 | let now_embassy = embassy_time::Instant::now(); | ||
| 951 | if ns_per_timer_tick == 0 { | ||
| 952 | return now_embassy; | ||
| 953 | } | ||
| 954 | let cantime = { self.regs.regs.tscv().read().tsc() }; | ||
| 955 | let delta = cantime.overflowing_sub(ts_val).0 as u64; | ||
| 956 | let ns = ns_per_timer_tick * delta as u64; | ||
| 957 | now_embassy - embassy_time::Duration::from_nanos(ns) | ||
| 958 | } | ||
| 959 | |||
| 960 | #[cfg(not(feature = "time"))] | ||
| 961 | fn calc_timestamp(&self, _ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 962 | ts_val | ||
| 963 | } | ||
| 964 | } | 948 | } |
| 965 | 949 | ||
| 966 | trait SealedInstance { | 950 | trait SealedInstance { |
| @@ -968,14 +952,12 @@ trait SealedInstance { | |||
| 968 | 952 | ||
| 969 | fn info() -> &'static Info; | 953 | fn info() -> &'static Info; |
| 970 | fn registers() -> crate::can::fd::peripheral::Registers; | 954 | fn registers() -> crate::can::fd::peripheral::Registers; |
| 971 | fn state() -> &'static State; | 955 | fn internal_operation(val: InternalOperation); |
| 972 | unsafe fn mut_state() -> &'static mut State; | ||
| 973 | fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp; | ||
| 974 | } | 956 | } |
| 975 | 957 | ||
| 976 | /// Instance trait | 958 | /// Instance trait |
| 977 | #[allow(private_bounds)] | 959 | #[allow(private_bounds)] |
| 978 | pub trait Instance: SealedInstance + RccPeripheral + 'static { | 960 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static { |
| 979 | /// Interrupt 0 | 961 | /// Interrupt 0 |
| 980 | type IT0Interrupt: crate::interrupt::typelevel::Interrupt; | 962 | type IT0Interrupt: crate::interrupt::typelevel::Interrupt; |
| 981 | /// Interrupt 1 | 963 | /// Interrupt 1 |
| @@ -983,7 +965,7 @@ pub trait Instance: SealedInstance + RccPeripheral + 'static { | |||
| 983 | } | 965 | } |
| 984 | 966 | ||
| 985 | /// Fdcan Instance struct | 967 | /// Fdcan Instance struct |
| 986 | pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); | 968 | pub struct FdcanInstance<'a, T: Instance>(Peri<'a, T>); |
| 987 | 969 | ||
| 988 | macro_rules! impl_fdcan { | 970 | macro_rules! impl_fdcan { |
| 989 | ($inst:ident, | 971 | ($inst:ident, |
| @@ -992,42 +974,56 @@ macro_rules! impl_fdcan { | |||
| 992 | impl SealedInstance for peripherals::$inst { | 974 | impl SealedInstance for peripherals::$inst { |
| 993 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; | 975 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; |
| 994 | 976 | ||
| 977 | fn internal_operation(val: InternalOperation) { | ||
| 978 | peripherals::$inst::info().state.lock(|s| { | ||
| 979 | let mut mut_state = s.borrow_mut(); | ||
| 980 | match val { | ||
| 981 | InternalOperation::NotifySenderCreated => { | ||
| 982 | mut_state.sender_instance_count += 1; | ||
| 983 | } | ||
| 984 | InternalOperation::NotifySenderDestroyed => { | ||
| 985 | mut_state.sender_instance_count -= 1; | ||
| 986 | if ( 0 == mut_state.sender_instance_count) { | ||
| 987 | (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 988 | } | ||
| 989 | } | ||
| 990 | InternalOperation::NotifyReceiverCreated => { | ||
| 991 | mut_state.receiver_instance_count += 1; | ||
| 992 | } | ||
| 993 | InternalOperation::NotifyReceiverDestroyed => { | ||
| 994 | mut_state.receiver_instance_count -= 1; | ||
| 995 | if ( 0 == mut_state.receiver_instance_count) { | ||
| 996 | (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 997 | } | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | if mut_state.sender_instance_count == 0 && mut_state.receiver_instance_count == 0 { | ||
| 1001 | unsafe { | ||
| 1002 | let tx_pin = crate::gpio::AnyPin::steal(mut_state.tx_pin_port.unwrap()); | ||
| 1003 | tx_pin.set_as_disconnected(); | ||
| 1004 | let rx_pin = crate::gpio::AnyPin::steal(mut_state.rx_pin_port.unwrap()); | ||
| 1005 | rx_pin.set_as_disconnected(); | ||
| 1006 | rcc::disable::<peripherals::$inst>(); | ||
| 1007 | } | ||
| 1008 | } | ||
| 1009 | }); | ||
| 1010 | } | ||
| 1011 | |||
| 995 | fn info() -> &'static Info { | 1012 | fn info() -> &'static Info { |
| 1013 | |||
| 996 | static INFO: Info = Info { | 1014 | static INFO: Info = Info { |
| 997 | regs: Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: $msg_ram_offset}, | 1015 | regs: Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: $msg_ram_offset}, |
| 998 | interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, | 1016 | interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, |
| 999 | _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, | 1017 | _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, |
| 1000 | tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, | 1018 | tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, |
| 1019 | internal_operation: peripherals::$inst::internal_operation, | ||
| 1020 | state: embassy_sync::blocking_mutex::Mutex::new(core::cell::RefCell::new(State::new())), | ||
| 1001 | }; | 1021 | }; |
| 1002 | &INFO | 1022 | &INFO |
| 1003 | } | 1023 | } |
| 1004 | fn registers() -> Registers { | 1024 | fn registers() -> Registers { |
| 1005 | Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET} | 1025 | Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET} |
| 1006 | } | 1026 | } |
| 1007 | unsafe fn mut_state() -> &'static mut State { | ||
| 1008 | static mut STATE: State = State::new(); | ||
| 1009 | &mut *core::ptr::addr_of_mut!(STATE) | ||
| 1010 | } | ||
| 1011 | fn state() -> &'static State { | ||
| 1012 | unsafe { peripherals::$inst::mut_state() } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | #[cfg(feature = "time")] | ||
| 1016 | fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 1017 | let now_embassy = embassy_time::Instant::now(); | ||
| 1018 | if ns_per_timer_tick == 0 { | ||
| 1019 | return now_embassy; | ||
| 1020 | } | ||
| 1021 | let cantime = { Self::registers().regs.tscv().read().tsc() }; | ||
| 1022 | let delta = cantime.overflowing_sub(ts_val).0 as u64; | ||
| 1023 | let ns = ns_per_timer_tick * delta as u64; | ||
| 1024 | now_embassy - embassy_time::Duration::from_nanos(ns) | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | #[cfg(not(feature = "time"))] | ||
| 1028 | fn calc_timestamp(_ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 1029 | ts_val | ||
| 1030 | } | ||
| 1031 | 1027 | ||
| 1032 | } | 1028 | } |
| 1033 | 1029 | ||
diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index d2d1f7aa6..0fbab053b 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs | |||
| @@ -104,15 +104,13 @@ pub trait CanHeader: Sized { | |||
| 104 | #[derive(Debug, Copy, Clone)] | 104 | #[derive(Debug, Copy, Clone)] |
| 105 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 105 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 106 | pub struct ClassicData { | 106 | pub struct ClassicData { |
| 107 | pub(crate) bytes: [u8; Self::MAX_DATA_LEN], | 107 | pub(crate) bytes: [u8; 8], |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | impl ClassicData { | 110 | impl ClassicData { |
| 111 | pub(crate) const MAX_DATA_LEN: usize = 8; | ||
| 112 | /// Creates a data payload from a raw byte slice. | 111 | /// Creates a data payload from a raw byte slice. |
| 113 | /// | 112 | /// |
| 114 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or | 113 | /// Returns `FrameCreateError` if `data` is more than 8 bytes (which is the maximum). |
| 115 | /// cannot be represented with an FDCAN DLC. | ||
| 116 | pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> { | 114 | pub fn new(data: &[u8]) -> Result<Self, FrameCreateError> { |
| 117 | if data.len() > 8 { | 115 | if data.len() > 8 { |
| 118 | return Err(FrameCreateError::InvalidDataLength); | 116 | return Err(FrameCreateError::InvalidDataLength); |
| @@ -129,6 +127,11 @@ impl ClassicData { | |||
| 129 | &self.bytes | 127 | &self.bytes |
| 130 | } | 128 | } |
| 131 | 129 | ||
| 130 | /// Raw mutable read access to data. | ||
| 131 | pub fn raw_mut(&mut self) -> &mut [u8] { | ||
| 132 | &mut self.bytes | ||
| 133 | } | ||
| 134 | |||
| 132 | /// Checks if the length can be encoded in FDCAN DLC field. | 135 | /// Checks if the length can be encoded in FDCAN DLC field. |
| 133 | pub const fn is_valid_len(len: usize) -> bool { | 136 | pub const fn is_valid_len(len: usize) -> bool { |
| 134 | match len { | 137 | match len { |
| @@ -206,7 +209,17 @@ impl Frame { | |||
| 206 | 209 | ||
| 207 | /// Get reference to data | 210 | /// Get reference to data |
| 208 | pub fn data(&self) -> &[u8] { | 211 | pub fn data(&self) -> &[u8] { |
| 209 | &self.data.raw() | 212 | &self.data.raw()[..self.can_header.len as usize] |
| 213 | } | ||
| 214 | |||
| 215 | /// Get reference to underlying 8-byte raw data buffer, some bytes on the tail might be undefined. | ||
| 216 | pub fn raw_data(&self) -> &[u8] { | ||
| 217 | self.data.raw() | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Get mutable reference to data | ||
| 221 | pub fn data_mut(&mut self) -> &mut [u8] { | ||
| 222 | &mut self.data.raw_mut()[..self.can_header.len as usize] | ||
| 210 | } | 223 | } |
| 211 | 224 | ||
| 212 | /// Get priority of frame | 225 | /// Get priority of frame |
| @@ -250,7 +263,7 @@ impl embedded_can::Frame for Frame { | |||
| 250 | self.can_header.len as usize | 263 | self.can_header.len as usize |
| 251 | } | 264 | } |
| 252 | fn data(&self) -> &[u8] { | 265 | fn data(&self) -> &[u8] { |
| 253 | &self.data.raw() | 266 | &self.data() |
| 254 | } | 267 | } |
| 255 | } | 268 | } |
| 256 | 269 | ||
| @@ -314,6 +327,11 @@ impl FdData { | |||
| 314 | &self.bytes | 327 | &self.bytes |
| 315 | } | 328 | } |
| 316 | 329 | ||
| 330 | /// Raw mutable read access to data. | ||
| 331 | pub fn raw_mut(&mut self) -> &mut [u8] { | ||
| 332 | &mut self.bytes | ||
| 333 | } | ||
| 334 | |||
| 317 | /// Checks if the length can be encoded in FDCAN DLC field. | 335 | /// Checks if the length can be encoded in FDCAN DLC field. |
| 318 | pub const fn is_valid_len(len: usize) -> bool { | 336 | pub const fn is_valid_len(len: usize) -> bool { |
| 319 | match len { | 337 | match len { |
| @@ -390,7 +408,12 @@ impl FdFrame { | |||
| 390 | 408 | ||
| 391 | /// Get reference to data | 409 | /// Get reference to data |
| 392 | pub fn data(&self) -> &[u8] { | 410 | pub fn data(&self) -> &[u8] { |
| 393 | &self.data.raw() | 411 | &self.data.raw()[..self.can_header.len as usize] |
| 412 | } | ||
| 413 | |||
| 414 | /// Get mutable reference to data | ||
| 415 | pub fn data_mut(&mut self) -> &mut [u8] { | ||
| 416 | &mut self.data.raw_mut()[..self.can_header.len as usize] | ||
| 394 | } | 417 | } |
| 395 | } | 418 | } |
| 396 | 419 | ||
| @@ -428,7 +451,7 @@ impl embedded_can::Frame for FdFrame { | |||
| 428 | self.can_header.len as usize | 451 | self.can_header.len as usize |
| 429 | } | 452 | } |
| 430 | fn data(&self) -> &[u8] { | 453 | fn data(&self) -> &[u8] { |
| 431 | &self.data.raw() | 454 | &self.data() |
| 432 | } | 455 | } |
| 433 | } | 456 | } |
| 434 | 457 | ||
diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index fb342d2e7..320774857 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! coordinate rotation digital computer (CORDIC) | 1 | //! coordinate rotation digital computer (CORDIC) |
| 2 | 2 | ||
| 3 | use embassy_hal_internal::drop::OnDrop; | 3 | use embassy_hal_internal::drop::OnDrop; |
| 4 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 4 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 5 | 5 | ||
| 6 | use crate::pac::cordic::vals; | 6 | use crate::pac::cordic::vals; |
| 7 | use crate::{dma, peripherals, rcc}; | 7 | use crate::{dma, peripherals, rcc}; |
| @@ -16,7 +16,7 @@ pub mod utils; | |||
| 16 | 16 | ||
| 17 | /// CORDIC driver | 17 | /// CORDIC driver |
| 18 | pub struct Cordic<'d, T: Instance> { | 18 | pub struct Cordic<'d, T: Instance> { |
| 19 | peri: PeripheralRef<'d, T>, | 19 | peri: Peri<'d, T>, |
| 20 | config: Config, | 20 | config: Config, |
| 21 | } | 21 | } |
| 22 | 22 | ||
| @@ -137,7 +137,7 @@ trait SealedInstance { | |||
| 137 | 137 | ||
| 138 | /// CORDIC instance trait | 138 | /// CORDIC instance trait |
| 139 | #[allow(private_bounds)] | 139 | #[allow(private_bounds)] |
| 140 | pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral {} | 140 | pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral {} |
| 141 | 141 | ||
| 142 | /// CORDIC configuration | 142 | /// CORDIC configuration |
| 143 | #[derive(Debug)] | 143 | #[derive(Debug)] |
| @@ -198,11 +198,9 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 198 | /// Note: | 198 | /// Note: |
| 199 | /// If you need a peripheral -> CORDIC -> peripheral mode, | 199 | /// If you need a peripheral -> CORDIC -> peripheral mode, |
| 200 | /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguments with [Self::extra_config] | 200 | /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguments with [Self::extra_config] |
| 201 | pub fn new(peri: impl Peripheral<P = T> + 'd, config: Config) -> Self { | 201 | pub fn new(peri: Peri<'d, T>, config: Config) -> Self { |
| 202 | rcc::enable_and_reset::<T>(); | 202 | rcc::enable_and_reset::<T>(); |
| 203 | 203 | ||
| 204 | into_ref!(peri); | ||
| 205 | |||
| 206 | let mut instance = Self { peri, config }; | 204 | let mut instance = Self { peri, config }; |
| 207 | 205 | ||
| 208 | instance.reconfigure(); | 206 | instance.reconfigure(); |
| @@ -378,8 +376,8 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 378 | /// If you want to make sure ARG2 is set to +1, consider run [.reconfigure()](Self::reconfigure). | 376 | /// If you want to make sure ARG2 is set to +1, consider run [.reconfigure()](Self::reconfigure). |
| 379 | pub async fn async_calc_32bit( | 377 | pub async fn async_calc_32bit( |
| 380 | &mut self, | 378 | &mut self, |
| 381 | write_dma: impl Peripheral<P = impl WriteDma<T>>, | 379 | mut write_dma: Peri<'_, impl WriteDma<T>>, |
| 382 | read_dma: impl Peripheral<P = impl ReadDma<T>>, | 380 | mut read_dma: Peri<'_, impl ReadDma<T>>, |
| 383 | arg: &[u32], | 381 | arg: &[u32], |
| 384 | res: &mut [u32], | 382 | res: &mut [u32], |
| 385 | arg1_only: bool, | 383 | arg1_only: bool, |
| @@ -393,8 +391,6 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 393 | 391 | ||
| 394 | let active_res_buf = &mut res[..res_cnt]; | 392 | let active_res_buf = &mut res[..res_cnt]; |
| 395 | 393 | ||
| 396 | into_ref!(write_dma, read_dma); | ||
| 397 | |||
| 398 | self.peri | 394 | self.peri |
| 399 | .set_argument_count(if arg1_only { AccessCount::One } else { AccessCount::Two }); | 395 | .set_argument_count(if arg1_only { AccessCount::One } else { AccessCount::Two }); |
| 400 | 396 | ||
| @@ -416,7 +412,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 416 | 412 | ||
| 417 | unsafe { | 413 | unsafe { |
| 418 | let write_transfer = dma::Transfer::new_write( | 414 | let write_transfer = dma::Transfer::new_write( |
| 419 | &mut write_dma, | 415 | write_dma.reborrow(), |
| 420 | write_req, | 416 | write_req, |
| 421 | arg, | 417 | arg, |
| 422 | T::regs().wdata().as_ptr() as *mut _, | 418 | T::regs().wdata().as_ptr() as *mut _, |
| @@ -424,7 +420,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 424 | ); | 420 | ); |
| 425 | 421 | ||
| 426 | let read_transfer = dma::Transfer::new_read( | 422 | let read_transfer = dma::Transfer::new_read( |
| 427 | &mut read_dma, | 423 | read_dma.reborrow(), |
| 428 | read_req, | 424 | read_req, |
| 429 | T::regs().rdata().as_ptr() as *mut _, | 425 | T::regs().rdata().as_ptr() as *mut _, |
| 430 | active_res_buf, | 426 | active_res_buf, |
| @@ -519,8 +515,8 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 519 | /// User will take respond to merge two u16 arguments into one u32 data, and/or split one u32 data into two u16 results. | 515 | /// User will take respond to merge two u16 arguments into one u32 data, and/or split one u32 data into two u16 results. |
| 520 | pub async fn async_calc_16bit( | 516 | pub async fn async_calc_16bit( |
| 521 | &mut self, | 517 | &mut self, |
| 522 | write_dma: impl Peripheral<P = impl WriteDma<T>>, | 518 | mut write_dma: Peri<'_, impl WriteDma<T>>, |
| 523 | read_dma: impl Peripheral<P = impl ReadDma<T>>, | 519 | mut read_dma: Peri<'_, impl ReadDma<T>>, |
| 524 | arg: &[u32], | 520 | arg: &[u32], |
| 525 | res: &mut [u32], | 521 | res: &mut [u32], |
| 526 | ) -> Result<usize, CordicError> { | 522 | ) -> Result<usize, CordicError> { |
| @@ -536,8 +532,6 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 536 | 532 | ||
| 537 | let active_res_buf = &mut res[..res_cnt]; | 533 | let active_res_buf = &mut res[..res_cnt]; |
| 538 | 534 | ||
| 539 | into_ref!(write_dma, read_dma); | ||
| 540 | |||
| 541 | // In q1.15 mode, 1 write/read to access 2 arguments/results | 535 | // In q1.15 mode, 1 write/read to access 2 arguments/results |
| 542 | self.peri.set_argument_count(AccessCount::One); | 536 | self.peri.set_argument_count(AccessCount::One); |
| 543 | self.peri.set_result_count(AccessCount::One); | 537 | self.peri.set_result_count(AccessCount::One); |
| @@ -557,7 +551,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 557 | 551 | ||
| 558 | unsafe { | 552 | unsafe { |
| 559 | let write_transfer = dma::Transfer::new_write( | 553 | let write_transfer = dma::Transfer::new_write( |
| 560 | &mut write_dma, | 554 | write_dma.reborrow(), |
| 561 | write_req, | 555 | write_req, |
| 562 | arg, | 556 | arg, |
| 563 | T::regs().wdata().as_ptr() as *mut _, | 557 | T::regs().wdata().as_ptr() as *mut _, |
| @@ -565,7 +559,7 @@ impl<'d, T: Instance> Cordic<'d, T> { | |||
| 565 | ); | 559 | ); |
| 566 | 560 | ||
| 567 | let read_transfer = dma::Transfer::new_read( | 561 | let read_transfer = dma::Transfer::new_read( |
| 568 | &mut read_dma, | 562 | read_dma.reborrow(), |
| 569 | read_req, | 563 | read_req, |
| 570 | T::regs().rdata().as_ptr() as *mut _, | 564 | T::regs().rdata().as_ptr() as *mut _, |
| 571 | active_res_buf, | 565 | active_res_buf, |
diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index f3d13de7c..13e5263de 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs | |||
| @@ -1,23 +1,18 @@ | |||
| 1 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 2 | |||
| 3 | use crate::pac::CRC as PAC_CRC; | 1 | use crate::pac::CRC as PAC_CRC; |
| 4 | use crate::peripherals::CRC; | 2 | use crate::peripherals::CRC; |
| 5 | use crate::{rcc, Peripheral}; | 3 | use crate::{rcc, Peri}; |
| 6 | 4 | ||
| 7 | /// CRC driver. | 5 | /// CRC driver. |
| 8 | pub struct Crc<'d> { | 6 | pub struct Crc<'d> { |
| 9 | _peri: PeripheralRef<'d, CRC>, | 7 | _peri: Peri<'d, CRC>, |
| 10 | } | 8 | } |
| 11 | 9 | ||
| 12 | impl<'d> Crc<'d> { | 10 | impl<'d> Crc<'d> { |
| 13 | /// Instantiates the CRC32 peripheral and initializes it to default values. | 11 | /// Instantiates the CRC32 peripheral and initializes it to default values. |
| 14 | pub fn new(peripheral: impl Peripheral<P = CRC> + 'd) -> Self { | 12 | pub fn new(peripheral: Peri<'d, CRC>) -> Self { |
| 15 | into_ref!(peripheral); | ||
| 16 | |||
| 17 | // Note: enable and reset come from RccPeripheral. | 13 | // Note: enable and reset come from RccPeripheral. |
| 18 | // enable CRC clock in RCC. | 14 | // enable CRC clock in RCC. |
| 19 | rcc::enable_and_reset::<CRC>(); | 15 | rcc::enable_and_reset::<CRC>(); |
| 20 | // Peripheral the peripheral | ||
| 21 | let mut instance = Self { _peri: peripheral }; | 16 | let mut instance = Self { _peri: peripheral }; |
| 22 | instance.reset(); | 17 | instance.reset(); |
| 23 | instance | 18 | instance |
| @@ -28,22 +23,16 @@ impl<'d> Crc<'d> { | |||
| 28 | PAC_CRC.cr().write(|w| w.set_reset(true)); | 23 | PAC_CRC.cr().write(|w| w.set_reset(true)); |
| 29 | } | 24 | } |
| 30 | 25 | ||
| 31 | /// Feeds a word to the peripheral and returns the current CRC value | 26 | /// Feeds a word into the CRC peripheral. Returns the computed CRC. |
| 32 | pub fn feed_word(&mut self, word: u32) -> u32 { | 27 | pub fn feed_word(&mut self, word: u32) -> u32 { |
| 33 | // write a single byte to the device, and return the result | 28 | // write a single byte to the device, and return the result |
| 34 | #[cfg(not(crc_v1))] | ||
| 35 | PAC_CRC.dr32().write_value(word); | ||
| 36 | #[cfg(crc_v1)] | ||
| 37 | PAC_CRC.dr().write_value(word); | 29 | PAC_CRC.dr().write_value(word); |
| 38 | self.read() | 30 | self.read() |
| 39 | } | 31 | } |
| 40 | 32 | ||
| 41 | /// Feed a slice of words to the peripheral and return the result. | 33 | /// Feeds a slice of words into the CRC peripheral. Returns the computed CRC. |
| 42 | pub fn feed_words(&mut self, words: &[u32]) -> u32 { | 34 | pub fn feed_words(&mut self, words: &[u32]) -> u32 { |
| 43 | for word in words { | 35 | for word in words { |
| 44 | #[cfg(not(crc_v1))] | ||
| 45 | PAC_CRC.dr32().write_value(*word); | ||
| 46 | #[cfg(crc_v1)] | ||
| 47 | PAC_CRC.dr().write_value(*word); | 36 | PAC_CRC.dr().write_value(*word); |
| 48 | } | 37 | } |
| 49 | 38 | ||
| @@ -51,12 +40,6 @@ impl<'d> Crc<'d> { | |||
| 51 | } | 40 | } |
| 52 | 41 | ||
| 53 | /// Read the CRC result value. | 42 | /// Read the CRC result value. |
| 54 | #[cfg(not(crc_v1))] | ||
| 55 | pub fn read(&self) -> u32 { | ||
| 56 | PAC_CRC.dr32().read() | ||
| 57 | } | ||
| 58 | /// Read the CRC result value. | ||
| 59 | #[cfg(crc_v1)] | ||
| 60 | pub fn read(&self) -> u32 { | 43 | pub fn read(&self) -> u32 { |
| 61 | PAC_CRC.dr().read() | 44 | PAC_CRC.dr().read() |
| 62 | } | 45 | } |
diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index 09d956d7c..d834d0971 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs | |||
| @@ -1,17 +1,15 @@ | |||
| 1 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 2 | |||
| 3 | use crate::pac::crc::vals; | 1 | use crate::pac::crc::vals; |
| 4 | use crate::pac::CRC as PAC_CRC; | 2 | use crate::pac::CRC as PAC_CRC; |
| 5 | use crate::peripherals::CRC; | 3 | use crate::peripherals::CRC; |
| 6 | use crate::{rcc, Peripheral}; | 4 | use crate::{rcc, Peri}; |
| 7 | 5 | ||
| 8 | /// CRC driver. | 6 | /// CRC driver. |
| 9 | pub struct Crc<'d> { | 7 | pub struct Crc<'d> { |
| 10 | _peripheral: PeripheralRef<'d, CRC>, | 8 | _peripheral: Peri<'d, CRC>, |
| 11 | _config: Config, | 9 | _config: Config, |
| 12 | } | 10 | } |
| 13 | 11 | ||
| 14 | /// CRC configuration errlr | 12 | /// CRC configuration error |
| 15 | #[derive(Debug)] | 13 | #[derive(Debug)] |
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 14 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 17 | pub enum ConfigError { | 15 | pub enum ConfigError { |
| @@ -36,9 +34,9 @@ pub enum InputReverseConfig { | |||
| 36 | None, | 34 | None, |
| 37 | /// Reverse bytes | 35 | /// Reverse bytes |
| 38 | Byte, | 36 | Byte, |
| 39 | /// Reverse 16-bit halfwords. | 37 | /// Reverse 16-bit halfwords |
| 40 | Halfword, | 38 | Halfword, |
| 41 | /// Reverse 32-bit words. | 39 | /// Reverse 32-bit words |
| 42 | Word, | 40 | Word, |
| 43 | } | 41 | } |
| 44 | 42 | ||
| @@ -80,11 +78,10 @@ pub enum PolySize { | |||
| 80 | 78 | ||
| 81 | impl<'d> Crc<'d> { | 79 | impl<'d> Crc<'d> { |
| 82 | /// Instantiates the CRC32 peripheral and initializes it to default values. | 80 | /// Instantiates the CRC32 peripheral and initializes it to default values. |
| 83 | pub fn new(peripheral: impl Peripheral<P = CRC> + 'd, config: Config) -> Self { | 81 | pub fn new(peripheral: Peri<'d, CRC>, config: Config) -> Self { |
| 84 | // Note: enable and reset come from RccPeripheral. | 82 | // Note: enable and reset come from RccPeripheral. |
| 85 | // reset to default values and enable CRC clock in RCC. | 83 | // reset to default values and enable CRC clock in RCC. |
| 86 | rcc::enable_and_reset::<CRC>(); | 84 | rcc::enable_and_reset::<CRC>(); |
| 87 | into_ref!(peripheral); | ||
| 88 | let mut instance = Self { | 85 | let mut instance = Self { |
| 89 | _peripheral: peripheral, | 86 | _peripheral: peripheral, |
| 90 | _config: config, | 87 | _config: config, |
| @@ -118,7 +115,7 @@ impl<'d> Crc<'d> { | |||
| 118 | w.set_rev_in(match self._config.reverse_in { | 115 | w.set_rev_in(match self._config.reverse_in { |
| 119 | InputReverseConfig::None => vals::RevIn::NORMAL, | 116 | InputReverseConfig::None => vals::RevIn::NORMAL, |
| 120 | InputReverseConfig::Byte => vals::RevIn::BYTE, | 117 | InputReverseConfig::Byte => vals::RevIn::BYTE, |
| 121 | InputReverseConfig::Halfword => vals::RevIn::HALFWORD, | 118 | InputReverseConfig::Halfword => vals::RevIn::HALF_WORD, |
| 122 | InputReverseConfig::Word => vals::RevIn::WORD, | 119 | InputReverseConfig::Word => vals::RevIn::WORD, |
| 123 | }); | 120 | }); |
| 124 | // configure the polynomial. | 121 | // configure the polynomial. |
| @@ -130,45 +127,52 @@ impl<'d> Crc<'d> { | |||
| 130 | PolySize::Width32 => vals::Polysize::POLYSIZE32, | 127 | PolySize::Width32 => vals::Polysize::POLYSIZE32, |
| 131 | }); | 128 | }); |
| 132 | }); | 129 | }); |
| 130 | } | ||
| 133 | 131 | ||
| 134 | self.reset(); | 132 | /// Read the CRC result value. |
| 133 | pub fn read(&self) -> u32 { | ||
| 134 | PAC_CRC.dr32().read() | ||
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | /// Feeds a byte into the CRC peripheral. Returns the computed checksum. | 137 | /// Feeds a byte into the CRC peripheral. Returns the computed CRC. |
| 138 | pub fn feed_byte(&mut self, byte: u8) -> u32 { | 138 | pub fn feed_byte(&mut self, byte: u8) -> u32 { |
| 139 | PAC_CRC.dr8().write_value(byte); | 139 | PAC_CRC.dr8().write_value(byte); |
| 140 | PAC_CRC.dr32().read() | 140 | self.read() |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum. | 143 | /// Feeds a slice of bytes into the CRC peripheral. Returns the computed CRC. |
| 144 | pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { | 144 | pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { |
| 145 | for byte in bytes { | 145 | for byte in bytes { |
| 146 | PAC_CRC.dr8().write_value(*byte); | 146 | PAC_CRC.dr8().write_value(*byte); |
| 147 | } | 147 | } |
| 148 | PAC_CRC.dr32().read() | 148 | self.read() |
| 149 | } | 149 | } |
| 150 | /// Feeds a halfword into the CRC peripheral. Returns the computed checksum. | 150 | |
| 151 | /// Feeds a halfword into the CRC peripheral. Returns the computed CRC. | ||
| 151 | pub fn feed_halfword(&mut self, halfword: u16) -> u32 { | 152 | pub fn feed_halfword(&mut self, halfword: u16) -> u32 { |
| 152 | PAC_CRC.dr16().write_value(halfword); | 153 | PAC_CRC.dr16().write_value(halfword); |
| 153 | PAC_CRC.dr32().read() | 154 | self.read() |
| 154 | } | 155 | } |
| 155 | /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum. | 156 | |
| 157 | /// Feeds a slice of halfwords into the CRC peripheral. Returns the computed CRC. | ||
| 156 | pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { | 158 | pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { |
| 157 | for halfword in halfwords { | 159 | for halfword in halfwords { |
| 158 | PAC_CRC.dr16().write_value(*halfword); | 160 | PAC_CRC.dr16().write_value(*halfword); |
| 159 | } | 161 | } |
| 160 | PAC_CRC.dr32().read() | 162 | self.read() |
| 161 | } | 163 | } |
| 162 | /// Feeds a words into the CRC peripheral. Returns the computed checksum. | 164 | |
| 165 | /// Feeds a word into the CRC peripheral. Returns the computed CRC. | ||
| 163 | pub fn feed_word(&mut self, word: u32) -> u32 { | 166 | pub fn feed_word(&mut self, word: u32) -> u32 { |
| 164 | PAC_CRC.dr32().write_value(word as u32); | 167 | PAC_CRC.dr32().write_value(word as u32); |
| 165 | PAC_CRC.dr32().read() | 168 | self.read() |
| 166 | } | 169 | } |
| 167 | /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum. | 170 | |
| 171 | /// Feeds a slice of words into the CRC peripheral. Returns the computed CRC. | ||
| 168 | pub fn feed_words(&mut self, words: &[u32]) -> u32 { | 172 | pub fn feed_words(&mut self, words: &[u32]) -> u32 { |
| 169 | for word in words { | 173 | for word in words { |
| 170 | PAC_CRC.dr32().write_value(*word as u32); | 174 | PAC_CRC.dr32().write_value(*word as u32); |
| 171 | } | 175 | } |
| 172 | PAC_CRC.dr32().read() | 176 | self.read() |
| 173 | } | 177 | } |
| 174 | } | 178 | } |
diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 8d600c73c..fba3c0fd7 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs | |||
| @@ -4,12 +4,13 @@ use core::cmp::min; | |||
| 4 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | use core::ptr; | 5 | use core::ptr; |
| 6 | 6 | ||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 7 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 8 | use embassy_sync::waitqueue::AtomicWaker; | 8 | use embassy_sync::waitqueue::AtomicWaker; |
| 9 | 9 | ||
| 10 | use crate::dma::{NoDma, Transfer, TransferOptions}; | 10 | use crate::dma::{ChannelAndRequest, TransferOptions}; |
| 11 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::{interrupt, pac, peripherals, rcc, Peripheral}; | 12 | use crate::mode::{Async, Blocking, Mode}; |
| 13 | use crate::{interrupt, pac, peripherals, rcc}; | ||
| 13 | 14 | ||
| 14 | const DES_BLOCK_SIZE: usize = 8; // 64 bits | 15 | const DES_BLOCK_SIZE: usize = 8; // 64 bits |
| 15 | const AES_BLOCK_SIZE: usize = 16; // 128 bits | 16 | const AES_BLOCK_SIZE: usize = 16; // 128 bits |
| @@ -57,15 +58,10 @@ pub trait Cipher<'c> { | |||
| 57 | fn prepare_key(&self, _p: pac::cryp::Cryp) {} | 58 | fn prepare_key(&self, _p: pac::cryp::Cryp) {} |
| 58 | 59 | ||
| 59 | /// Performs any cipher-specific initialization. | 60 | /// Performs any cipher-specific initialization. |
| 60 | fn init_phase_blocking<T: Instance, DmaIn, DmaOut>(&self, _p: pac::cryp::Cryp, _cryp: &Cryp<T, DmaIn, DmaOut>) {} | 61 | fn init_phase_blocking<T: Instance, M: Mode>(&self, _p: pac::cryp::Cryp, _cryp: &Cryp<T, M>) {} |
| 61 | 62 | ||
| 62 | /// Performs any cipher-specific initialization. | 63 | /// Performs any cipher-specific initialization. |
| 63 | async fn init_phase<T: Instance, DmaIn, DmaOut>(&self, _p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) | 64 | async fn init_phase<T: Instance>(&self, _p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, Async>) {} |
| 64 | where | ||
| 65 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 66 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 67 | { | ||
| 68 | } | ||
| 69 | 65 | ||
| 70 | /// Called prior to processing the last data block for cipher-specific operations. | 66 | /// Called prior to processing the last data block for cipher-specific operations. |
| 71 | fn pre_final(&self, _p: pac::cryp::Cryp, _dir: Direction, _padding_len: usize) -> [u32; 4] { | 67 | fn pre_final(&self, _p: pac::cryp::Cryp, _dir: Direction, _padding_len: usize) -> [u32; 4] { |
| @@ -73,10 +69,10 @@ pub trait Cipher<'c> { | |||
| 73 | } | 69 | } |
| 74 | 70 | ||
| 75 | /// Called after processing the last data block for cipher-specific operations. | 71 | /// Called after processing the last data block for cipher-specific operations. |
| 76 | fn post_final_blocking<T: Instance, DmaIn, DmaOut>( | 72 | fn post_final_blocking<T: Instance, M: Mode>( |
| 77 | &self, | 73 | &self, |
| 78 | _p: pac::cryp::Cryp, | 74 | _p: pac::cryp::Cryp, |
| 79 | _cryp: &Cryp<T, DmaIn, DmaOut>, | 75 | _cryp: &Cryp<T, M>, |
| 80 | _dir: Direction, | 76 | _dir: Direction, |
| 81 | _int_data: &mut [u8; AES_BLOCK_SIZE], | 77 | _int_data: &mut [u8; AES_BLOCK_SIZE], |
| 82 | _temp1: [u32; 4], | 78 | _temp1: [u32; 4], |
| @@ -85,18 +81,15 @@ pub trait Cipher<'c> { | |||
| 85 | } | 81 | } |
| 86 | 82 | ||
| 87 | /// Called after processing the last data block for cipher-specific operations. | 83 | /// Called after processing the last data block for cipher-specific operations. |
| 88 | async fn post_final<T: Instance, DmaIn, DmaOut>( | 84 | async fn post_final<T: Instance>( |
| 89 | &self, | 85 | &self, |
| 90 | _p: pac::cryp::Cryp, | 86 | _p: pac::cryp::Cryp, |
| 91 | _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, | 87 | _cryp: &mut Cryp<'_, T, Async>, |
| 92 | _dir: Direction, | 88 | _dir: Direction, |
| 93 | _int_data: &mut [u8; AES_BLOCK_SIZE], | 89 | _int_data: &mut [u8; AES_BLOCK_SIZE], |
| 94 | _temp1: [u32; 4], | 90 | _temp1: [u32; 4], |
| 95 | _padding_mask: [u8; 16], | 91 | _padding_mask: [u8; 16], |
| 96 | ) where | 92 | ) { |
| 97 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 98 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 99 | { | ||
| 100 | } | 93 | } |
| 101 | 94 | ||
| 102 | /// Returns the AAD header block as required by the cipher. | 95 | /// Returns the AAD header block as required by the cipher. |
| @@ -474,13 +467,13 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { | |||
| 474 | p.cr().modify(|w| w.set_algomode3(true)); | 467 | p.cr().modify(|w| w.set_algomode3(true)); |
| 475 | } | 468 | } |
| 476 | 469 | ||
| 477 | fn init_phase_blocking<T: Instance, DmaIn, DmaOut>(&self, p: pac::cryp::Cryp, _cryp: &Cryp<T, DmaIn, DmaOut>) { | 470 | fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::cryp::Cryp, _cryp: &Cryp<T, M>) { |
| 478 | p.cr().modify(|w| w.set_gcm_ccmph(0)); | 471 | p.cr().modify(|w| w.set_gcm_ccmph(0)); |
| 479 | p.cr().modify(|w| w.set_crypen(true)); | 472 | p.cr().modify(|w| w.set_crypen(true)); |
| 480 | while p.cr().read().crypen() {} | 473 | while p.cr().read().crypen() {} |
| 481 | } | 474 | } |
| 482 | 475 | ||
| 483 | async fn init_phase<T: Instance, DmaIn, DmaOut>(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) { | 476 | async fn init_phase<T: Instance>(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, Async>) { |
| 484 | p.cr().modify(|w| w.set_gcm_ccmph(0)); | 477 | p.cr().modify(|w| w.set_gcm_ccmph(0)); |
| 485 | p.cr().modify(|w| w.set_crypen(true)); | 478 | p.cr().modify(|w| w.set_crypen(true)); |
| 486 | while p.cr().read().crypen() {} | 479 | while p.cr().read().crypen() {} |
| @@ -508,10 +501,10 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { | |||
| 508 | } | 501 | } |
| 509 | 502 | ||
| 510 | #[cfg(cryp_v2)] | 503 | #[cfg(cryp_v2)] |
| 511 | fn post_final_blocking<T: Instance, DmaIn, DmaOut>( | 504 | fn post_final_blocking<T: Instance, M: Mode>( |
| 512 | &self, | 505 | &self, |
| 513 | p: pac::cryp::Cryp, | 506 | p: pac::cryp::Cryp, |
| 514 | cryp: &Cryp<T, DmaIn, DmaOut>, | 507 | cryp: &Cryp<T, M>, |
| 515 | dir: Direction, | 508 | dir: Direction, |
| 516 | int_data: &mut [u8; AES_BLOCK_SIZE], | 509 | int_data: &mut [u8; AES_BLOCK_SIZE], |
| 517 | _temp1: [u32; 4], | 510 | _temp1: [u32; 4], |
| @@ -534,18 +527,15 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { | |||
| 534 | } | 527 | } |
| 535 | 528 | ||
| 536 | #[cfg(cryp_v2)] | 529 | #[cfg(cryp_v2)] |
| 537 | async fn post_final<T: Instance, DmaIn, DmaOut>( | 530 | async fn post_final<T: Instance>( |
| 538 | &self, | 531 | &self, |
| 539 | p: pac::cryp::Cryp, | 532 | p: pac::cryp::Cryp, |
| 540 | cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, | 533 | cryp: &mut Cryp<'_, T, Async>, |
| 541 | dir: Direction, | 534 | dir: Direction, |
| 542 | int_data: &mut [u8; AES_BLOCK_SIZE], | 535 | int_data: &mut [u8; AES_BLOCK_SIZE], |
| 543 | _temp1: [u32; 4], | 536 | _temp1: [u32; 4], |
| 544 | padding_mask: [u8; AES_BLOCK_SIZE], | 537 | padding_mask: [u8; AES_BLOCK_SIZE], |
| 545 | ) where | 538 | ) { |
| 546 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 547 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 548 | { | ||
| 549 | if dir == Direction::Encrypt { | 539 | if dir == Direction::Encrypt { |
| 550 | // Handle special GCM partial block process. | 540 | // Handle special GCM partial block process. |
| 551 | p.cr().modify(|w| w.set_crypen(false)); | 541 | p.cr().modify(|w| w.set_crypen(false)); |
| @@ -559,8 +549,8 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { | |||
| 559 | 549 | ||
| 560 | let mut out_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; | 550 | let mut out_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; |
| 561 | 551 | ||
| 562 | let read = Cryp::<T, DmaIn, DmaOut>::read_bytes(&mut cryp.outdma, Self::BLOCK_SIZE, &mut out_data); | 552 | let read = Cryp::<T, Async>::read_bytes(cryp.outdma.as_mut().unwrap(), Self::BLOCK_SIZE, &mut out_data); |
| 563 | let write = Cryp::<T, DmaIn, DmaOut>::write_bytes(&mut cryp.indma, Self::BLOCK_SIZE, int_data); | 553 | let write = Cryp::<T, Async>::write_bytes(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, int_data); |
| 564 | 554 | ||
| 565 | embassy_futures::join::join(read, write).await; | 555 | embassy_futures::join::join(read, write).await; |
| 566 | 556 | ||
| @@ -615,13 +605,13 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { | |||
| 615 | p.cr().modify(|w| w.set_algomode3(true)); | 605 | p.cr().modify(|w| w.set_algomode3(true)); |
| 616 | } | 606 | } |
| 617 | 607 | ||
| 618 | fn init_phase_blocking<T: Instance, DmaIn, DmaOut>(&self, p: pac::cryp::Cryp, _cryp: &Cryp<T, DmaIn, DmaOut>) { | 608 | fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::cryp::Cryp, _cryp: &Cryp<T, M>) { |
| 619 | p.cr().modify(|w| w.set_gcm_ccmph(0)); | 609 | p.cr().modify(|w| w.set_gcm_ccmph(0)); |
| 620 | p.cr().modify(|w| w.set_crypen(true)); | 610 | p.cr().modify(|w| w.set_crypen(true)); |
| 621 | while p.cr().read().crypen() {} | 611 | while p.cr().read().crypen() {} |
| 622 | } | 612 | } |
| 623 | 613 | ||
| 624 | async fn init_phase<T: Instance, DmaIn, DmaOut>(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) { | 614 | async fn init_phase<T: Instance>(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, Async>) { |
| 625 | p.cr().modify(|w| w.set_gcm_ccmph(0)); | 615 | p.cr().modify(|w| w.set_gcm_ccmph(0)); |
| 626 | p.cr().modify(|w| w.set_crypen(true)); | 616 | p.cr().modify(|w| w.set_crypen(true)); |
| 627 | while p.cr().read().crypen() {} | 617 | while p.cr().read().crypen() {} |
| @@ -649,10 +639,10 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { | |||
| 649 | } | 639 | } |
| 650 | 640 | ||
| 651 | #[cfg(cryp_v2)] | 641 | #[cfg(cryp_v2)] |
| 652 | fn post_final_blocking<T: Instance, DmaIn, DmaOut>( | 642 | fn post_final_blocking<T: Instance, M: Mode>( |
| 653 | &self, | 643 | &self, |
| 654 | p: pac::cryp::Cryp, | 644 | p: pac::cryp::Cryp, |
| 655 | cryp: &Cryp<T, DmaIn, DmaOut>, | 645 | cryp: &Cryp<T, M>, |
| 656 | dir: Direction, | 646 | dir: Direction, |
| 657 | int_data: &mut [u8; AES_BLOCK_SIZE], | 647 | int_data: &mut [u8; AES_BLOCK_SIZE], |
| 658 | _temp1: [u32; 4], | 648 | _temp1: [u32; 4], |
| @@ -675,18 +665,15 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { | |||
| 675 | } | 665 | } |
| 676 | 666 | ||
| 677 | #[cfg(cryp_v2)] | 667 | #[cfg(cryp_v2)] |
| 678 | async fn post_final<T: Instance, DmaIn, DmaOut>( | 668 | async fn post_final<T: Instance>( |
| 679 | &self, | 669 | &self, |
| 680 | p: pac::cryp::Cryp, | 670 | p: pac::cryp::Cryp, |
| 681 | cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, | 671 | cryp: &mut Cryp<'_, T, Async>, |
| 682 | dir: Direction, | 672 | dir: Direction, |
| 683 | int_data: &mut [u8; AES_BLOCK_SIZE], | 673 | int_data: &mut [u8; AES_BLOCK_SIZE], |
| 684 | _temp1: [u32; 4], | 674 | _temp1: [u32; 4], |
| 685 | padding_mask: [u8; AES_BLOCK_SIZE], | 675 | padding_mask: [u8; AES_BLOCK_SIZE], |
| 686 | ) where | 676 | ) { |
| 687 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 688 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 689 | { | ||
| 690 | if dir == Direction::Encrypt { | 677 | if dir == Direction::Encrypt { |
| 691 | // Handle special GCM partial block process. | 678 | // Handle special GCM partial block process. |
| 692 | p.cr().modify(|w| w.set_crypen(false)); | 679 | p.cr().modify(|w| w.set_crypen(false)); |
| @@ -700,8 +687,8 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { | |||
| 700 | 687 | ||
| 701 | let mut out_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; | 688 | let mut out_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; |
| 702 | 689 | ||
| 703 | let read = Cryp::<T, DmaIn, DmaOut>::read_bytes(&mut cryp.outdma, Self::BLOCK_SIZE, &mut out_data); | 690 | let read = Cryp::<T, Async>::read_bytes(cryp.outdma.as_mut().unwrap(), Self::BLOCK_SIZE, &mut out_data); |
| 704 | let write = Cryp::<T, DmaIn, DmaOut>::write_bytes(&mut cryp.indma, Self::BLOCK_SIZE, int_data); | 691 | let write = Cryp::<T, Async>::write_bytes(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, int_data); |
| 705 | 692 | ||
| 706 | embassy_futures::join::join(read, write).await; | 693 | embassy_futures::join::join(read, write).await; |
| 707 | } | 694 | } |
| @@ -812,7 +799,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip | |||
| 812 | p.cr().modify(|w| w.set_algomode3(true)); | 799 | p.cr().modify(|w| w.set_algomode3(true)); |
| 813 | } | 800 | } |
| 814 | 801 | ||
| 815 | fn init_phase_blocking<T: Instance, DmaIn, DmaOut>(&self, p: pac::cryp::Cryp, cryp: &Cryp<T, DmaIn, DmaOut>) { | 802 | fn init_phase_blocking<T: Instance, M: Mode>(&self, p: pac::cryp::Cryp, cryp: &Cryp<T, M>) { |
| 816 | p.cr().modify(|w| w.set_gcm_ccmph(0)); | 803 | p.cr().modify(|w| w.set_gcm_ccmph(0)); |
| 817 | 804 | ||
| 818 | cryp.write_bytes_blocking(Self::BLOCK_SIZE, &self.block0); | 805 | cryp.write_bytes_blocking(Self::BLOCK_SIZE, &self.block0); |
| @@ -821,14 +808,10 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip | |||
| 821 | while p.cr().read().crypen() {} | 808 | while p.cr().read().crypen() {} |
| 822 | } | 809 | } |
| 823 | 810 | ||
| 824 | async fn init_phase<T: Instance, DmaIn, DmaOut>(&self, p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) | 811 | async fn init_phase<T: Instance>(&self, p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, Async>) { |
| 825 | where | ||
| 826 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 827 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 828 | { | ||
| 829 | p.cr().modify(|w| w.set_gcm_ccmph(0)); | 812 | p.cr().modify(|w| w.set_gcm_ccmph(0)); |
| 830 | 813 | ||
| 831 | Cryp::<T, DmaIn, DmaOut>::write_bytes(&mut cryp.indma, Self::BLOCK_SIZE, &self.block0).await; | 814 | Cryp::<T, Async>::write_bytes(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, &self.block0).await; |
| 832 | 815 | ||
| 833 | p.cr().modify(|w| w.set_crypen(true)); | 816 | p.cr().modify(|w| w.set_crypen(true)); |
| 834 | while p.cr().read().crypen() {} | 817 | while p.cr().read().crypen() {} |
| @@ -865,10 +848,10 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip | |||
| 865 | } | 848 | } |
| 866 | 849 | ||
| 867 | #[cfg(cryp_v2)] | 850 | #[cfg(cryp_v2)] |
| 868 | fn post_final_blocking<T: Instance, DmaIn, DmaOut>( | 851 | fn post_final_blocking<T: Instance, M: Mode>( |
| 869 | &self, | 852 | &self, |
| 870 | p: pac::cryp::Cryp, | 853 | p: pac::cryp::Cryp, |
| 871 | cryp: &Cryp<T, DmaIn, DmaOut>, | 854 | cryp: &Cryp<T, M>, |
| 872 | dir: Direction, | 855 | dir: Direction, |
| 873 | int_data: &mut [u8; AES_BLOCK_SIZE], | 856 | int_data: &mut [u8; AES_BLOCK_SIZE], |
| 874 | temp1: [u32; 4], | 857 | temp1: [u32; 4], |
| @@ -902,18 +885,15 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip | |||
| 902 | } | 885 | } |
| 903 | 886 | ||
| 904 | #[cfg(cryp_v2)] | 887 | #[cfg(cryp_v2)] |
| 905 | async fn post_final<T: Instance, DmaIn, DmaOut>( | 888 | async fn post_final<T: Instance>( |
| 906 | &self, | 889 | &self, |
| 907 | p: pac::cryp::Cryp, | 890 | p: pac::cryp::Cryp, |
| 908 | cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, | 891 | cryp: &mut Cryp<'_, T, Async>, |
| 909 | dir: Direction, | 892 | dir: Direction, |
| 910 | int_data: &mut [u8; AES_BLOCK_SIZE], | 893 | int_data: &mut [u8; AES_BLOCK_SIZE], |
| 911 | temp1: [u32; 4], | 894 | temp1: [u32; 4], |
| 912 | padding_mask: [u8; 16], | 895 | padding_mask: [u8; 16], |
| 913 | ) where | 896 | ) { |
| 914 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 915 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 916 | { | ||
| 917 | if dir == Direction::Decrypt { | 897 | if dir == Direction::Decrypt { |
| 918 | //Handle special CCM partial block process. | 898 | //Handle special CCM partial block process. |
| 919 | let mut temp2 = [0; 4]; | 899 | let mut temp2 = [0; 4]; |
| @@ -937,7 +917,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip | |||
| 937 | in_data[i] = int_word; | 917 | in_data[i] = int_word; |
| 938 | in_data[i] = in_data[i] ^ temp1[i] ^ temp2[i]; | 918 | in_data[i] = in_data[i] ^ temp1[i] ^ temp2[i]; |
| 939 | } | 919 | } |
| 940 | Cryp::<T, DmaIn, DmaOut>::write_words(&mut cryp.indma, Self::BLOCK_SIZE, &in_data).await; | 920 | Cryp::<T, Async>::write_words(cryp.indma.as_mut().unwrap(), Self::BLOCK_SIZE, &in_data).await; |
| 941 | } | 921 | } |
| 942 | } | 922 | } |
| 943 | } | 923 | } |
| @@ -1007,26 +987,25 @@ pub enum Direction { | |||
| 1007 | } | 987 | } |
| 1008 | 988 | ||
| 1009 | /// Crypto Accelerator Driver | 989 | /// Crypto Accelerator Driver |
| 1010 | pub struct Cryp<'d, T: Instance, DmaIn = NoDma, DmaOut = NoDma> { | 990 | pub struct Cryp<'d, T: Instance, M: Mode> { |
| 1011 | _peripheral: PeripheralRef<'d, T>, | 991 | _peripheral: Peri<'d, T>, |
| 1012 | indma: PeripheralRef<'d, DmaIn>, | 992 | _phantom: PhantomData<M>, |
| 1013 | outdma: PeripheralRef<'d, DmaOut>, | 993 | indma: Option<ChannelAndRequest<'d>>, |
| 994 | outdma: Option<ChannelAndRequest<'d>>, | ||
| 1014 | } | 995 | } |
| 1015 | 996 | ||
| 1016 | impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | 997 | impl<'d, T: Instance> Cryp<'d, T, Blocking> { |
| 1017 | /// Create a new CRYP driver. | 998 | /// Create a new CRYP driver in blocking mode. |
| 1018 | pub fn new( | 999 | pub fn new_blocking( |
| 1019 | peri: impl Peripheral<P = T> + 'd, | 1000 | peri: Peri<'d, T>, |
| 1020 | indma: impl Peripheral<P = DmaIn> + 'd, | ||
| 1021 | outdma: impl Peripheral<P = DmaOut> + 'd, | ||
| 1022 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1001 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1023 | ) -> Self { | 1002 | ) -> Self { |
| 1024 | rcc::enable_and_reset::<T>(); | 1003 | rcc::enable_and_reset::<T>(); |
| 1025 | into_ref!(peri, indma, outdma); | ||
| 1026 | let instance = Self { | 1004 | let instance = Self { |
| 1027 | _peripheral: peri, | 1005 | _peripheral: peri, |
| 1028 | indma: indma, | 1006 | _phantom: PhantomData, |
| 1029 | outdma: outdma, | 1007 | indma: None, |
| 1008 | outdma: None, | ||
| 1030 | }; | 1009 | }; |
| 1031 | 1010 | ||
| 1032 | T::Interrupt::unpend(); | 1011 | T::Interrupt::unpend(); |
| @@ -1034,7 +1013,9 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1034 | 1013 | ||
| 1035 | instance | 1014 | instance |
| 1036 | } | 1015 | } |
| 1016 | } | ||
| 1037 | 1017 | ||
| 1018 | impl<'d, T: Instance, M: Mode> Cryp<'d, T, M> { | ||
| 1038 | /// Start a new encrypt or decrypt operation for the given cipher. | 1019 | /// Start a new encrypt or decrypt operation for the given cipher. |
| 1039 | pub fn start_blocking<'c, C: Cipher<'c> + CipherSized + IVSized>( | 1020 | pub fn start_blocking<'c, C: Cipher<'c> + CipherSized + IVSized>( |
| 1040 | &self, | 1021 | &self, |
| @@ -1114,89 +1095,6 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1114 | ctx | 1095 | ctx |
| 1115 | } | 1096 | } |
| 1116 | 1097 | ||
| 1117 | /// Start a new encrypt or decrypt operation for the given cipher. | ||
| 1118 | pub async fn start<'c, C: Cipher<'c> + CipherSized + IVSized>( | ||
| 1119 | &mut self, | ||
| 1120 | cipher: &'c C, | ||
| 1121 | dir: Direction, | ||
| 1122 | ) -> Context<'c, C> | ||
| 1123 | where | ||
| 1124 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 1125 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 1126 | { | ||
| 1127 | let mut ctx: Context<'c, C> = Context { | ||
| 1128 | dir, | ||
| 1129 | last_block_processed: false, | ||
| 1130 | cr: 0, | ||
| 1131 | iv: [0; 4], | ||
| 1132 | csgcmccm: [0; 8], | ||
| 1133 | csgcm: [0; 8], | ||
| 1134 | aad_complete: false, | ||
| 1135 | header_len: 0, | ||
| 1136 | payload_len: 0, | ||
| 1137 | cipher: cipher, | ||
| 1138 | phantom_data: PhantomData, | ||
| 1139 | header_processed: false, | ||
| 1140 | aad_buffer: [0; 16], | ||
| 1141 | aad_buffer_len: 0, | ||
| 1142 | }; | ||
| 1143 | |||
| 1144 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1145 | |||
| 1146 | let key = ctx.cipher.key(); | ||
| 1147 | |||
| 1148 | if key.len() == (128 / 8) { | ||
| 1149 | T::regs().cr().modify(|w| w.set_keysize(0)); | ||
| 1150 | } else if key.len() == (192 / 8) { | ||
| 1151 | T::regs().cr().modify(|w| w.set_keysize(1)); | ||
| 1152 | } else if key.len() == (256 / 8) { | ||
| 1153 | T::regs().cr().modify(|w| w.set_keysize(2)); | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | self.load_key(key); | ||
| 1157 | |||
| 1158 | // Set data type to 8-bit. This will match software implementations. | ||
| 1159 | T::regs().cr().modify(|w| w.set_datatype(2)); | ||
| 1160 | |||
| 1161 | ctx.cipher.prepare_key(T::regs()); | ||
| 1162 | |||
| 1163 | ctx.cipher.set_algomode(T::regs()); | ||
| 1164 | |||
| 1165 | // Set encrypt/decrypt | ||
| 1166 | if dir == Direction::Encrypt { | ||
| 1167 | T::regs().cr().modify(|w| w.set_algodir(false)); | ||
| 1168 | } else { | ||
| 1169 | T::regs().cr().modify(|w| w.set_algodir(true)); | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | // Load the IV into the registers. | ||
| 1173 | let iv = ctx.cipher.iv(); | ||
| 1174 | let mut full_iv: [u8; 16] = [0; 16]; | ||
| 1175 | full_iv[0..iv.len()].copy_from_slice(iv); | ||
| 1176 | let mut iv_idx = 0; | ||
| 1177 | let mut iv_word: [u8; 4] = [0; 4]; | ||
| 1178 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1179 | iv_idx += 4; | ||
| 1180 | T::regs().init(0).ivlr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1181 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1182 | iv_idx += 4; | ||
| 1183 | T::regs().init(0).ivrr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1184 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1185 | iv_idx += 4; | ||
| 1186 | T::regs().init(1).ivlr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1187 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1188 | T::regs().init(1).ivrr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1189 | |||
| 1190 | // Flush in/out FIFOs | ||
| 1191 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1192 | |||
| 1193 | ctx.cipher.init_phase(T::regs(), self).await; | ||
| 1194 | |||
| 1195 | self.store_context(&mut ctx); | ||
| 1196 | |||
| 1197 | ctx | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | 1098 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] |
| 1201 | /// Controls the header phase of cipher processing. | 1099 | /// Controls the header phase of cipher processing. |
| 1202 | /// This function is only valid for authenticated ciphers including GCM, CCM, and GMAC. | 1100 | /// This function is only valid for authenticated ciphers including GCM, CCM, and GMAC. |
| @@ -1294,101 +1192,6 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1294 | self.store_context(ctx); | 1192 | self.store_context(ctx); |
| 1295 | } | 1193 | } |
| 1296 | 1194 | ||
| 1297 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1298 | /// Controls the header phase of cipher processing. | ||
| 1299 | /// This function is only valid for authenticated ciphers including GCM, CCM, and GMAC. | ||
| 1300 | /// All additional associated data (AAD) must be supplied to this function prior to starting the payload phase with `payload`. | ||
| 1301 | /// The AAD must be supplied in multiples of the block size (128-bits for AES, 64-bits for DES), except when supplying the last block. | ||
| 1302 | /// When supplying the last block of AAD, `last_aad_block` must be `true`. | ||
| 1303 | pub async fn aad<'c, const TAG_SIZE: usize, C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>>( | ||
| 1304 | &mut self, | ||
| 1305 | ctx: &mut Context<'c, C>, | ||
| 1306 | aad: &[u8], | ||
| 1307 | last_aad_block: bool, | ||
| 1308 | ) where | ||
| 1309 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 1310 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 1311 | { | ||
| 1312 | self.load_context(ctx); | ||
| 1313 | |||
| 1314 | // Perform checks for correctness. | ||
| 1315 | if ctx.aad_complete { | ||
| 1316 | panic!("Cannot update AAD after starting payload!") | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | ctx.header_len += aad.len() as u64; | ||
| 1320 | |||
| 1321 | // Header phase | ||
| 1322 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1323 | T::regs().cr().modify(|w| w.set_gcm_ccmph(1)); | ||
| 1324 | T::regs().cr().modify(|w| w.set_crypen(true)); | ||
| 1325 | |||
| 1326 | // First write the header B1 block if not yet written. | ||
| 1327 | if !ctx.header_processed { | ||
| 1328 | ctx.header_processed = true; | ||
| 1329 | let header = ctx.cipher.get_header_block(); | ||
| 1330 | ctx.aad_buffer[0..header.len()].copy_from_slice(header); | ||
| 1331 | ctx.aad_buffer_len += header.len(); | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | // Fill the header block to make a full block. | ||
| 1335 | let len_to_copy = min(aad.len(), C::BLOCK_SIZE - ctx.aad_buffer_len); | ||
| 1336 | ctx.aad_buffer[ctx.aad_buffer_len..ctx.aad_buffer_len + len_to_copy].copy_from_slice(&aad[..len_to_copy]); | ||
| 1337 | ctx.aad_buffer_len += len_to_copy; | ||
| 1338 | ctx.aad_buffer[ctx.aad_buffer_len..].fill(0); | ||
| 1339 | let mut aad_len_remaining = aad.len() - len_to_copy; | ||
| 1340 | |||
| 1341 | if ctx.aad_buffer_len < C::BLOCK_SIZE { | ||
| 1342 | // The buffer isn't full and this is the last buffer, so process it as is (already padded). | ||
| 1343 | if last_aad_block { | ||
| 1344 | Self::write_bytes(&mut self.indma, C::BLOCK_SIZE, &ctx.aad_buffer).await; | ||
| 1345 | assert_eq!(T::regs().sr().read().ifem(), true); | ||
| 1346 | |||
| 1347 | // Switch to payload phase. | ||
| 1348 | ctx.aad_complete = true; | ||
| 1349 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1350 | T::regs().cr().modify(|w| w.set_gcm_ccmph(2)); | ||
| 1351 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1352 | } else { | ||
| 1353 | // Just return because we don't yet have a full block to process. | ||
| 1354 | return; | ||
| 1355 | } | ||
| 1356 | } else { | ||
| 1357 | // Load the full block from the buffer. | ||
| 1358 | Self::write_bytes(&mut self.indma, C::BLOCK_SIZE, &ctx.aad_buffer).await; | ||
| 1359 | assert_eq!(T::regs().sr().read().ifem(), true); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | // Handle a partial block that is passed in. | ||
| 1363 | ctx.aad_buffer_len = 0; | ||
| 1364 | let leftovers = aad_len_remaining % C::BLOCK_SIZE; | ||
| 1365 | ctx.aad_buffer[..leftovers].copy_from_slice(&aad[aad.len() - leftovers..aad.len()]); | ||
| 1366 | ctx.aad_buffer_len += leftovers; | ||
| 1367 | ctx.aad_buffer[ctx.aad_buffer_len..].fill(0); | ||
| 1368 | aad_len_remaining -= leftovers; | ||
| 1369 | assert_eq!(aad_len_remaining % C::BLOCK_SIZE, 0); | ||
| 1370 | |||
| 1371 | // Load full data blocks into core. | ||
| 1372 | let num_full_blocks = aad_len_remaining / C::BLOCK_SIZE; | ||
| 1373 | let start_index = len_to_copy; | ||
| 1374 | let end_index = start_index + (C::BLOCK_SIZE * num_full_blocks); | ||
| 1375 | Self::write_bytes(&mut self.indma, C::BLOCK_SIZE, &aad[start_index..end_index]).await; | ||
| 1376 | |||
| 1377 | if last_aad_block { | ||
| 1378 | if leftovers > 0 { | ||
| 1379 | Self::write_bytes(&mut self.indma, C::BLOCK_SIZE, &ctx.aad_buffer).await; | ||
| 1380 | assert_eq!(T::regs().sr().read().ifem(), true); | ||
| 1381 | } | ||
| 1382 | // Switch to payload phase. | ||
| 1383 | ctx.aad_complete = true; | ||
| 1384 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1385 | T::regs().cr().modify(|w| w.set_gcm_ccmph(2)); | ||
| 1386 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | self.store_context(ctx); | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | /// Performs encryption/decryption on the provided context. | 1195 | /// Performs encryption/decryption on the provided context. |
| 1393 | /// The context determines algorithm, mode, and state of the crypto accelerator. | 1196 | /// The context determines algorithm, mode, and state of the crypto accelerator. |
| 1394 | /// When the last piece of data is supplied, `last_block` should be `true`. | 1197 | /// When the last piece of data is supplied, `last_block` should be `true`. |
| @@ -1478,105 +1281,6 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1478 | self.store_context(ctx); | 1281 | self.store_context(ctx); |
| 1479 | } | 1282 | } |
| 1480 | 1283 | ||
| 1481 | /// Performs encryption/decryption on the provided context. | ||
| 1482 | /// The context determines algorithm, mode, and state of the crypto accelerator. | ||
| 1483 | /// When the last piece of data is supplied, `last_block` should be `true`. | ||
| 1484 | /// This function panics under various mismatches of parameters. | ||
| 1485 | /// Output buffer must be at least as long as the input buffer. | ||
| 1486 | /// Data must be a multiple of block size (128-bits for AES, 64-bits for DES) for CBC and ECB modes. | ||
| 1487 | /// Padding or ciphertext stealing must be managed by the application for these modes. | ||
| 1488 | /// Data must also be a multiple of block size unless `last_block` is `true`. | ||
| 1489 | pub async fn payload<'c, C: Cipher<'c> + CipherSized + IVSized>( | ||
| 1490 | &mut self, | ||
| 1491 | ctx: &mut Context<'c, C>, | ||
| 1492 | input: &[u8], | ||
| 1493 | output: &mut [u8], | ||
| 1494 | last_block: bool, | ||
| 1495 | ) where | ||
| 1496 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 1497 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 1498 | { | ||
| 1499 | self.load_context(ctx); | ||
| 1500 | |||
| 1501 | let last_block_remainder = input.len() % C::BLOCK_SIZE; | ||
| 1502 | |||
| 1503 | // Perform checks for correctness. | ||
| 1504 | if !ctx.aad_complete && ctx.header_len > 0 { | ||
| 1505 | panic!("Additional associated data must be processed first!"); | ||
| 1506 | } else if !ctx.aad_complete { | ||
| 1507 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1508 | { | ||
| 1509 | ctx.aad_complete = true; | ||
| 1510 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1511 | T::regs().cr().modify(|w| w.set_gcm_ccmph(2)); | ||
| 1512 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1513 | T::regs().cr().modify(|w| w.set_crypen(true)); | ||
| 1514 | } | ||
| 1515 | } | ||
| 1516 | if ctx.last_block_processed { | ||
| 1517 | panic!("The last block has already been processed!"); | ||
| 1518 | } | ||
| 1519 | if input.len() > output.len() { | ||
| 1520 | panic!("Output buffer length must match input length."); | ||
| 1521 | } | ||
| 1522 | if !last_block { | ||
| 1523 | if last_block_remainder != 0 { | ||
| 1524 | panic!("Input length must be a multiple of {} bytes.", C::BLOCK_SIZE); | ||
| 1525 | } | ||
| 1526 | } | ||
| 1527 | if C::REQUIRES_PADDING { | ||
| 1528 | if last_block_remainder != 0 { | ||
| 1529 | panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); | ||
| 1530 | } | ||
| 1531 | } | ||
| 1532 | if last_block { | ||
| 1533 | ctx.last_block_processed = true; | ||
| 1534 | } | ||
| 1535 | |||
| 1536 | // Load data into core, block by block. | ||
| 1537 | let num_full_blocks = input.len() / C::BLOCK_SIZE; | ||
| 1538 | for block in 0..num_full_blocks { | ||
| 1539 | let index = block * C::BLOCK_SIZE; | ||
| 1540 | // Read block out | ||
| 1541 | let read = Self::read_bytes( | ||
| 1542 | &mut self.outdma, | ||
| 1543 | C::BLOCK_SIZE, | ||
| 1544 | &mut output[index..index + C::BLOCK_SIZE], | ||
| 1545 | ); | ||
| 1546 | // Write block in | ||
| 1547 | let write = Self::write_bytes(&mut self.indma, C::BLOCK_SIZE, &input[index..index + C::BLOCK_SIZE]); | ||
| 1548 | embassy_futures::join::join(read, write).await; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | // Handle the final block, which is incomplete. | ||
| 1552 | if last_block_remainder > 0 { | ||
| 1553 | let padding_len = C::BLOCK_SIZE - last_block_remainder; | ||
| 1554 | let temp1 = ctx.cipher.pre_final(T::regs(), ctx.dir, padding_len); | ||
| 1555 | |||
| 1556 | let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; | ||
| 1557 | let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; | ||
| 1558 | last_block[..last_block_remainder].copy_from_slice(&input[input.len() - last_block_remainder..input.len()]); | ||
| 1559 | let read = Self::read_bytes(&mut self.outdma, C::BLOCK_SIZE, &mut intermediate_data); | ||
| 1560 | let write = Self::write_bytes(&mut self.indma, C::BLOCK_SIZE, &last_block); | ||
| 1561 | embassy_futures::join::join(read, write).await; | ||
| 1562 | |||
| 1563 | // Handle the last block depending on mode. | ||
| 1564 | let output_len = output.len(); | ||
| 1565 | output[output_len - last_block_remainder..output_len] | ||
| 1566 | .copy_from_slice(&intermediate_data[0..last_block_remainder]); | ||
| 1567 | |||
| 1568 | let mut mask: [u8; 16] = [0; 16]; | ||
| 1569 | mask[..last_block_remainder].fill(0xFF); | ||
| 1570 | ctx.cipher | ||
| 1571 | .post_final(T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask) | ||
| 1572 | .await; | ||
| 1573 | } | ||
| 1574 | |||
| 1575 | ctx.payload_len += input.len() as u64; | ||
| 1576 | |||
| 1577 | self.store_context(ctx); | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | 1284 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] |
| 1581 | /// Generates an authentication tag for authenticated ciphers including GCM, CCM, and GMAC. | 1285 | /// Generates an authentication tag for authenticated ciphers including GCM, CCM, and GMAC. |
| 1582 | /// Called after the all data has been encrypted/decrypted by `payload`. | 1286 | /// Called after the all data has been encrypted/decrypted by `payload`. |
| @@ -1623,57 +1327,6 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1623 | tag | 1327 | tag |
| 1624 | } | 1328 | } |
| 1625 | 1329 | ||
| 1626 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1627 | // Generates an authentication tag for authenticated ciphers including GCM, CCM, and GMAC. | ||
| 1628 | /// Called after the all data has been encrypted/decrypted by `payload`. | ||
| 1629 | pub async fn finish< | ||
| 1630 | 'c, | ||
| 1631 | const TAG_SIZE: usize, | ||
| 1632 | C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>, | ||
| 1633 | >( | ||
| 1634 | &mut self, | ||
| 1635 | mut ctx: Context<'c, C>, | ||
| 1636 | ) -> [u8; TAG_SIZE] | ||
| 1637 | where | ||
| 1638 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 1639 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 1640 | { | ||
| 1641 | self.load_context(&mut ctx); | ||
| 1642 | |||
| 1643 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1644 | T::regs().cr().modify(|w| w.set_gcm_ccmph(3)); | ||
| 1645 | T::regs().cr().modify(|w| w.set_crypen(true)); | ||
| 1646 | |||
| 1647 | let headerlen1: u32 = ((ctx.header_len * 8) >> 32) as u32; | ||
| 1648 | let headerlen2: u32 = (ctx.header_len * 8) as u32; | ||
| 1649 | let payloadlen1: u32 = ((ctx.payload_len * 8) >> 32) as u32; | ||
| 1650 | let payloadlen2: u32 = (ctx.payload_len * 8) as u32; | ||
| 1651 | |||
| 1652 | #[cfg(cryp_v2)] | ||
| 1653 | let footer: [u32; 4] = [ | ||
| 1654 | headerlen1.swap_bytes(), | ||
| 1655 | headerlen2.swap_bytes(), | ||
| 1656 | payloadlen1.swap_bytes(), | ||
| 1657 | payloadlen2.swap_bytes(), | ||
| 1658 | ]; | ||
| 1659 | #[cfg(any(cryp_v3, cryp_v4))] | ||
| 1660 | let footer: [u32; 4] = [headerlen1, headerlen2, payloadlen1, payloadlen2]; | ||
| 1661 | |||
| 1662 | let write = Self::write_words(&mut self.indma, C::BLOCK_SIZE, &footer); | ||
| 1663 | |||
| 1664 | let mut full_tag: [u8; 16] = [0; 16]; | ||
| 1665 | let read = Self::read_bytes(&mut self.outdma, C::BLOCK_SIZE, &mut full_tag); | ||
| 1666 | |||
| 1667 | embassy_futures::join::join(read, write).await; | ||
| 1668 | |||
| 1669 | let mut tag: [u8; TAG_SIZE] = [0; TAG_SIZE]; | ||
| 1670 | tag.copy_from_slice(&full_tag[0..TAG_SIZE]); | ||
| 1671 | |||
| 1672 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1673 | |||
| 1674 | tag | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | fn load_key(&self, key: &[u8]) { | 1330 | fn load_key(&self, key: &[u8]) { |
| 1678 | // Load the key into the registers. | 1331 | // Load the key into the registers. |
| 1679 | let mut keyidx = 0; | 1332 | let mut keyidx = 0; |
| @@ -1774,97 +1427,435 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1774 | } | 1427 | } |
| 1775 | } | 1428 | } |
| 1776 | 1429 | ||
| 1777 | async fn write_bytes(dma: &mut PeripheralRef<'_, DmaIn>, block_size: usize, blocks: &[u8]) | 1430 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] |
| 1778 | where | 1431 | fn write_words_blocking(&self, block_size: usize, blocks: &[u32]) { |
| 1779 | DmaIn: crate::cryp::DmaIn<T>, | 1432 | assert_eq!((blocks.len() * 4) % block_size, 0); |
| 1780 | { | 1433 | let mut byte_counter: usize = 0; |
| 1434 | for word in blocks { | ||
| 1435 | T::regs().din().write_value(*word); | ||
| 1436 | byte_counter += 4; | ||
| 1437 | if byte_counter % block_size == 0 { | ||
| 1438 | // Block until input FIFO is empty. | ||
| 1439 | while !T::regs().sr().read().ifem() {} | ||
| 1440 | } | ||
| 1441 | } | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | fn read_bytes_blocking(&self, block_size: usize, blocks: &mut [u8]) { | ||
| 1445 | // Block until there is output to read. | ||
| 1446 | while !T::regs().sr().read().ofne() {} | ||
| 1447 | // Ensure input is a multiple of block size. | ||
| 1448 | assert_eq!(blocks.len() % block_size, 0); | ||
| 1449 | // Read block out | ||
| 1450 | let mut index = 0; | ||
| 1451 | let end_index = blocks.len(); | ||
| 1452 | while index < end_index { | ||
| 1453 | let out_word: u32 = T::regs().dout().read(); | ||
| 1454 | blocks[index..index + 4].copy_from_slice(u32::to_ne_bytes(out_word).as_slice()); | ||
| 1455 | index += 4; | ||
| 1456 | } | ||
| 1457 | } | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | impl<'d, T: Instance> Cryp<'d, T, Async> { | ||
| 1461 | /// Create a new CRYP driver. | ||
| 1462 | pub fn new( | ||
| 1463 | peri: Peri<'d, T>, | ||
| 1464 | indma: Peri<'d, impl DmaIn<T>>, | ||
| 1465 | outdma: Peri<'d, impl DmaOut<T>>, | ||
| 1466 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 1467 | ) -> Self { | ||
| 1468 | rcc::enable_and_reset::<T>(); | ||
| 1469 | let instance = Self { | ||
| 1470 | _peripheral: peri, | ||
| 1471 | _phantom: PhantomData, | ||
| 1472 | indma: new_dma!(indma), | ||
| 1473 | outdma: new_dma!(outdma), | ||
| 1474 | }; | ||
| 1475 | |||
| 1476 | T::Interrupt::unpend(); | ||
| 1477 | unsafe { T::Interrupt::enable() }; | ||
| 1478 | |||
| 1479 | instance | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | /// Start a new encrypt or decrypt operation for the given cipher. | ||
| 1483 | pub async fn start<'c, C: Cipher<'c> + CipherSized + IVSized>( | ||
| 1484 | &mut self, | ||
| 1485 | cipher: &'c C, | ||
| 1486 | dir: Direction, | ||
| 1487 | ) -> Context<'c, C> { | ||
| 1488 | let mut ctx: Context<'c, C> = Context { | ||
| 1489 | dir, | ||
| 1490 | last_block_processed: false, | ||
| 1491 | cr: 0, | ||
| 1492 | iv: [0; 4], | ||
| 1493 | csgcmccm: [0; 8], | ||
| 1494 | csgcm: [0; 8], | ||
| 1495 | aad_complete: false, | ||
| 1496 | header_len: 0, | ||
| 1497 | payload_len: 0, | ||
| 1498 | cipher: cipher, | ||
| 1499 | phantom_data: PhantomData, | ||
| 1500 | header_processed: false, | ||
| 1501 | aad_buffer: [0; 16], | ||
| 1502 | aad_buffer_len: 0, | ||
| 1503 | }; | ||
| 1504 | |||
| 1505 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1506 | |||
| 1507 | let key = ctx.cipher.key(); | ||
| 1508 | |||
| 1509 | if key.len() == (128 / 8) { | ||
| 1510 | T::regs().cr().modify(|w| w.set_keysize(0)); | ||
| 1511 | } else if key.len() == (192 / 8) { | ||
| 1512 | T::regs().cr().modify(|w| w.set_keysize(1)); | ||
| 1513 | } else if key.len() == (256 / 8) { | ||
| 1514 | T::regs().cr().modify(|w| w.set_keysize(2)); | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | self.load_key(key); | ||
| 1518 | |||
| 1519 | // Set data type to 8-bit. This will match software implementations. | ||
| 1520 | T::regs().cr().modify(|w| w.set_datatype(2)); | ||
| 1521 | |||
| 1522 | ctx.cipher.prepare_key(T::regs()); | ||
| 1523 | |||
| 1524 | ctx.cipher.set_algomode(T::regs()); | ||
| 1525 | |||
| 1526 | // Set encrypt/decrypt | ||
| 1527 | if dir == Direction::Encrypt { | ||
| 1528 | T::regs().cr().modify(|w| w.set_algodir(false)); | ||
| 1529 | } else { | ||
| 1530 | T::regs().cr().modify(|w| w.set_algodir(true)); | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | // Load the IV into the registers. | ||
| 1534 | let iv = ctx.cipher.iv(); | ||
| 1535 | let mut full_iv: [u8; 16] = [0; 16]; | ||
| 1536 | full_iv[0..iv.len()].copy_from_slice(iv); | ||
| 1537 | let mut iv_idx = 0; | ||
| 1538 | let mut iv_word: [u8; 4] = [0; 4]; | ||
| 1539 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1540 | iv_idx += 4; | ||
| 1541 | T::regs().init(0).ivlr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1542 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1543 | iv_idx += 4; | ||
| 1544 | T::regs().init(0).ivrr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1545 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1546 | iv_idx += 4; | ||
| 1547 | T::regs().init(1).ivlr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1548 | iv_word.copy_from_slice(&full_iv[iv_idx..iv_idx + 4]); | ||
| 1549 | T::regs().init(1).ivrr().write_value(u32::from_be_bytes(iv_word)); | ||
| 1550 | |||
| 1551 | // Flush in/out FIFOs | ||
| 1552 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1553 | |||
| 1554 | ctx.cipher.init_phase(T::regs(), self).await; | ||
| 1555 | |||
| 1556 | self.store_context(&mut ctx); | ||
| 1557 | |||
| 1558 | ctx | ||
| 1559 | } | ||
| 1560 | |||
| 1561 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1562 | /// Controls the header phase of cipher processing. | ||
| 1563 | /// This function is only valid for authenticated ciphers including GCM, CCM, and GMAC. | ||
| 1564 | /// All additional associated data (AAD) must be supplied to this function prior to starting the payload phase with `payload`. | ||
| 1565 | /// The AAD must be supplied in multiples of the block size (128-bits for AES, 64-bits for DES), except when supplying the last block. | ||
| 1566 | /// When supplying the last block of AAD, `last_aad_block` must be `true`. | ||
| 1567 | pub async fn aad< | ||
| 1568 | 'c, | ||
| 1569 | const TAG_SIZE: usize, | ||
| 1570 | C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>, | ||
| 1571 | >( | ||
| 1572 | &mut self, | ||
| 1573 | ctx: &mut Context<'c, C>, | ||
| 1574 | aad: &[u8], | ||
| 1575 | last_aad_block: bool, | ||
| 1576 | ) { | ||
| 1577 | self.load_context(ctx); | ||
| 1578 | |||
| 1579 | // Perform checks for correctness. | ||
| 1580 | if ctx.aad_complete { | ||
| 1581 | panic!("Cannot update AAD after starting payload!") | ||
| 1582 | } | ||
| 1583 | |||
| 1584 | ctx.header_len += aad.len() as u64; | ||
| 1585 | |||
| 1586 | // Header phase | ||
| 1587 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1588 | T::regs().cr().modify(|w| w.set_gcm_ccmph(1)); | ||
| 1589 | T::regs().cr().modify(|w| w.set_crypen(true)); | ||
| 1590 | |||
| 1591 | // First write the header B1 block if not yet written. | ||
| 1592 | if !ctx.header_processed { | ||
| 1593 | ctx.header_processed = true; | ||
| 1594 | let header = ctx.cipher.get_header_block(); | ||
| 1595 | ctx.aad_buffer[0..header.len()].copy_from_slice(header); | ||
| 1596 | ctx.aad_buffer_len += header.len(); | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | // Fill the header block to make a full block. | ||
| 1600 | let len_to_copy = min(aad.len(), C::BLOCK_SIZE - ctx.aad_buffer_len); | ||
| 1601 | ctx.aad_buffer[ctx.aad_buffer_len..ctx.aad_buffer_len + len_to_copy].copy_from_slice(&aad[..len_to_copy]); | ||
| 1602 | ctx.aad_buffer_len += len_to_copy; | ||
| 1603 | ctx.aad_buffer[ctx.aad_buffer_len..].fill(0); | ||
| 1604 | let mut aad_len_remaining = aad.len() - len_to_copy; | ||
| 1605 | |||
| 1606 | if ctx.aad_buffer_len < C::BLOCK_SIZE { | ||
| 1607 | // The buffer isn't full and this is the last buffer, so process it as is (already padded). | ||
| 1608 | if last_aad_block { | ||
| 1609 | Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &ctx.aad_buffer).await; | ||
| 1610 | assert_eq!(T::regs().sr().read().ifem(), true); | ||
| 1611 | |||
| 1612 | // Switch to payload phase. | ||
| 1613 | ctx.aad_complete = true; | ||
| 1614 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1615 | T::regs().cr().modify(|w| w.set_gcm_ccmph(2)); | ||
| 1616 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1617 | } else { | ||
| 1618 | // Just return because we don't yet have a full block to process. | ||
| 1619 | return; | ||
| 1620 | } | ||
| 1621 | } else { | ||
| 1622 | // Load the full block from the buffer. | ||
| 1623 | Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &ctx.aad_buffer).await; | ||
| 1624 | assert_eq!(T::regs().sr().read().ifem(), true); | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | // Handle a partial block that is passed in. | ||
| 1628 | ctx.aad_buffer_len = 0; | ||
| 1629 | let leftovers = aad_len_remaining % C::BLOCK_SIZE; | ||
| 1630 | ctx.aad_buffer[..leftovers].copy_from_slice(&aad[aad.len() - leftovers..aad.len()]); | ||
| 1631 | ctx.aad_buffer_len += leftovers; | ||
| 1632 | ctx.aad_buffer[ctx.aad_buffer_len..].fill(0); | ||
| 1633 | aad_len_remaining -= leftovers; | ||
| 1634 | assert_eq!(aad_len_remaining % C::BLOCK_SIZE, 0); | ||
| 1635 | |||
| 1636 | // Load full data blocks into core. | ||
| 1637 | let num_full_blocks = aad_len_remaining / C::BLOCK_SIZE; | ||
| 1638 | let start_index = len_to_copy; | ||
| 1639 | let end_index = start_index + (C::BLOCK_SIZE * num_full_blocks); | ||
| 1640 | Self::write_bytes( | ||
| 1641 | self.indma.as_mut().unwrap(), | ||
| 1642 | C::BLOCK_SIZE, | ||
| 1643 | &aad[start_index..end_index], | ||
| 1644 | ) | ||
| 1645 | .await; | ||
| 1646 | |||
| 1647 | if last_aad_block { | ||
| 1648 | if leftovers > 0 { | ||
| 1649 | Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &ctx.aad_buffer).await; | ||
| 1650 | assert_eq!(T::regs().sr().read().ifem(), true); | ||
| 1651 | } | ||
| 1652 | // Switch to payload phase. | ||
| 1653 | ctx.aad_complete = true; | ||
| 1654 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1655 | T::regs().cr().modify(|w| w.set_gcm_ccmph(2)); | ||
| 1656 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | self.store_context(ctx); | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | /// Performs encryption/decryption on the provided context. | ||
| 1663 | /// The context determines algorithm, mode, and state of the crypto accelerator. | ||
| 1664 | /// When the last piece of data is supplied, `last_block` should be `true`. | ||
| 1665 | /// This function panics under various mismatches of parameters. | ||
| 1666 | /// Output buffer must be at least as long as the input buffer. | ||
| 1667 | /// Data must be a multiple of block size (128-bits for AES, 64-bits for DES) for CBC and ECB modes. | ||
| 1668 | /// Padding or ciphertext stealing must be managed by the application for these modes. | ||
| 1669 | /// Data must also be a multiple of block size unless `last_block` is `true`. | ||
| 1670 | pub async fn payload<'c, C: Cipher<'c> + CipherSized + IVSized>( | ||
| 1671 | &mut self, | ||
| 1672 | ctx: &mut Context<'c, C>, | ||
| 1673 | input: &[u8], | ||
| 1674 | output: &mut [u8], | ||
| 1675 | last_block: bool, | ||
| 1676 | ) { | ||
| 1677 | self.load_context(ctx); | ||
| 1678 | |||
| 1679 | let last_block_remainder = input.len() % C::BLOCK_SIZE; | ||
| 1680 | |||
| 1681 | // Perform checks for correctness. | ||
| 1682 | if !ctx.aad_complete && ctx.header_len > 0 { | ||
| 1683 | panic!("Additional associated data must be processed first!"); | ||
| 1684 | } else if !ctx.aad_complete { | ||
| 1685 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1686 | { | ||
| 1687 | ctx.aad_complete = true; | ||
| 1688 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1689 | T::regs().cr().modify(|w| w.set_gcm_ccmph(2)); | ||
| 1690 | T::regs().cr().modify(|w| w.fflush()); | ||
| 1691 | T::regs().cr().modify(|w| w.set_crypen(true)); | ||
| 1692 | } | ||
| 1693 | } | ||
| 1694 | if ctx.last_block_processed { | ||
| 1695 | panic!("The last block has already been processed!"); | ||
| 1696 | } | ||
| 1697 | if input.len() > output.len() { | ||
| 1698 | panic!("Output buffer length must match input length."); | ||
| 1699 | } | ||
| 1700 | if !last_block { | ||
| 1701 | if last_block_remainder != 0 { | ||
| 1702 | panic!("Input length must be a multiple of {} bytes.", C::BLOCK_SIZE); | ||
| 1703 | } | ||
| 1704 | } | ||
| 1705 | if C::REQUIRES_PADDING { | ||
| 1706 | if last_block_remainder != 0 { | ||
| 1707 | panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); | ||
| 1708 | } | ||
| 1709 | } | ||
| 1710 | if last_block { | ||
| 1711 | ctx.last_block_processed = true; | ||
| 1712 | } | ||
| 1713 | |||
| 1714 | // Load data into core, block by block. | ||
| 1715 | let num_full_blocks = input.len() / C::BLOCK_SIZE; | ||
| 1716 | for block in 0..num_full_blocks { | ||
| 1717 | let index = block * C::BLOCK_SIZE; | ||
| 1718 | // Read block out | ||
| 1719 | let read = Self::read_bytes( | ||
| 1720 | self.outdma.as_mut().unwrap(), | ||
| 1721 | C::BLOCK_SIZE, | ||
| 1722 | &mut output[index..index + C::BLOCK_SIZE], | ||
| 1723 | ); | ||
| 1724 | // Write block in | ||
| 1725 | let write = Self::write_bytes( | ||
| 1726 | self.indma.as_mut().unwrap(), | ||
| 1727 | C::BLOCK_SIZE, | ||
| 1728 | &input[index..index + C::BLOCK_SIZE], | ||
| 1729 | ); | ||
| 1730 | embassy_futures::join::join(read, write).await; | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | // Handle the final block, which is incomplete. | ||
| 1734 | if last_block_remainder > 0 { | ||
| 1735 | let padding_len = C::BLOCK_SIZE - last_block_remainder; | ||
| 1736 | let temp1 = ctx.cipher.pre_final(T::regs(), ctx.dir, padding_len); | ||
| 1737 | |||
| 1738 | let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; | ||
| 1739 | let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; | ||
| 1740 | last_block[..last_block_remainder].copy_from_slice(&input[input.len() - last_block_remainder..input.len()]); | ||
| 1741 | let read = Self::read_bytes(self.outdma.as_mut().unwrap(), C::BLOCK_SIZE, &mut intermediate_data); | ||
| 1742 | let write = Self::write_bytes(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &last_block); | ||
| 1743 | embassy_futures::join::join(read, write).await; | ||
| 1744 | |||
| 1745 | // Handle the last block depending on mode. | ||
| 1746 | let output_len = output.len(); | ||
| 1747 | output[output_len - last_block_remainder..output_len] | ||
| 1748 | .copy_from_slice(&intermediate_data[0..last_block_remainder]); | ||
| 1749 | |||
| 1750 | let mut mask: [u8; 16] = [0; 16]; | ||
| 1751 | mask[..last_block_remainder].fill(0xFF); | ||
| 1752 | ctx.cipher | ||
| 1753 | .post_final(T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask) | ||
| 1754 | .await; | ||
| 1755 | } | ||
| 1756 | |||
| 1757 | ctx.payload_len += input.len() as u64; | ||
| 1758 | |||
| 1759 | self.store_context(ctx); | ||
| 1760 | } | ||
| 1761 | |||
| 1762 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1763 | // Generates an authentication tag for authenticated ciphers including GCM, CCM, and GMAC. | ||
| 1764 | /// Called after the all data has been encrypted/decrypted by `payload`. | ||
| 1765 | pub async fn finish< | ||
| 1766 | 'c, | ||
| 1767 | const TAG_SIZE: usize, | ||
| 1768 | C: Cipher<'c> + CipherSized + IVSized + CipherAuthenticated<TAG_SIZE>, | ||
| 1769 | >( | ||
| 1770 | &mut self, | ||
| 1771 | mut ctx: Context<'c, C>, | ||
| 1772 | ) -> [u8; TAG_SIZE] { | ||
| 1773 | self.load_context(&mut ctx); | ||
| 1774 | |||
| 1775 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1776 | T::regs().cr().modify(|w| w.set_gcm_ccmph(3)); | ||
| 1777 | T::regs().cr().modify(|w| w.set_crypen(true)); | ||
| 1778 | |||
| 1779 | let headerlen1: u32 = ((ctx.header_len * 8) >> 32) as u32; | ||
| 1780 | let headerlen2: u32 = (ctx.header_len * 8) as u32; | ||
| 1781 | let payloadlen1: u32 = ((ctx.payload_len * 8) >> 32) as u32; | ||
| 1782 | let payloadlen2: u32 = (ctx.payload_len * 8) as u32; | ||
| 1783 | |||
| 1784 | #[cfg(cryp_v2)] | ||
| 1785 | let footer: [u32; 4] = [ | ||
| 1786 | headerlen1.swap_bytes(), | ||
| 1787 | headerlen2.swap_bytes(), | ||
| 1788 | payloadlen1.swap_bytes(), | ||
| 1789 | payloadlen2.swap_bytes(), | ||
| 1790 | ]; | ||
| 1791 | #[cfg(any(cryp_v3, cryp_v4))] | ||
| 1792 | let footer: [u32; 4] = [headerlen1, headerlen2, payloadlen1, payloadlen2]; | ||
| 1793 | |||
| 1794 | let write = Self::write_words(self.indma.as_mut().unwrap(), C::BLOCK_SIZE, &footer); | ||
| 1795 | |||
| 1796 | let mut full_tag: [u8; 16] = [0; 16]; | ||
| 1797 | let read = Self::read_bytes(self.outdma.as_mut().unwrap(), C::BLOCK_SIZE, &mut full_tag); | ||
| 1798 | |||
| 1799 | embassy_futures::join::join(read, write).await; | ||
| 1800 | |||
| 1801 | let mut tag: [u8; TAG_SIZE] = [0; TAG_SIZE]; | ||
| 1802 | tag.copy_from_slice(&full_tag[0..TAG_SIZE]); | ||
| 1803 | |||
| 1804 | T::regs().cr().modify(|w| w.set_crypen(false)); | ||
| 1805 | |||
| 1806 | tag | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | async fn write_bytes(dma: &mut ChannelAndRequest<'d>, block_size: usize, blocks: &[u8]) { | ||
| 1781 | if blocks.len() == 0 { | 1810 | if blocks.len() == 0 { |
| 1782 | return; | 1811 | return; |
| 1783 | } | 1812 | } |
| 1784 | // Ensure input is a multiple of block size. | 1813 | // Ensure input is a multiple of block size. |
| 1785 | assert_eq!(blocks.len() % block_size, 0); | 1814 | assert_eq!(blocks.len() % block_size, 0); |
| 1786 | // Configure DMA to transfer input to crypto core. | 1815 | // Configure DMA to transfer input to crypto core. |
| 1787 | let dma_request = dma.request(); | 1816 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 1788 | let dst_ptr = T::regs().din().as_ptr(); | ||
| 1789 | let num_words = blocks.len() / 4; | 1817 | let num_words = blocks.len() / 4; |
| 1790 | let src_ptr = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); | 1818 | let src_ptr: *const [u8] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); |
| 1791 | let options = TransferOptions { | 1819 | let options = TransferOptions { |
| 1792 | #[cfg(not(gpdma))] | 1820 | #[cfg(not(gpdma))] |
| 1793 | priority: crate::dma::Priority::High, | 1821 | priority: crate::dma::Priority::High, |
| 1794 | ..Default::default() | 1822 | ..Default::default() |
| 1795 | }; | 1823 | }; |
| 1796 | let dma_transfer = unsafe { Transfer::new_write_raw(dma, dma_request, src_ptr, dst_ptr, options) }; | 1824 | let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; |
| 1797 | T::regs().dmacr().modify(|w| w.set_dien(true)); | 1825 | T::regs().dmacr().modify(|w| w.set_dien(true)); |
| 1798 | // Wait for the transfer to complete. | 1826 | // Wait for the transfer to complete. |
| 1799 | dma_transfer.await; | 1827 | dma_transfer.await; |
| 1800 | } | 1828 | } |
| 1801 | 1829 | ||
| 1802 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | 1830 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] |
| 1803 | fn write_words_blocking(&self, block_size: usize, blocks: &[u32]) { | 1831 | async fn write_words(dma: &mut ChannelAndRequest<'d>, block_size: usize, blocks: &[u32]) { |
| 1804 | assert_eq!((blocks.len() * 4) % block_size, 0); | ||
| 1805 | let mut byte_counter: usize = 0; | ||
| 1806 | for word in blocks { | ||
| 1807 | T::regs().din().write_value(*word); | ||
| 1808 | byte_counter += 4; | ||
| 1809 | if byte_counter % block_size == 0 { | ||
| 1810 | // Block until input FIFO is empty. | ||
| 1811 | while !T::regs().sr().read().ifem() {} | ||
| 1812 | } | ||
| 1813 | } | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | #[cfg(any(cryp_v2, cryp_v3, cryp_v4))] | ||
| 1817 | async fn write_words(dma: &mut PeripheralRef<'_, DmaIn>, block_size: usize, blocks: &[u32]) | ||
| 1818 | where | ||
| 1819 | DmaIn: crate::cryp::DmaIn<T>, | ||
| 1820 | { | ||
| 1821 | if blocks.len() == 0 { | 1832 | if blocks.len() == 0 { |
| 1822 | return; | 1833 | return; |
| 1823 | } | 1834 | } |
| 1824 | // Ensure input is a multiple of block size. | 1835 | // Ensure input is a multiple of block size. |
| 1825 | assert_eq!((blocks.len() * 4) % block_size, 0); | 1836 | assert_eq!((blocks.len() * 4) % block_size, 0); |
| 1826 | // Configure DMA to transfer input to crypto core. | 1837 | // Configure DMA to transfer input to crypto core. |
| 1827 | let dma_request = dma.request(); | 1838 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 1828 | let dst_ptr = T::regs().din().as_ptr(); | ||
| 1829 | let num_words = blocks.len(); | 1839 | let num_words = blocks.len(); |
| 1830 | let src_ptr = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); | 1840 | let src_ptr: *const [u32] = ptr::slice_from_raw_parts(blocks.as_ptr().cast(), num_words); |
| 1831 | let options = TransferOptions { | 1841 | let options = TransferOptions { |
| 1832 | #[cfg(not(gpdma))] | 1842 | #[cfg(not(gpdma))] |
| 1833 | priority: crate::dma::Priority::High, | 1843 | priority: crate::dma::Priority::High, |
| 1834 | ..Default::default() | 1844 | ..Default::default() |
| 1835 | }; | 1845 | }; |
| 1836 | let dma_transfer = unsafe { Transfer::new_write_raw(dma, dma_request, src_ptr, dst_ptr, options) }; | 1846 | let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr, options) }; |
| 1837 | T::regs().dmacr().modify(|w| w.set_dien(true)); | 1847 | T::regs().dmacr().modify(|w| w.set_dien(true)); |
| 1838 | // Wait for the transfer to complete. | 1848 | // Wait for the transfer to complete. |
| 1839 | dma_transfer.await; | 1849 | dma_transfer.await; |
| 1840 | } | 1850 | } |
| 1841 | 1851 | ||
| 1842 | fn read_bytes_blocking(&self, block_size: usize, blocks: &mut [u8]) { | 1852 | async fn read_bytes(dma: &mut ChannelAndRequest<'d>, block_size: usize, blocks: &mut [u8]) { |
| 1843 | // Block until there is output to read. | ||
| 1844 | while !T::regs().sr().read().ofne() {} | ||
| 1845 | // Ensure input is a multiple of block size. | ||
| 1846 | assert_eq!(blocks.len() % block_size, 0); | ||
| 1847 | // Read block out | ||
| 1848 | let mut index = 0; | ||
| 1849 | let end_index = blocks.len(); | ||
| 1850 | while index < end_index { | ||
| 1851 | let out_word: u32 = T::regs().dout().read(); | ||
| 1852 | blocks[index..index + 4].copy_from_slice(u32::to_ne_bytes(out_word).as_slice()); | ||
| 1853 | index += 4; | ||
| 1854 | } | ||
| 1855 | } | ||
| 1856 | |||
| 1857 | async fn read_bytes(dma: &mut PeripheralRef<'_, DmaOut>, block_size: usize, blocks: &mut [u8]) | ||
| 1858 | where | ||
| 1859 | DmaOut: crate::cryp::DmaOut<T>, | ||
| 1860 | { | ||
| 1861 | if blocks.len() == 0 { | 1853 | if blocks.len() == 0 { |
| 1862 | return; | 1854 | return; |
| 1863 | } | 1855 | } |
| 1864 | // Ensure input is a multiple of block size. | 1856 | // Ensure input is a multiple of block size. |
| 1865 | assert_eq!(blocks.len() % block_size, 0); | 1857 | assert_eq!(blocks.len() % block_size, 0); |
| 1866 | // Configure DMA to get output from crypto core. | 1858 | // Configure DMA to get output from crypto core. |
| 1867 | let dma_request = dma.request(); | ||
| 1868 | let src_ptr = T::regs().dout().as_ptr(); | 1859 | let src_ptr = T::regs().dout().as_ptr(); |
| 1869 | let num_words = blocks.len() / 4; | 1860 | let num_words = blocks.len() / 4; |
| 1870 | let dst_ptr = ptr::slice_from_raw_parts_mut(blocks.as_mut_ptr().cast(), num_words); | 1861 | let dst_ptr = ptr::slice_from_raw_parts_mut(blocks.as_mut_ptr().cast(), num_words); |
| @@ -1873,7 +1864,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { | |||
| 1873 | priority: crate::dma::Priority::VeryHigh, | 1864 | priority: crate::dma::Priority::VeryHigh, |
| 1874 | ..Default::default() | 1865 | ..Default::default() |
| 1875 | }; | 1866 | }; |
| 1876 | let dma_transfer = unsafe { Transfer::new_read_raw(dma, dma_request, src_ptr, dst_ptr, options) }; | 1867 | let dma_transfer = unsafe { dma.read_raw(src_ptr, dst_ptr, options) }; |
| 1877 | T::regs().dmacr().modify(|w| w.set_doen(true)); | 1868 | T::regs().dmacr().modify(|w| w.set_doen(true)); |
| 1878 | // Wait for the transfer to complete. | 1869 | // Wait for the transfer to complete. |
| 1879 | dma_transfer.await; | 1870 | dma_transfer.await; |
| @@ -1886,7 +1877,7 @@ trait SealedInstance { | |||
| 1886 | 1877 | ||
| 1887 | /// CRYP instance trait. | 1878 | /// CRYP instance trait. |
| 1888 | #[allow(private_bounds)] | 1879 | #[allow(private_bounds)] |
| 1889 | pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | 1880 | pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send { |
| 1890 | /// Interrupt for this CRYP instance. | 1881 | /// Interrupt for this CRYP instance. |
| 1891 | type Interrupt: interrupt::typelevel::Interrupt; | 1882 | type Interrupt: interrupt::typelevel::Interrupt; |
| 1892 | } | 1883 | } |
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 8bba5ded0..30046849b 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs | |||
| @@ -3,15 +3,15 @@ | |||
| 3 | 3 | ||
| 4 | use core::marker::PhantomData; | 4 | use core::marker::PhantomData; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 6 | use crate::dma::ChannelAndRequest; |
| 7 | 7 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | |
| 8 | use crate::dma::NoDma; | ||
| 9 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | 8 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 10 | use crate::pac::dac; | 9 | use crate::pac::dac; |
| 11 | use crate::rcc::{self, RccPeripheral}; | 10 | use crate::rcc::{self, RccPeripheral}; |
| 12 | use crate::{peripherals, Peripheral}; | 11 | use crate::{peripherals, Peri}; |
| 13 | 12 | ||
| 14 | mod tsel; | 13 | mod tsel; |
| 14 | use embassy_hal_internal::PeripheralType; | ||
| 15 | pub use tsel::TriggerSel; | 15 | pub use tsel::TriggerSel; |
| 16 | 16 | ||
| 17 | /// Operating mode for DAC channel | 17 | /// Operating mode for DAC channel |
| @@ -100,46 +100,34 @@ pub enum ValueArray<'a> { | |||
| 100 | /// | 100 | /// |
| 101 | /// If you want to use both channels, either together or independently, | 101 | /// If you want to use both channels, either together or independently, |
| 102 | /// create a [`Dac`] first and use it to access each channel. | 102 | /// create a [`Dac`] first and use it to access each channel. |
| 103 | pub struct DacChannel<'d, T: Instance, const N: u8, DMA = NoDma> { | 103 | pub struct DacChannel<'d, T: Instance, C: Channel, M: PeriMode> { |
| 104 | phantom: PhantomData<&'d mut T>, | 104 | phantom: PhantomData<&'d mut (T, C, M)>, |
| 105 | #[allow(unused)] | 105 | #[allow(unused)] |
| 106 | dma: PeripheralRef<'d, DMA>, | 106 | dma: Option<ChannelAndRequest<'d>>, |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | /// DAC channel 1 type alias. | 109 | /// DAC channel 1 type alias. |
| 110 | pub type DacCh1<'d, T, DMA = NoDma> = DacChannel<'d, T, 1, DMA>; | 110 | pub type DacCh1<'d, T, M> = DacChannel<'d, T, Ch1, M>; |
| 111 | /// DAC channel 2 type alias. | 111 | /// DAC channel 2 type alias. |
| 112 | pub type DacCh2<'d, T, DMA = NoDma> = DacChannel<'d, T, 2, DMA>; | 112 | pub type DacCh2<'d, T, M> = DacChannel<'d, T, Ch2, M>; |
| 113 | |||
| 114 | impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | ||
| 115 | const IDX: usize = (N - 1) as usize; | ||
| 116 | 113 | ||
| 114 | impl<'d, T: Instance, C: Channel> DacChannel<'d, T, C, Async> { | ||
| 117 | /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral. | 115 | /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral. |
| 118 | /// | 116 | /// |
| 119 | /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument. | ||
| 120 | /// | ||
| 121 | /// The channel is enabled on creation and begin to drive the output pin. | 117 | /// The channel is enabled on creation and begin to drive the output pin. |
| 122 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will | 118 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will |
| 123 | /// disable the channel; you must re-enable it with `enable()`. | 119 | /// disable the channel; you must re-enable it with `enable()`. |
| 124 | /// | 120 | /// |
| 125 | /// By default, triggering is disabled, but it can be enabled using | 121 | /// By default, triggering is disabled, but it can be enabled using |
| 126 | /// [`DacChannel::set_trigger()`]. | 122 | /// [`DacChannel::set_trigger()`]. |
| 127 | pub fn new( | 123 | pub fn new(peri: Peri<'d, T>, dma: Peri<'d, impl Dma<T, C>>, pin: Peri<'d, impl DacPin<T, C>>) -> Self { |
| 128 | _peri: impl Peripheral<P = T> + 'd, | ||
| 129 | dma: impl Peripheral<P = DMA> + 'd, | ||
| 130 | pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::Pin> + 'd, | ||
| 131 | ) -> Self { | ||
| 132 | into_ref!(dma, pin); | ||
| 133 | pin.set_as_analog(); | 124 | pin.set_as_analog(); |
| 134 | rcc::enable_and_reset::<T>(); | 125 | Self::new_inner( |
| 135 | let mut dac = Self { | 126 | peri, |
| 136 | phantom: PhantomData, | 127 | new_dma!(dma), |
| 137 | dma, | 128 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 138 | }; | 129 | Mode::NormalExternalBuffered, |
| 139 | #[cfg(any(dac_v5, dac_v6, dac_v7))] | 130 | ) |
| 140 | dac.set_hfsel(); | ||
| 141 | dac.enable(); | ||
| 142 | dac | ||
| 143 | } | 131 | } |
| 144 | 132 | ||
| 145 | /// Create a new `DacChannel` instance where the external output pin is not used, | 133 | /// Create a new `DacChannel` instance where the external output pin is not used, |
| @@ -150,13 +138,97 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 150 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the | 138 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the |
| 151 | /// channel; you must re-enable it with `enable()`. | 139 | /// channel; you must re-enable it with `enable()`. |
| 152 | /// | 140 | /// |
| 153 | /// If you're not using DMA, pass [`dma::NoDma`] for the `dma` argument. | 141 | /// By default, triggering is disabled, but it can be enabled using |
| 142 | /// [`DacChannel::set_trigger()`]. | ||
| 143 | #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] | ||
| 144 | pub fn new_internal(peri: Peri<'d, T>, dma: Peri<'d, impl Dma<T, C>>) -> Self { | ||
| 145 | Self::new_inner(peri, new_dma!(dma), Mode::NormalInternalUnbuffered) | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Write `data` to this channel via DMA. | ||
| 149 | /// | ||
| 150 | /// To prevent delays or glitches when outputing a periodic waveform, the `circular` | ||
| 151 | /// flag can be set. This configures a circular DMA transfer that continually outputs | ||
| 152 | /// `data`. Note that for performance reasons in circular mode the transfer-complete | ||
| 153 | /// interrupt is disabled. | ||
| 154 | #[cfg(not(gpdma))] | ||
| 155 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) { | ||
| 156 | // Enable DAC and DMA | ||
| 157 | T::regs().cr().modify(|w| { | ||
| 158 | w.set_en(C::IDX, true); | ||
| 159 | w.set_dmaen(C::IDX, true); | ||
| 160 | }); | ||
| 161 | |||
| 162 | let dma = self.dma.as_mut().unwrap(); | ||
| 163 | |||
| 164 | let tx_options = crate::dma::TransferOptions { | ||
| 165 | circular, | ||
| 166 | half_transfer_ir: false, | ||
| 167 | complete_transfer_ir: !circular, | ||
| 168 | ..Default::default() | ||
| 169 | }; | ||
| 170 | |||
| 171 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 172 | let tx_f = match data { | ||
| 173 | ValueArray::Bit8(buf) => unsafe { dma.write(buf, T::regs().dhr8r(C::IDX).as_ptr() as *mut u8, tx_options) }, | ||
| 174 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 175 | dma.write(buf, T::regs().dhr12l(C::IDX).as_ptr() as *mut u16, tx_options) | ||
| 176 | }, | ||
| 177 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 178 | dma.write(buf, T::regs().dhr12r(C::IDX).as_ptr() as *mut u16, tx_options) | ||
| 179 | }, | ||
| 180 | }; | ||
| 181 | |||
| 182 | tx_f.await; | ||
| 183 | |||
| 184 | T::regs().cr().modify(|w| { | ||
| 185 | w.set_en(C::IDX, false); | ||
| 186 | w.set_dmaen(C::IDX, false); | ||
| 187 | }); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | impl<'d, T: Instance, C: Channel> DacChannel<'d, T, C, Blocking> { | ||
| 192 | /// Create a new `DacChannel` instance, consuming the underlying DAC peripheral. | ||
| 193 | /// | ||
| 194 | /// The channel is enabled on creation and begin to drive the output pin. | ||
| 195 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will | ||
| 196 | /// disable the channel; you must re-enable it with `enable()`. | ||
| 197 | /// | ||
| 198 | /// By default, triggering is disabled, but it can be enabled using | ||
| 199 | /// [`DacChannel::set_trigger()`]. | ||
| 200 | pub fn new_blocking(peri: Peri<'d, T>, pin: Peri<'d, impl DacPin<T, C>>) -> Self { | ||
| 201 | pin.set_as_analog(); | ||
| 202 | Self::new_inner( | ||
| 203 | peri, | ||
| 204 | None, | ||
| 205 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||
| 206 | Mode::NormalExternalBuffered, | ||
| 207 | ) | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Create a new `DacChannel` instance where the external output pin is not used, | ||
| 211 | /// so the DAC can only be used to generate internal signals. | ||
| 212 | /// The GPIO pin is therefore available to be used for other functions. | ||
| 213 | /// | ||
| 214 | /// The channel is set to [`Mode::NormalInternalUnbuffered`] and enabled on creation. | ||
| 215 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the | ||
| 216 | /// channel; you must re-enable it with `enable()`. | ||
| 154 | /// | 217 | /// |
| 155 | /// By default, triggering is disabled, but it can be enabled using | 218 | /// By default, triggering is disabled, but it can be enabled using |
| 156 | /// [`DacChannel::set_trigger()`]. | 219 | /// [`DacChannel::set_trigger()`]. |
| 157 | #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] | 220 | #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] |
| 158 | pub fn new_internal(_peri: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = DMA> + 'd) -> Self { | 221 | pub fn new_internal_blocking(peri: Peri<'d, T>) -> Self { |
| 159 | into_ref!(dma); | 222 | Self::new_inner(peri, None, Mode::NormalInternalUnbuffered) |
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | impl<'d, T: Instance, C: Channel, M: PeriMode> DacChannel<'d, T, C, M> { | ||
| 227 | fn new_inner( | ||
| 228 | _peri: Peri<'d, T>, | ||
| 229 | dma: Option<ChannelAndRequest<'d>>, | ||
| 230 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode, | ||
| 231 | ) -> Self { | ||
| 160 | rcc::enable_and_reset::<T>(); | 232 | rcc::enable_and_reset::<T>(); |
| 161 | let mut dac = Self { | 233 | let mut dac = Self { |
| 162 | phantom: PhantomData, | 234 | phantom: PhantomData, |
| @@ -164,7 +236,8 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 164 | }; | 236 | }; |
| 165 | #[cfg(any(dac_v5, dac_v6, dac_v7))] | 237 | #[cfg(any(dac_v5, dac_v6, dac_v7))] |
| 166 | dac.set_hfsel(); | 238 | dac.set_hfsel(); |
| 167 | dac.set_mode(Mode::NormalInternalUnbuffered); | 239 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 240 | dac.set_mode(mode); | ||
| 168 | dac.enable(); | 241 | dac.enable(); |
| 169 | dac | 242 | dac |
| 170 | } | 243 | } |
| @@ -173,7 +246,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 173 | pub fn set_enable(&mut self, on: bool) { | 246 | pub fn set_enable(&mut self, on: bool) { |
| 174 | critical_section::with(|_| { | 247 | critical_section::with(|_| { |
| 175 | T::regs().cr().modify(|reg| { | 248 | T::regs().cr().modify(|reg| { |
| 176 | reg.set_en(Self::IDX, on); | 249 | reg.set_en(C::IDX, on); |
| 177 | }); | 250 | }); |
| 178 | }); | 251 | }); |
| 179 | } | 252 | } |
| @@ -194,8 +267,8 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 194 | pub fn set_trigger(&mut self, source: TriggerSel) { | 267 | pub fn set_trigger(&mut self, source: TriggerSel) { |
| 195 | critical_section::with(|_| { | 268 | critical_section::with(|_| { |
| 196 | T::regs().cr().modify(|reg| { | 269 | T::regs().cr().modify(|reg| { |
| 197 | reg.set_en(Self::IDX, false); | 270 | reg.set_en(C::IDX, false); |
| 198 | reg.set_tsel(Self::IDX, source as u8); | 271 | reg.set_tsel(C::IDX, source as u8); |
| 199 | }); | 272 | }); |
| 200 | }); | 273 | }); |
| 201 | } | 274 | } |
| @@ -204,7 +277,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 204 | pub fn set_triggering(&mut self, on: bool) { | 277 | pub fn set_triggering(&mut self, on: bool) { |
| 205 | critical_section::with(|_| { | 278 | critical_section::with(|_| { |
| 206 | T::regs().cr().modify(|reg| { | 279 | T::regs().cr().modify(|reg| { |
| 207 | reg.set_ten(Self::IDX, on); | 280 | reg.set_ten(C::IDX, on); |
| 208 | }); | 281 | }); |
| 209 | }); | 282 | }); |
| 210 | } | 283 | } |
| @@ -212,7 +285,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 212 | /// Software trigger this channel. | 285 | /// Software trigger this channel. |
| 213 | pub fn trigger(&mut self) { | 286 | pub fn trigger(&mut self) { |
| 214 | T::regs().swtrigr().write(|reg| { | 287 | T::regs().swtrigr().write(|reg| { |
| 215 | reg.set_swtrig(Self::IDX, true); | 288 | reg.set_swtrig(C::IDX, true); |
| 216 | }); | 289 | }); |
| 217 | } | 290 | } |
| 218 | 291 | ||
| @@ -223,10 +296,10 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 223 | pub fn set_mode(&mut self, mode: Mode) { | 296 | pub fn set_mode(&mut self, mode: Mode) { |
| 224 | critical_section::with(|_| { | 297 | critical_section::with(|_| { |
| 225 | T::regs().cr().modify(|reg| { | 298 | T::regs().cr().modify(|reg| { |
| 226 | reg.set_en(Self::IDX, false); | 299 | reg.set_en(C::IDX, false); |
| 227 | }); | 300 | }); |
| 228 | T::regs().mcr().modify(|reg| { | 301 | T::regs().mcr().modify(|reg| { |
| 229 | reg.set_mode(Self::IDX, mode.mode()); | 302 | reg.set_mode(C::IDX, mode.mode()); |
| 230 | }); | 303 | }); |
| 231 | }); | 304 | }); |
| 232 | } | 305 | } |
| @@ -237,15 +310,15 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 237 | /// it will be output after the next trigger. | 310 | /// it will be output after the next trigger. |
| 238 | pub fn set(&mut self, value: Value) { | 311 | pub fn set(&mut self, value: Value) { |
| 239 | match value { | 312 | match value { |
| 240 | Value::Bit8(v) => T::regs().dhr8r(Self::IDX).write(|reg| reg.set_dhr(v)), | 313 | Value::Bit8(v) => T::regs().dhr8r(C::IDX).write(|reg| reg.set_dhr(v)), |
| 241 | Value::Bit12Left(v) => T::regs().dhr12l(Self::IDX).write(|reg| reg.set_dhr(v)), | 314 | Value::Bit12Left(v) => T::regs().dhr12l(C::IDX).write(|reg| reg.set_dhr(v)), |
| 242 | Value::Bit12Right(v) => T::regs().dhr12r(Self::IDX).write(|reg| reg.set_dhr(v)), | 315 | Value::Bit12Right(v) => T::regs().dhr12r(C::IDX).write(|reg| reg.set_dhr(v)), |
| 243 | } | 316 | } |
| 244 | } | 317 | } |
| 245 | 318 | ||
| 246 | /// Read the current output value of the DAC. | 319 | /// Read the current output value of the DAC. |
| 247 | pub fn read(&self) -> u16 { | 320 | pub fn read(&self) -> u16 { |
| 248 | T::regs().dor(Self::IDX).read().dor() | 321 | T::regs().dor(C::IDX).read().dor() |
| 249 | } | 322 | } |
| 250 | 323 | ||
| 251 | /// Set HFSEL as appropriate for the current peripheral clock frequency. | 324 | /// Set HFSEL as appropriate for the current peripheral clock frequency. |
| @@ -279,82 +352,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { | |||
| 279 | } | 352 | } |
| 280 | } | 353 | } |
| 281 | 354 | ||
| 282 | macro_rules! impl_dma_methods { | 355 | impl<'d, T: Instance, C: Channel, M: PeriMode> Drop for DacChannel<'d, T, C, M> { |
| 283 | ($n:literal, $trait:ident) => { | ||
| 284 | impl<'d, T: Instance, DMA> DacChannel<'d, T, $n, DMA> | ||
| 285 | where | ||
| 286 | DMA: $trait<T>, | ||
| 287 | { | ||
| 288 | /// Write `data` to this channel via DMA. | ||
| 289 | /// | ||
| 290 | /// To prevent delays or glitches when outputing a periodic waveform, the `circular` | ||
| 291 | /// flag can be set. This configures a circular DMA transfer that continually outputs | ||
| 292 | /// `data`. Note that for performance reasons in circular mode the transfer-complete | ||
| 293 | /// interrupt is disabled. | ||
| 294 | #[cfg(not(gpdma))] | ||
| 295 | pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) { | ||
| 296 | // Enable DAC and DMA | ||
| 297 | T::regs().cr().modify(|w| { | ||
| 298 | w.set_en(Self::IDX, true); | ||
| 299 | w.set_dmaen(Self::IDX, true); | ||
| 300 | }); | ||
| 301 | |||
| 302 | let tx_request = self.dma.request(); | ||
| 303 | let dma_channel = &mut self.dma; | ||
| 304 | |||
| 305 | let tx_options = crate::dma::TransferOptions { | ||
| 306 | circular, | ||
| 307 | half_transfer_ir: false, | ||
| 308 | complete_transfer_ir: !circular, | ||
| 309 | ..Default::default() | ||
| 310 | }; | ||
| 311 | |||
| 312 | // Initiate the correct type of DMA transfer depending on what data is passed | ||
| 313 | let tx_f = match data { | ||
| 314 | ValueArray::Bit8(buf) => unsafe { | ||
| 315 | crate::dma::Transfer::new_write( | ||
| 316 | dma_channel, | ||
| 317 | tx_request, | ||
| 318 | buf, | ||
| 319 | T::regs().dhr8r(Self::IDX).as_ptr() as *mut u8, | ||
| 320 | tx_options, | ||
| 321 | ) | ||
| 322 | }, | ||
| 323 | ValueArray::Bit12Left(buf) => unsafe { | ||
| 324 | crate::dma::Transfer::new_write( | ||
| 325 | dma_channel, | ||
| 326 | tx_request, | ||
| 327 | buf, | ||
| 328 | T::regs().dhr12l(Self::IDX).as_ptr() as *mut u16, | ||
| 329 | tx_options, | ||
| 330 | ) | ||
| 331 | }, | ||
| 332 | ValueArray::Bit12Right(buf) => unsafe { | ||
| 333 | crate::dma::Transfer::new_write( | ||
| 334 | dma_channel, | ||
| 335 | tx_request, | ||
| 336 | buf, | ||
| 337 | T::regs().dhr12r(Self::IDX).as_ptr() as *mut u16, | ||
| 338 | tx_options, | ||
| 339 | ) | ||
| 340 | }, | ||
| 341 | }; | ||
| 342 | |||
| 343 | tx_f.await; | ||
| 344 | |||
| 345 | T::regs().cr().modify(|w| { | ||
| 346 | w.set_en(Self::IDX, false); | ||
| 347 | w.set_dmaen(Self::IDX, false); | ||
| 348 | }); | ||
| 349 | } | ||
| 350 | } | ||
| 351 | }; | ||
| 352 | } | ||
| 353 | |||
| 354 | impl_dma_methods!(1, DacDma1); | ||
| 355 | impl_dma_methods!(2, DacDma2); | ||
| 356 | |||
| 357 | impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> { | ||
| 358 | fn drop(&mut self) { | 356 | fn drop(&mut self) { |
| 359 | rcc::disable::<T>(); | 357 | rcc::disable::<T>(); |
| 360 | } | 358 | } |
| @@ -368,14 +366,14 @@ impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> { | |||
| 368 | /// | 366 | /// |
| 369 | /// ```ignore | 367 | /// ```ignore |
| 370 | /// // Pins may need to be changed for your specific device. | 368 | /// // Pins may need to be changed for your specific device. |
| 371 | /// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, NoDma, NoDma, p.PA4, p.PA5).split(); | 369 | /// let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new_blocking(p.DAC1, p.PA4, p.PA5).split(); |
| 372 | /// ``` | 370 | /// ``` |
| 373 | pub struct Dac<'d, T: Instance, DMACh1 = NoDma, DMACh2 = NoDma> { | 371 | pub struct Dac<'d, T: Instance, M: PeriMode> { |
| 374 | ch1: DacChannel<'d, T, 1, DMACh1>, | 372 | ch1: DacChannel<'d, T, Ch1, M>, |
| 375 | ch2: DacChannel<'d, T, 2, DMACh2>, | 373 | ch2: DacChannel<'d, T, Ch2, M>, |
| 376 | } | 374 | } |
| 377 | 375 | ||
| 378 | impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | 376 | impl<'d, T: Instance> Dac<'d, T, Async> { |
| 379 | /// Create a new `Dac` instance, consuming the underlying DAC peripheral. | 377 | /// Create a new `Dac` instance, consuming the underlying DAC peripheral. |
| 380 | /// | 378 | /// |
| 381 | /// This struct allows you to access both channels of the DAC, where available. You can either | 379 | /// This struct allows you to access both channels of the DAC, where available. You can either |
| @@ -389,37 +387,79 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | |||
| 389 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` | 387 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` |
| 390 | /// method on the underlying channels. | 388 | /// method on the underlying channels. |
| 391 | pub fn new( | 389 | pub fn new( |
| 392 | _peri: impl Peripheral<P = T> + 'd, | 390 | peri: Peri<'d, T>, |
| 393 | dma_ch1: impl Peripheral<P = DMACh1> + 'd, | 391 | dma_ch1: Peri<'d, impl Dma<T, Ch1>>, |
| 394 | dma_ch2: impl Peripheral<P = DMACh2> + 'd, | 392 | dma_ch2: Peri<'d, impl Dma<T, Ch2>>, |
| 395 | pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::Pin> + 'd, | 393 | pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>, |
| 396 | pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::Pin> + 'd, | 394 | pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>, |
| 397 | ) -> Self { | 395 | ) -> Self { |
| 398 | into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2); | ||
| 399 | pin_ch1.set_as_analog(); | 396 | pin_ch1.set_as_analog(); |
| 400 | pin_ch2.set_as_analog(); | 397 | pin_ch2.set_as_analog(); |
| 398 | Self::new_inner( | ||
| 399 | peri, | ||
| 400 | new_dma!(dma_ch1), | ||
| 401 | new_dma!(dma_ch2), | ||
| 402 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||
| 403 | Mode::NormalExternalBuffered, | ||
| 404 | ) | ||
| 405 | } | ||
| 401 | 406 | ||
| 402 | // Enable twice to increment the DAC refcount for each channel. | 407 | /// Create a new `Dac` instance where the external output pins are not used, |
| 403 | rcc::enable_and_reset::<T>(); | 408 | /// so the DAC can only be used to generate internal signals but the GPIO |
| 404 | rcc::enable_and_reset::<T>(); | 409 | /// pins remain available for other functions. |
| 405 | 410 | /// | |
| 406 | let mut ch1 = DacCh1 { | 411 | /// This struct allows you to access both channels of the DAC, where available. You can either |
| 407 | phantom: PhantomData, | 412 | /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use the two |
| 408 | dma: dma_ch1, | 413 | /// channels together. |
| 409 | }; | 414 | /// |
| 410 | #[cfg(any(dac_v5, dac_v6, dac_v7))] | 415 | /// The channels are set to [`Mode::NormalInternalUnbuffered`] and enabled on creation. |
| 411 | ch1.set_hfsel(); | 416 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will disable the |
| 412 | ch1.enable(); | 417 | /// channel; you must re-enable them with `enable()`. |
| 413 | 418 | /// | |
| 414 | let mut ch2 = DacCh2 { | 419 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` |
| 415 | phantom: PhantomData, | 420 | /// method on the underlying channels. |
| 416 | dma: dma_ch2, | 421 | #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] |
| 417 | }; | 422 | pub fn new_internal( |
| 418 | #[cfg(any(dac_v5, dac_v6, dac_v7))] | 423 | peri: Peri<'d, T>, |
| 419 | ch2.set_hfsel(); | 424 | dma_ch1: Peri<'d, impl Dma<T, Ch1>>, |
| 420 | ch2.enable(); | 425 | dma_ch2: Peri<'d, impl Dma<T, Ch2>>, |
| 426 | ) -> Self { | ||
| 427 | Self::new_inner( | ||
| 428 | peri, | ||
| 429 | new_dma!(dma_ch1), | ||
| 430 | new_dma!(dma_ch2), | ||
| 431 | Mode::NormalInternalUnbuffered, | ||
| 432 | ) | ||
| 433 | } | ||
| 434 | } | ||
| 421 | 435 | ||
| 422 | Self { ch1, ch2 } | 436 | impl<'d, T: Instance> Dac<'d, T, Blocking> { |
| 437 | /// Create a new `Dac` instance, consuming the underlying DAC peripheral. | ||
| 438 | /// | ||
| 439 | /// This struct allows you to access both channels of the DAC, where available. You can either | ||
| 440 | /// call `split()` to obtain separate `DacChannel`s, or use methods on `Dac` to use | ||
| 441 | /// the two channels together. | ||
| 442 | /// | ||
| 443 | /// The channels are enabled on creation and begin to drive their output pins. | ||
| 444 | /// Note that some methods, such as `set_trigger()` and `set_mode()`, will | ||
| 445 | /// disable the channel; you must re-enable them with `enable()`. | ||
| 446 | /// | ||
| 447 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` | ||
| 448 | /// method on the underlying channels. | ||
| 449 | pub fn new_blocking( | ||
| 450 | peri: Peri<'d, T>, | ||
| 451 | pin_ch1: Peri<'d, impl DacPin<T, Ch1> + crate::gpio::Pin>, | ||
| 452 | pin_ch2: Peri<'d, impl DacPin<T, Ch2> + crate::gpio::Pin>, | ||
| 453 | ) -> Self { | ||
| 454 | pin_ch1.set_as_analog(); | ||
| 455 | pin_ch2.set_as_analog(); | ||
| 456 | Self::new_inner( | ||
| 457 | peri, | ||
| 458 | None, | ||
| 459 | None, | ||
| 460 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] | ||
| 461 | Mode::NormalExternalBuffered, | ||
| 462 | ) | ||
| 423 | } | 463 | } |
| 424 | 464 | ||
| 425 | /// Create a new `Dac` instance where the external output pins are not used, | 465 | /// Create a new `Dac` instance where the external output pins are not used, |
| @@ -437,12 +477,18 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | |||
| 437 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` | 477 | /// By default, triggering is disabled, but it can be enabled using the `set_trigger()` |
| 438 | /// method on the underlying channels. | 478 | /// method on the underlying channels. |
| 439 | #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] | 479 | #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] |
| 440 | pub fn new_internal( | 480 | pub fn new_internal(peri: Peri<'d, T>) -> Self { |
| 441 | _peri: impl Peripheral<P = T> + 'd, | 481 | Self::new_inner(peri, None, None, Mode::NormalInternalUnbuffered) |
| 442 | dma_ch1: impl Peripheral<P = DMACh1> + 'd, | 482 | } |
| 443 | dma_ch2: impl Peripheral<P = DMACh2> + 'd, | 483 | } |
| 484 | |||
| 485 | impl<'d, T: Instance, M: PeriMode> Dac<'d, T, M> { | ||
| 486 | fn new_inner( | ||
| 487 | _peri: Peri<'d, T>, | ||
| 488 | dma_ch1: Option<ChannelAndRequest<'d>>, | ||
| 489 | dma_ch2: Option<ChannelAndRequest<'d>>, | ||
| 490 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] mode: Mode, | ||
| 444 | ) -> Self { | 491 | ) -> Self { |
| 445 | into_ref!(dma_ch1, dma_ch2); | ||
| 446 | // Enable twice to increment the DAC refcount for each channel. | 492 | // Enable twice to increment the DAC refcount for each channel. |
| 447 | rcc::enable_and_reset::<T>(); | 493 | rcc::enable_and_reset::<T>(); |
| 448 | rcc::enable_and_reset::<T>(); | 494 | rcc::enable_and_reset::<T>(); |
| @@ -453,7 +499,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | |||
| 453 | }; | 499 | }; |
| 454 | #[cfg(any(dac_v5, dac_v6, dac_v7))] | 500 | #[cfg(any(dac_v5, dac_v6, dac_v7))] |
| 455 | ch1.set_hfsel(); | 501 | ch1.set_hfsel(); |
| 456 | ch1.set_mode(Mode::NormalInternalUnbuffered); | 502 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 503 | ch1.set_mode(mode); | ||
| 457 | ch1.enable(); | 504 | ch1.enable(); |
| 458 | 505 | ||
| 459 | let mut ch2 = DacCh2 { | 506 | let mut ch2 = DacCh2 { |
| @@ -462,7 +509,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | |||
| 462 | }; | 509 | }; |
| 463 | #[cfg(any(dac_v5, dac_v6, dac_v7))] | 510 | #[cfg(any(dac_v5, dac_v6, dac_v7))] |
| 464 | ch2.set_hfsel(); | 511 | ch2.set_hfsel(); |
| 465 | ch2.set_mode(Mode::NormalInternalUnbuffered); | 512 | #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] |
| 513 | ch2.set_mode(mode); | ||
| 466 | ch2.enable(); | 514 | ch2.enable(); |
| 467 | 515 | ||
| 468 | Self { ch1, ch2 } | 516 | Self { ch1, ch2 } |
| @@ -471,17 +519,17 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { | |||
| 471 | /// Split this `Dac` into separate channels. | 519 | /// Split this `Dac` into separate channels. |
| 472 | /// | 520 | /// |
| 473 | /// You can access and move the channels around separately after splitting. | 521 | /// You can access and move the channels around separately after splitting. |
| 474 | pub fn split(self) -> (DacCh1<'d, T, DMACh1>, DacCh2<'d, T, DMACh2>) { | 522 | pub fn split(self) -> (DacCh1<'d, T, M>, DacCh2<'d, T, M>) { |
| 475 | (self.ch1, self.ch2) | 523 | (self.ch1, self.ch2) |
| 476 | } | 524 | } |
| 477 | 525 | ||
| 478 | /// Temporarily access channel 1. | 526 | /// Temporarily access channel 1. |
| 479 | pub fn ch1(&mut self) -> &mut DacCh1<'d, T, DMACh1> { | 527 | pub fn ch1(&mut self) -> &mut DacCh1<'d, T, M> { |
| 480 | &mut self.ch1 | 528 | &mut self.ch1 |
| 481 | } | 529 | } |
| 482 | 530 | ||
| 483 | /// Temporarily access channel 2. | 531 | /// Temporarily access channel 2. |
| 484 | pub fn ch2(&mut self) -> &mut DacCh2<'d, T, DMACh2> { | 532 | pub fn ch2(&mut self) -> &mut DacCh2<'d, T, M> { |
| 485 | &mut self.ch2 | 533 | &mut self.ch2 |
| 486 | } | 534 | } |
| 487 | 535 | ||
| @@ -513,12 +561,31 @@ trait SealedInstance { | |||
| 513 | 561 | ||
| 514 | /// DAC instance. | 562 | /// DAC instance. |
| 515 | #[allow(private_bounds)] | 563 | #[allow(private_bounds)] |
| 516 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | 564 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {} |
| 517 | dma_trait!(DacDma1, Instance); | 565 | |
| 518 | dma_trait!(DacDma2, Instance); | 566 | /// Channel 1 marker type. |
| 567 | pub enum Ch1 {} | ||
| 568 | /// Channel 2 marker type. | ||
| 569 | pub enum Ch2 {} | ||
| 570 | |||
| 571 | trait SealedChannel { | ||
| 572 | const IDX: usize; | ||
| 573 | } | ||
| 574 | /// DAC channel trait. | ||
| 575 | #[allow(private_bounds)] | ||
| 576 | pub trait Channel: SealedChannel {} | ||
| 577 | |||
| 578 | impl SealedChannel for Ch1 { | ||
| 579 | const IDX: usize = 0; | ||
| 580 | } | ||
| 581 | impl SealedChannel for Ch2 { | ||
| 582 | const IDX: usize = 1; | ||
| 583 | } | ||
| 584 | impl Channel for Ch1 {} | ||
| 585 | impl Channel for Ch2 {} | ||
| 519 | 586 | ||
| 520 | /// Marks a pin that can be used with the DAC | 587 | dma_trait!(Dma, Instance, Channel); |
| 521 | pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {} | 588 | pin_trait!(DacPin, Instance, Channel); |
| 522 | 589 | ||
| 523 | foreach_peripheral!( | 590 | foreach_peripheral!( |
| 524 | (dac, $inst:ident) => { | 591 | (dac, $inst:ident) => { |
| @@ -531,9 +598,3 @@ foreach_peripheral!( | |||
| 531 | impl crate::dac::Instance for peripherals::$inst {} | 598 | impl crate::dac::Instance for peripherals::$inst {} |
| 532 | }; | 599 | }; |
| 533 | ); | 600 | ); |
| 534 | |||
| 535 | macro_rules! impl_dac_pin { | ||
| 536 | ($inst:ident, $pin:ident, $ch:expr) => { | ||
| 537 | impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {} | ||
| 538 | }; | ||
| 539 | } | ||
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 4ba4e824e..d05faee21 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs | |||
| @@ -3,13 +3,13 @@ use core::future::poll_fn; | |||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 6 | use embassy_hal_internal::PeripheralType; |
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 8 | ||
| 9 | use crate::dma::Transfer; | 9 | use crate::dma::Transfer; |
| 10 | use crate::gpio::{AfType, Pull}; | 10 | use crate::gpio::{AfType, Pull}; |
| 11 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 12 | use crate::{interrupt, rcc, Peripheral}; | 12 | use crate::{interrupt, rcc, Peri}; |
| 13 | 13 | ||
| 14 | /// Interrupt handler. | 14 | /// Interrupt handler. |
| 15 | pub struct InterruptHandler<T: Instance> { | 15 | pub struct InterruptHandler<T: Instance> { |
| @@ -106,8 +106,7 @@ impl Default for Config { | |||
| 106 | 106 | ||
| 107 | macro_rules! config_pins { | 107 | macro_rules! config_pins { |
| 108 | ($($pin:ident),*) => { | 108 | ($($pin:ident),*) => { |
| 109 | into_ref!($($pin),*); | 109 | critical_section::with(|_| { |
| 110 | critical_section::with(|_| { | ||
| 111 | $( | 110 | $( |
| 112 | $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); | 111 | $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); |
| 113 | )* | 112 | )* |
| @@ -117,8 +116,8 @@ macro_rules! config_pins { | |||
| 117 | 116 | ||
| 118 | /// DCMI driver. | 117 | /// DCMI driver. |
| 119 | pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> { | 118 | pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> { |
| 120 | inner: PeripheralRef<'d, T>, | 119 | inner: Peri<'d, T>, |
| 121 | dma: PeripheralRef<'d, Dma>, | 120 | dma: Peri<'d, Dma>, |
| 122 | } | 121 | } |
| 123 | 122 | ||
| 124 | impl<'d, T, Dma> Dcmi<'d, T, Dma> | 123 | impl<'d, T, Dma> Dcmi<'d, T, Dma> |
| @@ -128,23 +127,22 @@ where | |||
| 128 | { | 127 | { |
| 129 | /// Create a new DCMI driver with 8 data bits. | 128 | /// Create a new DCMI driver with 8 data bits. |
| 130 | pub fn new_8bit( | 129 | pub fn new_8bit( |
| 131 | peri: impl Peripheral<P = T> + 'd, | 130 | peri: Peri<'d, T>, |
| 132 | dma: impl Peripheral<P = Dma> + 'd, | 131 | dma: Peri<'d, Dma>, |
| 133 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 132 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 134 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 133 | d0: Peri<'d, impl D0Pin<T>>, |
| 135 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 134 | d1: Peri<'d, impl D1Pin<T>>, |
| 136 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 135 | d2: Peri<'d, impl D2Pin<T>>, |
| 137 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 136 | d3: Peri<'d, impl D3Pin<T>>, |
| 138 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 137 | d4: Peri<'d, impl D4Pin<T>>, |
| 139 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 138 | d5: Peri<'d, impl D5Pin<T>>, |
| 140 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 139 | d6: Peri<'d, impl D6Pin<T>>, |
| 141 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 140 | d7: Peri<'d, impl D7Pin<T>>, |
| 142 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, | 141 | v_sync: Peri<'d, impl VSyncPin<T>>, |
| 143 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, | 142 | h_sync: Peri<'d, impl HSyncPin<T>>, |
| 144 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 143 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 145 | config: Config, | 144 | config: Config, |
| 146 | ) -> Self { | 145 | ) -> Self { |
| 147 | into_ref!(peri, dma); | ||
| 148 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); | 146 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); |
| 149 | config_pins!(v_sync, h_sync, pixclk); | 147 | config_pins!(v_sync, h_sync, pixclk); |
| 150 | 148 | ||
| @@ -153,25 +151,24 @@ where | |||
| 153 | 151 | ||
| 154 | /// Create a new DCMI driver with 10 data bits. | 152 | /// Create a new DCMI driver with 10 data bits. |
| 155 | pub fn new_10bit( | 153 | pub fn new_10bit( |
| 156 | peri: impl Peripheral<P = T> + 'd, | 154 | peri: Peri<'d, T>, |
| 157 | dma: impl Peripheral<P = Dma> + 'd, | 155 | dma: Peri<'d, Dma>, |
| 158 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 156 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 159 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 157 | d0: Peri<'d, impl D0Pin<T>>, |
| 160 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 158 | d1: Peri<'d, impl D1Pin<T>>, |
| 161 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 159 | d2: Peri<'d, impl D2Pin<T>>, |
| 162 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 160 | d3: Peri<'d, impl D3Pin<T>>, |
| 163 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 161 | d4: Peri<'d, impl D4Pin<T>>, |
| 164 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 162 | d5: Peri<'d, impl D5Pin<T>>, |
| 165 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 163 | d6: Peri<'d, impl D6Pin<T>>, |
| 166 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 164 | d7: Peri<'d, impl D7Pin<T>>, |
| 167 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, | 165 | d8: Peri<'d, impl D8Pin<T>>, |
| 168 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, | 166 | d9: Peri<'d, impl D9Pin<T>>, |
| 169 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, | 167 | v_sync: Peri<'d, impl VSyncPin<T>>, |
| 170 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, | 168 | h_sync: Peri<'d, impl HSyncPin<T>>, |
| 171 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 169 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 172 | config: Config, | 170 | config: Config, |
| 173 | ) -> Self { | 171 | ) -> Self { |
| 174 | into_ref!(peri, dma); | ||
| 175 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); | 172 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); |
| 176 | config_pins!(v_sync, h_sync, pixclk); | 173 | config_pins!(v_sync, h_sync, pixclk); |
| 177 | 174 | ||
| @@ -180,27 +177,26 @@ where | |||
| 180 | 177 | ||
| 181 | /// Create a new DCMI driver with 12 data bits. | 178 | /// Create a new DCMI driver with 12 data bits. |
| 182 | pub fn new_12bit( | 179 | pub fn new_12bit( |
| 183 | peri: impl Peripheral<P = T> + 'd, | 180 | peri: Peri<'d, T>, |
| 184 | dma: impl Peripheral<P = Dma> + 'd, | 181 | dma: Peri<'d, Dma>, |
| 185 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 182 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 186 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 183 | d0: Peri<'d, impl D0Pin<T>>, |
| 187 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 184 | d1: Peri<'d, impl D1Pin<T>>, |
| 188 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 185 | d2: Peri<'d, impl D2Pin<T>>, |
| 189 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 186 | d3: Peri<'d, impl D3Pin<T>>, |
| 190 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 187 | d4: Peri<'d, impl D4Pin<T>>, |
| 191 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 188 | d5: Peri<'d, impl D5Pin<T>>, |
| 192 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 189 | d6: Peri<'d, impl D6Pin<T>>, |
| 193 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 190 | d7: Peri<'d, impl D7Pin<T>>, |
| 194 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, | 191 | d8: Peri<'d, impl D8Pin<T>>, |
| 195 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, | 192 | d9: Peri<'d, impl D9Pin<T>>, |
| 196 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, | 193 | d10: Peri<'d, impl D10Pin<T>>, |
| 197 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, | 194 | d11: Peri<'d, impl D11Pin<T>>, |
| 198 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, | 195 | v_sync: Peri<'d, impl VSyncPin<T>>, |
| 199 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, | 196 | h_sync: Peri<'d, impl HSyncPin<T>>, |
| 200 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 197 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 201 | config: Config, | 198 | config: Config, |
| 202 | ) -> Self { | 199 | ) -> Self { |
| 203 | into_ref!(peri, dma); | ||
| 204 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); | 200 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); |
| 205 | config_pins!(v_sync, h_sync, pixclk); | 201 | config_pins!(v_sync, h_sync, pixclk); |
| 206 | 202 | ||
| @@ -209,29 +205,28 @@ where | |||
| 209 | 205 | ||
| 210 | /// Create a new DCMI driver with 14 data bits. | 206 | /// Create a new DCMI driver with 14 data bits. |
| 211 | pub fn new_14bit( | 207 | pub fn new_14bit( |
| 212 | peri: impl Peripheral<P = T> + 'd, | 208 | peri: Peri<'d, T>, |
| 213 | dma: impl Peripheral<P = Dma> + 'd, | 209 | dma: Peri<'d, Dma>, |
| 214 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 210 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 215 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 211 | d0: Peri<'d, impl D0Pin<T>>, |
| 216 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 212 | d1: Peri<'d, impl D1Pin<T>>, |
| 217 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 213 | d2: Peri<'d, impl D2Pin<T>>, |
| 218 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 214 | d3: Peri<'d, impl D3Pin<T>>, |
| 219 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 215 | d4: Peri<'d, impl D4Pin<T>>, |
| 220 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 216 | d5: Peri<'d, impl D5Pin<T>>, |
| 221 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 217 | d6: Peri<'d, impl D6Pin<T>>, |
| 222 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 218 | d7: Peri<'d, impl D7Pin<T>>, |
| 223 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, | 219 | d8: Peri<'d, impl D8Pin<T>>, |
| 224 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, | 220 | d9: Peri<'d, impl D9Pin<T>>, |
| 225 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, | 221 | d10: Peri<'d, impl D10Pin<T>>, |
| 226 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, | 222 | d11: Peri<'d, impl D11Pin<T>>, |
| 227 | d12: impl Peripheral<P = impl D12Pin<T>> + 'd, | 223 | d12: Peri<'d, impl D12Pin<T>>, |
| 228 | d13: impl Peripheral<P = impl D13Pin<T>> + 'd, | 224 | d13: Peri<'d, impl D13Pin<T>>, |
| 229 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, | 225 | v_sync: Peri<'d, impl VSyncPin<T>>, |
| 230 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, | 226 | h_sync: Peri<'d, impl HSyncPin<T>>, |
| 231 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 227 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 232 | config: Config, | 228 | config: Config, |
| 233 | ) -> Self { | 229 | ) -> Self { |
| 234 | into_ref!(peri, dma); | ||
| 235 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); | 230 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); |
| 236 | config_pins!(v_sync, h_sync, pixclk); | 231 | config_pins!(v_sync, h_sync, pixclk); |
| 237 | 232 | ||
| @@ -240,21 +235,20 @@ where | |||
| 240 | 235 | ||
| 241 | /// Create a new DCMI driver with 8 data bits, with embedded synchronization. | 236 | /// Create a new DCMI driver with 8 data bits, with embedded synchronization. |
| 242 | pub fn new_es_8bit( | 237 | pub fn new_es_8bit( |
| 243 | peri: impl Peripheral<P = T> + 'd, | 238 | peri: Peri<'d, T>, |
| 244 | dma: impl Peripheral<P = Dma> + 'd, | 239 | dma: Peri<'d, Dma>, |
| 245 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 240 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 246 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 241 | d0: Peri<'d, impl D0Pin<T>>, |
| 247 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 242 | d1: Peri<'d, impl D1Pin<T>>, |
| 248 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 243 | d2: Peri<'d, impl D2Pin<T>>, |
| 249 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 244 | d3: Peri<'d, impl D3Pin<T>>, |
| 250 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 245 | d4: Peri<'d, impl D4Pin<T>>, |
| 251 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 246 | d5: Peri<'d, impl D5Pin<T>>, |
| 252 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 247 | d6: Peri<'d, impl D6Pin<T>>, |
| 253 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 248 | d7: Peri<'d, impl D7Pin<T>>, |
| 254 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 249 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 255 | config: Config, | 250 | config: Config, |
| 256 | ) -> Self { | 251 | ) -> Self { |
| 257 | into_ref!(peri, dma); | ||
| 258 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); | 252 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); |
| 259 | config_pins!(pixclk); | 253 | config_pins!(pixclk); |
| 260 | 254 | ||
| @@ -263,23 +257,22 @@ where | |||
| 263 | 257 | ||
| 264 | /// Create a new DCMI driver with 10 data bits, with embedded synchronization. | 258 | /// Create a new DCMI driver with 10 data bits, with embedded synchronization. |
| 265 | pub fn new_es_10bit( | 259 | pub fn new_es_10bit( |
| 266 | peri: impl Peripheral<P = T> + 'd, | 260 | peri: Peri<'d, T>, |
| 267 | dma: impl Peripheral<P = Dma> + 'd, | 261 | dma: Peri<'d, Dma>, |
| 268 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 262 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 269 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 263 | d0: Peri<'d, impl D0Pin<T>>, |
| 270 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 264 | d1: Peri<'d, impl D1Pin<T>>, |
| 271 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 265 | d2: Peri<'d, impl D2Pin<T>>, |
| 272 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 266 | d3: Peri<'d, impl D3Pin<T>>, |
| 273 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 267 | d4: Peri<'d, impl D4Pin<T>>, |
| 274 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 268 | d5: Peri<'d, impl D5Pin<T>>, |
| 275 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 269 | d6: Peri<'d, impl D6Pin<T>>, |
| 276 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 270 | d7: Peri<'d, impl D7Pin<T>>, |
| 277 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, | 271 | d8: Peri<'d, impl D8Pin<T>>, |
| 278 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, | 272 | d9: Peri<'d, impl D9Pin<T>>, |
| 279 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 273 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 280 | config: Config, | 274 | config: Config, |
| 281 | ) -> Self { | 275 | ) -> Self { |
| 282 | into_ref!(peri, dma); | ||
| 283 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); | 276 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); |
| 284 | config_pins!(pixclk); | 277 | config_pins!(pixclk); |
| 285 | 278 | ||
| @@ -288,25 +281,24 @@ where | |||
| 288 | 281 | ||
| 289 | /// Create a new DCMI driver with 12 data bits, with embedded synchronization. | 282 | /// Create a new DCMI driver with 12 data bits, with embedded synchronization. |
| 290 | pub fn new_es_12bit( | 283 | pub fn new_es_12bit( |
| 291 | peri: impl Peripheral<P = T> + 'd, | 284 | peri: Peri<'d, T>, |
| 292 | dma: impl Peripheral<P = Dma> + 'd, | 285 | dma: Peri<'d, Dma>, |
| 293 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 286 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 294 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 287 | d0: Peri<'d, impl D0Pin<T>>, |
| 295 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 288 | d1: Peri<'d, impl D1Pin<T>>, |
| 296 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 289 | d2: Peri<'d, impl D2Pin<T>>, |
| 297 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 290 | d3: Peri<'d, impl D3Pin<T>>, |
| 298 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 291 | d4: Peri<'d, impl D4Pin<T>>, |
| 299 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 292 | d5: Peri<'d, impl D5Pin<T>>, |
| 300 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 293 | d6: Peri<'d, impl D6Pin<T>>, |
| 301 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 294 | d7: Peri<'d, impl D7Pin<T>>, |
| 302 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, | 295 | d8: Peri<'d, impl D8Pin<T>>, |
| 303 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, | 296 | d9: Peri<'d, impl D9Pin<T>>, |
| 304 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, | 297 | d10: Peri<'d, impl D10Pin<T>>, |
| 305 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, | 298 | d11: Peri<'d, impl D11Pin<T>>, |
| 306 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 299 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 307 | config: Config, | 300 | config: Config, |
| 308 | ) -> Self { | 301 | ) -> Self { |
| 309 | into_ref!(peri, dma); | ||
| 310 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); | 302 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); |
| 311 | config_pins!(pixclk); | 303 | config_pins!(pixclk); |
| 312 | 304 | ||
| @@ -315,27 +307,26 @@ where | |||
| 315 | 307 | ||
| 316 | /// Create a new DCMI driver with 14 data bits, with embedded synchronization. | 308 | /// Create a new DCMI driver with 14 data bits, with embedded synchronization. |
| 317 | pub fn new_es_14bit( | 309 | pub fn new_es_14bit( |
| 318 | peri: impl Peripheral<P = T> + 'd, | 310 | peri: Peri<'d, T>, |
| 319 | dma: impl Peripheral<P = Dma> + 'd, | 311 | dma: Peri<'d, Dma>, |
| 320 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 312 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 321 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 313 | d0: Peri<'d, impl D0Pin<T>>, |
| 322 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 314 | d1: Peri<'d, impl D1Pin<T>>, |
| 323 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 315 | d2: Peri<'d, impl D2Pin<T>>, |
| 324 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 316 | d3: Peri<'d, impl D3Pin<T>>, |
| 325 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 317 | d4: Peri<'d, impl D4Pin<T>>, |
| 326 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 318 | d5: Peri<'d, impl D5Pin<T>>, |
| 327 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 319 | d6: Peri<'d, impl D6Pin<T>>, |
| 328 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 320 | d7: Peri<'d, impl D7Pin<T>>, |
| 329 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, | 321 | d8: Peri<'d, impl D8Pin<T>>, |
| 330 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, | 322 | d9: Peri<'d, impl D9Pin<T>>, |
| 331 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, | 323 | d10: Peri<'d, impl D10Pin<T>>, |
| 332 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, | 324 | d11: Peri<'d, impl D11Pin<T>>, |
| 333 | d12: impl Peripheral<P = impl D12Pin<T>> + 'd, | 325 | d12: Peri<'d, impl D12Pin<T>>, |
| 334 | d13: impl Peripheral<P = impl D13Pin<T>> + 'd, | 326 | d13: Peri<'d, impl D13Pin<T>>, |
| 335 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, | 327 | pixclk: Peri<'d, impl PixClkPin<T>>, |
| 336 | config: Config, | 328 | config: Config, |
| 337 | ) -> Self { | 329 | ) -> Self { |
| 338 | into_ref!(peri, dma); | ||
| 339 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); | 330 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); |
| 340 | config_pins!(pixclk); | 331 | config_pins!(pixclk); |
| 341 | 332 | ||
| @@ -343,8 +334,8 @@ where | |||
| 343 | } | 334 | } |
| 344 | 335 | ||
| 345 | fn new_inner( | 336 | fn new_inner( |
| 346 | peri: PeripheralRef<'d, T>, | 337 | peri: Peri<'d, T>, |
| 347 | dma: PeripheralRef<'d, Dma>, | 338 | dma: Peri<'d, Dma>, |
| 348 | config: Config, | 339 | config: Config, |
| 349 | use_embedded_synchronization: bool, | 340 | use_embedded_synchronization: bool, |
| 350 | edm: u8, | 341 | edm: u8, |
| @@ -396,7 +387,7 @@ where | |||
| 396 | let r = self.inner.regs(); | 387 | let r = self.inner.regs(); |
| 397 | let src = r.dr().as_ptr() as *mut u32; | 388 | let src = r.dr().as_ptr() as *mut u32; |
| 398 | let request = self.dma.request(); | 389 | let request = self.dma.request(); |
| 399 | let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) }; | 390 | let dma_read = unsafe { Transfer::new_read(self.dma.reborrow(), request, src, buffer, Default::default()) }; |
| 400 | 391 | ||
| 401 | Self::clear_interrupt_flags(); | 392 | Self::clear_interrupt_flags(); |
| 402 | Self::enable_irqs(); | 393 | Self::enable_irqs(); |
| @@ -435,7 +426,7 @@ trait SealedInstance: crate::rcc::RccPeripheral { | |||
| 435 | 426 | ||
| 436 | /// DCMI instance. | 427 | /// DCMI instance. |
| 437 | #[allow(private_bounds)] | 428 | #[allow(private_bounds)] |
| 438 | pub trait Instance: SealedInstance + 'static { | 429 | pub trait Instance: SealedInstance + PeripheralType + 'static { |
| 439 | /// Interrupt for this instance. | 430 | /// Interrupt for this instance. |
| 440 | type Interrupt: interrupt::typelevel::Interrupt; | 431 | type Interrupt: interrupt::typelevel::Interrupt; |
| 441 | } | 432 | } |
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 8a6aa53a0..7dbbe7b72 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs | |||
| @@ -3,10 +3,10 @@ use core::pin::Pin; | |||
| 3 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; | 3 | use core::sync::atomic::{fence, AtomicUsize, Ordering}; |
| 4 | use core::task::{Context, Poll, Waker}; | 4 | use core::task::{Context, Poll, Waker}; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 6 | use embassy_hal_internal::Peri; |
| 7 | use embassy_sync::waitqueue::AtomicWaker; | 7 | use embassy_sync::waitqueue::AtomicWaker; |
| 8 | 8 | ||
| 9 | use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 10 | use super::word::{Word, WordSize}; | 10 | use super::word::{Word, WordSize}; |
| 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 12 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| @@ -15,6 +15,8 @@ use crate::{interrupt, pac}; | |||
| 15 | pub(crate) struct ChannelInfo { | 15 | pub(crate) struct ChannelInfo { |
| 16 | pub(crate) dma: DmaInfo, | 16 | pub(crate) dma: DmaInfo, |
| 17 | pub(crate) num: usize, | 17 | pub(crate) num: usize, |
| 18 | #[cfg(feature = "_dual-core")] | ||
| 19 | pub(crate) irq: pac::Interrupt, | ||
| 18 | #[cfg(dmamux)] | 20 | #[cfg(dmamux)] |
| 19 | pub(crate) dmamux: super::DmamuxInfo, | 21 | pub(crate) dmamux: super::DmamuxInfo, |
| 20 | } | 22 | } |
| @@ -98,7 +100,7 @@ impl From<Priority> for pac::dma::vals::Pl { | |||
| 98 | Priority::Low => pac::dma::vals::Pl::LOW, | 100 | Priority::Low => pac::dma::vals::Pl::LOW, |
| 99 | Priority::Medium => pac::dma::vals::Pl::MEDIUM, | 101 | Priority::Medium => pac::dma::vals::Pl::MEDIUM, |
| 100 | Priority::High => pac::dma::vals::Pl::HIGH, | 102 | Priority::High => pac::dma::vals::Pl::HIGH, |
| 101 | Priority::VeryHigh => pac::dma::vals::Pl::VERYHIGH, | 103 | Priority::VeryHigh => pac::dma::vals::Pl::VERY_HIGH, |
| 102 | } | 104 | } |
| 103 | } | 105 | } |
| 104 | } | 106 | } |
| @@ -110,7 +112,7 @@ impl From<Priority> for pac::bdma::vals::Pl { | |||
| 110 | Priority::Low => pac::bdma::vals::Pl::LOW, | 112 | Priority::Low => pac::bdma::vals::Pl::LOW, |
| 111 | Priority::Medium => pac::bdma::vals::Pl::MEDIUM, | 113 | Priority::Medium => pac::bdma::vals::Pl::MEDIUM, |
| 112 | Priority::High => pac::bdma::vals::Pl::HIGH, | 114 | Priority::High => pac::bdma::vals::Pl::HIGH, |
| 113 | Priority::VeryHigh => pac::bdma::vals::Pl::VERYHIGH, | 115 | Priority::VeryHigh => pac::bdma::vals::Pl::VERY_HIGH, |
| 114 | } | 116 | } |
| 115 | } | 117 | } |
| 116 | } | 118 | } |
| @@ -136,8 +138,8 @@ mod dma_only { | |||
| 136 | impl From<Dir> for vals::Dir { | 138 | impl From<Dir> for vals::Dir { |
| 137 | fn from(raw: Dir) -> Self { | 139 | fn from(raw: Dir) -> Self { |
| 138 | match raw { | 140 | match raw { |
| 139 | Dir::MemoryToPeripheral => Self::MEMORYTOPERIPHERAL, | 141 | Dir::MemoryToPeripheral => Self::MEMORY_TO_PERIPHERAL, |
| 140 | Dir::PeripheralToMemory => Self::PERIPHERALTOMEMORY, | 142 | Dir::PeripheralToMemory => Self::PERIPHERAL_TO_MEMORY, |
| 141 | } | 143 | } |
| 142 | } | 144 | } |
| 143 | } | 145 | } |
| @@ -205,7 +207,7 @@ mod dma_only { | |||
| 205 | match value { | 207 | match value { |
| 206 | FifoThreshold::Quarter => vals::Fth::QUARTER, | 208 | FifoThreshold::Quarter => vals::Fth::QUARTER, |
| 207 | FifoThreshold::Half => vals::Fth::HALF, | 209 | FifoThreshold::Half => vals::Fth::HALF, |
| 208 | FifoThreshold::ThreeQuarters => vals::Fth::THREEQUARTERS, | 210 | FifoThreshold::ThreeQuarters => vals::Fth::THREE_QUARTERS, |
| 209 | FifoThreshold::Full => vals::Fth::FULL, | 211 | FifoThreshold::Full => vals::Fth::FULL, |
| 210 | } | 212 | } |
| 211 | } | 213 | } |
| @@ -231,8 +233,8 @@ mod bdma_only { | |||
| 231 | impl From<Dir> for vals::Dir { | 233 | impl From<Dir> for vals::Dir { |
| 232 | fn from(raw: Dir) -> Self { | 234 | fn from(raw: Dir) -> Self { |
| 233 | match raw { | 235 | match raw { |
| 234 | Dir::MemoryToPeripheral => Self::FROMMEMORY, | 236 | Dir::MemoryToPeripheral => Self::FROM_MEMORY, |
| 235 | Dir::PeripheralToMemory => Self::FROMPERIPHERAL, | 237 | Dir::PeripheralToMemory => Self::FROM_PERIPHERAL, |
| 236 | } | 238 | } |
| 237 | } | 239 | } |
| 238 | } | 240 | } |
| @@ -259,10 +261,12 @@ pub(crate) unsafe fn init( | |||
| 259 | foreach_interrupt! { | 261 | foreach_interrupt! { |
| 260 | ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { | 262 | ($peri:ident, dma, $block:ident, $signal_name:ident, $irq:ident) => { |
| 261 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, dma_priority); | 263 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, dma_priority); |
| 264 | #[cfg(not(feature = "_dual-core"))] | ||
| 262 | crate::interrupt::typelevel::$irq::enable(); | 265 | crate::interrupt::typelevel::$irq::enable(); |
| 263 | }; | 266 | }; |
| 264 | ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { | 267 | ($peri:ident, bdma, $block:ident, $signal_name:ident, $irq:ident) => { |
| 265 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, bdma_priority); | 268 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, bdma_priority); |
| 269 | #[cfg(not(feature = "_dual-core"))] | ||
| 266 | crate::interrupt::typelevel::$irq::enable(); | 270 | crate::interrupt::typelevel::$irq::enable(); |
| 267 | }; | 271 | }; |
| 268 | } | 272 | } |
| @@ -295,7 +299,6 @@ impl AnyChannel { | |||
| 295 | } else { | 299 | } else { |
| 296 | return; | 300 | return; |
| 297 | } | 301 | } |
| 298 | |||
| 299 | state.waker.wake(); | 302 | state.waker.wake(); |
| 300 | } | 303 | } |
| 301 | #[cfg(bdma)] | 304 | #[cfg(bdma)] |
| @@ -337,10 +340,16 @@ impl AnyChannel { | |||
| 337 | mem_addr: *mut u32, | 340 | mem_addr: *mut u32, |
| 338 | mem_len: usize, | 341 | mem_len: usize, |
| 339 | incr_mem: bool, | 342 | incr_mem: bool, |
| 340 | data_size: WordSize, | 343 | mem_size: WordSize, |
| 344 | peripheral_size: WordSize, | ||
| 341 | options: TransferOptions, | 345 | options: TransferOptions, |
| 342 | ) { | 346 | ) { |
| 343 | let info = self.info(); | 347 | let info = self.info(); |
| 348 | #[cfg(feature = "_dual-core")] | ||
| 349 | { | ||
| 350 | use embassy_hal_internal::interrupt::InterruptExt as _; | ||
| 351 | info.irq.enable(); | ||
| 352 | } | ||
| 344 | 353 | ||
| 345 | #[cfg(dmamux)] | 354 | #[cfg(dmamux)] |
| 346 | super::dmamux::configure_dmamux(&info.dmamux, _request); | 355 | super::dmamux::configure_dmamux(&info.dmamux, _request); |
| @@ -350,11 +359,13 @@ impl AnyChannel { | |||
| 350 | match self.info().dma { | 359 | match self.info().dma { |
| 351 | #[cfg(dma)] | 360 | #[cfg(dma)] |
| 352 | DmaInfo::Dma(r) => { | 361 | DmaInfo::Dma(r) => { |
| 362 | let state: &ChannelState = &STATE[self.id as usize]; | ||
| 353 | let ch = r.st(info.num); | 363 | let ch = r.st(info.num); |
| 354 | 364 | ||
| 355 | // "Preceding reads and writes cannot be moved past subsequent writes." | 365 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 356 | fence(Ordering::SeqCst); | 366 | fence(Ordering::SeqCst); |
| 357 | 367 | ||
| 368 | state.complete_count.store(0, Ordering::Release); | ||
| 358 | self.clear_irqs(); | 369 | self.clear_irqs(); |
| 359 | 370 | ||
| 360 | ch.par().write_value(peri_addr as u32); | 371 | ch.par().write_value(peri_addr as u32); |
| @@ -372,8 +383,8 @@ impl AnyChannel { | |||
| 372 | }); | 383 | }); |
| 373 | ch.cr().write(|w| { | 384 | ch.cr().write(|w| { |
| 374 | w.set_dir(dir.into()); | 385 | w.set_dir(dir.into()); |
| 375 | w.set_msize(data_size.into()); | 386 | w.set_msize(mem_size.into()); |
| 376 | w.set_psize(data_size.into()); | 387 | w.set_psize(peripheral_size.into()); |
| 377 | w.set_pl(options.priority.into()); | 388 | w.set_pl(options.priority.into()); |
| 378 | w.set_minc(incr_mem); | 389 | w.set_minc(incr_mem); |
| 379 | w.set_pinc(false); | 390 | w.set_pinc(false); |
| @@ -406,8 +417,8 @@ impl AnyChannel { | |||
| 406 | ch.mar().write_value(mem_addr as u32); | 417 | ch.mar().write_value(mem_addr as u32); |
| 407 | ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); | 418 | ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); |
| 408 | ch.cr().write(|w| { | 419 | ch.cr().write(|w| { |
| 409 | w.set_psize(data_size.into()); | 420 | w.set_psize(peripheral_size.into()); |
| 410 | w.set_msize(data_size.into()); | 421 | w.set_msize(mem_size.into()); |
| 411 | w.set_minc(incr_mem); | 422 | w.set_minc(incr_mem); |
| 412 | w.set_dir(dir.into()); | 423 | w.set_dir(dir.into()); |
| 413 | w.set_teie(true); | 424 | w.set_teie(true); |
| @@ -484,6 +495,26 @@ impl AnyChannel { | |||
| 484 | } | 495 | } |
| 485 | } | 496 | } |
| 486 | 497 | ||
| 498 | fn request_pause(&self) { | ||
| 499 | let info = self.info(); | ||
| 500 | match self.info().dma { | ||
| 501 | #[cfg(dma)] | ||
| 502 | DmaInfo::Dma(r) => { | ||
| 503 | // Disable the channel without overwriting the existing configuration | ||
| 504 | r.st(info.num).cr().modify(|w| { | ||
| 505 | w.set_en(false); | ||
| 506 | }); | ||
| 507 | } | ||
| 508 | #[cfg(bdma)] | ||
| 509 | DmaInfo::Bdma(r) => { | ||
| 510 | // Disable the channel without overwriting the existing configuration | ||
| 511 | r.ch(info.num).cr().modify(|w| { | ||
| 512 | w.set_en(false); | ||
| 513 | }); | ||
| 514 | } | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 487 | fn is_running(&self) -> bool { | 518 | fn is_running(&self) -> bool { |
| 488 | let info = self.info(); | 519 | let info = self.info(); |
| 489 | match self.info().dma { | 520 | match self.info().dma { |
| @@ -540,13 +571,13 @@ impl AnyChannel { | |||
| 540 | /// DMA transfer. | 571 | /// DMA transfer. |
| 541 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 572 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 542 | pub struct Transfer<'a> { | 573 | pub struct Transfer<'a> { |
| 543 | channel: PeripheralRef<'a, AnyChannel>, | 574 | channel: Peri<'a, AnyChannel>, |
| 544 | } | 575 | } |
| 545 | 576 | ||
| 546 | impl<'a> Transfer<'a> { | 577 | impl<'a> Transfer<'a> { |
| 547 | /// Create a new read DMA transfer (peripheral to memory). | 578 | /// Create a new read DMA transfer (peripheral to memory). |
| 548 | pub unsafe fn new_read<W: Word>( | 579 | pub unsafe fn new_read<W: Word>( |
| 549 | channel: impl Peripheral<P = impl Channel> + 'a, | 580 | channel: Peri<'a, impl Channel>, |
| 550 | request: Request, | 581 | request: Request, |
| 551 | peri_addr: *mut W, | 582 | peri_addr: *mut W, |
| 552 | buf: &'a mut [W], | 583 | buf: &'a mut [W], |
| @@ -557,16 +588,14 @@ impl<'a> Transfer<'a> { | |||
| 557 | 588 | ||
| 558 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. | 589 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. |
| 559 | pub unsafe fn new_read_raw<W: Word>( | 590 | pub unsafe fn new_read_raw<W: Word>( |
| 560 | channel: impl Peripheral<P = impl Channel> + 'a, | 591 | channel: Peri<'a, impl Channel>, |
| 561 | request: Request, | 592 | request: Request, |
| 562 | peri_addr: *mut W, | 593 | peri_addr: *mut W, |
| 563 | buf: *mut [W], | 594 | buf: *mut [W], |
| 564 | options: TransferOptions, | 595 | options: TransferOptions, |
| 565 | ) -> Self { | 596 | ) -> Self { |
| 566 | into_ref!(channel); | ||
| 567 | |||
| 568 | Self::new_inner( | 597 | Self::new_inner( |
| 569 | channel.map_into(), | 598 | channel.into(), |
| 570 | request, | 599 | request, |
| 571 | Dir::PeripheralToMemory, | 600 | Dir::PeripheralToMemory, |
| 572 | peri_addr as *const u32, | 601 | peri_addr as *const u32, |
| @@ -574,57 +603,55 @@ impl<'a> Transfer<'a> { | |||
| 574 | buf.len(), | 603 | buf.len(), |
| 575 | true, | 604 | true, |
| 576 | W::size(), | 605 | W::size(), |
| 606 | W::size(), | ||
| 577 | options, | 607 | options, |
| 578 | ) | 608 | ) |
| 579 | } | 609 | } |
| 580 | 610 | ||
| 581 | /// Create a new write DMA transfer (memory to peripheral). | 611 | /// Create a new write DMA transfer (memory to peripheral). |
| 582 | pub unsafe fn new_write<W: Word>( | 612 | pub unsafe fn new_write<MW: Word, PW: Word>( |
| 583 | channel: impl Peripheral<P = impl Channel> + 'a, | 613 | channel: Peri<'a, impl Channel>, |
| 584 | request: Request, | 614 | request: Request, |
| 585 | buf: &'a [W], | 615 | buf: &'a [MW], |
| 586 | peri_addr: *mut W, | 616 | peri_addr: *mut PW, |
| 587 | options: TransferOptions, | 617 | options: TransferOptions, |
| 588 | ) -> Self { | 618 | ) -> Self { |
| 589 | Self::new_write_raw(channel, request, buf, peri_addr, options) | 619 | Self::new_write_raw(channel, request, buf, peri_addr, options) |
| 590 | } | 620 | } |
| 591 | 621 | ||
| 592 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. | 622 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. |
| 593 | pub unsafe fn new_write_raw<W: Word>( | 623 | pub unsafe fn new_write_raw<MW: Word, PW: Word>( |
| 594 | channel: impl Peripheral<P = impl Channel> + 'a, | 624 | channel: Peri<'a, impl Channel>, |
| 595 | request: Request, | 625 | request: Request, |
| 596 | buf: *const [W], | 626 | buf: *const [MW], |
| 597 | peri_addr: *mut W, | 627 | peri_addr: *mut PW, |
| 598 | options: TransferOptions, | 628 | options: TransferOptions, |
| 599 | ) -> Self { | 629 | ) -> Self { |
| 600 | into_ref!(channel); | ||
| 601 | |||
| 602 | Self::new_inner( | 630 | Self::new_inner( |
| 603 | channel.map_into(), | 631 | channel.into(), |
| 604 | request, | 632 | request, |
| 605 | Dir::MemoryToPeripheral, | 633 | Dir::MemoryToPeripheral, |
| 606 | peri_addr as *const u32, | 634 | peri_addr as *const u32, |
| 607 | buf as *const W as *mut u32, | 635 | buf as *const MW as *mut u32, |
| 608 | buf.len(), | 636 | buf.len(), |
| 609 | true, | 637 | true, |
| 610 | W::size(), | 638 | MW::size(), |
| 639 | PW::size(), | ||
| 611 | options, | 640 | options, |
| 612 | ) | 641 | ) |
| 613 | } | 642 | } |
| 614 | 643 | ||
| 615 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. | 644 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. |
| 616 | pub unsafe fn new_write_repeated<W: Word>( | 645 | pub unsafe fn new_write_repeated<W: Word>( |
| 617 | channel: impl Peripheral<P = impl Channel> + 'a, | 646 | channel: Peri<'a, impl Channel>, |
| 618 | request: Request, | 647 | request: Request, |
| 619 | repeated: &'a W, | 648 | repeated: &'a W, |
| 620 | count: usize, | 649 | count: usize, |
| 621 | peri_addr: *mut W, | 650 | peri_addr: *mut W, |
| 622 | options: TransferOptions, | 651 | options: TransferOptions, |
| 623 | ) -> Self { | 652 | ) -> Self { |
| 624 | into_ref!(channel); | ||
| 625 | |||
| 626 | Self::new_inner( | 653 | Self::new_inner( |
| 627 | channel.map_into(), | 654 | channel.into(), |
| 628 | request, | 655 | request, |
| 629 | Dir::MemoryToPeripheral, | 656 | Dir::MemoryToPeripheral, |
| 630 | peri_addr as *const u32, | 657 | peri_addr as *const u32, |
| @@ -632,12 +659,13 @@ impl<'a> Transfer<'a> { | |||
| 632 | count, | 659 | count, |
| 633 | false, | 660 | false, |
| 634 | W::size(), | 661 | W::size(), |
| 662 | W::size(), | ||
| 635 | options, | 663 | options, |
| 636 | ) | 664 | ) |
| 637 | } | 665 | } |
| 638 | 666 | ||
| 639 | unsafe fn new_inner( | 667 | unsafe fn new_inner( |
| 640 | channel: PeripheralRef<'a, AnyChannel>, | 668 | channel: Peri<'a, AnyChannel>, |
| 641 | _request: Request, | 669 | _request: Request, |
| 642 | dir: Dir, | 670 | dir: Dir, |
| 643 | peri_addr: *const u32, | 671 | peri_addr: *const u32, |
| @@ -645,25 +673,43 @@ impl<'a> Transfer<'a> { | |||
| 645 | mem_len: usize, | 673 | mem_len: usize, |
| 646 | incr_mem: bool, | 674 | incr_mem: bool, |
| 647 | data_size: WordSize, | 675 | data_size: WordSize, |
| 676 | peripheral_size: WordSize, | ||
| 648 | options: TransferOptions, | 677 | options: TransferOptions, |
| 649 | ) -> Self { | 678 | ) -> Self { |
| 650 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | 679 | assert!(mem_len > 0 && mem_len <= 0xFFFF); |
| 651 | 680 | ||
| 652 | channel.configure( | 681 | channel.configure( |
| 653 | _request, dir, peri_addr, mem_addr, mem_len, incr_mem, data_size, options, | 682 | _request, |
| 683 | dir, | ||
| 684 | peri_addr, | ||
| 685 | mem_addr, | ||
| 686 | mem_len, | ||
| 687 | incr_mem, | ||
| 688 | data_size, | ||
| 689 | peripheral_size, | ||
| 690 | options, | ||
| 654 | ); | 691 | ); |
| 655 | channel.start(); | 692 | channel.start(); |
| 656 | |||
| 657 | Self { channel } | 693 | Self { channel } |
| 658 | } | 694 | } |
| 659 | 695 | ||
| 660 | /// Request the transfer to stop. | 696 | /// Request the transfer to stop. |
| 697 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 698 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 661 | /// | 699 | /// |
| 662 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 700 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 663 | pub fn request_stop(&mut self) { | 701 | pub fn request_stop(&mut self) { |
| 664 | self.channel.request_stop() | 702 | self.channel.request_stop() |
| 665 | } | 703 | } |
| 666 | 704 | ||
| 705 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 706 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 707 | /// | ||
| 708 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 709 | pub fn request_pause(&mut self) { | ||
| 710 | self.channel.request_pause() | ||
| 711 | } | ||
| 712 | |||
| 667 | /// Return whether this transfer is still running. | 713 | /// Return whether this transfer is still running. |
| 668 | /// | 714 | /// |
| 669 | /// If this returns `false`, it can be because either the transfer finished, or | 715 | /// If this returns `false`, it can be because either the transfer finished, or |
| @@ -717,17 +763,13 @@ impl<'a> Future for Transfer<'a> { | |||
| 717 | 763 | ||
| 718 | // ============================== | 764 | // ============================== |
| 719 | 765 | ||
| 720 | struct DmaCtrlImpl<'a>(PeripheralRef<'a, AnyChannel>); | 766 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); |
| 721 | 767 | ||
| 722 | impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | 768 | impl<'a> DmaCtrl for DmaCtrlImpl<'a> { |
| 723 | fn get_remaining_transfers(&self) -> usize { | 769 | fn get_remaining_transfers(&self) -> usize { |
| 724 | self.0.get_remaining_transfers() as _ | 770 | self.0.get_remaining_transfers() as _ |
| 725 | } | 771 | } |
| 726 | 772 | ||
| 727 | fn get_complete_count(&self) -> usize { | ||
| 728 | STATE[self.0.id as usize].complete_count.load(Ordering::Acquire) | ||
| 729 | } | ||
| 730 | |||
| 731 | fn reset_complete_count(&mut self) -> usize { | 773 | fn reset_complete_count(&mut self) -> usize { |
| 732 | let state = &STATE[self.0.id as usize]; | 774 | let state = &STATE[self.0.id as usize]; |
| 733 | #[cfg(not(armv6m))] | 775 | #[cfg(not(armv6m))] |
| @@ -747,27 +789,27 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { | |||
| 747 | 789 | ||
| 748 | /// Ringbuffer for receiving data using DMA circular mode. | 790 | /// Ringbuffer for receiving data using DMA circular mode. |
| 749 | pub struct ReadableRingBuffer<'a, W: Word> { | 791 | pub struct ReadableRingBuffer<'a, W: Word> { |
| 750 | channel: PeripheralRef<'a, AnyChannel>, | 792 | channel: Peri<'a, AnyChannel>, |
| 751 | ringbuf: ReadableDmaRingBuffer<'a, W>, | 793 | ringbuf: ReadableDmaRingBuffer<'a, W>, |
| 752 | } | 794 | } |
| 753 | 795 | ||
| 754 | impl<'a, W: Word> ReadableRingBuffer<'a, W> { | 796 | impl<'a, W: Word> ReadableRingBuffer<'a, W> { |
| 755 | /// Create a new ring buffer. | 797 | /// Create a new ring buffer. |
| 756 | pub unsafe fn new( | 798 | pub unsafe fn new( |
| 757 | channel: impl Peripheral<P = impl Channel> + 'a, | 799 | channel: Peri<'a, impl Channel>, |
| 758 | _request: Request, | 800 | _request: Request, |
| 759 | peri_addr: *mut W, | 801 | peri_addr: *mut W, |
| 760 | buffer: &'a mut [W], | 802 | buffer: &'a mut [W], |
| 761 | mut options: TransferOptions, | 803 | mut options: TransferOptions, |
| 762 | ) -> Self { | 804 | ) -> Self { |
| 763 | into_ref!(channel); | 805 | let channel: Peri<'a, AnyChannel> = channel.into(); |
| 764 | let channel: PeripheralRef<'a, AnyChannel> = channel.map_into(); | ||
| 765 | 806 | ||
| 766 | let buffer_ptr = buffer.as_mut_ptr(); | 807 | let buffer_ptr = buffer.as_mut_ptr(); |
| 767 | let len = buffer.len(); | 808 | let len = buffer.len(); |
| 768 | let dir = Dir::PeripheralToMemory; | 809 | let dir = Dir::PeripheralToMemory; |
| 769 | let data_size = W::size(); | 810 | let data_size = W::size(); |
| 770 | 811 | ||
| 812 | options.half_transfer_ir = true; | ||
| 771 | options.complete_transfer_ir = true; | 813 | options.complete_transfer_ir = true; |
| 772 | options.circular = true; | 814 | options.circular = true; |
| 773 | 815 | ||
| @@ -779,6 +821,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 779 | len, | 821 | len, |
| 780 | true, | 822 | true, |
| 781 | data_size, | 823 | data_size, |
| 824 | data_size, | ||
| 782 | options, | 825 | options, |
| 783 | ); | 826 | ); |
| 784 | 827 | ||
| @@ -792,27 +835,27 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 792 | /// | 835 | /// |
| 793 | /// You must call this after creating it for it to work. | 836 | /// You must call this after creating it for it to work. |
| 794 | pub fn start(&mut self) { | 837 | pub fn start(&mut self) { |
| 795 | self.channel.start() | 838 | self.channel.start(); |
| 796 | } | 839 | } |
| 797 | 840 | ||
| 798 | /// Clear all data in the ring buffer. | 841 | /// Clear all data in the ring buffer. |
| 799 | pub fn clear(&mut self) { | 842 | pub fn clear(&mut self) { |
| 800 | self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); | 843 | self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow())); |
| 801 | } | 844 | } |
| 802 | 845 | ||
| 803 | /// Read elements from the ring buffer | 846 | /// Read elements from the ring buffer |
| 804 | /// Return a tuple of the length read and the length remaining in the buffer | 847 | /// Return a tuple of the length read and the length remaining in the buffer |
| 805 | /// If not all of the elements were read, then there will be some elements in the buffer remaining | 848 | /// If not all of the elements were read, then there will be some elements in the buffer remaining |
| 806 | /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read | 849 | /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read |
| 807 | /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. | 850 | /// Error is returned if the portion to be read was overwritten by the DMA controller. |
| 808 | pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { | 851 | pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), Error> { |
| 809 | self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) | 852 | self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) |
| 810 | } | 853 | } |
| 811 | 854 | ||
| 812 | /// Read an exact number of elements from the ringbuffer. | 855 | /// Read an exact number of elements from the ringbuffer. |
| 813 | /// | 856 | /// |
| 814 | /// Returns the remaining number of elements available for immediate reading. | 857 | /// Returns the remaining number of elements available for immediate reading. |
| 815 | /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. | 858 | /// Error is returned if the portion to be read was overwritten by the DMA controller. |
| 816 | /// | 859 | /// |
| 817 | /// Async/Wake Behavior: | 860 | /// Async/Wake Behavior: |
| 818 | /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, | 861 | /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, |
| @@ -820,12 +863,17 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 820 | /// ring buffer was created with a buffer of size 'N': | 863 | /// ring buffer was created with a buffer of size 'N': |
| 821 | /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. | 864 | /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. |
| 822 | /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. | 865 | /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. |
| 823 | pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> { | 866 | pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, Error> { |
| 824 | self.ringbuf | 867 | self.ringbuf |
| 825 | .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) | 868 | .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) |
| 826 | .await | 869 | .await |
| 827 | } | 870 | } |
| 828 | 871 | ||
| 872 | /// The current length of the ringbuffer | ||
| 873 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 874 | Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) | ||
| 875 | } | ||
| 876 | |||
| 829 | /// The capacity of the ringbuffer | 877 | /// The capacity of the ringbuffer |
| 830 | pub const fn capacity(&self) -> usize { | 878 | pub const fn capacity(&self) -> usize { |
| 831 | self.ringbuf.cap() | 879 | self.ringbuf.cap() |
| @@ -836,13 +884,23 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { | |||
| 836 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | 884 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); |
| 837 | } | 885 | } |
| 838 | 886 | ||
| 839 | /// Request DMA to stop. | 887 | /// Request the DMA to stop. |
| 888 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 889 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 840 | /// | 890 | /// |
| 841 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 891 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 842 | pub fn request_stop(&mut self) { | 892 | pub fn request_stop(&mut self) { |
| 843 | self.channel.request_stop() | 893 | self.channel.request_stop() |
| 844 | } | 894 | } |
| 845 | 895 | ||
| 896 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 897 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 898 | /// | ||
| 899 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 900 | pub fn request_pause(&mut self) { | ||
| 901 | self.channel.request_pause() | ||
| 902 | } | ||
| 903 | |||
| 846 | /// Return whether DMA is still running. | 904 | /// Return whether DMA is still running. |
| 847 | /// | 905 | /// |
| 848 | /// If this returns `false`, it can be because either the transfer finished, or | 906 | /// If this returns `false`, it can be because either the transfer finished, or |
| @@ -883,21 +941,20 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { | |||
| 883 | 941 | ||
| 884 | /// Ringbuffer for writing data using DMA circular mode. | 942 | /// Ringbuffer for writing data using DMA circular mode. |
| 885 | pub struct WritableRingBuffer<'a, W: Word> { | 943 | pub struct WritableRingBuffer<'a, W: Word> { |
| 886 | channel: PeripheralRef<'a, AnyChannel>, | 944 | channel: Peri<'a, AnyChannel>, |
| 887 | ringbuf: WritableDmaRingBuffer<'a, W>, | 945 | ringbuf: WritableDmaRingBuffer<'a, W>, |
| 888 | } | 946 | } |
| 889 | 947 | ||
| 890 | impl<'a, W: Word> WritableRingBuffer<'a, W> { | 948 | impl<'a, W: Word> WritableRingBuffer<'a, W> { |
| 891 | /// Create a new ring buffer. | 949 | /// Create a new ring buffer. |
| 892 | pub unsafe fn new( | 950 | pub unsafe fn new( |
| 893 | channel: impl Peripheral<P = impl Channel> + 'a, | 951 | channel: Peri<'a, impl Channel>, |
| 894 | _request: Request, | 952 | _request: Request, |
| 895 | peri_addr: *mut W, | 953 | peri_addr: *mut W, |
| 896 | buffer: &'a mut [W], | 954 | buffer: &'a mut [W], |
| 897 | mut options: TransferOptions, | 955 | mut options: TransferOptions, |
| 898 | ) -> Self { | 956 | ) -> Self { |
| 899 | into_ref!(channel); | 957 | let channel: Peri<'a, AnyChannel> = channel.into(); |
| 900 | let channel: PeripheralRef<'a, AnyChannel> = channel.map_into(); | ||
| 901 | 958 | ||
| 902 | let len = buffer.len(); | 959 | let len = buffer.len(); |
| 903 | let dir = Dir::MemoryToPeripheral; | 960 | let dir = Dir::MemoryToPeripheral; |
| @@ -916,6 +973,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 916 | len, | 973 | len, |
| 917 | true, | 974 | true, |
| 918 | data_size, | 975 | data_size, |
| 976 | data_size, | ||
| 919 | options, | 977 | options, |
| 920 | ); | 978 | ); |
| 921 | 979 | ||
| @@ -929,34 +987,45 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 929 | /// | 987 | /// |
| 930 | /// You must call this after creating it for it to work. | 988 | /// You must call this after creating it for it to work. |
| 931 | pub fn start(&mut self) { | 989 | pub fn start(&mut self) { |
| 932 | self.channel.start() | 990 | self.channel.start(); |
| 933 | } | 991 | } |
| 934 | 992 | ||
| 935 | /// Clear all data in the ring buffer. | 993 | /// Clear all data in the ring buffer. |
| 936 | pub fn clear(&mut self) { | 994 | pub fn clear(&mut self) { |
| 937 | self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); | 995 | self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow())); |
| 938 | } | 996 | } |
| 939 | 997 | ||
| 940 | /// Write elements directly to the raw buffer. | 998 | /// Write elements directly to the raw buffer. |
| 941 | /// This can be used to fill the buffer before starting the DMA transfer. | 999 | /// This can be used to fill the buffer before starting the DMA transfer. |
| 942 | #[allow(dead_code)] | 1000 | pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { |
| 943 | pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { | ||
| 944 | self.ringbuf.write_immediate(buf) | 1001 | self.ringbuf.write_immediate(buf) |
| 945 | } | 1002 | } |
| 946 | 1003 | ||
| 947 | /// Write elements from the ring buffer | 1004 | /// Write elements from the ring buffer |
| 948 | /// Return a tuple of the length written and the length remaining in the buffer | 1005 | /// Return a tuple of the length written and the length remaining in the buffer |
| 949 | pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { | 1006 | pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { |
| 950 | self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) | 1007 | self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) |
| 951 | } | 1008 | } |
| 952 | 1009 | ||
| 953 | /// Write an exact number of elements to the ringbuffer. | 1010 | /// Write an exact number of elements to the ringbuffer. |
| 954 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, OverrunError> { | 1011 | pub async fn write_exact(&mut self, buffer: &[W]) -> Result<usize, Error> { |
| 955 | self.ringbuf | 1012 | self.ringbuf |
| 956 | .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) | 1013 | .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) |
| 957 | .await | 1014 | .await |
| 958 | } | 1015 | } |
| 959 | 1016 | ||
| 1017 | /// Wait for any ring buffer write error. | ||
| 1018 | pub async fn wait_write_error(&mut self) -> Result<usize, Error> { | ||
| 1019 | self.ringbuf | ||
| 1020 | .wait_write_error(&mut DmaCtrlImpl(self.channel.reborrow())) | ||
| 1021 | .await | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | /// The current length of the ringbuffer | ||
| 1025 | pub fn len(&mut self) -> Result<usize, Error> { | ||
| 1026 | Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) | ||
| 1027 | } | ||
| 1028 | |||
| 960 | /// The capacity of the ringbuffer | 1029 | /// The capacity of the ringbuffer |
| 961 | pub const fn capacity(&self) -> usize { | 1030 | pub const fn capacity(&self) -> usize { |
| 962 | self.ringbuf.cap() | 1031 | self.ringbuf.cap() |
| @@ -967,13 +1036,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { | |||
| 967 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); | 1036 | DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); |
| 968 | } | 1037 | } |
| 969 | 1038 | ||
| 970 | /// Request DMA to stop. | 1039 | /// Request the DMA to stop. |
| 1040 | /// The configuration for this channel will **not be preserved**. If you need to restart the transfer | ||
| 1041 | /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. | ||
| 971 | /// | 1042 | /// |
| 972 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | 1043 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 973 | pub fn request_stop(&mut self) { | 1044 | pub fn request_stop(&mut self) { |
| 974 | self.channel.request_stop() | 1045 | self.channel.request_stop() |
| 975 | } | 1046 | } |
| 976 | 1047 | ||
| 1048 | /// Request the transfer to pause, keeping the existing configuration for this channel. | ||
| 1049 | /// To restart the transfer, call [`start`](Self::start) again. | ||
| 1050 | /// | ||
| 1051 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. | ||
| 1052 | pub fn request_pause(&mut self) { | ||
| 1053 | self.channel.request_pause() | ||
| 1054 | } | ||
| 1055 | |||
| 977 | /// Return whether DMA is still running. | 1056 | /// Return whether DMA is still running. |
| 978 | /// | 1057 | /// |
| 979 | /// If this returns `false`, it can be because either the transfer finished, or | 1058 | /// If this returns `false`, it can be because either the transfer finished, or |
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 13d5d15be..ade70fb55 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs | |||
| @@ -5,7 +5,7 @@ use core::pin::Pin; | |||
| 5 | use core::sync::atomic::{fence, Ordering}; | 5 | use core::sync::atomic::{fence, Ordering}; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 8 | use embassy_hal_internal::Peri; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 10 | ||
| 11 | use super::word::{Word, WordSize}; | 11 | use super::word::{Word, WordSize}; |
| @@ -18,6 +18,8 @@ use crate::pac::gpdma::vals; | |||
| 18 | pub(crate) struct ChannelInfo { | 18 | pub(crate) struct ChannelInfo { |
| 19 | pub(crate) dma: pac::gpdma::Gpdma, | 19 | pub(crate) dma: pac::gpdma::Gpdma, |
| 20 | pub(crate) num: usize, | 20 | pub(crate) num: usize, |
| 21 | #[cfg(feature = "_dual-core")] | ||
| 22 | pub(crate) irq: pac::Interrupt, | ||
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | /// GPDMA transfer options. | 25 | /// GPDMA transfer options. |
| @@ -36,7 +38,7 @@ impl From<WordSize> for vals::Dw { | |||
| 36 | fn from(raw: WordSize) -> Self { | 38 | fn from(raw: WordSize) -> Self { |
| 37 | match raw { | 39 | match raw { |
| 38 | WordSize::OneByte => Self::BYTE, | 40 | WordSize::OneByte => Self::BYTE, |
| 39 | WordSize::TwoBytes => Self::HALFWORD, | 41 | WordSize::TwoBytes => Self::HALF_WORD, |
| 40 | WordSize::FourBytes => Self::WORD, | 42 | WordSize::FourBytes => Self::WORD, |
| 41 | } | 43 | } |
| 42 | } | 44 | } |
| @@ -57,6 +59,7 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: P | |||
| 57 | foreach_interrupt! { | 59 | foreach_interrupt! { |
| 58 | ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { | 60 | ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { |
| 59 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority); | 61 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority); |
| 62 | #[cfg(not(feature = "_dual-core"))] | ||
| 60 | crate::interrupt::typelevel::$irq::enable(); | 63 | crate::interrupt::typelevel::$irq::enable(); |
| 61 | }; | 64 | }; |
| 62 | } | 65 | } |
| @@ -67,6 +70,12 @@ impl AnyChannel { | |||
| 67 | /// Safety: Must be called with a matching set of parameters for a valid dma channel | 70 | /// Safety: Must be called with a matching set of parameters for a valid dma channel |
| 68 | pub(crate) unsafe fn on_irq(&self) { | 71 | pub(crate) unsafe fn on_irq(&self) { |
| 69 | let info = self.info(); | 72 | let info = self.info(); |
| 73 | #[cfg(feature = "_dual-core")] | ||
| 74 | { | ||
| 75 | use embassy_hal_internal::interrupt::InterruptExt as _; | ||
| 76 | info.irq.enable(); | ||
| 77 | } | ||
| 78 | |||
| 70 | let state = &STATE[self.id as usize]; | 79 | let state = &STATE[self.id as usize]; |
| 71 | 80 | ||
| 72 | let ch = info.dma.ch(info.num); | 81 | let ch = info.dma.ch(info.num); |
| @@ -100,13 +109,13 @@ impl AnyChannel { | |||
| 100 | /// DMA transfer. | 109 | /// DMA transfer. |
| 101 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 110 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 102 | pub struct Transfer<'a> { | 111 | pub struct Transfer<'a> { |
| 103 | channel: PeripheralRef<'a, AnyChannel>, | 112 | channel: Peri<'a, AnyChannel>, |
| 104 | } | 113 | } |
| 105 | 114 | ||
| 106 | impl<'a> Transfer<'a> { | 115 | impl<'a> Transfer<'a> { |
| 107 | /// Create a new read DMA transfer (peripheral to memory). | 116 | /// Create a new read DMA transfer (peripheral to memory). |
| 108 | pub unsafe fn new_read<W: Word>( | 117 | pub unsafe fn new_read<W: Word>( |
| 109 | channel: impl Peripheral<P = impl Channel> + 'a, | 118 | channel: Peri<'a, impl Channel>, |
| 110 | request: Request, | 119 | request: Request, |
| 111 | peri_addr: *mut W, | 120 | peri_addr: *mut W, |
| 112 | buf: &'a mut [W], | 121 | buf: &'a mut [W], |
| @@ -117,16 +126,14 @@ impl<'a> Transfer<'a> { | |||
| 117 | 126 | ||
| 118 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. | 127 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. |
| 119 | pub unsafe fn new_read_raw<W: Word>( | 128 | pub unsafe fn new_read_raw<W: Word>( |
| 120 | channel: impl Peripheral<P = impl Channel> + 'a, | 129 | channel: Peri<'a, impl Channel>, |
| 121 | request: Request, | 130 | request: Request, |
| 122 | peri_addr: *mut W, | 131 | peri_addr: *mut W, |
| 123 | buf: *mut [W], | 132 | buf: *mut [W], |
| 124 | options: TransferOptions, | 133 | options: TransferOptions, |
| 125 | ) -> Self { | 134 | ) -> Self { |
| 126 | into_ref!(channel); | ||
| 127 | |||
| 128 | Self::new_inner( | 135 | Self::new_inner( |
| 129 | channel.map_into(), | 136 | channel.into(), |
| 130 | request, | 137 | request, |
| 131 | Dir::PeripheralToMemory, | 138 | Dir::PeripheralToMemory, |
| 132 | peri_addr as *const u32, | 139 | peri_addr as *const u32, |
| @@ -134,70 +141,69 @@ impl<'a> Transfer<'a> { | |||
| 134 | buf.len(), | 141 | buf.len(), |
| 135 | true, | 142 | true, |
| 136 | W::size(), | 143 | W::size(), |
| 144 | W::size(), | ||
| 137 | options, | 145 | options, |
| 138 | ) | 146 | ) |
| 139 | } | 147 | } |
| 140 | 148 | ||
| 141 | /// Create a new write DMA transfer (memory to peripheral). | 149 | /// Create a new write DMA transfer (memory to peripheral). |
| 142 | pub unsafe fn new_write<W: Word>( | 150 | pub unsafe fn new_write<MW: Word, PW: Word>( |
| 143 | channel: impl Peripheral<P = impl Channel> + 'a, | 151 | channel: Peri<'a, impl Channel>, |
| 144 | request: Request, | 152 | request: Request, |
| 145 | buf: &'a [W], | 153 | buf: &'a [MW], |
| 146 | peri_addr: *mut W, | 154 | peri_addr: *mut PW, |
| 147 | options: TransferOptions, | 155 | options: TransferOptions, |
| 148 | ) -> Self { | 156 | ) -> Self { |
| 149 | Self::new_write_raw(channel, request, buf, peri_addr, options) | 157 | Self::new_write_raw(channel, request, buf, peri_addr, options) |
| 150 | } | 158 | } |
| 151 | 159 | ||
| 152 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. | 160 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. |
| 153 | pub unsafe fn new_write_raw<W: Word>( | 161 | pub unsafe fn new_write_raw<MW: Word, PW: Word>( |
| 154 | channel: impl Peripheral<P = impl Channel> + 'a, | 162 | channel: Peri<'a, impl Channel>, |
| 155 | request: Request, | 163 | request: Request, |
| 156 | buf: *const [W], | 164 | buf: *const [MW], |
| 157 | peri_addr: *mut W, | 165 | peri_addr: *mut PW, |
| 158 | options: TransferOptions, | 166 | options: TransferOptions, |
| 159 | ) -> Self { | 167 | ) -> Self { |
| 160 | into_ref!(channel); | ||
| 161 | |||
| 162 | Self::new_inner( | 168 | Self::new_inner( |
| 163 | channel.map_into(), | 169 | channel.into(), |
| 164 | request, | 170 | request, |
| 165 | Dir::MemoryToPeripheral, | 171 | Dir::MemoryToPeripheral, |
| 166 | peri_addr as *const u32, | 172 | peri_addr as *const u32, |
| 167 | buf as *const W as *mut u32, | 173 | buf as *const MW as *mut u32, |
| 168 | buf.len(), | 174 | buf.len(), |
| 169 | true, | 175 | true, |
| 170 | W::size(), | 176 | MW::size(), |
| 177 | PW::size(), | ||
| 171 | options, | 178 | options, |
| 172 | ) | 179 | ) |
| 173 | } | 180 | } |
| 174 | 181 | ||
| 175 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. | 182 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. |
| 176 | pub unsafe fn new_write_repeated<W: Word>( | 183 | pub unsafe fn new_write_repeated<MW: Word, PW: Word>( |
| 177 | channel: impl Peripheral<P = impl Channel> + 'a, | 184 | channel: Peri<'a, impl Channel>, |
| 178 | request: Request, | 185 | request: Request, |
| 179 | repeated: &'a W, | 186 | repeated: &'a MW, |
| 180 | count: usize, | 187 | count: usize, |
| 181 | peri_addr: *mut W, | 188 | peri_addr: *mut PW, |
| 182 | options: TransferOptions, | 189 | options: TransferOptions, |
| 183 | ) -> Self { | 190 | ) -> Self { |
| 184 | into_ref!(channel); | ||
| 185 | |||
| 186 | Self::new_inner( | 191 | Self::new_inner( |
| 187 | channel.map_into(), | 192 | channel.into(), |
| 188 | request, | 193 | request, |
| 189 | Dir::MemoryToPeripheral, | 194 | Dir::MemoryToPeripheral, |
| 190 | peri_addr as *const u32, | 195 | peri_addr as *const u32, |
| 191 | repeated as *const W as *mut u32, | 196 | repeated as *const MW as *mut u32, |
| 192 | count, | 197 | count, |
| 193 | false, | 198 | false, |
| 194 | W::size(), | 199 | MW::size(), |
| 200 | PW::size(), | ||
| 195 | options, | 201 | options, |
| 196 | ) | 202 | ) |
| 197 | } | 203 | } |
| 198 | 204 | ||
| 199 | unsafe fn new_inner( | 205 | unsafe fn new_inner( |
| 200 | channel: PeripheralRef<'a, AnyChannel>, | 206 | channel: Peri<'a, AnyChannel>, |
| 201 | request: Request, | 207 | request: Request, |
| 202 | dir: Dir, | 208 | dir: Dir, |
| 203 | peri_addr: *const u32, | 209 | peri_addr: *const u32, |
| @@ -205,9 +211,13 @@ impl<'a> Transfer<'a> { | |||
| 205 | mem_len: usize, | 211 | mem_len: usize, |
| 206 | incr_mem: bool, | 212 | incr_mem: bool, |
| 207 | data_size: WordSize, | 213 | data_size: WordSize, |
| 214 | dst_size: WordSize, | ||
| 208 | _options: TransferOptions, | 215 | _options: TransferOptions, |
| 209 | ) -> Self { | 216 | ) -> Self { |
| 210 | assert!(mem_len > 0 && mem_len <= 0xFFFF); | 217 | // BNDT is specified as bytes, not as number of transfers. |
| 218 | let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { | ||
| 219 | panic!("DMA transfers may not be larger than 65535 bytes."); | ||
| 220 | }; | ||
| 211 | 221 | ||
| 212 | let info = channel.info(); | 222 | let info = channel.info(); |
| 213 | let ch = info.dma.ch(info.num); | 223 | let ch = info.dma.ch(info.num); |
| @@ -217,29 +227,24 @@ impl<'a> Transfer<'a> { | |||
| 217 | 227 | ||
| 218 | let this = Self { channel }; | 228 | let this = Self { channel }; |
| 219 | 229 | ||
| 220 | #[cfg(dmamux)] | ||
| 221 | super::dmamux::configure_dmamux(&*this.channel, request); | ||
| 222 | |||
| 223 | ch.cr().write(|w| w.set_reset(true)); | 230 | ch.cr().write(|w| w.set_reset(true)); |
| 224 | ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs | 231 | ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs |
| 225 | ch.llr().write(|_| {}); // no linked list | 232 | ch.llr().write(|_| {}); // no linked list |
| 226 | ch.tr1().write(|w| { | 233 | ch.tr1().write(|w| { |
| 227 | w.set_sdw(data_size.into()); | 234 | w.set_sdw(data_size.into()); |
| 228 | w.set_ddw(data_size.into()); | 235 | w.set_ddw(dst_size.into()); |
| 229 | w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); | 236 | w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); |
| 230 | w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); | 237 | w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); |
| 231 | }); | 238 | }); |
| 232 | ch.tr2().write(|w| { | 239 | ch.tr2().write(|w| { |
| 233 | w.set_dreq(match dir { | 240 | w.set_dreq(match dir { |
| 234 | Dir::MemoryToPeripheral => vals::Dreq::DESTINATIONPERIPHERAL, | 241 | Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL, |
| 235 | Dir::PeripheralToMemory => vals::Dreq::SOURCEPERIPHERAL, | 242 | Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL, |
| 236 | }); | 243 | }); |
| 237 | w.set_reqsel(request); | 244 | w.set_reqsel(request); |
| 238 | }); | 245 | }); |
| 239 | ch.br1().write(|w| { | 246 | ch.tr3().write(|_| {}); // no address offsets. |
| 240 | // BNDT is specified as bytes, not as number of transfers. | 247 | ch.br1().write(|w| w.set_bndt(bndt)); |
| 241 | w.set_bndt((mem_len * data_size.bytes()) as u16) | ||
| 242 | }); | ||
| 243 | 248 | ||
| 244 | match dir { | 249 | match dir { |
| 245 | Dir::MemoryToPeripheral => { | 250 | Dir::MemoryToPeripheral => { |
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 66c4aa53c..d3b070a6d 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs | |||
| @@ -22,7 +22,7 @@ pub(crate) use util::*; | |||
| 22 | pub(crate) mod ringbuffer; | 22 | pub(crate) mod ringbuffer; |
| 23 | pub mod word; | 23 | pub mod word; |
| 24 | 24 | ||
| 25 | use embassy_hal_internal::{impl_peripheral, Peripheral}; | 25 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; |
| 26 | 26 | ||
| 27 | use crate::interrupt; | 27 | use crate::interrupt; |
| 28 | 28 | ||
| @@ -51,17 +51,7 @@ pub(crate) trait ChannelInterrupt { | |||
| 51 | 51 | ||
| 52 | /// DMA channel. | 52 | /// DMA channel. |
| 53 | #[allow(private_bounds)] | 53 | #[allow(private_bounds)] |
| 54 | pub trait Channel: SealedChannel + Peripheral<P = Self> + Into<AnyChannel> + 'static { | 54 | pub trait Channel: SealedChannel + PeripheralType + Into<AnyChannel> + 'static {} |
| 55 | /// Type-erase (degrade) this pin into an `AnyChannel`. | ||
| 56 | /// | ||
| 57 | /// This converts DMA channel singletons (`DMA1_CH3`, `DMA2_CH1`, ...), which | ||
| 58 | /// are all different types, into the same type. It is useful for | ||
| 59 | /// creating arrays of channels, or avoiding generics. | ||
| 60 | #[inline] | ||
| 61 | fn degrade(self) -> AnyChannel { | ||
| 62 | AnyChannel { id: self.id() } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | 55 | ||
| 66 | macro_rules! dma_channel_impl { | 56 | macro_rules! dma_channel_impl { |
| 67 | ($channel_peri:ident, $index:expr) => { | 57 | ($channel_peri:ident, $index:expr) => { |
| @@ -79,8 +69,10 @@ macro_rules! dma_channel_impl { | |||
| 79 | impl crate::dma::Channel for crate::peripherals::$channel_peri {} | 69 | impl crate::dma::Channel for crate::peripherals::$channel_peri {} |
| 80 | 70 | ||
| 81 | impl From<crate::peripherals::$channel_peri> for crate::dma::AnyChannel { | 71 | impl From<crate::peripherals::$channel_peri> for crate::dma::AnyChannel { |
| 82 | fn from(x: crate::peripherals::$channel_peri) -> Self { | 72 | fn from(val: crate::peripherals::$channel_peri) -> Self { |
| 83 | crate::dma::Channel::degrade(x) | 73 | Self { |
| 74 | id: crate::dma::SealedChannel::id(&val), | ||
| 75 | } | ||
| 84 | } | 76 | } |
| 85 | } | 77 | } |
| 86 | }; | 78 | }; |
| @@ -108,17 +100,6 @@ impl Channel for AnyChannel {} | |||
| 108 | const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS.len(); | 100 | const CHANNEL_COUNT: usize = crate::_generated::DMA_CHANNELS.len(); |
| 109 | static STATE: [ChannelState; CHANNEL_COUNT] = [ChannelState::NEW; CHANNEL_COUNT]; | 101 | static STATE: [ChannelState; CHANNEL_COUNT] = [ChannelState::NEW; CHANNEL_COUNT]; |
| 110 | 102 | ||
| 111 | /// "No DMA" placeholder. | ||
| 112 | /// | ||
| 113 | /// You may pass this in place of a real DMA channel when creating a driver | ||
| 114 | /// to indicate it should not use DMA. | ||
| 115 | /// | ||
| 116 | /// This often causes async functionality to not be available on the instance, | ||
| 117 | /// leaving only blocking functionality. | ||
| 118 | pub struct NoDma; | ||
| 119 | |||
| 120 | impl_peripheral!(NoDma); | ||
| 121 | |||
| 122 | // safety: must be called only once at startup | 103 | // safety: must be called only once at startup |
| 123 | pub(crate) unsafe fn init( | 104 | pub(crate) unsafe fn init( |
| 124 | cs: critical_section::CriticalSection, | 105 | cs: critical_section::CriticalSection, |
diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs deleted file mode 100644 index 23f1d67d5..000000000 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ /dev/null | |||
| @@ -1,668 +0,0 @@ | |||
| 1 | #![cfg_attr(gpdma, allow(unused))] | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::ops::Range; | ||
| 5 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 6 | use core::task::{Poll, Waker}; | ||
| 7 | |||
| 8 | use super::word::Word; | ||
| 9 | |||
| 10 | /// A "read-only" ring-buffer to be used together with the DMA controller which | ||
| 11 | /// writes in a circular way, "uncontrolled" to the buffer. | ||
| 12 | /// | ||
| 13 | /// A snapshot of the ring buffer state can be attained by setting the `ndtr` field | ||
| 14 | /// to the current register value. `ndtr` describes the current position of the DMA | ||
| 15 | /// write. | ||
| 16 | /// | ||
| 17 | /// # Buffer layout | ||
| 18 | /// | ||
| 19 | /// ```text | ||
| 20 | /// Without wraparound: With wraparound: | ||
| 21 | /// | ||
| 22 | /// + buf +--- NDTR ---+ + buf +---------- NDTR ----------+ | ||
| 23 | /// | | | | | | | ||
| 24 | /// v v v v v v | ||
| 25 | /// +-----------------------------------------+ +-----------------------------------------+ | ||
| 26 | /// |oooooooooooXXXXXXXXXXXXXXXXoooooooooooooo| |XXXXXXXXXXXXXooooooooooooXXXXXXXXXXXXXXXX| | ||
| 27 | /// +-----------------------------------------+ +-----------------------------------------+ | ||
| 28 | /// ^ ^ ^ ^ ^ ^ | ||
| 29 | /// | | | | | | | ||
| 30 | /// +- start --+ | +- end ------+ | | ||
| 31 | /// | | | | | ||
| 32 | /// +- end --------------------+ +- start ----------------+ | ||
| 33 | /// ``` | ||
| 34 | pub struct ReadableDmaRingBuffer<'a, W: Word> { | ||
| 35 | pub(crate) dma_buf: &'a mut [W], | ||
| 36 | start: usize, | ||
| 37 | } | ||
| 38 | |||
| 39 | #[derive(Debug, PartialEq)] | ||
| 40 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 41 | pub struct OverrunError; | ||
| 42 | |||
| 43 | pub trait DmaCtrl { | ||
| 44 | /// Get the NDTR register value, i.e. the space left in the underlying | ||
| 45 | /// buffer until the dma writer wraps. | ||
| 46 | fn get_remaining_transfers(&self) -> usize; | ||
| 47 | |||
| 48 | /// Get the transfer completed counter. | ||
| 49 | /// This counter is incremented by the dma controller when NDTR is reloaded, | ||
| 50 | /// i.e. when the writing wraps. | ||
| 51 | fn get_complete_count(&self) -> usize; | ||
| 52 | |||
| 53 | /// Reset the transfer completed counter to 0 and return the value just prior to the reset. | ||
| 54 | fn reset_complete_count(&mut self) -> usize; | ||
| 55 | |||
| 56 | /// Set the waker for a running poll_fn | ||
| 57 | fn set_waker(&mut self, waker: &Waker); | ||
| 58 | } | ||
| 59 | |||
| 60 | impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { | ||
| 61 | pub fn new(dma_buf: &'a mut [W]) -> Self { | ||
| 62 | Self { dma_buf, start: 0 } | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Reset the ring buffer to its initial state | ||
| 66 | pub fn clear(&mut self, dma: &mut impl DmaCtrl) { | ||
| 67 | self.start = 0; | ||
| 68 | dma.reset_complete_count(); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// The capacity of the ringbuffer | ||
| 72 | pub const fn cap(&self) -> usize { | ||
| 73 | self.dma_buf.len() | ||
| 74 | } | ||
| 75 | |||
| 76 | /// The current position of the ringbuffer | ||
| 77 | fn pos(&self, dma: &mut impl DmaCtrl) -> usize { | ||
| 78 | self.cap() - dma.get_remaining_transfers() | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Read an exact number of elements from the ringbuffer. | ||
| 82 | /// | ||
| 83 | /// Returns the remaining number of elements available for immediate reading. | ||
| 84 | /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. | ||
| 85 | /// | ||
| 86 | /// Async/Wake Behavior: | ||
| 87 | /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, | ||
| 88 | /// and when it wraps around. This means that when called with a buffer of length 'M', when this | ||
| 89 | /// ring buffer was created with a buffer of size 'N': | ||
| 90 | /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. | ||
| 91 | /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. | ||
| 92 | pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, OverrunError> { | ||
| 93 | let mut read_data = 0; | ||
| 94 | let buffer_len = buffer.len(); | ||
| 95 | |||
| 96 | poll_fn(|cx| { | ||
| 97 | dma.set_waker(cx.waker()); | ||
| 98 | |||
| 99 | compiler_fence(Ordering::SeqCst); | ||
| 100 | |||
| 101 | match self.read(dma, &mut buffer[read_data..buffer_len]) { | ||
| 102 | Ok((len, remaining)) => { | ||
| 103 | read_data += len; | ||
| 104 | if read_data == buffer_len { | ||
| 105 | Poll::Ready(Ok(remaining)) | ||
| 106 | } else { | ||
| 107 | Poll::Pending | ||
| 108 | } | ||
| 109 | } | ||
| 110 | Err(e) => Poll::Ready(Err(e)), | ||
| 111 | } | ||
| 112 | }) | ||
| 113 | .await | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Read elements from the ring buffer | ||
| 117 | /// Return a tuple of the length read and the length remaining in the buffer | ||
| 118 | /// If not all of the elements were read, then there will be some elements in the buffer remaining | ||
| 119 | /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read | ||
| 120 | /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. | ||
| 121 | pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { | ||
| 122 | /* | ||
| 123 | This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check | ||
| 124 | after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed | ||
| 125 | to fire in the same clock cycle that a register is read, so checking get_complete_count early does | ||
| 126 | not yield relevant information. | ||
| 127 | |||
| 128 | Therefore, the only variable we really need to know is ndtr. If the dma has overrun by more than a full | ||
| 129 | buffer, we will do a bit more work than we have to, but algorithms should not be optimized for error | ||
| 130 | conditions. | ||
| 131 | |||
| 132 | After we've done our work, we confirm that we haven't overrun more than a full buffer, and also that | ||
| 133 | the dma has not overrun within the data we could have copied. We check the data we could have copied | ||
| 134 | rather than the data we actually copied because it costs nothing and confirms an error condition | ||
| 135 | earlier. | ||
| 136 | */ | ||
| 137 | let end = self.pos(dma); | ||
| 138 | if self.start == end && dma.get_complete_count() == 0 { | ||
| 139 | // No elements are available in the buffer | ||
| 140 | Ok((0, self.cap())) | ||
| 141 | } else if self.start < end { | ||
| 142 | // The available, unread portion in the ring buffer DOES NOT wrap | ||
| 143 | // Copy out the elements from the dma buffer | ||
| 144 | let len = self.copy_to(buf, self.start..end); | ||
| 145 | |||
| 146 | compiler_fence(Ordering::SeqCst); | ||
| 147 | |||
| 148 | /* | ||
| 149 | first, check if the dma has wrapped at all if it's after end | ||
| 150 | or more than once if it's before start | ||
| 151 | |||
| 152 | this is in a critical section to try to reduce mushy behavior. | ||
| 153 | it's not ideal but it's the best we can do | ||
| 154 | |||
| 155 | then, get the current position of of the dma write and check | ||
| 156 | if it's inside data we could have copied | ||
| 157 | */ | ||
| 158 | let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); | ||
| 159 | if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { | ||
| 160 | Err(OverrunError) | ||
| 161 | } else { | ||
| 162 | self.start = (self.start + len) % self.cap(); | ||
| 163 | |||
| 164 | Ok((len, self.cap() - self.start)) | ||
| 165 | } | ||
| 166 | } else if self.start + buf.len() < self.cap() { | ||
| 167 | // The available, unread portion in the ring buffer DOES wrap | ||
| 168 | // The DMA writer has wrapped since we last read and is currently | ||
| 169 | // writing (or the next byte added will be) in the beginning of the ring buffer. | ||
| 170 | |||
| 171 | // The provided read buffer is not large enough to include all elements from the tail of the dma buffer. | ||
| 172 | |||
| 173 | // Copy out from the dma buffer | ||
| 174 | let len = self.copy_to(buf, self.start..self.cap()); | ||
| 175 | |||
| 176 | compiler_fence(Ordering::SeqCst); | ||
| 177 | |||
| 178 | /* | ||
| 179 | first, check if the dma has wrapped around more than once | ||
| 180 | |||
| 181 | then, get the current position of of the dma write and check | ||
| 182 | if it's inside data we could have copied | ||
| 183 | */ | ||
| 184 | let pos = self.pos(dma); | ||
| 185 | if pos > self.start || pos < end || dma.get_complete_count() > 1 { | ||
| 186 | Err(OverrunError) | ||
| 187 | } else { | ||
| 188 | self.start = (self.start + len) % self.cap(); | ||
| 189 | |||
| 190 | Ok((len, self.start + end)) | ||
| 191 | } | ||
| 192 | } else { | ||
| 193 | // The available, unread portion in the ring buffer DOES wrap | ||
| 194 | // The DMA writer has wrapped since we last read and is currently | ||
| 195 | // writing (or the next byte added will be) in the beginning of the ring buffer. | ||
| 196 | |||
| 197 | // The provided read buffer is large enough to include all elements from the tail of the dma buffer, | ||
| 198 | // so the next read will not have any unread tail elements in the ring buffer. | ||
| 199 | |||
| 200 | // Copy out from the dma buffer | ||
| 201 | let tail = self.copy_to(buf, self.start..self.cap()); | ||
| 202 | let head = self.copy_to(&mut buf[tail..], 0..end); | ||
| 203 | |||
| 204 | compiler_fence(Ordering::SeqCst); | ||
| 205 | |||
| 206 | /* | ||
| 207 | first, check if the dma has wrapped around more than once | ||
| 208 | |||
| 209 | then, get the current position of of the dma write and check | ||
| 210 | if it's inside data we could have copied | ||
| 211 | */ | ||
| 212 | let pos = self.pos(dma); | ||
| 213 | if pos > self.start || pos < end || dma.reset_complete_count() > 1 { | ||
| 214 | Err(OverrunError) | ||
| 215 | } else { | ||
| 216 | self.start = head; | ||
| 217 | Ok((tail + head, self.cap() - self.start)) | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
| 221 | /// Copy from the dma buffer at `data_range` into `buf` | ||
| 222 | fn copy_to(&mut self, buf: &mut [W], data_range: Range<usize>) -> usize { | ||
| 223 | // Limit the number of elements that can be copied | ||
| 224 | let length = usize::min(data_range.len(), buf.len()); | ||
| 225 | |||
| 226 | // Copy from dma buffer into read buffer | ||
| 227 | // We need to do it like this instead of a simple copy_from_slice() because | ||
| 228 | // reading from a part of memory that may be simultaneously written to is unsafe | ||
| 229 | unsafe { | ||
| 230 | let dma_buf = self.dma_buf.as_ptr(); | ||
| 231 | |||
| 232 | for i in 0..length { | ||
| 233 | buf[i] = core::ptr::read_volatile(dma_buf.offset((data_range.start + i) as isize)); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | length | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | pub struct WritableDmaRingBuffer<'a, W: Word> { | ||
| 242 | pub(crate) dma_buf: &'a mut [W], | ||
| 243 | end: usize, | ||
| 244 | } | ||
| 245 | |||
| 246 | impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { | ||
| 247 | pub fn new(dma_buf: &'a mut [W]) -> Self { | ||
| 248 | Self { dma_buf, end: 0 } | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Reset the ring buffer to its initial state | ||
| 252 | pub fn clear(&mut self, dma: &mut impl DmaCtrl) { | ||
| 253 | self.end = 0; | ||
| 254 | dma.reset_complete_count(); | ||
| 255 | } | ||
| 256 | |||
| 257 | /// The capacity of the ringbuffer | ||
| 258 | pub const fn cap(&self) -> usize { | ||
| 259 | self.dma_buf.len() | ||
| 260 | } | ||
| 261 | |||
| 262 | /// The current position of the ringbuffer | ||
| 263 | fn pos(&self, dma: &mut impl DmaCtrl) -> usize { | ||
| 264 | self.cap() - dma.get_remaining_transfers() | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Write elements directly to the buffer. This must be done before the DMA is started | ||
| 268 | /// or after the buffer has been cleared using `clear()`. | ||
| 269 | pub fn write_immediate(&mut self, buffer: &[W]) -> Result<(usize, usize), OverrunError> { | ||
| 270 | if self.end != 0 { | ||
| 271 | return Err(OverrunError); | ||
| 272 | } | ||
| 273 | let written = self.copy_from(buffer, 0..self.cap()); | ||
| 274 | self.end = written % self.cap(); | ||
| 275 | Ok((written, self.cap() - written)) | ||
| 276 | } | ||
| 277 | |||
| 278 | /// Write an exact number of elements to the ringbuffer. | ||
| 279 | pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, OverrunError> { | ||
| 280 | let mut written_data = 0; | ||
| 281 | let buffer_len = buffer.len(); | ||
| 282 | |||
| 283 | poll_fn(|cx| { | ||
| 284 | dma.set_waker(cx.waker()); | ||
| 285 | |||
| 286 | compiler_fence(Ordering::SeqCst); | ||
| 287 | |||
| 288 | match self.write(dma, &buffer[written_data..buffer_len]) { | ||
| 289 | Ok((len, remaining)) => { | ||
| 290 | written_data += len; | ||
| 291 | if written_data == buffer_len { | ||
| 292 | Poll::Ready(Ok(remaining)) | ||
| 293 | } else { | ||
| 294 | Poll::Pending | ||
| 295 | } | ||
| 296 | } | ||
| 297 | Err(e) => Poll::Ready(Err(e)), | ||
| 298 | } | ||
| 299 | }) | ||
| 300 | .await | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Write elements from the ring buffer | ||
| 304 | /// Return a tuple of the length written and the capacity remaining to be written in the buffer | ||
| 305 | pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { | ||
| 306 | let start = self.pos(dma); | ||
| 307 | if start > self.end { | ||
| 308 | // The occupied portion in the ring buffer DOES wrap | ||
| 309 | let len = self.copy_from(buf, self.end..start); | ||
| 310 | |||
| 311 | compiler_fence(Ordering::SeqCst); | ||
| 312 | |||
| 313 | // Confirm that the DMA is not inside data we could have written | ||
| 314 | let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); | ||
| 315 | if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { | ||
| 316 | Err(OverrunError) | ||
| 317 | } else { | ||
| 318 | self.end = (self.end + len) % self.cap(); | ||
| 319 | |||
| 320 | Ok((len, self.cap() - (start - self.end))) | ||
| 321 | } | ||
| 322 | } else if start == self.end && dma.get_complete_count() == 0 { | ||
| 323 | Ok((0, 0)) | ||
| 324 | } else if start <= self.end && self.end + buf.len() < self.cap() { | ||
| 325 | // The occupied portion in the ring buffer DOES NOT wrap | ||
| 326 | // and copying elements into the buffer WILL NOT cause it to | ||
| 327 | |||
| 328 | // Copy into the dma buffer | ||
| 329 | let len = self.copy_from(buf, self.end..self.cap()); | ||
| 330 | |||
| 331 | compiler_fence(Ordering::SeqCst); | ||
| 332 | |||
| 333 | // Confirm that the DMA is not inside data we could have written | ||
| 334 | let pos = self.pos(dma); | ||
| 335 | if pos > self.end || pos < start || dma.get_complete_count() > 1 { | ||
| 336 | Err(OverrunError) | ||
| 337 | } else { | ||
| 338 | self.end = (self.end + len) % self.cap(); | ||
| 339 | |||
| 340 | Ok((len, self.cap() - (self.end - start))) | ||
| 341 | } | ||
| 342 | } else { | ||
| 343 | // The occupied portion in the ring buffer DOES NOT wrap | ||
| 344 | // and copying elements into the buffer WILL cause it to | ||
| 345 | |||
| 346 | let tail = self.copy_from(buf, self.end..self.cap()); | ||
| 347 | let head = self.copy_from(&buf[tail..], 0..start); | ||
| 348 | |||
| 349 | compiler_fence(Ordering::SeqCst); | ||
| 350 | |||
| 351 | // Confirm that the DMA is not inside data we could have written | ||
| 352 | let pos = self.pos(dma); | ||
| 353 | if pos > self.end || pos < start || dma.reset_complete_count() > 1 { | ||
| 354 | Err(OverrunError) | ||
| 355 | } else { | ||
| 356 | self.end = head; | ||
| 357 | |||
| 358 | Ok((tail + head, self.cap() - (start - self.end))) | ||
| 359 | } | ||
| 360 | } | ||
| 361 | } | ||
| 362 | /// Copy into the dma buffer at `data_range` from `buf` | ||
| 363 | fn copy_from(&mut self, buf: &[W], data_range: Range<usize>) -> usize { | ||
| 364 | // Limit the number of elements that can be copied | ||
| 365 | let length = usize::min(data_range.len(), buf.len()); | ||
| 366 | |||
| 367 | // Copy into dma buffer from read buffer | ||
| 368 | // We need to do it like this instead of a simple copy_from_slice() because | ||
| 369 | // reading from a part of memory that may be simultaneously written to is unsafe | ||
| 370 | unsafe { | ||
| 371 | let dma_buf = self.dma_buf.as_mut_ptr(); | ||
| 372 | |||
| 373 | for i in 0..length { | ||
| 374 | core::ptr::write_volatile(dma_buf.offset((data_range.start + i) as isize), buf[i]); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | length | ||
| 379 | } | ||
| 380 | } | ||
| 381 | #[cfg(test)] | ||
| 382 | mod tests { | ||
| 383 | use core::array; | ||
| 384 | use std::{cell, vec}; | ||
| 385 | |||
| 386 | use super::*; | ||
| 387 | |||
| 388 | #[allow(dead_code)] | ||
| 389 | #[derive(PartialEq, Debug)] | ||
| 390 | enum TestCircularTransferRequest { | ||
| 391 | GetCompleteCount(usize), | ||
| 392 | ResetCompleteCount(usize), | ||
| 393 | PositionRequest(usize), | ||
| 394 | } | ||
| 395 | |||
| 396 | struct TestCircularTransfer { | ||
| 397 | len: usize, | ||
| 398 | requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>, | ||
| 399 | } | ||
| 400 | |||
| 401 | impl DmaCtrl for TestCircularTransfer { | ||
| 402 | fn get_remaining_transfers(&self) -> usize { | ||
| 403 | match self.requests.borrow_mut().pop().unwrap() { | ||
| 404 | TestCircularTransferRequest::PositionRequest(pos) => { | ||
| 405 | let len = self.len; | ||
| 406 | |||
| 407 | assert!(len >= pos); | ||
| 408 | |||
| 409 | len - pos | ||
| 410 | } | ||
| 411 | _ => unreachable!(), | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | fn get_complete_count(&self) -> usize { | ||
| 416 | match self.requests.borrow_mut().pop().unwrap() { | ||
| 417 | TestCircularTransferRequest::GetCompleteCount(complete_count) => complete_count, | ||
| 418 | _ => unreachable!(), | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | fn reset_complete_count(&mut self) -> usize { | ||
| 423 | match self.requests.get_mut().pop().unwrap() { | ||
| 424 | TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count, | ||
| 425 | _ => unreachable!(), | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | fn set_waker(&mut self, waker: &Waker) {} | ||
| 430 | } | ||
| 431 | |||
| 432 | impl TestCircularTransfer { | ||
| 433 | pub fn new(len: usize) -> Self { | ||
| 434 | Self { | ||
| 435 | requests: cell::RefCell::new(vec![]), | ||
| 436 | len, | ||
| 437 | } | ||
| 438 | } | ||
| 439 | |||
| 440 | pub fn setup(&self, mut requests: vec::Vec<TestCircularTransferRequest>) { | ||
| 441 | requests.reverse(); | ||
| 442 | self.requests.replace(requests); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | |||
| 446 | #[test] | ||
| 447 | fn empty_and_read_not_started() { | ||
| 448 | let mut dma_buf = [0u8; 16]; | ||
| 449 | let ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 450 | |||
| 451 | assert_eq!(0, ringbuf.start); | ||
| 452 | } | ||
| 453 | |||
| 454 | #[test] | ||
| 455 | fn can_read() { | ||
| 456 | let mut dma = TestCircularTransfer::new(16); | ||
| 457 | |||
| 458 | let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 | ||
| 459 | let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 460 | |||
| 461 | assert_eq!(0, ringbuf.start); | ||
| 462 | assert_eq!(16, ringbuf.cap()); | ||
| 463 | |||
| 464 | dma.setup(vec![ | ||
| 465 | TestCircularTransferRequest::PositionRequest(8), | ||
| 466 | TestCircularTransferRequest::PositionRequest(10), | ||
| 467 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 468 | ]); | ||
| 469 | let mut buf = [0; 2]; | ||
| 470 | assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 471 | assert_eq!([0, 1], buf); | ||
| 472 | assert_eq!(2, ringbuf.start); | ||
| 473 | |||
| 474 | dma.setup(vec![ | ||
| 475 | TestCircularTransferRequest::PositionRequest(10), | ||
| 476 | TestCircularTransferRequest::PositionRequest(12), | ||
| 477 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 478 | ]); | ||
| 479 | let mut buf = [0; 2]; | ||
| 480 | assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 481 | assert_eq!([2, 3], buf); | ||
| 482 | assert_eq!(4, ringbuf.start); | ||
| 483 | |||
| 484 | dma.setup(vec![ | ||
| 485 | TestCircularTransferRequest::PositionRequest(12), | ||
| 486 | TestCircularTransferRequest::PositionRequest(14), | ||
| 487 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 488 | ]); | ||
| 489 | let mut buf = [0; 8]; | ||
| 490 | assert_eq!(8, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 491 | assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]); | ||
| 492 | assert_eq!(12, ringbuf.start); | ||
| 493 | } | ||
| 494 | |||
| 495 | #[test] | ||
| 496 | fn can_read_with_wrap() { | ||
| 497 | let mut dma = TestCircularTransfer::new(16); | ||
| 498 | |||
| 499 | let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 | ||
| 500 | let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 501 | |||
| 502 | assert_eq!(0, ringbuf.start); | ||
| 503 | assert_eq!(16, ringbuf.cap()); | ||
| 504 | |||
| 505 | /* | ||
| 506 | Read to close to the end of the buffer | ||
| 507 | */ | ||
| 508 | dma.setup(vec![ | ||
| 509 | TestCircularTransferRequest::PositionRequest(14), | ||
| 510 | TestCircularTransferRequest::PositionRequest(16), | ||
| 511 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 512 | ]); | ||
| 513 | let mut buf = [0; 14]; | ||
| 514 | assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 515 | assert_eq!(14, ringbuf.start); | ||
| 516 | |||
| 517 | /* | ||
| 518 | Now, read around the buffer | ||
| 519 | */ | ||
| 520 | dma.setup(vec![ | ||
| 521 | TestCircularTransferRequest::PositionRequest(6), | ||
| 522 | TestCircularTransferRequest::PositionRequest(8), | ||
| 523 | TestCircularTransferRequest::ResetCompleteCount(1), | ||
| 524 | ]); | ||
| 525 | let mut buf = [0; 6]; | ||
| 526 | assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 527 | assert_eq!(4, ringbuf.start); | ||
| 528 | } | ||
| 529 | |||
| 530 | #[test] | ||
| 531 | fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() { | ||
| 532 | let mut dma = TestCircularTransfer::new(16); | ||
| 533 | |||
| 534 | let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 | ||
| 535 | let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 536 | |||
| 537 | assert_eq!(0, ringbuf.start); | ||
| 538 | assert_eq!(16, ringbuf.cap()); | ||
| 539 | |||
| 540 | /* | ||
| 541 | Read to close to the end of the buffer | ||
| 542 | */ | ||
| 543 | dma.setup(vec![ | ||
| 544 | TestCircularTransferRequest::PositionRequest(14), | ||
| 545 | TestCircularTransferRequest::PositionRequest(16), | ||
| 546 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 547 | ]); | ||
| 548 | let mut buf = [0; 14]; | ||
| 549 | assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 550 | assert_eq!(14, ringbuf.start); | ||
| 551 | |||
| 552 | /* | ||
| 553 | Now, read to the end of the buffer | ||
| 554 | */ | ||
| 555 | dma.setup(vec![ | ||
| 556 | TestCircularTransferRequest::PositionRequest(6), | ||
| 557 | TestCircularTransferRequest::PositionRequest(8), | ||
| 558 | TestCircularTransferRequest::ResetCompleteCount(1), | ||
| 559 | ]); | ||
| 560 | let mut buf = [0; 2]; | ||
| 561 | assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 562 | assert_eq!(0, ringbuf.start); | ||
| 563 | } | ||
| 564 | |||
| 565 | #[test] | ||
| 566 | fn can_read_when_dma_writer_wraps_once_with_same_ndtr() { | ||
| 567 | let mut dma = TestCircularTransfer::new(16); | ||
| 568 | |||
| 569 | let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 | ||
| 570 | let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 571 | |||
| 572 | assert_eq!(0, ringbuf.start); | ||
| 573 | assert_eq!(16, ringbuf.cap()); | ||
| 574 | |||
| 575 | /* | ||
| 576 | Read to about the middle of the buffer | ||
| 577 | */ | ||
| 578 | dma.setup(vec![ | ||
| 579 | TestCircularTransferRequest::PositionRequest(6), | ||
| 580 | TestCircularTransferRequest::PositionRequest(6), | ||
| 581 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 582 | ]); | ||
| 583 | let mut buf = [0; 6]; | ||
| 584 | assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 585 | assert_eq!(6, ringbuf.start); | ||
| 586 | |||
| 587 | /* | ||
| 588 | Now, wrap the DMA controller around | ||
| 589 | */ | ||
| 590 | dma.setup(vec![ | ||
| 591 | TestCircularTransferRequest::PositionRequest(6), | ||
| 592 | TestCircularTransferRequest::GetCompleteCount(1), | ||
| 593 | TestCircularTransferRequest::PositionRequest(6), | ||
| 594 | TestCircularTransferRequest::GetCompleteCount(1), | ||
| 595 | ]); | ||
| 596 | let mut buf = [0; 6]; | ||
| 597 | assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 598 | assert_eq!(12, ringbuf.start); | ||
| 599 | } | ||
| 600 | |||
| 601 | #[test] | ||
| 602 | fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() { | ||
| 603 | let mut dma = TestCircularTransfer::new(16); | ||
| 604 | |||
| 605 | let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 | ||
| 606 | let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 607 | |||
| 608 | assert_eq!(0, ringbuf.start); | ||
| 609 | assert_eq!(16, ringbuf.cap()); | ||
| 610 | |||
| 611 | /* | ||
| 612 | Read a few bytes | ||
| 613 | */ | ||
| 614 | dma.setup(vec![ | ||
| 615 | TestCircularTransferRequest::PositionRequest(2), | ||
| 616 | TestCircularTransferRequest::PositionRequest(2), | ||
| 617 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 618 | ]); | ||
| 619 | let mut buf = [0; 6]; | ||
| 620 | assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 621 | assert_eq!(2, ringbuf.start); | ||
| 622 | |||
| 623 | /* | ||
| 624 | Now, overtake the reader | ||
| 625 | */ | ||
| 626 | dma.setup(vec![ | ||
| 627 | TestCircularTransferRequest::PositionRequest(4), | ||
| 628 | TestCircularTransferRequest::PositionRequest(6), | ||
| 629 | TestCircularTransferRequest::GetCompleteCount(1), | ||
| 630 | ]); | ||
| 631 | let mut buf = [0; 6]; | ||
| 632 | assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err()); | ||
| 633 | } | ||
| 634 | |||
| 635 | #[test] | ||
| 636 | fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() { | ||
| 637 | let mut dma = TestCircularTransfer::new(16); | ||
| 638 | |||
| 639 | let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 | ||
| 640 | let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); | ||
| 641 | |||
| 642 | assert_eq!(0, ringbuf.start); | ||
| 643 | assert_eq!(16, ringbuf.cap()); | ||
| 644 | |||
| 645 | /* | ||
| 646 | Read to close to the end of the buffer | ||
| 647 | */ | ||
| 648 | dma.setup(vec![ | ||
| 649 | TestCircularTransferRequest::PositionRequest(14), | ||
| 650 | TestCircularTransferRequest::PositionRequest(16), | ||
| 651 | TestCircularTransferRequest::GetCompleteCount(0), | ||
| 652 | ]); | ||
| 653 | let mut buf = [0; 14]; | ||
| 654 | assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); | ||
| 655 | assert_eq!(14, ringbuf.start); | ||
| 656 | |||
| 657 | /* | ||
| 658 | Now, overtake the reader | ||
| 659 | */ | ||
| 660 | dma.setup(vec![ | ||
| 661 | TestCircularTransferRequest::PositionRequest(8), | ||
| 662 | TestCircularTransferRequest::PositionRequest(10), | ||
| 663 | TestCircularTransferRequest::ResetCompleteCount(2), | ||
| 664 | ]); | ||
| 665 | let mut buf = [0; 6]; | ||
| 666 | assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err()); | ||
| 667 | } | ||
| 668 | } | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs new file mode 100644 index 000000000..44ea497fe --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs | |||
| @@ -0,0 +1,333 @@ | |||
| 1 | #![cfg_attr(gpdma, allow(unused))] | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::task::{Poll, Waker}; | ||
| 5 | |||
| 6 | use crate::dma::word::Word; | ||
| 7 | |||
| 8 | pub trait DmaCtrl { | ||
| 9 | /// Get the NDTR register value, i.e. the space left in the underlying | ||
| 10 | /// buffer until the dma writer wraps. | ||
| 11 | fn get_remaining_transfers(&self) -> usize; | ||
| 12 | |||
| 13 | /// Reset the transfer completed counter to 0 and return the value just prior to the reset. | ||
| 14 | fn reset_complete_count(&mut self) -> usize; | ||
| 15 | |||
| 16 | /// Set the waker for a running poll_fn | ||
| 17 | fn set_waker(&mut self, waker: &Waker); | ||
| 18 | } | ||
| 19 | |||
| 20 | #[derive(Debug, PartialEq)] | ||
| 21 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 22 | pub enum Error { | ||
| 23 | Overrun, | ||
| 24 | /// the newly read DMA positions don't make sense compared to the previous | ||
| 25 | /// ones. This can usually only occur due to wrong Driver implementation, if | ||
| 26 | /// the driver author (or the user using raw metapac code) directly resets | ||
| 27 | /// the channel for instance. | ||
| 28 | DmaUnsynced, | ||
| 29 | } | ||
| 30 | |||
| 31 | #[derive(Debug, Clone, Copy, Default)] | ||
| 32 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 33 | struct DmaIndex { | ||
| 34 | complete_count: usize, | ||
| 35 | pos: usize, | ||
| 36 | } | ||
| 37 | |||
| 38 | impl DmaIndex { | ||
| 39 | fn reset(&mut self) { | ||
| 40 | self.pos = 0; | ||
| 41 | self.complete_count = 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | fn as_index(&self, cap: usize, offset: usize) -> usize { | ||
| 45 | (self.pos + offset) % cap | ||
| 46 | } | ||
| 47 | |||
| 48 | fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) { | ||
| 49 | // Important! | ||
| 50 | // The ordering of the first two lines matters! | ||
| 51 | // If changed, the code will detect a wrong +capacity | ||
| 52 | // jump at wrap-around. | ||
| 53 | let count_diff = dma.reset_complete_count(); | ||
| 54 | let pos = cap - dma.get_remaining_transfers(); | ||
| 55 | self.pos = if pos < self.pos && count_diff == 0 { | ||
| 56 | cap - 1 | ||
| 57 | } else { | ||
| 58 | pos | ||
| 59 | }; | ||
| 60 | |||
| 61 | self.complete_count += count_diff; | ||
| 62 | } | ||
| 63 | |||
| 64 | fn advance(&mut self, cap: usize, steps: usize) { | ||
| 65 | let next = self.pos + steps; | ||
| 66 | self.complete_count += next / cap; | ||
| 67 | self.pos = next % cap; | ||
| 68 | } | ||
| 69 | |||
| 70 | fn normalize(lhs: &mut DmaIndex, rhs: &mut DmaIndex) { | ||
| 71 | let min_count = lhs.complete_count.min(rhs.complete_count); | ||
| 72 | lhs.complete_count -= min_count; | ||
| 73 | rhs.complete_count -= min_count; | ||
| 74 | } | ||
| 75 | |||
| 76 | fn diff(&self, cap: usize, rhs: &DmaIndex) -> isize { | ||
| 77 | (self.complete_count * cap + self.pos) as isize - (rhs.complete_count * cap + rhs.pos) as isize | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | pub struct ReadableDmaRingBuffer<'a, W: Word> { | ||
| 82 | dma_buf: &'a mut [W], | ||
| 83 | write_index: DmaIndex, | ||
| 84 | read_index: DmaIndex, | ||
| 85 | } | ||
| 86 | |||
| 87 | impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { | ||
| 88 | /// Construct an empty buffer. | ||
| 89 | pub fn new(dma_buf: &'a mut [W]) -> Self { | ||
| 90 | Self { | ||
| 91 | dma_buf, | ||
| 92 | write_index: Default::default(), | ||
| 93 | read_index: Default::default(), | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Reset the ring buffer to its initial state. | ||
| 98 | pub fn reset(&mut self, dma: &mut impl DmaCtrl) { | ||
| 99 | dma.reset_complete_count(); | ||
| 100 | self.write_index.reset(); | ||
| 101 | self.write_index.dma_sync(self.cap(), dma); | ||
| 102 | self.read_index = self.write_index; | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Get the full ringbuffer capacity. | ||
| 106 | pub const fn cap(&self) -> usize { | ||
| 107 | self.dma_buf.len() | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Get the available readable dma samples. | ||
| 111 | pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> { | ||
| 112 | self.write_index.dma_sync(self.cap(), dma); | ||
| 113 | DmaIndex::normalize(&mut self.write_index, &mut self.read_index); | ||
| 114 | |||
| 115 | let diff = self.write_index.diff(self.cap(), &self.read_index); | ||
| 116 | |||
| 117 | if diff < 0 { | ||
| 118 | Err(Error::DmaUnsynced) | ||
| 119 | } else if diff > self.cap() as isize { | ||
| 120 | Err(Error::Overrun) | ||
| 121 | } else { | ||
| 122 | Ok(diff as usize) | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | /// Read elements from the ring buffer. | ||
| 127 | /// | ||
| 128 | /// Return a tuple of the length read and the length remaining in the buffer | ||
| 129 | /// If not all of the elements were read, then there will be some elements in the buffer remaining | ||
| 130 | /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read | ||
| 131 | /// Error is returned if the portion to be read was overwritten by the DMA controller, | ||
| 132 | /// in which case the rinbuffer will automatically reset itself. | ||
| 133 | pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> { | ||
| 134 | self.read_raw(dma, buf).inspect_err(|_e| { | ||
| 135 | self.reset(dma); | ||
| 136 | }) | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Read an exact number of elements from the ringbuffer. | ||
| 140 | /// | ||
| 141 | /// Returns the remaining number of elements available for immediate reading. | ||
| 142 | /// Error is returned if the portion to be read was overwritten by the DMA controller. | ||
| 143 | /// | ||
| 144 | /// Async/Wake Behavior: | ||
| 145 | /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, | ||
| 146 | /// and when it wraps around. This means that when called with a buffer of length 'M', when this | ||
| 147 | /// ring buffer was created with a buffer of size 'N': | ||
| 148 | /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. | ||
| 149 | /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. | ||
| 150 | pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result<usize, Error> { | ||
| 151 | let mut read_data = 0; | ||
| 152 | let buffer_len = buffer.len(); | ||
| 153 | |||
| 154 | poll_fn(|cx| { | ||
| 155 | dma.set_waker(cx.waker()); | ||
| 156 | |||
| 157 | match self.read(dma, &mut buffer[read_data..buffer_len]) { | ||
| 158 | Ok((len, remaining)) => { | ||
| 159 | read_data += len; | ||
| 160 | if read_data == buffer_len { | ||
| 161 | Poll::Ready(Ok(remaining)) | ||
| 162 | } else { | ||
| 163 | Poll::Pending | ||
| 164 | } | ||
| 165 | } | ||
| 166 | Err(e) => Poll::Ready(Err(e)), | ||
| 167 | } | ||
| 168 | }) | ||
| 169 | .await | ||
| 170 | } | ||
| 171 | |||
| 172 | fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> { | ||
| 173 | let readable = self.len(dma)?.min(buf.len()); | ||
| 174 | for i in 0..readable { | ||
| 175 | buf[i] = self.read_buf(i); | ||
| 176 | } | ||
| 177 | let available = self.len(dma)?; | ||
| 178 | self.read_index.advance(self.cap(), readable); | ||
| 179 | Ok((readable, available - readable)) | ||
| 180 | } | ||
| 181 | |||
| 182 | fn read_buf(&self, offset: usize) -> W { | ||
| 183 | unsafe { | ||
| 184 | core::ptr::read_volatile( | ||
| 185 | self.dma_buf | ||
| 186 | .as_ptr() | ||
| 187 | .offset(self.read_index.as_index(self.cap(), offset) as isize), | ||
| 188 | ) | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | pub struct WritableDmaRingBuffer<'a, W: Word> { | ||
| 194 | dma_buf: &'a mut [W], | ||
| 195 | read_index: DmaIndex, | ||
| 196 | write_index: DmaIndex, | ||
| 197 | } | ||
| 198 | |||
| 199 | impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { | ||
| 200 | /// Construct a ringbuffer filled with the given buffer data. | ||
| 201 | pub fn new(dma_buf: &'a mut [W]) -> Self { | ||
| 202 | let len = dma_buf.len(); | ||
| 203 | Self { | ||
| 204 | dma_buf, | ||
| 205 | read_index: Default::default(), | ||
| 206 | write_index: DmaIndex { | ||
| 207 | complete_count: 0, | ||
| 208 | pos: len, | ||
| 209 | }, | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Reset the ring buffer to its initial state. The buffer after the reset will be full. | ||
| 214 | pub fn reset(&mut self, dma: &mut impl DmaCtrl) { | ||
| 215 | dma.reset_complete_count(); | ||
| 216 | self.read_index.reset(); | ||
| 217 | self.read_index.dma_sync(self.cap(), dma); | ||
| 218 | self.write_index = self.read_index; | ||
| 219 | self.write_index.advance(self.cap(), self.cap()); | ||
| 220 | } | ||
| 221 | |||
| 222 | /// Get the remaining writable dma samples. | ||
| 223 | pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> { | ||
| 224 | self.read_index.dma_sync(self.cap(), dma); | ||
| 225 | DmaIndex::normalize(&mut self.read_index, &mut self.write_index); | ||
| 226 | |||
| 227 | let diff = self.write_index.diff(self.cap(), &self.read_index); | ||
| 228 | |||
| 229 | if diff < 0 { | ||
| 230 | Err(Error::Overrun) | ||
| 231 | } else if diff > self.cap() as isize { | ||
| 232 | Err(Error::DmaUnsynced) | ||
| 233 | } else { | ||
| 234 | Ok(self.cap().saturating_sub(diff as usize)) | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Get the full ringbuffer capacity. | ||
| 239 | pub const fn cap(&self) -> usize { | ||
| 240 | self.dma_buf.len() | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Append data to the ring buffer. | ||
| 244 | /// Returns a tuple of the data written and the remaining write capacity in the buffer. | ||
| 245 | /// Error is returned if the portion to be written was previously read by the DMA controller. | ||
| 246 | /// In this case, the ringbuffer will automatically reset itself, giving a full buffer worth of | ||
| 247 | /// leeway between the write index and the DMA. | ||
| 248 | pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 249 | self.write_raw(dma, buf).inspect_err(|_e| { | ||
| 250 | self.reset(dma); | ||
| 251 | }) | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Write elements directly to the buffer. | ||
| 255 | /// | ||
| 256 | /// Subsequent writes will overwrite the content of the buffer, so it is not useful to call this more than once. | ||
| 257 | /// Data is aligned towards the end of the buffer. | ||
| 258 | /// | ||
| 259 | /// In case of success, returns the written length, and the empty space in front of the written block. | ||
| 260 | /// Fails if the data to write exceeds the buffer capacity. | ||
| 261 | pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 262 | if buf.len() > self.cap() { | ||
| 263 | return Err(Error::Overrun); | ||
| 264 | } | ||
| 265 | |||
| 266 | let start = self.cap() - buf.len(); | ||
| 267 | for (i, data) in buf.iter().enumerate() { | ||
| 268 | self.write_buf(start + i, *data) | ||
| 269 | } | ||
| 270 | let written = buf.len().min(self.cap()); | ||
| 271 | Ok((written, self.cap() - written)) | ||
| 272 | } | ||
| 273 | |||
| 274 | /// Wait for any ring buffer write error. | ||
| 275 | pub async fn wait_write_error(&mut self, dma: &mut impl DmaCtrl) -> Result<usize, Error> { | ||
| 276 | poll_fn(|cx| { | ||
| 277 | dma.set_waker(cx.waker()); | ||
| 278 | |||
| 279 | match self.len(dma) { | ||
| 280 | Ok(_) => Poll::Pending, | ||
| 281 | Err(e) => Poll::Ready(Err(e)), | ||
| 282 | } | ||
| 283 | }) | ||
| 284 | .await | ||
| 285 | } | ||
| 286 | |||
| 287 | /// Write an exact number of elements to the ringbuffer. | ||
| 288 | pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result<usize, Error> { | ||
| 289 | let mut written_data = 0; | ||
| 290 | let buffer_len = buffer.len(); | ||
| 291 | |||
| 292 | poll_fn(|cx| { | ||
| 293 | dma.set_waker(cx.waker()); | ||
| 294 | |||
| 295 | match self.write(dma, &buffer[written_data..buffer_len]) { | ||
| 296 | Ok((len, remaining)) => { | ||
| 297 | written_data += len; | ||
| 298 | if written_data == buffer_len { | ||
| 299 | Poll::Ready(Ok(remaining)) | ||
| 300 | } else { | ||
| 301 | Poll::Pending | ||
| 302 | } | ||
| 303 | } | ||
| 304 | Err(e) => Poll::Ready(Err(e)), | ||
| 305 | } | ||
| 306 | }) | ||
| 307 | .await | ||
| 308 | } | ||
| 309 | |||
| 310 | fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> { | ||
| 311 | let writable = self.len(dma)?.min(buf.len()); | ||
| 312 | for i in 0..writable { | ||
| 313 | self.write_buf(i, buf[i]); | ||
| 314 | } | ||
| 315 | let available = self.len(dma)?; | ||
| 316 | self.write_index.advance(self.cap(), writable); | ||
| 317 | Ok((writable, available - writable)) | ||
| 318 | } | ||
| 319 | |||
| 320 | fn write_buf(&mut self, offset: usize, value: W) { | ||
| 321 | unsafe { | ||
| 322 | core::ptr::write_volatile( | ||
| 323 | self.dma_buf | ||
| 324 | .as_mut_ptr() | ||
| 325 | .offset(self.write_index.as_index(self.cap(), offset) as isize), | ||
| 326 | value, | ||
| 327 | ) | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | #[cfg(test)] | ||
| 333 | mod tests; | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/tests/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs new file mode 100644 index 000000000..6fabedb83 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | use std::{cell, vec}; | ||
| 2 | |||
| 3 | use super::*; | ||
| 4 | |||
| 5 | #[allow(unused)] | ||
| 6 | #[derive(PartialEq, Debug)] | ||
| 7 | enum TestCircularTransferRequest { | ||
| 8 | ResetCompleteCount(usize), | ||
| 9 | PositionRequest(usize), | ||
| 10 | } | ||
| 11 | |||
| 12 | #[allow(unused)] | ||
| 13 | struct TestCircularTransfer { | ||
| 14 | len: usize, | ||
| 15 | requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl DmaCtrl for TestCircularTransfer { | ||
| 19 | fn get_remaining_transfers(&self) -> usize { | ||
| 20 | match self.requests.borrow_mut().pop().unwrap() { | ||
| 21 | TestCircularTransferRequest::PositionRequest(pos) => { | ||
| 22 | let len = self.len; | ||
| 23 | |||
| 24 | assert!(len >= pos); | ||
| 25 | |||
| 26 | len - pos | ||
| 27 | } | ||
| 28 | _ => unreachable!(), | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | fn reset_complete_count(&mut self) -> usize { | ||
| 33 | match self.requests.get_mut().pop().unwrap() { | ||
| 34 | TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count, | ||
| 35 | _ => unreachable!(), | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | fn set_waker(&mut self, _waker: &Waker) {} | ||
| 40 | } | ||
| 41 | |||
| 42 | impl TestCircularTransfer { | ||
| 43 | #[allow(unused)] | ||
| 44 | pub fn new(len: usize) -> Self { | ||
| 45 | Self { | ||
| 46 | requests: cell::RefCell::new(vec![]), | ||
| 47 | len, | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | #[allow(unused)] | ||
| 52 | pub fn setup(&self, mut requests: vec::Vec<TestCircularTransferRequest>) { | ||
| 53 | requests.reverse(); | ||
| 54 | self.requests.replace(requests); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | const CAP: usize = 16; | ||
| 59 | |||
| 60 | #[test] | ||
| 61 | fn dma_index_as_index_returns_index_mod_cap_by_default() { | ||
| 62 | let index = DmaIndex::default(); | ||
| 63 | assert_eq!(index.as_index(CAP, 0), 0); | ||
| 64 | assert_eq!(index.as_index(CAP, 1), 1); | ||
| 65 | assert_eq!(index.as_index(CAP, 2), 2); | ||
| 66 | assert_eq!(index.as_index(CAP, 3), 3); | ||
| 67 | assert_eq!(index.as_index(CAP, 4), 4); | ||
| 68 | assert_eq!(index.as_index(CAP, CAP), 0); | ||
| 69 | assert_eq!(index.as_index(CAP, CAP + 1), 1); | ||
| 70 | } | ||
| 71 | |||
| 72 | #[test] | ||
| 73 | fn dma_index_advancing_increases_as_index() { | ||
| 74 | let mut index = DmaIndex::default(); | ||
| 75 | assert_eq!(index.as_index(CAP, 0), 0); | ||
| 76 | index.advance(CAP, 1); | ||
| 77 | assert_eq!(index.as_index(CAP, 0), 1); | ||
| 78 | index.advance(CAP, 1); | ||
| 79 | assert_eq!(index.as_index(CAP, 0), 2); | ||
| 80 | index.advance(CAP, 1); | ||
| 81 | assert_eq!(index.as_index(CAP, 0), 3); | ||
| 82 | index.advance(CAP, 1); | ||
| 83 | assert_eq!(index.as_index(CAP, 0), 4); | ||
| 84 | index.advance(CAP, CAP - 4); | ||
| 85 | assert_eq!(index.as_index(CAP, 0), 0); | ||
| 86 | index.advance(CAP, 1); | ||
| 87 | assert_eq!(index.as_index(CAP, 0), 1); | ||
| 88 | } | ||
| 89 | |||
| 90 | mod prop_test; | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs new file mode 100644 index 000000000..661fb1728 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | use std::task::Waker; | ||
| 2 | |||
| 3 | use proptest::prop_oneof; | ||
| 4 | use proptest::strategy::{self, BoxedStrategy, Strategy as _}; | ||
| 5 | use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest}; | ||
| 6 | |||
| 7 | use super::*; | ||
| 8 | |||
| 9 | const CAP: usize = 128; | ||
| 10 | |||
| 11 | #[derive(Debug, Default)] | ||
| 12 | struct DmaMock { | ||
| 13 | pos: usize, | ||
| 14 | wraps: usize, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl DmaMock { | ||
| 18 | pub fn advance(&mut self, steps: usize) { | ||
| 19 | let next = self.pos + steps; | ||
| 20 | self.pos = next % CAP; | ||
| 21 | self.wraps += next / CAP; | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl DmaCtrl for DmaMock { | ||
| 26 | fn get_remaining_transfers(&self) -> usize { | ||
| 27 | CAP - self.pos | ||
| 28 | } | ||
| 29 | |||
| 30 | fn reset_complete_count(&mut self) -> usize { | ||
| 31 | core::mem::replace(&mut self.wraps, 0) | ||
| 32 | } | ||
| 33 | |||
| 34 | fn set_waker(&mut self, _waker: &Waker) {} | ||
| 35 | } | ||
| 36 | |||
| 37 | #[derive(Debug, Clone)] | ||
| 38 | enum Status { | ||
| 39 | Available(usize), | ||
| 40 | Failed, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Status { | ||
| 44 | pub fn new(capacity: usize) -> Self { | ||
| 45 | Self::Available(capacity) | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | mod reader; | ||
| 50 | mod writer; | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs new file mode 100644 index 000000000..4f3957a68 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | use core::fmt::Debug; | ||
| 2 | |||
| 3 | use super::*; | ||
| 4 | |||
| 5 | #[derive(Debug, Clone)] | ||
| 6 | enum ReaderTransition { | ||
| 7 | Write(usize), | ||
| 8 | Reset, | ||
| 9 | ReadUpTo(usize), | ||
| 10 | } | ||
| 11 | |||
| 12 | struct ReaderSM; | ||
| 13 | |||
| 14 | impl ReferenceStateMachine for ReaderSM { | ||
| 15 | type State = Status; | ||
| 16 | type Transition = ReaderTransition; | ||
| 17 | |||
| 18 | fn init_state() -> BoxedStrategy<Self::State> { | ||
| 19 | strategy::Just(Status::new(0)).boxed() | ||
| 20 | } | ||
| 21 | |||
| 22 | fn transitions(_state: &Self::State) -> BoxedStrategy<Self::Transition> { | ||
| 23 | prop_oneof![ | ||
| 24 | (1..50_usize).prop_map(ReaderTransition::Write), | ||
| 25 | (1..50_usize).prop_map(ReaderTransition::ReadUpTo), | ||
| 26 | strategy::Just(ReaderTransition::Reset), | ||
| 27 | ] | ||
| 28 | .boxed() | ||
| 29 | } | ||
| 30 | |||
| 31 | fn apply(status: Self::State, transition: &Self::Transition) -> Self::State { | ||
| 32 | match (status, transition) { | ||
| 33 | (_, ReaderTransition::Reset) => Status::Available(0), | ||
| 34 | (Status::Available(x), ReaderTransition::Write(y)) => { | ||
| 35 | if x + y > CAP { | ||
| 36 | Status::Failed | ||
| 37 | } else { | ||
| 38 | Status::Available(x + y) | ||
| 39 | } | ||
| 40 | } | ||
| 41 | (Status::Failed, ReaderTransition::Write(_)) => Status::Failed, | ||
| 42 | (Status::Available(x), ReaderTransition::ReadUpTo(y)) => Status::Available(x.saturating_sub(*y)), | ||
| 43 | (Status::Failed, ReaderTransition::ReadUpTo(_)) => Status::Available(0), | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | struct ReaderSut { | ||
| 49 | status: Status, | ||
| 50 | buffer: *mut [u8], | ||
| 51 | producer: DmaMock, | ||
| 52 | consumer: ReadableDmaRingBuffer<'static, u8>, | ||
| 53 | } | ||
| 54 | |||
| 55 | impl Debug for ReaderSut { | ||
| 56 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
| 57 | <DmaMock as Debug>::fmt(&self.producer, f) | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | struct ReaderTest; | ||
| 62 | |||
| 63 | impl StateMachineTest for ReaderTest { | ||
| 64 | type SystemUnderTest = ReaderSut; | ||
| 65 | type Reference = ReaderSM; | ||
| 66 | |||
| 67 | fn init_test(ref_status: &<Self::Reference as ReferenceStateMachine>::State) -> Self::SystemUnderTest { | ||
| 68 | let buffer = Box::into_raw(Box::new([0; CAP])); | ||
| 69 | ReaderSut { | ||
| 70 | status: ref_status.clone(), | ||
| 71 | buffer, | ||
| 72 | producer: DmaMock::default(), | ||
| 73 | consumer: ReadableDmaRingBuffer::new(unsafe { &mut *buffer }), | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | fn teardown(state: Self::SystemUnderTest) { | ||
| 78 | unsafe { | ||
| 79 | let _ = Box::from_raw(state.buffer); | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | fn apply( | ||
| 84 | mut sut: Self::SystemUnderTest, | ||
| 85 | ref_state: &<Self::Reference as ReferenceStateMachine>::State, | ||
| 86 | transition: <Self::Reference as ReferenceStateMachine>::Transition, | ||
| 87 | ) -> Self::SystemUnderTest { | ||
| 88 | match transition { | ||
| 89 | ReaderTransition::Write(x) => sut.producer.advance(x), | ||
| 90 | ReaderTransition::Reset => { | ||
| 91 | sut.consumer.reset(&mut sut.producer); | ||
| 92 | } | ||
| 93 | ReaderTransition::ReadUpTo(x) => { | ||
| 94 | let status = sut.status; | ||
| 95 | let ReaderSut { | ||
| 96 | ref mut producer, | ||
| 97 | ref mut consumer, | ||
| 98 | .. | ||
| 99 | } = sut; | ||
| 100 | let mut buf = vec![0; x]; | ||
| 101 | let res = consumer.read(producer, &mut buf); | ||
| 102 | match status { | ||
| 103 | Status::Available(n) => { | ||
| 104 | let readable = x.min(n); | ||
| 105 | |||
| 106 | assert_eq!(res.unwrap().0, readable); | ||
| 107 | } | ||
| 108 | Status::Failed => assert!(res.is_err()), | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | ReaderSut { | ||
| 114 | status: ref_state.clone(), | ||
| 115 | ..sut | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | prop_state_machine! { | ||
| 121 | #[test] | ||
| 122 | fn reader_state_test(sequential 1..20 => ReaderTest); | ||
| 123 | } | ||
diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs new file mode 100644 index 000000000..15433c0ee --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | use core::fmt::Debug; | ||
| 2 | |||
| 3 | use super::*; | ||
| 4 | |||
| 5 | #[derive(Debug, Clone)] | ||
| 6 | enum WriterTransition { | ||
| 7 | Read(usize), | ||
| 8 | WriteUpTo(usize), | ||
| 9 | Reset, | ||
| 10 | } | ||
| 11 | |||
| 12 | struct WriterSM; | ||
| 13 | |||
| 14 | impl ReferenceStateMachine for WriterSM { | ||
| 15 | type State = Status; | ||
| 16 | type Transition = WriterTransition; | ||
| 17 | |||
| 18 | fn init_state() -> BoxedStrategy<Self::State> { | ||
| 19 | strategy::Just(Status::new(CAP)).boxed() | ||
| 20 | } | ||
| 21 | |||
| 22 | fn transitions(_state: &Self::State) -> BoxedStrategy<Self::Transition> { | ||
| 23 | prop_oneof![ | ||
| 24 | (1..50_usize).prop_map(WriterTransition::Read), | ||
| 25 | (1..50_usize).prop_map(WriterTransition::WriteUpTo), | ||
| 26 | strategy::Just(WriterTransition::Reset), | ||
| 27 | ] | ||
| 28 | .boxed() | ||
| 29 | } | ||
| 30 | |||
| 31 | fn apply(status: Self::State, transition: &Self::Transition) -> Self::State { | ||
| 32 | match (status, transition) { | ||
| 33 | (_, WriterTransition::Reset) => Status::Available(CAP), | ||
| 34 | (Status::Available(x), WriterTransition::Read(y)) => { | ||
| 35 | if x < *y { | ||
| 36 | Status::Failed | ||
| 37 | } else { | ||
| 38 | Status::Available(x - y) | ||
| 39 | } | ||
| 40 | } | ||
| 41 | (Status::Failed, WriterTransition::Read(_)) => Status::Failed, | ||
| 42 | (Status::Available(x), WriterTransition::WriteUpTo(y)) => Status::Available((x + *y).min(CAP)), | ||
| 43 | (Status::Failed, WriterTransition::WriteUpTo(_)) => Status::Available(CAP), | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | struct WriterSut { | ||
| 49 | status: Status, | ||
| 50 | buffer: *mut [u8], | ||
| 51 | producer: WritableDmaRingBuffer<'static, u8>, | ||
| 52 | consumer: DmaMock, | ||
| 53 | } | ||
| 54 | |||
| 55 | impl Debug for WriterSut { | ||
| 56 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
| 57 | <DmaMock as Debug>::fmt(&self.consumer, f) | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | struct WriterTest; | ||
| 62 | |||
| 63 | impl StateMachineTest for WriterTest { | ||
| 64 | type SystemUnderTest = WriterSut; | ||
| 65 | type Reference = WriterSM; | ||
| 66 | |||
| 67 | fn init_test(ref_status: &<Self::Reference as ReferenceStateMachine>::State) -> Self::SystemUnderTest { | ||
| 68 | let buffer = Box::into_raw(Box::new([0; CAP])); | ||
| 69 | WriterSut { | ||
| 70 | status: ref_status.clone(), | ||
| 71 | buffer, | ||
| 72 | producer: WritableDmaRingBuffer::new(unsafe { &mut *buffer }), | ||
| 73 | consumer: DmaMock::default(), | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | fn teardown(state: Self::SystemUnderTest) { | ||
| 78 | unsafe { | ||
| 79 | let _ = Box::from_raw(state.buffer); | ||
| 80 | }; | ||
| 81 | } | ||
| 82 | |||
| 83 | fn apply( | ||
| 84 | mut sut: Self::SystemUnderTest, | ||
| 85 | ref_status: &<Self::Reference as ReferenceStateMachine>::State, | ||
| 86 | transition: <Self::Reference as ReferenceStateMachine>::Transition, | ||
| 87 | ) -> Self::SystemUnderTest { | ||
| 88 | match transition { | ||
| 89 | WriterTransition::Read(x) => sut.consumer.advance(x), | ||
| 90 | WriterTransition::Reset => { | ||
| 91 | sut.producer.reset(&mut sut.consumer); | ||
| 92 | } | ||
| 93 | WriterTransition::WriteUpTo(x) => { | ||
| 94 | let status = sut.status; | ||
| 95 | let WriterSut { | ||
| 96 | ref mut producer, | ||
| 97 | ref mut consumer, | ||
| 98 | .. | ||
| 99 | } = sut; | ||
| 100 | let mut buf = vec![0; x]; | ||
| 101 | let res = producer.write(consumer, &mut buf); | ||
| 102 | match status { | ||
| 103 | Status::Available(n) => { | ||
| 104 | let writable = x.min(CAP - n.min(CAP)); | ||
| 105 | assert_eq!(res.unwrap().0, writable); | ||
| 106 | } | ||
| 107 | Status::Failed => assert!(res.is_err()), | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | WriterSut { | ||
| 113 | status: ref_status.clone(), | ||
| 114 | ..sut | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | prop_state_machine! { | ||
| 120 | #[test] | ||
| 121 | fn writer_state_test(sequential 1..20 => WriterTest); | ||
| 122 | } | ||
diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 5aaca57c9..8bf89e2fe 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs | |||
| @@ -1,13 +1,12 @@ | |||
| 1 | use embassy_hal_internal::PeripheralRef; | ||
| 2 | |||
| 3 | use super::word::Word; | 1 | use super::word::Word; |
| 4 | use super::{AnyChannel, Request, Transfer, TransferOptions}; | 2 | use super::{AnyChannel, Request, Transfer, TransferOptions}; |
| 3 | use crate::Peri; | ||
| 5 | 4 | ||
| 6 | /// Convenience wrapper, contains a channel and a request number. | 5 | /// Convenience wrapper, contains a channel and a request number. |
| 7 | /// | 6 | /// |
| 8 | /// Commonly used in peripheral drivers that own DMA channels. | 7 | /// Commonly used in peripheral drivers that own DMA channels. |
| 9 | pub(crate) struct ChannelAndRequest<'d> { | 8 | pub(crate) struct ChannelAndRequest<'d> { |
| 10 | pub channel: PeripheralRef<'d, AnyChannel>, | 9 | pub channel: Peri<'d, AnyChannel>, |
| 11 | pub request: Request, | 10 | pub request: Request, |
| 12 | } | 11 | } |
| 13 | 12 | ||
| @@ -18,7 +17,7 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 18 | buf: &'a mut [W], | 17 | buf: &'a mut [W], |
| 19 | options: TransferOptions, | 18 | options: TransferOptions, |
| 20 | ) -> Transfer<'a> { | 19 | ) -> Transfer<'a> { |
| 21 | Transfer::new_read(&mut self.channel, self.request, peri_addr, buf, options) | 20 | Transfer::new_read(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 22 | } | 21 | } |
| 23 | 22 | ||
| 24 | pub unsafe fn read_raw<'a, W: Word>( | 23 | pub unsafe fn read_raw<'a, W: Word>( |
| @@ -27,7 +26,7 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 27 | buf: *mut [W], | 26 | buf: *mut [W], |
| 28 | options: TransferOptions, | 27 | options: TransferOptions, |
| 29 | ) -> Transfer<'a> { | 28 | ) -> Transfer<'a> { |
| 30 | Transfer::new_read_raw(&mut self.channel, self.request, peri_addr, buf, options) | 29 | Transfer::new_read_raw(self.channel.reborrow(), self.request, peri_addr, buf, options) |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | pub unsafe fn write<'a, W: Word>( | 32 | pub unsafe fn write<'a, W: Word>( |
| @@ -36,16 +35,16 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 36 | peri_addr: *mut W, | 35 | peri_addr: *mut W, |
| 37 | options: TransferOptions, | 36 | options: TransferOptions, |
| 38 | ) -> Transfer<'a> { | 37 | ) -> Transfer<'a> { |
| 39 | Transfer::new_write(&mut self.channel, self.request, buf, peri_addr, options) | 38 | Transfer::new_write(self.channel.reborrow(), self.request, buf, peri_addr, options) |
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | pub unsafe fn write_raw<'a, W: Word>( | 41 | pub unsafe fn write_raw<'a, MW: Word, PW: Word>( |
| 43 | &'a mut self, | 42 | &'a mut self, |
| 44 | buf: *const [W], | 43 | buf: *const [MW], |
| 45 | peri_addr: *mut W, | 44 | peri_addr: *mut PW, |
| 46 | options: TransferOptions, | 45 | options: TransferOptions, |
| 47 | ) -> Transfer<'a> { | 46 | ) -> Transfer<'a> { |
| 48 | Transfer::new_write_raw(&mut self.channel, self.request, buf, peri_addr, options) | 47 | Transfer::new_write_raw(self.channel.reborrow(), self.request, buf, peri_addr, options) |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | #[allow(dead_code)] | 50 | #[allow(dead_code)] |
| @@ -56,6 +55,13 @@ impl<'d> ChannelAndRequest<'d> { | |||
| 56 | peri_addr: *mut W, | 55 | peri_addr: *mut W, |
| 57 | options: TransferOptions, | 56 | options: TransferOptions, |
| 58 | ) -> Transfer<'a> { | 57 | ) -> Transfer<'a> { |
| 59 | Transfer::new_write_repeated(&mut self.channel, self.request, repeated, count, peri_addr, options) | 58 | Transfer::new_write_repeated( |
| 59 | self.channel.reborrow(), | ||
| 60 | self.request, | ||
| 61 | repeated, | ||
| 62 | count, | ||
| 63 | peri_addr, | ||
| 64 | options, | ||
| 65 | ) | ||
| 60 | } | 66 | } |
| 61 | } | 67 | } |
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index 77c3d95c3..e97ccd9d0 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs | |||
| @@ -2,12 +2,12 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 5 | use embassy_hal_internal::PeripheralType; |
| 6 | 6 | ||
| 7 | //use crate::gpio::{AnyPin, SealedPin}; | 7 | //use crate::gpio::{AnyPin, SealedPin}; |
| 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 8 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 9 | use crate::rcc::{self, RccPeripheral}; | 9 | use crate::rcc::{self, RccPeripheral}; |
| 10 | use crate::{peripherals, Peripheral}; | 10 | use crate::{peripherals, Peri}; |
| 11 | 11 | ||
| 12 | /// Performs a busy-wait delay for a specified number of microseconds. | 12 | /// Performs a busy-wait delay for a specified number of microseconds. |
| 13 | pub fn blocking_delay_ms(ms: u32) { | 13 | pub fn blocking_delay_ms(ms: u32) { |
| @@ -69,14 +69,12 @@ impl From<PacketType> for u8 { | |||
| 69 | /// DSIHOST driver. | 69 | /// DSIHOST driver. |
| 70 | pub struct DsiHost<'d, T: Instance> { | 70 | pub struct DsiHost<'d, T: Instance> { |
| 71 | _peri: PhantomData<&'d mut T>, | 71 | _peri: PhantomData<&'d mut T>, |
| 72 | _te: PeripheralRef<'d, AnyPin>, | 72 | _te: Peri<'d, AnyPin>, |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | impl<'d, T: Instance> DsiHost<'d, T> { | 75 | impl<'d, T: Instance> DsiHost<'d, T> { |
| 76 | /// Note: Full-Duplex modes are not supported at this time | 76 | /// Note: Full-Duplex modes are not supported at this time |
| 77 | pub fn new(_peri: impl Peripheral<P = T> + 'd, te: impl Peripheral<P = impl TePin<T>> + 'd) -> Self { | 77 | pub fn new(_peri: Peri<'d, T>, te: Peri<'d, impl TePin<T>>) -> Self { |
| 78 | into_ref!(te); | ||
| 79 | |||
| 80 | rcc::enable_and_reset::<T>(); | 78 | rcc::enable_and_reset::<T>(); |
| 81 | 79 | ||
| 82 | // Set Tearing Enable pin according to CubeMx example | 80 | // Set Tearing Enable pin according to CubeMx example |
| @@ -88,7 +86,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { | |||
| 88 | */ | 86 | */ |
| 89 | Self { | 87 | Self { |
| 90 | _peri: PhantomData, | 88 | _peri: PhantomData, |
| 91 | _te: te.map_into(), | 89 | _te: te.into(), |
| 92 | } | 90 | } |
| 93 | } | 91 | } |
| 94 | 92 | ||
| @@ -412,7 +410,7 @@ trait SealedInstance: crate::rcc::SealedRccPeripheral { | |||
| 412 | 410 | ||
| 413 | /// DSI instance trait. | 411 | /// DSI instance trait. |
| 414 | #[allow(private_bounds)] | 412 | #[allow(private_bounds)] |
| 415 | pub trait Instance: SealedInstance + RccPeripheral + 'static {} | 413 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static {} |
| 416 | 414 | ||
| 417 | pin_trait!(TePin, Instance); | 415 | pin_trait!(TePin, Instance); |
| 418 | 416 | ||
diff --git a/embassy-stm32/src/dts/mod.rs b/embassy-stm32/src/dts/mod.rs new file mode 100644 index 000000000..1f39c8db5 --- /dev/null +++ b/embassy-stm32/src/dts/mod.rs | |||
| @@ -0,0 +1,238 @@ | |||
| 1 | //! Digital Temperature Sensor (DTS) | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_hal_internal::Peri; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | |||
| 10 | use crate::interrupt::InterruptExt; | ||
| 11 | use crate::peripherals::DTS; | ||
| 12 | use crate::time::Hertz; | ||
| 13 | use crate::{interrupt, pac, rcc}; | ||
| 14 | |||
| 15 | mod tsel; | ||
| 16 | pub use tsel::TriggerSel; | ||
| 17 | |||
| 18 | #[allow(missing_docs)] | ||
| 19 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] | ||
| 20 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 21 | pub enum SampleTime { | ||
| 22 | ClockCycles1 = 1, | ||
| 23 | ClockCycles2 = 2, | ||
| 24 | ClockCycles3 = 3, | ||
| 25 | ClockCycles4 = 4, | ||
| 26 | ClockCycles5 = 5, | ||
| 27 | ClockCycles6 = 6, | ||
| 28 | ClockCycles7 = 7, | ||
| 29 | ClockCycles8 = 8, | ||
| 30 | ClockCycles9 = 9, | ||
| 31 | ClockCycles10 = 10, | ||
| 32 | ClockCycles11 = 11, | ||
| 33 | ClockCycles12 = 12, | ||
| 34 | ClockCycles13 = 13, | ||
| 35 | ClockCycles14 = 14, | ||
| 36 | ClockCycles15 = 15, | ||
| 37 | } | ||
| 38 | |||
| 39 | #[non_exhaustive] | ||
| 40 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 41 | /// Config | ||
| 42 | pub struct Config { | ||
| 43 | /// Sample time | ||
| 44 | pub sample_time: SampleTime, | ||
| 45 | /// Trigger selection | ||
| 46 | pub trigger: TriggerSel, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl Default for Config { | ||
| 50 | fn default() -> Self { | ||
| 51 | Self { | ||
| 52 | sample_time: SampleTime::ClockCycles1, | ||
| 53 | trigger: TriggerSel::Software, | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// The read-only factory calibration values used for converting a | ||
| 59 | /// measurement to a temperature. | ||
| 60 | #[derive(Clone, Debug)] | ||
| 61 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 62 | pub struct FactoryCalibration { | ||
| 63 | /// The calibration temperature in degrees Celsius. | ||
| 64 | pub t0: u8, | ||
| 65 | /// The frequency at the calibration temperature. | ||
| 66 | pub fmt0: Hertz, | ||
| 67 | /// The ramp coefficient in Hertz per degree Celsius. | ||
| 68 | pub ramp_coeff: u16, | ||
| 69 | } | ||
| 70 | |||
| 71 | const MAX_DTS_CLK_FREQ: Hertz = Hertz::mhz(1); | ||
| 72 | |||
| 73 | /// Digital temperature sensor driver. | ||
| 74 | pub struct Dts<'d> { | ||
| 75 | _peri: Peri<'d, DTS>, | ||
| 76 | } | ||
| 77 | |||
| 78 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 79 | |||
| 80 | impl<'d> Dts<'d> { | ||
| 81 | /// Create a new temperature sensor driver. | ||
| 82 | pub fn new( | ||
| 83 | _peri: Peri<'d, DTS>, | ||
| 84 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::DTS, InterruptHandler> + 'd, | ||
| 85 | config: Config, | ||
| 86 | ) -> Self { | ||
| 87 | rcc::enable_and_reset::<DTS>(); | ||
| 88 | |||
| 89 | let prescaler = rcc::frequency::<DTS>() / MAX_DTS_CLK_FREQ; | ||
| 90 | |||
| 91 | if prescaler > 127 { | ||
| 92 | panic!("DTS PCLK frequency must be less than 127 MHz."); | ||
| 93 | } | ||
| 94 | |||
| 95 | Self::regs().cfgr1().modify(|w| { | ||
| 96 | w.set_refclk_sel(false); | ||
| 97 | w.set_hsref_clk_div(prescaler as u8); | ||
| 98 | w.set_q_meas_opt(false); | ||
| 99 | // Software trigger | ||
| 100 | w.set_intrig_sel(0); | ||
| 101 | w.set_smp_time(config.sample_time as u8); | ||
| 102 | w.set_intrig_sel(config.trigger as u8); | ||
| 103 | w.set_start(true); | ||
| 104 | w.set_en(true); | ||
| 105 | }); | ||
| 106 | |||
| 107 | interrupt::DTS.unpend(); | ||
| 108 | unsafe { interrupt::DTS.enable() }; | ||
| 109 | |||
| 110 | Self { _peri } | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Reconfigure the driver. | ||
| 114 | pub fn set_config(&mut self, config: &Config) { | ||
| 115 | Self::regs().cfgr1().modify(|w| { | ||
| 116 | w.set_smp_time(config.sample_time as u8); | ||
| 117 | w.set_intrig_sel(config.trigger as u8); | ||
| 118 | }); | ||
| 119 | } | ||
| 120 | |||
| 121 | /// Get the read-only factory calibration values used for converting a | ||
| 122 | /// measurement to a temperature. | ||
| 123 | pub fn factory_calibration() -> FactoryCalibration { | ||
| 124 | let t0valr1 = Self::regs().t0valr1().read(); | ||
| 125 | let t0 = match t0valr1.t0() { | ||
| 126 | 0 => 30, | ||
| 127 | 1 => 130, | ||
| 128 | _ => unimplemented!(), | ||
| 129 | }; | ||
| 130 | let fmt0 = Hertz::hz(t0valr1.fmt0() as u32 * 100); | ||
| 131 | |||
| 132 | let ramp_coeff = Self::regs().rampvalr().read().ramp_coeff(); | ||
| 133 | |||
| 134 | FactoryCalibration { t0, fmt0, ramp_coeff } | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Perform an asynchronous temperature measurement. The returned future can | ||
| 138 | /// be awaited to obtain the measurement. | ||
| 139 | /// | ||
| 140 | /// The future returned waits for the next measurement to complete. | ||
| 141 | /// | ||
| 142 | /// # Example | ||
| 143 | /// | ||
| 144 | /// ```no_run | ||
| 145 | /// use embassy_stm32::{bind_interrupts, dts}; | ||
| 146 | /// use embassy_stm32::dts::Dts; | ||
| 147 | /// | ||
| 148 | /// bind_interrupts!(struct Irqs { | ||
| 149 | /// DTS => temp::InterruptHandler; | ||
| 150 | /// }); | ||
| 151 | /// | ||
| 152 | /// # async { | ||
| 153 | /// # let p: embassy_stm32::Peripherals = todo!(); | ||
| 154 | /// let mut dts = Dts::new(p.DTS, Irqs, Default::default()); | ||
| 155 | /// let v: u16 = dts.read().await; | ||
| 156 | /// # }; | ||
| 157 | /// ``` | ||
| 158 | pub async fn read(&mut self) -> u16 { | ||
| 159 | let r = Self::regs(); | ||
| 160 | |||
| 161 | r.itenr().modify(|w| w.set_iteen(true)); | ||
| 162 | |||
| 163 | poll_fn(|cx| { | ||
| 164 | WAKER.register(cx.waker()); | ||
| 165 | if r.itenr().read().iteen() { | ||
| 166 | Poll::Pending | ||
| 167 | } else { | ||
| 168 | Poll::Ready(r.dr().read().mfreq()) | ||
| 169 | } | ||
| 170 | }) | ||
| 171 | .await | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Returns the last measurement made, if any. | ||
| 175 | /// | ||
| 176 | /// There is no guarantee that the measurement is recent or that a | ||
| 177 | /// measurement has ever completed. | ||
| 178 | pub fn read_immediate(&mut self) -> u16 { | ||
| 179 | Self::regs().dr().read().mfreq() | ||
| 180 | } | ||
| 181 | |||
| 182 | fn regs() -> pac::dts::Dts { | ||
| 183 | pac::DTS | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | impl<'d> Drop for Dts<'d> { | ||
| 188 | fn drop(&mut self) { | ||
| 189 | Self::regs().cfgr1().modify(|w| w.set_en(false)); | ||
| 190 | rcc::disable::<DTS>(); | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Interrupt handler. | ||
| 195 | pub struct InterruptHandler { | ||
| 196 | _private: (), | ||
| 197 | } | ||
| 198 | |||
| 199 | impl interrupt::typelevel::Handler<interrupt::typelevel::DTS> for InterruptHandler { | ||
| 200 | unsafe fn on_interrupt() { | ||
| 201 | let r = pac::DTS; | ||
| 202 | let (sr, itenr) = (r.sr().read(), r.itenr().read()); | ||
| 203 | |||
| 204 | if (itenr.iteen() && sr.itef()) || (itenr.aiteen() && sr.aitef()) { | ||
| 205 | r.itenr().modify(|w| { | ||
| 206 | w.set_iteen(false); | ||
| 207 | w.set_aiteen(false); | ||
| 208 | }); | ||
| 209 | r.icifr().modify(|w| { | ||
| 210 | w.set_citef(true); | ||
| 211 | w.set_caitef(true); | ||
| 212 | }); | ||
| 213 | } else if (itenr.itlen() && sr.itlf()) || (itenr.aitlen() && sr.aitlf()) { | ||
| 214 | r.itenr().modify(|w| { | ||
| 215 | w.set_itlen(false); | ||
| 216 | w.set_aitlen(false); | ||
| 217 | }); | ||
| 218 | r.icifr().modify(|w| { | ||
| 219 | w.set_citlf(true); | ||
| 220 | w.set_caitlf(true); | ||
| 221 | }); | ||
| 222 | } else if (itenr.ithen() && sr.ithf()) || (itenr.aithen() && sr.aithf()) { | ||
| 223 | r.itenr().modify(|w| { | ||
| 224 | w.set_ithen(false); | ||
| 225 | w.set_aithen(false); | ||
| 226 | }); | ||
| 227 | r.icifr().modify(|w| { | ||
| 228 | w.set_cithf(true); | ||
| 229 | w.set_caithf(true); | ||
| 230 | }); | ||
| 231 | } else { | ||
| 232 | return; | ||
| 233 | } | ||
| 234 | |||
| 235 | compiler_fence(Ordering::SeqCst); | ||
| 236 | WAKER.wake(); | ||
| 237 | } | ||
| 238 | } | ||
diff --git a/embassy-stm32/src/dts/tsel.rs b/embassy-stm32/src/dts/tsel.rs new file mode 100644 index 000000000..99eab6dd8 --- /dev/null +++ b/embassy-stm32/src/dts/tsel.rs | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /// Trigger selection for H5 | ||
| 2 | #[cfg(stm32h5)] | ||
| 3 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 4 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 5 | pub enum TriggerSel { | ||
| 6 | /// Software triggering. Performs continuous measurements. | ||
| 7 | Software = 0, | ||
| 8 | /// LPTIM1 CH1 | ||
| 9 | Lptim1 = 1, | ||
| 10 | /// LPTIM2 CH1 | ||
| 11 | Lptim2 = 2, | ||
| 12 | /// LPTIM3 CH1 | ||
| 13 | #[cfg(not(stm32h503))] | ||
| 14 | Lptim3 = 3, | ||
| 15 | /// EXTI13 | ||
| 16 | Exti13 = 4, | ||
| 17 | } | ||
| 18 | |||
| 19 | /// Trigger selection for H7, except for H7R and H7S | ||
| 20 | #[cfg(stm32h7)] | ||
| 21 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 22 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 23 | pub enum TriggerSel { | ||
| 24 | /// Software triggering. Performs continuous measurements. | ||
| 25 | Software = 0, | ||
| 26 | /// LPTIM1 OUT | ||
| 27 | Lptim1 = 1, | ||
| 28 | /// LPTIM2 OUT | ||
| 29 | Lptim2 = 2, | ||
| 30 | /// LPTIM3 OUT | ||
| 31 | Lptim3 = 3, | ||
| 32 | /// EXTI13 | ||
| 33 | Exti13 = 4, | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Trigger selection for H7R and H7S | ||
| 37 | #[cfg(stm32h7rs)] | ||
| 38 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 40 | pub enum TriggerSel { | ||
| 41 | /// Software triggering. Performs continuous measurements. | ||
| 42 | Software = 0, | ||
| 43 | /// LPTIM4 OUT | ||
| 44 | Lptim4 = 1, | ||
| 45 | /// LPTIM2 CH1 | ||
| 46 | Lptim2 = 2, | ||
| 47 | /// LPTIM3 CH1 | ||
| 48 | Lptim3 = 3, | ||
| 49 | /// EXTI13 | ||
| 50 | Exti13 = 4, | ||
| 51 | } | ||
diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_phy.rs index 3b43051f4..774beef80 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_phy.rs | |||
| @@ -7,7 +7,7 @@ use embassy_time::{Duration, Timer}; | |||
| 7 | #[cfg(feature = "time")] | 7 | #[cfg(feature = "time")] |
| 8 | use futures_util::FutureExt; | 8 | use futures_util::FutureExt; |
| 9 | 9 | ||
| 10 | use super::{StationManagement, PHY}; | 10 | use super::{Phy, StationManagement}; |
| 11 | 11 | ||
| 12 | #[allow(dead_code)] | 12 | #[allow(dead_code)] |
| 13 | mod phy_consts { | 13 | mod phy_consts { |
| @@ -43,25 +43,71 @@ mod phy_consts { | |||
| 43 | use self::phy_consts::*; | 43 | use self::phy_consts::*; |
| 44 | 44 | ||
| 45 | /// Generic SMI Ethernet PHY implementation | 45 | /// Generic SMI Ethernet PHY implementation |
| 46 | pub struct GenericSMI { | 46 | pub struct GenericPhy { |
| 47 | phy_addr: u8, | 47 | phy_addr: u8, |
| 48 | #[cfg(feature = "time")] | 48 | #[cfg(feature = "time")] |
| 49 | poll_interval: Duration, | 49 | poll_interval: Duration, |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | impl GenericSMI { | 52 | impl GenericPhy { |
| 53 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication | 53 | /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication |
| 54 | /// | ||
| 55 | /// # Panics | ||
| 56 | /// `phy_addr` must be in range `0..32` | ||
| 54 | pub fn new(phy_addr: u8) -> Self { | 57 | pub fn new(phy_addr: u8) -> Self { |
| 58 | assert!(phy_addr < 32); | ||
| 55 | Self { | 59 | Self { |
| 56 | phy_addr, | 60 | phy_addr, |
| 57 | #[cfg(feature = "time")] | 61 | #[cfg(feature = "time")] |
| 58 | poll_interval: Duration::from_millis(500), | 62 | poll_interval: Duration::from_millis(500), |
| 59 | } | 63 | } |
| 60 | } | 64 | } |
| 65 | |||
| 66 | /// Construct the PHY. Try to probe all addresses from 0 to 31 during initialization | ||
| 67 | /// | ||
| 68 | /// # Panics | ||
| 69 | /// Initialization panics if PHY didn't respond on any address | ||
| 70 | pub fn new_auto() -> Self { | ||
| 71 | Self { | ||
| 72 | phy_addr: 0xFF, | ||
| 73 | #[cfg(feature = "time")] | ||
| 74 | poll_interval: Duration::from_millis(500), | ||
| 75 | } | ||
| 76 | } | ||
| 61 | } | 77 | } |
| 62 | 78 | ||
| 63 | unsafe impl PHY for GenericSMI { | 79 | // TODO: Factor out to shared functionality |
| 80 | fn blocking_delay_us(us: u32) { | ||
| 81 | #[cfg(feature = "time")] | ||
| 82 | embassy_time::block_for(Duration::from_micros(us as u64)); | ||
| 83 | #[cfg(not(feature = "time"))] | ||
| 84 | { | ||
| 85 | let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; | ||
| 86 | let us = us as u64; | ||
| 87 | let cycles = freq * us / 1_000_000; | ||
| 88 | cortex_m::asm::delay(cycles as u32); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | impl Phy for GenericPhy { | ||
| 64 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { | 93 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) { |
| 94 | // Detect SMI address | ||
| 95 | if self.phy_addr == 0xFF { | ||
| 96 | for addr in 0..32 { | ||
| 97 | sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | ||
| 98 | for _ in 0..10 { | ||
| 99 | if sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { | ||
| 100 | trace!("Found ETH PHY on address {}", addr); | ||
| 101 | self.phy_addr = addr; | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | // Give PHY a total of 100ms to respond | ||
| 105 | blocking_delay_us(10000); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | panic!("PHY did not respond"); | ||
| 109 | } | ||
| 110 | |||
| 65 | sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); | 111 | sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); |
| 66 | while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} | 112 | while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} |
| 67 | } | 113 | } |
| @@ -102,7 +148,7 @@ unsafe impl PHY for GenericSMI { | |||
| 102 | } | 148 | } |
| 103 | 149 | ||
| 104 | /// Public functions for the PHY | 150 | /// Public functions for the PHY |
| 105 | impl GenericSMI { | 151 | impl GenericPhy { |
| 106 | /// Set the SMI polling interval. | 152 | /// Set the SMI polling interval. |
| 107 | #[cfg(feature = "time")] | 153 | #[cfg(feature = "time")] |
| 108 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { | 154 | pub fn set_poll_interval(&mut self, poll_interval: Duration) { |
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index bfe8a60d6..97d7b4347 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs | |||
| @@ -4,15 +4,17 @@ | |||
| 4 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] | 4 | #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] |
| 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] | 5 | #[cfg_attr(eth_v2, path = "v2/mod.rs")] |
| 6 | mod _version; | 6 | mod _version; |
| 7 | pub mod generic_smi; | 7 | mod generic_phy; |
| 8 | 8 | ||
| 9 | use core::mem::MaybeUninit; | 9 | use core::mem::MaybeUninit; |
| 10 | use core::task::Context; | 10 | use core::task::Context; |
| 11 | 11 | ||
| 12 | use embassy_hal_internal::PeripheralType; | ||
| 12 | use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; | 13 | use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; |
| 13 | use embassy_sync::waitqueue::AtomicWaker; | 14 | use embassy_sync::waitqueue::AtomicWaker; |
| 14 | 15 | ||
| 15 | pub use self::_version::{InterruptHandler, *}; | 16 | pub use self::_version::{InterruptHandler, *}; |
| 17 | pub use self::generic_phy::*; | ||
| 16 | use crate::rcc::RccPeripheral; | 18 | use crate::rcc::RccPeripheral; |
| 17 | 19 | ||
| 18 | #[allow(unused)] | 20 | #[allow(unused)] |
| @@ -42,11 +44,9 @@ pub struct PacketQueue<const TX: usize, const RX: usize> { | |||
| 42 | impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> { | 44 | impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> { |
| 43 | /// Create a new packet queue. | 45 | /// Create a new packet queue. |
| 44 | pub const fn new() -> Self { | 46 | pub const fn new() -> Self { |
| 45 | const NEW_TDES: TDes = TDes::new(); | ||
| 46 | const NEW_RDES: RDes = RDes::new(); | ||
| 47 | Self { | 47 | Self { |
| 48 | tx_desc: [NEW_TDES; TX], | 48 | tx_desc: [const { TDes::new() }; TX], |
| 49 | rx_desc: [NEW_RDES; RX], | 49 | rx_desc: [const { RDes::new() }; RX], |
| 50 | tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], | 50 | tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], |
| 51 | rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], | 51 | rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], |
| 52 | } | 52 | } |
| @@ -73,9 +73,15 @@ impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> { | |||
| 73 | 73 | ||
| 74 | static WAKER: AtomicWaker = AtomicWaker::new(); | 74 | static WAKER: AtomicWaker = AtomicWaker::new(); |
| 75 | 75 | ||
| 76 | impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> { | 76 | impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> { |
| 77 | type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; | 77 | type RxToken<'a> |
| 78 | type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; | 78 | = RxToken<'a, 'd> |
| 79 | where | ||
| 80 | Self: 'a; | ||
| 81 | type TxToken<'a> | ||
| 82 | = TxToken<'a, 'd> | ||
| 83 | where | ||
| 84 | Self: 'a; | ||
| 79 | 85 | ||
| 80 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | 86 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| 81 | WAKER.register(cx.waker()); | 87 | WAKER.register(cx.waker()); |
| @@ -152,23 +158,15 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { | |||
| 152 | } | 158 | } |
| 153 | 159 | ||
| 154 | /// Station Management Interface (SMI) on an ethernet PHY | 160 | /// Station Management Interface (SMI) on an ethernet PHY |
| 155 | /// | 161 | pub trait StationManagement { |
| 156 | /// # Safety | ||
| 157 | /// | ||
| 158 | /// The methods cannot move out of self | ||
| 159 | pub unsafe trait StationManagement { | ||
| 160 | /// Read a register over SMI. | 162 | /// Read a register over SMI. |
| 161 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; | 163 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; |
| 162 | /// Write a register over SMI. | 164 | /// Write a register over SMI. |
| 163 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); | 165 | fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); |
| 164 | } | 166 | } |
| 165 | 167 | ||
| 166 | /// Traits for an Ethernet PHY | 168 | /// Trait for an Ethernet PHY |
| 167 | /// | 169 | pub trait Phy { |
| 168 | /// # Safety | ||
| 169 | /// | ||
| 170 | /// The methods cannot move S | ||
| 171 | pub unsafe trait PHY { | ||
| 172 | /// Reset PHY and wait for it to come out of reset. | 170 | /// Reset PHY and wait for it to come out of reset. |
| 173 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S); | 171 | fn phy_reset<S: StationManagement>(&mut self, sm: &mut S); |
| 174 | /// PHY initialisation. | 172 | /// PHY initialisation. |
| @@ -177,13 +175,32 @@ pub unsafe trait PHY { | |||
| 177 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; | 175 | fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool; |
| 178 | } | 176 | } |
| 179 | 177 | ||
| 178 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { | ||
| 179 | /// Directly expose the SMI interface used by the Ethernet driver. | ||
| 180 | /// | ||
| 181 | /// This can be used to for example configure special PHY registers for compliance testing. | ||
| 182 | pub fn station_management(&mut self) -> &mut impl StationManagement { | ||
| 183 | &mut self.station_management | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Access the user-supplied `Phy`. | ||
| 187 | pub fn phy(&self) -> &P { | ||
| 188 | &self.phy | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Mutably access the user-supplied `Phy`. | ||
| 192 | pub fn phy_mut(&mut self) -> &mut P { | ||
| 193 | &mut self.phy | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 180 | trait SealedInstance { | 197 | trait SealedInstance { |
| 181 | fn regs() -> crate::pac::eth::Eth; | 198 | fn regs() -> crate::pac::eth::Eth; |
| 182 | } | 199 | } |
| 183 | 200 | ||
| 184 | /// Ethernet instance. | 201 | /// Ethernet instance. |
| 185 | #[allow(private_bounds)] | 202 | #[allow(private_bounds)] |
| 186 | pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {} | 203 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} |
| 187 | 204 | ||
| 188 | impl SealedInstance for crate::peripherals::ETH { | 205 | impl SealedInstance for crate::peripherals::ETH { |
| 189 | fn regs() -> crate::pac::eth::Eth { | 206 | fn regs() -> crate::pac::eth::Eth { |
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index cce75ece7..01e321bce 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -6,7 +6,7 @@ mod tx_desc; | |||
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::sync::atomic::{fence, Ordering}; | 7 | use core::sync::atomic::{fence, Ordering}; |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 9 | use embassy_hal_internal::Peri; |
| 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; | 10 | use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; |
| 11 | 11 | ||
| 12 | pub(crate) use self::rx_desc::{RDes, RDesRing}; | 12 | pub(crate) use self::rx_desc::{RDes, RDesRing}; |
| @@ -15,6 +15,7 @@ use super::*; | |||
| 15 | #[cfg(eth_v1a)] | 15 | #[cfg(eth_v1a)] |
| 16 | use crate::gpio::Pull; | 16 | use crate::gpio::Pull; |
| 17 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 17 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 18 | use crate::interrupt; | ||
| 18 | use crate::interrupt::InterruptExt; | 19 | use crate::interrupt::InterruptExt; |
| 19 | #[cfg(eth_v1a)] | 20 | #[cfg(eth_v1a)] |
| 20 | use crate::pac::AFIO; | 21 | use crate::pac::AFIO; |
| @@ -22,7 +23,6 @@ use crate::pac::AFIO; | |||
| 22 | use crate::pac::SYSCFG; | 23 | use crate::pac::SYSCFG; |
| 23 | use crate::pac::{ETH, RCC}; | 24 | use crate::pac::{ETH, RCC}; |
| 24 | use crate::rcc::SealedRccPeripheral; | 25 | use crate::rcc::SealedRccPeripheral; |
| 25 | use crate::{interrupt, Peripheral}; | ||
| 26 | 26 | ||
| 27 | /// Interrupt handler. | 27 | /// Interrupt handler. |
| 28 | pub struct InterruptHandler {} | 28 | pub struct InterruptHandler {} |
| @@ -46,17 +46,23 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl | |||
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | /// Ethernet driver. | 48 | /// Ethernet driver. |
| 49 | pub struct Ethernet<'d, T: Instance, P: PHY> { | 49 | pub struct Ethernet<'d, T: Instance, P: Phy> { |
| 50 | _peri: PeripheralRef<'d, T>, | 50 | _peri: Peri<'d, T>, |
| 51 | pub(crate) tx: TDesRing<'d>, | 51 | pub(crate) tx: TDesRing<'d>, |
| 52 | pub(crate) rx: RDesRing<'d>, | 52 | pub(crate) rx: RDesRing<'d>, |
| 53 | 53 | ||
| 54 | pins: [PeripheralRef<'d, AnyPin>; 9], | 54 | pins: Pins<'d>, |
| 55 | pub(crate) phy: P, | 55 | pub(crate) phy: P, |
| 56 | pub(crate) station_management: EthernetStationManagement<T>, | 56 | pub(crate) station_management: EthernetStationManagement<T>, |
| 57 | pub(crate) mac_addr: [u8; 6], | 57 | pub(crate) mac_addr: [u8; 6], |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | /// Pins of ethernet driver. | ||
| 61 | enum Pins<'d> { | ||
| 62 | Rmii([Peri<'d, AnyPin>; 9]), | ||
| 63 | Mii([Peri<'d, AnyPin>; 14]), | ||
| 64 | } | ||
| 65 | |||
| 60 | #[cfg(eth_v1a)] | 66 | #[cfg(eth_v1a)] |
| 61 | macro_rules! config_in_pins { | 67 | macro_rules! config_in_pins { |
| 62 | ($($pin:ident),*) => { | 68 | ($($pin:ident),*) => { |
| @@ -91,26 +97,24 @@ macro_rules! config_pins { | |||
| 91 | }; | 97 | }; |
| 92 | } | 98 | } |
| 93 | 99 | ||
| 94 | impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | 100 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 95 | /// safety: the returned instance is not leak-safe | 101 | /// safety: the returned instance is not leak-safe |
| 96 | pub fn new<const TX: usize, const RX: usize>( | 102 | pub fn new<const TX: usize, const RX: usize>( |
| 97 | queue: &'d mut PacketQueue<TX, RX>, | 103 | queue: &'d mut PacketQueue<TX, RX>, |
| 98 | peri: impl Peripheral<P = T> + 'd, | 104 | peri: Peri<'d, T>, |
| 99 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 105 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 100 | ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd, | 106 | ref_clk: Peri<'d, impl RefClkPin<T>>, |
| 101 | mdio: impl Peripheral<P = impl MDIOPin<T>> + 'd, | 107 | mdio: Peri<'d, impl MDIOPin<T>>, |
| 102 | mdc: impl Peripheral<P = impl MDCPin<T>> + 'd, | 108 | mdc: Peri<'d, impl MDCPin<T>>, |
| 103 | crs: impl Peripheral<P = impl CRSPin<T>> + 'd, | 109 | crs: Peri<'d, impl CRSPin<T>>, |
| 104 | rx_d0: impl Peripheral<P = impl RXD0Pin<T>> + 'd, | 110 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 105 | rx_d1: impl Peripheral<P = impl RXD1Pin<T>> + 'd, | 111 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| 106 | tx_d0: impl Peripheral<P = impl TXD0Pin<T>> + 'd, | 112 | tx_d0: Peri<'d, impl TXD0Pin<T>>, |
| 107 | tx_d1: impl Peripheral<P = impl TXD1Pin<T>> + 'd, | 113 | tx_d1: Peri<'d, impl TXD1Pin<T>>, |
| 108 | tx_en: impl Peripheral<P = impl TXEnPin<T>> + 'd, | 114 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 109 | phy: P, | 115 | phy: P, |
| 110 | mac_addr: [u8; 6], | 116 | mac_addr: [u8; 6], |
| 111 | ) -> Self { | 117 | ) -> Self { |
| 112 | into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 113 | |||
| 114 | // Enable the necessary Clocks | 118 | // Enable the necessary Clocks |
| 115 | #[cfg(eth_v1a)] | 119 | #[cfg(eth_v1a)] |
| 116 | critical_section::with(|_| { | 120 | critical_section::with(|_| { |
| @@ -148,6 +152,29 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 148 | #[cfg(any(eth_v1b, eth_v1c))] | 152 | #[cfg(any(eth_v1b, eth_v1c))] |
| 149 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 153 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 150 | 154 | ||
| 155 | let pins = Pins::Rmii([ | ||
| 156 | ref_clk.into(), | ||
| 157 | mdio.into(), | ||
| 158 | mdc.into(), | ||
| 159 | crs.into(), | ||
| 160 | rx_d0.into(), | ||
| 161 | rx_d1.into(), | ||
| 162 | tx_d0.into(), | ||
| 163 | tx_d1.into(), | ||
| 164 | tx_en.into(), | ||
| 165 | ]); | ||
| 166 | |||
| 167 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | ||
| 168 | } | ||
| 169 | |||
| 170 | fn new_inner<const TX: usize, const RX: usize>( | ||
| 171 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 172 | peri: Peri<'d, T>, | ||
| 173 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 174 | pins: Pins<'d>, | ||
| 175 | phy: P, | ||
| 176 | mac_addr: [u8; 6], | ||
| 177 | ) -> Self { | ||
| 151 | let dma = T::regs().ethernet_dma(); | 178 | let dma = T::regs().ethernet_dma(); |
| 152 | let mac = T::regs().ethernet_mac(); | 179 | let mac = T::regs().ethernet_mac(); |
| 153 | 180 | ||
| @@ -159,8 +186,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 159 | w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times | 186 | w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times |
| 160 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping | 187 | w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping |
| 161 | w.set_fes(Fes::FES100); // fast ethernet speed | 188 | w.set_fes(Fes::FES100); // fast ethernet speed |
| 162 | w.set_dm(Dm::FULLDUPLEX); // full duplex | 189 | w.set_dm(Dm::FULL_DUPLEX); // full duplex |
| 163 | // TODO: Carrier sense ? ECRSFD | 190 | // TODO: Carrier sense ? ECRSFD |
| 191 | }); | ||
| 192 | |||
| 193 | // Set the mac to pass all multicast packets | ||
| 194 | mac.macffr().modify(|w| { | ||
| 195 | w.set_pam(true); | ||
| 164 | }); | 196 | }); |
| 165 | 197 | ||
| 166 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, | 198 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, |
| @@ -181,8 +213,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 181 | 213 | ||
| 182 | // Transfer and Forward, Receive and Forward | 214 | // Transfer and Forward, Receive and Forward |
| 183 | dma.dmaomr().modify(|w| { | 215 | dma.dmaomr().modify(|w| { |
| 184 | w.set_tsf(Tsf::STOREFORWARD); | 216 | w.set_tsf(Tsf::STORE_FORWARD); |
| 185 | w.set_rsf(Rsf::STOREFORWARD); | 217 | w.set_rsf(Rsf::STORE_FORWARD); |
| 186 | }); | 218 | }); |
| 187 | 219 | ||
| 188 | dma.dmabmr().modify(|w| { | 220 | dma.dmabmr().modify(|w| { |
| @@ -207,18 +239,6 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 207 | } | 239 | } |
| 208 | }; | 240 | }; |
| 209 | 241 | ||
| 210 | let pins = [ | ||
| 211 | ref_clk.map_into(), | ||
| 212 | mdio.map_into(), | ||
| 213 | mdc.map_into(), | ||
| 214 | crs.map_into(), | ||
| 215 | rx_d0.map_into(), | ||
| 216 | rx_d1.map_into(), | ||
| 217 | tx_d0.map_into(), | ||
| 218 | tx_d1.map_into(), | ||
| 219 | tx_en.map_into(), | ||
| 220 | ]; | ||
| 221 | |||
| 222 | let mut this = Self { | 242 | let mut this = Self { |
| 223 | _peri: peri, | 243 | _peri: peri, |
| 224 | pins, | 244 | pins, |
| @@ -264,15 +284,96 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 264 | 284 | ||
| 265 | this | 285 | this |
| 266 | } | 286 | } |
| 287 | |||
| 288 | /// Create a new MII ethernet driver using 14 pins. | ||
| 289 | pub fn new_mii<const TX: usize, const RX: usize>( | ||
| 290 | queue: &'d mut PacketQueue<TX, RX>, | ||
| 291 | peri: Peri<'d, T>, | ||
| 292 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | ||
| 293 | rx_clk: Peri<'d, impl RXClkPin<T>>, | ||
| 294 | tx_clk: Peri<'d, impl TXClkPin<T>>, | ||
| 295 | mdio: Peri<'d, impl MDIOPin<T>>, | ||
| 296 | mdc: Peri<'d, impl MDCPin<T>>, | ||
| 297 | rxdv: Peri<'d, impl RXDVPin<T>>, | ||
| 298 | rx_d0: Peri<'d, impl RXD0Pin<T>>, | ||
| 299 | rx_d1: Peri<'d, impl RXD1Pin<T>>, | ||
| 300 | rx_d2: Peri<'d, impl RXD2Pin<T>>, | ||
| 301 | rx_d3: Peri<'d, impl RXD3Pin<T>>, | ||
| 302 | tx_d0: Peri<'d, impl TXD0Pin<T>>, | ||
| 303 | tx_d1: Peri<'d, impl TXD1Pin<T>>, | ||
| 304 | tx_d2: Peri<'d, impl TXD2Pin<T>>, | ||
| 305 | tx_d3: Peri<'d, impl TXD3Pin<T>>, | ||
| 306 | tx_en: Peri<'d, impl TXEnPin<T>>, | ||
| 307 | phy: P, | ||
| 308 | mac_addr: [u8; 6], | ||
| 309 | ) -> Self { | ||
| 310 | // TODO: Handle optional signals like CRS, MII_COL, RX_ER? | ||
| 311 | |||
| 312 | // Enable the necessary Clocks | ||
| 313 | #[cfg(eth_v1a)] | ||
| 314 | critical_section::with(|_| { | ||
| 315 | RCC.apb2enr().modify(|w| w.set_afioen(true)); | ||
| 316 | |||
| 317 | // Select MII (Media Independent Interface) | ||
| 318 | // Must be done prior to enabling peripheral clock | ||
| 319 | AFIO.mapr().modify(|w| w.set_mii_rmii_sel(false)); | ||
| 320 | |||
| 321 | RCC.ahbenr().modify(|w| { | ||
| 322 | w.set_ethen(true); | ||
| 323 | w.set_ethtxen(true); | ||
| 324 | w.set_ethrxen(true); | ||
| 325 | }); | ||
| 326 | }); | ||
| 327 | |||
| 328 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 329 | critical_section::with(|_| { | ||
| 330 | RCC.ahb1enr().modify(|w| { | ||
| 331 | w.set_ethen(true); | ||
| 332 | w.set_ethtxen(true); | ||
| 333 | w.set_ethrxen(true); | ||
| 334 | }); | ||
| 335 | |||
| 336 | // MII (Media Independent Interface) | ||
| 337 | SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(false)); | ||
| 338 | }); | ||
| 339 | |||
| 340 | #[cfg(eth_v1a)] | ||
| 341 | { | ||
| 342 | config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); | ||
| 343 | config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | ||
| 344 | } | ||
| 345 | |||
| 346 | #[cfg(any(eth_v1b, eth_v1c))] | ||
| 347 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | ||
| 348 | |||
| 349 | let pins = Pins::Mii([ | ||
| 350 | rx_clk.into(), | ||
| 351 | tx_clk.into(), | ||
| 352 | mdio.into(), | ||
| 353 | mdc.into(), | ||
| 354 | rxdv.into(), | ||
| 355 | rx_d0.into(), | ||
| 356 | rx_d1.into(), | ||
| 357 | rx_d2.into(), | ||
| 358 | rx_d3.into(), | ||
| 359 | tx_d0.into(), | ||
| 360 | tx_d1.into(), | ||
| 361 | tx_d2.into(), | ||
| 362 | tx_d3.into(), | ||
| 363 | tx_en.into(), | ||
| 364 | ]); | ||
| 365 | |||
| 366 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | ||
| 367 | } | ||
| 267 | } | 368 | } |
| 268 | 369 | ||
| 269 | /// Ethernet station management interface. | 370 | /// Ethernet station management interface. |
| 270 | pub struct EthernetStationManagement<T: Instance> { | 371 | pub(crate) struct EthernetStationManagement<T: Instance> { |
| 271 | peri: PhantomData<T>, | 372 | peri: PhantomData<T>, |
| 272 | clock_range: Cr, | 373 | clock_range: Cr, |
| 273 | } | 374 | } |
| 274 | 375 | ||
| 275 | unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> { | 376 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { |
| 276 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | 377 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { |
| 277 | let mac = T::regs().ethernet_mac(); | 378 | let mac = T::regs().ethernet_mac(); |
| 278 | 379 | ||
| @@ -302,7 +403,7 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> { | |||
| 302 | } | 403 | } |
| 303 | } | 404 | } |
| 304 | 405 | ||
| 305 | impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { | 406 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { |
| 306 | fn drop(&mut self) { | 407 | fn drop(&mut self) { |
| 307 | let dma = T::regs().ethernet_dma(); | 408 | let dma = T::regs().ethernet_dma(); |
| 308 | let mac = T::regs().ethernet_mac(); | 409 | let mac = T::regs().ethernet_mac(); |
| @@ -319,7 +420,10 @@ impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { | |||
| 319 | dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED)); | 420 | dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED)); |
| 320 | 421 | ||
| 321 | critical_section::with(|_| { | 422 | critical_section::with(|_| { |
| 322 | for pin in self.pins.iter_mut() { | 423 | for pin in match self.pins { |
| 424 | Pins::Rmii(ref mut pins) => pins.iter_mut(), | ||
| 425 | Pins::Mii(ref mut pins) => pins.iter_mut(), | ||
| 426 | } { | ||
| 323 | pin.set_as_disconnected(); | 427 | pin.set_as_disconnected(); |
| 324 | } | 428 | } |
| 325 | }) | 429 | }) |
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index 668378bea..2a46c1895 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs | |||
| @@ -168,15 +168,15 @@ impl<'a> RDesRing<'a> { | |||
| 168 | // Reset or Stop Receive Command issued | 168 | // Reset or Stop Receive Command issued |
| 169 | Rps::STOPPED => RunningState::Stopped, | 169 | Rps::STOPPED => RunningState::Stopped, |
| 170 | // Fetching receive transfer descriptor | 170 | // Fetching receive transfer descriptor |
| 171 | Rps::RUNNINGFETCHING => RunningState::Running, | 171 | Rps::RUNNING_FETCHING => RunningState::Running, |
| 172 | // Waiting for receive packet | 172 | // Waiting for receive packet |
| 173 | Rps::RUNNINGWAITING => RunningState::Running, | 173 | Rps::RUNNING_WAITING => RunningState::Running, |
| 174 | // Receive descriptor unavailable | 174 | // Receive descriptor unavailable |
| 175 | Rps::SUSPENDED => RunningState::Stopped, | 175 | Rps::SUSPENDED => RunningState::Stopped, |
| 176 | // Closing receive descriptor | 176 | // Closing receive descriptor |
| 177 | Rps::_RESERVED_5 => RunningState::Running, | 177 | Rps::_RESERVED_5 => RunningState::Running, |
| 178 | // Transferring the receive packet data from receive buffer to host memory | 178 | // Transferring the receive packet data from receive buffer to host memory |
| 179 | Rps::RUNNINGWRITING => RunningState::Running, | 179 | Rps::RUNNING_WRITING => RunningState::Running, |
| 180 | _ => RunningState::Unknown, | 180 | _ => RunningState::Unknown, |
| 181 | } | 181 | } |
| 182 | } | 182 | } |
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index b26f08cd9..034c5dd88 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -3,16 +3,16 @@ mod descriptors; | |||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{fence, Ordering}; | 4 | use core::sync::atomic::{fence, Ordering}; |
| 5 | 5 | ||
| 6 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 6 | use embassy_hal_internal::Peri; |
| 7 | use stm32_metapac::syscfg::vals::EthSelPhy; | 7 | use stm32_metapac::syscfg::vals::EthSelPhy; |
| 8 | 8 | ||
| 9 | pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; | 9 | pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; |
| 10 | use super::*; | 10 | use super::*; |
| 11 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; | 11 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; |
| 12 | use crate::interrupt; | ||
| 12 | use crate::interrupt::InterruptExt; | 13 | use crate::interrupt::InterruptExt; |
| 13 | use crate::pac::ETH; | 14 | use crate::pac::ETH; |
| 14 | use crate::rcc::SealedRccPeripheral; | 15 | use crate::rcc::SealedRccPeripheral; |
| 15 | use crate::{interrupt, Peripheral}; | ||
| 16 | 16 | ||
| 17 | /// Interrupt handler. | 17 | /// Interrupt handler. |
| 18 | pub struct InterruptHandler {} | 18 | pub struct InterruptHandler {} |
| @@ -36,8 +36,8 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | /// Ethernet driver. | 38 | /// Ethernet driver. |
| 39 | pub struct Ethernet<'d, T: Instance, P: PHY> { | 39 | pub struct Ethernet<'d, T: Instance, P: Phy> { |
| 40 | _peri: PeripheralRef<'d, T>, | 40 | _peri: Peri<'d, T>, |
| 41 | pub(crate) tx: TDesRing<'d>, | 41 | pub(crate) tx: TDesRing<'d>, |
| 42 | pub(crate) rx: RDesRing<'d>, | 42 | pub(crate) rx: RDesRing<'d>, |
| 43 | pins: Pins<'d>, | 43 | pins: Pins<'d>, |
| @@ -48,8 +48,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { | |||
| 48 | 48 | ||
| 49 | /// Pins of ethernet driver. | 49 | /// Pins of ethernet driver. |
| 50 | enum Pins<'d> { | 50 | enum Pins<'d> { |
| 51 | Rmii([PeripheralRef<'d, AnyPin>; 9]), | 51 | Rmii([Peri<'d, AnyPin>; 9]), |
| 52 | Mii([PeripheralRef<'d, AnyPin>; 14]), | 52 | Mii([Peri<'d, AnyPin>; 14]), |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | macro_rules! config_pins { | 55 | macro_rules! config_pins { |
| @@ -63,21 +63,21 @@ macro_rules! config_pins { | |||
| 63 | }; | 63 | }; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | 66 | impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { |
| 67 | /// Create a new RMII ethernet driver using 9 pins. | 67 | /// Create a new RMII ethernet driver using 9 pins. |
| 68 | pub fn new<const TX: usize, const RX: usize>( | 68 | pub fn new<const TX: usize, const RX: usize>( |
| 69 | queue: &'d mut PacketQueue<TX, RX>, | 69 | queue: &'d mut PacketQueue<TX, RX>, |
| 70 | peri: impl Peripheral<P = T> + 'd, | 70 | peri: Peri<'d, T>, |
| 71 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 71 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 72 | ref_clk: impl Peripheral<P = impl RefClkPin<T>> + 'd, | 72 | ref_clk: Peri<'d, impl RefClkPin<T>>, |
| 73 | mdio: impl Peripheral<P = impl MDIOPin<T>> + 'd, | 73 | mdio: Peri<'d, impl MDIOPin<T>>, |
| 74 | mdc: impl Peripheral<P = impl MDCPin<T>> + 'd, | 74 | mdc: Peri<'d, impl MDCPin<T>>, |
| 75 | crs: impl Peripheral<P = impl CRSPin<T>> + 'd, | 75 | crs: Peri<'d, impl CRSPin<T>>, |
| 76 | rx_d0: impl Peripheral<P = impl RXD0Pin<T>> + 'd, | 76 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 77 | rx_d1: impl Peripheral<P = impl RXD1Pin<T>> + 'd, | 77 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| 78 | tx_d0: impl Peripheral<P = impl TXD0Pin<T>> + 'd, | 78 | tx_d0: Peri<'d, impl TXD0Pin<T>>, |
| 79 | tx_d1: impl Peripheral<P = impl TXD1Pin<T>> + 'd, | 79 | tx_d1: Peri<'d, impl TXD1Pin<T>>, |
| 80 | tx_en: impl Peripheral<P = impl TXEnPin<T>> + 'd, | 80 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 81 | phy: P, | 81 | phy: P, |
| 82 | mac_addr: [u8; 6], | 82 | mac_addr: [u8; 6], |
| 83 | ) -> Self { | 83 | ) -> Self { |
| @@ -92,19 +92,18 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 92 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); | 92 | crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); |
| 93 | }); | 93 | }); |
| 94 | 94 | ||
| 95 | into_ref!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | ||
| 96 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); | 95 | config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); |
| 97 | 96 | ||
| 98 | let pins = Pins::Rmii([ | 97 | let pins = Pins::Rmii([ |
| 99 | ref_clk.map_into(), | 98 | ref_clk.into(), |
| 100 | mdio.map_into(), | 99 | mdio.into(), |
| 101 | mdc.map_into(), | 100 | mdc.into(), |
| 102 | crs.map_into(), | 101 | crs.into(), |
| 103 | rx_d0.map_into(), | 102 | rx_d0.into(), |
| 104 | rx_d1.map_into(), | 103 | rx_d1.into(), |
| 105 | tx_d0.map_into(), | 104 | tx_d0.into(), |
| 106 | tx_d1.map_into(), | 105 | tx_d1.into(), |
| 107 | tx_en.map_into(), | 106 | tx_en.into(), |
| 108 | ]); | 107 | ]); |
| 109 | 108 | ||
| 110 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 109 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) |
| @@ -113,22 +112,22 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 113 | /// Create a new MII ethernet driver using 14 pins. | 112 | /// Create a new MII ethernet driver using 14 pins. |
| 114 | pub fn new_mii<const TX: usize, const RX: usize>( | 113 | pub fn new_mii<const TX: usize, const RX: usize>( |
| 115 | queue: &'d mut PacketQueue<TX, RX>, | 114 | queue: &'d mut PacketQueue<TX, RX>, |
| 116 | peri: impl Peripheral<P = T> + 'd, | 115 | peri: Peri<'d, T>, |
| 117 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 116 | irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 118 | rx_clk: impl Peripheral<P = impl RXClkPin<T>> + 'd, | 117 | rx_clk: Peri<'d, impl RXClkPin<T>>, |
| 119 | tx_clk: impl Peripheral<P = impl TXClkPin<T>> + 'd, | 118 | tx_clk: Peri<'d, impl TXClkPin<T>>, |
| 120 | mdio: impl Peripheral<P = impl MDIOPin<T>> + 'd, | 119 | mdio: Peri<'d, impl MDIOPin<T>>, |
| 121 | mdc: impl Peripheral<P = impl MDCPin<T>> + 'd, | 120 | mdc: Peri<'d, impl MDCPin<T>>, |
| 122 | rxdv: impl Peripheral<P = impl RXDVPin<T>> + 'd, | 121 | rxdv: Peri<'d, impl RXDVPin<T>>, |
| 123 | rx_d0: impl Peripheral<P = impl RXD0Pin<T>> + 'd, | 122 | rx_d0: Peri<'d, impl RXD0Pin<T>>, |
| 124 | rx_d1: impl Peripheral<P = impl RXD1Pin<T>> + 'd, | 123 | rx_d1: Peri<'d, impl RXD1Pin<T>>, |
| 125 | rx_d2: impl Peripheral<P = impl RXD2Pin<T>> + 'd, | 124 | rx_d2: Peri<'d, impl RXD2Pin<T>>, |
| 126 | rx_d3: impl Peripheral<P = impl RXD3Pin<T>> + 'd, | 125 | rx_d3: Peri<'d, impl RXD3Pin<T>>, |
| 127 | tx_d0: impl Peripheral<P = impl TXD0Pin<T>> + 'd, | 126 | tx_d0: Peri<'d, impl TXD0Pin<T>>, |
| 128 | tx_d1: impl Peripheral<P = impl TXD1Pin<T>> + 'd, | 127 | tx_d1: Peri<'d, impl TXD1Pin<T>>, |
| 129 | tx_d2: impl Peripheral<P = impl TXD2Pin<T>> + 'd, | 128 | tx_d2: Peri<'d, impl TXD2Pin<T>>, |
| 130 | tx_d3: impl Peripheral<P = impl TXD3Pin<T>> + 'd, | 129 | tx_d3: Peri<'d, impl TXD3Pin<T>>, |
| 131 | tx_en: impl Peripheral<P = impl TXEnPin<T>> + 'd, | 130 | tx_en: Peri<'d, impl TXEnPin<T>>, |
| 132 | phy: P, | 131 | phy: P, |
| 133 | mac_addr: [u8; 6], | 132 | mac_addr: [u8; 6], |
| 134 | ) -> Self { | 133 | ) -> Self { |
| @@ -145,24 +144,23 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 145 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); | 144 | .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); |
| 146 | }); | 145 | }); |
| 147 | 146 | ||
| 148 | into_ref!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | ||
| 149 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); | 147 | config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); |
| 150 | 148 | ||
| 151 | let pins = Pins::Mii([ | 149 | let pins = Pins::Mii([ |
| 152 | rx_clk.map_into(), | 150 | rx_clk.into(), |
| 153 | tx_clk.map_into(), | 151 | tx_clk.into(), |
| 154 | mdio.map_into(), | 152 | mdio.into(), |
| 155 | mdc.map_into(), | 153 | mdc.into(), |
| 156 | rxdv.map_into(), | 154 | rxdv.into(), |
| 157 | rx_d0.map_into(), | 155 | rx_d0.into(), |
| 158 | rx_d1.map_into(), | 156 | rx_d1.into(), |
| 159 | rx_d2.map_into(), | 157 | rx_d2.into(), |
| 160 | rx_d3.map_into(), | 158 | rx_d3.into(), |
| 161 | tx_d0.map_into(), | 159 | tx_d0.into(), |
| 162 | tx_d1.map_into(), | 160 | tx_d1.into(), |
| 163 | tx_d2.map_into(), | 161 | tx_d2.into(), |
| 164 | tx_d3.map_into(), | 162 | tx_d3.into(), |
| 165 | tx_en.map_into(), | 163 | tx_en.into(), |
| 166 | ]); | 164 | ]); |
| 167 | 165 | ||
| 168 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) | 166 | Self::new_inner(queue, peri, irq, pins, phy, mac_addr) |
| @@ -170,7 +168,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 170 | 168 | ||
| 171 | fn new_inner<const TX: usize, const RX: usize>( | 169 | fn new_inner<const TX: usize, const RX: usize>( |
| 172 | queue: &'d mut PacketQueue<TX, RX>, | 170 | queue: &'d mut PacketQueue<TX, RX>, |
| 173 | peri: impl Peripheral<P = T> + 'd, | 171 | peri: Peri<'d, T>, |
| 174 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, | 172 | _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::ETH, InterruptHandler> + 'd, |
| 175 | pins: Pins<'d>, | 173 | pins: Pins<'d>, |
| 176 | phy: P, | 174 | phy: P, |
| @@ -192,6 +190,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 192 | // TODO: Carrier sense ? ECRSFD | 190 | // TODO: Carrier sense ? ECRSFD |
| 193 | }); | 191 | }); |
| 194 | 192 | ||
| 193 | // Disable multicast filter | ||
| 194 | mac.macpfr().modify(|w| w.set_pm(true)); | ||
| 195 | |||
| 195 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, | 196 | // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, |
| 196 | // so the LR write must happen after the HR write. | 197 | // so the LR write must happen after the HR write. |
| 197 | mac.maca0hr() | 198 | mac.maca0hr() |
| @@ -251,7 +252,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { | |||
| 251 | }; | 252 | }; |
| 252 | 253 | ||
| 253 | let mut this = Self { | 254 | let mut this = Self { |
| 254 | _peri: peri.into_ref(), | 255 | _peri: peri, |
| 255 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), | 256 | tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), |
| 256 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), | 257 | rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), |
| 257 | pins, | 258 | pins, |
| @@ -301,7 +302,7 @@ pub struct EthernetStationManagement<T: Instance> { | |||
| 301 | clock_range: u8, | 302 | clock_range: u8, |
| 302 | } | 303 | } |
| 303 | 304 | ||
| 304 | unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> { | 305 | impl<T: Instance> StationManagement for EthernetStationManagement<T> { |
| 305 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { | 306 | fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { |
| 306 | let mac = T::regs().ethernet_mac(); | 307 | let mac = T::regs().ethernet_mac(); |
| 307 | 308 | ||
| @@ -331,7 +332,7 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> { | |||
| 331 | } | 332 | } |
| 332 | } | 333 | } |
| 333 | 334 | ||
| 334 | impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { | 335 | impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { |
| 335 | fn drop(&mut self) { | 336 | fn drop(&mut self) { |
| 336 | let dma = T::regs().ethernet_dma(); | 337 | let dma = T::regs().ethernet_dma(); |
| 337 | let mac = T::regs().ethernet_mac(); | 338 | let mac = T::regs().ethernet_mac(); |
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 224d51b84..9fce78f95 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -5,21 +5,25 @@ use core::marker::PhantomData; | |||
| 5 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{impl_peripheral, into_ref}; | 8 | use embassy_hal_internal::{impl_peripheral, PeripheralType}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | 10 | ||
| 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; | 11 | use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; |
| 12 | use crate::pac::exti::regs::Lines; | 12 | use crate::pac::exti::regs::Lines; |
| 13 | use crate::pac::EXTI; | 13 | use crate::pac::EXTI; |
| 14 | use crate::{interrupt, pac, peripherals, Peripheral}; | 14 | use crate::{interrupt, pac, peripherals, Peri}; |
| 15 | 15 | ||
| 16 | const EXTI_COUNT: usize = 16; | 16 | const EXTI_COUNT: usize = 16; |
| 17 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 17 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; |
| 18 | static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [NEW_AW; EXTI_COUNT]; | ||
| 19 | 18 | ||
| 20 | #[cfg(exti_w)] | 19 | #[cfg(all(exti_w, feature = "_core-cm0p"))] |
| 21 | fn cpu_regs() -> pac::exti::Cpu { | 20 | fn cpu_regs() -> pac::exti::Cpu { |
| 22 | EXTI.cpu(crate::pac::CORE_INDEX) | 21 | EXTI.cpu(1) |
| 22 | } | ||
| 23 | |||
| 24 | #[cfg(all(exti_w, not(feature = "_core-cm0p")))] | ||
| 25 | fn cpu_regs() -> pac::exti::Cpu { | ||
| 26 | EXTI.cpu(0) | ||
| 23 | } | 27 | } |
| 24 | 28 | ||
| 25 | #[cfg(not(exti_w))] | 29 | #[cfg(not(exti_w))] |
| @@ -41,9 +45,6 @@ fn exticr_regs() -> pac::afio::Afio { | |||
| 41 | } | 45 | } |
| 42 | 46 | ||
| 43 | unsafe fn on_irq() { | 47 | unsafe fn on_irq() { |
| 44 | #[cfg(feature = "low-power")] | ||
| 45 | crate::low_power::on_wakeup_irq(); | ||
| 46 | |||
| 47 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] | 48 | #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] |
| 48 | let bits = EXTI.pr(0).read().0; | 49 | let bits = EXTI.pr(0).read().0; |
| 49 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] | 50 | #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] |
| @@ -68,6 +69,9 @@ unsafe fn on_irq() { | |||
| 68 | EXTI.rpr(0).write_value(Lines(bits)); | 69 | EXTI.rpr(0).write_value(Lines(bits)); |
| 69 | EXTI.fpr(0).write_value(Lines(bits)); | 70 | EXTI.fpr(0).write_value(Lines(bits)); |
| 70 | } | 71 | } |
| 72 | |||
| 73 | #[cfg(feature = "low-power")] | ||
| 74 | crate::low_power::on_wakeup_irq(); | ||
| 71 | } | 75 | } |
| 72 | 76 | ||
| 73 | struct BitIter(u32); | 77 | struct BitIter(u32); |
| @@ -101,13 +105,7 @@ impl<'d> Unpin for ExtiInput<'d> {} | |||
| 101 | 105 | ||
| 102 | impl<'d> ExtiInput<'d> { | 106 | impl<'d> ExtiInput<'d> { |
| 103 | /// Create an EXTI input. | 107 | /// Create an EXTI input. |
| 104 | pub fn new<T: GpioPin>( | 108 | pub fn new<T: GpioPin>(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { |
| 105 | pin: impl Peripheral<P = T> + 'd, | ||
| 106 | ch: impl Peripheral<P = T::ExtiChannel> + 'd, | ||
| 107 | pull: Pull, | ||
| 108 | ) -> Self { | ||
| 109 | into_ref!(pin, ch); | ||
| 110 | |||
| 111 | // Needed if using AnyPin+AnyChannel. | 109 | // Needed if using AnyPin+AnyChannel. |
| 112 | assert_eq!(pin.pin(), ch.number()); | 110 | assert_eq!(pin.pin(), ch.number()); |
| 113 | 111 | ||
| @@ -334,23 +332,12 @@ trait SealedChannel {} | |||
| 334 | 332 | ||
| 335 | /// EXTI channel trait. | 333 | /// EXTI channel trait. |
| 336 | #[allow(private_bounds)] | 334 | #[allow(private_bounds)] |
| 337 | pub trait Channel: SealedChannel + Sized { | 335 | pub trait Channel: PeripheralType + SealedChannel + Sized { |
| 338 | /// Get the EXTI channel number. | 336 | /// Get the EXTI channel number. |
| 339 | fn number(&self) -> u8; | 337 | fn number(&self) -> u8; |
| 340 | |||
| 341 | /// Type-erase (degrade) this channel into an `AnyChannel`. | ||
| 342 | /// | ||
| 343 | /// This converts EXTI channel singletons (`EXTI0`, `EXTI1`, ...), which | ||
| 344 | /// are all different types, into the same type. It is useful for | ||
| 345 | /// creating arrays of channels, or avoiding generics. | ||
| 346 | fn degrade(self) -> AnyChannel { | ||
| 347 | AnyChannel { | ||
| 348 | number: self.number() as u8, | ||
| 349 | } | ||
| 350 | } | ||
| 351 | } | 338 | } |
| 352 | 339 | ||
| 353 | /// Type-erased (degraded) EXTI channel. | 340 | /// Type-erased EXTI channel. |
| 354 | /// | 341 | /// |
| 355 | /// This represents ownership over any EXTI channel, known at runtime. | 342 | /// This represents ownership over any EXTI channel, known at runtime. |
| 356 | pub struct AnyChannel { | 343 | pub struct AnyChannel { |
| @@ -373,6 +360,14 @@ macro_rules! impl_exti { | |||
| 373 | $number | 360 | $number |
| 374 | } | 361 | } |
| 375 | } | 362 | } |
| 363 | |||
| 364 | impl From<peripherals::$type> for AnyChannel { | ||
| 365 | fn from(val: peripherals::$type) -> Self { | ||
| 366 | Self { | ||
| 367 | number: val.number() as u8, | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } | ||
| 376 | }; | 371 | }; |
| 377 | } | 372 | } |
| 378 | 373 | ||
diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 9468ac632..006dcddeb 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs | |||
| @@ -2,28 +2,25 @@ use core::marker::PhantomData; | |||
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::drop::OnDrop; | 4 | use embassy_hal_internal::drop::OnDrop; |
| 5 | use embassy_hal_internal::into_ref; | ||
| 6 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 5 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 7 | use embassy_sync::mutex::Mutex; | 6 | use embassy_sync::mutex::Mutex; |
| 8 | 7 | ||
| 9 | use super::{ | 8 | use super::{ |
| 10 | blocking_read, ensure_sector_aligned, family, get_sector, Async, Error, Flash, FlashLayout, FLASH_BASE, FLASH_SIZE, | 9 | blocking_read, ensure_sector_aligned, family, get_flash_regions, get_sector, Async, Error, Flash, FlashLayout, |
| 11 | WRITE_SIZE, | 10 | FLASH_BASE, FLASH_SIZE, WRITE_SIZE, |
| 12 | }; | 11 | }; |
| 13 | use crate::interrupt::InterruptExt; | 12 | use crate::interrupt::InterruptExt; |
| 14 | use crate::peripherals::FLASH; | 13 | use crate::peripherals::FLASH; |
| 15 | use crate::{interrupt, Peripheral}; | 14 | use crate::{interrupt, Peri}; |
| 16 | 15 | ||
| 17 | pub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); | 16 | pub(super) static REGION_ACCESS: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); |
| 18 | 17 | ||
| 19 | impl<'d> Flash<'d, Async> { | 18 | impl<'d> Flash<'d, Async> { |
| 20 | /// Create a new flash driver with async capabilities. | 19 | /// Create a new flash driver with async capabilities. |
| 21 | pub fn new( | 20 | pub fn new( |
| 22 | p: impl Peripheral<P = FLASH> + 'd, | 21 | p: Peri<'d, FLASH>, |
| 23 | _irq: impl interrupt::typelevel::Binding<crate::interrupt::typelevel::FLASH, InterruptHandler> + 'd, | 22 | _irq: impl interrupt::typelevel::Binding<crate::interrupt::typelevel::FLASH, InterruptHandler> + 'd, |
| 24 | ) -> Self { | 23 | ) -> Self { |
| 25 | into_ref!(p); | ||
| 26 | |||
| 27 | crate::interrupt::FLASH.unpend(); | 24 | crate::interrupt::FLASH.unpend(); |
| 28 | unsafe { crate::interrupt::FLASH.enable() }; | 25 | unsafe { crate::interrupt::FLASH.enable() }; |
| 29 | 26 | ||
| @@ -37,7 +34,6 @@ impl<'d> Flash<'d, Async> { | |||
| 37 | /// | 34 | /// |
| 38 | /// See module-level documentation for details on how memory regions work. | 35 | /// See module-level documentation for details on how memory regions work. |
| 39 | pub fn into_regions(self) -> FlashLayout<'d, Async> { | 36 | pub fn into_regions(self) -> FlashLayout<'d, Async> { |
| 40 | assert!(family::is_default_layout()); | ||
| 41 | FlashLayout::new(self.inner) | 37 | FlashLayout::new(self.inner) |
| 42 | } | 38 | } |
| 43 | 39 | ||
| @@ -126,7 +122,7 @@ pub(super) async unsafe fn write_chunked(base: u32, size: u32, offset: u32, byte | |||
| 126 | pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { | 122 | pub(super) async unsafe fn erase_sectored(base: u32, from: u32, to: u32) -> Result<(), Error> { |
| 127 | let start_address = base + from; | 123 | let start_address = base + from; |
| 128 | let end_address = base + to; | 124 | let end_address = base + to; |
| 129 | let regions = family::get_flash_regions(); | 125 | let regions = get_flash_regions(); |
| 130 | 126 | ||
| 131 | ensure_sector_aligned(start_address, end_address, regions)?; | 127 | ensure_sector_aligned(start_address, end_address, regions)?; |
| 132 | 128 | ||
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 8ec4bb2a1..10023e637 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs | |||
| @@ -2,26 +2,27 @@ use core::marker::PhantomData; | |||
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::drop::OnDrop; | 4 | use embassy_hal_internal::drop::OnDrop; |
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 6 | use stm32_metapac::FLASH_BASE; | ||
| 7 | 5 | ||
| 8 | use super::{ | 6 | use super::{ |
| 9 | family, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, MAX_ERASE_SIZE, | 7 | family, get_flash_regions, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, |
| 10 | READ_SIZE, WRITE_SIZE, | 8 | MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, |
| 11 | }; | 9 | }; |
| 10 | use crate::Peri; | ||
| 11 | use crate::_generated::FLASH_BASE; | ||
| 12 | use crate::peripherals::FLASH; | 12 | use crate::peripherals::FLASH; |
| 13 | use crate::Peripheral; | ||
| 14 | 13 | ||
| 15 | /// Internal flash memory driver. | 14 | /// Internal flash memory driver. |
| 16 | pub struct Flash<'d, MODE = Async> { | 15 | pub struct Flash<'d, MODE = Async> { |
| 17 | pub(crate) inner: PeripheralRef<'d, FLASH>, | 16 | pub(crate) inner: Peri<'d, FLASH>, |
| 18 | pub(crate) _mode: PhantomData<MODE>, | 17 | pub(crate) _mode: PhantomData<MODE>, |
| 19 | } | 18 | } |
| 20 | 19 | ||
| 21 | impl<'d> Flash<'d, Blocking> { | 20 | impl<'d> Flash<'d, Blocking> { |
| 22 | /// Create a new flash driver, usable in blocking mode. | 21 | /// Create a new flash driver, usable in blocking mode. |
| 23 | pub fn new_blocking(p: impl Peripheral<P = FLASH> + 'd) -> Self { | 22 | pub fn new_blocking(p: Peri<'d, FLASH>) -> Self { |
| 24 | into_ref!(p); | 23 | #[cfg(bank_setup_configurable)] |
| 24 | // Check if the configuration matches the embassy setup | ||
| 25 | super::check_bank_setup(); | ||
| 25 | 26 | ||
| 26 | Self { | 27 | Self { |
| 27 | inner: p, | 28 | inner: p, |
| @@ -35,7 +36,6 @@ impl<'d, MODE> Flash<'d, MODE> { | |||
| 35 | /// | 36 | /// |
| 36 | /// See module-level documentation for details on how memory regions work. | 37 | /// See module-level documentation for details on how memory regions work. |
| 37 | pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { | 38 | pub fn into_blocking_regions(self) -> FlashLayout<'d, Blocking> { |
| 38 | assert!(family::is_default_layout()); | ||
| 39 | FlashLayout::new(self.inner) | 39 | FlashLayout::new(self.inner) |
| 40 | } | 40 | } |
| 41 | 41 | ||
| @@ -140,7 +140,7 @@ pub(super) unsafe fn blocking_erase( | |||
| 140 | ) -> Result<(), Error> { | 140 | ) -> Result<(), Error> { |
| 141 | let start_address = base + from; | 141 | let start_address = base + from; |
| 142 | let end_address = base + to; | 142 | let end_address = base + to; |
| 143 | let regions = family::get_flash_regions(); | 143 | let regions = get_flash_regions(); |
| 144 | 144 | ||
| 145 | ensure_sector_aligned(start_address, end_address, regions)?; | 145 | ensure_sector_aligned(start_address, end_address, regions)?; |
| 146 | 146 | ||
diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs new file mode 100644 index 000000000..cc3529eb9 --- /dev/null +++ b/embassy-stm32/src/flash/eeprom.rs | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | use embassy_hal_internal::drop::OnDrop; | ||
| 2 | |||
| 3 | use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; | ||
| 4 | |||
| 5 | #[cfg(eeprom)] | ||
| 6 | impl<'d> Flash<'d, Blocking> { | ||
| 7 | // --- Internal helpers --- | ||
| 8 | |||
| 9 | /// Checks if the given offset and size are within the EEPROM bounds. | ||
| 10 | fn check_eeprom_offset(&self, offset: u32, size: u32) -> Result<(), Error> { | ||
| 11 | if offset | ||
| 12 | .checked_add(size) | ||
| 13 | .filter(|&end| end <= EEPROM_SIZE as u32) | ||
| 14 | .is_some() | ||
| 15 | { | ||
| 16 | Ok(()) | ||
| 17 | } else { | ||
| 18 | Err(Error::Size) | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | // --- Unlocked (unsafe, internal) functions --- | ||
| 23 | |||
| 24 | /// Writes a slice of bytes to EEPROM at the given offset without locking. | ||
| 25 | /// | ||
| 26 | /// # Safety | ||
| 27 | /// Caller must ensure EEPROM is unlocked and offset is valid. | ||
| 28 | unsafe fn eeprom_write_u8_slice_unlocked(&self, offset: u32, data: &[u8]) -> Result<(), Error> { | ||
| 29 | for (i, &byte) in data.iter().enumerate() { | ||
| 30 | let addr = EEPROM_BASE as u32 + offset + i as u32; | ||
| 31 | core::ptr::write_volatile(addr as *mut u8, byte); | ||
| 32 | family::wait_ready_blocking()?; | ||
| 33 | family::clear_all_err(); | ||
| 34 | } | ||
| 35 | Ok(()) | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Writes a slice of u16 values to EEPROM at the given offset without locking. | ||
| 39 | /// | ||
| 40 | /// # Safety | ||
| 41 | /// Caller must ensure EEPROM is unlocked and offset is valid and aligned. | ||
| 42 | unsafe fn eeprom_write_u16_slice_unlocked(&self, offset: u32, data: &[u16]) -> Result<(), Error> { | ||
| 43 | for (i, &value) in data.iter().enumerate() { | ||
| 44 | let addr = EEPROM_BASE as u32 + offset + i as u32 * 2; | ||
| 45 | core::ptr::write_volatile(addr as *mut u16, value); | ||
| 46 | family::wait_ready_blocking()?; | ||
| 47 | family::clear_all_err(); | ||
| 48 | } | ||
| 49 | Ok(()) | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Writes a slice of u32 values to EEPROM at the given offset without locking. | ||
| 53 | /// | ||
| 54 | /// # Safety | ||
| 55 | /// Caller must ensure EEPROM is unlocked and offset is valid and aligned. | ||
| 56 | unsafe fn eeprom_write_u32_slice_unlocked(&self, offset: u32, data: &[u32]) -> Result<(), Error> { | ||
| 57 | for (i, &value) in data.iter().enumerate() { | ||
| 58 | let addr = EEPROM_BASE as u32 + offset + i as u32 * 4; | ||
| 59 | core::ptr::write_volatile(addr as *mut u32, value); | ||
| 60 | family::wait_ready_blocking()?; | ||
| 61 | family::clear_all_err(); | ||
| 62 | } | ||
| 63 | Ok(()) | ||
| 64 | } | ||
| 65 | |||
| 66 | // --- Public, safe API --- | ||
| 67 | |||
| 68 | /// Writes a single byte to EEPROM at the given offset. | ||
| 69 | pub fn eeprom_write_u8(&mut self, offset: u32, value: u8) -> Result<(), Error> { | ||
| 70 | self.check_eeprom_offset(offset, 1)?; | ||
| 71 | unsafe { | ||
| 72 | family::unlock(); | ||
| 73 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 74 | self.eeprom_write_u8_slice_unlocked(offset, core::slice::from_ref(&value))?; | ||
| 75 | } | ||
| 76 | Ok(()) | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Writes a single 16-bit value to EEPROM at the given offset. | ||
| 80 | /// | ||
| 81 | /// Returns an error if the offset is not 2-byte aligned. | ||
| 82 | pub fn eeprom_write_u16(&mut self, offset: u32, value: u16) -> Result<(), Error> { | ||
| 83 | if offset % 2 != 0 { | ||
| 84 | return Err(Error::Unaligned); | ||
| 85 | } | ||
| 86 | self.check_eeprom_offset(offset, 2)?; | ||
| 87 | unsafe { | ||
| 88 | family::unlock(); | ||
| 89 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 90 | self.eeprom_write_u16_slice_unlocked(offset, core::slice::from_ref(&value))?; | ||
| 91 | } | ||
| 92 | Ok(()) | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Writes a single 32-bit value to EEPROM at the given offset. | ||
| 96 | /// | ||
| 97 | /// Returns an error if the offset is not 4-byte aligned. | ||
| 98 | pub fn eeprom_write_u32(&mut self, offset: u32, value: u32) -> Result<(), Error> { | ||
| 99 | if offset % 4 != 0 { | ||
| 100 | return Err(Error::Unaligned); | ||
| 101 | } | ||
| 102 | self.check_eeprom_offset(offset, 4)?; | ||
| 103 | unsafe { | ||
| 104 | family::unlock(); | ||
| 105 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 106 | self.eeprom_write_u32_slice_unlocked(offset, core::slice::from_ref(&value))?; | ||
| 107 | } | ||
| 108 | Ok(()) | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Writes a slice of bytes to EEPROM at the given offset. | ||
| 112 | pub fn eeprom_write_u8_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { | ||
| 113 | self.check_eeprom_offset(offset, data.len() as u32)?; | ||
| 114 | unsafe { | ||
| 115 | family::unlock(); | ||
| 116 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 117 | self.eeprom_write_u8_slice_unlocked(offset, data)?; | ||
| 118 | } | ||
| 119 | Ok(()) | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Writes a slice of 16-bit values to EEPROM at the given offset. | ||
| 123 | /// | ||
| 124 | /// Returns an error if the offset is not 2-byte aligned. | ||
| 125 | pub fn eeprom_write_u16_slice(&mut self, offset: u32, data: &[u16]) -> Result<(), Error> { | ||
| 126 | if offset % 2 != 0 { | ||
| 127 | return Err(Error::Unaligned); | ||
| 128 | } | ||
| 129 | self.check_eeprom_offset(offset, data.len() as u32 * 2)?; | ||
| 130 | unsafe { | ||
| 131 | family::unlock(); | ||
| 132 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 133 | self.eeprom_write_u16_slice_unlocked(offset, data)?; | ||
| 134 | } | ||
| 135 | Ok(()) | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Writes a slice of 32-bit values to EEPROM at the given offset. | ||
| 139 | /// | ||
| 140 | /// Returns an error if the offset is not 4-byte aligned. | ||
| 141 | pub fn eeprom_write_u32_slice(&mut self, offset: u32, data: &[u32]) -> Result<(), Error> { | ||
| 142 | if offset % 4 != 0 { | ||
| 143 | return Err(Error::Unaligned); | ||
| 144 | } | ||
| 145 | self.check_eeprom_offset(offset, data.len() as u32 * 4)?; | ||
| 146 | unsafe { | ||
| 147 | family::unlock(); | ||
| 148 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 149 | self.eeprom_write_u32_slice_unlocked(offset, data)?; | ||
| 150 | } | ||
| 151 | Ok(()) | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Writes a byte slice to EEPROM at the given offset, handling alignment. | ||
| 155 | /// | ||
| 156 | /// This method will write unaligned prefix and suffix as bytes, and aligned middle as u32. | ||
| 157 | pub fn eeprom_write_slice(&mut self, offset: u32, data: &[u8]) -> Result<(), Error> { | ||
| 158 | self.check_eeprom_offset(offset, data.len() as u32)?; | ||
| 159 | let start = offset; | ||
| 160 | let misalign = (start % 4) as usize; | ||
| 161 | let prefix_len = if misalign == 0 { | ||
| 162 | 0 | ||
| 163 | } else { | ||
| 164 | (4 - misalign).min(data.len()) | ||
| 165 | }; | ||
| 166 | let (prefix, rest) = data.split_at(prefix_len); | ||
| 167 | let aligned_len = (rest.len() / 4) * 4; | ||
| 168 | let (bytes_for_u32_write, suffix) = rest.split_at(aligned_len); | ||
| 169 | |||
| 170 | unsafe { | ||
| 171 | family::unlock(); | ||
| 172 | let _on_drop = OnDrop::new(|| family::lock()); | ||
| 173 | |||
| 174 | if !prefix.is_empty() { | ||
| 175 | self.eeprom_write_u8_slice_unlocked(start, prefix)?; | ||
| 176 | } | ||
| 177 | if !bytes_for_u32_write.is_empty() { | ||
| 178 | let aligned_eeprom_offset = start + prefix_len as u32; | ||
| 179 | let base_eeprom_addr = EEPROM_BASE as u32 + aligned_eeprom_offset; | ||
| 180 | for (i, chunk) in bytes_for_u32_write.chunks_exact(4).enumerate() { | ||
| 181 | // Safely read a u32 from a potentially unaligned pointer into the chunk. | ||
| 182 | let value = (chunk.as_ptr() as *const u32).read_unaligned(); | ||
| 183 | let current_eeprom_addr = base_eeprom_addr + (i * 4) as u32; | ||
| 184 | core::ptr::write_volatile(current_eeprom_addr as *mut u32, value); | ||
| 185 | family::wait_ready_blocking()?; | ||
| 186 | family::clear_all_err(); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | if !suffix.is_empty() { | ||
| 190 | let suffix_offset = start + (prefix_len + aligned_len) as u32; | ||
| 191 | self.eeprom_write_u8_slice_unlocked(suffix_offset, suffix)?; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | Ok(()) | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Reads a single byte from EEPROM at the given offset. | ||
| 198 | pub fn eeprom_read_u8(&self, offset: u32) -> Result<u8, Error> { | ||
| 199 | self.check_eeprom_offset(offset, 1)?; | ||
| 200 | let addr = EEPROM_BASE as u32 + offset; | ||
| 201 | Ok(unsafe { core::ptr::read_volatile(addr as *const u8) }) | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Reads a single 16-bit value from EEPROM at the given offset. | ||
| 205 | /// | ||
| 206 | /// Returns an error if the offset is not 2-byte aligned. | ||
| 207 | pub fn eeprom_read_u16(&self, offset: u32) -> Result<u16, Error> { | ||
| 208 | if offset % 2 != 0 { | ||
| 209 | return Err(Error::Unaligned); | ||
| 210 | } | ||
| 211 | self.check_eeprom_offset(offset, 2)?; | ||
| 212 | let addr = EEPROM_BASE as u32 + offset; | ||
| 213 | Ok(unsafe { core::ptr::read_volatile(addr as *const u16) }) | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Reads a single 32-bit value from EEPROM at the given offset. | ||
| 217 | /// | ||
| 218 | /// Returns an error if the offset is not 4-byte aligned. | ||
| 219 | pub fn eeprom_read_u32(&self, offset: u32) -> Result<u32, Error> { | ||
| 220 | if offset % 4 != 0 { | ||
| 221 | return Err(Error::Unaligned); | ||
| 222 | } | ||
| 223 | self.check_eeprom_offset(offset, 4)?; | ||
| 224 | let addr = EEPROM_BASE as u32 + offset; | ||
| 225 | Ok(unsafe { core::ptr::read_volatile(addr as *const u32) }) | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Reads a slice of bytes from EEPROM at the given offset into the provided buffer. | ||
| 229 | pub fn eeprom_read_slice(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { | ||
| 230 | self.check_eeprom_offset(offset, buf.len() as u32)?; | ||
| 231 | let addr = EEPROM_BASE as u32 + offset; | ||
| 232 | let src = unsafe { core::slice::from_raw_parts(addr as *const u8, buf.len()) }; | ||
| 233 | buf.copy_from_slice(src); | ||
| 234 | Ok(()) | ||
| 235 | } | ||
| 236 | } | ||
diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 402312f68..3f9dbe945 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs | |||
| @@ -1,18 +1,10 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
| 8 | pub(crate) const fn is_default_layout() -> bool { | ||
| 9 | true | ||
| 10 | } | ||
| 11 | |||
| 12 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 13 | &FLASH_REGIONS | ||
| 14 | } | ||
| 15 | |||
| 16 | pub(crate) unsafe fn lock() { | 8 | pub(crate) unsafe fn lock() { |
| 17 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 9 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| 18 | } | 10 | } |
diff --git a/embassy-stm32/src/flash/f1f3.rs b/embassy-stm32/src/flash/f1f3.rs index ff7f810ea..bf9ad2893 100644 --- a/embassy-stm32/src/flash/f1f3.rs +++ b/embassy-stm32/src/flash/f1f3.rs | |||
| @@ -1,18 +1,10 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
| 8 | pub(crate) const fn is_default_layout() -> bool { | ||
| 9 | true | ||
| 10 | } | ||
| 11 | |||
| 12 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 13 | &FLASH_REGIONS | ||
| 14 | } | ||
| 15 | |||
| 16 | pub(crate) unsafe fn lock() { | 8 | pub(crate) unsafe fn lock() { |
| 17 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 9 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| 18 | } | 10 | } |
| @@ -64,8 +56,8 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 64 | // BSY bit, because there is a one-cycle delay between | 56 | // BSY bit, because there is a one-cycle delay between |
| 65 | // setting the STRT bit and the BSY bit being asserted | 57 | // setting the STRT bit and the BSY bit being asserted |
| 66 | // by hardware. See STM32F105xx, STM32F107xx device errata, | 58 | // by hardware. See STM32F105xx, STM32F107xx device errata, |
| 67 | // section 2.2.8 | 59 | // section 2.2.8, and also RM0316 Rev 10 section 4.2.3 for |
| 68 | #[cfg(stm32f1)] | 60 | // STM32F3xx series. |
| 69 | pac::FLASH.cr().read(); | 61 | pac::FLASH.cr().read(); |
| 70 | 62 | ||
| 71 | let mut ret: Result<(), Error> = wait_ready_blocking(); | 63 | let mut ret: Result<(), Error> = wait_ready_blocking(); |
diff --git a/embassy-stm32/src/flash/f2.rs b/embassy-stm32/src/flash/f2.rs new file mode 100644 index 000000000..67e380619 --- /dev/null +++ b/embassy-stm32/src/flash/f2.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | use core::ptr::write_volatile; | ||
| 2 | use core::sync::atomic::{fence, AtomicBool, Ordering}; | ||
| 3 | |||
| 4 | use pac::flash::regs::Sr; | ||
| 5 | |||
| 6 | use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; | ||
| 7 | use crate::flash::Error; | ||
| 8 | use crate::pac; | ||
| 9 | |||
| 10 | static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false); | ||
| 11 | |||
| 12 | impl FlashSector { | ||
| 13 | const fn snb(&self) -> u8 { | ||
| 14 | ((self.bank as u8) << 4) + self.index_in_bank | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | pub(crate) unsafe fn lock() { | ||
| 19 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | ||
| 20 | } | ||
| 21 | |||
| 22 | pub(crate) unsafe fn unlock() { | ||
| 23 | if pac::FLASH.cr().read().lock() { | ||
| 24 | pac::FLASH.keyr().write_value(0x4567_0123); | ||
| 25 | pac::FLASH.keyr().write_value(0xCDEF_89AB); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | pub(crate) unsafe fn enable_blocking_write() { | ||
| 30 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 31 | save_data_cache_state(); | ||
| 32 | |||
| 33 | pac::FLASH.cr().write(|w| { | ||
| 34 | w.set_pg(true); | ||
| 35 | w.set_psize(pac::flash::vals::Psize::PSIZE32); | ||
| 36 | }); | ||
| 37 | } | ||
| 38 | |||
| 39 | pub(crate) unsafe fn disable_blocking_write() { | ||
| 40 | pac::FLASH.cr().write(|w| w.set_pg(false)); | ||
| 41 | restore_data_cache_state(); | ||
| 42 | } | ||
| 43 | |||
| 44 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 45 | write_start(start_address, buf); | ||
| 46 | blocking_wait_ready() | ||
| 47 | } | ||
| 48 | |||
| 49 | unsafe fn write_start(start_address: u32, buf: &[u8; WRITE_SIZE]) { | ||
| 50 | let mut address = start_address; | ||
| 51 | for val in buf.chunks(4) { | ||
| 52 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 53 | address += val.len() as u32; | ||
| 54 | |||
| 55 | // prevents parallelism errors | ||
| 56 | fence(Ordering::SeqCst); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 61 | save_data_cache_state(); | ||
| 62 | |||
| 63 | trace!("Blocking erasing sector number {}", sector.snb()); | ||
| 64 | |||
| 65 | pac::FLASH.cr().modify(|w| { | ||
| 66 | w.set_ser(true); | ||
| 67 | w.set_snb(sector.snb()) | ||
| 68 | }); | ||
| 69 | |||
| 70 | pac::FLASH.cr().modify(|w| { | ||
| 71 | w.set_strt(true); | ||
| 72 | }); | ||
| 73 | |||
| 74 | let ret: Result<(), Error> = blocking_wait_ready(); | ||
| 75 | clear_all_err(); | ||
| 76 | restore_data_cache_state(); | ||
| 77 | ret | ||
| 78 | } | ||
| 79 | |||
| 80 | pub(crate) unsafe fn clear_all_err() { | ||
| 81 | // read and write back the same value. | ||
| 82 | // This clears all "write 1 to clear" bits. | ||
| 83 | pac::FLASH.sr().modify(|_| {}); | ||
| 84 | } | ||
| 85 | |||
| 86 | unsafe fn blocking_wait_ready() -> Result<(), Error> { | ||
| 87 | loop { | ||
| 88 | let sr = pac::FLASH.sr().read(); | ||
| 89 | |||
| 90 | if !sr.bsy() { | ||
| 91 | return get_result(sr); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | fn get_result(sr: Sr) -> Result<(), Error> { | ||
| 97 | if sr.pgserr() { | ||
| 98 | Err(Error::Seq) | ||
| 99 | } else if sr.pgperr() { | ||
| 100 | Err(Error::Parallelism) | ||
| 101 | } else if sr.pgaerr() { | ||
| 102 | Err(Error::Unaligned) | ||
| 103 | } else if sr.wrperr() { | ||
| 104 | Err(Error::Protected) | ||
| 105 | } else { | ||
| 106 | Ok(()) | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | fn save_data_cache_state() { | ||
| 111 | let dual_bank = unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2; | ||
| 112 | if dual_bank { | ||
| 113 | // Disable data cache during write/erase if there are two banks, see errata 2.2.12 | ||
| 114 | let dcen = pac::FLASH.acr().read().dcen(); | ||
| 115 | DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed); | ||
| 116 | if dcen { | ||
| 117 | pac::FLASH.acr().modify(|w| w.set_dcen(false)); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | fn restore_data_cache_state() { | ||
| 123 | let dual_bank = unwrap!(get_flash_regions().last()).bank == FlashBank::Bank2; | ||
| 124 | if dual_bank { | ||
| 125 | // Restore data cache if it was enabled | ||
| 126 | let dcen = DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed); | ||
| 127 | if dcen { | ||
| 128 | // Reset data cache before we enable it again | ||
| 129 | pac::FLASH.acr().modify(|w| w.set_dcrst(true)); | ||
| 130 | pac::FLASH.acr().modify(|w| w.set_dcrst(false)); | ||
| 131 | pac::FLASH.acr().modify(|w| w.set_dcen(true)) | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index d0bb957ee..62e0492b5 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs | |||
| @@ -3,176 +3,11 @@ use core::sync::atomic::{fence, AtomicBool, Ordering}; | |||
| 3 | 3 | ||
| 4 | use embassy_sync::waitqueue::AtomicWaker; | 4 | use embassy_sync::waitqueue::AtomicWaker; |
| 5 | use pac::flash::regs::Sr; | 5 | use pac::flash::regs::Sr; |
| 6 | use pac::FLASH_SIZE; | ||
| 7 | 6 | ||
| 8 | use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 7 | use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; |
| 8 | use crate::_generated::FLASH_SIZE; | ||
| 9 | use crate::flash::Error; | 9 | use crate::flash::Error; |
| 10 | use crate::pac; | 10 | use crate::pac; |
| 11 | #[allow(missing_docs)] // TODO | ||
| 12 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] | ||
| 13 | mod alt_regions { | ||
| 14 | use core::marker::PhantomData; | ||
| 15 | |||
| 16 | use embassy_hal_internal::PeripheralRef; | ||
| 17 | use stm32_metapac::FLASH_SIZE; | ||
| 18 | |||
| 19 | use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; | ||
| 20 | use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; | ||
| 21 | use crate::peripherals::FLASH; | ||
| 22 | |||
| 23 | pub const ALT_BANK1_REGION3: FlashRegion = FlashRegion { | ||
| 24 | size: 3 * BANK1_REGION3.erase_size, | ||
| 25 | ..BANK1_REGION3 | ||
| 26 | }; | ||
| 27 | pub const ALT_BANK2_REGION1: FlashRegion = FlashRegion { | ||
| 28 | bank: FlashBank::Bank2, | ||
| 29 | base: BANK1_REGION1.base + FLASH_SIZE as u32 / 2, | ||
| 30 | ..BANK1_REGION1 | ||
| 31 | }; | ||
| 32 | pub const ALT_BANK2_REGION2: FlashRegion = FlashRegion { | ||
| 33 | bank: FlashBank::Bank2, | ||
| 34 | base: BANK1_REGION2.base + FLASH_SIZE as u32 / 2, | ||
| 35 | ..BANK1_REGION2 | ||
| 36 | }; | ||
| 37 | pub const ALT_BANK2_REGION3: FlashRegion = FlashRegion { | ||
| 38 | bank: FlashBank::Bank2, | ||
| 39 | base: BANK1_REGION3.base + FLASH_SIZE as u32 / 2, | ||
| 40 | size: 3 * BANK1_REGION3.erase_size, | ||
| 41 | ..BANK1_REGION3 | ||
| 42 | }; | ||
| 43 | |||
| 44 | pub const ALT_FLASH_REGIONS: [&FlashRegion; 6] = [ | ||
| 45 | &BANK1_REGION1, | ||
| 46 | &BANK1_REGION2, | ||
| 47 | &ALT_BANK1_REGION3, | ||
| 48 | &ALT_BANK2_REGION1, | ||
| 49 | &ALT_BANK2_REGION2, | ||
| 50 | &ALT_BANK2_REGION3, | ||
| 51 | ]; | ||
| 52 | |||
| 53 | pub struct AltBank1Region3<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData<MODE>); | ||
| 54 | pub struct AltBank2Region1<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData<MODE>); | ||
| 55 | pub struct AltBank2Region2<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData<MODE>); | ||
| 56 | pub struct AltBank2Region3<'d, MODE = Async>(pub &'static FlashRegion, PeripheralRef<'d, FLASH>, PhantomData<MODE>); | ||
| 57 | |||
| 58 | pub struct AltFlashLayout<'d, MODE = Async> { | ||
| 59 | pub bank1_region1: Bank1Region1<'d, MODE>, | ||
| 60 | pub bank1_region2: Bank1Region2<'d, MODE>, | ||
| 61 | pub bank1_region3: AltBank1Region3<'d, MODE>, | ||
| 62 | pub bank2_region1: AltBank2Region1<'d, MODE>, | ||
| 63 | pub bank2_region2: AltBank2Region2<'d, MODE>, | ||
| 64 | pub bank2_region3: AltBank2Region3<'d, MODE>, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl<'d> Flash<'d> { | ||
| 68 | pub fn into_alt_regions(self) -> AltFlashLayout<'d, Async> { | ||
| 69 | assert!(!super::is_default_layout()); | ||
| 70 | |||
| 71 | // SAFETY: We never expose the cloned peripheral references, and their instance is not public. | ||
| 72 | // Also, all async flash region operations are protected with a mutex. | ||
| 73 | let p = self.inner; | ||
| 74 | AltFlashLayout { | ||
| 75 | bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 76 | bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 77 | bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 78 | bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 79 | bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 80 | bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn into_alt_blocking_regions(self) -> AltFlashLayout<'d, Blocking> { | ||
| 85 | assert!(!super::is_default_layout()); | ||
| 86 | |||
| 87 | // SAFETY: We never expose the cloned peripheral references, and their instance is not public. | ||
| 88 | // Also, all blocking flash region operations are protected with a cs. | ||
| 89 | let p = self.inner; | ||
| 90 | AltFlashLayout { | ||
| 91 | bank1_region1: Bank1Region1(&BANK1_REGION1, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 92 | bank1_region2: Bank1Region2(&BANK1_REGION2, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 93 | bank1_region3: AltBank1Region3(&ALT_BANK1_REGION3, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 94 | bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 95 | bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 96 | bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | macro_rules! foreach_altflash_region { | ||
| 102 | ($type_name:ident, $region:ident) => { | ||
| 103 | impl<MODE> $type_name<'_, MODE> { | ||
| 104 | pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { | ||
| 105 | crate::flash::common::blocking_read(self.0.base, self.0.size, offset, bytes) | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | impl $type_name<'_, Async> { | ||
| 110 | pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { | ||
| 111 | self.blocking_read(offset, bytes) | ||
| 112 | } | ||
| 113 | |||
| 114 | pub async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { | ||
| 115 | let _guard = asynch::REGION_ACCESS.lock().await; | ||
| 116 | unsafe { asynch::write_chunked(self.0.base, self.0.size, offset, bytes).await } | ||
| 117 | } | ||
| 118 | |||
| 119 | pub async fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { | ||
| 120 | let _guard = asynch::REGION_ACCESS.lock().await; | ||
| 121 | unsafe { asynch::erase_sectored(self.0.base, from, to).await } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | impl<MODE> embedded_storage::nor_flash::ErrorType for $type_name<'_, MODE> { | ||
| 126 | type Error = Error; | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<MODE> embedded_storage::nor_flash::ReadNorFlash for $type_name<'_, MODE> { | ||
| 130 | const READ_SIZE: usize = crate::flash::READ_SIZE; | ||
| 131 | |||
| 132 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 133 | self.blocking_read(offset, bytes) | ||
| 134 | } | ||
| 135 | |||
| 136 | fn capacity(&self) -> usize { | ||
| 137 | self.0.size as usize | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | impl embedded_storage_async::nor_flash::ReadNorFlash for $type_name<'_, Async> { | ||
| 142 | const READ_SIZE: usize = crate::flash::READ_SIZE; | ||
| 143 | |||
| 144 | async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||
| 145 | self.read(offset, bytes).await | ||
| 146 | } | ||
| 147 | |||
| 148 | fn capacity(&self) -> usize { | ||
| 149 | self.0.size as usize | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl embedded_storage_async::nor_flash::NorFlash for $type_name<'_, Async> { | ||
| 154 | const WRITE_SIZE: usize = $region.write_size as usize; | ||
| 155 | const ERASE_SIZE: usize = $region.erase_size as usize; | ||
| 156 | |||
| 157 | async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||
| 158 | self.write(offset, bytes).await | ||
| 159 | } | ||
| 160 | |||
| 161 | async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||
| 162 | self.erase(from, to).await | ||
| 163 | } | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | } | ||
| 167 | |||
| 168 | foreach_altflash_region!(AltBank1Region3, ALT_BANK1_REGION3); | ||
| 169 | foreach_altflash_region!(AltBank2Region1, ALT_BANK2_REGION1); | ||
| 170 | foreach_altflash_region!(AltBank2Region2, ALT_BANK2_REGION2); | ||
| 171 | foreach_altflash_region!(AltBank2Region3, ALT_BANK2_REGION3); | ||
| 172 | } | ||
| 173 | |||
| 174 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] | ||
| 175 | pub use alt_regions::*; | ||
| 176 | 11 | ||
| 177 | static WAKER: AtomicWaker = AtomicWaker::new(); | 12 | static WAKER: AtomicWaker = AtomicWaker::new(); |
| 178 | static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false); | 13 | static DATA_CACHE_WAS_ENABLED: AtomicBool = AtomicBool::new(false); |
| @@ -183,30 +18,6 @@ impl FlashSector { | |||
| 183 | } | 18 | } |
| 184 | } | 19 | } |
| 185 | 20 | ||
| 186 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] | ||
| 187 | pub(crate) fn is_default_layout() -> bool { | ||
| 188 | !pac::FLASH.optcr().read().db1m() | ||
| 189 | } | ||
| 190 | |||
| 191 | #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] | ||
| 192 | pub(crate) const fn is_default_layout() -> bool { | ||
| 193 | true | ||
| 194 | } | ||
| 195 | |||
| 196 | #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))] | ||
| 197 | pub fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 198 | if is_default_layout() { | ||
| 199 | &FLASH_REGIONS | ||
| 200 | } else { | ||
| 201 | &ALT_FLASH_REGIONS | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))] | ||
| 206 | pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 207 | &FLASH_REGIONS | ||
| 208 | } | ||
| 209 | |||
| 210 | pub(crate) unsafe fn on_interrupt() { | 21 | pub(crate) unsafe fn on_interrupt() { |
| 211 | // Clear IRQ flags | 22 | // Clear IRQ flags |
| 212 | pac::FLASH.sr().write(|w| { | 23 | pac::FLASH.sr().write(|w| { |
| @@ -469,7 +280,7 @@ fn pa12_is_output_pull_low() -> bool { | |||
| 469 | use pac::GPIOA; | 280 | use pac::GPIOA; |
| 470 | const PIN: usize = 12; | 281 | const PIN: usize = 12; |
| 471 | GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT | 282 | GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT |
| 472 | && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULLDOWN | 283 | && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN |
| 473 | && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW | 284 | && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW |
| 474 | } | 285 | } |
| 475 | 286 | ||
| @@ -485,71 +296,83 @@ mod tests { | |||
| 485 | const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; | 296 | const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; |
| 486 | const LARGE_SECTOR_SIZE: u32 = 128 * 1024; | 297 | const LARGE_SECTOR_SIZE: u32 = 128 * 1024; |
| 487 | 298 | ||
| 488 | let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| { | 299 | if !cfg!(feature = "dual-bank") { |
| 489 | let sector = get_sector(address, &FLASH_REGIONS); | 300 | let assert_sector = |snb: u8, index_in_bank: u8, start: u32, size: u32, address: u32| { |
| 490 | assert_eq!(snb, sector.snb()); | 301 | let sector = get_sector(address, crate::flash::get_flash_regions()); |
| 491 | assert_eq!( | 302 | assert_eq!(snb, sector.snb()); |
| 492 | FlashSector { | 303 | assert_eq!( |
| 493 | bank: FlashBank::Bank1, | 304 | FlashSector { |
| 494 | index_in_bank, | 305 | bank: sector.bank, |
| 495 | start, | 306 | index_in_bank, |
| 496 | size | 307 | start, |
| 497 | }, | 308 | size |
| 498 | sector | 309 | }, |
| 499 | ); | 310 | sector |
| 500 | }; | 311 | ); |
| 501 | 312 | }; | |
| 502 | assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); | 313 | |
| 503 | assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); | 314 | assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); |
| 504 | assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); | 315 | assert_sector(0x00, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); |
| 505 | assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); | 316 | assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); |
| 506 | 317 | assert_sector(0x03, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); | |
| 507 | assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); | 318 | |
| 508 | assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); | 319 | assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); |
| 509 | 320 | assert_sector(0x04, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); | |
| 510 | assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); | 321 | |
| 511 | assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); | 322 | assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); |
| 512 | assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); | 323 | assert_sector(0x05, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); |
| 513 | assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); | 324 | assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); |
| 514 | 325 | assert_sector(0x0B, 11, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); | |
| 515 | let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { | 326 | } else { |
| 516 | let sector = get_sector(address, &ALT_FLASH_REGIONS); | 327 | let assert_sector = |snb: u8, bank: FlashBank, index_in_bank: u8, start: u32, size: u32, address: u32| { |
| 517 | assert_eq!(snb, sector.snb()); | 328 | let sector = get_sector(address, crate::flash::get_flash_regions()); |
| 518 | assert_eq!( | 329 | assert_eq!(snb, sector.snb()); |
| 519 | FlashSector { | 330 | assert_eq!( |
| 520 | bank, | 331 | FlashSector { |
| 521 | index_in_bank, | 332 | bank, |
| 522 | start, | 333 | index_in_bank, |
| 523 | size | 334 | start, |
| 524 | }, | 335 | size |
| 525 | sector | 336 | }, |
| 526 | ) | 337 | sector |
| 527 | }; | 338 | ) |
| 528 | 339 | }; | |
| 529 | assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); | 340 | |
| 530 | assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); | 341 | assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000); |
| 531 | assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); | 342 | assert_sector(0x00, FlashBank::Bank1, 0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF); |
| 532 | assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); | 343 | assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000); |
| 533 | 344 | assert_sector(0x03, FlashBank::Bank1, 3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF); | |
| 534 | assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); | 345 | |
| 535 | assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); | 346 | assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000); |
| 536 | 347 | assert_sector(0x04, FlashBank::Bank1, 4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF); | |
| 537 | assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); | 348 | |
| 538 | assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); | 349 | assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000); |
| 539 | assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); | 350 | assert_sector(0x05, FlashBank::Bank1, 5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF); |
| 540 | assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); | 351 | assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0806_0000); |
| 541 | 352 | assert_sector(0x07, FlashBank::Bank1, 7, 0x0806_0000, LARGE_SECTOR_SIZE, 0x0807_FFFF); | |
| 542 | assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); | 353 | |
| 543 | assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); | 354 | assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_0000); |
| 544 | assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); | 355 | assert_sector(0x10, FlashBank::Bank2, 0, 0x0808_0000, SMALL_SECTOR_SIZE, 0x0808_3FFF); |
| 545 | assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); | 356 | assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_C000); |
| 546 | 357 | assert_sector(0x13, FlashBank::Bank2, 3, 0x0808_C000, SMALL_SECTOR_SIZE, 0x0808_FFFF); | |
| 547 | assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); | 358 | |
| 548 | assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); | 359 | assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_0000); |
| 549 | 360 | assert_sector(0x14, FlashBank::Bank2, 4, 0x0809_0000, MEDIUM_SECTOR_SIZE, 0x0809_FFFF); | |
| 550 | assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); | 361 | |
| 551 | assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); | 362 | assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080A_0000); |
| 552 | assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); | 363 | assert_sector(0x15, FlashBank::Bank2, 5, 0x080A_0000, LARGE_SECTOR_SIZE, 0x080B_FFFF); |
| 553 | assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); | 364 | assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080E_0000); |
| 365 | assert_sector(0x17, FlashBank::Bank2, 7, 0x080E_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | #[cfg(all(bank_setup_configurable))] | ||
| 371 | pub(crate) fn check_bank_setup() { | ||
| 372 | if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { | ||
| 373 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config"); | ||
| 374 | } | ||
| 375 | if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { | ||
| 376 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config"); | ||
| 554 | } | 377 | } |
| 555 | } | 378 | } |
diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 09ebe9db9..0547c747a 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
| 8 | pub(crate) const fn is_default_layout() -> bool { | 8 | impl FlashSector { |
| 9 | true | 9 | const fn snb(&self) -> u8 { |
| 10 | } | 10 | ((self.bank as u8) << 4) + self.index_in_bank |
| 11 | 11 | } | |
| 12 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 13 | &FLASH_REGIONS | ||
| 14 | } | 12 | } |
| 15 | 13 | ||
| 16 | pub(crate) unsafe fn lock() { | 14 | pub(crate) unsafe fn lock() { |
| @@ -53,7 +51,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 53 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 51 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 54 | pac::FLASH.cr().modify(|w| { | 52 | pac::FLASH.cr().modify(|w| { |
| 55 | w.set_ser(true); | 53 | w.set_ser(true); |
| 56 | w.set_snb(sector.index_in_bank) | 54 | w.set_snb(sector.snb()) |
| 57 | }); | 55 | }); |
| 58 | 56 | ||
| 59 | pac::FLASH.cr().modify(|w| { | 57 | pac::FLASH.cr().modify(|w| { |
| @@ -118,7 +116,7 @@ mod tests { | |||
| 118 | start, | 116 | start, |
| 119 | size | 117 | size |
| 120 | }, | 118 | }, |
| 121 | get_sector(address, &FLASH_REGIONS) | 119 | get_sector(address, crate::flash::get_flash_regions()) |
| 122 | ) | 120 | ) |
| 123 | }; | 121 | }; |
| 124 | 122 | ||
| @@ -137,7 +135,7 @@ mod tests { | |||
| 137 | } | 135 | } |
| 138 | 136 | ||
| 139 | #[test] | 137 | #[test] |
| 140 | #[cfg(stm32f769)] | 138 | #[cfg(all(stm32f769, feature = "single-bank"))] |
| 141 | fn can_get_sector() { | 139 | fn can_get_sector() { |
| 142 | const SMALL_SECTOR_SIZE: u32 = 32 * 1024; | 140 | const SMALL_SECTOR_SIZE: u32 = 32 * 1024; |
| 143 | const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; | 141 | const MEDIUM_SECTOR_SIZE: u32 = 128 * 1024; |
| @@ -151,7 +149,7 @@ mod tests { | |||
| 151 | start, | 149 | start, |
| 152 | size | 150 | size |
| 153 | }, | 151 | }, |
| 154 | get_sector(address, &FLASH_REGIONS) | 152 | get_sector(address, crate::flash::get_flash_regions()) |
| 155 | ) | 153 | ) |
| 156 | }; | 154 | }; |
| 157 | 155 | ||
| @@ -168,4 +166,61 @@ mod tests { | |||
| 168 | assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); | 166 | assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000); |
| 169 | assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); | 167 | assert_sector(7, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080F_FFFF); |
| 170 | } | 168 | } |
| 169 | |||
| 170 | #[test] | ||
| 171 | #[cfg(all(stm32f769, feature = "dual-bank"))] | ||
| 172 | fn can_get_sector() { | ||
| 173 | const SMALL_SECTOR_SIZE: u32 = 16 * 1024; | ||
| 174 | const MEDIUM_SECTOR_SIZE: u32 = 64 * 1024; | ||
| 175 | const LARGE_SECTOR_SIZE: u32 = 128 * 1024; | ||
| 176 | |||
| 177 | let assert_sector = |index_in_bank: u8, start: u32, size: u32, address: u32, snb: u8, bank: FlashBank| { | ||
| 178 | assert_eq!( | ||
| 179 | FlashSector { | ||
| 180 | bank: bank, | ||
| 181 | index_in_bank, | ||
| 182 | start, | ||
| 183 | size | ||
| 184 | }, | ||
| 185 | get_sector(address, crate::flash::get_flash_regions()) | ||
| 186 | ); | ||
| 187 | assert_eq!(get_sector(address, crate::flash::get_flash_regions()).snb(), snb); | ||
| 188 | }; | ||
| 189 | |||
| 190 | assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_0000, 0x00, FlashBank::Bank1); | ||
| 191 | assert_sector(0, 0x0800_0000, SMALL_SECTOR_SIZE, 0x0800_3FFF, 0x00, FlashBank::Bank1); | ||
| 192 | assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_C000, 0x03, FlashBank::Bank1); | ||
| 193 | assert_sector(3, 0x0800_C000, SMALL_SECTOR_SIZE, 0x0800_FFFF, 0x03, FlashBank::Bank1); | ||
| 194 | |||
| 195 | assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_0000, 0x04, FlashBank::Bank1); | ||
| 196 | assert_sector(4, 0x0801_0000, MEDIUM_SECTOR_SIZE, 0x0801_FFFF, 0x04, FlashBank::Bank1); | ||
| 197 | |||
| 198 | assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0802_0000, 0x05, FlashBank::Bank1); | ||
| 199 | assert_sector(5, 0x0802_0000, LARGE_SECTOR_SIZE, 0x0803_FFFF, 0x05, FlashBank::Bank1); | ||
| 200 | assert_sector(10, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080C_0000, 0x0A, FlashBank::Bank1); | ||
| 201 | assert_sector(10, 0x080C_0000, LARGE_SECTOR_SIZE, 0x080D_FFFF, 0x0A, FlashBank::Bank1); | ||
| 202 | |||
| 203 | assert_sector(0, 0x0810_0000, SMALL_SECTOR_SIZE, 0x0810_0000, 0x10, FlashBank::Bank2); | ||
| 204 | assert_sector(0, 0x0810_0000, SMALL_SECTOR_SIZE, 0x0810_3FFF, 0x10, FlashBank::Bank2); | ||
| 205 | assert_sector(3, 0x0810_C000, SMALL_SECTOR_SIZE, 0x0810_C000, 0x13, FlashBank::Bank2); | ||
| 206 | assert_sector(3, 0x0810_C000, SMALL_SECTOR_SIZE, 0x0810_FFFF, 0x13, FlashBank::Bank2); | ||
| 207 | |||
| 208 | assert_sector(4, 0x0811_0000, MEDIUM_SECTOR_SIZE, 0x0811_0000, 0x14, FlashBank::Bank2); | ||
| 209 | assert_sector(4, 0x0811_0000, MEDIUM_SECTOR_SIZE, 0x0811_FFFF, 0x14, FlashBank::Bank2); | ||
| 210 | |||
| 211 | assert_sector(5, 0x0812_0000, LARGE_SECTOR_SIZE, 0x0812_0000, 0x15, FlashBank::Bank2); | ||
| 212 | assert_sector(5, 0x0812_0000, LARGE_SECTOR_SIZE, 0x0813_FFFF, 0x15, FlashBank::Bank2); | ||
| 213 | assert_sector(10, 0x081C_0000, LARGE_SECTOR_SIZE, 0x081C_0000, 0x1A, FlashBank::Bank2); | ||
| 214 | assert_sector(10, 0x081C_0000, LARGE_SECTOR_SIZE, 0x081D_FFFF, 0x1A, FlashBank::Bank2); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | #[cfg(all(bank_setup_configurable))] | ||
| 219 | pub(crate) fn check_bank_setup() { | ||
| 220 | if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { | ||
| 221 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config"); | ||
| 222 | } | ||
| 223 | if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { | ||
| 224 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config"); | ||
| 225 | } | ||
| 171 | } | 226 | } |
diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index 01a0c603f..bc1fd360c 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs | |||
| @@ -3,24 +3,16 @@ use core::sync::atomic::{fence, Ordering}; | |||
| 3 | 3 | ||
| 4 | use cortex_m::interrupt; | 4 | use cortex_m::interrupt; |
| 5 | 5 | ||
| 6 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 6 | use super::{FlashSector, WRITE_SIZE}; |
| 7 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 8 | use crate::pac; | 8 | use crate::pac; |
| 9 | 9 | ||
| 10 | pub(crate) const fn is_default_layout() -> bool { | ||
| 11 | true | ||
| 12 | } | ||
| 13 | |||
| 14 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 15 | &FLASH_REGIONS | ||
| 16 | } | ||
| 17 | |||
| 18 | pub(crate) unsafe fn lock() { | 10 | pub(crate) unsafe fn lock() { |
| 19 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 11 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| 20 | } | 12 | } |
| 21 | pub(crate) unsafe fn unlock() { | 13 | pub(crate) unsafe fn unlock() { |
| 22 | // Wait, while the memory interface is busy. | 14 | // Wait, while the memory interface is busy. |
| 23 | while pac::FLASH.sr().read().bsy() {} | 15 | wait_busy(); |
| 24 | 16 | ||
| 25 | // Unlock flash | 17 | // Unlock flash |
| 26 | if pac::FLASH.cr().read().lock() { | 18 | if pac::FLASH.cr().read().lock() { |
| @@ -53,12 +45,17 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 53 | 45 | ||
| 54 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 46 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 55 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | 47 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; |
| 56 | while pac::FLASH.sr().read().bsy() {} | 48 | wait_busy(); |
| 57 | clear_all_err(); | 49 | clear_all_err(); |
| 58 | 50 | ||
| 59 | interrupt::free(|_| { | 51 | interrupt::free(|_| { |
| 60 | pac::FLASH.cr().modify(|w| { | 52 | pac::FLASH.cr().modify(|w| { |
| 61 | w.set_per(true); | 53 | w.set_per(true); |
| 54 | #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))] | ||
| 55 | w.set_bker(sector.bank == crate::flash::FlashBank::Bank2); | ||
| 56 | #[cfg(flash_g0x0)] | ||
| 57 | w.set_pnb(idx as u16); | ||
| 58 | #[cfg(not(flash_g0x0))] | ||
| 62 | w.set_pnb(idx as u8); | 59 | w.set_pnb(idx as u8); |
| 63 | w.set_strt(true); | 60 | w.set_strt(true); |
| 64 | }); | 61 | }); |
| @@ -94,3 +91,33 @@ pub(crate) unsafe fn clear_all_err() { | |||
| 94 | // This clears all "write 1 to clear" bits. | 91 | // This clears all "write 1 to clear" bits. |
| 95 | pac::FLASH.sr().modify(|_| {}); | 92 | pac::FLASH.sr().modify(|_| {}); |
| 96 | } | 93 | } |
| 94 | |||
| 95 | #[cfg(any(flash_g0x0, flash_g0x1))] | ||
| 96 | fn wait_busy() { | ||
| 97 | while pac::FLASH.sr().read().bsy() | pac::FLASH.sr().read().bsy2() {} | ||
| 98 | } | ||
| 99 | |||
| 100 | #[cfg(not(any(flash_g0x0, flash_g0x1)))] | ||
| 101 | fn wait_busy() { | ||
| 102 | while pac::FLASH.sr().read().bsy() {} | ||
| 103 | } | ||
| 104 | |||
| 105 | #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] | ||
| 106 | pub(crate) fn check_bank_setup() { | ||
| 107 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | ||
| 108 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); | ||
| 109 | } | ||
| 110 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | ||
| 111 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | #[cfg(all(bank_setup_configurable, flash_g0x1))] | ||
| 116 | pub(crate) fn check_bank_setup() { | ||
| 117 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { | ||
| 118 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config"); | ||
| 119 | } | ||
| 120 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { | ||
| 121 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config"); | ||
| 122 | } | ||
| 123 | } | ||
diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs new file mode 100644 index 000000000..fd9bfcc75 --- /dev/null +++ b/embassy-stm32/src/flash/h5.rs | |||
| @@ -0,0 +1,166 @@ | |||
| 1 | use core::ptr::write_volatile; | ||
| 2 | use core::sync::atomic::{fence, Ordering}; | ||
| 3 | |||
| 4 | use super::{FlashSector, WRITE_SIZE}; | ||
| 5 | use crate::flash::Error; | ||
| 6 | use crate::pac; | ||
| 7 | |||
| 8 | pub(crate) unsafe fn lock() { | ||
| 9 | if !pac::FLASH.nscr().read().lock() { | ||
| 10 | pac::FLASH.nscr().modify(|r| { | ||
| 11 | r.set_lock(true); | ||
| 12 | }); | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | pub(crate) unsafe fn unlock() { | ||
| 17 | // TODO: check locked first | ||
| 18 | while pac::FLASH.nssr().read().bsy() { | ||
| 19 | #[cfg(feature = "defmt")] | ||
| 20 | defmt::trace!("busy") | ||
| 21 | } | ||
| 22 | |||
| 23 | // only unlock if locked to begin with | ||
| 24 | if pac::FLASH.nscr().read().lock() { | ||
| 25 | pac::FLASH.nskeyr().write_value(0x4567_0123); | ||
| 26 | pac::FLASH.nskeyr().write_value(0xCDEF_89AB); | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | pub(crate) unsafe fn enable_blocking_write() { | ||
| 31 | assert_eq!(0, WRITE_SIZE % 4); | ||
| 32 | } | ||
| 33 | |||
| 34 | pub(crate) unsafe fn disable_blocking_write() {} | ||
| 35 | |||
| 36 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | ||
| 37 | // // We cannot have the write setup sequence in begin_write as it depends on the address | ||
| 38 | // let bank = if start_address < BANK1_REGION.end() { | ||
| 39 | // pac::FLASH.bank(0) | ||
| 40 | // } else { | ||
| 41 | // pac::FLASH.bank(1) | ||
| 42 | // }; | ||
| 43 | |||
| 44 | cortex_m::asm::isb(); | ||
| 45 | cortex_m::asm::dsb(); | ||
| 46 | fence(Ordering::SeqCst); | ||
| 47 | |||
| 48 | clear_all_err(); | ||
| 49 | |||
| 50 | pac::FLASH.nscr().write(|w| { | ||
| 51 | w.set_pg(true); | ||
| 52 | // w.set_psize(2); // 32 bits at once | ||
| 53 | }); | ||
| 54 | |||
| 55 | let mut res = None; | ||
| 56 | let mut address = start_address; | ||
| 57 | // TODO: see write size | ||
| 58 | for val in buf.chunks(4) { | ||
| 59 | write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); | ||
| 60 | address += val.len() as u32; | ||
| 61 | |||
| 62 | res = Some(blocking_wait_ready().map_err(|e| { | ||
| 63 | error!("write err"); | ||
| 64 | e | ||
| 65 | })); | ||
| 66 | pac::FLASH.nssr().modify(|w| { | ||
| 67 | if w.eop() { | ||
| 68 | w.set_eop(true); | ||
| 69 | } | ||
| 70 | }); | ||
| 71 | if unwrap!(res).is_err() { | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | cortex_m::asm::isb(); | ||
| 77 | cortex_m::asm::dsb(); | ||
| 78 | fence(Ordering::SeqCst); | ||
| 79 | |||
| 80 | pac::FLASH.nscr().write(|w| w.set_pg(false)); | ||
| 81 | |||
| 82 | unwrap!(res) | ||
| 83 | } | ||
| 84 | |||
| 85 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | ||
| 86 | // pac::FLASH.wrp2r_cur().read().wrpsg() | ||
| 87 | // TODO: write protection check | ||
| 88 | if pac::FLASH.nscr().read().lock() == true { | ||
| 89 | error!("flash locked"); | ||
| 90 | } | ||
| 91 | |||
| 92 | loop { | ||
| 93 | let sr = pac::FLASH.nssr().read(); | ||
| 94 | if !sr.bsy() && !sr.dbne() { | ||
| 95 | break; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | clear_all_err(); | ||
| 99 | |||
| 100 | pac::FLASH.nscr().modify(|r| { | ||
| 101 | // TODO: later check bank swap | ||
| 102 | r.set_bksel(match sector.bank { | ||
| 103 | crate::flash::FlashBank::Bank1 => stm32_metapac::flash::vals::NscrBksel::B_0X0, | ||
| 104 | crate::flash::FlashBank::Bank2 => stm32_metapac::flash::vals::NscrBksel::B_0X1, | ||
| 105 | _ => unreachable!(), | ||
| 106 | }); | ||
| 107 | r.set_snb(sector.index_in_bank); | ||
| 108 | r.set_ser(true); | ||
| 109 | }); | ||
| 110 | |||
| 111 | pac::FLASH.nscr().modify(|r| { | ||
| 112 | r.set_strt(true); | ||
| 113 | }); | ||
| 114 | |||
| 115 | cortex_m::asm::isb(); | ||
| 116 | cortex_m::asm::dsb(); | ||
| 117 | fence(Ordering::SeqCst); | ||
| 118 | |||
| 119 | let ret: Result<(), Error> = blocking_wait_ready().map_err(|e| { | ||
| 120 | error!("erase err"); | ||
| 121 | e | ||
| 122 | }); | ||
| 123 | |||
| 124 | pac::FLASH.nscr().modify(|w| w.set_ser(false)); | ||
| 125 | clear_all_err(); | ||
| 126 | ret | ||
| 127 | } | ||
| 128 | |||
| 129 | pub(crate) unsafe fn clear_all_err() { | ||
| 130 | pac::FLASH.nssr().modify(|_| {}) | ||
| 131 | } | ||
| 132 | |||
| 133 | unsafe fn blocking_wait_ready() -> Result<(), Error> { | ||
| 134 | loop { | ||
| 135 | let sr = pac::FLASH.nssr().read(); | ||
| 136 | |||
| 137 | if !sr.bsy() { | ||
| 138 | if sr.optchangeerr() { | ||
| 139 | error!("optchangeerr"); | ||
| 140 | return Err(Error::Prog); | ||
| 141 | } | ||
| 142 | if sr.obkwerr() { | ||
| 143 | error!("obkwerr"); | ||
| 144 | return Err(Error::Seq); | ||
| 145 | } | ||
| 146 | if sr.obkerr() { | ||
| 147 | error!("obkerr"); | ||
| 148 | return Err(Error::Seq); | ||
| 149 | } | ||
| 150 | if sr.incerr() { | ||
| 151 | error!("incerr"); | ||
| 152 | return Err(Error::Unaligned); | ||
| 153 | } | ||
| 154 | if sr.strberr() { | ||
| 155 | error!("strberr"); | ||
| 156 | return Err(Error::Parallelism); | ||
| 157 | } | ||
| 158 | if sr.wrperr() { | ||
| 159 | error!("protected"); | ||
| 160 | return Err(Error::Protected); | ||
| 161 | } | ||
| 162 | |||
| 163 | return Ok(()); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index 82e77d130..f8e210556 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs | |||
| @@ -8,17 +8,9 @@ use cortex_m::interrupt; | |||
| 8 | use pac::flash::regs::Nssr; | 8 | use pac::flash::regs::Nssr; |
| 9 | use pac::flash::vals::Bksel; | 9 | use pac::flash::vals::Bksel; |
| 10 | 10 | ||
| 11 | use super::{Error, FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 11 | use super::{Error, FlashBank, FlashSector, WRITE_SIZE}; |
| 12 | use crate::pac; | 12 | use crate::pac; |
| 13 | 13 | ||
| 14 | pub(crate) const fn is_default_layout() -> bool { | ||
| 15 | true | ||
| 16 | } | ||
| 17 | |||
| 18 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 19 | &FLASH_REGIONS | ||
| 20 | } | ||
| 21 | |||
| 22 | pub(crate) unsafe fn lock() { | 14 | pub(crate) unsafe fn lock() { |
| 23 | pac::FLASH.nscr().modify(|w| w.set_lock(true)); | 15 | pac::FLASH.nscr().modify(|w| w.set_lock(true)); |
| 24 | } | 16 | } |
| @@ -55,6 +47,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) | |||
| 55 | } | 47 | } |
| 56 | 48 | ||
| 57 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { | 49 | pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { |
| 50 | assert!(sector.bank != FlashBank::Otp); | ||
| 58 | assert!(sector.index_in_bank < 8); | 51 | assert!(sector.index_in_bank < 8); |
| 59 | 52 | ||
| 60 | while busy() {} | 53 | while busy() {} |
| @@ -67,6 +60,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 67 | (FlashBank::Bank2, true) => Bksel::BANK1, | 60 | (FlashBank::Bank2, true) => Bksel::BANK1, |
| 68 | (FlashBank::Bank2, false) => Bksel::BANK2, | 61 | (FlashBank::Bank2, false) => Bksel::BANK2, |
| 69 | (FlashBank::Bank1, true) => Bksel::BANK2, | 62 | (FlashBank::Bank1, true) => Bksel::BANK2, |
| 63 | _ => unreachable!(), | ||
| 70 | }); | 64 | }); |
| 71 | w.set_snb(sector.index_in_bank); | 65 | w.set_snb(sector.index_in_bank); |
| 72 | w.set_ser(true); | 66 | w.set_ser(true); |
diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 254915381..f1d84101c 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs | |||
| @@ -1,22 +1,14 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
| 8 | pub(crate) const fn is_default_layout() -> bool { | ||
| 9 | true | ||
| 10 | } | ||
| 11 | |||
| 12 | const fn is_dual_bank() -> bool { | 8 | const fn is_dual_bank() -> bool { |
| 13 | FLASH_REGIONS.len() >= 2 | 9 | FLASH_REGIONS.len() >= 2 |
| 14 | } | 10 | } |
| 15 | 11 | ||
| 16 | pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 17 | &FLASH_REGIONS | ||
| 18 | } | ||
| 19 | |||
| 20 | pub(crate) unsafe fn lock() { | 12 | pub(crate) unsafe fn lock() { |
| 21 | pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); | 13 | pac::FLASH.bank(0).cr().modify(|w| w.set_lock(true)); |
| 22 | if is_dual_bank() { | 14 | if is_dual_bank() { |
diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index a0bfeb395..1b82704ec 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs | |||
| @@ -1,18 +1,10 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
| 8 | pub(crate) const fn is_default_layout() -> bool { | ||
| 9 | true | ||
| 10 | } | ||
| 11 | |||
| 12 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 13 | &FLASH_REGIONS | ||
| 14 | } | ||
| 15 | |||
| 16 | pub(crate) unsafe fn lock() { | 8 | pub(crate) unsafe fn lock() { |
| 17 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 9 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 18 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 10 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| @@ -23,6 +15,9 @@ pub(crate) unsafe fn lock() { | |||
| 23 | w.set_prglock(true); | 15 | w.set_prglock(true); |
| 24 | w.set_pelock(true); | 16 | w.set_pelock(true); |
| 25 | }); | 17 | }); |
| 18 | |||
| 19 | #[cfg(any(flash_l5))] | ||
| 20 | pac::FLASH.nscr().modify(|w| w.set_nslock(true)); | ||
| 26 | } | 21 | } |
| 27 | 22 | ||
| 28 | pub(crate) unsafe fn unlock() { | 23 | pub(crate) unsafe fn unlock() { |
| @@ -46,6 +41,14 @@ pub(crate) unsafe fn unlock() { | |||
| 46 | pac::FLASH.prgkeyr().write_value(0x1314_1516); | 41 | pac::FLASH.prgkeyr().write_value(0x1314_1516); |
| 47 | } | 42 | } |
| 48 | } | 43 | } |
| 44 | |||
| 45 | #[cfg(any(flash_l5))] | ||
| 46 | { | ||
| 47 | if pac::FLASH.nscr().read().nslock() { | ||
| 48 | pac::FLASH.nskeyr().write_value(0x4567_0123); | ||
| 49 | pac::FLASH.nskeyr().write_value(0xCDEF_89AB); | ||
| 50 | } | ||
| 51 | } | ||
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | pub(crate) unsafe fn enable_blocking_write() { | 54 | pub(crate) unsafe fn enable_blocking_write() { |
| @@ -53,11 +56,17 @@ pub(crate) unsafe fn enable_blocking_write() { | |||
| 53 | 56 | ||
| 54 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 57 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 55 | pac::FLASH.cr().write(|w| w.set_pg(true)); | 58 | pac::FLASH.cr().write(|w| w.set_pg(true)); |
| 59 | |||
| 60 | #[cfg(any(flash_l5))] | ||
| 61 | pac::FLASH.nscr().write(|w| w.set_nspg(true)); | ||
| 56 | } | 62 | } |
| 57 | 63 | ||
| 58 | pub(crate) unsafe fn disable_blocking_write() { | 64 | pub(crate) unsafe fn disable_blocking_write() { |
| 59 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 65 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 60 | pac::FLASH.cr().write(|w| w.set_pg(false)); | 66 | pac::FLASH.cr().write(|w| w.set_pg(false)); |
| 67 | |||
| 68 | #[cfg(any(flash_l5))] | ||
| 69 | pac::FLASH.nscr().write(|w| w.set_nspg(false)); | ||
| 61 | } | 70 | } |
| 62 | 71 | ||
| 63 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { | 72 | pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { |
| @@ -84,13 +93,25 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 84 | write_volatile(sector.start as *mut u32, 0xFFFFFFFF); | 93 | write_volatile(sector.start as *mut u32, 0xFFFFFFFF); |
| 85 | } | 94 | } |
| 86 | 95 | ||
| 87 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 96 | #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] |
| 88 | { | 97 | { |
| 89 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; | 98 | let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; |
| 90 | 99 | ||
| 91 | #[cfg(flash_l4)] | 100 | #[cfg(flash_l4)] |
| 92 | let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; | 101 | let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; |
| 93 | 102 | ||
| 103 | #[cfg(flash_l5)] | ||
| 104 | let (idx, bank) = if pac::FLASH.optr().read().dbank() { | ||
| 105 | if idx > 255 { | ||
| 106 | (idx - 256, Some(true)) | ||
| 107 | } else { | ||
| 108 | (idx, Some(false)) | ||
| 109 | } | ||
| 110 | } else { | ||
| 111 | (idx, None) | ||
| 112 | }; | ||
| 113 | |||
| 114 | #[cfg(not(flash_l5))] | ||
| 94 | pac::FLASH.cr().modify(|w| { | 115 | pac::FLASH.cr().modify(|w| { |
| 95 | w.set_per(true); | 116 | w.set_per(true); |
| 96 | w.set_pnb(idx as u8); | 117 | w.set_pnb(idx as u8); |
| @@ -101,6 +122,16 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 101 | #[cfg(any(flash_l4))] | 122 | #[cfg(any(flash_l4))] |
| 102 | w.set_bker(bank); | 123 | w.set_bker(bank); |
| 103 | }); | 124 | }); |
| 125 | |||
| 126 | #[cfg(flash_l5)] | ||
| 127 | pac::FLASH.nscr().modify(|w| { | ||
| 128 | w.set_nsper(true); | ||
| 129 | w.set_nspnb(idx as u8); | ||
| 130 | if let Some(bank) = bank { | ||
| 131 | w.set_nsbker(bank); | ||
| 132 | } | ||
| 133 | w.set_nsstrt(true); | ||
| 134 | }); | ||
| 104 | } | 135 | } |
| 105 | 136 | ||
| 106 | let ret: Result<(), Error> = wait_ready_blocking(); | 137 | let ret: Result<(), Error> = wait_ready_blocking(); |
| @@ -108,6 +139,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 108 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 139 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 109 | pac::FLASH.cr().modify(|w| w.set_per(false)); | 140 | pac::FLASH.cr().modify(|w| w.set_per(false)); |
| 110 | 141 | ||
| 142 | #[cfg(any(flash_l5))] | ||
| 143 | pac::FLASH.nscr().modify(|w| w.set_nsper(false)); | ||
| 144 | |||
| 111 | #[cfg(any(flash_l0, flash_l1))] | 145 | #[cfg(any(flash_l0, flash_l1))] |
| 112 | pac::FLASH.pecr().modify(|w| { | 146 | pac::FLASH.pecr().modify(|w| { |
| 113 | w.set_erase(false); | 147 | w.set_erase(false); |
| @@ -121,42 +155,98 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 121 | pub(crate) unsafe fn clear_all_err() { | 155 | pub(crate) unsafe fn clear_all_err() { |
| 122 | // read and write back the same value. | 156 | // read and write back the same value. |
| 123 | // This clears all "write 1 to clear" bits. | 157 | // This clears all "write 1 to clear" bits. |
| 158 | #[cfg(not(flash_l5))] | ||
| 124 | pac::FLASH.sr().modify(|_| {}); | 159 | pac::FLASH.sr().modify(|_| {}); |
| 160 | |||
| 161 | #[cfg(flash_l5)] | ||
| 162 | pac::FLASH.nssr().modify(|_| {}); | ||
| 125 | } | 163 | } |
| 126 | 164 | ||
| 127 | unsafe fn wait_ready_blocking() -> Result<(), Error> { | 165 | pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { |
| 128 | loop { | 166 | loop { |
| 129 | let sr = pac::FLASH.sr().read(); | 167 | #[cfg(not(flash_l5))] |
| 130 | 168 | { | |
| 131 | if !sr.bsy() { | 169 | let sr = pac::FLASH.sr().read(); |
| 132 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 170 | |
| 133 | if sr.progerr() { | 171 | if !sr.bsy() { |
| 134 | return Err(Error::Prog); | 172 | #[cfg(any(flash_wl, flash_wb, flash_l4))] |
| 173 | if sr.progerr() { | ||
| 174 | return Err(Error::Prog); | ||
| 175 | } | ||
| 176 | |||
| 177 | if sr.wrperr() { | ||
| 178 | return Err(Error::Protected); | ||
| 179 | } | ||
| 180 | |||
| 181 | if sr.pgaerr() { | ||
| 182 | return Err(Error::Unaligned); | ||
| 183 | } | ||
| 184 | |||
| 185 | if sr.sizerr() { | ||
| 186 | return Err(Error::Size); | ||
| 187 | } | ||
| 188 | |||
| 189 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | ||
| 190 | if sr.miserr() { | ||
| 191 | return Err(Error::Miss); | ||
| 192 | } | ||
| 193 | |||
| 194 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | ||
| 195 | if sr.pgserr() { | ||
| 196 | return Err(Error::Seq); | ||
| 197 | } | ||
| 198 | |||
| 199 | return Ok(()); | ||
| 135 | } | 200 | } |
| 201 | } | ||
| 136 | 202 | ||
| 137 | if sr.wrperr() { | 203 | #[cfg(flash_l5)] |
| 138 | return Err(Error::Protected); | 204 | { |
| 139 | } | 205 | let nssr = pac::FLASH.nssr().read(); |
| 140 | 206 | ||
| 141 | if sr.pgaerr() { | 207 | if !nssr.nsbsy() { |
| 142 | return Err(Error::Unaligned); | 208 | if nssr.nsprogerr() { |
| 143 | } | 209 | return Err(Error::Prog); |
| 210 | } | ||
| 144 | 211 | ||
| 145 | if sr.sizerr() { | 212 | if nssr.nswrperr() { |
| 146 | return Err(Error::Size); | 213 | return Err(Error::Protected); |
| 147 | } | 214 | } |
| 148 | 215 | ||
| 149 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 216 | if nssr.nspgaerr() { |
| 150 | if sr.miserr() { | 217 | return Err(Error::Unaligned); |
| 151 | return Err(Error::Miss); | 218 | } |
| 152 | } | ||
| 153 | 219 | ||
| 154 | #[cfg(any(flash_wl, flash_wb, flash_l4))] | 220 | if nssr.nssizerr() { |
| 155 | if sr.pgserr() { | 221 | return Err(Error::Size); |
| 156 | return Err(Error::Seq); | 222 | } |
| 157 | } | 223 | |
| 224 | if nssr.nspgserr() { | ||
| 225 | return Err(Error::Seq); | ||
| 226 | } | ||
| 158 | 227 | ||
| 159 | return Ok(()); | 228 | return Ok(()); |
| 229 | } | ||
| 160 | } | 230 | } |
| 161 | } | 231 | } |
| 162 | } | 232 | } |
| 233 | |||
| 234 | #[cfg(all(bank_setup_configurable, flash_l5))] | ||
| 235 | pub(crate) fn check_bank_setup() { | ||
| 236 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { | ||
| 237 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); | ||
| 238 | } | ||
| 239 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { | ||
| 240 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | #[cfg(all(bank_setup_configurable, flash_l4))] | ||
| 245 | pub(crate) fn check_bank_setup() { | ||
| 246 | if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { | ||
| 247 | panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config"); | ||
| 248 | } | ||
| 249 | if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { | ||
| 250 | panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config"); | ||
| 251 | } | ||
| 252 | } | ||
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index ce2d1a04c..a3f9e00f7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs | |||
| @@ -5,27 +5,25 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; | |||
| 5 | mod asynch; | 5 | mod asynch; |
| 6 | #[cfg(flash)] | 6 | #[cfg(flash)] |
| 7 | mod common; | 7 | mod common; |
| 8 | #[cfg(eeprom)] | ||
| 9 | mod eeprom; | ||
| 8 | 10 | ||
| 9 | #[cfg(flash_f4)] | 11 | #[cfg(flash_f4)] |
| 10 | pub use asynch::InterruptHandler; | 12 | pub use asynch::InterruptHandler; |
| 11 | #[cfg(flash)] | 13 | #[cfg(flash)] |
| 12 | pub use common::*; | 14 | pub use common::*; |
| 15 | #[cfg(eeprom)] | ||
| 16 | #[allow(unused_imports)] | ||
| 17 | pub use eeprom::*; | ||
| 13 | 18 | ||
| 14 | pub use crate::_generated::flash_regions::*; | 19 | pub use crate::_generated::flash_regions::*; |
| 15 | pub use crate::_generated::MAX_ERASE_SIZE; | 20 | #[cfg(eeprom)] |
| 16 | pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; | 21 | pub use crate::_generated::{EEPROM_BASE, EEPROM_SIZE}; |
| 17 | 22 | pub use crate::_generated::{FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; | |
| 18 | /// Get whether the default flash layout is being used. | ||
| 19 | /// | ||
| 20 | /// In some chips, dual-bank is not default. This will then return `false` | ||
| 21 | /// when dual-bank is enabled. | ||
| 22 | pub fn is_default_layout() -> bool { | ||
| 23 | family::is_default_layout() | ||
| 24 | } | ||
| 25 | 23 | ||
| 26 | /// Get all flash regions. | 24 | /// Get all flash regions. |
| 27 | pub fn get_flash_regions() -> &'static [&'static FlashRegion] { | 25 | pub fn get_flash_regions() -> &'static [&'static FlashRegion] { |
| 28 | family::get_flash_regions() | 26 | &FLASH_REGIONS |
| 29 | } | 27 | } |
| 30 | 28 | ||
| 31 | /// Read size (always 1) | 29 | /// Read size (always 1) |
| @@ -89,23 +87,29 @@ pub enum FlashBank { | |||
| 89 | Bank1 = 0, | 87 | Bank1 = 0, |
| 90 | /// Bank 2 | 88 | /// Bank 2 |
| 91 | Bank2 = 1, | 89 | Bank2 = 1, |
| 90 | /// OTP region, | ||
| 91 | Otp, | ||
| 92 | } | 92 | } |
| 93 | 93 | #[cfg(all(eeprom, not(any(flash_l0, flash_l1))))] | |
| 94 | #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] | 94 | compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is an unsupported configuration."); |
| 95 | #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] | ||
| 95 | #[cfg_attr(flash_f0, path = "f0.rs")] | 96 | #[cfg_attr(flash_f0, path = "f0.rs")] |
| 96 | #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.rs")] | 97 | #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.rs")] |
| 98 | #[cfg_attr(flash_f2, path = "f2.rs")] | ||
| 97 | #[cfg_attr(flash_f4, path = "f4.rs")] | 99 | #[cfg_attr(flash_f4, path = "f4.rs")] |
| 98 | #[cfg_attr(flash_f7, path = "f7.rs")] | 100 | #[cfg_attr(flash_f7, path = "f7.rs")] |
| 99 | #[cfg_attr(any(flash_g0, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] | 101 | #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] |
| 100 | #[cfg_attr(flash_h7, path = "h7.rs")] | 102 | #[cfg_attr(flash_h7, path = "h7.rs")] |
| 101 | #[cfg_attr(flash_h7ab, path = "h7.rs")] | 103 | #[cfg_attr(flash_h7ab, path = "h7.rs")] |
| 102 | #[cfg_attr(flash_u5, path = "u5.rs")] | 104 | #[cfg_attr(flash_u5, path = "u5.rs")] |
| 105 | #[cfg_attr(flash_h5, path = "h5.rs")] | ||
| 103 | #[cfg_attr(flash_h50, path = "h50.rs")] | 106 | #[cfg_attr(flash_h50, path = "h50.rs")] |
| 104 | #[cfg_attr(flash_u0, path = "u0.rs")] | 107 | #[cfg_attr(flash_u0, path = "u0.rs")] |
| 105 | #[cfg_attr( | 108 | #[cfg_attr( |
| 106 | not(any( | 109 | not(any( |
| 107 | flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0, | 110 | flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, |
| 108 | flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0 | 111 | flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, |
| 112 | flash_h50, flash_u0, flash_h5, | ||
| 109 | )), | 113 | )), |
| 110 | path = "other.rs" | 114 | path = "other.rs" |
| 111 | )] | 115 | )] |
diff --git a/embassy-stm32/src/flash/other.rs b/embassy-stm32/src/flash/other.rs index 20f84a72f..293a79be3 100644 --- a/embassy-stm32/src/flash/other.rs +++ b/embassy-stm32/src/flash/other.rs | |||
| @@ -1,14 +1,6 @@ | |||
| 1 | #![allow(unused)] | 1 | #![allow(unused)] |
| 2 | 2 | ||
| 3 | use super::{Error, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 3 | use super::{Error, FlashSector, WRITE_SIZE}; |
| 4 | |||
| 5 | pub(crate) const fn is_default_layout() -> bool { | ||
| 6 | true | ||
| 7 | } | ||
| 8 | |||
| 9 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 10 | &FLASH_REGIONS | ||
| 11 | } | ||
| 12 | 4 | ||
| 13 | pub(crate) unsafe fn lock() { | 5 | pub(crate) unsafe fn lock() { |
| 14 | unimplemented!(); | 6 | unimplemented!(); |
diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs index bfdbd15a5..68d847eca 100644 --- a/embassy-stm32/src/flash/u0.rs +++ b/embassy-stm32/src/flash/u0.rs | |||
| @@ -3,18 +3,10 @@ use core::sync::atomic::{fence, Ordering}; | |||
| 3 | 3 | ||
| 4 | use cortex_m::interrupt; | 4 | use cortex_m::interrupt; |
| 5 | 5 | ||
| 6 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 6 | use super::{FlashSector, WRITE_SIZE}; |
| 7 | use crate::flash::Error; | 7 | use crate::flash::Error; |
| 8 | use crate::pac; | 8 | use crate::pac; |
| 9 | 9 | ||
| 10 | pub(crate) const fn is_default_layout() -> bool { | ||
| 11 | true | ||
| 12 | } | ||
| 13 | |||
| 14 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 15 | &FLASH_REGIONS | ||
| 16 | } | ||
| 17 | |||
| 18 | pub(crate) unsafe fn lock() { | 10 | pub(crate) unsafe fn lock() { |
| 19 | pac::FLASH.cr().modify(|w| w.set_lock(true)); | 11 | pac::FLASH.cr().modify(|w| w.set_lock(true)); |
| 20 | } | 12 | } |
diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 0601017ce..131caa195 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs | |||
| @@ -1,18 +1,10 @@ | |||
| 1 | use core::ptr::write_volatile; | 1 | use core::ptr::write_volatile; |
| 2 | use core::sync::atomic::{fence, Ordering}; | 2 | use core::sync::atomic::{fence, Ordering}; |
| 3 | 3 | ||
| 4 | use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; | 4 | use super::{FlashBank, FlashSector, WRITE_SIZE}; |
| 5 | use crate::flash::Error; | 5 | use crate::flash::Error; |
| 6 | use crate::pac; | 6 | use crate::pac; |
| 7 | 7 | ||
| 8 | pub(crate) const fn is_default_layout() -> bool { | ||
| 9 | true | ||
| 10 | } | ||
| 11 | |||
| 12 | pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { | ||
| 13 | &FLASH_REGIONS | ||
| 14 | } | ||
| 15 | |||
| 16 | pub(crate) unsafe fn lock() { | 8 | pub(crate) unsafe fn lock() { |
| 17 | #[cfg(feature = "trustzone-secure")] | 9 | #[cfg(feature = "trustzone-secure")] |
| 18 | pac::FLASH.seccr().modify(|w| w.set_lock(true)); | 10 | pac::FLASH.seccr().modify(|w| w.set_lock(true)); |
| @@ -70,12 +62,24 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E | |||
| 70 | #[cfg(feature = "trustzone-secure")] | 62 | #[cfg(feature = "trustzone-secure")] |
| 71 | pac::FLASH.seccr().modify(|w| { | 63 | pac::FLASH.seccr().modify(|w| { |
| 72 | w.set_per(pac::flash::vals::SeccrPer::B_0X1); | 64 | w.set_per(pac::flash::vals::SeccrPer::B_0X1); |
| 73 | w.set_pnb(sector.index_in_bank) | 65 | w.set_pnb(sector.index_in_bank); |
| 66 | // TODO: add check for bank swap | ||
| 67 | w.set_bker(match sector.bank { | ||
| 68 | FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, | ||
| 69 | FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, | ||
| 70 | _ => unreachable!(), | ||
| 71 | }); | ||
| 74 | }); | 72 | }); |
| 75 | #[cfg(not(feature = "trustzone-secure"))] | 73 | #[cfg(not(feature = "trustzone-secure"))] |
| 76 | pac::FLASH.nscr().modify(|w| { | 74 | pac::FLASH.nscr().modify(|w| { |
| 77 | w.set_per(pac::flash::vals::NscrPer::B_0X1); | 75 | w.set_per(pac::flash::vals::NscrPer::B_0X1); |
| 78 | w.set_pnb(sector.index_in_bank) | 76 | w.set_pnb(sector.index_in_bank); |
| 77 | // TODO: add check for bank swap | ||
| 78 | w.set_bker(match sector.bank { | ||
| 79 | FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, | ||
| 80 | FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, | ||
| 81 | _ => unreachable!(), | ||
| 82 | }); | ||
| 79 | }); | 83 | }); |
| 80 | 84 | ||
| 81 | #[cfg(feature = "trustzone-secure")] | 85 | #[cfg(feature = "trustzone-secure")] |
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 83b49a3dd..71ca775cb 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | //! Flexible Memory Controller (FMC) / Flexible Static Memory Controller (FSMC) | 1 | //! Flexible Memory Controller (FMC) / Flexible Static Memory Controller (FSMC) |
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::into_ref; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::gpio::{AfType, OutputType, Pull, Speed}; | 6 | use crate::gpio::{AfType, OutputType, Pull, Speed}; |
| 7 | use crate::{rcc, Peripheral}; | 7 | use crate::{rcc, Peri}; |
| 8 | 8 | ||
| 9 | /// FMC driver | 9 | /// FMC driver |
| 10 | pub struct Fmc<'d, T: Instance> { | 10 | pub struct Fmc<'d, T: Instance> { |
| @@ -21,7 +21,7 @@ where | |||
| 21 | /// | 21 | /// |
| 22 | /// **Note:** This is currently used to provide access to some basic FMC functions | 22 | /// **Note:** This is currently used to provide access to some basic FMC functions |
| 23 | /// for manual configuration for memory types that stm32-fmc does not support. | 23 | /// for manual configuration for memory types that stm32-fmc does not support. |
| 24 | pub fn new_raw(_instance: impl Peripheral<P = T> + 'd) -> Self { | 24 | pub fn new_raw(_instance: Peri<'d, T>) -> Self { |
| 25 | Self { peri: PhantomData } | 25 | Self { peri: PhantomData } |
| 26 | } | 26 | } |
| 27 | 27 | ||
| @@ -74,8 +74,7 @@ where | |||
| 74 | 74 | ||
| 75 | macro_rules! config_pins { | 75 | macro_rules! config_pins { |
| 76 | ($($pin:ident),*) => { | 76 | ($($pin:ident),*) => { |
| 77 | into_ref!($($pin),*); | 77 | $( |
| 78 | $( | ||
| 79 | $pin.set_as_af($pin.af_num(), AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); | 78 | $pin.set_as_af($pin.af_num(), AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); |
| 80 | )* | 79 | )* |
| 81 | }; | 80 | }; |
| @@ -92,12 +91,12 @@ macro_rules! fmc_sdram_constructor { | |||
| 92 | )) => { | 91 | )) => { |
| 93 | /// Create a new FMC instance. | 92 | /// Create a new FMC instance. |
| 94 | pub fn $name<CHIP: stm32_fmc::SdramChip>( | 93 | pub fn $name<CHIP: stm32_fmc::SdramChip>( |
| 95 | _instance: impl Peripheral<P = T> + 'd, | 94 | _instance: Peri<'d, T>, |
| 96 | $($addr_pin_name: impl Peripheral<P = impl $addr_signal<T>> + 'd),*, | 95 | $($addr_pin_name: Peri<'d, impl $addr_signal<T>>),*, |
| 97 | $($ba_pin_name: impl Peripheral<P = impl $ba_signal<T>> + 'd),*, | 96 | $($ba_pin_name: Peri<'d, impl $ba_signal<T>>),*, |
| 98 | $($d_pin_name: impl Peripheral<P = impl $d_signal<T>> + 'd),*, | 97 | $($d_pin_name: Peri<'d, impl $d_signal<T>>),*, |
| 99 | $($nbl_pin_name: impl Peripheral<P = impl $nbl_signal<T>> + 'd),*, | 98 | $($nbl_pin_name: Peri<'d, impl $nbl_signal<T>>),*, |
| 100 | $($ctrl_pin_name: impl Peripheral<P = impl $ctrl_signal<T>> + 'd),*, | 99 | $($ctrl_pin_name: Peri<'d, impl $ctrl_signal<T>>),*, |
| 101 | chip: CHIP | 100 | chip: CHIP |
| 102 | ) -> stm32_fmc::Sdram<Fmc<'d, T>, CHIP> { | 101 | ) -> stm32_fmc::Sdram<Fmc<'d, T>, CHIP> { |
| 103 | 102 | ||
| @@ -245,7 +244,7 @@ trait SealedInstance: crate::rcc::RccPeripheral { | |||
| 245 | 244 | ||
| 246 | /// FMC instance trait. | 245 | /// FMC instance trait. |
| 247 | #[allow(private_bounds)] | 246 | #[allow(private_bounds)] |
| 248 | pub trait Instance: SealedInstance + 'static {} | 247 | pub trait Instance: SealedInstance + PeripheralType + 'static {} |
| 249 | 248 | ||
| 250 | foreach_peripheral!( | 249 | foreach_peripheral!( |
| 251 | (fmc, $inst:ident) => { | 250 | (fmc, $inst:ident) => { |
diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index 8ca61bc39..b6ae24ee8 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs | |||
| @@ -7,6 +7,28 @@ use core::fmt::{Debug, Display, LowerHex}; | |||
| 7 | compile_error!("You may not enable both `defmt` and `log` features."); | 7 | compile_error!("You may not enable both `defmt` and `log` features."); |
| 8 | 8 | ||
| 9 | #[collapse_debuginfo(yes)] | 9 | #[collapse_debuginfo(yes)] |
| 10 | macro_rules! rcc_assert { | ||
| 11 | ($($x:tt)*) => { | ||
| 12 | { | ||
| 13 | #[cfg(not(feature = "unchecked-overclocking"))] | ||
| 14 | { | ||
| 15 | #[cfg(not(feature = "defmt"))] | ||
| 16 | ::core::assert!($($x)*); | ||
| 17 | #[cfg(feature = "defmt")] | ||
| 18 | ::defmt::assert!($($x)*); | ||
| 19 | } | ||
| 20 | #[cfg(feature = "unchecked-overclocking")] | ||
| 21 | { | ||
| 22 | #[cfg(feature = "log")] | ||
| 23 | ::log::warn!("`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled."); | ||
| 24 | #[cfg(feature = "defmt")] | ||
| 25 | ::defmt::warn!("`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled."); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | } | ||
| 30 | |||
| 31 | #[collapse_debuginfo(yes)] | ||
| 10 | macro_rules! assert { | 32 | macro_rules! assert { |
| 11 | ($($x:tt)*) => { | 33 | ($($x:tt)*) => { |
| 12 | { | 34 | { |
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 87519f51e..bb37c4194 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -4,10 +4,10 @@ | |||
| 4 | use core::convert::Infallible; | 4 | use core::convert::Infallible; |
| 5 | 5 | ||
| 6 | use critical_section::CriticalSection; | 6 | use critical_section::CriticalSection; |
| 7 | use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; | 7 | use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; |
| 8 | 8 | ||
| 9 | use crate::pac::gpio::{self, vals}; | 9 | use crate::pac::gpio::{self, vals}; |
| 10 | use crate::{pac, peripherals, Peripheral}; | 10 | use crate::peripherals; |
| 11 | 11 | ||
| 12 | /// GPIO flexible pin. | 12 | /// GPIO flexible pin. |
| 13 | /// | 13 | /// |
| @@ -15,7 +15,7 @@ use crate::{pac, peripherals, Peripheral}; | |||
| 15 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output | 15 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output |
| 16 | /// mode. | 16 | /// mode. |
| 17 | pub struct Flex<'d> { | 17 | pub struct Flex<'d> { |
| 18 | pub(crate) pin: PeripheralRef<'d, AnyPin>, | 18 | pub(crate) pin: Peri<'d, AnyPin>, |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | impl<'d> Flex<'d> { | 21 | impl<'d> Flex<'d> { |
| @@ -25,10 +25,9 @@ impl<'d> Flex<'d> { | |||
| 25 | /// before the pin is put into output mode. | 25 | /// before the pin is put into output mode. |
| 26 | /// | 26 | /// |
| 27 | #[inline] | 27 | #[inline] |
| 28 | pub fn new(pin: impl Peripheral<P = impl Pin> + 'd) -> Self { | 28 | pub fn new(pin: Peri<'d, impl Pin>) -> Self { |
| 29 | into_ref!(pin); | ||
| 30 | // Pin will be in disconnected state. | 29 | // Pin will be in disconnected state. |
| 31 | Self { pin: pin.map_into() } | 30 | Self { pin: pin.into() } |
| 32 | } | 31 | } |
| 33 | 32 | ||
| 34 | /// Put the pin into input mode. | 33 | /// Put the pin into input mode. |
| @@ -61,7 +60,7 @@ impl<'d> Flex<'d> { | |||
| 61 | #[cfg(gpio_v2)] | 60 | #[cfg(gpio_v2)] |
| 62 | { | 61 | { |
| 63 | r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr())); | 62 | r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr())); |
| 64 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL)); | 63 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSH_PULL)); |
| 65 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); | 64 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); |
| 66 | } | 65 | } |
| 67 | }); | 66 | }); |
| @@ -82,13 +81,13 @@ impl<'d> Flex<'d> { | |||
| 82 | { | 81 | { |
| 83 | r.cr(n / 8).modify(|w| { | 82 | r.cr(n / 8).modify(|w| { |
| 84 | w.set_mode(n % 8, speed.to_mode()); | 83 | w.set_mode(n % 8, speed.to_mode()); |
| 85 | w.set_cnf_out(n % 8, vals::CnfOut::PUSHPULL); | 84 | w.set_cnf_out(n % 8, vals::CnfOut::PUSH_PULL); |
| 86 | }); | 85 | }); |
| 87 | } | 86 | } |
| 88 | #[cfg(gpio_v2)] | 87 | #[cfg(gpio_v2)] |
| 89 | { | 88 | { |
| 90 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | 89 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); |
| 91 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL)); | 90 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSH_PULL)); |
| 92 | r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr())); | 91 | r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr())); |
| 93 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); | 92 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); |
| 94 | } | 93 | } |
| @@ -112,7 +111,7 @@ impl<'d> Flex<'d> { | |||
| 112 | let r = self.pin.block(); | 111 | let r = self.pin.block(); |
| 113 | let n = self.pin.pin() as usize; | 112 | let n = self.pin.pin() as usize; |
| 114 | r.cr(n / 8).modify(|w| w.set_mode(n % 8, speed.to_mode())); | 113 | r.cr(n / 8).modify(|w| w.set_mode(n % 8, speed.to_mode())); |
| 115 | r.cr(n / 8).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPENDRAIN)); | 114 | r.cr(n / 8).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPEN_DRAIN)); |
| 116 | }); | 115 | }); |
| 117 | 116 | ||
| 118 | #[cfg(gpio_v2)] | 117 | #[cfg(gpio_v2)] |
| @@ -130,7 +129,7 @@ impl<'d> Flex<'d> { | |||
| 130 | let r = self.pin.block(); | 129 | let r = self.pin.block(); |
| 131 | let n = self.pin.pin() as usize; | 130 | let n = self.pin.pin() as usize; |
| 132 | r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr())); | 131 | r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr())); |
| 133 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPENDRAIN)); | 132 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPEN_DRAIN)); |
| 134 | r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr())); | 133 | r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr())); |
| 135 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); | 134 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); |
| 136 | }); | 135 | }); |
| @@ -253,8 +252,8 @@ impl Pull { | |||
| 253 | const fn to_pupdr(self) -> vals::Pupdr { | 252 | const fn to_pupdr(self) -> vals::Pupdr { |
| 254 | match self { | 253 | match self { |
| 255 | Pull::None => vals::Pupdr::FLOATING, | 254 | Pull::None => vals::Pupdr::FLOATING, |
| 256 | Pull::Up => vals::Pupdr::PULLUP, | 255 | Pull::Up => vals::Pupdr::PULL_UP, |
| 257 | Pull::Down => vals::Pupdr::PULLDOWN, | 256 | Pull::Down => vals::Pupdr::PULL_DOWN, |
| 258 | } | 257 | } |
| 259 | } | 258 | } |
| 260 | } | 259 | } |
| @@ -293,11 +292,11 @@ impl Speed { | |||
| 293 | #[cfg(gpio_v2)] | 292 | #[cfg(gpio_v2)] |
| 294 | const fn to_ospeedr(self: Speed) -> vals::Ospeedr { | 293 | const fn to_ospeedr(self: Speed) -> vals::Ospeedr { |
| 295 | match self { | 294 | match self { |
| 296 | Speed::Low => vals::Ospeedr::LOWSPEED, | 295 | Speed::Low => vals::Ospeedr::LOW_SPEED, |
| 297 | Speed::Medium => vals::Ospeedr::MEDIUMSPEED, | 296 | Speed::Medium => vals::Ospeedr::MEDIUM_SPEED, |
| 298 | #[cfg(not(syscfg_f0))] | 297 | #[cfg(not(syscfg_f0))] |
| 299 | Speed::High => vals::Ospeedr::HIGHSPEED, | 298 | Speed::High => vals::Ospeedr::HIGH_SPEED, |
| 300 | Speed::VeryHigh => vals::Ospeedr::VERYHIGHSPEED, | 299 | Speed::VeryHigh => vals::Ospeedr::VERY_HIGH_SPEED, |
| 301 | } | 300 | } |
| 302 | } | 301 | } |
| 303 | } | 302 | } |
| @@ -310,7 +309,7 @@ pub struct Input<'d> { | |||
| 310 | impl<'d> Input<'d> { | 309 | impl<'d> Input<'d> { |
| 311 | /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration. | 310 | /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration. |
| 312 | #[inline] | 311 | #[inline] |
| 313 | pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, pull: Pull) -> Self { | 312 | pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self { |
| 314 | let mut pin = Flex::new(pin); | 313 | let mut pin = Flex::new(pin); |
| 315 | pin.set_as_input(pull); | 314 | pin.set_as_input(pull); |
| 316 | Self { pin } | 315 | Self { pin } |
| @@ -375,7 +374,7 @@ pub struct Output<'d> { | |||
| 375 | impl<'d> Output<'d> { | 374 | impl<'d> Output<'d> { |
| 376 | /// Create GPIO output driver for a [Pin] with the provided [Level] and [Speed] configuration. | 375 | /// Create GPIO output driver for a [Pin] with the provided [Level] and [Speed] configuration. |
| 377 | #[inline] | 376 | #[inline] |
| 378 | pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, speed: Speed) -> Self { | 377 | pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, speed: Speed) -> Self { |
| 379 | let mut pin = Flex::new(pin); | 378 | let mut pin = Flex::new(pin); |
| 380 | match initial_output { | 379 | match initial_output { |
| 381 | Level::High => pin.set_high(), | 380 | Level::High => pin.set_high(), |
| @@ -440,7 +439,7 @@ pub struct OutputOpenDrain<'d> { | |||
| 440 | impl<'d> OutputOpenDrain<'d> { | 439 | impl<'d> OutputOpenDrain<'d> { |
| 441 | /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed]. | 440 | /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed]. |
| 442 | #[inline] | 441 | #[inline] |
| 443 | pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, speed: Speed) -> Self { | 442 | pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, speed: Speed) -> Self { |
| 444 | let mut pin = Flex::new(pin); | 443 | let mut pin = Flex::new(pin); |
| 445 | match initial_output { | 444 | match initial_output { |
| 446 | Level::High => pin.set_high(), | 445 | Level::High => pin.set_high(), |
| @@ -454,7 +453,7 @@ impl<'d> OutputOpenDrain<'d> { | |||
| 454 | /// and [Pull]. | 453 | /// and [Pull]. |
| 455 | #[inline] | 454 | #[inline] |
| 456 | #[cfg(gpio_v2)] | 455 | #[cfg(gpio_v2)] |
| 457 | pub fn new_pull(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self { | 456 | pub fn new_pull(pin: Peri<'d, impl Pin>, initial_output: Level, speed: Speed, pull: Pull) -> Self { |
| 458 | let mut pin = Flex::new(pin); | 457 | let mut pin = Flex::new(pin); |
| 459 | match initial_output { | 458 | match initial_output { |
| 460 | Level::High => pin.set_high(), | 459 | Level::High => pin.set_high(), |
| @@ -539,16 +538,16 @@ impl OutputType { | |||
| 539 | #[cfg(gpio_v1)] | 538 | #[cfg(gpio_v1)] |
| 540 | const fn to_cnf_out(self) -> vals::CnfOut { | 539 | const fn to_cnf_out(self) -> vals::CnfOut { |
| 541 | match self { | 540 | match self { |
| 542 | OutputType::PushPull => vals::CnfOut::ALTPUSHPULL, | 541 | OutputType::PushPull => vals::CnfOut::ALT_PUSH_PULL, |
| 543 | OutputType::OpenDrain => vals::CnfOut::ALTOPENDRAIN, | 542 | OutputType::OpenDrain => vals::CnfOut::ALT_OPEN_DRAIN, |
| 544 | } | 543 | } |
| 545 | } | 544 | } |
| 546 | 545 | ||
| 547 | #[cfg(gpio_v2)] | 546 | #[cfg(gpio_v2)] |
| 548 | const fn to_ot(self) -> vals::Ot { | 547 | const fn to_ot(self) -> vals::Ot { |
| 549 | match self { | 548 | match self { |
| 550 | OutputType::PushPull => vals::Ot::PUSHPULL, | 549 | OutputType::PushPull => vals::Ot::PUSH_PULL, |
| 551 | OutputType::OpenDrain => vals::Ot::OPENDRAIN, | 550 | OutputType::OpenDrain => vals::Ot::OPEN_DRAIN, |
| 552 | } | 551 | } |
| 553 | } | 552 | } |
| 554 | } | 553 | } |
| @@ -624,8 +623,8 @@ impl AfType { | |||
| 624 | pub const fn input(pull: Pull) -> Self { | 623 | pub const fn input(pull: Pull) -> Self { |
| 625 | Self { | 624 | Self { |
| 626 | pupdr: pull.to_pupdr(), | 625 | pupdr: pull.to_pupdr(), |
| 627 | ot: vals::Ot::PUSHPULL, | 626 | ot: vals::Ot::PUSH_PULL, |
| 628 | ospeedr: vals::Ospeedr::LOWSPEED, | 627 | ospeedr: vals::Ospeedr::LOW_SPEED, |
| 629 | } | 628 | } |
| 630 | } | 629 | } |
| 631 | 630 | ||
| @@ -659,6 +658,16 @@ fn set_as_af(pin_port: u8, af_num: u8, af_type: AfType) { | |||
| 659 | } | 658 | } |
| 660 | 659 | ||
| 661 | #[inline(never)] | 660 | #[inline(never)] |
| 661 | #[cfg(gpio_v2)] | ||
| 662 | fn set_speed(pin_port: u8, speed: Speed) { | ||
| 663 | let pin = unsafe { AnyPin::steal(pin_port) }; | ||
| 664 | let r = pin.block(); | ||
| 665 | let n = pin._pin() as usize; | ||
| 666 | |||
| 667 | r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr())); | ||
| 668 | } | ||
| 669 | |||
| 670 | #[inline(never)] | ||
| 662 | fn set_as_analog(pin_port: u8) { | 671 | fn set_as_analog(pin_port: u8) { |
| 663 | let pin = unsafe { AnyPin::steal(pin_port) }; | 672 | let pin = unsafe { AnyPin::steal(pin_port) }; |
| 664 | let r = pin.block(); | 673 | let r = pin.block(); |
| @@ -695,8 +704,8 @@ fn get_pull(pin_port: u8) -> Pull { | |||
| 695 | #[cfg(gpio_v2)] | 704 | #[cfg(gpio_v2)] |
| 696 | return match r.pupdr().read().pupdr(n) { | 705 | return match r.pupdr().read().pupdr(n) { |
| 697 | vals::Pupdr::FLOATING => Pull::None, | 706 | vals::Pupdr::FLOATING => Pull::None, |
| 698 | vals::Pupdr::PULLDOWN => Pull::Down, | 707 | vals::Pupdr::PULL_DOWN => Pull::Down, |
| 699 | vals::Pupdr::PULLUP => Pull::Up, | 708 | vals::Pupdr::PULL_UP => Pull::Up, |
| 700 | vals::Pupdr::_RESERVED_3 => Pull::None, | 709 | vals::Pupdr::_RESERVED_3 => Pull::None, |
| 701 | }; | 710 | }; |
| 702 | } | 711 | } |
| @@ -716,7 +725,7 @@ pub(crate) trait SealedPin { | |||
| 716 | 725 | ||
| 717 | #[inline] | 726 | #[inline] |
| 718 | fn block(&self) -> gpio::Gpio { | 727 | fn block(&self) -> gpio::Gpio { |
| 719 | pac::GPIO(self._port() as _) | 728 | crate::_generated::gpio_block(self._port() as _) |
| 720 | } | 729 | } |
| 721 | 730 | ||
| 722 | /// Set the output as high. | 731 | /// Set the output as high. |
| @@ -739,6 +748,12 @@ pub(crate) trait SealedPin { | |||
| 739 | } | 748 | } |
| 740 | 749 | ||
| 741 | #[inline] | 750 | #[inline] |
| 751 | #[cfg(gpio_v2)] | ||
| 752 | fn set_speed(&self, speed: Speed) { | ||
| 753 | set_speed(self.pin_port(), speed) | ||
| 754 | } | ||
| 755 | |||
| 756 | #[inline] | ||
| 742 | fn set_as_analog(&self) { | 757 | fn set_as_analog(&self) { |
| 743 | set_as_analog(self.pin_port()); | 758 | set_as_analog(self.pin_port()); |
| 744 | } | 759 | } |
| @@ -764,7 +779,7 @@ pub(crate) trait SealedPin { | |||
| 764 | 779 | ||
| 765 | /// GPIO pin trait. | 780 | /// GPIO pin trait. |
| 766 | #[allow(private_bounds)] | 781 | #[allow(private_bounds)] |
| 767 | pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static { | 782 | pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static { |
| 768 | /// EXTI channel assigned to this pin. | 783 | /// EXTI channel assigned to this pin. |
| 769 | /// | 784 | /// |
| 770 | /// For example, PC4 uses EXTI4. | 785 | /// For example, PC4 uses EXTI4. |
| @@ -782,18 +797,6 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static | |||
| 782 | fn port(&self) -> u8 { | 797 | fn port(&self) -> u8 { |
| 783 | self._port() | 798 | self._port() |
| 784 | } | 799 | } |
| 785 | |||
| 786 | /// Type-erase (degrade) this pin into an `AnyPin`. | ||
| 787 | /// | ||
| 788 | /// This converts pin singletons (`PA5`, `PB6`, ...), which | ||
| 789 | /// are all different types, into the same type. It is useful for | ||
| 790 | /// creating arrays of pins, or avoiding generics. | ||
| 791 | #[inline] | ||
| 792 | fn degrade(self) -> AnyPin { | ||
| 793 | AnyPin { | ||
| 794 | pin_port: self.pin_port(), | ||
| 795 | } | ||
| 796 | } | ||
| 797 | } | 800 | } |
| 798 | 801 | ||
| 799 | /// Type-erased GPIO pin | 802 | /// Type-erased GPIO pin |
| @@ -806,8 +809,8 @@ impl AnyPin { | |||
| 806 | /// | 809 | /// |
| 807 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... | 810 | /// `pin_port` is `port_num * 16 + pin_num`, where `port_num` is 0 for port `A`, 1 for port `B`, etc... |
| 808 | #[inline] | 811 | #[inline] |
| 809 | pub unsafe fn steal(pin_port: u8) -> Self { | 812 | pub unsafe fn steal(pin_port: u8) -> Peri<'static, Self> { |
| 810 | Self { pin_port } | 813 | Peri::new_unchecked(Self { pin_port }) |
| 811 | } | 814 | } |
| 812 | 815 | ||
| 813 | #[inline] | 816 | #[inline] |
| @@ -819,7 +822,7 @@ impl AnyPin { | |||
| 819 | #[cfg(feature = "unstable-pac")] | 822 | #[cfg(feature = "unstable-pac")] |
| 820 | #[inline] | 823 | #[inline] |
| 821 | pub fn block(&self) -> gpio::Gpio { | 824 | pub fn block(&self) -> gpio::Gpio { |
| 822 | pac::GPIO(self._port() as _) | 825 | crate::_generated::gpio_block(self._port() as _) |
| 823 | } | 826 | } |
| 824 | } | 827 | } |
| 825 | 828 | ||
| @@ -851,8 +854,10 @@ foreach_pin!( | |||
| 851 | } | 854 | } |
| 852 | 855 | ||
| 853 | impl From<peripherals::$pin_name> for AnyPin { | 856 | impl From<peripherals::$pin_name> for AnyPin { |
| 854 | fn from(x: peripherals::$pin_name) -> Self { | 857 | fn from(val: peripherals::$pin_name) -> Self { |
| 855 | x.degrade() | 858 | Self { |
| 859 | pin_port: val.pin_port(), | ||
| 860 | } | ||
| 856 | } | 861 | } |
| 857 | } | 862 | } |
| 858 | }; | 863 | }; |
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 4d4a8ec5b..e62151bb5 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs | |||
| @@ -8,16 +8,18 @@ use core::ptr; | |||
| 8 | #[cfg(hash_v2)] | 8 | #[cfg(hash_v2)] |
| 9 | use core::task::Poll; | 9 | use core::task::Poll; |
| 10 | 10 | ||
| 11 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 11 | use embassy_hal_internal::PeripheralType; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | use stm32_metapac::hash::regs::*; | 13 | use stm32_metapac::hash::regs::*; |
| 14 | 14 | ||
| 15 | use crate::dma::NoDma; | ||
| 16 | #[cfg(hash_v2)] | 15 | #[cfg(hash_v2)] |
| 17 | use crate::dma::Transfer; | 16 | use crate::dma::ChannelAndRequest; |
| 18 | use crate::interrupt::typelevel::Interrupt; | 17 | use crate::interrupt::typelevel::Interrupt; |
| 18 | #[cfg(hash_v2)] | ||
| 19 | use crate::mode::Async; | ||
| 20 | use crate::mode::{Blocking, Mode}; | ||
| 19 | use crate::peripherals::HASH; | 21 | use crate::peripherals::HASH; |
| 20 | use crate::{interrupt, pac, peripherals, rcc, Peripheral}; | 22 | use crate::{interrupt, pac, peripherals, rcc, Peri}; |
| 21 | 23 | ||
| 22 | #[cfg(hash_v1)] | 24 | #[cfg(hash_v1)] |
| 23 | const NUM_CONTEXT_REGS: usize = 51; | 25 | const NUM_CONTEXT_REGS: usize = 51; |
| @@ -99,6 +101,7 @@ pub enum DataType { | |||
| 99 | 101 | ||
| 100 | /// Stores the state of the HASH peripheral for suspending/resuming | 102 | /// Stores the state of the HASH peripheral for suspending/resuming |
| 101 | /// digest calculation. | 103 | /// digest calculation. |
| 104 | #[derive(Clone)] | ||
| 102 | pub struct Context<'c> { | 105 | pub struct Context<'c> { |
| 103 | first_word_sent: bool, | 106 | first_word_sent: bool, |
| 104 | key_sent: bool, | 107 | key_sent: bool, |
| @@ -116,24 +119,25 @@ pub struct Context<'c> { | |||
| 116 | type HmacKey<'k> = Option<&'k [u8]>; | 119 | type HmacKey<'k> = Option<&'k [u8]>; |
| 117 | 120 | ||
| 118 | /// HASH driver. | 121 | /// HASH driver. |
| 119 | pub struct Hash<'d, T: Instance, D = NoDma> { | 122 | pub struct Hash<'d, T: Instance, M: Mode> { |
| 120 | _peripheral: PeripheralRef<'d, T>, | 123 | _peripheral: Peri<'d, T>, |
| 121 | #[allow(dead_code)] | 124 | _phantom: PhantomData<M>, |
| 122 | dma: PeripheralRef<'d, D>, | 125 | #[cfg(hash_v2)] |
| 126 | dma: Option<ChannelAndRequest<'d>>, | ||
| 123 | } | 127 | } |
| 124 | 128 | ||
| 125 | impl<'d, T: Instance, D> Hash<'d, T, D> { | 129 | impl<'d, T: Instance> Hash<'d, T, Blocking> { |
| 126 | /// Instantiates, resets, and enables the HASH peripheral. | 130 | /// Instantiates, resets, and enables the HASH peripheral. |
| 127 | pub fn new( | 131 | pub fn new_blocking( |
| 128 | peripheral: impl Peripheral<P = T> + 'd, | 132 | peripheral: Peri<'d, T>, |
| 129 | dma: impl Peripheral<P = D> + 'd, | ||
| 130 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 133 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 131 | ) -> Self { | 134 | ) -> Self { |
| 132 | rcc::enable_and_reset::<HASH>(); | 135 | rcc::enable_and_reset::<HASH>(); |
| 133 | into_ref!(peripheral, dma); | ||
| 134 | let instance = Self { | 136 | let instance = Self { |
| 135 | _peripheral: peripheral, | 137 | _peripheral: peripheral, |
| 136 | dma: dma, | 138 | _phantom: PhantomData, |
| 139 | #[cfg(hash_v2)] | ||
| 140 | dma: None, | ||
| 137 | }; | 141 | }; |
| 138 | 142 | ||
| 139 | T::Interrupt::unpend(); | 143 | T::Interrupt::unpend(); |
| @@ -141,7 +145,9 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { | |||
| 141 | 145 | ||
| 142 | instance | 146 | instance |
| 143 | } | 147 | } |
| 148 | } | ||
| 144 | 149 | ||
| 150 | impl<'d, T: Instance, M: Mode> Hash<'d, T, M> { | ||
| 145 | /// Starts computation of a new hash and returns the saved peripheral state. | 151 | /// Starts computation of a new hash and returns the saved peripheral state. |
| 146 | pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> { | 152 | pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> { |
| 147 | // Define a context for this new computation. | 153 | // Define a context for this new computation. |
| @@ -282,14 +288,135 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { | |||
| 282 | self.store_context(ctx); | 288 | self.store_context(ctx); |
| 283 | } | 289 | } |
| 284 | 290 | ||
| 291 | /// Computes a digest for the given context. | ||
| 292 | /// The digest buffer must be large enough to accomodate a digest for the selected algorithm. | ||
| 293 | /// The largest returned digest size is 128 bytes for SHA-512. | ||
| 294 | /// Panics if the supplied digest buffer is too short. | ||
| 295 | pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize { | ||
| 296 | // Restore the peripheral state. | ||
| 297 | self.load_context(&ctx); | ||
| 298 | |||
| 299 | // Hash the leftover bytes, if any. | ||
| 300 | self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]); | ||
| 301 | ctx.buflen = 0; | ||
| 302 | |||
| 303 | //Start the digest calculation. | ||
| 304 | T::regs().str().write(|w| w.set_dcal(true)); | ||
| 305 | |||
| 306 | // Load the HMAC key if provided. | ||
| 307 | if let Some(key) = ctx.key { | ||
| 308 | while !T::regs().sr().read().dinis() {} | ||
| 309 | self.accumulate_blocking(key); | ||
| 310 | T::regs().str().write(|w| w.set_dcal(true)); | ||
| 311 | } | ||
| 312 | |||
| 313 | // Block until digest computation is complete. | ||
| 314 | while !T::regs().sr().read().dcis() {} | ||
| 315 | |||
| 316 | // Return the digest. | ||
| 317 | let digest_words = match ctx.algo { | ||
| 318 | Algorithm::SHA1 => 5, | ||
| 319 | #[cfg(any(hash_v1, hash_v2, hash_v4))] | ||
| 320 | Algorithm::MD5 => 4, | ||
| 321 | Algorithm::SHA224 => 7, | ||
| 322 | Algorithm::SHA256 => 8, | ||
| 323 | #[cfg(hash_v3)] | ||
| 324 | Algorithm::SHA384 => 12, | ||
| 325 | #[cfg(hash_v3)] | ||
| 326 | Algorithm::SHA512_224 => 7, | ||
| 327 | #[cfg(hash_v3)] | ||
| 328 | Algorithm::SHA512_256 => 8, | ||
| 329 | #[cfg(hash_v3)] | ||
| 330 | Algorithm::SHA512 => 16, | ||
| 331 | }; | ||
| 332 | |||
| 333 | let digest_len_bytes = digest_words * 4; | ||
| 334 | // Panics if the supplied digest buffer is too short. | ||
| 335 | if digest.len() < digest_len_bytes { | ||
| 336 | panic!("Digest buffer must be at least {} bytes long.", digest_words * 4); | ||
| 337 | } | ||
| 338 | |||
| 339 | let mut i = 0; | ||
| 340 | while i < digest_words { | ||
| 341 | let word = T::regs().hr(i).read(); | ||
| 342 | digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||
| 343 | i += 1; | ||
| 344 | } | ||
| 345 | digest_len_bytes | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Push data into the hash core. | ||
| 349 | fn accumulate_blocking(&mut self, input: &[u8]) { | ||
| 350 | // Set the number of valid bits. | ||
| 351 | let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||
| 352 | T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | ||
| 353 | |||
| 354 | let mut i = 0; | ||
| 355 | while i < input.len() { | ||
| 356 | let mut word: [u8; 4] = [0; 4]; | ||
| 357 | let copy_idx = min(i + 4, input.len()); | ||
| 358 | word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); | ||
| 359 | T::regs().din().write_value(u32::from_ne_bytes(word)); | ||
| 360 | i += 4; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 | /// Save the peripheral state to a context. | ||
| 365 | fn store_context<'c>(&mut self, ctx: &mut Context<'c>) { | ||
| 366 | // Block waiting for data in ready. | ||
| 367 | while !T::regs().sr().read().dinis() {} | ||
| 368 | |||
| 369 | // Store peripheral context. | ||
| 370 | ctx.imr = T::regs().imr().read().0; | ||
| 371 | ctx.str = T::regs().str().read().0; | ||
| 372 | ctx.cr = T::regs().cr().read().0; | ||
| 373 | let mut i = 0; | ||
| 374 | while i < NUM_CONTEXT_REGS { | ||
| 375 | ctx.csr[i] = T::regs().csr(i).read(); | ||
| 376 | i += 1; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | /// Restore the peripheral state from a context. | ||
| 381 | fn load_context(&mut self, ctx: &Context) { | ||
| 382 | // Restore the peripheral state from the context. | ||
| 383 | T::regs().imr().write_value(Imr { 0: ctx.imr }); | ||
| 384 | T::regs().str().write_value(Str { 0: ctx.str }); | ||
| 385 | T::regs().cr().write_value(Cr { 0: ctx.cr }); | ||
| 386 | T::regs().cr().modify(|w| w.set_init(true)); | ||
| 387 | let mut i = 0; | ||
| 388 | while i < NUM_CONTEXT_REGS { | ||
| 389 | T::regs().csr(i).write_value(ctx.csr[i]); | ||
| 390 | i += 1; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | #[cfg(hash_v2)] | ||
| 396 | impl<'d, T: Instance> Hash<'d, T, Async> { | ||
| 397 | /// Instantiates, resets, and enables the HASH peripheral. | ||
| 398 | pub fn new( | ||
| 399 | peripheral: Peri<'d, T>, | ||
| 400 | dma: Peri<'d, impl Dma<T>>, | ||
| 401 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 402 | ) -> Self { | ||
| 403 | rcc::enable_and_reset::<HASH>(); | ||
| 404 | let instance = Self { | ||
| 405 | _peripheral: peripheral, | ||
| 406 | _phantom: PhantomData, | ||
| 407 | dma: new_dma!(dma), | ||
| 408 | }; | ||
| 409 | |||
| 410 | T::Interrupt::unpend(); | ||
| 411 | unsafe { T::Interrupt::enable() }; | ||
| 412 | |||
| 413 | instance | ||
| 414 | } | ||
| 415 | |||
| 285 | /// Restores the peripheral state using the given context, | 416 | /// Restores the peripheral state using the given context, |
| 286 | /// then updates the state with the provided data. | 417 | /// then updates the state with the provided data. |
| 287 | /// Peripheral state is saved upon return. | 418 | /// Peripheral state is saved upon return. |
| 288 | #[cfg(hash_v2)] | 419 | pub async fn update(&mut self, ctx: &mut Context<'_>, input: &[u8]) { |
| 289 | pub async fn update<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) | ||
| 290 | where | ||
| 291 | D: crate::hash::Dma<T>, | ||
| 292 | { | ||
| 293 | // Restore the peripheral state. | 420 | // Restore the peripheral state. |
| 294 | self.load_context(&ctx); | 421 | self.load_context(&ctx); |
| 295 | 422 | ||
| @@ -353,68 +480,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { | |||
| 353 | /// The digest buffer must be large enough to accomodate a digest for the selected algorithm. | 480 | /// The digest buffer must be large enough to accomodate a digest for the selected algorithm. |
| 354 | /// The largest returned digest size is 128 bytes for SHA-512. | 481 | /// The largest returned digest size is 128 bytes for SHA-512. |
| 355 | /// Panics if the supplied digest buffer is too short. | 482 | /// Panics if the supplied digest buffer is too short. |
| 356 | pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize { | 483 | pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize { |
| 357 | // Restore the peripheral state. | ||
| 358 | self.load_context(&ctx); | ||
| 359 | |||
| 360 | // Hash the leftover bytes, if any. | ||
| 361 | self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]); | ||
| 362 | ctx.buflen = 0; | ||
| 363 | |||
| 364 | //Start the digest calculation. | ||
| 365 | T::regs().str().write(|w| w.set_dcal(true)); | ||
| 366 | |||
| 367 | // Load the HMAC key if provided. | ||
| 368 | if let Some(key) = ctx.key { | ||
| 369 | while !T::regs().sr().read().dinis() {} | ||
| 370 | self.accumulate_blocking(key); | ||
| 371 | T::regs().str().write(|w| w.set_dcal(true)); | ||
| 372 | } | ||
| 373 | |||
| 374 | // Block until digest computation is complete. | ||
| 375 | while !T::regs().sr().read().dcis() {} | ||
| 376 | |||
| 377 | // Return the digest. | ||
| 378 | let digest_words = match ctx.algo { | ||
| 379 | Algorithm::SHA1 => 5, | ||
| 380 | #[cfg(any(hash_v1, hash_v2, hash_v4))] | ||
| 381 | Algorithm::MD5 => 4, | ||
| 382 | Algorithm::SHA224 => 7, | ||
| 383 | Algorithm::SHA256 => 8, | ||
| 384 | #[cfg(hash_v3)] | ||
| 385 | Algorithm::SHA384 => 12, | ||
| 386 | #[cfg(hash_v3)] | ||
| 387 | Algorithm::SHA512_224 => 7, | ||
| 388 | #[cfg(hash_v3)] | ||
| 389 | Algorithm::SHA512_256 => 8, | ||
| 390 | #[cfg(hash_v3)] | ||
| 391 | Algorithm::SHA512 => 16, | ||
| 392 | }; | ||
| 393 | |||
| 394 | let digest_len_bytes = digest_words * 4; | ||
| 395 | // Panics if the supplied digest buffer is too short. | ||
| 396 | if digest.len() < digest_len_bytes { | ||
| 397 | panic!("Digest buffer must be at least {} bytes long.", digest_words * 4); | ||
| 398 | } | ||
| 399 | |||
| 400 | let mut i = 0; | ||
| 401 | while i < digest_words { | ||
| 402 | let word = T::regs().hr(i).read(); | ||
| 403 | digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); | ||
| 404 | i += 1; | ||
| 405 | } | ||
| 406 | digest_len_bytes | ||
| 407 | } | ||
| 408 | |||
| 409 | /// Computes a digest for the given context. | ||
| 410 | /// The digest buffer must be large enough to accomodate a digest for the selected algorithm. | ||
| 411 | /// The largest returned digest size is 128 bytes for SHA-512. | ||
| 412 | /// Panics if the supplied digest buffer is too short. | ||
| 413 | #[cfg(hash_v2)] | ||
| 414 | pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize | ||
| 415 | where | ||
| 416 | D: crate::hash::Dma<T>, | ||
| 417 | { | ||
| 418 | // Restore the peripheral state. | 484 | // Restore the peripheral state. |
| 419 | self.load_context(&ctx); | 485 | self.load_context(&ctx); |
| 420 | 486 | ||
| @@ -483,27 +549,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { | |||
| 483 | } | 549 | } |
| 484 | 550 | ||
| 485 | /// Push data into the hash core. | 551 | /// Push data into the hash core. |
| 486 | fn accumulate_blocking(&mut self, input: &[u8]) { | 552 | async fn accumulate(&mut self, input: &[u8]) { |
| 487 | // Set the number of valid bits. | ||
| 488 | let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; | ||
| 489 | T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | ||
| 490 | |||
| 491 | let mut i = 0; | ||
| 492 | while i < input.len() { | ||
| 493 | let mut word: [u8; 4] = [0; 4]; | ||
| 494 | let copy_idx = min(i + 4, input.len()); | ||
| 495 | word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); | ||
| 496 | T::regs().din().write_value(u32::from_ne_bytes(word)); | ||
| 497 | i += 4; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | |||
| 501 | /// Push data into the hash core. | ||
| 502 | #[cfg(hash_v2)] | ||
| 503 | async fn accumulate(&mut self, input: &[u8]) | ||
| 504 | where | ||
| 505 | D: crate::hash::Dma<T>, | ||
| 506 | { | ||
| 507 | // Ignore an input length of 0. | 553 | // Ignore an input length of 0. |
| 508 | if input.len() == 0 { | 554 | if input.len() == 0 { |
| 509 | return; | 555 | return; |
| @@ -514,50 +560,20 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { | |||
| 514 | T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); | 560 | T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); |
| 515 | 561 | ||
| 516 | // Configure DMA to transfer input to hash core. | 562 | // Configure DMA to transfer input to hash core. |
| 517 | let dma_request = self.dma.request(); | 563 | let dst_ptr: *mut u32 = T::regs().din().as_ptr(); |
| 518 | let dst_ptr = T::regs().din().as_ptr(); | ||
| 519 | let mut num_words = input.len() / 4; | 564 | let mut num_words = input.len() / 4; |
| 520 | if input.len() % 4 > 0 { | 565 | if input.len() % 4 > 0 { |
| 521 | num_words += 1; | 566 | num_words += 1; |
| 522 | } | 567 | } |
| 523 | let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); | 568 | let src_ptr: *const [u8] = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); |
| 524 | let dma_transfer = | 569 | |
| 525 | unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; | 570 | let dma = self.dma.as_mut().unwrap(); |
| 571 | let dma_transfer = unsafe { dma.write_raw(src_ptr, dst_ptr as *mut u32, Default::default()) }; | ||
| 526 | T::regs().cr().modify(|w| w.set_dmae(true)); | 572 | T::regs().cr().modify(|w| w.set_dmae(true)); |
| 527 | 573 | ||
| 528 | // Wait for the transfer to complete. | 574 | // Wait for the transfer to complete. |
| 529 | dma_transfer.await; | 575 | dma_transfer.await; |
| 530 | } | 576 | } |
| 531 | |||
| 532 | /// Save the peripheral state to a context. | ||
| 533 | fn store_context<'c>(&mut self, ctx: &mut Context<'c>) { | ||
| 534 | // Block waiting for data in ready. | ||
| 535 | while !T::regs().sr().read().dinis() {} | ||
| 536 | |||
| 537 | // Store peripheral context. | ||
| 538 | ctx.imr = T::regs().imr().read().0; | ||
| 539 | ctx.str = T::regs().str().read().0; | ||
| 540 | ctx.cr = T::regs().cr().read().0; | ||
| 541 | let mut i = 0; | ||
| 542 | while i < NUM_CONTEXT_REGS { | ||
| 543 | ctx.csr[i] = T::regs().csr(i).read(); | ||
| 544 | i += 1; | ||
| 545 | } | ||
| 546 | } | ||
| 547 | |||
| 548 | /// Restore the peripheral state from a context. | ||
| 549 | fn load_context(&mut self, ctx: &Context) { | ||
| 550 | // Restore the peripheral state from the context. | ||
| 551 | T::regs().imr().write_value(Imr { 0: ctx.imr }); | ||
| 552 | T::regs().str().write_value(Str { 0: ctx.str }); | ||
| 553 | T::regs().cr().write_value(Cr { 0: ctx.cr }); | ||
| 554 | T::regs().cr().modify(|w| w.set_init(true)); | ||
| 555 | let mut i = 0; | ||
| 556 | while i < NUM_CONTEXT_REGS { | ||
| 557 | T::regs().csr(i).write_value(ctx.csr[i]); | ||
| 558 | i += 1; | ||
| 559 | } | ||
| 560 | } | ||
| 561 | } | 577 | } |
| 562 | 578 | ||
| 563 | trait SealedInstance { | 579 | trait SealedInstance { |
| @@ -566,7 +582,7 @@ trait SealedInstance { | |||
| 566 | 582 | ||
| 567 | /// HASH instance trait. | 583 | /// HASH instance trait. |
| 568 | #[allow(private_bounds)] | 584 | #[allow(private_bounds)] |
| 569 | pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | 585 | pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send { |
| 570 | /// Interrupt for this HASH instance. | 586 | /// Interrupt for this HASH instance. |
| 571 | type Interrupt: interrupt::typelevel::Interrupt; | 587 | type Interrupt: interrupt::typelevel::Interrupt; |
| 572 | } | 588 | } |
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 13343fc2a..1d0594125 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs | |||
| @@ -4,12 +4,12 @@ mod traits; | |||
| 4 | 4 | ||
| 5 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 6 | 6 | ||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 7 | use embassy_hal_internal::Peri; |
| 8 | pub use traits::Instance; | 8 | pub use traits::Instance; |
| 9 | 9 | ||
| 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 11 | use crate::rcc; | ||
| 11 | use crate::time::Hertz; | 12 | use crate::time::Hertz; |
| 12 | use crate::{rcc, Peripheral}; | ||
| 13 | 13 | ||
| 14 | /// HRTIM burst controller instance. | 14 | /// HRTIM burst controller instance. |
| 15 | pub struct BurstController<T: Instance> { | 15 | pub struct BurstController<T: Instance> { |
| @@ -62,13 +62,13 @@ pub trait AdvancedChannel<T: Instance>: SealedAdvancedChannel<T> {} | |||
| 62 | 62 | ||
| 63 | /// HRTIM PWM pin. | 63 | /// HRTIM PWM pin. |
| 64 | pub struct PwmPin<'d, T, C> { | 64 | pub struct PwmPin<'d, T, C> { |
| 65 | _pin: PeripheralRef<'d, AnyPin>, | 65 | _pin: Peri<'d, AnyPin>, |
| 66 | phantom: PhantomData<(T, C)>, | 66 | phantom: PhantomData<(T, C)>, |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | /// HRTIM complementary PWM pin. | 69 | /// HRTIM complementary PWM pin. |
| 70 | pub struct ComplementaryPwmPin<'d, T, C> { | 70 | pub struct ComplementaryPwmPin<'d, T, C> { |
| 71 | _pin: PeripheralRef<'d, AnyPin>, | 71 | _pin: Peri<'d, AnyPin>, |
| 72 | phantom: PhantomData<(T, C)>, | 72 | phantom: PhantomData<(T, C)>, |
| 73 | } | 73 | } |
| 74 | 74 | ||
| @@ -76,8 +76,7 @@ macro_rules! advanced_channel_impl { | |||
| 76 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { | 76 | ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { |
| 77 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { | 77 | impl<'d, T: Instance> PwmPin<'d, T, $channel<T>> { |
| 78 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 78 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 79 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self { | 79 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| 80 | into_ref!(pin); | ||
| 81 | critical_section::with(|_| { | 80 | critical_section::with(|_| { |
| 82 | pin.set_low(); | 81 | pin.set_low(); |
| 83 | pin.set_as_af( | 82 | pin.set_as_af( |
| @@ -86,7 +85,7 @@ macro_rules! advanced_channel_impl { | |||
| 86 | ); | 85 | ); |
| 87 | }); | 86 | }); |
| 88 | PwmPin { | 87 | PwmPin { |
| 89 | _pin: pin.map_into(), | 88 | _pin: pin.into(), |
| 90 | phantom: PhantomData, | 89 | phantom: PhantomData, |
| 91 | } | 90 | } |
| 92 | } | 91 | } |
| @@ -94,8 +93,7 @@ macro_rules! advanced_channel_impl { | |||
| 94 | 93 | ||
| 95 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { | 94 | impl<'d, T: Instance> ComplementaryPwmPin<'d, T, $channel<T>> { |
| 96 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] | 95 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] |
| 97 | pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<T>> + 'd) -> Self { | 96 | pub fn $new_chx(pin: Peri<'d, impl $complementary_pin_trait<T>>) -> Self { |
| 98 | into_ref!(pin); | ||
| 99 | critical_section::with(|_| { | 97 | critical_section::with(|_| { |
| 100 | pin.set_low(); | 98 | pin.set_low(); |
| 101 | pin.set_as_af( | 99 | pin.set_as_af( |
| @@ -104,7 +102,7 @@ macro_rules! advanced_channel_impl { | |||
| 104 | ); | 102 | ); |
| 105 | }); | 103 | }); |
| 106 | ComplementaryPwmPin { | 104 | ComplementaryPwmPin { |
| 107 | _pin: pin.map_into(), | 105 | _pin: pin.into(), |
| 108 | phantom: PhantomData, | 106 | phantom: PhantomData, |
| 109 | } | 107 | } |
| 110 | } | 108 | } |
| @@ -129,7 +127,7 @@ advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin); | |||
| 129 | 127 | ||
| 130 | /// Struct used to divide a high resolution timer into multiple channels | 128 | /// Struct used to divide a high resolution timer into multiple channels |
| 131 | pub struct AdvancedPwm<'d, T: Instance> { | 129 | pub struct AdvancedPwm<'d, T: Instance> { |
| 132 | _inner: PeripheralRef<'d, T>, | 130 | _inner: Peri<'d, T>, |
| 133 | /// Master instance. | 131 | /// Master instance. |
| 134 | pub master: Master<T>, | 132 | pub master: Master<T>, |
| 135 | /// Burst controller. | 133 | /// Burst controller. |
| @@ -154,7 +152,7 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { | |||
| 154 | /// | 152 | /// |
| 155 | /// This splits the HRTIM into its constituent parts, which you can then use individually. | 153 | /// This splits the HRTIM into its constituent parts, which you can then use individually. |
| 156 | pub fn new( | 154 | pub fn new( |
| 157 | tim: impl Peripheral<P = T> + 'd, | 155 | tim: Peri<'d, T>, |
| 158 | _cha: Option<PwmPin<'d, T, ChA<T>>>, | 156 | _cha: Option<PwmPin<'d, T, ChA<T>>>, |
| 159 | _chan: Option<ComplementaryPwmPin<'d, T, ChA<T>>>, | 157 | _chan: Option<ComplementaryPwmPin<'d, T, ChA<T>>>, |
| 160 | _chb: Option<PwmPin<'d, T, ChB<T>>>, | 158 | _chb: Option<PwmPin<'d, T, ChB<T>>>, |
| @@ -171,9 +169,7 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { | |||
| 171 | Self::new_inner(tim) | 169 | Self::new_inner(tim) |
| 172 | } | 170 | } |
| 173 | 171 | ||
| 174 | fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self { | 172 | fn new_inner(tim: Peri<'d, T>) -> Self { |
| 175 | into_ref!(tim); | ||
| 176 | |||
| 177 | rcc::enable_and_reset::<T>(); | 173 | rcc::enable_and_reset::<T>(); |
| 178 | 174 | ||
| 179 | #[cfg(stm32f334)] | 175 | #[cfg(stm32f334)] |
| @@ -236,8 +232,6 @@ pub struct BridgeConverter<T: Instance, C: AdvancedChannel<T>> { | |||
| 236 | impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> { | 232 | impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> { |
| 237 | /// Create a new HRTIM bridge converter driver. | 233 | /// Create a new HRTIM bridge converter driver. |
| 238 | pub fn new(_channel: C, frequency: Hertz) -> Self { | 234 | pub fn new(_channel: C, frequency: Hertz) -> Self { |
| 239 | use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; | ||
| 240 | |||
| 241 | T::set_channel_frequency(C::raw(), frequency); | 235 | T::set_channel_frequency(C::raw(), frequency); |
| 242 | 236 | ||
| 243 | // Always enable preload | 237 | // Always enable preload |
| @@ -258,28 +252,16 @@ impl<T: Instance, C: AdvancedChannel<T>> BridgeConverter<T, C> { | |||
| 258 | // Therefore, software-implemented dead time must be used when setting the duty cycles | 252 | // Therefore, software-implemented dead time must be used when setting the duty cycles |
| 259 | 253 | ||
| 260 | // Set output 1 to active on a period event | 254 | // Set output 1 to active on a period event |
| 261 | T::regs() | 255 | T::regs().tim(C::raw()).setr(0).modify(|w| w.set_per(true)); |
| 262 | .tim(C::raw()) | ||
| 263 | .setr(0) | ||
| 264 | .modify(|w| w.set_per(Activeeffect::SETACTIVE)); | ||
| 265 | 256 | ||
| 266 | // Set output 1 to inactive on a compare 1 event | 257 | // Set output 1 to inactive on a compare 1 event |
| 267 | T::regs() | 258 | T::regs().tim(C::raw()).rstr(0).modify(|w| w.set_cmp(0, true)); |
| 268 | .tim(C::raw()) | ||
| 269 | .rstr(0) | ||
| 270 | .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); | ||
| 271 | 259 | ||
| 272 | // Set output 2 to active on a compare 2 event | 260 | // Set output 2 to active on a compare 2 event |
| 273 | T::regs() | 261 | T::regs().tim(C::raw()).setr(1).modify(|w| w.set_cmp(1, true)); |
| 274 | .tim(C::raw()) | ||
| 275 | .setr(1) | ||
| 276 | .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE)); | ||
| 277 | 262 | ||
| 278 | // Set output 2 to inactive on a compare 3 event | 263 | // Set output 2 to inactive on a compare 3 event |
| 279 | T::regs() | 264 | T::regs().tim(C::raw()).rstr(1).modify(|w| w.set_cmp(2, true)); |
| 280 | .tim(C::raw()) | ||
| 281 | .rstr(1) | ||
| 282 | .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE)); | ||
| 283 | 265 | ||
| 284 | Self { | 266 | Self { |
| 285 | timer: PhantomData, | 267 | timer: PhantomData, |
diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index 75f9971e2..6c0661146 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | use embassy_hal_internal::PeripheralType; | ||
| 2 | |||
| 1 | use crate::rcc::RccPeripheral; | 3 | use crate::rcc::RccPeripheral; |
| 2 | use crate::time::Hertz; | 4 | use crate::time::Hertz; |
| 3 | 5 | ||
| @@ -153,7 +155,7 @@ pub(crate) trait SealedInstance: RccPeripheral { | |||
| 153 | 155 | ||
| 154 | /// HRTIM instance trait. | 156 | /// HRTIM instance trait. |
| 155 | #[allow(private_bounds)] | 157 | #[allow(private_bounds)] |
| 156 | pub trait Instance: SealedInstance + 'static {} | 158 | pub trait Instance: SealedInstance + PeripheralType + 'static {} |
| 157 | 159 | ||
| 158 | foreach_interrupt! { | 160 | foreach_interrupt! { |
| 159 | ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { | 161 | ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { |
diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 06ab7a9bc..31527bcdb 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs | |||
| @@ -1,13 +1,14 @@ | |||
| 1 | //! Hardware Semaphore (HSEM) | 1 | //! Hardware Semaphore (HSEM) |
| 2 | 2 | ||
| 3 | use embassy_hal_internal::PeripheralType; | ||
| 4 | |||
| 5 | use crate::pac; | ||
| 6 | use crate::rcc::RccPeripheral; | ||
| 3 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. | 7 | // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. |
| 4 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, | 8 | // Those MCUs have a different HSEM implementation (Secure semaphore lock support, |
| 5 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), | 9 | // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), |
| 6 | // which is not yet supported by this code. | 10 | // which is not yet supported by this code. |
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 11 | use crate::Peri; |
| 8 | |||
| 9 | use crate::rcc::RccPeripheral; | ||
| 10 | use crate::{pac, Peripheral}; | ||
| 11 | 12 | ||
| 12 | /// HSEM error. | 13 | /// HSEM error. |
| 13 | #[derive(Debug)] | 14 | #[derive(Debug)] |
| @@ -73,13 +74,12 @@ fn core_id_to_index(core: CoreId) -> usize { | |||
| 73 | 74 | ||
| 74 | /// HSEM driver | 75 | /// HSEM driver |
| 75 | pub struct HardwareSemaphore<'d, T: Instance> { | 76 | pub struct HardwareSemaphore<'d, T: Instance> { |
| 76 | _peri: PeripheralRef<'d, T>, | 77 | _peri: Peri<'d, T>, |
| 77 | } | 78 | } |
| 78 | 79 | ||
| 79 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { | 80 | impl<'d, T: Instance> HardwareSemaphore<'d, T> { |
| 80 | /// Creates a new HardwareSemaphore instance. | 81 | /// Creates a new HardwareSemaphore instance. |
| 81 | pub fn new(peripheral: impl Peripheral<P = T> + 'd) -> Self { | 82 | pub fn new(peripheral: Peri<'d, T>) -> Self { |
| 82 | into_ref!(peripheral); | ||
| 83 | HardwareSemaphore { _peri: peripheral } | 83 | HardwareSemaphore { _peri: peripheral } |
| 84 | } | 84 | } |
| 85 | 85 | ||
| @@ -177,7 +177,7 @@ trait SealedInstance { | |||
| 177 | 177 | ||
| 178 | /// HSEM instance trait. | 178 | /// HSEM instance trait. |
| 179 | #[allow(private_bounds)] | 179 | #[allow(private_bounds)] |
| 180 | pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {} | 180 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} |
| 181 | 181 | ||
| 182 | impl SealedInstance for crate::peripherals::HSEM { | 182 | impl SealedInstance for crate::peripherals::HSEM { |
| 183 | fn regs() -> crate::pac::hsem::Hsem { | 183 | fn regs() -> crate::pac::hsem::Hsem { |
diff --git a/embassy-stm32/src/hspi/enums.rs b/embassy-stm32/src/hspi/enums.rs new file mode 100644 index 000000000..83a88f4d9 --- /dev/null +++ b/embassy-stm32/src/hspi/enums.rs | |||
| @@ -0,0 +1,422 @@ | |||
| 1 | //! Enums used in Hspi configuration. | ||
| 2 | |||
| 3 | #[allow(dead_code)] | ||
| 4 | #[derive(Copy, Clone)] | ||
| 5 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | pub(crate) enum HspiMode { | ||
| 7 | IndirectWrite, | ||
| 8 | IndirectRead, | ||
| 9 | AutoPolling, | ||
| 10 | MemoryMapped, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl Into<u8> for HspiMode { | ||
| 14 | fn into(self) -> u8 { | ||
| 15 | match self { | ||
| 16 | HspiMode::IndirectWrite => 0b00, | ||
| 17 | HspiMode::IndirectRead => 0b01, | ||
| 18 | HspiMode::AutoPolling => 0b10, | ||
| 19 | HspiMode::MemoryMapped => 0b11, | ||
| 20 | } | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | /// Hspi lane width | ||
| 25 | #[allow(dead_code)] | ||
| 26 | #[derive(Copy, Clone)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub enum HspiWidth { | ||
| 29 | /// None | ||
| 30 | NONE, | ||
| 31 | /// Single lane | ||
| 32 | SING, | ||
| 33 | /// Dual lanes | ||
| 34 | DUAL, | ||
| 35 | /// Quad lanes | ||
| 36 | QUAD, | ||
| 37 | /// Eight lanes | ||
| 38 | OCTO, | ||
| 39 | /// Sixteen lanes | ||
| 40 | HEXADECA, | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Into<u8> for HspiWidth { | ||
| 44 | fn into(self) -> u8 { | ||
| 45 | match self { | ||
| 46 | HspiWidth::NONE => 0b00, | ||
| 47 | HspiWidth::SING => 0b01, | ||
| 48 | HspiWidth::DUAL => 0b10, | ||
| 49 | HspiWidth::QUAD => 0b11, | ||
| 50 | HspiWidth::OCTO => 0b100, | ||
| 51 | HspiWidth::HEXADECA => 0b101, | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Flash bank selection | ||
| 57 | #[allow(dead_code)] | ||
| 58 | #[derive(Copy, Clone)] | ||
| 59 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 60 | pub enum FlashSelection { | ||
| 61 | /// Bank 1 | ||
| 62 | Flash1, | ||
| 63 | /// Bank 2 | ||
| 64 | Flash2, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Into<bool> for FlashSelection { | ||
| 68 | fn into(self) -> bool { | ||
| 69 | match self { | ||
| 70 | FlashSelection::Flash1 => false, | ||
| 71 | FlashSelection::Flash2 => true, | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Wrap Size | ||
| 77 | #[allow(dead_code)] | ||
| 78 | #[allow(missing_docs)] | ||
| 79 | #[derive(Copy, Clone)] | ||
| 80 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 81 | pub enum WrapSize { | ||
| 82 | None, | ||
| 83 | _16Bytes, | ||
| 84 | _32Bytes, | ||
| 85 | _64Bytes, | ||
| 86 | _128Bytes, | ||
| 87 | } | ||
| 88 | |||
| 89 | impl Into<u8> for WrapSize { | ||
| 90 | fn into(self) -> u8 { | ||
| 91 | match self { | ||
| 92 | WrapSize::None => 0x00, | ||
| 93 | WrapSize::_16Bytes => 0x02, | ||
| 94 | WrapSize::_32Bytes => 0x03, | ||
| 95 | WrapSize::_64Bytes => 0x04, | ||
| 96 | WrapSize::_128Bytes => 0x05, | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Memory Type | ||
| 102 | #[allow(missing_docs)] | ||
| 103 | #[allow(dead_code)] | ||
| 104 | #[derive(Copy, Clone)] | ||
| 105 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 106 | pub enum MemoryType { | ||
| 107 | Micron, | ||
| 108 | Macronix, | ||
| 109 | Standard, | ||
| 110 | MacronixRam, | ||
| 111 | HyperBusMemory, | ||
| 112 | HyperBusRegister, | ||
| 113 | } | ||
| 114 | |||
| 115 | impl Into<u8> for MemoryType { | ||
| 116 | fn into(self) -> u8 { | ||
| 117 | match self { | ||
| 118 | MemoryType::Micron => 0x00, | ||
| 119 | MemoryType::Macronix => 0x01, | ||
| 120 | MemoryType::Standard => 0x02, | ||
| 121 | MemoryType::MacronixRam => 0x03, | ||
| 122 | MemoryType::HyperBusMemory => 0x04, | ||
| 123 | MemoryType::HyperBusRegister => 0x04, | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Hspi memory size. | ||
| 129 | #[allow(missing_docs)] | ||
| 130 | #[derive(Copy, Clone)] | ||
| 131 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 132 | pub enum MemorySize { | ||
| 133 | _1KiB, | ||
| 134 | _2KiB, | ||
| 135 | _4KiB, | ||
| 136 | _8KiB, | ||
| 137 | _16KiB, | ||
| 138 | _32KiB, | ||
| 139 | _64KiB, | ||
| 140 | _128KiB, | ||
| 141 | _256KiB, | ||
| 142 | _512KiB, | ||
| 143 | _1MiB, | ||
| 144 | _2MiB, | ||
| 145 | _4MiB, | ||
| 146 | _8MiB, | ||
| 147 | _16MiB, | ||
| 148 | _32MiB, | ||
| 149 | _64MiB, | ||
| 150 | _128MiB, | ||
| 151 | _256MiB, | ||
| 152 | _512MiB, | ||
| 153 | _1GiB, | ||
| 154 | _2GiB, | ||
| 155 | _4GiB, | ||
| 156 | Other(u8), | ||
| 157 | } | ||
| 158 | |||
| 159 | impl Into<u8> for MemorySize { | ||
| 160 | fn into(self) -> u8 { | ||
| 161 | match self { | ||
| 162 | MemorySize::_1KiB => 6, | ||
| 163 | MemorySize::_2KiB => 7, | ||
| 164 | MemorySize::_4KiB => 8, | ||
| 165 | MemorySize::_8KiB => 9, | ||
| 166 | MemorySize::_16KiB => 10, | ||
| 167 | MemorySize::_32KiB => 11, | ||
| 168 | MemorySize::_64KiB => 12, | ||
| 169 | MemorySize::_128KiB => 13, | ||
| 170 | MemorySize::_256KiB => 14, | ||
| 171 | MemorySize::_512KiB => 15, | ||
| 172 | MemorySize::_1MiB => 16, | ||
| 173 | MemorySize::_2MiB => 17, | ||
| 174 | MemorySize::_4MiB => 18, | ||
| 175 | MemorySize::_8MiB => 19, | ||
| 176 | MemorySize::_16MiB => 20, | ||
| 177 | MemorySize::_32MiB => 21, | ||
| 178 | MemorySize::_64MiB => 22, | ||
| 179 | MemorySize::_128MiB => 23, | ||
| 180 | MemorySize::_256MiB => 24, | ||
| 181 | MemorySize::_512MiB => 25, | ||
| 182 | MemorySize::_1GiB => 26, | ||
| 183 | MemorySize::_2GiB => 27, | ||
| 184 | MemorySize::_4GiB => 28, | ||
| 185 | MemorySize::Other(val) => val, | ||
| 186 | } | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Hspi Address size | ||
| 191 | #[derive(Copy, Clone)] | ||
| 192 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 193 | pub enum AddressSize { | ||
| 194 | /// 8-bit address | ||
| 195 | _8Bit, | ||
| 196 | /// 16-bit address | ||
| 197 | _16Bit, | ||
| 198 | /// 24-bit address | ||
| 199 | _24Bit, | ||
| 200 | /// 32-bit address | ||
| 201 | _32Bit, | ||
| 202 | } | ||
| 203 | |||
| 204 | impl Into<u8> for AddressSize { | ||
| 205 | fn into(self) -> u8 { | ||
| 206 | match self { | ||
| 207 | AddressSize::_8Bit => 0b00, | ||
| 208 | AddressSize::_16Bit => 0b01, | ||
| 209 | AddressSize::_24Bit => 0b10, | ||
| 210 | AddressSize::_32Bit => 0b11, | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Time the Chip Select line stays high. | ||
| 216 | #[allow(missing_docs)] | ||
| 217 | #[derive(Copy, Clone)] | ||
| 218 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 219 | pub enum ChipSelectHighTime { | ||
| 220 | _1Cycle, | ||
| 221 | _2Cycle, | ||
| 222 | _3Cycle, | ||
| 223 | _4Cycle, | ||
| 224 | _5Cycle, | ||
| 225 | _6Cycle, | ||
| 226 | _7Cycle, | ||
| 227 | _8Cycle, | ||
| 228 | } | ||
| 229 | |||
| 230 | impl Into<u8> for ChipSelectHighTime { | ||
| 231 | fn into(self) -> u8 { | ||
| 232 | match self { | ||
| 233 | ChipSelectHighTime::_1Cycle => 0, | ||
| 234 | ChipSelectHighTime::_2Cycle => 1, | ||
| 235 | ChipSelectHighTime::_3Cycle => 2, | ||
| 236 | ChipSelectHighTime::_4Cycle => 3, | ||
| 237 | ChipSelectHighTime::_5Cycle => 4, | ||
| 238 | ChipSelectHighTime::_6Cycle => 5, | ||
| 239 | ChipSelectHighTime::_7Cycle => 6, | ||
| 240 | ChipSelectHighTime::_8Cycle => 7, | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | /// FIFO threshold. | ||
| 246 | #[allow(missing_docs)] | ||
| 247 | #[derive(Copy, Clone)] | ||
| 248 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 249 | pub enum FIFOThresholdLevel { | ||
| 250 | _1Bytes, | ||
| 251 | _2Bytes, | ||
| 252 | _3Bytes, | ||
| 253 | _4Bytes, | ||
| 254 | _5Bytes, | ||
| 255 | _6Bytes, | ||
| 256 | _7Bytes, | ||
| 257 | _8Bytes, | ||
| 258 | _9Bytes, | ||
| 259 | _10Bytes, | ||
| 260 | _11Bytes, | ||
| 261 | _12Bytes, | ||
| 262 | _13Bytes, | ||
| 263 | _14Bytes, | ||
| 264 | _15Bytes, | ||
| 265 | _16Bytes, | ||
| 266 | _17Bytes, | ||
| 267 | _18Bytes, | ||
| 268 | _19Bytes, | ||
| 269 | _20Bytes, | ||
| 270 | _21Bytes, | ||
| 271 | _22Bytes, | ||
| 272 | _23Bytes, | ||
| 273 | _24Bytes, | ||
| 274 | _25Bytes, | ||
| 275 | _26Bytes, | ||
| 276 | _27Bytes, | ||
| 277 | _28Bytes, | ||
| 278 | _29Bytes, | ||
| 279 | _30Bytes, | ||
| 280 | _31Bytes, | ||
| 281 | _32Bytes, | ||
| 282 | } | ||
| 283 | |||
| 284 | impl Into<u8> for FIFOThresholdLevel { | ||
| 285 | fn into(self) -> u8 { | ||
| 286 | match self { | ||
| 287 | FIFOThresholdLevel::_1Bytes => 0, | ||
| 288 | FIFOThresholdLevel::_2Bytes => 1, | ||
| 289 | FIFOThresholdLevel::_3Bytes => 2, | ||
| 290 | FIFOThresholdLevel::_4Bytes => 3, | ||
| 291 | FIFOThresholdLevel::_5Bytes => 4, | ||
| 292 | FIFOThresholdLevel::_6Bytes => 5, | ||
| 293 | FIFOThresholdLevel::_7Bytes => 6, | ||
| 294 | FIFOThresholdLevel::_8Bytes => 7, | ||
| 295 | FIFOThresholdLevel::_9Bytes => 8, | ||
| 296 | FIFOThresholdLevel::_10Bytes => 9, | ||
| 297 | FIFOThresholdLevel::_11Bytes => 10, | ||
| 298 | FIFOThresholdLevel::_12Bytes => 11, | ||
| 299 | FIFOThresholdLevel::_13Bytes => 12, | ||
| 300 | FIFOThresholdLevel::_14Bytes => 13, | ||
| 301 | FIFOThresholdLevel::_15Bytes => 14, | ||
| 302 | FIFOThresholdLevel::_16Bytes => 15, | ||
| 303 | FIFOThresholdLevel::_17Bytes => 16, | ||
| 304 | FIFOThresholdLevel::_18Bytes => 17, | ||
| 305 | FIFOThresholdLevel::_19Bytes => 18, | ||
| 306 | FIFOThresholdLevel::_20Bytes => 19, | ||
| 307 | FIFOThresholdLevel::_21Bytes => 20, | ||
| 308 | FIFOThresholdLevel::_22Bytes => 21, | ||
| 309 | FIFOThresholdLevel::_23Bytes => 22, | ||
| 310 | FIFOThresholdLevel::_24Bytes => 23, | ||
| 311 | FIFOThresholdLevel::_25Bytes => 24, | ||
| 312 | FIFOThresholdLevel::_26Bytes => 25, | ||
| 313 | FIFOThresholdLevel::_27Bytes => 26, | ||
| 314 | FIFOThresholdLevel::_28Bytes => 27, | ||
| 315 | FIFOThresholdLevel::_29Bytes => 28, | ||
| 316 | FIFOThresholdLevel::_30Bytes => 29, | ||
| 317 | FIFOThresholdLevel::_31Bytes => 30, | ||
| 318 | FIFOThresholdLevel::_32Bytes => 31, | ||
| 319 | } | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | /// Dummy cycle count | ||
| 324 | #[allow(missing_docs)] | ||
| 325 | #[derive(Copy, Clone)] | ||
| 326 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 327 | pub enum DummyCycles { | ||
| 328 | _0, | ||
| 329 | _1, | ||
| 330 | _2, | ||
| 331 | _3, | ||
| 332 | _4, | ||
| 333 | _5, | ||
| 334 | _6, | ||
| 335 | _7, | ||
| 336 | _8, | ||
| 337 | _9, | ||
| 338 | _10, | ||
| 339 | _11, | ||
| 340 | _12, | ||
| 341 | _13, | ||
| 342 | _14, | ||
| 343 | _15, | ||
| 344 | _16, | ||
| 345 | _17, | ||
| 346 | _18, | ||
| 347 | _19, | ||
| 348 | _20, | ||
| 349 | _21, | ||
| 350 | _22, | ||
| 351 | _23, | ||
| 352 | _24, | ||
| 353 | _25, | ||
| 354 | _26, | ||
| 355 | _27, | ||
| 356 | _28, | ||
| 357 | _29, | ||
| 358 | _30, | ||
| 359 | _31, | ||
| 360 | } | ||
| 361 | |||
| 362 | impl Into<u8> for DummyCycles { | ||
| 363 | fn into(self) -> u8 { | ||
| 364 | match self { | ||
| 365 | DummyCycles::_0 => 0, | ||
| 366 | DummyCycles::_1 => 1, | ||
| 367 | DummyCycles::_2 => 2, | ||
| 368 | DummyCycles::_3 => 3, | ||
| 369 | DummyCycles::_4 => 4, | ||
| 370 | DummyCycles::_5 => 5, | ||
| 371 | DummyCycles::_6 => 6, | ||
| 372 | DummyCycles::_7 => 7, | ||
| 373 | DummyCycles::_8 => 8, | ||
| 374 | DummyCycles::_9 => 9, | ||
| 375 | DummyCycles::_10 => 10, | ||
| 376 | DummyCycles::_11 => 11, | ||
| 377 | DummyCycles::_12 => 12, | ||
| 378 | DummyCycles::_13 => 13, | ||
| 379 | DummyCycles::_14 => 14, | ||
| 380 | DummyCycles::_15 => 15, | ||
| 381 | DummyCycles::_16 => 16, | ||
| 382 | DummyCycles::_17 => 17, | ||
| 383 | DummyCycles::_18 => 18, | ||
| 384 | DummyCycles::_19 => 19, | ||
| 385 | DummyCycles::_20 => 20, | ||
| 386 | DummyCycles::_21 => 21, | ||
| 387 | DummyCycles::_22 => 22, | ||
| 388 | DummyCycles::_23 => 23, | ||
| 389 | DummyCycles::_24 => 24, | ||
| 390 | DummyCycles::_25 => 25, | ||
| 391 | DummyCycles::_26 => 26, | ||
| 392 | DummyCycles::_27 => 27, | ||
| 393 | DummyCycles::_28 => 28, | ||
| 394 | DummyCycles::_29 => 29, | ||
| 395 | DummyCycles::_30 => 30, | ||
| 396 | DummyCycles::_31 => 31, | ||
| 397 | } | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | /// Functional mode | ||
| 402 | #[allow(missing_docs)] | ||
| 403 | #[allow(dead_code)] | ||
| 404 | #[derive(Copy, Clone)] | ||
| 405 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 406 | pub enum FunctionalMode { | ||
| 407 | IndirectWrite, | ||
| 408 | IndirectRead, | ||
| 409 | AutoStatusPolling, | ||
| 410 | MemoryMapped, | ||
| 411 | } | ||
| 412 | |||
| 413 | impl Into<u8> for FunctionalMode { | ||
| 414 | fn into(self) -> u8 { | ||
| 415 | match self { | ||
| 416 | FunctionalMode::IndirectWrite => 0x00, | ||
| 417 | FunctionalMode::IndirectRead => 0x01, | ||
| 418 | FunctionalMode::AutoStatusPolling => 0x02, | ||
| 419 | FunctionalMode::MemoryMapped => 0x03, | ||
| 420 | } | ||
| 421 | } | ||
| 422 | } | ||
diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs new file mode 100644 index 000000000..62bc0e979 --- /dev/null +++ b/embassy-stm32/src/hspi/mod.rs | |||
| @@ -0,0 +1,1007 @@ | |||
| 1 | //! HSPI Serial Peripheral Interface | ||
| 2 | //! | ||
| 3 | |||
| 4 | // NOTE: This is a partial implementation of the HSPI driver. | ||
| 5 | // It implements only Single and Octal SPI modes, but additional | ||
| 6 | // modes can be added as needed following the same pattern and | ||
| 7 | // using ospi/mod.rs as a reference. | ||
| 8 | |||
| 9 | #![macro_use] | ||
| 10 | |||
| 11 | pub mod enums; | ||
| 12 | |||
| 13 | use core::marker::PhantomData; | ||
| 14 | |||
| 15 | use embassy_embedded_hal::{GetConfig, SetConfig}; | ||
| 16 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 17 | pub use enums::*; | ||
| 18 | |||
| 19 | use crate::dma::{word, ChannelAndRequest}; | ||
| 20 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | ||
| 21 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | ||
| 22 | use crate::pac::hspi::Hspi as Regs; | ||
| 23 | use crate::peripherals; | ||
| 24 | use crate::rcc::{self, RccPeripheral}; | ||
| 25 | |||
| 26 | /// HSPI driver config. | ||
| 27 | #[derive(Clone, Copy)] | ||
| 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 29 | pub struct Config { | ||
| 30 | /// Fifo threshold used by the peripheral to generate the interrupt indicating data | ||
| 31 | /// or space is available in the FIFO | ||
| 32 | pub fifo_threshold: FIFOThresholdLevel, | ||
| 33 | /// Indicates the type of external device connected | ||
| 34 | pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface | ||
| 35 | /// Defines the size of the external device connected to the HSPI corresponding | ||
| 36 | /// to the number of address bits required to access the device | ||
| 37 | pub device_size: MemorySize, | ||
| 38 | /// Sets the minimum number of clock cycles that the chip select signal must be held high | ||
| 39 | /// between commands | ||
| 40 | pub chip_select_high_time: ChipSelectHighTime, | ||
| 41 | /// Enables the free running clock | ||
| 42 | pub free_running_clock: bool, | ||
| 43 | /// Sets the clock level when the device is not selected | ||
| 44 | pub clock_mode: bool, | ||
| 45 | /// Indicates the wrap size corresponding to the external device configuration | ||
| 46 | pub wrap_size: WrapSize, | ||
| 47 | /// Specified the prescaler factor used for generating the external clock based | ||
| 48 | /// on the AHB clock | ||
| 49 | pub clock_prescaler: u8, | ||
| 50 | /// Allows the delay of 1/2 cycle the data sampling to account for external | ||
| 51 | /// signal delays | ||
| 52 | pub sample_shifting: bool, | ||
| 53 | /// Allows hold to 1/4 cycle the data | ||
| 54 | pub delay_hold_quarter_cycle: bool, | ||
| 55 | /// Enables the transaction boundary feature and defines the boundary to release | ||
| 56 | /// the chip select | ||
| 57 | pub chip_select_boundary: u8, | ||
| 58 | /// Enables the delay block bypass so the sampling is not affected by the delay block | ||
| 59 | pub delay_block_bypass: bool, | ||
| 60 | /// Enables communication regulation feature. Chip select is released when the other | ||
| 61 | /// HSPI requests access to the bus | ||
| 62 | pub max_transfer: u8, | ||
| 63 | /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles | ||
| 64 | pub refresh: u32, | ||
| 65 | } | ||
| 66 | |||
| 67 | impl Default for Config { | ||
| 68 | fn default() -> Self { | ||
| 69 | Self { | ||
| 70 | fifo_threshold: FIFOThresholdLevel::_16Bytes, | ||
| 71 | memory_type: MemoryType::Micron, | ||
| 72 | device_size: MemorySize::Other(0), | ||
| 73 | chip_select_high_time: ChipSelectHighTime::_5Cycle, | ||
| 74 | free_running_clock: false, | ||
| 75 | clock_mode: false, | ||
| 76 | wrap_size: WrapSize::None, | ||
| 77 | clock_prescaler: 0, | ||
| 78 | sample_shifting: false, | ||
| 79 | delay_hold_quarter_cycle: false, | ||
| 80 | chip_select_boundary: 0, // Acceptable range 0 to 31 | ||
| 81 | delay_block_bypass: true, | ||
| 82 | max_transfer: 0, | ||
| 83 | refresh: 0, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// HSPI transfer configuration. | ||
| 89 | pub struct TransferConfig { | ||
| 90 | /// Instruction width (IMODE) | ||
| 91 | pub iwidth: HspiWidth, | ||
| 92 | /// Instruction Id | ||
| 93 | pub instruction: Option<u32>, | ||
| 94 | /// Number of Instruction Bytes | ||
| 95 | pub isize: AddressSize, | ||
| 96 | /// Instruction Double Transfer rate enable | ||
| 97 | pub idtr: bool, | ||
| 98 | |||
| 99 | /// Address width (ADMODE) | ||
| 100 | pub adwidth: HspiWidth, | ||
| 101 | /// Device memory address | ||
| 102 | pub address: Option<u32>, | ||
| 103 | /// Number of Address Bytes | ||
| 104 | pub adsize: AddressSize, | ||
| 105 | /// Address Double Transfer rate enable | ||
| 106 | pub addtr: bool, | ||
| 107 | |||
| 108 | /// Alternate bytes width (ABMODE) | ||
| 109 | pub abwidth: HspiWidth, | ||
| 110 | /// Alternate Bytes | ||
| 111 | pub alternate_bytes: Option<u32>, | ||
| 112 | /// Number of Alternate Bytes | ||
| 113 | pub absize: AddressSize, | ||
| 114 | /// Alternate Bytes Double Transfer rate enable | ||
| 115 | pub abdtr: bool, | ||
| 116 | |||
| 117 | /// Data width (DMODE) | ||
| 118 | pub dwidth: HspiWidth, | ||
| 119 | /// Data buffer | ||
| 120 | pub ddtr: bool, | ||
| 121 | |||
| 122 | /// Number of dummy cycles (DCYC) | ||
| 123 | pub dummy: DummyCycles, | ||
| 124 | } | ||
| 125 | |||
| 126 | impl Default for TransferConfig { | ||
| 127 | fn default() -> Self { | ||
| 128 | Self { | ||
| 129 | iwidth: HspiWidth::NONE, | ||
| 130 | instruction: None, | ||
| 131 | isize: AddressSize::_8Bit, | ||
| 132 | idtr: false, | ||
| 133 | |||
| 134 | adwidth: HspiWidth::NONE, | ||
| 135 | address: None, | ||
| 136 | adsize: AddressSize::_8Bit, | ||
| 137 | addtr: false, | ||
| 138 | |||
| 139 | abwidth: HspiWidth::NONE, | ||
| 140 | alternate_bytes: None, | ||
| 141 | absize: AddressSize::_8Bit, | ||
| 142 | abdtr: false, | ||
| 143 | |||
| 144 | dwidth: HspiWidth::NONE, | ||
| 145 | ddtr: false, | ||
| 146 | |||
| 147 | dummy: DummyCycles::_0, | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | /// Error used for HSPI implementation | ||
| 153 | #[derive(Debug)] | ||
| 154 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 155 | pub enum HspiError { | ||
| 156 | /// Peripheral configuration is invalid | ||
| 157 | InvalidConfiguration, | ||
| 158 | /// Operation configuration is invalid | ||
| 159 | InvalidCommand, | ||
| 160 | /// Size zero buffer passed to instruction | ||
| 161 | EmptyBuffer, | ||
| 162 | } | ||
| 163 | |||
| 164 | /// HSPI driver. | ||
| 165 | pub struct Hspi<'d, T: Instance, M: PeriMode> { | ||
| 166 | _peri: Peri<'d, T>, | ||
| 167 | sck: Option<Peri<'d, AnyPin>>, | ||
| 168 | d0: Option<Peri<'d, AnyPin>>, | ||
| 169 | d1: Option<Peri<'d, AnyPin>>, | ||
| 170 | d2: Option<Peri<'d, AnyPin>>, | ||
| 171 | d3: Option<Peri<'d, AnyPin>>, | ||
| 172 | d4: Option<Peri<'d, AnyPin>>, | ||
| 173 | d5: Option<Peri<'d, AnyPin>>, | ||
| 174 | d6: Option<Peri<'d, AnyPin>>, | ||
| 175 | d7: Option<Peri<'d, AnyPin>>, | ||
| 176 | d8: Option<Peri<'d, AnyPin>>, | ||
| 177 | d9: Option<Peri<'d, AnyPin>>, | ||
| 178 | d10: Option<Peri<'d, AnyPin>>, | ||
| 179 | d11: Option<Peri<'d, AnyPin>>, | ||
| 180 | d12: Option<Peri<'d, AnyPin>>, | ||
| 181 | d13: Option<Peri<'d, AnyPin>>, | ||
| 182 | d14: Option<Peri<'d, AnyPin>>, | ||
| 183 | d15: Option<Peri<'d, AnyPin>>, | ||
| 184 | nss: Option<Peri<'d, AnyPin>>, | ||
| 185 | dqs0: Option<Peri<'d, AnyPin>>, | ||
| 186 | dqs1: Option<Peri<'d, AnyPin>>, | ||
| 187 | dma: Option<ChannelAndRequest<'d>>, | ||
| 188 | _phantom: PhantomData<M>, | ||
| 189 | config: Config, | ||
| 190 | width: HspiWidth, | ||
| 191 | } | ||
| 192 | |||
| 193 | impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { | ||
| 194 | /// Enter memory mode. | ||
| 195 | /// The Input `read_config` is used to configure the read operation in memory mode | ||
| 196 | pub fn enable_memory_mapped_mode( | ||
| 197 | &mut self, | ||
| 198 | read_config: TransferConfig, | ||
| 199 | write_config: TransferConfig, | ||
| 200 | ) -> Result<(), HspiError> { | ||
| 201 | // Use configure command to set read config | ||
| 202 | self.configure_command(&read_config, None)?; | ||
| 203 | |||
| 204 | // Set writing configurations, there are separate registers for write configurations in memory mapped mode | ||
| 205 | T::REGS.wccr().modify(|w| { | ||
| 206 | w.set_imode(write_config.iwidth.into()); | ||
| 207 | w.set_idtr(write_config.idtr); | ||
| 208 | w.set_isize(write_config.isize.into()); | ||
| 209 | |||
| 210 | w.set_admode(write_config.adwidth.into()); | ||
| 211 | w.set_addtr(write_config.idtr); | ||
| 212 | w.set_adsize(write_config.adsize.into()); | ||
| 213 | |||
| 214 | w.set_dmode(write_config.dwidth.into()); | ||
| 215 | w.set_ddtr(write_config.ddtr); | ||
| 216 | |||
| 217 | w.set_abmode(write_config.abwidth.into()); | ||
| 218 | w.set_dqse(true); | ||
| 219 | }); | ||
| 220 | |||
| 221 | T::REGS.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); | ||
| 222 | |||
| 223 | // Enable memory mapped mode | ||
| 224 | T::REGS.cr().modify(|r| { | ||
| 225 | r.set_fmode(FunctionalMode::MemoryMapped.into()); | ||
| 226 | r.set_tcen(false); | ||
| 227 | }); | ||
| 228 | Ok(()) | ||
| 229 | } | ||
| 230 | |||
| 231 | /// Quit from memory mapped mode | ||
| 232 | pub fn disable_memory_mapped_mode(&mut self) { | ||
| 233 | T::REGS.cr().modify(|r| { | ||
| 234 | r.set_fmode(FunctionalMode::IndirectWrite.into()); | ||
| 235 | r.set_abort(true); | ||
| 236 | r.set_dmaen(false); | ||
| 237 | r.set_en(false); | ||
| 238 | }); | ||
| 239 | |||
| 240 | // Clear transfer complete flag | ||
| 241 | T::REGS.fcr().write(|w| w.set_ctcf(true)); | ||
| 242 | |||
| 243 | // Re-enable HSPI | ||
| 244 | T::REGS.cr().modify(|r| { | ||
| 245 | r.set_en(true); | ||
| 246 | }); | ||
| 247 | } | ||
| 248 | |||
| 249 | fn new_inner( | ||
| 250 | peri: Peri<'d, T>, | ||
| 251 | d0: Option<Peri<'d, AnyPin>>, | ||
| 252 | d1: Option<Peri<'d, AnyPin>>, | ||
| 253 | d2: Option<Peri<'d, AnyPin>>, | ||
| 254 | d3: Option<Peri<'d, AnyPin>>, | ||
| 255 | d4: Option<Peri<'d, AnyPin>>, | ||
| 256 | d5: Option<Peri<'d, AnyPin>>, | ||
| 257 | d6: Option<Peri<'d, AnyPin>>, | ||
| 258 | d7: Option<Peri<'d, AnyPin>>, | ||
| 259 | d8: Option<Peri<'d, AnyPin>>, | ||
| 260 | d9: Option<Peri<'d, AnyPin>>, | ||
| 261 | d10: Option<Peri<'d, AnyPin>>, | ||
| 262 | d11: Option<Peri<'d, AnyPin>>, | ||
| 263 | d12: Option<Peri<'d, AnyPin>>, | ||
| 264 | d13: Option<Peri<'d, AnyPin>>, | ||
| 265 | d14: Option<Peri<'d, AnyPin>>, | ||
| 266 | d15: Option<Peri<'d, AnyPin>>, | ||
| 267 | sck: Option<Peri<'d, AnyPin>>, | ||
| 268 | nss: Option<Peri<'d, AnyPin>>, | ||
| 269 | dqs0: Option<Peri<'d, AnyPin>>, | ||
| 270 | dqs1: Option<Peri<'d, AnyPin>>, | ||
| 271 | dma: Option<ChannelAndRequest<'d>>, | ||
| 272 | config: Config, | ||
| 273 | width: HspiWidth, | ||
| 274 | dual_memory_mode: bool, | ||
| 275 | ) -> Self { | ||
| 276 | // System configuration | ||
| 277 | rcc::enable_and_reset::<T>(); | ||
| 278 | |||
| 279 | // Call this function just to check that the clock for HSPI1 is properly setup | ||
| 280 | let _ = T::frequency(); | ||
| 281 | |||
| 282 | while T::REGS.sr().read().busy() {} | ||
| 283 | |||
| 284 | Self::configure_registers(&config, Some(dual_memory_mode)); | ||
| 285 | |||
| 286 | Self { | ||
| 287 | _peri: peri, | ||
| 288 | sck, | ||
| 289 | d0, | ||
| 290 | d1, | ||
| 291 | d2, | ||
| 292 | d3, | ||
| 293 | d4, | ||
| 294 | d5, | ||
| 295 | d6, | ||
| 296 | d7, | ||
| 297 | d8, | ||
| 298 | d9, | ||
| 299 | d10, | ||
| 300 | d11, | ||
| 301 | d12, | ||
| 302 | d13, | ||
| 303 | d14, | ||
| 304 | d15, | ||
| 305 | nss, | ||
| 306 | dqs0, | ||
| 307 | dqs1, | ||
| 308 | dma, | ||
| 309 | _phantom: PhantomData, | ||
| 310 | config, | ||
| 311 | width, | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | fn configure_registers(config: &Config, dual_memory_mode: Option<bool>) { | ||
| 316 | // Device configuration | ||
| 317 | T::REGS.dcr1().modify(|w| { | ||
| 318 | w.set_mtyp(config.memory_type.into()); | ||
| 319 | w.set_devsize(config.device_size.into()); | ||
| 320 | w.set_csht(config.chip_select_high_time.into()); | ||
| 321 | w.set_frck(false); | ||
| 322 | w.set_ckmode(config.clock_mode); | ||
| 323 | w.set_dlybyp(config.delay_block_bypass); | ||
| 324 | }); | ||
| 325 | |||
| 326 | T::REGS.dcr2().modify(|w| { | ||
| 327 | w.set_wrapsize(config.wrap_size.into()); | ||
| 328 | }); | ||
| 329 | |||
| 330 | T::REGS.dcr3().modify(|w| { | ||
| 331 | w.set_csbound(config.chip_select_boundary); | ||
| 332 | w.set_maxtran(config.max_transfer); | ||
| 333 | }); | ||
| 334 | |||
| 335 | T::REGS.dcr4().modify(|w| { | ||
| 336 | w.set_refresh(config.refresh); | ||
| 337 | }); | ||
| 338 | |||
| 339 | T::REGS.cr().modify(|w| { | ||
| 340 | w.set_fthres(config.fifo_threshold.into()); | ||
| 341 | }); | ||
| 342 | |||
| 343 | // Wait for busy flag to clear | ||
| 344 | while T::REGS.sr().read().busy() {} | ||
| 345 | |||
| 346 | T::REGS.dcr2().modify(|w| { | ||
| 347 | w.set_prescaler(config.clock_prescaler); | ||
| 348 | }); | ||
| 349 | |||
| 350 | // The configuration of clock prescaler trigger automatically a calibration process | ||
| 351 | // So it is necessary to wait the calibration is complete | ||
| 352 | while T::REGS.sr().read().busy() {} | ||
| 353 | |||
| 354 | if let Some(dual_memory_mode) = dual_memory_mode { | ||
| 355 | T::REGS.cr().modify(|w| { | ||
| 356 | w.set_dmm(dual_memory_mode); | ||
| 357 | }); | ||
| 358 | } | ||
| 359 | |||
| 360 | T::REGS.tcr().modify(|w| { | ||
| 361 | w.set_sshift(config.sample_shifting); | ||
| 362 | w.set_dhqc(config.delay_hold_quarter_cycle); | ||
| 363 | }); | ||
| 364 | |||
| 365 | // Enable peripheral | ||
| 366 | T::REGS.cr().modify(|w| { | ||
| 367 | w.set_en(true); | ||
| 368 | }); | ||
| 369 | |||
| 370 | // Free running clock needs to be set after peripheral enable | ||
| 371 | if config.free_running_clock { | ||
| 372 | T::REGS.dcr1().modify(|w| { | ||
| 373 | w.set_frck(config.free_running_clock); | ||
| 374 | }); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | // Function to configure the peripheral for the requested command | ||
| 379 | fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), HspiError> { | ||
| 380 | // Check that transaction doesn't use more than hardware initialized pins | ||
| 381 | if <enums::HspiWidth as Into<u8>>::into(command.iwidth) > <enums::HspiWidth as Into<u8>>::into(self.width) | ||
| 382 | || <enums::HspiWidth as Into<u8>>::into(command.adwidth) > <enums::HspiWidth as Into<u8>>::into(self.width) | ||
| 383 | || <enums::HspiWidth as Into<u8>>::into(command.abwidth) > <enums::HspiWidth as Into<u8>>::into(self.width) | ||
| 384 | || <enums::HspiWidth as Into<u8>>::into(command.dwidth) > <enums::HspiWidth as Into<u8>>::into(self.width) | ||
| 385 | { | ||
| 386 | return Err(HspiError::InvalidCommand); | ||
| 387 | } | ||
| 388 | |||
| 389 | while T::REGS.sr().read().busy() {} | ||
| 390 | |||
| 391 | T::REGS.cr().modify(|w| { | ||
| 392 | w.set_fmode(0.into()); | ||
| 393 | }); | ||
| 394 | |||
| 395 | // Configure alternate bytes | ||
| 396 | if let Some(ab) = command.alternate_bytes { | ||
| 397 | T::REGS.abr().write(|v| v.set_alternate(ab)); | ||
| 398 | T::REGS.ccr().modify(|w| { | ||
| 399 | w.set_abmode(command.abwidth.into()); | ||
| 400 | w.set_abdtr(command.abdtr); | ||
| 401 | w.set_absize(command.absize.into()); | ||
| 402 | }) | ||
| 403 | } | ||
| 404 | |||
| 405 | // Configure dummy cycles | ||
| 406 | T::REGS.tcr().modify(|w| { | ||
| 407 | w.set_dcyc(command.dummy.into()); | ||
| 408 | }); | ||
| 409 | |||
| 410 | // Configure data | ||
| 411 | if let Some(data_length) = data_len { | ||
| 412 | T::REGS.dlr().write(|v| { | ||
| 413 | v.set_dl((data_length - 1) as u32); | ||
| 414 | }) | ||
| 415 | } else { | ||
| 416 | T::REGS.dlr().write(|v| { | ||
| 417 | v.set_dl((0) as u32); | ||
| 418 | }) | ||
| 419 | } | ||
| 420 | |||
| 421 | // Configure instruction/address/data modes | ||
| 422 | T::REGS.ccr().modify(|w| { | ||
| 423 | w.set_imode(command.iwidth.into()); | ||
| 424 | w.set_idtr(command.idtr); | ||
| 425 | w.set_isize(command.isize.into()); | ||
| 426 | |||
| 427 | w.set_admode(command.adwidth.into()); | ||
| 428 | w.set_addtr(command.addtr); | ||
| 429 | w.set_adsize(command.adsize.into()); | ||
| 430 | |||
| 431 | w.set_dmode(command.dwidth.into()); | ||
| 432 | w.set_ddtr(command.ddtr); | ||
| 433 | }); | ||
| 434 | |||
| 435 | // Configure DQS | ||
| 436 | T::REGS.ccr().modify(|w| { | ||
| 437 | w.set_dqse(command.ddtr && command.instruction.unwrap_or(0) != 0x12ED); | ||
| 438 | }); | ||
| 439 | |||
| 440 | // Set information required to initiate transaction | ||
| 441 | if let Some(instruction) = command.instruction { | ||
| 442 | if let Some(address) = command.address { | ||
| 443 | T::REGS.ir().write(|v| { | ||
| 444 | v.set_instruction(instruction); | ||
| 445 | }); | ||
| 446 | |||
| 447 | T::REGS.ar().write(|v| { | ||
| 448 | v.set_address(address); | ||
| 449 | }); | ||
| 450 | } else { | ||
| 451 | T::REGS.ir().write(|v| { | ||
| 452 | v.set_instruction(instruction); | ||
| 453 | }); | ||
| 454 | } | ||
| 455 | } else { | ||
| 456 | if let Some(address) = command.address { | ||
| 457 | T::REGS.ar().write(|v| { | ||
| 458 | v.set_address(address); | ||
| 459 | }); | ||
| 460 | } else { | ||
| 461 | // The only single phase transaction supported is instruction only | ||
| 462 | return Err(HspiError::InvalidCommand); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | Ok(()) | ||
| 467 | } | ||
| 468 | |||
| 469 | /// Function used to control or configure the target device without data transfer | ||
| 470 | pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), HspiError> { | ||
| 471 | // Wait for peripheral to be free | ||
| 472 | while T::REGS.sr().read().busy() {} | ||
| 473 | |||
| 474 | // Need additional validation that command configuration doesn't have data set | ||
| 475 | self.configure_command(command, None)?; | ||
| 476 | |||
| 477 | // Transaction initiated by setting final configuration, i.e the instruction register | ||
| 478 | while !T::REGS.sr().read().tcf() {} | ||
| 479 | T::REGS.fcr().write(|w| { | ||
| 480 | w.set_ctcf(true); | ||
| 481 | }); | ||
| 482 | |||
| 483 | Ok(()) | ||
| 484 | } | ||
| 485 | |||
| 486 | /// Blocking read with byte by byte data transfer | ||
| 487 | pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> { | ||
| 488 | if buf.is_empty() { | ||
| 489 | return Err(HspiError::EmptyBuffer); | ||
| 490 | } | ||
| 491 | |||
| 492 | // Wait for peripheral to be free | ||
| 493 | while T::REGS.sr().read().busy() {} | ||
| 494 | |||
| 495 | // Ensure DMA is not enabled for this transaction | ||
| 496 | T::REGS.cr().modify(|w| { | ||
| 497 | w.set_dmaen(false); | ||
| 498 | }); | ||
| 499 | |||
| 500 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 501 | |||
| 502 | let current_address = T::REGS.ar().read().address(); | ||
| 503 | let current_instruction = T::REGS.ir().read().instruction(); | ||
| 504 | |||
| 505 | // For a indirect read transaction, the transaction begins when the instruction/address is set | ||
| 506 | T::REGS | ||
| 507 | .cr() | ||
| 508 | .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into())); | ||
| 509 | if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() { | ||
| 510 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | ||
| 511 | } else { | ||
| 512 | T::REGS.ar().write(|v| v.set_address(current_address)); | ||
| 513 | } | ||
| 514 | |||
| 515 | for idx in 0..buf.len() { | ||
| 516 | while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} | ||
| 517 | buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; | ||
| 518 | } | ||
| 519 | |||
| 520 | while !T::REGS.sr().read().tcf() {} | ||
| 521 | T::REGS.fcr().write(|v| v.set_ctcf(true)); | ||
| 522 | |||
| 523 | Ok(()) | ||
| 524 | } | ||
| 525 | |||
| 526 | /// Blocking write with byte by byte data transfer | ||
| 527 | pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> { | ||
| 528 | if buf.is_empty() { | ||
| 529 | return Err(HspiError::EmptyBuffer); | ||
| 530 | } | ||
| 531 | |||
| 532 | // Wait for peripheral to be free | ||
| 533 | while T::REGS.sr().read().busy() {} | ||
| 534 | |||
| 535 | T::REGS.cr().modify(|w| { | ||
| 536 | w.set_dmaen(false); | ||
| 537 | }); | ||
| 538 | |||
| 539 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 540 | |||
| 541 | T::REGS | ||
| 542 | .cr() | ||
| 543 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | ||
| 544 | |||
| 545 | for idx in 0..buf.len() { | ||
| 546 | while !T::REGS.sr().read().ftf() {} | ||
| 547 | unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; | ||
| 548 | } | ||
| 549 | |||
| 550 | while !T::REGS.sr().read().tcf() {} | ||
| 551 | T::REGS.fcr().write(|v| v.set_ctcf(true)); | ||
| 552 | |||
| 553 | Ok(()) | ||
| 554 | } | ||
| 555 | |||
| 556 | /// Set new bus configuration | ||
| 557 | pub fn set_config(&mut self, config: &Config) { | ||
| 558 | // Wait for busy flag to clear | ||
| 559 | while T::REGS.sr().read().busy() {} | ||
| 560 | |||
| 561 | // Disable DMA channel while configuring the peripheral | ||
| 562 | T::REGS.cr().modify(|w| { | ||
| 563 | w.set_dmaen(false); | ||
| 564 | }); | ||
| 565 | |||
| 566 | Self::configure_registers(config, None); | ||
| 567 | |||
| 568 | self.config = *config; | ||
| 569 | } | ||
| 570 | |||
| 571 | /// Get current configuration | ||
| 572 | pub fn get_config(&self) -> Config { | ||
| 573 | self.config | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | impl<'d, T: Instance> Hspi<'d, T, Blocking> { | ||
| 578 | /// Create new blocking HSPI driver for single spi external chip | ||
| 579 | pub fn new_blocking_singlespi( | ||
| 580 | peri: Peri<'d, T>, | ||
| 581 | sck: Peri<'d, impl SckPin<T>>, | ||
| 582 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 583 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 584 | nss: Peri<'d, impl NSSPin<T>>, | ||
| 585 | config: Config, | ||
| 586 | ) -> Self { | ||
| 587 | Self::new_inner( | ||
| 588 | peri, | ||
| 589 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 590 | new_pin!(d1, AfType::input(Pull::None)), | ||
| 591 | None, | ||
| 592 | None, | ||
| 593 | None, | ||
| 594 | None, | ||
| 595 | None, | ||
| 596 | None, | ||
| 597 | None, | ||
| 598 | None, | ||
| 599 | None, | ||
| 600 | None, | ||
| 601 | None, | ||
| 602 | None, | ||
| 603 | None, | ||
| 604 | None, | ||
| 605 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 606 | new_pin!( | ||
| 607 | nss, | ||
| 608 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 609 | ), | ||
| 610 | None, | ||
| 611 | None, | ||
| 612 | None, | ||
| 613 | config, | ||
| 614 | HspiWidth::SING, | ||
| 615 | false, | ||
| 616 | ) | ||
| 617 | } | ||
| 618 | |||
| 619 | /// Create new blocking HSPI driver for octospi external chip | ||
| 620 | pub fn new_blocking_octospi( | ||
| 621 | peri: Peri<'d, T>, | ||
| 622 | sck: Peri<'d, impl SckPin<T>>, | ||
| 623 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 624 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 625 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 626 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 627 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 628 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 629 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 630 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 631 | nss: Peri<'d, impl NSSPin<T>>, | ||
| 632 | dqs0: Peri<'d, impl DQS0Pin<T>>, | ||
| 633 | config: Config, | ||
| 634 | ) -> Self { | ||
| 635 | Self::new_inner( | ||
| 636 | peri, | ||
| 637 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 638 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 639 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 640 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 641 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 642 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 643 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 644 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 645 | None, | ||
| 646 | None, | ||
| 647 | None, | ||
| 648 | None, | ||
| 649 | None, | ||
| 650 | None, | ||
| 651 | None, | ||
| 652 | None, | ||
| 653 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 654 | new_pin!( | ||
| 655 | nss, | ||
| 656 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 657 | ), | ||
| 658 | new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 659 | None, | ||
| 660 | None, | ||
| 661 | config, | ||
| 662 | HspiWidth::OCTO, | ||
| 663 | false, | ||
| 664 | ) | ||
| 665 | } | ||
| 666 | } | ||
| 667 | |||
| 668 | impl<'d, T: Instance> Hspi<'d, T, Async> { | ||
| 669 | /// Create new HSPI driver for a single spi external chip | ||
| 670 | pub fn new_singlespi( | ||
| 671 | peri: Peri<'d, T>, | ||
| 672 | sck: Peri<'d, impl SckPin<T>>, | ||
| 673 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 674 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 675 | nss: Peri<'d, impl NSSPin<T>>, | ||
| 676 | dma: Peri<'d, impl HspiDma<T>>, | ||
| 677 | config: Config, | ||
| 678 | ) -> Self { | ||
| 679 | Self::new_inner( | ||
| 680 | peri, | ||
| 681 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 682 | new_pin!(d1, AfType::input(Pull::None)), | ||
| 683 | None, | ||
| 684 | None, | ||
| 685 | None, | ||
| 686 | None, | ||
| 687 | None, | ||
| 688 | None, | ||
| 689 | None, | ||
| 690 | None, | ||
| 691 | None, | ||
| 692 | None, | ||
| 693 | None, | ||
| 694 | None, | ||
| 695 | None, | ||
| 696 | None, | ||
| 697 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 698 | new_pin!( | ||
| 699 | nss, | ||
| 700 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 701 | ), | ||
| 702 | None, | ||
| 703 | None, | ||
| 704 | new_dma!(dma), | ||
| 705 | config, | ||
| 706 | HspiWidth::SING, | ||
| 707 | false, | ||
| 708 | ) | ||
| 709 | } | ||
| 710 | |||
| 711 | /// Create new HSPI driver for octospi external chip | ||
| 712 | pub fn new_octospi( | ||
| 713 | peri: Peri<'d, T>, | ||
| 714 | sck: Peri<'d, impl SckPin<T>>, | ||
| 715 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 716 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 717 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 718 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 719 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 720 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 721 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 722 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 723 | nss: Peri<'d, impl NSSPin<T>>, | ||
| 724 | dqs0: Peri<'d, impl DQS0Pin<T>>, | ||
| 725 | dma: Peri<'d, impl HspiDma<T>>, | ||
| 726 | config: Config, | ||
| 727 | ) -> Self { | ||
| 728 | Self::new_inner( | ||
| 729 | peri, | ||
| 730 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 731 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 732 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 733 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 734 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 735 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 736 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 737 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 738 | new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 739 | None, | ||
| 740 | None, | ||
| 741 | None, | ||
| 742 | None, | ||
| 743 | None, | ||
| 744 | None, | ||
| 745 | None, | ||
| 746 | None, | ||
| 747 | new_pin!( | ||
| 748 | nss, | ||
| 749 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 750 | ), | ||
| 751 | new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 752 | None, | ||
| 753 | new_dma!(dma), | ||
| 754 | config, | ||
| 755 | HspiWidth::OCTO, | ||
| 756 | false, | ||
| 757 | ) | ||
| 758 | } | ||
| 759 | |||
| 760 | /// Blocking read with DMA transfer | ||
| 761 | pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> { | ||
| 762 | if buf.is_empty() { | ||
| 763 | return Err(HspiError::EmptyBuffer); | ||
| 764 | } | ||
| 765 | |||
| 766 | // Wait for peripheral to be free | ||
| 767 | while T::REGS.sr().read().busy() {} | ||
| 768 | |||
| 769 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 770 | |||
| 771 | let current_address = T::REGS.ar().read().address(); | ||
| 772 | let current_instruction = T::REGS.ir().read().instruction(); | ||
| 773 | |||
| 774 | // For a indirect read transaction, the transaction begins when the instruction/address is set | ||
| 775 | T::REGS | ||
| 776 | .cr() | ||
| 777 | .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into())); | ||
| 778 | if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() { | ||
| 779 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | ||
| 780 | } else { | ||
| 781 | T::REGS.ar().write(|v| v.set_address(current_address)); | ||
| 782 | } | ||
| 783 | |||
| 784 | let transfer = unsafe { | ||
| 785 | self.dma | ||
| 786 | .as_mut() | ||
| 787 | .unwrap() | ||
| 788 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | ||
| 789 | }; | ||
| 790 | |||
| 791 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 792 | |||
| 793 | transfer.blocking_wait(); | ||
| 794 | |||
| 795 | finish_dma(T::REGS); | ||
| 796 | |||
| 797 | Ok(()) | ||
| 798 | } | ||
| 799 | |||
| 800 | /// Blocking write with DMA transfer | ||
| 801 | pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> { | ||
| 802 | if buf.is_empty() { | ||
| 803 | return Err(HspiError::EmptyBuffer); | ||
| 804 | } | ||
| 805 | |||
| 806 | // Wait for peripheral to be free | ||
| 807 | while T::REGS.sr().read().busy() {} | ||
| 808 | |||
| 809 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 810 | T::REGS | ||
| 811 | .cr() | ||
| 812 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | ||
| 813 | |||
| 814 | let transfer = unsafe { | ||
| 815 | self.dma | ||
| 816 | .as_mut() | ||
| 817 | .unwrap() | ||
| 818 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 819 | }; | ||
| 820 | |||
| 821 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 822 | |||
| 823 | transfer.blocking_wait(); | ||
| 824 | |||
| 825 | finish_dma(T::REGS); | ||
| 826 | |||
| 827 | Ok(()) | ||
| 828 | } | ||
| 829 | |||
| 830 | /// Asynchronous read from external device | ||
| 831 | pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> { | ||
| 832 | if buf.is_empty() { | ||
| 833 | return Err(HspiError::EmptyBuffer); | ||
| 834 | } | ||
| 835 | |||
| 836 | // Wait for peripheral to be free | ||
| 837 | while T::REGS.sr().read().busy() {} | ||
| 838 | |||
| 839 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 840 | |||
| 841 | let current_address = T::REGS.ar().read().address(); | ||
| 842 | let current_instruction = T::REGS.ir().read().instruction(); | ||
| 843 | |||
| 844 | // For a indirect read transaction, the transaction begins when the instruction/address is set | ||
| 845 | T::REGS | ||
| 846 | .cr() | ||
| 847 | .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into())); | ||
| 848 | if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() { | ||
| 849 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | ||
| 850 | } else { | ||
| 851 | T::REGS.ar().write(|v| v.set_address(current_address)); | ||
| 852 | } | ||
| 853 | |||
| 854 | let transfer = unsafe { | ||
| 855 | self.dma | ||
| 856 | .as_mut() | ||
| 857 | .unwrap() | ||
| 858 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | ||
| 859 | }; | ||
| 860 | |||
| 861 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 862 | |||
| 863 | transfer.await; | ||
| 864 | |||
| 865 | finish_dma(T::REGS); | ||
| 866 | |||
| 867 | Ok(()) | ||
| 868 | } | ||
| 869 | |||
| 870 | /// Asynchronous write to external device | ||
| 871 | pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> { | ||
| 872 | if buf.is_empty() { | ||
| 873 | return Err(HspiError::EmptyBuffer); | ||
| 874 | } | ||
| 875 | |||
| 876 | // Wait for peripheral to be free | ||
| 877 | while T::REGS.sr().read().busy() {} | ||
| 878 | |||
| 879 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 880 | T::REGS | ||
| 881 | .cr() | ||
| 882 | .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); | ||
| 883 | |||
| 884 | let transfer = unsafe { | ||
| 885 | self.dma | ||
| 886 | .as_mut() | ||
| 887 | .unwrap() | ||
| 888 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 889 | }; | ||
| 890 | |||
| 891 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 892 | |||
| 893 | transfer.await; | ||
| 894 | |||
| 895 | finish_dma(T::REGS); | ||
| 896 | |||
| 897 | Ok(()) | ||
| 898 | } | ||
| 899 | } | ||
| 900 | |||
| 901 | impl<'d, T: Instance, M: PeriMode> Drop for Hspi<'d, T, M> { | ||
| 902 | fn drop(&mut self) { | ||
| 903 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | ||
| 904 | self.d0.as_ref().map(|x| x.set_as_disconnected()); | ||
| 905 | self.d1.as_ref().map(|x| x.set_as_disconnected()); | ||
| 906 | self.d2.as_ref().map(|x| x.set_as_disconnected()); | ||
| 907 | self.d3.as_ref().map(|x| x.set_as_disconnected()); | ||
| 908 | self.d4.as_ref().map(|x| x.set_as_disconnected()); | ||
| 909 | self.d5.as_ref().map(|x| x.set_as_disconnected()); | ||
| 910 | self.d6.as_ref().map(|x| x.set_as_disconnected()); | ||
| 911 | self.d7.as_ref().map(|x| x.set_as_disconnected()); | ||
| 912 | self.d8.as_ref().map(|x| x.set_as_disconnected()); | ||
| 913 | self.d9.as_ref().map(|x| x.set_as_disconnected()); | ||
| 914 | self.d10.as_ref().map(|x| x.set_as_disconnected()); | ||
| 915 | self.d11.as_ref().map(|x| x.set_as_disconnected()); | ||
| 916 | self.d12.as_ref().map(|x| x.set_as_disconnected()); | ||
| 917 | self.d13.as_ref().map(|x| x.set_as_disconnected()); | ||
| 918 | self.d14.as_ref().map(|x| x.set_as_disconnected()); | ||
| 919 | self.d15.as_ref().map(|x| x.set_as_disconnected()); | ||
| 920 | self.nss.as_ref().map(|x| x.set_as_disconnected()); | ||
| 921 | self.dqs0.as_ref().map(|x| x.set_as_disconnected()); | ||
| 922 | self.dqs1.as_ref().map(|x| x.set_as_disconnected()); | ||
| 923 | |||
| 924 | rcc::disable::<T>(); | ||
| 925 | } | ||
| 926 | } | ||
| 927 | |||
| 928 | fn finish_dma(regs: Regs) { | ||
| 929 | while !regs.sr().read().tcf() {} | ||
| 930 | regs.fcr().write(|v| v.set_ctcf(true)); | ||
| 931 | |||
| 932 | regs.cr().modify(|w| { | ||
| 933 | w.set_dmaen(false); | ||
| 934 | }); | ||
| 935 | } | ||
| 936 | |||
| 937 | /// HSPI instance trait. | ||
| 938 | pub(crate) trait SealedInstance { | ||
| 939 | const REGS: Regs; | ||
| 940 | } | ||
| 941 | |||
| 942 | /// HSPI instance trait. | ||
| 943 | #[allow(private_bounds)] | ||
| 944 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {} | ||
| 945 | |||
| 946 | pin_trait!(SckPin, Instance); | ||
| 947 | pin_trait!(NckPin, Instance); | ||
| 948 | pin_trait!(D0Pin, Instance); | ||
| 949 | pin_trait!(D1Pin, Instance); | ||
| 950 | pin_trait!(D2Pin, Instance); | ||
| 951 | pin_trait!(D3Pin, Instance); | ||
| 952 | pin_trait!(D4Pin, Instance); | ||
| 953 | pin_trait!(D5Pin, Instance); | ||
| 954 | pin_trait!(D6Pin, Instance); | ||
| 955 | pin_trait!(D7Pin, Instance); | ||
| 956 | pin_trait!(D8Pin, Instance); | ||
| 957 | pin_trait!(D9Pin, Instance); | ||
| 958 | pin_trait!(D10Pin, Instance); | ||
| 959 | pin_trait!(D11Pin, Instance); | ||
| 960 | pin_trait!(D12Pin, Instance); | ||
| 961 | pin_trait!(D13Pin, Instance); | ||
| 962 | pin_trait!(D14Pin, Instance); | ||
| 963 | pin_trait!(D15Pin, Instance); | ||
| 964 | pin_trait!(DQS0Pin, Instance); | ||
| 965 | pin_trait!(DQS1Pin, Instance); | ||
| 966 | pin_trait!(NSSPin, Instance); | ||
| 967 | dma_trait!(HspiDma, Instance); | ||
| 968 | |||
| 969 | foreach_peripheral!( | ||
| 970 | (hspi, $inst:ident) => { | ||
| 971 | impl SealedInstance for peripherals::$inst { | ||
| 972 | const REGS: Regs = crate::pac::$inst; | ||
| 973 | } | ||
| 974 | |||
| 975 | impl Instance for peripherals::$inst {} | ||
| 976 | }; | ||
| 977 | ); | ||
| 978 | |||
| 979 | impl<'d, T: Instance, M: PeriMode> SetConfig for Hspi<'d, T, M> { | ||
| 980 | type Config = Config; | ||
| 981 | type ConfigError = (); | ||
| 982 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | ||
| 983 | self.set_config(config); | ||
| 984 | Ok(()) | ||
| 985 | } | ||
| 986 | } | ||
| 987 | |||
| 988 | impl<'d, T: Instance, M: PeriMode> GetConfig for Hspi<'d, T, M> { | ||
| 989 | type Config = Config; | ||
| 990 | fn get_config(&self) -> Self::Config { | ||
| 991 | self.get_config() | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | /// Word sizes usable for HSPI. | ||
| 996 | #[allow(private_bounds)] | ||
| 997 | pub trait Word: word::Word {} | ||
| 998 | |||
| 999 | macro_rules! impl_word { | ||
| 1000 | ($T:ty) => { | ||
| 1001 | impl Word for $T {} | ||
| 1002 | }; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | impl_word!(u8); | ||
| 1006 | impl_word!(u16); | ||
| 1007 | impl_word!(u32); | ||
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 2ff21702b..825dd240c 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs | |||
| @@ -12,7 +12,7 @@ use core::iter; | |||
| 12 | use core::marker::PhantomData; | 12 | use core::marker::PhantomData; |
| 13 | 13 | ||
| 14 | pub use config::*; | 14 | pub use config::*; |
| 15 | use embassy_hal_internal::{Peripheral, PeripheralRef}; | 15 | use embassy_hal_internal::Peri; |
| 16 | use embassy_sync::waitqueue::AtomicWaker; | 16 | use embassy_sync::waitqueue::AtomicWaker; |
| 17 | #[cfg(feature = "time")] | 17 | #[cfg(feature = "time")] |
| 18 | use embassy_time::{Duration, Instant}; | 18 | use embassy_time::{Duration, Instant}; |
| @@ -47,6 +47,24 @@ pub enum Error { | |||
| 47 | ZeroLengthTransfer, | 47 | ZeroLengthTransfer, |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | impl core::fmt::Display for Error { | ||
| 51 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 52 | let message = match self { | ||
| 53 | Self::Bus => "Bus Error", | ||
| 54 | Self::Arbitration => "Arbitration Lost", | ||
| 55 | Self::Nack => "ACK Not Received", | ||
| 56 | Self::Timeout => "Request Timed Out", | ||
| 57 | Self::Crc => "CRC Mismatch", | ||
| 58 | Self::Overrun => "Buffer Overrun", | ||
| 59 | Self::ZeroLengthTransfer => "Zero-Length Transfers are not allowed", | ||
| 60 | }; | ||
| 61 | |||
| 62 | write!(f, "{}", message) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | impl core::error::Error for Error {} | ||
| 67 | |||
| 50 | /// I2C modes | 68 | /// I2C modes |
| 51 | pub mod mode { | 69 | pub mod mode { |
| 52 | trait SealedMode {} | 70 | trait SealedMode {} |
| @@ -99,8 +117,8 @@ pub enum SendStatus { | |||
| 99 | 117 | ||
| 100 | struct I2CDropGuard<'d> { | 118 | struct I2CDropGuard<'d> { |
| 101 | info: &'static Info, | 119 | info: &'static Info, |
| 102 | scl: Option<PeripheralRef<'d, AnyPin>>, | 120 | scl: Option<Peri<'d, AnyPin>>, |
| 103 | sda: Option<PeripheralRef<'d, AnyPin>>, | 121 | sda: Option<Peri<'d, AnyPin>>, |
| 104 | } | 122 | } |
| 105 | impl<'d> Drop for I2CDropGuard<'d> { | 123 | impl<'d> Drop for I2CDropGuard<'d> { |
| 106 | fn drop(&mut self) { | 124 | fn drop(&mut self) { |
| @@ -132,14 +150,14 @@ pub struct I2c<'d, M: Mode, IM: MasterMode> { | |||
| 132 | impl<'d> I2c<'d, Async, Master> { | 150 | impl<'d> I2c<'d, Async, Master> { |
| 133 | /// Create a new I2C driver. | 151 | /// Create a new I2C driver. |
| 134 | pub fn new<T: Instance>( | 152 | pub fn new<T: Instance>( |
| 135 | peri: impl Peripheral<P = T> + 'd, | 153 | peri: Peri<'d, T>, |
| 136 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, | 154 | scl: Peri<'d, impl SclPin<T>>, |
| 137 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | 155 | sda: Peri<'d, impl SdaPin<T>>, |
| 138 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> | 156 | _irq: impl interrupt::typelevel::Binding<T::EventInterrupt, EventInterruptHandler<T>> |
| 139 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> | 157 | + interrupt::typelevel::Binding<T::ErrorInterrupt, ErrorInterruptHandler<T>> |
| 140 | + 'd, | 158 | + 'd, |
| 141 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 159 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 142 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 160 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 143 | freq: Hertz, | 161 | freq: Hertz, |
| 144 | config: Config, | 162 | config: Config, |
| 145 | ) -> Self { | 163 | ) -> Self { |
| @@ -158,9 +176,9 @@ impl<'d> I2c<'d, Async, Master> { | |||
| 158 | impl<'d> I2c<'d, Blocking, Master> { | 176 | impl<'d> I2c<'d, Blocking, Master> { |
| 159 | /// Create a new blocking I2C driver. | 177 | /// Create a new blocking I2C driver. |
| 160 | pub fn new_blocking<T: Instance>( | 178 | pub fn new_blocking<T: Instance>( |
| 161 | peri: impl Peripheral<P = T> + 'd, | 179 | peri: Peri<'d, T>, |
| 162 | scl: impl Peripheral<P = impl SclPin<T>> + 'd, | 180 | scl: Peri<'d, impl SclPin<T>>, |
| 163 | sda: impl Peripheral<P = impl SdaPin<T>> + 'd, | 181 | sda: Peri<'d, impl SdaPin<T>>, |
| 164 | freq: Hertz, | 182 | freq: Hertz, |
| 165 | config: Config, | 183 | config: Config, |
| 166 | ) -> Self { | 184 | ) -> Self { |
| @@ -179,9 +197,9 @@ impl<'d> I2c<'d, Blocking, Master> { | |||
| 179 | impl<'d, M: Mode> I2c<'d, M, Master> { | 197 | impl<'d, M: Mode> I2c<'d, M, Master> { |
| 180 | /// Create a new I2C driver. | 198 | /// Create a new I2C driver. |
| 181 | fn new_inner<T: Instance>( | 199 | fn new_inner<T: Instance>( |
| 182 | _peri: impl Peripheral<P = T> + 'd, | 200 | _peri: Peri<'d, T>, |
| 183 | scl: Option<PeripheralRef<'d, AnyPin>>, | 201 | scl: Option<Peri<'d, AnyPin>>, |
| 184 | sda: Option<PeripheralRef<'d, AnyPin>>, | 202 | sda: Option<Peri<'d, AnyPin>>, |
| 185 | tx_dma: Option<ChannelAndRequest<'d>>, | 203 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 186 | rx_dma: Option<ChannelAndRequest<'d>>, | 204 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 187 | freq: Hertz, | 205 | freq: Hertz, |
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 64ccd24c7..990ce7d21 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs | |||
| @@ -40,7 +40,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 40 | let regs = T::info().regs; | 40 | let regs = T::info().regs; |
| 41 | let isr = regs.isr().read(); | 41 | let isr = regs.isr().read(); |
| 42 | 42 | ||
| 43 | if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() { | 43 | if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() { |
| 44 | T::state().waker.wake(); | 44 | T::state().waker.wake(); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| @@ -48,10 +48,11 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() { | |||
| 48 | regs.cr1().modify(|w| { | 48 | regs.cr1().modify(|w| { |
| 49 | w.set_addrie(false); | 49 | w.set_addrie(false); |
| 50 | w.set_stopie(false); | 50 | w.set_stopie(false); |
| 51 | // The flag can only be cleared by writting to nbytes, we won't do that here | ||
| 51 | w.set_tcie(false); | 52 | w.set_tcie(false); |
| 52 | // The flag can only be cleared by writting to nbytes, we won't do that here, so disable | 53 | // Error flags are to be read in the routines, so we also don't clear them here |
| 53 | // the interrupt | 54 | w.set_nackie(false); |
| 54 | w.set_tcie(false); | 55 | w.set_errie(false); |
| 55 | }); | 56 | }); |
| 56 | }); | 57 | }); |
| 57 | } | 58 | } |
| @@ -107,7 +108,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 107 | // is BUSY or I2C is in slave mode. | 108 | // is BUSY or I2C is in slave mode. |
| 108 | 109 | ||
| 109 | let reload = if reload { | 110 | let reload = if reload { |
| 110 | i2c::vals::Reload::NOTCOMPLETED | 111 | i2c::vals::Reload::NOT_COMPLETED |
| 111 | } else { | 112 | } else { |
| 112 | i2c::vals::Reload::COMPLETED | 113 | i2c::vals::Reload::COMPLETED |
| 113 | }; | 114 | }; |
| @@ -148,7 +149,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 148 | } | 149 | } |
| 149 | 150 | ||
| 150 | let reload = if reload { | 151 | let reload = if reload { |
| 151 | i2c::vals::Reload::NOTCOMPLETED | 152 | i2c::vals::Reload::NOT_COMPLETED |
| 152 | } else { | 153 | } else { |
| 153 | i2c::vals::Reload::COMPLETED | 154 | i2c::vals::Reload::COMPLETED |
| 154 | }; | 155 | }; |
| @@ -177,7 +178,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | |||
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | let will_reload = if will_reload { | 180 | let will_reload = if will_reload { |
| 180 | i2c::vals::Reload::NOTCOMPLETED | 181 | i2c::vals::Reload::NOT_COMPLETED |
| 181 | } else { | 182 | } else { |
| 182 | i2c::vals::Reload::COMPLETED | 183 | i2c::vals::Reload::COMPLETED |
| 183 | }; | 184 | }; |
| @@ -483,6 +484,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 483 | write: &[u8], | 484 | write: &[u8], |
| 484 | first_slice: bool, | 485 | first_slice: bool, |
| 485 | last_slice: bool, | 486 | last_slice: bool, |
| 487 | send_stop: bool, | ||
| 486 | timeout: Timeout, | 488 | timeout: Timeout, |
| 487 | ) -> Result<(), Error> { | 489 | ) -> Result<(), Error> { |
| 488 | let total_len = write.len(); | 490 | let total_len = write.len(); |
| @@ -494,6 +496,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 494 | if first_slice { | 496 | if first_slice { |
| 495 | w.set_tcie(true); | 497 | w.set_tcie(true); |
| 496 | } | 498 | } |
| 499 | w.set_nackie(true); | ||
| 500 | w.set_errie(true); | ||
| 497 | }); | 501 | }); |
| 498 | let dst = regs.txdr().as_ptr() as *mut u8; | 502 | let dst = regs.txdr().as_ptr() as *mut u8; |
| 499 | 503 | ||
| @@ -504,18 +508,41 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 504 | 508 | ||
| 505 | let on_drop = OnDrop::new(|| { | 509 | let on_drop = OnDrop::new(|| { |
| 506 | let regs = self.info.regs; | 510 | let regs = self.info.regs; |
| 511 | let isr = regs.isr().read(); | ||
| 507 | regs.cr1().modify(|w| { | 512 | regs.cr1().modify(|w| { |
| 508 | if last_slice { | 513 | if last_slice || isr.nackf() || isr.arlo() || isr.berr() || isr.ovr() { |
| 509 | w.set_txdmaen(false); | 514 | w.set_txdmaen(false); |
| 510 | } | 515 | } |
| 511 | w.set_tcie(false); | 516 | w.set_tcie(false); |
| 512 | }) | 517 | w.set_nackie(false); |
| 518 | w.set_errie(false); | ||
| 519 | }); | ||
| 520 | regs.icr().write(|w| { | ||
| 521 | w.set_nackcf(true); | ||
| 522 | w.set_berrcf(true); | ||
| 523 | w.set_arlocf(true); | ||
| 524 | w.set_ovrcf(true); | ||
| 525 | }); | ||
| 513 | }); | 526 | }); |
| 514 | 527 | ||
| 515 | poll_fn(|cx| { | 528 | poll_fn(|cx| { |
| 516 | self.state.waker.register(cx.waker()); | 529 | self.state.waker.register(cx.waker()); |
| 517 | 530 | ||
| 518 | let isr = self.info.regs.isr().read(); | 531 | let isr = self.info.regs.isr().read(); |
| 532 | |||
| 533 | if isr.nackf() { | ||
| 534 | return Poll::Ready(Err(Error::Nack)); | ||
| 535 | } | ||
| 536 | if isr.arlo() { | ||
| 537 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 538 | } | ||
| 539 | if isr.berr() { | ||
| 540 | return Poll::Ready(Err(Error::Bus)); | ||
| 541 | } | ||
| 542 | if isr.ovr() { | ||
| 543 | return Poll::Ready(Err(Error::Overrun)); | ||
| 544 | } | ||
| 545 | |||
| 519 | if remaining_len == total_len { | 546 | if remaining_len == total_len { |
| 520 | if first_slice { | 547 | if first_slice { |
| 521 | Self::master_write( | 548 | Self::master_write( |
| @@ -550,10 +577,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 550 | .await?; | 577 | .await?; |
| 551 | 578 | ||
| 552 | dma_transfer.await; | 579 | dma_transfer.await; |
| 553 | |||
| 554 | if last_slice { | 580 | if last_slice { |
| 555 | // This should be done already | 581 | // This should be done already |
| 556 | self.wait_tc(timeout)?; | 582 | self.wait_tc(timeout)?; |
| 583 | } | ||
| 584 | |||
| 585 | if last_slice & send_stop { | ||
| 557 | self.master_stop(); | 586 | self.master_stop(); |
| 558 | } | 587 | } |
| 559 | 588 | ||
| @@ -576,6 +605,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 576 | regs.cr1().modify(|w| { | 605 | regs.cr1().modify(|w| { |
| 577 | w.set_rxdmaen(true); | 606 | w.set_rxdmaen(true); |
| 578 | w.set_tcie(true); | 607 | w.set_tcie(true); |
| 608 | w.set_nackie(true); | ||
| 609 | w.set_errie(true); | ||
| 579 | }); | 610 | }); |
| 580 | let src = regs.rxdr().as_ptr() as *mut u8; | 611 | let src = regs.rxdr().as_ptr() as *mut u8; |
| 581 | 612 | ||
| @@ -589,34 +620,62 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 589 | regs.cr1().modify(|w| { | 620 | regs.cr1().modify(|w| { |
| 590 | w.set_rxdmaen(false); | 621 | w.set_rxdmaen(false); |
| 591 | w.set_tcie(false); | 622 | w.set_tcie(false); |
| 592 | }) | 623 | w.set_nackie(false); |
| 624 | w.set_errie(false); | ||
| 625 | }); | ||
| 626 | regs.icr().write(|w| { | ||
| 627 | w.set_nackcf(true); | ||
| 628 | w.set_berrcf(true); | ||
| 629 | w.set_arlocf(true); | ||
| 630 | w.set_ovrcf(true); | ||
| 631 | }); | ||
| 593 | }); | 632 | }); |
| 594 | 633 | ||
| 595 | poll_fn(|cx| { | 634 | poll_fn(|cx| { |
| 596 | self.state.waker.register(cx.waker()); | 635 | self.state.waker.register(cx.waker()); |
| 597 | 636 | ||
| 598 | let isr = self.info.regs.isr().read(); | 637 | let isr = self.info.regs.isr().read(); |
| 638 | |||
| 639 | if isr.nackf() { | ||
| 640 | return Poll::Ready(Err(Error::Nack)); | ||
| 641 | } | ||
| 642 | if isr.arlo() { | ||
| 643 | return Poll::Ready(Err(Error::Arbitration)); | ||
| 644 | } | ||
| 645 | if isr.berr() { | ||
| 646 | return Poll::Ready(Err(Error::Bus)); | ||
| 647 | } | ||
| 648 | if isr.ovr() { | ||
| 649 | return Poll::Ready(Err(Error::Overrun)); | ||
| 650 | } | ||
| 651 | |||
| 599 | if remaining_len == total_len { | 652 | if remaining_len == total_len { |
| 600 | Self::master_read( | 653 | Self::master_read( |
| 601 | self.info, | 654 | self.info, |
| 602 | address, | 655 | address, |
| 603 | total_len.min(255), | 656 | total_len.min(255), |
| 604 | Stop::Software, | 657 | Stop::Automatic, |
| 605 | total_len > 255, | 658 | total_len > 255, |
| 606 | restart, | 659 | restart, |
| 607 | timeout, | 660 | timeout, |
| 608 | )?; | 661 | )?; |
| 609 | } else if !(isr.tcr() || isr.tc()) { | 662 | if total_len <= 255 { |
| 663 | return Poll::Ready(Ok(())); | ||
| 664 | } | ||
| 665 | } else if isr.tcr() { | ||
| 610 | // poll_fn was woken without an interrupt present | 666 | // poll_fn was woken without an interrupt present |
| 611 | return Poll::Pending; | 667 | return Poll::Pending; |
| 612 | } else if remaining_len == 0 { | ||
| 613 | return Poll::Ready(Ok(())); | ||
| 614 | } else { | 668 | } else { |
| 615 | let last_piece = remaining_len <= 255; | 669 | let last_piece = remaining_len <= 255; |
| 616 | 670 | ||
| 617 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { | 671 | if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { |
| 618 | return Poll::Ready(Err(e)); | 672 | return Poll::Ready(Err(e)); |
| 619 | } | 673 | } |
| 674 | // Return here if we are on last chunk, | ||
| 675 | // end of transfer will be awaited with the DMA below | ||
| 676 | if last_piece { | ||
| 677 | return Poll::Ready(Ok(())); | ||
| 678 | } | ||
| 620 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); | 679 | self.info.regs.cr1().modify(|w| w.set_tcie(true)); |
| 621 | } | 680 | } |
| 622 | 681 | ||
| @@ -626,11 +685,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 626 | .await?; | 685 | .await?; |
| 627 | 686 | ||
| 628 | dma_transfer.await; | 687 | dma_transfer.await; |
| 629 | |||
| 630 | // This should be done already | ||
| 631 | self.wait_tc(timeout)?; | ||
| 632 | self.master_stop(); | ||
| 633 | |||
| 634 | drop(on_drop); | 688 | drop(on_drop); |
| 635 | 689 | ||
| 636 | Ok(()) | 690 | Ok(()) |
| @@ -645,7 +699,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 645 | self.write_internal(address.into(), write, true, timeout) | 699 | self.write_internal(address.into(), write, true, timeout) |
| 646 | } else { | 700 | } else { |
| 647 | timeout | 701 | timeout |
| 648 | .with(self.write_dma_internal(address.into(), write, true, true, timeout)) | 702 | .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) |
| 649 | .await | 703 | .await |
| 650 | } | 704 | } |
| 651 | } | 705 | } |
| @@ -667,7 +721,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 667 | let next = iter.next(); | 721 | let next = iter.next(); |
| 668 | let is_last = next.is_none(); | 722 | let is_last = next.is_none(); |
| 669 | 723 | ||
| 670 | let fut = self.write_dma_internal(address, c, first, is_last, timeout); | 724 | let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); |
| 671 | timeout.with(fut).await?; | 725 | timeout.with(fut).await?; |
| 672 | first = false; | 726 | first = false; |
| 673 | current = next; | 727 | current = next; |
| @@ -694,7 +748,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { | |||
| 694 | if write.is_empty() { | 748 | if write.is_empty() { |
| 695 | self.write_internal(address.into(), write, false, timeout)?; | 749 | self.write_internal(address.into(), write, false, timeout)?; |
| 696 | } else { | 750 | } else { |
| 697 | let fut = self.write_dma_internal(address.into(), write, true, true, timeout); | 751 | let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); |
| 698 | timeout.with(fut).await?; | 752 | timeout.with(fut).await?; |
| 699 | } | 753 | } |
| 700 | 754 | ||
| @@ -1202,7 +1256,12 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> { | |||
| 1202 | type Config = Hertz; | 1256 | type Config = Hertz; |
| 1203 | type ConfigError = (); | 1257 | type ConfigError = (); |
| 1204 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1258 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
| 1259 | self.info.regs.cr1().modify(|reg| { | ||
| 1260 | reg.set_pe(false); | ||
| 1261 | }); | ||
| 1262 | |||
| 1205 | let timings = Timings::new(self.kernel_clock, *config); | 1263 | let timings = Timings::new(self.kernel_clock, *config); |
| 1264 | |||
| 1206 | self.info.regs.timingr().write(|reg| { | 1265 | self.info.regs.timingr().write(|reg| { |
| 1207 | reg.set_presc(timings.prescale); | 1266 | reg.set_presc(timings.prescale); |
| 1208 | reg.set_scll(timings.scll); | 1267 | reg.set_scll(timings.scll); |
| @@ -1211,6 +1270,10 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> { | |||
| 1211 | reg.set_scldel(timings.scldel); | 1270 | reg.set_scldel(timings.scldel); |
| 1212 | }); | 1271 | }); |
| 1213 | 1272 | ||
| 1273 | self.info.regs.cr1().modify(|reg| { | ||
| 1274 | reg.set_pe(true); | ||
| 1275 | }); | ||
| 1276 | |||
| 1214 | Ok(()) | 1277 | Ok(()) |
| 1215 | } | 1278 | } |
| 1216 | } | 1279 | } |
| @@ -1232,3 +1295,4 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> { | |||
| 1232 | Ok(()) | 1295 | Ok(()) |
| 1233 | } | 1296 | } |
| 1234 | } | 1297 | } |
| 1298 | |||
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 094de2461..5005a5cdb 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs | |||
| @@ -1,14 +1,14 @@ | |||
| 1 | //! Inter-IC Sound (I2S) | 1 | //! Inter-IC Sound (I2S) |
| 2 | 2 | ||
| 3 | use embassy_hal_internal::into_ref; | 3 | use embassy_futures::join::join; |
| 4 | use stm32_metapac::spi::vals; | ||
| 4 | 5 | ||
| 5 | use crate::dma::ChannelAndRequest; | 6 | use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer}; |
| 6 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 7 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| 7 | use crate::mode::Async; | 8 | use crate::mode::Async; |
| 8 | use crate::pac::spi::vals; | 9 | use crate::spi::{Config as SpiConfig, RegsExt as _, *}; |
| 9 | use crate::spi::{Config as SpiConfig, *}; | ||
| 10 | use crate::time::Hertz; | 10 | use crate::time::Hertz; |
| 11 | use crate::{Peripheral, PeripheralRef}; | 11 | use crate::Peri; |
| 12 | 12 | ||
| 13 | /// I2S mode | 13 | /// I2S mode |
| 14 | #[derive(Copy, Clone)] | 14 | #[derive(Copy, Clone)] |
| @@ -19,6 +19,19 @@ pub enum Mode { | |||
| 19 | Slave, | 19 | Slave, |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /// I2S function | ||
| 23 | #[derive(Copy, Clone)] | ||
| 24 | #[allow(dead_code)] | ||
| 25 | enum Function { | ||
| 26 | /// Transmit audio data | ||
| 27 | Transmit, | ||
| 28 | /// Receive audio data | ||
| 29 | Receive, | ||
| 30 | #[cfg(spi_v3)] | ||
| 31 | /// Transmit and Receive audio data | ||
| 32 | FullDuplex, | ||
| 33 | } | ||
| 34 | |||
| 22 | /// I2C standard | 35 | /// I2C standard |
| 23 | #[derive(Copy, Clone)] | 36 | #[derive(Copy, Clone)] |
| 24 | pub enum Standard { | 37 | pub enum Standard { |
| @@ -34,6 +47,30 @@ pub enum Standard { | |||
| 34 | PcmShortSync, | 47 | PcmShortSync, |
| 35 | } | 48 | } |
| 36 | 49 | ||
| 50 | /// SAI error | ||
| 51 | #[derive(Debug, PartialEq, Eq)] | ||
| 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 53 | pub enum Error { | ||
| 54 | /// `write` called on a SAI in receive mode. | ||
| 55 | NotATransmitter, | ||
| 56 | /// `read` called on a SAI in transmit mode. | ||
| 57 | NotAReceiver, | ||
| 58 | /// Overrun | ||
| 59 | Overrun, | ||
| 60 | } | ||
| 61 | |||
| 62 | impl From<ringbuffer::Error> for Error { | ||
| 63 | fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { | ||
| 64 | #[cfg(feature = "defmt")] | ||
| 65 | { | ||
| 66 | if err == ringbuffer::Error::DmaUnsynced { | ||
| 67 | defmt::error!("Ringbuffer broken invariants detected!"); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | Self::Overrun | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 37 | impl Standard { | 74 | impl Standard { |
| 38 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | 75 | #[cfg(any(spi_v1, spi_v3, spi_f1))] |
| 39 | const fn i2sstd(&self) -> vals::I2sstd { | 76 | const fn i2sstd(&self) -> vals::I2sstd { |
| @@ -103,8 +140,8 @@ impl ClockPolarity { | |||
| 103 | #[cfg(any(spi_v1, spi_v3, spi_f1))] | 140 | #[cfg(any(spi_v1, spi_v3, spi_f1))] |
| 104 | const fn ckpol(&self) -> vals::Ckpol { | 141 | const fn ckpol(&self) -> vals::Ckpol { |
| 105 | match self { | 142 | match self { |
| 106 | ClockPolarity::IdleHigh => vals::Ckpol::IDLEHIGH, | 143 | ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH, |
| 107 | ClockPolarity::IdleLow => vals::Ckpol::IDLELOW, | 144 | ClockPolarity::IdleLow => vals::Ckpol::IDLE_LOW, |
| 108 | } | 145 | } |
| 109 | } | 146 | } |
| 110 | } | 147 | } |
| @@ -142,50 +179,107 @@ impl Default for Config { | |||
| 142 | } | 179 | } |
| 143 | } | 180 | } |
| 144 | 181 | ||
| 145 | /// I2S driver. | 182 | /// I2S driver writer. Useful for moving write functionality across tasks. |
| 146 | pub struct I2S<'d> { | 183 | pub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>); |
| 147 | _peri: Spi<'d, Async>, | 184 | |
| 148 | txsd: Option<PeripheralRef<'d, AnyPin>>, | 185 | impl<'s, 'd, W: Word> Writer<'s, 'd, W> { |
| 149 | rxsd: Option<PeripheralRef<'d, AnyPin>>, | 186 | /// Write data to the I2S ringbuffer. |
| 150 | ws: Option<PeripheralRef<'d, AnyPin>>, | 187 | /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background. |
| 151 | ck: Option<PeripheralRef<'d, AnyPin>>, | 188 | /// If thfre’s no space in the buffer, this waits until there is. |
| 152 | mck: Option<PeripheralRef<'d, AnyPin>>, | 189 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { |
| 190 | self.0.write_exact(data).await?; | ||
| 191 | Ok(()) | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Reset the ring buffer to its initial state. | ||
| 195 | /// Can be used to recover from overrun. | ||
| 196 | /// The ringbuffer will always auto-reset on Overrun in any case. | ||
| 197 | pub fn reset(&mut self) { | ||
| 198 | self.0.clear(); | ||
| 199 | } | ||
| 153 | } | 200 | } |
| 154 | 201 | ||
| 155 | /// I2S function | 202 | /// I2S driver reader. Useful for moving read functionality across tasks. |
| 156 | #[derive(Copy, Clone)] | 203 | pub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>); |
| 157 | #[allow(dead_code)] | 204 | |
| 158 | enum Function { | 205 | impl<'s, 'd, W: Word> Reader<'s, 'd, W> { |
| 159 | /// Transmit audio data | 206 | /// Read data from the I2S ringbuffer. |
| 160 | Transmit, | 207 | /// SAI is always receiving data in the background. This function pops already-received data from the buffer. |
| 161 | /// Receive audio data | 208 | /// If there’s less than data.len() data in the buffer, this waits until there is. |
| 162 | Receive, | 209 | pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 163 | #[cfg(spi_v3)] | 210 | self.0.read_exact(data).await?; |
| 164 | /// Transmit and Receive audio data | 211 | Ok(()) |
| 165 | FullDuplex, | 212 | } |
| 213 | |||
| 214 | /// Reset the ring buffer to its initial state. | ||
| 215 | /// Can be used to prevent overrun. | ||
| 216 | /// The ringbuffer will always auto-reset on Overrun in any case. | ||
| 217 | pub fn reset(&mut self) { | ||
| 218 | self.0.clear(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | /// I2S driver. | ||
| 223 | pub struct I2S<'d, W: Word> { | ||
| 224 | #[allow(dead_code)] | ||
| 225 | mode: Mode, | ||
| 226 | spi: Spi<'d, Async>, | ||
| 227 | txsd: Option<Peri<'d, AnyPin>>, | ||
| 228 | rxsd: Option<Peri<'d, AnyPin>>, | ||
| 229 | ws: Option<Peri<'d, AnyPin>>, | ||
| 230 | ck: Option<Peri<'d, AnyPin>>, | ||
| 231 | mck: Option<Peri<'d, AnyPin>>, | ||
| 232 | tx_ring_buffer: Option<WritableRingBuffer<'d, W>>, | ||
| 233 | rx_ring_buffer: Option<ReadableRingBuffer<'d, W>>, | ||
| 166 | } | 234 | } |
| 167 | 235 | ||
| 168 | impl<'d> I2S<'d> { | 236 | impl<'d, W: Word> I2S<'d, W> { |
| 169 | /// Create a transmitter driver | 237 | /// Create a transmitter driver. |
| 170 | pub fn new_txonly<T: Instance>( | 238 | pub fn new_txonly<T: Instance>( |
| 171 | peri: impl Peripheral<P = T> + 'd, | 239 | peri: Peri<'d, T>, |
| 172 | sd: impl Peripheral<P = impl MosiPin<T>> + 'd, | 240 | sd: Peri<'d, impl MosiPin<T>>, |
| 173 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | 241 | ws: Peri<'d, impl WsPin<T>>, |
| 174 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 242 | ck: Peri<'d, impl CkPin<T>>, |
| 175 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 243 | mck: Peri<'d, impl MckPin<T>>, |
| 176 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | 244 | txdma: Peri<'d, impl TxDma<T>>, |
| 245 | txdma_buf: &'d mut [W], | ||
| 246 | freq: Hertz, | ||
| 247 | config: Config, | ||
| 248 | ) -> Self { | ||
| 249 | Self::new_inner( | ||
| 250 | peri, | ||
| 251 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 252 | None, | ||
| 253 | ws, | ||
| 254 | ck, | ||
| 255 | new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 256 | new_dma!(txdma).map(|d| (d, txdma_buf)), | ||
| 257 | None, | ||
| 258 | freq, | ||
| 259 | config, | ||
| 260 | Function::Transmit, | ||
| 261 | ) | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Create a transmitter driver without a master clock pin. | ||
| 265 | pub fn new_txonly_nomck<T: Instance>( | ||
| 266 | peri: Peri<'d, T>, | ||
| 267 | sd: Peri<'d, impl MosiPin<T>>, | ||
| 268 | ws: Peri<'d, impl WsPin<T>>, | ||
| 269 | ck: Peri<'d, impl CkPin<T>>, | ||
| 270 | txdma: Peri<'d, impl TxDma<T>>, | ||
| 271 | txdma_buf: &'d mut [W], | ||
| 177 | freq: Hertz, | 272 | freq: Hertz, |
| 178 | config: Config, | 273 | config: Config, |
| 179 | ) -> Self { | 274 | ) -> Self { |
| 180 | into_ref!(sd); | ||
| 181 | Self::new_inner( | 275 | Self::new_inner( |
| 182 | peri, | 276 | peri, |
| 183 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 277 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 184 | None, | 278 | None, |
| 185 | ws, | 279 | ws, |
| 186 | ck, | 280 | ck, |
| 187 | mck, | 281 | None, |
| 188 | new_dma!(txdma), | 282 | new_dma!(txdma).map(|d| (d, txdma_buf)), |
| 189 | None, | 283 | None, |
| 190 | freq, | 284 | freq, |
| 191 | config, | 285 | config, |
| @@ -193,120 +287,210 @@ impl<'d> I2S<'d> { | |||
| 193 | ) | 287 | ) |
| 194 | } | 288 | } |
| 195 | 289 | ||
| 196 | /// Create a receiver driver | 290 | /// Create a receiver driver. |
| 197 | pub fn new_rxonly<T: Instance>( | 291 | pub fn new_rxonly<T: Instance>( |
| 198 | peri: impl Peripheral<P = T> + 'd, | 292 | peri: Peri<'d, T>, |
| 199 | sd: impl Peripheral<P = impl MisoPin<T>> + 'd, | 293 | sd: Peri<'d, impl MisoPin<T>>, |
| 200 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | 294 | ws: Peri<'d, impl WsPin<T>>, |
| 201 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 295 | ck: Peri<'d, impl CkPin<T>>, |
| 202 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 296 | mck: Peri<'d, impl MckPin<T>>, |
| 203 | #[cfg(not(spi_v3))] txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | 297 | rxdma: Peri<'d, impl RxDma<T>>, |
| 204 | rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, | 298 | rxdma_buf: &'d mut [W], |
| 205 | freq: Hertz, | 299 | freq: Hertz, |
| 206 | config: Config, | 300 | config: Config, |
| 207 | ) -> Self { | 301 | ) -> Self { |
| 208 | into_ref!(sd); | ||
| 209 | Self::new_inner( | 302 | Self::new_inner( |
| 210 | peri, | 303 | peri, |
| 211 | None, | 304 | None, |
| 212 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 305 | new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 213 | ws, | 306 | ws, |
| 214 | ck, | 307 | ck, |
| 215 | mck, | 308 | new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 216 | #[cfg(not(spi_v3))] | ||
| 217 | new_dma!(txdma), | ||
| 218 | #[cfg(spi_v3)] | ||
| 219 | None, | 309 | None, |
| 220 | new_dma!(rxdma), | 310 | new_dma!(rxdma).map(|d| (d, rxdma_buf)), |
| 221 | freq, | 311 | freq, |
| 222 | config, | 312 | config, |
| 223 | #[cfg(not(spi_v3))] | ||
| 224 | Function::Transmit, | ||
| 225 | #[cfg(spi_v3)] | ||
| 226 | Function::Receive, | 313 | Function::Receive, |
| 227 | ) | 314 | ) |
| 228 | } | 315 | } |
| 229 | 316 | ||
| 230 | #[cfg(spi_v3)] | 317 | #[cfg(spi_v3)] |
| 231 | /// Create a full duplex transmitter driver | 318 | /// Create a full duplex driver. |
| 232 | pub fn new_full_duplex<T: Instance>( | 319 | pub fn new_full_duplex<T: Instance>( |
| 233 | peri: impl Peripheral<P = T> + 'd, | 320 | peri: Peri<'d, T>, |
| 234 | txsd: impl Peripheral<P = impl MosiPin<T>> + 'd, | 321 | txsd: Peri<'d, impl MosiPin<T>>, |
| 235 | rxsd: impl Peripheral<P = impl MisoPin<T>> + 'd, | 322 | rxsd: Peri<'d, impl MisoPin<T>>, |
| 236 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | 323 | ws: Peri<'d, impl WsPin<T>>, |
| 237 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 324 | ck: Peri<'d, impl CkPin<T>>, |
| 238 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 325 | mck: Peri<'d, impl MckPin<T>>, |
| 239 | txdma: impl Peripheral<P = impl TxDma<T>> + 'd, | 326 | txdma: Peri<'d, impl TxDma<T>>, |
| 240 | rxdma: impl Peripheral<P = impl RxDma<T>> + 'd, | 327 | txdma_buf: &'d mut [W], |
| 328 | rxdma: Peri<'d, impl RxDma<T>>, | ||
| 329 | rxdma_buf: &'d mut [W], | ||
| 241 | freq: Hertz, | 330 | freq: Hertz, |
| 242 | config: Config, | 331 | config: Config, |
| 243 | ) -> Self { | 332 | ) -> Self { |
| 244 | into_ref!(txsd, rxsd); | ||
| 245 | Self::new_inner( | 333 | Self::new_inner( |
| 246 | peri, | 334 | peri, |
| 247 | new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 335 | new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 248 | new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 336 | new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 249 | ws, | 337 | ws, |
| 250 | ck, | 338 | ck, |
| 251 | mck, | 339 | new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), |
| 252 | new_dma!(txdma), | 340 | new_dma!(txdma).map(|d| (d, txdma_buf)), |
| 253 | new_dma!(rxdma), | 341 | new_dma!(rxdma).map(|d| (d, rxdma_buf)), |
| 254 | freq, | 342 | freq, |
| 255 | config, | 343 | config, |
| 256 | Function::FullDuplex, | 344 | Function::FullDuplex, |
| 257 | ) | 345 | ) |
| 258 | } | 346 | } |
| 259 | 347 | ||
| 260 | /// Write audio data. | 348 | /// Start I2S driver. |
| 261 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 349 | pub fn start(&mut self) { |
| 262 | self._peri.read(data).await | 350 | self.spi.info.regs.cr1().modify(|w| { |
| 351 | w.set_spe(false); | ||
| 352 | }); | ||
| 353 | self.spi.set_word_size(W::CONFIG); | ||
| 354 | if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { | ||
| 355 | tx_ring_buffer.start(); | ||
| 356 | |||
| 357 | set_txdmaen(self.spi.info.regs, true); | ||
| 358 | } | ||
| 359 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | ||
| 360 | rx_ring_buffer.start(); | ||
| 361 | // SPIv3 clears rxfifo on SPE=0 | ||
| 362 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | ||
| 363 | flush_rx_fifo(self.spi.info.regs); | ||
| 364 | |||
| 365 | set_rxdmaen(self.spi.info.regs, true); | ||
| 366 | } | ||
| 367 | self.spi.info.regs.cr1().modify(|w| { | ||
| 368 | w.set_spe(true); | ||
| 369 | }); | ||
| 370 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | ||
| 371 | self.spi.info.regs.cr1().modify(|w| { | ||
| 372 | w.set_cstart(true); | ||
| 373 | }); | ||
| 263 | } | 374 | } |
| 264 | 375 | ||
| 265 | /// Write audio data. | 376 | /// Reset the ring buffer to its initial state. |
| 266 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 377 | /// Can be used to recover from overrun. |
| 267 | self._peri.write(data).await | 378 | pub fn clear(&mut self) { |
| 379 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | ||
| 380 | rx_ring_buffer.clear(); | ||
| 381 | } | ||
| 382 | if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { | ||
| 383 | tx_ring_buffer.clear(); | ||
| 384 | } | ||
| 268 | } | 385 | } |
| 269 | 386 | ||
| 270 | /// Transfer audio data. | 387 | /// Stop I2S driver. |
| 271 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 388 | pub async fn stop(&mut self) { |
| 272 | self._peri.transfer(read, write).await | 389 | let regs = self.spi.info.regs; |
| 390 | |||
| 391 | let tx_f = async { | ||
| 392 | if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { | ||
| 393 | tx_ring_buffer.stop().await; | ||
| 394 | |||
| 395 | set_txdmaen(regs, false); | ||
| 396 | } | ||
| 397 | }; | ||
| 398 | |||
| 399 | let rx_f = async { | ||
| 400 | if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { | ||
| 401 | rx_ring_buffer.stop().await; | ||
| 402 | |||
| 403 | set_rxdmaen(regs, false); | ||
| 404 | } | ||
| 405 | }; | ||
| 406 | |||
| 407 | join(rx_f, tx_f).await; | ||
| 408 | |||
| 409 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | ||
| 410 | { | ||
| 411 | if let Mode::Master = self.mode { | ||
| 412 | regs.cr1().modify(|w| { | ||
| 413 | w.set_csusp(true); | ||
| 414 | }); | ||
| 415 | |||
| 416 | while regs.cr1().read().cstart() {} | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | regs.cr1().modify(|w| { | ||
| 421 | w.set_spe(false); | ||
| 422 | }); | ||
| 423 | |||
| 424 | self.clear(); | ||
| 425 | } | ||
| 426 | |||
| 427 | /// Split the driver into a Reader/Writer pair. | ||
| 428 | /// Useful for splitting the reader/writer functionality across tasks or | ||
| 429 | /// for calling the read/write methods in parallel. | ||
| 430 | pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> { | ||
| 431 | match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) { | ||
| 432 | (None, _) => Err(Error::NotAReceiver), | ||
| 433 | (_, None) => Err(Error::NotATransmitter), | ||
| 434 | (Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))), | ||
| 435 | } | ||
| 273 | } | 436 | } |
| 274 | 437 | ||
| 275 | /// Transfer audio data in place. | 438 | /// Read data from the I2S ringbuffer. |
| 276 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 439 | /// SAI is always receiving data in the background. This function pops already-received data from the buffer. |
| 277 | self._peri.transfer_in_place(data).await | 440 | /// If there’s less than data.len() data in the buffer, this waits until there is. |
| 441 | pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { | ||
| 442 | match &mut self.rx_ring_buffer { | ||
| 443 | Some(ring) => Reader(ring).read(data).await, | ||
| 444 | _ => Err(Error::NotAReceiver), | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | /// Write data to the I2S ringbuffer. | ||
| 449 | /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background. | ||
| 450 | /// If thfre’s no space in the buffer, this waits until there is. | ||
| 451 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { | ||
| 452 | match &mut self.tx_ring_buffer { | ||
| 453 | Some(ring) => Writer(ring).write(data).await, | ||
| 454 | _ => Err(Error::NotATransmitter), | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | /// Write data directly to the raw I2S ringbuffer. | ||
| 459 | /// This can be used to fill the buffer before starting the DMA transfer. | ||
| 460 | pub async fn write_immediate(&mut self, data: &[W]) -> Result<(usize, usize), Error> { | ||
| 461 | match &mut self.tx_ring_buffer { | ||
| 462 | Some(ring) => Ok(ring.write_immediate(data)?), | ||
| 463 | _ => return Err(Error::NotATransmitter), | ||
| 464 | } | ||
| 278 | } | 465 | } |
| 279 | 466 | ||
| 280 | fn new_inner<T: Instance>( | 467 | fn new_inner<T: Instance>( |
| 281 | peri: impl Peripheral<P = T> + 'd, | 468 | peri: Peri<'d, T>, |
| 282 | txsd: Option<PeripheralRef<'d, AnyPin>>, | 469 | txsd: Option<Peri<'d, AnyPin>>, |
| 283 | rxsd: Option<PeripheralRef<'d, AnyPin>>, | 470 | rxsd: Option<Peri<'d, AnyPin>>, |
| 284 | ws: impl Peripheral<P = impl WsPin<T>> + 'd, | 471 | ws: Peri<'d, impl WsPin<T>>, |
| 285 | ck: impl Peripheral<P = impl CkPin<T>> + 'd, | 472 | ck: Peri<'d, impl CkPin<T>>, |
| 286 | mck: impl Peripheral<P = impl MckPin<T>> + 'd, | 473 | mck: Option<Peri<'d, AnyPin>>, |
| 287 | txdma: Option<ChannelAndRequest<'d>>, | 474 | txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, |
| 288 | rxdma: Option<ChannelAndRequest<'d>>, | 475 | rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, |
| 289 | freq: Hertz, | 476 | freq: Hertz, |
| 290 | config: Config, | 477 | config: Config, |
| 291 | function: Function, | 478 | function: Function, |
| 292 | ) -> Self { | 479 | ) -> Self { |
| 293 | into_ref!(ws, ck, mck); | ||
| 294 | |||
| 295 | ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 480 | ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 296 | ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 481 | ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 297 | mck.set_as_af(mck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 298 | 482 | ||
| 299 | let mut spi_cfg = SpiConfig::default(); | 483 | let spi = Spi::new_internal(peri, None, None, { |
| 300 | spi_cfg.frequency = freq; | 484 | let mut config = SpiConfig::default(); |
| 301 | 485 | config.frequency = freq; | |
| 302 | let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); | 486 | config |
| 487 | }); | ||
| 303 | 488 | ||
| 304 | let regs = T::info().regs; | 489 | let regs = T::info().regs; |
| 305 | 490 | ||
| 306 | // TODO move i2s to the new mux infra. | 491 | #[cfg(all(rcc_f4, not(stm32f410)))] |
| 307 | //#[cfg(all(rcc_f4, not(stm32f410)))] | 492 | let pclk = unsafe { crate::rcc::get_freqs() }.plli2s1_r.to_hertz().unwrap(); |
| 308 | //let pclk = unsafe { get_freqs() }.plli2s1_q.unwrap(); | 493 | #[cfg(not(all(rcc_f4, not(stm32f410))))] |
| 309 | //#[cfg(stm32f410)] | ||
| 310 | let pclk = T::frequency(); | 494 | let pclk = T::frequency(); |
| 311 | 495 | ||
| 312 | let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format); | 496 | let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format); |
| @@ -376,36 +560,43 @@ impl<'d> I2S<'d> { | |||
| 376 | w.set_chlen(config.format.chlen()); | 560 | w.set_chlen(config.format.chlen()); |
| 377 | 561 | ||
| 378 | w.set_i2scfg(match (config.mode, function) { | 562 | w.set_i2scfg(match (config.mode, function) { |
| 379 | (Mode::Master, Function::Transmit) => I2scfg::MASTERTX, | 563 | (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX, |
| 380 | (Mode::Master, Function::Receive) => I2scfg::MASTERRX, | 564 | (Mode::Master, Function::Receive) => I2scfg::MASTER_RX, |
| 381 | #[cfg(spi_v3)] | 565 | #[cfg(spi_v3)] |
| 382 | (Mode::Master, Function::FullDuplex) => I2scfg::MASTERFULLDUPLEX, | 566 | (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX, |
| 383 | (Mode::Slave, Function::Transmit) => I2scfg::SLAVETX, | 567 | (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX, |
| 384 | (Mode::Slave, Function::Receive) => I2scfg::SLAVERX, | 568 | (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX, |
| 385 | #[cfg(spi_v3)] | 569 | #[cfg(spi_v3)] |
| 386 | (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVEFULLDUPLEX, | 570 | (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX, |
| 387 | }); | 571 | }); |
| 388 | 572 | ||
| 389 | #[cfg(any(spi_v1, spi_f1))] | 573 | #[cfg(any(spi_v1, spi_f1))] |
| 390 | w.set_i2se(true); | 574 | w.set_i2se(true); |
| 391 | }); | 575 | }); |
| 392 | 576 | ||
| 393 | #[cfg(spi_v3)] | 577 | let mut opts = TransferOptions::default(); |
| 394 | regs.cr1().modify(|w| w.set_spe(true)); | 578 | opts.half_transfer_ir = true; |
| 395 | } | 579 | |
| 396 | 580 | Self { | |
| 397 | Self { | 581 | mode: config.mode, |
| 398 | _peri: spi, | 582 | spi, |
| 399 | txsd: txsd.map(|w| w.map_into()), | 583 | txsd: txsd.map(|w| w.into()), |
| 400 | rxsd: rxsd.map(|w| w.map_into()), | 584 | rxsd: rxsd.map(|w| w.into()), |
| 401 | ws: Some(ws.map_into()), | 585 | ws: Some(ws.into()), |
| 402 | ck: Some(ck.map_into()), | 586 | ck: Some(ck.into()), |
| 403 | mck: Some(mck.map_into()), | 587 | mck: mck.map(|w| w.into()), |
| 588 | tx_ring_buffer: txdma.map(|(ch, buf)| unsafe { | ||
| 589 | WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) | ||
| 590 | }), | ||
| 591 | rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe { | ||
| 592 | ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) | ||
| 593 | }), | ||
| 594 | } | ||
| 404 | } | 595 | } |
| 405 | } | 596 | } |
| 406 | } | 597 | } |
| 407 | 598 | ||
| 408 | impl<'d> Drop for I2S<'d> { | 599 | impl<'d, W: Word> Drop for I2S<'d, W> { |
| 409 | fn drop(&mut self) { | 600 | fn drop(&mut self) { |
| 410 | self.txsd.as_ref().map(|x| x.set_as_disconnected()); | 601 | self.txsd.as_ref().map(|x| x.set_as_disconnected()); |
| 411 | self.rxsd.as_ref().map(|x| x.set_as_disconnected()); | 602 | self.rxsd.as_ref().map(|x| x.set_as_disconnected()); |
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 6c8347311..20cd20dca 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs | |||
| @@ -229,11 +229,9 @@ struct State { | |||
| 229 | 229 | ||
| 230 | impl State { | 230 | impl State { |
| 231 | const fn new() -> Self { | 231 | const fn new() -> Self { |
| 232 | const WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 233 | |||
| 234 | Self { | 232 | Self { |
| 235 | rx_wakers: [WAKER; 6], | 233 | rx_wakers: [const { AtomicWaker::new() }; 6], |
| 236 | tx_wakers: [WAKER; 6], | 234 | tx_wakers: [const { AtomicWaker::new() }; 6], |
| 237 | } | 235 | } |
| 238 | } | 236 | } |
| 239 | 237 | ||
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 12ebbae2d..973acc9bb 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -68,6 +68,8 @@ pub mod dac; | |||
| 68 | pub mod dcmi; | 68 | pub mod dcmi; |
| 69 | #[cfg(dsihost)] | 69 | #[cfg(dsihost)] |
| 70 | pub mod dsihost; | 70 | pub mod dsihost; |
| 71 | #[cfg(dts)] | ||
| 72 | pub mod dts; | ||
| 71 | #[cfg(eth)] | 73 | #[cfg(eth)] |
| 72 | pub mod eth; | 74 | pub mod eth; |
| 73 | #[cfg(feature = "exti")] | 75 | #[cfg(feature = "exti")] |
| @@ -81,6 +83,8 @@ pub mod hash; | |||
| 81 | pub mod hrtim; | 83 | pub mod hrtim; |
| 82 | #[cfg(hsem)] | 84 | #[cfg(hsem)] |
| 83 | pub mod hsem; | 85 | pub mod hsem; |
| 86 | #[cfg(hspi)] | ||
| 87 | pub mod hspi; | ||
| 84 | #[cfg(i2c)] | 88 | #[cfg(i2c)] |
| 85 | pub mod i2c; | 89 | pub mod i2c; |
| 86 | #[cfg(any(all(spi_v1, rcc_f4), spi_v3))] | 90 | #[cfg(any(all(spi_v1, rcc_f4), spi_v3))] |
| @@ -89,6 +93,8 @@ pub mod i2s; | |||
| 89 | pub mod ipcc; | 93 | pub mod ipcc; |
| 90 | #[cfg(feature = "low-power")] | 94 | #[cfg(feature = "low-power")] |
| 91 | pub mod low_power; | 95 | pub mod low_power; |
| 96 | #[cfg(lptim)] | ||
| 97 | pub mod lptim; | ||
| 92 | #[cfg(ltdc)] | 98 | #[cfg(ltdc)] |
| 93 | pub mod ltdc; | 99 | pub mod ltdc; |
| 94 | #[cfg(opamp)] | 100 | #[cfg(opamp)] |
| @@ -105,6 +111,8 @@ pub mod rtc; | |||
| 105 | pub mod sai; | 111 | pub mod sai; |
| 106 | #[cfg(sdmmc)] | 112 | #[cfg(sdmmc)] |
| 107 | pub mod sdmmc; | 113 | pub mod sdmmc; |
| 114 | #[cfg(spdifrx)] | ||
| 115 | pub mod spdifrx; | ||
| 108 | #[cfg(spi)] | 116 | #[cfg(spi)] |
| 109 | pub mod spi; | 117 | pub mod spi; |
| 110 | #[cfg(tsc)] | 118 | #[cfg(tsc)] |
| @@ -119,6 +127,8 @@ pub mod usart; | |||
| 119 | pub mod usb; | 127 | pub mod usb; |
| 120 | #[cfg(iwdg)] | 128 | #[cfg(iwdg)] |
| 121 | pub mod wdg; | 129 | pub mod wdg; |
| 130 | #[cfg(xspi)] | ||
| 131 | pub mod xspi; | ||
| 122 | 132 | ||
| 123 | // This must go last, so that it sees all the impl_foo! macros defined earlier. | 133 | // This must go last, so that it sees all the impl_foo! macros defined earlier. |
| 124 | pub(crate) mod _generated { | 134 | pub(crate) mod _generated { |
| @@ -153,39 +163,63 @@ pub use crate::_generated::interrupt; | |||
| 153 | /// ```rust,ignore | 163 | /// ```rust,ignore |
| 154 | /// use embassy_stm32::{bind_interrupts, i2c, peripherals}; | 164 | /// use embassy_stm32::{bind_interrupts, i2c, peripherals}; |
| 155 | /// | 165 | /// |
| 156 | /// bind_interrupts!(struct Irqs { | 166 | /// bind_interrupts!( |
| 157 | /// I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>; | 167 | /// /// Binds the I2C interrupts. |
| 158 | /// I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>, | 168 | /// struct Irqs { |
| 159 | /// i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>; | 169 | /// I2C1 => i2c::EventInterruptHandler<peripherals::I2C1>, i2c::ErrorInterruptHandler<peripherals::I2C1>; |
| 160 | /// }); | 170 | /// I2C2_3 => i2c::EventInterruptHandler<peripherals::I2C2>, i2c::ErrorInterruptHandler<peripherals::I2C2>, |
| 171 | /// i2c::EventInterruptHandler<peripherals::I2C3>, i2c::ErrorInterruptHandler<peripherals::I2C3>; | ||
| 172 | /// } | ||
| 173 | /// ); | ||
| 161 | /// ``` | 174 | /// ``` |
| 162 | 175 | ||
| 163 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. | 176 | // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. |
| 164 | #[macro_export] | 177 | #[macro_export] |
| 165 | macro_rules! bind_interrupts { | 178 | macro_rules! bind_interrupts { |
| 166 | ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { | 179 | ($(#[$outer:meta])* $vis:vis struct $name:ident { |
| 180 | $( | ||
| 181 | $(#[$inner:meta])* | ||
| 182 | $(#[cfg($cond_irq:meta)])? | ||
| 183 | $irq:ident => $( | ||
| 184 | $(#[cfg($cond_handler:meta)])? | ||
| 185 | $handler:ty | ||
| 186 | ),*; | ||
| 187 | )* | ||
| 188 | }) => { | ||
| 167 | #[derive(Copy, Clone)] | 189 | #[derive(Copy, Clone)] |
| 190 | $(#[$outer])* | ||
| 168 | $vis struct $name; | 191 | $vis struct $name; |
| 169 | 192 | ||
| 170 | $( | 193 | $( |
| 171 | #[allow(non_snake_case)] | 194 | #[allow(non_snake_case)] |
| 172 | #[no_mangle] | 195 | #[no_mangle] |
| 196 | $(#[cfg($cond_irq)])? | ||
| 197 | $(#[$inner])* | ||
| 173 | unsafe extern "C" fn $irq() { | 198 | unsafe extern "C" fn $irq() { |
| 174 | $( | 199 | $( |
| 200 | $(#[cfg($cond_handler)])? | ||
| 175 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); | 201 | <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); |
| 202 | |||
| 176 | )* | 203 | )* |
| 177 | } | 204 | } |
| 178 | 205 | ||
| 179 | $( | 206 | $(#[cfg($cond_irq)])? |
| 180 | unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} | 207 | $crate::bind_interrupts!(@inner |
| 181 | )* | 208 | $( |
| 209 | $(#[cfg($cond_handler)])? | ||
| 210 | unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} | ||
| 211 | )* | ||
| 212 | ); | ||
| 182 | )* | 213 | )* |
| 183 | }; | 214 | }; |
| 215 | (@inner $($t:tt)*) => { | ||
| 216 | $($t)* | ||
| 217 | } | ||
| 184 | } | 218 | } |
| 185 | 219 | ||
| 186 | // Reexports | 220 | // Reexports |
| 187 | pub use _generated::{peripherals, Peripherals}; | 221 | pub use _generated::{peripherals, Peripherals}; |
| 188 | pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 222 | pub use embassy_hal_internal::{Peri, PeripheralType}; |
| 189 | #[cfg(feature = "unstable-pac")] | 223 | #[cfg(feature = "unstable-pac")] |
| 190 | pub use stm32_metapac as pac; | 224 | pub use stm32_metapac as pac; |
| 191 | #[cfg(not(feature = "unstable-pac"))] | 225 | #[cfg(not(feature = "unstable-pac"))] |
| @@ -197,6 +231,7 @@ pub use crate::pac::NVIC_PRIO_BITS; | |||
| 197 | 231 | ||
| 198 | /// `embassy-stm32` global configuration. | 232 | /// `embassy-stm32` global configuration. |
| 199 | #[non_exhaustive] | 233 | #[non_exhaustive] |
| 234 | #[derive(Clone, Copy)] | ||
| 200 | pub struct Config { | 235 | pub struct Config { |
| 201 | /// RCC config. | 236 | /// RCC config. |
| 202 | pub rcc: rcc::Config, | 237 | pub rcc: rcc::Config, |
| @@ -215,6 +250,10 @@ pub struct Config { | |||
| 215 | #[cfg(any(stm32l4, stm32l5, stm32u5))] | 250 | #[cfg(any(stm32l4, stm32l5, stm32u5))] |
| 216 | pub enable_independent_io_supply: bool, | 251 | pub enable_independent_io_supply: bool, |
| 217 | 252 | ||
| 253 | /// On the U5 series all analog peripherals are powered by a separate supply. | ||
| 254 | #[cfg(stm32u5)] | ||
| 255 | pub enable_independent_analog_supply: bool, | ||
| 256 | |||
| 218 | /// BDMA interrupt priority. | 257 | /// BDMA interrupt priority. |
| 219 | /// | 258 | /// |
| 220 | /// Defaults to P0 (highest). | 259 | /// Defaults to P0 (highest). |
| @@ -254,6 +293,8 @@ impl Default for Config { | |||
| 254 | enable_debug_during_sleep: true, | 293 | enable_debug_during_sleep: true, |
| 255 | #[cfg(any(stm32l4, stm32l5, stm32u5))] | 294 | #[cfg(any(stm32l4, stm32l5, stm32u5))] |
| 256 | enable_independent_io_supply: true, | 295 | enable_independent_io_supply: true, |
| 296 | #[cfg(stm32u5)] | ||
| 297 | enable_independent_analog_supply: true, | ||
| 257 | #[cfg(bdma)] | 298 | #[cfg(bdma)] |
| 258 | bdma_interrupt_priority: Priority::P0, | 299 | bdma_interrupt_priority: Priority::P0, |
| 259 | #[cfg(dma)] | 300 | #[cfg(dma)] |
| @@ -293,6 +334,9 @@ mod dual_core { | |||
| 293 | /// It cannot be initialized by the user. The intended use is: | 334 | /// It cannot be initialized by the user. The intended use is: |
| 294 | /// | 335 | /// |
| 295 | /// ``` | 336 | /// ``` |
| 337 | /// use core::mem::MaybeUninit; | ||
| 338 | /// use embassy_stm32::{init_secondary, SharedData}; | ||
| 339 | /// | ||
| 296 | /// #[link_section = ".ram_d3"] | 340 | /// #[link_section = ".ram_d3"] |
| 297 | /// static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); | 341 | /// static SHARED_DATA: MaybeUninit<SharedData> = MaybeUninit::uninit(); |
| 298 | /// | 342 | /// |
| @@ -300,9 +344,11 @@ mod dual_core { | |||
| 300 | /// ``` | 344 | /// ``` |
| 301 | /// | 345 | /// |
| 302 | /// This static must be placed in the same position for both cores. How and where this is done is left to the user. | 346 | /// This static must be placed in the same position for both cores. How and where this is done is left to the user. |
| 347 | #[repr(C)] | ||
| 303 | pub struct SharedData { | 348 | pub struct SharedData { |
| 304 | init_flag: AtomicUsize, | 349 | init_flag: AtomicUsize, |
| 305 | clocks: UnsafeCell<MaybeUninit<Clocks>>, | 350 | clocks: UnsafeCell<MaybeUninit<Clocks>>, |
| 351 | config: UnsafeCell<MaybeUninit<SharedConfig>>, | ||
| 306 | } | 352 | } |
| 307 | 353 | ||
| 308 | unsafe impl Sync for SharedData {} | 354 | unsafe impl Sync for SharedData {} |
| @@ -322,9 +368,16 @@ mod dual_core { | |||
| 322 | pub fn init_primary(config: Config, shared_data: &'static MaybeUninit<SharedData>) -> Peripherals { | 368 | pub fn init_primary(config: Config, shared_data: &'static MaybeUninit<SharedData>) -> Peripherals { |
| 323 | let shared_data = unsafe { shared_data.assume_init_ref() }; | 369 | let shared_data = unsafe { shared_data.assume_init_ref() }; |
| 324 | 370 | ||
| 371 | // Write the flag as soon as possible. Reading this flag uninitialized in the `init_secondary` | ||
| 372 | // is maybe unsound? Unclear. If it is indeed unsound, writing it sooner doesn't fix it all, | ||
| 373 | // but improves the odds of it going right | ||
| 374 | shared_data.init_flag.store(0, Ordering::SeqCst); | ||
| 375 | |||
| 325 | rcc::set_freqs_ptr(shared_data.clocks.get()); | 376 | rcc::set_freqs_ptr(shared_data.clocks.get()); |
| 326 | let p = init_hw(config); | 377 | let p = init_hw(config); |
| 327 | 378 | ||
| 379 | unsafe { *shared_data.config.get() }.write(config.into()); | ||
| 380 | |||
| 328 | shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst); | 381 | shared_data.init_flag.store(INIT_DONE_FLAG, Ordering::SeqCst); |
| 329 | 382 | ||
| 330 | p | 383 | p |
| @@ -372,15 +425,63 @@ mod dual_core { | |||
| 372 | fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals { | 425 | fn init_secondary_hw(shared_data: &'static SharedData) -> Peripherals { |
| 373 | rcc::set_freqs_ptr(shared_data.clocks.get()); | 426 | rcc::set_freqs_ptr(shared_data.clocks.get()); |
| 374 | 427 | ||
| 428 | let config = unsafe { (*shared_data.config.get()).assume_init() }; | ||
| 429 | |||
| 375 | // We use different timers on the different cores, so we have to still initialize one here | 430 | // We use different timers on the different cores, so we have to still initialize one here |
| 376 | #[cfg(feature = "_time-driver")] | ||
| 377 | critical_section::with(|cs| { | 431 | critical_section::with(|cs| { |
| 432 | unsafe { | ||
| 433 | dma::init( | ||
| 434 | cs, | ||
| 435 | #[cfg(bdma)] | ||
| 436 | config.bdma_interrupt_priority, | ||
| 437 | #[cfg(dma)] | ||
| 438 | config.dma_interrupt_priority, | ||
| 439 | #[cfg(gpdma)] | ||
| 440 | config.gpdma_interrupt_priority, | ||
| 441 | ) | ||
| 442 | } | ||
| 443 | |||
| 444 | #[cfg(feature = "_time-driver")] | ||
| 378 | // must be after rcc init | 445 | // must be after rcc init |
| 379 | time_driver::init(cs); | 446 | time_driver::init(cs); |
| 380 | }); | 447 | }); |
| 381 | 448 | ||
| 382 | Peripherals::take() | 449 | Peripherals::take() |
| 383 | } | 450 | } |
| 451 | |||
| 452 | #[repr(C)] | ||
| 453 | #[derive(Clone, Copy)] | ||
| 454 | struct SharedConfig { | ||
| 455 | #[cfg(bdma)] | ||
| 456 | bdma_interrupt_priority: Priority, | ||
| 457 | #[cfg(dma)] | ||
| 458 | dma_interrupt_priority: Priority, | ||
| 459 | #[cfg(gpdma)] | ||
| 460 | gpdma_interrupt_priority: Priority, | ||
| 461 | } | ||
| 462 | |||
| 463 | impl From<Config> for SharedConfig { | ||
| 464 | fn from(value: Config) -> Self { | ||
| 465 | let Config { | ||
| 466 | #[cfg(bdma)] | ||
| 467 | bdma_interrupt_priority, | ||
| 468 | #[cfg(dma)] | ||
| 469 | dma_interrupt_priority, | ||
| 470 | #[cfg(gpdma)] | ||
| 471 | gpdma_interrupt_priority, | ||
| 472 | .. | ||
| 473 | } = value; | ||
| 474 | |||
| 475 | SharedConfig { | ||
| 476 | #[cfg(bdma)] | ||
| 477 | bdma_interrupt_priority, | ||
| 478 | #[cfg(dma)] | ||
| 479 | dma_interrupt_priority, | ||
| 480 | #[cfg(gpdma)] | ||
| 481 | gpdma_interrupt_priority, | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 384 | } | 485 | } |
| 385 | 486 | ||
| 386 | #[cfg(feature = "_dual-core")] | 487 | #[cfg(feature = "_dual-core")] |
| @@ -397,7 +498,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 397 | cr.set_stop(config.enable_debug_during_sleep); | 498 | cr.set_stop(config.enable_debug_during_sleep); |
| 398 | cr.set_standby(config.enable_debug_during_sleep); | 499 | cr.set_standby(config.enable_debug_during_sleep); |
| 399 | } | 500 | } |
| 400 | #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5))] | 501 | #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u0, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5))] |
| 401 | { | 502 | { |
| 402 | cr.set_dbg_stop(config.enable_debug_during_sleep); | 503 | cr.set_dbg_stop(config.enable_debug_during_sleep); |
| 403 | cr.set_dbg_standby(config.enable_debug_during_sleep); | 504 | cr.set_dbg_standby(config.enable_debug_during_sleep); |
| @@ -444,6 +545,20 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 444 | crate::pac::PWR.svmcr().modify(|w| { | 545 | crate::pac::PWR.svmcr().modify(|w| { |
| 445 | w.set_io2sv(config.enable_independent_io_supply); | 546 | w.set_io2sv(config.enable_independent_io_supply); |
| 446 | }); | 547 | }); |
| 548 | if config.enable_independent_analog_supply { | ||
| 549 | crate::pac::PWR.svmcr().modify(|w| { | ||
| 550 | w.set_avm1en(true); | ||
| 551 | }); | ||
| 552 | while !crate::pac::PWR.svmsr().read().vdda1rdy() {} | ||
| 553 | crate::pac::PWR.svmcr().modify(|w| { | ||
| 554 | w.set_asv(true); | ||
| 555 | }); | ||
| 556 | } else { | ||
| 557 | crate::pac::PWR.svmcr().modify(|w| { | ||
| 558 | w.set_avm1en(false); | ||
| 559 | w.set_avm2en(false); | ||
| 560 | }); | ||
| 561 | } | ||
| 447 | } | 562 | } |
| 448 | 563 | ||
| 449 | // dead battery functionality is still present on these | 564 | // dead battery functionality is still present on these |
| @@ -491,17 +606,7 @@ fn init_hw(config: Config) -> Peripherals { | |||
| 491 | #[cfg(feature = "exti")] | 606 | #[cfg(feature = "exti")] |
| 492 | exti::init(cs); | 607 | exti::init(cs); |
| 493 | 608 | ||
| 494 | rcc::init(config.rcc); | 609 | rcc::init_rcc(cs, config.rcc); |
| 495 | |||
| 496 | // must be after rcc init | ||
| 497 | #[cfg(feature = "_time-driver")] | ||
| 498 | time_driver::init(cs); | ||
| 499 | |||
| 500 | #[cfg(feature = "low-power")] | ||
| 501 | { | ||
| 502 | crate::rcc::REFCOUNT_STOP2 = 0; | ||
| 503 | crate::rcc::REFCOUNT_STOP1 = 0; | ||
| 504 | } | ||
| 505 | } | 610 | } |
| 506 | 611 | ||
| 507 | p | 612 | p |
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 604bdf416..7734365f1 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs | |||
| @@ -51,6 +51,9 @@ | |||
| 51 | //! } | 51 | //! } |
| 52 | //! ``` | 52 | //! ``` |
| 53 | 53 | ||
| 54 | // TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` | ||
| 55 | #![allow(static_mut_refs)] | ||
| 56 | |||
| 54 | use core::arch::asm; | 57 | use core::arch::asm; |
| 55 | use core::marker::PhantomData; | 58 | use core::marker::PhantomData; |
| 56 | use core::sync::atomic::{compiler_fence, Ordering}; | 59 | use core::sync::atomic::{compiler_fence, Ordering}; |
| @@ -67,6 +70,7 @@ use crate::rtc::Rtc; | |||
| 67 | 70 | ||
| 68 | static mut EXECUTOR: Option<Executor> = None; | 71 | static mut EXECUTOR: Option<Executor> = None; |
| 69 | 72 | ||
| 73 | #[cfg(not(stm32u0))] | ||
| 70 | foreach_interrupt! { | 74 | foreach_interrupt! { |
| 71 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { | 75 | (RTC, rtc, $block:ident, WKUP, $irq:ident) => { |
| 72 | #[interrupt] | 76 | #[interrupt] |
| @@ -77,6 +81,17 @@ foreach_interrupt! { | |||
| 77 | }; | 81 | }; |
| 78 | } | 82 | } |
| 79 | 83 | ||
| 84 | #[cfg(stm32u0)] | ||
| 85 | foreach_interrupt! { | ||
| 86 | (RTC, rtc, $block:ident, TAMP, $irq:ident) => { | ||
| 87 | #[interrupt] | ||
| 88 | #[allow(non_snake_case)] | ||
| 89 | unsafe fn $irq() { | ||
| 90 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 80 | #[allow(dead_code)] | 95 | #[allow(dead_code)] |
| 81 | pub(crate) unsafe fn on_wakeup_irq() { | 96 | pub(crate) unsafe fn on_wakeup_irq() { |
| 82 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); | 97 | EXECUTOR.as_mut().unwrap().on_wakeup_irq(); |
| @@ -109,10 +124,10 @@ pub enum StopMode { | |||
| 109 | Stop2, | 124 | Stop2, |
| 110 | } | 125 | } |
| 111 | 126 | ||
| 112 | #[cfg(stm32l5)] | 127 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] |
| 113 | use stm32_metapac::pwr::vals::Lpms; | 128 | use stm32_metapac::pwr::vals::Lpms; |
| 114 | 129 | ||
| 115 | #[cfg(stm32l5)] | 130 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] |
| 116 | impl Into<Lpms> for StopMode { | 131 | impl Into<Lpms> for StopMode { |
| 117 | fn into(self) -> Lpms { | 132 | fn into(self) -> Lpms { |
| 118 | match self { | 133 | match self { |
| @@ -152,7 +167,9 @@ impl Executor { | |||
| 152 | time_driver: get_driver(), | 167 | time_driver: get_driver(), |
| 153 | }); | 168 | }); |
| 154 | 169 | ||
| 155 | EXECUTOR.as_mut().unwrap() | 170 | let executor = EXECUTOR.as_mut().unwrap(); |
| 171 | |||
| 172 | executor | ||
| 156 | }) | 173 | }) |
| 157 | } | 174 | } |
| 158 | 175 | ||
| @@ -181,7 +198,7 @@ impl Executor { | |||
| 181 | 198 | ||
| 182 | #[allow(unused_variables)] | 199 | #[allow(unused_variables)] |
| 183 | fn configure_stop(&mut self, stop_mode: StopMode) { | 200 | fn configure_stop(&mut self, stop_mode: StopMode) { |
| 184 | #[cfg(stm32l5)] | 201 | #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] |
| 185 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); | 202 | crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); |
| 186 | #[cfg(stm32h5)] | 203 | #[cfg(stm32h5)] |
| 187 | crate::pac::PWR.pmcr().modify(|v| { | 204 | crate::pac::PWR.pmcr().modify(|v| { |
| @@ -238,11 +255,12 @@ impl Executor { | |||
| 238 | /// | 255 | /// |
| 239 | /// This function never returns. | 256 | /// This function never returns. |
| 240 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | 257 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { |
| 241 | init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner()); | 258 | let executor = unsafe { EXECUTOR.as_mut().unwrap() }; |
| 259 | init(executor.inner.spawner()); | ||
| 242 | 260 | ||
| 243 | loop { | 261 | loop { |
| 244 | unsafe { | 262 | unsafe { |
| 245 | EXECUTOR.as_mut().unwrap().inner.poll(); | 263 | executor.inner.poll(); |
| 246 | self.configure_pwr(); | 264 | self.configure_pwr(); |
| 247 | asm!("wfe"); | 265 | asm!("wfe"); |
| 248 | }; | 266 | }; |
diff --git a/embassy-stm32/src/lptim/channel.rs b/embassy-stm32/src/lptim/channel.rs new file mode 100644 index 000000000..17fc2fb86 --- /dev/null +++ b/embassy-stm32/src/lptim/channel.rs | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /// Timer channel. | ||
| 2 | #[derive(Clone, Copy)] | ||
| 3 | pub enum Channel { | ||
| 4 | /// Channel 1. | ||
| 5 | Ch1, | ||
| 6 | /// Channel 2. | ||
| 7 | Ch2, | ||
| 8 | } | ||
| 9 | |||
| 10 | impl Channel { | ||
| 11 | /// Get the channel index (0..1) | ||
| 12 | pub fn index(&self) -> usize { | ||
| 13 | match self { | ||
| 14 | Channel::Ch1 => 0, | ||
| 15 | Channel::Ch2 => 1, | ||
| 16 | } | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/embassy-stm32/src/lptim/mod.rs b/embassy-stm32/src/lptim/mod.rs new file mode 100644 index 000000000..e0ddba1c7 --- /dev/null +++ b/embassy-stm32/src/lptim/mod.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | //! Low-power timer (LPTIM) | ||
| 2 | |||
| 3 | pub mod pwm; | ||
| 4 | pub mod timer; | ||
| 5 | |||
| 6 | use crate::rcc::RccPeripheral; | ||
| 7 | |||
| 8 | /// Timer channel. | ||
| 9 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 10 | mod channel; | ||
| 11 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 12 | pub use channel::Channel; | ||
| 13 | use embassy_hal_internal::PeripheralType; | ||
| 14 | |||
| 15 | pin_trait!(OutputPin, BasicInstance); | ||
| 16 | pin_trait!(Channel1Pin, BasicInstance); | ||
| 17 | pin_trait!(Channel2Pin, BasicInstance); | ||
| 18 | |||
| 19 | pub(crate) trait SealedInstance: RccPeripheral { | ||
| 20 | fn regs() -> crate::pac::lptim::Lptim; | ||
| 21 | } | ||
| 22 | pub(crate) trait SealedBasicInstance: RccPeripheral {} | ||
| 23 | |||
| 24 | /// LPTIM basic instance trait. | ||
| 25 | #[allow(private_bounds)] | ||
| 26 | pub trait BasicInstance: PeripheralType + SealedBasicInstance + 'static {} | ||
| 27 | |||
| 28 | /// LPTIM instance trait. | ||
| 29 | #[allow(private_bounds)] | ||
| 30 | pub trait Instance: BasicInstance + SealedInstance + 'static {} | ||
| 31 | |||
| 32 | foreach_interrupt! { | ||
| 33 | ($inst:ident, lptim, LPTIM, GLOBAL, $irq:ident) => { | ||
| 34 | impl SealedInstance for crate::peripherals::$inst { | ||
| 35 | fn regs() -> crate::pac::lptim::Lptim { | ||
| 36 | crate::pac::$inst | ||
| 37 | } | ||
| 38 | } | ||
| 39 | impl SealedBasicInstance for crate::peripherals::$inst { | ||
| 40 | } | ||
| 41 | impl BasicInstance for crate::peripherals::$inst {} | ||
| 42 | impl Instance for crate::peripherals::$inst {} | ||
| 43 | }; | ||
| 44 | ($inst:ident, lptim, LPTIM_BASIC, GLOBAL, $irq:ident) => { | ||
| 45 | impl SealedBasicInstance for crate::peripherals::$inst { | ||
| 46 | } | ||
| 47 | impl BasicInstance for crate::peripherals::$inst {} | ||
| 48 | }; | ||
| 49 | } | ||
diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs new file mode 100644 index 000000000..2f2d7ba01 --- /dev/null +++ b/embassy-stm32/src/lptim/pwm.rs | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | //! PWM driver. | ||
| 2 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use super::timer::Timer; | ||
| 8 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | ||
| 9 | use super::OutputPin; | ||
| 10 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 11 | use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin}; | ||
| 12 | use super::{BasicInstance, Instance}; | ||
| 13 | #[cfg(gpio_v2)] | ||
| 14 | use crate::gpio::Pull; | ||
| 15 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 16 | use crate::time::Hertz; | ||
| 17 | |||
| 18 | /// Output marker type. | ||
| 19 | pub enum Output {} | ||
| 20 | /// Channel 1 marker type. | ||
| 21 | pub enum Ch1 {} | ||
| 22 | /// Channel 2 marker type. | ||
| 23 | pub enum Ch2 {} | ||
| 24 | |||
| 25 | /// PWM pin wrapper. | ||
| 26 | /// | ||
| 27 | /// This wraps a pin to make it usable with PWM. | ||
| 28 | pub struct PwmPin<'d, T, C> { | ||
| 29 | _pin: Peri<'d, AnyPin>, | ||
| 30 | phantom: PhantomData<(T, C)>, | ||
| 31 | } | ||
| 32 | |||
| 33 | /// PWM pin config | ||
| 34 | /// | ||
| 35 | /// This configures the pwm pin settings | ||
| 36 | pub struct PwmPinConfig { | ||
| 37 | /// PWM Pin output type | ||
| 38 | pub output_type: OutputType, | ||
| 39 | /// PWM Pin speed | ||
| 40 | pub speed: Speed, | ||
| 41 | /// PWM Pin pull type | ||
| 42 | #[cfg(gpio_v2)] | ||
| 43 | pub pull: Pull, | ||
| 44 | } | ||
| 45 | |||
| 46 | macro_rules! channel_impl { | ||
| 47 | ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { | ||
| 48 | impl<'d, T: BasicInstance> PwmPin<'d, T, $channel> { | ||
| 49 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | ||
| 50 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { | ||
| 51 | critical_section::with(|_| { | ||
| 52 | pin.set_low(); | ||
| 53 | pin.set_as_af( | ||
| 54 | pin.af_num(), | ||
| 55 | AfType::output(OutputType::PushPull, Speed::VeryHigh), | ||
| 56 | ); | ||
| 57 | }); | ||
| 58 | PwmPin { | ||
| 59 | _pin: pin.into(), | ||
| 60 | phantom: PhantomData, | ||
| 61 | } | ||
| 62 | } | ||
| 63 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")] | ||
| 64 | pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self { | ||
| 65 | critical_section::with(|_| { | ||
| 66 | pin.set_low(); | ||
| 67 | pin.set_as_af( | ||
| 68 | pin.af_num(), | ||
| 69 | #[cfg(gpio_v1)] | ||
| 70 | AfType::output(pin_config.output_type, pin_config.speed), | ||
| 71 | #[cfg(gpio_v2)] | ||
| 72 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | ||
| 73 | ); | ||
| 74 | }); | ||
| 75 | PwmPin { | ||
| 76 | _pin: pin.into(), | ||
| 77 | phantom: PhantomData, | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | ||
| 85 | channel_impl!(new, new_with_config, Output, OutputPin); | ||
| 86 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 87 | channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin); | ||
| 88 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 89 | channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin); | ||
| 90 | |||
| 91 | /// PWM driver. | ||
| 92 | pub struct Pwm<'d, T: Instance> { | ||
| 93 | inner: Timer<'d, T>, | ||
| 94 | } | ||
| 95 | |||
| 96 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | ||
| 97 | impl<'d, T: Instance> Pwm<'d, T> { | ||
| 98 | /// Create a new PWM driver. | ||
| 99 | pub fn new(tim: Peri<'d, T>, _output_pin: PwmPin<'d, T, Output>, freq: Hertz) -> Self { | ||
| 100 | Self::new_inner(tim, freq) | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Set the duty. | ||
| 104 | /// | ||
| 105 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 106 | pub fn set_duty(&mut self, duty: u16) { | ||
| 107 | assert!(duty <= self.get_max_duty()); | ||
| 108 | self.inner.set_compare_value(duty) | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Get the duty. | ||
| 112 | /// | ||
| 113 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 114 | pub fn get_duty(&self) -> u16 { | ||
| 115 | self.inner.get_compare_value() | ||
| 116 | } | ||
| 117 | |||
| 118 | fn post_init(&mut self) {} | ||
| 119 | } | ||
| 120 | |||
| 121 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 122 | impl<'d, T: Instance> Pwm<'d, T> { | ||
| 123 | /// Create a new PWM driver. | ||
| 124 | pub fn new( | ||
| 125 | tim: Peri<'d, T>, | ||
| 126 | _ch1_pin: Option<PwmPin<'d, T, Ch1>>, | ||
| 127 | _ch2_pin: Option<PwmPin<'d, T, Ch2>>, | ||
| 128 | freq: Hertz, | ||
| 129 | ) -> Self { | ||
| 130 | Self::new_inner(tim, freq) | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Enable the given channel. | ||
| 134 | pub fn enable(&mut self, channel: Channel) { | ||
| 135 | self.inner.enable_channel(channel, true); | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Disable the given channel. | ||
| 139 | pub fn disable(&mut self, channel: Channel) { | ||
| 140 | self.inner.enable_channel(channel, false); | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Check whether given channel is enabled | ||
| 144 | pub fn is_enabled(&self, channel: Channel) -> bool { | ||
| 145 | self.inner.get_channel_enable_state(channel) | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Set the duty for a given channel. | ||
| 149 | /// | ||
| 150 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 151 | pub fn set_duty(&mut self, channel: Channel, duty: u16) { | ||
| 152 | assert!(duty <= self.get_max_duty()); | ||
| 153 | self.inner.set_compare_value(channel, duty) | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Get the duty for a given channel. | ||
| 157 | /// | ||
| 158 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 159 | pub fn get_duty(&self, channel: Channel) -> u16 { | ||
| 160 | self.inner.get_compare_value(channel) | ||
| 161 | } | ||
| 162 | |||
| 163 | fn post_init(&mut self) { | ||
| 164 | [Channel::Ch1, Channel::Ch2].iter().for_each(|&channel| { | ||
| 165 | self.inner.set_channel_direction(channel, ChannelDirection::OutputPwm); | ||
| 166 | }); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | impl<'d, T: Instance> Pwm<'d, T> { | ||
| 171 | fn new_inner(tim: Peri<'d, T>, freq: Hertz) -> Self { | ||
| 172 | let mut this = Self { inner: Timer::new(tim) }; | ||
| 173 | |||
| 174 | this.inner.enable(); | ||
| 175 | this.set_frequency(freq); | ||
| 176 | |||
| 177 | this.post_init(); | ||
| 178 | |||
| 179 | this.inner.continuous_mode_start(); | ||
| 180 | |||
| 181 | this | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Set PWM frequency. | ||
| 185 | /// | ||
| 186 | /// Note: when you call this, the max duty value changes, so you will have to | ||
| 187 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | ||
| 188 | pub fn set_frequency(&mut self, frequency: Hertz) { | ||
| 189 | self.inner.set_frequency(frequency); | ||
| 190 | } | ||
| 191 | |||
| 192 | /// Get max duty value. | ||
| 193 | /// | ||
| 194 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 195 | pub fn get_max_duty(&self) -> u16 { | ||
| 196 | self.inner.get_max_compare_value() + 1 | ||
| 197 | } | ||
| 198 | } | ||
diff --git a/embassy-stm32/src/lptim/timer/channel_direction.rs b/embassy-stm32/src/lptim/timer/channel_direction.rs new file mode 100644 index 000000000..e4af8f45f --- /dev/null +++ b/embassy-stm32/src/lptim/timer/channel_direction.rs | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | use crate::pac::lptim::vals; | ||
| 2 | |||
| 3 | /// Direction of a low-power timer channel | ||
| 4 | pub enum ChannelDirection { | ||
| 5 | /// Use channel as a PWM output | ||
| 6 | OutputPwm, | ||
| 7 | /// Use channel as an input capture | ||
| 8 | InputCapture, | ||
| 9 | } | ||
| 10 | |||
| 11 | impl From<ChannelDirection> for vals::Ccsel { | ||
| 12 | fn from(direction: ChannelDirection) -> Self { | ||
| 13 | match direction { | ||
| 14 | ChannelDirection::OutputPwm => vals::Ccsel::OUTPUT_COMPARE, | ||
| 15 | ChannelDirection::InputCapture => vals::Ccsel::INPUT_CAPTURE, | ||
| 16 | } | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/embassy-stm32/src/lptim/timer/mod.rs b/embassy-stm32/src/lptim/timer/mod.rs new file mode 100644 index 000000000..a629be62b --- /dev/null +++ b/embassy-stm32/src/lptim/timer/mod.rs | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | //! Low-level timer driver. | ||
| 2 | mod prescaler; | ||
| 3 | |||
| 4 | use embassy_hal_internal::Peri; | ||
| 5 | |||
| 6 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 7 | use super::channel::Channel; | ||
| 8 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 9 | mod channel_direction; | ||
| 10 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 11 | pub use channel_direction::ChannelDirection; | ||
| 12 | use prescaler::Prescaler; | ||
| 13 | |||
| 14 | use super::Instance; | ||
| 15 | use crate::rcc; | ||
| 16 | use crate::time::Hertz; | ||
| 17 | |||
| 18 | /// Low-level timer driver. | ||
| 19 | pub struct Timer<'d, T: Instance> { | ||
| 20 | _tim: Peri<'d, T>, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl<'d, T: Instance> Timer<'d, T> { | ||
| 24 | /// Create a new timer driver. | ||
| 25 | pub fn new(tim: Peri<'d, T>) -> Self { | ||
| 26 | rcc::enable_and_reset::<T>(); | ||
| 27 | |||
| 28 | Self { _tim: tim } | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Enable the timer. | ||
| 32 | pub fn enable(&self) { | ||
| 33 | T::regs().cr().modify(|w| w.set_enable(true)); | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Disable the timer. | ||
| 37 | pub fn disable(&self) { | ||
| 38 | T::regs().cr().modify(|w| w.set_enable(false)); | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Start the timer in single pulse mode. | ||
| 42 | pub fn single_mode_start(&self) { | ||
| 43 | T::regs().cr().modify(|w| w.set_sngstrt(true)); | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Start the timer in continuous mode. | ||
| 47 | pub fn continuous_mode_start(&self) { | ||
| 48 | T::regs().cr().modify(|w| w.set_cntstrt(true)); | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. | ||
| 52 | pub fn set_frequency(&self, frequency: Hertz) { | ||
| 53 | let f = frequency.0; | ||
| 54 | assert!(f > 0); | ||
| 55 | |||
| 56 | let pclk_f = T::frequency().0; | ||
| 57 | |||
| 58 | let pclk_ticks_per_timer_period = pclk_f / f; | ||
| 59 | |||
| 60 | let psc = Prescaler::from_ticks(pclk_ticks_per_timer_period); | ||
| 61 | let arr = psc.scale_down(pclk_ticks_per_timer_period); | ||
| 62 | |||
| 63 | T::regs().cfgr().modify(|r| r.set_presc((&psc).into())); | ||
| 64 | T::regs().arr().modify(|r| r.set_arr(arr.into())); | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Get the timer frequency. | ||
| 68 | pub fn get_frequency(&self) -> Hertz { | ||
| 69 | let pclk_f = T::frequency(); | ||
| 70 | let arr = T::regs().arr().read().arr(); | ||
| 71 | let psc = Prescaler::from(T::regs().cfgr().read().presc()); | ||
| 72 | |||
| 73 | pclk_f / psc.scale_up(arr) | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Get the clock frequency of the timer (before prescaler is applied). | ||
| 77 | pub fn get_clock_frequency(&self) -> Hertz { | ||
| 78 | T::frequency() | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. | ||
| 82 | pub fn get_max_compare_value(&self) -> u16 { | ||
| 83 | T::regs().arr().read().arr() | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 88 | impl<'d, T: Instance> Timer<'d, T> { | ||
| 89 | /// Enable/disable a channel. | ||
| 90 | pub fn enable_channel(&self, channel: Channel, enable: bool) { | ||
| 91 | T::regs().ccmr(0).modify(|w| { | ||
| 92 | w.set_cce(channel.index(), enable); | ||
| 93 | }); | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Get enable/disable state of a channel | ||
| 97 | pub fn get_channel_enable_state(&self, channel: Channel) -> bool { | ||
| 98 | T::regs().ccmr(0).read().cce(channel.index()) | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Set compare value for a channel. | ||
| 102 | pub fn set_compare_value(&self, channel: Channel, value: u16) { | ||
| 103 | T::regs().ccr(channel.index()).modify(|w| w.set_ccr(value)); | ||
| 104 | } | ||
| 105 | |||
| 106 | /// Get compare value for a channel. | ||
| 107 | pub fn get_compare_value(&self, channel: Channel) -> u16 { | ||
| 108 | T::regs().ccr(channel.index()).read().ccr() | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Set channel direction. | ||
| 112 | #[cfg(any(lptim_v2a, lptim_v2b))] | ||
| 113 | pub fn set_channel_direction(&self, channel: Channel, direction: ChannelDirection) { | ||
| 114 | T::regs() | ||
| 115 | .ccmr(0) | ||
| 116 | .modify(|w| w.set_ccsel(channel.index(), direction.into())); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | #[cfg(not(any(lptim_v2a, lptim_v2b)))] | ||
| 121 | impl<'d, T: Instance> Timer<'d, T> { | ||
| 122 | /// Set compare value for a channel. | ||
| 123 | pub fn set_compare_value(&self, value: u16) { | ||
| 124 | T::regs().cmp().modify(|w| w.set_cmp(value)); | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Get compare value for a channel. | ||
| 128 | pub fn get_compare_value(&self) -> u16 { | ||
| 129 | T::regs().cmp().read().cmp() | ||
| 130 | } | ||
| 131 | } | ||
diff --git a/embassy-stm32/src/lptim/timer/prescaler.rs b/embassy-stm32/src/lptim/timer/prescaler.rs new file mode 100644 index 000000000..5d2326faf --- /dev/null +++ b/embassy-stm32/src/lptim/timer/prescaler.rs | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | //! Low-level timer driver. | ||
| 2 | |||
| 3 | use crate::pac::lptim::vals; | ||
| 4 | |||
| 5 | pub enum Prescaler { | ||
| 6 | Div1, | ||
| 7 | Div2, | ||
| 8 | Div4, | ||
| 9 | Div8, | ||
| 10 | Div16, | ||
| 11 | Div32, | ||
| 12 | Div64, | ||
| 13 | Div128, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl From<&Prescaler> for vals::Presc { | ||
| 17 | fn from(prescaler: &Prescaler) -> Self { | ||
| 18 | match prescaler { | ||
| 19 | Prescaler::Div1 => vals::Presc::DIV1, | ||
| 20 | Prescaler::Div2 => vals::Presc::DIV2, | ||
| 21 | Prescaler::Div4 => vals::Presc::DIV4, | ||
| 22 | Prescaler::Div8 => vals::Presc::DIV8, | ||
| 23 | Prescaler::Div16 => vals::Presc::DIV16, | ||
| 24 | Prescaler::Div32 => vals::Presc::DIV32, | ||
| 25 | Prescaler::Div64 => vals::Presc::DIV64, | ||
| 26 | Prescaler::Div128 => vals::Presc::DIV128, | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | impl From<vals::Presc> for Prescaler { | ||
| 32 | fn from(prescaler: vals::Presc) -> Self { | ||
| 33 | match prescaler { | ||
| 34 | vals::Presc::DIV1 => Prescaler::Div1, | ||
| 35 | vals::Presc::DIV2 => Prescaler::Div2, | ||
| 36 | vals::Presc::DIV4 => Prescaler::Div4, | ||
| 37 | vals::Presc::DIV8 => Prescaler::Div8, | ||
| 38 | vals::Presc::DIV16 => Prescaler::Div16, | ||
| 39 | vals::Presc::DIV32 => Prescaler::Div32, | ||
| 40 | vals::Presc::DIV64 => Prescaler::Div64, | ||
| 41 | vals::Presc::DIV128 => Prescaler::Div128, | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | impl From<&Prescaler> for u32 { | ||
| 47 | fn from(prescaler: &Prescaler) -> Self { | ||
| 48 | match prescaler { | ||
| 49 | Prescaler::Div1 => 1, | ||
| 50 | Prescaler::Div2 => 2, | ||
| 51 | Prescaler::Div4 => 4, | ||
| 52 | Prescaler::Div8 => 8, | ||
| 53 | Prescaler::Div16 => 16, | ||
| 54 | Prescaler::Div32 => 32, | ||
| 55 | Prescaler::Div64 => 64, | ||
| 56 | Prescaler::Div128 => 128, | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | impl From<u32> for Prescaler { | ||
| 62 | fn from(prescaler: u32) -> Self { | ||
| 63 | match prescaler { | ||
| 64 | 1 => Prescaler::Div1, | ||
| 65 | 2 => Prescaler::Div2, | ||
| 66 | 4 => Prescaler::Div4, | ||
| 67 | 8 => Prescaler::Div8, | ||
| 68 | 16 => Prescaler::Div16, | ||
| 69 | 32 => Prescaler::Div32, | ||
| 70 | 64 => Prescaler::Div64, | ||
| 71 | 128 => Prescaler::Div128, | ||
| 72 | _ => unreachable!(), | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | impl Prescaler { | ||
| 78 | pub fn from_ticks(ticks: u32) -> Self { | ||
| 79 | // We need to scale down to a 16-bit range | ||
| 80 | (ticks >> 16).next_power_of_two().into() | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn scale_down(&self, ticks: u32) -> u16 { | ||
| 84 | (ticks / u32::from(self)).try_into().unwrap() | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn scale_up(&self, ticks: u16) -> u32 { | ||
| 88 | u32::from(self) * ticks as u32 | ||
| 89 | } | ||
| 90 | } | ||
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 4c5239971..0f6ef569c 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs | |||
| @@ -6,7 +6,7 @@ use core::future::poll_fn; | |||
| 6 | use core::marker::PhantomData; | 6 | use core::marker::PhantomData; |
| 7 | use core::task::Poll; | 7 | use core::task::Poll; |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 9 | use embassy_hal_internal::PeripheralType; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | use stm32_metapac::ltdc::regs::Dccr; | 11 | use stm32_metapac::ltdc::regs::Dccr; |
| 12 | use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr}; | 12 | use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr}; |
| @@ -14,7 +14,7 @@ use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr}; | |||
| 14 | use crate::gpio::{AfType, OutputType, Speed}; | 14 | use crate::gpio::{AfType, OutputType, Speed}; |
| 15 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::interrupt::{self}; | 16 | use crate::interrupt::{self}; |
| 17 | use crate::{peripherals, rcc, Peripheral}; | 17 | use crate::{peripherals, rcc, Peri}; |
| 18 | 18 | ||
| 19 | static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); | 19 | static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); |
| 20 | 20 | ||
| @@ -83,7 +83,7 @@ pub enum PolarityActive { | |||
| 83 | 83 | ||
| 84 | /// LTDC driver. | 84 | /// LTDC driver. |
| 85 | pub struct Ltdc<'d, T: Instance> { | 85 | pub struct Ltdc<'d, T: Instance> { |
| 86 | _peri: PeripheralRef<'d, T>, | 86 | _peri: Peri<'d, T>, |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | /// LTDC interrupt handler. | 89 | /// LTDC interrupt handler. |
| @@ -178,47 +178,45 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 178 | impl<'d, T: Instance> Ltdc<'d, T> { | 178 | impl<'d, T: Instance> Ltdc<'d, T> { |
| 179 | // Create a new LTDC driver without specifying color and control pins. This is typically used if you want to drive a display though a DsiHost | 179 | // Create a new LTDC driver without specifying color and control pins. This is typically used if you want to drive a display though a DsiHost |
| 180 | /// Note: Full-Duplex modes are not supported at this time | 180 | /// Note: Full-Duplex modes are not supported at this time |
| 181 | pub fn new(peri: impl Peripheral<P = T> + 'd) -> Self { | 181 | pub fn new(peri: Peri<'d, T>) -> Self { |
| 182 | Self::setup_clocks(); | 182 | Self::setup_clocks(); |
| 183 | into_ref!(peri); | ||
| 184 | Self { _peri: peri } | 183 | Self { _peri: peri } |
| 185 | } | 184 | } |
| 186 | 185 | ||
| 187 | /// Create a new LTDC driver. 8 pins per color channel for blue, green and red | 186 | /// Create a new LTDC driver. 8 pins per color channel for blue, green and red |
| 188 | #[allow(clippy::too_many_arguments)] | 187 | #[allow(clippy::too_many_arguments)] |
| 189 | pub fn new_with_pins( | 188 | pub fn new_with_pins( |
| 190 | peri: impl Peripheral<P = T> + 'd, | 189 | peri: Peri<'d, T>, |
| 191 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 190 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 192 | clk: impl Peripheral<P = impl ClkPin<T>> + 'd, | 191 | clk: Peri<'d, impl ClkPin<T>>, |
| 193 | hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd, | 192 | hsync: Peri<'d, impl HsyncPin<T>>, |
| 194 | vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd, | 193 | vsync: Peri<'d, impl VsyncPin<T>>, |
| 195 | b0: impl Peripheral<P = impl B0Pin<T>> + 'd, | 194 | b0: Peri<'d, impl B0Pin<T>>, |
| 196 | b1: impl Peripheral<P = impl B1Pin<T>> + 'd, | 195 | b1: Peri<'d, impl B1Pin<T>>, |
| 197 | b2: impl Peripheral<P = impl B2Pin<T>> + 'd, | 196 | b2: Peri<'d, impl B2Pin<T>>, |
| 198 | b3: impl Peripheral<P = impl B3Pin<T>> + 'd, | 197 | b3: Peri<'d, impl B3Pin<T>>, |
| 199 | b4: impl Peripheral<P = impl B4Pin<T>> + 'd, | 198 | b4: Peri<'d, impl B4Pin<T>>, |
| 200 | b5: impl Peripheral<P = impl B5Pin<T>> + 'd, | 199 | b5: Peri<'d, impl B5Pin<T>>, |
| 201 | b6: impl Peripheral<P = impl B6Pin<T>> + 'd, | 200 | b6: Peri<'d, impl B6Pin<T>>, |
| 202 | b7: impl Peripheral<P = impl B7Pin<T>> + 'd, | 201 | b7: Peri<'d, impl B7Pin<T>>, |
| 203 | g0: impl Peripheral<P = impl G0Pin<T>> + 'd, | 202 | g0: Peri<'d, impl G0Pin<T>>, |
| 204 | g1: impl Peripheral<P = impl G1Pin<T>> + 'd, | 203 | g1: Peri<'d, impl G1Pin<T>>, |
| 205 | g2: impl Peripheral<P = impl G2Pin<T>> + 'd, | 204 | g2: Peri<'d, impl G2Pin<T>>, |
| 206 | g3: impl Peripheral<P = impl G3Pin<T>> + 'd, | 205 | g3: Peri<'d, impl G3Pin<T>>, |
| 207 | g4: impl Peripheral<P = impl G4Pin<T>> + 'd, | 206 | g4: Peri<'d, impl G4Pin<T>>, |
| 208 | g5: impl Peripheral<P = impl G5Pin<T>> + 'd, | 207 | g5: Peri<'d, impl G5Pin<T>>, |
| 209 | g6: impl Peripheral<P = impl G6Pin<T>> + 'd, | 208 | g6: Peri<'d, impl G6Pin<T>>, |
| 210 | g7: impl Peripheral<P = impl G7Pin<T>> + 'd, | 209 | g7: Peri<'d, impl G7Pin<T>>, |
| 211 | r0: impl Peripheral<P = impl R0Pin<T>> + 'd, | 210 | r0: Peri<'d, impl R0Pin<T>>, |
| 212 | r1: impl Peripheral<P = impl R1Pin<T>> + 'd, | 211 | r1: Peri<'d, impl R1Pin<T>>, |
| 213 | r2: impl Peripheral<P = impl R2Pin<T>> + 'd, | 212 | r2: Peri<'d, impl R2Pin<T>>, |
| 214 | r3: impl Peripheral<P = impl R3Pin<T>> + 'd, | 213 | r3: Peri<'d, impl R3Pin<T>>, |
| 215 | r4: impl Peripheral<P = impl R4Pin<T>> + 'd, | 214 | r4: Peri<'d, impl R4Pin<T>>, |
| 216 | r5: impl Peripheral<P = impl R5Pin<T>> + 'd, | 215 | r5: Peri<'d, impl R5Pin<T>>, |
| 217 | r6: impl Peripheral<P = impl R6Pin<T>> + 'd, | 216 | r6: Peri<'d, impl R6Pin<T>>, |
| 218 | r7: impl Peripheral<P = impl R7Pin<T>> + 'd, | 217 | r7: Peri<'d, impl R7Pin<T>>, |
| 219 | ) -> Self { | 218 | ) -> Self { |
| 220 | Self::setup_clocks(); | 219 | Self::setup_clocks(); |
| 221 | into_ref!(peri); | ||
| 222 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 220 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 223 | new_pin!(hsync, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 221 | new_pin!(hsync, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 224 | new_pin!(vsync, AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 222 | new_pin!(vsync, AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| @@ -261,23 +259,23 @@ impl<'d, T: Instance> Ltdc<'d, T> { | |||
| 261 | // configure the HS, VS, DE and PC polarity | 259 | // configure the HS, VS, DE and PC polarity |
| 262 | ltdc.gcr().modify(|w| { | 260 | ltdc.gcr().modify(|w| { |
| 263 | w.set_hspol(match config.h_sync_polarity { | 261 | w.set_hspol(match config.h_sync_polarity { |
| 264 | PolarityActive::ActiveHigh => Hspol::ACTIVEHIGH, | 262 | PolarityActive::ActiveHigh => Hspol::ACTIVE_HIGH, |
| 265 | PolarityActive::ActiveLow => Hspol::ACTIVELOW, | 263 | PolarityActive::ActiveLow => Hspol::ACTIVE_LOW, |
| 266 | }); | 264 | }); |
| 267 | 265 | ||
| 268 | w.set_vspol(match config.v_sync_polarity { | 266 | w.set_vspol(match config.v_sync_polarity { |
| 269 | PolarityActive::ActiveHigh => Vspol::ACTIVEHIGH, | 267 | PolarityActive::ActiveHigh => Vspol::ACTIVE_HIGH, |
| 270 | PolarityActive::ActiveLow => Vspol::ACTIVELOW, | 268 | PolarityActive::ActiveLow => Vspol::ACTIVE_LOW, |
| 271 | }); | 269 | }); |
| 272 | 270 | ||
| 273 | w.set_depol(match config.data_enable_polarity { | 271 | w.set_depol(match config.data_enable_polarity { |
| 274 | PolarityActive::ActiveHigh => Depol::ACTIVEHIGH, | 272 | PolarityActive::ActiveHigh => Depol::ACTIVE_HIGH, |
| 275 | PolarityActive::ActiveLow => Depol::ACTIVELOW, | 273 | PolarityActive::ActiveLow => Depol::ACTIVE_LOW, |
| 276 | }); | 274 | }); |
| 277 | 275 | ||
| 278 | w.set_pcpol(match config.pixel_clock_polarity { | 276 | w.set_pcpol(match config.pixel_clock_polarity { |
| 279 | PolarityEdge::RisingEdge => Pcpol::RISINGEDGE, | 277 | PolarityEdge::RisingEdge => Pcpol::RISING_EDGE, |
| 280 | PolarityEdge::FallingEdge => Pcpol::FALLINGEDGE, | 278 | PolarityEdge::FallingEdge => Pcpol::FALLING_EDGE, |
| 281 | }); | 279 | }); |
| 282 | }); | 280 | }); |
| 283 | 281 | ||
| @@ -395,7 +393,10 @@ impl<'d, T: Instance> Ltdc<'d, T> { | |||
| 395 | // framebuffer pitch and line length | 393 | // framebuffer pitch and line length |
| 396 | layer.cfblr().modify(|w| { | 394 | layer.cfblr().modify(|w| { |
| 397 | w.set_cfbp(width * bytes_per_pixel); | 395 | w.set_cfbp(width * bytes_per_pixel); |
| 396 | #[cfg(not(stm32u5))] | ||
| 398 | w.set_cfbll(width * bytes_per_pixel + 7); | 397 | w.set_cfbll(width * bytes_per_pixel + 7); |
| 398 | #[cfg(stm32u5)] | ||
| 399 | w.set_cfbll(width * bytes_per_pixel + 3); | ||
| 399 | }); | 400 | }); |
| 400 | 401 | ||
| 401 | // framebuffer line number | 402 | // framebuffer line number |
| @@ -526,7 +527,7 @@ trait SealedInstance: crate::rcc::SealedRccPeripheral { | |||
| 526 | 527 | ||
| 527 | /// LTDC instance trait. | 528 | /// LTDC instance trait. |
| 528 | #[allow(private_bounds)] | 529 | #[allow(private_bounds)] |
| 529 | pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | 530 | pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send { |
| 530 | /// Interrupt for this LTDC instance. | 531 | /// Interrupt for this LTDC instance. |
| 531 | type Interrupt: interrupt::typelevel::Interrupt; | 532 | type Interrupt: interrupt::typelevel::Interrupt; |
| 532 | } | 533 | } |
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index ae53deb08..7526bb180 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs | |||
| @@ -14,7 +14,7 @@ macro_rules! peri_trait { | |||
| 14 | 14 | ||
| 15 | /// Peripheral instance trait. | 15 | /// Peripheral instance trait. |
| 16 | #[allow(private_bounds)] | 16 | #[allow(private_bounds)] |
| 17 | pub trait Instance: crate::Peripheral<P = Self> + SealedInstance + crate::rcc::RccPeripheral { | 17 | pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { |
| 18 | $($( | 18 | $($( |
| 19 | /// Interrupt for this peripheral. | 19 | /// Interrupt for this peripheral. |
| 20 | type $irq: crate::interrupt::typelevel::Interrupt; | 20 | type $irq: crate::interrupt::typelevel::Interrupt; |
| @@ -60,6 +60,17 @@ macro_rules! pin_trait_impl { | |||
| 60 | }; | 60 | }; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | #[allow(unused_macros)] | ||
| 64 | macro_rules! sel_trait_impl { | ||
| 65 | (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { | ||
| 66 | impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin { | ||
| 67 | fn sel(&self) -> u8 { | ||
| 68 | $sel | ||
| 69 | } | ||
| 70 | } | ||
| 71 | }; | ||
| 72 | } | ||
| 73 | |||
| 63 | // ==================== | 74 | // ==================== |
| 64 | 75 | ||
| 65 | macro_rules! dma_trait { | 76 | macro_rules! dma_trait { |
| @@ -85,12 +96,24 @@ macro_rules! dma_trait_impl { | |||
| 85 | }; | 96 | }; |
| 86 | } | 97 | } |
| 87 | 98 | ||
| 99 | #[allow(unused)] | ||
| 100 | macro_rules! new_dma_nonopt { | ||
| 101 | ($name:ident) => {{ | ||
| 102 | let dma = $name; | ||
| 103 | let request = dma.request(); | ||
| 104 | crate::dma::ChannelAndRequest { | ||
| 105 | channel: dma.into(), | ||
| 106 | request, | ||
| 107 | } | ||
| 108 | }}; | ||
| 109 | } | ||
| 110 | |||
| 88 | macro_rules! new_dma { | 111 | macro_rules! new_dma { |
| 89 | ($name:ident) => {{ | 112 | ($name:ident) => {{ |
| 90 | let dma = $name.into_ref(); | 113 | let dma = $name; |
| 91 | let request = dma.request(); | 114 | let request = dma.request(); |
| 92 | Some(crate::dma::ChannelAndRequest { | 115 | Some(crate::dma::ChannelAndRequest { |
| 93 | channel: dma.map_into(), | 116 | channel: dma.into(), |
| 94 | request, | 117 | request, |
| 95 | }) | 118 | }) |
| 96 | }}; | 119 | }}; |
| @@ -98,8 +121,8 @@ macro_rules! new_dma { | |||
| 98 | 121 | ||
| 99 | macro_rules! new_pin { | 122 | macro_rules! new_pin { |
| 100 | ($name:ident, $af_type:expr) => {{ | 123 | ($name:ident, $af_type:expr) => {{ |
| 101 | let pin = $name.into_ref(); | 124 | let pin = $name; |
| 102 | pin.set_as_af(pin.af_num(), $af_type); | 125 | pin.set_as_af(pin.af_num(), $af_type); |
| 103 | Some(pin.map_into()) | 126 | Some(pin.into()) |
| 104 | }}; | 127 | }}; |
| 105 | } | 128 | } |
diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index c86c18e22..2eb2e61c1 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs | |||
| @@ -1,40 +1,48 @@ | |||
| 1 | //! Operational Amplifier (OPAMP) | 1 | //! Operational Amplifier (OPAMP) |
| 2 | #![macro_use] | 2 | #![macro_use] |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | 5 | ||
| 6 | use crate::pac::opamp::vals::*; | 6 | use crate::pac::opamp::vals::*; |
| 7 | use crate::Peripheral; | 7 | use crate::Peri; |
| 8 | |||
| 9 | /// Performs a busy-wait delay for a specified number of microseconds. | ||
| 10 | #[cfg(opamp_g4)] | ||
| 11 | fn blocking_delay_ms(ms: u32) { | ||
| 12 | #[cfg(feature = "time")] | ||
| 13 | embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); | ||
| 14 | #[cfg(not(feature = "time"))] | ||
| 15 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); | ||
| 16 | } | ||
| 8 | 17 | ||
| 9 | /// Gain | 18 | /// Gain |
| 10 | #[allow(missing_docs)] | 19 | #[allow(missing_docs)] |
| 11 | #[derive(Clone, Copy)] | 20 | #[derive(Clone, Copy)] |
| 12 | pub enum OpAmpGain { | 21 | pub enum OpAmpGain { |
| 13 | Mul1, | ||
| 14 | Mul2, | 22 | Mul2, |
| 15 | Mul4, | 23 | Mul4, |
| 16 | Mul8, | 24 | Mul8, |
| 17 | Mul16, | 25 | Mul16, |
| 26 | #[cfg(opamp_g4)] | ||
| 27 | Mul32, | ||
| 28 | #[cfg(opamp_g4)] | ||
| 29 | Mul64, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[cfg(opamp_g4)] | ||
| 33 | enum OpAmpDifferentialPair { | ||
| 34 | P, | ||
| 35 | N, | ||
| 18 | } | 36 | } |
| 19 | 37 | ||
| 20 | /// Speed | 38 | /// Speed |
| 21 | #[allow(missing_docs)] | 39 | #[allow(missing_docs)] |
| 22 | #[derive(Clone, Copy)] | 40 | #[derive(Clone, Copy, PartialEq)] |
| 23 | pub enum OpAmpSpeed { | 41 | pub enum OpAmpSpeed { |
| 24 | Normal, | 42 | Normal, |
| 25 | HighSpeed, | 43 | HighSpeed, |
| 26 | } | 44 | } |
| 27 | 45 | ||
| 28 | #[cfg(opamp_g4)] | ||
| 29 | impl From<OpAmpSpeed> for crate::pac::opamp::vals::Opahsm { | ||
| 30 | fn from(v: OpAmpSpeed) -> Self { | ||
| 31 | match v { | ||
| 32 | OpAmpSpeed::Normal => crate::pac::opamp::vals::Opahsm::NORMAL, | ||
| 33 | OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::Opahsm::HIGHSPEED, | ||
| 34 | } | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | /// OpAmp external outputs, wired to a GPIO pad. | 46 | /// OpAmp external outputs, wired to a GPIO pad. |
| 39 | /// | 47 | /// |
| 40 | /// This struct can also be used as an ADC input. | 48 | /// This struct can also be used as an ADC input. |
| @@ -52,19 +60,17 @@ pub struct OpAmpInternalOutput<'d, T: Instance> { | |||
| 52 | 60 | ||
| 53 | /// OpAmp driver. | 61 | /// OpAmp driver. |
| 54 | pub struct OpAmp<'d, T: Instance> { | 62 | pub struct OpAmp<'d, T: Instance> { |
| 55 | _inner: PeripheralRef<'d, T>, | 63 | _inner: Peri<'d, T>, |
| 56 | } | 64 | } |
| 57 | 65 | ||
| 58 | impl<'d, T: Instance> OpAmp<'d, T> { | 66 | impl<'d, T: Instance> OpAmp<'d, T> { |
| 59 | /// Create a new driver instance. | 67 | /// Create a new driver instance. |
| 60 | /// | 68 | /// |
| 61 | /// Does not enable the opamp, but does set the speed mode on some families. | 69 | /// Does not enable the opamp, but does set the speed mode on some families. |
| 62 | pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { | 70 | pub fn new(opamp: Peri<'d, T>, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self { |
| 63 | into_ref!(opamp); | ||
| 64 | |||
| 65 | #[cfg(opamp_g4)] | 71 | #[cfg(opamp_g4)] |
| 66 | T::regs().csr().modify(|w| { | 72 | T::regs().csr().modify(|w| { |
| 67 | w.set_opahsm(speed.into()); | 73 | w.set_opahsm(speed == OpAmpSpeed::HighSpeed); |
| 68 | }); | 74 | }); |
| 69 | 75 | ||
| 70 | Self { _inner: opamp } | 76 | Self { _inner: opamp } |
| @@ -82,35 +88,81 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 82 | /// [`OpAmpOutput`] is dropped. | 88 | /// [`OpAmpOutput`] is dropped. |
| 83 | pub fn buffer_ext( | 89 | pub fn buffer_ext( |
| 84 | &mut self, | 90 | &mut self, |
| 85 | in_pin: impl Peripheral<P = impl NonInvertingPin<T> + crate::gpio::Pin>, | 91 | in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| 86 | out_pin: impl Peripheral<P = impl OutputPin<T> + crate::gpio::Pin>, | 92 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, |
| 93 | ) -> OpAmpOutput<'_, T> { | ||
| 94 | in_pin.set_as_analog(); | ||
| 95 | out_pin.set_as_analog(); | ||
| 96 | |||
| 97 | #[cfg(opamp_g4)] | ||
| 98 | let vm_sel = VmSel::OUTPUT; | ||
| 99 | #[cfg(not(opamp_g4))] | ||
| 100 | let vm_sel = VmSel::from_bits(0b11); | ||
| 101 | |||
| 102 | T::regs().csr().modify(|w| { | ||
| 103 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); | ||
| 104 | w.set_vm_sel(vm_sel); | ||
| 105 | #[cfg(opamp_g4)] | ||
| 106 | w.set_opaintoen(false); | ||
| 107 | w.set_opampen(true); | ||
| 108 | }); | ||
| 109 | |||
| 110 | OpAmpOutput { _inner: self } | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Configure the OpAmp as a PGA for the provided input pin, | ||
| 114 | /// outputting to the provided output pin, and enable the opamp. | ||
| 115 | /// | ||
| 116 | /// The input pin is configured for analogue mode but not consumed, | ||
| 117 | /// so it may subsequently be used for ADC or comparator inputs. | ||
| 118 | /// | ||
| 119 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 120 | /// preventing it being used elsewhere. The `OpAmpOutput` can then be | ||
| 121 | /// directly used as an ADC input. The opamp will be disabled when the | ||
| 122 | /// [`OpAmpOutput`] is dropped. | ||
| 123 | pub fn pga_ext( | ||
| 124 | &mut self, | ||
| 125 | in_pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 126 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, | ||
| 87 | gain: OpAmpGain, | 127 | gain: OpAmpGain, |
| 88 | ) -> OpAmpOutput<'_, T> { | 128 | ) -> OpAmpOutput<'_, T> { |
| 89 | into_ref!(in_pin); | ||
| 90 | into_ref!(out_pin); | ||
| 91 | in_pin.set_as_analog(); | 129 | in_pin.set_as_analog(); |
| 92 | out_pin.set_as_analog(); | 130 | out_pin.set_as_analog(); |
| 93 | 131 | ||
| 94 | // PGA_GAIN value may have different meaning in different MCU serials, use with caution. | 132 | #[cfg(opamp_g4)] |
| 95 | let (vm_sel, pga_gain) = match gain { | 133 | let vm_sel = VmSel::PGA; |
| 96 | OpAmpGain::Mul1 => (0b11, 0b00), | 134 | #[cfg(not(opamp_g4))] |
| 97 | OpAmpGain::Mul2 => (0b10, 0b00), | 135 | let vm_sel = VmSel::from_bits(0b10); |
| 98 | OpAmpGain::Mul4 => (0b10, 0b01), | 136 | |
| 99 | OpAmpGain::Mul8 => (0b10, 0b10), | 137 | #[cfg(opamp_g4)] |
| 100 | OpAmpGain::Mul16 => (0b10, 0b11), | 138 | let pga_gain = match gain { |
| 139 | OpAmpGain::Mul2 => PgaGain::GAIN2, | ||
| 140 | OpAmpGain::Mul4 => PgaGain::GAIN4, | ||
| 141 | OpAmpGain::Mul8 => PgaGain::GAIN8, | ||
| 142 | OpAmpGain::Mul16 => PgaGain::GAIN16, | ||
| 143 | OpAmpGain::Mul32 => PgaGain::GAIN32, | ||
| 144 | OpAmpGain::Mul64 => PgaGain::GAIN64, | ||
| 101 | }; | 145 | }; |
| 146 | #[cfg(not(opamp_g4))] | ||
| 147 | let pga_gain = PgaGain::from_bits(match gain { | ||
| 148 | OpAmpGain::Mul2 => 0b00, | ||
| 149 | OpAmpGain::Mul4 => 0b01, | ||
| 150 | OpAmpGain::Mul8 => 0b10, | ||
| 151 | OpAmpGain::Mul16 => 0b11, | ||
| 152 | }); | ||
| 102 | 153 | ||
| 103 | T::regs().csr().modify(|w| { | 154 | T::regs().csr().modify(|w| { |
| 104 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); | 155 | w.set_vp_sel(VpSel::from_bits(in_pin.channel())); |
| 105 | w.set_vm_sel(VmSel::from_bits(vm_sel)); | 156 | w.set_vm_sel(vm_sel); |
| 106 | w.set_pga_gain(PgaGain::from_bits(pga_gain)); | 157 | w.set_pga_gain(pga_gain); |
| 107 | #[cfg(opamp_g4)] | 158 | #[cfg(opamp_g4)] |
| 108 | w.set_opaintoen(Opaintoen::OUTPUTPIN); | 159 | w.set_opaintoen(false); |
| 109 | w.set_opampen(true); | 160 | w.set_opampen(true); |
| 110 | }); | 161 | }); |
| 111 | 162 | ||
| 112 | OpAmpOutput { _inner: self } | 163 | OpAmpOutput { _inner: self } |
| 113 | } | 164 | } |
| 165 | |||
| 114 | /// Configure the OpAmp as a buffer for the DAC it is connected to, | 166 | /// Configure the OpAmp as a buffer for the DAC it is connected to, |
| 115 | /// outputting to the provided output pin, and enable the opamp. | 167 | /// outputting to the provided output pin, and enable the opamp. |
| 116 | /// | 168 | /// |
| @@ -119,11 +171,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 119 | /// directly used as an ADC input. The opamp will be disabled when the | 171 | /// directly used as an ADC input. The opamp will be disabled when the |
| 120 | /// [`OpAmpOutput`] is dropped. | 172 | /// [`OpAmpOutput`] is dropped. |
| 121 | #[cfg(opamp_g4)] | 173 | #[cfg(opamp_g4)] |
| 122 | pub fn buffer_dac( | 174 | pub fn buffer_dac(&mut self, out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>) -> OpAmpOutput<'_, T> { |
| 123 | &mut self, | ||
| 124 | out_pin: impl Peripheral<P = impl OutputPin<T> + crate::gpio::Pin>, | ||
| 125 | ) -> OpAmpOutput<'_, T> { | ||
| 126 | into_ref!(out_pin); | ||
| 127 | out_pin.set_as_analog(); | 175 | out_pin.set_as_analog(); |
| 128 | 176 | ||
| 129 | T::regs().csr().modify(|w| { | 177 | T::regs().csr().modify(|w| { |
| @@ -131,7 +179,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 131 | 179 | ||
| 132 | w.set_vm_sel(VmSel::OUTPUT); | 180 | w.set_vm_sel(VmSel::OUTPUT); |
| 133 | w.set_vp_sel(VpSel::DAC3_CH1); | 181 | w.set_vp_sel(VpSel::DAC3_CH1); |
| 134 | w.set_opaintoen(Opaintoen::OUTPUTPIN); | 182 | w.set_opaintoen(false); |
| 135 | w.set_opampen(true); | 183 | w.set_opampen(true); |
| 136 | }); | 184 | }); |
| 137 | 185 | ||
| @@ -149,32 +197,259 @@ impl<'d, T: Instance> OpAmp<'d, T> { | |||
| 149 | #[cfg(opamp_g4)] | 197 | #[cfg(opamp_g4)] |
| 150 | pub fn buffer_int( | 198 | pub fn buffer_int( |
| 151 | &mut self, | 199 | &mut self, |
| 152 | pin: impl Peripheral<P = impl NonInvertingPin<T> + crate::gpio::Pin>, | 200 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, |
| 201 | ) -> OpAmpInternalOutput<'_, T> { | ||
| 202 | pin.set_as_analog(); | ||
| 203 | |||
| 204 | T::regs().csr().modify(|w| { | ||
| 205 | w.set_vp_sel(VpSel::from_bits(pin.channel())); | ||
| 206 | w.set_vm_sel(VmSel::OUTPUT); | ||
| 207 | #[cfg(opamp_g4)] | ||
| 208 | w.set_opaintoen(true); | ||
| 209 | w.set_opampen(true); | ||
| 210 | }); | ||
| 211 | |||
| 212 | OpAmpInternalOutput { _inner: self } | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Configure the OpAmp as a PGA for the provided input pin, | ||
| 216 | /// with the output only used internally, and enable the opamp. | ||
| 217 | /// | ||
| 218 | /// The input pin is configured for analogue mode but not consumed, | ||
| 219 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 220 | /// | ||
| 221 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC input. | ||
| 222 | /// The opamp output will be disabled when it is dropped. | ||
| 223 | #[cfg(opamp_g4)] | ||
| 224 | pub fn pga_int( | ||
| 225 | &mut self, | ||
| 226 | pin: Peri<'_, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 153 | gain: OpAmpGain, | 227 | gain: OpAmpGain, |
| 154 | ) -> OpAmpInternalOutput<'_, T> { | 228 | ) -> OpAmpInternalOutput<'_, T> { |
| 155 | into_ref!(pin); | ||
| 156 | pin.set_as_analog(); | 229 | pin.set_as_analog(); |
| 157 | 230 | ||
| 158 | // PGA_GAIN value may have different meaning in different MCU serials, use with caution. | 231 | let pga_gain = match gain { |
| 159 | let (vm_sel, pga_gain) = match gain { | 232 | OpAmpGain::Mul2 => PgaGain::GAIN2, |
| 160 | OpAmpGain::Mul1 => (0b11, 0b00), | 233 | OpAmpGain::Mul4 => PgaGain::GAIN4, |
| 161 | OpAmpGain::Mul2 => (0b10, 0b00), | 234 | OpAmpGain::Mul8 => PgaGain::GAIN8, |
| 162 | OpAmpGain::Mul4 => (0b10, 0b01), | 235 | OpAmpGain::Mul16 => PgaGain::GAIN16, |
| 163 | OpAmpGain::Mul8 => (0b10, 0b10), | 236 | OpAmpGain::Mul32 => PgaGain::GAIN32, |
| 164 | OpAmpGain::Mul16 => (0b10, 0b11), | 237 | OpAmpGain::Mul64 => PgaGain::GAIN64, |
| 165 | }; | 238 | }; |
| 166 | 239 | ||
| 167 | T::regs().csr().modify(|w| { | 240 | T::regs().csr().modify(|w| { |
| 168 | use crate::pac::opamp::vals::*; | ||
| 169 | w.set_vp_sel(VpSel::from_bits(pin.channel())); | 241 | w.set_vp_sel(VpSel::from_bits(pin.channel())); |
| 170 | w.set_vm_sel(VmSel::from_bits(vm_sel)); | 242 | w.set_vm_sel(VmSel::PGA); |
| 171 | w.set_pga_gain(PgaGain::from_bits(pga_gain)); | 243 | w.set_pga_gain(pga_gain); |
| 172 | w.set_opaintoen(Opaintoen::ADCCHANNEL); | 244 | w.set_opaintoen(true); |
| 245 | w.set_opampen(true); | ||
| 246 | }); | ||
| 247 | |||
| 248 | OpAmpInternalOutput { _inner: self } | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Configure the OpAmp as a standalone DAC with the inverting input | ||
| 252 | /// connected to the provided pin, and the output is connected | ||
| 253 | /// internally to an ADC channel. | ||
| 254 | /// | ||
| 255 | /// The input pin is configured for analogue mode but not consumed, | ||
| 256 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 257 | /// | ||
| 258 | /// The returned `OpAmpInternalOutput` struct may be used as an ADC | ||
| 259 | /// input. The opamp output will be disabled when it is dropped. | ||
| 260 | #[cfg(opamp_g4)] | ||
| 261 | pub fn standalone_dac_int( | ||
| 262 | &mut self, | ||
| 263 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 264 | ) -> OpAmpInternalOutput<'_, T> { | ||
| 265 | m_pin.set_as_analog(); | ||
| 266 | |||
| 267 | T::regs().csr().modify(|w| { | ||
| 268 | use crate::pac::opamp::vals::*; | ||
| 269 | w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx | ||
| 270 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 271 | w.set_opaintoen(true); | ||
| 173 | w.set_opampen(true); | 272 | w.set_opampen(true); |
| 174 | }); | 273 | }); |
| 175 | 274 | ||
| 176 | OpAmpInternalOutput { _inner: self } | 275 | OpAmpInternalOutput { _inner: self } |
| 177 | } | 276 | } |
| 277 | |||
| 278 | /// Configure the OpAmp as a standalone DAC with the inverting input | ||
| 279 | /// connected to the provided pin, and the output connected to the | ||
| 280 | /// provided pin. | ||
| 281 | /// | ||
| 282 | /// The input pin is configured for analogue mode but not consumed, | ||
| 283 | /// so it may be subsequently used for ADC or comparator inputs. | ||
| 284 | /// | ||
| 285 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 286 | /// preventing it being used elsewhere. The opamp will be disabled when | ||
| 287 | /// the [`OpAmpOutput`] is dropped. | ||
| 288 | #[cfg(opamp_g4)] | ||
| 289 | pub fn standalone_dac_ext( | ||
| 290 | &mut self, | ||
| 291 | m_pin: Peri<'_, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 292 | out_pin: Peri<'_, impl OutputPin<T> + crate::gpio::Pin>, | ||
| 293 | ) -> OpAmpOutput<'_, T> { | ||
| 294 | m_pin.set_as_analog(); | ||
| 295 | out_pin.set_as_analog(); | ||
| 296 | |||
| 297 | T::regs().csr().modify(|w| { | ||
| 298 | use crate::pac::opamp::vals::*; | ||
| 299 | w.set_vp_sel(VpSel::DAC3_CH1); // Actually DAC3_CHx | ||
| 300 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 301 | w.set_opaintoen(false); | ||
| 302 | w.set_opampen(true); | ||
| 303 | }); | ||
| 304 | |||
| 305 | OpAmpOutput { _inner: self } | ||
| 306 | } | ||
| 307 | |||
| 308 | /// Configure the OpAmp in standalone mode with the non-inverting input | ||
| 309 | /// connected to the provided `p_pin`, the inverting input connected to | ||
| 310 | /// the `m_pin`, and output to the provided `out_pin`. | ||
| 311 | /// | ||
| 312 | /// The input pins are configured for analogue mode but not consumed, | ||
| 313 | /// allowing their subsequent use for ADC or comparator inputs. | ||
| 314 | /// | ||
| 315 | /// The output pin is held within the returned [`OpAmpOutput`] struct, | ||
| 316 | /// preventing it being used elsewhere. The opamp will be disabled when | ||
| 317 | /// the [`OpAmpOutput`] is dropped. | ||
| 318 | #[cfg(opamp_g4)] | ||
| 319 | pub fn standalone_ext( | ||
| 320 | &mut self, | ||
| 321 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 322 | m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 323 | out_pin: Peri<'d, impl OutputPin<T> + crate::gpio::Pin>, | ||
| 324 | ) -> OpAmpOutput<'_, T> { | ||
| 325 | p_pin.set_as_analog(); | ||
| 326 | m_pin.set_as_analog(); | ||
| 327 | out_pin.set_as_analog(); | ||
| 328 | |||
| 329 | T::regs().csr().modify(|w| { | ||
| 330 | use crate::pac::opamp::vals::*; | ||
| 331 | w.set_vp_sel(VpSel::from_bits(p_pin.channel())); | ||
| 332 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 333 | w.set_opaintoen(false); | ||
| 334 | w.set_opampen(true); | ||
| 335 | }); | ||
| 336 | |||
| 337 | OpAmpOutput { _inner: self } | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Configure the OpAmp in standalone mode with the non-inverting input | ||
| 341 | /// connected to the provided `p_pin`, the inverting input connected to | ||
| 342 | /// the `m_pin`, and output is connected to the DAC. | ||
| 343 | /// | ||
| 344 | /// The input pins are configured for analogue mode but not consumed, | ||
| 345 | /// allowing their subsequent use for ADC or comparator inputs. | ||
| 346 | /// | ||
| 347 | /// The returned `OpAmpOutput` struct may be used as an ADC | ||
| 348 | /// input. The opamp output will be disabled when it is dropped. | ||
| 349 | #[cfg(opamp_g4)] | ||
| 350 | pub fn standalone_int( | ||
| 351 | &mut self, | ||
| 352 | p_pin: Peri<'d, impl NonInvertingPin<T> + crate::gpio::Pin>, | ||
| 353 | m_pin: Peri<'d, impl InvertingPin<T> + crate::gpio::Pin>, | ||
| 354 | ) -> OpAmpOutput<'_, T> { | ||
| 355 | p_pin.set_as_analog(); | ||
| 356 | m_pin.set_as_analog(); | ||
| 357 | |||
| 358 | T::regs().csr().modify(|w| { | ||
| 359 | use crate::pac::opamp::vals::*; | ||
| 360 | w.set_vp_sel(VpSel::from_bits(p_pin.channel())); | ||
| 361 | w.set_vm_sel(VmSel::from_bits(m_pin.channel())); | ||
| 362 | w.set_opaintoen(true); | ||
| 363 | w.set_opampen(true); | ||
| 364 | }); | ||
| 365 | |||
| 366 | OpAmpOutput { _inner: self } | ||
| 367 | } | ||
| 368 | |||
| 369 | /// Calibrates the operational amplifier. | ||
| 370 | /// | ||
| 371 | /// This function enables the opamp and sets the user trim mode for calibration. | ||
| 372 | /// Depending on the speed mode of the opamp, it calibrates the differential pair inputs. | ||
| 373 | /// For normal speed, both the P and N differential pairs are calibrated, | ||
| 374 | /// while for high-speed mode, only the P differential pair is calibrated. | ||
| 375 | /// | ||
| 376 | /// Calibrating a differential pair requires waiting 12ms in the worst case (binary method). | ||
| 377 | #[cfg(opamp_g4)] | ||
| 378 | pub fn calibrate(&mut self) { | ||
| 379 | T::regs().csr().modify(|w| { | ||
| 380 | w.set_opampen(true); | ||
| 381 | w.set_calon(true); | ||
| 382 | w.set_usertrim(true); | ||
| 383 | }); | ||
| 384 | |||
| 385 | if T::regs().csr().read().opahsm() { | ||
| 386 | self.calibrate_differential_pair(OpAmpDifferentialPair::P); | ||
| 387 | } else { | ||
| 388 | self.calibrate_differential_pair(OpAmpDifferentialPair::P); | ||
| 389 | self.calibrate_differential_pair(OpAmpDifferentialPair::N); | ||
| 390 | } | ||
| 391 | |||
| 392 | T::regs().csr().modify(|w| { | ||
| 393 | w.set_calon(false); | ||
| 394 | w.set_opampen(false); | ||
| 395 | }); | ||
| 396 | } | ||
| 397 | |||
| 398 | /// Calibrate differential pair. | ||
| 399 | /// | ||
| 400 | /// The calibration is done by trying different offset values and | ||
| 401 | /// measuring the outcal bit. | ||
| 402 | /// | ||
| 403 | /// The calibration range is from 0 to 31. | ||
| 404 | /// | ||
| 405 | /// The result is stored in the OPAMP_CSR register. | ||
| 406 | #[cfg(opamp_g4)] | ||
| 407 | fn calibrate_differential_pair(&mut self, pair: OpAmpDifferentialPair) { | ||
| 408 | let mut low = 0; | ||
| 409 | let mut high = 31; | ||
| 410 | |||
| 411 | let calsel = match pair { | ||
| 412 | OpAmpDifferentialPair::P => Calsel::PERCENT10, | ||
| 413 | OpAmpDifferentialPair::N => Calsel::PERCENT90, | ||
| 414 | }; | ||
| 415 | |||
| 416 | T::regs().csr().modify(|w| { | ||
| 417 | w.set_calsel(calsel); | ||
| 418 | }); | ||
| 419 | |||
| 420 | while low <= high { | ||
| 421 | let mid = (low + high) / 2; | ||
| 422 | |||
| 423 | T::regs().csr().modify(|w| match pair { | ||
| 424 | OpAmpDifferentialPair::P => { | ||
| 425 | #[cfg(feature = "defmt")] | ||
| 426 | defmt::debug!("opamp p calibration. offset: {}", mid); | ||
| 427 | w.set_trimoffsetp(mid); | ||
| 428 | } | ||
| 429 | OpAmpDifferentialPair::N => { | ||
| 430 | #[cfg(feature = "defmt")] | ||
| 431 | defmt::debug!("opamp n calibration. offset: {}", mid); | ||
| 432 | w.set_trimoffsetn(mid); | ||
| 433 | } | ||
| 434 | }); | ||
| 435 | |||
| 436 | // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize | ||
| 437 | // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 | ||
| 438 | blocking_delay_ms(2); | ||
| 439 | |||
| 440 | if !T::regs().csr().read().calout() { | ||
| 441 | if mid == 0 { | ||
| 442 | break; | ||
| 443 | } | ||
| 444 | high = mid - 1; | ||
| 445 | } else { | ||
| 446 | if mid == 31 { | ||
| 447 | break; | ||
| 448 | } | ||
| 449 | low = mid + 1; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | } | ||
| 178 | } | 453 | } |
| 179 | 454 | ||
| 180 | impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { | 455 | impl<'d, T: Instance> Drop for OpAmpOutput<'d, T> { |
| @@ -211,7 +486,7 @@ pub(crate) trait SealedOutputPin<T: Instance> {} | |||
| 211 | 486 | ||
| 212 | /// Opamp instance trait. | 487 | /// Opamp instance trait. |
| 213 | #[allow(private_bounds)] | 488 | #[allow(private_bounds)] |
| 214 | pub trait Instance: SealedInstance + 'static {} | 489 | pub trait Instance: SealedInstance + PeripheralType + 'static {} |
| 215 | /// Non-inverting pin trait. | 490 | /// Non-inverting pin trait. |
| 216 | #[allow(private_bounds)] | 491 | #[allow(private_bounds)] |
| 217 | pub trait NonInvertingPin<T: Instance>: SealedNonInvertingPin<T> {} | 492 | pub trait NonInvertingPin<T: Instance>: SealedNonInvertingPin<T> {} |
| @@ -251,10 +526,12 @@ foreach_peripheral!( | |||
| 251 | impl_opamp_external_output!(OPAMP2, ADC2, 3); | 526 | impl_opamp_external_output!(OPAMP2, ADC2, 3); |
| 252 | }; | 527 | }; |
| 253 | (opamp, OPAMP3) => { | 528 | (opamp, OPAMP3) => { |
| 529 | impl_opamp_external_output!(OPAMP3, ADC1, 12); | ||
| 254 | impl_opamp_external_output!(OPAMP3, ADC3, 1); | 530 | impl_opamp_external_output!(OPAMP3, ADC3, 1); |
| 255 | }; | 531 | }; |
| 256 | // OPAMP4 only in STM32G4 Cat 3 devices | 532 | // OPAMP4 only in STM32G4 Cat 3 devices |
| 257 | (opamp, OPAMP4) => { | 533 | (opamp, OPAMP4) => { |
| 534 | impl_opamp_external_output!(OPAMP4, ADC1, 11); | ||
| 258 | impl_opamp_external_output!(OPAMP4, ADC4, 3); | 535 | impl_opamp_external_output!(OPAMP4, ADC4, 3); |
| 259 | }; | 536 | }; |
| 260 | // OPAMP5 only in STM32G4 Cat 3 devices | 537 | // OPAMP5 only in STM32G4 Cat 3 devices |
| @@ -264,6 +541,7 @@ foreach_peripheral!( | |||
| 264 | // OPAMP6 only in STM32G4 Cat 3/4 devices | 541 | // OPAMP6 only in STM32G4 Cat 3/4 devices |
| 265 | (opamp, OPAMP6) => { | 542 | (opamp, OPAMP6) => { |
| 266 | impl_opamp_external_output!(OPAMP6, ADC1, 14); | 543 | impl_opamp_external_output!(OPAMP6, ADC1, 14); |
| 544 | impl_opamp_external_output!(OPAMP6, ADC2, 14); | ||
| 267 | }; | 545 | }; |
| 268 | ); | 546 | ); |
| 269 | 547 | ||
| @@ -345,6 +623,18 @@ macro_rules! impl_opamp_vp_pin { | |||
| 345 | } | 623 | } |
| 346 | 624 | ||
| 347 | #[allow(unused_macros)] | 625 | #[allow(unused_macros)] |
| 626 | macro_rules! impl_opamp_vn_pin { | ||
| 627 | ($inst:ident, $pin:ident, $ch:expr) => { | ||
| 628 | impl crate::opamp::InvertingPin<peripherals::$inst> for crate::peripherals::$pin {} | ||
| 629 | impl crate::opamp::SealedInvertingPin<peripherals::$inst> for crate::peripherals::$pin { | ||
| 630 | fn channel(&self) -> u8 { | ||
| 631 | $ch | ||
| 632 | } | ||
| 633 | } | ||
| 634 | }; | ||
| 635 | } | ||
| 636 | |||
| 637 | #[allow(unused_macros)] | ||
| 348 | macro_rules! impl_opamp_vout_pin { | 638 | macro_rules! impl_opamp_vout_pin { |
| 349 | ($inst:ident, $pin:ident) => { | 639 | ($inst:ident, $pin:ident) => { |
| 350 | impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {} | 640 | impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {} |
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 289bfa672..74edfd5e4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs | |||
| @@ -8,7 +8,7 @@ pub mod enums; | |||
| 8 | use core::marker::PhantomData; | 8 | use core::marker::PhantomData; |
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::{GetConfig, SetConfig}; | 10 | use embassy_embedded_hal::{GetConfig, SetConfig}; |
| 11 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 11 | use embassy_hal_internal::PeripheralType; |
| 12 | pub use enums::*; | 12 | pub use enums::*; |
| 13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; | 13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; |
| 14 | 14 | ||
| @@ -16,8 +16,10 @@ use crate::dma::{word, ChannelAndRequest}; | |||
| 16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 18 | use crate::pac::octospi::{vals, Octospi as Regs}; | 18 | use crate::pac::octospi::{vals, Octospi as Regs}; |
| 19 | #[cfg(octospim_v1)] | ||
| 20 | use crate::pac::octospim::Octospim; | ||
| 19 | use crate::rcc::{self, RccPeripheral}; | 21 | use crate::rcc::{self, RccPeripheral}; |
| 20 | use crate::{peripherals, Peripheral}; | 22 | use crate::{peripherals, Peri}; |
| 21 | 23 | ||
| 22 | /// OPSI driver config. | 24 | /// OPSI driver config. |
| 23 | #[derive(Clone, Copy)] | 25 | #[derive(Clone, Copy)] |
| @@ -50,7 +52,7 @@ pub struct Config { | |||
| 50 | /// Enables the transaction boundary feature and defines the boundary to release | 52 | /// Enables the transaction boundary feature and defines the boundary to release |
| 51 | /// the chip select | 53 | /// the chip select |
| 52 | pub chip_select_boundary: u8, | 54 | pub chip_select_boundary: u8, |
| 53 | /// Enbales the delay block bypass so the sampling is not affected by the delay block | 55 | /// Enables the delay block bypass so the sampling is not affected by the delay block |
| 54 | pub delay_block_bypass: bool, | 56 | pub delay_block_bypass: bool, |
| 55 | /// Enables communication regulation feature. Chip select is released when the other | 57 | /// Enables communication regulation feature. Chip select is released when the other |
| 56 | /// OctoSpi requests access to the bus | 58 | /// OctoSpi requests access to the bus |
| @@ -158,18 +160,18 @@ pub enum OspiError { | |||
| 158 | 160 | ||
| 159 | /// OSPI driver. | 161 | /// OSPI driver. |
| 160 | pub struct Ospi<'d, T: Instance, M: PeriMode> { | 162 | pub struct Ospi<'d, T: Instance, M: PeriMode> { |
| 161 | _peri: PeripheralRef<'d, T>, | 163 | _peri: Peri<'d, T>, |
| 162 | sck: Option<PeripheralRef<'d, AnyPin>>, | 164 | sck: Option<Peri<'d, AnyPin>>, |
| 163 | d0: Option<PeripheralRef<'d, AnyPin>>, | 165 | d0: Option<Peri<'d, AnyPin>>, |
| 164 | d1: Option<PeripheralRef<'d, AnyPin>>, | 166 | d1: Option<Peri<'d, AnyPin>>, |
| 165 | d2: Option<PeripheralRef<'d, AnyPin>>, | 167 | d2: Option<Peri<'d, AnyPin>>, |
| 166 | d3: Option<PeripheralRef<'d, AnyPin>>, | 168 | d3: Option<Peri<'d, AnyPin>>, |
| 167 | d4: Option<PeripheralRef<'d, AnyPin>>, | 169 | d4: Option<Peri<'d, AnyPin>>, |
| 168 | d5: Option<PeripheralRef<'d, AnyPin>>, | 170 | d5: Option<Peri<'d, AnyPin>>, |
| 169 | d6: Option<PeripheralRef<'d, AnyPin>>, | 171 | d6: Option<Peri<'d, AnyPin>>, |
| 170 | d7: Option<PeripheralRef<'d, AnyPin>>, | 172 | d7: Option<Peri<'d, AnyPin>>, |
| 171 | nss: Option<PeripheralRef<'d, AnyPin>>, | 173 | nss: Option<Peri<'d, AnyPin>>, |
| 172 | dqs: Option<PeripheralRef<'d, AnyPin>>, | 174 | dqs: Option<Peri<'d, AnyPin>>, |
| 173 | dma: Option<ChannelAndRequest<'d>>, | 175 | dma: Option<ChannelAndRequest<'d>>, |
| 174 | _phantom: PhantomData<M>, | 176 | _phantom: PhantomData<M>, |
| 175 | config: Config, | 177 | config: Config, |
| @@ -177,25 +179,165 @@ pub struct Ospi<'d, T: Instance, M: PeriMode> { | |||
| 177 | } | 179 | } |
| 178 | 180 | ||
| 179 | impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | 181 | impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { |
| 182 | /// Enter memory mode. | ||
| 183 | /// The Input `read_config` is used to configure the read operation in memory mode | ||
| 184 | pub fn enable_memory_mapped_mode( | ||
| 185 | &mut self, | ||
| 186 | read_config: TransferConfig, | ||
| 187 | write_config: TransferConfig, | ||
| 188 | ) -> Result<(), OspiError> { | ||
| 189 | // Use configure command to set read config | ||
| 190 | self.configure_command(&read_config, None)?; | ||
| 191 | |||
| 192 | let reg = T::REGS; | ||
| 193 | while reg.sr().read().busy() {} | ||
| 194 | |||
| 195 | reg.ccr().modify(|r| { | ||
| 196 | r.set_dqse(false); | ||
| 197 | r.set_sioo(true); | ||
| 198 | }); | ||
| 199 | |||
| 200 | // Set wrting configurations, there are separate registers for write configurations in memory mapped mode | ||
| 201 | reg.wccr().modify(|w| { | ||
| 202 | w.set_imode(PhaseMode::from_bits(write_config.iwidth.into())); | ||
| 203 | w.set_idtr(write_config.idtr); | ||
| 204 | w.set_isize(SizeInBits::from_bits(write_config.isize.into())); | ||
| 205 | |||
| 206 | w.set_admode(PhaseMode::from_bits(write_config.adwidth.into())); | ||
| 207 | w.set_addtr(write_config.idtr); | ||
| 208 | w.set_adsize(SizeInBits::from_bits(write_config.adsize.into())); | ||
| 209 | |||
| 210 | w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into())); | ||
| 211 | w.set_ddtr(write_config.ddtr); | ||
| 212 | |||
| 213 | w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into())); | ||
| 214 | w.set_dqse(true); | ||
| 215 | }); | ||
| 216 | |||
| 217 | reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); | ||
| 218 | |||
| 219 | // Enable memory mapped mode | ||
| 220 | reg.cr().modify(|r| { | ||
| 221 | r.set_fmode(crate::ospi::vals::FunctionalMode::MEMORY_MAPPED); | ||
| 222 | r.set_tcen(false); | ||
| 223 | }); | ||
| 224 | Ok(()) | ||
| 225 | } | ||
| 226 | |||
| 227 | /// Quit from memory mapped mode | ||
| 228 | pub fn disable_memory_mapped_mode(&mut self) { | ||
| 229 | let reg = T::REGS; | ||
| 230 | |||
| 231 | reg.cr().modify(|r| { | ||
| 232 | r.set_fmode(crate::ospi::vals::FunctionalMode::INDIRECT_WRITE); | ||
| 233 | r.set_abort(true); | ||
| 234 | r.set_dmaen(false); | ||
| 235 | r.set_en(false); | ||
| 236 | }); | ||
| 237 | |||
| 238 | // Clear transfer complete flag | ||
| 239 | reg.fcr().write(|w| w.set_ctcf(true)); | ||
| 240 | |||
| 241 | // Re-enable ospi | ||
| 242 | reg.cr().modify(|r| { | ||
| 243 | r.set_en(true); | ||
| 244 | }); | ||
| 245 | } | ||
| 246 | |||
| 180 | fn new_inner( | 247 | fn new_inner( |
| 181 | peri: impl Peripheral<P = T> + 'd, | 248 | peri: Peri<'d, T>, |
| 182 | d0: Option<PeripheralRef<'d, AnyPin>>, | 249 | d0: Option<Peri<'d, AnyPin>>, |
| 183 | d1: Option<PeripheralRef<'d, AnyPin>>, | 250 | d1: Option<Peri<'d, AnyPin>>, |
| 184 | d2: Option<PeripheralRef<'d, AnyPin>>, | 251 | d2: Option<Peri<'d, AnyPin>>, |
| 185 | d3: Option<PeripheralRef<'d, AnyPin>>, | 252 | d3: Option<Peri<'d, AnyPin>>, |
| 186 | d4: Option<PeripheralRef<'d, AnyPin>>, | 253 | d4: Option<Peri<'d, AnyPin>>, |
| 187 | d5: Option<PeripheralRef<'d, AnyPin>>, | 254 | d5: Option<Peri<'d, AnyPin>>, |
| 188 | d6: Option<PeripheralRef<'d, AnyPin>>, | 255 | d6: Option<Peri<'d, AnyPin>>, |
| 189 | d7: Option<PeripheralRef<'d, AnyPin>>, | 256 | d7: Option<Peri<'d, AnyPin>>, |
| 190 | sck: Option<PeripheralRef<'d, AnyPin>>, | 257 | sck: Option<Peri<'d, AnyPin>>, |
| 191 | nss: Option<PeripheralRef<'d, AnyPin>>, | 258 | nss: Option<Peri<'d, AnyPin>>, |
| 192 | dqs: Option<PeripheralRef<'d, AnyPin>>, | 259 | dqs: Option<Peri<'d, AnyPin>>, |
| 193 | dma: Option<ChannelAndRequest<'d>>, | 260 | dma: Option<ChannelAndRequest<'d>>, |
| 194 | config: Config, | 261 | config: Config, |
| 195 | width: OspiWidth, | 262 | width: OspiWidth, |
| 196 | dual_quad: bool, | 263 | dual_quad: bool, |
| 197 | ) -> Self { | 264 | ) -> Self { |
| 198 | into_ref!(peri); | 265 | #[cfg(octospim_v1)] |
| 266 | { | ||
| 267 | // RCC for octospim should be enabled before writing register | ||
| 268 | #[cfg(stm32l4)] | ||
| 269 | crate::pac::RCC.ahb2smenr().modify(|w| w.set_octospimsmen(true)); | ||
| 270 | #[cfg(stm32u5)] | ||
| 271 | crate::pac::RCC.ahb2enr1().modify(|w| w.set_octospimen(true)); | ||
| 272 | #[cfg(not(any(stm32l4, stm32u5)))] | ||
| 273 | crate::pac::RCC.ahb3enr().modify(|w| w.set_iomngren(true)); | ||
| 274 | |||
| 275 | // Disable OctoSPI peripheral first | ||
| 276 | T::REGS.cr().modify(|w| { | ||
| 277 | w.set_en(false); | ||
| 278 | }); | ||
| 279 | |||
| 280 | // OctoSPI IO Manager has been enabled before | ||
| 281 | T::OCTOSPIM_REGS.cr().modify(|w| { | ||
| 282 | w.set_muxen(false); | ||
| 283 | w.set_req2ack_time(0xff); | ||
| 284 | }); | ||
| 285 | |||
| 286 | // Clear config | ||
| 287 | T::OCTOSPIM_REGS.p1cr().modify(|w| { | ||
| 288 | w.set_clksrc(false); | ||
| 289 | w.set_dqssrc(false); | ||
| 290 | w.set_ncssrc(false); | ||
| 291 | w.set_clken(false); | ||
| 292 | w.set_dqsen(false); | ||
| 293 | w.set_ncsen(false); | ||
| 294 | w.set_iolsrc(0); | ||
| 295 | w.set_iohsrc(0); | ||
| 296 | }); | ||
| 297 | |||
| 298 | T::OCTOSPIM_REGS.p1cr().modify(|w| { | ||
| 299 | let octospi_src = if T::OCTOSPI_IDX == 1 { false } else { true }; | ||
| 300 | w.set_ncsen(true); | ||
| 301 | w.set_ncssrc(octospi_src); | ||
| 302 | w.set_clken(true); | ||
| 303 | w.set_clksrc(octospi_src); | ||
| 304 | if dqs.is_some() { | ||
| 305 | w.set_dqsen(true); | ||
| 306 | w.set_dqssrc(octospi_src); | ||
| 307 | } | ||
| 308 | |||
| 309 | // Set OCTOSPIM IOL and IOH according to the index of OCTOSPI instance | ||
| 310 | if T::OCTOSPI_IDX == 1 { | ||
| 311 | w.set_iolen(true); | ||
| 312 | w.set_iolsrc(0); | ||
| 313 | // Enable IOH in octo and dual quad mode | ||
| 314 | if let OspiWidth::OCTO = width { | ||
| 315 | w.set_iohen(true); | ||
| 316 | w.set_iohsrc(0b01); | ||
| 317 | } else if dual_quad { | ||
| 318 | w.set_iohen(true); | ||
| 319 | w.set_iohsrc(0b00); | ||
| 320 | } else { | ||
| 321 | w.set_iohen(false); | ||
| 322 | w.set_iohsrc(0b00); | ||
| 323 | } | ||
| 324 | } else { | ||
| 325 | w.set_iolen(true); | ||
| 326 | w.set_iolsrc(0b10); | ||
| 327 | // Enable IOH in octo and dual quad mode | ||
| 328 | if let OspiWidth::OCTO = width { | ||
| 329 | w.set_iohen(true); | ||
| 330 | w.set_iohsrc(0b11); | ||
| 331 | } else if dual_quad { | ||
| 332 | w.set_iohen(true); | ||
| 333 | w.set_iohsrc(0b10); | ||
| 334 | } else { | ||
| 335 | w.set_iohen(false); | ||
| 336 | w.set_iohsrc(0b00); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | }); | ||
| 340 | } | ||
| 199 | 341 | ||
| 200 | // System configuration | 342 | // System configuration |
| 201 | rcc::enable_and_reset::<T>(); | 343 | rcc::enable_and_reset::<T>(); |
| @@ -228,7 +370,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 228 | }); | 370 | }); |
| 229 | 371 | ||
| 230 | T::REGS.cr().modify(|w| { | 372 | T::REGS.cr().modify(|w| { |
| 231 | w.set_fthres(vals::Threshold(config.fifo_threshold.into())); | 373 | w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into())); |
| 232 | }); | 374 | }); |
| 233 | 375 | ||
| 234 | // Wait for busy flag to clear | 376 | // Wait for busy flag to clear |
| @@ -244,7 +386,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 244 | 386 | ||
| 245 | T::REGS.tcr().modify(|w| { | 387 | T::REGS.tcr().modify(|w| { |
| 246 | w.set_sshift(match config.sample_shifting { | 388 | w.set_sshift(match config.sample_shifting { |
| 247 | true => vals::SampleShift::HALFCYCLE, | 389 | true => vals::SampleShift::HALF_CYCLE, |
| 248 | false => vals::SampleShift::NONE, | 390 | false => vals::SampleShift::NONE, |
| 249 | }); | 391 | }); |
| 250 | w.set_dhqc(config.delay_hold_quarter_cycle); | 392 | w.set_dhqc(config.delay_hold_quarter_cycle); |
| @@ -376,7 +518,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 376 | } | 518 | } |
| 377 | 519 | ||
| 378 | /// Function used to control or configure the target device without data transfer | 520 | /// Function used to control or configure the target device without data transfer |
| 379 | pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { | 521 | pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { |
| 380 | // Wait for peripheral to be free | 522 | // Wait for peripheral to be free |
| 381 | while T::REGS.sr().read().busy() {} | 523 | while T::REGS.sr().read().busy() {} |
| 382 | 524 | ||
| @@ -412,7 +554,9 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 412 | let current_instruction = T::REGS.ir().read().instruction(); | 554 | let current_instruction = T::REGS.ir().read().instruction(); |
| 413 | 555 | ||
| 414 | // For a indirect read transaction, the transaction begins when the instruction/address is set | 556 | // For a indirect read transaction, the transaction begins when the instruction/address is set |
| 415 | T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); | 557 | T::REGS |
| 558 | .cr() | ||
| 559 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ)); | ||
| 416 | if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { | 560 | if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { |
| 417 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | 561 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); |
| 418 | } else { | 562 | } else { |
| @@ -447,7 +591,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 447 | 591 | ||
| 448 | T::REGS | 592 | T::REGS |
| 449 | .cr() | 593 | .cr() |
| 450 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); | 594 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 451 | 595 | ||
| 452 | for idx in 0..buf.len() { | 596 | for idx in 0..buf.len() { |
| 453 | while !T::REGS.sr().read().ftf() {} | 597 | while !T::REGS.sr().read().ftf() {} |
| @@ -497,7 +641,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 497 | }); | 641 | }); |
| 498 | 642 | ||
| 499 | T::REGS.cr().modify(|w| { | 643 | T::REGS.cr().modify(|w| { |
| 500 | w.set_fthres(vals::Threshold(config.fifo_threshold.into())); | 644 | w.set_fthres(vals::Threshold::from_bits(config.fifo_threshold.into())); |
| 501 | }); | 645 | }); |
| 502 | 646 | ||
| 503 | // Wait for busy flag to clear | 647 | // Wait for busy flag to clear |
| @@ -509,7 +653,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 509 | 653 | ||
| 510 | T::REGS.tcr().modify(|w| { | 654 | T::REGS.tcr().modify(|w| { |
| 511 | w.set_sshift(match config.sample_shifting { | 655 | w.set_sshift(match config.sample_shifting { |
| 512 | true => vals::SampleShift::HALFCYCLE, | 656 | true => vals::SampleShift::HALF_CYCLE, |
| 513 | false => vals::SampleShift::NONE, | 657 | false => vals::SampleShift::NONE, |
| 514 | }); | 658 | }); |
| 515 | w.set_dhqc(config.delay_hold_quarter_cycle); | 659 | w.set_dhqc(config.delay_hold_quarter_cycle); |
| @@ -539,11 +683,11 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { | |||
| 539 | impl<'d, T: Instance> Ospi<'d, T, Blocking> { | 683 | impl<'d, T: Instance> Ospi<'d, T, Blocking> { |
| 540 | /// Create new blocking OSPI driver for a single spi external chip | 684 | /// Create new blocking OSPI driver for a single spi external chip |
| 541 | pub fn new_blocking_singlespi( | 685 | pub fn new_blocking_singlespi( |
| 542 | peri: impl Peripheral<P = T> + 'd, | 686 | peri: Peri<'d, T>, |
| 543 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 687 | sck: Peri<'d, impl SckPin<T>>, |
| 544 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 688 | d0: Peri<'d, impl D0Pin<T>>, |
| 545 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 689 | d1: Peri<'d, impl D1Pin<T>>, |
| 546 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 690 | nss: Peri<'d, impl NSSPin<T>>, |
| 547 | config: Config, | 691 | config: Config, |
| 548 | ) -> Self { | 692 | ) -> Self { |
| 549 | Self::new_inner( | 693 | Self::new_inner( |
| @@ -571,11 +715,11 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { | |||
| 571 | 715 | ||
| 572 | /// Create new blocking OSPI driver for a dualspi external chip | 716 | /// Create new blocking OSPI driver for a dualspi external chip |
| 573 | pub fn new_blocking_dualspi( | 717 | pub fn new_blocking_dualspi( |
| 574 | peri: impl Peripheral<P = T> + 'd, | 718 | peri: Peri<'d, T>, |
| 575 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 719 | sck: Peri<'d, impl SckPin<T>>, |
| 576 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 720 | d0: Peri<'d, impl D0Pin<T>>, |
| 577 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 721 | d1: Peri<'d, impl D1Pin<T>>, |
| 578 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 722 | nss: Peri<'d, impl NSSPin<T>>, |
| 579 | config: Config, | 723 | config: Config, |
| 580 | ) -> Self { | 724 | ) -> Self { |
| 581 | Self::new_inner( | 725 | Self::new_inner( |
| @@ -603,13 +747,13 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { | |||
| 603 | 747 | ||
| 604 | /// Create new blocking OSPI driver for a quadspi external chip | 748 | /// Create new blocking OSPI driver for a quadspi external chip |
| 605 | pub fn new_blocking_quadspi( | 749 | pub fn new_blocking_quadspi( |
| 606 | peri: impl Peripheral<P = T> + 'd, | 750 | peri: Peri<'d, T>, |
| 607 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 751 | sck: Peri<'d, impl SckPin<T>>, |
| 608 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 752 | d0: Peri<'d, impl D0Pin<T>>, |
| 609 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 753 | d1: Peri<'d, impl D1Pin<T>>, |
| 610 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 754 | d2: Peri<'d, impl D2Pin<T>>, |
| 611 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 755 | d3: Peri<'d, impl D3Pin<T>>, |
| 612 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 756 | nss: Peri<'d, impl NSSPin<T>>, |
| 613 | config: Config, | 757 | config: Config, |
| 614 | ) -> Self { | 758 | ) -> Self { |
| 615 | Self::new_inner( | 759 | Self::new_inner( |
| @@ -637,17 +781,17 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { | |||
| 637 | 781 | ||
| 638 | /// Create new blocking OSPI driver for two quadspi external chips | 782 | /// Create new blocking OSPI driver for two quadspi external chips |
| 639 | pub fn new_blocking_dualquadspi( | 783 | pub fn new_blocking_dualquadspi( |
| 640 | peri: impl Peripheral<P = T> + 'd, | 784 | peri: Peri<'d, T>, |
| 641 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 785 | sck: Peri<'d, impl SckPin<T>>, |
| 642 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 786 | d0: Peri<'d, impl D0Pin<T>>, |
| 643 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 787 | d1: Peri<'d, impl D1Pin<T>>, |
| 644 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 788 | d2: Peri<'d, impl D2Pin<T>>, |
| 645 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 789 | d3: Peri<'d, impl D3Pin<T>>, |
| 646 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 790 | d4: Peri<'d, impl D4Pin<T>>, |
| 647 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 791 | d5: Peri<'d, impl D5Pin<T>>, |
| 648 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 792 | d6: Peri<'d, impl D6Pin<T>>, |
| 649 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 793 | d7: Peri<'d, impl D7Pin<T>>, |
| 650 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 794 | nss: Peri<'d, impl NSSPin<T>>, |
| 651 | config: Config, | 795 | config: Config, |
| 652 | ) -> Self { | 796 | ) -> Self { |
| 653 | Self::new_inner( | 797 | Self::new_inner( |
| @@ -675,17 +819,17 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { | |||
| 675 | 819 | ||
| 676 | /// Create new blocking OSPI driver for octospi external chips | 820 | /// Create new blocking OSPI driver for octospi external chips |
| 677 | pub fn new_blocking_octospi( | 821 | pub fn new_blocking_octospi( |
| 678 | peri: impl Peripheral<P = T> + 'd, | 822 | peri: Peri<'d, T>, |
| 679 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 823 | sck: Peri<'d, impl SckPin<T>>, |
| 680 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 824 | d0: Peri<'d, impl D0Pin<T>>, |
| 681 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 825 | d1: Peri<'d, impl D1Pin<T>>, |
| 682 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 826 | d2: Peri<'d, impl D2Pin<T>>, |
| 683 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 827 | d3: Peri<'d, impl D3Pin<T>>, |
| 684 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 828 | d4: Peri<'d, impl D4Pin<T>>, |
| 685 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 829 | d5: Peri<'d, impl D5Pin<T>>, |
| 686 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 830 | d6: Peri<'d, impl D6Pin<T>>, |
| 687 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 831 | d7: Peri<'d, impl D7Pin<T>>, |
| 688 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 832 | nss: Peri<'d, impl NSSPin<T>>, |
| 689 | config: Config, | 833 | config: Config, |
| 690 | ) -> Self { | 834 | ) -> Self { |
| 691 | Self::new_inner( | 835 | Self::new_inner( |
| @@ -715,12 +859,12 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { | |||
| 715 | impl<'d, T: Instance> Ospi<'d, T, Async> { | 859 | impl<'d, T: Instance> Ospi<'d, T, Async> { |
| 716 | /// Create new blocking OSPI driver for a single spi external chip | 860 | /// Create new blocking OSPI driver for a single spi external chip |
| 717 | pub fn new_singlespi( | 861 | pub fn new_singlespi( |
| 718 | peri: impl Peripheral<P = T> + 'd, | 862 | peri: Peri<'d, T>, |
| 719 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 863 | sck: Peri<'d, impl SckPin<T>>, |
| 720 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 864 | d0: Peri<'d, impl D0Pin<T>>, |
| 721 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 865 | d1: Peri<'d, impl D1Pin<T>>, |
| 722 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 866 | nss: Peri<'d, impl NSSPin<T>>, |
| 723 | dma: impl Peripheral<P = impl OctoDma<T>> + 'd, | 867 | dma: Peri<'d, impl OctoDma<T>>, |
| 724 | config: Config, | 868 | config: Config, |
| 725 | ) -> Self { | 869 | ) -> Self { |
| 726 | Self::new_inner( | 870 | Self::new_inner( |
| @@ -748,12 +892,12 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 748 | 892 | ||
| 749 | /// Create new blocking OSPI driver for a dualspi external chip | 893 | /// Create new blocking OSPI driver for a dualspi external chip |
| 750 | pub fn new_dualspi( | 894 | pub fn new_dualspi( |
| 751 | peri: impl Peripheral<P = T> + 'd, | 895 | peri: Peri<'d, T>, |
| 752 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 896 | sck: Peri<'d, impl SckPin<T>>, |
| 753 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 897 | d0: Peri<'d, impl D0Pin<T>>, |
| 754 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 898 | d1: Peri<'d, impl D1Pin<T>>, |
| 755 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 899 | nss: Peri<'d, impl NSSPin<T>>, |
| 756 | dma: impl Peripheral<P = impl OctoDma<T>> + 'd, | 900 | dma: Peri<'d, impl OctoDma<T>>, |
| 757 | config: Config, | 901 | config: Config, |
| 758 | ) -> Self { | 902 | ) -> Self { |
| 759 | Self::new_inner( | 903 | Self::new_inner( |
| @@ -781,14 +925,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 781 | 925 | ||
| 782 | /// Create new blocking OSPI driver for a quadspi external chip | 926 | /// Create new blocking OSPI driver for a quadspi external chip |
| 783 | pub fn new_quadspi( | 927 | pub fn new_quadspi( |
| 784 | peri: impl Peripheral<P = T> + 'd, | 928 | peri: Peri<'d, T>, |
| 785 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 929 | sck: Peri<'d, impl SckPin<T>>, |
| 786 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 930 | d0: Peri<'d, impl D0Pin<T>>, |
| 787 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 931 | d1: Peri<'d, impl D1Pin<T>>, |
| 788 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 932 | d2: Peri<'d, impl D2Pin<T>>, |
| 789 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 933 | d3: Peri<'d, impl D3Pin<T>>, |
| 790 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 934 | nss: Peri<'d, impl NSSPin<T>>, |
| 791 | dma: impl Peripheral<P = impl OctoDma<T>> + 'd, | 935 | dma: Peri<'d, impl OctoDma<T>>, |
| 792 | config: Config, | 936 | config: Config, |
| 793 | ) -> Self { | 937 | ) -> Self { |
| 794 | Self::new_inner( | 938 | Self::new_inner( |
| @@ -816,18 +960,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 816 | 960 | ||
| 817 | /// Create new blocking OSPI driver for two quadspi external chips | 961 | /// Create new blocking OSPI driver for two quadspi external chips |
| 818 | pub fn new_dualquadspi( | 962 | pub fn new_dualquadspi( |
| 819 | peri: impl Peripheral<P = T> + 'd, | 963 | peri: Peri<'d, T>, |
| 820 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 964 | sck: Peri<'d, impl SckPin<T>>, |
| 821 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 965 | d0: Peri<'d, impl D0Pin<T>>, |
| 822 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 966 | d1: Peri<'d, impl D1Pin<T>>, |
| 823 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 967 | d2: Peri<'d, impl D2Pin<T>>, |
| 824 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 968 | d3: Peri<'d, impl D3Pin<T>>, |
| 825 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 969 | d4: Peri<'d, impl D4Pin<T>>, |
| 826 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 970 | d5: Peri<'d, impl D5Pin<T>>, |
| 827 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 971 | d6: Peri<'d, impl D6Pin<T>>, |
| 828 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 972 | d7: Peri<'d, impl D7Pin<T>>, |
| 829 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 973 | nss: Peri<'d, impl NSSPin<T>>, |
| 830 | dma: impl Peripheral<P = impl OctoDma<T>> + 'd, | 974 | dma: Peri<'d, impl OctoDma<T>>, |
| 831 | config: Config, | 975 | config: Config, |
| 832 | ) -> Self { | 976 | ) -> Self { |
| 833 | Self::new_inner( | 977 | Self::new_inner( |
| @@ -855,18 +999,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 855 | 999 | ||
| 856 | /// Create new blocking OSPI driver for octospi external chips | 1000 | /// Create new blocking OSPI driver for octospi external chips |
| 857 | pub fn new_octospi( | 1001 | pub fn new_octospi( |
| 858 | peri: impl Peripheral<P = T> + 'd, | 1002 | peri: Peri<'d, T>, |
| 859 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 1003 | sck: Peri<'d, impl SckPin<T>>, |
| 860 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 1004 | d0: Peri<'d, impl D0Pin<T>>, |
| 861 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 1005 | d1: Peri<'d, impl D1Pin<T>>, |
| 862 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 1006 | d2: Peri<'d, impl D2Pin<T>>, |
| 863 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 1007 | d3: Peri<'d, impl D3Pin<T>>, |
| 864 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, | 1008 | d4: Peri<'d, impl D4Pin<T>>, |
| 865 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, | 1009 | d5: Peri<'d, impl D5Pin<T>>, |
| 866 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, | 1010 | d6: Peri<'d, impl D6Pin<T>>, |
| 867 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, | 1011 | d7: Peri<'d, impl D7Pin<T>>, |
| 868 | nss: impl Peripheral<P = impl NSSPin<T>> + 'd, | 1012 | nss: Peri<'d, impl NSSPin<T>>, |
| 869 | dma: impl Peripheral<P = impl OctoDma<T>> + 'd, | 1013 | dma: Peri<'d, impl OctoDma<T>>, |
| 870 | config: Config, | 1014 | config: Config, |
| 871 | ) -> Self { | 1015 | ) -> Self { |
| 872 | Self::new_inner( | 1016 | Self::new_inner( |
| @@ -907,7 +1051,9 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 907 | let current_instruction = T::REGS.ir().read().instruction(); | 1051 | let current_instruction = T::REGS.ir().read().instruction(); |
| 908 | 1052 | ||
| 909 | // For a indirect read transaction, the transaction begins when the instruction/address is set | 1053 | // For a indirect read transaction, the transaction begins when the instruction/address is set |
| 910 | T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); | 1054 | T::REGS |
| 1055 | .cr() | ||
| 1056 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ)); | ||
| 911 | if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { | 1057 | if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { |
| 912 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | 1058 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); |
| 913 | } else { | 1059 | } else { |
| @@ -942,7 +1088,7 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 942 | self.configure_command(&transaction, Some(buf.len()))?; | 1088 | self.configure_command(&transaction, Some(buf.len()))?; |
| 943 | T::REGS | 1089 | T::REGS |
| 944 | .cr() | 1090 | .cr() |
| 945 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); | 1091 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 946 | 1092 | ||
| 947 | let transfer = unsafe { | 1093 | let transfer = unsafe { |
| 948 | self.dma | 1094 | self.dma |
| @@ -975,7 +1121,9 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 975 | let current_instruction = T::REGS.ir().read().instruction(); | 1121 | let current_instruction = T::REGS.ir().read().instruction(); |
| 976 | 1122 | ||
| 977 | // For a indirect read transaction, the transaction begins when the instruction/address is set | 1123 | // For a indirect read transaction, the transaction begins when the instruction/address is set |
| 978 | T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); | 1124 | T::REGS |
| 1125 | .cr() | ||
| 1126 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ)); | ||
| 979 | if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { | 1127 | if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { |
| 980 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | 1128 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); |
| 981 | } else { | 1129 | } else { |
| @@ -1010,7 +1158,7 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { | |||
| 1010 | self.configure_command(&transaction, Some(buf.len()))?; | 1158 | self.configure_command(&transaction, Some(buf.len()))?; |
| 1011 | T::REGS | 1159 | T::REGS |
| 1012 | .cr() | 1160 | .cr() |
| 1013 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); | 1161 | .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); |
| 1014 | 1162 | ||
| 1015 | let transfer = unsafe { | 1163 | let transfer = unsafe { |
| 1016 | self.dma | 1164 | self.dma |
| @@ -1056,13 +1204,27 @@ fn finish_dma(regs: Regs) { | |||
| 1056 | }); | 1204 | }); |
| 1057 | } | 1205 | } |
| 1058 | 1206 | ||
| 1207 | #[cfg(octospim_v1)] | ||
| 1208 | /// OctoSPI I/O manager instance trait. | ||
| 1209 | pub(crate) trait SealedOctospimInstance { | ||
| 1210 | const OCTOSPIM_REGS: Octospim; | ||
| 1211 | const OCTOSPI_IDX: u8; | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | /// OctoSPI instance trait. | ||
| 1059 | pub(crate) trait SealedInstance { | 1215 | pub(crate) trait SealedInstance { |
| 1060 | const REGS: Regs; | 1216 | const REGS: Regs; |
| 1061 | } | 1217 | } |
| 1062 | 1218 | ||
| 1063 | /// OSPI instance trait. | 1219 | /// OSPI instance trait. |
| 1220 | #[cfg(octospim_v1)] | ||
| 1221 | #[allow(private_bounds)] | ||
| 1222 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + SealedOctospimInstance {} | ||
| 1223 | |||
| 1224 | /// OSPI instance trait. | ||
| 1225 | #[cfg(not(octospim_v1))] | ||
| 1064 | #[allow(private_bounds)] | 1226 | #[allow(private_bounds)] |
| 1065 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} | 1227 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {} |
| 1066 | 1228 | ||
| 1067 | pin_trait!(SckPin, Instance); | 1229 | pin_trait!(SckPin, Instance); |
| 1068 | pin_trait!(NckPin, Instance); | 1230 | pin_trait!(NckPin, Instance); |
| @@ -1078,6 +1240,31 @@ pin_trait!(DQSPin, Instance); | |||
| 1078 | pin_trait!(NSSPin, Instance); | 1240 | pin_trait!(NSSPin, Instance); |
| 1079 | dma_trait!(OctoDma, Instance); | 1241 | dma_trait!(OctoDma, Instance); |
| 1080 | 1242 | ||
| 1243 | // Hard-coded the octospi index, for OCTOSPIM | ||
| 1244 | #[cfg(octospim_v1)] | ||
| 1245 | impl SealedOctospimInstance for peripherals::OCTOSPI1 { | ||
| 1246 | const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM; | ||
| 1247 | const OCTOSPI_IDX: u8 = 1; | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | #[cfg(all(octospim_v1, peri_octospi2))] | ||
| 1251 | impl SealedOctospimInstance for peripherals::OCTOSPI2 { | ||
| 1252 | const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM; | ||
| 1253 | const OCTOSPI_IDX: u8 = 2; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | #[cfg(octospim_v1)] | ||
| 1257 | foreach_peripheral!( | ||
| 1258 | (octospi, $inst:ident) => { | ||
| 1259 | impl SealedInstance for peripherals::$inst { | ||
| 1260 | const REGS: Regs = crate::pac::$inst; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | impl Instance for peripherals::$inst {} | ||
| 1264 | }; | ||
| 1265 | ); | ||
| 1266 | |||
| 1267 | #[cfg(not(octospim_v1))] | ||
| 1081 | foreach_peripheral!( | 1268 | foreach_peripheral!( |
| 1082 | (octospi, $inst:ident) => { | 1269 | (octospi, $inst:ident) => { |
| 1083 | impl SealedInstance for peripherals::$inst { | 1270 | impl SealedInstance for peripherals::$inst { |
diff --git a/embassy-stm32/src/qspi/enums.rs b/embassy-stm32/src/qspi/enums.rs index ecade9b1a..9ec4c1b43 100644 --- a/embassy-stm32/src/qspi/enums.rs +++ b/embassy-stm32/src/qspi/enums.rs | |||
| @@ -9,9 +9,9 @@ pub(crate) enum QspiMode { | |||
| 9 | MemoryMapped, | 9 | MemoryMapped, |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | impl Into<u8> for QspiMode { | 12 | impl From<QspiMode> for u8 { |
| 13 | fn into(self) -> u8 { | 13 | fn from(val: QspiMode) -> Self { |
| 14 | match self { | 14 | match val { |
| 15 | QspiMode::IndirectWrite => 0b00, | 15 | QspiMode::IndirectWrite => 0b00, |
| 16 | QspiMode::IndirectRead => 0b01, | 16 | QspiMode::IndirectRead => 0b01, |
| 17 | QspiMode::AutoPolling => 0b10, | 17 | QspiMode::AutoPolling => 0b10, |
| @@ -34,9 +34,9 @@ pub enum QspiWidth { | |||
| 34 | QUAD, | 34 | QUAD, |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | impl Into<u8> for QspiWidth { | 37 | impl From<QspiWidth> for u8 { |
| 38 | fn into(self) -> u8 { | 38 | fn from(val: QspiWidth) -> Self { |
| 39 | match self { | 39 | match val { |
| 40 | QspiWidth::NONE => 0b00, | 40 | QspiWidth::NONE => 0b00, |
| 41 | QspiWidth::SING => 0b01, | 41 | QspiWidth::SING => 0b01, |
| 42 | QspiWidth::DUAL => 0b10, | 42 | QspiWidth::DUAL => 0b10, |
| @@ -55,9 +55,9 @@ pub enum FlashSelection { | |||
| 55 | Flash2, | 55 | Flash2, |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | impl Into<bool> for FlashSelection { | 58 | impl From<FlashSelection> for bool { |
| 59 | fn into(self) -> bool { | 59 | fn from(val: FlashSelection) -> Self { |
| 60 | match self { | 60 | match val { |
| 61 | FlashSelection::Flash1 => false, | 61 | FlashSelection::Flash1 => false, |
| 62 | FlashSelection::Flash2 => true, | 62 | FlashSelection::Flash2 => true, |
| 63 | } | 63 | } |
| @@ -94,9 +94,9 @@ pub enum MemorySize { | |||
| 94 | Other(u8), | 94 | Other(u8), |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | impl Into<u8> for MemorySize { | 97 | impl From<MemorySize> for u8 { |
| 98 | fn into(self) -> u8 { | 98 | fn from(val: MemorySize) -> Self { |
| 99 | match self { | 99 | match val { |
| 100 | MemorySize::_1KiB => 9, | 100 | MemorySize::_1KiB => 9, |
| 101 | MemorySize::_2KiB => 10, | 101 | MemorySize::_2KiB => 10, |
| 102 | MemorySize::_4KiB => 11, | 102 | MemorySize::_4KiB => 11, |
| @@ -138,9 +138,9 @@ pub enum AddressSize { | |||
| 138 | _32bit, | 138 | _32bit, |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | impl Into<u8> for AddressSize { | 141 | impl From<AddressSize> for u8 { |
| 142 | fn into(self) -> u8 { | 142 | fn from(val: AddressSize) -> Self { |
| 143 | match self { | 143 | match val { |
| 144 | AddressSize::_8Bit => 0b00, | 144 | AddressSize::_8Bit => 0b00, |
| 145 | AddressSize::_16Bit => 0b01, | 145 | AddressSize::_16Bit => 0b01, |
| 146 | AddressSize::_24bit => 0b10, | 146 | AddressSize::_24bit => 0b10, |
| @@ -163,9 +163,9 @@ pub enum ChipSelectHighTime { | |||
| 163 | _8Cycle, | 163 | _8Cycle, |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | impl Into<u8> for ChipSelectHighTime { | 166 | impl From<ChipSelectHighTime> for u8 { |
| 167 | fn into(self) -> u8 { | 167 | fn from(val: ChipSelectHighTime) -> Self { |
| 168 | match self { | 168 | match val { |
| 169 | ChipSelectHighTime::_1Cycle => 0, | 169 | ChipSelectHighTime::_1Cycle => 0, |
| 170 | ChipSelectHighTime::_2Cycle => 1, | 170 | ChipSelectHighTime::_2Cycle => 1, |
| 171 | ChipSelectHighTime::_3Cycle => 2, | 171 | ChipSelectHighTime::_3Cycle => 2, |
| @@ -216,9 +216,9 @@ pub enum FIFOThresholdLevel { | |||
| 216 | _32Bytes, | 216 | _32Bytes, |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | impl Into<u8> for FIFOThresholdLevel { | 219 | impl From<FIFOThresholdLevel> for u8 { |
| 220 | fn into(self) -> u8 { | 220 | fn from(val: FIFOThresholdLevel) -> Self { |
| 221 | match self { | 221 | match val { |
| 222 | FIFOThresholdLevel::_1Bytes => 0, | 222 | FIFOThresholdLevel::_1Bytes => 0, |
| 223 | FIFOThresholdLevel::_2Bytes => 1, | 223 | FIFOThresholdLevel::_2Bytes => 1, |
| 224 | FIFOThresholdLevel::_3Bytes => 2, | 224 | FIFOThresholdLevel::_3Bytes => 2, |
| @@ -293,9 +293,9 @@ pub enum DummyCycles { | |||
| 293 | _31, | 293 | _31, |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | impl Into<u8> for DummyCycles { | 296 | impl From<DummyCycles> for u8 { |
| 297 | fn into(self) -> u8 { | 297 | fn from(val: DummyCycles) -> Self { |
| 298 | match self { | 298 | match val { |
| 299 | DummyCycles::_0 => 0, | 299 | DummyCycles::_0 => 0, |
| 300 | DummyCycles::_1 => 1, | 300 | DummyCycles::_1 => 1, |
| 301 | DummyCycles::_2 => 2, | 301 | DummyCycles::_2 => 2, |
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 308947e99..0df057c53 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs | |||
| @@ -6,7 +6,7 @@ pub mod enums; | |||
| 6 | 6 | ||
| 7 | use core::marker::PhantomData; | 7 | use core::marker::PhantomData; |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 9 | use embassy_hal_internal::PeripheralType; |
| 10 | use enums::*; | 10 | use enums::*; |
| 11 | 11 | ||
| 12 | use crate::dma::ChannelAndRequest; | 12 | use crate::dma::ChannelAndRequest; |
| @@ -14,11 +14,11 @@ use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed}; | |||
| 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 15 | use crate::pac::quadspi::Quadspi as Regs; | 15 | use crate::pac::quadspi::Quadspi as Regs; |
| 16 | use crate::rcc::{self, RccPeripheral}; | 16 | use crate::rcc::{self, RccPeripheral}; |
| 17 | use crate::{peripherals, Peripheral}; | 17 | use crate::{peripherals, Peri}; |
| 18 | 18 | ||
| 19 | /// QSPI transfer configuration. | 19 | /// QSPI transfer configuration. |
| 20 | pub struct TransferConfig { | 20 | pub struct TransferConfig { |
| 21 | /// Instraction width (IMODE) | 21 | /// Instruction width (IMODE) |
| 22 | pub iwidth: QspiWidth, | 22 | pub iwidth: QspiWidth, |
| 23 | /// Address width (ADMODE) | 23 | /// Address width (ADMODE) |
| 24 | pub awidth: QspiWidth, | 24 | pub awidth: QspiWidth, |
| @@ -75,13 +75,13 @@ impl Default for Config { | |||
| 75 | /// QSPI driver. | 75 | /// QSPI driver. |
| 76 | #[allow(dead_code)] | 76 | #[allow(dead_code)] |
| 77 | pub struct Qspi<'d, T: Instance, M: PeriMode> { | 77 | pub struct Qspi<'d, T: Instance, M: PeriMode> { |
| 78 | _peri: PeripheralRef<'d, T>, | 78 | _peri: Peri<'d, T>, |
| 79 | sck: Option<PeripheralRef<'d, AnyPin>>, | 79 | sck: Option<Peri<'d, AnyPin>>, |
| 80 | d0: Option<PeripheralRef<'d, AnyPin>>, | 80 | d0: Option<Peri<'d, AnyPin>>, |
| 81 | d1: Option<PeripheralRef<'d, AnyPin>>, | 81 | d1: Option<Peri<'d, AnyPin>>, |
| 82 | d2: Option<PeripheralRef<'d, AnyPin>>, | 82 | d2: Option<Peri<'d, AnyPin>>, |
| 83 | d3: Option<PeripheralRef<'d, AnyPin>>, | 83 | d3: Option<Peri<'d, AnyPin>>, |
| 84 | nss: Option<PeripheralRef<'d, AnyPin>>, | 84 | nss: Option<Peri<'d, AnyPin>>, |
| 85 | dma: Option<ChannelAndRequest<'d>>, | 85 | dma: Option<ChannelAndRequest<'d>>, |
| 86 | _phantom: PhantomData<M>, | 86 | _phantom: PhantomData<M>, |
| 87 | config: Config, | 87 | config: Config, |
| @@ -89,19 +89,17 @@ pub struct Qspi<'d, T: Instance, M: PeriMode> { | |||
| 89 | 89 | ||
| 90 | impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | 90 | impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { |
| 91 | fn new_inner( | 91 | fn new_inner( |
| 92 | peri: impl Peripheral<P = T> + 'd, | 92 | peri: Peri<'d, T>, |
| 93 | d0: Option<PeripheralRef<'d, AnyPin>>, | 93 | d0: Option<Peri<'d, AnyPin>>, |
| 94 | d1: Option<PeripheralRef<'d, AnyPin>>, | 94 | d1: Option<Peri<'d, AnyPin>>, |
| 95 | d2: Option<PeripheralRef<'d, AnyPin>>, | 95 | d2: Option<Peri<'d, AnyPin>>, |
| 96 | d3: Option<PeripheralRef<'d, AnyPin>>, | 96 | d3: Option<Peri<'d, AnyPin>>, |
| 97 | sck: Option<PeripheralRef<'d, AnyPin>>, | 97 | sck: Option<Peri<'d, AnyPin>>, |
| 98 | nss: Option<PeripheralRef<'d, AnyPin>>, | 98 | nss: Option<Peri<'d, AnyPin>>, |
| 99 | dma: Option<ChannelAndRequest<'d>>, | 99 | dma: Option<ChannelAndRequest<'d>>, |
| 100 | config: Config, | 100 | config: Config, |
| 101 | fsel: FlashSelection, | 101 | fsel: FlashSelection, |
| 102 | ) -> Self { | 102 | ) -> Self { |
| 103 | into_ref!(peri); | ||
| 104 | |||
| 105 | rcc::enable_and_reset::<T>(); | 103 | rcc::enable_and_reset::<T>(); |
| 106 | 104 | ||
| 107 | while T::REGS.sr().read().busy() {} | 105 | while T::REGS.sr().read().busy() {} |
| @@ -148,7 +146,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | |||
| 148 | } | 146 | } |
| 149 | 147 | ||
| 150 | /// Do a QSPI command. | 148 | /// Do a QSPI command. |
| 151 | pub fn command(&mut self, transaction: TransferConfig) { | 149 | pub fn blocking_command(&mut self, transaction: TransferConfig) { |
| 152 | #[cfg(not(stm32h7))] | 150 | #[cfg(not(stm32h7))] |
| 153 | T::REGS.cr().modify(|v| v.set_dmaen(false)); | 151 | T::REGS.cr().modify(|v| v.set_dmaen(false)); |
| 154 | self.setup_transaction(QspiMode::IndirectWrite, &transaction, None); | 152 | self.setup_transaction(QspiMode::IndirectWrite, &transaction, None); |
| @@ -172,7 +170,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | |||
| 172 | }); | 170 | }); |
| 173 | 171 | ||
| 174 | for b in buf { | 172 | for b in buf { |
| 175 | while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} | 173 | while !T::REGS.sr().read().tcf() && (T::REGS.sr().read().flevel() == 0) {} |
| 176 | *b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; | 174 | *b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; |
| 177 | } | 175 | } |
| 178 | 176 | ||
| @@ -201,7 +199,42 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | |||
| 201 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); | 199 | T::REGS.fcr().modify(|v| v.set_ctcf(true)); |
| 202 | } | 200 | } |
| 203 | 201 | ||
| 202 | /// Enable memory map mode | ||
| 203 | pub fn enable_memory_map(&mut self, transaction: &TransferConfig) { | ||
| 204 | T::REGS.fcr().modify(|v| { | ||
| 205 | v.set_csmf(true); | ||
| 206 | v.set_ctcf(true); | ||
| 207 | v.set_ctef(true); | ||
| 208 | v.set_ctof(true); | ||
| 209 | }); | ||
| 210 | T::REGS.ccr().write(|v| { | ||
| 211 | v.set_fmode(QspiMode::MemoryMapped.into()); | ||
| 212 | v.set_imode(transaction.iwidth.into()); | ||
| 213 | v.set_instruction(transaction.instruction); | ||
| 214 | v.set_admode(transaction.awidth.into()); | ||
| 215 | v.set_adsize(self.config.address_size.into()); | ||
| 216 | v.set_dmode(transaction.dwidth.into()); | ||
| 217 | v.set_abmode(QspiWidth::NONE.into()); | ||
| 218 | v.set_dcyc(transaction.dummy.into()); | ||
| 219 | }); | ||
| 220 | } | ||
| 221 | |||
| 204 | fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) { | 222 | fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) { |
| 223 | match (transaction.address, transaction.awidth) { | ||
| 224 | (Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"), | ||
| 225 | (Some(_), _) => {} | ||
| 226 | (None, QspiWidth::NONE) => {} | ||
| 227 | (None, _) => panic!("QSPI address is not set, so the address width should be NONE"), | ||
| 228 | } | ||
| 229 | |||
| 230 | match (data_len, transaction.dwidth) { | ||
| 231 | (Some(0), _) => panic!("QSPI data must be at least one byte"), | ||
| 232 | (Some(_), QspiWidth::NONE) => panic!("QSPI data can't be sent with a data width of NONE"), | ||
| 233 | (Some(_), _) => {} | ||
| 234 | (None, QspiWidth::NONE) => {} | ||
| 235 | (None, _) => panic!("QSPI data is empty, so the data width should be NONE"), | ||
| 236 | } | ||
| 237 | |||
| 205 | T::REGS.fcr().modify(|v| { | 238 | T::REGS.fcr().modify(|v| { |
| 206 | v.set_csmf(true); | 239 | v.set_csmf(true); |
| 207 | v.set_ctcf(true); | 240 | v.set_ctcf(true); |
| @@ -237,13 +270,13 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { | |||
| 237 | impl<'d, T: Instance> Qspi<'d, T, Blocking> { | 270 | impl<'d, T: Instance> Qspi<'d, T, Blocking> { |
| 238 | /// Create a new QSPI driver for bank 1, in blocking mode. | 271 | /// Create a new QSPI driver for bank 1, in blocking mode. |
| 239 | pub fn new_blocking_bank1( | 272 | pub fn new_blocking_bank1( |
| 240 | peri: impl Peripheral<P = T> + 'd, | 273 | peri: Peri<'d, T>, |
| 241 | d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd, | 274 | d0: Peri<'d, impl BK1D0Pin<T>>, |
| 242 | d1: impl Peripheral<P = impl BK1D1Pin<T>> + 'd, | 275 | d1: Peri<'d, impl BK1D1Pin<T>>, |
| 243 | d2: impl Peripheral<P = impl BK1D2Pin<T>> + 'd, | 276 | d2: Peri<'d, impl BK1D2Pin<T>>, |
| 244 | d3: impl Peripheral<P = impl BK1D3Pin<T>> + 'd, | 277 | d3: Peri<'d, impl BK1D3Pin<T>>, |
| 245 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 278 | sck: Peri<'d, impl SckPin<T>>, |
| 246 | nss: impl Peripheral<P = impl BK1NSSPin<T>> + 'd, | 279 | nss: Peri<'d, impl BK1NSSPin<T>>, |
| 247 | config: Config, | 280 | config: Config, |
| 248 | ) -> Self { | 281 | ) -> Self { |
| 249 | Self::new_inner( | 282 | Self::new_inner( |
| @@ -265,13 +298,13 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> { | |||
| 265 | 298 | ||
| 266 | /// Create a new QSPI driver for bank 2, in blocking mode. | 299 | /// Create a new QSPI driver for bank 2, in blocking mode. |
| 267 | pub fn new_blocking_bank2( | 300 | pub fn new_blocking_bank2( |
| 268 | peri: impl Peripheral<P = T> + 'd, | 301 | peri: Peri<'d, T>, |
| 269 | d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd, | 302 | d0: Peri<'d, impl BK2D0Pin<T>>, |
| 270 | d1: impl Peripheral<P = impl BK2D1Pin<T>> + 'd, | 303 | d1: Peri<'d, impl BK2D1Pin<T>>, |
| 271 | d2: impl Peripheral<P = impl BK2D2Pin<T>> + 'd, | 304 | d2: Peri<'d, impl BK2D2Pin<T>>, |
| 272 | d3: impl Peripheral<P = impl BK2D3Pin<T>> + 'd, | 305 | d3: Peri<'d, impl BK2D3Pin<T>>, |
| 273 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 306 | sck: Peri<'d, impl SckPin<T>>, |
| 274 | nss: impl Peripheral<P = impl BK2NSSPin<T>> + 'd, | 307 | nss: Peri<'d, impl BK2NSSPin<T>>, |
| 275 | config: Config, | 308 | config: Config, |
| 276 | ) -> Self { | 309 | ) -> Self { |
| 277 | Self::new_inner( | 310 | Self::new_inner( |
| @@ -295,14 +328,14 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> { | |||
| 295 | impl<'d, T: Instance> Qspi<'d, T, Async> { | 328 | impl<'d, T: Instance> Qspi<'d, T, Async> { |
| 296 | /// Create a new QSPI driver for bank 1. | 329 | /// Create a new QSPI driver for bank 1. |
| 297 | pub fn new_bank1( | 330 | pub fn new_bank1( |
| 298 | peri: impl Peripheral<P = T> + 'd, | 331 | peri: Peri<'d, T>, |
| 299 | d0: impl Peripheral<P = impl BK1D0Pin<T>> + 'd, | 332 | d0: Peri<'d, impl BK1D0Pin<T>>, |
| 300 | d1: impl Peripheral<P = impl BK1D1Pin<T>> + 'd, | 333 | d1: Peri<'d, impl BK1D1Pin<T>>, |
| 301 | d2: impl Peripheral<P = impl BK1D2Pin<T>> + 'd, | 334 | d2: Peri<'d, impl BK1D2Pin<T>>, |
| 302 | d3: impl Peripheral<P = impl BK1D3Pin<T>> + 'd, | 335 | d3: Peri<'d, impl BK1D3Pin<T>>, |
| 303 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 336 | sck: Peri<'d, impl SckPin<T>>, |
| 304 | nss: impl Peripheral<P = impl BK1NSSPin<T>> + 'd, | 337 | nss: Peri<'d, impl BK1NSSPin<T>>, |
| 305 | dma: impl Peripheral<P = impl QuadDma<T>> + 'd, | 338 | dma: Peri<'d, impl QuadDma<T>>, |
| 306 | config: Config, | 339 | config: Config, |
| 307 | ) -> Self { | 340 | ) -> Self { |
| 308 | Self::new_inner( | 341 | Self::new_inner( |
| @@ -324,14 +357,14 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 324 | 357 | ||
| 325 | /// Create a new QSPI driver for bank 2. | 358 | /// Create a new QSPI driver for bank 2. |
| 326 | pub fn new_bank2( | 359 | pub fn new_bank2( |
| 327 | peri: impl Peripheral<P = T> + 'd, | 360 | peri: Peri<'d, T>, |
| 328 | d0: impl Peripheral<P = impl BK2D0Pin<T>> + 'd, | 361 | d0: Peri<'d, impl BK2D0Pin<T>>, |
| 329 | d1: impl Peripheral<P = impl BK2D1Pin<T>> + 'd, | 362 | d1: Peri<'d, impl BK2D1Pin<T>>, |
| 330 | d2: impl Peripheral<P = impl BK2D2Pin<T>> + 'd, | 363 | d2: Peri<'d, impl BK2D2Pin<T>>, |
| 331 | d3: impl Peripheral<P = impl BK2D3Pin<T>> + 'd, | 364 | d3: Peri<'d, impl BK2D3Pin<T>>, |
| 332 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 365 | sck: Peri<'d, impl SckPin<T>>, |
| 333 | nss: impl Peripheral<P = impl BK2NSSPin<T>> + 'd, | 366 | nss: Peri<'d, impl BK2NSSPin<T>>, |
| 334 | dma: impl Peripheral<P = impl QuadDma<T>> + 'd, | 367 | dma: Peri<'d, impl QuadDma<T>>, |
| 335 | config: Config, | 368 | config: Config, |
| 336 | ) -> Self { | 369 | ) -> Self { |
| 337 | Self::new_inner( | 370 | Self::new_inner( |
| @@ -353,6 +386,21 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 353 | 386 | ||
| 354 | /// Blocking read data, using DMA. | 387 | /// Blocking read data, using DMA. |
| 355 | pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { | 388 | pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { |
| 389 | let transfer = self.start_read_transfer(transaction, buf); | ||
| 390 | transfer.blocking_wait(); | ||
| 391 | } | ||
| 392 | |||
| 393 | /// Async read data, using DMA. | ||
| 394 | pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { | ||
| 395 | let transfer = self.start_read_transfer(transaction, buf); | ||
| 396 | transfer.await; | ||
| 397 | } | ||
| 398 | |||
| 399 | fn start_read_transfer<'a>( | ||
| 400 | &'a mut self, | ||
| 401 | transaction: TransferConfig, | ||
| 402 | buf: &'a mut [u8], | ||
| 403 | ) -> crate::dma::Transfer<'a> { | ||
| 356 | self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); | 404 | self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); |
| 357 | 405 | ||
| 358 | T::REGS.ccr().modify(|v| { | 406 | T::REGS.ccr().modify(|v| { |
| @@ -373,12 +421,22 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 373 | // STM32H7 does not have dmaen | 421 | // STM32H7 does not have dmaen |
| 374 | #[cfg(not(stm32h7))] | 422 | #[cfg(not(stm32h7))] |
| 375 | T::REGS.cr().modify(|v| v.set_dmaen(true)); | 423 | T::REGS.cr().modify(|v| v.set_dmaen(true)); |
| 376 | 424 | transfer | |
| 377 | transfer.blocking_wait(); | ||
| 378 | } | 425 | } |
| 379 | 426 | ||
| 380 | /// Blocking write data, using DMA. | 427 | /// Blocking write data, using DMA. |
| 381 | pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { | 428 | pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { |
| 429 | let transfer = self.start_write_transfer(transaction, buf); | ||
| 430 | transfer.blocking_wait(); | ||
| 431 | } | ||
| 432 | |||
| 433 | /// Async write data, using DMA. | ||
| 434 | pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { | ||
| 435 | let transfer = self.start_write_transfer(transaction, buf); | ||
| 436 | transfer.await; | ||
| 437 | } | ||
| 438 | |||
| 439 | fn start_write_transfer<'a>(&'a mut self, transaction: TransferConfig, buf: &'a [u8]) -> crate::dma::Transfer<'a> { | ||
| 382 | self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); | 440 | self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); |
| 383 | 441 | ||
| 384 | T::REGS.ccr().modify(|v| { | 442 | T::REGS.ccr().modify(|v| { |
| @@ -395,8 +453,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { | |||
| 395 | // STM32H7 does not have dmaen | 453 | // STM32H7 does not have dmaen |
| 396 | #[cfg(not(stm32h7))] | 454 | #[cfg(not(stm32h7))] |
| 397 | T::REGS.cr().modify(|v| v.set_dmaen(true)); | 455 | T::REGS.cr().modify(|v| v.set_dmaen(true)); |
| 398 | 456 | transfer | |
| 399 | transfer.blocking_wait(); | ||
| 400 | } | 457 | } |
| 401 | } | 458 | } |
| 402 | 459 | ||
| @@ -406,7 +463,7 @@ trait SealedInstance { | |||
| 406 | 463 | ||
| 407 | /// QSPI instance trait. | 464 | /// QSPI instance trait. |
| 408 | #[allow(private_bounds)] | 465 | #[allow(private_bounds)] |
| 409 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} | 466 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {} |
| 410 | 467 | ||
| 411 | pin_trait!(SckPin, Instance); | 468 | pin_trait!(SckPin, Instance); |
| 412 | pin_trait!(BK1D0Pin, Instance); | 469 | pin_trait!(BK1D0Pin, Instance); |
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 4e9c18594..e2c704405 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs | |||
| @@ -16,9 +16,13 @@ pub enum LseMode { | |||
| 16 | Bypass, | 16 | Bypass, |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | #[derive(Clone, Copy)] | ||
| 19 | pub struct LseConfig { | 20 | pub struct LseConfig { |
| 20 | pub frequency: Hertz, | 21 | pub frequency: Hertz, |
| 21 | pub mode: LseMode, | 22 | pub mode: LseMode, |
| 23 | /// If peripherals other than RTC/TAMP or RCC functions need the lse this bit must be set | ||
| 24 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] | ||
| 25 | pub peripherals_clocked: bool, | ||
| 22 | } | 26 | } |
| 23 | 27 | ||
| 24 | #[allow(dead_code)] | 28 | #[allow(dead_code)] |
| @@ -41,8 +45,8 @@ impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv { | |||
| 41 | match value { | 45 | match value { |
| 42 | #[cfg(not(stm32h5))] // ES0565: LSE Low drive mode is not functional | 46 | #[cfg(not(stm32h5))] // ES0565: LSE Low drive mode is not functional |
| 43 | LseDrive::Low => Lsedrv::LOW, | 47 | LseDrive::Low => Lsedrv::LOW, |
| 44 | LseDrive::MediumLow => Lsedrv::MEDIUMLOW, | 48 | LseDrive::MediumLow => Lsedrv::MEDIUM_LOW, |
| 45 | LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH, | 49 | LseDrive::MediumHigh => Lsedrv::MEDIUM_HIGH, |
| 46 | LseDrive::High => Lsedrv::HIGH, | 50 | LseDrive::High => Lsedrv::HIGH, |
| 47 | } | 51 | } |
| 48 | } | 52 | } |
| @@ -80,6 +84,7 @@ fn bdcr() -> Reg<Bdcr, RW> { | |||
| 80 | return crate::pac::RCC.csr1(); | 84 | return crate::pac::RCC.csr1(); |
| 81 | } | 85 | } |
| 82 | 86 | ||
| 87 | #[derive(Clone, Copy)] | ||
| 83 | pub struct LsConfig { | 88 | pub struct LsConfig { |
| 84 | pub rtc: RtcClockSource, | 89 | pub rtc: RtcClockSource, |
| 85 | pub lsi: bool, | 90 | pub lsi: bool, |
| @@ -87,12 +92,25 @@ pub struct LsConfig { | |||
| 87 | } | 92 | } |
| 88 | 93 | ||
| 89 | impl LsConfig { | 94 | impl LsConfig { |
| 95 | /// Creates an [`LsConfig`] using the LSI when possible. | ||
| 96 | pub const fn new() -> Self { | ||
| 97 | // on L5, just the fact that LSI is enabled makes things crash. | ||
| 98 | // TODO: investigate. | ||
| 99 | |||
| 100 | #[cfg(not(stm32l5))] | ||
| 101 | return Self::default_lsi(); | ||
| 102 | #[cfg(stm32l5)] | ||
| 103 | return Self::off(); | ||
| 104 | } | ||
| 105 | |||
| 90 | pub const fn default_lse() -> Self { | 106 | pub const fn default_lse() -> Self { |
| 91 | Self { | 107 | Self { |
| 92 | rtc: RtcClockSource::LSE, | 108 | rtc: RtcClockSource::LSE, |
| 93 | lse: Some(LseConfig { | 109 | lse: Some(LseConfig { |
| 94 | frequency: Hertz(32_768), | 110 | frequency: Hertz(32_768), |
| 95 | mode: LseMode::Oscillator(LseDrive::MediumHigh), | 111 | mode: LseMode::Oscillator(LseDrive::MediumHigh), |
| 112 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] | ||
| 113 | peripherals_clocked: false, | ||
| 96 | }), | 114 | }), |
| 97 | lsi: false, | 115 | lsi: false, |
| 98 | } | 116 | } |
| @@ -117,13 +135,7 @@ impl LsConfig { | |||
| 117 | 135 | ||
| 118 | impl Default for LsConfig { | 136 | impl Default for LsConfig { |
| 119 | fn default() -> Self { | 137 | fn default() -> Self { |
| 120 | // on L5, just the fact that LSI is enabled makes things crash. | 138 | Self::new() |
| 121 | // TODO: investigate. | ||
| 122 | |||
| 123 | #[cfg(not(stm32l5))] | ||
| 124 | return Self::default_lsi(); | ||
| 125 | #[cfg(stm32l5)] | ||
| 126 | return Self::off(); | ||
| 127 | } | 139 | } |
| 128 | } | 140 | } |
| 129 | 141 | ||
| @@ -146,6 +158,15 @@ impl LsConfig { | |||
| 146 | }, | 158 | }, |
| 147 | None => (false, false, None), | 159 | None => (false, false, None), |
| 148 | }; | 160 | }; |
| 161 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] | ||
| 162 | let lse_sysen = if let Some(lse) = self.lse { | ||
| 163 | Some(lse.peripherals_clocked) | ||
| 164 | } else { | ||
| 165 | None | ||
| 166 | }; | ||
| 167 | #[cfg(rcc_u0)] | ||
| 168 | let lse_sysen = Some(lse_en); | ||
| 169 | |||
| 149 | _ = lse_drv; // not all chips have it. | 170 | _ = lse_drv; // not all chips have it. |
| 150 | 171 | ||
| 151 | // Disable backup domain write protection | 172 | // Disable backup domain write protection |
| @@ -186,6 +207,10 @@ impl LsConfig { | |||
| 186 | } | 207 | } |
| 187 | ok &= reg.lseon() == lse_en; | 208 | ok &= reg.lseon() == lse_en; |
| 188 | ok &= reg.lsebyp() == lse_byp; | 209 | ok &= reg.lsebyp() == lse_byp; |
| 210 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | ||
| 211 | if let Some(lse_sysen) = lse_sysen { | ||
| 212 | ok &= reg.lsesysen() == lse_sysen; | ||
| 213 | } | ||
| 189 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] | 214 | #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] |
| 190 | if let Some(lse_drv) = lse_drv { | 215 | if let Some(lse_drv) = lse_drv { |
| 191 | ok &= reg.lsedrv() == lse_drv.into(); | 216 | ok &= reg.lsedrv() == lse_drv.into(); |
| @@ -233,6 +258,17 @@ impl LsConfig { | |||
| 233 | }); | 258 | }); |
| 234 | 259 | ||
| 235 | while !bdcr().read().lserdy() {} | 260 | while !bdcr().read().lserdy() {} |
| 261 | |||
| 262 | #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] | ||
| 263 | if let Some(lse_sysen) = lse_sysen { | ||
| 264 | bdcr().modify(|w| { | ||
| 265 | w.set_lsesysen(lse_sysen); | ||
| 266 | }); | ||
| 267 | |||
| 268 | if lse_sysen { | ||
| 269 | while !bdcr().read().lsesysrdy() {} | ||
| 270 | } | ||
| 271 | } | ||
| 236 | } | 272 | } |
| 237 | 273 | ||
| 238 | if self.rtc != RtcClockSource::DISABLE { | 274 | if self.rtc != RtcClockSource::DISABLE { |
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 5adf37941..cac2a9149 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs | |||
| @@ -3,6 +3,7 @@ pub use crate::pac::rcc::vals::{ | |||
| 3 | Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk, | 3 | Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Hsikerdiv as HsiKerDiv, Ppre as APBPrescaler, Sw as Sysclk, |
| 4 | }; | 4 | }; |
| 5 | use crate::pac::{FLASH, RCC}; | 5 | use crate::pac::{FLASH, RCC}; |
| 6 | use crate::rcc::LSI_FREQ; | ||
| 6 | use crate::time::Hertz; | 7 | use crate::time::Hertz; |
| 7 | 8 | ||
| 8 | /// HSI speed | 9 | /// HSI speed |
| @@ -37,6 +38,7 @@ pub struct Hsi { | |||
| 37 | 38 | ||
| 38 | /// Clocks configutation | 39 | /// Clocks configutation |
| 39 | #[non_exhaustive] | 40 | #[non_exhaustive] |
| 41 | #[derive(Clone, Copy)] | ||
| 40 | pub struct Config { | 42 | pub struct Config { |
| 41 | /// HSI Configuration | 43 | /// HSI Configuration |
| 42 | pub hsi: Option<Hsi>, | 44 | pub hsi: Option<Hsi>, |
| @@ -57,9 +59,8 @@ pub struct Config { | |||
| 57 | pub mux: super::mux::ClockMux, | 59 | pub mux: super::mux::ClockMux, |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | impl Default for Config { | 62 | impl Config { |
| 61 | #[inline] | 63 | pub const fn new() -> Self { |
| 62 | fn default() -> Config { | ||
| 63 | Config { | 64 | Config { |
| 64 | hsi: Some(Hsi { | 65 | hsi: Some(Hsi { |
| 65 | sys_div: HsiSysDiv::DIV4, | 66 | sys_div: HsiSysDiv::DIV4, |
| @@ -69,12 +70,18 @@ impl Default for Config { | |||
| 69 | sys: Sysclk::HSISYS, | 70 | sys: Sysclk::HSISYS, |
| 70 | ahb_pre: AHBPrescaler::DIV1, | 71 | ahb_pre: AHBPrescaler::DIV1, |
| 71 | apb1_pre: APBPrescaler::DIV1, | 72 | apb1_pre: APBPrescaler::DIV1, |
| 72 | ls: Default::default(), | 73 | ls: crate::rcc::LsConfig::new(), |
| 73 | mux: Default::default(), | 74 | mux: super::mux::ClockMux::default(), |
| 74 | } | 75 | } |
| 75 | } | 76 | } |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 79 | impl Default for Config { | ||
| 80 | fn default() -> Config { | ||
| 81 | Self::new() | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 78 | pub(crate) unsafe fn init(config: Config) { | 85 | pub(crate) unsafe fn init(config: Config) { |
| 79 | // Turn on the HSI | 86 | // Turn on the HSI |
| 80 | match config.hsi { | 87 | match config.hsi { |
| @@ -109,8 +116,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 109 | } | 116 | } |
| 110 | Some(hse) => { | 117 | Some(hse) => { |
| 111 | match hse.mode { | 118 | match hse.mode { |
| 112 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | 119 | HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), |
| 113 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | 120 | HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), |
| 114 | } | 121 | } |
| 115 | 122 | ||
| 116 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | 123 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); |
| @@ -120,20 +127,27 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 120 | } | 127 | } |
| 121 | }; | 128 | }; |
| 122 | 129 | ||
| 130 | let rtc = config.ls.init(); | ||
| 131 | |||
| 123 | let sys = match config.sys { | 132 | let sys = match config.sys { |
| 124 | Sysclk::HSISYS => unwrap!(hsisys), | 133 | Sysclk::HSISYS => unwrap!(hsisys), |
| 125 | Sysclk::HSE => unwrap!(hse), | 134 | Sysclk::HSE => unwrap!(hse), |
| 135 | Sysclk::LSI => { | ||
| 136 | assert!(config.ls.lsi); | ||
| 137 | LSI_FREQ | ||
| 138 | } | ||
| 139 | Sysclk::LSE => unwrap!(config.ls.lse).frequency, | ||
| 126 | _ => unreachable!(), | 140 | _ => unreachable!(), |
| 127 | }; | 141 | }; |
| 128 | 142 | ||
| 129 | assert!(max::SYSCLK.contains(&sys)); | 143 | rcc_assert!(max::SYSCLK.contains(&sys)); |
| 130 | 144 | ||
| 131 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. | 145 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. |
| 132 | let hclk = sys / config.ahb_pre; | 146 | let hclk = sys / config.ahb_pre; |
| 133 | assert!(max::HCLK.contains(&hclk)); | 147 | rcc_assert!(max::HCLK.contains(&hclk)); |
| 134 | 148 | ||
| 135 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | 149 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 136 | assert!(max::PCLK.contains(&pclk1)); | 150 | rcc_assert!(max::PCLK.contains(&pclk1)); |
| 137 | 151 | ||
| 138 | let latency = match hclk.0 { | 152 | let latency = match hclk.0 { |
| 139 | ..=24_000_000 => Latency::WS0, | 153 | ..=24_000_000 => Latency::WS0, |
| @@ -161,8 +175,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 161 | RCC.cr().modify(|w| w.set_hsion(false)); | 175 | RCC.cr().modify(|w| w.set_hsion(false)); |
| 162 | } | 176 | } |
| 163 | 177 | ||
| 164 | let rtc = config.ls.init(); | ||
| 165 | |||
| 166 | config.mux.init(); | 178 | config.mux.init(); |
| 167 | 179 | ||
| 168 | set_clocks!( | 180 | set_clocks!( |
| @@ -179,6 +191,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 179 | lsi: None, | 191 | lsi: None, |
| 180 | lse: None, | 192 | lse: None, |
| 181 | ); | 193 | ); |
| 194 | |||
| 195 | RCC.ccipr() | ||
| 196 | .modify(|w| w.set_adc1sel(stm32_metapac::rcc::vals::Adcsel::SYS)); | ||
| 182 | } | 197 | } |
| 183 | 198 | ||
| 184 | mod max { | 199 | mod max { |
diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index 63dc27bdd..1155b6acd 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs | |||
| @@ -4,11 +4,13 @@ pub use crate::pac::rcc::vals::Adcpre as ADCPrescaler; | |||
| 4 | #[cfg(stm32f3)] | 4 | #[cfg(stm32f3)] |
| 5 | pub use crate::pac::rcc::vals::Adcpres as AdcPllPrescaler; | 5 | pub use crate::pac::rcc::vals::Adcpres as AdcPllPrescaler; |
| 6 | use crate::pac::rcc::vals::Pllsrc; | 6 | use crate::pac::rcc::vals::Pllsrc; |
| 7 | #[cfg(stm32f1)] | 7 | #[cfg(all(stm32f1, not(stm32f107)))] |
| 8 | pub use crate::pac::rcc::vals::Pllxtpre as PllPreDiv; | 8 | pub use crate::pac::rcc::vals::Pllxtpre as PllPreDiv; |
| 9 | #[cfg(any(stm32f0, stm32f3))] | 9 | #[cfg(any(stm32f0, stm32f3))] |
| 10 | pub use crate::pac::rcc::vals::Prediv as PllPreDiv; | 10 | pub use crate::pac::rcc::vals::Prediv as PllPreDiv; |
| 11 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Sw as Sysclk}; | 11 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllmul as PllMul, Ppre as APBPrescaler, Sw as Sysclk}; |
| 12 | #[cfg(stm32f107)] | ||
| 13 | pub use crate::pac::rcc::vals::{I2s2src, Pll2mul as Pll2Mul, Prediv1 as PllPreDiv, Prediv1src, Usbpre as UsbPre}; | ||
| 12 | use crate::pac::{FLASH, RCC}; | 14 | use crate::pac::{FLASH, RCC}; |
| 13 | use crate::time::Hertz; | 15 | use crate::time::Hertz; |
| 14 | 16 | ||
| @@ -37,6 +39,8 @@ pub enum PllSource { | |||
| 37 | HSI, | 39 | HSI, |
| 38 | #[cfg(rcc_f0v4)] | 40 | #[cfg(rcc_f0v4)] |
| 39 | HSI48, | 41 | HSI48, |
| 42 | #[cfg(stm32f107)] | ||
| 43 | PLL2, | ||
| 40 | } | 44 | } |
| 41 | 45 | ||
| 42 | #[derive(Clone, Copy)] | 46 | #[derive(Clone, Copy)] |
| @@ -52,6 +56,12 @@ pub struct Pll { | |||
| 52 | pub mul: PllMul, | 56 | pub mul: PllMul, |
| 53 | } | 57 | } |
| 54 | 58 | ||
| 59 | #[cfg(stm32f107)] | ||
| 60 | #[derive(Clone, Copy)] | ||
| 61 | pub struct Pll2Or3 { | ||
| 62 | pub mul: Pll2Mul, | ||
| 63 | } | ||
| 64 | |||
| 55 | #[cfg(all(stm32f3, not(rcc_f37)))] | 65 | #[cfg(all(stm32f3, not(rcc_f37)))] |
| 56 | #[derive(Clone, Copy)] | 66 | #[derive(Clone, Copy)] |
| 57 | pub enum AdcClockSource { | 67 | pub enum AdcClockSource { |
| @@ -76,6 +86,7 @@ pub enum HrtimClockSource { | |||
| 76 | 86 | ||
| 77 | /// Clocks configutation | 87 | /// Clocks configutation |
| 78 | #[non_exhaustive] | 88 | #[non_exhaustive] |
| 89 | #[derive(Clone, Copy)] | ||
| 79 | pub struct Config { | 90 | pub struct Config { |
| 80 | pub hsi: bool, | 91 | pub hsi: bool, |
| 81 | pub hse: Option<Hse>, | 92 | pub hse: Option<Hse>, |
| @@ -84,6 +95,12 @@ pub struct Config { | |||
| 84 | pub sys: Sysclk, | 95 | pub sys: Sysclk, |
| 85 | 96 | ||
| 86 | pub pll: Option<Pll>, | 97 | pub pll: Option<Pll>, |
| 98 | #[cfg(stm32f107)] | ||
| 99 | pub pll2: Option<Pll2Or3>, | ||
| 100 | #[cfg(stm32f107)] | ||
| 101 | pub pll3: Option<Pll2Or3>, | ||
| 102 | #[cfg(stm32f107)] | ||
| 103 | pub prediv2: PllPreDiv, | ||
| 87 | 104 | ||
| 88 | pub ahb_pre: AHBPrescaler, | 105 | pub ahb_pre: AHBPrescaler, |
| 89 | pub apb1_pre: APBPrescaler, | 106 | pub apb1_pre: APBPrescaler, |
| @@ -98,26 +115,39 @@ pub struct Config { | |||
| 98 | #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] | 115 | #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] |
| 99 | pub adc34: AdcClockSource, | 116 | pub adc34: AdcClockSource, |
| 100 | 117 | ||
| 118 | #[cfg(stm32f107)] | ||
| 119 | pub i2s2_src: I2s2src, | ||
| 120 | #[cfg(stm32f107)] | ||
| 121 | pub i2s3_src: I2s2src, | ||
| 122 | |||
| 101 | /// Per-peripheral kernel clock selection muxes | 123 | /// Per-peripheral kernel clock selection muxes |
| 102 | pub mux: super::mux::ClockMux, | 124 | pub mux: super::mux::ClockMux, |
| 103 | 125 | ||
| 104 | pub ls: super::LsConfig, | 126 | pub ls: super::LsConfig, |
| 105 | } | 127 | } |
| 106 | 128 | ||
| 107 | impl Default for Config { | 129 | impl Config { |
| 108 | fn default() -> Self { | 130 | pub const fn new() -> Self { |
| 109 | Self { | 131 | Self { |
| 110 | hsi: true, | 132 | hsi: true, |
| 111 | hse: None, | 133 | hse: None, |
| 112 | #[cfg(crs)] | 134 | #[cfg(crs)] |
| 113 | hsi48: Some(Default::default()), | 135 | hsi48: Some(crate::rcc::Hsi48Config::new()), |
| 114 | sys: Sysclk::HSI, | 136 | sys: Sysclk::HSI, |
| 115 | pll: None, | 137 | pll: None, |
| 138 | |||
| 139 | #[cfg(stm32f107)] | ||
| 140 | pll2: None, | ||
| 141 | #[cfg(stm32f107)] | ||
| 142 | pll3: None, | ||
| 143 | #[cfg(stm32f107)] | ||
| 144 | prediv2: PllPreDiv::DIV1, | ||
| 145 | |||
| 116 | ahb_pre: AHBPrescaler::DIV1, | 146 | ahb_pre: AHBPrescaler::DIV1, |
| 117 | apb1_pre: APBPrescaler::DIV1, | 147 | apb1_pre: APBPrescaler::DIV1, |
| 118 | #[cfg(not(stm32f0))] | 148 | #[cfg(not(stm32f0))] |
| 119 | apb2_pre: APBPrescaler::DIV1, | 149 | apb2_pre: APBPrescaler::DIV1, |
| 120 | ls: Default::default(), | 150 | ls: crate::rcc::LsConfig::new(), |
| 121 | 151 | ||
| 122 | #[cfg(stm32f1)] | 152 | #[cfg(stm32f1)] |
| 123 | // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz) | 153 | // ensure ADC is not out of range by default even if APB2 is maxxed out (36mhz) |
| @@ -128,11 +158,22 @@ impl Default for Config { | |||
| 128 | #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] | 158 | #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] |
| 129 | adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), | 159 | adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), |
| 130 | 160 | ||
| 131 | mux: Default::default(), | 161 | #[cfg(stm32f107)] |
| 162 | i2s2_src: I2s2src::SYS, | ||
| 163 | #[cfg(stm32f107)] | ||
| 164 | i2s3_src: I2s2src::SYS, | ||
| 165 | |||
| 166 | mux: super::mux::ClockMux::default(), | ||
| 132 | } | 167 | } |
| 133 | } | 168 | } |
| 134 | } | 169 | } |
| 135 | 170 | ||
| 171 | impl Default for Config { | ||
| 172 | fn default() -> Self { | ||
| 173 | Self::new() | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 136 | /// Initialize and Set the clock frequencies | 177 | /// Initialize and Set the clock frequencies |
| 137 | pub(crate) unsafe fn init(config: Config) { | 178 | pub(crate) unsafe fn init(config: Config) { |
| 138 | // Turn on the HSI | 179 | // Turn on the HSI |
| @@ -157,8 +198,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 157 | } | 198 | } |
| 158 | Some(hse) => { | 199 | Some(hse) => { |
| 159 | match hse.mode { | 200 | match hse.mode { |
| 160 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | 201 | HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), |
| 161 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | 202 | HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), |
| 162 | } | 203 | } |
| 163 | 204 | ||
| 164 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | 205 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); |
| @@ -174,6 +215,28 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 174 | #[cfg(not(crs))] | 215 | #[cfg(not(crs))] |
| 175 | let hsi48: Option<Hertz> = None; | 216 | let hsi48: Option<Hertz> = None; |
| 176 | 217 | ||
| 218 | // PLL2 and PLL3 | ||
| 219 | // Configure this before PLL since PLL2 can be the source for PLL. | ||
| 220 | #[cfg(stm32f107)] | ||
| 221 | { | ||
| 222 | // Common prediv for PLL2 and PLL3 | ||
| 223 | RCC.cfgr2().modify(|w| w.set_prediv2(config.prediv2)); | ||
| 224 | |||
| 225 | // Configure PLL2 | ||
| 226 | if let Some(pll2) = config.pll2 { | ||
| 227 | RCC.cfgr2().modify(|w| w.set_pll2mul(pll2.mul)); | ||
| 228 | RCC.cr().modify(|w| w.set_pll2on(true)); | ||
| 229 | while !RCC.cr().read().pll2rdy() {} | ||
| 230 | } | ||
| 231 | |||
| 232 | // Configure PLL3 | ||
| 233 | if let Some(pll3) = config.pll3 { | ||
| 234 | RCC.cfgr2().modify(|w| w.set_pll3mul(pll3.mul)); | ||
| 235 | RCC.cr().modify(|w| w.set_pll3on(true)); | ||
| 236 | while !RCC.cr().read().pll3rdy() {} | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 177 | // Enable PLL | 240 | // Enable PLL |
| 178 | let pll = config.pll.map(|pll| { | 241 | let pll = config.pll.map(|pll| { |
| 179 | let (src_val, src_freq) = match pll.src { | 242 | let (src_val, src_freq) = match pll.src { |
| @@ -186,21 +249,44 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 186 | } | 249 | } |
| 187 | (Pllsrc::HSI_DIV2, unwrap!(hsi)) | 250 | (Pllsrc::HSI_DIV2, unwrap!(hsi)) |
| 188 | } | 251 | } |
| 189 | PllSource::HSE => (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)), | 252 | PllSource::HSE => { |
| 253 | #[cfg(stm32f107)] | ||
| 254 | RCC.cfgr2().modify(|w| w.set_prediv1src(Prediv1src::HSE)); | ||
| 255 | |||
| 256 | (Pllsrc::HSE_DIV_PREDIV, unwrap!(hse)) | ||
| 257 | } | ||
| 190 | #[cfg(rcc_f0v4)] | 258 | #[cfg(rcc_f0v4)] |
| 191 | PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), | 259 | PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), |
| 260 | #[cfg(stm32f107)] | ||
| 261 | PllSource::PLL2 => { | ||
| 262 | if config.pll2.is_none() { | ||
| 263 | panic!("if PLL source is PLL2, Config::pll2 must also be set."); | ||
| 264 | } | ||
| 265 | RCC.cfgr2().modify(|w| w.set_prediv1src(Prediv1src::PLL2)); | ||
| 266 | |||
| 267 | let pll2 = unwrap!(config.pll2); | ||
| 268 | let in_freq = hse.unwrap() / config.prediv2; | ||
| 269 | let pll2freq = in_freq * pll2.mul; | ||
| 270 | |||
| 271 | (Pllsrc::HSE_DIV_PREDIV, pll2freq) | ||
| 272 | } | ||
| 192 | }; | 273 | }; |
| 193 | let in_freq = src_freq / pll.prediv; | 274 | let in_freq = src_freq / pll.prediv; |
| 194 | assert!(max::PLL_IN.contains(&in_freq)); | 275 | |
| 276 | rcc_assert!(max::PLL_IN.contains(&in_freq)); | ||
| 195 | let out_freq = in_freq * pll.mul; | 277 | let out_freq = in_freq * pll.mul; |
| 196 | assert!(max::PLL_OUT.contains(&out_freq)); | 278 | rcc_assert!(max::PLL_OUT.contains(&out_freq)); |
| 197 | 279 | ||
| 198 | #[cfg(not(stm32f1))] | 280 | #[cfg(not(stm32f1))] |
| 199 | RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); | 281 | RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); |
| 282 | |||
| 283 | #[cfg(stm32f107)] | ||
| 284 | RCC.cfgr2().modify(|w| w.set_prediv1(pll.prediv)); | ||
| 285 | |||
| 200 | RCC.cfgr().modify(|w| { | 286 | RCC.cfgr().modify(|w| { |
| 201 | w.set_pllmul(pll.mul); | 287 | w.set_pllmul(pll.mul); |
| 202 | w.set_pllsrc(src_val); | 288 | w.set_pllsrc(src_val); |
| 203 | #[cfg(stm32f1)] | 289 | #[cfg(all(stm32f1, not(stm32f107)))] |
| 204 | w.set_pllxtpre(pll.prediv); | 290 | w.set_pllxtpre(pll.prediv); |
| 205 | }); | 291 | }); |
| 206 | RCC.cr().modify(|w| w.set_pllon(true)); | 292 | RCC.cr().modify(|w| w.set_pllon(true)); |
| @@ -209,10 +295,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 209 | out_freq | 295 | out_freq |
| 210 | }); | 296 | }); |
| 211 | 297 | ||
| 212 | #[cfg(stm32f3)] | 298 | #[cfg(any(rcc_f1, rcc_f1cl, stm32f3, stm32f107))] |
| 213 | let pll_mul_2 = pll.map(|pll| pll * 2u32); | ||
| 214 | |||
| 215 | #[cfg(any(rcc_f1, rcc_f1cl, stm32f3))] | ||
| 216 | let usb = match pll { | 299 | let usb = match pll { |
| 217 | Some(Hertz(72_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1_5), | 300 | Some(Hertz(72_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1_5), |
| 218 | Some(Hertz(48_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1), | 301 | Some(Hertz(48_000_000)) => Some(crate::pac::rcc::vals::Usbpre::DIV1), |
| @@ -228,6 +311,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 228 | Sysclk::HSI => unwrap!(hsi), | 311 | Sysclk::HSI => unwrap!(hsi), |
| 229 | Sysclk::HSE => unwrap!(hse), | 312 | Sysclk::HSE => unwrap!(hse), |
| 230 | Sysclk::PLL1_P => unwrap!(pll), | 313 | Sysclk::PLL1_P => unwrap!(pll), |
| 314 | #[cfg(crs)] | ||
| 315 | Sysclk::HSI48 => unwrap!(hsi48), | ||
| 316 | #[cfg(not(crs))] | ||
| 231 | _ => unreachable!(), | 317 | _ => unreachable!(), |
| 232 | }; | 318 | }; |
| 233 | 319 | ||
| @@ -238,15 +324,15 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 238 | #[cfg(stm32f0)] | 324 | #[cfg(stm32f0)] |
| 239 | let (pclk2, pclk2_tim) = (pclk1, pclk1_tim); | 325 | let (pclk2, pclk2_tim) = (pclk1, pclk1_tim); |
| 240 | 326 | ||
| 241 | assert!(max::HCLK.contains(&hclk)); | 327 | rcc_assert!(max::HCLK.contains(&hclk)); |
| 242 | assert!(max::PCLK1.contains(&pclk1)); | 328 | rcc_assert!(max::PCLK1.contains(&pclk1)); |
| 243 | #[cfg(not(stm32f0))] | 329 | #[cfg(not(stm32f0))] |
| 244 | assert!(max::PCLK2.contains(&pclk2)); | 330 | rcc_assert!(max::PCLK2.contains(&pclk2)); |
| 245 | 331 | ||
| 246 | #[cfg(stm32f1)] | 332 | #[cfg(stm32f1)] |
| 247 | let adc = pclk2 / config.adc_pre; | 333 | let adc = pclk2 / config.adc_pre; |
| 248 | #[cfg(stm32f1)] | 334 | #[cfg(stm32f1)] |
| 249 | assert!(max::ADC.contains(&adc)); | 335 | rcc_assert!(max::ADC.contains(&adc)); |
| 250 | 336 | ||
| 251 | // Set latency based on HCLK frquency | 337 | // Set latency based on HCLK frquency |
| 252 | #[cfg(stm32f0)] | 338 | #[cfg(stm32f0)] |
| @@ -289,6 +375,13 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 289 | w.set_adcpre(config.adc_pre); | 375 | w.set_adcpre(config.adc_pre); |
| 290 | }); | 376 | }); |
| 291 | 377 | ||
| 378 | // I2S2 and I2S3 | ||
| 379 | #[cfg(stm32f107)] | ||
| 380 | { | ||
| 381 | RCC.cfgr2().modify(|w| w.set_i2s2src(config.i2s2_src)); | ||
| 382 | RCC.cfgr2().modify(|w| w.set_i2s3src(config.i2s3_src)); | ||
| 383 | } | ||
| 384 | |||
| 292 | // Wait for the new prescalers to kick in | 385 | // Wait for the new prescalers to kick in |
| 293 | // "The clocks are divided with the new prescaler factor from | 386 | // "The clocks are divided with the new prescaler factor from |
| 294 | // 1 to 16 AHB cycles after write" | 387 | // 1 to 16 AHB cycles after write" |
| @@ -328,9 +421,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 328 | assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1)); | 421 | assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1)); |
| 329 | 422 | ||
| 330 | let (div, ckmode) = match adcpres { | 423 | let (div, ckmode) = match adcpres { |
| 331 | AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1), | 424 | AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNC_DIV1), |
| 332 | AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2), | 425 | AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNC_DIV2), |
| 333 | AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4), | 426 | AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNC_DIV4), |
| 334 | }; | 427 | }; |
| 335 | common.ccr().modify(|w| w.set_ckmode(ckmode)); | 428 | common.ccr().modify(|w| w.set_ckmode(ckmode)); |
| 336 | 429 | ||
| @@ -357,9 +450,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 357 | assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1)); | 450 | assert!(!(adcpres == AdcHclkPrescaler::Div1 && config.ahb_pre != AHBPrescaler::DIV1)); |
| 358 | 451 | ||
| 359 | let (div, ckmode) = match adcpres { | 452 | let (div, ckmode) = match adcpres { |
| 360 | AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNCDIV1), | 453 | AdcHclkPrescaler::Div1 => (1u32, Ckmode::SYNC_DIV1), |
| 361 | AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNCDIV2), | 454 | AdcHclkPrescaler::Div2 => (2u32, Ckmode::SYNC_DIV2), |
| 362 | AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNCDIV4), | 455 | AdcHclkPrescaler::Div4 => (4u32, Ckmode::SYNC_DIV4), |
| 363 | }; | 456 | }; |
| 364 | common.ccr().modify(|w| w.set_ckmode(ckmode)); | 457 | common.ccr().modify(|w| w.set_ckmode(ckmode)); |
| 365 | 458 | ||
| @@ -393,9 +486,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 393 | hsi: hsi, | 486 | hsi: hsi, |
| 394 | hse: hse, | 487 | hse: hse, |
| 395 | pll1_p: pll, | 488 | pll1_p: pll, |
| 396 | #[cfg(stm32f3)] | ||
| 397 | pll1_p_mul_2: pll_mul_2, | ||
| 398 | hsi_div_244: hsi.map(|h| h / 244u32), | ||
| 399 | sys: Some(sys), | 489 | sys: Some(sys), |
| 400 | pclk1: Some(pclk1), | 490 | pclk1: Some(pclk1), |
| 401 | pclk2: Some(pclk2), | 491 | pclk2: Some(pclk2), |
diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 61f687d30..8f2e8db5f 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use stm32_metapac::flash::vals::Latency; |
| 2 | 2 | ||
| 3 | #[cfg(any(stm32f413, stm32f423, stm32f412))] | ||
| 4 | pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; | ||
| 3 | pub use crate::pac::rcc::vals::{ | 5 | pub use crate::pac::rcc::vals::{ |
| 4 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, | 6 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, |
| 5 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 7 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| @@ -63,6 +65,7 @@ pub struct Pll { | |||
| 63 | /// Used to calculate flash waitstates. See | 65 | /// Used to calculate flash waitstates. See |
| 64 | /// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency | 66 | /// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency |
| 65 | #[cfg(stm32f2)] | 67 | #[cfg(stm32f2)] |
| 68 | #[derive(Clone, Copy)] | ||
| 66 | pub enum VoltageScale { | 69 | pub enum VoltageScale { |
| 67 | /// 2.7 to 3.6 V | 70 | /// 2.7 to 3.6 V |
| 68 | Range0, | 71 | Range0, |
| @@ -76,12 +79,15 @@ pub enum VoltageScale { | |||
| 76 | 79 | ||
| 77 | /// Configuration of the core clocks | 80 | /// Configuration of the core clocks |
| 78 | #[non_exhaustive] | 81 | #[non_exhaustive] |
| 82 | #[derive(Clone, Copy)] | ||
| 79 | pub struct Config { | 83 | pub struct Config { |
| 80 | pub hsi: bool, | 84 | pub hsi: bool, |
| 81 | pub hse: Option<Hse>, | 85 | pub hse: Option<Hse>, |
| 82 | pub sys: Sysclk, | 86 | pub sys: Sysclk, |
| 83 | 87 | ||
| 84 | pub pll_src: PllSource, | 88 | pub pll_src: PllSource, |
| 89 | #[cfg(any(stm32f412, stm32f413, stm32f423))] | ||
| 90 | pub external_i2s_clock: Option<Hertz>, | ||
| 85 | 91 | ||
| 86 | pub pll: Option<Pll>, | 92 | pub pll: Option<Pll>, |
| 87 | #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] | 93 | #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] |
| @@ -102,13 +108,15 @@ pub struct Config { | |||
| 102 | pub voltage: VoltageScale, | 108 | pub voltage: VoltageScale, |
| 103 | } | 109 | } |
| 104 | 110 | ||
| 105 | impl Default for Config { | 111 | impl Config { |
| 106 | fn default() -> Self { | 112 | pub const fn new() -> Self { |
| 107 | Self { | 113 | Self { |
| 108 | hsi: true, | 114 | hsi: true, |
| 109 | hse: None, | 115 | hse: None, |
| 110 | sys: Sysclk::HSI, | 116 | sys: Sysclk::HSI, |
| 111 | pll_src: PllSource::HSI, | 117 | pll_src: PllSource::HSI, |
| 118 | #[cfg(any(stm32f412, stm32f413, stm32f423))] | ||
| 119 | external_i2s_clock: None, | ||
| 112 | pll: None, | 120 | pll: None, |
| 113 | #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] | 121 | #[cfg(any(stm32f2, all(stm32f4, not(stm32f410)), stm32f7))] |
| 114 | plli2s: None, | 122 | plli2s: None, |
| @@ -119,15 +127,21 @@ impl Default for Config { | |||
| 119 | apb1_pre: APBPrescaler::DIV1, | 127 | apb1_pre: APBPrescaler::DIV1, |
| 120 | apb2_pre: APBPrescaler::DIV1, | 128 | apb2_pre: APBPrescaler::DIV1, |
| 121 | 129 | ||
| 122 | ls: Default::default(), | 130 | ls: crate::rcc::LsConfig::new(), |
| 123 | 131 | ||
| 124 | #[cfg(stm32f2)] | 132 | #[cfg(stm32f2)] |
| 125 | voltage: VoltageScale::Range3, | 133 | voltage: VoltageScale::Range3, |
| 126 | mux: Default::default(), | 134 | mux: super::mux::ClockMux::default(), |
| 127 | } | 135 | } |
| 128 | } | 136 | } |
| 129 | } | 137 | } |
| 130 | 138 | ||
| 139 | impl Default for Config { | ||
| 140 | fn default() -> Self { | ||
| 141 | Self::new() | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 131 | pub(crate) unsafe fn init(config: Config) { | 145 | pub(crate) unsafe fn init(config: Config) { |
| 132 | // set VOS to SCALE1, if use PLL | 146 | // set VOS to SCALE1, if use PLL |
| 133 | // TODO: check real clock speed before set VOS | 147 | // TODO: check real clock speed before set VOS |
| @@ -168,8 +182,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 168 | } | 182 | } |
| 169 | Some(hse) => { | 183 | Some(hse) => { |
| 170 | match hse.mode { | 184 | match hse.mode { |
| 171 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | 185 | HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), |
| 172 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | 186 | HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), |
| 173 | } | 187 | } |
| 174 | 188 | ||
| 175 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | 189 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); |
| @@ -183,6 +197,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 183 | let pll_input = PllInput { | 197 | let pll_input = PllInput { |
| 184 | hse, | 198 | hse, |
| 185 | hsi, | 199 | hsi, |
| 200 | #[cfg(any(stm32f412, stm32f413, stm32f423))] | ||
| 201 | external: config.external_i2s_clock, | ||
| 186 | source: config.pll_src, | 202 | source: config.pll_src, |
| 187 | }; | 203 | }; |
| 188 | let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); | 204 | let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); |
| @@ -203,10 +219,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 203 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | 219 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 204 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); | 220 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); |
| 205 | 221 | ||
| 206 | assert!(max::SYSCLK.contains(&sys)); | 222 | rcc_assert!(max::SYSCLK.contains(&sys)); |
| 207 | assert!(max::HCLK.contains(&hclk)); | 223 | rcc_assert!(max::HCLK.contains(&hclk)); |
| 208 | assert!(max::PCLK1.contains(&pclk1)); | 224 | rcc_assert!(max::PCLK1.contains(&pclk1)); |
| 209 | assert!(max::PCLK2.contains(&pclk2)); | 225 | rcc_assert!(max::PCLK2.contains(&pclk2)); |
| 210 | 226 | ||
| 211 | let rtc = config.ls.init(); | 227 | let rtc = config.ls.init(); |
| 212 | 228 | ||
| @@ -306,7 +322,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 306 | #[cfg(dsihost)] | 322 | #[cfg(dsihost)] |
| 307 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers | 323 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers |
| 308 | 324 | ||
| 309 | hsi_div488: hsi.map(|hsi| hsi/488u32), | ||
| 310 | hsi_hse: None, | 325 | hsi_hse: None, |
| 311 | afif: None, | 326 | afif: None, |
| 312 | ); | 327 | ); |
| @@ -316,6 +331,8 @@ struct PllInput { | |||
| 316 | source: PllSource, | 331 | source: PllSource, |
| 317 | hsi: Option<Hertz>, | 332 | hsi: Option<Hertz>, |
| 318 | hse: Option<Hertz>, | 333 | hse: Option<Hertz>, |
| 334 | #[cfg(any(stm32f412, stm32f413, stm32f423))] | ||
| 335 | external: Option<Hertz>, | ||
| 319 | } | 336 | } |
| 320 | 337 | ||
| 321 | #[derive(Default)] | 338 | #[derive(Default)] |
| @@ -360,10 +377,17 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll | |||
| 360 | 377 | ||
| 361 | let Some(pll) = config else { return PllOutput::default() }; | 378 | let Some(pll) = config else { return PllOutput::default() }; |
| 362 | 379 | ||
| 380 | #[cfg(not(any(stm32f412, stm32f413, stm32f423)))] | ||
| 363 | let pll_src = match input.source { | 381 | let pll_src = match input.source { |
| 364 | PllSource::HSE => input.hse, | 382 | PllSource::HSE => input.hse, |
| 365 | PllSource::HSI => input.hsi, | 383 | PllSource::HSI => input.hsi, |
| 366 | }; | 384 | }; |
| 385 | #[cfg(any(stm32f412, stm32f413, stm32f423))] | ||
| 386 | let pll_src = match (input.source, input.external) { | ||
| 387 | (PllSource::HSE, None) => input.hse, | ||
| 388 | (PllSource::HSI, None) => input.hsi, | ||
| 389 | (_, Some(ext)) => Some(ext), | ||
| 390 | }; | ||
| 367 | 391 | ||
| 368 | let pll_src = pll_src.unwrap(); | 392 | let pll_src = pll_src.unwrap(); |
| 369 | 393 | ||
| @@ -412,6 +436,17 @@ fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> Pll | |||
| 412 | }), | 436 | }), |
| 413 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] | 437 | #[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))] |
| 414 | PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { | 438 | PllInstance::Plli2s => RCC.plli2scfgr().write(|w| { |
| 439 | #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] | ||
| 440 | w.set_pllm(pll.prediv); | ||
| 441 | #[cfg(any(stm32f412, stm32f413, stm32f423))] | ||
| 442 | { | ||
| 443 | let plli2ssource = match input.external { | ||
| 444 | Some(_) => Plli2sSource::EXTERNAL, | ||
| 445 | None => Plli2sSource::HSE_HSI, | ||
| 446 | }; | ||
| 447 | w.set_plli2ssrc(plli2ssource); | ||
| 448 | } | ||
| 449 | |||
| 415 | write_fields!(w); | 450 | write_fields!(w); |
| 416 | }), | 451 | }), |
| 417 | #[cfg(stm32f2)] | 452 | #[cfg(stm32f2)] |
diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index c2fa0ca39..ce6398afd 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | use crate::pac::flash::vals::Latency; | 1 | use crate::pac::flash::vals::Latency; |
| 2 | pub use crate::pac::pwr::vals::Vos as VoltageRange; | 2 | pub use crate::pac::pwr::vals::Vos as VoltageRange; |
| 3 | pub use crate::pac::rcc::vals::{ | 3 | pub use crate::pac::rcc::vals::{ |
| 4 | Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, | 4 | Hpre as AHBPrescaler, Hsidiv as HsiSysDiv, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, |
| 5 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 5 | Pllr as PllRDiv, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 6 | }; | 6 | }; |
| 7 | use crate::pac::{FLASH, PWR, RCC}; | 7 | use crate::pac::{FLASH, PWR, RCC}; |
| 8 | use crate::rcc::LSI_FREQ; | ||
| 8 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 9 | 10 | ||
| 10 | /// HSI speed | 11 | /// HSI speed |
| @@ -28,11 +29,18 @@ pub struct Hse { | |||
| 28 | pub mode: HseMode, | 29 | pub mode: HseMode, |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 32 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 33 | pub struct Hsi { | ||
| 34 | /// Division factor for HSISYS clock. Default is 1. | ||
| 35 | pub sys_div: HsiSysDiv, | ||
| 36 | } | ||
| 37 | |||
| 31 | /// PLL Configuration | 38 | /// PLL Configuration |
| 32 | /// | 39 | /// |
| 33 | /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output | 40 | /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output |
| 34 | /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate | 41 | /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate |
| 35 | /// frequency ranges for each of these settings. | 42 | /// frequency ranges for each of these settings. |
| 43 | #[derive(Clone, Copy)] | ||
| 36 | pub struct Pll { | 44 | pub struct Pll { |
| 37 | /// PLL Source clock selection. | 45 | /// PLL Source clock selection. |
| 38 | pub source: PllSource, | 46 | pub source: PllSource, |
| @@ -55,9 +63,10 @@ pub struct Pll { | |||
| 55 | 63 | ||
| 56 | /// Clocks configutation | 64 | /// Clocks configutation |
| 57 | #[non_exhaustive] | 65 | #[non_exhaustive] |
| 66 | #[derive(Clone, Copy)] | ||
| 58 | pub struct Config { | 67 | pub struct Config { |
| 59 | /// HSI Enable | 68 | /// HSI Configuration |
| 60 | pub hsi: bool, | 69 | pub hsi: Option<Hsi>, |
| 61 | 70 | ||
| 62 | /// HSE Configuration | 71 | /// HSE Configuration |
| 63 | pub hse: Option<Hse>, | 72 | pub hse: Option<Hse>, |
| @@ -88,26 +97,33 @@ pub struct Config { | |||
| 88 | pub mux: super::mux::ClockMux, | 97 | pub mux: super::mux::ClockMux, |
| 89 | } | 98 | } |
| 90 | 99 | ||
| 91 | impl Default for Config { | 100 | impl Config { |
| 92 | #[inline] | 101 | pub const fn new() -> Self { |
| 93 | fn default() -> Config { | ||
| 94 | Config { | 102 | Config { |
| 95 | hsi: true, | 103 | hsi: Some(Hsi { |
| 104 | sys_div: HsiSysDiv::DIV1, | ||
| 105 | }), | ||
| 96 | hse: None, | 106 | hse: None, |
| 97 | sys: Sysclk::HSI, | 107 | sys: Sysclk::HSI, |
| 98 | #[cfg(crs)] | 108 | #[cfg(crs)] |
| 99 | hsi48: Some(Default::default()), | 109 | hsi48: Some(crate::rcc::Hsi48Config::new()), |
| 100 | pll: None, | 110 | pll: None, |
| 101 | ahb_pre: AHBPrescaler::DIV1, | 111 | ahb_pre: AHBPrescaler::DIV1, |
| 102 | apb1_pre: APBPrescaler::DIV1, | 112 | apb1_pre: APBPrescaler::DIV1, |
| 103 | low_power_run: false, | 113 | low_power_run: false, |
| 104 | ls: Default::default(), | 114 | ls: crate::rcc::LsConfig::new(), |
| 105 | voltage_range: VoltageRange::RANGE1, | 115 | voltage_range: VoltageRange::RANGE1, |
| 106 | mux: Default::default(), | 116 | mux: super::mux::ClockMux::default(), |
| 107 | } | 117 | } |
| 108 | } | 118 | } |
| 109 | } | 119 | } |
| 110 | 120 | ||
| 121 | impl Default for Config { | ||
| 122 | fn default() -> Config { | ||
| 123 | Self::new() | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 111 | #[derive(Default)] | 127 | #[derive(Default)] |
| 112 | pub struct PllFreq { | 128 | pub struct PllFreq { |
| 113 | pub pll_p: Option<Hertz>, | 129 | pub pll_p: Option<Hertz>, |
| @@ -117,7 +133,12 @@ pub struct PllFreq { | |||
| 117 | 133 | ||
| 118 | pub(crate) unsafe fn init(config: Config) { | 134 | pub(crate) unsafe fn init(config: Config) { |
| 119 | // Turn on the HSI | 135 | // Turn on the HSI |
| 120 | RCC.cr().modify(|w| w.set_hsion(true)); | 136 | RCC.cr().modify(|w| { |
| 137 | w.set_hsion(true); | ||
| 138 | if let Some(hsi) = config.hsi { | ||
| 139 | w.set_hsidiv(hsi.sys_div); | ||
| 140 | } | ||
| 141 | }); | ||
| 121 | while !RCC.cr().read().hsirdy() {} | 142 | while !RCC.cr().read().hsirdy() {} |
| 122 | 143 | ||
| 123 | // Use the HSI clock as system clock during the actual clock setup | 144 | // Use the HSI clock as system clock during the actual clock setup |
| @@ -125,9 +146,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 125 | while RCC.cfgr().read().sws() != Sysclk::HSI {} | 146 | while RCC.cfgr().read().sws() != Sysclk::HSI {} |
| 126 | 147 | ||
| 127 | // Configure HSI | 148 | // Configure HSI |
| 128 | let hsi = match config.hsi { | 149 | let (hsi, hsisys) = match config.hsi { |
| 129 | false => None, | 150 | None => (None, None), |
| 130 | true => Some(HSI_FREQ), | 151 | Some(hsi) => (Some(HSI_FREQ), Some(HSI_FREQ / hsi.sys_div)), |
| 131 | }; | 152 | }; |
| 132 | 153 | ||
| 133 | // Configure HSE | 154 | // Configure HSE |
| @@ -138,8 +159,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 138 | } | 159 | } |
| 139 | Some(hse) => { | 160 | Some(hse) => { |
| 140 | match hse.mode { | 161 | match hse.mode { |
| 141 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | 162 | HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), |
| 142 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | 163 | HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), |
| 143 | } | 164 | } |
| 144 | 165 | ||
| 145 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | 166 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); |
| @@ -167,10 +188,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 167 | while RCC.cr().read().pllrdy() {} | 188 | while RCC.cr().read().pllrdy() {} |
| 168 | 189 | ||
| 169 | let in_freq = src_freq / pll_config.prediv; | 190 | let in_freq = src_freq / pll_config.prediv; |
| 170 | assert!(max::PLL_IN.contains(&in_freq)); | 191 | rcc_assert!(max::PLL_IN.contains(&in_freq)); |
| 171 | let internal_freq = in_freq * pll_config.mul; | 192 | let internal_freq = in_freq * pll_config.mul; |
| 172 | 193 | rcc_assert!(max::PLL_VCO.contains(&internal_freq)); | |
| 173 | assert!(max::PLL_VCO.contains(&internal_freq)); | ||
| 174 | 194 | ||
| 175 | RCC.pllcfgr().write(|w| { | 195 | RCC.pllcfgr().write(|w| { |
| 176 | w.set_plln(pll_config.mul); | 196 | w.set_plln(pll_config.mul); |
| @@ -184,7 +204,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 184 | w.set_pllpen(true); | 204 | w.set_pllpen(true); |
| 185 | }); | 205 | }); |
| 186 | let freq = internal_freq / div_p; | 206 | let freq = internal_freq / div_p; |
| 187 | assert!(max::PLL_P.contains(&freq)); | 207 | rcc_assert!(max::PLL_P.contains(&freq)); |
| 188 | freq | 208 | freq |
| 189 | }); | 209 | }); |
| 190 | 210 | ||
| @@ -194,7 +214,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 194 | w.set_pllqen(true); | 214 | w.set_pllqen(true); |
| 195 | }); | 215 | }); |
| 196 | let freq = internal_freq / div_q; | 216 | let freq = internal_freq / div_q; |
| 197 | assert!(max::PLL_Q.contains(&freq)); | 217 | rcc_assert!(max::PLL_Q.contains(&freq)); |
| 198 | freq | 218 | freq |
| 199 | }); | 219 | }); |
| 200 | 220 | ||
| @@ -204,7 +224,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 204 | w.set_pllren(true); | 224 | w.set_pllren(true); |
| 205 | }); | 225 | }); |
| 206 | let freq = internal_freq / div_r; | 226 | let freq = internal_freq / div_r; |
| 207 | assert!(max::PLL_R.contains(&freq)); | 227 | rcc_assert!(max::PLL_R.contains(&freq)); |
| 208 | freq | 228 | freq |
| 209 | }); | 229 | }); |
| 210 | 230 | ||
| @@ -220,21 +240,28 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 220 | }) | 240 | }) |
| 221 | .unwrap_or_default(); | 241 | .unwrap_or_default(); |
| 222 | 242 | ||
| 243 | let rtc = config.ls.init(); | ||
| 244 | |||
| 223 | let sys = match config.sys { | 245 | let sys = match config.sys { |
| 224 | Sysclk::HSI => unwrap!(hsi), | 246 | Sysclk::HSI => unwrap!(hsisys), |
| 225 | Sysclk::HSE => unwrap!(hse), | 247 | Sysclk::HSE => unwrap!(hse), |
| 226 | Sysclk::PLL1_R => unwrap!(pll.pll_r), | 248 | Sysclk::PLL1_R => unwrap!(pll.pll_r), |
| 249 | Sysclk::LSI => { | ||
| 250 | assert!(config.ls.lsi); | ||
| 251 | LSI_FREQ | ||
| 252 | } | ||
| 253 | Sysclk::LSE => unwrap!(config.ls.lse).frequency, | ||
| 227 | _ => unreachable!(), | 254 | _ => unreachable!(), |
| 228 | }; | 255 | }; |
| 229 | 256 | ||
| 230 | assert!(max::SYSCLK.contains(&sys)); | 257 | rcc_assert!(max::SYSCLK.contains(&sys)); |
| 231 | 258 | ||
| 232 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. | 259 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. |
| 233 | let hclk = sys / config.ahb_pre; | 260 | let hclk = sys / config.ahb_pre; |
| 234 | assert!(max::HCLK.contains(&hclk)); | 261 | rcc_assert!(max::HCLK.contains(&hclk)); |
| 235 | 262 | ||
| 236 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | 263 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 237 | assert!(max::PCLK.contains(&pclk1)); | 264 | rcc_assert!(max::PCLK.contains(&pclk1)); |
| 238 | 265 | ||
| 239 | let latency = match (config.voltage_range, hclk.0) { | 266 | let latency = match (config.voltage_range, hclk.0) { |
| 240 | (VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0, | 267 | (VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0, |
| @@ -263,7 +290,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 263 | while RCC.cfgr().read().sws() != config.sys {} | 290 | while RCC.cfgr().read().sws() != config.sys {} |
| 264 | 291 | ||
| 265 | // Disable HSI if not used | 292 | // Disable HSI if not used |
| 266 | if !config.hsi { | 293 | if config.hsi.is_none() { |
| 267 | RCC.cr().modify(|w| w.set_hsion(false)); | 294 | RCC.cr().modify(|w| w.set_hsion(false)); |
| 268 | } | 295 | } |
| 269 | 296 | ||
| @@ -272,8 +299,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 272 | PWR.cr1().modify(|w| w.set_lpr(true)); | 299 | PWR.cr1().modify(|w| w.set_lpr(true)); |
| 273 | } | 300 | } |
| 274 | 301 | ||
| 275 | let rtc = config.ls.init(); | ||
| 276 | |||
| 277 | config.mux.init(); | 302 | config.mux.init(); |
| 278 | 303 | ||
| 279 | set_clocks!( | 304 | set_clocks!( |
| @@ -289,8 +314,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 289 | #[cfg(crs)] | 314 | #[cfg(crs)] |
| 290 | hsi48: hsi48, | 315 | hsi48: hsi48, |
| 291 | rtc: rtc, | 316 | rtc: rtc, |
| 292 | hsi_div_8: hsi.map(|h| h / 8u32), | ||
| 293 | hsi_div_488: hsi.map(|h| h / 488u32), | ||
| 294 | 317 | ||
| 295 | // TODO | 318 | // TODO |
| 296 | lsi: None, | 319 | lsi: None, |
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index c261c0fed..da13e16aa 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs | |||
| @@ -32,6 +32,7 @@ pub struct Hse { | |||
| 32 | /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output | 32 | /// Use this struct to configure the PLL source, input frequency, multiplication factor, and output |
| 33 | /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate | 33 | /// dividers. Be sure to keep check the datasheet for your specific part for the appropriate |
| 34 | /// frequency ranges for each of these settings. | 34 | /// frequency ranges for each of these settings. |
| 35 | #[derive(Clone, Copy)] | ||
| 35 | pub struct Pll { | 36 | pub struct Pll { |
| 36 | /// PLL Source clock selection. | 37 | /// PLL Source clock selection. |
| 37 | pub source: PllSource, | 38 | pub source: PllSource, |
| @@ -54,6 +55,7 @@ pub struct Pll { | |||
| 54 | 55 | ||
| 55 | /// Clocks configutation | 56 | /// Clocks configutation |
| 56 | #[non_exhaustive] | 57 | #[non_exhaustive] |
| 58 | #[derive(Clone, Copy)] | ||
| 57 | pub struct Config { | 59 | pub struct Config { |
| 58 | /// HSI Enable | 60 | /// HSI Enable |
| 59 | pub hsi: bool, | 61 | pub hsi: bool, |
| @@ -89,26 +91,31 @@ pub struct Config { | |||
| 89 | pub mux: super::mux::ClockMux, | 91 | pub mux: super::mux::ClockMux, |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 92 | impl Default for Config { | 94 | impl Config { |
| 93 | #[inline] | 95 | pub const fn new() -> Self { |
| 94 | fn default() -> Config { | ||
| 95 | Config { | 96 | Config { |
| 96 | hsi: true, | 97 | hsi: true, |
| 97 | hse: None, | 98 | hse: None, |
| 98 | sys: Sysclk::HSI, | 99 | sys: Sysclk::HSI, |
| 99 | hsi48: Some(Default::default()), | 100 | hsi48: Some(crate::rcc::Hsi48Config::new()), |
| 100 | pll: None, | 101 | pll: None, |
| 101 | ahb_pre: AHBPrescaler::DIV1, | 102 | ahb_pre: AHBPrescaler::DIV1, |
| 102 | apb1_pre: APBPrescaler::DIV1, | 103 | apb1_pre: APBPrescaler::DIV1, |
| 103 | apb2_pre: APBPrescaler::DIV1, | 104 | apb2_pre: APBPrescaler::DIV1, |
| 104 | low_power_run: false, | 105 | low_power_run: false, |
| 105 | ls: Default::default(), | 106 | ls: crate::rcc::LsConfig::new(), |
| 106 | boost: false, | 107 | boost: false, |
| 107 | mux: Default::default(), | 108 | mux: super::mux::ClockMux::default(), |
| 108 | } | 109 | } |
| 109 | } | 110 | } |
| 110 | } | 111 | } |
| 111 | 112 | ||
| 113 | impl Default for Config { | ||
| 114 | fn default() -> Config { | ||
| 115 | Self::new() | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 112 | #[derive(Default)] | 119 | #[derive(Default)] |
| 113 | pub struct PllFreq { | 120 | pub struct PllFreq { |
| 114 | pub pll_p: Option<Hertz>, | 121 | pub pll_p: Option<Hertz>, |
| @@ -139,8 +146,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 139 | } | 146 | } |
| 140 | Some(hse) => { | 147 | Some(hse) => { |
| 141 | match hse.mode { | 148 | match hse.mode { |
| 142 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | 149 | HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), |
| 143 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | 150 | HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), |
| 144 | } | 151 | } |
| 145 | 152 | ||
| 146 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | 153 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); |
| @@ -167,10 +174,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 167 | while RCC.cr().read().pllrdy() {} | 174 | while RCC.cr().read().pllrdy() {} |
| 168 | 175 | ||
| 169 | let in_freq = src_freq / pll_config.prediv; | 176 | let in_freq = src_freq / pll_config.prediv; |
| 170 | assert!(max::PLL_IN.contains(&in_freq)); | 177 | rcc_assert!(max::PLL_IN.contains(&in_freq)); |
| 171 | let internal_freq = in_freq * pll_config.mul; | 178 | let internal_freq = in_freq * pll_config.mul; |
| 172 | 179 | ||
| 173 | assert!(max::PLL_VCO.contains(&internal_freq)); | 180 | rcc_assert!(max::PLL_VCO.contains(&internal_freq)); |
| 174 | 181 | ||
| 175 | RCC.pllcfgr().write(|w| { | 182 | RCC.pllcfgr().write(|w| { |
| 176 | w.set_plln(pll_config.mul); | 183 | w.set_plln(pll_config.mul); |
| @@ -184,7 +191,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 184 | w.set_pllpen(true); | 191 | w.set_pllpen(true); |
| 185 | }); | 192 | }); |
| 186 | let freq = internal_freq / div_p; | 193 | let freq = internal_freq / div_p; |
| 187 | assert!(max::PLL_P.contains(&freq)); | 194 | rcc_assert!(max::PLL_P.contains(&freq)); |
| 188 | freq | 195 | freq |
| 189 | }); | 196 | }); |
| 190 | 197 | ||
| @@ -194,7 +201,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 194 | w.set_pllqen(true); | 201 | w.set_pllqen(true); |
| 195 | }); | 202 | }); |
| 196 | let freq = internal_freq / div_q; | 203 | let freq = internal_freq / div_q; |
| 197 | assert!(max::PLL_Q.contains(&freq)); | 204 | rcc_assert!(max::PLL_Q.contains(&freq)); |
| 198 | freq | 205 | freq |
| 199 | }); | 206 | }); |
| 200 | 207 | ||
| @@ -204,7 +211,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 204 | w.set_pllren(true); | 211 | w.set_pllren(true); |
| 205 | }); | 212 | }); |
| 206 | let freq = internal_freq / div_r; | 213 | let freq = internal_freq / div_r; |
| 207 | assert!(max::PLL_R.contains(&freq)); | 214 | rcc_assert!(max::PLL_R.contains(&freq)); |
| 208 | freq | 215 | freq |
| 209 | }); | 216 | }); |
| 210 | 217 | ||
| @@ -227,16 +234,16 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 227 | _ => unreachable!(), | 234 | _ => unreachable!(), |
| 228 | }; | 235 | }; |
| 229 | 236 | ||
| 230 | assert!(max::SYSCLK.contains(&sys)); | 237 | rcc_assert!(max::SYSCLK.contains(&sys)); |
| 231 | 238 | ||
| 232 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. | 239 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. |
| 233 | let hclk = sys / config.ahb_pre; | 240 | let hclk = sys / config.ahb_pre; |
| 234 | assert!(max::HCLK.contains(&hclk)); | 241 | rcc_assert!(max::HCLK.contains(&hclk)); |
| 235 | 242 | ||
| 236 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); | 243 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 237 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); | 244 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); |
| 238 | assert!(max::PCLK.contains(&pclk2)); | 245 | rcc_assert!(max::PCLK.contains(&pclk1)); |
| 239 | assert!(max::PCLK.contains(&pclk2)); | 246 | rcc_assert!(max::PCLK.contains(&pclk2)); |
| 240 | 247 | ||
| 241 | // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) | 248 | // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) |
| 242 | if config.boost { | 249 | if config.boost { |
| @@ -318,6 +325,8 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 318 | hse: hse, | 325 | hse: hse, |
| 319 | hsi48: hsi48, | 326 | hsi48: hsi48, |
| 320 | rtc: rtc, | 327 | rtc: rtc, |
| 328 | lsi: None, | ||
| 329 | lse: None, | ||
| 321 | ); | 330 | ); |
| 322 | } | 331 | } |
| 323 | 332 | ||
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index e3c7dd158..383f48874 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -34,8 +34,10 @@ pub enum VoltageScale { | |||
| 34 | Scale2, | 34 | Scale2, |
| 35 | Scale3, | 35 | Scale3, |
| 36 | } | 36 | } |
| 37 | #[cfg(any(stm32h7rs))] | 37 | #[cfg(stm32h7rs)] |
| 38 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 38 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 39 | #[cfg(all(stm32h7rs, peri_usb_otg_hs))] | ||
| 40 | pub use crate::pac::rcc::vals::{Usbphycsel, Usbrefcksel}; | ||
| 39 | 41 | ||
| 40 | #[derive(Clone, Copy, Eq, PartialEq)] | 42 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 41 | pub enum HseMode { | 43 | pub enum HseMode { |
| @@ -111,8 +113,8 @@ pub enum TimerPrescaler { | |||
| 111 | impl From<TimerPrescaler> for Timpre { | 113 | impl From<TimerPrescaler> for Timpre { |
| 112 | fn from(value: TimerPrescaler) -> Self { | 114 | fn from(value: TimerPrescaler) -> Self { |
| 113 | match value { | 115 | match value { |
| 114 | TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2, | 116 | TimerPrescaler::DefaultX2 => Timpre::DEFAULT_X2, |
| 115 | TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4, | 117 | TimerPrescaler::DefaultX4 => Timpre::DEFAULT_X4, |
| 116 | } | 118 | } |
| 117 | } | 119 | } |
| 118 | } | 120 | } |
| @@ -120,7 +122,7 @@ impl From<TimerPrescaler> for Timpre { | |||
| 120 | /// Power supply configuration | 122 | /// Power supply configuration |
| 121 | /// See RM0433 Rev 4 7.4 | 123 | /// See RM0433 Rev 4 7.4 |
| 122 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))] | 124 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))] |
| 123 | #[derive(PartialEq)] | 125 | #[derive(Clone, Copy, PartialEq)] |
| 124 | pub enum SupplyConfig { | 126 | pub enum SupplyConfig { |
| 125 | /// Default power supply configuration. | 127 | /// Default power supply configuration. |
| 126 | /// V CORE Power Domains are supplied from the LDO according to VOS. | 128 | /// V CORE Power Domains are supplied from the LDO according to VOS. |
| @@ -180,6 +182,7 @@ pub enum SMPSSupplyVoltage { | |||
| 180 | 182 | ||
| 181 | /// Configuration of the core clocks | 183 | /// Configuration of the core clocks |
| 182 | #[non_exhaustive] | 184 | #[non_exhaustive] |
| 185 | #[derive(Clone, Copy)] | ||
| 183 | pub struct Config { | 186 | pub struct Config { |
| 184 | pub hsi: Option<HSIPrescaler>, | 187 | pub hsi: Option<HSIPrescaler>, |
| 185 | pub hse: Option<Hse>, | 188 | pub hse: Option<Hse>, |
| @@ -215,13 +218,13 @@ pub struct Config { | |||
| 215 | pub mux: super::mux::ClockMux, | 218 | pub mux: super::mux::ClockMux, |
| 216 | } | 219 | } |
| 217 | 220 | ||
| 218 | impl Default for Config { | 221 | impl Config { |
| 219 | fn default() -> Self { | 222 | pub const fn new() -> Self { |
| 220 | Self { | 223 | Self { |
| 221 | hsi: Some(HSIPrescaler::DIV1), | 224 | hsi: Some(HSIPrescaler::DIV1), |
| 222 | hse: None, | 225 | hse: None, |
| 223 | csi: false, | 226 | csi: false, |
| 224 | hsi48: Some(Default::default()), | 227 | hsi48: Some(crate::rcc::Hsi48Config::new()), |
| 225 | sys: Sysclk::HSI, | 228 | sys: Sysclk::HSI, |
| 226 | pll1: None, | 229 | pll1: None, |
| 227 | pll2: None, | 230 | pll2: None, |
| @@ -245,16 +248,22 @@ impl Default for Config { | |||
| 245 | voltage_scale: VoltageScale::Scale0, | 248 | voltage_scale: VoltageScale::Scale0, |
| 246 | #[cfg(rcc_h7rs)] | 249 | #[cfg(rcc_h7rs)] |
| 247 | voltage_scale: VoltageScale::HIGH, | 250 | voltage_scale: VoltageScale::HIGH, |
| 248 | ls: Default::default(), | 251 | ls: crate::rcc::LsConfig::new(), |
| 249 | 252 | ||
| 250 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))] | 253 | #[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468, pwr_h7rs))] |
| 251 | supply_config: SupplyConfig::LDO, | 254 | supply_config: SupplyConfig::LDO, |
| 252 | 255 | ||
| 253 | mux: Default::default(), | 256 | mux: super::mux::ClockMux::default(), |
| 254 | } | 257 | } |
| 255 | } | 258 | } |
| 256 | } | 259 | } |
| 257 | 260 | ||
| 261 | impl Default for Config { | ||
| 262 | fn default() -> Self { | ||
| 263 | Self::new() | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 258 | pub(crate) unsafe fn init(config: Config) { | 267 | pub(crate) unsafe fn init(config: Config) { |
| 259 | #[cfg(any(stm32h7))] | 268 | #[cfg(any(stm32h7))] |
| 260 | let pwr_reg = PWR.cr3(); | 269 | let pwr_reg = PWR.cr3(); |
| @@ -556,6 +565,27 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 556 | 565 | ||
| 557 | let rtc = config.ls.init(); | 566 | let rtc = config.ls.init(); |
| 558 | 567 | ||
| 568 | #[cfg(all(stm32h7rs, peri_usb_otg_hs))] | ||
| 569 | let usb_refck = match config.mux.usbphycsel { | ||
| 570 | Usbphycsel::HSE => hse, | ||
| 571 | Usbphycsel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), | ||
| 572 | Usbphycsel::PLL3_Q => pll3.q, | ||
| 573 | _ => None, | ||
| 574 | }; | ||
| 575 | #[cfg(all(stm32h7rs, peri_usb_otg_hs))] | ||
| 576 | let usb_refck_sel = match usb_refck { | ||
| 577 | Some(clk_val) => match clk_val { | ||
| 578 | Hertz(16_000_000) => Usbrefcksel::MHZ16, | ||
| 579 | Hertz(19_200_000) => Usbrefcksel::MHZ19_2, | ||
| 580 | Hertz(20_000_000) => Usbrefcksel::MHZ20, | ||
| 581 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | ||
| 582 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | ||
| 583 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | ||
| 584 | _ => panic!("cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | ||
| 585 | }, | ||
| 586 | None => Usbrefcksel::MHZ24, | ||
| 587 | }; | ||
| 588 | |||
| 559 | #[cfg(stm32h7)] | 589 | #[cfg(stm32h7)] |
| 560 | { | 590 | { |
| 561 | RCC.d1cfgr().modify(|w| { | 591 | RCC.d1cfgr().modify(|w| { |
| @@ -592,6 +622,11 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 592 | w.set_ppre4(config.apb4_pre); | 622 | w.set_ppre4(config.apb4_pre); |
| 593 | w.set_ppre5(config.apb5_pre); | 623 | w.set_ppre5(config.apb5_pre); |
| 594 | }); | 624 | }); |
| 625 | |||
| 626 | #[cfg(peri_usb_otg_hs)] | ||
| 627 | RCC.ahbperckselr().modify(|w| { | ||
| 628 | w.set_usbrefcksel(usb_refck_sel); | ||
| 629 | }); | ||
| 595 | } | 630 | } |
| 596 | #[cfg(stm32h5)] | 631 | #[cfg(stm32h5)] |
| 597 | { | 632 | { |
| @@ -658,7 +693,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 658 | hsi: hsi, | 693 | hsi: hsi, |
| 659 | hsi48: hsi48, | 694 | hsi48: hsi48, |
| 660 | csi: csi, | 695 | csi: csi, |
| 661 | csi_div_122: csi.map(|c| c / 122u32), | ||
| 662 | hse: hse, | 696 | hse: hse, |
| 663 | 697 | ||
| 664 | lse: None, | 698 | lse: None, |
| @@ -697,7 +731,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 697 | #[cfg(stm32h7rs)] | 731 | #[cfg(stm32h7rs)] |
| 698 | clk48mohci: None, // TODO | 732 | clk48mohci: None, // TODO |
| 699 | #[cfg(stm32h7rs)] | 733 | #[cfg(stm32h7rs)] |
| 700 | usb: None, // TODO | 734 | usb: Some(Hertz(48_000_000)), |
| 735 | #[cfg(stm32h5)] | ||
| 736 | hse_div_rtcpre: None, // TODO | ||
| 701 | ); | 737 | ); |
| 702 | } | 738 | } |
| 703 | 739 | ||
| @@ -748,7 +784,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput { | |||
| 748 | ..=3_999_999 => Pllrge::RANGE2, | 784 | ..=3_999_999 => Pllrge::RANGE2, |
| 749 | ..=7_999_999 => Pllrge::RANGE4, | 785 | ..=7_999_999 => Pllrge::RANGE4, |
| 750 | ..=16_000_000 => Pllrge::RANGE8, | 786 | ..=16_000_000 => Pllrge::RANGE8, |
| 751 | x => panic!("pll ref_clk out of range: {} mhz", x), | 787 | x => panic!("pll ref_clk out of range: {} hz", x), |
| 752 | }; | 788 | }; |
| 753 | 789 | ||
| 754 | // The smaller range (150 to 420 MHz) must | 790 | // The smaller range (150 to 420 MHz) must |
| @@ -757,18 +793,18 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput { | |||
| 757 | 793 | ||
| 758 | let vco_clk = ref_clk * config.mul; | 794 | let vco_clk = ref_clk * config.mul; |
| 759 | let vco_range = if VCO_RANGE.contains(&vco_clk) { | 795 | let vco_range = if VCO_RANGE.contains(&vco_clk) { |
| 760 | Pllvcosel::MEDIUMVCO | 796 | Pllvcosel::MEDIUM_VCO |
| 761 | } else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk) { | 797 | } else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk) { |
| 762 | Pllvcosel::WIDEVCO | 798 | Pllvcosel::WIDE_VCO |
| 763 | } else { | 799 | } else { |
| 764 | panic!("pll vco_clk out of range: {} hz", vco_clk.0) | 800 | panic!("pll vco_clk out of range: {}", vco_clk) |
| 765 | }; | 801 | }; |
| 766 | 802 | ||
| 767 | let p = config.divp.map(|div| { | 803 | let p = config.divp.map(|div| { |
| 768 | if num == 0 { | 804 | if num == 0 { |
| 769 | // on PLL1, DIVP must be even for most series. | 805 | // on PLL1, DIVP must be even for most series. |
| 770 | // The enum value is 1 less than the divider, so check it's odd. | 806 | // The enum value is 1 less than the divider, so check it's odd. |
| 771 | #[cfg(not(pwr_h7rm0468))] | 807 | #[cfg(not(any(pwr_h7rm0468, stm32h7rs)))] |
| 772 | assert!(div.to_bits() % 2 == 1); | 808 | assert!(div.to_bits() % 2 == 1); |
| 773 | #[cfg(pwr_h7rm0468)] | 809 | #[cfg(pwr_h7rm0468)] |
| 774 | assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0); | 810 | assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0); |
diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs index efabd059f..3ea5c96c9 100644 --- a/embassy-stm32/src/rcc/hsi48.rs +++ b/embassy-stm32/src/rcc/hsi48.rs | |||
| @@ -18,9 +18,15 @@ pub struct Hsi48Config { | |||
| 18 | pub sync_from_usb: bool, | 18 | pub sync_from_usb: bool, |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | impl Hsi48Config { | ||
| 22 | pub const fn new() -> Self { | ||
| 23 | Self { sync_from_usb: false } | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 21 | impl Default for Hsi48Config { | 27 | impl Default for Hsi48Config { |
| 22 | fn default() -> Self { | 28 | fn default() -> Self { |
| 23 | Self { sync_from_usb: false } | 29 | Self::new() |
| 24 | } | 30 | } |
| 25 | } | 31 | } |
| 26 | 32 | ||
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index e9266c65b..81b89046e 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs | |||
| @@ -5,6 +5,7 @@ use crate::pac::rcc::regs::Cfgr; | |||
| 5 | pub use crate::pac::rcc::vals::Hsepre as HsePrescaler; | 5 | pub use crate::pac::rcc::vals::Hsepre as HsePrescaler; |
| 6 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange as MSIRange, Ppre as APBPrescaler, Sw as Sysclk}; | 6 | pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange as MSIRange, Ppre as APBPrescaler, Sw as Sysclk}; |
| 7 | use crate::pac::{FLASH, RCC}; | 7 | use crate::pac::{FLASH, RCC}; |
| 8 | use crate::rcc::LSI_FREQ; | ||
| 8 | use crate::time::Hertz; | 9 | use crate::time::Hertz; |
| 9 | 10 | ||
| 10 | /// HSI speed | 11 | /// HSI speed |
| @@ -30,6 +31,7 @@ pub struct Hse { | |||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | /// Clocks configuration | 33 | /// Clocks configuration |
| 34 | #[derive(Clone, Copy)] | ||
| 33 | pub struct Config { | 35 | pub struct Config { |
| 34 | // base clock sources | 36 | // base clock sources |
| 35 | pub msi: Option<MSIRange>, | 37 | pub msi: Option<MSIRange>, |
| @@ -66,9 +68,8 @@ pub struct Config { | |||
| 66 | pub mux: super::mux::ClockMux, | 68 | pub mux: super::mux::ClockMux, |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | impl Default for Config { | 71 | impl Config { |
| 70 | #[inline] | 72 | pub const fn new() -> Self { |
| 71 | fn default() -> Config { | ||
| 72 | Config { | 73 | Config { |
| 73 | hse: None, | 74 | hse: None, |
| 74 | hsi: false, | 75 | hsi: false, |
| @@ -88,15 +89,21 @@ impl Default for Config { | |||
| 88 | #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] | 89 | #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] |
| 89 | pllsai2: None, | 90 | pllsai2: None, |
| 90 | #[cfg(crs)] | 91 | #[cfg(crs)] |
| 91 | hsi48: Some(Default::default()), | 92 | hsi48: Some(crate::rcc::Hsi48Config::new()), |
| 92 | ls: Default::default(), | 93 | ls: crate::rcc::LsConfig::new(), |
| 93 | #[cfg(any(stm32l0, stm32l1))] | 94 | #[cfg(any(stm32l0, stm32l1))] |
| 94 | voltage_scale: VoltageScale::RANGE1, | 95 | voltage_scale: VoltageScale::RANGE1, |
| 95 | mux: Default::default(), | 96 | mux: super::mux::ClockMux::default(), |
| 96 | } | 97 | } |
| 97 | } | 98 | } |
| 98 | } | 99 | } |
| 99 | 100 | ||
| 101 | impl Default for Config { | ||
| 102 | fn default() -> Config { | ||
| 103 | Self::new() | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 100 | #[cfg(stm32wb)] | 107 | #[cfg(stm32wb)] |
| 101 | pub const WPAN_DEFAULT: Config = Config { | 108 | pub const WPAN_DEFAULT: Config = Config { |
| 102 | hse: Some(Hse { | 109 | hse: Some(Hse { |
| @@ -181,6 +188,9 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 181 | 188 | ||
| 182 | let rtc = config.ls.init(); | 189 | let rtc = config.ls.init(); |
| 183 | 190 | ||
| 191 | let lse = config.ls.lse.map(|l| l.frequency); | ||
| 192 | let lsi = config.ls.lsi.then_some(LSI_FREQ); | ||
| 193 | |||
| 184 | let msi = config.msi.map(|range| { | 194 | let msi = config.msi.map(|range| { |
| 185 | msi_enable(range); | 195 | msi_enable(range); |
| 186 | msirange_to_hertz(range) | 196 | msirange_to_hertz(range) |
| @@ -391,7 +401,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 391 | hsi48: hsi48, | 401 | hsi48: hsi48, |
| 392 | 402 | ||
| 393 | #[cfg(any(stm32l0, stm32l1))] | 403 | #[cfg(any(stm32l0, stm32l1))] |
| 394 | pll1_vco_div_2: pll.vco.map(|c| c/2u32), | 404 | pll1_vco: pll.vco, |
| 395 | 405 | ||
| 396 | #[cfg(not(any(stm32l0, stm32l1)))] | 406 | #[cfg(not(any(stm32l0, stm32l1)))] |
| 397 | pll1_p: pll.p, | 407 | pll1_p: pll.p, |
| @@ -424,12 +434,12 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 424 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers | 434 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers |
| 425 | 435 | ||
| 426 | rtc: rtc, | 436 | rtc: rtc, |
| 437 | lse: lse, | ||
| 438 | lsi: lsi, | ||
| 427 | 439 | ||
| 428 | // TODO | 440 | // TODO |
| 429 | sai1_extclk: None, | 441 | sai1_extclk: None, |
| 430 | sai2_extclk: None, | 442 | sai2_extclk: None, |
| 431 | lsi: None, | ||
| 432 | lse: None, | ||
| 433 | ); | 443 | ); |
| 434 | } | 444 | } |
| 435 | 445 | ||
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index d1ce14c86..c50e071fb 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | 2 | ||
| 3 | use embassy_hal_internal::into_ref; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | 4 | ||
| 5 | use crate::gpio::{AfType, OutputType, Speed}; | 5 | use crate::gpio::{AfType, OutputType, Speed}; |
| 6 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] | 6 | #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] |
| @@ -32,7 +32,7 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; | |||
| 32 | ))] | 32 | ))] |
| 33 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; | 33 | pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; |
| 34 | use crate::pac::RCC; | 34 | use crate::pac::RCC; |
| 35 | use crate::{peripherals, Peripheral}; | 35 | use crate::{peripherals, Peri}; |
| 36 | 36 | ||
| 37 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] | 37 | #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] |
| 38 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 38 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] |
| @@ -43,7 +43,7 @@ pub enum McoPrescaler { | |||
| 43 | pub(crate) trait SealedMcoInstance {} | 43 | pub(crate) trait SealedMcoInstance {} |
| 44 | 44 | ||
| 45 | #[allow(private_bounds)] | 45 | #[allow(private_bounds)] |
| 46 | pub trait McoInstance: SealedMcoInstance + 'static { | 46 | pub trait McoInstance: PeripheralType + SealedMcoInstance + 'static { |
| 47 | type Source; | 47 | type Source; |
| 48 | 48 | ||
| 49 | #[doc(hidden)] | 49 | #[doc(hidden)] |
| @@ -91,14 +91,7 @@ pub struct Mco<'d, T: McoInstance> { | |||
| 91 | 91 | ||
| 92 | impl<'d, T: McoInstance> Mco<'d, T> { | 92 | impl<'d, T: McoInstance> Mco<'d, T> { |
| 93 | /// Create a new MCO instance. | 93 | /// Create a new MCO instance. |
| 94 | pub fn new( | 94 | pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin<T>>, source: T::Source, prescaler: McoPrescaler) -> Self { |
| 95 | _peri: impl Peripheral<P = T> + 'd, | ||
| 96 | pin: impl Peripheral<P = impl McoPin<T>> + 'd, | ||
| 97 | source: T::Source, | ||
| 98 | prescaler: McoPrescaler, | ||
| 99 | ) -> Self { | ||
| 100 | into_ref!(pin); | ||
| 101 | |||
| 102 | critical_section::with(|_| unsafe { | 95 | critical_section::with(|_| unsafe { |
| 103 | T::_apply_clock_settings(source, prescaler); | 96 | T::_apply_clock_settings(source, prescaler); |
| 104 | pin.set_as_af(pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 97 | pin.set_as_af(pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 8022a35a4..c41f81816 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -86,7 +86,7 @@ pub(crate) unsafe fn set_freqs(freqs: Clocks) { | |||
| 86 | #[cfg(not(feature = "_dual-core"))] | 86 | #[cfg(not(feature = "_dual-core"))] |
| 87 | /// Safety: Reads a mutable global. | 87 | /// Safety: Reads a mutable global. |
| 88 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { | 88 | pub(crate) unsafe fn get_freqs() -> &'static Clocks { |
| 89 | CLOCK_FREQS.assume_init_ref() | 89 | (*core::ptr::addr_of_mut!(CLOCK_FREQS)).assume_init_ref() |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | #[cfg(feature = "_dual-core")] | 92 | #[cfg(feature = "_dual-core")] |
| @@ -95,8 +95,19 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { | |||
| 95 | unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() | 95 | unwrap!(CLOCK_FREQS_PTR.load(core::sync::atomic::Ordering::SeqCst).as_ref()).assume_init_ref() |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | /// Get the current clock configuration of the chip. | ||
| 99 | pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clocks { | ||
| 100 | // Safety: the existence of a `Peri<RCC>` means that `rcc::init()` | ||
| 101 | // has already been called, so `CLOCK_FREQS` must be initialized. | ||
| 102 | // The clocks could be modified again by `reinit()`, but reinit | ||
| 103 | // (for this reason) requires an exclusive reference to `Peri<RCC>`. | ||
| 104 | unsafe { get_freqs() } | ||
| 105 | } | ||
| 106 | |||
| 98 | pub(crate) trait SealedRccPeripheral { | 107 | pub(crate) trait SealedRccPeripheral { |
| 99 | fn frequency() -> Hertz; | 108 | fn frequency() -> Hertz; |
| 109 | #[allow(dead_code)] | ||
| 110 | fn bus_frequency() -> Hertz; | ||
| 100 | const RCC_INFO: RccInfo; | 111 | const RCC_INFO: RccInfo; |
| 101 | } | 112 | } |
| 102 | 113 | ||
| @@ -171,7 +182,9 @@ impl RccInfo { | |||
| 171 | // Use .get_mut instead of []-operator so that we control how bounds checks happen. | 182 | // Use .get_mut instead of []-operator so that we control how bounds checks happen. |
| 172 | // Otherwise, core::fmt will be pulled in here in order to format the integer in the | 183 | // Otherwise, core::fmt will be pulled in here in order to format the integer in the |
| 173 | // out-of-bounds error. | 184 | // out-of-bounds error. |
| 174 | if let Some(refcount) = unsafe { crate::_generated::REFCOUNTS.get_mut(refcount_idx) } { | 185 | if let Some(refcount) = |
| 186 | unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) } | ||
| 187 | { | ||
| 175 | *refcount += 1; | 188 | *refcount += 1; |
| 176 | if *refcount > 1 { | 189 | if *refcount > 1 { |
| 177 | return; | 190 | return; |
| @@ -235,7 +248,9 @@ impl RccInfo { | |||
| 235 | // Use .get_mut instead of []-operator so that we control how bounds checks happen. | 248 | // Use .get_mut instead of []-operator so that we control how bounds checks happen. |
| 236 | // Otherwise, core::fmt will be pulled in here in order to format the integer in the | 249 | // Otherwise, core::fmt will be pulled in here in order to format the integer in the |
| 237 | // out-of-bounds error. | 250 | // out-of-bounds error. |
| 238 | if let Some(refcount) = unsafe { crate::_generated::REFCOUNTS.get_mut(refcount_idx) } { | 251 | if let Some(refcount) = |
| 252 | unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) } | ||
| 253 | { | ||
| 239 | *refcount -= 1; | 254 | *refcount -= 1; |
| 240 | if *refcount > 0 { | 255 | if *refcount > 0 { |
| 241 | return; | 256 | return; |
| @@ -365,3 +380,32 @@ pub fn enable_and_reset<T: RccPeripheral>() { | |||
| 365 | pub fn disable<T: RccPeripheral>() { | 380 | pub fn disable<T: RccPeripheral>() { |
| 366 | T::RCC_INFO.disable(); | 381 | T::RCC_INFO.disable(); |
| 367 | } | 382 | } |
| 383 | |||
| 384 | /// Re-initialize the `embassy-stm32` clock configuration with the provided configuration. | ||
| 385 | /// | ||
| 386 | /// This is useful when you need to alter the CPU clock after configuring peripherals. | ||
| 387 | /// For instance, configure an external clock via spi or i2c. | ||
| 388 | /// | ||
| 389 | /// Please not this only re-configures the rcc and the time driver (not GPIO, EXTI, etc). | ||
| 390 | /// | ||
| 391 | /// This should only be called after `init`. | ||
| 392 | #[cfg(not(feature = "_dual-core"))] | ||
| 393 | pub fn reinit<'a>(config: Config, _rcc: &'a mut crate::Peri<'a, crate::peripherals::RCC>) { | ||
| 394 | critical_section::with(|cs| init_rcc(cs, config)) | ||
| 395 | } | ||
| 396 | |||
| 397 | pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { | ||
| 398 | unsafe { | ||
| 399 | init(config); | ||
| 400 | |||
| 401 | // must be after rcc init | ||
| 402 | #[cfg(feature = "_time-driver")] | ||
| 403 | crate::time_driver::init(_cs); | ||
| 404 | |||
| 405 | #[cfg(feature = "low-power")] | ||
| 406 | { | ||
| 407 | REFCOUNT_STOP2 = 0; | ||
| 408 | REFCOUNT_STOP1 = 0; | ||
| 409 | } | ||
| 410 | } | ||
| 411 | } | ||
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index d6331f512..97eb2eb6d 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs | |||
| @@ -1,10 +1,15 @@ | |||
| 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; | 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 2 | #[cfg(all(peri_usb_otg_hs))] | ||
| 3 | pub use crate::pac::rcc::vals::Otghssel; | ||
| 2 | pub use crate::pac::rcc::vals::{ | 4 | pub use crate::pac::rcc::vals::{ |
| 3 | Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, | 5 | Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, |
| 4 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, | 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 5 | }; | 7 | }; |
| 6 | use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; | 8 | use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; |
| 9 | #[cfg(all(peri_usb_otg_hs))] | ||
| 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; | ||
| 7 | use crate::pac::{FLASH, PWR, RCC}; | 11 | use crate::pac::{FLASH, PWR, RCC}; |
| 12 | use crate::rcc::LSI_FREQ; | ||
| 8 | use crate::time::Hertz; | 13 | use crate::time::Hertz; |
| 9 | 14 | ||
| 10 | /// HSI speed | 15 | /// HSI speed |
| @@ -59,9 +64,11 @@ pub struct Pll { | |||
| 59 | pub divr: Option<PllDiv>, | 64 | pub divr: Option<PllDiv>, |
| 60 | } | 65 | } |
| 61 | 66 | ||
| 67 | #[derive(Clone, Copy)] | ||
| 62 | pub struct Config { | 68 | pub struct Config { |
| 63 | // base clock sources | 69 | // base clock sources |
| 64 | pub msi: Option<MSIRange>, | 70 | pub msis: Option<MSIRange>, |
| 71 | pub msik: Option<MSIRange>, | ||
| 65 | pub hsi: bool, | 72 | pub hsi: bool, |
| 66 | pub hse: Option<Hse>, | 73 | pub hse: Option<Hse>, |
| 67 | pub hsi48: Option<super::Hsi48Config>, | 74 | pub hsi48: Option<super::Hsi48Config>, |
| @@ -90,13 +97,14 @@ pub struct Config { | |||
| 90 | pub mux: super::mux::ClockMux, | 97 | pub mux: super::mux::ClockMux, |
| 91 | } | 98 | } |
| 92 | 99 | ||
| 93 | impl Default for Config { | 100 | impl Config { |
| 94 | fn default() -> Self { | 101 | pub const fn new() -> Self { |
| 95 | Self { | 102 | Self { |
| 96 | msi: Some(Msirange::RANGE_4MHZ), | 103 | msis: Some(Msirange::RANGE_4MHZ), |
| 104 | msik: Some(Msirange::RANGE_4MHZ), | ||
| 97 | hse: None, | 105 | hse: None, |
| 98 | hsi: false, | 106 | hsi: false, |
| 99 | hsi48: Some(Default::default()), | 107 | hsi48: Some(crate::rcc::Hsi48Config::new()), |
| 100 | pll1: None, | 108 | pll1: None, |
| 101 | pll2: None, | 109 | pll2: None, |
| 102 | pll3: None, | 110 | pll3: None, |
| @@ -106,18 +114,24 @@ impl Default for Config { | |||
| 106 | apb2_pre: APBPrescaler::DIV1, | 114 | apb2_pre: APBPrescaler::DIV1, |
| 107 | apb3_pre: APBPrescaler::DIV1, | 115 | apb3_pre: APBPrescaler::DIV1, |
| 108 | voltage_range: VoltageScale::RANGE1, | 116 | voltage_range: VoltageScale::RANGE1, |
| 109 | ls: Default::default(), | 117 | ls: crate::rcc::LsConfig::new(), |
| 110 | mux: Default::default(), | 118 | mux: super::mux::ClockMux::default(), |
| 111 | } | 119 | } |
| 112 | } | 120 | } |
| 113 | } | 121 | } |
| 114 | 122 | ||
| 123 | impl Default for Config { | ||
| 124 | fn default() -> Self { | ||
| 125 | Self::new() | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 115 | pub(crate) unsafe fn init(config: Config) { | 129 | pub(crate) unsafe fn init(config: Config) { |
| 116 | // Set the requested power mode | 130 | // Set the requested power mode |
| 117 | PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); | 131 | PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); |
| 118 | while !PWR.vosr().read().vosrdy() {} | 132 | while !PWR.vosr().read().vosrdy() {} |
| 119 | 133 | ||
| 120 | let msi = config.msi.map(|range| { | 134 | let msis = config.msis.map(|range| { |
| 121 | // Check MSI output per RM0456 § 11.4.10 | 135 | // Check MSI output per RM0456 § 11.4.10 |
| 122 | match config.voltage_range { | 136 | match config.voltage_range { |
| 123 | VoltageScale::RANGE4 => { | 137 | VoltageScale::RANGE4 => { |
| @@ -146,8 +160,36 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 146 | msirange_to_hertz(range) | 160 | msirange_to_hertz(range) |
| 147 | }); | 161 | }); |
| 148 | 162 | ||
| 163 | let msik = config.msik.map(|range| { | ||
| 164 | // Check MSI output per RM0456 § 11.4.10 | ||
| 165 | match config.voltage_range { | ||
| 166 | VoltageScale::RANGE4 => { | ||
| 167 | assert!(msirange_to_hertz(range).0 <= 24_000_000); | ||
| 168 | } | ||
| 169 | _ => {} | ||
| 170 | } | ||
| 171 | |||
| 172 | // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range | ||
| 173 | loop { | ||
| 174 | let cr = RCC.cr().read(); | ||
| 175 | if cr.msikon() == false || cr.msikrdy() == true { | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | RCC.icscr1().modify(|w| { | ||
| 181 | w.set_msikrange(range); | ||
| 182 | w.set_msirgsel(Msirgsel::ICSCR1); | ||
| 183 | }); | ||
| 184 | RCC.cr().modify(|w| { | ||
| 185 | w.set_msikon(true); | ||
| 186 | }); | ||
| 187 | while !RCC.cr().read().msikrdy() {} | ||
| 188 | msirange_to_hertz(range) | ||
| 189 | }); | ||
| 190 | |||
| 149 | let hsi = config.hsi.then(|| { | 191 | let hsi = config.hsi.then(|| { |
| 150 | RCC.cr().write(|w| w.set_hsion(true)); | 192 | RCC.cr().modify(|w| w.set_hsion(true)); |
| 151 | while !RCC.cr().read().hsirdy() {} | 193 | while !RCC.cr().read().hsirdy() {} |
| 152 | 194 | ||
| 153 | HSI_FREQ | 195 | HSI_FREQ |
| @@ -165,7 +207,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 165 | } | 207 | } |
| 166 | 208 | ||
| 167 | // Enable HSE, and wait for it to stabilize | 209 | // Enable HSE, and wait for it to stabilize |
| 168 | RCC.cr().write(|w| { | 210 | RCC.cr().modify(|w| { |
| 169 | w.set_hseon(true); | 211 | w.set_hseon(true); |
| 170 | w.set_hsebyp(hse.mode != HseMode::Oscillator); | 212 | w.set_hsebyp(hse.mode != HseMode::Oscillator); |
| 171 | w.set_hseext(match hse.mode { | 213 | w.set_hseext(match hse.mode { |
| @@ -180,7 +222,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 180 | 222 | ||
| 181 | let hsi48 = config.hsi48.map(super::init_hsi48); | 223 | let hsi48 = config.hsi48.map(super::init_hsi48); |
| 182 | 224 | ||
| 183 | let pll_input = PllInput { hse, hsi, msi }; | 225 | let pll_input = PllInput { hse, hsi, msi: msis }; |
| 184 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); | 226 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); |
| 185 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); | 227 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); |
| 186 | let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range); | 228 | let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range); |
| @@ -188,7 +230,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 188 | let sys_clk = match config.sys { | 230 | let sys_clk = match config.sys { |
| 189 | Sysclk::HSE => hse.unwrap(), | 231 | Sysclk::HSE => hse.unwrap(), |
| 190 | Sysclk::HSI => hsi.unwrap(), | 232 | Sysclk::HSI => hsi.unwrap(), |
| 191 | Sysclk::MSIS => msi.unwrap(), | 233 | Sysclk::MSIS => msis.unwrap(), |
| 192 | Sysclk::PLL1_R => pll1.r.unwrap(), | 234 | Sysclk::PLL1_R => pll1.r.unwrap(), |
| 193 | }; | 235 | }; |
| 194 | 236 | ||
| @@ -263,6 +305,34 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 263 | 305 | ||
| 264 | let rtc = config.ls.init(); | 306 | let rtc = config.ls.init(); |
| 265 | 307 | ||
| 308 | #[cfg(all(stm32u5, peri_usb_otg_hs))] | ||
| 309 | let usb_refck = match config.mux.otghssel { | ||
| 310 | Otghssel::HSE => hse, | ||
| 311 | Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), | ||
| 312 | Otghssel::PLL1_P => pll1.p, | ||
| 313 | Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8), | ||
| 314 | }; | ||
| 315 | #[cfg(all(stm32u5, peri_usb_otg_hs))] | ||
| 316 | let usb_refck_sel = match usb_refck { | ||
| 317 | Some(clk_val) => match clk_val { | ||
| 318 | Hertz(16_000_000) => Usbrefcksel::MHZ16, | ||
| 319 | Hertz(19_200_000) => Usbrefcksel::MHZ19_2, | ||
| 320 | Hertz(20_000_000) => Usbrefcksel::MHZ20, | ||
| 321 | Hertz(24_000_000) => Usbrefcksel::MHZ24, | ||
| 322 | Hertz(26_000_000) => Usbrefcksel::MHZ26, | ||
| 323 | Hertz(32_000_000) => Usbrefcksel::MHZ32, | ||
| 324 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), | ||
| 325 | }, | ||
| 326 | None => Usbrefcksel::MHZ24, | ||
| 327 | }; | ||
| 328 | #[cfg(all(stm32u5, peri_usb_otg_hs))] | ||
| 329 | SYSCFG.otghsphycr().modify(|w| { | ||
| 330 | w.set_clksel(usb_refck_sel); | ||
| 331 | }); | ||
| 332 | |||
| 333 | let lse = config.ls.lse.map(|l| l.frequency); | ||
| 334 | let lsi = config.ls.lsi.then_some(LSI_FREQ); | ||
| 335 | |||
| 266 | config.mux.init(); | 336 | config.mux.init(); |
| 267 | 337 | ||
| 268 | set_clocks!( | 338 | set_clocks!( |
| @@ -275,8 +345,11 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 275 | pclk3: Some(pclk3), | 345 | pclk3: Some(pclk3), |
| 276 | pclk1_tim: Some(pclk1_tim), | 346 | pclk1_tim: Some(pclk1_tim), |
| 277 | pclk2_tim: Some(pclk2_tim), | 347 | pclk2_tim: Some(pclk2_tim), |
| 348 | msik: msik, | ||
| 278 | hsi48: hsi48, | 349 | hsi48: hsi48, |
| 279 | rtc: rtc, | 350 | rtc: rtc, |
| 351 | lse: lse, | ||
| 352 | lsi: lsi, | ||
| 280 | hse: hse, | 353 | hse: hse, |
| 281 | hsi: hsi, | 354 | hsi: hsi, |
| 282 | pll1_p: pll1.p, | 355 | pll1_p: pll1.p, |
| @@ -294,12 +367,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 294 | 367 | ||
| 295 | // TODO | 368 | // TODO |
| 296 | audioclk: None, | 369 | audioclk: None, |
| 297 | hsi48_div_2: None, | ||
| 298 | lse: None, | ||
| 299 | lsi: None, | ||
| 300 | msik: None, | ||
| 301 | shsi: None, | 370 | shsi: None, |
| 302 | shsi_div_2: None, | ||
| 303 | ); | 371 | ); |
| 304 | } | 372 | } |
| 305 | 373 | ||
diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 8e1779d7c..b9fc4e423 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs | |||
| @@ -15,6 +15,7 @@ pub struct Hse { | |||
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | /// Clocks configuration | 17 | /// Clocks configuration |
| 18 | #[derive(Clone, Copy)] | ||
| 18 | pub struct Config { | 19 | pub struct Config { |
| 19 | // base clock sources | 20 | // base clock sources |
| 20 | pub hsi: bool, | 21 | pub hsi: bool, |
| @@ -36,9 +37,8 @@ pub struct Config { | |||
| 36 | pub mux: super::mux::ClockMux, | 37 | pub mux: super::mux::ClockMux, |
| 37 | } | 38 | } |
| 38 | 39 | ||
| 39 | impl Default for Config { | 40 | impl Config { |
| 40 | #[inline] | 41 | pub const fn new() -> Self { |
| 41 | fn default() -> Config { | ||
| 42 | Config { | 42 | Config { |
| 43 | hse: None, | 43 | hse: None, |
| 44 | hsi: true, | 44 | hsi: true, |
| @@ -47,13 +47,19 @@ impl Default for Config { | |||
| 47 | apb1_pre: APBPrescaler::DIV1, | 47 | apb1_pre: APBPrescaler::DIV1, |
| 48 | apb2_pre: APBPrescaler::DIV1, | 48 | apb2_pre: APBPrescaler::DIV1, |
| 49 | apb7_pre: APBPrescaler::DIV1, | 49 | apb7_pre: APBPrescaler::DIV1, |
| 50 | ls: Default::default(), | 50 | ls: crate::rcc::LsConfig::new(), |
| 51 | voltage_scale: VoltageScale::RANGE2, | 51 | voltage_scale: VoltageScale::RANGE2, |
| 52 | mux: Default::default(), | 52 | mux: super::mux::ClockMux::default(), |
| 53 | } | 53 | } |
| 54 | } | 54 | } |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | impl Default for Config { | ||
| 58 | fn default() -> Config { | ||
| 59 | Self::new() | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 57 | fn hsi_enable() { | 63 | fn hsi_enable() { |
| 58 | RCC.cr().modify(|w| w.set_hsion(true)); | 64 | RCC.cr().modify(|w| w.set_hsion(true)); |
| 59 | while !RCC.cr().read().hsirdy() {} | 65 | while !RCC.cr().read().hsirdy() {} |
diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 6f4c81c8a..312f343b9 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs | |||
| @@ -5,12 +5,11 @@ use core::future::poll_fn; | |||
| 5 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use rand_core::{CryptoRng, RngCore}; | ||
| 11 | 10 | ||
| 12 | use crate::interrupt::typelevel::Interrupt; | 11 | use crate::interrupt::typelevel::Interrupt; |
| 13 | use crate::{interrupt, pac, peripherals, rcc, Peripheral}; | 12 | use crate::{interrupt, pac, peripherals, rcc, Peri}; |
| 14 | 13 | ||
| 15 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); | 14 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); |
| 16 | 15 | ||
| @@ -43,17 +42,16 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 43 | 42 | ||
| 44 | /// RNG driver. | 43 | /// RNG driver. |
| 45 | pub struct Rng<'d, T: Instance> { | 44 | pub struct Rng<'d, T: Instance> { |
| 46 | _inner: PeripheralRef<'d, T>, | 45 | _inner: Peri<'d, T>, |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | impl<'d, T: Instance> Rng<'d, T> { | 48 | impl<'d, T: Instance> Rng<'d, T> { |
| 50 | /// Create a new RNG driver. | 49 | /// Create a new RNG driver. |
| 51 | pub fn new( | 50 | pub fn new( |
| 52 | inner: impl Peripheral<P = T> + 'd, | 51 | inner: Peri<'d, T>, |
| 53 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 52 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 54 | ) -> Self { | 53 | ) -> Self { |
| 55 | rcc::enable_and_reset::<T>(); | 54 | rcc::enable_and_reset::<T>(); |
| 56 | into_ref!(inner); | ||
| 57 | let mut random = Self { _inner: inner }; | 55 | let mut random = Self { _inner: inner }; |
| 58 | random.reset(); | 56 | random.reset(); |
| 59 | 57 | ||
| @@ -88,10 +86,10 @@ impl<'d, T: Instance> Rng<'d, T> { | |||
| 88 | reg.set_nistc(pac::rng::vals::Nistc::CUSTOM); | 86 | reg.set_nistc(pac::rng::vals::Nistc::CUSTOM); |
| 89 | // set RNG config "A" according to reference manual | 87 | // set RNG config "A" according to reference manual |
| 90 | // this has to be written within the same write access as setting the CONDRST bit | 88 | // this has to be written within the same write access as setting the CONDRST bit |
| 91 | reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); | 89 | reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIG_A); |
| 92 | reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); | 90 | reg.set_clkdiv(pac::rng::vals::Clkdiv::NO_DIV); |
| 93 | reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); | 91 | reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIG_A_B); |
| 94 | reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); | 92 | reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIG_A); |
| 95 | reg.set_ced(true); | 93 | reg.set_ced(true); |
| 96 | reg.set_ie(false); | 94 | reg.set_ie(false); |
| 97 | reg.set_rngen(true); | 95 | reg.set_rngen(true); |
| @@ -185,10 +183,9 @@ impl<'d, T: Instance> Rng<'d, T> { | |||
| 185 | 183 | ||
| 186 | Ok(()) | 184 | Ok(()) |
| 187 | } | 185 | } |
| 188 | } | ||
| 189 | 186 | ||
| 190 | impl<'d, T: Instance> RngCore for Rng<'d, T> { | 187 | /// Get a random u32 |
| 191 | fn next_u32(&mut self) -> u32 { | 188 | pub fn next_u32(&mut self) -> u32 { |
| 192 | loop { | 189 | loop { |
| 193 | let sr = T::regs().sr().read(); | 190 | let sr = T::regs().sr().read(); |
| 194 | if sr.seis() | sr.ceis() { | 191 | if sr.seis() | sr.ceis() { |
| @@ -199,13 +196,15 @@ impl<'d, T: Instance> RngCore for Rng<'d, T> { | |||
| 199 | } | 196 | } |
| 200 | } | 197 | } |
| 201 | 198 | ||
| 202 | fn next_u64(&mut self) -> u64 { | 199 | /// Get a random u64 |
| 200 | pub fn next_u64(&mut self) -> u64 { | ||
| 203 | let mut rand = self.next_u32() as u64; | 201 | let mut rand = self.next_u32() as u64; |
| 204 | rand |= (self.next_u32() as u64) << 32; | 202 | rand |= (self.next_u32() as u64) << 32; |
| 205 | rand | 203 | rand |
| 206 | } | 204 | } |
| 207 | 205 | ||
| 208 | fn fill_bytes(&mut self, dest: &mut [u8]) { | 206 | /// Fill a slice with random bytes |
| 207 | pub fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
| 209 | for chunk in dest.chunks_mut(4) { | 208 | for chunk in dest.chunks_mut(4) { |
| 210 | let rand = self.next_u32(); | 209 | let rand = self.next_u32(); |
| 211 | for (slot, num) in chunk.iter_mut().zip(rand.to_ne_bytes().iter()) { | 210 | for (slot, num) in chunk.iter_mut().zip(rand.to_ne_bytes().iter()) { |
| @@ -213,14 +212,53 @@ impl<'d, T: Instance> RngCore for Rng<'d, T> { | |||
| 213 | } | 212 | } |
| 214 | } | 213 | } |
| 215 | } | 214 | } |
| 215 | } | ||
| 216 | |||
| 217 | impl<'d, T: Instance> Drop for Rng<'d, T> { | ||
| 218 | fn drop(&mut self) { | ||
| 219 | T::regs().cr().modify(|reg| { | ||
| 220 | reg.set_rngen(false); | ||
| 221 | }); | ||
| 222 | rcc::disable::<T>(); | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | impl<'d, T: Instance> rand_core_06::RngCore for Rng<'d, T> { | ||
| 227 | fn next_u32(&mut self) -> u32 { | ||
| 228 | self.next_u32() | ||
| 229 | } | ||
| 230 | |||
| 231 | fn next_u64(&mut self) -> u64 { | ||
| 232 | self.next_u64() | ||
| 233 | } | ||
| 216 | 234 | ||
| 217 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { | 235 | fn fill_bytes(&mut self, dest: &mut [u8]) { |
| 236 | self.fill_bytes(dest); | ||
| 237 | } | ||
| 238 | |||
| 239 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> { | ||
| 218 | self.fill_bytes(dest); | 240 | self.fill_bytes(dest); |
| 219 | Ok(()) | 241 | Ok(()) |
| 220 | } | 242 | } |
| 221 | } | 243 | } |
| 222 | 244 | ||
| 223 | impl<'d, T: Instance> CryptoRng for Rng<'d, T> {} | 245 | impl<'d, T: Instance> rand_core_06::CryptoRng for Rng<'d, T> {} |
| 246 | |||
| 247 | impl<'d, T: Instance> rand_core_09::RngCore for Rng<'d, T> { | ||
| 248 | fn next_u32(&mut self) -> u32 { | ||
| 249 | self.next_u32() | ||
| 250 | } | ||
| 251 | |||
| 252 | fn next_u64(&mut self) -> u64 { | ||
| 253 | self.next_u64() | ||
| 254 | } | ||
| 255 | |||
| 256 | fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
| 257 | self.fill_bytes(dest); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | impl<'d, T: Instance> rand_core_09::CryptoRng for Rng<'d, T> {} | ||
| 224 | 262 | ||
| 225 | trait SealedInstance { | 263 | trait SealedInstance { |
| 226 | fn regs() -> pac::rng::Rng; | 264 | fn regs() -> pac::rng::Rng; |
| @@ -228,7 +266,7 @@ trait SealedInstance { | |||
| 228 | 266 | ||
| 229 | /// RNG instance trait. | 267 | /// RNG instance trait. |
| 230 | #[allow(private_bounds)] | 268 | #[allow(private_bounds)] |
| 231 | pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send { | 269 | pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send { |
| 232 | /// Interrupt for this RNG instance. | 270 | /// Interrupt for this RNG instance. |
| 233 | type Interrupt: interrupt::typelevel::Interrupt; | 271 | type Interrupt: interrupt::typelevel::Interrupt; |
| 234 | } | 272 | } |
diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 32732e96e..8b420bb6e 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs | |||
| @@ -3,6 +3,7 @@ use chrono::{Datelike, NaiveDate, Timelike, Weekday}; | |||
| 3 | 3 | ||
| 4 | /// Errors regarding the [`DateTime`] struct. | 4 | /// Errors regarding the [`DateTime`] struct. |
| 5 | #[derive(Clone, Debug, PartialEq, Eq)] | 5 | #[derive(Clone, Debug, PartialEq, Eq)] |
| 6 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 6 | pub enum Error { | 7 | pub enum Error { |
| 7 | /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. | 8 | /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. |
| 8 | InvalidYear, | 9 | InvalidYear, |
| @@ -21,9 +22,12 @@ pub enum Error { | |||
| 21 | InvalidMinute, | 22 | InvalidMinute, |
| 22 | /// The [DateTime] contains an invalid second value. Must be between `0..=59`. | 23 | /// The [DateTime] contains an invalid second value. Must be between `0..=59`. |
| 23 | InvalidSecond, | 24 | InvalidSecond, |
| 25 | /// The [DateTime] contains an invalid microsecond value. Must be between `0..=999_999`. | ||
| 26 | InvalidMicrosecond, | ||
| 24 | } | 27 | } |
| 25 | 28 | ||
| 26 | /// Structure containing date and time information | 29 | /// Structure containing date and time information |
| 30 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 27 | pub struct DateTime { | 31 | pub struct DateTime { |
| 28 | /// 0..4095 | 32 | /// 0..4095 |
| 29 | year: u16, | 33 | year: u16, |
| @@ -39,6 +43,8 @@ pub struct DateTime { | |||
| 39 | minute: u8, | 43 | minute: u8, |
| 40 | /// 0..59 | 44 | /// 0..59 |
| 41 | second: u8, | 45 | second: u8, |
| 46 | /// 0..999_999 | ||
| 47 | usecond: u32, | ||
| 42 | } | 48 | } |
| 43 | 49 | ||
| 44 | impl DateTime { | 50 | impl DateTime { |
| @@ -77,6 +83,11 @@ impl DateTime { | |||
| 77 | self.second | 83 | self.second |
| 78 | } | 84 | } |
| 79 | 85 | ||
| 86 | /// Get the microsecond (0..=999_999) | ||
| 87 | pub const fn microsecond(&self) -> u32 { | ||
| 88 | self.usecond | ||
| 89 | } | ||
| 90 | |||
| 80 | /// Create a new DateTime with the given information. | 91 | /// Create a new DateTime with the given information. |
| 81 | pub fn from( | 92 | pub fn from( |
| 82 | year: u16, | 93 | year: u16, |
| @@ -86,6 +97,7 @@ impl DateTime { | |||
| 86 | hour: u8, | 97 | hour: u8, |
| 87 | minute: u8, | 98 | minute: u8, |
| 88 | second: u8, | 99 | second: u8, |
| 100 | usecond: u32, | ||
| 89 | ) -> Result<Self, Error> { | 101 | ) -> Result<Self, Error> { |
| 90 | if year > 4095 { | 102 | if year > 4095 { |
| 91 | Err(Error::InvalidYear) | 103 | Err(Error::InvalidYear) |
| @@ -99,6 +111,8 @@ impl DateTime { | |||
| 99 | Err(Error::InvalidMinute) | 111 | Err(Error::InvalidMinute) |
| 100 | } else if second > 59 { | 112 | } else if second > 59 { |
| 101 | Err(Error::InvalidSecond) | 113 | Err(Error::InvalidSecond) |
| 114 | } else if usecond > 999_999 { | ||
| 115 | Err(Error::InvalidMicrosecond) | ||
| 102 | } else { | 116 | } else { |
| 103 | Ok(Self { | 117 | Ok(Self { |
| 104 | year, | 118 | year, |
| @@ -108,6 +122,7 @@ impl DateTime { | |||
| 108 | hour, | 122 | hour, |
| 109 | minute, | 123 | minute, |
| 110 | second, | 124 | second, |
| 125 | usecond, | ||
| 111 | }) | 126 | }) |
| 112 | } | 127 | } |
| 113 | } | 128 | } |
| @@ -124,6 +139,7 @@ impl From<chrono::NaiveDateTime> for DateTime { | |||
| 124 | hour: date_time.hour() as u8, | 139 | hour: date_time.hour() as u8, |
| 125 | minute: date_time.minute() as u8, | 140 | minute: date_time.minute() as u8, |
| 126 | second: date_time.second() as u8, | 141 | second: date_time.second() as u8, |
| 142 | usecond: date_time.and_utc().timestamp_subsec_micros(), | ||
| 127 | } | 143 | } |
| 128 | } | 144 | } |
| 129 | } | 145 | } |
| @@ -133,7 +149,12 @@ impl From<DateTime> for chrono::NaiveDateTime { | |||
| 133 | fn from(date_time: DateTime) -> Self { | 149 | fn from(date_time: DateTime) -> Self { |
| 134 | NaiveDate::from_ymd_opt(date_time.year as i32, date_time.month as u32, date_time.day as u32) | 150 | NaiveDate::from_ymd_opt(date_time.year as i32, date_time.month as u32, date_time.day as u32) |
| 135 | .unwrap() | 151 | .unwrap() |
| 136 | .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32) | 152 | .and_hms_micro_opt( |
| 153 | date_time.hour as u32, | ||
| 154 | date_time.minute as u32, | ||
| 155 | date_time.second as u32, | ||
| 156 | date_time.usecond, | ||
| 157 | ) | ||
| 137 | .unwrap() | 158 | .unwrap() |
| 138 | } | 159 | } |
| 139 | } | 160 | } |
| @@ -141,6 +162,7 @@ impl From<DateTime> for chrono::NaiveDateTime { | |||
| 141 | /// A day of the week | 162 | /// A day of the week |
| 142 | #[repr(u8)] | 163 | #[repr(u8)] |
| 143 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] | 164 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] |
| 165 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 144 | #[allow(missing_docs)] | 166 | #[allow(missing_docs)] |
| 145 | pub enum DayOfWeek { | 167 | pub enum DayOfWeek { |
| 146 | Monday = 1, | 168 | Monday = 1, |
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index b9aaa63b9..cd075f3de 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs | |||
| @@ -65,7 +65,9 @@ pub(crate) enum WakeupPrescaler { | |||
| 65 | Div16 = 16, | 65 | Div16 = 16, |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | #[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))] | 68 | #[cfg(any( |
| 69 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 | ||
| 70 | ))] | ||
| 69 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | 71 | impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { |
| 70 | fn from(val: WakeupPrescaler) -> Self { | 72 | fn from(val: WakeupPrescaler) -> Self { |
| 71 | use crate::pac::rtc::vals::Wucksel; | 73 | use crate::pac::rtc::vals::Wucksel; |
| @@ -79,7 +81,9 @@ impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel { | |||
| 79 | } | 81 | } |
| 80 | } | 82 | } |
| 81 | 83 | ||
| 82 | #[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))] | 84 | #[cfg(any( |
| 85 | stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 | ||
| 86 | ))] | ||
| 83 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { | 87 | impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler { |
| 84 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { | 88 | fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { |
| 85 | use crate::pac::rtc::vals::Wucksel; | 89 | use crate::pac::rtc::vals::Wucksel; |
| @@ -219,12 +223,29 @@ impl Rtc { | |||
| 219 | 223 | ||
| 220 | pub(crate) fn enable_wakeup_line(&self) { | 224 | pub(crate) fn enable_wakeup_line(&self) { |
| 221 | use crate::interrupt::typelevel::Interrupt; | 225 | use crate::interrupt::typelevel::Interrupt; |
| 222 | use crate::pac::EXTI; | ||
| 223 | 226 | ||
| 224 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); | 227 | <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend(); |
| 225 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; | 228 | unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() }; |
| 226 | 229 | ||
| 227 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | 230 | #[cfg(not(any(stm32u5, stm32u0)))] |
| 228 | EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | 231 | { |
| 232 | use crate::pac::EXTI; | ||
| 233 | EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 234 | |||
| 235 | #[cfg(not(stm32wb))] | ||
| 236 | { | ||
| 237 | EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 238 | } | ||
| 239 | #[cfg(stm32wb)] | ||
| 240 | { | ||
| 241 | EXTI.cpu(0).imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | #[cfg(stm32u5)] | ||
| 245 | { | ||
| 246 | use crate::pac::RCC; | ||
| 247 | RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); | ||
| 248 | RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true)); | ||
| 249 | } | ||
| 229 | } | 250 | } |
| 230 | } | 251 | } |
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index fe57cfe66..49f423f37 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs | |||
| @@ -25,17 +25,18 @@ use crate::time::Hertz; | |||
| 25 | ), | 25 | ), |
| 26 | path = "v2.rs" | 26 | path = "v2.rs" |
| 27 | )] | 27 | )] |
| 28 | #[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5), path = "v3.rs")] | 28 | #[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs), path = "v3.rs")] |
| 29 | mod _version; | 29 | mod _version; |
| 30 | #[allow(unused_imports)] | 30 | #[allow(unused_imports)] |
| 31 | pub use _version::*; | 31 | pub use _version::*; |
| 32 | use embassy_hal_internal::Peripheral; | ||
| 33 | 32 | ||
| 34 | use crate::peripherals::RTC; | 33 | use crate::peripherals::RTC; |
| 34 | use crate::Peri; | ||
| 35 | 35 | ||
| 36 | /// Errors that can occur on methods on [RtcClock] | 36 | /// Errors that can occur on methods on [RtcClock] |
| 37 | #[non_exhaustive] | 37 | #[non_exhaustive] |
| 38 | #[derive(Clone, Debug, PartialEq, Eq)] | 38 | #[derive(Clone, Debug, PartialEq, Eq)] |
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 39 | pub enum RtcError { | 40 | pub enum RtcError { |
| 40 | /// An invalid DateTime was given or stored on the hardware. | 41 | /// An invalid DateTime was given or stored on the hardware. |
| 41 | InvalidDateTime(DateTimeError), | 42 | InvalidDateTime(DateTimeError), |
| @@ -59,7 +60,7 @@ impl RtcTimeProvider { | |||
| 59 | /// | 60 | /// |
| 60 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. | 61 | /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. |
| 61 | pub fn now(&self) -> Result<DateTime, RtcError> { | 62 | pub fn now(&self) -> Result<DateTime, RtcError> { |
| 62 | self.read(|dr, tr, _| { | 63 | self.read(|dr, tr, _ss| { |
| 63 | let second = bcd2_to_byte((tr.st(), tr.su())); | 64 | let second = bcd2_to_byte((tr.st(), tr.su())); |
| 64 | let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); | 65 | let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); |
| 65 | let hour = bcd2_to_byte((tr.ht(), tr.hu())); | 66 | let hour = bcd2_to_byte((tr.ht(), tr.hu())); |
| @@ -69,7 +70,17 @@ impl RtcTimeProvider { | |||
| 69 | let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); | 70 | let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); |
| 70 | let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 2000_u16; | 71 | let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 2000_u16; |
| 71 | 72 | ||
| 72 | DateTime::from(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) | 73 | // Calculate second fraction and multiply to microseconds |
| 74 | // Formula from RM0410 | ||
| 75 | #[cfg(not(rtc_v2f2))] | ||
| 76 | let us = { | ||
| 77 | let prediv = RTC::regs().prer().read().prediv_s() as f32; | ||
| 78 | (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32 | ||
| 79 | }; | ||
| 80 | #[cfg(rtc_v2f2)] | ||
| 81 | let us = 0; | ||
| 82 | |||
| 83 | DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime) | ||
| 73 | }) | 84 | }) |
| 74 | } | 85 | } |
| 75 | 86 | ||
| @@ -140,7 +151,7 @@ pub enum RtcCalibrationCyclePeriod { | |||
| 140 | 151 | ||
| 141 | impl Rtc { | 152 | impl Rtc { |
| 142 | /// Create a new RTC instance. | 153 | /// Create a new RTC instance. |
| 143 | pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self { | 154 | pub fn new(_rtc: Peri<'static, RTC>, rtc_config: RtcConfig) -> Self { |
| 144 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] | 155 | #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] |
| 145 | crate::rcc::enable_and_reset::<RTC>(); | 156 | crate::rcc::enable_and_reset::<RTC>(); |
| 146 | 157 | ||
| @@ -285,6 +296,7 @@ trait SealedInstance { | |||
| 285 | const BACKUP_REGISTER_COUNT: usize; | 296 | const BACKUP_REGISTER_COUNT: usize; |
| 286 | 297 | ||
| 287 | #[cfg(feature = "low-power")] | 298 | #[cfg(feature = "low-power")] |
| 299 | #[cfg(not(any(stm32u5, stm32u0)))] | ||
| 288 | const EXTI_WAKEUP_LINE: usize; | 300 | const EXTI_WAKEUP_LINE: usize; |
| 289 | 301 | ||
| 290 | #[cfg(feature = "low-power")] | 302 | #[cfg(feature = "low-power")] |
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index cdc1cb299..28380a3c0 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs | |||
| @@ -77,7 +77,7 @@ impl super::Rtc { | |||
| 77 | // When the offset is positive (0 to 512), the opposite of | 77 | // When the offset is positive (0 to 512), the opposite of |
| 78 | // the offset (512 - offset) is masked, i.e. for the | 78 | // the offset (512 - offset) is masked, i.e. for the |
| 79 | // maximum offset (512), 0 pulses are masked. | 79 | // maximum offset (512), 0 pulses are masked. |
| 80 | w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ); | 80 | w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASE_FREQ); |
| 81 | w.set_calm(512 - clock_drift as u16); | 81 | w.set_calm(512 - clock_drift as u16); |
| 82 | } else { | 82 | } else { |
| 83 | // Minimum (about -510.7) rounds to -511. | 83 | // Minimum (about -510.7) rounds to -511. |
| @@ -86,7 +86,7 @@ impl super::Rtc { | |||
| 86 | // When the offset is negative or zero (-511 to 0), | 86 | // When the offset is negative or zero (-511 to 0), |
| 87 | // the absolute offset is masked, i.e. for the minimum | 87 | // the absolute offset is masked, i.e. for the minimum |
| 88 | // offset (-511), 511 pulses are masked. | 88 | // offset (-511), 511 pulses are masked. |
| 89 | w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE); | 89 | w.set_calp(stm32_metapac::rtc::vals::Calp::NO_CHANGE); |
| 90 | w.set_calm((clock_drift * -1.0) as u16); | 90 | w.set_calm((clock_drift * -1.0) as u16); |
| 91 | } | 91 | } |
| 92 | }); | 92 | }); |
| @@ -131,10 +131,16 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 131 | #[cfg(all(feature = "low-power", stm32f4))] | 131 | #[cfg(all(feature = "low-power", stm32f4))] |
| 132 | const EXTI_WAKEUP_LINE: usize = 22; | 132 | const EXTI_WAKEUP_LINE: usize = 22; |
| 133 | 133 | ||
| 134 | #[cfg(all(feature = "low-power", stm32l4))] | ||
| 135 | const EXTI_WAKEUP_LINE: usize = 20; | ||
| 136 | |||
| 134 | #[cfg(all(feature = "low-power", stm32l0))] | 137 | #[cfg(all(feature = "low-power", stm32l0))] |
| 135 | const EXTI_WAKEUP_LINE: usize = 20; | 138 | const EXTI_WAKEUP_LINE: usize = 20; |
| 136 | 139 | ||
| 137 | #[cfg(all(feature = "low-power", stm32f4))] | 140 | #[cfg(all(feature = "low-power", stm32wb))] |
| 141 | const EXTI_WAKEUP_LINE: usize = 19; | ||
| 142 | |||
| 143 | #[cfg(all(feature = "low-power", any(stm32f4, stm32l4, stm32wb)))] | ||
| 138 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | 144 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; |
| 139 | 145 | ||
| 140 | #[cfg(all(feature = "low-power", stm32l0))] | 146 | #[cfg(all(feature = "low-power", stm32l0))] |
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 02fd5272e..39aa6c5cb 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs | |||
| @@ -12,7 +12,7 @@ impl super::Rtc { | |||
| 12 | self.write(true, |rtc| { | 12 | self.write(true, |rtc| { |
| 13 | rtc.cr().modify(|w| { | 13 | rtc.cr().modify(|w| { |
| 14 | w.set_bypshad(true); | 14 | w.set_bypshad(true); |
| 15 | w.set_fmt(Fmt::TWENTYFOURHOUR); | 15 | w.set_fmt(Fmt::TWENTY_FOUR_HOUR); |
| 16 | w.set_osel(Osel::DISABLED); | 16 | w.set_osel(Osel::DISABLED); |
| 17 | w.set_pol(Pol::HIGH); | 17 | w.set_pol(Pol::HIGH); |
| 18 | }); | 18 | }); |
| @@ -25,7 +25,7 @@ impl super::Rtc { | |||
| 25 | // TODO: configuration for output pins | 25 | // TODO: configuration for output pins |
| 26 | rtc.cr().modify(|w| { | 26 | rtc.cr().modify(|w| { |
| 27 | w.set_out2en(false); | 27 | w.set_out2en(false); |
| 28 | w.set_tampalrm_type(TampalrmType::PUSHPULL); | 28 | w.set_tampalrm_type(TampalrmType::PUSH_PULL); |
| 29 | w.set_tampalrm_pu(false); | 29 | w.set_tampalrm_pu(false); |
| 30 | }); | 30 | }); |
| 31 | }); | 31 | }); |
| @@ -56,10 +56,10 @@ impl super::Rtc { | |||
| 56 | rtc.calr().write(|w| { | 56 | rtc.calr().write(|w| { |
| 57 | match period { | 57 | match period { |
| 58 | RtcCalibrationCyclePeriod::Seconds8 => { | 58 | RtcCalibrationCyclePeriod::Seconds8 => { |
| 59 | w.set_calw8(Calw8::EIGHTSECONDS); | 59 | w.set_calw8(Calw8::EIGHT_SECONDS); |
| 60 | } | 60 | } |
| 61 | RtcCalibrationCyclePeriod::Seconds16 => { | 61 | RtcCalibrationCyclePeriod::Seconds16 => { |
| 62 | w.set_calw16(Calw16::SIXTEENSECONDS); | 62 | w.set_calw16(Calw16::SIXTEEN_SECONDS); |
| 63 | } | 63 | } |
| 64 | RtcCalibrationCyclePeriod::Seconds32 => { | 64 | RtcCalibrationCyclePeriod::Seconds32 => { |
| 65 | // Set neither `calw8` nor `calw16` to use 32 seconds | 65 | // Set neither `calw8` nor `calw16` to use 32 seconds |
| @@ -79,7 +79,7 @@ impl super::Rtc { | |||
| 79 | // When the offset is positive (0 to 512), the opposite of | 79 | // When the offset is positive (0 to 512), the opposite of |
| 80 | // the offset (512 - offset) is masked, i.e. for the | 80 | // the offset (512 - offset) is masked, i.e. for the |
| 81 | // maximum offset (512), 0 pulses are masked. | 81 | // maximum offset (512), 0 pulses are masked. |
| 82 | w.set_calp(Calp::INCREASEFREQ); | 82 | w.set_calp(Calp::INCREASE_FREQ); |
| 83 | w.set_calm(512 - clock_drift as u16); | 83 | w.set_calm(512 - clock_drift as u16); |
| 84 | } else { | 84 | } else { |
| 85 | // Minimum (about -510.7) rounds to -511. | 85 | // Minimum (about -510.7) rounds to -511. |
| @@ -88,7 +88,7 @@ impl super::Rtc { | |||
| 88 | // When the offset is negative or zero (-511 to 0), | 88 | // When the offset is negative or zero (-511 to 0), |
| 89 | // the absolute offset is masked, i.e. for the minimum | 89 | // the absolute offset is masked, i.e. for the minimum |
| 90 | // offset (-511), 511 pulses are masked. | 90 | // offset (-511), 511 pulses are masked. |
| 91 | w.set_calp(Calp::NOCHANGE); | 91 | w.set_calp(Calp::NO_CHANGE); |
| 92 | w.set_calm((clock_drift * -1.0) as u16); | 92 | w.set_calm((clock_drift * -1.0) as u16); |
| 93 | } | 93 | } |
| 94 | }); | 94 | }); |
| @@ -133,12 +133,20 @@ impl SealedInstance for crate::peripherals::RTC { | |||
| 133 | cfg_if::cfg_if!( | 133 | cfg_if::cfg_if!( |
| 134 | if #[cfg(stm32g4)] { | 134 | if #[cfg(stm32g4)] { |
| 135 | const EXTI_WAKEUP_LINE: usize = 20; | 135 | const EXTI_WAKEUP_LINE: usize = 20; |
| 136 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | ||
| 137 | } else if #[cfg(stm32g0)] { | 136 | } else if #[cfg(stm32g0)] { |
| 138 | const EXTI_WAKEUP_LINE: usize = 19; | 137 | const EXTI_WAKEUP_LINE: usize = 19; |
| 139 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; | ||
| 140 | } else if #[cfg(any(stm32l5, stm32h5))] { | 138 | } else if #[cfg(any(stm32l5, stm32h5))] { |
| 141 | const EXTI_WAKEUP_LINE: usize = 17; | 139 | const EXTI_WAKEUP_LINE: usize = 17; |
| 140 | } | ||
| 141 | ); | ||
| 142 | |||
| 143 | #[cfg(feature = "low-power")] | ||
| 144 | cfg_if::cfg_if!( | ||
| 145 | if #[cfg(stm32g4)] { | ||
| 146 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; | ||
| 147 | } else if #[cfg(any(stm32g0, stm32u0))] { | ||
| 148 | type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; | ||
| 149 | } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { | ||
| 142 | type WakeupInterrupt = crate::interrupt::typelevel::RTC; | 150 | type WakeupInterrupt = crate::interrupt::typelevel::RTC; |
| 143 | } | 151 | } |
| 144 | ); | 152 | ); |
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index c48d81b5f..0c9c27797 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | use core::marker::PhantomData; | 5 | use core::marker::PhantomData; |
| 6 | 6 | ||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 7 | use embassy_hal_internal::PeripheralType; |
| 8 | 8 | ||
| 9 | pub use crate::dma::word; | 9 | pub use crate::dma::word; |
| 10 | #[cfg(not(gpdma))] | 10 | #[cfg(not(gpdma))] |
| @@ -12,7 +12,7 @@ use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptio | |||
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 13 | use crate::pac::sai::{vals, Sai as Regs}; | 13 | use crate::pac::sai::{vals, Sai as Regs}; |
| 14 | use crate::rcc::{self, RccPeripheral}; | 14 | use crate::rcc::{self, RccPeripheral}; |
| 15 | use crate::{peripherals, Peripheral}; | 15 | use crate::{peripherals, Peri}; |
| 16 | 16 | ||
| 17 | /// SAI error | 17 | /// SAI error |
| 18 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 18 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| @@ -27,8 +27,14 @@ pub enum Error { | |||
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | #[cfg(not(gpdma))] | 29 | #[cfg(not(gpdma))] |
| 30 | impl From<ringbuffer::OverrunError> for Error { | 30 | impl From<ringbuffer::Error> for Error { |
| 31 | fn from(_: ringbuffer::OverrunError) -> Self { | 31 | fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { |
| 32 | #[cfg(feature = "defmt")] | ||
| 33 | { | ||
| 34 | if err == ringbuffer::Error::DmaUnsynced { | ||
| 35 | defmt::error!("Ringbuffer broken invariants detected!"); | ||
| 36 | } | ||
| 37 | } | ||
| 32 | Self::Overrun | 38 | Self::Overrun |
| 33 | } | 39 | } |
| 34 | } | 40 | } |
| @@ -46,12 +52,12 @@ impl Mode { | |||
| 46 | const fn mode(&self, tx_rx: TxRx) -> vals::Mode { | 52 | const fn mode(&self, tx_rx: TxRx) -> vals::Mode { |
| 47 | match tx_rx { | 53 | match tx_rx { |
| 48 | TxRx::Transmitter => match self { | 54 | TxRx::Transmitter => match self { |
| 49 | Mode::Master => vals::Mode::MASTERTX, | 55 | Mode::Master => vals::Mode::MASTER_TX, |
| 50 | Mode::Slave => vals::Mode::SLAVETX, | 56 | Mode::Slave => vals::Mode::SLAVE_TX, |
| 51 | }, | 57 | }, |
| 52 | TxRx::Receiver => match self { | 58 | TxRx::Receiver => match self { |
| 53 | Mode::Master => vals::Mode::MASTERRX, | 59 | Mode::Master => vals::Mode::MASTER_RX, |
| 54 | Mode::Slave => vals::Mode::SLAVERX, | 60 | Mode::Slave => vals::Mode::SLAVE_RX, |
| 55 | }, | 61 | }, |
| 56 | } | 62 | } |
| 57 | } | 63 | } |
| @@ -80,7 +86,7 @@ impl SlotSize { | |||
| 80 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 86 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 81 | const fn slotsz(&self) -> vals::Slotsz { | 87 | const fn slotsz(&self) -> vals::Slotsz { |
| 82 | match self { | 88 | match self { |
| 83 | SlotSize::DataSize => vals::Slotsz::DATASIZE, | 89 | SlotSize::DataSize => vals::Slotsz::DATA_SIZE, |
| 84 | SlotSize::Channel16 => vals::Slotsz::BIT16, | 90 | SlotSize::Channel16 => vals::Slotsz::BIT16, |
| 85 | SlotSize::Channel32 => vals::Slotsz::BIT32, | 91 | SlotSize::Channel32 => vals::Slotsz::BIT32, |
| 86 | } | 92 | } |
| @@ -149,8 +155,8 @@ impl MuteValue { | |||
| 149 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 155 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 150 | const fn muteval(&self) -> vals::Muteval { | 156 | const fn muteval(&self) -> vals::Muteval { |
| 151 | match self { | 157 | match self { |
| 152 | MuteValue::Zero => vals::Muteval::SENDZERO, | 158 | MuteValue::Zero => vals::Muteval::SEND_ZERO, |
| 153 | MuteValue::LastValue => vals::Muteval::SENDLAST, | 159 | MuteValue::LastValue => vals::Muteval::SEND_LAST, |
| 154 | } | 160 | } |
| 155 | } | 161 | } |
| 156 | } | 162 | } |
| @@ -184,7 +190,7 @@ pub enum SyncInput { | |||
| 184 | /// Syncs with the other A/B sub-block within the SAI unit | 190 | /// Syncs with the other A/B sub-block within the SAI unit |
| 185 | Internal, | 191 | Internal, |
| 186 | /// Syncs with a sub-block in the other SAI unit | 192 | /// Syncs with a sub-block in the other SAI unit |
| 187 | #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))] | 193 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 188 | External(SyncInputInstance), | 194 | External(SyncInputInstance), |
| 189 | } | 195 | } |
| 190 | 196 | ||
| @@ -193,14 +199,14 @@ impl SyncInput { | |||
| 193 | match self { | 199 | match self { |
| 194 | SyncInput::None => vals::Syncen::ASYNCHRONOUS, | 200 | SyncInput::None => vals::Syncen::ASYNCHRONOUS, |
| 195 | SyncInput::Internal => vals::Syncen::INTERNAL, | 201 | SyncInput::Internal => vals::Syncen::INTERNAL, |
| 196 | #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))] | 202 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 197 | SyncInput::External(_) => vals::Syncen::EXTERNAL, | 203 | SyncInput::External(_) => vals::Syncen::EXTERNAL, |
| 198 | } | 204 | } |
| 199 | } | 205 | } |
| 200 | } | 206 | } |
| 201 | 207 | ||
| 202 | /// SAI instance to sync from. | 208 | /// SAI instance to sync from. |
| 203 | #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))] | 209 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 204 | #[derive(Copy, Clone, PartialEq)] | 210 | #[derive(Copy, Clone, PartialEq)] |
| 205 | #[allow(missing_docs)] | 211 | #[allow(missing_docs)] |
| 206 | pub enum SyncInputInstance { | 212 | pub enum SyncInputInstance { |
| @@ -245,8 +251,8 @@ impl BitOrder { | |||
| 245 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 251 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 246 | const fn lsbfirst(&self) -> vals::Lsbfirst { | 252 | const fn lsbfirst(&self) -> vals::Lsbfirst { |
| 247 | match self { | 253 | match self { |
| 248 | BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST, | 254 | BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST, |
| 249 | BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST, | 255 | BitOrder::MsbFirst => vals::Lsbfirst::MSB_FIRST, |
| 250 | } | 256 | } |
| 251 | } | 257 | } |
| 252 | } | 258 | } |
| @@ -264,8 +270,8 @@ impl FrameSyncOffset { | |||
| 264 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 270 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 265 | const fn fsoff(&self) -> vals::Fsoff { | 271 | const fn fsoff(&self) -> vals::Fsoff { |
| 266 | match self { | 272 | match self { |
| 267 | FrameSyncOffset::OnFirstBit => vals::Fsoff::ONFIRST, | 273 | FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST, |
| 268 | FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFOREFIRST, | 274 | FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFORE_FIRST, |
| 269 | } | 275 | } |
| 270 | } | 276 | } |
| 271 | } | 277 | } |
| @@ -283,8 +289,8 @@ impl FrameSyncPolarity { | |||
| 283 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 289 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 284 | const fn fspol(&self) -> vals::Fspol { | 290 | const fn fspol(&self) -> vals::Fspol { |
| 285 | match self { | 291 | match self { |
| 286 | FrameSyncPolarity::ActiveLow => vals::Fspol::FALLINGEDGE, | 292 | FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE, |
| 287 | FrameSyncPolarity::ActiveHigh => vals::Fspol::RISINGEDGE, | 293 | FrameSyncPolarity::ActiveHigh => vals::Fspol::RISING_EDGE, |
| 288 | } | 294 | } |
| 289 | } | 295 | } |
| 290 | } | 296 | } |
| @@ -319,8 +325,8 @@ impl ClockStrobe { | |||
| 319 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 325 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 320 | const fn ckstr(&self) -> vals::Ckstr { | 326 | const fn ckstr(&self) -> vals::Ckstr { |
| 321 | match self { | 327 | match self { |
| 322 | ClockStrobe::Falling => vals::Ckstr::FALLINGEDGE, | 328 | ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE, |
| 323 | ClockStrobe::Rising => vals::Ckstr::RISINGEDGE, | 329 | ClockStrobe::Rising => vals::Ckstr::RISING_EDGE, |
| 324 | } | 330 | } |
| 325 | } | 331 | } |
| 326 | } | 332 | } |
| @@ -337,8 +343,8 @@ impl ComplementFormat { | |||
| 337 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 343 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 338 | const fn cpl(&self) -> vals::Cpl { | 344 | const fn cpl(&self) -> vals::Cpl { |
| 339 | match self { | 345 | match self { |
| 340 | ComplementFormat::OnesComplement => vals::Cpl::ONESCOMPLEMENT, | 346 | ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT, |
| 341 | ComplementFormat::TwosComplement => vals::Cpl::TWOSCOMPLEMENT, | 347 | ComplementFormat::TwosComplement => vals::Cpl::TWOS_COMPLEMENT, |
| 342 | } | 348 | } |
| 343 | } | 349 | } |
| 344 | } | 350 | } |
| @@ -356,8 +362,8 @@ impl Companding { | |||
| 356 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 362 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 357 | const fn comp(&self) -> vals::Comp { | 363 | const fn comp(&self) -> vals::Comp { |
| 358 | match self { | 364 | match self { |
| 359 | Companding::None => vals::Comp::NOCOMPANDING, | 365 | Companding::None => vals::Comp::NO_COMPANDING, |
| 360 | Companding::MuLaw => vals::Comp::MULAW, | 366 | Companding::MuLaw => vals::Comp::MU_LAW, |
| 361 | Companding::ALaw => vals::Comp::ALAW, | 367 | Companding::ALaw => vals::Comp::ALAW, |
| 362 | } | 368 | } |
| 363 | } | 369 | } |
| @@ -375,7 +381,7 @@ impl OutputDrive { | |||
| 375 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 381 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 376 | const fn outdriv(&self) -> vals::Outdriv { | 382 | const fn outdriv(&self) -> vals::Outdriv { |
| 377 | match self { | 383 | match self { |
| 378 | OutputDrive::OnStart => vals::Outdriv::ONSTART, | 384 | OutputDrive::OnStart => vals::Outdriv::ON_START, |
| 379 | OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY, | 385 | OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY, |
| 380 | } | 386 | } |
| 381 | } | 387 | } |
| @@ -661,19 +667,19 @@ fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) { | |||
| 661 | //sd is defined by tx/rx mode | 667 | //sd is defined by tx/rx mode |
| 662 | match tx_rx { | 668 | match tx_rx { |
| 663 | TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh), | 669 | TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh), |
| 664 | TxRx::Receiver => AfType::input(Pull::None), | 670 | TxRx::Receiver => AfType::input(Pull::Down), // Ensure mute level when no input is connected. |
| 665 | }, | 671 | }, |
| 666 | //clocks (mclk, sck and fs) are defined by master/slave | 672 | //clocks (mclk, sck and fs) are defined by master/slave |
| 667 | match mode { | 673 | match mode { |
| 668 | Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh), | 674 | Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh), |
| 669 | Mode::Slave => AfType::input(Pull::None), | 675 | Mode::Slave => AfType::input(Pull::Down), // Ensure no clocks when no input is connected. |
| 670 | }, | 676 | }, |
| 671 | ) | 677 | ) |
| 672 | } | 678 | } |
| 673 | 679 | ||
| 674 | #[cfg(not(gpdma))] | 680 | #[cfg(not(gpdma))] |
| 675 | fn get_ring_buffer<'d, T: Instance, W: word::Word>( | 681 | fn get_ring_buffer<'d, T: Instance, W: word::Word>( |
| 676 | dma: impl Peripheral<P = impl Channel> + 'd, | 682 | dma: Peri<'d, impl Channel>, |
| 677 | dma_buf: &'d mut [W], | 683 | dma_buf: &'d mut [W], |
| 678 | request: Request, | 684 | request: Request, |
| 679 | sub_block: WhichSubBlock, | 685 | sub_block: WhichSubBlock, |
| @@ -698,12 +704,12 @@ fn update_synchronous_config(config: &mut Config) { | |||
| 698 | config.mode = Mode::Slave; | 704 | config.mode = Mode::Slave; |
| 699 | config.sync_output = false; | 705 | config.sync_output = false; |
| 700 | 706 | ||
| 701 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm))] | 707 | #[cfg(any(sai_v1, sai_v2))] |
| 702 | { | 708 | { |
| 703 | config.sync_input = SyncInput::Internal; | 709 | config.sync_input = SyncInput::Internal; |
| 704 | } | 710 | } |
| 705 | 711 | ||
| 706 | #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))] | 712 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 707 | { | 713 | { |
| 708 | //this must either be Internal or External | 714 | //this must either be Internal or External |
| 709 | //The asynchronous sub-block on the same SAI needs to enable sync_output | 715 | //The asynchronous sub-block on the same SAI needs to enable sync_output |
| @@ -712,16 +718,15 @@ fn update_synchronous_config(config: &mut Config) { | |||
| 712 | } | 718 | } |
| 713 | 719 | ||
| 714 | /// SAI subblock instance. | 720 | /// SAI subblock instance. |
| 715 | pub struct SubBlock<'d, T, S: SubBlockInstance> { | 721 | pub struct SubBlock<'d, T: Instance, S: SubBlockInstance> { |
| 716 | peri: PeripheralRef<'d, T>, | 722 | peri: Peri<'d, T>, |
| 717 | _phantom: PhantomData<S>, | 723 | _phantom: PhantomData<S>, |
| 718 | } | 724 | } |
| 719 | 725 | ||
| 720 | /// Split the main SAIx peripheral into the two subblocks. | 726 | /// Split the main SAIx peripheral into the two subblocks. |
| 721 | /// | 727 | /// |
| 722 | /// You can then create a [`Sai`] driver for each each half. | 728 | /// You can then create a [`Sai`] driver for each each half. |
| 723 | pub fn split_subblocks<'d, T: Instance>(peri: impl Peripheral<P = T> + 'd) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) { | 729 | pub fn split_subblocks<'d, T: Instance>(peri: Peri<'d, T>) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) { |
| 724 | into_ref!(peri); | ||
| 725 | rcc::enable_and_reset::<T>(); | 730 | rcc::enable_and_reset::<T>(); |
| 726 | 731 | ||
| 727 | ( | 732 | ( |
| @@ -738,11 +743,11 @@ pub fn split_subblocks<'d, T: Instance>(peri: impl Peripheral<P = T> + 'd) -> (S | |||
| 738 | 743 | ||
| 739 | /// SAI sub-block driver. | 744 | /// SAI sub-block driver. |
| 740 | pub struct Sai<'d, T: Instance, W: word::Word> { | 745 | pub struct Sai<'d, T: Instance, W: word::Word> { |
| 741 | _peri: PeripheralRef<'d, T>, | 746 | _peri: Peri<'d, T>, |
| 742 | sd: Option<PeripheralRef<'d, AnyPin>>, | 747 | sd: Option<Peri<'d, AnyPin>>, |
| 743 | fs: Option<PeripheralRef<'d, AnyPin>>, | 748 | fs: Option<Peri<'d, AnyPin>>, |
| 744 | sck: Option<PeripheralRef<'d, AnyPin>>, | 749 | sck: Option<Peri<'d, AnyPin>>, |
| 745 | mclk: Option<PeripheralRef<'d, AnyPin>>, | 750 | mclk: Option<Peri<'d, AnyPin>>, |
| 746 | #[cfg(gpdma)] | 751 | #[cfg(gpdma)] |
| 747 | ring_buffer: PhantomData<W>, | 752 | ring_buffer: PhantomData<W>, |
| 748 | #[cfg(not(gpdma))] | 753 | #[cfg(not(gpdma))] |
| @@ -757,16 +762,14 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 757 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. | 762 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. |
| 758 | pub fn new_asynchronous_with_mclk<S: SubBlockInstance>( | 763 | pub fn new_asynchronous_with_mclk<S: SubBlockInstance>( |
| 759 | peri: SubBlock<'d, T, S>, | 764 | peri: SubBlock<'d, T, S>, |
| 760 | sck: impl Peripheral<P = impl SckPin<T, S>> + 'd, | 765 | sck: Peri<'d, impl SckPin<T, S>>, |
| 761 | sd: impl Peripheral<P = impl SdPin<T, S>> + 'd, | 766 | sd: Peri<'d, impl SdPin<T, S>>, |
| 762 | fs: impl Peripheral<P = impl FsPin<T, S>> + 'd, | 767 | fs: Peri<'d, impl FsPin<T, S>>, |
| 763 | mclk: impl Peripheral<P = impl MclkPin<T, S>> + 'd, | 768 | mclk: Peri<'d, impl MclkPin<T, S>>, |
| 764 | dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd, | 769 | dma: Peri<'d, impl Channel + Dma<T, S>>, |
| 765 | dma_buf: &'d mut [W], | 770 | dma_buf: &'d mut [W], |
| 766 | mut config: Config, | 771 | mut config: Config, |
| 767 | ) -> Self { | 772 | ) -> Self { |
| 768 | into_ref!(mclk); | ||
| 769 | |||
| 770 | let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); | 773 | let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); |
| 771 | mclk.set_as_af(mclk.af_num(), ck_af_type); | 774 | mclk.set_as_af(mclk.af_num(), ck_af_type); |
| 772 | 775 | ||
| @@ -782,15 +785,14 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 782 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. | 785 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. |
| 783 | pub fn new_asynchronous<S: SubBlockInstance>( | 786 | pub fn new_asynchronous<S: SubBlockInstance>( |
| 784 | peri: SubBlock<'d, T, S>, | 787 | peri: SubBlock<'d, T, S>, |
| 785 | sck: impl Peripheral<P = impl SckPin<T, S>> + 'd, | 788 | sck: Peri<'d, impl SckPin<T, S>>, |
| 786 | sd: impl Peripheral<P = impl SdPin<T, S>> + 'd, | 789 | sd: Peri<'d, impl SdPin<T, S>>, |
| 787 | fs: impl Peripheral<P = impl FsPin<T, S>> + 'd, | 790 | fs: Peri<'d, impl FsPin<T, S>>, |
| 788 | dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd, | 791 | dma: Peri<'d, impl Channel + Dma<T, S>>, |
| 789 | dma_buf: &'d mut [W], | 792 | dma_buf: &'d mut [W], |
| 790 | config: Config, | 793 | config: Config, |
| 791 | ) -> Self { | 794 | ) -> Self { |
| 792 | let peri = peri.peri; | 795 | let peri = peri.peri; |
| 793 | into_ref!(peri, dma, sck, sd, fs); | ||
| 794 | 796 | ||
| 795 | let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); | 797 | let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); |
| 796 | sd.set_as_af(sd.af_num(), sd_af_type); | 798 | sd.set_as_af(sd.af_num(), sd_af_type); |
| @@ -803,10 +805,10 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 803 | Self::new_inner( | 805 | Self::new_inner( |
| 804 | peri, | 806 | peri, |
| 805 | sub_block, | 807 | sub_block, |
| 806 | Some(sck.map_into()), | 808 | Some(sck.into()), |
| 807 | None, | 809 | None, |
| 808 | Some(sd.map_into()), | 810 | Some(sd.into()), |
| 809 | Some(fs.map_into()), | 811 | Some(fs.into()), |
| 810 | get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx), | 812 | get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx), |
| 811 | config, | 813 | config, |
| 812 | ) | 814 | ) |
| @@ -817,15 +819,14 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 817 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. | 819 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. |
| 818 | pub fn new_synchronous<S: SubBlockInstance>( | 820 | pub fn new_synchronous<S: SubBlockInstance>( |
| 819 | peri: SubBlock<'d, T, S>, | 821 | peri: SubBlock<'d, T, S>, |
| 820 | sd: impl Peripheral<P = impl SdPin<T, S>> + 'd, | 822 | sd: Peri<'d, impl SdPin<T, S>>, |
| 821 | dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd, | 823 | dma: Peri<'d, impl Channel + Dma<T, S>>, |
| 822 | dma_buf: &'d mut [W], | 824 | dma_buf: &'d mut [W], |
| 823 | mut config: Config, | 825 | mut config: Config, |
| 824 | ) -> Self { | 826 | ) -> Self { |
| 825 | update_synchronous_config(&mut config); | 827 | update_synchronous_config(&mut config); |
| 826 | 828 | ||
| 827 | let peri = peri.peri; | 829 | let peri = peri.peri; |
| 828 | into_ref!(dma, peri, sd); | ||
| 829 | 830 | ||
| 830 | let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); | 831 | let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); |
| 831 | sd.set_as_af(sd.af_num(), sd_af_type); | 832 | sd.set_as_af(sd.af_num(), sd_af_type); |
| @@ -838,7 +839,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 838 | sub_block, | 839 | sub_block, |
| 839 | None, | 840 | None, |
| 840 | None, | 841 | None, |
| 841 | Some(sd.map_into()), | 842 | Some(sd.into()), |
| 842 | None, | 843 | None, |
| 843 | get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx), | 844 | get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx), |
| 844 | config, | 845 | config, |
| @@ -846,22 +847,25 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 846 | } | 847 | } |
| 847 | 848 | ||
| 848 | fn new_inner( | 849 | fn new_inner( |
| 849 | peri: impl Peripheral<P = T> + 'd, | 850 | peri: Peri<'d, T>, |
| 850 | sub_block: WhichSubBlock, | 851 | sub_block: WhichSubBlock, |
| 851 | sck: Option<PeripheralRef<'d, AnyPin>>, | 852 | sck: Option<Peri<'d, AnyPin>>, |
| 852 | mclk: Option<PeripheralRef<'d, AnyPin>>, | 853 | mclk: Option<Peri<'d, AnyPin>>, |
| 853 | sd: Option<PeripheralRef<'d, AnyPin>>, | 854 | sd: Option<Peri<'d, AnyPin>>, |
| 854 | fs: Option<PeripheralRef<'d, AnyPin>>, | 855 | fs: Option<Peri<'d, AnyPin>>, |
| 855 | ring_buffer: RingBuffer<'d, W>, | 856 | ring_buffer: RingBuffer<'d, W>, |
| 856 | config: Config, | 857 | config: Config, |
| 857 | ) -> Self { | 858 | ) -> Self { |
| 859 | let ch = T::REGS.ch(sub_block as usize); | ||
| 860 | |||
| 858 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 861 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 859 | { | 862 | { |
| 860 | let ch = T::REGS.ch(sub_block as usize); | ||
| 861 | ch.cr1().modify(|w| w.set_saien(false)); | 863 | ch.cr1().modify(|w| w.set_saien(false)); |
| 862 | } | 864 | } |
| 863 | 865 | ||
| 864 | #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))] | 866 | ch.cr2().modify(|w| w.set_fflush(true)); |
| 867 | |||
| 868 | #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | ||
| 865 | { | 869 | { |
| 866 | if let SyncInput::External(i) = config.sync_input { | 870 | if let SyncInput::External(i) = config.sync_input { |
| 867 | T::REGS.gcr().modify(|w| { | 871 | T::REGS.gcr().modify(|w| { |
| @@ -882,7 +886,6 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 882 | 886 | ||
| 883 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] | 887 | #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
| 884 | { | 888 | { |
| 885 | let ch = T::REGS.ch(sub_block as usize); | ||
| 886 | ch.cr1().modify(|w| { | 889 | ch.cr1().modify(|w| { |
| 887 | w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { | 890 | w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { |
| 888 | TxRx::Transmitter | 891 | TxRx::Transmitter |
| @@ -899,9 +902,9 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 899 | w.set_mckdiv(config.master_clock_divider.mckdiv()); | 902 | w.set_mckdiv(config.master_clock_divider.mckdiv()); |
| 900 | w.set_nodiv( | 903 | w.set_nodiv( |
| 901 | if config.master_clock_divider == MasterClockDivider::MasterClockDisabled { | 904 | if config.master_clock_divider == MasterClockDivider::MasterClockDisabled { |
| 902 | vals::Nodiv::NODIV | 905 | vals::Nodiv::NO_DIV |
| 903 | } else { | 906 | } else { |
| 904 | vals::Nodiv::MASTERCLOCK | 907 | vals::Nodiv::MASTER_CLOCK |
| 905 | }, | 908 | }, |
| 906 | ); | 909 | ); |
| 907 | w.set_dmaen(true); | 910 | w.set_dmaen(true); |
| @@ -928,7 +931,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 928 | w.set_nbslot(config.slot_count.0 as u8 - 1); | 931 | w.set_nbslot(config.slot_count.0 as u8 - 1); |
| 929 | w.set_slotsz(config.slot_size.slotsz()); | 932 | w.set_slotsz(config.slot_size.slotsz()); |
| 930 | w.set_fboff(config.first_bit_offset.0 as u8); | 933 | w.set_fboff(config.first_bit_offset.0 as u8); |
| 931 | w.set_sloten(vals::Sloten(config.slot_enable as u16)); | 934 | w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16)); |
| 932 | }); | 935 | }); |
| 933 | 936 | ||
| 934 | ch.cr1().modify(|w| w.set_saien(true)); | 937 | ch.cr1().modify(|w| w.set_saien(true)); |
| @@ -939,7 +942,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 939 | } | 942 | } |
| 940 | 943 | ||
| 941 | Self { | 944 | Self { |
| 942 | _peri: peri.into_ref(), | 945 | _peri: peri, |
| 943 | sub_block, | 946 | sub_block, |
| 944 | sck, | 947 | sck, |
| 945 | mclk, | 948 | mclk, |
| @@ -950,13 +953,14 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 950 | } | 953 | } |
| 951 | 954 | ||
| 952 | /// Start the SAI driver. | 955 | /// Start the SAI driver. |
| 953 | pub fn start(&mut self) { | 956 | /// |
| 957 | /// Only receivers can be started. Transmitters are started on the first writing operation. | ||
| 958 | pub fn start(&mut self) -> Result<(), Error> { | ||
| 954 | match self.ring_buffer { | 959 | match self.ring_buffer { |
| 955 | RingBuffer::Writable(ref mut rb) => { | 960 | RingBuffer::Writable(_) => Err(Error::NotAReceiver), |
| 956 | rb.start(); | ||
| 957 | } | ||
| 958 | RingBuffer::Readable(ref mut rb) => { | 961 | RingBuffer::Readable(ref mut rb) => { |
| 959 | rb.start(); | 962 | rb.start(); |
| 963 | Ok(()) | ||
| 960 | } | 964 | } |
| 961 | } | 965 | } |
| 962 | } | 966 | } |
| @@ -973,22 +977,48 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 973 | rcc::enable_and_reset::<T>(); | 977 | rcc::enable_and_reset::<T>(); |
| 974 | } | 978 | } |
| 975 | 979 | ||
| 976 | /// Flush. | ||
| 977 | pub fn flush(&mut self) { | ||
| 978 | let ch = T::REGS.ch(self.sub_block as usize); | ||
| 979 | ch.cr1().modify(|w| w.set_saien(false)); | ||
| 980 | ch.cr2().modify(|w| w.set_fflush(true)); | ||
| 981 | ch.cr1().modify(|w| w.set_saien(true)); | ||
| 982 | } | ||
| 983 | |||
| 984 | /// Enable or disable mute. | 980 | /// Enable or disable mute. |
| 985 | pub fn set_mute(&mut self, value: bool) { | 981 | pub fn set_mute(&mut self, value: bool) { |
| 986 | let ch = T::REGS.ch(self.sub_block as usize); | 982 | let ch = T::REGS.ch(self.sub_block as usize); |
| 987 | ch.cr2().modify(|w| w.set_mute(value)); | 983 | ch.cr2().modify(|w| w.set_mute(value)); |
| 988 | } | 984 | } |
| 989 | 985 | ||
| 986 | /// Determine the mute state of the receiver. | ||
| 987 | /// | ||
| 988 | /// Clears the mute state flag in the status register. | ||
| 989 | pub fn is_muted(&self) -> Result<bool, Error> { | ||
| 990 | match &self.ring_buffer { | ||
| 991 | RingBuffer::Readable(_) => { | ||
| 992 | let ch = T::REGS.ch(self.sub_block as usize); | ||
| 993 | let mute_state = ch.sr().read().mutedet(); | ||
| 994 | ch.clrfr().write(|w| w.set_cmutedet(true)); | ||
| 995 | Ok(mute_state) | ||
| 996 | } | ||
| 997 | _ => Err(Error::NotAReceiver), | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | |||
| 1001 | /// Wait until any SAI write error occurs. | ||
| 1002 | /// | ||
| 1003 | /// One useful application for this is stopping playback as soon as the SAI | ||
| 1004 | /// experiences an overrun of the ring buffer. Then, instead of letting | ||
| 1005 | /// the SAI peripheral play the last written buffer over and over again, SAI | ||
| 1006 | /// can be muted or dropped instead. | ||
| 1007 | pub async fn wait_write_error(&mut self) -> Result<(), Error> { | ||
| 1008 | match &mut self.ring_buffer { | ||
| 1009 | RingBuffer::Writable(buffer) => { | ||
| 1010 | buffer.wait_write_error().await?; | ||
| 1011 | Ok(()) | ||
| 1012 | } | ||
| 1013 | _ => return Err(Error::NotATransmitter), | ||
| 1014 | } | ||
| 1015 | } | ||
| 1016 | |||
| 990 | /// Write data to the SAI ringbuffer. | 1017 | /// Write data to the SAI ringbuffer. |
| 991 | /// | 1018 | /// |
| 1019 | /// The first write starts the DMA after filling the ring buffer with the provided data. | ||
| 1020 | /// This ensures that the DMA does not run before data is available in the ring buffer. | ||
| 1021 | /// | ||
| 992 | /// This appends the data to the buffer and returns immediately. The | 1022 | /// This appends the data to the buffer and returns immediately. The |
| 993 | /// data will be transmitted in the background. | 1023 | /// data will be transmitted in the background. |
| 994 | /// | 1024 | /// |
| @@ -996,7 +1026,12 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { | |||
| 996 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { | 1026 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { |
| 997 | match &mut self.ring_buffer { | 1027 | match &mut self.ring_buffer { |
| 998 | RingBuffer::Writable(buffer) => { | 1028 | RingBuffer::Writable(buffer) => { |
| 999 | buffer.write_exact(data).await?; | 1029 | if buffer.is_running() { |
| 1030 | buffer.write_exact(data).await?; | ||
| 1031 | } else { | ||
| 1032 | buffer.write_immediate(data)?; | ||
| 1033 | buffer.start(); | ||
| 1034 | } | ||
| 1000 | Ok(()) | 1035 | Ok(()) |
| 1001 | } | 1036 | } |
| 1002 | _ => return Err(Error::NotATransmitter), | 1037 | _ => return Err(Error::NotATransmitter), |
| @@ -1024,6 +1059,7 @@ impl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> { | |||
| 1024 | fn drop(&mut self) { | 1059 | fn drop(&mut self) { |
| 1025 | let ch = T::REGS.ch(self.sub_block as usize); | 1060 | let ch = T::REGS.ch(self.sub_block as usize); |
| 1026 | ch.cr1().modify(|w| w.set_saien(false)); | 1061 | ch.cr1().modify(|w| w.set_saien(false)); |
| 1062 | ch.cr2().modify(|w| w.set_fflush(true)); | ||
| 1027 | self.fs.as_ref().map(|x| x.set_as_disconnected()); | 1063 | self.fs.as_ref().map(|x| x.set_as_disconnected()); |
| 1028 | self.sd.as_ref().map(|x| x.set_as_disconnected()); | 1064 | self.sd.as_ref().map(|x| x.set_as_disconnected()); |
| 1029 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | 1065 | self.sck.as_ref().map(|x| x.set_as_disconnected()); |
| @@ -1065,7 +1101,7 @@ impl SubBlockInstance for B {} | |||
| 1065 | 1101 | ||
| 1066 | /// SAI instance trait. | 1102 | /// SAI instance trait. |
| 1067 | #[allow(private_bounds)] | 1103 | #[allow(private_bounds)] |
| 1068 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} | 1104 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {} |
| 1069 | 1105 | ||
| 1070 | pin_trait!(SckPin, Instance, SubBlockInstance); | 1106 | pin_trait!(SckPin, Instance, SubBlockInstance); |
| 1071 | pin_trait!(FsPin, Instance, SubBlockInstance); | 1107 | pin_trait!(FsPin, Instance, SubBlockInstance); |
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 44ff9fcd5..6a02aae70 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs | |||
| @@ -8,11 +8,15 @@ use core::ops::{Deref, DerefMut}; | |||
| 8 | use core::task::Poll; | 8 | use core::task::Poll; |
| 9 | 9 | ||
| 10 | use embassy_hal_internal::drop::OnDrop; | 10 | use embassy_hal_internal::drop::OnDrop; |
| 11 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 11 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 12 | use embassy_sync::waitqueue::AtomicWaker; | 12 | use embassy_sync::waitqueue::AtomicWaker; |
| 13 | use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; | 13 | use sdio_host::common_cmd::{self, Resp, ResponseLen}; |
| 14 | use sdio_host::emmc::{ExtCSD, EMMC}; | ||
| 15 | use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; | ||
| 16 | use sdio_host::{emmc_cmd, sd_cmd, Cmd}; | ||
| 14 | 17 | ||
| 15 | use crate::dma::NoDma; | 18 | #[cfg(sdmmc_v1)] |
| 19 | use crate::dma::ChannelAndRequest; | ||
| 16 | #[cfg(gpio_v2)] | 20 | #[cfg(gpio_v2)] |
| 17 | use crate::gpio::Pull; | 21 | use crate::gpio::Pull; |
| 18 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; | 22 | use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; |
| @@ -20,7 +24,7 @@ use crate::interrupt::typelevel::Interrupt; | |||
| 20 | use crate::pac::sdmmc::Sdmmc as RegBlock; | 24 | use crate::pac::sdmmc::Sdmmc as RegBlock; |
| 21 | use crate::rcc::{self, RccPeripheral}; | 25 | use crate::rcc::{self, RccPeripheral}; |
| 22 | use crate::time::Hertz; | 26 | use crate::time::Hertz; |
| 23 | use crate::{interrupt, peripherals, Peripheral}; | 27 | use crate::{interrupt, peripherals}; |
| 24 | 28 | ||
| 25 | /// Interrupt handler. | 29 | /// Interrupt handler. |
| 26 | pub struct InterruptHandler<T: Instance> { | 30 | pub struct InterruptHandler<T: Instance> { |
| @@ -135,51 +139,59 @@ pub enum Error { | |||
| 135 | UnsupportedCardVersion, | 139 | UnsupportedCardVersion, |
| 136 | /// Unsupported card type. | 140 | /// Unsupported card type. |
| 137 | UnsupportedCardType, | 141 | UnsupportedCardType, |
| 142 | /// Unsupported voltage. | ||
| 143 | UnsupportedVoltage, | ||
| 138 | /// CRC error. | 144 | /// CRC error. |
| 139 | Crc, | 145 | Crc, |
| 140 | /// No card inserted. | 146 | /// No card inserted. |
| 141 | NoCard, | 147 | NoCard, |
| 148 | /// 8-lane buses are not supported for SD cards. | ||
| 149 | BusWidth, | ||
| 142 | /// Bad clock supplied to the SDMMC peripheral. | 150 | /// Bad clock supplied to the SDMMC peripheral. |
| 143 | BadClock, | 151 | BadClock, |
| 144 | /// Signaling switch failed. | 152 | /// Signaling switch failed. |
| 145 | SignalingSwitchFailed, | 153 | SignalingSwitchFailed, |
| 154 | /// Underrun error | ||
| 155 | Underrun, | ||
| 146 | /// ST bit error. | 156 | /// ST bit error. |
| 147 | #[cfg(sdmmc_v1)] | 157 | #[cfg(sdmmc_v1)] |
| 148 | StBitErr, | 158 | StBitErr, |
| 149 | } | 159 | } |
| 150 | 160 | ||
| 151 | /// A SD command | ||
| 152 | struct Cmd { | ||
| 153 | cmd: u8, | ||
| 154 | arg: u32, | ||
| 155 | resp: Response, | ||
| 156 | } | ||
| 157 | |||
| 158 | #[derive(Clone, Copy, Debug, Default)] | 161 | #[derive(Clone, Copy, Debug, Default)] |
| 159 | /// SD Card | 162 | /// SD Card |
| 160 | pub struct Card { | 163 | pub struct Card { |
| 161 | /// The type of this card | 164 | /// The type of this card |
| 162 | pub card_type: CardCapacity, | 165 | pub card_type: CardCapacity, |
| 163 | /// Operation Conditions Register | 166 | /// Operation Conditions Register |
| 164 | pub ocr: OCR, | 167 | pub ocr: OCR<SD>, |
| 165 | /// Relative Card Address | 168 | /// Relative Card Address |
| 166 | pub rca: u32, | 169 | pub rca: u16, |
| 167 | /// Card ID | 170 | /// Card ID |
| 168 | pub cid: CID, | 171 | pub cid: CID<SD>, |
| 169 | /// Card Specific Data | 172 | /// Card Specific Data |
| 170 | pub csd: CSD, | 173 | pub csd: CSD<SD>, |
| 171 | /// SD CARD Configuration Register | 174 | /// SD CARD Configuration Register |
| 172 | pub scr: SCR, | 175 | pub scr: SCR, |
| 173 | /// SD Status | 176 | /// SD Status |
| 174 | pub status: SDStatus, | 177 | pub status: SDStatus, |
| 175 | } | 178 | } |
| 176 | 179 | ||
| 177 | impl Card { | 180 | #[derive(Clone, Copy, Debug, Default)] |
| 178 | /// Size in bytes | 181 | /// eMMC storage |
| 179 | pub fn size(&self) -> u64 { | 182 | pub struct Emmc { |
| 180 | // SDHC / SDXC / SDUC | 183 | /// The capacity of this card |
| 181 | u64::from(self.csd.block_count()) * 512 | 184 | pub capacity: CardCapacity, |
| 182 | } | 185 | /// Operation Conditions Register |
| 186 | pub ocr: OCR<EMMC>, | ||
| 187 | /// Relative Card Address | ||
| 188 | pub rca: u16, | ||
| 189 | /// Card ID | ||
| 190 | pub cid: CID<EMMC>, | ||
| 191 | /// Card Specific Data | ||
| 192 | pub csd: CSD<EMMC>, | ||
| 193 | /// Extended Card Specific Data | ||
| 194 | pub ext_csd: ExtCSD, | ||
| 183 | } | 195 | } |
| 184 | 196 | ||
| 185 | #[repr(u8)] | 197 | #[repr(u8)] |
| @@ -188,22 +200,12 @@ enum PowerCtrl { | |||
| 188 | On = 0b11, | 200 | On = 0b11, |
| 189 | } | 201 | } |
| 190 | 202 | ||
| 191 | #[repr(u32)] | 203 | fn get_waitresp_val(rlen: ResponseLen) -> u8 { |
| 192 | #[allow(dead_code)] | 204 | match rlen { |
| 193 | #[allow(non_camel_case_types)] | 205 | common_cmd::ResponseLen::Zero => 0, |
| 194 | enum CmdAppOper { | 206 | common_cmd::ResponseLen::R48 => 1, |
| 195 | VOLTAGE_WINDOW_SD = 0x8010_0000, | 207 | common_cmd::ResponseLen::R136 => 3, |
| 196 | HIGH_CAPACITY = 0x4000_0000, | 208 | } |
| 197 | SDMMC_STD_CAPACITY = 0x0000_0000, | ||
| 198 | SDMMC_CHECK_PATTERN = 0x0000_01AA, | ||
| 199 | SD_SWITCH_1_8V_CAPACITY = 0x0100_0000, | ||
| 200 | } | ||
| 201 | |||
| 202 | #[derive(Eq, PartialEq, Copy, Clone)] | ||
| 203 | enum Response { | ||
| 204 | None = 0, | ||
| 205 | Short = 1, | ||
| 206 | Long = 3, | ||
| 207 | } | 209 | } |
| 208 | 210 | ||
| 209 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to | 211 | /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to |
| @@ -300,18 +302,77 @@ impl Default for Config { | |||
| 300 | } | 302 | } |
| 301 | } | 303 | } |
| 302 | 304 | ||
| 305 | /// Peripheral that can be operated over SDMMC | ||
| 306 | #[derive(Clone, Copy, Debug)] | ||
| 307 | pub enum SdmmcPeripheral { | ||
| 308 | /// SD Card | ||
| 309 | SdCard(Card), | ||
| 310 | /// eMMC memory | ||
| 311 | Emmc(Emmc), | ||
| 312 | } | ||
| 313 | |||
| 314 | impl SdmmcPeripheral { | ||
| 315 | /// Get this peripheral's address on the SDMMC bus | ||
| 316 | fn get_address(&self) -> u16 { | ||
| 317 | match self { | ||
| 318 | Self::SdCard(c) => c.rca, | ||
| 319 | Self::Emmc(e) => e.rca, | ||
| 320 | } | ||
| 321 | } | ||
| 322 | /// Is this a standard or high capacity peripheral? | ||
| 323 | fn get_capacity(&self) -> CardCapacity { | ||
| 324 | match self { | ||
| 325 | Self::SdCard(c) => c.card_type, | ||
| 326 | Self::Emmc(e) => e.capacity, | ||
| 327 | } | ||
| 328 | } | ||
| 329 | /// Size in bytes | ||
| 330 | fn size(&self) -> u64 { | ||
| 331 | match self { | ||
| 332 | // SDHC / SDXC / SDUC | ||
| 333 | Self::SdCard(c) => u64::from(c.csd.block_count()) * 512, | ||
| 334 | // capacity > 2GB | ||
| 335 | Self::Emmc(e) => u64::from(e.ext_csd.sector_count()) * 512, | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Get a mutable reference to the SD Card. | ||
| 340 | /// | ||
| 341 | /// Panics if there is another peripheral instead. | ||
| 342 | fn get_sd_card(&mut self) -> &mut Card { | ||
| 343 | match *self { | ||
| 344 | Self::SdCard(ref mut c) => c, | ||
| 345 | _ => unreachable!("SD only"), | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | /// Get a mutable reference to the eMMC. | ||
| 350 | /// | ||
| 351 | /// Panics if there is another peripheral instead. | ||
| 352 | fn get_emmc(&mut self) -> &mut Emmc { | ||
| 353 | match *self { | ||
| 354 | Self::Emmc(ref mut e) => e, | ||
| 355 | _ => unreachable!("eMMC only"), | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 303 | /// Sdmmc device | 360 | /// Sdmmc device |
| 304 | pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma<T> = NoDma> { | 361 | pub struct Sdmmc<'d, T: Instance> { |
| 305 | _peri: PeripheralRef<'d, T>, | 362 | _peri: Peri<'d, T>, |
| 306 | #[allow(unused)] | 363 | #[cfg(sdmmc_v1)] |
| 307 | dma: PeripheralRef<'d, Dma>, | 364 | dma: ChannelAndRequest<'d>, |
| 308 | 365 | ||
| 309 | clk: PeripheralRef<'d, AnyPin>, | 366 | clk: Peri<'d, AnyPin>, |
| 310 | cmd: PeripheralRef<'d, AnyPin>, | 367 | cmd: Peri<'d, AnyPin>, |
| 311 | d0: PeripheralRef<'d, AnyPin>, | 368 | d0: Peri<'d, AnyPin>, |
| 312 | d1: Option<PeripheralRef<'d, AnyPin>>, | 369 | d1: Option<Peri<'d, AnyPin>>, |
| 313 | d2: Option<PeripheralRef<'d, AnyPin>>, | 370 | d2: Option<Peri<'d, AnyPin>>, |
| 314 | d3: Option<PeripheralRef<'d, AnyPin>>, | 371 | d3: Option<Peri<'d, AnyPin>>, |
| 372 | d4: Option<Peri<'d, AnyPin>>, | ||
| 373 | d5: Option<Peri<'d, AnyPin>>, | ||
| 374 | d6: Option<Peri<'d, AnyPin>>, | ||
| 375 | d7: Option<Peri<'d, AnyPin>>, | ||
| 315 | 376 | ||
| 316 | config: Config, | 377 | config: Config, |
| 317 | /// Current clock to card | 378 | /// Current clock to card |
| @@ -319,7 +380,7 @@ pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma<T> = NoDma> { | |||
| 319 | /// Current signalling scheme to card | 380 | /// Current signalling scheme to card |
| 320 | signalling: Signalling, | 381 | signalling: Signalling, |
| 321 | /// Card | 382 | /// Card |
| 322 | card: Option<Card>, | 383 | card: Option<SdmmcPeripheral>, |
| 323 | 384 | ||
| 324 | /// An optional buffer to be used for commands | 385 | /// An optional buffer to be used for commands |
| 325 | /// This should be used if there are special memory location requirements for dma | 386 | /// This should be used if there are special memory location requirements for dma |
| @@ -334,19 +395,17 @@ const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh | |||
| 334 | const DATA_AF: AfType = CMD_AF; | 395 | const DATA_AF: AfType = CMD_AF; |
| 335 | 396 | ||
| 336 | #[cfg(sdmmc_v1)] | 397 | #[cfg(sdmmc_v1)] |
| 337 | impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | 398 | impl<'d, T: Instance> Sdmmc<'d, T> { |
| 338 | /// Create a new SDMMC driver, with 1 data lane. | 399 | /// Create a new SDMMC driver, with 1 data lane. |
| 339 | pub fn new_1bit( | 400 | pub fn new_1bit( |
| 340 | sdmmc: impl Peripheral<P = T> + 'd, | 401 | sdmmc: Peri<'d, T>, |
| 341 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 402 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 342 | dma: impl Peripheral<P = Dma> + 'd, | 403 | dma: Peri<'d, impl SdmmcDma<T>>, |
| 343 | clk: impl Peripheral<P = impl CkPin<T>> + 'd, | 404 | clk: Peri<'d, impl CkPin<T>>, |
| 344 | cmd: impl Peripheral<P = impl CmdPin<T>> + 'd, | 405 | cmd: Peri<'d, impl CmdPin<T>>, |
| 345 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 406 | d0: Peri<'d, impl D0Pin<T>>, |
| 346 | config: Config, | 407 | config: Config, |
| 347 | ) -> Self { | 408 | ) -> Self { |
| 348 | into_ref!(clk, cmd, d0); | ||
| 349 | |||
| 350 | critical_section::with(|_| { | 409 | critical_section::with(|_| { |
| 351 | clk.set_as_af(clk.af_num(), CLK_AF); | 410 | clk.set_as_af(clk.af_num(), CLK_AF); |
| 352 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 411 | cmd.set_as_af(cmd.af_num(), CMD_AF); |
| @@ -355,10 +414,14 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | |||
| 355 | 414 | ||
| 356 | Self::new_inner( | 415 | Self::new_inner( |
| 357 | sdmmc, | 416 | sdmmc, |
| 358 | dma, | 417 | new_dma_nonopt!(dma), |
| 359 | clk.map_into(), | 418 | clk.into(), |
| 360 | cmd.map_into(), | 419 | cmd.into(), |
| 361 | d0.map_into(), | 420 | d0.into(), |
| 421 | None, | ||
| 422 | None, | ||
| 423 | None, | ||
| 424 | None, | ||
| 362 | None, | 425 | None, |
| 363 | None, | 426 | None, |
| 364 | None, | 427 | None, |
| @@ -368,19 +431,63 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | |||
| 368 | 431 | ||
| 369 | /// Create a new SDMMC driver, with 4 data lanes. | 432 | /// Create a new SDMMC driver, with 4 data lanes. |
| 370 | pub fn new_4bit( | 433 | pub fn new_4bit( |
| 371 | sdmmc: impl Peripheral<P = T> + 'd, | 434 | sdmmc: Peri<'d, T>, |
| 372 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 435 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 373 | dma: impl Peripheral<P = Dma> + 'd, | 436 | dma: Peri<'d, impl SdmmcDma<T>>, |
| 374 | clk: impl Peripheral<P = impl CkPin<T>> + 'd, | 437 | clk: Peri<'d, impl CkPin<T>>, |
| 375 | cmd: impl Peripheral<P = impl CmdPin<T>> + 'd, | 438 | cmd: Peri<'d, impl CmdPin<T>>, |
| 376 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 439 | d0: Peri<'d, impl D0Pin<T>>, |
| 377 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 440 | d1: Peri<'d, impl D1Pin<T>>, |
| 378 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 441 | d2: Peri<'d, impl D2Pin<T>>, |
| 379 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 442 | d3: Peri<'d, impl D3Pin<T>>, |
| 380 | config: Config, | 443 | config: Config, |
| 381 | ) -> Self { | 444 | ) -> Self { |
| 382 | into_ref!(clk, cmd, d0, d1, d2, d3); | 445 | critical_section::with(|_| { |
| 446 | clk.set_as_af(clk.af_num(), CLK_AF); | ||
| 447 | cmd.set_as_af(cmd.af_num(), CMD_AF); | ||
| 448 | d0.set_as_af(d0.af_num(), DATA_AF); | ||
| 449 | d1.set_as_af(d1.af_num(), DATA_AF); | ||
| 450 | d2.set_as_af(d2.af_num(), DATA_AF); | ||
| 451 | d3.set_as_af(d3.af_num(), DATA_AF); | ||
| 452 | }); | ||
| 453 | |||
| 454 | Self::new_inner( | ||
| 455 | sdmmc, | ||
| 456 | new_dma_nonopt!(dma), | ||
| 457 | clk.into(), | ||
| 458 | cmd.into(), | ||
| 459 | d0.into(), | ||
| 460 | Some(d1.into()), | ||
| 461 | Some(d2.into()), | ||
| 462 | Some(d3.into()), | ||
| 463 | None, | ||
| 464 | None, | ||
| 465 | None, | ||
| 466 | None, | ||
| 467 | config, | ||
| 468 | ) | ||
| 469 | } | ||
| 470 | } | ||
| 383 | 471 | ||
| 472 | #[cfg(sdmmc_v1)] | ||
| 473 | impl<'d, T: Instance> Sdmmc<'d, T> { | ||
| 474 | /// Create a new SDMMC driver, with 8 data lanes. | ||
| 475 | pub fn new_8bit( | ||
| 476 | sdmmc: Peri<'d, T>, | ||
| 477 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 478 | dma: Peri<'d, impl SdmmcDma<T>>, | ||
| 479 | clk: Peri<'d, impl CkPin<T>>, | ||
| 480 | cmd: Peri<'d, impl CmdPin<T>>, | ||
| 481 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 482 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 483 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 484 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 485 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 486 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 487 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 488 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 489 | config: Config, | ||
| 490 | ) -> Self { | ||
| 384 | critical_section::with(|_| { | 491 | critical_section::with(|_| { |
| 385 | clk.set_as_af(clk.af_num(), CLK_AF); | 492 | clk.set_as_af(clk.af_num(), CLK_AF); |
| 386 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 493 | cmd.set_as_af(cmd.af_num(), CMD_AF); |
| @@ -388,35 +495,41 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> { | |||
| 388 | d1.set_as_af(d1.af_num(), DATA_AF); | 495 | d1.set_as_af(d1.af_num(), DATA_AF); |
| 389 | d2.set_as_af(d2.af_num(), DATA_AF); | 496 | d2.set_as_af(d2.af_num(), DATA_AF); |
| 390 | d3.set_as_af(d3.af_num(), DATA_AF); | 497 | d3.set_as_af(d3.af_num(), DATA_AF); |
| 498 | d4.set_as_af(d4.af_num(), DATA_AF); | ||
| 499 | d5.set_as_af(d5.af_num(), DATA_AF); | ||
| 500 | d6.set_as_af(d6.af_num(), DATA_AF); | ||
| 501 | d7.set_as_af(d7.af_num(), DATA_AF); | ||
| 391 | }); | 502 | }); |
| 392 | 503 | ||
| 393 | Self::new_inner( | 504 | Self::new_inner( |
| 394 | sdmmc, | 505 | sdmmc, |
| 395 | dma, | 506 | new_dma_nonopt!(dma), |
| 396 | clk.map_into(), | 507 | clk.into(), |
| 397 | cmd.map_into(), | 508 | cmd.into(), |
| 398 | d0.map_into(), | 509 | d0.into(), |
| 399 | Some(d1.map_into()), | 510 | Some(d1.into()), |
| 400 | Some(d2.map_into()), | 511 | Some(d2.into()), |
| 401 | Some(d3.map_into()), | 512 | Some(d3.into()), |
| 513 | Some(d4.into()), | ||
| 514 | Some(d5.into()), | ||
| 515 | Some(d6.into()), | ||
| 516 | Some(d7.into()), | ||
| 402 | config, | 517 | config, |
| 403 | ) | 518 | ) |
| 404 | } | 519 | } |
| 405 | } | 520 | } |
| 406 | 521 | ||
| 407 | #[cfg(sdmmc_v2)] | 522 | #[cfg(sdmmc_v2)] |
| 408 | impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | 523 | impl<'d, T: Instance> Sdmmc<'d, T> { |
| 409 | /// Create a new SDMMC driver, with 1 data lane. | 524 | /// Create a new SDMMC driver, with 1 data lane. |
| 410 | pub fn new_1bit( | 525 | pub fn new_1bit( |
| 411 | sdmmc: impl Peripheral<P = T> + 'd, | 526 | sdmmc: Peri<'d, T>, |
| 412 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 527 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 413 | clk: impl Peripheral<P = impl CkPin<T>> + 'd, | 528 | clk: Peri<'d, impl CkPin<T>>, |
| 414 | cmd: impl Peripheral<P = impl CmdPin<T>> + 'd, | 529 | cmd: Peri<'d, impl CmdPin<T>>, |
| 415 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 530 | d0: Peri<'d, impl D0Pin<T>>, |
| 416 | config: Config, | 531 | config: Config, |
| 417 | ) -> Self { | 532 | ) -> Self { |
| 418 | into_ref!(clk, cmd, d0); | ||
| 419 | |||
| 420 | critical_section::with(|_| { | 533 | critical_section::with(|_| { |
| 421 | clk.set_as_af(clk.af_num(), CLK_AF); | 534 | clk.set_as_af(clk.af_num(), CLK_AF); |
| 422 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 535 | cmd.set_as_af(cmd.af_num(), CMD_AF); |
| @@ -425,10 +538,13 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 425 | 538 | ||
| 426 | Self::new_inner( | 539 | Self::new_inner( |
| 427 | sdmmc, | 540 | sdmmc, |
| 428 | NoDma.into_ref(), | 541 | clk.into(), |
| 429 | clk.map_into(), | 542 | cmd.into(), |
| 430 | cmd.map_into(), | 543 | d0.into(), |
| 431 | d0.map_into(), | 544 | None, |
| 545 | None, | ||
| 546 | None, | ||
| 547 | None, | ||
| 432 | None, | 548 | None, |
| 433 | None, | 549 | None, |
| 434 | None, | 550 | None, |
| @@ -438,18 +554,60 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 438 | 554 | ||
| 439 | /// Create a new SDMMC driver, with 4 data lanes. | 555 | /// Create a new SDMMC driver, with 4 data lanes. |
| 440 | pub fn new_4bit( | 556 | pub fn new_4bit( |
| 441 | sdmmc: impl Peripheral<P = T> + 'd, | 557 | sdmmc: Peri<'d, T>, |
| 442 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 558 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 443 | clk: impl Peripheral<P = impl CkPin<T>> + 'd, | 559 | clk: Peri<'d, impl CkPin<T>>, |
| 444 | cmd: impl Peripheral<P = impl CmdPin<T>> + 'd, | 560 | cmd: Peri<'d, impl CmdPin<T>>, |
| 445 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, | 561 | d0: Peri<'d, impl D0Pin<T>>, |
| 446 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, | 562 | d1: Peri<'d, impl D1Pin<T>>, |
| 447 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, | 563 | d2: Peri<'d, impl D2Pin<T>>, |
| 448 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, | 564 | d3: Peri<'d, impl D3Pin<T>>, |
| 449 | config: Config, | 565 | config: Config, |
| 450 | ) -> Self { | 566 | ) -> Self { |
| 451 | into_ref!(clk, cmd, d0, d1, d2, d3); | 567 | critical_section::with(|_| { |
| 568 | clk.set_as_af(clk.af_num(), CLK_AF); | ||
| 569 | cmd.set_as_af(cmd.af_num(), CMD_AF); | ||
| 570 | d0.set_as_af(d0.af_num(), DATA_AF); | ||
| 571 | d1.set_as_af(d1.af_num(), DATA_AF); | ||
| 572 | d2.set_as_af(d2.af_num(), DATA_AF); | ||
| 573 | d3.set_as_af(d3.af_num(), DATA_AF); | ||
| 574 | }); | ||
| 452 | 575 | ||
| 576 | Self::new_inner( | ||
| 577 | sdmmc, | ||
| 578 | clk.into(), | ||
| 579 | cmd.into(), | ||
| 580 | d0.into(), | ||
| 581 | Some(d1.into()), | ||
| 582 | Some(d2.into()), | ||
| 583 | Some(d3.into()), | ||
| 584 | None, | ||
| 585 | None, | ||
| 586 | None, | ||
| 587 | None, | ||
| 588 | config, | ||
| 589 | ) | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | #[cfg(sdmmc_v2)] | ||
| 594 | impl<'d, T: Instance> Sdmmc<'d, T> { | ||
| 595 | /// Create a new SDMMC driver, with 8 data lanes. | ||
| 596 | pub fn new_8bit( | ||
| 597 | sdmmc: Peri<'d, T>, | ||
| 598 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 599 | clk: Peri<'d, impl CkPin<T>>, | ||
| 600 | cmd: Peri<'d, impl CmdPin<T>>, | ||
| 601 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 602 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 603 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 604 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 605 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 606 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 607 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 608 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 609 | config: Config, | ||
| 610 | ) -> Self { | ||
| 453 | critical_section::with(|_| { | 611 | critical_section::with(|_| { |
| 454 | clk.set_as_af(clk.af_num(), CLK_AF); | 612 | clk.set_as_af(clk.af_num(), CLK_AF); |
| 455 | cmd.set_as_af(cmd.af_num(), CMD_AF); | 613 | cmd.set_as_af(cmd.af_num(), CMD_AF); |
| @@ -457,36 +615,45 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { | |||
| 457 | d1.set_as_af(d1.af_num(), DATA_AF); | 615 | d1.set_as_af(d1.af_num(), DATA_AF); |
| 458 | d2.set_as_af(d2.af_num(), DATA_AF); | 616 | d2.set_as_af(d2.af_num(), DATA_AF); |
| 459 | d3.set_as_af(d3.af_num(), DATA_AF); | 617 | d3.set_as_af(d3.af_num(), DATA_AF); |
| 618 | d4.set_as_af(d4.af_num(), DATA_AF); | ||
| 619 | d5.set_as_af(d5.af_num(), DATA_AF); | ||
| 620 | d6.set_as_af(d6.af_num(), DATA_AF); | ||
| 621 | d7.set_as_af(d7.af_num(), DATA_AF); | ||
| 460 | }); | 622 | }); |
| 461 | 623 | ||
| 462 | Self::new_inner( | 624 | Self::new_inner( |
| 463 | sdmmc, | 625 | sdmmc, |
| 464 | NoDma.into_ref(), | 626 | clk.into(), |
| 465 | clk.map_into(), | 627 | cmd.into(), |
| 466 | cmd.map_into(), | 628 | d0.into(), |
| 467 | d0.map_into(), | 629 | Some(d1.into()), |
| 468 | Some(d1.map_into()), | 630 | Some(d2.into()), |
| 469 | Some(d2.map_into()), | 631 | Some(d3.into()), |
| 470 | Some(d3.map_into()), | 632 | Some(d4.into()), |
| 633 | Some(d5.into()), | ||
| 634 | Some(d6.into()), | ||
| 635 | Some(d7.into()), | ||
| 471 | config, | 636 | config, |
| 472 | ) | 637 | ) |
| 473 | } | 638 | } |
| 474 | } | 639 | } |
| 475 | 640 | ||
| 476 | impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | 641 | impl<'d, T: Instance> Sdmmc<'d, T> { |
| 477 | fn new_inner( | 642 | fn new_inner( |
| 478 | sdmmc: impl Peripheral<P = T> + 'd, | 643 | sdmmc: Peri<'d, T>, |
| 479 | dma: impl Peripheral<P = Dma> + 'd, | 644 | #[cfg(sdmmc_v1)] dma: ChannelAndRequest<'d>, |
| 480 | clk: PeripheralRef<'d, AnyPin>, | 645 | clk: Peri<'d, AnyPin>, |
| 481 | cmd: PeripheralRef<'d, AnyPin>, | 646 | cmd: Peri<'d, AnyPin>, |
| 482 | d0: PeripheralRef<'d, AnyPin>, | 647 | d0: Peri<'d, AnyPin>, |
| 483 | d1: Option<PeripheralRef<'d, AnyPin>>, | 648 | d1: Option<Peri<'d, AnyPin>>, |
| 484 | d2: Option<PeripheralRef<'d, AnyPin>>, | 649 | d2: Option<Peri<'d, AnyPin>>, |
| 485 | d3: Option<PeripheralRef<'d, AnyPin>>, | 650 | d3: Option<Peri<'d, AnyPin>>, |
| 651 | d4: Option<Peri<'d, AnyPin>>, | ||
| 652 | d5: Option<Peri<'d, AnyPin>>, | ||
| 653 | d6: Option<Peri<'d, AnyPin>>, | ||
| 654 | d7: Option<Peri<'d, AnyPin>>, | ||
| 486 | config: Config, | 655 | config: Config, |
| 487 | ) -> Self { | 656 | ) -> Self { |
| 488 | into_ref!(sdmmc, dma); | ||
| 489 | |||
| 490 | rcc::enable_and_reset::<T>(); | 657 | rcc::enable_and_reset::<T>(); |
| 491 | 658 | ||
| 492 | T::Interrupt::unpend(); | 659 | T::Interrupt::unpend(); |
| @@ -514,6 +681,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 514 | 681 | ||
| 515 | Self { | 682 | Self { |
| 516 | _peri: sdmmc, | 683 | _peri: sdmmc, |
| 684 | #[cfg(sdmmc_v1)] | ||
| 517 | dma, | 685 | dma, |
| 518 | 686 | ||
| 519 | clk, | 687 | clk, |
| @@ -522,6 +690,10 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 522 | d1, | 690 | d1, |
| 523 | d2, | 691 | d2, |
| 524 | d3, | 692 | d3, |
| 693 | d4, | ||
| 694 | d5, | ||
| 695 | d6, | ||
| 696 | d7, | ||
| 525 | 697 | ||
| 526 | config, | 698 | config, |
| 527 | clock: SD_INIT_FREQ, | 699 | clock: SD_INIT_FREQ, |
| @@ -567,7 +739,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 567 | #[allow(unused_variables)] | 739 | #[allow(unused_variables)] |
| 568 | fn prepare_datapath_read<'a>( | 740 | fn prepare_datapath_read<'a>( |
| 569 | config: &Config, | 741 | config: &Config, |
| 570 | dma: &'a mut PeripheralRef<'d, Dma>, | 742 | #[cfg(sdmmc_v1)] dma: &'a mut ChannelAndRequest<'d>, |
| 571 | buffer: &'a mut [u32], | 743 | buffer: &'a mut [u32], |
| 572 | length_bytes: u32, | 744 | length_bytes: u32, |
| 573 | block_size: u8, | 745 | block_size: u8, |
| @@ -583,16 +755,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 583 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); | 755 | regs.dlenr().write(|w| w.set_datalength(length_bytes)); |
| 584 | 756 | ||
| 585 | #[cfg(sdmmc_v1)] | 757 | #[cfg(sdmmc_v1)] |
| 586 | let transfer = unsafe { | 758 | let transfer = unsafe { dma.read(regs.fifor().as_ptr() as *mut u32, buffer, DMA_TRANSFER_OPTIONS) }; |
| 587 | let request = dma.request(); | ||
| 588 | Transfer::new_read( | ||
| 589 | dma, | ||
| 590 | request, | ||
| 591 | regs.fifor().as_ptr() as *mut u32, | ||
| 592 | buffer, | ||
| 593 | DMA_TRANSFER_OPTIONS, | ||
| 594 | ) | ||
| 595 | }; | ||
| 596 | #[cfg(sdmmc_v2)] | 759 | #[cfg(sdmmc_v2)] |
| 597 | let transfer = { | 760 | let transfer = { |
| 598 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); | 761 | regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); |
| @@ -632,14 +795,8 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 632 | 795 | ||
| 633 | #[cfg(sdmmc_v1)] | 796 | #[cfg(sdmmc_v1)] |
| 634 | let transfer = unsafe { | 797 | let transfer = unsafe { |
| 635 | let request = self.dma.request(); | 798 | self.dma |
| 636 | Transfer::new_write( | 799 | .write(buffer, regs.fifor().as_ptr() as *mut u32, DMA_TRANSFER_OPTIONS) |
| 637 | &mut self.dma, | ||
| 638 | request, | ||
| 639 | buffer, | ||
| 640 | regs.fifor().as_ptr() as *mut u32, | ||
| 641 | DMA_TRANSFER_OPTIONS, | ||
| 642 | ) | ||
| 643 | }; | 800 | }; |
| 644 | #[cfg(sdmmc_v2)] | 801 | #[cfg(sdmmc_v2)] |
| 645 | let transfer = { | 802 | let transfer = { |
| @@ -707,168 +864,29 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 707 | Ok(()) | 864 | Ok(()) |
| 708 | } | 865 | } |
| 709 | 866 | ||
| 710 | /// Switch mode using CMD6. | ||
| 711 | /// | ||
| 712 | /// Attempt to set a new signalling mode. The selected | ||
| 713 | /// signalling mode is returned. Expects the current clock | ||
| 714 | /// frequency to be > 12.5MHz. | ||
| 715 | async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> { | ||
| 716 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 717 | // necessary" | ||
| 718 | |||
| 719 | let set_function = 0x8000_0000 | ||
| 720 | | match signalling { | ||
| 721 | // See PLSS v7_10 Table 4-11 | ||
| 722 | Signalling::DDR50 => 0xFF_FF04, | ||
| 723 | Signalling::SDR104 => 0xFF_1F03, | ||
| 724 | Signalling::SDR50 => 0xFF_1F02, | ||
| 725 | Signalling::SDR25 => 0xFF_FF01, | ||
| 726 | Signalling::SDR12 => 0xFF_FF00, | ||
| 727 | }; | ||
| 728 | |||
| 729 | let status = match self.cmd_block.as_deref_mut() { | ||
| 730 | Some(x) => x, | ||
| 731 | None => &mut CmdBlock::new(), | ||
| 732 | }; | ||
| 733 | |||
| 734 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 735 | let regs = T::regs(); | ||
| 736 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 737 | |||
| 738 | let transfer = Self::prepare_datapath_read(&self.config, &mut self.dma, status.as_mut(), 64, 6); | ||
| 739 | InterruptHandler::<T>::data_interrupts(true); | ||
| 740 | Self::cmd(Cmd::cmd6(set_function), true)?; // CMD6 | ||
| 741 | |||
| 742 | let res = poll_fn(|cx| { | ||
| 743 | T::state().register(cx.waker()); | ||
| 744 | let status = regs.star().read(); | ||
| 745 | |||
| 746 | if status.dcrcfail() { | ||
| 747 | return Poll::Ready(Err(Error::Crc)); | ||
| 748 | } | ||
| 749 | if status.dtimeout() { | ||
| 750 | return Poll::Ready(Err(Error::Timeout)); | ||
| 751 | } | ||
| 752 | #[cfg(sdmmc_v1)] | ||
| 753 | if status.stbiterr() { | ||
| 754 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 755 | } | ||
| 756 | if status.dataend() { | ||
| 757 | return Poll::Ready(Ok(())); | ||
| 758 | } | ||
| 759 | Poll::Pending | ||
| 760 | }) | ||
| 761 | .await; | ||
| 762 | Self::clear_interrupt_flags(); | ||
| 763 | |||
| 764 | // Host is allowed to use the new functions at least 8 | ||
| 765 | // clocks after the end of the switch command | ||
| 766 | // transaction. We know the current clock period is < 80ns, | ||
| 767 | // so a total delay of 640ns is required here | ||
| 768 | for _ in 0..300 { | ||
| 769 | cortex_m::asm::nop(); | ||
| 770 | } | ||
| 771 | |||
| 772 | match res { | ||
| 773 | Ok(_) => { | ||
| 774 | on_drop.defuse(); | ||
| 775 | Self::stop_datapath(); | ||
| 776 | drop(transfer); | ||
| 777 | |||
| 778 | // Function Selection of Function Group 1 | ||
| 779 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | ||
| 780 | |||
| 781 | match selection { | ||
| 782 | 0 => Ok(Signalling::SDR12), | ||
| 783 | 1 => Ok(Signalling::SDR25), | ||
| 784 | 2 => Ok(Signalling::SDR50), | ||
| 785 | 3 => Ok(Signalling::SDR104), | ||
| 786 | 4 => Ok(Signalling::DDR50), | ||
| 787 | _ => Err(Error::UnsupportedCardType), | ||
| 788 | } | ||
| 789 | } | ||
| 790 | Err(e) => Err(e), | ||
| 791 | } | ||
| 792 | } | ||
| 793 | |||
| 794 | /// Query the card status (CMD13, returns R1) | 867 | /// Query the card status (CMD13, returns R1) |
| 795 | fn read_status(&self, card: &Card) -> Result<CardStatus, Error> { | 868 | fn read_status<Ext>(&self, card: &SdmmcPeripheral) -> Result<CardStatus<Ext>, Error> |
| 869 | where | ||
| 870 | CardStatus<Ext>: From<u32>, | ||
| 871 | { | ||
| 796 | let regs = T::regs(); | 872 | let regs = T::regs(); |
| 797 | let rca = card.rca; | 873 | let rca = card.get_address(); |
| 798 | 874 | ||
| 799 | Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13 | 875 | Self::cmd(common_cmd::card_status(rca, false), false)?; // CMD13 |
| 800 | 876 | ||
| 801 | let r1 = regs.respr(0).read().cardstatus(); | 877 | let r1 = regs.respr(0).read().cardstatus(); |
| 802 | Ok(r1.into()) | 878 | Ok(r1.into()) |
| 803 | } | 879 | } |
| 804 | 880 | ||
| 805 | /// Reads the SD Status (ACMD13) | ||
| 806 | async fn read_sd_status(&mut self) -> Result<(), Error> { | ||
| 807 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 808 | let rca = card.rca; | ||
| 809 | |||
| 810 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 811 | Some(x) => x, | ||
| 812 | None => &mut CmdBlock::new(), | ||
| 813 | }; | ||
| 814 | |||
| 815 | Self::cmd(Cmd::set_block_length(64), false)?; // CMD16 | ||
| 816 | Self::cmd(Cmd::app_cmd(rca << 16), false)?; // APP | ||
| 817 | |||
| 818 | let status = cmd_block; | ||
| 819 | |||
| 820 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 821 | let regs = T::regs(); | ||
| 822 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 823 | |||
| 824 | let transfer = Self::prepare_datapath_read(&self.config, &mut self.dma, status.as_mut(), 64, 6); | ||
| 825 | InterruptHandler::<T>::data_interrupts(true); | ||
| 826 | Self::cmd(Cmd::card_status(0), true)?; | ||
| 827 | |||
| 828 | let res = poll_fn(|cx| { | ||
| 829 | T::state().register(cx.waker()); | ||
| 830 | let status = regs.star().read(); | ||
| 831 | |||
| 832 | if status.dcrcfail() { | ||
| 833 | return Poll::Ready(Err(Error::Crc)); | ||
| 834 | } | ||
| 835 | if status.dtimeout() { | ||
| 836 | return Poll::Ready(Err(Error::Timeout)); | ||
| 837 | } | ||
| 838 | #[cfg(sdmmc_v1)] | ||
| 839 | if status.stbiterr() { | ||
| 840 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 841 | } | ||
| 842 | if status.dataend() { | ||
| 843 | return Poll::Ready(Ok(())); | ||
| 844 | } | ||
| 845 | Poll::Pending | ||
| 846 | }) | ||
| 847 | .await; | ||
| 848 | Self::clear_interrupt_flags(); | ||
| 849 | |||
| 850 | if res.is_ok() { | ||
| 851 | on_drop.defuse(); | ||
| 852 | Self::stop_datapath(); | ||
| 853 | drop(transfer); | ||
| 854 | |||
| 855 | for byte in status.iter_mut() { | ||
| 856 | *byte = u32::from_be(*byte); | ||
| 857 | } | ||
| 858 | self.card.as_mut().unwrap().status = status.0.into(); | ||
| 859 | } | ||
| 860 | res | ||
| 861 | } | ||
| 862 | |||
| 863 | /// Select one card and place it into the _Tranfer State_ | 881 | /// Select one card and place it into the _Tranfer State_ |
| 864 | /// | 882 | /// |
| 865 | /// If `None` is specifed for `card`, all cards are put back into | 883 | /// If `None` is specifed for `card`, all cards are put back into |
| 866 | /// _Stand-by State_ | 884 | /// _Stand-by State_ |
| 867 | fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { | 885 | fn select_card(&self, rca: Option<u16>) -> Result<(), Error> { |
| 868 | // Determine Relative Card Address (RCA) of given card | 886 | // Determine Relative Card Address (RCA) of given card |
| 869 | let rca = card.map(|c| c.rca << 16).unwrap_or(0); | 887 | let rca = rca.unwrap_or(0); |
| 870 | 888 | ||
| 871 | let r = Self::cmd(Cmd::sel_desel_card(rca), false); | 889 | let r = Self::cmd(common_cmd::select_card(rca), false); |
| 872 | match (r, rca) { | 890 | match (r, rca) { |
| 873 | (Err(Error::Timeout), 0) => Ok(()), | 891 | (Err(Error::Timeout), 0) => Ok(()), |
| 874 | _ => r, | 892 | _ => r, |
| @@ -909,63 +927,9 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 909 | }); | 927 | }); |
| 910 | } | 928 | } |
| 911 | 929 | ||
| 912 | async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { | ||
| 913 | // Read the the 64-bit SCR register | ||
| 914 | Self::cmd(Cmd::set_block_length(8), false)?; // CMD16 | ||
| 915 | Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; | ||
| 916 | |||
| 917 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 918 | Some(x) => x, | ||
| 919 | None => &mut CmdBlock::new(), | ||
| 920 | }; | ||
| 921 | let scr = &mut cmd_block.0[..2]; | ||
| 922 | |||
| 923 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 924 | let regs = T::regs(); | ||
| 925 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 926 | |||
| 927 | let transfer = Self::prepare_datapath_read(&self.config, &mut self.dma, scr, 8, 3); | ||
| 928 | InterruptHandler::<T>::data_interrupts(true); | ||
| 929 | Self::cmd(Cmd::cmd51(), true)?; | ||
| 930 | |||
| 931 | let res = poll_fn(|cx| { | ||
| 932 | T::state().register(cx.waker()); | ||
| 933 | let status = regs.star().read(); | ||
| 934 | |||
| 935 | if status.dcrcfail() { | ||
| 936 | return Poll::Ready(Err(Error::Crc)); | ||
| 937 | } | ||
| 938 | if status.dtimeout() { | ||
| 939 | return Poll::Ready(Err(Error::Timeout)); | ||
| 940 | } | ||
| 941 | #[cfg(sdmmc_v1)] | ||
| 942 | if status.stbiterr() { | ||
| 943 | return Poll::Ready(Err(Error::StBitErr)); | ||
| 944 | } | ||
| 945 | if status.dataend() { | ||
| 946 | return Poll::Ready(Ok(())); | ||
| 947 | } | ||
| 948 | Poll::Pending | ||
| 949 | }) | ||
| 950 | .await; | ||
| 951 | Self::clear_interrupt_flags(); | ||
| 952 | |||
| 953 | if res.is_ok() { | ||
| 954 | on_drop.defuse(); | ||
| 955 | Self::stop_datapath(); | ||
| 956 | drop(transfer); | ||
| 957 | |||
| 958 | unsafe { | ||
| 959 | let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); | ||
| 960 | card.scr = SCR(u64::from_be_bytes(*scr_bytes)); | ||
| 961 | } | ||
| 962 | } | ||
| 963 | res | ||
| 964 | } | ||
| 965 | |||
| 966 | /// Send command to card | 930 | /// Send command to card |
| 967 | #[allow(unused_variables)] | 931 | #[allow(unused_variables)] |
| 968 | fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { | 932 | fn cmd<R: Resp>(cmd: Cmd<R>, data: bool) -> Result<(), Error> { |
| 969 | let regs = T::regs(); | 933 | let regs = T::regs(); |
| 970 | 934 | ||
| 971 | Self::clear_interrupt_flags(); | 935 | Self::clear_interrupt_flags(); |
| @@ -978,7 +942,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 978 | // Command index and start CP State Machine | 942 | // Command index and start CP State Machine |
| 979 | regs.cmdr().write(|w| { | 943 | regs.cmdr().write(|w| { |
| 980 | w.set_waitint(false); | 944 | w.set_waitint(false); |
| 981 | w.set_waitresp(cmd.resp as u8); | 945 | w.set_waitresp(get_waitresp_val(cmd.response_len())); |
| 982 | w.set_cmdindex(cmd.cmd); | 946 | w.set_cmdindex(cmd.cmd); |
| 983 | w.set_cpsmen(true); | 947 | w.set_cpsmen(true); |
| 984 | 948 | ||
| @@ -993,7 +957,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 993 | }); | 957 | }); |
| 994 | 958 | ||
| 995 | let mut status; | 959 | let mut status; |
| 996 | if cmd.resp == Response::None { | 960 | if cmd.response_len() == ResponseLen::Zero { |
| 997 | // Wait for CMDSENT or a timeout | 961 | // Wait for CMDSENT or a timeout |
| 998 | while { | 962 | while { |
| 999 | status = regs.star().read(); | 963 | status = regs.star().read(); |
| @@ -1029,7 +993,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1029 | // Command index and start CP State Machine | 993 | // Command index and start CP State Machine |
| 1030 | regs.cmdr().write(|w| { | 994 | regs.cmdr().write(|w| { |
| 1031 | w.set_waitint(false); | 995 | w.set_waitint(false); |
| 1032 | w.set_waitresp(Response::Short as u8); | 996 | w.set_waitresp(get_waitresp_val(ResponseLen::R48)); |
| 1033 | w.set_cmdindex(12); | 997 | w.set_cmdindex(12); |
| 1034 | w.set_cpsmen(true); | 998 | w.set_cpsmen(true); |
| 1035 | 999 | ||
| @@ -1048,175 +1012,113 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1048 | Self::stop_datapath(); | 1012 | Self::stop_datapath(); |
| 1049 | } | 1013 | } |
| 1050 | 1014 | ||
| 1051 | /// Initializes card (if present) and sets the bus at the specified frequency. | 1015 | /// Wait for a previously started datapath transfer to complete from an interrupt. |
| 1052 | pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { | 1016 | #[inline] |
| 1017 | async fn complete_datapath_transfer() -> Result<(), Error> { | ||
| 1053 | let regs = T::regs(); | 1018 | let regs = T::regs(); |
| 1054 | let ker_ck = T::frequency(); | ||
| 1055 | 1019 | ||
| 1056 | let bus_width = match self.d3.is_some() { | 1020 | let res = poll_fn(|cx| { |
| 1057 | true => BusWidth::Four, | 1021 | T::state().register(cx.waker()); |
| 1058 | false => BusWidth::One, | 1022 | let status = regs.star().read(); |
| 1059 | }; | ||
| 1060 | |||
| 1061 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 1062 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 1063 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 1064 | self.clock = init_clock; | ||
| 1065 | |||
| 1066 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1067 | Self::wait_idle(); | ||
| 1068 | 1023 | ||
| 1069 | regs.clkcr().modify(|w| { | 1024 | if status.dcrcfail() { |
| 1070 | w.set_widbus(0); | 1025 | return Poll::Ready(Err(Error::Crc)); |
| 1071 | w.set_clkdiv(clkdiv); | 1026 | } |
| 1027 | if status.dtimeout() { | ||
| 1028 | return Poll::Ready(Err(Error::Timeout)); | ||
| 1029 | } | ||
| 1030 | if status.txunderr() { | ||
| 1031 | return Poll::Ready(Err(Error::Underrun)); | ||
| 1032 | } | ||
| 1072 | #[cfg(sdmmc_v1)] | 1033 | #[cfg(sdmmc_v1)] |
| 1073 | w.set_bypass(_bypass); | 1034 | if status.stbiterr() { |
| 1074 | }); | 1035 | return Poll::Ready(Err(Error::StBitErr)); |
| 1075 | |||
| 1076 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 1077 | Self::cmd(Cmd::idle(), false)?; | ||
| 1078 | |||
| 1079 | // Check if cards supports CMD8 (with pattern) | ||
| 1080 | Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; | ||
| 1081 | let r1 = regs.respr(0).read().cardstatus(); | ||
| 1082 | |||
| 1083 | let mut card = if r1 == 0x1AA { | ||
| 1084 | // Card echoed back the pattern. Must be at least v2 | ||
| 1085 | Card::default() | ||
| 1086 | } else { | ||
| 1087 | return Err(Error::UnsupportedCardVersion); | ||
| 1088 | }; | ||
| 1089 | |||
| 1090 | let ocr = loop { | ||
| 1091 | // Signal that next command is a app command | ||
| 1092 | Self::cmd(Cmd::app_cmd(0), false)?; // CMD55 | ||
| 1093 | |||
| 1094 | let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | ||
| 1095 | | CmdAppOper::HIGH_CAPACITY as u32 | ||
| 1096 | | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; | ||
| 1097 | |||
| 1098 | // Initialize card | ||
| 1099 | match Self::cmd(Cmd::app_op_cmd(arg), false) { | ||
| 1100 | // ACMD41 | ||
| 1101 | Ok(_) => (), | ||
| 1102 | Err(Error::Crc) => (), | ||
| 1103 | Err(err) => return Err(err), | ||
| 1104 | } | 1036 | } |
| 1105 | let ocr: OCR = regs.respr(0).read().cardstatus().into(); | 1037 | if status.dataend() { |
| 1106 | if !ocr.is_busy() { | 1038 | return Poll::Ready(Ok(())); |
| 1107 | // Power up done | ||
| 1108 | break ocr; | ||
| 1109 | } | 1039 | } |
| 1110 | }; | 1040 | Poll::Pending |
| 1111 | 1041 | }) | |
| 1112 | if ocr.high_capacity() { | 1042 | .await; |
| 1113 | // Card is SDHC or SDXC or SDUC | ||
| 1114 | card.card_type = CardCapacity::SDHC; | ||
| 1115 | } else { | ||
| 1116 | card.card_type = CardCapacity::SDSC; | ||
| 1117 | } | ||
| 1118 | card.ocr = ocr; | ||
| 1119 | |||
| 1120 | Self::cmd(Cmd::all_send_cid(), false)?; // CMD2 | ||
| 1121 | let cid0 = regs.respr(0).read().cardstatus() as u128; | ||
| 1122 | let cid1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1123 | let cid2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1124 | let cid3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1125 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 1126 | card.cid = cid.into(); | ||
| 1127 | 1043 | ||
| 1128 | Self::cmd(Cmd::send_rel_addr(), false)?; | 1044 | Self::clear_interrupt_flags(); |
| 1129 | card.rca = regs.respr(0).read().cardstatus() >> 16; | ||
| 1130 | 1045 | ||
| 1131 | Self::cmd(Cmd::send_csd(card.rca << 16), false)?; | 1046 | res |
| 1132 | let csd0 = regs.respr(0).read().cardstatus() as u128; | 1047 | } |
| 1133 | let csd1 = regs.respr(1).read().cardstatus() as u128; | ||
| 1134 | let csd2 = regs.respr(2).read().cardstatus() as u128; | ||
| 1135 | let csd3 = regs.respr(3).read().cardstatus() as u128; | ||
| 1136 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 1137 | card.csd = csd.into(); | ||
| 1138 | 1048 | ||
| 1139 | self.select_card(Some(&card))?; | 1049 | /// Read a data block. |
| 1050 | #[inline] | ||
| 1051 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | ||
| 1052 | let card_capacity = self.card()?.get_capacity(); | ||
| 1140 | 1053 | ||
| 1141 | self.get_scr(&mut card).await?; | 1054 | // NOTE(unsafe) DataBlock uses align 4 |
| 1055 | let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1142 | 1056 | ||
| 1143 | // Set bus width | 1057 | // Always read 1 block of 512 bytes |
| 1144 | let (width, acmd_arg) = match bus_width { | 1058 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes |
| 1145 | BusWidth::Eight => unimplemented!(), | 1059 | let address = match card_capacity { |
| 1146 | BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), | 1060 | CardCapacity::StandardCapacity => block_idx * 512, |
| 1147 | _ => (BusWidth::One, 0), | 1061 | _ => block_idx, |
| 1148 | }; | 1062 | }; |
| 1149 | Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; | 1063 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 |
| 1150 | Self::cmd(Cmd::cmd6(acmd_arg), false)?; | ||
| 1151 | |||
| 1152 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1153 | Self::wait_idle(); | ||
| 1154 | |||
| 1155 | regs.clkcr().modify(|w| { | ||
| 1156 | w.set_widbus(match width { | ||
| 1157 | BusWidth::One => 0, | ||
| 1158 | BusWidth::Four => 1, | ||
| 1159 | BusWidth::Eight => 2, | ||
| 1160 | _ => panic!("Invalid Bus Width"), | ||
| 1161 | }) | ||
| 1162 | }); | ||
| 1163 | |||
| 1164 | // Set Clock | ||
| 1165 | if freq.0 <= 25_000_000 { | ||
| 1166 | // Final clock frequency | ||
| 1167 | self.clkcr_set_clkdiv(freq.0, width)?; | ||
| 1168 | } else { | ||
| 1169 | // Switch to max clock for SDR12 | ||
| 1170 | self.clkcr_set_clkdiv(25_000_000, width)?; | ||
| 1171 | } | ||
| 1172 | 1064 | ||
| 1173 | self.card = Some(card); | 1065 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1174 | |||
| 1175 | // Read status | ||
| 1176 | self.read_sd_status().await?; | ||
| 1177 | 1066 | ||
| 1178 | if freq.0 > 25_000_000 { | 1067 | let transfer = Self::prepare_datapath_read( |
| 1179 | // Switch to SDR25 | 1068 | &self.config, |
| 1180 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; | 1069 | #[cfg(sdmmc_v1)] |
| 1070 | &mut self.dma, | ||
| 1071 | buffer, | ||
| 1072 | 512, | ||
| 1073 | 9, | ||
| 1074 | ); | ||
| 1075 | InterruptHandler::<T>::data_interrupts(true); | ||
| 1076 | Self::cmd(common_cmd::read_single_block(address), true)?; | ||
| 1181 | 1077 | ||
| 1182 | if self.signalling == Signalling::SDR25 { | 1078 | let res = Self::complete_datapath_transfer().await; |
| 1183 | // Set final clock frequency | ||
| 1184 | self.clkcr_set_clkdiv(freq.0, width)?; | ||
| 1185 | 1079 | ||
| 1186 | if self.read_status(&card)?.state() != CurrentState::Transfer { | 1080 | if res.is_ok() { |
| 1187 | return Err(Error::SignalingSwitchFailed); | 1081 | on_drop.defuse(); |
| 1188 | } | 1082 | Self::stop_datapath(); |
| 1189 | } | 1083 | drop(transfer); |
| 1190 | } | 1084 | } |
| 1191 | 1085 | res | |
| 1192 | // Read status after signalling change | ||
| 1193 | self.read_sd_status().await?; | ||
| 1194 | |||
| 1195 | Ok(()) | ||
| 1196 | } | 1086 | } |
| 1197 | 1087 | ||
| 1198 | /// Read a data block. | 1088 | /// Read multiple data blocks. |
| 1199 | #[inline] | 1089 | #[inline] |
| 1200 | pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { | 1090 | pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { |
| 1201 | let card_capacity = self.card()?.card_type; | 1091 | let card_capacity = self.card()?.get_capacity(); |
| 1202 | 1092 | ||
| 1203 | // NOTE(unsafe) DataBlock uses align 4 | 1093 | // NOTE(unsafe) reinterpret buffer as &mut [u32] |
| 1204 | let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; | 1094 | let buffer = unsafe { |
| 1095 | let ptr = blocks.as_mut_ptr() as *mut u32; | ||
| 1096 | let len = blocks.len() * 128; | ||
| 1097 | core::slice::from_raw_parts_mut(ptr, len) | ||
| 1098 | }; | ||
| 1205 | 1099 | ||
| 1206 | // Always read 1 block of 512 bytes | 1100 | // Always read 1 block of 512 bytes |
| 1207 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | 1101 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes |
| 1208 | let address = match card_capacity { | 1102 | let address = match card_capacity { |
| 1209 | CardCapacity::SDSC => block_idx * 512, | 1103 | CardCapacity::StandardCapacity => block_idx * 512, |
| 1210 | _ => block_idx, | 1104 | _ => block_idx, |
| 1211 | }; | 1105 | }; |
| 1212 | Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 | 1106 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 |
| 1213 | 1107 | ||
| 1214 | let regs = T::regs(); | 1108 | let regs = T::regs(); |
| 1215 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1109 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1216 | 1110 | ||
| 1217 | let transfer = Self::prepare_datapath_read(&self.config, &mut self.dma, buffer, 512, 9); | 1111 | let transfer = Self::prepare_datapath_read( |
| 1112 | &self.config, | ||
| 1113 | #[cfg(sdmmc_v1)] | ||
| 1114 | &mut self.dma, | ||
| 1115 | buffer, | ||
| 1116 | 512 * blocks.len() as u32, | ||
| 1117 | 9, | ||
| 1118 | ); | ||
| 1218 | InterruptHandler::<T>::data_interrupts(true); | 1119 | InterruptHandler::<T>::data_interrupts(true); |
| 1219 | Self::cmd(Cmd::read_single_block(address), true)?; | 1120 | |
| 1121 | Self::cmd(common_cmd::read_multiple_blocks(address), true)?; | ||
| 1220 | 1122 | ||
| 1221 | let res = poll_fn(|cx| { | 1123 | let res = poll_fn(|cx| { |
| 1222 | T::state().register(cx.waker()); | 1124 | T::state().register(cx.waker()); |
| @@ -1238,6 +1140,8 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1238 | Poll::Pending | 1140 | Poll::Pending |
| 1239 | }) | 1141 | }) |
| 1240 | .await; | 1142 | .await; |
| 1143 | |||
| 1144 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 1241 | Self::clear_interrupt_flags(); | 1145 | Self::clear_interrupt_flags(); |
| 1242 | 1146 | ||
| 1243 | if res.is_ok() { | 1147 | if res.is_ok() { |
| @@ -1256,28 +1160,92 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1256 | let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; | 1160 | let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; |
| 1257 | 1161 | ||
| 1258 | // Always read 1 block of 512 bytes | 1162 | // Always read 1 block of 512 bytes |
| 1259 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | 1163 | // cards are byte addressed hence the blockaddress is in multiples of 512 bytes |
| 1260 | let address = match card.card_type { | 1164 | let address = match card.get_capacity() { |
| 1261 | CardCapacity::SDSC => block_idx * 512, | 1165 | CardCapacity::StandardCapacity => block_idx * 512, |
| 1262 | _ => block_idx, | 1166 | _ => block_idx, |
| 1263 | }; | 1167 | }; |
| 1264 | Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 | 1168 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 |
| 1265 | 1169 | ||
| 1266 | let regs = T::regs(); | ||
| 1267 | let on_drop = OnDrop::new(|| Self::on_drop()); | 1170 | let on_drop = OnDrop::new(|| Self::on_drop()); |
| 1268 | 1171 | ||
| 1269 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes | 1172 | // sdmmc_v1 uses different cmd/dma order than v2, but only for writes |
| 1270 | #[cfg(sdmmc_v1)] | 1173 | #[cfg(sdmmc_v1)] |
| 1271 | Self::cmd(Cmd::write_single_block(address), true)?; | 1174 | Self::cmd(common_cmd::write_single_block(address), true)?; |
| 1272 | 1175 | ||
| 1273 | let transfer = self.prepare_datapath_write(buffer, 512, 9); | 1176 | let transfer = self.prepare_datapath_write(buffer, 512, 9); |
| 1274 | InterruptHandler::<T>::data_interrupts(true); | 1177 | InterruptHandler::<T>::data_interrupts(true); |
| 1275 | 1178 | ||
| 1276 | #[cfg(sdmmc_v2)] | 1179 | #[cfg(sdmmc_v2)] |
| 1277 | Self::cmd(Cmd::write_single_block(address), true)?; | 1180 | Self::cmd(common_cmd::write_single_block(address), true)?; |
| 1181 | |||
| 1182 | let res = Self::complete_datapath_transfer().await; | ||
| 1183 | |||
| 1184 | match res { | ||
| 1185 | Ok(_) => { | ||
| 1186 | on_drop.defuse(); | ||
| 1187 | Self::stop_datapath(); | ||
| 1188 | drop(transfer); | ||
| 1189 | |||
| 1190 | // TODO: Make this configurable | ||
| 1191 | let mut timeout: u32 = 0x00FF_FFFF; | ||
| 1192 | |||
| 1193 | let card = self.card.as_ref().unwrap(); | ||
| 1194 | while timeout > 0 { | ||
| 1195 | let ready_for_data = match card { | ||
| 1196 | SdmmcPeripheral::Emmc(_) => self.read_status::<EMMC>(card)?.ready_for_data(), | ||
| 1197 | SdmmcPeripheral::SdCard(_) => self.read_status::<SD>(card)?.ready_for_data(), | ||
| 1198 | }; | ||
| 1199 | |||
| 1200 | if ready_for_data { | ||
| 1201 | return Ok(()); | ||
| 1202 | } | ||
| 1203 | timeout -= 1; | ||
| 1204 | } | ||
| 1205 | Err(Error::SoftwareTimeout) | ||
| 1206 | } | ||
| 1207 | Err(e) => Err(e), | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | /// Write multiple data blocks. | ||
| 1212 | pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { | ||
| 1213 | let card = self.card.as_mut().ok_or(Error::NoCard)?; | ||
| 1214 | |||
| 1215 | // NOTE(unsafe) reinterpret buffer as &[u32] | ||
| 1216 | let buffer = unsafe { | ||
| 1217 | let ptr = blocks.as_ptr() as *const u32; | ||
| 1218 | let len = blocks.len() * 128; | ||
| 1219 | core::slice::from_raw_parts(ptr, len) | ||
| 1220 | }; | ||
| 1221 | |||
| 1222 | // Always read 1 block of 512 bytes | ||
| 1223 | // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes | ||
| 1224 | let address = match card.get_capacity() { | ||
| 1225 | CardCapacity::StandardCapacity => block_idx * 512, | ||
| 1226 | _ => block_idx, | ||
| 1227 | }; | ||
| 1228 | |||
| 1229 | Self::cmd(common_cmd::set_block_length(512), false)?; // CMD16 | ||
| 1230 | |||
| 1231 | let block_count = blocks.len(); | ||
| 1232 | |||
| 1233 | let regs = T::regs(); | ||
| 1234 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1235 | |||
| 1236 | #[cfg(sdmmc_v1)] | ||
| 1237 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 1238 | |||
| 1239 | // Setup write command | ||
| 1240 | let transfer = self.prepare_datapath_write(buffer, 512 * block_count as u32, 9); | ||
| 1241 | InterruptHandler::<T>::data_interrupts(true); | ||
| 1242 | |||
| 1243 | #[cfg(sdmmc_v2)] | ||
| 1244 | Self::cmd(common_cmd::write_multiple_blocks(address), true)?; // CMD25 | ||
| 1278 | 1245 | ||
| 1279 | let res = poll_fn(|cx| { | 1246 | let res = poll_fn(|cx| { |
| 1280 | T::state().register(cx.waker()); | 1247 | T::state().register(cx.waker()); |
| 1248 | |||
| 1281 | let status = regs.star().read(); | 1249 | let status = regs.star().read(); |
| 1282 | 1250 | ||
| 1283 | if status.dcrcfail() { | 1251 | if status.dcrcfail() { |
| @@ -1286,6 +1254,9 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1286 | if status.dtimeout() { | 1254 | if status.dtimeout() { |
| 1287 | return Poll::Ready(Err(Error::Timeout)); | 1255 | return Poll::Ready(Err(Error::Timeout)); |
| 1288 | } | 1256 | } |
| 1257 | if status.txunderr() { | ||
| 1258 | return Poll::Ready(Err(Error::Underrun)); | ||
| 1259 | } | ||
| 1289 | #[cfg(sdmmc_v1)] | 1260 | #[cfg(sdmmc_v1)] |
| 1290 | if status.stbiterr() { | 1261 | if status.stbiterr() { |
| 1291 | return Poll::Ready(Err(Error::StBitErr)); | 1262 | return Poll::Ready(Err(Error::StBitErr)); |
| @@ -1293,9 +1264,12 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1293 | if status.dataend() { | 1264 | if status.dataend() { |
| 1294 | return Poll::Ready(Ok(())); | 1265 | return Poll::Ready(Ok(())); |
| 1295 | } | 1266 | } |
| 1267 | |||
| 1296 | Poll::Pending | 1268 | Poll::Pending |
| 1297 | }) | 1269 | }) |
| 1298 | .await; | 1270 | .await; |
| 1271 | |||
| 1272 | Self::cmd(common_cmd::stop_transmission(), false)?; // CMD12 | ||
| 1299 | Self::clear_interrupt_flags(); | 1273 | Self::clear_interrupt_flags(); |
| 1300 | 1274 | ||
| 1301 | match res { | 1275 | match res { |
| @@ -1326,10 +1300,10 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1326 | /// | 1300 | /// |
| 1327 | /// # Errors | 1301 | /// # Errors |
| 1328 | /// | 1302 | /// |
| 1329 | /// Returns Error::NoCard if [`init_card`](#method.init_card) | 1303 | /// Returns Error::NoCard if [`init_sd_card`](#method.init_sd_card) or |
| 1330 | /// has not previously succeeded | 1304 | /// [`init_emmc`](#method.init_emmc) has not previously succeeded |
| 1331 | #[inline] | 1305 | #[inline] |
| 1332 | pub fn card(&self) -> Result<&Card, Error> { | 1306 | pub fn card(&self) -> Result<&SdmmcPeripheral, Error> { |
| 1333 | self.card.as_ref().ok_or(Error::NoCard) | 1307 | self.card.as_ref().ok_or(Error::NoCard) |
| 1334 | } | 1308 | } |
| 1335 | 1309 | ||
| @@ -1345,114 +1319,490 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> { | |||
| 1345 | pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { | 1319 | pub fn set_cmd_block(&mut self, cmd_block: &'d mut CmdBlock) { |
| 1346 | self.cmd_block = Some(cmd_block) | 1320 | self.cmd_block = Some(cmd_block) |
| 1347 | } | 1321 | } |
| 1348 | } | ||
| 1349 | 1322 | ||
| 1350 | impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Drop for Sdmmc<'d, T, Dma> { | 1323 | async fn init_internal(&mut self, freq: Hertz, mut card: SdmmcPeripheral) -> Result<(), Error> { |
| 1351 | fn drop(&mut self) { | 1324 | let regs = T::regs(); |
| 1352 | T::Interrupt::disable(); | 1325 | let ker_ck = T::frequency(); |
| 1353 | Self::on_drop(); | ||
| 1354 | 1326 | ||
| 1355 | critical_section::with(|_| { | 1327 | let bus_width = match (self.d3.is_some(), self.d7.is_some()) { |
| 1356 | self.clk.set_as_disconnected(); | 1328 | (true, true) => { |
| 1357 | self.cmd.set_as_disconnected(); | 1329 | if matches!(card, SdmmcPeripheral::SdCard(_)) { |
| 1358 | self.d0.set_as_disconnected(); | 1330 | return Err(Error::BusWidth); |
| 1359 | if let Some(x) = &mut self.d1 { | 1331 | } |
| 1360 | x.set_as_disconnected(); | 1332 | BusWidth::Eight |
| 1361 | } | 1333 | } |
| 1362 | if let Some(x) = &mut self.d2 { | 1334 | (true, false) => BusWidth::Four, |
| 1363 | x.set_as_disconnected(); | 1335 | _ => BusWidth::One, |
| 1336 | }; | ||
| 1337 | |||
| 1338 | // While the SD/SDIO card or eMMC is in identification mode, | ||
| 1339 | // the SDMMC_CK frequency must be no more than 400 kHz. | ||
| 1340 | let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); | ||
| 1341 | self.clock = init_clock; | ||
| 1342 | |||
| 1343 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1344 | Self::wait_idle(); | ||
| 1345 | |||
| 1346 | regs.clkcr().modify(|w| { | ||
| 1347 | w.set_widbus(0); | ||
| 1348 | w.set_clkdiv(clkdiv); | ||
| 1349 | #[cfg(sdmmc_v1)] | ||
| 1350 | w.set_bypass(_bypass); | ||
| 1351 | }); | ||
| 1352 | |||
| 1353 | regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); | ||
| 1354 | Self::cmd(common_cmd::idle(), false)?; | ||
| 1355 | |||
| 1356 | match card { | ||
| 1357 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1358 | // Check if cards supports CMD8 (with pattern) | ||
| 1359 | Self::cmd(sd_cmd::send_if_cond(1, 0xAA), false)?; | ||
| 1360 | let cic = CIC::from(regs.respr(0).read().cardstatus()); | ||
| 1361 | |||
| 1362 | if cic.pattern() != 0xAA { | ||
| 1363 | return Err(Error::UnsupportedCardVersion); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | if cic.voltage_accepted() & 1 == 0 { | ||
| 1367 | return Err(Error::UnsupportedVoltage); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | let ocr = loop { | ||
| 1371 | // Signal that next command is a app command | ||
| 1372 | Self::cmd(common_cmd::app_cmd(0), false)?; // CMD55 | ||
| 1373 | |||
| 1374 | // 3.2-3.3V | ||
| 1375 | let voltage_window = 1 << 5; | ||
| 1376 | // Initialize card | ||
| 1377 | match Self::cmd(sd_cmd::sd_send_op_cond(true, false, true, voltage_window), false) { | ||
| 1378 | // ACMD41 | ||
| 1379 | Ok(_) => (), | ||
| 1380 | Err(Error::Crc) => (), | ||
| 1381 | Err(err) => return Err(err), | ||
| 1382 | } | ||
| 1383 | let ocr: OCR<SD> = regs.respr(0).read().cardstatus().into(); | ||
| 1384 | if !ocr.is_busy() { | ||
| 1385 | // Power up done | ||
| 1386 | break ocr; | ||
| 1387 | } | ||
| 1388 | }; | ||
| 1389 | |||
| 1390 | if ocr.high_capacity() { | ||
| 1391 | // Card is SDHC or SDXC or SDUC | ||
| 1392 | card.card_type = CardCapacity::HighCapacity; | ||
| 1393 | } else { | ||
| 1394 | card.card_type = CardCapacity::StandardCapacity; | ||
| 1395 | } | ||
| 1396 | card.ocr = ocr; | ||
| 1364 | } | 1397 | } |
| 1365 | if let Some(x) = &mut self.d3 { | 1398 | SdmmcPeripheral::Emmc(ref mut emmc) => { |
| 1366 | x.set_as_disconnected(); | 1399 | let ocr = loop { |
| 1400 | let high_voltage = 0b0 << 7; | ||
| 1401 | let access_mode = 0b10 << 29; | ||
| 1402 | let op_cond = high_voltage | access_mode | 0b1_1111_1111 << 15; | ||
| 1403 | // Initialize card | ||
| 1404 | match Self::cmd(emmc_cmd::send_op_cond(op_cond), false) { | ||
| 1405 | Ok(_) => (), | ||
| 1406 | Err(Error::Crc) => (), | ||
| 1407 | Err(err) => return Err(err), | ||
| 1408 | } | ||
| 1409 | let ocr: OCR<EMMC> = regs.respr(0).read().cardstatus().into(); | ||
| 1410 | if !ocr.is_busy() { | ||
| 1411 | // Power up done | ||
| 1412 | break ocr; | ||
| 1413 | } | ||
| 1414 | }; | ||
| 1415 | |||
| 1416 | emmc.capacity = if ocr.access_mode() == 0b10 { | ||
| 1417 | // Card is SDHC or SDXC or SDUC | ||
| 1418 | CardCapacity::HighCapacity | ||
| 1419 | } else { | ||
| 1420 | CardCapacity::StandardCapacity | ||
| 1421 | }; | ||
| 1422 | emmc.ocr = ocr; | ||
| 1367 | } | 1423 | } |
| 1368 | }); | 1424 | } |
| 1369 | } | ||
| 1370 | } | ||
| 1371 | 1425 | ||
| 1372 | /// SD card Commands | 1426 | Self::cmd(common_cmd::all_send_cid(), false)?; // CMD2 |
| 1373 | impl Cmd { | 1427 | let cid0 = regs.respr(0).read().cardstatus() as u128; |
| 1374 | const fn new(cmd: u8, arg: u32, resp: Response) -> Cmd { | 1428 | let cid1 = regs.respr(1).read().cardstatus() as u128; |
| 1375 | Cmd { cmd, arg, resp } | 1429 | let cid2 = regs.respr(2).read().cardstatus() as u128; |
| 1376 | } | 1430 | let cid3 = regs.respr(3).read().cardstatus() as u128; |
| 1431 | let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); | ||
| 1377 | 1432 | ||
| 1378 | /// CMD0: Idle | 1433 | match card { |
| 1379 | const fn idle() -> Cmd { | 1434 | SdmmcPeripheral::SdCard(ref mut card) => { |
| 1380 | Cmd::new(0, 0, Response::None) | 1435 | card.cid = cid.into(); |
| 1381 | } | ||
| 1382 | 1436 | ||
| 1383 | /// CMD2: Send CID | 1437 | Self::cmd(sd_cmd::send_relative_address(), false)?; |
| 1384 | const fn all_send_cid() -> Cmd { | 1438 | let rca = RCA::<SD>::from(regs.respr(0).read().cardstatus()); |
| 1385 | Cmd::new(2, 0, Response::Long) | 1439 | card.rca = rca.address(); |
| 1386 | } | 1440 | } |
| 1441 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1442 | emmc.cid = cid.into(); | ||
| 1387 | 1443 | ||
| 1388 | /// CMD3: Send Relative Address | 1444 | emmc.rca = 1u16.into(); |
| 1389 | const fn send_rel_addr() -> Cmd { | 1445 | Self::cmd(emmc_cmd::assign_relative_address(emmc.rca), false)?; |
| 1390 | Cmd::new(3, 0, Response::Short) | 1446 | } |
| 1391 | } | 1447 | } |
| 1392 | 1448 | ||
| 1393 | /// CMD6: Switch Function Command | 1449 | Self::cmd(common_cmd::send_csd(card.get_address()), false)?; |
| 1394 | /// ACMD6: Bus Width | 1450 | let csd0 = regs.respr(0).read().cardstatus() as u128; |
| 1395 | const fn cmd6(arg: u32) -> Cmd { | 1451 | let csd1 = regs.respr(1).read().cardstatus() as u128; |
| 1396 | Cmd::new(6, arg, Response::Short) | 1452 | let csd2 = regs.respr(2).read().cardstatus() as u128; |
| 1397 | } | 1453 | let csd3 = regs.respr(3).read().cardstatus() as u128; |
| 1454 | let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3); | ||
| 1398 | 1455 | ||
| 1399 | /// CMD7: Select one card and put it into the _Tranfer State_ | 1456 | self.select_card(Some(card.get_address()))?; |
| 1400 | const fn sel_desel_card(rca: u32) -> Cmd { | 1457 | |
| 1401 | Cmd::new(7, rca, Response::Short) | 1458 | let bus_width = match card { |
| 1402 | } | 1459 | SdmmcPeripheral::SdCard(ref mut card) => { |
| 1460 | card.csd = csd.into(); | ||
| 1461 | |||
| 1462 | self.get_scr(card).await?; | ||
| 1463 | |||
| 1464 | if !card.scr.bus_width_four() { | ||
| 1465 | BusWidth::One | ||
| 1466 | } else { | ||
| 1467 | BusWidth::Four | ||
| 1468 | } | ||
| 1469 | } | ||
| 1470 | SdmmcPeripheral::Emmc(ref mut emmc) => { | ||
| 1471 | emmc.csd = csd.into(); | ||
| 1472 | |||
| 1473 | bus_width | ||
| 1474 | } | ||
| 1475 | }; | ||
| 1403 | 1476 | ||
| 1404 | /// CMD8: | 1477 | // Set bus width |
| 1405 | const fn hs_send_ext_csd(arg: u32) -> Cmd { | 1478 | let widbus = match bus_width { |
| 1406 | Cmd::new(8, arg, Response::Short) | 1479 | BusWidth::Eight => 2, |
| 1480 | BusWidth::Four => 1, | ||
| 1481 | BusWidth::One => 0, | ||
| 1482 | _ => unreachable!(), | ||
| 1483 | }; | ||
| 1484 | |||
| 1485 | match card { | ||
| 1486 | SdmmcPeripheral::SdCard(ref mut card) => { | ||
| 1487 | let acmd_arg = match bus_width { | ||
| 1488 | BusWidth::Four if card.scr.bus_width_four() => 2, | ||
| 1489 | _ => 0, | ||
| 1490 | }; | ||
| 1491 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1492 | Self::cmd(sd_cmd::cmd6(acmd_arg), false)?; | ||
| 1493 | } | ||
| 1494 | SdmmcPeripheral::Emmc(_) => { | ||
| 1495 | // Write bus width to ExtCSD byte 183 | ||
| 1496 | Self::cmd( | ||
| 1497 | emmc_cmd::modify_ext_csd(emmc_cmd::AccessMode::WriteByte, 183, widbus), | ||
| 1498 | false, | ||
| 1499 | )?; | ||
| 1500 | |||
| 1501 | // Wait for ready after R1b response | ||
| 1502 | loop { | ||
| 1503 | let status = self.read_status::<EMMC>(&card)?; | ||
| 1504 | |||
| 1505 | if status.ready_for_data() { | ||
| 1506 | break; | ||
| 1507 | } | ||
| 1508 | } | ||
| 1509 | } | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | // CPSMACT and DPSMACT must be 0 to set WIDBUS | ||
| 1513 | Self::wait_idle(); | ||
| 1514 | |||
| 1515 | regs.clkcr().modify(|w| w.set_widbus(widbus)); | ||
| 1516 | |||
| 1517 | // Set Clock | ||
| 1518 | if freq.0 <= 25_000_000 { | ||
| 1519 | // Final clock frequency | ||
| 1520 | self.clkcr_set_clkdiv(freq.0, bus_width)?; | ||
| 1521 | } else { | ||
| 1522 | // Switch to max clock for SDR12 | ||
| 1523 | self.clkcr_set_clkdiv(25_000_000, bus_width)?; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | self.card = Some(card); | ||
| 1527 | |||
| 1528 | match card { | ||
| 1529 | SdmmcPeripheral::SdCard(_) => { | ||
| 1530 | // Read status | ||
| 1531 | self.read_sd_status().await?; | ||
| 1532 | |||
| 1533 | if freq.0 > 25_000_000 { | ||
| 1534 | // Switch to SDR25 | ||
| 1535 | self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; | ||
| 1536 | |||
| 1537 | if self.signalling == Signalling::SDR25 { | ||
| 1538 | // Set final clock frequency | ||
| 1539 | self.clkcr_set_clkdiv(freq.0, bus_width)?; | ||
| 1540 | |||
| 1541 | if self.read_status::<SD>(self.card.as_ref().unwrap())?.state() != CurrentState::Transfer { | ||
| 1542 | return Err(Error::SignalingSwitchFailed); | ||
| 1543 | } | ||
| 1544 | } | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | // Read status after signalling change | ||
| 1548 | self.read_sd_status().await?; | ||
| 1549 | } | ||
| 1550 | SdmmcPeripheral::Emmc(_) => { | ||
| 1551 | self.read_ext_csd().await?; | ||
| 1552 | } | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | Ok(()) | ||
| 1407 | } | 1556 | } |
| 1408 | 1557 | ||
| 1409 | /// CMD9: | 1558 | /// Initializes card (if present) and sets the bus at the specified frequency. |
| 1410 | const fn send_csd(rca: u32) -> Cmd { | 1559 | /// |
| 1411 | Cmd::new(9, rca, Response::Long) | 1560 | /// SD only. |
| 1561 | pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1562 | self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await | ||
| 1412 | } | 1563 | } |
| 1413 | 1564 | ||
| 1414 | /// CMD12: | 1565 | /// Switch mode using CMD6. |
| 1415 | //const fn stop_transmission() -> Cmd { | 1566 | /// |
| 1416 | // Cmd::new(12, 0, Response::Short) | 1567 | /// Attempt to set a new signalling mode. The selected |
| 1417 | //} | 1568 | /// signalling mode is returned. Expects the current clock |
| 1569 | /// frequency to be > 12.5MHz. | ||
| 1570 | /// | ||
| 1571 | /// SD only. | ||
| 1572 | async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result<Signalling, Error> { | ||
| 1573 | let _ = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); | ||
| 1574 | // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not | ||
| 1575 | // necessary" | ||
| 1418 | 1576 | ||
| 1419 | /// CMD13: Ask card to send status register | 1577 | let set_function = 0x8000_0000 |
| 1420 | /// ACMD13: SD Status | 1578 | | match signalling { |
| 1421 | const fn card_status(rca: u32) -> Cmd { | 1579 | // See PLSS v7_10 Table 4-11 |
| 1422 | Cmd::new(13, rca, Response::Short) | 1580 | Signalling::DDR50 => 0xFF_FF04, |
| 1423 | } | 1581 | Signalling::SDR104 => 0xFF_1F03, |
| 1582 | Signalling::SDR50 => 0xFF_1F02, | ||
| 1583 | Signalling::SDR25 => 0xFF_FF01, | ||
| 1584 | Signalling::SDR12 => 0xFF_FF00, | ||
| 1585 | }; | ||
| 1586 | |||
| 1587 | let status = match self.cmd_block.as_deref_mut() { | ||
| 1588 | Some(x) => x, | ||
| 1589 | None => &mut CmdBlock::new(), | ||
| 1590 | }; | ||
| 1591 | |||
| 1592 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1593 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1424 | 1594 | ||
| 1425 | /// CMD16: | 1595 | let transfer = Self::prepare_datapath_read( |
| 1426 | const fn set_block_length(blocklen: u32) -> Cmd { | 1596 | &self.config, |
| 1427 | Cmd::new(16, blocklen, Response::Short) | 1597 | #[cfg(sdmmc_v1)] |
| 1598 | &mut self.dma, | ||
| 1599 | status.as_mut(), | ||
| 1600 | 64, | ||
| 1601 | 6, | ||
| 1602 | ); | ||
| 1603 | InterruptHandler::<T>::data_interrupts(true); | ||
| 1604 | Self::cmd(sd_cmd::cmd6(set_function), true)?; // CMD6 | ||
| 1605 | |||
| 1606 | let res = Self::complete_datapath_transfer().await; | ||
| 1607 | |||
| 1608 | // Host is allowed to use the new functions at least 8 | ||
| 1609 | // clocks after the end of the switch command | ||
| 1610 | // transaction. We know the current clock period is < 80ns, | ||
| 1611 | // so a total delay of 640ns is required here | ||
| 1612 | for _ in 0..300 { | ||
| 1613 | cortex_m::asm::nop(); | ||
| 1614 | } | ||
| 1615 | |||
| 1616 | match res { | ||
| 1617 | Ok(_) => { | ||
| 1618 | on_drop.defuse(); | ||
| 1619 | Self::stop_datapath(); | ||
| 1620 | drop(transfer); | ||
| 1621 | |||
| 1622 | // Function Selection of Function Group 1 | ||
| 1623 | let selection = (u32::from_be(status[4]) >> 24) & 0xF; | ||
| 1624 | |||
| 1625 | match selection { | ||
| 1626 | 0 => Ok(Signalling::SDR12), | ||
| 1627 | 1 => Ok(Signalling::SDR25), | ||
| 1628 | 2 => Ok(Signalling::SDR50), | ||
| 1629 | 3 => Ok(Signalling::SDR104), | ||
| 1630 | 4 => Ok(Signalling::DDR50), | ||
| 1631 | _ => Err(Error::UnsupportedCardType), | ||
| 1632 | } | ||
| 1633 | } | ||
| 1634 | Err(e) => Err(e), | ||
| 1635 | } | ||
| 1428 | } | 1636 | } |
| 1429 | 1637 | ||
| 1430 | /// CMD17: Block Read | 1638 | /// Reads the SCR register. |
| 1431 | const fn read_single_block(addr: u32) -> Cmd { | 1639 | /// |
| 1432 | Cmd::new(17, addr, Response::Short) | 1640 | /// SD only. |
| 1641 | async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { | ||
| 1642 | // Read the 64-bit SCR register | ||
| 1643 | Self::cmd(common_cmd::set_block_length(8), false)?; // CMD16 | ||
| 1644 | Self::cmd(common_cmd::app_cmd(card.rca), false)?; | ||
| 1645 | |||
| 1646 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 1647 | Some(x) => x, | ||
| 1648 | None => &mut CmdBlock::new(), | ||
| 1649 | }; | ||
| 1650 | let scr = &mut cmd_block.0[..2]; | ||
| 1651 | |||
| 1652 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1653 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1654 | |||
| 1655 | let transfer = Self::prepare_datapath_read( | ||
| 1656 | &self.config, | ||
| 1657 | #[cfg(sdmmc_v1)] | ||
| 1658 | &mut self.dma, | ||
| 1659 | scr, | ||
| 1660 | 8, | ||
| 1661 | 3, | ||
| 1662 | ); | ||
| 1663 | InterruptHandler::<T>::data_interrupts(true); | ||
| 1664 | Self::cmd(sd_cmd::send_scr(), true)?; | ||
| 1665 | |||
| 1666 | let res = Self::complete_datapath_transfer().await; | ||
| 1667 | |||
| 1668 | if res.is_ok() { | ||
| 1669 | on_drop.defuse(); | ||
| 1670 | Self::stop_datapath(); | ||
| 1671 | drop(transfer); | ||
| 1672 | |||
| 1673 | unsafe { | ||
| 1674 | let scr_bytes = &*(&scr as *const _ as *const [u8; 8]); | ||
| 1675 | card.scr = SCR(u64::from_be_bytes(*scr_bytes)); | ||
| 1676 | } | ||
| 1677 | } | ||
| 1678 | res | ||
| 1433 | } | 1679 | } |
| 1434 | 1680 | ||
| 1435 | /// CMD18: Multiple Block Read | 1681 | /// Reads the SD Status (ACMD13) |
| 1436 | //const fn read_multiple_blocks(addr: u32) -> Cmd { | 1682 | /// |
| 1437 | // Cmd::new(18, addr, Response::Short) | 1683 | /// SD only. |
| 1438 | //} | 1684 | async fn read_sd_status(&mut self) -> Result<(), Error> { |
| 1685 | let card = self.card.as_mut().ok_or(Error::NoCard)?.get_sd_card(); | ||
| 1686 | let rca = card.rca; | ||
| 1687 | |||
| 1688 | let cmd_block = match self.cmd_block.as_deref_mut() { | ||
| 1689 | Some(x) => x, | ||
| 1690 | None => &mut CmdBlock::new(), | ||
| 1691 | }; | ||
| 1692 | |||
| 1693 | Self::cmd(common_cmd::set_block_length(64), false)?; // CMD16 | ||
| 1694 | Self::cmd(common_cmd::app_cmd(rca), false)?; // APP | ||
| 1695 | |||
| 1696 | let status = cmd_block; | ||
| 1697 | |||
| 1698 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1699 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1439 | 1700 | ||
| 1440 | /// CMD24: Block Write | 1701 | let transfer = Self::prepare_datapath_read( |
| 1441 | const fn write_single_block(addr: u32) -> Cmd { | 1702 | &self.config, |
| 1442 | Cmd::new(24, addr, Response::Short) | 1703 | #[cfg(sdmmc_v1)] |
| 1704 | &mut self.dma, | ||
| 1705 | status.as_mut(), | ||
| 1706 | 64, | ||
| 1707 | 6, | ||
| 1708 | ); | ||
| 1709 | InterruptHandler::<T>::data_interrupts(true); | ||
| 1710 | Self::cmd(sd_cmd::sd_status(), true)?; | ||
| 1711 | |||
| 1712 | let res = Self::complete_datapath_transfer().await; | ||
| 1713 | |||
| 1714 | if res.is_ok() { | ||
| 1715 | on_drop.defuse(); | ||
| 1716 | Self::stop_datapath(); | ||
| 1717 | drop(transfer); | ||
| 1718 | |||
| 1719 | for byte in status.iter_mut() { | ||
| 1720 | *byte = u32::from_be(*byte); | ||
| 1721 | } | ||
| 1722 | card.status = status.0.into(); | ||
| 1723 | } | ||
| 1724 | res | ||
| 1443 | } | 1725 | } |
| 1444 | 1726 | ||
| 1445 | const fn app_op_cmd(arg: u32) -> Cmd { | 1727 | /// Initializes eMMC and sets the bus at the specified frequency. |
| 1446 | Cmd::new(41, arg, Response::Short) | 1728 | /// |
| 1729 | /// eMMC only. | ||
| 1730 | pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { | ||
| 1731 | self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await | ||
| 1447 | } | 1732 | } |
| 1448 | 1733 | ||
| 1449 | const fn cmd51() -> Cmd { | 1734 | /// Gets the EXT_CSD register. |
| 1450 | Cmd::new(51, 0, Response::Short) | 1735 | /// |
| 1736 | /// eMMC only. | ||
| 1737 | async fn read_ext_csd(&mut self) -> Result<(), Error> { | ||
| 1738 | let card = self.card.as_mut().ok_or(Error::NoCard)?.get_emmc(); | ||
| 1739 | |||
| 1740 | // Note: cmd_block can't be used because ExtCSD is too long to fit. | ||
| 1741 | let mut data_block = DataBlock([0u8; 512]); | ||
| 1742 | |||
| 1743 | // NOTE(unsafe) DataBlock uses align 4 | ||
| 1744 | let buffer = unsafe { &mut *((&mut data_block.0) as *mut [u8; 512] as *mut [u32; 128]) }; | ||
| 1745 | |||
| 1746 | Self::cmd(common_cmd::set_block_length(512), false).unwrap(); // CMD16 | ||
| 1747 | |||
| 1748 | // Arm `OnDrop` after the buffer, so it will be dropped first | ||
| 1749 | let on_drop = OnDrop::new(|| Self::on_drop()); | ||
| 1750 | |||
| 1751 | let transfer = Self::prepare_datapath_read( | ||
| 1752 | &self.config, | ||
| 1753 | #[cfg(sdmmc_v1)] | ||
| 1754 | &mut self.dma, | ||
| 1755 | buffer, | ||
| 1756 | 512, | ||
| 1757 | 9, | ||
| 1758 | ); | ||
| 1759 | InterruptHandler::<T>::data_interrupts(true); | ||
| 1760 | Self::cmd(emmc_cmd::send_ext_csd(), true)?; | ||
| 1761 | |||
| 1762 | let res = Self::complete_datapath_transfer().await; | ||
| 1763 | |||
| 1764 | if res.is_ok() { | ||
| 1765 | on_drop.defuse(); | ||
| 1766 | Self::stop_datapath(); | ||
| 1767 | drop(transfer); | ||
| 1768 | |||
| 1769 | card.ext_csd = unsafe { core::mem::transmute::<_, [u32; 128]>(data_block.0) }.into(); | ||
| 1770 | } | ||
| 1771 | res | ||
| 1451 | } | 1772 | } |
| 1773 | } | ||
| 1774 | |||
| 1775 | impl<'d, T: Instance> Drop for Sdmmc<'d, T> { | ||
| 1776 | fn drop(&mut self) { | ||
| 1777 | T::Interrupt::disable(); | ||
| 1778 | Self::on_drop(); | ||
| 1452 | 1779 | ||
| 1453 | /// App Command. Indicates that next command will be a app command | 1780 | critical_section::with(|_| { |
| 1454 | const fn app_cmd(rca: u32) -> Cmd { | 1781 | self.clk.set_as_disconnected(); |
| 1455 | Cmd::new(55, rca, Response::Short) | 1782 | self.cmd.set_as_disconnected(); |
| 1783 | self.d0.set_as_disconnected(); | ||
| 1784 | if let Some(x) = &mut self.d1 { | ||
| 1785 | x.set_as_disconnected(); | ||
| 1786 | } | ||
| 1787 | if let Some(x) = &mut self.d2 { | ||
| 1788 | x.set_as_disconnected(); | ||
| 1789 | } | ||
| 1790 | if let Some(x) = &mut self.d3 { | ||
| 1791 | x.set_as_disconnected(); | ||
| 1792 | } | ||
| 1793 | if let Some(x) = &mut self.d4 { | ||
| 1794 | x.set_as_disconnected(); | ||
| 1795 | } | ||
| 1796 | if let Some(x) = &mut self.d5 { | ||
| 1797 | x.set_as_disconnected(); | ||
| 1798 | } | ||
| 1799 | if let Some(x) = &mut self.d6 { | ||
| 1800 | x.set_as_disconnected(); | ||
| 1801 | } | ||
| 1802 | if let Some(x) = &mut self.d7 { | ||
| 1803 | x.set_as_disconnected(); | ||
| 1804 | } | ||
| 1805 | }); | ||
| 1456 | } | 1806 | } |
| 1457 | } | 1807 | } |
| 1458 | 1808 | ||
| @@ -1465,7 +1815,7 @@ trait SealedInstance { | |||
| 1465 | 1815 | ||
| 1466 | /// SDMMC instance trait. | 1816 | /// SDMMC instance trait. |
| 1467 | #[allow(private_bounds)] | 1817 | #[allow(private_bounds)] |
| 1468 | pub trait Instance: SealedInstance + RccPeripheral + 'static { | 1818 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static { |
| 1469 | /// Interrupt for this instance. | 1819 | /// Interrupt for this instance. |
| 1470 | type Interrupt: interrupt::typelevel::Interrupt; | 1820 | type Interrupt: interrupt::typelevel::Interrupt; |
| 1471 | } | 1821 | } |
| @@ -1484,15 +1834,6 @@ pin_trait!(D7Pin, Instance); | |||
| 1484 | #[cfg(sdmmc_v1)] | 1834 | #[cfg(sdmmc_v1)] |
| 1485 | dma_trait!(SdmmcDma, Instance); | 1835 | dma_trait!(SdmmcDma, Instance); |
| 1486 | 1836 | ||
| 1487 | /// DMA instance trait. | ||
| 1488 | /// | ||
| 1489 | /// This is only implemented for `NoDma`, since SDMMCv2 has DMA built-in, instead of | ||
| 1490 | /// using ST's system-wide DMA peripheral. | ||
| 1491 | #[cfg(sdmmc_v2)] | ||
| 1492 | pub trait SdmmcDma<T: Instance> {} | ||
| 1493 | #[cfg(sdmmc_v2)] | ||
| 1494 | impl<T: Instance> SdmmcDma<T> for NoDma {} | ||
| 1495 | |||
| 1496 | foreach_peripheral!( | 1837 | foreach_peripheral!( |
| 1497 | (sdmmc, $inst:ident) => { | 1838 | (sdmmc, $inst:ident) => { |
| 1498 | impl SealedInstance for peripherals::$inst { | 1839 | impl SealedInstance for peripherals::$inst { |
| @@ -1511,3 +1852,46 @@ foreach_peripheral!( | |||
| 1511 | } | 1852 | } |
| 1512 | }; | 1853 | }; |
| 1513 | ); | 1854 | ); |
| 1855 | |||
| 1856 | impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { | ||
| 1857 | type Error = Error; | ||
| 1858 | type Align = aligned::A4; | ||
| 1859 | |||
| 1860 | async fn read( | ||
| 1861 | &mut self, | ||
| 1862 | block_address: u32, | ||
| 1863 | buf: &mut [aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1864 | ) -> Result<(), Self::Error> { | ||
| 1865 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1866 | if buf.len() == 1 { | ||
| 1867 | let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; | ||
| 1868 | self.read_block(block_address, block).await?; | ||
| 1869 | } else { | ||
| 1870 | let blocks: &mut [DataBlock] = | ||
| 1871 | unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut DataBlock, buf.len()) }; | ||
| 1872 | self.read_blocks(block_address, blocks).await?; | ||
| 1873 | } | ||
| 1874 | Ok(()) | ||
| 1875 | } | ||
| 1876 | |||
| 1877 | async fn write( | ||
| 1878 | &mut self, | ||
| 1879 | block_address: u32, | ||
| 1880 | buf: &[aligned::Aligned<Self::Align, [u8; 512]>], | ||
| 1881 | ) -> Result<(), Self::Error> { | ||
| 1882 | // TODO: I think block_address needs to be adjusted by the partition start offset | ||
| 1883 | if buf.len() == 1 { | ||
| 1884 | let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; | ||
| 1885 | self.write_block(block_address, block).await?; | ||
| 1886 | } else { | ||
| 1887 | let blocks: &[DataBlock] = | ||
| 1888 | unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const DataBlock, buf.len()) }; | ||
| 1889 | self.write_blocks(block_address, blocks).await?; | ||
| 1890 | } | ||
| 1891 | Ok(()) | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | async fn size(&mut self) -> Result<u64, Self::Error> { | ||
| 1895 | Ok(self.card()?.size()) | ||
| 1896 | } | ||
| 1897 | } | ||
diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs new file mode 100644 index 000000000..9c42217f0 --- /dev/null +++ b/embassy-stm32/src/spdifrx/mod.rs | |||
| @@ -0,0 +1,333 @@ | |||
| 1 | //! S/PDIF receiver | ||
| 2 | #![macro_use] | ||
| 3 | #![cfg_attr(gpdma, allow(unused))] | ||
| 4 | |||
| 5 | use core::marker::PhantomData; | ||
| 6 | |||
| 7 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 8 | |||
| 9 | use crate::dma::ringbuffer::Error as RingbufferError; | ||
| 10 | pub use crate::dma::word; | ||
| 11 | #[cfg(not(gpdma))] | ||
| 12 | use crate::dma::ReadableRingBuffer; | ||
| 13 | use crate::dma::{Channel, TransferOptions}; | ||
| 14 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; | ||
| 15 | use crate::interrupt::typelevel::Interrupt; | ||
| 16 | use crate::pac::spdifrx::Spdifrx as Regs; | ||
| 17 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | ||
| 18 | use crate::{interrupt, peripherals, Peri}; | ||
| 19 | |||
| 20 | /// Possible S/PDIF preamble types. | ||
| 21 | #[allow(dead_code)] | ||
| 22 | #[repr(u8)] | ||
| 23 | enum PreambleType { | ||
| 24 | Unused = 0x00, | ||
| 25 | /// The preamble changes to preamble “B” once every 192 frames to identify the start of the block structure used to | ||
| 26 | /// organize the channel status and user information. | ||
| 27 | B = 0x01, | ||
| 28 | /// The first sub-frame (left or “A” channel in stereophonic operation and primary channel in monophonic operation) | ||
| 29 | /// normally starts with preamble “M” | ||
| 30 | M = 0x02, | ||
| 31 | /// The second sub-frame (right or “B” channel in stereophonic operation and secondary channel in monophonic | ||
| 32 | /// operation) always starts with preamble “W”. | ||
| 33 | W = 0x03, | ||
| 34 | } | ||
| 35 | |||
| 36 | macro_rules! new_spdifrx_pin { | ||
| 37 | ($name:ident, $af_type:expr) => {{ | ||
| 38 | let pin = $name; | ||
| 39 | let input_sel = pin.input_sel(); | ||
| 40 | pin.set_as_af(pin.af_num(), $af_type); | ||
| 41 | (Some(pin.into()), input_sel) | ||
| 42 | }}; | ||
| 43 | } | ||
| 44 | |||
| 45 | macro_rules! impl_spdifrx_pin { | ||
| 46 | ($inst:ident, $pin:ident, $af:expr, $sel:expr) => { | ||
| 47 | impl crate::spdifrx::InPin<peripherals::$inst> for crate::peripherals::$pin { | ||
| 48 | fn af_num(&self) -> u8 { | ||
| 49 | $af | ||
| 50 | } | ||
| 51 | fn input_sel(&self) -> u8 { | ||
| 52 | $sel | ||
| 53 | } | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Ring-buffered SPDIFRX driver. | ||
| 59 | /// | ||
| 60 | /// Data is read by DMAs and stored in a ring buffer. | ||
| 61 | #[cfg(not(gpdma))] | ||
| 62 | pub struct Spdifrx<'d, T: Instance> { | ||
| 63 | _peri: Peri<'d, T>, | ||
| 64 | spdifrx_in: Option<Peri<'d, AnyPin>>, | ||
| 65 | data_ring_buffer: ReadableRingBuffer<'d, u32>, | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Gives the address of the data register. | ||
| 69 | fn dr_address(r: Regs) -> *mut u32 { | ||
| 70 | #[cfg(spdifrx_v1)] | ||
| 71 | let address = r.dr().as_ptr() as _; | ||
| 72 | #[cfg(spdifrx_h7)] | ||
| 73 | let address = r.fmt0_dr().as_ptr() as _; // All fmtx_dr() implementations have the same address. | ||
| 74 | |||
| 75 | return address; | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Gives the address of the channel status register. | ||
| 79 | #[allow(unused)] | ||
| 80 | fn csr_address(r: Regs) -> *mut u32 { | ||
| 81 | r.csr().as_ptr() as _ | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Select the channel for capturing control information. | ||
| 85 | pub enum ControlChannelSelection { | ||
| 86 | /// Capture control info from channel A. | ||
| 87 | A, | ||
| 88 | /// Capture control info from channel B. | ||
| 89 | B, | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Configuration options for the SPDIFRX driver. | ||
| 93 | pub struct Config { | ||
| 94 | /// Select the channel for capturing control information. | ||
| 95 | pub control_channel_selection: ControlChannelSelection, | ||
| 96 | } | ||
| 97 | |||
| 98 | /// S/PDIF errors. | ||
| 99 | #[derive(Debug)] | ||
| 100 | pub enum Error { | ||
| 101 | /// DMA overrun error. | ||
| 102 | RingbufferError(RingbufferError), | ||
| 103 | /// Left/right channel synchronization error. | ||
| 104 | ChannelSyncError, | ||
| 105 | } | ||
| 106 | |||
| 107 | impl From<RingbufferError> for Error { | ||
| 108 | fn from(error: RingbufferError) -> Self { | ||
| 109 | Self::RingbufferError(error) | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | impl Default for Config { | ||
| 114 | fn default() -> Self { | ||
| 115 | Self { | ||
| 116 | control_channel_selection: ControlChannelSelection::A, | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | #[cfg(not(gpdma))] | ||
| 122 | impl<'d, T: Instance> Spdifrx<'d, T> { | ||
| 123 | fn dma_opts() -> TransferOptions { | ||
| 124 | TransferOptions { | ||
| 125 | half_transfer_ir: true, | ||
| 126 | // new_write() and new_read() always use circular mode | ||
| 127 | ..Default::default() | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Create a new `Spdifrx` instance. | ||
| 132 | pub fn new( | ||
| 133 | peri: Peri<'d, T>, | ||
| 134 | _irq: impl interrupt::typelevel::Binding<T::GlobalInterrupt, GlobalInterruptHandler<T>> + 'd, | ||
| 135 | config: Config, | ||
| 136 | spdifrx_in: Peri<'d, impl InPin<T>>, | ||
| 137 | data_dma: Peri<'d, impl Channel + Dma<T>>, | ||
| 138 | data_dma_buf: &'d mut [u32], | ||
| 139 | ) -> Self { | ||
| 140 | let (spdifrx_in, input_sel) = new_spdifrx_pin!(spdifrx_in, AfType::input(Pull::None)); | ||
| 141 | Self::setup(config, input_sel); | ||
| 142 | |||
| 143 | let regs = T::info().regs; | ||
| 144 | let dr_request = data_dma.request(); | ||
| 145 | let dr_ring_buffer = | ||
| 146 | unsafe { ReadableRingBuffer::new(data_dma, dr_request, dr_address(regs), data_dma_buf, Self::dma_opts()) }; | ||
| 147 | |||
| 148 | Self { | ||
| 149 | _peri: peri, | ||
| 150 | spdifrx_in, | ||
| 151 | data_ring_buffer: dr_ring_buffer, | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | fn setup(config: Config, input_sel: u8) { | ||
| 156 | T::info().rcc.enable_and_reset(); | ||
| 157 | T::GlobalInterrupt::unpend(); | ||
| 158 | unsafe { T::GlobalInterrupt::enable() }; | ||
| 159 | |||
| 160 | let regs = T::info().regs; | ||
| 161 | |||
| 162 | regs.imr().write(|imr| { | ||
| 163 | imr.set_ifeie(true); // Enables interrupts for TERR, SERR, FERR. | ||
| 164 | imr.set_syncdie(true); // Enables SYNCD interrupt. | ||
| 165 | }); | ||
| 166 | |||
| 167 | regs.cr().write(|cr| { | ||
| 168 | cr.set_spdifen(0x00); // Disable SPDIF receiver synchronization. | ||
| 169 | cr.set_rxdmaen(true); // Use RX DMA for data. Enabled on `read`. | ||
| 170 | cr.set_cbdmaen(false); // Do not capture channel info. | ||
| 171 | cr.set_rxsteo(true); // Operate in stereo mode. | ||
| 172 | cr.set_drfmt(0x01); // Data is left-aligned (MSB). | ||
| 173 | |||
| 174 | // Disable all status fields in the data register. | ||
| 175 | // Status can be obtained directly with the status register DMA. | ||
| 176 | cr.set_pmsk(false); // Write parity bit to the data register. FIXME: Add parity check. | ||
| 177 | cr.set_vmsk(false); // Write validity to the data register. | ||
| 178 | cr.set_cumsk(true); // Do not write C and U bits to the data register. | ||
| 179 | cr.set_ptmsk(false); // Write preamble bits to the data register. | ||
| 180 | |||
| 181 | cr.set_chsel(match config.control_channel_selection { | ||
| 182 | ControlChannelSelection::A => false, | ||
| 183 | ControlChannelSelection::B => true, | ||
| 184 | }); // Select channel status source. | ||
| 185 | |||
| 186 | cr.set_nbtr(0x02); // 16 attempts are allowed. | ||
| 187 | cr.set_wfa(true); // Wait for activity before going to synchronization phase. | ||
| 188 | cr.set_insel(input_sel); // Input pin selection. | ||
| 189 | |||
| 190 | #[cfg(stm32h7)] | ||
| 191 | cr.set_cksen(true); // Generate a symbol clock. | ||
| 192 | |||
| 193 | #[cfg(stm32h7)] | ||
| 194 | cr.set_cksbkpen(true); // Generate a backup symbol clock. | ||
| 195 | }); | ||
| 196 | } | ||
| 197 | |||
| 198 | /// Start the SPDIFRX driver. | ||
| 199 | pub fn start(&mut self) { | ||
| 200 | self.data_ring_buffer.start(); | ||
| 201 | |||
| 202 | T::info().regs.cr().modify(|cr| { | ||
| 203 | cr.set_spdifen(0x03); // Enable S/PDIF receiver. | ||
| 204 | }); | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Read from the SPDIFRX data ring buffer. | ||
| 208 | /// | ||
| 209 | /// SPDIFRX is always receiving data in the background. This function pops already-received | ||
| 210 | /// data from the buffer. | ||
| 211 | /// | ||
| 212 | /// If there's less than `data.len()` data in the buffer, this waits until there is. | ||
| 213 | pub async fn read(&mut self, data: &mut [u32]) -> Result<(), Error> { | ||
| 214 | self.data_ring_buffer.read_exact(data).await?; | ||
| 215 | |||
| 216 | let first_preamble = (data[0] >> 4) & 0b11_u32; | ||
| 217 | if (first_preamble as u8) == (PreambleType::W as u8) { | ||
| 218 | trace!("S/PDIF left/right mismatch"); | ||
| 219 | |||
| 220 | // Resynchronize until the first sample is for the left channel. | ||
| 221 | self.data_ring_buffer.clear(); | ||
| 222 | return Err(Error::ChannelSyncError); | ||
| 223 | }; | ||
| 224 | |||
| 225 | for sample in data.as_mut() { | ||
| 226 | if (*sample & (0x0002_u32)) != 0 { | ||
| 227 | // Discard invalid samples, setting them to mute level. | ||
| 228 | *sample = 0; | ||
| 229 | } else { | ||
| 230 | // Discard status information in the lowest byte. | ||
| 231 | *sample &= 0xFFFFFF00; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | Ok(()) | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | #[cfg(not(gpdma))] | ||
| 240 | impl<'d, T: Instance> Drop for Spdifrx<'d, T> { | ||
| 241 | fn drop(&mut self) { | ||
| 242 | T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00)); | ||
| 243 | self.spdifrx_in.as_ref().map(|x| x.set_as_disconnected()); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | struct State { | ||
| 248 | #[allow(unused)] | ||
| 249 | waker: AtomicWaker, | ||
| 250 | } | ||
| 251 | |||
| 252 | impl State { | ||
| 253 | const fn new() -> Self { | ||
| 254 | Self { | ||
| 255 | waker: AtomicWaker::new(), | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | struct Info { | ||
| 261 | regs: crate::pac::spdifrx::Spdifrx, | ||
| 262 | rcc: RccInfo, | ||
| 263 | } | ||
| 264 | |||
| 265 | peri_trait!( | ||
| 266 | irqs: [GlobalInterrupt], | ||
| 267 | ); | ||
| 268 | |||
| 269 | /// SPIDFRX pin trait | ||
| 270 | pub trait InPin<T: Instance>: crate::gpio::Pin { | ||
| 271 | /// Get the GPIO AF number needed to use this pin. | ||
| 272 | fn af_num(&self) -> u8; | ||
| 273 | /// Get the SPIDFRX INSEL number needed to use this pin. | ||
| 274 | fn input_sel(&self) -> u8; | ||
| 275 | } | ||
| 276 | |||
| 277 | dma_trait!(Dma, Instance); | ||
| 278 | |||
| 279 | /// Global interrupt handler. | ||
| 280 | pub struct GlobalInterruptHandler<T: Instance> { | ||
| 281 | _phantom: PhantomData<T>, | ||
| 282 | } | ||
| 283 | |||
| 284 | impl<T: Instance> interrupt::typelevel::Handler<T::GlobalInterrupt> for GlobalInterruptHandler<T> { | ||
| 285 | unsafe fn on_interrupt() { | ||
| 286 | T::state().waker.wake(); | ||
| 287 | |||
| 288 | let regs = T::info().regs; | ||
| 289 | let sr = regs.sr().read(); | ||
| 290 | |||
| 291 | if sr.serr() || sr.terr() || sr.ferr() { | ||
| 292 | trace!("SPDIFRX error, resync"); | ||
| 293 | |||
| 294 | // Clear errors by disabling SPDIFRX, then reenable. | ||
| 295 | regs.cr().modify(|cr| cr.set_spdifen(0x00)); | ||
| 296 | regs.cr().modify(|cr| cr.set_spdifen(0x03)); | ||
| 297 | } else if sr.syncd() { | ||
| 298 | // Synchronization was successful. | ||
| 299 | trace!("SPDIFRX sync success"); | ||
| 300 | } | ||
| 301 | |||
| 302 | // Clear interrupt flags. | ||
| 303 | regs.ifcr().write(|ifcr| { | ||
| 304 | ifcr.set_perrcf(true); // Clears parity error flag. | ||
| 305 | ifcr.set_ovrcf(true); // Clears overrun error flag. | ||
| 306 | ifcr.set_sbdcf(true); // Clears synchronization block detected flag. | ||
| 307 | ifcr.set_syncdcf(true); // Clears SYNCD from SR (was read above). | ||
| 308 | }); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | foreach_peripheral!( | ||
| 313 | (spdifrx, $inst:ident) => { | ||
| 314 | #[allow(private_interfaces)] | ||
| 315 | impl SealedInstance for peripherals::$inst { | ||
| 316 | fn info() -> &'static Info { | ||
| 317 | static INFO: Info = Info{ | ||
| 318 | regs: crate::pac::$inst, | ||
| 319 | rcc: crate::peripherals::$inst::RCC_INFO, | ||
| 320 | }; | ||
| 321 | &INFO | ||
| 322 | } | ||
| 323 | fn state() -> &'static State { | ||
| 324 | static STATE: State = State::new(); | ||
| 325 | &STATE | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | impl Instance for peripherals::$inst { | ||
| 330 | type GlobalInterrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; | ||
| 331 | } | ||
| 332 | }; | ||
| 333 | ); | ||
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 20718147a..9e2ba093a 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -6,7 +6,6 @@ use core::ptr; | |||
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | use embassy_hal_internal::PeripheralRef; | ||
| 10 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 9 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; |
| 11 | 10 | ||
| 12 | use crate::dma::{word, ChannelAndRequest}; | 11 | use crate::dma::{word, ChannelAndRequest}; |
| @@ -15,7 +14,7 @@ use crate::mode::{Async, Blocking, Mode as PeriMode}; | |||
| 15 | use crate::pac::spi::{regs, vals, Spi as Regs}; | 14 | use crate::pac::spi::{regs, vals, Spi as Regs}; |
| 16 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 17 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 18 | use crate::Peripheral; | 17 | use crate::Peri; |
| 19 | 18 | ||
| 20 | /// SPI error. | 19 | /// SPI error. |
| 21 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| @@ -31,6 +30,21 @@ pub enum Error { | |||
| 31 | Overrun, | 30 | Overrun, |
| 32 | } | 31 | } |
| 33 | 32 | ||
| 33 | impl core::fmt::Display for Error { | ||
| 34 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 35 | let message = match self { | ||
| 36 | Self::Framing => "Invalid Framing", | ||
| 37 | Self::Crc => "Hardware CRC Check Failed", | ||
| 38 | Self::ModeFault => "Mode Fault", | ||
| 39 | Self::Overrun => "Buffer Overrun", | ||
| 40 | }; | ||
| 41 | |||
| 42 | write!(f, "{}", message) | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | impl core::error::Error for Error {} | ||
| 47 | |||
| 34 | /// SPI bit order | 48 | /// SPI bit order |
| 35 | #[derive(Copy, Clone)] | 49 | #[derive(Copy, Clone)] |
| 36 | pub enum BitOrder { | 50 | pub enum BitOrder { |
| @@ -55,6 +69,9 @@ pub struct Config { | |||
| 55 | /// There are some ICs that require a pull-up on the MISO pin for some applications. | 69 | /// There are some ICs that require a pull-up on the MISO pin for some applications. |
| 56 | /// If you are unsure, you probably don't need this. | 70 | /// If you are unsure, you probably don't need this. |
| 57 | pub miso_pull: Pull, | 71 | pub miso_pull: Pull, |
| 72 | /// signal rise/fall speed (slew rate) - defaults to `Medium`. | ||
| 73 | /// Increase for high SPI speeds. Change to `Low` to reduce ringing. | ||
| 74 | pub rise_fall_speed: Speed, | ||
| 58 | } | 75 | } |
| 59 | 76 | ||
| 60 | impl Default for Config { | 77 | impl Default for Config { |
| @@ -64,6 +81,7 @@ impl Default for Config { | |||
| 64 | bit_order: BitOrder::MsbFirst, | 81 | bit_order: BitOrder::MsbFirst, |
| 65 | frequency: Hertz(1_000_000), | 82 | frequency: Hertz(1_000_000), |
| 66 | miso_pull: Pull::None, | 83 | miso_pull: Pull::None, |
| 84 | rise_fall_speed: Speed::VeryHigh, | ||
| 67 | } | 85 | } |
| 68 | } | 86 | } |
| 69 | } | 87 | } |
| @@ -71,15 +89,15 @@ impl Default for Config { | |||
| 71 | impl Config { | 89 | impl Config { |
| 72 | fn raw_phase(&self) -> vals::Cpha { | 90 | fn raw_phase(&self) -> vals::Cpha { |
| 73 | match self.mode.phase { | 91 | match self.mode.phase { |
| 74 | Phase::CaptureOnSecondTransition => vals::Cpha::SECONDEDGE, | 92 | Phase::CaptureOnSecondTransition => vals::Cpha::SECOND_EDGE, |
| 75 | Phase::CaptureOnFirstTransition => vals::Cpha::FIRSTEDGE, | 93 | Phase::CaptureOnFirstTransition => vals::Cpha::FIRST_EDGE, |
| 76 | } | 94 | } |
| 77 | } | 95 | } |
| 78 | 96 | ||
| 79 | fn raw_polarity(&self) -> vals::Cpol { | 97 | fn raw_polarity(&self) -> vals::Cpol { |
| 80 | match self.mode.polarity { | 98 | match self.mode.polarity { |
| 81 | Polarity::IdleHigh => vals::Cpol::IDLEHIGH, | 99 | Polarity::IdleHigh => vals::Cpol::IDLE_HIGH, |
| 82 | Polarity::IdleLow => vals::Cpol::IDLELOW, | 100 | Polarity::IdleLow => vals::Cpol::IDLE_LOW, |
| 83 | } | 101 | } |
| 84 | } | 102 | } |
| 85 | 103 | ||
| @@ -92,14 +110,14 @@ impl Config { | |||
| 92 | 110 | ||
| 93 | #[cfg(gpio_v1)] | 111 | #[cfg(gpio_v1)] |
| 94 | fn sck_af(&self) -> AfType { | 112 | fn sck_af(&self) -> AfType { |
| 95 | AfType::output(OutputType::PushPull, Speed::VeryHigh) | 113 | AfType::output(OutputType::PushPull, self.rise_fall_speed) |
| 96 | } | 114 | } |
| 97 | 115 | ||
| 98 | #[cfg(gpio_v2)] | 116 | #[cfg(gpio_v2)] |
| 99 | fn sck_af(&self) -> AfType { | 117 | fn sck_af(&self) -> AfType { |
| 100 | AfType::output_pull( | 118 | AfType::output_pull( |
| 101 | OutputType::PushPull, | 119 | OutputType::PushPull, |
| 102 | Speed::VeryHigh, | 120 | self.rise_fall_speed, |
| 103 | match self.mode.polarity { | 121 | match self.mode.polarity { |
| 104 | Polarity::IdleLow => Pull::Down, | 122 | Polarity::IdleLow => Pull::Down, |
| 105 | Polarity::IdleHigh => Pull::Up, | 123 | Polarity::IdleHigh => Pull::Up, |
| @@ -111,21 +129,22 @@ impl Config { | |||
| 111 | pub struct Spi<'d, M: PeriMode> { | 129 | pub struct Spi<'d, M: PeriMode> { |
| 112 | pub(crate) info: &'static Info, | 130 | pub(crate) info: &'static Info, |
| 113 | kernel_clock: Hertz, | 131 | kernel_clock: Hertz, |
| 114 | sck: Option<PeripheralRef<'d, AnyPin>>, | 132 | sck: Option<Peri<'d, AnyPin>>, |
| 115 | mosi: Option<PeripheralRef<'d, AnyPin>>, | 133 | mosi: Option<Peri<'d, AnyPin>>, |
| 116 | miso: Option<PeripheralRef<'d, AnyPin>>, | 134 | miso: Option<Peri<'d, AnyPin>>, |
| 117 | tx_dma: Option<ChannelAndRequest<'d>>, | 135 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 118 | rx_dma: Option<ChannelAndRequest<'d>>, | 136 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 119 | _phantom: PhantomData<M>, | 137 | _phantom: PhantomData<M>, |
| 120 | current_word_size: word_impl::Config, | 138 | current_word_size: word_impl::Config, |
| 139 | rise_fall_speed: Speed, | ||
| 121 | } | 140 | } |
| 122 | 141 | ||
| 123 | impl<'d, M: PeriMode> Spi<'d, M> { | 142 | impl<'d, M: PeriMode> Spi<'d, M> { |
| 124 | fn new_inner<T: Instance>( | 143 | fn new_inner<T: Instance>( |
| 125 | _peri: impl Peripheral<P = T> + 'd, | 144 | _peri: Peri<'d, T>, |
| 126 | sck: Option<PeripheralRef<'d, AnyPin>>, | 145 | sck: Option<Peri<'d, AnyPin>>, |
| 127 | mosi: Option<PeripheralRef<'d, AnyPin>>, | 146 | mosi: Option<Peri<'d, AnyPin>>, |
| 128 | miso: Option<PeripheralRef<'d, AnyPin>>, | 147 | miso: Option<Peri<'d, AnyPin>>, |
| 129 | tx_dma: Option<ChannelAndRequest<'d>>, | 148 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 130 | rx_dma: Option<ChannelAndRequest<'d>>, | 149 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 131 | config: Config, | 150 | config: Config, |
| @@ -140,6 +159,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 140 | rx_dma, | 159 | rx_dma, |
| 141 | current_word_size: <u8 as SealedWord>::CONFIG, | 160 | current_word_size: <u8 as SealedWord>::CONFIG, |
| 142 | _phantom: PhantomData, | 161 | _phantom: PhantomData, |
| 162 | rise_fall_speed: config.rise_fall_speed, | ||
| 143 | }; | 163 | }; |
| 144 | this.enable_and_init(config); | 164 | this.enable_and_init(config); |
| 145 | this | 165 | this |
| @@ -174,7 +194,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 174 | // we're doing "fake rxonly", by actually writing one | 194 | // we're doing "fake rxonly", by actually writing one |
| 175 | // byte to TXDR for each byte we want to receive. if we | 195 | // byte to TXDR for each byte we want to receive. if we |
| 176 | // set OUTPUTDISABLED here, this hangs. | 196 | // set OUTPUTDISABLED here, this hangs. |
| 177 | w.set_rxonly(vals::Rxonly::FULLDUPLEX); | 197 | w.set_rxonly(vals::Rxonly::FULL_DUPLEX); |
| 178 | w.set_dff(<u8 as SealedWord>::CONFIG) | 198 | w.set_dff(<u8 as SealedWord>::CONFIG) |
| 179 | }); | 199 | }); |
| 180 | } | 200 | } |
| @@ -211,18 +231,18 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 211 | w.set_lsbfirst(lsbfirst); | 231 | w.set_lsbfirst(lsbfirst); |
| 212 | w.set_ssm(true); | 232 | w.set_ssm(true); |
| 213 | w.set_master(vals::Master::MASTER); | 233 | w.set_master(vals::Master::MASTER); |
| 214 | w.set_comm(vals::Comm::FULLDUPLEX); | 234 | w.set_comm(vals::Comm::FULL_DUPLEX); |
| 215 | w.set_ssom(vals::Ssom::ASSERTED); | 235 | w.set_ssom(vals::Ssom::ASSERTED); |
| 216 | w.set_midi(0); | 236 | w.set_midi(0); |
| 217 | w.set_mssi(0); | 237 | w.set_mssi(0); |
| 218 | w.set_afcntr(true); | 238 | w.set_afcntr(true); |
| 219 | w.set_ssiop(vals::Ssiop::ACTIVEHIGH); | 239 | w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); |
| 220 | }); | 240 | }); |
| 221 | regs.cfg1().modify(|w| { | 241 | regs.cfg1().modify(|w| { |
| 222 | w.set_crcen(false); | 242 | w.set_crcen(false); |
| 223 | w.set_mbr(br); | 243 | w.set_mbr(br); |
| 224 | w.set_dsize(<u8 as SealedWord>::CONFIG); | 244 | w.set_dsize(<u8 as SealedWord>::CONFIG); |
| 225 | w.set_fthlv(vals::Fthlv::ONEFRAME); | 245 | w.set_fthlv(vals::Fthlv::ONE_FRAME); |
| 226 | }); | 246 | }); |
| 227 | regs.cr2().modify(|w| { | 247 | regs.cr2().modify(|w| { |
| 228 | w.set_tsize(0); | 248 | w.set_tsize(0); |
| @@ -243,6 +263,17 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 243 | 263 | ||
| 244 | let br = compute_baud_rate(self.kernel_clock, config.frequency); | 264 | let br = compute_baud_rate(self.kernel_clock, config.frequency); |
| 245 | 265 | ||
| 266 | #[cfg(gpio_v2)] | ||
| 267 | { | ||
| 268 | self.rise_fall_speed = config.rise_fall_speed; | ||
| 269 | if let Some(sck) = self.sck.as_ref() { | ||
| 270 | sck.set_speed(config.rise_fall_speed); | ||
| 271 | } | ||
| 272 | if let Some(mosi) = self.mosi.as_ref() { | ||
| 273 | mosi.set_speed(config.rise_fall_speed); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 246 | #[cfg(any(spi_v1, spi_f1, spi_v2))] | 277 | #[cfg(any(spi_v1, spi_f1, spi_v2))] |
| 247 | self.info.regs.cr1().modify(|w| { | 278 | self.info.regs.cr1().modify(|w| { |
| 248 | w.set_cpha(cpha); | 279 | w.set_cpha(cpha); |
| @@ -253,6 +284,10 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 253 | 284 | ||
| 254 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 285 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 255 | { | 286 | { |
| 287 | self.info.regs.cr1().modify(|w| { | ||
| 288 | w.set_spe(false); | ||
| 289 | }); | ||
| 290 | |||
| 256 | self.info.regs.cfg2().modify(|w| { | 291 | self.info.regs.cfg2().modify(|w| { |
| 257 | w.set_cpha(cpha); | 292 | w.set_cpha(cpha); |
| 258 | w.set_cpol(cpol); | 293 | w.set_cpol(cpol); |
| @@ -261,6 +296,10 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 261 | self.info.regs.cfg1().modify(|w| { | 296 | self.info.regs.cfg1().modify(|w| { |
| 262 | w.set_mbr(br); | 297 | w.set_mbr(br); |
| 263 | }); | 298 | }); |
| 299 | |||
| 300 | self.info.regs.cr1().modify(|w| { | ||
| 301 | w.set_spe(true); | ||
| 302 | }); | ||
| 264 | } | 303 | } |
| 265 | Ok(()) | 304 | Ok(()) |
| 266 | } | 305 | } |
| @@ -274,12 +313,12 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 274 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 313 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 275 | let cfg1 = self.info.regs.cfg1().read(); | 314 | let cfg1 = self.info.regs.cfg1().read(); |
| 276 | 315 | ||
| 277 | let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { | 316 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { |
| 278 | Polarity::IdleLow | 317 | Polarity::IdleLow |
| 279 | } else { | 318 | } else { |
| 280 | Polarity::IdleHigh | 319 | Polarity::IdleHigh |
| 281 | }; | 320 | }; |
| 282 | let phase = if cfg.cpha() == vals::Cpha::FIRSTEDGE { | 321 | let phase = if cfg.cpha() == vals::Cpha::FIRST_EDGE { |
| 283 | Phase::CaptureOnFirstTransition | 322 | Phase::CaptureOnFirstTransition |
| 284 | } else { | 323 | } else { |
| 285 | Phase::CaptureOnSecondTransition | 324 | Phase::CaptureOnSecondTransition |
| @@ -308,54 +347,32 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 308 | bit_order, | 347 | bit_order, |
| 309 | frequency, | 348 | frequency, |
| 310 | miso_pull, | 349 | miso_pull, |
| 350 | rise_fall_speed: self.rise_fall_speed, | ||
| 311 | } | 351 | } |
| 312 | } | 352 | } |
| 313 | 353 | ||
| 314 | fn set_word_size(&mut self, word_size: word_impl::Config) { | 354 | pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) { |
| 315 | if self.current_word_size == word_size { | 355 | if self.current_word_size == word_size { |
| 316 | return; | 356 | return; |
| 317 | } | 357 | } |
| 318 | 358 | ||
| 359 | self.info.regs.cr1().modify(|w| { | ||
| 360 | w.set_spe(false); | ||
| 361 | }); | ||
| 362 | |||
| 319 | #[cfg(any(spi_v1, spi_f1))] | 363 | #[cfg(any(spi_v1, spi_f1))] |
| 320 | { | 364 | self.info.regs.cr1().modify(|reg| { |
| 321 | self.info.regs.cr1().modify(|reg| { | 365 | reg.set_dff(word_size); |
| 322 | reg.set_spe(false); | 366 | }); |
| 323 | reg.set_dff(word_size) | ||
| 324 | }); | ||
| 325 | self.info.regs.cr1().modify(|reg| { | ||
| 326 | reg.set_spe(true); | ||
| 327 | }); | ||
| 328 | } | ||
| 329 | #[cfg(spi_v2)] | 367 | #[cfg(spi_v2)] |
| 330 | { | 368 | self.info.regs.cr2().modify(|w| { |
| 331 | self.info.regs.cr1().modify(|w| { | 369 | w.set_frxth(word_size.1); |
| 332 | w.set_spe(false); | 370 | w.set_ds(word_size.0); |
| 333 | }); | 371 | }); |
| 334 | self.info.regs.cr2().modify(|w| { | ||
| 335 | w.set_frxth(word_size.1); | ||
| 336 | w.set_ds(word_size.0); | ||
| 337 | }); | ||
| 338 | self.info.regs.cr1().modify(|w| { | ||
| 339 | w.set_spe(true); | ||
| 340 | }); | ||
| 341 | } | ||
| 342 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 372 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 343 | { | 373 | self.info.regs.cfg1().modify(|w| { |
| 344 | self.info.regs.cr1().modify(|w| { | 374 | w.set_dsize(word_size); |
| 345 | w.set_csusp(true); | 375 | }); |
| 346 | }); | ||
| 347 | while self.info.regs.sr().read().eot() {} | ||
| 348 | self.info.regs.cr1().modify(|w| { | ||
| 349 | w.set_spe(false); | ||
| 350 | }); | ||
| 351 | self.info.regs.cfg1().modify(|w| { | ||
| 352 | w.set_dsize(word_size); | ||
| 353 | }); | ||
| 354 | self.info.regs.cr1().modify(|w| { | ||
| 355 | w.set_csusp(false); | ||
| 356 | w.set_spe(true); | ||
| 357 | }); | ||
| 358 | } | ||
| 359 | 376 | ||
| 360 | self.current_word_size = word_size; | 377 | self.current_word_size = word_size; |
| 361 | } | 378 | } |
| @@ -365,9 +382,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 365 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 382 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 366 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 383 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 367 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 384 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 385 | self.set_word_size(W::CONFIG); | ||
| 368 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 386 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 369 | flush_rx_fifo(self.info.regs); | 387 | flush_rx_fifo(self.info.regs); |
| 370 | self.set_word_size(W::CONFIG); | ||
| 371 | for word in words.iter() { | 388 | for word in words.iter() { |
| 372 | // this cannot use `transfer_word` because on SPIv2 and higher, | 389 | // this cannot use `transfer_word` because on SPIv2 and higher, |
| 373 | // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. | 390 | // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. |
| @@ -402,9 +419,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 402 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 419 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 403 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 420 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 404 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 421 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 422 | self.set_word_size(W::CONFIG); | ||
| 405 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 423 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 406 | flush_rx_fifo(self.info.regs); | 424 | flush_rx_fifo(self.info.regs); |
| 407 | self.set_word_size(W::CONFIG); | ||
| 408 | for word in words.iter_mut() { | 425 | for word in words.iter_mut() { |
| 409 | *word = transfer_word(self.info.regs, W::default())?; | 426 | *word = transfer_word(self.info.regs, W::default())?; |
| 410 | } | 427 | } |
| @@ -418,9 +435,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 418 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 435 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 419 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 436 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 420 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 437 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 438 | self.set_word_size(W::CONFIG); | ||
| 421 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 439 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 422 | flush_rx_fifo(self.info.regs); | 440 | flush_rx_fifo(self.info.regs); |
| 423 | self.set_word_size(W::CONFIG); | ||
| 424 | for word in words.iter_mut() { | 441 | for word in words.iter_mut() { |
| 425 | *word = transfer_word(self.info.regs, *word)?; | 442 | *word = transfer_word(self.info.regs, *word)?; |
| 426 | } | 443 | } |
| @@ -437,9 +454,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 437 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? | 454 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
| 438 | #[cfg(any(spi_v3, spi_v4, spi_v5))] | 455 | #[cfg(any(spi_v3, spi_v4, spi_v5))] |
| 439 | self.info.regs.cr1().modify(|w| w.set_spe(false)); | 456 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
| 457 | self.set_word_size(W::CONFIG); | ||
| 440 | self.info.regs.cr1().modify(|w| w.set_spe(true)); | 458 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
| 441 | flush_rx_fifo(self.info.regs); | 459 | flush_rx_fifo(self.info.regs); |
| 442 | self.set_word_size(W::CONFIG); | ||
| 443 | let len = read.len().max(write.len()); | 460 | let len = read.len().max(write.len()); |
| 444 | for i in 0..len { | 461 | for i in 0..len { |
| 445 | let wb = write.get(i).copied().unwrap_or_default(); | 462 | let wb = write.get(i).copied().unwrap_or_default(); |
| @@ -455,16 +472,16 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 455 | impl<'d> Spi<'d, Blocking> { | 472 | impl<'d> Spi<'d, Blocking> { |
| 456 | /// Create a new blocking SPI driver. | 473 | /// Create a new blocking SPI driver. |
| 457 | pub fn new_blocking<T: Instance>( | 474 | pub fn new_blocking<T: Instance>( |
| 458 | peri: impl Peripheral<P = T> + 'd, | 475 | peri: Peri<'d, T>, |
| 459 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 476 | sck: Peri<'d, impl SckPin<T>>, |
| 460 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | 477 | mosi: Peri<'d, impl MosiPin<T>>, |
| 461 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, | 478 | miso: Peri<'d, impl MisoPin<T>>, |
| 462 | config: Config, | 479 | config: Config, |
| 463 | ) -> Self { | 480 | ) -> Self { |
| 464 | Self::new_inner( | 481 | Self::new_inner( |
| 465 | peri, | 482 | peri, |
| 466 | new_pin!(sck, config.sck_af()), | 483 | new_pin!(sck, config.sck_af()), |
| 467 | new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 484 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
| 468 | new_pin!(miso, AfType::input(config.miso_pull)), | 485 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 469 | None, | 486 | None, |
| 470 | None, | 487 | None, |
| @@ -474,9 +491,9 @@ impl<'d> Spi<'d, Blocking> { | |||
| 474 | 491 | ||
| 475 | /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). | 492 | /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). |
| 476 | pub fn new_blocking_rxonly<T: Instance>( | 493 | pub fn new_blocking_rxonly<T: Instance>( |
| 477 | peri: impl Peripheral<P = T> + 'd, | 494 | peri: Peri<'d, T>, |
| 478 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 495 | sck: Peri<'d, impl SckPin<T>>, |
| 479 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, | 496 | miso: Peri<'d, impl MisoPin<T>>, |
| 480 | config: Config, | 497 | config: Config, |
| 481 | ) -> Self { | 498 | ) -> Self { |
| 482 | Self::new_inner( | 499 | Self::new_inner( |
| @@ -492,15 +509,15 @@ impl<'d> Spi<'d, Blocking> { | |||
| 492 | 509 | ||
| 493 | /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). | 510 | /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). |
| 494 | pub fn new_blocking_txonly<T: Instance>( | 511 | pub fn new_blocking_txonly<T: Instance>( |
| 495 | peri: impl Peripheral<P = T> + 'd, | 512 | peri: Peri<'d, T>, |
| 496 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 513 | sck: Peri<'d, impl SckPin<T>>, |
| 497 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | 514 | mosi: Peri<'d, impl MosiPin<T>>, |
| 498 | config: Config, | 515 | config: Config, |
| 499 | ) -> Self { | 516 | ) -> Self { |
| 500 | Self::new_inner( | 517 | Self::new_inner( |
| 501 | peri, | 518 | peri, |
| 502 | new_pin!(sck, config.sck_af()), | 519 | new_pin!(sck, config.sck_af()), |
| 503 | new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 520 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
| 504 | None, | 521 | None, |
| 505 | None, | 522 | None, |
| 506 | None, | 523 | None, |
| @@ -512,14 +529,14 @@ impl<'d> Spi<'d, Blocking> { | |||
| 512 | /// | 529 | /// |
| 513 | /// This can be useful for bit-banging non-SPI protocols. | 530 | /// This can be useful for bit-banging non-SPI protocols. |
| 514 | pub fn new_blocking_txonly_nosck<T: Instance>( | 531 | pub fn new_blocking_txonly_nosck<T: Instance>( |
| 515 | peri: impl Peripheral<P = T> + 'd, | 532 | peri: Peri<'d, T>, |
| 516 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | 533 | mosi: Peri<'d, impl MosiPin<T>>, |
| 517 | config: Config, | 534 | config: Config, |
| 518 | ) -> Self { | 535 | ) -> Self { |
| 519 | Self::new_inner( | 536 | Self::new_inner( |
| 520 | peri, | 537 | peri, |
| 521 | None, | 538 | None, |
| 522 | new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 539 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
| 523 | None, | 540 | None, |
| 524 | None, | 541 | None, |
| 525 | None, | 542 | None, |
| @@ -531,18 +548,18 @@ impl<'d> Spi<'d, Blocking> { | |||
| 531 | impl<'d> Spi<'d, Async> { | 548 | impl<'d> Spi<'d, Async> { |
| 532 | /// Create a new SPI driver. | 549 | /// Create a new SPI driver. |
| 533 | pub fn new<T: Instance>( | 550 | pub fn new<T: Instance>( |
| 534 | peri: impl Peripheral<P = T> + 'd, | 551 | peri: Peri<'d, T>, |
| 535 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 552 | sck: Peri<'d, impl SckPin<T>>, |
| 536 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | 553 | mosi: Peri<'d, impl MosiPin<T>>, |
| 537 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, | 554 | miso: Peri<'d, impl MisoPin<T>>, |
| 538 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 555 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 539 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 556 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 540 | config: Config, | 557 | config: Config, |
| 541 | ) -> Self { | 558 | ) -> Self { |
| 542 | Self::new_inner( | 559 | Self::new_inner( |
| 543 | peri, | 560 | peri, |
| 544 | new_pin!(sck, config.sck_af()), | 561 | new_pin!(sck, config.sck_af()), |
| 545 | new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 562 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
| 546 | new_pin!(miso, AfType::input(config.miso_pull)), | 563 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 547 | new_dma!(tx_dma), | 564 | new_dma!(tx_dma), |
| 548 | new_dma!(rx_dma), | 565 | new_dma!(rx_dma), |
| @@ -552,11 +569,11 @@ impl<'d> Spi<'d, Async> { | |||
| 552 | 569 | ||
| 553 | /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). | 570 | /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). |
| 554 | pub fn new_rxonly<T: Instance>( | 571 | pub fn new_rxonly<T: Instance>( |
| 555 | peri: impl Peripheral<P = T> + 'd, | 572 | peri: Peri<'d, T>, |
| 556 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 573 | sck: Peri<'d, impl SckPin<T>>, |
| 557 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, | 574 | miso: Peri<'d, impl MisoPin<T>>, |
| 558 | #[cfg(any(spi_v1, spi_f1, spi_v2))] tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 575 | #[cfg(any(spi_v1, spi_f1, spi_v2))] tx_dma: Peri<'d, impl TxDma<T>>, |
| 559 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 576 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 560 | config: Config, | 577 | config: Config, |
| 561 | ) -> Self { | 578 | ) -> Self { |
| 562 | Self::new_inner( | 579 | Self::new_inner( |
| @@ -575,16 +592,16 @@ impl<'d> Spi<'d, Async> { | |||
| 575 | 592 | ||
| 576 | /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). | 593 | /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). |
| 577 | pub fn new_txonly<T: Instance>( | 594 | pub fn new_txonly<T: Instance>( |
| 578 | peri: impl Peripheral<P = T> + 'd, | 595 | peri: Peri<'d, T>, |
| 579 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, | 596 | sck: Peri<'d, impl SckPin<T>>, |
| 580 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | 597 | mosi: Peri<'d, impl MosiPin<T>>, |
| 581 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 598 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 582 | config: Config, | 599 | config: Config, |
| 583 | ) -> Self { | 600 | ) -> Self { |
| 584 | Self::new_inner( | 601 | Self::new_inner( |
| 585 | peri, | 602 | peri, |
| 586 | new_pin!(sck, config.sck_af()), | 603 | new_pin!(sck, config.sck_af()), |
| 587 | new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 604 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
| 588 | None, | 605 | None, |
| 589 | new_dma!(tx_dma), | 606 | new_dma!(tx_dma), |
| 590 | None, | 607 | None, |
| @@ -596,15 +613,15 @@ impl<'d> Spi<'d, Async> { | |||
| 596 | /// | 613 | /// |
| 597 | /// This can be useful for bit-banging non-SPI protocols. | 614 | /// This can be useful for bit-banging non-SPI protocols. |
| 598 | pub fn new_txonly_nosck<T: Instance>( | 615 | pub fn new_txonly_nosck<T: Instance>( |
| 599 | peri: impl Peripheral<P = T> + 'd, | 616 | peri: Peri<'d, T>, |
| 600 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, | 617 | mosi: Peri<'d, impl MosiPin<T>>, |
| 601 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 618 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 602 | config: Config, | 619 | config: Config, |
| 603 | ) -> Self { | 620 | ) -> Self { |
| 604 | Self::new_inner( | 621 | Self::new_inner( |
| 605 | peri, | 622 | peri, |
| 606 | None, | 623 | None, |
| 607 | new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | 624 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
| 608 | None, | 625 | None, |
| 609 | new_dma!(tx_dma), | 626 | new_dma!(tx_dma), |
| 610 | None, | 627 | None, |
| @@ -615,9 +632,9 @@ impl<'d> Spi<'d, Async> { | |||
| 615 | #[cfg(stm32wl)] | 632 | #[cfg(stm32wl)] |
| 616 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. | 633 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. |
| 617 | pub fn new_subghz<T: Instance>( | 634 | pub fn new_subghz<T: Instance>( |
| 618 | peri: impl Peripheral<P = T> + 'd, | 635 | peri: Peri<'d, T>, |
| 619 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 636 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 620 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 637 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 621 | ) -> Self { | 638 | ) -> Self { |
| 622 | // see RM0453 rev 1 section 7.2.13 page 291 | 639 | // see RM0453 rev 1 section 7.2.13 page 291 |
| 623 | // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. | 640 | // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. |
| @@ -634,7 +651,7 @@ impl<'d> Spi<'d, Async> { | |||
| 634 | 651 | ||
| 635 | #[allow(dead_code)] | 652 | #[allow(dead_code)] |
| 636 | pub(crate) fn new_internal<T: Instance>( | 653 | pub(crate) fn new_internal<T: Instance>( |
| 637 | peri: impl Peripheral<P = T> + 'd, | 654 | peri: Peri<'d, T>, |
| 638 | tx_dma: Option<ChannelAndRequest<'d>>, | 655 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 639 | rx_dma: Option<ChannelAndRequest<'d>>, | 656 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 640 | config: Config, | 657 | config: Config, |
| @@ -648,10 +665,10 @@ impl<'d> Spi<'d, Async> { | |||
| 648 | return Ok(()); | 665 | return Ok(()); |
| 649 | } | 666 | } |
| 650 | 667 | ||
| 651 | self.set_word_size(W::CONFIG); | ||
| 652 | self.info.regs.cr1().modify(|w| { | 668 | self.info.regs.cr1().modify(|w| { |
| 653 | w.set_spe(false); | 669 | w.set_spe(false); |
| 654 | }); | 670 | }); |
| 671 | self.set_word_size(W::CONFIG); | ||
| 655 | 672 | ||
| 656 | let tx_dst = self.info.regs.tx_ptr(); | 673 | let tx_dst = self.info.regs.tx_ptr(); |
| 657 | let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; | 674 | let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; |
| @@ -685,6 +702,8 @@ impl<'d> Spi<'d, Async> { | |||
| 685 | w.set_spe(false); | 702 | w.set_spe(false); |
| 686 | }); | 703 | }); |
| 687 | 704 | ||
| 705 | self.set_word_size(W::CONFIG); | ||
| 706 | |||
| 688 | let comm = regs.cfg2().modify(|w| { | 707 | let comm = regs.cfg2().modify(|w| { |
| 689 | let prev = w.comm(); | 708 | let prev = w.comm(); |
| 690 | w.set_comm(vals::Comm::RECEIVER); | 709 | w.set_comm(vals::Comm::RECEIVER); |
| @@ -696,8 +715,8 @@ impl<'d> Spi<'d, Async> { | |||
| 696 | w.i2smod().then(|| { | 715 | w.i2smod().then(|| { |
| 697 | let prev = w.i2scfg(); | 716 | let prev = w.i2scfg(); |
| 698 | w.set_i2scfg(match prev { | 717 | w.set_i2scfg(match prev { |
| 699 | vals::I2scfg::SLAVERX | vals::I2scfg::SLAVEFULLDUPLEX => vals::I2scfg::SLAVERX, | 718 | vals::I2scfg::SLAVE_RX | vals::I2scfg::SLAVE_FULL_DUPLEX => vals::I2scfg::SLAVE_RX, |
| 700 | vals::I2scfg::MASTERRX | vals::I2scfg::MASTERFULLDUPLEX => vals::I2scfg::MASTERRX, | 719 | vals::I2scfg::MASTER_RX | vals::I2scfg::MASTER_FULL_DUPLEX => vals::I2scfg::MASTER_RX, |
| 701 | _ => panic!("unsupported configuration"), | 720 | _ => panic!("unsupported configuration"), |
| 702 | }); | 721 | }); |
| 703 | prev | 722 | prev |
| @@ -707,7 +726,6 @@ impl<'d> Spi<'d, Async> { | |||
| 707 | let rx_src = regs.rx_ptr(); | 726 | let rx_src = regs.rx_ptr(); |
| 708 | 727 | ||
| 709 | for mut chunk in data.chunks_mut(u16::max_value().into()) { | 728 | for mut chunk in data.chunks_mut(u16::max_value().into()) { |
| 710 | self.set_word_size(W::CONFIG); | ||
| 711 | set_rxdmaen(regs, true); | 729 | set_rxdmaen(regs, true); |
| 712 | 730 | ||
| 713 | let tsize = chunk.len(); | 731 | let tsize = chunk.len(); |
| @@ -765,12 +783,12 @@ impl<'d> Spi<'d, Async> { | |||
| 765 | return Ok(()); | 783 | return Ok(()); |
| 766 | } | 784 | } |
| 767 | 785 | ||
| 768 | self.set_word_size(W::CONFIG); | ||
| 769 | |||
| 770 | self.info.regs.cr1().modify(|w| { | 786 | self.info.regs.cr1().modify(|w| { |
| 771 | w.set_spe(false); | 787 | w.set_spe(false); |
| 772 | }); | 788 | }); |
| 773 | 789 | ||
| 790 | self.set_word_size(W::CONFIG); | ||
| 791 | |||
| 774 | // SPIv3 clears rxfifo on SPE=0 | 792 | // SPIv3 clears rxfifo on SPE=0 |
| 775 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 793 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 776 | flush_rx_fifo(self.info.regs); | 794 | flush_rx_fifo(self.info.regs); |
| @@ -783,7 +801,7 @@ impl<'d> Spi<'d, Async> { | |||
| 783 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; | 801 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; |
| 784 | 802 | ||
| 785 | let tx_dst = self.info.regs.tx_ptr(); | 803 | let tx_dst = self.info.regs.tx_ptr(); |
| 786 | let clock_byte = 0x00u8; | 804 | let clock_byte = W::default(); |
| 787 | let tx_f = unsafe { | 805 | let tx_f = unsafe { |
| 788 | self.tx_dma | 806 | self.tx_dma |
| 789 | .as_mut() | 807 | .as_mut() |
| @@ -813,11 +831,12 @@ impl<'d> Spi<'d, Async> { | |||
| 813 | return Ok(()); | 831 | return Ok(()); |
| 814 | } | 832 | } |
| 815 | 833 | ||
| 816 | self.set_word_size(W::CONFIG); | ||
| 817 | self.info.regs.cr1().modify(|w| { | 834 | self.info.regs.cr1().modify(|w| { |
| 818 | w.set_spe(false); | 835 | w.set_spe(false); |
| 819 | }); | 836 | }); |
| 820 | 837 | ||
| 838 | self.set_word_size(W::CONFIG); | ||
| 839 | |||
| 821 | // SPIv3 clears rxfifo on SPE=0 | 840 | // SPIv3 clears rxfifo on SPE=0 |
| 822 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 841 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 823 | flush_rx_fifo(self.info.regs); | 842 | flush_rx_fifo(self.info.regs); |
| @@ -827,7 +846,7 @@ impl<'d> Spi<'d, Async> { | |||
| 827 | let rx_src = self.info.regs.rx_ptr(); | 846 | let rx_src = self.info.regs.rx_ptr(); |
| 828 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; | 847 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; |
| 829 | 848 | ||
| 830 | let tx_dst = self.info.regs.tx_ptr(); | 849 | let tx_dst: *mut W = self.info.regs.tx_ptr(); |
| 831 | let tx_f = unsafe { | 850 | let tx_f = unsafe { |
| 832 | self.tx_dma | 851 | self.tx_dma |
| 833 | .as_mut() | 852 | .as_mut() |
| @@ -915,7 +934,7 @@ fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz { | |||
| 915 | kernel_clock / div | 934 | kernel_clock / div |
| 916 | } | 935 | } |
| 917 | 936 | ||
| 918 | trait RegsExt { | 937 | pub(crate) trait RegsExt { |
| 919 | fn tx_ptr<W>(&self) -> *mut W; | 938 | fn tx_ptr<W>(&self) -> *mut W; |
| 920 | fn rx_ptr<W>(&self) -> *mut W; | 939 | fn rx_ptr<W>(&self) -> *mut W; |
| 921 | } | 940 | } |
| @@ -1003,7 +1022,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { | |||
| 1003 | } | 1022 | } |
| 1004 | } | 1023 | } |
| 1005 | 1024 | ||
| 1006 | fn flush_rx_fifo(regs: Regs) { | 1025 | pub(crate) fn flush_rx_fifo(regs: Regs) { |
| 1007 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1026 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 1008 | while regs.sr().read().rxne() { | 1027 | while regs.sr().read().rxne() { |
| 1009 | #[cfg(not(spi_v2))] | 1028 | #[cfg(not(spi_v2))] |
| @@ -1017,7 +1036,7 @@ fn flush_rx_fifo(regs: Regs) { | |||
| 1017 | } | 1036 | } |
| 1018 | } | 1037 | } |
| 1019 | 1038 | ||
| 1020 | fn set_txdmaen(regs: Regs, val: bool) { | 1039 | pub(crate) fn set_txdmaen(regs: Regs, val: bool) { |
| 1021 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1040 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 1022 | regs.cr2().modify(|reg| { | 1041 | regs.cr2().modify(|reg| { |
| 1023 | reg.set_txdmaen(val); | 1042 | reg.set_txdmaen(val); |
| @@ -1028,7 +1047,7 @@ fn set_txdmaen(regs: Regs, val: bool) { | |||
| 1028 | }); | 1047 | }); |
| 1029 | } | 1048 | } |
| 1030 | 1049 | ||
| 1031 | fn set_rxdmaen(regs: Regs, val: bool) { | 1050 | pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { |
| 1032 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] | 1051 | #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] |
| 1033 | regs.cr2().modify(|reg| { | 1052 | regs.cr2().modify(|reg| { |
| 1034 | reg.set_rxdmaen(val); | 1053 | reg.set_rxdmaen(val); |
| @@ -1189,13 +1208,13 @@ impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | |||
| 1189 | } | 1208 | } |
| 1190 | } | 1209 | } |
| 1191 | 1210 | ||
| 1192 | trait SealedWord { | 1211 | pub(crate) trait SealedWord { |
| 1193 | const CONFIG: word_impl::Config; | 1212 | const CONFIG: word_impl::Config; |
| 1194 | } | 1213 | } |
| 1195 | 1214 | ||
| 1196 | /// Word sizes usable for SPI. | 1215 | /// Word sizes usable for SPI. |
| 1197 | #[allow(private_bounds)] | 1216 | #[allow(private_bounds)] |
| 1198 | pub trait Word: word::Word + SealedWord {} | 1217 | pub trait Word: word::Word + SealedWord + Default {} |
| 1199 | 1218 | ||
| 1200 | macro_rules! impl_word { | 1219 | macro_rules! impl_word { |
| 1201 | ($T:ty, $config:expr) => { | 1220 | ($T:ty, $config:expr) => { |
diff --git a/embassy-stm32/src/time.rs b/embassy-stm32/src/time.rs index 802ff41ce..532877f70 100644 --- a/embassy-stm32/src/time.rs +++ b/embassy-stm32/src/time.rs | |||
| @@ -1,12 +1,25 @@ | |||
| 1 | //! Time units | 1 | //! Time units |
| 2 | 2 | ||
| 3 | use core::fmt::Display; | ||
| 3 | use core::ops::{Div, Mul}; | 4 | use core::ops::{Div, Mul}; |
| 4 | 5 | ||
| 5 | /// Hertz | 6 | /// Hertz |
| 6 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] | 7 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] |
| 7 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 8 | pub struct Hertz(pub u32); | 8 | pub struct Hertz(pub u32); |
| 9 | 9 | ||
| 10 | impl Display for Hertz { | ||
| 11 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 12 | write!(f, "{} Hz", self.0) | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | #[cfg(feature = "defmt")] | ||
| 17 | impl defmt::Format for Hertz { | ||
| 18 | fn format(&self, f: defmt::Formatter) { | ||
| 19 | defmt::write!(f, "{=u32} Hz", self.0) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 10 | impl Hertz { | 23 | impl Hertz { |
| 11 | /// Create a `Hertz` from the given hertz. | 24 | /// Create a `Hertz` from the given hertz. |
| 12 | pub const fn hz(hertz: u32) -> Self { | 25 | pub const fn hz(hertz: u32) -> Self { |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index f8041bf1e..7db74bdf6 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -1,13 +1,13 @@ | |||
| 1 | #![allow(non_snake_case)] | 1 | #![allow(non_snake_case)] |
| 2 | 2 | ||
| 3 | use core::cell::Cell; | 3 | use core::cell::{Cell, RefCell}; |
| 4 | use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; | 4 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; |
| 5 | use core::{mem, ptr}; | ||
| 6 | 5 | ||
| 7 | use critical_section::CriticalSection; | 6 | use critical_section::CriticalSection; |
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | 7 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 9 | use embassy_sync::blocking_mutex::Mutex; | 8 | use embassy_sync::blocking_mutex::Mutex; |
| 10 | use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ}; | 9 | use embassy_time_driver::{Driver, TICK_HZ}; |
| 10 | use embassy_time_queue_utils::Queue; | ||
| 11 | use stm32_metapac::timer::{regs, TimGp16}; | 11 | use stm32_metapac::timer::{regs, TimGp16}; |
| 12 | 12 | ||
| 13 | use crate::interrupt::typelevel::Interrupt; | 13 | use crate::interrupt::typelevel::Interrupt; |
| @@ -24,18 +24,6 @@ use crate::{interrupt, peripherals}; | |||
| 24 | // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST | 24 | // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST |
| 25 | // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not | 25 | // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not |
| 26 | // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) | 26 | // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) |
| 27 | // | ||
| 28 | // The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number | ||
| 29 | // available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: | ||
| 30 | // CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. | ||
| 31 | |||
| 32 | cfg_if::cfg_if! { | ||
| 33 | if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] { | ||
| 34 | const ALARM_COUNT: usize = 1; | ||
| 35 | } else { | ||
| 36 | const ALARM_COUNT: usize = 3; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | 27 | ||
| 40 | #[cfg(time_driver_tim1)] | 28 | #[cfg(time_driver_tim1)] |
| 41 | type T = peripherals::TIM1; | 29 | type T = peripherals::TIM1; |
| @@ -75,14 +63,6 @@ foreach_interrupt! { | |||
| 75 | DRIVER.on_interrupt() | 63 | DRIVER.on_interrupt() |
| 76 | } | 64 | } |
| 77 | }; | 65 | }; |
| 78 | (TIM1, timer, $block:ident, CC, $irq:ident) => { | ||
| 79 | #[cfg(time_driver_tim1)] | ||
| 80 | #[cfg(feature = "rt")] | ||
| 81 | #[interrupt] | ||
| 82 | fn $irq() { | ||
| 83 | DRIVER.on_interrupt() | ||
| 84 | } | ||
| 85 | }; | ||
| 86 | (TIM2, timer, $block:ident, CC, $irq:ident) => { | 66 | (TIM2, timer, $block:ident, CC, $irq:ident) => { |
| 87 | #[cfg(time_driver_tim2)] | 67 | #[cfg(time_driver_tim2)] |
| 88 | #[cfg(feature = "rt")] | 68 | #[cfg(feature = "rt")] |
| @@ -123,14 +103,6 @@ foreach_interrupt! { | |||
| 123 | DRIVER.on_interrupt() | 103 | DRIVER.on_interrupt() |
| 124 | } | 104 | } |
| 125 | }; | 105 | }; |
| 126 | (TIM8, timer, $block:ident, CC, $irq:ident) => { | ||
| 127 | #[cfg(time_driver_tim8)] | ||
| 128 | #[cfg(feature = "rt")] | ||
| 129 | #[interrupt] | ||
| 130 | fn $irq() { | ||
| 131 | DRIVER.on_interrupt() | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | (TIM9, timer, $block:ident, CC, $irq:ident) => { | 106 | (TIM9, timer, $block:ident, CC, $irq:ident) => { |
| 135 | #[cfg(time_driver_tim9)] | 107 | #[cfg(time_driver_tim9)] |
| 136 | #[cfg(feature = "rt")] | 108 | #[cfg(feature = "rt")] |
| @@ -163,14 +135,6 @@ foreach_interrupt! { | |||
| 163 | DRIVER.on_interrupt() | 135 | DRIVER.on_interrupt() |
| 164 | } | 136 | } |
| 165 | }; | 137 | }; |
| 166 | (TIM20, timer, $block:ident, CC, $irq:ident) => { | ||
| 167 | #[cfg(time_driver_tim20)] | ||
| 168 | #[cfg(feature = "rt")] | ||
| 169 | #[interrupt] | ||
| 170 | fn $irq() { | ||
| 171 | DRIVER.on_interrupt() | ||
| 172 | } | ||
| 173 | }; | ||
| 174 | (TIM21, timer, $block:ident, CC, $irq:ident) => { | 138 | (TIM21, timer, $block:ident, CC, $irq:ident) => { |
| 175 | #[cfg(time_driver_tim21)] | 139 | #[cfg(time_driver_tim21)] |
| 176 | #[cfg(feature = "rt")] | 140 | #[cfg(feature = "rt")] |
| @@ -232,11 +196,6 @@ fn calc_now(period: u32, counter: u16) -> u64 { | |||
| 232 | 196 | ||
| 233 | struct AlarmState { | 197 | struct AlarmState { |
| 234 | timestamp: Cell<u64>, | 198 | timestamp: Cell<u64>, |
| 235 | |||
| 236 | // This is really a Option<(fn(*mut ()), *mut ())> | ||
| 237 | // but fn pointers aren't allowed in const yet | ||
| 238 | callback: Cell<*const ()>, | ||
| 239 | ctx: Cell<*mut ()>, | ||
| 240 | } | 199 | } |
| 241 | 200 | ||
| 242 | unsafe impl Send for AlarmState {} | 201 | unsafe impl Send for AlarmState {} |
| @@ -245,8 +204,6 @@ impl AlarmState { | |||
| 245 | const fn new() -> Self { | 204 | const fn new() -> Self { |
| 246 | Self { | 205 | Self { |
| 247 | timestamp: Cell::new(u64::MAX), | 206 | timestamp: Cell::new(u64::MAX), |
| 248 | callback: Cell::new(ptr::null()), | ||
| 249 | ctx: Cell::new(ptr::null_mut()), | ||
| 250 | } | 207 | } |
| 251 | } | 208 | } |
| 252 | } | 209 | } |
| @@ -254,22 +211,18 @@ impl AlarmState { | |||
| 254 | pub(crate) struct RtcDriver { | 211 | pub(crate) struct RtcDriver { |
| 255 | /// Number of 2^15 periods elapsed since boot. | 212 | /// Number of 2^15 periods elapsed since boot. |
| 256 | period: AtomicU32, | 213 | period: AtomicU32, |
| 257 | alarm_count: AtomicU8, | 214 | alarm: Mutex<CriticalSectionRawMutex, AlarmState>, |
| 258 | /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. | ||
| 259 | alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>, | ||
| 260 | #[cfg(feature = "low-power")] | 215 | #[cfg(feature = "low-power")] |
| 261 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, | 216 | rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>, |
| 217 | queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>, | ||
| 262 | } | 218 | } |
| 263 | 219 | ||
| 264 | #[allow(clippy::declare_interior_mutable_const)] | ||
| 265 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); | ||
| 266 | |||
| 267 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | 220 | embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 268 | period: AtomicU32::new(0), | 221 | period: AtomicU32::new(0), |
| 269 | alarm_count: AtomicU8::new(0), | 222 | alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), |
| 270 | alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), | ||
| 271 | #[cfg(feature = "low-power")] | 223 | #[cfg(feature = "low-power")] |
| 272 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), | 224 | rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), |
| 225 | queue: Mutex::new(RefCell::new(Queue::new())) | ||
| 273 | }); | 226 | }); |
| 274 | 227 | ||
| 275 | impl RtcDriver { | 228 | impl RtcDriver { |
| @@ -293,9 +246,9 @@ impl RtcDriver { | |||
| 293 | r.arr().write(|w| w.set_arr(u16::MAX)); | 246 | r.arr().write(|w| w.set_arr(u16::MAX)); |
| 294 | 247 | ||
| 295 | // Set URS, generate update and clear URS | 248 | // Set URS, generate update and clear URS |
| 296 | r.cr1().modify(|w| w.set_urs(vals::Urs::COUNTERONLY)); | 249 | r.cr1().modify(|w| w.set_urs(vals::Urs::COUNTER_ONLY)); |
| 297 | r.egr().write(|w| w.set_ug(true)); | 250 | r.egr().write(|w| w.set_ug(true)); |
| 298 | r.cr1().modify(|w| w.set_urs(vals::Urs::ANYEVENT)); | 251 | r.cr1().modify(|w| w.set_urs(vals::Urs::ANY_EVENT)); |
| 299 | 252 | ||
| 300 | // Mid-way point | 253 | // Mid-way point |
| 301 | r.ccr(0).write(|w| w.set_ccr(0x8000)); | 254 | r.ccr(0).write(|w| w.set_ccr(0x8000)); |
| @@ -315,7 +268,6 @@ impl RtcDriver { | |||
| 315 | fn on_interrupt(&self) { | 268 | fn on_interrupt(&self) { |
| 316 | let r = regs_gp16(); | 269 | let r = regs_gp16(); |
| 317 | 270 | ||
| 318 | // XXX: reduce the size of this critical section ? | ||
| 319 | critical_section::with(|cs| { | 271 | critical_section::with(|cs| { |
| 320 | let sr = r.sr().read(); | 272 | let sr = r.sr().read(); |
| 321 | let dier = r.dier().read(); | 273 | let dier = r.dier().read(); |
| @@ -335,10 +287,9 @@ impl RtcDriver { | |||
| 335 | self.next_period(); | 287 | self.next_period(); |
| 336 | } | 288 | } |
| 337 | 289 | ||
| 338 | for n in 0..ALARM_COUNT { | 290 | let n = 0; |
| 339 | if sr.ccif(n + 1) && dier.ccie(n + 1) { | 291 | if sr.ccif(n + 1) && dier.ccie(n + 1) { |
| 340 | self.trigger_alarm(n, cs); | 292 | self.trigger_alarm(cs); |
| 341 | } | ||
| 342 | } | 293 | } |
| 343 | }) | 294 | }) |
| 344 | } | 295 | } |
| @@ -353,36 +304,23 @@ impl RtcDriver { | |||
| 353 | 304 | ||
| 354 | critical_section::with(move |cs| { | 305 | critical_section::with(move |cs| { |
| 355 | r.dier().modify(move |w| { | 306 | r.dier().modify(move |w| { |
| 356 | for n in 0..ALARM_COUNT { | 307 | let n = 0; |
| 357 | let alarm = &self.alarms.borrow(cs)[n]; | 308 | let alarm = self.alarm.borrow(cs); |
| 358 | let at = alarm.timestamp.get(); | 309 | let at = alarm.timestamp.get(); |
| 359 | 310 | ||
| 360 | if at < t + 0xc000 { | 311 | if at < t + 0xc000 { |
| 361 | // just enable it. `set_alarm` has already set the correct CCR val. | 312 | // just enable it. `set_alarm` has already set the correct CCR val. |
| 362 | w.set_ccie(n + 1, true); | 313 | w.set_ccie(n + 1, true); |
| 363 | } | ||
| 364 | } | 314 | } |
| 365 | }) | 315 | }) |
| 366 | }) | 316 | }) |
| 367 | } | 317 | } |
| 368 | 318 | ||
| 369 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { | 319 | fn trigger_alarm(&self, cs: CriticalSection) { |
| 370 | // safety: we're allowed to assume the AlarmState is created by us, and | 320 | let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); |
| 371 | // we never create one that's out of bounds. | 321 | while !self.set_alarm(cs, next) { |
| 372 | unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) } | 322 | next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); |
| 373 | } | 323 | } |
| 374 | |||
| 375 | fn trigger_alarm(&self, n: usize, cs: CriticalSection) { | ||
| 376 | let alarm = &self.alarms.borrow(cs)[n]; | ||
| 377 | alarm.timestamp.set(u64::MAX); | ||
| 378 | |||
| 379 | // Call after clearing alarm, so the callback can set another alarm. | ||
| 380 | |||
| 381 | // safety: | ||
| 382 | // - we can ignore the possibility of `f` being unset (null) because of the safety contract of `allocate_alarm`. | ||
| 383 | // - other than that we only store valid function pointers into alarm.callback | ||
| 384 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; | ||
| 385 | f(alarm.ctx.get()); | ||
| 386 | } | 324 | } |
| 387 | 325 | ||
| 388 | /* | 326 | /* |
| @@ -394,14 +332,7 @@ impl RtcDriver { | |||
| 394 | fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { | 332 | fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { |
| 395 | let now = self.now() + 32; | 333 | let now = self.now() + 32; |
| 396 | 334 | ||
| 397 | embassy_time::Duration::from_ticks( | 335 | embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now)) |
| 398 | self.alarms | ||
| 399 | .borrow(cs) | ||
| 400 | .iter() | ||
| 401 | .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) | ||
| 402 | .min() | ||
| 403 | .unwrap_or(u64::MAX), | ||
| 404 | ) | ||
| 405 | } | 336 | } |
| 406 | 337 | ||
| 407 | #[cfg(feature = "low-power")] | 338 | #[cfg(feature = "low-power")] |
| @@ -436,12 +367,12 @@ impl RtcDriver { | |||
| 436 | self.period.store(period, Ordering::SeqCst); | 367 | self.period.store(period, Ordering::SeqCst); |
| 437 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); | 368 | regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); |
| 438 | 369 | ||
| 439 | // Now, recompute all alarms | 370 | // Now, recompute alarm |
| 440 | for i in 0..ALARM_COUNT { | 371 | let alarm = self.alarm.borrow(cs); |
| 441 | let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; | ||
| 442 | let alarm = self.get_alarm(cs, alarm_handle); | ||
| 443 | 372 | ||
| 444 | self.set_alarm(alarm_handle, alarm.timestamp.get()); | 373 | if !self.set_alarm(cs, alarm.timestamp.get()) { |
| 374 | // If the alarm timestamp has passed, we need to trigger it | ||
| 375 | self.trigger_alarm(cs); | ||
| 445 | } | 376 | } |
| 446 | } | 377 | } |
| 447 | 378 | ||
| @@ -513,82 +444,71 @@ impl RtcDriver { | |||
| 513 | regs_gp16().cr1().modify(|w| w.set_cen(true)); | 444 | regs_gp16().cr1().modify(|w| w.set_cen(true)); |
| 514 | }) | 445 | }) |
| 515 | } | 446 | } |
| 516 | } | ||
| 517 | 447 | ||
| 518 | impl Driver for RtcDriver { | 448 | fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { |
| 519 | fn now(&self) -> u64 { | ||
| 520 | let r = regs_gp16(); | 449 | let r = regs_gp16(); |
| 521 | 450 | ||
| 522 | let period = self.period.load(Ordering::Relaxed); | 451 | let n = 0; |
| 523 | compiler_fence(Ordering::Acquire); | 452 | self.alarm.borrow(cs).timestamp.set(timestamp); |
| 524 | let counter = r.cnt().read().cnt(); | ||
| 525 | calc_now(period, counter) | ||
| 526 | } | ||
| 527 | 453 | ||
| 528 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | 454 | let t = self.now(); |
| 529 | critical_section::with(|_| { | 455 | if timestamp <= t { |
| 530 | let id = self.alarm_count.load(Ordering::Relaxed); | 456 | // If alarm timestamp has passed the alarm will not fire. |
| 531 | if id < ALARM_COUNT as u8 { | 457 | // Disarm the alarm and return `false` to indicate that. |
| 532 | self.alarm_count.store(id + 1, Ordering::Relaxed); | 458 | r.dier().modify(|w| w.set_ccie(n + 1, false)); |
| 533 | Some(AlarmHandle::new(id)) | ||
| 534 | } else { | ||
| 535 | None | ||
| 536 | } | ||
| 537 | }) | ||
| 538 | } | ||
| 539 | 459 | ||
| 540 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | 460 | self.alarm.borrow(cs).timestamp.set(u64::MAX); |
| 541 | critical_section::with(|cs| { | ||
| 542 | let alarm = self.get_alarm(cs, alarm); | ||
| 543 | 461 | ||
| 544 | alarm.callback.set(callback as *const ()); | 462 | return false; |
| 545 | alarm.ctx.set(ctx); | 463 | } |
| 546 | }) | ||
| 547 | } | ||
| 548 | 464 | ||
| 549 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | 465 | // Write the CCR value regardless of whether we're going to enable it now or not. |
| 550 | critical_section::with(|cs| { | 466 | // This way, when we enable it later, the right value is already set. |
| 551 | let r = regs_gp16(); | 467 | r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); |
| 552 | 468 | ||
| 553 | let n = alarm.id() as usize; | 469 | // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. |
| 554 | let alarm = self.get_alarm(cs, alarm); | 470 | let diff = timestamp - t; |
| 555 | alarm.timestamp.set(timestamp); | 471 | r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); |
| 556 | 472 | ||
| 557 | let t = self.now(); | 473 | // Reevaluate if the alarm timestamp is still in the future |
| 558 | if timestamp <= t { | 474 | let t = self.now(); |
| 559 | // If alarm timestamp has passed the alarm will not fire. | 475 | if timestamp <= t { |
| 560 | // Disarm the alarm and return `false` to indicate that. | 476 | // If alarm timestamp has passed since we set it, we have a race condition and |
| 561 | r.dier().modify(|w| w.set_ccie(n + 1, false)); | 477 | // the alarm may or may not have fired. |
| 478 | // Disarm the alarm and return `false` to indicate that. | ||
| 479 | // It is the caller's responsibility to handle this ambiguity. | ||
| 480 | r.dier().modify(|w| w.set_ccie(n + 1, false)); | ||
| 562 | 481 | ||
| 563 | alarm.timestamp.set(u64::MAX); | 482 | self.alarm.borrow(cs).timestamp.set(u64::MAX); |
| 564 | 483 | ||
| 565 | return false; | 484 | return false; |
| 566 | } | 485 | } |
| 567 | 486 | ||
| 568 | // Write the CCR value regardless of whether we're going to enable it now or not. | 487 | // We're confident the alarm will ring in the future. |
| 569 | // This way, when we enable it later, the right value is already set. | 488 | true |
| 570 | r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); | 489 | } |
| 490 | } | ||
| 571 | 491 | ||
| 572 | // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. | 492 | impl Driver for RtcDriver { |
| 573 | let diff = timestamp - t; | 493 | fn now(&self) -> u64 { |
| 574 | r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); | 494 | let r = regs_gp16(); |
| 575 | 495 | ||
| 576 | // Reevaluate if the alarm timestamp is still in the future | 496 | let period = self.period.load(Ordering::Relaxed); |
| 577 | let t = self.now(); | 497 | compiler_fence(Ordering::Acquire); |
| 578 | if timestamp <= t { | 498 | let counter = r.cnt().read().cnt(); |
| 579 | // If alarm timestamp has passed since we set it, we have a race condition and | 499 | calc_now(period, counter) |
| 580 | // the alarm may or may not have fired. | 500 | } |
| 581 | // Disarm the alarm and return `false` to indicate that. | ||
| 582 | // It is the caller's responsibility to handle this ambiguity. | ||
| 583 | r.dier().modify(|w| w.set_ccie(n + 1, false)); | ||
| 584 | 501 | ||
| 585 | alarm.timestamp.set(u64::MAX); | 502 | fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { |
| 503 | critical_section::with(|cs| { | ||
| 504 | let mut queue = self.queue.borrow(cs).borrow_mut(); | ||
| 586 | 505 | ||
| 587 | return false; | 506 | if queue.schedule_wake(at, waker) { |
| 507 | let mut next = queue.next_expiration(self.now()); | ||
| 508 | while !self.set_alarm(cs, next) { | ||
| 509 | next = queue.next_expiration(self.now()); | ||
| 510 | } | ||
| 588 | } | 511 | } |
| 589 | |||
| 590 | // We're confident the alarm will ring in the future. | ||
| 591 | true | ||
| 592 | }) | 512 | }) |
| 593 | } | 513 | } |
| 594 | } | 514 | } |
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 46ccbf3df..8eec6c0c7 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 6 | use stm32_metapac::timer::vals::Ckd; | 5 | use stm32_metapac::timer::vals::Ckd; |
| 7 | 6 | ||
| 8 | use super::low_level::{CountingMode, OutputPolarity, Timer}; | 7 | use super::low_level::{CountingMode, OutputPolarity, Timer}; |
| @@ -14,13 +13,13 @@ use super::{ | |||
| 14 | use crate::gpio::{AnyPin, OutputType}; | 13 | use crate::gpio::{AnyPin, OutputType}; |
| 15 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 16 | use crate::timer::low_level::OutputCompareMode; | 15 | use crate::timer::low_level::OutputCompareMode; |
| 17 | use crate::Peripheral; | 16 | use crate::Peri; |
| 18 | 17 | ||
| 19 | /// Complementary PWM pin wrapper. | 18 | /// Complementary PWM pin wrapper. |
| 20 | /// | 19 | /// |
| 21 | /// This wraps a pin to make it usable with PWM. | 20 | /// This wraps a pin to make it usable with PWM. |
| 22 | pub struct ComplementaryPwmPin<'d, T, C> { | 21 | pub struct ComplementaryPwmPin<'d, T, C> { |
| 23 | _pin: PeripheralRef<'d, AnyPin>, | 22 | _pin: Peri<'d, AnyPin>, |
| 24 | phantom: PhantomData<(T, C)>, | 23 | phantom: PhantomData<(T, C)>, |
| 25 | } | 24 | } |
| 26 | 25 | ||
| @@ -28,8 +27,7 @@ macro_rules! complementary_channel_impl { | |||
| 28 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 27 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
| 29 | impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { | 28 | impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { |
| 30 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] | 29 | #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] |
| 31 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self { | 30 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { |
| 32 | into_ref!(pin); | ||
| 33 | critical_section::with(|_| { | 31 | critical_section::with(|_| { |
| 34 | pin.set_low(); | 32 | pin.set_low(); |
| 35 | pin.set_as_af( | 33 | pin.set_as_af( |
| @@ -38,7 +36,7 @@ macro_rules! complementary_channel_impl { | |||
| 38 | ); | 36 | ); |
| 39 | }); | 37 | }); |
| 40 | ComplementaryPwmPin { | 38 | ComplementaryPwmPin { |
| 41 | _pin: pin.map_into(), | 39 | _pin: pin.into(), |
| 42 | phantom: PhantomData, | 40 | phantom: PhantomData, |
| 43 | } | 41 | } |
| 44 | } | 42 | } |
| @@ -60,7 +58,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 60 | /// Create a new complementary PWM driver. | 58 | /// Create a new complementary PWM driver. |
| 61 | #[allow(clippy::too_many_arguments)] | 59 | #[allow(clippy::too_many_arguments)] |
| 62 | pub fn new( | 60 | pub fn new( |
| 63 | tim: impl Peripheral<P = T> + 'd, | 61 | tim: Peri<'d, T>, |
| 64 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 62 | _ch1: Option<PwmPin<'d, T, Ch1>>, |
| 65 | _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>, | 63 | _ch1n: Option<ComplementaryPwmPin<'d, T, Ch1>>, |
| 66 | _ch2: Option<PwmPin<'d, T, Ch2>>, | 64 | _ch2: Option<PwmPin<'d, T, Ch2>>, |
| @@ -75,7 +73,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 75 | Self::new_inner(tim, freq, counting_mode) | 73 | Self::new_inner(tim, freq, counting_mode) |
| 76 | } | 74 | } |
| 77 | 75 | ||
| 78 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { | 76 | fn new_inner(tim: Peri<'d, T>, freq: Hertz, counting_mode: CountingMode) -> Self { |
| 79 | let mut this = Self { inner: Timer::new(tim) }; | 77 | let mut this = Self { inner: Timer::new(tim) }; |
| 80 | 78 | ||
| 81 | this.inner.set_counting_mode(counting_mode); | 79 | this.inner.set_counting_mode(counting_mode); |
| @@ -116,7 +114,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { | |||
| 116 | } else { | 114 | } else { |
| 117 | 1u8 | 115 | 1u8 |
| 118 | }; | 116 | }; |
| 119 | self.inner.set_frequency(freq * multiplier); | 117 | self.inner.set_frequency_internal(freq * multiplier, 16); |
| 120 | } | 118 | } |
| 121 | 119 | ||
| 122 | /// Get max duty value. | 120 | /// Get max duty value. |
| @@ -242,11 +240,11 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { | |||
| 242 | let (these_bits, result) = if target < 128 { | 240 | let (these_bits, result) = if target < 128 { |
| 243 | (target as u8, target) | 241 | (target as u8, target) |
| 244 | } else if target < 255 { | 242 | } else if target < 255 { |
| 245 | (64 + (target / 2) as u8, (target - target % 2)) | 243 | ((64 + (target / 2) as u8) | 128, (target - target % 2)) |
| 246 | } else if target < 508 { | 244 | } else if target < 508 { |
| 247 | (32 + (target / 8) as u8, (target - target % 8)) | 245 | ((32 + (target / 8) as u8) | 192, (target - target % 8)) |
| 248 | } else if target < 1008 { | 246 | } else if target < 1008 { |
| 249 | (32 + (target / 16) as u8, (target - target % 16)) | 247 | ((32 + (target / 16) as u8) | 224, (target - target % 16)) |
| 250 | } else { | 248 | } else { |
| 251 | (u8::MAX, 1008) | 249 | (u8::MAX, 1008) |
| 252 | }; | 250 | }; |
| @@ -302,7 +300,7 @@ mod tests { | |||
| 302 | TestRun { | 300 | TestRun { |
| 303 | value: 400, | 301 | value: 400, |
| 304 | ckd: Ckd::DIV1, | 302 | ckd: Ckd::DIV1, |
| 305 | bits: 32 + (400u16 / 8) as u8, | 303 | bits: 210, |
| 306 | }, | 304 | }, |
| 307 | TestRun { | 305 | TestRun { |
| 308 | value: 600, | 306 | value: 600, |
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 341ac2c04..ec8b1ddf1 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs | |||
| @@ -5,32 +5,22 @@ use core::marker::PhantomData; | |||
| 5 | use core::pin::Pin; | 5 | use core::pin::Pin; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 9 | |||
| 10 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; | 8 | use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; |
| 11 | use super::{ | 9 | use super::{ |
| 12 | CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, | 10 | CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, |
| 13 | GeneralInstance4Channel, | 11 | GeneralInstance4Channel, |
| 14 | }; | 12 | }; |
| 13 | pub use super::{Ch1, Ch2, Ch3, Ch4}; | ||
| 15 | use crate::gpio::{AfType, AnyPin, Pull}; | 14 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 16 | use crate::interrupt::typelevel::{Binding, Interrupt}; | 15 | use crate::interrupt::typelevel::{Binding, Interrupt}; |
| 17 | use crate::time::Hertz; | 16 | use crate::time::Hertz; |
| 18 | use crate::Peripheral; | 17 | use crate::Peri; |
| 19 | |||
| 20 | /// Channel 1 marker type. | ||
| 21 | pub enum Ch1 {} | ||
| 22 | /// Channel 2 marker type. | ||
| 23 | pub enum Ch2 {} | ||
| 24 | /// Channel 3 marker type. | ||
| 25 | pub enum Ch3 {} | ||
| 26 | /// Channel 4 marker type. | ||
| 27 | pub enum Ch4 {} | ||
| 28 | 18 | ||
| 29 | /// Capture pin wrapper. | 19 | /// Capture pin wrapper. |
| 30 | /// | 20 | /// |
| 31 | /// This wraps a pin to make it usable with capture. | 21 | /// This wraps a pin to make it usable with capture. |
| 32 | pub struct CapturePin<'d, T, C> { | 22 | pub struct CapturePin<'d, T, C> { |
| 33 | _pin: PeripheralRef<'d, AnyPin>, | 23 | _pin: Peri<'d, AnyPin>, |
| 34 | phantom: PhantomData<(T, C)>, | 24 | phantom: PhantomData<(T, C)>, |
| 35 | } | 25 | } |
| 36 | 26 | ||
| @@ -38,11 +28,10 @@ macro_rules! channel_impl { | |||
| 38 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 28 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
| 39 | impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { | 29 | impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> { |
| 40 | #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] | 30 | #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] |
| 41 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull: Pull) -> Self { | 31 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { |
| 42 | into_ref!(pin); | ||
| 43 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 32 | pin.set_as_af(pin.af_num(), AfType::input(pull)); |
| 44 | CapturePin { | 33 | CapturePin { |
| 45 | _pin: pin.map_into(), | 34 | _pin: pin.into(), |
| 46 | phantom: PhantomData, | 35 | phantom: PhantomData, |
| 47 | } | 36 | } |
| 48 | } | 37 | } |
| @@ -63,7 +52,7 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> { | |||
| 63 | impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | 52 | impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { |
| 64 | /// Create a new input capture driver. | 53 | /// Create a new input capture driver. |
| 65 | pub fn new( | 54 | pub fn new( |
| 66 | tim: impl Peripheral<P = T> + 'd, | 55 | tim: Peri<'d, T>, |
| 67 | _ch1: Option<CapturePin<'d, T, Ch1>>, | 56 | _ch1: Option<CapturePin<'d, T, Ch1>>, |
| 68 | _ch2: Option<CapturePin<'d, T, Ch2>>, | 57 | _ch2: Option<CapturePin<'d, T, Ch2>>, |
| 69 | _ch3: Option<CapturePin<'d, T, Ch3>>, | 58 | _ch3: Option<CapturePin<'d, T, Ch3>>, |
| @@ -75,7 +64,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 75 | Self::new_inner(tim, freq, counting_mode) | 64 | Self::new_inner(tim, freq, counting_mode) |
| 76 | } | 65 | } |
| 77 | 66 | ||
| 78 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { | 67 | fn new_inner(tim: Peri<'d, T>, freq: Hertz, counting_mode: CountingMode) -> Self { |
| 79 | let mut this = Self { inner: Timer::new(tim) }; | 68 | let mut this = Self { inner: Timer::new(tim) }; |
| 80 | 69 | ||
| 81 | this.inner.set_counting_mode(counting_mode); | 70 | this.inner.set_counting_mode(counting_mode); |
| @@ -129,7 +118,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { | |||
| 129 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 | 118 | // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 |
| 130 | // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode | 119 | // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode |
| 131 | self.inner.set_input_ti_selection(channel, tisel); | 120 | self.inner.set_input_ti_selection(channel, tisel); |
| 132 | self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER); | 121 | self.inner.set_input_capture_filter(channel, FilterValue::NO_FILTER); |
| 133 | self.inner.set_input_capture_mode(channel, mode); | 122 | self.inner.set_input_capture_mode(channel, mode); |
| 134 | self.inner.set_input_capture_prescaler(channel, 0); | 123 | self.inner.set_input_capture_prescaler(channel, 0); |
| 135 | self.inner.enable_channel(channel, true); | 124 | self.inner.enable_channel(channel, true); |
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index e643722aa..dc8ceb725 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs | |||
| @@ -6,7 +6,9 @@ | |||
| 6 | //! | 6 | //! |
| 7 | //! The available functionality depends on the timer type. | 7 | //! The available functionality depends on the timer type. |
| 8 | 8 | ||
| 9 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 9 | use core::mem::ManuallyDrop; |
| 10 | |||
| 11 | use embassy_hal_internal::Peri; | ||
| 10 | // Re-export useful enums | 12 | // Re-export useful enums |
| 11 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; |
| 12 | 14 | ||
| @@ -93,11 +95,11 @@ impl CountingMode { | |||
| 93 | impl From<CountingMode> for (vals::Cms, vals::Dir) { | 95 | impl From<CountingMode> for (vals::Cms, vals::Dir) { |
| 94 | fn from(value: CountingMode) -> Self { | 96 | fn from(value: CountingMode) -> Self { |
| 95 | match value { | 97 | match value { |
| 96 | CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP), | 98 | CountingMode::EdgeAlignedUp => (vals::Cms::EDGE_ALIGNED, vals::Dir::UP), |
| 97 | CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN), | 99 | CountingMode::EdgeAlignedDown => (vals::Cms::EDGE_ALIGNED, vals::Dir::DOWN), |
| 98 | CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP), | 100 | CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTER_ALIGNED1, vals::Dir::UP), |
| 99 | CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP), | 101 | CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTER_ALIGNED2, vals::Dir::UP), |
| 100 | CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP), | 102 | CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTER_ALIGNED3, vals::Dir::UP), |
| 101 | } | 103 | } |
| 102 | } | 104 | } |
| 103 | } | 105 | } |
| @@ -105,11 +107,11 @@ impl From<CountingMode> for (vals::Cms, vals::Dir) { | |||
| 105 | impl From<(vals::Cms, vals::Dir)> for CountingMode { | 107 | impl From<(vals::Cms, vals::Dir)> for CountingMode { |
| 106 | fn from(value: (vals::Cms, vals::Dir)) -> Self { | 108 | fn from(value: (vals::Cms, vals::Dir)) -> Self { |
| 107 | match value { | 109 | match value { |
| 108 | (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, | 110 | (vals::Cms::EDGE_ALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, |
| 109 | (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, | 111 | (vals::Cms::EDGE_ALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, |
| 110 | (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, | 112 | (vals::Cms::CENTER_ALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, |
| 111 | (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, | 113 | (vals::Cms::CENTER_ALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, |
| 112 | (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, | 114 | (vals::Cms::CENTER_ALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, |
| 113 | } | 115 | } |
| 114 | } | 116 | } |
| 115 | } | 117 | } |
| @@ -148,13 +150,13 @@ impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm { | |||
| 148 | fn from(mode: OutputCompareMode) -> Self { | 150 | fn from(mode: OutputCompareMode) -> Self { |
| 149 | match mode { | 151 | match mode { |
| 150 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, | 152 | OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, |
| 151 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, | 153 | OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVE_ON_MATCH, |
| 152 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, | 154 | OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVE_ON_MATCH, |
| 153 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, | 155 | OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, |
| 154 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, | 156 | OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCE_INACTIVE, |
| 155 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, | 157 | OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCE_ACTIVE, |
| 156 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, | 158 | OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWM_MODE1, |
| 157 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, | 159 | OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWM_MODE2, |
| 158 | } | 160 | } |
| 159 | } | 161 | } |
| 160 | } | 162 | } |
| @@ -179,7 +181,7 @@ impl From<OutputPolarity> for bool { | |||
| 179 | 181 | ||
| 180 | /// Low-level timer driver. | 182 | /// Low-level timer driver. |
| 181 | pub struct Timer<'d, T: CoreInstance> { | 183 | pub struct Timer<'d, T: CoreInstance> { |
| 182 | tim: PeripheralRef<'d, T>, | 184 | tim: Peri<'d, T>, |
| 183 | } | 185 | } |
| 184 | 186 | ||
| 185 | impl<'d, T: CoreInstance> Drop for Timer<'d, T> { | 187 | impl<'d, T: CoreInstance> Drop for Timer<'d, T> { |
| @@ -190,14 +192,17 @@ impl<'d, T: CoreInstance> Drop for Timer<'d, T> { | |||
| 190 | 192 | ||
| 191 | impl<'d, T: CoreInstance> Timer<'d, T> { | 193 | impl<'d, T: CoreInstance> Timer<'d, T> { |
| 192 | /// Create a new timer driver. | 194 | /// Create a new timer driver. |
| 193 | pub fn new(tim: impl Peripheral<P = T> + 'd) -> Self { | 195 | pub fn new(tim: Peri<'d, T>) -> Self { |
| 194 | into_ref!(tim); | ||
| 195 | |||
| 196 | rcc::enable_and_reset::<T>(); | 196 | rcc::enable_and_reset::<T>(); |
| 197 | 197 | ||
| 198 | Self { tim } | 198 | Self { tim } |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop<Self> { | ||
| 202 | let tim = unsafe { self.tim.clone_unchecked() }; | ||
| 203 | ManuallyDrop::new(Self { tim }) | ||
| 204 | } | ||
| 205 | |||
| 201 | /// Get access to the virutal core 16bit timer registers. | 206 | /// Get access to the virutal core 16bit timer registers. |
| 202 | /// | 207 | /// |
| 203 | /// Note: This works even if the timer is more capable, because registers | 208 | /// Note: This works even if the timer is more capable, because registers |
| @@ -228,6 +233,11 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 228 | self.regs_core().cnt().write(|r| r.set_cnt(0)); | 233 | self.regs_core().cnt().write(|r| r.set_cnt(0)); |
| 229 | } | 234 | } |
| 230 | 235 | ||
| 236 | /// get the capability of the timer | ||
| 237 | pub fn bits(&self) -> TimerBits { | ||
| 238 | T::BITS | ||
| 239 | } | ||
| 240 | |||
| 231 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. | 241 | /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. |
| 232 | /// | 242 | /// |
| 233 | /// This means that in the default edge-aligned mode, | 243 | /// This means that in the default edge-aligned mode, |
| @@ -235,16 +245,28 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 235 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved | 245 | /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved |
| 236 | /// because it needs to count up and down. | 246 | /// because it needs to count up and down. |
| 237 | pub fn set_frequency(&self, frequency: Hertz) { | 247 | pub fn set_frequency(&self, frequency: Hertz) { |
| 248 | match T::BITS { | ||
| 249 | TimerBits::Bits16 => { | ||
| 250 | self.set_frequency_internal(frequency, 16); | ||
| 251 | } | ||
| 252 | #[cfg(not(stm32l0))] | ||
| 253 | TimerBits::Bits32 => { | ||
| 254 | self.set_frequency_internal(frequency, 32); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { | ||
| 238 | let f = frequency.0; | 260 | let f = frequency.0; |
| 239 | assert!(f > 0); | 261 | assert!(f > 0); |
| 240 | let timer_f = T::frequency().0; | 262 | let timer_f = T::frequency().0; |
| 241 | 263 | ||
| 264 | let pclk_ticks_per_timer_period = (timer_f / f) as u64; | ||
| 265 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); | ||
| 266 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); | ||
| 267 | |||
| 242 | match T::BITS { | 268 | match T::BITS { |
| 243 | TimerBits::Bits16 => { | 269 | TimerBits::Bits16 => { |
| 244 | let pclk_ticks_per_timer_period = timer_f / f; | ||
| 245 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); | ||
| 246 | let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1); | ||
| 247 | |||
| 248 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | 270 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 249 | let arr = unwrap!(u16::try_from(divide_by - 1)); | 271 | let arr = unwrap!(u16::try_from(divide_by - 1)); |
| 250 | 272 | ||
| @@ -252,16 +274,12 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 252 | regs.psc().write_value(psc); | 274 | regs.psc().write_value(psc); |
| 253 | regs.arr().write(|r| r.set_arr(arr)); | 275 | regs.arr().write(|r| r.set_arr(arr)); |
| 254 | 276 | ||
| 255 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); | 277 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 256 | regs.egr().write(|r| r.set_ug(true)); | 278 | regs.egr().write(|r| r.set_ug(true)); |
| 257 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); | 279 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 258 | } | 280 | } |
| 259 | #[cfg(not(stm32l0))] | 281 | #[cfg(not(stm32l0))] |
| 260 | TimerBits::Bits32 => { | 282 | TimerBits::Bits32 => { |
| 261 | let pclk_ticks_per_timer_period = (timer_f / f) as u64; | ||
| 262 | let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); | ||
| 263 | let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); | ||
| 264 | |||
| 265 | // the timer counts `0..=arr`, we want it to count `0..divide_by` | 283 | // the timer counts `0..=arr`, we want it to count `0..divide_by` |
| 266 | let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); | 284 | let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); |
| 267 | 285 | ||
| @@ -269,9 +287,9 @@ impl<'d, T: CoreInstance> Timer<'d, T> { | |||
| 269 | regs.psc().write_value(psc); | 287 | regs.psc().write_value(psc); |
| 270 | regs.arr().write_value(arr); | 288 | regs.arr().write_value(arr); |
| 271 | 289 | ||
| 272 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); | 290 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); |
| 273 | regs.egr().write(|r| r.set_ug(true)); | 291 | regs.egr().write(|r| r.set_ug(true)); |
| 274 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); | 292 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); |
| 275 | } | 293 | } |
| 276 | } | 294 | } |
| 277 | } | 295 | } |
| @@ -405,6 +423,36 @@ impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { | |||
| 405 | TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), | 423 | TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), |
| 406 | } | 424 | } |
| 407 | } | 425 | } |
| 426 | |||
| 427 | /// Set the max compare value. | ||
| 428 | /// | ||
| 429 | /// An update event is generated to load the new value. The update event is | ||
| 430 | /// generated such that it will not cause an interrupt or DMA request. | ||
| 431 | pub fn set_max_compare_value(&self, ticks: u32) { | ||
| 432 | match T::BITS { | ||
| 433 | TimerBits::Bits16 => { | ||
| 434 | let arr = unwrap!(u16::try_from(ticks)); | ||
| 435 | |||
| 436 | let regs = self.regs_1ch(); | ||
| 437 | regs.arr().write(|r| r.set_arr(arr)); | ||
| 438 | |||
| 439 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 440 | regs.egr().write(|r| r.set_ug(true)); | ||
| 441 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 442 | } | ||
| 443 | #[cfg(not(stm32l0))] | ||
| 444 | TimerBits::Bits32 => { | ||
| 445 | let arr = ticks; | ||
| 446 | |||
| 447 | let regs = self.regs_gp32_unchecked(); | ||
| 448 | regs.arr().write_value(arr); | ||
| 449 | |||
| 450 | regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); | ||
| 451 | regs.egr().write(|r| r.set_ug(true)); | ||
| 452 | regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); | ||
| 453 | } | ||
| 454 | } | ||
| 455 | } | ||
| 408 | } | 456 | } |
| 409 | 457 | ||
| 410 | impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { | 458 | impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { |
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 25782ee13..b29382fc8 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs | |||
| @@ -2,12 +2,14 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::PeripheralType; | ||
| 5 | use embassy_sync::waitqueue::AtomicWaker; | 6 | use embassy_sync::waitqueue::AtomicWaker; |
| 6 | 7 | ||
| 7 | #[cfg(not(stm32l0))] | 8 | #[cfg(not(stm32l0))] |
| 8 | pub mod complementary_pwm; | 9 | pub mod complementary_pwm; |
| 9 | pub mod input_capture; | 10 | pub mod input_capture; |
| 10 | pub mod low_level; | 11 | pub mod low_level; |
| 12 | pub mod one_pulse; | ||
| 11 | pub mod pwm_input; | 13 | pub mod pwm_input; |
| 12 | pub mod qei; | 14 | pub mod qei; |
| 13 | pub mod simple_pwm; | 15 | pub mod simple_pwm; |
| @@ -40,6 +42,15 @@ impl Channel { | |||
| 40 | } | 42 | } |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 45 | /// Channel 1 marker type. | ||
| 46 | pub enum Ch1 {} | ||
| 47 | /// Channel 2 marker type. | ||
| 48 | pub enum Ch2 {} | ||
| 49 | /// Channel 3 marker type. | ||
| 50 | pub enum Ch3 {} | ||
| 51 | /// Channel 4 marker type. | ||
| 52 | pub enum Ch4 {} | ||
| 53 | |||
| 43 | /// Amount of bits of a timer. | 54 | /// Amount of bits of a timer. |
| 44 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 55 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 56 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -58,15 +69,14 @@ struct State { | |||
| 58 | 69 | ||
| 59 | impl State { | 70 | impl State { |
| 60 | const fn new() -> Self { | 71 | const fn new() -> Self { |
| 61 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | ||
| 62 | Self { | 72 | Self { |
| 63 | up_waker: NEW_AW, | 73 | up_waker: AtomicWaker::new(), |
| 64 | cc_waker: [NEW_AW; 4], | 74 | cc_waker: [const { AtomicWaker::new() }; 4], |
| 65 | } | 75 | } |
| 66 | } | 76 | } |
| 67 | } | 77 | } |
| 68 | 78 | ||
| 69 | trait SealedInstance: RccPeripheral { | 79 | trait SealedInstance: RccPeripheral + PeripheralType { |
| 70 | /// Async state for this timer | 80 | /// Async state for this timer |
| 71 | fn state() -> &'static State; | 81 | fn state() -> &'static State; |
| 72 | } | 82 | } |
diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs new file mode 100644 index 000000000..933165ef9 --- /dev/null +++ b/embassy-stm32/src/timer/one_pulse.rs | |||
| @@ -0,0 +1,383 @@ | |||
| 1 | //! One pulse mode driver. | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::mem::ManuallyDrop; | ||
| 6 | use core::pin::Pin; | ||
| 7 | use core::task::{Context, Poll}; | ||
| 8 | |||
| 9 | use super::low_level::{ | ||
| 10 | CountingMode, FilterValue, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource, | ||
| 11 | }; | ||
| 12 | use super::{ | ||
| 13 | CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, ExternalTriggerPin, GeneralInstance4Channel, | ||
| 14 | }; | ||
| 15 | pub use super::{Ch1, Ch2}; | ||
| 16 | use crate::gpio::{AfType, AnyPin, Pull}; | ||
| 17 | use crate::interrupt::typelevel::{Binding, Interrupt}; | ||
| 18 | use crate::pac::timer::vals::Etp; | ||
| 19 | use crate::time::Hertz; | ||
| 20 | use crate::Peri; | ||
| 21 | |||
| 22 | /// External input marker type. | ||
| 23 | pub enum Ext {} | ||
| 24 | |||
| 25 | /// External trigger pin trigger polarity. | ||
| 26 | #[derive(Clone, Copy)] | ||
| 27 | pub enum ExternalTriggerPolarity { | ||
| 28 | /// Rising edge only. | ||
| 29 | Rising, | ||
| 30 | /// Falling edge only. | ||
| 31 | Falling, | ||
| 32 | } | ||
| 33 | |||
| 34 | impl From<ExternalTriggerPolarity> for Etp { | ||
| 35 | fn from(mode: ExternalTriggerPolarity) -> Self { | ||
| 36 | match mode { | ||
| 37 | ExternalTriggerPolarity::Rising => 0.into(), | ||
| 38 | ExternalTriggerPolarity::Falling => 1.into(), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Trigger pin wrapper. | ||
| 44 | /// | ||
| 45 | /// This wraps a pin to make it usable as a timer trigger. | ||
| 46 | pub struct TriggerPin<'d, T, C> { | ||
| 47 | _pin: Peri<'d, AnyPin>, | ||
| 48 | phantom: PhantomData<(T, C)>, | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! channel_impl { | ||
| 52 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | ||
| 53 | impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, $channel> { | ||
| 54 | #[doc = concat!("Create a new ", stringify!($channel), " trigger pin instance.")] | ||
| 55 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, pull: Pull) -> Self { | ||
| 56 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | ||
| 57 | TriggerPin { | ||
| 58 | _pin: pin.into(), | ||
| 59 | phantom: PhantomData, | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | } | ||
| 65 | |||
| 66 | channel_impl!(new_ch1, Ch1, Channel1Pin); | ||
| 67 | channel_impl!(new_ch2, Ch2, Channel2Pin); | ||
| 68 | channel_impl!(new_ext, Ext, ExternalTriggerPin); | ||
| 69 | |||
| 70 | /// One pulse driver. | ||
| 71 | /// | ||
| 72 | /// Generates a pulse after a trigger and some configurable delay. | ||
| 73 | pub struct OnePulse<'d, T: GeneralInstance4Channel> { | ||
| 74 | inner: Timer<'d, T>, | ||
| 75 | } | ||
| 76 | |||
| 77 | impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { | ||
| 78 | /// Create a new one pulse driver. | ||
| 79 | /// | ||
| 80 | /// The pulse is triggered by a channel 1 input pin on both rising and | ||
| 81 | /// falling edges. Channel 1 will unusable as an output. | ||
| 82 | pub fn new_ch1_edge_detect( | ||
| 83 | tim: Peri<'d, T>, | ||
| 84 | _pin: TriggerPin<'d, T, Ch1>, | ||
| 85 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | ||
| 86 | freq: Hertz, | ||
| 87 | pulse_end: u32, | ||
| 88 | counting_mode: CountingMode, | ||
| 89 | ) -> Self { | ||
| 90 | let mut this = Self { inner: Timer::new(tim) }; | ||
| 91 | |||
| 92 | this.inner.set_trigger_source(TriggerSource::TI1F_ED); | ||
| 93 | this.inner | ||
| 94 | .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); | ||
| 95 | this.inner | ||
| 96 | .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); | ||
| 97 | this.new_inner(freq, pulse_end, counting_mode); | ||
| 98 | |||
| 99 | this | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Create a new one pulse driver. | ||
| 103 | /// | ||
| 104 | /// The pulse is triggered by a channel 1 input pin. Channel 1 will unusable | ||
| 105 | /// as an output. | ||
| 106 | pub fn new_ch1( | ||
| 107 | tim: Peri<'d, T>, | ||
| 108 | _pin: TriggerPin<'d, T, Ch1>, | ||
| 109 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | ||
| 110 | freq: Hertz, | ||
| 111 | pulse_end: u32, | ||
| 112 | counting_mode: CountingMode, | ||
| 113 | capture_mode: InputCaptureMode, | ||
| 114 | ) -> Self { | ||
| 115 | let mut this = Self { inner: Timer::new(tim) }; | ||
| 116 | |||
| 117 | this.inner.set_trigger_source(TriggerSource::TI1FP1); | ||
| 118 | this.inner | ||
| 119 | .set_input_ti_selection(Channel::Ch1, InputTISelection::Normal); | ||
| 120 | this.inner | ||
| 121 | .set_input_capture_filter(Channel::Ch1, FilterValue::NO_FILTER); | ||
| 122 | this.inner.set_input_capture_mode(Channel::Ch1, capture_mode); | ||
| 123 | this.new_inner(freq, pulse_end, counting_mode); | ||
| 124 | |||
| 125 | this | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Create a new one pulse driver. | ||
| 129 | /// | ||
| 130 | /// The pulse is triggered by a channel 2 input pin. Channel 2 will unusable | ||
| 131 | /// as an output. | ||
| 132 | pub fn new_ch2( | ||
| 133 | tim: Peri<'d, T>, | ||
| 134 | _pin: TriggerPin<'d, T, Ch1>, | ||
| 135 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | ||
| 136 | freq: Hertz, | ||
| 137 | pulse_end: u32, | ||
| 138 | counting_mode: CountingMode, | ||
| 139 | capture_mode: InputCaptureMode, | ||
| 140 | ) -> Self { | ||
| 141 | let mut this = Self { inner: Timer::new(tim) }; | ||
| 142 | |||
| 143 | this.inner.set_trigger_source(TriggerSource::TI2FP2); | ||
| 144 | this.inner | ||
| 145 | .set_input_ti_selection(Channel::Ch2, InputTISelection::Normal); | ||
| 146 | this.inner | ||
| 147 | .set_input_capture_filter(Channel::Ch2, FilterValue::NO_FILTER); | ||
| 148 | this.inner.set_input_capture_mode(Channel::Ch2, capture_mode); | ||
| 149 | this.new_inner(freq, pulse_end, counting_mode); | ||
| 150 | |||
| 151 | this | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Create a new one pulse driver. | ||
| 155 | /// | ||
| 156 | /// The pulse is triggered by a external trigger input pin. | ||
| 157 | pub fn new_ext( | ||
| 158 | tim: Peri<'d, T>, | ||
| 159 | _pin: TriggerPin<'d, T, Ext>, | ||
| 160 | _irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd, | ||
| 161 | freq: Hertz, | ||
| 162 | pulse_end: u32, | ||
| 163 | counting_mode: CountingMode, | ||
| 164 | polarity: ExternalTriggerPolarity, | ||
| 165 | ) -> Self { | ||
| 166 | let mut this = Self { inner: Timer::new(tim) }; | ||
| 167 | |||
| 168 | this.inner.regs_gp16().smcr().modify(|r| { | ||
| 169 | r.set_etp(polarity.into()); | ||
| 170 | // No pre-scaling | ||
| 171 | r.set_etps(0.into()); | ||
| 172 | // No filtering | ||
| 173 | r.set_etf(FilterValue::NO_FILTER); | ||
| 174 | }); | ||
| 175 | this.inner.set_trigger_source(TriggerSource::ETRF); | ||
| 176 | this.new_inner(freq, pulse_end, counting_mode); | ||
| 177 | |||
| 178 | this | ||
| 179 | } | ||
| 180 | |||
| 181 | fn new_inner(&mut self, freq: Hertz, pulse_end: u32, counting_mode: CountingMode) { | ||
| 182 | self.inner.set_counting_mode(counting_mode); | ||
| 183 | self.inner.set_tick_freq(freq); | ||
| 184 | self.inner.set_max_compare_value(pulse_end); | ||
| 185 | self.inner.regs_core().cr1().modify(|r| r.set_opm(true)); | ||
| 186 | // Required for advanced timers, see GeneralInstance4Channel for details | ||
| 187 | self.inner.enable_outputs(); | ||
| 188 | self.inner.set_slave_mode(SlaveMode::TRIGGER_MODE); | ||
| 189 | |||
| 190 | T::CaptureCompareInterrupt::unpend(); | ||
| 191 | unsafe { T::CaptureCompareInterrupt::enable() }; | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Get the end of the pulse in ticks from the trigger. | ||
| 195 | pub fn pulse_end(&self) -> u32 { | ||
| 196 | let max = self.inner.get_max_compare_value(); | ||
| 197 | assert!(max < u32::MAX); | ||
| 198 | max + 1 | ||
| 199 | } | ||
| 200 | |||
| 201 | /// Set the end of the pulse in ticks from the trigger. | ||
| 202 | pub fn set_pulse_end(&mut self, ticks: u32) { | ||
| 203 | self.inner.set_max_compare_value(ticks) | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Reset the timer on each trigger | ||
| 207 | #[cfg(not(stm32l0))] | ||
| 208 | pub fn set_reset_on_trigger(&mut self, reset: bool) { | ||
| 209 | let slave_mode = if reset { | ||
| 210 | SlaveMode::COMBINED_RESET_TRIGGER | ||
| 211 | } else { | ||
| 212 | SlaveMode::TRIGGER_MODE | ||
| 213 | }; | ||
| 214 | self.inner.set_slave_mode(slave_mode); | ||
| 215 | } | ||
| 216 | |||
| 217 | /// Get a single channel | ||
| 218 | /// | ||
| 219 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 220 | pub fn channel(&mut self, channel: Channel) -> OnePulseChannel<'_, T> { | ||
| 221 | OnePulseChannel { | ||
| 222 | inner: unsafe { self.inner.clone_unchecked() }, | ||
| 223 | channel, | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | /// Channel 1 | ||
| 228 | /// | ||
| 229 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 230 | /// | ||
| 231 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 232 | pub fn ch1(&mut self) -> OnePulseChannel<'_, T> { | ||
| 233 | self.channel(Channel::Ch1) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Channel 2 | ||
| 237 | /// | ||
| 238 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 239 | /// | ||
| 240 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 241 | pub fn ch2(&mut self) -> OnePulseChannel<'_, T> { | ||
| 242 | self.channel(Channel::Ch2) | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Channel 3 | ||
| 246 | /// | ||
| 247 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 248 | /// | ||
| 249 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 250 | pub fn ch3(&mut self) -> OnePulseChannel<'_, T> { | ||
| 251 | self.channel(Channel::Ch3) | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Channel 4 | ||
| 255 | /// | ||
| 256 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 257 | /// | ||
| 258 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 259 | pub fn ch4(&mut self) -> OnePulseChannel<'_, T> { | ||
| 260 | self.channel(Channel::Ch4) | ||
| 261 | } | ||
| 262 | |||
| 263 | /// Splits a [`OnePulse`] into four output channels. | ||
| 264 | // TODO: I hate the name "split" | ||
| 265 | pub fn split(self) -> OnePulseChannels<'static, T> | ||
| 266 | where | ||
| 267 | // must be static because the timer will never be dropped/disabled | ||
| 268 | 'd: 'static, | ||
| 269 | { | ||
| 270 | // without this, the timer would be disabled at the end of this function | ||
| 271 | let timer = ManuallyDrop::new(self.inner); | ||
| 272 | |||
| 273 | let ch = |channel| OnePulseChannel { | ||
| 274 | inner: unsafe { timer.clone_unchecked() }, | ||
| 275 | channel, | ||
| 276 | }; | ||
| 277 | |||
| 278 | OnePulseChannels { | ||
| 279 | ch1: ch(Channel::Ch1), | ||
| 280 | ch2: ch(Channel::Ch2), | ||
| 281 | ch3: ch(Channel::Ch3), | ||
| 282 | ch4: ch(Channel::Ch4), | ||
| 283 | } | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | /// A group of four [`OnePulseChannel`]s, obtained from [`OnePulse::split`]. | ||
| 288 | pub struct OnePulseChannels<'d, T: GeneralInstance4Channel> { | ||
| 289 | /// Channel 1 | ||
| 290 | pub ch1: OnePulseChannel<'d, T>, | ||
| 291 | /// Channel 2 | ||
| 292 | pub ch2: OnePulseChannel<'d, T>, | ||
| 293 | /// Channel 3 | ||
| 294 | pub ch3: OnePulseChannel<'d, T>, | ||
| 295 | /// Channel 4 | ||
| 296 | pub ch4: OnePulseChannel<'d, T>, | ||
| 297 | } | ||
| 298 | |||
| 299 | /// A single channel of a one pulse-configured timer, obtained from | ||
| 300 | /// [`OnePulse::split`],[`OnePulse::channel`], [`OnePulse::ch1`], etc. | ||
| 301 | /// | ||
| 302 | /// It is not possible to change the pulse end tick because the end tick | ||
| 303 | /// configuration is shared with all four channels. | ||
| 304 | pub struct OnePulseChannel<'d, T: GeneralInstance4Channel> { | ||
| 305 | inner: ManuallyDrop<Timer<'d, T>>, | ||
| 306 | channel: Channel, | ||
| 307 | } | ||
| 308 | |||
| 309 | impl<'d, T: GeneralInstance4Channel> OnePulseChannel<'d, T> { | ||
| 310 | /// Get the end of the pulse in ticks from the trigger. | ||
| 311 | pub fn pulse_end(&self) -> u32 { | ||
| 312 | let max = self.inner.get_max_compare_value(); | ||
| 313 | assert!(max < u32::MAX); | ||
| 314 | max + 1 | ||
| 315 | } | ||
| 316 | |||
| 317 | /// Get the width of the pulse in ticks. | ||
| 318 | pub fn pulse_width(&mut self) -> u32 { | ||
| 319 | self.pulse_end().saturating_sub(self.pulse_delay()) | ||
| 320 | } | ||
| 321 | |||
| 322 | /// Get the start of the pulse in ticks from the trigger. | ||
| 323 | pub fn pulse_delay(&mut self) -> u32 { | ||
| 324 | self.inner.get_compare_value(self.channel) | ||
| 325 | } | ||
| 326 | |||
| 327 | /// Set the start of the pulse in ticks from the trigger. | ||
| 328 | pub fn set_pulse_delay(&mut self, delay: u32) { | ||
| 329 | assert!(delay <= self.pulse_end()); | ||
| 330 | self.inner.set_compare_value(self.channel, delay); | ||
| 331 | } | ||
| 332 | |||
| 333 | /// Set the pulse width in ticks. | ||
| 334 | pub fn set_pulse_width(&mut self, width: u32) { | ||
| 335 | assert!(width <= self.pulse_end()); | ||
| 336 | self.set_pulse_delay(self.pulse_end() - width); | ||
| 337 | } | ||
| 338 | |||
| 339 | /// Waits until the trigger and following delay has passed. | ||
| 340 | pub async fn wait_for_pulse_start(&mut self) { | ||
| 341 | self.inner.enable_input_interrupt(self.channel, true); | ||
| 342 | |||
| 343 | OnePulseFuture::<T> { | ||
| 344 | channel: self.channel, | ||
| 345 | phantom: PhantomData, | ||
| 346 | } | ||
| 347 | .await | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 352 | struct OnePulseFuture<T: GeneralInstance4Channel> { | ||
| 353 | channel: Channel, | ||
| 354 | phantom: PhantomData<T>, | ||
| 355 | } | ||
| 356 | |||
| 357 | impl<'d, T: GeneralInstance4Channel> Drop for OnePulseFuture<T> { | ||
| 358 | fn drop(&mut self) { | ||
| 359 | critical_section::with(|_| { | ||
| 360 | let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; | ||
| 361 | |||
| 362 | // disable interrupt enable | ||
| 363 | regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); | ||
| 364 | }); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | impl<'d, T: GeneralInstance4Channel> Future for OnePulseFuture<T> { | ||
| 369 | type Output = (); | ||
| 370 | |||
| 371 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 372 | T::state().cc_waker[self.channel.index()].register(cx.waker()); | ||
| 373 | |||
| 374 | let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; | ||
| 375 | |||
| 376 | let dier = regs.dier().read(); | ||
| 377 | if !dier.ccie(self.channel.index()) { | ||
| 378 | Poll::Ready(()) | ||
| 379 | } else { | ||
| 380 | Poll::Pending | ||
| 381 | } | ||
| 382 | } | ||
| 383 | } | ||
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index e3eb6042a..98b798634 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs | |||
| @@ -1,12 +1,10 @@ | |||
| 1 | //! PWM Input driver. | 1 | //! PWM Input driver. |
| 2 | 2 | ||
| 3 | use embassy_hal_internal::into_ref; | ||
| 4 | |||
| 5 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; | 3 | use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; |
| 6 | use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; | 4 | use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; |
| 7 | use crate::gpio::{AfType, Pull}; | 5 | use crate::gpio::{AfType, Pull}; |
| 8 | use crate::time::Hertz; | 6 | use crate::time::Hertz; |
| 9 | use crate::Peripheral; | 7 | use crate::Peri; |
| 10 | 8 | ||
| 11 | /// PWM Input driver. | 9 | /// PWM Input driver. |
| 12 | pub struct PwmInput<'d, T: GeneralInstance4Channel> { | 10 | pub struct PwmInput<'d, T: GeneralInstance4Channel> { |
| @@ -16,34 +14,20 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> { | |||
| 16 | 14 | ||
| 17 | impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { | 15 | impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { |
| 18 | /// Create a new PWM input driver. | 16 | /// Create a new PWM input driver. |
| 19 | pub fn new( | 17 | pub fn new(tim: Peri<'d, T>, pin: Peri<'d, impl Channel1Pin<T>>, pull: Pull, freq: Hertz) -> Self { |
| 20 | tim: impl Peripheral<P = T> + 'd, | ||
| 21 | pin: impl Peripheral<P = impl Channel1Pin<T>> + 'd, | ||
| 22 | pull: Pull, | ||
| 23 | freq: Hertz, | ||
| 24 | ) -> Self { | ||
| 25 | into_ref!(pin); | ||
| 26 | |||
| 27 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 18 | pin.set_as_af(pin.af_num(), AfType::input(pull)); |
| 28 | 19 | ||
| 29 | Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) | 20 | Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) |
| 30 | } | 21 | } |
| 31 | 22 | ||
| 32 | /// Create a new PWM input driver. | 23 | /// Create a new PWM input driver. |
| 33 | pub fn new_alt( | 24 | pub fn new_alt(tim: Peri<'d, T>, pin: Peri<'d, impl Channel2Pin<T>>, pull: Pull, freq: Hertz) -> Self { |
| 34 | tim: impl Peripheral<P = T> + 'd, | ||
| 35 | pin: impl Peripheral<P = impl Channel2Pin<T>> + 'd, | ||
| 36 | pull: Pull, | ||
| 37 | freq: Hertz, | ||
| 38 | ) -> Self { | ||
| 39 | into_ref!(pin); | ||
| 40 | |||
| 41 | pin.set_as_af(pin.af_num(), AfType::input(pull)); | 25 | pin.set_as_af(pin.af_num(), AfType::input(pull)); |
| 42 | 26 | ||
| 43 | Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) | 27 | Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) |
| 44 | } | 28 | } |
| 45 | 29 | ||
| 46 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { | 30 | fn new_inner(tim: Peri<'d, T>, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { |
| 47 | let mut inner = Timer::new(tim); | 31 | let mut inner = Timer::new(tim); |
| 48 | 32 | ||
| 49 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); | 33 | inner.set_counting_mode(CountingMode::EdgeAlignedUp); |
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index fc5835414..f3c81667c 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs | |||
| @@ -2,13 +2,13 @@ | |||
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | ||
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 6 | use stm32_metapac::timer::vals; | 5 | use stm32_metapac::timer::vals; |
| 7 | 6 | ||
| 8 | use super::low_level::Timer; | 7 | use super::low_level::Timer; |
| 8 | pub use super::{Ch1, Ch2}; | ||
| 9 | use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; | 9 | use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; |
| 10 | use crate::gpio::{AfType, AnyPin, Pull}; | 10 | use crate::gpio::{AfType, AnyPin, Pull}; |
| 11 | use crate::Peripheral; | 11 | use crate::Peri; |
| 12 | 12 | ||
| 13 | /// Counting direction | 13 | /// Counting direction |
| 14 | pub enum Direction { | 14 | pub enum Direction { |
| @@ -18,14 +18,9 @@ pub enum Direction { | |||
| 18 | Downcounting, | 18 | Downcounting, |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | /// Channel 1 marker type. | ||
| 22 | pub enum Ch1 {} | ||
| 23 | /// Channel 2 marker type. | ||
| 24 | pub enum Ch2 {} | ||
| 25 | |||
| 26 | /// Wrapper for using a pin with QEI. | 21 | /// Wrapper for using a pin with QEI. |
| 27 | pub struct QeiPin<'d, T, Channel> { | 22 | pub struct QeiPin<'d, T, Channel> { |
| 28 | _pin: PeripheralRef<'d, AnyPin>, | 23 | _pin: Peri<'d, AnyPin>, |
| 29 | phantom: PhantomData<(T, Channel)>, | 24 | phantom: PhantomData<(T, Channel)>, |
| 30 | } | 25 | } |
| 31 | 26 | ||
| @@ -33,14 +28,13 @@ macro_rules! channel_impl { | |||
| 33 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 28 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { |
| 34 | impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { | 29 | impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { |
| 35 | #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] | 30 | #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] |
| 36 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self { | 31 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>) -> Self { |
| 37 | into_ref!(pin); | ||
| 38 | critical_section::with(|_| { | 32 | critical_section::with(|_| { |
| 39 | pin.set_low(); | 33 | pin.set_low(); |
| 40 | pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); | 34 | pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); |
| 41 | }); | 35 | }); |
| 42 | QeiPin { | 36 | QeiPin { |
| 43 | _pin: pin.map_into(), | 37 | _pin: pin.into(), |
| 44 | phantom: PhantomData, | 38 | phantom: PhantomData, |
| 45 | } | 39 | } |
| 46 | } | 40 | } |
| @@ -58,11 +52,11 @@ pub struct Qei<'d, T: GeneralInstance4Channel> { | |||
| 58 | 52 | ||
| 59 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { | 53 | impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { |
| 60 | /// Create a new quadrature decoder driver. | 54 | /// Create a new quadrature decoder driver. |
| 61 | pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { | 55 | pub fn new(tim: Peri<'d, T>, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { |
| 62 | Self::new_inner(tim) | 56 | Self::new_inner(tim) |
| 63 | } | 57 | } |
| 64 | 58 | ||
| 65 | fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self { | 59 | fn new_inner(tim: Peri<'d, T>) -> Self { |
| 66 | let inner = Timer::new(tim); | 60 | let inner = Timer::new(tim); |
| 67 | let r = inner.regs_gp16(); | 61 | let r = inner.regs_gp16(); |
| 68 | 62 | ||
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index b7771bd64..f7f433154 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs | |||
| @@ -1,14 +1,15 @@ | |||
| 1 | //! Simple PWM driver. | 1 | //! Simple PWM driver. |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | 4 | use core::mem::ManuallyDrop; | |
| 5 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 6 | 5 | ||
| 7 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 8 | use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; | 7 | use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, TimerBits}; |
| 8 | #[cfg(gpio_v2)] | ||
| 9 | use crate::gpio::Pull; | ||
| 9 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 10 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; |
| 10 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| 11 | use crate::Peripheral; | 12 | use crate::Peri; |
| 12 | 13 | ||
| 13 | /// Channel 1 marker type. | 14 | /// Channel 1 marker type. |
| 14 | pub enum Ch1 {} | 15 | pub enum Ch1 {} |
| @@ -23,22 +24,54 @@ pub enum Ch4 {} | |||
| 23 | /// | 24 | /// |
| 24 | /// This wraps a pin to make it usable with PWM. | 25 | /// This wraps a pin to make it usable with PWM. |
| 25 | pub struct PwmPin<'d, T, C> { | 26 | pub struct PwmPin<'d, T, C> { |
| 26 | _pin: PeripheralRef<'d, AnyPin>, | 27 | _pin: Peri<'d, AnyPin>, |
| 27 | phantom: PhantomData<(T, C)>, | 28 | phantom: PhantomData<(T, C)>, |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 31 | /// PWM pin config | ||
| 32 | /// | ||
| 33 | /// This configures the pwm pin settings | ||
| 34 | #[derive(Debug, Copy, Clone)] | ||
| 35 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 36 | pub struct PwmPinConfig { | ||
| 37 | /// PWM Pin output type | ||
| 38 | pub output_type: OutputType, | ||
| 39 | /// PWM Pin speed | ||
| 40 | pub speed: Speed, | ||
| 41 | /// PWM Pin pull type | ||
| 42 | #[cfg(gpio_v2)] | ||
| 43 | pub pull: Pull, | ||
| 44 | } | ||
| 45 | |||
| 30 | macro_rules! channel_impl { | 46 | macro_rules! channel_impl { |
| 31 | ($new_chx:ident, $channel:ident, $pin_trait:ident) => { | 47 | ($new_chx:ident, $new_chx_with_config:ident, $channel:ident, $pin_trait:ident) => { |
| 32 | impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { | 48 | impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { |
| 33 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] | 49 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] |
| 34 | pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self { | 50 | pub fn $new_chx(pin: Peri<'d, impl $pin_trait<T>>, output_type: OutputType) -> Self { |
| 35 | into_ref!(pin); | ||
| 36 | critical_section::with(|_| { | 51 | critical_section::with(|_| { |
| 37 | pin.set_low(); | 52 | pin.set_low(); |
| 38 | pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); | 53 | pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); |
| 39 | }); | 54 | }); |
| 40 | PwmPin { | 55 | PwmPin { |
| 41 | _pin: pin.map_into(), | 56 | _pin: pin.into(), |
| 57 | phantom: PhantomData, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance with config.")] | ||
| 62 | pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait<T>>, pin_config: PwmPinConfig) -> Self { | ||
| 63 | critical_section::with(|_| { | ||
| 64 | pin.set_low(); | ||
| 65 | pin.set_as_af( | ||
| 66 | pin.af_num(), | ||
| 67 | #[cfg(gpio_v1)] | ||
| 68 | AfType::output(pin_config.output_type, pin_config.speed), | ||
| 69 | #[cfg(gpio_v2)] | ||
| 70 | AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), | ||
| 71 | ); | ||
| 72 | }); | ||
| 73 | PwmPin { | ||
| 74 | _pin: pin.into(), | ||
| 42 | phantom: PhantomData, | 75 | phantom: PhantomData, |
| 43 | } | 76 | } |
| 44 | } | 77 | } |
| @@ -46,10 +79,115 @@ macro_rules! channel_impl { | |||
| 46 | }; | 79 | }; |
| 47 | } | 80 | } |
| 48 | 81 | ||
| 49 | channel_impl!(new_ch1, Ch1, Channel1Pin); | 82 | channel_impl!(new_ch1, new_ch1_with_config, Ch1, Channel1Pin); |
| 50 | channel_impl!(new_ch2, Ch2, Channel2Pin); | 83 | channel_impl!(new_ch2, new_ch2_with_config, Ch2, Channel2Pin); |
| 51 | channel_impl!(new_ch3, Ch3, Channel3Pin); | 84 | channel_impl!(new_ch3, new_ch3_with_config, Ch3, Channel3Pin); |
| 52 | channel_impl!(new_ch4, Ch4, Channel4Pin); | 85 | channel_impl!(new_ch4, new_ch4_with_config, Ch4, Channel4Pin); |
| 86 | |||
| 87 | /// A single channel of a pwm, obtained from [`SimplePwm::split`], | ||
| 88 | /// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. | ||
| 89 | /// | ||
| 90 | /// It is not possible to change the pwm frequency because | ||
| 91 | /// the frequency configuration is shared with all four channels. | ||
| 92 | pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { | ||
| 93 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 94 | channel: Channel, | ||
| 95 | } | ||
| 96 | |||
| 97 | // TODO: check for RMW races | ||
| 98 | impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { | ||
| 99 | /// Enable the given channel. | ||
| 100 | pub fn enable(&mut self) { | ||
| 101 | self.timer.enable_channel(self.channel, true); | ||
| 102 | } | ||
| 103 | |||
| 104 | /// Disable the given channel. | ||
| 105 | pub fn disable(&mut self) { | ||
| 106 | self.timer.enable_channel(self.channel, false); | ||
| 107 | } | ||
| 108 | |||
| 109 | /// Check whether given channel is enabled | ||
| 110 | pub fn is_enabled(&self) -> bool { | ||
| 111 | self.timer.get_channel_enable_state(self.channel) | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Get max duty value. | ||
| 115 | /// | ||
| 116 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | ||
| 117 | pub fn max_duty_cycle(&self) -> u16 { | ||
| 118 | let max = self.timer.get_max_compare_value(); | ||
| 119 | assert!(max < u16::MAX as u32); | ||
| 120 | max as u16 + 1 | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Set the duty for a given channel. | ||
| 124 | /// | ||
| 125 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. | ||
| 126 | pub fn set_duty_cycle(&mut self, duty: u16) { | ||
| 127 | assert!(duty <= (*self).max_duty_cycle()); | ||
| 128 | self.timer.set_compare_value(self.channel, duty.into()) | ||
| 129 | } | ||
| 130 | |||
| 131 | /// Set the duty cycle to 0%, or always inactive. | ||
| 132 | pub fn set_duty_cycle_fully_off(&mut self) { | ||
| 133 | self.set_duty_cycle(0); | ||
| 134 | } | ||
| 135 | |||
| 136 | /// Set the duty cycle to 100%, or always active. | ||
| 137 | pub fn set_duty_cycle_fully_on(&mut self) { | ||
| 138 | self.set_duty_cycle((*self).max_duty_cycle()); | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Set the duty cycle to `num / denom`. | ||
| 142 | /// | ||
| 143 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, | ||
| 144 | /// and that `denom` is not zero. | ||
| 145 | pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { | ||
| 146 | assert!(denom != 0); | ||
| 147 | assert!(num <= denom); | ||
| 148 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); | ||
| 149 | |||
| 150 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) | ||
| 151 | #[allow(clippy::cast_possible_truncation)] | ||
| 152 | self.set_duty_cycle(duty as u16); | ||
| 153 | } | ||
| 154 | |||
| 155 | /// Set the duty cycle to `percent / 100` | ||
| 156 | /// | ||
| 157 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. | ||
| 158 | pub fn set_duty_cycle_percent(&mut self, percent: u8) { | ||
| 159 | self.set_duty_cycle_fraction(u16::from(percent), 100) | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Get the duty for a given channel. | ||
| 163 | /// | ||
| 164 | /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. | ||
| 165 | pub fn current_duty_cycle(&self) -> u16 { | ||
| 166 | unwrap!(self.timer.get_compare_value(self.channel).try_into()) | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Set the output polarity for a given channel. | ||
| 170 | pub fn set_polarity(&mut self, polarity: OutputPolarity) { | ||
| 171 | self.timer.set_output_polarity(self.channel, polarity); | ||
| 172 | } | ||
| 173 | |||
| 174 | /// Set the output compare mode for a given channel. | ||
| 175 | pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { | ||
| 176 | self.timer.set_output_compare_mode(self.channel, mode); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. | ||
| 181 | pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> { | ||
| 182 | /// Channel 1 | ||
| 183 | pub ch1: SimplePwmChannel<'d, T>, | ||
| 184 | /// Channel 2 | ||
| 185 | pub ch2: SimplePwmChannel<'d, T>, | ||
| 186 | /// Channel 3 | ||
| 187 | pub ch3: SimplePwmChannel<'d, T>, | ||
| 188 | /// Channel 4 | ||
| 189 | pub ch4: SimplePwmChannel<'d, T>, | ||
| 190 | } | ||
| 53 | 191 | ||
| 54 | /// Simple PWM driver. | 192 | /// Simple PWM driver. |
| 55 | pub struct SimplePwm<'d, T: GeneralInstance4Channel> { | 193 | pub struct SimplePwm<'d, T: GeneralInstance4Channel> { |
| @@ -59,7 +197,7 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> { | |||
| 59 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | 197 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { |
| 60 | /// Create a new simple PWM driver. | 198 | /// Create a new simple PWM driver. |
| 61 | pub fn new( | 199 | pub fn new( |
| 62 | tim: impl Peripheral<P = T> + 'd, | 200 | tim: Peri<'d, T>, |
| 63 | _ch1: Option<PwmPin<'d, T, Ch1>>, | 201 | _ch1: Option<PwmPin<'d, T, Ch1>>, |
| 64 | _ch2: Option<PwmPin<'d, T, Ch2>>, | 202 | _ch2: Option<PwmPin<'d, T, Ch2>>, |
| 65 | _ch3: Option<PwmPin<'d, T, Ch3>>, | 203 | _ch3: Option<PwmPin<'d, T, Ch3>>, |
| @@ -70,7 +208,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 70 | Self::new_inner(tim, freq, counting_mode) | 208 | Self::new_inner(tim, freq, counting_mode) |
| 71 | } | 209 | } |
| 72 | 210 | ||
| 73 | fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self { | 211 | fn new_inner(tim: Peri<'d, T>, freq: Hertz, counting_mode: CountingMode) -> Self { |
| 74 | let mut this = Self { inner: Timer::new(tim) }; | 212 | let mut this = Self { inner: Timer::new(tim) }; |
| 75 | 213 | ||
| 76 | this.inner.set_counting_mode(counting_mode); | 214 | this.inner.set_counting_mode(counting_mode); |
| @@ -89,19 +227,76 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 89 | this | 227 | this |
| 90 | } | 228 | } |
| 91 | 229 | ||
| 92 | /// Enable the given channel. | 230 | /// Get a single channel |
| 93 | pub fn enable(&mut self, channel: Channel) { | 231 | /// |
| 94 | self.inner.enable_channel(channel, true); | 232 | /// If you need to use multiple channels, use [`Self::split`]. |
| 233 | pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { | ||
| 234 | SimplePwmChannel { | ||
| 235 | timer: unsafe { self.inner.clone_unchecked() }, | ||
| 236 | channel, | ||
| 237 | } | ||
| 95 | } | 238 | } |
| 96 | 239 | ||
| 97 | /// Disable the given channel. | 240 | /// Channel 1 |
| 98 | pub fn disable(&mut self, channel: Channel) { | 241 | /// |
| 99 | self.inner.enable_channel(channel, false); | 242 | /// This is just a convenience wrapper around [`Self::channel`]. |
| 243 | /// | ||
| 244 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 245 | pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 246 | self.channel(Channel::Ch1) | ||
| 100 | } | 247 | } |
| 101 | 248 | ||
| 102 | /// Check whether given channel is enabled | 249 | /// Channel 2 |
| 103 | pub fn is_enabled(&self, channel: Channel) -> bool { | 250 | /// |
| 104 | self.inner.get_channel_enable_state(channel) | 251 | /// This is just a convenience wrapper around [`Self::channel`]. |
| 252 | /// | ||
| 253 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 254 | pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 255 | self.channel(Channel::Ch2) | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Channel 3 | ||
| 259 | /// | ||
| 260 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 261 | /// | ||
| 262 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 263 | pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 264 | self.channel(Channel::Ch3) | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Channel 4 | ||
| 268 | /// | ||
| 269 | /// This is just a convenience wrapper around [`Self::channel`]. | ||
| 270 | /// | ||
| 271 | /// If you need to use multiple channels, use [`Self::split`]. | ||
| 272 | pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { | ||
| 273 | self.channel(Channel::Ch4) | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Splits a [`SimplePwm`] into four pwm channels. | ||
| 277 | /// | ||
| 278 | /// This returns all four channels, including channels that | ||
| 279 | /// aren't configured with a [`PwmPin`]. | ||
| 280 | // TODO: I hate the name "split" | ||
| 281 | pub fn split(self) -> SimplePwmChannels<'static, T> | ||
| 282 | where | ||
| 283 | // must be static because the timer will never be dropped/disabled | ||
| 284 | 'd: 'static, | ||
| 285 | { | ||
| 286 | // without this, the timer would be disabled at the end of this function | ||
| 287 | let timer = ManuallyDrop::new(self.inner); | ||
| 288 | |||
| 289 | let ch = |channel| SimplePwmChannel { | ||
| 290 | timer: unsafe { timer.clone_unchecked() }, | ||
| 291 | channel, | ||
| 292 | }; | ||
| 293 | |||
| 294 | SimplePwmChannels { | ||
| 295 | ch1: ch(Channel::Ch1), | ||
| 296 | ch2: ch(Channel::Ch2), | ||
| 297 | ch3: ch(Channel::Ch3), | ||
| 298 | ch4: ch(Channel::Ch4), | ||
| 299 | } | ||
| 105 | } | 300 | } |
| 106 | 301 | ||
| 107 | /// Set PWM frequency. | 302 | /// Set PWM frequency. |
| @@ -109,63 +304,34 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 109 | /// Note: when you call this, the max duty value changes, so you will have to | 304 | /// Note: when you call this, the max duty value changes, so you will have to |
| 110 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. | 305 | /// call `set_duty` on all channels with the duty calculated based on the new max duty. |
| 111 | pub fn set_frequency(&mut self, freq: Hertz) { | 306 | pub fn set_frequency(&mut self, freq: Hertz) { |
| 307 | // TODO: prevent ARR = u16::MAX? | ||
| 112 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { | 308 | let multiplier = if self.inner.get_counting_mode().is_center_aligned() { |
| 113 | 2u8 | 309 | 2u8 |
| 114 | } else { | 310 | } else { |
| 115 | 1u8 | 311 | 1u8 |
| 116 | }; | 312 | }; |
| 117 | self.inner.set_frequency(freq * multiplier); | 313 | self.inner.set_frequency_internal(freq * multiplier, 16); |
| 118 | } | 314 | } |
| 119 | 315 | ||
| 120 | /// Get max duty value. | 316 | /// Get max duty value. |
| 121 | /// | 317 | /// |
| 122 | /// This value depends on the configured frequency and the timer's clock rate from RCC. | 318 | /// This value depends on the configured frequency and the timer's clock rate from RCC. |
| 123 | pub fn get_max_duty(&self) -> u32 { | 319 | pub fn max_duty_cycle(&self) -> u16 { |
| 124 | self.inner.get_max_compare_value() + 1 | 320 | let max = self.inner.get_max_compare_value(); |
| 125 | } | 321 | assert!(max < u16::MAX as u32); |
| 126 | 322 | max as u16 + 1 | |
| 127 | /// Set the duty for a given channel. | ||
| 128 | /// | ||
| 129 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 130 | pub fn set_duty(&mut self, channel: Channel, duty: u32) { | ||
| 131 | assert!(duty <= self.get_max_duty()); | ||
| 132 | self.inner.set_compare_value(channel, duty) | ||
| 133 | } | ||
| 134 | |||
| 135 | /// Get the duty for a given channel. | ||
| 136 | /// | ||
| 137 | /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. | ||
| 138 | pub fn get_duty(&self, channel: Channel) -> u32 { | ||
| 139 | self.inner.get_compare_value(channel) | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Set the output polarity for a given channel. | ||
| 143 | pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { | ||
| 144 | self.inner.set_output_polarity(channel, polarity); | ||
| 145 | } | ||
| 146 | |||
| 147 | /// Set the output compare mode for a given channel. | ||
| 148 | pub fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { | ||
| 149 | self.inner.set_output_compare_mode(channel, mode); | ||
| 150 | } | 323 | } |
| 151 | 324 | ||
| 152 | /// Generate a sequence of PWM waveform | 325 | /// Generate a sequence of PWM waveform |
| 153 | /// | 326 | /// |
| 154 | /// Note: | 327 | /// Note: |
| 155 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | 328 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. |
| 156 | pub async fn waveform_up( | 329 | pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { |
| 157 | &mut self, | ||
| 158 | dma: impl Peripheral<P = impl super::UpDma<T>>, | ||
| 159 | channel: Channel, | ||
| 160 | duty: &[u16], | ||
| 161 | ) { | ||
| 162 | into_ref!(dma); | ||
| 163 | |||
| 164 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 330 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 165 | let req = dma.request(); | 331 | let req = dma.request(); |
| 166 | 332 | ||
| 167 | let original_duty_state = self.get_duty(channel); | 333 | let original_duty_state = self.channel(channel).current_duty_cycle(); |
| 168 | let original_enable_state = self.is_enabled(channel); | 334 | let original_enable_state = self.channel(channel).is_enabled(); |
| 169 | let original_update_dma_state = self.inner.get_update_dma_state(); | 335 | let original_update_dma_state = self.inner.get_update_dma_state(); |
| 170 | 336 | ||
| 171 | if !original_update_dma_state { | 337 | if !original_update_dma_state { |
| @@ -173,7 +339,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 173 | } | 339 | } |
| 174 | 340 | ||
| 175 | if !original_enable_state { | 341 | if !original_enable_state { |
| 176 | self.enable(channel); | 342 | self.channel(channel).enable(); |
| 177 | } | 343 | } |
| 178 | 344 | ||
| 179 | unsafe { | 345 | unsafe { |
| @@ -190,10 +356,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 190 | }; | 356 | }; |
| 191 | 357 | ||
| 192 | Transfer::new_write( | 358 | Transfer::new_write( |
| 193 | &mut dma, | 359 | dma, |
| 194 | req, | 360 | req, |
| 195 | duty, | 361 | duty, |
| 196 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _, | 362 | self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, |
| 197 | dma_transfer_option, | 363 | dma_transfer_option, |
| 198 | ) | 364 | ) |
| 199 | .await | 365 | .await |
| @@ -201,10 +367,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 201 | 367 | ||
| 202 | // restore output compare state | 368 | // restore output compare state |
| 203 | if !original_enable_state { | 369 | if !original_enable_state { |
| 204 | self.disable(channel); | 370 | self.channel(channel).disable(); |
| 205 | } | 371 | } |
| 206 | 372 | ||
| 207 | self.set_duty(channel, original_duty_state); | 373 | self.channel(channel).set_duty_cycle(original_duty_state); |
| 208 | 374 | ||
| 209 | // Since DMA is closed before timer update event trigger DMA is turn off, | 375 | // Since DMA is closed before timer update event trigger DMA is turn off, |
| 210 | // this can almost always trigger a DMA FIFO error. | 376 | // this can almost always trigger a DMA FIFO error. |
| @@ -215,33 +381,111 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | |||
| 215 | self.inner.enable_update_dma(false); | 381 | self.inner.enable_update_dma(false); |
| 216 | } | 382 | } |
| 217 | } | 383 | } |
| 384 | |||
| 385 | /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. | ||
| 386 | /// | ||
| 387 | /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers | ||
| 388 | /// in sequence on each update event (UEV). The data is written via the DMAR register using the | ||
| 389 | /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. | ||
| 390 | /// | ||
| 391 | /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row | ||
| 392 | /// represents a single update event and each column corresponds to a specific timer channel (starting | ||
| 393 | /// from `starting_channel` up to and including `ending_channel`). | ||
| 394 | /// | ||
| 395 | /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: | ||
| 396 | /// | ||
| 397 | /// let dma_buf: [u16; 16] = [ | ||
| 398 | /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 | ||
| 399 | /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 | ||
| 400 | /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 | ||
| 401 | /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 | ||
| 402 | /// ]; | ||
| 403 | /// | ||
| 404 | /// Each group of N values (where N = number of channels) is transferred on one update event, | ||
| 405 | /// updating the duty cycles of all selected channels simultaneously. | ||
| 406 | /// | ||
| 407 | /// Note: | ||
| 408 | /// you will need to provide corresponding TIMx_UP DMA channel to use this method. | ||
| 409 | pub async fn waveform_up_multi_channel( | ||
| 410 | &mut self, | ||
| 411 | dma: Peri<'_, impl super::UpDma<T>>, | ||
| 412 | starting_channel: Channel, | ||
| 413 | ending_channel: Channel, | ||
| 414 | duty: &[u16], | ||
| 415 | ) { | ||
| 416 | let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; | ||
| 417 | let start_ch_index = starting_channel.index(); | ||
| 418 | let end_ch_index = ending_channel.index(); | ||
| 419 | |||
| 420 | assert!(start_ch_index <= end_ch_index); | ||
| 421 | |||
| 422 | let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; | ||
| 423 | self.inner | ||
| 424 | .regs_gp16() | ||
| 425 | .dcr() | ||
| 426 | .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); | ||
| 427 | self.inner | ||
| 428 | .regs_gp16() | ||
| 429 | .dcr() | ||
| 430 | .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); | ||
| 431 | |||
| 432 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | ||
| 433 | let req = dma.request(); | ||
| 434 | |||
| 435 | let original_update_dma_state = self.inner.get_update_dma_state(); | ||
| 436 | if !original_update_dma_state { | ||
| 437 | self.inner.enable_update_dma(true); | ||
| 438 | } | ||
| 439 | |||
| 440 | unsafe { | ||
| 441 | #[cfg(not(any(bdma, gpdma)))] | ||
| 442 | use crate::dma::{Burst, FifoThreshold}; | ||
| 443 | use crate::dma::{Transfer, TransferOptions}; | ||
| 444 | |||
| 445 | let dma_transfer_option = TransferOptions { | ||
| 446 | #[cfg(not(any(bdma, gpdma)))] | ||
| 447 | fifo_threshold: Some(FifoThreshold::Full), | ||
| 448 | #[cfg(not(any(bdma, gpdma)))] | ||
| 449 | mburst: Burst::Incr4, | ||
| 450 | ..Default::default() | ||
| 451 | }; | ||
| 452 | |||
| 453 | Transfer::new_write( | ||
| 454 | dma, | ||
| 455 | req, | ||
| 456 | duty, | ||
| 457 | self.inner.regs_gp16().dmar().as_ptr() as *mut u16, | ||
| 458 | dma_transfer_option, | ||
| 459 | ) | ||
| 460 | .await | ||
| 461 | }; | ||
| 462 | |||
| 463 | if !original_update_dma_state { | ||
| 464 | self.inner.enable_update_dma(false); | ||
| 465 | } | ||
| 466 | } | ||
| 218 | } | 467 | } |
| 219 | 468 | ||
| 220 | macro_rules! impl_waveform_chx { | 469 | macro_rules! impl_waveform_chx { |
| 221 | ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { | 470 | ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { |
| 222 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { | 471 | impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { |
| 223 | /// Generate a sequence of PWM waveform | 472 | /// Generate a sequence of PWM waveform |
| 224 | /// | 473 | pub async fn $fn_name(&mut self, dma: Peri<'_, impl super::$dma_ch<T>>, duty: &[u16]) { |
| 225 | /// Note: | ||
| 226 | /// you will need to provide corresponding TIMx_CHy DMA channel to use this method. | ||
| 227 | pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) { | ||
| 228 | use crate::pac::timer::vals::Ccds; | 474 | use crate::pac::timer::vals::Ccds; |
| 229 | 475 | ||
| 230 | into_ref!(dma); | ||
| 231 | |||
| 232 | #[allow(clippy::let_unit_value)] // eg. stm32f334 | 476 | #[allow(clippy::let_unit_value)] // eg. stm32f334 |
| 233 | let req = dma.request(); | 477 | let req = dma.request(); |
| 234 | 478 | ||
| 235 | let cc_channel = Channel::$cc_ch; | 479 | let cc_channel = Channel::$cc_ch; |
| 236 | 480 | ||
| 237 | let original_duty_state = self.get_duty(cc_channel); | 481 | let original_duty_state = self.channel(cc_channel).current_duty_cycle(); |
| 238 | let original_enable_state = self.is_enabled(cc_channel); | 482 | let original_enable_state = self.channel(cc_channel).is_enabled(); |
| 239 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; | 483 | let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; |
| 240 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); | 484 | let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); |
| 241 | 485 | ||
| 242 | // redirect CC DMA request onto Update Event | 486 | // redirect CC DMA request onto Update Event |
| 243 | if !original_cc_dma_on_update { | 487 | if !original_cc_dma_on_update { |
| 244 | self.inner.set_cc_dma_selection(Ccds::ONUPDATE) | 488 | self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) |
| 245 | } | 489 | } |
| 246 | 490 | ||
| 247 | if !original_cc_dma_enabled { | 491 | if !original_cc_dma_enabled { |
| @@ -249,7 +493,7 @@ macro_rules! impl_waveform_chx { | |||
| 249 | } | 493 | } |
| 250 | 494 | ||
| 251 | if !original_enable_state { | 495 | if !original_enable_state { |
| 252 | self.enable(cc_channel); | 496 | self.channel(cc_channel).enable(); |
| 253 | } | 497 | } |
| 254 | 498 | ||
| 255 | unsafe { | 499 | unsafe { |
| @@ -265,22 +509,41 @@ macro_rules! impl_waveform_chx { | |||
| 265 | ..Default::default() | 509 | ..Default::default() |
| 266 | }; | 510 | }; |
| 267 | 511 | ||
| 268 | Transfer::new_write( | 512 | match self.inner.bits() { |
| 269 | &mut dma, | 513 | TimerBits::Bits16 => { |
| 270 | req, | 514 | Transfer::new_write( |
| 271 | duty, | 515 | dma, |
| 272 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _, | 516 | req, |
| 273 | dma_transfer_option, | 517 | duty, |
| 274 | ) | 518 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, |
| 275 | .await | 519 | dma_transfer_option, |
| 520 | ) | ||
| 521 | .await | ||
| 522 | } | ||
| 523 | #[cfg(not(any(stm32l0)))] | ||
| 524 | TimerBits::Bits32 => { | ||
| 525 | #[cfg(not(any(bdma, gpdma)))] | ||
| 526 | panic!("unsupported timer bits"); | ||
| 527 | |||
| 528 | #[cfg(any(bdma, gpdma))] | ||
| 529 | Transfer::new_write( | ||
| 530 | dma, | ||
| 531 | req, | ||
| 532 | duty, | ||
| 533 | self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, | ||
| 534 | dma_transfer_option, | ||
| 535 | ) | ||
| 536 | .await | ||
| 537 | } | ||
| 538 | }; | ||
| 276 | }; | 539 | }; |
| 277 | 540 | ||
| 278 | // restore output compare state | 541 | // restore output compare state |
| 279 | if !original_enable_state { | 542 | if !original_enable_state { |
| 280 | self.disable(cc_channel); | 543 | self.channel(cc_channel).disable(); |
| 281 | } | 544 | } |
| 282 | 545 | ||
| 283 | self.set_duty(cc_channel, original_duty_state); | 546 | self.channel(cc_channel).set_duty_cycle(original_duty_state); |
| 284 | 547 | ||
| 285 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, | 548 | // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, |
| 286 | // this can almost always trigger a DMA FIFO error. | 549 | // this can almost always trigger a DMA FIFO error. |
| @@ -292,7 +555,7 @@ macro_rules! impl_waveform_chx { | |||
| 292 | } | 555 | } |
| 293 | 556 | ||
| 294 | if !original_cc_dma_on_update { | 557 | if !original_cc_dma_on_update { |
| 295 | self.inner.set_cc_dma_selection(Ccds::ONCOMPARE) | 558 | self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) |
| 296 | } | 559 | } |
| 297 | } | 560 | } |
| 298 | } | 561 | } |
| @@ -304,6 +567,41 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); | |||
| 304 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); | 567 | impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); |
| 305 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); | 568 | impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); |
| 306 | 569 | ||
| 570 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { | ||
| 571 | type Error = core::convert::Infallible; | ||
| 572 | } | ||
| 573 | |||
| 574 | impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { | ||
| 575 | fn max_duty_cycle(&self) -> u16 { | ||
| 576 | self.max_duty_cycle() | ||
| 577 | } | ||
| 578 | |||
| 579 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { | ||
| 580 | self.set_duty_cycle(duty); | ||
| 581 | Ok(()) | ||
| 582 | } | ||
| 583 | |||
| 584 | fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { | ||
| 585 | self.set_duty_cycle_fully_off(); | ||
| 586 | Ok(()) | ||
| 587 | } | ||
| 588 | |||
| 589 | fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { | ||
| 590 | self.set_duty_cycle_fully_on(); | ||
| 591 | Ok(()) | ||
| 592 | } | ||
| 593 | |||
| 594 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { | ||
| 595 | self.set_duty_cycle_fraction(num, denom); | ||
| 596 | Ok(()) | ||
| 597 | } | ||
| 598 | |||
| 599 | fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { | ||
| 600 | self.set_duty_cycle_percent(percent); | ||
| 601 | Ok(()) | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 307 | impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { | 605 | impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { |
| 308 | type Channel = Channel; | 606 | type Channel = Channel; |
| 309 | type Time = Hertz; | 607 | type Time = Hertz; |
| @@ -330,7 +628,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { | |||
| 330 | } | 628 | } |
| 331 | 629 | ||
| 332 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { | 630 | fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { |
| 333 | assert!(duty <= self.get_max_duty()); | 631 | assert!(duty <= self.max_duty_cycle() as u32); |
| 334 | self.inner.set_compare_value(channel, duty) | 632 | self.inner.set_compare_value(channel, duty) |
| 335 | } | 633 | } |
| 336 | 634 | ||
diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs new file mode 100644 index 000000000..6791ef6c1 --- /dev/null +++ b/embassy-stm32/src/tsc/acquisition_banks.rs | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | use super::io_pin::*; | ||
| 2 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 3 | use super::pin_groups::G7; | ||
| 4 | #[cfg(tsc_v3)] | ||
| 5 | use super::pin_groups::G8; | ||
| 6 | use super::pin_groups::{pin_roles, G1, G2, G3, G4, G5, G6}; | ||
| 7 | use super::types::{Group, GroupStatus}; | ||
| 8 | use super::TSC_NUM_GROUPS; | ||
| 9 | |||
| 10 | /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. | ||
| 11 | /// | ||
| 12 | /// This struct holds optional `tsc::IOPin` values for each TSC group, allowing for flexible | ||
| 13 | /// configuration of TSC acquisition banks. Each field corresponds to a specific TSC group | ||
| 14 | /// and can be set to `Some(tsc::IOPin)` if that group is to be included in the acquisition, | ||
| 15 | /// or `None` if it should be excluded. | ||
| 16 | #[allow(missing_docs)] | ||
| 17 | #[derive(Default)] | ||
| 18 | pub struct AcquisitionBankPins { | ||
| 19 | pub g1_pin: Option<IOPinWithRole<G1, pin_roles::Channel>>, | ||
| 20 | pub g2_pin: Option<IOPinWithRole<G2, pin_roles::Channel>>, | ||
| 21 | pub g3_pin: Option<IOPinWithRole<G3, pin_roles::Channel>>, | ||
| 22 | pub g4_pin: Option<IOPinWithRole<G4, pin_roles::Channel>>, | ||
| 23 | pub g5_pin: Option<IOPinWithRole<G5, pin_roles::Channel>>, | ||
| 24 | pub g6_pin: Option<IOPinWithRole<G6, pin_roles::Channel>>, | ||
| 25 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 26 | pub g7_pin: Option<IOPinWithRole<G7, pin_roles::Channel>>, | ||
| 27 | #[cfg(tsc_v3)] | ||
| 28 | pub g8_pin: Option<IOPinWithRole<G8, pin_roles::Channel>>, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl AcquisitionBankPins { | ||
| 32 | /// Returns an iterator over the pins in this acquisition bank. | ||
| 33 | /// | ||
| 34 | /// This method allows for easy traversal of all configured pins in the bank. | ||
| 35 | pub fn iter(&self) -> AcquisitionBankPinsIterator { | ||
| 36 | AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Iterator for TSC acquisition banks. | ||
| 41 | /// | ||
| 42 | /// This iterator allows traversing through the pins of a `AcquisitionBankPins` struct, | ||
| 43 | /// yielding each configured pin in order of the TSC groups. | ||
| 44 | pub struct AcquisitionBankIterator<'a> { | ||
| 45 | pins: &'a AcquisitionBankPins, | ||
| 46 | current_group: u8, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl<'a> AcquisitionBankIterator<'a> { | ||
| 50 | fn new(pins: &'a AcquisitionBankPins) -> Self { | ||
| 51 | Self { pins, current_group: 0 } | ||
| 52 | } | ||
| 53 | |||
| 54 | fn next_pin(&mut self) -> Option<IOPin> { | ||
| 55 | while self.current_group < TSC_NUM_GROUPS as u8 { | ||
| 56 | let pin = match self.current_group { | ||
| 57 | 0 => self.pins.g1_pin.map(IOPinWithRole::get_pin), | ||
| 58 | 1 => self.pins.g2_pin.map(IOPinWithRole::get_pin), | ||
| 59 | 2 => self.pins.g3_pin.map(IOPinWithRole::get_pin), | ||
| 60 | 3 => self.pins.g4_pin.map(IOPinWithRole::get_pin), | ||
| 61 | 4 => self.pins.g5_pin.map(IOPinWithRole::get_pin), | ||
| 62 | 5 => self.pins.g6_pin.map(IOPinWithRole::get_pin), | ||
| 63 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 64 | 6 => self.pins.g7_pin.map(IOPinWithRole::get_pin), | ||
| 65 | #[cfg(tsc_v3)] | ||
| 66 | 7 => self.pins.g8_pin.map(IOPinWithRole::get_pin), | ||
| 67 | _ => None, | ||
| 68 | }; | ||
| 69 | self.current_group += 1; | ||
| 70 | if pin.is_some() { | ||
| 71 | return pin; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | None | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Iterator for TSC acquisition bank pins. | ||
| 79 | /// | ||
| 80 | /// This iterator yields `tsc::IOPin` values for each configured pin in the acquisition bank. | ||
| 81 | pub struct AcquisitionBankPinsIterator<'a>(AcquisitionBankIterator<'a>); | ||
| 82 | |||
| 83 | impl<'a> Iterator for AcquisitionBankPinsIterator<'a> { | ||
| 84 | type Item = IOPin; | ||
| 85 | |||
| 86 | fn next(&mut self) -> Option<Self::Item> { | ||
| 87 | self.0.next_pin() | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl AcquisitionBankPins { | ||
| 92 | /// Returns an iterator over the available pins in the bank | ||
| 93 | pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { | ||
| 94 | AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Represents a collection of TSC pins to be acquired simultaneously. | ||
| 99 | /// | ||
| 100 | /// This struct contains a set of pins to be used in a TSC acquisition with a pre-computed and | ||
| 101 | /// verified mask for efficiently setting up the TSC peripheral before performing an acquisition. | ||
| 102 | /// It ensures that only one channel pin per TSC group is included, adhering to hardware limitations. | ||
| 103 | pub struct AcquisitionBank { | ||
| 104 | pub(super) pins: AcquisitionBankPins, | ||
| 105 | pub(super) mask: u32, | ||
| 106 | } | ||
| 107 | |||
| 108 | impl AcquisitionBank { | ||
| 109 | /// Returns an iterator over the available pins in the bank. | ||
| 110 | pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { | ||
| 111 | self.pins.pins_iterator() | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Returns the mask for this bank. | ||
| 115 | pub fn mask(&self) -> u32 { | ||
| 116 | self.mask | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Retrieves the TSC I/O pin for a given group in this acquisition bank. | ||
| 120 | /// | ||
| 121 | /// # Arguments | ||
| 122 | /// * `group` - The TSC group to retrieve the pin for. | ||
| 123 | /// | ||
| 124 | /// # Returns | ||
| 125 | /// An `Option<tsc::IOPin>` containing the pin if it exists for the given group, or `None` if not. | ||
| 126 | pub fn get_pin(&self, group: Group) -> Option<IOPin> { | ||
| 127 | match group { | ||
| 128 | Group::One => self.pins.g1_pin.map(|p| p.pin), | ||
| 129 | Group::Two => self.pins.g2_pin.map(|p| p.pin), | ||
| 130 | Group::Three => self.pins.g3_pin.map(|p| p.pin), | ||
| 131 | Group::Four => self.pins.g4_pin.map(|p| p.pin), | ||
| 132 | Group::Five => self.pins.g5_pin.map(|p| p.pin), | ||
| 133 | Group::Six => self.pins.g6_pin.map(|p| p.pin), | ||
| 134 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 135 | Group::Seven => self.pins.g7_pin.map(|p| p.pin), | ||
| 136 | #[cfg(tsc_v3)] | ||
| 137 | Group::Eight => self.pins.g8_pin.map(|p| p.pin), | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Represents the status of all TSC groups in an acquisition bank | ||
| 143 | #[derive(Default)] | ||
| 144 | pub struct AcquisitionBankStatus { | ||
| 145 | pub(super) groups: [Option<GroupStatus>; TSC_NUM_GROUPS], | ||
| 146 | } | ||
| 147 | |||
| 148 | impl AcquisitionBankStatus { | ||
| 149 | /// Check if all groups in the bank are complete | ||
| 150 | pub fn all_complete(&self) -> bool { | ||
| 151 | self.groups | ||
| 152 | .iter() | ||
| 153 | .all(|&status| status.map_or(true, |s| s == GroupStatus::Complete)) | ||
| 154 | } | ||
| 155 | |||
| 156 | /// Check if any group in the bank is ongoing | ||
| 157 | pub fn any_ongoing(&self) -> bool { | ||
| 158 | self.groups.iter().any(|&status| status == Some(GroupStatus::Ongoing)) | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Get the status of a specific group, if the group is present in the bank | ||
| 162 | pub fn get_group_status(&self, group: Group) -> Option<GroupStatus> { | ||
| 163 | let index: usize = group.into(); | ||
| 164 | self.groups[index] | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Iterator for groups present in the bank | ||
| 168 | pub fn iter(&self) -> impl Iterator<Item = (Group, GroupStatus)> + '_ { | ||
| 169 | self.groups.iter().enumerate().filter_map(|(group_num, status)| { | ||
| 170 | status.and_then(|s| Group::try_from(group_num).ok().map(|group| (group, s))) | ||
| 171 | }) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Represents the result of a Touch Sensing Controller (TSC) acquisition for a specific pin. | ||
| 176 | /// | ||
| 177 | /// This struct contains a reference to the `tsc::IOPin` from which a value was read, | ||
| 178 | /// along with the actual sensor reading for that pin. It provides a convenient way | ||
| 179 | /// to associate TSC readings with their corresponding pins after an acquisition. | ||
| 180 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 181 | #[derive(Clone, Copy, Debug)] | ||
| 182 | pub struct ChannelReading { | ||
| 183 | /// The sensor reading value obtained from the TSC acquisition. | ||
| 184 | /// Lower values typically indicate a detected touch, while higher values indicate no touch. | ||
| 185 | pub sensor_value: u16, | ||
| 186 | |||
| 187 | /// The `tsc::IOPin` associated with this reading. | ||
| 188 | /// This allows for easy identification of which pin the reading corresponds to. | ||
| 189 | pub tsc_pin: IOPin, | ||
| 190 | } | ||
| 191 | |||
| 192 | /// Represents the readings from all TSC groups | ||
| 193 | #[derive(Default)] | ||
| 194 | pub struct AcquisitionBankReadings { | ||
| 195 | pub(super) groups: [Option<ChannelReading>; TSC_NUM_GROUPS], | ||
| 196 | } | ||
| 197 | |||
| 198 | impl AcquisitionBankReadings { | ||
| 199 | /// Get the reading for a specific group, if the group is present in the bank | ||
| 200 | pub fn get_group_reading(&self, group: Group) -> Option<ChannelReading> { | ||
| 201 | let index: usize = group.into(); | ||
| 202 | self.groups[index] | ||
| 203 | } | ||
| 204 | |||
| 205 | /// Iterator for readings for groups present in the bank | ||
| 206 | pub fn iter(&self) -> impl Iterator<Item = ChannelReading> + '_ { | ||
| 207 | self.groups.iter().filter_map(|&x| x) | ||
| 208 | } | ||
| 209 | } | ||
diff --git a/embassy-stm32/src/tsc/config.rs b/embassy-stm32/src/tsc/config.rs new file mode 100644 index 000000000..efa1f9a0d --- /dev/null +++ b/embassy-stm32/src/tsc/config.rs | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | /// Charge transfer pulse cycles | ||
| 2 | #[allow(missing_docs)] | ||
| 3 | #[derive(Copy, Clone, PartialEq)] | ||
| 4 | pub enum ChargeTransferPulseCycle { | ||
| 5 | _1, | ||
| 6 | _2, | ||
| 7 | _3, | ||
| 8 | _4, | ||
| 9 | _5, | ||
| 10 | _6, | ||
| 11 | _7, | ||
| 12 | _8, | ||
| 13 | _9, | ||
| 14 | _10, | ||
| 15 | _11, | ||
| 16 | _12, | ||
| 17 | _13, | ||
| 18 | _14, | ||
| 19 | _15, | ||
| 20 | _16, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl Into<u8> for ChargeTransferPulseCycle { | ||
| 24 | fn into(self) -> u8 { | ||
| 25 | match self { | ||
| 26 | ChargeTransferPulseCycle::_1 => 0, | ||
| 27 | ChargeTransferPulseCycle::_2 => 1, | ||
| 28 | ChargeTransferPulseCycle::_3 => 2, | ||
| 29 | ChargeTransferPulseCycle::_4 => 3, | ||
| 30 | ChargeTransferPulseCycle::_5 => 4, | ||
| 31 | ChargeTransferPulseCycle::_6 => 5, | ||
| 32 | ChargeTransferPulseCycle::_7 => 6, | ||
| 33 | ChargeTransferPulseCycle::_8 => 7, | ||
| 34 | ChargeTransferPulseCycle::_9 => 8, | ||
| 35 | ChargeTransferPulseCycle::_10 => 9, | ||
| 36 | ChargeTransferPulseCycle::_11 => 10, | ||
| 37 | ChargeTransferPulseCycle::_12 => 11, | ||
| 38 | ChargeTransferPulseCycle::_13 => 12, | ||
| 39 | ChargeTransferPulseCycle::_14 => 13, | ||
| 40 | ChargeTransferPulseCycle::_15 => 14, | ||
| 41 | ChargeTransferPulseCycle::_16 => 15, | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | /// Max count | ||
| 47 | #[allow(missing_docs)] | ||
| 48 | #[derive(Copy, Clone)] | ||
| 49 | pub enum MaxCount { | ||
| 50 | _255, | ||
| 51 | _511, | ||
| 52 | _1023, | ||
| 53 | _2047, | ||
| 54 | _4095, | ||
| 55 | _8191, | ||
| 56 | _16383, | ||
| 57 | } | ||
| 58 | |||
| 59 | impl Into<u8> for MaxCount { | ||
| 60 | fn into(self) -> u8 { | ||
| 61 | match self { | ||
| 62 | MaxCount::_255 => 0, | ||
| 63 | MaxCount::_511 => 1, | ||
| 64 | MaxCount::_1023 => 2, | ||
| 65 | MaxCount::_2047 => 3, | ||
| 66 | MaxCount::_4095 => 4, | ||
| 67 | MaxCount::_8191 => 5, | ||
| 68 | MaxCount::_16383 => 6, | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Prescaler divider | ||
| 74 | #[allow(missing_docs)] | ||
| 75 | #[derive(Copy, Clone, PartialEq)] | ||
| 76 | pub enum PGPrescalerDivider { | ||
| 77 | _1, | ||
| 78 | _2, | ||
| 79 | _4, | ||
| 80 | _8, | ||
| 81 | _16, | ||
| 82 | _32, | ||
| 83 | _64, | ||
| 84 | _128, | ||
| 85 | } | ||
| 86 | |||
| 87 | impl Into<u8> for PGPrescalerDivider { | ||
| 88 | fn into(self) -> u8 { | ||
| 89 | match self { | ||
| 90 | PGPrescalerDivider::_1 => 0, | ||
| 91 | PGPrescalerDivider::_2 => 1, | ||
| 92 | PGPrescalerDivider::_4 => 2, | ||
| 93 | PGPrescalerDivider::_8 => 3, | ||
| 94 | PGPrescalerDivider::_16 => 4, | ||
| 95 | PGPrescalerDivider::_32 => 5, | ||
| 96 | PGPrescalerDivider::_64 => 6, | ||
| 97 | PGPrescalerDivider::_128 => 7, | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | /// Error type for SSDeviation | ||
| 103 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 104 | pub enum SSDeviationError { | ||
| 105 | /// The provided value is too low (0) | ||
| 106 | ValueTooLow, | ||
| 107 | /// The provided value is too high (greater than 128) | ||
| 108 | ValueTooHigh, | ||
| 109 | } | ||
| 110 | |||
| 111 | /// Spread Spectrum Deviation | ||
| 112 | #[derive(Copy, Clone)] | ||
| 113 | pub struct SSDeviation(u8); | ||
| 114 | impl SSDeviation { | ||
| 115 | /// Create new deviation value, acceptable inputs are 1-128 | ||
| 116 | pub fn new(val: u8) -> Result<Self, SSDeviationError> { | ||
| 117 | if val == 0 { | ||
| 118 | return Err(SSDeviationError::ValueTooLow); | ||
| 119 | } else if val > 128 { | ||
| 120 | return Err(SSDeviationError::ValueTooHigh); | ||
| 121 | } | ||
| 122 | Ok(Self(val - 1)) | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl Into<u8> for SSDeviation { | ||
| 127 | fn into(self) -> u8 { | ||
| 128 | self.0 | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Peripheral configuration | ||
| 133 | #[derive(Clone, Copy)] | ||
| 134 | pub struct Config { | ||
| 135 | /// Duration of high state of the charge transfer pulse | ||
| 136 | pub ct_pulse_high_length: ChargeTransferPulseCycle, | ||
| 137 | /// Duration of the low state of the charge transfer pulse | ||
| 138 | pub ct_pulse_low_length: ChargeTransferPulseCycle, | ||
| 139 | /// Enable/disable of spread spectrum feature | ||
| 140 | pub spread_spectrum: bool, | ||
| 141 | /// Adds variable number of periods of the SS clk to pulse high state | ||
| 142 | pub spread_spectrum_deviation: SSDeviation, | ||
| 143 | /// Selects AHB clock divider used to generate SS clk | ||
| 144 | pub spread_spectrum_prescaler: bool, | ||
| 145 | /// Selects AHB clock divider used to generate pulse generator clk | ||
| 146 | pub pulse_generator_prescaler: PGPrescalerDivider, | ||
| 147 | /// Maximum number of charge transfer pulses that can be generated before error | ||
| 148 | pub max_count_value: MaxCount, | ||
| 149 | /// Defines config of all IOs when no ongoing acquisition | ||
| 150 | pub io_default_mode: bool, | ||
| 151 | /// Polarity of sync input pin | ||
| 152 | pub synchro_pin_polarity: bool, | ||
| 153 | /// Acquisition starts when start bit is set or with sync pin input | ||
| 154 | pub acquisition_mode: bool, | ||
| 155 | /// Enable max count interrupt | ||
| 156 | pub max_count_interrupt: bool, | ||
| 157 | } | ||
| 158 | |||
| 159 | impl Default for Config { | ||
| 160 | fn default() -> Self { | ||
| 161 | Self { | ||
| 162 | ct_pulse_high_length: ChargeTransferPulseCycle::_1, | ||
| 163 | ct_pulse_low_length: ChargeTransferPulseCycle::_1, | ||
| 164 | spread_spectrum: false, | ||
| 165 | spread_spectrum_deviation: SSDeviation::new(1).unwrap(), | ||
| 166 | spread_spectrum_prescaler: false, | ||
| 167 | pulse_generator_prescaler: PGPrescalerDivider::_1, | ||
| 168 | max_count_value: MaxCount::_255, | ||
| 169 | io_default_mode: false, | ||
| 170 | synchro_pin_polarity: false, | ||
| 171 | acquisition_mode: false, | ||
| 172 | max_count_interrupt: false, | ||
| 173 | } | ||
| 174 | } | ||
| 175 | } | ||
diff --git a/embassy-stm32/src/tsc/enums.rs b/embassy-stm32/src/tsc/enums.rs deleted file mode 100644 index 0d34a43ec..000000000 --- a/embassy-stm32/src/tsc/enums.rs +++ /dev/null | |||
| @@ -1,238 +0,0 @@ | |||
| 1 | use core::ops::BitOr; | ||
| 2 | |||
| 3 | /// Pin defines | ||
| 4 | #[allow(missing_docs)] | ||
| 5 | pub enum TscIOPin { | ||
| 6 | Group1Io1, | ||
| 7 | Group1Io2, | ||
| 8 | Group1Io3, | ||
| 9 | Group1Io4, | ||
| 10 | Group2Io1, | ||
| 11 | Group2Io2, | ||
| 12 | Group2Io3, | ||
| 13 | Group2Io4, | ||
| 14 | Group3Io1, | ||
| 15 | Group3Io2, | ||
| 16 | Group3Io3, | ||
| 17 | Group3Io4, | ||
| 18 | Group4Io1, | ||
| 19 | Group4Io2, | ||
| 20 | Group4Io3, | ||
| 21 | Group4Io4, | ||
| 22 | Group5Io1, | ||
| 23 | Group5Io2, | ||
| 24 | Group5Io3, | ||
| 25 | Group5Io4, | ||
| 26 | Group6Io1, | ||
| 27 | Group6Io2, | ||
| 28 | Group6Io3, | ||
| 29 | Group6Io4, | ||
| 30 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 31 | Group7Io1, | ||
| 32 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 33 | Group7Io2, | ||
| 34 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 35 | Group7Io3, | ||
| 36 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 37 | Group7Io4, | ||
| 38 | #[cfg(tsc_v3)] | ||
| 39 | Group8Io1, | ||
| 40 | #[cfg(tsc_v3)] | ||
| 41 | Group8Io2, | ||
| 42 | #[cfg(tsc_v3)] | ||
| 43 | Group8Io3, | ||
| 44 | #[cfg(tsc_v3)] | ||
| 45 | Group8Io4, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl BitOr<TscIOPin> for u32 { | ||
| 49 | type Output = u32; | ||
| 50 | fn bitor(self, rhs: TscIOPin) -> Self::Output { | ||
| 51 | let rhs: u32 = rhs.into(); | ||
| 52 | self | rhs | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | impl BitOr<u32> for TscIOPin { | ||
| 57 | type Output = u32; | ||
| 58 | fn bitor(self, rhs: u32) -> Self::Output { | ||
| 59 | let val: u32 = self.into(); | ||
| 60 | val | rhs | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | impl BitOr for TscIOPin { | ||
| 65 | type Output = u32; | ||
| 66 | fn bitor(self, rhs: Self) -> Self::Output { | ||
| 67 | let val: u32 = self.into(); | ||
| 68 | let rhs: u32 = rhs.into(); | ||
| 69 | val | rhs | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | impl Into<u32> for TscIOPin { | ||
| 74 | fn into(self) -> u32 { | ||
| 75 | match self { | ||
| 76 | TscIOPin::Group1Io1 => 0x00000001, | ||
| 77 | TscIOPin::Group1Io2 => 0x00000002, | ||
| 78 | TscIOPin::Group1Io3 => 0x00000004, | ||
| 79 | TscIOPin::Group1Io4 => 0x00000008, | ||
| 80 | TscIOPin::Group2Io1 => 0x00000010, | ||
| 81 | TscIOPin::Group2Io2 => 0x00000020, | ||
| 82 | TscIOPin::Group2Io3 => 0x00000040, | ||
| 83 | TscIOPin::Group2Io4 => 0x00000080, | ||
| 84 | TscIOPin::Group3Io1 => 0x00000100, | ||
| 85 | TscIOPin::Group3Io2 => 0x00000200, | ||
| 86 | TscIOPin::Group3Io3 => 0x00000400, | ||
| 87 | TscIOPin::Group3Io4 => 0x00000800, | ||
| 88 | TscIOPin::Group4Io1 => 0x00001000, | ||
| 89 | TscIOPin::Group4Io2 => 0x00002000, | ||
| 90 | TscIOPin::Group4Io3 => 0x00004000, | ||
| 91 | TscIOPin::Group4Io4 => 0x00008000, | ||
| 92 | TscIOPin::Group5Io1 => 0x00010000, | ||
| 93 | TscIOPin::Group5Io2 => 0x00020000, | ||
| 94 | TscIOPin::Group5Io3 => 0x00040000, | ||
| 95 | TscIOPin::Group5Io4 => 0x00080000, | ||
| 96 | TscIOPin::Group6Io1 => 0x00100000, | ||
| 97 | TscIOPin::Group6Io2 => 0x00200000, | ||
| 98 | TscIOPin::Group6Io3 => 0x00400000, | ||
| 99 | TscIOPin::Group6Io4 => 0x00800000, | ||
| 100 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 101 | TscIOPin::Group7Io1 => 0x01000000, | ||
| 102 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 103 | TscIOPin::Group7Io2 => 0x02000000, | ||
| 104 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 105 | TscIOPin::Group7Io3 => 0x04000000, | ||
| 106 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 107 | TscIOPin::Group7Io4 => 0x08000000, | ||
| 108 | #[cfg(tsc_v3)] | ||
| 109 | TscIOPin::Group8Io1 => 0x10000000, | ||
| 110 | #[cfg(tsc_v3)] | ||
| 111 | TscIOPin::Group8Io2 => 0x20000000, | ||
| 112 | #[cfg(tsc_v3)] | ||
| 113 | TscIOPin::Group8Io3 => 0x40000000, | ||
| 114 | #[cfg(tsc_v3)] | ||
| 115 | TscIOPin::Group8Io4 => 0x80000000, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Spread Spectrum Deviation | ||
| 121 | #[derive(Copy, Clone)] | ||
| 122 | pub struct SSDeviation(u8); | ||
| 123 | impl SSDeviation { | ||
| 124 | /// Create new deviation value, acceptable inputs are 1-128 | ||
| 125 | pub fn new(val: u8) -> Result<Self, ()> { | ||
| 126 | if val == 0 || val > 128 { | ||
| 127 | return Err(()); | ||
| 128 | } | ||
| 129 | Ok(Self(val - 1)) | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | impl Into<u8> for SSDeviation { | ||
| 134 | fn into(self) -> u8 { | ||
| 135 | self.0 | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Charge transfer pulse cycles | ||
| 140 | #[allow(missing_docs)] | ||
| 141 | #[derive(Copy, Clone, PartialEq)] | ||
| 142 | pub enum ChargeTransferPulseCycle { | ||
| 143 | _1, | ||
| 144 | _2, | ||
| 145 | _3, | ||
| 146 | _4, | ||
| 147 | _5, | ||
| 148 | _6, | ||
| 149 | _7, | ||
| 150 | _8, | ||
| 151 | _9, | ||
| 152 | _10, | ||
| 153 | _11, | ||
| 154 | _12, | ||
| 155 | _13, | ||
| 156 | _14, | ||
| 157 | _15, | ||
| 158 | _16, | ||
| 159 | } | ||
| 160 | |||
| 161 | impl Into<u8> for ChargeTransferPulseCycle { | ||
| 162 | fn into(self) -> u8 { | ||
| 163 | match self { | ||
| 164 | ChargeTransferPulseCycle::_1 => 0, | ||
| 165 | ChargeTransferPulseCycle::_2 => 1, | ||
| 166 | ChargeTransferPulseCycle::_3 => 2, | ||
| 167 | ChargeTransferPulseCycle::_4 => 3, | ||
| 168 | ChargeTransferPulseCycle::_5 => 4, | ||
| 169 | ChargeTransferPulseCycle::_6 => 5, | ||
| 170 | ChargeTransferPulseCycle::_7 => 6, | ||
| 171 | ChargeTransferPulseCycle::_8 => 7, | ||
| 172 | ChargeTransferPulseCycle::_9 => 8, | ||
| 173 | ChargeTransferPulseCycle::_10 => 9, | ||
| 174 | ChargeTransferPulseCycle::_11 => 10, | ||
| 175 | ChargeTransferPulseCycle::_12 => 11, | ||
| 176 | ChargeTransferPulseCycle::_13 => 12, | ||
| 177 | ChargeTransferPulseCycle::_14 => 13, | ||
| 178 | ChargeTransferPulseCycle::_15 => 14, | ||
| 179 | ChargeTransferPulseCycle::_16 => 15, | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Prescaler divider | ||
| 185 | #[allow(missing_docs)] | ||
| 186 | #[derive(Copy, Clone, PartialEq)] | ||
| 187 | pub enum PGPrescalerDivider { | ||
| 188 | _1, | ||
| 189 | _2, | ||
| 190 | _4, | ||
| 191 | _8, | ||
| 192 | _16, | ||
| 193 | _32, | ||
| 194 | _64, | ||
| 195 | _128, | ||
| 196 | } | ||
| 197 | |||
| 198 | impl Into<u8> for PGPrescalerDivider { | ||
| 199 | fn into(self) -> u8 { | ||
| 200 | match self { | ||
| 201 | PGPrescalerDivider::_1 => 0, | ||
| 202 | PGPrescalerDivider::_2 => 1, | ||
| 203 | PGPrescalerDivider::_4 => 2, | ||
| 204 | PGPrescalerDivider::_8 => 3, | ||
| 205 | PGPrescalerDivider::_16 => 4, | ||
| 206 | PGPrescalerDivider::_32 => 5, | ||
| 207 | PGPrescalerDivider::_64 => 6, | ||
| 208 | PGPrescalerDivider::_128 => 7, | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | /// Max count | ||
| 214 | #[allow(missing_docs)] | ||
| 215 | #[derive(Copy, Clone)] | ||
| 216 | pub enum MaxCount { | ||
| 217 | _255, | ||
| 218 | _511, | ||
| 219 | _1023, | ||
| 220 | _2047, | ||
| 221 | _4095, | ||
| 222 | _8191, | ||
| 223 | _16383, | ||
| 224 | } | ||
| 225 | |||
| 226 | impl Into<u8> for MaxCount { | ||
| 227 | fn into(self) -> u8 { | ||
| 228 | match self { | ||
| 229 | MaxCount::_255 => 0, | ||
| 230 | MaxCount::_511 => 1, | ||
| 231 | MaxCount::_1023 => 2, | ||
| 232 | MaxCount::_2047 => 3, | ||
| 233 | MaxCount::_4095 => 4, | ||
| 234 | MaxCount::_8191 => 5, | ||
| 235 | MaxCount::_16383 => 6, | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
diff --git a/embassy-stm32/src/tsc/errors.rs b/embassy-stm32/src/tsc/errors.rs new file mode 100644 index 000000000..21f6441ba --- /dev/null +++ b/embassy-stm32/src/tsc/errors.rs | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /// Represents errors that can occur when configuring or validating TSC pin groups. | ||
| 2 | #[derive(Debug)] | ||
| 3 | pub enum GroupError { | ||
| 4 | /// Error when a group has no sampling capacitor | ||
| 5 | NoSamplingCapacitor, | ||
| 6 | /// Error when a group has neither channel IOs nor a shield IO | ||
| 7 | NoChannelOrShield, | ||
| 8 | /// Error when a group has both channel IOs and a shield IO | ||
| 9 | MixedChannelAndShield, | ||
| 10 | /// Error when there is more than one shield IO across all groups | ||
| 11 | MultipleShields, | ||
| 12 | } | ||
| 13 | |||
| 14 | /// Error returned when attempting to set an invalid channel pin as active in the TSC. | ||
| 15 | #[derive(Debug)] | ||
| 16 | pub enum AcquisitionBankError { | ||
| 17 | /// Indicates that one or more of the provided pins is not a valid channel pin. | ||
| 18 | InvalidChannelPin, | ||
| 19 | /// Indicates that multiple channels from the same group were provided. | ||
| 20 | MultipleChannelsPerGroup, | ||
| 21 | } | ||
diff --git a/embassy-stm32/src/tsc/io_pin.rs b/embassy-stm32/src/tsc/io_pin.rs new file mode 100644 index 000000000..ad2010ed7 --- /dev/null +++ b/embassy-stm32/src/tsc/io_pin.rs | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::ops::{BitAnd, BitOr, BitOrAssign}; | ||
| 3 | |||
| 4 | use super::pin_roles; | ||
| 5 | use super::types::Group; | ||
| 6 | |||
| 7 | /// Pin defines | ||
| 8 | #[allow(missing_docs)] | ||
| 9 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 10 | #[derive(PartialEq, Clone, Copy, Debug)] | ||
| 11 | pub enum IOPin { | ||
| 12 | Group1Io1, | ||
| 13 | Group1Io2, | ||
| 14 | Group1Io3, | ||
| 15 | Group1Io4, | ||
| 16 | Group2Io1, | ||
| 17 | Group2Io2, | ||
| 18 | Group2Io3, | ||
| 19 | Group2Io4, | ||
| 20 | Group3Io1, | ||
| 21 | Group3Io2, | ||
| 22 | Group3Io3, | ||
| 23 | Group3Io4, | ||
| 24 | Group4Io1, | ||
| 25 | Group4Io2, | ||
| 26 | Group4Io3, | ||
| 27 | Group4Io4, | ||
| 28 | Group5Io1, | ||
| 29 | Group5Io2, | ||
| 30 | Group5Io3, | ||
| 31 | Group5Io4, | ||
| 32 | Group6Io1, | ||
| 33 | Group6Io2, | ||
| 34 | Group6Io3, | ||
| 35 | Group6Io4, | ||
| 36 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 37 | Group7Io1, | ||
| 38 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 39 | Group7Io2, | ||
| 40 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 41 | Group7Io3, | ||
| 42 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 43 | Group7Io4, | ||
| 44 | #[cfg(tsc_v3)] | ||
| 45 | Group8Io1, | ||
| 46 | #[cfg(tsc_v3)] | ||
| 47 | Group8Io2, | ||
| 48 | #[cfg(tsc_v3)] | ||
| 49 | Group8Io3, | ||
| 50 | #[cfg(tsc_v3)] | ||
| 51 | Group8Io4, | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Represents a TSC I/O pin with associated group and role information. | ||
| 55 | /// | ||
| 56 | /// This type combines an `tsc::IOPin` with phantom type parameters to statically | ||
| 57 | /// encode the pin's group and role. This allows for type-safe operations | ||
| 58 | /// on TSC pins within their specific contexts. | ||
| 59 | /// | ||
| 60 | /// - `Group`: A type parameter representing the TSC group (e.g., `G1`, `G2`). | ||
| 61 | /// - `Role`: A type parameter representing the pin's role (e.g., `Channel`, `Sample`). | ||
| 62 | #[derive(Clone, Copy, Debug)] | ||
| 63 | pub struct IOPinWithRole<Group, Role: pin_roles::Role> { | ||
| 64 | /// The underlying TSC I/O pin. | ||
| 65 | pub pin: IOPin, | ||
| 66 | pub(super) phantom: PhantomData<(Group, Role)>, | ||
| 67 | } | ||
| 68 | |||
| 69 | impl<G, R: pin_roles::Role> IOPinWithRole<G, R> { | ||
| 70 | pub(super) fn get_pin(wrapped_pin: IOPinWithRole<G, R>) -> IOPin { | ||
| 71 | wrapped_pin.pin | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl IOPin { | ||
| 76 | /// Maps this IOPin to the Group it belongs to. | ||
| 77 | /// | ||
| 78 | /// This method provides a convenient way to determine which Group | ||
| 79 | /// a specific TSC I/O pin is associated with. | ||
| 80 | pub const fn group(&self) -> Group { | ||
| 81 | match self { | ||
| 82 | IOPin::Group1Io1 | IOPin::Group1Io2 | IOPin::Group1Io3 | IOPin::Group1Io4 => Group::One, | ||
| 83 | IOPin::Group2Io1 | IOPin::Group2Io2 | IOPin::Group2Io3 | IOPin::Group2Io4 => Group::Two, | ||
| 84 | IOPin::Group3Io1 | IOPin::Group3Io2 | IOPin::Group3Io3 | IOPin::Group3Io4 => Group::Three, | ||
| 85 | IOPin::Group4Io1 | IOPin::Group4Io2 | IOPin::Group4Io3 | IOPin::Group4Io4 => Group::Four, | ||
| 86 | IOPin::Group5Io1 | IOPin::Group5Io2 | IOPin::Group5Io3 | IOPin::Group5Io4 => Group::Five, | ||
| 87 | IOPin::Group6Io1 | IOPin::Group6Io2 | IOPin::Group6Io3 | IOPin::Group6Io4 => Group::Six, | ||
| 88 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 89 | IOPin::Group7Io1 | IOPin::Group7Io2 | IOPin::Group7Io3 | IOPin::Group7Io4 => Group::Seven, | ||
| 90 | #[cfg(tsc_v3)] | ||
| 91 | IOPin::Group8Io1 | IOPin::Group8Io2 | IOPin::Group8Io3 | IOPin::Group8Io4 => Group::Eight, | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Returns the `Group` associated with the given `IOPin`. | ||
| 96 | pub fn get_group(pin: IOPin) -> Group { | ||
| 97 | pin.group() | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | impl BitOr<IOPin> for u32 { | ||
| 102 | type Output = u32; | ||
| 103 | fn bitor(self, rhs: IOPin) -> Self::Output { | ||
| 104 | let rhs: u32 = rhs.into(); | ||
| 105 | self | rhs | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | impl BitOr<u32> for IOPin { | ||
| 110 | type Output = u32; | ||
| 111 | fn bitor(self, rhs: u32) -> Self::Output { | ||
| 112 | let val: u32 = self.into(); | ||
| 113 | val | rhs | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | impl BitOr for IOPin { | ||
| 118 | type Output = u32; | ||
| 119 | fn bitor(self, rhs: Self) -> Self::Output { | ||
| 120 | let val: u32 = self.into(); | ||
| 121 | let rhs: u32 = rhs.into(); | ||
| 122 | val | rhs | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | impl BitOrAssign<IOPin> for u32 { | ||
| 127 | fn bitor_assign(&mut self, rhs: IOPin) { | ||
| 128 | let rhs: u32 = rhs.into(); | ||
| 129 | *self |= rhs; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | impl BitAnd<IOPin> for u32 { | ||
| 134 | type Output = u32; | ||
| 135 | fn bitand(self, rhs: IOPin) -> Self::Output { | ||
| 136 | let rhs: u32 = rhs.into(); | ||
| 137 | self & rhs | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | impl BitAnd<u32> for IOPin { | ||
| 142 | type Output = u32; | ||
| 143 | fn bitand(self, rhs: u32) -> Self::Output { | ||
| 144 | let val: u32 = self.into(); | ||
| 145 | val & rhs | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | impl IOPin { | ||
| 150 | const fn to_u32(self) -> u32 { | ||
| 151 | match self { | ||
| 152 | IOPin::Group1Io1 => 0x00000001, | ||
| 153 | IOPin::Group1Io2 => 0x00000002, | ||
| 154 | IOPin::Group1Io3 => 0x00000004, | ||
| 155 | IOPin::Group1Io4 => 0x00000008, | ||
| 156 | IOPin::Group2Io1 => 0x00000010, | ||
| 157 | IOPin::Group2Io2 => 0x00000020, | ||
| 158 | IOPin::Group2Io3 => 0x00000040, | ||
| 159 | IOPin::Group2Io4 => 0x00000080, | ||
| 160 | IOPin::Group3Io1 => 0x00000100, | ||
| 161 | IOPin::Group3Io2 => 0x00000200, | ||
| 162 | IOPin::Group3Io3 => 0x00000400, | ||
| 163 | IOPin::Group3Io4 => 0x00000800, | ||
| 164 | IOPin::Group4Io1 => 0x00001000, | ||
| 165 | IOPin::Group4Io2 => 0x00002000, | ||
| 166 | IOPin::Group4Io3 => 0x00004000, | ||
| 167 | IOPin::Group4Io4 => 0x00008000, | ||
| 168 | IOPin::Group5Io1 => 0x00010000, | ||
| 169 | IOPin::Group5Io2 => 0x00020000, | ||
| 170 | IOPin::Group5Io3 => 0x00040000, | ||
| 171 | IOPin::Group5Io4 => 0x00080000, | ||
| 172 | IOPin::Group6Io1 => 0x00100000, | ||
| 173 | IOPin::Group6Io2 => 0x00200000, | ||
| 174 | IOPin::Group6Io3 => 0x00400000, | ||
| 175 | IOPin::Group6Io4 => 0x00800000, | ||
| 176 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 177 | IOPin::Group7Io1 => 0x01000000, | ||
| 178 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 179 | IOPin::Group7Io2 => 0x02000000, | ||
| 180 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 181 | IOPin::Group7Io3 => 0x04000000, | ||
| 182 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 183 | IOPin::Group7Io4 => 0x08000000, | ||
| 184 | #[cfg(tsc_v3)] | ||
| 185 | IOPin::Group8Io1 => 0x10000000, | ||
| 186 | #[cfg(tsc_v3)] | ||
| 187 | IOPin::Group8Io2 => 0x20000000, | ||
| 188 | #[cfg(tsc_v3)] | ||
| 189 | IOPin::Group8Io3 => 0x40000000, | ||
| 190 | #[cfg(tsc_v3)] | ||
| 191 | IOPin::Group8Io4 => 0x80000000, | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | impl Into<u32> for IOPin { | ||
| 197 | fn into(self) -> u32 { | ||
| 198 | self.to_u32() | ||
| 199 | } | ||
| 200 | } | ||
diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs index 17240e6bc..9359d83e9 100644 --- a/embassy-stm32/src/tsc/mod.rs +++ b/embassy-stm32/src/tsc/mod.rs | |||
| @@ -1,90 +1,120 @@ | |||
| 1 | //! TSC Peripheral Interface | 1 | //! TSC Peripheral Interface |
| 2 | //! | 2 | //! |
| 3 | //! This module provides an interface for the Touch Sensing Controller (TSC) peripheral. | ||
| 4 | //! It supports both blocking and async modes of operation, as well as different TSC versions (v1, v2, v3). | ||
| 3 | //! | 5 | //! |
| 4 | //! # Example (stm32) | 6 | //! # Key Concepts |
| 5 | //! ``` rust, ignore | ||
| 6 | //! | 7 | //! |
| 7 | //! let mut device_config = embassy_stm32::Config::default(); | 8 | //! - **Pin Groups**: TSC pins are organized into groups, each containing up to four IOs. |
| 8 | //! { | 9 | //! - **Pin Roles**: Each pin in a group can have a role: Channel, Sample, or Shield. |
| 9 | //! device_config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_4MHZ); | 10 | //! - **Acquisition Banks**: Used for efficient, repeated TSC acquisitions on specific sets of pins. |
| 10 | //! } | 11 | //! |
| 12 | //! # Example (stm32) | ||
| 11 | //! | 13 | //! |
| 14 | //! ```rust | ||
| 15 | //! let device_config = embassy_stm32::Config::default(); | ||
| 12 | //! let context = embassy_stm32::init(device_config); | 16 | //! let context = embassy_stm32::init(device_config); |
| 13 | //! | 17 | //! |
| 14 | //! let config = tsc::Config { | 18 | //! let config = tsc::Config { |
| 15 | //! ct_pulse_high_length: ChargeTransferPulseCycle::_2, | 19 | //! ct_pulse_high_length: ChargeTransferPulseCycle::_4, |
| 16 | //! ct_pulse_low_length: ChargeTransferPulseCycle::_2, | 20 | //! ct_pulse_low_length: ChargeTransferPulseCycle::_4, |
| 17 | //! spread_spectrum: false, | 21 | //! spread_spectrum: false, |
| 18 | //! spread_spectrum_deviation: SSDeviation::new(2).unwrap(), | 22 | //! spread_spectrum_deviation: SSDeviation::new(2).unwrap(), |
| 19 | //! spread_spectrum_prescaler: false, | 23 | //! spread_spectrum_prescaler: false, |
| 20 | //! pulse_generator_prescaler: PGPrescalerDivider::_4, | 24 | //! pulse_generator_prescaler: PGPrescalerDivider::_16, |
| 21 | //! max_count_value: MaxCount::_8191, | 25 | //! max_count_value: MaxCount::_255, |
| 22 | //! io_default_mode: false, | 26 | //! io_default_mode: false, |
| 23 | //! synchro_pin_polarity: false, | 27 | //! synchro_pin_polarity: false, |
| 24 | //! acquisition_mode: false, | 28 | //! acquisition_mode: false, |
| 25 | //! max_count_interrupt: false, | 29 | //! max_count_interrupt: false, |
| 26 | //! channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3, | ||
| 27 | //! shield_ios: TscIOPin::Group1Io3.into(), | ||
| 28 | //! sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2, | ||
| 29 | //! }; | 30 | //! }; |
| 30 | //! | 31 | //! |
| 31 | //! let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new(); | 32 | //! let mut g2: PinGroupWithRoles<embassy_stm32::peripherals::TSC, G2> = PinGroupWithRoles::new(); |
| 32 | //! g1.set_io2(context.PB13, PinType::Sample); | 33 | //! g2.set_io1::<tsc_pin_roles::Sample>(context.PB4); |
| 33 | //! g1.set_io3(context.PB14, PinType::Shield); | 34 | //! let sensor_pin = g2.set_io2::<tsc_pin_roles::Channel>(context.PB5); |
| 34 | //! | ||
| 35 | //! let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new(); | ||
| 36 | //! g2.set_io1(context.PB4, PinType::Sample); | ||
| 37 | //! g2.set_io2(context.PB5, PinType::Channel); | ||
| 38 | //! | 35 | //! |
| 39 | //! let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new(); | 36 | //! let pin_groups = PinGroups { |
| 40 | //! g7.set_io2(context.PE3, PinType::Sample); | 37 | //! g2: Some(g2.pin_group), |
| 41 | //! g7.set_io3(context.PE4, PinType::Channel); | 38 | //! ..Default::default() |
| 39 | //! }; | ||
| 42 | //! | 40 | //! |
| 43 | //! let mut touch_controller = tsc::Tsc::new_blocking( | 41 | //! let mut touch_controller = tsc::Tsc::new_blocking( |
| 44 | //! context.TSC, | 42 | //! context.TSC, |
| 45 | //! Some(g1), | 43 | //! pin_groups, |
| 46 | //! Some(g2), | ||
| 47 | //! None, | ||
| 48 | //! None, | ||
| 49 | //! None, | ||
| 50 | //! None, | ||
| 51 | //! Some(g7), | ||
| 52 | //! None, | ||
| 53 | //! config, | 44 | //! config, |
| 54 | //! ); | 45 | //! ).unwrap(); |
| 55 | //! | 46 | //! |
| 56 | //! touch_controller.discharge_io(true); | 47 | //! let discharge_delay = 5; // ms |
| 57 | //! Timer::after_millis(1).await; | ||
| 58 | //! | 48 | //! |
| 59 | //! touch_controller.start(); | 49 | //! loop { |
| 50 | //! touch_controller.set_active_channels_mask(sensor_pin.pin.into()); | ||
| 51 | //! touch_controller.start(); | ||
| 52 | //! touch_controller.poll_for_acquisition(); | ||
| 53 | //! touch_controller.discharge_io(true); | ||
| 54 | //! Timer::after_millis(discharge_delay).await; | ||
| 60 | //! | 55 | //! |
| 56 | //! match touch_controller.group_get_status(sensor_pin.pin.group()) { | ||
| 57 | //! GroupStatus::Complete => { | ||
| 58 | //! let group_val = touch_controller.group_get_value(sensor_pin.pin.group()); | ||
| 59 | //! // Process the touch value | ||
| 60 | //! // ... | ||
| 61 | //! } | ||
| 62 | //! GroupStatus::Ongoing => { | ||
| 63 | //! // Handle ongoing acquisition | ||
| 64 | //! // ... | ||
| 65 | //! } | ||
| 66 | //! } | ||
| 67 | //! } | ||
| 61 | //! ``` | 68 | //! ``` |
| 69 | //! | ||
| 70 | //! # Async Usage | ||
| 71 | //! | ||
| 72 | //! For async operation, use `Tsc::new_async` and `pend_for_acquisition` instead of polling. | ||
| 62 | 73 | ||
| 63 | #![macro_use] | 74 | #![macro_use] |
| 64 | 75 | ||
| 65 | /// Enums defined for peripheral parameters | 76 | /// Configuration structures and enums for the TSC peripheral. |
| 66 | pub mod enums; | 77 | pub mod config; |
| 78 | |||
| 79 | /// Definitions and implementations for TSC pin groups. | ||
| 80 | pub mod pin_groups; | ||
| 81 | |||
| 82 | /// Definitions and implementations for individual TSC I/O pins. | ||
| 83 | pub mod io_pin; | ||
| 84 | |||
| 85 | /// Structures and implementations for TSC acquisition banks. | ||
| 86 | pub mod acquisition_banks; | ||
| 87 | |||
| 88 | /// Core implementation of the TSC (Touch Sensing Controller) driver. | ||
| 89 | pub mod tsc; | ||
| 90 | |||
| 91 | /// Type definitions used throughout the TSC module. | ||
| 92 | pub mod types; | ||
| 93 | |||
| 94 | /// Error types and definitions for the TSC module. | ||
| 95 | pub mod errors; | ||
| 67 | 96 | ||
| 68 | use core::future::poll_fn; | ||
| 69 | use core::marker::PhantomData; | 97 | use core::marker::PhantomData; |
| 70 | use core::task::Poll; | ||
| 71 | 98 | ||
| 72 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 99 | pub use acquisition_banks::*; |
| 100 | pub use config::*; | ||
| 101 | use embassy_hal_internal::PeripheralType; | ||
| 73 | use embassy_sync::waitqueue::AtomicWaker; | 102 | use embassy_sync::waitqueue::AtomicWaker; |
| 74 | pub use enums::*; | 103 | pub use errors::*; |
| 104 | pub use io_pin::*; | ||
| 105 | pub use pin_groups::*; | ||
| 106 | pub use tsc::*; | ||
| 107 | pub use types::*; | ||
| 75 | 108 | ||
| 76 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | 109 | use crate::rcc::RccPeripheral; |
| 77 | use crate::interrupt::typelevel::Interrupt; | 110 | use crate::{interrupt, peripherals}; |
| 78 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | ||
| 79 | use crate::rcc::{self, RccPeripheral}; | ||
| 80 | use crate::{interrupt, peripherals, Peripheral}; | ||
| 81 | 111 | ||
| 82 | #[cfg(tsc_v1)] | 112 | #[cfg(tsc_v1)] |
| 83 | const TSC_NUM_GROUPS: u32 = 6; | 113 | const TSC_NUM_GROUPS: usize = 6; |
| 84 | #[cfg(tsc_v2)] | 114 | #[cfg(tsc_v2)] |
| 85 | const TSC_NUM_GROUPS: u32 = 7; | 115 | const TSC_NUM_GROUPS: usize = 7; |
| 86 | #[cfg(tsc_v3)] | 116 | #[cfg(tsc_v3)] |
| 87 | const TSC_NUM_GROUPS: u32 = 8; | 117 | const TSC_NUM_GROUPS: usize = 8; |
| 88 | 118 | ||
| 89 | /// Error type defined for TSC | 119 | /// Error type defined for TSC |
| 90 | #[derive(Debug, Clone, Copy)] | 120 | #[derive(Debug, Clone, Copy)] |
| @@ -106,859 +136,6 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 106 | } | 136 | } |
| 107 | } | 137 | } |
| 108 | 138 | ||
| 109 | /// Pin type definition to control IO parameters | ||
| 110 | pub enum PinType { | ||
| 111 | /// Sensing channel pin connected to an electrode | ||
| 112 | Channel, | ||
| 113 | /// Sampling capacitor pin, one required for every pin group | ||
| 114 | Sample, | ||
| 115 | /// Shield pin connected to capacitive sensing shield | ||
| 116 | Shield, | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Peripheral state | ||
| 120 | #[derive(PartialEq, Clone, Copy)] | ||
| 121 | pub enum State { | ||
| 122 | /// Peripheral is being setup or reconfigured | ||
| 123 | Reset, | ||
| 124 | /// Ready to start acquisition | ||
| 125 | Ready, | ||
| 126 | /// In process of sensor acquisition | ||
| 127 | Busy, | ||
| 128 | /// Error occured during acquisition | ||
| 129 | Error, | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Individual group status checked after acquisition reported as complete | ||
| 133 | /// For groups with multiple channel pins, may take longer because acquisitions | ||
| 134 | /// are done sequentially. Check this status before pulling count for each | ||
| 135 | /// sampled channel | ||
| 136 | #[derive(PartialEq)] | ||
| 137 | pub enum GroupStatus { | ||
| 138 | /// Acquisition for channel still in progress | ||
| 139 | Ongoing, | ||
| 140 | /// Acquisition either not started or complete | ||
| 141 | Complete, | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Group identifier used to interrogate status | ||
| 145 | #[allow(missing_docs)] | ||
| 146 | pub enum Group { | ||
| 147 | One, | ||
| 148 | Two, | ||
| 149 | Three, | ||
| 150 | Four, | ||
| 151 | Five, | ||
| 152 | Six, | ||
| 153 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 154 | Seven, | ||
| 155 | #[cfg(tsc_v3)] | ||
| 156 | Eight, | ||
| 157 | } | ||
| 158 | |||
| 159 | impl Into<usize> for Group { | ||
| 160 | fn into(self) -> usize { | ||
| 161 | match self { | ||
| 162 | Group::One => 0, | ||
| 163 | Group::Two => 1, | ||
| 164 | Group::Three => 2, | ||
| 165 | Group::Four => 3, | ||
| 166 | Group::Five => 4, | ||
| 167 | Group::Six => 5, | ||
| 168 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 169 | Group::Seven => 6, | ||
| 170 | #[cfg(tsc_v3)] | ||
| 171 | Group::Eight => 7, | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Peripheral configuration | ||
| 177 | #[derive(Clone, Copy)] | ||
| 178 | pub struct Config { | ||
| 179 | /// Duration of high state of the charge transfer pulse | ||
| 180 | pub ct_pulse_high_length: ChargeTransferPulseCycle, | ||
| 181 | /// Duration of the low state of the charge transfer pulse | ||
| 182 | pub ct_pulse_low_length: ChargeTransferPulseCycle, | ||
| 183 | /// Enable/disable of spread spectrum feature | ||
| 184 | pub spread_spectrum: bool, | ||
| 185 | /// Adds variable number of periods of the SS clk to pulse high state | ||
| 186 | pub spread_spectrum_deviation: SSDeviation, | ||
| 187 | /// Selects AHB clock divider used to generate SS clk | ||
| 188 | pub spread_spectrum_prescaler: bool, | ||
| 189 | /// Selects AHB clock divider used to generate pulse generator clk | ||
| 190 | pub pulse_generator_prescaler: PGPrescalerDivider, | ||
| 191 | /// Maximum number of charge transfer pulses that can be generated before error | ||
| 192 | pub max_count_value: MaxCount, | ||
| 193 | /// Defines config of all IOs when no ongoing acquisition | ||
| 194 | pub io_default_mode: bool, | ||
| 195 | /// Polarity of sync input pin | ||
| 196 | pub synchro_pin_polarity: bool, | ||
| 197 | /// Acquisition starts when start bit is set or with sync pin input | ||
| 198 | pub acquisition_mode: bool, | ||
| 199 | /// Enable max count interrupt | ||
| 200 | pub max_count_interrupt: bool, | ||
| 201 | /// Channel IO mask | ||
| 202 | pub channel_ios: u32, | ||
| 203 | /// Shield IO mask | ||
| 204 | pub shield_ios: u32, | ||
| 205 | /// Sampling IO mask | ||
| 206 | pub sampling_ios: u32, | ||
| 207 | } | ||
| 208 | |||
| 209 | impl Default for Config { | ||
| 210 | fn default() -> Self { | ||
| 211 | Self { | ||
| 212 | ct_pulse_high_length: ChargeTransferPulseCycle::_1, | ||
| 213 | ct_pulse_low_length: ChargeTransferPulseCycle::_1, | ||
| 214 | spread_spectrum: false, | ||
| 215 | spread_spectrum_deviation: SSDeviation::new(1).unwrap(), | ||
| 216 | spread_spectrum_prescaler: false, | ||
| 217 | pulse_generator_prescaler: PGPrescalerDivider::_1, | ||
| 218 | max_count_value: MaxCount::_255, | ||
| 219 | io_default_mode: false, | ||
| 220 | synchro_pin_polarity: false, | ||
| 221 | acquisition_mode: false, | ||
| 222 | max_count_interrupt: false, | ||
| 223 | channel_ios: 0, | ||
| 224 | shield_ios: 0, | ||
| 225 | sampling_ios: 0, | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Pin struct that maintains usage | ||
| 231 | #[allow(missing_docs)] | ||
| 232 | pub struct TscPin<'d, T, C> { | ||
| 233 | _pin: PeripheralRef<'d, AnyPin>, | ||
| 234 | role: PinType, | ||
| 235 | phantom: PhantomData<(T, C)>, | ||
| 236 | } | ||
| 237 | |||
| 238 | enum GroupError { | ||
| 239 | NoSample, | ||
| 240 | ChannelShield, | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Pin group definition | ||
| 244 | /// Pins are organized into groups of four IOs, all groups with a | ||
| 245 | /// sampling channel must also have a sampling capacitor channel. | ||
| 246 | #[allow(missing_docs)] | ||
| 247 | #[derive(Default)] | ||
| 248 | pub struct PinGroup<'d, T, C> { | ||
| 249 | d1: Option<TscPin<'d, T, C>>, | ||
| 250 | d2: Option<TscPin<'d, T, C>>, | ||
| 251 | d3: Option<TscPin<'d, T, C>>, | ||
| 252 | d4: Option<TscPin<'d, T, C>>, | ||
| 253 | } | ||
| 254 | |||
| 255 | impl<'d, T: Instance, C> PinGroup<'d, T, C> { | ||
| 256 | /// Create new sensing group | ||
| 257 | pub fn new() -> Self { | ||
| 258 | Self { | ||
| 259 | d1: None, | ||
| 260 | d2: None, | ||
| 261 | d3: None, | ||
| 262 | d4: None, | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | fn contains_shield(&self) -> bool { | ||
| 267 | let mut shield_count = 0; | ||
| 268 | |||
| 269 | if let Some(pin) = &self.d1 { | ||
| 270 | if let PinType::Shield = pin.role { | ||
| 271 | shield_count += 1; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | if let Some(pin) = &self.d2 { | ||
| 276 | if let PinType::Shield = pin.role { | ||
| 277 | shield_count += 1; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | if let Some(pin) = &self.d3 { | ||
| 282 | if let PinType::Shield = pin.role { | ||
| 283 | shield_count += 1; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | if let Some(pin) = &self.d4 { | ||
| 288 | if let PinType::Shield = pin.role { | ||
| 289 | shield_count += 1; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | shield_count == 1 | ||
| 294 | } | ||
| 295 | |||
| 296 | fn check_group(&self) -> Result<(), GroupError> { | ||
| 297 | let mut channel_count = 0; | ||
| 298 | let mut shield_count = 0; | ||
| 299 | let mut sample_count = 0; | ||
| 300 | if let Some(pin) = &self.d1 { | ||
| 301 | match pin.role { | ||
| 302 | PinType::Channel => { | ||
| 303 | channel_count += 1; | ||
| 304 | } | ||
| 305 | PinType::Shield => { | ||
| 306 | shield_count += 1; | ||
| 307 | } | ||
| 308 | PinType::Sample => { | ||
| 309 | sample_count += 1; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | if let Some(pin) = &self.d2 { | ||
| 315 | match pin.role { | ||
| 316 | PinType::Channel => { | ||
| 317 | channel_count += 1; | ||
| 318 | } | ||
| 319 | PinType::Shield => { | ||
| 320 | shield_count += 1; | ||
| 321 | } | ||
| 322 | PinType::Sample => { | ||
| 323 | sample_count += 1; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | if let Some(pin) = &self.d3 { | ||
| 329 | match pin.role { | ||
| 330 | PinType::Channel => { | ||
| 331 | channel_count += 1; | ||
| 332 | } | ||
| 333 | PinType::Shield => { | ||
| 334 | shield_count += 1; | ||
| 335 | } | ||
| 336 | PinType::Sample => { | ||
| 337 | sample_count += 1; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | if let Some(pin) = &self.d4 { | ||
| 343 | match pin.role { | ||
| 344 | PinType::Channel => { | ||
| 345 | channel_count += 1; | ||
| 346 | } | ||
| 347 | PinType::Shield => { | ||
| 348 | shield_count += 1; | ||
| 349 | } | ||
| 350 | PinType::Sample => { | ||
| 351 | sample_count += 1; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | // Every group requires one sampling capacitor | ||
| 357 | if sample_count != 1 { | ||
| 358 | return Err(GroupError::NoSample); | ||
| 359 | } | ||
| 360 | |||
| 361 | // Each group must have at least one shield or channel IO | ||
| 362 | if shield_count == 0 && channel_count == 0 { | ||
| 363 | return Err(GroupError::ChannelShield); | ||
| 364 | } | ||
| 365 | |||
| 366 | // Any group can either contain channel ios or a shield IO | ||
| 367 | if shield_count != 0 && channel_count != 0 { | ||
| 368 | return Err(GroupError::ChannelShield); | ||
| 369 | } | ||
| 370 | |||
| 371 | // No more than one shield IO is allow per group and amongst all groups | ||
| 372 | if shield_count > 1 { | ||
| 373 | return Err(GroupError::ChannelShield); | ||
| 374 | } | ||
| 375 | |||
| 376 | Ok(()) | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | macro_rules! group_impl { | ||
| 381 | ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { | ||
| 382 | impl<'d, T: Instance> PinGroup<'d, T, $group> { | ||
| 383 | #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] | ||
| 384 | pub fn set_io1(&mut self, pin: impl Peripheral<P = impl $trait1<T>> + 'd, role: PinType) { | ||
| 385 | into_ref!(pin); | ||
| 386 | critical_section::with(|_| { | ||
| 387 | pin.set_low(); | ||
| 388 | pin.set_as_af( | ||
| 389 | pin.af_num(), | ||
| 390 | AfType::output( | ||
| 391 | match role { | ||
| 392 | PinType::Channel => OutputType::PushPull, | ||
| 393 | PinType::Sample => OutputType::OpenDrain, | ||
| 394 | PinType::Shield => OutputType::PushPull, | ||
| 395 | }, | ||
| 396 | Speed::VeryHigh, | ||
| 397 | ), | ||
| 398 | ); | ||
| 399 | self.d1 = Some(TscPin { | ||
| 400 | _pin: pin.map_into(), | ||
| 401 | role: role, | ||
| 402 | phantom: PhantomData, | ||
| 403 | }) | ||
| 404 | }) | ||
| 405 | } | ||
| 406 | |||
| 407 | #[doc = concat!("Create a new pin2 for ", stringify!($group), " TSC group instance.")] | ||
| 408 | pub fn set_io2(&mut self, pin: impl Peripheral<P = impl $trait2<T>> + 'd, role: PinType) { | ||
| 409 | into_ref!(pin); | ||
| 410 | critical_section::with(|_| { | ||
| 411 | pin.set_low(); | ||
| 412 | pin.set_as_af( | ||
| 413 | pin.af_num(), | ||
| 414 | AfType::output( | ||
| 415 | match role { | ||
| 416 | PinType::Channel => OutputType::PushPull, | ||
| 417 | PinType::Sample => OutputType::OpenDrain, | ||
| 418 | PinType::Shield => OutputType::PushPull, | ||
| 419 | }, | ||
| 420 | Speed::VeryHigh, | ||
| 421 | ), | ||
| 422 | ); | ||
| 423 | self.d2 = Some(TscPin { | ||
| 424 | _pin: pin.map_into(), | ||
| 425 | role: role, | ||
| 426 | phantom: PhantomData, | ||
| 427 | }) | ||
| 428 | }) | ||
| 429 | } | ||
| 430 | |||
| 431 | #[doc = concat!("Create a new pin3 for ", stringify!($group), " TSC group instance.")] | ||
| 432 | pub fn set_io3(&mut self, pin: impl Peripheral<P = impl $trait3<T>> + 'd, role: PinType) { | ||
| 433 | into_ref!(pin); | ||
| 434 | critical_section::with(|_| { | ||
| 435 | pin.set_low(); | ||
| 436 | pin.set_as_af( | ||
| 437 | pin.af_num(), | ||
| 438 | AfType::output( | ||
| 439 | match role { | ||
| 440 | PinType::Channel => OutputType::PushPull, | ||
| 441 | PinType::Sample => OutputType::OpenDrain, | ||
| 442 | PinType::Shield => OutputType::PushPull, | ||
| 443 | }, | ||
| 444 | Speed::VeryHigh, | ||
| 445 | ), | ||
| 446 | ); | ||
| 447 | self.d3 = Some(TscPin { | ||
| 448 | _pin: pin.map_into(), | ||
| 449 | role: role, | ||
| 450 | phantom: PhantomData, | ||
| 451 | }) | ||
| 452 | }) | ||
| 453 | } | ||
| 454 | |||
| 455 | #[doc = concat!("Create a new pin4 for ", stringify!($group), " TSC group instance.")] | ||
| 456 | pub fn set_io4(&mut self, pin: impl Peripheral<P = impl $trait4<T>> + 'd, role: PinType) { | ||
| 457 | into_ref!(pin); | ||
| 458 | critical_section::with(|_| { | ||
| 459 | pin.set_low(); | ||
| 460 | pin.set_as_af( | ||
| 461 | pin.af_num(), | ||
| 462 | AfType::output( | ||
| 463 | match role { | ||
| 464 | PinType::Channel => OutputType::PushPull, | ||
| 465 | PinType::Sample => OutputType::OpenDrain, | ||
| 466 | PinType::Shield => OutputType::PushPull, | ||
| 467 | }, | ||
| 468 | Speed::VeryHigh, | ||
| 469 | ), | ||
| 470 | ); | ||
| 471 | self.d4 = Some(TscPin { | ||
| 472 | _pin: pin.map_into(), | ||
| 473 | role: role, | ||
| 474 | phantom: PhantomData, | ||
| 475 | }) | ||
| 476 | }) | ||
| 477 | } | ||
| 478 | } | ||
| 479 | }; | ||
| 480 | } | ||
| 481 | |||
| 482 | group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin); | ||
| 483 | group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin); | ||
| 484 | group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin); | ||
| 485 | group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin); | ||
| 486 | group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin); | ||
| 487 | group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin); | ||
| 488 | group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin); | ||
| 489 | group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin); | ||
| 490 | |||
| 491 | /// Group 1 marker type. | ||
| 492 | pub enum G1 {} | ||
| 493 | /// Group 2 marker type. | ||
| 494 | pub enum G2 {} | ||
| 495 | /// Group 3 marker type. | ||
| 496 | pub enum G3 {} | ||
| 497 | /// Group 4 marker type. | ||
| 498 | pub enum G4 {} | ||
| 499 | /// Group 5 marker type. | ||
| 500 | pub enum G5 {} | ||
| 501 | /// Group 6 marker type. | ||
| 502 | pub enum G6 {} | ||
| 503 | /// Group 7 marker type. | ||
| 504 | pub enum G7 {} | ||
| 505 | /// Group 8 marker type. | ||
| 506 | pub enum G8 {} | ||
| 507 | |||
| 508 | /// TSC driver | ||
| 509 | pub struct Tsc<'d, T: Instance, K: PeriMode> { | ||
| 510 | _peri: PeripheralRef<'d, T>, | ||
| 511 | _g1: Option<PinGroup<'d, T, G1>>, | ||
| 512 | _g2: Option<PinGroup<'d, T, G2>>, | ||
| 513 | _g3: Option<PinGroup<'d, T, G3>>, | ||
| 514 | _g4: Option<PinGroup<'d, T, G4>>, | ||
| 515 | _g5: Option<PinGroup<'d, T, G5>>, | ||
| 516 | _g6: Option<PinGroup<'d, T, G6>>, | ||
| 517 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 518 | _g7: Option<PinGroup<'d, T, G7>>, | ||
| 519 | #[cfg(tsc_v3)] | ||
| 520 | _g8: Option<PinGroup<'d, T, G8>>, | ||
| 521 | state: State, | ||
| 522 | config: Config, | ||
| 523 | _kind: PhantomData<K>, | ||
| 524 | } | ||
| 525 | |||
| 526 | impl<'d, T: Instance> Tsc<'d, T, Async> { | ||
| 527 | /// Create a Tsc instance that can be awaited for completion | ||
| 528 | pub fn new_async( | ||
| 529 | peri: impl Peripheral<P = T> + 'd, | ||
| 530 | g1: Option<PinGroup<'d, T, G1>>, | ||
| 531 | g2: Option<PinGroup<'d, T, G2>>, | ||
| 532 | g3: Option<PinGroup<'d, T, G3>>, | ||
| 533 | g4: Option<PinGroup<'d, T, G4>>, | ||
| 534 | g5: Option<PinGroup<'d, T, G5>>, | ||
| 535 | g6: Option<PinGroup<'d, T, G6>>, | ||
| 536 | #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>, | ||
| 537 | #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>, | ||
| 538 | config: Config, | ||
| 539 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 540 | ) -> Self { | ||
| 541 | // Need to check valid pin configuration input | ||
| 542 | let g1 = g1.filter(|b| b.check_group().is_ok()); | ||
| 543 | let g2 = g2.filter(|b| b.check_group().is_ok()); | ||
| 544 | let g3 = g3.filter(|b| b.check_group().is_ok()); | ||
| 545 | let g4 = g4.filter(|b| b.check_group().is_ok()); | ||
| 546 | let g5 = g5.filter(|b| b.check_group().is_ok()); | ||
| 547 | let g6 = g6.filter(|b| b.check_group().is_ok()); | ||
| 548 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 549 | let g7 = g7.filter(|b| b.check_group().is_ok()); | ||
| 550 | #[cfg(tsc_v3)] | ||
| 551 | let g8 = g8.filter(|b| b.check_group().is_ok()); | ||
| 552 | |||
| 553 | match Self::check_shields( | ||
| 554 | &g1, | ||
| 555 | &g2, | ||
| 556 | &g3, | ||
| 557 | &g4, | ||
| 558 | &g5, | ||
| 559 | &g6, | ||
| 560 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 561 | &g7, | ||
| 562 | #[cfg(tsc_v3)] | ||
| 563 | &g8, | ||
| 564 | ) { | ||
| 565 | Ok(()) => Self::new_inner( | ||
| 566 | peri, | ||
| 567 | g1, | ||
| 568 | g2, | ||
| 569 | g3, | ||
| 570 | g4, | ||
| 571 | g5, | ||
| 572 | g6, | ||
| 573 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 574 | g7, | ||
| 575 | #[cfg(tsc_v3)] | ||
| 576 | g8, | ||
| 577 | config, | ||
| 578 | ), | ||
| 579 | Err(_) => Self::new_inner( | ||
| 580 | peri, | ||
| 581 | None, | ||
| 582 | None, | ||
| 583 | None, | ||
| 584 | None, | ||
| 585 | None, | ||
| 586 | None, | ||
| 587 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 588 | None, | ||
| 589 | #[cfg(tsc_v3)] | ||
| 590 | None, | ||
| 591 | config, | ||
| 592 | ), | ||
| 593 | } | ||
| 594 | } | ||
| 595 | /// Asyncronously wait for the end of an acquisition | ||
| 596 | pub async fn pend_for_acquisition(&mut self) { | ||
| 597 | poll_fn(|cx| match self.get_state() { | ||
| 598 | State::Busy => { | ||
| 599 | T::waker().register(cx.waker()); | ||
| 600 | T::regs().ier().write(|w| w.set_eoaie(true)); | ||
| 601 | if self.get_state() != State::Busy { | ||
| 602 | T::regs().ier().write(|w| w.set_eoaie(false)); | ||
| 603 | return Poll::Ready(()); | ||
| 604 | } | ||
| 605 | Poll::Pending | ||
| 606 | } | ||
| 607 | _ => { | ||
| 608 | T::regs().ier().write(|w| w.set_eoaie(false)); | ||
| 609 | Poll::Ready(()) | ||
| 610 | } | ||
| 611 | }) | ||
| 612 | .await; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | impl<'d, T: Instance> Tsc<'d, T, Blocking> { | ||
| 617 | /// Create a Tsc instance that must be polled for completion | ||
| 618 | pub fn new_blocking( | ||
| 619 | peri: impl Peripheral<P = T> + 'd, | ||
| 620 | g1: Option<PinGroup<'d, T, G1>>, | ||
| 621 | g2: Option<PinGroup<'d, T, G2>>, | ||
| 622 | g3: Option<PinGroup<'d, T, G3>>, | ||
| 623 | g4: Option<PinGroup<'d, T, G4>>, | ||
| 624 | g5: Option<PinGroup<'d, T, G5>>, | ||
| 625 | g6: Option<PinGroup<'d, T, G6>>, | ||
| 626 | #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>, | ||
| 627 | #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>, | ||
| 628 | config: Config, | ||
| 629 | ) -> Self { | ||
| 630 | // Need to check valid pin configuration input | ||
| 631 | let g1 = g1.filter(|b| b.check_group().is_ok()); | ||
| 632 | let g2 = g2.filter(|b| b.check_group().is_ok()); | ||
| 633 | let g3 = g3.filter(|b| b.check_group().is_ok()); | ||
| 634 | let g4 = g4.filter(|b| b.check_group().is_ok()); | ||
| 635 | let g5 = g5.filter(|b| b.check_group().is_ok()); | ||
| 636 | let g6 = g6.filter(|b| b.check_group().is_ok()); | ||
| 637 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 638 | let g7 = g7.filter(|b| b.check_group().is_ok()); | ||
| 639 | #[cfg(tsc_v3)] | ||
| 640 | let g8 = g8.filter(|b| b.check_group().is_ok()); | ||
| 641 | |||
| 642 | match Self::check_shields( | ||
| 643 | &g1, | ||
| 644 | &g2, | ||
| 645 | &g3, | ||
| 646 | &g4, | ||
| 647 | &g5, | ||
| 648 | &g6, | ||
| 649 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 650 | &g7, | ||
| 651 | #[cfg(tsc_v3)] | ||
| 652 | &g8, | ||
| 653 | ) { | ||
| 654 | Ok(()) => Self::new_inner( | ||
| 655 | peri, | ||
| 656 | g1, | ||
| 657 | g2, | ||
| 658 | g3, | ||
| 659 | g4, | ||
| 660 | g5, | ||
| 661 | g6, | ||
| 662 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 663 | g7, | ||
| 664 | #[cfg(tsc_v3)] | ||
| 665 | g8, | ||
| 666 | config, | ||
| 667 | ), | ||
| 668 | Err(_) => Self::new_inner( | ||
| 669 | peri, | ||
| 670 | None, | ||
| 671 | None, | ||
| 672 | None, | ||
| 673 | None, | ||
| 674 | None, | ||
| 675 | None, | ||
| 676 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 677 | None, | ||
| 678 | #[cfg(tsc_v3)] | ||
| 679 | None, | ||
| 680 | config, | ||
| 681 | ), | ||
| 682 | } | ||
| 683 | } | ||
| 684 | /// Wait for end of acquisition | ||
| 685 | pub fn poll_for_acquisition(&mut self) { | ||
| 686 | while self.get_state() == State::Busy {} | ||
| 687 | } | ||
| 688 | } | ||
| 689 | |||
| 690 | impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { | ||
| 691 | /// Create new TSC driver | ||
| 692 | fn check_shields( | ||
| 693 | g1: &Option<PinGroup<'d, T, G1>>, | ||
| 694 | g2: &Option<PinGroup<'d, T, G2>>, | ||
| 695 | g3: &Option<PinGroup<'d, T, G3>>, | ||
| 696 | g4: &Option<PinGroup<'d, T, G4>>, | ||
| 697 | g5: &Option<PinGroup<'d, T, G5>>, | ||
| 698 | g6: &Option<PinGroup<'d, T, G6>>, | ||
| 699 | #[cfg(any(tsc_v2, tsc_v3))] g7: &Option<PinGroup<'d, T, G7>>, | ||
| 700 | #[cfg(tsc_v3)] g8: &Option<PinGroup<'d, T, G8>>, | ||
| 701 | ) -> Result<(), GroupError> { | ||
| 702 | let mut shield_count = 0; | ||
| 703 | |||
| 704 | if let Some(pin_group) = g1 { | ||
| 705 | if pin_group.contains_shield() { | ||
| 706 | shield_count += 1; | ||
| 707 | } | ||
| 708 | }; | ||
| 709 | if let Some(pin_group) = g2 { | ||
| 710 | if pin_group.contains_shield() { | ||
| 711 | shield_count += 1; | ||
| 712 | } | ||
| 713 | }; | ||
| 714 | if let Some(pin_group) = g3 { | ||
| 715 | if pin_group.contains_shield() { | ||
| 716 | shield_count += 1; | ||
| 717 | } | ||
| 718 | }; | ||
| 719 | if let Some(pin_group) = g4 { | ||
| 720 | if pin_group.contains_shield() { | ||
| 721 | shield_count += 1; | ||
| 722 | } | ||
| 723 | }; | ||
| 724 | if let Some(pin_group) = g5 { | ||
| 725 | if pin_group.contains_shield() { | ||
| 726 | shield_count += 1; | ||
| 727 | } | ||
| 728 | }; | ||
| 729 | if let Some(pin_group) = g6 { | ||
| 730 | if pin_group.contains_shield() { | ||
| 731 | shield_count += 1; | ||
| 732 | } | ||
| 733 | }; | ||
| 734 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 735 | if let Some(pin_group) = g7 { | ||
| 736 | if pin_group.contains_shield() { | ||
| 737 | shield_count += 1; | ||
| 738 | } | ||
| 739 | }; | ||
| 740 | #[cfg(tsc_v3)] | ||
| 741 | if let Some(pin_group) = g8 { | ||
| 742 | if pin_group.contains_shield() { | ||
| 743 | shield_count += 1; | ||
| 744 | } | ||
| 745 | }; | ||
| 746 | |||
| 747 | if shield_count > 1 { | ||
| 748 | return Err(GroupError::ChannelShield); | ||
| 749 | } | ||
| 750 | |||
| 751 | Ok(()) | ||
| 752 | } | ||
| 753 | |||
| 754 | fn extract_groups(io_mask: u32) -> u32 { | ||
| 755 | let mut groups: u32 = 0; | ||
| 756 | for idx in 0..TSC_NUM_GROUPS { | ||
| 757 | if io_mask & (0x0F << idx * 4) != 0 { | ||
| 758 | groups |= 1 << idx | ||
| 759 | } | ||
| 760 | } | ||
| 761 | groups | ||
| 762 | } | ||
| 763 | |||
| 764 | fn new_inner( | ||
| 765 | peri: impl Peripheral<P = T> + 'd, | ||
| 766 | g1: Option<PinGroup<'d, T, G1>>, | ||
| 767 | g2: Option<PinGroup<'d, T, G2>>, | ||
| 768 | g3: Option<PinGroup<'d, T, G3>>, | ||
| 769 | g4: Option<PinGroup<'d, T, G4>>, | ||
| 770 | g5: Option<PinGroup<'d, T, G5>>, | ||
| 771 | g6: Option<PinGroup<'d, T, G6>>, | ||
| 772 | #[cfg(any(tsc_v2, tsc_v3))] g7: Option<PinGroup<'d, T, G7>>, | ||
| 773 | #[cfg(tsc_v3)] g8: Option<PinGroup<'d, T, G8>>, | ||
| 774 | config: Config, | ||
| 775 | ) -> Self { | ||
| 776 | into_ref!(peri); | ||
| 777 | |||
| 778 | rcc::enable_and_reset::<T>(); | ||
| 779 | |||
| 780 | T::regs().cr().modify(|w| { | ||
| 781 | w.set_tsce(true); | ||
| 782 | w.set_ctph(config.ct_pulse_high_length.into()); | ||
| 783 | w.set_ctpl(config.ct_pulse_low_length.into()); | ||
| 784 | w.set_sse(config.spread_spectrum); | ||
| 785 | // Prevent invalid configuration for pulse generator prescaler | ||
| 786 | if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1 | ||
| 787 | && (config.pulse_generator_prescaler == PGPrescalerDivider::_1 | ||
| 788 | || config.pulse_generator_prescaler == PGPrescalerDivider::_2) | ||
| 789 | { | ||
| 790 | w.set_pgpsc(PGPrescalerDivider::_4.into()); | ||
| 791 | } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2 | ||
| 792 | && config.pulse_generator_prescaler == PGPrescalerDivider::_1 | ||
| 793 | { | ||
| 794 | w.set_pgpsc(PGPrescalerDivider::_2.into()); | ||
| 795 | } else { | ||
| 796 | w.set_pgpsc(config.pulse_generator_prescaler.into()); | ||
| 797 | } | ||
| 798 | w.set_ssd(config.spread_spectrum_deviation.into()); | ||
| 799 | w.set_sspsc(config.spread_spectrum_prescaler); | ||
| 800 | |||
| 801 | w.set_mcv(config.max_count_value.into()); | ||
| 802 | w.set_syncpol(config.synchro_pin_polarity); | ||
| 803 | w.set_am(config.acquisition_mode); | ||
| 804 | }); | ||
| 805 | |||
| 806 | // Set IO configuration | ||
| 807 | // Disable Schmitt trigger hysteresis on all used TSC IOs | ||
| 808 | T::regs() | ||
| 809 | .iohcr() | ||
| 810 | .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios)); | ||
| 811 | |||
| 812 | // Set channel and shield IOs | ||
| 813 | T::regs() | ||
| 814 | .ioccr() | ||
| 815 | .write(|w| w.0 = config.channel_ios | config.shield_ios); | ||
| 816 | |||
| 817 | // Set sampling IOs | ||
| 818 | T::regs().ioscr().write(|w| w.0 = config.sampling_ios); | ||
| 819 | |||
| 820 | // Set the groups to be acquired | ||
| 821 | T::regs() | ||
| 822 | .iogcsr() | ||
| 823 | .write(|w| w.0 = Self::extract_groups(config.channel_ios)); | ||
| 824 | |||
| 825 | // Disable interrupts | ||
| 826 | T::regs().ier().modify(|w| { | ||
| 827 | w.set_eoaie(false); | ||
| 828 | w.set_mceie(false); | ||
| 829 | }); | ||
| 830 | |||
| 831 | // Clear flags | ||
| 832 | T::regs().icr().modify(|w| { | ||
| 833 | w.set_eoaic(true); | ||
| 834 | w.set_mceic(true); | ||
| 835 | }); | ||
| 836 | |||
| 837 | unsafe { | ||
| 838 | T::Interrupt::enable(); | ||
| 839 | } | ||
| 840 | |||
| 841 | Self { | ||
| 842 | _peri: peri, | ||
| 843 | _g1: g1, | ||
| 844 | _g2: g2, | ||
| 845 | _g3: g3, | ||
| 846 | _g4: g4, | ||
| 847 | _g5: g5, | ||
| 848 | _g6: g6, | ||
| 849 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 850 | _g7: g7, | ||
| 851 | #[cfg(tsc_v3)] | ||
| 852 | _g8: g8, | ||
| 853 | state: State::Ready, | ||
| 854 | config, | ||
| 855 | _kind: PhantomData, | ||
| 856 | } | ||
| 857 | } | ||
| 858 | |||
| 859 | /// Start charge transfer acquisition | ||
| 860 | pub fn start(&mut self) { | ||
| 861 | self.state = State::Busy; | ||
| 862 | |||
| 863 | // Disable interrupts | ||
| 864 | T::regs().ier().modify(|w| { | ||
| 865 | w.set_eoaie(false); | ||
| 866 | w.set_mceie(false); | ||
| 867 | }); | ||
| 868 | |||
| 869 | // Clear flags | ||
| 870 | T::regs().icr().modify(|w| { | ||
| 871 | w.set_eoaic(true); | ||
| 872 | w.set_mceic(true); | ||
| 873 | }); | ||
| 874 | |||
| 875 | // Set the touch sensing IOs not acquired to the default mode | ||
| 876 | T::regs().cr().modify(|w| { | ||
| 877 | w.set_iodef(self.config.io_default_mode); | ||
| 878 | }); | ||
| 879 | |||
| 880 | // Start the acquisition | ||
| 881 | T::regs().cr().modify(|w| { | ||
| 882 | w.set_start(true); | ||
| 883 | }); | ||
| 884 | } | ||
| 885 | |||
| 886 | /// Stop charge transfer acquisition | ||
| 887 | pub fn stop(&mut self) { | ||
| 888 | T::regs().cr().modify(|w| { | ||
| 889 | w.set_start(false); | ||
| 890 | }); | ||
| 891 | |||
| 892 | // Set the touch sensing IOs in low power mode | ||
| 893 | T::regs().cr().modify(|w| { | ||
| 894 | w.set_iodef(false); | ||
| 895 | }); | ||
| 896 | |||
| 897 | // Clear flags | ||
| 898 | T::regs().icr().modify(|w| { | ||
| 899 | w.set_eoaic(true); | ||
| 900 | w.set_mceic(true); | ||
| 901 | }); | ||
| 902 | |||
| 903 | self.state = State::Ready; | ||
| 904 | } | ||
| 905 | |||
| 906 | /// Get current state of acquisition | ||
| 907 | pub fn get_state(&mut self) -> State { | ||
| 908 | if self.state == State::Busy { | ||
| 909 | if T::regs().isr().read().eoaf() { | ||
| 910 | if T::regs().isr().read().mcef() { | ||
| 911 | self.state = State::Error | ||
| 912 | } else { | ||
| 913 | self.state = State::Ready | ||
| 914 | } | ||
| 915 | } | ||
| 916 | } | ||
| 917 | self.state | ||
| 918 | } | ||
| 919 | |||
| 920 | /// Get the individual group status to check acquisition complete | ||
| 921 | pub fn group_get_status(&mut self, index: Group) -> GroupStatus { | ||
| 922 | // Status bits are set by hardware when the acquisition on the corresponding | ||
| 923 | // enabled analog IO group is complete, cleared when new acquisition is started | ||
| 924 | let status = match index { | ||
| 925 | Group::One => T::regs().iogcsr().read().g1s(), | ||
| 926 | Group::Two => T::regs().iogcsr().read().g2s(), | ||
| 927 | Group::Three => T::regs().iogcsr().read().g3s(), | ||
| 928 | Group::Four => T::regs().iogcsr().read().g4s(), | ||
| 929 | Group::Five => T::regs().iogcsr().read().g5s(), | ||
| 930 | Group::Six => T::regs().iogcsr().read().g6s(), | ||
| 931 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 932 | Group::Seven => T::regs().iogcsr().read().g7s(), | ||
| 933 | #[cfg(tsc_v3)] | ||
| 934 | Group::Eight => T::regs().iogcsr().read().g8s(), | ||
| 935 | }; | ||
| 936 | match status { | ||
| 937 | true => GroupStatus::Complete, | ||
| 938 | false => GroupStatus::Ongoing, | ||
| 939 | } | ||
| 940 | } | ||
| 941 | |||
| 942 | /// Get the count for the acquisiton, valid once group status is set | ||
| 943 | pub fn group_get_value(&mut self, index: Group) -> u16 { | ||
| 944 | T::regs().iogcr(index.into()).read().cnt() | ||
| 945 | } | ||
| 946 | |||
| 947 | /// Discharge the IOs for subsequent acquisition | ||
| 948 | pub fn discharge_io(&mut self, status: bool) { | ||
| 949 | // Set the touch sensing IOs in low power mode | ||
| 950 | T::regs().cr().modify(|w| { | ||
| 951 | w.set_iodef(!status); | ||
| 952 | }); | ||
| 953 | } | ||
| 954 | } | ||
| 955 | |||
| 956 | impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> { | ||
| 957 | fn drop(&mut self) { | ||
| 958 | rcc::disable::<T>(); | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 962 | pub(crate) trait SealedInstance { | 139 | pub(crate) trait SealedInstance { |
| 963 | fn regs() -> crate::pac::tsc::Tsc; | 140 | fn regs() -> crate::pac::tsc::Tsc; |
| 964 | fn waker() -> &'static AtomicWaker; | 141 | fn waker() -> &'static AtomicWaker; |
| @@ -966,7 +143,7 @@ pub(crate) trait SealedInstance { | |||
| 966 | 143 | ||
| 967 | /// TSC instance trait | 144 | /// TSC instance trait |
| 968 | #[allow(private_bounds)] | 145 | #[allow(private_bounds)] |
| 969 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral { | 146 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral { |
| 970 | /// Interrupt for this TSC instance | 147 | /// Interrupt for this TSC instance |
| 971 | type Interrupt: interrupt::typelevel::Interrupt; | 148 | type Interrupt: interrupt::typelevel::Interrupt; |
| 972 | } | 149 | } |
| @@ -988,36 +165,3 @@ foreach_interrupt!( | |||
| 988 | } | 165 | } |
| 989 | }; | 166 | }; |
| 990 | ); | 167 | ); |
| 991 | |||
| 992 | pin_trait!(G1IO1Pin, Instance); | ||
| 993 | pin_trait!(G1IO2Pin, Instance); | ||
| 994 | pin_trait!(G1IO3Pin, Instance); | ||
| 995 | pin_trait!(G1IO4Pin, Instance); | ||
| 996 | pin_trait!(G2IO1Pin, Instance); | ||
| 997 | pin_trait!(G2IO2Pin, Instance); | ||
| 998 | pin_trait!(G2IO3Pin, Instance); | ||
| 999 | pin_trait!(G2IO4Pin, Instance); | ||
| 1000 | pin_trait!(G3IO1Pin, Instance); | ||
| 1001 | pin_trait!(G3IO2Pin, Instance); | ||
| 1002 | pin_trait!(G3IO3Pin, Instance); | ||
| 1003 | pin_trait!(G3IO4Pin, Instance); | ||
| 1004 | pin_trait!(G4IO1Pin, Instance); | ||
| 1005 | pin_trait!(G4IO2Pin, Instance); | ||
| 1006 | pin_trait!(G4IO3Pin, Instance); | ||
| 1007 | pin_trait!(G4IO4Pin, Instance); | ||
| 1008 | pin_trait!(G5IO1Pin, Instance); | ||
| 1009 | pin_trait!(G5IO2Pin, Instance); | ||
| 1010 | pin_trait!(G5IO3Pin, Instance); | ||
| 1011 | pin_trait!(G5IO4Pin, Instance); | ||
| 1012 | pin_trait!(G6IO1Pin, Instance); | ||
| 1013 | pin_trait!(G6IO2Pin, Instance); | ||
| 1014 | pin_trait!(G6IO3Pin, Instance); | ||
| 1015 | pin_trait!(G6IO4Pin, Instance); | ||
| 1016 | pin_trait!(G7IO1Pin, Instance); | ||
| 1017 | pin_trait!(G7IO2Pin, Instance); | ||
| 1018 | pin_trait!(G7IO3Pin, Instance); | ||
| 1019 | pin_trait!(G7IO4Pin, Instance); | ||
| 1020 | pin_trait!(G8IO1Pin, Instance); | ||
| 1021 | pin_trait!(G8IO2Pin, Instance); | ||
| 1022 | pin_trait!(G8IO3Pin, Instance); | ||
| 1023 | pin_trait!(G8IO4Pin, Instance); | ||
diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs new file mode 100644 index 000000000..6f914a94e --- /dev/null +++ b/embassy-stm32/src/tsc/pin_groups.rs | |||
| @@ -0,0 +1,663 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::ops::BitOr; | ||
| 3 | |||
| 4 | use super::errors::GroupError; | ||
| 5 | use super::io_pin::*; | ||
| 6 | use super::Instance; | ||
| 7 | use crate::gpio::{AfType, AnyPin, OutputType, Speed}; | ||
| 8 | use crate::Peri; | ||
| 9 | |||
| 10 | /// Pin type definition to control IO parameters | ||
| 11 | #[derive(PartialEq, Clone, Copy)] | ||
| 12 | pub enum PinType { | ||
| 13 | /// Sensing channel pin connected to an electrode | ||
| 14 | Channel, | ||
| 15 | /// Sampling capacitor pin, one required for every pin group | ||
| 16 | Sample, | ||
| 17 | /// Shield pin connected to capacitive sensing shield | ||
| 18 | Shield, | ||
| 19 | } | ||
| 20 | |||
| 21 | /// Pin struct that maintains usage | ||
| 22 | #[allow(missing_docs)] | ||
| 23 | pub struct Pin<'d, T, Group> { | ||
| 24 | _pin: Peri<'d, AnyPin>, | ||
| 25 | role: PinType, | ||
| 26 | tsc_io_pin: IOPin, | ||
| 27 | phantom: PhantomData<(T, Group)>, | ||
| 28 | } | ||
| 29 | |||
| 30 | impl<'d, T, Group> Pin<'d, T, Group> { | ||
| 31 | /// Returns the role of this TSC pin. | ||
| 32 | /// | ||
| 33 | /// The role indicates whether this pin is configured as a channel, | ||
| 34 | /// sampling capacitor, or shield in the TSC group. | ||
| 35 | /// | ||
| 36 | /// # Returns | ||
| 37 | /// The `PinType` representing the role of this pin. | ||
| 38 | pub fn role(&self) -> PinType { | ||
| 39 | self.role | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Returns the TSC IO pin associated with this pin. | ||
| 43 | /// | ||
| 44 | /// This method provides access to the specific TSC IO pin configuration, | ||
| 45 | /// which includes information about the pin's group and position within that group. | ||
| 46 | /// | ||
| 47 | /// # Returns | ||
| 48 | /// The `IOPin` representing this pin's TSC-specific configuration. | ||
| 49 | pub fn tsc_io_pin(&self) -> IOPin { | ||
| 50 | self.tsc_io_pin | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Represents a group of TSC (Touch Sensing Controller) pins. | ||
| 55 | /// | ||
| 56 | /// In the TSC peripheral, pins are organized into groups of four IOs. Each group | ||
| 57 | /// must have exactly one sampling capacitor pin and can have multiple channel pins | ||
| 58 | /// or a single shield pin. This structure encapsulates these pin configurations | ||
| 59 | /// for a single TSC group. | ||
| 60 | /// | ||
| 61 | /// # Pin Roles | ||
| 62 | /// - Sampling Capacitor: One required per group, used for charge transfer. | ||
| 63 | /// - Channel: Sensing pins connected to electrodes for touch detection. | ||
| 64 | /// - Shield: Optional, used for active shielding to improve sensitivity. | ||
| 65 | /// | ||
| 66 | /// # Constraints | ||
| 67 | /// - Each group must have exactly one sampling capacitor pin. | ||
| 68 | /// - A group can have either channel pins or a shield pin, but not both. | ||
| 69 | /// - No more than one shield pin is allowed across all groups. | ||
| 70 | #[allow(missing_docs)] | ||
| 71 | pub struct PinGroup<'d, T, Group> { | ||
| 72 | pin1: Option<Pin<'d, T, Group>>, | ||
| 73 | pin2: Option<Pin<'d, T, Group>>, | ||
| 74 | pin3: Option<Pin<'d, T, Group>>, | ||
| 75 | pin4: Option<Pin<'d, T, Group>>, | ||
| 76 | } | ||
| 77 | |||
| 78 | impl<'d, T, G> Default for PinGroup<'d, T, G> { | ||
| 79 | fn default() -> Self { | ||
| 80 | Self { | ||
| 81 | pin1: None, | ||
| 82 | pin2: None, | ||
| 83 | pin3: None, | ||
| 84 | pin4: None, | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Defines roles and traits for TSC (Touch Sensing Controller) pins. | ||
| 90 | /// | ||
| 91 | /// This module contains marker types and traits that represent different roles | ||
| 92 | /// a TSC pin can have, such as channel, sample, or shield. | ||
| 93 | pub mod pin_roles { | ||
| 94 | use super::{OutputType, PinType}; | ||
| 95 | |||
| 96 | /// Marker type for a TSC channel pin. | ||
| 97 | #[derive(PartialEq, Clone, Copy, Debug)] | ||
| 98 | pub struct Channel; | ||
| 99 | |||
| 100 | /// Marker type for a TSC sampling pin. | ||
| 101 | #[derive(PartialEq, Clone, Copy, Debug)] | ||
| 102 | pub struct Sample; | ||
| 103 | |||
| 104 | /// Marker type for a TSC shield pin. | ||
| 105 | #[derive(PartialEq, Clone, Copy, Debug)] | ||
| 106 | pub struct Shield; | ||
| 107 | |||
| 108 | /// Trait for TSC pin roles. | ||
| 109 | /// | ||
| 110 | /// This trait defines the behavior and properties of different TSC pin roles. | ||
| 111 | /// It is implemented by the marker types `Channel`, `Sample`, and `Shield`. | ||
| 112 | pub trait Role { | ||
| 113 | /// Returns the `PinType` associated with this role. | ||
| 114 | fn pin_type() -> PinType; | ||
| 115 | |||
| 116 | /// Returns the `OutputType` associated with this role. | ||
| 117 | fn output_type() -> OutputType; | ||
| 118 | } | ||
| 119 | |||
| 120 | impl Role for Channel { | ||
| 121 | fn pin_type() -> PinType { | ||
| 122 | PinType::Channel | ||
| 123 | } | ||
| 124 | fn output_type() -> OutputType { | ||
| 125 | OutputType::PushPull | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | impl Role for Sample { | ||
| 130 | fn pin_type() -> PinType { | ||
| 131 | PinType::Sample | ||
| 132 | } | ||
| 133 | fn output_type() -> OutputType { | ||
| 134 | OutputType::OpenDrain | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl Role for Shield { | ||
| 139 | fn pin_type() -> PinType { | ||
| 140 | PinType::Shield | ||
| 141 | } | ||
| 142 | fn output_type() -> OutputType { | ||
| 143 | OutputType::PushPull | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Represents a group of TSC pins with their associated roles. | ||
| 149 | /// | ||
| 150 | /// This struct allows for type-safe configuration of TSC pin groups, | ||
| 151 | /// ensuring that pins are assigned appropriate roles within their group. | ||
| 152 | /// This type is essentially just a wrapper type around a `PinGroup` value. | ||
| 153 | /// | ||
| 154 | /// # Type Parameters | ||
| 155 | /// - `'d`: Lifetime of the pin group. | ||
| 156 | /// - `T`: The TSC instance type. | ||
| 157 | /// - `G`: The group identifier. | ||
| 158 | /// - `R1`, `R2`, `R3`, `R4`: Role types for each pin in the group, defaulting to `Channel`. | ||
| 159 | pub struct PinGroupWithRoles< | ||
| 160 | 'd, | ||
| 161 | T: Instance, | ||
| 162 | G, | ||
| 163 | R1 = pin_roles::Channel, | ||
| 164 | R2 = pin_roles::Channel, | ||
| 165 | R3 = pin_roles::Channel, | ||
| 166 | R4 = pin_roles::Channel, | ||
| 167 | > { | ||
| 168 | /// The underlying pin group without role information. | ||
| 169 | pub pin_group: PinGroup<'d, T, G>, | ||
| 170 | _phantom: PhantomData<(R1, R2, R3, R4)>, | ||
| 171 | } | ||
| 172 | |||
| 173 | impl<'d, T: Instance, G, R1, R2, R3, R4> Default for PinGroupWithRoles<'d, T, G, R1, R2, R3, R4> { | ||
| 174 | fn default() -> Self { | ||
| 175 | Self { | ||
| 176 | pin_group: PinGroup::default(), | ||
| 177 | _phantom: PhantomData, | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | impl<'d, T: Instance, G> PinGroup<'d, T, G> { | ||
| 183 | fn contains_exactly_one_shield_pin(&self) -> bool { | ||
| 184 | let shield_count = self.shield_pins().count(); | ||
| 185 | shield_count == 1 | ||
| 186 | } | ||
| 187 | |||
| 188 | fn check_group(&self) -> Result<(), GroupError> { | ||
| 189 | let mut channel_count = 0; | ||
| 190 | let mut shield_count = 0; | ||
| 191 | let mut sample_count = 0; | ||
| 192 | for pin in self.pins().into_iter().flatten() { | ||
| 193 | match pin.role { | ||
| 194 | PinType::Channel => { | ||
| 195 | channel_count += 1; | ||
| 196 | } | ||
| 197 | PinType::Shield => { | ||
| 198 | shield_count += 1; | ||
| 199 | } | ||
| 200 | PinType::Sample => { | ||
| 201 | sample_count += 1; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | // Every group requires exactly one sampling capacitor | ||
| 207 | if sample_count != 1 { | ||
| 208 | return Err(GroupError::NoSamplingCapacitor); | ||
| 209 | } | ||
| 210 | |||
| 211 | // Each group must have at least one shield or channel IO | ||
| 212 | if shield_count == 0 && channel_count == 0 { | ||
| 213 | return Err(GroupError::NoChannelOrShield); | ||
| 214 | } | ||
| 215 | |||
| 216 | // Any group can either contain channel ios or a shield IO. | ||
| 217 | // (An active shield requires its own sampling capacitor) | ||
| 218 | if shield_count != 0 && channel_count != 0 { | ||
| 219 | return Err(GroupError::MixedChannelAndShield); | ||
| 220 | } | ||
| 221 | |||
| 222 | // No more than one shield IO is allow per group and amongst all groups | ||
| 223 | if shield_count > 1 { | ||
| 224 | return Err(GroupError::MultipleShields); | ||
| 225 | } | ||
| 226 | |||
| 227 | Ok(()) | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Returns a reference to the first pin in the group, if configured. | ||
| 231 | pub fn pin1(&self) -> Option<&Pin<'d, T, G>> { | ||
| 232 | self.pin1.as_ref() | ||
| 233 | } | ||
| 234 | |||
| 235 | /// Returns a reference to the second pin in the group, if configured. | ||
| 236 | pub fn pin2(&self) -> Option<&Pin<'d, T, G>> { | ||
| 237 | self.pin2.as_ref() | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Returns a reference to the third pin in the group, if configured. | ||
| 241 | pub fn pin3(&self) -> Option<&Pin<'d, T, G>> { | ||
| 242 | self.pin3.as_ref() | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Returns a reference to the fourth pin in the group, if configured. | ||
| 246 | pub fn pin4(&self) -> Option<&Pin<'d, T, G>> { | ||
| 247 | self.pin4.as_ref() | ||
| 248 | } | ||
| 249 | |||
| 250 | fn sample_pins(&self) -> impl Iterator<Item = IOPin> + '_ { | ||
| 251 | self.pins_filtered(PinType::Sample) | ||
| 252 | } | ||
| 253 | |||
| 254 | fn shield_pins(&self) -> impl Iterator<Item = IOPin> + '_ { | ||
| 255 | self.pins_filtered(PinType::Shield) | ||
| 256 | } | ||
| 257 | |||
| 258 | fn channel_pins(&self) -> impl Iterator<Item = IOPin> + '_ { | ||
| 259 | self.pins_filtered(PinType::Channel) | ||
| 260 | } | ||
| 261 | |||
| 262 | fn pins_filtered(&self, pin_type: PinType) -> impl Iterator<Item = IOPin> + '_ { | ||
| 263 | self.pins().into_iter().filter_map(move |pin| { | ||
| 264 | pin.as_ref() | ||
| 265 | .and_then(|p| if p.role == pin_type { Some(p.tsc_io_pin) } else { None }) | ||
| 266 | }) | ||
| 267 | } | ||
| 268 | |||
| 269 | fn make_channel_ios_mask(&self) -> u32 { | ||
| 270 | self.channel_pins().fold(0, u32::bitor) | ||
| 271 | } | ||
| 272 | |||
| 273 | fn make_shield_ios_mask(&self) -> u32 { | ||
| 274 | self.shield_pins().fold(0, u32::bitor) | ||
| 275 | } | ||
| 276 | |||
| 277 | fn make_sample_ios_mask(&self) -> u32 { | ||
| 278 | self.sample_pins().fold(0, u32::bitor) | ||
| 279 | } | ||
| 280 | |||
| 281 | fn pins(&self) -> [&Option<Pin<'d, T, G>>; 4] { | ||
| 282 | [&self.pin1, &self.pin2, &self.pin3, &self.pin4] | ||
| 283 | } | ||
| 284 | |||
| 285 | fn pins_mut(&mut self) -> [&mut Option<Pin<'d, T, G>>; 4] { | ||
| 286 | [&mut self.pin1, &mut self.pin2, &mut self.pin3, &mut self.pin4] | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 291 | macro_rules! TSC_V2_V3_GUARD { | ||
| 292 | ($e:expr) => {{ | ||
| 293 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 294 | { | ||
| 295 | $e | ||
| 296 | } | ||
| 297 | #[cfg(not(any(tsc_v2, tsc_v3)))] | ||
| 298 | { | ||
| 299 | compile_error!("Group 7 is not supported in this TSC version") | ||
| 300 | } | ||
| 301 | }}; | ||
| 302 | } | ||
| 303 | |||
| 304 | #[cfg(tsc_v3)] | ||
| 305 | macro_rules! TSC_V3_GUARD { | ||
| 306 | ($e:expr) => {{ | ||
| 307 | #[cfg(tsc_v3)] | ||
| 308 | { | ||
| 309 | $e | ||
| 310 | } | ||
| 311 | #[cfg(not(tsc_v3))] | ||
| 312 | { | ||
| 313 | compile_error!("Group 8 is not supported in this TSC version") | ||
| 314 | } | ||
| 315 | }}; | ||
| 316 | } | ||
| 317 | |||
| 318 | macro_rules! trait_to_io_pin { | ||
| 319 | (G1IO1Pin) => { | ||
| 320 | IOPin::Group1Io1 | ||
| 321 | }; | ||
| 322 | (G1IO2Pin) => { | ||
| 323 | IOPin::Group1Io2 | ||
| 324 | }; | ||
| 325 | (G1IO3Pin) => { | ||
| 326 | IOPin::Group1Io3 | ||
| 327 | }; | ||
| 328 | (G1IO4Pin) => { | ||
| 329 | IOPin::Group1Io4 | ||
| 330 | }; | ||
| 331 | |||
| 332 | (G2IO1Pin) => { | ||
| 333 | IOPin::Group2Io1 | ||
| 334 | }; | ||
| 335 | (G2IO2Pin) => { | ||
| 336 | IOPin::Group2Io2 | ||
| 337 | }; | ||
| 338 | (G2IO3Pin) => { | ||
| 339 | IOPin::Group2Io3 | ||
| 340 | }; | ||
| 341 | (G2IO4Pin) => { | ||
| 342 | IOPin::Group2Io4 | ||
| 343 | }; | ||
| 344 | |||
| 345 | (G3IO1Pin) => { | ||
| 346 | IOPin::Group3Io1 | ||
| 347 | }; | ||
| 348 | (G3IO2Pin) => { | ||
| 349 | IOPin::Group3Io2 | ||
| 350 | }; | ||
| 351 | (G3IO3Pin) => { | ||
| 352 | IOPin::Group3Io3 | ||
| 353 | }; | ||
| 354 | (G3IO4Pin) => { | ||
| 355 | IOPin::Group3Io4 | ||
| 356 | }; | ||
| 357 | |||
| 358 | (G4IO1Pin) => { | ||
| 359 | IOPin::Group4Io1 | ||
| 360 | }; | ||
| 361 | (G4IO2Pin) => { | ||
| 362 | IOPin::Group4Io2 | ||
| 363 | }; | ||
| 364 | (G4IO3Pin) => { | ||
| 365 | IOPin::Group4Io3 | ||
| 366 | }; | ||
| 367 | (G4IO4Pin) => { | ||
| 368 | IOPin::Group4Io4 | ||
| 369 | }; | ||
| 370 | |||
| 371 | (G5IO1Pin) => { | ||
| 372 | IOPin::Group5Io1 | ||
| 373 | }; | ||
| 374 | (G5IO2Pin) => { | ||
| 375 | IOPin::Group5Io2 | ||
| 376 | }; | ||
| 377 | (G5IO3Pin) => { | ||
| 378 | IOPin::Group5Io3 | ||
| 379 | }; | ||
| 380 | (G5IO4Pin) => { | ||
| 381 | IOPin::Group5Io4 | ||
| 382 | }; | ||
| 383 | |||
| 384 | (G6IO1Pin) => { | ||
| 385 | IOPin::Group6Io1 | ||
| 386 | }; | ||
| 387 | (G6IO2Pin) => { | ||
| 388 | IOPin::Group6Io2 | ||
| 389 | }; | ||
| 390 | (G6IO3Pin) => { | ||
| 391 | IOPin::Group6Io3 | ||
| 392 | }; | ||
| 393 | (G6IO4Pin) => { | ||
| 394 | IOPin::Group6Io4 | ||
| 395 | }; | ||
| 396 | |||
| 397 | (G7IO1Pin) => { | ||
| 398 | TSC_V2_V3_GUARD!(IOPin::Group7Io1) | ||
| 399 | }; | ||
| 400 | (G7IO2Pin) => { | ||
| 401 | TSC_V2_V3_GUARD!(IOPin::Group7Io2) | ||
| 402 | }; | ||
| 403 | (G7IO3Pin) => { | ||
| 404 | TSC_V2_V3_GUARD!(IOPin::Group7Io3) | ||
| 405 | }; | ||
| 406 | (G7IO4Pin) => { | ||
| 407 | TSC_V2_V3_GUARD!(IOPin::Group7Io4) | ||
| 408 | }; | ||
| 409 | |||
| 410 | (G8IO1Pin) => { | ||
| 411 | TSC_V3_GUARD!(IOPin::Group8Io1) | ||
| 412 | }; | ||
| 413 | (G8IO2Pin) => { | ||
| 414 | TSC_V3_GUARD!(IOPin::Group8Io2) | ||
| 415 | }; | ||
| 416 | (G8IO3Pin) => { | ||
| 417 | TSC_V3_GUARD!(IOPin::Group8Io3) | ||
| 418 | }; | ||
| 419 | (G8IO4Pin) => { | ||
| 420 | TSC_V3_GUARD!(IOPin::Group8Io4) | ||
| 421 | }; | ||
| 422 | } | ||
| 423 | |||
| 424 | macro_rules! impl_set_io { | ||
| 425 | ($method:ident, $group:ident, $trait:ident, $index:expr) => { | ||
| 426 | #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] | ||
| 427 | pub fn $method<Role: pin_roles::Role>(&mut self, pin: Peri<'d, impl $trait<T>>) -> IOPinWithRole<$group, Role> { | ||
| 428 | critical_section::with(|_| { | ||
| 429 | pin.set_low(); | ||
| 430 | pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh)); | ||
| 431 | let tsc_io_pin = trait_to_io_pin!($trait); | ||
| 432 | let new_pin = Pin { | ||
| 433 | _pin: pin.into(), | ||
| 434 | role: Role::pin_type(), | ||
| 435 | tsc_io_pin, | ||
| 436 | phantom: PhantomData, | ||
| 437 | }; | ||
| 438 | *self.pin_group.pins_mut()[$index] = Some(new_pin); | ||
| 439 | IOPinWithRole { | ||
| 440 | pin: tsc_io_pin, | ||
| 441 | phantom: PhantomData, | ||
| 442 | } | ||
| 443 | }) | ||
| 444 | } | ||
| 445 | }; | ||
| 446 | } | ||
| 447 | |||
| 448 | macro_rules! group_impl { | ||
| 449 | ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { | ||
| 450 | impl<'d, T: Instance, R1: pin_roles::Role, R2: pin_roles::Role, R3: pin_roles::Role, R4: pin_roles::Role> | ||
| 451 | PinGroupWithRoles<'d, T, $group, R1, R2, R3, R4> | ||
| 452 | { | ||
| 453 | impl_set_io!(set_io1, $group, $trait1, 0); | ||
| 454 | impl_set_io!(set_io2, $group, $trait2, 1); | ||
| 455 | impl_set_io!(set_io3, $group, $trait3, 2); | ||
| 456 | impl_set_io!(set_io4, $group, $trait4, 3); | ||
| 457 | } | ||
| 458 | }; | ||
| 459 | } | ||
| 460 | |||
| 461 | group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin); | ||
| 462 | group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin); | ||
| 463 | group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin); | ||
| 464 | group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin); | ||
| 465 | group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin); | ||
| 466 | group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin); | ||
| 467 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 468 | group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin); | ||
| 469 | #[cfg(tsc_v3)] | ||
| 470 | group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin); | ||
| 471 | |||
| 472 | /// Group 1 marker type. | ||
| 473 | #[derive(Clone, Copy, Debug)] | ||
| 474 | pub enum G1 {} | ||
| 475 | /// Group 2 marker type. | ||
| 476 | #[derive(Clone, Copy, Debug)] | ||
| 477 | pub enum G2 {} | ||
| 478 | /// Group 3 marker type. | ||
| 479 | #[derive(Clone, Copy, Debug)] | ||
| 480 | pub enum G3 {} | ||
| 481 | /// Group 4 marker type. | ||
| 482 | #[derive(Clone, Copy, Debug)] | ||
| 483 | pub enum G4 {} | ||
| 484 | /// Group 5 marker type. | ||
| 485 | #[derive(Clone, Copy, Debug)] | ||
| 486 | pub enum G5 {} | ||
| 487 | /// Group 6 marker type. | ||
| 488 | #[derive(Clone, Copy, Debug)] | ||
| 489 | pub enum G6 {} | ||
| 490 | /// Group 7 marker type. | ||
| 491 | #[derive(Clone, Copy, Debug)] | ||
| 492 | pub enum G7 {} | ||
| 493 | /// Group 8 marker type. | ||
| 494 | #[derive(Clone, Copy, Debug)] | ||
| 495 | pub enum G8 {} | ||
| 496 | |||
| 497 | /// Represents the collection of pin groups for the Touch Sensing Controller (TSC). | ||
| 498 | /// | ||
| 499 | /// Each field corresponds to a specific group of TSC pins: | ||
| 500 | #[allow(missing_docs)] | ||
| 501 | pub struct PinGroups<'d, T: Instance> { | ||
| 502 | pub g1: Option<PinGroup<'d, T, G1>>, | ||
| 503 | pub g2: Option<PinGroup<'d, T, G2>>, | ||
| 504 | pub g3: Option<PinGroup<'d, T, G3>>, | ||
| 505 | pub g4: Option<PinGroup<'d, T, G4>>, | ||
| 506 | pub g5: Option<PinGroup<'d, T, G5>>, | ||
| 507 | pub g6: Option<PinGroup<'d, T, G6>>, | ||
| 508 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 509 | pub g7: Option<PinGroup<'d, T, G7>>, | ||
| 510 | #[cfg(tsc_v3)] | ||
| 511 | pub g8: Option<PinGroup<'d, T, G8>>, | ||
| 512 | } | ||
| 513 | |||
| 514 | impl<'d, T: Instance> PinGroups<'d, T> { | ||
| 515 | pub(super) fn check(&self) -> Result<(), GroupError> { | ||
| 516 | let mut shield_count = 0; | ||
| 517 | |||
| 518 | // Helper function to check a single group | ||
| 519 | fn check_group<C, T: Instance>( | ||
| 520 | group: &Option<PinGroup<'_, T, C>>, | ||
| 521 | shield_count: &mut u32, | ||
| 522 | ) -> Result<(), GroupError> { | ||
| 523 | if let Some(group) = group { | ||
| 524 | group.check_group()?; | ||
| 525 | if group.contains_exactly_one_shield_pin() { | ||
| 526 | *shield_count += 1; | ||
| 527 | if *shield_count > 1 { | ||
| 528 | return Err(GroupError::MultipleShields); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | } | ||
| 532 | Ok(()) | ||
| 533 | } | ||
| 534 | |||
| 535 | // Check each group | ||
| 536 | check_group(&self.g1, &mut shield_count)?; | ||
| 537 | check_group(&self.g2, &mut shield_count)?; | ||
| 538 | check_group(&self.g3, &mut shield_count)?; | ||
| 539 | check_group(&self.g4, &mut shield_count)?; | ||
| 540 | check_group(&self.g5, &mut shield_count)?; | ||
| 541 | check_group(&self.g6, &mut shield_count)?; | ||
| 542 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 543 | check_group(&self.g7, &mut shield_count)?; | ||
| 544 | #[cfg(tsc_v3)] | ||
| 545 | check_group(&self.g8, &mut shield_count)?; | ||
| 546 | |||
| 547 | Ok(()) | ||
| 548 | } | ||
| 549 | |||
| 550 | pub(super) fn make_channel_ios_mask(&self) -> u32 { | ||
| 551 | #[allow(unused_mut)] | ||
| 552 | let mut mask = self.g1.as_ref().map_or(0, |g| g.make_channel_ios_mask()) | ||
| 553 | | self.g2.as_ref().map_or(0, |g| g.make_channel_ios_mask()) | ||
| 554 | | self.g3.as_ref().map_or(0, |g| g.make_channel_ios_mask()) | ||
| 555 | | self.g4.as_ref().map_or(0, |g| g.make_channel_ios_mask()) | ||
| 556 | | self.g5.as_ref().map_or(0, |g| g.make_channel_ios_mask()) | ||
| 557 | | self.g6.as_ref().map_or(0, |g| g.make_channel_ios_mask()); | ||
| 558 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 559 | { | ||
| 560 | mask |= self.g7.as_ref().map_or(0, |g| g.make_channel_ios_mask()); | ||
| 561 | } | ||
| 562 | #[cfg(tsc_v3)] | ||
| 563 | { | ||
| 564 | mask |= self.g8.as_ref().map_or(0, |g| g.make_channel_ios_mask()); | ||
| 565 | } | ||
| 566 | mask | ||
| 567 | } | ||
| 568 | |||
| 569 | pub(super) fn make_shield_ios_mask(&self) -> u32 { | ||
| 570 | #[allow(unused_mut)] | ||
| 571 | let mut mask = self.g1.as_ref().map_or(0, |g| g.make_shield_ios_mask()) | ||
| 572 | | self.g2.as_ref().map_or(0, |g| g.make_shield_ios_mask()) | ||
| 573 | | self.g3.as_ref().map_or(0, |g| g.make_shield_ios_mask()) | ||
| 574 | | self.g4.as_ref().map_or(0, |g| g.make_shield_ios_mask()) | ||
| 575 | | self.g5.as_ref().map_or(0, |g| g.make_shield_ios_mask()) | ||
| 576 | | self.g6.as_ref().map_or(0, |g| g.make_shield_ios_mask()); | ||
| 577 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 578 | { | ||
| 579 | mask |= self.g7.as_ref().map_or(0, |g| g.make_shield_ios_mask()); | ||
| 580 | } | ||
| 581 | #[cfg(tsc_v3)] | ||
| 582 | { | ||
| 583 | mask |= self.g8.as_ref().map_or(0, |g| g.make_shield_ios_mask()); | ||
| 584 | } | ||
| 585 | mask | ||
| 586 | } | ||
| 587 | |||
| 588 | pub(super) fn make_sample_ios_mask(&self) -> u32 { | ||
| 589 | #[allow(unused_mut)] | ||
| 590 | let mut mask = self.g1.as_ref().map_or(0, |g| g.make_sample_ios_mask()) | ||
| 591 | | self.g2.as_ref().map_or(0, |g| g.make_sample_ios_mask()) | ||
| 592 | | self.g3.as_ref().map_or(0, |g| g.make_sample_ios_mask()) | ||
| 593 | | self.g4.as_ref().map_or(0, |g| g.make_sample_ios_mask()) | ||
| 594 | | self.g5.as_ref().map_or(0, |g| g.make_sample_ios_mask()) | ||
| 595 | | self.g6.as_ref().map_or(0, |g| g.make_sample_ios_mask()); | ||
| 596 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 597 | { | ||
| 598 | mask |= self.g7.as_ref().map_or(0, |g| g.make_sample_ios_mask()); | ||
| 599 | } | ||
| 600 | #[cfg(tsc_v3)] | ||
| 601 | { | ||
| 602 | mask |= self.g8.as_ref().map_or(0, |g| g.make_sample_ios_mask()); | ||
| 603 | } | ||
| 604 | mask | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | impl<'d, T: Instance> Default for PinGroups<'d, T> { | ||
| 609 | fn default() -> Self { | ||
| 610 | Self { | ||
| 611 | g1: None, | ||
| 612 | g2: None, | ||
| 613 | g3: None, | ||
| 614 | g4: None, | ||
| 615 | g5: None, | ||
| 616 | g6: None, | ||
| 617 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 618 | g7: None, | ||
| 619 | #[cfg(tsc_v3)] | ||
| 620 | g8: None, | ||
| 621 | } | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | pin_trait!(G1IO1Pin, Instance); | ||
| 626 | pin_trait!(G1IO2Pin, Instance); | ||
| 627 | pin_trait!(G1IO3Pin, Instance); | ||
| 628 | pin_trait!(G1IO4Pin, Instance); | ||
| 629 | |||
| 630 | pin_trait!(G2IO1Pin, Instance); | ||
| 631 | pin_trait!(G2IO2Pin, Instance); | ||
| 632 | pin_trait!(G2IO3Pin, Instance); | ||
| 633 | pin_trait!(G2IO4Pin, Instance); | ||
| 634 | |||
| 635 | pin_trait!(G3IO1Pin, Instance); | ||
| 636 | pin_trait!(G3IO2Pin, Instance); | ||
| 637 | pin_trait!(G3IO3Pin, Instance); | ||
| 638 | pin_trait!(G3IO4Pin, Instance); | ||
| 639 | |||
| 640 | pin_trait!(G4IO1Pin, Instance); | ||
| 641 | pin_trait!(G4IO2Pin, Instance); | ||
| 642 | pin_trait!(G4IO3Pin, Instance); | ||
| 643 | pin_trait!(G4IO4Pin, Instance); | ||
| 644 | |||
| 645 | pin_trait!(G5IO1Pin, Instance); | ||
| 646 | pin_trait!(G5IO2Pin, Instance); | ||
| 647 | pin_trait!(G5IO3Pin, Instance); | ||
| 648 | pin_trait!(G5IO4Pin, Instance); | ||
| 649 | |||
| 650 | pin_trait!(G6IO1Pin, Instance); | ||
| 651 | pin_trait!(G6IO2Pin, Instance); | ||
| 652 | pin_trait!(G6IO3Pin, Instance); | ||
| 653 | pin_trait!(G6IO4Pin, Instance); | ||
| 654 | |||
| 655 | pin_trait!(G7IO1Pin, Instance); | ||
| 656 | pin_trait!(G7IO2Pin, Instance); | ||
| 657 | pin_trait!(G7IO3Pin, Instance); | ||
| 658 | pin_trait!(G7IO4Pin, Instance); | ||
| 659 | |||
| 660 | pin_trait!(G8IO1Pin, Instance); | ||
| 661 | pin_trait!(G8IO2Pin, Instance); | ||
| 662 | pin_trait!(G8IO3Pin, Instance); | ||
| 663 | pin_trait!(G8IO4Pin, Instance); | ||
diff --git a/embassy-stm32/src/tsc/tsc.rs b/embassy-stm32/src/tsc/tsc.rs new file mode 100644 index 000000000..e92479c26 --- /dev/null +++ b/embassy-stm32/src/tsc/tsc.rs | |||
| @@ -0,0 +1,446 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::ops::BitOr; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use embassy_hal_internal::Peri; | ||
| 7 | |||
| 8 | use super::acquisition_banks::*; | ||
| 9 | use super::config::*; | ||
| 10 | use super::errors::*; | ||
| 11 | use super::io_pin::*; | ||
| 12 | use super::pin_groups::*; | ||
| 13 | use super::types::*; | ||
| 14 | use super::{Instance, InterruptHandler, TSC_NUM_GROUPS}; | ||
| 15 | use crate::interrupt::typelevel::Interrupt; | ||
| 16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | ||
| 17 | use crate::{interrupt, rcc}; | ||
| 18 | |||
| 19 | /// Internal structure holding masks for different types of TSC IOs. | ||
| 20 | /// | ||
| 21 | /// These masks are used during the initial configuration of the TSC peripheral | ||
| 22 | /// and for validating pin types during operations like creating acquisition banks. | ||
| 23 | struct IOMasks { | ||
| 24 | /// Mask representing all configured channel IOs | ||
| 25 | channel_ios: u32, | ||
| 26 | /// Mask representing all configured shield IOs | ||
| 27 | shield_ios: u32, | ||
| 28 | /// Mask representing all configured sampling IOs | ||
| 29 | sampling_ios: u32, | ||
| 30 | } | ||
| 31 | |||
| 32 | /// TSC driver | ||
| 33 | pub struct Tsc<'d, T: Instance, K: PeriMode> { | ||
| 34 | _peri: Peri<'d, T>, | ||
| 35 | _pin_groups: PinGroups<'d, T>, | ||
| 36 | state: State, | ||
| 37 | config: Config, | ||
| 38 | masks: IOMasks, | ||
| 39 | _kind: PhantomData<K>, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { | ||
| 43 | // Helper method to check if a pin is a channel pin | ||
| 44 | fn is_channel_pin(&self, pin: IOPin) -> bool { | ||
| 45 | (self.masks.channel_ios & pin) != 0 | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Get the status of all groups involved in a AcquisitionBank | ||
| 49 | pub fn get_acquisition_bank_status(&self, bank: &AcquisitionBank) -> AcquisitionBankStatus { | ||
| 50 | let mut bank_status = AcquisitionBankStatus::default(); | ||
| 51 | for pin in bank.pins_iterator() { | ||
| 52 | let group = pin.group(); | ||
| 53 | let group_status = self.group_get_status(group); | ||
| 54 | let index: usize = group.into(); | ||
| 55 | bank_status.groups[index] = Some(group_status); | ||
| 56 | } | ||
| 57 | bank_status | ||
| 58 | } | ||
| 59 | |||
| 60 | /// Get the values for all channels involved in a AcquisitionBank | ||
| 61 | pub fn get_acquisition_bank_values(&self, bank: &AcquisitionBank) -> AcquisitionBankReadings { | ||
| 62 | let mut bank_readings = AcquisitionBankReadings::default(); | ||
| 63 | for pin in bank.pins_iterator() { | ||
| 64 | let group = pin.group(); | ||
| 65 | let value = self.group_get_value(group); | ||
| 66 | let reading = ChannelReading { | ||
| 67 | sensor_value: value, | ||
| 68 | tsc_pin: pin, | ||
| 69 | }; | ||
| 70 | let index: usize = group.into(); | ||
| 71 | bank_readings.groups[index] = Some(reading); | ||
| 72 | } | ||
| 73 | bank_readings | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Creates a new TSC acquisition bank from the provided pin configuration. | ||
| 77 | /// | ||
| 78 | /// This method creates a `AcquisitionBank` that can be used for efficient, | ||
| 79 | /// repeated TSC acquisitions. It automatically generates the appropriate mask | ||
| 80 | /// for the provided pins. | ||
| 81 | /// | ||
| 82 | /// # Note on TSC Hardware Limitation | ||
| 83 | /// | ||
| 84 | /// The TSC hardware can only read one channel pin from each TSC group per acquisition. | ||
| 85 | /// | ||
| 86 | /// # Arguments | ||
| 87 | /// * `acquisition_bank_pins` - The pin configuration for the acquisition bank. | ||
| 88 | /// | ||
| 89 | /// # Returns | ||
| 90 | /// A new `AcquisitionBank` instance. | ||
| 91 | /// | ||
| 92 | /// # Example | ||
| 93 | /// | ||
| 94 | /// ``` | ||
| 95 | /// let tsc = // ... initialize TSC | ||
| 96 | /// let tsc_sensor1: tsc::IOPinWithRole<G1, tsc_pin_roles::Channel> = ...; | ||
| 97 | /// let tsc_sensor2: tsc::IOPinWithRole<G2, tsc_pin_roles::Channel> = ...; | ||
| 98 | /// | ||
| 99 | /// let bank = tsc.create_acquisition_bank(AcquisitionBankPins { | ||
| 100 | /// g1_pin: Some(tsc_sensor1), | ||
| 101 | /// g2_pin: Some(tsc_sensor2), | ||
| 102 | /// ..Default::default() | ||
| 103 | /// }); | ||
| 104 | /// | ||
| 105 | /// // Use the bank for acquisitions | ||
| 106 | /// tsc.set_active_channels_bank(&bank); | ||
| 107 | /// tsc.start(); | ||
| 108 | /// // ... perform acquisition ... | ||
| 109 | /// ``` | ||
| 110 | pub fn create_acquisition_bank(&self, acquisition_bank_pins: AcquisitionBankPins) -> AcquisitionBank { | ||
| 111 | let bank_mask = acquisition_bank_pins.iter().fold(0u32, BitOr::bitor); | ||
| 112 | |||
| 113 | AcquisitionBank { | ||
| 114 | pins: acquisition_bank_pins, | ||
| 115 | mask: bank_mask, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | fn make_channels_mask<Itt>(&self, channels: Itt) -> Result<u32, AcquisitionBankError> | ||
| 120 | where | ||
| 121 | Itt: IntoIterator<Item = IOPin>, | ||
| 122 | { | ||
| 123 | let mut group_mask = 0u32; | ||
| 124 | let mut channel_mask = 0u32; | ||
| 125 | |||
| 126 | for channel in channels { | ||
| 127 | if !self.is_channel_pin(channel) { | ||
| 128 | return Err(AcquisitionBankError::InvalidChannelPin); | ||
| 129 | } | ||
| 130 | |||
| 131 | let group = channel.group(); | ||
| 132 | let group_bit: u32 = 1 << Into::<usize>::into(group); | ||
| 133 | if group_mask & group_bit != 0 { | ||
| 134 | return Err(AcquisitionBankError::MultipleChannelsPerGroup); | ||
| 135 | } | ||
| 136 | |||
| 137 | group_mask |= group_bit; | ||
| 138 | channel_mask |= channel; | ||
| 139 | } | ||
| 140 | |||
| 141 | Ok(channel_mask) | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Sets the active channels for the next TSC acquisition. | ||
| 145 | /// | ||
| 146 | /// This is a low-level method that directly sets the channel mask. For most use cases, | ||
| 147 | /// consider using `set_active_channels_bank` with a `AcquisitionBank` instead, which | ||
| 148 | /// provides a higher-level interface and additional safety checks. | ||
| 149 | /// | ||
| 150 | /// This method configures which sensor channels will be read during the next | ||
| 151 | /// touch sensing acquisition cycle. It should be called before starting a new | ||
| 152 | /// acquisition with the start() method. | ||
| 153 | /// | ||
| 154 | /// # Arguments | ||
| 155 | /// * `mask` - A 32-bit mask where each bit represents a channel. Set bits indicate | ||
| 156 | /// active channels. | ||
| 157 | /// | ||
| 158 | /// # Note | ||
| 159 | /// Only one pin from each TSC group can be read for each acquisition. This method | ||
| 160 | /// does not perform checks to ensure this limitation is met. Incorrect masks may | ||
| 161 | /// lead to unexpected behavior. | ||
| 162 | /// | ||
| 163 | /// # Safety | ||
| 164 | /// This method doesn't perform extensive checks on the provided mask. Ensure that | ||
| 165 | /// the mask is valid and adheres to hardware limitations to avoid undefined behavior. | ||
| 166 | pub fn set_active_channels_mask(&mut self, mask: u32) { | ||
| 167 | T::regs().ioccr().write(|w| w.0 = mask | self.masks.shield_ios); | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Convenience method for setting active channels directly from a slice of tsc::IOPin. | ||
| 171 | /// This method performs safety checks but is less efficient for repeated use. | ||
| 172 | pub fn set_active_channels(&mut self, channels: &[IOPin]) -> Result<(), AcquisitionBankError> { | ||
| 173 | let mask = self.make_channels_mask(channels.iter().cloned())?; | ||
| 174 | self.set_active_channels_mask(mask); | ||
| 175 | Ok(()) | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Sets the active channels for the next TSC acquisition using a pre-configured acquisition bank. | ||
| 179 | /// | ||
| 180 | /// This method efficiently configures the TSC peripheral to read the channels specified | ||
| 181 | /// in the provided `AcquisitionBank`. It's the recommended way to set up | ||
| 182 | /// channel configurations for acquisition, especially when using the same set of channels repeatedly. | ||
| 183 | /// | ||
| 184 | /// # Arguments | ||
| 185 | /// | ||
| 186 | /// * `bank` - A reference to a `AcquisitionBank` containing the pre-configured | ||
| 187 | /// TSC channel mask. | ||
| 188 | /// | ||
| 189 | /// # Example | ||
| 190 | /// | ||
| 191 | /// ``` | ||
| 192 | /// let tsc_sensor1: tsc::IOPinWithRole<G1, Channel> = ...; | ||
| 193 | /// let tsc_sensor2: tsc::IOPinWithRole<G5, Channel> = ...; | ||
| 194 | /// let mut touch_controller: Tsc<'_, TSC, Async> = ...; | ||
| 195 | /// let bank = touch_controller.create_acquisition_bank(AcquisitionBankPins { | ||
| 196 | /// g1_pin: Some(tsc_sensor1), | ||
| 197 | /// g2_pin: Some(tsc_sensor2), | ||
| 198 | /// ..Default::default() | ||
| 199 | /// }); | ||
| 200 | /// | ||
| 201 | /// touch_controller.set_active_channels_bank(&bank); | ||
| 202 | /// touch_controller.start(); | ||
| 203 | /// // ... perform acquisition ... | ||
| 204 | /// ``` | ||
| 205 | /// | ||
| 206 | /// This method should be called before starting a new acquisition with the `start()` method. | ||
| 207 | pub fn set_active_channels_bank(&mut self, bank: &AcquisitionBank) { | ||
| 208 | self.set_active_channels_mask(bank.mask) | ||
| 209 | } | ||
| 210 | |||
| 211 | fn extract_groups(io_mask: u32) -> u32 { | ||
| 212 | let mut groups: u32 = 0; | ||
| 213 | for idx in 0..TSC_NUM_GROUPS { | ||
| 214 | if io_mask & (0x0F << (idx * 4)) != 0 { | ||
| 215 | groups |= 1 << idx | ||
| 216 | } | ||
| 217 | } | ||
| 218 | groups | ||
| 219 | } | ||
| 220 | |||
| 221 | fn new_inner(peri: Peri<'d, T>, pin_groups: PinGroups<'d, T>, config: Config) -> Result<Self, GroupError> { | ||
| 222 | pin_groups.check()?; | ||
| 223 | |||
| 224 | let masks = IOMasks { | ||
| 225 | channel_ios: pin_groups.make_channel_ios_mask(), | ||
| 226 | shield_ios: pin_groups.make_shield_ios_mask(), | ||
| 227 | sampling_ios: pin_groups.make_sample_ios_mask(), | ||
| 228 | }; | ||
| 229 | |||
| 230 | rcc::enable_and_reset::<T>(); | ||
| 231 | |||
| 232 | T::regs().cr().modify(|w| { | ||
| 233 | w.set_tsce(true); | ||
| 234 | w.set_ctph(config.ct_pulse_high_length.into()); | ||
| 235 | w.set_ctpl(config.ct_pulse_low_length.into()); | ||
| 236 | w.set_sse(config.spread_spectrum); | ||
| 237 | // Prevent invalid configuration for pulse generator prescaler | ||
| 238 | if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1 | ||
| 239 | && (config.pulse_generator_prescaler == PGPrescalerDivider::_1 | ||
| 240 | || config.pulse_generator_prescaler == PGPrescalerDivider::_2) | ||
| 241 | { | ||
| 242 | w.set_pgpsc(PGPrescalerDivider::_4.into()); | ||
| 243 | } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2 | ||
| 244 | && config.pulse_generator_prescaler == PGPrescalerDivider::_1 | ||
| 245 | { | ||
| 246 | w.set_pgpsc(PGPrescalerDivider::_2.into()); | ||
| 247 | } else { | ||
| 248 | w.set_pgpsc(config.pulse_generator_prescaler.into()); | ||
| 249 | } | ||
| 250 | w.set_ssd(config.spread_spectrum_deviation.into()); | ||
| 251 | w.set_sspsc(config.spread_spectrum_prescaler); | ||
| 252 | |||
| 253 | w.set_mcv(config.max_count_value.into()); | ||
| 254 | w.set_syncpol(config.synchro_pin_polarity); | ||
| 255 | w.set_am(config.acquisition_mode); | ||
| 256 | }); | ||
| 257 | |||
| 258 | // Set IO configuration | ||
| 259 | // Disable Schmitt trigger hysteresis on all used TSC IOs | ||
| 260 | T::regs() | ||
| 261 | .iohcr() | ||
| 262 | .write(|w| w.0 = !(masks.channel_ios | masks.shield_ios | masks.sampling_ios)); | ||
| 263 | |||
| 264 | // Set channel and shield IOs | ||
| 265 | T::regs().ioccr().write(|w| w.0 = masks.channel_ios | masks.shield_ios); | ||
| 266 | |||
| 267 | // Set sampling IOs | ||
| 268 | T::regs().ioscr().write(|w| w.0 = masks.sampling_ios); | ||
| 269 | |||
| 270 | // Set the groups to be acquired | ||
| 271 | // Lower bits of `iogcsr` are for enabling groups, while the higher bits are for reading | ||
| 272 | // status of acquisiton for a group, see method `Tsc::group_get_status`. | ||
| 273 | T::regs() | ||
| 274 | .iogcsr() | ||
| 275 | .write(|w| w.0 = Self::extract_groups(masks.channel_ios)); | ||
| 276 | |||
| 277 | // Disable interrupts | ||
| 278 | T::regs().ier().modify(|w| { | ||
| 279 | w.set_eoaie(false); | ||
| 280 | w.set_mceie(false); | ||
| 281 | }); | ||
| 282 | |||
| 283 | // Clear flags | ||
| 284 | T::regs().icr().modify(|w| { | ||
| 285 | w.set_eoaic(true); | ||
| 286 | w.set_mceic(true); | ||
| 287 | }); | ||
| 288 | |||
| 289 | unsafe { | ||
| 290 | T::Interrupt::enable(); | ||
| 291 | } | ||
| 292 | |||
| 293 | Ok(Self { | ||
| 294 | _peri: peri, | ||
| 295 | _pin_groups: pin_groups, | ||
| 296 | state: State::Ready, | ||
| 297 | config, | ||
| 298 | masks, | ||
| 299 | _kind: PhantomData, | ||
| 300 | }) | ||
| 301 | } | ||
| 302 | |||
| 303 | /// Start charge transfer acquisition | ||
| 304 | pub fn start(&mut self) { | ||
| 305 | self.state = State::Busy; | ||
| 306 | |||
| 307 | // Disable interrupts | ||
| 308 | T::regs().ier().modify(|w| { | ||
| 309 | w.set_eoaie(false); | ||
| 310 | w.set_mceie(false); | ||
| 311 | }); | ||
| 312 | |||
| 313 | // Clear flags | ||
| 314 | T::regs().icr().modify(|w| { | ||
| 315 | w.set_eoaic(true); | ||
| 316 | w.set_mceic(true); | ||
| 317 | }); | ||
| 318 | |||
| 319 | // Set the touch sensing IOs not acquired to the default mode | ||
| 320 | T::regs().cr().modify(|w| { | ||
| 321 | w.set_iodef(self.config.io_default_mode); | ||
| 322 | }); | ||
| 323 | |||
| 324 | // Start the acquisition | ||
| 325 | T::regs().cr().modify(|w| { | ||
| 326 | w.set_start(true); | ||
| 327 | }); | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Stop charge transfer acquisition | ||
| 331 | pub fn stop(&mut self) { | ||
| 332 | T::regs().cr().modify(|w| { | ||
| 333 | w.set_start(false); | ||
| 334 | }); | ||
| 335 | |||
| 336 | // Set the touch sensing IOs in low power mode | ||
| 337 | T::regs().cr().modify(|w| { | ||
| 338 | w.set_iodef(false); | ||
| 339 | }); | ||
| 340 | |||
| 341 | // Clear flags | ||
| 342 | T::regs().icr().modify(|w| { | ||
| 343 | w.set_eoaic(true); | ||
| 344 | w.set_mceic(true); | ||
| 345 | }); | ||
| 346 | |||
| 347 | self.state = State::Ready; | ||
| 348 | } | ||
| 349 | |||
| 350 | /// Get current state of acquisition | ||
| 351 | pub fn get_state(&mut self) -> State { | ||
| 352 | if self.state == State::Busy && T::regs().isr().read().eoaf() { | ||
| 353 | if T::regs().isr().read().mcef() { | ||
| 354 | self.state = State::Error | ||
| 355 | } else { | ||
| 356 | self.state = State::Ready | ||
| 357 | } | ||
| 358 | } | ||
| 359 | self.state | ||
| 360 | } | ||
| 361 | |||
| 362 | /// Get the individual group status to check acquisition complete | ||
| 363 | pub fn group_get_status(&self, index: Group) -> GroupStatus { | ||
| 364 | // Status bits are set by hardware when the acquisition on the corresponding | ||
| 365 | // enabled analog IO group is complete, cleared when new acquisition is started | ||
| 366 | let status = match index { | ||
| 367 | Group::One => T::regs().iogcsr().read().g1s(), | ||
| 368 | Group::Two => T::regs().iogcsr().read().g2s(), | ||
| 369 | Group::Three => T::regs().iogcsr().read().g3s(), | ||
| 370 | Group::Four => T::regs().iogcsr().read().g4s(), | ||
| 371 | Group::Five => T::regs().iogcsr().read().g5s(), | ||
| 372 | Group::Six => T::regs().iogcsr().read().g6s(), | ||
| 373 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 374 | Group::Seven => T::regs().iogcsr().read().g7s(), | ||
| 375 | #[cfg(tsc_v3)] | ||
| 376 | Group::Eight => T::regs().iogcsr().read().g8s(), | ||
| 377 | }; | ||
| 378 | match status { | ||
| 379 | true => GroupStatus::Complete, | ||
| 380 | false => GroupStatus::Ongoing, | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | /// Get the count for the acquisiton, valid once group status is set | ||
| 385 | pub fn group_get_value(&self, index: Group) -> u16 { | ||
| 386 | T::regs().iogcr(index.into()).read().cnt() | ||
| 387 | } | ||
| 388 | |||
| 389 | /// Discharge the IOs for subsequent acquisition | ||
| 390 | pub fn discharge_io(&mut self, status: bool) { | ||
| 391 | // Set the touch sensing IOs in low power mode | ||
| 392 | T::regs().cr().modify(|w| { | ||
| 393 | w.set_iodef(!status); | ||
| 394 | }); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> { | ||
| 399 | fn drop(&mut self) { | ||
| 400 | rcc::disable::<T>(); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | impl<'d, T: Instance> Tsc<'d, T, Async> { | ||
| 405 | /// Create a Tsc instance that can be awaited for completion | ||
| 406 | pub fn new_async( | ||
| 407 | peri: Peri<'d, T>, | ||
| 408 | pin_groups: PinGroups<'d, T>, | ||
| 409 | config: Config, | ||
| 410 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 411 | ) -> Result<Self, GroupError> { | ||
| 412 | Self::new_inner(peri, pin_groups, config) | ||
| 413 | } | ||
| 414 | |||
| 415 | /// Asyncronously wait for the end of an acquisition | ||
| 416 | pub async fn pend_for_acquisition(&mut self) { | ||
| 417 | poll_fn(|cx| match self.get_state() { | ||
| 418 | State::Busy => { | ||
| 419 | T::waker().register(cx.waker()); | ||
| 420 | T::regs().ier().write(|w| w.set_eoaie(true)); | ||
| 421 | if self.get_state() != State::Busy { | ||
| 422 | T::regs().ier().write(|w| w.set_eoaie(false)); | ||
| 423 | return Poll::Ready(()); | ||
| 424 | } | ||
| 425 | Poll::Pending | ||
| 426 | } | ||
| 427 | _ => { | ||
| 428 | T::regs().ier().write(|w| w.set_eoaie(false)); | ||
| 429 | Poll::Ready(()) | ||
| 430 | } | ||
| 431 | }) | ||
| 432 | .await; | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | impl<'d, T: Instance> Tsc<'d, T, Blocking> { | ||
| 437 | /// Create a Tsc instance that must be polled for completion | ||
| 438 | pub fn new_blocking(peri: Peri<'d, T>, pin_groups: PinGroups<'d, T>, config: Config) -> Result<Self, GroupError> { | ||
| 439 | Self::new_inner(peri, pin_groups, config) | ||
| 440 | } | ||
| 441 | |||
| 442 | /// Wait for end of acquisition | ||
| 443 | pub fn poll_for_acquisition(&mut self) { | ||
| 444 | while self.get_state() == State::Busy {} | ||
| 445 | } | ||
| 446 | } | ||
diff --git a/embassy-stm32/src/tsc/types.rs b/embassy-stm32/src/tsc/types.rs new file mode 100644 index 000000000..0e8fa7f28 --- /dev/null +++ b/embassy-stm32/src/tsc/types.rs | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | /// Peripheral state | ||
| 2 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 3 | #[derive(PartialEq, Clone, Copy)] | ||
| 4 | pub enum State { | ||
| 5 | /// Peripheral is being setup or reconfigured | ||
| 6 | Reset, | ||
| 7 | /// Ready to start acquisition | ||
| 8 | Ready, | ||
| 9 | /// In process of sensor acquisition | ||
| 10 | Busy, | ||
| 11 | /// Error occured during acquisition | ||
| 12 | Error, | ||
| 13 | } | ||
| 14 | |||
| 15 | /// Individual group status checked after acquisition reported as complete | ||
| 16 | /// For groups with multiple channel pins, may take longer because acquisitions | ||
| 17 | /// are done sequentially. Check this status before pulling count for each | ||
| 18 | /// sampled channel | ||
| 19 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 20 | #[derive(PartialEq, Clone, Copy)] | ||
| 21 | pub enum GroupStatus { | ||
| 22 | /// Acquisition for channel still in progress | ||
| 23 | Ongoing, | ||
| 24 | /// Acquisition either not started or complete | ||
| 25 | Complete, | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Group identifier used to interrogate status | ||
| 29 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 30 | #[allow(missing_docs)] | ||
| 31 | #[derive(PartialEq, Clone, Copy)] | ||
| 32 | pub enum Group { | ||
| 33 | One, | ||
| 34 | Two, | ||
| 35 | Three, | ||
| 36 | Four, | ||
| 37 | Five, | ||
| 38 | Six, | ||
| 39 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 40 | Seven, | ||
| 41 | #[cfg(tsc_v3)] | ||
| 42 | Eight, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl Into<usize> for Group { | ||
| 46 | fn into(self) -> usize { | ||
| 47 | match self { | ||
| 48 | Group::One => 0, | ||
| 49 | Group::Two => 1, | ||
| 50 | Group::Three => 2, | ||
| 51 | Group::Four => 3, | ||
| 52 | Group::Five => 4, | ||
| 53 | Group::Six => 5, | ||
| 54 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 55 | Group::Seven => 6, | ||
| 56 | #[cfg(tsc_v3)] | ||
| 57 | Group::Eight => 7, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Error returned when attempting to create a Group from an invalid numeric value. | ||
| 63 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 64 | pub struct InvalidGroupError { | ||
| 65 | invalid_value: usize, | ||
| 66 | } | ||
| 67 | |||
| 68 | impl InvalidGroupError { | ||
| 69 | #[allow(missing_docs)] | ||
| 70 | pub fn new(value: usize) -> Self { | ||
| 71 | Self { invalid_value: value } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl TryFrom<usize> for Group { | ||
| 76 | type Error = InvalidGroupError; | ||
| 77 | |||
| 78 | fn try_from(value: usize) -> Result<Self, Self::Error> { | ||
| 79 | match value { | ||
| 80 | 0 => Ok(Group::One), | ||
| 81 | 1 => Ok(Group::Two), | ||
| 82 | 2 => Ok(Group::Three), | ||
| 83 | 3 => Ok(Group::Four), | ||
| 84 | 4 => Ok(Group::Five), | ||
| 85 | 5 => Ok(Group::Six), | ||
| 86 | #[cfg(any(tsc_v2, tsc_v3))] | ||
| 87 | 6 => Ok(Group::Two), | ||
| 88 | #[cfg(tsc_v3)] | ||
| 89 | 7 => Ok(Group::Two), | ||
| 90 | n => Err(InvalidGroupError::new(n)), | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 40aea75cb..87693f148 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs | |||
| @@ -20,15 +20,15 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 20 | use core::task::Poll; | 20 | use core::task::Poll; |
| 21 | 21 | ||
| 22 | use embassy_hal_internal::drop::OnDrop; | 22 | use embassy_hal_internal::drop::OnDrop; |
| 23 | use embassy_hal_internal::{into_ref, Peripheral}; | 23 | use embassy_hal_internal::PeripheralType; |
| 24 | use embassy_sync::waitqueue::AtomicWaker; | 24 | use embassy_sync::waitqueue::AtomicWaker; |
| 25 | 25 | ||
| 26 | use crate::dma::{ChannelAndRequest, TransferOptions}; | 26 | use crate::dma::{ChannelAndRequest, TransferOptions}; |
| 27 | use crate::interrupt; | ||
| 28 | use crate::interrupt::typelevel::Interrupt; | 27 | use crate::interrupt::typelevel::Interrupt; |
| 29 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; | 28 | use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; |
| 30 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState}; | 29 | pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState}; |
| 31 | use crate::rcc::{self, RccPeripheral}; | 30 | use crate::rcc::{self, RccPeripheral}; |
| 31 | use crate::{interrupt, Peri}; | ||
| 32 | 32 | ||
| 33 | pub(crate) fn init( | 33 | pub(crate) fn init( |
| 34 | _cs: critical_section::CriticalSection, | 34 | _cs: critical_section::CriticalSection, |
| @@ -86,6 +86,34 @@ pub enum CcPull { | |||
| 86 | Source3_0A, | 86 | Source3_0A, |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | /// UCPD configuration | ||
| 90 | #[non_exhaustive] | ||
| 91 | #[derive(Copy, Clone, Debug)] | ||
| 92 | pub struct Config { | ||
| 93 | /// Receive SOP packets | ||
| 94 | pub sop: bool, | ||
| 95 | /// Receive SOP' packets | ||
| 96 | pub sop_prime: bool, | ||
| 97 | /// Receive SOP'' packets | ||
| 98 | pub sop_double_prime: bool, | ||
| 99 | /// Receive SOP'_Debug packets | ||
| 100 | pub sop_prime_debug: bool, | ||
| 101 | /// Receive SOP''_Debug packets | ||
| 102 | pub sop_double_prime_debug: bool, | ||
| 103 | } | ||
| 104 | |||
| 105 | impl Default for Config { | ||
| 106 | fn default() -> Self { | ||
| 107 | Self { | ||
| 108 | sop: true, | ||
| 109 | sop_prime: false, | ||
| 110 | sop_double_prime: false, | ||
| 111 | sop_prime_debug: false, | ||
| 112 | sop_double_prime_debug: false, | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 89 | /// UCPD driver. | 117 | /// UCPD driver. |
| 90 | pub struct Ucpd<'d, T: Instance> { | 118 | pub struct Ucpd<'d, T: Instance> { |
| 91 | cc_phy: CcPhy<'d, T>, | 119 | cc_phy: CcPhy<'d, T>, |
| @@ -94,12 +122,12 @@ pub struct Ucpd<'d, T: Instance> { | |||
| 94 | impl<'d, T: Instance> Ucpd<'d, T> { | 122 | impl<'d, T: Instance> Ucpd<'d, T> { |
| 95 | /// Creates a new UCPD driver instance. | 123 | /// Creates a new UCPD driver instance. |
| 96 | pub fn new( | 124 | pub fn new( |
| 97 | _peri: impl Peripheral<P = T> + 'd, | 125 | _peri: Peri<'d, T>, |
| 98 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 126 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 99 | cc1: impl Peripheral<P = impl Cc1Pin<T>> + 'd, | 127 | cc1: Peri<'d, impl Cc1Pin<T>>, |
| 100 | cc2: impl Peripheral<P = impl Cc2Pin<T>> + 'd, | 128 | cc2: Peri<'d, impl Cc2Pin<T>>, |
| 129 | config: Config, | ||
| 101 | ) -> Self { | 130 | ) -> Self { |
| 102 | into_ref!(cc1, cc2); | ||
| 103 | cc1.set_as_analog(); | 131 | cc1.set_as_analog(); |
| 104 | cc2.set_as_analog(); | 132 | cc2.set_as_analog(); |
| 105 | 133 | ||
| @@ -108,6 +136,13 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 108 | unsafe { T::Interrupt::enable() }; | 136 | unsafe { T::Interrupt::enable() }; |
| 109 | 137 | ||
| 110 | let r = T::REGS; | 138 | let r = T::REGS; |
| 139 | |||
| 140 | #[cfg(stm32h5)] | ||
| 141 | r.cfgr2().write(|w| { | ||
| 142 | // Only takes effect, when UCPDEN=0. | ||
| 143 | w.set_rxafilten(true); | ||
| 144 | }); | ||
| 145 | |||
| 111 | r.cfgr1().write(|w| { | 146 | r.cfgr1().write(|w| { |
| 112 | // "The receiver is designed to work in the clock frequency range from 6 to 18 MHz. | 147 | // "The receiver is designed to work in the clock frequency range from 6 to 18 MHz. |
| 113 | // However, the optimum performance is ensured in the range from 6 to 12 MHz" | 148 | // However, the optimum performance is ensured in the range from 6 to 12 MHz" |
| @@ -129,9 +164,15 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 129 | // 1.75us * 17 = ~30us | 164 | // 1.75us * 17 = ~30us |
| 130 | w.set_ifrgap(17 - 1); | 165 | w.set_ifrgap(17 - 1); |
| 131 | 166 | ||
| 132 | // TODO: Currently only hard reset and SOP messages can be received. | ||
| 133 | // UNDOCUMENTED: This register can only be written while UCPDEN=0 (found by testing). | 167 | // UNDOCUMENTED: This register can only be written while UCPDEN=0 (found by testing). |
| 134 | w.set_rxordseten(0b1001); | 168 | let rxordset = (config.sop as u16) << 0 |
| 169 | | (config.sop_prime as u16) << 1 | ||
| 170 | | (config.sop_double_prime as u16) << 2 | ||
| 171 | // Hard reset | ||
| 172 | | 0x1 << 3 | ||
| 173 | | (config.sop_prime_debug as u16) << 4 | ||
| 174 | | (config.sop_double_prime_debug as u16) << 5; | ||
| 175 | w.set_rxordseten(rxordset); | ||
| 135 | 176 | ||
| 136 | // Enable DMA | 177 | // Enable DMA |
| 137 | w.set_txdmaen(true); | 178 | w.set_txdmaen(true); |
| @@ -140,6 +181,18 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 140 | w.set_ucpden(true); | 181 | w.set_ucpden(true); |
| 141 | }); | 182 | }); |
| 142 | 183 | ||
| 184 | // Software trim according to RM0481, p. 2650/2668 | ||
| 185 | #[cfg(stm32h5)] | ||
| 186 | { | ||
| 187 | let trim_rd_cc1 = unsafe { *(0x4002_242C as *const u32) & 0xF }; | ||
| 188 | let trim_rd_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 8) & 0xF }; | ||
| 189 | |||
| 190 | r.cfgr3().write(|w| { | ||
| 191 | w.set_trim_cc1_rd(trim_rd_cc1 as u8); | ||
| 192 | w.set_trim_cc2_rd(trim_rd_cc2 as u8); | ||
| 193 | }); | ||
| 194 | } | ||
| 195 | |||
| 143 | Self { | 196 | Self { |
| 144 | cc_phy: CcPhy { _lifetime: PhantomData }, | 197 | cc_phy: CcPhy { _lifetime: PhantomData }, |
| 145 | } | 198 | } |
| @@ -154,8 +207,8 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 154 | /// and a Power Delivery (PD) PHY with receiver and transmitter. | 207 | /// and a Power Delivery (PD) PHY with receiver and transmitter. |
| 155 | pub fn split_pd_phy( | 208 | pub fn split_pd_phy( |
| 156 | self, | 209 | self, |
| 157 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 210 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 158 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 211 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 159 | cc_sel: CcSel, | 212 | cc_sel: CcSel, |
| 160 | ) -> (CcPhy<'d, T>, PdPhy<'d, T>) { | 213 | ) -> (CcPhy<'d, T>, PdPhy<'d, T>) { |
| 161 | let r = T::REGS; | 214 | let r = T::REGS; |
| @@ -175,7 +228,6 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 175 | // Both parts must be dropped before the peripheral can be disabled. | 228 | // Both parts must be dropped before the peripheral can be disabled. |
| 176 | T::state().drop_not_ready.store(true, Ordering::Relaxed); | 229 | T::state().drop_not_ready.store(true, Ordering::Relaxed); |
| 177 | 230 | ||
| 178 | into_ref!(rx_dma, tx_dma); | ||
| 179 | let rx_dma_req = rx_dma.request(); | 231 | let rx_dma_req = rx_dma.request(); |
| 180 | let tx_dma_req = tx_dma.request(); | 232 | let tx_dma_req = tx_dma.request(); |
| 181 | ( | 233 | ( |
| @@ -183,11 +235,11 @@ impl<'d, T: Instance> Ucpd<'d, T> { | |||
| 183 | PdPhy { | 235 | PdPhy { |
| 184 | _lifetime: PhantomData, | 236 | _lifetime: PhantomData, |
| 185 | rx_dma: ChannelAndRequest { | 237 | rx_dma: ChannelAndRequest { |
| 186 | channel: rx_dma.map_into(), | 238 | channel: rx_dma.into(), |
| 187 | request: rx_dma_req, | 239 | request: rx_dma_req, |
| 188 | }, | 240 | }, |
| 189 | tx_dma: ChannelAndRequest { | 241 | tx_dma: ChannelAndRequest { |
| 190 | channel: tx_dma.map_into(), | 242 | channel: tx_dma.into(), |
| 191 | request: tx_dma_req, | 243 | request: tx_dma_req, |
| 192 | }, | 244 | }, |
| 193 | }, | 245 | }, |
| @@ -212,7 +264,7 @@ impl<'d, T: Instance> Drop for CcPhy<'d, T> { | |||
| 212 | // Check if the PdPhy part was dropped already. | 264 | // Check if the PdPhy part was dropped already. |
| 213 | let drop_not_ready = &T::state().drop_not_ready; | 265 | let drop_not_ready = &T::state().drop_not_ready; |
| 214 | if drop_not_ready.load(Ordering::Relaxed) { | 266 | if drop_not_ready.load(Ordering::Relaxed) { |
| 215 | drop_not_ready.store(true, Ordering::Relaxed); | 267 | drop_not_ready.store(false, Ordering::Relaxed); |
| 216 | } else { | 268 | } else { |
| 217 | r.cfgr1().write(|w| w.set_ucpden(false)); | 269 | r.cfgr1().write(|w| w.set_ucpden(false)); |
| 218 | rcc::disable::<T>(); | 270 | rcc::disable::<T>(); |
| @@ -243,6 +295,25 @@ impl<'d, T: Instance> CcPhy<'d, T> { | |||
| 243 | }); | 295 | }); |
| 244 | }); | 296 | }); |
| 245 | 297 | ||
| 298 | // Software trim according to RM0481, p. 2650/2668 | ||
| 299 | #[cfg(stm32h5)] | ||
| 300 | T::REGS.cfgr3().modify(|w| match cc_pull { | ||
| 301 | CcPull::Source1_5A => { | ||
| 302 | let trim_1a5_cc1 = unsafe { *(0x08FF_F844 as *const u32) & 0xF }; | ||
| 303 | let trim_1a5_cc2 = unsafe { ((*(0x08FF_F844 as *const u32)) >> 16) & 0xF }; | ||
| 304 | |||
| 305 | w.set_trim_cc1_rp(trim_1a5_cc1 as u8); | ||
| 306 | w.set_trim_cc2_rp(trim_1a5_cc2 as u8); | ||
| 307 | } | ||
| 308 | _ => { | ||
| 309 | let trim_3a0_cc1 = unsafe { (*(0x4002_242C as *const u32) >> 4) & 0xF }; | ||
| 310 | let trim_3a0_cc2 = unsafe { ((*(0x4002_242C as *const u32)) >> 12) & 0xF }; | ||
| 311 | |||
| 312 | w.set_trim_cc1_rp(trim_3a0_cc1 as u8); | ||
| 313 | w.set_trim_cc2_rp(trim_3a0_cc2 as u8); | ||
| 314 | } | ||
| 315 | }); | ||
| 316 | |||
| 246 | // Disable dead-battery pull-down resistors which are enabled by default on boot. | 317 | // Disable dead-battery pull-down resistors which are enabled by default on boot. |
| 247 | critical_section::with(|cs| { | 318 | critical_section::with(|cs| { |
| 248 | init( | 319 | init( |
| @@ -288,6 +359,22 @@ impl<'d, T: Instance> CcPhy<'d, T> { | |||
| 288 | } | 359 | } |
| 289 | } | 360 | } |
| 290 | 361 | ||
| 362 | /// Receive SOP. | ||
| 363 | #[derive(Debug, Clone, Copy)] | ||
| 364 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 365 | pub enum Sop { | ||
| 366 | /// SOP | ||
| 367 | Sop, | ||
| 368 | /// SOP' | ||
| 369 | SopPrime, | ||
| 370 | /// SOP'' | ||
| 371 | SopDoublePrime, | ||
| 372 | /// SOP'_Debug | ||
| 373 | SopPrimeDebug, | ||
| 374 | /// SOP''_Debug | ||
| 375 | SopDoublePrimeDebug, | ||
| 376 | } | ||
| 377 | |||
| 291 | /// Receive Error. | 378 | /// Receive Error. |
| 292 | #[derive(Debug, Clone, Copy)] | 379 | #[derive(Debug, Clone, Copy)] |
| 293 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 380 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -322,13 +409,14 @@ pub struct PdPhy<'d, T: Instance> { | |||
| 322 | 409 | ||
| 323 | impl<'d, T: Instance> Drop for PdPhy<'d, T> { | 410 | impl<'d, T: Instance> Drop for PdPhy<'d, T> { |
| 324 | fn drop(&mut self) { | 411 | fn drop(&mut self) { |
| 325 | T::REGS.cr().modify(|w| w.set_phyrxen(false)); | 412 | let r = T::REGS; |
| 326 | // Check if the Type-C part was dropped already. | 413 | r.cr().modify(|w| w.set_phyrxen(false)); |
| 414 | // Check if the CcPhy part was dropped already. | ||
| 327 | let drop_not_ready = &T::state().drop_not_ready; | 415 | let drop_not_ready = &T::state().drop_not_ready; |
| 328 | if drop_not_ready.load(Ordering::Relaxed) { | 416 | if drop_not_ready.load(Ordering::Relaxed) { |
| 329 | drop_not_ready.store(true, Ordering::Relaxed); | 417 | drop_not_ready.store(false, Ordering::Relaxed); |
| 330 | } else { | 418 | } else { |
| 331 | T::REGS.cfgr1().write(|w| w.set_ucpden(false)); | 419 | r.cfgr1().write(|w| w.set_ucpden(false)); |
| 332 | rcc::disable::<T>(); | 420 | rcc::disable::<T>(); |
| 333 | T::Interrupt::disable(); | 421 | T::Interrupt::disable(); |
| 334 | } | 422 | } |
| @@ -340,9 +428,16 @@ impl<'d, T: Instance> PdPhy<'d, T> { | |||
| 340 | /// | 428 | /// |
| 341 | /// Returns the number of received bytes or an error. | 429 | /// Returns the number of received bytes or an error. |
| 342 | pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, RxError> { | 430 | pub async fn receive(&mut self, buf: &mut [u8]) -> Result<usize, RxError> { |
| 431 | self.receive_with_sop(buf).await.map(|(_sop, size)| size) | ||
| 432 | } | ||
| 433 | |||
| 434 | /// Receives SOP and a PD message into the provided buffer. | ||
| 435 | /// | ||
| 436 | /// Returns the start of packet type and number of received bytes or an error. | ||
| 437 | pub async fn receive_with_sop(&mut self, buf: &mut [u8]) -> Result<(Sop, usize), RxError> { | ||
| 343 | let r = T::REGS; | 438 | let r = T::REGS; |
| 344 | 439 | ||
| 345 | let dma = unsafe { | 440 | let mut dma = unsafe { |
| 346 | self.rx_dma | 441 | self.rx_dma |
| 347 | .read(r.rxdr().as_ptr() as *mut u8, buf, TransferOptions::default()) | 442 | .read(r.rxdr().as_ptr() as *mut u8, buf, TransferOptions::default()) |
| 348 | }; | 443 | }; |
| @@ -357,14 +452,24 @@ impl<'d, T: Instance> PdPhy<'d, T> { | |||
| 357 | }); | 452 | }); |
| 358 | }); | 453 | }); |
| 359 | 454 | ||
| 455 | let mut rxpaysz = 0; | ||
| 456 | |||
| 457 | // Stop DMA reception immediately after receiving a packet, to prevent storing multiple packets in the same buffer. | ||
| 360 | poll_fn(|cx| { | 458 | poll_fn(|cx| { |
| 361 | let sr = r.sr().read(); | 459 | let sr = r.sr().read(); |
| 460 | |||
| 362 | if sr.rxhrstdet() { | 461 | if sr.rxhrstdet() { |
| 462 | dma.request_stop(); | ||
| 463 | |||
| 363 | // Clean and re-enable hard reset receive interrupt. | 464 | // Clean and re-enable hard reset receive interrupt. |
| 364 | r.icr().write(|w| w.set_rxhrstdetcf(true)); | 465 | r.icr().write(|w| w.set_rxhrstdetcf(true)); |
| 365 | r.imr().modify(|w| w.set_rxhrstdetie(true)); | 466 | r.imr().modify(|w| w.set_rxhrstdetie(true)); |
| 366 | Poll::Ready(Err(RxError::HardReset)) | 467 | Poll::Ready(Err(RxError::HardReset)) |
| 367 | } else if sr.rxmsgend() { | 468 | } else if sr.rxmsgend() { |
| 469 | dma.request_stop(); | ||
| 470 | // Should be read immediately on interrupt. | ||
| 471 | rxpaysz = r.rx_payszr().read().rxpaysz().into(); | ||
| 472 | |||
| 368 | let ret = if sr.rxovr() { | 473 | let ret = if sr.rxovr() { |
| 369 | Err(RxError::Overrun) | 474 | Err(RxError::Overrun) |
| 370 | } else if sr.rxerr() { | 475 | } else if sr.rxerr() { |
| @@ -388,7 +493,18 @@ impl<'d, T: Instance> PdPhy<'d, T> { | |||
| 388 | } | 493 | } |
| 389 | } | 494 | } |
| 390 | 495 | ||
| 391 | Ok(r.rx_payszr().read().rxpaysz().into()) | 496 | let sop = match r.rx_ordsetr().read().rxordset() { |
| 497 | Rxordset::SOP => Sop::Sop, | ||
| 498 | Rxordset::SOP_PRIME => Sop::SopPrime, | ||
| 499 | Rxordset::SOP_DOUBLE_PRIME => Sop::SopDoublePrime, | ||
| 500 | Rxordset::SOP_PRIME_DEBUG => Sop::SopPrimeDebug, | ||
| 501 | Rxordset::SOP_DOUBLE_PRIME_DEBUG => Sop::SopDoublePrimeDebug, | ||
| 502 | Rxordset::CABLE_RESET => return Err(RxError::HardReset), | ||
| 503 | // Extension headers are not supported | ||
| 504 | _ => unreachable!(), | ||
| 505 | }; | ||
| 506 | |||
| 507 | Ok((sop, rxpaysz)) | ||
| 392 | } | 508 | } |
| 393 | 509 | ||
| 394 | fn enable_rx_interrupt(enable: bool) { | 510 | fn enable_rx_interrupt(enable: bool) { |
| @@ -571,7 +687,7 @@ trait SealedInstance { | |||
| 571 | 687 | ||
| 572 | /// UCPD instance trait. | 688 | /// UCPD instance trait. |
| 573 | #[allow(private_bounds)] | 689 | #[allow(private_bounds)] |
| 574 | pub trait Instance: SealedInstance + RccPeripheral { | 690 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral { |
| 575 | /// Interrupt for this instance. | 691 | /// Interrupt for this instance. |
| 576 | type Interrupt: crate::interrupt::typelevel::Interrupt; | 692 | type Interrupt: crate::interrupt::typelevel::Interrupt; |
| 577 | } | 693 | } |
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 06cc0e41d..73ab46404 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs | |||
| @@ -6,16 +6,17 @@ use core::task::Poll; | |||
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | 8 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; |
| 9 | use embassy_hal_internal::{Peripheral, PeripheralRef}; | 9 | use embassy_hal_internal::Peri; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 10 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | 11 | ||
| 12 | #[cfg(not(any(usart_v1, usart_v2)))] | 12 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 13 | use super::DePin; | 13 | use super::DePin; |
| 14 | use super::{ | 14 | use super::{ |
| 15 | clear_interrupt_flags, configure, rdr, reconfigure, sr, tdr, Config, ConfigError, CtsPin, Error, Info, Instance, | 15 | clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate, |
| 16 | Regs, RtsPin, RxPin, TxPin, | 16 | sr, tdr, Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, |
| 17 | TxPin, | ||
| 17 | }; | 18 | }; |
| 18 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 19 | use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; |
| 19 | use crate::interrupt::{self, InterruptExt}; | 20 | use crate::interrupt::{self, InterruptExt}; |
| 20 | use crate::time::Hertz; | 21 | use crate::time::Hertz; |
| 21 | 22 | ||
| @@ -108,6 +109,8 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { | |||
| 108 | }); | 109 | }); |
| 109 | } | 110 | } |
| 110 | 111 | ||
| 112 | half_duplex_set_rx_tx_before_write(&r, state.half_duplex_readback.load(Ordering::Relaxed)); | ||
| 113 | |||
| 111 | tdr(r).write_volatile(buf[0].into()); | 114 | tdr(r).write_volatile(buf[0].into()); |
| 112 | tx_reader.pop_done(1); | 115 | tx_reader.pop_done(1); |
| 113 | } else { | 116 | } else { |
| @@ -126,6 +129,7 @@ pub(super) struct State { | |||
| 126 | tx_buf: RingBuffer, | 129 | tx_buf: RingBuffer, |
| 127 | tx_done: AtomicBool, | 130 | tx_done: AtomicBool, |
| 128 | tx_rx_refcount: AtomicU8, | 131 | tx_rx_refcount: AtomicU8, |
| 132 | half_duplex_readback: AtomicBool, | ||
| 129 | } | 133 | } |
| 130 | 134 | ||
| 131 | impl State { | 135 | impl State { |
| @@ -137,6 +141,7 @@ impl State { | |||
| 137 | tx_waker: AtomicWaker::new(), | 141 | tx_waker: AtomicWaker::new(), |
| 138 | tx_done: AtomicBool::new(true), | 142 | tx_done: AtomicBool::new(true), |
| 139 | tx_rx_refcount: AtomicU8::new(0), | 143 | tx_rx_refcount: AtomicU8::new(0), |
| 144 | half_duplex_readback: AtomicBool::new(false), | ||
| 140 | } | 145 | } |
| 141 | } | 146 | } |
| 142 | } | 147 | } |
| @@ -154,9 +159,10 @@ pub struct BufferedUartTx<'d> { | |||
| 154 | info: &'static Info, | 159 | info: &'static Info, |
| 155 | state: &'static State, | 160 | state: &'static State, |
| 156 | kernel_clock: Hertz, | 161 | kernel_clock: Hertz, |
| 157 | tx: Option<PeripheralRef<'d, AnyPin>>, | 162 | tx: Option<Peri<'d, AnyPin>>, |
| 158 | cts: Option<PeripheralRef<'d, AnyPin>>, | 163 | cts: Option<Peri<'d, AnyPin>>, |
| 159 | de: Option<PeripheralRef<'d, AnyPin>>, | 164 | de: Option<Peri<'d, AnyPin>>, |
| 165 | is_borrowed: bool, | ||
| 160 | } | 166 | } |
| 161 | 167 | ||
| 162 | /// Rx-only buffered UART | 168 | /// Rx-only buffered UART |
| @@ -166,8 +172,9 @@ pub struct BufferedUartRx<'d> { | |||
| 166 | info: &'static Info, | 172 | info: &'static Info, |
| 167 | state: &'static State, | 173 | state: &'static State, |
| 168 | kernel_clock: Hertz, | 174 | kernel_clock: Hertz, |
| 169 | rx: Option<PeripheralRef<'d, AnyPin>>, | 175 | rx: Option<Peri<'d, AnyPin>>, |
| 170 | rts: Option<PeripheralRef<'d, AnyPin>>, | 176 | rts: Option<Peri<'d, AnyPin>>, |
| 177 | is_borrowed: bool, | ||
| 171 | } | 178 | } |
| 172 | 179 | ||
| 173 | impl<'d> SetConfig for BufferedUart<'d> { | 180 | impl<'d> SetConfig for BufferedUart<'d> { |
| @@ -200,18 +207,18 @@ impl<'d> SetConfig for BufferedUartTx<'d> { | |||
| 200 | impl<'d> BufferedUart<'d> { | 207 | impl<'d> BufferedUart<'d> { |
| 201 | /// Create a new bidirectional buffered UART driver | 208 | /// Create a new bidirectional buffered UART driver |
| 202 | pub fn new<T: Instance>( | 209 | pub fn new<T: Instance>( |
| 203 | peri: impl Peripheral<P = T> + 'd, | 210 | peri: Peri<'d, T>, |
| 204 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 211 | rx: Peri<'d, impl RxPin<T>>, |
| 205 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 212 | tx: Peri<'d, impl TxPin<T>>, |
| 206 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | ||
| 207 | tx_buffer: &'d mut [u8], | 213 | tx_buffer: &'d mut [u8], |
| 208 | rx_buffer: &'d mut [u8], | 214 | rx_buffer: &'d mut [u8], |
| 215 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 209 | config: Config, | 216 | config: Config, |
| 210 | ) -> Result<Self, ConfigError> { | 217 | ) -> Result<Self, ConfigError> { |
| 211 | Self::new_inner( | 218 | Self::new_inner( |
| 212 | peri, | 219 | peri, |
| 213 | new_pin!(rx, AfType::input(Pull::None)), | 220 | new_pin!(rx, config.rx_af()), |
| 214 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 221 | new_pin!(tx, config.tx_af()), |
| 215 | None, | 222 | None, |
| 216 | None, | 223 | None, |
| 217 | None, | 224 | None, |
| @@ -223,23 +230,71 @@ impl<'d> BufferedUart<'d> { | |||
| 223 | 230 | ||
| 224 | /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins | 231 | /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins |
| 225 | pub fn new_with_rtscts<T: Instance>( | 232 | pub fn new_with_rtscts<T: Instance>( |
| 226 | peri: impl Peripheral<P = T> + 'd, | 233 | peri: Peri<'d, T>, |
| 234 | rx: Peri<'d, impl RxPin<T>>, | ||
| 235 | tx: Peri<'d, impl TxPin<T>>, | ||
| 236 | rts: Peri<'d, impl RtsPin<T>>, | ||
| 237 | cts: Peri<'d, impl CtsPin<T>>, | ||
| 238 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 239 | tx_buffer: &'d mut [u8], | ||
| 240 | rx_buffer: &'d mut [u8], | ||
| 241 | config: Config, | ||
| 242 | ) -> Result<Self, ConfigError> { | ||
| 243 | Self::new_inner( | ||
| 244 | peri, | ||
| 245 | new_pin!(rx, config.rx_af()), | ||
| 246 | new_pin!(tx, config.tx_af()), | ||
| 247 | new_pin!(rts, config.rts_config.af_type()), | ||
| 248 | new_pin!(cts, AfType::input(config.cts_pull)), | ||
| 249 | None, | ||
| 250 | tx_buffer, | ||
| 251 | rx_buffer, | ||
| 252 | config, | ||
| 253 | ) | ||
| 254 | } | ||
| 255 | |||
| 256 | /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin | ||
| 257 | pub fn new_with_rts_as_de<T: Instance>( | ||
| 258 | peri: Peri<'d, T>, | ||
| 259 | rx: Peri<'d, impl RxPin<T>>, | ||
| 260 | tx: Peri<'d, impl TxPin<T>>, | ||
| 261 | rts: Peri<'d, impl RtsPin<T>>, | ||
| 227 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 262 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 228 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | ||
| 229 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | ||
| 230 | rts: impl Peripheral<P = impl RtsPin<T>> + 'd, | ||
| 231 | cts: impl Peripheral<P = impl CtsPin<T>> + 'd, | ||
| 232 | tx_buffer: &'d mut [u8], | 263 | tx_buffer: &'d mut [u8], |
| 233 | rx_buffer: &'d mut [u8], | 264 | rx_buffer: &'d mut [u8], |
| 234 | config: Config, | 265 | config: Config, |
| 235 | ) -> Result<Self, ConfigError> { | 266 | ) -> Result<Self, ConfigError> { |
| 236 | Self::new_inner( | 267 | Self::new_inner( |
| 237 | peri, | 268 | peri, |
| 238 | new_pin!(rx, AfType::input(Pull::None)), | 269 | new_pin!(rx, config.rx_af()), |
| 239 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 270 | new_pin!(tx, config.tx_af()), |
| 240 | new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), | ||
| 241 | new_pin!(cts, AfType::input(Pull::None)), | ||
| 242 | None, | 271 | None, |
| 272 | None, | ||
| 273 | new_pin!(rts, AfType::input(Pull::None)), // RTS mapped used as DE | ||
| 274 | tx_buffer, | ||
| 275 | rx_buffer, | ||
| 276 | config, | ||
| 277 | ) | ||
| 278 | } | ||
| 279 | |||
| 280 | /// Create a new bidirectional buffered UART driver with only the request-to-send pin | ||
| 281 | pub fn new_with_rts<T: Instance>( | ||
| 282 | peri: Peri<'d, T>, | ||
| 283 | rx: Peri<'d, impl RxPin<T>>, | ||
| 284 | tx: Peri<'d, impl TxPin<T>>, | ||
| 285 | rts: Peri<'d, impl RtsPin<T>>, | ||
| 286 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 287 | tx_buffer: &'d mut [u8], | ||
| 288 | rx_buffer: &'d mut [u8], | ||
| 289 | config: Config, | ||
| 290 | ) -> Result<Self, ConfigError> { | ||
| 291 | Self::new_inner( | ||
| 292 | peri, | ||
| 293 | new_pin!(rx, config.rx_af()), | ||
| 294 | new_pin!(tx, config.tx_af()), | ||
| 295 | new_pin!(rts, AfType::input(Pull::None)), | ||
| 296 | None, // no CTS | ||
| 297 | None, // no DE | ||
| 243 | tx_buffer, | 298 | tx_buffer, |
| 244 | rx_buffer, | 299 | rx_buffer, |
| 245 | config, | 300 | config, |
| @@ -249,22 +304,98 @@ impl<'d> BufferedUart<'d> { | |||
| 249 | /// Create a new bidirectional buffered UART driver with a driver-enable pin | 304 | /// Create a new bidirectional buffered UART driver with a driver-enable pin |
| 250 | #[cfg(not(any(usart_v1, usart_v2)))] | 305 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 251 | pub fn new_with_de<T: Instance>( | 306 | pub fn new_with_de<T: Instance>( |
| 252 | peri: impl Peripheral<P = T> + 'd, | 307 | peri: Peri<'d, T>, |
| 308 | rx: Peri<'d, impl RxPin<T>>, | ||
| 309 | tx: Peri<'d, impl TxPin<T>>, | ||
| 310 | de: Peri<'d, impl DePin<T>>, | ||
| 253 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 311 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 254 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | ||
| 255 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | ||
| 256 | de: impl Peripheral<P = impl DePin<T>> + 'd, | ||
| 257 | tx_buffer: &'d mut [u8], | 312 | tx_buffer: &'d mut [u8], |
| 258 | rx_buffer: &'d mut [u8], | 313 | rx_buffer: &'d mut [u8], |
| 259 | config: Config, | 314 | config: Config, |
| 260 | ) -> Result<Self, ConfigError> { | 315 | ) -> Result<Self, ConfigError> { |
| 261 | Self::new_inner( | 316 | Self::new_inner( |
| 262 | peri, | 317 | peri, |
| 263 | new_pin!(rx, AfType::input(Pull::None)), | 318 | new_pin!(rx, config.rx_af()), |
| 264 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 319 | new_pin!(tx, config.tx_af()), |
| 320 | None, | ||
| 321 | None, | ||
| 322 | new_pin!(de, config.de_config.af_type()), | ||
| 323 | tx_buffer, | ||
| 324 | rx_buffer, | ||
| 325 | config, | ||
| 326 | ) | ||
| 327 | } | ||
| 328 | |||
| 329 | /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. | ||
| 330 | /// | ||
| 331 | /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin | ||
| 332 | /// (when it is available for your chip). There is no functional difference between these methods, as both | ||
| 333 | /// allow bidirectional communication. | ||
| 334 | /// | ||
| 335 | /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard | ||
| 336 | /// I/O in idle or in reception. It means that the I/O must be configured so that TX is | ||
| 337 | /// configured as alternate function open-drain with an external pull-up | ||
| 338 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict | ||
| 339 | /// on the line must be managed by software (for instance by using a centralized arbiter). | ||
| 340 | #[doc(alias("HDSEL"))] | ||
| 341 | pub fn new_half_duplex<T: Instance>( | ||
| 342 | peri: Peri<'d, T>, | ||
| 343 | tx: Peri<'d, impl TxPin<T>>, | ||
| 344 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 345 | tx_buffer: &'d mut [u8], | ||
| 346 | rx_buffer: &'d mut [u8], | ||
| 347 | mut config: Config, | ||
| 348 | readback: HalfDuplexReadback, | ||
| 349 | ) -> Result<Self, ConfigError> { | ||
| 350 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 351 | { | ||
| 352 | config.swap_rx_tx = false; | ||
| 353 | } | ||
| 354 | config.duplex = Duplex::Half(readback); | ||
| 355 | |||
| 356 | Self::new_inner( | ||
| 357 | peri, | ||
| 358 | None, | ||
| 359 | new_pin!(tx, config.tx_af()), | ||
| 360 | None, | ||
| 361 | None, | ||
| 362 | None, | ||
| 363 | tx_buffer, | ||
| 364 | rx_buffer, | ||
| 365 | config, | ||
| 366 | ) | ||
| 367 | } | ||
| 368 | |||
| 369 | /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. | ||
| 370 | /// | ||
| 371 | /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. | ||
| 372 | /// There is no functional difference between these methods, as both allow bidirectional communication. | ||
| 373 | /// | ||
| 374 | /// The pin is always released when no data is transmitted. Thus, it acts as a standard | ||
| 375 | /// I/O in idle or in reception. | ||
| 376 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict | ||
| 377 | /// on the line must be managed by software (for instance by using a centralized arbiter). | ||
| 378 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 379 | #[doc(alias("HDSEL"))] | ||
| 380 | pub fn new_half_duplex_on_rx<T: Instance>( | ||
| 381 | peri: Peri<'d, T>, | ||
| 382 | rx: Peri<'d, impl RxPin<T>>, | ||
| 383 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 384 | tx_buffer: &'d mut [u8], | ||
| 385 | rx_buffer: &'d mut [u8], | ||
| 386 | mut config: Config, | ||
| 387 | readback: HalfDuplexReadback, | ||
| 388 | ) -> Result<Self, ConfigError> { | ||
| 389 | config.swap_rx_tx = true; | ||
| 390 | config.duplex = Duplex::Half(readback); | ||
| 391 | |||
| 392 | Self::new_inner( | ||
| 393 | peri, | ||
| 394 | new_pin!(rx, config.rx_af()), | ||
| 395 | None, | ||
| 396 | None, | ||
| 265 | None, | 397 | None, |
| 266 | None, | 398 | None, |
| 267 | new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), | ||
| 268 | tx_buffer, | 399 | tx_buffer, |
| 269 | rx_buffer, | 400 | rx_buffer, |
| 270 | config, | 401 | config, |
| @@ -272,12 +403,12 @@ impl<'d> BufferedUart<'d> { | |||
| 272 | } | 403 | } |
| 273 | 404 | ||
| 274 | fn new_inner<T: Instance>( | 405 | fn new_inner<T: Instance>( |
| 275 | _peri: impl Peripheral<P = T> + 'd, | 406 | _peri: Peri<'d, T>, |
| 276 | rx: Option<PeripheralRef<'d, AnyPin>>, | 407 | rx: Option<Peri<'d, AnyPin>>, |
| 277 | tx: Option<PeripheralRef<'d, AnyPin>>, | 408 | tx: Option<Peri<'d, AnyPin>>, |
| 278 | rts: Option<PeripheralRef<'d, AnyPin>>, | 409 | rts: Option<Peri<'d, AnyPin>>, |
| 279 | cts: Option<PeripheralRef<'d, AnyPin>>, | 410 | cts: Option<Peri<'d, AnyPin>>, |
| 280 | de: Option<PeripheralRef<'d, AnyPin>>, | 411 | de: Option<Peri<'d, AnyPin>>, |
| 281 | tx_buffer: &'d mut [u8], | 412 | tx_buffer: &'d mut [u8], |
| 282 | rx_buffer: &'d mut [u8], | 413 | rx_buffer: &'d mut [u8], |
| 283 | config: Config, | 414 | config: Config, |
| @@ -286,6 +417,11 @@ impl<'d> BufferedUart<'d> { | |||
| 286 | let state = T::buffered_state(); | 417 | let state = T::buffered_state(); |
| 287 | let kernel_clock = T::frequency(); | 418 | let kernel_clock = T::frequency(); |
| 288 | 419 | ||
| 420 | state.half_duplex_readback.store( | ||
| 421 | config.duplex == Duplex::Half(HalfDuplexReadback::Readback), | ||
| 422 | Ordering::Relaxed, | ||
| 423 | ); | ||
| 424 | |||
| 289 | let mut this = Self { | 425 | let mut this = Self { |
| 290 | rx: BufferedUartRx { | 426 | rx: BufferedUartRx { |
| 291 | info, | 427 | info, |
| @@ -293,6 +429,7 @@ impl<'d> BufferedUart<'d> { | |||
| 293 | kernel_clock, | 429 | kernel_clock, |
| 294 | rx, | 430 | rx, |
| 295 | rts, | 431 | rts, |
| 432 | is_borrowed: false, | ||
| 296 | }, | 433 | }, |
| 297 | tx: BufferedUartTx { | 434 | tx: BufferedUartTx { |
| 298 | info, | 435 | info, |
| @@ -301,6 +438,7 @@ impl<'d> BufferedUart<'d> { | |||
| 301 | tx, | 438 | tx, |
| 302 | cts, | 439 | cts, |
| 303 | de, | 440 | de, |
| 441 | is_borrowed: false, | ||
| 304 | }, | 442 | }, |
| 305 | }; | 443 | }; |
| 306 | this.enable_and_configure(tx_buffer, rx_buffer, &config)?; | 444 | this.enable_and_configure(tx_buffer, rx_buffer, &config)?; |
| @@ -319,8 +457,10 @@ impl<'d> BufferedUart<'d> { | |||
| 319 | 457 | ||
| 320 | info.rcc.enable_and_reset(); | 458 | info.rcc.enable_and_reset(); |
| 321 | 459 | ||
| 460 | assert!(!tx_buffer.is_empty()); | ||
| 322 | let len = tx_buffer.len(); | 461 | let len = tx_buffer.len(); |
| 323 | unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; | 462 | unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; |
| 463 | assert!(!rx_buffer.is_empty()); | ||
| 324 | let len = rx_buffer.len(); | 464 | let len = rx_buffer.len(); |
| 325 | unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; | 465 | unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; |
| 326 | 466 | ||
| @@ -329,12 +469,20 @@ impl<'d> BufferedUart<'d> { | |||
| 329 | w.set_ctse(self.tx.cts.is_some()); | 469 | w.set_ctse(self.tx.cts.is_some()); |
| 330 | #[cfg(not(any(usart_v1, usart_v2)))] | 470 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 331 | w.set_dem(self.tx.de.is_some()); | 471 | w.set_dem(self.tx.de.is_some()); |
| 472 | w.set_hdsel(config.duplex.is_half()); | ||
| 332 | }); | 473 | }); |
| 333 | configure(info, self.rx.kernel_clock, &config, true, true)?; | 474 | configure(info, self.rx.kernel_clock, &config, true, true)?; |
| 334 | 475 | ||
| 335 | info.regs.cr1().modify(|w| { | 476 | info.regs.cr1().modify(|w| { |
| 336 | w.set_rxneie(true); | 477 | w.set_rxneie(true); |
| 337 | w.set_idleie(true); | 478 | w.set_idleie(true); |
| 479 | |||
| 480 | if config.duplex.is_half() { | ||
| 481 | // The te and re bits will be set by write, read and flush methods. | ||
| 482 | // Receiver should be enabled by default for Half-Duplex. | ||
| 483 | w.set_te(false); | ||
| 484 | w.set_re(true); | ||
| 485 | } | ||
| 338 | }); | 486 | }); |
| 339 | 487 | ||
| 340 | info.interrupt.unpend(); | 488 | info.interrupt.unpend(); |
| @@ -348,6 +496,31 @@ impl<'d> BufferedUart<'d> { | |||
| 348 | (self.tx, self.rx) | 496 | (self.tx, self.rx) |
| 349 | } | 497 | } |
| 350 | 498 | ||
| 499 | /// Split the Uart into a transmitter and receiver, | ||
| 500 | /// which is particularly useful when having two tasks correlating to | ||
| 501 | /// transmitting and receiving. | ||
| 502 | pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) { | ||
| 503 | ( | ||
| 504 | BufferedUartTx { | ||
| 505 | info: self.tx.info, | ||
| 506 | state: self.tx.state, | ||
| 507 | kernel_clock: self.tx.kernel_clock, | ||
| 508 | tx: self.tx.tx.as_mut().map(Peri::reborrow), | ||
| 509 | cts: self.tx.cts.as_mut().map(Peri::reborrow), | ||
| 510 | de: self.tx.de.as_mut().map(Peri::reborrow), | ||
| 511 | is_borrowed: true, | ||
| 512 | }, | ||
| 513 | BufferedUartRx { | ||
| 514 | info: self.rx.info, | ||
| 515 | state: self.rx.state, | ||
| 516 | kernel_clock: self.rx.kernel_clock, | ||
| 517 | rx: self.rx.rx.as_mut().map(Peri::reborrow), | ||
| 518 | rts: self.rx.rts.as_mut().map(Peri::reborrow), | ||
| 519 | is_borrowed: true, | ||
| 520 | }, | ||
| 521 | ) | ||
| 522 | } | ||
| 523 | |||
| 351 | /// Reconfigure the driver | 524 | /// Reconfigure the driver |
| 352 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 525 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 353 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; | 526 | reconfigure(self.rx.info, self.rx.kernel_clock, config)?; |
| @@ -359,6 +532,18 @@ impl<'d> BufferedUart<'d> { | |||
| 359 | 532 | ||
| 360 | Ok(()) | 533 | Ok(()) |
| 361 | } | 534 | } |
| 535 | |||
| 536 | /// Send break character | ||
| 537 | pub fn send_break(&self) { | ||
| 538 | self.tx.send_break() | ||
| 539 | } | ||
| 540 | |||
| 541 | /// Set baudrate | ||
| 542 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 543 | self.tx.set_baudrate(baudrate)?; | ||
| 544 | self.rx.set_baudrate(baudrate)?; | ||
| 545 | Ok(()) | ||
| 546 | } | ||
| 362 | } | 547 | } |
| 363 | 548 | ||
| 364 | impl<'d> BufferedUartRx<'d> { | 549 | impl<'d> BufferedUartRx<'d> { |
| @@ -453,6 +638,11 @@ impl<'d> BufferedUartRx<'d> { | |||
| 453 | 638 | ||
| 454 | Ok(()) | 639 | Ok(()) |
| 455 | } | 640 | } |
| 641 | |||
| 642 | /// Set baudrate | ||
| 643 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 644 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 645 | } | ||
| 456 | } | 646 | } |
| 457 | 647 | ||
| 458 | impl<'d> BufferedUartTx<'d> { | 648 | impl<'d> BufferedUartTx<'d> { |
| @@ -538,44 +728,58 @@ impl<'d> BufferedUartTx<'d> { | |||
| 538 | 728 | ||
| 539 | Ok(()) | 729 | Ok(()) |
| 540 | } | 730 | } |
| 731 | |||
| 732 | /// Send break character | ||
| 733 | pub fn send_break(&self) { | ||
| 734 | send_break(&self.info.regs); | ||
| 735 | } | ||
| 736 | |||
| 737 | /// Set baudrate | ||
| 738 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 739 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 740 | } | ||
| 541 | } | 741 | } |
| 542 | 742 | ||
| 543 | impl<'d> Drop for BufferedUartRx<'d> { | 743 | impl<'d> Drop for BufferedUartRx<'d> { |
| 544 | fn drop(&mut self) { | 744 | fn drop(&mut self) { |
| 545 | let state = self.state; | 745 | if !self.is_borrowed { |
| 546 | unsafe { | 746 | let state = self.state; |
| 547 | state.rx_buf.deinit(); | 747 | unsafe { |
| 748 | state.rx_buf.deinit(); | ||
| 548 | 749 | ||
| 549 | // TX is inactive if the the buffer is not available. | 750 | // TX is inactive if the buffer is not available. |
| 550 | // We can now unregister the interrupt handler | 751 | // We can now unregister the interrupt handler |
| 551 | if state.tx_buf.len() == 0 { | 752 | if state.tx_buf.len() == 0 { |
| 552 | self.info.interrupt.disable(); | 753 | self.info.interrupt.disable(); |
| 754 | } | ||
| 553 | } | 755 | } |
| 554 | } | ||
| 555 | 756 | ||
| 556 | self.rx.as_ref().map(|x| x.set_as_disconnected()); | 757 | self.rx.as_ref().map(|x| x.set_as_disconnected()); |
| 557 | self.rts.as_ref().map(|x| x.set_as_disconnected()); | 758 | self.rts.as_ref().map(|x| x.set_as_disconnected()); |
| 558 | drop_tx_rx(self.info, state); | 759 | drop_tx_rx(self.info, state); |
| 760 | } | ||
| 559 | } | 761 | } |
| 560 | } | 762 | } |
| 561 | 763 | ||
| 562 | impl<'d> Drop for BufferedUartTx<'d> { | 764 | impl<'d> Drop for BufferedUartTx<'d> { |
| 563 | fn drop(&mut self) { | 765 | fn drop(&mut self) { |
| 564 | let state = self.state; | 766 | if !self.is_borrowed { |
| 565 | unsafe { | 767 | let state = self.state; |
| 566 | state.tx_buf.deinit(); | 768 | unsafe { |
| 769 | state.tx_buf.deinit(); | ||
| 567 | 770 | ||
| 568 | // RX is inactive if the the buffer is not available. | 771 | // RX is inactive if the buffer is not available. |
| 569 | // We can now unregister the interrupt handler | 772 | // We can now unregister the interrupt handler |
| 570 | if state.rx_buf.len() == 0 { | 773 | if state.rx_buf.len() == 0 { |
| 571 | self.info.interrupt.disable(); | 774 | self.info.interrupt.disable(); |
| 775 | } | ||
| 572 | } | 776 | } |
| 573 | } | ||
| 574 | 777 | ||
| 575 | self.tx.as_ref().map(|x| x.set_as_disconnected()); | 778 | self.tx.as_ref().map(|x| x.set_as_disconnected()); |
| 576 | self.cts.as_ref().map(|x| x.set_as_disconnected()); | 779 | self.cts.as_ref().map(|x| x.set_as_disconnected()); |
| 577 | self.de.as_ref().map(|x| x.set_as_disconnected()); | 780 | self.de.as_ref().map(|x| x.set_as_disconnected()); |
| 578 | drop_tx_rx(self.info, state); | 781 | drop_tx_rx(self.info, state); |
| 782 | } | ||
| 579 | } | 783 | } |
| 580 | } | 784 | } |
| 581 | 785 | ||
| @@ -704,26 +908,17 @@ impl<'d> embedded_hal_02::serial::Read<u8> for BufferedUartRx<'d> { | |||
| 704 | type Error = Error; | 908 | type Error = Error; |
| 705 | 909 | ||
| 706 | fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> { | 910 | fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> { |
| 707 | let r = self.info.regs; | 911 | let state = self.state; |
| 708 | unsafe { | 912 | let mut rx_reader = unsafe { state.rx_buf.reader() }; |
| 709 | let sr = sr(r).read(); | 913 | |
| 710 | if sr.pe() { | 914 | let do_pend = state.rx_buf.is_full(); |
| 711 | rdr(r).read_volatile(); | 915 | if let Some(data) = rx_reader.pop_one() { |
| 712 | Err(nb::Error::Other(Error::Parity)) | 916 | if do_pend { |
| 713 | } else if sr.fe() { | 917 | self.info.interrupt.pend(); |
| 714 | rdr(r).read_volatile(); | ||
| 715 | Err(nb::Error::Other(Error::Framing)) | ||
| 716 | } else if sr.ne() { | ||
| 717 | rdr(r).read_volatile(); | ||
| 718 | Err(nb::Error::Other(Error::Noise)) | ||
| 719 | } else if sr.ore() { | ||
| 720 | rdr(r).read_volatile(); | ||
| 721 | Err(nb::Error::Other(Error::Overrun)) | ||
| 722 | } else if sr.rxne() { | ||
| 723 | Ok(rdr(r).read_volatile()) | ||
| 724 | } else { | ||
| 725 | Err(nb::Error::WouldBlock) | ||
| 726 | } | 918 | } |
| 919 | Ok(data) | ||
| 920 | } else { | ||
| 921 | Err(nb::Error::WouldBlock) | ||
| 727 | } | 922 | } |
| 728 | } | 923 | } |
| 729 | } | 924 | } |
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 7ed3793a1..b3f8bc00c 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -9,7 +9,7 @@ use core::task::Poll; | |||
| 9 | 9 | ||
| 10 | use embassy_embedded_hal::SetConfig; | 10 | use embassy_embedded_hal::SetConfig; |
| 11 | use embassy_hal_internal::drop::OnDrop; | 11 | use embassy_hal_internal::drop::OnDrop; |
| 12 | use embassy_hal_internal::PeripheralRef; | 12 | use embassy_hal_internal::PeripheralType; |
| 13 | use embassy_sync::waitqueue::AtomicWaker; | 13 | use embassy_sync::waitqueue::AtomicWaker; |
| 14 | use futures_util::future::{select, Either}; | 14 | use futures_util::future::{select, Either}; |
| 15 | 15 | ||
| @@ -18,11 +18,6 @@ use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | |||
| 18 | use crate::interrupt::typelevel::Interrupt as _; | 18 | use crate::interrupt::typelevel::Interrupt as _; |
| 19 | use crate::interrupt::{self, Interrupt, InterruptExt}; | 19 | use crate::interrupt::{self, Interrupt, InterruptExt}; |
| 20 | use crate::mode::{Async, Blocking, Mode}; | 20 | use crate::mode::{Async, Blocking, Mode}; |
| 21 | #[allow(unused_imports)] | ||
| 22 | #[cfg(not(any(usart_v1, usart_v2)))] | ||
| 23 | use crate::pac::usart::regs::Isr as Sr; | ||
| 24 | #[cfg(any(usart_v1, usart_v2))] | ||
| 25 | use crate::pac::usart::regs::Sr; | ||
| 26 | #[cfg(not(any(usart_v1, usart_v2)))] | 21 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 27 | use crate::pac::usart::Lpuart as Regs; | 22 | use crate::pac::usart::Lpuart as Regs; |
| 28 | #[cfg(any(usart_v1, usart_v2))] | 23 | #[cfg(any(usart_v1, usart_v2))] |
| @@ -30,7 +25,7 @@ use crate::pac::usart::Usart as Regs; | |||
| 30 | use crate::pac::usart::{regs, vals}; | 25 | use crate::pac::usart::{regs, vals}; |
| 31 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 26 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 32 | use crate::time::Hertz; | 27 | use crate::time::Hertz; |
| 33 | use crate::Peripheral; | 28 | use crate::Peri; |
| 34 | 29 | ||
| 35 | /// Interrupt handler. | 30 | /// Interrupt handler. |
| 36 | pub struct InterruptHandler<T: Instance> { | 31 | pub struct InterruptHandler<T: Instance> { |
| @@ -69,6 +64,12 @@ unsafe fn on_interrupt(r: Regs, s: &'static State) { | |||
| 69 | // disable idle line detection | 64 | // disable idle line detection |
| 70 | w.set_idleie(false); | 65 | w.set_idleie(false); |
| 71 | }); | 66 | }); |
| 67 | } else if cr1.tcie() && sr.tc() { | ||
| 68 | // Transmission complete detected | ||
| 69 | r.cr1().modify(|w| { | ||
| 70 | // disable Transmission complete interrupt | ||
| 71 | w.set_tcie(false); | ||
| 72 | }); | ||
| 72 | } else if cr1.rxneie() { | 73 | } else if cr1.rxneie() { |
| 73 | // We cannot check the RXNE flag as it is auto-cleared by the DMA controller | 74 | // We cannot check the RXNE flag as it is auto-cleared by the DMA controller |
| 74 | 75 | ||
| @@ -79,12 +80,15 @@ unsafe fn on_interrupt(r: Regs, s: &'static State) { | |||
| 79 | 80 | ||
| 80 | compiler_fence(Ordering::SeqCst); | 81 | compiler_fence(Ordering::SeqCst); |
| 81 | s.rx_waker.wake(); | 82 | s.rx_waker.wake(); |
| 83 | s.tx_waker.wake(); | ||
| 82 | } | 84 | } |
| 83 | 85 | ||
| 84 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 86 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 85 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 87 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 86 | /// Number of data bits | 88 | /// Number of data bits |
| 87 | pub enum DataBits { | 89 | pub enum DataBits { |
| 90 | /// 7 Data Bits | ||
| 91 | DataBits7, | ||
| 88 | /// 8 Data Bits | 92 | /// 8 Data Bits |
| 89 | DataBits8, | 93 | DataBits8, |
| 90 | /// 9 Data Bits | 94 | /// 9 Data Bits |
| @@ -117,6 +121,57 @@ pub enum StopBits { | |||
| 117 | STOP1P5, | 121 | STOP1P5, |
| 118 | } | 122 | } |
| 119 | 123 | ||
| 124 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 125 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 126 | /// Enables or disables receiver so written data are read back in half-duplex mode | ||
| 127 | pub enum HalfDuplexReadback { | ||
| 128 | /// Disables receiver so written data are not read back | ||
| 129 | NoReadback, | ||
| 130 | /// Enables receiver so written data are read back | ||
| 131 | Readback, | ||
| 132 | } | ||
| 133 | |||
| 134 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 135 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 136 | /// Half duplex IO mode | ||
| 137 | pub enum OutputConfig { | ||
| 138 | /// Push pull allows for faster baudrates, no internal pullup | ||
| 139 | PushPull, | ||
| 140 | /// Open drain output (external pull up needed) | ||
| 141 | OpenDrain, | ||
| 142 | #[cfg(not(gpio_v1))] | ||
| 143 | /// Open drain output with internal pull up resistor | ||
| 144 | OpenDrainPullUp, | ||
| 145 | } | ||
| 146 | |||
| 147 | impl OutputConfig { | ||
| 148 | const fn af_type(self) -> AfType { | ||
| 149 | match self { | ||
| 150 | OutputConfig::PushPull => AfType::output(OutputType::PushPull, Speed::Medium), | ||
| 151 | OutputConfig::OpenDrain => AfType::output(OutputType::OpenDrain, Speed::Medium), | ||
| 152 | #[cfg(not(gpio_v1))] | ||
| 153 | OutputConfig::OpenDrainPullUp => AfType::output_pull(OutputType::OpenDrain, Speed::Medium, Pull::Up), | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 159 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 160 | /// Duplex mode | ||
| 161 | pub enum Duplex { | ||
| 162 | /// Full duplex | ||
| 163 | Full, | ||
| 164 | /// Half duplex with possibility to read back written data | ||
| 165 | Half(HalfDuplexReadback), | ||
| 166 | } | ||
| 167 | |||
| 168 | impl Duplex { | ||
| 169 | /// Returns true if half-duplex | ||
| 170 | fn is_half(&self) -> bool { | ||
| 171 | matches!(self, Duplex::Half(_)) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 120 | #[non_exhaustive] | 175 | #[non_exhaustive] |
| 121 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | 176 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
| 122 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 177 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -128,6 +183,8 @@ pub enum ConfigError { | |||
| 128 | BaudrateTooHigh, | 183 | BaudrateTooHigh, |
| 129 | /// Rx or Tx not enabled | 184 | /// Rx or Tx not enabled |
| 130 | RxOrTxNotEnabled, | 185 | RxOrTxNotEnabled, |
| 186 | /// Data bits and parity combination not supported | ||
| 187 | DataParityNotSupported, | ||
| 131 | } | 188 | } |
| 132 | 189 | ||
| 133 | #[non_exhaustive] | 190 | #[non_exhaustive] |
| @@ -167,25 +224,40 @@ pub struct Config { | |||
| 167 | #[cfg(any(usart_v3, usart_v4))] | 224 | #[cfg(any(usart_v3, usart_v4))] |
| 168 | pub invert_rx: bool, | 225 | pub invert_rx: bool, |
| 169 | 226 | ||
| 227 | /// Set the pull configuration for the RX pin. | ||
| 228 | pub rx_pull: Pull, | ||
| 229 | |||
| 230 | /// Set the pull configuration for the CTS pin. | ||
| 231 | pub cts_pull: Pull, | ||
| 232 | |||
| 233 | /// Set the pin configuration for the TX pin. | ||
| 234 | pub tx_config: OutputConfig, | ||
| 235 | |||
| 236 | /// Set the pin configuration for the RTS pin. | ||
| 237 | pub rts_config: OutputConfig, | ||
| 238 | |||
| 239 | /// Set the pin configuration for the DE pin. | ||
| 240 | pub de_config: OutputConfig, | ||
| 241 | |||
| 170 | // private: set by new_half_duplex, not by the user. | 242 | // private: set by new_half_duplex, not by the user. |
| 171 | half_duplex: bool, | 243 | duplex: Duplex, |
| 172 | } | 244 | } |
| 173 | 245 | ||
| 174 | impl Config { | 246 | impl Config { |
| 175 | fn tx_af(&self) -> AfType { | 247 | fn tx_af(&self) -> AfType { |
| 176 | #[cfg(any(usart_v3, usart_v4))] | 248 | #[cfg(any(usart_v3, usart_v4))] |
| 177 | if self.swap_rx_tx { | 249 | if self.swap_rx_tx { |
| 178 | return AfType::input(Pull::None); | 250 | return AfType::input(self.rx_pull); |
| 179 | }; | 251 | }; |
| 180 | AfType::output(OutputType::PushPull, Speed::Medium) | 252 | self.tx_config.af_type() |
| 181 | } | 253 | } |
| 182 | 254 | ||
| 183 | fn rx_af(&self) -> AfType { | 255 | fn rx_af(&self) -> AfType { |
| 184 | #[cfg(any(usart_v3, usart_v4))] | 256 | #[cfg(any(usart_v3, usart_v4))] |
| 185 | if self.swap_rx_tx { | 257 | if self.swap_rx_tx { |
| 186 | return AfType::output(OutputType::PushPull, Speed::Medium); | 258 | return self.tx_config.af_type(); |
| 187 | }; | 259 | }; |
| 188 | AfType::input(Pull::None) | 260 | AfType::input(self.rx_pull) |
| 189 | } | 261 | } |
| 190 | } | 262 | } |
| 191 | 263 | ||
| @@ -206,7 +278,12 @@ impl Default for Config { | |||
| 206 | invert_tx: false, | 278 | invert_tx: false, |
| 207 | #[cfg(any(usart_v3, usart_v4))] | 279 | #[cfg(any(usart_v3, usart_v4))] |
| 208 | invert_rx: false, | 280 | invert_rx: false, |
| 209 | half_duplex: false, | 281 | rx_pull: Pull::None, |
| 282 | cts_pull: Pull::None, | ||
| 283 | tx_config: OutputConfig::PushPull, | ||
| 284 | rts_config: OutputConfig::PushPull, | ||
| 285 | de_config: OutputConfig::PushPull, | ||
| 286 | duplex: Duplex::Full, | ||
| 210 | } | 287 | } |
| 211 | } | 288 | } |
| 212 | } | 289 | } |
| @@ -228,6 +305,22 @@ pub enum Error { | |||
| 228 | BufferTooLong, | 305 | BufferTooLong, |
| 229 | } | 306 | } |
| 230 | 307 | ||
| 308 | impl core::fmt::Display for Error { | ||
| 309 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 310 | let message = match self { | ||
| 311 | Self::Framing => "Framing Error", | ||
| 312 | Self::Noise => "Noise Error", | ||
| 313 | Self::Overrun => "RX Buffer Overrun", | ||
| 314 | Self::Parity => "Parity Check Error", | ||
| 315 | Self::BufferTooLong => "Buffer too large for DMA", | ||
| 316 | }; | ||
| 317 | |||
| 318 | write!(f, "{}", message) | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | impl core::error::Error for Error {} | ||
| 323 | |||
| 231 | enum ReadCompletionEvent { | 324 | enum ReadCompletionEvent { |
| 232 | // DMA Read transfer completed first | 325 | // DMA Read transfer completed first |
| 233 | DmaCompleted, | 326 | DmaCompleted, |
| @@ -266,10 +359,11 @@ pub struct UartTx<'d, M: Mode> { | |||
| 266 | info: &'static Info, | 359 | info: &'static Info, |
| 267 | state: &'static State, | 360 | state: &'static State, |
| 268 | kernel_clock: Hertz, | 361 | kernel_clock: Hertz, |
| 269 | tx: Option<PeripheralRef<'d, AnyPin>>, | 362 | tx: Option<Peri<'d, AnyPin>>, |
| 270 | cts: Option<PeripheralRef<'d, AnyPin>>, | 363 | cts: Option<Peri<'d, AnyPin>>, |
| 271 | de: Option<PeripheralRef<'d, AnyPin>>, | 364 | de: Option<Peri<'d, AnyPin>>, |
| 272 | tx_dma: Option<ChannelAndRequest<'d>>, | 365 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 366 | duplex: Duplex, | ||
| 273 | _phantom: PhantomData<M>, | 367 | _phantom: PhantomData<M>, |
| 274 | } | 368 | } |
| 275 | 369 | ||
| @@ -315,12 +409,12 @@ pub struct UartRx<'d, M: Mode> { | |||
| 315 | info: &'static Info, | 409 | info: &'static Info, |
| 316 | state: &'static State, | 410 | state: &'static State, |
| 317 | kernel_clock: Hertz, | 411 | kernel_clock: Hertz, |
| 318 | rx: Option<PeripheralRef<'d, AnyPin>>, | 412 | rx: Option<Peri<'d, AnyPin>>, |
| 319 | rts: Option<PeripheralRef<'d, AnyPin>>, | 413 | rts: Option<Peri<'d, AnyPin>>, |
| 320 | rx_dma: Option<ChannelAndRequest<'d>>, | 414 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 321 | detect_previous_overrun: bool, | 415 | detect_previous_overrun: bool, |
| 322 | #[cfg(any(usart_v1, usart_v2))] | 416 | #[cfg(any(usart_v1, usart_v2))] |
| 323 | buffered_sr: stm32_metapac::usart::regs::Sr, | 417 | buffered_sr: regs::Sr, |
| 324 | _phantom: PhantomData<M>, | 418 | _phantom: PhantomData<M>, |
| 325 | } | 419 | } |
| 326 | 420 | ||
| @@ -336,32 +430,26 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> { | |||
| 336 | impl<'d> UartTx<'d, Async> { | 430 | impl<'d> UartTx<'d, Async> { |
| 337 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. | 431 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. |
| 338 | pub fn new<T: Instance>( | 432 | pub fn new<T: Instance>( |
| 339 | peri: impl Peripheral<P = T> + 'd, | 433 | peri: Peri<'d, T>, |
| 340 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 434 | tx: Peri<'d, impl TxPin<T>>, |
| 341 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 435 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 342 | config: Config, | 436 | config: Config, |
| 343 | ) -> Result<Self, ConfigError> { | 437 | ) -> Result<Self, ConfigError> { |
| 344 | Self::new_inner( | 438 | Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, new_dma!(tx_dma), config) |
| 345 | peri, | ||
| 346 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | ||
| 347 | None, | ||
| 348 | new_dma!(tx_dma), | ||
| 349 | config, | ||
| 350 | ) | ||
| 351 | } | 439 | } |
| 352 | 440 | ||
| 353 | /// Create a new tx-only UART with a clear-to-send pin | 441 | /// Create a new tx-only UART with a clear-to-send pin |
| 354 | pub fn new_with_cts<T: Instance>( | 442 | pub fn new_with_cts<T: Instance>( |
| 355 | peri: impl Peripheral<P = T> + 'd, | 443 | peri: Peri<'d, T>, |
| 356 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 444 | tx: Peri<'d, impl TxPin<T>>, |
| 357 | cts: impl Peripheral<P = impl CtsPin<T>> + 'd, | 445 | cts: Peri<'d, impl CtsPin<T>>, |
| 358 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 446 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 359 | config: Config, | 447 | config: Config, |
| 360 | ) -> Result<Self, ConfigError> { | 448 | ) -> Result<Self, ConfigError> { |
| 361 | Self::new_inner( | 449 | Self::new_inner( |
| 362 | peri, | 450 | peri, |
| 363 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 451 | new_pin!(tx, config.tx_af()), |
| 364 | new_pin!(cts, AfType::input(Pull::None)), | 452 | new_pin!(cts, AfType::input(config.cts_pull)), |
| 365 | new_dma!(tx_dma), | 453 | new_dma!(tx_dma), |
| 366 | config, | 454 | config, |
| 367 | ) | 455 | ) |
| @@ -371,13 +459,7 @@ impl<'d> UartTx<'d, Async> { | |||
| 371 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 459 | pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 372 | let r = self.info.regs; | 460 | let r = self.info.regs; |
| 373 | 461 | ||
| 374 | // Enable Transmitter and disable Receiver for Half-Duplex mode | 462 | half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); |
| 375 | let mut cr1 = r.cr1().read(); | ||
| 376 | if r.cr3().read().hdsel() && !cr1.te() { | ||
| 377 | cr1.set_te(true); | ||
| 378 | cr1.set_re(false); | ||
| 379 | r.cr1().write_value(cr1); | ||
| 380 | } | ||
| 381 | 463 | ||
| 382 | let ch = self.tx_dma.as_mut().unwrap(); | 464 | let ch = self.tx_dma.as_mut().unwrap(); |
| 383 | r.cr3().modify(|reg| { | 465 | r.cr3().modify(|reg| { |
| @@ -392,7 +474,7 @@ impl<'d> UartTx<'d, Async> { | |||
| 392 | 474 | ||
| 393 | /// Wait until transmission complete | 475 | /// Wait until transmission complete |
| 394 | pub async fn flush(&mut self) -> Result<(), Error> { | 476 | pub async fn flush(&mut self) -> Result<(), Error> { |
| 395 | self.blocking_flush() | 477 | flush(&self.info, &self.state).await |
| 396 | } | 478 | } |
| 397 | } | 479 | } |
| 398 | 480 | ||
| @@ -401,30 +483,24 @@ impl<'d> UartTx<'d, Blocking> { | |||
| 401 | /// | 483 | /// |
| 402 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. | 484 | /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. |
| 403 | pub fn new_blocking<T: Instance>( | 485 | pub fn new_blocking<T: Instance>( |
| 404 | peri: impl Peripheral<P = T> + 'd, | 486 | peri: Peri<'d, T>, |
| 405 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 487 | tx: Peri<'d, impl TxPin<T>>, |
| 406 | config: Config, | 488 | config: Config, |
| 407 | ) -> Result<Self, ConfigError> { | 489 | ) -> Result<Self, ConfigError> { |
| 408 | Self::new_inner( | 490 | Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config) |
| 409 | peri, | ||
| 410 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | ||
| 411 | None, | ||
| 412 | None, | ||
| 413 | config, | ||
| 414 | ) | ||
| 415 | } | 491 | } |
| 416 | 492 | ||
| 417 | /// Create a new blocking tx-only UART with a clear-to-send pin | 493 | /// Create a new blocking tx-only UART with a clear-to-send pin |
| 418 | pub fn new_blocking_with_cts<T: Instance>( | 494 | pub fn new_blocking_with_cts<T: Instance>( |
| 419 | peri: impl Peripheral<P = T> + 'd, | 495 | peri: Peri<'d, T>, |
| 420 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 496 | tx: Peri<'d, impl TxPin<T>>, |
| 421 | cts: impl Peripheral<P = impl CtsPin<T>> + 'd, | 497 | cts: Peri<'d, impl CtsPin<T>>, |
| 422 | config: Config, | 498 | config: Config, |
| 423 | ) -> Result<Self, ConfigError> { | 499 | ) -> Result<Self, ConfigError> { |
| 424 | Self::new_inner( | 500 | Self::new_inner( |
| 425 | peri, | 501 | peri, |
| 426 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 502 | new_pin!(tx, config.tx_af()), |
| 427 | new_pin!(cts, AfType::input(Pull::None)), | 503 | new_pin!(cts, AfType::input(config.cts_pull)), |
| 428 | None, | 504 | None, |
| 429 | config, | 505 | config, |
| 430 | ) | 506 | ) |
| @@ -433,9 +509,9 @@ impl<'d> UartTx<'d, Blocking> { | |||
| 433 | 509 | ||
| 434 | impl<'d, M: Mode> UartTx<'d, M> { | 510 | impl<'d, M: Mode> UartTx<'d, M> { |
| 435 | fn new_inner<T: Instance>( | 511 | fn new_inner<T: Instance>( |
| 436 | _peri: impl Peripheral<P = T> + 'd, | 512 | _peri: Peri<'d, T>, |
| 437 | tx: Option<PeripheralRef<'d, AnyPin>>, | 513 | tx: Option<Peri<'d, AnyPin>>, |
| 438 | cts: Option<PeripheralRef<'d, AnyPin>>, | 514 | cts: Option<Peri<'d, AnyPin>>, |
| 439 | tx_dma: Option<ChannelAndRequest<'d>>, | 515 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 440 | config: Config, | 516 | config: Config, |
| 441 | ) -> Result<Self, ConfigError> { | 517 | ) -> Result<Self, ConfigError> { |
| @@ -447,6 +523,7 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 447 | cts, | 523 | cts, |
| 448 | de: None, | 524 | de: None, |
| 449 | tx_dma, | 525 | tx_dma, |
| 526 | duplex: config.duplex, | ||
| 450 | _phantom: PhantomData, | 527 | _phantom: PhantomData, |
| 451 | }; | 528 | }; |
| 452 | this.enable_and_configure(&config)?; | 529 | this.enable_and_configure(&config)?; |
| @@ -477,13 +554,7 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 477 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { | 554 | pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { |
| 478 | let r = self.info.regs; | 555 | let r = self.info.regs; |
| 479 | 556 | ||
| 480 | // Enable Transmitter and disable Receiver for Half-Duplex mode | 557 | half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); |
| 481 | let mut cr1 = r.cr1().read(); | ||
| 482 | if r.cr3().read().hdsel() && !cr1.te() { | ||
| 483 | cr1.set_te(true); | ||
| 484 | cr1.set_re(false); | ||
| 485 | r.cr1().write_value(cr1); | ||
| 486 | } | ||
| 487 | 558 | ||
| 488 | for &b in buffer { | 559 | for &b in buffer { |
| 489 | while !sr(r).read().txe() {} | 560 | while !sr(r).read().txe() {} |
| @@ -496,53 +567,110 @@ impl<'d, M: Mode> UartTx<'d, M> { | |||
| 496 | pub fn blocking_flush(&mut self) -> Result<(), Error> { | 567 | pub fn blocking_flush(&mut self) -> Result<(), Error> { |
| 497 | blocking_flush(self.info) | 568 | blocking_flush(self.info) |
| 498 | } | 569 | } |
| 570 | |||
| 571 | /// Send break character | ||
| 572 | pub fn send_break(&self) { | ||
| 573 | send_break(&self.info.regs); | ||
| 574 | } | ||
| 575 | |||
| 576 | /// Set baudrate | ||
| 577 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 578 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 579 | } | ||
| 499 | } | 580 | } |
| 500 | 581 | ||
| 501 | fn blocking_flush(info: &Info) -> Result<(), Error> { | 582 | /// Wait until transmission complete |
| 583 | async fn flush(info: &Info, state: &State) -> Result<(), Error> { | ||
| 502 | let r = info.regs; | 584 | let r = info.regs; |
| 503 | while !sr(r).read().tc() {} | 585 | if r.cr1().read().te() && !sr(r).read().tc() { |
| 586 | r.cr1().modify(|w| { | ||
| 587 | // enable Transmission Complete interrupt | ||
| 588 | w.set_tcie(true); | ||
| 589 | }); | ||
| 590 | |||
| 591 | compiler_fence(Ordering::SeqCst); | ||
| 592 | |||
| 593 | // future which completes when Transmission complete is detected | ||
| 594 | let abort = poll_fn(move |cx| { | ||
| 595 | state.tx_waker.register(cx.waker()); | ||
| 504 | 596 | ||
| 505 | // Enable Receiver after transmission complete for Half-Duplex mode | 597 | let sr = sr(r).read(); |
| 506 | if r.cr3().read().hdsel() { | 598 | if sr.tc() { |
| 507 | r.cr1().modify(|reg| reg.set_re(true)); | 599 | // Transmission complete detected |
| 600 | return Poll::Ready(()); | ||
| 601 | } | ||
| 602 | |||
| 603 | Poll::Pending | ||
| 604 | }); | ||
| 605 | |||
| 606 | abort.await; | ||
| 508 | } | 607 | } |
| 509 | 608 | ||
| 510 | Ok(()) | 609 | Ok(()) |
| 511 | } | 610 | } |
| 512 | 611 | ||
| 612 | fn blocking_flush(info: &Info) -> Result<(), Error> { | ||
| 613 | let r = info.regs; | ||
| 614 | if r.cr1().read().te() { | ||
| 615 | while !sr(r).read().tc() {} | ||
| 616 | } | ||
| 617 | |||
| 618 | Ok(()) | ||
| 619 | } | ||
| 620 | |||
| 621 | /// Send break character | ||
| 622 | pub fn send_break(regs: &Regs) { | ||
| 623 | // Busy wait until previous break has been sent | ||
| 624 | #[cfg(any(usart_v1, usart_v2))] | ||
| 625 | while regs.cr1().read().sbk() {} | ||
| 626 | #[cfg(any(usart_v3, usart_v4))] | ||
| 627 | while regs.isr().read().sbkf() {} | ||
| 628 | |||
| 629 | // Send break right after completing the current character transmission | ||
| 630 | #[cfg(any(usart_v1, usart_v2))] | ||
| 631 | regs.cr1().modify(|w| w.set_sbk(true)); | ||
| 632 | #[cfg(any(usart_v3, usart_v4))] | ||
| 633 | regs.rqr().write(|w| w.set_sbkrq(true)); | ||
| 634 | } | ||
| 635 | |||
| 636 | /// Enable Transmitter and disable Receiver for Half-Duplex mode | ||
| 637 | /// In case of readback, keep Receiver enabled | ||
| 638 | fn half_duplex_set_rx_tx_before_write(r: &Regs, enable_readback: bool) { | ||
| 639 | let mut cr1 = r.cr1().read(); | ||
| 640 | if r.cr3().read().hdsel() && !cr1.te() { | ||
| 641 | cr1.set_te(true); | ||
| 642 | cr1.set_re(enable_readback); | ||
| 643 | r.cr1().write_value(cr1); | ||
| 644 | } | ||
| 645 | } | ||
| 646 | |||
| 513 | impl<'d> UartRx<'d, Async> { | 647 | impl<'d> UartRx<'d, Async> { |
| 514 | /// Create a new rx-only UART with no hardware flow control. | 648 | /// Create a new rx-only UART with no hardware flow control. |
| 515 | /// | 649 | /// |
| 516 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. | 650 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. |
| 517 | pub fn new<T: Instance>( | 651 | pub fn new<T: Instance>( |
| 518 | peri: impl Peripheral<P = T> + 'd, | 652 | peri: Peri<'d, T>, |
| 519 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 653 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 520 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 654 | rx: Peri<'d, impl RxPin<T>>, |
| 521 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 655 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 522 | config: Config, | 656 | config: Config, |
| 523 | ) -> Result<Self, ConfigError> { | 657 | ) -> Result<Self, ConfigError> { |
| 524 | Self::new_inner( | 658 | Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, new_dma!(rx_dma), config) |
| 525 | peri, | ||
| 526 | new_pin!(rx, AfType::input(Pull::None)), | ||
| 527 | None, | ||
| 528 | new_dma!(rx_dma), | ||
| 529 | config, | ||
| 530 | ) | ||
| 531 | } | 659 | } |
| 532 | 660 | ||
| 533 | /// Create a new rx-only UART with a request-to-send pin | 661 | /// Create a new rx-only UART with a request-to-send pin |
| 534 | pub fn new_with_rts<T: Instance>( | 662 | pub fn new_with_rts<T: Instance>( |
| 535 | peri: impl Peripheral<P = T> + 'd, | 663 | peri: Peri<'d, T>, |
| 536 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 664 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 537 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 665 | rx: Peri<'d, impl RxPin<T>>, |
| 538 | rts: impl Peripheral<P = impl RtsPin<T>> + 'd, | 666 | rts: Peri<'d, impl RtsPin<T>>, |
| 539 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 667 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 540 | config: Config, | 668 | config: Config, |
| 541 | ) -> Result<Self, ConfigError> { | 669 | ) -> Result<Self, ConfigError> { |
| 542 | Self::new_inner( | 670 | Self::new_inner( |
| 543 | peri, | 671 | peri, |
| 544 | new_pin!(rx, AfType::input(Pull::None)), | 672 | new_pin!(rx, config.rx_af()), |
| 545 | new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), | 673 | new_pin!(rts, config.rts_config.af_type()), |
| 546 | new_dma!(rx_dma), | 674 | new_dma!(rx_dma), |
| 547 | config, | 675 | config, |
| 548 | ) | 676 | ) |
| @@ -570,12 +698,17 @@ impl<'d> UartRx<'d, Async> { | |||
| 570 | // Call flush for Half-Duplex mode if some bytes were written and flush was not called. | 698 | // Call flush for Half-Duplex mode if some bytes were written and flush was not called. |
| 571 | // It prevents reading of bytes which have just been written. | 699 | // It prevents reading of bytes which have just been written. |
| 572 | if r.cr3().read().hdsel() && r.cr1().read().te() { | 700 | if r.cr3().read().hdsel() && r.cr1().read().te() { |
| 573 | blocking_flush(self.info)?; | 701 | flush(&self.info, &self.state).await?; |
| 702 | |||
| 703 | // Disable Transmitter and enable Receiver after flush | ||
| 704 | r.cr1().modify(|reg| { | ||
| 705 | reg.set_re(true); | ||
| 706 | reg.set_te(false); | ||
| 707 | }); | ||
| 574 | } | 708 | } |
| 575 | 709 | ||
| 576 | // make sure USART state is restored to neutral state when this future is dropped | 710 | // make sure USART state is restored to neutral state when this future is dropped |
| 577 | let on_drop = OnDrop::new(move || { | 711 | let on_drop = OnDrop::new(move || { |
| 578 | // defmt::trace!("Clear all USART interrupts and DMA Read Request"); | ||
| 579 | // clear all interrupts and DMA Rx Request | 712 | // clear all interrupts and DMA Rx Request |
| 580 | r.cr1().modify(|w| { | 713 | r.cr1().modify(|w| { |
| 581 | // disable RXNE interrupt | 714 | // disable RXNE interrupt |
| @@ -767,24 +900,24 @@ impl<'d> UartRx<'d, Blocking> { | |||
| 767 | /// | 900 | /// |
| 768 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. | 901 | /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. |
| 769 | pub fn new_blocking<T: Instance>( | 902 | pub fn new_blocking<T: Instance>( |
| 770 | peri: impl Peripheral<P = T> + 'd, | 903 | peri: Peri<'d, T>, |
| 771 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 904 | rx: Peri<'d, impl RxPin<T>>, |
| 772 | config: Config, | 905 | config: Config, |
| 773 | ) -> Result<Self, ConfigError> { | 906 | ) -> Result<Self, ConfigError> { |
| 774 | Self::new_inner(peri, new_pin!(rx, AfType::input(Pull::None)), None, None, config) | 907 | Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config) |
| 775 | } | 908 | } |
| 776 | 909 | ||
| 777 | /// Create a new rx-only UART with a request-to-send pin | 910 | /// Create a new rx-only UART with a request-to-send pin |
| 778 | pub fn new_blocking_with_rts<T: Instance>( | 911 | pub fn new_blocking_with_rts<T: Instance>( |
| 779 | peri: impl Peripheral<P = T> + 'd, | 912 | peri: Peri<'d, T>, |
| 780 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 913 | rx: Peri<'d, impl RxPin<T>>, |
| 781 | rts: impl Peripheral<P = impl RtsPin<T>> + 'd, | 914 | rts: Peri<'d, impl RtsPin<T>>, |
| 782 | config: Config, | 915 | config: Config, |
| 783 | ) -> Result<Self, ConfigError> { | 916 | ) -> Result<Self, ConfigError> { |
| 784 | Self::new_inner( | 917 | Self::new_inner( |
| 785 | peri, | 918 | peri, |
| 786 | new_pin!(rx, AfType::input(Pull::None)), | 919 | new_pin!(rx, config.rx_af()), |
| 787 | new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), | 920 | new_pin!(rts, config.rts_config.af_type()), |
| 788 | None, | 921 | None, |
| 789 | config, | 922 | config, |
| 790 | ) | 923 | ) |
| @@ -793,9 +926,9 @@ impl<'d> UartRx<'d, Blocking> { | |||
| 793 | 926 | ||
| 794 | impl<'d, M: Mode> UartRx<'d, M> { | 927 | impl<'d, M: Mode> UartRx<'d, M> { |
| 795 | fn new_inner<T: Instance>( | 928 | fn new_inner<T: Instance>( |
| 796 | _peri: impl Peripheral<P = T> + 'd, | 929 | _peri: Peri<'d, T>, |
| 797 | rx: Option<PeripheralRef<'d, AnyPin>>, | 930 | rx: Option<Peri<'d, AnyPin>>, |
| 798 | rts: Option<PeripheralRef<'d, AnyPin>>, | 931 | rts: Option<Peri<'d, AnyPin>>, |
| 799 | rx_dma: Option<ChannelAndRequest<'d>>, | 932 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 800 | config: Config, | 933 | config: Config, |
| 801 | ) -> Result<Self, ConfigError> { | 934 | ) -> Result<Self, ConfigError> { |
| @@ -809,7 +942,7 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 809 | rx_dma, | 942 | rx_dma, |
| 810 | detect_previous_overrun: config.detect_previous_overrun, | 943 | detect_previous_overrun: config.detect_previous_overrun, |
| 811 | #[cfg(any(usart_v1, usart_v2))] | 944 | #[cfg(any(usart_v1, usart_v2))] |
| 812 | buffered_sr: stm32_metapac::usart::regs::Sr(0), | 945 | buffered_sr: regs::Sr(0), |
| 813 | }; | 946 | }; |
| 814 | this.enable_and_configure(&config)?; | 947 | this.enable_and_configure(&config)?; |
| 815 | Ok(this) | 948 | Ok(this) |
| @@ -909,6 +1042,12 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 909 | // It prevents reading of bytes which have just been written. | 1042 | // It prevents reading of bytes which have just been written. |
| 910 | if r.cr3().read().hdsel() && r.cr1().read().te() { | 1043 | if r.cr3().read().hdsel() && r.cr1().read().te() { |
| 911 | blocking_flush(self.info)?; | 1044 | blocking_flush(self.info)?; |
| 1045 | |||
| 1046 | // Disable Transmitter and enable Receiver after flush | ||
| 1047 | r.cr1().modify(|reg| { | ||
| 1048 | reg.set_re(true); | ||
| 1049 | reg.set_te(false); | ||
| 1050 | }); | ||
| 912 | } | 1051 | } |
| 913 | 1052 | ||
| 914 | for b in buffer { | 1053 | for b in buffer { |
| @@ -917,6 +1056,11 @@ impl<'d, M: Mode> UartRx<'d, M> { | |||
| 917 | } | 1056 | } |
| 918 | Ok(()) | 1057 | Ok(()) |
| 919 | } | 1058 | } |
| 1059 | |||
| 1060 | /// Set baudrate | ||
| 1061 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1062 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 1063 | } | ||
| 920 | } | 1064 | } |
| 921 | 1065 | ||
| 922 | impl<'d, M: Mode> Drop for UartTx<'d, M> { | 1066 | impl<'d, M: Mode> Drop for UartTx<'d, M> { |
| @@ -952,12 +1096,12 @@ fn drop_tx_rx(info: &Info, state: &State) { | |||
| 952 | impl<'d> Uart<'d, Async> { | 1096 | impl<'d> Uart<'d, Async> { |
| 953 | /// Create a new bidirectional UART | 1097 | /// Create a new bidirectional UART |
| 954 | pub fn new<T: Instance>( | 1098 | pub fn new<T: Instance>( |
| 955 | peri: impl Peripheral<P = T> + 'd, | 1099 | peri: Peri<'d, T>, |
| 956 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1100 | rx: Peri<'d, impl RxPin<T>>, |
| 957 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1101 | tx: Peri<'d, impl TxPin<T>>, |
| 958 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1102 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 959 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 1103 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 960 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 1104 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 961 | config: Config, | 1105 | config: Config, |
| 962 | ) -> Result<Self, ConfigError> { | 1106 | ) -> Result<Self, ConfigError> { |
| 963 | Self::new_inner( | 1107 | Self::new_inner( |
| @@ -975,22 +1119,22 @@ impl<'d> Uart<'d, Async> { | |||
| 975 | 1119 | ||
| 976 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins | 1120 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins |
| 977 | pub fn new_with_rtscts<T: Instance>( | 1121 | pub fn new_with_rtscts<T: Instance>( |
| 978 | peri: impl Peripheral<P = T> + 'd, | 1122 | peri: Peri<'d, T>, |
| 979 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1123 | rx: Peri<'d, impl RxPin<T>>, |
| 980 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1124 | tx: Peri<'d, impl TxPin<T>>, |
| 981 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1125 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 982 | rts: impl Peripheral<P = impl RtsPin<T>> + 'd, | 1126 | rts: Peri<'d, impl RtsPin<T>>, |
| 983 | cts: impl Peripheral<P = impl CtsPin<T>> + 'd, | 1127 | cts: Peri<'d, impl CtsPin<T>>, |
| 984 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 1128 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 985 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 1129 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 986 | config: Config, | 1130 | config: Config, |
| 987 | ) -> Result<Self, ConfigError> { | 1131 | ) -> Result<Self, ConfigError> { |
| 988 | Self::new_inner( | 1132 | Self::new_inner( |
| 989 | peri, | 1133 | peri, |
| 990 | new_pin!(rx, config.rx_af()), | 1134 | new_pin!(rx, config.rx_af()), |
| 991 | new_pin!(tx, config.tx_af()), | 1135 | new_pin!(tx, config.tx_af()), |
| 992 | new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), | 1136 | new_pin!(rts, config.rts_config.af_type()), |
| 993 | new_pin!(cts, AfType::input(Pull::None)), | 1137 | new_pin!(cts, AfType::input(config.cts_pull)), |
| 994 | None, | 1138 | None, |
| 995 | new_dma!(tx_dma), | 1139 | new_dma!(tx_dma), |
| 996 | new_dma!(rx_dma), | 1140 | new_dma!(rx_dma), |
| @@ -1001,13 +1145,13 @@ impl<'d> Uart<'d, Async> { | |||
| 1001 | #[cfg(not(any(usart_v1, usart_v2)))] | 1145 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1002 | /// Create a new bidirectional UART with a driver-enable pin | 1146 | /// Create a new bidirectional UART with a driver-enable pin |
| 1003 | pub fn new_with_de<T: Instance>( | 1147 | pub fn new_with_de<T: Instance>( |
| 1004 | peri: impl Peripheral<P = T> + 'd, | 1148 | peri: Peri<'d, T>, |
| 1005 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1149 | rx: Peri<'d, impl RxPin<T>>, |
| 1006 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1150 | tx: Peri<'d, impl TxPin<T>>, |
| 1007 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1151 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1008 | de: impl Peripheral<P = impl DePin<T>> + 'd, | 1152 | de: Peri<'d, impl DePin<T>>, |
| 1009 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 1153 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1010 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 1154 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 1011 | config: Config, | 1155 | config: Config, |
| 1012 | ) -> Result<Self, ConfigError> { | 1156 | ) -> Result<Self, ConfigError> { |
| 1013 | Self::new_inner( | 1157 | Self::new_inner( |
| @@ -1016,7 +1160,7 @@ impl<'d> Uart<'d, Async> { | |||
| 1016 | new_pin!(tx, config.tx_af()), | 1160 | new_pin!(tx, config.tx_af()), |
| 1017 | None, | 1161 | None, |
| 1018 | None, | 1162 | None, |
| 1019 | new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), | 1163 | new_pin!(de, config.de_config.af_type()), |
| 1020 | new_dma!(tx_dma), | 1164 | new_dma!(tx_dma), |
| 1021 | new_dma!(rx_dma), | 1165 | new_dma!(rx_dma), |
| 1022 | config, | 1166 | config, |
| @@ -1029,29 +1173,31 @@ impl<'d> Uart<'d, Async> { | |||
| 1029 | /// (when it is available for your chip). There is no functional difference between these methods, as both | 1173 | /// (when it is available for your chip). There is no functional difference between these methods, as both |
| 1030 | /// allow bidirectional communication. | 1174 | /// allow bidirectional communication. |
| 1031 | /// | 1175 | /// |
| 1032 | /// The pin is always released when no data is transmitted. Thus, it acts as a standard | 1176 | /// The TX pin is always released when no data is transmitted. Thus, it acts as a standard |
| 1033 | /// I/O in idle or in reception. | 1177 | /// I/O in idle or in reception. It means that the I/O must be configured so that TX is |
| 1178 | /// configured as alternate function open-drain with an external pull-up | ||
| 1034 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict | 1179 | /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict |
| 1035 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 1180 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 1036 | #[doc(alias("HDSEL"))] | 1181 | #[doc(alias("HDSEL"))] |
| 1037 | pub fn new_half_duplex<T: Instance>( | 1182 | pub fn new_half_duplex<T: Instance>( |
| 1038 | peri: impl Peripheral<P = T> + 'd, | 1183 | peri: Peri<'d, T>, |
| 1039 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1184 | tx: Peri<'d, impl TxPin<T>>, |
| 1040 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1185 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1041 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 1186 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1042 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 1187 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 1043 | mut config: Config, | 1188 | mut config: Config, |
| 1189 | readback: HalfDuplexReadback, | ||
| 1044 | ) -> Result<Self, ConfigError> { | 1190 | ) -> Result<Self, ConfigError> { |
| 1045 | #[cfg(not(any(usart_v1, usart_v2)))] | 1191 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1046 | { | 1192 | { |
| 1047 | config.swap_rx_tx = false; | 1193 | config.swap_rx_tx = false; |
| 1048 | } | 1194 | } |
| 1049 | config.half_duplex = true; | 1195 | config.duplex = Duplex::Half(readback); |
| 1050 | 1196 | ||
| 1051 | Self::new_inner( | 1197 | Self::new_inner( |
| 1052 | peri, | 1198 | peri, |
| 1053 | None, | 1199 | None, |
| 1054 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 1200 | new_pin!(tx, config.tx_af()), |
| 1055 | None, | 1201 | None, |
| 1056 | None, | 1202 | None, |
| 1057 | None, | 1203 | None, |
| @@ -1073,21 +1219,22 @@ impl<'d> Uart<'d, Async> { | |||
| 1073 | #[cfg(not(any(usart_v1, usart_v2)))] | 1219 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1074 | #[doc(alias("HDSEL"))] | 1220 | #[doc(alias("HDSEL"))] |
| 1075 | pub fn new_half_duplex_on_rx<T: Instance>( | 1221 | pub fn new_half_duplex_on_rx<T: Instance>( |
| 1076 | peri: impl Peripheral<P = T> + 'd, | 1222 | peri: Peri<'d, T>, |
| 1077 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1223 | rx: Peri<'d, impl RxPin<T>>, |
| 1078 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 1224 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 1079 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, | 1225 | tx_dma: Peri<'d, impl TxDma<T>>, |
| 1080 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, | 1226 | rx_dma: Peri<'d, impl RxDma<T>>, |
| 1081 | mut config: Config, | 1227 | mut config: Config, |
| 1228 | readback: HalfDuplexReadback, | ||
| 1082 | ) -> Result<Self, ConfigError> { | 1229 | ) -> Result<Self, ConfigError> { |
| 1083 | config.swap_rx_tx = true; | 1230 | config.swap_rx_tx = true; |
| 1084 | config.half_duplex = true; | 1231 | config.duplex = Duplex::Half(readback); |
| 1085 | 1232 | ||
| 1086 | Self::new_inner( | 1233 | Self::new_inner( |
| 1087 | peri, | 1234 | peri, |
| 1088 | None, | 1235 | None, |
| 1089 | None, | 1236 | None, |
| 1090 | new_pin!(rx, AfType::output(OutputType::PushPull, Speed::Medium)), | 1237 | new_pin!(rx, config.rx_af()), |
| 1091 | None, | 1238 | None, |
| 1092 | None, | 1239 | None, |
| 1093 | new_dma!(tx_dma), | 1240 | new_dma!(tx_dma), |
| @@ -1101,6 +1248,11 @@ impl<'d> Uart<'d, Async> { | |||
| 1101 | self.tx.write(buffer).await | 1248 | self.tx.write(buffer).await |
| 1102 | } | 1249 | } |
| 1103 | 1250 | ||
| 1251 | /// Wait until transmission complete | ||
| 1252 | pub async fn flush(&mut self) -> Result<(), Error> { | ||
| 1253 | self.tx.flush().await | ||
| 1254 | } | ||
| 1255 | |||
| 1104 | /// Perform an asynchronous read into `buffer` | 1256 | /// Perform an asynchronous read into `buffer` |
| 1105 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 1257 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 1106 | self.rx.read(buffer).await | 1258 | self.rx.read(buffer).await |
| @@ -1115,9 +1267,9 @@ impl<'d> Uart<'d, Async> { | |||
| 1115 | impl<'d> Uart<'d, Blocking> { | 1267 | impl<'d> Uart<'d, Blocking> { |
| 1116 | /// Create a new blocking bidirectional UART. | 1268 | /// Create a new blocking bidirectional UART. |
| 1117 | pub fn new_blocking<T: Instance>( | 1269 | pub fn new_blocking<T: Instance>( |
| 1118 | peri: impl Peripheral<P = T> + 'd, | 1270 | peri: Peri<'d, T>, |
| 1119 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1271 | rx: Peri<'d, impl RxPin<T>>, |
| 1120 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1272 | tx: Peri<'d, impl TxPin<T>>, |
| 1121 | config: Config, | 1273 | config: Config, |
| 1122 | ) -> Result<Self, ConfigError> { | 1274 | ) -> Result<Self, ConfigError> { |
| 1123 | Self::new_inner( | 1275 | Self::new_inner( |
| @@ -1135,19 +1287,19 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1135 | 1287 | ||
| 1136 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins | 1288 | /// Create a new bidirectional UART with request-to-send and clear-to-send pins |
| 1137 | pub fn new_blocking_with_rtscts<T: Instance>( | 1289 | pub fn new_blocking_with_rtscts<T: Instance>( |
| 1138 | peri: impl Peripheral<P = T> + 'd, | 1290 | peri: Peri<'d, T>, |
| 1139 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1291 | rx: Peri<'d, impl RxPin<T>>, |
| 1140 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1292 | tx: Peri<'d, impl TxPin<T>>, |
| 1141 | rts: impl Peripheral<P = impl RtsPin<T>> + 'd, | 1293 | rts: Peri<'d, impl RtsPin<T>>, |
| 1142 | cts: impl Peripheral<P = impl CtsPin<T>> + 'd, | 1294 | cts: Peri<'d, impl CtsPin<T>>, |
| 1143 | config: Config, | 1295 | config: Config, |
| 1144 | ) -> Result<Self, ConfigError> { | 1296 | ) -> Result<Self, ConfigError> { |
| 1145 | Self::new_inner( | 1297 | Self::new_inner( |
| 1146 | peri, | 1298 | peri, |
| 1147 | new_pin!(rx, config.rx_af()), | 1299 | new_pin!(rx, config.rx_af()), |
| 1148 | new_pin!(tx, config.tx_af()), | 1300 | new_pin!(tx, config.tx_af()), |
| 1149 | new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), | 1301 | new_pin!(rts, config.rts_config.af_type()), |
| 1150 | new_pin!(cts, AfType::input(Pull::None)), | 1302 | new_pin!(cts, AfType::input(config.cts_pull)), |
| 1151 | None, | 1303 | None, |
| 1152 | None, | 1304 | None, |
| 1153 | None, | 1305 | None, |
| @@ -1158,10 +1310,10 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1158 | #[cfg(not(any(usart_v1, usart_v2)))] | 1310 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1159 | /// Create a new bidirectional UART with a driver-enable pin | 1311 | /// Create a new bidirectional UART with a driver-enable pin |
| 1160 | pub fn new_blocking_with_de<T: Instance>( | 1312 | pub fn new_blocking_with_de<T: Instance>( |
| 1161 | peri: impl Peripheral<P = T> + 'd, | 1313 | peri: Peri<'d, T>, |
| 1162 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1314 | rx: Peri<'d, impl RxPin<T>>, |
| 1163 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1315 | tx: Peri<'d, impl TxPin<T>>, |
| 1164 | de: impl Peripheral<P = impl DePin<T>> + 'd, | 1316 | de: Peri<'d, impl DePin<T>>, |
| 1165 | config: Config, | 1317 | config: Config, |
| 1166 | ) -> Result<Self, ConfigError> { | 1318 | ) -> Result<Self, ConfigError> { |
| 1167 | Self::new_inner( | 1319 | Self::new_inner( |
| @@ -1170,7 +1322,7 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1170 | new_pin!(tx, config.tx_af()), | 1322 | new_pin!(tx, config.tx_af()), |
| 1171 | None, | 1323 | None, |
| 1172 | None, | 1324 | None, |
| 1173 | new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)), | 1325 | new_pin!(de, config.de_config.af_type()), |
| 1174 | None, | 1326 | None, |
| 1175 | None, | 1327 | None, |
| 1176 | config, | 1328 | config, |
| @@ -1189,20 +1341,21 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1189 | /// on the line must be managed by software (for instance by using a centralized arbiter). | 1341 | /// on the line must be managed by software (for instance by using a centralized arbiter). |
| 1190 | #[doc(alias("HDSEL"))] | 1342 | #[doc(alias("HDSEL"))] |
| 1191 | pub fn new_blocking_half_duplex<T: Instance>( | 1343 | pub fn new_blocking_half_duplex<T: Instance>( |
| 1192 | peri: impl Peripheral<P = T> + 'd, | 1344 | peri: Peri<'d, T>, |
| 1193 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 1345 | tx: Peri<'d, impl TxPin<T>>, |
| 1194 | mut config: Config, | 1346 | mut config: Config, |
| 1347 | readback: HalfDuplexReadback, | ||
| 1195 | ) -> Result<Self, ConfigError> { | 1348 | ) -> Result<Self, ConfigError> { |
| 1196 | #[cfg(not(any(usart_v1, usart_v2)))] | 1349 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1197 | { | 1350 | { |
| 1198 | config.swap_rx_tx = false; | 1351 | config.swap_rx_tx = false; |
| 1199 | } | 1352 | } |
| 1200 | config.half_duplex = true; | 1353 | config.duplex = Duplex::Half(readback); |
| 1201 | 1354 | ||
| 1202 | Self::new_inner( | 1355 | Self::new_inner( |
| 1203 | peri, | 1356 | peri, |
| 1204 | None, | 1357 | None, |
| 1205 | new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), | 1358 | new_pin!(tx, config.tx_af()), |
| 1206 | None, | 1359 | None, |
| 1207 | None, | 1360 | None, |
| 1208 | None, | 1361 | None, |
| @@ -1224,18 +1377,19 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1224 | #[cfg(not(any(usart_v1, usart_v2)))] | 1377 | #[cfg(not(any(usart_v1, usart_v2)))] |
| 1225 | #[doc(alias("HDSEL"))] | 1378 | #[doc(alias("HDSEL"))] |
| 1226 | pub fn new_blocking_half_duplex_on_rx<T: Instance>( | 1379 | pub fn new_blocking_half_duplex_on_rx<T: Instance>( |
| 1227 | peri: impl Peripheral<P = T> + 'd, | 1380 | peri: Peri<'d, T>, |
| 1228 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 1381 | rx: Peri<'d, impl RxPin<T>>, |
| 1229 | mut config: Config, | 1382 | mut config: Config, |
| 1383 | readback: HalfDuplexReadback, | ||
| 1230 | ) -> Result<Self, ConfigError> { | 1384 | ) -> Result<Self, ConfigError> { |
| 1231 | config.swap_rx_tx = true; | 1385 | config.swap_rx_tx = true; |
| 1232 | config.half_duplex = true; | 1386 | config.duplex = Duplex::Half(readback); |
| 1233 | 1387 | ||
| 1234 | Self::new_inner( | 1388 | Self::new_inner( |
| 1235 | peri, | 1389 | peri, |
| 1236 | None, | 1390 | None, |
| 1237 | None, | 1391 | None, |
| 1238 | new_pin!(rx, AfType::output(OutputType::PushPull, Speed::Medium)), | 1392 | new_pin!(rx, config.rx_af()), |
| 1239 | None, | 1393 | None, |
| 1240 | None, | 1394 | None, |
| 1241 | None, | 1395 | None, |
| @@ -1247,12 +1401,12 @@ impl<'d> Uart<'d, Blocking> { | |||
| 1247 | 1401 | ||
| 1248 | impl<'d, M: Mode> Uart<'d, M> { | 1402 | impl<'d, M: Mode> Uart<'d, M> { |
| 1249 | fn new_inner<T: Instance>( | 1403 | fn new_inner<T: Instance>( |
| 1250 | _peri: impl Peripheral<P = T> + 'd, | 1404 | _peri: Peri<'d, T>, |
| 1251 | rx: Option<PeripheralRef<'d, AnyPin>>, | 1405 | rx: Option<Peri<'d, AnyPin>>, |
| 1252 | tx: Option<PeripheralRef<'d, AnyPin>>, | 1406 | tx: Option<Peri<'d, AnyPin>>, |
| 1253 | rts: Option<PeripheralRef<'d, AnyPin>>, | 1407 | rts: Option<Peri<'d, AnyPin>>, |
| 1254 | cts: Option<PeripheralRef<'d, AnyPin>>, | 1408 | cts: Option<Peri<'d, AnyPin>>, |
| 1255 | de: Option<PeripheralRef<'d, AnyPin>>, | 1409 | de: Option<Peri<'d, AnyPin>>, |
| 1256 | tx_dma: Option<ChannelAndRequest<'d>>, | 1410 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 1257 | rx_dma: Option<ChannelAndRequest<'d>>, | 1411 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 1258 | config: Config, | 1412 | config: Config, |
| @@ -1271,6 +1425,7 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1271 | cts, | 1425 | cts, |
| 1272 | de, | 1426 | de, |
| 1273 | tx_dma, | 1427 | tx_dma, |
| 1428 | duplex: config.duplex, | ||
| 1274 | }, | 1429 | }, |
| 1275 | rx: UartRx { | 1430 | rx: UartRx { |
| 1276 | _phantom: PhantomData, | 1431 | _phantom: PhantomData, |
| @@ -1282,7 +1437,7 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1282 | rx_dma, | 1437 | rx_dma, |
| 1283 | detect_previous_overrun: config.detect_previous_overrun, | 1438 | detect_previous_overrun: config.detect_previous_overrun, |
| 1284 | #[cfg(any(usart_v1, usart_v2))] | 1439 | #[cfg(any(usart_v1, usart_v2))] |
| 1285 | buffered_sr: stm32_metapac::usart::regs::Sr(0), | 1440 | buffered_sr: regs::Sr(0), |
| 1286 | }, | 1441 | }, |
| 1287 | }; | 1442 | }; |
| 1288 | this.enable_and_configure(&config)?; | 1443 | this.enable_and_configure(&config)?; |
| @@ -1336,6 +1491,25 @@ impl<'d, M: Mode> Uart<'d, M> { | |||
| 1336 | pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { | 1491 | pub fn split(self) -> (UartTx<'d, M>, UartRx<'d, M>) { |
| 1337 | (self.tx, self.rx) | 1492 | (self.tx, self.rx) |
| 1338 | } | 1493 | } |
| 1494 | |||
| 1495 | /// Split the Uart into a transmitter and receiver by mutable reference, | ||
| 1496 | /// which is particularly useful when having two tasks correlating to | ||
| 1497 | /// transmitting and receiving. | ||
| 1498 | pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { | ||
| 1499 | (&mut self.tx, &mut self.rx) | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | /// Send break character | ||
| 1503 | pub fn send_break(&self) { | ||
| 1504 | self.tx.send_break(); | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | /// Set baudrate | ||
| 1508 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1509 | self.tx.set_baudrate(baudrate)?; | ||
| 1510 | self.rx.set_baudrate(baudrate)?; | ||
| 1511 | Ok(()) | ||
| 1512 | } | ||
| 1339 | } | 1513 | } |
| 1340 | 1514 | ||
| 1341 | fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> { | 1515 | fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> { |
| @@ -1351,20 +1525,33 @@ fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), | |||
| 1351 | Ok(()) | 1525 | Ok(()) |
| 1352 | } | 1526 | } |
| 1353 | 1527 | ||
| 1354 | fn configure( | 1528 | fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { |
| 1355 | info: &Info, | 1529 | // The calculation to be done to get the BRR is `mul * pclk / presc / baud` |
| 1356 | kernel_clock: Hertz, | 1530 | // To do this in 32-bit only we can't multiply `mul` and `pclk` |
| 1357 | config: &Config, | 1531 | let clock = pclk / presc; |
| 1358 | enable_rx: bool, | ||
| 1359 | enable_tx: bool, | ||
| 1360 | ) -> Result<(), ConfigError> { | ||
| 1361 | let r = info.regs; | ||
| 1362 | let kind = info.kind; | ||
| 1363 | 1532 | ||
| 1364 | if !enable_rx && !enable_tx { | 1533 | // The mul is applied as the last operation to prevent overflow |
| 1365 | return Err(ConfigError::RxOrTxNotEnabled); | 1534 | let brr = clock / baud * mul; |
| 1366 | } | 1535 | |
| 1536 | // The BRR calculation will be a bit off because of integer rounding. | ||
| 1537 | // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. | ||
| 1538 | let rounding = ((clock % baud) * mul + (baud / 2)) / baud; | ||
| 1539 | |||
| 1540 | brr + rounding | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1544 | info.interrupt.disable(); | ||
| 1367 | 1545 | ||
| 1546 | set_usart_baudrate(info, kernel_clock, baudrate)?; | ||
| 1547 | |||
| 1548 | info.interrupt.unpend(); | ||
| 1549 | unsafe { info.interrupt.enable() }; | ||
| 1550 | |||
| 1551 | Ok(()) | ||
| 1552 | } | ||
| 1553 | |||
| 1554 | fn find_and_set_brr(r: Regs, kind: Kind, kernel_clock: Hertz, baudrate: u32) -> Result<bool, ConfigError> { | ||
| 1368 | #[cfg(not(usart_v4))] | 1555 | #[cfg(not(usart_v4))] |
| 1369 | static DIVS: [(u16, ()); 1] = [(1, ())]; | 1556 | static DIVS: [(u16, ()); 1] = [(1, ())]; |
| 1370 | 1557 | ||
| @@ -1396,31 +1583,14 @@ fn configure( | |||
| 1396 | } | 1583 | } |
| 1397 | }; | 1584 | }; |
| 1398 | 1585 | ||
| 1399 | fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { | 1586 | let mut found_brr = None; |
| 1400 | // The calculation to be done to get the BRR is `mul * pclk / presc / baud` | ||
| 1401 | // To do this in 32-bit only we can't multiply `mul` and `pclk` | ||
| 1402 | let clock = pclk / presc; | ||
| 1403 | |||
| 1404 | // The mul is applied as the last operation to prevent overflow | ||
| 1405 | let brr = clock / baud * mul; | ||
| 1406 | |||
| 1407 | // The BRR calculation will be a bit off because of integer rounding. | ||
| 1408 | // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. | ||
| 1409 | let rounding = ((clock % baud) * mul + (baud / 2)) / baud; | ||
| 1410 | |||
| 1411 | brr + rounding | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | // UART must be disabled during configuration. | ||
| 1415 | r.cr1().modify(|w| { | ||
| 1416 | w.set_ue(false); | ||
| 1417 | }); | ||
| 1418 | |||
| 1419 | #[cfg(not(usart_v1))] | 1587 | #[cfg(not(usart_v1))] |
| 1420 | let mut over8 = false; | 1588 | let mut over8 = false; |
| 1421 | let mut found_brr = None; | 1589 | #[cfg(usart_v1)] |
| 1590 | let over8 = false; | ||
| 1591 | |||
| 1422 | for &(presc, _presc_val) in &DIVS { | 1592 | for &(presc, _presc_val) in &DIVS { |
| 1423 | let brr = calculate_brr(config.baudrate, kernel_clock.0, presc as u32, mul); | 1593 | let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); |
| 1424 | trace!( | 1594 | trace!( |
| 1425 | "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", | 1595 | "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", |
| 1426 | presc, | 1596 | presc, |
| @@ -1451,18 +1621,70 @@ fn configure( | |||
| 1451 | } | 1621 | } |
| 1452 | } | 1622 | } |
| 1453 | 1623 | ||
| 1454 | let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; | 1624 | match found_brr { |
| 1625 | Some(brr) => { | ||
| 1626 | #[cfg(not(usart_v1))] | ||
| 1627 | let oversampling = if over8 { "8 bit" } else { "16 bit" }; | ||
| 1628 | #[cfg(usart_v1)] | ||
| 1629 | let oversampling = "default"; | ||
| 1630 | trace!( | ||
| 1631 | "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", | ||
| 1632 | oversampling, | ||
| 1633 | baudrate, | ||
| 1634 | kernel_clock.0 / brr * mul | ||
| 1635 | ); | ||
| 1636 | Ok(over8) | ||
| 1637 | } | ||
| 1638 | None => Err(ConfigError::BaudrateTooLow), | ||
| 1639 | } | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { | ||
| 1643 | let r = info.regs; | ||
| 1644 | r.cr1().modify(|w| { | ||
| 1645 | // disable uart | ||
| 1646 | w.set_ue(false); | ||
| 1647 | }); | ||
| 1648 | |||
| 1649 | #[cfg(not(usart_v1))] | ||
| 1650 | let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; | ||
| 1651 | #[cfg(usart_v1)] | ||
| 1652 | let _over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; | ||
| 1653 | |||
| 1654 | r.cr1().modify(|w| { | ||
| 1655 | // enable uart | ||
| 1656 | w.set_ue(true); | ||
| 1657 | |||
| 1658 | #[cfg(not(usart_v1))] | ||
| 1659 | w.set_over8(vals::Over8::from_bits(over8 as _)); | ||
| 1660 | }); | ||
| 1661 | |||
| 1662 | Ok(()) | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | fn configure( | ||
| 1666 | info: &Info, | ||
| 1667 | kernel_clock: Hertz, | ||
| 1668 | config: &Config, | ||
| 1669 | enable_rx: bool, | ||
| 1670 | enable_tx: bool, | ||
| 1671 | ) -> Result<(), ConfigError> { | ||
| 1672 | let r = info.regs; | ||
| 1673 | let kind = info.kind; | ||
| 1674 | |||
| 1675 | if !enable_rx && !enable_tx { | ||
| 1676 | return Err(ConfigError::RxOrTxNotEnabled); | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | // UART must be disabled during configuration. | ||
| 1680 | r.cr1().modify(|w| { | ||
| 1681 | w.set_ue(false); | ||
| 1682 | }); | ||
| 1455 | 1683 | ||
| 1456 | #[cfg(not(usart_v1))] | 1684 | #[cfg(not(usart_v1))] |
| 1457 | let oversampling = if over8 { "8 bit" } else { "16 bit" }; | 1685 | let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; |
| 1458 | #[cfg(usart_v1)] | 1686 | #[cfg(usart_v1)] |
| 1459 | let oversampling = "default"; | 1687 | let _over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; |
| 1460 | trace!( | ||
| 1461 | "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", | ||
| 1462 | oversampling, | ||
| 1463 | config.baudrate, | ||
| 1464 | kernel_clock.0 / brr * mul | ||
| 1465 | ); | ||
| 1466 | 1688 | ||
| 1467 | r.cr2().write(|w| { | 1689 | r.cr2().write(|w| { |
| 1468 | w.set_stop(match config.stop_bits { | 1690 | w.set_stop(match config.stop_bits { |
| @@ -1483,14 +1705,14 @@ fn configure( | |||
| 1483 | r.cr3().modify(|w| { | 1705 | r.cr3().modify(|w| { |
| 1484 | #[cfg(not(usart_v1))] | 1706 | #[cfg(not(usart_v1))] |
| 1485 | w.set_onebit(config.assume_noise_free); | 1707 | w.set_onebit(config.assume_noise_free); |
| 1486 | w.set_hdsel(config.half_duplex); | 1708 | w.set_hdsel(config.duplex.is_half()); |
| 1487 | }); | 1709 | }); |
| 1488 | 1710 | ||
| 1489 | r.cr1().write(|w| { | 1711 | r.cr1().write(|w| { |
| 1490 | // enable uart | 1712 | // enable uart |
| 1491 | w.set_ue(true); | 1713 | w.set_ue(true); |
| 1492 | 1714 | ||
| 1493 | if config.half_duplex { | 1715 | if config.duplex.is_half() { |
| 1494 | // The te and re bits will be set by write, read and flush methods. | 1716 | // The te and re bits will be set by write, read and flush methods. |
| 1495 | // Receiver should be enabled by default for Half-Duplex. | 1717 | // Receiver should be enabled by default for Half-Duplex. |
| 1496 | w.set_te(false); | 1718 | w.set_te(false); |
| @@ -1502,31 +1724,66 @@ fn configure( | |||
| 1502 | w.set_re(enable_rx); | 1724 | w.set_re(enable_rx); |
| 1503 | } | 1725 | } |
| 1504 | 1726 | ||
| 1505 | // configure word size | 1727 | // configure word size and parity, since the parity bit is inserted into the MSB position, |
| 1506 | // if using odd or even parity it must be configured to 9bits | 1728 | // it increases the effective word size |
| 1507 | w.set_m0(if config.parity != Parity::ParityNone { | 1729 | match (config.parity, config.data_bits) { |
| 1508 | trace!("USART: m0: vals::M0::BIT9"); | 1730 | (Parity::ParityNone, DataBits::DataBits8) => { |
| 1509 | vals::M0::BIT9 | 1731 | trace!("USART: m0: 8 data bits, no parity"); |
| 1510 | } else { | 1732 | w.set_m0(vals::M0::BIT8); |
| 1511 | trace!("USART: m0: vals::M0::BIT8"); | 1733 | #[cfg(any(usart_v3, usart_v4))] |
| 1512 | vals::M0::BIT8 | 1734 | w.set_m1(vals::M1::M0); |
| 1513 | }); | 1735 | w.set_pce(false); |
| 1514 | // configure parity | ||
| 1515 | w.set_pce(config.parity != Parity::ParityNone); | ||
| 1516 | w.set_ps(match config.parity { | ||
| 1517 | Parity::ParityOdd => { | ||
| 1518 | trace!("USART: set_ps: vals::Ps::ODD"); | ||
| 1519 | vals::Ps::ODD | ||
| 1520 | } | 1736 | } |
| 1521 | Parity::ParityEven => { | 1737 | (Parity::ParityNone, DataBits::DataBits9) => { |
| 1522 | trace!("USART: set_ps: vals::Ps::EVEN"); | 1738 | trace!("USART: m0: 9 data bits, no parity"); |
| 1523 | vals::Ps::EVEN | 1739 | w.set_m0(vals::M0::BIT9); |
| 1740 | #[cfg(any(usart_v3, usart_v4))] | ||
| 1741 | w.set_m1(vals::M1::M0); | ||
| 1742 | w.set_pce(false); | ||
| 1743 | } | ||
| 1744 | #[cfg(any(usart_v3, usart_v4))] | ||
| 1745 | (Parity::ParityNone, DataBits::DataBits7) => { | ||
| 1746 | trace!("USART: m0: 7 data bits, no parity"); | ||
| 1747 | w.set_m0(vals::M0::BIT8); | ||
| 1748 | w.set_m1(vals::M1::BIT7); | ||
| 1749 | w.set_pce(false); | ||
| 1750 | } | ||
| 1751 | (Parity::ParityEven, DataBits::DataBits8) => { | ||
| 1752 | trace!("USART: m0: 8 data bits, even parity"); | ||
| 1753 | w.set_m0(vals::M0::BIT9); | ||
| 1754 | #[cfg(any(usart_v3, usart_v4))] | ||
| 1755 | w.set_m1(vals::M1::M0); | ||
| 1756 | w.set_pce(true); | ||
| 1757 | w.set_ps(vals::Ps::EVEN); | ||
| 1758 | } | ||
| 1759 | (Parity::ParityEven, DataBits::DataBits7) => { | ||
| 1760 | trace!("USART: m0: 7 data bits, even parity"); | ||
| 1761 | w.set_m0(vals::M0::BIT8); | ||
| 1762 | #[cfg(any(usart_v3, usart_v4))] | ||
| 1763 | w.set_m1(vals::M1::M0); | ||
| 1764 | w.set_pce(true); | ||
| 1765 | w.set_ps(vals::Ps::EVEN); | ||
| 1766 | } | ||
| 1767 | (Parity::ParityOdd, DataBits::DataBits8) => { | ||
| 1768 | trace!("USART: m0: 8 data bits, odd parity"); | ||
| 1769 | w.set_m0(vals::M0::BIT9); | ||
| 1770 | #[cfg(any(usart_v3, usart_v4))] | ||
| 1771 | w.set_m1(vals::M1::M0); | ||
| 1772 | w.set_pce(true); | ||
| 1773 | w.set_ps(vals::Ps::ODD); | ||
| 1774 | } | ||
| 1775 | (Parity::ParityOdd, DataBits::DataBits7) => { | ||
| 1776 | trace!("USART: m0: 7 data bits, odd parity"); | ||
| 1777 | w.set_m0(vals::M0::BIT8); | ||
| 1778 | #[cfg(any(usart_v3, usart_v4))] | ||
| 1779 | w.set_m1(vals::M1::M0); | ||
| 1780 | w.set_pce(true); | ||
| 1781 | w.set_ps(vals::Ps::ODD); | ||
| 1524 | } | 1782 | } |
| 1525 | _ => { | 1783 | _ => { |
| 1526 | trace!("USART: set_ps: vals::Ps::EVEN"); | 1784 | return Err(ConfigError::DataParityNotSupported); |
| 1527 | vals::Ps::EVEN | ||
| 1528 | } | 1785 | } |
| 1529 | }); | 1786 | } |
| 1530 | #[cfg(not(usart_v1))] | 1787 | #[cfg(not(usart_v1))] |
| 1531 | w.set_over8(vals::Over8::from_bits(over8 as _)); | 1788 | w.set_over8(vals::Over8::from_bits(over8 as _)); |
| 1532 | #[cfg(usart_v4)] | 1789 | #[cfg(usart_v4)] |
| @@ -1534,7 +1791,9 @@ fn configure( | |||
| 1534 | trace!("USART: set_fifoen: true (usart_v4)"); | 1791 | trace!("USART: set_fifoen: true (usart_v4)"); |
| 1535 | w.set_fifoen(true); | 1792 | w.set_fifoen(true); |
| 1536 | } | 1793 | } |
| 1537 | }); | 1794 | |
| 1795 | Ok(()) | ||
| 1796 | })?; | ||
| 1538 | 1797 | ||
| 1539 | Ok(()) | 1798 | Ok(()) |
| 1540 | } | 1799 | } |
| @@ -1672,7 +1931,7 @@ impl embedded_io_async::Write for Uart<'_, Async> { | |||
| 1672 | } | 1931 | } |
| 1673 | 1932 | ||
| 1674 | async fn flush(&mut self) -> Result<(), Self::Error> { | 1933 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1675 | self.blocking_flush() | 1934 | self.flush().await |
| 1676 | } | 1935 | } |
| 1677 | } | 1936 | } |
| 1678 | 1937 | ||
| @@ -1683,7 +1942,7 @@ impl embedded_io_async::Write for UartTx<'_, Async> { | |||
| 1683 | } | 1942 | } |
| 1684 | 1943 | ||
| 1685 | async fn flush(&mut self) -> Result<(), Self::Error> { | 1944 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1686 | self.blocking_flush() | 1945 | self.flush().await |
| 1687 | } | 1946 | } |
| 1688 | } | 1947 | } |
| 1689 | 1948 | ||
| @@ -1749,6 +2008,7 @@ enum Kind { | |||
| 1749 | 2008 | ||
| 1750 | struct State { | 2009 | struct State { |
| 1751 | rx_waker: AtomicWaker, | 2010 | rx_waker: AtomicWaker, |
| 2011 | tx_waker: AtomicWaker, | ||
| 1752 | tx_rx_refcount: AtomicU8, | 2012 | tx_rx_refcount: AtomicU8, |
| 1753 | } | 2013 | } |
| 1754 | 2014 | ||
| @@ -1756,6 +2016,7 @@ impl State { | |||
| 1756 | const fn new() -> Self { | 2016 | const fn new() -> Self { |
| 1757 | Self { | 2017 | Self { |
| 1758 | rx_waker: AtomicWaker::new(), | 2018 | rx_waker: AtomicWaker::new(), |
| 2019 | tx_waker: AtomicWaker::new(), | ||
| 1759 | tx_rx_refcount: AtomicU8::new(0), | 2020 | tx_rx_refcount: AtomicU8::new(0), |
| 1760 | } | 2021 | } |
| 1761 | } | 2022 | } |
| @@ -1777,7 +2038,7 @@ pub(crate) trait SealedInstance: crate::rcc::RccPeripheral { | |||
| 1777 | 2038 | ||
| 1778 | /// USART peripheral instance trait. | 2039 | /// USART peripheral instance trait. |
| 1779 | #[allow(private_bounds)] | 2040 | #[allow(private_bounds)] |
| 1780 | pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send { | 2041 | pub trait Instance: SealedInstance + PeripheralType + 'static + Send { |
| 1781 | /// Interrupt for this peripheral. | 2042 | /// Interrupt for this peripheral. |
| 1782 | type Interrupt: interrupt::typelevel::Interrupt; | 2043 | type Interrupt: interrupt::typelevel::Interrupt; |
| 1783 | } | 2044 | } |
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 8cf75933a..1d4a44896 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs | |||
| @@ -4,25 +4,85 @@ use core::sync::atomic::{compiler_fence, Ordering}; | |||
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use embassy_embedded_hal::SetConfig; | 6 | use embassy_embedded_hal::SetConfig; |
| 7 | use embassy_hal_internal::PeripheralRef; | 7 | use embedded_io_async::ReadReady; |
| 8 | use futures_util::future::{select, Either}; | 8 | use futures_util::future::{select, Either}; |
| 9 | 9 | ||
| 10 | use super::{clear_interrupt_flags, rdr, reconfigure, sr, Config, ConfigError, Error, Info, State, UartRx}; | 10 | use super::{rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx}; |
| 11 | use crate::dma::ReadableRingBuffer; | 11 | use crate::dma::ReadableRingBuffer; |
| 12 | use crate::gpio::{AnyPin, SealedPin as _}; | 12 | use crate::gpio::{AnyPin, SealedPin as _}; |
| 13 | use crate::mode::Async; | 13 | use crate::mode::Async; |
| 14 | use crate::time::Hertz; | 14 | use crate::time::Hertz; |
| 15 | use crate::usart::{Regs, Sr}; | 15 | use crate::usart::Regs; |
| 16 | use crate::Peri; | ||
| 16 | 17 | ||
| 17 | /// Rx-only Ring-buffered UART Driver | 18 | /// Rx-only Ring-buffered UART Driver |
| 18 | /// | 19 | /// |
| 19 | /// Created with [UartRx::into_ring_buffered] | 20 | /// Created with [UartRx::into_ring_buffered] |
| 21 | /// | ||
| 22 | /// ### Notes on 'waiting for bytes' | ||
| 23 | /// | ||
| 24 | /// The `read(buf)` (but not `read()`) and `read_exact(buf)` functions | ||
| 25 | /// may need to wait for bytes to arrive, if the ring buffer does not | ||
| 26 | /// contain enough bytes to fill the buffer passed by the caller of | ||
| 27 | /// the function, or is empty. | ||
| 28 | /// | ||
| 29 | /// Waiting for bytes operates in one of two modes, depending on | ||
| 30 | /// the behavior of the sender and the size of the buffer passed | ||
| 31 | /// to the function: | ||
| 32 | /// | ||
| 33 | /// - If the sender sends intermittently, the 'idle line' | ||
| 34 | /// condition will be detected when the sender stops, and any | ||
| 35 | /// bytes in the ring buffer will be returned. If there are no | ||
| 36 | /// bytes in the buffer, the check will be repeated each time the | ||
| 37 | /// 'idle line' condition is detected, so if the sender sends just | ||
| 38 | /// a single byte, it will be returned once the 'idle line' | ||
| 39 | /// condition is detected. | ||
| 40 | /// | ||
| 41 | /// - If the sender sends continuously, the call will wait until | ||
| 42 | /// the DMA controller indicates that it has written to either the | ||
| 43 | /// middle byte or last byte of the ring buffer ('half transfer' | ||
| 44 | /// or 'transfer complete', respectively). This does not indicate | ||
| 45 | /// the buffer is half-full or full, though, because the DMA | ||
| 46 | /// controller does not detect those conditions; it sends an | ||
| 47 | /// interrupt when those specific buffer addresses have been | ||
| 48 | /// written. | ||
| 49 | /// | ||
| 50 | /// In both cases this will result in variable latency due to the | ||
| 51 | /// buffering effect. For example, if the baudrate is 2400 bps, and | ||
| 52 | /// the configuration is 8 data bits, no parity bit, and one stop bit, | ||
| 53 | /// then a byte will be received every ~4.16ms. If the ring buffer is | ||
| 54 | /// 32 bytes, then a 'wait for bytes' delay may have to wait for 16 | ||
| 55 | /// bytes in the worst case, resulting in a delay (latency) of | ||
| 56 | /// ~62.46ms for the first byte in the ring buffer. If the sender | ||
| 57 | /// sends only 6 bytes and then stops, but the buffer was empty when | ||
| 58 | /// the read function was called, then those bytes may not be returned | ||
| 59 | /// until ~24.96ms after the first byte was received (time for 5 | ||
| 60 | /// additional bytes plus the 'idle frame' which triggers the 'idle | ||
| 61 | /// line' condition). | ||
| 62 | /// | ||
| 63 | /// Applications subject to this latency must be careful if they | ||
| 64 | /// also apply timeouts during reception, as it may appear (to | ||
| 65 | /// them) that the sender has stopped sending when it did not. In | ||
| 66 | /// the example above, a 50ms timeout (12 bytes at 2400bps) might | ||
| 67 | /// seem to be reasonable to detect that the sender has stopped | ||
| 68 | /// sending, but would be falsely triggered in the worst-case | ||
| 69 | /// buffer delay scenario. | ||
| 70 | /// | ||
| 71 | /// Note: This latency is caused by the limited capabilities of the | ||
| 72 | /// STM32 DMA controller; since it cannot generate an interrupt when | ||
| 73 | /// it stores a byte into an empty ring buffer, or in any other | ||
| 74 | /// configurable conditions, it is not possible to take notice of the | ||
| 75 | /// contents of the ring buffer more quickly without introducing | ||
| 76 | /// polling. As a result the latency can be reduced by calling the | ||
| 77 | /// read functions repeatedly with smaller buffers to receive the | ||
| 78 | /// available bytes, as each call to a read function will explicitly | ||
| 79 | /// check the ring buffer for available bytes. | ||
| 20 | pub struct RingBufferedUartRx<'d> { | 80 | pub struct RingBufferedUartRx<'d> { |
| 21 | info: &'static Info, | 81 | info: &'static Info, |
| 22 | state: &'static State, | 82 | state: &'static State, |
| 23 | kernel_clock: Hertz, | 83 | kernel_clock: Hertz, |
| 24 | rx: Option<PeripheralRef<'d, AnyPin>>, | 84 | rx: Option<Peri<'d, AnyPin>>, |
| 25 | rts: Option<PeripheralRef<'d, AnyPin>>, | 85 | rts: Option<Peri<'d, AnyPin>>, |
| 26 | ring_buf: ReadableRingBuffer<'d, u8>, | 86 | ring_buf: ReadableRingBuffer<'d, u8>, |
| 27 | } | 87 | } |
| 28 | 88 | ||
| @@ -71,33 +131,18 @@ impl<'d> UartRx<'d, Async> { | |||
| 71 | } | 131 | } |
| 72 | 132 | ||
| 73 | impl<'d> RingBufferedUartRx<'d> { | 133 | impl<'d> RingBufferedUartRx<'d> { |
| 74 | /// Clear the ring buffer and start receiving in the background | ||
| 75 | pub fn start(&mut self) -> Result<(), Error> { | ||
| 76 | // Clear the ring buffer so that it is ready to receive data | ||
| 77 | self.ring_buf.clear(); | ||
| 78 | |||
| 79 | self.setup_uart(); | ||
| 80 | |||
| 81 | Ok(()) | ||
| 82 | } | ||
| 83 | |||
| 84 | fn stop(&mut self, err: Error) -> Result<usize, Error> { | ||
| 85 | self.teardown_uart(); | ||
| 86 | |||
| 87 | Err(err) | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Reconfigure the driver | 134 | /// Reconfigure the driver |
| 91 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { | 135 | pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { |
| 92 | reconfigure(self.info, self.kernel_clock, config) | 136 | reconfigure(self.info, self.kernel_clock, config) |
| 93 | } | 137 | } |
| 94 | 138 | ||
| 95 | /// Start uart background receive | 139 | /// Configure and start the DMA backed UART receiver |
| 96 | fn setup_uart(&mut self) { | 140 | /// |
| 97 | // fence before starting DMA. | 141 | /// Note: This is also done automatically by the read functions if |
| 142 | /// required. | ||
| 143 | pub fn start_uart(&mut self) { | ||
| 144 | // Clear the buffer so that it is ready to receive data | ||
| 98 | compiler_fence(Ordering::SeqCst); | 145 | compiler_fence(Ordering::SeqCst); |
| 99 | |||
| 100 | // start the dma controller | ||
| 101 | self.ring_buf.start(); | 146 | self.ring_buf.start(); |
| 102 | 147 | ||
| 103 | let r = self.info.regs; | 148 | let r = self.info.regs; |
| @@ -118,9 +163,9 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 118 | }); | 163 | }); |
| 119 | } | 164 | } |
| 120 | 165 | ||
| 121 | /// Stop uart background receive | 166 | /// Stop DMA backed UART receiver |
| 122 | fn teardown_uart(&mut self) { | 167 | fn stop_uart(&mut self) { |
| 123 | self.ring_buf.request_stop(); | 168 | self.ring_buf.request_pause(); |
| 124 | 169 | ||
| 125 | let r = self.info.regs; | 170 | let r = self.info.regs; |
| 126 | // clear all interrupts and DMA Rx Request | 171 | // clear all interrupts and DMA Rx Request |
| @@ -142,23 +187,40 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 142 | compiler_fence(Ordering::SeqCst); | 187 | compiler_fence(Ordering::SeqCst); |
| 143 | } | 188 | } |
| 144 | 189 | ||
| 145 | /// Read bytes that are readily available in the ring buffer. | 190 | /// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and |
| 146 | /// If no bytes are currently available in the buffer the call waits until the some | 191 | /// check for errors in status register. Error flags are checked/cleared first. |
| 147 | /// bytes are available (at least one byte and at most half the buffer size) | 192 | fn start_dma_or_check_errors(&mut self) -> Result<(), Error> { |
| 148 | /// | ||
| 149 | /// Background receive is started if `start()` has not been previously called. | ||
| 150 | /// | ||
| 151 | /// Receive in the background is terminated if an error is returned. | ||
| 152 | /// It must then manually be started again by calling `start()` or by re-calling `read()`. | ||
| 153 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { | ||
| 154 | let r = self.info.regs; | 193 | let r = self.info.regs; |
| 155 | 194 | ||
| 156 | // Start background receive if it was not already started | 195 | check_idle_and_errors(r)?; |
| 157 | if !r.cr3().read().dmar() { | 196 | if !r.cr3().read().dmar() { |
| 158 | self.start()?; | 197 | self.start_uart(); |
| 159 | } | 198 | } |
| 199 | Ok(()) | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Read bytes that are available in the ring buffer, or wait for | ||
| 203 | /// bytes to become available and return them. | ||
| 204 | /// | ||
| 205 | /// Background reception is started if necessary (if `start_uart()` had | ||
| 206 | /// not previously been called, or if an error was detected which | ||
| 207 | /// caused background reception to be stopped). | ||
| 208 | /// | ||
| 209 | /// Background reception is terminated when an error is returned. | ||
| 210 | /// It must be started again by calling `start_uart()` or by | ||
| 211 | /// calling a read function again. | ||
| 212 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { | ||
| 213 | self.start_dma_or_check_errors()?; | ||
| 160 | 214 | ||
| 161 | check_for_errors(clear_idle_flag(r))?; | 215 | // In half-duplex mode, we need to disable the Transmitter and enable the Receiver |
| 216 | // since they can't operate simultaneously on the shared line | ||
| 217 | let r = self.info.regs; | ||
| 218 | if r.cr3().read().hdsel() && r.cr1().read().te() { | ||
| 219 | r.cr1().modify(|reg| { | ||
| 220 | reg.set_re(true); | ||
| 221 | reg.set_te(false); | ||
| 222 | }); | ||
| 223 | } | ||
| 162 | 224 | ||
| 163 | loop { | 225 | loop { |
| 164 | match self.ring_buf.read(buf) { | 226 | match self.ring_buf.read(buf) { |
| @@ -167,14 +229,16 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 167 | return Ok(len); | 229 | return Ok(len); |
| 168 | } | 230 | } |
| 169 | Err(_) => { | 231 | Err(_) => { |
| 170 | return self.stop(Error::Overrun); | 232 | self.stop_uart(); |
| 233 | return Err(Error::Overrun); | ||
| 171 | } | 234 | } |
| 172 | } | 235 | } |
| 173 | 236 | ||
| 174 | match self.wait_for_data_or_idle().await { | 237 | match self.wait_for_data_or_idle().await { |
| 175 | Ok(_) => {} | 238 | Ok(_) => {} |
| 176 | Err(err) => { | 239 | Err(err) => { |
| 177 | return self.stop(err); | 240 | self.stop_uart(); |
| 241 | return Err(err); | ||
| 178 | } | 242 | } |
| 179 | } | 243 | } |
| 180 | } | 244 | } |
| @@ -184,20 +248,6 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 184 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { | 248 | async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { |
| 185 | compiler_fence(Ordering::SeqCst); | 249 | compiler_fence(Ordering::SeqCst); |
| 186 | 250 | ||
| 187 | let mut dma_init = false; | ||
| 188 | // Future which completes when there is dma is half full or full | ||
| 189 | let dma = poll_fn(|cx| { | ||
| 190 | self.ring_buf.set_waker(cx.waker()); | ||
| 191 | |||
| 192 | let status = match dma_init { | ||
| 193 | false => Poll::Pending, | ||
| 194 | true => Poll::Ready(()), | ||
| 195 | }; | ||
| 196 | |||
| 197 | dma_init = true; | ||
| 198 | status | ||
| 199 | }); | ||
| 200 | |||
| 201 | // Future which completes when idle line is detected | 251 | // Future which completes when idle line is detected |
| 202 | let s = self.state; | 252 | let s = self.state; |
| 203 | let uart = poll_fn(|cx| { | 253 | let uart = poll_fn(|cx| { |
| @@ -205,13 +255,7 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 205 | 255 | ||
| 206 | compiler_fence(Ordering::SeqCst); | 256 | compiler_fence(Ordering::SeqCst); |
| 207 | 257 | ||
| 208 | // Critical section is needed so that IDLE isn't set after | 258 | if check_idle_and_errors(self.info.regs)? { |
| 209 | // our read but before we clear it. | ||
| 210 | let sr = critical_section::with(|_| clear_idle_flag(self.info.regs)); | ||
| 211 | |||
| 212 | check_for_errors(sr)?; | ||
| 213 | |||
| 214 | if sr.idle() { | ||
| 215 | // Idle line is detected | 259 | // Idle line is detected |
| 216 | Poll::Ready(Ok(())) | 260 | Poll::Ready(Ok(())) |
| 217 | } else { | 261 | } else { |
| @@ -219,58 +263,131 @@ impl<'d> RingBufferedUartRx<'d> { | |||
| 219 | } | 263 | } |
| 220 | }); | 264 | }); |
| 221 | 265 | ||
| 222 | match select(dma, uart).await { | 266 | let mut dma_init = false; |
| 223 | Either::Left(((), _)) => Ok(()), | 267 | // Future which completes when the DMA controller indicates it |
| 224 | Either::Right((result, _)) => result, | 268 | // has written to the ring buffer's middle byte, or last byte |
| 269 | let dma = poll_fn(|cx| { | ||
| 270 | self.ring_buf.set_waker(cx.waker()); | ||
| 271 | |||
| 272 | let status = match dma_init { | ||
| 273 | false => Poll::Pending, | ||
| 274 | true => Poll::Ready(()), | ||
| 275 | }; | ||
| 276 | |||
| 277 | dma_init = true; | ||
| 278 | status | ||
| 279 | }); | ||
| 280 | |||
| 281 | match select(uart, dma).await { | ||
| 282 | Either::Left((result, _)) => result, | ||
| 283 | Either::Right(((), _)) => Ok(()), | ||
| 225 | } | 284 | } |
| 226 | } | 285 | } |
| 286 | |||
| 287 | /// Set baudrate | ||
| 288 | pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { | ||
| 289 | set_baudrate(self.info, self.kernel_clock, baudrate) | ||
| 290 | } | ||
| 227 | } | 291 | } |
| 228 | 292 | ||
| 229 | impl Drop for RingBufferedUartRx<'_> { | 293 | impl Drop for RingBufferedUartRx<'_> { |
| 230 | fn drop(&mut self) { | 294 | fn drop(&mut self) { |
| 231 | self.teardown_uart(); | 295 | self.stop_uart(); |
| 232 | self.rx.as_ref().map(|x| x.set_as_disconnected()); | 296 | self.rx.as_ref().map(|x| x.set_as_disconnected()); |
| 233 | self.rts.as_ref().map(|x| x.set_as_disconnected()); | 297 | self.rts.as_ref().map(|x| x.set_as_disconnected()); |
| 234 | super::drop_tx_rx(self.info, self.state); | 298 | super::drop_tx_rx(self.info, self.state); |
| 235 | } | 299 | } |
| 236 | } | 300 | } |
| 237 | 301 | ||
| 238 | /// Return an error result if the Sr register has errors | 302 | /// Check and clear idle and error interrupts, return true if idle, Err(e) on error |
| 239 | fn check_for_errors(s: Sr) -> Result<(), Error> { | 303 | /// |
| 240 | if s.pe() { | 304 | /// All flags are read and cleared in a single step, respectively. When more than one flag is set |
| 305 | /// at the same time, all flags will be cleared but only one flag will be reported. So the other | ||
| 306 | /// flag(s) will gone missing unnoticed. The error flags are checked first, the idle flag last. | ||
| 307 | /// | ||
| 308 | /// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags | ||
| 309 | /// are cleared by a single read to the RDR register. | ||
| 310 | fn check_idle_and_errors(r: Regs) -> Result<bool, Error> { | ||
| 311 | // Critical section is required so that the flags aren't set after read and before clear | ||
| 312 | let sr = critical_section::with(|_| { | ||
| 313 | // SAFETY: read only and we only use Rx related flags | ||
| 314 | let sr = sr(r).read(); | ||
| 315 | |||
| 316 | #[cfg(any(usart_v3, usart_v4))] | ||
| 317 | r.icr().write(|w| { | ||
| 318 | w.set_idle(true); | ||
| 319 | w.set_pe(true); | ||
| 320 | w.set_fe(true); | ||
| 321 | w.set_ne(true); | ||
| 322 | w.set_ore(true); | ||
| 323 | }); | ||
| 324 | #[cfg(not(any(usart_v3, usart_v4)))] | ||
| 325 | unsafe { | ||
| 326 | // This read also clears the error and idle interrupt flags on v1 (TODO and v2?) | ||
| 327 | rdr(r).read_volatile() | ||
| 328 | }; | ||
| 329 | sr | ||
| 330 | }); | ||
| 331 | if sr.pe() { | ||
| 241 | Err(Error::Parity) | 332 | Err(Error::Parity) |
| 242 | } else if s.fe() { | 333 | } else if sr.fe() { |
| 243 | Err(Error::Framing) | 334 | Err(Error::Framing) |
| 244 | } else if s.ne() { | 335 | } else if sr.ne() { |
| 245 | Err(Error::Noise) | 336 | Err(Error::Noise) |
| 246 | } else if s.ore() { | 337 | } else if sr.ore() { |
| 247 | Err(Error::Overrun) | 338 | Err(Error::Overrun) |
| 248 | } else { | 339 | } else { |
| 249 | Ok(()) | 340 | r.cr1().modify(|w| w.set_idleie(true)); |
| 341 | Ok(sr.idle()) | ||
| 250 | } | 342 | } |
| 251 | } | 343 | } |
| 252 | 344 | ||
| 253 | /// Clear IDLE and return the Sr register | 345 | impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { |
| 254 | fn clear_idle_flag(r: Regs) -> Sr { | 346 | type Error = Error; |
| 255 | // SAFETY: read only and we only use Rx related flags | 347 | } |
| 256 | |||
| 257 | let sr = sr(r).read(); | ||
| 258 | 348 | ||
| 259 | // This read also clears the error and idle interrupt flags on v1. | 349 | impl embedded_io_async::Read for RingBufferedUartRx<'_> { |
| 260 | unsafe { rdr(r).read_volatile() }; | 350 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { |
| 261 | clear_interrupt_flags(r, sr); | 351 | self.read(buf).await |
| 352 | } | ||
| 353 | } | ||
| 262 | 354 | ||
| 263 | r.cr1().modify(|w| w.set_idleie(true)); | 355 | impl embedded_hal_nb::serial::Read for RingBufferedUartRx<'_> { |
| 356 | fn read(&mut self) -> nb::Result<u8, Self::Error> { | ||
| 357 | self.start_dma_or_check_errors()?; | ||
| 264 | 358 | ||
| 265 | sr | 359 | let mut buf = [0u8; 1]; |
| 360 | match self.ring_buf.read(&mut buf) { | ||
| 361 | Ok((0, _)) => Err(nb::Error::WouldBlock), | ||
| 362 | Ok((len, _)) => { | ||
| 363 | assert!(len == 1); | ||
| 364 | Ok(buf[0]) | ||
| 365 | } | ||
| 366 | Err(_) => { | ||
| 367 | self.stop_uart(); | ||
| 368 | Err(nb::Error::Other(Error::Overrun)) | ||
| 369 | } | ||
| 370 | } | ||
| 371 | } | ||
| 266 | } | 372 | } |
| 267 | 373 | ||
| 268 | impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> { | 374 | impl embedded_hal_nb::serial::ErrorType for RingBufferedUartRx<'_> { |
| 269 | type Error = Error; | 375 | type Error = Error; |
| 270 | } | 376 | } |
| 271 | 377 | ||
| 272 | impl embedded_io_async::Read for RingBufferedUartRx<'_> { | 378 | impl ReadReady for RingBufferedUartRx<'_> { |
| 273 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { | 379 | fn read_ready(&mut self) -> Result<bool, Self::Error> { |
| 274 | self.read(buf).await | 380 | let len = self.ring_buf.len().map_err(|e| match e { |
| 381 | crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, | ||
| 382 | crate::dma::ringbuffer::Error::DmaUnsynced => { | ||
| 383 | error!( | ||
| 384 | "Ringbuffer error: DmaUNsynced, driver implementation is | ||
| 385 | probably bugged please open an issue" | ||
| 386 | ); | ||
| 387 | // we report this as overrun since its recoverable in the same way | ||
| 388 | Self::Error::Overrun | ||
| 389 | } | ||
| 390 | })?; | ||
| 391 | Ok(len > 0) | ||
| 275 | } | 392 | } |
| 276 | } | 393 | } |
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index ce9fe0a9b..ae5963420 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs | |||
| @@ -13,9 +13,19 @@ fn common_init<T: Instance>() { | |||
| 13 | // Check the USB clock is enabled and running at exactly 48 MHz. | 13 | // Check the USB clock is enabled and running at exactly 48 MHz. |
| 14 | // frequency() will panic if not enabled | 14 | // frequency() will panic if not enabled |
| 15 | let freq = T::frequency(); | 15 | let freq = T::frequency(); |
| 16 | |||
| 17 | // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally | ||
| 18 | #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))] | ||
| 19 | if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { | ||
| 20 | panic!( | ||
| 21 | "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", | ||
| 22 | freq.0 | ||
| 23 | ) | ||
| 24 | } | ||
| 16 | // Check frequency is within the 0.25% tolerance allowed by the spec. | 25 | // Check frequency is within the 0.25% tolerance allowed by the spec. |
| 17 | // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user | 26 | // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user |
| 18 | // has tight clock restrictions due to something else (like audio). | 27 | // has tight clock restrictions due to something else (like audio). |
| 28 | #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))] | ||
| 19 | if freq.0.abs_diff(48_000_000) > 120_000 { | 29 | if freq.0.abs_diff(48_000_000) > 120_000 { |
| 20 | panic!( | 30 | panic!( |
| 21 | "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", | 31 | "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", |
| @@ -48,6 +58,26 @@ fn common_init<T: Instance>() { | |||
| 48 | while !crate::pac::PWR.cr3().read().usb33rdy() {} | 58 | while !crate::pac::PWR.cr3().read().usb33rdy() {} |
| 49 | } | 59 | } |
| 50 | 60 | ||
| 61 | #[cfg(stm32h7rs)] | ||
| 62 | { | ||
| 63 | // If true, VDD33USB is generated by internal regulator from VDD50USB | ||
| 64 | // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) | ||
| 65 | // TODO: unhardcode | ||
| 66 | let internal_regulator = false; | ||
| 67 | |||
| 68 | // Enable USB power | ||
| 69 | critical_section::with(|_| { | ||
| 70 | crate::pac::PWR.csr2().modify(|w| { | ||
| 71 | w.set_usbregen(internal_regulator); | ||
| 72 | w.set_usb33den(true); | ||
| 73 | w.set_usbhsregen(true); | ||
| 74 | }) | ||
| 75 | }); | ||
| 76 | |||
| 77 | // Wait for USB power to stabilize | ||
| 78 | while !crate::pac::PWR.csr2().read().usb33rdy() {} | ||
| 79 | } | ||
| 80 | |||
| 51 | #[cfg(stm32u5)] | 81 | #[cfg(stm32u5)] |
| 52 | { | 82 | { |
| 53 | // Enable USB power | 83 | // Enable USB power |
| @@ -60,6 +90,16 @@ fn common_init<T: Instance>() { | |||
| 60 | 90 | ||
| 61 | // Wait for USB power to stabilize | 91 | // Wait for USB power to stabilize |
| 62 | while !crate::pac::PWR.svmsr().read().vddusbrdy() {} | 92 | while !crate::pac::PWR.svmsr().read().vddusbrdy() {} |
| 93 | |||
| 94 | // Now set up transceiver power if it's a OTG-HS | ||
| 95 | #[cfg(peri_usb_otg_hs)] | ||
| 96 | { | ||
| 97 | crate::pac::PWR.vosr().modify(|w| { | ||
| 98 | w.set_usbpwren(true); | ||
| 99 | w.set_usbboosten(true); | ||
| 100 | }); | ||
| 101 | while !crate::pac::PWR.vosr().read().usbboostrdy() {} | ||
| 102 | } | ||
| 63 | } | 103 | } |
| 64 | 104 | ||
| 65 | T::Interrupt::unpend(); | 105 | T::Interrupt::unpend(); |
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 8ee8dcc36..590d1a427 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | 2 | ||
| 3 | use embassy_hal_internal::{into_ref, Peripheral}; | 3 | use embassy_hal_internal::PeripheralType; |
| 4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; | 4 | use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; |
| 5 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; | 5 | use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; |
| 6 | use embassy_usb_synopsys_otg::otg_v1::Otg; | 6 | use embassy_usb_synopsys_otg::otg_v1::Otg; |
| @@ -11,9 +11,9 @@ use embassy_usb_synopsys_otg::{ | |||
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | use crate::gpio::{AfType, OutputType, Speed}; | 13 | use crate::gpio::{AfType, OutputType, Speed}; |
| 14 | use crate::interrupt; | ||
| 15 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::rcc::{self, RccPeripheral}; | 15 | use crate::rcc::{self, RccPeripheral}; |
| 16 | use crate::{interrupt, Peri}; | ||
| 17 | 17 | ||
| 18 | const MAX_EP_COUNT: usize = 9; | 18 | const MAX_EP_COUNT: usize = 9; |
| 19 | 19 | ||
| @@ -24,20 +24,15 @@ pub struct InterruptHandler<T: Instance> { | |||
| 24 | 24 | ||
| 25 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | 25 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
| 26 | unsafe fn on_interrupt() { | 26 | unsafe fn on_interrupt() { |
| 27 | trace!("irq"); | ||
| 28 | let r = T::regs(); | 27 | let r = T::regs(); |
| 29 | let state = T::state(); | 28 | let state = T::state(); |
| 30 | 29 | on_interrupt_impl(r, state, T::ENDPOINT_COUNT); | |
| 31 | let setup_late_cnak = quirk_setup_late_cnak(r); | ||
| 32 | |||
| 33 | on_interrupt_impl(r, state, T::ENDPOINT_COUNT, setup_late_cnak); | ||
| 34 | } | 30 | } |
| 35 | } | 31 | } |
| 36 | 32 | ||
| 37 | macro_rules! config_ulpi_pins { | 33 | macro_rules! config_ulpi_pins { |
| 38 | ($($pin:ident),*) => { | 34 | ($($pin:ident),*) => { |
| 39 | into_ref!($($pin),*); | 35 | critical_section::with(|_| { |
| 40 | critical_section::with(|_| { | ||
| 41 | $( | 36 | $( |
| 42 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 37 | $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 43 | )* | 38 | )* |
| @@ -66,15 +61,13 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 66 | /// Must be large enough to fit all OUT endpoint max packet sizes. | 61 | /// Must be large enough to fit all OUT endpoint max packet sizes. |
| 67 | /// Endpoint allocation will fail if it is too small. | 62 | /// Endpoint allocation will fail if it is too small. |
| 68 | pub fn new_fs( | 63 | pub fn new_fs( |
| 69 | _peri: impl Peripheral<P = T> + 'd, | 64 | _peri: Peri<'d, T>, |
| 70 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 65 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 71 | dp: impl Peripheral<P = impl DpPin<T>> + 'd, | 66 | dp: Peri<'d, impl DpPin<T>>, |
| 72 | dm: impl Peripheral<P = impl DmPin<T>> + 'd, | 67 | dm: Peri<'d, impl DmPin<T>>, |
| 73 | ep_out_buffer: &'d mut [u8], | 68 | ep_out_buffer: &'d mut [u8], |
| 74 | config: Config, | 69 | config: Config, |
| 75 | ) -> Self { | 70 | ) -> Self { |
| 76 | into_ref!(dp, dm); | ||
| 77 | |||
| 78 | dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 71 | dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 79 | dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | 72 | dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); |
| 80 | 73 | ||
| @@ -87,7 +80,90 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 87 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, | 80 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, |
| 88 | endpoint_count: T::ENDPOINT_COUNT, | 81 | endpoint_count: T::ENDPOINT_COUNT, |
| 89 | phy_type: PhyType::InternalFullSpeed, | 82 | phy_type: PhyType::InternalFullSpeed, |
| 90 | quirk_setup_late_cnak: quirk_setup_late_cnak(regs), | 83 | calculate_trdt_fn: calculate_trdt::<T>, |
| 84 | }; | ||
| 85 | |||
| 86 | Self { | ||
| 87 | inner: OtgDriver::new(ep_out_buffer, instance, config), | ||
| 88 | phantom: PhantomData, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | /// Initializes USB OTG peripheral with internal High-Speed PHY. | ||
| 93 | /// | ||
| 94 | /// # Arguments | ||
| 95 | /// | ||
| 96 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. | ||
| 97 | /// Must be large enough to fit all OUT endpoint max packet sizes. | ||
| 98 | /// Endpoint allocation will fail if it is too small. | ||
| 99 | pub fn new_hs( | ||
| 100 | _peri: Peri<'d, T>, | ||
| 101 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 102 | _dp: Peri<'d, impl DpPin<T>>, | ||
| 103 | _dm: Peri<'d, impl DmPin<T>>, | ||
| 104 | ep_out_buffer: &'d mut [u8], | ||
| 105 | config: Config, | ||
| 106 | ) -> Self { | ||
| 107 | // For STM32U5 High speed pins need to be left in analog mode | ||
| 108 | #[cfg(not(all(stm32u5, peri_usb_otg_hs)))] | ||
| 109 | { | ||
| 110 | _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 111 | _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 112 | } | ||
| 113 | |||
| 114 | let instance = OtgInstance { | ||
| 115 | regs: T::regs(), | ||
| 116 | state: T::state(), | ||
| 117 | fifo_depth_words: T::FIFO_DEPTH_WORDS, | ||
| 118 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, | ||
| 119 | endpoint_count: T::ENDPOINT_COUNT, | ||
| 120 | phy_type: PhyType::InternalHighSpeed, | ||
| 121 | calculate_trdt_fn: calculate_trdt::<T>, | ||
| 122 | }; | ||
| 123 | |||
| 124 | Self { | ||
| 125 | inner: OtgDriver::new(ep_out_buffer, instance, config), | ||
| 126 | phantom: PhantomData, | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode). | ||
| 131 | /// | ||
| 132 | /// # Arguments | ||
| 133 | /// | ||
| 134 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. | ||
| 135 | /// Must be large enough to fit all OUT endpoint max packet sizes. | ||
| 136 | /// Endpoint allocation will fail if it is too small. | ||
| 137 | pub fn new_fs_ulpi( | ||
| 138 | _peri: Peri<'d, T>, | ||
| 139 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 140 | ulpi_clk: Peri<'d, impl UlpiClkPin<T>>, | ||
| 141 | ulpi_dir: Peri<'d, impl UlpiDirPin<T>>, | ||
| 142 | ulpi_nxt: Peri<'d, impl UlpiNxtPin<T>>, | ||
| 143 | ulpi_stp: Peri<'d, impl UlpiStpPin<T>>, | ||
| 144 | ulpi_d0: Peri<'d, impl UlpiD0Pin<T>>, | ||
| 145 | ulpi_d1: Peri<'d, impl UlpiD1Pin<T>>, | ||
| 146 | ulpi_d2: Peri<'d, impl UlpiD2Pin<T>>, | ||
| 147 | ulpi_d3: Peri<'d, impl UlpiD3Pin<T>>, | ||
| 148 | ulpi_d4: Peri<'d, impl UlpiD4Pin<T>>, | ||
| 149 | ulpi_d5: Peri<'d, impl UlpiD5Pin<T>>, | ||
| 150 | ulpi_d6: Peri<'d, impl UlpiD6Pin<T>>, | ||
| 151 | ulpi_d7: Peri<'d, impl UlpiD7Pin<T>>, | ||
| 152 | ep_out_buffer: &'d mut [u8], | ||
| 153 | config: Config, | ||
| 154 | ) -> Self { | ||
| 155 | config_ulpi_pins!( | ||
| 156 | ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, | ||
| 157 | ulpi_d7 | ||
| 158 | ); | ||
| 159 | |||
| 160 | let instance = OtgInstance { | ||
| 161 | regs: T::regs(), | ||
| 162 | state: T::state(), | ||
| 163 | fifo_depth_words: T::FIFO_DEPTH_WORDS, | ||
| 164 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, | ||
| 165 | endpoint_count: T::ENDPOINT_COUNT, | ||
| 166 | phy_type: PhyType::ExternalFullSpeed, | ||
| 91 | calculate_trdt_fn: calculate_trdt::<T>, | 167 | calculate_trdt_fn: calculate_trdt::<T>, |
| 92 | }; | 168 | }; |
| 93 | 169 | ||
| @@ -105,20 +181,20 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 105 | /// Must be large enough to fit all OUT endpoint max packet sizes. | 181 | /// Must be large enough to fit all OUT endpoint max packet sizes. |
| 106 | /// Endpoint allocation will fail if it is too small. | 182 | /// Endpoint allocation will fail if it is too small. |
| 107 | pub fn new_hs_ulpi( | 183 | pub fn new_hs_ulpi( |
| 108 | _peri: impl Peripheral<P = T> + 'd, | 184 | _peri: Peri<'d, T>, |
| 109 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 185 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 110 | ulpi_clk: impl Peripheral<P = impl UlpiClkPin<T>> + 'd, | 186 | ulpi_clk: Peri<'d, impl UlpiClkPin<T>>, |
| 111 | ulpi_dir: impl Peripheral<P = impl UlpiDirPin<T>> + 'd, | 187 | ulpi_dir: Peri<'d, impl UlpiDirPin<T>>, |
| 112 | ulpi_nxt: impl Peripheral<P = impl UlpiNxtPin<T>> + 'd, | 188 | ulpi_nxt: Peri<'d, impl UlpiNxtPin<T>>, |
| 113 | ulpi_stp: impl Peripheral<P = impl UlpiStpPin<T>> + 'd, | 189 | ulpi_stp: Peri<'d, impl UlpiStpPin<T>>, |
| 114 | ulpi_d0: impl Peripheral<P = impl UlpiD0Pin<T>> + 'd, | 190 | ulpi_d0: Peri<'d, impl UlpiD0Pin<T>>, |
| 115 | ulpi_d1: impl Peripheral<P = impl UlpiD1Pin<T>> + 'd, | 191 | ulpi_d1: Peri<'d, impl UlpiD1Pin<T>>, |
| 116 | ulpi_d2: impl Peripheral<P = impl UlpiD2Pin<T>> + 'd, | 192 | ulpi_d2: Peri<'d, impl UlpiD2Pin<T>>, |
| 117 | ulpi_d3: impl Peripheral<P = impl UlpiD3Pin<T>> + 'd, | 193 | ulpi_d3: Peri<'d, impl UlpiD3Pin<T>>, |
| 118 | ulpi_d4: impl Peripheral<P = impl UlpiD4Pin<T>> + 'd, | 194 | ulpi_d4: Peri<'d, impl UlpiD4Pin<T>>, |
| 119 | ulpi_d5: impl Peripheral<P = impl UlpiD5Pin<T>> + 'd, | 195 | ulpi_d5: Peri<'d, impl UlpiD5Pin<T>>, |
| 120 | ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd, | 196 | ulpi_d6: Peri<'d, impl UlpiD6Pin<T>>, |
| 121 | ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd, | 197 | ulpi_d7: Peri<'d, impl UlpiD7Pin<T>>, |
| 122 | ep_out_buffer: &'d mut [u8], | 198 | ep_out_buffer: &'d mut [u8], |
| 123 | config: Config, | 199 | config: Config, |
| 124 | ) -> Self { | 200 | ) -> Self { |
| @@ -129,8 +205,6 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 129 | ulpi_d7 | 205 | ulpi_d7 |
| 130 | ); | 206 | ); |
| 131 | 207 | ||
| 132 | let regs = T::regs(); | ||
| 133 | |||
| 134 | let instance = OtgInstance { | 208 | let instance = OtgInstance { |
| 135 | regs: T::regs(), | 209 | regs: T::regs(), |
| 136 | state: T::state(), | 210 | state: T::state(), |
| @@ -138,7 +212,6 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 138 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, | 212 | extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, |
| 139 | endpoint_count: T::ENDPOINT_COUNT, | 213 | endpoint_count: T::ENDPOINT_COUNT, |
| 140 | phy_type: PhyType::ExternalHighSpeed, | 214 | phy_type: PhyType::ExternalHighSpeed, |
| 141 | quirk_setup_late_cnak: quirk_setup_late_cnak(regs), | ||
| 142 | calculate_trdt_fn: calculate_trdt::<T>, | 215 | calculate_trdt_fn: calculate_trdt::<T>, |
| 143 | }; | 216 | }; |
| 144 | 217 | ||
| @@ -223,6 +296,33 @@ impl<'d, T: Instance> Bus<'d, T> { | |||
| 223 | } | 296 | } |
| 224 | }); | 297 | }); |
| 225 | 298 | ||
| 299 | #[cfg(stm32h7rs)] | ||
| 300 | critical_section::with(|_| { | ||
| 301 | let rcc = crate::pac::RCC; | ||
| 302 | rcc.ahb1enr().modify(|w| { | ||
| 303 | w.set_usbphycen(true); | ||
| 304 | w.set_usb_otg_hsen(true); | ||
| 305 | }); | ||
| 306 | rcc.ahb1lpenr().modify(|w| { | ||
| 307 | w.set_usbphyclpen(true); | ||
| 308 | w.set_usb_otg_hslpen(true); | ||
| 309 | }); | ||
| 310 | }); | ||
| 311 | |||
| 312 | #[cfg(all(stm32u5, peri_usb_otg_hs))] | ||
| 313 | { | ||
| 314 | crate::pac::SYSCFG.otghsphycr().modify(|w| { | ||
| 315 | w.set_en(true); | ||
| 316 | }); | ||
| 317 | |||
| 318 | critical_section::with(|_| { | ||
| 319 | crate::pac::RCC.ahb2enr1().modify(|w| { | ||
| 320 | w.set_usb_otg_hsen(true); | ||
| 321 | w.set_usb_otg_hs_phyen(true); | ||
| 322 | }); | ||
| 323 | }); | ||
| 324 | } | ||
| 325 | |||
| 226 | let r = T::regs(); | 326 | let r = T::regs(); |
| 227 | let core_id = r.cid().read().0; | 327 | let core_id = r.cid().read().0; |
| 228 | trace!("Core id {:08x}", core_id); | 328 | trace!("Core id {:08x}", core_id); |
| @@ -235,8 +335,9 @@ impl<'d, T: Instance> Bus<'d, T> { | |||
| 235 | 335 | ||
| 236 | // Configuring Vbus sense and SOF output | 336 | // Configuring Vbus sense and SOF output |
| 237 | match core_id { | 337 | match core_id { |
| 238 | 0x0000_1200 | 0x0000_1100 => self.inner.config_v1(), | 338 | 0x0000_1200 | 0x0000_1100 | 0x0000_1000 => self.inner.config_v1(), |
| 239 | 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(), | 339 | 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(), |
| 340 | 0x0000_5000 => self.inner.config_v5(), | ||
| 240 | _ => unimplemented!("Unknown USB core id {:X}", core_id), | 341 | _ => unimplemented!("Unknown USB core id {:X}", core_id), |
| 241 | } | 342 | } |
| 242 | } | 343 | } |
| @@ -306,7 +407,7 @@ trait SealedInstance { | |||
| 306 | 407 | ||
| 307 | /// USB instance trait. | 408 | /// USB instance trait. |
| 308 | #[allow(private_bounds)] | 409 | #[allow(private_bounds)] |
| 309 | pub trait Instance: SealedInstance + RccPeripheral + 'static { | 410 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static { |
| 310 | /// Interrupt for this USB instance. | 411 | /// Interrupt for this USB instance. |
| 311 | type Interrupt: interrupt::typelevel::Interrupt; | 412 | type Interrupt: interrupt::typelevel::Interrupt; |
| 312 | } | 413 | } |
| @@ -448,11 +549,11 @@ foreach_interrupt!( | |||
| 448 | ); | 549 | ); |
| 449 | 550 | ||
| 450 | fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 { | 551 | fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 { |
| 451 | let ahb_freq = T::frequency().0; | 552 | let ahb_freq = T::bus_frequency().0; |
| 452 | match speed { | 553 | match speed { |
| 453 | Dspd::HIGH_SPEED => { | 554 | Dspd::HIGH_SPEED => { |
| 454 | // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) | 555 | // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) |
| 455 | if ahb_freq >= 30_000_000 { | 556 | if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) { |
| 456 | 0x9 | 557 | 0x9 |
| 457 | } else { | 558 | } else { |
| 458 | panic!("AHB frequency is too low") | 559 | panic!("AHB frequency is too low") |
| @@ -477,7 +578,3 @@ fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 { | |||
| 477 | _ => unimplemented!(), | 578 | _ => unimplemented!(), |
| 478 | } | 579 | } |
| 479 | } | 580 | } |
| 480 | |||
| 481 | fn quirk_setup_late_cnak(r: Otg) -> bool { | ||
| 482 | r.cid().read().0 & 0xf000 == 0x1000 | ||
| 483 | } | ||
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 9384c8688..3e8e74a1f 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -5,7 +5,7 @@ use core::marker::PhantomData; | |||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | 5 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 6 | use core::task::Poll; | 6 | use core::task::Poll; |
| 7 | 7 | ||
| 8 | use embassy_hal_internal::into_ref; | 8 | use embassy_hal_internal::PeripheralType; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | use embassy_usb_driver as driver; | 10 | use embassy_usb_driver as driver; |
| 11 | use embassy_usb_driver::{ | 11 | use embassy_usb_driver::{ |
| @@ -16,7 +16,7 @@ use crate::pac::usb::regs; | |||
| 16 | use crate::pac::usb::vals::{EpType, Stat}; | 16 | use crate::pac::usb::vals::{EpType, Stat}; |
| 17 | use crate::pac::USBRAM; | 17 | use crate::pac::USBRAM; |
| 18 | use crate::rcc::RccPeripheral; | 18 | use crate::rcc::RccPeripheral; |
| 19 | use crate::{interrupt, Peripheral}; | 19 | use crate::{interrupt, Peri}; |
| 20 | 20 | ||
| 21 | /// Interrupt handler. | 21 | /// Interrupt handler. |
| 22 | pub struct InterruptHandler<T: Instance> { | 22 | pub struct InterruptHandler<T: Instance> { |
| @@ -80,8 +80,10 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 80 | 80 | ||
| 81 | if istr.ctr() { | 81 | if istr.ctr() { |
| 82 | let index = istr.ep_id() as usize; | 82 | let index = istr.ep_id() as usize; |
| 83 | |||
| 83 | let mut epr = regs.epr(index).read(); | 84 | let mut epr = regs.epr(index).read(); |
| 84 | if epr.ctr_rx() { | 85 | if epr.ctr_rx() { |
| 86 | RX_COMPLETE[index].store(true, Ordering::Relaxed); | ||
| 85 | if index == 0 && epr.setup() { | 87 | if index == 0 && epr.setup() { |
| 86 | EP0_SETUP.store(true, Ordering::Relaxed); | 88 | EP0_SETUP.store(true, Ordering::Relaxed); |
| 87 | } | 89 | } |
| @@ -89,6 +91,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 89 | EP_OUT_WAKERS[index].wake(); | 91 | EP_OUT_WAKERS[index].wake(); |
| 90 | } | 92 | } |
| 91 | if epr.ctr_tx() { | 93 | if epr.ctr_tx() { |
| 94 | TX_PENDING[index].store(false, Ordering::Relaxed); | ||
| 92 | //trace!("EP {} TX", index); | 95 | //trace!("EP {} TX", index); |
| 93 | EP_IN_WAKERS[index].wake(); | 96 | EP_IN_WAKERS[index].wake(); |
| 94 | } | 97 | } |
| @@ -117,11 +120,13 @@ const USBRAM_ALIGN: usize = 2; | |||
| 117 | #[cfg(any(usbram_32_2048, usbram_32_1024))] | 120 | #[cfg(any(usbram_32_2048, usbram_32_1024))] |
| 118 | const USBRAM_ALIGN: usize = 4; | 121 | const USBRAM_ALIGN: usize = 4; |
| 119 | 122 | ||
| 120 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 123 | static BUS_WAKER: AtomicWaker = AtomicWaker::new(); |
| 121 | static BUS_WAKER: AtomicWaker = NEW_AW; | ||
| 122 | static EP0_SETUP: AtomicBool = AtomicBool::new(false); | 124 | static EP0_SETUP: AtomicBool = AtomicBool::new(false); |
| 123 | static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; | 125 | |
| 124 | static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; | 126 | static TX_PENDING: [AtomicBool; EP_COUNT] = [const { AtomicBool::new(false) }; EP_COUNT]; |
| 127 | static RX_COMPLETE: [AtomicBool; EP_COUNT] = [const { AtomicBool::new(false) }; EP_COUNT]; | ||
| 128 | static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT]; | ||
| 129 | static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT]; | ||
| 125 | static IRQ_RESET: AtomicBool = AtomicBool::new(false); | 130 | static IRQ_RESET: AtomicBool = AtomicBool::new(false); |
| 126 | static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); | 131 | static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); |
| 127 | static IRQ_RESUME: AtomicBool = AtomicBool::new(false); | 132 | static IRQ_RESUME: AtomicBool = AtomicBool::new(false); |
| @@ -163,20 +168,37 @@ fn calc_out_len(len: u16) -> (u16, u16) { | |||
| 163 | mod btable { | 168 | mod btable { |
| 164 | use super::*; | 169 | use super::*; |
| 165 | 170 | ||
| 166 | pub(super) fn write_in<T: Instance>(index: usize, addr: u16) { | 171 | pub(super) fn write_in_tx<T: Instance>(index: usize, addr: u16) { |
| 167 | USBRAM.mem(index * 4 + 0).write_value(addr); | 172 | USBRAM.mem(index * 4 + 0).write_value(addr); |
| 168 | } | 173 | } |
| 169 | 174 | ||
| 170 | pub(super) fn write_in_len<T: Instance>(index: usize, _addr: u16, len: u16) { | 175 | pub(super) fn write_in_rx<T: Instance>(index: usize, addr: u16) { |
| 176 | USBRAM.mem(index * 4 + 2).write_value(addr); | ||
| 177 | } | ||
| 178 | |||
| 179 | pub(super) fn write_in_len_rx<T: Instance>(index: usize, _addr: u16, len: u16) { | ||
| 180 | USBRAM.mem(index * 4 + 3).write_value(len); | ||
| 181 | } | ||
| 182 | |||
| 183 | pub(super) fn write_in_len_tx<T: Instance>(index: usize, _addr: u16, len: u16) { | ||
| 171 | USBRAM.mem(index * 4 + 1).write_value(len); | 184 | USBRAM.mem(index * 4 + 1).write_value(len); |
| 172 | } | 185 | } |
| 173 | 186 | ||
| 174 | pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | 187 | pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { |
| 175 | USBRAM.mem(index * 4 + 2).write_value(addr); | 188 | USBRAM.mem(index * 4 + 2).write_value(addr); |
| 176 | USBRAM.mem(index * 4 + 3).write_value(max_len_bits); | 189 | USBRAM.mem(index * 4 + 3).write_value(max_len_bits); |
| 177 | } | 190 | } |
| 178 | 191 | ||
| 179 | pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 { | 192 | pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { |
| 193 | USBRAM.mem(index * 4 + 0).write_value(addr); | ||
| 194 | USBRAM.mem(index * 4 + 1).write_value(max_len_bits); | ||
| 195 | } | ||
| 196 | |||
| 197 | pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 { | ||
| 198 | USBRAM.mem(index * 4 + 1).read() | ||
| 199 | } | ||
| 200 | |||
| 201 | pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 { | ||
| 180 | USBRAM.mem(index * 4 + 3).read() | 202 | USBRAM.mem(index * 4 + 3).read() |
| 181 | } | 203 | } |
| 182 | } | 204 | } |
| @@ -184,19 +206,35 @@ mod btable { | |||
| 184 | mod btable { | 206 | mod btable { |
| 185 | use super::*; | 207 | use super::*; |
| 186 | 208 | ||
| 187 | pub(super) fn write_in<T: Instance>(_index: usize, _addr: u16) {} | 209 | pub(super) fn write_in_len_tx<T: Instance>(index: usize, addr: u16, len: u16) { |
| 188 | 210 | assert_eq!(addr & 0b11, 0); | |
| 189 | pub(super) fn write_in_len<T: Instance>(index: usize, addr: u16, len: u16) { | ||
| 190 | USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); | 211 | USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); |
| 191 | } | 212 | } |
| 192 | 213 | ||
| 193 | pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | 214 | pub(super) fn write_in_len_rx<T: Instance>(index: usize, addr: u16, len: u16) { |
| 215 | assert_eq!(addr & 0b11, 0); | ||
| 216 | USBRAM | ||
| 217 | .mem(index * 2 + 1) | ||
| 218 | .write_value((addr as u32) | ((len as u32) << 16)); | ||
| 219 | } | ||
| 220 | |||
| 221 | pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | ||
| 222 | USBRAM | ||
| 223 | .mem(index * 2) | ||
| 224 | .write_value((addr as u32) | ((max_len_bits as u32) << 16)); | ||
| 225 | } | ||
| 226 | |||
| 227 | pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | ||
| 194 | USBRAM | 228 | USBRAM |
| 195 | .mem(index * 2 + 1) | 229 | .mem(index * 2 + 1) |
| 196 | .write_value((addr as u32) | ((max_len_bits as u32) << 16)); | 230 | .write_value((addr as u32) | ((max_len_bits as u32) << 16)); |
| 197 | } | 231 | } |
| 198 | 232 | ||
| 199 | pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 { | 233 | pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 { |
| 234 | (USBRAM.mem(index * 2).read() >> 16) as u16 | ||
| 235 | } | ||
| 236 | |||
| 237 | pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 { | ||
| 200 | (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 | 238 | (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 |
| 201 | } | 239 | } |
| 202 | } | 240 | } |
| @@ -249,15 +287,30 @@ pub struct Driver<'d, T: Instance> { | |||
| 249 | } | 287 | } |
| 250 | 288 | ||
| 251 | impl<'d, T: Instance> Driver<'d, T> { | 289 | impl<'d, T: Instance> Driver<'d, T> { |
| 290 | /// Create a new USB driver with start-of-frame (SOF) output. | ||
| 291 | #[cfg(not(stm32l1))] | ||
| 292 | pub fn new_with_sof( | ||
| 293 | _usb: Peri<'d, T>, | ||
| 294 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 295 | dp: Peri<'d, impl DpPin<T>>, | ||
| 296 | dm: Peri<'d, impl DmPin<T>>, | ||
| 297 | sof: Peri<'d, impl SofPin<T>>, | ||
| 298 | ) -> Self { | ||
| 299 | { | ||
| 300 | use crate::gpio::{AfType, OutputType, Speed}; | ||
| 301 | sof.set_as_af(sof.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); | ||
| 302 | } | ||
| 303 | |||
| 304 | Self::new(_usb, _irq, dp, dm) | ||
| 305 | } | ||
| 306 | |||
| 252 | /// Create a new USB driver. | 307 | /// Create a new USB driver. |
| 253 | pub fn new( | 308 | pub fn new( |
| 254 | _usb: impl Peripheral<P = T> + 'd, | 309 | _usb: Peri<'d, T>, |
| 255 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | 310 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
| 256 | dp: impl Peripheral<P = impl DpPin<T>> + 'd, | 311 | dp: Peri<'d, impl DpPin<T>>, |
| 257 | dm: impl Peripheral<P = impl DmPin<T>> + 'd, | 312 | dm: Peri<'d, impl DmPin<T>>, |
| 258 | ) -> Self { | 313 | ) -> Self { |
| 259 | into_ref!(dp, dm); | ||
| 260 | |||
| 261 | super::common_init::<T>(); | 314 | super::common_init::<T>(); |
| 262 | 315 | ||
| 263 | let regs = T::regs(); | 316 | let regs = T::regs(); |
| @@ -267,10 +320,8 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 267 | w.set_fres(true); | 320 | w.set_fres(true); |
| 268 | }); | 321 | }); |
| 269 | 322 | ||
| 270 | #[cfg(feature = "time")] | 323 | // wait t_STARTUP = 1us |
| 271 | embassy_time::block_for(embassy_time::Duration::from_millis(100)); | 324 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000_000); |
| 272 | #[cfg(not(feature = "time"))] | ||
| 273 | cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 10); | ||
| 274 | 325 | ||
| 275 | #[cfg(not(usb_v4))] | 326 | #[cfg(not(usb_v4))] |
| 276 | regs.btable().write(|w| w.set_btable(0)); | 327 | regs.btable().write(|w| w.set_btable(0)); |
| @@ -327,6 +378,14 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 327 | return false; // reserved for control pipe | 378 | return false; // reserved for control pipe |
| 328 | } | 379 | } |
| 329 | let used = ep.used_out || ep.used_in; | 380 | let used = ep.used_out || ep.used_in; |
| 381 | if used && (ep.ep_type == EndpointType::Isochronous) { | ||
| 382 | // Isochronous endpoints are always double-buffered. | ||
| 383 | // Their corresponding endpoint/channel registers are forced to be unidirectional. | ||
| 384 | // Do not reuse this index. | ||
| 385 | // FIXME: Bulk endpoints can be double buffered, but are not in the current implementation. | ||
| 386 | return false; | ||
| 387 | } | ||
| 388 | |||
| 330 | let used_dir = match D::dir() { | 389 | let used_dir = match D::dir() { |
| 331 | Direction::Out => ep.used_out, | 390 | Direction::Out => ep.used_out, |
| 332 | Direction::In => ep.used_in, | 391 | Direction::In => ep.used_in, |
| @@ -350,7 +409,11 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 350 | let addr = self.alloc_ep_mem(len); | 409 | let addr = self.alloc_ep_mem(len); |
| 351 | 410 | ||
| 352 | trace!(" len_bits = {:04x}", len_bits); | 411 | trace!(" len_bits = {:04x}", len_bits); |
| 353 | btable::write_out::<T>(index, addr, len_bits); | 412 | btable::write_out_rx::<T>(index, addr, len_bits); |
| 413 | |||
| 414 | if ep_type == EndpointType::Isochronous { | ||
| 415 | btable::write_out_tx::<T>(index, addr, len_bits); | ||
| 416 | } | ||
| 354 | 417 | ||
| 355 | EndpointBuffer { | 418 | EndpointBuffer { |
| 356 | addr, | 419 | addr, |
| @@ -365,8 +428,24 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 365 | let len = align_len_up(max_packet_size); | 428 | let len = align_len_up(max_packet_size); |
| 366 | let addr = self.alloc_ep_mem(len); | 429 | let addr = self.alloc_ep_mem(len); |
| 367 | 430 | ||
| 368 | // ep_in_len is written when actually TXing packets. | 431 | #[cfg(not(any(usbram_32_2048, usbram_32_1024)))] |
| 369 | btable::write_in::<T>(index, addr); | 432 | { |
| 433 | // ep_in_len is written when actually transmitting packets. | ||
| 434 | btable::write_in_tx::<T>(index, addr); | ||
| 435 | |||
| 436 | if ep_type == EndpointType::Isochronous { | ||
| 437 | btable::write_in_rx::<T>(index, addr); | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | #[cfg(any(usbram_32_2048, usbram_32_1024))] | ||
| 442 | { | ||
| 443 | btable::write_in_len_tx::<T>(index, addr, 0); | ||
| 444 | |||
| 445 | if ep_type == EndpointType::Isochronous { | ||
| 446 | btable::write_in_len_rx::<T>(index, addr, 0); | ||
| 447 | } | ||
| 448 | } | ||
| 370 | 449 | ||
| 371 | EndpointBuffer { | 450 | EndpointBuffer { |
| 372 | addr, | 451 | addr, |
| @@ -587,24 +666,27 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | |||
| 587 | } | 666 | } |
| 588 | 667 | ||
| 589 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { | 668 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { |
| 590 | trace!("set_enabled {:x} {}", ep_addr, enabled); | 669 | trace!("set_enabled {:?} {}", ep_addr, enabled); |
| 591 | // This can race, so do a retry loop. | 670 | // This can race, so do a retry loop. |
| 592 | let reg = T::regs().epr(ep_addr.index() as _); | 671 | let epr = T::regs().epr(ep_addr.index() as _); |
| 593 | trace!("EPR before: {:04x}", reg.read().0); | 672 | trace!("EPR before: {:04x}", epr.read().0); |
| 594 | match ep_addr.direction() { | 673 | match ep_addr.direction() { |
| 595 | Direction::In => { | 674 | Direction::In => { |
| 596 | loop { | 675 | loop { |
| 597 | let want_stat = match enabled { | 676 | let want_stat = match enabled { |
| 598 | false => Stat::DISABLED, | 677 | false => Stat::DISABLED, |
| 599 | true => Stat::NAK, | 678 | true => match epr.read().ep_type() { |
| 679 | EpType::ISO => Stat::VALID, | ||
| 680 | _ => Stat::NAK, | ||
| 681 | }, | ||
| 600 | }; | 682 | }; |
| 601 | let r = reg.read(); | 683 | let r = epr.read(); |
| 602 | if r.stat_tx() == want_stat { | 684 | if r.stat_tx() == want_stat { |
| 603 | break; | 685 | break; |
| 604 | } | 686 | } |
| 605 | let mut w = invariant(r); | 687 | let mut w = invariant(r); |
| 606 | w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits())); | 688 | w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits())); |
| 607 | reg.write_value(w); | 689 | epr.write_value(w); |
| 608 | } | 690 | } |
| 609 | EP_IN_WAKERS[ep_addr.index()].wake(); | 691 | EP_IN_WAKERS[ep_addr.index()].wake(); |
| 610 | } | 692 | } |
| @@ -614,18 +696,18 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | |||
| 614 | false => Stat::DISABLED, | 696 | false => Stat::DISABLED, |
| 615 | true => Stat::VALID, | 697 | true => Stat::VALID, |
| 616 | }; | 698 | }; |
| 617 | let r = reg.read(); | 699 | let r = epr.read(); |
| 618 | if r.stat_rx() == want_stat { | 700 | if r.stat_rx() == want_stat { |
| 619 | break; | 701 | break; |
| 620 | } | 702 | } |
| 621 | let mut w = invariant(r); | 703 | let mut w = invariant(r); |
| 622 | w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits())); | 704 | w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits())); |
| 623 | reg.write_value(w); | 705 | epr.write_value(w); |
| 624 | } | 706 | } |
| 625 | EP_OUT_WAKERS[ep_addr.index()].wake(); | 707 | EP_OUT_WAKERS[ep_addr.index()].wake(); |
| 626 | } | 708 | } |
| 627 | } | 709 | } |
| 628 | trace!("EPR after: {:04x}", reg.read().0); | 710 | trace!("EPR after: {:04x}", epr.read().0); |
| 629 | } | 711 | } |
| 630 | 712 | ||
| 631 | async fn enable(&mut self) {} | 713 | async fn enable(&mut self) {} |
| @@ -656,6 +738,19 @@ impl Dir for Out { | |||
| 656 | } | 738 | } |
| 657 | } | 739 | } |
| 658 | 740 | ||
| 741 | /// Selects the packet buffer. | ||
| 742 | /// | ||
| 743 | /// For double-buffered endpoints, both the `Rx` and `Tx` buffer from a channel are used for the same | ||
| 744 | /// direction of transfer. This is opposed to single-buffered endpoints, where one channel can serve | ||
| 745 | /// two directions at the same time. | ||
| 746 | #[derive(Clone, Copy, Debug)] | ||
| 747 | enum PacketBuffer { | ||
| 748 | /// The RX buffer - must be used for single-buffered OUT endpoints | ||
| 749 | Rx, | ||
| 750 | /// The TX buffer - must be used for single-buffered IN endpoints | ||
| 751 | Tx, | ||
| 752 | } | ||
| 753 | |||
| 659 | /// USB endpoint. | 754 | /// USB endpoint. |
| 660 | pub struct Endpoint<'d, T: Instance, D> { | 755 | pub struct Endpoint<'d, T: Instance, D> { |
| 661 | _phantom: PhantomData<(&'d mut T, D)>, | 756 | _phantom: PhantomData<(&'d mut T, D)>, |
| @@ -664,15 +759,46 @@ pub struct Endpoint<'d, T: Instance, D> { | |||
| 664 | } | 759 | } |
| 665 | 760 | ||
| 666 | impl<'d, T: Instance, D> Endpoint<'d, T, D> { | 761 | impl<'d, T: Instance, D> Endpoint<'d, T, D> { |
| 667 | fn write_data(&mut self, buf: &[u8]) { | 762 | /// Write to a double-buffered endpoint. |
| 763 | /// | ||
| 764 | /// For double-buffered endpoints, the data buffers overlap, but we still need to write to the right counter field. | ||
| 765 | /// The DTOG_TX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in | ||
| 766 | /// which the next transmit packet will be stored, so we need to write the counter of the OTHER buffer, which is | ||
| 767 | /// where the last transmitted packet was stored. | ||
| 768 | fn write_data_double_buffered(&mut self, buf: &[u8], packet_buffer: PacketBuffer) { | ||
| 668 | let index = self.info.addr.index(); | 769 | let index = self.info.addr.index(); |
| 669 | self.buf.write(buf); | 770 | self.buf.write(buf); |
| 670 | btable::write_in_len::<T>(index, self.buf.addr, buf.len() as _); | 771 | |
| 772 | match packet_buffer { | ||
| 773 | PacketBuffer::Rx => btable::write_in_len_rx::<T>(index, self.buf.addr, buf.len() as _), | ||
| 774 | PacketBuffer::Tx => btable::write_in_len_tx::<T>(index, self.buf.addr, buf.len() as _), | ||
| 775 | } | ||
| 671 | } | 776 | } |
| 672 | 777 | ||
| 673 | fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | 778 | /// Write to a single-buffered endpoint. |
| 779 | fn write_data(&mut self, buf: &[u8]) { | ||
| 780 | self.write_data_double_buffered(buf, PacketBuffer::Tx); | ||
| 781 | } | ||
| 782 | |||
| 783 | /// Read from a double-buffered endpoint. | ||
| 784 | /// | ||
| 785 | /// For double-buffered endpoints, the data buffers overlap, but we still need to read from the right counter field. | ||
| 786 | /// The DTOG_RX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in | ||
| 787 | /// which the next received packet will be stored, so we need to read the counter of the OTHER buffer, which is | ||
| 788 | /// where the last received packet was stored. | ||
| 789 | fn read_data_double_buffered( | ||
| 790 | &mut self, | ||
| 791 | buf: &mut [u8], | ||
| 792 | packet_buffer: PacketBuffer, | ||
| 793 | ) -> Result<usize, EndpointError> { | ||
| 674 | let index = self.info.addr.index(); | 794 | let index = self.info.addr.index(); |
| 675 | let rx_len = btable::read_out_len::<T>(index) as usize & 0x3FF; | 795 | |
| 796 | let rx_len = match packet_buffer { | ||
| 797 | PacketBuffer::Rx => btable::read_out_len_rx::<T>(index), | ||
| 798 | PacketBuffer::Tx => btable::read_out_len_tx::<T>(index), | ||
| 799 | } as usize | ||
| 800 | & 0x3FF; | ||
| 801 | |||
| 676 | trace!("READ DONE, rx_len = {}", rx_len); | 802 | trace!("READ DONE, rx_len = {}", rx_len); |
| 677 | if rx_len > buf.len() { | 803 | if rx_len > buf.len() { |
| 678 | return Err(EndpointError::BufferOverflow); | 804 | return Err(EndpointError::BufferOverflow); |
| @@ -680,6 +806,11 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> { | |||
| 680 | self.buf.read(&mut buf[..rx_len]); | 806 | self.buf.read(&mut buf[..rx_len]); |
| 681 | Ok(rx_len) | 807 | Ok(rx_len) |
| 682 | } | 808 | } |
| 809 | |||
| 810 | /// Read from a single-buffered endpoint. | ||
| 811 | fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | ||
| 812 | self.read_data_double_buffered(buf, PacketBuffer::Rx) | ||
| 813 | } | ||
| 683 | } | 814 | } |
| 684 | 815 | ||
| 685 | impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { | 816 | impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { |
| @@ -734,29 +865,65 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | |||
| 734 | EP_OUT_WAKERS[index].register(cx.waker()); | 865 | EP_OUT_WAKERS[index].register(cx.waker()); |
| 735 | let regs = T::regs(); | 866 | let regs = T::regs(); |
| 736 | let stat = regs.epr(index).read().stat_rx(); | 867 | let stat = regs.epr(index).read().stat_rx(); |
| 737 | if matches!(stat, Stat::NAK | Stat::DISABLED) { | 868 | if self.info.ep_type == EndpointType::Isochronous { |
| 738 | Poll::Ready(stat) | 869 | // The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet. |
| 870 | // Therefore, this instead waits until the `CTR` interrupt was triggered. | ||
| 871 | if matches!(stat, Stat::DISABLED) || RX_COMPLETE[index].load(Ordering::Relaxed) { | ||
| 872 | assert!(matches!(stat, Stat::VALID | Stat::DISABLED)); | ||
| 873 | Poll::Ready(stat) | ||
| 874 | } else { | ||
| 875 | Poll::Pending | ||
| 876 | } | ||
| 739 | } else { | 877 | } else { |
| 740 | Poll::Pending | 878 | if matches!(stat, Stat::NAK | Stat::DISABLED) { |
| 879 | Poll::Ready(stat) | ||
| 880 | } else { | ||
| 881 | Poll::Pending | ||
| 882 | } | ||
| 741 | } | 883 | } |
| 742 | }) | 884 | }) |
| 743 | .await; | 885 | .await; |
| 744 | 886 | ||
| 887 | // Errata for STM32H5, 2.20.1: | ||
| 888 | // During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses | ||
| 889 | // have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. | ||
| 890 | // | ||
| 891 | // Workaround: | ||
| 892 | // Software should ensure that a small delay is included before accessing the SRAM contents. This delay should be | ||
| 893 | // 800 ns in Full Speed mode and 6.4 μs in Low Speed mode. | ||
| 894 | #[cfg(stm32h5)] | ||
| 895 | embassy_time::block_for(embassy_time::Duration::from_nanos(800)); | ||
| 896 | |||
| 897 | RX_COMPLETE[index].store(false, Ordering::Relaxed); | ||
| 898 | |||
| 745 | if stat == Stat::DISABLED { | 899 | if stat == Stat::DISABLED { |
| 746 | return Err(EndpointError::Disabled); | 900 | return Err(EndpointError::Disabled); |
| 747 | } | 901 | } |
| 748 | 902 | ||
| 749 | let rx_len = self.read_data(buf)?; | ||
| 750 | |||
| 751 | let regs = T::regs(); | 903 | let regs = T::regs(); |
| 752 | regs.epr(index).write(|w| { | 904 | |
| 753 | w.set_ep_type(convert_type(self.info.ep_type)); | 905 | let rx_len = if self.info.ep_type == EndpointType::Isochronous { |
| 754 | w.set_ea(self.info.addr.index() as _); | 906 | // Find the buffer, which is currently in use. Read from the OTHER buffer. |
| 755 | w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | 907 | let packet_buffer = if regs.epr(index).read().dtog_rx() { |
| 756 | w.set_stat_tx(Stat::from_bits(0)); | 908 | PacketBuffer::Tx |
| 757 | w.set_ctr_rx(true); // don't clear | 909 | } else { |
| 758 | w.set_ctr_tx(true); // don't clear | 910 | PacketBuffer::Rx |
| 759 | }); | 911 | }; |
| 912 | self.read_data_double_buffered(buf, packet_buffer)? | ||
| 913 | } else { | ||
| 914 | let len = self.read_data(buf)?; | ||
| 915 | |||
| 916 | regs.epr(index).write(|w| { | ||
| 917 | w.set_ep_type(convert_type(self.info.ep_type)); | ||
| 918 | w.set_ea(self.info.addr.index() as _); | ||
| 919 | w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | ||
| 920 | w.set_stat_tx(Stat::from_bits(0)); | ||
| 921 | w.set_ctr_rx(true); // don't clear | ||
| 922 | w.set_ctr_tx(true); // don't clear | ||
| 923 | }); | ||
| 924 | |||
| 925 | len | ||
| 926 | }; | ||
| 760 | trace!("READ OK, rx_len = {}", rx_len); | 927 | trace!("READ OK, rx_len = {}", rx_len); |
| 761 | 928 | ||
| 762 | Ok(rx_len) | 929 | Ok(rx_len) |
| @@ -768,18 +935,41 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 768 | if buf.len() > self.info.max_packet_size as usize { | 935 | if buf.len() > self.info.max_packet_size as usize { |
| 769 | return Err(EndpointError::BufferOverflow); | 936 | return Err(EndpointError::BufferOverflow); |
| 770 | } | 937 | } |
| 938 | trace!("WRITE WAITING, buf.len() = {}", buf.len()); | ||
| 771 | 939 | ||
| 940 | let regs = T::regs(); | ||
| 772 | let index = self.info.addr.index(); | 941 | let index = self.info.addr.index(); |
| 773 | 942 | ||
| 774 | trace!("WRITE WAITING"); | 943 | if self.info.ep_type == EndpointType::Isochronous { |
| 944 | // Find the buffer, which is currently in use. Write to the OTHER buffer. | ||
| 945 | let packet_buffer = if regs.epr(index).read().dtog_tx() { | ||
| 946 | PacketBuffer::Rx | ||
| 947 | } else { | ||
| 948 | PacketBuffer::Tx | ||
| 949 | }; | ||
| 950 | |||
| 951 | self.write_data_double_buffered(buf, packet_buffer); | ||
| 952 | } | ||
| 953 | |||
| 775 | let stat = poll_fn(|cx| { | 954 | let stat = poll_fn(|cx| { |
| 776 | EP_IN_WAKERS[index].register(cx.waker()); | 955 | EP_IN_WAKERS[index].register(cx.waker()); |
| 777 | let regs = T::regs(); | 956 | let regs = T::regs(); |
| 778 | let stat = regs.epr(index).read().stat_tx(); | 957 | let stat = regs.epr(index).read().stat_tx(); |
| 779 | if matches!(stat, Stat::NAK | Stat::DISABLED) { | 958 | if self.info.ep_type == EndpointType::Isochronous { |
| 780 | Poll::Ready(stat) | 959 | // The isochronous endpoint does not change its `STAT_TX` field to `NAK` after sending a packet. |
| 960 | // Therefore, this instead waits until the `CTR` interrupt was triggered. | ||
| 961 | if matches!(stat, Stat::DISABLED) || !TX_PENDING[index].load(Ordering::Relaxed) { | ||
| 962 | assert!(matches!(stat, Stat::VALID | Stat::DISABLED)); | ||
| 963 | Poll::Ready(stat) | ||
| 964 | } else { | ||
| 965 | Poll::Pending | ||
| 966 | } | ||
| 781 | } else { | 967 | } else { |
| 782 | Poll::Pending | 968 | if matches!(stat, Stat::NAK | Stat::DISABLED) { |
| 969 | Poll::Ready(stat) | ||
| 970 | } else { | ||
| 971 | Poll::Pending | ||
| 972 | } | ||
| 783 | } | 973 | } |
| 784 | }) | 974 | }) |
| 785 | .await; | 975 | .await; |
| @@ -788,18 +978,19 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 788 | return Err(EndpointError::Disabled); | 978 | return Err(EndpointError::Disabled); |
| 789 | } | 979 | } |
| 790 | 980 | ||
| 791 | self.write_data(buf); | 981 | if self.info.ep_type != EndpointType::Isochronous { |
| 792 | 982 | self.write_data(buf); | |
| 793 | let regs = T::regs(); | ||
| 794 | regs.epr(index).write(|w| { | ||
| 795 | w.set_ep_type(convert_type(self.info.ep_type)); | ||
| 796 | w.set_ea(self.info.addr.index() as _); | ||
| 797 | w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | ||
| 798 | w.set_stat_rx(Stat::from_bits(0)); | ||
| 799 | w.set_ctr_rx(true); // don't clear | ||
| 800 | w.set_ctr_tx(true); // don't clear | ||
| 801 | }); | ||
| 802 | 983 | ||
| 984 | regs.epr(index).write(|w| { | ||
| 985 | w.set_ep_type(convert_type(self.info.ep_type)); | ||
| 986 | w.set_ea(self.info.addr.index() as _); | ||
| 987 | w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | ||
| 988 | w.set_stat_rx(Stat::from_bits(0)); | ||
| 989 | w.set_ctr_rx(true); // don't clear | ||
| 990 | w.set_ctr_tx(true); // don't clear | ||
| 991 | }); | ||
| 992 | } | ||
| 993 | TX_PENDING[index].store(true, Ordering::Relaxed); | ||
| 803 | trace!("WRITE OK"); | 994 | trace!("WRITE OK"); |
| 804 | 995 | ||
| 805 | Ok(()) | 996 | Ok(()) |
| @@ -1044,7 +1235,7 @@ trait SealedInstance { | |||
| 1044 | 1235 | ||
| 1045 | /// USB instance trait. | 1236 | /// USB instance trait. |
| 1046 | #[allow(private_bounds)] | 1237 | #[allow(private_bounds)] |
| 1047 | pub trait Instance: SealedInstance + RccPeripheral + 'static { | 1238 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + 'static { |
| 1048 | /// Interrupt for this USB instance. | 1239 | /// Interrupt for this USB instance. |
| 1049 | type Interrupt: interrupt::typelevel::Interrupt; | 1240 | type Interrupt: interrupt::typelevel::Interrupt; |
| 1050 | } | 1241 | } |
| @@ -1052,6 +1243,7 @@ pub trait Instance: SealedInstance + RccPeripheral + 'static { | |||
| 1052 | // Internal PHY pins | 1243 | // Internal PHY pins |
| 1053 | pin_trait!(DpPin, Instance); | 1244 | pin_trait!(DpPin, Instance); |
| 1054 | pin_trait!(DmPin, Instance); | 1245 | pin_trait!(DmPin, Instance); |
| 1246 | pin_trait!(SofPin, Instance); | ||
| 1055 | 1247 | ||
| 1056 | foreach_interrupt!( | 1248 | foreach_interrupt!( |
| 1057 | ($inst:ident, usb, $block:ident, LP, $irq:ident) => { | 1249 | ($inst:ident, usb, $block:ident, LP, $irq:ident) => { |
diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index ab21c4b6b..fb5c3d930 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | //! Watchdog Timer (IWDG, WWDG) | 1 | //! Watchdog Timer (IWDG, WWDG) |
| 2 | use core::marker::PhantomData; | 2 | use core::marker::PhantomData; |
| 3 | 3 | ||
| 4 | use embassy_hal_internal::{into_ref, Peripheral}; | 4 | use embassy_hal_internal::PeripheralType; |
| 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; | 5 | use stm32_metapac::iwdg::vals::{Key, Pr}; |
| 6 | 6 | ||
| 7 | use crate::rcc::LSI_FREQ; | 7 | use crate::rcc::LSI_FREQ; |
| 8 | use crate::Peri; | ||
| 8 | 9 | ||
| 9 | /// Independent watchdog (IWDG) driver. | 10 | /// Independent watchdog (IWDG) driver. |
| 10 | pub struct IndependentWatchdog<'d, T: Instance> { | 11 | pub struct IndependentWatchdog<'d, T: Instance> { |
| @@ -29,9 +30,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { | |||
| 29 | /// | 30 | /// |
| 30 | /// [Self] has to be started with [Self::unleash()]. | 31 | /// [Self] has to be started with [Self::unleash()]. |
| 31 | /// Once timer expires, MCU will be reset. To prevent this, timer must be reloaded by repeatedly calling [Self::pet()] within timeout interval. | 32 | /// Once timer expires, MCU will be reset. To prevent this, timer must be reloaded by repeatedly calling [Self::pet()] within timeout interval. |
| 32 | pub fn new(_instance: impl Peripheral<P = T> + 'd, timeout_us: u32) -> Self { | 33 | pub fn new(_instance: Peri<'d, T>, timeout_us: u32) -> Self { |
| 33 | into_ref!(_instance); | ||
| 34 | |||
| 35 | // Find lowest prescaler value, which makes watchdog period longer or equal to timeout. | 34 | // Find lowest prescaler value, which makes watchdog period longer or equal to timeout. |
| 36 | // This iterates from 4 (2^2) to 256 (2^8). | 35 | // This iterates from 4 (2^2) to 256 (2^8). |
| 37 | let psc_power = unwrap!((2..=8).find(|psc_power| { | 36 | let psc_power = unwrap!((2..=8).find(|psc_power| { |
| @@ -86,7 +85,7 @@ trait SealedInstance { | |||
| 86 | 85 | ||
| 87 | /// IWDG instance trait. | 86 | /// IWDG instance trait. |
| 88 | #[allow(private_bounds)] | 87 | #[allow(private_bounds)] |
| 89 | pub trait Instance: SealedInstance {} | 88 | pub trait Instance: SealedInstance + PeripheralType {} |
| 90 | 89 | ||
| 91 | foreach_peripheral!( | 90 | foreach_peripheral!( |
| 92 | (iwdg, $inst:ident) => { | 91 | (iwdg, $inst:ident) => { |
diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs new file mode 100644 index 000000000..c96641180 --- /dev/null +++ b/embassy-stm32/src/xspi/enums.rs | |||
| @@ -0,0 +1,364 @@ | |||
| 1 | //! Enums used in Xspi configuration. | ||
| 2 | #[derive(Copy, Clone)] | ||
| 3 | pub(crate) enum XspiMode { | ||
| 4 | IndirectWrite, | ||
| 5 | IndirectRead, | ||
| 6 | #[expect(dead_code)] | ||
| 7 | AutoPolling, | ||
| 8 | #[expect(dead_code)] | ||
| 9 | MemoryMapped, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl Into<u8> for XspiMode { | ||
| 13 | fn into(self) -> u8 { | ||
| 14 | match self { | ||
| 15 | XspiMode::IndirectWrite => 0b00, | ||
| 16 | XspiMode::IndirectRead => 0b01, | ||
| 17 | XspiMode::AutoPolling => 0b10, | ||
| 18 | XspiMode::MemoryMapped => 0b11, | ||
| 19 | } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Xspi lane width | ||
| 24 | #[derive(Copy, Clone)] | ||
| 25 | pub enum XspiWidth { | ||
| 26 | /// None | ||
| 27 | NONE, | ||
| 28 | /// Single lane | ||
| 29 | SING, | ||
| 30 | /// Dual lanes | ||
| 31 | DUAL, | ||
| 32 | /// Quad lanes | ||
| 33 | QUAD, | ||
| 34 | /// Eight lanes | ||
| 35 | OCTO, | ||
| 36 | } | ||
| 37 | |||
| 38 | impl Into<u8> for XspiWidth { | ||
| 39 | fn into(self) -> u8 { | ||
| 40 | match self { | ||
| 41 | XspiWidth::NONE => 0b00, | ||
| 42 | XspiWidth::SING => 0b01, | ||
| 43 | XspiWidth::DUAL => 0b10, | ||
| 44 | XspiWidth::QUAD => 0b11, | ||
| 45 | XspiWidth::OCTO => 0b100, | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Wrap Size | ||
| 51 | #[allow(missing_docs)] | ||
| 52 | #[derive(Copy, Clone)] | ||
| 53 | pub enum WrapSize { | ||
| 54 | None, | ||
| 55 | _16Bytes, | ||
| 56 | _32Bytes, | ||
| 57 | _64Bytes, | ||
| 58 | _128Bytes, | ||
| 59 | } | ||
| 60 | |||
| 61 | impl Into<u8> for WrapSize { | ||
| 62 | fn into(self) -> u8 { | ||
| 63 | match self { | ||
| 64 | WrapSize::None => 0x00, | ||
| 65 | WrapSize::_16Bytes => 0x02, | ||
| 66 | WrapSize::_32Bytes => 0x03, | ||
| 67 | WrapSize::_64Bytes => 0x04, | ||
| 68 | WrapSize::_128Bytes => 0x05, | ||
| 69 | } | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | /// Memory Type | ||
| 74 | #[allow(missing_docs)] | ||
| 75 | #[derive(Copy, Clone)] | ||
| 76 | pub enum MemoryType { | ||
| 77 | Micron, | ||
| 78 | Macronix, | ||
| 79 | Standard, | ||
| 80 | MacronixRam, | ||
| 81 | HyperBusMemory, | ||
| 82 | HyperBusRegister, | ||
| 83 | } | ||
| 84 | |||
| 85 | impl Into<u8> for MemoryType { | ||
| 86 | fn into(self) -> u8 { | ||
| 87 | match self { | ||
| 88 | MemoryType::Micron => 0x00, | ||
| 89 | MemoryType::Macronix => 0x01, | ||
| 90 | MemoryType::Standard => 0x02, | ||
| 91 | MemoryType::MacronixRam => 0x03, | ||
| 92 | MemoryType::HyperBusMemory => 0x04, | ||
| 93 | MemoryType::HyperBusRegister => 0x04, | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Xspi memory size. | ||
| 99 | #[allow(missing_docs)] | ||
| 100 | #[derive(Copy, Clone)] | ||
| 101 | pub enum MemorySize { | ||
| 102 | _1KiB, | ||
| 103 | _2KiB, | ||
| 104 | _4KiB, | ||
| 105 | _8KiB, | ||
| 106 | _16KiB, | ||
| 107 | _32KiB, | ||
| 108 | _64KiB, | ||
| 109 | _128KiB, | ||
| 110 | _256KiB, | ||
| 111 | _512KiB, | ||
| 112 | _1MiB, | ||
| 113 | _2MiB, | ||
| 114 | _4MiB, | ||
| 115 | _8MiB, | ||
| 116 | _16MiB, | ||
| 117 | _32MiB, | ||
| 118 | _64MiB, | ||
| 119 | _128MiB, | ||
| 120 | _256MiB, | ||
| 121 | _512MiB, | ||
| 122 | _1GiB, | ||
| 123 | _2GiB, | ||
| 124 | _4GiB, | ||
| 125 | Other(u8), | ||
| 126 | } | ||
| 127 | |||
| 128 | impl Into<u8> for MemorySize { | ||
| 129 | fn into(self) -> u8 { | ||
| 130 | match self { | ||
| 131 | MemorySize::_1KiB => 9, | ||
| 132 | MemorySize::_2KiB => 10, | ||
| 133 | MemorySize::_4KiB => 11, | ||
| 134 | MemorySize::_8KiB => 12, | ||
| 135 | MemorySize::_16KiB => 13, | ||
| 136 | MemorySize::_32KiB => 14, | ||
| 137 | MemorySize::_64KiB => 15, | ||
| 138 | MemorySize::_128KiB => 16, | ||
| 139 | MemorySize::_256KiB => 17, | ||
| 140 | MemorySize::_512KiB => 18, | ||
| 141 | MemorySize::_1MiB => 19, | ||
| 142 | MemorySize::_2MiB => 20, | ||
| 143 | MemorySize::_4MiB => 21, | ||
| 144 | MemorySize::_8MiB => 22, | ||
| 145 | MemorySize::_16MiB => 23, | ||
| 146 | MemorySize::_32MiB => 24, | ||
| 147 | MemorySize::_64MiB => 25, | ||
| 148 | MemorySize::_128MiB => 26, | ||
| 149 | MemorySize::_256MiB => 27, | ||
| 150 | MemorySize::_512MiB => 28, | ||
| 151 | MemorySize::_1GiB => 29, | ||
| 152 | MemorySize::_2GiB => 30, | ||
| 153 | MemorySize::_4GiB => 31, | ||
| 154 | MemorySize::Other(val) => val, | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /// Xspi Address size | ||
| 160 | #[derive(Copy, Clone)] | ||
| 161 | pub enum AddressSize { | ||
| 162 | /// 8-bit address | ||
| 163 | _8bit, | ||
| 164 | /// 16-bit address | ||
| 165 | _16bit, | ||
| 166 | /// 24-bit address | ||
| 167 | _24bit, | ||
| 168 | /// 32-bit address | ||
| 169 | _32bit, | ||
| 170 | } | ||
| 171 | |||
| 172 | impl Into<u8> for AddressSize { | ||
| 173 | fn into(self) -> u8 { | ||
| 174 | match self { | ||
| 175 | AddressSize::_8bit => 0b00, | ||
| 176 | AddressSize::_16bit => 0b01, | ||
| 177 | AddressSize::_24bit => 0b10, | ||
| 178 | AddressSize::_32bit => 0b11, | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Time the Chip Select line stays high. | ||
| 184 | #[allow(missing_docs)] | ||
| 185 | #[derive(Copy, Clone)] | ||
| 186 | pub enum ChipSelectHighTime { | ||
| 187 | _1Cycle, | ||
| 188 | _2Cycle, | ||
| 189 | _3Cycle, | ||
| 190 | _4Cycle, | ||
| 191 | _5Cycle, | ||
| 192 | _6Cycle, | ||
| 193 | _7Cycle, | ||
| 194 | _8Cycle, | ||
| 195 | } | ||
| 196 | |||
| 197 | impl Into<u8> for ChipSelectHighTime { | ||
| 198 | fn into(self) -> u8 { | ||
| 199 | match self { | ||
| 200 | ChipSelectHighTime::_1Cycle => 0, | ||
| 201 | ChipSelectHighTime::_2Cycle => 1, | ||
| 202 | ChipSelectHighTime::_3Cycle => 2, | ||
| 203 | ChipSelectHighTime::_4Cycle => 3, | ||
| 204 | ChipSelectHighTime::_5Cycle => 4, | ||
| 205 | ChipSelectHighTime::_6Cycle => 5, | ||
| 206 | ChipSelectHighTime::_7Cycle => 6, | ||
| 207 | ChipSelectHighTime::_8Cycle => 7, | ||
| 208 | } | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | /// FIFO threshold. | ||
| 213 | #[allow(missing_docs)] | ||
| 214 | #[derive(Copy, Clone)] | ||
| 215 | pub enum FIFOThresholdLevel { | ||
| 216 | _1Bytes, | ||
| 217 | _2Bytes, | ||
| 218 | _3Bytes, | ||
| 219 | _4Bytes, | ||
| 220 | _5Bytes, | ||
| 221 | _6Bytes, | ||
| 222 | _7Bytes, | ||
| 223 | _8Bytes, | ||
| 224 | _9Bytes, | ||
| 225 | _10Bytes, | ||
| 226 | _11Bytes, | ||
| 227 | _12Bytes, | ||
| 228 | _13Bytes, | ||
| 229 | _14Bytes, | ||
| 230 | _15Bytes, | ||
| 231 | _16Bytes, | ||
| 232 | _17Bytes, | ||
| 233 | _18Bytes, | ||
| 234 | _19Bytes, | ||
| 235 | _20Bytes, | ||
| 236 | _21Bytes, | ||
| 237 | _22Bytes, | ||
| 238 | _23Bytes, | ||
| 239 | _24Bytes, | ||
| 240 | _25Bytes, | ||
| 241 | _26Bytes, | ||
| 242 | _27Bytes, | ||
| 243 | _28Bytes, | ||
| 244 | _29Bytes, | ||
| 245 | _30Bytes, | ||
| 246 | _31Bytes, | ||
| 247 | _32Bytes, | ||
| 248 | } | ||
| 249 | |||
| 250 | impl Into<u8> for FIFOThresholdLevel { | ||
| 251 | fn into(self) -> u8 { | ||
| 252 | match self { | ||
| 253 | FIFOThresholdLevel::_1Bytes => 0, | ||
| 254 | FIFOThresholdLevel::_2Bytes => 1, | ||
| 255 | FIFOThresholdLevel::_3Bytes => 2, | ||
| 256 | FIFOThresholdLevel::_4Bytes => 3, | ||
| 257 | FIFOThresholdLevel::_5Bytes => 4, | ||
| 258 | FIFOThresholdLevel::_6Bytes => 5, | ||
| 259 | FIFOThresholdLevel::_7Bytes => 6, | ||
| 260 | FIFOThresholdLevel::_8Bytes => 7, | ||
| 261 | FIFOThresholdLevel::_9Bytes => 8, | ||
| 262 | FIFOThresholdLevel::_10Bytes => 9, | ||
| 263 | FIFOThresholdLevel::_11Bytes => 10, | ||
| 264 | FIFOThresholdLevel::_12Bytes => 11, | ||
| 265 | FIFOThresholdLevel::_13Bytes => 12, | ||
| 266 | FIFOThresholdLevel::_14Bytes => 13, | ||
| 267 | FIFOThresholdLevel::_15Bytes => 14, | ||
| 268 | FIFOThresholdLevel::_16Bytes => 15, | ||
| 269 | FIFOThresholdLevel::_17Bytes => 16, | ||
| 270 | FIFOThresholdLevel::_18Bytes => 17, | ||
| 271 | FIFOThresholdLevel::_19Bytes => 18, | ||
| 272 | FIFOThresholdLevel::_20Bytes => 19, | ||
| 273 | FIFOThresholdLevel::_21Bytes => 20, | ||
| 274 | FIFOThresholdLevel::_22Bytes => 21, | ||
| 275 | FIFOThresholdLevel::_23Bytes => 22, | ||
| 276 | FIFOThresholdLevel::_24Bytes => 23, | ||
| 277 | FIFOThresholdLevel::_25Bytes => 24, | ||
| 278 | FIFOThresholdLevel::_26Bytes => 25, | ||
| 279 | FIFOThresholdLevel::_27Bytes => 26, | ||
| 280 | FIFOThresholdLevel::_28Bytes => 27, | ||
| 281 | FIFOThresholdLevel::_29Bytes => 28, | ||
| 282 | FIFOThresholdLevel::_30Bytes => 29, | ||
| 283 | FIFOThresholdLevel::_31Bytes => 30, | ||
| 284 | FIFOThresholdLevel::_32Bytes => 31, | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | /// Dummy cycle count | ||
| 290 | #[allow(missing_docs)] | ||
| 291 | #[derive(Copy, Clone)] | ||
| 292 | pub enum DummyCycles { | ||
| 293 | _0, | ||
| 294 | _1, | ||
| 295 | _2, | ||
| 296 | _3, | ||
| 297 | _4, | ||
| 298 | _5, | ||
| 299 | _6, | ||
| 300 | _7, | ||
| 301 | _8, | ||
| 302 | _9, | ||
| 303 | _10, | ||
| 304 | _11, | ||
| 305 | _12, | ||
| 306 | _13, | ||
| 307 | _14, | ||
| 308 | _15, | ||
| 309 | _16, | ||
| 310 | _17, | ||
| 311 | _18, | ||
| 312 | _19, | ||
| 313 | _20, | ||
| 314 | _21, | ||
| 315 | _22, | ||
| 316 | _23, | ||
| 317 | _24, | ||
| 318 | _25, | ||
| 319 | _26, | ||
| 320 | _27, | ||
| 321 | _28, | ||
| 322 | _29, | ||
| 323 | _30, | ||
| 324 | _31, | ||
| 325 | } | ||
| 326 | |||
| 327 | impl Into<u8> for DummyCycles { | ||
| 328 | fn into(self) -> u8 { | ||
| 329 | match self { | ||
| 330 | DummyCycles::_0 => 0, | ||
| 331 | DummyCycles::_1 => 1, | ||
| 332 | DummyCycles::_2 => 2, | ||
| 333 | DummyCycles::_3 => 3, | ||
| 334 | DummyCycles::_4 => 4, | ||
| 335 | DummyCycles::_5 => 5, | ||
| 336 | DummyCycles::_6 => 6, | ||
| 337 | DummyCycles::_7 => 7, | ||
| 338 | DummyCycles::_8 => 8, | ||
| 339 | DummyCycles::_9 => 9, | ||
| 340 | DummyCycles::_10 => 10, | ||
| 341 | DummyCycles::_11 => 11, | ||
| 342 | DummyCycles::_12 => 12, | ||
| 343 | DummyCycles::_13 => 13, | ||
| 344 | DummyCycles::_14 => 14, | ||
| 345 | DummyCycles::_15 => 15, | ||
| 346 | DummyCycles::_16 => 16, | ||
| 347 | DummyCycles::_17 => 17, | ||
| 348 | DummyCycles::_18 => 18, | ||
| 349 | DummyCycles::_19 => 19, | ||
| 350 | DummyCycles::_20 => 20, | ||
| 351 | DummyCycles::_21 => 21, | ||
| 352 | DummyCycles::_22 => 22, | ||
| 353 | DummyCycles::_23 => 23, | ||
| 354 | DummyCycles::_24 => 24, | ||
| 355 | DummyCycles::_25 => 25, | ||
| 356 | DummyCycles::_26 => 26, | ||
| 357 | DummyCycles::_27 => 27, | ||
| 358 | DummyCycles::_28 => 28, | ||
| 359 | DummyCycles::_29 => 29, | ||
| 360 | DummyCycles::_30 => 30, | ||
| 361 | DummyCycles::_31 => 31, | ||
| 362 | } | ||
| 363 | } | ||
| 364 | } | ||
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs new file mode 100644 index 000000000..44c10b961 --- /dev/null +++ b/embassy-stm32/src/xspi/mod.rs | |||
| @@ -0,0 +1,1425 @@ | |||
| 1 | //! XSPI Serial Peripheral Interface | ||
| 2 | //! | ||
| 3 | |||
| 4 | #![macro_use] | ||
| 5 | |||
| 6 | pub mod enums; | ||
| 7 | |||
| 8 | use core::marker::PhantomData; | ||
| 9 | |||
| 10 | use embassy_embedded_hal::{GetConfig, SetConfig}; | ||
| 11 | use embassy_hal_internal::PeripheralType; | ||
| 12 | pub use enums::*; | ||
| 13 | |||
| 14 | use crate::dma::{word, ChannelAndRequest}; | ||
| 15 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | ||
| 16 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | ||
| 17 | use crate::pac::xspi::vals::*; | ||
| 18 | use crate::pac::xspi::Xspi as Regs; | ||
| 19 | #[cfg(xspim_v1)] | ||
| 20 | use crate::pac::xspim::Xspim; | ||
| 21 | use crate::rcc::{self, RccPeripheral}; | ||
| 22 | use crate::{peripherals, Peri}; | ||
| 23 | |||
| 24 | /// XPSI driver config. | ||
| 25 | #[derive(Clone, Copy)] | ||
| 26 | pub struct Config { | ||
| 27 | /// Fifo threshold used by the peripheral to generate the interrupt indicating data | ||
| 28 | /// or space is available in the FIFO | ||
| 29 | pub fifo_threshold: FIFOThresholdLevel, | ||
| 30 | /// Indicates the type of external device connected | ||
| 31 | pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface | ||
| 32 | /// Defines the size of the external device connected to the XSPI corresponding | ||
| 33 | /// to the number of address bits required to access the device | ||
| 34 | pub device_size: MemorySize, | ||
| 35 | /// Sets the minimum number of clock cycles that the chip select signal must be held high | ||
| 36 | /// between commands | ||
| 37 | pub chip_select_high_time: ChipSelectHighTime, | ||
| 38 | /// Enables the free running clock | ||
| 39 | pub free_running_clock: bool, | ||
| 40 | /// Sets the clock level when the device is not selected | ||
| 41 | pub clock_mode: bool, | ||
| 42 | /// Indicates the wrap size corresponding to the external device configuration | ||
| 43 | pub wrap_size: WrapSize, | ||
| 44 | /// Specified the prescaler factor used for generating the external clock based | ||
| 45 | /// on the AHB clock. 0 = Fkernel, 1 = Fkernel/2, 2 = Fkernel/3 etc. | ||
| 46 | pub clock_prescaler: u8, | ||
| 47 | /// Allows the delay of 1/2 cycle the data sampling to account for external | ||
| 48 | /// signal delays | ||
| 49 | pub sample_shifting: bool, | ||
| 50 | /// Allows hold to 1/4 cycle the data | ||
| 51 | pub delay_hold_quarter_cycle: bool, | ||
| 52 | /// Enables the transaction boundary feature and defines the boundary to release | ||
| 53 | /// the chip select | ||
| 54 | pub chip_select_boundary: u8, | ||
| 55 | /// Enables communication regulation feature. Chip select is released when the other | ||
| 56 | /// XSpi requests access to the bus | ||
| 57 | pub max_transfer: u8, | ||
| 58 | /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles | ||
| 59 | pub refresh: u32, | ||
| 60 | } | ||
| 61 | |||
| 62 | impl Default for Config { | ||
| 63 | fn default() -> Self { | ||
| 64 | Self { | ||
| 65 | fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity | ||
| 66 | memory_type: MemoryType::Micron, | ||
| 67 | device_size: MemorySize::Other(0), | ||
| 68 | chip_select_high_time: ChipSelectHighTime::_5Cycle, | ||
| 69 | free_running_clock: false, | ||
| 70 | clock_mode: false, | ||
| 71 | wrap_size: WrapSize::None, | ||
| 72 | clock_prescaler: 0, | ||
| 73 | sample_shifting: false, | ||
| 74 | delay_hold_quarter_cycle: false, | ||
| 75 | chip_select_boundary: 0, // Acceptable range 0 to 31 | ||
| 76 | max_transfer: 0, | ||
| 77 | refresh: 0, | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | /// XSPI transfer configuration. | ||
| 83 | pub struct TransferConfig { | ||
| 84 | /// Instruction width (IMODE) | ||
| 85 | pub iwidth: XspiWidth, | ||
| 86 | /// Instruction Id | ||
| 87 | pub instruction: Option<u32>, | ||
| 88 | /// Number of Instruction Bytes | ||
| 89 | pub isize: AddressSize, | ||
| 90 | /// Instruction Double Transfer rate enable | ||
| 91 | pub idtr: bool, | ||
| 92 | |||
| 93 | /// Address width (ADMODE) | ||
| 94 | pub adwidth: XspiWidth, | ||
| 95 | /// Device memory address | ||
| 96 | pub address: Option<u32>, | ||
| 97 | /// Number of Address Bytes | ||
| 98 | pub adsize: AddressSize, | ||
| 99 | /// Address Double Transfer rate enable | ||
| 100 | pub addtr: bool, | ||
| 101 | |||
| 102 | /// Alternate bytes width (ABMODE) | ||
| 103 | pub abwidth: XspiWidth, | ||
| 104 | /// Alternate Bytes | ||
| 105 | pub alternate_bytes: Option<u32>, | ||
| 106 | /// Number of Alternate Bytes | ||
| 107 | pub absize: AddressSize, | ||
| 108 | /// Alternate Bytes Double Transfer rate enable | ||
| 109 | pub abdtr: bool, | ||
| 110 | |||
| 111 | /// Data width (DMODE) | ||
| 112 | pub dwidth: XspiWidth, | ||
| 113 | /// Data buffer | ||
| 114 | pub ddtr: bool, | ||
| 115 | |||
| 116 | /// Number of dummy cycles (DCYC) | ||
| 117 | pub dummy: DummyCycles, | ||
| 118 | } | ||
| 119 | |||
| 120 | impl Default for TransferConfig { | ||
| 121 | fn default() -> Self { | ||
| 122 | Self { | ||
| 123 | iwidth: XspiWidth::NONE, | ||
| 124 | instruction: None, | ||
| 125 | isize: AddressSize::_8bit, | ||
| 126 | idtr: false, | ||
| 127 | |||
| 128 | adwidth: XspiWidth::NONE, | ||
| 129 | address: None, | ||
| 130 | adsize: AddressSize::_8bit, | ||
| 131 | addtr: false, | ||
| 132 | |||
| 133 | abwidth: XspiWidth::NONE, | ||
| 134 | alternate_bytes: None, | ||
| 135 | absize: AddressSize::_8bit, | ||
| 136 | abdtr: false, | ||
| 137 | |||
| 138 | dwidth: XspiWidth::NONE, | ||
| 139 | ddtr: false, | ||
| 140 | |||
| 141 | dummy: DummyCycles::_0, | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Error used for Xspi implementation | ||
| 147 | #[derive(Debug)] | ||
| 148 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 149 | pub enum XspiError { | ||
| 150 | /// Peripheral configuration is invalid | ||
| 151 | InvalidConfiguration, | ||
| 152 | /// Operation configuration is invalid | ||
| 153 | InvalidCommand, | ||
| 154 | /// Size zero buffer passed to instruction | ||
| 155 | EmptyBuffer, | ||
| 156 | } | ||
| 157 | |||
| 158 | /// XSPI driver. | ||
| 159 | pub struct Xspi<'d, T: Instance, M: PeriMode> { | ||
| 160 | _peri: Peri<'d, T>, | ||
| 161 | clk: Option<Peri<'d, AnyPin>>, | ||
| 162 | d0: Option<Peri<'d, AnyPin>>, | ||
| 163 | d1: Option<Peri<'d, AnyPin>>, | ||
| 164 | d2: Option<Peri<'d, AnyPin>>, | ||
| 165 | d3: Option<Peri<'d, AnyPin>>, | ||
| 166 | d4: Option<Peri<'d, AnyPin>>, | ||
| 167 | d5: Option<Peri<'d, AnyPin>>, | ||
| 168 | d6: Option<Peri<'d, AnyPin>>, | ||
| 169 | d7: Option<Peri<'d, AnyPin>>, | ||
| 170 | d8: Option<Peri<'d, AnyPin>>, | ||
| 171 | d9: Option<Peri<'d, AnyPin>>, | ||
| 172 | d10: Option<Peri<'d, AnyPin>>, | ||
| 173 | d11: Option<Peri<'d, AnyPin>>, | ||
| 174 | d12: Option<Peri<'d, AnyPin>>, | ||
| 175 | d13: Option<Peri<'d, AnyPin>>, | ||
| 176 | d14: Option<Peri<'d, AnyPin>>, | ||
| 177 | d15: Option<Peri<'d, AnyPin>>, | ||
| 178 | ncs: Option<Peri<'d, AnyPin>>, | ||
| 179 | // TODO: allow switching between multiple chips | ||
| 180 | ncs_alt: Option<Peri<'d, AnyPin>>, | ||
| 181 | // false if ncs == NCS1, true if ncs == NCS2 | ||
| 182 | // (ncs_alt will be the opposite to ncs). | ||
| 183 | _cssel_swap: bool, | ||
| 184 | dqs0: Option<Peri<'d, AnyPin>>, | ||
| 185 | dqs1: Option<Peri<'d, AnyPin>>, | ||
| 186 | dma: Option<ChannelAndRequest<'d>>, | ||
| 187 | _phantom: PhantomData<M>, | ||
| 188 | config: Config, | ||
| 189 | width: XspiWidth, | ||
| 190 | } | ||
| 191 | |||
| 192 | impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { | ||
| 193 | /// Enter memory mode. | ||
| 194 | /// The Input `read_config` is used to configure the read operation in memory mode | ||
| 195 | pub fn enable_memory_mapped_mode( | ||
| 196 | &mut self, | ||
| 197 | read_config: TransferConfig, | ||
| 198 | write_config: TransferConfig, | ||
| 199 | ) -> Result<(), XspiError> { | ||
| 200 | // Use configure command to set read config | ||
| 201 | self.configure_command(&read_config, None)?; | ||
| 202 | |||
| 203 | let reg = T::REGS; | ||
| 204 | while reg.sr().read().busy() {} | ||
| 205 | |||
| 206 | reg.ccr().modify(|r| { | ||
| 207 | r.set_dqse(false); | ||
| 208 | }); | ||
| 209 | |||
| 210 | // Set wrting configurations, there are separate registers for write configurations in memory mapped mode | ||
| 211 | reg.wccr().modify(|w| { | ||
| 212 | w.set_imode(WccrImode::from_bits(write_config.iwidth.into())); | ||
| 213 | w.set_idtr(write_config.idtr); | ||
| 214 | w.set_isize(WccrIsize::from_bits(write_config.isize.into())); | ||
| 215 | |||
| 216 | w.set_admode(WccrAdmode::from_bits(write_config.adwidth.into())); | ||
| 217 | w.set_addtr(write_config.addtr); | ||
| 218 | w.set_adsize(WccrAdsize::from_bits(write_config.adsize.into())); | ||
| 219 | |||
| 220 | w.set_dmode(WccrDmode::from_bits(write_config.dwidth.into())); | ||
| 221 | w.set_ddtr(write_config.ddtr); | ||
| 222 | |||
| 223 | w.set_abmode(WccrAbmode::from_bits(write_config.abwidth.into())); | ||
| 224 | w.set_dqse(true); | ||
| 225 | }); | ||
| 226 | |||
| 227 | reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); | ||
| 228 | |||
| 229 | // Enable memory mapped mode | ||
| 230 | reg.cr().modify(|r| { | ||
| 231 | r.set_fmode(Fmode::B_0X3); | ||
| 232 | r.set_tcen(false); | ||
| 233 | }); | ||
| 234 | Ok(()) | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Quit from memory mapped mode | ||
| 238 | pub fn disable_memory_mapped_mode(&mut self) { | ||
| 239 | let reg = T::REGS; | ||
| 240 | |||
| 241 | reg.cr().modify(|r| { | ||
| 242 | r.set_fmode(Fmode::B_0X0); | ||
| 243 | r.set_abort(true); | ||
| 244 | r.set_dmaen(false); | ||
| 245 | r.set_en(false); | ||
| 246 | }); | ||
| 247 | |||
| 248 | // Clear transfer complete flag | ||
| 249 | reg.fcr().write(|w| w.set_ctcf(true)); | ||
| 250 | |||
| 251 | // Re-enable ospi | ||
| 252 | reg.cr().modify(|r| { | ||
| 253 | r.set_en(true); | ||
| 254 | }); | ||
| 255 | } | ||
| 256 | |||
| 257 | fn new_inner( | ||
| 258 | peri: Peri<'d, T>, | ||
| 259 | d0: Option<Peri<'d, AnyPin>>, | ||
| 260 | d1: Option<Peri<'d, AnyPin>>, | ||
| 261 | d2: Option<Peri<'d, AnyPin>>, | ||
| 262 | d3: Option<Peri<'d, AnyPin>>, | ||
| 263 | d4: Option<Peri<'d, AnyPin>>, | ||
| 264 | d5: Option<Peri<'d, AnyPin>>, | ||
| 265 | d6: Option<Peri<'d, AnyPin>>, | ||
| 266 | d7: Option<Peri<'d, AnyPin>>, | ||
| 267 | d8: Option<Peri<'d, AnyPin>>, | ||
| 268 | d9: Option<Peri<'d, AnyPin>>, | ||
| 269 | d10: Option<Peri<'d, AnyPin>>, | ||
| 270 | d11: Option<Peri<'d, AnyPin>>, | ||
| 271 | d12: Option<Peri<'d, AnyPin>>, | ||
| 272 | d13: Option<Peri<'d, AnyPin>>, | ||
| 273 | d14: Option<Peri<'d, AnyPin>>, | ||
| 274 | d15: Option<Peri<'d, AnyPin>>, | ||
| 275 | clk: Option<Peri<'d, AnyPin>>, | ||
| 276 | ncs_cssel: u8, | ||
| 277 | ncs: Option<Peri<'d, AnyPin>>, | ||
| 278 | ncs_alt: Option<Peri<'d, AnyPin>>, | ||
| 279 | dqs0: Option<Peri<'d, AnyPin>>, | ||
| 280 | dqs1: Option<Peri<'d, AnyPin>>, | ||
| 281 | dma: Option<ChannelAndRequest<'d>>, | ||
| 282 | config: Config, | ||
| 283 | width: XspiWidth, | ||
| 284 | dual_quad: bool, | ||
| 285 | ) -> Self { | ||
| 286 | // Enable the interface | ||
| 287 | match T::SPI_IDX { | ||
| 288 | 1 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim1(true)), | ||
| 289 | 2 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim2(true)), | ||
| 290 | _ => unreachable!(), | ||
| 291 | }; | ||
| 292 | |||
| 293 | #[cfg(xspim_v1)] | ||
| 294 | { | ||
| 295 | // RCC for xspim should be enabled before writing register | ||
| 296 | crate::pac::RCC.ahb5enr().modify(|w| w.set_iomngren(true)); | ||
| 297 | |||
| 298 | // Disable XSPI peripheral first | ||
| 299 | T::REGS.cr().modify(|w| { | ||
| 300 | w.set_en(false); | ||
| 301 | }); | ||
| 302 | |||
| 303 | // XSPI IO Manager has been enabled before | ||
| 304 | T::SPIM_REGS.cr().modify(|w| { | ||
| 305 | w.set_muxen(false); | ||
| 306 | w.set_req2ack_time(0xff); | ||
| 307 | }); | ||
| 308 | } | ||
| 309 | |||
| 310 | // System configuration | ||
| 311 | rcc::enable_and_reset::<T>(); | ||
| 312 | while T::REGS.sr().read().busy() {} | ||
| 313 | |||
| 314 | // Device configuration | ||
| 315 | T::REGS.dcr1().modify(|w| { | ||
| 316 | w.set_devsize(config.device_size.into()); | ||
| 317 | w.set_mtyp(Mtyp::from_bits(config.memory_type.into())); | ||
| 318 | w.set_csht(Csht::from_bits(config.chip_select_high_time.into())); | ||
| 319 | w.set_frck(false); | ||
| 320 | w.set_ckmode(Ckmode::from_bits(config.clock_mode.into())); | ||
| 321 | }); | ||
| 322 | |||
| 323 | T::REGS.dcr2().modify(|w| { | ||
| 324 | w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into())); | ||
| 325 | }); | ||
| 326 | |||
| 327 | T::REGS.dcr3().modify(|w| { | ||
| 328 | w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into())); | ||
| 329 | #[cfg(xspi_v1)] | ||
| 330 | { | ||
| 331 | w.set_maxtran(Maxtran::from_bits(config.max_transfer.into())); | ||
| 332 | } | ||
| 333 | }); | ||
| 334 | |||
| 335 | T::REGS.dcr4().modify(|w| { | ||
| 336 | w.set_refresh(Refresh::from_bits(config.refresh.into())); | ||
| 337 | }); | ||
| 338 | |||
| 339 | T::REGS.cr().modify(|w| { | ||
| 340 | w.set_fthres(Fthres::from_bits(config.fifo_threshold.into())); | ||
| 341 | }); | ||
| 342 | |||
| 343 | // Wait for busy flag to clear | ||
| 344 | while T::REGS.sr().read().busy() {} | ||
| 345 | |||
| 346 | T::REGS.dcr2().modify(|w| { | ||
| 347 | w.set_prescaler(config.clock_prescaler); | ||
| 348 | }); | ||
| 349 | |||
| 350 | // Wait for busy flag to clear after changing prescaler, during calibration | ||
| 351 | while T::REGS.sr().read().busy() {} | ||
| 352 | |||
| 353 | T::REGS.cr().modify(|w| { | ||
| 354 | w.set_dmm(dual_quad); | ||
| 355 | |||
| 356 | assert!(ncs_alt.is_none(), "ncs_alt TODO"); | ||
| 357 | let cssel = if ncs_cssel == 0 { Cssel::B_0X0 } else { Cssel::B_0X1 }; | ||
| 358 | w.set_cssel(cssel); | ||
| 359 | }); | ||
| 360 | |||
| 361 | T::REGS.tcr().modify(|w| { | ||
| 362 | w.set_sshift(config.sample_shifting); | ||
| 363 | w.set_dhqc(config.delay_hold_quarter_cycle); | ||
| 364 | }); | ||
| 365 | |||
| 366 | // Enable peripheral | ||
| 367 | T::REGS.cr().modify(|w| { | ||
| 368 | w.set_en(true); | ||
| 369 | }); | ||
| 370 | |||
| 371 | // Free running clock needs to be set after peripheral enable | ||
| 372 | if config.free_running_clock { | ||
| 373 | T::REGS.dcr1().modify(|w| { | ||
| 374 | w.set_frck(config.free_running_clock); | ||
| 375 | }); | ||
| 376 | } | ||
| 377 | |||
| 378 | Self { | ||
| 379 | _peri: peri, | ||
| 380 | clk, | ||
| 381 | d0, | ||
| 382 | d1, | ||
| 383 | d2, | ||
| 384 | d3, | ||
| 385 | d4, | ||
| 386 | d5, | ||
| 387 | d6, | ||
| 388 | d7, | ||
| 389 | d8, | ||
| 390 | d9, | ||
| 391 | d10, | ||
| 392 | d11, | ||
| 393 | d12, | ||
| 394 | d13, | ||
| 395 | d14, | ||
| 396 | d15, | ||
| 397 | ncs, | ||
| 398 | ncs_alt, | ||
| 399 | _cssel_swap: ncs_cssel == 1, | ||
| 400 | dqs0, | ||
| 401 | dqs1, | ||
| 402 | dma, | ||
| 403 | _phantom: PhantomData, | ||
| 404 | config, | ||
| 405 | width, | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | // Function to configure the peripheral for the requested command | ||
| 410 | fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), XspiError> { | ||
| 411 | // Check that transaction doesn't use more than hardware initialized pins | ||
| 412 | if <enums::XspiWidth as Into<u8>>::into(command.iwidth) > <enums::XspiWidth as Into<u8>>::into(self.width) | ||
| 413 | || <enums::XspiWidth as Into<u8>>::into(command.adwidth) > <enums::XspiWidth as Into<u8>>::into(self.width) | ||
| 414 | || <enums::XspiWidth as Into<u8>>::into(command.abwidth) > <enums::XspiWidth as Into<u8>>::into(self.width) | ||
| 415 | || <enums::XspiWidth as Into<u8>>::into(command.dwidth) > <enums::XspiWidth as Into<u8>>::into(self.width) | ||
| 416 | { | ||
| 417 | return Err(XspiError::InvalidCommand); | ||
| 418 | } | ||
| 419 | |||
| 420 | T::REGS.cr().modify(|w| { | ||
| 421 | w.set_fmode(0.into()); | ||
| 422 | }); | ||
| 423 | |||
| 424 | // Configure alternate bytes | ||
| 425 | if let Some(ab) = command.alternate_bytes { | ||
| 426 | T::REGS.abr().write(|v| v.set_alternate(ab)); | ||
| 427 | T::REGS.ccr().modify(|w| { | ||
| 428 | w.set_abmode(CcrAbmode::from_bits(command.abwidth.into())); | ||
| 429 | w.set_abdtr(command.abdtr); | ||
| 430 | w.set_absize(CcrAbsize::from_bits(command.absize.into())); | ||
| 431 | }) | ||
| 432 | } | ||
| 433 | |||
| 434 | // Configure dummy cycles | ||
| 435 | T::REGS.tcr().modify(|w| { | ||
| 436 | w.set_dcyc(command.dummy.into()); | ||
| 437 | }); | ||
| 438 | |||
| 439 | // Configure data | ||
| 440 | if let Some(data_length) = data_len { | ||
| 441 | T::REGS.dlr().write(|v| { | ||
| 442 | v.set_dl((data_length - 1) as u32); | ||
| 443 | }) | ||
| 444 | } else { | ||
| 445 | T::REGS.dlr().write(|v| { | ||
| 446 | v.set_dl((0) as u32); | ||
| 447 | }) | ||
| 448 | } | ||
| 449 | |||
| 450 | // Configure instruction/address/data modes | ||
| 451 | T::REGS.ccr().modify(|w| { | ||
| 452 | w.set_imode(CcrImode::from_bits(command.iwidth.into())); | ||
| 453 | w.set_idtr(command.idtr); | ||
| 454 | w.set_isize(CcrIsize::from_bits(command.isize.into())); | ||
| 455 | |||
| 456 | w.set_admode(CcrAdmode::from_bits(command.adwidth.into())); | ||
| 457 | w.set_addtr(command.addtr); | ||
| 458 | w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); | ||
| 459 | |||
| 460 | w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); | ||
| 461 | w.set_ddtr(command.ddtr); | ||
| 462 | }); | ||
| 463 | |||
| 464 | // Set information required to initiate transaction | ||
| 465 | if let Some(instruction) = command.instruction { | ||
| 466 | if let Some(address) = command.address { | ||
| 467 | T::REGS.ir().write(|v| { | ||
| 468 | v.set_instruction(instruction); | ||
| 469 | }); | ||
| 470 | |||
| 471 | T::REGS.ar().write(|v| { | ||
| 472 | v.set_address(address); | ||
| 473 | }); | ||
| 474 | } else { | ||
| 475 | // Double check requirements for delay hold and sample shifting | ||
| 476 | // if let None = command.data_len { | ||
| 477 | // if self.config.delay_hold_quarter_cycle && command.idtr { | ||
| 478 | // T::REGS.ccr().modify(|w| { | ||
| 479 | // w.set_ddtr(true); | ||
| 480 | // }); | ||
| 481 | // } | ||
| 482 | // } | ||
| 483 | |||
| 484 | // warn!("instruction: {:#x}", instruction); | ||
| 485 | T::REGS.ir().write(|v| { | ||
| 486 | v.set_instruction(instruction); | ||
| 487 | }); | ||
| 488 | } | ||
| 489 | } else { | ||
| 490 | if let Some(address) = command.address { | ||
| 491 | T::REGS.ar().write(|v| { | ||
| 492 | v.set_address(address); | ||
| 493 | }); | ||
| 494 | } else { | ||
| 495 | // The only single phase transaction supported is instruction only | ||
| 496 | return Err(XspiError::InvalidCommand); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | Ok(()) | ||
| 501 | } | ||
| 502 | |||
| 503 | /// Function used to control or configure the target device without data transfer | ||
| 504 | pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), XspiError> { | ||
| 505 | // Wait for peripheral to be free | ||
| 506 | while T::REGS.sr().read().busy() {} | ||
| 507 | |||
| 508 | // Need additional validation that command configuration doesn't have data set | ||
| 509 | self.configure_command(command, None)?; | ||
| 510 | |||
| 511 | // Transaction initiated by setting final configuration, i.e the instruction register | ||
| 512 | while !T::REGS.sr().read().tcf() {} | ||
| 513 | T::REGS.fcr().write(|w| { | ||
| 514 | w.set_ctcf(true); | ||
| 515 | }); | ||
| 516 | |||
| 517 | Ok(()) | ||
| 518 | } | ||
| 519 | |||
| 520 | /// Blocking read with byte by byte data transfer | ||
| 521 | pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> { | ||
| 522 | if buf.is_empty() { | ||
| 523 | return Err(XspiError::EmptyBuffer); | ||
| 524 | } | ||
| 525 | |||
| 526 | // Wait for peripheral to be free | ||
| 527 | while T::REGS.sr().read().busy() {} | ||
| 528 | |||
| 529 | // Ensure DMA is not enabled for this transaction | ||
| 530 | T::REGS.cr().modify(|w| { | ||
| 531 | w.set_dmaen(false); | ||
| 532 | }); | ||
| 533 | |||
| 534 | // self.configure_command(&transaction, Some(buf.len()))?; | ||
| 535 | self.configure_command(&transaction, Some(buf.len())).unwrap(); | ||
| 536 | |||
| 537 | let current_address = T::REGS.ar().read().address(); | ||
| 538 | let current_instruction = T::REGS.ir().read().instruction(); | ||
| 539 | |||
| 540 | // For a indirect read transaction, the transaction begins when the instruction/address is set | ||
| 541 | T::REGS | ||
| 542 | .cr() | ||
| 543 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); | ||
| 544 | if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 { | ||
| 545 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | ||
| 546 | } else { | ||
| 547 | T::REGS.ar().write(|v| v.set_address(current_address)); | ||
| 548 | } | ||
| 549 | |||
| 550 | for idx in 0..buf.len() { | ||
| 551 | while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} | ||
| 552 | buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; | ||
| 553 | } | ||
| 554 | |||
| 555 | while !T::REGS.sr().read().tcf() {} | ||
| 556 | T::REGS.fcr().write(|v| v.set_ctcf(true)); | ||
| 557 | |||
| 558 | Ok(()) | ||
| 559 | } | ||
| 560 | |||
| 561 | /// Blocking write with byte by byte data transfer | ||
| 562 | pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> { | ||
| 563 | if buf.is_empty() { | ||
| 564 | return Err(XspiError::EmptyBuffer); | ||
| 565 | } | ||
| 566 | |||
| 567 | // Wait for peripheral to be free | ||
| 568 | while T::REGS.sr().read().busy() {} | ||
| 569 | |||
| 570 | T::REGS.cr().modify(|w| { | ||
| 571 | w.set_dmaen(false); | ||
| 572 | }); | ||
| 573 | |||
| 574 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 575 | |||
| 576 | T::REGS | ||
| 577 | .cr() | ||
| 578 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | ||
| 579 | |||
| 580 | for idx in 0..buf.len() { | ||
| 581 | while !T::REGS.sr().read().ftf() {} | ||
| 582 | unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; | ||
| 583 | } | ||
| 584 | |||
| 585 | while !T::REGS.sr().read().tcf() {} | ||
| 586 | T::REGS.fcr().write(|v| v.set_ctcf(true)); | ||
| 587 | |||
| 588 | Ok(()) | ||
| 589 | } | ||
| 590 | |||
| 591 | /// Set new bus configuration | ||
| 592 | pub fn set_config(&mut self, config: &Config) { | ||
| 593 | // Wait for busy flag to clear | ||
| 594 | while T::REGS.sr().read().busy() {} | ||
| 595 | |||
| 596 | // Disable DMA channel while configuring the peripheral | ||
| 597 | T::REGS.cr().modify(|w| { | ||
| 598 | w.set_dmaen(false); | ||
| 599 | }); | ||
| 600 | |||
| 601 | // Device configuration | ||
| 602 | T::REGS.dcr1().modify(|w| { | ||
| 603 | w.set_devsize(config.device_size.into()); | ||
| 604 | w.set_mtyp(Mtyp::from_bits(config.memory_type.into())); | ||
| 605 | w.set_csht(Csht::from_bits(config.chip_select_high_time.into())); | ||
| 606 | w.set_frck(false); | ||
| 607 | w.set_ckmode(match config.clock_mode { | ||
| 608 | true => Ckmode::B_0X1, | ||
| 609 | false => Ckmode::B_0X0, | ||
| 610 | }); | ||
| 611 | }); | ||
| 612 | |||
| 613 | T::REGS.dcr2().modify(|w| { | ||
| 614 | w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into())); | ||
| 615 | }); | ||
| 616 | |||
| 617 | T::REGS.dcr3().modify(|w| { | ||
| 618 | w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into())); | ||
| 619 | #[cfg(xspi_v1)] | ||
| 620 | { | ||
| 621 | w.set_maxtran(Maxtran::from_bits(config.max_transfer.into())); | ||
| 622 | } | ||
| 623 | }); | ||
| 624 | |||
| 625 | T::REGS.dcr4().modify(|w| { | ||
| 626 | w.set_refresh(Refresh::from_bits(config.refresh.into())); | ||
| 627 | }); | ||
| 628 | |||
| 629 | T::REGS.cr().modify(|w| { | ||
| 630 | w.set_fthres(Fthres::from_bits(config.fifo_threshold.into())); | ||
| 631 | }); | ||
| 632 | |||
| 633 | // Wait for busy flag to clear | ||
| 634 | while T::REGS.sr().read().busy() {} | ||
| 635 | |||
| 636 | T::REGS.dcr2().modify(|w| { | ||
| 637 | w.set_prescaler(config.clock_prescaler); | ||
| 638 | }); | ||
| 639 | |||
| 640 | T::REGS.tcr().modify(|w| { | ||
| 641 | w.set_sshift(config.sample_shifting); | ||
| 642 | w.set_dhqc(config.delay_hold_quarter_cycle); | ||
| 643 | }); | ||
| 644 | |||
| 645 | // Enable peripheral | ||
| 646 | T::REGS.cr().modify(|w| { | ||
| 647 | w.set_en(true); | ||
| 648 | }); | ||
| 649 | |||
| 650 | // Free running clock needs to be set after peripheral enable | ||
| 651 | if config.free_running_clock { | ||
| 652 | T::REGS.dcr1().modify(|w| { | ||
| 653 | w.set_frck(config.free_running_clock); | ||
| 654 | }); | ||
| 655 | } | ||
| 656 | |||
| 657 | self.config = *config; | ||
| 658 | } | ||
| 659 | |||
| 660 | /// Get current configuration | ||
| 661 | pub fn get_config(&self) -> Config { | ||
| 662 | self.config | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | impl<'d, T: Instance> Xspi<'d, T, Blocking> { | ||
| 667 | /// Create new blocking XSPI driver for a single spi external chip | ||
| 668 | pub fn new_blocking_singlespi( | ||
| 669 | peri: Peri<'d, T>, | ||
| 670 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 671 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 672 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 673 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 674 | config: Config, | ||
| 675 | ) -> Self { | ||
| 676 | Self::new_inner( | ||
| 677 | peri, | ||
| 678 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 679 | new_pin!(d1, AfType::input(Pull::None)), | ||
| 680 | None, | ||
| 681 | None, | ||
| 682 | None, | ||
| 683 | None, | ||
| 684 | None, | ||
| 685 | None, | ||
| 686 | None, | ||
| 687 | None, | ||
| 688 | None, | ||
| 689 | None, | ||
| 690 | None, | ||
| 691 | None, | ||
| 692 | None, | ||
| 693 | None, | ||
| 694 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 695 | ncs.sel(), | ||
| 696 | new_pin!( | ||
| 697 | ncs, | ||
| 698 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 699 | ), | ||
| 700 | None, | ||
| 701 | None, | ||
| 702 | None, | ||
| 703 | None, | ||
| 704 | config, | ||
| 705 | XspiWidth::SING, | ||
| 706 | false, | ||
| 707 | ) | ||
| 708 | } | ||
| 709 | |||
| 710 | /// Create new blocking XSPI driver for a dualspi external chip | ||
| 711 | pub fn new_blocking_dualspi( | ||
| 712 | peri: Peri<'d, T>, | ||
| 713 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 714 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 715 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 716 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 717 | config: Config, | ||
| 718 | ) -> Self { | ||
| 719 | Self::new_inner( | ||
| 720 | peri, | ||
| 721 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 722 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 723 | None, | ||
| 724 | None, | ||
| 725 | None, | ||
| 726 | None, | ||
| 727 | None, | ||
| 728 | None, | ||
| 729 | None, | ||
| 730 | None, | ||
| 731 | None, | ||
| 732 | None, | ||
| 733 | None, | ||
| 734 | None, | ||
| 735 | None, | ||
| 736 | None, | ||
| 737 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 738 | ncs.sel(), | ||
| 739 | new_pin!( | ||
| 740 | ncs, | ||
| 741 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 742 | ), | ||
| 743 | None, | ||
| 744 | None, | ||
| 745 | None, | ||
| 746 | None, | ||
| 747 | config, | ||
| 748 | XspiWidth::DUAL, | ||
| 749 | false, | ||
| 750 | ) | ||
| 751 | } | ||
| 752 | |||
| 753 | /// Create new blocking XSPI driver for a quadspi external chip | ||
| 754 | pub fn new_blocking_quadspi( | ||
| 755 | peri: Peri<'d, T>, | ||
| 756 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 757 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 758 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 759 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 760 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 761 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 762 | config: Config, | ||
| 763 | ) -> Self { | ||
| 764 | Self::new_inner( | ||
| 765 | peri, | ||
| 766 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 767 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 768 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 769 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 770 | None, | ||
| 771 | None, | ||
| 772 | None, | ||
| 773 | None, | ||
| 774 | None, | ||
| 775 | None, | ||
| 776 | None, | ||
| 777 | None, | ||
| 778 | None, | ||
| 779 | None, | ||
| 780 | None, | ||
| 781 | None, | ||
| 782 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 783 | ncs.sel(), | ||
| 784 | new_pin!( | ||
| 785 | ncs, | ||
| 786 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 787 | ), | ||
| 788 | None, | ||
| 789 | None, | ||
| 790 | None, | ||
| 791 | None, | ||
| 792 | config, | ||
| 793 | XspiWidth::QUAD, | ||
| 794 | false, | ||
| 795 | ) | ||
| 796 | } | ||
| 797 | |||
| 798 | /// Create new blocking XSPI driver for two quadspi external chips | ||
| 799 | pub fn new_blocking_dualquadspi( | ||
| 800 | peri: Peri<'d, T>, | ||
| 801 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 802 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 803 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 804 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 805 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 806 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 807 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 808 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 809 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 810 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 811 | config: Config, | ||
| 812 | ) -> Self { | ||
| 813 | Self::new_inner( | ||
| 814 | peri, | ||
| 815 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 816 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 817 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 818 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 819 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 820 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 821 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 822 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 823 | None, | ||
| 824 | None, | ||
| 825 | None, | ||
| 826 | None, | ||
| 827 | None, | ||
| 828 | None, | ||
| 829 | None, | ||
| 830 | None, | ||
| 831 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 832 | ncs.sel(), | ||
| 833 | new_pin!( | ||
| 834 | ncs, | ||
| 835 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 836 | ), | ||
| 837 | None, | ||
| 838 | None, | ||
| 839 | None, | ||
| 840 | None, | ||
| 841 | config, | ||
| 842 | XspiWidth::QUAD, | ||
| 843 | true, | ||
| 844 | ) | ||
| 845 | } | ||
| 846 | |||
| 847 | /// Create new blocking XSPI driver for xspi external chips | ||
| 848 | pub fn new_blocking_xspi( | ||
| 849 | peri: Peri<'d, T>, | ||
| 850 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 851 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 852 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 853 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 854 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 855 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 856 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 857 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 858 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 859 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 860 | config: Config, | ||
| 861 | ) -> Self { | ||
| 862 | Self::new_inner( | ||
| 863 | peri, | ||
| 864 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 865 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 866 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 867 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 868 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 869 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 870 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 871 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 872 | None, | ||
| 873 | None, | ||
| 874 | None, | ||
| 875 | None, | ||
| 876 | None, | ||
| 877 | None, | ||
| 878 | None, | ||
| 879 | None, | ||
| 880 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 881 | ncs.sel(), | ||
| 882 | new_pin!( | ||
| 883 | ncs, | ||
| 884 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 885 | ), | ||
| 886 | None, | ||
| 887 | None, | ||
| 888 | None, | ||
| 889 | None, | ||
| 890 | config, | ||
| 891 | XspiWidth::OCTO, | ||
| 892 | false, | ||
| 893 | ) | ||
| 894 | } | ||
| 895 | } | ||
| 896 | |||
| 897 | impl<'d, T: Instance> Xspi<'d, T, Async> { | ||
| 898 | /// Create new blocking XSPI driver for a single spi external chip | ||
| 899 | pub fn new_singlespi( | ||
| 900 | peri: Peri<'d, T>, | ||
| 901 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 902 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 903 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 904 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 905 | dma: Peri<'d, impl XDma<T>>, | ||
| 906 | config: Config, | ||
| 907 | ) -> Self { | ||
| 908 | Self::new_inner( | ||
| 909 | peri, | ||
| 910 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 911 | new_pin!(d1, AfType::input(Pull::None)), | ||
| 912 | None, | ||
| 913 | None, | ||
| 914 | None, | ||
| 915 | None, | ||
| 916 | None, | ||
| 917 | None, | ||
| 918 | None, | ||
| 919 | None, | ||
| 920 | None, | ||
| 921 | None, | ||
| 922 | None, | ||
| 923 | None, | ||
| 924 | None, | ||
| 925 | None, | ||
| 926 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 927 | ncs.sel(), | ||
| 928 | new_pin!( | ||
| 929 | ncs, | ||
| 930 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 931 | ), | ||
| 932 | None, | ||
| 933 | None, | ||
| 934 | None, | ||
| 935 | new_dma!(dma), | ||
| 936 | config, | ||
| 937 | XspiWidth::SING, | ||
| 938 | false, | ||
| 939 | ) | ||
| 940 | } | ||
| 941 | |||
| 942 | /// Create new blocking XSPI driver for a dualspi external chip | ||
| 943 | pub fn new_dualspi( | ||
| 944 | peri: Peri<'d, T>, | ||
| 945 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 946 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 947 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 948 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 949 | dma: Peri<'d, impl XDma<T>>, | ||
| 950 | config: Config, | ||
| 951 | ) -> Self { | ||
| 952 | Self::new_inner( | ||
| 953 | peri, | ||
| 954 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 955 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 956 | None, | ||
| 957 | None, | ||
| 958 | None, | ||
| 959 | None, | ||
| 960 | None, | ||
| 961 | None, | ||
| 962 | None, | ||
| 963 | None, | ||
| 964 | None, | ||
| 965 | None, | ||
| 966 | None, | ||
| 967 | None, | ||
| 968 | None, | ||
| 969 | None, | ||
| 970 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 971 | ncs.sel(), | ||
| 972 | new_pin!( | ||
| 973 | ncs, | ||
| 974 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 975 | ), | ||
| 976 | None, | ||
| 977 | None, | ||
| 978 | None, | ||
| 979 | new_dma!(dma), | ||
| 980 | config, | ||
| 981 | XspiWidth::DUAL, | ||
| 982 | false, | ||
| 983 | ) | ||
| 984 | } | ||
| 985 | |||
| 986 | /// Create new blocking XSPI driver for a quadspi external chip | ||
| 987 | pub fn new_quadspi( | ||
| 988 | peri: Peri<'d, T>, | ||
| 989 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 990 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 991 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 992 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 993 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 994 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 995 | dma: Peri<'d, impl XDma<T>>, | ||
| 996 | config: Config, | ||
| 997 | ) -> Self { | ||
| 998 | Self::new_inner( | ||
| 999 | peri, | ||
| 1000 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1001 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1002 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1003 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1004 | None, | ||
| 1005 | None, | ||
| 1006 | None, | ||
| 1007 | None, | ||
| 1008 | None, | ||
| 1009 | None, | ||
| 1010 | None, | ||
| 1011 | None, | ||
| 1012 | None, | ||
| 1013 | None, | ||
| 1014 | None, | ||
| 1015 | None, | ||
| 1016 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1017 | ncs.sel(), | ||
| 1018 | new_pin!( | ||
| 1019 | ncs, | ||
| 1020 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 1021 | ), | ||
| 1022 | None, | ||
| 1023 | None, | ||
| 1024 | None, | ||
| 1025 | new_dma!(dma), | ||
| 1026 | config, | ||
| 1027 | XspiWidth::QUAD, | ||
| 1028 | false, | ||
| 1029 | ) | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | /// Create new blocking XSPI driver for two quadspi external chips | ||
| 1033 | pub fn new_dualquadspi( | ||
| 1034 | peri: Peri<'d, T>, | ||
| 1035 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 1036 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 1037 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 1038 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 1039 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 1040 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 1041 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 1042 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 1043 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 1044 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 1045 | dma: Peri<'d, impl XDma<T>>, | ||
| 1046 | config: Config, | ||
| 1047 | ) -> Self { | ||
| 1048 | Self::new_inner( | ||
| 1049 | peri, | ||
| 1050 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1051 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1052 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1053 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1054 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1055 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1056 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1057 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1058 | None, | ||
| 1059 | None, | ||
| 1060 | None, | ||
| 1061 | None, | ||
| 1062 | None, | ||
| 1063 | None, | ||
| 1064 | None, | ||
| 1065 | None, | ||
| 1066 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1067 | ncs.sel(), | ||
| 1068 | new_pin!( | ||
| 1069 | ncs, | ||
| 1070 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 1071 | ), | ||
| 1072 | None, | ||
| 1073 | None, | ||
| 1074 | None, | ||
| 1075 | new_dma!(dma), | ||
| 1076 | config, | ||
| 1077 | XspiWidth::QUAD, | ||
| 1078 | true, | ||
| 1079 | ) | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | /// Create new blocking XSPI driver for xspi external chips | ||
| 1083 | pub fn new_xspi( | ||
| 1084 | peri: Peri<'d, T>, | ||
| 1085 | clk: Peri<'d, impl CLKPin<T>>, | ||
| 1086 | d0: Peri<'d, impl D0Pin<T>>, | ||
| 1087 | d1: Peri<'d, impl D1Pin<T>>, | ||
| 1088 | d2: Peri<'d, impl D2Pin<T>>, | ||
| 1089 | d3: Peri<'d, impl D3Pin<T>>, | ||
| 1090 | d4: Peri<'d, impl D4Pin<T>>, | ||
| 1091 | d5: Peri<'d, impl D5Pin<T>>, | ||
| 1092 | d6: Peri<'d, impl D6Pin<T>>, | ||
| 1093 | d7: Peri<'d, impl D7Pin<T>>, | ||
| 1094 | ncs: Peri<'d, impl NCSEither<T>>, | ||
| 1095 | dma: Peri<'d, impl XDma<T>>, | ||
| 1096 | config: Config, | ||
| 1097 | ) -> Self { | ||
| 1098 | Self::new_inner( | ||
| 1099 | peri, | ||
| 1100 | new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1101 | new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1102 | new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1103 | new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1104 | new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1105 | new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1106 | new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1107 | new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1108 | None, | ||
| 1109 | None, | ||
| 1110 | None, | ||
| 1111 | None, | ||
| 1112 | None, | ||
| 1113 | None, | ||
| 1114 | None, | ||
| 1115 | None, | ||
| 1116 | new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), | ||
| 1117 | ncs.sel(), | ||
| 1118 | new_pin!( | ||
| 1119 | ncs, | ||
| 1120 | AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) | ||
| 1121 | ), | ||
| 1122 | None, | ||
| 1123 | None, | ||
| 1124 | None, | ||
| 1125 | new_dma!(dma), | ||
| 1126 | config, | ||
| 1127 | XspiWidth::OCTO, | ||
| 1128 | false, | ||
| 1129 | ) | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | /// Blocking read with DMA transfer | ||
| 1133 | pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> { | ||
| 1134 | if buf.is_empty() { | ||
| 1135 | return Err(XspiError::EmptyBuffer); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | // Wait for peripheral to be free | ||
| 1139 | while T::REGS.sr().read().busy() {} | ||
| 1140 | |||
| 1141 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 1142 | |||
| 1143 | let current_address = T::REGS.ar().read().address(); | ||
| 1144 | let current_instruction = T::REGS.ir().read().instruction(); | ||
| 1145 | |||
| 1146 | // For a indirect read transaction, the transaction begins when the instruction/address is set | ||
| 1147 | T::REGS | ||
| 1148 | .cr() | ||
| 1149 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); | ||
| 1150 | if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 { | ||
| 1151 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | ||
| 1152 | } else { | ||
| 1153 | T::REGS.ar().write(|v| v.set_address(current_address)); | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | let transfer = unsafe { | ||
| 1157 | self.dma | ||
| 1158 | .as_mut() | ||
| 1159 | .unwrap() | ||
| 1160 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | ||
| 1161 | }; | ||
| 1162 | |||
| 1163 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 1164 | |||
| 1165 | transfer.blocking_wait(); | ||
| 1166 | |||
| 1167 | finish_dma(T::REGS); | ||
| 1168 | |||
| 1169 | Ok(()) | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | /// Blocking write with DMA transfer | ||
| 1173 | pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> { | ||
| 1174 | if buf.is_empty() { | ||
| 1175 | return Err(XspiError::EmptyBuffer); | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | // Wait for peripheral to be free | ||
| 1179 | while T::REGS.sr().read().busy() {} | ||
| 1180 | |||
| 1181 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 1182 | T::REGS | ||
| 1183 | .cr() | ||
| 1184 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | ||
| 1185 | |||
| 1186 | let transfer = unsafe { | ||
| 1187 | self.dma | ||
| 1188 | .as_mut() | ||
| 1189 | .unwrap() | ||
| 1190 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1191 | }; | ||
| 1192 | |||
| 1193 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 1194 | |||
| 1195 | transfer.blocking_wait(); | ||
| 1196 | |||
| 1197 | finish_dma(T::REGS); | ||
| 1198 | |||
| 1199 | Ok(()) | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | /// Asynchronous read from external device | ||
| 1203 | pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> { | ||
| 1204 | if buf.is_empty() { | ||
| 1205 | return Err(XspiError::EmptyBuffer); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | // Wait for peripheral to be free | ||
| 1209 | while T::REGS.sr().read().busy() {} | ||
| 1210 | |||
| 1211 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 1212 | |||
| 1213 | let current_address = T::REGS.ar().read().address(); | ||
| 1214 | let current_instruction = T::REGS.ir().read().instruction(); | ||
| 1215 | |||
| 1216 | // For a indirect read transaction, the transaction begins when the instruction/address is set | ||
| 1217 | T::REGS | ||
| 1218 | .cr() | ||
| 1219 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); | ||
| 1220 | if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 { | ||
| 1221 | T::REGS.ir().write(|v| v.set_instruction(current_instruction)); | ||
| 1222 | } else { | ||
| 1223 | T::REGS.ar().write(|v| v.set_address(current_address)); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | let transfer = unsafe { | ||
| 1227 | self.dma | ||
| 1228 | .as_mut() | ||
| 1229 | .unwrap() | ||
| 1230 | .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) | ||
| 1231 | }; | ||
| 1232 | |||
| 1233 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 1234 | |||
| 1235 | transfer.await; | ||
| 1236 | |||
| 1237 | finish_dma(T::REGS); | ||
| 1238 | |||
| 1239 | Ok(()) | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | /// Asynchronous write to external device | ||
| 1243 | pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> { | ||
| 1244 | if buf.is_empty() { | ||
| 1245 | return Err(XspiError::EmptyBuffer); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | // Wait for peripheral to be free | ||
| 1249 | while T::REGS.sr().read().busy() {} | ||
| 1250 | |||
| 1251 | self.configure_command(&transaction, Some(buf.len()))?; | ||
| 1252 | T::REGS | ||
| 1253 | .cr() | ||
| 1254 | .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); | ||
| 1255 | |||
| 1256 | let transfer = unsafe { | ||
| 1257 | self.dma | ||
| 1258 | .as_mut() | ||
| 1259 | .unwrap() | ||
| 1260 | .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) | ||
| 1261 | }; | ||
| 1262 | |||
| 1263 | T::REGS.cr().modify(|w| w.set_dmaen(true)); | ||
| 1264 | |||
| 1265 | transfer.await; | ||
| 1266 | |||
| 1267 | finish_dma(T::REGS); | ||
| 1268 | |||
| 1269 | Ok(()) | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | impl<'d, T: Instance, M: PeriMode> Drop for Xspi<'d, T, M> { | ||
| 1274 | fn drop(&mut self) { | ||
| 1275 | self.clk.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1276 | self.d0.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1277 | self.d1.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1278 | self.d2.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1279 | self.d3.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1280 | self.d4.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1281 | self.d5.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1282 | self.d6.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1283 | self.d7.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1284 | self.d8.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1285 | self.d9.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1286 | self.d10.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1287 | self.d11.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1288 | self.d12.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1289 | self.d13.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1290 | self.d14.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1291 | self.d15.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1292 | self.ncs.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1293 | self.ncs_alt.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1294 | self.dqs0.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1295 | self.dqs1.as_ref().map(|x| x.set_as_disconnected()); | ||
| 1296 | |||
| 1297 | rcc::disable::<T>(); | ||
| 1298 | } | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | fn finish_dma(regs: Regs) { | ||
| 1302 | while !regs.sr().read().tcf() {} | ||
| 1303 | regs.fcr().write(|v| v.set_ctcf(true)); | ||
| 1304 | |||
| 1305 | regs.cr().modify(|w| { | ||
| 1306 | w.set_dmaen(false); | ||
| 1307 | }); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | /// XSPI I/O manager instance trait. | ||
| 1311 | #[cfg(xspim_v1)] | ||
| 1312 | pub(crate) trait SealedXspimInstance { | ||
| 1313 | const SPIM_REGS: Xspim; | ||
| 1314 | const SPI_IDX: u8; | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | /// XSPI instance trait. | ||
| 1318 | pub(crate) trait SealedInstance { | ||
| 1319 | const REGS: Regs; | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | /// XSPI instance trait. | ||
| 1323 | #[cfg(xspim_v1)] | ||
| 1324 | #[allow(private_bounds)] | ||
| 1325 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + SealedXspimInstance {} | ||
| 1326 | |||
| 1327 | /// XSPI instance trait. | ||
| 1328 | #[cfg(not(xspim_v1))] | ||
| 1329 | #[allow(private_bounds)] | ||
| 1330 | pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {} | ||
| 1331 | |||
| 1332 | pin_trait!(D0Pin, Instance); | ||
| 1333 | pin_trait!(D1Pin, Instance); | ||
| 1334 | pin_trait!(D2Pin, Instance); | ||
| 1335 | pin_trait!(D3Pin, Instance); | ||
| 1336 | pin_trait!(D4Pin, Instance); | ||
| 1337 | pin_trait!(D5Pin, Instance); | ||
| 1338 | pin_trait!(D6Pin, Instance); | ||
| 1339 | pin_trait!(D7Pin, Instance); | ||
| 1340 | pin_trait!(D8Pin, Instance); | ||
| 1341 | pin_trait!(D9Pin, Instance); | ||
| 1342 | pin_trait!(D10Pin, Instance); | ||
| 1343 | pin_trait!(D11Pin, Instance); | ||
| 1344 | pin_trait!(D12Pin, Instance); | ||
| 1345 | pin_trait!(D13Pin, Instance); | ||
| 1346 | pin_trait!(D14Pin, Instance); | ||
| 1347 | pin_trait!(D15Pin, Instance); | ||
| 1348 | pin_trait!(DQS0Pin, Instance); | ||
| 1349 | pin_trait!(DQS1Pin, Instance); | ||
| 1350 | pin_trait!(NCSPin, Instance); | ||
| 1351 | pin_trait!(CLKPin, Instance); | ||
| 1352 | pin_trait!(NCLKPin, Instance); | ||
| 1353 | dma_trait!(XDma, Instance); | ||
| 1354 | |||
| 1355 | /// Trait for either NCS1 or NCS2 pins | ||
| 1356 | pub trait NCSEither<T: Instance>: NCSPin<T> { | ||
| 1357 | /// Get the CSSEL for this NCS pin | ||
| 1358 | fn sel(&self) -> u8; | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | // Hard-coded the xspi index, for SPIM | ||
| 1362 | #[cfg(xspim_v1)] | ||
| 1363 | impl SealedXspimInstance for peripherals::XSPI1 { | ||
| 1364 | const SPIM_REGS: Xspim = crate::pac::XSPIM; | ||
| 1365 | const SPI_IDX: u8 = 1; | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | // Some cubedb files are missing XSPI2, for example STM32H7R3Z8 | ||
| 1369 | #[cfg(all(xspim_v1, peri_xspi2))] | ||
| 1370 | impl SealedXspimInstance for peripherals::XSPI2 { | ||
| 1371 | const SPIM_REGS: Xspim = crate::pac::XSPIM; | ||
| 1372 | const SPI_IDX: u8 = 2; | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | #[cfg(xspim_v1)] | ||
| 1376 | foreach_peripheral!( | ||
| 1377 | (xspi, $inst:ident) => { | ||
| 1378 | impl SealedInstance for peripherals::$inst { | ||
| 1379 | const REGS: Regs = crate::pac::$inst; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | impl Instance for peripherals::$inst {} | ||
| 1383 | }; | ||
| 1384 | ); | ||
| 1385 | |||
| 1386 | #[cfg(not(xspim_v1))] | ||
| 1387 | foreach_peripheral!( | ||
| 1388 | (xspi, $inst:ident) => { | ||
| 1389 | impl SealedInstance for peripherals::$inst { | ||
| 1390 | const REGS: Regs = crate::pac::$inst; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | impl Instance for peripherals::$inst {} | ||
| 1394 | }; | ||
| 1395 | ); | ||
| 1396 | |||
| 1397 | impl<'d, T: Instance, M: PeriMode> SetConfig for Xspi<'d, T, M> { | ||
| 1398 | type Config = Config; | ||
| 1399 | type ConfigError = (); | ||
| 1400 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | ||
| 1401 | self.set_config(config); | ||
| 1402 | Ok(()) | ||
| 1403 | } | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | impl<'d, T: Instance, M: PeriMode> GetConfig for Xspi<'d, T, M> { | ||
| 1407 | type Config = Config; | ||
| 1408 | fn get_config(&self) -> Self::Config { | ||
| 1409 | self.get_config() | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | /// Word sizes usable for XSPI. | ||
| 1414 | #[allow(private_bounds)] | ||
| 1415 | pub trait Word: word::Word {} | ||
| 1416 | |||
| 1417 | macro_rules! impl_word { | ||
| 1418 | ($T:ty) => { | ||
| 1419 | impl Word for $T {} | ||
| 1420 | }; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | impl_word!(u8); | ||
| 1424 | impl_word!(u16); | ||
| 1425 | impl_word!(u32); | ||
