0

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:

enter image description here

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:

enter image description here

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();
}
Lut Ze
  • 123
  • 2
  • 10

2 Answers2

0

Ok, i found the solution by myself. It seems to be connected with the Timers counter. I guess it overflows and triggers the timer at a wrong interval.

adding the following line to the timer interrupt function resets the counter and removes the jitter:

#[interrupt]
fn TIM4() {
  ...
  tim2.reset();
}
Lut Ze
  • 123
  • 2
  • 10
0

You need to confirm the priority of your interrupt to make sure there isn't other higher priority interrupts that delay the UART output.

Spectrem
  • 682
  • 1
  • 11
  • 37