STM32——SPI通信实验

程序配置过程:

1、使能SPIx和IO口时钟:

RCC_AHBxPeriphClockCmd()/RCC_APBxPeriphClockCmd();

2、初始化IO口为复用功能:

void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct);

3、设置引脚复用映射:

GPIO_PinAFConfig();

4、初始化SPIx,设置SPIx工作模式:

void SPI_Init(SPI_TypeDef* SPIx,SPI_InitTypeDef* SPI_InitStruct);

5、使能SPIx:

void SPI_Cmd(SPI_TypeDef* SPIx,FunctionalState NewState);

6、SPI传输数据:

void SPI_I2C_SendData(SPI_TypeDef* SPIx,uint16_t Data);

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);

7、查看SPI传输数据:

SPI_I2C_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE);

使能SPIx和IO口时钟

void SPI_Init(void)
{
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
}

初始化IO口为复用功能

void SPI_Init(void)
{
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
  GPIO_InitTypeDef GPIO_InitABC;
  GPIO_InitABC.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
  GPIO_InitABC.GPIO_Mode=GPIO_Mode_AF;//复用功能
  GPIO_InitABC.GPIO_OType=GPIO_OType_PP;//推挽输出
  GPIO_InitABC.GPIO_Speed=GPIO_Speed_100MHz;//100MHz
  GPIO_InitABC.GPIO_PuPd=GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOB,&GPIO_InitABC);
}

设置引脚复用映射

void SPI_Init(void)
{
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);//PB3复用为SPI1
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);//PB4复用为SPI1
  GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);//PB5复用为SPI1
  
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
  RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
}

 初始化SPIx,设置SPIx工作模式

void SPI_Init(void)
{
  SPI_InitTypeDef SPI_InitABC;
  SPI_InitABC.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//设置SPI单向或者双向的数据模式
  SPI_InitABC.SPI_Mode=SPI_Mode_Master;//设置SPI工作模式:设置为主SPI
  SPI_InitABC.SPI_DataSize=SPI_DataSize_8b;//设置SPI的数据大小:SPI发送接收8为帧结构
  SPI_InitABC.SPI_CPOL=SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
  SPI_InitABC.SPI_CPHA=SPI_CPHA_2Edge;//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
  SPI_InitABC.SPI_NSS=SPI_NSS_Soft;//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理
  SPI_InitABC.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;//定义波特率预分频的值
  SPI_InitABC.SPI_FirstBit=SPI_FirstBit_MSB;//指定数据传输从MSB位开始
  SPI_InitABC.SPI_CRCPolynomial=7;//CRC值计算的多项式
  SPI_Init(SPI1,&SPI_InitABC);
}

使能SPIx

void SPI_Init(void)
{
SPI_Cmd(SPI1,ENABLE);
SPI1_ReadWriteByte(0xff);
}
//设置SPI1速度
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATEPRESCALER(SPI_BauudRatePrescaler));//判断有效性
  SPI1->CR1&=0XFFC7;//位3~5清零,用来设置波特率
  SPI1->CR1|=SPI_BaudRatePrescaler;//设置SPI1速度
  SPI_Cmd(SPI1,ENABLE);//使能SPI1
}

SPI传输数据 

u8 SPI1_ReadEriteByte(u8 TxData)
{
  while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)==RESET){}//等待发送区空
  SPI_I2S_SendData(SPI1,TxData);//通过外设SPIx发送一个byte数据
  while(SPI_I2S_GetFlagStatus(SPI1_I2S_FLAG_RXNE)==RESET){}//等待接收完一个byte
  return SPI_I2S_ReceiveData(SPI1);//返回通过SPIx最近接收的数据
}

程序思路:

写一个地址之前需要先判断是不是被擦除了(比如擦除之后的值为100,则需要将内容与100相比较从而判断是否擦除了),若未擦除则需要擦除,FLASH的最小单位是4K。

W25QXX_Write函数思路:每个sector是4K(等于4096个地址),在写任何一个地址前,如果该地址的值不是0xFF,必须先擦除对应的sector然后再写。

擦除的原理:将整个sector读出来放到一个4k的Buffer中,然后将sector擦除,在Buffer中更新数据,然后将Buffer中的数据写到sector中。(写的数据未超越一个sector)如果写的数据超过一个sector,处理方式相同。

相关代码:

void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
  u32 secpos;
  u16 secoff;
  u16 i;
  u16 secremain;
  u8* W25QXX_BUF;
  W25QXX_BUF=W25QXX_BUFFER;
  secpos=WriteAddr/4096;//确定扇区地址(也就是sector的地址)
  secoff=WriteAddr%4096;//判断在扇区的偏移(判断sector中数据的位置,是否跨越一个sector)
  secremain=4096-secoff;//扇区剩余空间(sector剩余空间)
  if(NmByteToWrite<=secremain) secremain=NumByteToWrite;//如果写入的数据小于剩余的空间,就把secremain设置为写入的数据大小,这样只要写到了secremain就会终止。
  while(1)//先把扇区的数据读取出来
  {
    W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
    for(i=1;i<secremain;i++)//校验数据,是否擦除
    {
      if(W25QXX_BUF[secoff+i]!=oXFF) break;//需要擦除
    } 
    if(i<Secremain)//
    {
      W25QXX_Erase_Sector(secpos);//擦除扇区
      for(i=0;i<secremain;i++)
      {
        W25QXX_BUF[i+secoff]=pBuffer[i];
      }
      W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区
    }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间。
    if(NumByteToWrite==secremain) break;//写入结束了
    else
    {
      secpos++;//扇区地址递增1
      secoff=0;//偏移位置为0
      pBuffer+=secremain;//指针偏移
      WriteAddr+=secremain;//写地址偏移
      NumByteToWrite-=secremain;//字节数递减
      if(NumByteToWrite>4096) secremain=4096;//下一个扇区还是没写完
      else secremain=NumByteToWrite;//下一个扇区可以写完了
    }
  }
}
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值