zynq学习笔记——EMIO方式模拟SCCB时序进行读写操作
2017-06-04 10:36
447 查看
一、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
EMIO_init.c
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
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; }
相关文章推荐
- zynq学习笔记——EMIO方式模拟I2C时序对ADV7511进行读写
- Fileatream表示文件流,它能够打开和关闭文件,并对文件进行单字节的读写操作。 StreamReader和StreamWriter以文本方式对流进行读写操作。建立一个文本文件,分别使用上面两种方
- 使用面向对象的方式模拟TOM使用ATM进行存取款的操作
- PIC单片机-普通IO口模拟I2C总线对24C02进行读写操作
- java各种文件的读写,通过不同的方式进行操作。
- 使用面向对象的方式模拟TOM使用ATM进行存取款的操作
- 通过文件的方式对硬盘扇区进行直接读写操作
- VS用OLE方式对Excel进行读写操作
- C++中对文件进行读写操作
- C++中对文件进行读写操作
- C++中对文件进行读写操作
- 解决模拟MOSS用户调用WebService打开个人站点进行操作
- C++中对文件进行读写操作
- .NET中对串口(COM)读写操作方式汇总
- ACCESS利用语句进行表的操作方式
- C++中对文件进行读写操作
- C++中对文件进行读写操作
- C++中对文件进行读写操作
- C++中对文件进行读写操作
- C++中对文件进行读写操作