本帖最后由 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遇到的相关问题,欢迎讨论交流。
|