要用到IIC程序网上找了许多参考大多不完整,或不完善,不尽如意。参考他人程序写了一份模拟程序,通用性强,略做修改引脚即可移植到51单片机平台,测试可使用多种芯片,功能完善可单个程序读写一个数据,也可连续读写一组数据,速度快,具有速度调节。可靠具有超时检测,故障重启功能。目前使用它驱动4线OLED显示和读写时钟,达到设计目标,使用过程可以任意通断一条线,线路恢复,显示和时钟读写恢复正常。有需要高可靠性读写EEPRAM的可以,进一步采用写后复读比对。
单片机代码
//=====================MRI2C.h===========================
#ifndef __MRI2C_H
#define __MRI2C_H
#include "stm32f10x.h"
#include "wei_bit.h" //位带操作
#define W_SCL PBout(8) // 定义输出SCL引脚
#define W_SDA PBout(9) //定义输出SDA引脚
#define R_SDA PBin(9) //定义SDA输入用于读取状态
// 如果要启用高速 I2C,则定义 IIC_HS 注释掉下面一行降速
#define IIC_HS
// 条件编译的代码
#ifdef IIC_HS //电平改变后无延时 最高速度 约300Khz
#define W_SCL_H W_SCL=1
#define W_SCL_L W_SCL=0
#define W_SDA_H W_SDA=1
#define W_SDA_L W_SDA=0
#else //电平改变后延时达到减低速度目的 约100Khz
#define W_SCL_H W_SCL=1 ; IIc_Delay(10) //SCL拉高 并延时
#define W_SCL_L W_SCL=0 ; IIc_Delay(10) //SCL拉低 并延时
#define W_SDA_H W_SDA=1 ; IIc_Delay(5) //SDA拉高 加延时
#define W_SDA_L W_SDA=0 ; IIc_Delay(5) //SDA拉低 并延时
#endif
#define PCF8575_ADDRESS 0x40 //IIC的I2C从机地址
#define PCF8574_ADDRESS 0x4E //IIC的I2C从机地址
#define PCF2129_ADDRESS 0xA2 //pcf2129
#define PCF8563_ADDRESS 0xA2 //pcf8563
#define R8025_ADDRESS 0x64 //R8025
extern uint8_t IIC_EEr ; //错误标志
extern void I2C1_Init(void); // IIC初始化
extern void I2C1_Reset(void); //I2C故障重启
extern void I2C1_Read (uint8_t ADDRESS , uint8_t addr,uint8_t *pData,uint8_t len);
extern void I2C1_Write(uint8_t ADDRESS , uint8_t addr,uint8_t *pData,uint8_t len);
#endif
//====================MRI2C.c========================
//***************模拟IIC程序************************************
/*无延时,最高速度,时钟脉宽1.25us,间歇2.5us */
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "MRI2C.h"
#include "stm32f10x_i2c.h"
uint8_t IIC_EEr = 0; //错误标志。
uint16_t EER_coner = 0; //错误计数器
//*********************************************************
void IIc_Delay(uint16_t ys)
{ uint16_t ystm;
ystm=ys;
while(ystm--){}
}
//产生IIC起始信号
void MyI2C_Start(void)
{
W_SDA_H;
W_SCL_H;
W_SDA_L;
W_SCL_L;
}
//产生IIC停止信号
void MyI2C_Stop(void)
{
W_SDA_L;
W_SCL_H;
W_SDA_H;
}
/**
* 函 数:I2C发送一个字节
* 参 数:Byte 要发送的一个字节数据,范围:0x00~0xFF
* 返 回 值:无
*/
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++)
{
W_SDA=(Byte&0x80)>>7;
Byte<<=1;
W_SCL_H;
W_SCL_L;
}
}
/**
* 函 数:I2C接收一个字节
* 参 数:无
* 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
*/
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
W_SDA_H;
for (i = 0; i < 8; i ++)
{
W_SCL_H;
Byte<<=1;
if(R_SDA)
Byte|= 0x01;
W_SCL_L;
}
return Byte;
}
/**
* 函 数:I2C发送应答位
* 参 数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
* 返 回 值:无
*/
void MyI2C_SendAck(uint8_t AckBit)
{
W_SDA=AckBit;
W_SCL_H;
W_SCL_L;
}
//============================================================
//等待应答信号到来
//返回值:0,接收应答失败 报错IIC_EEr
// 1,接收应答成功
u8 IIC_Wait_Ack(void)
{
u16 ucErrTime=0;
W_SDA_H;
W_SCL_H;
while(-R_SDA)
{
ucErrTime++;
if(ucErrTime>10000)
{
IIC_EEr = 1; //错误标志 重启IIC
EER_coner++; //错误计数器用于调试
MyI2C_Stop();
return 0;
}
}
W_SCL_L;
return 1;
}
/**
* 函 数:I2C连续写一组数据
* 参 数:ADDRESS 器件码, addr 起始寄存地址,*pData 要写的数组,len 写入字数
* 返 回 值:无
*/
void I2C1_Write(uint8_t ADDRESS , uint8_t addr,uint8_t *pData,uint8_t len)
{
uint8_t i;
MyI2C_Start(); //I2C起始
MyI2C_SendByte(ADDRESS); //发送从机地址,读写位为0,表示即将写入
if(IIC_Wait_Ack()) //接受应答出错跳过
MyI2C_SendByte(addr); //发送寄存器地址
if(IIC_Wait_Ack()) //接受应答出错跳过
for(i=len;i>0;i--){
MyI2C_SendByte(*pData);pData++;//发送要写入寄存器的数据
IIC_Wait_Ack(); //接受应答
}
MyI2C_Stop(); //I2C终止
}
/**
* 函 数:I2C连续读一组数据
* 参 数:ADDRESS 器件码, addr 起始寄存地址,*pData 保存数组位置,len 读出字数
* 返 回 值:无
*/
void I2C1_Read(uint8_t ADDRESS, uint8_t addr,uint8_t *pData,uint8_t len)
{
// uint8_t i;
MyI2C_Start(); //I2C起始
MyI2C_SendByte(ADDRESS); //发送从机地址,读写位为0,表示即将写入
if(IIC_Wait_Ack()) //接受应答出错退出
MyI2C_SendByte(addr); //发送寄存器地址
if(IIC_Wait_Ack()) //接受应答出错退出
{ MyI2C_Start(); //I2C重复起始
MyI2C_SendByte(ADDRESS | 0x01); //发送从机地址,读写位为1,表示即将读取
}
if(IIC_Wait_Ack()){ //接受应答出错退出
while (len)
{
*pData = MyI2C_ReceiveByte(); //接收指定寄存器的数据
if(len == 1)
MyI2C_SendAck(1); //发送非应答,终止从机的数据输出
else
MyI2C_SendAck(0); //发送应答, 继续从机的数据输出
pData++;
len--;
}
}
MyI2C_Stop(); //I2C终止
}
// IIC初始化
void I2C1_Init(void) {
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB8和PB9引脚初始化为开漏输出
/*设置默认电平*/
GPIO_SetBits(GPIOB, GPIO_Pin_8 | GPIO_Pin_9); //设置PB8和PB9引脚初始化后默认为高电平(释放总线状态)
}
//---------------------------------------------------------------------------------------
void I2C1_Reset(void) //I2C故障重启
{ uint8_t i;
// 手动操作SCL和SDA
W_SDA_L;
for (i = 0; i < 9; i++)
{
W_SCL_H;
Delay_us(1); // 延时
W_SCL_L;
Delay_us(1); // 延时
}
W_SDA_L; // l拉低SDA
Delay_us(1); // 延时
W_SCL_H;
Delay_us(1); // 延时
W_SDA_H; // 释放SDA 产生停止信号 必须 用于消除重启后I2C->SR .BUSY 位
Delay_us(1);// 延时
IIC_EEr = 0; //清除错误标志
}
/*************************例子:main.c *********************************
uint8_t buffer[7]={0,0,0,0,0,0,0};
int main(void)
{
uint8_t Data=0;
// IIC初始化
I2C1_Init();
//I2C故障重启 消除异常状态
I2C1_Reset();
I2C1_Write(PCF8563_ADDRESS,0,buffer, 7); // 将缓冲区数据写入PCF8563寄存器 时钟清零
//OLED初始化
OLED_Init();
while
{
I2C1_Read(R8025_ADDRESS,0,buffer, 7); //读取时钟
I2C1_Write(0x78,0x40,&Data,1); Data++; // 写一个数据 oled显示屏驱动测试
if (IIC_EEr == 0) //错误标志{
{ disp_p(dis);} //IIC4线oled显示驱动程序
else {
I2C1_Reset(); //I2C故障重启
OLED_Init();//OLED初始化
num_REST++; //错误计数
}
}
}
}
************************************************************************/
以上3个文件下载:
3个文件.7z
(3.4 KB, 下载次数: 13)
|