学习板芯片为STM32F103VE
先简单介绍下SPI(全称:Serial Peripheral interface,串行外围设备接口):SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,是一种高速的,全双工,同步的通信总线。一般SPI芯片与MCU通信,只会使用4个管脚:MOSI(主输出),MISO(主输入),CS(片选低电平有效),SCK(时钟)
STM32内的SPI的特点:
1 8或16位传输帧格式选择
2 可编程的时钟极性(CPOL)和相位(CPHA)
3 可编程的数据顺序,MSB在前或LSB在前
4 8个主模式波特率预分频系数(最大为fPCLK/2)
5 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求
6 可触发中断的专用发送和接收标志
SPI较重要的寄存器:
8或16位传输帧格式选择 由SPI_CR1寄存器上的DFF位确定,0:8位
时钟极性和相位由SPI_CR1寄存器上的CPOL,CPHA 位确定
模式0:CPOL=0,CPHA=0:SCK在空闲时保持低电平,在第一个变化沿传递数据 (本次使用的SPI芯片SST25VF016B只能使用在模式0和3)
模式1:CPOL=0,CPHA=1:SCK在空闲时保持低电平,在第二个变化沿传递数据
模式2:CPOL=1,CPHA=0:SCK在空闲时保持高电平,在第一个变化沿传递数据
模式3:CPOL=1,CPHA=1:SCK在空闲时保持高电平,在第二个变化沿传递数据
可编程的数据顺序 ,MSB在前或LSB在前 由SPI_CR1寄存器中的LSBFIRST位确定,0:MSB在前
8个主模式波特率预分频系数(最大为fPCLK/2) 由SPI_CR1寄存器上的BR[2:0]确定
应用程序通过3个状态标志可以完全监控SPI总线的状态:
1.发送缓冲器空闲标志(TXE),此标志为’1’时表明发送缓冲器为空,要等到该位为0,进行下一次发送
2.接收缓冲器非空(RXNE),同理
3.忙(Busy)标志
SPI芯片SST25VF016B
8个管脚,一般只有4个管脚与MCU连接,SI,SO,CE,SCK
状态寄存器:
更多信息参考SST25VF016B数据手册
STM32y与SST25VF016B进行SPI通信代码实现:
//时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//用于打印读取到的数据
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
//管脚配置 SPI PA5:SCK PA6:MOSI PA7:MISO PE6:CS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_6);
//SPI配置 SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//双向全双工 SPI_InitStructure.SPI_Mode=SPI_Mode_Master; //主机模式 SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b; //每次发生8位 SPI_InitStructure.SPI_CPOL=SPI_CPOL_High; //时钟空闲时为高 SPI_InitStructure.SPI_CPHA=SPI_CPHA_2Edge; //第2个变化沿传递数据 模式3 SPI_InitStructure.SPI_NSS=SPI_NSS_Soft; //NSS软件模式 SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;//时钟8分频 SPI 在APB2上,则为9M SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB; //高位在前 SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1,&SPI_InitStructure); SPI_Cmd(SPI1,ENABLE); //SPI发送数据 u8 SPI_SendData(u8 data){ while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET); SPI_I2S_SendData(SPI1,data); } //SPI接收数据 u8 SPI_ReadData(void){ while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)==RESET); SPI_I2S_SendData(SPI1, 0); while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)==RESET); return SPI_I2S_ReceiveData(SPI1); } //根据芯片SST25VF016B的数据手册,编写对该芯片操作的函数 //读取状态寄存器 u8 RDSR(void){ u8 data; SPI_CS_L; SPI_SendData(0x05); data = SPI_ReadData(); SPI_CS_H; return data; } //根据状态寄存器最低位BUSY判断芯片是否处于忙状态 u8 Is_Busy(void){ if((RDSR()&0x01)==1) return 1; //1 : 忙 else return 0; //0 :空闲 } void SST_SPI_Read(u32 addr,u8* buf,u16 size){ int i =0; SPI_CS_L; SPI_SendData(0x0B); SPI_SendData((addr&0xFFFFFF)>>16); //È¡24λµÄ¸ß8λ SPI_SendData((addr&0xFFFF)>>8); //È¡24λµÄÖÐ8λ SPI_SendData(addr&0xFF); //È¡24λµÄµÍ8λ SPI_SendData(0); while(i<size){ buf[i] = SPI_ReadData(); i++; } SPI_CS_H; } // void SST_SPI_WRITE(u32 addr,u8* buf,u16 size){ int i; SST_SPI_DEL4K(addr); //先清除要写的地址原先的数据 WRSR(); //使能写状态寄存器,打开写保护 WREN(); //使能写数据存储器 /*必须要先擦除目标地址的数据,再使能写状态寄存器,打开写保护 再使能写数据存储器,顺序不可调换*/ SPI_CS_L; SPI_SendData(0xAD); SPI_SendData((addr&0xFFFFFF)>>16); //先发送地址高8位 SPI_SendData((addr&0xFFFF)>>8); //发送地址中8位 SPI_SendData(addr&0xFF); //发送地址低8位 因为该芯片是一共24位 SPI_SendData(buf[0]); SPI_SendData(buf[1]); SPI_CS_H; i=2; while(i<size){ delay_us(10); SPI_CS_L; SPI_SendData(0xAD); SPI_SendData(buf[i++]); SPI_SendData(buf[i++]); SPI_CS_H; } delay_us(10); WRDI(); // while(Is_Busy()); } //4KB擦除 void SST_SPI_DEL4K(u32 addr){
WRSR(); //使能写状态寄存器,打开写保护 WREN(); //使能写数据存储器 /*上面2个函数的调用顺序不能调换*/
SPI_CS_L;
SPI_SendData(0x20);
SPI_SendData((addr&0xFFFFFF)>>16);
SPI_SendData((addr&0xFFFF)>>8);
SPI_SendData(addr&0xFF);
SPI_CS_H;
while(Is_Busy());
}
//写数据存储器使能
void WREN(void){
SPI_CS_L;
SPI_SendData(0x06);
SPI_CS_H;
}
//写数据存储器禁止
void WRDI(void){
SPI_CS_L;
SPI_SendData(0x04);
SPI_CS_H;
while(Is_Busy());
}
//使能写状态寄存器,打开写保护
void WRSR(void){
SPI_CS_L;
SPI_SendData(0x50);
SPI_CS_H;
SPI_CS_L;
SPI_SendData(0x01); // BP3 BP2 BP1 BP0 置0,取消写保护
SPI_SendData(0);
SPI_CS_H;
while(Is_Busy());
}
//读取器件ID,用于判断SPI通信是否成功
u16 RDID(void){
u16 id;
SPI_CS_L;
SPI_SendData(0x90);
SPI_SendData(0x00);
SPI_SendData(0x00);
SPI_SendData(0x00);
id = SPI_ReadData();
id<<=8;
id += SPI_ReadData();
SPI_CS_H;
return id;
}
//主函数
int main(void){
u8 readBuf[100];
u8 str[]="hello world 你好";
u16 size=sizeof(str)/sizeof(str[0]);
RCC_Configuration();
GPIO_Configuration();
USART_Configuration();
SPI_Configuration();
/*data = RDID();
printf("%X\r\n",data);*/
SST_SPI_WRITE(0,str,size); //写入hello world 你好
delay_ms(100);
SST_SPI_Read(0,readBuf,size);//读出hello world 你好
printf("%s\r\n",readBuf);
delay_ms(5000);
while(1){
}
}