NikTalk
Hi!大家好,我是Nik。一名在人工智能+物联网行业混迹多年的AIOT架构工程师兼产品经理。欢迎您来到我的博客!这里记录了我的AIOT职业生涯,从最开始的一名普通嵌入式软件工程师->到嵌入式系统工程师->再到AIOT架构师兼产品经理一路上的所学所感所得。希望这些知识,经验,技术以及感悟对后来的你,在技术学习以及职业发展的道路上有所帮助。记得关注我,我将不定期分享一些AIOT相关的技术和行业信息。唯一微信号:aiotnik

LCD屏显-04

AIOTNIK-ONE开发板配置了2.8寸,240*320分辨率的8位并口LCD显示屏,可以用来显示静态图片和动态视频,以及摄像头实时视频流。这里以显示一张图静态图片和实时视频流为例。展示如何显示一张240*320分辨率的图片和抓取摄像头实时流并显示的过程和完整代码。

图片和视频要求:

图片和视频的格式:nv12格式(必须将常见的jpg、png等格式的图片转换为nv12格式,视频也一样是nv12格式)。
分辨率:240*320(因为显示屏的大小只有240*320)。
以下是将一张nv12格式的图片以及实时视频流显示到显示屏的代码。源码所在sdk路径sdk/samples/libimp-samples/sample-LCD.c

/*
 * sample-lcd.c
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 */
#include <imp/imp_log.h>
#include <imp/imp_common.h>
#include <imp/imp_system.h>
#include <imp/imp_framesource.h>
#include <imp/imp_encoder.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/fb.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include "sample-common.h"
#define TAG "Sample-LCD"
extern struct chn_conf chn[];
#define FB0DEV "/dev/fb0"
#define MAX_DESC_NUM    2
#define MAX_LAYER_NUM 1
//显示图层结构体
struct jzfb_dev {
unsigned int data_buf[MAX_DESC_NUM][MAX_LAYER_NUM];
unsigned int num_buf;
void *buf_addr;
int width;
int height;
unsigned int vid_size;
unsigned int fb_size;
int bpp;
int format;
struct fb_fix_screeninfo fix_info;
struct fb_var_screeninfo var_info;
int fd;
};
//显示图层设备初始化 
static int jzfb_dev_init(struct jzfb_dev * jzfb_dev)
{
int ret = 0;
int i, j;
jzfb_dev->fd = open(FB0DEV, O_RDWR);
if (jzfb_dev->fd <= 2) {
perror("fb0 open error");
return jzfb_dev->fd;
}
/* get framebuffer's var_info */
if ((ret = ioctl(jzfb_dev->fd, FBIOGET_VSCREENINFO, &jzfb_dev->var_info)) < 0) {
perror("FBIOGET_VSCREENINFO failed");
goto err_getinfo;
}
/* get framebuffer's fix_info */
if ((ret = ioctl(jzfb_dev->fd, FBIOGET_FSCREENINFO, &jzfb_dev->fix_info)) < 0) {
perror("FBIOGET_FSCREENINFO failed");
goto err_getinfo;
}
jzfb_dev->var_info.width = jzfb_dev->var_info.xres;
jzfb_dev->var_info.height = jzfb_dev->var_info.yres;
jzfb_dev->bpp = jzfb_dev->var_info.bits_per_pixel >> 3;
jzfb_dev->width = jzfb_dev->var_info.xres;
jzfb_dev->height = jzfb_dev->var_info.yres;
/* format rgb888 use 4 word ; format nv12/nv21 user 2 word */
jzfb_dev->fb_size = jzfb_dev->var_info.xres * jzfb_dev->var_info.yres * jzfb_dev->bpp;
jzfb_dev->num_buf = jzfb_dev->var_info.yres_virtual / jzfb_dev->var_info.yres;
jzfb_dev->vid_size = jzfb_dev->fb_size * jzfb_dev->num_buf;
jzfb_dev->buf_addr = mmap(0, jzfb_dev->vid_size, PROT_READ | PROT_WRITE, MAP_SHARED, jzfb_dev->fd, 0);
if(jzfb_dev->buf_addr == 0) {
perror("Map failed");
ret = -1;
goto err_getinfo;
}
for(i = 0; i < MAX_DESC_NUM; i++) {
for(j = 0; j < MAX_LAYER_NUM; j++) {
jzfb_dev->data_buf[i][j] = (unsigned int)(jzfb_dev->buf_addr +
j * jzfb_dev->fb_size +
i * jzfb_dev->fb_size * MAX_LAYER_NUM);
}
}
printf("xres = %d, yres = %d line_length = %d fbsize = %d, num_buf = %d, vidSize = %d\n",
jzfb_dev->var_info.xres, jzfb_dev->var_info.yres,
jzfb_dev->fix_info.line_length, jzfb_dev->fb_size,
jzfb_dev->num_buf, jzfb_dev->vid_size);
return ret;
err_getinfo:
close(jzfb_dev->fd);
return ret;
}
//图层显示函数
static int jzfb_pan_display(struct jzfb_dev *jzfb_dev, int fram_num)
{
int ret;
switch(fram_num) {
case 0:
jzfb_dev->var_info.yoffset = jzfb_dev->height * 0;
break;
case 1:
jzfb_dev->var_info.yoffset = jzfb_dev->height * 1;
break;
case 2:
jzfb_dev->var_info.yoffset = jzfb_dev->height * 2;
break;
}
jzfb_dev->var_info.activate = FB_ACTIVATE_NOW;
ret = ioctl(jzfb_dev->fd, FBIOPAN_DISPLAY, &jzfb_dev->var_info);
if(ret < 0){
printf("pan display error!");
return ret;
}
return 0;
}
// 定义一个结构体来表示 NV12 图像
typedef struct {
    int width;
    int height;
    unsigned char *data;
}NV12Image;
// 实现读取 NV12 图像数据的函数
NV12Image* readNV12Image(const char *filename, int width, int height) {
    // 计算图像数据大小
    int dataSize = width * height * 3 / 2;
    // 打开文件
    FILE *file = fopen(filename, "rb");
    if (!file) {
        perror("Failed to open file");
        return NULL;
    }
    // 分配内存
    NV12Image *image = (NV12Image *)malloc(sizeof(NV12Image));
    if (!image) {
        perror("Failed to allocate memory for NV12Image");
        fclose(file);
        return NULL;
    }
    image->data = (unsigned char *)malloc(dataSize);
    if (!image->data) {
        perror("Failed to allocate memory for image data");
        free(image);
        fclose(file);
        return NULL;
    }
    // 设置图像宽度和高度
    image->width = width;
    image->height = height;
    // 读取图像数据
    size_t bytesRead = fread(image->data, 1, dataSize, file);
    if (bytesRead != dataSize) {
        perror("Failed to read image data");
        free(image->data);
        free(image);
        fclose(file);
        return NULL;
    }
    // 关闭文件
    fclose(file);
    return image;
}
int main(int argc, char *argv[])
{
int ret;
struct jzfb_dev *jzfb_dev;
int fram_num = 0;
IMPFrameInfo *frame_bak;
IMPFSChnAttr fs_chn_attr[2];
    IMPISPSensorFps fps;
jzfb_dev = calloc(1, sizeof(struct jzfb_dev));
if (jzfb_dev == NULL) {
IMP_LOG_ERR(TAG,"jzfb_dev alloc mem for hwc dev failed!");
return -1;
}
/* Step.1 System init */
ret = sample_system_init();
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_System_Init() failed\n");
return -1;
}
//在系统初始化后,需加载fb驱动。否则系统会卡死
if (access("/dev/fb0", F_OK) == -1) {
// 不存在
system("insmod /lib/modules/jzfb.ko");
} else {
// 文件存在
printf("fb 驱动已经加载!\n");
}
/* Step.2 init jzfb_dev */
ret = jzfb_dev_init(jzfb_dev);
if(ret) {
IMP_LOG_ERR(TAG,"jzfb_dev init error!\n");
return -1;
}
/*Step.3 set sensror fps */
fps.num = 30;
fps.den = 1;
ret = IMP_ISP_Tuning_SetSensorFPS(0, &fps);
if (ret < 0){
IMP_LOG_ERR(TAG, "failed to set sensor fps\n");
return -1;
}
/* Step.4 FrameSource init */
if(chn[0].enable){
ret = IMP_FrameSource_CreateChn(chn[0].index, &chn[0].fs_chn_attr);
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_FrameSource_CreateChn(chn%d) error !\n", chn[0].index);
return -1;
}
}
/* Step.5 set framesource attr config */
IMPFrameAlign param_info;
param_info.enable = 1;
param_info.param.w = 1;
param_info.param.h = 0;
IMP_FrameSource_SetYuvAlign(0, &param_info);
ret = IMP_FrameSource_SetChnAttr(chn[0].index, &chn[0].fs_chn_attr);
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_FrameSource_SetChnAttr(chn%d) error !\n",  chn[0].index);
return -1;
}
ret = IMP_FrameSource_GetChnAttr(0, &fs_chn_attr[0]);
if(ret < 0){
IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_GetChnAttr failed\n", __func__, __LINE__);
return -1;
}
fs_chn_attr[0].pixFmt = PIX_FMT_NV12;
fs_chn_attr[0].crop.enable = 0;
fs_chn_attr[0].scaler.enable = 1;
fs_chn_attr[0].scaler.outwidth = jzfb_dev->width;
fs_chn_attr[0].scaler.outheight = jzfb_dev->height;
fs_chn_attr[0].picWidth = jzfb_dev->width;
fs_chn_attr[0].picHeight = jzfb_dev->height;
ret = IMP_FrameSource_SetChnAttr(0, &fs_chn_attr[0]);
if(ret < 0) {
IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetChnAttr failed\n", __func__, __LINE__);
return -1;
}
/* Step.6 Stream On */
if (chn[0].enable){
ret = IMP_FrameSource_EnableChn(chn[0].index);
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_FrameSource_EnableChn(%d) error: %d\n", ret, chn[1].index);
return -1;
}
}
//设置缓存帧为1
ret = IMP_FrameSource_SetFrameDepth(0, 1);
if(ret < 0){
IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetFrameDepth failed\n", __func__, __LINE__);
return -1;
}
int break_num = 0;
/* 显示一张nv12的静态图片 */
while(1){
const char *filename = "/system/config/logo.nv12";//静态图片所在路径
int width = 240; // 设置图像宽度
int height = 320; // 设置图像高度
NV12Image *image = readNV12Image(filename, width, height);
if (image) {
// 在这里可以处理图像数据
switch(fram_num) {
case 0:
memcpy((void *)jzfb_dev->data_buf[0][0], (void *)(image->data), image->width * image->height * 3 / 2);
break;
case 1:
memcpy((void *)jzfb_dev->data_buf[1][0], (void *)(image->data), image->width * image->height * 3 / 2);
break;
default:
break;
}
//图片显示到lcd屏
ret = jzfb_pan_display(jzfb_dev, fram_num);
if (ret){
printf("ret=%d\n", ret);
break;
}
fram_num ++;
if (fram_num > 1){
fram_num = 0;
}
// 释放内存
free(image->data);
free(image);
} else {
printf("图片不存在,请检查/system/config/logo.nv12目录\n");
}
sleep(1);
break_num++;
if(break_num == 10){//10秒后退出循环
break;
}
}
/* Step.7 将摄像头的实时视频显示到显示屏上  */
while(1) {
switch(fram_num) {
case 0:
ret = IMP_FrameSource_GetFrame(0, &frame_bak);
if(ret < 0){
IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_GetFrame failed\n", __func__, __LINE__);
return -1;
}
memcpy((void *)jzfb_dev->data_buf[0][0], (void *)(frame_bak->virAddr), frame_bak->size);
break;
case 1:
ret = IMP_FrameSource_GetFrame(0, &frame_bak);
if(ret < 0){
IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_GetFrame failed\n", __func__, __LINE__);
return -1;
}
memcpy((void *)jzfb_dev->data_buf[1][0], (void *)(frame_bak->virAddr), frame_bak->size);
break;
default:
break;
}
//实时视频流显示到lcd屏
if (jzfb_pan_display(jzfb_dev, fram_num))
break;
fram_num ++;
if (fram_num > 1)
fram_num = 0;
IMP_FrameSource_ReleaseFrame(0, frame_bak);
}
//设置缓存帧为0
ret = IMP_FrameSource_SetFrameDepth(0, 0);
if(ret < 0) {
IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetFrameDepth failed\n", __func__, __LINE__);
return -1;
}
/* Step.7 Stream Off */
if (chn[0].enable) {
ret = IMP_FrameSource_DisableChn(chn[0].index);
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_FrameSource_DisableChn(%d) error: %d\n", ret, chn[0].index);
return -1;
}
}
/* Step.8 FrameSource exit */
if (chn[0].enable){
ret = IMP_FrameSource_DestroyChn(0);
if(ret < 0){
IMP_LOG_ERR(TAG, "IMP_FrameSource_DestroyChn() error: %d\n", ret);
return -1;
}
}
/* Step.9 System exit */
ret = sample_system_exit();
if(ret < 0){
IMP_LOG_ERR(TAG, "sample_system_exit() failed\n");
return -1;
}
/*释放缓存*/
munmap(jzfb_dev->buf_addr, jzfb_dev->vid_size);
close(jzfb_dev->fd);
free(jzfb_dev);
return 0;
}

例程sample-LCD.c编译。

cd sdk/samples/libimp-samples
make 

将编译出来的可执行程序sample-LCD拷贝到板子并运行。

运行结果如下,显示10秒图片后,显示实时视频流。