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

Add SPI driver for samg family #680

Merged
merged 1 commit into from
Sep 10, 2021
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 @@ -360,7 +360,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
36 changes: 36 additions & 0 deletions examples/samg55_xplained_pro/spi-loopback/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <modm/board.hpp>

using namespace modm::platform;

int main() {
// Test SPI send and receive in loopback mode. If we receive the expected
// characters back, flash slowly. Otherwise, flash fast.
Board::initialize();

SpiMaster0::connect<GpioB0::Sck, GpioA9::Miso, GpioA10::Mosi>();
SpiMaster0::initialize<Board::SystemClock, 1_MHz>();

SpiMaster0::setLocalLoopback(true);

while (true)
{
uint32_t flash_time_ms;
uint8_t tx[] = {0xa5, 0x21};
uint8_t rx[2];

SpiMaster0::transferBlocking(tx, rx, 2);

if(rx[0] == 0xa5 && rx[1] == 0x21) {
flash_time_ms = 500;
} else {
flash_time_ms = 100;
}

for(uint32_t i=0; i<5; i++) {
Board::Led::set();
modm::delay_ms(flash_time_ms);
Board::Led::reset();
modm::delay_ms(flash_time_ms);
}
}
}
11 changes: 11 additions & 0 deletions examples/samg55_xplained_pro/spi-loopback/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<library>
<extends>modm:samg55-xplained-pro</extends>
<options>
<option name="modm:build:project.name">samg-spi-demo</option>
<option name="modm:build:build.path">../../../build/samg55_xplained_pro/spi-loopback</option>
</options>
<modules>
<module>modm:build:scons</module>
<module>modm:platform:spi:0</module>
</modules>
</library>
16 changes: 8 additions & 8 deletions src/modm/platform/clock/samg/clockgen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ enum class MainInternalFreq : uint32_t {
};

enum class ClockPeripheral : uint32_t {
FlexCom0 = ID_FLEXCOM0,
FlexCom1 = ID_FLEXCOM1,
FlexCom2 = ID_FLEXCOM2,
FlexCom3 = ID_FLEXCOM3,
FlexCom4 = ID_FLEXCOM4,
FlexCom5 = ID_FLEXCOM5,
FlexCom6 = ID_FLEXCOM6,
FlexCom7 = ID_FLEXCOM7,
Flexcom0 = ID_FLEXCOM0,
Flexcom1 = ID_FLEXCOM1,
Flexcom2 = ID_FLEXCOM2,
Flexcom3 = ID_FLEXCOM3,
Flexcom4 = ID_FLEXCOM4,
Flexcom5 = ID_FLEXCOM5,
Flexcom6 = ID_FLEXCOM6,
Flexcom7 = ID_FLEXCOM7,
PioA = ID_PIOA,
PioB = ID_PIOB,
Pdmic0 = ID_PDMIC0,
Expand Down
6 changes: 6 additions & 0 deletions src/modm/platform/gpio/sam/pin.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ enum class PeripheralPin
Dm,
Dp,
Wo,
Sck,
Miso,
Mosi
};

template<typename... Tuples>
Expand Down Expand Up @@ -418,6 +421,9 @@ public:
using ExtInt = As<PeripheralPin::ExtInt>;
using Dm = As<PeripheralPin::Dm>;
using Dp = As<PeripheralPin::Dp>;
using Sck = As<PeripheralPin::Sck>;
using Miso = As<PeripheralPin::Miso>;
using Mosi = As<PeripheralPin::Mosi>;

inline static bool
read()
Expand Down
69 changes: 69 additions & 0 deletions src/modm/platform/spi/sam/module.lb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021, Jeff McBride
#
# 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"]
driver = device.get_driver("spi")
properties = {}
properties["target"] = device.identifier
properties["features"] = driver["feature"] if "feature" in driver else []
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_master.hpp.in", "spi_master_{}.hpp".format(self.instance))
env.template("spi_master.cpp.in", "spi_master_{}.cpp".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:samg*"):
return False

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

for driver in device.get_all_drivers("spi:samg*"):
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"
131 changes: 131 additions & 0 deletions src/modm/platform/spi/sam/spi_master.cpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2009, Martin Rosekeit
* Copyright (c) 2009-2012, Fabian Greif
* Copyright (c) 2010, Georgi Grinshpun
* Copyright (c) 2012-2017, Niklas Hauser
* Copyright (c) 2013, Kevin Läufer
* Copyright (c) 2014, Sascha Schade
* Copyright (c) 2021, Jeff McBride
*
* 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 "spi_master_{{id}}.hpp"

// ----------------------------------------------------------------------------

uint8_t
modm::platform::SpiMaster{{ id }}::acquire(void *ctx, ConfigurationHandler handler)
{
if (context == nullptr)
{
context = ctx;
count = 1;
// if handler is not nullptr and is different from previous configuration
if (handler and configuration != handler) {
configuration = handler;
configuration();
}
return 1;
}

if (ctx == context)
return ++count;

return 0;
}

uint8_t
modm::platform::SpiMaster{{ id }}::release(void *ctx)
{
if (ctx == context)
{
if (--count == 0)
context = nullptr;
}
return count;
}
// ----------------------------------------------------------------------------

modm::ResumableResult<uint8_t>
modm::platform::SpiMaster{{ id }}::transfer(uint8_t data)
{
// this is a manually implemented "fast resumable function"
// there is no context or nesting protection, since we don't need it.
// there are only two states encoded into 1 bit (LSB of state):
// 1. waiting to start, and
// 2. waiting to finish.

// LSB != Bit0 ?
if ( !(state & Bit0) )
{
// wait for previous transfer to finish
if (!isTransmitDataRegisterEmpty())
return {modm::rf::Running};

// start transfer by copying data into register
write(data);

// set LSB = Bit0
state |= Bit0;
}

if (!isReceiveDataRegisterFull())
return {modm::rf::Running};

// transfer finished
state &= ~Bit0;
return {modm::rf::Stop, read()};
}

modm::ResumableResult<void>
modm::platform::SpiMaster{{ id }}::transfer(
const uint8_t * tx, uint8_t * rx, std::size_t length)
{
// this is a manually implemented "fast resumable function"
// there is no context or nesting protection, since we don't need it.
// there are only two states encoded into 1 bit (Bit1 of state):
// 1. initialize index, and
// 2. wait for 1-byte transfer to finish.

// we need to globally remember which byte we are currently transferring
static std::size_t index = 0;

// we are only interested in Bit1
switch(state & Bit1)
{
case 0:
// we will only visit this state once
state |= Bit1;

// initialize index and check range
index = 0;
while (index < length)
{
default:
{
// if tx == 0, we use a dummy byte 0x00
// else we copy it from the array
// call the resumable function
modm::ResumableResult<uint8_t> result = transfer(tx ? tx[index] : 0);

// if the resumable function is still running, so are we
if (result.getState() > modm::rf::NestingError)
return {modm::rf::Running};

// if rx != 0, we copy the result into the array
if (rx) rx[index] = result.getResult();
}
index++;
}

// clear the state
state &= ~Bit1;
return {modm::rf::Stop};
}
}
Loading