本軟件可分為以下五個(gè)部分,第一部分是狀態(tài)設(shè)定和位置監(jiān)測模塊,其中發(fā)電車的行車和發(fā)電狀態(tài)的設(shè)定是由一個(gè)船型開關(guān)決定的,當(dāng)開關(guān)閉合時(shí),為發(fā)電狀態(tài),反之為行車狀態(tài),步進(jìn)電機(jī)控制油門的開合,但考慮到異常情況,用一個(gè)常開型開關(guān)實(shí)時(shí)監(jiān)測步進(jìn)電機(jī)是否轉(zhuǎn)動到了極限位置;第二部分為LCD液晶顯示,主要將串口通信得到的發(fā)電機(jī)輸出電的頻率fre、發(fā)電機(jī)輸出電的電流current以及控制步進(jìn)電機(jī)轉(zhuǎn)動快慢的方波頻率mtfre這三個(gè)參數(shù)實(shí)時(shí)顯示在LCD液晶上。第三部分為單片機(jī)與發(fā)電機(jī)上的儀表之間的Modbus-RTU串口通信,通過軟件的實(shí)時(shí)發(fā)送和接收可以得到發(fā)電機(jī)輸出電的頻率和電流等參數(shù);第四部分為利用定時(shí)中斷實(shí)現(xiàn)頻率可調(diào)的方波輸出,根據(jù)發(fā)電機(jī)的頻率與額定頻率50HZ的比較來決定輸出方波的頻率,而方波的頻率之所以可以實(shí)時(shí)變化的原因是通過改變定時(shí)器的初值來實(shí)現(xiàn)的,改變了定時(shí)器的初值,則改變了定時(shí)時(shí)間的大小,在每一次溢出中斷產(chǎn)生時(shí),置反某一個(gè)I/O口的電平,從而實(shí)現(xiàn)了頻率可調(diào)的方波輸出;第五部分是步進(jìn)電機(jī)的控制模塊,這里需要了解步進(jìn)電機(jī)的工作原理以及驅(qū)動原理,這個(gè)系統(tǒng)采用專用驅(qū)動器驅(qū)動步進(jìn)電機(jī)進(jìn)行轉(zhuǎn)動,驅(qū)動器上的輸入信號包括脈沖信號、方向信號和使能信號,只要將這三種信號按照要求進(jìn)行輸入,驅(qū)動器就可以控制步進(jìn)電機(jī)進(jìn)行正常轉(zhuǎn)動; 整體的軟件功能是在幾個(gè)模塊函數(shù)的相互聯(lián)系相互調(diào)用的情況下實(shí)現(xiàn)的,每一個(gè)模塊有自己的專用函數(shù),在編寫程序時(shí),用特殊的專用的函數(shù)名來對各個(gè)模塊的函數(shù)進(jìn)行命名,這樣方便查找和修改。 我覺得大家感興趣的應(yīng)該是MODBUS通信,因?yàn)橛玫竭@種通信的一般是工業(yè)儀器儀表,所以我想給大家介紹一下,我在編寫這一塊時(shí)的問題和解決辦法。 1. Modbus-RTU串口通信模塊通信協(xié)議:該模塊是整個(gè)軟件的重點(diǎn)部分,實(shí)現(xiàn)發(fā)電車上的電力儀表與單片機(jī)之間的實(shí)時(shí)通信,得到系統(tǒng)所要提取的發(fā)電機(jī)的頻率和電流。Modbus協(xié)議是應(yīng)用于電子控制器上的一種通用語言。通過此協(xié)議,控制器相互之間、控制器經(jīng)由網(wǎng)絡(luò)和其它設(shè)備之間可以通信。發(fā)電機(jī)上的電力儀表本身規(guī)定用的通信協(xié)議為RS485接口Modbus-RTU通信協(xié)議。當(dāng)在Modbus網(wǎng)絡(luò)上通信時(shí),此協(xié)議決定了每個(gè)控制器必須要知道它們的設(shè)備地址,識別按地址發(fā)來的消息,決定要產(chǎn)生何種行為。如果需要回應(yīng),控制器將生成反饋信息并通過Modbus協(xié)議發(fā)送。 在Modbus協(xié)議中,典型的主設(shè)備可以為:主機(jī)和可編程儀表。典型的從設(shè)備主要為:可編程控制器。在系統(tǒng)中,單片機(jī)為主設(shè)備,發(fā)電機(jī)上的電力儀表為從設(shè)備。主設(shè)備可單獨(dú)和從設(shè)備通信,也能以廣播方式和所有從設(shè)備通信,如果單獨(dú)通信,從設(shè)備返回一消息作為回應(yīng),如果是以廣播方式查詢的,則從設(shè)備們不作任何回應(yīng)。Modbus協(xié)議建立了主設(shè)備查詢的格式:設(shè)備(或廣播)地址、功能代碼、所有要發(fā)送的數(shù)據(jù)、一錯(cuò)誤檢測域。從設(shè)備回應(yīng)消息也由Modbus協(xié)議構(gòu)成,包括要確認(rèn)行動的域、任何要返回的數(shù)據(jù)、和一錯(cuò)誤檢測域。如果在消息接收過程中發(fā)生一錯(cuò)誤,或從設(shè)備不能執(zhí)行其命令,從設(shè)備將建立一錯(cuò)誤消息并把它作為回應(yīng)發(fā)送出去。 (1)查詢 查詢消息中的功能代碼告之被選中的從設(shè)備要執(zhí)行何種功能。數(shù)據(jù)段包含了從設(shè)備要執(zhí)行功能的任何附加信息。例如功能代碼03是要求從設(shè)備讀保持寄存器并返回它們的內(nèi)容。數(shù)據(jù)段必須包含要告之從設(shè)備的信息:從何寄存器開始讀及要讀的寄存器數(shù)量。錯(cuò)誤檢測域?yàn)閺脑O(shè)備提供了一種驗(yàn)證消息內(nèi)容是否正確的方法。 查詢數(shù)據(jù)包結(jié)構(gòu)如下: 從機(jī) 地址 | | | | 數(shù)據(jù)字節(jié)長度高字節(jié) | 數(shù)據(jù)字節(jié)長度低字節(jié) | | |
(2)回應(yīng) 如果從設(shè)備產(chǎn)生一正常的回應(yīng),在回應(yīng)消息中的功能代碼是在查詢消息中的功能代碼的回應(yīng)。數(shù)據(jù)段包括了從設(shè)備收集的數(shù)據(jù):像寄存器值或狀態(tài)。如果有錯(cuò)誤發(fā)生,功能代碼將被修改以用于指出回應(yīng)消息是錯(cuò)誤的,同時(shí)數(shù)據(jù)段包含了描述此錯(cuò)誤信息的代碼。錯(cuò)誤檢測域允許主設(shè)備確認(rèn)消息內(nèi)容是否可用。 回應(yīng)數(shù)據(jù)包結(jié)構(gòu): (3)兩種傳輸方式 控制器能設(shè)置為兩種傳輸模式:ASCII或RTU中的任何一種在標(biāo)準(zhǔn)的Modbus網(wǎng)絡(luò)通信。用戶選擇想要的模式,包括串口通信參數(shù)(波特率、校驗(yàn)方式等),在配置每個(gè)控制器的時(shí)候,在一個(gè)Modbus網(wǎng)絡(luò)上的所有設(shè)備都必須選擇相同的傳輸模式和串口參數(shù)。相對來說,RTU模式傳輸效率更高,因此,在當(dāng)前普遍的生產(chǎn)環(huán)境中RTU模式獲得了廣泛應(yīng)用,而ASCII模式只作為特殊情況下的可選項(xiàng)。 ASCII模式 當(dāng)控制器設(shè)為在Modbus網(wǎng)絡(luò)上以ASCII(美國標(biāo)準(zhǔn)信息交換代碼)模式通信,一個(gè)信息中的每8個(gè)比特作為2個(gè)ASCII字符傳輸,如數(shù)值63H用ASCII方式時(shí),需發(fā)送兩個(gè)字節(jié),即ASCII“6"(0110110)和ASCII”3“(0110011),ASCII字符占用的位數(shù)有7位和8位,國際通用7位為多。這種方式的主要優(yōu)點(diǎn)是字符發(fā)送的時(shí)間間隔可達(dá)到1秒而不產(chǎn)生錯(cuò)誤。 代碼系統(tǒng) · 十六進(jìn)制,ASCII字符0...9,A...F · 消息中的每個(gè)ASCII字符都是一個(gè)十六進(jìn)制字符組成 每個(gè)字節(jié)的位 · 1個(gè)起始位 · 7個(gè)數(shù)據(jù)位,最小的有效位先發(fā)送 · 1個(gè)奇偶校驗(yàn)位,無校驗(yàn)則無 1個(gè)停止位(有校驗(yàn)時(shí)),2個(gè)Bit(無校驗(yàn)時(shí)) 錯(cuò)誤檢測域 · LRC(縱向冗長檢測) RTU模式 當(dāng)控制器設(shè)為在Modbus網(wǎng)絡(luò)上以RTU模式通信,在消息中的每個(gè)8Bit字節(jié)按照原值傳送,不做處理,如63H,RTU將直接發(fā)送01100011。這種方式的主要優(yōu)點(diǎn)是:數(shù)據(jù)幀傳送之間沒有間隔,相同波特率下傳輸數(shù)據(jù)的密度要比ASCII高,傳輸速度更快。 代碼系統(tǒng) 8位二進(jìn)制,十六進(jìn)制數(shù)0...9,A...F 消息中的每個(gè)8位域都是一或兩個(gè)十六進(jìn)制字符組成 每個(gè)字節(jié)的位 1個(gè)起始位 8個(gè)數(shù)據(jù)位,最小的有效位先發(fā)送 1個(gè)奇偶校驗(yàn)位,無校驗(yàn)則無 1個(gè)停止位(有校驗(yàn)時(shí)),2個(gè)Bit(無校驗(yàn)時(shí)) (4)數(shù)據(jù)校驗(yàn)方式 CRC校驗(yàn) CRC域是兩個(gè)字節(jié),包含一16位的二進(jìn)制值。它由傳輸設(shè)備計(jì)算后加入到消息中。接收設(shè)備重新計(jì)算收到消息的CRC,并與接收到的CRC域中的值比較,如果兩值不同,則有誤。 CRC是先調(diào)入一值是全“1”的16位寄存器,然后調(diào)用一過程將消息中連續(xù)的8位字節(jié)和當(dāng)前寄存器中的值進(jìn)行處理。僅每個(gè)字符中的8Bit數(shù)據(jù)對CRC有效,起始位和停止位以及奇偶校驗(yàn)位均無效。 CRC產(chǎn)生過程中,每個(gè)8位字符都單獨(dú)和寄存器內(nèi)容相異或(XOR),結(jié)果向最低有效位方向移動,最高有效位以0填充。LSB被提取出來檢測,如果LSB為1,寄存器單獨(dú)和預(yù)置的值或一下,如果LSB為0,則不進(jìn)行。整個(gè)過程要重復(fù)8次。在最后一位(第8位)完成后,下一個(gè)8位字節(jié)又單獨(dú)和寄存器的當(dāng)前值相異或(XOR)。最終寄存器中的值,是消息中所有的字節(jié)都執(zhí)行之后的CRC值。 CRC添加到消息中時(shí),低字節(jié)先加入,然后高字節(jié)。 CRC-16錯(cuò)誤校驗(yàn)程序如下:報(bào)文(此處只涉及數(shù)據(jù)位,不指起始位、停止位和任選的奇偶校驗(yàn)位)被看作是一個(gè)連續(xù)的二進(jìn)制,其最高有效位(MSB)首選發(fā)送。報(bào)文先與X↑16相乘(左移16位),然后看X↑16+X↑15+X↑2+1除,X↑16+X↑15+X↑2+1可以表示為二進(jìn)制數(shù)11000,0000,0000,0101。整數(shù)商位忽略不記,16位余數(shù)加入該報(bào)文(MSB先發(fā)送),成為2個(gè)CRC校驗(yàn)字節(jié)。余數(shù)中的1全部初始化,以免所有的零成為一條報(bào)文被接收。經(jīng)上述處理而含有CRC字節(jié)的報(bào)文,若無錯(cuò)誤,到接收設(shè)備后再被同一多項(xiàng)式(X↑16+X↑15+X↑2+1)除,會得到一個(gè)零余數(shù)(接收設(shè)備核驗(yàn)這個(gè)CRC字節(jié),并將其與被傳送的CRC比較)。全部運(yùn)算以2為模(無進(jìn)位)。 習(xí)慣于成串發(fā)送數(shù)據(jù)的設(shè)備會首選送出字符的最右位(LSB-最低有效位)。而在生成CRC情況下,發(fā)送首位應(yīng)是被除數(shù)的最高有效位MSB。由于在運(yùn)算中不用進(jìn)位,為便于操作起見,計(jì)算CRC時(shí)設(shè)MSB在最右位。生成多項(xiàng)式的位序也必須反過來,以保持一致。多項(xiàng)式的MSB略去不記,因其只對商有影響而不影響余數(shù)。 LRC檢驗(yàn) LRC錯(cuò)誤校驗(yàn)用于ASCII模式。這個(gè)錯(cuò)誤校驗(yàn)是一個(gè)8位二進(jìn)制數(shù),可作為2個(gè)ASCII十六進(jìn)制字節(jié)傳送。把十六進(jìn)制字符轉(zhuǎn)換成二進(jìn)制,加上無循環(huán)進(jìn)位的二進(jìn)制字符和二進(jìn)制補(bǔ)碼結(jié)果生成LRC錯(cuò)誤校驗(yàn)(參見圖)。這個(gè)LRC在接收設(shè)備進(jìn)行核驗(yàn),并與被傳送的LRC進(jìn)行比較,冒號(:)、回車符號(CR)、換行字符(LF)和置入的其他任何非ASCII十六進(jìn)制字符在運(yùn)算時(shí)忽略不計(jì)。 (5)Modbus協(xié)議中功能碼定義 Modbus功能碼在查詢數(shù)據(jù)包和回應(yīng)數(shù)據(jù)包里都只占用一個(gè)字節(jié),取值范圍是1~127。之所以127以上不能使用,是因?yàn)?/font>Modbus規(guī)定當(dāng)通信出現(xiàn)異常時(shí),功能碼+0X80(十進(jìn)制128)代表異常狀態(tài),因此129~255的取值代表異常碼。 Modbus相關(guān)公共功能碼: 函數(shù)說明:在了解了Modbus協(xié)議之后,開始編寫Modbus-RTU通信函數(shù)。本系統(tǒng)所用的PIC18F4520單片機(jī)最小系統(tǒng)只有RS232通信接口,電力儀表提供的是串行異步半雙工的RS485通信接口,故需用RS232轉(zhuǎn)RS485轉(zhuǎn)接器實(shí)現(xiàn)通信。本模塊用USART來編寫通信函數(shù),且必須將其設(shè)置為串行異步半雙工的通信方式,相關(guān)USART初始化函數(shù)如下: //USART模塊初始化
void USARTinit(void)
{
TXSTAbits.SYNC=0; //選擇異步通信方式
TXSTAbits.TX9=0; //選擇8位發(fā)送數(shù)據(jù)格式
//TXSTAbits.TXEN=1; //允許發(fā)送,配置為半雙工方式
RCSTAbits.SPEN=1; //使能串口
RCSTAbits.RX9=0; //選擇8位接收數(shù)據(jù)模式
//RCSTAbits.CREN=1; //異位模式下,使能接收器
BAUDCONbits.BRG16=1; //使能16位的波特率發(fā)生器
TXSTAbits.BRGH=1; //采用高速波特率
SPBRGH=832/256;
SPBRG=832%256; //波特率設(shè)置為9600,系統(tǒng)時(shí)鐘為32MHZ
} 在這個(gè)函數(shù)模塊中,電力儀表要收到來自單片機(jī)的查詢數(shù)據(jù)包以后,會返回一個(gè)回應(yīng)數(shù)據(jù)包,而且由于已知要查的頻率變量在儀表的74、75兩個(gè)寄存器以內(nèi),發(fā)送數(shù)據(jù)包一共有8個(gè)字節(jié),回應(yīng)數(shù)據(jù)包一共有9個(gè)字節(jié),本來需要根據(jù)兩個(gè)字節(jié)之間的傳輸時(shí)間是否大于3.5個(gè)字節(jié)傳輸時(shí)間來確定是否為新的數(shù)據(jù)包,但在已知回應(yīng)數(shù)據(jù)包字節(jié)數(shù)的情況下,軟件可以簡化為直接判斷單片機(jī)接收到的回應(yīng)數(shù)據(jù)包的字節(jié)數(shù)是否為9。按照這個(gè)思路,我們可以打開單片機(jī)UASRT的接收中斷,首先,將通信方式配置為發(fā)送模式,在單片機(jī)將查詢數(shù)據(jù)包發(fā)送給電力儀表后,將通信方式配置為接收模式,電力儀表發(fā)送給單片機(jī)一個(gè)字節(jié),則接收中斷標(biāo)志位置1,我們將接收到的字節(jié)存儲在接收數(shù)組里面,通過相關(guān)函數(shù)將接收數(shù)組里面有效的信息提取出來加以使用。 接收中斷相關(guān)函數(shù)如下: void USARTINT(void)
{
RCONbits.IPEN=0; //禁止中斷嵌套,禁止中斷優(yōu)先級功能,所以這里不用加上低優(yōu)先級中斷服務(wù)函數(shù),但加上問題不大
INTCON|=0XC0; //CPU開中斷,開啟了總中斷GIE和外設(shè)中斷PEIE,即允許外設(shè)中斷 IPR1bits.RCIP=0; //設(shè)置禁止EUSART接收中斷為高優(yōu)先級
PIR1bits.RCIF=0; //清USART接收中斷標(biāo)志
PIE1bits.RCIE=1; //允許USART接收中斷
}
#pragma interrupt PIC18F_HIGH_ISR
void PIC18F_HIGH_ISR(void)
{
if(1==PIR1bits.RCIF) //rx_index為接收數(shù)組的長度
{
flag=0;
PIR1bits.RCIF=0; //清除接收中斷標(biāo)志位
if(rx_index<sizeof(rx_buf)) //接收緩沖器尚未用完
{
rx_buf[rx_index]=RCREG;
rx_index++; //將接收到的數(shù)據(jù)存進(jìn)接收數(shù)組,并遞增計(jì)數(shù)器rx_index
}
if(rx_index==9) //接收到九個(gè)字節(jié),說明一幀結(jié)束
{ rx_index=0;flag=1;} //接受到完整的一幀數(shù)據(jù)
} } 該模塊發(fā)送函數(shù)的設(shè)計(jì)過程為構(gòu)建有效信息發(fā)送數(shù)組,根據(jù)發(fā)送數(shù)組計(jì)算CRC校驗(yàn)碼,最后將有效信息和CRC校驗(yàn)碼構(gòu)造成一個(gè)完整的發(fā)送數(shù)組再一并發(fā)送出去。在這個(gè)過程中,比較重要的為CRC計(jì)算函數(shù),因?yàn)橛行畔⒃谝阎娏x表相關(guān)寄存器信息以后是確定的,但若校驗(yàn)碼錯(cuò)誤,即使單片機(jī)將發(fā)送數(shù)組發(fā)送給電力儀表,電力儀表經(jīng)過計(jì)算以后與單片機(jī)發(fā)送過來的CRC校驗(yàn)碼不相等,電力儀表則判斷為發(fā)送信息錯(cuò)誤,會發(fā)送異常碼給單片機(jī),這樣通信為不成功,故以下著重介紹CRC計(jì)算函數(shù): //CRC檢驗(yàn)函數(shù) 1 unsigned short crc(unsigned char *ptr,unsigned char size) { unsigned short a,b,tmp,CRC16,V; CRC16=0xffff; //CRC寄存器初始值 for (a=0;a<size;a++) //N個(gè)字節(jié) { CRC16=*ptr^CRC16; for (b=0;b<8;b++) //8位數(shù)據(jù) { tmp=CRC16 & 0x0001; CRC16 =CRC16 >>1; //右移一位 if (tmp) CRC16=CRC16 ^ 0xa001; //異或多項(xiàng)式 } *ptr++; } V = ((CRC16 & 0x00FF) << 8) | ((CRC16 & 0xFF00) >> 8); //高低字節(jié)轉(zhuǎn)換,這個(gè)時(shí)候低字節(jié)在高八位,高字節(jié)在低八位 return V; } 生成CRC-16校驗(yàn)字節(jié)的步驟如下: ①例如一個(gè)16位寄存器,所有數(shù)位均為1。 ②該16位寄存器的高位字節(jié)與開始8位字節(jié)進(jìn)行“異或”運(yùn)算。運(yùn)算結(jié)果放入這個(gè)16位寄存器。 ③把這個(gè)16寄存器向右移一位。 ④若向右(標(biāo)記位)移出的數(shù)位是1,則生成多項(xiàng)式10,1000,000,0000,001和這個(gè)寄存器進(jìn)行“異或”運(yùn)算;若向右移出的數(shù)位是0,則返回③。 ⑤重復(fù)③和④,直至移出8位。 ⑥另外8位與該十六位寄存器進(jìn)行“異或”運(yùn)算。 ⑦重復(fù)③~⑥,直至該報(bào)文所有字節(jié)均與16位寄存器進(jìn)行“異或”運(yùn)算,并移位8次。 ⑧這個(gè)16位寄存器的內(nèi)容即2字節(jié)CRC錯(cuò)誤校驗(yàn),被加到報(bào)文的最高有效位。 另外,在某些非ModBus通信協(xié)議中也經(jīng)常使用CRC16作為校驗(yàn)手段,而且產(chǎn)生了一些CRC16的變種,他們是使用CRC16多項(xiàng)式X↑16+X↑15+X↑2+1,單首次裝入的16位寄存器為0000;使用CRC16的反序X↑16+X↑14+X↑1+1,首次裝入寄存器值為0000或FFFFH。 Modbus-RTU通信函數(shù)如下: //modbus-rtu通信得到輸出電頻率
float modbus_rtu(void)
{
float a;
cnttxd=rtu_read_hldreg(LOCALADDR,tx_buf,73,2); //得到發(fā)送數(shù)組的字節(jié)數(shù)
transmit(tx_buf,cnttxd); //發(fā)送,cnttxd為發(fā)送數(shù)組的長度
while(!flag&&RC4==0); //判斷接收數(shù)組長度是否達(dá)到了要求的長度
if(rtu_data_anlys(rx_buf,9)==1) //數(shù)據(jù)分析,檢驗(yàn)接收到的數(shù)據(jù)對不對
a=receive(rx_buf); //實(shí)時(shí)接收到儀表的頻率fre
else
a=0;
return a;
} 接口設(shè)計(jì):本模塊的接口電路已在PIC18F4520單片機(jī)最小系統(tǒng)里面集成,值得注意的是本模塊雖然使用的是RS232串行通信模塊,但是由于儀表的通信接口為RS485,儀表與單片機(jī)之間用公—公串口線與RS485轉(zhuǎn)RS232的轉(zhuǎn)接器相連進(jìn)行通信,故實(shí)際中即使RS232串行通訊模塊是全雙工的,其中的RC6、RC7本可以在同一時(shí)間使用,但是在本模塊下,必須設(shè)置為半雙工工作方式。RS232串行通信電路如下: 通信的接線如下: 最后發(fā)電機(jī)的儀表與單片機(jī)實(shí)現(xiàn)的通信結(jié)果如下:
|