
使用Arm Mbed OS后,可以使LED闪烁 ,该是测试和配置其他重要服务的时候了。 描述如下:
- Mbed配置技术
- 为什么在常规RTOS中很难切换到C ++
- 如何在RTOS中节省内存
- 在Mbed OS中如何组织中断
- 比调试Mbed OS更方便
- 如何摆脱额外的SDK抽象层
我们将继续使用ARM Mbed OS对MKE18F系列微控制器进行编程的技术。
Mbed配置技术
Mbed项目的重要组成部分是在线和离线的自动配置和组装系统,即 在用户计算机上本地。 建议通过编辑具有特殊名称的.json文件进行配置。 然后, Python项目中的脚本将这些文件转换为头文件,用户选择的IDE的工作空间文件,链接器命令文件以及其他支持文件。
但是,从源文本的角度来看,所描述的方法的问题在于不透明,因为我们很难跟踪源中配置系统在哪里和什么发生了变化。 另一方面,在我们的案例中,没有动机支持将项目自动转移到不同的IDE的功能。
因此,决定放弃这种方法。 如上一篇文章所述,它只是被简单地形成为IDE IAR的在线项目,在IDE工作区中获得了非结构化的文件堆,然后将它们系统化并丢弃了不必要的文件。 因此,您不再需要通过.json文件进行配置,并且仅在三个特定位置放置了影响Mbed配置的参数:
- IDE 编译器选项
- 链接器MKE18F512xxx16_flash.icf批处理文件
- 头文件mbed_config.h
在mbed_config.h文件中, 您可以计算大约130个定义,一开始非常烦人。 但幸运的是,它们大多数与该项目中当前未使用的无线协议栈有关。 为方便起见,对条目进行了排序,以便将实际条目放在顶部。 最初, mbed_config.h文件中用于不同模块的定义是随机放置的 ,但是在排序之后,它看起来像这样:
开门#ifndef __MBED_CONFIG_DATA__ #define __MBED_CONFIG_DATA__ // Configuration parameters #define MBED_CONF_RTOS_PRESENT 1 // set by library:rtos #define MBED_ALL_STATS_ENABLED 1 //#define DEVICE_SLEEP=1 SLEEP #define MBED_CONF_APP_MAIN_STACK_SIZE 1024 #define MBED_CONF_APP_TIMER_THREAD_STACK_SIZE 512 #define MBED_CONF_APP_IDLE_THREAD_STACK_SIZE 512 #define MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE 3000000 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_ALL_THREADS_INFO 0 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_FILENAME_CAPTURE_ENABLED 0 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_HIST_ENABLED 0 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_HIST_SIZE 4 // set by library:platform #define MBED_CONF_PLATFORM_FORCE_NON_COPYABLE_ERROR 0 // set by library:platform #define MBED_CONF_PLATFORM_MAX_ERROR_FILENAME_LEN 16 // set by library:platform #define MBED_CONF_PLATFORM_POLL_USE_LOWPOWER_TIMER 0 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_BAUD_RATE 3000000 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_BUFFERED_SERIAL 0 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_CONVERT_NEWLINES 1 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_CONVERT_TTY_NEWLINES 0 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_FLUSH_AT_EXIT 1 // set by library:platform #define MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE 256 // set by library:drivers #define MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE 256 // set by library:drivers #define MBED_CONF_EVENTS_PRESENT 1 // set by library:events #define MBED_CONF_EVENTS_SHARED_DISPATCH_FROM_APPLICATION 0 // set by library:events #define MBED_CONF_EVENTS_SHARED_EVENTSIZE 256 // set by library:events #define MBED_CONF_EVENTS_SHARED_HIGHPRIO_EVENTSIZE 256 // set by library:events #define MBED_CONF_EVENTS_SHARED_HIGHPRIO_STACKSIZE 1024 // set by library:events #define MBED_CONF_EVENTS_SHARED_STACKSIZE 1024 // set by library:events #define MBED_CONF_EVENTS_USE_LOWPOWER_TIMER_TICKER 0 // set by library:events #define MBED_CONF_CELLULAR_DEBUG_AT 0 // set by library:cellular #define MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY 0 // set by library:cellular #define MBED_CONF_CELLULAR_USE_APN_LOOKUP 1 // set by library:cellular #define MBED_CONF_FILESYSTEM_PRESENT 1 // set by library:filesystem #define MBED_CONF_KINETIS_EMAC_RX_RING_LEN 16 // set by library:kinetis-emac #define MBED_CONF_KINETIS_EMAC_TX_RING_LEN 8 // set by library:kinetis-emac #define MBED_CONF_LORA_ADR_ON 1 // set by library:lora #define MBED_CONF_LORA_APP_PORT 15 // set by library:lora #define MBED_CONF_LORA_APPLICATION_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_APPLICATION_KEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_APPSKEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE 1 // set by library:lora #define MBED_CONF_LORA_DEVICE_ADDRESS 0x00000000 // set by library:lora #define MBED_CONF_LORA_DEVICE_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_DUTY_CYCLE_ON 1 // set by library:lora #define MBED_CONF_LORA_LBT_ON 0 // set by library:lora #define MBED_CONF_LORA_NB_TRIALS 12 // set by library:lora #define MBED_CONF_LORA_NWKSKEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION 1 // set by library:lora #define MBED_CONF_LORA_PHY EU868 // set by library:lora #define MBED_CONF_LORA_PUBLIC_NETWORK 1 // set by library:lora #define MBED_CONF_LORA_TX_MAX_SIZE 64 // set by library:lora #define MBED_CONF_LWIP_ADDR_TIMEOUT 5 // set by library:lwip #define MBED_CONF_LWIP_ADDR_TIMEOUT_MODE 1 // set by library:lwip #define MBED_CONF_LWIP_DEBUG_ENABLED 0 // set by library:lwip #define MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE 512 // set by library:lwip #define MBED_CONF_LWIP_ENABLE_PPP_TRACE 0 // set by library:lwip #define MBED_CONF_LWIP_ETHERNET_ENABLED 1 // set by library:lwip #define MBED_CONF_LWIP_IP_VER_PREF 4 // set by library:lwip #define MBED_CONF_LWIP_IPV4_ENABLED 1 // set by library:lwip #define MBED_CONF_LWIP_IPV6_ENABLED 0 // set by library:lwip #define MBED_CONF_LWIP_MEM_SIZE 36560 // set by library:lwip[Freescale] #define MBED_CONF_LWIP_PPP_THREAD_STACKSIZE 768 // set by library:lwip #define MBED_CONF_LWIP_SOCKET_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_TCP_ENABLED 1 // set by library:lwip #define MBED_CONF_LWIP_TCP_SERVER_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_TCP_SOCKET_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE 1200 // set by library:lwip #define MBED_CONF_LWIP_UDP_SOCKET_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_USE_MBED_TRACE 0 // set by library:lwip #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_CHANNEL 0 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_CHANNEL_MASK 0x7fff800 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_CHANNEL_PAGE 0 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_DEVICE_TYPE NET_6LOWPAN_ROUTER // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_PANID_FILTER 0xffff // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_PSK_KEY {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_PSK_KEY_ID 1 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_SEC_LEVEL 5 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_SECURITY_MODE NONE // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_HEAP_SIZE 32500 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL 22 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL_MASK 0x7fff800 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL_PAGE 0 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_COMMISSIONING_DATASET_TIMESTAMP 0x10000 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_EXTENDED_PANID {0xf1, 0xb5, 0xa1, 0xb2,0xc4, 0xd5, 0xa1, 0xbd } // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_ML_PREFIX {0xfd, 0x0, 0x0d, 0xb8, 0x0, 0x0, 0x0, 0x0} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_NETWORK_NAME // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_PANID 0x0700 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_PSKC {0xc8, 0xa6, 0x2e, 0xae, 0xf3, 0x68, 0xf3, 0x46, 0xa9, 0x9e, 0x57, 0x85, 0x98, 0x9d, 0x1c, 0xd0} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_DEVICE_TYPE MESH_DEVICE_TYPE_THREAD_ROUTER // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_MASTER_KEY {0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_PSKD // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_SECURITY_POLICY 255 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_USE_STATIC_LINK_CONFIG 1 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_USE_MALLOC_FOR_HEAP 0 // set by library:mbed-mesh-api #define MBED_CONF_NANOSTACK_CONFIGURATION nanostack_full // set by library:nanostack #define MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT 0 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_EVENT_LOOP_DISPATCH_FROM_APPLICATION 0 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_EVENT_LOOP_THREAD_STACK_SIZE 6144 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_EVENT_LOOP_USE_MBED_EVENTS 0 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_NVM_CFSTORE 0 // set by library:nanostack-hal #define MBED_CONF_NSAPI_DEFAULT_MESH_TYPE THREAD // set by library:nsapi #define MBED_CONF_NSAPI_DEFAULT_STACK LWIP // set by library:nsapi #define MBED_CONF_NSAPI_DEFAULT_WIFI_SECURITY NONE // set by library:nsapi #define MBED_CONF_NSAPI_DNS_CACHE_SIZE 3 // set by library:nsapi #define MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME 5000 // set by library:nsapi #define MBED_CONF_NSAPI_DNS_RETRIES 0 // set by library:nsapi #define MBED_CONF_NSAPI_DNS_TOTAL_ATTEMPTS 3 // set by library:nsapi #define MBED_CONF_NSAPI_PRESENT 1 // set by library:nsapi #define MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP 1 // set by library:ppp-cell-iface #define MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE 256 // set by library:ppp-cell-iface #define MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT 8000 // set by library:ppp-cell-iface #define MBED_CONF_PPP_CELL_IFACE_BAUD_RATE 115200 // set by library:ppp-cell-iface #define MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE ETHERNET // set by target:K66F #define MBED_LFS_BLOCK_SIZE 512 // set by library:littlefs #define MBED_LFS_ENABLE_INFO 0 // set by library:littlefs #define MBED_LFS_INTRINSICS 1 // set by library:littlefs #define MBED_LFS_LOOKAHEAD 512 // set by library:littlefs #define MBED_LFS_PROG_SIZE 64 // set by library:littlefs #define MBED_LFS_READ_SIZE 64 // set by library:littlefs #define NSAPI_PPP_AVAILABLE 0 // set by library:lwip #define NSAPI_PPP_IPV4_AVAILABLE 1 // set by library:lwip #define NSAPI_PPP_IPV6_AVAILABLE 0 // set by library:lwip #define NVSTORE_ENABLED 1 // set by library:nvstore #define NVSTORE_MAX_KEYS 16 // set by library:nvstore // Macros #define _RTE_ // defined by library:rtos #define NS_USE_EXTERNAL_MBED_TLS // defined by library:nanostack #define UNITY_INCLUDE_CONFIG_H // defined by library:utest #endif
应用于RTOS的向C ++过渡的功能
Mbed中 的顶级API用C ++编写,因此该语言必须在应用程序代码中使用。 但是,您需要了解一些细微差别。
在小型嵌入式系统上使用C ++进行RTOS的情况仍然很少。 这里的问题是,成功的RTOS项目力争成为多平台的,而与C相比, C ++对平台资源管理有很高的要求。 原因是希望向用户隐藏低级资源管理的详细信息。 它主要与内存资源有关。 构造函数,析构函数,线程,具有自动销毁的异常,数据结构对象的模板等使用具有动态内存的隐式操作。 但是小型系统中的RAM资源非常有限。 在此类系统中,尤其是RTOS中,RAM是最稀缺的资源。 在RTOS中,为每个任务分配了一个堆栈,开发人员无法预先预测其确切大小,因此选择时要留一些余量。 因此,存在数十个任务的RTOS立即需要10到30 kB的RAM大小。 各种解析器和协议(HTTP,HTML ...)和文件系统需要大量内存。 如果使用显示器,则对空闲RAM的要求会进一步提高。
IAR等开发环境的库配备了良好的动态内存管理器,但它们是为单线程运行时环境设计的。 他们开始在RTOS中工作,有必要编写其他代码。 此过程称为重定向 。
在用C语言编写的RTOS中,通常不会执行重新定向。 由于在语言级别没有动态内存的隐式操作,因此所有操作都通过调用它们自己的malloc和free函数的线程安全版本来显式执行。 程序员可以完全控制具有动态内存的操作,并且可以轻松地应用所有可能的措施来保存它。
对于C ++,如果我们想使用该语言的所有功能,则必须进行重定向 。 但是在每个开发环境中重新定向是一个纯粹的单独过程。 这使RTOS开发人员的生活变得困难。
下图是带有重定向的调用结构的示例。 __write , __lseek , __read函数可能无法由用户实现,但其功能仍由 IDE自行决定。 并且肯定printf和scanf将不会是多线程的。

Mbed是为少数几个知名开发工具( GCC , IAR , Keil)提供重新定位目标的RTOS之一,即使不是唯一的RTOS之一。
尽管有上述所有内容,您仍可以找到有关将RTOS移植到C ++而不执行重新定位的文章,例如,通过简单地用您自己的通用标准函数替换来解决问题。 这可能可行,但是当在IAR中使用C ++构造时,程序员需要意识到各种隐式和未记录的限制(仅静态构造器,检查所有模式的new ,丢弃异常等)。 调用C ++已经很困难。 作为用户友好的系统, Mbed消除了许多这些限制,以简单的方式接近Arduino 。
除了所有最新版本的IAR,切换到C11和C ++ 14都有困难,这在此处进行了描述-https: //www.iar.com/support/resources/articles/exploring-c11-and-c14/
Mbed中的中断如何组织
奇怪的是,将无法在Mbed API中找到用于组织中断服务的类或函数或某些东西。 您只能找到InterruptIn类,该类仅用于外部端口。
这些问题的答案应在CMSIS-RTOS中 ,即在CMSIS Cortex-M4核心外围访问层中寻求。 宏在此处定义:
- NVIC_SetVector-为给定的向量设置中断服务程序(ISR)
- NVIC_SetPriority-设置指定中断向量的优先级。
- NVIC_EnableIRQ-允许在给定向量上进行中断调用
初始化SysTick计时器的中断组织的方法如下:
NVIC_SetVector(mbed_get_m0_tick_irqn(), (uint32_t)SysTick_Handler)
Mbed任务优先级和中断优先级不应混淆。
如果未分配优先级,则默认情况下将其设置为最大。
从以这种方式分配的中断处理程序中,您可以调用任何不会引起期望的RTOS服务。 即 传输标志,信号量,消息,邮箱等 但是,不是从ISR本身调用服务,而是通过将Cortex-M内核的系统控制块 ( SCB )的中断控制和状态寄存器 ( ICSR )中的PENDSVSET位置1来调用软件中断。 即 当前中断处理程序完成后,如果没有其他优先级中断,则将通过PendSV向量调用系统处理程序,并在其中执行服务。
Mbed如何与动态内存一起使用?
使用C ++进行编程时, 动态内存(或堆或堆)是必需的组件。 在IAR下的Mbed项目中,该存储区的大小在MKE18F512xxx16_flash.icf链接器的设置文件中通过写入__size_heap__变量来确定。 我们设置大小以使其占用所有剩余的可用内存。 我们知道编译后.map文件还剩下多少可用内存,即 调整堆大小是一个迭代过程。
调用C ++对象的静态构造函数
使用C ++时的一个重要问题是在何处以及如何调用全局对象构造函数。 即使在RTOS声称严重性(例如MAX)中 ,也将忽略它,即 留给机会。 在那里,设计人员可以使用通常的单线程内存分配机制来开发标准的单线程开发环境库。 但是在开始之后,通用RTOS创建了自己的用于管理动态内存的机制,而全局对象占用的内存仍然被遗忘了。 这是我们努力节省内存并控制一切的一个漏洞。
Mbed更认真地处理了这个问题。 那里对于每种开发环境都有自己的方法。 在IAR中,这样做是这样的:
- 甚至在设计者调用之前,初始化流就被用户代码拦截
- 从RTOS API阻止流的方法已替换为标准库
- 标准函数new , delete , malloc , free ...被重定向到对RTOS动态内存API的调用 。
Mbed使用IAR库适配器进行多线程操作
您可以在此处了解有关将IAR调整为多线程的信息-http://supp.iar.com/FilesPublic/UPDINFO/005691/arm/doc/infocenter/DLIBThreadSupport.html
Mbed修改了IAR库的系统锁和文件流锁 。 它们在mbed_boot.c文件中实现,并使用操作系统互斥锁 。
在startup_MKE18F16.s文件的第一行中执行的__iar_program_start函数通过调用mbed_set_stack_heap初始化操作系统堆栈和动态内存。
调整任务堆栈的大小
将任务堆栈最小化是节省RAM的最有吸引力的选择。
对于需要较少堆栈的任务,使用了不同的技术。 例如,库函数强烈影响用于输出和格式化printf , sprintf , scanf行等的堆栈。 他们具有为堆栈上的数据存储分配较大的临时区域的能力。 如果我们拒绝在任务中使用这些功能,则可以将任务堆栈减少几百个字节。
Mbed OS在启动时会立即创建三个名称为“ main_thread” , “ timer_thread” , “ idle_thread”的任务 。 它们的默认堆栈大小由头文件mbed_rtx_conf.h中的宏确定。 我们将这些堆栈的声明移至mbed_config.h配置文件中,并减小了堆栈的大小。 现在定义如下:
- “ main_thread”任务堆栈由宏MBED_CONF_APP_MAIN_STACK_SIZE = 1024字节定义
- timer_thread任务堆栈由宏MBED_CONF_APP_TIMER_THREAD_STACK_SIZE = 512字节定义
- idle_thread任务堆栈由宏MBED_CONF_APP_IDLE_THREAD_STACK_SIZE = 512字节定义
Mbed内存使用情况控件
动态内存以及堆栈都是需要不断关注的资源。 要查看使用了多少动态内存以及对它的请求强度是多少,每个任务还剩下多少堆栈以及Mbed中的峰值堆栈负载是多少,这里有一些特殊的计数器。 默认情况下,它们被条件编译指令禁用;要启用它们,必须声明MBED_ALL_STATS_ENABLED 。 声明定义后,您需要编写自己的过程以向用户显示信息。 我们编写了一个特殊的过程,用于将统计信息输出到VT100终端仿真器,稍后将进行讨论。
除了OS提供的工具外,最新版本的IAR开发环境还添加了新的功能栈canaries 。 在这里阅读有关它们的信息 。 此处和此处讨论了有关溢出的一般堆栈保护问题。
Mbed代码调试和分析工具
只有使用JTAG / SWD调试器, 才能对Mbed在新平台上的工作进行真正深入的研究。

Mbed源充满了多级宏和条件编译指令。 仅查看源代码就无法说出哪些功能有效,哪些功能无效,程序在哪里运行以及在哪里无效。 唯一的出路是连接调试器并逐步分析程序执行路径。 在移植阶段,如果不进行逐步调试,几乎是不可能的。
我建议使用J-Link Pro和J-Link Ultra的 Segger调试器版本。 它们的特点是带宽高,比普通的廉价型号高几倍。 对于实时调试系统,这一点很重要。 跟踪快速事件时,此类调试器不太容易发生溢出,并且所需的调试迭代次数也更少。 除了分步调试之外,它们还能够记录中断,保留中断统计信息(包括其执行持续时间),支持RTT和ITM调试技术,捕获硬件异常和其他功能。
下面是通过J-Link适配器工作时IAR调试器窗口的视图。

这不值得节省,开发时间的90%用于调试。 尽管更昂贵的Segger J-Trace跟踪器不再具有很大的优势,但由于MKE18F系列没有特殊的跟踪器接口。
第二种调试工具,当然是通过UART的 I / O。 Mbed具有至少四种不同的技术,可通过UART访问串行数据通道,
这是:
- 通过标准C / C ++ I / O函数的DirectSerial类进行重定向
- RawSerial类
- 类序列
- UARTSerial类
非常不同的品种。 可以同时使用所有四个输出到同一端口。 其中一些功能更强大,一些功能更少,有文件记录,而另一些则没有。 将来,在我们的项目中,我们将使用RawSerial作为资源最少的类。
通过移植Mbed我们失去了多少个计时器
要使LED闪烁,使用了 Mbed OS API中的等待功能。 上一篇文章讨论了移植的问题。 但这还不是全部,除了Mbed保留其工作计数器(可以由mbed_stats_cpu_get函数读取)并且有一个计时器API 。 这些服务使用lp_ticker.c文件中的低级功能。 在此文件中, Kinetis外设套件中的LPTMR计时器用于组织时间计数 。 在移植过程中,对该文件进行了编辑以匹配MKE18F512VLL16微控制器中使用的时钟方法。
因此, Mbed端口完全捕获了两个计数器模块-PIT和LPTMR以及SysTick核心计时器 。 在为应用任务计划资源时,应记住这一点。
MKE18F引导程序功能
MKE18F系列芯片具有一个内置ROM,它具有一个通过串行接口UART,CAN,SPI,I2C进行引导加载的程序。 但是计划在板上使用我们的安全引导加载程序,因此常规引导加载程序的工作是不可取的。
在这种情况下, Kinetis芯片需要注意0x040D处的Program Flash区域的内容 。 存储一个确定启动顺序的常数。 在我们的例子中,常数0x7B应该写入那里 ,指示从闪存而不是从ROM开始,并在外部输出上禁用NMI功能。 使用此常数的不同值,程序可能会冻结在意外从ROM调用的内置引导程序中。
同样重要的是要记住,只有在核心频率为120 MHz而不是更高时(即最高频率),才可以写入控制器的闪存。 在HRUN模式下,无法进行Flash录制。
激活看门狗
我们控制器的电路板专用于工业用途,这意味着没有Watchdog不能做的事情。
最初,将system_MKE18F16.h文件中的DISABLE_WDOG宏设置为禁用看门狗 。 为了纠正这种情况,该宏被擦除,并实现了其自己的WDOG初始化。
看门狗的初始化是在SystemInit函数中完成的。 看门狗计数器在IDLE任务中更新。
这种方法要求优先级较高的任务不能独占处理器超过100毫秒 。 但这很容易发生,例如在向终端输出大型数据转储时。 因此,在任何地方,我们都使用等待功能将优先级大于IDLE的任务中的冗长过程分解为一些小片段,并散布着暂停。
SDK交付中的驱动程序问题
SDK驱动程序的前缀为NXP_MKE18F_drivers目录中的fsl ,是一种外围抽象层。 它们被认为可以促进难以学习的外围设备的编程,但是不幸的是文档很少。 相反,它们的文档仅限于函数头中的注释。 然后为他们写的东西很困惑,又使他们从研究微控制器外围的手册中解脱出来。 我的答案是没有办法。 通过驱动程序,可以更轻松地将程序传输到同一系列中的不同微控制器。 但是为了有效地应用它们,您需要很好地处理外围文档。 因此, SDK驱动程序解决了驱动程序开发人员自己的一个相当私人的问题,而不是开始学习Kinetis的用户的需求。
驱动程序还可以在该系列的所有芯片上工作,例如 是通用的,因此充满了条件宏,并检查每个特定芯片是否没有任何有用的功能。
驱动程序仍然可以通过某种方式帮助您更好地了解如何处理外围设备,但是在了解了驱动程序之后,您可以放心地忽略它。
就这一点而言,在此项目中,不受Mbed端口影响的外围设备的访问直接绕过驱动程序也就不足为奇了。
但是,可能会担心特定外围设备对SDK驱动程序可用性的依赖性。 在这里,您必须检查驱动程序的源代码。 依赖关系的主要危险来自DMA驱动程序功能的分离。 为避免这种情况,在使用外设时,您需要跟踪并禁止常规的Mbed服务使用DMA 。 如果DMA仍然不受SDK驱动程序的影响,那么从NXP_MKE18F_drivers目录中可以丢弃或忽略几乎所有不适用于上述2个计时器模块( PIT和LPTMR )和调试UART的内容 。
优先考虑通过SDK驱动程序涉及的外围设备的危险较小,但也很重要。 例如,您需要知道为SysTick中断,调试UART和计时器分配了什么优先级。 在其优先级等于或大于实时管理中使用的外围设备的优先级的情况下,这可能导致管理质量下降。
请记住, MKE18F的Mbed端口在没有优先级的情况下启动UART和定时器中断,即 他们默认情况下会获得最高级别。