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

2019-11-19来源: eefocus关键字: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块擦写函数 编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic480470.html 本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM8S——Clock control(CLK)
下一篇:stm8s eeprom读写

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

ST推出经济好用的STM8 Nucleo-32开发板
意法半导体新推出的STM8开发板采用方便好用的Nucleo-32 开发板外形尺寸,让使用8位STM8微控制器(MCU)开发原型速度更快,更经济实惠,更容易上手,适合所有类型的创客。 这款紧凑型板子配备USB接口,板子控制和供电都很简便。板载ST-LINK调试器/编程器,省去了外部调试探针,支持简单的拖放式闪存烧写。板上Arduino™Nano引脚可连接现成的shield板子,简化主板功能扩展,并让用户能够与开源硬件社区互动。这些板子得到主要开发工具链的支持,包括IAR Embedded Workbench for STM8 和Cosmic CXSTM8。 STM8 MCU基于一颗高性能8位
发表于 2019-12-05
ST推出经济好用的STM8 Nucleo-32开发板
STM8S开发环境搭建IAR For STM8
/************************************************************************************************************************************ Name    : STM8S开发环境搭建IAR For STM8* Author  : MingMing* Release : 2013/12/29* Update  : 2013/12/29* E-mail  : clint.wang@foxmail.com
发表于 2019-12-05
STM8S开发环境搭建IAR For STM8
STM8S内部时钟切换问题解决
/************************************************************************************************************************************ Name    : STM8S内部时钟切换问题解决* Author  : MingMing* Release : 2013/12/28* Update  : 2013/12/29* E-mail  : clint.wang@foxmail.com
发表于 2019-12-05
STM8S内部时钟切换问题解决
STM8S存储器的读写操作
/************************************************************************************************************************************ Name    : STM8S存储器的读写操作* Author  : MingMing* Release : 2014/1/2* Update  : 2014/1/2* E-mail  : clint.wang@foxmail.com
发表于 2019-12-05
stm8l051 halt之后外部中断唤醒问题
最近用到stm8l051 halt,在halt之后开启了外部中断,有时灵,有时不灵,设置下降沿触发,但是按键(低有效)放开了才会从HALT退出,最后发现在进去外部中断的时候一直在外部中断里面不退出。在外部中断程序里面把端口的外部中断和端口使能的外部中断功能关闭,问题解决,可以在下降沿从halt退出。1、开启halt,其他代码省略,只贴halt部分:    /* 禁止TIM3 */    TIM3->SR1 = (uint8_t)(~(uint8_t)TIM3_IT_Update);    CLK_PeripheralClockConfig
发表于 2019-12-05
STM8L052低功耗模式
Stm8L系列单片机的低功耗有五种模式:§ wait模式§ Lowpower run模式§ Lowpower wait模式§ Active-haltwith full RTC模式§ Halt模式最低功耗的就是就是halt模式。这里也主要总结一下如何进入halt模式,进入以后可以通过什么方式唤醒,以及有很多客户会关心的如何自动唤醒。Halt模式进入很简单,执行一条halt指令,调用库函数也就是halt()就行了。但是进入前要注意把所有的中断挂起标志给清除掉。要是不清零又恰巧有中断标志的时候进入该模式也会被立马唤醒。进入这种模式,所有的外设全都关闭了,所有时钟关闭。这时候它自己是醒不过来的,只能靠掐人中(给个外部中断)或者重新复活
发表于 2019-12-05
小广播
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved