鸿蒙OpenHarmony hi3516开发板,标准系统响应按钮拍照
自从搞懂了标准系统GPIO的操作后,即鸿蒙OpenHarmony hi3516开发板,标准系统按钮开关灯,下一步使用按钮也很快的搞定了,先做个暂时的记录吧。下一步,研究如何http调用云服务AI识图整体开发方式跟上次一样,就不再追溯了,仅仅修改了applications/standard/app/hello.c重点参考了OpenHarmony的文档和一个test源代码,文档中写了主要的步骤,另外我
自从搞懂了标准系统GPIO的操作后,即鸿蒙OpenHarmony hi3516开发板,标准系统按钮开关灯,下一步使用按钮拍照也很快的搞定了,先做个暂时的记录吧。下一步,研究如何http调用云服务AI识图
整体开发方式跟上次一样,就不再追溯了,仅仅修改了applications/standard/app/hello.c
重点参考了OpenHarmony的文档和一个test源代码,文档中写了主要的步骤,另外我在代码中把每个步骤都写了注释:
使用的代码版本是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\
源代码
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)