根据SPI通信规范(具体可以参考“
浅谈SPI总线”),与IO口模拟I2C类似,通过普通IO端口模拟也可以实现单片机(主设备)与从设备的SPI通信,其中
使能信号CS在开始SPI通信前置低,在通信结束后置高,时钟线SCK通过IO口延时高低电平变化实现,MOSI,MISO根据SCK状态实现发送接收数据等,以下是相关代码
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: SPI Communication driver(By IO)
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.16
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef SPIDRV_H
#define SPIDRV_H
/* Include Files */
/* Macros */
//#define SPI_MISO_USE
//#define SPI_CS_USE
#define SPI_MOSI PTT_PTT0 //举例以Freescale PT0端口为MOSI线,PT1端口为MISO线,PT2端口为MOSI线,PT3端口为CS线
#define SPI_MOSI_IO DDRT_DDRT0
#ifdef SPI_MISO_USE
#define SPI_MISO PTT_PTT1
#define SPI_MISO_IO DDRT_DDRT1
#endif
#define SPI_SCK PTT_PTT2
#define SPI_SCK_IO DDRT_DDRT2
#ifdef SPI_CS_USE
#define SPI_CS PTT_PTT3
#define SPI_CS_IO DDRT_DDRT3
#endif
#define IO_OUT_MODE 1 //Freescale:1为输出模式,0为输入模式;NEC:0为输出模式,1为输入模式
#define IO_IN_MODE 0
/* Function Prototypes */
void SPIDelay(void);
void SPIInit(void);
void SPISendByte(unsigned char sendData);
#ifdef SPI_MISO_USE
unsigned char SPIReceiveByte(void);
#endif
#endif
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: SPI Communication driver(By IO)
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.16
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Include Files */
#include "SPIDrv.h"
/* Function Prototypes */
/* Function Definitions */
/*
* FunctionName: SPIDelay
* Purpose: SPI时序模拟SCK时间间隔(周期),需要根据Slave性能及单片机工作频率调整
* Parameters: 无
*/
void SPIDelay(void)
{
_asm("nop");
_asm("nop");
_asm("nop");
_asm("nop");
_asm("nop");
}
/*
* FunctionName: SPIInit
* Purpose: SPI使用的相应端口初始化
* Parameters: 无
*/
void SPIInit(void)
{
SPI_SCK_IO=IO_OUT_MODE; //设置SCK端口为输出端口
SPI_MOSI_IO=IO_OUT_MODE; //设置MOSI端口为输出端口
#ifdef SPI_MISO_USE
SPI_MISO_IO=IO_IN_MODE; //设置MISO端口为输入端口
#endif
#ifdef SPI_CS_USE
SPI_CS_IO=IO_OUT_MODE; //设置CS端口为输出端口
SPI_CS=1; //设置CS端口为高电平,CS无效
#endif
}
/*
* FunctionName: SPISendByte
* Purpose: 模拟SPI发送一个字节数据
* Parameters: sendData-发送的一个字节数据
*/
void SPISendByte(unsigned char sendData)
{
unsigned char serialNum = 0;
SPI_SCK=0; //设置SCK端口为低电平
#ifdef SPI_CS_USE
SPI_CS=0; //设置CS端口为低电平,CS有效
#endif
for(serialNum=8;serialNum>=1;serialNum--) //以MSB方式按位发送一个字节数据,上升沿一位数据被存入移位寄存器
{
SPI_MOSI = (sendData>>(serialNum-1))&0x01;
SPIDelay();
SPI_SCK = 1;
SPIDelay();
SPI_SCK = 0;
SPIDelay();
}
#ifdef SPI_CS_USE
SPI_CS=1; //设置CS端口为高电平,CS无效
#endif
}
#ifdef SPI_MISO_USE
/*
* FunctionName: SPIReceiveByte
* Purpose: 模拟SPI接收一个字节数据
* Parameters: 无
*/
unsigned char SPIReceiveByte(void)
{
unsigned char serialNum = 0;
unsigned char dataValue=0;
SPI_SCK=0; //设置SCK端口为低电平
SPI_CS=0; //设置CS端口为低电平,CS有效
for(serialNum=0;serialNum<=7;serialNum++)//以MSB方式按位接收一个字节数据,上升沿一位数据被存入移位寄存器
{
SPI_SCK=1;
SPIDelay();
if(SPI_MISO) dataValue|=(0b10000000>>serialNum);
SPI_SCK=0;
SPIDelay();
}
SPI_CS=1; //设置CS端口为高电平,CS无效
return dataValue;
}
#endif
模拟SCK需要注意采用的延时需要根据从设备的特性来调整,延时时间不能小于从设备的最小SCK间隔时间,以LED驱动芯片HEF4894为例,在VDD为5V的情况下fclk(max)典型值为10MHZ,最小值为5MHZ,那么模拟SCK不应超过5MHZ,以下是HEF4894控制LED相关函数的代码
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: Communication with LED Driver - HEF4894
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.16
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef HEF4894APP_H
#define HEF4894APP_H
/* Include Files */
#include "SPIDrv.h"
/* Macros */
#define HEF4894_OE PTT_PTT4 //举例以Freescale PT4端口为HEF4894 OE线,PT5端口为HEF4894 STR线
#define HEF4894_OE_IO DDRT_DDRT4
#define HEF4894_STR PTT_PTT5
#define HEF4894_STR_IO DDRT_DDRT5
#define LED_BUF_SIZE 2
/* Variables */
extern unsigned char LEDSPIBuffer[LED_BUF_SIZE];
/* Function Prototypes */
void SlaveHEF4894Init(void);
void SlaveHEF4894Transmit(void);
#endif
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: Communication with LED Driver - HEF4894
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.16
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Include Files */
#include "HEF4894App.h"
/* Variables */
unsigned char LEDSPIBuffer[LED_BUF_SIZE];
/* Function Definitions */
/*
* FunctionName: SlaveHEF4894Init
* Purpose: HEF4894自用的相应端口及数据初始化
* Parameters: 无
*/
void SlaveHEF4894Init(void)
{
unsigned char i;
HEF4894_OE_IO=IO_OUT_MODE; //设置HEF4894 OE端口为输出端口
HEF4894_STR_IO=IO_OUT_MODE; //设置HEF4894 OE端口为输出端口
HEF4894_OE=0; //设置HEF4894 OE无效
HEF4894_STR=0; //设置HEF4894 STR无效
for(i=0;i
需要注意不同的从设备可能还会有一些特殊要求,例如HEF4894需要STR端口控制将shift register 的数据传到storage register,需要OE端口控制将storage register 的数据输出。
和采用IO口来模拟I2C通信一样,通过普通IO口模拟实现SPI通信一般仅用于单片机没有SPI模块的情况下,如果单片机本身具有SPI模块,还是应该通过配置单片机相应的寄存器来实现SPI通信