当前课程知识点:ARM微控制器与嵌入式系统 > 第五章 ARM微控制器的各种外设 > 5.6.2 ARM微控制器外设:SPI寄存器与编程 > 5.6.2 ARM微控制器外设:SPI寄存器与编程
各位同学大家好
我是清华大学工程物理系的曾鸣老师
我们来继续进行我们
ARM微控制器与嵌入式系统里
SPI外设的学习吧
那么在上一个单元里呢
我们学习了SPI的基本的时序结构
以及它能怎么用
包括它一些基本在这个数据帧
和时序上的一些概念
知道了它又灵活又令人烦恼的
这个灵活性 这些相位 时钟这些
那么现在把它都放在一边
我们究竟怎么才能编程
把我们的一个ARM微控制器
或者我们KL25这个芯片上的
SPI的通讯接口给用起来呢
然后我要问大家
把一个SPI外设用起来要几个步骤
如果在我的课堂上很多同学
就会齐声大喊要三个步骤
对 是三个步骤
还是三个步骤
哪三个步骤呢
第一我们要把这个SPI模块
对应的这些时钟打开
第二呢还是要把我们的引脚
指定给SPI模块来用
最后我们要看一看一个SPI模块
这个外设究竟有哪些寄存器
我们对这些寄存器怎么编程和定位
能够实现对它的控制
第一个步骤非常简单
就是把这个时钟打开
这个我们可以讲快一点了
把这个时钟打开呢
还是用到我们芯片手册里
提到的这个System integration这个模块里头
大家熟悉那几个寄存器
一方面是把SPI模块所对应的引脚的
IO对应的时钟打开
另外一方面就是把我们要用到的SPI的
比如SPI0 SPI1这样的模块时钟给打开
那么除了把时钟打开
我们还要建立一个基本的概念
就是从时钟tree上来讲
我们SPI0和除了SPI0之外的SPI1
这几个不同的SPI模块
一个微控制器里可能有若干个
它们的时钟源是不一样的
SPI0使用的是我们这个微控制器的Bus Clock
SPI1及其之后使用的是System Clock
如果我们这个微控制器
在没有单独启用锁相环 锁频环的时候
上电默认状况
我们的System Clock
就是原来提到过很多次的20.97兆
而Bus Clock是它的一半
也就20.97兆除以2
所以我们之后在算我们SPI通讯的
那个速率做分频的时候
用来计算的基本频率SPI0和SPI1
是有一点点不一样的
大家注意一下就好了
那么这些信息在芯片手册上
写的非常清楚
那么很简单我们两句话
一个是打开我们对应的SPI模块的时钟
一个是别忘了打开我们对应这个SPI
所占的这个Port的时钟
一个在SIM的SCGC4里
一个在SIM的SCGC5里头
把它写完
第一个步骤就完成了
第二个步骤呢
把我们所用到的SPI引脚指定给SPI用
而不是当IO用 对吧
那么这个功能就是大家非常熟悉的
每个引脚一个的那个寄存器
PORTx_PCRn寄存器
对于每一个对应的引脚
选择它所要使用的第几号功能是SPI的
这个我们应该慢慢的学会对电路图
我接到了哪个引脚上
这个引脚是哪个Port的第几号引脚
它的第几个功能是SPI
是SPI的MISO还是SPI的MOSI
把它看清楚
自己把这句话写出来就可以了
那么这就是第二个步骤
那么我在今天这节课里重点
前面都已经讲过很多遍对吧
讲讲第三个步骤
对于SPI的寄存器进行编程
真正让它能够收发数据该怎么写
那么大家已经很熟悉了
我们每一个外设都会有一组寄存器
来实现对它的控制 状态读取
以及这个数据的传输等等这些功能
那么我们在一个微控制器里头
SPI模块可能不止一个
比如刚才提到SPI0 SPI1
那么SPI的每一个模块
都有一组寄存器
就是大家所看到的这六个
C1、C2两个控制寄存器
然后这个BR
一个速度速率波特率的寄存器
一个S状态寄存器
一个D数据寄存器
还有一个M match寄存器
那么如果我们看地址映射表的时候
我们会发现在400开始的
这个外设的寄存器这个地址段
4000几几这个地址段里头
每六个寄存器一套
每个SPI模块对应这个寄存器
一套一套来了两遍 对吧
六个是SPI0的 六个是SPI1的
那么对于这六个寄存器 它所控制的背后
是我们一个SPI模块
它实际上的一个框图
一个电路的实现
这个前面也讲过很多次
大家概念越来越清楚
寄存器从编程的角度
是对于地址映射项存储单元的读写
这些存储单元我们写了0或者1以后
表现为对电路上的高低电平
乃至时序对于一些实际电路的控制
从而控制我们的外设
产生我们要的功能
所以寄存器是外设电路与编程之间的
一个interface一个接口
那么我们一个SPI的框图
如果展开来不详细讲的话
势必会有时钟的产生和驱动
在这个时钟上面去获得
我们总线写进来的数据
在这个时钟的驱动下
把这个数据一个比特一个比特的发送出去
同时一个比特一个比特的检测
接收到的数据
这样一系列的功能
构成我们一个SPI的完整通讯模块
那么从编程模型上来讲
我们刚才讲到的这六个寄存器当中
对于基础使用最核心会涉及到是5个
对于本门课程
大家待会做实验会涉及到的其实只是四个
我来一个个给大家讲
第一个我要讲的SPIx_D这个寄存器
就是Data寄存器 数据寄存器
这个寄存器其实是挂在总线上的
一个缓冲结构
一个缓存寄存器
我们可以通过总线给这个寄存器
写入一个值
再把它拷贝到我们的电路里
让电路在时钟驱动下
把这个值一个比特一个比特
按照约定好的时钟 相位 极性
发送出去 对吧
那么这样一个数据寄存器
大家需要理解的最重要的一个点是什么呢
是跟UART异步串行通讯接口一样
它是一个数据寄存器同一个地址映射
但是读和写操作指向了
两个不同的电路实现
所以你对它写
是写入了要发送的数据
你对它读是读到了接口接受到的数据
并且在SPI里头
每当你对SPI_Data这个寄存器
_D这个寄存器完成一次写操作
写入一个数据
其实就是启动了一次SPI的同步串行通讯
那么发送时
数据会从这样一个SPI地址寄存器
移位到我们的移位寄存器
驱动到电路引脚上
接收时
电路引脚接收到的数据
会移到另一个移位寄存器
被我们的读操作读出来
所以是同地址读写不同操作
不同含义
大家理解这件事
那么第二个寄存器呢
是SPI的控制寄存器1
这个控制寄存器里头有非常多的比特
我将我们在这门课里
会用到的在图上标为了绿色
那么它的含义在前面
如果大家理解了上一节课所讲的
数据帧的结构
会非常好理解
比如第六个比特SPE控制的是
SPI模块enable或者disable
就是总的开关是否启用SPI模块
第四个比特MSTR
控制的是我们这个接口
现在工作在master模式还是slave模式
比如说我们跟一个SPI接口的
温度传感器进行通讯的时候
温度传感器是Slave
那我们就应该设置
我们单片机的SPI接口
工作在master模式 对吧
如果是两个单片机互相通讯
用SPI接口的时候呢
我们是不是就应该一个外设是master
一个外设是Slave来实现通讯
然后然后接下来的三个比特
都是在我们第一个单元里
给大家讲的数据帧的结构里用到的
第三个比特CPL控制的是时钟的极性
那么第二个比特CPHA呢
控制的是我们在第一个单元讲的
那两种相位模式使用哪一种
然后最后一个比特第0个比特
LSBFE控制的是
我们每一笔通讯比如八个比特
是先发LSB还是先发MSB
所以大家就会意识到我刚才讲过
这三个比特的控制两两组合
一共八组状况
都是符合SPI通讯的协议标准的
那么就要根据我们所要通讯的外设对象
和我们通讯双方的约定
来决定我们程序该怎么配置它
怎么写
配置对了这个通讯才能持续正确的完成
否则就可能通不上
或者接收不到数据
然后第二个控制寄存器
在我们这门课的实验范畴里
并没有我们用到的比特
在这里大家看一看就略过去了
然后我们接下来还要讲的
倒数第二个寄存器
就是我们SPI通讯的波特率寄存器
波特率寄存器一共8个比特
第四个比特和上面三个比特
除掉一个不用的以外分成了两段
也就三比特和四比特
这两段的数符合一个计算公式
这个计算公式就是波特率
是我们这样一个模块的输入时钟
大家想一想
就是SPI0 SPI1
分别是20.97兆或者20.97兆的一半
来除一个分频因子
分频因子的计算式
是我们的SPPR这一段加1乘以2的
SPR加一次幂来算出它的分频因子
所以大家可以算一算
我们这两段组合
最大和最小分别是多少分频
或者我给大家一个小的思考题说
20.97兆的总线时钟
在我们这个寄存器的设置里
最大SPI的速率是多少
最小SPI的速率是多少
那么有的同学说
那这个设置的范围这么大
我设成多少合适呢
比如UART老师说9600就合适
我们做SPI做多少合适呢
根据我们编程的对象
比如说你读一个温度传感器
你读一个加速度传感器
你读一个对象
它的手册里会告诉你
它的SPI接口
建议你在不快于多少的速度
或者在多少速度范围内与它通讯
那么我们就应该编程设置波特率
让我们在对应的时钟上与它进行通讯
最后一个也是最复杂的一个寄存器呢
就是我们SPI的状态寄存器
状态寄存器里最核心的是两个比特
一个是SPTEF比特
一个是SPRF比特
跟UART非常非常像
这两个比特虽然都名字上有Interrupt flag
中断标志位的含义
意味着在启用了中断的时候
它们置位会导致SPI模块
产生一个通讯中断
我们在这里没有展开讲
但是我们在普通编程的时候
我们可以把它当做标志位
每发起和结束一次通讯
应该把它当做标志位
来检查它确认什么呢
确认比特5 SPTEF
就是SPI的发送寄存器是否为空
以及比特7的SPRF
也就SPI的接收寄存器是否收到了数据
是不是跟UART很像啊
我们应该分别检测这两个比特
是否置1来编写我们的C语言函数
特别需要注意的是
在我们芯片手册里写了
每当一笔通讯完成的时候
我们第6个比特
也就SPTF会置1
来表示上一次通讯已经成功发送结束
所以每当我们进行一次
对于SPI的数据寄存器写操作
来发起一次新的通讯的时候
必须确保这一个比特的当前状态是0
而如果我们在做一次数据写操作之前
没有读一下这个比特
使它的标志位清零的话
我们写进去数据会被SPR模块忽略
无法产生一次新的通讯
大家一定注意这一点
也就是做任何对SPI数据寄存器的写操作
发起一次新的通讯之前
一定要读一下这个状态寄存器
使这个标志位清零
这样在这一次通讯成功发送完以后
它才能把这一位由零置1标志产生
否则你写的数据就会被忽略
这个通讯就不会被发起
也就是很多同学调试程序
看到的SPI并没有实际的完成通讯
不知道卡在哪儿了
可能在这儿
那么最高位接收寄存器
收到了数据也是一样的道理
每当接收寄存器置位
我们应该及时把数据寄存器的值读走
芯片手册告诉我们如果数据寄存器
收到一个值 这个位置位
我们没有读
这时又来一次新的通讯会怎么办呢
新收到的数据 注意
新收到的数据会被忽略
寄存器里仍然保留上一次收到的数据
这就是芯片手册告诉我们注意事项
所以大家编写函数的时候要加以注意
所以呢
我们讲了讲会发现SPI模块
名义上有六个寄存器
但实际上要把它用起来只需要用四个
控制 状态 数据和波特率
那么我有两个问题来了
第一个问题
如果扔掉曾老师这个拐杖
大家自己写一个SPI0Init函数 你会写吗
把刚才的时钟打开
IO配置给SPI用
SPI的寄存器完成那些初始化
设置好相位 通讯模式 波特率
写进去 对吧
第二个问题是个嵌套的问题
SPI的通讯接口
我们应该以什么样的函数形式来封装呢
有很多同学问我函数封装 对吧
除了初始化
这样一个模块应该怎么封装
像UART一样
封装成一个Put char和Get char两个函数吗
这个时候大家要把C语言
和我们功能关联起来
C语言的函数就是function
function是功能
所以函数一定是紧密描写功能的
SPI的通讯功能的特点是什么
它在时钟的驱动下
主机发送一个字节的同时
从机返回一个字节
所以我们对它的函数的一个正确的封装
应该是一个SPI write and read的读写
同时完成的函数
它带一个字节unsigned char类型的参数
作为要发送的字节
同时有一个unsigned char类型的返回值
返回接收到的值
大家想想是不是这么回事
那么问题来了
这个函数大家应该怎么写呢
应该按我们刚才反复讲的
特别详细的状态寄存器
查哪些标志位
查完了才能写数据寄存器
写完了我是不是还应该查什么
然后再读数据寄存器呢
请大家自己把它编程给写出来吧
在下一个单元
我教大家把这些函数用起来
去点我们的点阵式显示屏
-1.1 课程概览
--1.1 课程概览
-1.2 如何学好嵌入式系统
-2.1 计算机的基本概念、发展历史
-2.2 从晶体管到CPU
-2.3 概念CPU、微控制器MCU和嵌入式系统
-2.4 八卦计算机史
-2.5 不同领域、不同系列的嵌入式系统
-2.6 ARM历史与MKL25Z128 MCU
--2.6 ARM历史与MKL25Z128 MCU【习题】
-3.1 CPU的基本结构和运行机制
-3.2.1 堆栈的概念
--3.2.1 堆栈的概念【习题】
-3.2.2 堆栈的概念-头脑体操
-3.3.1 ARM的体系结构
--3.3.1 ARM的体系结构【习题】
-3.3.2 ARM的体系结构-头脑体操
-3.4.1 中断的概念和机制
-3.4.2 中断子程的概念和编程
-3.5 复位、时钟、存储器和总线
--3.5 复位、时钟、存储器和总线【习题】
-3.6 小结:MCU的总体结构和程序运行机制
--3.6 小结:MCU的总体结构和程序运行机制【习题】
-4.1 第一种外设:IO
-4.2 IO外设的编程实操-点亮LED
-4.3 IO外设的进阶知识
-4.4 嵌入式开发的基本概念与工具链
-4.5 嵌入式开发的进阶知识
-4.6 嵌入式开发中的C语言(上)
--4.6 嵌入式开发中的C语言(上)【习题】
-4.7 嵌入式开发中的C语言(下)
--4.7 嵌入式开发中的C语言(下)【习题】
-E0.1 实验零 开发板的初步认识与工具链的安装
-E0.2 实验零 体验一个例程的编译与下载
-E0.3 实验零 编写第一个程序:点亮核心板LED
-E1 实验一 点灯秘籍
-5.1 ARM微控制器外设学习概述
-5.2.1 ARM微控制器外设:通讯
-5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)
--5.2.2 ARM微控制器外设:异步串行通讯UART的原理(上)【习题】
-5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器的外设:异步串行通讯UART的原理(下)
--5.2.3 ARM微控制器外设:异步串行通讯UART的原理(下)【习题】
-5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
--5.2.4 ARM微控制器外设:RS-232串口与USB虚拟串口
-5.2.5 ARM微控制器外设:UART的寄存器编程(上)
--5.2.5 ARM微控制器外设:UART的寄存器编程(上)
-5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)
--5.2.6 ARM微控制器外设:UART的寄存器编程(下)【习题】
-E2 实验二 UART编程实操
-5.3.1 ARM微控制器外设:IO的中断编程(上)
-5.3.2 ARM微控制器外设:IO的中断编程(下)
-5.4.1 ARM微控制器外设:定时器的原理
-5.4.2 ARM微控制器外设:定时器的编程
--5.4.2 ARM微控制器外设:定时器的编程【习题】
-E3 实验三 定时器中断编程实操
-5.5.1 ARM微控制器外设:PWM的原理
-5.5.2 ARM微控制器外设:PWM寄存器与编程
-5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
--5.5.3 ARM微控制器外设:PWM编程实例—电子音乐
-E4 实验四 数码管显示编程实操
-5.6.1 ARM微控制器外设:SPI通讯简介
--5.6.1 ARM微控制器外设:SPI通讯简介【习题】
-5.6.2 ARM微控制器外设:SPI寄存器与编程
-5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
--5.6.3 ARM微控制器外设:SPI编程实例—OLED显示屏驱动
-5.7.1 ARM微控制器外设:I2C通讯简介
-5.7.2 ARM微控制器外设:I2C的通讯协议
-5.7.3 ARM微控制器外设:I2C寄存器与编程
--5.7.3 ARM微控制器外设:I2C寄存器与编程【习题】
-5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
--5.7.4 ARM微控制器外设:I2C编程实例—加速度传感器
-5.8.1 ARM微控制器外设:ADC简介
-5.8.2 ARM微控制器外设:ADC基础
-5.8.3 ARM微控制器外设:ADC寄存器与编程
-E5 实验五 ADC编程实操
-E6 挑战实验
--E6 挑战实验
-6.1 嵌入式系统的接口与设计
-6.2 嵌入式系统的实例