| #![no_main] |
| #![no_std] |
| |
| extern crate panic_semihosting; |
| |
| use rtic::app; |
| |
| use cortex_m::asm::delay; |
| use stm32f1xx_hal::{ |
| gpio::{gpioc::*, Output, PushPull}, |
| i2c::{BlockingI2c, Mode}, |
| pac::{Peripherals}, |
| prelude::*, |
| usb::{Peripheral, UsbBus, UsbBusType}, |
| }; |
| use embedded_hal::digital::v2::OutputPin; |
| |
| use usb_device::bus; |
| use usb_device::prelude::*; |
| |
| use usbd_webusb::WebUsb; |
| |
| mod i2c; |
| mod print; |
| |
| // The main RTIC application object. See RTIC documentation for more information about how to read |
| // this. |
| |
| #[app(device = stm32f1xx_hal::stm32, peripherals = true)] |
| const APP: () = { |
| struct Resources { |
| usb_dev: UsbDevice<'static, UsbBusType>, |
| webusb: WebUsb<UsbBusType>, |
| // The I2C USB device class that performs the main logic of accessing the I2C bus over USB |
| // for users of the device. |
| i2c: i2c::I2CClass<'static, UsbBusType, PC13<Output<PushPull>>>, |
| } |
| |
| /// Idle loop to prevent WFI which in turn prevents debugging. |
| // TODO: make this only happen on debug builds? |
| #[idle] |
| fn idle(_: idle::Context) -> ! { |
| loop {} |
| } |
| |
| #[init] |
| fn init(cx: init::Context) -> init::LateResources { |
| static mut USB_BUS: Option<bus::UsbBusAllocator<UsbBusType>> = None; |
| |
| let mut flash = cx.device.FLASH.constrain(); |
| let mut rcc = cx.device.RCC.constrain(); |
| |
| let clocks = rcc |
| .cfgr |
| .use_hse(8.mhz()) |
| .sysclk(48.mhz()) |
| .pclk1(24.mhz()) |
| .freeze(&mut flash.acr); |
| |
| assert!(clocks.usbclk_valid()); |
| |
| let mut gpioa = cx.device.GPIOA.split(&mut rcc.apb2); |
| let mut gpiob = cx.device.GPIOB.split(&mut rcc.apb2); |
| let mut gpioc = cx.device.GPIOC.split(&mut rcc.apb2); |
| |
| // Active-low LED on bluepill board. |
| let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); |
| led.set_high().ok(); |
| |
| let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2); |
| |
| // BluePill board has a pull-up resistor on the D+ line. |
| // Pull the D+ pin down to send a RESET condition to the USB bus. |
| // This forced reset is needed only for development, without it host |
| // will not reset your device when you upload new firmware. |
| let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); |
| usb_dp.set_low().unwrap(); |
| delay(clocks.sysclk().0 / 100); |
| |
| let usb_dm = gpioa.pa11; |
| let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh); |
| |
| let usb = Peripheral { |
| usb: cx.device.USB, |
| pin_dm: usb_dm, |
| pin_dp: usb_dp, |
| }; |
| |
| *USB_BUS = Some(UsbBus::new(usb)); |
| |
| let i2c_pins = ( |
| gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl), |
| gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl), |
| ); |
| |
| // Blocking I2C peripheral for use by the I2C app. |
| let i2c_dev = BlockingI2c::i2c1( |
| cx.device.I2C1, |
| i2c_pins, |
| &mut afio.mapr, |
| Mode::standard(100.khz()), |
| clocks, |
| &mut rcc.apb1, |
| 1000, 10, 1000, 1000, |
| ); |
| |
| // I2C app. |
| let i2c = i2c::I2CClass::new( |
| USB_BUS.as_ref().unwrap(), |
| led, i2c_dev, |
| ); |
| |
| let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27d8)) |
| .manufacturer("Warsaw Hackerspace") |
| .product("Web I2C Programmer") |
| // TODO(q3k): generate serial at build time? |
| .serial_number("2137") |
| .build(); |
| |
| init::LateResources { |
| usb_dev, i2c, |
| webusb: WebUsb::new( |
| USB_BUS.as_ref().unwrap(), |
| usbd_webusb::url_scheme::HTTPS, |
| "hackdoc.hackerspace.pl/dc/hbj11/flasher", |
| ), |
| } |
| } |
| |
| #[task(binds = USB_LP_CAN_RX0, resources = [usb_dev, webusb, i2c])] |
| fn usb_lp(cx: usb_lp::Context) { |
| cx.resources |
| .usb_dev |
| .poll(&mut [cx.resources.webusb, cx.resources.i2c]); |
| } |
| }; |
| |