STM32的SPI学习(SPI芯片为SST25VF016B)

来源:互联网 发布:淘宝网抢红包 编辑:程序博客网 时间:2024/06/05 10:54

学习板芯片为STM32F103VE

先简单介绍下SPI(全称:Serial Peripheral interface,串行外围设备接口):SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,是一种高速的,全双工,同步的通信总线。一般SPI芯片与MCU通信,只会使用4个管脚:MOSI(主输出),MISO(主输入),CS(片选低电平有效),SCK(时钟)



从图中可以看出,
主机和从机都有一个串行移位寄存器,主机通过向它的串行寄存器写入一个字节来发起一次传输。
寄存器通过MOSI信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过MISO信号线返回给主机。
这样,两个移位寄存器中的内容就被交换。
外设的写操作和读操作是同步完成的。
如果只进行写操作,主机只需忽略接收到的字节;
反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。


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:CSGPIO_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个变化沿传递数据      模式3SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;                         //NSS软件模式SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;//时钟8分频        SPI 在APB2上,则为9MSPI_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){
    }
}







原创粉丝点击