yanniwang

ADuC7026串行通信接口SPI解析

0
阅读(17657)

ADuC7026芯片集成了一个完整的片上硬件外围串行接口(SPI)。SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有DSP和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如ADuC70XX全系列MCU.

  SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASHRAM、LCD显示驱动器ADC和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCLK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。

  SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(用于单向传输时,也就是半双工方式)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)。

  其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。

  接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。

  要注意的是,SCLK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCLK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCLK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。

在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。

 

下面例子是在两片ADUC7026上的SPI通信,实现主机发送从机接收的功能。

SPI主机程序1和主机程序2,均实现了发送0X00—0X24的功能,只是在SPI主机程序2中利用中断来发送。从机程序1和从机程序2也都实现了接收主机发送数据的功能,只是从机程序2是利用中断程序来接收。

SPI主机程序
#include

unsigned char results[30] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
	0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
	0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
	0x21, 0x22, 0x23, 0x24};

 void delay (int length)
{
	while (length >=0)
    	length--;
}

int main(void) {
	int i = 0;

	GP1CON = 0x22220000;			//使能SPI引脚
	SPIDIV = 0xCC;					//设定SPI时钟频率,f=41780000/(2x(1+SPIDIV)),
									// SPIDIV = 0xCC,对应f=100Kz
	  SPICON = 0x104B;				//连续传送数据使能和SPI主机使能,串行时钟
                                    极性模式为低电平  
	for (i=0;i<30;i++)
	{
		SPITX = results[i];              //发送数据
 		while ((SPISTA & 0x02) != 0x02) ; //等待发送数据完成
	   delay(100000);
	}   	  
while (1){}
} 

SPI主机程序2
/*#include
#define SPI_MASTER_BIT 	0x00002000	
 
void IRQ_Handler(void) __irq;			//	常规中断子程序 
int i = 0;
void delay (int length)
{
	while (length >=0)
    	length--;
}


int main(void) {
	GP4DAT = 0x04000000;			// P4.2 输出低电平, LED 被点亮	
	IRQEN = SPI_MASTER_BIT;
	GP1CON = 0x22220000;			// 设置SPI各功能引脚
	SPIDIV = 0xCC;					//设定SPI时钟频率,f=41780000/(2x(1+SPIDIV)),
									// SPIDIV = 0xCC,对应f=100Kz
	SPICON = 0x104B;			    //连续传送数据使能和SPI主机使能,串行时 
                                    //钟极性模式为低电平
while (1){
	}				
}
/*********************************************************************/                                                                
/*		               中断服务子程序							    */
/********************************************************************/

void IRQ_Handler() __irq {                          //主机发送
	GP4DAT ^= 0x00040000;		
	if ((IRQSTA & SPI_MASTER_BIT) != 0) {
		SPITX = i;
		delay(100000);
		i++;
		if (i==30) IRQCLR = SPI_MASTER_BIT;
	}
	return ;
}

SPI从机程序1
#include

int main(void) {
char i;	
char received_data[30];

	GP1CON = 0x22220000;			//设置SPI各功能引脚
	SPICON = 0x1409;					//使能SPI从机模式
	
for (i=0; i <30; i++) {
 	while (!(SPISTA & 0x08)) ; 			// 等待SPIRX寄存器满
	received_data[i] = SPIRX;			//读数据并清零SPISTA的位4
	}
while (1) {

	}			
}
SPI从机程序2
#include
#define SPI_MASTER_BIT 			0x00002000

void IRQ_Handler(void) __irq;			//	常规中断子程序
char i;	
char received_data[30];


int main(void) {

	GP4DAT = 0x04000000;			//	使P4.2输出低电平,LED点亮

	IRQEN = SPI_SLAVE_BIT;         //使能SPI从机中断

	GP1CON = 0x22220000;			//设置SPI各功能引脚
	SPICON = 0x1409;					// 使能SPI从机模式,且从机接收
	
while (1) {

	}			
}


/*********************************************************************/                                                                
/*		               中断服务子程序							    */
/********************************************************************/

void IRQ_Handler() __irq {
	GP4DAT ^= 0x00040000;		
	if ((IRQSTA & SPI_SLAVE_BIT) != 0) {
		received_data[i] = SPIRX;
		i++;
		SPITX = i;
		if (i==30) i=0;
	}
	return ;
}