LPC2138微控制器有2个SPI接口,其中SPI 1兼容SSP接口。
关于SPI协议,可以参考我的上一篇博文《SPI协议详解》。
SPI总线全双工通信体现在发送数据的同时也在接收数据,主要是在SCLK时钟的控制下,通过两个双向移位寄存器进行数据交换。
寄存器描述
S0SPCCR和S0SPCR寄存器在数据传输之前设置,主要是配置SPI工作模式、时钟频率和中断。
S0SPSR寄存器保存SPI控制器状态,只读。其中SPIF位表示数据传输完成,其他位表示异常状态。
S0SPDR寄存器负责数据传输
1)写数据
SPI控制器内部有一个移位寄存器负责数据发送和接收,往S0SPDR寄存器写数据会马上写入移位寄存器,因此在SPIF没有置位时(即数据传输过程中),不应该往S0SPDR中写入数据。
另外,往S0SPDR写数据,才会产生SCLK时钟周期,其他情况下,SCLK保持高电平或者低电平。
2)读数据
SPI控制器接收数据会缓存到一个单独的数据缓存寄存器中,读S0SPDR寄存器实际上返回该寄存器值。如果在接收到下一个字节数据之前,没有及时读取该寄存器,则下一个字节会直接丢弃,而不是覆写到该寄存器中。
实例
本来想用Proteus模拟SPI读写MMC卡,但是折腾了两天,MMC卡初始化都不成功,CMD0切换到idle模式一直不返回0x1。所以直接用MISO和MOSI自环测试了SPI读写。
#include <lpc213x.h> #include "spi.h" #include "serial.h" #define SPI_DEBUG (1) #if 0 void spi0_isr(void) __irq { unsigned char status; unsigned char recv_byte; if ((VICIRQStatus & 0x400) && (S0SPINT & 0x1)) { /* Interrupt Occurs */ status = S0SPSR; if (status & 0x78) { /* ABRT/MODF/ROVR/WCOL exceptions */ sendstr("spi bus 0 exception, status="); sendhex(status); sendstr(" "); } else if (status == 0x80) { /* SPIF indicates SPI Transfer Completed, ** read or write SPI Data Reg clear SPIF */ recv_byte = S0SPDR; sendstr("spi bus 0 recv "); sendhex(recv_byte); sendstr(" "); } /* Clear Interrupt */ S0SPINT = 0x1; } } #endif void spi_init(void) { /* Configure P0.4~7 as SPI bus 0 */ PINSEL0 &= ~0x0000FF00; PINSEL0 |= 0x00005500; /* PCLK is 30MHz, SPI Clock Frequency is 5MHz */ S0SPCCR = 0x6; /* Master mode, 8 bits per transfer(MSB first), ** disable interrupt, CPOL=0, CPHA=0 */ S0SPCR = 0x20; #if 0 /* SPI Bus 0 ISR */ VICVectCntl2 = 0x20 | 10; VICVectAddr2 = (unsigned int)spi0_isr; VICIntEnable = 1 << 10; #endif } int spi_write_byte(unsigned char byte) { unsigned char recv_byte; if (SPI_DEBUG) { sendstr("spi bus 0 send "); sendhex(byte); sendstr(" "); } S0SPDR = byte; /* Check SPIF bit, Wait for Data Transfer Complete */ while (!(S0SPSR & 0x80)); recv_byte = S0SPDR; if (SPI_DEBUG) { sendstr("spi bus 0 recv "); sendhex(recv_byte); sendstr(" "); } return recv_byte; } int spi_read_byte(unsigned char *byte) { if (0 == byte) { sendstr("spi_read_byte byte is NULL "); return -1; } /* Only Write S0SPDR generate SPI Clock for receive data */ *byte = spi_write_byte(0xFF); return 0; } int spi_write(unsigned char *data, int len) { int i = 0; if (0 == data) { sendstr("spi_write data is NULL "); return -1; } for (i = 0; i < len; i++) { spi_write_byte(data[i]); } return 0; } int spi_read(unsigned char *data, int len) { int i = 0; if (0 == data) { sendstr("spi_read data is NULL "); return -1; } for (i = 0; i < len; i++) { spi_read_byte(&data[len - 1 -i]); } return 0; }