我想在stm32f1 bluepill板上用串口产生一个Midi时钟信号。该信号基本上只需要以128Hz128 hz的最大频率向31250 bps串行接口发送一个字节(0xF8)。我使用其中一个STM32f1s计时器创建了一个最小示例。问题是,我的midi设备上接收到的信号似乎不是很稳定。它在319到321 bpm之间跳跃,而对于128 60的信号,它应该显示320bpm的稳定时钟(转换公式: freq = bpm * 24 / 60)。你知道为什么会有这么多的抖动吗?是串行实现造成了抖动,还是硬件问题?或者是引入抖动的hal抽象层?
这是我在124赫兹测量的时钟信号之间的时间差:
Y轴是以秒为单位的时间差,x轴是读数。8000 us应该是信号之间的正确时间间隔。但每隔一段时间,似乎就会有一个时差只有500左右的信号发出。是什么导致了这种情况?也许是计数器溢出?
在将预分频器降低到12 the后,我得到了这个模式:
下面是生成时钟信号的代码
#![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();
}
发布于 2021-10-03 21:13:07
好了,我自己找到了解决方案。它似乎和计时器计数器有关。我猜它溢出并在错误的时间间隔触发计时器。
将以下行添加到定时器中断函数可复位计数器并消除抖动:
#[interrupt]
fn TIM4() {
...
tim2.reset();
}
发布于 2021-10-04 19:50:18
您需要确认中断的优先级,以确保没有其他优先级更高的中断会延迟UART输出。
https://stackoverflow.com/questions/69424911
复制相似问题