一、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表示指定從機的地址
后面是數據,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;
}
-
SCCB
+關注
關注
0文章
11瀏覽量
11324 -
Vivado
+關注
關注
19文章
808瀏覽量
66349
發布評論請先 登錄
相關推薦
評論