久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 10190|回復(fù): 5
打印 上一主題 下一主題
收起左側(cè)

51單片機(jī)485通訊講解 通俗易懂

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:211328 發(fā)表于 2017-6-14 21:50 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
51單片機(jī)S485通訊,正真好用,講解通俗易懂。
9-串口通訊之485通訊.doc (74.5 KB, 下載次數(shù): 75)

第九節(jié):串口通訊之485通訊
(1)       開(kāi)場(chǎng)白:
串口通訊有三種常用的硬件方式。
第一種是TTL方式。比如兩個(gè)單片機(jī)的RX,TX引腳直接連接上(甲的RX連接乙的TX, 甲的TX連接乙的RX),這種通訊距離最短,局限于兩三米長(zhǎng)的距離。
第二種是232方式。兩個(gè)CPU之間都經(jīng)過(guò)了兩個(gè)MAX232等電平轉(zhuǎn)換芯片,比如單片機(jī)跟電腦的串口通訊。這種最大傳輸距離大概十米左右。
第三種是485方式。兩個(gè)CPU之間都經(jīng)過(guò)了兩個(gè)MAX485等電平轉(zhuǎn)換芯片,此種方式在工控上應(yīng)用最多,尤其是距離長(zhǎng),要求一臺(tái)主機(jī)控制多臺(tái)設(shè)備的情況下。從地址呼叫原理上看,一臺(tái)主機(jī)應(yīng)該可以控制N多臺(tái)從機(jī),但是書上說(shuō)一臺(tái)主機(jī)最多可以控制32個(gè)從機(jī),可能主要是從電阻匹配的角度來(lái)考慮,我沒(méi)驗(yàn)證過(guò),我只搞過(guò)一臺(tái)主機(jī)控制十幾臺(tái)從機(jī)的項(xiàng)目。485的最大傳輸距離大概在1000米左右。

這三種方式在編程上還是會(huì)有一些差異。TTL方式與232方式的編程幾乎完全兼容,唯一需要注意的是,發(fā)送一串?dāng)?shù)據(jù)時(shí),每個(gè)字節(jié)延時(shí)間隔的大小可能會(huì)有些差異。
485通訊跟前面兩種方式有一個(gè)最大的區(qū)別是,485通訊要多增加一個(gè)IO口來(lái)控制數(shù)據(jù)流的方向,輸出低電平時(shí)表示接受數(shù)據(jù)的狀態(tài),輸出高電平時(shí)表示發(fā)送數(shù)據(jù)的狀態(tài)。485的通訊協(xié)議一般都是用主機(jī)對(duì)從機(jī)廣播呼叫的模式。即平時(shí)所有從機(jī)處于接受數(shù)據(jù)的狀態(tài)(流控IO口處于低電平),主機(jī)發(fā)送呼叫某個(gè)從機(jī)的地址,相應(yīng)的從機(jī)收到數(shù)據(jù)后,馬上切換到發(fā)送數(shù)據(jù)的狀態(tài)(流控IO口處于高電平),然后往主機(jī)返回對(duì)應(yīng)的數(shù)據(jù),從機(jī)對(duì)主機(jī)發(fā)送完數(shù)據(jù)之后,從機(jī)又馬上切換到接受數(shù)據(jù)的狀態(tài)(流控IO口處于低電平)。
這節(jié)利用第八節(jié)的232通訊程序修改成485通訊程序。
(2)功能需求:
           無(wú)論是單片機(jī)還是上位機(jī),最好在固定協(xié)議前多發(fā)送一個(gè)填充的無(wú)效字節(jié)0x00,因?yàn)橛布颍谝粋(gè)字節(jié)往往容易丟失。
           通訊協(xié)議:XX YY  EB 00 55
           其中后三位 EB 00 55就是我所說(shuō)的數(shù)據(jù)尾,它的有效數(shù)據(jù)XX YY在數(shù)據(jù)尾的前面。
         
任意時(shí)刻,從電腦“串口調(diào)試助手”上位機(jī)收到的一堆數(shù)據(jù)中,只要此數(shù)據(jù)中包含關(guān)鍵字EB 00 55 ,并且此關(guān)鍵字前面兩個(gè)字節(jié)的數(shù)據(jù)XX YY 分別為01 02,那么就往上位機(jī)發(fā)送“eb  00 aa”表示確認(rèn),同時(shí)蜂鳴器叫一聲。否則,往上位機(jī)發(fā)送“eb  00 55”表示出錯(cuò)。
(3)硬件原理:
在電腦的串口處加一個(gè)232轉(zhuǎn)485的通訊模塊。然后把單片機(jī)串口通訊的那兩個(gè)引腳經(jīng)過(guò)一個(gè)MAX485芯片,直接與電腦處的232轉(zhuǎn)485的通訊模塊相連接,多增加一根單片機(jī)的IO口,用此IO連接MAX485的第2和第3引腳,實(shí)現(xiàn)流控的功能。當(dāng)此IO處于低電平時(shí)為接收數(shù)據(jù)的狀態(tài),當(dāng)此IO口處于高電平時(shí)為發(fā)送數(shù)據(jù)的狀態(tài)。
(4)源碼適合的單片機(jī):PIC18f4520,晶振為22.1184MHz,波特率115200
(5)源代碼講解如下:
#include<pic18.h>         //包含芯片相關(guān)頭文件

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是輸出IO后綴都是_dr,凡是輸入的//IO后綴都//是_sr
#define  beep_dr  LATA2  //蜂鳴器輸出
#define  rede_dr  LATC5  //485通訊的流控

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是做延時(shí)計(jì)數(shù)閥值的常量
//前綴都用cnt_表示。
#define cnt_voice_time   150  //蜂鳴器響的聲音長(zhǎng)短的延時(shí)閥值
#define cnt_send   300           //確保接收緩沖區(qū)沒(méi)有繼續(xù)接收數(shù)據(jù),是變量
//send_cnt的溢出閥值

Void usart_service();        //串口通訊服務(wù)程序,放在main函數(shù)里
unsigned char asy_recieve();  //把串口緩沖區(qū)的數(shù)據(jù)一個(gè)個(gè)提取出來(lái)
void eusart_send(unsigned char t_data); //串口發(fā)送一個(gè)字節(jié)的數(shù)據(jù)
Void Buf_clear() ;  //把余下的緩沖區(qū)清零
void Delay11(unsigned int MS); //延時(shí)函數(shù)


//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是計(jì)數(shù)器延時(shí)的變量
//后綴都用_cnt表示。
unsigned int voice_time_cnt;        //蜂鳴器響的聲音長(zhǎng)短的計(jì)數(shù)延時(shí)
unsigned int send_cnt=0;            //一串?dāng)?shù)據(jù)從上位機(jī)發(fā)過(guò)來(lái)的時(shí)候,他們每個(gè)字節(jié)之間//的延時(shí)間隔很短,如果他們的延時(shí)間隔一旦超過(guò)了這個(gè)send_cnt變量的延時(shí),那么就////認(rèn)為他們的一串?dāng)?shù)據(jù)已經(jīng)發(fā)送完畢

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是涉及統(tǒng)計(jì)數(shù)量的變量
//后綴都用_total表示。
unsigned int RCREG_total;            //統(tǒng)計(jì)串口緩沖區(qū)已經(jīng)收了多少個(gè)數(shù)據(jù)
unsigned int RCREG_read_total;   //統(tǒng)計(jì)已經(jīng)從串口緩沖區(qū)讀出了多少個(gè)數(shù)據(jù)

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是用來(lái)更新的標(biāo)識(shí)變量,比如液晶刷屏,或者有新接收的串口數(shù)據(jù)更新等等,后綴一律用_update表示
Unsigned char send_update=0;  //一旦有數(shù)據(jù)從上位機(jī)發(fā)送過(guò)來(lái),就會(huì)引發(fā)串口接收中////斷,在串口中斷里,我把send_update=1表示目前正在接收數(shù)據(jù),警告單片機(jī)先不要//猴急,等串口中斷把所有從上位機(jī)連續(xù)發(fā)送過(guò)來(lái)的一堆數(shù)據(jù)接收完,再處理。那么什么///時(shí)候才知道發(fā)送的數(shù)據(jù)已經(jīng)發(fā)送完畢了呢?用send_cnt識(shí)別。因?yàn)樵诖谥袛嗬铮?//每次都會(huì)把send_cnt=0,而在main函數(shù)里,一旦發(fā)現(xiàn)send_update==1,send_cnt就//會(huì)開(kāi)始自加,當(dāng)它超過(guò)某個(gè)數(shù)值的時(shí)候,就會(huì)自動(dòng)把send_update=0,表示目前已經(jīng)沒(méi)//有數(shù)據(jù)發(fā)送了。而如果有數(shù)據(jù)不斷從上位機(jī)傳來(lái),send_cnt永遠(yuǎn)也不會(huì)超過(guò)某個(gè)數(shù)值,//因?yàn)槊總(gè)中斷它都被清零,這個(gè)原理跟看門口狗喂狗的原理很像。

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是用來(lái)接收數(shù)據(jù)的緩沖區(qū)數(shù)組后綴都用_buf表//示
Unsigned char RCREG_buf[50];  //串口接收緩沖區(qū),讀者可以根據(jù)實(shí)際項(xiàng)目設(shè)置大小
Unsigned char RCREG_buf_temp[50];  //臨時(shí)處理串口數(shù)據(jù)的緩沖區(qū),可以不用那么大

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是自鎖變量名, 后綴都用_lock表示。
Unsigned char send_lock=0;

//補(bǔ)充說(shuō)明:吳堅(jiān)鴻程序風(fēng)格是這樣的,凡是在main函數(shù)中用的中間變量,前綴m_,后//綴用_char或者_(dá)int表示類型
Unsigned int m_int;      //中間變量,只要是用在main函數(shù)里,誰(shuí)都可以重復(fù)用。

//主程序
main()
{
ADCON0=0x00;  
ADCON1=0x0f;                              //全部為數(shù)字信號(hào)
    ADCON2=0xa1;                             //右對(duì)齊
    RBPU=0;                                    //上拉電阻
    SSPEN=0;                                  //決定RA5不作為串口

   TRISA2=0;  //蜂鳴器輸出
TRISC5=0;  //485通訊的流控輸出

    BRG16=0;         //設(shè)置串口通信寄存器
    BRGH=0;
    SPBRGH=0x00;
    SPBRG=0x02;     //22.1184MHz晶振,115200波特率
    SYNC=0;
    SPEN=1;
    TX9=0;
    TXEN=1;
    TXIF=1;
    RX9=0;
    CREN=1;
    RCIE=1;
    PEIE=1;
    GIE=1;


    T1CON=0x24;     //定時(shí)器中斷配置
    TMR1H=0xFE;
TMR1L=0xEF;
    TMR1IF=0;
    TMR1IE=1;
    TMR1ON=1;
    TMR1IE=1;
//補(bǔ)充說(shuō)明,以上的內(nèi)容為寄存器配置,每種不同的單片機(jī)會(huì)有點(diǎn)差異,
//大家不用過(guò)度關(guān)注以上寄存器的配置,只要知道有這么一回事即可

    beep_dr=0;                             //關(guān)蜂鳴器,上電初始化IO
rede_dr=0;                              //流控IO,從機(jī)處于接收數(shù)據(jù)的狀態(tài)
   while(1)   
   {
                    CLRWDT(); //喂看門狗,大家不用過(guò)度關(guān)注此行
               usart_service();        //串口通訊服務(wù)
}

}
      
//中斷
void interrupt timer1rbint(void)
{

    if(RCIE==1&&RCIF==1)   //串口中斷,一次只能接受一個(gè)字節(jié)
{
        RCIE=0;
        RCIF=0;


        ++RCREG_total;   //以下代碼是鴻哥的在所有串口項(xiàng)目中用到的標(biāo)準(zhǔn)代碼
        if(RCREG_total>50)  //超過(guò)緩沖區(qū)
        {
           RCREG_total=50;
        }
       RCREG_buf[RCREG_total-1]=RCREG;  //依次把上位機(jī)來(lái)的數(shù)據(jù)存入數(shù)組緩沖區(qū)
        send_update=1;    //通知單片機(jī)目前正在接收數(shù)據(jù)
        send_cnt=0;        //及時(shí)喂狗,雖然main函數(shù)那邊不斷在累加,但是只要串口的數(shù)//據(jù)還沒(méi)發(fā)送完畢,那么它永遠(yuǎn)也長(zhǎng)不大,因?yàn)槊總(gè)中斷都被清零,很可憐。

        RCIE=1;

}

    if(TMR1IE==1&&TMR1IF==1)    //定時(shí)中斷
        {
         
              TMR1IF=0;     //定時(shí)中斷標(biāo)志位關(guān)閉
               TMR1ON=0;    //定時(shí)中斷開(kāi)關(guān)關(guān)閉

      if(voice_time_cnt)                      //控制蜂鳴器聲音的長(zhǎng)短
                 {
                       beep_dr=1;        //蜂鳴器響
                     --voice_time_cnt;        //蜂鳴器響的聲音長(zhǎng)短的計(jì)數(shù)延時(shí)
                 }
                else
                {
                   Asm(“nop”);   //添加此行空指令為了使else的內(nèi)容跟if的內(nèi)容對(duì)稱,意義////不大
beep_dr=0;      //蜂鳴器停止
                }

      TMR1H=0xFe;   //重新設(shè)置定時(shí)時(shí)間間隔
    TMR1L=0x00;
      TMR1ON=1;        //定時(shí)中斷開(kāi)關(guān)打開(kāi)
    }
}


void usart_service()  //串口服務(wù)程序,在main函數(shù)里
{

     if(send_update==1)  //說(shuō)明目前串口正在接收數(shù)據(jù),不要讀緩沖區(qū)數(shù)據(jù)
     {
        send_lock=1;     //開(kāi)自鎖標(biāo)志
        ++send_cnt;    //只要有數(shù)據(jù)接收,send_cnt每次都被串口中斷清零
        if(send_cnt>cnt_send)   //延時(shí)一段時(shí)間,確認(rèn)緩沖區(qū)沒(méi)有繼續(xù)接受數(shù)據(jù)
        {
            send_cnt=0;
            send_update=0;  
        }
     }
     Else  //說(shuō)明當(dāng)前沒(méi)有繼續(xù)接收數(shù)據(jù)了
     {
         if(send_lock==1)    //在數(shù)據(jù)已經(jīng)接收完畢,并且還沒(méi)有處理過(guò)數(shù)據(jù)的情況下
         {
            send_lock=0;   //處理一次就鎖起來(lái),不用每次都進(jìn)來(lái),除非有新接收的數(shù)據(jù)
           m_int=0;         //中間變量清零,用來(lái)統(tǒng)計(jì)處理了多少個(gè)剛剛接收的數(shù)據(jù)
           while(RCREG_read_total<RCREG_total)   //說(shuō)明還沒(méi)有把緩沖區(qū)的數(shù)據(jù)讀取完
            {
              CLRWDT();
              RCREG_buf_temp[m_int]=asy_recieve();
              If(m_int>=4)   //說(shuō)明接收了5個(gè)數(shù)據(jù)以上(2個(gè)有效數(shù)據(jù)加3個(gè)數(shù)據(jù)尾)
              {
         if(RCREG_buf_temp[m_int-2]==0xeb&&RCREG_buf_temp[m_int-1]==0x00&&RCREG_buf_temp[m_int]==0x55)  //數(shù)據(jù)尾”eb 00 55”判斷
                 {
                 if(RCREG_buf_temp[m_int-4]==0x01&& RCREG_buf_temp[m_int-3]==0x02)
                                        //有效數(shù)據(jù)是否為01 02的判斷
                  {
                              eusart_send(0x00); //串口發(fā)送一個(gè)填充的無(wú)效字節(jié)0x00,避免第一個(gè)字節(jié)丟失而引起的問(wèn)題
                              eusart_send(0xeb); //串口發(fā)送應(yīng)答的數(shù)據(jù)
eusart_send(0x00); //串口發(fā)送應(yīng)答的數(shù)據(jù)
eusart_send(0xaa); //串口發(fā)送應(yīng)答的數(shù)據(jù)
                               voice_time_cnt= cnt_voice_time;    //蜂鳴器響“滴”一聲就停

}
Else
{
                              eusart_send(0x00); //串口發(fā)送一個(gè)填充的無(wú)效字節(jié)0x00,避免第一個(gè)字節(jié)丟失而引起的問(wèn)題
eusart_send(0xeb); //串口發(fā)送應(yīng)答的數(shù)據(jù)
eusart_send(0x00); //串口發(fā)送應(yīng)答的數(shù)據(jù)
eusart_send(0x55); //串口發(fā)送應(yīng)答的數(shù)據(jù)

}

Break;   //跳出循環(huán)
                 }
              }
             m_int++;    //中間變量加1,
           }

         Buf_clear();   //把緩沖區(qū)的下標(biāo)清零,方便下一堆數(shù)據(jù)接收與處理

      
        }


     }


}

Void Buf_clear()   //把緩沖區(qū)的下標(biāo)清零
{
          RCREG_read_total =0;
          RCREG_total=0;
}
unsigned char asy_recieve()  //把串口緩沖區(qū)的數(shù)據(jù)一個(gè)個(gè)提取出來(lái)
{
      unsigned char RCREG_dt;

      ++RCREG_read_total;    //已經(jīng)讀出了多少個(gè)數(shù)據(jù)
      RCREG_dt=RCREG_buf[RCREG_read_total -1];

      if(RCREG_read_total >=RCREG_total) //只要把全部數(shù)據(jù)都讀完,馬上把緩沖區(qū)清零
         {
          RCREG_read_total =0;
          RCREG_total=0;

         }
      return RCREG_dt;

}


void eusart_send(unsigned char t_data) //串口發(fā)送一個(gè)字節(jié)的數(shù)據(jù)

{
unsigned int error_delay;
Rede_dr=1;     //流控IO,切換至發(fā)送數(shù)據(jù)的狀態(tài),準(zhǔn)備發(fā)送數(shù)據(jù),
//以下幾個(gè)空延時(shí)是必要的。
asm("nop");
asm("nop");
asm("nop");
asm("nop");

     TXREG=t_data;   //發(fā)送數(shù)據(jù)

     error_delay=0;//等待把數(shù)據(jù)發(fā)送完畢
     while(1)  //此處也可以直接用延時(shí)替代.
     {

        CLRWDT();
        if(TXIF==1)   //等待把數(shù)據(jù)發(fā)送完畢
        {
           break;
        }
        Else            
        {
          ++error_delay;
          if(error_delay>200)  //超時(shí)也要退出,不能死等
          {
              break;
          }
       }
     }

     Delay11(1);    //此處最玄機(jī),要特別注意。每發(fā)送完一個(gè)字節(jié),由于不同的項(xiàng)目,這//里的延時(shí)間隔都不一樣,讀者根據(jù)實(shí)際情況來(lái)改。這里最容易出問(wèn)題,必須要延時(shí),尤其是連續(xù)發(fā)送一堆數(shù)據(jù)的時(shí)候.讀者也可以改成計(jì)數(shù)延時(shí)的方式,有興趣的自己動(dòng)腦筋,程序框架需要改動(dòng)一點(diǎn)。

Rede_dr=0;   //流控IO,發(fā)送完數(shù)據(jù)之后,務(wù)必馬上把從機(jī)切換回接收數(shù)據(jù)的狀態(tài),把總//線釋放
}

//延時(shí)函數(shù)
void Delay11(unsigned int MS)
{
     unsigned char us,usn;
     while(MS!=0)           //for 12M
           {
               CLRWDT();
                 usn = 2;
                while(usn!=0)
                      {       CLRWDT();
                            us=0xf5;
                            while (us!=0){us--;};
                            usn--;
                      }
                MS--;
           }
}

(6)小結(jié):
             485跟TTL與232通訊唯一的編程區(qū)別就是多增加一個(gè)流控IO(本文是rede_dr)。此IO除了在發(fā)送數(shù)據(jù)的時(shí)候?yàn)楦唠娖,發(fā)送完數(shù)據(jù)之后,其他任意時(shí)刻都必須為低電平,起到釋放總線的作用。略微要注意的細(xì)節(jié)是,在流控IO從低電平切換到高電平的時(shí)候,不要馬上發(fā)送數(shù)據(jù),中間有必要加幾個(gè)空延時(shí)指令(asm(“nop”);)。
(未完待續(xù),下節(jié)更精彩,不要走開(kāi)哦)


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏8 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:25103 發(fā)表于 2018-2-6 21:08 | 只看該作者
謝謝謝謝分享。
回復(fù)

使用道具 舉報(bào)

板凳
ID:282739 發(fā)表于 2018-2-7 00:18 來(lái)自手機(jī) | 只看該作者
通俗易懂
回復(fù)

使用道具 舉報(bào)

地板
ID:384485 發(fā)表于 2018-8-9 10:37 | 只看該作者
不錯(cuò),學(xué)習(xí)了。
回復(fù)

使用道具 舉報(bào)

5#
ID:47652 發(fā)表于 2019-5-4 20:57 | 只看該作者
無(wú)下文了嗎?
回復(fù)

使用道具 舉報(bào)

6#
ID:617813 發(fā)表于 2020-8-18 20:29 | 只看該作者
雖然看不懂,還是要支持一下
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 在线视频91| 琪琪午夜伦伦电影福利片 | 伊人导航 | 在线观看免费国产 | 亚洲一区二区三区免费在线观看 | 性生活毛片 | 国产成人精品午夜视频免费 | 国产片网站 | 欧美精品久久久 | 成人免费视频观看视频 | 亚洲综合色视频在线观看 | 综合色久 | 精品免费在线 | 国产美女网站 | 日日干天天操 | 精品网站999www | 午夜在线视频一区二区三区 | 中文字幕男人的天堂 | 欧美亚洲国产一区二区三区 | 成人免费网站视频 | 国产精品久久影院 | 成人国产免费观看 | 日韩中文一区二区三区 | 一区二区在线看 | 亚洲精品9999 | 91精品国产综合久久精品 | 免费日韩av | 91小视频在线 | 精品视频在线观看 | 黄视频网站免费观看 | 亚洲日韩视频 | 国产情侣啪啪 | 日韩at | 日韩欧美高清 | 国产一级一片免费播放 | 黄色大片免费观看 | 99九九久久 | 日批的视频 | 天天操精品视频 | 久久久91精品国产一区二区三区 | 久草在线中文888 |