Skip to content

Commit

Permalink
Support USB on samg family
Browse files Browse the repository at this point in the history
- Currently suports device mode only, not host
- Adds example CDC app for samg55_xplained_pro board
  • Loading branch information
mcbridejc committed Sep 9, 2021
1 parent c148bf8 commit 93bba13
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,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
2 changes: 1 addition & 1 deletion examples/samg55_xplained_pro/blink/project.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<library>
<extends>modm:samg55-xplained-pro</extends>
<options>
<option name="modm:build:build.path">../../../build/samg/blink</option>
<option name="modm:build:build.path">../../../build/samg55_xplained_pro/blink</option>
</options>
<modules>
<module>modm:build:scons</module>
Expand Down
29 changes: 29 additions & 0 deletions examples/samg55_xplained_pro/usbserial/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <modm/board.hpp>
#include <modm/platform.hpp>

using namespace modm::platform;

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

// Pull DP low briefly on reset to make sure USB host disconnects/reconnects
MATRIX->CCFG_SYSIO |= (CCFG_SYSIO_SYSIO11 | CCFG_SYSIO_SYSIO10);
GpioA22::setOutput(false);
modm::delay_ms(5);
GpioA22::setInput();

Board::initializeUsbFs();

tusb_init();

while (true)
{
// Read any received data and echo it back
uint8_t buf[64];
uint32_t read_count;
tud_task();
read_count = UsbUart0::read(buf, sizeof(buf));
UsbUart0::write(buf, read_count);
Board::Led::toggle();
}
}
13 changes: 13 additions & 0 deletions examples/samg55_xplained_pro/usbserial/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<library>
<extends>modm:samg55-xplained-pro</extends>
<options>
<option name="modm:build:project.name">samg-demo</option>
<option name="modm:tinyusb:config">device.cdc</option>
<option name="modm:build:build.path">../../../build/samg55_xplained_pro/usbserial</option>
</options>
<modules>

<module>modm:tinyusb</module>
<module>modm:build:scons</module>
</modules>
</library>
14 changes: 11 additions & 3 deletions ext/hathach/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ def init(module):
def prepare(module, options):
if not (options[":target"].has_driver("usb") or
options[":target"].has_driver("usb_otg_fs") or
options[":target"].has_driver("usb_otg_hs")):
options[":target"].has_driver("usb_otg_hs") or
options[":target"].has_driver("udp") or
options[":target"].has_driver("uhp")):
return False

configs = {"device.cdc", "device.msc", "device.vendor", "device.midi", "device.dfu"}
Expand Down Expand Up @@ -153,11 +155,16 @@ def build(env):
raise ValidateException("Unknown ITF device class '{}'!".format(devclass))

endpoints[itf_prefix + "_OUT"] = hex(endpoint_counter)
# SAMG doesn't support using the same EP number for IN and OUT
if target.platform == "sam" and target.family in ["g"]:
endpoint_counter += 1
endpoints[itf_prefix + "_IN"] = hex(0x80 | endpoint_counter)

if target.platform == "stm32":
irq_data = env.query(":platform:usb:irqs")
irqs = irq_data["port_irqs"][speed]
elif target.platform == "sam" and target.family in ["g"]:
irqs = ["UDP"]
else:
irqs = ["USB"]

Expand Down Expand Up @@ -220,9 +227,10 @@ Configuration options:
"""

def prepare(self, module, options):
# Only OTG has Host Mode
# On STM32, only OTG has Host Mode
if not (options[":target"].has_driver("usb_otg_fs") or
options[":target"].has_driver("usb_otg_hs")):
options[":target"].has_driver("usb_otg_hs") or
options[":target"].has_driver("uhp:samg*")):
return False

paths = {p.parent for p in Path(localpath("tinyusb/src/class/")).glob("*/*_host.h")}
Expand Down
20 changes: 20 additions & 0 deletions src/modm/board/samg55_xplained_pro/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ using namespace modm::platform;

struct SystemClock
{
// Chosen to achieve 120MHz system clock
static constexpr uint32_t PllAMult = 3662;
// Chosen to achieve 48MHz USB clock
static constexpr uint32_t PllBMult = 1465;

static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz;
static constexpr uint32_t Usb = PllBMult * SlowClkFreqHz;
static bool inline
enable()
{
Expand All @@ -32,6 +37,15 @@ struct SystemClock
ClockGen::selectMasterClk<MasterClkSource::PLLA_CLK, MasterClkPrescaler::CLK_1>();
return true;
}

static bool inline
enableUsb()
{
ClockGen::enablePllB<PllBMult>();
// Use PLLB as USB clock source
PMC->PMC_USB = PMC_USB_USBS;
return true;
}
};

using Led = GpioA6;
Expand All @@ -51,5 +65,11 @@ initialize()
Button::setInput();
}

inline void initializeUsbFs()
{
SystemClock::enableUsb();
modm::platform::Usb::initialize<Board::SystemClock>();
}

} // namespace Board

2 changes: 1 addition & 1 deletion src/modm/board/samg55_xplained_pro/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def prepare(module, options):
if not options[":target"].partname == "samg55j19a-au":
return False

module.depends(":platform:clockgen", ":platform:gpio", ":platform:core");
module.depends(":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usb");
return True

def build(env):
Expand Down
40 changes: 40 additions & 0 deletions src/modm/platform/clock/samg/clockgen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ enum class MainInternalFreq : uint32_t {
Rc24Mhz
};

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,
PioA = ID_PIOA,
PioB = ID_PIOB,
Pdmic0 = ID_PDMIC0,
Pdmic1 = ID_PDMIC1,
Mem2Mem = ID_MEM2MEM,
I2sc0 = ID_I2SC0,
I2sc1 = ID_I2SC1,
Tc0 = ID_TC0_CHANNEL0,
Tc1 = ID_TC0_CHANNEL1,
Tc2 = ID_TC0_CHANNEL2,
Tc3 = ID_TC1_CHANNEL0,
Tc4 = ID_TC1_CHANNEL1,
Tc5 = ID_TC1_CHANNEL2,
Adc = ID_ADC,
Uhp = ID_UHP,
Udp = ID_UDP,
Crccu = ID_CRCCU
};

static constexpr uint32_t SlowClkFreqHz = 32'768;

/**
Expand Down Expand Up @@ -105,6 +133,18 @@ class ClockGen {

/** Returns the configured frequency of PLL B output */
static uint32_t pllBFrequency();

template< ClockPeripheral peripheral >
static void
enable();

template< ClockPeripheral peripheral >
static bool
isEnabled();

template< ClockPeripheral peripheral >
static void
disable();
};

} // namespace modm::platform
Expand Down
31 changes: 31 additions & 0 deletions src/modm/platform/clock/samg/clockgen_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,35 @@ void ClockGen::setFlashLatency() {
EFC->EEFC_FMR = (EFC->EEFC_FMR & ~EEFC_FMR_FWS_Msk) | EEFC_FMR_FWS(fws);
}

template< ClockPeripheral peripheral >
void ClockGen::enable() {
constexpr uint32_t id = (uint32_t)peripheral;
if constexpr (id < 32) {
PMC->PMC_PCER0 = (1<<id);
} else {
PMC->PMC_PCER1 = (1<<(id-32));
}
}

template< ClockPeripheral peripheral >
bool ClockGen::isEnabled() {
constexpr uint32_t id = (uint32_t)peripheral;
if constexpr (id < 32) {
return PMC->PMC_PCSR0 & (1<<id);
} else {
return PMC->PMC_PCSR1 & (1<<(id-32));
}
}

template< ClockPeripheral peripheral >
void ClockGen::disable() {
constexpr uint32_t id = (uint32_t)peripheral;
if constexpr (id < 32) {
PMC->PMC_PCDR0 = (1<<id);
} else {
PMC->PMC_PCDR1 = (1<<(id-32));
}
}


} // namespace modm::platform
13 changes: 11 additions & 2 deletions src/modm/platform/usb/sam/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def init(module):

def prepare(module, options):
device = options[":target"]
if not (device.has_driver("usb:sam*")):
if not (device.has_driver("usb:sam*") or
device.has_driver("udp:sam*")):
return False

module.depends(
Expand All @@ -27,9 +28,17 @@ def prepare(module, options):

return True

def validate(env):
if "samg" in str(env[":target"].identifier) and env.has_module(":tinyusb:host"):
raise ValidateException("USB HOST mode is not currently supported on SAMG family")

def build(env):
device = env[":target"]
properties = device.properties
properties["target"] = device.identifier
env.substitutions = properties
env.outbasepath = "modm/src/modm/platform/usb"
env.copy("usb.hpp")
env.template("usb.hpp.in")
if env.has_module(":tinyusb:device:cdc"):
env.copy(repopath("ext/hathach/uart.hpp"), "uart.hpp")

Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

#pragma once

#include <modm/math/tolerance.hpp>
#include <modm/platform/device.hpp>
#include <modm/platform/gpio/connector.hpp>


namespace modm::platform
{

Expand All @@ -25,13 +27,32 @@ class Usb
static void
initialize(uint8_t priority=3)
{
%% if target["family"] in ["g"]
// only the USB device mode is supported for now
static_assert(modm::Tolerance::isValueInTolerance(48_MHz, SystemClock::Usb, 0.25_pct),
"The USB clock frequency must be within 0.25\% of 48MHz!");
// Select DM/DP function for PA21/22
MATRIX->CCFG_SYSIO &= ~(CCFG_SYSIO_SYSIO11 | CCFG_SYSIO_SYSIO10);
// Put USB in device mode
MATRIX->CCFG_USBMR = CCFG_USBMR_USBMODE;
// Enable the 48MHz clock
PMC->PMC_SCER = PMC_SCER_UDP;
// Enable the CPU clock
ClockGen::enable<ClockPeripheral::Udp>();
NVIC_SetPriority(UDP_IRQn, priority);
%% else
static_assert(SystemClock::Frequency == 48_MHz, "Usb must have a 48MHz clock!");
PM->APBBMASK.reg |= PM_APBBMASK_USB;
PM->AHBMASK.reg |= PM_AHBMASK_USB;
GenericClockController::connect<ClockPeripheral::Usb>(ClockGenerator::System);
NVIC_SetPriority(USB_IRQn, priority);
%% endif
}

// SAMG does not provide a connect method for USB. It's not defined in the SVD,
// and the USB pins are fixed. Instead, calling Usb::initialize() switches the
// IO pin function to the USB controller.
%% if target["family"] not in ["g"]
template<class... Pins>
static void
connect()
Expand All @@ -46,6 +67,7 @@ class Usb
DpConnector::connect();
DmConnector::connect();
}
%% endif
};

} // namespace modm::platform

0 comments on commit 93bba13

Please sign in to comment.