百问网,T113 usb摄像头使用cpu解码显示

1 资料下载

https://download.100ask.net/boards/Allwinner/T113/index.html

2 软件安装

资料下载完毕后,根据说明,安装vmware,安装win驱动

3 配置开发环境

为了方便,建议将sdk文件上传到目录 /home/book/

安装必要的工具包,如果出现问题 Could not get lock /var/lib/dpkg/lock-frontend ,可执行以下指令

sudo rm /var/lib/apt/lists/lock
sudo rm /var/cache/apt/archives/lock
sudo rm /var/lib/dpkg/lock*
sudo dpkg --configure -a
sudo apt update

按照以下顺序执行指令

sudo apt-get install -y sed make binutils build-essential gcc g++ bash patch gzip bzip2 perl tar cpio unzip rsync file bc wget python cvs git mercurial rsync subversion android-tools-mkbootimg vim libssl-dev android-tools-fastboot

cat tina-d1-h.tar.bz2.* | tar -jxv

sudo apt-get install build-essential subversion git libncurses5-dev zlib1g-dev gawk flex quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip lib32z1 lib32z1-dev lib32stdc++6 libstdc++6 libc6:i386 libstdc++6:i386 lib32ncurses5 lib32z1 -y

系统推荐的
git clone https://gitee.com/weidongshan/100ASK_T113-Pro_TinaSDK.git
cd 100ASK_T113-Pro_TinaSDK
git submodule update --init
cp ./* -rfvd ../tina-d1-h

以下指令作用是编译镜像,建议执行一次(,编译过程中,如果中途出现选项,直接按enter)

cd ~/tina-d1-h
source build/envsetup.sh
lunch 4
make && pack

4 usb摄像头lcd显示源码

可根据需要自行进行多线程改编

源代码 camera_usb_yuyv_lcd.c

点击查看代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <math.h>
#include <wchar.h>
#include <time.h>
#include <stdbool.h>

#define CAM_WIDTH 640
#define CAM_HEIGHT 480
// #define CAM_WIDTH 1280
// #define CAM_HEIGHT 720 

#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 600

static char dev_video[64];
static char *dev_fb0;

static char *yuv_buffer;
static char *rgb_buffer;

typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
#define YUVToRGB(Y)                                                            \
	((u16)((((u8)(Y) >> 3) << 11) | (((u8)(Y) >> 2) << 5) | ((u8)(Y) >> 3)))
struct v4l2_buffer video_buffer;
/*全局变量*/
int lcd_fd;
int video_fd;
static unsigned char *lcd_mem_p = NULL; //保存LCD屏映射到进程空间的首地址
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;

char *video_buff_buff[4]; /*保存摄像头缓冲区的地址*/
int video_height = 0;
int video_width = 0;
unsigned char *lcd_display_buff; //LCD显存空间
unsigned char *lcd_display_buff2; //LCD显存空间
static void errno_exit(const char *s)
{
	fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
	exit(EXIT_FAILURE);
}

static int xioctl(int fh, int request, void *arg)
{
	int r;

	do {
		r = ioctl(fh, request, arg);
	} while (-1 == r && EINTR == errno);

	return r;
}


static int video_init(void)
{
	struct v4l2_capability cap;
	ioctl(video_fd, VIDIOC_QUERYCAP, &cap);

	struct v4l2_fmtdesc dis_fmtdesc;
	dis_fmtdesc.index = 0;
	dis_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("-----------------------支持格式---------------------\n");
	while (ioctl(video_fd, VIDIOC_ENUM_FMT, &dis_fmtdesc) != -1) {
		printf("\t%d.%s\n", dis_fmtdesc.index + 1,
		       dis_fmtdesc.description);
		dis_fmtdesc.index++;
	}
	struct v4l2_format video_format;
	video_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	video_format.fmt.pix.width = CAM_WIDTH;
	video_format.fmt.pix.height = CAM_HEIGHT;
	video_format.fmt.pix.pixelformat =
		V4L2_PIX_FMT_YUYV; //使用JPEG格式帧,用于静态图像采集

	ioctl(video_fd, VIDIOC_S_FMT, &video_format);

	printf("当前摄像头支持的分辨率:%dx%d\n", video_format.fmt.pix.width,
	       video_format.fmt.pix.height);
	if (video_format.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
		printf("当前摄像头不支持YUYV格式输出.\n");
		video_height = video_format.fmt.pix.height;
		video_width = video_format.fmt.pix.width;
		//return -3;
	} else {
		video_height = video_format.fmt.pix.height;
		video_width = video_format.fmt.pix.width;
		printf("当前摄像头支持YUYV格式输出.width %d height %d\n",
		       video_height, video_height);
	}
	/*3. 申请缓冲区*/
	struct v4l2_requestbuffers video_requestbuffers;
	memset(&video_requestbuffers, 0, sizeof(struct v4l2_requestbuffers));
	video_requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	video_requestbuffers.count = 4;
	video_requestbuffers.memory = V4L2_MEMORY_MMAP;
	if (ioctl(video_fd, VIDIOC_REQBUFS, &video_requestbuffers))
		return -4;
	printf("成功申请的缓冲区数量:%d\n", video_requestbuffers.count);
	/*4. 得到每个缓冲区的地址: 将申请的缓冲区映射到进程空间*/
	struct v4l2_buffer video_buffer;
	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
	int i;
	for (i = 0; i < video_requestbuffers.count; i++) {
		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		video_buffer.index = i;
		video_buffer.memory = V4L2_MEMORY_MMAP;
		if (ioctl(video_fd, VIDIOC_QUERYBUF, &video_buffer))
			return -5;
		/*映射缓冲区的地址到进程空间*/
		video_buff_buff[i] =
			mmap(NULL, video_buffer.length, PROT_READ | PROT_WRITE,
			     MAP_SHARED, video_fd, video_buffer.m.offset);
		printf("第%d个缓冲区地址:%#X\n", i, video_buff_buff[i]);
	}
	/*5. 将缓冲区放入到采集队列*/
	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
	for (i = 0; i < video_requestbuffers.count; i++) {
		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		video_buffer.index = i;
		video_buffer.memory = V4L2_MEMORY_MMAP;
		if (ioctl(video_fd, VIDIOC_QBUF, &video_buffer)) {
			printf("VIDIOC_QBUF error\n");
			return -6;
		}
	}
	printf("启动摄像头采集\n");
	/*6. 启动摄像头采集*/
	int opt_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (ioctl(video_fd, VIDIOC_STREAMON, &opt_type)) {
		printf("VIDIOC_STREAMON error\n");
		return -7;
	}

	return 0;
}

int lcd_init(void)
{
	/*2. 获取可变参数*/
	if (ioctl(lcd_fd, FBIOGET_VSCREENINFO, &vinfo))
		return -2;
	printf("屏幕X:%d   屏幕Y:%d  像素位数:%d\n", vinfo.xres, vinfo.yres,
	       vinfo.bits_per_pixel);
	//分配显存空间,完成图像显示
	lcd_display_buff =
		malloc(vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
	/*3. 获取固定参数*/
	if (ioctl(lcd_fd, FBIOGET_FSCREENINFO, &finfo))
		return -3;
	printf("smem_len=%d Byte,line_length=%d Byte\n", finfo.smem_len,
	       finfo.line_length);

	/*4. 映射LCD屏物理地址到进程空间*/
	lcd_mem_p = (unsigned char *)mmap(0, finfo.smem_len,
					  PROT_READ | PROT_WRITE, MAP_SHARED,
					  lcd_fd, 0); //从文件的那个地方开始映射
	memset(lcd_mem_p, 0xFFFFFF, finfo.smem_len);
	printf("映射LCD屏物理地址到进程空间\n");
	return 0;
}

static void close_device(void)
{
	if (-1 == close(video_fd))
		errno_exit("close");
	video_fd = -1;

	if (-1 == close(lcd_fd))
		errno_exit("close");
	lcd_fd = -1;
}

static void open_device(void)
{
	video_fd = open(dev_video, O_RDWR /* required */ | O_NONBLOCK, 0);
	if (-1 == video_fd) {
		fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_video, errno,
			strerror(errno));
		exit(EXIT_FAILURE);
	}

	lcd_fd = open(dev_fb0, O_RDWR, 0);
	if (-1 == lcd_fd) {
		fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_fb0, errno,
			strerror(errno));
		exit(EXIT_FAILURE);
	}
}
/*
将YUV格式数据转为RGB
*/
void yuv_to_rgb(unsigned char *yuv_buffer, unsigned char *rgb_buffer,
		int iWidth, int iHeight)
{
	int x;
	int z = 0;
	unsigned char *ptr = rgb_buffer;
	unsigned char *yuyv = yuv_buffer;

	for (x = 0; x < iWidth * iHeight; x++) {
		int r, g, b;
		int y, u, v;
		if (!z)
			y = yuyv[0] << 8;
		else
			y = yuyv[2] << 8;
		u = yuyv[1] - 128;
		v = yuyv[3] - 128;
		r = (y + (359 * v)) >> 8;
		g = (y - (88 * u) - (183 * v)) >> 8;
		b = (y + (454 * u)) >> 8;
		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b); // b color
		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g); // g color
		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r); // r color
		*(ptr++) = 0xff;                                // a color
		if (z++) {
			z = 0;
			yuyv += 4;
		}
	}
}

void rgb24_to_rgb565(char *rgb24, char *rgb16)
{
	int i = 0, j = 0;
	for (i = 0; i < 240 * 240 * 3; i += 3) {
		rgb16[j] = rgb24[i] >> 3; // B
		rgb16[j] |= ((rgb24[i + 1] & 0x1C) << 3); // G
		rgb16[j + 1] = rgb24[i + 2] & 0xF8; // R
		rgb16[j + 1] |= (rgb24[i + 1] >> 5); // G

		j += 2;
	}
}

static void lcd_image(unsigned int start_x, unsigned int end_x,
                    unsigned int start_y, unsigned int end_y,
                    unsigned char* color)
{
    unsigned long i;
    unsigned int j;
    /* 填充颜色 */
    i = start_y * vinfo.xres; //定位到起点行首

    for ( ; start_y <= end_y; start_y++, i+=(vinfo.xres*4))
    {
        for (j = start_x; j <= end_x*4; j++)
        {
            lcd_mem_p[i + j] = *color++;
        }
		*color--;
    }
}





int stopflag=0;

#include <fcntl.h> // for open
#include <stdio.h>
#include <unistd.h> // for close
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

//在 ctrl+c 后,主循环中的while(1)在此期间不会退出 exit(0)时,主循环在while(1)之后的程序也不会被执行
// int stopflag=0;
void sigintHandler(int signum) {
    //退出
    if(stopflag>0){return ;}
    stopflag=1;
	usleep(1000*200);//等待退出
	printf("waitfor normal exit!!!\n");
    exit(0);
}






int main(int argc, char **argv)
{
	// 注册SIGINT信号处理函数
    signal(SIGINT, sigintHandler);

	if(argc==2){
		strcpy(dev_video,argv[1]);
	}
	else{
		strcpy(dev_video,"/dev/video0");
	}


	dev_fb0 = "/dev/fb0";

    printf("camera:%s\n",dev_video);
    printf("fb:%s\n",dev_fb0);


	open_device();

	video_init();
	lcd_init();
	/*3. 读取摄像头的数据*/
	struct pollfd video_fds;
	video_fds.events = POLLIN;
	video_fds.fd = video_fd;

	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
	rgb_buffer = malloc(CAM_WIDTH * CAM_HEIGHT * 4);
	yuv_buffer = malloc(CAM_WIDTH * CAM_HEIGHT * 4);
	unsigned char *rgb_p;
	int w, h, i, j;
	unsigned char r, g, b;
	unsigned int c;

	int flag_loss = 0;
	while (!stopflag) {// stopflag
		/*等待摄像头采集数据*/
		poll(&video_fds, 1, -1);
		/*得到缓冲区的编号*/
		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		video_buffer.memory = V4L2_MEMORY_MMAP;
		ioctl(video_fd, VIDIOC_DQBUF, &video_buffer);

		// 数据丢帧显示
		if(flag_loss%3==0){
			yuv_to_rgb(video_buff_buff[video_buffer.index], yuv_buffer, video_height, video_width);

            // 数据检测

            // 数据显示
			lcd_image(0, CAM_WIDTH, 0, CAM_HEIGHT, yuv_buffer);
		}
		flag_loss+=1;

		/*将缓冲区放入采集队列*/
		ioctl(video_fd, VIDIOC_QBUF, &video_buffer);
		//printf("将缓冲区放入采集队列\n");
	}
	/*4. 关闭视频设备*/
	close_device();

	printf("normal exit!!!\n");	

	return 0;
}

对应makefile文件

点击查看代码
PRO_DIR ?= ${shell pwd}
export ARCH=arm
export CROSS_COMPILE=/home/book/tina-d1-h/prebuilt/gcc/linux-x86/arm/toolchain-sunxi-musl/toolchain/bin/arm-openwrt-linux-muslgnueabi-
export STAGING_DIR=/home/book/tina-d1-h/out/t113-100ask/staging_dir
CC := $(CROSS_COMPILE)gcc

# 外部头文件目录  库文件,库目录  
CFLAGS += -I/home/book/tina-d1-h/out/t113-100ask/staging_dir/target/usr/include/allwinner
CFLAGS += -I/home/book/tina-d1-h/prebuilt/gcc/linux-x86/arm/toolchain-sunxi-musl/toolchain/include
LDFLAGS += -L/home/book/tina-d1-h/prebuilt/gcc/linux-x86/arm/toolchain-sunxi-musl/toolchain/lib/
CFLAGS += -I./include
CFLAGS += -I/home/book/tina-d1-h/out/t113-100ask/compile_dir/target/libuapi/ipkg-install/usr/include
LDFLAGS += -L/home/book/tina-d1-h/out/t113-100ask/staging_dir/target/usr/lib
LDFLAGS += -L./lib  -lpthread -ldface -ldl -lMNN 

# 使用gcc编译c++
CFLAGS += -lstdc++ -mfloat-abi=hard
CFLAGS += -DDEBUG -g # -lbacktrace

usb_lcd:
	$(CC) -o 12_safe_lcd camera_usb_yuyv_lcd.c
	adb push ./12_safe_lcd /mnt/UDISK

#    /mnt/UDISK/12_safe_lcd

5 数据下发

此处采用adb进行文件下发(在makefile中已经包含了下发指令)
关于adb设置有一下几个注意点


如果忘记了,或者不弹窗了,点击虚拟机左下角,点击链接就可以了,

测试指令 adb devices 正常情况如下

异常情况如下, adb devices 指令找不到设备,解决方案参考
https://blog.csdn.net/linpeach/article/details/109027746

常用指令

ls /etc/udev/rules.d/
echo SUBSYSTEM=="usb",ATTRS{idVendor}=="18d1",ATTRS{idProduct}=="d002",MODE="0666",GROUP="plugdev",SYMLINK+="android",SYMLINK+="android_adb" > /etc/udev/rules.d/90-android.rules

sudo udevadm control --reload-rules
sudo service udev restart
sudo udevadm trigger
adb kill-server
adb start-server

参考    https://www.cnblogs.com/feiquan/p/12806081.html


传文件   
adb pull <手机路径>   <本机路径>  从手机中拉取信息到本地电脑上
adb push <本机路径>  <手机路径>  从本地电脑推送信息到手机上

adb pull /root/test.txt ./
adb push ./test.txt /root/

6 程序运行如下

执行指令 /mnt/UDISK/12_safe_lcd默认使用 /dev/video0

显示效果如下

7 其他常用指令

时间校准
date -s 2024-04-18     
date -s 16:47:00
开发板加入临时运行库目录
export LD_LIBRARY_PATH=/mnt/UDISK/lib:$LD_LIBRARY_PATH
posted @ 2024-05-23 15:58  小城熊儿  阅读(12)  评论(0编辑  收藏  举报