blob: da6cefdc344ba6a7a551801c0cbc2c2823675af2 [file] [log] [blame]
#![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]);
}
};