STM32G030F6P6读写flash失败问题(HAL)

STM32G030是F0系列的升级版,其在性能上比F0要好很多,具体G0参数如下:

        最开始做项目选用的单片机是STM32F030F4P6,但是在后期使用中发现,我的FLASH(16K)不够用了,就选择了STM32G030F6P6来进行项目使用,主要是价格便宜,资源够用。

        在F030使用的flash拿到G030上来发现不可使用,就进行了一些修改,但是这个时候就出现了报错,在进行flash擦除的时候报错

HAL_FLASHEx_Erase(&EraseInitStruct,&PageError);

通过发现擦除有问题,我就去查看其底层函数。

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError)
{
  HAL_StatusTypeDef status;
  uint32_t index;

  /* Check the parameters */
  assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));

  /* Process Locked */
  __HAL_LOCK(&pFlash);

  /* Reset error code */
  pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;

  /* Wait for last operation to be completed */
  status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

  if (status == HAL_OK)
  {
#if !defined(FLASH_DBANK_SUPPORT)
    /* For single bank product force Banks to Bank 1 */
    pEraseInit->Banks = FLASH_BANK_1;
#endif /* FLASH_DBANK_SUPPORT */

    if (pEraseInit->TypeErase == FLASH_TYPEERASE_MASS)
    {
      /* Proceed to Mass Erase */
      FLASH_MassErase(pEraseInit->Banks);

      /* Wait for last operation to be completed */
      status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);
    }
    else
    {
      /*Initialization of PageError variable*/
      *PageError = 0xFFFFFFFFU;

      for (index = pEraseInit->Page; index < (pEraseInit->Page + pEraseInit->NbPages); index++)
      {
        /* Start erase page */
        FLASH_PageErase(pEraseInit->Banks, index);

        /* Wait for last operation to be completed */
        status = FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);

        if (status != HAL_OK)
        {
          /* In case of error, stop erase procedure and return the faulty address */
          *PageError = index;
          break;
        }
      }

      /* If operation is completed or interrupted, disable the Page Erase Bit */
      CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
    }
  }

  /* Process Unlocked */
  __HAL_UNLOCK(&pFlash);

  /* return status */
  return status;
}

        其大致意思就是两种擦除方式,片擦除以及全部擦除。然后发现其status是在  FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE);   里面进行报错,其函数实现如下:

HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout)
{
  uint32_t error;
  /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
     Even if the FLASH operation fails, the BUSY flag will be reset and an error
     flag will be set */
  uint32_t timeout = HAL_GetTick() + Timeout;

  /* Wait if any operation is ongoing */
#if defined(FLASH_DBANK_SUPPORT)
  error = (FLASH_SR_BSY1 | FLASH_SR_BSY2);
#else
  error = FLASH_SR_BSY1;
#endif /* FLASH_DBANK_SUPPORT */

  while ((FLASH->SR & error) != 0x00U)
  {
    if (HAL_GetTick() >= timeout)
    {
      return HAL_TIMEOUT;
    }
  }

  /* check flash errors */
  error = (FLASH->SR & FLASH_SR_ERRORS);

  /* Clear SR register */
  FLASH->SR = FLASH_SR_CLEAR;

  if (error != 0x00U)
  {
    /*Save the error code*/
    pFlash.ErrorCode = error;
    return HAL_ERROR;
  }

  /* Wait for control register to be written */
  timeout = HAL_GetTick() + Timeout;

  while ((FLASH->SR & FLASH_SR_CFGBSY) != 0x00U)
  {
    if (HAL_GetTick() >= timeout)
    {
      return HAL_TIMEOUT;
    }
  }

  return HAL_OK;
}

发现其在这里进行报错,然后从下面返回错误码上来。

返回我打印了这个error到底是多少,发现其值为0x80,发现报的错误是   FLASH_SR_PGSERR

然后查看数据手册以及使用手册,发现这个是编程错误。然后继续查找问题,发现G030的一个bank是2K,修改之后发现还是报这个错误。

        在详细查看数据手册后,发现G030进行Flash读写是uint64_t进行读写的,如下:

        在此情况下,对读写函数进行修改,将数据等改为uint64_t。在将这些修改过后,发现问题没有在flash擦除那里进行报错,而是在FLASH写入那里卡死。

HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST,addr,Data[i])!=HAL_OK

上述函数错误的地方是        FLASH_TYPEPROGRAM_FAST   ,因为其意思是32位写

Fast program a 32 row double-word (64-bit) at a specified address

但是手册给出是64位写,所以这里进行了报错,然后将这里改成下面函数,整个程序的读写就没有问题了,在此问题就得到了解决。

HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,Data[i])!=HAL_OK

以下则是G030F6P6单片机的FLASH的程序

读:

/*******************************************************************************
* Function Name  : 读取Flash数据
* Description    : Read packed message form flash
* Input          : buff:point to first location of received buffer.length:Maxmum length of reception
* Output         : 
* Return         : reception length
*******************************************************************************/
uint16_t Read_Flash( uint64_t *Data, uint16_t num, uint32_t addr)
{
    uint16_t i=0;
	uint32_t add=0;
	if(num == 0)
	{
		return 0;
	}
	add = addr;
	i=0;
	while((add < FLASH_USER_END_ADDR1) && (i<num))
	{
		Data[i++] = *(__IO uint64_t *)add;
		add = add+8;
	}
	return i;
}

写:

/*******************************************************************************
* Function Name  : Flash写数据
* Description    : Write a group of datas to flash.
* Input          : buff:pointer of first data, length: write length
* Output         : 
* Return         : true/false
*******************************************************************************/
uint16_t Write_Flash( uint64_t *Data , uint16_t num, uint32_t add)
{
	uint16_t i=0;
	uint32_t addr=0;
    FLASH_EraseInitTypeDef EraseInitStruct={0};
    uint32_t PageError=0;//擦除错误地址
    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;//仅擦除页
    EraseInitStruct.Banks = FLASH_BANK_1;
    EraseInitStruct.Page = 15;		//注:该page为0-15页
    EraseInitStruct.NbPages = 1;	//擦除一页
    
	HAL_FLASH_Unlock();
    HAL_FLASHEx_Erase(&EraseInitStruct,&PageError);
	if(PageError != 0xFFFFFFFF) {
        return 1;
    }
	addr = add;
	i=0;
	while((addr < FLASH_USER_END_ADDR1) && (i<num))
	{
		if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,addr,Data[i])!=HAL_OK)
		{
			addr = addr + 8;
		}else{
			i++;
		}
	}
	HAL_FLASH_Lock();
    return 0;    
}

        在这里使用的是uint64_t进行数据的读写,但是如果用在其他程序,就会出现error。因为单片机是32位的出现uint64_t参与的函数就报错。

        在这里我写了一个简易的flash内存管理,因为我们在写flash数据的时候,往往很多数据,并不是单一数据,但是每写一次flash则需要进行flash擦除,我这里采用一个数组进行使用,如下:

实现原理:一次性读取一定数量的数据出来,将自己需要的数据修改过后,再讲修改后的数据全部写入,在实际项目中还是比较实用的。

读:

/*
*********************************************************************************************************
*	函 数 名:uint8_t data_read(uint16_t *data,uint16_t datalen,uint16_t save_addr,uint32_t bank_addr)
*	功能说明:数据读取
*	形    参:data 数据   datalen 长度   save_addr数组中保存的地址    bank_addr  地址
*	返 回 值:
*********************************************************************************************************
*/ 
uint8_t data_read(uint16_t *data,uint16_t datalen,uint16_t save_addr,uint32_t bank_addr) 
{
	uint64_t buf[READ_NUM]={0}; //根据实际数据量进行设置
 
	if(datalen == 0)
	{
		return 1;
	}
	Read_Flash(buf,READ_NUM,bank_addr);  //读数据
	
	for(int i=0;i<datalen;i++)   //数据更新
    {
		data[i] = buf[save_addr+i];
    }
	return 0;
}

写:

/*
*********************************************************************************************************
*	函 数 名:uint8_t data_save(uint16_t *data,uint16_t datalen,uint16_t save_addr,uint32_t bank_addr) 
*	功能说明:数据保存
*	形    参:data 数据   datalen 长度   save_addr数组中保存的地址    bank_addr  地址
*	返 回 值:
*********************************************************************************************************
*/ 
uint8_t data_save(uint16_t *data,uint16_t datalen,uint16_t save_addr,uint32_t bank_addr) 
{
	uint64_t buf[READ_NUM]={0}; //根据实际数据量进行设置
	if(datalen == 0)
	{
		return 1;
	}
	Read_Flash(buf,READ_NUM,bank_addr);  //读数据
	if(buf[0]==0xffffffff)
	{
		for(int i=0;i<READ_NUM;i++)
        {
			buf[i] = 1;
        }
		Write_Flash(buf,READ_NUM,bank_addr);  //写数据
	}
	for(int i=0;i<datalen;i++)   //数据更新
    {
		buf[save_addr+i] = data[i];
    }
	
	Write_Flash(buf,READ_NUM,bank_addr);  //写数据
	return 0;
}

其他宏定义相关代码:

//数组大小
#define READ_NUM	30
//地址
#define ADDR_FLASH_PAGE_0  	((uint32_t)0x08000000)   //第一页
#define ADDR_FLASH_PAGE(n) (ADDR_FLASH_PAGE_0 + (uint32_t)(n)*FLASH_PAGE_SIZE)

#define FLASH_USER_PAGE_NUM		1
#define FLASH_USER_START_ADDR1  ADDR_FLASH_PAGE(16-1)
#define FLASH_USER_END_ADDR1    (FLASH_USER_START_ADDR1 + FLASH_USER_PAGE_NUM*FLASH_PAGE_SIZE)

基本上可以实现功能:主要的问题就是那个必须64位读写,不然数据就有问题

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要连接STM32G030F6P6微控制器和DRV2605L驱动,您需要按照以下步骤进行连接: 1. 将STM32G030F6P6的引脚与DRV2605L进行连接。以下是基本的连接示意图: ``` STM32G030F6P6 DRV2605L ------------------------------------ PB6 (I2C1_SCL) SCL PB7 (I2C1_SDA) SDA GND GND 3.3V VCC ``` 请注意,PB6和PB7引脚是STM32G030F6P6上的I2C1引脚,用于与DRV2605L进行I2C通信。确保连接正确,以避免引脚冲突。 2. 确保正确配置STM32G030F6P6的GPIO和I2C外设。您可以使用STM32CubeIDE来轻松配置这些设置。确保正确设置I2C外设并选择正确的引脚。 3. 初始化STM32G030F6P6的I2C外设并与DRV2605L进行通信。您可以使用STM32 HAL库提供的函数来初始化和操作I2C外设。在上面的示例代码中,我们使用了`hi2c1`作为I2C1的句柄,并将其传递给DRV2605L库函数。 4. 使用DRV2605L库函数来配置和控制DRV2605L驱动器。在示例代码中,我们使用了`drv2605l_init()`函数来初始化DRV2605L,然后使用`drv2605l_set_mode()`和`drv2605l_set_pwm_amplitude()`函数来设置驱动器的模式和PWM幅度。您可以根据DRV2605L的数据手册和您的具体需求来使用其他功能。 请注意,以上步骤仅提供了一个基本的连接和初始化示例。具体的连接和配置可能因您的硬件和项目要求而有所不同。确保参考STM32G030F6P6和DRV2605L的数据手册以获取更详细的信息,并根据您的具体情况进行适当的修改。 希望能对您有所帮助!如果您有任何其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啵啵520520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值