1. บทนำ
XMODEM เป็นโปรโตคอลการถ่ายโอนไฟล์แบบอะซิงโครนัสที่ใช้กันอย่างแพร่หลายในการสื่อสารแบบอนุกรมแบ่งออกเป็นสองโปรโตคอล: XMODEM (ใช้บล็อกข้อมูล 128 ไบต์) และ 1K-XMODEM (ใช้บล็อกข้อมูล 1024-byte
บทความนี้ใช้โปรโตคอล XMODEM ของบล็อกข้อมูล 128 ไบต์ซึ่งใช้การตรวจสอบ CRC16 เมื่อนำไปใช้ในโครงการการส่งและรับสิ้นสุดสามารถแก้ไขข้อตกลงระหว่างทั้งสองฝ่ายตามสถานการณ์เฉพาะ
หากคุณไม่ทราบเกี่ยวกับการสื่อสารแบบอนุกรมคุณสามารถอ่านบล็อกที่ฉันเขียนเพื่อใช้ Java เพื่อใช้การสื่อสารแบบอนุกรม
2. การดำเนินการ
ในระหว่างกระบวนการดีบักกับนักเรียนที่ฝังตัวพบว่าข้อมูลที่ส่งด้านส่งเร็วเกินไปซึ่งทำให้ฝ่ายรับไม่สามารถประมวลผลได้ ดังนั้นเธรดเด็กจึงถูกเปิดในวิธีการส่งเพื่อประมวลผลข้อมูลการส่งข้อมูลลอจิกซึ่งอำนวยความสะดวกในการเพิ่มการประมวลผลการหน่วงเวลา
ในวิธีการรับการส่ง C หมายถึงการตรวจสอบใน CRC
คลาสสาธารณะ XMODEM {// เริ่มต้น BYTE สุดท้าย BYTE SOH = 0x01; // สิ้นสุดไบต์สุดท้ายส่วนตัว eot = 0x04; // ตอบส่วนตัวไบต์สุดท้าย ACK = 0x06; // RETRANSMIT BYTE BYTE NAK = 0x15; // ไบต์สุดท้ายที่ไม่มีเงื่อนไขไบต์สุดท้ายสามารถ = 0x18; // ถ่ายโอนข้อมูลใน 128 ไบต์บล็อกส่วนตัวสุดท้าย int sector_size = 128; // ข้อผิดพลาดสูงสุด (ไม่มีการตอบสนอง) จำนวนแพ็กเก็ตส่วนตัวสุดท้าย int max_errors = 10; // อินพุตสตรีมใช้เพื่ออ่านข้อมูลพอร์ตอนุกรม Private InputStream InputStream; // output stream ใช้เพื่อส่งข้อมูลพอร์ตอนุกรมเอาท์พุทสตรีมส่วนตัวเอาท์พุทสตรีม; สาธารณะ xmodem (inputstream inputstream, outputstream outputstream) {this.inputstream = inputstream; this.outputStream = outputStream; } / *** ส่งข้อมูล** @param filepath* เส้นทางไฟล์* / โมฆะสาธารณะส่ง (สตริงสุดท้าย filepath) {เธรดใหม่ () {โมฆะสาธารณะเรียกใช้ () {ลอง {// จำนวนของข้อผิดพลาดแพ็คเก็ต int errorcount; // หมายเลขแพ็คเกจไบต์ blocknumber = 0x01; // ตรวจสอบ int checksum; // จำนวนไบต์ที่อ่านไปยังบัฟเฟอร์ int nbytes; // เริ่มต้นไบต์บัฟเฟอร์ข้อมูล [] ส่วน = ไบต์ใหม่ [sector_size]; // อ่านการเริ่มต้นไฟล์ datainputStream inputStream = ใหม่ datainputStream (ใหม่ FileInputStream (filePath)); ในขณะที่ ((nbytes = inputstream.read (เซกเตอร์))> 0) {// ถ้าแพ็คเก็ตข้อมูลสุดท้ายน้อยกว่า 128 ไบต์ให้เติมด้วย 0xff ถ้า (nbytes <sector_size) {สำหรับ (int i = nbytes; i <sector_size; i ++) }} // แพ็คเก็ตข้อมูลเดียวกันจะถูกส่งได้สูงสุด 10 เท่า errorcount = 0; ในขณะที่ (errorcount <max_errors) {// กลุ่มแพ็คเก็ต // ตัวควบคุมอักขระ + หมายเลขแพ็คเก็ต + รหัสผกผันของหมายเลขแพ็คเก็ต + ข้อมูล + ตรวจสอบ putdata (SOH); Putdata (blocknumber); Putdata (~ blocknumber); checksum = crc16.calc (เซกเตอร์) & 0x00ffff; Putchar (Sector, (สั้น) ตรวจสอบ); outputstream.flush (); // รับข้อมูลคำตอบข้อมูลไบต์ = getData (); // หากได้รับคำตอบแพ็คเก็ตข้อมูลถัดไปจะกระโดดออกมาและส่งแพ็คเก็ตข้อมูลถัดไปของข้อมูล // ไม่ได้รับการตอบกลับจำนวนแพ็กเก็ตไม่ถูกต้อง +1 ดำเนินการต่อเพื่อส่งต่อหาก (data == ack) {break; } else {++ errorcount; }} // หมายเลขแพ็คเก็ตเพิ่มขึ้นโดย blockNumber = (ไบต์) ((++ blockNumber) % 256); } // หลังจากส่งข้อมูลทั้งหมดแล้ว Send End Flag บูลีน isack = false; ในขณะที่ (! isack) {putdata (eot); isack = getData () == ack; }} catch (exception e) {e.printstacktrace (); - }.เริ่ม(); } / *** รับข้อมูล** @param filepath* เส้นทางไฟล์* @return ว่าการรับเสร็จสมบูรณ์* @throws ioexception* ข้อยกเว้น* / บูลีนสาธารณะรับ (สตริง filepath) โยนข้อยกเว้น {// ข้อผิดพลาดแพ็คเก็ต int errorcount = 0; // หมายเลขแพ็คเกจไบต์ blocknumber = 0x01; // ข้อมูลไบต์ข้อมูล; // ตรวจสอบ int checksum; // เริ่มต้นไบต์บัฟเฟอร์ข้อมูล [] ส่วน = ไบต์ใหม่ [sector_size]; // เขียนไปยังไฟล์เพื่อเริ่มต้น dataOutputStream outputStream = ใหม่ dataOutputStream (ใหม่ fileOutputStream (filePath)); // ส่งอักขระ C และ CRC การตรวจสอบ Putdata ((ไบต์) 0x43); ในขณะที่ (จริง) {ถ้า (errorcount> max_errors) {outputstream.close (); กลับเท็จ; } // รับข้อมูลตอบกลับ = getData (); if (data! = eot) {ลอง {// พิจารณาว่าตัวระบุเริ่มต้นที่ได้รับถ้า (data! = soh) {errorcount ++; ดำเนินการต่อ; } // รับข้อมูลหมายเลขซีเรียลแพ็คเก็ต = getData (); // ตรวจสอบว่าหมายเลขซีเรียลแพ็คเก็ตนั้นถูกต้องหรือไม่ถ้า (data! = blocknumber) {errorcount ++; ดำเนินการต่อ; } // รับรหัสผกผันของหมายเลขซีเรียลแพ็คเก็ตไบต์ _blockNumber = (ไบต์) ~ getData (); // ตรวจสอบว่ารหัสผกผันของหมายเลขซีเรียลแพ็คเก็ตนั้นถูกต้องหรือไม่ถ้า (ข้อมูล! = _blockNumber) {ErrorCount ++; ดำเนินการต่อ; } // รับข้อมูลสำหรับ (int i = 0; i <sector_size; i ++) {sector [i] = getData (); } // รับ checksum checksum = (getData () & 0xff) << 8; checksum | = (getData () & 0xff); // ตรวจสอบว่า checksum นั้นถูกต้อง int crc = crc16.calc (ภาค); if (crc! = checksum) {errorcount ++; ดำเนินการต่อ; } // ส่งคำตอบ putdata (ack); // หมายเลขแพ็คเก็ตเพิ่มขึ้นโดย blocknumber ++; // เขียนข้อมูลไปยัง OutputStream.Write (ภาค); // ErrorCount ถูกรีเซ็ตเป็นศูนย์ = 0; } catch (exception e) {e.printstacktrace (); } ในที่สุด {// หากเกิดข้อผิดพลาดให้ส่งตัวระบุ retransmission ถ้า (ErrorCount! = 0) {Putdata (NAK); }}} else {break; }} // ปิดสตรีมเอาต์พุต outputstream.close (); // ส่งคำตอบ putdata (ack); กลับมาจริง; } / *** รับข้อมูล** @return data* @throws ioexception* ข้อยกเว้น* / ไบต์ไบต์ getData () พ่น IOException {return (byte) inputStream.read (); } / *** ส่งข้อมูล** @param data* data* @throws ioexception* ข้อยกเว้น* / โมฆะส่วนตัว putdata (ข้อมูล int) พ่น IOException {outputStream.write ((ไบต์) ข้อมูล); } / *** ส่งข้อมูล** @param data* data* @param checksum* checksum* @throws ioexception* ข้อยกเว้น* / โมฆะส่วนตัว putchar (byte [] ข้อมูล, ตรวจสอบสั้น ๆ ) โยน ioexception {bytebuffer bb = bytebuffer.allocate (data.length + 2) bb.put (ข้อมูล); bb.putshort (checksum); outputStream.write (bb.array ()); -อัลกอริทึมการตรวจสอบ CRC16 ใช้วิธีการค้นหาตาราง
คลาสสาธารณะ crc16 {ส่วนตัวคงที่ crctable crctable [] = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xa14a 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72F7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x969, 0xa90a 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a1a, 0xbb3b1a6 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17 0x2E93, 0x3EB2, 0x0ed1, 0x1ef0}; calc ถ่านสาธารณะสาธารณะ (ไบต์ [] ไบต์) {Char crc = 0x0000; สำหรับ (byte b: bytes) {crc = (char) ((crc << 8) ^ crctable [(crc >> 8) ^ b) & 0x00ff]); } return (char) (CRC); -3. ใช้
// serialport เป็นวัตถุพอร์ตอนุกรม xmodem xmodem = ใหม่ xmodem (serialport.getInputStream (), serialport.getOutputStream ()); // filepath เป็นเส้นทางไฟล์ // ./bin/xxx.binxmodem.send(filepath);
4. เขียนในตอนท้าย
ดาวน์โหลดรหัสเสร็จสมบูรณ์
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น