13

Nuoeriris · 2021年02月04日

MM32F013x——硬件看门狗及选项字节操作

嵌入式应用中经常会遇到需要保存一些数据,比如配置信息等等,为了在设备关机或掉电情况下数据不丢失,我们通常做法是会将数据保存在存储区,可以在FLASH主存储区、备份寄存器和选项字节等存储用户数据。

MM32F013X系列芯片的嵌入式闪存高达 64K 字节 ,整个片内 FLASH由两部分组成:一部分是主存储块,另一部分是信息存储块。主存储块除了被用于存储用户代码,也可被模拟成EEPROM来存储用户数据;在信息存储块中,包括了保护字节、保密空间、系统存储器 ISP 和选项字节四部分,其中除了系统存储器 ISP区域用户不可使用外,用户可以通过对应的操作流程对其它区域进行读写操作,于用户而言,选项字节用户数据区也可以被用来存储2个字节长度的有效数据。

本文将重点介绍如何在MM32F013X上实现用FLASH 选项字节存储用户数据以及使能硬件独立看门狗模式的功能。

1. 选项字节介绍

MM32F013X 系列MCU 的选项字节主要用于存储用户对芯片的配置信息及用户关键数据,主要有写保护使能、看门狗模式切换等等不同配置内容,FLASH 控制器可以通过对这些值的设置来选择不同的系统功能选项。选项字节区块的前16字节,每两个字节组成一个正反对,其中用户只需要设置低位的字节,高位由系统自动填充为其反码。

选项字节的组成如下表所示(位 15 ∼ 8 中的值为位 7 ∼ 0 中选项字节 0 的反码):

1.png
注意:在写保护值中,一个比特位对应四页,即 4096 字节,其它详情请参见UM手册。

  • USER:字节2,用户字节,配置看门狗模式、停机复位模式、待机复位模式以及BOOT1。
  • DATA0:字节4,数据字节0,由用户存储数据。
  • DATA1:字节6,数据字节1,由用户存储数据。
  • WRP0:字节8,写保护字节0,存储对主存储块的写保护设置。
  • WRP1:字节10,写保护字节1,存储对主存储块的写保护设置。
1.1 相关寄存器

4.png5.png

1.2 擦除流程

选项字节区块擦除操作流程的具体步骤如下:

2.png
1.2.1 检查FLASH\_CR寄存器的LOCK位,如未被解锁则执行解锁操作。

if((FLASH->CR & FLASH_CR_LOCK) == SET )
{
    FLASH->KEYR = FLASH_KEY1 ;
    FLASH->KEYR = FLASH_KEY2 ;
}

1.2.2 检查FLASH\_CR寄存器的OPTWRE位,如有置位则执行解锁OPTWRE操作。

if((FLASH->CR & FLASH_CR_OPTER) == RESET)
{
    FLASH->OPTKEYR = FLASH_KEY1 ;
    FLASH->OPTKEYR = FLASH_KEY2 ;
}

1.2.3 将闪存选项字节块基地址写入FLASH\_AR寄存器。

FLASH->AR = OB_BASE ;

1.2.4 设置FLASH\_CR寄存器的OPTER位为1,选择选项字节擦除操作。

FLASH->CR |= FLASH_CR_OPTER ;

1.2.5 设置FLASH\_CR寄存器的STRT位为1。

FLASH->CR |= FLASH_CR_STRT ;

1.2.6 等待FLASH\_SR寄存器的BSY位变为0,再读出选项字节寄存器中所有地址来验证擦除操作,直到流程结束。

do{
       ret = (((FLASH->SR & FLASH_FLAG_BSY)) ? FLASH_BUSY:            \
              ((FLASH->SR & FLASH_FLAG_PGERR) ? FLASH_ERROR_PG:         \
              ((FLASH->SR & FLASH_FLAG_WRPRTERR) ? 
                FLASH_ERROR_WRP : FLASH_COMPLETE))) ;
       EraseTimeout-- ;
       for (i = 0xFF; i != 0; i--) ;
  }while((ret == FLASH_BUSY) && (EraseTimeout!= 0x00)) ;
  FLASH->CR = 0 ;
  FLASH->SR = FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR ;
  return (FLASH_Status)((EraseTimeout== 0x00) ? FLASH_TIMEOUT : ret) ;

由于选项字节只有16字节,因此,擦除时是整个选项字节都将被擦除,此时需要注意,将有效的用户数据及配置信息提前保存到内存中再进行擦除。

1.3 编程流程

选项字节区块编程操作流程的具体步骤如下:

3.png

1.3.1 检查FLASH\_CR寄存器的LOCK位,如未被解锁则执行解锁操作。

if((FLASH->CR & FLASH_CR_LOCK) == SET )
    {
        FLASH->KEYR = FLASH_KEY1 ;
        FLASH->KEYR = FLASH_KEY2 ;
    }

1.3.2 检查FLASH\_CR寄存器的OPTWRE位,如有置位则执行解锁OPTWRE操作。

if((FLASH->CR & FLASH_CR_OPTER) == RESET)
{
    FLASH->OPTKEYR = FLASH_KEY1 ;
    FLASH->OPTKEYR = FLASH_KEY2 ;
}

1.3.3 设置FLASH\_CR寄存器的OPTPG位为1,选择编程操作。

FLASH->CR |= FLASH_CR_OPTPG ;

1.3.4 写入要编程的半字到指定的地址,启动编程操作。

__IO u16 temp ;
temp = (u16)(~data) ;
temp = (temp << 8) & 0xFF00 ;
temp = temp | (u16)data ;
*(__IO u16*)address = temp ;

1.3.5 等待FLASH\_SR寄存器的BSY位变为0,可选读目标地址数据,以确保半字编程成功,直到所有编程流程结束。

do{
       ret = (((FLASH->SR & FLASH_FLAG_BSY)) ? FLASH_BUSY:            \
              ((FLASH->SR & FLASH_FLAG_PGERR) ? FLASH_ERROR_PG:         \
              ((FLASH->SR & FLASH_FLAG_WRPRTERR) ? 
                FLASH_ERROR_WRP : FLASH_COMPLETE))) ;
       ProgramTimeout -- ;
       for (i = 0xFF; i != 0; i--) ;
  }while((ret == FLASH_BUSY) && (ProgramTimeout != 0x00)) ;
   FLASH->CR = 0 ;
   FLASH->SR = FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR ;
   return (FLASH_Status)((ProgramTimeout == 0x00) ? FLASH_TIMEOUT : ret) ;

2. 软件实现步骤

下面列出配置代码,实现开启硬件IWDG独立看门狗以及将用户数据存储在DATA0和DATA1。

2.1 主程序初始化
s32 main(void)
{    /* Systick delay init */
    DELAY_Init();
    /* Uart1 init */
    CONSOLE_Init(115200) ;
    printf("\r\nMM32F013x OptionByte Demo %s %s\r\n", __DATE__, __TIME__);

    /* OptionByte test entry */
    FLASH_OptionByte_Entry() ;

    /* Set the HWIWDG parameters,no need to turn on LSI or IWDG_Enable */
    Set_IWDG(IWDG_Prescaler_64, 999);
    printf("\r\n IWDG reload value is set to 1.6s !\r\n ");

    while (1)
    {
        /* Feed the HW_IWDG if needed*/
        Feed_IWDG() ;
        DELAY_Ms(1000) ;
        printf("\r\n System is going on. Feed the IWDG...\r\n "); 
    }
}

以上为整个软件工程的入口主函数,平台相关的堆栈设置、时钟设置以及FLASH延迟设置等等都在STARTUP\_MM32F013X\_KEIL.S和SYSTEM\_MM32F013X.C文件中已经完成,默认主频为内部时钟HSI倍频到72M,上电后程序会跳转到主函数。初始化好延时和串口后,进入到测试选项字节的功能函数中,并且接下来的初始化中还配置了IWDG的重装载值以及分频系数,在while(1)循环里选择是否喂狗看不同现象。

2.2 OptionByte 实验函数
void FLASH_OptionByte_Entry(void)
{
    volatile uint32_t TempOptionByteValue = 0;
    uint8_t WriteUserData[2] ={0x01,0x02} ;

    /* Unlock The FLASH Controller */
    FLASH_Unlock();

    if( FLASH_GetFlagStatus(FLASH_FLAG_OPTERR) == RESET )
    {
        TempOptionByteValue = FLASH_GetUserOptionByte();
        printf("\r\nTempOptionByteValue=0x%2x\r\n ", TempOptionByteValue);

        if(((uint8_t)(TempOptionByteValue >> 10) != WriteUserData[0]) &&           \
            ((uint8_t)(TempOptionByteValue >> 18) != WriteUserData[1]) )
        {/* User Data0 != 0x01 , User Data1 != 0x02*/
            /* Erase Option Bytes */
            FLASH_Status status = FLASH_EraseOptionBytes();

            /* Write User Data0 */
            status = FLASH_ProgramOptionByteData(0x1FFFF804, WriteUserData[0]) ;
            /* Write User Data1 */
            status = FLASH_ProgramOptionByteData(0x1FFFF806, WriteUserData[1]) ;
            /* Write User Byte to enable HW mode of IWDG */
            status = FLASH_ProgramOptionByteData(0x1FFFF802,0xFE) ;
            printf("\r\n UserDatas and UserByte are written!\r\n ");

            printf("\r\n INPORTANT! Need to reset the system ……\r\n ");
            /* Lock The Flash Program Erase Controller */
            FLASH_Lock();
            /* System Reset */
            NVIC_SystemReset();
        }
        else
        {
            if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
            {
                printf("\r\n RCC_FLAG_IWDGRST is set!\r\n ");
                /* Clear reset flags */
                RCC_ClearFlag();
                printf("HW IWDG was set successfully,need to RL_IWDG!\r\n ") ;
            }
            else
            {
                printf("\r\n Reboot ok.\r\n "); 
                printf("\r\n UserDatas were updated successfully!\r\n ");
            }
        }
    }
    /* Lock The Flash Controller */
    FLASH_Lock();
}

由于每次系统复位后,选项字节才会被重新加载,并保存在选项字节寄存器 (FLASH\_OBR)中,每个选择位都在信息块中有它的反码位,在加载选择位时,反码位用于验证选择位是否正确,如果有任何的差别,将产生一个选项字节错误标志 (OPTERR),所以建议操作选项字节空间时先判断FLASH标志位。

按照流程对FLASH的选项字节空间进行数据读取和写入操作,且设置独立看门狗为硬件模式,做完所有的操作后,一定要将系统进行软复位或者上电复位,这样才能使之前的写入操作正式生效。

最后,通过RCC的复位标志判断此次复位是否由看门狗引发,搭配串口信息输出来进行所有功能验证。

3. 测试验证

本次实验同样是基于eMiniboard MB-025硬件资源完成的,前述完整代码中,开启了在主循环的喂狗操作,每隔1s钟进行喂狗动作且通过串口打印运行状态,串口助手完整显示如下:

6.png

可以发现已经正常将用户数据0x01和0x02分别写入到了Data0和Data1中,且独立看门狗的硬件模式也有设置,为了验证看门狗已经生效,屏蔽喂狗语句后重新编译工程,擦除全片后重新烧录程序,此刻,串口助手完整显示如下:

7.png

通过以上打印信息,可以确认硬件看门狗的硬件模式已经生效,无需通过用户代码开启LSI时钟和开启独立看门狗,只要芯片正常上电即可完成对用户程序异常情况发生的防护,从而提高系统整体安全性能

推荐阅读
关注数
6108
内容数
272
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息