334 lines
15 KiB
C
334 lines
15 KiB
C
#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
|
||
|