前面我介紹了51單片機的串口通信協議, 其核心是操作單片機的SCON,SBUF和定時器1,通過外部引腳Tx與Rx來實現與外部的數據交換?,F在加入我們要實現兩個mcu之間的遠程通信,顯然直接連接他們的Tx與Rx腳是不可行的。因為TTL通信容易受噪聲干擾,其次線路過長本身也會有壓降,再次信號線與地線之間形成一個電容,我們知道電容兩端電壓不能突變,因為TTL電平容易變形進而導致傳輸錯誤。
RS485通信
因此我們引入一種差分傳輸接口標準RS485,它具備以下特點。
1、我們在講A/D的時候,講過差分信號輸入的概念,同時也介紹了差分輸入的好處,最大的優勢是可以抑制共模干擾。尤其工業現場的環境比較復雜,干擾比較多,所以通信如果采用的是差分方式,就可以有效的抑制共模干擾。而RS485就是一種差分通信方式,它的通信線路是兩根,通常用A和B或者D+和D-來表示。邏輯“1”以兩線之間的電壓差為+(0.2~6)V表示,邏輯“0”以兩線間的電壓差為-(0.2~6)V來表示,是一種典型的差分通信。
2、RS485通信速度快,最大傳輸速度可以達到10Mb/s以上。
3、RS485內部的物理結構,采用的是平衡驅動器和差分接收器的組合,抗干擾能力也大大增加。
4、傳輸距離最遠可以達到1200米左右,但是他的傳輸速率和傳輸距離是成反比的,只有在100Kb/s以下的傳輸速度,才能達到最大的通信距離,如果需要傳輸更遠距離可以使用中繼。
5、可以在總線上進行聯網實現多機通信,總線上允許掛多個收發器,從現有的RS485芯片來看,有可以掛32、64、128、256等不同個設備的驅動器。
RS485的接口非常簡單,和RS232所使用的MAX232是類似的,只需要一個RS485轉換器,就可以直接和我們單片機的UART串行接口連接起來,并且完全使用的是和UART一致的異步串行通信協議。但是由于RS485是差分通信,因此接收數據和發送數據是不能同時進行的,也就是說它是一種半雙工通信。那我們如何判斷什么時候發送,什么時候接收呢?MAX485是美信(Maxim)推出的一款常用RS485轉換器。其中5腳和8腳是電源引腳,6腳和7腳就是485通信中的A和B兩個引腳,而1腳和4腳分別接到我們單片機的RXD和TXD引腳上,直接使用單片機UART進行數據接收和發送。而2腳和3腳就是方向引腳了,其中2腳是低電平使能接收器,3腳是高電平使能輸出驅動器。我們把這兩個引腳連到一起,平時不發送數據的時候,保持這兩個引腳是低電平,讓MAX485處于接收狀態,當需要發送數據的時候,把這個引腳拉高,發送數據,發送完畢后再拉低這個引腳就可以了。為了提高RS485的抗干擾性能,需要在靠近MAX485的A和B引腳之間并接一個電阻,這個電阻阻值從100歐到1K都可以。
RS485的單工通信協議
好了,現在我們先來用485做一次單向的傳遞,一塊MCU做主機發送一個片上AD讀取的數字量給另一塊MCU。我們可以定義一個簡單的單工通信協議。即主機發送的數據需遵守如下格式:
0xAA
adH
adL
adsum
其中0XAA為包頭,表示數據包的開始;adH為AD轉換結果的高8位,adL位AD轉換結果的低8位,adsum為(adH+adL)%256為校驗碼。為什么需要校驗碼?這是因為我們要發送的是一個占兩個字節的數ad,而SBUF是一個字節的寄存器,因此一個ad要分兩次發。如果發送過程傳輸中斷,正巧只發了高8位,那么下一次再開始發送的時候重新開始,這個高8位便會和另外一個8位組成一個數。因此需要校驗,當然這種校驗方法是不準確的。好了。直接上代碼吧。
RS485主機代碼
先看主機的代碼。main.c
#include "12864.h"
#include "stc_adc.h"
#include "uart.h"
#include "timer.h"
#include "rs485.h"
void Isr_Init()
{
EA=1;
ES=1;
ET0=1;
}
void main()
{
LCD_Init();
STCADC_Init();
Timer0_Init();
RS485_Init();
Isr_Init();
Show_String(0x80,"RS485 主機");
while(1)
{
ad=Read_StcAdc(5);
RS485_Send();
Show_Number(0x88,ad);
//...
}
}
rs485.c
#include"rs485.h" #include "stc_adc.h" #include "uart.h" #include "timer.h" sbit RT485=P1^0;//MAX485的收發狀態控制位 //1:發送 0:接收 void RS485_Init() { Uart_Init(); RT485=0; //初始化485為接收狀態 } void RS485_Send() //將當前的數字量發送給從機 { u8 buf[4],i; if(ms.ms1<200) { return ; } buf[0]=0xaa; buf[1]=ad/256; buf[2]=ad%256; buf[3]=(buf[1]+buf[2])%256; RT485=1; //發送狀態 for(i=0;i<4;i++) { Send_Byte(buf[i]); } RT485=0; //接收狀態 ms.ms1=0; }rs485.h
#ifndef _485_
#define _485_
#include "reg51.h"
#define u8 unsigned char
#define u16 unsigned int
void RS485_Init();
void RS485_Send();
#endif
uart.c
#include "uart.h" void Uart_Init() { TMOD=0X20; SCON=0X50; TH1=253; //9600bit/s-->11.0592MHZ TR1=1; } void Send_Byte(u8 dat) { SBUF=dat; while(TI==0); TI=0; } void Isr_uart() interrupt 4 //串口中斷處理 { u8 t; if(RI==1) { RI=0; t=SBUF; //.... } }uart.h
#ifndef _uart_ #define _uart_ #include "reg51.h" #define u8 unsigned char #define u16 unsigned int void Uart_Init(); void Send_Byte(u8 dat); #endif
timer.c
#include "timer.h" TMS ms; // void Timer0_Init() //1ms { TMOD|=0X01; TH0=64614/256; TL0=64614%256; TR0=1; } void Timer0_Isr() interrupt 1 //t0 1ms { TH0=64614/256; TL0=64614%256; ms.ms1++; ms.ms2++; ms.ms3++; ms.ms4++; //... }timer.h
#ifndef _TIMER_
#define _TIMER_
#include "reg51.h"
#define u8 unsigned char
#define u16 unsigned int
typedef struct
{
u16 ms1;
u16 ms2;
u16 ms3;
u16 ms4;
//...
}TMS;
extern TMS ms; //
void Timer0_Init();
#endif
RS485從機代碼
main.c
#include "12864.h" #include "uart.h" #include "timer.h" #include "rs485.h" u16 ad; //當前數字量 void Isr_Init() { EA=1; ES=1; ET0=1; } void main() { LCD_Init(); Timer0_Init(); RS485_Init(); Isr_Init(); Show_String(0x80,"RS485 從機"); while(1) { Show_Number(0x88,ad); //... } }rs485.c
#include"rs485.h" #include "uart.h" #include "timer.h" u8 Rs485buf[4]; //Rs485接收緩沖區 sbit RT485=P1^0;//MAX485的發送接收狀態控制位定義 /* 0XAA ADH ADL SUM%256 */ void RS485_Init() { Uart_Init(); RT485=0; //初始化MAX485為接收狀態 }rs485.h
#ifndef _485_
#define _485_
#include "reg51.h"
#define u8 unsigned char
#define u16 unsigned int
void RS485_Init();
#endif
uart.c
#include "uart.h"
void Uart_Init()
{
TMOD=0X20;
SCON=0X50;
TH1=253; //9600bit/s-->11.0592MHZ
TR1=1;
}
void Send_Byte(u8 dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
void Isr_uart() interrupt 4 //串口中斷處理
{
u8 t;
static u8 i;
if(RI==1)
{
RI=0;
t=SBUF;
Rs485buf[i++]=t;
if(Rs485buf[0]==0xaa)
{
if(i>=4)
{
if((Rs485buf[1] + Rs485buf[2])%256 == Rs485buf[3])
{
ad=Rs485buf[1]*256+Rs485buf[2];
}
i=0;
}
}
else
{
i=0;
}
}
}
uart.h
#ifndef _uart_
#define _uart_
#include "reg51.h"
#define u8 unsigned char
#define u16 unsigned int
extern u8 Rs485buf[4]; //Rs485?óê??o3???
void Uart_Init();
void Send_Byte(u8 dat);
#endif另外的12864和timer就不列出了,復制主機里面的代碼即可。
RS485多機通信
如圖給出了主從機的框圖,圖中主機每100ms輪詢一個從機。主機發送的尋址命令幀包含:
(1)本次輪詢的從機地址
(2)本次輪詢該從機的目的
(3)本次輪詢該從機的附加信息
(4)本尋址幀的校驗信息
從機收到尋址幀后:
(1)校驗數據包的正確性
(2)檢驗數據包中地址部分是否與自己的地址相等
(3)對數據包進行處理
(4)對主機發回響應數據包
主機尋址幀的結構:
從機發回數據包結構:
評論