i would like to generate a Midi Clock signal with UART on a stm32f1 bluepill board. The Signal basicly just needs to send one byte (0xF8) at a maximum frequency of 128 hz to a 31250 bps serial interface. I created a minimal example using one of the STM32f1s Timers. The problem is, that the received signal on my midi gear does not seem to be very stable. It jumps between 319 and 321 bpm, whereas it should show a stable clock of 320bpm for a 128hz signal (the conversion formula: freq = bpm * 24 / 60). Do you have any idea why there is so much jitter? is it the serial implementation that creates that jitter or can it be a hardware problem? or is it the hal abstraction layer which introduces the jitter?
This is the time differences between the clock signals i measured for 124hz:
On the y axis is the time difference in useconds, on the x axis is the number of readings. 8000 us should be the correct time interval between signals. But in regular intervals there seems to be a signal fired with a time difference of only ~500. What could cause that? Maybe a Counter overflow?
After reducing the prescaler to 12mhz i got this pattern:
Here is the code that generates the clock signal
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use stm32f1xx_hal::{
pac,
pac::{interrupt, Interrupt, TIM4},
prelude::*,
gpio,
afio,
serial::{Serial, Config},
timer::{Event, Timer, CountDownTimer},
};
use core::mem::MaybeUninit;
use stm32f1xx_hal::pac::{USART2};
pub use embedded_hal::digital::v2::{OutputPin, InputPin};
pub type Usart2Serial = Serial<
USART2, (gpio::gpioa::PA2<gpio::Alternate<gpio::PushPull>>,
gpio::gpioa::PA3<gpio::Input<gpio::Floating>>)>;
// When a panic occurs, stop the microcontroller
#[allow(unused_imports)]
use panic_halt;
static mut G_TIM2: MaybeUninit<CountDownTimer<TIM4>> = MaybeUninit::uninit();
static mut G_SERIAL: MaybeUninit<Usart2Serial> = MaybeUninit::uninit();
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let mut flash = dp.FLASH.constrain();
let clocks = rcc.cfgr
.use_hse(8.mhz()) // set clock frequency to external 8mhz oscillator
.sysclk(72.mhz()) // set sysclock
.pclk1(36.mhz()) // clock for apb1 prescaler -> TIM1
.pclk2(36.mhz()) // clock for apb2 prescaler -> TIM2,3,4
.adcclk(12.mhz()) // clock for analog digital converters
.freeze(&mut flash.acr);
let mut apb1 = rcc.apb1;
let mut apb2 = rcc.apb2;
let mut gpioa = dp.GPIOA.split(&mut apb2);
let mut afio = dp.AFIO.constrain(&mut apb2);
// init serial
let mut serial = init_usart2(dp.USART2, gpioa.pa2, gpioa.pa3, &mut gpioa.crl, &mut afio, &clocks, &mut apb1);
unsafe { G_SERIAL.write(serial) };
// init timer
let bpm = 320;
let frequency_in_hertz : u32 = (bpm as u32) * 24 / 60;
let mut timer = Timer::tim4(dp.TIM4, &clocks, &mut apb1).start_count_down((frequency_in_hertz).hz());
timer.listen(Event::Update);
// write to global static var
unsafe { G_TIM2.write(timer); }
cortex_m::peripheral::NVIC::unpend(Interrupt::TIM4);
unsafe {
cortex_m::peripheral::NVIC::unmask(Interrupt::TIM4);
}
loop {
// do nothing
}
}
fn init_usart2(
usart2: USART2,
pa2: gpio::gpioa::PA2<gpio::Input<gpio::Floating>>,
pa3: gpio::gpioa::PA3<gpio::Input<gpio::Floating>>,
crl: &mut gpio::gpioa::CRL,
afio: &mut afio::Parts,
clocks: &stm32f1xx_hal::rcc::Clocks,
apb1: &mut stm32f1xx_hal::rcc::APB1
) -> Usart2Serial {
let tx = pa2.into_alternate_push_pull(crl);
let rx = pa3;
return Serial::usart2(
usart2,
(tx, rx),
&mut afio.mapr,
Config::default().baudrate(31250.bps()),
*clocks,
apb1,
);
}
#[interrupt]
fn TIM4() {
let serial = unsafe { &mut *G_SERIAL.as_mut_ptr() };
serial.write(0xF8).ok();
let tim2 = unsafe { &mut *G_TIM2.as_mut_ptr() };
tim2.clear_update_interrupt_flag();
}