- 主题:GD32的库真是写的稀烂
f4的usb我玩过,也一样有不稳定问题。
【 在 tom6bj (tom) 的大作中提到: 】
: 对了, f4的usb你玩了没? f1和f3似乎能通用, f0也差不多, 但是到f4完全变样了
--
FROM 49.94.69.*
没看过gd32的库,但我觉得stm32的库确实就是稀烂,绝对不是这点点命名风格问题。
我最近开始尝试用rust做stm32的开发了,目前感觉良好。
rust的好处有几个,一个是非常严格的编译期代码检查,基本上能通过编译的代码就不会
有c里面的内存管理问题。对于嵌入式设备来说,你只要操行逻辑问题即可,不用担心代
码会跑飞。
第二个是rust能做到zero cost abstraction,不像c的hal库,一用编译出来的尺寸
就暴涨。
第三个是rust有很强的描述能力,都2020年了,用c做点事情实在是太折腾了。用了
rust总算有点新时代语言的感觉了...
第四个是目前rust还相对小众,但这种语言设计风格掌握的门槛较高,反过来也只有高手
和对某些方面有完美主义追求的人才会使用,所以目前rust的库代码质量相对较高,是我
看到的各种生态中质量最高的,没有之一。
可以去github看看example感受下:
https://github.com/stm32-rs/stm32f1xx-hal/tree/master/examples
我选个adc的例子贴下,这tm才叫HAL库啊,之前的stm32官方库那写的都是什么鬼东西。
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use panic_semihosting as _;
use cortex_m_rt::entry;
use stm32f1xx_hal::{adc, pac, prelude::*};
use cortex_m_semihosting::hprintln;
#[entry]
fn main() -> ! {
// Acquire peripherals
let p = pac::Peripherals::take().unwrap();
let mut flash = p.FLASH.constrain();
let mut rcc = p.RCC.constrain();
// Configure ADC clocks
// Default value is the slowest possible ADC clock: PCLK2 / 8. Meanwhile ADC
// clock is configurable. So its frequency may be tweaked to meet certain
// practical needs. User specified value is be approximated using supported
// prescaler values 2/4/6/8.
let clocks = rcc.cfgr.adcclk(2.mhz()).freeze(&mut flash.acr);
hprintln!("adc freq: {}", clocks.adcclk().0).unwrap();
// Setup ADC
let mut adc1 = adc::Adc::adc1(p.ADC1, &mut rcc.apb2, clocks);
#[cfg(feature = "stm32f103")]
let mut adc2 = adc::Adc::adc2(p.ADC2, &mut rcc.apb2, clocks);
// Setup GPIOB
let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
// Configure pb0, pb1 as an analog input
let mut ch0 = gpiob.pb0.into_analog(&mut gpiob.crl);
#[cfg(feature = "stm32f103")]
let mut ch1 = gpiob.pb1.into_analog(&mut gpiob.crl);
loop {
let data: u16 = adc1.read(&mut ch0).unwrap();
hprintln!("adc1: {}", data).unwrap();
#[cfg(feature = "stm32f103")]
{
let data1: u16 = adc2.read(&mut ch1).unwrap();
hprintln!("adc2: {}", data1).unwrap();
}
}
【 在 tom6bj (tom) 的大作中提到: 】
: 具体怎么烂法, 举个例子?
: 我是觉得取寄存器时用函数调用风格的RDATA(USART1)之类不如stm32官方库的USART1->RDR看着舒
服, 不过纯小写+下划线的命名风格比匈牙利式看起来更顺眼.
: 个人喜好问题吧
: ...................
--
修改:lvsoft FROM 49.94.69.*
FROM 49.94.69.*
没多少吧...这个可读性应该是一目了然的,学没学过没影响。
里面出现的比较多的是mut和unwrap,
mut是声明这是变量,rust默认“变量”都是immutable的...
rust作为新时代的语言,比较鼓励用函数式的方式写代码,所以“变量”是immutable的
情况比较多,mutable才是少数。或者准确的说,变量这个概念就是问题的根源,能把这
个概念消除的越彻底,麻烦就越少。
unwrap是新语言里常用的异常处理方式,比如你要返回一个数字42,但这个操作可能会
失败。c里面是返回-1这种作为errno,c++里是抛出异常,rust里是包裹一层返回
Some(42),如果错误就返回None。
这个包裹的一层在使用的时候自然需要unwrap,这个时候就可以检查是否产生错误了。
所以里面出现了很多unwrap。其实可以用一个?代替,但在main里因为一些细节问题
不能这么操作。
hprintln!这个!号其实是宏...还有#[xxx]这个也是宏...rust的宏比较的骚操作...
前者是函数宏还比较简单,类似于强化版的c宏;后者是过程宏...直接丢文法解析器的
token stream给你自己看着办...所以理论上你可以用过程宏实现任何语言...
同时也是为啥rust可以做到zero cost,因为这里本质上是meta programming。
此外还有&这个符号,是rust最为核心的生命周期管理的东西。看起来像c++的引用,但
在rust里叫借用。rust里每个资源都有且仅有一个owner,如果你要把值传递给别的变
量,要么owner转移过去,要么借用之后归还。rust就是靠这个提供任何资源在生命周期
内只能有且只有一个地方可以控制的严格保证的。
rust学习门槛是比较高的,我目前也还处于新手阶段。其实我2-3年前就想用rust搞mcu
开发了,连续好几年每年新年愿望都是“今年一定要学rust...”,还是得感谢这一轮疫
情...
【 在 tom6bj (tom) 的大作中提到: 】
: 以后有空试试
: 引入的新字符不少啊, 猛一看有点晕...
--
修改:lvsoft FROM 49.94.69.*
FROM 49.94.69.*
总之,强烈安利rust就对了~
我目前准备从嵌入式和系统开发到web开发各个层面都用rust替换~
【 在 tom6bj (tom) 的大作中提到: 】
: 好吧,虽然没多少, 也不少了...
--
FROM 180.109.234.*
rust有个rust-yew,基于webassembly做的界面。
其实我对传统的web不是很感冒,我想要的就是gui搬到web上去。
这个就非常对我路子。
你可以看看这个,这个从最底层的在mcu上的gcode parser,到后台到web端,全部是
用rust做的
【 在 javaboy (喝了咖啡就话多-_-;) 的大作中提到: 】
: 哈哈,web你就别想了。。。
--
FROM 180.109.234.*
这是我目前在经历的过程。
就跟python有pythonic的表述习惯一样,
我目前还没掌握rusty的思维方式,写的代码看起来就比较的别扭。
【 在 zkr (Just Waiting.) 的大作中提到: 】
: 扫一下官网的Rust Book,粗看代码就大体没啥障碍了
: 然则写出对的代码是另一码事儿,需要和编译器进行心智校准
: 为了给编译期提供足够的信息,源码有时候看着会犯晕
--
修改:lvsoft FROM 180.109.234.*
FROM 180.109.234.*
更要命的是这样的库还有peripheral,hal,ll三套...基本上是重新定义了“烂的最
高境界”...
在rust下不需要折腾makefile,折腾各种库的编译环境,各种库都是模块化的直接
装了就有(比如lvgl)。跨平台方面虽然不能做到完全不改代码跨平台,但至少不
同器件的hal表述是一致的,换平台只要略做features的调整就行。最重要的是rust
的解耦合做很漂亮,可以在host pc上直接unittest。semihosting,itm之类的支持
也都是模块化支持了,完全不需要折腾,导入就有。
且不说hal库,整个stm32cube在基于rust的开发环境面前都毫无意义了。
rust目前的问题有几方面,一个是学习曲线比较陡峭,我学的比较折腾的语言也不
少了,rust在这里面依然是排前列的;二是目前各种实现都是0.x版本,都在快速演
变中,文档有点跟不上。比如搜stm32 rust很容易看到一篇2018年的一个新加坡人
写的教程,那个就已经outdate了;三是rust编译比较慢,它不像c/c++,通过.o可
以避免重复编译。rust是所有代码连带库一起整体编译的,所以它没有跨.o文件带
来的符号连接开销,但编译速度确实是比较慢的。当然这也是为了zero cost付出的
合理代价。
【 在 eggcar (eggcar) 的大作中提到: 】
: st的hal库真的设计的一坨shit...不知道从哪个角度定出来的路线,成功做到了
既没跟底层硬件解耦,又浪费了百分之好几百的性能,写起来还特别反人类
--
修改:lvsoft FROM 180.109.234.*
FROM 180.109.234.*
编译速度这块是rust目前还在努力解决的问题...
这个主要还是涉及优化的问题。
rust的check是很快的,但compile就比较慢了。
【 在 tom6bj (tom) 的大作中提到: 】
: 要是能把库预编译成.a也能快点啊...
--
FROM 180.109.234.*
贴个usb的例子,如果用寄存器要写死人吧?
//! CDC-ACM serial port example using interrupts.
//! Target board: Blue Pill
#![no_std]
#![no_main]
extern crate panic_semihosting;
use cortex_m::asm::{delay, wfi};
use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use stm32f1xx_hal::pac::{interrupt, Interrupt};
use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType};
use stm32f1xx_hal::{prelude::*, stm32};
use usb_device::{bus::UsbBusAllocator, prelude::*};
use usbd_serial::{SerialPort, USB_CLASS_CDC};
static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None;
static mut USB_SERIAL: Option<usbd_serial::SerialPort<UsbBusType>> = None;
static mut USB_DEVICE: Option<UsbDevice<UsbBusType>> = None;
#[entry]
fn main() -> ! {
let p = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc
.cfgr
.use_hse(8.mhz())
.sysclk(48.mhz())
.pclk1(24.mhz())
.freeze(&mut flash.acr);
assert!(clocks.usbclk_valid());
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
// BluePill board has a pull-up resistor on the D+ line.
// Pull the D+ pin down to send a RESET condition to the USB bus.
// This forced reset is needed only for development, without it host
// will not reset your device when you upload new firmware.
let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
usb_dp.set_low();
delay(clocks.sysclk().0 / 100);
let usb_dm = gpioa.pa11;
let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh);
let usb = Peripheral {
usb: dp.USB,
pin_dm: usb_dm,
pin_dp: usb_dp,
};
// Unsafe to allow access to static variables
unsafe {
let bus = UsbBus::new(usb);
USB_BUS = Some(bus);
USB_SERIAL = Some(SerialPort::new(USB_BUS.as_ref().unwrap()));
let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd))
.manufacturer("Fake company")
.product("Serial port")
.serial_number("TEST")
.device_class(USB_CLASS_CDC)
.build();
USB_DEVICE = Some(usb_dev);
}
let mut nvic = p.NVIC;
nvic.enable(Interrupt::USB_HP_CAN_TX);
nvic.enable(Interrupt::USB_LP_CAN_RX0);
loop {
wfi();
}
}
#[interrupt]
fn USB_HP_CAN_TX() {
usb_interrupt();
}
#[interrupt]
fn USB_LP_CAN_RX0() {
usb_interrupt();
}
fn usb_interrupt() {
let usb_dev = unsafe { USB_DEVICE.as_mut().unwrap() };
let serial = unsafe { USB_SERIAL.as_mut().unwrap() };
if !usb_dev.poll(&mut [serial]) {
return;
}
let mut buf = [0u8; 8];
match serial.read(&mut buf) {
Ok(count) if count > 0 => {
// Echo back in upper case
for c in buf[0..count].iter_mut() {
if 0x61 <= *c && *c <= 0x7a {
*c &= !0x20;
}
}
serial.write(&buf[0..count]).ok();
}
_ => {}
}
}
【 在 dismoon (伐开心要包包) 的大作中提到: 】
: 所以为啥你非要用HAL,寄存器操作不香么
--
FROM 180.109.234.*
编译慢对于mcu开发问题不大。因为mcu上要塞的东西本来就不多。
编译出的文件大小和c的全面对比还要再往后放放,目前我也还在探索中。
你更关心极限最小体积,这块我觉得要精通rust之后才行,眼下我的熟练度还完全
不够。而且单对比hal很难说是否公平。毕竟stm32的hal和rust的hal完全不一样。
我只能说rust在机制上对比c是有优势的,但rust比c复杂的多,不同选项下有的东
西一开可能很容易就超了。
所以我目前是在stm32f0上开发找个real case综合测试下。64K rom,8K ram,塞个
gui进去,含字库,再加一些常见的逻辑,i2c啊,timer啊,一些逻辑控制啊,485
通讯啊等等。之前用c基于hal做,把所有优化措施都用上之后非常勉强的能塞进
去。rust这块我还没把之前的gui port完暂时还不知道,只能说先搞个感性认识。
真要折腾极限测试需要投入巨大精力,rust和c两边都要做极限尺寸优化之后再pk才
有价值,我估计我是没时间去做这个事情了~
另一方面在系统编程方面,我也开始用rust代替之前用python做的项目了。目前大
概是python一天完成的活用rust干了20天...当然目前我在两方面的熟练度上存在巨
大差异...rust下我知道怎么用meta programming做的更快的,但熟练度不够暂时无
法支撑这些想法...
【 在 spadger (imdx) 的大作中提到: 】
: 编译慢可以等等,生成的二进制文件尺寸和C比如何?我比较关心这个。
: HAL库太臃肿了,只要用了,目标文件就小不了。
--
修改:lvsoft FROM 180.109.234.*
FROM 180.109.234.*