1 个不稳定版本
0.1.0 | 2024年8月5日 |
---|
#373 在 嵌入式开发
每月 110 次下载
36KB
439 行
PCB Artists I2C SPL模块嵌入式HAL驱动程序
为嵌入式-hal编写的Rust no_std
驱动程序,用于PCB Artists I2C 声级模块。
功能
- 读取时间窗口(TAVG 寄存器)内平均的当前SPL值,范围从35 dB到120 dB(±2 dB),频率从30 Hz到8 kHz。
- 可调整的时间窗口(TAVG 寄存器)以从10 ms到10,000 ms平均SPL值。
- 读取在电源周期或复位之间感应到的最小/最大SPL值。
用法
此示例使用STM32F3 Discovery开发板和USB-TTL转换器与SPL模块。
请参阅示例项目以获取完整示例。
点击显示Cargo.toml。
[package]
name = "example-read-decibel-value"
description = "Example of reading SPL with the pa-spl driver, PCB Artists SPL module, and STM32F3 Discovery"
authors = ["Jason Scott <>"]
edition = "2021"
publish = false
readme = "README.md"
version = "0.1.0"
[dependencies]
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
cortex-m-semihosting = "0.5.0"
panic-halt = "0.2.0"
stm32f3xx-hal = { version = "0.10.0", features = ["ld", "rt", "stm32f303xc"] }
pa-spl = "0.1.0"
[[bin]]
name = "example-read-decibel-value"
test = false
bench = false
[profile.release]
codegen-units = 1
debug = true
lto = true
// Reads the latest decibel value and prints it to UART4.
#![no_main]
#![no_std]
use cortex_m::asm;
use cortex_m_rt::entry;
// Use halt as the panicking behavior.
//
// A breakpoint can be set on `rust_begin_unwind` to catch panics.
//
use pa_spl::PaSpl;
use panic_halt as _;
use stm32f3xx_hal::{delay::Delay, i2c::I2c, pac, prelude::*, serial::config, serial::Serial};
use core::fmt::Write;
// Provide an implementation of a buffer writer in order to use the write!
// macro.
//
struct BufWriter<'a> {
buf: &'a mut [u8],
pos: usize,
}
impl<'a> BufWriter<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
BufWriter { buf, pos: 0 }
}
pub fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf[..self.pos]).unwrap()
}
pub fn reset(&mut self) {
self.pos = 0;
self.buf.fill(0);
}
}
// Provide implementation of write_str in order to use the buffer writer with
// the write! formatting macro.
//
impl<'a> core::fmt::Write for BufWriter<'a> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let bytes = s.as_bytes();
let len = bytes.len();
if self.pos + len > self.buf.len() {
return Err(core::fmt::Error);
}
self.buf[self.pos..self.pos + len].copy_from_slice(bytes);
self.pos += len;
Ok(())
}
}
#[entry]
fn main() -> ! {
// Get peripherals.
//
// take() returns an Option, which requires handling the possibility of the
// return of an Err or None instead of the desired value, which is of type
// pac::Peripherals in this case.
//
// Since this is an embedded application, it's not as simple as writing to
// stdout. This is a minimal example, so we'll drop into an inifinite loop
// to allow a debugger to find the failure.
//
let device_periphs = pac::Peripherals::take().unwrap_or_else(|| {
loop {
// Failed to take cortex_m::Peripherals.
asm::nop(); // If real app, replace with actual error handling.
}
});
// Get RCC peripheral and configure clocks.
//
// The constrain() method is used here to provide a higher-level abstraction
// of the peripheral rather than raw register access. The method consumes
// the raw peripheral and returns an instance of the RCC peripheral with
// higher-level safe abstractions provided by the HAL, which is of type Rcc,
// while setting the system clock frequency.
//
let mut rcc = device_periphs.RCC.constrain();
let mut flash = device_periphs.FLASH.constrain();
let clocks = rcc.cfgr.sysclk(48.MHz()).freeze(&mut flash.acr);
// Set up delay capability.
//
// Use the same unwrap method to get the core periphs, then
// create a delay abstraction using SysTick (SYST).
//
let core_periphs = cortex_m::Peripherals::take().unwrap_or_else(|| {
loop {
// Failed to take cortex_m::Peripherals.
asm::nop(); // If real app, replace with actual error handling.
}
});
let mut delay = Delay::new(core_periphs.SYST, clocks);
// Get GPIO Ports B and C.
//
// The split method here splits out the functionality of the GPIO Port B/C
// while taking a mutable borrow of an "enabler" that enables the clock for
// the port at the same time. The mutable borrow allows modification of the
// borrowed value while ensuring exclusive access.
//
let mut gpiob = device_periphs.GPIOB.split(&mut rcc.ahb);
let mut gpioc = device_periphs.GPIOC.split(&mut rcc.ahb);
// Configure pins PB6 as SCL and PB7 as SDA for I2C1.
//
let mut scl =
gpiob
.pb6
.into_af_open_drain(&mut gpiob.moder, &mut gpiob.otyper, &mut gpiob.afrl);
let mut sda =
gpiob
.pb7
.into_af_open_drain(&mut gpiob.moder, &mut gpiob.otyper, &mut gpiob.afrl);
scl.internal_pull_up(&mut gpiob.pupdr, true);
sda.internal_pull_up(&mut gpiob.pupdr, true);
// Create an instance of I2C1 with the pins.
//
let i2c = I2c::new(
device_periphs.I2C1,
(scl, sda),
100.kHz().try_into().unwrap(),
clocks,
&mut rcc.apb1,
);
// Configure GPIO pins PC10 as TX and PC11 as RX for UART4.
//
let tx_pin = gpioc
.pc10
.into_af_push_pull(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh);
let rx_pin = gpioc
.pc11
.into_af_push_pull(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrh);
// Create an instance of UART4 with the pins.
//
let mut uart4 = Serial::new(
device_periphs.UART4,
(tx_pin, rx_pin),
config::Config::default().baudrate(115_200.Bd()),
clocks,
&mut rcc.apb1,
);
// Use the I2C1 instance to create an instance of PaSpl.
//
let mut pa_spl = PaSpl::new(i2c);
// Create a buffer able to be converted to a string.
//
let mut buffer: [u8; 8] = [0; 8];
let mut buf_writer = BufWriter::new(&mut buffer);
// Algo delay in milliseconds.
//
const ALGO_DELAY_MS: u16 = 500;
loop {
// Reset the buffer at the start of each iteration
//
buf_writer.reset();
// Get SPL value from the sensor.
//
let spl = pa_spl.get_latest_decibel().unwrap();
// Format string with SPL value, then covert to string.
//
write!(buf_writer, "SPL: {}\r", spl).unwrap();
let spl_str = buf_writer.as_str();
// Write the string out to the UART.
//
uart4.write_str(spl_str).unwrap_or_else(|_| {
loop {
// Failed to write to UART4.
asm::nop(); // If real app, replace with actual error handling.
}
});
// Limit algorithm to (1000 * (1 / UART_WRITE_DELAY_MS)) Hz.
//
delay.delay_ms(ALGO_DELAY_MS);
}
}
测试
包括两个测试套件 - 一个包含在驱动程序源中的离标单元测试套件,该套件使用嵌入式-hal-mock,以及一个硬件在环(HIL)测试套件,该套件作为嵌套cargo项目包含,遵循模式,该模式由Ferrous Systems描述,用于测试驱动程序存储库。
要运行离标测试
cargo test
HIL测试项目位于target-tests
目录中,并配置为使用probe-rs自动构建测试、闪存目标、运行测试并报告结果。
要运行HIL测试,根据仓库中提供的STM32F3 Discovery示例连接硬件(不包括TTL-USB转换器)与PCB Artists传感器的频谱分析版本,固件版本0x32或0x33(从VERSION寄存器读取的数字),然后
cd target-tests
cargo test
CI配置在每次提交和PR中运行针对Rust 1.65、稳定版、beta版和nightly版的测试。由于将本地GitHub Actions运行器附加到公开存储库存在安全问题,因此HIL测试是私下运行的,但任何人都可以在自己的硬件上本地运行它们。
最低支持的 Rust 版本 (MSRV)
本软件包保证在稳定 Rust 1.65 及以上版本上编译。它可能可以用较老版本编译,但这一点可能会在任何新的补丁版本中发生变化。
有关如何升级 MSRV 的详细信息,请参阅此处。
最低支持的嵌入式 HAL 版本
TL;DR:此初始版本仅支持嵌入式-hal 0.2,后续版本中将添加对 1.0 版本的支持。
此软件包依赖于嵌入式-hal软件包,因为它是用于嵌入式-hal 的驱动程序。嵌入式版本通常比主流版本更新速度慢得多,因此存储库中的许多软件包仍然依赖于嵌入式-hal 的0.2版本,而不是最近的 1.0 版本。因此,此软件包的最低支持的嵌入式-hal 版本是 0.2,1.0 版本在此初始版本中尚不支持。将在后续版本中添加对嵌入式-hal 1.0 版本的支持。
术语表
- API:应用程序编程接口。
- HAL:硬件抽象层。
- PCB:印刷电路板。
- I2C:集成电路互连协议。
- SPL:声压级。
许可
根据您的选择,本软件包受Apache 许可证 2.0 版或MIT 许可证的许可。除非您明确声明,否则根据 Apache-2.0 许可证定义,您有意提交给 pa-spl 的任何贡献都应按照上述方式双许可,而不附加任何额外条款或条件。
贡献
除非您明确声明,否则根据 Apache-2.0 许可证定义,您有意提交给本作品的所有贡献都应按照上述方式双许可,而不附加任何额外条款或条件。依赖关系
~0.6-1.1MB
~24K SLoC