×
单片机 > 单片机程序设计 > 详情

51单片机实现的485通讯程序

发布时间:2020-05-28 发布时间:
|
#ifndef __485_C__ 
#define __485_C__ 
#include  
#include  
 
#define unsigned char uchar 
#define unsigned int uint 
/* 通信命令 */ 
#define __ACTIVE_ 0x01    // 主机询问从机是否存在 
#define __GETDATA_ 0x02    // 主机发送读设备请求 
#define __OK_ 0x03    // 从机应答 
#define __STATUS_ 0x04    // 从机发送设备状态信息 
#define __MAXSIZE 0x08    // 缓冲区长度 
#define __ERRLEN 12    // 任何通信帧长度超过12则表示出错 
uchar dbuf[__MAXSIZE];    // 该缓冲区用于保存设备状态信息 
uchar dev;    // 该字节用于保存本机设备号 
sbit M_DE = P1 ^ 0;  // 驱动器使能,1有效 
sbit M_RE = P1 ^ 1;  // 接收器使能,0有效 
void get_status();    // 调用该函数获得设备状态信息,函数代码未给出 
void send_data(uchar type, uchar len, uchar *buf);    // 发送数据帧 
bit recv_cmd(uchar *type);    // 接收主机命令,主机请求仅包含命令信息 
void send_byte(uchar da);    // 该函数发送一帧数据中的一个字节,由send_data()函数调用 
void main() 

    uchar type; 
    uchar len; 
 
    /* 系统初始化 */ 
    P1 = 0xff;    // 读取本机设备号 
    dev = (P1 >> 2); 
    TMOD = 0x20; // 定时器T1使用工作方式2 
    TH1 = 250;    // 设置初值 
    TL1 = 250; 
    TR1 = 1;    // 开始计时 
    PCON = 0x80;    // SMOD = 1 
    SCON = 0x50; // 工作方式1,波特率9600bps,允许接收 
    ES = 0;    // 关闭串口中断 
    IT0 = 0;    // 外部中断0使用电平触发模式 
    EX0 = 1;    // 开启外部中断0 
    EA = 1;    // 开启中断 
    /* 主程序流程 */ 
    while(1)    // 主循环 
    { 
        if(recv_cmd(&type) == 0)    // 发生帧错误或帧地址与本机地址不符,丢弃当前帧后返回 
            continue; 
        switch(type) 
        { 
        case __ACTIVE_:    // 主机询问从机是否存在 
            send_data(__OK_, 0, dbuf);    // 发送应答信息,这里buf的内容并未用到 
            break; 
        case __GETDATA_: 
            len = strlen(dbuf); 
            send_data(__STATUS_, len, dbuf);    // 发送设备状态信息 
            break; 
        default: 
            break;    // 命令类型错误,丢弃当前帧后返回 
        } 
    } 

void READSTATUS() interrupt 0 using 1    // 产生外部中断0时表示设备状态发生改变,该函数使用寄存器组1 

    get_status();    // 获得设备状态信息,并将其存入dbuf指向的存储区,数据最后一字节置0表示数据结束 

/* 该函数接收一帧数据并进行检测,无论该帧是否错误,函数均会返回 
* 函数参数type保存接收到的命令字 
* 当接收到数据帧错误或其地址位不为0时(非主机发送帧),函数返回0,反之返回1 
*/ 
bit recv_cmd(uchar *type) 

    bit db = 0;    // 当接收到的上一个字节为0xdb时,该位置位 
    bit c0 = 0;    // 当接收到的上一个字节为0xc0时,该位置位 
    uchar data_buf[__ERRLEN];    // 保存接收到的帧 
    uchar tmp; 
    uchar ecc = 0; 
    uchar i; 
 
    M_DE = 0;    // 置发送禁止,接收允许 
    M_RE = 0; 
 
    /* 接收一帧数据 */ 
    i = 0; 
    while(!c0)    // 循环直至帧接收完毕 
    { 
        RI = 0; 
        while(!RI); 
        tmp = SBUF; 
        RI = 0; 
        if(db == 1)    // 接收到的上一个字节为0xdb 
        { 
            switch(tmp) 
            { 
            case 0xdd: 
                data_buf[i] = 0xdb;    // 0xdbdd表示0xdb 
                ecc = ecc ^ 0xdb; 
                db = 0; 
                break; 
            case 0xdc 
                    data_buf[i] = 0xc0;    // 0xdbdc表示0xc0 
                ecc = ecc ^ 0xc0; 
                db = 0; 
                break; 
            default 
                    return 0;    // 帧错误,返回 
            } 
            i++; 
        } 
        switch(tmp)    // 正常情况 
        { 
        case 0xc0:    // 帧结束 
            c0 = 1; 
            break; 
        case 0xdb:    // 检测到转义字符 
            db = 1; 
            break; 
        default:    // 普通数据 
            data_buf[i] = tmp;    // 保存数据 
            ecc = ecc ^ tmp;  // 计算校验字节 
            i++; 
        } 
        if(i == __ERRLEN)    // 帧超长,错误,返回 
            return 0; 
    } 
    /* 判断帧是否错误 */ 
    if(i          return 0; 
    if(ecc != 0)    // 校验错误,返回 
        return 0; 
    if(data_buf[0] != dev)    // 非访问本机命令,错误,返回 
        return 0; 
    *type = data_buf[1];    // 获得命令字 
    return 1;    // 函数成功返回 

/* 该函数发送一帧数据帧,参数type为命令字、len为数据长度、buf为要发送的数据内容 */ 
void send_data(uchar type, uchar len, uchar *buf) 

    uchar i; 
    uchar ecc = 0;    // 该字节用于保存校验字节 
    M_DE = 1;    // 置发送允许,接收禁止 
    M_RE = 1; 
 
    send_byte(dev);    // 发送本机地址 
    ecc = dev; 
    send_byte(type);    // 发送命令字 
    ecc = ecc ^ type; 
    send_byte(len);    // 发送长度 
    ecc = ecc ^ len; 
    for(i = 0; i      { 
        send_byte(*buf); 
        ecc = ecc ^ (*buf); 
        buf++; 
    } 
    send_byte(ecc);    // 发送校验字节 
 
    TI = 0;    // 发送帧结束标志 
    SBUF = 0xc0; 
    while(!TI); 
    TI = 0; 

/* 该函数发送一个数据字节,若该字节为0xdb,则发送0xdbdd,若该字节为0xc0则,发送0xdbdc */ 
void send_byte(uchar da) 

    switch(da) 
    { 
    case 0xdb:    // 字节为0xdb,发送0xdbdd 
        TI = 0; 
        SBUF = 0xdb; 
        while(!TI); 
        TI = 0; 
        SBUF = 0xdd; 
        while(!TI) 
            TI = 0; 
        break; 
    case 0xc0:    // 字节为0xc0,发送0xdbdc 
        TI = 0; 
        SBUF = 0xdb; 
        while(!TI); 
        TI = 0; 
        SBUF = 0xdc; 
        while(!TI) 
            TI = 0; 
        break; 
    default:    // 普通数据则直接发送 
        TI = 0; 
        SBUF = da; 
        while(!TI); 
        TI = 0; 
    } 

#endif 
 
 
RS - 232接口实现计算机和单片机通信程序 
作者: 
佚名    来源: 
本站原创    点击数:…   更新时间:2008年07月10日     【字体:大 中 小】   
 
 
//此程序通过RS-232接口来完成计算机和单片机通信(程序已用p18f458试验板上调试通过) 
//程序的调试可以用"串口调试助手V2.1"辅助完成,此程序可在http://www.51hei.com 下载 
//此程序首先发送测试数据55H,再通过中断实现数据的接收和发送 
#include "p18f458.h" 
void InterruptHandlerHigh(void); 
//初始化程序 
void initial() 

    SPBRG = 0X19;  //选择传输波特率为9600bps 
    TXSTA = 0X04;  //选择异步高速方式传输8位数据 
    RCSTA = 0X80;  //允许同步串行口工作 
    TRISC = 0X80;  //将RC7,RC6设置为输入,断绝与外接电路的连接 
    TXSTAbits.TXEN = 1; //发送允许 
    RCSTAbits.CREN = 1; //接受数据允许 
    PIE1bits.RCIE = 1; //接收中断使能 
    INTCON = 0XC0; //总中断和外围中断允许 

//高优先级中断向量 
#pragma code InterruptVectorHigh=0x08 
void InterruptVectorHigh (void) 

    _asm 
    goto InterruptHandlerHigh   //跳到中断程 
    _endasm 

//高优先级中断服务程序 
#pragma code 
#pragma interrupt InterruptHandlerHigh 
void InterruptHandlerHigh () 

    while(PIR1bits.RCIF == 1) //若接收中断标志不为1,则为误操作,返回 
    { 
        TXREG = RCREG; //将接收到的数据放入发送寄存器,并启动发送 
    } 

//主程序 
main() 

    initial();     //系统初始化 
    TXREG = 0X55;  //发送数据55H进行测试 
    for(;;); 

 
 
------------------------------------------汇编语言版本的RS - 232接口实现计算机和单片机通信程序------------ 

此程序通过RS - 232接口来完成计算机和单片机通讯(程序以在p18f458试验板上调通) 

本单片机程序由http://www.51hei.com提供 

此程序首先发送测试数据55H,再通过中断实现数据的接收和发送 

程序的调试可以用"串口调试助手V2.1"辅助完成 
LIST     P = 18f458 
             INCLUDE    "P18f458.INC" 
             ORG  0x00 
             GOTO MAIN 
             ORG  0x08 
             GOTO INTSERVE 
             ORG  0X30 
             ; 
************ **中断服务子程序 ************ *** 
INTSERVE 
BTFSS PIR1,RCIF  ; 
接收中断标志为1? 
GOTO ERR_RE   ; 
误操作,返回 
MOVF RCREG,0   ; 
否则,将接收到的数据通过W寄存器 
MOVWF TXREG    ; 
放入发送寄存器,并启动发送 
ERR_RE NOP 
RETFIE 

************** **初始化程序 ************ *** 
INITIAL NOP 
MOVLW  0X19   ; 
选择传输波特率为9600bps 
MOVWF  SPBRG 
MOVLW  0X04   ; 
选择异步高速方式传输8位数据 
MOVWF  TXSTA 
MOVLW  0X80   ; 
允许同步串行口工作 
MOVWF  RCSTA 
MOVLW  0X80   ; 
将RC7,RC6设置为输入,断绝与外接电路的连接 
MOVWF  TRISC 
BSF  TXSTA,TXEN  ; 
发送允许 
BSF  RCSTA,CREN  ; 
接受数据允许 
BSF      PIE1,RCIE  ; 
接收中断使能 
MOVLW  0XC0   ; 
总中断和外围中断允许 
MOVWF  INTCON 
RETURN 

******************** **主程序 ****************** *** 
MAIN     NOP 
CLRWDT 
CALL  INITIAL 
MOVLW  0X55   ; 
发送数据55H进行测试 
MOVWF  TXREG 
LOOP 
GOTO  LOOP 
END 
 
 
 
 
两片51单片机互相通信的串行通信程序(一个发送程序, 一个接收程序) 
2007 - 05 - 27 08: 27 

系统晶振是 11.0592 MHz 

51单片机发送单片机程序 

此程序用Proteus仿真通过 

此程序在硬件上测试通过 

2007 - 05 - 27 

附有简化电路图 

为了使初学者能看懂,程序与图尽可能的简单扼要 

实验现象为,发送端的P1口的哪个键被接下,接收端的哪个灯对应着亮 

如果把两个单片机的T和R通过无线模块(如基于MCP2120芯片的模块)来扩充,便可做成无线通信 
ORG                 0000H 
AJMP                START 
ORG                 0040H 
START: 
MOV                 SP, #60H 
MOV                 SCON, #50H                   ;串口 方式 1 
MOV                 TMOD, #20H                   ;T1 方式2 
MOV                 TL1, #0FDH                   ;波特率 9600 的常数 
MOV                 TH1, #0FDH 
SETB                TR1 
mov                 r5, #00h 
WAIT: 
mov               p1, #0ffh 
mov               a , p1 
mov               r5, a 
lcall delay                      ; 
读键盘,这里去抖动,还要加几句话 
mov               a , p1 
nop 
CJNE                A, 5, WAIT                ; 
是否有键输入 
MOV                 SBUF, a                      ; 
串口输出键盘输入的值 
NOP 
SS: 
JBC                 TI, WAIT                     ; 
是否发送完毕 
SJMP                SS 
DELAY: 

延时子程序 
PUSH                0                           ; 
保存现场 
PUSH                1 
MOV                 0, #06H 
DELAY1: 
MOV                 1, #0H 
DJNZ                1, $ 
DJNZ                0, DELAY1 
POP                 1                           ; 
恢复现场 
POP                 0 
RET 
END 
 
 

系统晶振是 11.0592 MHz 

51单片机接收单片机程序 

此程序用Proteus仿真通过 

此程序在硬件上测试通过 

2007 - 05 - 27 

附有简化电路图 

为了使初学者能看懂,程序与图尽可能的简单扼要 

实验现象为,发送端的P1口的哪个键被接下,接收端的哪个灯对应着亮 

如果把两个单片机的T和R通过无线模块(如基于MCP2120芯片的模块)来扩充,便可做成无线通信 
ORG                 0000H 
AJMP                START 
ORG                 0040H 
START: 
MOV                 SCON, #50H                   ;串口 方式 1 
MOV                 TMOD, #20H                   ;T1 方式 2 
MOV                 TL1, #0FDH                   ;波特率 9600 的常数 
MOV                 TH1, #0FDH 
SETB                TR1 
WAIT: 
JBC                 RI, DIS_REC                  ; 
是否接收到数据 
sjmp                wait 
DIS_REC: 
MOV                 A, SBUF                      ; 
读串口接收到的数据 
mov                 p1, a 
SJMP                wait 
end 
 
 
 
 
51单片机串行口通信程序设计例子 
时间: 2009 - 03 - 06 17: 13来源: 
未知 作者: 
牛牛 点击:  768次 
串行口方式0应用编程 8051单片机串行口方式0为移位寄存器方式,外接一个串入并出的移位寄存器,就能扩展一个并行口。 单片机串行口通信 
程序设计硬件连接图 例:用8051单片机串行口外接CD4094扩展8位并行输出口,如图所示,8位并行口的各位都接一个发光二极管 
   串行口方式0应用编程 8051单片机串行口方式0为移位寄存器方式,外接一个串入并出的移位寄存器,就能扩展一个并行口。 
 
 
例:用8051单片机串行口外接CD4094扩展8位并行输出口,如图所示,8位并行口的各位都接一个发光二极管,要求发光管呈流水灯状态。 串行口方式0的数据 
传送可采用中断方式,也可采用查询方式,无论哪种方式,都要借助于TI或RI标志。串行发送时,能靠TI置位(发完一帧数据后)引起中断申请,在中断服务程序中发送 
下一帧数据,或者通过查询TI的状态,只要TI为0就继续查询,TI为1就结束查询,发送下一帧数据。在串行接收时,则由RI引起中断或对RI查询来确定何时接收下一帧数据。 
无论采用什么方式,在开始通信之前,都要先对控制寄存器SCON进行初始化。在方式0中将,将00H送SCON就能了。 
 
 
---------------- -单片机串行口通信程序设计列子-------------------------- 
ORG 2000H 
START: 
MOV SCON, #00H ;置串行口工作方式0 
MOV A, #80H ;最高位灯先亮 
CLR P1.0 ; 
关闭并行输出(避象传输过程中,各LED的"暗红"现象) 
OUT0: 
MOV SBUF, A ; 
开始串行输出 
OUT1: 
JNB TI, OUT1 ; 
输出完否 
CLR TI ; 
完了,清TI标志,以备下次发送 
SETB P1.0 ; 
打开并行口输出 
ACALL DELAY ; 
延时一段时间 
RR A ; 
循环右移 
CLR P1.0 ; 
关闭并行输出 
JMP OUT0 ; 
循环 
说明:DELAY延时子程序能用前面我们讲P1口流水灯时用的延时子程序,这里就不给出了 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
单片机中高阻态的实质及意义