IO模拟串口通信
- 格式:doc
- 大小:331.50 KB
- 文档页数:20
pc机中io接口用于连接在计算机系统中,输入/输出(Input/Output,简称I/O)是指计算机与外部设备之间进行数据交换的过程。
而在PC机中,I/O接口就是用于连接计算机主机与外部设备之间的硬件接口。
本文将以PC机中的I/O接口为主题,介绍其作用和常见的接口类型。
一、什么是PC机中的I/O接口在PC机中,I/O接口是计算机主机与外围设备之间进行通信的枢纽。
通过I/O接口,计算机可以将数据发送给外部设备,或者从外部设备接收数据。
它实质上是通过电信号的方式将计算机内部的电信号与外部设备连接起来。
二、PC机中常见的I/O接口类型及其作用1. 串口(Serial Port)串口是一种用于串行通信的接口,它通过一根信号线将数据逐位地传输给外部设备。
串口接口通常用于连接打印机、调制解调器、条码扫描器等设备。
2. 并口(Parallel Port)并口是一种用于并行通信的接口,它能同时传输多个位的数据。
并口接口通常用于连接打印机、扫描仪等需要高速数据传输的设备。
3. USB接口(Universal Serial Bus)USB接口是目前应用最广泛的接口类型之一。
它可以连接多种外部设备,包括鼠标、键盘、摄像头、移动存储设备等。
USB接口的优势在于方便插拔和高速数据传输。
4. 网络接口(Network Interface Controller,NIC)网络接口是用于连接计算机与局域网或互联网的接口。
通过网络接口,计算机可以实现与其他计算机的通信和数据传输,实现Internet上的各种功能。
5. 显卡接口(Graphics Card Interface)显卡接口是用于连接计算机主机与显示器的接口。
通过显卡接口,计算机可以将图形信号发送给显示器,并实现图像的显示。
6. 声卡接口(Sound Card Interface)声卡接口是用于连接计算机主机与音频设备的接口。
通过声卡接口,计算机可以实现音频信号的输入和输出,实现音频的播放和录制功能。
单片机IO口介绍单片机(microcontroller)是一种集成电路芯片,具有运算、存储和控制功能。
它是嵌入式系统中最常用的处理器之一、在单片机中,IO (Input/Output)口是用来进行输入输出操作的接口。
IO口通常包括数字IO口和模拟IO口两种类型。
下面将详细介绍单片机IO口的功能和应用。
1.数字IO口:数字IO口是单片机与外部设备进行数字信号交换的接口。
数字IO口可以进行输入和输出操作,具有以下特点:-输入功能:可以通过读取外部设备的状态或信号,并将其转换为数字信号输入到单片机中进行处理。
例如,传感器的信号输入和按键的输入等。
-输出功能:可以通过将数字信号输出到外部设备,控制其工作状态。
例如,LED的控制、驱动电机或继电器等。
数字IO口通常以引脚(pin)的形式存在于单片机芯片上。
一个引脚包括输入端和输出端,可以根据需要进行配置。
数字IO口操作简单、速度快、精度高,常用于控制和通信等方面。
2.模拟IO口:模拟IO口是单片机与外部设备进行模拟信号交换的接口。
模拟IO口可以进行模拟输入和输出操作,常用于采集和控制模拟信号。
-模拟输入功能:可以从外部信号源中获取模拟信号,并将其转换为数字信号输入到单片机中进行处理。
例如,温度传感器、声音传感器等。
-模拟输出功能:可以将数字信号转换为模拟电压、电流等形式,输出到外部设备中。
例如,通过PWM(脉冲宽度调制)信号控制电机的转速。
模拟IO口通常通过ADC(模数转换器)和DAC(数模转换器)实现。
ADC将模拟信号转换为数字信号,DAC将数字信号转换为模拟信号。
模拟IO口的使用相对复杂,需要进行模数转换和数模转换,但在一些需要对模拟信号进行处理和控制的应用中起到关键作用。
3.应用场景:IO口在单片机系统中广泛应用于各种应用场景。
以下是一些常见的应用场景:-传感器接口:通过IO口连接传感器,读取传感器的输出信号,进行数据采集和处理。
例如温度、湿度、光照等传感器的接口。
论坛新老朋友们。
祝大家新年快乐。
在新的一年开始的时候,给大家一点小小的玩意。
工程师经常碰到需要多个串口通信的时候,而低端单片机大多只有一个串行口,甚至没有串口。
这时候无论是选择高端芯片,还是更改系统设计都是比较麻烦的事。
我把以前搞的用普通I/O口模拟串行口通讯的程序拿出来,供大家参考,希望各位兄弟轻点拍砖。
基本原理:我们模拟的是串行口方式1.就是最普通的方式。
一个起始位、8个数据位、一个停止位。
模拟串行口最关键的就是要计算出每个位的时间。
以波特率9600为例,每秒发9600个位,每个位就是1/9600秒,约104个微秒。
我们需要做一个精确的延时,延时时间+对IO口置位的时间=104微秒。
起始位是低状态,再延时一个位的时间。
停止位是高状态,也是一个位的时间。
数据位是8个位,发送时低位先发出去,接收时先接低位。
了解这些以后,做个IO 模拟串口的程序,就是很容易的事。
我们开始。
先上简单原理图:就一个MAX232芯片,没什么好说的,一看就明白。
使用单片机普通I/O口,232数据输入端使用51单片机P3.2口(外部中断1口,接到普通口上也可以,模拟中断方式的串行口会有用。
呵呵)。
数据输出为P0.4(随便哪个口都行)。
下面这个程序,您只需吧P0.4 和P3.2 当成串口直接使用即可,经过测试完全没有问题. 2、底层函数代码如下:sbit TXD1 = P0^4; //定义模拟输出脚sbit RXD1 = P3^2; //定义模拟输入脚bdata unsigned char SBUF1; //定义一个位操作变量sbit SBUF1_bit0 = SBUF1^0;sbit SBUF1_bit1 = SBUF1^1;sbit SBUF1_bit2 = SBUF1^2;sbit SBUF1_bit3 = SBUF1^3;sbit SBUF1_bit4 = SBUF1^4;sbit SBUF1_bit5 = SBUF1^5;sbit SBUF1_bit6 = SBUF1^6;sbit SBUF1_bit7 = SBUF1^7;void delay_bps() {unsigned char i; for (i = 0; i < 29; i++); _nop_();_nop_();} //波特率9600 模拟一个9600波特率unsigned char getchar2() //模拟接收一个字节数据{while (RXD1);_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();delay_bps();SBUF1_bit0 = RXD1; //0delay_bps();SBUF1_bit1 = RXD1; //1delay_bps();SBUF1_bit2 = RXD1; //2delay_bps();SBUF1_bit3 = RXD1; //3delay_bps();SBUF1_bit4 = RXD1; //4delay_bps();SBUF1_bit5 = RXD1; //5delay_bps();SBUF1_bit6 = RXD1; //6delay_bps();SBUF1_bit7 = RXD1; //7delay_bps();return(SBUF1) ; //返回读取的数据}void putchar2(unsigned char input) //模拟发送一个字节数据{SBUF1 = input;TXD1 = 0; //起始位delay_bps();TXD1 = SBUF1_bit0; //0delay_bps();TXD1 = SBUF1_bit1; //1delay_bps();TXD1 = SBUF1_bit2; //2delay_bps();TXD1 = SBUF1_bit3; //3delay_bps();TXD1 = SBUF1_bit4; //4delay_bps();TXD1 = SBUF1_bit5; //5delay_bps();TXD1 = SBUF1_bit6; //6delay_bps();TXD1 = SBUF1_bit7; //7delay_bps();TXD1 = 1; //停止位delay_bps();}3、实现串行通讯。
IO口工作原理
IO口是Input/Output口的简称,它是计算机与外部设备进行通信的接口。
IO口的工作原理是通过发送和接收电信号来实现数据的输入和输出。
对于输入操作,当外部设备需要将数据输入到计算机时,它会发送一个电信号到计算机的IO口。
计算机通过IO口接收到电信号后,会将其转换成数字信号,然后传递给相应的输入设备驱动程序进行处理和解析。
最终,输入设备驱动程序会将数据传递给操作系统,供应用程序进行处理和使用。
对于输出操作,当计算机需要将数据输出到外部设备时,它会将数字信号传递给相应的输出设备驱动程序。
输出设备驱动程序会将数字信号转换成相应的电信号,并通过IO口发送到外部设备上。
外部设备接收到电信号后,会进行相应的处理,从而实现数据的输出。
IO口的工作原理实质上是通过控制和传递电信号来实现数据输入和输出。
计算机通过IO口与外部设备进行通信,从而实现数据的交互和传输。
这样,用户就可以通过外部设备与计算机进行交互,实现各种功能和操作。
通信的三种基本类型常用的通信从传输方向上可以分为单工通信、半双工通信、全双工通信三类。
单工通信就是指只允许一方向另外一方传送信息,而另一方不能回传信息。
比如电视遥控器、收音机广播等,都是单工通信技术。
半双工通信是指数据可以在双方之间相互传播,但是同一时刻只能其中一方发给另外一方,比如我们的对讲机就是典型的半双工。
全双工通信就发送数据的同时也能够接收数据,两者同步进行,就如同我们的电话一样,我们说话的同时也可以听到对方的声音。
UART 模块介绍IO 口模拟串口通信,让大家了解了串口通信的本质,但是我们的单片机程序却需要不停的检测扫描单片机 IO 口收到的数据,大量占用了单片机的运行时间。
这时候就会有聪明人想了,其实我们并不是很关心通信的过程,我们只需要一个通信的结果,最终得到接收到的数据就行了。
这样我们可以在单片机内部做一个硬件模块,让它自动接收数据,接收完了,通知我们一下就可以了,我们的 51 单片机内部就存在这样一个 UART 模块,要正确使用它,当然还得先把对应的特殊功能寄存器配置好。
51 单片机的 UART 串口的结构由串行口控制寄存器 SCON、发送和接收电路三部分构成,先来了解一下串口控制寄存器 SCON。
如表 11-1 表 11-2 所示。
表 11-1 SCON——串行控制寄存器的位分配(地址 0x98、可位寻址)位7 6 5 4 3 2 1 0符号SM0 SM1 SM2 REN TB8 RB8 TI RI复位值0 0 0 0 0 0 0 0表 11-2 SCON——串行控制寄存器的位描述位符号描述7 SM0 这两位共同决定了串口通信的模式0~模式 3 共 4 种模式。
我们最常用的就是模式1,也就是SM0=0,SM1=1,下边我们重点就讲模式1,其它模式从略。
6 SM15 SM2 多机通信控制位(极少用),模式 1 直接清零。
4 REN 使能串行接收。
由软件置位使能接收,软件清零则禁止接收。
在低端单⽚机之间的单线IO通信利⽤IO模拟串⼝对于接收⽐较复杂,稳定性肯定没有串⼝模块稳定性好,⽽且要占⽤⼀个定时器中断,对于不允许使⽤中断的场合就不能适应,⽐如⾼速的⽆刷控制器,我发明了⼀种⽅案可以解决这个问题,除了传输速度慢以外,有很多优点,因为越慢越稳定,⽬前为单主多从,其很容易扩展为类似can那样的单线多主⽅式,由于个⼈原因没有条件与时间将此⽅案改的完美,现在将其分享给需要的⼈/******************************************************************************************************************************************************************************************************************** File : ucos.h* By : Minglie* Date :**********************************************************************************************************/#ifndef __Ucos_H#define __Ucos_H#include "HT66F018.h"//announce all the head files#include "Main_Constant.h"void os_init();void task0(void);#define OSTCBCur 0#define OSTimeDly(k) {task[OSTCBCur].one.rdy =0; task[OSTCBCur].delay =k-1; }#define OSTimeTick()\{\#define config_max_tasks 1 //最⼤任务个数#define configTICK_RATE_us 800 //800us 配置⼼跳周期#define config_single_wire_task0_mode 0 //单线⼯作模式,0为主机,1为从机#define config_com_task0_lengh 8 //配置通信有效位数,只能取2,4,6,8//引脚配置#define COM _pc0#define COM_C _pcc0//ming_single_wire⽤到的变量#pragma rambank0OS_EXT OSTCB_TypeDef task[config_max_tasks];//task0的变量OS_EXT unsigned char task0_dat_cur;OS_EXT unsigned char task0_wait_com_h_count;OS_EXT unsigned char task0_wait_com_l_count;OS_EXT unsigned char task0_tx_buf;OS_EXT unsigned char task0_tx_buf_temp;OS_EXT unsigned char task0_rx_buf;OS_EXT unsigned char task0_rx_buf_temp;OS_EXT unsigned char task0_level_count;OS_EXT bit task0_bit_no_back;OS_EXT bit task0_bit_level_s;OS_EXT bit task0_bit_com_err;#pragma norambank#endif/********************************************************************************************************** ********************************************************************************************************* * File : ucos.c* By : Minglie* Date :********************************************************************************************************* */#include "Main_Constant.h"#include "ucos.h"void os_init(){//初始化时钟800us/////////////////////////////////////////////////////////////////_tm2c0 = 0b01010000;_tm2c1 = 0b11000001;_tm2dl = 0x00;_tm2dh = 0x00;_tm2al = configTICK_RATE_us & 0xff;_tm2ah = configTICK_RATE_us >> 8;_tm2rp = 0x00;_t2on = 1;task0_bit_level_s = 0;task0_dat_cur = 0;task0_bit_no_back = 0;task[0].step = 0;task0_bit_com_err = 0;task[0].one.rdy = 1; //任务就绪task[0].one.enable = 1; //任务使能task[0].delay = 0;task0_rx_buf = 0;task0_tx_buf = 0;}#if config_single_wire_task0_mode==0void task0(){if (task[OSTCBCur].one.rdy){switch (task[OSTCBCur].step){case0: {//初始化发送,并发送起始位1msCOM_C = 0;COM = 0;task0_tx_buf_temp = task0_tx_buf;task0_dat_cur = 0;OSTimeDly(5);COM_C = 1;OSTimeDly(5);task[OSTCBCur].step = 2;break;}case2: {//发送第0,2,4,6,8(从左数)COM_C = 0;COM = 0;if (task0_tx_buf_temp & 0b10000000){OSTimeDly(10);}else{OSTimeDly(5);}task0_tx_buf_temp <<= 1;task0_dat_cur++;task[OSTCBCur].step = 3;break;}case3: {//发送第1,3,5,7,9(从左数)COM_C = 1;if (task0_tx_buf_temp & 0b10000000){OSTimeDly(10);}else{OSTimeDly(5);}task0_tx_buf_temp <<= 1;task0_dat_cur++;if (task0_dat_cur>config_com_task0_lengh + 1) //task0_dat_cur==10, bit[0:9]发送完{task0_level_count = 0;task0_dat_cur = 0;task[OSTCBCur].step = 4;}else{task[OSTCBCur].step = 2;}break;}case4: { ////////////////////////////////////////开始接收COM_C = 1;if (COM)//等待对⽅发送,对齐{task[OSTCBCur].step = 4;task0_level_count++; //if (task0_level_count>20) //4ms{task0_level_count = 0;task0_wait_com_h_count = 0;task0_wait_com_l_count = 0;task[OSTCBCur].step = 255; //溢出,跳到异常分⽀}}else{task0_bit_level_s = 0;task0_rx_buf_temp = 0;task0_dat_cur = 0;task0_level_count = 0;task[OSTCBCur].step = 5;}break;}case5: { //////////////////////////////////////COM_C = 1;if (task0_bit_level_s == COM){task0_level_count++;if (task0_level_count>20) //4ms溢出{task0_level_count = 0;task0_wait_com_h_count = 0;task0_wait_com_l_count = 0;{if (task0_dat_cur>0) task0_rx_buf_temp <<= 1; //如果已经收到⼀些数据,将得到的数据向左移动task[OSTCBCur].step = 6;}break;}case6: { //////////////////////////////////////if (task0_level_count >= 7) task0_rx_buf_temp++; //超过1.4ms认为是1task0_bit_level_s ^= 1;task0_dat_cur++;if (task0_dat_cur >= config_com_task0_lengh) //task0_dat_cur==8说明[0:7]数据已经接收完毕{task0_level_count = 0;task[OSTCBCur].step = 7;task0_rx_buf = task0_rx_buf_temp;}else{task0_level_count = 0;task[OSTCBCur].step = 5; //接收下⼀位数据}break;}case7: { //////////////////////////////////////COM_C = 1;if (COM == 0) //等待bit[8]{task0_level_count++;if (task0_level_count>20) //4ms{task0_level_count = 0;task0_wait_com_h_count = 0;task0_wait_com_l_count = 0;task[OSTCBCur].step = 255; //溢出,跳到异常分⽀}}else{COM_C = 1;OSTimeDly(25); //延时5ms,准备下⼀次通信task[OSTCBCur].step = 0;task0_bit_no_back = 0;}break;}case255: { ////////////////////////////////////////异常分⽀COM_C = 1;task0_bit_no_back = 1;//主板不回复if (COM == 1){task0_wait_com_l_count = 0;task0_wait_com_h_count++;task[OSTCBCur].step = 255;if (task0_wait_com_h_count>20) //持续⾼4ms,重新发送{task[OSTCBCur].step = 0;task0_wait_com_h_count = 0;}}else{ //总线被拉低task0_wait_com_h_count = 0;task0_wait_com_l_count++;if (task0_wait_com_l_count>80)//16ms{task0_bit_com_err = 1; //总线被拉低task0_wait_com_l_count = 0;}}break;}}}}#elsevoid task0(){case0: {////初始化分⽀COM_C = 1;task0_level_count = 0;task0_wait_com_l_count = 0;task0_wait_com_h_count = 0;task[OSTCBCur].step = 1;break;}case1: { //等待起始位COM_C = 1;if (COM == 1){task[OSTCBCur].step = 1;task0_wait_com_h_count++;if (task0_wait_com_h_count>100)//持续拉⾼200us*100=20ms{task[OSTCBCur].step = 0; //重新开始接收}if (task0_wait_com_l_count >= 2)//检测到⼤于400us以上低电平{task0_level_count = 0;task[OSTCBCur].step = 2;}}else{task0_wait_com_l_count++;task[OSTCBCur].step = 1;}break;}case2: {//开始接收COM_C = 1;if (COM)//等待对⽅发送,对齐{task[OSTCBCur].step = 2;task0_level_count++; //if (task0_level_count>20) //4ms{task0_level_count = 0;task0_wait_com_h_count = 0;task0_wait_com_l_count = 0;task[OSTCBCur].step = 255; //溢出,跳到异常分⽀}}else{task0_level_count = 0;task0_bit_level_s = 0;task0_rx_buf_temp = 0;task0_dat_cur = 0;task[OSTCBCur].step = 3;}break;}case3: {if (task0_bit_level_s == COM){task0_level_count++;if (task0_level_count>20) //4ms溢出{task0_level_count = 0;task0_wait_com_h_count = 0;task0_wait_com_l_count = 0;task[OSTCBCur].step = 255; //溢出,跳到异常分⽀}}else{ //task0_dat_cur>0 ,说明已经得到⾄少1bit数据if (task0_dat_cur>0) task0_rx_buf_temp <<= 1; //将得到的数据向左移动task[OSTCBCur].step = 4;}break;}case4: {if (task0_level_count >= 7) task0_rx_buf_temp++; //超过1.4ms认为是1 task0_bit_level_s ^= 1;task[OSTCBCur].step = 5;task0_rx_buf = task0_rx_buf_temp;}else{task0_level_count = 0;task[OSTCBCur].step = 3; //接收下⼀位数据}break;}case5: {COM_C = 1;if (COM == 0) //等待bit[8]{task0_level_count++;if (task0_level_count>20) //4ms{task0_level_count = 0;task0_wait_com_h_count = 0;task0_wait_com_l_count = 0;task[OSTCBCur].step = 255; //溢出,跳到异常分⽀}}else{COM_C = 1;OSTimeDly(10); //延时2ms,准备发送//初始化发送task0_tx_buf_temp = task0_tx_buf;task0_dat_cur = 0;task[OSTCBCur].step = 6;}break;}case6: {//发送第0,2,4,6,8(从左数)COM_C = 0;COM = 0;if (task0_tx_buf_temp & 0b10000000){OSTimeDly(10);}else{OSTimeDly(5);}task0_tx_buf_temp <<= 1;task0_dat_cur++;task[OSTCBCur].step = 7;break;}case7: {//发送第1,3,5,7,9(从左数)COM_C = 1;if (task0_tx_buf_temp & 0b10000000){OSTimeDly(10);}else{OSTimeDly(5);}task0_tx_buf_temp <<= 1;task0_dat_cur++;if (task0_dat_cur>config_com_task0_lengh + 1) //task0_dat_cur==10, bit[0:9]发送完{task0_level_count = 0;task0_dat_cur = 0;COM_C = 1;OSTimeDly(5);//延时1ms,准备下⼀次接收task[OSTCBCur].step = 0;}else{task[OSTCBCur].step = 6;}break;}case255: { //异常分⽀COM_C = 1;task0_wait_com_h_count++;task[OSTCBCur].step = 255;if (task0_wait_com_h_count>20) //持续⾼4ms,重新接收{task[OSTCBCur].step = 0;task0_wait_com_h_count = 0;}}else{task0_wait_com_h_count = 0;task0_wait_com_l_count++;if (task0_wait_com_l_count>80)//16ms{task0_bit_com_err = 1; //总线被拉低task0_wait_com_l_count = 0;}}break;}}}}#endif对于使⽤只需引⼊这两个⽂件周期性的调⽤宏 OSTimeTick()即可实现双向通信,主机从机是⼀样的代码通过宏config_single_wire_task0_mode 加以区别,这种⽅式对于时间并不需要很精准,⽐如你1ms调⽤⼀次,突然800us调⼀次,或1200us⼀次调⽤⼀次,并不会影响通信的正确率,但是你连续的⼤偏差肯定会出问题,只需保证调⽤周期偏差可以在20%以内即可,对于将其改进为多主异步就不需要周期调⽤了。
/****************************************************作者:温子祺*联系方式:wenziqi@*说明:模拟串口实验***************************************************/传统的8051系列单片机一般都配备一个串口,而STC89C52RC增强型单片机也不例外,只有一个串口可供使用,这样就出问题了,假如当前单片机系统要求二个串口或多个串口进行同时通信,8051系列单片机只有一个串口可供通信就显得十分尴尬,但是在实际的应用中,有两种方法可以选择。
方法1:使用能够支持多串口通信的单片机,不过通过更换其他单片机来代替8051系列单片机,这样就会直接导致成本的增加,优点就是编程简单,而且通信稳定可靠。
方法2:在IO资源比较充足的情况下,可以通过IO来模拟串口的通信,虽然这样会增加编程的难度,模拟串口的波特率会比真正的串口通信低一个层次,但是唯一优点就是成本上得到控制,而且通过不同的IO 组合可以实现更加之多的模拟串口,在实际应用中往往会采用模拟串口的方法来实现多串口通信。
普遍使用串口通信的数据流都是1位起始位、8位数据位、1位停止位的格式的,如表1。
表1要注意的是,起始位作为识别是否有数据到来,停止位标志数据已经发送完毕。
起始位固定值为0,停止位固定值为1,那么为什么起始位要是0,停止位要是1呢?这个很好理解,假设停止位固定值为1,为了更加易识别数据的到来,电平的跳变最为简单也最容易识别,那么当有数据来的时候,只要在规定的时间内检测到发送过来的第一位的电平是否0值,就可以确定是否有数据到来;另外停止位为1的作用就是当没有收发数据之后引脚置为高电平起到抗干扰的作用。
在平时使用红外无线收发数据时,一般都采用模拟串口来实现的,但是有个问题要注意,波特率越高,传输距离越近;波特率越低,传输距离越远。
对于这些通过模拟串口进行数据传输,波特率适宜为1200b/s 来进行数据传输。
单片机多串口扩展07计本三班汪庆0704013005设计要求:选定具体单片机,利用IO口模拟单片机的串口时序,该软串口具有修改波特率、设定串口通信数据格式等功能,对外提供串口电平。
报告要求:选定单片机和所有器件具体型号,报告需有设计过程、原理图、程序流程图和源程序。
功能分析: 针对大多数单片机都只有一个串口的局限,在多数情况下限制它们的应用。
利用单片机串口扩展技术,以MCS51 系列单片机8751 为例进行串行接口扩展,包括扩展两个独立的串口、一点对多点分时串口、单片机与RS232/ RS422/RS485 的串行通信接口。
实际应用证明,设计可靠, 稳定性好。
用多种方法进行串口扩展, 解决了单片机在串行通信系统中的串口局限问题。
关键词: 单片机; 串行接口; 串口扩展; 串行通信引言随着单片机技术的不断发展, 特别是网络技术在测控领域的广泛应用, 由单片机构成的多机网络测控系统已成为单片机技术发展的一个方向。
单片机的应用已不仅仅局限于传统意义上的自动监测或控制[ 1 ],而形成了向以网络为核心的分布式多点系统发展的趋势[ 2 ] 。
大多数单片机都只有一个串行接口, 在多数情况下限制了这些单片机的进一步应用。
要实现单片机在应用系统中的有效通信, 就必须利用单片机的串口扩展技术对单片机进行串口扩展。
单片机串口扩展是根据应用系统设计的需要, 把一个串口扩展为多个同类型的串口或一个串口扩展为多个不同类型( RS232/ RS422/ RS458) 的串口,或扩展两个独立的串口, 以便与不同接口的计算机或设备进行串行通信。
1 单片机串口扩展的硬件总体设计单片机与PC 机或外设的串行通信一般采用RS232/RS422/ RS485 总线标准接口[ 3 ] 。
为保证通信可靠, 在选择接口时必须注意通信的速率、通信距离、抗干扰能力、电平匹配和通信方式[ 4 ] 。
本文为了解决在单片机串行通信时遇到的串口问题, 以MCS51 系列单片机8751为例, 进行串口扩展, 其串口扩展的逻辑框图如图1 所示, 包括通过通信接口芯片8251 再扩展一个独立串口,通过16 ×1 的多路切换器CD4067 实现一点对多点分时串口通信, 以及通过电平转换器MAX232 , MAX488 ,MAX485 实现单片机与不同类型接口RS232/ RS422/RS458 的计算机或设备的串行通信。
单片机IO口模拟串口实现数据通信1设计任务与要求本设计为单片机IO口模拟串口实现数据通信,它可以用单片机的IO口实现单片机RX和TX的功能。
具体要求如下:●用单片机的P3.4和P3.5分别模拟RX和TX的串行通信功能,能够接收和发送数据。
●通过PC机的键盘输入字符,并传送给单片机,由单片机接收后,发达给PC机,由PC机加以显示。
●单片机接收由键盘输入的数据后,如果是数字,则由数码管显示,并由LED灯表示其ASCII码,如果是其他字符,则由仅由LED灯显示其ASCII码。
2总体方案设计2.1串行通信的方式设计本设计要求用单片机的IO口来模拟串口的串行通信,因此有必要先简要介绍一下单片机的IO和通信的基本原理与串行口P3.0和P3.1。
2.1.1并行I/O口MCS-51单片机共有4个双向的8位并行I/O端口(Port),分别记作P0-P3,共有32根口线,各口的每一位均由锁存器、输出驱动器和输入缓冲器所组成。
实际上P0-P3已被归入特殊功能寄存器之列。
这四个口除了按字节寻址以外,还可以按位寻址。
由于它们在结构上有一些差异,故各口的性质和功能有一些差异。
P0口是双向8位三态I/O口,此口为地址总线(低8位)及数据总线分时复用口,可驱动8个LS型TTL负载。
P1口是8位准双向I/O口,可驱动4个LS 型负载。
P2口是8位准双向I/O口,与地址总线(高8位)复用,可驱动4个LS型TTL负载。
P3口是8位准双向I/O口,是双功能复用口,可驱动4个LS 型TTL负载。
P1口、P2口、P3口各I/O口线片内均有固定的上拉电阻,当这3个准双向I/O口做输入口使用时,要向该口先写“1”,另外准双向I/O口无高阻的“浮空”状态,故称为双向三态I/O 口。
2.1.2通信的基本原理串行通信只用一位数据线传送数据的位信号,即使加上几条通信联络控制线,也用不了很多电缆线。
因此串行通信适合远距离数据传送。
,如大型主机与其远程终端之间、处于两地的计算机之间采用串行通信就非常经济。
当然串行通信要求有转换数据格式、时间控制等逻辑电路,这些电路目前已被集成在大规模集成电路(称为可编程串行通信控制器),使用很方便。
通信方式有两种:并行通信和串行通信。
通常根据传送的的距离决定采用哪种通信方式。
例如,在IBM—PC机与外部设备(如打印机等)通信时,距离小于30m,则可采用并行通信方式,当距离大于30m时,则要采用串行通信方式。
89C51单片机具有并行和串行两种基本通信方式。
并行通信是指数据的各位同时进行传送(发送或接收)的通信方式。
其优点是传送速度高;缺点是数据有多少位,就需要多少根传送线。
例如,89C51单片机与打印机之间的数据传送就属于并行通信。
图1所示为89C51单片机与外设之间8位数据并行通信的连接方法。
并行通信在位数多、传送距离又远时就不太合适了。
图1 两种通信方式连接串行通信指数据是一位一位按顺序传送的通信方式。
它的突出优点是只需一对传输线(利用电话线就可以作为传输线),这样大大降低了传送成本,特别适用于远距离通信;其缺点是传送速度较低。
假设并行传送N位数据所需时间为T,那么串行传送的时间至少为NT,实际上问题总是大于NT的,图7—1所示为串行通信方式的连接方法。
串行通信的传送方式通常有3种:单向(或)单工配置,只允许数据向一个方向传送;半双向(或半双工)配置,允许数据向两个方向中的任一方向传送,但每次只能有一个站点发送;全双向(或全工)配置,允许同时双向传送数据,因此,全双工配置是一对单向配置,它要求两端的通信设备都有完整和独立的发送和接收能力。
串行通信有两种基本的通信方式:异步通信和同步通信。
异步通信在异步通信中,数据是一帧一帧(包括一个字符代码或一字节数据)传送的,第一帧的数据格式如图2所示。
在帧格式中,一个字符由4部分组成:起始位、数据位、奇偶校验位和停止位。
首先是一个起始位(0),然后是5—8位数据(规定低位在前,高位在后),接下来是奇偶校验位(可省略),最后是停止位(1)。
起始位(0)信号只占一位,用来通知接收设备一个待接收的字符开始到达。
线路上在不传送字符时应保持为1。
接收端不断检测线路的状态,若连续为1以后又测到一个0,就知道发来一个新字符,应马上准备接收。
字符的起始位还被用作同步接收端的时钟,以保证以后的接收能正确进行。
起始位后面紧接着是数据位,它可以 5位(D0—D4)、6位、7位或8位(D0—D7)。
奇偶校验(D8)只占一位,但在字符中也可以规定不用奇偶校验位,则这一位就可以省去。
也可用这一位(1/0)来确定这一帧中的字符所代表信息的性质(地址/数据等)。
停止位用来表征字符的结束,它一定是高电位(逻辑1)。
停止位可以是1位、1.5位或2位。
接收端收到停止位后,知道上一字符已传送完毕,同时也为接收下一个字符作好准备—只发再接收到0,就是新字符的起始位。
若停止位以后不是紧接着传送下一个字符,,则使线路电平保持为高电平(逻辑1)。
图7—3(a)表示一个字符紧接一个字符传送的情况,上一个字符的停止位和下一个字符的起始位是紧邻的;图7—3(b)则是两个字符间有空闲位的情况,空闲位为期不远,线路处于等待状态。
存在空闲位正是异步通信的特征之一。
例如规定用ASCII编码,字符为7位,加1个奇偶校验位、1个起始位、1个停止位,则一帧共10位。
同步通信同步通信中,在数据开始传送前用同步字符来指示(常约定1—2个),并由时钟来实现发送端和接收端同步,即检测到规定的同步字符后,下面就连续按顺序传送数据,直到通信告一段落。
同步传送时,字符与字符之间没有间隙,也不用起始位和停止位,仅在数据块开始时用同步字符SYNC来指示。
波特率波特率,即数据传送速率,表示每秒钟传送二进制代码的位数,它的单位是b/s。
波特率对于CPU与外界的通信是很重要的。
假设数据传送速率是120字符/s,而每个字符格式包含1个代码位(1个起始位、1个终位、8个数据位)。
这时,传送的波特率为:10b/字符×120字符/s=1200b/s每一位代码的传送时间Td为波特率的倒数。
Td=1b/(1200bs-1)=0.833ms异步通信的传送速率在50b/s--19200b/s之间,常用于计算机到终端机和打印机之间的通信、直通电报以及无线电通信的数据发送等。
图2 异步通信的一般数据格式串行通信协议通信协议是对数据传送方式的规定,包括数据格式定义和数据位定义等。
通信双方必须遵守统一的通信协议。
串行通信协议包括同步协议和异步协议两种。
在此只讨论异步串行通信协议和异步串性协议规定的字符数据的传送格式。
(1)起始位通信线上没有数据被传送时处于逻辑1状态。
当发送设备要发送一个字符数据时,首先发出一个逻辑0信号,这个逻辑低电平就是起始位。
起始位通过通信线传向接收设备,接收设备检测到这个逻辑低电平后,就开始准备接收数据位信号。
起始位所起的作用就是设备同步,通信双方必须在传送数据位前协调同步。
(2)数据位当接收设备收到起始位后,紧接着就会收到数据位。
数据位的个数可以是5、6、7或8。
IBM-PC中经常采用7位或8位数据传送,89C51串行口采用8位或9位数据传送。
这些数据位被接收到移位寄存器中,构成传送数据字符。
在字符数据传送过程中,数据位从最低有效位开始发送,依次顺序在接收设备中被转换为并行数据。
(3)奇偶校验位数据位发送完之后,可以发送奇偶校验位。
奇偶校验用于有限差错检测,通信双方需约定已知的奇偶校验方式。
如果选择偶校验,那么组成数据位和奇偶位的逻辑1的个数必须是偶数;如果选择奇校验,那么逻辑1的个数必须是奇数。
(4)停止位约定在奇偶位或数据位(当无奇偶校验时)之后发送的是停止位。
停止位是一个字符数据的结束标志,可以是1位,1.5位或2位的高电平。
接收设备收到停止位之后,通信线路上便又恢复逻辑1状态,直至下一个字符数据的起始位到来。
(5)波特率设置通信线上传送的所有位信号都保持一致的信号持续时间,每一位的信号持续时间都由数据传送速度确定,而传送速度是以每秒多少个二进制位来衡量的,这个速度叫波特率。
如果数据以300个二进制位每秒在通信线上传送,那么传送速度为300波特,通常记为300b/s。
2.1.3 89C51的串行口89C51单片机除具有4个8位并行口外,还具有串行接口。
此串行接口是一个全双工串行通信接口,即能同时进行串行发送和接收数据。
它可以作UATR(通用异步接收和发送器)用,也可以作同步移位寄存器用。
使用串行接口可以实现89C51单片机系统之间点对点的单机通信和89C51与系统机(如IBM-PC机等)的单机或多机通信。
通信和89C51与系统机(如IBM-PC机等)的单机或多机通信。
图3 串行口内部结构示意图结构89C51通过引脚RXD(P3.0,串行数据接收端)和引脚TXD(P3.1,串行数据发送端)与外界进行通信。
其内部结构简化示意图如图3所示。
图3中有两个物理独立的接收、发送缓冲器SBUF,它们占用同一低值99H,可同时发送、接收数据。
发送缓冲器只能写入,不能读出;接收缓冲器只能读出,不能写入。
串行发送与接收的速率与移位时钟同步。
89C51用定时器T1作为串行通信的波特率发生器,T1溢出率经2分频(或不分频)后又经16分频作为串行发送或接收的移位脉冲。
移位脉冲的速率即是波特率。
从图中可看出,接收器是双缓冲结构,在前一个字节被从接收缓冲器SBUF读出之前,第二个字节即开始被接收(串行输入至移位寄存器),但是,在第二个字节接收完毕而前一个字节CPU未读取时,会丢失前一个字节。
串行口的发送和接收都是以特殊功能寄存器SBUF的名义进行读或写的。
当向SBUF发“写”命令时(执行“MOV SBUF,A”指令),即是向发送缓冲器SBUF 装载并开始由TXD引脚向外发送一帧数据,发送完便使发送中断标志位TI=1。
在满足串行口接收中断标志位RI(SCON.0)=0的条件下,置允许接收位REN (SCON.4)=1就会接收一帧数据进入移位寄存器,并装载到接收SBUF中,同时使RI=1。
当发读SBUF命令时(执行“MOV A,SBUF”命令),便由接收缓冲器(SBUF)取出信息通过89C51内部总线送CPU。
对于发送缓冲器,因为发送时CPU是主动的,不会产生重叠错误,一般不需要用双缓冲器结构来保持最大传送速率。
2、串行口控制字及控制寄存器89C51串行口是可编程接口,对它初始化编程只用两个控制字分别写入特殊功能寄存器SCON(98H)和电源控制寄存器PCON(87H)中即可。
2.1.4 用IO口模拟串口通信IO口没有89C51的串口结构,因此IO不能自动发送数据和接收数据,也没有发送中断标志和接收中断标志。
而89C51串口还有T1计时器的参与,用来产生波特率。