新增平台无关渲染模块 mag160c_render + Linux 参考程序 + 移植评估文档
- csdk/include/mag160c/mag160c_render.h + csdk/src/mag160c_render.c: 官方渲染管线(快门提取/FFC 状态机/ref 采集/表重建/NUC/盲元/窗口/ LUT(0x8c)/gray/2x/调色板)封装为无平台依赖 API,FFC 命令经回调发出 - csdk/tools/mag160c_linux_demo.c:Linux 风格参考程序(libusb + render API + 显示后端抽象 fbdev/DRM/LVGL),Windows 可编译验证(BMP 输出) - 修复帧组装:第二段 bulk 读 38428B(像素+尾),尾部含快门字 实测 shutter=31492 sel=2 stats 正常 - docs/linux_port_plan.md:芯片选型(推荐 T113-S3 QFP128 非 BGA ~20元)、 动画库(LVGL+rlottie 参考 LiThermal)、显示方案、接口抽象设计 - csdk/README 增加 Linux 移植章节
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
/* mag160c_linux_demo.c - Linux-oriented reference app.
|
||||
*
|
||||
* Uses the platform-independent mag160c_render API. On Windows this
|
||||
* builds/runs too (no GDI): output is written to thermal.bmp every 30
|
||||
* frames. On Linux the same code path is used; only the display
|
||||
* backend differs (fbdev/DRM/LVGL - see DISPLAY_BACKEND below).
|
||||
*
|
||||
* Build (Windows, same toolchain as demo3):
|
||||
* gcc -O2 -w -DMAG160C_STATIC -I"csdk\third_party\libusb\win64"
|
||||
* -I"csdk\include" -I"csdk\src" -o build-artifacts\mag160c_linux_demo.exe
|
||||
* csdk\tools\mag160c_linux_demo.c csdk\src\mag160c_render.c
|
||||
* csdk\src\mag160c_ir.c csdk\src\mag160c_frame.c csdk\src\mag160c_temp.c
|
||||
* csdk\src\mag160c_tcm.c csdk\src\mag160c_error.c csdk\src\mag160c_display.c
|
||||
* csdk\third_party\libusb\win64\libusb-1.0.x64.a
|
||||
* Build (Linux):
|
||||
* gcc -O2 -Wall csdk/tools/mag160c_linux_demo.c csdk/src/mag160c_render.c
|
||||
* csdk/src/mag160c_ir.c csdk/src/mag160c_frame.c csdk/src/mag160c_temp.c
|
||||
* csdk/src/mag160c_tcm.c csdk/src/mag160c_error.c csdk/src/mag160c_display.c
|
||||
* -lusb-1.0 -lpthread -lm -o mag160c_linux_demo
|
||||
*/
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define SLEEP_MS(ms) Sleep(ms)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#define SLEEP_MS(ms) usleep((ms) * 1000)
|
||||
#endif
|
||||
#include <libusb.h>
|
||||
#include "mag160c/mag160c.h"
|
||||
#include "mag160c/mag160c_render.h"
|
||||
#include "mag160c/mag160c_display.h"
|
||||
|
||||
#define W 160
|
||||
#define H 120
|
||||
#define OUT_W 320
|
||||
#define OUT_H 240
|
||||
|
||||
static libusb_context *g_ctx;
|
||||
static libusb_device_handle *g_h;
|
||||
|
||||
static int sendcmd(unsigned magic, unsigned param, int len) {
|
||||
unsigned char cmd[8] = {0};
|
||||
cmd[0] = (unsigned char)(magic);
|
||||
cmd[1] = (unsigned char)(magic >> 8);
|
||||
cmd[2] = (unsigned char)(magic >> 16);
|
||||
cmd[3] = (unsigned char)(magic >> 24);
|
||||
if (len >= 8) {
|
||||
cmd[4] = (unsigned char)(param);
|
||||
cmd[5] = (unsigned char)(param >> 8);
|
||||
cmd[6] = (unsigned char)(param >> 16);
|
||||
cmd[7] = (unsigned char)(param >> 24);
|
||||
}
|
||||
int xfer = 0;
|
||||
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
|
||||
unsigned char resp[0x1000];
|
||||
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ffc_cb(void *user, int param) {
|
||||
(void)user;
|
||||
if (g_h) sendcmd(0x6bb6b672, (unsigned)param, 8);
|
||||
}
|
||||
|
||||
/* ---- display backend abstraction ----
|
||||
* Windows: no-op (terminal stats). Linux: implement one of:
|
||||
* A. fbdev: open("/dev/fb0"), mmap, memcpy RGB (convert to RGB565)
|
||||
* B. DRM/KMS: drmModeSetCrtc + dumb buffer
|
||||
* C. LVGL: lv_img_set_src + lv_obj_invalidate (best for UI/animation)
|
||||
*/
|
||||
static void display_rgb(const uint8_t *rgb, int w, int h) {
|
||||
(void)rgb; (void)w; (void)h;
|
||||
/* Linux example (fbdev RGB565):
|
||||
* static int fd = -1; static unsigned char *fb; static int fbw, fbh;
|
||||
* ... open/mmap once, then:
|
||||
* for (y) for (x) { u16 v = ((rgb[y*w*3+x*3]>>3)<<11) |
|
||||
* ((rgb[y*w*3+x*3+1]>>2)<<5) |
|
||||
* (rgb[y*w*3+x*3+2]>>3);
|
||||
* *(u16*)(fb + y*fbw*2 + x*2) = v; }
|
||||
*/
|
||||
}
|
||||
|
||||
static void save_bmp(const char *path, const uint8_t *rgb, int w, int h) {
|
||||
uint32_t sz = 54 + (uint32_t)w * h * 3;
|
||||
uint8_t bmp[54];
|
||||
memset(bmp, 0, 54);
|
||||
bmp[0] = 'B'; bmp[1] = 'M';
|
||||
bmp[2] = (uint8_t)sz; bmp[3] = (uint8_t)(sz >> 8);
|
||||
bmp[4] = (uint8_t)(sz >> 16); bmp[5] = (uint8_t)(sz >> 24);
|
||||
bmp[10] = 54; bmp[14] = 40;
|
||||
bmp[18] = (uint8_t)w; bmp[19] = (uint8_t)(w >> 8);
|
||||
bmp[20] = (uint8_t)(w >> 16); bmp[21] = (uint8_t)(w >> 24);
|
||||
bmp[22] = (uint8_t)h; bmp[23] = (uint8_t)(h >> 8);
|
||||
bmp[24] = (uint8_t)(h >> 16); bmp[25] = (uint8_t)(h >> 24);
|
||||
bmp[26] = 1; bmp[28] = 24;
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return;
|
||||
fwrite(bmp, 1, 54, f);
|
||||
for (int y = h - 1; y >= 0; --y)
|
||||
fwrite(rgb + (size_t)y * w * 3, 1, (size_t)w * 3, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *ddt = argc > 1 ? argv[1] : "mag160c_official.ddt";
|
||||
libusb_init(&g_ctx);
|
||||
|
||||
mag160c_render_cfg_t cfg;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.width = W;
|
||||
cfg.height = H;
|
||||
cfg.ddt_path = ddt;
|
||||
cfg.ffc_period = 1800;
|
||||
cfg.ffc_drift = 250;
|
||||
cfg.startup_force_ffc = 1;
|
||||
cfg.warm_frames = 20;
|
||||
cfg.cdf_pivot_75 = 1;
|
||||
cfg.ffc_cb = ffc_cb;
|
||||
mag160c_render_t *render = NULL;
|
||||
if (mag160c_render_init(&render, &cfg) != MAG160C_OK) {
|
||||
fprintf(stderr, "render init failed (need %s)\n", ddt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ok = 0;
|
||||
for (int attempt = 0; attempt < 4 && !ok; ++attempt) {
|
||||
if (attempt > 0) {
|
||||
if (g_h) libusb_reset_device(g_h);
|
||||
if (g_h) { libusb_close(g_h); g_h = NULL; }
|
||||
SLEEP_MS(2500);
|
||||
}
|
||||
libusb_device **list = NULL;
|
||||
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
|
||||
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
|
||||
struct libusb_device_descriptor d;
|
||||
libusb_get_device_descriptor(list[i], &d);
|
||||
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
|
||||
}
|
||||
libusb_free_device_list(list, 1);
|
||||
if (!g_h) continue;
|
||||
libusb_set_configuration(g_h, 2);
|
||||
libusb_set_configuration(g_h, 1);
|
||||
if (libusb_claim_interface(g_h, 0) != 0) { libusb_close(g_h); g_h = NULL; continue; }
|
||||
if (sendcmd(0x6bb6b66b, 0, 4)) continue;
|
||||
if (sendcmd(0x6bb6b66c, 0, 4)) continue;
|
||||
if (sendcmd(0x6bb6b66f, 0, 4)) continue;
|
||||
if (sendcmd(0x6bb6b672, 0, 8)) continue;
|
||||
SLEEP_MS(100);
|
||||
if (sendcmd(0x6bb6b672, 0, 8)) continue;
|
||||
SLEEP_MS(300);
|
||||
if (sendcmd(0x6bb6b673, 0, 4)) continue;
|
||||
SLEEP_MS(700);
|
||||
ok = 1;
|
||||
}
|
||||
if (!ok) {
|
||||
fprintf(stderr, "camera init failed\n");
|
||||
mag160c_render_destroy(render);
|
||||
libusb_exit(g_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint8_t rgb[OUT_W * OUT_H * 3];
|
||||
static uint8_t frame[40000];
|
||||
unsigned prev = 0xffffffff;
|
||||
unsigned long nframe = 0;
|
||||
for (;;) {
|
||||
uint8_t hdr[64];
|
||||
int xfer = 0;
|
||||
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 500) || xfer < 28)
|
||||
continue;
|
||||
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
|
||||
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
|
||||
if (m != 0x1bb1b11b) continue;
|
||||
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
|
||||
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
|
||||
if (c == prev) continue;
|
||||
prev = c;
|
||||
if (libusb_bulk_transfer(g_h, 0x81, frame, sizeof(frame), &xfer, 500) || xfer < 38400)
|
||||
continue;
|
||||
nframe++;
|
||||
|
||||
/* assemble the full frame (28B header + pixels + tail) for the
|
||||
* renderer: the second bulk read carries 38400 px + 28B tail */
|
||||
static uint8_t full[28 + 40000];
|
||||
memcpy(full, hdr, 28);
|
||||
memcpy(full + 28, frame, 38400);
|
||||
if (xfer > 38400)
|
||||
memcpy(full + 28 + 38400, frame + 38400, (size_t)(xfer - 38400));
|
||||
/* feed the platform-independent pipeline */
|
||||
mag160c_error_t rc = mag160c_render_frame(render, full, 1, rgb);
|
||||
if (rc != MAG160C_OK) continue; /* FFC window / startup: frozen */
|
||||
|
||||
display_rgb(rgb, OUT_W, OUT_H);
|
||||
if (nframe % 30 == 0) {
|
||||
char path[64];
|
||||
snprintf(path, sizeof(path), "thermal_%05lu.bmp", nframe);
|
||||
save_bmp(path, rgb, OUT_W, OUT_H);
|
||||
int lo, hi, fmin, fmax, mean, std;
|
||||
mag160c_render_get_stats(render, &lo, &hi, &fmin, &fmax, &mean, &std);
|
||||
printf("f=%lu shutter=%d sel=%d win=[%d,%d] stats=(%d..%d m%d s%d) ref=%d\n",
|
||||
nframe, mag160c_render_get_shutter(render),
|
||||
mag160c_render_get_endpoint(render), lo, hi,
|
||||
fmin, fmax, mean, std, mag160c_render_has_reference(render));
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
mag160c_render_destroy(render);
|
||||
libusb_close(g_h);
|
||||
libusb_exit(g_ctx);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user