一、简介
SPI(Serial Perripheral Interface)串行外设接口,是由摩托罗拉(Motorola)推出的一种高速、同步、串行、全双工的通信技术,在嵌入式设备中广泛使用的接口协议。SPI支
SPI分为两种模式:主模式,从模式,当作为主模式时可以挂载多个从设备,每个设备都有单独的片选线。
图1.0 SPI一主多从
SPI接口共有四根通信线,分别为MOSI、MISO、CS(SSN)、SCLK,当采用4根通信线则为全双工通信,当采用3根通信线则为单工通信:
·MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入
·MISO(Master Input Slave Output):主设备的数据输入,从设备数据输出
·SCLK(Serial Clock):时钟信号,由主设备产生
·CS(Chip Select):片选信号,拉低选择对应的从设备
SPI根据时钟相位(CPHA)与时钟极性(CPOL)的不同组合共有四种模式,它能决定数据是在什么时刻采样。
图1.1 SPI采样模式
SPI模式0,CPOL = 0,CPHA = 0,时钟空闲时为低电平,数据在时钟的偶数边沿采样(橙色线)
图1.2 SPI模式0
SPI模式1,CPOL = 0,CPHA = 1,时钟空闲时为低电平,数据在时钟的奇数边沿采样(橙色线)
图1.3 SPI模式1
SPI模式2,CPOL = 1,CPHA = 0,时钟空闲时为高电平,数据在时钟的奇数边沿采样(橙色线)
图1.4 SPI模式2
SPI模式3,CPOL = 1,CPHA = 1,时钟空闲时为高电平,数据在时钟的偶数边沿采样(橙色线)
图1.5 SPI模式3
二、程序
初始化SPI设备函数
extern CyU3PReturnStatus_t
CyU3PSpiInit (
void);
配置SPI函数
extern CyU3PReturnStatus_t
CyU3PSpiSetConfig (
CyU3PSpiConfig_t *config, /**< Pointer to the SPI config structure. */
CyU3PSpiIntrCb_t cb /**< Callback for receiving SPI events. */
);
设置片选信号函数
extern CyU3PReturnStatus_t
CyU3PSpiSetSsnLine (
CyBool_t isHigh /**< CyFalse: Pull down the SSN line, CyTrue: Pull up the SSN line. */
);
SPI发送函数(单位:字)
extern CyU3PReturnStatus_t
CyU3PSpiTransmitWords (
uint8_t *data, /**< Source data pointer. This needs to be padded to nearest
byte if the word length is not byte aligned. */
uint32_t byteCount /**< This needs to be a multiple of the word length aligned to the next
byte. */
);
SPI接收函数(单位:字)
extern CyU3PReturnStatus_t
CyU3PSpiReceiveWords (
uint8_t *data, /**< Buffer to read the data into. */
uint32_t byteCount /**< Amount of data to be read. This should be an integral multiple
of the SPI word length aligned to the next byte. */
);
SPI发送/接收函数(单位:字)
extern CyU3PReturnStatus_t
CyU3PSpiTransferWords (
uint8_t *txBuf, /**< Source buffer containing data to transmit. Can be NULL if txByteCount is 0. */
uint32_t txByteCount, /**< Number of data bytes to transmit. Needs to be a multiple of the word length
aligned to the next byte. */
uint8_t *rxBuf, /**< Destination buffer to receive data into. Can be NULL if rxByteCount is 0. */
uint32_t rxByteCount /**< Number of data bytes to receive. Needs to be a multiple of the word length
aligned to the next byte. */
);
SPI配置源文件
#include "cyfx3_spi.h"
#include "cyu3error.h"
#include "cyu3utils.h"
static CyU3PReturnStatus_t CyFx3GpioConfig(void)
{
CyU3PReturnStatus_t CyFx3Status;
//GPIO时钟配置(整个工程中只需要初始化一次)
CyU3PGpioClock_t GpioClkConifg = {
.fastClkDiv = 2,
.slowClkDiv = 0,
.halfDiv = 0,
.simpleDiv = CY_U3P_GPIO_SIMPLE_DIV_BY_2,
.clkSrc = CY_U3P_SYS_CLK,
};
CyFx3Status = CyU3PGpioInit(&GpioClkConifg,NULL);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
#if !SPI_HARDWARE
CyU3PGpioSimpleConfig_t SpiMosiGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyTrue,
.driveHighEn = CyTrue,
.inputEn = CyFalse,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
CyFx3Status = CyU3PGpioSetSimpleConfig(SPI_MOSI_GPIO_PIN,&SpiMosiGpioConfig);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
CyU3PGpioSimpleConfig_t SpiMisoGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyFalse,
.driveHighEn = CyFalse,
.inputEn = CyTrue,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
CyFx3Status = CyU3PGpioSetSimpleConfig(SPI_MISO_GPIO_PIN,&SpiMisoGpioConfig);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
CyU3PGpioSimpleConfig_t SpiClkGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyTrue,
.driveHighEn = CyTrue,
.inputEn = CyFalse,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
CyFx3Status = CyU3PGpioSetSimpleConfig(SPI_CLK_GPIO_PIN,&SpiClkGpioConfig);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
CyU3PGpioSimpleConfig_t SpiSsnGpioConfig = {
.outValue = CyTrue,
.driveLowEn = CyTrue,
.driveHighEn = CyTrue,
.inputEn = CyFalse,
.intrMode = CY_U3P_GPIO_NO_INTR,
};
return CyU3PGpioSetSimpleConfig(SPI_SSN_GPIO_PIN,&SpiSsnGpioConfig);
#endif
}
CyU3PReturnStatus_t CyFx3SpiInit(void)
{
#if !SPI_HARDWARE
return CyFx3GpioConfig();
#else
CyU3PReturnStatus_t CyFx3Status;
CyFx3Status = CyU3PSpiInit();
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
CyU3PSpiConfig_t SpiConfig = {
.isLsbFirst = CyFalse,
.cpol = CyFalse,
.cpha = CyFalse,
.ssnPol = CyFalse,
.ssnCtrl = CyFalse,
.leadTime = CY_U3P_SPI_SSN_LAG_LEAD_HALF_CLK,
.lagTime = CY_U3P_SPI_SSN_LAG_LEAD_HALF_CLK,
.clock = SPI_FREQUENCY,
.wordLen = 8,
};
CyFx3Status = CyU3PSpiSetConfig(&SpiConfig,NULL);
if(CyFx3Status != CY_U3P_SUCCESS)
{
return CyFx3Status;
}
return CyU3PSpiSetSsnLine(CyFalse);
#endif
}
#if !SPI_HARDWARE
static CyU3PReturnStatus_t SpiSetMosiValue(CyBool_t isHigh)
{
return CyU3PGpioSetValue(SPI_MOSI_GPIO_PIN,isHigh);
}
static CyBool_t SpiGetMisoValue(void)
{
return CyFx3GpioReadBit(SPI_MISO_GPIO_PIN);
}
static CyU3PReturnStatus_t SpiSetClkValue(CyBool_t isHigh)
{
return CyU3PGpioSetValue(SPI_CLK_GPIO_PIN,isHigh);
}
CyU3PReturnStatus_t SpiSetSsnValue(CyBool_t isHigh)
{
return CyU3PGpioSetValue(SPI_SSN_GPIO_PIN,isHigh);
}
//CPOL = 0, CPHA = 0, MSB first
uint64_t SpiReadWriteMode0(uint32_t write_dat1,uint32_t write_dat2,uint32_t len)
{
uint32_t i;
uint32_t read_dat;
uint32_t testreadspi;
if(len == 32)
{
for(i = 0;i < 32;i++)
{
if(write_dat1 & 0x80000000)
{
SpiSetMosiValue(CyTrue);
}
else
{
SpiSetMosiValue(CyFalse);
}
SpiSetClkValue(CyTrue);
read_dat <<= 1;
write_dat1 <<= 1;
CyU3PBusyWait(1);
if(SpiGetMisoValue() == CyTrue)
{
read_dat++;
}
SpiSetClkValue(CyFalse);
}
testreadspi = read_dat;
}
else if(len == 64)
{
for(i = 0;i < 32;i++)
{
if(write_dat1 & 0x80000000)
{
SpiSetMosiValue(CyTrue);
}
else
{
SpiSetMosiValue(CyFalse);
}
SpiSetClkValue(CyTrue);
write_dat1 <<= 1;
//CyU3PBusyWait(1);
read_dat <<= 1;
CyU3PBusyWait(1);
if(SpiGetMisoValue() == CyTrue)
{
read_dat++;
}
SpiSetClkValue(CyFalse);
}
testreadspi = read_dat << 16;
for(i = 0;i < 32;i++)
{
if(write_dat2 & 0x80000000)
{
SpiSetMosiValue(CyTrue);
}
else
{
SpiSetMosiValue(CyFalse);
}
SpiSetClkValue(CyTrue);
write_dat2 <<= 1;
read_dat <<= 1;
CyU3PBusyWait(1);
if(SpiGetMisoValue() == CyTrue)
{
read_dat++;
}
//CyU3PBusyWait(1);
SpiSetClkValue(CyFalse);
}
testreadspi += read_dat >> 16;
}
return testreadspi;
}
uint8_t SpiReadWriteByte(uint8_t Byte)
{
uint8_t i;
for(i = 0;i < 8;i++)
{
if(Byte & 0x80)
{
SpiSetMosiValue(CyTrue);
}
else
{
SpiSetMosiValue(CyFalse);
}
Byte <<= 1;
CyU3PBusyWait(1);
SpiSetClkValue(CyFalse);
if(SpiGetMisoValue() == CyTrue)
{
Byte |= 0x01;
}
CyU3PBusyWait(1);
SpiSetClkValue(CyTrue);
}
/*
for(i = 0;i < 8;i++)
{
SpiSetClkValue(CyTrue);
if(Byte & 0x80)
{
SpiSetMosiValue(CyTrue);
}
else
{
SpiSetMosiValue(CyFalse);
}
Byte <<= 1;
CyU3PBusyWait(1);
SpiSetClkValue(CyFalse);
if(SpiGetMisoValue() == CyTrue)
{
Byte |= 0x01;
}
CyU3PBusyWait(1);
}
*/
return Byte;
}
void CyFx3SpiReceiveTransmitBytes(uint8_t *WriteData,uint8_t *ReadData,uint16_t DataLen)
{
for(uint16_t i = 0;i < DataLen;i++)
{
ReadData[i] = SpiReadWriteByte(WriteData[i]);
}
}
#endif
SPI配置头文件
#ifndef CYFX3_SPI_H_
#define CYFX3_SPI_H_
#include "cyu3spi.h"
#include "cyu3gpio.h"
#include "gpio_regs.h"
#define SPI_HARDWARE 0
#define SPI_GPIO_REGS 0
#define SPI_FREQUENCY 8000000U //8MHz
#if !SPI_HARDWARE
#define CyFx3GpioReadBit(GpioPin) ((GPIO->lpp_gpio_simple[GpioPin]& CY_U3P_LPP_GPIO_IN_VALUE) >> 1)
#define CyFx3GpioSetBit(GpioPin) ((*(uvint32_t *)&GPIO->lpp_gpio_simple[GpioPin]) |= CY_U3P_LPP_GPIO_OUT_VALUE)
#define CyFx3GpioResetBit(GpioPin) ((*(uvint32_t *)&GPIO->lpp_gpio_simple[GpioPin]) &= ~CY_U3P_LPP_GPIO_OUT_VALUE)
#define SPI_MOSI_GPIO_PIN 51
#define SPI_MISO_GPIO_PIN 57
#define SPI_CLK_GPIO_PIN 50
#define SPI_SSN_GPIO_PIN 52
CyU3PReturnStatus_t SpiSetSsnValue(CyBool_t isHigh);
uint8_t SpiReadWriteByte(uint8_t WriteData);
void CyFx3SpiReceiveTransmitBytes(uint8_t *WriteData,uint8_t *ReadData,uint16_t DataLen);
#endif
CyU3PReturnStatus_t CyFx3SpiInit(void);
#endif /* CYFX3_SPI_H_ */