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

SAMx7x SPI driver #938

Merged
merged 5 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,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
7 changes: 7 additions & 0 deletions src/modm/board/samv71_xplained_ultra/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct SystemClock
static constexpr uint32_t Frequency = 300_MHz;
static constexpr uint32_t Mck = Frequency / 2; // 150 MHz max.
static constexpr uint32_t Usart1 = Mck;
static constexpr uint32_t Spi0 = Mck;
// static constexpr uint32_t Usb = 48_MHz;

static bool inline
Expand Down Expand Up @@ -63,6 +64,12 @@ struct Leds
Led1::setOutput();
}

static void setOutput(bool state)
{
Led0::setOutput(state);
Led1::setOutput(state);
}

static void write(uint32_t value)
{
Led0::set(value & 1);
Expand Down
3 changes: 2 additions & 1 deletion src/modm/platform/gpio/sam/pin.hpp.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2017-2018, Niklas Hauser
* Copyright (c) 2020, Erik Henriksson
* Copyright (c) 2023, Christopher Durand
*
* This file is part of the modm project.
*
Expand Down Expand Up @@ -221,8 +222,8 @@ public:
setPortReg(PIO_PPDDR_OFFSET); // disable pull down
setPortReg(PIO_PUDR_OFFSET); // disable pull up
} else if (type == InputType::PullDown) {
setPortReg(PIO_PPDER_OFFSET); // Enable pull down
setPortReg(PIO_PUDR_OFFSET); // disable pull up
setPortReg(PIO_PPDER_OFFSET); // enable pull down
} else {
setPortReg(PIO_PPDDR_OFFSET); // Disable pull down
setPortReg(PIO_PUER_OFFSET); // Enable pull up
Expand Down
77 changes: 77 additions & 0 deletions src/modm/platform/spi/sam_x7x/module.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016-2018, Niklas Hauser
# Copyright (c) 2017, Fabian Greif
# Copyright (c) 2023, Christopher Durand
#
# 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/.
# -----------------------------------------------------------------------------

def get_properties(env):
device = env[":target"]
properties = {}
properties["target"] = device.identifier
return properties

class Instance(Module):
def __init__(self, driver, instance):
self.instance = int(instance)
self.driver = driver

def init(self, module):
module.name = str(self.instance)
module.description = "Instance {}".format(self.instance)

def prepare(self, module, options):
module.depends(":platform:spi")
return True

def build(self, env):
properties = get_properties(env)
properties["id"] = self.instance

env.substitutions = properties
env.outbasepath = "modm/src/modm/platform/spi"

env.template("spi_hal.hpp.in", "spi_hal_{}.hpp".format(self.instance))
env.template("spi_hal_impl.hpp.in", "spi_hal_{}_impl.hpp".format(self.instance))
env.template("spi_master.hpp.in", "spi_master_{}.hpp".format(self.instance))
env.template("spi_master.cpp.in", "spi_master_{}.cpp".format(self.instance))
# TODO: DMA
# if env.has_module(":platform:dma"):
# env.template("spi_master_dma.hpp.in", "spi_master_{}_dma.hpp".format(self.instance))
# env.template("spi_master_dma_impl.hpp.in", "spi_master_{}_dma_impl.hpp".format(self.instance))


def init(module):
module.name = ":platform:spi"
module.description = "Serial Peripheral Interface (SPI)"

def prepare(module, options):
device = options[":target"]
if not device.has_driver("spi:sam") or device.identifier.family != "e7x/s7x/v7x":
return False

module.depends(
":architecture:register",
":architecture:spi",
":cmsis:device",
":math:algorithm",
":platform:gpio")

for driver in device.get_all_drivers("spi:sam"):
for instance in driver["instance"]:
module.add_submodule(Instance(driver, instance))

return True

def build(env):
env.substitutions = get_properties(env)
env.outbasepath = "modm/src/modm/platform/spi"

env.template("spi_base.hpp.in")
110 changes: 110 additions & 0 deletions src/modm/platform/spi/sam_x7x/spi_base.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) 2023, Christopher Durand
*
* 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/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_SAMX7X_SPI_BASE_HPP
#define MODM_SAMX7X_SPI_BASE_HPP

#include <cstdint>
#include "../device.hpp"
#include <modm/architecture/interface/register.hpp>

namespace modm::platform
{

/**
* Base class for SPI classes
*
* Provides common enums that do not depend on the specific SPI instance type.
*
* @author Christopher Durand
* @ingroup modm_platform_spi
*/
class SpiBase
{
public:
enum class
Interrupt : uint32_t
{
RxRegisterFull = SPI_IMR_RDRF,
TxRegisterEmpty = SPI_IMR_TDRE,
ModeFaultError = SPI_IMR_MODF,
OverrunError = SPI_IMR_OVRES,
NssRising = SPI_IMR_NSSR,
TransferFinished = SPI_IMR_TXEMPTY,
UnderrunError = SPI_IMR_UNDES
};
MODM_FLAGS32(Interrupt);

enum class
StatusFlag : uint32_t
{
RxRegisterFull = SPI_SR_RDRF,
TxRegisterEmpty = SPI_SR_TDRE,
ModeFaultError = SPI_SR_MODF,
OverrunError = SPI_SR_OVRES,
NssRising = SPI_SR_NSSR,
TransferFinished = SPI_SR_TXEMPTY,
UnderrunError = SPI_SR_UNDES,
// SFERR is defined in the datasheet but not in any device header?
// SlaveFrameError = SPI_SR_SFERR,
EnableStatus = SPI_SR_SPIENS
};
MODM_FLAGS32(StatusFlag);

enum class
MasterSelection : uint32_t
{
Slave = 0,
Master = SPI_MR_MSTR,

Mask = SPI_MR_MSTR_Msk
};

enum class
LocalLoopback : uint32_t
{
Disabled = 0,
Enabled = SPI_MR_LLB,

Mask = SPI_MR_LLB_Msk
};

enum class
DataMode : uint32_t
{
Mode0 = SPI_CSR_NCPHA, ///< clock normal, sample on rising edge
Mode1 = 0, ///< clock normal, sample on falling edge
Mode2 = SPI_CSR_CPOL | SPI_CSR_NCPHA, ///< clock inverted, sample on falling edge
Mode3 = SPI_CSR_CPOL, ///< clock inverted, sample on rising edge

Mask = SPI_CSR_CPOL_Msk | SPI_CSR_NCPHA_Msk
};

enum class
DataSize : uint32_t
{
Bit8 = SPI_CSR_BITS_8_BIT,
Bit9 = SPI_CSR_BITS_9_BIT,
Bit10 = SPI_CSR_BITS_10_BIT,
Bit11 = SPI_CSR_BITS_11_BIT,
Bit12 = SPI_CSR_BITS_12_BIT,
Bit13 = SPI_CSR_BITS_13_BIT,
Bit14 = SPI_CSR_BITS_14_BIT,
Bit15 = SPI_CSR_BITS_15_BIT,
Bit16 = SPI_CSR_BITS_16_BIT,

Mask = SPI_CSR_BITS_Msk
};
};

} // namespace modm::platform

#endif // MODM_SAMX7X_SPI_BASE_HPP
120 changes: 120 additions & 0 deletions src/modm/platform/spi/sam_x7x/spi_hal.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (c) 2023, Christopher Durand
*
* 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/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_SAMX7X_SPI_HAL{{ id }}_HPP
#define MODM_SAMX7X_SPI_HAL{{ id }}_HPP

#include <cstddef>
#include <cstdint>
#include "spi_base.hpp"

namespace modm::platform
{

/**
* Serial peripheral interface (SPI{{ id }})
*
* Low-level SPI peripheral register access API
*
* @author Christopher Durand
* @ingroup modm_platform_spi modm_platform_spi_{{id}}
*/
class SpiHal{{ id }} : public SpiBase
{
public:
/// Enable the clock, reset and enable the peripheral
static void
enable();

/// Disable the peripheral clock
static void
disable();

/**
* Enable and initialize SPI in master mode
*
* \param prescaler clock divider (1..255), value 0 is not allowed
* \todo Support for slave mode and hardware-managed chip select
*/
static void
initialize(uint8_t prescaler,
DataMode dataMode = DataMode::Mode0,
DataSize dataSize = DataSize::Bit8);

static void
setDataMode(DataMode dataMode);

static void
setDataSize(DataSize dataSize);

static void
setMasterSelection(MasterSelection masterSelection);

static void
setLoopbackMode(LocalLoopback loopback);

/**
* Write up to 8 bits to the data register
*
* \pre Transmit data register must be empty
*/
static void
write(std::byte data);
salkinium marked this conversation as resolved.
Show resolved Hide resolved

/**
* Write up to 16 bits to the data register
*
* \pre Transmit data register must be empty
*/
static void
write(uint16_t data);

/**
* Read byte value from the receive data register
*
* \pre the receive data register must contain a valid value
*/
static void
read(std::byte& data);

/**
* Read 16-bit value from the receive data register
*
* \pre the receive data register must contain a valid value
*/
static void
read(uint16_t& data);

static void
enableInterruptVector(bool enable, uint32_t priority);

static void
enableInterrupt(Interrupt_t interrupt);

static void
disableInterrupt(Interrupt_t interrupt);

/// Read status flags
/// Clears all flags except rx data full, tx data empty and spi enable
static StatusFlag_t
readStatusFlags();

private:
// workaround for vendor macros using "Spi*" which is erroneously resolved to "modm::Spi*"
static auto
regs() { return reinterpret_cast<::Spi*>(SPI{{ id }}); }
};

} // namespace modm::platform

#include "spi_hal_{{ id }}_impl.hpp"

#endif // MODM_SAMX7X_SPI_HAL{{ id }}_HPP
Loading