490 likes | 730 Views
第四章 C51 程序设计 -2 实例. 开题报告第 6 , 9 , 10 , 12 还没有发给齐老师,明天下午 5 : 00 之前必须交给齐老师。. 1-wire 总线及应用. 一根数据线。设备(主机或从机)通过一个漏极开路端口,连接至该数据线,这样允许设备在不发送数据时释放数据总线,以便总线被其它设备所使用。 1-wire 总线端口为漏极开路,单总线要求外接一个约 5k 的上拉电阻,这样单总线的闲置状态为高电平。 主机对 1-Wire 总线的基本操作分为复位、读和写三种,其中所有的读写操作均为低位在前,高位在后。 典型的单总线命令序列 第一步 初始化;
E N D
第四章 C51程序设计-2实例 开题报告第6,9,10,12还没有发给齐老师,明天下午5:00之前必须交给齐老师。
1-wire总线及应用 • 一根数据线。设备(主机或从机)通过一个漏极开路端口,连接至该数据线,这样允许设备在不发送数据时释放数据总线,以便总线被其它设备所使用。 1-wire总线端口为漏极开路,单总线要求外接一个约5k 的上拉电阻,这样单总线的闲置状态为高电平。 • 主机对1-Wire总线的基本操作分为复位、读和写三种,其中所有的读写操作均为低位在前,高位在后。 • 典型的单总线命令序列 • 第一步 初始化; • 第二步 ROM 命令 跟随需要交换的数据; • 第三步 功能命令 跟随需要交换的数据;
实例:DS18B20单线温度传感器 • 主要特征 • 全数字温度转换及输出。 • 1-wire总线数据通信。 • 最高12位分辨率,精度可达土0.5摄氏度。 • 12位分辨率,最大工作周期为750毫秒。 • 检测温度范围为-55℃~+125℃。 • 内置EEPROM,限温报警功能。 • 64位光刻ROM,内置产品序列号,方便多机挂接。 • 多样封装形式,适应不同硬件系统。
DS18B20工作原理 • 三种形态的存储器资源 • ROM 只读存储器,用于存放DS18B20ID编码。 • RAM 数据暂存器,用于内部计算和数据存取,数据在掉电后丢失,DS18B20共9个字节RAM,每个字节为8位。 • EEPROM 非易失性记忆体,用于存放长期需要保存的数据,上下限温度报警值和校验数据。 • RAM及EEPROM 结构图:
控制器对18b20操作流程 • 复位:给DS18B20单总线至少480uS的低电平信号。 • 存在脉冲:复位电平结束之后,控制器应该将数据单总线拉高,以便于在15~60uS后接收存在脉冲,存在脉冲为一个60~240uS的低电平信号。 • 控制器发送ROM指令:ROM指令共有5条,每一个工作周期只能发一条,ROM指令分别是读ROM数据、指定匹配芯片、跳跃ROM、芯片搜索、报警芯片搜索。(一般只挂接单个18B20芯片时可以跳过ROM指令) • 控制器发送存储器操作指令:分别是写RAM数据、读RAM数据、将RAM数据复制到EEPROM、温度转换、将EEPROM中的报警值复制到RAM、工作方式切换。 • 执行或数据读写:一个存储器操作指令结束后则将进行指令执行或数据的读写,这个操作要视存储器操作指令而定。
几种时间隙 • DS18B20复位及应答关系 • 写时间隙
读时间隙 注意:必须在读间隙开始的15uS内读取数据位才可以保证通信的正确。 • DS18B20与单片机接法
示例程序: void Init_DS18B20(void) //18b20初始化 { DQ = 1; //DQ复位 _nop_();_nop_();_nop_(); DQ = 0; //拉低DQ delay(200); //约600us DQ = 1; //拉高总线 delay(20); // 75us,30:100us }
uchar Read_byte(void) //读一字节 { uchar i=0; uchar dat = 0; for (i=8;i>0;i--) { DQ = 1; _nop_();_nop_();_nop_();_nop_(); DQ = 0; //从高拉到低,产生读时间隙 _nop_();_nop_(); //至少保持低电平1us _nop_();_nop_();_nop_();_nop_(); dat>>=1; //右移一位 DQ = 1; //15us内停止将DQ拉低,15us内数据有效 _nop_();_nop_(); //稍作延时 _nop_();_nop_(); if(DQ) //若高电平 置1 dat|=0x80; delay(30); //至少保持60us,100us,确保读数据成功 } DQ = 1; //结束拉高 return(dat); }
void Write_byte(uchar dat) //写一个字节 { uchar i=0; for (i=8; i>0; i--) { DQ = 1; _nop_();_nop_(); //稍作延时 _nop_();_nop_();_nop_();_nop_(); DQ = 0; //拉低DQ开始写 _nop_();_nop_(); _nop_();_nop_(); //15us内释放总线 _nop_();_nop_();_nop_();_nop_(); DQ = dat&0x01; //从lsb开始 delay(20); //75us 至少需保持60us写时间隙 dat>>=1; //右移一位 } DQ = 1; //结束拉高 delay(4); }
uint Get_Temperature(void) //读温度函数 { uchar a=0; uchar b=0; uint t=0; Init_DS18B20(); Write_byte(0xCC); //只接一个18b20芯片,skip ROM Write_byte(0x44); //温度转换 delay(200); //延时600us 等待转换完成,一般转换时间为500us Init_DS18B20(); Write_byte(0xCC); //skip ROM Write_byte(0xBE); //从RAM读数据 a=Read_byte(); //先读低位 b=Read_byte(); //再读高位 t=b<<8; t=t|a; //t为最后读得的数据 return (t); }
双向二线制串行通信总线,时钟线SCL, 数据线SDA。 NXP 半导体(原Philips 半导体)于20 多年前发明。 I2C总线是同步串行数据传输总线。 经常使用的带I2C总线通用外围器件有:SRAM、E2PROM、ADC/DAC、RTC、I/O口等。 带I2C总线的外围设备模块有由LED驱动控制器构成的LED显示器,由各种LCD驱动控制器构成的段式、字符点阵、图形点阵液晶显示器等。 I2C总线及应用
I2C 总线的信号线 I2C 总线只需要由两根信号线组成,一根是串行数据线SDA,另一根是串行时钟线SCL,均为为开漏结构,故总线上必须有上拉电阻Rp,通常可选5~10kΩ。
I2C总线时序定义: 起始条件和停止条件(START and STOP conditions) 起始条件:当SCL 处于高电平期间时,SDA 从高电平向低电平跳变时产生起始条件。总线在起始条件产生后便处于忙的状态。起始条件常常简记为S。 停止条件:当SCL 处于高电平期间时,SDA 从低电平向高电平跳变时产生停止条件。总线在停止条件产生后处于空闲状态。停止条件简记为P。
I2C 总线上数据的有效性(Data validity) 数据线SDA 的电平状态必须在时钟线SCL 处于高电平期间保持稳定不变。SDA 的电平状态只有在SCL 处于低电平期间才允许改变。但是在I2C 总线的起始和结束时例外。
从机地址(Slave Address) I2C 总线不需要地址译码器和片选信号。多个具有I2C 总线接口的器件都可以连接到同一条I2C 总线上,它们之间通过器件地址来区分。 主机是主控器件,它不需要器件地址,其它器件都属于从机,要有器件地址。必须保证同一条I2C 总线上所有从机的地址都是唯一。 一般从机地址由7 位地址位和一位读写标志R/W 组成,7 位地址占据高7 位,读写位在最后。读写位是0,表示主机将要向从机写入数据;读写位是1,则表示主机将要从从机读取数据。 从机地址由一个固定和一个可编程的部分构成。从机地址的可编程部分使最大数量的相同器件可以连接到I2C 总线上,器件可编程地址位的数量由管脚决定,如果器件3 个可编程的地址管脚,那么I2C总线上共可以连接8 个相同的器件。
I2C数据传输的方式 以字节(Byte)为单位收发数据。首先传输的是数据的最高位(MSB,第7 位),最后传输的是最低位(LSB,第0 位)。另外,每个字节之后还要跟一个响应位,称为应答。 应答(Acknowledge) 每传输一个字节,要跟一个应答状态位。接收器接收数据的情况可通过应答位来告知发送器。应答位的时钟脉冲由主机产生,而应答位的数据状态则遵循“谁接收谁产生”的原则,即总是由接收器产生应答位。主机向从机发送数据时,应答位由从机产生;主机从从机接收数据时,应答位由主机产生。 I2C总线上第9个脉冲对应应答位,SDA为0 表示接收器应答(ACK),A;为1 则表示非应答(NACK),/A。
基本的数据传输格式 主机向从机发送数据的基本格式 主机从从机接收数据的基本格式 注意:主机向从机发送最后一个字节的数据时,从机可能应答也可能非应答,但不管怎样主机都可以产生停止条件。如果主机在向从机发送数据(甚至包括从机地址在内)时检测到从机非应答,则应当及时停止传输。
数据传输时序图 主机向从机写数据 (24LC01)
EEPROM —— 24LC01B • 主要特征 • 低至2.5V的单电源供电; • 低功耗的CMOS技术; • 128 bytes(128×8)的存储块; • 标准2线串行接口总线 ,I2C总线; • 兼容100 kHz (2.5V) 、 400kHz (5.0V); • 高达8 bytes的页写入缓存; • 2 ms页写入时间周期; • 硬件写保护。
I2C通过IO口模拟 void Start(void) //起始信号 { Sda=1; Scl=1; delay1us(); Sda=0; delay1us(); } void Stop(void) //停止 { Sda=0; Scl=1; delay1us(); Sda=1; delay1us(); }
void Ack(void) //应答 { Sda=0; delay1us(); Scl=1; delay1us(); Scl=0; } void NoAck(void) //不应答 { Sda=1; delay1us(); Scl=1; delay1us(); Scl=0; }
void Send(unsigned char Data) //发送字节 { unsigned char xdata BitCounter=8; unsigned char xdata temp; do { temp=Data; Scl=0; delay1us(); if((temp&0x80)==0x80) Sda=1; else Sda=0; delay1us(); Scl=1; delay1us(); temp=Data<<1; //左移1位 Data=temp; BitCounter- -; }while(BitCounter); Scl=0; }
unsigned char Read(void) //读取字节 { unsigned char xdata temp=0; unsigned char xdata temp1=0; unsigned char xdata BitCounter=8; Sda=1; do{ Scl=0; delay1us(); Scl=1; delay1us(); if(Sda) temp=temp|0x01; else temp=temp&0xfe; if(BitCounter-1) { temp1=temp<<1; temp=temp1; } BitCounter- -; }while(BitCounter); return(temp); }
void WrToROM(unsigned int Data_Write,unsigned char Address) //Data_Write:要写的整数,Address:写入的起始地址 { unsigned char xdata Temp_Data; Start(); Send(0xa0); Ack(); Send(Address); Ack(); Temp_Data=(Data_Write>>8) & 0xff; //先写高8位 Send(Temp_Data); Ack(); Stop(); delay1ms(5); Start(); Send(0xa0); Ack(); Send(Address+1); Ack(); Temp_Data=Data_Write & 0xff; //低8位 Send(Temp_Data); Ack(); Stop(); delay1ms(5); }
unsigned int RdFromROM(unsigned char Address) //读取数据 { unsigned int xdata Temp_Data; Start(); Send(0xa0); Ack(); Send(Address); Ack(); Start(); Send(0xa1); Ack(); Temp_Data=(Read())*256; //高8位 NoAck(); Stop(); delay1ms(5); Start(); Send(0xa0); Ack(); Send(Address+1); Ack(); Start(); Send(0xa1); Ack(); Temp_Data += Read(); //低8位 NoAck(); Stop(); delay1ms(5); return Temp_Data; }
通过单片机自带I2C接口 P89C669自带I2C功能实现对EEPROM单字节的读取与写入,写入单个字节和读取只要分别调用bit ISendbyte_1(uchar sla,uchar suba,uchar c)和bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no)即可,参考资料:P89C669 I2C软件包。 /******************************************************************* 申请总线 功能:进行I2C 总线的初始化包括时钟速率I2C 使能发送起始信号等 *******************************************************************/ void GetBus() { I2CON=0xc4; //use internal SCL generator I2CLL=50; //bits data rate=fosc/(I2CLL+I2CLH) I2CLH=50; I2CON=I2CON|0x20; /*STA=1,申请成为主机起动总线 */ while(SI==0); } /******************************************************************* 发送数据函数 功能:用于向总线发送数据 *******************************************************************/ void SendByte(uchar c) { I2DAT=c; I2CON=0XC4; /*清除SI 位等等 */ while(SI==0); }
/********************************************** *向有子地址器件发送一个字节数据函数 ***********************************************/ bit ISendbyte_1(uchar sla,uchar suba,uchar c) { GetBus(); /*启动总线 */ SendByte(sla); /*发送器件地址 */ if(I2STAT!=0X18) { I2CON=0XD4; return(0); } SendByte(suba); /*发送器件子地址 */ if(I2STAT!=0X28) { I2CON=0XD4; return(0); } SendByte(c); /*发送数据 */ if(I2STAT!=0X28) { I2CON=0XD4; return(0); } I2CON=0XD4; /*结束总线 */ return(1); }
/*******************************************************************/******************************************************************* 向有子地址器件读取多字节数据函数 函数原型: bit ISendStr(uchar sla,uchar suba,ucahr *s,uchar no); 功能: 从启动总线到发送地址子地址,读数据结束总线的全过程,从器件 地址sla 子地址suba 读出的内容放入s 指向的存储区读no 个字节 如果返回1 表示操作成功否则操作有误 ********************************************************************/ bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no) { uchar i; GetBus(); /*启动总线 */ SendByte(sla); /*发送器件地址 */ if(I2STAT!=0X18) { I2CON=0XD4; return(0); } SendByte(suba); /*发送器件子地址 */ if(I2STAT!=0X28) { I2CON=0XD4; return(0); } I2CON=0XE4; /*重新启动总线 */ while(SI==0); SendByte(sla+1); if(I2STAT!=0X40) { I2CON=0XD4; return(0); }
for(i=0;i<no-1;i++) { I2CON=0XC4; /*接收一字节数据并发送应答位*/ while(SI==0); if(I2STAT!=0X50) { I2CON=0XD4; return(0); } *s=I2DAT; /*读取数据 */ s++; } I2CON=0XC0; /*接收最后一字节数据并发送非应答位*/ while(SI==0); *s=I2DAT; I2CON=0XD4; /*结束总线 */ return(1); }
RS232C/RS485 • 电子工业协会(EIA)公布的RS-232C是用得最多的一种串行通信标准,它是从远程通信标准中导出来的,是使用于数据终端设备(DTE)和数据通信设备(DCE)之间的接口。该标准除包括物理指标外,还包括表明按位串行传送时的电气指标。
RS-232C电气特性 在电气性能方面,RS-232C使用负逻辑。逻辑“1”电平是在 -5V~-15V范围内,逻辑“0”电平是在+5V~+15V范围内。 标准要求RS-232C接收器必须能够识别+3V以上的信号作为逻辑“0”,-3V以下的信号作为逻辑“1”,即有2V的噪声容限。 RS-232C的主要电气特性见下表。
RS-232C数据传送格式 RS-232C的数据传送格式是位串行方式,传输数据的格式如下图所示,这是微处理机应用系统中最通用的格式。数据的连续传送由最低有效数字位开始,以奇偶校验位作结束。 RS-485标准 : 由RS-232C的电气特性表可知,若不采用调制解调器,其传输距离很短,且最大数据传输率也受到限制。 因此,EIA又公布了能够适合于远距离传输的RS-485标准。 RS485用差分接收器接收信号电压,差分信号的抗噪声能力强。
当采用+5V电源供电时,RS-485信号定义如下: 若差分电压信号为-2500~-200mV时,为逻辑“0”; 若差分电压信号为+2500~+200mV时,为逻辑“1”;
MAX485主要特征: • 采用单一电源+5 V工作,额定电流为300 μA,采用半双工通讯方式。完成将TTL电平转换为RS-485电平的功能。 • 内部含有一个驱动器和接收器。RO和DI端分别为接收器的输出和驱动器的输入端,与单片机连接时只需分别与单片机的RXD和TXD相连即可;/RE和DE端分别为接收和发送的使能端,当/RE为逻辑0时,器件处于接收状态;当DE为逻辑1时,器件处于发送状态,因为MAX485工作在半双工状态,所以只需用单片机的一个管脚控制这两个引脚即可; • A端和B端分别为接收和发送的差分信号端,当A引脚的电平高于B时,代表发送的数据为1;当A的电平低于B端时,代表发送的数据为0。 • 与单片机连接时,只需要一个信号控制MAX485的接收和发送即可。同时将A和B端之间加匹配电阻,一般可选100Ω的电阻。
void init_uart(void) //串行口初始化 { PCON = 0; // Set PCON register, clear SMOD0 and SMOD1 bits /*initiate uart0*/ T2MOD = 0; // T2OE=0;DCEN=0; S0CON = 0x50; // uart 0 in mode 1 (8 bit), REN=1 RCAP2H= 0xff; //ffb8: 9600 Bds at 11.059MHz RCAP2L= 0xb8; //ffdc: 19200 Bds at 11.059MHz TCLK=1; //T2 overflow as the baud rate generator for uart0 transmit RCLK=1; //T2 overflow as the baud rate generator for uart0 receive EXEN2=0; //ignore events on T2EX C_T2=0; //timer mode TR2=1; //timer2 run ES0R = 1 ; //开放UART0 接收中断 EA = 1; // Enable global interrupt UART1_CONTROL=R1_ENABLE;//enable UART1 receive(in max485) UART0_CONTROL=R0_ENABLE;//enable UART0 receive(in max485) } • 示例程序
void respond_PLC_uart0(void) //单片机与PLC进行485通信 { uchar i=0; if((ID_num%2)==0) { ID_num_send=ID_num/2; } else if((ID_num%2)==1) { ID_num_send=(ID_num+1)/2; } ReCommand[0] = '*'; //设置起始位、从机地址、终止位 ReCommand[1] = ID_num_send; //地址 if(flag_alert_LM==1) { ReCommand[2] = 0x01; //亮度报警信息 } else { ReCommand[2] = 0x0; //正常 }
ReCommand[3]=tm_send[1]; //温度值 ReCommand[4]=tm_send[0]; ReCommand[5]=lumin_buf[2]; ReCommand[6]=lumin_buf[1]; ReCommand[7]=lumin_buf[0]; ReCommand[9] = '#'; //结束位 if((MASTER==0)&&(SPARE==1)) { ReCommand[8]=0x0;//主灯工作 } else if((SPARE==0)&&(MASTER==1)) { ReCommand[8]=0x01;//备灯工作 } ES0R = 0 ; //关闭UART0 接收中断 UART0_CONTROL=T0_ENABLE; //发送使能 for(i=0;i<10;i++) { S0BUF=ReCommand[i]; while(TI_0==0) feed_watchdog(); TI_0=0; } UART0_CONTROL=R0_ENABLE;//接收使能 _nop_();_nop_();_nop_();_nop_(); ES0R = 1 ; //开放UART0 接收中断 }
/******** Uart0 receive interrut****************/ void IntUart0Rx( void ) interrupt 4 { RI_0=0; //清除接收标志 if(S0BUF == '@') { Command[0]=S0BUF; UartCount =0; } else if (S0BUF == '$') { Command[3]=S0BUF; UartCount =0; } else if (UartCount < 3) { UartCount++; Command[UartCount]= S0BUF; } else UartCount =0; }
SPI总线: 由同步串行外设接口SPI构成的串行总线是一种三线同步总线。总线上可以连接多个可以作为主机的微控制器MCU及装有SPI接口的I/O设备如液晶驱动、A/D转换等外设。 SPI是全双工的,即主机在发送的同时也可以在接收数据,传送的速率由主机编程决定;时钟的极性和相位也是可选择的,具体的约定由设计人员根据总线上各设备接口的功能决定。
SPI具有四种工作模式,取决于时钟极性(CPOL)和时钟相位(CPHA),使用的最为广泛的是SPI0和SPI3方式。SPI具有四种工作模式,取决于时钟极性(CPOL)和时钟相位(CPHA),使用的最为广泛的是SPI0和SPI3方式。
数据在时钟信号的上升沿锁存,下降沿变化,高位在前,低位在后数据在时钟信号的上升沿锁存,下降沿变化,高位在前,低位在后
//-----------------------------------------------------------------------------//----------------------------------------------------------------------------- // SPI_defs.h //----------------------------------------------------------------------------- // // This file defines the pins used for the SPI device. // The SPI device is mapped to pins P0.0 - P0.3, but can be modified to map to // any of the available GPIO pins on the device. // #ifndef SPI_DEFS #define SPI_DEFS sbit MOSI = P0^0; // Master Out / Slave In (output) sbit MISO = P0^1; // Master In / Slave Out (input) sbit SCK = P0^2; // Serial Clock (output) sbit NSS = P0^3; // Slave Select (output to chip select) #endif
// SPI_Transfer mode 0 // Simultaneously transmits and receives one byte <SPI_byte> using // the SPI protocol. SCK is idle-low, and bits are latched on SCK rising. char SPI_Transfer (char SPI_byte) { unsigned char SPI_count; // counter for SPI transaction SCK = 0; for (SPI_count = 8; SPI_count > 0; SPI_count--) // single byte SPI loop { MOSI = SPI_byte & 0x80; // put current outgoing bit on MOSI SPI_byte = SPI_byte << 1; // shift next bit into MSB SCK = 0x01; // set SCK high SPI_byte |= MISO; // capture current bit on MISO SCK = 0x00; // set SCK low } return (SPI_byte); } // END SPI_Transfer