Created
June 29, 2025 18:34
-
-
Save dlancea/0008a8618da3affb934e73ac04a3d35d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! CDC-ACM serial port example using polling in a busy loop | |
| //! | |
| //! Tested with a NUCLEO-H723ZG | |
| //! | |
| //! This example uses the USB1 peripheral. On parts that have multiple USB | |
| //! OTG_HS peripherals the USB1 D+/D- pins are located on PB14 and PB15. If your | |
| //! development board uses PA11 and PA12 instead, you should adapt the example | |
| //! to use the USB2 peripheral together with PA11 and PA12. This applies to the | |
| //! NUCLEO-H743ZI2 board. | |
| //! | |
| // #![deny(warnings)] | |
| #![no_std] | |
| #![no_main] | |
| use cortex_m::asm::nop; | |
| use defmt_rtt as _; // global logger | |
| // TODO(5) adjust HAL import | |
| // use some_hal as _; // memory layout | |
| use panic_probe as _; | |
| // use panic_abort as _; | |
| // same panicking *behavior* as `panic-probe` but doesn't print a panic message | |
| // this prevents the panic message being printed *twice* when `defmt::panic` is invoked | |
| #[defmt::panic_handler] | |
| fn panic() -> ! { | |
| cortex_m::asm::udf() | |
| } | |
| use core::mem::MaybeUninit; | |
| #[macro_use] | |
| #[allow(unused)] | |
| mod utilities; | |
| use cortex_m_rt::entry; | |
| use stm32h7xx_hal::rcc::rec::UsbClkSel; | |
| use stm32h7xx_hal::usb_hs::{UsbBus, USB1}; | |
| use stm32h7xx_hal::{prelude::*, stm32}; | |
| // use usb_device::prelude::*; | |
| use usb_device::{bus::UsbBusAllocator, prelude::UsbDevice, prelude::UsbVidPid, prelude::UsbDeviceBuilder, prelude::StringDescriptors}; | |
| use usbd_midi::{CableNumber, UsbMidiClass, UsbMidiEventPacket, UsbMidiPacketReader}; | |
| use midi_convert::midi_types::{Channel, MidiMessage, Note, Value7}; | |
| use midi_convert::{parse::MidiTryParseSlice, render_slice::MidiRenderSlice}; | |
| use stm32h7xx_hal::time::Hertz; | |
| use stm32h7xx_hal::time::MegaHertz; | |
| use stm32h7xx_hal::rcc; | |
| pub const CLOCK_RATE_HZ: Hertz = Hertz::from_raw(480_000_000_u32); | |
| // pub const CLOCK_RATE_HZ: Hertz = Hertz::from_raw(480_000_000_u32); | |
| pub const AUDIO_SAMPLE_HZ: Hertz = Hertz::from_raw(48_000); | |
| const HSE_CLOCK_MHZ: MegaHertz = MegaHertz::from_raw(16); | |
| // const HCLK_MHZ: MegaHertz = MegaHertz::from_raw(200); | |
| // const HCLK2_MHZ: MegaHertz = MegaHertz::from_raw(200); | |
| // PCLKx | |
| const PCLK_HZ: Hertz = Hertz::from_raw(CLOCK_RATE_HZ.raw() / 4); | |
| // 49_152_344 | |
| // PLL1 | |
| const PLL1_P_HZ: Hertz = CLOCK_RATE_HZ; | |
| const PLL1_Q_HZ: Hertz = Hertz::from_raw(CLOCK_RATE_HZ.raw() / 18); | |
| const PLL1_R_HZ: Hertz = Hertz::from_raw(CLOCK_RATE_HZ.raw() / 32); | |
| // PLL2 | |
| const PLL2_P_HZ: Hertz = Hertz::from_raw(4_000_000); | |
| // PLL3 | |
| const FS: Hertz = Hertz::from_raw(48_000); | |
| const PLL3_P_HZ: Hertz = Hertz::from_raw(FS.to_Hz() * 256); | |
| static mut EP_MEMORY: MaybeUninit<[u32; 1024]> = MaybeUninit::uninit(); | |
| #[entry] | |
| fn main() -> ! { | |
| let dp = stm32::Peripherals::take().unwrap(); | |
| // Power | |
| let pwr = dp.PWR.constrain(); | |
| // let vos = pwr.freeze(); | |
| let vos = pwr.vos0(&dp.SYSCFG).freeze(); | |
| //ORIG | |
| // // RCC | |
| let rcc = dp.RCC.constrain(); | |
| let mut ccdr = rcc | |
| .use_hse(16.MHz()) // high speed external crystal @ 16 MHz | |
| .sys_ck(CLOCK_RATE_HZ) | |
| .pll1_strategy(rcc::PllConfigStrategy::Iterative) | |
| .pll1_p_ck(PLL1_P_HZ) | |
| .pll3_strategy(rcc::PllConfigStrategy::FractionalNotLess) | |
| .pll3_p_ck(PLL3_P_HZ) // used for SAI1 | |
| .freeze(vos, &dp.SYSCFG); | |
| // 48MHz CLOCK | |
| // let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run"); | |
| // ccdr.peripheral.kernel_usb_clk_mux(UsbClkSel::Hsi48); | |
| // let rcc = dp.RCC.constrain(); | |
| // let mut ccdr = rcc | |
| // .use_hse(HSE_CLOCK_MHZ.convert()) | |
| // .sys_ck(CLOCK_RATE_HZ) | |
| // .pclk1(PCLK_HZ) // DMA clock // Redundant? | |
| // .pclk2(PCLK_HZ) // DMA clock // Redundant? | |
| // // PLL1 | |
| // .pll1_strategy(rcc::PllConfigStrategy::Iterative) | |
| // .pll1_p_ck(PLL1_P_HZ) | |
| // .pll1_q_ck(PLL1_Q_HZ) | |
| // // .pll1_r_ck(PLL1_R_HZ) // Redundant? | |
| // // PLL2 | |
| // .pll2_p_ck(PLL2_P_HZ) // Default adc_ker_ck_input // Issue? | |
| // // PLL3 | |
| // .pll3_strategy(rcc::PllConfigStrategy::FractionalNotLess) | |
| // .pll3_p_ck(PLL3_P_HZ) // used for SAI1 | |
| // .freeze(vos, &dp.SYSCFG); | |
| // dp.RCC.cr.read().hserdy().is_not_ready(); | |
| // let mut ccdr = rcc | |
| // .use_hse(16.MHz()) // high speed external crystal @ 16 MHz | |
| // .pll1_strategy(rcc::PllConfigStrategy::Iterative) // pll1 drives system clock | |
| // .pll1_q_ck(48.MHz()) // required for SPI display | |
| // .pll3_strategy(rcc::PllConfigStrategy::Fractional) // ensure we get as close as possible to 12.288 MHz (audio clock) | |
| // .sys_ck(480.MHz()) // system clock @ 480 MHz | |
| // .pll3_p_ck(PLL3_P_HZ) // audio clock @ 12.288 MHz | |
| // .freeze(vos, &dp.SYSCFG); | |
| ccdr.peripheral.kernel_usb_clk_mux(stm32h7xx_hal::rcc::rec::UsbClkSel::Hsi48); | |
| let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run"); | |
| let _ = ccdr.clocks.hse_ck().expect("hse_ck must run"); | |
| // let _ = ccdr.clocks.hse_ck() | |
| // .expect("hse_ck must run"); | |
| // defmt::info!("{}", hz1.to_Hz()); | |
| // defmt::info!("{}", ccdr.clocks.hse_ck().unwrap().to_Hz()); | |
| // defmt::info!("{}", ccdr.clocks.hsi48_ck().unwrap().to_Hz()); | |
| // for _ in 0..100 { | |
| // nop(); | |
| // } | |
| //ORIG | |
| // // RCC | |
| // let rcc = dp.RCC.constrain(); | |
| // let mut ccdr = rcc.sys_ck(80.MHz()).freeze(vos, &dp.SYSCFG); | |
| // // 48MHz CLOCK | |
| // let _ = ccdr.clocks.hsi48_ck().expect("HSI48 must run"); | |
| // ccdr.peripheral.kernel_usb_clk_mux(UsbClkSel::Hsi48); | |
| // If your hardware uses the internal USB voltage regulator in ON mode, you | |
| // should uncomment this block. | |
| // unsafe { | |
| // let pwr = &*stm32::PWR::ptr(); | |
| // pwr.cr3.modify(|_, w| w.usbregen().set_bit()); | |
| // while pwr.cr3.read().usb33rdy().bit_is_clear() {} | |
| // } | |
| // IO | |
| let (pin_dm, pin_dp) = { | |
| let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); | |
| (gpiob.pb14.into_alternate(), gpiob.pb15.into_alternate()) | |
| }; | |
| // let (pin_dm, pin_dp) = { | |
| // let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); | |
| // (gpioa.pa11, gpioa.pa12) | |
| // }; | |
| let usb = USB1::new( | |
| dp.OTG1_HS_GLOBAL, | |
| dp.OTG1_HS_DEVICE, | |
| dp.OTG1_HS_PWRCLK, | |
| pin_dm, | |
| pin_dp, | |
| ccdr.peripheral.USB1OTG, | |
| &ccdr.clocks, | |
| ); | |
| // Initialise EP_MEMORY to zero | |
| unsafe { | |
| let buf: &mut [MaybeUninit<u32>; 1024] = | |
| &mut *(core::ptr::addr_of_mut!(EP_MEMORY) as *mut _); | |
| for value in buf.iter_mut() { | |
| value.as_mut_ptr().write(0); | |
| } | |
| } | |
| // Now we may assume that EP_MEMORY is initialised | |
| #[allow(static_mut_refs)] // TODO: Fix this | |
| let usb_bus = UsbBus::new(usb, unsafe { EP_MEMORY.assume_init_mut() }); | |
| // let mut serial = usbd_serial::SerialPort::new(&usb_bus); | |
| let mut midi_class = UsbMidiClass::new(&usb_bus, 1, 1).unwrap(); | |
| let usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x5e4)) | |
| .device_class(0) | |
| .device_sub_class(0) | |
| .strings(&[StringDescriptors::default() | |
| .manufacturer("Music Company") | |
| .product("MIDI Device") | |
| .serial_number("12345678")]) | |
| .unwrap(); | |
| let mut usb_dev = usb_dev.build(); | |
| defmt::info!("Built!"); | |
| loop { | |
| // info!("loop!"); | |
| if usb_dev.poll(&mut [&mut midi_class]) { | |
| // info!("poll!"); | |
| // Receive messages. | |
| let mut buffer = [0; 64]; | |
| if let Ok(size) = midi_class.read(&mut buffer) { | |
| let packet_reader = UsbMidiPacketReader::new(&buffer, size); | |
| for packet in packet_reader.into_iter().flatten() { | |
| if !packet.is_sysex() { | |
| // Just a regular 3-byte message that can be processed directly. | |
| let message = MidiMessage::try_parse_slice(packet.payload_bytes()); | |
| defmt::println!( | |
| "Regular Message, message: {:?}", | |
| // packet.cable_number(), | |
| message | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // loop { | |
| // if !usb_dev.poll(&mut [&mut serial]) { | |
| // continue; | |
| // } | |
| // let mut buf = [0u8; 64]; | |
| // match serial.read(&mut buf) { | |
| // Ok(count) if count > 0 => { | |
| // // Echo back in upper case | |
| // for c in buf[0..count].iter_mut() { | |
| // if 0x61 <= *c && *c <= 0x7a { | |
| // *c &= !0x20; | |
| // } | |
| // } | |
| // let mut write_offset = 0; | |
| // while write_offset < count { | |
| // match serial.write(&buf[write_offset..count]) { | |
| // Ok(len) if len > 0 => { | |
| // write_offset += len; | |
| // } | |
| // _ => {} | |
| // } | |
| // } | |
| // } | |
| // _ => {} | |
| // } | |
| // } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment