• 【STM32H7教程】第69章 STM32H7的系统bootloader之串口IAP固件升级


    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    第69章       STM32H7的系统bootloader之串口IAP固件升级

    本章节为大家讲解使用系统bootloader做程序升级的方法,即使不依赖外部boot引脚也可以方便升级。

    IAP的全称是In Application Programming,即在线应用编程。

    69.1 初学者重要提示

    69.2 跳转到系统bootloader的程序设计

    69.3 STM32CubeProg的安装说明

    69.3 STM32CubeProg的程序下载说明

    69.4 串口方式系统bootloader驱动移植和使用

    69.6 实验例程设计框架

    69.7 实验例程说明(MDK)

    69.8 实验例程说明(IAR)

    69.9 总结

    69.1 初学者重要提示

    1.   学习本章节前,务必优先学习第67章。
    2.   特别注意STM32H7的系统BootLoader地址并不是0x1FFF 0000。
    3.   本章用到的相关软件和文档下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=96573
    4.   本章节的串口IAP下载软件使用STM32CubeProg,此软件实现了之前的DfuSe,STLINK小软件和Flashloader三合一,并且支持外部EEPROM,NOR Flash,SPI Flash,NAND Flash等烧写,也支持OTA编程。
    5.   使用系统bootloader做串口IAP升级时,MicroUSB接口不要接线到电脑端,因为这会导致系统bootloader工作在USB DFU模式,无法再使用串口IAP。

    69.2 跳转到系统bootLoader的程序设计

    程序设计如下,基本是按照第67章3.2小节的方法进行设计

    1.    /*
    2.    ******************************************************************************************************
    3.    *    函 数 名: JumpToBootloader
    4.    *    功能说明: 跳转到系统BootLoader
    5.    *    形    参: 无
    6.    *    返 回 值: 无
    7.    ******************************************************************************************************
    8.    */
    9.    static void JumpToBootloader(void)
    10.    {
    11.        uint32_t i=0;
    12.        void (*SysMemBootJump)(void);        /* 声明一个函数指针 */
    13.        __IO uint32_t BootAddr = 0x1FF09800; /* STM32H7的系统BootLoader地址 */
    14.    
    15.        /* 关闭全局中断 */
    16.        DISABLE_INT(); 
    17.    
    18.        /* 关闭滴答定时器,复位到默认值 */
    19.        SysTick->CTRL = 0;
    20.        SysTick->LOAD = 0;
    21.        SysTick->VAL = 0;
    22.    
    23.        /* 设置所有时钟到默认状态,使用HSI时钟 */
    24.        HAL_RCC_DeInit();
    25.    
    26.        /* 关闭所有中断,清除所有中断挂起标志 */
    27.        for (i = 0; i < 8; i++)
    28.        {
    29.            NVIC->ICER[i]=0xFFFFFFFF;
    30.            NVIC->ICPR[i]=0xFFFFFFFF;
    31.        }    
    32.    
    33.        /* 使能全局中断 */
    34.        ENABLE_INT();
    35.    
    36.        /* 跳转到系统BootLoader,首地址是MSP,地址+4是复位中断服务程序地址 */
    37.        SysMemBootJump = (void (*)(void)) (*((uint32_t *) (BootAddr + 4)));
    38.    
    39.        /* 设置主堆栈指针 */
    40.        __set_MSP(*(uint32_t *)BootAddr);
    41.        
    42.        /* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */
    43.        __set_CONTROL(0);
    44.    
    45.        /* 跳转到系统BootLoader */
    46.        SysMemBootJump(); 
    47.    
    48.        /* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
    49.        while (1)
    50.        {
    51.    
    52.        }
    53.    }

    这里把程序设计中的几个关键地方做个说明:

    •   第12行,声明一个函数指针。
    •   第13行,这个要特别注意,H7的系统Bootloader地址在0x1FF09800。
    •   第19到21行,设置滴答定时器到复位值。
    •   第24行,此函数比较省事,可以方便的设置H7所有时钟到复位值,内部时钟使用HSI。
    •   第27到31行,清除所有中断挂起标志并关闭中断,这里是直接通过一个for循环设置了NVIC所有配置位,共8组。

     

    •   第37行,将系统bootLoader的中断复位服务程序的入口地址赋给第12行声明的函数,用户执行这个函数时,就会直接跳转过去。
    •   第40行,设置主堆栈指针位置,即系统bootloader的首地址存储的就是栈地址。
    •   第43行,这个设置在RTOS应用程序中比较重要,因为基于Cortex-M内核的RTOS任务堆栈基本都是使用线程堆栈指针PSP。但系统bootLoader使用的是主堆栈指针MSP,所以务必要设置下,同时让M内核工作于特权级。此寄存器的作用:

     

    •   第46行,跳转到系统bootLoader。

    69.3 STM32CubeProg的安装说明

    STM32CubeProg的安装比较简单,如果大家的电脑中缺少JAVA环境,会提示安装,按照提示操作即可。

    这里特别注意USB DFU驱动的安装,如果大家的电脑上安装了DfuSe软件,那边板子工作在系统bootLoader模式时,电脑端的设备管理器识别出来的标识是这样的:

     

    如果用STM32CubeProg的话,务必要将此驱动删掉,鼠标右击此标识,选择卸载,弹出如下对话框:

     

    卸载完毕后,重启电脑,然后运行STM32CubeProg安装目录里面的STM32Bootloader.bat即可,最后插上设备就可以正常识别了。识别后的标识:

     

    69.4 STM32CubeProg的程序下载说明

    这里把两种下载方式都做个说明,一种是设置外部boot引脚进行下载,另一种是设置程序跳转到系统botloader进行下载。

    69.4.1 选择好用的串口线

    (注:MicroUSB接口不要接线到电脑端,因为这会导致系统bootloader工作在USB DFU模式,无法再使用串口IAP)

    选择好用的USB线很重要,比如我们开发板赠送的那根USB转RS232串口线是不可以用在这里做串口IAP的,这根线只能用于一般的串口通信和串口打印功能。

     

    当前我这里是用的我们H7-TOOL的USB转TTL输出,注意交叉方式连接,即RX接TX,TX接RX。GNG接GND。

     

    注,我这里没有接共地线,推荐大家接上,3.3V可以不接。

    69.4.2 设置boot引脚跳转到系统botLoader

    •   第1步:板子上电前按住右下角的BOOT引脚。

     

    •   第2步:板子上电3秒左右,松手。

    在电脑端设备管理器就可以看到已经识别出来:

     

    69.4.3 应用程序跳转到系统bootloader

    应用程序跳转到系统bootLoader比较方便,无需用户操作外置的boot引脚了,只需调用本章第2小节的程序就可以跳转。本章配套的例子是用户按下按键K1后执行跳转程序,大家可以根据需要实现各种触发跳转的方式。跳转成功后,在电脑端设备管理器里面也会看到bootloader标识:

     

    69.4.4 STM32CubeProg下载程序设置

    识别成功后就可以下载程序了。

      第1步,选择UART方式,配置使用的串口号,串口波特率115200和偶校验,点击Connect按钮。

     

    识别成功后的效果如下:

     

      第2步,添加要下载的hex文件,勾选需要设置的选项,点击启动编程。

     

    •   Start address选项不填的话,默认会下载到内部Flash的首地址,保险起见,大家也可以填上首地址0x0800 0000,或者其它要下载的地址。
    •   Run after programming选项可以根据需要勾上,如果勾上此选项后,下载完毕程序后,会自动断开连接,并弹出一些列窗口,最终弹出下面这个窗口:

     

    弹出这个窗口并不是表示下载失败了,而是下载完成后退出了系统bootloader,启动用刚刚下载的程序了。

      第3步,完成下载后的效果如下:

     

    下载完成后板子重新上电就可以看到程序已经成功下载了。

    69.5 串口方式系统Bootloader驱动移植和使用

    系统bootloader的移植比较简单,仅需添加本章第2小节的程序到自己工程里面即可。里面有个开关中断API,是在bsp.h文件里面定义的:

    /* 开关全局中断的宏 */
    #define ENABLE_INT()    __set_PRIMASK(0)    /* 使能全局中断 */
    #define DISABLE_INT()    __set_PRIMASK(1)    /* 禁止全局中断 */

    69.6 实验例程设计框架

    通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

     

      第1阶段,上电启动阶段:

    • 这部分在第14章进行了详细说明。

      第2阶段,进入main函数:

    •  第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。
    •  第2部分,应用程序设计部分,K1按键按下后跳转到系统bootloader。。

    69.7 实验例程说明(MDK)

    配套例子:

    V7-048_基于系统bootloader的串口IAP方式固件升级

    实验目的:

    1. 学习基于系统bootloader的串口IAP方式固件升级。

    实验内容:

    1. STM32的系统存储区自带bootLoader,可以方便的实现串口,I2C,CAN,SPI,USB等接口方式的程序升级。
    2. 如果使用系统bootLoader支持的接口升级方式,基本就不需要用户自己做bootLoader了。
    3. 除了通过boot引脚控制启动地址,也可以直接从应用程序里面跳转到系统存储区。

    实验操作:

    1. K1键按下,跳转到系统bootLoader。

    上电后串口打印的信息:

    波特率 115200,数据位 8,奇偶校验位无,停止位 1。


     

    程序设计:

      系统栈大小分配:

     

      RAM空间用的DTCM:

     

      硬件外设初始化

    硬件外设的初始化是在 bsp.c 文件实现:

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_Init
    *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    *    形    参:无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_Init(void)
    {
        /* 配置MPU */
        MPU_Config();
        
        /* 使能L1 Cache */
        CPU_CACHE_Enable();
    
        /* 
           STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
           - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
           - 设置NVIV优先级分组为4。
         */
        HAL_Init();
    
        /* 
           配置系统时钟到400MHz
           - 切换使用HSE。
           - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
        */
        SystemClock_Config();
    
        /* 
           Event Recorder:
           - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
           - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
        */    
    #if Enable_EventRecorder == 1  
        /* 初始化EventRecorder并开启 */
        EventRecorderInitialize(EventRecordAll, 1U);
        EventRecorderStart();
    #endif
        
    bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       
        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
        bsp_InitTimer();      /* 初始化滴答定时器 */
        bsp_InitUart();    /* 初始化串口 */
        bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
        bsp_InitLed();        /* 初始化LED */    
        bsp_InitExtSDRAM(); /* 初始化SDRAM */
    }

      MPU配置和Cache配置:

    数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

    /*
    *********************************************************************************************************
    *    函 数 名: MPU_Config
    *    功能说明: 配置MPU
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void MPU_Config( void )
    {
        MPU_Region_InitTypeDef MPU_InitStruct;
    
        /* 禁止 MPU */
        HAL_MPU_Disable();
    
        /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x24000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
        
        
        /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x60000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
        
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /*使能 MPU */
        HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    }
    
    /*
    *********************************************************************************************************
    *    函 数 名: CPU_CACHE_Enable
    *    功能说明: 使能L1 Cache
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void CPU_CACHE_Enable(void)
    {
        /* 使能 I-Cache */
        SCB_EnableICache();
    
        /* 使能 D-Cache */
        SCB_EnableDCache();
    }

      每10ms调用一次蜂鸣器处理:

    蜂鸣器处理是在滴答定时器中断里面实现,每10ms执行一次检测。

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_RunPer10ms
    *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_RunPer10ms(void)
    {
        bsp_KeyScan10ms();
    }

      主功能:

    主程序实现如下操作:

    •   启动一个自动重装软件定时器,每100ms翻转一次LED2。
    •   K1键按下,跳转到系统BootLoader。
    /*
    *********************************************************************************************************
    *    函 数 名: main
    *    功能说明: c程序入口
    *    形    参: 无
    *    返 回 值: 错误代码(无需处理)
    *********************************************************************************************************
    */
    int main(void)
    {
        uint8_t ucKeyCode;    /* 按键代码 */
    
        
        bsp_Init();        /* 硬件初始化 */
        PrintfLogo();    /* 打印例程名称和版本等信息 */
        PrintfHelp();    /* 打印操作提示 */
        
        bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
        
        while (1)
        {
            bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    
            /* 判断定时器超时时间 */
            if (bsp_CheckTimer(0))    
            {
                /* 每隔100ms 进来一次 */  
                bsp_LedToggle(2);
            }
    
            /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
            ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
            if (ucKeyCode != KEY_NONE)
            {
                switch (ucKeyCode)
                {
                    case KEY_DOWN_K1:            /* K1键按下,K1键按下,跳转到系统BootLoader */
                        JumpToBootloader();
                        break;
                        
                    default:
                        /* 其它的键值不处理 */
                        break;
                }
            }
        }
    }

    69.8 实验例程说明(IAR)

    配套例子:

    V7-048_基于系统bootloader的串口IAP方式固件升级

    实验目的:

    1. 学习基于系统bootloader的USB接口方式IAP升级。

    实验内容:

    1. STM32的系统存储区自带bootLoader,可以方便的实现串口,I2C,CAN,SPI,USB等接口方式的程序升级。
    2. 如果使用系统bootLoader支持的接口升级方式,基本就不需要用户自己做bootLoader了。
    3. 除了通过boot引脚控制启动地址,也可以直接从应用程序里面跳转到系统存储区。

    实验操作:

    1. K1键按下,跳转到系统bootLoader。

    上电后串口打印的信息:

    波特率 115200,数据位 8,奇偶校验位无,停止位 1。

     

    程序设计:

      系统栈大小分配:

     

      RAM空间用的DTCM:

     

      硬件外设初始化

    硬件外设的初始化是在 bsp.c 文件实现:

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_Init
    *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    *    形    参:无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_Init(void)
    {
        /* 配置MPU */
        MPU_Config();
        
        /* 使能L1 Cache */
        CPU_CACHE_Enable();
    
        /* 
           STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
           - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
           - 设置NVIV优先级分组为4。
         */
        HAL_Init();
    
        /* 
           配置系统时钟到400MHz
           - 切换使用HSE。
           - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
        */
        SystemClock_Config();
    
        /* 
           Event Recorder:
           - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
           - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
        */    
    #if Enable_EventRecorder == 1  
        /* 初始化EventRecorder并开启 */
        EventRecorderInitialize(EventRecordAll, 1U);
        EventRecorderStart();
    #endif
        
    bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */       
        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
        bsp_InitTimer();      /* 初始化滴答定时器 */
        bsp_InitUart();    /* 初始化串口 */
        bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
        bsp_InitLed();        /* 初始化LED */    
        bsp_InitExtSDRAM(); /* 初始化SDRAM */
    }

      MPU配置和Cache配置:

    数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

    /*
    *********************************************************************************************************
    *    函 数 名: MPU_Config
    *    功能说明: 配置MPU
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void MPU_Config( void )
    {
        MPU_Region_InitTypeDef MPU_InitStruct;
    
        /* 禁止 MPU */
        HAL_MPU_Disable();
    
        /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x24000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
        
        
        /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x60000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
        
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /*使能 MPU */
        HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    }
    
    /*
    *********************************************************************************************************
    *    函 数 名: CPU_CACHE_Enable
    *    功能说明: 使能L1 Cache
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void CPU_CACHE_Enable(void)
    {
        /* 使能 I-Cache */
        SCB_EnableICache();
    
        /* 使能 D-Cache */
        SCB_EnableDCache();
    }

      每10ms调用一次蜂鸣器处理:

    蜂鸣器处理是在滴答定时器中断里面实现,每10ms执行一次检测。

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_RunPer10ms
    *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_RunPer10ms(void)
    {
        bsp_KeyScan10ms();
    }

      主功能:

    主程序实现如下操作:

    •  启动一个自动重装软件定时器,每100ms翻转一次LED2。
    •  K1键按下,跳转到系统BootLoader。
    /*
    *********************************************************************************************************
    *    函 数 名: main
    *    功能说明: c程序入口
    *    形    参: 无
    *    返 回 值: 错误代码(无需处理)
    *********************************************************************************************************
    */
    int main(void)
    {
        uint8_t ucKeyCode;    /* 按键代码 */
    
        
        bsp_Init();        /* 硬件初始化 */
        PrintfLogo();    /* 打印例程名称和版本等信息 */
        PrintfHelp();    /* 打印操作提示 */
        
        bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
        
        while (1)
        {
            bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    
            /* 判断定时器超时时间 */
            if (bsp_CheckTimer(0))    
            {
                /* 每隔100ms 进来一次 */  
                bsp_LedToggle(2);
            }
    
            /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
            ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
            if (ucKeyCode != KEY_NONE)
            {
                switch (ucKeyCode)
                {
                    case KEY_DOWN_K1:            /* K1键按下,K1键按下,跳转到系统BootLoader */
                        JumpToBootloader();
                        break;
                        
                    default:
                        /* 其它的键值不处理 */
                        break;
                }
            }
        }
    }

    69.9 总结

    本章节为大家介绍的串口IAP方式还是非常实用的,特别是产品硬件不带boot引脚时。

  • 相关阅读:
    Java中WebService实例
    Sublime Text 3 史上最性感的编辑器
    win2003的IIS無法使用,又一次安裝提示找不到iisadmin.mfl文件
    [Unity3D]Unity3D游戏开发之刀光剑影特效的实现
    fopen()函数
    UML学习(一)类图和对象图
    AfxMessageBox和MessageBox差别
    【课程分享】基于plusgantt的项目管理系统实战开发(Spring3+JDBC+RMI的架构、自己定义工作流)
    Android自己主动化測试之Monkeyrunner用法及实例
    Java工厂模式
  • 原文地址:https://www.cnblogs.com/armfly/p/12410739.html
Copyright © 2020-2023  润新知