From 67c925865c2692c612ae6b45882f8b7636404d6d Mon Sep 17 00:00:00 2001 From: Siddharth Purohit Date: Fri, 5 Jul 2019 13:18:54 +0800 Subject: [PATCH] HAL_ChibiOS: add support for CAN on STM32H7 boards --- libraries/AP_HAL_ChibiOS/CAN.h | 4 + libraries/AP_HAL_ChibiOS/CANFDIface.cpp | 1092 +++++++++++++++++ libraries/AP_HAL_ChibiOS/CANFDIface.h | 448 +++++++ libraries/AP_HAL_ChibiOS/CANIface.h | 6 +- libraries/AP_HAL_ChibiOS/CanIface.cpp | 6 +- libraries/AP_HAL_ChibiOS/bxcan.hpp | 3 +- libraries/AP_HAL_ChibiOS/fdcan.hpp | 90 ++ .../AP_HAL_ChibiOS/hwdef/CubeOrange/hwdef.dat | 8 +- 8 files changed, 1649 insertions(+), 8 deletions(-) create mode 100644 libraries/AP_HAL_ChibiOS/CANFDIface.cpp create mode 100644 libraries/AP_HAL_ChibiOS/CANFDIface.h create mode 100644 libraries/AP_HAL_ChibiOS/fdcan.hpp diff --git a/libraries/AP_HAL_ChibiOS/CAN.h b/libraries/AP_HAL_ChibiOS/CAN.h index d46075c857..1f11192a70 100644 --- a/libraries/AP_HAL_ChibiOS/CAN.h +++ b/libraries/AP_HAL_ChibiOS/CAN.h @@ -28,7 +28,11 @@ #include "CANThread.h" #include "CANClock.h" +#if defined(STM32H7XX) +#include "CANFDIface.h" +#else #include "CANIface.h" +#endif #define MAX_NUMBER_OF_CAN_INTERFACES 2 #define MAX_NUMBER_OF_CAN_DRIVERS 2 diff --git a/libraries/AP_HAL_ChibiOS/CANFDIface.cpp b/libraries/AP_HAL_ChibiOS/CANFDIface.cpp new file mode 100644 index 0000000000..de21f2d8b6 --- /dev/null +++ b/libraries/AP_HAL_ChibiOS/CANFDIface.cpp @@ -0,0 +1,1092 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Kirienko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * This file is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Code by Siddharth Bharat Purohit + */ + +#include "AP_HAL_ChibiOS.h" + +#if HAL_WITH_UAVCAN +#include +#include +#include "CANClock.h" +#include "CANInternal.h" +#include "CANSerialRouter.h" +#include +#include +# include + +# if defined(STM32H7XX) +#include "CANFDIface.h" + +#define FDCAN1_IT0_IRQHandler STM32_FDCAN1_IT0_HANDLER +#define FDCAN1_IT1_IRQHandler STM32_FDCAN1_IT1_HANDLER +#define FDCAN2_IT0_IRQHandler STM32_FDCAN2_IT0_HANDLER +#define FDCAN2_IT1_IRQHandler STM32_FDCAN2_IT1_HANDLER + +#define FDCAN_FRAME_BUFFER_SIZE 4 // Buffer size for 8 bytes data field + +//Message RAM Allocations in Word lengths +#define MAX_FILTER_LIST_SIZE 80U //80 element Standard Filter List elements or 40 element Extended Filter List +#define FDCAN_NUM_RXFIFO0_SIZE 104U //26 Frames +#define FDCAN_TX_FIFO_BUFFER_SIZE 128U //32 Frames + +#define MESSAGE_RAM_END_ADDR 0x4000B5FC + +extern const AP_HAL::HAL& hal; + +namespace ChibiOS_CAN +{ +namespace +{ + +CanIface* ifaces[UAVCAN_STM32_NUM_IFACES] = { + UAVCAN_NULLPTR +#if UAVCAN_STM32_NUM_IFACES > 1 + , UAVCAN_NULLPTR +#endif +}; + +inline void handleInterrupt(uavcan::uint8_t iface_index, uavcan::uint8_t line_index) +{ + UAVCAN_ASSERT(iface_index < UAVCAN_STM32_NUM_IFACES); + if (ifaces[iface_index] == UAVCAN_NULLPTR) { + //Just reset all the interrupts and return + ifaces[iface_index]->can_reg()->IR = FDCAN_IR_RF0N; + ifaces[iface_index]->can_reg()->IR = FDCAN_IR_RF1N; + ifaces[iface_index]->can_reg()->IR = FDCAN_IR_TEFN; + UAVCAN_ASSERT(0); + return; + } + if (line_index == 0) { + if ((ifaces[iface_index]->can_reg()->IR & FDCAN_IR_RF0N) || + (ifaces[iface_index]->can_reg()->IR & FDCAN_IR_RF0F)) { + ifaces[iface_index]->can_reg()->IR = FDCAN_IR_RF0N | FDCAN_IR_RF0F; + ifaces[iface_index]->handleRxInterrupt(0); + } + if ((ifaces[iface_index]->can_reg()->IR & FDCAN_IR_RF1N) || + (ifaces[iface_index]->can_reg()->IR & FDCAN_IR_RF1F)) { + ifaces[iface_index]->can_reg()->IR = FDCAN_IR_RF1N | FDCAN_IR_RF1F; + ifaces[iface_index]->handleRxInterrupt(1); + } + } else { + if (ifaces[iface_index]->can_reg()->IR & FDCAN_IR_TC) { + ifaces[iface_index]->can_reg()->IR = FDCAN_IR_TC; + uavcan::uint64_t utc_usec = clock::getUtcUSecFromCanInterrupt(); + if (utc_usec > 0) { + utc_usec--; + } + ifaces[iface_index]->handleTxCompleteInterrupt(utc_usec); + } + } + ifaces[iface_index]->pollErrorFlagsFromISR(); +} + +} // namespace + +uint32_t CanIface::FDCANMessageRAMOffset_ = 0; +SLCANRouter *CanIface::_slcan_router = nullptr; + +CanIface::CanIface(fdcan::CanType* can, BusEvent& update_event, uavcan::uint8_t self_index, + CanRxItem* rx_queue_buffer, uavcan::uint8_t rx_queue_capacity) + : rx_queue_(rx_queue_buffer, rx_queue_capacity) + , can_(can) + , error_cnt_(0) + , served_aborts_cnt_(0) + , update_event_(update_event) + , peak_tx_mailbox_index_(0) + , self_index_(self_index) + , had_activity_(false) +{ + UAVCAN_ASSERT(self_index_ < UAVCAN_STM32_NUM_IFACES); + _slcan_router = SLCANRouter::get_singleton(); +} + +/* + * CanIface::RxQueue + */ +void CanIface::RxQueue::registerOverflow() +{ + if (overflow_cnt_ < 0xFFFFFFFF) { + overflow_cnt_++; + } +} + +void CanIface::RxQueue::push(const uavcan::CanFrame& frame, const uint64_t& utc_usec, uavcan::CanIOFlags flags) +{ + buf_[in_].frame = frame; + buf_[in_].utc_usec = utc_usec; + buf_[in_].flags = flags; + in_++; + if (in_ >= capacity_) { + in_ = 0; + } + len_++; + if (len_ > capacity_) { + len_ = capacity_; + registerOverflow(); + out_++; + if (out_ >= capacity_) { + out_ = 0; + } + } +} + +void CanIface::RxQueue::pop(uavcan::CanFrame& out_frame, uavcan::uint64_t& out_utc_usec, uavcan::CanIOFlags& out_flags) +{ + if (len_ > 0) { + out_frame = buf_[out_].frame; + out_utc_usec = buf_[out_].utc_usec; + out_flags = buf_[out_].flags; + out_++; + if (out_ >= capacity_) { + out_ = 0; + } + len_--; + } else { + UAVCAN_ASSERT(0); + } +} + +void CanIface::RxQueue::reset() +{ + in_ = 0; + out_ = 0; + len_ = 0; + overflow_cnt_ = 0; +} + +int CanIface::computeTimings(const uavcan::uint32_t target_bitrate, Timings& out_timings) +{ + if (target_bitrate < 1) { + return -ErrInvalidBitRate; + } + + /* + * Hardware configuration + */ + const uavcan::uint32_t pclk = STM32_PLL1_Q_CK; + + static const int MaxBS1 = 16; + static const int MaxBS2 = 8; + + /* + * Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG + * CAN in Automation, 2003 + * + * According to the source, optimal quanta per bit are: + * Bitrate Optimal Maximum + * 1000 kbps 8 10 + * 500 kbps 16 17 + * 250 kbps 16 17 + * 125 kbps 16 17 + */ + const int max_quanta_per_bit = (target_bitrate >= 1000000) ? 10 : 17; + + UAVCAN_ASSERT(max_quanta_per_bit <= (MaxBS1 + MaxBS2)); + + static const int MaxSamplePointLocation = 900; + + /* + * Computing (prescaler * BS): + * BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual + * BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified + * let: + * BS = 1 + BS1 + BS2 -- Number of time quanta per bit + * PRESCALER_BS = PRESCALER * BS + * ==> + * PRESCALER_BS = PCLK / BITRATE + */ + const uavcan::uint32_t prescaler_bs = pclk / target_bitrate; + + /* + * Searching for such prescaler value so that the number of quanta per bit is highest. + */ + uavcan::uint8_t bs1_bs2_sum = uavcan::uint8_t(max_quanta_per_bit - 1); + + while ((prescaler_bs % (1 + bs1_bs2_sum)) != 0) { + if (bs1_bs2_sum <= 2) { + return -ErrInvalidBitRate; // No solution + } + bs1_bs2_sum--; + } + + const uavcan::uint32_t prescaler = prescaler_bs / (1 + bs1_bs2_sum); + if ((prescaler < 1U) || (prescaler > 1024U)) { + return -ErrInvalidBitRate; // No solution + } + + /* + * Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. + * We need to find the values so that the sample point is as close as possible to the optimal value. + * + * Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) + * {{bs2 -> (1 + bs1)/7}} + * + * Hence: + * bs2 = (1 + bs1) / 7 + * bs1 = (7 * bs1_bs2_sum - 1) / 8 + * + * Sample point location can be computed as follows: + * Sample point location = (1 + bs1) / (1 + bs1 + bs2) + * + * Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: + * - With rounding to nearest + * - With rounding to zero + */ + struct BsPair { + uavcan::uint8_t bs1; + uavcan::uint8_t bs2; + uavcan::uint16_t sample_point_permill; + + BsPair() : + bs1(0), + bs2(0), + sample_point_permill(0) + { } + + BsPair(uavcan::uint8_t bs1_bs2_sum, uavcan::uint8_t arg_bs1) : + bs1(arg_bs1), + bs2(uavcan::uint8_t(bs1_bs2_sum - bs1)), + sample_point_permill(uavcan::uint16_t(1000 * (1 + bs1) / (1 + bs1 + bs2))) + { + UAVCAN_ASSERT(bs1_bs2_sum > arg_bs1); + } + + bool isValid() const + { + return (bs1 >= 1) && (bs1 <= MaxBS1) && (bs2 >= 1) && (bs2 <= MaxBS2); + } + }; + + // First attempt with rounding to nearest + BsPair solution(bs1_bs2_sum, uavcan::uint8_t(((7 * bs1_bs2_sum - 1) + 4) / 8)); + + if (solution.sample_point_permill > MaxSamplePointLocation) { + // Second attempt with rounding to zero + solution = BsPair(bs1_bs2_sum, uavcan::uint8_t((7 * bs1_bs2_sum - 1) / 8)); + } + + /* + * Final validation + * Helpful Python: + * def sample_point_from_btr(x): + * assert 0b0011110010000000111111000000000 & x == 0 + * ts2,ts1,brp = (x>>20)&7, (x>>16)&15, x&511 + * return (1+ts1+1)/(1+ts1+1+ts2+1) + * + */ + if ((target_bitrate != (pclk / (prescaler * (1 + solution.bs1 + solution.bs2)))) || !solution.isValid()) { + UAVCAN_ASSERT(0); + return -ErrLogic; + } + + UAVCAN_STM32_LOG("Timings: quanta/bit: %d, sample point location: %.1f%%", + int(1 + solution.bs1 + solution.bs2), float(solution.sample_point_permill) / 10.F); + + out_timings.prescaler = uavcan::uint16_t(prescaler - 1U); + out_timings.sjw = 0; // Which means one + out_timings.bs1 = uavcan::uint8_t(solution.bs1 - 1); + out_timings.bs2 = uavcan::uint8_t(solution.bs2 - 1); + return 0; +} + +uavcan::int16_t CanIface::send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline, + uavcan::CanIOFlags flags) +{ + if (frame.isErrorFrame() || frame.dlc > 8) { + return -ErrUnsupportedFrame; + } + + /* + * Normally we should perform the same check as in @ref canAcceptNewTxFrame(), because + * it is possible that the highest-priority frame between select() and send() could have been + * replaced with a lower priority one due to TX timeout. But we don't do this check because: + * + * - It is a highly unlikely scenario. + * + * - Frames do not timeout on a properly functioning bus. Since frames do not timeout, the new + * frame can only have higher priority, which doesn't break the logic. + * + * - If high-priority frames are timing out in the TX queue, there's probably a lot of other + * issues to take care of before this one becomes relevant. + * + * - It takes CPU time. Not just CPU time, but critical section time, which is expensive. + */ + CriticalSectionLocker lock; + + /* + * Seeking for an empty slot + */ + uavcan::uint8_t index; + + if ((can_->TXFQS & FDCAN_TXFQS_TFQF) != 0) { + return false; //we don't have free space + } + index = ((can_->TXFQS & FDCAN_TXFQS_TFQPI) >> FDCAN_TXFQS_TFQPI_Pos); + + // Copy Frame to RAM + // Calculate Tx element address + uint32_t* buffer = (uint32_t *)(MessageRam_.TxFIFOQSA + (index * FDCAN_FRAME_BUFFER_SIZE * 4)); + + //Setup Frame ID + if (frame.isExtended()) { + buffer[0] = (fdcan::IDE | frame.id); + } else { + buffer[0] = (frame.id << 18); + } + if (frame.isRemoteTransmissionRequest()) { + buffer[0] |= fdcan::RTR; + } + //Write Data Length Code, and Message Marker + buffer[1] = frame.dlc << 16 | index << 24; + + // Write Frame to the message RAM + buffer[2] = (uavcan::uint32_t(frame.data[3]) << 24) | + (uavcan::uint32_t(frame.data[2]) << 16) | + (uavcan::uint32_t(frame.data[1]) << 8) | + (uavcan::uint32_t(frame.data[0]) << 0); + buffer[3] = (uavcan::uint32_t(frame.data[7]) << 24) | + (uavcan::uint32_t(frame.data[6]) << 16) | + (uavcan::uint32_t(frame.data[5]) << 8) | + (uavcan::uint32_t(frame.data[4]) << 0); + //Set Add Request + can_->TXBAR = (1 << index); + + //Registering the pending transmission so we can track its deadline and loopback it as needed + pending_tx_[index].deadline = tx_deadline; + pending_tx_[index].frame = frame; + pending_tx_[index].loopback = (flags & uavcan::CanIOFlagLoopback) != 0; + pending_tx_[index].abort_on_error = (flags & uavcan::CanIOFlagAbortOnError) != 0; + pending_tx_[index].index = index; + return 1; +} + +uavcan::int16_t CanIface::receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, + uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags) +{ + out_ts_monotonic = clock::getMonotonic(); // High precision is not required for monotonic timestamps + uavcan::uint64_t utc_usec = 0; + { + CriticalSectionLocker lock; + if (rx_queue_.getLength() == 0) { + return 0; + } + rx_queue_.pop(out_frame, utc_usec, out_flags); + } + out_ts_utc = uavcan::UtcTime::fromUSec(utc_usec); + return 1; +} + +uavcan::int16_t CanIface::configureFilters(const uavcan::CanFilterConfig* filter_configs, + uavcan::uint16_t num_configs) +{ + uint32_t num_extid = 0, num_stdid = 0; + uint32_t total_available_list_size = MAX_FILTER_LIST_SIZE; + uint32_t* filter_ptr; + //count number of frames of each type + for (uint8_t i = 0; i < num_configs; i++) { + const uavcan::CanFilterConfig* const cfg = filter_configs + i; + if ((cfg->id & uavcan::CanFrame::FlagEFF) || !(cfg->mask & uavcan::CanFrame::FlagEFF)) { + num_extid++; + } else { + num_stdid++; + } + } + + CriticalSectionLocker lock; + can_->CCCR |= FDCAN_CCCR_INIT; // Request init + while ((can_->CCCR & FDCAN_CCCR_INIT) == 0) {} + can_->CCCR |= FDCAN_CCCR_CCE; //Enable Config change + + //Allocate Message RAM for Standard ID Filter List + if (num_stdid == 0) { //No Frame with Standard ID is to be accepted + can_->GFC |= 0x2; //Reject All Standard ID Frames + } else if ((num_stdid < total_available_list_size) && (num_stdid <= 128)) { + can_->SIDFC = (FDCANMessageRAMOffset_ << 2) | (num_stdid << 16); + MessageRam_.StandardFilterSA = SRAMCAN_BASE + (FDCANMessageRAMOffset_ * 4U); + FDCANMessageRAMOffset_ += num_stdid; + total_available_list_size -= num_stdid; + can_->GFC |= (0x3U << 4); //Reject non matching Standard frames + } else { //The List is too big, return fail + can_->CCCR &= ~FDCAN_CCCR_INIT; // Leave init mode + return -ErrFilterNumConfigs; + } + + if (num_stdid) { + num_stdid = 0; //reset list count + filter_ptr = (uint32_t*)MessageRam_.StandardFilterSA; + //Run through the filter list and setup standard id filter list + for (uint8_t i = 0; i < num_configs; i++) { + uint32_t id = 0; + uint32_t mask = 0; + const uavcan::CanFilterConfig* const cfg = filter_configs + i; + if (!((cfg->id & uavcan::CanFrame::FlagEFF) || !(cfg->mask & uavcan::CanFrame::FlagEFF))) { + id = (cfg->id & uavcan::CanFrame::MaskStdID); // Regular std frames, nothing fancy. + mask = (cfg->mask & 0x7F); + filter_ptr[num_stdid] = 0x2U << 30 | //Classic CAN Filter + 0x1U << 27 | //Store in Rx FIFO0 if filter matches + id << 16 | + mask; + num_stdid++; + } + } + } + + //Allocate Message RAM for Extended ID Filter List + if (num_extid == 0) { //No Frame with Extended ID is to be accepted + can_->GFC |= 0x1; //Reject All Extended ID Frames + } else if ((num_extid < (total_available_list_size/2)) && (num_extid <= 64)) { + can_->XIDFC = (FDCANMessageRAMOffset_ << 2) | (num_extid << 16); + MessageRam_.ExtendedFilterSA = SRAMCAN_BASE + (FDCANMessageRAMOffset_ * 4U); + FDCANMessageRAMOffset_ += num_extid*2; + can_->GFC = (0x3U << 2); // Reject non matching Extended frames + } else { //The List is too big, return fail + can_->CCCR &= ~FDCAN_CCCR_INIT; // Leave init mode + return -ErrFilterNumConfigs; + } + + if (num_extid) { + num_extid = 0; + filter_ptr = (uint32_t*)MessageRam_.ExtendedFilterSA; + //Run through the filter list and setup extended id filter list + for (uint8_t i = 0; i < num_configs; i++) { + uint32_t id = 0; + uint32_t mask = 0; + const uavcan::CanFilterConfig* const cfg = filter_configs + i; + if ((cfg->id & uavcan::CanFrame::FlagEFF) || !(cfg->mask & uavcan::CanFrame::FlagEFF)) { + id = (cfg->id & uavcan::CanFrame::MaskExtID); + mask = (cfg->mask & uavcan::CanFrame::MaskExtID); + filter_ptr[num_extid*2] = 0x1U << 29 | id; // Classic CAN Filter + filter_ptr[num_extid*2 + 1] = 0x2U << 30 | mask; //Store in Rx FIFO0 if filter matches + num_extid++; + } + } + } + + MessageRam_.EndAddress = SRAMCAN_BASE + (FDCANMessageRAMOffset_ * 4U); + if (MessageRam_.EndAddress > MESSAGE_RAM_END_ADDR) { + //We are overflowing the limit of Allocated Message RAM + AP_HAL::panic("CANFDIface: Message RAM Overflow!"); + } + + can_->CCCR &= ~FDCAN_CCCR_INIT; // Leave init mode + return 0; +} + +uavcan::uint16_t CanIface::getNumFilters() const +{ + return MAX_FILTER_LIST_SIZE; +} + +int CanIface::init(const uavcan::uint32_t bitrate, const OperatingMode mode) +{ + // Setup FDCAN for configuration mode and disable all interrupts + { + CriticalSectionLocker lock; + + can_->CCCR &= ~FDCAN_CCCR_CSR; // Exit sleep mode + while ((can_->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA) {} //Wait for wake up ack + can_->CCCR |= FDCAN_CCCR_INIT; // Request init + while ((can_->CCCR & FDCAN_CCCR_INIT) == 0) {} + can_->CCCR |= FDCAN_CCCR_CCE; //Enable Config change + can_->IE = 0; // Disable interrupts while initialization is in progress + } + + /* + * Object state - interrupts are disabled, so it's safe to modify it now + */ + rx_queue_.reset(); + error_cnt_ = 0; + served_aborts_cnt_ = 0; + uavcan::fill_n(pending_tx_, NumTxMailboxes, TxItem()); + peak_tx_mailbox_index_ = 0; + had_activity_ = false; + + /* + * CAN timings for this bitrate + */ + Timings timings; + const int timings_res = computeTimings(bitrate, timings); + if (timings_res < 0) { + can_->CCCR &= ~FDCAN_CCCR_INIT; + return timings_res; + } + UAVCAN_STM32_LOG("Timings: presc=%u sjw=%u bs1=%u bs2=%u", + unsigned(timings.prescaler), unsigned(timings.sjw), unsigned(timings.bs1), unsigned(timings.bs2)); + + //setup timing register + //TODO: Do timing calculations for FDCAN + can_->NBTP = ((timings.sjw << FDCAN_NBTP_NSJW_Pos) | + (timings.bs1 << FDCAN_NBTP_NTSEG1_Pos) | + (timings.bs2 << FDCAN_NBTP_TSEG2_Pos) | + (timings.prescaler << FDCAN_NBTP_NBRP_Pos)); + + //RX Config + can_->RXESC = 0; //Set for 8Byte Frames + + //Setup Message RAM + setupMessageRam(); + //Clear all Interrupts + can_->IR = 0x3FFFFFFF; + //Enable Interrupts + can_->IE = FDCAN_IE_TCE | // Transmit Complete interrupt enable + FDCAN_IE_RF0NE | // RX FIFO 0 new message + FDCAN_IE_RF0FE | // Rx FIFO 1 FIFO Full + FDCAN_IE_RF1NE | // RX FIFO 1 new message + FDCAN_IE_RF1FE; // Rx FIFO 1 FIFO Full + can_->ILS = FDCAN_ILS_TCL; //Set Line 1 for Transmit Complete Event Interrupt + can_->TXBTIE = 0xFFFFFFFF; + can_->ILE = 0x3; + + //Leave Init + can_->CCCR &= ~FDCAN_CCCR_INIT; // Leave init mode + return 0; +} + +void CanIface::setupMessageRam() +{ + uint32_t num_elements = 0; + + // Rx FIFO 0 start address and element count + num_elements = MIN((FDCAN_NUM_RXFIFO0_SIZE/FDCAN_FRAME_BUFFER_SIZE), 64U); + if (num_elements) { + can_->RXF0C = (FDCANMessageRAMOffset_ << 2) | (num_elements << 16); + MessageRam_.RxFIFO0SA = SRAMCAN_BASE + (FDCANMessageRAMOffset_ * 4U); + FDCANMessageRAMOffset_ += num_elements*FDCAN_FRAME_BUFFER_SIZE; + } + + // Tx FIFO/queue start address and element count + num_elements = MIN((FDCAN_TX_FIFO_BUFFER_SIZE/FDCAN_FRAME_BUFFER_SIZE), 32U); + if (num_elements) { + can_->TXBC = (FDCANMessageRAMOffset_ << 2) | (num_elements << 24); + can_->TXBC |= 1U << 30; //Set Queue mode + MessageRam_.TxFIFOQSA = SRAMCAN_BASE + (FDCANMessageRAMOffset_ * 4U); + FDCANMessageRAMOffset_ += num_elements*FDCAN_FRAME_BUFFER_SIZE; + } + MessageRam_.EndAddress = SRAMCAN_BASE + (FDCANMessageRAMOffset_ * 4U); + if (MessageRam_.EndAddress > MESSAGE_RAM_END_ADDR) { + //We are overflowing the limit of Allocated Message RAM + AP_HAL::panic("CANFDIface: Message RAM Overflow!"); + return; + } +} + +void CanIface::handleTxCompleteInterrupt(const uavcan::uint64_t utc_usec) +{ + for (uint8_t i = 0; i < NumTxMailboxes; i++) { + if ((can_->TXBTO & (1UL << i))) { + if (pending_tx_[i].loopback && had_activity_) { + rx_queue_.push(pending_tx_[i].frame, utc_usec, uavcan::CanIOFlagLoopback); + } + } + } +} + +bool CanIface::readRxFIFO(uavcan::uint8_t fifo_index) +{ + UAVCAN_ASSERT(fifo_index < 2); + uint32_t *frame_ptr; + uint32_t index; + uavcan::uint64_t utc_usec = clock::getUtcUSecFromCanInterrupt(); + if (fifo_index == 0) { + //Check if RAM allocated to RX FIFO + if ((can_->RXF0C & FDCAN_RXF0C_F0S) == 0) { + UAVCAN_ASSERT(0); + return false; + } + //Register Message Lost as a hardware error + if ((can_->RXF0S & FDCAN_RXF0S_RF0L) != 0) { + error_cnt_++; + } + + if ((can_->RXF0S & FDCAN_RXF0S_F0FL) == 0) { + return false; //No More messages in FIFO + } else { + index = ((can_->RXF0S & FDCAN_RXF0S_F0GI) >> 8); + frame_ptr = (uint32_t *)(MessageRam_.RxFIFO0SA + (index * FDCAN_FRAME_BUFFER_SIZE * 4)); + } + } else if (fifo_index == 1) { + //Check if RAM allocated to RX FIFO + if ((can_->RXF1C & FDCAN_RXF1C_F1S) == 0) { + UAVCAN_ASSERT(0); + return false; + } + //Register Message Lost as a hardware error + if ((can_->RXF1S & FDCAN_RXF1S_RF1L) != 0) { + error_cnt_++; + } + + if ((can_->RXF1S & FDCAN_RXF1S_F1FL) == 0) { + return false; + } else { + index = ((can_->RXF1S & FDCAN_RXF1S_F1GI) >> 8); + frame_ptr = (uint32_t *)(MessageRam_.RxFIFO1SA + (index * FDCAN_FRAME_BUFFER_SIZE * 4)); + } + } else { + return false; + } + + // Read the frame contents + uavcan::CanFrame frame; + uint32_t id = frame_ptr[0]; + if ((id & fdcan::IDE) == 0) { + //Standard ID + frame.id = ((id & fdcan::STID_MASK) >> 18) & uavcan::CanFrame::MaskStdID; + } else { + //Extended ID + frame.id = (id & fdcan::EXID_MASK) & uavcan::CanFrame::MaskExtID; + frame.id |= uavcan::CanFrame::FlagEFF; + } + + if ((id & fdcan::RTR) != 0) { + frame.id |= uavcan::CanFrame::FlagRTR; + } + frame.dlc = (frame_ptr[1] & fdcan::DLC_MASK) >> 16; + uint8_t *data = (uint8_t*)&frame_ptr[2]; + //We only handle Data Length of 8 Bytes for now + for (uint8_t i = 0; i < 8; i++) { + frame.data[i] = data[i]; + } + + //Acknowledge the FIFO entry we just read + if (fifo_index == 0) { + can_->RXF0A = index; + } else if (fifo_index == 1) { + can_->RXF1A = index; + } + + /* + * Store with timeout into the FIFO buffer and signal update event + */ + rx_queue_.push(frame, utc_usec, 0); +#if !HAL_MINIMIZE_FEATURES + if (_slcan_router != nullptr) { + _slcan_router->route_frame_to_slcan(this, frame, utc_usec); + } +#endif + return true; +} + +void CanIface::handleRxInterrupt(uavcan::uint8_t fifo_index) +{ + while (readRxFIFO(fifo_index)) { + had_activity_ = true; + } + update_event_.signalFromInterrupt(); +} + +void CanIface::pollErrorFlagsFromISR() +{ + const uavcan::uint8_t cel = can_->ECR >> 16; + + if (cel != 0) { + for (int i = 0; i < NumTxMailboxes; i++) { + if (!pending_tx_[i].abort_on_error) { + continue; + } + if (((1 << pending_tx_[i].index) & can_->TXBRP)) { + can_->TXBCR = 1 << pending_tx_[i].index; // Goodnight sweet transmission + error_cnt_++; + //Wait for Cancelation to finish + while (!(can_->TXBCF & (1 << pending_tx_[i].index))) {} + served_aborts_cnt_++; + } + } + } +} + +void CanIface::discardTimedOutTxMailboxes(uavcan::MonotonicTime current_time) +{ + CriticalSectionLocker lock; + for (int i = 0; i < NumTxMailboxes; i++) { + if (((1 << pending_tx_[i].index) & can_->TXBRP) && pending_tx_[i].deadline < current_time) { + can_->TXBCR = 1 << pending_tx_[i].index; // Goodnight sweet transmission + error_cnt_++; + //Wait for Cancelation to finish + while (!(can_->TXBCF & (1 << pending_tx_[i].index))) {} + } + } +} + +bool CanIface::canAcceptNewTxFrame(const uavcan::CanFrame& frame) const +{ + //Check if Tx FIFO is allocated + if ((can_->TXBC & FDCAN_TXBC_TFQS) == 0) { + return false; + } + if ((can_->TXFQS & FDCAN_TXFQS_TFQF) != 0) { + return false; //we don't have free space + } + + return true; +} + +bool CanIface::isRxBufferEmpty() const +{ + CriticalSectionLocker lock; + return rx_queue_.getLength() == 0; +} + +uavcan::uint64_t CanIface::getErrorCount() const +{ + CriticalSectionLocker lock; + return error_cnt_ + rx_queue_.getOverflowCount(); +} + +unsigned CanIface::getRxQueueLength() const +{ + CriticalSectionLocker lock; + return rx_queue_.getLength(); +} + +bool CanIface::hadActivity() +{ + CriticalSectionLocker lock; + const bool ret = had_activity_; + had_activity_ = false; + return ret; +} + +/* + * CanDriver + */ +uavcan::CanSelectMasks CanDriver::makeSelectMasks(const uavcan::CanFrame* (& pending_tx)[uavcan::MaxCanIfaces]) const +{ + uavcan::CanSelectMasks msk; + + for (uavcan::uint8_t i = 0; i < num_ifaces_; i++) { + CanIface* iface = ifaces[if_int_to_gl_index_[i]]; + msk.read |= (iface->isRxBufferEmpty() ? 0 : 1) << i; + + if (pending_tx[i] != UAVCAN_NULLPTR) { + msk.write |= (iface->canAcceptNewTxFrame(*pending_tx[i]) ? 1 : 0) << i; + } + } + + return msk; +} + +bool CanDriver::hasReadableInterfaces() const +{ + for (uavcan::uint8_t i = 0; i < num_ifaces_; i++) { + if (!ifaces[if_int_to_gl_index_[i]]->isRxBufferEmpty()) { + return true; + } + } + + return false; +} + +uavcan::int16_t CanDriver::select(uavcan::CanSelectMasks& inout_masks, + const uavcan::CanFrame* (& pending_tx)[uavcan::MaxCanIfaces], + const uavcan::MonotonicTime blocking_deadline) +{ + const uavcan::CanSelectMasks in_masks = inout_masks; + const uavcan::MonotonicTime time = clock::getMonotonic(); + + for (uavcan::uint8_t i = 0; i < num_ifaces_; i++) { + CanIface* iface = ifaces[if_int_to_gl_index_[i]]; + iface->discardTimedOutTxMailboxes(time); // Check TX timeouts - this may release some TX slots + { + CriticalSectionLocker cs_locker; + iface->pollErrorFlagsFromISR(); + } + } + + inout_masks = makeSelectMasks(pending_tx); // Check if we already have some of the requested events + if ((inout_masks.read & in_masks.read) != 0 || + (inout_masks.write & in_masks.write) != 0) { + return 1; + } + + (void)update_event_.wait(blocking_deadline - time); // Block until timeout expires or any iface updates + inout_masks = makeSelectMasks(pending_tx); // Return what we got even if none of the requested events are set + return 1; // Return value doesn't matter as long as it is non-negative +} + +void CanDriver::initOnce() +{ + { + CriticalSectionLocker lock; + RCC->APB1HRSTR |= RCC_APB1HRSTR_FDCANRST; + RCC->APB1HRSTR &= ~RCC_APB1HRSTR_FDCANRST; + RCC->APB1HENR |= RCC_APB1HENR_FDCANEN; + } + + /* + * IRQ + */ + { + CriticalSectionLocker lock; + nvicEnableVector(FDCAN1_IT0_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); + nvicEnableVector(FDCAN1_IT1_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); +# if UAVCAN_STM32_NUM_IFACES > 1 + nvicEnableVector(FDCAN2_IT0_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); + nvicEnableVector(FDCAN2_IT1_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); +# endif + } +} + +int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode) +{ + int res = 0; + + UAVCAN_STM32_LOG("Bitrate %lu mode %d", static_cast(bitrate), static_cast(mode)); + + static bool initialized_once = false; + if (!initialized_once) { + initialized_once = true; + UAVCAN_STM32_LOG("First initialization"); + initOnce(); + } + + /* + * CAN1 + */ + UAVCAN_STM32_LOG("Initing iface 0..."); + ifaces[0] = &if0_; // This link must be initialized first, + res = if0_.init(bitrate, mode); // otherwise an IRQ may fire while the interface is not linked yet; + if (res < 0) { // a typical race condition. + UAVCAN_STM32_LOG("Iface 0 init failed %i", res); + ifaces[0] = UAVCAN_NULLPTR; + goto fail; + } + + /* + * CAN2 + */ +#if UAVCAN_STM32_NUM_IFACES > 1 + UAVCAN_STM32_LOG("Initing iface 1..."); + ifaces[1] = &if1_; // Same thing here. + res = if1_.init(bitrate, mode); + if (res < 0) { + UAVCAN_STM32_LOG("Iface 1 init failed %i", res); + ifaces[1] = UAVCAN_NULLPTR; + goto fail; + } +#endif + + UAVCAN_STM32_LOG("CAN drv init OK"); + UAVCAN_ASSERT(res >= 0); + return res; + +fail: + UAVCAN_STM32_LOG("CAN drv init failed %i", res); + UAVCAN_ASSERT(res < 0); + return res; +} + +bool CanDriver::clock_init_ = false; +void CanDriver::initOnce(uavcan::uint8_t can_number, bool enable_irqs) +{ + //Only do it once + //Doing it second time will reset the previously initialised bus + if (!clock_init_) { + CriticalSectionLocker lock; + RCC->APB1HENR |= RCC_APB1HENR_FDCANEN; + RCC->APB1HRSTR |= RCC_APB1HRSTR_FDCANRST; + RCC->APB1HRSTR &= ~RCC_APB1HRSTR_FDCANRST; + clock_init_ = true; + } + + if (!enable_irqs) { + return; + } + /* + * IRQ + */ + { + CriticalSectionLocker lock; + if (can_number == 0) { + nvicEnableVector(FDCAN1_IT0_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); + nvicEnableVector(FDCAN1_IT1_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); + } +# if UAVCAN_STM32_NUM_IFACES > 1 + else if (can_number == 1) { + nvicEnableVector(FDCAN2_IT0_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); + nvicEnableVector(FDCAN2_IT1_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK); + } +# endif + } +} + +int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode, uavcan::uint8_t can_number) +{ + int res = 0; + + UAVCAN_STM32_LOG("Bitrate %lu mode %d", static_cast(bitrate), static_cast(mode)); + if (can_number > UAVCAN_STM32_NUM_IFACES) { + res = -1; + goto fail; + } + static bool initialized_once[UAVCAN_STM32_NUM_IFACES] = {false}; + + if (!initialized_once[can_number]) { + initialized_once[can_number] = true; + initialized_by_me_[can_number] = true; + + if (can_number == 1 && !initialized_once[0]) { + UAVCAN_STM32_LOG("Iface 0 is not initialized yet but we need it for Iface 1, trying to init it"); + UAVCAN_STM32_LOG("Enabling CAN iface 0"); + initOnce(0, false); + UAVCAN_STM32_LOG("Initing iface 0..."); + res = if0_.init(bitrate, mode); + + if (res < 0) { + UAVCAN_STM32_LOG("Iface 0 init failed %i", res); + goto fail; + } + } + + UAVCAN_STM32_LOG("Enabling CAN iface %d", can_number); + initOnce(can_number, true); + } else if (!initialized_by_me_[can_number]) { + UAVCAN_STM32_LOG("CAN iface %d initialized in another CANDriver!", can_number); + res = -2; + goto fail; + } + + if (can_number == 0) { + /* + * CAN1 + */ + UAVCAN_STM32_LOG("Initing iface 0..."); + ifaces[0] = &if0_; // This link must be initialized first, + res = if0_.init(bitrate, mode); // otherwise an IRQ may fire while the interface is not linked yet; + if (res < 0) { // a typical race condition. + UAVCAN_STM32_LOG("Iface 0 init failed %i", res); + ifaces[0] = UAVCAN_NULLPTR; + goto fail; + } + } else if (can_number == 1) { + /* + * CAN2 + */ +#if UAVCAN_STM32_NUM_IFACES > 1 + UAVCAN_STM32_LOG("Initing iface 1..."); + ifaces[1] = &if1_; // Same thing here. + res = if1_.init(bitrate, mode); + if (res < 0) { + UAVCAN_STM32_LOG("Iface 1 init failed %i", res); + ifaces[1] = UAVCAN_NULLPTR; + goto fail; + } +#endif + } + + if_int_to_gl_index_[num_ifaces_++] = can_number; + + UAVCAN_STM32_LOG("CAN drv init OK"); + UAVCAN_ASSERT(res >= 0); + return res; + +fail: + UAVCAN_STM32_LOG("CAN drv init failed %i", res); + UAVCAN_ASSERT(res < 0); + return res; +} + +CanIface* CanDriver::getIface(uavcan::uint8_t iface_index) +{ + if (iface_index < num_ifaces_) { + return ifaces[if_int_to_gl_index_[iface_index]]; + } + return UAVCAN_NULLPTR; +} + +bool CanDriver::hadActivity() +{ + for (uavcan::uint8_t i = 0; i < num_ifaces_; i++) { + if (ifaces[if_int_to_gl_index_[i]]->hadActivity()) { + return true; + } + } + + return false; +} + +} // namespace uavcan_stm32 + +/* + * Interrupt handlers + */ +extern "C" +{ + + UAVCAN_STM32_IRQ_HANDLER(FDCAN1_IT0_IRQHandler); + UAVCAN_STM32_IRQ_HANDLER(FDCAN1_IT0_IRQHandler) + { + UAVCAN_STM32_IRQ_PROLOGUE(); + ChibiOS_CAN::handleInterrupt(0, 0); + UAVCAN_STM32_IRQ_EPILOGUE(); + } + + UAVCAN_STM32_IRQ_HANDLER(FDCAN1_IT1_IRQHandler); + UAVCAN_STM32_IRQ_HANDLER(FDCAN1_IT1_IRQHandler) + { + UAVCAN_STM32_IRQ_PROLOGUE(); + ChibiOS_CAN::handleInterrupt(0, 1); + UAVCAN_STM32_IRQ_EPILOGUE(); + } + + +# if UAVCAN_STM32_NUM_IFACES > 1 + + UAVCAN_STM32_IRQ_HANDLER(FDCAN2_IT0_IRQHandler); + UAVCAN_STM32_IRQ_HANDLER(FDCAN2_IT0_IRQHandler) + { + UAVCAN_STM32_IRQ_PROLOGUE(); + ChibiOS_CAN::handleInterrupt(1, 0); + UAVCAN_STM32_IRQ_EPILOGUE(); + } + + UAVCAN_STM32_IRQ_HANDLER(FDCAN2_IT1_IRQHandler); + UAVCAN_STM32_IRQ_HANDLER(FDCAN2_IT1_IRQHandler) + { + UAVCAN_STM32_IRQ_PROLOGUE(); + ChibiOS_CAN::handleInterrupt(1, 1); + UAVCAN_STM32_IRQ_EPILOGUE(); + } + +# endif + +} // extern "C" + +#endif //defined(STM32H7XX) + +#endif //HAL_WITH_UAVCAN diff --git a/libraries/AP_HAL_ChibiOS/CANFDIface.h b/libraries/AP_HAL_ChibiOS/CANFDIface.h new file mode 100644 index 0000000000..69f6a57d1d --- /dev/null +++ b/libraries/AP_HAL_ChibiOS/CANFDIface.h @@ -0,0 +1,448 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Kirienko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * This file is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Code by Siddharth Bharat Purohit + */ + +#pragma once + +#include "AP_HAL_ChibiOS.h" + +#if HAL_WITH_UAVCAN + +#include "CANThread.h" +#include "fdcan.hpp" + +class SLCANRouter; + +namespace ChibiOS_CAN +{ +/** + * Driver error codes. + * These values can be returned from driver functions negated. + */ +//static const uavcan::int16_t ErrUnknown = 1000; ///< Reserved for future use +static const uavcan::int16_t ErrNotImplemented = 1001; ///< Feature not implemented +static const uavcan::int16_t ErrInvalidBitRate = 1002; ///< Bit rate not supported +static const uavcan::int16_t ErrLogic = 1003; ///< Internal logic error +static const uavcan::int16_t ErrUnsupportedFrame = 1004; ///< Frame not supported (e.g. RTR, CAN FD, etc) +static const uavcan::int16_t ErrMsrInakNotSet = 1005; ///< INAK bit of the MSR register is not 1 +static const uavcan::int16_t ErrMsrInakNotCleared = 1006; ///< INAK bit of the MSR register is not 0 +static const uavcan::int16_t ErrBitRateNotDetected = 1007; ///< Auto bit rate detection could not be finished +static const uavcan::int16_t ErrFilterNumConfigs = 1008; ///< Number of filters is more than supported + +/** + * RX queue item. + * The application shall not use this directly. + */ +struct CanRxItem { + uavcan::uint64_t utc_usec; + uavcan::CanFrame frame; + uavcan::CanIOFlags flags; + CanRxItem() + : utc_usec(0) + , flags(0) + { } +}; + +/** + * Single CAN iface. + * The application shall not use this directly. + */ +class CanIface : public uavcan::ICanIface, uavcan::Noncopyable +{ + friend class ::SLCANRouter; + static SLCANRouter *_slcan_router; + class RxQueue + { + CanRxItem* const buf_; + const uavcan::uint8_t capacity_; + uavcan::uint8_t in_; + uavcan::uint8_t out_; + uavcan::uint8_t len_; + uavcan::uint32_t overflow_cnt_; + void registerOverflow(); + + public: + RxQueue(CanRxItem* buf, uavcan::uint8_t capacity) + : buf_(buf) + , capacity_(capacity) + , in_(0) + , out_(0) + , len_(0) + , overflow_cnt_(0) + { } + + void push(const uavcan::CanFrame& frame, const uint64_t& utc_usec, uavcan::CanIOFlags flags); + void pop(uavcan::CanFrame& out_frame, uavcan::uint64_t& out_utc_usec, uavcan::CanIOFlags& out_flags); + + void reset(); + + unsigned getLength() const + { + return len_; + } + + uavcan::uint32_t getOverflowCount() const + { + return overflow_cnt_; + } + }; + + struct MessageRAM { + uavcan::uint32_t StandardFilterSA; + uavcan::uint32_t ExtendedFilterSA; + uavcan::uint32_t RxFIFO0SA; + uavcan::uint32_t RxFIFO1SA; + uavcan::uint32_t TxFIFOQSA; + uavcan::uint32_t EndAddress; + } MessageRam_; + + struct Timings { + uavcan::uint16_t prescaler; + uavcan::uint8_t sjw; + uavcan::uint8_t bs1; + uavcan::uint8_t bs2; + + Timings() + : prescaler(0) + , sjw(0) + , bs1(0) + , bs2(0) + { } + }; + + struct TxItem { + uavcan::MonotonicTime deadline; + uavcan::CanFrame frame; + bool loopback; + bool abort_on_error; + uint8_t index; + TxItem() : + loopback(false), + abort_on_error(false) + { } + }; + + enum { NumTxMailboxes = 32 }; + + static const uavcan::uint32_t TSR_ABRQx[NumTxMailboxes]; + static uint32_t FDCANMessageRAMOffset_; + RxQueue rx_queue_; + fdcan::CanType* const can_; + uavcan::uint64_t error_cnt_; + uavcan::uint32_t served_aborts_cnt_; + BusEvent& update_event_; + TxItem pending_tx_[NumTxMailboxes]; + uavcan::uint8_t peak_tx_mailbox_index_; + const uavcan::uint8_t self_index_; + bool had_activity_; + int computeTimings(uavcan::uint32_t target_bitrate, Timings& out_timings); + + virtual uavcan::int16_t send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline, + uavcan::CanIOFlags flags); + + virtual uavcan::int16_t receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic, + uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags); + + virtual uavcan::int16_t configureFilters(const uavcan::CanFilterConfig* filter_configs, + uavcan::uint16_t num_configs); + + virtual uavcan::uint16_t getNumFilters() const; + + void setupMessageRam(void); + + static uint32_t FDCAN2MessageRAMOffset_; +public: + enum { MaxRxQueueCapacity = 254 }; + + enum OperatingMode { + NormalMode, + SilentMode + }; + + CanIface(fdcan::CanType* can, BusEvent& update_event, uavcan::uint8_t self_index, + CanRxItem* rx_queue_buffer, uavcan::uint8_t rx_queue_capacity); + + /** + * Initializes the hardware CAN controller. + * Assumes: + * - Iface clock is enabled + * - Iface has been resetted via RCC + * - Caller will configure NVIC by itself + */ + int init(const uavcan::uint32_t bitrate, const OperatingMode mode); + + void handleTxCompleteInterrupt(uavcan::uint64_t utc_usec); + void handleRxInterrupt(uavcan::uint8_t fifo_index); + + bool readRxFIFO(uavcan::uint8_t fifo_index); + /** + * This method is used to count errors and abort transmission on error if necessary. + * This functionality used to be implemented in the SCE interrupt handler, but that approach was + * generating too much processing overhead, especially on disconnected interfaces. + * + * Should be called from RX ISR, TX ISR, and select(); interrupts must be enabled. + */ + void pollErrorFlagsFromISR(); + + void discardTimedOutTxMailboxes(uavcan::MonotonicTime current_time); + + bool canAcceptNewTxFrame(const uavcan::CanFrame& frame) const; + bool isRxBufferEmpty() const; + + /** + * Number of RX frames lost due to queue overflow. + * This is an atomic read, it doesn't require a critical section. + */ + uavcan::uint32_t getRxQueueOverflowCount() const + { + return rx_queue_.getOverflowCount(); + } + + /** + * Total number of hardware failures and other kinds of errors (e.g. queue overruns). + * May increase continuously if the interface is not connected to the bus. + */ + virtual uavcan::uint64_t getErrorCount() const; + + /** + * Number of times the driver exercised library's requirement to abort transmission on first error. + * This is an atomic read, it doesn't require a critical section. + * See @ref uavcan::CanIOFlagAbortOnError. + */ + uavcan::uint32_t getVoluntaryTxAbortCount() const + { + return served_aborts_cnt_; + } + + /** + * Returns the number of frames pending in the RX queue. + * This is intended for debug use only. + */ + unsigned getRxQueueLength() const; + + /** + * Whether this iface had at least one successful IO since the previous call of this method. + * This is designed for use with iface activity LEDs. + */ + bool hadActivity(); + + /** + * Peak number of TX mailboxes used concurrently since initialization. + * Range is [1, 3]. + * Value of 3 suggests that priority inversion could be taking place. + */ + uavcan::uint8_t getPeakNumTxMailboxesUsed() const + { + return uavcan::uint8_t(peak_tx_mailbox_index_ + 1); + } + + fdcan::CanType* can_reg(void) + { + return can_; + } +}; + +/** + * CAN driver, incorporates all available CAN ifaces. + * Please avoid direct use, prefer @ref CanInitHelper instead. + */ +class CanDriver : public uavcan::ICanDriver, uavcan::Noncopyable +{ + BusEvent update_event_; + static bool clock_init_; + CanIface if0_; +#if UAVCAN_STM32_NUM_IFACES > 1 + CanIface if1_; +#endif + bool initialized_by_me_[UAVCAN_STM32_NUM_IFACES]; + uavcan::uint8_t num_ifaces_; + uavcan::uint8_t if_int_to_gl_index_[UAVCAN_STM32_NUM_IFACES]; + + + virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks, + const uavcan::CanFrame* (& pending_tx)[uavcan::MaxCanIfaces], + uavcan::MonotonicTime blocking_deadline); + + static void initOnce(); + static void initOnce(uavcan::uint8_t can_number, bool enable_irqs); + +public: + template + CanDriver(CanRxItem(&rx_queue_storage)[UAVCAN_STM32_NUM_IFACES][RxQueueCapacity]) + : update_event_(*this) + , if0_(fdcan::Can[0], update_event_, 0, rx_queue_storage[0], RxQueueCapacity) +#if UAVCAN_STM32_NUM_IFACES > 1 + , if1_(fdcan::Can[1], update_event_, 1, rx_queue_storage[1], RxQueueCapacity) +#endif + { + uavcan::StaticAssert<(RxQueueCapacity <= CanIface::MaxRxQueueCapacity)>::check(); + } + + /** + * This function returns select masks indicating which interfaces are available for read/write. + */ + uavcan::CanSelectMasks makeSelectMasks(const uavcan::CanFrame* (& pending_tx)[uavcan::MaxCanIfaces]) const; + + BusEvent* getUpdateEvent() + { + return &update_event_; + } + /** + * Whether there's at least one interface where receive() would return a frame. + */ + bool hasReadableInterfaces() const; + + /** + * Returns zero if OK. + * Returns negative value if failed (e.g. invalid bitrate). + */ + int init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode); + int init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode, uavcan::uint8_t can_number); + + virtual CanIface* getIface(uavcan::uint8_t iface_index); + + virtual uavcan::uint8_t getNumIfaces() const + { + return num_ifaces_; + } + + /** + * Whether at least one iface had at least one successful IO since previous call of this method. + * This is designed for use with iface activity LEDs. + */ + bool hadActivity(); +}; + +/** + * Helper class. + * Normally only this class should be used by the application. + * 145 usec per Extended CAN frame @ 1 Mbps, e.g. 32 RX slots * 145 usec --> 4.6 msec before RX queue overruns. + */ +template +class CanInitHelper +{ + CanRxItem queue_storage_[UAVCAN_STM32_NUM_IFACES][RxQueueCapacity]; + +public: + enum { BitRateAutoDetect = 0 }; + + CanDriver driver; + + CanInitHelper() : + driver(queue_storage_) + { } + + /** + * This overload simply configures the provided bitrate. + * Auto bit rate detection will not be performed. + * Bitrate value must be positive. + * @return Negative value on error; non-negative on success. Refer to constants Err*. + */ + int init(uavcan::uint32_t bitrate) + { + return driver.init(bitrate, CanIface::NormalMode); + } + + int init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode, uavcan::uint8_t can_number) + { + return driver.init(bitrate, mode, can_number); + } + + /** + * This function can either initialize the driver at a fixed bit rate, or it can perform + * automatic bit rate detection. For theory please refer to the CiA application note #801. + * + * @param delay_callable A callable entity that suspends execution for strictly more than one second. + * The callable entity will be invoked without arguments. + * @ref getRecommendedListeningDelay(). + * + * @param inout_bitrate Fixed bit rate or zero. Zero invokes the bit rate detection process. + * If auto detection was used, the function will update the argument + * with established bit rate. In case of an error the value will be undefined. + * + * @return Negative value on error; non-negative on success. Refer to constants Err*. + */ + template + int init(DelayCallable delay_callable, uavcan::uint32_t& inout_bitrate = BitRateAutoDetect) + { + if (inout_bitrate > 0) { + return driver.init(inout_bitrate, CanIface::NormalMode); + } else { + static const uavcan::uint32_t StandardBitRates[] = { + 1000000, + 500000, + 250000, + 125000 + }; + + for (uavcan::uint8_t br = 0; br < sizeof(StandardBitRates) / sizeof(StandardBitRates[0]); br++) { + inout_bitrate = StandardBitRates[br]; + + const int res = driver.init(inout_bitrate, CanIface::SilentMode); + + delay_callable(); + + if (res >= 0) { + for (uavcan::uint8_t iface = 0; iface < driver.getNumIfaces(); iface++) { + if (!driver.getIface(iface)->isRxBufferEmpty()) { + // Re-initializing in normal mode + return driver.init(inout_bitrate, CanIface::NormalMode); + } + } + } + } + + return -ErrBitRateNotDetected; + } + } + + /** + * Use this value for listening delay during automatic bit rate detection. + */ + static uavcan::MonotonicDuration getRecommendedListeningDelay() + { + return uavcan::MonotonicDuration::fromMSec(1050); + } +}; + +} + +#include "CANSerialRouter.h" + +#endif //HAL_WITH_UAVCAN diff --git a/libraries/AP_HAL_ChibiOS/CANIface.h b/libraries/AP_HAL_ChibiOS/CANIface.h index 761c57d0ac..6fe8d848db 100644 --- a/libraries/AP_HAL_ChibiOS/CANIface.h +++ b/libraries/AP_HAL_ChibiOS/CANIface.h @@ -43,7 +43,9 @@ #include "AP_HAL_ChibiOS.h" #if HAL_WITH_UAVCAN - +# if defined(STM32H7XX) +#include "CANFDIface.h" +#else #include "CANThread.h" #include "CANIface.h" #include "bxcan.hpp" @@ -444,5 +446,5 @@ public: } #include "CANSerialRouter.h" - +#endif // defined(STM32H7XX) #endif //HAL_WITH_UAVCAN diff --git a/libraries/AP_HAL_ChibiOS/CanIface.cpp b/libraries/AP_HAL_ChibiOS/CanIface.cpp index d5d32a8790..9b06e156a6 100644 --- a/libraries/AP_HAL_ChibiOS/CanIface.cpp +++ b/libraries/AP_HAL_ChibiOS/CanIface.cpp @@ -43,13 +43,15 @@ #if HAL_WITH_UAVCAN #include #include -#include "CANIface.h" #include "CANClock.h" #include "CANInternal.h" #include "CANSerialRouter.h" #include # include +# if !defined(STM32H7XX) +#include "CANIface.h" + #if CH_KERNEL_MAJOR == 2 # if !(defined(STM32F10X_CL) || defined(STM32F2XX) || defined(STM32F3XX) || defined(STM32F4XX)) // IRQ numbers @@ -1253,4 +1255,6 @@ UAVCAN_STM32_IRQ_HANDLER(CAN2_RX1_IRQHandler) } // extern "C" +#endif //!defined(STM32H7XX) + #endif //HAL_WITH_UAVCAN diff --git a/libraries/AP_HAL_ChibiOS/bxcan.hpp b/libraries/AP_HAL_ChibiOS/bxcan.hpp index 334009e9e4..ba255b60a0 100644 --- a/libraries/AP_HAL_ChibiOS/bxcan.hpp +++ b/libraries/AP_HAL_ChibiOS/bxcan.hpp @@ -44,7 +44,7 @@ #if HAL_WITH_UAVCAN -#include +# if !defined(STM32H7XX) #include #include @@ -326,4 +326,5 @@ constexpr unsigned long FMR_FINIT = (1U << 0); /* Bit 0: Filter Init #if UAVCAN_CPP_VERSION < UAVCAN_CPP11 # undef constexpr #endif +#endif //!defined(STM32H7XX) #endif //HAL_WITH_UAVCAN \ No newline at end of file diff --git a/libraries/AP_HAL_ChibiOS/fdcan.hpp b/libraries/AP_HAL_ChibiOS/fdcan.hpp new file mode 100644 index 0000000000..67165833cd --- /dev/null +++ b/libraries/AP_HAL_ChibiOS/fdcan.hpp @@ -0,0 +1,90 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Pavel Kirienko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * This file is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Code by Siddharth Bharat Purohit + */ + +#pragma once + +#include "AP_HAL_ChibiOS.h" + +#if HAL_WITH_UAVCAN + +#if defined(STM32H7XX) + +#include +#include + +#ifndef UAVCAN_CPP_VERSION +# error UAVCAN_CPP_VERSION +#endif + +#if UAVCAN_CPP_VERSION < UAVCAN_CPP11 +// #undef'ed at the end of this file +# define constexpr const +#endif + +namespace ChibiOS_CAN +{ +namespace fdcan +{ +typedef FDCAN_GlobalTypeDef CanType; + +constexpr unsigned long IDE = (0x40000000U); // Identifier Extension +constexpr unsigned long STID_MASK = (0x1FFC0000U); // Standard Identifier Mask +constexpr unsigned long EXID_MASK = (0x1FFFFFFFU); // Extended Identifier Mask +constexpr unsigned long RTR = (0x20000000U); // Remote Transmission Request +constexpr unsigned long DLC_MASK = (0x000F0000U); // Data Length Code + +/** + * CANx register sets + */ +CanType* const Can[UAVCAN_STM32_NUM_IFACES] = { + reinterpret_cast(FDCAN1_BASE) +#if UAVCAN_STM32_NUM_IFACES > 1 + , + reinterpret_cast(FDCAN2_BASE) +#endif +}; +} +} + +#if UAVCAN_CPP_VERSION < UAVCAN_CPP11 +# undef constexpr +#endif +#endif //defined(STM32H7XX) +#endif //HAL_WITH_UAVCAN \ No newline at end of file diff --git a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange/hwdef.dat b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange/hwdef.dat index 8175c9f77f..4633234702 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange/hwdef.dat +++ b/libraries/AP_HAL_ChibiOS/hwdef/CubeOrange/hwdef.dat @@ -81,8 +81,8 @@ PB2 BOOT1 INPUT PB3 FMU_SW0 INPUT # This defines the pins for the 2nd CAN interface, if available. -#PB6 CAN2_TX CAN2 -#PB12 CAN2_RX CAN2 +PB6 CAN2_TX CAN2 +PB12 CAN2_RX CAN2 # Now the first I2C bus. The pin speeds are automatically setup # correctly, but can be overridden here if needed. @@ -135,8 +135,8 @@ PD7 BARO_CS CS PE4 MPU_EXT_CS CS # the first CAN bus -#PD0 CAN1_RX CAN1 -#PD1 CAN1_TX CAN1 +PD0 CAN1_RX CAN1 +PD1 CAN1_TX CAN1 # Another USART, this one for telem1. This one has RTS and CTS lines. # USART2 serial2 telem1