1 /* |
|
2 This file is part of CanFestival, a library implementing CanOpen Stack. |
|
3 |
|
4 CanFestival Copyright (C): Edouard TISSERANT and Francis DUPIN |
|
5 CanFestival Win32 port Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc. |
|
6 |
|
7 See COPYING file for copyrights details. |
|
8 |
|
9 This library is free software; you can redistribute it and/or |
|
10 modify it under the terms of the GNU Lesser General Public |
|
11 License as published by the Free Software Foundation; either |
|
12 version 2.1 of the License, or (at your option) any later version. |
|
13 |
|
14 This library is distributed in the hope that it will be useful, |
|
15 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 Lesser General Public License for more details. |
|
18 |
|
19 You should have received a copy of the GNU Lesser General Public |
|
20 License along with this library; if not, write to the Free Software |
|
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
22 */ |
|
23 // pragma based message |
|
24 // http://www.codeproject.com/macro/location_pragma.asp |
|
25 #define __STR2__(x) #x |
|
26 #define __STR1__(x) __STR2__(x) |
|
27 #define __LOC2__ __FILE__ "("__STR1__(__LINE__)") : " |
|
28 |
|
29 |
|
30 #pragma message("*********************************************************************************") |
|
31 #pragma message(" NOTE: IXXAT Win32 drivers and API should be installed in order to build this project!") |
|
32 #pragma message(__LOC2__ "See IXXAT.Cpp header for details.") |
|
33 #pragma message("*********************************************************************************") |
|
34 |
|
35 |
|
36 // IXXAT adapter driver for CanFestival-3 Win32 port |
|
37 // |
|
38 // Notes |
|
39 //-------------------------------------------- |
|
40 // For building of this project you will need |
|
41 // the following IXXAT API files |
|
42 // Vci2.h |
|
43 // Vci11un6.lib |
|
44 // |
|
45 // IXXAT Win32 drivers and API can be downloaded from |
|
46 // http://www.ixxat.com/download_vci_en,7547,5873.html |
|
47 // |
|
48 // Copy Vci2.h & Vci11un6.lib files to can_ixxat_win32 folder of add path to them in Project settings. |
|
49 |
|
50 |
|
51 #include <stdio.h> |
|
52 extern "C" { |
|
53 #include "applicfg.h" |
|
54 #include "can_driver.h" |
|
55 #include "def.h" |
|
56 } |
|
57 #include "VCI2.h" |
|
58 #include "async_access_que.h" |
|
59 |
|
60 #define CAN_NUM 0 |
|
61 |
|
62 class IXXAT |
|
63 { |
|
64 public: |
|
65 class error |
|
66 { |
|
67 }; |
|
68 IXXAT(s_BOARD *board); |
|
69 ~IXXAT(); |
|
70 bool send(const Message *m); |
|
71 bool receive(Message *m); |
|
72 |
|
73 static bool isDriverClosed() {return m_driverClosed;} |
|
74 private: |
|
75 bool open(int board_number, const char* baud_rate); |
|
76 bool close(); |
|
77 void receive_queuedata(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ* p_obj); |
|
78 // VCI2 handler |
|
79 static void VCI_CALLBACKATTR message_handler(char *msg_str); |
|
80 static void VCI_CALLBACKATTR receive_queuedata_handler(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ* p_obj); |
|
81 static void VCI_CALLBACKATTR exception_handler(VCI_FUNC_NUM func_num, INT32 err_code, UINT16 ext_err, char* err_str); |
|
82 |
|
83 static void CALLBACK canBusWatchdog(HWND hwnd, UINT msg, UINT_PTR idEvent, DWORD dwTime); |
|
84 void watchdog(); |
|
85 private: |
|
86 UINT16 m_BoardHdl; |
|
87 UINT16 m_TxQueHdl; |
|
88 UINT16 m_RxQueHdl; |
|
89 async_access_que<VCI_CAN_OBJ> m_RX_Que; |
|
90 static IXXAT* m_callbackPtr; |
|
91 |
|
92 static bool m_driverClosed; |
|
93 static UINT_PTR m_watchdogTimerId; |
|
94 static const unsigned int CAN_BUS_WATCHDOG_INTERVAL_MSEC = 10000; |
|
95 |
|
96 /** Bitmask inside sts from VCI_ReadCanStatus() that defines the can bus off state.*/ |
|
97 static const unsigned char STS_CAN_BUS_OFF = 0x80; |
|
98 |
|
99 /** Bitmask inside sts from VCI_ReadCanStatus() that defines the can data overrun state.*/ |
|
100 static const unsigned char STS_CAN_DATA_OVERRUN = 0x20; |
|
101 |
|
102 /** Bitmask inside sts from VCI_ReadCanStatus() that defines the remote queue overrun state.*/ |
|
103 static const unsigned char STS_REMOTE_QUEUE_OVERRUN = 0x04; |
|
104 }; |
|
105 |
|
106 IXXAT *IXXAT::m_callbackPtr = NULL; |
|
107 |
|
108 UINT_PTR IXXAT::m_watchdogTimerId = 0; |
|
109 |
|
110 bool IXXAT::m_driverClosed = false; |
|
111 |
|
112 IXXAT::IXXAT(s_BOARD *board) : m_BoardHdl(0xFFFF), |
|
113 m_TxQueHdl(0xFFFF), |
|
114 m_RxQueHdl(0xFFFF) |
|
115 |
|
116 { |
|
117 if (!board) |
|
118 { |
|
119 close(); |
|
120 throw error(); |
|
121 } |
|
122 |
|
123 long board_number = 0; |
|
124 |
|
125 if (board->busname) |
|
126 { |
|
127 board_number = atol(board->busname); |
|
128 } |
|
129 |
|
130 if (!open(board_number, board->baudrate)) |
|
131 { |
|
132 close(); |
|
133 throw error(); |
|
134 } |
|
135 m_callbackPtr = this; |
|
136 } |
|
137 |
|
138 IXXAT::~IXXAT() |
|
139 { |
|
140 close(); |
|
141 m_callbackPtr = 0; |
|
142 } |
|
143 |
|
144 bool IXXAT::send(const Message *m) |
|
145 { |
|
146 if (m_BoardHdl == 0xFFFF) |
|
147 return true; // true -> NOT OK |
|
148 long res = VCI_ERR; |
|
149 if (m->rtr == NOT_A_REQUEST) |
|
150 res = VCI_TransmitObj(m_BoardHdl, m_TxQueHdl, m->cob_id, m->len, const_cast<unsigned char*>(m->data)); |
|
151 else |
|
152 res = VCI_RequestObj(m_BoardHdl, m_TxQueHdl, m->cob_id, m->len); |
|
153 |
|
154 return (res == VCI_OK); |
|
155 } |
|
156 |
|
157 |
|
158 bool IXXAT::receive(Message *m) |
|
159 { |
|
160 if (m_BoardHdl == 0xFFFF) |
|
161 return false; |
|
162 VCI_CAN_OBJ obj; |
|
163 if (m_RX_Que.extract_top(obj)) |
|
164 { |
|
165 m->cob_id = static_cast<UNS16>(obj.id); //valid for 11Bit ids |
|
166 m->len = obj.len; |
|
167 m->rtr = (obj.rtr == VCI_RX_BUF) ? NOT_A_REQUEST : REQUEST; |
|
168 if (m->rtr == NOT_A_REQUEST) |
|
169 ::memcpy(m->data, obj.a_data, m->len); |
|
170 return true; |
|
171 } |
|
172 return false; |
|
173 } |
|
174 |
|
175 bool IXXAT::open(int board_number, const char* baud_rate) |
|
176 { |
|
177 // check, if baudrate is supported |
|
178 struct IXXAT_baud_rate_param |
|
179 { |
|
180 UINT8 bt0; |
|
181 UINT8 bt1; |
|
182 }; |
|
183 struct IXXAT_look_up_table |
|
184 { |
|
185 char baud_rate[20]; |
|
186 IXXAT_baud_rate_param bt; |
|
187 }; |
|
188 static const IXXAT_look_up_table br_lut[] = { |
|
189 {"10K",{VCI_10KB}}, |
|
190 {"20K",{VCI_20KB}}, |
|
191 {"50K",{VCI_50KB}}, |
|
192 {"100K",{VCI_100KB}}, |
|
193 {"125K",{VCI_125KB}}, |
|
194 {"250K",{VCI_250KB}}, |
|
195 {"500K",{VCI_500KB}}, |
|
196 {"800K",{VCI_800KB}}, |
|
197 {"1M",{VCI_1000KB}} |
|
198 }; |
|
199 static const long br_lut_size = sizeof (br_lut)/sizeof(IXXAT_look_up_table); |
|
200 int index; |
|
201 for (index = 0; index < br_lut_size; ++index) |
|
202 { |
|
203 if (::strcmp(br_lut[index].baud_rate,baud_rate)==0) |
|
204 break; |
|
205 } |
|
206 if (index == br_lut_size) |
|
207 { |
|
208 MSG_ERR_DRV("IXXAT::open: The given baudrate %S is invalid.", baud_rate); |
|
209 return false; |
|
210 } |
|
211 // close existing board |
|
212 close(); |
|
213 // init IXXAT board |
|
214 long res = VCI2_PrepareBoard( 0, // board type, unused in VCI2 |
|
215 board_number, // unique board index, see XAT_EnumHwEntry() and XAT_GetConfig() |
|
216 NULL, // pointer to buffer for additional info |
|
217 0, // length of additional info buffer |
|
218 message_handler, // pointer to msg-callbackhandler |
|
219 receive_queuedata_handler, // pointer to receive-callbackhandler |
|
220 exception_handler); // pointer to exception-callbackhandler |
|
221 if (res < 0) |
|
222 { |
|
223 MSG_ERR_DRV("IXXAT::open: VCI2_PrepareBoard failed with code '%d'.", res); |
|
224 return false; |
|
225 } |
|
226 m_BoardHdl = (UINT16)res; |
|
227 |
|
228 VCI_ResetBoard(m_BoardHdl); |
|
229 |
|
230 // init CAN parameters |
|
231 |
|
232 // initialize CAN-Controller |
|
233 res = VCI_InitCan(m_BoardHdl, CAN_NUM, br_lut[index].bt.bt0,br_lut[index].bt.bt1, VCI_11B); |
|
234 |
|
235 // definition of Acceptance-Mask (define to receive all IDs) |
|
236 res = VCI_SetAccMask(m_BoardHdl, CAN_NUM, 0x0UL, 0x0UL); |
|
237 |
|
238 // definition of Transmit Queue |
|
239 res = VCI_ConfigQueue(m_BoardHdl, CAN_NUM, VCI_TX_QUE, 100 , 0, 0, 0, &m_TxQueHdl); |
|
240 |
|
241 // definition of Receive Queue (interrupt mode) |
|
242 res = VCI_ConfigQueue(m_BoardHdl, CAN_NUM, VCI_RX_QUE, 500, 1, 0, 100, &m_RxQueHdl); |
|
243 |
|
244 // assign the all IDs to the Receive Queue |
|
245 res = VCI_AssignRxQueObj(m_BoardHdl, m_RxQueHdl ,VCI_ACCEPT, 0, 0); |
|
246 |
|
247 // And now start the CAN |
|
248 res = VCI_StartCan(m_BoardHdl, CAN_NUM); |
|
249 |
|
250 //Start CAN Bus-Off watchdog |
|
251 m_watchdogTimerId = SetTimer(NULL, NULL, IXXAT::CAN_BUS_WATCHDOG_INTERVAL_MSEC, IXXAT::canBusWatchdog); |
|
252 |
|
253 m_driverClosed = false; |
|
254 |
|
255 return true; |
|
256 } |
|
257 |
|
258 bool IXXAT::close() |
|
259 { |
|
260 m_driverClosed = true; |
|
261 if (m_BoardHdl == 0xFFFF) |
|
262 return true; |
|
263 VCI_ResetBoard(m_BoardHdl); |
|
264 VCI_CancelBoard(m_BoardHdl); |
|
265 m_BoardHdl = |
|
266 m_TxQueHdl = |
|
267 m_RxQueHdl = 0xFFFF; |
|
268 return true; |
|
269 } |
|
270 |
|
271 void IXXAT::receive_queuedata(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ *p_obj) |
|
272 { |
|
273 for (int i = 0; i < count; ++i) |
|
274 m_RX_Que.append(p_obj[i]); // can packet |
|
275 } |
|
276 |
|
277 void VCI_CALLBACKATTR IXXAT::receive_queuedata_handler(UINT16 que_hdl, UINT16 count, VCI_CAN_OBJ *p_obj) |
|
278 { |
|
279 if (m_callbackPtr != NULL) |
|
280 m_callbackPtr->receive_queuedata(que_hdl, count, p_obj); |
|
281 } |
|
282 |
|
283 void VCI_CALLBACKATTR IXXAT::message_handler(char *msg_str) |
|
284 { |
|
285 MSG_ERR_DRV("IXXAT Message: [%S]", msg_str); |
|
286 } |
|
287 |
|
288 void VCI_CALLBACKATTR IXXAT::exception_handler(VCI_FUNC_NUM func_num, INT32 err_code, UINT16 ext_err, char* err_str) |
|
289 { |
|
290 static const char* Num2Function[] = |
|
291 { |
|
292 "VCI_Init", |
|
293 "VCI_Searchboard", |
|
294 "VCI_Prepareboard", |
|
295 "VCI_Cancel_board", |
|
296 "VCI_Testboard", |
|
297 "VCI_ReadBoardInfo", |
|
298 "VCI_ReadBoardStatus", |
|
299 "VCI_Resetboard", |
|
300 "VCI_ReadCANInfo", |
|
301 "VCI_ReadCANStatus", |
|
302 "VCI_InitCAN", |
|
303 "VCI_SetAccMask", |
|
304 "VCI_ResetCAN", |
|
305 "VCI_StartCAN", |
|
306 "VCI_ResetTimeStamps", |
|
307 "VCI_ConfigQueue", |
|
308 "VCI_AssignRxQueObj", |
|
309 "VCI_ConfigBuffer", |
|
310 "VCI_ReconfigBuffer", |
|
311 "VCI_ConfigTimer", |
|
312 "VCI_ReadQueStatus", |
|
313 "VCI_ReadQueObj", |
|
314 "VCI_ReadBufStatus", |
|
315 "VCI_ReadBufData", |
|
316 "VCI_TransmitObj", |
|
317 "VCI_RequestObj", |
|
318 "VCI_UpdateBufObj", |
|
319 "VCI_CciReqData" |
|
320 }; |
|
321 |
|
322 MSG_ERR_DRV("IXXAT Exception: %S (%i / %u) [%S]", Num2Function[func_num], err_code, ext_err, err_str); |
|
323 } |
|
324 |
|
325 void IXXAT::watchdog() |
|
326 { |
|
327 VCI_CAN_STS sts; |
|
328 long res = VCI_ReadCanStatus(m_BoardHdl, CAN_NUM, &sts); |
|
329 |
|
330 if (res < 0) |
|
331 { |
|
332 MSG_ERR_DRV("IXXAT canBusWatchdog: ERROR: Reading the can state failed!"); |
|
333 } |
|
334 else |
|
335 { |
|
336 if (sts.sts & (STS_CAN_BUS_OFF | STS_CAN_DATA_OVERRUN | STS_REMOTE_QUEUE_OVERRUN)) |
|
337 { |
|
338 if (sts.sts & STS_CAN_BUS_OFF) |
|
339 { |
|
340 MSG_ERR_DRV("IXXAT canBusWatchdog: CAN bus off detected!"); |
|
341 } |
|
342 if (sts.sts & STS_CAN_DATA_OVERRUN) |
|
343 { |
|
344 MSG_ERR_DRV("IXXAT canBusWatchdog: CAN data overrun detected!"); |
|
345 } |
|
346 if (sts.sts & STS_REMOTE_QUEUE_OVERRUN) |
|
347 { |
|
348 MSG_ERR_DRV("IXXAT canBusWatchdog: Remote queue overrun detected!"); |
|
349 } |
|
350 |
|
351 res = VCI_ResetCan(m_BoardHdl, CAN_NUM); |
|
352 if (res <= 0) |
|
353 { |
|
354 MSG_ERR_DRV("IXXAT canBusWatchdog: ERROR: Resetting the can module failed with code '%d'!", res); |
|
355 } |
|
356 |
|
357 res = VCI_StartCan(m_BoardHdl, CAN_NUM); |
|
358 if (res <= 0) |
|
359 { |
|
360 MSG_ERR_DRV("IXXAT canBusWatchdog: ERROR: Restaring the can module failed with code '%d'!", res); |
|
361 } |
|
362 } |
|
363 } |
|
364 |
|
365 if (SetTimer(NULL, m_watchdogTimerId, IXXAT::CAN_BUS_WATCHDOG_INTERVAL_MSEC, IXXAT::canBusWatchdog) == 0) |
|
366 { |
|
367 MSG_ERR_DRV("IXXAT canBusWatchdog: ERROR: Creation of the watchdog timer failed!"); |
|
368 } |
|
369 } |
|
370 |
|
371 void CALLBACK IXXAT::canBusWatchdog(HWND hwnd, UINT msg, UINT_PTR idEvent, DWORD dwTime) |
|
372 { |
|
373 if (m_callbackPtr != NULL) |
|
374 m_callbackPtr->watchdog(); |
|
375 } |
|
376 |
|
377 //------------------------------------------------------------------------ |
|
378 extern "C" |
|
379 UNS8 __stdcall canReceive_driver(CAN_HANDLE inst, Message *m) |
|
380 { |
|
381 if (IXXAT::isDriverClosed()) return 0; |
|
382 return reinterpret_cast<IXXAT*>(inst)->receive(m) ? 0 : 1; |
|
383 } |
|
384 |
|
385 extern "C" |
|
386 UNS8 __stdcall canSend_driver(CAN_HANDLE inst, Message const *m) |
|
387 { |
|
388 if (IXXAT::isDriverClosed()) return 0; |
|
389 return reinterpret_cast<IXXAT*>(inst)->send(m) ? 0 : 1; |
|
390 } |
|
391 |
|
392 extern "C" |
|
393 CAN_HANDLE __stdcall canOpen_driver(s_BOARD *board) |
|
394 { |
|
395 try |
|
396 { |
|
397 return new IXXAT(board); |
|
398 } |
|
399 catch (IXXAT::error&) |
|
400 { |
|
401 return 0; |
|
402 } |
|
403 } |
|
404 |
|
405 extern "C" |
|
406 int __stdcall canClose_driver(CAN_HANDLE inst) |
|
407 { |
|
408 delete reinterpret_cast<IXXAT*>(inst); |
|
409 return 1; |
|
410 } |
|
411 |
|
412 extern "C" |
|
413 UNS8 __stdcall canChangeBaudRate_driver( CAN_HANDLE fd, char* baud) |
|
414 { |
|
415 //printf("canChangeBaudRate not yet supported by this driver\n"); |
|
416 return 0; |
|
417 } |
|