当前课程知识点:智能车制作:嵌入式系统 > 第四章 MCU外设与开发 > 4.6 嵌入式开发中的C语言(上) > Video
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家回到我们ARM微控制器
与嵌入式系统的课程
我们的课程呢现在还是在第四章
继续讲解嵌入系统开发当中的一些知识和结构
在前面几节课呢我们讲了一个最典型的外设 IO
以及用怎样简单的几句话
来把这样几个灯给点起来
也就是我们传说中的
对一个嵌入式系统先点个灯
来掌握它的开发 接着我们认识了一下
嵌入式开发里面的一些基本的步骤 方法 概念
比如什么是开发板 什么是开发工具
什么是IDE集成环境 然后我们上节课顺了顺
在看起来很神奇的IDE集成环境的背后
编译器 汇编器 链接器是怎么工作的
怎么把我们写的代码中的简单的main函数
变成一个放在烧写在芯片里可以运行的程序
我们讲了这个比较fantasy的从一个main函数
到你看到点灯的过程
然后呢也讲了一个实际上reality
我们这个CPU怎么读取0或者1
按着一个一个时序来执行 那么今天这节课
我们进入一个 我个人认为也比较好玩的阶段
就是讲一讲嵌入式开发里头的C语言
我们这门课不是一个C语言的课 但是呢
在之前的这个学习里头我们讲过我们这门课
只要求大家有C语言的基础 而这节课
给大家讲一讲c语言的
基本的历史渊源 在嵌入式系统里的特点
或者说它跟我们以前所熟悉的
在计算机平台上的C语言编程 有点什么区别
那么C语言是一种什么样的语言呢
在嵌入式行业里应该说它用的是非常非常广泛
特别是在微控制器这样一种比较simple的
比较简单的这个嵌入式系统里头
绝大多数的时候都是C语言的天下
那么C语言它是一种结构化的语言
它写起来代码非常的小巧 更重要的呢
它非常容易地实现一些底层的
比如说按位的操作 所以很多时候
我们会把C语言称为一个 高阶的汇编语言
或者叫宏汇编语言
那么C语言除了这些特点以外
我们为什么要用C语言呢
我们任何一个程序员经常会分成很多很多派
有C派 有JAVA派 有C++派 甚至C#派
他们都会说出自己这个语言的特点
那么C语言经常会提到的一个特点呢
就是比如说它很可靠 它可剪裁
它非常容易在不同的平台和系统上移植
然后呢它又非常容易的维护
一方面因为它结构化
一方面它的比较对象比如说跟汇编比
在实现同样的底层功能的时候
它要容易维护的多 另外它非常的高效
我本人见过的同样的一个程序
用汇编语言的版本给我的代码
打印出来像一尺那么厚
但是用C语言写的代码都非常整齐 就是这个
很多个函数的集合体
所以这样的代码的维护工作量
如果你有亲身体验 你一定会特别容易感觉到
然后最后我想说 C语言有一个非常重要的精神
或者我们叫philosophy of C language(C语言的哲学)
就是编程人员 这个programmer 在座的诸位
你们应该知道 what you are doing
你们在做什么
换言之这件事情的背后在于
语言它不是智能的 它只是一个工具
作为一个编程语言的开发者
特别是当它非常地底层 非常强大的时候
当我们做一个嵌入式开发的时候
你能够通过它去直接掌控硬件里头的很多东西
所以你必须很清楚我写的每句话
它在时序上是怎么样的
它在电路上是如何工作的
那么我们有些同学
在编程当中会因为软件编程的错误
导致硬件上的损坏
比如把电路烧了把一个I/O给损毁了
这个时候呢 就是体现了C语言这种精神
但大家不要怕 我们可以一步一步往下试
那么回顾C语言 我们不是一门C语言的课
但还是应该看看它的历史
所以呢如果大家看一看这样两位这个
叫前人也好 叫这个大牛也好
左边的一位呢叫做肯·汤姆森(Ken Thompson)
右边的一位呢叫做丹尼斯.里奇(D.Ritchie)
这两个人应该来讲在某种意义上
是对计算机行业影响深远的人
丹尼斯.里奇(D.Ritchie)理论上是我们
实际上C语言这样一种语言的开发者
那么他在这个1969年到1973年这个年代里
工作在美国的贝尔实验室
那么开发C语言的故事为什么我要在这讲一讲
跟我这门课给大家讲的这个精神很有关
比如大家再看看这张图片
这张图片是一个非常有意思的小游戏
它的名字叫做space travel
是一个星际旅行的游戏
这个游戏从来没有商业发表过
它是一个爱好者写的游戏
它的作者呢就刚才我们看到这个大胡子
这个肯·汤姆森(Ken Thomsen)
他最早在一些大型机上自己写了一个代码
一个二维的平面的小游戏
用一些小圆弧代表飞船
在一个二维平面模拟的太阳系里
靠近若干个星体
每个行星每个卫星 尝试在它们表面着陆
于是在贝尔实验室里
他的很多同事成了这个游戏的疯狂爱好者
那么在1960年代的某一天
他手上有一台DEC的PDP7的这种计算机
尝试把这个space travel这个游戏
移植到这样一个计算机上
但是发现这个计算机没有操作系统
他们以前使用的操作系统Multics
在这个计算机上无法运行
于是决定自己又动手为了玩这个游戏
写个操作系统 久而久之
这个操作系统开发出来
也是我们今天所使用到的Unix系统的1.0版本
然后他有个很好的朋友叫
就刚才提到的丹尼斯.里奇(D.Ritchie)
也很爱好这个游戏
于是到了1969年前后他们尝试
把这个游戏和这个操作系统
进一步移植到
PDP-11这样一个型号的机器上的时候呢
发现了问题说这个操作系统移植很困难
因为大量代码是用汇编写的
于是他们决定使用一种叫B language B语言
来编写这个新的操作系统
而在写的时候
发现这个时候的计算机已经进化到了
比如说以字节为单位来做操作
所以说原有的B语言不够使用
于是他们从想玩一个简单的space travel游戏
到写了个操作系统
再做操作过程当中对它进行语法的扩充
于是产生一种新的语言 完善了它的语法
那这个语言叫什么呢
这些不负责任的玩家们就决定
既然它的前身叫B语言
那这个新的语言就叫C语言吧
所以C语言只是很Simple地从α β下来
B语言的后一个字母作为它的名字
所以这个故事呢
说我们所从事的 所熟悉的计算机
乃至嵌入式这个行业
并没有想象中那么高大上
很多东西都是在爱好者的疯狂的 玩儿
并且把它玩得很酷 玩的很好
这样一种Geeks(极客)的精神里头来发扬光大的
那么C语言的诞生在了1969到1973年代
早期的C语言是没有标准的Standard的
所以呢在1978年最早由这个
由Kernighan和Ritchie这两个人一起写的这样一本书
叫“The C Programming Language”
这本书在实质上成为了非Standard的
一个Specification 一个C语言的标准
这本书里给出了C语言的基本的语法
然后C语言的所有的语序和关键词
那么也定义了C语言的标准的C语言函数库
也就是我们现在经常会一上来就include的
那几个函数库
那么它成为了C语言实际上的标准
到了某一个年代这本书出第二版的时候
才出现了ANSI的C标准
一直发展到我们今天的C89 C99这些Standard
所以在很长时间内K&R的C就是C语言的
实质上的标准
所以这样一种C语言的历史发展到今天
也就是我们大家在之前的别的课堂上所学习的
C语言 那么C语言拉起来大家会发现
它有很多很多的关键字
有很多keywords 同时呢比如include
比如说define 同时呢它还有很多的运算符
那么如果大家的C语言基础不扎实
在学这门课的时候 一方面大家应该补一补
C语言的基础语法 首先熟悉各种运算符
尤其熟悉指针的概念 其次呢
跟着我们课程在我后面要讲的内容里
也会进一步加深
C语言的一些基本概念在嵌入式系统里头的应用
那么这些运算符 这些关键字当中
有一些我要提示大家注意的
比如说我们在C语言的编程里
很多同学都会学习if else语句
都会用感叹号来表示取非
对吧 一个逻辑条件
那么在计算机在嵌入式应用里头
我们会经常会做位的操作
比如这么会用一个小的波浪号
进行按位取反的操作
我们会用两个向右的这个尖括号
代表左移或者右移的运算
这是可能大家在学习计算机平台的C语言
编程当中学习到但使用得比较少的运算符
希望大家加以巩固 那么从C语言的历史
到C语言的这些大家耳熟能详的Standard
究竟在嵌入式的 在微控制器里头C语言
有哪些特别要注意的地方
或者说跟计算机系统不一样的东西呢
我们分成几个小的topic来给大家讲
第一个呢 是C语言当中的数据类型
C语言当中的数据类型大家都很熟
我们有char 有int 有long然后还有float
还有single double这样整数的
和浮点数的运算类型
然后整数运算类型这些我们当中
还可以是signed unsigned有符号无符号
那么大家对这些都很熟
那我想问作为一个本科同学
如果让你写个C程序
上来你知道有个main 要声明一个变量
你会给变量用什么类型呢
我们很多本科的同学或者初学者
很喜欢就是int 一个整型
然后编到尾巴里头从整个程序编下来
那么在这个过程当中
其实大家还有一个非常糟糕的习惯
在这里我只提 不给大家讨论了
就是这个变量的名字信手拈来
用到谁是谁 int abc ijk不够了再往后面lmn
编程序的时候经常信手拈来的用
回头就忘了这个变量是什么功能
那么这件事情先放在一边 我们单讨论类型
在嵌入式应用的行业里头
我们实际上对于C语言的一个基本概念
扣一扣就会知道每一种变量类型
它是有它自己所占的这个字节数
或者叫字位数的
那么它们就会有一个range的概念
也就是动态范围
比如说一个unsigned的char
它是8个比特一个字节 那么它的值就从00
一直到ff 或者是二进制的0000八个0
1111八个1 从0到255
如果它是signed的char
就是有符号类型的char变量八个比特
那么根据负数的这个定义
我们从负的128到正的127是它的取值范围
以此类推包括int 包括long
大家都会知道它是有它的这个range
有概念的有它的范围的
那么如这样一个代码
比如说一个delay函数
大家觉得有点熟悉 为什么
我们在本章的第一小节讲点灯
说我让一个灯变亮 中间delay一段时间
这个delay我可以是五百个毫秒
也可以是一秒
那么在C语言编程里怎么实现它呢
最简单的方法就是
我给大家提示过做一个大大的for循环
我们的计算机指令是大约以每条指令
几十个纳秒的速度在运行
那么我们如果编一个大大的for循环
让它在这执行上千条上百万条指令
差不多一秒钟的时间就过去了
所以我们会做一个大大的delay
比如像现在这个函数叫void delay(void)
我没有参数 我就delay一个固定的时间
那进去以后我unsigned char两个变量
大家注意这两个变量什么类型
底下有个嵌套的for循环 那么第一层for循环
是从i等于0到10循环
第二层是从j等于0到j到6万循环
那我想问这个函数正确吗
它执行的时候能够正确的延时或者能够退出吗
大家可以想一想
然后如果C语言基础比较扎实
又跟着我刚才那些概念
顺下来的同学会很快的反应过来这有问题
有什么问题呢
因为我们的变量类型是unsigned char
所以在我们的嵌套循环的第二层循环里
我们判断条件是j小于6万
而对一个8比特的无符号整型变量
每当他的值到255的时候
它累加就会溢出回到零
所以它的值永远在0到255的这个range
它的范围内循环 这个条件永远不会满足
所以我们的函数我们的程序 走到这一步
如果你单步执行或者你跟踪执行的话
它就死在这儿了 永远出不了这个循环体
那么这就是函数类型(字幕菌注:应为变量类型)引起了一个错误
那么在计算机的行业里头
我们实际上会发现很多很多这样的问题
现在大家可能接触不到
我只举了这个简单的例子 我提一下
比如有兴趣的同学
你希望去做一个USB的设备 做USB通讯
希望自己开发一个以太网的接口程序
这个时候你会非常明显的感觉到一个概念
叫protocol 也就通讯协议
我们所使用的网络 我们所使用的USB
每一个数据都以数据帧
数据包的形式来发
包头包尾校验帧每一个单元是几个字节
都是非常清晰定义了的
所以按照协议
关心的不仅仅是这个数 值是多少
更关注的是在按照通信协议
在每个step我们应该发送一个字节
或者两个字节 这个位宽是一定不能出错的
所以在这个时候 这个变量一定要准确
我们刚才讲了 那就是说这个数据的类型
关系到程序运行结果的正确与否
而实际上在嵌入式开发的领域里头呢
数据类型不仅仅涉及到正确与否
还涉及到我们应该尽可能地节约
我们的代码空间 我举个非常简单的例子
我们绝大多数的C语言初学者
都非常喜欢int型变量因为这是第一个学的
又很顺手甚至是默认的类型
那么这样的情况下我们在32位
我们这门课
所学习的ARM Cortex M系列的CPU上 MCU上
它是个32位宽架构的MCU 所以大多数时候
它的编译器int类型
对应的是32位的四字节存储空间
而我们很多时候使用的变量
只是用来给if else做一个简单的判断
那么大家可以知道if else判断
实际上是一个布尔型的变量
我们所需要用到它的值只是1或者0
这样一个1比特的信息
于是我们这样子进行一个
习惯性的编程开发的时候
你往往会为了一个比特的信息
用掉32比特四个字节的空间
嵌入式MCU嵌入式系统的存储空间
往往都是非常有限的
所以我们需要量入为出地来使用它
这是我在之前讲基本开发时给大家强调过的概念
所以实际上在C语言编程的时候
我们应该尽量使用满足我们程序功能要求的
最小的变量类型来做
那么另外一个需要注意的点呢
就是关于浮点数 很多同学会说我想算个周长
乘以3.14 测个温度 几点几除以几点几
我们在不带有浮点运算单元的MCU
或者CPU架构下
比如说我们现在讲的ARM Cortex M0 M0+
乃至ARM Cortex M3
这样的MCU上使用浮点运算的时候一定要小心
在这样的不带有浮点运算功能的CPU上面
与带有浮点运算功能
比如像M4这样的MCU上是不一样的
它的浮点运算功能需要一个庞大的
C语言的标准函数库来加以支撑
这样也许在你的代码里
你只出现了一次浮点运算
就是每次运算结果比如说几点几乘以几点几
算一步就一句话 但是它会导致你的程序
需要include进来一个非常大的浮点运算的
支持函数库
使你整个代码编译出来的量比原来要大的多
占掉更多的存储空间
那么在一些存储空间非常受限的应用里头
我们可能会发现这是一个瓶颈
那么处理的方法当然很多了
大家如果有兴趣去学习嵌入式的书可以做更多的阅读
比如说我们将浮点数在有限精度下
通过移动小数点的位数全部转换成整数运算
或者类似于三角函数这样一些浮点的运算
我们可以做一张在一定精度下的查表
用一张查表运算来替代我们的浮点运算
这都是技巧 接下来我们要讲的一件事情呢
就是数据类型不仅仅取决于我们所使用的CPU
它还取决于我们所使用的编译器换言之
char类型的变量 int型的变量
乃至long型的变量 它对应一个字节两个字节
还是四个字节 这件事儿既取决我们所用的CPU
也取决于我们当前开发所使用的编译器
根据我们所使用的 比如是code warrior里头的C编译器
Freescale公司提供的或者是开源的
ARM下的这个GCC的编译器
它是有不同的界定的
具体的值应该去查这个编译器的手册
它会有一些默认的规范同时它是可以设置的
比如说我们通过编译器的一些参数
可以指定int型对应一个字节或者两个字节
那么有同学会说 既然它是可以设置是灵活的
我是不是就可以随意使用了
等到我发现这个字节不对的时候
我改一改编译器设置不就行了吗
我要放在这也是一个不好的习惯
如果大家养成良好的编程习惯
熟悉嵌入式C语言
大家可以阅读一些开放源代码的作品
它里头的代码的写法 那么最常用的一种做法
就是我们在嵌入式开发里头
避免直接使用基本类型来声明变量
就是说我们尽量避免使用 char int short
那怎么办呢
我们在我们程序的最开头或者它们头文件里
使用typedef这样一个关键字 将一种变量类型
声明为一种替代类型
比如我这里要举的这两个例子
int型的变量在8比特的MCU或者CPU上
一般编译器会把它处理成16比特占两个字节
而在32比特的系统上默认它就是32个比特
反过来short变量在32位系统上
才是两个字节16个比特
那么这样一种不一样的对应关系
我们怎么在代码上做到统一呢
大家如果去看一看μC/OS linux
以及包括lwIP这样的
开放源代码的project你就会发现
它们往往都会像这两段代码所写的
先把char 把int和long这样的变量
typedef成u32_t或者是int32_t
这样的变量类型
用一种替代变量类型直接说明这个变量应该是
8比特还是16比特
日后声明变量的时候
只用使用后面这样一个替代类型
来声明变量比如说uint32_t声明一个变量
那么在任何代码上
就知道这个变量是32位4个字节的
那么当我们把这样一个程序代码
移植到一个新的计算机构架的时候
我只用改最前面这些typedef
根据我当前所使用的CPU和所使用的编译器
把它定位到正确的类型上
那么这个代码就不会出现任何
因为字节的长短而出现的bug
那么在日后会节约我们大量的时间
讲完了这件事呢
再来讲一个第二个topic 比较简单的一件事儿
就是C语言里头的位操作 C语言里头的位操作
在计算机层面的开发用得比较少
但如果根据我们这个课的第一小节
给大家讲的这个IO的编程
就会感觉到我们要大量的使用位的操作
为什么呢 因为我们直接涉及到这寄存器里头
一个比特两个比特的操作
比如说在嵌入式开发里头
位操作有很多具体的应用
像我们已经有的例子里头
我们要控制一个引脚输出的值
需要改变寄存器里某一个比特的值
我要读取一个引脚输入的电压值
我应该在对应的寄存器里
读取寄存器某一个比特的值是0还是1
那么我们在它的配置里头也根据功能打开时钟
设置使用的复用功能
我们要给某一位置1或者置0
所以可以发现在嵌入式C语言开发里头
我们会非常频繁使用的一类操作
就包括了对一个变量对一个寄存器
把某一个比特某几个比特置位或者清零
把某几个比特的值进行移位
然后判断某几个比特是零或者一
或者把某几个比特里某一个寄存器里头
某几个比特值拿出来进行进一步的判断
那么这样会涉及到很多按位的操作
这块儿我会讲得稍微快点
因为它涉及到C语言的基础
比如说与 或 亦或 按位取反 左移 右移这些运算
那么比如说我这里给两句话
在我们之前做点灯的例子
大家都看到过GDIOD_PDOR输出数据寄存器
等于它自身的值按位取反 所有的0变1
所有的1变0 说if GPIOD_PDOR这个寄存器的值
与上0x80等于0 这是判断什么
这是判断输出的这些引脚当中
第八个引脚也就是最高的那个引脚
是否为一 对不对
那么通过这些语句我们可以
控制寄存器的某一个比特为0为1
或者把它值给拎出来 那么归总起来
它的操作应该这样几类 我们顺着捋一下
按照C语言来讲它并不困难
比如说我有一个变量
我希望把当中的BIT7也就是第8个比特
从0开始数置1 变成1 我应该怎么做呢
我们应该写这么一句简单的话
就是这个变量或等于0b1000
后面7个0 0b是二进制的C语言写法
与这样一个二进制数来或
0与任何数或 不改变它的值
1与任何数或 变成1
于是这个变量在其他位不变的情况下
对应的这个比特变成了1
那同时我要把这个变量里头BIT5和BIT4
两个比特同时变成1怎么办呢
我应该把它与一个0b00110000
这样的一个变量来或
对吧这是这非常简单的
那么我们会把后面的称为0b1000
或者0b0011000这样的二进制
0和1组成的这样一个码串
在很多时候称为mask mask是什么 面具
面膜对吧
实际上在计算机行业我们把它称为掩码
它来标识哪些位要被改变哪些位不被改变
很像一个纸板上若干个被抠出的洞
让你来涂改的意思 那么 一个小quiz
大家看一看
说这两个变量经过这个程序以后
它有什么区别
一个是这个变量或一个值
一个是变量等于一个值
我想这件事情是非常清楚的
我们在第一小节做IO操作的时就说过
或的时候只改变一个比特 等于的时候
是把不想改变别的比特也都强行置0了
这就是位操作的好处
那么我们如果把类似于0001
0010这样的值全部拎出来
把它做一个define成一个宏呢
大家会发现这件事情变得很简单
比如说define BIT1的mask
这个宏是0b00000010 相应的BIT2
BIT3我们的代码就会变得更加可读
比如说刚才的程序
就变成了一个变量或等于BIT3的mask
日后我们再来读这段代码的时候
不用去看它的运算什么
我很清楚 我把它的BIT3置位了
那么下面代码也是变量或等于BIT2的mask
或上BIT1的mask
我们很清楚我们把哪两个比特给置位了
那么反过来说
如果我们要把一个变量
一个寄存器里头的某一个位清0怎么做
大家会说置位用或清0呢用与
但是这个时候我们不用为了清零
重新再做一组掩码 非常简单把0001000
这样的一个掩码按位取反
是不是就它的反变成1110111
我们要的那一位是0其他位都是1
这个时候再与变量自身与
写成变量与等于我们要的掩码去反
就是别的位不变
掩码对应的这一位被清零了
那么我们也可以用同样的方法
来实现对多个比特的清零 那么除了置位清零
我们还会很常见做到的一个操作
大家在学习计算机的C语言
可能基础不扎实的时候不太熟的左移和右移
我们把运算符写在等号前面
等于是这个变量来移
往右移四位往左移四位 0x34往右移四位
四没有了 只剩下0x03
往左移四位3被移出去了如果它是char
类型变量的话
那么4被移到了高四位 下面替补0变成0x40
左移和右移
大家可以在C语言里头去回忆一下
那么它有什么用呢
在我们计算机操作的时候
比如说有一个变量 有个寄存器
我想把它当中某几个比特的值取出来
赋给一个新的变量
看一看我们应该怎么做
比如现在最简单取高四位和低四位
我们就定义了两个掩码
h4的high 4的mask
是0b11110000
low 4是00001111 那么同样一个变量
我想把三和四分别取出来 怎么取
这在我们以后做通讯的时候
会用到这个这段代码
这个变量或等于它自己与上低四位的掩码
这样就拿到了最低的四个比特
那么高四位呢比它多一个步骤
让这个变量与高四位的掩码11110000来或
这样它的四就没了
只剩下三但这个时候是0x30啊 我们再右移四位
把三作为一个独立的部分
取到一个新的变量里头
所以根据移位 加 与或
我们就可以非常容易地取出
任意一个寄存器任一个变量里的某几位的值
来进行下一步的编程操作
那么这是左移和右移
最后呢还有一个就是我们的
叫做condition判断条件
比如说我们会说我们回到点灯
我们点了八个灯
它输出的值是零的时候都是亮的
输出的值是一的时候都是灭的 那我想看一看
这个我上一步的程序运行结果
这个灯当前状态是亮是灭
我怎么判断呢
我要把这个寄存器的值读出来
检查某一位是零还是一
我们往往会在语句里出现这样的句子
if一个变量与上一个掩码不等于0
if一个变量与上一个掩码等于0
因为任何数跟零与都是零
所以整个这个与的逻辑表达是否为1是否为0
只取决于掩码对应的那一位是否为1
大家想想这件事儿
所以我们
就很容易把一个变量里头的某几个比特的值
拿出来做个条件判断 所以呢
我们在这样短短的一节课里
已经回顾了C语言的基本历史渊源
讲了C语言的数据类型的概念
讲了C语言按位取的概念
这都是非常基础的
那么这节课的内容差不多到这就结束
下一节课我们会再往前走一点
去给大家讲讲C语言在嵌入式开发里头
指针的灵活运用
和volatile这样的关键字的使用
我们究竟在Code Warrior里头 在我们第一节课里头
去编一个点灯的那几句简单的代码
它背后的故事是什么
请大家下一节课再继续学习
-1.1 课程概览
--Video
-1.2 进入嵌入式系统的世界
--Video
-1.3 如何学好嵌入式系统
--Video
-2.1 计算机的基本概念、发展历史
--Video
-2.2 从晶体管到CPU
--Video
-2.3 概念CPU、微控制器MCU和嵌入式系统
--Video
-2.4 八卦计算机史
--Video
-2.5 不同领域、不同系列的嵌入式系统
--Video
-2.6 ARM历史与MKL25Z128 MCU
--Video
-3.1 CPU的基本结构和运行机制
--Video
-3.2.1 堆栈的概念
--Video
-3.2.2 堆栈的概念-头脑体操
--Video
-3.3.1 ARM的体系结构
--Video
-3.3.2 ARM的体系结构-头脑体操
--Video
-3.4 中断的概念和机制
--Video
-3.5 中断子程的概念和编程
--Video
-3.6 复位、时钟、存储器和总线
--Video
-3.7 小结:MCU的总体结构和程序运行机制
--Video
-4.1 第一种外设:IO
--Video
-4.2 IO外设的编程实操-点亮LED
--Video
-4.3 IO外设的进阶知识
--Video
-4.4 嵌入式开发的基本概念与工具链
--Video
-4.5 嵌入式开发的进阶知识
--Video
-4.6 嵌入式开发中的C语言(上)
--Video
-4.7 嵌入式开发中的C语言(下)
--Video
-E0.1 实验零 开发板的初步认识与工具链的安装
--Video
-E0.2 实验零 体验一个例程的编译与下载
--Video
-E0.3 实验零 编写第一个程序:点亮核心板LED
--Video
-E1 实验一 点灯秘籍
--Video
-5 智能车视角的嵌入式设计
--Video