- 主题:小新不懂就问:自己设计的开发板固件怎么搞定的?
看到jlc上很多自己设计stm32,esp32等开发板的,他们怎么搞定固件的,很多底层重写一遍真是很伤吧。
--
FROM 112.32.39.*
没想到你这个问题居然是个问题。搞不定软件开发,搞啥“开发”板呢。 开发板就是用来开发的啊
--
FROM 113.89.9.36
我都底层C重写了一遍类RTOS,写固件是个问题?
【 在 annodom 的大作中提到: 】
: 看到jlc上很多自己设计stm32,esp32等开发板的,他们怎么搞定固件的,很多底层重写一遍真是很伤吧。
--
FROM 180.116.135.*
好吧,我想简单了。。。
看到有些高中生都搞开发板,觉得有点想不到,没想到是这么厉害啊。
【 在 zdg102 的大作中提到: 】
: 没想到你这个问题居然是个问题。搞不定软件开发,搞啥“开发”板呢。 开发板就是用来开发的啊
--
FROM 112.32.39.*
膜拜,前段时间写个ADS的驱动都头大打的很了。。。
【 在 dismoon 的大作中提到: 】
: 我都底层C重写了一遍类RTOS,写固件是个问题?
:
--
FROM 112.32.39.*
ESP32有些是跑arduino的,不一定要写底层驱动。STM32官方都给你自动生成了。
--
FROM 218.13.181.*
我又忍不住又要来安利rust了...
我最近通过一个项目已经基本完全迁移到rust上开发了。
目前在手撸一个运控。
我是感觉到rust的抽象能力实在是太爽了,比如我是先定义mm,um,丝,inch等公制,英制单位,(注意丝这个单位英文里没有对应的单词,于是我直接用了中文...)
比如驱动频率有hz,khz,mhz。旋转运动有rpm,feedrate有mm/min,ipm等单位...
写死之后基本上不可能出错。因为整个系统不允许任何出现无量纲的数字...
比如设定电机转速得写成set_rotary_speed(3000.rpm()),不能直接写个3000进去。
比如pwm频率得写成10.khz(),不能直接写10000。
然后除了量纲的区分外,每个值的适用领域也有区分。
比如move_to如果传的是Position(100.mm())表示绝对定位100mm,如果没有做过homing就会报错。
但这没有homing的情况下可以传Displacement(100.mm()),表示从当前位置开始算的相对位移。
最后,不仅传入的数字不允许出现无量纲,这些数值之间内部是否可以计算的关系,计算的结果类型也都准确定义了,比如Position-Position得到Distance等等,这样不管是外部给别人用,还是内部写逻辑,还是debug dump内部状态,都很清晰并且不太可能写错。
最后回到rtos的问题。rust有个框架叫RTIC,用async/await的形式直接就提供了异步支持,直接拿来用就行了:
#[task(
priority = 2,
shared = [led],
local = [tog: bool = true],
)]
async fn heartbeat(mut c: heartbeat::Context) {
loop {
info!("heartbeat, tog = {}", *c.local.tog);
if *c.local.tog {
c.shared.led.lock(|l| l.set_high().unwrap());
} else {
c.shared.led.lock(|l| l.set_low().unwrap());
}
*c.local.tog = !*c.local.tog;
rtic_monotonics::systick::Systick::delay(500.millis()).await;
}
}
#[task(
priority = 1,
shared = [timer, cap_displacement_sensor],
)]
async fn displacement_update(mut c: displacement_update::Context) {
loop {
let now = c.shared.timer.lock(|timer| timer.get_counter());
info!("now = {}", now.ticks());
c.shared.cap_displacement_sensor.lock(|cds| {
info!("{:?}", cds.read_spi_and_feed(now));
});
// Period to read the displacement.
rtic_monotonics::systick::Systick::delay(10.millis()).await;
}
}
总的来说,这个就叫降维打击,实在是太爽了。rust会逼着你去思考,逼着你只能规划好资源的分配逻辑,逼着你只能写出正确的代码。
比如上面的代码有很多unwrap,看着很丑。这种丑就是rust在逼你把代码写成带错误处理的模式。
因为我目前故障处理这块还没做完,所以先unwrap跑起来再说。但等把所有的故障都规划好设计好,并把代码都改成带故障处理的模式之后,这些unwrap就不会再存在了。
最主要的是,我前几年尝试切rust,每次都被它陡峭的学习曲线打击了,导致半途而废。但现在有chatgpt,有啥不懂得直接请教ai就行了。学rust难度降低了一个数量级,现在转真心不是什么问题了。
【 在 dismoon 的大作中提到: 】
: 我都底层C重写了一遍类RTOS,写固件是个问题?
:
--
修改:lvsoft FROM 121.225.189.*
FROM 121.225.189.*
嵌入式是纯C的天下,因为提升的那点开发效率相对后期调试阶段代码可读性要求与项目交接人员招聘难度,可能得不偿失。
【 在 lvsoft 的大作中提到: 】
: 我又忍不住又要来安利rust了...
: 我最近通过一个项目已经基本完全迁移到rust上开发了。
: 目前在手撸一个运控。
: ...................
--
FROM 60.178.169.*
自信点,去掉可能。
【 在 conepoint 的大作中提到: 】
: 嵌入式是纯C的天下,因为提升的那点开发效率相对后期调试阶段代码可读性要求与项目交接人员招聘难度,可能得不偿失。
--
FROM 111.18.33.*
没深刻体会过rust开发的好处,是无法理解我前面说的“降维打击”这四个字的厚重的。
所谓的后期调试阶段之类的优劣,只有在同层次的情况下才好谈。而rust压根就不需要什么后期调试阶段。
举个例子,比如我这块的代码:
let spi_mosi = pins.gpio15.into_push_pull_output();
let spi_miso = pins.gpio12.into_floating_input();
let spi_sclk = pins.gpio14.into_floating_input();
let spi = bsp::hal::spi::Spi::<_, _, _, 8>::new(pac.SPI1, (spi_mosi, spi_miso, spi_sclk));
let mut spi = spi.init_slave(&mut pac.RESETS, embedded_hal::spi::MODE_3);
注意看最后一行,一开始我也不知道怎么用spi,抄的example里面例子。example里用的是spi.init,这个是master模式。而我需要的slave模式要用init_slave初始化,但这一切我当时都不知道。
放c环境里原样照抄的代码肯定是能跑的。然后你就会发现不work,然后你就得拿示波器测来测去。运气不好没第一时间怀疑到这里,可能还要搭个最小系统,好多天就搭进去了。这就是所谓的“后期调试阶段”。
但在rust里,这里原样照抄来的spi.init是编译不过的。因为rust知道spi.init是master模式,而master模式会把sck配置成输出,而我在前面的声明中定义了spi sck是输入。编译器直接就会把这个矛盾指出来。这就是所谓的最好的解决问题的办法,是让这个问题直接不存在。这就是所谓的你会被逼着只能写出尽量正确的代码。
而且,rust本身就是个很完美的hal抽象层。事实上我现在的代码就可以同时跑在rp2040和esp32上。
包括esp32的Xtensa core和risc-v core。
并且,因为我还要被结构,机械设计等各方面的问题分心。所以firmware这块是我朋友和我一起写的。他是cs出生,连spi是个啥都不知道,纯嵌入式新手。而且他在地球另一头,跟我有时空阻隔,连个硬件开发环境都没有,根本就不可能联调。但他写的代码就是拿过来直接跑,直接就ok的。后期调试?项目交接?这些都不存在。
至于招聘难度,这个肯定会比招c要难。但就如我前面说的,在ai的辅助下一个老手熟练掌握rust,也就是一个月的时间而已。而rust+ai释放的生产力足以顶一个团队。找到一两个高手让他转rust就行,这一点并不难。
【 在 conepoint 的大作中提到: 】
: 嵌入式是纯C的天下,因为提升的那点开发效率相对后期调试阶段代码可读性要求与项目交接人员招聘难度,可能得不偿失。
:
--
FROM 121.225.189.*