打印
[其他ST产品]

mcu 是如何执行代码的?

[复制链接]
101|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
背景
​ 你是否会有这种想法:程序是怎么跑起来的?什么是程序?整个系统是怎么跑起来的?为什么单片机能懂得我们写的代码?单片机上电到进入我们的 main 这一过程是怎么样的?中断系统到底是怎么触发的?中断产生后怎么进入到我写的中断服务函数的?

​ 这些问题真正讲明白涉及的知识点还真有点多。我也是带着这些问题,思考一段时间后才悟出来一点能够解释的答案。毕竟我们只是 c 语言开发,只是干代码就完事了。但是,这些问题的答案是非常有助于我们理解一片芯片到底是怎么工作的,不在被这黑呼呼的正方形外壳给迷惑到。真正明白之后,真的就完全掌控了一个芯片。


使用特权

评论回复
评论
wailian1265k 2023-10-6 14:53 回复TA
版权声明:本文为CSDN博主「EINT」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_56548489/article/details/123604723 
沙发
wailian1265k|  楼主 | 2023-10-6 14:54 | 只看该作者
基础概念
机器中有一个大脑,叫做 cpu ,我们写的代码是不能够放在 cpu 中的,那代码总得有地方放吧?那我们的 c 代码到底放在了哪里?总得有地方放我们辛辛苦苦写出来的代码吧。实际上,代码是放在 rom 性质的存储器中,比如 flash,sd卡。我们写的代码组成中,有代码,有变量,代码是死的,可变量是活的,因此,当我们把可执行文件放进 flash 中去后,变量部分需要拷贝到 ram 中去,拷贝的这一过程的代码通常厂商已经帮我们写好了,比如 mdk 将这部分代码自动的添加到生成的可执行文件中(bin 或者 hex)。编译器将我们写的 C 代码编译成机器能读懂的 bin 文件或者 hex 文件,bin 文件或者 hex 文件代表了我们的代码,如果想要读懂 bin 或者 hex ,只能通过对应的汇编指令去读。为什么要把变量放到 ram ?因为变量时刻都在发生变化, 且 ram 的运行速度远超过 rom。

使用特权

评论回复
板凳
wailian1265k|  楼主 | 2023-10-6 14:54 | 只看该作者
cpu 怎么执行代码?cpu 只认识机器码,也就是01组成的数字,最早一批的程序员估计就是写的机器码,因为机器码可读性很差,基本没可读性。因此,便在机器码之上发明了汇编语言,有了汇编语言,程序员能够方便的操作 cpu。cpu 通过读取 flash 中的机器码去执行各种操作,机器码对应的便是汇编指令,因此,程序最终是根据芯片平台所提供的指令集配合平台提供的编译工具链编译产生的汇编指令,不管是用 C 、python、java等等语言,都要被翻译为汇编指令,而汇编指令能用机器码来表示,最终计算机只认识 01组成的机器码 。

使用特权

评论回复
地板
wailian1265k|  楼主 | 2023-10-6 14:54 | 只看该作者
单片机的启动代码便是汇编语言写的。汇编语言也不容易读懂,且可移植性很差,因为不同的架构,汇编指令集是不同的,当从汇编进入到 c 语言的世界时,任何架构都变得不重要了,c 语言写的代码可移植性极强。c 语言虽好,但是进入 c 的世界是要付出代价的,因为 c 语言运行需要一个栈环境,我们知道 c 语言的局部变量、形式参数、函数调用,是需要栈环境的,因此在进入 c 世界之前,就需要把这一环境给准备好(初始化栈空间),初始化栈这一操作通常是在启动代码中,cpu 中有专门的寄存器来存储栈的地址,这个寄存器便是 R13,通常叫他为 sp。

使用特权

评论回复
5
wailian1265k|  楼主 | 2023-10-6 15:00 | 只看该作者
在 CM3 中是从 0x00000000 开始执行指令的,在 0 地址处提供 MSP(R13寄存器) 的初始值,然后紧跟着就是向量表 ,因此,系统上电后或者复位后执行的第一条指令便是给R13 寄存器一个内存地址来当作栈空间,然后执行复位中断服务程序,栈环境初始化好之后,然后在复位中断服务程序中初始化系统时钟(还会执行分散加载,将指定的代码或变量加载到想要的地方去以及拷贝 flash 中的 数据段到 ram 中),高级一点单片机的还会初始化 ddr 等等操作之后,通过一条跳转指令跳转到 main 函数中,比如 b main 就跳转到了我们 C 中定义的 main 了,此时就能够执行我们写的 C 代码了。

使用特权

评论回复
6
wailian1265k|  楼主 | 2023-10-6 15:01 | 只看该作者
cpu 通过 flash 中读取指令,然后执行各种操作,比如读取变量 ,问题是 cpu 读取的变量放在哪了??总该有地方放吧 ?cpu 内部有寄存器,寄存器便是存储执行这些指令的暂存场所。 cpu 怎么知道用那些寄存器 ?此时汇编指令给出了要用那些寄存器,我们写的 C 代码最终被编译器转换成机器码。cpu 可以读写内存(ram)到 cpu 内部的寄存器中去。

使用特权

评论回复
7
wailian1265k|  楼主 | 2023-10-6 15:01 | 只看该作者
通过汇编指令理解 CPU 是如何执行 C 代码的

//及其简单的 C 代码
int main(void)
{
    volatile int a; // 三个局部变量都要 push 到栈中,栈指针通过 sp 来表示
    volatile int b;
    volatile int c;
   
    a = 1;
    b = 1;
    c = a+b;
}

// 将 c 反汇编后,对应下面的图看
    i.main
    main
        0x08000242:    b50e        ..      PUSH     {r1-r3,lr} // 从右到左 push
        0x08000244:    2001        .       MOVS     r0,#1 // 将立即数 1 放到 cpu 寄存器 r0 中
        0x08000246:    9002        ..      STR      r0,[sp,#8] // 将 r0 中的值存储到 sp + 8 的地址中去
        0x08000248:    9001        ..      STR      r0,[sp,#4]
        0x0800024a:    e9dd1001    ....    LDRD     r1,r0,[sp,#4]
        0x0800024e:    4408        .D      ADD      r0,r0,r1
        0x08000250:    9000        ..      STR      r0,[sp,#0] // sp + 0 的地址便是 c 的地址
        0x08000252:    2000        .       MOVS     r0,#0
        0x08000254:    bd0e        ..      POP      {r1-r3,pc}
        0x08000256:    0000        ..      MOVS     r0,r0

使用特权

评论回复
8
wailian1265k|  楼主 | 2023-10-6 15:01 | 只看该作者

使用特权

评论回复
9
wailian1265k|  楼主 | 2023-10-6 15:02 | 只看该作者
整个裸机系统怎么跑起来
cpu 想跑起来得有电吧?再牛X的 cpu 他没电也没啥用。电是怎么产生的???电流是导体在磁场中切割磁感线产生的,切割这一动作能用人力来,人得吃饭,不然没力,能量守恒定律能够完美解释。为了摆脱人力发电,人类利用了大自然的能力,比如风能,水的势能。通上电之后 cpu 还是跑不了,因为没心跳啊。cpu 还需要另外一个重要的东西,便是晶振,晶振能够产生特定频率的时钟脉冲,有了心跳之后,cpu 就彻底的能够运行起来了,怎么运行的??内部是晶体管组合起来的各种门电路,比如数字电路中的锁存器、译码器等等。

使用特权

评论回复
10
wailian1265k|  楼主 | 2023-10-6 15:02 | 只看该作者
有了时钟脉冲,有了电,真的能够跑起来了,真的能够从 0 地址处取指令了,0地址后面便是一张中断向量表。通产我们是把 bin 或者 hex 放在了 0x08000000 这个地址处,中断向量表肯定是要在这个地方的。是因为支持中断向量表偏移,通过中断向量表偏移就可以将中断向量表存放到任意地址处。

使用特权

评论回复
11
wailian1265k|  楼主 | 2023-10-6 15:02 | 只看该作者
经过以上种种准备之后,便能够跳转到 C 语言的世界中去执行我们熟悉的 C 语法了。此时就能够指针的方式去操作寄存器了。为了降低开发者的开发难度(不同的芯片经过内核扩展出各种不同功能的芯片,外设寄存器定义也不一样),各大芯片厂商一般都会创造出一个库,这个库中将各种外设的寄存器通过宏的方式给抽象出来,然后利用 C 语言结构体的特性(结构体中的成员的地址连续)将寄存器放到结构体里面,具体查看相关库的代码。比如常用的 ST 厂商提供的 HAL (硬件抽象层)库,大大降低开发者的难度(不用去查手册那个寄存器的那个位的地址是什么,因为芯片有上百个外设寄存器)。

使用特权

评论回复
12
wailian1265k|  楼主 | 2023-10-6 15:02 | 只看该作者
为什么叫裸机?因为裸机不存在操作系统,通过一个循环加中断的方式去执行代码。

使用特权

评论回复
13
wailian1265k|  楼主 | 2023-10-6 15:02 | 只看该作者
中断是如何产生的?
说实话,我也不清楚中断到底是怎么产生的。我只知道是通过中断控制器配置的,配置了之后(配置优先级,任务紧急的中断优先级高),满足了中断的条件之后,便会去中断向量表中找到对象的中断向量,如果我们写了对应的中断服务程序,便会执行到我们的中断服务程序中去。

使用特权

评论回复
14
豌豆爹| | 2023-10-7 08:56 | 只看该作者
整理成一个完整的文档不好吗,分成这么多小片段影响版面!

使用特权

评论回复
15
Bowclad| | 2023-10-8 23:07 | 只看该作者
我也一直好奇是怎么实现运算的

使用特权

评论回复
16
Henryko| | 2023-10-10 13:12 | 只看该作者
裸机好还是带系统更好啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

332

帖子

0

粉丝