打印

【学习探索】Hello:LaunchPad(从)--SPI--EK LM3S811(主)

[复制链接]
5204|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 yirongfu 于 2012-7-5 09:41 编辑

    此前一直使用模拟的SPI接口或者主机模式的SPI接口,未尝试过从机SPI编程,近期学习了视频教程,想试试来个跨界握手:通过LaunchPad上的MSP430G2553的硬件SPIEK-LM3S811上的SSISPI模式)进行互联,简化起见,MSP430G2553作为从机,LM3S811作为主机。利用LM3S811的虚拟串口来间接检验双向通讯效果,通讯的思路是LM3S811发出若干字节数据,MSP430G2553接收到后原文回传,LM3S811接收到回传直接通过UART显示到PC端的串口调试助手上。当然也可以逆过来设计,我这么选择只是为了实验的方便,因为需要尝试的重点是SPI通讯,其他的如虚拟串口可以利用LM3S的库来快速实现。关键的难点还是接口时序的选择,比如时钟的极性、相位等。
*****************************************

硬件连接(从评估板的引出端子处飞线互连):
PC1+MSP430G2553                                 PC2+ LM3S811
GNDPin20------------------------------GND(外侧引出焊盘列,下同)
UCA0SOMIP1.1------------------------SSIRx
UCA0SIMOP1.2------------------------SSITx
UCA0CLK P1.4-------------------------SSICLK
UCA0STE P1.5-------------------------SSIFss(可省)
*****************************************

注:目前的MSP430G2553器件手册SLAS735F3页第一个器件引脚图上,第4脚描述有误,多了个字母P,变成PUCA0SIMO了。




   
    接下来的重点就是编程了,首先需要查阅官方的例程,当然没有现成的上述连接方式的例程,不过
MSP430G2553倒是有两个分别作为从机和主机的例程,主从双方均为MSP430单片机,LM3S811也有SPI主机模式的例程,但没找着对应的从机例程。参考例程作如下设计:


1. MSP430G2553采用三线SPI从机模式,上电复位进行必要的初始化后进入低功耗LPM4状态,接收数据和发送数据均在中断服务程序内执行。这也是近期的视频教程第八讲“串行通信模块”里,丁经理反复强调的430适合低功耗设计的要求,为低功耗设计而设计,它可从任意低功耗模式(LPM)实现自动时钟起动,具体来说就是即使在CPU和所有时钟禁能的LPM4模式下都可以正常通信,不过请注意,这针对的是SPI的时钟取自SMCLK且作为从机的操作,在需要的时候SMCLK被自动激活,比如接收和发送操作就可以激活,并保持到操作执行完毕,然后回到激活前的低功耗状态(详见用户手册说明)。而如果作为主机,它需要向外输送时钟的,所以就跟低功耗模式相矛盾了。为了便于调试观察,设置了一个数组用于存放从SPI收到的数据,接收到一个字节立即回传该字节,芯片大部分时间处于LPM4,另外,联调时先运行该从机。调试环境CCSv4.2.4


2. LM3S811采用FreescaleSPI主机模式,SPH=1SPO=0,速率取500kHz8-bit数据宽度。注意,LM3S811数据手册里提到,当工作模式为主机模式时,系统时钟至少是SSIClk的两倍;当工作模式为从机模式时,系统时钟至少是SSIClk12。程序采用查询方式发送和接收数据,发送5个字符Hello,全部发送完毕再进行接收查询,接收的数据直接送UART口,UART口采用例程中的配置115200 bps8-N-1。调试环境IAREmbedded Workbench for ARM 5.30 Kickstart


3. 两个MCU的用户手册所述及的SPI接口模式不太一样,MSP430G2553并不像LM3S811那样区分出三种定义的SPI接口,从具体的配置来看,MSP430G2553LM3S811Freescale接口模式更为接近,就是不知道LM3S811支持的TI的模式是不是430上的SPI模式?最终基本可行的接口时序形式如下:
MSP430G2553CKPL=0CKPH=0


LM3S811FreescaleSPO= 0SPH=1

其实我一开始分析时,觉得SPO=0SPH=0更配对,可惜不对。


实验内容及结果总结:


1. MSP430G2553作为从机,SPI的总线时钟CLK取决于主机,当然,也不能让MCU的系统时钟低于SPI时钟线频率。实验过上电默认的约1MHz DCO,修改为8MHz,修改为4.37.3MHzRSELx = 12, DCOx = 3, MODx = 0),均可。稳妥起见,个人觉得系统时钟和总线频率的选择参考上述LM3S811数据手册的建议为宜。


2. 试了MSP430G2553配置为3线制,LM3S811配置为TI SPI模式,MSP430G2553接收到的数据错误。


3. LM3S811Freescale SPI是4线制,试验了如下情况:
· 分别试了用与不用SSISTE引脚(软件配置LM3S811I/O口部分的PA3屏蔽),MSP430G2553也分别配置了3线制和4线制,效果一样,这个STE脚应该就是多主机的时候才有用(此时注意如果使用4线JTAG进行调试,MSP430G2xx3UCA0STETMS引脚是复用的,开发环境可以使用Release JTAG on Go或者Free Run来避免冲突)。
· 试了LM3S811SPO=0SPH=0模式,MSP430G2553接收的数据正确,但LM3S811收到的其回传数据与原发出数据不一样,不过基本都是固定的几个值。
· 试了LM3S811SPO=0SPH=1模式,MSP430G2553接收数据正确,但回送给LM3S811的数据不完全一致,这个不一致是说收到的字符是对的,但先后顺序不吻合,比如实验中LM3S811发送的是“Hello”,MSP430G2553接收到的也是“Hello”,并且收一个字符回传一个,LM3S811也是读出一个就往串口送一个,结果PC端的串口调试器上得到的是“oHell”,就是说顺序错了,还试过只送三个字节“mhz”,同样存在这种现象,变成“hzm”,DCO改为8MHz后,又变成“zmh”。目前只调试到这种程度,虽然不完全吻合,排序有错,但至少收到正确的数据了,下面是串口终端的截图。


· 至于为何数据错位,LM3S811FIFO接收机制既然先进先出,照理不应该错,而且从机MSP430G2553也是按正确的先后顺序回传的,我想只可能在主机端接收和读取时才会产生错误,感觉也不应该是查询方式存取FIFO的问题,否则先不说错位,就连数值的正确性都无法保证。至于问题到底出在哪里,还没找出来,有待继续研究调试,如果有朋友知道,望指点!
· 现在总算深刻体会到了SPI总线的弱点:没有指定的流控制,没有完善的应答机制确认是否接收到数据。曾经将MSP430G2553的回传屏蔽,结果LM3S811的接收还是照常执行,收到的数据当然都是空白的,也就是说,SPI并没有像I2C那样有停止位、起始位、确认位之类的,它仅是依靠时钟线来作为存取数据的基准,如果主从机的时序没有严格对应上,就很可能出错。也曾怀疑我上述失败实验中有些就是数据回传与LM3S811开始程序读取FIFO之间的时间间隔问题,可惜没有示波器,暂时无法去验证分析。对于这种双MCU的由程序控制的SPI主从通讯,特别是两种不同架构的MCU之间的通讯,关键之关键,应该还是时序的同步。


    这回调试费了好几天的业余时间,因为没有好的实验条件,比调试I2C和其它仅作Slave用途的SPI器件要费劲的多,而且熟悉CCS调试环境也花了点时间,好在多少有些成果,亲自验证了MSP430LM3S之间的SPI互联,虽然还有数据错位的问题,但至少数值对了,如果大家有碰到类似的实际应用,可以再进一步做调试。


后面附上两MCU的主代码供大家参考。
另外,因为本文既涉及MSP430又涉及Stellaris,所以放到《讨论》这个版块。

dserror.JPG (38.99 KB )

dserror.JPG

相关帖子

沙发
yirongfu|  楼主 | 2012-7-5 09:55 | 只看该作者
本帖最后由 yirongfu 于 2012-7-5 09:59 编辑

发帖系统真的有些问题,烦人,上帖最后一张图重复,部分字体颜色也编辑不成功,期待新版!

下面是MSP430G2553的源代码,环境CCSv4.2.4
参考例程:slac485a ——MSP430G2xx3Code Examples.zip(例程有些描述也不一定都正确)


#include "msp430g2553.h"



unsigned char temp[5];

static unsigned char num;



void main(void)

{

  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer

  

  /*if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)                                    


{  

   
while(1);                              
// If calibration constants erased
// do not load, trap CPU!!

  }


//8Mhz


  BCSCTL1 = CALBC1_8MHZ;//0x0C;                    // Set range


  DCOCTL = CALDCO_8MHZ;//0x60;                     // Set DCO step +
modulation */

  

  P1SEL = BIT1 + BIT2 + BIT4;

  P1SEL2 = BIT1 + BIT2 + BIT4;

  UCA0CTL1 = UCSWRST;                       // **Put state machine in reset**




  UCA0CTL0 |= UCMSB + UCSYNC + UCMODE_2;               // 3-pin, 8-bit SPI slave

//官方例程此处注释有误,变成了master
  UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**

  IE2 |= UCA0RXIE;                          // Enable USCI0 RX interrupt

  temp[0] = '-';

  temp[1] = '-';

  temp[2] = '-';

  temp[3] = '-';

  temp[4] = '-';

  num = 0;



  __bis_SR_register(LPM4_bits + GIE);       // Enter LPM4, enable interrupts

}



// Echo character

#pragma vector=USCIAB0RX_VECTOR

__interrupt void USCI0RX_ISR (void)

{

  temp[num] = UCA0RXBUF;

  while (!(IFG2 & UCA0TXIFG));

  UCA0TXBUF = temp[num];

  num++;
  

  if(num == 5)
     num = 0;

}

使用特权

评论回复
板凳
yirongfu|  楼主 | 2012-7-5 10:02 | 只看该作者
这是LM3S811的主程序源代码,环境IAR Embedded Workbench forARM 5.30 Kickstart,调试之用,所以只设计了单次发送和接收:
参考例程:StellarisWare_for_EK-LM3S811


//*****************************************************************************



#include "inc/hw_memmap.h"

#include "inc/hw_ssi.h"

#include "inc/hw_types.h"

#include "driverlib/ssi.h"

#include "driverlib/gpio.h"

#include "driverlib/sysctl.h"

#include "driverlib/uart.h"

#include "utils/uartstdio.h"



//*****************************************************************************

//

//! \addtogroup ssi_examples_list

//! <h1>SPI Master
(spi_master)</h1>

//!

//! This example shows how to configure the
SSI0 as SPI Master.  The code will

//! send three characters on the master Tx
then polls the receive FIFO until

//! 3 characters are received on the master
Rx.

//!

//! This example uses the following
peripherals and I/O signals.  You must

//! review these and change as needed for
your own board:

//! - SSI0 peripheral

//! - GPIO Port
A peripheral (for SSI0 pins)

//! - SSI0CLK - PA2

//! - SSI0Fss - PA3

//! - SSI0Rx  - PA4

//! - SSI0Tx  - PA5

//!

//! The following UART signals are
configured only for displaying console

//! messages for this example.  These are not required for operation of SSI0.

//! - UART0 peripheral

//! - GPIO Port
A peripheral (for UART0 pins)

//! - UART0RX - PA0

//! - UART0TX - PA1

//!

//! This example uses the following
interrupt handlers.  To use this example

//! in your own application you must add
these interrupt handlers to your

//! vector table.

//! - None.

//!

//

//*****************************************************************************



//*****************************************************************************

//

// Number of bytes to send and receive.

//

//*****************************************************************************

#define NUM_SSI_DATA 5



//*****************************************************************************

//

// This function sets up UART0 to be used
for a console to display information

// as the example is running.

//

//*****************************************************************************

void

InitConsole(void)

{

   
//

   
// Enable GPIO port A which is used for UART0 pins.

   
// TODO: change this to whichever GPIO port you are using.

   
//

   
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);



   
//

   
// Configure the pin muxing for UART0 functions on port A0 and A1.

   
// This step is not necessary if your part does not support pin muxing.

   
// TODO: change this to select the port/pin you are using.

   
//

   
GPIOPinConfigure(GPIO_PA0_U0RX);

   
GPIOPinConfigure(GPIO_PA1_U0TX);



   
//

   
// Select the alternate (UART) function for these pins.

   
// TODO: change this to select the port/pin you are using.

   
//

   
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);



   
//

   
// Initialize the UART for console I/O.

   
//

    UARTStdioInit(0);

}



//*****************************************************************************

//

// Configure SSI0
in master Freescale (SPI) mode.
This example will send out

// 3 bytes of data, then wait for 3 bytes
of data to come in.  This will all be

// done using the polling method.

//

//*****************************************************************************

int

main(void)

{

   
unsigned long ulDataTx[NUM_SSI_DATA];

   
unsigned long ulDataRx[NUM_SSI_DATA];

   
unsigned long ulindex;



    //

   
// Set the clocking to run directly from the external
crystal/oscillator.

   
// TODO: The SYSCTL_XTAL_ value must be changed to match the value of
the

   
// crystal on your board.

   
//

   
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |

                   SYSCTL_XTAL_6MHZ);



   
//

   
// Set up the serial console to use for displaying messages.  This is

   
// just for this example program and is not needed for SSI operation.

   
//

   
InitConsole();



   
//

   
// Display the setup on the console.

   
//

   
UARTprintf("SSI ->\n");

   
UARTprintf("  Mode:
SPI\n");

   
UARTprintf("  Data:
8-bit\n\n");

   

   
/*SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

   
GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3);

   
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3,0);

   
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3,GPIO_PIN_3);

   
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3,~0);

   
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3,GPIO_PIN_3);

   
SysCtlPeripheralDisable(SYSCTL_PERIPH_GPIOA);*/

   
//

   
// The SSI0 peripheral must be enabled for use.

   
//

   
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);



   
//

   
// For this example SSI0 is used with PortA[5:2].  The actual port and pins

   
// used may be different on your part, consult the data sheet for more

   
// information.  GPIO port A needs
to be enabled so these pins can be used.

   
// TODO: change this to whichever GPIO port you are using.

   
//

   
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);



   
//

   
// Configure the pin muxing for SSI0 functions on port A2, A3, A4, and
A5.

   
// This step is not necessary if your part does not support pin muxing.

   
// TODO: change this to select the port/pin you are using.

   
//

   
GPIOPinConfigure(GPIO_PA2_SSI0CLK);

   
GPIOPinConfigure(GPIO_PA3_SSI0FSS);

   
GPIOPinConfigure(GPIO_PA4_SSI0RX);

   
GPIOPinConfigure(GPIO_PA5_SSI0TX);



   
//

   
// Configure the GPIO settings for the SSI pins.  This function also gives

   
// control of these pins to the SSI hardware.  Consult the data sheet to

   
// see which functions are allocated per pin.

   
// The pins are assigned as follows:

   
//      PA5 - SSI0Tx

   
//      PA4 - SSI0Rx

   
//      PA3 - SSI0Fss

   
//      PA2 - SSI0CLK

   
// TODO: change this to select the port/pin you are using.

   
//

   
GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |

                   GPIO_PIN_2);

   
//GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_2);



   
//

   
// Configure and enable the SSI port for SPI master mode.  Use SSI0,

   
// system clock supply, idle clock level low and active low clock in

   
// freescale SPI mode, master mode, 1MHz SSI frequency, and 8-bit data.

   
// For SPI mode, you can set the polarity of the SSI clock when the SSI

   
// unit is idle.  You can also
configure what clock edge you want to

   
// capture data on.  Please
reference the datasheet for more information on

   
// the different SPI modes.

   
//

   
//SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_TI,

   
//                  
SSI_MODE_MASTER, 500000, 8);

   
SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_1,

                       SSI_MODE_MASTER, 500000,
8);



   
//

   
// Enable the SSI0 module.

   
//

   
SSIEnable(SSI0_BASE);



   
//

   
// Read any residual data from the SSI port.  This makes sure the receive

   
// FIFOs are empty, so we don't read any unwanted junk.  This is done here

   
// because the SPI SSI mode is full-duplex, which allows you to send and

   
// receive at the same time.  The
SSIDataGetNonBlocking function returns

   
// "true" when data was returned, and "false" when
no data was returned.

   
// The "non-blocking" function checks if there is any data in
the receive

   
// FIFO and does not "hang" if there isn't.

   
//

   
while(SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx[0]))

    {

    }



   
//

   
// Initialize the data to send.

   
//

   
ulDataTx[0] = 'H';//s

   
ulDataTx[1] = 'e';//p

   
ulDataTx[2] = 'l';//i

   
ulDataTx[3] = 'l';//i

   
ulDataTx[4] = 'o';//i



   
//

   
// Display indication that the SSI is transmitting data.

   
//

   
UARTprintf("Sent:\n  ");



   
//

   
// Send 3 bytes of data.

   
//

   

   
//SSIDataPut(SSI0_BASE, 'A');

   

   
for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++)

    {

      
//

      
// Display the data that SSI is transferring.

      
//

      
UARTprintf("'%c' ", ulDataTx[ulindex]);



      
//

      
// Send the data using the "blocking" put function.  This function

      
// will wait until there is room in the send FIFO before returning.

      
// This allows you to assure that all the data you send makes it into

      
// the send FIFO.

      
//

      
SSIDataPut(SSI0_BASE, ulDataTx[ulindex]);

    }



   
//

   
// Wait until SSI0 is done transferring all the data in the transmit
FIFO.

   
//

   
while(!SSIBusy(SSI0_BASE))

    {

    }



   
//

   
// Display indication that the SSI is receiving data.

   
//

   
UARTprintf("\nReceived:\n
");



   
//

   
// Receive 3 bytes of data.

   
//

   
for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++)

    {

      
//

      
// Receive the data using the "blocking" Get function. This
function

      
// will wait until there is data in the receive FIFO before returning.

      
//

      
SSIDataGet(SSI0_BASE, &ulDataRx[ulindex]);



      
//

      
// Since we are using 8-bit data, mask off the MSB.

      
//

      
ulDataRx[ulindex] &= 0x00FF;



      
//

      
// Display the data that SSI0 received.

      
//

      
UARTprintf("'%c' ", ulDataRx[ulindex]);

    }

   
UARTprintf("\n");



   
while(1);

   
//

   
// Return no errors

   
//

   
//return(0);

}

使用特权

评论回复
地板
nwx8899| | 2012-7-5 12:00 | 只看该作者
mark 顶

使用特权

评论回复
5
yirongfu|  楼主 | 2012-7-6 23:50 | 只看该作者
谢谢捧场:)

没人解答我的问题吗?:L

使用特权

评论回复
6
mdq123| | 2012-7-26 19:59 | 只看该作者
xiexielouzhu

使用特权

评论回复
7
mdq123| | 2012-7-26 20:00 | 只看该作者
xiexielouzhu

使用特权

评论回复
8
wugang1213| | 2012-8-11 12:07 | 只看该作者
好东西 顶一顶

使用特权

评论回复
9
wangzhisunshine| | 2014-8-9 20:43 | 只看该作者
现在才看到这好东西,大赞!

使用特权

评论回复
10
wangzhisunshine| | 2014-8-9 20:45 | 只看该作者
还希望楼主持续更新。

使用特权

评论回复
11
u880| | 2014-8-11 08:16 | 只看该作者
好给力的分享啊

使用特权

评论回复
12
huigoushang| | 2014-8-11 08:18 | 只看该作者
受益匪浅 多谢分享

使用特权

评论回复
13
G21372| | 2014-8-11 08:21 | 只看该作者
楼主的分享真心不错

使用特权

评论回复
14
yangguangaisha| | 2014-8-11 08:24 | 只看该作者
楼主强悍 多谢分享

使用特权

评论回复
15
gexingyouxian| | 2014-8-11 08:26 | 只看该作者
简直不能更详细了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:生活将我们磨圆,是为了让我们滚得更远。。。 我来到这个世上就没打算活着回去!

99

主题

906

帖子

2

粉丝