IO口模拟SPI总线
SPI是一种全双工同步串行接口,四总线结构SCK、MOSI、MISO、CS分别是时钟、主机输出、主机接收、片选。其中各个厂商的写法可能不一致。SPI总线有四种工作模式,在不再做介绍。最常用的SPI总线时序CS为低时SCK上冲沿数据有效,数据从MISO、MOSI输入和输出。我们只要有这个概念就行,具体时序可以直接看芯片手册。很多芯片都集成了SPI总线接口,没有SPI接口的芯片同样可以用IO口模拟其时序加深理解其时序。最近工作中也用到了SPI接口,闲暇之余用SISI软件用51单片机IO口模拟SPI总线和25LC1024 EEPROM通信。电路连接如下:
用25LC1024的两种操作方式来测试IO口模拟的SPI,这种两种方式分别是连续读、连续写。当然还有其他操作方式如擦写等。连续读、连续写时序如下:
连续读方式:
读操作流程:读指令->24bit地址->数据
连续读的时序:
写数据时首先发送读指令和24bit的地址。值得注意的是整个过程CS总是为低的,中间不能跳回高电平。之前我没有注意导致了读写失败。
连续写方式:
写操作比较特殊,具体流程比读操作多一个使能:写使能->写指令->24bit地址->数据
写使能命令时序:
发送了写使能之后就可以进一步操作,写时序如下:
测试思路:将0到15的七段数码管十六进制模数据写入到SPI EEPROM中然后读出,再用七段数码管显示出来具体代码如下:
#include <reg51.h>#include<intrins.h>//包含_nop_()函数//定义命令#define READ 0x03#define WRITE 0x02#define WREN 0x06#define WRDI 0x04#define RDSR 0x05#define WRSR 0x01#define PE 0x42#define SE 0xd8#define CE 0xc7#define RDID 0xab#define DPD 0xb9//端口定义sbit SCK = P1^4;sbit MOSI = P1^5;sbit MISO = P1^6;sbit CS = P1^7;//延时程序void delay_ms(int n){int i, j;for(i=0;i<n;++i)for(j=0;j<1000;++j);}//延时程序void delay_ns(void) //延时>4us{_nop_();//空指令_nop_();_nop_();_nop_();}//SPI写一个bytevoid SPI_byte_write(const char *dat){char tmp = *dat, i;for(i=0;i<8;++i){SCK = 0;MOSI = (bit)(tmp >> 7); //先输出高bittmp <<= 1; //数据左移一位SCK = 1; //SCK为高发送bitdelay_ns(); //延时防止芯片为响应}}//SPI读一个bytevoid SPI_byte_read(char *dat){char tmp = 0, i;for(i=0;i<8;++i){SCK = 0;tmp <<= 1; //左移一位tmp |= MISO; //接收最高位SCK = 1;//SCK为高接收bitdelay_ns();}*dat = tmp;}//SPI写多个bytevoid SPI_nbyte_write(const char *addr, const char *dat, char num){char inst_wren = WREN, inst_write = WRITE, i;CS = 0;SPI_byte_write(&inst_wren); //写使能CS = 1;delay_ms(1);CS = 0;SPI_byte_write(&inst_write); //写指令for(i=0;i<3;++i){SPI_byte_write(addr + i);//写地址}for(i=0;i<num;++i){SPI_byte_write(dat + i);//写数据}CS = 1;}//SPI读多个bytevoid SPI_nbyte_read(const char *addr, char *dat, char num){char inst = READ, i;CS = 0;SPI_byte_write(&inst); //读指令for(i=0;i<3;++i){SPI_byte_write(addr + i);//写地址}for(i=0;i<num;++i){SPI_byte_read(dat + i);//写数据}CS = 1;}char dat[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0~F的字形码char save[16] = {0};const char addr[3] = {0x00, 0x00, 0x00};//读写的地址int main(void){ int i; SPI_nbyte_write(addr, dat, 16);//写入数据 delay_ms(30); SPI_nbyte_read(addr, save, 16);//读出数据 for(i=0;i<16;i++) {P3=save[i];//数码管显示delay_ms(60);}return 0;}