Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ATSAM CAN driver (MCAN peripheral) #955

Merged
merged 9 commits into from
May 9, 2023
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
<td align="center">✅</td>
<td align="center">✕</td>
<td align="center">○</td>
<td align="center"></td>
<td align="center"></td>
<td align="center">✕</td>
<td align="center">✕</td>
<td align="center">○</td>
Expand Down
77 changes: 77 additions & 0 deletions examples/samv71_xplained_ultra/fdcan/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2023, Raphael Lehmann
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include <modm/board.hpp>
#include <modm/debug/logger.hpp>

using namespace modm::literals;
using namespace modm::platform;
using namespace Board;

// Set the log level
#undef MODM_LOG_LEVEL
#define MODM_LOG_LEVEL modm::log::INFO

int
main()
{
Board::initialize();

MODM_LOG_INFO << "CAN Test Program" << modm::endl;

MODM_LOG_INFO << "Mcan1: Initializing with 125kbps / 1Mbps (FDCAN) for boards CAN transceiver (PC12/PC14)." << modm::endl;
// Mcan1 is connted in Board::initialize(); CAN transceiver on the dev board
Mcan1::initialize<Board::SystemClock, 125_kbps, 1_pct, 1_Mbps>(12);

MODM_LOG_INFO << "Mcan1: Setting up Filter to receive every message." << modm::endl;
Mcan1::setExtendedFilter(0, Mcan1::FilterConfig::Fifo0,
modm::can::ExtendedIdentifier(0),
modm::can::ExtendedMask(0));
Mcan1::setStandardFilter(0, Mcan1::FilterConfig::Fifo0,
modm::can::StandardIdentifier(0),
modm::can::StandardMask(0));

Mcan1::setMode(Mcan1::Mode::TestExternalLoopback);

Mcan1::setErrorInterruptCallback([](){
Board::Led1::set();
});

uint32_t counter{0};

while (true)
{
counter++;
MODM_LOG_INFO << "loop: " << counter << modm::endl;

modm::can::Message txMsg{counter, 64};
txMsg.setExtended(true);
for (size_t i = 0; i < txMsg.capacity; ++i) {
txMsg.data[i] = counter;
}
MODM_LOG_INFO << "Mcan1: Sending message... " << txMsg << modm::endl;
Mcan1::sendMessage(txMsg);

if (Mcan1::isMessageAvailable())
{
MODM_LOG_INFO << "Mcan1: Message is available... ";
modm::can::Message rxMsg;
if (Mcan1::getMessage(rxMsg))
MODM_LOG_INFO << rxMsg << modm::endl;
else
MODM_LOG_INFO << " but getting message FAILED" << modm::endl;
}

Led0::toggle();
modm::delay(500ms);
}

return 0;
}
10 changes: 10 additions & 0 deletions examples/samv71_xplained_ultra/fdcan/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<library>
<extends>modm:samv71-xplained-ultra</extends>
<options>
<option name="modm:build:build.path">../../../build/samv71_xplained_ultra/fdcan</option>
<option name="modm:architecture:can:message.buffer">64</option>
</options>
<modules>
<module>modm:build:scons</module>
</modules>
</library>
54 changes: 30 additions & 24 deletions src/modm/architecture/interface/can_message.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,90 +26,95 @@ namespace modm::can
/// @ingroup modm_architecture_can
struct Message
{
inline Message(uint32_t inIdentifier = 0, uint8_t inLength = 0) :
constexpr Message(uint32_t inIdentifier = 0, uint8_t inLength = 0) :
identifier(inIdentifier), flags()
{
setLength(inLength);
}

inline Message(uint32_t inIdentifier, uint8_t inLength, const uint64_t &inData, bool extended = false) :
constexpr Message(uint32_t inIdentifier, uint8_t inLength, const uint64_t &inData, bool extended = false) :
Message(inIdentifier, std::min(inLength, uint8_t(8)))
{
flags.extended = extended;
const uint8_t *inDataB = reinterpret_cast<const uint8_t *>(&inData);
for (uint8_t ii = 0; ii < length; ++ii)
data[ii] = inDataB[length - ii - 1];
data[ii] = inData >> ((7 - ii - (8 - length)) * 8);
}

inline Message(uint32_t inIdentifier, uint8_t inLength, const uint8_t *inData, bool extended = false) :
constexpr Message(uint32_t inIdentifier, uint8_t inLength, const uint8_t *inData, bool extended = false) :
Message(inIdentifier, inLength)
{
flags.extended = extended;
for (uint8_t ii = 0; ii < length; ++ii)
data[ii] = inData[ii];
}

inline uint32_t
constexpr Message(const modm::can::Message& rhs)
: identifier{rhs.identifier}, flags{rhs.flags}, dlc{rhs.dlc}, length{rhs.length}
{
std::copy(std::begin(rhs.data), std::end(rhs.data), std::begin(data));
}

constexpr uint32_t
getIdentifier() const
{
return identifier;
}

inline void
constexpr void
setIdentifier(uint32_t id)
{
identifier = id;
}

inline constexpr uint8_t
constexpr uint8_t
getCapacity() const
{
return capacity;
}

inline void
constexpr void
setFlexibleData(bool fd = true)
{
flags.fd = fd;
}

inline bool
constexpr bool
isFlexibleData() const
{
return (flags.fd != 0);
}

inline bool
constexpr bool
isBitRateSwitching() const
{
return (flags.brs != 0);
}

inline void
constexpr void
setExtended(bool extended = true)
{
flags.extended = (extended) ? 1 : 0;
}

inline bool
constexpr bool
isExtended() const
{
return (flags.extended != 0);
}

inline void
constexpr void
setRemoteTransmitRequest(bool rtr = true)
{
flags.rtr = (rtr) ? 1 : 0;
}

inline bool
constexpr bool
isRemoteTransmitRequest() const
{
return (flags.rtr != 0);
}

inline void
constexpr void
setDataLengthCode(uint8_t inDlc)
{
while (dlcConversionTable[inDlc] > capacity) inDlc--;
Expand All @@ -119,7 +124,7 @@ struct Message
if (dlc > 8) setFlexibleData();
}

inline void
constexpr void
setLength(uint8_t inLength)
{
if constexpr (capacity <= 8)
Expand All @@ -134,13 +139,13 @@ struct Message
}
}

inline uint8_t
constexpr uint8_t
getLength() const
{
return length;
}

inline uint8_t
constexpr uint8_t
getDataLengthCode() const
{
return dlc;
Expand All @@ -153,7 +158,7 @@ public:
uint32_t identifier;
struct Flags
{
Flags() :
constexpr Flags() :
fd(0), brs(0), rtr(0), extended(1)
{
}
Expand All @@ -165,10 +170,10 @@ public:
} flags;
uint8_t dlc;
uint8_t length;
uint8_t modm_aligned(4) data[capacity];
uint8_t modm_aligned(4) data[capacity]{};

public:
inline bool
constexpr bool
operator == (const modm::can::Message& rhs) const
{
return ((this->identifier == rhs.identifier) and
Expand All @@ -181,7 +186,7 @@ public:
std::equal(data, data + getLength(), rhs.data));
}

inline void
constexpr modm::can::Message&
operator = (const modm::can::Message& rhs)
{
this->identifier = rhs.identifier;
Expand All @@ -192,9 +197,10 @@ public:
this->flags.rtr = rhs.flags.rtr;
this->flags.extended = rhs.flags.extended;
std::copy(std::begin(rhs.data), std::end(rhs.data), std::begin(this->data));
return *this;
}

inline bool
constexpr bool
operator < (const modm::can::Message& rhs) const
{
return (this->identifier << (this->flags.extended ? 0 : 18))
Expand Down
6 changes: 6 additions & 0 deletions src/modm/architecture/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,18 @@ class Can(Module):
module.description = FileReader("interface/can.md")

def prepare(self, module, options):
def validate_dlc(s: int):
valid_dlcs = [8, 12, 16, 20, 24, 32, 48, 64]
if s not in valid_dlcs:
raise ValueError(f"Input must be a valid CAN data length: {', '.join(valid_dlcs)}!")
module.add_option(
NumericOption(
name="message.buffer",
description="",
minimum=8, maximum=64,
validate=validate_dlc,
default=8))
module.depends(":debug")
return True

def build(self, env):
Expand Down
31 changes: 29 additions & 2 deletions src/modm/board/samv71_xplained_ultra/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ struct SystemClock
static constexpr uint32_t Spi0 = Mck;
static constexpr uint32_t Twihs0 = Mck;
static constexpr uint32_t Dacc = Mck;

static constexpr uint32_t Pck5 = 80_MHz;
static constexpr uint32_t Mcan = Pck5;

// static constexpr uint32_t Usb = 48_MHz;

static bool inline
Expand All @@ -47,12 +51,22 @@ struct SystemClock
ClockGen::selectMasterClk<MasterClkSource::PLLA_CLK, MasterClkPrescaler::CLK_1, MasterClkDivider::Div2>();
ClockGen::updateCoreFrequency<Frequency>();

// Enable PMC bus-independent clock output PCK5 for MCAN
// Manual (48.4.2): "It is recommended to use the CAN clock at frequencies of 20, 40 or 80 MHz.
// To achieve these frequencies, PMC PCK5 must select the UPLLCK (480 MHz) as source clock and
// divide by 24, 12, or 6. PCK5 allows the system bus and processor clock to be modified
// without affecting the bit rate communication."
ClockGen::enableUPll<UtmiRefClk::Xtal12>();
ClockGen::disablePck(Pck::Pck5);
ClockGen::configurePck(Pck::Pck5, PckSource::UPll, 6);
ClockGen::enablePck(Pck::Pck5);

return true;
}
};

using Led0 = GpioA23;
using Led1 = GpioC9;
using Led0 = GpioInverted<GpioA23>;
using Led1 = GpioInverted<GpioC9>;
using ButtonSW0 = GpioInverted<GpioA9>;

using Leds = SoftwareGpioPort<Led1, Led0>;
Expand All @@ -70,6 +84,16 @@ using Scl = GpioA4;

using LoggerDevice = modm::IODeviceWrapper<Debug::Uart, modm::IOBuffer::BlockIfFull>;

namespace Can
{
/// @ingroup modm_board_samv71_xplained_ultra
/// @{
using Rx = GpioC12;
using Tx = GpioC14;
using Can = Mcan1;
/// @}
}

inline void
initialize()
{
Expand All @@ -85,8 +109,11 @@ initialize()
Debug::Uart::initialize<SystemClock, 115200>();
Debug::Uart::connect<Debug::UartTx::Tx, Debug::UartRx::Rx>();

Leds::reset();
Leds::setOutput();
ButtonSW0::setInput(InputType::PullUp);

Can::Can::connect<Can::Rx::Rx, Can::Tx::Tx>(InputType::PullUp);
}

/*
Expand Down
8 changes: 7 additions & 1 deletion src/modm/board/samv71_xplained_ultra/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ def prepare(module, options):
if not options[":target"].partname == "samv71q21b-aab":
return False

module.depends(":debug", ":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usart:1", ":platform:i2c:0") #, ":platform:usb")
module.depends(":debug",
":platform:can:1",
":platform:clockgen",
":platform:core",
":platform:gpio",
":platform:i2c:0",
":platform:usart:1") # , ":platform:usb")
return True

def build(env):
Expand Down
Loading