软件SCCB协议学习笔记

一、SCCB协议简介

    SCCB协议是OV公司公布的摄像头用的串行总线协议,相当于一个简易的I2C协议。本文写作意图在于记录使用软件SCCB驱动OV7670模块的配置方法。

二、SCCB协议时序简介(二线)

软件SCCB协议学习笔记_第1张图片

    这是官方历程中给出的SCCB时序图,虽然是三线的但是我们也可以用来学习二线的时序。

2.1 软件引脚初始化

    任意挑选两个IO口作为时钟线和数据线,将时钟线设置为推挽输出模式,数据线在输出时为推挽输出模式;在输入时是上拉输入模式。

2.2 协议开始

    在开始数据传输时,SIO_C要先处于高电平,由SIO_D先保持一段时间高电平并产生下降沿来标志传输开始。
软件SCCB协议学习笔记_第2张图片

2.3 协议结束

    在停止数据传输时,SIO_C也要首先置于高电平,由SIO_D产生上升沿并保持一段时间来标志传输结束。
软件SCCB协议学习笔记_第3张图片

2.4 软件延时

    软件延时是SCCB协议中的关键所在,如若其他配置都已正确而无法读写数据,那么简易通过示波器查看SIO_C和SIO_D两根线查看波形,调整其时序。

2.5 NACK回应

    在进行收发时,主机或是从机需要在接收数据之后向对方进行应答,而在接收完毕时就不需要应答了。ACK不需要软件编写,而NACK需要。在NACK应答开始时先将SIO_D拉高,再将SIO_C置于高电平,因为在时钟线为高电平时,数据线是不能改变的。保持一段时间后,将时钟线拉低,延时,再将数据线拉低,至此完成NACK信号的发送。

2.6 读取Byte数据

    首先,需要将SIO_D修改成输入模式,因为Byte有8位数据,所以用一个循环读取数据,每读取一个BIT便将其左移一位,在读取8次后可以获得一个完整的字节。读取数据时,需要将时钟线置于高电平,此时可以读取数据线上的数据,保存后将时钟线拉低为下一次接收做准备。

2.7发送Byte数据

    首先,需要将SIO_D修改成输出模式,因为Byte有8位数据,所以用一个循环发送,高位在前,每发送一个数据后,判断下一位是0或是1,判断结束后发送。发送前,时钟线为低,将数据线置于需要的电平后,将时钟线拉高,保持一段时间,至此完成发送,并将时钟线拉低为下一次发送做准备。

2.8 读取寄存器

    读取寄存器分为几个步骤:1.协议开始,2.发送设备ID编号,等待,3. 发送要读取的寄存器号,等待,4.协议结束,5.协议开始,6.发送设备ID编号并将其最后一位置1,等待,7.读取Byte数据,8.NACK回应,9.协议结束。
软件SCCB协议学习笔记_第4张图片
软件SCCB协议学习笔记_第5张图片

2.9 写入寄存器

    1.协议开始,2.发送设备ID编号,等待,3. 发送要存入的寄存器号,等待,4.发送要写入的数据,5.协议结束。

软件SCCB协议学习笔记_第6张图片

3.实现代码

#include "sccb.h" #include "./iodefine.h"
void sub_SDA_DirectSet( unsigned char u8_Tmp ) {   DDR_EESDA = u8_Tmp; }
void sub_SCL_DirectSet( unsigned char u8_Tmp ) {   DDR_EESCL = u8_Tmp; }
void sub_SDA_Set( unsigned char u8_Tmp ) {  IO_EESDAO = u8_Tmp; }
void sub_SCL_Set( unsigned char u8_Tmp ) {   IO_EESCL = u8_Tmp; }
unsigned char sub_SDA_Read( void ) {   unsigned char u8_Tmp;
  u8_Tmp = IO_EESDAI;
  return u8_Tmp; }
//初始化SCCB接口 //CHECK OK void SCCB_Init(void) {    sub_SDA_DirectSet( _OUTPUT );    sub_SCL_DirectSet( _OUTPUT );    sub_SCL_Set( _HIGH );    I2C_Flash();    sub_SDA_Set( _HIGH );    I2C_Flash(); }
void I2C_Flash( void ) {   unsigned char i,j;
  for( i=0; i<20; i++ )   {    for(j=0;j<20;j++){
   }   } }
//SCCB起始信号 //当时钟为高的时候,数据线的高到低,为SCCB起始信号 //在激活状态下,SDA和SCL均为低电平 void SCCB_Start(void) {
   sub_SDA_Set( _HIGH );    sub_SCL_Set( _HIGH );    I2C_Flash();    sub_SDA_Set( _LOW );    I2C_Flash();    sub_SCL_Set( _LOW ); }
//SCCB停止信号 //当时钟为高的时候,数据线的低到高,为SCCB停止信号 //空闲状况下,SDA,SCL均为高电平 void SCCB_Stop(void) {    sub_SDA_Set( _LOW );    I2C_Flash();    sub_SCL_Set( _HIGH );    I2C_Flash();    sub_SDA_Set( _HIGH );    I2C_Flash(); } //产生NA信号 void SCCB_No_Ack(void) {  I2C_Flash();  sub_SDA_Set( _HIGH );  sub_SCL_Set( _HIGH );  I2C_Flash();  sub_SCL_Set( _LOW );  I2C_Flash();  sub_SDA_Set( _LOW );  I2C_Flash(); }
//SCCB,写入一个字节 //返回值:0,成功;1,失败. unsigned char SCCB_WR_Byte(unsigned char dat) {  unsigned char j,res;  for(j=0;j<8;j++) //循环8次发送数据  {   if(dat&0x80)sub_SDA_Set( _HIGH );   else sub_SDA_Set( _LOW );   dat<<=1;   I2C_Flash();   sub_SCL_Set( _HIGH );   I2C_Flash();   sub_SCL_Set( _LOW );  }  sub_SDA_DirectSet( _INPUT );  //设置SDA为输入  I2C_Flash();  sub_SCL_Set( _HIGH );  //接收第九位,以判断是否发送成功  I2C_Flash();  if(sub_SDA_Read())res=1;  //SDA=1发送失败,返回1  else res=0;         //SDA=0发送成功,返回0  sub_SCL_Set( _LOW );  sub_SDA_DirectSet( _OUTPUT );  //设置SDA为输出  return res; } //SCCB 读取一个字节 //在SCL的上升沿,数据锁存 //返回值:读到的数据 unsigned char SCCB_RD_Byte(void) {  unsigned char temp=0,j;  sub_SDA_DirectSet( _INPUT );  //设置SDA为输入  for(j=8;j>0;j--)  //循环8次接收数据  {   I2C_Flash();   sub_SCL_Set( _HIGH );   temp=temp<<1;   if(sub_SDA_Read())temp++;   I2C_Flash();   sub_SCL_Set( _LOW );  }  sub_SDA_DirectSet( _OUTPUT );  //设置SDA为输出  return temp; } //写寄存器 //返回值:0,成功;1,失败. unsigned char SCCB_WR_Reg(unsigned char reg,unsigned char data) {  unsigned char res=0;  SCCB_Start();      //启动SCCB传输  if(SCCB_WR_Byte(SCCB_ID))res=1; //写器件ID  I2C_Flash();  I2C_Flash();    if(SCCB_WR_Byte(reg))res=1;  //写寄存器地址    I2C_Flash();    I2C_Flash();    if(SCCB_WR_Byte(data))res=1;  //写数据    SCCB_Stop();    return res; } //读寄存器 //返回值:读到的寄存器值 unsigned char SCCB_RD_Reg(unsigned char reg) {  unsigned char val=0;  SCCB_Start();     //启动SCCB传输  SCCB_WR_Byte(SCCB_ID);  //写器件ID  I2C_Flash();  I2C_Flash();    SCCB_WR_Byte(reg);   //写寄存器地址    I2C_Flash();    I2C_Flash();  SCCB_Stop();  I2C_Flash();  I2C_Flash();  //设置寄存器地址后,才是读  SCCB_Start();  SCCB_WR_Byte(SCCB_ID|0X01); //发送读命令  I2C_Flash();  I2C_Flash();    val=SCCB_RD_Byte();    //读取数据    SCCB_No_Ack();    SCCB_Stop();    return val; }

#ifndef SCCB_H_ #define SCCB_H_
//IO操作 #define   DDR_EESCL           PORTB.PDR.BIT.B1 #define   DDR_EESDA           PORTB.PDR.BIT.B2
#define   IO_EESDAO           PORTB.PODR.BIT.B2 #define   IO_EESDAI           PORTB.PIDR.BIT.B2
#define   IO_EESCL            PORTB.PODR.BIT.B1
#define _OUTPUT 1 #define _INPUT 0
#define _HIGH 1 #define _LOW 0
#define SCCB_ID      0X42     //OV7670µÄID
/////////////////////////////////////////// void SCCB_Init(void); void SCCB_Start(void); void SCCB_Stop(void); void SCCB_No_Ack(void); unsigned char SCCB_WR_Byte(unsigned char dat); unsigned char SCCB_RD_Byte(void); unsigned char SCCB_WR_Reg(unsigned char reg,unsigned char data); unsigned char SCCB_RD_Reg(unsigned char reg); void I2C_Flash( void );
#endif /* SCCB_H_ */


备注:本文图片截取于OV颁布的SCCB协议手册

你可能感兴趣的:(单片机学习笔记)