当前课程知识点:智能车制作:嵌入式系统 > 第四章 MCU外设与开发 > 4.3 IO外设的进阶知识 > Video
各位同学大家好
我是清华大学工程物理系的曾鸣老师
欢迎大家回到我们ARM微控制器与
嵌入式系统的慕课课堂
我们在第四个章节里
前面两个小段已经分别学习了
IO模块的基本概念原理方法
以及在我们KL25这样一个具体型号里
它所涉及到的寄存器
如何通过一个简单 三个步骤的编程
把八个灯给点亮
了解了编程的模型和方法
那么这个小节呢
我们会在这个基础上
再对于IO的日常使用
进行一些更进一步的探讨和学习
我们学习了如何通过简单三个步骤
若干句简单的C语言语句编程
来把这八个灯给点亮
那大家会觉得 哇 小菜一碟
接下来我们还要再看一下
IO在使用当中不是那么小菜一碟
或者稍微比看上去
要复杂一点的一些注意事项
比如说最最简单的在点灯这件事儿上
我这两张抽象的图
省掉了当中的限流电阻
但大家最直观的一个感受是
我们LED发光二极管的接法是一正一反
大家都会觉得从引脚连接一个
LED的阳极直接接地
这样一种正向的连接
是比较符合很多初学者的感觉的
我用一个引脚来驱动一个LED
给高电压 灯就亮了
给低电压 灯就灭了
为什么老师给的例子里头
要把LED倒过来接
从电源接阳极
再通过限流电阻到我们的IO
让IO输出高电平的时候灯灭
IO输出低电平的时候灯亮呢
这是一个问题
其实在我们数字电路漫长的发展历程里
很多数字器件和它的引脚
拉和灌电流的能力是不一样的
也就是对外输出电流
对内灌入电流的能力是不一样的
很多时候
灌电流的能力要大于拉电流的能力
也就是说更善于向内输入电流
而在我们所使用的这样一个微控制器
我们刚才看了它的手册
不知道大家还记不记得
它对内对外输出电流
也就正向和反向电流的局限
都是25个毫安也就是这样的微控制器
在他的IO引脚上专门做了强化
增加了电流的驱动能力
所以25个毫安点亮一个LED
无论正或者反都是绰绰有余的
有的同学会说
我不知道点亮一个LED要多少电流呢
这就是我们在电子学上一个长期的积累
我们一般在这样的弱电电路里头
我们的VCC
比如我们系统是3.3伏
那么我们的限流电阻一般在百欧到K欧量级
所以我们这个二极管的
流过的电流一般就在毫安量级
电流大一点二极管就亮一点
电流小一点二极管就弱一点
但是呢我们的电流不能超过这个
发光二极管的额定功率或者额定电流值
那么是不是在我们这样一个
微控制器上这电流就随便接了呢
不是
记不记得看芯片手册的时候我给大家提示
注意过另外一件事儿
就是我们这个芯片的VCC 3.3伏引脚供电
有个最大的供电电流值是120个毫安
也就说如果我们采用第一种接法
由引脚来直接驱动发光二极管
所有的电流都需要通过
我们芯片的电源芯片
来统一的由芯片内部获得
所以这些电流都会叠加到
我们的电源引脚所获取的电流上
而那是有120毫安的总限值的
所以基于这些考虑
考虑到芯片的发热
我们会更多地使用
反着的这样的连接方法
这是在我们设计包括智能车 机器人的
很多类似电路当中
同学们往往会忽略的
所以说数字电路和嵌入式系统
一定不要仅仅站在编程的角度去理解
一定要抠它的电路的本质和原理
第二个方面呢 是我们在用IO
获取一个外部量的时候
比如说一个按键有没有按下
一个量是零还是一
我们往往会把它觉得很理想
得到零就是零 得到一就是一
而在现实生活当中往往不是这样
从比较慢的尺度来看
两类现象我们都可以体验过
一个呢是我们的手机一响
经常会旁边的喇叭发出干扰
哗哗声 对不对
那么我们的电子学器件
往往是生活在一个复杂的电磁环境里的
我们引脚上的电信号
经常会受到周围电磁波的干扰
产生短暂的这种干扰信号 零或者一的跳变
这些跳变如果被我们的IO引脚错误的拾取
那么会引起我们对一些控制信号的误判
其次呢在我们一个简单的按键
这样一个操作
人对机器的输入的时候
有很多同学做了一个简单的按键的程序
最后跟我说老师这个电路不好
这个按键不灵
为什么呢
因为按下去以后程序没有让
他期望的一按发生作用
这个时候我们会发现
因为我们的按键是一个机械元件
它是由一个弹簧金属片的按下
和弹起来发挥作用的
而这样的机械金属片
它往往无法做到干净利落的由一变零
由导通变断开
而是会产生若干次机械的振动
这些振动会形成时间持续较短
但是对于计算机的视角
注意计算机的视角
一个时钟周期在纳秒微秒尺度看来
是持续了一个足够长时间的若干次跳变
那么这样若干次跳变
如果我们的程序写的不合适
每一次跳变都会被识别为一次按键
所以大家会发现
按键时灵时不灵
为什么呢
如果跳变了奇数次
你的程序正好发挥了作用
如果跳变了偶数次
你的程序比如点个灯
它就可能体现为 什么都没做
因为偶数次归零了
那么像这样一些我们
称为glitch 称为干扰的这样一些东西
是我们在IO使用当中要注意的
还有一类注意的事项呢
就是我们如何去掉这样一种干扰
我们往往会采用硬件和软件的方法
硬件去掉这样的跳转
我们最容易想到的是什么
是加入一个RC电路
使它的电压无法迅速的快速的
发生零到一的多次变化而只能渐变
那么我们往往会在我们的按键电路
不像我们上节课所看到那么简单
只是一个电阻加个按键
而是像图中这样加上一些RC
加上一些单稳触发
来实现硬件的去抖
那么软件的去抖呢
我们往往会考虑真正产生
这个信号的物理机制是什么
比如说我们按健是一个人的输入
大家想想人的反应在什么尺度上
如果没有概念的同学可以想想膝跳反射
敲下你的膝盖你什么时候会产生反应
对于一个人
他可能在百毫秒尺度
而对于一个树懒
它可能在分钟尺度
我们的输入决定了
我们的物理量的大小
那么一个人输入无论如何多快
在十毫秒尺度不可能发生若干次
所以我们可以在检测到了
一个跳变的信号的时候间隔
五毫秒 十毫秒再检查一次
这个值是否仍然存在
因为人按下键在十毫秒之内肯定来不及松开
通过这样的方法
我们可以过滤掉一定
时间长度以内的干扰信号
这是一种软件的方法
日后我们如果学习了信号与系统的概念
我们会发现
我实际上是做了一个时域
或者频域上的滤波
等效于RC电路在硬件上所做的事情
这就是数字电路和嵌入式开发里头的本质
那么站在一个更快的角度
我们往往会想要用IO
或者用IO引脚衍生出来的时钟模块
这些功能来对一个周期跳变的
信号进行计数或者检测
这个时候我们会说
如果这个输入信号的频率过高的时候
使用IO不一定是个好方法
或者换一句话说
用IO做这样的事情的时候
我们一定要考虑
这个频率和周期是多少
我们有同学说做智能车
做智能车的时候
我们用一个霍尔元件去检测轮子的转速
轮子每转过一下会产生一个脉冲
说这个速度测的不准
数到的脉冲数远低于轮子真实转过的圈数
为什么呢
检查会发现 哦
因为这个轮子
通过霍尔元件产生的脉冲太窄了
那么这就回到我们最最本初的一个概念
就是在嵌入式系统里头
我们每个模块都是一个数字系统
它是在一个时钟驱动下的
所以我们引脚上的电压
只能在一定的时钟频率下
被刷新到我们的寄存器
那么这样一个最小的时钟周期
最快的这样一个频率
就是我们这样一个IO模块
工作在一个特定频率的
微控制器里头的极限
所以当我们产生的输入信号
至少要大于这么一个时钟周期的时候
才能体现在我们的寄存器里
而在我们寄存器里这个值
所体现出来的最小时间间隔
也至少是一个周期
换言之这个值变成一或者变成零
在寄存器里至少维持一个周期的时间
那么其次呢
如果这个信号真实存在的时间
比我们的最小时钟周期还要短
就跟这个图当中所示
它就压根不会体现在
我们这个寄存器上
我们就会漏记
如果我们使用IO
我们在寄存器里
看不到这样一次值的跳变
如果我们使用这个微控制器的计数
或者脉冲计数这样的模块
我们会发现数出来的数就比它短
这也是我们需要考虑的
那么这样一个时钟周期的极限值
在哪可以找到呢
在我们所使用的任何一款芯片
它的电气特性指标的
这个参数表里一定会给
然后除掉这些
我们的IO模块
不仅仅可以通过寄存器被动的去读取
我们在前面学习中断的原理的时候
我们会说很多的编程会工作在
轮询的模式和中断的模式
我们反复读寄存器
看寄存器所体现出来的这个
IO的值有没有发生跳变
这叫做轮询
那么实际上我们的IO
也可以工作在中断方式
每当IO上的状态值发生了变化
产生一个中断主动的通知CPU
来查询这种中断
调用程序做相应的处理
这样的话
就工作在了中断方式
对于数字IO
它可以工作在若干种
中断源产生的中断方式
我们可以在这个IO引脚
每当产生上升沿
也就是电压由零变一的时候产生中断
也可以让这个IO引脚
在下降沿由一变零的时候
或者是Both Edge 上升沿和下降沿都发生中断
或者是用电平触发
当它为高电平或者
为低电平的时候产生中断
那么这些东西在哪里设置呢
首先我们一方面要对照我们的芯片手册
找到它的中断源 中断号
以及对应的中断编程的注意事项
其次呢
就在我们刚才所讲的
每一个引脚对应的PORTx
比如说PORTD PORTA_PCRn
比如说PCR0 PCR1 PCR3
这样一个每个引脚对应的
这个寄存器里可以配置
在哪呢
在上面的16到20这四个比特
可以进行我们中断源的配置
使用DMA或者使用不同类型的
上升沿下降沿触发的中断
那么对它进行配置加上我们之前讲过的
中断的向量表中断函数的若干的设置
我们可以对任意一个引脚
工作在中断模式
当外部的信号发生变化的时候
自动调用一个函数来完成编程(功能)
那么在我们所使用的Kinetis (Cortex M0)这样一个
MCU里头
这样一个微控制器里头
具体到KL25这个型号上
所有PORTA和PORTD的引脚
都是具有中断的能力的
那么More and Better我们再来讲讲更多的
我们刚才在编程里头有很多高阶的
有兴趣同学会认真的揣摩老师给的程序
会给我挑刺
说老师你说我们一个方向寄存器
和一个赋值寄存器
管了32个比特
对应32个引脚
对它统一进行赋值是不好的
所以你告诉我们在初始化的时候
方向寄存器用或 赋值寄存器
也就是PDOR寄存器用与
分别想办法让它的最低八位
只变成一
和只有最低八位变成零
来实现只对最低的八个比特赋值
前24个比特不变
但是到了循环体里
你这句话写的并不够漂亮啊
你是直接把这个寄存器
整体读出来32个比特取反赋回去
那除了这八个灯每次一起亮或者灭
那前24个引脚的值不又是变了吗
对 所以呢
如果我们在编程的角度
要做只改变后八位的值的话
我们需要把这个for循环写得更加的复杂
是不要写if和else的判断语句来把它做的更完善
这是聪明的同学或者细心的同学
注意到的非常好的一个事
那么我现在要讲的是在微控制器设计的时候
也给我们留出了这样的寄存器
让我非常简单地用单指令
能够完成这样的操作
那是哪几个寄存器呢
就是在一开始
我所讲的标灰的这三个寄存器
作为高阶的使用
它们分别是这个PORT的翻转寄存器
SET赋值寄存器
和Clear清零寄存器
那么这三个寄存器非常的简单
我几乎用一句话就能讲完
比如PSOR寄存器 GPIOx的Port Set Output Register
它就有32个比特
对它进行读操作是忽略的
对它进行写操作
任何时候对它写一
就会使它对应的引脚变成一
而对它写零 是对应的引脚的值不发生改变
注意这是不一样的
写一变成一 写零不发生改变
所以我们希望最低八个引脚变一的时候
是不是我们对它进行
最低八个引脚写一
前面二十四个引脚写零
前面二十四个引脚的值就没有变
与之呼应的PCOR寄存器Port Clear Output Register
是对应的比特写一
那么对应的引脚就会清零
注意写一清零
而对应的比特写零的时候
对应的引脚的值不发生改变
换言之你希望把哪个引脚上的
电压变成零电压
你对应的比特
赋一就可以了
那么对于我们刚才的翻转
这好像还不够方便
没关系 还有第三个寄存器
叫PTOR寄存器Port Toggle Output Register
它的定义是32个比特里头
对应的比特写一
使这个比特所对应的引脚上的电压值
在原来的值基础上进行一次翻转
由一变零 由零变一
而你希望不改变的引脚
它对应的比特写零就可以了
有了这个寄存器
刚才我们所说的那个编程非常简单
只用给这个寄存器赋上一个
多少0x0000 00FF的值
前24个比特是零
最后八个比特是一
那么就能在每一次的循环里头
只让最后八个引脚进行翻转
一个语句漂漂亮亮地完成了这件事儿
编程的时候会非常的舒服
那么有一些有洁癖或者有瑕疵的同学
很多程序员都有洁癖 对不对
会说老师那我觉得还有个很别扭的事情
我用了八个引脚
刚才的这个PORTx_PCRn这个寄存器的语句
我就得写八遍 每一次 每一句
都要定义这个引脚的值
当哪个功能用
当一号功能用 能不能简单一点
答案是也是可以的
在我们对应的这个模块
我们芯片手册的177页里头
除了PORTx_PCRn这个寄存器
还有PORTx_GPCLR
和PORTx_GPCHR这两个寄存器
它们各能管十六个引脚的集中配置
对于它们的功能
我在这里就不展开讲了
有兴趣同学可以到指定的页面
看这个芯片的英文说明
锻炼阅读英文的能力
学会如何用一句话完成八个引脚的功能配置
那么IO模块的高阶讲解呢
到这基本上就结束了
所以呢 我们学习了IO的使用
也能完成花样的点灯
那么在后面的环节
我会花几个小节 以它为例来讲
微控制器开发的开发工具
和一般性的开发方法
更重要的是深入理解
我刚才给大家讲的
那些看上去很简单的c语言
赋值语句背后的含义是什么
最后呢
我们会动手实践
来把按键的功能 引脚的功能
组合使用起来
玩一些好玩的程序
那么在后面几节课
我会给大家讲解
今天的课就到此结束
-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