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

Support USB device on samg family #679

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 @@ -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