[IT技术] 主流的单片机语言是 C 吗?是的话为啥不是 C++?

[复制链接]
可人 发表于 2023-10-9 02:32:17|来自:北京 | 显示全部楼层 |阅读模式
主流的单片机语言是 C 吗?是的话为啥不是 C++?
全部回复5 显示全部楼层
klklklkl 发表于 2023-10-9 02:33:06|来自:北京 | 显示全部楼层
“你或许应该试试rust,bro”(大误
(两个人撒尿.jpg)
现在基于arm和riscv的基本都在用gcc,如果你打开cray pointer再配合特定的链接脚本并且成功避免运行时库的调用,用Fortran 写单片机也不是不可能。
C++是一样的道理,所有持C++慢或者占空间大的观点的都是半吊子。C++可以写单片机,我写过一些基于C++的单片机程序,它们的性能并不比用C 写低,而且关掉异常特性的话固件尺寸也差不多。在一部分场景下,C++对人来说更容易通过更大的代码段换更高的性能。
qswh 发表于 2023-10-9 02:33:50|来自:北京 | 显示全部楼层
应该说,无操作系统或者只有极简操作系统的单片机的主流语言是c和汇编,因为其他语言很难离开操作系统的支持。
phpstyle 发表于 2023-10-9 02:34:34|来自:北京 | 显示全部楼层
高赞说的其实很有道理。。就是:
人家单片机厂商是做硬件的。。软件这种事情。。
对付对付就好了。。
C是实现起来最简单的高级语言编译系统了。。
cgbbs 发表于 2023-10-9 02:35:22|来自:北京 | 显示全部楼层
目前主流确实还是c。但是以目前的条件看,切换为c++,应该没太大的技术限制,纯粹是长期积累下来的习惯。
当然,单片机内大多跑的业务,无论是规模上还是复杂度上,也没有到非用c++不可的地步。
人都或多或少有那么点惰性的。
zjok 发表于 2023-10-9 02:35:59|来自:北京 | 显示全部楼层
我从17年开始 单片机项目用的语言都是C++ 前段时间已经上C++17了
最近我在用C++模板封装STM32寄存器 打算实现一套零开销的STM32库(汇编查看 和直接操作寄存器一样)


这是其中一部分gpio的测试代码
希望达成的目标
1 零成本 和手写寄存器没有区别
2 易于使用 不用像C语言写的库函数一样 初始化一些外设 还要定义一个结构体  结构体字段太多 赋值比较麻烦
3 防止错误 比方说 如果尝试初始化PA<16>这样不存在的IO口 编译器将会报错,编译期尽可能的进行检查可能的错误


发现好多人都认为使用C++就一定是会占用更多内存和耗费更多CPU  这其实是一种误解
其实使用模板元编程实现性能会比C更好,因为数据都是编译期生成的 。
比如说 C语言实现一个控制PA口为高电平的函数
void pa_set(uint8_t pin) {
    GPIOA->BSRR = 0x01 << pin; // 0x01<<pin  这个移位运算是执行这个函数时 再进行计算的  会耗费CPU
}C++ 模板传参数
template <uint8_t PIN>
void pa_set() {
    static_assert(PIN <= 15, "PIN INVALID"); // PIN 范围是0~15 超过15就是无效参数 编译器报错
    constexpr uint32_t MASK = 0x01 << PIN; // 这个MASK是编译期 编译器计算的, 因此相比C语言的实现会少一步 移位运算(但是这里PIN必须是常量 否则编译器报错)
    GPIOA->BSRR = MASK;
}

虽然C语言也可以把pa_set写成宏的形式来实现编译期计算,但是一些比较复杂的逻辑是不行的 (比如if else)
C++实现PA口控制高或者低 电平
template <uint8_t PIN, uint8_t CTRL>
void pa_ctrl() {
    static_assert(PIN <= 15, "PIN INVALID"); // PIN 范围是0~15 超过15就是无效参数 编译器报错
    static_assert(CTRL <= 1, "CTRL INVALID"); // CTRL只能为0或者1 否则就是参数无效 编译器报错
    constexpr uint32_t MASK = 0x01 << PIN;
    if constexpr (CTRL == 0) { // 编译期 判断
        GPIOA->BRR = MASK;
    } else if constexpr (CTRL == 1) { // 编译期 判断
        GPIOA->BSRR = MASK;
    }
}
C语言实现
void pa_ctrl(uint8_t pin, uint8_t ctrl) {
    // 无法在编译期判断pin和ctrl是否合法,只能在运行时判断,运行时判断就会耗费CPU 而且参数无效的时候 不好处理这种错误
    if (ctrl == 0) { // 单片机运行时判断ctrl
        GPIOA->BRR = 0x01 << pin;
    } else if (ctrl == 1) { // 单片机运行时判断ctrl
        GPIOA->BSRR = 0x01 << pin;
    } else {
        // 不知道 怎么处理? 代码错误了打印日志?
    }
}显然C++的实现更加有优势,不仅执行的代码会更少 而且 更加安全(参数错误的时候编译器会提醒你)


有些人觉得使用C语言宏也可以实现类似的功能,(我是想不到用宏怎么实现)。而且C++写的代码是否真的零开销 也不确定! 我今天有时间把测试结果发一下
为了使编译器不要生成其他的代码 我修改了汇编启动部分和链接部分(编译器除了生成用户编写的代码 应该还会生成其他一些代码用于初始化全局变量等操作)



修改汇编启动代码 将ResetHandler改成main2,复位后直接运行main2这个函数,删掉与测试无关的部分



main2函数 什么也不做 编译后结果 Code体积接近0



链接文件 ,不链接初始化代码



gpio_config函数初始化1个io时的编译结果



gpio_config函数初始化2个io时的编译结果



gpio_config函数初始化3个io时的编译结果

分析汇编 验证是否零开销



汇编分析 (直接操作寄存器了,没有函数调用)

gpio_config这个函数在1个io和2个io是生成的代码体积是一样的。我选择2个io的汇编 加了一些伪代码注释。比如r0=$0x08000018 类似于C语言中 r0=*((volatile uint32_t *) 0x08000018).  



直接写寄存器 代码体积一样的

为什么3个io的时候会多4个字节的代码体积?



3个io时的汇编

主要代码差不多。MOVS换成了MOVW, 估计是MOVS无法加载2个字节的数据.

问题

  • 为什么有一个警告  ====> 修改main2为启动函数导致和链接文件导致的
  • 为什么要修改main2为启动函数 ====> 使编译器只生成 我写的代码,便于分析(否则即使main函数什么都不写 代码体积还是有点大)。
  • 为什么加main2函数 有一个 exteran "C" ====> main2这个标识符要加到向量表里面, 为了使C++编译器不修改这个标识符.


很多人有一种感觉 他们认为越低级的编程语言性能越高 所以 汇编性能最高 其次是c 然后是c++ 其实这是一种错误的感觉 这个感觉完全没有依据。手写汇编 编译器还能给你优化吗?觉得我说得不对你可以和c/c++编译器pk一下 !现在汇编语言的用途就是用来写那些无法用c/c++写出来的功能 比如修改堆栈指针 切换上下文环境 等等。

快速回帖

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则