51单片机的I2C底层驱动程序(IO口模拟)
MC51 AVR MCU 的I2C、SPI、232、485、IO的使用
MC51\AVR MCU 的I2C\SPI\232\485\IO的使用首先:AVR的IO口操作玉日信有三个寄存器:DDR xn PORTxn PINxnDD Rxn(方向选择):1为输出0为输入PORTxn:在引脚为输入的前提下,为1上啦电阻使能0不使能在引脚为输出的前提下,为1输出高电平0输出低电平PINxn:通过读取PINxn来得到该引脚的状态,他不会所存状态,他读到的只是当前管脚的电平状态。
注意:xn是 A B CD..口管脚设置PA口的第一个脚为输出:DDRA|=0x01;51单片机的IO口操作比如现让P1.7为输出,其余为输入,P2=0x0111 1111B,也就是0x7F。
=============================================================一、模拟SPI的使用时钟是100KHZ这样延时3微妙一周期6微妙这样最大传输速度为输入时钟频率的1/8CS输出低电平→MOSI与1向与→CLK输出一个脉冲→MOSI的数据右移动一位与1相与→ CLK输出一个脉冲……输出够8位(一个字节了)再下一个字节或者停止→停止之后CS拉高CS输出低电平→ CLK输出一个脉冲→MISO管脚取数据→ CLK输出一个脉冲→MISO管脚取数据→ CLK输出一个脉冲……输入够8位(一个字节了)再下一个字节或者停止→停止之后CS拉高3、一个主机和多个从器件的通信系统。
(2)3个既可以当做主机也可以当做从机的单片机组成的系统。
特点:SPI是一位一位发送的,并允许暂停,发多少位都行,不用寻址操作AVR有SPI功能接口的单片机SPI操作时钟是最大1/4 FOSC 最小1/128 FOSC把数据写到数据寄存器→CS拉低(启动一次通信)→再把数据写到数据寄存器→CS拉低(启动一次通信)→CS拉高结束通信注意:是时钟的上升沿还是下降沿有效时可以设置的---CPHA为1上升沿采样数据,为0下降沿采样数据,传输结束了SPIF会置位高位还是地位先发送是可以设置的----DORD为1地位先发送为0高位先发送数据寄存器SPFR是可读可写的—写数据进去就启动数据传输,读数据将读取数据的接收缓存区在主模式下,时钟信号的1次作用对应一位数据的发送(MOSI)和另一位数据的接收(MISO)。
51单片机的I2C底层驱动程序(IO口模拟)
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() //等待从机应答信号,如果从机迟迟没有应答,则结束总线。
实验八 51系列单片机IIC
I2C总线上的所有器件连接在一个公共的总线上,因此,主器件在进行数据传输前选择需要通信的从器件,即进行总线寻址。 I2C总线上所有外围器件都需要有惟一的地址,由器件地址和引脚地址两部分组成,共7位。器件地址是I2C器件固有的地址编码,器件出厂时就已经给定,不可更改。引脚地址是由I2C总线外围器件的地址引脚(A2,A1,A0)决定,根据其在电路中接电源正极、接地或悬空的不同,形成不同的地址代码。引脚地址数也决定了同一种器件可接入总线的最大数目。 地址位与一个方向位共同构成I2C总线器件寻址字节。寻址字节的格式如表所示。方向位(R/)规定了总线上的主器件与外围器件(从器件)的数据传输送方向。当方向位R/=1,表示主器件读取从器件中的数据;R/=0,表示主器件向从器件发送数据。
从地址中读取一个字节的数据
INT8U read_random(INT8U RomAddress) { INT8U Read_data; I_Start(); I_Write8Bit(WriteDeviceAddress); I_TestAck(); I_Write8Bit(RomAddress); I_TestAck(); I_Start(); I_Write8Bit(ReadDeviceAddress); I_TestAck(); Read_data=I_Read8Bit(); I_NoAck(); I_Stop(); return (Read_data); }
8.4.1 串行EEPROM存储器简介
串行EEPROM存储器是一种采用串行总线的存储器,这类存储器具有体积小、功耗低、允许工作电压范围宽等特点。目前,单片机系统中使用较多的EEPROM芯片是24系列串行EEPROM。其具有型号多、容量大、支持I2C总线协议、占用单片机I/O端口少,芯片扩展方便、读写简单等优点。 目前,Atmel、MicroChip、National等公司均提供各种型号的I2C总线接口的串行EEPROM存储器。下面以Atmel公司的产品为例进行介绍。 AT24C01/02/04/08系列是Atmel公司典型的I2C串行总线的EEPROM。这里以AT24C08为例介绍。AT24C08具有1024×8位的存储容量,工作于从器件模式,可重复擦写100万次,数据可以掉电保存100年。8引脚DIP封装的AT24C08的封装结构,如图所示。
51单片机任意2个IO口驱动LCD1602
51单片机任意2个IO口驱动LCD1602相信大家对1602显示屏已经十分熟悉,驱动方式有8线制(需要11根线)和4线制(需要7根线),这里为大家推荐一种只需要2根线就能驱动1602的方法。
之前在网上见到Arduino通过IIC驱动1602的实例,本人完全不懂Arduino程序,看了一下驱动电路,发现PCF8574这个关键芯片,它就相当于一个桥梁,将IIC总线转换为8位准双向口。
思路1、单片机通过IIC与PCF8574进行通信。
首先写好IIC通信程序,网上到处都是IIC通信程序,很容易找。
PCF8574 的器件地址为40h,由于硬件地址引脚A0-A2可寻址8 个器件,所以器件地址并不唯一,具体说明大家去查查PCF8574芯片手册.2、单片机4线制驱动1602网上也有很多相关程序,我就不再多说。
4线制驱动方式需要7个IO口(RS、RW、E和4条数据线),而PCF8574提供了8位准双向口,所以管脚还有剩余。
3、IIC通信程序和1602的4线制驱动程序相结合4、51单片机任意2个IO口驱动1602成功!!!。
(我只是个业余爱好者,要是各位觉得太低端那就见谅了)驱动电路图效果图实物图Proteus仿真程序#include <reg52。
h>#include 〈intrins。
h〉sbit SCL = P3^0;sbit SDA = P3^1;bit ack;unsigned char LCD_data;unsigned char code digit[ ]={”0123456789"}; //定义字符数组显示数字//*****************延时************************void delay_nus(unsigned int n) //N us延时函数{unsigned int i=0;for (i=0;i〈n;i++)_nop_();}void delay_nms(unsigned int n) //N ms延时函数{unsigned int i,j;for (i=0;i<n;i++)for (j=0;j<1140;j++);}void nop4(){_nop_(); //等待一个机器周期_nop_(); //等待一个机器周期_nop_(); //等待一个机器周期_nop_(); //等待一个机器周期}//***************************************void Start(){SDA=1;_nop_();SCL=1;nop4();SDA=0;nop4();SCL=0;_nop_();_nop_();}void Stop(){SDA=0;_nop_();SCL=0;nop4();//>4us后SCL跳变SCL=1;nop4();SDA=1;_nop_();_nop_();}//****************************************** void Write_A_Byte(unsigned char c){unsigned char BitCnt;for(BitCnt=0;BitCnt〈8;BitCnt++) //要传送的数据长度为8位{if((c<<BitCnt)&0x80) SDA=1; //判断发送位else SDA=0;_nop_();SCL=1; //置时钟线为高,通知被控器开始接收数据位 nop4();_nop_();SCL=0;}_nop_();_nop_();SDA=1; //8位发送完后释放数据线,准备接收应答位_nop_();_nop_();SCL=1;_nop_();_nop_();_nop_();if(SDA==1)ack=0;else ack=1; //判断是否接收到应答信号SCL=0;_nop_();_nop_();}bit Write_Random_Address_Byte(unsigned char add,unsigned char dat){Start(); //启动总线Write_A_Byte(add); //发送器件地址if(ack==0)return(0);Write_A_Byte(dat); //发送数据if(ack==0)return(0);Stop(); //结束总线return(1);}//********************液晶屏使能********************* void Enable_LCD_write(){LCD_data|=(1<<(3—1));//E=1;Write_Random_Address_Byte(0x40,LCD_data);delay_nus(2);LCD_data&=~(1〈<(3-1));//E=0;Write_Random_Address_Byte(0x40,LCD_data);}//*************写命令****************************void LCD_write_command(unsigned char command){delay_nus(16);LCD_data&=~(1<<(1—1));//RS=0;LCD_data&=~(1<〈(2-1));//RW=0;Write_Random_Address_Byte(0x40,LCD_data);LCD_data&=0X0f; //清高四位LCD_data|=command & 0xf0; //写高四位Write_Random_Address_Byte(0x40,LCD_data);Enable_LCD_write();command=command〈〈4; //低四位移到高四位LCD_data&=0x0f; //清高四位LCD_data|=command&0xf0; //写低四位Write_Random_Address_Byte(0x40,LCD_data);Enable_LCD_write();}//*************写数据****************************void LCD_write_data(unsigned char value){delay_nus(16);LCD_data|=(1<〈(1-1));//RS=1;LCD_data&=~(1〈〈(2-1));//RW=0;Write_Random_Address_Byte(0x40,LCD_data);LCD_data&=0X0f; //清高四位LCD_data|=value&0xf0; //写高四位Write_Random_Address_Byte(0x40,LCD_data);Enable_LCD_write();value=value〈〈4; //低四位移到高四位LCD_data&=0x0f; //清高四位LCD_data|=value&0xf0; //写低四位Write_Random_Address_Byte(0x40,LCD_data);Enable_LCD_write();}//**********************设置显示位置*********************************void set_position(unsigned char x,unsigned char y){unsigned char position;if (y == 0)position = 0x80 + x;elseposition = 0xc0 + x;LCD_write_command(position);}//**********************显示字符串*****************************void display_string(unsigned char x,unsigned char y,unsigned char *s){set_position(x,y);while (*s){LCD_write_data(*s);s++;}}//*************液晶初始化****************************void LCD_init(void){LCD_write_command(0x28);delay_nus(40);LCD_write_command(0x28);delay_nus(40);Enable_LCD_write();delay_nus(40);LCD_write_command(0x28); //4位显示!!!!!!!!!!!!!!!!!!LCD_write_command(0x0c); //显示开LCD_write_command(0x01); //清屏delay_nms(2);}void main(void){LCD_init();display_string(4,0,"imxuheng"); //显示一段文字display_string(2,1,”Hello Today!”); //显示一段文字while(1);}程序还不够完美,自身工作与电学没什么关系,只是业余爱好鼓捣鼓捣,希望各位能够提出修改意见。
51单片机模拟IIC工作时序,以EEPROM器件进行掉电保护数据
51单片机软件模拟I2C时序,进行掉电保护数据在数码管上按顺序显示数据0—f(十六进制)模拟断电-切断单片机电源复位后接着断电前数据显示#include<reg51.h>#define uchar unsigned char#define uint unsigned intsbit sda=P2^0;sbit scl=P2^1;uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39 ,0x5e,0x79,0x71} ;uchar num,sec,panju;//由定时器进行计数,计时,以数组的形式显示于数码管每隔1S读一次AT24C16中的数据//-------------------------------定时器0进行精确的计时,由AT24C16进行掉电保护----------------------------------//void delay(){;;}void init_timer0()//定时器初始化{TMOD=0X01;//工作方式1,GATE=0,C/~T=0,M0=0,M1=1;TH0=15536/256;//求模TL0=15536%256;//求余装入初值EA=1;ET0=1;//中断允许寄存器TR0=1;//TCON寄存器}//------------------------------------------------------------------------------------------------------------//void init_AT()//EEPROM器件初始化释放总线{scl=0;delay();//微秒延时sda=1;delay();scl=1;delay();}//----------------------------------------------------------------------------------------------------------//void start() //起始信号{scl=0;delay();sda=1;delay();scl=1;delay();sda=0;delay();sda=1;//释放数据总线//--------------------------------------------------------------------------------------------------------//void write_byte(uchar date)//字节写{uchar s,temp;//局部变量temp=date;//由高位一位一位的写入for(s=0;s<8;s++){temp=temp<<1;//左移溢出到PSW寄存器CY位scl=0;//允许sda进行的变化delay();sda=CY;//进行一位一位的送入数据总线delay();scl=1;//符合读走数据总线上数据的要求delay();//稳定一段时间}scl=0;//允许sda变化,以便于接受应答信号delay();sda=1;//释放数据总线,准备接收应答信号delay();}//--------------------------------------------------------------------------------------------------------//void response()//应答,可以看书page181,由接收设备发出的第九位数据,要经由sda传输给发送设备{uchar t;//局部变量scl=1;delay();while((sda==1)&&(t<250)){t++;//等待应答有一定的时间限制,sda被拉低表示有应答,由硬件控制}scl=0;delay();}//------------------------------------------------------------------------------------------------------//void stop()//停止信号,scl=1,sda形成一个正跳变{scl=0;//允许sda变化delay();sda=0;//准备形成正跳变delay();scl=1;//信号有效前提1delay();sda=1;//形成正跳变delay();//信号有效前提1}//-----------------------------------------------------------------------------------------------------//void delay1(uint z)//毫秒级延时{for(x=z;x>0;x--)for(y=10;y>0;y--);}//---------------------------------------------------------------------------------------------------------------//uchar read_byte()//带有返回值,将所读到的数据作为返回值{uchar i,d;//局部变量d=0x00;//清空存储变量的空间for(i=0;i<8;i++){scl=0;//允许变化,即允许传送数据,sda是serial dual date busdelay();scl=1;//允许读走数据delay();d=(d<<1)|sda;//将sda上数据放入存储变量d,也是8位ucharscl=0;delay();}return d;}//----------------------------------------------------------------------------------------------------------//void write_add(uchar address,uchar date) //任意位置,写任意内容{start();//起始信号write_byte(0xa0);//写入EEPROM器件地址,写入分为数据,地址数据,地址又分为器件地址和内部存储地址,例如某班级某学生座位response();//等待应答write_byte(address);//内部存储地址实际参数addressresponse();write_byte(date);//写入真正意义上的数据,由引入的实际参数dateresponse();//等待应答stop();//停止读取数据信号delay1(1);}//-------------------------------------------------------------------------------------------------------//uchar read_add(uchar address){uchar shu;start();write_byte(0xa0);//申明所读器件地址response();write_byte(address);//申明该器件中所要求读数据的位置response();start();write_byte(0xa1);response();shu=read_byte();//进行读数据操作后,非应答,budaixingcan,而是将返回值直接赋予变量stop();}//------------------------------------------------------------------------------------------------------------// void main(){P0=0X00;init_AT();sec=read_add(0X03);//将芯片中数据读出;作为基数init_timer0();//定时器初始化设置if(sec==16)sec=0;while(1){P0=table[sec];if(panju==1){panju=0;write_add(0x03,sec);}}}//---------------------------------------------------------------------------------------------------------// void timer0_break() interrupt 1{TH0=15536/256;//求模TL0=15536%256;//求余装入初值num++;if(num==20){num=0;sec++;panju=1;if(sec==16) sec=0;}}。
C51单片机模拟I2C总线驱动程序设计
_nop_(); } SCL = 0; _nop_(); _nop_(); return(retc); } /************************************ I2C_Ackn ******************************** 函数名:void I2C_Ackn(bit a) 入口:0 或 1 出口: 功能描述:主控制器进行应答信号(可以是应答或非应答信号) 说明:作为接收方的时候,必须根据当前自己的状态向发送器反馈应答信号 调用函数: 全局变量: *******************************************************************************
//位
retc = 0; SDA = 1; //置数据总线为输入方式,作为接收方要释放 SDA. for(BitCnt=0;BitCnt<8;BitCnt++) { _nop_(); SCL = 0; //置时钟线为低准备接收数据位 SomeNOP(); //时钟低电平周期大于 4.7us SCL = 1; //置时钟线为高使数据有效 _nop_(); _nop_(); retc = retc<<1; if(SDA==1) { retc = retc + 1; //读数据位,接收的数据放入 retc 中 } _nop_();
//发送器件子地址
//发送数据
//结束总线
/********************************** I2C_IRcvB ********************************* 函数名:bit I2C_IRcvB(uchar sla, uchar suba, uchar *c) 入口:从器件地址 sla, 子地址 suba, 收到的数据在 c 出口:1(操作成功),0(操作有误) 功能描述:从启动总线到发送地址、读数据,结束总线的全过程。
51单片机I2C总线驱动程序
51单片机I2C总线驱动程序SI2I2C 总线是PHLIPS 公司推出的一种串行总线,是具备多主机系统所需的包括总线裁决和高低速器件同步功能的高性能串行总线。
I2C 总线只有两根双向信号线。
一根是数据线SDA,另一根是时钟线SCL。
一.I2C 系统结构每个接到I2C 总线上的器件都有唯一的地址。
主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发送器。
由总线上接收数据的器件则为接收器。
二.数据位的有效性规定I2C 总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
三.字节传送与应答每一个字节必须保证是8 位长度。
数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9 位)。
四.驱动程序#define uchar unsigned char#define uint unsigned int#define somenop() _nop_(),_nop_(),_nop_(),_nop_(),_nop_(),_nop_()sbit SCL=P2;sb it SDA=P2;123451.起始信号和终止信号SCL 线为高电平期间,SDA 线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA 线由低电平向高电平的变化表示终止信号。
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态void I2C_Start() //起始{SCL=1;somenop();SDA=1;somenop(); SDA=0;somenop();SCL=0;somenop();}void I2C_Stop() //终止{ SDA=0;somenop();SCL=1;somenop();SDA=1;somenop();}12345678910111213141。
软件模拟I2C总线的C51实现.
软件模拟I2C总线的C51实现摘要:介绍51系列单片机上的I2C总线主节点模拟程序,从而实现与具有I2C接口的器件通信。
1I2C总线简介1.1硬件结构I2C串行总线支持所有NMOS、CMOS、I2L工艺制造的器件。
从物理上看由两根双向I/O线组成,一根为数据线(SDA),一根为时钟线(SCL),通过这两根线把所有器件连接到总线上,并通过SDA和SCL在各器件间传递信息(根据地址识别每个器件)。
SDA和SCL通过上拉电阻接正电源,总线空闲时,两根线都是高电平。
这两根I/O线在电气上允许“线与”操作,其输出的驱动形式为集电极开路或漏极开路。
根据通信速度的不同,I2C总线分为三种工作模式:标准模式、快速模式和高速模式。
它们分别对应不同的波特率:100kb/s、400kb/s和3.4Gb/s。
总线上允许的设备数以总线上的电容量不超过400pF为限。
1.2数据传输I2C总线上数据为同步传输。
挂在I2C总线上的每一个器件都有一个独立的地址,而且在传输过程中有主节点和从节点的区分,主节点的作用是启动和结束一次通信,并负责控制总线时钟,总线上可以有多个主节点或多个从节点,但是在一次通信中只能有一个节点作为主节点。
主从机之间一次数据的传输称为一帧,由启动信号、地址信息、应答位及停止位组成。
其传送格式见图1。
2MCS-51与I2C总线芯片接口及程序2.18051经I2C总线扩展存储器PCF8582对于内部没有硬件I2C总线接口的51系列单片机,可以采用软件模拟的方法实现I2C总线接口功能。
硬件连接如图2所示。
用8051的P1.6和P1.7作为I2C总线的SCL利SDA信号,在总线上连接256*8的EEPROM芯片PCF8582。
8051单片机与PCF8582进行数据传递时,首先传送器件的从机地址SLA,格式如下:START为起始信号,从机地址的固定部分是4位——1010,可编程部分由,则该片的从机地址为引脚A2、A1、A0确定。
51单片机IO口线模拟IIC总线3部曲之第3部
本文是对第1,2部的使用说明。
若有错误之处请不吝赐教。
IIC底层驱动包使用说明一:说明:(1) 本软件包是使用51单片机IO口线来模拟IIC总线。
适用于12T周期单片机。
若想移植到单T周期单片机,请自行修改。
(2) IIC总线时钟是通过软件延时的方法产生的,所以对不同频率的时钟,需要做一些调整。
(3) 本驱动是IIC最底层的驱动函数,请勿随意改动本驱动。
(4) 本驱动对外只有两个函数可供使用。
一个是“写多字节函数”IICMCUSendStr(),另一个是“先写多字节,然后重新启动总线写一字节,最后接收多字节函数”IICMCUSendStrSendByteRcvStr()。
关于这两个函数的说明请见第三部分,函数详解。
(5) 针对不同的IIC器件读写时序的不同,您应该自己编写IIC器件的驱动。
二:移植方法:使用时有3个地方需要修改,以实现移植。
SDA,SCL和N_12MHz,这三个变量都在IICLowDriver.H文件中。
并且它们定义在文件最开始的位置。
SDA:IIC数据线。
请根据您的硬件设置来选择正确的IO口。
SCL:IIC时钟线。
请根据您的硬件设置来选择正确的IO口。
N_12MHz:所用时钟频率F(单位MHz)与12MHz的比值。
该变量关系到IIC总线时序的正确与否,一定要设置正确。
其取值见下表。
时钟频率F取值范围(MHz)0 ≤ F ≤ 1212 < F ≤ 2424 < F ≤ 3636 < F ≤ 48N_12MHz取值1234只要设置好这三个变量,就可以方便的使用IIC总线了。
为增加移植时的安全性,这三个变量用了预处理命令,移植时请勿改动预处理命令。
三:函数详解。
1、向IIC总线发送多字节数据函数(对外接口)本函数用于IIC器件的写操作。
//-------------------------------------------------------------------------------////函数名称:IICMCUSendStr//函数功能:向IIC总线发送多字节数据。
STC51单片机普通IO口模拟IIC(I2C)接口通讯的程序代码
STC51单片机普通IO口模拟IIC(I2C)接口通讯的程序代码STC 51单片机普通IO口模拟IIC(I2C)接口通讯的程序代码原文:(改自周立功软件包)#include <reg51.h>#include <intrins.h>#define uchar unsigned char /*宏定义*/#define uint unsigned intextern void Delay1us(unsigned char );sbit SDA=P1^6; /*模拟I2C数据传送位*/sbit SCL=P1^7; /*模拟I2C时钟控制位*/bit ack; /*应答标志位*//************************************************************** *****起动总线函数函数原型: void Start_I2c();功能: 启动I2C总线,即发送I2C起始条件.*************************************************************** *****/void Start_I2c(){SDA=1; /*发送起始条件的数据信号*/Delay1us(1);SCL=1;Delay1us(5); /*起始条件建立时间大于4.7us,延时*/SDA=0; /*发送起始信号*/Delay1us(5); /* 起始条件锁定时间大于4μs*/SCL=0; /*钳住I2C总线,准备发送或接收数据 */Delay1us(2);}/************************************************************** *****结束总线函数函数原型: void Stop_I2c();功能: 结束I2C总线,即发送I2C结束条件.*************************************************************** *****/void Stop_I2c(){SDA=0; /*发送结束条件的数据信号*/Delay1us(1); /*发送结束条件的时钟信号*/SCL=1; /*结束条件建立时间大于4us*/Delay1us(5);SDA=1; /*发送I2C总线结束信号*/Delay1us(4);}/*******************************************************************字节数据发送函数函数原型: void SendByte(uchar c);功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对此状态位进行操作.(不应答或非应答都使ack=0)发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
单片机IO口模拟串口程序(发送+接收)
单片机IO口模拟串口程序(发送+接收)前一阵一直在做单片机的程序,由于串口不够,需要用IO口来模拟出一个串口。
经过若干曲折并参考了一些现有的资料,基本上完成了。
现在将完整的测试程序,以及其中一些需要总结的部分贴出来。
程序硬件平台:11.0592M晶振,STC单片机(兼容51)/************************************** ************************** 在单片机上模拟了一个串口,使用P2.1作为发送端* 把单片机中存放的数据通过P2.1作为串口TXD发送出去*************************************** ************************/#include <reg51.h>#include <stdio.h>#include <string.h>typedef unsigned char uchar;int i;uchar code info[] ={0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x5 5,0x55,0x55,0x55,0x55,0x55,0x55,0x55 };sbit newTXD = P2^1;//模拟串口的发送端设为P2.1void UartInit(){SCON = 0x50; // SCON: serail mode 1, 8-bit UARTTMOD |= 0x21; // T0工作在方式1,十六位定时PCON |= 0x80; // SMOD=1;TH0 = 0xFE; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHzTL0 = 0x7F; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=11.0592MHz// TH0 = 0xFD; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=18.432MHz// TL0 = 0x7F; // 定时器0初始值,延时417us,目的是令模拟串口的波特率为2400bps fosc=18.432MHz}void WaitTF0(void){while(!TF0);TF0=0;TH0=0xFE; // 定时器重装初值fosc=11.0592MHzTL0=0x7F; // 定时器重装初值fosc=11.0592MHz// TH0 = 0xFD; // 定时器重装初值 fosc=18.432MHz// TL0 = 0x7F; // 定时器重装初值 fosc=18.432MHz}void WByte(uchar input){//发送启始位uchar j=8;TR0=1;newTXD=(bit)0;WaitTF0();//发送8位数据位while(j--){newTXD=(bit)(input&0x01); //先传低位WaitTF0();input=input>>1;}//发送校验位(无)//发送结束位newTXD=(bit)1;WaitTF0();TR0=0;}void Sendata(){for(i=0;i<sizeof(info);i++)//外层循环,遍历数组{WByte(info[i]);}}void main(){UartInit();while(1){Sendata();}}########################################## ####################################/************************************** ************************** 模拟接收程序,这个程序的作用从模拟串口接收数据,然后将这些数据发送到实际串口* 在单片机上模拟了一个串口,使用P3.2作为发送和接收端* 以P3.2模拟串口接收端,从模拟串口接收数据发至串口*************************************** ************************/#include<reg51.h>#include<stdio.h>#include<string.h>typedef unsigned char uchar ;//这里用来切换晶振频率,支持11.0592MHz 和18.432MHz//#define F18_432#define F11_0592uchar tmpbuf2[64]={0};//用来作为模拟串口接收数据的缓存struct{uchar recv :6 ;//tmpbuf2数组下标,用来将模拟串口接收到的数据存放到tmpbuf2中uchar send :6 ;//tmpbuf2数组下标,用来将tmpbuf2中的数据发送到串口}tmpbuf2_point={0,0};sbit newRXD=P3^2 ;//模拟串口的接收端设为P3.2void UartInit(){SCON=0x50 ;// SCON: serail mode 1, 8-bit UARTTMOD|=0x21 ;// TMOD: timer 1, mode 2, 8-bit reload,自动装载预置数(自动将TH1送到TL1);T0工作在方式1,十六位定时PCON|=0x80 ;// SMOD=1;#ifdef F11_0592TH1=0xE8 ;// Baud:2400 fosc=11.0592MHz 2400bps为从串口接收数据的速率TL1=0xE8 ;// 计数器初始值,fosc=11.0592MHz 因为TH1一直往TL1送,所以这个初值的意义不大TH0=0xFF ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHzTL0=0xA0 ;// 定时器0初始值,延时208us,目的是令模拟串口的波特率为9600bps fosc=11.0592MHz#endif#ifdef F18_432TH1=0xD8 ;// Baud:2400fosc=18.432MHz 2400bps为从串口接收数据的速率TL1=0xD8 ;// 计数器初始值,fosc=18.432MHz 因为TH1一直往TL1送,所以这个初值的意义不大TH0=0xFF ;// 定时器0初始值,延时104us,目的是令模拟串口的波特率为9600bps fosc=18.432MHzTL0=0x60 ;// 定时器0初始值,延时104us,目的是令模拟串口的波特率为9600bps fosc=18.432MHz#endifIE|=0x81 ;// 中断允许总控制位EA=1;使能外部中断0TF0=0 ;IT0=1 ;// 设置外部中断0为边沿触发方式TR1=1 ;// 启动TIMER1,用于产生波特率}void WaitTF0(void){while(!TF0);TF0=0 ;#ifdef F11_0592TH0=0xFF ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz TL0=0xA0 ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz #endif#ifdef F18_432TH0=0xFF ;// 定时器重装初值 fosc=18.432MHzTL0=0x60 ;// 定时器重装初值 fosc=18.432MHz#endif}//接收一个字符uchar RByte(){uchar Output=0 ;uchar i=8 ;TR0=1 ;//启动Timer0#ifdef F11_0592TH0=0xFF ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz TL0=0xA0 ;// 定时器重装初值模拟串口的波特率为9600bps fosc=11.0592MHz #endif#ifdef F18_432TH0=0xFF ;// 定时器重装初值fosc=18.432MHzTL0=0x60 ;// 定时器重装初值fosc=18.432MHz#endifTF0=0 ;WaitTF0();//等过起始位//接收8位数据位while(i--){Output>>=1 ;if(newRXD)Output|=0x80 ;//先收低位WaitTF0();//位间延时}TR0=0 ;//停止Timer0return Output ;}//向COM1发送一个字符void SendChar(uchar byteToSend){SBUF=byteToSend ;while(!TI);TI=0 ;}void main(){UartInit();while(1){if(tmpbuf2_point.recv!=tmpbuf2_point.send)//差值表示模拟串口接收数据缓存中还有多少个字节的数据未被处理(发送至串口){SendChar(tmpbuf2[tmpbuf2_point.send++]);}}}//外部中断0,说明模拟串口的起始位到来了void Simulated_Serial_Start()interrupt 0{EX0=0 ;//屏蔽外部中断0tmpbuf2[tmpbuf2_point.recv++]=RByte(); //从模拟串口读取数据,存放到tmpbuf2数组中IE0=0 ;//防止外部中断响应2次,防止外部中断函数执行2次EX0=1 ;//打开外部中断0}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~以上是两个独立的测试程序,分别是模拟串口发送的测试程序和接收的测试程序上面两个程序在编写过程中参考了这篇文章《51单片机模拟串口的三种方法》(在后文中简称《51》),但在它的基础上做了一些补充,下面是若干总结的内容:1、《51》在接收数据的程序中,采用的是循环等待的方法来检测起始位(见《51》的“附:51 IO 口模拟串口通讯C源程序(定时器计数法)”部分),这种方法在较大程序中,可能会错过起始位(比如起始位到来的时候程序正好在干别的,而没有处于判断起始位到来的状态),或者一直在检测起始位,而没有办法完成其他工作。
I_2C总线的软件模拟在KeilC51中的实现
电脑编程技巧与维护I2C总线的软件模拟在KeilC51中的实现孙刚 1,冯国雨 2,朱孟忠 1(1. 空军航空大学电工电子教研室,长春 130022;2. 空军航空大学航空理论系机载设备教研室,长春 130022)摘 要:I2C总线是飞利浦公司总线提出的串行通信接口规范,它使用两条线:串行数据线(SDA)和串行时钟线(SCL),即可实现总线上器件之间传送信息。
因此,I2C总线占用的空间非常小,减少了芯片管脚的数量,降低了互联成本。
但目前应用很广的MCS51系列和AT89系列的单片机都不具有这种总线的接口,很大程度上限制了它们的应用范围。
本文介绍了I2C总线的协议,并给出了在KeilC51 uVision 2中的实现方法,方便了不具备I2C总线功能的单片机的应用。
关键词:I2C;KeiC51;软件模拟The Implement of I2C Bus Software Simulation in Keil C51SUN Gang1,FENG GuoYu2,ZHU Mengzhong1(1.Department of Electronics, Aviation University of Air Force, Changchun 130022;2. Department of Aviation Theory, Aviation University of Air Force, Changchun 130022 )Abstract: I2C bus is raised by Philips semiconductors company,which is a serial communication rule,it makes ICs on the bus transmit information through 2 lines:serial data ( SDA ) and serial clock(SCL),so the space I2C occupied is very little, this can largely reduce the pins of ICs and the cost of connecting ICs. But at present the widely-used microprocessors MCS51 series and AT89 series do not have I2C interface, this largely confines the application area of them. This paper introduces the rule of I2C bus and the implement of I2C bus in Keil C51 uVision 2,this can makes the application of the microprocessors convenient,which do not have I2C bus interface.Key words: I2C; KeiC51; software simulation1 前言I2C 总线是飞利浦公司总线是提出的串行通信接口规范,它使用两条线: 串行数据线(SDA)和串行时钟线(SCL),使连接到该总线上可访问的器件之间实现信息的传送。
51单片机模拟I2C总线的C语言实现
51单⽚机模拟I2C总线的C语⾔实现I2C(Inter-Integrated Circuit)总线是⼀种由PHILIPS公司开发的两线式串⾏总线,⽤于连接微控制器及其外围设备。
I2C总线产⽣于在80年代,最初为⾳频和视频设备开发,如今主要在服务器管理中使⽤,其中包括单个组件状态的通信。
例如管理员可对各个组件进⾏查询,以管理系统的配置或掌握组件的功能状态,如电源和系统风扇。
可随时监控内存、硬盘、⽹络、系统温度等多个参数,增加了系统的安全性,⽅便了管理。
⼀、I2C总线特点 I2C总线最主要的优点是其简单性和有效性。
由于接⼝直接在组件之上,因此I2C总线占⽤的空间⾮常⼩,减少了电路板的空间和芯⽚管脚的数量,降低了互联成本。
总线的长度可⾼达25英尺,并且能够以10Kbps的最⼤传输速率⽀持40个组件。
I2C总线的另⼀个优点是,它⽀持多主控 (multimastering), 其中任何能够进⾏发送和接收的设备都可以成为主总线。
⼀个主控能够控制信号的传输和时钟频率。
当然,在任何时间点上只能有⼀个主控。
⼆、I2C总线⼯作原理2.1、总线的构成及信号类型I2C 总线是⼀种串⾏数据总线,只有⼆根信号线,⼀根是双向的数据线SDA,另⼀根是时钟线SCL。
在CPU与被控IC之间、IC与IC之间进⾏双向传送,最⾼传送速率100kbps。
各种被控制电路均并联在这条总线上,但就像电话机⼀样只有拨通各⾃的号码才能⼯作,所以每个电路和模块都有唯⼀的地址,在信息的传输过程中,I2C总线上并接的每⼀模块电路既是主控器(或被控器),⼜是发送器(或接收器),这取决于它所要完成的功能。
CPU发出的控制信号分为地址码和控制量两部分,地址码⽤来选址,即接通需要控制的电路,确定控制的种类;控制量决定该调整的类别(如对⽐度、亮度等)及需要调整的量。
这样,各控制电路虽然挂在同⼀条总线上,却彼此独⽴,互不相关。
2.2、位的传输SDA 线上的数据必须在时钟的⾼电平周期保持稳定数据线的⾼或低电平状态只有在SCL 线的时钟信号是低电平时才能改变。
stm8s使用IO口模拟I2C
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。
I2C总线及其驱动程序
SDA SCL 起始 信号 static void __zyI2cAckSend (void) { __ZY_I2C_SDA = 0; __ZY_I2C_DELAY(); __ZY_I2C_SCL = 1; __ZY_I2C_DELAY(); __ZY_I2C_SCL = 0; } 1 2 8 9 应答 信号 1 2 8 9 非应答 信号 停止 信号
1 0 1 0 1 0 1 0
MSB LSB
主机
MSB
LSB
从机
接收一个字节的数据
程序示例
static unsigned char __zyI2cByteReceive(void) { unsigned char ucRt; unsigned char i; // 接收数据 i = 8; do { ucRt = (ucRt << 1) + __zyI2cBitReceive(); } while (--i != 0); }
空闲时SDA状态未知, 需手动拉低
发送重复起始信号
在I2C总线忙时,产生起始条件,以改变数据收发方向。
SDA(I/O) SDA
80C51
SCL(I/O)
I2C从机
SCL
static void __zyI2cStartSend (void) { __ZY_I2C_SDA = 1; __ZY_I2C_DELAY(); __ZY_I2C_SCL = 1; __ZY_I2C_DELAY(); __ZY_I2C_SDA = 0; __ZY_I2C_DELAY(); __ZY_I2C_SCL = 0; }
从机具有I2C地址,内部寄存器均对应具体地址 主机对从机的操作即对寄存器的读写操作
初始化
读操作
STM32L051测试 (三、I2C协议设备的添加测试)
前面两篇文章,把L051基本功能都测试过了,确实感觉到ST的CubeMX工具真是好用,对应换芯片的应用来说,着实方便,底层库封装好,上层应用程序基本都一样~~今天我们就来吧I2C设备添加一下,今天正好借这个机会,把I2C的代码优化一下。
•1、CubeMX IO口的设置•2、HAL库中的us延时函数•3、移植I2C代码▪ 3.1 SHT21温湿度传感器1、CubeMX IO口的设置模拟I2C的IO口都设置为开漏输出,因为电路图上有外部上拉。
初始化的时候,2个SDA和SCL都得拉高。
所以设置如下:在这里插入图片描述2、HAL库中的us延时函数在HAL库中,只有ms的延时函数HAL_Delay,没有us的延时函数,今天用了网上一个延时函数,发现有问题,搞得整个时钟出了问题,郁闷,折腾了一下午。
时间浪费了,通讯模块那边因为这个时钟设置也出了点问题,一直返回复位信息,用了怎么多年的芯片还第一次遇到,Enocean 的TCM310模块,主要这个技术支持也不太好找。
= =!还是快速找一个us函数,不是那么精确也可以,直接用空语句测试,后面已经经过测试,下面这个函数做的us延时可以正常移植以前的I2C程序。
/* USER CODE BEGIN 4 */void delay_us(uint32_t Delay){uint32_t cnt = Delay * 8;uint32_t i = 0;for(i = 0; i < cnt; i++)__NOP();}/* USER CODE END 4 */3、移植I2C代码3.1 SHT21温湿度传感器代码移植没什么问题,i2c.c:#include "i2c.h"// ------------------------------------------------------------------void i2c_init(void) {// the SDA and SCL pins are defined as input with pull up enabled// pins are initialized as inputs, ext. pull => SDA and SCL = high}// ------------------------------------------------------------------// send start sequence (S)void i2c_start(void) {sda_high();delay_us(10);scl_high();delay_us(10);sda_low();delay_us(10);scl_low();delay_us(10);}// ------------------------------------------------------------------// send stop sequence (P)void i2c_stop(void) {sda_low();delay_us(10);scl_low();delay_us(10);scl_high();delay_us(10);sda_high();delay_us(10);}// ------------------------------------------------------------------// returns the ACK or NACKuint8 i2c_write(uint8 u8Data){uint8 u8Bit;uint8 u8AckBit;// write 8 data bitsu8Bit = 0x80; //msb firstwhile(u8Bit) {if(u8Data&u8Bit) {sda_high();delay_us(20);}//& compare every bitelse{sda_low();delay_us(20);}scl_high();delay_us(30);u8Bit >>= 1;//next bitscl_low();delay_us(30);}// read acknowledge (9th bit)sda_high();delay_us(10);scl_high();delay_us(10);u8AckBit= sda_read(); //#define sda_read() (sda_port & sda_pin)? 1 :0 ack on busis low -> u8AckBit = 1 sda_port gpio0 sda_pin SCSEDIO0delay_us(10);scl_low();delay_us(10);return u8AckBit;}// ------------------------------------------------------------------// pass the ack/nack// returns the read datauint8 i2c_read(uint8 u8Ack){uint8 u8Bit;uint8 u8Data;u8Bit = 0x80; // msb firstu8Data = 0;while(u8Bit){scl_high();delay_us(20);u8Bit >>= 1; //next bitu8Data <<= 1;u8Data |= sda_read(); //(sda_port & sda_pin)? 1 :0 sda_port gpio0 sda_pin SCSEDIO0delay_us(20);scl_low();delay_us(50);}// 9th bit acknowledgeif(u8Ack==I2C_ACK) {sda_low();delay_us(20);}//I2C_ACK=0else {sda_high();delay_us(20);}scl_high();delay_us(20);scl_low();delay_us(20);sda_high();delay_us(20);return u8Data;}i2c.h:#ifndef _I2C_H_INCLUDED#define _I2C_H_INCLUDED#include "main.h"#include "Datadef.h"// #define I2C_CLK_HIGH() HAL_GPIO_WritePin(sht_scl_GPIO_Port,sht_scl_Pin,GPIO_PIN _SET);// #define I2C_CLK_LOW() HAL_GPIO_WritePin(sht_scl_GPIO_Port,sht_scl_Pin,GPIO_PIN _RESET);// #define I2C_DATA_HIGH() HAL_GPIO_WritePin(sht_sda_GPIO_Port,sht_sda_Pin,GPIO_P IN_SET);// #define I2C_DATA_LOW() HAL_GPIO_WritePin(sht_sda_GPIO_Port,sht_sda_Pin,GPIO_P IN_RESET);// #define I2C_DATA_STATE() (HAL_GPIO_ReadPin(sht_sda_GPIO_Port,sht_sda_Pin) == GP IO_PIN_SET);// #define sda_high() I2C_DATA_HIGH() // set signals to HIGH first before sele cting IN -> slew rates// #define sda_low() I2C_DATA_LOW()// #define sda_read() I2C_DATA_STATE() //ack on bus is low -> u8AckBit = 1// #define scl_high() I2C_CLK_HIGH() // set signals to HIGH first before selec ting IN -> slew rates// #define scl_low() I2C_CLK_LOW()#define sda_high() HAL_GPIO_WritePin(sht_sda_GPIO_Port,sht_sda_Pin,GPIO_PIN_SET); #define sda_low() HAL_GPIO_WritePin(sht_sda_GPIO_Port,sht_sda_Pin,GPIO_PIN_RESET) ;#define sda_read() (HAL_GPIO_ReadPin(sht_sda_GPIO_Port,sht_sda_Pin) == GPIO_PIN_SE T);#define scl_high() HAL_GPIO_WritePin(sht_scl_GPIO_Port,sht_scl_Pin,GPIO_PIN_SET); #define scl_low() HAL_GPIO_WritePin(sht_scl_GPIO_Port,sht_scl_Pin,GPIO_PIN_RESET) ;// ------------------------#define DONOTHING() {;}// ------------------------// command's#define I2C_WRITE 0#define I2C_READ 1#define I2C_ACK 0#define I2C_NACK 1void i2c_init(void);void i2c_start(void);void i2c_stop(void);uint8 i2c_write(uint8 u8Data);uint8 i2c_read(uint8 u8Ack);#endifSHT21部分的驱动就不用怎么修改了,基本上直接拿过来,把ms延时函数替换一下,就直接用,这里就不贴出来测试结果:板载的HTU21D,实际测试下来,一直会比空气问题高一点,这个问题,我倒是有点头疼,虽然做过分割,不铺铜等一些处理,还是不尽如人意。
基于软件模拟的51单片机IIC总线的实现[1]
IIC 总线应用系统的组网方式非常灵活,如 1 个主
MCU 和几个从 MCU 或一个主 MCU 和几个 I/O 设备 等构成的多种系统。大多数系统中,只用一个主 MCU 来控制挂在 IIC 总线上的所有被控器,图 1 是 IIC 总 线系统的典型电路图。
SCL 主控器
SDA
《电子技术》2004 年第 5 期
计算机应用
基于软件模拟的 51 单片机 IIC 总线的实现
安徽理工大学电气工程系(232001) 凌六一 淮南师范学院信息技术系(232001) 伍 龙
摘 要 文章在简述 IIC 总线的基础上,介绍了通过软件模拟来实现 IIC 总线在 MCS51 系列单片机中 的应用,从而使不带 IIC 总线接口的 51 单片机,也能扩展 IIC 总线接口器件。同时,以访问 IIC 总线 接口芯片 EEPROM AT24C02 为例,并且给出具体程序来说明软件模拟的实现过程。 关键词 IIC 总线 MCS51 单片机 软件模拟
下位机系统作为测控站安装在被测点上(每测点
装置的设计开发趋于容易、方便、周期短、成本低。 以 nRF 系列芯片为核心的 PTR2000 无线收发数传
SDA
SCL
启动信号
Байду номын сангаас停止信号
图4 IIC总线的启动信号和停止信号
(2)停止信号 在时钟线 SCL 保持高电平期间,数据线 SDA 被 释放,使 SDA 的电平正跳变,定义为 IIC 总线的“停 止信号”。停止信号也是一种电平跳变的时序信号,而 不是一个电平信号。停止信号也是由主控器主动建立 的,该信号之后,IIC 总线返回空闲状态。如图 4 所 示。 (3)数据传送字节信号 在 IIC 总线上传送的每一位数据都有一个时钟脉 冲相对应。也就是说,在 SCL 时钟的配合下,在 SDA 数据线上一位一位地传送数据。进行数据传送时,在 SCL 高电平期间,SDA 线上的电平必须保持稳定,当 SCL 为低电平期间,才允许 SDA 线上的电平改变状 态。如图 5 所示。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
*/ Hale Waihona Puke /*i2c.h文件 */
#ifndef __I2C_H_
#define __I2C_H_
sbit SCL = P2^1;
sbit SDA = P2^0;
void start_i2c(); //启动I2C总线:SCL高电平期间,SDA由高变低
/*
Title:I2C for 80C51
Author:yuyouliang
51单片机(本人使用STC89C52单片机,12T模式)的I2C驱动程序,使用逻辑分析仪对该协议进行分析,发现波形比较美观,SCL的频率在70KHz左右(11.0592M晶振),低于标准的100K,可以适应大多数的I2C器件。如果感觉速度过快或过慢,可以自行修改延时。
void stop_i2c(); //停止I2C总线:SCL高电平期间,SDA由低变高
void send_i2c(unsigned char c); //主机发送一个字节,先发送最高位
unsigned char receive_i2c(); //主机接收一个字节,先接收最高位
void master_ack(bit ack); //主机非应答信号(填参数0)或应答信号(填参数1)
i=200;
while(SDA&&(i--));
if(!SDA)
SCL = 0;
else
stop_i2c();
}
void send_i2c(unsigned char c) //主机发送一个字节,先发送最高位
void slave_ack(); //等待从机应答信号
#endif
/* i2c.c文件 */
#include <reg51.h>
#include <i2c.h>
#include <intrins.h>
#define nop() _nop_()
void start_i2c() //启动I2C总线:SCL高电平期间,SDA由高变低
{
unsigned char BitCnt;
for(BitCnt=8;BitCnt>0;BitCnt--)
{
c<<=1;
SDA=CY;
SCL=1;
nop();nop();nop();nop();
SCL=0;
SCL=1;
nop();nop();nop();nop();
retc|=SDA;
SCL=0;
}
return retc;
}
void master_ack(bit ack) //主机非应答信号(填参数0)或应答信号(填参数1)
{
SDA=!ack;
nop();nop();nop();nop();
SDA=1;
}
void slave_ack() //等待从机应答信号,如果从机迟迟没有应答,则结束总线。
{
unsigned char i;
SDA = 1;
SCL = 1;
{
SDA=1;
SCL=1;
nop();nop();nop();nop();
SDA=0;
SCL=0;
}
void stop_i2c() //停止I2C总线,SCL高电平期间,SDA由低变高
{
SDA=0;
SCL=1;
}
}
unsigned char receive_i2c() //主机接收一个字节,先接收最高位
{
unsigned char retc=0,BitCnt;
SDA=1;
for(BitCnt=8;BitCnt>0;BitCnt--)
{
retc<<=1;
SCL=1;
nop();nop();nop();nop();
SCL=0;
}