当前课程知识点:智能车制作:嵌入式系统 >  第四章 MCU外设与开发 >  4.2 IO外设的编程实操-点亮LED >  Video

返回《智能车制作:嵌入式系统》慕课在线视频课程列表

Video在线视频

Video

下一节:Video

返回《智能车制作:嵌入式系统》慕课在线视频列表

Video课程教案、知识点、字幕

各位同学大家好

我是清华大学工程物理系的曾鸣老师

欢迎大家回到我们

ARM微控制器与嵌入式系统的慕课课堂

这个章节呢

那我们开始学习ARM微控制器的开发

那么在上一小节

我们进入了第一类最简单的外设IO

以及认识了一个真正的芯片

它是长什么样的

我们应该怎么样把它用起来

那么这个小节

针对上节课最后提出来的我们如何

去控制一个小的led灯

或者读取一个按键开关的人的

输入状态来进行编程的学习

在我们学习的这样一个ARM Kineties KL25

这样一个具体型号上

我们使用哪些寄存器

怎样通过编程把它给使用起来

那么我们上节课最后讲的

这个任务非常简单

我们假设我们这个芯片的PORTD

PTD0到PTD7这八个引脚

而通过外围电路都接着八个LED灯

当然我是略去了这些灯

都应该存在的那一个限流电阻

那么换言之

因为上面接的是电源

所以我们这些引脚

每个引脚输出逻辑一的时候

这些灯应该是灭掉的

输出逻辑零的时候

这些灯因为有电流的存在导通就会亮

那么我们左边给了一个空的main涵数

说怎样通过一个空的没函数

编成让这样一些灯

如我们所愿地闪烁起来呢

但是闪的时候

我们爱玩的同学就可以

各种玩的花样和闪法

比如我让八个灯最简单都亮

或者都灭一起亮一起灭

交替灯走马灯或者呼吸灯

那么这一系列的技巧

都是编程当中我们可以玩的东西

那我们怎么样让它

如我们所愿的实现出来

是这节课所要学习的内容

那么在我们以后的学习当中

经常会给大家开个玩笑

说每一个模块要想使用起来要几个步骤

我说很简单

就跟把一个大象放到冰箱里一样三个步骤

这个开冰箱门大象塞到冰箱里

然后关门

我们使用这么一个模块的时候

也是这么三个步骤

我在这里列出了三个步骤

分别是把我们这个模块

所需要对应的时钟打开

然后呢把我们这些引脚

配置为我们现在所要使用的这个功能

比如我现在学了第一类外设

就是当GPIO来用

最后一个步骤呢对这个

IO模块的寄存器进行编程

设置它如我们所愿的

去进行输入输出的IO功能等三个步骤

那这三个步骤我现在没有按顺序来讲

我讲一个最直观的步骤

也就最后一步骤先讲

我们如何对IO模块进行编程

让这些灯如我们所愿地工作起来

那么在我展开之前

我们回忆回忆我们实际上

刚才介绍IO模块的内部原理的时候

已经隐隐约约讲到了

它是由若干个寄存器来控制的

每一个引脚会对应寄存器里头一个比特

有的寄存器里头一个比特

管这个引脚是输入还是输出

有的寄存器一个比特管这个

引脚输出的值是零或者一

有的寄存器呢

是在这个引脚是输入的时候

来读取这个引脚上的值是

高电压还是低电压

那么我们可以大致想象出

这么一个编程的模型

我们前面在讲编程模型的时候讲到了

在我们这样一个嵌入式的ARM微控制器里头

我们每一个模块

它的控制都会抽象为若干个寄存器

那么对于我们所讲的IO模块实际上呢

抽象出来的是六个寄存器

不仅有刚才提到的方向输入输出这三个

那么在我们这张表格里

我把其中另外三个现在先标灰

因为他们比较的复杂

在我们现在这个基本任务里面先不用

所以大家不用怕只用这三个寄存器

也就是我标为黑色的这三个

那么这些信息在哪里找到

具体到一个芯片的时候

可以在芯片手册找到

比如说我们现在所使用的KL25

这样一个Cortex M0的ARM微控制器

它的芯片手册呢一点也不厚

也就1000多页对吧

那么我们这个寄存器呢

在第41章节的771页可以找到

那么我来带着大家来读它

通过我们前面的学习

我们一个微控制器

他的编程模型从0000地址一直到

FFF 8个F的这个地址

32位的地址空间是统一地址映射

映射成了FLASH RAM和外设寄存器不同的模块

而所有的外设寄存器所分配的地址

这个是由ARM公司在设计

ARM芯片的时候所分配规定的

都在从4000 0000开始的这个地址段

那么相应的我们可以想象我们的IO

也一定在这个地址段里

映射为了某几个地址

是我们刚才所说的寄存器

C语言编程对这些地址写入对应的值

也就能访问这些寄存器

那么换到我们芯片手册所说的771页

我们会发现这些寄存器编成了很多组

我们刚才说每一组的外设的引脚

每一组的芯片的引脚

都会分成一个一个的端口

比如PORTA PORTB PORTC PORTD

当这些引脚对应的使用成IO的时候

我们就可以把它对应的称为

GPIOA GPIOB GPIOC这么很多组

每一组的GPIO都会管若干个引脚

相应的每一组的GPIO

都有自己的一套寄存器

所以从寄存器的地址映射来讲

我们看这个芯片手册就会发现

同样的这一组六个寄存器

从GPIOA若干个地址

GPIOB

再来一遍一模一样若干个地址

一直到我们刚才这个LED灯

所接的GPIOD这一组

也是六个寄存器占了六组地址

那么这六组地址的起始地址

大家从这个表格的最左边一列就可以看到

是400F F0C0这个地址开始

那么在我们之后的编程当中

我们很多时候用C语言编程

可能就会忘记了这个地址

我们直接使用它的名字

来进行编程就可以

在我们这节课

我们也是这么来进行编程

后面的课

我会给大家讲它背后的机制原理

以及它的语法上的讲究

那么我们可以看到在这张表格里

这六个寄存器如我刚才所说

中间有三个我们现在不用

我们最重要关注的是GPIOD Port Data Direction Register

这样一个方向寄存器

以及GPIOD_PDOR GPIOD的Port Data Output Register

还有GPIOD_PDIR

也就是GPIOD的Port Data Input Register

这样三个方向 输入 输出

分别对应的寄存器

他们各自有各自的地址

那么它们的功能和内部结构

是什么样的呢

我们看一看会发现非常简单

比如说GPIO的PDDR寄存器

放在GPIOD这个一组里头

GPIOD_PDDR 寄存器

它是一个32位的寄存器

所以它从它的起始地址开始

一共会占四个字节32个比特

那么我们在对外围的引脚

编组到一个PORT端口里头的时候

我们最多会有32个引脚编组在一个端口

那么换言之在我们这个

GPIOD这个编组里头

也最多会管32个引脚

那么这些引脚不一定在我们的芯片里

都全部引出来了

变成了亮亮的金属腿

但是如果他一旦出现

它一定会一一对应在这个比特里

那么我们非常直观地会发现

这个寄存器有32个比特

每一个比特都对应一个

可能存在的外部的物理引脚

所以对应着一个比特的值是零还是一

就直接对应了我们外围的芯片的引脚

PORTD上的0 1 2 3 4一直到31

这每一个引脚的方向

我们把对应的比特设为一

那么对应的这一个引脚

就会被配置为输出模式

我们把对应的比特设为零

这个引脚就会配置成输入模式

这就是GPIOD

PDDR寄存器的功能和含义

那么与之相应的GPIOD的PDOR寄存器

管PORTD的这一组32个引脚的输出值

那么这32个引脚对应的32个比特

也组成了一个四字节的寄存器

每一个比特对应一个引脚

所以当我们的引脚配置为输出模式的时候

这一个比特设置为一或者零

对应的值就会在引脚上

像我们上节课所讲的示意图一样

送到引脚上出现3.3V的

高电压或者零电压

于是我们就成功地实现了输出

那么与之相应的我们还有GPIOD

当然也可以是GPIO A B C任何一个

PORT的PDIR寄存器

也就是Port Data Input Register

那么这个寄存器我们会发现

它的写入功能

在芯片手册里标为了灰色

为什么呢

因为它是一个输入寄存器

它的值不是我们写给它的

我们任何时候读取它的值

都是由芯片根据外部所获得的

电压值是高还是低来生成的

所以我们对它写是没有什么意义的

而我们对它读 当这个引脚设为输入模式的时候

这里的PDIR寄存器的值

会相应的与引脚上

所接的电压值随之变化

所以引脚得到了高电压

我们从这里可以读到一

引脚变成了低电压

我们就可以读到零

就这么简单

那么这三个寄存器的组合

我们就完成了刚才所讲的

上一节课紧密联系的

最最简单的功能实现

设置方向输出还是输入

输出什么值输入读到什么值

那么回到点灯的任务

我们可以发现

在点灯的任务里面

我们所使用的是

PORTD的零到七的八个引脚

我们接了八个灯

那么我们做一个最简单的编程

我们要把这八个灯给点起来

让它亮或者让它灭甚至让它闪烁

我们应该怎么来写我们的main函数呢

那么很聪明的

有一定C语言基础的同学

我们可以稍微想想就可以发现

我可以这么写

首先我应该把引脚都设为输出

对吧我要控制灯的亮和灭

那么我不管让它亮让它灭

让它输出零还是输出一

我都是输出所以呢

PORTD GPIOD 的PDDR寄存器

它的最低八个比特

比特零到比特七

八个引脚PTD0到PTD7

对应的这八个比特

应该都变成一是输出

所以呢我们把这个寄存器赋了一个初值

等于0x0F(字幕菌注:老湿口误啦~应该是0xFF) 这是一个三十二位寄存器

所以你可以脑补

0xFF实际上是0000 00FF

一共三十二个比特

最后八个比特是1111 1111

写成十六进制是FF

那么后面我不再这样类似地这么复杂解释

比如说我们的初始值输出

我们可以给GPIOD的PDOR寄存器

设一个初始的输出值

那么这个输出值根据我们自己的喜好

我们可以给它最低的

八个比特都赋上零

那么我们输出的就都是低电压

大家可以想象那这个时候

是不是八个灯都亮了对吧

那么如果我们希望初始状态

这八个灯都是灭的呢

我们可以很灵活给这个寄存器赋上的值是

GPIOD的PDOR寄存器等于0xFF

这样最低是八个比特是1111 1111

那么随之这八个引脚都会输出逻辑一

也就是3.3V

那么因为电压等电位没有电流

这八个灯就灭了

那么我们可以通过简单的两句话

控制外面一个灯的亮或者灭

那怎么才能玩起来呢

我们来玩一个最简单的玩法

我们写一个for循环

在里头我们假设我们已经用C语言

写好了一个delay函数

这个delay函数可以跟一个参数是

比如说毫秒为单位

我们称为delay毫秒

参数1000 等于一秒钟

那么这种函数以后大家可以学习怎么写

我给大家提示一个最简单的写法

就是你做一个足够大的死循环

让它循环几百万次上亿次

这是在我们计算机系统里

它就会浪费掉一段时间对吧这就是delay

那么跳开这个delay函数

每次进for循环delay一段时间

我们再做一件什么事情呢

我们要读取我们

GPIOD_PDOR寄存器的值

知道我们之前给这个引脚赋的是什么值

这个灯之前是什么状态

我们给按位取个反

赋回给这个寄存器

那是不是原来是一的变成了零

原来是零的变成了一呀

那这样一句话放在这里

是不是每隔一秒钟我们会发现

我们把这个引脚的值变了一下

那个灯是不是由亮就变灭了

那么如果我们有良好的C语言基础

一定要具备逐行去读这个代码

脑袋跟着他

用脑补的方法来运行程序的

这样一种初步能力

一个for循环完了

回来我们再delay一秒钟

这个时候上次亮的再翻转一次是不是灭了

于是这个程序持续源源不断运行起来

它的流程是初始化方向

给灯一个初值比如说点亮

然后进循环 延迟一秒钟

灯由亮变灭

再回循环 再延迟一秒钟

由灭变亮 如此反复永不退出

那我们就得到了一个

八个灯不停周期闪烁的例程

那么我多说一句 也就是有了这个例程

在我们实验的范畴

在我们这个爱好者的范畴

其实大家就可以花样玩了

比如你希望体现出这个灯

像坠物一样从上面一个一个落下来

越攒越多是什么效果

你希望这个灯跟街头的霓虹灯一样

形成走马灯是怎么做

然后你希望这个灯像呼吸灯一样

看到常亮常灭渐亮渐灭应该怎么做

提示一下 在这里我们可以做人的视觉暂留

所以小小一个点灯当中

我们可以有非常非常多的玩法

可以玩的非常非常的酷

但是反过来我们应该从当中

提炼它最后的数学模式

用编程语言加以体现

这是我们应该掌握的能力

也是跳出任何一个单片机微控制器

具体型号通用的知识

好第一个步骤完成了

那么我们放在这里

说如果像刚才那样写还有什么缺点呢

同学们想一想

如果是有一定基础的同学会说

老师你这个GPIOD和这个PORTD

寄存器有32个比特能管32个引脚

你刚才用的全部是赋值语句

那你把最后八个引脚的值赋了

方向定了 怎么把带上前面24个引脚

也给强行变了值赋了值呢

对 这是不好的

在一个复杂应用的时候

每个引脚都有它特定的功能

如果这些引脚刚好也当IO用

我们这样写程序可能就会引起BUG

严重的时候可以引起我们的系统

没有如我们所期望的去工作

所以呢在真正规范的写法里头

我们会尽量学习C语言的"与"或者"或"

只改变局部几个比特的值的使用

这段代码大家可以自己读和琢磨

我就不展开详细的解释了

那么把大象塞到冰箱里的三个步骤

我们完成了第三个步骤

好像这个灯已经能运行了

那这个程序写进去能干活吗

还不行还缺前面两个步骤

我们来看看前面两个步骤该怎么做

这也是未来的其他模块比较通用的步骤

那么第一个步骤我们用了IO模块

就应该把IO模块的时钟打开

让它不再是低功耗的

而是实实在在运行起来

否则我们如果直接写下面几个语句

在我们的开发环境里

编译会得到一个错误

说这个程序写的不对

那么时钟模块怎么打开呢

我们会看一看在芯片手册的第192页

在别的芯片里你也可以找到类似的地方

叫做系统集成模块

它里头有很多寄存器

如果你仔细的查找

会发现一个系统集成的时钟的门控寄存器

也就是clock的gating寄存器

它的编号是第五个

这个寄存器如果把它的解释内容打开

我们会发现一些很熟悉的地方

它有若干比特

分别写着PORTA PORTB PORTC PORTD PORTE

所以我们一旦用到了这一个端口上的引脚

我们就应该把对应的这个端口的时钟打开

那么刚才回忆一下刚才用到的点灯

是哪个端口呢

是PORTD 对吧

PORTD的 PTD0 到PTD7

所以我们应该把这个PORTD的时钟打开

它就一个比特

那么它的含义也非常简单

默认状态下这个比特是零

始终是关着的是低功耗的

我把这个比特置一

这个时钟就打开了

说起来很麻烦其实非常简单

那么写成语句就是一句话

也就是System Integration Module SIM这个模块里头的

SCGC5这个寄存器

我们把它的值

PORTD对应的这个比特置一

这个时候我采用了一种简单的写法

把一左移十二位与它或

大家回头看看

它是不是对应的

是这个第十二个比特呢

这就是我们把这个时钟打开

所以有了这个步骤我们就完成了

这样一个编程的三个步骤当中

第一个步骤把时钟打开

那么只剩下还有第二个步骤没有做了对不对

第二个步骤是把我们这个PORT上的引脚

配置为我们这个GPIO模块用

而不是给别的模块用

我们在上一节课的概念里讲过

它可以给通讯给ADC给别的模块用有可能

当IO用我们需要配置

回到我们的PORT的概念

我们会发现我们有很多很多引脚

它变成了一个一个的端口

像PORTD PORTD0 PORTD7

它们首先属于PORTD

然后它们自己有个序号分别是0 1 2 3 4 5 6 7

因此每一个物理存在的引脚

一个物理上存在的引脚

它都有自己一个独立对应的寄存器

来配置它的功能

这跟刚才不一样

刚才是所有的引脚都在一个寄存器里

32个比特 一个比特对应一个

在对引脚的功能进行配置的时候

这是一个非常专门的事情

所以每一个引脚都有一个对应的寄存器

它的名称它的功能

在我们这芯片手册第177页

也是一个独立的章节

叫做Port Contrl and Interrupt模块

那么在这个模块里头

每一个引脚都有一个对应的寄存器

它的名称叫做PORTx

比如A B C D_PCRn

n比如说01234567

所以我们可以想象PTD0对应的就是

PORTD_PCR0寄存器

PORTD1对应的就是PORTD_PCR1寄存器

每个引脚对应一个寄存器

那么它的功能是什么呢

它有32个比特

每一个引脚都由32个比特来配置

标为灰色的部分是这个忽略 保留的比特

标为白色的部分

可以控制这个引脚的很多功能

比如说它的第16到第20个比特这四个比特

是控制这个引脚的中断功能

我们以后会讲

最下面零到七之内的若干个比特

可以控制引脚是否有上拉电阻

输出的电流 大小等等

这些高阶的功能

大家日后可以通过手册

自行的了解和学习 并不复杂

那么它的第八个比特

第九个比特和第十个比特这三个比特

也就是我们中间有个小缺块这个区域

这三个比特大家想想

从000到111可以组合成八种选择

这八种选择的值赋给这个寄存器

就是选择我们这个引脚

使用它的第几个功能

什么叫第几个功能

大家回忆一下

在上一节课最开始的时候

我们拉出了这个芯片手册里

一个芯片的Pinout的表格

大家还记得吗

每一个名称为PTD0 PTD1 PTD2的引脚

它可能都有若干种复用的功能

我们称为ALT1一直到ALT7

那么我们在这些功能的复用里面

我们可以发现PORTD0的所有IO功能

都刚好是它的第一个功能

也就是001号功能

后面它复用的其他功能

还有通讯还有别的

所以对于任何一个引脚

我们应该把刚才寄存器里

对应的这三个比特

赋值为001选用第一个功能

也就是告诉单片机告诉微控制器

这个引脚使用它的功能一

也就是当IO用

这是我们刚才说的

编程的第二个步骤啊

所以这件事情说起来很复杂

操作起来很简单 八句话

PORTD_PCR0这个寄存器赋值等于0x0100

0100就是比特八到比特十这三个比特

正好二进制是001对不对

那么这样的语句重复八遍

从PORTD_PCR0一直到PORTD_PCR7

我们都给它赋值等于0100

也就是告诉我们的程序

这八个引脚请都当第一个功能

也就是IO使用

而不是当别的功能使用

那么我们完成了大象塞在冰箱里的

第二个步骤以后

再回过头来看我们的程序

已经非常的完整了

第一个步骤

我们把这样一个芯片的这个IO

我们用到的PORTD它的时钟打开

第二个们步骤呢用八句话

把PORTD0到PTD7这八个引脚

指定为给IO使用

而不是给别的功能使用

最后一个步骤呢到了IO模块里头

根据我刚才前面所讲的

我们把这八个引脚

它的对应的输入和输出进行编程和配置

把它们设置为输出

赋上零的初值

让灯亮起来接着在一个循环里

我们每延迟和delay一段时间

然后让这个引脚的值进行一次翻转

于是我们会看到

这外面的八个灯已经成功的被我们点亮

它们一起变亮

然后隔一段时间一起变灭

然后开始闪烁

于是我们就完成了这样一个

IO点灯的一个最基本的编程

熟悉了刚才所提到的

这若干个步骤当中寄存器的使用

而有了这样一个基础

首先我们大家会感觉什么呢

会感觉IO的编程小菜一碟

其次呢我们还觉得

我们其实还可以有更多可以玩的

比如说我可以把这延迟的时间改的更短

看看会发现什么情况

改到短到你眼睛无法分辨

然后我们在这个for循环里头增加更多的结构

让灯的闪烁可以变得更加的花样

我们可以尝试着把这当中

我们想显示的每一种模式

提炼出它的数学的模型

数学的模式再用编程的语言加以实现

乃至实现类似于像iPhone上的呼吸灯

这样的效果

这样大家就可以放手去玩

好我今天这个编程的内容呢到此结束

下一个节课我会给大家介绍一下

IO编程的注意事项和高阶使用的技巧

智能车制作:嵌入式系统课程列表:

第一章 概览

-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

第三章 MCU基础

-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

第四章 MCU外设与开发

-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

第五章 MCU与嵌入式系统设计

-5 智能车视角的嵌入式设计

--Video

Video笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。