日韩一区二区三区精品,欧美疯狂xxxxbbbb牲交,热99re久久免费视精品频,人妻互换 综合,欧美激情肉欲高潮视频

歷史上的今天

今天是:2024年12月27日(星期五)

正在發(fā)生

2021年12月27日 | STM8單片機(jī)串口同時(shí)識(shí)別自定義協(xié)議和Modbus協(xié)議

發(fā)布者:Blissful567 來源: eefocus關(guān)鍵字:STM8  單片機(jī)  自定義協(xié)議  Modbus協(xié)議 手機(jī)看文章 掃描二維碼
隨時(shí)隨地手機(jī)看文章

??在單片機(jī)開發(fā)中,串口是最常用的和外界交換數(shù)據(jù)的渠道,要使用串口,那必不可少的就是通信協(xié)議,通信協(xié)議就是單片機(jī)和外界通信的語(yǔ)言,要想正常和其他設(shè)備正常交流,首先語(yǔ)言必須相通。


??在實(shí)際開發(fā)過程中由于各種原因,導(dǎo)致很多時(shí)候單片機(jī)和外界其他設(shè)備協(xié)議不兼容,在使用的時(shí)候就比較麻煩。比如單片機(jī)要和兩個(gè)設(shè)備通信,但是這兩個(gè)設(shè)備的通信協(xié)議的不一樣,在使用時(shí)單片機(jī)就必須使用兩個(gè)串口分別和兩個(gè)設(shè)備通信。如果這兩個(gè)設(shè)備同時(shí)使用時(shí)還不感覺到資源浪費(fèi),如果每次只接一個(gè)設(shè)備,那么另一個(gè)串口也不能作為其他功能使用,還得留著備用。這樣的話單片機(jī)的資源就被白白浪費(fèi)掉了。于是想著能不能在一個(gè)串口上支持兩個(gè)協(xié)議,讓單片機(jī)自動(dòng)去識(shí)別接收到的數(shù)據(jù)使用的是哪個(gè)協(xié)議。


??一般使用通信協(xié)議接收數(shù)據(jù)時(shí),都需要通過判斷數(shù)據(jù)頭和數(shù)據(jù)尾來確定什么時(shí)候開始接收數(shù)據(jù),什么時(shí)候停止接收數(shù)據(jù)。但是如果要兼容多個(gè)協(xié)議的話,就不能使用這個(gè)方式去接收數(shù)據(jù)。必須先將一組數(shù)據(jù)接收完畢,然后根據(jù)數(shù)據(jù)的特點(diǎn)去分析數(shù)據(jù)使用的是哪個(gè)協(xié)議。


??首先要實(shí)現(xiàn)的就是如何判斷一組數(shù)據(jù)是否接收完畢。


??實(shí)現(xiàn)的大概思路就是,單片機(jī)使用中斷去接收數(shù)據(jù),同時(shí)記錄接收到的數(shù)據(jù)長(zhǎng)度,在主函數(shù)中循環(huán)的去讀取串口接收到的字符長(zhǎng)度,如果超過一定時(shí)間之后,串口中接收到的數(shù)據(jù)長(zhǎng)度沒有發(fā)生變化,就說明一組數(shù)據(jù)接收完畢了。


??比如在主函數(shù)中讀取到了當(dāng)前串口數(shù)據(jù)長(zhǎng)度不為0,說明此時(shí)串口正在接收數(shù)據(jù),此時(shí)記錄下當(dāng)前串口數(shù)據(jù)長(zhǎng)度,延時(shí)一段時(shí)間再去讀取一次串口接收數(shù)據(jù)的長(zhǎng)度,如果此時(shí)數(shù)據(jù)長(zhǎng)度和上一次數(shù)據(jù)長(zhǎng)度一樣,說明串口接收數(shù)據(jù)結(jié)束了,就可以去處理接收到的數(shù)據(jù)了,如果此時(shí)數(shù)據(jù)長(zhǎng)度和上一次的數(shù)據(jù)長(zhǎng)度不一樣,說明串口正在接收數(shù)據(jù),接收數(shù)據(jù)還未結(jié)束,不能去處理數(shù)據(jù)。


??下面就通過一個(gè)工程案例來演示一個(gè)串口兼容兩種協(xié)議的使用方法。


??設(shè)備默認(rèn)使用的是自定義協(xié)議,協(xié)議格式如下:

[ 頭1 ] (0xA5) [ 頭2 ] (0x5A) [ 地址 ] [ 命令 ] [ 數(shù)據(jù)高位 ] [ 數(shù)據(jù)低位 ] [ 尾1 ] (0x55) [尾2] (0xAA)


??后來設(shè)備需要和市場(chǎng)上其他的工業(yè)設(shè)備對(duì)接,而大多數(shù)工業(yè)設(shè)備使用的都是Modbus協(xié)議,如果直接將設(shè)備協(xié)議修改為Modbus協(xié)議的話,那么好多舊設(shè)備和新設(shè)備就會(huì)不兼容,為了兼容舊設(shè)備同時(shí)又要對(duì)接其他工業(yè)設(shè)備,那么設(shè)備要在自定義協(xié)議的基礎(chǔ)上兼容Modbus協(xié)議。Modbus協(xié)議格式如下:


[ 地址 ] [ 功能碼 ] [ 起始地址高 ] [ 起始地址低] [ 總寄存器數(shù)高 ] [ 總寄存器數(shù)低 ] [ CRC低 ] [ CRC高 ]


??下面分析這兩種協(xié)議的特點(diǎn)。


??首先看自定義協(xié)議,自定義協(xié)議的數(shù)據(jù)長(zhǎng)度是固定的,同時(shí)數(shù)據(jù)的開始和結(jié)尾都是由兩個(gè)字節(jié)標(biāo)識(shí)。那么識(shí)別自定義協(xié)議就和容易了,直接判斷數(shù)據(jù)頭和數(shù)據(jù)為就行了。當(dāng)串口接收一組數(shù)據(jù)結(jié)束后,判斷接收到的數(shù)據(jù) 是不是以 0xA5和0X5A 開頭,同時(shí)以 0x55 和0xAA結(jié)尾。如果是那么就使用自定義協(xié)議去解析數(shù)據(jù)。


??接下來分析Modbus協(xié)議,由于設(shè)備都是單獨(dú)使用的,不需要級(jí)聯(lián),所以設(shè)備的地址是固定的0x01,同時(shí)由于設(shè)備的功能比較簡(jiǎn)單,所以在使用Modubus協(xié)議的時(shí)候,只用到了讀保持寄存器(0x03)和寫保持寄存器(0x06)這兩個(gè)功能,所以Modbus的數(shù)據(jù)開頭只有兩種情況 0x01 0x03 和 0x01 0x06,如果數(shù)據(jù)開頭是這兩種情況,那么就使用Modbus協(xié)議去解析數(shù)據(jù)。


??通過對(duì)協(xié)議的分析,思路已經(jīng)很清晰了,接下來使用代碼來實(shí)現(xiàn)。


struct uart_info

{

    u8 cnt;

    u8 rec_buf[10];

};


struct uart_info uart1;


//在Library Options中將Printf formatter改成Large

//重新定向putchar函數(shù),使支持printf函數(shù)

int putchar( int ch )

{

    while( !( UART1_SR & 0X80 ) ); //循環(huán)發(fā)送,直到發(fā)送完畢

    UART1_DR = ( u8 ) ch;

    return ch;

}

static void uart_io_init( void )

{

    PD_DDR |= ( 1 << 5 ); //輸出模式 TXD

    PD_CR1 |= ( 1 << 5 ); //推挽輸出

    PD_DDR &= ~( 1 << 6 ); //輸入模式 RXD

    PD_CR1 &= ~( 1 << 6 ); //浮空輸入

}

//波特率最大可以設(shè)置為38400

void uart_init( unsigned int baudrate )

{

    unsigned int baud;


    uart_io_init();


    baud = 16000000 / baudrate;

    UART1_CR1 = 0;

    UART1_CR2 = 0;

    UART1_CR3 = 0;

    UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );

    UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );

    UART1_CR2_bit.REN = 1;        //接收使能

    UART1_CR2_bit.TEN = 1;        //發(fā)送使能

    UART1_CR2_bit.RIEN = 1;       //接收中斷使能

}



//接收中斷函數(shù) 中斷號(hào)18

#pragma vector = 20                             // IAR中的中斷號(hào),要在STVD中的中斷號(hào)上加2

__interrupt void UART1_Handle( void )

{

    unsigned char res = 0;

    UART1_SR &= ~( 1 << 5 );                    //RXNE 清零


    res = UART1_DR;

    if( uart1.cnt < 9 )

        uart1.rec_buf[uart1.cnt++] = res;

}


??定義一個(gè)結(jié)構(gòu)體來存儲(chǔ)接收到的數(shù)據(jù)長(zhǎng)度和數(shù)據(jù),由于自定義協(xié)議和Modbus協(xié)議最長(zhǎng)的數(shù)據(jù)只有8個(gè)字節(jié),所以這里的數(shù)組長(zhǎng)度設(shè)置10就可以了。下面初始化串口使用的IO口,設(shè)置數(shù)據(jù)位和波特率。最后在中斷中接收數(shù)據(jù)并存儲(chǔ)到數(shù)組中。


??接下來在編寫一個(gè)函數(shù)用來分析接收到的數(shù)據(jù)。


//檢測(cè)串口數(shù)據(jù)

void read_uart( void )

{

    static u8 recevie_buf[10];

    static u8 recevie_cnt = 0;

    u8 i = 0;


    delay_ms( 10 ); //隔一段時(shí)間檢測(cè)一次串口數(shù)據(jù)長(zhǎng)度,如果數(shù)據(jù)長(zhǎng)度沒有發(fā)生變化說明串口接收數(shù)據(jù)完成


    if( ( uart1.cnt != recevie_cnt ) && ( uart1.cnt > 6 ) )

    {

        recevie_cnt = uart1.cnt;

        for( i = 0; i < recevie_cnt; i++ )            //拷貝數(shù)據(jù)

        {

            recevie_buf[i] = uart1.rec_buf[i];

        }

        //根據(jù)接收到的數(shù)據(jù)區(qū)分協(xié)議

        //自定義協(xié)議

        if( ( recevie_buf[0] == 0xA5 ) && ( recevie_buf[1] == 0x5A ) )

        {

            self_define_protocol( recevie_buf, recevie_cnt );

        }

        //modbus協(xié)議

        if( ( recevie_buf[0] == 0x01 ) && ( ( recevie_buf[1] == 0x03 ) || ( recevie_buf[1] == 0x06 ) ) )

        {

            modbus_protocol( recevie_buf, recevie_cnt );

        }


        //清空數(shù)組

        for( i = 0; i < 10; i++ )

        {

            uart1.rec_buf[i] = 0;

            recevie_buf[i] = 0;

        }

        uart1.cnt = recevie_cnt  = 0;

    }

}


??由于此設(shè)備中兩種協(xié)議的數(shù)據(jù)長(zhǎng)度都比較小,所以這里并沒有分兩次去判斷數(shù)據(jù)長(zhǎng)度,然后根據(jù)數(shù)據(jù)長(zhǎng)度來判斷數(shù)據(jù)接收是否完畢。只是延時(shí)10ms之后去判斷數(shù)據(jù)長(zhǎng)度,當(dāng)接收的數(shù)據(jù)長(zhǎng)度大于6時(shí),說明數(shù)據(jù)基本已經(jīng)接收完成了。由于數(shù)據(jù)的最大長(zhǎng)度是8,所以接收的數(shù)據(jù)長(zhǎng)度大于6時(shí),基本數(shù)據(jù)已經(jīng)接收完了,在中斷中接收8個(gè)數(shù)據(jù)速度還是非??斓?。如果數(shù)據(jù)量比較大,數(shù)據(jù)比較長(zhǎng)時(shí),最好還是通過數(shù)據(jù)長(zhǎng)度去判斷比較可靠。這里為了編寫方便,就簡(jiǎn)單的時(shí)候延時(shí)去判斷了。


??當(dāng)串口接收的數(shù)據(jù)長(zhǎng)度大于6時(shí),說明一組數(shù)據(jù)已經(jīng)接收完畢了,此時(shí)需要將串口接收的數(shù)據(jù)拷貝一份出來在使用。那為什么要將數(shù)據(jù)拷貝出來,而不是直接使用串口接收緩存區(qū)的數(shù)據(jù)呢?這是為了防止在處理數(shù)據(jù)的過程中國(guó),串口又接收到了新的數(shù)據(jù),這樣新數(shù)據(jù)就會(huì)將舊的數(shù)據(jù)覆蓋掉,有可能導(dǎo)致數(shù)據(jù)異常。為了數(shù)據(jù)的安全性將數(shù)據(jù)拷貝一份使用,即使在處理數(shù)據(jù)過程中串口緩存區(qū)的數(shù)據(jù)發(fā)生了變化,也不會(huì)破壞掉上一次接收的數(shù)據(jù)。


??數(shù)據(jù)拷貝結(jié)束后,就根據(jù)數(shù)據(jù)的特點(diǎn)來判斷當(dāng)前接收到的數(shù)據(jù)使用的是哪種協(xié)議。如果接收到的數(shù)據(jù)前兩個(gè)是 0xA5和0x5A那么就直接調(diào)用自定義協(xié)議處理函數(shù),如果前面的數(shù)據(jù)是0x01和0x03或者是0x01和0x06那么就直接調(diào)用Modbus協(xié)議去處理函數(shù)。


??接下來就可以單獨(dú)編寫兩個(gè)函數(shù)分別處理這這種協(xié)議。


//處理自定義協(xié)議

// [頭1](0xA5) [頭2](0x5A) [地址]  [命令]  [數(shù)據(jù)高位] [數(shù)據(jù)低位]  [尾1](0x55) [尾2](0xAA)

//A5 5A 00 01 01 90 55 AA

void self_define_protocol( u8 arr[], u8 size )

{


    if( ( arr[0] == 0xA5 ) && ( arr[1] == 0x5A ) && ( arr[6] == 0x55 ) && ( arr[7] == 0xAA ) )

    {

//根據(jù)命令值執(zhí)行不同的動(dòng)作

    }

}


//處理modbus協(xié)議

//[地址][功能碼][起始地址高][起始地址低][總寄存器數(shù)高][總寄存器數(shù)低][CRC低][CRC高]

//01 03 00 00 00 01 84 0A

//01 06 00 00 03 E8 89 74

void modbus_protocol( u8 arr[], u8 size )

{

    //調(diào)用 modbus相關(guān)處理代碼

}


??將數(shù)據(jù)協(xié)議識(shí)別出來之后,就可以按照通常處理協(xié)議的方式去處理數(shù)據(jù)了。最后在主函數(shù)中循環(huán)的調(diào)用數(shù)據(jù)查詢函數(shù),檢查串口是否有接收到數(shù)據(jù)。


void main( void )

{

   

    __asm( "sim" );                             //禁止中斷

    SysClkInit();

    delay_init( 16 );

    LED_GPIO_Init();

    uart_init( 9600 );

    __asm( "rim" );                             //開啟中斷

    while( 1 )

    {

        read_uart();

    }

}


??接下來分別用這兩種協(xié)議測(cè)試代碼。

在這里插入圖片描述

??使用串口助手發(fā)送自定義協(xié)議時(shí),代碼就會(huì)調(diào)用自定義協(xié)議處理函數(shù)。

在這里插入圖片描述

??發(fā)送Modbus協(xié)議時(shí),代碼就會(huì)調(diào)用Modbus協(xié)議處理函數(shù)。


??這樣根據(jù)協(xié)議的特點(diǎn)可以通過代碼自動(dòng)去識(shí)別協(xié)議的類型,用一個(gè)串口就可以實(shí)現(xiàn)不同協(xié)議的解析。按照同樣的方法還可以解析更多的協(xié)議。當(dāng)前在不同的協(xié)議使用的波特率要相同,否則波特率不同解析出來的數(shù)據(jù)就會(huì)出現(xiàn)錯(cuò)誤,導(dǎo)致協(xié)議解析失敗。

關(guān)鍵字:STM8  單片機(jī)  自定義協(xié)議  Modbus協(xié)議 引用地址:STM8單片機(jī)串口同時(shí)識(shí)別自定義協(xié)議和Modbus協(xié)議

上一篇:IAR軟件中直接查看編譯后代碼大小
下一篇:在STM8單片機(jī)中自己實(shí)現(xiàn) printf()函數(shù)功能

推薦閱讀

兩個(gè)文件分別在stm32f4xx.h(中斷名)CORE—startup_stm32f40_41xxx.s(中斷函數(shù)名)#if defined (STM32F40_41xxx) CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ CAN1_RX0_IRQn = 20, /*!< CAN1 RX0...
2017年,英特諾攜手系統(tǒng)集成商REMA TEC集團(tuán),對(duì)德國(guó)家用產(chǎn)品專家WENKO公司位于德國(guó)巴爾的物流中心進(jìn)行了全方位的現(xiàn)代化改造,該擴(kuò)建項(xiàng)目幾乎用到了英特諾全線產(chǎn)品解決方案。為了更好地展示這整套解決方案,并用于員工培訓(xùn),英特諾在位于德國(guó)巴爾的英特諾學(xué)院里設(shè)置了一座完全用樂高積木搭建的微縮模型,生動(dòng)地還原了這個(gè)物流中心。 繽紛多彩的樂高積木向...
網(wǎng)友都說新浪眾測(cè)不講武德,數(shù)碼新品說發(fā)就發(fā),新奇好物說送就送。不怕你沒想法,就怕你不行動(dòng),加入新浪眾測(cè),和數(shù)碼大V一起聊產(chǎn)品,談體驗(yàn)!12月25日消息,博主@阿寬akuan曝光了iQOO 7線下預(yù)售信息。從門店曝光的海報(bào)來看,iQOO 7搭載高通驍龍888旗艦處理器,支持120W超快閃充,這是業(yè)界第一款支持百瓦級(jí)快充的驍龍888旗艦。在此之前,iQOO 5 Pro已...
繼成功推出超級(jí)充電樁HYC150和HYC300之后,意大利公司alpitronic近日再次宣布推出業(yè)界領(lǐng)先的50 kW直流充電樁HYC50。該產(chǎn)品是此功率范圍內(nèi)首款壁掛式直流充電樁,具有兩個(gè)充電端口,能以50 kW的充電功率為電動(dòng)汽車快速充電,或以25 kW的充電功率同時(shí)給兩輛電動(dòng)汽車進(jìn)行充電。該產(chǎn)品通過采用英飛凌科技股份公司(FSE: IFX / OTCQX: IFNNY)的EasyPACK...

史海拾趣

小廣播
設(shè)計(jì)資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機(jī)文章

 
EEWorld訂閱號(hào)

 
EEWorld服務(wù)號(hào)

 
汽車開發(fā)圈

 
機(jī)器人開發(fā)圈

電子工程世界版權(quán)所有 京ICP證060456號(hào) 京ICP備10001474號(hào)-1 電信業(yè)務(wù)審批[2006]字第258號(hào)函 京公網(wǎng)安備 11010802033920號(hào) Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved