基于STM32CubeMx的FLASH和SD卡文件读取

基于STM32CubeMx的FLASH和SD卡文件读取

实验任务:
(一)Flash地址空间的数据读取。stm32f103c8t6只有20KB 内存(RAM)供程序代码和数组变量存放,因此,针对内部Flash的总计64KB存储空间(地址从0x08000000开始),运行一次写入8KB数据,总计复位运行代码8次,将64KB数据写入Flash,并验证写入数据的正确性和读写速率。此外,继续往后续地址写入数据,检验stm32f103c8t6 实际FlashROM是否超过64KB。
(二)掌握SD卡协议原理,用STM32F103完成对SD卡的数据读取(fat文件模式)。

(一)Flash原理

  不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了 1024K 字节。市面上 STM32F1 开发板使用的芯片是 STM32F103系列,其 FLASH 容量一般为 512K 字节,属于大容量芯片。

Flash的编程原理都是只能将1写为0,而不能将0写为1,所以在进行Flash编程前,必须将对应的块擦除,即将该块的每一位都变为1,块内所有字节变为0xFF。

  STM32F1 的闪存(Flash)模块:主存储器、信息块、闪存存储器接口寄存器。

  • 主存储器:该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。
  • 信息块:该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 BOOT0 接 V3.3, BOOT1 接 GND 的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能。
  • 闪存存储器接口寄存器:该部分用于控制闪存读写等,是整个闪存模块的控制机构。对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;编程与擦除的高电压由内部产生。
      在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。

(二)SD卡协议原理

1.SD卡简述

  很多单片机系统都需要大容量存储设备,以存储数据。目前常用的有 U 盘,FLASH 芯片,SD 卡等。他们各有优点,综合比较,最适合单片机系统的莫过于 SD 卡了,它不仅容量可以做到很大(32GB 以上),支持 SPI/SDIO 驱动,而且有多种体积的尺寸可供选择(标准的 SD 卡尺寸,以及 TF 卡尺寸等),能满足不同应用的要求。
  只需要少数几个 IO 口即可外扩一个高达 32GB 以上的外部存储器,容量从几十 M 到几十G 选择尺度很大,更换也很方便,编程也简单,是单片机大容量外部存储器的首选。

2.SD卡物理结构

 &emsp一般SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器 5个部分。
在这里插入图片描述

  • 存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
  • 电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和 存储单元接口复位;
  • 卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;
  • 接口驱动器控制SD卡引脚的输入输出。

3.SD卡寄存器

 &emspSD卡总共有8个寄存器,用于设定或表示SD卡信息。这些寄存器只能通过对应的命令访问,SDIO定义64个命令,每个命令都有特殊意义,可以实现某一特定功能,SD卡接收到命令后,根据命令要求对SD卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
以下是使用Markdown格式编写的表格:

名称 bit宽度 描述
CID 128 卡识别号(Card Identification number):用来识别的卡的个体号码(唯一的)
RCA 16 相对地址(Relative card address):卡的本地系统地址,初始化时,动态地由卡建议,主机核准
DSR 16 驱动级寄存器(Driver Stage Register):配置卡的输出驱动
CSD 128 卡的特定数据(Card Specific Data):卡的操作条件信息
SCR 64 SD配置寄存器(CD Configuration Register):SD卡特殊特性信息
OCR 32 操作条件寄存器(Operation conditings register)
SSR 512 SD状态(SD Status):SD卡专有特征的信息
CSR 32 卡状态(Card Status):卡状态信息

4.SD卡下SPI操作模式

4.1SD卡初始化

  • SPI操作模式下:在SD卡收到复位命令时,CS为有效电平(低电平),则SPI模式被启用,在发送CMD之前要先发送74个时钟,64个为内部供电上升时间,10个用于SD卡同步;才能开始CMD操作,在初始化时CLK时钟不能超过400KHz。
  1. 初始化与SD卡连接的硬件条件(MCU的SPI配置,IO口配置);
  2. 上电延时(>74个CLK);
  3. 复位卡(CMD0),进入IDLE状态;
  4. 发送CMD8,检查是否支持2.0协议;
  5. 根据不同协议检查SD卡(命令包括:CMD55、CMD41、CMD58和CMD1等);
  6. 取消片选,发多8个CLK,结束初始化。

这样我们就完成了对SD卡的初始化,注意末尾发送的8个CLK是提供SD卡额外的时钟,完成某些操作。通过SD卡初始化,我们可以知道SD卡的类型(V1、V2、V2HC或者MMC),在完成了初始化之后,就可以开始读写数据了。

4.2SD卡读取与写入

SD卡读取

  1. 发送CMD17;
  2. 接收卡响应R1;
  3. 接收数据起始令牌0XFE;
  4. 接收数据;
  5. 接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。
  6. 禁止片选之后,发多8个CLK;

以上就是一个典型的读取SD卡数据过程,SD卡的写与读数据差不多,写数据通过CMD24来实现,具体过程如下:
SD卡写入

  1. 发送CMD24;
  2. 接收卡响应R1;
  3. 发送写数据起始令牌0XFE;
  4. 发送数据;
  5. 发送2字节的伪CRC;
  6. 禁止片选之后,发多8个CLK;

(三)HAL库配置

1.基本配置

(1)打开STM32CubeMX,在主界面点击:ACCESS TO MCU SELECTOR:
在这里插入图片描述

(2)选择单片机型号以及点击开始工程项目:

在这里插入图片描述
(3)点击system core,进入SYS,在debug下选择serial wire:
在这里插入图片描述
(4)配置时钟,进入上面的RCC,有两个时钟,一个是HSEe和LSE,将HSE那里设为Crystal/Ceramic Resonator:
在这里插入图片描述

(5)主要介绍整套配置过程,可以配置完后在生成工程与代码。
进入Project Manager(工程管理),进行工程设置点击生成工程与代码:
在这里插入图片描述
在这里插入图片描述

2.FLASH

  • 管脚配置,对应外设c8t6本身设计好的PC13 LED灯
    在这里插入图片描述

对应的GPIO设置
在这里插入图片描述

  • 设置堆栈大小
    在这里插入图片描述
  • 进入CLK Configuration (时钟配置)中,进行时钟配置:
    开发板的外部晶振为8MHz,我们填入8;通道选择LSE;PLLM选择为/1;倍频系数N选择为x9;系统时钟选择PLLCLK;系统时钟设定为72Mz;APB1分频系数选择为/2即PCLK1位36MHz;APB2分频系数选择为/1即PCLK2位72MHz。

图 10‑14 如何在工程中添加文件

3.读取SD卡

  • 点击Middleware,选择FATFS模式。

在这里插入图片描述

  • 配置PA4GPIO_Output模式。
    在这里插入图片描述
  • 点击Connectivity,配置SPI1Full-Duplex Master模式。
    在这里插入图片描述
  • 配置USART1为异步模式
    在这里插入图片描述
  • 修改最小栈容量为0x1400,否则会导致调试时死机。
    在这里插入图片描述

(四) keil设置

1.FLASH数据读取

1.1添加文件

flash.h

#ifndef _flash_H_
#define _flash_H_


#include "stm32f1xx_hal.h"


/*********************************************************************/
//                        变量定义
/*********************************************************************/
//-- 用途划分
// 0x0800FC00-0x0800FFFF -- 使用最后4k 个字节用来存放电机信息
#define FMC_FLASH_BASE      0x08000000 	// FLASH的起始地址
#define APP_MAX_SIZE        0x00010000  // 


#define FMC_FLASH_END        0x08010000  // FLASH的结束地址 256
#define DEVICE_INFO_ADDRESS  0x0800C000  //(STM32_FLASH_END - DEVICE_INFO_SIZE)   // 设备信息起始地址
#define DEVICE_LOG_ADDRESS   0x0800E000  //(STM32_FLASH_END - 2*DEVICE_INFO_SIZE) // 设备日志起始地址



#define FMC_FLASH_SIZE 64			          // 定义Flash大小,单位KB


#if FMC_FLASH_SIZE < 256
#define FMC_SECTOR_SIZE 1024            // 字节
#define MOD_SECTOR_SIZE 0X3FF
#define PAGE_COUNT_BY16 512
#else
#define FMC_SECTOR_SIZE	2048
#define MOD_SECTOR_SIZE 0X7FF
#define PAGE_COUNT_BY16 1024
#endif





/*********************************************************************/
//                        函数声明
/*********************************************************************/
void FlashWriteBuff( const uint32_t destination_address, uint8_t *const buffer,uint32_t length );
void FlashReadBuff(const uint32_t source_address,uint8_t *const buffer,uint16_t length);




#endif

flash.c

#include "flash.h"


// 不检查的写入
// WriteAddr:起始地址
// pBuffer:  数据指针
// NumToWrite:字节数数
void FlashWriteNoCheck( uint32_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite )
{
   
    uint16_t i;

    for( i=0; i<NumToWrite; i+=4 )
    {
   
        while( HAL_OK != HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, WriteAddr+i,*(uint32_t *)(pBuffer+i) ) );
    }

}

extern void FLASH_PageErase(uint32_t PageAddress);
void FlashWriteBuff( const uint32_t destination_address, uint8_t *const buffer,uint32_t length )
{
   
    uint16_t i;
    uint8_t FlashBuff[FMC_SECTOR_SIZE];
    uint32_t StartAddress = destination_address - destination_address%FMC_SECTOR_SIZE;
    uint16_t Offset = destination_address - StartAddress;
    uint8_t *pBuf = buffer;
    uint32_t remaindNum = length;

    HAL_StatusTypeDef status = HAL_ERROR;

    // 地址检查
    if( (destination_address < FMC_FLASH_BASE) || ( destination_address + length >= FMC_FLASH_END) || (length <= 0) )
        return;

    HAL_FLASH_Unlock();	// 解锁
    do
    {
   
        // 读出一页数据
        for(i=0; i < FMC_SECTOR_SIZE; i += 4 )
            *(uint32_t *)(FlashBuff+i) = *(uint32_t *)(StartAddress+i);

        // 修改要改入的数据
        for ( i=0; (i+Offset)<FMC_SECTOR_SIZE && i< remaindNum; i++ )
            *(FlashBuff+Offset+i) = *(pBuf+i);


        // 擦除一ROW数据
        FLASH_PageErase( StartAddress );

        // HAL库 FLASH_PageErase有BUFF,要加上下面三行代码
        while( status != HAL_OK )
            status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
        CLEAR_BIT(FLASH->CR, FLASH_CR_PER);

        // 写入数据
        FlashWriteNoCheck( StartAddress,FlashBuff,FMC_SECTOR_SIZE );

        // 为下一页做准备
        StartAddress +=  FMC_SECTOR_SIZE;
        remaindNum -= i;
        pBuf += i;
        Offset = 0;

    } while( remaindNum > 0 );

    HAL_FLASH_Lock();  // 上锁
		
}

// 从FLASH中读指定长度数据
void FlashReadBuff(const uint32_t source_address,uint8_t *const buffer,uint16_t length)
{
   
    uint16_t i;
    uint8_t Offset = 0;
    uint32_t StartAddress = source_address;
    uint16_t data;

    // 地址检测
    if( source_address + length > FMC_FLASH_END ) return;

    // 如果没有对16齐
    if( source_address & 1 )
    {
   
        Offset = 1;
        StartAddress = source_address-1;
    }

    // flash的操作要求16对齐 最小读写操作16个比特
    if ( Offset )
    {
   
        data = *(uint16_t *)(StartAddress);
        buffer[0] = data >> 8;
        StartAddress += 2;
    }

    for ( i = 0; i < (length-Offset); i += 2)
    {
   
        data = *(uint16_t *)(StartAddress+i);
        buffer[i+Offset] = (data & 0xFF);
        if ( (i+Offset) < (length - 1) )
            buffer[i + Offset + 1] = (data >> 8);
    }

}

1.2主函数

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "flash.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t FlashWBuff [255];
uint8_t FlashRBuff [255];
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
   

  /* USER CODE BEGIN 1 */
	uint8_t i;
	uint8_t FlashTest[] ="Hello World hello pupu";
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
	FlashWriteBuff(DEVICE_INFO_ADDRESS, FlashTest, sizeof(FlashTest));//写入数据到Flash
	
	for(i=0;i<255;i++)
	FlashWBuff[i]=i;
	FlashWriteBuff(DEVICE_INFO_ADDRESS + sizeof(FlashTest), FlashWBuff, 255);//
	FlashReadBuff(DEVICE_INFO_ADDRESS + sizeof(FlashTest), FlashRBuff, 255);
	
	  
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
   
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
   
  RCC_OscInitTypeDef RCC_OscInitStruct = {
   0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {
   0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
   
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
   
    Error_Handler();
  }
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
   
  GPIO_InitTypeDef GPIO_InitStruct = {
   0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值