序: 在世界诞生之前
我在大一学习嵌入式开发的过程中曾经无数次陷入迷茫. 网上的资料多而杂, 要么是花费了太多时间在这个阶段不必要的细节, 要么是太过简略以至于完全不明所以.
另一个痛苦的来源其实是你校的计算机课程教学. 几乎可以暴论, 理解嵌入式开发完全取决于对计算机系统的认识程度; 我在大一留下的诸多疑问一直到我学习了南京大学计算机系统实验课程之后才得到解答.
所以我决定做一份学习资料(不如说更像是实验导引, 就像是国外学校EECS课程常有的Lab). 在这份文档中, 我会尽量做到只讲述当前有必要的细节, 应当交给视频去讲明白的就会交给视频去做; 应当由你自己STFW / RTFM / RTFSC.
必做题: STFW
搜索网络, 了解一下STFW/RTFM/RTFSC几个词的意思.
收集建议
本文档目前的版本目前由科协内部使用(当然你分享出去了大家也不会说什么).
试用的一个重要目的是, 希望能收集大家在自由探索过程中真实遇到的困惑和改进建议, 这些建议会在此后 SEU-Project R 项目讲义的编写中发挥重要作用.
"总有一天我会把所有的细节搞明白的"
"我们都是活生生的人, 从小就被不由自主地教导用最小的付出获得最大的得到, 经常会忘记我们究竟要的是什么. 我承认我完美主义, 但我想每个人心中都有那一份求知的渴望和对真理的向往, "大学"的灵魂也就在于超越世俗, 超越时代的纯真和理想 -- 我们不是要讨好企业的毕业生, 而是要寻找改变世界的力量."
--- jyy
"教育除了知识的记忆之外, 更本质的是能力的训练, 即所谓的training. 而但凡training就必须克服一定的难度, 否则你就是在做重复劳动, 能力也不会有改变. 如果遇到难度就选择退缩, 或者让别人来替你克服本该由你自己克服的难度, 等于是自动放弃了获得training的机会, 而这其实是大学专业教育最宝贵的部分."
--- etone
计算机的所有东西都是人做出来的,别人能想得出来的,我也一定能想得出来,在计算机里头,没有任何黑魔法,所有的东西,只不过是我现在不知道而已,总有一天,我会把所有的细节,所有的内部的东西全都搞明白的。
--- 翁恺
正确提问和解决问题
如何求助
在你的实验碰到问题, 需要求助之前 --- 哦不, 在开始实验之旅之前, 请先简单阅读一下这两篇文章: 提问的智慧和别像弱智一样提问.
TIP
是的, 别再问"为什么板子上的灯亮起来了但是电脑检测不到"这种问题了 - 仔细检查一下你连接开发板用的是数据线还是电源线( ---它们之间有什么区别? )
"未测试的代码一定是错误的"
一定要记住: 机器 (除非芯片烧了) 和编译器永远是对的; 未测试的代码一定是错的; 杜邦线很有可能是连错的; 时钟有可能是忘记开了的.
不可否认的是, 嵌入式开发的软硬件结合特性和底层性注定了错误调试的难度, 但错误一定不是不可排除的黑魔法. 你有万用表, 有调试器(在单片机开发时, 你同样可以通过gdb打断点, 看内存等), 已经足够排除90% 的问题了. 剩下的交给玄学吧.
用正确的手段解决问题
在掌握嵌入式开发基础的同时, 我们希望你能在实验的过程中培养用正确的手段解决问题的能力. 具体的内容在此后的讲义中将会一再涉及.
C语言基础?
毫无疑问嵌入式开发的主力是C语言. 尽管需要强调C++和C基本上是两种语言, 但大家大一学的C++仍然足够帮你应付嵌入式开发的入门需要了. 然而C中有两件事物仍然值得强调:
- 指针, 指针的本质, 数据类型的本质.
- 结构体, 结构体的内存分布, 结构体指针, 结构体指针和各种其他事物的互转
在此, 非常推荐访问笨方法学C进行C语言的学习(同样会涉及一些命令行和编译器的使用小方法, 很实用).
推荐完成练习1-18, 31(调试器), 44环形缓冲区的学习.
C语言和指针基础
除非你已经是写C语言的一把好手了, 否则请一定要重新学习一下! 大一学习的指针和结构体相关内容是绝对不足以理解后续的某些内容的.
C++可以写单片机程序吗?
一个有趣的问题: 能不能用C++写单片机程序?
答案是肯定的. 不过要完全理解这个过程还需要一些对编译过程的理解. 而且你不能直接在C里引用C++声明的函数 --- 为什么? (事实上, 这就是你在单片机上写C++的唯一阻碍了. )
这个问题就交给两千年后的你来STFW解决吧.
不过公认的经验是, 不推荐使用C++进行嵌入式的开发. 这是由于C++ (或者其他的现代高级语言) 的模型对计算机底层的运行做了过多的抽象, 这对现代软件开发是一件好事, 对于资源及其受限的嵌入式设备而言却并非如此.
实验方案
Part 1. 从零开始的单片机之旅
实验一: 点灯工程师
- 流水灯
- 读取按键
- RTFM(一)
- 发生了什么?
实验二: Hello World!
- UART和发送消息
- printf在做什么?
- 接受号令吧!
实验三: 穿越时空的旅程
- 正片开始: 中断
- 执行流的切换: 状态机的视角
- GPIO中断和UART中断
- 缓冲区, 消息队列
Part 2. 连结世界
实验四: IIC初见
- EEPROM读写
- SCL与SDA: 了解协议
- 从单片机到现代计算机系统: "外设"究竟是什么?
- Hello World (二): 屏幕, 启动!
实验五: One Last Kiss
- 尝试MPU6050
- SD卡
- One Last Kiss
实验六: 跳动的方块
- 姿态解算
- 对它使用线性代数吧!
- 数学运算: 性能与空间的trade-off
- * Kalman Filter
Part 3. 我逐渐开始理解一切
实验六: SPI和图形库
- SPI协议和屏幕驱动
- 使用LVGL
// TODO:
RTOS
// TODO:
实验环境
- 操作系统: Windows / GNU/Linux
- 编程语言: C语言
- IDE环境: Keil / STM32-CubeIDE / CLion / Visual Studio Code
如何获得帮助
其他说明
欢迎加入科协嵌入式培训教材编写组 / Project-R 文档编写和维护组.
关于本讲义内容的问题和建议请联系当前嵌入式部分的负责人gyh: 213221544@seu.edu.cn / 127941818(QQ), 或是在GitHub上提出相应issue.
修改记录
本章修改记录
2024/2 完成编写. (顾雨杭)
2024/3 网页适配 (顾雨杭)