STM8做IAP(Bootloader)时在RAM中执行Flash块擦写函数问题

1、STM8的外设库驱动提供了很多代码,要求不高的话直接用库驱动即可

2、Flash块擦写速度快,但是必须要把函数放到RAM中执行(因为MCU的是NorFlash,普通的函数都是直接在Flash上执行的)

官方库如下

/**
  * @brief  Erases a block in the program or data memory.
  * @note   This function should be executed from RAM.
  * @param  FLASH_MemType :  The type of memory to erase
  * @param  BlockNum : Indicates the block number to erase
  * @retval None.
  */
IN_RAM(void FLASH_EraseBlock(uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType))
{
  uint32_t startaddress = 0;
  
#if defined(STM8S105) || defined(STM8S005) || defined(STM8S103) || defined(STM8S003) || \
    defined(STM8S001) || defined(STM8S903) || defined (STM8AF626x) || defined (STM8AF622x)
    uint32_t PointerAttr  *pwFlash;
#elif defined (STM8S208) || defined(STM8S207) || defined(STM8S007) || defined (STM8AF62Ax) || defined (STM8AF52Ax) 
  uint8_t PointerAttr  *pwFlash;
#endif
  
  /* Check parameters */
  assert_param(IS_MEMORY_TYPE_OK(FLASH_MemType));
  if(FLASH_MemType == FLASH_MEMTYPE_PROG)
  {
    assert_param(IS_FLASH_PROG_BLOCK_NUMBER_OK(BlockNum));
    startaddress = FLASH_PROG_START_PHYSICAL_ADDRESS;
  }
  else
  {
    assert_param(IS_FLASH_DATA_BLOCK_NUMBER_OK(BlockNum));
    startaddress = FLASH_DATA_START_PHYSICAL_ADDRESS;
  }
  
  /* Point to the first block address */
#if defined (STM8S208) || defined(STM8S207) || defined(STM8S007) || defined (STM8AF62Ax) || defined (STM8AF52Ax)
  pwFlash = (PointerAttr uint8_t *)(MemoryAddressCast)(startaddress + ((uint32_t)BlockNum * FLASH_BLOCK_SIZE));
#elif defined(STM8S105) || defined(STM8S005) || defined(STM8S103) || defined(STM8S003) || \
      defined(STM8S001) || defined (STM8S903) || defined (STM8AF626x) || defined (STM8AF622x)
    pwFlash = (PointerAttr uint32_t *)(MemoryAddressCast)(startaddress + ((uint32_t)BlockNum * FLASH_BLOCK_SIZE));
#endif	/* STM8S208, STM8S207 */
  
  /* Enable erase block mode */
  FLASH->CR2 |= FLASH_CR2_ERASE;
  FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NERASE);
  
#if defined(STM8S105) || defined(STM8S005) || defined(STM8S103) || defined(STM8S003) ||  \
    defined(STM8S001) || defined(STM8S903) || defined (STM8AF626x) || defined (STM8AF622x)
    *pwFlash = (uint32_t)0;
#elif defined (STM8S208) || defined(STM8S207) || defined(STM8S007) || defined (STM8AF62Ax) || \
  defined (STM8AF52Ax)
    *pwFlash = (uint8_t)0;
  *(pwFlash + 1) = (uint8_t)0;
  *(pwFlash + 2) = (uint8_t)0;
  *(pwFlash + 3) = (uint8_t)0;    
#endif
}

/**
  * @brief  Programs a memory block
  * @note   This function should be executed from RAM.
  * @param  FLASH_MemType : The type of memory to program
  * @param  BlockNum : The block number
  * @param  FLASH_ProgMode : The programming mode.
  * @param  Buffer : Pointer to buffer containing source data.
  * @retval None.
  */
IN_RAM(void FLASH_ProgramBlock(uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType, 
                        FLASH_ProgramMode_TypeDef FLASH_ProgMode, uint8_t *Buffer))
{
  uint16_t Count = 0;
  uint32_t startaddress = 0;
  
  /* Check parameters */
  assert_param(IS_MEMORY_TYPE_OK(FLASH_MemType));
  assert_param(IS_FLASH_PROGRAM_MODE_OK(FLASH_ProgMode));
  if(FLASH_MemType == FLASH_MEMTYPE_PROG)
  {
    assert_param(IS_FLASH_PROG_BLOCK_NUMBER_OK(BlockNum));
    startaddress = FLASH_PROG_START_PHYSICAL_ADDRESS;
  }
  else
  {
    assert_param(IS_FLASH_DATA_BLOCK_NUMBER_OK(BlockNum));
    startaddress = FLASH_DATA_START_PHYSICAL_ADDRESS;
  }
  
  /* Point to the first block address */
  startaddress = startaddress + ((uint32_t)BlockNum * FLASH_BLOCK_SIZE);
  
  /* Selection of Standard or Fast programming mode */
  if(FLASH_ProgMode == FLASH_PROGRAMMODE_STANDARD)
  {
    /* Standard programming mode */ /*No need in standard mode */
    FLASH->CR2 |= FLASH_CR2_PRG;
    FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NPRG);
  }
  else
  {
    /* Fast programming mode */
    FLASH->CR2 |= FLASH_CR2_FPRG;
    FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NFPRG);
  }
  
  /* Copy data bytes from RAM to FLASH memory */
  for(Count = 0; Count < FLASH_BLOCK_SIZE; Count++)
  {
    *((PointerAttr uint8_t*) (MemoryAddressCast)startaddress + Count) = ((uint8_t)(Buffer[Count]));
  }
}

默认IN_RAM是不打开,需要#define RAM_EXECUTION  (1)

这样就可以用库里的块擦写函数了

3、分析一下库的代码,发现

擦除和写入命令差不多:首先操作FLASH->CR2和FLASH->NCR2,然后在相应的Flash上写数据,擦除写4个0即可

于是自己重写一下驱动

typedef byte (* flashProgramCodeInRamFun)(byte cr2, byte ncr2, word BlockNum, byte length, byte *Buffer);

__ramfunc byte LaunchFlashCommand(byte cr2, byte ncr2, word BlockNum, byte length, byte *Buffer)
{
    uint16_t Count = 0;
    uint32_t addr;
    
    addr = BlockNum;
    addr *= FLASH_BLOCK_SIZE;
    addr += FLASH_PROG_START_PHYSICAL_ADDRESS;
 
    /* Unlock program memory */
    FLASH->PUKR = FLASH_RASS_KEY1;
    FLASH->PUKR = FLASH_RASS_KEY2;
        
    FLASH->CR2 |= cr2;
    FLASH->NCR2 &= (uint8_t)(~ncr2);
    
    for (Count = 0; Count < length; Count++)
    {
        *((PointerAttr uint8_t*) (uint32_t)addr + Count) = ((uint8_t)(Buffer[Count]));
    }
    
    while( (FLASH->IAPSR & (FLASH_IAPSR_EOP | FLASH_IAPSR_WR_PG_DIS)) == 0);
    
    /* Lock memory */
    FLASH->IAPSR &= (uint8_t)FLASH_MEMTYPE_PROG;
    
    return FLASH_NO_ERROR;  
}

byte PFlashEraseBlock(dword gAddr)
{
  static byte data[]={0,0,0,0};
  byte ret = 0;
  dword blockNum = (gAddr - FLASH_PROG_START_PHYSICAL_ADDRESS) / FLASH_SECTOR_SIZE;
  ret = ((flashProgramCodeInRamFun)DRIVER_ADDR)(FLASH_CR2_ERASE, FLASH_NCR2_NERASE, blockNum, 4, data);

  return ret;
} 

byte PFlashProg(dword gAddr, byte len, byte *pDat)
{
  byte ret = 0;
  dword blockNum = (gAddr - FLASH_PROG_START_PHYSICAL_ADDRESS) / FLASH_SECTOR_SIZE;
  ret = ((flashProgramCodeInRamFun)DRIVER_ADDR)(FLASH_CR2_PRG, FLASH_NCR2_NPRG, blockNum, FLASH_SECTOR_SIZE, pDat);

  return ret;
}

(1)那么我想把地址直接传到LaunchFlashCommand中不用BlockNum行不行呢?

答案是分情况:如果参数是u16则完全没问题,适合32K以内的小容量MCU,如果是u32的参数则会出问题,程序跑飞

因此地址空间只有8000~FFFF=32K

(2)我想把函数放到固定地址上,参数是u16的BlockNum,不用__ramfunc 行不行?

答案也是分情况:如果在LaunchFlashCommand不用u32就可以,因此地址空间仍是32K

方法如下:

在icf中设置place at address mem: 0x0000CE00 { readonly section .funflash };

然后函数定义时:byte LaunchFlashCommand(byte cr2, byte ncr2, word BlockNum, byte length, byte *Buffer) @ ".funflash"

使用时手动将数据copy到ram中,长度在hex文件中查看

(3)为什么不能直接传u32的地址?为什么不用__ramfunc自己直接copy机器码,函数中使用u32就不行?

这问题我回答不上来,使用飞思卡尔的16位MCU也有类似的现象,自己指定函数地址,copy到ram,函数里如果使用u32则会出错。比较一下生成的hex文件,两种方式生成的机器码也不一样。

4、如何提取Flash驱动呢?

在汽车ECU上,OEM处于于安全考虑要求Flash的擦写函数不能保存在Flash中;执行Bootloader之前首先将Flash擦写函数(称之为驱动)发给MCU,MCU放到RAM里执行,用毕清空。

因此如何提取呢?

(1)查找map文件,找到LaunchFlashCommand的地址和长度
(2)仿真时,查看RAM窗口,找到LaunchFlashCommand部分的数据,选中复制
(3)将复制的数据做成s19或者hex文件
 

 

 

你可能感兴趣的:(STM8做IAP(Bootloader)时在RAM中执行Flash块擦写函数问题)