377 lines
13 KiB
C
377 lines
13 KiB
C
#include "app_config.h"
|
||
#include "system/includes.h"
|
||
#include "device/device.h"
|
||
#include "asm/spi.h"
|
||
#include "camera.h"
|
||
#include "device/gpio.h"
|
||
|
||
#ifdef CONFIG_SPI_VIDEO_ENABLE
|
||
/********************************************************************************************
|
||
*当SPI需要接收不固定数据量时,系统打开不可屏蔽方式,详情example的不可屏蔽中断使用方法
|
||
*1.首先系统打开不可屏蔽中断方式,SPI中断优先级设为7
|
||
*2.中断函数定义在内部sram方式
|
||
*3.中断函数用户自行处理
|
||
*4.注意:使用不可屏蔽方式,接收区间,禁止对flash进行写操作,读操作只能非代码区(除非系统程序代码跑sdram模式)
|
||
*********************************************************************************************/
|
||
|
||
/******************************以下为例子****************************************************/
|
||
|
||
//比如按照协议,对方主机SPI协议为如下,头4字节为信息,读取size可知道连续数据有多少字节。
|
||
//对方主机SPI协议发送为:先发4字节头信息,延时几个微秒就发送实际数据量,延时10几个毫秒,继续发送下一次数据,以此类推。
|
||
//注意结构体需要1字节对齐
|
||
|
||
struct spi_heads {
|
||
u8 init;
|
||
u8 reinit;
|
||
u8 kill;
|
||
u8 state;
|
||
u32 err;
|
||
volatile u32 fcnt;
|
||
void *camera_hdl;
|
||
};
|
||
|
||
#define JL_SPI JL_SPI2 //与spi设备对应:JL_SPI0 JL_SPI1 JL_SPI2
|
||
#define JL_SPI_NAME "spi2" //与spi设备对应:"spi1", "spi2"
|
||
|
||
//使用单线数据D0 接 spi口DO
|
||
/* SPI1_PLATFORM_DATA_BEGIN(spi1_data) */
|
||
/* .clk = 80000000, */
|
||
/*.mode = SPI_STD_MODE,*/
|
||
/* .attr = SPI_MODE_SLAVE | SPI_SCLK_L_UPH_SMPH | SPI_UNIDIR_MODE,//从机,CLK高 更新数据高,单向模式(SPI_DO单线) */
|
||
/* .port = 'B', */
|
||
/* SPI1_PLATFORM_DATA_END() */
|
||
|
||
/**********SPI state ************/
|
||
#define SPI_VIDEO_IDEL 0x0
|
||
#define SPI_VIDEO_OPEN 0x1
|
||
#define SPI_VIDEO_OPENING 0x2
|
||
#define SPI_VIDEO_CLOSE 0x3
|
||
#define SPI_VIDEO_CLOSEING 0x4
|
||
#define SPI_VIDEO_RESEST 0x5
|
||
|
||
/**********SPI video ************/
|
||
#define SPI_LSTART ntohl(0xff000080)
|
||
#define SPI_LEND ntohl(0xff00009d)
|
||
#define SPI_FSTART ntohl(0xff0000ab)
|
||
#define SPI_FEND ntohl(0xff0000b6)
|
||
#define SPI_FHEAD_SIZE 4
|
||
#define SPI_FEND_SIZE 4
|
||
#define SPI_LHEAD_SIZE 4
|
||
#define SPI_LEND_SIZE 4
|
||
|
||
#define SPI_CAMERA_W 176
|
||
#define SPI_CAMERA_H 128
|
||
|
||
#define SPI_CAMERA_ONLINE_SIZE (SPI_CAMERA_W + SPI_LHEAD_SIZE + SPI_LEND_SIZE)//y
|
||
#define SPI_CAMERA_ONFRAM_ONLY_Y_SIZE (SPI_CAMERA_ONLINE_SIZE * SPI_CAMERA_H + SPI_FHEAD_SIZE + SPI_FEND_SIZE)
|
||
/**********SPI video ************/
|
||
|
||
#define SPI_FIRST_DMA_SIZE (SPI_CAMERA_ONFRAM_ONLY_Y_SIZE + SPI_FHEAD_SIZE)//第一帧加第二帧的头部
|
||
#define SPI_DMA_SIZE (SPI_CAMERA_ONFRAM_ONLY_Y_SIZE)//第一帧加第二帧的头部
|
||
|
||
#define RECV_SIZE_MAX (SPI_CAMERA_ONFRAM_ONLY_Y_SIZE + SPI_FHEAD_SIZE + SPI_FEND_SIZE) //先了解协议1帧的最大字节数在多少再配置该值
|
||
#define RECV_FPS 5 //硬件缓冲帧切换使用,务必>=2帧
|
||
|
||
#define SPI_XCLK_PORT IO_PORTC_00
|
||
|
||
#define SPI_BUF_USE_SRAM 1 //使用内部sram,静态
|
||
#define RECV_BUF_SIZE (RECV_SIZE_MAX * RECV_FPS)
|
||
|
||
#if SPI_BUF_USE_SRAM
|
||
static volatile char recv_buf[RECV_BUF_SIZE] sec(.sram) ALIGNED(32);//spi硬件使用接收buf
|
||
#else
|
||
static volatile char *recv_buf = NULL;//spi硬件使用接收buf
|
||
#endif
|
||
static volatile char recv_read_cnt = 0;
|
||
static volatile char recv_write_cnt = 0;
|
||
static char *app_recv_buf = NULL;//应用层使用的buf
|
||
static volatile int app_recv_len = 0;
|
||
static struct spi_heads spi_head_info sec(.sram) = {0};
|
||
|
||
#define IRQ_USE_SEM_POST 0 //中断使用信号量通知
|
||
#if IRQ_USE_SEM_POST
|
||
static OS_SEM spi_sem;
|
||
#endif
|
||
|
||
/*#define SPI_IO_DBG*/
|
||
|
||
#ifdef SPI_IO_DBG
|
||
#define DBG_PORT_BIT BIT(3)
|
||
#define DBG_PORT JL_PORTB
|
||
#endif
|
||
//定义中断回调函数,注意:中断函数不能调用系统API(调试期间可加打印printf函数),调用函数需要用户自写,调用其他API也需要指定到指定在内部sram,即sec(.volatile_ram_code)
|
||
|
||
static ___interrupt void spi_irq_cb(void) //sec(.volatile_ram_code) //中断函数,指定在内部sram
|
||
{
|
||
JL_SPI->CON |= BIT(14);//清除中断标记
|
||
asm volatile("csync;");
|
||
#ifdef SPI_IO_DBG
|
||
DBG_PORT->OUT ^= DBG_PORT_BIT;
|
||
#endif
|
||
if (recv_buf) {
|
||
char *next_head_buf;
|
||
char *next_buf;
|
||
next_head_buf = (char *)(recv_buf + recv_write_cnt * RECV_SIZE_MAX + SPI_CAMERA_ONFRAM_ONLY_Y_SIZE);
|
||
if (((recv_write_cnt + 1) == RECV_FPS && !recv_read_cnt) || (recv_read_cnt && (recv_write_cnt + 1) == recv_read_cnt)) {
|
||
goto next_recv;
|
||
} else {
|
||
recv_write_cnt = (++recv_write_cnt) >= RECV_FPS ? 0 : recv_write_cnt;
|
||
}
|
||
next_recv:
|
||
next_buf = (char *)(recv_buf + recv_write_cnt * RECV_SIZE_MAX);
|
||
for (int i = 0; i < SPI_FHEAD_SIZE; i++) {
|
||
*next_buf++ = *next_head_buf++;
|
||
}
|
||
JL_SPI->ADR = (u32)next_buf;//偏移地址
|
||
JL_SPI->CNT = (u32)SPI_DMA_SIZE;//继续接收数据头
|
||
spi_head_info.fcnt++;
|
||
#if IRQ_USE_SEM_POST
|
||
os_sem_post(&spi_sem);//不可屏蔽不能使用2
|
||
#endif
|
||
}
|
||
#ifdef SPI_IO_DBG
|
||
DBG_PORT->OUT ^= DBG_PORT_BIT;
|
||
#endif
|
||
|
||
asm volatile("csync;");
|
||
}
|
||
|
||
static int spi_camera_open(void)
|
||
{
|
||
int err = 0;
|
||
if (spi_head_info.state == SPI_VIDEO_OPEN || spi_head_info.state == SPI_VIDEO_OPENING) {
|
||
puts("spi video is open \n");
|
||
return 0;
|
||
}
|
||
spi_head_info.state = SPI_VIDEO_OPENING;
|
||
/*------SPI摄像头的CLK,OUP_CH2 24M-----*/
|
||
gpio_output_channle(SPI_XCLK_PORT, CH1_PLL_24M);
|
||
|
||
if (spi_head_info.state == SPI_VIDEO_RESEST) {
|
||
printf("--->SPI_VIDEO_RESEST\n");
|
||
spi_head_info.state = SPI_VIDEO_OPENING;
|
||
if (spi_head_info.camera_hdl) {
|
||
dev_ioctl(spi_head_info.camera_hdl, CAMERA_SET_SENSOR_RESET, 0);
|
||
spi_head_info.state = SPI_VIDEO_OPEN;
|
||
}
|
||
} else {
|
||
spi_head_info.camera_hdl = dev_open("video1.0", NULL);
|
||
if (!spi_head_info.camera_hdl) {
|
||
printf("camera open err\n");
|
||
err = -EINVAL;
|
||
goto error;
|
||
}
|
||
}
|
||
spi_head_info.state = SPI_VIDEO_OPEN;
|
||
spi_head_info.init = 1;
|
||
printf("camera open ok\n");
|
||
return 0;
|
||
|
||
error:
|
||
spi_head_info.state = SPI_VIDEO_CLOSE;
|
||
return err;
|
||
}
|
||
static int spi_camera_close(void)
|
||
{
|
||
if (!spi_head_info.init) {
|
||
printf("spi_video no init \n");
|
||
return -EINVAL;
|
||
}
|
||
if (spi_head_info.state == SPI_VIDEO_CLOSE || spi_head_info.state == SPI_VIDEO_CLOSEING) {
|
||
puts("spi video is close \n");
|
||
return 0;
|
||
}
|
||
spi_head_info.state = SPI_VIDEO_CLOSEING;
|
||
|
||
gpio_clear_output_channle(SPI_XCLK_PORT, CH1_PLL_24M);
|
||
|
||
if (spi_head_info.camera_hdl) {
|
||
dev_close(spi_head_info.camera_hdl);
|
||
spi_head_info.camera_hdl = NULL;
|
||
}
|
||
spi_head_info.state = SPI_VIDEO_CLOSE;
|
||
return 0;
|
||
}
|
||
static void spi_recv_task(void *p)
|
||
{
|
||
void *spi_hdl = NULL;
|
||
int ret;
|
||
int wlen, offset, online_data_size, sline_cnt;
|
||
int *head, *end;
|
||
char *read_buf;
|
||
int now = 0, last = 0, last_fp = 0;
|
||
int msg[4];
|
||
u32 timer_get_ms(void);
|
||
|
||
//1.打开spi设备
|
||
spi_hdl = dev_open(JL_SPI_NAME, NULL);
|
||
if (!spi_hdl) {
|
||
printf("spi open err \n");
|
||
goto exit;
|
||
}
|
||
//2.注册中断函数
|
||
dev_ioctl(spi_hdl, IOCTL_SPI_SET_IRQ_FUNC, (u32)spi_irq_cb);
|
||
|
||
#if IRQ_USE_SEM_POST
|
||
os_sem_create(&spi_sem, 0);
|
||
#endif
|
||
|
||
#if !SPI_BUF_USE_SRAM
|
||
recv_buf = malloc(RECV_BUF_SIZE);
|
||
if (!recv_buf) {
|
||
printf("err spi no buf \n");
|
||
goto exit;
|
||
}
|
||
#endif // SPI_BUF_USE_SRAM
|
||
|
||
reinit:
|
||
ret = spi_camera_open();
|
||
if (ret) {
|
||
printf("spi_camera_open err \n\n");
|
||
goto exit;
|
||
}
|
||
|
||
/*for(int i = 0; i<32;i++){*/
|
||
/*recv_buf[RECV_BUF_SIZE + i] = (u8)i;*/
|
||
/*}*/
|
||
|
||
//3.配置接收模式,开启中断,开启SPI
|
||
JL_SPI->CON &= ~(BIT(10) | BIT(11) | BIT(2)); //1bit模式
|
||
JL_SPI->CON |= BIT(12);//接收模式
|
||
JL_SPI->CON |= BIT(13);//开启中断
|
||
JL_SPI->CON |= BIT(0);//开启SPI
|
||
|
||
#ifdef SPI_IO_DBG
|
||
DBG_PORT->DIR &= ~DBG_PORT_BIT;
|
||
DBG_PORT->PU &= ~DBG_PORT_BIT;
|
||
DBG_PORT->PD &= ~DBG_PORT_BIT;
|
||
DBG_PORT->OUT &= ~DBG_PORT_BIT;
|
||
#endif
|
||
spi_head_info.fcnt = 0;
|
||
spi_head_info.err = 0;
|
||
recv_write_cnt = 0;
|
||
recv_read_cnt = 0;
|
||
app_recv_buf = NULL;
|
||
//4.配置接收地址,如对方主机SPI协议为如下,注意:写JL_SPI->CNT之前需保证此时对方SPI还没有发送数据,否则中途接收数据会出错
|
||
JL_SPI->ADR = (u32)recv_buf;//配置硬件接收地址
|
||
//此处根据实际应用选择接收字节数,比如收到4字节数据头,在数据头解析后可知下次应该收多少字节
|
||
JL_SPI->CNT = (u32)SPI_FIRST_DMA_SIZE;//写该寄存器则正式启动硬件接收,例如此处先接收4字节,接收 SPI_DMA_FISRT_SIZE 字节完就进spi_irq_cb中断解析
|
||
|
||
printf("---> SPI_FIRST_DMA_SIZE = %d ,SPI_DMA_SIZE = %d\n", SPI_FIRST_DMA_SIZE, SPI_DMA_SIZE);
|
||
printf("---> spi_head_info.kill = %d \n", spi_head_info.kill);
|
||
while (1) {
|
||
//5.接收数据查询
|
||
|
||
#if IRQ_USE_SEM_POST
|
||
ret = os_sem_pend(&spi_sem, 10);
|
||
#else
|
||
// os_time_dly(1);//延时1个tick,防止看门狗。不允许丢帧则注释这行:当注释掉这个行代码时,该任务优先级需要8以下(否则影响系统其他任务调度)。
|
||
taskYIELD();//可以使用空闲时候调度任务一次
|
||
#endif
|
||
|
||
if (spi_head_info.kill) {
|
||
spi_head_info.reinit = 0;
|
||
printf("---> kill req \n");
|
||
break;
|
||
}
|
||
|
||
#if 1
|
||
now = timer_get_ms();
|
||
if (last && (now - last) >= 1000) {
|
||
printf("---> camera fp = %d \n", spi_head_info.fcnt - last_fp);
|
||
last_fp = spi_head_info.fcnt;
|
||
last = now;
|
||
} else if (!last) {
|
||
last = now;
|
||
}
|
||
#endif
|
||
if (recv_read_cnt < recv_write_cnt || recv_read_cnt != recv_write_cnt) {
|
||
app_recv_buf = (char *)(recv_buf + recv_read_cnt * RECV_SIZE_MAX);
|
||
app_recv_len = SPI_CAMERA_ONFRAM_ONLY_Y_SIZE;
|
||
head = (int *)app_recv_buf; //校验
|
||
end = (int *)&app_recv_buf[app_recv_len - SPI_FEND_SIZE]; //校验
|
||
if (*head == SPI_FSTART && *end == SPI_FEND) {
|
||
read_buf = (char *)app_recv_buf;
|
||
read_buf += SPI_FHEAD_SIZE;//偏移头
|
||
wlen = 0;
|
||
sline_cnt = 0;
|
||
online_data_size = SPI_CAMERA_ONLINE_SIZE - SPI_LHEAD_SIZE - SPI_LEND_SIZE;
|
||
while (wlen < SPI_CAMERA_ONFRAM_ONLY_Y_SIZE && read_buf < (read_buf + app_recv_len)) {
|
||
head = (int *)read_buf;
|
||
if (*head == SPI_LSTART) {
|
||
sline_cnt++;
|
||
memcpy((char *)app_recv_buf + wlen, (char *)((int)head + SPI_LHEAD_SIZE), online_data_size);
|
||
wlen += online_data_size;
|
||
read_buf += SPI_CAMERA_ONLINE_SIZE;
|
||
continue;
|
||
} else if (*head == SPI_FEND) {
|
||
break;
|
||
}
|
||
read_buf += SPI_LHEAD_SIZE;
|
||
}
|
||
if (sline_cnt == SPI_CAMERA_H) {
|
||
void send_yuv_data(char *buf, u16 len);
|
||
send_yuv_data(app_recv_buf, wlen);
|
||
//todo,数据处理
|
||
}
|
||
spi_head_info.err = 0;
|
||
} else {
|
||
spi_head_info.err++;
|
||
printf("---> %d, %d \n", recv_read_cnt, recv_write_cnt);
|
||
printf("--->recv err 0x%x, 0x%x \n", *head, *end);
|
||
if (spi_head_info.err >= 5) {
|
||
spi_head_info.reinit = TRUE;//需要重新打开摄像头
|
||
spi_head_info.state = SPI_VIDEO_RESEST;
|
||
break;
|
||
}
|
||
}
|
||
//读取++
|
||
recv_read_cnt = (++recv_read_cnt) >= RECV_FPS ? 0 : recv_read_cnt;
|
||
}
|
||
|
||
}
|
||
|
||
exit:
|
||
spi_camera_close();
|
||
if (spi_hdl) { //关闭spi设备
|
||
JL_SPI->CON &= ~BIT(0);
|
||
dev_close(spi_hdl);
|
||
}
|
||
if (spi_head_info.reinit) {
|
||
goto reinit;
|
||
}
|
||
#if !SPI_BUF_USE_SRAM
|
||
if (recv_buf) { //释放内存
|
||
free(recv_buf);
|
||
recv_buf = NULL;
|
||
}
|
||
#endif
|
||
spi_head_info.kill = 0;
|
||
while (1) { //等待其他任务杀此任务
|
||
os_time_dly(100);//延时100个tick,防止看门狗
|
||
}
|
||
}
|
||
|
||
int spi_recv_task_init(void)
|
||
{
|
||
spi_head_info.kill = 0;
|
||
os_task_create(spi_recv_task, NULL, 20, 2048, 0, "spi_recv_task");
|
||
return 0;
|
||
}
|
||
int spi_recv_task_kill(void)
|
||
{
|
||
printf("---> spi_recv_task_kill\n");
|
||
spi_head_info.kill = 1;
|
||
#if IRQ_USE_SEM_POST
|
||
os_sem_post(&spi_sem);
|
||
#endif
|
||
while (spi_head_info.kill) {
|
||
os_time_dly(2);
|
||
}
|
||
task_kill("spi_recv_task");
|
||
spi_head_info.kill = 0;
|
||
return 0;
|
||
}
|
||
#endif
|
||
|