IIC驱动 IO模拟
- 格式:doc
- 大小:245.50 KB
- 文档页数:8
51单片机的I2C底层驱动程序(IO口模拟)/*Title:I2C for 80C51Author:yuyouliang51单片机(本人使用STC89C52单片机,12T模式)的I2C驱动程序,使用逻辑分析仪对该协议进行分析,发现波形比较美观,SCL 的频率在70KHz左右(11.0592M晶振),低于标准的100K,可以适应大多数的I2C器件。
如果感觉速度过快或过慢,可以自行修改延时。
希望可以给读者一个参考,给读者一些帮助!*//*i2c.h文件 */#ifndef __I2C_H_#define __I2C_H_sbit SCL = P2^1;sbit SDA = P2^0;void start_i2c(); //启动I2C总线:SCL高电平期间,SDA由高变低void stop_i2c(); //停止I2C总线:SCL高电平期间,SDA由低变高void send_i2c(unsigned char c); //主机发送一个字节,先发送最高位unsigned char receive_i2c(); //主机接收一个字节,先接收最高位void master_ack(bit ack); //主机非应答信号(填参数0)或应答信号(填参数1)void slave_ack(); //等待从机应答信号#endif/* i2c.c文件 */#include#include#include#define nop() _nop_()void start_i2c() //启动I2C总线:SCL高电平期间,SDA由高变低{SDA=1;SCL=1;nop();nop();nop();nop();SDA=0;SCL=0;}void stop_i2c() //停止I2C总线,SCL高电平期间,SDA由低变高{SDA=0;SCL=1;nop();nop();nop();nop();SDA=1;}void slave_ack() //等待从机应答信号,如果从机迟迟没有应答,则结束总线。
用IO模拟IIC方式读写EEPROM容易出现的问题 在嵌入式软件开发过程中,经常会用模拟IO的方式读写EEPROM 的操作。
有时会因程序编码的问题,出现读写数据不正确的情况。
根据笔者多年的开发经验与大家分享希望对大家有所帮助。
对EEPROM的操作常见的有两种方式:I2C,SPI;在应用中经常会用到模拟IO方式读写EEPROM。
在编写此类代码时必须要掌握I2C 与SPI协议。
I2C总线上的数据稳定规则,SCL为高电平时SDA上的数据保持稳定,SCL为低电平时允许SDA变化。
如果SCL处于高电平时,SDA上产生下降沿,则认为是起始位,SDA上的上升沿认为是停止位。
通信速率分为常规模式(时钟频率100kHz)和快速模式(时钟频率400kHz)。
同一总线上可以连接多个带有I2C接口的器件,每个器件都有一个唯一的地址,既可以是单接收的器件,也可以是能够接收发送的器件。
SPI,串行外围设备接口。
是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。
在操作中出现读取数据不正确,或者不能读取数据可能有以下原因:1.没有遵照通信协议:这两种通信方式都有各自独立的协议,如果在编写程序时不按通信协议,自然就不能正常通信了;2.时序不对:两处通讯方式都有严格的时序,编写程序时要严格按对应的时序;3.IO口方式方向设置不正确:在通信中数据有输入与输出的时候,特别是在I2C的方式下,有数据输入时相应的IO口要设置为输入,反之亦然,初学都容易犯这样的错误;4.看页的大小,超过一个页连续写就回滚,前面的就被覆盖。
其实里面有一个页地址计数器,如果页是8个byte,那么这个计数器就3bit,增加的地址(连续写的地址范围)就相当于溢出或者相当于按页取模。
在应用中的建议:1. SCL与SDA引脚用2.2K--4.7K电阻上拉;2. IO口与EEPROM的相应引脚不能直接相连,串一个几百欧电阻;3.为保证数据的正确性,在写入数据时最好对数据进行检验;4.采用循环地址的方式进行存储,避免只在一个固定地址上读写数据;。
谁有单片机io口模拟i2c读写AT24C256的程序?悬赏分:100 - 解决时间:2009-11-8 12:49问题补充:我自己写了一个,是修改别人的,但就是不管写什么,读出来一个字节都是255,郁闷啊!有调好的程序发给我,谢了。
我的晶振是24M的。
单片机是c8051f0202。
cuikaimin@提问者:崔凯敏000 - 三级最佳答案你这个单片机是带I2C接口的那个SMBus就是,我用这个系列的单片机写过,模拟的还没有借口直接来的好。
我的空间里面有我调好的一篇247519442你说的全部是0xff也是正常的,因为外部存储一般都是高电平为空,也就是说你的数据没有写进去我给你一个模拟的看看#include<reg51.h>#include <Intrins.h>#define uchar unsigned char#define uint unsigned intsbit pcf8563_scl=P0^5;//时钟频率sbit pcf8563_sda=P0^4;//串行数据传输脚bit busy=0;uchar sg;//时高位uchar sd;//时低位uchar fg;//分高位uchar fd;//分低位uchar mg;//秒高位uchar md;//秒低位uchar hou=0;uchar min=0;uchar sec=0;uchar subadd;//地址uchar dat;//数据uchar number;void start_pcf8563();//开始数据void send_pcf8563_byte();//发送void stop_pcf8563();//结束数据void receive_pcf8563_byte();//接收void spit_time();//分别计算时、分、秒的各位数字void spit_time()//分别计算时、分、秒的各位数字{sg=(int)hou/10;sd=(int)hou%10;fg=(int)min/10;fd=(int)min%10;mg=(int)sec/10;md=(int)sec%10;}void Send_pcf8563_byte(uchar bb) //向PCF8563发送一个字节{uchar aa;pcf8563_scl=0;for(aa=0;aa<8;aa++){if((bb&0x80)==0x80){pcf8563_sda=1;}else{pcf8563_sda=0;}pcf8563_scl=1;pcf8563_scl=0;bb=bb<<1;}_nop_();_nop_();pcf8563_sda=1;pcf8563_scl=1;busy=0;if(pcf8563_sda){busy=1;}else{_nop_();_nop_();pcf8563_scl=0;busy=0;}}void write_pcf8563(uchar subadd,uchar dat)// 向PCF8563对应地址写数据{start_pcf8563();Send_pcf8563_byte(0xa2);if(!busy){Send_pcf8563_byte(subadd);if(!busy){Send_pcf8563_byte(dat);}}stop_pcf8563();}void read_pcf8563() //读当时的时,分,钞{start_pcf8563();Send_pcf8563_byte(0xa2);if(!busy){Send_pcf8563_byte(0x02);if(!busy){start_pcf8563();Send_pcf8563_byte(0xa3);receive_pcf8563_byte();sec=number&0x7f;start_pcf8563();Send_pcf8563_byte(0xa3);receive_pcf8563_byte();min=number&0x7f;start_pcf8563();Send_pcf8563_byte(0xa3);receive_pcf8563_byte();hou=number&0x3f;}}stop_pcf8563();}void receive_pcf8563_byte() //从PCF8563接受一个字节{uchar cc;pcf8563_sda=1;number=0;for(cc=0;cc<8;cc++){number<<=1;pcf8563_scl=0;pcf8563_scl=1;_nop_();_nop_();number= number|pcf8563_sda;}pcf8563_scl=0;_nop_();_nop_();}void start_pcf8563() //开启PCF8563IIC{pcf8563_sda=1;pcf8563_scl=1;pcf8563_sda=0;//SCL为高,SDA执行一个下跳pcf8563_scl=0;//SCL为低,嵌住数据线}void stop_pcf8563() //关闭PCF8563IIC{pcf8563_sda=0;pcf8563_scl=1;pcf8563_sda=1;//SCL为高,SDA执行一个上跳pcf8563_scl=0;//SCL为低,嵌住数据线}void main(void){write_pcf8563(0x02,sec); //写钞write_pcf8563(0x03,min); //写分write_pcf8563(0x04,hou); //写时while(1){read_pcf8563();//读当前时间spit_time(); //切换时间,为显示做准备} }。
防止gpio模拟iic驱动被其他线程打断的方法防止GPIO模拟I2C驱动在多线程环境下被打断是一个重要的问题。
在这篇文章中,我将介绍一些方法来解决这个问题,以确保GPIO模拟I2C驱动的稳定性和可靠性。
首先,让我们了解一下什么是GPIO和I2C。
GPIO是通用输入输出端口,它可以用来控制外部设备或读取外部设备的状态。
而I2C是一种通信协议,它可以用于连接和控制多个设备,通过两根线进行数据传输。
在使用GPIO模拟I2C驱动时,我们需要保证驱动程序能够正确地处理并发访问和时序要求。
为了实现这一点,我们可以采取以下几个步骤:1. 锁定访问:在GPIO模拟I2C驱动中,我们可以使用信号量或互斥锁来实现对共享资源的访问控制。
当一个线程访问I2C总线时,它可以获得锁,阻止其他线程同时访问。
2. 设置优先级:在多线程环境中,我们可以通过设置线程的优先级来控制它们的执行顺序。
将GPIO模拟I2C驱动的线程设置为较高的优先级,可以确保它能够及时地响应,并且不会被低优先级的线程打断。
3. 中断处理:在一些特殊情况下,我们可能需要使用中断来处理GPIO模拟I2C驱动的数据请求。
通过配置中断处理程序,我们可以在需要时及时响应外部设备的请求,而不会被其他线程的操作打断。
4. 错误处理:在GPIO模拟I2C驱动中,我们应该实现错误处理机制,以便在发生错误时进行适当的处理。
例如,在发送或接收数据时发生错误,我们可以采取一些补救措施,如重新发送或重新接收数据,以确保数据的可靠性。
综上所述,要防止GPIO模拟I2C驱动在多线程环境下被打断,我们可以采取一系列的措施来确保驱动程序的稳定性和可靠性。
通过锁定访问、设置优先级、中断处理和错误处理等方法,我们可以保证GPIO模拟I2C驱动能够正确地处理并发访问和时序要求,从而实现可靠的数据传输和外设控制。
希望本文能够对需要实现GPIO模拟I2C驱动的开发者有所帮助,并能够在实际应用中发挥指导作用。
iic io模拟延时计算(最新版)目录1.IIC 总线的概述2.IIC io 模拟延时的原理3.计算 IIC io 模拟延时的方法4.IIC io 模拟延时的应用实例5.总结正文一、IIC 总线的概述IIC(Inter-Integrated Circuit),又称为 I2C(Inter IC),是一种串行双向通信总线,它是由 Philips 公司(现在的 NXP 半导体公司)于 1980 年代研发的。
IIC 总线主要用于低速度、短距离的双向通信,特别适合于连接微处理器和外围设备,如存储器、传感器、LCD 驱动器等。
二、IIC io 模拟延时的原理在 IIC 总线通信过程中,由于信号传输和处理的时延,会产生一定的延时。
IIC io 模拟延时是指通过硬件或软件模拟的方式,在 IIC 总线上增加一定的延时,以满足通信需求。
模拟延时的原理是通过插入一定数量的时钟周期,使得数据传输过程中的时序关系得以满足。
三、计算 IIC io 模拟延时的方法计算 IIC io 模拟延时的方法通常分为两步:1.计算总线时钟周期总线时钟周期是指 IIC 总线上一个时钟周期的时间长度。
根据 IIC 总线的时钟频率和总线宽度,可以计算出总线时钟周期。
例如,如果 IIC总线的时钟频率为 1MHz,总线宽度为 3 位,那么总线时钟周期为1/1000000 秒。
2.计算延时时间延时时间是指 IIC io 模拟延时所需的时间。
根据通信协议和总线时钟周期,可以计算出延时时间。
例如,如果 IIC 协议要求数据传输时延为 3 个时钟周期,那么延时时间为 3*1/1000000 秒。
四、IIC io 模拟延时的应用实例在实际应用中,IIC io 模拟延时可以用于测试和调试 IIC 总线通信系统。
通过模拟延时,可以观察通信系统在不同延时时间的工作状态,从而找出最佳的延时参数。
此外,IIC io 模拟延时还可以用于解决通信冲突和保证数据传输的可靠性。
STM32F10X--模拟IIC程序听说STM32的IIC硬件做的很鸡肋,所以在这⾥通过模拟的⽅式实现IIC协议。
此程序能成功对AT24C02操作。
程序中的带参数宏 IIC_DELAY(time)的功能是延时time us,在实际中具体场合具体分析。
宏定义⽂件--IIC.h#ifndef _IIC_#define _IIC_#include "SysTick.h"#include "stm32f10x.h"#include "SystemConfig.h"/* 配置IIC的SDA、SCL两个端⼝ */#define IIC_SCL_IO GPIO_Pin_6#define IIC_SDA_IO GPIO_Pin_7/* SDA端⼝动态改变输⼊输出状态定义 */#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X80000000;} //配置上拉输⼊#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=0X30000000;} //配置推挽输出/* IIC 操作定义 */#define IIC_SDA PBOUT(7) //定义的STM32位操作#define READ_SDA PBIN(7)#define IIC_SCL PBOUT(6)#define IIC_DELAY(time) delay(time)/* 定义的函数原型 */void IIC_IOInit(void);void IIC_Start(void);void IIC_Stop(void);unsigned char IIC_Wait_Ack(void);void IIC_NoAck(void);void IIC_Ack(void);void IIC_Send_Byte(unsigned char txd);unsigned char IIC_Read_Byte(unsigned char ack);#endif程序源⽂件IIC.c⽂件#include "IIC.h"/*********************************************************************** STM32模拟IIC通信** ⽂件编码: GBK2312**********************************************************************//******************************* 初始化IIC的IO⼝********************************/void IIC_IOInit(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE ); //使能IO⼝时钟GPIO_InitStructure.GPIO_Pin = IIC_SCL_IO | IIC_SDA_IO;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_SetBits(GPIOB,IIC_SCL_IO | IIC_SDA_IO); //置位SDA,SCL}/******************************* 起始信号********************************/void IIC_Start(void){SDA_OUT(); //sda 线输出IIC_SDA=1;IIC_SCL=1;IIC_DELAY(4);IIC_SDA=0;//当时钟线为⾼时,SDA拉低则起始信号才有效IIC_DELAY(4);IIC_SCL=0;//钳住 I2C 总线,准备发送或接收数据}/******************************* 停⽌信号********************************/void IIC_Stop(void){SDA_OUT();//sda 线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highIIC_DELAY(4);IIC_SCL=1;IIC_DELAY(1);IIC_SDA=1;//发送 I2C 总线结束信号IIC_DELAY(4);}/******************************* 等待应答信号** 返回值: 1-接收应答失败, 0-为接收成功******************************/unsigned char IIC_Wait_Ack(void){unsigned char ucErrTime=0;SDA_IN(); //SDA 设置为输⼊IIC_SDA=1;IIC_DELAY(1);IIC_SCL=1;IIC_DELAY(1);while(READ_SDA) //判断SDA上是否有低电平发⽣{ucErrTime++;if(ucErrTime>250) //如果在⼀段时间内没有接收到应答信号,则发送停⽌信号 {IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出 0return 0;}/******************************* 产⽣应答信号********************************/void IIC_Ack(void){IIC_SCL=0;SDA_OUT();IIC_SDA=0;IIC_DELAY(2); //数据改变有效IIC_SCL=1;IIC_DELAY(2);IIC_SCL=0;}/******************************* 不产⽣应答信号********************************/void IIC_NoAck(void){IIC_SCL=0;SDA_OUT();IIC_SDA=1;IIC_DELAY(2);IIC_SCL=1;IIC_DELAY(2);IIC_SCL=0;}/******************************* 发送⼀个字节的数据********************************/void IIC_Send_Byte(unsigned char txd){unsigned char t;SDA_OUT();IIC_SCL=0;//拉低时钟开始数据传输,时SDA上的数据改变有效 for(t=0;t<8;t++){IIC_SDA=(txd&0x80)>>7;txd<<=1;IIC_DELAY(2); //对 TEA5767 这三个延时都是必须的IIC_SCL=1;IIC_DELAY(2); //保持数据⼀段时间IIC_SCL=0;IIC_DELAY(2);}}/******************************* 读⼀个字节的数据** 形式参数:ack=1,发送ACK, ack=0,发送NACK******************************/unsigned char IIC_Read_Byte(unsigned char ack){unsigned char i,receive=0;SDA_IN();//SDA 设置为输⼊for(i=0;i<8;i++ ){IIC_SCL=0;IIC_DELAY(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++;IIC_DELAY(1);}if (!ack)IIC_NoAck();//发送 nACKelseIIC_Ack(); //发送 ACKreturn receive;}。
I2C硬件与模拟的区别
硬件I2C对应芯⽚上的I2C外设,有相应I2C驱动电路,其所使⽤的I2C管脚也是专⽤的,因⽽效率要远⾼于软件模拟的I2C;⼀般也较为稳定,但是程序较为繁琐。
硬件(固件)I2C是直接调⽤内部寄存器进⾏配置;⽽软件I2C是没有寄存器这个概念的。
软件I2C⼀般是使⽤GPIO管脚,⽤软件控制SCL,SDA线输出⾼低电平,模拟i2c协议的时序。
主要对⽐:
1.硬件IIC⽤法⽐较复杂,模拟IIC的流程更清楚⼀些。
2.硬件IIC速度⽐模拟快,并且可以⽤DMA
3.模拟IIC可以在任何管脚上,⽽硬件只能在固定管脚上。
有些单⽚机的硬件i2c不太稳定,调试问题较多。
例如⽹上很多⼈吐槽的STM32…。
九齐MCUio⼝模拟I2C IO⼝模拟I2C的要点:1. 数据帧符合标准的I2C协议2. 分的清楚ACK与NACK的区别,是那个设备下拉。
3. 数据位时间符合要求项⽬合作开发,吹⽜扯蛋、交朋友,请联系:186********#include "iic_p.h"#include <ny8.h>#include "main.h"#include <ny8_constant.h>#include <stdint.h>#define sda_out() IOSTA=(IOSTA & ((uint8_t)(~ C_PA6_Input))) //输出#define sda_in() IOSTA=(IOSTA | C_PA6_Input) //输⼊#define sda PORTAbits.PA6 //sda输出⾼#define scl PORTAbits.PA4 //scl输出⾼,写程序注意默认让其保持⾼电平void delayus(uint8_t tim){while(tim--) //2.4us周期 clk_8M{NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();}}void i2c_stop(void){scl = 0;delayus(1);sda = 0;sda_out();delayus(2);scl = 1;delayus(1);sda = 1;}void i2c_start(void){sda = 1; //delayus(1);scl = 1; //发送停⽌位后将SCL及SDA置位delayus(1);sda = 0;delayus(1);}//测试okvoid trandata(uint8_t data){//IOSTB=(IOSTB & ((uint8_t)(~ C_PB2_Input))); //输出uint8_t i;//scl = 0;for( i = 0;i<8;i++ ){scl = 0;delayus(1);if((data & 0x80) == 0x80){sda = 1;}else{sda = 0;data <<=1;delayus(2);scl = 1;delayus(3);}//scl = 0;//delayus(3);//sda = 0; //因该把IO输出禁⽌,不然slaver⽆法下拉}//测试okvoid revdata(uint8_t *data){uint8_t i;//sda_in();for(i=0;i<8;i++){scl = 0;delayus(3);scl = 1;delayus(1);*data <<= 1;if(sda==1){*data |= 0x01; //⾸位置为⼀}else{*data &= 0xfe;}delayus(2);}}//测试okvoid i2c_nack(void){scl = 0;delayus(1);sda = 1;sda_out();delayus(2);scl = 1;delayus(3);}//测试okuint8_t i2c_dectack(void){uint8_t ackflg;scl = 0;delayus(1);sda_in();delayus(2);scl = 1;delayus(1);if(sda ==1){ackflg = 0; //为接收到应答}else{ackflg = 1; //接收到应答}delayus(2);return ackflg;}//测试ok 3msuint8_t i2c_wtdata(uint8_t address,uint8_t data){uint8_t temp,errflg=0;i2c_start();temp = 0xa0; //地址位赋值1010add(R/W)trandata(temp);if(i2c_dectack() == 0){errflg = 1; //未接收到应答数据scl = 0;delayus(1);sda = 1;sda_out();temp = address;trandata(temp);if(i2c_dectack() == 0){errflg = 1; //未接收到应答数据}scl = 0;delayus(1);sda = 1;sda_out();trandata(data);if(i2c_dectack() == 0){errflg = 1; //未接收到应答数据}// scl = 0;// delayus(3);// sda = 0;// sda_out();i2c_stop();return errflg;}//测试ok //测试不稳定uint8_t curddata(uint8_t *data){uint8_t temp,errflg=0;i2c_start();temp = 0xa1; //地址位赋值1010add(R/W)trandata(temp);if(i2c_dectack() == 0){errflg = 1; //未接收到应答数据}//sda_out();revdata(data);i2c_nack();i2c_stop();return errflg;}//测试ok 4msuint8_t i2c_rddata(uint8_t address,uint8_t *pdata) {uint8_t temp,errflg=0;i2c_start();temp = 0xa0; //地址位赋值1010000(R/W)trandata(temp);if(i2c_dectack() == 0){errflg = 1; //未接收到应答数据}scl = 0;delayus(1);sda = 1; //默认为上拉sda_out();temp = address;trandata(temp);if(i2c_dectack() == 0){errflg = 1; //未接收到应答数据}scl = 0;delayus(1);sda = 1;sda_out();if(curddata(pdata) == 1){errflg = 1;return errflg;}/*//写数据遵守写⼊读出void i2cbus_wt(uint8_t address,uint8_t data){uint8_t temp;do{while(i2c_wtdata(address,data) == 1); //写⼊数据 while(curddata(&temp) == 1); //读出数据} while(data != temp); //⽐较通过视为写⼊成功}*/。
stm8s使⽤IO⼝模拟I2C刚⼊职不久,下⾯是使⽤stm8s005k6写的eeprom驱动程序。
EEPROM型号为ST公司的M24C256.#include "bsp_i2c.h"/* 在stm8平台下移植,只需要改下⾯的宏定义即可 */#define PORT_I2C_SCL GPIOC#define PIN_I2C_SCL GPIO_PIN_1#define PORT_I2C_SDA GPIOC#define PIN_I2C_SDA GPIO_PIN_2static void i2c_Delay(void);static void i2c_PinModeOutput(void);static void i2c_PinModeInput(void);static void i2c_SCL(uint8_t stat);static void i2c_SDA(uint8_t stat);static uint8_t i2c_ReadSDA(void);/** 函数名: i2c_InitGpio* 功能说明: 初始化IIC接⼝* 形参: ⽆* 返回值: ⽆*/void i2c_InitGpio(void){GPIO_Init(PORT_I2C_SCL, PIN_I2C_SCL, GPIO_MODE_OUT_PP_LOW_FAST);GPIO_Init(PORT_I2C_SDA, PIN_I2C_SDA, GPIO_MODE_OUT_PP_LOW_FAST);i2c_Stop();}/** 函数名: i2c_Delay* 功能说明: 延时函数* 形参: ⽆* 返回值: ⽆*/static void i2c_Delay(void){uint8_t time = 10;while (time--);}/** 函数名: i2c_PinModeOutput* 功能说明: 将SDA线的端⼝设置为输出* 形参: ⽆* 返回值: ⽆*/static void i2c_PinModeOutput(void){GPIO_Init(PORT_I2C_SDA, PIN_I2C_SDA, GPIO_MODE_OUT_PP_LOW_FAST);}/** 函数名: i2c_PinModeOutput* 功能说明: 将SDA线的端⼝设置为输⼊* 形参: ⽆* 返回值: ⽆*/static void i2c_PinModeInput(void){GPIO_Init(PORT_I2C_SDA, PIN_I2C_SDA, GPIO_MODE_IN_FL_NO_IT);}/** 函数名: i2c_SCL* 功能说明: 控制SCL线电平状态* 形参: stat:0 输出低电平,1 输出⾼电平* 返回值: ⽆*/static void i2c_SCL(uint8_t stat){if (stat){GPIO_WriteHigh(PORT_I2C_SCL, PIN_I2C_SCL);}else{GPIO_WriteLow(PORT_I2C_SCL, PIN_I2C_SCL);}}/** 函数名: i2c_SDA* 功能说明: 控制SDA线电平状态* 形参: stat:0 输出低电平,1 输出⾼电平* 返回值: ⽆*/static void i2c_SDA(uint8_t stat){if (stat){GPIO_WriteHigh(PORT_I2C_SDA, PIN_I2C_SDA);}else{GPIO_WriteLow(PORT_I2C_SDA, PIN_I2C_SDA);}}/** 函数名: i2c_ReadSDA* 功能说明: 读取SDA线电平状态* 形参: ⽆* 返回值: 0 或 1*/static uint8_t i2c_ReadSDA(void){if (GPIO_ReadInputPin(PORT_I2C_SDA, PIN_I2C_SDA)) {return1;}else{return0;}}/** 函数名: i2c_Start* 功能说明: IIC总线起始信号* 形参: ⽆* 返回值: ⽆*/void i2c_Start(void){i2c_PinModeOutput();i2c_SDA(1);i2c_Delay();i2c_SCL(1);i2c_Delay();i2c_SDA(0);i2c_Delay();i2c_SCL(0);i2c_Delay();}/** 函数名: i2c_Stop* 功能说明: IIC总线停⽌信号* 形参: ⽆* 返回值: ⽆*/void i2c_Stop(void){i2c_PinModeOutput();i2c_SCL(0);i2c_SDA(0);i2c_Delay();i2c_SCL(1);i2c_Delay();i2c_SDA(1);i2c_Delay();}/** 函数名: i2c_WriteByte* 功能说明: IIC总线写数据* 形参: _ucByte:写⼊的⼀个字节数据* 返回值: ⽆*/void i2c_WriteByte(uint8_t _ucByte){uint8_t i;i2c_PinModeOutput();i2c_SCL(0);i2c_Delay();for (i = 0; i < 8; i++){if (_ucByte & 0x80){i2c_SDA(1);}else{i2c_SDA(0);}_ucByte = _ucByte << 1;i2c_SCL(1);i2c_Delay();i2c_SCL(0);i2c_Delay();}i2c_SDA(1);}/** 函数名: i2c_ReadByte* 功能说明: IIC总线读数据* 形参: ⽆* 返回值: recv:读取的⼀个字节数据*/uint8_t i2c_ReadByte(void){uint8_t i;uint8_t recv = 0;i2c_PinModeOutput();i2c_SDA(1);i2c_Delay();i2c_PinModeInput();for (i = 0; i < 8; i++){recv = recv << 1;i2c_SCL(1);i2c_Delay();if (i2c_ReadSDA()){recv |= 0x01;}else{recv |= 0x00;}i2c_SCL(0);i2c_Delay();}return recv;}/** 函数名: i2c_Ack* 功能说明: IIC总线主机主动应答* 形参: ⽆* 返回值: ⽆*/void i2c_Ack(void){i2c_PinModeOutput();i2c_SCL(0);i2c_SDA(0);i2c_Delay();i2c_SCL(1);i2c_Delay();i2c_SCL(0);}/** 函数名: i2c_NAck* 功能说明: IIC总线主机主动⾮应答* 形参: ⽆* 返回值: ⽆*/void i2c_NAck(void){i2c_PinModeOutput();i2c_SCL(0);i2c_SDA(1);i2c_Delay();i2c_SCL(1);i2c_Delay();i2c_SCL(0);}/** 函数名: i2c_CheckAck* 功能说明: IIC总线检测应答信号* 形参: ⽆* 返回值: 0 应答信号,1 ⾮应答信号*/uint8_t i2c_CheckAck(void){uint8_t time = 0;i2c_PinModeOutput();i2c_SDA(1);i2c_Delay();i2c_SCL(1);i2c_Delay();i2c_PinModeInput();while (i2c_ReadSDA()){time++;if (time >= 100){return1;}}i2c_SCL(0);return0;}bsp_i2c.c#ifndef __BSP_I2C_H__#define __BSP_I2C_H__#include "stm8s.h"#define I2C_WR ((uint8_t) 0) /* 写控制bit */ #define I2C_RD ((uint8_t) 1) /* 读控制bit */ #define ACK ((uint8_t) 0)#define NOACK ((uint8_t) 1)void i2c_InitGpio(void);void i2c_Start(void);void i2c_Stop(void);void i2c_WriteByte(uint8_t _ucByte);uint8_t i2c_ReadByte(void);void i2c_Ack(void);void i2c_NAck(void);uint8_t i2c_CheckAck(void);#endif /* __BSP_I2C_H__ */bsp_i2c.h#include "m24256_driver.h"#include "bsp_i2c.h"#include "modbus_driver.h"//uint8_t g_ucIsEEPROMBusy;/** 函数名: M24256_Init* 功能说明: 初始化* 形参: ⽆* 返回值: ⽆*/void M24256_Init(void){i2c_InitGpio();}/** 函数名: M24256_WriteByte* 功能说明: 写⼀个字节* 形参: _usAddress:地址* _ucByte:数据* 返回值: ⽆*/uint8_t M24256_WriteByte(uint16_t _usAddress, uint8_t _ucByte) {uint16_t i;for (i = 0; i < 3000; i++){i2c_Start();i2c_WriteByte(EE_DEV_ADDR | I2C_WR); /* 选中器件 + 写 */ if (i2c_CheckAck() == ACK){break;}}if (i >= 3000){i2c_Stop();return0;}/* 发送地址 */i2c_WriteByte((_usAddress & 0xFF00) >> 8);if (i2c_CheckAck() == NOACK){i2c_Stop();return2;}i2c_WriteByte(_usAddress & 0x00FF);if (i2c_CheckAck() == NOACK){i2c_Stop();return3;}/* 读数据 */i2c_WriteByte(_ucByte);if (i2c_CheckAck() == NOACK){i2c_Stop();return4;}i2c_Stop();/** 函数名: M24256_ReadByte* 功能说明: 读⼀个字节* 形参: _usAddress:地址* 返回值: recv:读取到的⼀个字节数据*/uint8_t M24256_ReadByte(uint16_t _usAddress){uint8_t recv = 0;uint16_t i;for (i = 0; i < 3000; i++){i2c_Start();i2c_WriteByte(EE_DEV_ADDR | I2C_WR); /* 选中器件 + 写 */if (i2c_CheckAck() == ACK){break;}}if (i >= 3000){i2c_Stop();return0;}/* 发送地址 */i2c_WriteByte((_usAddress & 0xFF00) >> 8);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}i2c_WriteByte(_usAddress & 0x00FF);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}i2c_Start();i2c_WriteByte(EE_DEV_ADDR | I2C_RD); /* 选中器件 + 读 */if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}recv = i2c_ReadByte();i2c_NAck();i2c_Stop();return recv;}/** 函数名: M24256_ReadBlock* 功能说明: 从串⾏EEPROM指定地址处开始读取若⼲数据* 形参: _usAddress : 起始地址* _usSize : 数据长度,单位为字节* _pReadBuf : 存放读到的数据的缓冲区指针* 返回值: 0 表⽰失败,1表⽰成功*/unsigned char M24256_ReadBlock(unsigned char *_pReadBuf, unsigned short _usSize, unsigned short _usAddress) {uint16_t i;for (i = 0; i < 3000; i++){i2c_Start();i2c_WriteByte(EE_DEV_ADDR | I2C_WR); /* 选中器件 + 写 */if (i2c_CheckAck() == ACK){break;}}if (i >= 3000){i2c_Stop();/* 发送地址 */i2c_WriteByte((_usAddress & 0xFF00) >> 8);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}i2c_WriteByte(_usAddress &0x00FF);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}i2c_Start();i2c_WriteByte(EE_DEV_ADDR | I2C_RD); /* 选中器件 + 读 */if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}/* 循环读取数据 */for (i = 0; i < _usSize; i++){_pReadBuf[i] = i2c_ReadByte();if (i != _usSize - 1){i2c_Ack();}else{i2c_NAck(); /* 读完最后⼀个字节,主机发送⾮应答 */}}i2c_Stop();return1;}/** 函数名: M24256_WriteBlock* 功能说明: 向串⾏EEPROM指定地址写⼊若⼲数据,采⽤页写操作提⾼写⼊效率* 形参: _usAddress : 起始地址* _usSize : 数据长度,单位为字节* _pWriteBuf : 存放读到的数据的缓冲区指针* 返回值: 0 表⽰失败,1表⽰成功*/unsigned char M24256_WriteBlock(unsigned char *_pWriteBuf, unsigned short _usSize, unsigned short _usAddress) {uint16_t i, m;uint16_t usAddr;usAddr = _usAddress;for (i = 0; i < _usSize; i++){if ((i == 0) || (usAddr & (EE_PAGE_SIZE - 1)) == 0){i2c_Stop();/* 重复发送起始信号,eeprom要重新开始写要等待10ms左右 */for (m = 0; m < 3000; m++){i2c_Start();i2c_WriteByte(EE_DEV_ADDR | I2C_WR);if (i2c_CheckAck() == ACK){break;}}if (m >= 3000){i2c_Stop(); /* 写eeprom超时 */return0;}/* 发送地址 */i2c_WriteByte((usAddr & 0xFF00) >> 8);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}i2c_WriteByte(usAddr & 0x00FF);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}}/* 写⼊数据 */i2c_WriteByte(_pWriteBuf[i]);if (i2c_CheckAck() == NOACK){i2c_Stop();return0;}usAddr++;}i2c_Stop();return1;}bsp_eeprom.c#ifndef __M24256_DRIVER_H__#define __M24256_DRIVER_H__#include "stm8s.h"#define EE_MODEL_NAME "M24256"#define EE_DEV_ADDR 0xA0 /* 设备地址 */#define EE_PAGE_SIZE 64 /* 页⾯⼤⼩(字节) */#define EE_SIZE (32*1024) /* 总容量(字节) */#define EE_ADDR_BYTES 1 /* 地址字节个数 */extern unsigned char g_ucIsEEPROMBusy;void M24256_Init(void);uint8_t M24256_WriteByte(uint16_t _usAddress, uint8_t _ucByte);uint8_t M24256_ReadByte(uint16_t _usAddress);unsigned char M24256_ReadBlock(unsigned char *_pReadBuf, unsigned short _usSize, unsigned short _usAddress); unsigned char M24256_WriteBlock(unsigned char *_pWriteBuf, unsigned short _usSize, unsigned short _usAddress); void ee_Test(void);#endif /* __M24256_DRIVER_H__ */bsp_eeprom.h。
STM32-软件模拟I2C⼀、SCL和SDA引脚说明 I2C的两个引脚(SCL引脚和SDA引脚)需要既能输出⼜能输⼊,为了避免复杂的配置操作需要把该引脚配置为开漏输出模式,该模式的说明如下图所⽰: 当单⽚机的SDA引脚配置为低电平时,SDA线被拉低;当单⽚机的SDA引脚配置为⾼电平时,引脚端⼝为⾼阻态,SDA线通过上拉电阻被VCC 拉⾼。
因此⼀定要注意在进⾏I2C通讯时确保SDA线和SCL线外接上拉电阻。
三、代码实现 1、I2C软件延时/*** @brief 模拟I2C延时* @retval none* @author Mr.W* @date 2020-10-12*/static void analog_i2c_delay(void){volatile uint8_t i;for (i = 0; i < 10; i++);} 2、I2C引脚初始化/*** @brief 软件模拟I2C初始化* @author Mr.W* @date 2020-10-12*/void bsp_analog_i2c_init(void){GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOB_CLK_ENABLE();/*Configure GPIO pins : PB6 PB7 */GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);bsp_analog_i2c_stop();} 3、I2C开始/*** @brief I2C 开始,SCL为⾼电平的时候SDA产⽣⼀个下降沿信号 * @retval none* @author Mr.W* @date 2020-10-12*/void bsp_analog_i2c_start(void){/* _____*SDA \_____________* __________*SCL \________*/i2c_sda_high();i2c_scl_high();analog_i2c_delay();i2c_sda_low();analog_i2c_delay();i2c_scl_low();analog_i2c_delay();} 4、I2C停⽌/*** @brief I2C 停⽌,SCL为⾼电平的时候SDA产⽣⼀个上升沿信号 * @retval none* @author Mr.W* @date 2020-10-12*/void bsp_analog_i2c_stop(void){/* _______*SDA __________/* ____________*SCL _____/*/i2c_sda_low();i2c_scl_high();analog_i2c_delay();i2c_sda_high();analog_i2c_delay();} 5、I2C等待响应/*** @brief I2C 等待响应* @retval none* @author Mr.W* @date 2020-10-12*/uint8_t bsp_analog_i2c_wait_ack(void){uint32_t timeout = 0;i2c_sda_high();analog_i2c_delay();i2c_scl_high();analog_i2c_delay();while(i2c_read_sda()){if(timeout > 2000){return0;}}i2c_scl_low();analog_i2c_delay();return1;} 6、I2C响应/*** @brief I2C 响应* @retval none* @author Mr.W* @date 2020-10-12*/void bsp_analog_i2c_ack(void){/* ____*SCL ______/ \______* ____ _____*SDA \_______/*/i2c_sda_low();analog_i2c_delay();i2c_scl_high();analog_i2c_delay();i2c_scl_low();analog_i2c_delay();i2c_sda_high();} 7、I2C不响应/*** @brief I2C 不响应* @retval none* @author Mr.W* @date 2020-10-12*/void bsp_analog_i2c_nack(void){/* ____*SCL ______/ \______* __________________*SDA*/i2c_sda_high();analog_i2c_delay();i2c_scl_high();analog_i2c_delay();i2c_scl_low();analog_i2c_delay();} 8、I2C发送⼀个字节数据/*** @brief I2C 发送⼀个字节数据* @retval none* @author Mr.W* @date 2020-10-12*/void bsp_analog_i2c_send_byte(uint8_t data) {uint8_t i;for(i = 0; i < 8; i++){if(data & 0x80){i2c_sda_high();}else{i2c_sda_low();}analog_i2c_delay();i2c_scl_high();analog_i2c_delay();i2c_scl_low();if(i == 7){i2c_sda_high();}data <<= 1;analog_i2c_delay();}} 9、I2C接收⼀个字节数据/*** @brief I2C 读⼀个字节数据* @retval none* @author Mr.W* @date 2020-10-12*/uint8_t bsp_analog_i2c_read_byte(void) {uint8_t i, data = 0;for(i = 0; i < 8; i++ ){data <<= 1;i2c_scl_high();analog_i2c_delay();if(i2c_read_sda()){data++;}i2c_scl_low();analog_i2c_delay();}return data;}#endif。
像STM32开漏上拉4.7k可作为准双向io口,有些没有开漏功能的mcu(frescale kenities L:KL25)就要自主切换io方向了。
通过微处理器I/O口模拟I2C总线对AT24C进行读写之前应注意一下两个问题:一、微处理器的两个模拟I/O口在和SDA,SCL连接时必须使用上拉电阻。
一、I2C总线空闲的时候,两条信号线应该维持高电平。
否则,上拉电阻上会有耗电。
特别是在上电过程中,I/O线上电平也应保持在高电平状态。
也就是说:当Master的I2C使用的是I/O软件模拟时,一定要保证该两个I/O上电默认均为输入(或高阻)或者输出高电平,切不可默认为输出低电平。
I/O默认为输入时,可以通过外部上拉电阻将I2C信号线拉至高电平。
该程序通过调试,可以直接应用,程序如下:文件名: AT24Cxx.C* 功能描述: I/O模拟I2C时序读写AT24CXX(支持字节写、页写、字节读、顺序读)* 作者:* 创建日期:* 版本:************************************************************************************ *************/#include <msp430x16x.h>#define SDA_IN P5DIR &=~BIT0 // P5.0 IN#define SDA_OUT P5DIR |=BIT0 // P5.0 OUT#define SDA_LOW P5OUT &=~BIT0 // sda=0#define SDA_HIGH P5OUT |=BIT0 // sda=1#define SCL_IN P5DIR &=~BIT1 // P5.1 IN#define SCL_OUT P5DIR |=BIT1 // P5.1 OUT#define SCL_LOW P5OUT &=~BIT1#define SCL_HIGH P5OUT |=BIT1#define W_EEPROM_LENGH 14#define TURE 1#define FALSE 0#define AckError 0x55#define OutOfRang 0xaa#define OutOfAddr 0xbbunsigned char dat[W_EEPROM_LENGH]={1,2,3,4,5,6,7,8,9,10,11,12,13,14};unsigned char x[W_EEPROM_LENGH];void i2c_delay(unsigned char us);void i2c_delay_ms(unsigned char ms);void i2c_start();void i2c_stop(void);void i2c_SendAck(void);void i2c_SendNoAck(void);unsigned char i2c_check_ACK(void);void i2c_SendByte(unsigned char data);unsigned char i2c_RevByte(void);unsigned char EEPROM_ByteWrite(unsigned int addr,unsigned char data);unsigned char EEPROM_RandomRead(unsigned int addr);unsigned char EEPROM_SequentialRead(unsigned int addr,unsigned int n,unsigned char* p); unsigned int EEPROM_PageWrite(unsigned int page,unsigned char* p,unsigned char n);void i2c_delay(unsigned char us){unsigned char tmp;while(us--){for(tmp=0;tmp<4;tmp++){_NOP();}}}void i2c_delay_ms(unsigned char ms){unsigned int tmp;while(ms--){for(tmp=0;tmp<5000;tmp++){_NOP();}}}void i2c_start(void){SDA_OUT;i2c_delay(20);SDA_HIGH;i2c_delay(3);SCL_HIGH;i2c_delay(2);SDA_LOW;i2c_delay(5);SCL_LOW;i2c_delay(10);}void i2c_stop(void){SDA_OUT;SDA_LOW;i2c_delay(2);SCL_HIGH;i2c_delay(2);SDA_LOW;i2c_delay(2);SDA_HIGH;}void i2c_SendAck(void){SDA_OUT;SDA_LOW;i2c_delay(2);SCL_LOW;i2c_delay(2);SCL_HIGH;i2c_delay(2);SCL_LOW;SDA_HIGH;}void i2c_SendNoAck(void){SDA_OUT;SDA_HIGH;i2c_delay(2);SCL_LOW;i2c_delay(2);SCL_HIGH;i2c_delay(2);SCL_LOW;}unsigned char i2c_check_ACK(void) {unsigned char AckStatus;SDA_IN;SCL_HIGH;i2c_delay(2);if(P5IN & 0x01){AckStatus = FALSE;}else{AckStatus = TURE;}SCL_LOW;i2c_delay(2);SDA_OUT;return AckStatus;}void i2c_SendByte(unsigned char data) {unsigned char tmp;SDA_OUT;for(tmp=0;tmp<8;tmp++){if(data & 0x80){SDA_HIGH;}else{SDA_LOW;}i2c_delay(2);SCL_HIGH;i2c_delay(2);SCL_LOW;i2c_delay(2);data <<= 1;}i2c_delay(15);}unsigned char i2c_RevByte(void){unsigned char tmp;unsigned char DATA=0;SDA_IN;SCL_LOW;i2c_delay(2);for(tmp=0;tmp<8;tmp++){SCL_HIGH;i2c_delay(2);DATA <<= 1;if(P5IN & 0x01){DATA |= 0x01;}else{DATA &= 0xfe;}SCL_LOW;}SDA_OUT;return DATA;}unsigned char EEPROM_ByteWrite(unsigned int addr,unsigned char data) {unsigned char Dev_addr; //设备地址unsigned char AddrLow;unsigned char AddrHigh;AddrLow = (unsigned char)addr;AddrHigh = (unsigned char)(addr>>8);Dev_addr = 0xa0|(AddrHigh<<1);i2c_start();i2c_SendByte(Dev_addr);if(i2c_check_ACK() == FALSE){return AckError;}i2c_SendByte(AddrHigh);if(i2c_check_ACK() == FALSE){return AckError;}i2c_SendByte(AddrLow);if(i2c_check_ACK() == FALSE){return AckError;}i2c_SendByte(data);if(i2c_check_ACK() == FALSE){return AckError;}i2c_stop();i2c_delay_ms(10);return 0;}unsigned char EEPROM_RandomRead(unsigned int addr) {unsigned char Dev_addr; //设备地址unsigned char AddrLow;unsigned char AddrHigh;unsigned char tmp;AddrLow = (unsigned char)addr;AddrHigh = (unsigned char)(addr>>8);Dev_addr = 0xa0|(AddrHigh<<1);i2c_start();i2c_SendByte(Dev_addr);if(i2c_check_ACK() == FALSE){return AckError;}i2c_delay_ms(5);i2c_SendByte(AddrHigh);if(i2c_check_ACK() == FALSE){return AckError;}i2c_SendByte(AddrLow);if(i2c_check_ACK() == FALSE){return AckError;}i2c_delay_ms(5);i2c_start();Dev_addr = 0xa1|(AddrHigh<<1);i2c_SendByte(Dev_addr);if(i2c_check_ACK() == FALSE){return AckError;}i2c_delay_ms(5);tmp = i2c_RevByte();i2c_SendNoAck();i2c_stop();return tmp;}unsigned int EEPROM_PageWrite(unsigned int page,unsigned char* p,unsigned char n) {unsigned char Dev_addr;unsigned char AddrLow;unsigned char AddrHigh;unsigned int tmp;if((n > 64)|(page > 512)) //根据读写的设备而变更为适合的页数和每页字节数{return OutOfRang;}tmp = ((unsigned int)page) << 6; //得出页首地址AddrLow = (unsigned char)tmp;AddrHigh = (unsigned char)(tmp>>8);Dev_addr = 0xa0 | (AddrHigh << 1);i2c_start();i2c_SendByte(Dev_addr);if(i2c_check_ACK() == FALSE){return AckError;}i2c_delay_ms(5);i2c_SendByte(AddrHigh);if(i2c_check_ACK() == FALSE){return AckError;}i2c_SendByte(AddrLow);if(i2c_check_ACK() == FALSE){return AckError;}while(n--){i2c_SendByte(*p++);if(i2c_check_ACK() == FALSE){return AckError;}}i2c_stop();return 0;}unsigned char EEPROM_SequentialRead(unsigned int addr,unsigned int n,unsigned char* p) {unsigned char Dev_addr; //设备地址unsigned char AddrLow;unsigned char AddrHigh;if(n > (32768 - addr)) //检查预写入地址是否有效{return OutOfAddr;}AddrLow = (unsigned char)addr;AddrHigh = (unsigned char)(addr>>8);Dev_addr = 0xa0|(AddrHigh<<1);i2c_start();i2c_SendByte(Dev_addr);if(i2c_check_ACK() == FALSE){return AckError;}i2c_delay_ms(5);i2c_SendByte(AddrHigh);if(i2c_check_ACK() == FALSE){return AckError;}i2c_SendByte(AddrLow);if(i2c_check_ACK() == FALSE){return AckError;}i2c_delay_ms(5);i2c_start();Dev_addr = 0xa1|(AddrHigh<<1);i2c_SendByte(Dev_addr);if(i2c_check_ACK() == FALSE){return AckError;}while(n--){*p = i2c_RevByte();p++;if(n)i2c_SendAck();elsei2c_SendNoAck();}i2c_stop();return 0;}main(){//unsigned char tt,tt1;WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer DCOCTL = 0X73;BCSCTL1= 0X87; // 设置时钟频率4.00MHzP5DIR |=0X03;P5OUT |=0X03;//EEPROM_ByteWrite(0x0,12);//tt=EEPROM_RandomRead(0x0);//tt1=tt;EEPROM_PageWrite(2,dat,14);EEPROM_SequentialRead(128,14,x);}。
iic io模拟延时计算摘要:I.引言A.介绍IIC 通信协议B.阐述IIC 通信中的延时问题II.IIC 通信协议简介A.IIC 通信协议的发展历程B.IIC 通信协议的基本原理C.IIC 通信协议的主要特点III.IIC 通信中的延时问题A.延时的定义和计算方法B.延时对IIC 通信的影响C.导致延时的主要因素IV.IO 模拟延时计算方法A.IO 模拟延时的基本概念B.IO 模拟延时的计算公式C.IO 模拟延时的实例分析V.减少IIC 通信延时的方法A.优化硬件设计B.调整软件参数C.选择合适的通信速率VI.结论A.总结IIC 通信中的延时问题B.强调IO 模拟延时计算的重要性正文:I.引言IIC(Inter-Integrated Circuit)通信协议,又称为两线制串行通信协议,是一种由Philips 公司于1980 年代研发的串行通信协议。
在IIC 通信中,延时是一个不可忽视的问题,因为它直接影响到通信的稳定性和效率。
为了更好地理解和解决IIC 通信中的延时问题,我们需要先了解IIC 通信协议的基本原理和特点。
II.IIC 通信协议简介A.IIC 通信协议的发展历程IIC 通信协议最早由Philips 公司研发,并应用于其生产的微控制器(MCU)之间的高速通信。
随着技术的发展,IIC 通信协议逐渐得到了其他厂商的支持,并成为了一种广泛应用于各种电子设备之间的通信标准。
B.IIC 通信协议的基本原理IIC 通信协议基于串行通信方式,通过两根信号线(数据线SDA 和时钟线SCL)进行通信。
在通信过程中,发送方将数据按照一定的时序发送到接收方,接收方根据时钟信号提取数据。
C.IIC 通信协议的主要特点IIC 通信协议具有以下特点:主从模式、多主控制结构、数据传输速率可调、设备地址唯一等。
这些特点使得IIC 通信协议在电子设备间通信中具有广泛的应用前景。
III.IIC 通信中的延时问题A.延时的定义和计算方法延时是指在通信过程中,数据从发送方传输到接收方所需的时间。
QPSKSTV0903 TUNER STV6110 驱动设计1.IIC 理论MEGA128 模拟IIC时序控制QPSK 中的STV0903 STV6110STV0903 STV6110 连接图IIC时序理论先看下STV0903写时序图1)起总线先将SCL、SDA拉高,然后维持SCL为高先将SDA拉低参考代码如下:void Start(void){SBI(PORTD,SAA7113_DA TA);SBI(PORTD,SAA7113_CLK);IIC_delay();CBI(PORTD,SAA7113_DA TA);IIC_delay();CBI(PORTD,SAA7113_CLK);IIC_delay();}2)停总线先将SCL、SDA拉低,然后先拉高SCL,维持SCL为高时拉高SDA参考代码:void Stop(void){CBI(PORTD,SAA7113_DA TA);CBI(PORTD,SAA7113_CLK);IIC_delay();SBI(PORTD,SAA7113_CLK);IIC_delay();SBI(PORTD,SAA7113_DA TA);IIC_delay();}3)写总线在写总线时将SDA设置为输出,参考代码:void write_byte(unsigned char data){uchar m,tmp;SBI(SAA7113_DDR,SAA7113_DA TA);for(m = 0; m < 8; m++){if(data & 0x80)SBI(PORTD,SAA7113_DA TA);elseCBI(PORTD,SAA7113_DA TA);IIC_delay();SBI(PORTD,SAA7113_CLK);IIC_delay();data = data<<1;CBI(PORTD,SAA7113_CLK);}IIC_delay();saa7113_ack();IIC_delay();}4)应答应答由被控制芯片回应过来,在此时SDA应该设置为输入等待ACK, 在CLK第九个脉冲时,CLK为高电平,而SDA为一个低电平在表示收到器件的一个ACK应答。
参考代码:void saa7113_ack(){uchar tmp,m;CBI(DDRD,SAA7113_DA TA);SBI(PORTD,SAA7113_CLK);for(m=0;m<10;m++){IIC_delay();tmp = PIND;tmp &=0x02;if(tmp == 0)break;}CBI(PORTD,SAA7113_CLK);SBI(DDRD,SAA7113_DA TA);}5)读时序读芯片的数据时SDA应该设置为输入,参考代码:uchar read_byte(void){uchar m;uchar tmp1 = 0;uchar tmp2 = 0;CBI(SAA7113_DDR,SAA7113_DA TA);//输入for(m = 0; m < 8; m++){CBI(PORTD,SAA7113_CLK);IIC_delay();SBI(PORTD,SAA7113_CLK);IIC_delay();tmp1 = PIND; //sda 所在管脚tmp1 &= 0x02;if(0x02 == tmp1)tmp2 = ((tmp2 << 1) | 1);elsetmp2 = ((tmp2 << 1) & 0xfe);CBI(PORTD,SAA7113_CLK);}IIC_delay();saa7113_ack();return(tmp2);}STV0903 and STV6110通过STV0903来控制STV6110 当STV0903能正常读写时,先是打开I2CRPT控制寄存器用示波器测量SCL、SDA引脚观测波形,再测STV6110波形,如果STV6110波形出现脉冲宽度不等,则可能是SCLT 延迟于SCL,通过配置寄存器去掉延迟。
如果波形变化比较多出现多种波形,而且脉冲有时与SDA对准出现不标准,则可能是设置STV6110的时候速度不对。
开发所遇到的问题:1)IIC时序不对,在示波器上看哪出波形不对则修改哪处,观察到的波形就是我们模拟的波形,注意转折点SDA的输入输出。
2)STV6110波形不对,比如SCL为高时,SDA不能确定是高还是低。
则是STV6110速度设置问题。
I2C repeater速度设置不对,我们设置为010我们的repeater是16MHZ,而datasheet上是(that is, 135 MHz);3)不标准波形如下所示。
如上波形:不好认为是010 还是111 ,这种波形我认为是不标准波形。
示波器的使用说明P11-触发 2 –水平3- 用作调节视图(观察范围)(秒/格)4-5 –垂直调节一个为CH1 一个为CH2 ,6-7 分别为CH1 CH2 电压调节,8-9 为CH1 CH2 (CH1是指通道1) 10 运行/停止3-逆时针则会视野更广,看到的波形越小,顺时针则会局部到某一部分波形上。
1-触发调节触发可以使得波形停留时间更长以及更加清晰,易于捕捉波形按10停止。
使用实例:测试STV0903波形,在程序中不断向SCL、SDA发送信号,设置延时易于捕捉波形,开机时调节3使得观察到全部波形,然后将2向左调,调节6,7使得电压都为2V,调节4,5使得波形垂直方向上挨齐些,按10停止,再按3查看局部波形。
指针二级指针与指针的指针的区别(以char 为例)指针变量char * p; p 被定义为一个char * 类型的指针变量。
&p则表示指针变量所分配的内存处的地址,*p 表示所指向地址的处的值。
P为所指地址。
Ef:Char str[10];Char *p1=str;P1 和str 的值是一样的,都是指数组的首地址。
*p1 和str[0]的值相同。
Char *p2=p1;P2 与p1 的值相同也是str的地址。
Char ** p2 = &p1;意思为P2所指向地址&p1,p1中存放是一个char * 的指针;参考代码:#include <stdio.h>#include <stdlib.h>void test(char *);void second(char *);int main(void){char str[10];char tmp;printf(" str addr : %x \n " , str);for(tmp = 0;tmp <9;tmp++)str[tmp]=tmp+0x30;str[tmp]='\0';puts("main:");puts(str);test(str);puts(str);return 0;}void test(char *a){char tmp;puts("test:");puts(a);printf("a addr : %x \n " , &a);// second(a);char ** b = &a;for(tmp=0;tmp<9;tmp++)*(*b)++ = tmp + 65;return ;}void second(char *b){printf(" b addr : %x \n " , &b);puts("second:");char tmp;for(tmp=0;tmp < 9;tmp++)*(b++) = tmp + 65;return ;}/*str addr : 12ff74main:012345678test:012345678a addr : 12ff20ABCDEFGHIPress any key to continue*/数据手册参考词汇lagacy 继承reflection 反射Confidential 机密的demodulator 解调器implementation 实现Semiconductors 半导体internal 内部的device master 主设备access 访问repeater 中继器Electrical specifications 电气规格tuning 调整transactions执行oversample采样ascertain 确定integrates 整合significantly 值得注目地impedeance 阻抗respectively 分别的reserved 默认bidirectional 双向tuner 调谐器compliant适应sub addresses 子地址attention items 注意事项isolation 分离perform 执行pulse 脉冲respectively 分别的invert 反向integrates 集成configures 配置high impedance 高阻抗Consult 咨询Unallocated 未分配的conforms 符合consecutive 连续的open-drain 开漏RF IC 射频IC unless otherwise 除非calibration 校准bitfield位域determines 决定residual 剩余further 更进一步。
constant 常数dedicated专用subsequent随后amplifier放大器compensation 补偿assigned 分配foreword 序Recommendation 推荐division ratio 分频比local oscillator 本振频率divisor 除数divider 分频器either……or 要么……要么hence 因此Spectrum 频谱conforms符合。