串口通信用戶層協(xié)議編制技巧與實(shí)現(xiàn)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
前言
協(xié)議就是約束雙方通信的一種規(guī)范,只有嚴(yán)格遵守這種協(xié)議的設(shè)備才能進(jìn)行相互的通信。比如串口通信協(xié)議,必須包含起始位、主體數(shù)據(jù)、校驗(yàn)位及停止位,雙方需要約定一致的數(shù)據(jù)包格式才能正常收發(fā)數(shù)據(jù)的有關(guān)規(guī)范。在串口通信中,常用的協(xié)議包括RS-232、RS-422和RS-485等。與此類似還有I2C通信協(xié)議。但是往往這些只是底層的通信協(xié)議,很多外設(shè)都已經(jīng)集成好了,只需配置相關(guān)的寄存器就能夠得到數(shù)據(jù)主體了。根本不需要用戶去關(guān)注協(xié)議的組成,而我這篇文章主要講的是用戶層協(xié)議的編制原理和實(shí)現(xiàn)手段。
什么是用戶層協(xié)議?簡(jiǎn)單的說(shuō)就是用戶自定義的某種數(shù)據(jù)格式,有包頭,心跳幀,命令字,數(shù)據(jù)幀,校驗(yàn)幀,結(jié)尾幀等。當(dāng)需要接受數(shù)據(jù)幀的設(shè)備收到一幀數(shù)據(jù)后,進(jìn)行解包,并且校驗(yàn),將有用的數(shù)據(jù)幀提取出來(lái),然后根據(jù)具體的數(shù)據(jù)幀執(zhí)行相應(yīng)的動(dòng)作。這就是用戶協(xié)議編制的基本原理。下面來(lái)具體講一下這個(gè)過(guò)程。
什么情況下會(huì)用到用戶層協(xié)議
做嵌入式肯定會(huì)遇到兩個(gè)模塊進(jìn)行通信的情景,比如兩個(gè)mcu或者兩個(gè)mpu,mcu與mpu之間的交互,往往涉及到多種邏輯。不像單功能模塊一樣只做一件事,可能涉及到許多的交互,比如狀態(tài)上傳,控制邏輯等等。舉個(gè)例子來(lái)說(shuō),mcu要和智能電表進(jìn)行交互。此時(shí)mcu不僅可以讀取到電表電能,電量等信息,也可以控制空開(kāi)的開(kāi)合閘。同時(shí),電表也可能主動(dòng)向mcu上傳一些報(bào)警信息。針對(duì)這樣的情況,就需要制定電表協(xié)議,電表按照這樣的協(xié)議進(jìn)行數(shù)據(jù)收發(fā),mcu也需要有發(fā)出或者解析這樣格式的能力。
從modbus協(xié)議開(kāi)始分析
首先Modbus是一種工業(yè)上常用的通信協(xié)議,其中包含RTU,ASCII,TCP等等,其中MODBUS-RTU比較容易實(shí)現(xiàn)。一般的文檔寫的都比較復(fù)雜,將用最簡(jiǎn)單的方式講述一下這個(gè)過(guò)程。我們不分析協(xié)議內(nèi)容,只講協(xié)議的實(shí)現(xiàn)。
假如我們從串口收到了一條報(bào)文:01 06 00 01 00 17 98 04。這一串16進(jìn)制的數(shù)據(jù)被存放在一個(gè)buf數(shù)組中,首先編程人員要理解這一串?dāng)?shù)據(jù)的含義,所以先進(jìn)行拆包。
01 | 06 | 00 ?01 | 00 ?17 | 98 ?04 |
---|---|---|---|---|
從機(jī)地址 | 功能號(hào) | 數(shù)據(jù)地址 | 數(shù)據(jù) | CRC校驗(yàn) |
從機(jī)地址:
一條總線上可能掛載多個(gè)設(shè)備(對(duì)于串口一般是RS-485)。所以需要廣播這個(gè)消息是發(fā)送給哪個(gè)設(shè)備的,01表示告訴的是地址編號(hào)為01的設(shè)備。
功能號(hào):
顧名思義,功能號(hào)就是通知該設(shè)備執(zhí)行什么動(dòng)作,比如控制LED燈,或者打開(kāi)某個(gè)寄存器等等。每一項(xiàng)動(dòng)作都是有特定的功能號(hào)。
數(shù)據(jù)地址:
數(shù)據(jù)地址就是操作的設(shè)備的寄存器,這個(gè)在modbus協(xié)議特有,暫時(shí)不做分析。自定義協(xié)議時(shí)也很少用到。
數(shù)據(jù):
這部分就是將特定的數(shù)據(jù)交給設(shè)備,比如可以自定義開(kāi)燈動(dòng)作為數(shù)據(jù)”1“,關(guān)燈動(dòng)作為數(shù)據(jù)"0"。設(shè)備接收到特定的功能號(hào),然后解析這個(gè)數(shù)據(jù),就能夠執(zhí)行具體的動(dòng)作了。
CRC校驗(yàn):
校驗(yàn)的目的就是為了保證這一包數(shù)據(jù)的完整性,發(fā)送過(guò)來(lái)的數(shù)據(jù)是主機(jī)已經(jīng)算好了CRC校驗(yàn)碼,然后設(shè)備收到數(shù)據(jù)后,再次將前面的數(shù)據(jù)算一遍,然后與接收到的CRC校驗(yàn)碼進(jìn)行對(duì)比,如果數(shù)據(jù)是準(zhǔn)確的,就證明數(shù)據(jù)完整且傳輸過(guò)程中沒(méi)有出現(xiàn)錯(cuò)誤數(shù)據(jù)。然后才能開(kāi)始執(zhí)行動(dòng)作。如果校驗(yàn)有誤,應(yīng)該丟棄該包數(shù)據(jù),請(qǐng)求重傳,以免造成不可預(yù)知的后果。
主機(jī)發(fā)送完這串字符串后,可以看到編號(hào)為01的從機(jī)開(kāi)始執(zhí)行動(dòng)作了。這只是一個(gè)最簡(jiǎn)單的例子,但是也能說(shuō)明白用戶層協(xié)議是怎么一回事。
開(kāi)始一個(gè)簡(jiǎn)單的應(yīng)用
協(xié)議制定
在做項(xiàng)目之前,先設(shè)計(jì)需求,假如我們有這樣一個(gè)需求:現(xiàn)在做了一個(gè)從機(jī)的板子,上邊只有一個(gè)按鍵和一個(gè)led。這塊板子和另一塊主機(jī)通過(guò)串口方式進(jìn)行連接。當(dāng)主機(jī)發(fā)送數(shù)據(jù)包:命令碼0x01,控制碼0x01的數(shù)據(jù)過(guò)來(lái)時(shí),點(diǎn)亮模塊板上的LED,當(dāng)主機(jī)發(fā)送數(shù)據(jù)包:命令碼0x01,控制碼0x00的數(shù)據(jù)過(guò)來(lái)時(shí),熄滅板子上的LED。當(dāng)按鍵按下時(shí),板子主動(dòng)上傳:命令碼0x02,控制碼0x00的數(shù)據(jù)。
功能已經(jīng)明確,接著就開(kāi)始寫協(xié)議文檔。
協(xié)議文檔就是約束通信雙方的通信格式,一般用word文檔比較正式也比較好交接。首先可以規(guī)定串口波特率,有無(wú)校驗(yàn)位,停止位。然后開(kāi)始規(guī)范包格式。根據(jù)這個(gè)簡(jiǎn)單的應(yīng)用需求,我可以先制定包格式如下
包頭 | 命令碼 | 控制碼 | CRC校驗(yàn) |
---|---|---|---|
2字節(jié) | 1字節(jié) | 1字節(jié) | 2字節(jié) |
制定完基本格式,然后一條一條的完善這個(gè)協(xié)議
LED控制(0x01)
數(shù)據(jù)格式
包頭 | 命令碼 | 控制碼 | CRC校驗(yàn) |
---|---|---|---|
0xAA 0xBB | 0x01 | 0x00(關(guān)燈) ?0x01(熄燈) | -- |
說(shuō)明:
當(dāng)收到命令為0x01的命令碼時(shí),需要判斷控制碼執(zhí)行的動(dòng)作,然后進(jìn)行關(guān)燈和開(kāi)燈的操作。
按鍵狀態(tài)上傳(0x02)
數(shù)據(jù)格式
包頭 | 命令碼 | 控制碼 | CRC校驗(yàn) |
---|---|---|---|
0xAA 0xBB | 0x02 | 0x00 | -- |
說(shuō)明:
當(dāng)按鍵按下時(shí),模塊主動(dòng)向主機(jī)發(fā)送命令碼為0x02的數(shù)據(jù)包,其他情況下不會(huì)發(fā)送。
程序分析
可以用在stm32f103cbt6上進(jìn)行實(shí)驗(yàn),在寫代碼時(shí),先設(shè)計(jì)程序的框架。用到的資源:
1.串口
2.rt-thread操作系統(tǒng)
3.按鍵中斷
4.led
如果用到了rt-thread系統(tǒng),就不用從頭開(kāi)始,我們只專注于協(xié)議部分的實(shí)現(xiàn)就好。點(diǎn)燈操作可以采用pin驅(qū)動(dòng)模型。程序部分的設(shè)計(jì)我將在下一篇文章中詳細(xì)介紹。
總結(jié)
用戶協(xié)議的制定是非常重要,也是非常的關(guān)鍵的,前面很多人都不理解協(xié)議,這篇文章寫得很基礎(chǔ),也非常容易理解,希望能夠給大家一點(diǎn)幫助。下一篇文章將從協(xié)議的去耦合性以及可擴(kuò)展性上詳細(xì)設(shè)計(jì)一下代碼。