Hubo Library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
SampleCallbacks.cpp
Go to the documentation of this file.
1 #include <stdio.h>
2 #include <unistd.h>
3 
4 #include "../hubolib.h"
5 
6 using namespace HuboLib;
7 
8 /*
9 Compile and link:
10  g++ SampleCallbacks.cpp -L../ -lhubo -lpthread -lrt -o SampleCallbacks.out
11 Run:
12  sudo ./SampleCallbacks.out
13 Purpose:
14  Implementing a proper working controller algorithm requires to gather data equidistant
15  in time.
16  The cycle time already defines a time slice that will be used for gathering data.
17  Use Register_CycleTickCallback() to register a notification callback which is executed
18  at the end of each cycle.
19  Register_ChannelDelayCallback() can be used to get notifications when a cycle time is
20  violated by more then the length of one cycle.
21  Be aware of the fact that these callbacks are executed in the background thread and
22  therefore require synchronisation to any other thread of your own (including the main
23  thread)! Also note that the callback should not do lengthly operations as this would
24  cause it to delay its operation and the next cycle time might get dismissed!
25 */
26 
27 void ChannelDelayCallback (__int64 cycleCount, __int64 t_Overrun_ms);
28 void CycleTickCallback (unsigned long* pADChannelValues, unsigned char* pDigitalInputValues, unsigned char* pDigitalOutputValues);
29 void BreakingCycleTickCallback (unsigned long* pADChannelValues, unsigned char* pDigitalInputValues, unsigned char* pDigitalOutputValues);
30 
31 int main(void)
32 {
33  // Initialize the library once in your program.
34  if (!Initialize())
35  {
36  printf ("Error: Initialize\n");
37  return 1;
38  }
39 
40  // Define channel 0 to be oversampled 40 times (set to 40) all other channels (set to 0) are not read from the ADC.
41  unsigned short overSampling[MAX_MCP3x08_CHANNELS] = { 1, 0, 0, 0, 0, 0, 0, 0 };
42  Set_MCP3x08_Oversampling (overSampling);
43 
44  // Let's sample every 250ms (4Hz).
45  Set_Cycle_Time (250);
46 
47  // Register the 2 callbacks.
50 
51  int channel = 0;
52  unsigned long adcCount;
53  double volt;
54  for (int i=0; i<10; i++)
55  {
56  Get_AI_Channel (channel, adcCount, volt);
57  printf ("Main thread: Channel=%d ADC count=0x%02lX Volts=%lf\n\n", channel, adcCount, volt);
58 
59  // Wait a second.
60  usleep(1000000);
61  }
62  // Result:
63  // You'll see a frequent calling of CycleTickCallback at a rate of 4Hz. Each second (1Hz) the main thread
64  // will also output the value. Since the cycle time will not be broken there are no calls to ChannelDelayCallback()
65  // for the time being.
66 
67  printf ("Now breaking the cycle time.\n");
68 
69  // The easiest way to break the cycle time of the background thread is to have it sleep for a time which
70  // lasts longer than the cycle time is defined. Hence we'll let it sleep for 300ms.
71  // Note that due to the higher thread priority of the background thread you'll not be able break the cycle
72  // time from within the main thread or any other thread that runs at a lower priority.
73 
74  // Unregister the current cycle tick callback and reregister a new one that will break the cycle time.
77 
78  for (int i=0; i<10; i++)
79  {
80  Get_AI_Channel (channel, adcCount, volt); // Comment out as described below.
81  printf ("Main thread: Channel=%d ADC count=0x%02lX Volts=%lf\n\n", channel, adcCount, volt);
82 
83  // Wait a second.
84  usleep(1000000);
85  }
86 
87  // Result:
88  // On top of the output above you'll see calls made to ChannelDelayCallback with an increasing delay.
89  // One more thing that is not that straight forward. Even though the for-loop seems to end - it will not!
90  // Since the background thread runs at high priority and is rescheduled soon after it left its usleep
91  // it will prevent the mainthread from getting rescheduled. Strange thing - as the background thread runs
92  // into a usleep you might think. That's true. But the main thread tries to call Get_AI_Channel()
93  // which internally runs against a mutex to sync the buffered sample data between the background thread
94  // and the threads calling into the library (in this case the main thread). Since the background thread
95  // still owns the mutex while calling into the callback - the library is blocked for "client" calls.
96 
97  // Conclusion:
98  // Do not perform any time intensive functions within the callback functions.
99  // During the execution of the callback functions any other threads are blocked to use the library.
100  // Also do not modify the data passed into the callbacks. A modification would directly impact the
101  // internal buffers. Hence - never keep a copy of the pointers in order to pass it around!
102  // A good style would be perform a copy of the data and use it the same way as you'd be doing in any other
103  // multi threaded application (use mutex access for accessing your copies).
104 
105  // Comment out the Get_AI_Channel() call above and the application would nearly exit! The reason it still
106  // doesn't ist because the library is trying to catch up with the cycle tick calls but cannot as with
107  // every new call it's loosing even more time.
108  // Alternativley have the background thread get preempted (e.g. by a call to usleep() in order
109  // to let an application continue its flow of control (e.g. terminate).
110 
111  // Once not needed anymore the callbacks can be unregistered.
114 
115  // Free library resources.
116  Uninitialize();
117 
118  return 0;
119 }
120 
121 void CycleTickCallback (unsigned long* pADChannelValues, unsigned char* pDigitalInputValues, unsigned char* pDigitalOutputValues)
122 {
123  // Avoid lengthly operations such as printf's etc. in reality. This is just for demonstration!
124 
125  printf ("CycleTickCallback called.\n");
126  if (pADChannelValues)
127  {
128  for (int i=0; i<8; i++)
129  printf("0x%04lX ", pADChannelValues[i]);
130  printf("\n");
131  }
132 
133  if (pDigitalInputValues)
134  printf ("Digital input 0x%02X\n", *pDigitalInputValues);
135 
136  if (pDigitalOutputValues)
137  printf ("Digital output 0x%02X\n", *pDigitalOutputValues);
138 
139  printf ("\n");
140 }
141 
142 void BreakingCycleTickCallback (unsigned long* pADChannelValues, unsigned char* pDigitalInputValues, unsigned char* pDigitalOutputValues)
143 {
144  printf ("BreakingCycleTickCallback called.\n");
145  CycleTickCallback(pADChannelValues, pDigitalInputValues, pDigitalOutputValues);
146 
147  // Sleeping for 300ms will break the cycle time (250ms) for 50ms.
148  usleep (300 * 1000);
149 }
150 
151 void ChannelDelayCallback (__int64 cycleCount, __int64 t_Overrun_ms)
152 {
153  // Avoid lengthly operations such as printf's etc. in reality. This is just for demonstration!
154 
155  printf ("ChannelDelayCallback called\n");
156  printf ("delay: count = %lld time[ms] = %lld\n\n", cycleCount, t_Overrun_ms);
157 
158  // Preepmting the background thread while it is late already will further increase its delay.
159  // However, this might be a last resort to get an application to exit while the cycle time
160  // is permanently broken. In such a case the design of the software should be considered to
161  // get changed.
162  // Uncomment the line below and the application will exit.
163  // usleep (100 * 1000);
164 }
165 
166 
167 
#define MAX_MCP3x08_CHANNELS
Definition: hubolib.h:39
bool Unregister_ChannelDelayCallback(T_pfn_ChannelDelayCallback pFnChannelDelayCallback)
Unregister a callback previouly registered by a call to Register_ChannelDelayCallback().
bool Initialize()
Initializes the library.
bool Set_Cycle_Time(long cycleTime)
Sets the backgrounds threads polling interval in ms.
void CycleTickCallback(unsigned long *pADChannelValues, unsigned char *pDigitalInputValues, unsigned char *pDigitalOutputValues)
void ChannelDelayCallback(__int64 cycleCount, __int64 t_Overrun_ms)
void BreakingCycleTickCallback(unsigned long *pADChannelValues, unsigned char *pDigitalInputValues, unsigned char *pDigitalOutputValues)
bool Unregister_CycleTickCallback(T_pfn_CycleTickCallback pFnCycleTickCallback)
Unregister a callback previouly registered by a call to Register_CycleTickCallback().
#define __int64
Definition: hubolib.h:47
bool Get_AI_Channel(int channel, unsigned long &count, double &volt)
Get the buffered and oversampled data from the MCP3x08.
bool Set_MCP3x08_Oversampling(unsigned short overSampling[MAX_MCP3x08_CHANNELS])
Specifies the ADC channels to be sampled as well as the number they get oversampled.
void Uninitialize()
Releases any resources bound to the library.
bool Register_ChannelDelayCallback(T_pfn_ChannelDelayCallback pFnChannelDelayCallback)
Register a callback to be called whenever the cycle time has been violated by more than one cycle tim...
bool Register_CycleTickCallback(T_pfn_CycleTickCallback pFnCycleTickCallback)
Registers a callback function called on every cycle tick.
int main(void)