学习使用 MCU 就是理解 MCU 硬件结构,以及内部资源的应用。在汇编或C语言中学会各种功能的初始化设置,以及实现各种功能的程序编制
MCU 通过配置寄存器来控制内部电路的连接,通过不同内部的连接方式来实现不同电路,不同电路来完成不同功能。
了解一个 MCU 所使用的寄存器是几位的,是如何连接的是很重要的 了解 MCU 所在的外围电路板的连接情况也同样重要
一般每使用 MCU 的一个功能,就要对控制该功能的寄存器进行设置,这就是 MCU 编程的特点,千万不要怕麻烦,所有的 MCU 都是这样
MCU由CPU和一系列寄存器和io口(input和output)组成,CPU通过编写好的程序,控制寄存器里面各个位数的布尔值,进而控制MCU的各个io口输出的是高电平还是低电平
而MCU的 io 口一般这样命名:寄存器的名称
比如:P20是P2寄存器的0位(可以打开头文件来观察寄存器地址和位数信息)
所以,io 口的值是对应的某一寄存器的某一位的值
1. 可以通过物理手段改变 io 口的值来改变寄存器的值
2. 也可以在程序中对寄存器赋值来改变 io 口的值
注意!!
- 使用一个赋值语句对寄存器进行赋值是影响一连串对应同一寄存器的一系列 io 口的,而且一般8位寄存器的值是用8位二进制数表示的,而这个二进制数用十六进制数的写法会更方便(需要对进制转化熟悉)
- 为了直接操控寄存器的位来控制1个io口,可以使用位寄存器(如果头文件有声明的话,没有就要获取物理地址再自己声明)
- 可以通过检测寄存器的值来检测io口电平的高低变化,进而检测MCU系统的变化
所以,了解一个 MCU 所使用的寄存器是几位的很重要,因为寄存器的位数决定了它的 io 口数目,以及一次给寄存器赋值要怎么赋值
下面是一张进制转换表
十进制 | 二进制 | 十六进制 | 十进制 | 二进制 | 十六进制 | |
---|---|---|---|---|---|---|
0 | 0000 | 0 | 8 | 1000 | 8 | |
1 | 0001 | 1 | 9 | 1001 | 9 | |
2 | 0010 | 2 | ==10== | ==1010== | ==A== | |
3 | 0011 | 3 | 11 | 1011 | B | |
4 | 0100 | 4 | ==12== | ==1100== | ==C== | |
5 | 0101 | 5 | 13 | 1101 | D | |
6 | 0110 | 6 | ==14== | ==1110== | ==E== | |
7 | 0111 | 7 | 15 | 1111 | F |
而了解 MCU 所在的外围电路板的连接情况也很重要,一般而言,MCU 使用的TTL规定,也就是高电平是5v,低电平是0v;外围电路使用接地符号 gnd代表0v,使用 vcc代表5v恒压源的正极
因为 MCU 通过控制io口输出的电平高低,配合外加恒压源来控制外围电路板某一回路的通断来实现某一功能
比如:
1. 外接恒压源vcc是5v,该回路的io口是0v,则该回路是通的
2. 外接恒压源vcc是5v,该回路的io口是5v,则该回路是断的
3. 可以用这个特性控制某回路通断,比如让LED闪烁
而 MCU 上电的时候,所有io口输出的都是高电平,按下按键变成低电平,复位按钮相当于重新启动,重新上电
一般要在程序中先规划要用到什么寄存器,要对寄存器的初始值进行什么的处理,才能使用好io口发挥到什么要的效果
每使用 MCU 的一个功能,就要对控制该功能的寄存器进行设置,这就是 MCU 编程的特点,千万不要怕麻烦,所有的 MCU 都是这样
MCU 执行语句的速度很快,尽管在程序里面可以通过在一个循环里面对进行操作实现振荡的周期效果,但是在 MCU 实际运行时我们却观察不到这种效果,于是可以在振荡的过程中加入延时语句
1. 对于51单片机,可以使用软件生成延时函数,不过是写死的
2. 利用写死的延迟1ms,传入参数进行给定次的循环,实现任意单位1ms的延迟函数
对寄存器的位数进行操作时,常常有溢出的风险,所以一般使用一个中间变量+条件判断进行操作,可以是直接改变中间变量的值,或者将中间变量作为移位的幅度
io口有四种操作模式,可以在学数电之后了解一下
定时器也是一种计数器,是属于 MCU 内部的资源,其电路的连接和运转都在 MCU 内部完成。它主要用于
- 计时,记录程序执行的时间,或者使程序每隔一段固定的时间就完成一项操作
- 代替长时间的Delay函数,解放你的 CPU,因为Delay函数是让 CPU 进入空循环,会占用 CPU 资源
- 可以实现一部分中断的功能,达到异步多线程的任务效果
关注一个 MCU 的定时器,就是说去关注它的定时器个数还有使用方法。要注意的是,定时器的资源和 MCU 的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式
定时器在 MCU 内部就像一个小闹钟一样
- 根据时钟的输出信号,每隔 “一秒”,计数单元的数值就 “增加一”
- 当计数单元数值增加到 “设定的闹钟提醒时间” 时,计数单元就会向中断系统发出中断申请,产生 “响铃提醒”,使程序跳转到中断服务函数中执行
MCU 通过配置寄存器来控制内部电路的连接,通过不同内部的连接方式来实现不同电路,不同电路来完成不同功能。
像电路原理图中的模式选择啊,开关啊,一般都是通过配置寄存器来实现的 寄存器是连接软硬件的媒介,MCU 中寄存器就是一段特殊的RAM存储器
- 一方面,寄存器可以存储和读取数据
- 另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式,于是寄存器相当于一个复杂机器的 “操作按钮”
具体流程参见[[定时器寄存器流程图.excalidraw]] 相关寄存器的使用一定要去看参考手册,这里列一下STC89C52常见的表
寄存器符号 | 描述 | 包含了 | |||
---|---|---|---|---|---|
TCON | Timer control 定时器控制用 | TF Timer Flag定时器溢出标志 | TR Timer Run 定时器运行控制 | IE 外部中断源 | IT 外部中断触发方式 |
TCON | Timer control 定时器控制用 | Gate 门控端,配合外部中断引脚INT0像TR一样看来控制定时器(外部中断) | C/T决定是配合外部时钟的计数器,还是使用内置时钟做定时器 | M0 和 M1 用来控制定时器的模式 | |
TMOD | Timer Mode 定时器选模式用 | ||||
TL0 | Timer Low 0 | ||||
TL1 | Timer Low 1 | ||||
TH0 | Timer High 0 | ||||
TH1 | Timer High 1 |
寄存器符号 | 描述 | 包含了 | |||
---|---|---|---|---|---|
IE | Interruption Enable | EA Enable All 相当于家里的总电闸 这个设置开启时,只要出现了任何情况的中断,都会直接跳转到中断系统中 相反,如果没开,则会使得所有中断源不会生效,包括计时器T0溢出 |
ET0 Enable T0 允许计时器T0溢位中断的事件 | EX0 允许外部中断0的事件 | |
IP | Interruption Priority | PT0 控制计时器T0的中断优先级 |
学会定时器的使用,就可以用 MCU 实现时序电路,时序电路的功能是强大的,在工业、家用电气设备的控制中有很多应用
例如,可以用单片机实现一个具有一个按钮的楼道灯开关,该开关在按钮按下一次后,灯亮3分钟后自动灭,当按钮连续按下两次后,灯常亮不灭,当按钮按下时间超过2s,则灯灭。
数字集成电路可以实现时序电路,可编程逻辑器件(PLD)可以实现时序电路,可编程控制器(PLC)也可以实现时序电路,但是只有单片机实现起来最简单,成本最低。定时器的使用是非常重要的,逻辑加时间控制是 MCU 使用的基础
MCU 的中断系统是会和很多外围设备打交道的。 当 CPU 正在处理某件事情时,如果外界有突然的紧急事件请求,中断系统可以紧急停止正在进行的程序,然后去处理外界紧急事件,之后再回到原来被中断的地方继续工作
这种外界紧急事件就是中断源,一般会规定不同的中断源有不同的优先级。如果有多个中断源同时请求中断,CPU 会优先处理优先级高的中断源
注意,中断的资源和 MCU 的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等
MCU 的特点是一段程序反复执行,程序中的每个指令的执行都需要一定的执行时间,如果程序没有执行到某指令,则该指令的动作就不会发生,这样就会耽误很多快速发生的事情 要使 MCU 在程序正常运行过程中,对快速动作做出反应,就必须使用 MCU 的中断功能
使用中断的困难是需要精确地知道什么时候不允许中断发生(屏蔽中断)、什么时候允许中断发生(开中断),需要设置哪些寄存器才能使某 种中断起作用,中断开始时,程序应该干什么,中断完成后,程序应该干什么等等
中断学会后,就可以编制更复杂结构的程序,这样的程序可以干着一件事,监视着一件事,一旦监视的事情发生,就中断正在干的事情,处理监视的事情,当然也可以监视多个事情,形象的比喻,中断功能使 MCU 具有吃着碗里的,看着锅里的功能
对于在电路图中标注元件的数值,一般用下面的规定
- 473表示47 000
- 1003表示100 000
- 2v2表示电压2.2v
- 2r2表示电阻2.2r
LED 的长脚是正极;在电路图中,底边是正极
要实现流水 LED 灯的效果,可以配合独立按键使用位运算符
符号 | 效果 | 例子 or 理解 |
---|---|---|
<< | 按位左移 | 0011 1010<<1 -> 0111 0100 |
>> | 按位右移 | 0011 1010>>1 <- 0001 1101 |
& | 按位与 | 全1出1 |
| | 按位或 | 有1出1 |
^ | 按位异或 | 男女才可以生——按位异或3次可以交换位数 |
~ | 按位取反 |
使用了什么点
- 设置了延时函数,来控制点亮时间,以实现闪烁的效果
- 一次性给控制LED灯的寄存器赋8位二进制值
- 使用位运算符,达到控制亮点移动的效果 具体代码参见下面的文件
- 51 MCU 版本
四个引脚,没按下时只有两对平行导通,按下时只有两对的中间连接 [[独立按键原理图.excalidraw|里面是直观原理图]]
对于机械按键而言,按键按下去会有[[独立按键原理图.excalidraw|抖动]]。
- 可以使用硬件消抖(设置过滤电路)
- 或者使用软件消抖,在执行语句前延时一段时间(一般而言,抖动时间是5~10ms,但是往往是按下再松开才会改变,所以需要一个空循环来等待松手)
在使用按键进行操作时,一般是按一次按键可以改变某个寄存器的某一位
- 先根据电路图判断按键控制了哪个寄存器的哪一位
- 可以把这一寄存器的这一位作为一个条件表达式
独立按键的使用和消抖参见下面的文件
一位数码管是8个LED二极管组成的,分为共阴极(COM)和共阳极(GRN)连接 [[一位数码管的原理图.excalidraw]]
四位数码管是把四个一位数码管的相同段位的位选端并联(比如A和A的位选端并联) [[四位数码管的原理图.excalidraw]]
所以要显示一位数码管时,一般是先给公共端一个电平(电平高低由是共阴极(COM)或者共阳极(GNR)决定的);然后再给位选端一个相应的电平,从而控制哪一位数码管亮哪几个二极管段
- 位选,[[使用3-8译码器给公共端一个电平.excalidraw|先给公共端一个电平]],来选定是数码管的哪一位亮
- 段选,[[用数据传输器再给位选端一个相应的电平.excalidraw|再给位选端一个相应的电平]],来决定那一位亮的是什么显示图案 关键是看电路图确定公共端和位选端相应的引脚或者说io端口
- 可以将数码管那一位的显示图案对应的16进制数编成一个数组来方便地控制其显示
如果要显示多位数码管,则要把一位数码管的显示封装成一个函数 然后在一个循环里面调用多次,利用视觉暂留的方式同时显示。
这里要注意消影的问题,因为数码管显示的流程是
1.位选(确定哪一位)-> 2.段选(确定显示什么图案)
-> 3.下一次位选 => 4.下一次段选
我们在第一次位选(1)时就通过控制公共端来选定我们希望亮的那一位了,所以第一次段选(2)传入的图案的时候,根据原理图,是所有位的数码管共用的图案。
但这就导致了在进行下一次位选(3)时我们直接使用了上一次的段选图案(2)而不是对应的下一次段选图案(4)
因此需要优化一下一位数码管的显示函数,在段选后加入清零位选端的操作
----------------------------------------------------------
1.位选(确定哪一位)-> 2.段选(确定显示什么图案)-> 3.清零
----------------------------------------------------------
清零 = 延时 + 归零
具体代码参考
数码管的驱动方式有两种,一种是像上面那样,通过 MCU 直接多次扫描数码管 这种方式所需要的设备简单,只需要 MCU 和简单的硬件,但是会占用 MCU自己大量的CPU时间
还有一种就是使用数码管专用驱动芯片,它内部自带显存、扫描电路,MCU只需告诉它显示什么即可(这里推荐TM1640芯片)
因为键盘的按键比较多,为了减少io 口的占用,经常会把按键排列成矩阵的模式(就是一次控制或者读取一行或者一列)
于是采取逐行或逐列的扫描,就可以检测或者控制矩阵内部任意行任意列的元件状态
数码管扫描(输出扫描)原理是 显示第1位 → 显示第2位 → 显示第3位 ... 然后快速循环这个过程,最终实现所有数码管同时显示的效果
矩阵键盘扫描(输入扫描)原理是 读取第1行(列) → 读取第2行(列) → 读取第3行(列)... 然后快速循环这个过程,最终实现所有按键同时检测的效果
扫描的操作就是为了节省io口
[[具体的矩阵键盘扫描方式.excalidraw]]
具体代码实现参考下面的文件
报错记录
- 千万不要忘记写返回值,否则会没有效果喔
想法
- 实现一个矩阵键盘加法器
- 首先要有两个变量来接受两次函数的调用
- 把两个变量做加法,得到结果存在第三个变量中
- 然后清零三个变量,开始下一轮循环
小技巧
- 用1
10的十进制数获取一位09的十进制数(把0~10的数取余,因为10取余数=0) - 获取多位十进制数,可以把上次获得的数乘10在加上这次获得的一位数
- 还可以用数组来编码
-
数字I/O的使用
-
定时器的使用
-
中断
-
与PC机进行RS232通信 单片机都有USART接口,特别是MSP430系列中很多型号,都具有两个USART接口。USART接口不能直接与PC机的RS232接口连接,它们之间的逻辑电平不同,需要使用一个MAX3232芯片进行电平转换。USART接口的使用是非常重要的,通过该接口,可以使单片机与PC机之间交换信息,虽然RS232通信并不先进,但是对于接口的学习是非常重要的。正确使用USART接口,需要学习通信协议,PC机的RS232接口编程等等知识。试想,单片机实验板上的数据显示在PC机监视器上,而PC机的键盘信号可以在单片机实验板上得到显示,将是多么有意思的事情啊!
-
学会A/D转换 MAP430单片机带有多通道12位A/D转换器,通过这些A/D转换器可以使单片机操作模拟量,显示和检测电压、电流等信号。学习时注意模拟地与数字地、参考电压、采样时间,转换速率,转换误差等概念。使用A/D转换功能的简单的例子是设计一个电压表
-
学会PCI、I2C接口和液晶显示器接口 这些接口的使用可以使单片机更容易连接外部设备,在扩展单片机功能方面非常重要
-
学会比较、捕捉、PWM功能 这些功能可以使单片机能够控制电机,检测转速信号,实现电机调速器等控制起功能。如果以上七步都学会,就可以设计一般的应用系统,相当于学会十招降龙十八掌,可以出手攻击了。
-
学习USB接口、TCP/IP接口、各种工业总线的硬件与软件设计 学习USB接口、TCP/IP接口、各种工业总线的硬件与软件设计是非常重要的,因为这是当前产品开发的发展方向
消抖按键
在循环中用取反模拟开关