跳转至

摄像头应用

一、演示功能概述

摄像头拍照功能通过光学传感器与图像处理算法,实现将现实场景转化为数字图像数据的技术。它允许设备捕捉静态画面,并支持实时预览、参数调节及后期处理。本章节将以 Air8000 整机开发板为平台,演示如何通过集成摄像头模块实现多场景智能拍摄,并展示其在嵌入式视觉应用中的创新实践。

二、准备硬件环境

参考:Air8000 硬件环境清单,准备好硬件环境。

2.1 Air8000 整机开发板

2.2 30W 摄像头

三、准备软件环境

3.1 文章内容应用

1. 烧录工具:Luatools 工具

2. Air8000 烧录需要的固件和脚本文件:

内核固件:Air8000 内核固件

脚本文件:Air8000 camera演示脚本

3. LuatOS 运行所需要的 lib 文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件。

准备好软件环境之后,接下来查看如何烧录项目文件到 Air8000 开发板中,将本篇文章中演示使用的项目文件烧录到 Air8000 开发板中。

3.2 API 介绍

camera.on(id, event, func)

注册摄像头事件回调。

参数

**参数**
**类型**
**释义**
**取值**
id
int
camera id
camera 0写0, camera 1写1
event
string

事件名称
字符串
func
function
回调函数
回调函数

返回值

**返回值**
**类型**
**释义**
**取值**
nil
nil
无返回值
nil

例子

camera.on(0, "scanned", function(id, str)
    if type(str) == 'string' then -- 如果是扫码模式(使用摄像头对二维码、条形码或其他类型的图案进行扫描和识别)
        log.info("扫码结果", str)
    elseif str == false then -- 如果摄像头没有正常工作
        log.error("摄像头没有数据")
    else -- 如果摄像头正常工作,并且不是扫码模式
        log.info("摄像头数据", str)
        sys.publish("capture done", true)
    end
end) -- 注册 camera 0 的 "scanned" 事件
camera.preview(id, onoff)

启停 camera 预览功能,直接输出到 LCD 上,只有硬件支持的 SOC 可以运行。

参数

**参数**
**类型**
**释义**
**取值**
id
int
camera id
camera 0写0, camera 1写1
onoff
boolean
启停camera预览功能
true开启,false停止

返回值

**返回值**
**类型**
**释义**
**取值**
onoff
boolean
是否成功启动camera预览功能
成功返回true,否则返回false

例子

camera.preview(camera_id, true) -- 打开LCD预览功能(直接将摄像头数据输出到LCD)
camera.capture(id, save_path, quality)

启动摄像头拍照功能。

参数

**参数**
**类型**
**释义**
**取值**
id
int
camera id
camera 0写0, camera 1写1
save_path
string/zbuff/nil
摄像头拍照后的数据存放路径
string/nil:文件保存路径,空则写在上次路径里,默认是/capture.jpg
zbuff:将图片保存在buff内不写入文件系统
quality
int
jpeg压缩质量
1最差,占用空间小,3最高,占用空间最大而且费时间,默认1

返回值

**返回值**
**类型**
**释义**
**取值**
onoff
boolean
是否成功启动摄像头拍照功能。完成后通过camera.on设置的回调函数回调接收到的长度
成功返回true,否则返回false

例子

-- 将摄像头拍照的数据存入 "/testCamera.jpg" 文件中
-- jpeg压缩质量1最差,占用空间小,3最高,占用空间大,2和3需要非常多非常多的psram,尽量不要用
camera.capture(camera_id, "/testCamera.jpg", 1)
camera.close(id)

完全关闭指定的 camera,释放相应的 IO 资源。

参数

**参数**
**类型**
**释义**
**取值**
id
int
camera id
camera 0写0, camera 1写1

返回值

**返回值**
**类型**
**释义**
**取值**
onoff
boolean
是否成功关闭指定摄像头
成功返回true,否则返回false

例子

camera.close(0)
camera.stop(id)

暂停摄像头捕获数据。仅停止了图像捕获,未影响预览功能。

注意:调用该函数 camera.preview 的 LCD 预览功能依旧存在。

参数

**参数**
**类型**
**释义**
**取值**
id
int
camera id
camera 0写0, camera 1写1

返回值

**返回值**
**类型**
**释义**
**取值**
onoff
boolean
是否成功暂停指定摄像头
成功返回true,否则返回false

例子

camera.stop(0)

四、代码示例

--[[
1. 本demo可直接在Air8000整机开发板上运行
2. 演示摄像头拍照扫码功能,通过TEST_MODE宏来选择演示的内容。
摄像头使用了如下管脚
[85, "AGPIO4", " PIN85脚, PA供电使能"],
[67, "CAM_SPI_CLK", " PIN67脚, 用作摄像头时钟"],
[66, "CAM_SPI_CS", " PIN66脚, 用作摄像头片选"],
[98, "CAM_MCLK", " PIN98脚, 用于摄像头时钟"],
[52, "GPIO153", " PIN52脚, 控制摄像头开启/关闭"],
[53, "GPIO147", " PIN53脚, 控制摄像头电源"],
[80, "I2C0_SCL", " PIN80脚, 用作摄像头复用"],
[81, "I2C0_SDA", " PIN81脚, 用作摄像头复用"],
[1, "USB_BOOT", " PIN1脚, 用作功能键"],
[16, "UART1_TX", " PIN16脚,初始化串口1"],
[17, "UART1_RX", " PIN17脚,用作输出拍摄的照片"]
3.注意:在air8000整机开发版上,因为es8311/gsensor/lcd_tp触摸/camera 用的都是I2C0(80/81脚),所以使用此demo时不能同时使用es8311/gsensor/lcd_tp触摸功能
4. 本程序使用逻辑:
4.1 如果TEST_MODE  设置为1 ,程序运行后,点击boot 键,将进行扫码测试,如果解析二维码或者条形码成功,将会打印在luatools 中。
4.2 如果TEST_MODE  设置为0,程序运行后,点击boot 键,将进行拍照测试,并且保存在本地。
]]
PROJECT = "spi_camera_demo"
VERSION = "1.0.0"
-- 实际使用时选1个就行
-- require "bf30a2"
-- require "gc0310"
require "gc032a"
sys = require("sys")
sysplus = require("sysplus")

local taskName = "SPI_CAMERA"
local TEST_MODE = 0 -- 写1 演示扫码(使用摄像头对二维码、条形码或其他类型的图案进行扫描和识别),0 演示拍照
local scan_pause = true -- 扫码启动与停止标志位
local done_with_close = false -- true 结束后关闭摄像头
local uartid = 1 -- 根据实际设备选取不同的uartid
local cspiId = 1 -- 摄像头使用SPI1、I2C0
local i2cId = 0
local camera_id

-- 初始化UART
local result = uart.setup(uartid, -- 串口id
115200, -- 波特率
8, -- 数据位
1 -- 停止位
)

-- 注册摄像头事件回调
camera.on(0, "scanned", function(id, str)
    if type(str) == 'string' then
        log.info("扫码结果", str)
    elseif str == false then
        log.error("摄像头没有数据")
    else
        log.info("摄像头数据", str)
        sys.publish("capture done", true)
    end
end)

-- 初始化按键,这里选取boot键作为功能键
local function press_key()
    log.info("boot press")
    sys.publish("PRESS", true)
end
gpio.setup(0, press_key, gpio.PULLDOWN, gpio.RISING)
gpio.debounce(0, 100, 1)

-- 初始化摄像头
local rawbuff, err = zbuff.create(60 * 1024, 0, zbuff.HEAP_AUTO) -- gc032a
if rawbuff == nil then
    log.info(err)
end
local function device_init()
    i2c.setup(i2cId, i2c.FAST)
    gpio.setup(153, 0) -- PD拉低
    sys.wait(500)
    -- return bf30a2Init(cspiId,i2cId,25500000,TEST_MODE,TEST_MODE)
    -- return gc0310Init(cspiId, i2cId, 25500000, TEST_MODE, TEST_MODE)
    return gc032aInit(cspiId, i2cId, 24000000, TEST_MODE, TEST_MODE)
end

local function main_task()
        sys.wait(500)
        gpio.setup(147, 1, gpio.PULLUP) -- camera的供电使能脚
        gpio.setup(153, 1, gpio.PULLUP) -- 控制camera电源的pd脚
        sys.wait(4000)
        log.info("摄像头启动")
        local camera_id = device_init()

        if done_with_close then
            camera.close(camera_id)
        else
            camera.stop(camera_id)
        end
        log.info("按下boot开始测试")
        log.info(rtos.meminfo("sys"))
        log.info(rtos.meminfo("psram"))
        while 1 do
            result, data = sys.waitUntil("PRESS", 30000)
            if result == true and data == true then
                if TEST_MODE == 1 then
                    if scan_pause then
                        log.info("启动扫码")
                        if done_with_close then
                            camera_id = device_init()
                        end
                        camera.start(camera_id)
                        scan_pause = false
                        sys.wait(1000)
                        log.info(rtos.meminfo("sys"))
                        log.info(rtos.meminfo("psram"))
                    else
                        log.info("停止扫码")
                        if done_with_close then
                            camera.close(camera_id)
                        else
                            camera.stop(camera_id)
                        end
                        scan_pause = true
                        sys.wait(1000)
                        log.info(rtos.meminfo("sys"))
                        log.info(rtos.meminfo("psram"))
                    end
                else
                    log.debug("摄像头拍照")
                    if done_with_close then
                        camera_id = device_init()
                    end
                    camera.capture(camera_id, rawbuff, 1) -- 2和3需要非常多非常多的psram,尽量不要用
                    result, data = sys.waitUntil("capture done", 30000)
                    log.info(rawbuff:used())
                    if done_with_close then
                        camera.close(camera_id)
                    else
                        camera.stop(camera_id)
                    end
                    uart.tx(uartid, rawbuff) -- 找个能保存数据的串口工具保存成文件就能在电脑上看了, 格式为JPG
                    rawbuff:resize(60 * 1024)
                    log.info(rtos.meminfo("sys"))
                    log.info(rtos.meminfo("psram"))
                end
            end
        end
end

-- 启动任务时传入依赖
sysplus.taskInitEx(main_task,taskName)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

五、功能演示

5.1 扫码功能演示

首先将 TEST_MODE 宏置 1

然后将我们的摄像头安装到板子上,并烧录代码

参考:如何使用 LuaTools 给 Air8000 烧录软件,将准备好的软件烧录进 Air8000。

烧录完毕后可以看到日志中输出摄像头启动的打印。

按下 boot 按键(下载键),就进入了扫码模式

打开 https://cli.im/text 网址,生成一个二维码。

用摄像头扫码,扫描成功则可以在日志中看到扫描的信息

5.2 拍照功能演示

首先将 TEST_MODE 宏置 0

然后将我们的摄像头安装到板子上,并烧录代码

参考:如何使用 LuaTools 给 Air8000 烧录软件,将准备好的软件烧录进 Air8000。

烧录完毕后可以看到日志中输出摄像头启动的打印。

如果想让图片输出,就需要按照下图所示,将开发板的 uart1 通过串口线链接到电脑。

打开一个可以保存数据的串口工具,用来将拍照的图片保存成文件。这里演示的工具为 SSCOM。点我获取 SSCOM

按照图片上的方式选择相应的串口,取消时间戳和分包,并将接收到的数据保存为文件。

这时按下 boot 按键(下载键),即可开始拍照。

等文件传输完毕,也就是拍照完成后。打开之前串口保存的地址,可以看到文件已经有内容了。

将文件后缀修改为.jpg 格式。

双击打开,就可以看到我们所拍摄的照片了。

六、总结

至此,我们已使用 Air8000 整机开发板 演示了 camera 的扫码和拍照功能。