EMIO方式模拟SCCB时序进行读写操作详解

一、sccb介绍
sccb是omnivision serial camera control bus的简称,即ov公司的串行摄像机控制总线。ov公司定义的sccb是一个3线结构,但是,为了缩减sensor的pin封装,sccb大多采用2线方式。
开始传输数据
结束数据传输
传输规则
一个基本传输单元称作一个相
一个相包含总共9比特,前8比特为数据,第9比特为 don‘t-care bit 不关心比特,该第9比特的数据取决于
传输任务是读还是写。一个传输任务的最大相个数是3
3相写传输规则
提供些传输,主机能将1byte数据写至指定从机
id address表示指定从机的地址
sub-address表示从机的寄存器
后面是数据,1字节数据
3个相为后一位都是don’t-care bits
2相写传输规则
2相写传送是在2相读传送前的,它的目的是指明主机要从哪个从机的哪个寄存器读数据。
2相读传输规则
在2相读前面必须有2相写或者3相写,否则2相读没有办法读出哪个寄存器发的数据,主机必须将na bit置为1(否则ov摄像头会把siod拉低)
“x”表示don‘t-care bit。意思是master可以不关注此bit。从ov给的手册来看,slave也可以驱动此bit为低,然后master来确认响应。当然,master也可以不关注。在sccb手册中,这部分说的比较含糊,所以,简单来说,在sccb中,master不关注是否传输数据有错误发生。说实话,为了所做的设计可靠,还是应该关注的。
二、emio介绍
zynq的gpio,分为两种,mio(multiuse i/o)和emio(extendable multiuse i/o)
mio分配在bank0和bank1直接与ps部分相连,emio分配在bank2和接和pl部分相连。除了bank1是22-bit之外,其他的bank都是32-bit。所以mio有53个引脚可供我们使用,而emio有64个引脚可供我们使用。
使用emio的好处就,当mio不够用时,ps可以通过驱动emio控制pl部分的引脚,接下来就来详细介绍下emio的使用。
emio的使用和mio的使用其实是非常相似的。区别在于,emio的使用相当于,是一个ps + pl的结合使用的例子。所以,emio需要分配引脚,以及编译综合生成bit文件。
三、例子
1、新建vivado 工程,create一个block design,添加zynq ps核
2、运行自动连接
3、双击ps ip进行配置,增加三个emio,其中两个是sccb的数据和时钟,另外一个拿来做复位
4、把新增的emio连接出来,并把时钟接好
5、增加一个clock ip,修改输出频率为24mhz,把输出管脚接出
5、create hdl wrapper,生产hdl顶层文件,双击打开,可以看到刚才接出来的emio管脚名为gpio_0_tri_io
6、创建约束文件,我的摄像头的siod接到了w8,sioc接到了v8,reset接到ab11,xclk接到w11
7、综合,生成bit文件,导出hardware并启动sdk,创建项目,添加如下代码
emio_init.h
#ifndef emio_init_h_
#define emio_init_h_
#include“xgpiops.h”
int emio_sccb_init(void);
#define siod_pin 54
#define sioc_pin 55
#define reset_pin 56
#define direction_input 0
#define direction_output 1
void clock_high(void);
void clock_low(void);
void data_high(void);
void data_low(void);
void data_input(void);
void data_output(void);
int get_data(void);
void sccb_reset(void);
#endif /* emio_init_h_ */
emio_init.c
#include “xgpiops.h”
#include “emio_init.h”
static xgpiops psgpioinstanceptr;
int emio_sccb_init(void)
{
xgpiops_config* gpioconfigptr;
int xstatus;
gpioconfigptr = xgpiops_lookupconfig(xpar_ps7_gpio_0_device_id);
if(gpioconfigptr == null)
return xst_failure;
xstatus = xgpiops_cfginitialize(&psgpioinstanceptr,gpioconfigptr,gpioconfigptr-》baseaddr);
if(xst_success != xstatus)
print(“emio init failed \n\r”);
xgpiops_setdirectionpin(&psgpioinstanceptr, sioc_pin,direction_output);
xgpiops_setdirectionpin(&psgpioinstanceptr, siod_pin,direction_output);
xgpiops_setdirectionpin(&psgpioinstanceptr, reset_pin,direction_output);
xgpiops_setoutputenablepin(&psgpioinstanceptr, sioc_pin,1);
xgpiops_setoutputenablepin(&psgpioinstanceptr, siod_pin,1);
xgpiops_setoutputenablepin(&psgpioinstanceptr, reset_pin,1);//
return xstatus;
}
void sccb_reset(void)
{
xgpiops_writepin(&psgpioinstanceptr,reset_pin, 0);
usleep(50*1000);
xgpiops_writepin(&psgpioinstanceptr,reset_pin, 1);
}
void clock_high(void)
{
xgpiops_writepin(&psgpioinstanceptr,sioc_pin, 1);
}
void clock_low(void)
{
xgpiops_writepin(&psgpioinstanceptr,sioc_pin, 0);
}
int get_data(void)
{
return xgpiops_readpin(&psgpioinstanceptr,siod_pin);
}
void data_input(void)
{
xgpiops_setdirectionpin(&psgpioinstanceptr, siod_pin,direction_input);//
}
void data_output(void)
{
xgpiops_setdirectionpin(&psgpioinstanceptr, siod_pin,direction_output);//
}
void data_high(void)
{
xgpiops_writepin(&psgpioinstanceptr,siod_pin, 1);
}
void data_low(void)
{
xgpiops_writepin(&psgpioinstanceptr,siod_pin,0);
}
sccb_ctrl.h
#ifndef sccb_ctrl_h_
#define sccb_ctrl_h_
void sccb_start(void);
void sccb_end(void);
void sccb_sendbyte( unsigned char value );
void sccb_senddata(unsigned char subaddr,unsigned char value);
int sccb_readdata(unsigned char addr, unsigned char *value);
#endif /* sccb_ctrl_h_ */
sccb_ctrl.c
#include “sleep.h”
#include “emio_init.h”
#define ov7670_write_addr 0x42
#define ov7670_read_addr 0x43
#define sccb_delay usleep(10)
void sccb_start(void)
{
clock_high();
data_high();
sccb_delay;
data_low();
sccb_delay;
clock_low();
sccb_delay;
}
void sccb_end(void)
{
data_low();
sccb_delay;
clock_high();
sccb_delay;
data_high();
sccb_delay;
}
int sccb_sendbyte( unsigned char value )
{
unsigned char tmp = value;
unsigned char i=0,ack;
for(i=0; i《8; i++)
{
if(tmp & 0x80 )
data_high();
else
data_low();
sccb_delay;
clock_high();
sccb_delay;
clock_low();
sccb_delay;
tmp《《=1;
}
data_high();
data_input();
sccb_delay;
clock_high();
ack = get_data();
sccb_delay;
clock_low();
sccb_delay;
data_output();
if(ack==1)
{
return -1;
}
return 0;
}
unsigned char sccb_readbyte( unsigned char addr)
{
unsigned char i=0,data=0;
data_high();
data_input();
for(i=0; i《8; i++)
{
clock_high();
sccb_delay;
data 《《= 1;
if(get_data())
data |= 1;
sccb_delay;
clock_low();
sccb_delay;
}
data_output();
data_high();
sccb_delay;
clock_high();
sccb_delay;
clock_low();
sccb_delay;
data_high();
return data;
}
int sccb_readdata(unsigned char addr, unsigned char *value)
{
// 两相写
sccb_start();
if(sccb_sendbyte(ov7670_write_addr) != 0)
{
sccb_end();
return -1;
}
if(sccb_sendbyte(addr) != 0)
{
sccb_end();
return -1;
}
sccb_end();
sccb_delay;
// 两相读
sccb_start();
if(sccb_sendbyte(ov7670_read_addr) != 0)
{
sccb_end();
return -1;
}
*value = sccb_readbyte(addr);
sccb_end();
return 0;
}
void sccb_senddata(unsigned char addr,unsigned char value)
{
sccb_start();
sccb_sendbyte(ov7670_write_addr);
sccb_sendbyte(addr);
sccb_sendbyte(value);
sccb_end();
}
修改main函数,读取pid和ver寄存器,验证是否正确,实际上我读取到的ver是0x73
int main()
{
unsigned char data;
init_platform();
print(“hello world\n\r”);
emio_sccb_init();
sccb_reset();
usleep(500*1000);
while(1)
{
//读取pid
data = 0;
if(sccb_readdata(0x0a,&data) != 0)
{
print(“error\n\r”);
}
else
{
if(data != 0x76)
print(“error\n\r”);
}
//读取ver
data = 0;
if(sccb_readdata(0x0b,&data) != 0)
{
print(“error\n\r”);
}
else
{
if(data != 0x73)
print(“error\n\r”);
}
}
cleanup_platform();
return 0;
}

用AI绘制细胞图谱 可提早诊断老化相关疾病
威航科技推出高效能Venus638FLPx GPS单芯片
「自行科技」用视觉算法助力安全出行
报警系统发展面临多重瓶颈 应用水平一直停滞不前
适于家居照明低成本LED驱动电源方案
EMIO方式模拟SCCB时序进行读写操作详解
2021年FTF青少年无人机大赛线上选拔赛举办
使用英特尔开发者套件搭建RTMP流媒体服务器
VR和AR在直播行业中的应用及其发展
由4G迈向5G,这一步有多大?
(TOSHIBA)东芝光耦:DIP8(LF1)封装
如果CPU坏了会发生什么?
哪款U盘最值得购买
安卓线、TypeC线、苹果线...为什么会有这么多数据线?
一文了解汽车液压助力转向系统
寒武纪宣布将在2019世界人工智能大会上展示新一代云端AI芯片
北快手南抖音_相比其他的短视频APP_微博该何去何从呢?
工业AI与商业AI的差异有何差异?
如何成功导入STEP9
4294A阻抗分析仪维修白屏案例