drivers/timers_win32/timers_win32.cpp
author etisserant
Fri, 01 Feb 2008 18:06:55 +0100
changeset 378 d2abf6c8c27b
parent 267 96c688ebcde7
child 406 92e28415d026
permissions -rwxr-xr-x
As requested long ago, added CoData* parameter to all this applications callback, let application designer use identical callback for multiple nodes, and reduce source code length.

Concerned callbacks :

initialisation
preOperational
operational
stopped
heartbeatError
post_sync
post_TPDO
post_SlaveBootup
storeODSubIndex
post_emcy
/*
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();
   }