#usb-device #nxp #usb-serial #embedded-hal #imxrt #no-std

无 std imxrt-log

为 i.MX RT 处理器提供的日志扩展

3 个版本

0.1.2 2024 年 6 月 6 日
0.1.1 2023 年 3 月 14 日
0.1.0 2023 年 1 月 5 日

嵌入式开发 中排名第 1490

Download history 53/week @ 2024-04-22 70/week @ 2024-04-29 51/week @ 2024-05-06 56/week @ 2024-05-13 100/week @ 2024-05-20 116/week @ 2024-05-27 183/week @ 2024-06-03 46/week @ 2024-06-10 129/week @ 2024-06-17 60/week @ 2024-06-24 21/week @ 2024-07-08 59/week @ 2024-07-15 46/week @ 2024-07-22 30/week @ 2024-07-29 41/week @ 2024-08-05

每月下载量 180
5 个包中(直接使用 3 个)中使用

MIT/Apache 许可协议

160KB
2.5K SLoC

imxrt-log

imxrt-hal 的扩展,用于启用设备日志。 imxrt-log 支持两个日志前端

  • defmt 用于高效日志记录。
  • log 用于基于文本的日志记录。

它支持两种不同的后端外设

  • 高速 USB 串行设备。
  • 带有 DMA 的串行 UART。

有关更多信息,请参阅 API 文档。要尝试在硬件上使用各种前端和后端,请使用与 imxrt-hal 一起维护的 *_logging 示例。


lib.rs:

为 i.MX RT 处理器提供的日志扩展。

imxrt-log 支持两个日志前端

  • defmt 用于高效日志记录。
  • log 用于基于文本的日志记录。

有关 defmt 和 [log] 模块的信息,请参阅。

imxrt-log 基于 imxrt-hal 硬件抽象层 (HAL) 并提供两个外设后端

  • LPUART 带DMA
  • USB 串行(CDC)设备

混合和匹配这些前端和后端以将日志集成到您的 i.MX RT 处理器中。要了解每个前端的不同之处,请参阅包文档。继续阅读以了解如何构建此包以及每个后端的不同之处。

构建

由于此包依赖于 imxrt-hal,因此此包具有与 imxrt-hal 相同的构建要求。要了解如何构建此包,请参阅 HAL 文档。基本上,如果您可以构建 HAL,则可以构建此包。

本软件包使用 critical-section 确保对日志生产者的安全并发访问。为了构建它,您必须为您的系统选择正确的临界区实现。有关更多信息,请参阅 critical-section 文档。

设计

日志前端将日志帧放置在循环缓冲区中。编译和运行时过滤器防止日志消息格式化和复制。有关前端设计细节,请参阅每个前端模块的文档。

后端从该循环缓冲区读取数据,并异步地将数据从内存中传输出去。后端可以将其实现作为其数据缓冲的一部分。

循环缓冲区是限制性资源。一旦使用前端-后端组合初始化了日志记录器,就不能初始化其他任何日志记录器。

后端使用

LPUART 和 USB 后端提供了一个一致的接口来驱动日志。初始化前端和后端对后,您将收到一个 Poller 对象。为了移动日志消息,您必须偶尔调用 poller 对象上的 poll()

API 允许您启用或禁用在传输完成时触发的中断。根据后端的不同,中断可能会定期触发。如果中断定期触发,则可以使用中断偶尔调用 poll()

后端有一些行为和性能差异。它们的初始化方式也不同。下一节将描述这些差异。

LPUART 带DMA

带有 DMA 的 LPUART 实现使用 DMA 传输通过 LPUART 传输日志消息。简而言之,

  • 在初始化日志记录器之前初始化您的 LPUART。
  • 如果您启用中断,请定义您的中断处理程序。
  • 携带自己的计时器来调用 poll()
  • 它使用的内存比 USB 少。

初始化。日志初始化例程需要来自 imxrt-hal 的 LPUART 对象。在提供给日志初始化例程之前,使用波特率、奇偶校验位等配置您的 Lpuart 对象。

初始化例程还需要一个 DMA 通道。任何 DMA 通道都可以。实现完全配置 DMA 通道,因此不需要您进行配置。

中断。如果您启用中断(请参阅 Interrupts),则在每次传输完成后,DMA 通道将发出中断。您必须调用 poll() 以清除中断。实现不会接触 LPUART 中断。

计时器。LPUART 后端启用的中断不能定期触发。因此,您负责定期调用 poll()。考虑使用来自 imxrt-hal 的 PIT 或 GPT 计时器来帮助解决这个问题,或者考虑在软件循环中调用 poll()

缓冲区管理。实现直接从日志消息缓冲区执行 DMA 传输。这意味着没有日志消息的中间缓冲区。实现一旦传输完成,就释放循环缓冲区中的日志消息。

USBD

USB 设备实现通过向 USB 主机提供一个串行(USB CDC)类来通过 USB 传输日志消息。简而言之,

  • 只需向日志初始化例程提供 USB 寄存器块。
  • 如果启用中断,请定义您的中断处理程序。
  • 您可能不需要自己的定时器。
  • 它使用的内存比LPUART多。

初始化。日志初始化例程处理所有外设配置。您只需提供USB寄存器块实例;imxrt-hal可以帮助您完成此操作。

默认情况下,初始化例程配置了一个高速USB设备,其批量端点最大包大小为512字节。您可以通过后续讨论的构建时环境变量更改这些设置。

中断。如果您启用中断(请参阅Interrupts),USB设备控制器在每次传输完成时断言其中断。它还启用了一个USB管理的定时器,定期触发中断。您必须调用poll()以清除这些中断条件。

定时器。如果您启用中断,相关的USB中断会定期触发。您可以使用此功能定期调用poll(),而无需使用任何其他定时器或软件循环。

定时器有一个默认间隔。您可以通过每个日志初始化例程配置此间隔。

如果您未启用中断,您需要定期调用poll()。请参阅LPUART 定时器讨论中的建议。

缓冲区管理。实现会从环形缓冲区中复制数据并将其放置在中间传输缓冲区中。一旦此复制完成,实现就会从环形缓冲区释放日志帧,并从中间缓冲区开始USB传输。中间缓冲区的要求是USB驱动程序实现的细节,这增加了此后端内存的需求。

示例

使用USB后端最容易,因为它具有内置的定时器,并且实现处理所有外设初始化。下面的示例显示了中断驱动的USB日志记录器。它使用imxrt-hal API准备日志记录器。

use imxrt_log::defmt; // <-- Change 'defmt' to 'log' to change the frontend.
use imxrt_hal as hal;
use imxrt_ral as ral;

use ral::interrupt;
#[cortex_m_rt::interrupt]
fn USB_OTG1() {
    static mut POLLER: Option<imxrt_log::Poller> = None;
    if let Some(poller) = POLLER.as_mut() {
        poller.poll();
    } else {
        let poller = initialize_logger().unwrap();
        *POLLER = Some(poller);
        // Since we enabled interrupts, this interrupt
        // handler will be called for USB traffic and timer
        // events. These are handled by poll().
    }
}

/// Initialize a USB logger.
///
/// Returns `None` if any USB peripheral instance is taken,
/// or if initialization fails.
fn initialize_logger() -> Option<imxrt_log::Poller> {
    let usb_instances = hal::usbd::Instances {
        usb: unsafe { ral::usb::USB1::instance() },
        usbnc: unsafe { ral::usbnc::USBNC1::instance() },
        usbphy: unsafe { ral::usbphy::USBPHY1::instance() },
    };
    // Initialize the logger, and ensure that it triggers interrupts.
    let poller = defmt::usbd(usb_instances, imxrt_log::Interrupts::Enabled).ok()?;
    Some(poller)
}

// Elsewhere in your code, configure USB clocks. Then, pend the USB_OTG1()
// interrupt so that it fires and initializes the logger.
let mut ccm = unsafe { ral::ccm::CCM::instance() };
let mut ccm_analog = unsafe { ral::ccm_analog::CCM_ANALOG::instance() };
hal::ccm::analog::pll3::restart(&mut ccm_analog);
hal::ccm::clock_gate::usb().set(&mut ccm, hal::ccm::clock_gate::ON);

cortex_m::peripheral::NVIC::pend(interrupt::USB_OTG1);
// Safety: interrupt handler is self contained and safe to unmask.
unsafe { cortex_m::peripheral::NVIC::unmask(interrupt::USB_OTG1) };

// After the USB device enumerates and configures, you're ready for
// logging.
::defmt::info!("Hello world!");

有关使用RTIC的高级示例,请参阅在imxrt-hal存储库中维护的rtic_logging示例。此示例允许您轻松探索所有前端-后端组合,并且它在各种i.MX RT开发板上工作。

包配置

您可以在编译时配置此包。

  • 二进制配置使用功能标志。
  • 变量配置使用编译期间设置的编译环境变量。

下表描述了包功能标志。默认功能使得您可以轻松使用所有包功能。为了减少依赖性,请禁用此包的默认功能,然后选择性地启用前端和后端。

功能标志 描述 默认启用?
defmt 启用defmt日志前端
log 启用log日志前端
lpuart 启用LPUART后端
usbd 启用USB设备后端

此包没有前端-后端组合就不会特别有趣,因此不支持此配置。任何未列出的功能都视为实现细节,可能会不通知而更改。

环境变量提供了额外的配置钩子。下表描述了受支持的配置变量及其对构建的影响。

环境变量 描述 默认值 接受的值
IMXRT_LOG_USB_BULK_MPS 批量端点最大包大小,以字节为单位。 512 8、16、32、64、512之一
IMXRT_LOG_USB_SPEED 指定高速(USB2)或全速(USB 1.1)USB设备。 HIGH 可以是HIGHFULL
IMXRT_LOG_BUFFER_SIZE 指定日志消息缓冲区大小,以字节为单位。 1024 2的整数次幂

注意

  • IMXRT_LOG_USB_* 始终被允许。如果 usbd 被禁用,那么 IMXRT_LOG_USB_* 配置将不起作用。
  • 如果 IMXRT_LOG_USB_SPEED=FULL,那么 IMXRT_LOG_USB_BULK_MPS 不能是 512。另一方面,如果 IMXRT_LOG_USB_SPEED=HIGH,那么 IMXRT_LOG_USB_BULK_MPS 必须是 512。
  • IMXRT_LOG_USB_BULK_MPSIMXRT_LOG_BUFFER_SIZE 都会影响内部管理的缓冲区大小。如果空间紧张,减少这些数字以回收内存。

限制

尽管它使用了 critical-section,但这个日志包可能不是为立即在多核系统(如 i.MX RT 1160 和 1170 微控制器)中使用而设计的。值得注意的是,这些处理器没有为跨核心确保日志生产者安全共享访问的临界区实现。此外,目前还不清楚如何为这些系统构建嵌入式 Rust 应用程序。

尽管存在这些限制,但在多核微控制器上使用此包可能还是可能的,但您需要将其视为两个单核微控制器。具体来说,您需要构建两个二进制文件——每个核心一个,每个核心都有自己的数据内存区域——并且每个核心都需要使用其自己的、独特的外围设备进行传输。然后,选择单核 critical-section 实现,如由 cortex-m 提供的实现。

依赖项

~0.6–7.5MB
~199K SLoC