• 串口通讯



    一、 串口基础知识

      串口是串行接口(serial port)的简称,也称为串行通信接口或COM接口。

      串口通信是指采用串行通信协议(serial communication)在一条信号线上将数据一个比特一个比特地逐位进行传输的通信模式。

      串口按电气标准及协议来划分,包括RS-232-C、RS-422、RS485等。

     1. 串行通讯方式

      为了更好的理解串口通信,我们还需要了解几个串口通信当中的基本概念。

      (1)发送时钟:发送数据时,首先将要发送的数据送入移位寄存器,然后在发送时钟的控制下,将该并行数据逐位移位输出。

      (2)接收时钟:在接收串行数据时,接收时钟的上升沿对接收数据采样,进行数据位检测,并将其移入接收器的移位寄存器中,最后组成并行数据输出。

      (3)波特率:波特率就是每秒钟传输的数据位数。波特率的单位是每秒比特数(bps),常用的单位还有:每秒千比特数Kbps,每秒兆比特数Mbps。串口典型的传输波特率600bps,1200bps,2400bps,4800bps,9600bps,19200bps,38400bps。

      (4)波特率因子:波特率因子是指发送或接收1个数据位所需要的时钟脉冲个数。

      在串行通信中,数据在1位宽的单条线路上进行传输,一个字节的数据要分为8次,由低位到高位按顺序一位一位的进行传送。

      串行通信的数据是逐位传输的,发送方发送的每一位都具有固定的时间间隔,这就要求接收方也要按照发送方同样的时间间隔来接收每一位。不仅如此,接收方还必须能够确定一个信息组的开始和结束。

      常用的两种基本串行通信方式包括同步通信和异步通信。

     1.1 串行同步通讯    

      同步通信(SYNC:synchronous data communication)是指在约定的通信速率下,发送端和接收端的时钟信号频率和相位始终保持一致(同步),这样就保证了通信双方在发送和接收数据时具有完全一致的定时关系。

      同步通信把许多字符组成一个信息组(信息帧),每帧的开始用同步字符来指示,一次通信只传送一帧信息。在传输数据的同时还需要传输时钟信号,以便接收方可以用时针信号来确定每个信息位。

      同步通信的优点是传送信息的位数几乎不受限制,一次通信传输的数据有几十到几千个字节,通信效率较高。同步通信的缺点是要求在通信中始终保持精确的同步时钟,即发送时钟和接收时钟要严格的同步(常用的做法是两个设备使用同一个时钟源)。

      在后续的串口通信与编程中将只讨论异步通信方式,所以在这里就不对同步通信做过多的赘述了。

     1.2 串行异步通信

      异步通信(ASYNC:asynchronous data communication),又称为起止式异步通信,是以字符为单位进行传输的,字符之间没有固定的时间间隔要求,而每个字符中的各位则以固定的时间传送。

      在异步通信中,收发双方取得同步是通过在字符格式中设置起始位和停止位的方法来实现的。具体来说就是,在一个有效字符正式发送之前,发送器先发送一个起始位,然后发送有效字符位,在字符结束时再发送一个停止位,起始位至停止位构成一帧。停止位至下一个起始位之间是不定长的空闲位,并且规定起始位为低电平(逻辑值为0),停止位和空闲位都是高电平(逻辑值为1),这样就保证了起始位开始处一定会有一个下跳沿,由此就可以标志一个字符传输的起始。而根据起始位和停止位也就很容易的实现了字符的界定和同步。

      显然,采用异步通信时,发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,可以互不同步。

      下面简单的说说异步通信的数据发送和接收过程。

     1.2.1 异步通信的数据格式

       在介绍异步通信的数据发送和接收过程之前,有必要先弄清楚异步通信的数据格式。

    异步通信规定传输的数据格式由起始位(start bit)、数据位(data bit)、奇偶校验位(parity bit)和停止位(stop bit)组成,如图1-1所示(该图中未画出奇偶校验位,因为奇偶检验位不是必须有的,如果有奇偶检验位,则奇偶检验位应该在数据位之后,停止位之前)。

    图1-1 异步通信数据格式

      (1)起始位:起始位必须是持续一个比特时间的逻辑0电平,标志传输一个字符的开始,接收方可用起始位使自己的接收时钟与发送方的数据同步。

      (2)数据位:数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定,一般可以是5位、7位或8位,标准的ASCII码是0~127(7位),扩展的ASCII码是0~255(8位)。传输数据时先传送字符的低位,后传送字符的高位。

      (3)奇偶校验位:奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个逻辑高位;如果是偶校验,需要保证传输的数据总共有偶数个逻辑高位。

    举例来说,假设传输的数据位为01001100,如果是奇校验,则奇校验位为0(要确保总共有奇数个1),如果是偶校验,则偶校验位为1(要确保总共有偶数个1)。

    由此可见,奇偶校验位仅是对数据进行简单的置逻辑高位或逻辑低位,不会对数据进行实质的判断,这样做的好处是接收设备能够知道一个位的状态,有可能判断是否有噪声干扰了通信以及传输的数据是否同步。

      (4)停止位:停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是逻辑1电平,标志着传输一个字符的结束。

      (5)空闲位:空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。

     1.2.2 异步通信数据发送过程

      清楚了异步通信的数据格式之后,就可以按照指定的数据格式发送数据了,发送数据的具体步骤如下:

      (1)初始化后或者没有数据需要发送时,发送端输出逻辑1,可以有任意数量的空闲位。

      (2)当需要发送数据时,发送端首先输出逻辑0,作为起始位。

      (3)接着就可以开始输出数据位了,发送端首先输出数据的最低位D0,然后是D1,最后是数据的最高位。

      (4)如果设有奇偶检验位,发送端输出检验位。

      (5)最后,发送端输出停止位(逻辑1)。

      (6)如果没有信息需要发送,发送端输出逻辑1(空闲位),如果有信息需要发送,则转入步骤(2)。

     1.2.3 异步通信数据接收过程

      在异步通信中,接收端以接收时钟和波特率因子决定每一位的时间长度。下面以波特率因子等于16(接收时钟每16个时钟周期使接收移位寄存器移位一次)为例来说明。

      (1)开始通信,信号线为空闲(逻辑1),当检测到由1到0的跳变时,开始对接收时钟计数。

      (2)当计到8个时钟的时候,对输入信号进行检测,若仍然为低电平,则确认这是起始位,而不是干扰信号。

      (3)接收端检测到起始位后,隔16个接收时钟对输入信号检测一次,把对应的值作为D0位数据。

      (4)再隔16个接收时钟,对输入信号检测一次,把对应的值作为D1位数据,直到全部数据位都输入。

      (5)检验奇偶检验位。

      (6)接收到规定的数据位个数和校验位之后,通信接口电路希望收到停止位(逻辑1),若此时未收到逻辑1,说明出现了错误,在状态寄存器中置“帧错误”标志;若没有错误,对全部数据位进行奇偶校验,无校验错时,把数据位从移位寄存器中取出送至数据输入寄存器,若校验错,在状态寄存器中置“奇偶错”标志。

      (7)本帧信息全部接收完,把线路上出现的高电平作为空闲位。

      (8)当信号再次变为低时,开始进入下一帧的检测。

      以上就是异步通信中数据发送和接收的全过程了。

    二、 串口接头简介

      常用的串口接头有两种,一种是9针串口(简称DB-9),一种是25针串口(简称DB-25)。每种接头都有公头和母头之分,其中带针状的接头是公头,而带孔状的接头是母头。9针串口的外观如图2所示。

    图2-1 DB-9 外观图

      由图2-1可以看出,在9针串口接头中,公头和母头的管脚定义顺序是不一样,这一点需要特别注意。那么,这些管脚都有什么作用呢?9针串口和25针串口常用管脚的功能说明如图2-2所示。

    图2-2 9针串口和25针串口常用管脚功能说明

     

    三、 串口通信接口标准

      基本概念:

      (1)单工模式:(Simplex Communication)的数据传输是单向的。通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输,使用一根传输线。

      (2)半双工模式:(Half Duplex)通信使用同一根传输线,既可以发送数据又可以接收数据,但不能同时进行发送和接收。数据传输允许数据在两个方向上传输,但是,在任何时刻只能由其中的一方发送数据,另一方接收数据。因此半双工模式既可以使用一条数据线,也可以使用两条数据线。半双工通信中每端需有一个收发切换电子开关,通过切换来决定数据向哪个方向传输。因为有切换,所以会产生时间延迟,信息传输效率低些。

      (3)全双工模式:(Full Duplex)通信允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。在全双工模式中,每一端都有发送器和接收器,有两条传输线,信息传输效率高。

      显然,在其它参数都一样的情况下,全双工比半双工传输速度要快,效率要高。

     1. RS-232标准

       RS232是计算机与通信工业应用中最广泛一种串行接口。它以全双工方式工作,需要地线、发送线和接收线三条线。RS232只能实现点对点的通信方式。

     1.1 RS232串口缺点

        ●接口信号电平值较高,接口电路芯片容易损坏。
        ●传输速率低,最高波特率19200bps。
        ●抗干扰能力较差。
        ●传输距离有限,一般在15m以内。
        ●只能实现点对点的通讯方式。

     1.2 RS232串口接口定义:

      RXD:接收数据,TXD:发送数据,GND/SG:信号地。

     

     2. RS-485标准

     2.1 RS-485串口特点

        ●RS485采用平衡发送和差分接收,具有良好的抗干扰能力,信号能传输上千米。
        ●RS485有两线制和四线制两种接线。采用四线制时,只能实现点对多的通讯(即只能有一个主设备,其余为从设备)。四线制现在很少采用,现在多采用两线制接线方式。
        ●两线制RS485只能以半双式方式工作,收发不能同时进行。
        ●RS485在同一总线上最多可以接32个结点,可实现真正的多点通讯,但一般采用的是主从通信方式,即一个主机带多个从机。
        ●因RS485接口具有良好的抗干扰能力,长的传输距离和多站能力等优点使其成为首选的串行接口。

     2.2 RS-485抑制共模干扰

       

     2.3 RS-485串口接口定义

      A 或 Data+(D+) 或 +:信号正;
      B 或 Data-(D-) 或 -:信号负

     2.4 计算机与RS485仪表通讯

      计算机自带的串口只有RS232,没有RS485,如果计算机要与RS485串口的仪表进行通讯,必须使用串口转换器或装上RS485串口转换卡后才能进行通讯。
      2.4.1 RS485串口的终端电阻
        ●一般情况下不需要增加终端电阻,只有在RS485通信距离超过100米的情况下,要在RS485通讯的开始端和结束端增加终端电阻,RS485典型终端电阻是120欧。
        ●终端电阻是为了消除在通信电缆中的信号反射在通信过程中,有两种信号因导致信号反射:阻抗不连续和阻抗不匹配。

        阻抗不连续,信号在传输线末端突然遇到电缆阻抗很小甚至没有,信号在这个地方就会引起反射。消除这种反射的方法,就必须在电缆的末端跨接一个与电缆的特性阻抗同样大小的终端电阻,使电缆的阻抗连续。由于信号在电缆上的传输是双向的,因此,在通讯电缆的另一端可跨接一个同样大小的终端电阻。
        引起信号反射的另一原因是数据收发器与传输电缆之间的阻抗不匹配。这种原因引起的反射,主要表现在通讯线路处在空闲方式时,整个网络数据混乱。要减弱反射信号对通讯线路的影响,通常采用噪声抑制和加偏置电阻的方法。在实际应用中,对于比较小的反射信号,为简单方便,经常采用加偏置电阻的方法。

     

    四、串口通讯硬件常见的注意事项

        ●通讯电缆端子一定接牢,不可有任何松动,否则,可能会烧坏仪表或上位机的通讯板。
        ●不可带电拔插通讯端子,否则,可能会烧坏仪表或上位机的通讯板,一定要关闭仪表电源后才能去拔插通讯端子或接通讯线。
        ●通讯用的屏蔽电缆最好选用双层隔离型屏蔽电缆,其次选用单层屏蔽电缆,最好不要选用无屏蔽层的电缆,且电缆屏蔽层一定要能完全屏蔽,有些质量差的电缆,屏蔽层很松散,根本起不到屏蔽的作用。单层屏蔽的电缆屏蔽层应一端接地,双层屏蔽的电缆屏蔽层其外层(含铠装)应两端接地,内层屏蔽则应一端接地。
        ●仪表使用RS232通讯时,通讯电缆长度不得超过15米。
        ●一般RS485协议的接头没有固定的标准,可能根据厂家的不同引脚顺序和管脚功能可能不尽相同,用户可以查阅相关产品RS485的引脚图。
        ●RS485通讯电缆最好选用阻阬匹配、低衰减的RS485专用通讯电缆(双绞线),不要使用普通的双绞电缆或质量较差的通讯电缆。因为普通电缆或质量差的通讯电缆,可能阻抗不匹配、衰减大、绞合度不够、屏蔽层太松散,这样会导致干扰将非常大,会造成通讯不畅,甚至通讯不上。
        ●仪表使用RS485通讯时,每台仪表必须手牵手地串下去,不可以有星型连接或者分叉,如果有星型连接或者分叉,干扰将非常大,会造成通讯不畅,甚至通讯不上。

        

        ●485总线结构理论上传输距离达到1200米,一般是指通讯线材优质达标,波特率9600,只有一台485设备才能使得通讯距离达到1200米,而且能通讯并不代表每次通讯都正常,所以通常485总线实际的稳定通讯距离远远达不到1200米。负载485设备多,线材阻抗不同时,通讯距离更短。
        ●仪表使用RS485通讯时,必要时,请接入终端电阻,以增强系统的抗干扰性,典型的终端电阻阻值是120欧。

    五、串口通讯软件设置要点

     1. 通讯的一些基本概念
        ●主机与从机:在通讯系统中起主要作用、发布主要命令的称为主机,接收命令的称为从机。
        ●连续方式:指主机不需要发布命令,从机就能自动地向主机发送数据。
        ●指令方式:指主机向从机发布命令,从机根据指令执行动作,并将结果“应答”给主机的模式。
        ●输出数据类型:指在连续方式通讯时,从机输出给主机的数据类型。
        ●通讯协议:指主机与从机通讯时,按哪一种编码规则来通讯。
        ●波特率:主从机之间通讯的速度。
        ●数据位:每次传输数据时,数据由几位组成。
        ●校验位:数据传输错误检测,可以是奇校验、偶校验或无校验。
        ●地址:每一台从机的编号。

     2. 主从机之间通讯设置要点
        ●要点一:主/从RS232/485硬件有无设置正确,通讯线有无接对。有些通讯板卡是RS422与RS485共用的,依靠板上跳线来实现的,有些仪表RS232/485也需要通讯跳线来实现。
        ●要点二:主机上的通讯端口有无设置正确;超时(一般设置为2s)、通讯延时(一般设置为5~20ms)、ACK信号延时(一般设置为0ms)有无设置正确。
        ●要点三:主/从机通讯协议有无选择正确。
        ●要点四:主/从机波特率有无选择正确。
        ●要点五:主/从机数据位有无选择正确。数据位可以选择7位,8位。
        ●要点六:主/从机校验位有无选择正确。校验位一般可选择偶校验、奇校验、无校验。
        ●要点七:主/从机停止位有无选择正确。停止位可以选择1位、1.5位还是2位。
        ●要点八:从机地址有无选择正确。
        ●要点九:主/从机的通讯方式有无选择正确。

     

    PS:进行通讯测试的时候经常会进行线路测试,测试所用的串口线是否可用,方法有二如下:
      (1)  把串口线接到不同的串口,用串口调试工具从一个串口发数据,另一个能正常收到说明串口线是OK的。
      (2)  把串口线的一端短接(用金属把2,3号脚连通),用万用表测另一端的2,3号如果正常的话会有嘀嘀的短接报警声。
     
     
    参考来源:依旧淡然->串口通讯与编程01:串口基础知识  http://www.cnblogs.com/menlsh/archive/2013/01/28/2880580.html
     
     
     
     

    附:模拟串口代码

    1. 模拟串口底层驱动:

    /**************************************************************
     * 函数名:UART_Analog_Init
     * 描  述:模拟串口初始化函数
     * 说  明:初始化I/O配置,包括TX,RX,RE(RS-485使能),初始化
              定时器。
    **************************************************************/
    void UART_Analog_Init(void)
    {          
    }

     

    /**************************************************************
     * 函数名:anal_WByte
     * 描  述:发送一个字节数据
     * 输  入:unsigned char Byt,需要发送的字节。
     * 说  明:串口发送标志位置一,初始化发送位计数,传递要发送的字节
             重装波特率,启动定时器。
    **************************************************************/
    void anal_WByte(unsigned char Byt)            // 发送一个字节
    {
        f_Uart_TX = 1;                    // 发送标志位置一
        bitcount = 10;                    // 发送位计数
        anal_Sent_Byte = Byt;        // 传递要发送的字节数据
        TIMER_STOP;
        TIMR_SETPERIOD = BAUD_RATE_4800;            // 重装波特率时钟
        TIMER_START;                    // 启动定时器
    }

     

    /**************************************************************
     * 函数名:anal_Uart_Send_Data
     * 描  述:发送Len个字节数据
     * 输  入:unsigned char *buf,要发送的数据;
                         unsigned char Len,要发送数据的个数。
     * 说  明:字发送完成(字发送标志位清零)后,装载下一个字节数据,
                        当装载字节数超过要发送数据的个数,则字符串发送完成,
                        字符串发送完成标志位置一。
    **************************************************************/
    void anal_Uart_Send_Data(unsigned char *buf,unsigned char Len)            // 发送一串字符
    {
        static unsigned char b_anal_Send_Count = 0;            // 发送字节计数
        if(!f_Uart_Byte_W)            // 字发送完成标志位
        {                
            if(b_anal_Send_Count >= Len)
            {
                b_anal_Send_Count = 0;
                f_Uart_W = 1;            // 字符串发送完成标志位
            }
            else
            {    
                f_Uart_Byte_W = 1;
                anal_WByte(buf[b_anal_Send_Count]);    
                b_anal_Send_Count++;    
            }
        }
    }

     

    /**************************************************************
     * 函数名:anal_RData
     * 描  述:启动串口接收数据。
     * 说  明:串口发送标志位清零,字符串接收完成标志位清零,打开
                        串口接收中断。开始检测接收数据。
    **************************************************************/
    void anal_RData(void)
    {
        f_Uart_TX = 0;            // 串口发送标志位清零
        f_Uart_R = 0;                // 字符串接收完成标志位清零
        EXTI_OPEN(UART_RX_PIN);            // 打开串口接收中断
    }

     

    /**************************************************************
     * 函数名:anal_Uart_Receive_Data
     * 描  述:字符串接收校验函数
     * 输  入:unsigned char b_Receive,接收到的字节。
     * 说  明:接收一个字节数据,传递给串口接收寄存器,判断数据正确
              则下标加1,如果判断错误(或字节接收超时错误,20ms)则
              下标归零,直到完成整个字符串的接收。进行数据和校验检测,
              检测通过则字符串接收完成标志位置一。
    **************************************************************/
    void    anal_Uart_Receive_Data(unsigned char b_Receive)
    {
        unsigned char b_Sum,i;
        if(!b_Time_UART_Byte)            // 字节接收超时错误,20ms
        {
            b_anal_Recieve_Count = 0;
        }
        b_Time_UART_Byte = 20;
        
        b_anal_RecieveBuf[b_anal_Recieve_Count] = b_Receive;
        switch(b_anal_Recieve_Count)
        {
            case 0:
                if(b_anal_RecieveBuf[0] == 0xAA)            // 判断帧头:0xAA
                {
                    b_anal_Recieve_Count ++;
                }
                else
                {
                    b_anal_Recieve_Count = 0;
                }
                break;
            case 1:
                if(b_anal_RecieveBuf[1] == 0x11)            // 判断标志位:0x11
                {
                    b_anal_Recieve_Count ++;
                }
                else
                {
                    b_anal_Recieve_Count = 0;
                }
                break;
            case 2:
                if(b_anal_RecieveBuf[2] <= 24)            // 判断数据长度
                {
                    b_anal_Recieve_Count ++;
                }
                else
                {
                    b_anal_Recieve_Count = 0;
                }
                break;
            default:
                if(b_anal_Recieve_Count < b_anal_RecieveBuf[2])
                {
                    if(++b_anal_Recieve_Count >= b_anal_RecieveBuf[2])
                    {
                        b_Sum = 0;
                        for(i=0; i < b_anal_RecieveBuf[2]-1; i++)
                        { 
                            b_Sum += b_anal_RecieveBuf[i];
                            i=i;
                        }
                        if(b_anal_RecieveBuf[b_anal_RecieveBuf[2]-1] == b_Sum)        // 和校验检测
                        {
                            b_anal_Recieve_Count = 0;
                            f_Uart_R = 1;                                // 字符串接收完成标志位置一
                            TIMER_STOP;                                    // 关闭定时器
                            EXTI_CLOSE(UART_RX_PIN);        // 关闭串口中断
                        }
                        else
                        {
                            b_anal_Recieve_Count = 0;
                        }
                    }                    
                }
                else
                {
                    b_anal_Recieve_Count = 0;
                }
                break;
        }
    }

     

    /**************************************************************
     * 函数名:UART_TIMER_Handler
     * 描  述:串口定时器中断函数
     * 说  明:发送一字节数据或接收一字节数据,详见函数!
    **************************************************************/
    void UART_TIMER_Handler(void)
    {
        uint32_t tmp;
        static unsigned char b_buf;
        
        TIMR_INTClr;         // 清除中断
        if(f_Uart_TX)                    // 发送一字节数据
        {
            bitcount--;
            if(bitcount == 9)                // 起始位(1)
            {
                UART_TX_0;
            }
            else if(bitcount>0)
            {
                if(anal_Sent_Byte&0x01)            // 数据位(8):低位在前,高位在后
                {
                    UART_TX_1;
                }
                else
                {
                    UART_TX_0;
                }
                anal_Sent_Byte=anal_Sent_Byte>>1;
            }
            else if(bitcount == 0)            // 停止位(1)
            {
                UART_TX_1;
                f_Uart_Byte_W = 0;                // 发送字节标志位
                TIMER_STOP;                                // 关闭定时器
            }
        }
        else                // 接收一字节数据
        {
            bitcount--;
            if(bitcount == 9)            // 起始位(1):
            {
                TIMER_STOP;                
                if(!Read_RX)
                {
                    TIMR_SETPERIOD = UART_Analog_BAUD_RATE;            // 检测起始位正确,重装波特率
                    TIMER_START;        
                }    
            }
            else if(bitcount>0)            // 数据位(8):低位在前,高位在后
            {
                b_buf >>=1;                
                if(Read_RX)
                {            
                    b_buf |=0x80;                // 接收数据
                }
            }
            else if(bitcount==0)        // 停止位(1):
            {
                TIMER_STOP;                                            // 关闭定时器
                EXTI_OPEN(UART_RX_PIN);                    // 打开接收中断
                if(Read_RX)
                {
                    anal_Uart_Receive_Data(b_buf);    // 检验该字节数据
                }
            }        
        }
    }

     

    /**************************************************************
     * 函数名:GPIOE_Handler
     * 描  述:串口接收中断函数
     * 说  明:下降沿触发中断,滤波时间可修改。初始化接收位计数,重装
              半周期波特率,作起始位校验。
    **************************************************************/
    void GPIOE_Handler(void)
    {
        if(EXTI_INTSTATUS(UART_RX_PIN))            // 判断中断位
        {
            EXTI_CLEAR(UART_RX_PIN);        // 清中断标志位
            delay_us(10);            // 滤波:10us
            if(!Read_RX)            // 判断起始位
            {
                bitcount=10;        //n初始化接收位计数
                TIMER_STOP;            // 关闭定时器
                TIMR_SETPERIOD = BAUD_RATE_4800/2;            // 重装半个周期波特率,检测起始位
                TIMER_START;                    // 启动定时器
                EXTI_CLOSE(UART_RX_PIN);        // 关闭串口中断        
            }
        }
    }

     2. 串通讯:

    /********************************************************************
     * 函数名:Uart
     * 描  述:模拟串口收发控制函数
     * 说  明:被动模式,接收正确数据后,返回数据。
                    
        控制流程:初始化->接收数据->数据解码->发送数据打包->发送数据->初始化
        
        c_Uart_Recieve_Init:模拟串口接收数据初始化,接收数据前准备。
        c_Uart_anal_Recieve:模拟串口接收数据等待。接收超时错误判断。
        c_Uart_anal_Decode:模拟串口接收数据解码。
        c_Uart_anal_Load:模拟串口发送数据加载。
        c_Uart_anal_Send:模拟串口发送数据。
        c_Uart_anal_Send_Wait:模拟串口发送数据等待。
    ********************************************************************/
    void    Uart(void)
    {    
        switch(b_Uart_Progress)
        {
            case c_Uart_Recieve_Init:            // 接收初始化
            {
                b_Uart_Progress = c_Uart_anal_Recieve;
                w_UART_Wait_Time = 100;                // 接收超时,时间:100ms
                REDE_CLR;            // 接收使能        
                anal_RData();
            }break;
            
            case c_Uart_anal_Recieve:            // 接收数据
            {
                if(f_Uart_R)            // 接收数据完成
                {
                    e_anal_Uart_Count = 0;        // 清接收超时错误计数
                    e_anal_UART = 0;                    // 清接收超时错误标志位
                    b_Uart_Progress = c_Uart_anal_Decode;
                    EXTI_CLOSE(UART_RX_PIN);        // 接收成功转发送,并关闭接收中断
                }
                else if(!w_UART_Wait_Time)        // 接收超时
                {
                    b_Uart_Progress =  c_Uart_Recieve_Init;
                    if(++e_anal_Uart_Count >= 5)        // 接收超时错误计数>=5次,判断为接收超时错误
                    {
                        e_anal_UART = 1;        // 接收超时错误
                    }
                }    
            }break;
            
            case c_Uart_anal_Decode:            // 接收数据解码
            {
                anal_Uart_Decode();            // 解码函数
                b_Uart_Progress = c_Uart_anal_Load;
            }break;
                    
            case c_Uart_anal_Load:            // 发送数据加载
            {
                anal_Uart_Load_Data();        // 加载发送数据函数
                f_Uart_W = FALSE;                    // 清发送字符串完成标志位
                REDE_SET;            // 发送使能
                b_Uart_Progress = c_Uart_anal_Send;
            }break;
    
            case c_Uart_anal_Send:            // 发送数据
            {
                anal_Uart_Send_Data(b_anal_SendBuf,20);            // 发送数据
                if(f_Uart_W)
                {
                    b_Uart_Progress = c_Uart_anal_Send_Wait;            
                    w_UART_Wait_Time = 1;                
                }
            }break;
    
            case c_Uart_anal_Send_Wait:            // 发送线控板数据等待
            {
                if(!w_UART_Wait_Time)
                {
                    b_Uart_Progress =  c_Uart_Recieve_Init;
                }            
            }break;
    
            default:
                b_Uart_Progress = c_Uart_Recieve_Init;
                break;
        }    
    }

     

    /**************************************************************
     * 函数名:anal_Uart_Decode
     * 描  述:模拟串口接收数据解码
     * 说  明:模拟串口接收数据提取并赋值到相应寄存器。
    **************************************************************/
    void    anal_Uart_Decode(void)            // 解码
    {
    /**** 控制标志位清零 ****
        _System_Ctrl.byte = 0x00;
        f_Open_Air_Con = FALSE;            // 空调开机    
        f_Open_Fan = FALSE;                    // 室外风机控制
        f_Open_YJ = FALSE;                    // 室内机要求开压缩机
        **** 控制位赋值 ****
        f_System_Run             = b_Recieve_Data[3] & 0x01;
        f_Manual_Heater     = b_Recieve_Data[3]>>1 & 0x01;
        f_Sys_Calorifier     = b_Recieve_Data[3]>>4 & 0x01;
        f_System_SFS             = b_Recieve_Data[3]>>5 & 0x01;
        f_System_CSS             = b_Recieve_Data[3]>>6 & 0x01;
        */
    }

     

     

    /**************************************************************
     * 函数名:anal_Uart_Load_Data
     * 描  述:模拟串口发送数据打包
     * 说  明:模拟串口发送数据加载,包括校验等。
    **************************************************************/
    void    anal_Uart_Load_Data(void)            // 装载数据
    {
        unsigned char b_Sum,i;
        b_anal_SendBuf[0] = 0xAA;        // 帧头
        b_anal_SendBuf[1] = 0x11;        // 识别码 
        /*
        b_anal_SendBuf[2] = ;                // 数据长度
        b_anal_SendBuf[3] = ;                // 数据
        ···
        */
        
        b_Sum = 0;
        for(i=0;i <= (b_anal_SendBuf[2]-2);i++)
        {
            b_Sum += b_anal_SendBuf[i];
        }
        b_anal_SendBuf[b_anal_SendBuf[2]-1] = b_Sum;            // 和校验
    }

     

     

     

     

     

     

  • 相关阅读:
    Python 杨辉三角形
    Python 输出由星号*组成的菱形图案
    Python 计算组合数C(n,i),即从n个元素中任选i个,有多少种选法
    Python 快速判断一个数是不是素数
    判断今天是今年的第几天
    Pyhon 输入若干个成绩,求所有成绩的平均分。每输入一个成绩后询问是 否继续输入下一个成绩,回答“yes”就继续输入下一个成绩,回答“no” 就停止输入成绩
    KMP算法
    递归实现求解幂集问题
    Python 用大量小矩形来画曲线
    Python 所谓的艺术操作2(带颜色)
  • 原文地址:https://www.cnblogs.com/BinB-W/p/5687422.html
Copyright © 2020-2023  润新知