2. 1.1_11 节_在 LCD 上显示摄像头图像 1_效果_框架_准备工作

本文详细介绍嵌入式系统开发全过程,包括虚拟机准备、工具链安装、内核编译及补丁应用、文件系统配置等关键步骤。特别关注于ARM架构下的Linux内核3.4.2版本,涵盖网卡驱动、LCD驱动更新及USB摄像头UVC驱动优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

准备工作:

1.准备虚拟机

2.安装工具链

3.编译内核

4.文件系统:

5.用新内核、新文件系统启动开发板


准备工作:

1.准备虚拟机


2.安装工具链

编译内核前先看看工具链版本 arm-linux-gcc -v , 3.4.5 是不可以的。
sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C /
设置环境变量:
sudo vi /etc/environment :

将 PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/tools/gcc-3.4.5-glibc-
2.3.6/bin"
修改
为 PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin"
重启后生效。
echo $PATH //查看环境变量
不重启改变环境变量的方法:
export  PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin
arm-linux-gcc -v //检测版本变为 4.3.2
有时经常报:export : command not found

解决:export PATH=/usr/local/arm/4.3.2/bin:$PATH

后来发现把4.3.2导出时放最前面是可以的

3.编译内核

① 首先解压缩内核:

tar xjf linux-3.4.2.tar.bz2
cd linux-3.4.2

② 打补丁:

可以使用我们制作好的补丁:
linux-3.4.2_camera_jz2440.patch
linux-3.4.2_camera_mini2440.patch
linux-3.4.2_camera_tq2440.patch

patch -p1 <  ../linux-3.4.2_camera_jz2440.patch

③ 编译内核:

cp config_ok .config
make uImage


 

3.1另一种内核打补丁、编译的方法:

(重要)

内核、补丁、工具链

也可以从毕业班的内核补丁、驱动程序,自己修改、编译:

tar xjf linux-3.4.2.tar.bz2
cd linux-3.4.2
patch -p1 < ../linux-3.4.2_100ask.patch

修改内核:

① 替换s3c2410fb,修改Makefile

把 lcd_4.3.c 复制到 /work/projects/linux-3.4.2/drivers/video中
修改/work/projects/linux-3.4.2/drivers/video/Makefile

/s3c2410fb 找到并注释点,换成lcd_4.3.c

#obj-$(CONFIG_FB_S3C2410)         += s3c2410fb.o
obj-$(CONFIG_FB_S3C2410)          += lcd_4.3.o

② 修改网卡驱动:

把dm9dev9000c.c、dm9000.h复制到/work/projects/linux-3.4.2/drivers/net/ethernet/davicom
修改/work/projects/linux-3.4.2/drivers/net/ethernet/davicom/Makefile
dm9000.o 替换为 dm9dev9000c.o

③ 内核菜单修改

/work/projects/LCD-projects/linux-3.4.2

cp config_ok .config
make menuconfig 

// 如果你使用的是百问网自制的USB摄像头,
// 还需要参考第2课1.1.9节视频修改UVC驱动

搜索 /usb_video

修改如下

cp config_ok .config //config_ok中并没有加入UVC驱动程序,所以需要设置
make menuconfig
   <*> Multimedia support  --->
       <*>   Video For Linux 
       [*]   Video capture adapters (NEW)  --->
              [*]   V4L USB devices (NEW)  --->
                   <*>   USB Video Class (UVC) 

 

④ 内核源码修改

参考 How to build up UVC Driver on Linux_1.1.pdf
UVC_driver.c -- static struct usb_device_id uvc_ids[]中
添加:

/*iPassion USB Web Camera */
        { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
          | USB_DEVICE_ID_MATCH_INT_INFO,
          .idVendor = 0x1B3B,
          .idProduct = 0x2970,/*If you use iP2977, then type "0x2977" */
          .bInterfaceClass = USB_CLASS_VIDEO,
          .bInterfaceSubClass = 1,
          .bInterfaceProtocol = 0,
          .driver_info = UVC_QUIRK_PROBE_MINMAX
          | UVC_QUIRK_IGNORE_SELECTOR_UNIT},

    /*iPassion USB Web Camera */
        { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
          | USB_DEVICE_ID_MATCH_INT_INFO,
          .idVendor = 0x1B3B,
          .idProduct = 0x2977,/*If you use iP2977, then type "0x2977" */
          .bInterfaceClass = USB_CLASS_VIDEO,
          .bInterfaceSubClass = 1,
          .bInterfaceProtocol = 0,
          .driver_info = UVC_QUIRK_PROBE_MINMAX
          | UVC_QUIRK_IGNORE_SELECTOR_UNIT},

Improve compatibility (增加相容性):
uvc_video.c -- static int uvc_video_decode_start 函数修改
 

//__u8 fid;
static __u8 fid;//修改成 static 型態

if(stream->dev->udev->descriptor.idVendor == 0x1B3B)
    {   
        if ( len >= 16 ) // have data in buffer
        {
            // 資料必須從data[12]開始判斷,是因為前面的資料是封包專用
            if ( (data[12]==0xFF && data[13]==0xD8 && data[14]==0xFF) ||
                (data[12]==0xD8 && data[13]==0xFF && data[14]==0xC4))
            {
                if(stream->last_fid)
                fid &= ~UVC_STREAM_FID;
                else
                fid |= UVC_STREAM_FID;
            }
        }
    }
    else 
    {
        fid = data[1] & UVC_STREAM_FID;
    }

uvc_video.c -- static void uvc_video_decode_data 函数修改
 

// 要修改影像資料,必須先宣告一個特別型態的指標變數,才能正確存取記憶體中的資料
    unsigned char *point_mem;
    static unsigned char *mem_temp = NULL;
    // 初始化暫存用的記憶體位置
    static unsigned int nArrayTemp_Size = 1000;

    /*ip2970/ip2977*/
    if(stream->dev->udev->descriptor.idVendor == 0x1B3B)
    {
        if(mem_temp == NULL){
            mem_temp = kmalloc(nArrayTemp_Size, GFP_KERNEL);
        }
        else if(nArrayTemp_Size <= nbytes){ // 當收到的資料長度大於上一次的資料長度,則重新分配所需的空間+
            kfree(mem_temp);
            nArrayTemp_Size += 500;
            kmalloc(nArrayTemp_Size, GFP_KERNEL);
        }
        memset(mem_temp, 0x00, nArrayTemp_Size);
        // 指向資料儲存的記憶體位置
        point_mem = (unsigned char *)mem;
        if( *(point_mem) == 0xD8 && *(point_mem + 1) == 0xFF && *(point_mem + 2) == 0xC4){
            memcpy( mem_temp + 1, point_mem, nbytes);
            mem_temp[0] = 0xFF;
            memcpy( point_mem, mem_temp, nbytes + 1);
            }
    }   

⑤编译

make uImage
cp arch/arm/boot/uImage   /work/nfs_root/uImage_new

 

 

4.文件系统:

cd /work/nfs_root
sudo tar xjf fs_mini_mdev_new.tar.bz2
sudo chown book:book fs_mini_mdev_new

5.用新内核、新文件系统启动开发板

启动开发板至UBOOT
设置UBOOT的环境变量:
set ipaddr 192.168.1.148
set bootcmd ‘nfs 32000000 192.168.1.149:/work/nfs_root/uImage_new; bootm 32000000’
set bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.1.149:/work/nfs_root/fs_mini_mdev_new ip=192.168.1.148
save
boot

环境搭建完成

我们只要完成读出数据,和转化数据部分即可,其他的在数码相框中已实现
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

import time from media.sensor import * from media.display import * from media.media import * import math from machine import UART from machine import FPIOA # ---------- 参数 ---------- LCD_W = 800 LCD_H = 480 A4_REAL_W = 210.0 # mm A4_REAL_H = 297.0 # mm FOCAL_PIX = 389 # 标定后填入 class Mode: BASIC = 0 # 默认模式:找外/内框 + 圆/三角/四边形 MIN_QUAD = 1 # 最小四边形模式:只找四边形,找最小 TILT = 2 # 倾斜模式:A4纸倾斜30-60度时测量正方形边长 current_mode = Mode.BASIC sensor = Sensor(width=1280, height=960) sensor.reset() sensor.set_framesize(width=320, height=240) sensor.set_pixformat(Sensor.RGB565) Display.init(Display.ST7701, width=LCD_W, height=LCD_H, to_ide=True) MediaManager.init() sensor.run() sensor.set_hmirror(True) fpioa = FPIOA() fpioa.set_function(5, fpioa.UART2_TXD) fpioa.set_function(6, fpioa.UART2_RXD) uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE) clk = time.clock() def is_a4_like(rect): w, h = rect.w(), rect.h() ratio = max(w, h) / min(w, h) return 1.25 < ratio < 1.55 def is_a4_like_tilt(rect): w, h = rect.w(), rect.h() area = w * h ratio = max(w, h) / min(w, h) return (0.9 < ratio < 3.8) and (area > 1000) def inside(inner, outer): xo, yo, wo, ho = outer.rect() for (x, y) in inner.corners(): if not (xo < x < xo + wo and yo < y < yo + ho): return False return True def dist_mm(p1, p2, current_pix_w): pix_dist = math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) return (pix_dist * A4_REAL_W) / current_pix_w def dist_mm_tilt(p1, p2, current_pix_h): pix_dist = math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) return (pix_dist * A4_REAL_H) / current_pix_h # ------------------------------------------------- # ROI 边界保护 # ------------------------------------------------- def clamp_roi(x, y, w, h, img_w, img_h): x = max(0, min(x, img_w - 1)) y = max(0, min(y, img_h - 1)) w = max(1, min(w, img_w - x)) h = max(1, min(h, img_h - y)) return (x, y, w, h) # ------------------------------------------------- # 用 draw_line() 模拟 draw_polygon() # ------------------------------------------------- def draw_poly(img, pts, color, thickness=2): n = len(pts) for i in range(n): x1, y1 = int(pts[i][0]), int(pts[i][1]) x2, y2 = int(pts[(i+1) % n][0]), int(pts[(i+1) % n][1]) img.draw_line(x1, y1, x2, y2, color=color, thickness=thickness) # -------------------- 串口命令解析 ------------------------- def parse_uart(): global current_mode if uart.any(): cmd = uart.read().decode().strip() if cmd == 'detectminQuad': current_mode = Mode.MIN_QUAD uart.write("M\n") elif cmd == 'basic': current_mode = Mode.BASIC uart.write("M\n") elif cmd == 'tilt': current_mode = Mode.TILT uart.write("M\n") while True: clk.tick() parse_uart() # 每帧先检查串口指令 img = sensor.snapshot() # ---------- BASIC 模式 ---------- if current_mode == Mode.BASIC: rects = img.find_rects(threshold=10000) rects = sorted(rects, key=lambda r: r.w()*r.h(), reverse=True) outer = inner = None if len(rects) >= 2: cand = [r for r in rects[:2] if is_a4_like_tilt(r)] if len(cand) == 2: outer, inner = cand[0], cand[1] if not inside(inner, outer): outer = inner = None dist_mm_a4 = 0 if outer and inner: img.draw_rectangle(outer.rect(), color=(255, 0, 0), thickness=2) for p in outer.corners(): img.draw_circle(p[0], p[1], 5, color=(0, 255, 0)) img.draw_rectangle(inner.rect(), color=(0, 255, 255), thickness=2) for p in inner.corners(): img.draw_circle(p[0], p[1], 5, color=(0, 0, 255)) pixel_width = outer.w() dist_mm_a4 = (A4_REAL_W * FOCAL_PIX) / pixel_width img.draw_string(5, 5, "A4:%.1f cm" % (dist_mm_a4/10), color=(255, 255, 255), scale=2) uart.write("D%d\n" % int (dist_mm_a4)) img.draw_string(5, 200, "w_px=%d" % pixel_width, color=(255, 255, 255), scale=2) # 圆/三角/四边形逻辑 shrink = 5 x0, y0, w0, h0 = inner.rect() x0, y0, w0, h0 = clamp_roi(x0 + shrink, y0 + shrink, w0 - 2*shrink, h0 - 2*shrink, img.width(), img.height()) circles = img.find_circles(threshold=3000, x_margin=10, y_margin=10, r_margin=10, r_min=2, r_max=100, r_step=2, roi=(x0, y0, w0, h0)) has_circle = False for c in circles: has_circle = True dia_mm = 2 * c.r() * A4_REAL_W / pixel_width img.draw_circle(c.x(), c.y(), c.r(), color=(0, 255, 0), thickness=2) img.draw_string(int(c.x()-10), int(c.y()-10), "C:%.1f mm" % dia_mm, color=(0, 255, 0), scale=1) uart.write("R%d\n" % int(dia_mm)) if not has_circle: gray = img.to_grayscale(roi=(x0, y0, w0, h0)) bw = gray.binary([(0, 80)],invert=False) inner_area = w0 * h0 MIN_AREA = int(inner_area * 0.1) MAX_AREA = int(inner_area * 0.7) center_x = w0 // 2 center_y = h0 // 2 blobs = sorted( bw.find_blobs([(255, 255)], pixels_threshold=100, area_threshold=MIN_AREA, roi=(0, 0, w0, h0)), key=lambda b: (b.cx() - center_x) ** 2 + (b.cy() - center_y) ** 2 ) for blob in blobs: corners = blob.min_corners() corners = [(cx + x0, cy + y0) for (cx, cy) in corners] blob_area = blob.pixels() bbox_area = 0.5 * abs(sum(corners[i][0] * corners[(i+1)%4][1] - corners[(i+1)%4][0] * corners[i][1] for i in range(4))) area_ratio = blob_area / bbox_area if bbox_area > 0 else 1 if ( area_ratio < 0.9): a = dist_mm(corners[0], corners[1], pixel_width) b = dist_mm(corners[1], corners[2], pixel_width) c = dist_mm(corners[2], corners[0], pixel_width) info = "T:%.1f/%.1f/%.1f mm" % (a, b, c) img.draw_string(int(blob.cx()+x0), int(blob.cy()+y0), info, color=(255, 255, 0), scale=1) uart.write("T%d\n" % int(a)) else: sides = [] for i in range(4): sides.append(dist_mm(corners[i], corners[(i+1)%4], pixel_width)) draw_poly(img, corners, color=(255, 0, 255), thickness=2) info = "Q:%.1f/%.1f/%.1f/%.1f mm" % tuple(sides) img.draw_string(int(blob.cx()+x0), int(blob.cy()+y0), info, color=(255, 0, 255), scale=1) avg_side = sum(sides) / 4 uart.write("Q%d\n" % int(avg_side)) elif current_mode == Mode.TILT: rects = img.find_rects(threshold=10000) rects = sorted(rects, key=lambda r: r.w()*r.h(), reverse=True) outer = inner = None if len(rects) >= 2: cand = [r for r in rects[:2] if is_a4_like_tilt(r)] if len(cand) == 2: outer, inner = cand[0], cand[1] if not inside(inner, outer): outer = inner = None if not outer: Display.show_image(img, x=(LCD_W - img.width())//2, y=(LCD_H - img.height())//2) continue draw_poly(img, outer.corners(), color=(255, 0, 0), thickness=2) if inner: draw_poly(img, inner.corners(), color=(0, 255, 255), thickness=2) corners = outer.corners() edges = [] for i in range(4): edge_length = math.sqrt((corners[i][0]-corners[(i+1)%4][0])**2 + (corners[i][1]-corners[(i+1)%4][1])**2) edges.append(edge_length) edges_sorted = sorted(edges) long_edge = edges_sorted[-1] pixel_width = long_edge dist_mm_a4 = (A4_REAL_H * FOCAL_PIX) / pixel_width img.draw_string(5, 5, "TILT %.1f cm" % (dist_mm_a4/10), color=(255, 255, 255), scale=2) uart.write("D%d\n" % int(dist_mm_a4)) # 内框ROI - 使用外框区域作为ROI shrink = 4 x0, y0, w0, h0 = outer.rect() x0, y0, w0, h0 = clamp_roi(x0 + shrink, y0 + shrink, w0 - 2*shrink, h0 - 2*shrink, img.width(), img.height()) gray = img.to_grayscale(roi=(x0, y0, w0, h0)) bw = gray.binary([(0, 80)], invert=False) inner_area = w0 * h0 MIN_AREA = int(inner_area * 0.06) MAX_AREA = int(inner_area * 0.7) blobs = bw.find_blobs([(255, 255)], pixels_threshold=50, area_threshold=MIN_AREA, roi=(0, 0, w0, h0)) # 收集所有四边形 quad_list = [] for blob in blobs: corners = blob.min_corners() corners = [(cx + x0, cy + y0) for (cx, cy) in corners] blob_area = blob.pixels() bbox_area = 0.5 * abs(sum(corners[i][0] * corners[(i+1)%4][1] - corners[(i+1)%4][0] * corners[i][1] for i in range(4))) area_ratio = blob_area / bbox_area if bbox_area > 0 else 1 if area_ratio >= 0.8: sides = [dist_mm_tilt(corners[i], corners[(i+1)%4], pixel_width) for i in range(4)] avg_side = sum(sides) / 4 max_diff = max(abs(s - avg_side) for s in sides) if max_diff < avg_side * 0.2: quad_list.append((corners, avg_side)) if quad_list: # 按面积排序,取最大的正方形 quad_list.sort(key=lambda q: abs((q[0][1][0]-q[0][0][0])*(q[0][2][1]-q[0][1][1])), reverse=True) square_corners, square_side = quad_list[0] # 正方形 draw_poly(img, square_corners, color=(0, 255, 0), thickness=3) center_x = sum(p[0] for p in square_corners) / 4 center_y = sum(p[1] for p in square_corners) / 4 img.draw_string(int(center_x), int(center_y), "SQ:%.1f mm" % square_side, color=(0, 255, 0), scale=2) # 发送正方形边长 uart.write("Q%d\n" % int(square_side)) img.draw_string(5, 30, "SQ:%.1f mm" % square_side, color=(0, 255, 0), scale=2) # ---------- MIN_QUAD 模式 ---------- elif current_mode == Mode.MIN_QUAD: rects = img.find_rects(threshold=10000) rects = sorted(rects, key=lambda r: r.w()*r.h(), reverse=True) outer = inner = None if len(rects) >= 2: cand = [r for r in rects[:2] if is_a4_like_tilt(r)] if len(cand) == 2: outer, inner = cand[0], cand[1] if not inside(inner, outer): outer = inner = None if not outer: Display.show_image(img, x=(LCD_W - img.width())//2, y=(LCD_H - img.height())//2) continue # 外框内框 draw_poly(img, outer.corners(), color=(255, 0, 0), thickness=2) if inner: draw_poly(img, inner.corners(), color=(0, 255, 255), thickness=2) corners = outer.corners() edges = [] for i in range(4): edge_length = math.sqrt((corners[i][0]-corners[(i+1)%4][0])**2 + (corners[i][1]-corners[(i+1)%4][1])**2) edges.append(edge_length) edges_sorted = sorted(edges) pixel_width = edges_sorted[0] dist_mm_a4 = (A4_REAL_W * FOCAL_PIX) / pixel_width img.draw_string(5, 5, "MIN_QUAD %.1f cm" % (dist_mm_a4/10), color=(255, 255, 255), scale=2) uart.write("D%d\n" % int (dist_mm_a4)) # ROI shrink = 3 x0, y0, w0, h0 = outer.rect() x0, y0, w0, h0 = clamp_roi(x0 + shrink, y0 + shrink, w0 - 2*shrink, h0 - 2*shrink, img.width(), img.height()) gray = img.to_grayscale(roi=(x0, y0, w0, h0)) bw = gray.binary([(0, 80)], invert=False) inner_area = w0 * h0 MIN_AREA = int(inner_area * 0.04) MAX_AREA = int(inner_area * 0.7) blobs = bw.find_blobs([(255, 255)], pixels_threshold=50, area_threshold=MIN_AREA, roi=(0, 0, w0, h0)) quad_list = [] for blob in blobs: corners = blob.min_corners() corners = [(cx + x0, cy + y0) for (cx, cy) in corners] blob_area = blob.pixels() bbox_area = 0.5 * abs(sum(corners[i][0] * corners[(i+1)%4][1] - corners[(i+1)%4][0] * corners[i][1] for i in range(4))) area_ratio = blob_area / bbox_area if bbox_area > 0 else 1 if area_ratio >= 0.9: sides = [dist_mm(corners[i], corners[(i+1)%4], pixel_width) for i in range(4)] min_side = min(sides) quad_list.append((corners, min_side, sides)) if quad_list: quad_list.sort(key=lambda q: q[1]) min_corners, min_side, min_sides = quad_list[0] for corners, _, sides in quad_list: color = (255, 0, 0) if corners == min_corners else (255, 0, 255) draw_poly(img, corners, color=color, thickness=2) info = "Q:%.1f/%.1f/%.1f/%.1f" % tuple(sides) img.draw_string(int(sum(p[0] for p in corners)/4), int(sum(p[1] for p in corners)/4), info, color=color, scale=1) # 发送最小四边形最短边 uart.write("Q%d\n" % int(min_side)) img.draw_string(5, 30, "MIN:%.1f mm" % min_side, color=(255, 0, 0), scale=2) # 统一显示 Display.show_image(img, x=(LCD_W - img.width())//2, y=(LCD_H - img.height())//2)
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值