打印
[APM32F4]

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

[复制链接]
804|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函数中,定义并调用的类即可。
/*
* Copyright (c) 2006-2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/

#include "mbed.h"

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

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

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

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


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

// Blinking rate in milliseconds
#define BLINKING_RATE     500ms

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

void flip()
{
    led = !led;//led翻转
}

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




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




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



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



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


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



其主要的启动流程如下:
* Reset (TARGET)
*     -> SystemInit (TARGET)
*     -> __main (LIBC)
*         -> __rt_entry (MBED: rtos)
*             -> __user_setup_stackheap (LIBC)
*             -> mbed_set_stack_heap (MBED: rtos/mbed_boot.c)
*             -> mbed_cpy_nvic (MBED: rtos/mbed_boot.c)
*             -> mbed_sdk_init (TARGET)
*             -> _platform_post_stackheap_init (RTX)
*                 -> osKernelInitialize (RTX)
*             -> mbed_start_main (MBED: rtos/mbed_boot.c)
*                 -> osThreadNew (RTX)
*                     -> pre_main(MBED: rtos/mbed_boot.c)
*                         -> __rt_lib_init (LIBC)
*                         -> $Sub$main (MBED: rtos/mbed_boot.c)
*                             -> mbed_main (MBED: rtos/mbed_boot.c)
*                             -> main (APP)
*                 -> 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。


除了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文件中调用一次。




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



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























使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

38

主题

65

帖子

6

粉丝