51单片机(八).单片机的I2C与串口通讯

您所在的位置:网站首页 美国士官臂章图片大全 51单片机(八).单片机的I2C与串口通讯

51单片机(八).单片机的I2C与串口通讯

2023-10-05 12:05| 来源: 网络整理| 查看: 265

STC89C52的I2C与串口通信 I2C总线

单片机电路中的I2C总线(Inter Integrated Circuit Bus)是一种Philips公司推行的串行总线标准。连接在总线上的外设通过总线寻址,是具有总线仲裁和高低速设备同步等功能的高性能多主机总线。

I2C总线的工作原理

1、总线由串行数据线SDA和串行时钟线SCL构成,可发送和接受数据。 2、所有连接在I2C总线上的器件和接口电路都必须具有I2C总线接口,且所有的SDL/SCL同名端相互连接。 3、在总线上各个I2C设备之间依靠SDA发送的地址信息进行寻址。

I2C总线的特点

组成I2C系统的电路结构简单(两根线)、占用空间小、可以降低芯片的引脚数量、允许若干兼容器件共享总线。总线长度可达7.6m,传送速度可达400Kbps,标准速率100Kbps。支持多主控器件(某一时刻只能有一个主控器件)。需要注意的是:I2C线上的所有设备SDA、SCL引脚必须要外接上拉电阻(电阻值的大小会影响通信)。

I2C总线结构示意图

挂载在I2C总线上的器件

I2C总线器件寻址方式

I2C总线上的主器件在进行数据传输前需要通过寻址,选择需要进行通信的从器件。总线上所有外围器件都必须要有唯一的7位地址,由器件地址和引脚地址两部分组成。 器件地址是I2C器件固有的编码地址,器件出厂时已经给定不可更改。 引脚地址由I2C总线外设的地址引脚决定(A2、A1、A0),根据其在电路中的正负极或悬空等状态,形成不同的地址代码。(内部电路)

51单片机模拟I2C总线协议

在实际应用中,往往遇到所使用的单片机没有I2C总线接口,例如典型的51系列单片机。为了让此类单片机用于操作 I2C总线器件的能力,需要在程序模拟I2C总线数据传输协议。

I2C总线通信时序图

时序图1 起始信号用于开启I2C总线的通信。其中,起始信号是在时钟线SCL为高电平期间,数据SDA上高电平向低电平变化的下降沿信号。起始信号出现以后,才可以进行后续的I2C总线寻址或数据传输; 终止信号用于终止I2C总线的通信。其中,终止信号是在时钟线SCL为高电平期间,数据线SDA上低电平到高电平变化的上升沿信号。终止信号一出现,所有I2C总线操作都结束,并释放总线控制权; 应答信号用于表明I2C总线数据传输的结束。 I2C总线数据传送时, 一个字节数据传送完毕后都必须由主器件产生应答信号。主器件在第9个时钟位上释放数据总线SDA,使其处于高电平状态,此时从器件输出低电平拉低数据总线SDA为应答信号。 时序图2

I2C总线传送格式

I2C总线的传送格式为主从式,对系统中的某一器件来说有四种工作方式: 主发送方式、从发送方式、主接收方式、从接收方式。 单片机发送 24C02(存储芯片)接收:主器件产生开始信号以后,发送的第一个字节为控制字节。前七位为从器件的地址片选信号。最低位为数据传送方向位(高电平表示读从器件,低电平表示写从器件),然后发送一个选择从器件片内地址的字节,来决定开始读写数据的起始地址。接着再发送数据字节,可以是单字节数据,也可以是一组数据,由主器件来决定。从器件每接收到一个字节以后,都要返回一个应答信号(ASK=0)。主器件在应答时钟周期高电平期间释放SDA线,转由从器件控制,从器件在这个时钟周期的高电平期间必须拉低SDA线,并使之为稳定的低电平,作为有效的应答信号。

I2C总线应用实例 1、24C02存储芯片的使用 #include #include #define uchar unsigned char #define uint unsigned int #define OP_READ 0xa1 //24c02地址和读取操作 #define OP_WRITE 0xa0//写入操作 sbit SDA=P2^0;//器件数据总线 sbit SCL=P2^1; //器件时钟总线 sbit DS1302=P2^4;//1302片选端 sbit LCDEN=P2^5; uchar code display[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff}; void delay1ms() { uchar i,j; for(i=0;i uint y; for(y=0;y SCL=1; SDA=0; _nop_(); _nop_(); _nop_(); _nop_(); SDA=1; _nop_(); _nop_(); _nop_(); _nop_(); SDA=0; SCL=0; } uchar ReadDate()//从器件中读数据 { uchar i,x; for(i=0;i uchar i; bit ack_bit; for(i=0;i start(); WriteCurrent(OP_WRITE); //选择芯片告知操作 WriteCurrent(add); //写入地址 WriteCurrent(date); //向上述地址传输数据 stop(); delaynms(4); } uchar ReadCurrent() //读数据后发送回单片机 { uchar x; start(); WriteCurrent(OP_READ); x=ReadDate(); stop(); return x; } uchar ReadSet(uchar set_addr) //在指定地址读取数据 { start(); WriteCurrent(OP_WRITE); WriteCurrent(set_addr); return(ReadCurrent()); } main(void) { uchar i; LCDEN=0; DS1302=0; P1=0xfe; SDA=1; SCL=1; //空闲状态 for(i=0;i P1=ReadSet(i); delaynms(200); delaynms(200); } } 2、AD数模转换的使用 #include #include #define uchar unsigned char #define uint unsigned int #define delaynop(); {_nop_();_nop_();_nop_();_nop_();};//延时4us #define PCF8591_WRITE 0x90 #define PCF8591_READ 0x91//IIC器件PCF5891的写地址和读地址 #define disdata P0 //液晶LCD1602定义 sbit lcd_RS=P1^0; sbit lcd_RW=P1^1; sbit lcd_EN=P2^5; //数码管定义 sbit dula=P2^6; sbit wela=P2^7; sbit DS1302=P2^4; //IIC总线定义 sbit SDA=P2^0; sbit SCL=P2^1; uint data dis[4]={0x00,0x00,0x00,0x00}; uchar code dis4[]={"1- . V 2- . V"}; uchar code dis5[]={"3- . V 4- . V"}; bit bdata SystemError;//丛机错误标志位 #define NUM 4 //缓存区深度 uchar idata receivebuf[NUM];//数据接收缓冲区 void stopSMG()//关闭数码管 { dula=1; P0=0x00; dula=0; } void delay(int ms)//延时n毫秒 { while(ms--) { uchar i; for(i=0;i bit result; lcd_RS=0; lcd_RW=1; lcd_EN=1; delaynop(); result=(bit)(P0&0x80); lcd_EN=0; return result; } void lcd_writezhiling(uchar cmd) //lcd写指令函数 { while(lcd_busy()); lcd_RS=0; lcd_RW=0; lcd_EN=0; _nop_(); _nop_(); P0=cmd; delaynop(); lcd_EN=1; delaynop(); lcd_EN=0; } void lcd_writedate(uchar date) //lcd写数据 { while(lcd_busy()); lcd_RS=1; lcd_RW=0; lcd_EN=0; P0=date; delaynop(); lcd_EN=1; delaynop(); lcd_EN=0; } void lcd_init() //lcd初始化 { lcd_writezhiling(0x38); lcd_writezhiling(0x0c); lcd_writezhiling(0x06); lcd_writezhiling(0x01); lcd_writezhiling(0x80); } //AD数据处理与显示 show_value(uchar ad_date) { dis[2]=ad_date/51; dis[2]=dis[2]+0x30; dis[3]=ad_date%51; dis[3]*=10; dis[1]=dis[3]/51; dis[1]+=0x30; dis[3]%=51; dis[3]*=10; dis[0]=dis[3]/51; dis[0]+=0x30; } //IIC设置 void iic_start() //起始信号 { SCL=1; SDA=1; delaynop(); SDA=0; delaynop(); SCL=0; } void iic_stop() //终止信号 { SCL=1; SDA=0; delaynop(); SDA=1; delaynop(); SDA=0; } void iic_init() //iic 初始化 { SCL=0; iic_stop(); } //从机发送应答信号 void slave_ACK(void) { SDA=0; SCL=1; delaynop(); SCL=0; SDA=1; } //发送非应答信号 void slave_NOACK(void) { SDA=1; SCL=1; delaynop(); SCL=0; SDA=0; } //主机应答检查函数 void check_ACK(void) { SDA=1; SCL=1; F0=0; delaynop(); if(SDA==1) //非应答,标志位置1 F0=1; //F0全局标志位 SCL=0; } //IIC发送单个字节 void IICSendByte(uchar ch) { uchar idata n=8; //向SDA上发送数据字节,8位 while(n--) { if((ch&0x80)==0x80) //发送数据的最高位为1则发送位1 { SDA=1; SCL=1; delaynop(); SCL=0; } else//否则传送位0 { SDA=0; SCL=1; delaynop(); SCL=0; } ch=ch SDA=1; SCL=1; tdata=tdata SystemError=1; return; } IICSendByte(controlbyte&0x77); check_ACK(); if(F0==1) { SystemError=1; return; } IICSendByte(w_data); check_ACK(); if(F0==1) { SystemError=1; return; } iic_stop(); delaynop(); } //连续读入4路通道A/D转化结果到receivebuf(缓冲区) void ADC_PCF8591(uchar controlbyte) { uchar idata receive_da,i=0; iic_start(); IICSendByte(PCF8591_WRITE); check_ACK(); if(F0==1) { SystemError=1; return; } IICSendByte(controlbyte); check_ACK(); if(F0==1) { SystemError=1; return; } iic_start(); IICSendByte(PCF8591_READ); check_ACK(); if(F0==1) { SystemError=1; return; } IICreceiveByte(); //空读一次 slave_ACK(); //收到一字解后发送一个应答位 while(i uchar i,l; delay(10); stopSMG(); DS1302=0; lcd_init(); lcd_writezhiling(0); i=0; while(dis4[i]!='\0') { lcd_writedate(dis4[i]); i++; } lcd_writezhiling(0x80+0x40); i=0; while(dis5[i]!='\0') { lcd_writedate(dis5[i]); i++; } while(1) { iic_init(); ADC_PCF8591(0x04); if(SystemError==1) { iic_init(); ADC_PCF8591(0x04); } for(l=0;l iic_init(); DAC_PCF8591(0x40,receivebuf[0]); } } } } 3、DA模数转换的使用 #include #include #define uchar unsigned char #define uint unsigned int #define WRITE 0x90 #define READ 0x91 sbit fm=P2^3; sbit RST=P2^4; sbit dula=P2^6; sbit wela=P2^7; sbit SDA=P2^0; sbit SCL=P2^1; bit ADflag; void delayms(uchar i) { uint n; for(;i>0;i--) for(n=0;n SDA=1; _nop_(); SCL=1; _nop_(); SDA=0; _nop_(); SCL=0; } void stop(void) //IIC停止信号 { SDA=0; _nop_(); SCL=1; _nop_(); SDA=1; _nop_(); SCL=0; } void ack(void) //应答信号 { SDA=0; _nop_(); SCL=1; _nop_(); SCL=0; _nop_(); } void noack(void) //非应答信号 { SDA=1; _nop_(); SCL=1; _nop_(); SCL=0; _nop_(); } void send(uchar Data)//发送一个字节 { uchar bitcounter=8; uchar temp; do{ temp=Data; SCL=0; _nop_(); if((temp&0x80)==0x80) SDA=1; else SDA=0; SCL=1; temp=Data SCL=0; _nop_(); SCL=1; _nop_(); if(SDA) temp=temp|0x01; else temp=temp&0xfe; if(bitcounter-1) { temp1=temp uchar Data; start(); send(WRITE); ack(); send(0x40|ch1);//写入选择通道1,2,3,4 ack(); start(); send(READ); ack(); Data=read(); SCL=0; noack(); stop(); return Data; } void cmg() { dula=1; P0=0x00; dula=0; wela=1; P0=0x00; wela=0; RST=0; } void main() { uchar num=0; uchar adtemp; timer0_init(); cmg(); // DS1302=0; fm=1; while(1) { for(num=0;num DAC(num); delayms(10); } if(ADflag) { ADflag=0; } } } 串口通讯 串口通信传输方向

1、单工:单工是指数据传输仅能沿一个方向进行。 2、半双工:半双工是指数据传输可以沿两个方向,但需要分时进行。 3、全双工:全双工是指数据可以同时进行双向传输

串口通信奇偶校验

如约定采用奇校验:发送数据时,数据位尾随1位奇校验位,奇校验 位可以取0或1,但要保证,数据中“1”的个数与校验位“1”的个数之和 应为奇数;接收数据时,检查数据中“1”的个数与校验位“1”的个数之 和是否仍应为奇数,如不是,则说明数据在传输过程中出现了差错。 如约定采用偶校验:发送数据时,数据位尾随1位偶校验位,偶校验 位可以取0或1,但要保证,数据中“1”的个数与校验位“1”的个数之和 应为偶数;接收数据时,检查数据中“1”的个数与校验位“1”的个数之 和是否仍应为偶数,如不是,则说明数据在传输过程中出现了差错。

串口通信数据传输率

简称数传率,指单位时间内传输的信息量,可用比特率和波特率来表示。 ⑴、比特率:是数字信号的传输速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数bit/s(bps) 。 ⑵、波特率:波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)。 对于串行通信来说,或者说是对于普通的数字电路来说,都是两相调制,也就是单个调制状态对应1个二进制位,因此: 比特率= 波特率 1bps=1 Baud 计算机中常用的波特率是:110、300、600、1200、2400、4800、9600、 19200、28800、33600,目前最高可达56Kbps. CH340转出的虚拟串口的波特率突破了传统串口的56Kbps限制,最高可达 2Mbps。

串行通信结构框图

串行通信

51串口模块结构图

51串口

单片机串口初始化步骤

1、TX设置为输出、RX设置为输入,也可不设置,采用默认的准双向口模式。 2、设置串口1 的工作模式,SCON 寄存器中的SM0 和SM1 两位决定了串口1 的4 种工作模式。 3、如要串口1 接收,将SCON 寄存器中的REN 位置1 即可。 4、计算BRT的值,并置数; 5、设置独立波特率发生器相关位:BRTx12:=0, S1BRS: =1, SMOD 6、启动独立波特率发生器( BRTR =1); 7、串行口工作在中断方式时,还要设置串口1 的中断优先级 ( PS,PSH ),如果不设置的话,默认是低优先级;打开中断相应的 控制位(ES,EA)。接收完成标志RI,发送完成标志TI,要由软件清0。

单片机串口通信应用实例 1、接收数据 #include #define uchar unsigned char #define uint unsigned int uchar Receive(void) //接收一个字节数据 { uchar dat; while(RI==0);//RI没有被置1,就一直等待单片机数据接收完毕(RI=1) RI=0; dat=SBUF; return dat; } void mian() { TMOD=0x20;//定时器T1工作方式2; SCON=0x50; //串口工作方式1,允许接收(REN=1) PCON=0x00; //波特率9600 TH1=0xfd; TL1=0xfd; TR1=1; REN=1;//允许接收 while(1) { P1=Receive(); } } 2、发送数据 #include #define uchar unsigned char uchar code Tab[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; void Send(uchar dat) { SBUF=dat; while(TI==0); TI=0; } void delay() { uchar m,n; for(m=0;m for(i=0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3