CO_NMT_Heartbeat.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * CANopen NMT and Heartbeat producer object.
  3. *
  4. * @file CO_NMT_Heartbeat.c
  5. * @ingroup CO_NMT_Heartbeat
  6. * @author Janez Paternoster
  7. * @copyright 2004 - 2020 Janez Paternoster
  8. *
  9. * This file is part of CANopenNode, an opensource CANopen Stack.
  10. * Project home page is <https://github.com/CANopenNode/CANopenNode>.
  11. * For more information on CANopen see <http://www.can-cia.org/>.
  12. *
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. */
  25. #include "301/CO_NMT_Heartbeat.h"
  26. /*
  27. * Read received message from CAN module.
  28. *
  29. * Function will be called (by CAN receive interrupt) every time, when CAN
  30. * message with correct identifier will be received. For more information and
  31. * description of parameters see file CO_driver.h.
  32. */
  33. static void CO_NMT_receive(void *object, void *msg){
  34. CO_NMT_t *NMT;
  35. uint8_t nodeId;
  36. uint8_t DLC = CO_CANrxMsg_readDLC(msg);
  37. uint8_t *data = CO_CANrxMsg_readData(msg);
  38. NMT = (CO_NMT_t*)object; /* this is the correct pointer type of the first argument */
  39. nodeId = data[1];
  40. if ((DLC == 2) && ((nodeId == 0) || (nodeId == NMT->nodeId))){
  41. uint8_t command = data[0];
  42. #if (CO_CONFIG_NMT) & (CO_CONFIG_NMT_CALLBACK_CHANGE | CO_CONFIG_FLAG_CALLBACK_PRE)
  43. CO_NMT_internalState_t currentOperatingState = NMT->operatingState;
  44. #endif
  45. switch(command){
  46. case CO_NMT_ENTER_OPERATIONAL:
  47. if ((*NMT->emPr->errorRegister) == 0U){
  48. NMT->operatingState = CO_NMT_OPERATIONAL;
  49. }
  50. break;
  51. case CO_NMT_ENTER_STOPPED:
  52. NMT->operatingState = CO_NMT_STOPPED;
  53. break;
  54. case CO_NMT_ENTER_PRE_OPERATIONAL:
  55. NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  56. break;
  57. case CO_NMT_RESET_NODE:
  58. NMT->resetCommand = CO_RESET_APP;
  59. break;
  60. case CO_NMT_RESET_COMMUNICATION:
  61. NMT->resetCommand = CO_RESET_COMM;
  62. break;
  63. default:
  64. break;
  65. }
  66. #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE
  67. if (NMT->pFunctNMT!=NULL && currentOperatingState!=NMT->operatingState){
  68. NMT->pFunctNMT(NMT->operatingState);
  69. }
  70. #endif
  71. #if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_CALLBACK_PRE
  72. /* Optional signal to RTOS, which can resume task, which handles NMT. */
  73. if (NMT->pFunctSignalPre != NULL && currentOperatingState!=NMT->operatingState) {
  74. NMT->pFunctSignalPre(NMT->functSignalObjectPre);
  75. }
  76. #endif
  77. }
  78. }
  79. /******************************************************************************/
  80. CO_ReturnError_t CO_NMT_init(
  81. CO_NMT_t *NMT,
  82. CO_EMpr_t *emPr,
  83. uint8_t nodeId,
  84. uint16_t firstHBTime_ms,
  85. CO_CANmodule_t *NMT_CANdevRx,
  86. uint16_t NMT_rxIdx,
  87. uint16_t CANidRxNMT,
  88. CO_CANmodule_t *NMT_CANdevTx,
  89. uint16_t NMT_txIdx,
  90. uint16_t CANidTxNMT,
  91. CO_CANmodule_t *HB_CANdevTx,
  92. uint16_t HB_txIdx,
  93. uint16_t CANidTxHB)
  94. {
  95. CO_ReturnError_t ret = CO_ERROR_NO;
  96. /* verify arguments */
  97. if (NMT == NULL || emPr == NULL || NMT_CANdevRx == NULL || HB_CANdevTx == NULL
  98. #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
  99. || NMT_CANdevTx == NULL
  100. #endif
  101. ) {
  102. return CO_ERROR_ILLEGAL_ARGUMENT;
  103. }
  104. /* clear the object */
  105. memset(NMT, 0, sizeof(CO_NMT_t));
  106. /* Configure object variables */
  107. NMT->operatingState = CO_NMT_INITIALIZING;
  108. NMT->operatingStatePrev = CO_NMT_INITIALIZING;
  109. NMT->nodeId = nodeId;
  110. NMT->firstHBTime = (int32_t)firstHBTime_ms * 1000;
  111. NMT->emPr = emPr;
  112. /* configure NMT CAN reception */
  113. ret = CO_CANrxBufferInit(
  114. NMT_CANdevRx, /* CAN device */
  115. NMT_rxIdx, /* rx buffer index */
  116. CANidRxNMT, /* CAN identifier */
  117. 0x7FF, /* mask */
  118. 0, /* rtr */
  119. (void*)NMT, /* object passed to receive function */
  120. CO_NMT_receive); /* this function will process received message */
  121. #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
  122. /* configure NMT CAN transmission */
  123. NMT->NMT_CANdevTx = NMT_CANdevTx;
  124. NMT->NMT_TXbuff = CO_CANtxBufferInit(
  125. NMT_CANdevTx, /* CAN device */
  126. NMT_txIdx, /* index of specific buffer inside CAN module */
  127. CANidTxNMT, /* CAN identifier */
  128. 0, /* rtr */
  129. 2, /* number of data bytes */
  130. 0); /* synchronous message flag bit */
  131. if (NMT->NMT_TXbuff == NULL) {
  132. ret = CO_ERROR_ILLEGAL_ARGUMENT;
  133. }
  134. #endif
  135. /* configure HB CAN transmission */
  136. NMT->HB_CANdevTx = HB_CANdevTx;
  137. NMT->HB_TXbuff = CO_CANtxBufferInit(
  138. HB_CANdevTx, /* CAN device */
  139. HB_txIdx, /* index of specific buffer inside CAN module */
  140. CANidTxHB, /* CAN identifier */
  141. 0, /* rtr */
  142. 1, /* number of data bytes */
  143. 0); /* synchronous message flag bit */
  144. if (NMT->HB_TXbuff == NULL) {
  145. ret = CO_ERROR_ILLEGAL_ARGUMENT;
  146. }
  147. return ret;
  148. }
  149. #if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_CALLBACK_PRE
  150. void CO_NMT_initCallbackPre(
  151. CO_NMT_t *NMT,
  152. void *object,
  153. void (*pFunctSignal)(void *object))
  154. {
  155. if (NMT != NULL) {
  156. NMT->pFunctSignalPre = pFunctSignal;
  157. NMT->functSignalObjectPre = object;
  158. }
  159. }
  160. #endif
  161. #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE
  162. /******************************************************************************/
  163. void CO_NMT_initCallbackChanged(
  164. CO_NMT_t *NMT,
  165. void (*pFunctNMT)(CO_NMT_internalState_t state))
  166. {
  167. if (NMT != NULL){
  168. NMT->pFunctNMT = pFunctNMT;
  169. if (NMT->pFunctNMT != NULL){
  170. NMT->pFunctNMT(NMT->operatingState);
  171. }
  172. }
  173. }
  174. #endif
  175. /******************************************************************************/
  176. CO_NMT_reset_cmd_t CO_NMT_process(
  177. CO_NMT_t *NMT,
  178. uint32_t timeDifference_us,
  179. uint16_t HBtime_ms,
  180. uint32_t NMTstartup,
  181. uint8_t errorRegister,
  182. const uint8_t errorBehavior[],
  183. uint32_t *timerNext_us)
  184. {
  185. (void)timerNext_us; /* may be unused */
  186. uint8_t CANpassive;
  187. CO_NMT_internalState_t currentOperatingState = NMT->operatingState;
  188. uint32_t HBtime = (uint32_t)HBtime_ms * 1000;
  189. NMT->HBproducerTimer += timeDifference_us;
  190. /* Send heartbeat producer message if:
  191. * - First start, send bootup message or
  192. * - HB producer and Timer expired or
  193. * - HB producer and NMT->operatingState changed, but not from initialised */
  194. if ((NMT->operatingState == CO_NMT_INITIALIZING) ||
  195. (HBtime != 0 && (NMT->HBproducerTimer >= HBtime ||
  196. NMT->operatingState != NMT->operatingStatePrev)
  197. )) {
  198. /* Start from the beginning. If OS is slow, time sliding may occur. However,
  199. * heartbeat is not for synchronization, it is for health report. */
  200. NMT->HBproducerTimer = 0;
  201. NMT->HB_TXbuff->data[0] = (uint8_t) NMT->operatingState;
  202. CO_CANsend(NMT->HB_CANdevTx, NMT->HB_TXbuff);
  203. if (NMT->operatingState == CO_NMT_INITIALIZING) {
  204. /* After bootup messages send first heartbeat earlier */
  205. if (HBtime > NMT->firstHBTime) {
  206. NMT->HBproducerTimer = HBtime - NMT->firstHBTime;
  207. }
  208. /* NMT slave self starting */
  209. if (NMTstartup == 0x00000008U) NMT->operatingState = CO_NMT_OPERATIONAL;
  210. else NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  211. }
  212. }
  213. NMT->operatingStatePrev = NMT->operatingState;
  214. /* CAN passive flag */
  215. CANpassive = 0;
  216. if (CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_PASSIVE) || CO_isError(NMT->emPr->em, CO_EM_CAN_RX_BUS_PASSIVE))
  217. CANpassive = 1;
  218. /* in case of error enter pre-operational state */
  219. if (errorBehavior && (NMT->operatingState == CO_NMT_OPERATIONAL)){
  220. if (CANpassive && (errorBehavior[2] == 0 || errorBehavior[2] == 2)) errorRegister |= 0x10;
  221. if (errorRegister){
  222. /* Communication error */
  223. if (errorRegister & CO_ERR_REG_COMM_ERR){
  224. if (errorBehavior[1] == 0){
  225. NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  226. }
  227. else if (errorBehavior[1] == 2){
  228. NMT->operatingState = CO_NMT_STOPPED;
  229. }
  230. else if (CO_isError(NMT->emPr->em, CO_EM_CAN_TX_BUS_OFF)
  231. || CO_isError(NMT->emPr->em, CO_EM_HEARTBEAT_CONSUMER)
  232. || CO_isError(NMT->emPr->em, CO_EM_HB_CONSUMER_REMOTE_RESET))
  233. {
  234. if (errorBehavior[0] == 0){
  235. NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  236. }
  237. else if (errorBehavior[0] == 2){
  238. NMT->operatingState = CO_NMT_STOPPED;
  239. }
  240. }
  241. }
  242. /* Generic error */
  243. if (errorRegister & CO_ERR_REG_GENERIC_ERR){
  244. if (errorBehavior[3] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  245. else if (errorBehavior[3] == 2) NMT->operatingState = CO_NMT_STOPPED;
  246. }
  247. /* Device profile error */
  248. if (errorRegister & CO_ERR_REG_DEV_PROFILE){
  249. if (errorBehavior[4] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  250. else if (errorBehavior[4] == 2) NMT->operatingState = CO_NMT_STOPPED;
  251. }
  252. /* Manufacturer specific error */
  253. if (errorRegister & CO_ERR_REG_MANUFACTURER){
  254. if (errorBehavior[5] == 0) NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  255. else if (errorBehavior[5] == 2) NMT->operatingState = CO_NMT_STOPPED;
  256. }
  257. /* if operational state is lost, send HB immediately. */
  258. if (NMT->operatingState != CO_NMT_OPERATIONAL)
  259. NMT->HBproducerTimer = HBtime;
  260. }
  261. }
  262. if (currentOperatingState != NMT->operatingState) {
  263. #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_CALLBACK_CHANGE
  264. if (NMT->pFunctNMT != NULL) {
  265. NMT->pFunctNMT(NMT->operatingState);
  266. }
  267. #endif
  268. #if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
  269. /* execute next CANopen processing immediately */
  270. if (timerNext_us != NULL) {
  271. *timerNext_us = 0;
  272. }
  273. #endif
  274. }
  275. #if (CO_CONFIG_NMT) & CO_CONFIG_FLAG_TIMERNEXT
  276. /* Calculate, when next Heartbeat needs to be send and lower timerNext_us if necessary. */
  277. if (HBtime != 0 && timerNext_us != NULL) {
  278. if (NMT->HBproducerTimer < HBtime) {
  279. uint32_t diff = HBtime - NMT->HBproducerTimer;
  280. if (*timerNext_us > diff) {
  281. *timerNext_us = diff;
  282. }
  283. } else {
  284. *timerNext_us = 0;
  285. }
  286. }
  287. #endif
  288. return (CO_NMT_reset_cmd_t) NMT->resetCommand;
  289. }
  290. #if (CO_CONFIG_NMT) & CO_CONFIG_NMT_MASTER
  291. /******************************************************************************/
  292. CO_ReturnError_t CO_NMT_sendCommand(CO_NMT_t *NMT,
  293. CO_NMT_command_t command,
  294. uint8_t nodeID)
  295. {
  296. CO_ReturnError_t error = CO_ERROR_NO;
  297. /* verify arguments */
  298. if (NMT == NULL) {
  299. error = CO_ERROR_TX_UNCONFIGURED;
  300. }
  301. /* Apply NMT command also to this node, if set so. */
  302. if (error == CO_ERROR_NO && (nodeID == 0 || nodeID == NMT->nodeId)) {
  303. switch (command) {
  304. case CO_NMT_ENTER_OPERATIONAL:
  305. if ((*NMT->emPr->errorRegister) == 0) {
  306. NMT->operatingState = CO_NMT_OPERATIONAL;
  307. }
  308. break;
  309. case CO_NMT_ENTER_STOPPED:
  310. NMT->operatingState = CO_NMT_STOPPED;
  311. break;
  312. case CO_NMT_ENTER_PRE_OPERATIONAL:
  313. NMT->operatingState = CO_NMT_PRE_OPERATIONAL;
  314. break;
  315. case CO_NMT_RESET_NODE:
  316. NMT->resetCommand = CO_RESET_APP;
  317. break;
  318. case CO_NMT_RESET_COMMUNICATION:
  319. NMT->resetCommand = CO_RESET_COMM;
  320. break;
  321. default:
  322. error = CO_ERROR_ILLEGAL_ARGUMENT;
  323. break;
  324. }
  325. }
  326. /* Send NMT master message. */
  327. if (error == CO_ERROR_NO) {
  328. NMT->NMT_TXbuff->data[0] = command;
  329. NMT->NMT_TXbuff->data[1] = nodeID;
  330. error = CO_CANsend(NMT->NMT_CANdevTx, NMT->NMT_TXbuff);
  331. }
  332. return error;
  333. }
  334. #endif