7819|16

1158

帖子

2

TA的资源

版主

楼主
 

【MSP430趣谈】MSP430第十讲之SPI总线驱动OLED [复制链接]

 
本帖最后由 michael_llh 于 2016-11-17 18:03 编辑

    上次我们说到了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这个目录的话就不用了直 接#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:这里的MISO全称是Master input Slave Output的意思,就是主机输入,从机输出的意思,MIOSI则相反。),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的时候进行讲解,从零开始,能够明白如果现在给你一个新的模块,一个新的芯片,你该从何下手,敬请期待哦!)


code10_1.rar

449.36 KB, 下载次数: 181

最新回复

楼主,我用169做主机发送数据,然后用5529接收数据,现在发现169在发送数据,但是在5529中怎么也进不了接收数据的中断,上拉电阻也加了,请问哪里还没设置对啊,附上代码:169主机的代码:#include /file:///C:\Users\ADMINI~1\AppData\Local\Temp\)QN1UH78VKP2T7)IA]ZM(FW.gifar TXData = 0; int main (void) {   WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT   P3SEL |= 0x0A;                            // Select I2C pins   P1OUT |= 0x00;   U0CTL |= I2C + SYNC;                      // Recommended init procedure   U0CTL &= ~I2CEN;                          // Recommended init procedure   I2CTCTL |= I2CSSEL1;                      // SMCLK   I2CNDAT = 0x01;                           // Write one byte   I2CSA = 0x0048;                           // Slave Address is 048h   I2CIE = TXRDYIE;                          // Enable RXRDYIFG interrupt   U0CTL |= I2CEN;                           // Enable I2C //  I2CDCTL |=0x00;   __enable_interrupt();                     // Enable interrupts   while (1)   {     U0CTL |= MST;                           // Master mode     I2CTCTL |= I2CSTT + I2CSTP + I2CTRX;    // Initiate transfer     __bis_SR_register(CPUOFF);              // Enter LPM0   } } // Common ISR for I2C Module #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USART0TX_VECTOR __interrupt void I2C_ISR(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USART0TX_VECTOR))) I2C_ISR (void) #else #error Compiler not supported! #endif {      I2CDRB = 0x5A;                         // TX data      while (I2CBUSY & I2CDCTL);             // I2C ready?      P1OUT |= 0x01;      P1DIR |= 0x01;      __bic_SR_register_on_exit(CPUOFF);     // Clear LPM0 } 5529从机的代码: #include volatile unsigned char RXData; int main(void) {   WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT   P3SEL |= 0x03;                            // Assign I2C pins to USCI_B0   P1OUT = 0;   UCB0CTL1 |= UCSWRST;                      // Enable SW reset   UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode   UCB0I2COA = 0x48;                         // Own Address is 048h   UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation   UCB0IE |= UCRXIE;                         // Enable RX interrupt   while (1)   {     __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0, enable interrupts(就停在这里了)     __no_operation();                       // Set breakpoint >>here  详情 回复 发表于 2017-4-1 11:41
 
点赞 关注(2)

回复
举报

5979

帖子

8

TA的资源

版主

沙发
 
这个oled不错
 
个人签名生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙
===================================
做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
 

回复

1158

帖子

2

TA的资源

版主

板凳
 

买的是龙丘的,但是上次看的时候涨到了36块,后来自己做了一批
 
 
 

回复

25

帖子

0

TA的资源

一粒金砂(中级)

4
 
看看...................
 
 
 

回复

2万

帖子

340

TA的资源

版主

5
 
第十讲又开始了,SPI总线驱动OLED
 
 
 

回复

1158

帖子

2

TA的资源

版主

6
 
qwqwqw2088 发表于 2016-2-20 19:41
第十讲又开始了,SPI总线驱动OLED

嘿嘿,谢谢支持!
 
 
 

回复

316

帖子

5

TA的资源

一粒金砂(高级)

7
 
关于OLED驱动这方面程序好多,好难啊!
 
 
 

回复

1158

帖子

2

TA的资源

版主

8
 
xueyongchao8805 发表于 2016-3-2 16:31
关于OLED驱动这方面程序好多,好难啊!

恩恩,确实很多,不过对应的很多都类似,想这个文章里面的oled,其实很通用的代码,好好理解一下
 
 
 

回复

316

帖子

5

TA的资源

一粒金砂(高级)

9
 
michael_llh 发表于 2016-3-2 19:18
恩恩,确实很多,不过对应的很多都类似,想这个文章里面的oled,其实很通用的代码,好好理解一下

软件模拟方面,我用示波器接引脚,是不是就可以看波形了!
 
 
 

回复

1158

帖子

2

TA的资源

版主

10
 
xueyongchao8805 发表于 2016-3-2 19:50
软件模拟方面,我用示波器接引脚,是不是就可以看波形了!

可以
 
 
 

回复

135

帖子

2

TA的资源

一粒金砂(高级)

11
 
MOSI引脚(PS:这里的MOSI全称是Master input Slave Output的意思,就是主机输入,从机输出的意思,MISO则相反。
这儿貌似写反了?
 
个人签名

矛盾的我

 
 

回复

1158

帖子

2

TA的资源

版主

12
 
fangkaixin 发表于 2016-11-17 17:14
MOSI引脚(PS:这里的MOSI全称是Master input Slave Output的意思,就是主机输入,从机输出的意思,MISO则 ...

sorry,马上改下,谢谢!
 
 
 

回复

41

帖子

0

TA的资源

一粒金砂(中级)

13
 
楼主大大,请问怎么验证MSP430F6638的例程中的SPI主从机通信啊?我把程序下载进去了,发现从机接收数据的程序没走啊,主机的指示灯也没亮啊,求解啊!!
 
 
 

回复

1158

帖子

2

TA的资源

版主

14
 
下雨天不洗澡 发表于 2017-3-1 17:04
楼主大大,请问怎么验证MSP430F6638的例程中的SPI主从机通信啊?我把程序下载进去了,发现从机接收数据的程 ...

时间有点长了,我只能大概和你说下思路。一个是对应从机的时序进行设置主机的时序,然后有条件的话用下逻辑分析仪来调试。另外时序的问题是比较麻烦的,简单的验证思想就是很多对应从机,如果是芯片的话都会有一个id的寄存器,如果那个寄存器读出来的值和芯片手册中说的id是一致的,那么一般情况下时序就没有问题了
 
 
 

回复

41

帖子

0

TA的资源

一粒金砂(中级)

15
 
michael_llh 发表于 2017-3-2 21:51
时间有点长了,我只能大概和你说下思路。一个是对应从机的时序进行设置主机的时序,然后有条件的话用下逻 ...

我单步调试主机,看到UCA0TXBUF__SPI        寄存器的值是在递增的额,(我设置的发送数据就是递增的),但是UCA0RXBUF__SPI始终没有读回数据,一直为零。我再调试从机,发现在__bis_SR_register(LPM0_bits+GIE)        这里就停了,没有进入下面的接收数据中断,所以没有把值传回主机,这是为什么啊?我需要再用逻辑分析看一下主机和从机的输入输出引脚的值吗?
       
 
 
 

回复

1158

帖子

2

TA的资源

版主

16
 
下雨天不洗澡 发表于 2017-3-3 11:34
我单步调试主机,看到UCA0TXBUF__SPI        寄存器的值是在递增的额,(我设置的发送数据就是递增的),但是UCA ...

你在测试功能的时候最好先不要加入低功耗的内容,确定功能没有问题再引入。时序是调试是需要注意的,单步调试的时候需要注意时序是否被调试打乱的问题
 
 
 

回复

41

帖子

0

TA的资源

一粒金砂(中级)

17
 
楼主,我用169做主机发送数据,然后用5529接收数据,现在发现169在发送数据,但是在5529中怎么也进不了接收数据的中断,上拉电阻也加了,请问哪里还没设置对啊,附上代码:169主机的代码:#include /file:///C:\Users\ADMINI~1\AppData\Local\Temp\)QN1UH78VKP2T7)IA]ZM(FW.gifar TXData = 0;
int main (void)
{

  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P3SEL |= 0x0A;                            // Select I2C pins
  P1OUT |= 0x00;
  U0CTL |= I2C + SYNC;                      // Recommended init procedure
  U0CTL &= ~I2CEN;                          // Recommended init procedure
  I2CTCTL |= I2CSSEL1;                      // SMCLK
  I2CNDAT = 0x01;                           // Write one byte
  I2CSA = 0x0048;                           // Slave Address is 048h
  I2CIE = TXRDYIE;                          // Enable RXRDYIFG interrupt
  U0CTL |= I2CEN;                           // Enable I2C
//  I2CDCTL |=0x00;
  __enable_interrupt();                     // Enable interrupts

  while (1)
  {
    U0CTL |= MST;                           // Master mode
    I2CTCTL |= I2CSTT + I2CSTP + I2CTRX;    // Initiate transfer
    __bis_SR_register(CPUOFF);              // Enter LPM0
  }

}

// Common ISR for I2C Module
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USART0TX_VECTOR
__interrupt void I2C_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USART0TX_VECTOR))) I2C_ISR (void)
#else
#error Compiler not supported!
#endif
{

     I2CDRB = 0x5A;                         // TX data
     while (I2CBUSY & I2CDCTL);             // I2C ready?
     P1OUT |= 0x01;
     P1DIR |= 0x01;
     __bic_SR_register_on_exit(CPUOFF);     // Clear LPM0

}

5529从机的代码:

#include

volatile unsigned char RXData;

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P3SEL |= 0x03;                            // Assign I2C pins to USCI_B0
  P1OUT = 0;
  UCB0CTL1 |= UCSWRST;                      // Enable SW reset
  UCB0CTL0 = UCMODE_3 + UCSYNC;             // I2C Slave, synchronous mode
  UCB0I2COA = 0x48;                         // Own Address is 048h
  UCB0CTL1 &= ~UCSWRST;                     // Clear SW reset, resume operation
  UCB0IE |= UCRXIE;                         // Enable RX interrupt

  while (1)
  {
    __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0, enable interrupts(就停在这里了)
    __no_operation();                       // Set breakpoint >>here<< and read
  }                                         // RXData
}

// USCI_B0 Data ISR
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_B0_VECTOR))) USCI_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{                                  // Vector 10: RXIFG
    RXData = UCB0RXBUF;                     // Get RX data
    P1DIR |= 0X01;
    P1OUT =0x01;
    __bic_SR_register_on_exit(LPM0_bits);   // Exit LPM0

}
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表