doubao_chat/apps/wifi_camera/bt656_camera_get_yuv.c

334 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "system/includes.h"
#include "server/video_server.h"
#include "app_config.h"
#include "asm/debug.h"
#include "asm/osd.h"
#include "asm/isc.h"
#include "app_database.h"
#include "storage_device.h"
#include "os/os_api.h"
#include "camera.h"
#include "yuv_soft_scalling.h"
#ifdef CONFIG_VIDEO1_ENABLE
/*============================================================================================================*/
/********************BT656格式的-1/2/4bit摄像头只获取YUV使用说明*****************************************
请注意:下列说明关系到是否正确收到BT656-1/2/4bit摄像头的YUV数据
MSB正向:MSB=D7,LSB=D0,(D7为Sensor_D7的IO,D0为Sensor_D0的IO)
MSB反向:MSB=D0,LSB=D7,(D7为Sensor_D7的IO,D0为Sensor_D0的IO)
MSB反向则移位为左移,MSB正向则移位右移
遵循:MSB反向左移,MSB正向右移;先反向,再左右移原则
协议:BT656-1/2/4bit,PCLK有时钟则有数据,PCLK无时钟则无数据,协议满足SPI-1/2/4bit协议
帧起始:ff0000ab
帧结束:ff0000b6
行起始:ff000080
行结束:ff00009d
1、1bit情况下,不用关心移位和反向问题(内部已经处理好)
2、2bit/4bit情况下
关键函数:bt656_one_line_databits_config()
void bt656_one_line_databits_config(u32 databit, u8 msb_lsb_rev, u8 shift);
//databit:SEN_MBUS_DATA_WIDTH_2B/SEN_MBUS_DATA_WIDTH_1B/SEN_MBUS_DATA_WIDTH_4B
//msb_lsb_rev:0正向,1反向
//shift:移位(0-7)
(1)使用bt656_one_line_xxx函数,则PCLK只能选择IO_PORTC_08和IO_PORTH_04
①在PCLK选择IO_PORTC_08下:数据位只能从IO_PORTC_00到IO_PORTC_07,对应的YUV-ISC硬件模块的输入IO的数据位对应为:D7->D0对应PC0->PC7(与IO芯片IO顺序刚好相反)。
如摄像头为2bit两线数据线,PC0、PC1连接了摄像头D0和D1数据,摄像头D0为低位数据,D1为高位数据,由于YUV-ISC硬件模块输入高位MSB对应的IO-D7优先输入,
则需要如下步骤进行分析:
YUV-ISC硬件模块的输入到IO为MSB先输入,再依次输入低位,由于是2bit,所以D7先输入高位(PC0-高位),D6再输入低位(PC1-低位),
那么IO高低位和摄像头高低位相反(需求:PC0、PC1连接了摄像头D0和D1数据,D0为低位数据,D1为高位数据),因此需要设置反向输入。
因此需要选反向输入再移位,如下(遵循:先MSB-LSB反向,再左右移原则,MSB反向为左移,MSB正向为右移):
首先:设置反向输入,即MSB=D0,LSB=D7,此时2bit下输入为D0和D1(PC7和PC6),高位在D0,低位在D1(高位在PC7,低位在PC6)(bt656_one_line_databits_config函数的第二个参数为1)。
再次:左移6位,上步骤后下输入为D0和D1,左移6位后,输入为D(0+6)和D(1+6),即输入为D6和D7(高位在PC1,低位在PC0),达到符合的需求:PC0、PC1连接了摄像头D0和D1数据,D0为低位数据,D1为高位数据(bt656_one_line_databits_config函数的第三个参数为6)。
②在PCLK选择IO_PORTH_04下:数据位只能为IO_PORTH_03, IO_PORTH_02, IO_PORTH_06, IO_PORTH_05,对应的YUV硬件模块的输出到IO的数据位对应为:D0->PH3、D1->PH2、D3->PH6、D4->PH5、D5到D7无对应IO。
如摄像头为2bit两线数据线,PH3、PH2连接了摄像头D0和D1数据,摄像头D0为低位数据,D1为高位数据,由于YUV-ISC硬件模块输入高位MSB对应的IO-D7优先输入,
则需要如下步骤进行分析:
YUV-ISC硬件模块的输入到IO为MSB先输入,再依次输入低位,由于是2bit,所以D7先输入高位,D6再输入低位,由于没有D7和D6的IO,因此需要右移6位,
把输入移到D1和D0,刚好高低位符合需求:PH3、PH2连接了摄像头D0和D1数据,摄像头D0为低位数据,D1为高位数据。
因此只需要移位(遵循:先MSB-LSB反向,再左右移原则,MSB反向为左移,MSB正向为右移),即:bt656_one_line_databits_config函数的第二个参数为0,第三个参数为6)
***********************************************************************************************/
//******************摄像头时钟输出IO配置********************//
#define CAMERA_VIDEO_POWERDOWN_IO IO_PORTH_09 //powedown引脚
#define CAMERA_VIDEO_RESET_IO IO_PORTH_07 //reset引脚
#define CAMERA_VIDEO_XMCLK_IO IO_PORTH_08 //xmclk引脚
//******************BT656硬件接收IO配置********************//
#define CAMERA_VIDEO_PCLK_PORT IO_PORTH_04 //pclk选择的IO
#define CAMERA_VIDEO_START_IO IO_PORTH_03 //数据位的低位IO,2/4bit下也只需设置1个IO,但是必须是对应的最低位
/*#define CAMERA_VIDEO_PCLK_PORT IO_PORTC_08*/
/*#define CAMERA_VIDEO_START_IO IO_PORTC_05*/
//******************摄像头分辨率**************************//
#define CAMERA_VIDEO_W CONFIG_VIDEO1_IMAGE_W //摄像头的分辨率-宽,需要和摄像头驱动设置的输出分辨率寄存器配置一致
#define CAMERA_VIDEO_H CONFIG_VIDEO1_IMAGE_H //摄像头的分辨率-高,需要和摄像头驱动设置的输出分辨率寄存器配置一致
#define CAMERA_ONFRAM_YUV420_SIZE (CAMERA_VIDEO_W * CAMERA_VIDEO_H * 3 / 2)//一帧YUV420大小
#define IIC_DEV_ID 1 //isp_dev参数:0则打开"iic0"设备,1则打开"iic1"设备,board.c需要配置好IIC的IO
/*============================================================================================================*/
//******************yuv vdeo状态**************************//
#define CAMERA_VIDEO_IDEL 0x0
#define CAMERA_VIDEO_OPEN 0x1
#define CAMERA_VIDEO_CLOSE 0x3
#define CAMERA_VIDEO_YUV_USE_DMA 1 //1使用硬件DMA拷贝,0使用memcpy
struct bt656_video {
u8 frame;
u8 init;
u8 state;
u8 frame_done;
u8 dma_copy_id;
u16 width;
u16 height;
u8 *app_yuv_buf;
u8 *yuv_buf;
u32 frame_size;
u32 pid;
void (*yuv_cb)(u8 *buf, u32 size, int width, int height);
OS_SEM sem;
} bt656_video_info = {0};
void sdfile_save_test(char *buf, int len, char one_file, char close);
/*
* 硬件接收完成回调函数,需要进行数据拷贝到应用层的BUF
*/
static int bt656_one_line_framedone_callback(void *priv, int id, void *blk_info)
{
struct yuv_block_info *block_info = (struct yuv_block_info *)blk_info;//获取回调参数
char *buf = (char *)block_info->y;//获取YUV地址
int len = block_info->ylen + block_info->ulen + block_info->vlen;//获取YUV长度
if (!bt656_video_info.frame_done && buf) {
/*复制YUV到应用层缓冲区*/
#if CAMERA_VIDEO_YUV_USE_DMA
bt656_video_info.dma_copy_id = dma_copy_async(bt656_video_info.app_yuv_buf, buf, len);
#else
memcpy(bt656_video_info.app_yuv_buf, buf, len);
#endif
bt656_video_info.frame_done = TRUE;
os_sem_post(&bt656_video_info.sem);
return 0;
}
return -EINVAL;
}
/*
* 用户的摄像头检测和打开函数:主要为配置摄像头寄存器
*/
static int camera_device_open(void)
{
int err = 0;
if (bt656_video_info.state == CAMERA_VIDEO_OPEN) {
printf("bt656 video is open \n");
return 0;
}
/* 1. 设置摄像头的IO和输出时钟给摄像头 */
gpio_direction_output(CAMERA_VIDEO_POWERDOWN_IO, 0); //设置powedown引脚正常工作电平,一般摄像头要求低电平,需要留意数据手册要求
gpio_direction_output(CAMERA_VIDEO_RESET_IO, 1); //设置reset引脚正常工作电平,一般摄像头要求高电平,需要留意数据手册要求
gpio_output_channle(CAMERA_VIDEO_XMCLK_IO, CH3_PLL_24M);//输出时钟给摄像头
/* 在2/4bit情况下:如果不清楚设置最低IO,则应用层需要设置IO的输入模式,打开上下拉和数字输入输出模式,如下2bit-PH3-PH2例子*/
/*
#if (CONFIG_SPI_ONE_LINE_ENABLE == 0)
gpio_direction_input(IO_PORTH_03);//输入
gpio_set_pull_up(IO_PORTH_03, 0);//关上拉
gpio_set_pull_down(IO_PORTH_03, 0);//关下拉
gpio_set_die(IO_PORTH_03, 1);//设置数字模式
gpio_direction_input(IO_PORTH_02);//输入
gpio_set_pull_up(IO_PORTH_02, 0);//关上拉
gpio_set_pull_down(IO_PORTH_02, 0);//关下拉
gpio_set_die(IO_PORTH_02, 1);//设置数字模式
#endif
*/
/* 2. 检测摄像头是否正常,GC0310_check函数的isp_dev参数:0则打开iic0设备,1则打开iic1设备,board.c需要配置好IIC的IO */
err = GC0310_check(IIC_DEV_ID, CAMERA_VIDEO_RESET_IO, CAMERA_VIDEO_POWERDOWN_IO);
if (err) {
printf("GC0310_check err\n");
return -1;
}
/* 3. 初始化摄像头,配置摄像头寄存器 */
err = GC0310_init(IIC_DEV_ID, NULL, NULL, NULL, NULL);
if (err) {
printf("GC0310_init err\n");
return -1;
}
bt656_video_info.state = CAMERA_VIDEO_OPEN;
printf("camera open ok\n");
return 0;
}
/*
* 用户的摄像头关闭函数:一般的摄像头为关闭xmclk或者进行reset引脚复位
*/
static int camera_device_close(void)
{
if (!bt656_video_info.init) {
printf("bt656 video no init \n");
return -EINVAL;
}
if (bt656_video_info.state == CAMERA_VIDEO_CLOSE) {
puts("spi video is close \n");
return 0;
}
/* 1. 复位摄像头和关闭时钟输出 */
gpio_direction_output(CAMERA_VIDEO_RESET_IO, 0);//设置reset引脚正常工作电平,一般摄像头要求高电平,需要留意数据手册要求
os_time_dly(1);
gpio_direction_output(CAMERA_VIDEO_RESET_IO, 1);//设置reset引脚正常工作电平,一般摄像头要求高电平,需要留意数据手册要求
gpio_clear_output_channle(CAMERA_VIDEO_XMCLK_IO, CH3_PLL_24M);//关闭输出时钟给摄像头
/* 2. 标记video状态 */
bt656_video_info.state = CAMERA_VIDEO_CLOSE;
return 0;
}
/*
* 用户的摄像头YUV获取任务
*/
static void bt656_video_yuv_task(void *priv)
{
int ret;
int msg[4];
/* 1. 创建信号量、初始化参数*/
bt656_video_info.width = CAMERA_VIDEO_W;
bt656_video_info.height = CAMERA_VIDEO_H;
bt656_video_info.frame_done = FALSE;
bt656_video_info.frame_size = CAMERA_ONFRAM_YUV420_SIZE;
if (!os_sem_valid(&bt656_video_info.sem)) {
os_sem_create(&bt656_video_info.sem, 0);
}
os_sem_set(&bt656_video_info.sem, 0);
/* 2. 申请buf*/
if (!bt656_video_info.yuv_buf) {
bt656_video_info.yuv_buf = malloc(bt656_video_info.frame_size * 2);//申请2帧数据缓存大小,1帧给硬件接收,1帧给应用层使用
if (!bt656_video_info.yuv_buf) {
printf("no mem bt656 video \n");
goto exit;
}
bt656_video_info.app_yuv_buf = bt656_video_info.yuv_buf + bt656_video_info.frame_size;
}
/* 2. bt656协议参数初始化*/
bt656_one_line_init((u32)bt656_video_info.yuv_buf, //硬件接收的YUV地址
bt656_video_info.frame_size, //传一帧YUV420大小
bt656_video_info.width, //宽
bt656_video_info.height, //高
SEN_IN_FORMAT_YUYV); //YUYV格式
/* 3. bt656-IO初始化*/
#if (CONFIG_SPI_ONE_LINE_ENABLE == 0)
bt656_one_line_io_init(CAMERA_VIDEO_PCLK_PORT, //摄像头pclk的IO,固定IO_PORTC_08和IO_PORTH_04
SEN_MBUS_PCLK_SAMPLE_FALLING,//摄像头pclk下降沿采样
0, //pclk是否进过硬件滤波器(默认0经过,1不经过)
CAMERA_VIDEO_START_IO, //数据线最低位IO
2); //数据线占2个IO,用于初始化IO,如上行参数为IO_PORTC_00,则会初始化PC0和PC1
#else
bt656_one_line_io_init(CAMERA_VIDEO_PCLK_PORT, //摄像头pclk的IO,固定IO_PORTC_08和IO_PORTH_04
SEN_MBUS_PCLK_SAMPLE_FALLING,//摄像头pclk下降沿采样
0, //pclk是否进过硬件滤波器(默认0经过,1不经过)
CAMERA_VIDEO_START_IO, //数据线最低位IO
1); //数据线占1个IO,用于初始化IO,如上行参数为IO_PORTC_00,则会初始化PC0
#endif
/* 4. bt656-帧完成回调函数配置*/
bt656_one_line_framedone_reg((int *)bt656_one_line_framedone_callback, NULL);
/* 5. 打开摄像头并配置输出分辨率等寄存器*/
if (camera_device_open()) {
printf("camera_device_open err \n");
goto exit;
}
/* 6. 使能硬件接收,在2/4bit下,最后一步在配置MSB正反向和移位(因为camera_device_open有可能会对MSB正反向和移位进行重置)*/
#if (CONFIG_SPI_ONE_LINE_ENABLE == 0)
/*
* 2/4bit下的bt656-数据IO移位配置,默认不配置则是单线的1bit,放在最后一步再设置反向和移位
* 移位和反向和需要输入的IO相关,需要注意!!!!!!!!!
*/
bt656_one_line_databits_config(SEN_MBUS_DATA_WIDTH_2B, 0, 6);//按照移位来配置对应输入IO
#endif
bt656_one_line_en(1);//最后一步再打开
bt656_video_info.init = TRUE;
while (1) {
os_taskq_accept(ARRAY_SIZE(msg), msg);
ret = os_sem_pend(&bt656_video_info.sem, 100);
if (thread_kill_req()) {
break;
}
if (!ret && bt656_video_info.frame_done) {
putchar('R');
#if CAMERA_VIDEO_YUV_USE_DMA
dma_copy_async_wait(bt656_video_info.dma_copy_id);
#endif
u8 *read_buf = bt656_video_info.app_yuv_buf;
u32 wlen = bt656_video_info.frame_size;
/* 7. 使用地址:read_buf,长度:wlen,可以在这里进行YUV的算法或者编码JPEG*/
/*
static u32 cnt = 0;
cnt++;
sdfile_save_test(read_buf, wlen, 1, cnt % 100 == 0);
*/
if (bt656_video_info.yuv_cb) { //yuv数据回调
bt656_video_info.yuv_cb(read_buf, wlen, bt656_video_info.width, bt656_video_info.height);
}
//使用结束需要清frame_done
bt656_video_info.frame_done = FALSE;
} else {
printf("yuv recv timeout \n");
}
}
/* 8. 关闭硬件模块和关闭摄像头*/
exit:
bt656_one_line_exit();
camera_device_close();
if (bt656_video_info.yuv_buf) {
free(bt656_video_info.yuv_buf);
bt656_video_info.yuv_buf = NULL;
}
bt656_video_info.init = FALSE;
printf("\n\n bt656_video_yuv_task exit \n\n");
}
/*
* 用户的摄像头YUV获取任务创建:参数为YUV回调函数
*/
int bt656_video_yuv_task_create(void (*cb)(u8 *buf, u32 size, int width, int height))
{
bt656_video_info.yuv_cb = cb;
return thread_fork("bt656_yuv_task", 10, 512, 64, &bt656_video_info.pid, bt656_video_yuv_task, NULL);
}
/*
* 用户的摄像头YUV获取任务删除
*/
void bt656_video_yuv_task_kill(void)
{
if (bt656_video_info.init) {
os_sem_post(&bt656_video_info.sem);
thread_kill(&bt656_video_info.pid, 0);//关闭再重新打开
while (bt656_video_info.init) {
os_time_dly(100);
printf("bt656_video_yuv_task_kill err\n");
}
}
}
#endif