⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xmodem.c

📁 使用xmodem进行串口通讯的程序
💻 C
字号:
////////////////////////////////////////////////////////////////////////////
//
//  XMODEM.C    - Written by Mike Sax for Dr. Dobb's Journal
//
//  This file contains two public functions:
//      BOOL UploadXModem(HWND hParent, int gnPortID, LPSTR lpFilename);
//      BOOL DownloadXModem(HWND hParent, int gnPortID, LPSTR lpFilename);
//
//  These functions will transfer the file specified by lpFilename on the
//  Windows comm. port specified by gnPortID.  The hParent parameter is the
//  handle of the window that will be the parent of the XModem status dialog.
//
//  To include these functions in your own program, you should include the
//  following files with your program: XMODEM.C COMM.C XMODEM.RC.  No
//  additional functions or variables are required.
//
////////////////////////////////////////////////////////////////////////////

#define dprintf(s) MessageBox(GetFocus(), s, "Debug Message", MB_OK)

//  xmodem.c
#define USECOMM 1               // for 3.1 windows.h
#include "windows.h"
#include "wincom.h"
#include "comm.h"
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <memory.h>

#define RETRIES  12
#define CRCTRIES 2
#define PADCHAR  0x1a
#define SOH      1
#define EOT      4
#define ACK      6
#define NAK      0x15
#define CAN      0x18
#define CRC      'C'

// Local data
static int gcTries;               // retry counter
static char gachBuffer[130];      // I/O buffer
static int ghStatusDlg;           // Handle of the transfer status dialog
static BOOL gbUserCanceled;       // Did the user press cancel?
static int gnPortID;              // ID of the current comm. port
static FARPROC glpDlgProc;        // ProcInstance of status dialog
static BOOL gbParentEnabled;      // Parent of status dialog enabled?
static int gbTimeOut;             // Time out char when reading?
static DCB gDCB;                  // Comm. status that we save

// Prototypes:
static void ReceiveError(int, int);
static void Sleep(int);
static void Status(char *);
static void TestWordLen(void);
HANDLE GetCurrentInstance(void);
static BOOL TimeOut(void);
WORD CalculateCRC(char *pchBuffer, int nLen);
static BOOL CreateTransferDialog(HWND hParent, char *szTitle, char *szFilename);
void DestroyTransferDialog(void);
BOOL _export _far PASCAL TransferDlgProc(HWND hDlg, WORD wMessage, WORD wParam, long lParam);
static void DoEvents(void);
static void ModifyCommState(void);
static void RestoreCommState(void);

// Error messages
static char *aszError[] = 
	{
	"Timed Out",
	"Invalid SOH",
	"Invalid Block #",
	"Invalid checksum/crc"
	};

enum
	{
	errTIMEOUT,
	errINVALIDSOH,
	errINVALIDBLOCK,
	errINVALIDCHECKSUM
	};

// Give control to other windows
static void DoEvents(void)
	{
	MSG msg;

	while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
		{
		if (msg.message == WM_QUIT)
			{   // Our application has to quit: simulate user pressed cancel
			PostQuitMessage(msg.wParam);
			gbUserCanceled = TRUE;
			return;
			}
		if (!IsDialogMessage(ghStatusDlg, &msg))
			{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			}
		}
	}

// Classic "pause" function for Windows
static void Sleep(int nTicks)
	{
	DWORD dwEnd = GetTickCount() + nTicks * 55;

	while ((dwEnd > GetTickCount()) && !gbUserCanceled)
		DoEvents();
	}

// Wait x seconds for char in the com. port, return -1 if none, char otherwise
static int ComReadCharTimeOut(int nPortID, int nSeconds)
	{
	DWORD dwEnd = GetTickCount() + nSeconds * 1000l;

	gbTimeOut = FALSE;
	while ((dwEnd > GetTickCount()) && !gbUserCanceled)
		{
		if (CharsWaitingToBeRead(nPortID))
			return ComReadChar(nPortID);
		DoEvents();
		}
	gbTimeOut = TRUE;
	return -1;
	}

// Tansfer status dialog proc.
BOOL _export _far PASCAL TransferDlgProc(HWND hDlg, WORD wMessage, WORD wParam,
								  long lParam)
	{
	switch(wMessage)
		{
		case WM_INITDIALOG:
			gbUserCanceled = FALSE;
			break;
		case WM_COMMAND:
			if (wParam != IDCANCEL)
				return FALSE;           // Let dialog perform default action
		case WM_CLOSE:                  // fall thru!
			gbUserCanceled = TRUE;
			break;
		default:
			return FALSE;
		}
	return TRUE;
	}

// Returns the instance of the current task
HANDLE GetCurrentInstance(void)
	{
	_asm push ss
	_asm call GlobalHandle
	// return value in ax
	}

// Create transfer dialog box.  Return TRUE if success, FALSE otherwise
static BOOL CreateTransferDialog(HWND hParent, char *szTitle, char *szFilename)
	{
	HANDLE hInstance = GetCurrentInstance();

	glpDlgProc = MakeProcInstance((FARPROC)TransferDlgProc, hInstance);
	if (NULL == glpDlgProc)
		return FALSE;
	ghStatusDlg = CreateDialog(hInstance, "XMDMSTATUS", hParent, glpDlgProc);
	if (ghStatusDlg)
		{
		DoEvents();
		SetWindowText(ghStatusDlg, szTitle);
		SetDlgItemText(ghStatusDlg, IDD_FILENAME, szFilename);
		ShowWindow(ghStatusDlg, SW_SHOW);
		gbParentEnabled = !EnableWindow(hParent, FALSE);
		ModifyCommState();
		}
	else
		FreeProcInstance(glpDlgProc);
	return ghStatusDlg;
	}

// Destroys the transfer status dialog box
void DestroyTransferDialog(void)
	{
	if (ghStatusDlg)
		{
		EnableWindow(GetParent(ghStatusDlg), gbParentEnabled);
		DestroyWindow(ghStatusDlg);
		FreeProcInstance(glpDlgProc);
		ghStatusDlg = NULL;
		RestoreCommState();
		}
	}

// Saves current comm. port settings and sets XOn/XOff off, N-8-1
static void ModifyCommState(void)
	{
	DCB dcb;

	GetCommState(gnPortID, &gDCB);
	dcb = gDCB;
	dcb.fOutX = dcb.fInX = (BYTE)FALSE;
	dcb.ByteSize = (BYTE)8;
	dcb.Parity =  NOPARITY;
	dcb.StopBits = (BYTE)ONESTOPBIT;
	SetCommState(&dcb);
	}

static void RestoreCommState(void)
	{
	SetCommState(&gDCB);
	}

// Upload a file using the XModem protocol return TRUE if success
// hParent:    the handle of the window that should be the parent of
//             the transfer status dialog box (shouldn't be NULL).
// gnPortID:   the port ID of the port which is used for the transfer
// szFilename: the name of the file to be transfered
BOOL UploadXModem(HWND hParent, int gnPortID, char *szFilename)
	{
	int i, nCheckSum, bEOF = FALSE, chAnswer = 0, nLength, chCRCOut = 0;
	WORD wCRC;
	char nBlock = 1;
	BOOL bResult;
	int hFile;

	hFile = _lopen(szFilename, OF_READ);
	if ((gnPortID < 0) || (hFile < 1))
		return FALSE;       // Port is not open or file not found
	if (!CreateTransferDialog(hParent, "XMODEM Upload (Checksum)", szFilename))
		{
		_lclose(hFile);
		return FALSE;       // Couldn't create dialog --> failure
		}
	gcTries = 0;
	while (gcTries++ < RETRIES && chCRCOut != NAK && chCRCOut != CRC)
		chCRCOut = ComReadCharTimeOut(gnPortID, 6);
	gcTries = 0;
	if (chCRCOut == CRC)
		SetWindowText(ghStatusDlg, " XMODEM Upload (CRC)  ");
	else if (chCRCOut != NAK)   // Time out or tried 10 times without succes
		gcTries = RETRIES;
	while (gcTries < RETRIES && !bEOF && chAnswer != CAN)
		{
		// read the next data block
		memset(gachBuffer, 128, PADCHAR);
		if ((nLength = _lread(hFile, gachBuffer, 128)) != 128)
			bEOF = TRUE;
		if (nLength == 0)
			break;
		SetDlgItemInt(ghStatusDlg, IDD_BLOCK, nBlock, FALSE);
		if (gbUserCanceled)
			{
			ComWriteChar(gnPortID, CAN);
			ComWriteChar(gnPortID, CAN);
			break;
			}
		ComWriteChar(gnPortID, SOH);        // SOH
		ComWriteChar(gnPortID, nBlock);     // block number
		ComWriteChar(gnPortID, ~nBlock);    // 1s complement
		nCheckSum = 0;
		// send the data block
		for (i = 0; i < 128; i++)
			{
			ComWriteChar(gnPortID, gachBuffer[i]);
			nCheckSum += gachBuffer[i];     // checksum calculation
			}
		// Send error-correcting value (nCheckSum or crc)
		if (chCRCOut == NAK)
			ComWriteChar(gnPortID, nCheckSum & 255);
		else
			{
			wCRC = CalculateCRC(gachBuffer, 130);
			ComWriteChar(gnPortID, (wCRC >> 8) & 255);
			ComWriteChar(gnPortID, wCRC & 255);
			}
		// read ACK, NAK, or CAN from receiver
		chAnswer = ComReadCharTimeOut(gnPortID, 10);
		if (chAnswer == ACK)
			{
			nBlock++;
			gcTries = 0;
			SetDlgItemInt(ghStatusDlg, IDD_ERRORS, 0, FALSE);
			SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, "");
			}
		else
			{
			bEOF = FALSE;
			SetDlgItemInt(ghStatusDlg, IDD_ERRORS, ++gcTries, FALSE);
			SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, chAnswer == NAK ?
						   "Invalid CRC" : "Time out");
			// Position to previous block
			if (_llseek(hFile, -128l, 1) == -1)
				_llseek(hFile, 0L, 0);
			}
		}
	if (bEOF)
		{
		ComWriteChar(gnPortID, EOT);        // send the EOT
		ComReadCharTimeOut(gnPortID, 10);   // wait for an ACK
		bResult = TRUE;
		}
	else
		bResult = FALSE;                    // transfer aborted
	_lclose(hFile);
	DestroyTransferDialog();
	return bResult;                         // success
	}


// Download a file using the XModem protocol return TRUE if success
// hParent:    the handle of the window that should be the parent of
//             the transfer status dialog box.
// gnPortID:   the port ID of the port which is used for the transfer
// szFilename: the name of the file to be transfered
BOOL DownloadXModem(HWND hParent, int gnPortID, char *szFilename)
	{
	int nCurrentBlock=0, nSOH= 0, nBlock, nNotBlock, i, hFile;
	BOOL bCRC = TRUE, bFirst = TRUE;
	WORD wOurChecksum, wChecksumSent;

	hFile = _lcreat(szFilename, 0);
	if ((gnPortID < 0) || (hFile < 0))
		return FALSE;       // Port is not open --> failure
	if (!CreateTransferDialog(hParent, "XMODEM Download (Checksum)", szFilename))
		return FALSE;       // Couldn't create dialog --> failure
	if(!ghStatusDlg)
		return FALSE;
	// Send Cs then NAKs until the sender starts sending
	gcTries = 0;
	while (nSOH != SOH && gcTries < RETRIES)
		{
		bCRC = (gcTries++ < CRCTRIES);
		ComWriteChar(gnPortID, bCRC ? CRC : NAK);
		nSOH = ComReadCharTimeOut(gnPortID, 6);
		if (nSOH != -1 && nSOH != SOH)
			Sleep(6);
		}
	if (bCRC)
		SetWindowText(ghStatusDlg, "XMODEM Download (CRC)");
	while ((gcTries < RETRIES) && (!gbUserCanceled))
		{
		if (gbTimeOut)
			ReceiveError(errTIMEOUT, NAK);
		SetDlgItemInt(ghStatusDlg, IDD_BLOCK, nCurrentBlock + 1, FALSE);
		if (!bFirst)
			{
			nSOH = ComReadCharTimeOut(gnPortID, 10);
			if (nSOH == -1)     // TimeOut
				continue;
			else if (nSOH == CAN)
				break;
			else if (nSOH == EOT)
				{
				ComWriteChar(gnPortID, ACK);
				break;
				}
			}
		bFirst = FALSE;
		nBlock  = ComReadCharTimeOut(gnPortID, 1);      // block number
		nNotBlock = ComReadCharTimeOut(gnPortID, 1);    // 1's complement
		// get data block
		for (i = 0, wOurChecksum = 0 ; i < 128; i++)
			{
			int nValue;
			nValue = ComReadCharTimeOut(gnPortID,1);
			if (nValue < 0)     // Time out?
				break;
			gachBuffer[i] = (char)nValue;
			wOurChecksum = (wOurChecksum + (*(gachBuffer + i)) & 255) & 255;
			}
		if (i != 128)
			continue;
		// checksum or crc from sender
		wChecksumSent = ComReadCharTimeOut(gnPortID, 1);
		if (bCRC)
			wChecksumSent = (wChecksumSent << 8) +
							ComReadCharTimeOut(gnPortID, 1);
		if (gbTimeOut)
			continue;
		if (nSOH != SOH)      // Check the nSOH
			{
			ReceiveError(errINVALIDSOH, NAK);
			continue;
			}
		if (LOBYTE(nBlock) == LOBYTE(nCurrentBlock))
			_llseek(hFile, -128L, 1);
		else if (LOBYTE(nBlock) != LOBYTE(nCurrentBlock + 1) )
			{
			ComWriteChar(gnPortID, CAN);
			ReceiveError(errINVALIDBLOCK, CAN);
			break;
			}
		else
			nCurrentBlock++;
		// Test the block # 1s complement
		if (LOBYTE(nNotBlock) != LOBYTE(~nCurrentBlock))
			{
			ReceiveError(errINVALIDBLOCK, NAK);
			continue;
			}
		if (bCRC)
			wOurChecksum = CalculateCRC(gachBuffer, 130);
		// Test wOurChecksum or crc vs one sent
		if (wChecksumSent != wOurChecksum)
			{
			ReceiveError(errINVALIDCHECKSUM, NAK);
			continue;
			}
		nSOH = nBlock = nNotBlock = wChecksumSent = gcTries = 0;
		// Write the block to disk
		_lwrite(hFile, gachBuffer, 128);
		if (gbUserCanceled)
			{
			ComWriteChar(gnPortID, CAN);
			ComWriteChar(gnPortID, CAN);
			break;
			}
		ComWriteChar(gnPortID, ACK);
		}
	_lclose(hFile);
	DestroyTransferDialog();
	return (nSOH == EOT);   // return TRUE if transfer was succesfull
	}

static void SendError(int erno)
	{
	++gcTries;
	SetDlgItemInt(ghStatusDlg, IDD_ERRORS, gcTries, FALSE);
	SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, aszError[erno]);
	}

static void ReceiveError(int erno, int rtn)
	{
	++gcTries;
	SetDlgItemInt(ghStatusDlg, IDD_ERRORS, gcTries, FALSE);
	SetDlgItemText(ghStatusDlg, IDD_ERRORMESSAGE, aszError[erno]);
	ComWriteChar(gnPortID, rtn);
	Sleep(18);
	FlushComm(gnPortID, 0);
	FlushComm(gnPortID, 1);
	}

WORD CalculateCRC(char *pchBuffer, int nLen)
	{
	int i;
	DWORD dwCRC = 0;

	while (nLen--)
		{
		dwCRC |= (*pchBuffer++) & 255;
		for (i = 0; i < 8; i++) 
			{
			dwCRC <<= 1;
			if (dwCRC & 0x1000000L)
				dwCRC ^= 0x102100L;
			}
		}
	return (WORD) (dwCRC >> 8);
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -