对自己近期SPI模块通信的学习进行相关记录。
学习自用,防止遗忘!
实验例程1. SPIA模块的自发自收(spi_ex2_loopback_fifo_interrupts)
#include "driverlib.h"
#include "device.h"
//
// Globals
//
uint16_t sData[2]; // Send data buffer
uint16_t rData[2]; // Receive data buffer
uint16_t rDataPoint = 0; // To keep track of where we are in the
// data stream to check received data
//
// Function Prototypes
//
void initSPIFIFO(void);
__interrupt void spiTxFIFOISR(void);
__interrupt void spiRxFIFOISR(void);
//
// Main
//
void main(void)
{
uint16_t i;
//
// Initialize device clock and peripherals
//
Device_init();
//
// Disable pin locks and enable internal pullups.
//
Device_initGPIO();
//
// Initialize PIE and clear PIE registers. Disables CPU interrupts.
//
Interrupt_initModule();
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
//
Interrupt_initVectorTable();
//
// Interrupts that are used in this example are re-mapped to ISR functions
// found within this file.
//
Interrupt_register(INT_SPIA_TX, &spiTxFIFOISR);
Interrupt_register(INT_SPIA_RX, &spiRxFIFOISR);
//
// Set up SPI, initializing it for FIFO mode
//
initSPIFIFO();
//
// Initialize the data buffers
//
for(i = 0; i < 2; i++)
{
sData[i] = i;
rData[i]= 0;
}
//
// Enable interrupts required for this example
//
Interrupt_enable(INT_SPIA_TX);
Interrupt_enable(INT_SPIA_RX);
//
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
//
EINT;
ERTM;
//
// Loop forever. Suspend or place breakpoints to observe the buffers.
//
while(1)
{
;
}
}
//
// Function to configure SPI A in FIFO mode.
//
void initSPIFIFO()
{
//
// Must put SPI into reset before configuring it
//
SPI_disableModule(SPIA_BASE);
//
// SPI configuration. Use a 500kHz SPICLK and 16-bit word size.
//
SPI_setConfig(SPIA_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA0,
SPI_MODE_MASTER, 500000, 16);
SPI_enableLoopback(SPIA_BASE);
SPI_setEmulationMode(SPIA_BASE, SPI_EMULATION_STOP_AFTER_TRANSMIT);
//
// FIFO and interrupt configuration
//
SPI_enableFIFO(SPIA_BASE);
SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_RXFF | SPI_INT_TXFF);
SPI_setFIFOInterruptLevel(SPIA_BASE, SPI_FIFO_TX2, SPI_FIFO_RX2);
SPI_enableInterrupt(SPIA_BASE, SPI_INT_RXFF | SPI_INT_TXFF);
//
// Configuration complete. Enable the module.
//
SPI_enableModule(SPIA_BASE);
}
//
// SPI A Transmit FIFO ISR
//
__interrupt void spiTxFIFOISR(void)
{
uint16_t i;
//
// Send data
//
for(i = 0; i < 2; i++)
{
SPI_writeDataNonBlocking(SPIA_BASE, sData[i]);
}
//
// Increment data for next cycle
//
for(i = 0; i < 2; i++)
{
sData[i] = sData[i] + 1;
}
//
// Clear interrupt flag and issue ACK
//
SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_TXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP6);
}
//
// SPI A Receive FIFO ISR
//
__interrupt void spiRxFIFOISR(void)
{
uint16_t i;
//
// Read data
//
for(i = 0; i < 2; i++)
{
rData[i] = SPI_readDataNonBlocking(SPIA_BASE);
}
//
// Check received data
//
for(i = 0; i < 2; i++)
{
if(rData[i] != (rDataPoint + i))
{
// Something went wrong. rData doesn't contain expected data.
Example_Fail = 1;
ESTOP0;
}
}
rDataPoint++;
// ESTOP0;
//
// Clear interrupt flag and issue ACK
//
SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_RXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP6);
Example_PassCount++;
}
【框架理解】:自发自收模式设置下,不需要对外部线路进行连接。设置每次发送2个字符。
当FIFO深度设置为2,接收FIFO接收到大于等于2个字符时,触发中断;发送FIFO小于等于2个字节时,触发中断。因此当程序刚开始上电时,发送FIFO=0,触发中断,开始往发送FIFO中填充数据,当填充到2个的时候,符合深度设置条件,自动经过移位寄存器往接收FIFO中传输,当传输一个字节进入移位寄存器时,此时发送FIFO=1,继续往发送FIFO中填充2个字节,发送FIFO=3,跳出等待,接收FIFO将数据读取,当数据经移位寄存器到达接收FIFO=2时,接收FIFO触发中断,将数据输出,然后再由发送FIFO往接收FIFO发送,当发送FIFO<2,再次往里面填充2个,以此类推,and so on...
实验例程2. SPIA与SPIB之间的通(spi_ex5_external_loopback_fifo_interrupts)
#include "driverlib.h"
#include "device.h"
//
// Globals
//
volatile uint16_t sData[2]; // Send data buffer
volatile uint16_t rData[2]; // Receive data buffer
volatile uint16_t rDataPoint = 0; // To keep track of where we are in the
// data stream to check received data
//
// Function Prototypes
//
__interrupt void spibTxFIFOISR(void);
__interrupt void spiaRxFIFOISR(void);
void SPI_init();
void PinMux_init();
//
// Main
//
void main(void)
{
uint16_t i;
//
// Initialize device clock and peripherals
//
Device_init();
//
// Disable pin locks and enable internal pullups.
//
Device_initGPIO();
//
// Initialize PIE and clear PIE registers. Disables CPU interrupts.
//
Interrupt_initModule();
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
//
Interrupt_initVectorTable();
//
// Interrupts that are used in this example are re-mapped to ISR functions
// found within this file.
//
Interrupt_register(INT_SPIB_TX, &spibTxFIFOISR);
Interrupt_register(INT_SPIA_RX, &spiaRxFIFOISR);
//
// Board initialization
//
EALLOW;
PinMux_init();
SPI_init();
EDIS;
//
// Initialize the data buffers
//
for(i = 0; i < 2; i++)
{
sData[i] = i;
rData[i]= 0;
}
//
// Enable interrupts required for this example
//
Interrupt_enable(INT_SPIA_RX);
Interrupt_enable(INT_SPIB_TX);
//
// Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
//
EINT;
ERTM;
//
// Loop forever. Suspend or place breakpoints to observe the buffers.
//
while(1)
{
;
}
}
void PinMux_init()
{
//
// SPIA -> SPIA_slave Pinmux
//
GPIO_setPinConfig(GPIO_16_SPIA_SIMO);
GPIO_setPinConfig(GPIO_17_SPIA_SOMI);
GPIO_setPinConfig(GPIO_56_SPIA_CLK);
GPIO_setPinConfig(GPIO_57_SPIA_STE);
//
// SPIB -> SPIB_master Pinmux
//
GPIO_setPinConfig(GPIO_24_SPIB_SIMO);
GPIO_setPinConfig(GPIO_31_SPIB_SOMI);
GPIO_setPinConfig(GPIO_22_SPIB_CLK);
GPIO_setPinConfig(GPIO_27_SPIB_STE);
}
void SPI_init()
{
//SPIA_slave initialization
SPI_disableModule(SPIA_BASE);
SPI_setConfig(SPIA_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA0,
SPI_MODE_SLAVE, 500000, 16);
SPI_enableFIFO(SPIA_BASE);
SPI_setFIFOInterruptLevel(SPIA_BASE, SPI_FIFO_TX2, SPI_FIFO_RX2);
SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_RXFF);
SPI_enableInterrupt(SPIA_BASE, SPI_INT_RXFF);
SPI_disableLoopback(SPIA_BASE);
SPI_setEmulationMode(SPIA_BASE, SPI_EMULATION_FREE_RUN);
SPI_enableModule(SPIA_BASE);
//SPIB_master initialization
SPI_disableModule(SPIB_BASE);
SPI_setConfig(SPIB_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA0,
SPI_MODE_MASTER, 500000, 16);
SPI_enableFIFO(SPIB_BASE);
SPI_setFIFOInterruptLevel(SPIB_BASE, SPI_FIFO_TX2, SPI_FIFO_RX2);
SPI_clearInterruptStatus(SPIB_BASE, SPI_INT_TXFF);
SPI_enableInterrupt(SPIB_BASE, SPI_INT_TXFF);
SPI_disableLoopback(SPIB_BASE);
SPI_setEmulationMode(SPIB_BASE, SPI_EMULATION_FREE_RUN);
SPI_enableModule(SPIB_BASE);
}
//
// SPI A Transmit FIFO ISR
//
__interrupt void spibTxFIFOISR(void)
{
uint16_t i;
//
// Send data
//
for(i = 0; i < 2; i++)
{
SPI_writeDataNonBlocking(SPIB_BASE, sData[i]);
}
//
// Increment data for next cycle
//
for(i = 0; i < 2; i++)
{
sData[i] = sData[i] + 1;
}
//
// Clear interrupt flag and issue ACK
//
SPI_clearInterruptStatus(SPIB_BASE, SPI_INT_TXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP6);
}
//
// SPI B Receive FIFO ISR
//
__interrupt void spiaRxFIFOISR(void)
{
uint16_t i;
//
// Read data
//
for(i = 0; i < 2; i++)
{
rData[i] = SPI_readDataNonBlocking(SPIA_BASE);
}
//
// Check received data
//
for(i = 0; i < 2; i++)
{
if(rData[i] != (rDataPoint + i))
{
// Something went wrong. rData doesn't contain expected data.
ESTOP0;
}
}
rDataPoint++;
//
// Clear interrupt flag and issue ACK
//
SPI_clearInterruptStatus(SPIA_BASE, SPI_INT_RXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP6);
}
【使用说明】:设置SPIA--接收(从);SPIB--发送(主)。由于是采用两个模块之间的数据通信,因此需要进行外部线路连接。如下:
//! -GPIO24(SPIB_SIMO) and GPIO16(SPIA_SIMO) - SPISIMO
//! -GPIO31(SPIB_SOMI) and GPIO17(SPIA_SOMI) - SPISOMI
//! -GPIO22(SPIB_CLK) and GPIO56(SPIA_CLK) - SPICLK
//! -GPIO27(SPIB_STE) and GPIO57(SPIA_STE) - SPISTE
这里将官方例程进行适当修改,已实现需求。
PS:【温馨提示】若你采用官方例程,然后只能发送数据,无法接收到数据,可以用示波器来看一下时钟信号线与接收线波形 。(可将GPIO22引脚换为26引脚,尝试。因为22引脚有其他用途,可能存在冲突,导致出现问题。)
对问题的排查主要从软件与硬件两方面着手,在确保软件没有问题的情况下,对硬件及配置进行排查。通过采用示波器测量数据的发送端与接收端及时钟信号的数据波形情况,判断是否真的有数据进行传输通信,如下图:
由测量波形可知,MOSI主机输出数据成功发送,但MOSI从机输入无接收信号且无时钟信号,对时钟信号配置引脚更换后,时钟信号恢复正常,从机成功接收数据。