drivers/timers_win32/timers_win32.cpp
changeset 267 96c688ebcde7
child 406 92e28415d026
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/timers_win32/timers_win32.cpp	Mon Sep 10 08:04:32 2007 +0200
@@ -0,0 +1,241 @@
+/*
+This file is part of CanFestival, a library implementing CanOpen Stack.
+
+Copyright (C): Edouard TISSERANT and Francis DUPIN
+Copyright (C) Win32 Port Leonid Tochinski
+
+See COPYING file for copyrights details.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+
+
+#include <windows.h>
+#include <stdlib.h>
+
+extern "C"
+{
+#include "applicfg.h"
+#include "can_driver.h"
+#include "timer.h"
+#include "timers_driver.h"
+};
+
+// --------------- Synchronization Object Implementation ---------------
+class ccritical_section
+   {
+   public:
+      ccritical_section()
+         {
+         ::InitializeCriticalSection(&m_cs);
+         }
+      ~ccritical_section()
+         {
+         ::DeleteCriticalSection(&m_cs);
+         }
+      void enter()
+         {
+         ::EnterCriticalSection(&m_cs);
+         }
+      void leave()
+         {
+         ::LeaveCriticalSection(&m_cs);
+         }
+   private:
+      CRITICAL_SECTION m_cs;
+   };
+
+static ccritical_section g_cs;
+
+
+void EnterMutex(void)
+   {
+   g_cs.enter();
+   }
+
+void LeaveMutex(void)
+   {
+   g_cs.leave();
+   }
+// --------------- Synchronization Object Implementation ---------------
+
+
+// --------------- CAN Receive Thread Implementation ---------------
+
+void CreateReceiveTask(CAN_HANDLE fd0, TASK_HANDLE* Thread, void* ReceiveLoopPtr)
+   {
+   unsigned long thread_id = 0;
+   *Thread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReceiveLoopPtr, fd0, 0, &thread_id);
+   }
+
+void WaitReceiveTaskEnd(TASK_HANDLE Thread)
+   {
+   ::WaitForSingleObject(Thread, INFINITE);
+   ::CloseHandle(Thread);
+   //*Thread = NULL;
+   }
+// --------------- CAN Receive Thread Implementation ---------------
+
+
+// --------------- Timer Thread Implementation ---------------
+class class_timers
+   {
+   public:
+      class_timers();
+      ~class_timers();
+      void start_timer_thread();
+      void resume_timer_thread();
+      void stop_timer_thread();
+      void set_timer(TIMEVAL value);
+      TIMEVAL get_elapsed_time();
+   private:
+      TIMEVAL get_timer() const;   
+      static DWORD WINAPI timer_loop_thread_proc(void* arg);
+   private:
+      TIMEVAL m_last_occured_alarm_time;
+      volatile TIMEVAL m_last_alarm_set_time;
+      HANDLE m_timer_thread;
+      volatile bool m_continue_timer_loop;
+      bool m_use_hi_res_timer;
+      double m_counts_per_usec;
+   };
+
+class_timers::class_timers() : m_last_occured_alarm_time(TIMEVAL_MAX),
+      m_last_alarm_set_time(TIMEVAL_MAX),
+      m_timer_thread(0),
+      m_continue_timer_loop(false),
+      m_use_hi_res_timer(false),
+      m_counts_per_usec(0.)
+   {
+   // initialize hi resolution timer
+   LARGE_INTEGER counts_per_sec = {0, 0};
+   if (::QueryPerformanceFrequency(&counts_per_sec) && counts_per_sec.QuadPart > 0)
+      {
+      m_use_hi_res_timer = true;
+      m_counts_per_usec = counts_per_sec.QuadPart / 1000000.;
+      }
+   m_use_hi_res_timer = true;
+   }
+
+class_timers::~class_timers()
+   {
+   stop_timer_thread();
+   }
+
+// time is in micro seconds
+TIMEVAL class_timers::get_timer() const
+   {
+   if (m_use_hi_res_timer)
+      {
+      LARGE_INTEGER performance_count = {0, 0};
+      ::QueryPerformanceCounter(&performance_count);
+      return (TIMEVAL)(performance_count.QuadPart / m_counts_per_usec);
+      }
+   // hi-res timer is unavailable
+   return 1000 * ::GetTickCount();
+   }
+
+DWORD WINAPI class_timers::timer_loop_thread_proc(void* arg)
+   {
+   class_timers* This = reinterpret_cast<class_timers*>(arg);
+   while (This->m_continue_timer_loop)
+      {
+      TIMEVAL cur_time = This->get_timer();
+      if (cur_time >= This->m_last_alarm_set_time)
+         {
+         This->m_last_occured_alarm_time = cur_time;
+         This->m_last_alarm_set_time = TIMEVAL_MAX;         
+         EnterMutex();
+         TimeDispatch();
+         LeaveMutex();
+         }
+      else
+         {
+         ::Sleep(1);
+         }
+      }
+   return 0;
+   }
+
+void class_timers::start_timer_thread()
+   {
+   if (m_timer_thread == 0)
+      {
+      unsigned long thread_id = 0;
+      m_timer_thread = ::CreateThread(NULL, 0, &timer_loop_thread_proc, this, CREATE_SUSPENDED, &thread_id);
+      m_last_alarm_set_time = TIMEVAL_MAX;
+      m_last_occured_alarm_time = get_timer();
+      }
+   }
+
+void class_timers::resume_timer_thread()
+   {
+   if (m_timer_thread)
+      {
+      m_continue_timer_loop = true;
+      ::ResumeThread(m_timer_thread);
+      }
+   }
+
+void class_timers::stop_timer_thread()
+   {
+   if (m_timer_thread)
+      {
+      m_continue_timer_loop = false;
+      ::WaitForSingleObject(m_timer_thread, INFINITE);
+      ::CloseHandle(m_timer_thread);
+      m_timer_thread = 0;
+      }
+   }
+
+void class_timers::set_timer(TIMEVAL value)
+   {
+   m_last_alarm_set_time = (value == TIMEVAL_MAX) ? TIMEVAL_MAX : get_timer() + value;
+   }
+
+// elapsed time since last occured alarm
+TIMEVAL class_timers::get_elapsed_time()
+   {
+   return get_timer() - m_last_occured_alarm_time;
+   }
+
+// ----------------------------------------------------------
+
+static class_timers s_timers;
+
+void StartTimerLoop(TimerCallback_t init_callback)
+   {
+   s_timers.start_timer_thread();
+   // At first, TimeDispatch will call init_callback.
+   if (init_callback != NULL)
+      SetAlarm(NULL, 0, init_callback, (TIMEVAL)0, (TIMEVAL)0);
+   s_timers.resume_timer_thread();
+   }
+
+void StopTimerLoop(void)
+   {
+   s_timers.stop_timer_thread();
+   }
+
+void setTimer(TIMEVAL value)
+   {
+   s_timers.set_timer(value);
+   }
+
+TIMEVAL getElapsedTime(void)
+   {
+   return s_timers.get_elapsed_time();
+   }