【问题标题】:Jitter Problem on Sending Clock Signal over Serial on STM32F103 (Bluepill)在 STM32F103 (Bluepill) 上通过串行发送时钟信号的抖动问题
【发布时间】:2021-10-03 12:39:09
【问题描述】:

我想在 stm32f1 bluepill 板上使用 UART 生成 Midi 时钟信号。信号基本上只需要以 128 Hz 的最大频率向 31250 bps 串行接口发送一个字节 (0xF8)。我使用 STM32f1s 定时器之一创建了一个最小示例。问题是,我的 midi 设备上接收到的信号似乎不是很稳定。它在 319 和 321 bpm 之间跳跃,而对于 128hz 信号,它应该显示 320bpm 的稳定时钟(转换公式:频率 = bpm * 24 / 60)。你知道为什么会有这么多抖动吗?是串行实现导致抖动还是硬件问题?还是 hal 抽象层引入了抖动?

这是我测量的 124hz 时钟信号之间的时间差:

y 轴上是以 u 秒为单位的时间差,x 轴上是读数的数量。 8000 us 应该是信号之间的正确时间间隔。但在固定的时间间隔内,似乎有一个信号发射,时差仅为约 500。是什么原因造成的?也许是计数器溢出?

将预分频器降低到 12mhz 后,我得到了这个模式:

这是生成时钟信号的代码

#![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();
}

【问题讨论】:

    标签: rust embedded hal stm32f1


    【解决方案1】:

    好的,我自己找到了解决方案。它似乎与 Timers 计数器有关。我猜它会溢出并以错误的时间间隔触发计时器。

    在定时器中断函数中添加以下行会重置计数器并消除抖动:

    #[interrupt]
    fn TIM4() {
      ...
      tim2.reset();
    }
    

    【讨论】:

      【解决方案2】:

      您需要确认中断的优先级,以确保没有其他更高优先级的中断会延迟 UART 输出。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-12
        • 1970-01-01
        • 2021-11-04
        • 1970-01-01
        • 2011-05-18
        • 1970-01-01
        相关资源
        最近更新 更多