Archive

Posts Tagged ‘vc2005’

调试串口通信程序

May 27th, 2008 will No comments

项目做到最后了,上位机(普通台式计算机)需要接收下位机(实验板)处理过后的数据,由于返回的数据较少,一次可能就是几个字节,传输时间没有严格要求,所以选择了串口通信。串口通信方式简单。调试起来也比较简单。下位机的串口硬件调试是PK同学负责的,我负责上位机的串口程序的调试。

刚开始的调试想法是让计算机COM1口发送数据,然后COM2接收,后来发现计算机就只带有一个串口,那就用邻近同学的计算机发送数据吧,但是这样是无法通信的,因为我的串口线是对等的。后来根据小丫头同学调试串口的经验,让一个串口COM1自发自收,这样就不用考虑对等线还是交叉线的问题了。

调试计算机串口,需要一根串口线,对等线或者交叉线均可。
1、检测串口线是否能连通;检测方法,用万用表检测串口线的1-9号线是否对应连通。
2、检测计算机串口是否能正常工作;检测方法,将串口线一端接入计算机串口,另一端2、3两线用导线连通。下载串口调试助手观察计算机串口的自发自收情况。
3、检测自己程序的正确性;

以下串口类在原始是应用于WINCE下,我稍作修改即可在windows下运行。以下程序在VC2005下编译、测试通过。PSerialPort.cpp增加了TRACE调试的代码。

//From unkown author:unkonwn modified by IndexZ@xinzero.com
//PSerialPort.h
#pragma once
typedef void (*LPDataArriveProc)(char *data,int length,DWORD userdata);
class CPSerialPort
{
public:
	CPSerialPort();
	virtual ~CPSerialPort();
	BOOL OpenPort(LPCTSTR Port,int Baudrate,
		/*int DataBits,int StopBits,int Parity,*/
		LPDataArriveProc proc=NULL,DWORD userdata=0); //打开串口
	BOOL ClosePort();		//关闭串口
	//激活与关闭持续接收
	BOOL Activate();
	BOOL Deactivate();
	BOOL IsActive();
	//接收数据线程函数
	static DWORD WINAPI ReadPortThread(LPVOID lpParameter);
	DWORD ReadPort(char *data,int length);		//读取一定长度的数据
	DWORD WritePort(char *data,int length);		//发送数据
	DWORD WriteFileToPort(LPCTSTR FileName);	//发送文件
private:
	HANDLE m_hComm; //串口设备handle
	HANDLE m_hReadThread; //读串口线程handle
	BOOL m_bReceiving; //是否持续接收
	int m_nBufferSize; //缓冲大小
	char *Buffer; //缓冲区
	LPDataArriveProc m_lpDataArriveProc;
	DWORD m_dwUserData;
	//串口设置以及超时参数
	DCB dcb;
	COMMTIMEOUTS CommTimeOuts;
};
//From unkown author:unkonwn modified by IndexZ@xinzero.com
//PSerialPort.cpp
#include "StdAfx.h"
#include "PSerialPort.h" 

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
////////////////////////////////////////////////////////////////////// 

CPSerialPort::CPSerialPort()
{
	m_hComm = INVALID_HANDLE_VALUE;
	m_hReadThread = NULL;
	m_bReceiving = FALSE;
	m_nBufferSize = 256; //缓冲大小
} 

CPSerialPort::~CPSerialPort()
{
	ClosePort();
} 

DWORD WINAPI CPSerialPort::ReadPortThread(LPVOID lpParameter)
{
	CPSerialPort* m_pSerial;
	m_pSerial=(CPSerialPort*)lpParameter;
	BOOL fReadState;
	DWORD dwLength;
	char* buf=new char[m_pSerial->m_nBufferSize];
	while((m_pSerial->m_hComm!=INVALID_HANDLE_VALUE)
		&&(m_pSerial->m_bReceiving))
	{
		fReadState=ReadFile(m_pSerial->m_hComm,
			buf,m_pSerial->m_nBufferSize,&dwLength,NULL);
		if(!fReadState)
		{
			AfxMessageBox(_T("无法从串口读取数据!"));
		}
		else
		{
			if(dwLength!=0)
			{	//调试输出代码开始
				TRACE("%d\n",dwLength);
				int *pint = (int*)buf;
				for(int i=0;i<(dwLength/(sizeof(int)));i++)
				{
					TRACE("%d\n",pint[i]);
				}//调试输出代码结束
				//回送数据
				if(m_pSerial->m_lpDataArriveProc!=NULL)
				{
					m_pSerial->m_lpDataArriveProc(buf,
							dwLength,m_pSerial->m_dwUserData);
				}
			}
		}
		Sleep(100);		//休眠cpu
	}
	delete[] buf;
	return 0;
} 

BOOL CPSerialPort::OpenPort(LPCTSTR Port,int BaudRate,
			/*int DataBits,int StopBits,int Parity,*/
			LPDataArriveProc proc,DWORD userdata)
{
	m_lpDataArriveProc = proc;
	m_dwUserData = userdata; 

	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		m_hComm=CreateFile(Port,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
		if(m_hComm==INVALID_HANDLE_VALUE )
		{
			AfxMessageBox(_T("无法打开端口!请检查是否已被占用。"));
			return FALSE;
		}
		GetCommState(m_hComm,&dcb);
		dcb.BaudRate=BaudRate;
		//dcb.ByteSize=DataBits;
		//dcb.Parity=Parity;
		//dcb.StopBits=StopBits;
		dcb.fParity=FALSE;
		dcb.fBinary=TRUE;
		dcb.fDtrControl=0;
		dcb.fRtsControl=0;
		dcb.fOutX=dcb.fInX=dcb.fTXContinueOnXoff=0; 

		//设置状态参数
		SetCommMask(m_hComm,EV_RXCHAR);
		SetupComm(m_hComm,16384,16384);
		if(!SetCommState(m_hComm,&dcb))
		{
			AfxMessageBox(_T("无法按当前参数配置端口,请检查参数!"));
			PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
			ClosePort();
			return FALSE;
		} 

		//设置超时参数
		GetCommTimeouts(m_hComm,&CommTimeOuts);
		CommTimeOuts.ReadIntervalTimeout=100;
		CommTimeOuts.ReadTotalTimeoutMultiplier=1;
		CommTimeOuts.ReadTotalTimeoutConstant=100;
		CommTimeOuts.WriteTotalTimeoutMultiplier=0;
		CommTimeOuts.WriteTotalTimeoutConstant=0;
		if(!SetCommTimeouts(m_hComm,&CommTimeOuts))
		{
			AfxMessageBox(_T("无法设置超时参数!"));
			PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
			ClosePort();
			return FALSE;
		} 

		PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
		return TRUE;
	} 

	return FALSE;
} 

BOOL CPSerialPort::ClosePort()
{
	Deactivate();
	if(m_hComm!=INVALID_HANDLE_VALUE)
	{
		SetCommMask(m_hComm,0);
		PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
		CloseHandle(m_hComm);
		m_hComm=INVALID_HANDLE_VALUE;
		return TRUE;
	} 

	return TRUE;
} 

BOOL CPSerialPort::Activate()
{
	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		return FALSE;
	} 

	if(!m_bReceiving)
	{
		//开始接收线程
		PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
		m_bReceiving=TRUE;
		m_hReadThread=CreateThread(NULL,0,ReadPortThread,this,0,NULL);
	}
	if(m_hReadThread!=NULL)
	{
		return TRUE;
	}
	else
	{
		m_bReceiving=FALSE;
		return FALSE;
	} 

	return FALSE;
} 

BOOL CPSerialPort::Deactivate()
{
	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		return FALSE;
	} 

	//停止接收线程
	if(m_bReceiving)
	{
		m_bReceiving=FALSE;
		WaitForSingleObject(m_hReadThread,500);
		CloseHandle(m_hReadThread);
		m_hReadThread=NULL;
		return TRUE;
	} 

	return FALSE;
} 

BOOL CPSerialPort::IsActive()
{
	return m_bReceiving;
} 

DWORD CPSerialPort::WritePort(char *data,int length)
{
	if(m_hComm == INVALID_HANDLE_VALUE)
	{
		return 0;
	} 

	BOOL bFileWriteState;
	DWORD dwBytesWritten=0; 

	bFileWriteState = WriteFile(m_hComm,data,
			length*sizeof(char),&dwBytesWritten,NULL);
//	AfxMessageBox(L"正在写串口数据。。。");
	return dwBytesWritten;
} 

DWORD CPSerialPort::ReadPort(char *data,int length)
{
	BOOL bFileReadState;
	DWORD dwLength,dwByteRead;
	int TimeOutCount; 

	dwByteRead=0;
	TimeOutCount=0;
//	AfxMessageBox(L"正在读串口数据。。。");
	while(m_hComm!=INVALID_HANDLE_VALUE)
	{
		char* buf=new char[length];
		bFileReadState = ReadFile(m_hComm,data,length,&dwLength,NULL);
		if(!bFileReadState)
		{
			break;
		}
		else
		{
			dwByteRead+=dwLength;
			data+=dwLength;
		}
		if(dwByteRead == (unsigned long)length)
		{
			break;
		}
		if(dwLength!=0)
		{
			TimeOutCount=0;
		}
		else
		{
			TimeOutCount++;
			Sleep(5);
		}
		if(TimeOutCount==5)
		{
			break;
		}
	}
	return dwByteRead;
} 

DWORD CPSerialPort::WriteFileToPort(LPCTSTR FileName)
{
	CFile cf; 

	BOOL fWriteState,isOpen;
	DWORD dwBytesWritten;
	DWORD dwCharToWrite; 

	dwCharToWrite=0; 

	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		return 0;
	}
	isOpen = cf.Open(FileName,CFile::modeRead);
	if(!isOpen)
	{
		//AfxMessageBox(_T("无法打开Hex文件!"));
		return 0;
	}
	dwCharToWrite=(DWORD)cf.GetLength();
	cf.Seek(0,CFile::begin); 

	dwBytesWritten=0; 

	if(m_hComm!=INVALID_HANDLE_VALUE&&dwCharToWrite!=0)
	{
		char* buf=new char[dwCharToWrite];
		cf.Read(buf,dwCharToWrite); 

		fWriteState=WriteFile(m_hComm,buf,dwCharToWrite*sizeof(char),
					&dwBytesWritten,NULL);
		if(!fWriteState)
		{
			//AfxMessageBox(_T("无法向端口写入数据!"));
		}
		delete[] buf;
	}
	cf.Close();
	return dwBytesWritten;
}

VC2005中TRACE调试

May 9th, 2008 will No comments

在vc2005中基于对话框的程序中,一个函数处理数据数据量较大,跟踪很不方便,因为循环次数太多,而前面的循环又没有问题,VC2005中又不能像Visual DSP++中那样设置循环多少次之后的断点(或许有,但是我没有找到)。可行的办法就是打印中间结果,对话框程序中不能和控制台程序一样简单的加个printf()输出结果。需要使用vc中的宏TRACE,TRACE和printf()类似。

TRACE( “This is a TRACE statement\n” );
TRACE( “The value of x is %d\n”, x );

输出的结果在“ 输出”窗口中显示,显示输出窗口,视图->输出。下图为输出效果。
trace_output2
double型的temp变量全部溢出,但还是没有找到原因,需要继续努力。

Categories: 学习笔记 Tags: ,

VC2005下基于控制台(console)程序设计的小问题

April 24th, 2008 will No comments

由于调试的需要,前段时间将微软的开发平台升级到VS2005(VC8.0),第一感觉就是大,仅安装光盘的镜像就3G多,安装需要近4G空间。安装完成后总体来说还是可以,启动、运行速度没有想象中那么慢。在VS2005中看源程序很方便,右键菜单中的”转到定义”、”转到声明”,配合上工具栏上的”向前定位”、”向后定位”使用。也遇到了几个小问题,解决方法如下,希望对于用VC调试C语言程序的同学有一点帮助。

问题一、基于控制台的程序,运行一闪而过,没法看结果,为什么不能和VC6.0那样显示”press any key to continue…”?
此时想起好像是java中有个system(”pause”);函数可以是屏幕暂停。测试一下,果然出现了熟悉”请按任意键继续. . .”,后来查了一了c语言函数手册,才知道system(),原来是c的库函数包含于dos.h头文件中。但在vs2005的控制台程序中使用时,发现必须包含stdlib.h头文件,才能正常使用。

问题二,本次调试的程序是带有的命令行的控制台程序,调试的时候如何输入命令行呢?
以前确实没有遇到过这中情况,百度一下,解决办法如下:
在VS2005的菜单->项目->属性->配置属性->调试   命令行参数中输入运行参数,即可带参数调试了。

问题三、设置断点后,到断点程序不停止,”输出”窗口显示”未使用调试信息生成二进制文件”,为什么不能单步调试呢?
在VC6中建一个基于控制台(console)空项目,就可以对C++程序进行调试,然而在vs2005下新建项目选择 常规->空项目 那么所生成的工程就不能调试,此时工程默认情况下也不会生成调试信息。
如何才能单步调试呢?我没有找到好的解决办法。我是通过新建工程解决。新建 Win32->Win32控制台应用程序 然后选择空工程就可以了。

Categories: 学习笔记 Tags: ,

无法解析的外部符号,VC2005链接错误解决办法

April 2nd, 2008 will 2 comments

今天在生成一个工程的时候,编译没有问题,但是在链接的时候出了问题,出现了无法解析的外部符号,错误提示如下:

error LNK2019: 无法解析的外部符号
_imp__SetupDiGetDeviceInterfaceDetailW@24
error LNK2001: 无法解析的外部符号
“private: static struct _OVERLAPPED
CUsbCom::g_WriteOverlapped”

既然编译通过了,就说明了没有语法错误,不用在代码中死抠语法了。从错误中提示中找原因吧。

SetupDiGetDeviceInterfaceDetailW是程序中调用的一个函数,MSDN一下,应用此函数需要Setupapi.lib头文件,这个已经包含(不然编译也不能通过),再往下看发现需要Setupapi.lib或Setupapi.dll库。手动添加,项目->属性->配置属性->链接器->输入 然后在附件依赖项添加Setupapi.lib,再生成第一个无法解析的外部符号错误消失了。

g_WriteOverlapped是我定义的一个类的静态数据成员,原来是没有初始化,在类的.cpp文件中,函数体外初始化g_WriteOverlapped,再生成没有错误了。

Categories: 学习笔记 Tags: