Skip to content

Commit

Permalink
Add SPI driver for samg family
Browse files Browse the repository at this point in the history
  • Loading branch information
mcbridejc committed Sep 10, 2021
1 parent 93bba13 commit f4d5d6c
Show file tree
Hide file tree
Showing 8 changed files with 410 additions and 9 deletions.
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

0 comments on commit f4d5d6c

Please sign in to comment.