TI论坛
直播中

刘欢

7年用户 196经验值
私信 关注
[问答]

请问有两块LM3S9B96用SPI通信的例程吗?

我刚接触LM3S9B96,想学习SPI通信,之前没有做过,就先用LM3S9B96自发自收,经过几天努力,成功的读取到发送的数据,可是用两块板子通信,一点头绪也没有,请大家帮帮我,或者给我点思路。先谢谢了

回帖(5)

周润艺

2019-8-28 09:54:10
SPI:高速同步串行口。3~4线接口,收发独立、可同步进行.
SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是
Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH
,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI
,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了?
酒墓芙牛蔽狿CB
的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成?
苏庵滞ㄐ判椋热鏏T91RM9200.
SPI总线系统是一种同步串行外设接口,它可以使MCU
与各种外围设备以串行方式进行通信以交换信息。外围设置FLASHRAM、网络控制器、LCD
显示驱动器、A/D转换器和MCU等。SPI
总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4
条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线
MOST和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT或INT、有的SPI
接口芯片没有主机输出/从机输入数据线MOSI)。
SPI
的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备?
枰辽?根线,事实上3根也可以(单向传输时)。也是所有基于SPI
的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。
(1)SDO     – 主设备数据输出,从设备数据输入
(2)SDI    – 主设备数据输入,从设备数据输出
(3)SCLK – 时钟信号,由主设备产生
(4)CS        – 从设备使能信号,由主设备控制
其中CS
是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低?
缥唬源诵酒牟僮鞑庞行***U饩驮市碓谕蛔芟呱狭佣喔鯯PI设备成为可能。
接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI
是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由
SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO
线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数?
荽洌淙胍彩褂猛怼U庋谥辽?
次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。
要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI
的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通?
拇型ㄑ恫煌胀ǖ拇型ㄑ兑淮瘟椭辽?位数据,而SPI
允许数据一位一位的传送,甚至允许暂停,因为SCK
时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设?
竿ü許CK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI
的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI
设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采?
胁煌ㄒ澹咛迩氩慰枷喙仄骷奈牡怠?
在点对点的通信中,SPI
接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每?
龃由璞感枰懒⒌氖鼓苄藕牛布媳菼2C系统要稍微复杂一些。
最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
AT91RM9200的SPI接口主要由4个引脚构成:SPICLK、MOSI、MISO及 /SS,其中SPICLK
是整个SPI总线的公用时钟,MOSI、MISO作为主机,从机的输入输出的标志,MOSI
是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。/SS
是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS
管脚的电平低的是从机,相反/SS管脚的电平高的是主机。在一个SPI
通信系统中,必须有主机。SPI总线可以配置成单主单从,单主多从,互为主从。
SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,说NPCS0~3接4-16译码器,
这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。
SPI协议举例
SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo
构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
  假设下面的8位寄存器装的是待发送的数据10101010
,上升沿发送、下降沿接收、高位先发送。
  那么第一个上升沿来的时候 数据将会是sdo=1;寄存器=0101010x
。下降沿到来的时候,sdi上的电平将所存到寄存器中去,那么这时寄存器=0101010sdi
,这样在 8个时钟脉冲以后,两个寄存器的内容互相交换一次。这样就完成里一个spi
时序。
举例:
  假设主机和从机初始化就绪:并且主机的***uff=0xaa,从机的***uff=0x55
,下面将分步对spi的8个时钟周期的数据情况演示一遍:假设上升沿发送数据
这样就完成了两个寄存器8位的交换,上面的上表示上升沿、下表示下降沿,sdi、sdo
相对于主机而言的。其中ss
引脚作为主机的时候,从机可以把它拉底被动选为从机,作为从机的是时候,可以作为片?
〗庞谩8菀陨戏治觯桓鐾暾拇椭芷谑?6
位,即两个字节,因为,首先主机要发送命令过去,然后从机根据主机的命令准备数据,?
骰谙乱桓?位时钟周期才把数据读回来。    SPI 总线是Motorola
公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线
MOSI,一条数据输出线MISO;用于CPU与各种外围器件进行全双工、同步串行通讯。SPI
主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;
提供频率可编程时钟;发送结束 中断标志;写冲突保护;总线竞争保护等。下图示出SPI
总线工作的四种方式,其中使用的最为广泛的是SPI0和SPI3方式 (实线表示)
:                  
              SPI总线四种工作方式 SPI
模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以?
信渲茫敝蛹裕–POL)对传输协议没有重大的影响。如果 CPOL=0
,串行同步时钟的空闲状态为低电平;如果CPOL=1
,串行同步时钟的空闲状态为高电平。时钟相位(CPHA
)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0
,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1
,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI
主模块和与之通信的外设备时钟相位和极性应该一致。
SPI总线包括1根串行同步时钟信号线以及2根数据线。
  SPI
模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以?
信渲茫敝蛹裕–POL)对传输协议没有重大的影响。如果CPOL=0
,串行同步时钟的空闲状态为低电平;如果CPOL=1
,串行同步时钟的空闲状态为高电平。时钟相位(CPHA
)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0
,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1
,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI
主模块和与之通信的外设音时钟相位和极性应该一致。SPI接口时序如图3、图4所示。
补充:
上文中最后一句话:SPI
主模块和与之通信的外设备时钟相位和极性应该一致。个人理解这句话有2
层意思:其一,主设备SPI
时钟和极性的配置应该由外设来决定;其二,二者的配置应该保持一致,即主设备的SDO
同从设备的SDO配置一致,主设备的SDI同从设备的SDI配置一致。因为主从设备是在SCLK
的控制下,同时发送和接收数据,并通过2
个双向移位寄存器来交换数据。工作原理演示如下图:
上升沿主机SDO发送数据1,同时从设备SDO发送数据0;紧接着在SCLK
的下降沿的时候从设备的SDI接收到了主机发送过来的数据1
,同时主机也接收到了从设备发送过来的数据0.
SPI协议心得
SPI接口时钟配置心得:
在主设备这边配置SPI
接口时钟的时候一定要弄清楚从设备的时钟要求,因为主设备这边的时钟极性和相位都是?
源由璞肝嫉摹R虼嗽谑敝蛹缘呐渲蒙弦欢ㄒ闱宄由璞甘窃谑敝拥纳仙鼗故窍?
降沿接收数据,是在时钟的下降沿还是上升沿输出数据。但要注意的是,由于主设备的
SDO连接从设备的SDI,从设备的SDO连接主设备的SDI,从设备SDI接收的数据是主设备的
SDO发送过来的,主设备SDI接收的数据是从设备SDO发送过来的,所以主设备这边SPI
时钟极性的配置(即SDO的配置)跟从设备的SDI接收数据的极性是相反的,跟从设备SDO
发送数据的极性是相同的。下面这段话是Sychip Wlan8100 Module Spec
上说的,充分说明了时钟极性是如何配置的:
The 81xx module will always input data bits at the rising edge of the clock,
and the host will always output data bits on the falling edge of the clock.
意思是:主设备在时钟的下降沿发送数据,从设备在时钟的上升沿接收数据。因此主设备?
獗逽PI时钟极性应该配置为下降沿有效。
又如,下面这段话是摘自LCD Driver IC SSD1289:
SDI is shifted into 8-bit shift register on every rising edge of SCK in the
order of data bit 7, data bit 6 …… data bit 0.
意思是:从设备SSD1289
在时钟的上升沿接收数据,而且是按照从高位到地位的顺序接收数据的。因此主设备的
SPI时钟极性同样应该配置为下降沿有效。
时钟极性和相位配置正确后,数据才能够被准确的发送和接收。因此应该对照从设备的
SPI接口时序或者Spec文档说明来正确配置主设备的时钟
举报

周润艺

2019-8-28 10:03:02
两块板子一块做主,一块做从机~
举报

朱虹博

2019-8-28 10:18:48
例程如下,该例程在同一块芯片上使用SSI0模块为主,SSI1模块为从,一个发送一个接收,需要外部连接两个SSI模块的对应引脚。
这其实和2块板子通信原理完全一样。另外附件中还有另外3个例程也可以一并同时参考。
外部引脚连接:   
SSI0CLK (PA2) ---> SSI1CLK (PE0)
    SSI0Fss (PA3) ---> SSI1Fss (PE1)
    SSI0Tx  (PA5) ---> SSI1Rx  (PE2)
举报

朱虹博

2019-8-28 10:32:40
引用: Emily1225 发表于 2019-8-28 14:38
例程如下,该例程在同一块芯片上使用SSI0模块为主,SSI1模块为从,一个发送一个接收,需要外部连接两个SSI模块的对应引脚。
这其实和2块板子通信原理完全一样。另外附件中还有另外3个例程也可以一并同时参考。
外部引脚连接:   

对应程序:
 
 
//*****************************************************************************
//
// 设定 SSI0 为SPI主模式,SSI1 为SPI从模式
// SSI0,发送8位数据,SSI1接收
//
//*****************************************************************************
int
main(void)
[
    unsigned long ulDataTx[NUM_SSI_DATA];
    unsigned long ulDataRx[NUM_SSI_DATA];
    unsigned long ulindex;
    //必须写入0xA5A5A5A5,之后才能够改写一些被MWRALLOW保护的寄存器                                        
    HWREG(SYSCTL_MWRALLOW) =  0xA5A5A5A5;
    // 设定 PLL,Fout=300M,M3 running at 75MHz and C28 running at 150MHz
    SysCtlClockConfigSet(SYSCTL_SYSDIV_1 | SYSCTL_M3SSDIV_2 | SYSCTL_USE_PLL |
                         (SYSCTL_SPLLIMULT_M & 0x0F));
    // 该子程序用于设定串口,调试时可以看到串口输出
    // 设定PE4,PE5为SCI功能,115200的速度
    InitConsole();
    // UART输出
    UARTprintf("SSI ->n");
    UARTprintf("  Mode: SPIn");
    UARTprintf("  Data: 8-bitnn");
    //SSI0时钟允许
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
    //允许GPIOA模块的时钟,因为GPIOA2,GPIOA3,GPIOA4,GPIOA5是SSI0引脚,允许时钟后才能使用SSI0模块
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    //设定PA2&PA3&PA4&PA5为SSI0引脚,GPIOPinConfigure设定GPIOPCTL和GPIOAPSEL寄存器.
    GPIOPinConfigure(GPIO_PA2_SSI0CLK);
    GPIOPinConfigure(GPIO_PA3_SSI0FSS);
    GPIOPinConfigure(GPIO_PA4_SSI0RX);
    GPIOPinConfigure(GPIO_PA5_SSI0TX);
    //GPIOPinTypeSSI中设定相关引脚的方向和上拉大小,推挽式无弱上拉
    GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
                   GPIO_PIN_2);
    //SSIConfigSetExpClk(SSI的基础地址,供给SSI的时钟即系统时钟,SSI的数据格式SPI格式还是TI格式,
    //                      SSI是作为主模式还是从模式,设置BITRATE,设置数据长度)
    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(SYSTEM_CLOCK_SPEED),
                       SSI_FRF_MOTO_MODE_0,
                       SSI_MODE_MASTER, USER_BITRATE, 8);
    //设置SSICR1[SSE]寄存器,允许SSI0
    SSIEnable(SSI0_BASE);
    //接收数据,直到无数据直接返回0
    while(SSIDataGetNonBlocking(SSI0_BASE, &ulDataRx[0]))
    [
    ]
   
    //SSI1时钟允许
    SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI1);
   
    //允许GPIOE模块的时钟,因为GPIOE0,GPIOE1,GPIOE2,GPIOE3是SSI1引脚,允许时钟后才能使用SSI1模块
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    //设定PA2&PA3&PA4&PA5为SSI1引脚,GPIOPinConfigure设定GPIOPCTL和GPIOAPSEL寄存器.
    GPIOPinConfigure(GPIO_PE0_SSI1CLK);
    GPIOPinConfigure(GPIO_PE1_SSI1FSS);
    GPIOPinConfigure(GPIO_PE2_SSI1RX);
    GPIOPinConfigure(GPIO_PE3_SSI1TX);
    //GPIOPinTypeSSI中设定相关引脚的方向和上拉大小,推挽式无弱上拉
    GPIOPinTypeSSI(GPIO_PORTE_BASE, GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1 |
                   GPIO_PIN_0);
    //SSIConfigSetExpClk(SSI的基础地址,供给SSI的时钟即系统时钟,SSI的数据格式SPI格式还是TI格式,
    //                      SSI是作为主模式还是从模式,设置BITRATE,设置数据长度)
    SSIConfigSetExpClk(SSI1_BASE, SysCtlClockGet(SYSTEM_CLOCK_SPEED),
                       SSI_FRF_MOTO_MODE_0,
                       SSI_MODE_SLAVE, USER_BITRATE, 8);
    //设置SSICR1[SSE]寄存器,允许SSI1
    SSIEnable(SSI1_BASE);
    //接收数据,直到无数据直接返回0
    while(SSIDataGetNonBlocking(SSI1_BASE, &ulDataRx[0]))
    [
    ]
    //初始化需要发送的数据
    ulDataTx[0] = 'M';
    ulDataTx[1] = '3';
    ulDataTx[2] = 'S';
    ulDataTx[3] = 'P';
    ulDataTx[4] = 'I';
    ulDataTx[5] = 'A';
    ulDataTx[6] = 'B';
    ulDataTx[7] = 'C';
    //UART输出
    UARTprintf("Sent:n  ");
    // 发送3个数据
    for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++)
    [
        // UART显示正在发送的数据
        UARTprintf("'%c' ", ulDataTx[ulindex]);
        //发送数据,把一个数据写入SSI0 的TX FIFO(如无空间一直等待)
        SSIDataPut(SSI0_BASE, ulDataTx[ulindex]);
    ]
    //通过SSIDR[BSY]确认SSI发送是否为忙,用于确认所有的发送是否都已完成
    while(SSIBusy(SSI0_BASE))
    [
    ]
    // UART显示
    UARTprintf("nReceived:n  ");
    // 接收3次数据
    for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++)
    [
        //接收数据,把一个数据读出SSI1 的RX FIFO(如无数据直接返回)
        //前面用SSIBusy确认数据都发完了,所以可以NonBlocking
        SSIDataGetNonBlocking(SSI1_BASE, &ulDataRx[ulindex]);
        // 只要低八位
        ulDataRx[ulindex] &= 0x00FF;
        //输出接收到的数据
        UARTprintf("'%c' ", ulDataRx[ulindex]);
    ]
    // 看看数据是否都正确
    for(ulindex = 0; ulindex < NUM_SSI_DATA; ulindex++)
    [
        if(ulDataTx[ulindex] != ulDataRx[ulindex])
        [
            // 告诉用户测试错误
            UARTprintf("nnError: Data does not exactly match.n");
            UARTprintf("Check that the SPI signals are connected.nn");
            // Wait in infinite loop for debugging.
            while(1)
            [
            ]
        ]
    ]
    // 说明测试通过
    UARTprintf("nnTest Passed.nn");
    // Return no errors
    return(0);
]
举报

更多回帖

发帖
×
20
完善资料,
赚取积分