1. 소개
Xmodem은 직렬 통신에 널리 사용되는 비동기 파일 전송 프로토콜로 XModem (128 바이트 데이터 블록 사용)과 1K-XModem (1024 바이트 데이터 블록, 즉 1K-BYTE 데이터 블록) 프로토콜로 나뉩니다.
이 기사는 CRC16 검증을 채택하는 128 바이트 데이터 블록의 Xmodem 프로토콜을 구현합니다. 프로젝트에 적용될 때, 발송 및 수신 종료는 특정 상황에 따라 양 당사자 간의 계약을 수정할 수 있습니다.
직렬 커뮤니케이션에 대해 잘 모른다면 Java를 사용하여 연속 커뮤니케이션을 구현하기 위해 작성한 블로그를 읽을 수 있습니다.
2. 구현
임베디드 학생들과의 디버깅 과정에서 보내는 측면이 데이터를 너무 빨리 보냈으므로 수신 측면이 처리 할 수 없었습니다. 따라서 보내기 메소드에서 하위 스레드가 열려 데이터 보내기 로직을 처리하여 지연 처리의 추가를 용이하게합니다.
수신 방법에서 C를 보내는 것은 CRC에서 검증을 의미합니다.
공개 클래스 xmodem {// 개인 최종 바이트 시작 SOH = 0x01; // 개인 최종 바이트 EOT = 0x04; // 개인 최종 바이트 ACK = 0x06에 답하십시오. // 개인 최종 바이트 NAK = 0x15를 retransment합니다. // 무조건 최종 개인 최종 바이트 캔 = 0x18; // 128 바이트 블록에서 데이터를 전송 개인 최종 int sector_size = 128; // 최대 오류 (응답 없음) 패킷 수 개인 최종 최종 int max_errors = 10; // 직렬 포트 데이터를 읽는 데 사용되는 입력 스트림 개인 입력 스트림 입력 스트림; // 출력 스트림, 직렬 포트 데이터를 보내는 데 사용됩니다. 비공개 출력 스트림 출력 스트림; public xmodem (inputStream inputStream, outputStream outputStream) {this.inputStream = inputStream; this.outputStream = outputStream; } / *** 데이터 보내기** @param filepath* 파일 경로* / public void send (final string filepath) {new Thread () {public void run () {try {// 오류 패킷 수 Int ErrorCount; // 패키지 번호 바이트 번호 블록 누르 = 0x01; // 체크섬 int 체크섬; // 버퍼 int nbytes에 읽는 바이트 수; // 데이터 버퍼 바이트 초기화 [] 섹션 = 새로운 바이트 [sector_size]; // 파일 읽기 초기화 datainputStream inputStream = new DatainputStream (new FileInputStream (filepath)); while ((nbytes = inputstream.read (sector))> 0) {// 마지막 데이터 패킷이 128 바이트 미만인 경우 (nbytes <sector_size) {(int i = nbytes; i <sector_size; i ++) {sector [i] = (byte) 0xff; }} // 동일한 데이터 패킷의 데이터 패킷이 최대 10 배의 전송됩니다. ErrorCount = 0; while (errorCount <max_errors) {// 그룹 패킷 // 문자 제어 + 패킷 번호 + 패킷 번호 + Data + Checksum Putdata (SOH); Putdata (BlockNumber); putdata (~ blocknumber); CheckSum = crc16.calc (섹터) & 0x00ffff; Putchar (Sector, (짧은) 체크섬); outputStream.flush (); // 답변 데이터 바이트 바이트 데이터 = getData (); // 답변이 수신되면 다음 데이터 패킷이 뛰어 내려 다음 데이터 패킷을 보냅니다. // 응답이 수신되지 않으면 패킷 수 +1, (data == ack) {break; } else {++ errorCount; }} // 패킷 번호는 blockNumber = (byte) ((++ blockNumber) % 256)에 의해 증가합니다. } // 모든 데이터가 전송 된 후, 보내기 엔드 플래그 부울 isack = false; while (! isack) {putdata (eot); isack = getData () == ACK; }} catch (예외 e) {e.printstacktrace (); }}; }.시작(); } / *** 수신 데이터** @param filepath* 파일 경로* @return 리셉션이 완료되었는지 여부* @throws ioException* 예외* / public boolean lece (string filepath) 예외 {// Error Packet int ErrorCount = 0; // 패키지 번호 바이트 번호 블록 누르 = 0x01; // 데이터 바이트 데이터; // 체크섬 int 체크섬; // 데이터 버퍼 바이트 초기화 [] 섹션 = 새로운 바이트 [sector_size]; // dataOutputStream outputStream을 초기화하기 위해 파일에 쓰기 = new DataOutputStream (new FileOutputStream (Filepath)); // 문자 C 및 CRC Verification PutData ((BYTE) 0x43)을 보내십시오. while (true) {if (ErrorCount> max_errors) {outputStream.close (); 거짓을 반환합니다. } // 답장 가져 오기 데이터 데이터 = getData (); if (data! = eot) {try {// 수신 된 시작 식별자 if (data! = soh) {ErrorCount ++; 계속하다; } // 패킷 일련 번호 데이터를 가져옵니다 = getData (); // 패킷 일련 번호가 올바른지 확인 if (data! = blocknumber) {ErrorCount ++; 계속하다; } // 패킷 시리얼 번호 바이트의 역 코드를 가져옵니다. // 패킷 일련 번호의 역 코드가 올바른지 여부를 결정하십시오. 계속하다; } // (int i = 0; i <sector_size; i ++) {sector [i] = getData (); } // 체크섬 checksum = (getData () & 0xff) << 8; CheckSum | = (getData () & 0xff); // 체크섬이 올바른지 여부를 결정합니다 int crc = crc16.calc (sector); if (crc! = checksum) {ErrorCount ++; 계속하다; } // 답장 PutData (ACK)를 보냅니다. // 패킷 번호는 BlockNumber ++로 증가합니다. // 로컬 outputStream.write (섹터)에 데이터를 작성합니다. // ErrorCount는 Zero = 0으로 재설정됩니다. } catch (예외 e) {e.printstacktrace (); } 마침내 {// 오류가 발생하면 (ErrorCount! = 0) {putdata (nak); }}} else {break; }} // 출력 스트림 outputStream.close ()를 닫습니다. // 답장을 보내면 putdata (ACK); 진실을 반환하십시오. } / *** 데이터 가져옵니다** @return data* @throws ioexception* 예외* / private byte getData ()는 ioexception {return (byte) inputStream.read (); } / *** 데이터 보내기** @param data* data* @throws ioexception* / private void putdata (int data)는 ioException {outputStream.write ((byte) data); } / *** 데이터 전송** @param data* data* @param checksum* checksum* @throws ioexception* / private void putchar (byte [] data, short checksum) ioexception {bytebuffer bb = bytebuffer.allother (byteorder.bigerder.big_endian); bb.put (데이터); BB. PutShort (체크섬); outputStream.write (bb.array ()); }}CRC16 검증 알고리즘은 테이블 조회 방법을 사용합니다.
공개 클래스 CRC16 {Private STATIC Final Char CrcTable [] = {0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd16b, 0xb16b. 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xd3bd. 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653. 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0xc7bc, 0xc7bc. 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x4ad4, 0x4aD4, 0x4af5. 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5. 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d. 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,, 0xc33d 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xe54f, 0xd52c, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x879, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xc71d, 0xd77e. 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0x8a, 0x89a, 0x89e9. 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0xb9a, 0x475, 0x475, 0xb9a. 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e5, 0x4e5, 0x4e5, 0x7e36. 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0}; public static char calc (byte [] bytes) {char crc = 0x0000; for (byte b : bytes) {crc = (char) ((crc << 8) ^ crctable [((crc >> 8) ^ b) & 0x00ff]); } return (char) (CRC); }}3. 사용
// Serialport는 시리얼 포트 객체 xmodem xmodem = new xmodem입니다 (serialport.getInputStream (), serialport.getOutputStream ()); // filePath는 파일 경로 // ./bin/xxx.binxmodem.send (filepath);
4. 끝에 쓰십시오
완전한 코드 다운로드
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.