RA8P1 摄像头调通测评(Titan_npu_ai_face_detection)

参考文档链接:https://rt-thread-studio.github.io/sdk-bsp-ra8p1-titan-board/latest/multimedia/Titan_display_camera_ceu/README_zh.html#id1


一、必要知识

  1. 摄像头链路基本结构

    • 传感器(OV5640)通过 I2C 配置寄存器,通过 并行 DVP/CEU 输出图像数据。
    • RA8P1 的 CEU 负责采集数据写入内存缓冲区。
  2. 显示链路是另一条

    • 我用的是 ST7789 SPI LCD(240x240),不是板载并行 RGB LCD。
    • 所以显示要走 SPI + ST7789 驱动,不能继续用 lcd_port.h + D2D 的并行 LCD 路径。
  3. 推理链路(NPU)

    • 图像从 RGB565 转灰度 + resize 到 192x192,再量化喂给模型。
    • 模型输出做反量化、解码、NMS,最后得到检测框。
  4. Cache 和内存对齐

    • CEU 写 SDRAM 后,CPU 要读就必须 invalidate DCache,否则读到旧数据。
    • 图像 buffer 要对齐到 32 字节,避免 cache 与 DMA 冲突。

二、工作原理

摄像头(OV5640) --I2C配置--> 传感器
                   |
                   | 并行DVP
                   v
RA8P1 CEU --> RGB565帧写入buffer
                   |
                   | 1) 显示链路:中心裁剪 → ST7789 SPI 刷屏
                   | 2) 推理链路:RGB565→灰度/缩放→量化→NPU推理→解码/NMS→画框
  • 显示链路和推理链路都用同一帧缓冲。
  • 没检测到人脸时,仍然要刷屏(我加了固定小绿框方便确认刷新)。

三、FSP 底层配置详解(Smart Configurator)

在打开 RT-Thread Studio 的 RA Smart Configurator 后,我们需要重点配置 CEU (摄像头采集)GPT (摄像头时钟)I2C (摄像头控制)SPI (屏幕驱动) 这四个部分。

1. 配置 CEU (Capture Engine Unit) - 负责采集图像

CEU 负责将 DVP 接口传来的并行数据通过 DMA 搬运到内存。

  • 操作:在 Stacks 选项卡中,New Stack -> Graphics -> CEU (r_ceu)。

  • 关键属性配置

  • Name: g_ceu_qvga

  • Capture Resolution:

  • Horizontal: 320

  • Vertical: 240

  • Data Bus: 8-bit

  • Byte Swapping: 全部勾选 (Swap 8/16/32-bit)。

  • 注意:这一步至关重要!摄像头传来的 RGB565 大小端序往往和 RA8 内存不一致,不勾选会导致图像颜色错乱(如红蓝颠倒)。

  • Sync Polarity: 为 High

  • Callback: g_ceu_callback

image.png

2. 配置 GPT (General PWM Timer) - 提供 XCLK 时钟

摄像头需要一个外部时钟信号 (XCLK/MCLK) 才能工作,我们使用定时器 PWM 生成 24MHz 时钟。

  • 操作:在 Stacks 选项卡中,New Stack -> Timers -> Timer, General PWM (r_gpt)。

  • 关键属性配置

  • Name: g_timer4

  • Mode: Saw-wave PWM (锯齿波 PWM)

  • Period: 24000,Unit 选 Kilohertz (即 24MHz)

  • Output:

  • GTIOCA Output Enabled: True (开启输出)

  • Duty Cycle: 50 (50% 占空比,产生方波)

  • Pins: 确认 GTIOCA 绑定到 PA05 (CAM_XCLK)。

image.png

3. 配置 I2C 在RT-Thread里面配置

在组件库里面开启下面中的设置
image.png
image.png

4. 配置 SPI - 负责 ST7789 屏幕显示

  • 操作:在 Stacks 选项卡中,New Stack -> Connectivity -> SPI (r_spi)。
  • 关键属性配置
  • Channel: 0 (根据你的原理图连接选择)
  • Data Bit Width: 8 Bits
  • Bitrate: 建议 20MHz 或更高 (保证刷屏帧率)
  • Support for using DTC: 建议 Disabled (开启 DTC 在某些情况下会导致传输卡死,纯中断模式刷屏虽然 CPU 占用稍高但最稳定)。

image.png

5. 检查引脚 (Pins Configuration)

最后务必去 Pins 标签页确认引脚没有冲突,并且连接正确。

  • Timers -> GPT4: 确认 GTIOC4A 是 PA05
  • Graphics -> CEU: 确认 VSD/HSD/VCLK 及数据线 D0-D7 已分配且无冲突。

image.png


配置完成后,点击右上角 “Generate Project Content” 生成代码,然后回到 RT-Thread Studio 进行编译。


四、工程配置与代码流程

  1. 工程入口流程

    • hal_entry() 中完成:st7789_init() → 摄像头初始化 → NPU 使能 → 主循环
  2. 摄像头参数

    • PIXFORMAT_RGB565
    • FRAMESIZE_QVGA (320x240)
  3. 显示参数

    • ST7789 240x240
    • 画面以 中心裁剪 的方式贴到屏幕
  4. 关键代码点

    • sensor_snapshot() 获取帧
    • SCB_InvalidateDCache_by_Addr() 刷新 cache
    • st7789_blit_rgb565_center() 刷屏
    • draw_rect_rgb565() 画检测框

代码片段(摄像头初始化 + ST7789 初始化)

void hal_entry(void)
{
    if (st7789_init() != 0)
    {
        LOG_E("ST7789 init failed");
        return;
    }

    sensor_init();
    sensor_reset();
    sensor_set_pixformat(PIXFORMAT_RGB565);
    sensor_set_framesize(FRAMESIZE_QVGA);
    ...
}

五、调试遇到的问题与解决办法

1. 问题现象

  • 终端提示“not found any sensors !!”或无数据,或者 LCD 无法刷出摄像头图像。

3. 解决建议

不要动sensor.c的这部分注释

image.png

然后改造原工程代码的hal_entry.c 建议不要自己重新写一个因为很可能会导致摄像头的数据一直处于忙碌的状态
image.png


六、问题现象与排查过程

1) 现象

  • 推理日志正常输出(detect box num: 0),但屏幕黑。
  • 上电偶尔看见一次彩条,复位后一直黑。

串口 log(能看到 detect box num 和 time elapsed)

detect box num: 0
Time elapsed: 120 ms
detect box num: 1
Time elapsed: 122 ms

2) 排查思路

  • 先确认 程序有没有在跑 → 串口日志一直输出
  • 再确认 LCD 初始化有没有成功st7789_init() 打印成功
  • 进一步确认 是否真正刷屏 → 上电显示彩条 + OK
  • 如果还是黑,检查 摄像头帧是否在变化 → 每秒打印像素值

七、最终修复办法

修复 1:显示链路切到 ST7789

  • 原工程 hal_entry.c 走的是 并行 LCD + D2D 的路径,和我实际硬件不匹配。
  • 我改成 ST7789 SPI 刷屏

修复 2:上电自检画面

  • 增加“彩条 + OK”自检画面,验证 LCD 不是黑屏。
  • 这样一眼能确认 LCD 是否正常工作。

上电彩条 + OK(自检代码)
image.png

static void st7789_show_boot_test(void)
{
    static const uint16_t bar_colors[8] = {
        0xFFFF, 0xFFE0, 0x07FF, 0x07E0,
        0xF81F, 0xF800, 0x001F, 0x0000
    };

    int w = ST7789_LCD_WIDTH;
    int h = ST7789_LCD_HEIGHT;

    st7789_set_window(0, 0, (uint16_t)(w - 1), (uint16_t)(h - 1));
    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            uint16_t color = bar_colors[(x * 8) / w];
            s_lcd_line_buf[x * 2] = (uint8_t)(color >> 8);
            s_lcd_line_buf[x * 2 + 1] = (uint8_t)(color & 0xFF);
        }
        st7789_write_pixels(s_lcd_line_buf, (uint32_t)(w * 2));
    }

    st7789_draw_text((w - 2 * 5 * 5 - 5) / 2, (h - 7 * 5) / 2, "OK",
                     0x0000, 0xFFFF, 5);
}

修复 3:栈溢出导致硬 Fault

  • st7789_draw_text() 里有 2.5KB 的大数组,放在栈上容易崩。
  • 我把它改成 static buffer,避免主线程栈溢出。
int st7789_draw_text(int x, int y, const char *text, uint16_t color, uint16_t bg, int scale)
{
    ...
    static uint8_t buf[5 * 7 * 6 * 6 * 2];
    ...
}

修复 4:缓存失效

  • sensor_snapshot() 后加入 SCB_InvalidateDCache_by_Addr(),确保 CPU 读到最新帧。
sensor_snapshot(&sensor, g_image_rgb565_sdram_buffer, 0);
#if (BSP_CFG_DCACHE_ENABLED)
SCB_InvalidateDCache_by_Addr((uint32_t *) g_image_rgb565_sdram_buffer,
                             CAM_WIDTH * CAM_HEIGHT * 2);
#endif

八、最终结果

  • 上电显示彩条+OK
  • 进入识别后屏幕能看到摄像头画面
  • 识别到人脸后能画出检测框

image.png

int32_t kept = nms_filter(pool, total, NMS_THRESH);
int32_t out_n = MIN(kept, MAX_BOXES);

for (int i = 0; i < out_n; i++)
{
    draw_rect_rgb565((uint16_t *)g_image_rgb565_sdram_buffer,
                     CAM_WIDTH, CAM_HEIGHT, &pool[i], 0x07E0, 2);
}

st7789_blit_rgb565_center((const uint16_t *)g_image_rgb565_sdram_buffer,
                          CAM_WIDTH, CAM_HEIGHT);

运行时串口日志(detect box num)

detect box num: 0
Time elapsed: 120 ms
detect box num: 1
Time elapsed: 118 ms

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐