[APM32F4] mbedos gpio外部中断无法进入 问题定位分析

[复制链接]
1220|0
 楼主| DKENNY 发表于 2023-10-25 10:13 | 显示全部楼层 |阅读模式
本帖最后由 DKENNY 于 2023-10-25 10:13 编辑

#申请原创#

1、前言      
       近期了解了mbedos,mbedos是一款功能非常强大的操作系统,可支持所有基于armcortex-m系列开发板的外设,提供了wifi、eth、蓝牙等多种外设接口。最近在使用mbedos移植APM32F4xx系列时,遇到了一些问题,简单分享一下。

2、mbed os简介
       mbedos是一个面向ARM Cortex-M系列处理器的嵌入式开源系统,它由ARM公司提供,专门针对Cortex-M系列处理器进行开发。mbedos具有简单、轻量级、可移植性等特点,它支持多种开发工具和平台,包括Keil、IAR、GCC等。

mbedos的特征有:
       轻量级:mbedos非常轻量级,不会占用太多系统资源,适合在资源有限的嵌入式系统中运行。
       简单易用:mbedos提供了简单易用的API和开发工具,方便开发者进行嵌入式系统的开发。
       可移植性:mbedos具有可移植性,可以在不同的Cortex-M系列处理器上运行。
       高度集成:mbedos集成了多种常用的库和工具,方便开发者进行开发和管理。
       开放源代码:mbedos的源代码是公开的,开发者可以自由获取和使用。

官网:https://os.mbed.com/mbed-os
开源平台:https://github.com/ARMmbed

mbedos将每个外设、引脚都封装成了一个class,我们只需在main函数中,定义并调用的类即可。
  1. /*
  2. * Copyright (c) 2006-2020 Arm Limited and affiliates.
  3. * SPDX-License-Identifier: Apache-2.0
  4. */

  5. #include "mbed.h"

  6. DigitalOut myled(LED1);//LED1,输出模式

  7. int main()
  8. {
  9.     // check that myled object is initialized and connected to a pin
  10.     if (myled.is_connected()) {
  11.         printf("myled is initialized and connected!\n\r");
  12.     }

  13.     // Blink LED
  14.     while (1) {
  15.         myled = 1;          // set LED1 pin to high
  16.         printf("myled = %d \n\r", (uint8_t)myled);
  17.         ThisThread::sleep_for(500);

  18.         myled.write(0);     // set LED1 pin to low
  19.         printf("myled = %d \n\r", myled.read());
  20.         ThisThread::sleep_for(500);
  21.     }
  22. }


3、问题定位
       在封装好gpio的前提下,我在main()中编写了如下代码进行测试,当触发按键中断时,程序找不到中断向量服务函数,pc指针跳至B.
  1. #include "mbed.h"

  2. // Blinking rate in milliseconds
  3. #define BLINKING_RATE     500ms

  4. InterruptIn button(BUTTON1);//定义一个按键输入类
  5. DigitalOut led(LED2);//LED1,输入模式

  6. void flip()
  7. {
  8.     led = !led;//led翻转
  9. }

  10. int main()
  11. {
  12.     // Initialise the digital pin LED1 as an output
  13.     button.rise(&flip);//配置button上升沿触发
  14.         
  15.     while (true)
  16.     {
  17.         ThisThread::sleep_for(BLINKING_RATE);//线程休眠500ms
  18.     }
  19. }


image002.jpg

我们需要知道一个前提,除了内核中断,mbedos会重新注册所有外设中断向量号所对应的中断服务函数。

image009.png


于是,我开始进入Debug调试,也就是在执行NVIC_SetVector()这个函数时,会重新设置中断向量号对应的地址和偏移量

image001.jpg


进一步调试,进入NVIC_SetVector(),发现中断号为7(按键的中断向量号)的vector的地址(0x08003E69),在0x08000000中并没有出现。

image003.jpg
image004.jpg
而vector的地址(0x08003E69)却在0x20000000中奇迹般的出现了。
这是怎么回事?于是,我特地去看了一下mbed os的具体启动流程。


4、mbedos的启动流程
我查看了mbedos源码,发现在mbed_boot.c文件中有相关的阐述。
image005.jpg


其主要的启动流程如下:
  1. * Reset (TARGET)
  2. *     -> SystemInit (TARGET)
  3. *     -> __main (LIBC)
  4. *         -> __rt_entry (MBED: rtos)
  5. *             -> __user_setup_stackheap (LIBC)
  6. *             -> mbed_set_stack_heap (MBED: rtos/mbed_boot.c)
  7. *             -> mbed_cpy_nvic (MBED: rtos/mbed_boot.c)
  8. *             -> mbed_sdk_init (TARGET)
  9. *             -> _platform_post_stackheap_init (RTX)
  10. *                 -> osKernelInitialize (RTX)
  11. *             -> mbed_start_main (MBED: rtos/mbed_boot.c)
  12. *                 -> osThreadNew (RTX)
  13. *                     -> pre_main(MBED: rtos/mbed_boot.c)
  14. *                         -> __rt_lib_init (LIBC)
  15. *                         -> $Sub$main (MBED: rtos/mbed_boot.c)
  16. *                             -> mbed_main (MBED: rtos/mbed_boot.c)
  17. *                             -> main (APP)
  18. *                 -> osKernelStart (RTX)

因为mbedos内置了rtx操作系统,所以系统在执行SystemInit()后,执行__main后,不会立即执行main()函数,还会进行操作系统的一些初始化操作。

__user_setup_stackheap (LIBC) 取消startup.s的堆栈分配,由mbedos重新分配堆栈大小
mbed_cpy_nvic () 复制中断向量表:ROM区->RAM区
mbed_sdk_init (TARGET) 用户自定义,时钟更新及相关中断配置
_platform_post_stackheap_init 初始化RTX堆栈
mbed_start_main() 创建main的一个子线程,初始化C库
pre_main() 执行__libc_init_array函数,主要用来定义C/C++中的对象的创建和销毁相关的动作
mbed_main 用户自定义,程序的默认入口点
main main函数入口点


5、问题解决
mbedos初始化时,系统会执行mbed_cpy_nvic(),将flash的中断向量表复制到ram区。当中断产生的时候,PC指针会跳转到ram区,从ram区查找对应的中断向量服务函数的入口地址。mbed_cpy_nvic()设置了SCB->VTOR寄存器的值为0x20000000。
image008.png

除了mbed_cpy_nvic()外,还会执行mbed_sdk_init(),这个是用户自定义的,我发现我在里面又调用了一次SystemInit(),执行SystemInit(),会将SCB->VTOR的值设置为0x08000000。
设置了SCB->VTOR=0x08000000,这并不会改变向量表在0x20000000处的内容。这是因为,VTOR寄存器只保存了向量表的首地址,而不会保存向量表的内容。

而前面执行了mbed_cpy_nvic(),已经将中断向量表copy到了ram区,所以当中断发生时,最新的中断向量表会在ram区更新,而不会在rom区更新。

设置了SCB->VTOR=0x08000000后,处理器只会从0x08000000开始查找中断向量表,而不会再从0x20000000地址开始查找。每个特定的地址范围对应一个特定的中断向量,一旦设置了新的向量表首地址,处理器就会按照新的首地址来查找中断向量表,所以会出现中断服务函数不存在的情况,程序跳至B.。


解决方式:

1、注释掉自定义的mbed_sdk_init()的SystemInit(),保证程序只在startup.s文件中调用一次。

image006.png


2、删除SystemInit()中对于SCB->VTOR的操作。
image007.jpg


       以上就是使用mbedos遇到的相关问题,欢迎讨论交流。























您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

108

帖子

17

粉丝
快速回复 在线客服 返回列表 返回顶部