【MSP430趣谈】MSP430第十讲之SPI总线驱动OLED
上次我们说到了430的UART的使用方法,当我们一步一步开始探索的时候会发现它的实际应用是十分复杂的,但是经过我们的慢慢的看下来,我们看到TI给了很多官方的参数供给我们进行选择,所以这里给我们带来了很大的便利。
其实上一讲中我也犯了一个错误,就是和之前一样,遇到一个难的东西,经常就是拿起来,玩两下,不行,没意思,就放弃了,前前后后花了十天的时间才完成了UART的编写,有些东西写的也不是非常的完整。其实我相信大家可能都会遇到这张情况,正所谓人之本性嘛!还是比较喜欢偷懒的。除非你真的是做到了废寝忘食想要把这个东西弄明白的情况,那你可能会一直不肯放弃,当然这个也是值得赞扬的。
但是我没有这个毅力哈。。。惭愧惭愧,但是既然已经走了十讲的路途还是不会放弃的,会一直将这个系列完成,把我认为该讲的东西都讲清楚,也会加上一些具体的应用,就比如这一讲我们将说到另外一种异步通信的方式,就是SPI,一种非常常用的通信方式,下次我们就会说到IIC,这次我们将运用SPI来驱动一个OLED,尝试将我们所学习的东西应用到实际上面,好了我们正式进入本次的SPI学习了。
从数据手册中我们看到了,TI对于SPI分为了A和B模块,而对于UART来说,划分的是A0和A1这样子,总的来说都差不多,应用方式上面还是一样的,可能对于引脚上面有所区别。这里我们说明一下该如何查看芯片的引脚。
在我们的第一讲中我们给出了430的相关资料,其中有两份材料,一份是User’s Guide,另外一份是430的Datasheet。
之前我们一直用的是两份材料,不知道大家还记得吗,一份是User’s Guide,另外一份是我们的库函数使用的参考手册,也就是Driverlib User’s Guide.
当我们需要查看某个引脚的功能我们需要查看的是一个芯片的数据手册,这个大家要记住,因为之后当你运用其他芯片的时候也是一样的,要查看某个引脚功能,就要看该芯片的数据手册。
我们打开,找到目录中的:
这个章节讲的就是我们的引脚功能定义,找到我们板子上面印出来的SPI接口。
这三个引脚就是我们使用的官方FR5969的Launchpad上面引出的SPI引脚,大家可能现在还不是很明白这三个引脚,等下我们会一一道来。
这里我们看下怎么看这个图:
好了,这里封装对应的标号是什么意思呢?
我们看到封装有给了具体的字母来代表,这里有三种封装形式,RGZ,RHZ,DA三种形式,
封装如果不懂啥意思的话百度哈,简单来说就是芯片外观长什么样就是了。
我们这里选取我们板子上的芯片封装进行说明,为RGZ的形式,我们找到这三个引脚看到他们在芯片上面有26,31,32的标号,这里就是我们上面所说的引脚标号了。
相信到这里大家已经明白说具体该如何去查看一个芯片的引脚以及他的相关功能了,接下来我们正式介绍一下SPI。
SPI同样是一种串行通信,也是通过一位一位传输的,这个我们之前解释过了,这里不再累赘了。它的工作模式有主从两种方式,可以有一个主设备和一个或多个从设备,至少需要4根线,当然也可以是三根线,三根线只限于单向传输。
和前面的串口一样我们同样还是需要介绍一下他所需要的引脚:
这里需要四根引脚,我们看下是哪些?
(1)SDO – 主设备数据输出,从设备数据输出;
(2)SDI – 主设备数据输入,从设备数据输入;
(3)SCLK – 时钟信号,由主设备产生;
(4)CS – 从设备使能信号,由主设备控制。
但是我们看到我们我们上面的截图中,我们只选取了三根线,我们少了一根片选线,因为我们只用了一个设备,当我们使用多个设备的时候我们可以通过片选信号进行设备的选择,具体的方式如下图:
画的有点丑,希望大家可以明白哈。
具体的传输协议我们不具体说明,大家有兴趣可以去查阅,我们这里关心的是他的应用方式哈。
我们说明一下代码管理我们一般情况下把代码分成各个模块进行编写,这样便于维护和关系。这里我们看下具体的做法。右键工程我们新建一个文件夹。
键入文件夹名称:
接下来我们在我们新建的src文件夹右键在新建一个文件夹oled,因为我们这次要用spi来驱动一个spi模式的oled模块。完成如下:
接下去我们把OLED的源代码拷贝到工程目录中的oled文件夹下面。这个代码我只会统一打包上传的。我们拷贝的是在32上面实现的代码,要改成我们需要的代码。
一些步骤我这里说下哈,首先还是要在我们的工程上面添加我们的文件夹路径,和我们之前导入库的方法是一样的,这里说明一下,我们选择路径的时候到哪一级是有区别的,比如说我们这里是选择到src这个路径,但是我们这个文件夹下面还有一个文件夹是oled,然后才是oled的文件,那么当我们的main函数中要使用这个文件的时候我们需要要写成这样才可以:#include “oled/oled.h”或者是#include <oled/oled.h>,才可以,当然你要是选择到oled这个目录的话就不用了直 接#include “oled.h”就可以了。这样子大家可以明白吧。不懂留言吧。
解释一下,我们在include中使用<>和使用“”的区别:
使用尖括号表示在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找.使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中去查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。
我们看到这里给的是32的代码所以包含的是一些32的头文件和32的引脚定义,具体怎么移植的话可能没有办法这样子一张照片一张照片的截图跟大家说下去,我们就讲一下具体的方法好了,具体大家有什么不懂的话留言问下好了。
一般情况下,移植的话重点就是引脚的修改,相关的引脚配置和功能配置,这个方面弄好了也基本没有什么问题了。我们这里就直接修改了,有些重要的地方我们特别截图说明的。
在书写硬件SPI的时候我们遇到了一个问题,
GPIO_setAsPeripheralModuleFunctionOutputPin
上面这个库函数的最后一个参量,也就是所谓的引脚模式的选择,库函数中给出了三个选择:
当时看到就懵逼了,这什么鬼,怎么选,我觉得TI这一点就做的特别不厚道,能不能简单一点,下面我们看下如何找到这个具体该怎么选?
我们在 MSP430FR59xx 混合信号微控制器.pdf 这个文件中我们找到如下:
我们放大图形看可以看到,从图中我们可以得到我们P1SEL1.x P1SEL0.x这两个寄存器位的设置值。
此时我们找到另外一份文档 MSP430FR58xx, MSP430FR59xx, MSP430FR68xx, and MSP430FR69xx Family User's Guid.pdf 中的表格:P340页
所以这里对P1.6和P1.7来说就是Secondary module function。
相信大家可以明白这个方法吧,同样的我们可以找到,P2.2对应的也是第二功能。这里就不在解释了哈,方法和上面一样的哈。
这里我们使用了两种方式来编写OLED的驱动代码,这里我们来解释一下我们的软件模拟是怎么实现的。其实这里面的原理就是我们控制每个引脚对应输出高低电平来模拟出对应的SPI时序,来实现两者的通信功能。
这里我们举一个例子,就是我们的OLED_WR_BYTE这个函数,我们看到这个函数的具体功能我们已经注释的很清楚了,相信大家一目了然了。这里我们解释一下函数实现的方法。
对于OLED的驱动的话,我们看到有一个cmd的选择,并且这里连接的是DC脚,对应我们的SPI中的MOSI引脚(PS:这里的MOSI全称是Master input Slave Output的意思,就是主机输入,从机输出的意思,MISO则相反。),OLED就是根据这个引脚的电平来判断你从D1口,也就是MISO口得到的数据是命令还是说要显示的数据,相信大家可以明白了哈。
那么这里使用了条件编译的方式,这个方法我们之前也有说过了哈。可以提高整个代码的通用性,我们可以直接通过修改以下宏定义既可以选择为硬件驱动还是软件模拟驱动了。
下面我们进入比较重要的部分。我们看到一个for循环。这里的D0口连接的是我们的SCLK口,模拟的时钟的高低电平,我们一步一步分析下来,一开始是把SCLK置低,然后我们通过dat&0x80得到最低位是否为1,这里具体为什么这么写的话我们看下哈。
比如说,我现在的数据为0x57,对应的二进制为0101_0111
那么现在我和0x80按位与(ps:这里用的是单个与的符号,&表示按位与,&&表示逻辑与,是一个逻辑表达式的相与,例如(a==0)&&(b==0))
0101_0111 & 1000_0000 = 0000_0000
这样子我们就得到了最高位的数值为0。在这里大家就可以比较好的明白什么是高位传输和地位传输了,就是我们之前所说的MSB和LSB的,这样子大家会比较形象的理解了吧。
接下来的一步是再次把SCLK电平拉高,这时就将我们的最高位0传输过去了。
在下面是dat<<=1,这里的意思是dat左移一位,另外一位补零。效果如下
0101_0111 >>>>>> 1010_1110
这样到下一个循环,我们就同样还是传输最高位,此时我们和0x08相与,这样的得到:
1010_1110 & 1000_0000 = 1000_0000
这里就是按照高位传输一位一位的传输过去。这样子大家应该可以明白大概软件模拟的一个思想,至于为什么要在数据得到之后,我们再次拉高SCLK就是根据我们的通信协议来的。通过这样我们就实现了软件模拟。后面的很多也都通过这种类似的方式。
对于硬件来说,这里我们就是直接调用了库函数来实现发送一个数据或者命令这样。
由于这次写的时间花了比较久,涉及到相对多一点的内容,所以这里就暂时告一段落,相信我们已经把软件部分讲的比较清楚了,接下去我会进行一个补充说明硬件部分的实现方法。
这次上传的代码中硬件部分还有问题,还没有测试完成,将在后续完成更新。
我们看下使用软件仿真实现的结果:
这里我的OLED有点坏了,有一些点不能显示出来,大家可以试下自己的看行不行。(PS:我买的OLED是3.3V的,然后我们FR5969上面引出来的电源是5V的,哭瞎,只好自己搞一个稳压,见下图)。
关于OLED显示的函数使用方法,希望大家可以自己查看代码!基本都有注释清楚的。
本次使用的OLED引脚定义如下:
(这次其实在使用OLED模块的时候我们注重讲了时序的搭建我们的通信协议到他的使用方法,但是我们没有讲到我们一个OLED模块如何来利用SPI总线进行驱动,而仅仅只说到了他的数据传输问题,这个问题我们将放在下一次IIC驱动ADXL345的时候进行讲解,从零开始,能够明白如果现在给你一个新的模块,一个新的芯片,你该从何下手,敬请期待哦!)