Forum: PC-Programmierung Spannung am COM1


von Hans W. (walti)


Lesenswert?

Hallo,
ich soll für eine Veranstaltung ein C/C++ Programm schreiben das eine 
Zeitmessung vornimmt:

Ausgelöst wird die Zeitmessung von Hand am PC und stoppen soll die 
Zeitmessung bei der Durchbrechung einer Lichtschranke.
Die Lichtschranke ist über die RS232 Schnittstelle mit dem PC verbunden 
und schickt eine Spannung von 1 Volt, wird die Schranke durchbrochen 
fällt die Spannung auf 0 Volt ab und die Zeitmessung soll stoppen.

Darum benötige ich bitte einen Code der mir dieses Problem lösen kann.

Ich freue mich über jede Antwort und hoffe das mir bitte jemand helfen 
kann.

von Marek S. (Gast)


Lesenswert?

Hi Hans,
also mit 0-1 Volt wirst du an der RS232 nicht wirklich viel machen 
können.
Die arbeitet mit +-12 V.

Grundsätzlich kann ich dir helfen, gehen wir mal das Problem an. 0-1 V 
wird schwer, möchtest du einen µC verwenden oder "nur eine schaltung 
aufbauen".

Wenn du einen MC verwendest, könntest du das Signal wandel und 
aufnehmen. Wenn nun die lichtschranke durchbrochen wird, wird ein Pin am 
µC wie ein Taster "gedrückt" du sendest über die RS232 ein paar daten an 
den PC.

Wenn du keinen MC verwenden möchst musst du die Spannung modifizieren. 
Wenn du diese auf +- 12 V ändern kannst, könntest du den CTS (so heist 
das ding glaube ich) abfragen. Wenn du nicht mit -+ 12 Volt arbeiten 
möchtest musst du dass ganze auf 5 V trimmen und du könntest an die LPT 
schnittstelle( Drucker schnittstelle) gehen.

Grundsätzlich, solltest du dir weniger gedanken um die Software machen 
als um die Schaltung. Bei fragen stehe ich dir gerne zur verfügung.

gruß

marek

von Hans W. (walti)


Lesenswert?

Danke schon mal im Voraus marek

Die 0-1 Volt habe ich nur als Beispiel genommen weil ich nicht genau 
wusste mit welcher Spannung RS232 arbeitet aber danke das du mich 
aufgeklärt hast.

Das heißt also die Schaltung wird dann so aufgebaut das grundsätzlich 12 
Volt fließen und bei einer Durchbrechung der Schranke auf -12 Volt 
umgeschalten wird und ich dann diese Veränderung an der RS232 
Schnittstelle ablesen kann?
Hoffe ich habe das richtig verstanden.

Kennst du dich mit solchen Schaltungen aus?
Wenn ja, könntest du mir bitte eine Zusammenkritzeln?
Wäre dir sehr dankbar.

PS: Wäre es mit einem µC leichter und wieviel kostet so ein Ding?

mfg Walti

von Marek S. (Gast)


Lesenswert?

Hi Walter,

das mit dem uC wäre leichter, da man da ein Protokoll definieren kann. 
Ehmmmmm.... ich kann morgen aber mal testen wie schnell die Schaltung 
mit den 24 V zu realisieren wäre.... wofür brauchst du diese Anwendung?

P.S. ja du hast es richtig verstanden.

Gruß

Marek

von Hans W. (walti)


Lesenswert?

Ich soll für eine Veranstaltung in meiner Gemeinde eine Zeitmessung 
machen und deshalb ist auch das Budget für das Ganze extrem knapp. 
Deshalb ist auch wichtig das die Schaltung nicht zu komplex und präzise 
wird da das ja teuer werden kann.

Mit "wie schnell die Schaltung zu realisieren wäre", meinst du damit wie 
lange es dauern würde diese Schaltung zu bauen dann kann ich dich 
beruhigen, es sind noch mehrere Wochen bis zur Veranstaltung.

mfg Walti

von Hans W. (walti)


Lesenswert?

PS: nochmal ein dickes DANKE für deine Hilfe

von Marek S. (Gast)


Lesenswert?

Ok... ich verstehe.... ich schaue mir dass ganze morgen mal an, wenn ich 
die Schaltung hinbekomme, entwickel ich dir das Programm..... wenn das 
ganze läuft schicke ich dir die Pläne via email o. die Platine via Post.

Das ganze kostet dich dann ein paar nette fotos... dann nehme ich dass 
ganze als projekt auf für meine Homepage ( welche ich noch gestallten 
muss :) )

gru0

marek

von Marek S. (Gast)


Lesenswert?

Eh.... mom.... wie sind nun die Daten der Lichtschranke ?
Ich muss schon wissen was für ein verhalten vorliegt .

gruß

marek

von Hans W. (walti)


Lesenswert?

Weche Daten brauchst du denn genau von der Lichtschranke?
Auch muss ich die Schranke leider erst noch kaufen und mich vorher noch 
mit einem Kollegen über die ganze Sache absprechen, weil ich den Auftrag 
erst vor 3 Stunden bekommen hab.
Tut mir wirklich leid aber das Ganze könnte noch ein paar Tage dauern.

Kann ich denoch bitte auf dein Angebot zurückkommen?

PS: kannst du mir eine Lichtschranke empfehlen?

mfg Walti

von Marek S. (Gast)


Lesenswert?

Eine Lichtschranke kann ich dir nicht empfehlen, da ich mit der 
Thematik,so noch nicht gearbeitet habe.

Such dir mal eine Lichtschranke aus, welche eine digitales interface 
hat. Schick mir die informationen und wir gehen dass ganze durch.


Sobald dein Projekt steht, schickst du mir eine email und wir nehmen 
kontakt auf.

Gruß

Marek

von Hans W. (walti)


Lesenswert?

Okay geht klar werd ich machen.

Und nochmal vielen Dank für deine Hilfe.

mfg Walti

von Blackbird (Gast)


Lesenswert?

Warum so kompliziert?

An CTS (Pin8), DSR (Pin6), RI (Pin9) und DCD (Pin1), jeweils gegen Masse 
(Pin5) kann je ein Lichtschrankenausgang oder ein anderer Ausgang, der 
mindestens +5V gegen Masse oder -5V gegen Masse anlegt, angeschlossen 
werden.
Die Software registriert alle Pegelwechsel an den 4 Eingängen mit 
Zeitangabe in µS. Die Eigenverzögerung ist aber von PC zu PC 
verschieden, so das man davon ausgehen kann, das die Zeiten auf die 
Millisekunde genau ist.

Um der Diskussion wegen "Echtzeit" und dergleichen vorzubeugen: Ja, ich 
weiß, was das ist!

/************************************************************
** CommThread      : Öffnet, schließt und liest den Port,
**                   sendet die Zeiten per PostThreadMessage
**                   an den MonitorThread
************************************************************/

#include <windows.h>
#include <process.h>
#include <stdio.h>


// Prototypes:
DWORD WINAPI ComThread (LPVOID lpParam);
DWORD WINAPI MonitorThread (LPVOID lpParam);
HANDLE StartComThread (DWORD iPortNum, HWND hEdit);
long queryPrecisionTime (void);
long queryPrecisionTimeStamp (void);

// Globals:
HANDLE hCom = INVALID_HANDLE_VALUE;

// global struc contains parameter for threads
typedef struct
{
  volatile BOOL ThreadStoppen;
  volatile int iComPortNum;
  volatile DWORD  dwMonitorThreadId;
} PARAMS, *PPARAMS;

static HANDLE hEvent;

// Message types
#define MSG_MIN_RANGE      0
// Der Message-Type für PostThreadMessage setzt sich aus
// QS_ALLPOSTMESSAGE (=256) und dem eigenem Message-Type
// (siehe MSG_xxxxxxx) zusammen.
// Mit MSG_MIN_RANGE können verschiedene Gruppen bei
// Bedarf gebildet werden.
#define MSG_TIME        MSG_MIN_RANGE + 1000 // vom ComThread
#define MSG_TRIG        MSG_MIN_RANGE + 2000 // reserviert
#define MSG_COMOPEN     MSG_TRIG + 4
#define MSG_COMCLOSE    MSG_TRIG + 5

#define QEVENT     "QueueEvent"


//--------------------------------------------------------------------
// COMThread
//
// erstellt eine Queue, setzt danach den Event ("Queue ist ready").
// Schließt eine geöffneten COM-Port und öffnet den neuen (Nummer ist
// in der Strukur, auf die lpParam zeigt).
// liest die queue aus.
//
// Thread Funktion
// Liest eventgesteuert den Status von CTS, DSR, ... ein und
// sendet in dwEvtMask alle zum Event gehörenden Ereignisse
// zum Monitor-Thread.
//
//--------------------------------------------------------------------
DWORD WINAPI ComThread (LPVOID lpParam)
{
  long lTime;
  volatile PPARAMS pparams;                     // struct-Instanz
  pparams = (PPARAMS) lpParam;                  // Zuweisung zu lpParam
  int iPortNum = pparams->iComPortNum;          // ...
  DWORD dwMonitorThreadId = pparams->dwMonitorThreadId; // ...
  char szPort[15];
  static OVERLAPPED o;
  // Maske für SetCommMask, die bestimmt, welche Ereignisse auftreten 
können
  DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR |
                      EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY;
  DWORD dwEvtMask = 0;  // Maske, in die WaitCommEvent aktuelle Werte 
schreibt
  BOOL bRet;

  CloseHandle (hCom);       // "alten" COM-Port schließen

  // Com Port öffnen
  wsprintf (szPort, "COM%d", iPortNum);
  hCom = CreateFile (szPort,
    GENERIC_READ | GENERIC_WRITE,
    0,    // exclusive access
    NULL, // no security attributes
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    NULL);

  if (hCom == INVALID_HANDLE_VALUE)
  {
    LPVOID lpMsgBuf;
    FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR) &lpMsgBuf, 0, NULL);
    MessageBox (NULL, (LPCTSTR)lpMsgBuf, TEXT("ComThread: error: 
CreateFile()"),
      MB_OK | MB_ICONINFORMATION);
    LocalFree (lpMsgBuf);
    pparams->ThreadStoppen = TRUE;
  }
  else
  {
    if (!SetCommMask (hCom, dwEvtMaskIn))
      MessageBox (NULL, "SetCommMask fehlgeschlagen", "COM Port:", MB_OK 
| MB_ICONSTOP);

    // Create an event object for use in WaitCommEvent.
    o.hEvent = CreateEvent (NULL,   // no security attributes
        FALSE,  // auto reset event
        FALSE,  // not signaled
        NULL);  // no name
  }

  // Setzt die Priorität "High" für das Programm
  // Achtung: nicht "RealTime" verwenden - System wird instabil!
  DWORD  dwProcessId;  // own process identifier
  HANDLE hProcess;                 // handle to the own process
  DWORD  dwDesiredAccess = PROCESS_SET_INFORMATION;  // access flag
  BOOL   bInheritHandle = TRUE;   // handle inheritance flag
  BOOL   bReturn;           // If the SetPriorityClass succeeds, then 
nonzero
  DWORD  dwPriorityClass = HIGH_PRIORITY_CLASS;

  dwProcessId = GetCurrentProcessId ();
  hProcess = OpenProcess (dwDesiredAccess, bInheritHandle, dwProcessId);
  bReturn = SetPriorityClass (hProcess, dwPriorityClass);

  printf ("\r\nComThread: %s geöffnet, warte auf Events ...", szPort);

  // eine message zum Monitor-Thread senden:
  bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE + 
MSG_COMOPEN),
      (WPARAM)iPortNum , (LPARAM)0);

  // Auf Events warten:
  while (!pparams->ThreadStoppen) // solange weitermachen bis TRUE
  {
    WaitCommEvent (hCom, &dwEvtMask, &o);  // EventMask "scharf machen"
    // kommt der Event, ist auch die dwEvtMask geladen und es kann 
weitergehen
    if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) // 
warten bis Event
    {
      lTime = queryPrecisionTimeStamp ();  // Zeit holen
      // Message senden. In dwEvtMask können mehrere Ereignisse gesetzt 
sein
      // Ereignisse und Daten in eine Message-Queue schreiben und dann
      // gleich weitermachen. Die Daten NICHT hier weiterverarbeiten!
      bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE + 
MSG_TIME),
          (WPARAM)dwEvtMask , (LPARAM)lTime);
    }    // end of: if (WAIT_OBJECT_0 == ...
  }      // end of: while (...

  CloseHandle (o.hEvent);
  CloseHandle (hCom);          // COM-Port schließen

  printf ("\r\nComThread: %s geschlossen", szPort);

  bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE + 
MSG_COMCLOSE),
      (WPARAM)iPortNum , (LPARAM)0);

  return (0);
}


//---------------------------------------------------------------------- 
------
// MonitorThread
//
// erstellt eine Queue, setzt danach den Event und liest
// die queue aus
//---------------------------------------------------------------------- 
------
DWORD WINAPI MonitorThread (LPVOID lpParam)
{
  static char szMsg[255] = "";
  static char szMsgTmp[30] = "";
  int i = 0;
  BOOL bRet;
  MSG msg;
  volatile PPARAMS pparams;                     // struct-Instanz
  pparams = (PPARAMS) lpParam;                  // Zuweisung zu lpParam

  DWORD COMStatus;


  // create a queue for the thread
  PeekMessage ((LPMSG)&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  // sent event after queue creation back to the threads creator
  bRet = SetEvent (hEvent);

  printf ("\r\nMonitorThread: warte auf Events ...");

  // get messages by message from threads local queue
  while ((bRet = GetMessage (&msg, NULL, 0, 0)) != 0)
  {
    if (bRet == -1)
    { // handle the error and possibly exit
      MessageBox (NULL, "MonitorThread: GetMessage error", "COM Port:", 
MB_OK | MB_ICONSTOP);
      return NULL;  // error!
    }
    else
    { // a valid message is received
      switch (msg.message)
      {
      case (QS_ALLPOSTMESSAGE + MSG_COMOPEN):
        printf ("\r\nMonitorThread: MSG_COMOPEN: lParam=0x%08x 
wParam=0x%08x",
          msg.lParam, msg.wParam);
        break;

      case (QS_ALLPOSTMESSAGE + MSG_COMCLOSE):
        printf ("\r\nMonitorThread: MSG_COMCLOSE: lParam=0x%08x 
wParam=0x%08x",
          msg.lParam, msg.wParam);
        break;

      case (QS_ALLPOSTMESSAGE + MSG_TIME):
        // Time stamp message: lParam=time, wParam=dwEvtMask
        szMsg[0] = '\0';

        if (DWORD(msg.wParam) & EV_CTS)
        {
          GetCommModemStatus (hCom, &COMStatus);
          if (COMStatus & MS_CTS_ON)
          { // CTS (Pin8) activated
            wsprintf (szMsgTmp, "\r\nCTS_ON : %lu µs", msg.lParam);
          }
          else
          { // CTS (Pin8) deactivated
            wsprintf (szMsgTmp, "\r\nCTS_OFF: %lu µs", msg.lParam);
          }
          strcat (szMsg, szMsgTmp);
        }

        if (DWORD(msg.wParam) & EV_DSR)
        {
          GetCommModemStatus (hCom, &COMStatus);
          if (COMStatus & MS_DSR_ON)
          { // DSR (Pin6) activated
            wsprintf (szMsgTmp, "\r\nDSR_ON : %lu µs", msg.lParam);
          }
          else
          { // DSR (Pin6) deactivated
            wsprintf (szMsgTmp, "\r\nDSR_OFF: %lu µs", msg.lParam);
          }
          strcat (szMsg, szMsgTmp);
        }

        if (DWORD(msg.wParam) & EV_RLSD)
        {
          GetCommModemStatus (hCom, &COMStatus);
          if (COMStatus & MS_RLSD_ON)
          { // DCD (Pin1) activated
            wsprintf (szMsgTmp, "\r\nRLSD_ON: %lu µs", msg.lParam);
          }
          else
          { // DCD (Pin1) deactivated
            wsprintf (szMsgTmp, "\r\nRLSD_OFF: %lu µs ", msg.lParam);
          }
          strcat (szMsg, szMsgTmp);
        }

        if (DWORD(msg.wParam) & EV_RING)
        {
          GetCommModemStatus (hCom, &COMStatus);
          if ((COMStatus & MS_RING_ON))
          { // RI (Pin9) activated
            wsprintf (szMsgTmp, "\r\nRING_ON: %lu µs", msg.lParam);
          }
          else
          { // RI (Pin9) deactivated
            wsprintf (szMsgTmp, "\r\nRING_OFF: %lu µs", msg.lParam);
          }
          strcat (szMsg, szMsgTmp);
        }

        printf (szMsg);
        break;

      default:
        wsprintf (szMsg, "\r\nMonitorThread: default: lParam=0x%08x 
wParam=0x%08x message=0x%08x",
          msg.lParam, msg.wParam, msg.message);
        MessageBox (NULL, (LPCTSTR)szMsg, "COM Port:", MB_OK | 
MB_ICONSTOP);
        break;
      }  // end of: switch (msg.message)
    }    // end of: if (bRet == -1) else ...
  }      // end of: while((bRet = GetMessage(...

  printf ("\r\nMonitorThread: beendet");
  return 0;
}


// Globale Variablen
// struct für Zeitmessung
union ut_LargeInteger
{
  LARGE_INTEGER o_WinPart;
  ULONGLONG       l_MyPart;
};

int i_ResetPrecisionTime = 0;
ULONGLONG l_PerfFrequ;
ut_LargeInteger uo_PerfCount;

/*********************************************************************** 
*
Die Funktion queryPrecisionTimeStamp () liefert die seit dem Start des 
PC
verstrichene Zeit in Mikrosekunden.
************************************************************************ 
/
long queryPrecisionTimeStamp (void)
{
  if (i_ResetPrecisionTime == 0)
  {
    i_ResetPrecisionTime = 1;
    QueryPerformanceFrequency (&uo_PerfCount.o_WinPart);
    l_PerfFrequ = uo_PerfCount.l_MyPart;
  }

  ut_LargeInteger uo_perfCount;
  QueryPerformanceCounter (&uo_perfCount.o_WinPart);
  return (long) (uo_perfCount.l_MyPart * 1000000 / l_PerfFrequ);
}

// *********************************************************************
int main (void)
{
  DWORD  dwMonThreadId;
  DWORD  dwComThreadId;
  HANDLE hMonThread;
  HANDLE hComThread;
  DWORD  dwState;
  PARAMS p;

  // MonitorThread erstellen:
  // Erstellt den Event für die Thread Queue
  hEvent = CreateEvent (NULL, false, true, QEVENT);
  // Thread starten und uMonThreadID für ComThread merken
  hMonThread = CreateThread (      // Handle des Threads
      NULL,                        // no security attributes
      0,                           // use default stack size
      MonitorThread,               // thread function
      &p,                          // argument to thread function
      0,                           // use default creation flags
      &dwMonThreadId);             // returns the thread identifier

  if (hMonThread == NULL)
    printf ("\r\nCreateThread (MonitorThread) fehlgeschlagen");

  p.dwMonitorThreadId = dwMonThreadId;  // merken, für ComThread

  // wait for queue completition
  dwState = WaitForSingleObject (hEvent, 100);
  CloseHandle (hEvent);

  // "alten" Thread stoppen
  p.ThreadStoppen = TRUE;
  CloseHandle (hComThread); // "alten" Thread stoppen
  p.iComPortNum = 1;
  // ComThread: Starten und Parameterstruct übergeben
  p.ThreadStoppen = FALSE;
  hComThread = CreateThread (    // Handle des Threads
    NULL,                        // no security attributes
    0,                           // use default stack size
    ComThread,                   // thread function
    &p,                          // argument to thread function
    0,                           // use default creation flags
    &dwComThreadId);             // returns the thread identifier

  if (hComThread == NULL)
    printf ("\r\nCreateThread (ComThread) fehlgeschlagen");

  Sleep (30000); // nur so zum Test

  CloseHandle (hMonThread);    // MonitorThread beenden
  CloseHandle (hComThread);    // ComThread beenden

  return (0);
}

Die main-funktion sollte noch angepaßt werden, z.B. mit einer Schleife. 
Jetzt ist sie nach 30 Sekunden zu Ende.

Blackbird

von Philipp B. (philipp_burch)


Lesenswert?

Also ich würde dazu ja VB empfehlen, da ist sowas in 10 - 20 Zeilen 
erledigt...

von Hans W. (walti)


Lesenswert?

Hallo Blackbird, vielen Dank erstmal für den Source-Code

Wieviel mA darf ich maximal am RS232 anschließen ohne das ich das 
Motherboard völlig brate?

Und bei diesem Programm muss ich also eine Spannung von 5 Volt an 
beispielsweise Pin8 anlegen und Pin5 auf Masse legen.
Falls die Lichtschranke dann durchbrochen wird, fällt die Spannung auf 0 
und das Programm reagiert darauf und stoppt?
Ich hoffe mal das ich das richtig verstanden habe.

mfg Walti

PS: sorry Phillipp aber leider beherrsche ich kein VB deshalb werde ich 
auch die Version von Blackbird verwenden, nicht böse sein ;)

von Blackbird (Gast)


Lesenswert?

War ein paar Tagen nicht anwesend, kann also erst jetzt antworten:

"Wieviel mA darf ich maximal am RS232 anschließen ohne das ich das
Motherboard völlig brate?"

Die PC-Hardware begrenzt den Strom, dem Motherboard geschieht nichts. 
5mA an einem Ausgang sind so die Grenze, ist jedoch von PC zu Pc 
verschieden. Die Spannung beträgt dabei noch ca. max. 8V.

Einen Ausgang (z.B. RTS Pin7) kann man so auf -10V (gegen Masse Pin5) 
setzen:

EscapeCommFunction (hPort, SETRTS); // RTS setzten Pin7

Auf +10V kann man in so setzen:

EscapeCommFunction (hPort, CLRRTS); // RTS rücksetzten Pin7


"Und bei diesem Programm muss ich also eine Spannung von 5 Volt an
beispielsweise Pin8 anlegen und Pin5 auf Masse legen."

Ja


"Falls die Lichtschranke dann durchbrochen wird, fällt die Spannung auf 
0
und das Programm reagiert darauf und stoppt?
Ich hoffe mal das ich das richtig verstanden habe."

Ja
Ja



"PS: sorry Phillipp aber leider beherrsche ich kein VB deshalb werde ich
auch die Version von Blackbird verwenden, nicht böse sein ;)"

Das VB-Programm würde ich auch gerne sehen. Muß nicht kommentiert sein, 
ich programmiere auch VB und denke, dass ich es "pur" verstehe.


Blackbird

von Blackbird (Gast)


Lesenswert?

Kleiner Nachtrag:
Nicht das Programm stoppt, sondern die Zeit für das Auftreten dieses 
Ereignisses (Pegelwechsel an Pin8) wird "gestoppt".

Blackbird

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.