当前位置:七道奇文章资讯编程技术VC/C++编程
日期:2011-03-22 13:55:00  来源:本站整理

深化浅出Win32多线程程序计划综合实例[VC/C++编程]

赞助商链接



  本文“深化浅出Win32多线程程序计划综合实例[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:

本章我们将以产业掌握和嵌入式系统中应用极其遍及的串口通信为例报告多线程的典型利用.

而网络通信也是多线程利用最遍及的范畴之一,所以本章的最后一节也将对多线程网络通信举行简短的描写.

1.串口通信

在产业掌握系统中,工控机(普通都基于PC Windows平台)常常需求与单片机通过串口举行通信.因此,操作和利用PC的串口成为大大都单片机、嵌入式系统范畴工程师必须具有的本领.

串口的利用需求通过三个步骤来完成的:

(1) 翻开通信端口;

(2) 初始化串口,设置波特率、数据位、终止位、奇偶校验等参数.为了给读者一个直观的印象,下图从Windows的"掌握面板->系统->设备管理器->通信端口(COM1)"翻开COM的设置窗口:

(3) 读写串口.

在WIN32平台下,对通信端口举行操作跟基本的文件操作一样.

成立/翻开COM资源

下列函数假如调用成功,则返回一个标识通信端口的句柄,不然返回-1:

HADLE CreateFile(PCTSTR lpFileName, //通信端口名,如"COM1"
WORD dwDesiredAccess, //对资源的拜候范例
WORD dwShareMode, //指定同享情势,COM不能同享,该参数为0
PSECURITY_ATTRIBUTES lpSecurityAttributes,
//安全描写符指针,可为NULL
WORD dwCreationDisposition, //成立方法
WORD dwFlagsAndAttributes, //文件属性,可为NULL
HANDLE hTemplateFile //模板文件句柄,置为NULL
);

得到/设置COM属性

下列函数可以得到COM口的设备掌握块,从而得到相关参数:

BOOL WINAPI GetCommState(
  HANDLE hFile, //标识通信端口的句柄
  LPDCB lpDCB //指向一个设备掌握块(DCB构造)的指针
);

假如要调整通信端口的参数,则需求重新配置设备掌握块,再用WIN32 API SetCommState()函数举行设置:

BOOL SetCommState(
  HANDLE hFile, //标识通信端口的句柄
  LPDCB lpDCB //指向一个设备掌握块(DCB构造)的指针
);

DCB构造包含了串口的各项参数设置,以下:

typedef struct _DCB
{
  // dcb
  DWORD DCBlength; // sizeof(DCB)
  DWORD BaudRate; // current baud rate
  DWORD fBinary: 1; // binary mode, no EOF check
  DWORD fParity: 1; // enable parity checking
  DWORD fOutxCtsFlow: 1; // CTS output flow control
  DWORD fOutxDsrFlow: 1; // DSR output flow control
  DWORD fDtrControl: 2; // DTR flow control type
  DWORD fDsrSensitivity: 1; // DSR sensitivity
  DWORD fTXContinueOnXoff: 1; // XOFF continues Tx
  DWORD fOutX: 1; // XON/XOFF out flow control
  DWORD fInX: 1; // XON/XOFF in flow control
  DWORD fErrorChar: 1; // enable error replacement
  DWORD fNull: 1; // enable null stripping
  DWORD fRtsControl: 2; // RTS flow control
  DWORD fAbortOnError: 1; // abort reads/writes on error
  DWORD fDummy2: 17; // reserved
  WORD wReserved; // not currently used
  WORD XonLim; // transmit XON threshold
  WORD XoffLim; // transmit XOFF threshold
  BYTE ByteSize; // number of bits/byte, 4-8
  BYTE Parity; // 0-4=no,odd,even,mark,space
  BYTE StopBits; // 0,1,2 = 1, 1.5, 2
  char XonChar; // Tx and Rx XON character
  char XoffChar; // Tx and Rx XOFF character
  char ErrorChar; // error replacement character
  char EofChar; // end of input character
  char EvtChar; // received event character
  WORD wReserved1; // reserved; do not use
} DCB;

读写串口

在读写串口之前,还要用PurgeComm()函数清空缓冲区,并用SetCommMask ()函数设置事件掩模来监督指定通信端口上的事件,其原型为:

BOOL SetCommMask(
  HANDLE hFile, //标识通信端口的句柄
  DWORD dwEvtMask //可以使能的通信事件
);

串口上大概发生的事件以下表所示:

事件描写
EV_BREAK A break was detected on input.
EV_CTS The CTS (clear-to-send) signal changed state.
EV_DSR The DSR(data-set-ready) signal changed state.
EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING A ring indicator was detected.
EV_RLSD The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR A character was received and placed in the input buffer.
EV_RXFLAG The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function.
EV_TXEMPTY The last character in the output buffer was sent.

在设置功德件掩模后,我们便可以操纵WaitCommEvent()函数来等候串口上发闹事件,其函数原型为:

BOOL WaitCommEvent(
  HANDLE hFile, //标识通信端口的句柄
  LPDWORD lpEvtMask, //指向存放事件标识变量的指针
  LPOVERLAPPED lpOverlapped, // 指向overlapped构造
);

我们可以在发闹事件后,按照呼应的事件范例,举行串口的读写操作:

BOOL ReadFile(HANDLE hFile, //标识通信端口的句柄
  LPVOID lpBuffer, //输入数据Buffer指针
  DWORD nNumberOfBytesToRead, // 需求读取的字节数
  LPDWORD lpNumberOfBytesRead, //实际读取的字节数指针
  LPOVERLAPPED lpOverlapped //指向overlapped构造
);
BOOL WriteFile(HANDLE hFile, //标识通信端口的句柄
  LPCVOID lpBuffer, //输出数据Buffer指针
  DWORD nNumberOfBytesToWrite, //需求写的字节数
  LPDWORD lpNumberOfBytesWritten, //实际写入的字节数指针
  LPOVERLAPPED lpOverlapped //指向overlapped构造
);

2.工程实例

下面我们用第1节所述API实现一个多线程的串口通信程序.这个例子工程(工程名为MultiThreadCom)的界面很简单,以下图所示:

它是一个多线程的利用程序,包含两个工作者线程,辨别处理串口1和串口2.为了简化问题,我们让衔接两个串口的电缆只包含RX、TX两根连线(即不以硬件掌握RS-232,串口上只会发生EV_TXEMPTY、EV_RXCHAR事件).

在工程实例的BOOL CMultiThreadComApp::InitInstance()函数中,启动并设置COM1和COM2,其源代码为:

BOOL CMultiThreadComApp::InitInstance()
{
  AfxEnableControlContainer();
  //翻开并设置COM1
  hComm1=CreateFile("COM1", GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL);
  if (hComm1==(HANDLE)-1)
  {
   AfxMessageBox("翻开COM1失利");
   return false;
  }
  else
  {
   DCB wdcb;
   GetCommState (hComm1,&wdcb);
   wdcb.BaudRate=9600;
   SetCommState (hComm1,&wdcb);
   PurgeComm(hComm1,PURGE_TXCLEAR);
  }
  //翻开并设置COM2
  hComm2=CreateFile("COM2", GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL);
  if (hComm2==(HANDLE)-1)
  {
   AfxMessageBox("翻开COM2失利");
   return false;
  }
  else
  {
   DCB wdcb;
   GetCommState (hComm2,&wdcb);
   wdcb.BaudRate=9600;
   SetCommState (hComm2,&wdcb);
   PurgeComm(hComm2,PURGE_TXCLEAR);
  }
  CMultiThreadComDlg dlg;
  m_pMainWnd = &dlg;
  int nResponse = dlg.DoModal();
  if (nResponse == IDOK)
  {
   // TODO: Place code here to handle when the dialog is
   // dismissed with OK
  }
  else if (nResponse == IDCANCEL)
  {
   // TODO: Place code here to handle when the dialog is
   // dismissed with Cancel
  }
  return FALSE;
}

此后我们在对话框CMultiThreadComDlg的初始化函数OnInitDialog中启动两个辨别处理COM1和COM2的线程:

BOOL CMultiThreadComDlg::OnInitDialog()
{
  CDialog::OnInitDialog();
  // Add "About..." menu item to system menu.
  // IDM_ABOUTBOX must be in the system command range.
  ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  ASSERT(IDM_ABOUTBOX < 0xF000);
  CMenu* pSysMenu = GetSystemMenu(FALSE);
  if (pSysMenu != NULL)
  {
   CString strAboutMenu;
   strAboutMenu.LoadString(IDS_ABOUTBOX);
   if (!strAboutMenu.IsEmpty())
   {
    pSysMenu->AppendMenu(MF_SEPARATOR);
    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
   }
  }
  // Set the icon for this dialog. The framework does this automatically
  // when the application's main window is not a dialog
  SetIcon(m_hIcon, TRUE); // Set big icon
  SetIcon(m_hIcon, FALSE); // Set small icon
  // TODO: Add extra initialization here
  //启动串口1处理线程
  DWORD nThreadId1;
  hCommThread1 = ::CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE)Com1ThreadProcess, AfxGetMainWnd()->m_hWnd, 0, &nThreadId1);
  if (hCommThread1 == NULL)
  {
   AfxMessageBox("成立串口1处理线程失利");
   return false;
  }
  //启动串口2处理线程
  DWORD nThreadId2;
  hCommThread2 = ::CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE)Com2ThreadProcess, AfxGetMainWnd()->m_hWnd, 0, &nThreadId2);
  if (hCommThread2 == NULL)
  {
   AfxMessageBox("成立串口2处理线程失利");
   return false;
  }
  return TRUE; // return TRUE unless you set the focus to a control
}

两个串口COM1和COM2对应的线程处理函数等候串口上发闹事件,并按照事件范例和自身缓冲区能否有数据要发送举行呼应的处理,其源代码为:

DWORD WINAPI Com1ThreadProcess(HWND hWnd//主窗口句柄)
{
  DWORD wEven;
  char str[10]; //读入数据
  SetCommMask(hComm1, EV_RXCHAR | EV_TXEMPTY);
  while (TRUE)
  {
   WaitCommEvent(hComm1, &wEven, NULL);
   if(wEven = 0)
   {
    CloseHandle(hCommThread1);
    hCommThread1 = NULL;
    ExitThread(0);
   }
   else
   {
    switch (wEven)
    {
     case EV_TXEMPTY:
      if (wTxPos < wTxLen)
      {
       //在串口1写入数据
       DWORD wCount; //写入的字节数
       WriteFile(hComm1, com1Data.TxBuf[wTxPos], 1, &wCount, NULL);
       com1Data.wTxPos++;
      }
      break;
     case EV_RXCHAR:
      if (com1Data.wRxPos < com1Data.wRxLen)
      {
       //读取串口数据, 处理收到的数据
       DWORD wCount; //读取的字节数
       ReadFile(hComm1, com1Data.RxBuf[wRxPos], 1, &wCount, NULL);
       com1Data.wRxPos++;
       if(com1Data.wRxPos== com1Data.wRxLen);
        ::PostMessage(hWnd, COM_SENDCHAR, 0, 1);
      }
      break;
     }
    }
   }
  }
  return TRUE;
}
DWORD WINAPI Com2ThreadProcess(HWND hWnd //主窗口句柄)
{
  DWORD wEven;
  char str[10]; //读入数据
  SetCommMask(hComm2, EV_RXCHAR | EV_TXEMPTY);
  while (TRUE)
  {
   WaitCommEvent(hComm2, &wEven, NULL);
   if (wEven = 0)
   {
    CloseHandle(hCommThread2);
    hCommThread2 = NULL;
    ExitThread(0);
   }
   else
   {
    switch (wEven)
    {
     case EV_TXEMPTY:
      if (wTxPos < wTxLen)
      {
       //在串口2写入数据
       DWORD wCount; //写入的字节数
       WriteFile(hComm2, com2Data.TxBuf[wTxPos], 1, &wCount, NULL);
       com2Data.wTxPos++;
      }
      break;
     case EV_RXCHAR:
      if (com2Data.wRxPos < com2Data.wRxLen)
      {
       //读取串口数据, 处理收到的数据
       DWORD wCount; //读取的字节数
       ReadFile(hComm2, com2Data.RxBuf[wRxPos], 1, &wCount, NULL);
       com2Data.wRxPos++;
       if(com2Data.wRxPos== com2Data.wRxLen);
        ::PostMessage(hWnd, COM_SENDCHAR, 0, 1);
      }
      break;
     }
    }
   }
   return TRUE;
  }

线程掌握函数中所操作的com1Data和com2Data是与串口对应的数据构造struct tagSerialPort的实例,这个数据构造是:

typedef struct tagSerialPort
{
  BYTE RxBuf[SPRX_BUFLEN];//接纳Buffer
  WORD wRxPos; //当前接纳字节位置
  WORD wRxLen; //要接纳的字节数
  BYTE TxBuf[SPTX_BUFLEN];//发送Buffer
  WORD wTxPos; //当前发送字节位置
  WORD wTxLen; //要发送的字节数
}SerialPort, * LPSerialPort;

  以上是“深化浅出Win32多线程程序计划综合实例[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
  • 深化浅出Netfilter/iptables防火墙框架(底子篇)
  • 数据库计划范式深化浅出
  • 深化浅出基于Java的责任链情势
  • 深化浅出基于Java的制作筹划情势
  • 深化浅出谈垃圾的回收—Java堆的管理
  • <b>深化浅出Java筹划情势之迭代器情势</b>
  • 深化浅出基于Java的代理筹划情势
  • 深化浅出Java多线程(1)-办法join
  • 深化浅出Java多线程(2)-Swing中的EDT(事件分发线程)
  • 深化浅出基于Java的注释器筹划情势
  • 深化浅出Java堆的管理 - 垃圾回收
  • 深化浅出Java的访谒者情势
  • 本文地址: 与您的QQ/BBS好友分享!
    • 好的评价 如果您觉得此文章好,就请您
        0%(0)
    • 差的评价 如果您觉得此文章差,就请您
        0%(0)

    文章评论评论内容只代表网友观点,与本站立场无关!

       评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
    Copyright © 2020-2022 www.xiamiku.com. All Rights Reserved .