自从搞懂了标准系统GPIO的操作后,即鸿蒙OpenHarmony hi3516开发板,标准系统按钮开关灯,下一步使用按钮拍照也很快的搞定了,先做个暂时的记录吧。下一步,研究如何http调用云服务AI识图

整体开发方式跟上次一样,就不再追溯了,仅仅修改了applications/standard/app/hello.c

重点参考了OpenHarmony的文档和一个test源代码,文档中写了主要的步骤,另外我在代码中把每个步骤都写了注释:

参考文档:multimedia_camera_standard: Implementation of camera device management and camera capture functions | 相机设备和相机采集功能实现

参考源代码:interfaces/innerkits/native/test/camera_capture.cpp · OpenHarmony/multimedia_camera_standard - Gitee.com

使用的代码版本是OH2021-12-25的master源代码,等最近出了新版本tag后,我再验证下这个最新版本是否也OK。

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<poll.h>

#include "input/camera_input.h"
#include "input/camera_manager.h"
#include "surface.h"

#include <cinttypes>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <sstream>

#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>

#include <securec.h>

using namespace std;
using namespace OHOS;
using namespace OHOS::CameraStandard;


#define MSG(args...) printf(args) 
 
//函数声明
static int gpio_export(int pin);
static int gpio_unexport(int pin);
static int gpio_direction(int pin, int dir);
static int gpio_write(int pin, int value);
static int gpio_read(int pin);
static int gpio_edge(int pin, int edge);



static int gpio_export(int pin)  
{  
    char buffer[64];  
    int len;  
    int fd;  
  
    fd = open("/sys/class/gpio/export", O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open export for writing!\n");  
        return(-1);  
    }  
  
    len = snprintf(buffer, sizeof(buffer), "%d", pin);  
    printf("%s,%d,%d\n",buffer,sizeof(buffer),len);
    if (write(fd, buffer, len) < 0) 
    {  
        MSG("Failed to export gpio!");  
        return -1;  
    }  
     
    close(fd);  
    return 0;  
}  
static int gpio_unexport(int pin)  
{  
    char buffer[64];  
    int len;  
    int fd;  
  
    fd = open("/sys/class/gpio/unexport", O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open unexport for writing!\n");  
        return -1;  
    }  
  
    len = snprintf(buffer, sizeof(buffer), "%d", pin);  
    if (write(fd, buffer, len) < 0) 
    {  
        MSG("Failed to unexport gpio!");  
        return -1;  
    }  
     
    close(fd);  
    return 0;  
} 
//dir: 0-->IN, 1-->OUT
static int gpio_direction(int pin, int dir)  
{  
    static const char dir_str[] = "in\0out";  
    char path[64];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio direction for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) < 0) 
    {  
        MSG("Failed to set direction!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}  
//value: 0-->LOW, 1-->HIGH
static int gpio_write(int pin, int value)  
{  
    static const char values_str[] = "01";  
    char path[64];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio value for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) 
    {  
        MSG("Failed to write value!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}
static int gpio_read(int pin)  
{  
    char path[64];  
    char value_str[3];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);  
    fd = open(path, O_RDONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio value for reading!\n");  
        return -1;  
    }  
  
    if (read(fd, value_str, 3) < 0)
    {  
        MSG("Failed to read value!\n");  
        return -1;  
    }  
  
    close(fd);  
    return (atoi(value_str));
}  
// none表示引脚为输入,不是中断引脚
// rising表示引脚为中断输入,上升沿触发
// falling表示引脚为中断输入,下降沿触发
// both表示引脚为中断输入,边沿触发
// 0-->none, 1-->rising, 2-->falling, 3-->both
static int gpio_edge(int pin, int edge)
{
const char dir_str[] = "none\0rising\0falling\0both"; 
int ptr;
char path[64];  
    int fd; 
switch(edge)
{
    case 0:
        ptr = 0;
        break;
    case 1:
        ptr = 5;
        break;
    case 2:
        ptr = 12;
        break;
    case 3:
        ptr = 20;
        break;
    default:
        ptr = 0;
} 
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio edge for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) < 0) 
    {  
        MSG("Failed to set edge!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}

enum class mode_ {
    MODE_PREVIEW = 0,
    MODE_PHOTO
};

// 获取当前时间
static uint64_t GetCurrentLocalTimeStamp()
{
    std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp =
        std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
    auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
    return tmp.count();
}

// 保存图片
static int32_t SaveYUV(mode_ mode, const char *buffer, int32_t size)
{
    static const std::int32_t FILE_PERMISSION_FLAG = 00766;
    char path[PATH_MAX] = {0};
    int32_t retVal;

    if (mode == mode_::MODE_PREVIEW) {
        system("mkdir -p /data/preview");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/preview/%s_%lld.yuv", "preview",
            GetCurrentLocalTimeStamp());
    } else {
        system("mkdir -p /data/capture");
        retVal = sprintf_s(path, sizeof(path) / sizeof(path[0]), "/data/capture/%s_%lld.jpg", "photo",
            GetCurrentLocalTimeStamp());
    }
    if (retVal < 0) {
        printf("Path Assignment failed");
        return -1;
    }
    printf("%s, saving file to %s", __FUNCTION__, path);
    int imgFd = open(path, O_RDWR | O_CREAT, FILE_PERMISSION_FLAG);
    if (imgFd == -1) {
        printf("%s, open file failed, errno = %s.", __FUNCTION__, strerror(errno));
        return -1;
    }
    int ret = write(imgFd, buffer, size);
    if (ret == -1) {
        printf("%s, write file failed, error = %s.", __FUNCTION__, strerror(errno));
        close(imgFd);
        return -1;
    }
    close(imgFd);
    return 0;
}

// 1.创建缓冲区消费者端监听器(CaptureSurfaceListener)以保存图像。
class CaptureSurfaceListener : public IBufferConsumerListener {
public:
    mode_ mode;
    sptr<Surface> surface_;

    void OnBufferAvailable() override
    {
        int32_t flushFence = 0;
        int64_t timestamp = 0;
        OHOS::Rect damage; // initialize the damage

        OHOS::sptr<OHOS::SurfaceBuffer> buffer = nullptr;
        surface_->AcquireBuffer(buffer, flushFence, timestamp, damage);
        if (buffer != nullptr) {
            char *addr = static_cast<char *>(buffer->GetVirAddr());
            int32_t size = buffer->GetSize();

            // Save the buffer(addr) to a file.
            SaveYUV(mode, addr, size);

            surface_->ReleaseBuffer(buffer, -1);
        }
    }
};


int main()  
{  
    int gpio_fd, ret;
    struct pollfd fds[1];
    char buff[10];
    //41为红灯, 1为1号按键
 
    gpio_unexport(41);
    gpio_unexport(1);
 
    
    //41红灯亮起
    gpio_export(41);
    gpio_direction(41, 1);//output out
    gpio_write(41, 1);
    
    //1按钮初始化
    gpio_export(1);
    gpio_direction(1, 0);//input in
    gpio_edge(1,2);
    gpio_fd = open("/sys/class/gpio/gpio1/value",O_RDONLY);
    if(gpio_fd < 0)
    {
        MSG("Failed to open value!\n");  
        return -1;  
    }
    fds[0].fd = gpio_fd;
    fds[0].events  = POLLPRI;

    // 参考Camera组件拍摄 https://gitee.com/openharmony/multimedia_camera_standard
    // 样例:foundation/multimedia/camera_standard/interfaces/innerkits/native/test/camera_capture.cpp

    // 2.获取相机管理器实例并获取相机对象列表。
    int32_t intResult = -1;
    sptr<CameraManager> camManagerObj = CameraManager::GetInstance();
    std::vector<sptr<CameraInfo>> cameraObjList = camManagerObj->GetCameras();
    if (cameraObjList.size() < 0) {
        printf("No Camera existed! Return -1!");
        return -1;
    }
    for (auto& it : cameraObjList) {
        printf("Camera ID: %s\n", it->GetID().c_str());
    }

    //3. 创建采集会话。
    sptr<CaptureSession> captureSession = camManagerObj->CreateCaptureSession();
    if (captureSession == nullptr) {
        printf("Failed to create capture session");
        return -1;
    }
    //4. 开始配置采集会话。
    captureSession->BeginConfig();

    //5. 使用相机对象创建相机输入。
    sptr<CaptureOutput> photoOutput;
    sptr<CaptureInput> cameraInput = camManagerObj->CreateCameraInput(cameraObjList[0]);
    if (cameraInput != nullptr) {
        //6.将相机输入添加到采集会话。
        intResult = captureSession->AddInput(cameraInput);
        if (intResult == 0) {
            //7.创建消费者Surface并注册监听器以监听缓冲区更新。拍照的宽和高可以配置为所支持的 1280x960 分辨率。
            sptr<Surface> photoSurface = Surface::CreateSurfaceAsConsumer();
            photoSurface->SetDefaultWidthAndHeight(1280, 960);
            sptr<CaptureSurfaceListener> capturelistener = new CaptureSurfaceListener();
            capturelistener->mode = mode_::MODE_PHOTO;
            capturelistener->surface_ = photoSurface;
            photoSurface->RegisterConsumerListener((sptr<IBufferConsumerListener> &)capturelistener);

            //8.使用上面创建的 Surface 创建拍照输出。
            photoOutput = camManagerObj->CreatePhotoOutput(photoSurface);
            if (photoOutput == nullptr) {
                printf("Failed to create PhotoOutput");
                return -1;
            }
            //9.将拍照输出添加到采集会话。
            intResult = captureSession->AddOutput(photoOutput);
            if (intResult != 0) {
                printf("Failed to Add output to session, intResult: %d", intResult);
                return -1;
            }
            //10. 将配置提交到采集会话。
            intResult = captureSession->CommitConfig();
            if (intResult != 0) {
                printf("Failed to Commit config, intResult: %d", intResult);
                return -1;
            }

        }
    }




    
    while(1)
    {

        ret = poll(fds,1,5000);
        if( ret == -1 )
        MSG("poll\n");
        if( fds[0].revents & POLLPRI)
        {
            ret = lseek(gpio_fd,0,SEEK_SET);
            if( ret == -1 )
            MSG("lseek\n");

             ret = read(gpio_fd,buff,10);//读取按钮值,但这里没使用
            if( ret == -1 )
            MSG("read\n");

            //切换红灯
            int status = gpio_read(41);
            printf("41 = %d\n",status);
            gpio_write(41, 1 - status);

            //11.拍摄照片。
            intResult = ((sptr<PhotoOutput> &)photoOutput)->Capture();
            if (intResult != 0) {
                printf("Failed to capture, intResult: %d", intResult);
                return -1;
            }else{
                printf("Success to capture, intResult: %d", intResult);
            }
    
        
            //gpio_write(44, cnt++%2);
            printf("**********************************\n");
        }
        printf("one loop\n");
        //usleep(5);
    }

    //12. 释放采集会话资源。
    captureSession->Release();

    return 0;
}

Build.gn 引入camera子系统的头文件及模块

import("//build/ohos.gni")
import("//drivers/adapter/uhdf2/uhdf.gni")

ohos_executable("madixin") {
  sources = [
    "madixin.cpp"
  ]
  subsystem_name = "applications"
  part_name = "prebuilt_hap"

  include_dirs = [
    "//foundation/multimedia/camera_standard/interfaces/innerkits/native/camera/include",
    "//foundation/multimedia/camera_standard/interfaces/innerkits/native/test",
    "//foundation/multimedia/camera_standard/services/camera_service/include",
    "//foundation/multimedia/camera_standard/services/camera_service/binder/base/include",
    "//foundation/multimedia/camera_standard/services/camera_service/binder/client/include",
    "//foundation/multimedia/camera_standard/services/camera_service/binder/server/include",
    "//foundation/graphic/standard/frameworks/surface/include",
    "//foundation/multimedia/camera_standard/frameworks/innerkitsimpl/metadata/include",
    "//utils/system/safwk/native/include",
    "//drivers/framework/include/utils",
    "//drivers/adapter/uhdf2/osal/include",
    "//drivers/adapter/uhdf2/include/hdi",
    "//drivers/peripheral/display/interfaces/include",
    "//drivers/peripheral/camera/interfaces/include",
    "//drivers/peripheral/camera/interfaces/hdi_ipc",
    "//drivers/peripheral/camera/interfaces/hdi_ipc/server",
    "//drivers/peripheral/camera/interfaces/hdi_ipc/callback/device",
    "//drivers/peripheral/camera/interfaces/hdi_ipc/callback/operator",
    "//drivers/peripheral/camera/interfaces/hdi_ipc/callback/host",
  ]

 deps = [
    "//foundation/graphic/standard:libsurface",
    "//foundation/multimedia/camera_standard/frameworks/innerkitsimpl/camera:camera_framework",
    "//foundation/multimedia/camera_standard/frameworks/innerkitsimpl/metadata:metadata",
    "//utils/native/base:utils",
  ]
}

因为在CaptureSurfaceListener定义了照片保存路径,即/data/capture目录,因此点击按钮拍照后,串口中会输出信息,照片也保存在了/data/capture目录下。

使用命令把照片发到本机就可以浏览拉:

hdc_std file recv /data/capture/photo_4942243.jpg D:\madixin\test\images\

源代码

MyOpenHarmonySample: 我的OpenHarmonySample代码 - Gitee.com

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐