libuav original
Dependents: UAVCAN UAVCAN_Subscriber
can.cpp
00001 /* 00002 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com> 00003 */ 00004 00005 #include <uavcan_lpc11c24/can.hpp> 00006 #include <uavcan_lpc11c24/clock.hpp> 00007 #include <uavcan/util/templates.hpp> 00008 #include <chip.h> 00009 #include "c_can.hpp" 00010 #include "internal.hpp" 00011 00012 /** 00013 * The default value should be OK for any use case. 00014 */ 00015 #ifndef UAVCAN_LPC11C24_RX_QUEUE_LEN 00016 # define UAVCAN_LPC11C24_RX_QUEUE_LEN 8 00017 #endif 00018 00019 #if UAVCAN_LPC11C24_RX_QUEUE_LEN > 254 00020 # error UAVCAN_LPC11C24_RX_QUEUE_LEN is too large 00021 #endif 00022 00023 extern "C" void canRxCallback(std::uint8_t msg_obj_num); 00024 extern "C" void canTxCallback(std::uint8_t msg_obj_num); 00025 extern "C" void canErrorCallback(std::uint32_t error_info); 00026 00027 namespace uavcan_lpc11c24 00028 { 00029 namespace 00030 { 00031 /** 00032 * Hardware message objects are allocated as follows: 00033 * - 1 - Single TX object 00034 * - 2..32 - RX objects 00035 * TX priority is defined by the message object number, not by the CAN ID (chapter 16.7.3.5 of the user manual), 00036 * hence we can't use more than one object because that would cause priority inversion on long transfers. 00037 */ 00038 constexpr unsigned NumberOfMessageObjects = 32; 00039 constexpr unsigned NumberOfTxMessageObjects = 1; 00040 constexpr unsigned NumberOfRxMessageObjects = NumberOfMessageObjects - NumberOfTxMessageObjects; 00041 constexpr unsigned TxMessageObjectNumber = 1; 00042 00043 /** 00044 * Total number of CAN errors. 00045 * Does not overflow. 00046 */ 00047 volatile std::uint32_t error_cnt = 0; 00048 00049 /** 00050 * False if there's no pending TX frame, i.e. write is possible. 00051 */ 00052 volatile bool tx_pending = false; 00053 00054 /** 00055 * Currently pending frame must be aborted on first error. 00056 */ 00057 volatile bool tx_abort_on_error = false; 00058 00059 /** 00060 * Gets updated every time the CAN IRQ handler is being called. 00061 */ 00062 volatile std::uint64_t last_irq_utc_timestamp = 0; 00063 00064 /** 00065 * Set by the driver on every successful TX or RX; reset by the application. 00066 */ 00067 volatile bool had_activity = false; 00068 00069 /** 00070 * After a received message gets extracted from C_CAN, it will be stored in the RX queue until libuavcan 00071 * reads it via select()/receive() calls. 00072 */ 00073 class RxQueue 00074 { 00075 struct Item 00076 { 00077 std::uint64_t utc_usec = 0; 00078 uavcan::CanFrame frame; 00079 }; 00080 00081 Item buf_[UAVCAN_LPC11C24_RX_QUEUE_LEN]; 00082 std::uint32_t overflow_cnt_ = 0; 00083 std::uint8_t in_ = 0; 00084 std::uint8_t out_ = 0; 00085 std::uint8_t len_ = 0; 00086 00087 public: 00088 void push(const uavcan::CanFrame& frame, const volatile std::uint64_t& utc_usec) 00089 { 00090 buf_[in_].frame = frame; 00091 buf_[in_].utc_usec = utc_usec; 00092 in_++; 00093 if (in_ >= UAVCAN_LPC11C24_RX_QUEUE_LEN) 00094 { 00095 in_ = 0; 00096 } 00097 len_++; 00098 if (len_ > UAVCAN_LPC11C24_RX_QUEUE_LEN) 00099 { 00100 len_ = UAVCAN_LPC11C24_RX_QUEUE_LEN; 00101 if (overflow_cnt_ < 0xFFFFFFFF) 00102 { 00103 overflow_cnt_++; 00104 } 00105 out_++; 00106 if (out_ >= UAVCAN_LPC11C24_RX_QUEUE_LEN) 00107 { 00108 out_ = 0; 00109 } 00110 } 00111 } 00112 00113 void pop(uavcan::CanFrame& out_frame, std::uint64_t& out_utc_usec) 00114 { 00115 if (len_ > 0) 00116 { 00117 out_frame = buf_[out_].frame; 00118 out_utc_usec = buf_[out_].utc_usec; 00119 out_++; 00120 if (out_ >= UAVCAN_LPC11C24_RX_QUEUE_LEN) 00121 { 00122 out_ = 0; 00123 } 00124 len_--; 00125 } 00126 } 00127 00128 unsigned getLength() const { return len_; } 00129 00130 std::uint32_t getOverflowCount() const { return overflow_cnt_; } 00131 }; 00132 00133 RxQueue rx_queue; 00134 00135 00136 struct BitTimingSettings 00137 { 00138 std::uint32_t canclkdiv; 00139 std::uint32_t canbtr; 00140 00141 bool isValid() const { return canbtr != 0; } 00142 }; 00143 00144 /** 00145 * http://www.bittiming.can-wiki.info 00146 */ 00147 BitTimingSettings computeBitTimings(std::uint32_t bitrate) 00148 { 00149 if (Chip_Clock_GetSystemClockRate() == 48000000) // 48 MHz is optimal for CAN timings 00150 { 00151 switch (bitrate) 00152 { 00153 case 1000000: return BitTimingSettings{ 0, 0x0505 }; // 8 quanta, 87.5% 00154 case 500000: return BitTimingSettings{ 0, 0x1c05 }; // 16 quanta, 87.5% 00155 case 250000: return BitTimingSettings{ 0, 0x1c0b }; // 16 quanta, 87.5% 00156 case 125000: return BitTimingSettings{ 0, 0x1c17 }; // 16 quanta, 87.5% 00157 case 100000: return BitTimingSettings{ 0, 0x1c1d }; // 16 quanta, 87.5% 00158 default: return BitTimingSettings{ 0, 0 }; 00159 } 00160 } 00161 else 00162 { 00163 return BitTimingSettings{ 0, 0 }; 00164 } 00165 } 00166 00167 } // namespace 00168 00169 CanDriver CanDriver::self; 00170 00171 uavcan::uint32_t CanDriver::detectBitRate(void (*idle_callback)()) 00172 { 00173 static constexpr uavcan::uint32_t BitRatesToTry[] = 00174 { 00175 1000000, 00176 500000, 00177 250000, 00178 125000, 00179 100000 00180 }; 00181 00182 const auto ListeningDuration = uavcan::MonotonicDuration::fromMSec(1050); 00183 00184 NVIC_DisableIRQ(CAN_IRQn ); 00185 Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_CAN ); 00186 00187 for (auto bitrate : BitRatesToTry) 00188 { 00189 // Computing bit timings 00190 const auto bit_timings = computeBitTimings(bitrate); 00191 if (!bit_timings.isValid()) 00192 { 00193 return 0; 00194 } 00195 00196 // Configuring the CAN controller 00197 { 00198 CriticalSectionLocker locker; 00199 00200 LPC_SYSCTL->PRESETCTRL |= (1U << RESET_CAN0 ); 00201 00202 // Entering initialization mode 00203 c_can::CAN.CNTL = c_can::CNTL_INIT | c_can::CNTL_CCE; 00204 00205 while ((c_can::CAN.CNTL & c_can::CNTL_INIT) == 0) 00206 { 00207 ; // Nothing to do 00208 } 00209 00210 // Configuring bit rate 00211 c_can::CAN.CLKDIV = bit_timings.canclkdiv; 00212 c_can::CAN.BT = bit_timings.canbtr; 00213 c_can::CAN.BRPE = 0; 00214 00215 // Configuring silent mode 00216 c_can::CAN.CNTL |= c_can::CNTL_TEST; 00217 c_can::CAN.TEST = c_can::TEST_SILENT; 00218 00219 // Configuring status monitor 00220 c_can::CAN.STAT = (unsigned(c_can::StatLec::Unused) << c_can::STAT_LEC_SHIFT); 00221 00222 // Leaving initialization mode 00223 c_can::CAN.CNTL &= ~(c_can::CNTL_INIT | c_can::CNTL_CCE); 00224 00225 while ((c_can::CAN.CNTL & c_can::CNTL_INIT) != 0) 00226 { 00227 ; // Nothing to do 00228 } 00229 } 00230 00231 // Listening 00232 const auto deadline = clock::getMonotonic() + ListeningDuration; 00233 bool match_detected = false; 00234 while (clock::getMonotonic() < deadline) 00235 { 00236 if (idle_callback != nullptr) 00237 { 00238 idle_callback(); 00239 } 00240 00241 const auto LastErrorCode = (c_can::CAN.STAT >> c_can::STAT_LEC_SHIFT) & c_can::STAT_LEC_MASK; 00242 00243 if (LastErrorCode == unsigned(c_can::StatLec::NoError)) 00244 { 00245 match_detected = true; 00246 break; 00247 } 00248 } 00249 00250 // De-configuring the CAN controller back to reset state 00251 { 00252 CriticalSectionLocker locker; 00253 00254 c_can::CAN.CNTL = c_can::CNTL_INIT; 00255 00256 while ((c_can::CAN.CNTL & c_can::CNTL_INIT) == 0) 00257 { 00258 ; // Nothing to do 00259 } 00260 00261 LPC_SYSCTL->PRESETCTRL &= ~(1U << RESET_CAN0 ); 00262 } 00263 00264 // Termination condition 00265 if (match_detected) 00266 { 00267 return bitrate; 00268 } 00269 } 00270 00271 return 0; // No match 00272 } 00273 00274 int CanDriver::init(uavcan::uint32_t bitrate) 00275 { 00276 { 00277 auto bit_timings = computeBitTimings(bitrate); 00278 if (!bit_timings.isValid()) 00279 { 00280 return -1; 00281 } 00282 00283 CriticalSectionLocker locker; 00284 00285 error_cnt = 0; 00286 tx_abort_on_error = false; 00287 tx_pending = false; 00288 last_irq_utc_timestamp = 0; 00289 had_activity = false; 00290 00291 /* 00292 * C_CAN init 00293 */ 00294 Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_CAN ); 00295 00296 LPC_CCAN_API->init_can(reinterpret_cast<std::uint32_t*>(&bit_timings), true); 00297 00298 static CCAN_CALLBACKS_T ccan_callbacks = 00299 { 00300 canRxCallback, 00301 canTxCallback, 00302 canErrorCallback, 00303 nullptr, 00304 nullptr, 00305 nullptr, 00306 nullptr, 00307 nullptr 00308 }; 00309 LPC_CCAN_API->config_calb(&ccan_callbacks); 00310 00311 /* 00312 * Interrupts 00313 */ 00314 c_can::CAN.CNTL |= c_can::CNTL_SIE; // This is necessary for transmission aborts on error 00315 00316 NVIC_EnableIRQ(CAN_IRQn ); 00317 } 00318 00319 /* 00320 * Applying default filter configuration (accept all) 00321 */ 00322 if (configureFilters(nullptr, 0) < 0) 00323 { 00324 return -1; 00325 } 00326 00327 return 0; 00328 } 00329 00330 bool CanDriver::hasReadyRx() const 00331 { 00332 CriticalSectionLocker locker; 00333 return rx_queue.getLength() > 0; 00334 } 00335 00336 bool CanDriver::hasEmptyTx() const 00337 { 00338 CriticalSectionLocker locker; 00339 return !tx_pending; 00340 } 00341 00342 bool CanDriver::hadActivity() 00343 { 00344 CriticalSectionLocker locker; 00345 const bool ret = had_activity; 00346 had_activity = false; 00347 return ret; 00348 } 00349 00350 uavcan::uint32_t CanDriver::getRxQueueOverflowCount() const 00351 { 00352 CriticalSectionLocker locker; 00353 return rx_queue.getOverflowCount(); 00354 } 00355 00356 bool CanDriver::isInBusOffState() const 00357 { 00358 return (c_can::CAN.STAT & c_can::STAT_BOFF) != 0; 00359 } 00360 00361 uavcan::int16_t CanDriver::send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline, 00362 uavcan::CanIOFlags flags) 00363 { 00364 if (frame.isErrorFrame() || 00365 frame.dlc > 8 || 00366 (flags & uavcan::CanIOFlagLoopback) != 0) // TX timestamping is not supported by this driver. 00367 { 00368 return -1; 00369 } 00370 00371 /* 00372 * Frame conversion 00373 */ 00374 CCAN_MSG_OBJ_T msgobj = CCAN_MSG_OBJ_T(); 00375 msgobj.mode_id = frame.id & uavcan::CanFrame::MaskExtID; 00376 if (frame.isExtended()) 00377 { 00378 msgobj.mode_id |= CAN_MSGOBJ_EXT; 00379 } 00380 if (frame.isRemoteTransmissionRequest()) 00381 { 00382 msgobj.mode_id |= CAN_MSGOBJ_RTR; 00383 } 00384 msgobj.dlc = frame.dlc; 00385 uavcan::copy(frame.data, frame.data + frame.dlc, msgobj.data); 00386 00387 /* 00388 * Transmission 00389 */ 00390 (void)tx_deadline; // TX timeouts are not supported by this driver yet (and hardly going to be). 00391 00392 CriticalSectionLocker locker; 00393 00394 if (!tx_pending) 00395 { 00396 tx_pending = true; // Mark as pending - will be released in TX callback 00397 tx_abort_on_error = (flags & uavcan::CanIOFlagAbortOnError) != 0; 00398 msgobj.msgobj = TxMessageObjectNumber; 00399 LPC_CCAN_API->can_transmit(&msgobj); 00400 return 1; 00401 } 00402 return 0; 00403 } 00404 00405 uavcan::int16_t CanDriver::receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, 00406 uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags) 00407 { 00408 out_ts_monotonic = clock::getMonotonic(); 00409 out_flags = 0; // We don't support any IO flags 00410 00411 CriticalSectionLocker locker; 00412 if (rx_queue.getLength() == 0) 00413 { 00414 return 0; 00415 } 00416 std::uint64_t ts_utc = 0; 00417 rx_queue.pop(out_frame, ts_utc); 00418 out_ts_utc = uavcan::UtcTime::fromUSec(ts_utc); 00419 return 1; 00420 } 00421 00422 uavcan::int16_t CanDriver::select(uavcan::CanSelectMasks& inout_masks, 00423 const uavcan::CanFrame* (&)[uavcan::MaxCanIfaces], 00424 uavcan::MonotonicTime blocking_deadline) 00425 { 00426 const bool bus_off = isInBusOffState(); 00427 if (bus_off) // Recover automatically on bus-off 00428 { 00429 CriticalSectionLocker locker; 00430 if ((c_can::CAN.CNTL & c_can::CNTL_INIT) != 0) 00431 { 00432 c_can::CAN.CNTL &= ~c_can::CNTL_INIT; 00433 } 00434 } 00435 00436 const bool noblock = ((inout_masks.read == 1) && hasReadyRx()) || 00437 ((inout_masks.write == 1) && hasEmptyTx()); 00438 00439 if (!noblock && (clock::getMonotonic() > blocking_deadline)) 00440 { 00441 #if defined(UAVCAN_LPC11C24_USE_WFE) && UAVCAN_LPC11C24_USE_WFE 00442 /* 00443 * It's not cool (literally) to burn cycles in a busyloop, and we have no OS to pass control to other 00444 * tasks, thus solution is to halt the core until a hardware event occurs - e.g. clock timer overflow. 00445 * Upon such event the select() call will return, even if no requested IO operations became available. 00446 * It's OK to do that, libuavcan can handle such behavior. 00447 * 00448 * Note that it is not possible to precisely control the sleep duration with WFE, since we can't predict when 00449 * the next hardware event occurs. Worst case conditions: 00450 * - WFE gets executed right after the clock timer interrupt; 00451 * - CAN bus is completely silent (no traffic); 00452 * - User's application has no interrupts and generates no hardware events. 00453 * In such scenario execution will stuck here for one period of the clock timer interrupt, even if 00454 * blocking_deadline expires sooner. 00455 * If the user's application requires higher timing precision, an extra dummy IRQ can be added just to 00456 * break WFE every once in a while. 00457 */ 00458 __WFE(); 00459 #endif 00460 } 00461 00462 inout_masks.read = hasReadyRx() ? 1 : 0; 00463 00464 inout_masks.write = (hasEmptyTx() && !bus_off) ? 1 : 0; // Disable write while in bus-off 00465 00466 return 0; // Return value doesn't matter as long as it is non-negative 00467 } 00468 00469 uavcan::int16_t CanDriver::configureFilters(const uavcan::CanFilterConfig* filter_configs, 00470 uavcan::uint16_t num_configs) 00471 { 00472 CriticalSectionLocker locker; 00473 00474 /* 00475 * If C_CAN is active (INIT=0) and the CAN bus has intensive traffic, RX object configuration may fail. 00476 * The solution is to disable the controller while configuration is in progress. 00477 * The documentation, as always, doesn't bother to mention this detail. Shame on you, NXP. 00478 */ 00479 struct RAIIDisabler 00480 { 00481 RAIIDisabler() 00482 { 00483 c_can::CAN.CNTL |= c_can::CNTL_INIT; 00484 } 00485 ~RAIIDisabler() 00486 { 00487 c_can::CAN.CNTL &= ~c_can::CNTL_INIT; 00488 } 00489 } can_disabler; // Must be instantiated AFTER the critical section locker 00490 00491 if (num_configs == 0) 00492 { 00493 auto msg_obj = CCAN_MSG_OBJ_T(); 00494 msg_obj.msgobj = NumberOfTxMessageObjects + 1; 00495 LPC_CCAN_API->config_rxmsgobj(&msg_obj); // all STD frames 00496 00497 msg_obj.mode_id = CAN_MSGOBJ_EXT; 00498 msg_obj.msgobj = NumberOfTxMessageObjects + 2; 00499 LPC_CCAN_API->config_rxmsgobj(&msg_obj); // all EXT frames 00500 } 00501 else if (num_configs <= NumberOfRxMessageObjects) 00502 { 00503 // Making sure the configs use only EXT frames; otherwise we can't accept them 00504 for (unsigned i = 0; i < num_configs; i++) 00505 { 00506 auto& f = filter_configs[i]; 00507 if ((f.id & f.mask & uavcan::CanFrame::FlagEFF) == 0) 00508 { 00509 return -1; 00510 } 00511 } 00512 00513 // Installing the configuration 00514 for (unsigned i = 0; i < NumberOfRxMessageObjects; i++) 00515 { 00516 auto msg_obj = CCAN_MSG_OBJ_T(); 00517 msg_obj.msgobj = std::uint8_t(i + 1U + NumberOfTxMessageObjects); // Message objects are numbered from 1 00518 00519 if (i < num_configs) 00520 { 00521 msg_obj.mode_id = (filter_configs[i].id & uavcan::CanFrame::MaskExtID) | CAN_MSGOBJ_EXT; // Only EXT 00522 msg_obj.mask = filter_configs[i].mask & uavcan::CanFrame::MaskExtID; 00523 } 00524 else 00525 { 00526 msg_obj.mode_id = CAN_MSGOBJ_RTR; // Using this configuration to disable the object 00527 msg_obj.mask = uavcan::CanFrame::MaskStdID; 00528 } 00529 00530 LPC_CCAN_API->config_rxmsgobj(&msg_obj); 00531 } 00532 } 00533 else 00534 { 00535 return -1; 00536 } 00537 00538 return 0; 00539 } 00540 00541 uavcan::uint64_t CanDriver::getErrorCount() const 00542 { 00543 CriticalSectionLocker locker; 00544 return std::uint64_t(error_cnt) + std::uint64_t(rx_queue.getOverflowCount()); 00545 } 00546 00547 uavcan::uint16_t CanDriver::getNumFilters() const 00548 { 00549 return NumberOfRxMessageObjects; 00550 } 00551 00552 uavcan::ICanIface* CanDriver::getIface(uavcan::uint8_t iface_index) 00553 { 00554 return (iface_index == 0) ? this : nullptr; 00555 } 00556 00557 uavcan::uint8_t CanDriver::getNumIfaces() const 00558 { 00559 return 1; 00560 } 00561 00562 } 00563 00564 /* 00565 * C_CAN handlers 00566 */ 00567 extern "C" 00568 { 00569 00570 void canRxCallback(std::uint8_t msg_obj_num) 00571 { 00572 using namespace uavcan_lpc11c24; 00573 00574 auto msg_obj = CCAN_MSG_OBJ_T(); 00575 msg_obj.msgobj = msg_obj_num; 00576 LPC_CCAN_API->can_receive(&msg_obj); 00577 00578 uavcan::CanFrame frame; 00579 00580 // CAN ID, EXT or not 00581 if (msg_obj.mode_id & CAN_MSGOBJ_EXT) 00582 { 00583 frame.id = msg_obj.mode_id & uavcan::CanFrame::MaskExtID; 00584 frame.id |= uavcan::CanFrame::FlagEFF; 00585 } 00586 else 00587 { 00588 frame.id = msg_obj.mode_id & uavcan::CanFrame::MaskStdID; 00589 } 00590 00591 // RTR 00592 if (msg_obj.mode_id & CAN_MSGOBJ_RTR) 00593 { 00594 frame.id |= uavcan::CanFrame::FlagRTR; 00595 } 00596 00597 // Payload 00598 frame.dlc = msg_obj.dlc; 00599 uavcan::copy(msg_obj.data, msg_obj.data + msg_obj.dlc, frame.data); 00600 00601 rx_queue.push(frame, last_irq_utc_timestamp); 00602 had_activity = true; 00603 } 00604 00605 void canTxCallback(std::uint8_t msg_obj_num) 00606 { 00607 using namespace uavcan_lpc11c24; 00608 00609 (void)msg_obj_num; 00610 00611 tx_pending = false; 00612 had_activity = true; 00613 } 00614 00615 void canErrorCallback(std::uint32_t error_info) 00616 { 00617 using namespace uavcan_lpc11c24; 00618 00619 // Updating the error counter 00620 if ((error_info != 0) && (error_cnt < 0xFFFFFFFFUL)) 00621 { 00622 error_cnt++; 00623 } 00624 00625 // Serving abort requests 00626 if (tx_pending && tx_abort_on_error) 00627 { 00628 tx_pending = false; 00629 tx_abort_on_error = false; 00630 00631 // Using the first interface, because this approach seems to be compliant with the BASIC mode (just in case) 00632 c_can::CAN.IF[0].CMDREQ = TxMessageObjectNumber; 00633 c_can::CAN.IF[0].CMDMSK.W = c_can::IF_CMDMSK_W_WR_RD; // Clearing IF_CMDMSK_W_TXRQST 00634 c_can::CAN.IF[0].MCTRL &= ~c_can::IF_MCTRL_TXRQST; // Clearing IF_MCTRL_TXRQST 00635 } 00636 } 00637 00638 void CAN_IRQHandler(); 00639 00640 void CAN_IRQHandler() 00641 { 00642 using namespace uavcan_lpc11c24; 00643 00644 last_irq_utc_timestamp = clock::getUtcUSecFromCanInterrupt(); 00645 00646 LPC_CCAN_API->isr(); 00647 } 00648 00649 }
Generated on Tue Jul 12 2022 17:17:30 by 1.7.2