PIC 单片机软件异步串行口实现技巧

发布者:乐观向前最新更新时间:2017-12-07 来源: eefocus关键字:PIC  单片机  异步串行口 手机看文章 扫描二维码
随时随地手机看文章

在用 单片机 开发各种嵌入式应用系统时,异步串行通信是经常要用到的一种通信模式,很多应用中还要求实现多路异步串行通信。大家平时熟悉的各种厂家的单片机,绝大部分片上只提供一个硬件UART模块,利用它可以方便实现一路串行通讯。PIC系列单片机也不例外,在其丰富的产品家族成员中,除高端系列(PIC17/18)一些型号片上带有两路硬件UART模块外,其它大部分型号片上只有一路UART,一些低端廉价的PIC单片机甚至还不带硬件 UART。为了提高系统的性能价格比,就要求设计工程师用软件增加实现一路或多路异步串行通信。很多工程师对用软件实现的UART在可靠性和效率方面持怀疑态度,其实关键问题是看软件采用何种方式来实现可靠的UART功能。


  在讨论具体实现方式前,我们先来简单回顾一下异步串行通信的格式定义。发送一个完整的字节信息,必须有“起始位”、“若干数据位”、“奇偶校验位”和“停止位”;必须定义每位信息的时间宽度——每秒发送的信息位个数,即为“波特率”。 单片机 系统中常用的波特率从300~19 200 b/s。当波特率为1200b/s时,每个信息位的时间宽度为 1/1200≈833μs;无数据通信时,数据线空闲状态应该是高电平,“起始位”为低电平,数据位低位先发且后跟奇偶校验位(若有),“停止位”为高电平,如图1所示。

 
图1

  按图1最基本的异步串行通信时序,软件实现UART在不同架构的 单片机 上有多种方法。其中数据接收是关键,因异步通信没有可参照的时钟信号,发送方随时都可能发送数据,任何时刻串行数据到来时,系统都应该及时准确地接收。比较而言,本机发送串行数据相对容易,只要对发送出去的电平做持续时间的定时即可。按不同的接收技巧并针对PIC单片机的特点,这里介绍两种常用且十分可靠的方法。

1  三倍速采样法

  三倍速采样法顾名思义就是以三倍于波特率的频率对接收引脚Rx进行采样,保证检测到“起始位”,又可以调整采样的时间间隔;将有效数据位的采样点控制在码元的中间1/3处,最大限度地减少误码,提高接收的准确性。我们把图1的起始位和部分数据位放大,如图2所示,把每个信息位分成三等份,每等份的时间宽度设为ts,以方便分析。

 
图2

  以三倍频对信息位进行采样时,每个信息位都将可能被采样到三次。当处于空闲状态并检测起始位时,最早检测到起始位低电平的时刻必将落在S0阴影区,虽然每次具体的采样点会在此S0阴影区随机变化。检测到起始位低电平后,间隔4×ts时间,正好是第一位数据位的中间1/3处(图2中Ds阴影区)。此后的数据位、校验位和停止位的采样间隔都是3×ts,所有采样点均落在码元的中间1/3处,采样数据最可靠。

  PIC 单片机 采用此法实现软件UART时,硬件上只要任意定义两个I/O引脚,分别初始化成输入(串行数据接收)和输出(串行数据发送)即可;软件上只要实现定时采样,定时时间间隔在中档以上有中断机制的单片机上可以用不同的定时器(TMR0、TMR1、TMR2等)通过定时中断实现,在低档无中断的PIC单片机上可以控制每次主循环所耗的时间来实现。对于1200 b/s波特率,码元宽度为833μs,采样时间间隔即为278μs。整个串行接收或发送是一个过程控制问题,用状态机方式实现最为高效简易。图3给出了串行接收的参考状态机转移过程。

 
图3

  本刊网络补充版中,介绍了简单的C语言参考源程序。此段程序实现1200b/s全双工串行通信,1位起始位,8位数据位,无校验位,1位停止位,没有帧错误等判别。编译环境为HITECH-PICC编译器V8.00PL4或更高版。

  在网络补充版的程序中,关键部分是TMR0的中断服务。TMR0每隔278μs左右中断一次,TMR0的中断响应即为软件UART接收和发送全双工通信过程的实现。通过Hitech-PICC高效的代码编译后,约有150条单字指令代码,整个中断服务平均用约35个指令周期,即实现一路软件 UART在4 MHz工作频率下占用MCU约12%的运行带宽。理论上,只要保证MCU留有足够的运行带宽给其它任务,在此中断服务程序内把接收和发送的代码再复制一份或多份(数据结构独立),即可实现多路软件UART。当然,如果每路的波特率不同,采样频率必须是最高波特率的三倍。不同波特率的采样点间隔独立调整。

  此法最大的好处是软硬件配置极其灵活:接收发送的引脚可以任意定义;采样定时可以用不同的定时器实现;利用同一个定时采样可以方便地实现多路软件UART等。缺点是:不管有无数据通信,始终占用MCU运行带宽;串行通信的波特率不能太高,4 MHz工作的PIC 单片机 一般能实现2400bps的全双工通信。当然,可以通过提高MCU的振荡频率来实现高波特率通信,当PIC单片机工作在20 MHz时,实现9600b/s绰绰有余。

2  起始位中断捕捉、定时采样法

  实现此法的硬件条件是PIC 单片机 有外部脉冲下降沿中断触发功能,在中档以上PIC单片机中有RB0/INT外部中断脚,CCP1/CCP2脉冲沿捕捉脚,PORTB的第4/5/6/7电平变化中断脚等都可以满足。另外需配备一个定时器,以定时中断方式对接收码元正确采样,或发送串行数据流。其关键的异步接收工作原理简介如图4所示。

 
图4

  设串行数据位宽度为td。起始位到来时刻(图4 A点)的下降沿触发一个中断并立即响应该中断。在此中断服务中立即关闭本中断使能位(后续的数据流变化无需触发中断),开启定时器,使其在 1.5td后产生定时中断,用于采样第一个数据位(确保S0采样点落在数据位的中心位置处);在处理下降沿中断服务的最后,再检测接收端是否还是0电平,以区分窄脉冲干扰。在S0点采样到第一个数据位后的所有采样间隔都是1td,直到收到停止位后,关闭定时器中断,重新开放下降沿捕捉中断,准备接收下一个字节。

  异步数据接收和发送的状态机控制流程,除了起始位判断和定时时间参数设置与前述方式不同外,其它几乎一样,此处不再重复。

  此法的好处是可以实现较高的通信波特率。对于通信不是很频繁的系统,此软件UART几乎不耗MCU运行带宽,9600b/s接收或发送在4 MHz运行的PIC 单片机 上即可轻松实现;另外,由于下降沿中断可以唤醒处于睡眠的单片机,故极易实现通信唤醒的功能。缺点是不能全双工通信(除非另外单独用一个定时器实现发送定时),异步接收的引脚必须有下降沿触发中断的能力。

  上面介绍的两种方法在实际产品设计中都得到了很好的验证,最典型的是红外线自动抄表系统。该系统要求收发均为38 kHz红外调制,串行数据1 200bps半双工通讯。用软件实现此UART,并充分利用PIC 单片机 CCP模块的脉宽调制PWM输出38 kHz载波时,在单片机外除了一个一体化红外接收头和一个红外发射二极管,无需其它任何外围器件,即可完成所有设计要求,最大程度地减化了硬件设计,降低了成本,提高了系统的可靠性和性能价格比。

  以上的侧重点是基本原理的介绍,希望对大家有所帮助。在接收数据的可靠性处理方面没有太多涉及。有兴趣者可以在采样时刻到来时对数据做多次采样,以消除干扰误码;或有其它处理技巧,欢迎和笔者作进一步交流。

简单的C语言参源程序如下:


#i nclude    //PIC 单片机 通用头文件,实际型号为16F84

__CONFIG(XT | PROTECT | PWRTEN | WDTEN);//程序中设定配置信息

//===========================
//定义软件UART发送/接收引脚
//===========================
#define      RX_PIN  RB0  //串行接收脚
#define      TX_PIN  RB1  //串行发送脚

//===========================
//定义软件UART状态机控制字
//===========================
#define   RS_IDLE        0 //空闲
#define   RS_DATA_BIT    1 //数据位
#define   RS_STOP_BIT    2 //停止位
#define   RS_STOP_END    3 //停止位结束

//===========================
//定义软件UART采样频率
//===========================
#define   OSC_FREQ       4000   // 单片机 工作频率(单位:KHz)
#define   BAUDRATE       1200 //通讯波特率
#define   TMR0PRE        2 //TMR0预分频比1:2
#define   TMR0CONST      117    //256 - OSC_FREQ*1000/TMR0PRE/4/(BAUDRATE*3)


//===================================================================
//定义函数类型
void UART_Out(void);
void UART_In(void);

//===================================================================
//定义位变量
bit rsTxBusy; //串行发送忙标志

//定义串行发送的数据结构
struct {
   unsigned char state;  //发送状态机控制单元
   unsigned char sliceCount; //波特率控制
   unsigned char shiftBuff; //字节数据发送移位寄存器
   unsigned char shiftCount; //字节数据发送移位计数器
} rsTx;

//定义串行接收的数据结构
struct {
   unsigned char state;  //接收状态机控制单元
   unsigned char sliceCount; //波特率(采样点)控制
   unsigned char shiftBuff; //字节数据接收移位寄存器
   unsigned char shiftCount; //字节数据接收移位计数器
   unsigned char dataBuff[8]; //接收数据FIFO缓冲队列
   unsigned char putPtr, getPtr;//FIFO队列存放/读取指针
} rsRx;

//用于串行发送的变量定义
unsigned char outBuff[10]; //发送队列
unsigned char outPtr,   //发送队列指针
              outTotal,  //发送的字节总数
              chkSum;  //发送的校验码


//=====================================================================
//主程序
//=====================================================================
void main(void)
{
  PORTA  = 0;
  PORTB  = 0;
  
  TRISB  =  0b01; //输入输出定义

  OPTION = 0b10000000;  //TMR0选择内部指令周期计数
    //TMR0预分频 1:2

  rsRx.state  = RS_IDLE; //初始化接收状态
  rsTxBusy    = 0;  //发送空闲
  
  INTCON   = 0b00100000; //T0IE使能
  GIE = 1;   //打开中断

  while(1) {   //程序主循环
     asm("clrwdt");  //清看门狗
     UART_In();   //接收串行数据
     UART_Out();  //发送串行数据
  }
}

//=====================================================================
//查询在接收FIFO队列中是否有新数据到
//然后解读数据
//=====================================================================
void UART_In(void)
{
 unsigned char data1;

  if (rsRx.putPtr==rsRx.getPtr) 
     return; //如果读取和存放的指针相同,则队列为空

  data1 = rsRx.dataBuff[rsRx.getPtr]; //读取1个数据字节
  rsRx.getPtr++;   //调整读取指针到下一位置
  rsRx.getPtr &= 0x07;       //考虑环形队列回绕

  //此处为数据解读分析,略
}

//=====================================================================
//软件UART发送数据
//数据在outBuff中,outTotal为总字节数
//=====================================================================
void UART_Out(void)
{
 if (rsTxBusy==1) 
    return;     //正处于移位发送忙

 //可以发送新数据
 if (outTotal) {   //如果有字节要发送
    rsTx.shiftBuff = outBuff[outPtr++]; //取字节到发送移位寄存器
    rsTxBusy = 1;   //置发送忙标志,启动发送
    outTotal--;    //字节计数器减1
 }

}

//===================================================================
//中断服务程序
//===================================================================
void interrupt isr(void)
{
  //利用TMR0 定时中断实现全双工软件UART
  if (T0IE && T0IF) {
     T0IF = 0;  //清TMR0中断标志

     //实现串行接收 RX 状态机控制
     switch (rsRx.state) {  //判当前接收状态
 case RS_IDLE:
 //当前状态为"空闲", 唯一要做的就是判"起始位"出现
    if (RX_PIN==0) {  //如果接收到低电平
       rsRx.sliceCount = 4; //准备4*Ts时间间隔
       rsRx.shiftCount = 8; //总共接收8位数据位
      //改变此数值可以实现任意位数的数据接收
       rsRx.state = RS_DATA_BIT; //切换到数据位接收状态
    }
    break;
 case RS_DATA_BIT: 
 //当前状态为"数据接收"
    if (--rsRx.sliceCount==0) {  //等采样时间到
       rsRx.shiftBuff >>= 1;  //接收移位寄存器右移1位
       if (RX_PIN) rsRx.shiftBuff|=0x80; //保存最新收到的数据位
       rsRx.sliceCount = 3;  //下次采样间隔为3*Ts
       if (--rsRx.shiftCount==0) { //已经收到8位数据位?
   //保存数据字节到FIFO缓冲队列
   rsRx.dataBuff[rsRx.putPtr] = rsRx.shiftBuff;
   //队列存放指针调整,最多8个字节缓冲
   rsRx.putPtr = (rsRx.putPtr+1) & 0x07;
   //转去下个状态,判停止位
   rsRx.state = RS_STOP_BIT;
       }
    }
    break;
 case RS_STOP_BIT:
 //当前状态为停止位判别(此程序没有判别)
    if (--rsRx.sliceCount==0) { //等采样时间到
       //此处可以判RX_PIN是否为1
       rsRx.state = RS_IDLE; //复位接收过程
    }
    break;
 default:
 //异常处理
    rsRx.state = RS_IDLE; //复位接收过程
     }

     //实现串行发送 TX 状态机控制
     switch (rsTx.state) {  //判当前发送状态
 case RS_IDLE:   //发送起始位
    if (rsTxBusy) {  //如果发送启动
       TX_PIN = 0;  //发出起始位低电平
       rsTx.sliceCount = 3; //持续时间3*Ts
       rsTx.shiftCount = 8; //数据位数为8位
       rsTx.state = RS_DATA_BIT; //转去下一状态
    } else TX_PIN = 1;  //如果没有数据发送则保证数据线为空闲
    break;
 case RS_DATA_BIT:  //发送8位数据位
    if (--rsTx.sliceCount==0) { //码元宽度定时到
       if (rsTx.shiftBuff & 0x01)//看数据位是0还是1 
          TX_PIN = 1;  //发送1
       else 
          TX_PIN = 0;  //发送0
       rsTx.shiftBuff >>= 1; //准备下次数据位发送
       rsTx.sliceCount = 3; //数据位宽度为3*Ts
       if (--rsTx.shiftCount==0) {
   //8位数据位发送结束,转去发送停止位
   rsTx.state = RS_STOP_BIT;
       }
    }
    break;
 case RS_STOP_BIT:  //发送1位停止位
    if (--rsTx.sliceCount==0) { //等数据位发送结束
       TX_PIN = 1;  //发送停止位高电平
       rsTx.sliceCount = 9; //持续宽度9*Ts 
//额外考虑字节连续发送的时间间隔
       rsTx.state = RS_STOP_END; //转停止位宽度延时
    }
    break;
 case RS_STOP_END:  //等待停止位时间宽度结束
    if (--rsTx.sliceCount==0) { //如果停止位结束时间到
       rsTxBusy = 0;  //一个字节发送过程结束,清发送忙标志
       rsTx.state = RS_IDLE; //复位发送过程
    }
    break;
 default:
 //异常处理
    rsTx.state = RS_IDLE; //复位发送过程
     }

     TMR0 += TMR0CONST;   //重载TMR0,实现下次定时中断
  }
}


关键字:PIC  单片机  异步串行口 引用地址:PIC 单片机软件异步串行口实现技巧

上一篇:用PIC16F877的c语言写的一个时闹钟程序
下一篇:pic单片机p18f458上实现发送

推荐阅读最新更新时间:2024-03-16 15:48

C8051F单片机端口配置
前言 最近项目上使用C8051单片机,我也是首次用这款单片机,所以项目开发过程中还是或多或少的遇到了一些问题,在此做一个总结,方便以后再次使用,避免相同问题重复犯错。 一、涉及的寄存器 PxMDIN:端口输入方式寄存器,有模拟输入和非模拟输入两种方式 PxMDOUT:端口输出方式寄存器,开漏输出和推挽输出两种方式 PxSKIP:端口跳过寄存器 Px:端口寄存器 二、实例 1.配置端口输入 //P0.X(1 3 5 7)为数字输入,开漏输出,普通IO功能 P0MDIN |= (1 1) | (1 3) | (1 5) | (1 7); P0MDOUT &= ~((1 1) | (1 3) | (1 5)
[单片机]
C8051F<font color='red'>单片机</font>端口配置
基于单片机和无线加密技术实现汽车中央门锁系统的设计
汽车门锁有开锁、闭锁两种状态,闭锁时通过内外把手无法打开车门。中央门锁控制装置:是控制门锁状态的电气设备,在汽车电器中属于安全、舒适系统。一般工作原理可用图1来说明:当旋转车钥匙或拉动门提会带动锁止机构运动,带动状态开关K,和K2动作,电容C1(或C2)放电,继电器J1(或J2)吸合,执行电动机M1(或M2)通电带动锁止机构动作。放完电后继电器释放,电动机停止,闭锁过程自动完成。将汽车所有车门(包括行李厢)的执行电动机连在一起,同时动作,以实现门锁集中控制,使用很方便。图1为原理性电路,实际电路和执行装置多种多样,但原理大同小异,其目的都为实现门锁的集中控制。随着技术发展和汽车舒适性需求提高,以手动控制电路为基础,以单片机控制技术
[单片机]
基于<font color='red'>单片机</font>和无线加密技术实现汽车中央门锁系统的设计
采用单片机C8051F047和H桥组件LMD18200T实现巡线机器人控制系统设计
1.引言 高压输及杆塔附件长期暴露在野外,因受到持续的机械张力、电气闪络、材料老化的影响而产生断股、磨损、腐蚀等损伤,如不及时修复更换,原本微小的和缺陷就可能扩大,最终导致严重事故。因此,电力公司需要定期对线路设备进行巡检,及时发现早期损伤和缺陷并加以评估,根据评估结果安排必要的维护和修复,从而确保供电的安全可靠性。传统的人工巡检方法不仅工作量大而且条件艰苦,特别是对于山区和大江大河等的输电线路巡检存在很大困难,甚至一些巡检项目靠常规方法都难以完成。因此,采用机器人自动巡线成为保障高压输电线安全运行的一种必要手段。 高压输电线路巡线机器人属于特种机器人的研究范畴,主要完成高压供电线缆的无损探伤、悬垂绝缘子绝缘特性检测、输
[机器人]
51单片机实现温度采集与显示(二)
下面简单介绍一下DS18B20: DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线) ” 接 口的温度传感器。 与传统的热敏电阻等测温元件相比, 它是一种新型的体积小、 适用电压宽、 与微处理器接口简单的数字化温度传感器。 通过P37模拟单总线通信读取数据和发送命令 上面是DS18B20 的示意图,可以看出他与51单片机相连的只有一个管脚——P37,那么二者是如何通信的呢?——采用软件模拟单总线通信 DS18B20 时序包括如下几种: 初始化时序、 写(0 和 1) 时序、 读(0 和 1) 时序。 DS18B20 发送所有的命令和数据都是字节的低位在前。 初始化时序:简单来说,就是主机发送复位
[单片机]
51<font color='red'>单片机</font>实现温度采集与显示(二)
Ramtron推出USB接口的Versa 8051 MCU开发套件
USB 接口的 JTAG 仿真器能够实现更快速的编程和强大的在线调试功能 世界顶尖的非易失性铁电存储器 (FRAM) 和集成半导体产品开发商及供应商 Ramtron International Corporation 宣布:针对其公司的Versa 8051 微控制器推出USB 接口的编程/调试开发工具。 USB 接口的VJTAG-USB 开发器将与 VersaKit-30xx 开发板配套发售。VersaKit-30xx 是针对 Ramtron 的高速、多功能的 VRS51L2000 系列以及带有 FRAM的 VRS51L3000 系列 MCU 的通用开发平台 。 新型的 USB 板卡能够实现更快速的器件编程和出色的在线调试。
[新品]
Mouser库存飞思卡尔微控制器MCF5225x系列
  Mouser Electronics近日宣布,该公司库存飞思卡尔半导体(Freescale Semiconductor)ColdFire 微控制器MCF5225x 系列。Freescale Semiconductor公司成立于1953年,原为摩托罗拉半导体部,在全球半导体技术领域以创新设计领先闻名。现在,飞思卡尔是全球领先的嵌入式半导体设计商和生产商, 为无线通信、网络、汽车、消费电子和工业控制等行业提供产品。   ColdFire 微控制器MCF5225x 系列由紧密结合的与即时的Freescale MQX-ROTS软件,软件堆栈、培训资源及一站式第三方解决方案支持。飞思卡尔 MCF5225x MCU 由一个RISC微处
[单片机]
基于PIC单片机通信适配卡设计
  CAN总线-控制器局部网(CAN)国际标准(ISO11898)。CAN总线与一般的通信总线相比,它的数据通信具有突出的可靠性、实时性和灵活性。   其特点可概括如下:CAN为多主方式工作。在报文标识符上,各节点分成不同的优先级。采用非破坏总线仲裁技术。只需通过对报文的标识符滤波即可实现点对点、一点对多点方式传送接收数据。直接通信距离最远可达零10km(速率5kbps以下);通信速率最高可达到1Mbps(此时通讯距离最长为40m)。CAN上的节点数主要取决于总线驱动电路,目前可达110个。报文采用短帧结构,传输时间短,受到干扰的概率低。每帧信息都有CRC校验及其他检错措施,具有极好的检错效果。通信介质可为双绞线、同轴电缆或光纤
[单片机]
基于<font color='red'>PIC</font><font color='red'>单片机</font>通信适配卡设计
stc12c5a60s2复位电路说明
STC12C5A60S2在众多的51系列单片机中,要算国内STC 公司的1T增强系列更具有竞争力,因他不但和8051指令、管脚完全兼容,而且其片内的具有大容量程序存储器且是FLASH工艺的,如STC12C5A60S2单片机内部就自带高达60K FLASHROM,这种工艺的存储器用户可以用电的方式瞬间擦除、改写。而且STC系列单片机支持串口程序烧写。显而易见,这种单片机对开发设备的要求很低,开发时间也大大缩短。写入单片机内的程序还可以进行加密,这又很好地保护了你的劳动成果。 stc12c5a60s2内部结构图 stc12c5a60s2内部结构图如下: stc12c5a60s2复位电路 就是在复位引脚接1个10UF电容到电源+,
[单片机]
stc12c5a60s2复位电路说明
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved