zynq學習筆記——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)

wps10CB.tmp

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;
}



發佈了87 篇原創文章 · 獲贊 59 · 訪問量 55萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章