Ovládání COM portu (MFC a C++)

Pokud máte v úmyslu používat počítač k ovládání nějakých jednoduchých přístrojů, tak byste si určitě měli přečíst tento článek. Chci vás ovšem upozornit, že při neopatrné práci s portem byste si ho mohli klidně "odbouchnout", takže doporučuji maximální opatrnost při práci.

Další věc se týká různých verzí Windows. Pokud ještě používáte systém Windows 95 nebo Windows 98, vše se dá vyřešit pomocí jednoduchého přímého přístupu do paměti. Problém ovšem nastává ve Windows založených na platformě NT (2000,XP, ...). Zde si totiž systém všechny přístupy do paměti hlídá a našemu programu nic nepovolí. Proto jsem se rozhodl, že článek udělám tak, aby byl použitelný pro všechny Windows, to znamená bez přímého přístupu do paměti.

Technické informace o portu

Port COM se používá spíše pro sériové přenášení dat, to znamená, že data proudí po jedné lince za sebou. Já však, abych zachoval jednoduchost ovládání, budu používat takový přenos, kdy je na každé lince buď kladné napětí nebo záporné napětí (asi 10V +-).

Každý COM port má celkem 9 pinů. Zde je jejich seznam

Výstupní: TXD, RTS, DTR
Vstupní: RXD, CTS, DCD, RI, DSR (pozn. RXD zde nebudu používat)
Zem: GND

A nyní k tomu, jak se vše zapojí. Jelikož z portu můžete z každé linky odebírat maximálně asi 10mA, je asi nejlepší připojit na výstup nějaký tranzistor a relé a tím pak ovládat více výkonové spotřebiče. Zde je schéma:

Zapojení

Relé je navíc nutné přemostit závěrně zapojenou diodou pro odvedení napěťových špiček vznikajících při rozpojení vnitřní cívky (na schématu nezakleslena).

Ovládací program

Jak už jsem řekl na začátku, nemůžeme využít přímý přistup do paměti, čímž se celý kód podstatně ztěžuje. Já abych ostatním ulehčil práci, vytvořil jsem třídu, která obsahuje všechny funkce pro práci s porty. Třídu si můžete stáhnout na konci článku.

Funkce, které budeme používat:

CreateFile - otevření portu
CloseHandle - zavření portu
EscapeCommFunction - práce s výstupy na portu
GetCommModemStatus - zjišťování stavů na vstupech

Otevření portu

BOOL CPortCom::OpenCom(int ComNumber)
{
  char ComName[6];
  sprintf(ComName,"COM%i",ComNumber);
  
  m_ComHandle=CreateFile(ComName,GENERIC_READ | GENERIC_WRITE,0,NULL,
    OPEN_EXISTING,0,NULL);

  if(m_ComHandle==INVALID_HANDLE_VALUE)
    return 0;
    
  return 1;
}

Jak vidíte, nejprve si vytvoříme celý název portu, jehož číslo určíme pomocí ComNumber . Dále ho otevřeme stejně jako soubor, pouze pomocí hodnot GENERIC_READ a GENERIC_WRITE určíme, že se bude i číst i zapisovat. OpenExisting znamená, že otevíráme existující soubor (port). Nakonec otestujeme návratovou hodnotu a ukončíme funkci.

Zavření portu

void CPortCom::CloseCom()
{
  CloseHandle(m_ComHandle);
}

Zde není co vysvětlovat.

Nastavování výstupů

void CPortCom::SetDTR(BOOL x)
{
  EscapeCommFunction(m_ComHandle,x ? SETDTR:CLRDTR);
}

void CPortCom::SetRTS(BOOL x)
{
  EscapeCommFunction(m_ComHandle,x ? SETRTS:CLRRTS);
}

void CPortCom::SetTXD(BOOL x)
{
  EscapeCommFunction(m_ComHandle,x ? SETBREAK:CLRBREAK);
}

Ke všem operacím využijeme funkci EscapeCommFunction, která má dva parametry. První značí handle portu, se kterým chceme pracovat, a druhý určuje, který výstup nastavit na 1 nebo 0.

Zjišťování vstupů

BOOL CPortCom::GetDSR()
{
  DWORD ModemStatus;
  GetCommModemStatus(m_ComHandle,&ModemStatus);
  ModemStatus=ModemStatus & MS_DSR_ON;
  return (BOOL)ModemStatus;
}

BOOL CPortCom::GetRI()
{
  DWORD ModemStatus;
  GetCommModemStatus(m_ComHandle,&ModemStatus);
  ModemStatus=ModemStatus & MS_RING_ON;
  return (BOOL)ModemStatus;
}

BOOL CPortCom::GetDCD()
{
  DWORD ModemStatus;
  GetCommModemStatus(m_ComHandle,&ModemStatus);
  ModemStatus=ModemStatus & MS_RLSD_ON;
  return (BOOL)ModemStatus;
}

BOOL CPortCom::GetCTS()
{
  DWORD ModemStatus;
  GetCommModemStatus(m_ComHandle,&ModemStatus);
  ModemStatus=ModemStatus & MS_CTS_ON;
  return (BOOL)ModemStatus;
}

Zde naopak použijeme funkci GetCommModemStatus, která má také dva parametry. První je opět handle portu a druhý je adresa proměnné, do které se uloží aktuální stav portu. Položku, kterou chceme, zjistíme tak, že výslednou hodnotu vymaskujeme s jménem vstupu.

To je tedy vše. Myslím si, že je to docela jednoduché, ale já jsem měl vždy jeden problém a to bylo časování. Proto jsem do třídy přidal i funkci pro čekání v mikrosekundách, která program na tu chvíli úplně zastaví. A nakonec, pokud je na vás mikrosekundový rozsah moc přesný, můžete si vytvořit vlastní funkci s využitím funkce timeGetTime .

Stáhnout zdrojový kód (2kb)

UPOZORNĚNÍ: Jedná se o archiv článků z let 2003 - 2005, uvedené technologie či postupy již mohou být neaktuální.