|
| 1 | +#![no_std] |
| 2 | +#![no_main] |
| 3 | +#![allow(async_fn_in_trait)] |
| 4 | +#![allow(incomplete_features)] |
| 5 | +#![feature(impl_trait_in_assoc_type)] |
| 6 | +#![feature(type_alias_impl_trait)] |
| 7 | + |
| 8 | +use defmt_rtt as _; // global logger |
| 9 | + |
| 10 | +use assign_resources::assign_resources; |
| 11 | +use core::panic::PanicInfo; |
| 12 | +use cortex_m::peripheral::SCB; |
| 13 | +use embassy_executor::Spawner; |
| 14 | +use embassy_stm32::bind_interrupts; |
| 15 | +use embassy_stm32::gpio::{Level, Output, Speed}; |
| 16 | +use embassy_stm32::peripherals::{self, USB}; |
| 17 | +use embassy_stm32::spi::{Config as SpiConfig, Spi}; |
| 18 | +use embassy_stm32::time::Hertz; |
| 19 | +use embassy_stm32::usb::{Driver, InterruptHandler as USBInterruptHandler}; |
| 20 | +use embassy_time::Timer; |
| 21 | +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; |
| 22 | +use embassy_usb::driver::EndpointError; |
| 23 | +use embassy_usb::{Config as UsbConfig, UsbDevice}; |
| 24 | +use heapless::String; |
| 25 | +use static_cell::StaticCell; |
| 26 | +use ufmt::uwrite; |
| 27 | + |
| 28 | +use defmt::{error, info}; |
| 29 | + |
| 30 | +bind_interrupts!(struct Irqs { |
| 31 | + USB_LP_CAN1_RX0 => USBInterruptHandler<USB>; |
| 32 | +}); |
| 33 | + |
| 34 | +assign_resources! { |
| 35 | + usb: UsbResources { |
| 36 | + peripheral: USB, |
| 37 | + dm: PA11, |
| 38 | + dp: PA12, |
| 39 | + } |
| 40 | + spi: SpiResources{ |
| 41 | + peripheral: SPI1, |
| 42 | + clk: PA5, |
| 43 | + mosi: PA7, |
| 44 | + mosi_dma: DMA1_CH3, |
| 45 | + miso: PA6, |
| 46 | + miso_dma: DMA1_CH2, |
| 47 | + cs: PA4, |
| 48 | + led: PC13, |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +// According to Serial Flasher Protocol Specification - version 1 |
| 53 | +#[embassy_executor::main] |
| 54 | +async fn main(spawner: Spawner) { |
| 55 | + let mut config = embassy_stm32::Config::default(); |
| 56 | + { |
| 57 | + use embassy_stm32::rcc::*; |
| 58 | + config.rcc.hse = Some(Hse { |
| 59 | + freq: Hertz(8_000_000), |
| 60 | + // Oscillator for bluepill, Bypass for nucleos. |
| 61 | + mode: HseMode::Oscillator, |
| 62 | + }); |
| 63 | + config.rcc.pll = Some(Pll { |
| 64 | + src: PllSource::HSE, |
| 65 | + prediv: PllPreDiv::DIV1, |
| 66 | + mul: PllMul::MUL9, |
| 67 | + }); |
| 68 | + config.rcc.sys = Sysclk::PLL1_P; |
| 69 | + config.rcc.ahb_pre = AHBPrescaler::DIV1; |
| 70 | + config.rcc.apb1_pre = APBPrescaler::DIV2; |
| 71 | + config.rcc.apb2_pre = APBPrescaler::DIV1; |
| 72 | + } |
| 73 | + let p = embassy_stm32::init(config); |
| 74 | + let mut r = split_resources!(p); |
| 75 | + |
| 76 | + { |
| 77 | + // BluePill board has a pull-up resistor on the D+ line. |
| 78 | + // Pull the D+ pin down to send a RESET condition to the USB bus. |
| 79 | + // This forced reset is needed only for development, without it host |
| 80 | + // will not reset your device when you upload new firmware. |
| 81 | + let _dp = Output::new(&mut r.usb.dp, Level::Low, Speed::Low); |
| 82 | + Timer::after_millis(10).await; |
| 83 | + } |
| 84 | + |
| 85 | + info!("hello"); |
| 86 | + |
| 87 | + let driver = Driver::new(r.usb.peripheral, Irqs, r.usb.dp, r.usb.dm); |
| 88 | + |
| 89 | + let uid: [u8; 8] = embassy_stm32::uid::uid()[..8].try_into().unwrap(); |
| 90 | + |
| 91 | + static UID_STR: StaticCell<String<16>> = StaticCell::new(); |
| 92 | + let uid_str = UID_STR.init(String::<16>::new()); |
| 93 | + for byte in uid.iter() { |
| 94 | + uwrite!(uid_str, "{:02X}", *byte).unwrap_or_default(); |
| 95 | + } |
| 96 | + |
| 97 | + let config = { |
| 98 | + let mut config = UsbConfig::new(0x1ced, 0xc0fe); |
| 99 | + config.manufacturer = Some("9elements"); |
| 100 | + config.product = Some("bluepill-prog"); |
| 101 | + config.serial_number = Some(uid_str.as_str()); |
| 102 | + config.max_power = 100; |
| 103 | + config.max_packet_size_0 = 64; |
| 104 | + |
| 105 | + // Required for windows compatibility. |
| 106 | + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help |
| 107 | + config.device_class = 0xEF; |
| 108 | + config.device_sub_class = 0x02; |
| 109 | + config.device_protocol = 0x01; |
| 110 | + config.composite_with_iads = true; |
| 111 | + config |
| 112 | + }; |
| 113 | + |
| 114 | + let mut builder = { |
| 115 | + static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); |
| 116 | + static BOS_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); |
| 117 | + static CONTROL_BUF: StaticCell<[u8; 64]> = StaticCell::new(); |
| 118 | + |
| 119 | + let builder = embassy_usb::Builder::new( |
| 120 | + driver, |
| 121 | + config, |
| 122 | + CONFIG_DESCRIPTOR.init([0; 256]), |
| 123 | + BOS_DESCRIPTOR.init([0; 256]), |
| 124 | + &mut [], // no msos descriptors |
| 125 | + CONTROL_BUF.init([0; 64]), |
| 126 | + ); |
| 127 | + builder |
| 128 | + }; |
| 129 | + |
| 130 | + let serprog_class = { |
| 131 | + static STATE: StaticCell<State> = StaticCell::new(); |
| 132 | + let state = STATE.init(State::new()); |
| 133 | + CdcAcmClass::new(&mut builder, state, 64) |
| 134 | + }; |
| 135 | + |
| 136 | + let usb = builder.build(); |
| 137 | + |
| 138 | + // We can't really recover here so just unwrap |
| 139 | + spawner.spawn(usb_task(usb)).unwrap(); |
| 140 | + spawner.spawn(serprog_task(serprog_class, r.spi)).unwrap(); |
| 141 | + |
| 142 | + loop { |
| 143 | + embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +type CustomUsbDriver = Driver<'static, USB>; |
| 148 | +type CustomUsbDevice = UsbDevice<'static, CustomUsbDriver>; |
| 149 | + |
| 150 | +struct Disconnected {} |
| 151 | + |
| 152 | +impl From<EndpointError> for Disconnected { |
| 153 | + fn from(val: EndpointError) -> Self { |
| 154 | + match val { |
| 155 | + EndpointError::BufferOverflow => defmt::panic!("USB buffer overflow"), |
| 156 | + EndpointError::Disabled => Disconnected {}, |
| 157 | + } |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +#[embassy_executor::task] |
| 162 | +async fn usb_task(mut usb: CustomUsbDevice) -> ! { |
| 163 | + usb.run().await |
| 164 | +} |
| 165 | + |
| 166 | +#[embassy_executor::task] |
| 167 | +async fn serprog_task(mut class: CdcAcmClass<'static, CustomUsbDriver>, r: SpiResources) -> ! { |
| 168 | + let mut config = SpiConfig::default(); |
| 169 | + config.frequency = Hertz(12_000_000); // 12 MHz |
| 170 | + |
| 171 | + let spi = Spi::new( |
| 172 | + r.peripheral, |
| 173 | + r.clk, |
| 174 | + r.mosi, |
| 175 | + r.miso, |
| 176 | + r.mosi_dma, |
| 177 | + r.miso_dma, |
| 178 | + config, |
| 179 | + ); |
| 180 | + let cs = Output::new(r.cs, Level::High, Speed::Low); |
| 181 | + let led = Output::new(r.led, Level::Low, Speed::Low); |
| 182 | + |
| 183 | + loop { |
| 184 | + class.wait_connection().await; |
| 185 | + let serprog = serprog::Serprog::new( |
| 186 | + spi, |
| 187 | + cs, |
| 188 | + led, |
| 189 | + class, |
| 190 | + None::<fn(&mut Spi<'_, embassy_stm32::mode::Async>, u32)>, |
| 191 | + ); |
| 192 | + serprog.run_loop().await |
| 193 | + } |
| 194 | +} |
| 195 | + |
| 196 | +#[panic_handler] |
| 197 | +fn panic(info: &PanicInfo) -> ! { |
| 198 | + // Print out the panic info |
| 199 | + error!("Panic occurred: {:?}", info); |
| 200 | + |
| 201 | + // Reboot the system |
| 202 | + SCB::sys_reset(); |
| 203 | +} |
0 commit comments