天天看点

普通IO口模拟实现SPI通信及应用解析

根据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通信