• STM32-SPI读写外部FLASH(W25Q64)


    一、工具

      1、STM32F103VET6单片机;

      2、编译环境:TrueSTUDIO

      3、辅助工具:STM32CubeMX

    二、电路原理图

    三、单片机系统时钟配置

      1、时钟源选择:

       2、时钟树:

     四、SPI配置

      1、选用的是SPI1,全双工主机模式(单片机是主机,外部FLASH做从机),片选引脚由软件控制。

      2、再检查一下SPI1的引脚设置是否正确。

      3、设置FLASH的片选引脚即PC0引脚为输出模式,因为SPI1上只有一个器件,默认输出低电平。

    五、生成代码

      1、SPI1初始化代码如下所示:

    static void MX_SPI1_Init(void)
    {
    
      /* USER CODE BEGIN SPI1_Init 0 */
    
      /* USER CODE END SPI1_Init 0 */
    
      /* USER CODE BEGIN SPI1_Init 1 */
    
      /* USER CODE END SPI1_Init 1 */
      /* SPI1 parameter configuration*/
      hspi1.Instance = SPI1;
      hspi1.Init.Mode = SPI_MODE_MASTER;
      hspi1.Init.Direction = SPI_DIRECTION_2LINES;
      hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
      hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
      hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
      hspi1.Init.NSS = SPI_NSS_SOFT;
      hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
      hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
      hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
      hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
      hspi1.Init.CRCPolynomial = 10;
      if (HAL_SPI_Init(&hspi1) != HAL_OK)
      {
        Error_Handler();
      }
      /* USER CODE BEGIN SPI1_Init 2 */
    
      /* USER CODE END SPI1_Init 2 */
    
    }

      2、SPI1引脚及其相关的代码如下所示:

    /**
    * @brief SPI MSP Initialization
    * This function configures the hardware resources used in this example
    * @param hspi: SPI handle pointer
    * @retval None
    */
    void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(hspi->Instance==SPI1)
      {
      /* USER CODE BEGIN SPI1_MspInit 0 */
    
      /* USER CODE END SPI1_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_SPI1_CLK_ENABLE();
      
        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**SPI1 GPIO Configuration    
        PA5     ------> SPI1_SCK
        PA6     ------> SPI1_MISO
        PA7     ------> SPI1_MOSI 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        GPIO_InitStruct.Pin = GPIO_PIN_6;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
      /* USER CODE BEGIN SPI1_MspInit 1 */
    
      /* USER CODE END SPI1_MspInit 1 */
      }
    
    }
    
    /**
    * @brief SPI MSP De-Initialization
    * This function freeze the hardware resources used in this example
    * @param hspi: SPI handle pointer
    * @retval None
    */
    void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
    {
      if(hspi->Instance==SPI1)
      {
      /* USER CODE BEGIN SPI1_MspDeInit 0 */
    
      /* USER CODE END SPI1_MspDeInit 0 */
        /* Peripheral clock disable */
        __HAL_RCC_SPI1_CLK_DISABLE();
      
        /**SPI1 GPIO Configuration    
        PA5     ------> SPI1_SCK
        PA6     ------> SPI1_MISO
        PA7     ------> SPI1_MOSI 
        */
        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
    
      /* USER CODE BEGIN SPI1_MspDeInit 1 */
    
      /* USER CODE END SPI1_MspDeInit 1 */
      }
    
    }
    /**
      * @brief GPIO Initialization Function
      * @param None
      * @retval None
      */
    static void MX_GPIO_Init(void)
    {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
    
      /* GPIO Ports Clock Enable */
      __HAL_RCC_GPIOC_CLK_ENABLE();
      __HAL_RCC_GPIOA_CLK_ENABLE();
    
      /*Configure GPIO pin Output Level */
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
    
      /*Configure GPIO pin : PC0 */
      GPIO_InitStruct.Pin = GPIO_PIN_0;
      GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
      HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
    }

    六、编写与FLASH的相关代码

      1、读一个字节

    /**
      * @brief SPI1 读一个字节
      * @param None
      * @retval None
      */
    static uint8_t spi1_flash_read_byte(void)
    {
        uint8_t t_data, r_data;
    
        if(HAL_SPI_TransmitReceive(&hspi1, &t_data, &r_data, 1, 0xFFFFFF) != HAL_OK)
        {
            r_data = 0xff;
        }
        return r_data;
    }

      2、写一个字节

    /**
      * @brief SPI1 写一个字节
      * @param byte 写入的字节
      * @retval 写状态 0成功 1失败
      */
    static uint8_t spi1_flash_send_byte(uint8_t byte)
    {
        uint8_t r_data;
    
        if(HAL_SPI_TransmitReceive(&hspi1, &byte, &r_data, 1, 0xFFFFFF) != HAL_OK)
        {
          return 1;
        }
        return 0;
    }

      3、FLASH写使能

    /**
      * @brief FLASH 写使能
      * @param None
      * @retval None
      */
    static void spi1_flash_write_enable(void)
    {
        cs_low;
        spi1_flash_send_byte(0x06);
        cs_high;
    }

      4、FLASH等待写结束

    /**
      * @brief FLASH 等待写结束
      * @param None
      * @retval None
      */
    static void spi1_flash_wait_for_write_end(void)
    {
      uint8_t state = 0;
    
      cs_low;
    
      spi1_flash_send_byte(0x05);
    
      do
      {
          state = spi1_flash_read_byte();
      }
      while((state & 0x01) == SET);
    
      cs_high;
    }

      5、读FLASH的ID

    /**
      * @brief FLASH 读ID
      * @param None
      * @retval None
      */
    uint32_t spi_flash_read_ID(void)
    {
      uint32_t temp, temp0, temp1, temp2;
    
      cs_low;
    
      spi1_flash_send_byte(0x9F);
    
      temp0 = spi1_flash_read_byte();
      temp1 = spi1_flash_read_byte();
      temp2 = spi1_flash_read_byte();
    
      cs_high;
    
      temp = (temp0 << 16) | (temp1 << 8) | temp2;
    
      return temp;
    }

      6、读FLASH

    /**
      * @brief 读FLASH
      * @param addr 读flash的起始地址
      * @param pdata 读到的数据存放起始地址
      * pdata size 读数据大小
      * @retval None
      */
    void spi1_flash_read(uint32_t addr,uint8_t *pdata, uint16_t size)
    {
      cs_low;
    
      spi1_flash_send_byte(0x03);
    
      spi1_flash_send_byte((addr & 0xFF0000) >> 16);
      spi1_flash_send_byte((addr & 0xFF00) >> 8);
      spi1_flash_send_byte(addr  & 0xFF);
    
      while (size--)
      {
        *pdata = spi1_flash_read_byte();
        pdata++;
      }
    
      cs_high;
    }

      7、按页写FLASH

    /**
      * @brief 按页写FLASH
      * @param addr 写入flash的起始地址
      * @param pdata 写入数据的起始地址
      * pdata size 写数据大小
      * @retval None
      */
    void spi1_flash_page_write(uint32_t addr, uint8_t *pdata, uint16_t size)
    {
        uint16_t i;
    
        spi1_flash_write_enable();
    
        cs_low;
    
        spi1_flash_send_byte(0x02);
        spi1_flash_send_byte((uint8_t)((addr)>>16));
        spi1_flash_send_byte((uint8_t)((addr)>>8));
        spi1_flash_send_byte((uint8_t)addr);
    
        for(i = 0; i < size; i++)
        {
            spi1_flash_send_byte(pdata[i]);
        }
    
        cs_high;
        spi1_flash_wait_for_write_end();
    }

      8、写FLASH

    /**
      * @brief 写FLASH
      * @param addr 写入flash的起始地址
      * @param pdata 写入数据的起始地址
      * pdata size 写数据大小
      * @retval None
      */
    void spi1_flash_write(uint32_t addr, uint8_t *pdata, uint32_t size)
    {
        uint32_t page_remain;
    
        page_remain = 256 - addr%256;
    
        if(size <= page_remain)
        {
            page_remain = size;
        }
        while(1)
        {
            spi1_flash_page_write(addr, pdata, page_remain);
    
            if(size == page_remain)
                break;
             else
            {
                 pdata += page_remain;
                 addr += page_remain;
    
                 size -= page_remain;
                if(size > 256)
                    page_remain = 256;
                else
                    page_remain = size;
            }
        }
    }

      9、擦除FLASH扇区

    /**
      * @brief 擦除FLASH扇区
      * @param sector_addr 扇区的起始地址
      * @retval None
      */
    void spi1_flash_sector_erase(uint32_t sector_addr)
    {
        spi1_flash_write_enable();
        spi1_flash_wait_for_write_end();
    
        cs_low;
        spi1_flash_send_byte(0x20);
        spi1_flash_send_byte((sector_addr & 0xFF0000) >> 16);
        spi1_flash_send_byte((sector_addr & 0xFF00) >> 8);
        spi1_flash_send_byte(sector_addr & 0xFF);
    
        cs_high;
    
        spi1_flash_wait_for_write_end();
    }

      10、擦除FLASH块

    /**
      * @brief 擦除FLASH块
      * @param None
      * @retval None
      */
    void spi1_flash_block_erase(void)
    {
        spi1_flash_write_enable();
    
        cs_low;
        spi1_flash_send_byte(0xC7);
        cs_high;
    
        spi1_flash_wait_for_write_end();
    }

    #endif

  • 相关阅读:
    MVC4.0系统开发新手历程1
    hdu1205(类似 分布垃圾数列)
    python manage.py startapp app 时候报错No module named _sqlite3
    delphi 文件夹权限设置(执行一个小脚本的笨办法)
    FreeBSD 10安装KDE桌面环境简介(亲测bsdconfig命令有效)
    Qt的目录依赖问题----怎样生成一个绿色的Qt软件包
    Qt5位置相关函数异同详解(附源码)
    Qt 设置背景图片3种方法(三种方法:QPalette调色板,paintEvent,QSS)
    简单实现android和wp聊天
    进程间通讯之mmap文件共享
  • 原文地址:https://www.cnblogs.com/wenhao-Web/p/13827313.html
Copyright © 2020-2023  润新知