历史上的今天
返回首页

历史上的今天

今天是:2024年11月19日(星期二)

正在发生

2019年11月19日 | STM8做IAP(Bootloader)时在RAM中执行Flash块擦写函数问题

2019-11-19 来源:eefocus

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文件


推荐阅读

史海拾趣

西博臣(CYBERSEN)公司的发展小趣事

西博臣非常重视产品质量和品牌建设。公司建立了严格的质量管理体系,从原材料采购到产品生产、测试、包装等各个环节都进行严格的质量控制。同时,公司还注重品牌形象的塑造和推广,通过参加各类行业展会、举办技术交流会等方式提升品牌知名度和美誉度。

Electronic公司的发展小趣事

小米,作为一家年轻的科技公司,在电子行业的发展中展现了惊人的成长速度。小米凭借出色的产品设计和创新的营销策略,迅速在手机市场获得了巨大成功。随着业务的不断拓展,小米还涉足了智能家居、物联网和互联网金融等领域,形成了完整的生态布局。小米通过提供高性价比的产品和优质的服务,赢得了大量年轻用户的喜爱和支持。

DAYA公司的发展小趣事

大雅智能深知,品质是企业立足之本。因此,在产品研发和生产过程中,大雅智能始终坚持严格的质量控制标准,确保每一台产品都符合高品质要求。同时,大雅智能还建立了完善的售后服务体系,为用户提供及时、专业的技术支持和维修服务。正是这份对品质的执着追求,让大雅智能在消费者中赢得了良好的口碑。

CYANLITE公司的发展小趣事

近年来,随着数字化技术的快速发展,CYANLITE公司也积极拥抱数字化转型。他们利用大数据、人工智能等先进技术优化生产流程、提高生产效率,并加强了对市场的分析和预测能力。此外,公司还加强了对电子商务平台的投入和建设,通过线上渠道拓展销售渠道并提高品牌曝光度。展望未来,CYANLITE公司将继续加大在技术研发和数字化转型方面的投入力度,推动公司向更高层次发展。同时他们也将继续关注市场变化和客户需求的变化趋势,不断调整和优化自己的战略方向和产品布局以应对未来的挑战和机遇。

EECO Switch公司的发展小趣事

在人机界面产品领域,开关是不可或缺的重要组件。EECO Switch公司凭借其卓越的研发实力,不断在开关技术上取得突破和创新。公司成功开发出了二进制编码的指轮开关、STRIPSWITCH®和微DIP®印刷电路板上安装的编码开关等多种产品,这些产品不仅具有高度的可靠性和稳定性,而且操作简便、功能丰富,满足了不同客户的需求。

Dexter Research Center Inc公司的发展小趣事

Dexter始终将技术创新作为企业发展的核心动力。公司不断投入研发资源,推动传感器技术的创新和发展。通过与高校、研究机构的合作,Dexter成功引入了多项新技术、新材料,并将其应用于产品中。这些创新产品不仅提高了传感器的性能和精度,还拓展了其应用领域。Dexter的创新精神引领了行业的发展方向,使其成为了行业的佼佼者。

问答坊 | AI 解惑

欧姆龙PLC中断问题

欧姆龙PLC如何实现中断,从那里进入及退出,请高人指点!…

查看全部问答>

Keil C51 几类重要库函数 (2)

8. 第八章 dScope for Windows使用详解 1. 第一节 概述 1. 1. 主窗口(Mainframe Window) 可设置其它各种调试窗口,设置断点、观察点,修改地址空间,加载文件等等; 2. 2. 调试窗口(DEBUG Window) 支持用户程序的各种显示方式,可连续运行 ...…

查看全部问答>

请问在wince里怎样用wifi发送特定数据

请问在wince里怎样用wifi发送特定数据 有人说就是socket编程,我没想明白,难道系统会自动调用wifi? 还请各位前辈指点迷津!…

查看全部问答>

ST选型

请问谁清楚ST的哪个8位机有2个PWM,3个定时器,10路ADC,34个I/O(含10个AD口) ,是用来做电源的。wendellyang@163.com…

查看全部问答>

热电堆 信号选择

小弟最近想做一个用于微波炉的温度测量的红外探测模块。由于以前都没有做过项目,所以经验非常少,现在我苦于热电堆型号的挑选。         网上看了看有这些:SC0067、SC0070、10TP583T、OTP系列(如OTP-537F2、OTP- ...…

查看全部问答>

关于硬盘无刷电机的驱动

小弟在研究硬盘电机的驱动,我曾试过用小功率管9013,8050,8550等等来搭成达林顿管子,但是驱动电机的时候发热严重,而且明显有气无力...虽然能转...在网上找了很久,想用场效管IRF540N来驱动,但是不知道电路如何搭建...求各位高手,走过路过的能 ...…

查看全部问答>

8962 lwip 如何实现不同网段连接啊

求助,哪位告诉能指点下, 在8962上面使用lwip 如何实现不同网段的能够连接啊???就是公司网络 192.168.1.12的电脑 可以 ping通 192.168.2.2的电脑但是我的板子 192.168.1.10 ping不同 192.168.2.2的电脑…

查看全部问答>

差分放大器低通滤波器设计

有没谁用差分放大器做个低通滤波器的?请指点小弟一下,谢了...…

查看全部问答>

大三学生方向分流,求好心人指点

大三嵌入式又要分方向了, ARM 、 PCL、 单片机,感觉吧....就是没感觉,有没有前辈给这三个方向的发展,优缺点,就业情况分析一下啊? 小弟先谢谢了....!!!   …

查看全部问答>