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

2019-04-15 12:55发布

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