Skip to content

Commit

Permalink
[sam] Add SAMx7x U(S)ART support, fix data reception
Browse files Browse the repository at this point in the history
SAMx7x devices feature both UART and USART peripherals with the same id.
The driver will provide `:platform:uart:*` modules for UART instances
and `:platform:usart:*` modules for USART instances.

Fixes receiving data by enabling the RX interrupt.

**WARNING**, this is a breaking change.

For the sake of consistency, also SAMG devices, which have had a USART
peripheral provided as `UartX` with module `:platform:uart:*`, will use
the USART naming.

Furthermore, the clock source has to be specified as `U(s)art*` in
`SystemClock` to allow to configure it to sources different to `Mclk`.
  • Loading branch information
chris-durand committed Feb 9, 2022
1 parent 5c174b2 commit a5716d3
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 62 deletions.
3 changes: 2 additions & 1 deletion src/modm/board/samg55_xplained_pro/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct SystemClock
static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz; // CPU core frequency
static constexpr uint32_t Usb = PllBMult * SlowClkFreqHz;
static constexpr uint32_t Mck = Frequency; // Master clock, used by most peripherals
static constexpr uint32_t Usart7 = Mck;

static bool inline
enable()
Expand All @@ -52,7 +53,7 @@ struct SystemClock

using Led = GpioA6;
using Button = GpioA2;
using DebugUart = Uart7;
using DebugUart = Usart7;
using TxPin = GpioA28;
using RxPin = GpioA27;

Expand Down
4 changes: 2 additions & 2 deletions src/modm/board/samg55_xplained_pro/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def prepare(module, options):

module.depends(
":platform:clockgen",
":platform:uart:7",
":platform:usart:7",
":platform:gpio",
":platform:core",
":platform:usb",
Expand All @@ -31,4 +31,4 @@ def build(env):
env.outbasepath = "modm/src/modm/board"
env.copy('board.hpp')

# TODO: openocd config?
# TODO: openocd config?
56 changes: 31 additions & 25 deletions src/modm/platform/uart/sam/uart.cpp.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Jeff McBride
* Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
Expand All @@ -9,30 +10,33 @@
*/
// ----------------------------------------------------------------------------

#include "uart_{{ id }}.hpp"
#include "{{ type }}_{{ id }}.hpp"

#include <modm/architecture/driver/atomic/queue.hpp>

%% set name="{}{}".format(type.capitalize(), id)
%% set reg=name.upper()

namespace
{
static modm::atomic::Queue<uint8_t, {{ options["buffer.rx"] }}> rxBuffer;
static modm::atomic::Queue<uint8_t, {{ options["buffer.tx"] }}> txBuffer;
}

MODM_ISR(FLEXCOM{{ id }})
MODM_ISR({{ peripheral | upper }}{{ id }})
{
using namespace modm::platform;
if(Uart{{ id }}::isReceiveReady()) {
uint8_t data = (uint8_t)USART{{ id }}->US_RHR;
Uart{{ id }}::read(data);
if({{ name }}::isReceiveReady()) {
uint8_t data = (uint8_t){{ name }}::Regs()->{{ prefix }}_RHR;
{{ name }}::read(data);
rxBuffer.push(data);
}

if(Uart{{ id }}::isTransmitReady()) {
if({{ name }}::isTransmitReady()) {
if(txBuffer.isEmpty()) {
USART{{ id }}->US_IDR = US_IDR_TXRDY;
{{ name }}::Regs()->{{ prefix }}_IDR = {{ prefix }}_IDR_TXRDY;
} else {
USART{{ id }}->US_THR = txBuffer.get();
{{ name }}::Regs()->{{ prefix }}_THR = txBuffer.get();
txBuffer.pop();
}
}
Expand All @@ -42,7 +46,7 @@ namespace modm::platform
{

bool
Uart{{ id }}::read(uint8_t &dataOut) {
{{ name }}::read(uint8_t &dataOut) {
if(rxBuffer.isEmpty()) {
return false;
} else {
Expand All @@ -53,7 +57,7 @@ Uart{{ id }}::read(uint8_t &dataOut) {
}

std::size_t
Uart{{ id }}::read(uint8_t *data, std::size_t length) {
{{ name }}::read(uint8_t *data, std::size_t length) {
uint32_t i = 0;
for(; i < length; i++) {
if(rxBuffer.isEmpty()) {
Expand All @@ -67,22 +71,22 @@ Uart{{ id }}::read(uint8_t *data, std::size_t length) {
}

bool
Uart{{ id }}::write(uint8_t data)
{{ name }}::write(uint8_t data)
{
if(txBuffer.isEmpty() && isTransmitReady()) {
USART{{ id }}->US_THR = data;
Regs()->{{ prefix }}_THR = data;
} else {
if(!txBuffer.push(data)) {
return false;
}
// Enable tx interrupt
USART{{ id }}->US_IER = US_IER_TXRDY;
Regs()->{{ prefix }}_IER = {{ prefix }}_IER_TXRDY;
}
return true;
}

std::size_t
Uart{{ id }}::write(const uint8_t *data, std::size_t length)
{{ name }}::write(const uint8_t *data, std::size_t length)
{
uint32_t i = 0;
for(; i < length; i++) {
Expand All @@ -94,34 +98,36 @@ Uart{{ id }}::write(const uint8_t *data, std::size_t length)
}

bool
Uart{{ id }}::isWriteFinished()
{{ name }}::isWriteFinished()
{
return txBuffer.isEmpty() && isTransmitReady();
}

void
Uart{{ id }}::flushWriteBuffer()
{{ name }}::flushWriteBuffer()
{
while(!isWriteFinished());
}

void
Uart{{ id }}::setParity(Parity parity)
{{ name }}::setParity(Parity parity)
{
USART{{ id }}->US_MR = (USART{{ id }}->US_MR & ~US_MR_PAR_Msk) | (uint32_t)parity;
Regs()->{{ prefix }}_MR = (Regs()->{{ prefix }}_MR & ~{{ prefix }}_MR_PAR_Msk) | (uint32_t)parity;
}

%% if type == "usart"
void
Uart{{ id }}::setWordLength(WordLength length)
{{ name }}::setWordLength(WordLength length)
{
if(length == WordLength::Bit9) {
USART{{ id }}->US_MR |= US_MR_MODE9;
Regs()->{{ prefix }}_MR |= {{ prefix }}_MR_MODE9;
} else {
USART{{ id }}->US_MR &= ~US_MR_MODE9;
USART{{ id }}->US_MR =
(USART{{ id }}->US_MR & ~US_MR_CHRL_Msk)
| US_MR_CHRL((uint32_t)length);
Regs()->{{ prefix }}_MR &= ~{{ prefix }}_MR_MODE9;
Regs()->{{ prefix }}_MR =
(Regs()->{{ prefix }}_MR & ~{{ prefix }}_MR_CHRL_Msk)
| {{ prefix }}_MR_CHRL((uint32_t)length);
}
}
%% endif

} // namespace modm::platform
} // namespace modm::platform
74 changes: 57 additions & 17 deletions src/modm/platform/uart/sam/uart.hpp.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Jeff McBride
* Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
Expand All @@ -11,23 +12,39 @@

#pragma once

#include "uart_base.hpp"
#include "{{ type }}_base.hpp"
#include <modm/architecture/interface/uart.hpp>
#include <modm/math/algorithm/prescaler.hpp>
#include <modm/platform/gpio/connector.hpp>
#include <modm/platform/clock/clockgen.hpp>

%% set name="{}{}".format(type.capitalize(), id)
%% set reg=name.upper()

namespace modm::platform
{

/**
%% if type == "usart"
* Universal synchronous asynchronous receiver transmitter (USART{{ id }})
%% else
* Universal asynchronous receiver transmitter (UART{{ id }})
%% endif
*
* @author Jeff McBride
* @ingroup modm_platform_uart modm_platform_uart_{{id}}
* @author Christopher Durand
* @ingroup modm_platform_{{type}} modm_platform_{{type}}_{{id}}
*/
class Uart{{ id }} : public UartBase, public modm::Uart
class {{ name }} : public {{ type }}Base, public modm::Uart
{
private:
// prevent name collision between ::Uart* from SAM header with modm::Uart
%% if type == "uart"
static auto* Regs() { return reinterpret_cast<::Uart*>{{ reg }}; }
%% else
static auto* Regs() { return {{ reg }}; }
%% endif
friend void ::{{ peripheral | upper }}{{ id }}_IRQHandler();
public:
static constexpr size_t RxBufferSize = {{ options["buffer.rx"] }};
static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }};
Expand All @@ -41,9 +58,17 @@ public:
static_assert(
!std::is_same_v<typename RxPin::Base, typename TxPin::Base>,
"Rx and Tx cannot use the same pin!");
using Flexcom = Peripherals::Flexcom<{{ id | int }}>;
using RxConnector = typename RxPin::template Connector<Flexcom, Flexcom::Rxd>;
using TxConnector = typename TxPin::template Connector<Flexcom, Flexcom::Txd>;
using Peripheral = Peripherals::{{ peripheral }}<{{ id | int }}>;
%% if peripheral == "Flexcom"
using RxConnector = typename RxPin::template Connector<Peripheral, Peripheral::Rxd>;
using TxConnector = typename TxPin::template Connector<Peripheral, Peripheral::Txd>;
%% elif type == "usart"
using RxConnector = typename RxPin::template Connector<Peripheral, Peripheral::Rxd<{{id}}>>;
using TxConnector = typename TxPin::template Connector<Peripheral, Peripheral::Txd<{{id}}>>;
%% elif type == "uart"
using RxConnector = typename RxPin::template Connector<Peripheral, Peripheral::Urxd<{{id}}>>;
using TxConnector = typename TxPin::template Connector<Peripheral, Peripheral::Utxd<{{id}}>>;
%% endif
RxConnector::connect();
TxConnector::connect();
}
Expand All @@ -53,33 +78,46 @@ public:
static inline void
initialize(
Parity parity=Parity::Disabled,
%% if type == "usart"
WordLength length=WordLength::Bit8,
%% endif
uint8_t irq_priority = 5)
{
ClockGen::enable<ClockPeripheral::Flexcom{{ id }}>();
ClockGen::enable<ClockPeripheral::{{ peripheral }}{{ id }}>();

%% if peripheral == "Flexcom"
FLEXCOM{{ id }}->FLEXCOM_MR = FLEXCOM_MR_OPMODE_USART;
%% endif
constexpr auto result = Prescaler::from_function(
SystemClock::Mck,
SystemClock::{{type | capitalize}}{{id}},
baudrate,
1,
65535,
%% if type == "usart"
[](uint32_t x) { return x * 8; }
%% else
[](uint32_t x) { return x * 16; }
%% endif
);

USART{{ id }}->US_BRGR = result.index;
Regs()->{{ prefix }}_BRGR = result.index;

%% if type == "usart"
// Use 8x oversampling (this affects baud rate generation)
USART{{ id }}->US_MR = US_MR_OVER;
Regs()->{{ prefix }}_MR = {{ prefix }}_MR_OVER;
%% endif

setParity(parity);
%% if type == "usart"
setWordLength(length);

USART{{ id }}->US_CR = US_CR_RXEN | US_CR_TXEN;
%% endif
Regs()->{{ prefix }}_CR = {{ prefix }}_CR_RXEN | {{ prefix }}_CR_TXEN;
// Enable rx interrupt
Regs()->{{ prefix }}_IER = {{ prefix }}_IER_RXRDY;

// Enable the IRQ

NVIC_SetPriority(FLEXCOM{{ id }}_IRQn, irq_priority);
NVIC_EnableIRQ(FLEXCOM{{ id }}_IRQn);
NVIC_SetPriority({{ peripheral | upper }}{{ id }}_IRQn, irq_priority);
NVIC_EnableIRQ({{ peripheral | upper }}{{ id }}_IRQn);
}

static bool read(uint8_t &dataOut);
Expand All @@ -96,11 +134,13 @@ public:

static void setParity(Parity parity);

%% if type == "usart"
static void setWordLength(WordLength length);
%% endif

static inline bool isTransmitReady() { return USART{{ id }}->US_CSR & US_CSR_TXRDY; }
static inline bool isTransmitReady() { return Regs()->{{ prefix }}_{{ sr }} & {{ prefix }}_{{ sr }}_TXRDY; }

static inline bool isReceiveReady() { return USART{{ id }}->US_CSR & US_CSR_RXRDY; }
static inline bool isReceiveReady() { return Regs()->{{ prefix }}_{{ sr }} & {{ prefix }}_{{ sr }}_RXRDY; }
};

} // namespace modm::platform
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------

props = {}
def _get_properties(env, instance=None):
device = env[":target"]
return {
"type" : "uart",
"peripheral" : "Uart",
"id" : instance,
"prefix" : "UART",
"sr" : "SR"
}


class Instance(Module):
def __init__(self, driver, instance):
Expand Down Expand Up @@ -40,24 +49,21 @@ class Instance(Module):
return True

def build(self, env):
device = env[":target"].identifier
global props
props["id"] = self.instance

env.substitutions = props
env.substitutions = _get_properties(env, self.instance)
env.outbasepath = "modm/src/modm/platform/uart"

env.template("uart.hpp.in", "uart_{}.hpp".format(self.instance))
env.template("uart.cpp.in", "uart_{}.cpp".format(self.instance))
env.template("../uart.hpp.in", "uart_{}.hpp".format(self.instance))
env.template("../uart.cpp.in", "uart_{}.cpp".format(self.instance))


def init(module):
module.name = ":platform:uart"
module.description = "Universal Synchronous Asynchronous Receiver Transmitter (UART)"
module.description = "Universal Asynchronous Receiver Transmitter (UART)"


def prepare(module, options):
device = options[":target"]
if not (device.has_driver("usart:samg*")):
if (not device.has_driver("uart:sam*")) or device.has_driver("sercom:sam"):
return False

module.depends(
Expand All @@ -67,17 +73,15 @@ def prepare(module, options):
":platform:gpio",
":platform:clockgen")

global props
drivers = options[":target"].get_all_drivers("usart")
drivers = options[":target"].get_all_drivers("uart")
for driver in drivers:
for instance in driver["instance"]:
module.add_submodule(Instance(driver, instance))

props["target"] = device.identifier
return True


def build(env):
global props
env.substitutions = props
env.substitutions = _get_properties(env)
env.outbasepath = "modm/src/modm/platform/uart"
env.copy("uart_base.hpp")
env.template("../uart_base.hpp.in", "uart_base.hpp")
Loading

0 comments on commit a5716d3

Please sign in to comment.