实时断言开始
扫描二维码
随时随地手机看文章
我们讨论了为嵌入式应用程序设置断言通常需要的内容。我们还看到,根据所使用的工具链,它可能会略有不同。尽管存在这些较小的差异,但仍将断言用于相同的目的:检测开发人员对应用程序的假设何时在应用程序中的特定点不正确。
当适当使用时,断言可以提高代码质量。但是,存在一些关键情况,即断言要么无效或可能引起意外问题。
在今天的帖子中,我们将研究主张可能失败的地方以及为什么要失败,我将分享一些在实时系统中安全实施断言的实用技巧。
断言会引起问题的地方
在您查看实时断言之前,您需要了解主张可能使您失败的地方。在三种情况下,您可能会发现标准断言实现将使您失败。
1。在系统初始化期间
当微控制器启动时,它会执行供应商供应的代码,该代码初始化系统时钟,执行内存设置并准备运行时环境。这似乎是断言的逻辑场所,但事实并非如此。为什么:
· 振荡器才刚刚启动,外围设备可能需要充分时钟。
· 仍然需要映射诸如printf之类的函数,因此即使断言失败,输出也将无处可去。
· 如果您尝试停止处理器或打印消息,则可能会触发一个例外,以阻止系统启动。
除非必要,否则避免在启动代码中断言,并且仅使用直接I/O切换(例如LED )等低级机制进行诊断。
2。在微控制器驱动程序中
驾驶员是使用断言的非常有用的地方,但是再次,我们需要小心我们使用哪些驱动程序。考虑一下我们通过启动代码获得的情况。许多开发人员要做的第一件事是初始化GPIO引脚。我经常使用传递到gpio_init函数的配置表。例如,如果您有GPIO的配置表:
断言(config!= null);
如果我有断言检查这种结构并且不正确的事物,我将发出断言,但是该断言的结果将无处可寻! GPIO引脚不仅没有初始化,而且尚未映射printf和关联的输出。在这一点上,我将得到一个默默失败的断言。
沉默的断言不一定是一件坏事。我们仍然获得失败的断言的行号和有关原因的信息。问题是,我们没有意识到,主张已经发射了,并且在我们的系统中看起来很混乱,以了解为什么它不运行。
正如我们上次讨论的那样,我们可以设置自动断点或使用汇编指令使我们显而易见。我最喜欢的技巧之一是将LED作为主张LED献上,该主张在断言开火时会锁定。我们将很快谈论更多。
3。实时硬件组件
这是可能变得危险的地方。想象一个嵌入式系统驱动电动机。如果断言发射并停止电动机:
· 重载有效载荷可能会突然让位,并造成身体伤害。
· 涡轮机,火箭发动机或其他实时机制可能会灾难性地失效。
在这一点上,断言不仅会导致软件错误,还会损害安全性。在这些情况下,停止系统不是一个选择。
我们不能允许这些类型的情况发生。对于可能处于实时或生产操作中间的系统,我们的断言需要具备更多的技巧,而不仅仅是停止执行我们的代码。断言需要能够记录他们可以的任何数据,然后通知应用程序代码出现问题。然后,应用程序可以决定是否应安全地关闭系统,或终止该系统,或者该应用程序是否应继续进行(甚至可能尝试恢复)。现在,让我们看一下如何实施主张,以便可以在实时系统中使用它们的提示。
实时断言的提示
实时断言是旨在验证具有实时限制的系统执行过程中开发人员假设的程序检查。与传统的断言在失败时停止系统,实时断言对关键诊断信息进行记录,向应用程序或开发人员发出信号,并允许系统执行或过渡到安全状态,而不会破坏关键的实时操作。
以下是在使用实时断言时需要考虑的一些技巧:
提示#1 - 使用视觉辅助
在不停止CPU的情况下通知开发人员失败断言的最简单方法是使用视觉指示器。对于大多数系统,这意味着要切换LED:
void __aeabi_assert(const char *expr,const char *file,int line){
//使用LED
dio_channelwrite信号故障(assert_led,low);
而(1); //停止执行
}
LED闩锁表明发生了断言,终端上的消息告诉您确切的发生在哪里。
提示#2 - 创建断言日志
正如我们在这些博客中所看到的那样,断言通常会停止CPU,但是如果我们有电动机旋转或其他原因,为什么我们不想停止执行代码,我们可以改为将终端信息重定向到日志。日志可以用于开发中,但它们也是记录没有与之连接的终端的生产系统中的断言信息的最佳方法之一。
我们可以将几个不同的位置重定向到主张日志。我们应该考虑的第一个位置,无论将主张数据记录到RAM是什么。我通常会使用一个可以容纳最大长度的字符串然后将信息写入缓冲区的圆形缓冲区。我通常会包含文件,行号,然后包括我认为可能很重要的任何其他信息。有时,我什至会修改断言失败的功能,以获取一个自定义字符串,该字符串可以在发出断言之前立即提供有关条件的更多信息。我们可能会记录执行以下操作的数据:
void __aeabi_assert(const char *expr,const char *file,int line){
#if assert_uart == true
uart_printf(uart1,“ ostertion ostertion in lin s in line s in line%d \ n”,file,line);
#elif
log_append(assert,assert_string,file,line);
#endif
//视觉指示器
dio_channelwrite(assert_led,low);
}
圆形缓冲区可以在RAM中暂时固定断言日志。然后,背景任务可以将这些日志冲洗到非易失性内存(例如,闪存或SD卡)。
提示#3 - 通知应用程序
主张并非被设计为故障处理程序,而是实时断言不能仅仅阻止嵌入式系统。我们可能仍然想停止系统,但是要这样做,我们需要让主应用程序知道已经检测到缺陷,并且我们需要尽快进入安全模式。我们可以通过在主张库和主要应用程序之间创建信号机制来做到这一点。
例如,我们可能会认为我们的实时系统中有三种不同类型的断言:
1. 关键主张要求系统立即进入安全状态。
2. 中等主张不需要系统停止,但是开发人员立即通知存在问题,以便开发人员可以决定如何进行。
3. 次要断言不需要停止系统,甚至可能不需要通知申请。
我们可能会将这些断言的严重性水平添加到一个枚举中,然后我们可以在代码中使用。例如,枚举可能是:
typedef enum
{
assert_severity_minor,
assert_severity_moderate,
assert_severity_critical,
assert_severity_max_count
} assertseertseverity_t
然后,我们断言失败的功能可能会成为完全自定义的实现,不是来自C库断言。H模块,而是来自myAssert.h或某种效果。一个示例可能看起来像以下内容:
void assert_failed(const char expr,const char *file,int line,assertSeverity_t严重性)
{
#if assert_uart == true
uart_printf(uart1,“断言在line s in line s in line%d \ n”中失败,file,file,line);
#elif
log_append(assert,assert_string,file,line);
#endif
#将I/O线拉低以打开LED并发出断言。
dio_channelwrite(LED_ASSERT,低);
app_notify(严重性);
}
此示例只是将调用添加到app_notify,然后通过严重性,但是这种简单性非常强大。 app_notify将使应用程序知道已经检测到缺陷,然后可以决定该系统应放入哪种状态。
提示#4 - 有条件配置断言
断言是检测缺陷的简单机制,但是开发人员可以在确定适合其应用的情况下以复杂的机制创建。如果您打算在开发和生产中使用断言,则创建一系列条件来确定您的断言的运作方式可能很有用。例如,您可能会创建允许输出映射到的条件:
· uart
· 调试控制台
· 日志
也可以有一些条件可以完全禁用断言功能或将收集并提供给日志的信息。开发人员在开发过程中可能会有不同的功能,而他们在生产中想要的功能可能会有不同的功能。重要的是要从主张能力中获得什么来思考,并设计最简单的功能。您做的越复杂,出现问题的机会就越大。
采取您的下一步
断言是捕获错误的强大工具,但是在实时系统中,滥用它们可能会引起严重的问题。安全地使用断言:
1. 避免将主张放在启动代码中。
2. 使用视觉指示灯(例如LED)向早期失败发出信号。
3. log未能用于诊断或非易失性内存。
4. 通知申请,以便可以适当处理失败。
5. 有条件地为开发与生产构建配置断言。
通过这些提示,您可以实现实时断言,以帮助您检测缺陷,而不会损害系统的安全性或稳定性。