#define DS_SCL BIT4 //DS_SCL = P5.4
#define DS_SDA BIT6 //DS_SDA = P2.6
#define DS_SCL_IN P5DIR &= ~DS_SCL
#define DS_SCL_OUT P5DIR |= DS_SCL
#define DS_SCL0 P5OUT &= ~DS_SCL
#define DS_SCL1 P5OUT |= DS_SCL
#define DS_SDA_IN P2DIR &= ~DS_SDA
#define DS_SDA_OUT P2DIR |= DS_SDA
#define DS_SDA0 P2OUT &= ~DS_SDA
#define DS_SDA1 P2OUT |= DS_SDA
#define DS_SDA_BIT P2IN & DS_SDA
void Init_CLK(void)
{
unsigned int i;
BCSCTL1 &= ~XTS;
do{
IFG1 &= ~OFIFG;
for(i=0xFF;i>0;i--);
}
while((IFG1 & OFIFG)!=0);
BCSCTL2 |= SELS+SELM1;
_EINT();
P5SEL|=0x30;
DS_SCL_OUT; //SCLK对应的IO设置为输出状态
P2DIR&=0xbf;
P2OUT|=BIT6;
DS_SCL0; //SCLK=0
delay_us(5);
}
void DS3231_Write(u8 write_address,u8 write_data)
{
u8 Device_Address = 0xD0; //DS3231写设备地址
iic_Start(); //开始
iic_SendByte(Device_Address); //发送设备地址
iic_Ack();
iic_SendByte(write_address); //发送地址
iic_Ack(); //应答
iic_SendByte(write_data); //发送数据
iic_Ack(); //应答
iic_Stop(); //停止
}
u8 DS3231_Read(u8 read_address)
{
u8 Receive_Data; //数据接收
u8 Device_Address = 0xD0; //DS3231写设备地址
iic_Start(); //开始
iic_SendByte(Device_Address); //发送设备地址
iic_Ack(); //应答
iic_SendByte(read_address); //发送地址
iic_Ack(); //应答
iic_Start(); //重复开始
Device_Address = 0xD1; //DS3231读设备地址
iic_SendByte(Device_Address); //发送地址
iic_Ack(); //应答
iic_in();
Receive_Data = iic_ReceiveByte(); //接收数据
iic_out();
iic_NAck(); //不应答
iic_Stop(); //停止
return Receive_Data; //返回数据
}
void delay_us(u8 time)
{
for(int i=0;i<time;i++)
_NOP();
}
view plaincopy to clipboardprint?
//------------iic开始-----------
void iic_Start(void)
{
DS_SDA_OUT;
DS_SCL_OUT;
DS_SCL1;
delay_us(5);
DS_SDA1;
delay_us(10);
DS_SDA0;
delay_us(5);
DS_SCL0;
delay_us(5);
DS_SDA_IN;
}
//------------iic结束-----------
void iic_Stop(void)
{ DS_SDA_OUT;
DS_SCL_OUT;
DS_SCL1;
delay_us(10);
DS_SDA0;
delay_us(5);
DS_SDA1;
delay_us(5);
DS_SDA_IN;
}
//------------iic应答-----------
void iic_Ack(void)
{
DS_SCL0;
DS_SDA0;
delay_us(5);
DS_SCL1;
delay_us(5);
DS_SCL0;
delay_us(5);
}
//------------iic不应答---------
void iic_NAck(void)
{
DS_SCL0;
DS_SDA1;
delay_us(5);
DS_SCL1;
delay_us(5);
DS_SCL0;
delay_us(5);
}
//------------iic发送-----------
void iic_SendByte(u8 SendData)
{
DS_SDA_OUT;
DS_SCL_OUT;
DS_SCL0;
u8 i;
for(i=0;i<8;i++)
{
DS_SCL0;
delay_us(5);
if(SendData&0x80) //MSB在前
DS_SDA1;
else
DS_SDA0;
DS_SCL1;
delay_us(2);
DS_SCL0;
SendData<<=1;
}
//delay_us(5);
//DS_SDA1;
//delay_us(5);
//DS_SDA_IN;
}
//------------iic接收-----------
u8 iic_ReceiveByte(void)
{
u8 i,temp=0;
delay_us(5);
DS_SDA1;
delay_us(5);
for(i=0;i<8;i++)
{
temp<<=1;
DS_SCL0;
delay_us(5);
DS_SCL1;
delay_us(2);
temp=DS_SDA_BIT;
if(temp==0x80)
temp=temp|0x01;
else
temp=temp&0xFE;
}
DS_SCL0;
delay_us(2);
return temp;
}
void iic_in()
{
DS_SCL_OUT;
DS_SDA_IN;
}
void iic_out()
{
DS_SCL_OUT;
DS_SDA_OUT;
DS_SCL1;
delay_us(5);
DS_SDA1;
delay_us(2);
}
我自己用的软IIC程序,IAR平台,芯片STM8f103,你稍作修改可以适用别的平台和芯片。
整个IIC驱动严格按照IIC标准时序做的,为了这个,连延时函数都是半周期。
你可以分析一下我程序里面延时函数是用法,来进一步了解IIC时钟、数据、ACK之间的时序关系。
被控制芯片我的程序里面是ADS1115 AD芯片,这个修改一下芯片地址就可以用到任何芯片上,控制指令每个芯片不同,必须按照你选择的芯片重新修改。
写IIC程序其实和传统软串口没什么本质区别,只是动作更多点而已,当然由于IIC通信速率比较高,因此对信号的时序要求就比较高,否则容易引起通信失败。
这个软IIC时序,是根据延时函数确定速率的,修改该函数,可以实现各种通信速率,按照我的程序里面,机器时钟是16MHz,速率大概近400KHz。
通过示波器观察过,我这个IIC程序的波形和标准IIC时序图比较非常标准。
#define ADS1115_ADD 0X90
#define SCL PB_ODR_ODR4
#define SDA PB_ODR_ODR5
#define SDA_IN PB_IDR_IDR5
#define SET_SCL_OUT() {PB_DDR_DDR4=1;PB_CR1_C14=0;PB_CR2_C24=0;}
#define SET_SDA_OUT() {PB_DDR_DDR5=1;PB_CR1_C15=0;PB_CR2_C25=0;}
#define SET_SDA_IN() {PB_DDR_DDR5=0;PB_CR1_C15=0;PB_CR2_C25=0;}
void Init_IIC(void)
{
I2C_CR1 &= 0xFE; // PE=0, disable IIC module
SET_SDA_OUT();
SDA=1;
SET_SCL_OUT();
SCL=1;
}
//--------------------------------------------------------------
//IIC主时钟定时用延时函数
//--------------------------------------------------------------
void Delay_CLK()
{
NOP();NOP();NOP();NOP();NOP();
}
//--------------------------------------------------------------
//IIC 启动信号发送
//--------------------------------------------------------------
void IIC_Start(void)
{
SDA = 0;
Delay_CLK();
SCL = 0;
}
//--------------------------------------------------------------
//IIC 停止信号发送
//--------------------------------------------------------------
void IIC_Stop(void)
{
SDA = 1;
Delay_CLK();
SCL = 1;
}
//--------------------------------------------------------------
//MASTER模式 发送ACK应答
//--------------------------------------------------------------
void IIC_SendACK(u8 ACK)
{
SDA = ACK;
SCL = 1;
Delay_CLK();
Delay_CLK();
SCL = 0;
}
//--------------------------------------------------------------
//IIC单字节发送+ACK检测
//--------------------------------------------------------------
u8 IIC_SendByte(u8 Send_DAT)
{
u8 CHK_ACK;
for (u8 i=0;i<8;i++)
{
SCL = 0;
Delay_CLK();
SDA=Send_DAT>>7;
Send_DAT <<= 1;
SCL = 1;
Delay_CLK();Delay_CLK();
}
SCL = 0;
Delay_CLK();
SET_SDA_IN();
Delay_CLK();
SCL = 1;
CHK_ACK = (u8)SDA_IN;
SET_SDA_OUT();
Delay_CLK();
SCL = 0;
Delay_CLK();
return CHK_ACK;
}
//--------------------------------------------------------------
//IIC 单字节接受
//--------------------------------------------------------------
u8 IIC_RecvByte()
{
u8 Recv_DAT = 0;
SDA = 1;
SET_SDA_IN();
for (u8 i=0; i<8; i++)
{
Recv_DAT <<= 1;
SCL = 1;
Delay_CLK();
Recv_DAT+=(u8)SDA_IN;
NOP();NOP();
SCL = 0;
Delay_CLK();
}
SET_SDA_OUT();
return Recv_DAT;
}
//--------------------------------------------------------------
//ADS1115 写控制项
//--------------------------------------------------------------
void config_ads1115(u8 regadd,u8 wdata,u8 wdata1)
{
IIC_Start();
IIC_SendByte(ADS1115_ADD);//根据需要,这里可以加上ACK检测
IIC_SendByte(regadd);
IIC_SendByte(wdata);
IIC_SendByte(wdata1);
IIC_Stop();
}
//--------------------------------------------------------------
//ADS1115 读转换结果
//--------------------------------------------------------------
int Read_ads1115(u8 REG_ADD)
{
int READ_TMP;
IIC_Start();
IIC_SendByte(ADS1115_ADD); //写地址为7位器件地址+读写位=0
IIC_SendByte(REG_ADD);
IIC_Stop();
IIC_Start();
IIC_SendByte(ADS1115_ADD|1); //读地址为7位器件地址+读写位=1
*((u8*)(&READ_TMP))= IIC_RecvByte();
IIC_SendACK(0);
*((u8*)(&READ_TMP)+1)= IIC_RecvByte();
IIC_SendACK(0);
IIC_Stop();
return READ_TMP;
}