1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu: Introduce Atmel xmega cpu

Add ATxmega common files and cpu definitions.

This works was originally developed by @Josar.  The 2018 version
were port to 2021 mainline.

This version changes original port to have only the atxmega CPU
definition. With that, all family can be accomodated.

Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
This commit is contained in:
Gerson Fernando Budke 2021-01-07 13:05:50 -03:00
parent d041199825
commit 1a88f0bad6
23 changed files with 2730 additions and 0 deletions

82
cpu/atxmega/Kconfig Normal file
View File

@ -0,0 +1,82 @@
# Copyright (c) 2020 HAW Hamburg
# Copyright (c) 2021 Gerson Fernando Budke
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#
config CPU_COMMON_ATXMEGA
bool
select CPU_ARCH_AVR8
select CPU_CORE_AVR
select HAS_CPU_ATXMEGA
select HAS_CPP
select HAS_PERIPH_CPUID
select HAS_PERIPH_GPIO
select HAS_PERIPH_GPIO_IRQ
select HAS_PERIPH_NVM
select HAS_PERIPH_PM
select HAS_PERIPH_TIMER
select HAS_PERIPH_TIMER_PERIODIC
select HAS_PERIPH_UART
config CPU_CORE_ATXMEGA_A1
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_A3
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_A4
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_B1
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_B3
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_C3
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_C4
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_D3
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_D4
bool
select CPU_COMMON_ATXMEGA
config CPU_CORE_ATXMEGA_E5
bool
select CPU_COMMON_ATXMEGA
config CPU
default "atxmega" if CPU_COMMON_ATXMEGA
source "$(RIOTCPU)/atxmega/Kconfig.XMEGAA"
source "$(RIOTCPU)/atxmega/Kconfig.XMEGAB"
source "$(RIOTCPU)/atxmega/Kconfig.XMEGAC"
source "$(RIOTCPU)/atxmega/Kconfig.XMEGAD"
source "$(RIOTCPU)/atxmega/Kconfig.XMEGAE"
## Declaration of specific features
config HAS_CPU_ATXMEGA
bool
config HAS_PERIPH_NVM
bool
help
Indicates that the Non Volatile Memory controller is present.
source "$(RIOTCPU)/avr8_common/Kconfig"

105
cpu/atxmega/Kconfig.XMEGAA Normal file
View File

@ -0,0 +1,105 @@
## CPU Models
# XMEGA - A1/A1U
config CPU_MODEL_XMEGA64A1
bool
select CPU_CORE_ATXMEGA_A1
config CPU_MODEL_XMEGA128A1
bool
select CPU_CORE_ATXMEGA_A1
config CPU_MODEL_XMEGA64A1U
bool
select CPU_CORE_ATXMEGA_A1
config CPU_MODEL_XMEGA128A1U
bool
select CPU_CORE_ATXMEGA_A1
# XMEGA - A3/A3U/A3BU
config CPU_MODEL_XMEGA64A3
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA128A3
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA192A3
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA256A3
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA64A3U
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA128A3U
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA192A3U
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA256A3U
bool
select CPU_CORE_ATXMEGA_A3
config CPU_MODEL_XMEGA256A3BU
bool
select CPU_CORE_ATXMEGA_A3
# XMEGA - A4/A4U
config CPU_MODEL_XMEGA16A4
bool
select CPU_CORE_ATXMEGA_A4
config CPU_MODEL_XMEGA32A4
bool
select CPU_CORE_ATXMEGA_A4
config CPU_MODEL_XMEGA16A4U
bool
select CPU_CORE_ATXMEGA_A4
config CPU_MODEL_XMEGA32A4U
bool
select CPU_CORE_ATXMEGA_A4
config CPU_MODEL_XMEGA64A4U
bool
select CPU_CORE_ATXMEGA_A4
config CPU_MODEL_XMEGA128A4U
bool
select CPU_CORE_ATXMEGA_A4
config CPU_MODEL
string
default "atxmega64a1" if CPU_MODEL_XMEGA64A1
default "atxmega128a1" if CPU_MODEL_XMEGA128A1
default "atxmega192a1" if CPU_MODEL_XMEGA192A1
default "atxmega256a1" if CPU_MODEL_XMEGA256A1
default "atxmega64a1u" if CPU_MODEL_XMEGA64A1U
default "atxmega128a1u" if CPU_MODEL_XMEGA128A1U
default "atxmega64a3" if CPU_MODEL_XMEGA64A3
default "atxmega128a3" if CPU_MODEL_XMEGA128A3
default "atxmega192a3" if CPU_MODEL_XMEGA192A3
default "atxmega256a3" if CPU_MODEL_XMEGA256A3
default "atxmega64a3u" if CPU_MODEL_XMEGA64A3U
default "atxmega128a3u" if CPU_MODEL_XMEGA128A3U
default "atxmega192a3u" if CPU_MODEL_XMEGA192A3U
default "atxmega256a3u" if CPU_MODEL_XMEGA256A3U
default "atxmega256a3bu" if CPU_MODEL_XMEGA256A3BU
default "atxmega16a4" if CPU_MODEL_XMEGA16A4
default "atxmega32a4" if CPU_MODEL_XMEGA32A4
default "atxmega16a4u" if CPU_MODEL_XMEGA16A4U
default "atxmega32a4u" if CPU_MODEL_XMEGA32A4U
default "atxmega64a4u" if CPU_MODEL_XMEGA64A4U
default "atxmega128a4u" if CPU_MODEL_XMEGA128A4U

View File

@ -0,0 +1,25 @@
## CPU Models
# XMEGA - B1
config CPU_MODEL_XMEGA64B1
bool
select CPU_CORE_ATXMEGA_B1
config CPU_MODEL_XMEGA128B1
bool
select CPU_CORE_ATXMEGA_B1
# XMEGA - B3
config CPU_MODEL_XMEGA64B3
bool
select CPU_CORE_ATXMEGA_B3
config CPU_MODEL_XMEGA128B3
bool
select CPU_CORE_ATXMEGA_B3
config CPU_MODEL
default "atxmega64b3" if CPU_MODEL_XMEGA64B3
default "atxmega128b3" if CPU_MODEL_XMEGA128B3
default "atxmega64b1" if CPU_MODEL_XMEGA64B1
default "atxmega128b1" if CPU_MODEL_XMEGA128B1

View File

@ -0,0 +1,45 @@
## CPU Models
# XMEGA - C3
config CPU_MODEL_XMEGA32C3
bool
select CPU_CORE_ATXMEGA_C3
config CPU_MODEL_XMEGA64C3
bool
select CPU_CORE_ATXMEGA_C3
config CPU_MODEL_XMEGC128C3
bool
select CPU_CORE_ATXMEGA_C3
config CPU_MODEL_XMEGC192C3
bool
select CPU_CORE_ATXMEGA_C3
config CPU_MODEL_XMEGA256C3
bool
select CPU_CORE_ATXMEGA_C3
config CPU_MODEL_XMEGA384C3
bool
select CPU_CORE_ATXMEGA_C3
# XMEGA - C4
config CPU_MODEL_XMEGA16C4
bool
select CPU_CORE_ATXMEGA_C4
config CPU_MODEL_XMEGA32C4
bool
select CPU_CORE_ATXMEGA_C4
config CPU_MODEL
default "atxmega32c3" if CPU_MODEL_XMEGA32C3
default "atxmega64c3" if CPU_MODEL_XMEGA64C3
default "atxmega128c3" if CPU_MODEL_XMEGC128C3
default "atxmega192c3" if CPU_MODEL_XMEGC192C3
default "atxmega256c3" if CPU_MODEL_XMEGA256C3
default "atxmega384c3" if CPU_MODEL_XMEGA384C3
default "atxmega16c4" if CPU_MODEL_XMEGA16C4
default "atxmega32c4" if CPU_MODEL_XMEGA32C4

View File

@ -0,0 +1,55 @@
## CPU Models
# XMEGA - D3
config CPU_MODEL_XMEGA32D3
bool
select CPU_CORE_ATXMEGA_D3
config CPU_MODEL_XMEGA64D3
bool
select CPU_CORE_ATXMEGA_D3
config CPU_MODEL_XMEGC128D3
bool
select CPU_CORE_ATXMEGA_D3
config CPU_MODEL_XMEGC192D3
bool
select CPU_CORE_ATXMEGA_D3
config CPU_MODEL_XMEGA256D3
bool
select CPU_CORE_ATXMEGA_D3
config CPU_MODEL_XMEGA384D3
bool
select CPU_CORE_ATXMEGA_D3
# XMEGA - D4
config CPU_MODEL_XMEGA16D4
bool
select CPU_CORE_ATXMEGA_D4
config CPU_MODEL_XMEGA32D4
bool
select CPU_CORE_ATXMEGA_D4
config CPU_MODEL_XMEGA64D4
bool
select CPU_CORE_ATXMEGA_D4
config CPU_MODEL_XMEGA128D4
bool
select CPU_CORE_ATXMEGA_D4
config CPU_MODEL
default "atxmega32d3" if CPU_MODEL_XMEGA32D3
default "atxmega64d3" if CPU_MODEL_XMEGA64D3
default "atxmega128d3" if CPU_MODEL_XMEGC128D3
default "atxmega192d3" if CPU_MODEL_XMEGC192D3
default "atxmega256d3" if CPU_MODEL_XMEGA256D3
default "atxmega384d3" if CPU_MODEL_XMEGA384D3
default "atxmega16d4" if CPU_MODEL_XMEGA16D4
default "atxmega32d4" if CPU_MODEL_XMEGA32D4
default "atxmega64d4" if CPU_MODEL_XMEGA64D4
default "atxmega128d4" if CPU_MODEL_XMEGA128D4

View File

@ -0,0 +1,17 @@
## CPU Models
config CPU_MODEL_XMEGA8E5
bool
select CPU_CORE_ATXMEGA_E5
config CPU_MODEL_XMEGA16E5
bool
select CPU_CORE_ATXMEGA_E5
config CPU_MODEL_XMEGA32E5
bool
select CPU_CORE_ATXMEGA_E5
config CPU_MODEL
default "atxmega8e5" if CPU_MODEL_XMEGA8E5
default "atxmega16e5" if CPU_MODEL_XMEGA16E5
default "atxmega32e5" if CPU_MODEL_XMEGA32E5

7
cpu/atxmega/Makefile Normal file
View File

@ -0,0 +1,7 @@
# define the module that is build
MODULE = cpu
# add a list of subdirectories, that should also be build
DIRS = periph $(RIOTCPU)/avr8_common/
include $(RIOTBASE)/Makefile.base

11
cpu/atxmega/Makefile.dep Normal file
View File

@ -0,0 +1,11 @@
# peripheral drivers are linked into the final binary
USEMODULE += atxmega_periph
# All ATxmega based CPUs provide PM
USEMODULE += pm_layered
ifeq (,$(filter cpuid,$(USEMODULE)))
USEMODULE += periph_nvm
endif
include $(RIOTCPU)/avr8_common/Makefile.dep

View File

@ -0,0 +1,11 @@
include $(RIOTCPU)/avr8_common/Makefile.features
# common feature are defined in avr8_common/Makefile.features
# Only add Additional features
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
FEATURES_PROVIDED += periph_nvm
FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += periph_timer periph_timer_periodic
FEATURES_PROVIDED += periph_uart

View File

@ -0,0 +1,38 @@
export CPU_ATXMEGA 1
# CPU ROM/RAM
ifneq (,$(findstring atxmega8,$(CPU_MODEL)))
RAM_LEN = 1K
ROM_LEN = 8K
endif
ifneq (,$(findstring atxmega16,$(CPU_MODEL)))
RAM_LEN = 2K
ROM_LEN = 16K
endif
ifneq (,$(findstring atxmega32,$(CPU_MODEL)))
RAM_LEN = 4K
ROM_LEN = 32K
endif
ifneq (,$(findstring atxmega64,$(CPU_MODEL)))
RAM_LEN ?= 4K
ROM_LEN = 64K
endif
#ifneq (,$(findstring atxmega128,$(CPU_MODEL)))
RAM_LEN ?= 8K
ROM_LEN = 128K
#endif
ifneq (,$(findstring atxmega192,$(CPU_MODEL)))
RAM_LEN = 16K
ROM_LEN = 192K
endif
ifneq (,$(findstring atxmega256,$(CPU_MODEL)))
RAM_LEN = 16K
ROM_LEN = 256K
endif
ifneq (,$(findstring atxmega384,$(CPU_MODEL)))
RAM_LEN = 32K
ROM_LEN = 384K
endif
# CPU depends on the avr8 common module, so include it
include $(RIOTCPU)/avr8_common/Makefile.include

132
cpu/atxmega/atxmega_cpu.c Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @{
*
* @file
* @brief Implementation of the CPU initialization
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
* @}
*/
#include <avr/pgmspace.h>
#include "cpu.h"
#include "cpu_clock.h"
#include "panic.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifndef CPU_ATXMEGA_CLK_SCALE_INIT
#define CPU_ATXMEGA_CLK_SCALE_INIT CPU_ATXMEGA_CLK_SCALE_DIV1
#endif
#ifndef CPU_ATXMEGA_BUS_SCALE_INIT
#define CPU_ATXMEGA_BUS_SCALE_INIT CPU_ATXMEGA_BUS_SCALE_DIV1_1
#endif
extern uint8_t mcusr_mirror;
void avr8_reset_cause(void)
{
if (mcusr_mirror & (1 << RST_PORF_bp)) {
DEBUG("Power-on reset.\n");
}
if (mcusr_mirror & (1 << RST_EXTRF_bp)) {
DEBUG("External reset!\n");
}
if (mcusr_mirror & (1 << RST_BORF_bp)) {
DEBUG("Brown-out reset!\n");
}
if (mcusr_mirror & (1 << RST_WDRF_bp)) {
DEBUG("Watchdog reset!\n");
}
if (mcusr_mirror & (1 << RST_PDIRF_bp)) {
DEBUG("Programming and Debug Interface reset!\n");
}
if (mcusr_mirror & (1 << RST_SRF_bp)) {
DEBUG("Software reset!\n");
}
if (mcusr_mirror & (1 << RST_SDRF_bp)) {
DEBUG("Spike Detection reset!\n");
}
}
void __attribute__((weak)) avr8_clk_init(void)
{
volatile uint8_t *reg = (uint8_t *)&PR.PRGEN;
uint8_t i;
/* Turn off all peripheral clocks that can be turned off. */
for (i = 0; i <= 7; i++) {
reg[i] = 0xff;
}
/* Turn on all peripheral clocks that can be turned on. */
for (i = 0; i <= 7; i++) {
reg[i] = 0x00;
}
/* XMEGA A3U [DATASHEET] p.23 After reset, the device starts up running
* from the 2MHz internal oscillator. The other clock sources, DFLLs
* and PLL, are turned off by default.
*
* Configure clock to 32MHz with calibration
* application note AVR1003
*
* From errata http://www.avrfreaks.net/forum/xmega-dfll-does-it-work
* In order to use the automatic runtime calibration for the 2 MHz or
* the 32 MHz internal oscillators, the DFLL for both oscillators and
* both oscillators has to be enabled for one to work.
*/
OSC.PLLCTRL = 0;
/* Enable the internal PLL & 32MHz & 32KHz oscillators */
OSC.CTRL |= OSC_PLLEN_bm | OSC_RC32MEN_bm | OSC_RC32KEN_bm;
/* Wait for 32Khz and 32MHz oscillator to stabilize */
while ((OSC.STATUS & (OSC_RC32KRDY_bm | OSC_RC32MRDY_bm))
!= (OSC_RC32KRDY_bm | OSC_RC32MRDY_bm)) {}
/* Enable DFLL - defaults to calibrate against internal 32Khz clock */
DFLLRC32M.CTRL = DFLL_ENABLE_bm;
/* Enable DFLL - defaults to calibrate against internal 32Khz clock */
DFLLRC2M.CTRL = DFLL_ENABLE_bm;
atxmega_set_prescaler(CPU_ATXMEGA_CLK_SCALE_INIT,
CPU_ATXMEGA_BUS_SCALE_INIT);
/* Disable CCP for Protected IO register and set new value*/
/* Switch to 32MHz clock */
_PROTECTED_WRITE(CLK.CTRL, CLK_SCLKSEL_RC32M_gc);
}
/* This is a vector which is aliased to __vector_default,
* the vector executed when an ISR fires with no accompanying
* ISR handler. This may be used along with the ISR() macro to
* create a catch-all for undefined but used ISRs for debugging
* purposes.
*/
ISR(BADISR_vect)
{
avr8_reset_cause();
#ifdef LED_PANIC
/* Use LED light to signal ERROR. */
LED_PANIC;
#endif
core_panic(PANIC_GENERAL_ERROR,
PSTR("FATAL ERROR: BADISR_vect called, unprocessed Interrupt.\n"
"STOP Execution.\n"));
}

10
cpu/atxmega/doc.txt Normal file
View File

@ -0,0 +1,10 @@
/**
* @defgroup cpu_atxmega Atmel ATxmega MCU
* @ingroup cpu
* @brief Implementation of Atmel's ATxmega MCU
*/
/**
* @defgroup cpu_atxmega_periph Atmel ATxmega MCU Peripherals
* @ingroup cpu_atxmega
*/

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @brief Common implementations and headers for ATxmega family based micro-controllers
* @{
*
* @file
* @brief Basic definitions for the ATxmega common clock module
*
* When ever you want to do something hardware related, that is accessing MCUs registers directly,
* just include this file. It will then make sure that the MCU specific headers are included.
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
*/
#ifndef CPU_CLOCK_H
#define CPU_CLOCK_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ATxmega system clock prescaler settings
*
* Some CPUs may not support the highest prescaler settings
*/
enum {
CPU_ATXMEGA_CLK_SCALE_DIV1 = 0,
CPU_ATXMEGA_CLK_SCALE_DIV2 = 1,
CPU_ATXMEGA_CLK_SCALE_DIV4 = 3,
CPU_ATXMEGA_CLK_SCALE_DIV8 = 5,
CPU_ATXMEGA_CLK_SCALE_DIV16 = 7,
CPU_ATXMEGA_CLK_SCALE_DIV32 = 9,
CPU_ATXMEGA_CLK_SCALE_DIV64 = 11,
CPU_ATXMEGA_CLK_SCALE_DIV128 = 13,
CPU_ATXMEGA_CLK_SCALE_DIV256 = 15,
CPU_ATXMEGA_CLK_SCALE_DIV512 = 17,
};
enum {
CPU_ATXMEGA_BUS_SCALE_DIV1_1 = 0,
CPU_ATXMEGA_BUS_SCALE_DIV1_2 = 1,
CPU_ATXMEGA_BUS_SCALE_DIV4_1 = 2,
CPU_ATXMEGA_BUS_SCALE_DIV2_2 = 3,
};
/**
* @brief Initializes system clock prescaler
*/
static inline void atxmega_set_prescaler(uint8_t clk_scale, uint8_t bus_scale)
{
/* Disable CCP for Protected IO register and set new value
* Set system clock prescalers to zero. PSCTRL contains A Prescaler
* Value and one value for and B and C Prescaler
*/
_PROTECTED_WRITE(CLK.PSCTRL, clk_scale | bus_scale);
}
#ifdef __cplusplus
}
#endif
#endif /* CPU_CLOCK_H */
/** @} */

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @{
*
* @file
* @brief Implementation specific CPU configuration options
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*/
#ifndef CPU_CONF_H
#define CPU_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
#define THREAD_EXTRA_STACKSIZE_PRINTF (128)
/**
* @name Kernel configuration
*
* Since printf seems to get memory allocated by the
* linker/avr-libc the stack size tested successfully
* even with pretty small stacks.
* @{
*/
#ifndef THREAD_STACKSIZE_DEFAULT
#define THREAD_STACKSIZE_DEFAULT (512)
#endif
/* keep THREAD_STACKSIZE_IDLE > THREAD_EXTRA_STACKSIZE_PRINTF
* to avoid not printing of debug in interrupts
*/
#ifndef THREAD_STACKSIZE_IDLE
#ifdef MODULE_XTIMER
/* xtimer's 64 bit arithmetic doesn't perform well on 8 bit archs. In order to
* prevent a stack overflow when an timer triggers while the idle thread is
* running, we have to increase the stack size then
*/
#define THREAD_STACKSIZE_IDLE (384)
#else
#define THREAD_STACKSIZE_IDLE (192)
#endif
#endif
/** @} */
/**
* @brief Declare the heap_stats function as available
*/
#define HAVE_HEAP_STATS
/**
* @brief This arch uses the inlined IRQ API.
*/
#define IRQ_API_INLINED (1)
/**
* @brief This arch require special clock initialization.
*/
#define CPU_AVR8_HAS_CLOCK_INIT 1
#ifdef __cplusplus
}
#endif
#endif /* CPU_CONF_H */
/** @} */

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @brief Non Volatile Memory (NVM) internal API
* @{
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
*/
#ifndef CPU_NVM_H
#define CPU_NVM_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Get offset of calibration bytes in the production signature row
*
* @note In some distributions may require most recent vendor headers, even
* more recent than the upstream avr-libc. The headers can be download
* directly from Microchip web site. In general, Microchip have a
* dedicated page at Tools and Software / AVR and SAM Downloads Archive.
* The most recent version is AVR 8-bit Toolchain (3.4.4) 6.2.0.334.
* http://ww1.microchip.com/downloads/archive/avr8-headers-6.2.0.334.zip
*
* The official RIOT-OS docker container, Debian and Ubuntu are
* distributions that already had updated versions of those files.
*
* @param regname Name of register within the production signature row
* @retval Offset of register into the production signature row
*/
#define nvm_get_production_signature_row_offset(regname) \
offsetof(NVM_PROD_SIGNATURES_t, regname)
/**
* @brief Read one byte from the production signature row
*
* This function reads one byte from the production signature row of the device
* at the given address.
*
* @note This function is modifying the NVM.CMD register.
* If the application are using program space access in interrupts
* (__flash pointers in IAR EW or pgm_read_byte in GCC) interrupts
* needs to be disabled when running EEPROM access functions. If not
* the program space reads will be corrupted.
*
* @param address Byte offset into the signature row
*/
uint8_t nvm_read_production_signature_row(uint8_t address);
#ifdef __cplusplus
}
#endif
#endif /* CPU_NVM_H */
/** @} */

View File

@ -0,0 +1,247 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @{
*
* @file
* @brief CPU specific definitions for internal peripheral handling
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*/
#ifndef PERIPH_CPU_H
#define PERIPH_CPU_H
#include <avr/io.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Length of the CPU_ID in octets
* @{
*/
#define CPUID_LEN (11U)
/** @} */
/**
* @name Interrupt level used to control nested interrupts
* @{
*/
typedef enum {
CPU_INT_LVL_OFF, /**< Interrupt Disabled */
CPU_INT_LVL_LOW, /**< Interrupt Low Level */
CPU_INT_LVL_MID, /**< Interrupt Medium Level */
CPU_INT_LVL_HIGH, /**< Interrupt High Level */
} cpu_int_lvl_t;
/** @} */
/**
* @brief Available ports on the ATxmega family
*/
enum {
PORT_A, /**< port A - 600 - 0 */
PORT_B, /**< port B - 620 - 1 */
PORT_C, /**< port C - 640 - 2 */
PORT_D, /**< port D - 660 - 3 */
PORT_E, /**< port E - 680 - 4 */
PORT_F, /**< port F - 6A0 - 5 */
PORT_G, /**< port G - 6C0 - 6 */
PORT_H, /**< port H - 6E0 - 7 */
PORT_J, /**< port J - 700 - 8 */
PORT_K, /**< port K - 720 - 9 */
PORT_L, /**< port L - 740 - A */
PORT_M, /**< port M - 760 - B */
PORT_N, /**< port N - 780 - C */
PORT_P, /**< port P - 7A0 - D */
PORT_Q, /**< port Q - 7C0 - E */
PORT_R, /**< port R - 7E0 - F */
/* ... */
PORT_MAX,
};
/**
* @name Power management configuration
* @{
*/
#define PM_NUM_MODES (4)
/** @} */
/**
* @brief Define the number of GPIO interrupts vectors for ATxmega CPU.
* @{
*/
#define GPIO_EXT_INT_NUMOF (2 * PORT_MAX)
/** @} */
/**
* @brief Override GPIO type
* @{
*/
#define HAVE_GPIO_T
typedef uint16_t gpio_t;
/** @} */
/**
* @brief Definition of a fitting UNDEF value
*/
#define GPIO_UNDEF (0xffff)
/**
* @brief Define a CPU specific GPIO pin generator macro
*
* The ATxmega internally uses pin mask to manipulate all gpio functions.
* This allows simultaneous pin actions at any method call. ATxmega specific
* applications can use ATXMEGA_GPIO_PIN macro to define pins and generic
* RIOT-OS application should continue to use GPIO_PIN API for compatibility.
*
* @{
*/
#define ATXMEGA_GPIO_PIN(x, y) (((x & 0x0f) << 8) | (y & 0xff))
#define GPIO_PIN(x, y) ATXMEGA_GPIO_PIN(x, (1U << (y & 0x07)))
/** @} */
/**
* @brief Available pin modes
*
* Generally, a pin can be configured to be input or output. In output mode, a
* pin can further be put into push-pull or open drain configuration. Though
* this is supported by most platforms, this is not always the case, so driver
* implementations may return an error code if a mode is not supported.
* @{
*/
#define HAVE_GPIO_MODE_T
typedef enum GPIO_MODE {
GPIO_SLEW_RATE = (1 << 7), /**< enable slew rate */
GPIO_INVERTED = (1 << 6), /**< enable inverted signal */
GPIO_OPC_TOTEN = (0 << 3), /**< select no pull resistor (TOTEM) */
GPIO_OPC_BSKPR = (1 << 3), /**< push-pull mode (BUSKEEPER) */
GPIO_OPC_PD = (2 << 3), /**< pull-down resistor */
GPIO_OPC_PU = (3 << 3), /**< pull-up resistor */
GPIO_OPC_WRD_OR = (4 << 3), /**< enable wired OR */
GPIO_OPC_WRD_AND = (5 << 3), /**< enable wired AND */
GPIO_OPC_WRD_OR_PULL = (6 << 3), /**< enable wired OR and pull-down resistor */
GPIO_OPC_WRD_AND_PULL = (7 << 3), /**< enable wired AND and pull-up resistor */
GPIO_ANALOG = (1 << 1), /**< select GPIO for analog function */
GPIO_IN = (0 << 0), /**< select GPIO MASK as input */
GPIO_OUT = (1 << 0), /**< select GPIO MASK as output */
/* Compatibility Mode */
GPIO_IN_PU = GPIO_IN | GPIO_OPC_PU,
GPIO_IN_PD = GPIO_IN | GPIO_OPC_PD,
GPIO_OD = GPIO_OUT | GPIO_OPC_WRD_OR,
GPIO_OD_PU = GPIO_OUT | GPIO_OPC_WRD_OR_PULL,
} gpio_mode_t;
/** @} */
/**
* @brief Definition of possible active flanks for external interrupt mode
* @{
*/
#define HAVE_GPIO_FLANK_T
typedef enum {
GPIO_ISC_BOTH = (0 << 4), /**< emit interrupt on both flanks (default) */
GPIO_ISC_RISING = (1 << 4), /**< emit interrupt on rising flank */
GPIO_ISC_FALLING = (2 << 4), /**< emit interrupt on falling flank */
GPIO_ISC_LOW_LEVEL = (3 << 4), /**< emit interrupt on low level */
GPIO_INT_DISABLED_ALL = (1 << 3), /**< disable all interrupts */
GPIO_INT0_VCT = (0 << 2), /**< enable interrupt on Vector 0 (default) */
GPIO_INT1_VCT = (1 << 2), /**< enable interrupt on Vector 1 */
GPIO_LVL_OFF = (0 << 0), /**< interrupt disabled (default) */
GPIO_LVL_LOW = (1 << 0), /**< interrupt low level */
GPIO_LVL_MID = (2 << 0), /**< interrupt medium level */
GPIO_LVL_HIGH = (3 << 0), /**< interrupt higher */
/* Compatibility Mode */
GPIO_FALLING = GPIO_ISC_FALLING | GPIO_LVL_LOW,
GPIO_RISING = GPIO_ISC_RISING | GPIO_LVL_LOW,
GPIO_BOTH = GPIO_ISC_BOTH | GPIO_LVL_LOW,
} gpio_flank_t;
/** @} */
/**
* @brief Max number of available UARTs
*/
#define UART_MAX_NUMOF (7)
/**
* @brief Size of the UART TX buffer for non-blocking mode.
*/
#ifndef UART_TXBUF_SIZE
#define UART_TXBUF_SIZE (64)
#endif
/**
* @brief UART device configuration
*/
typedef struct {
USART_t *dev; /**< pointer to the used UART device */
gpio_t rx_pin; /**< pin used for RX */
gpio_t tx_pin; /**< pin used for TX */
#ifdef MODULE_PERIPH_UART_HW_FC
gpio_t rts_pin; /**< RTS pin */
gpio_t cts_pin; /**< CTS pin */
#endif
cpu_int_lvl_t rx_int_lvl; /**< RX Complete Interrupt Level */
cpu_int_lvl_t tx_int_lvl; /**< TX Complete Interrupt Level */
cpu_int_lvl_t dre_int_lvl; /**< Data Registry Empty Interrupt Level */
} uart_conf_t;
/**
* @brief Max number of available timer channels
*/
#define TIMER_CH_MAX_NUMOF (4)
/**
* @brief A low-level timer_set() implementation is provided
*/
#define PERIPH_TIMER_PROVIDES_SET
/**
* @brief Timer Type
*
* Timer Type 1 is equal to Type 0 (two channels instead four)
* Timer Type 2 is Type 0 configured as two 8 bit timers instead one 16 bit
* Timer Type 2 won't be available as a standard timer
* Timer Type 5 is equal to Type 4 (two channels instead four)
*/
typedef enum {
TC_TYPE_0 = 0,
TC_TYPE_1 = 1,
TC_TYPE_2 = 2,
TC_TYPE_4 = 4,
TC_TYPE_5 = 5,
} timer_type_t;
/**
* @brief Timer device configuration
*
* All timers can be derived from TC0_t struct. Need check at runtime the
* type and number of channels to perform all operations.
*/
typedef struct {
TC0_t *dev; /**< Pointer to the used as Timer device */
timer_type_t type; /**< Timer Type */
cpu_int_lvl_t int_lvl[TIMER_CH_MAX_NUMOF]; /**< Interrupt channels level */
} timer_conf_t;
#ifdef __cplusplus
}
#endif
#endif /* PERIPH_CPU_H */
/** @} */

View File

@ -0,0 +1,3 @@
MODULE = atxmega_periph
include $(RIOTMAKE)/periph.mk

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level CPUID driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include "periph_cpu.h"
#include "cpu_nvm.h"
#define ENABLE_DEBUG 0
#include "debug.h"
void cpuid_get(void *id)
{
uint8_t *addr = id;
addr[0x0] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(LOTNUM0));
addr[0x1] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(LOTNUM1));
addr[0x2] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(LOTNUM2));
addr[0x3] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(LOTNUM3));
addr[0x4] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(LOTNUM4));
addr[0x5] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(LOTNUM5));
addr[0x6] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(WAFNUM));
addr[0x7] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(COORDX0));
addr[0x8] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(COORDX1));
addr[0x9] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(COORDY0));
addr[0xa] = nvm_read_production_signature_row(
nvm_get_production_signature_row_offset(COORDY1));
if (IS_ACTIVE(ENABLE_DEBUG)) {
DEBUG("CPUID: ");
for (uint8_t i = 0; i < CPUID_LEN; i++) {
DEBUG("%02x ", addr[i]);
}
DEBUG("\n");
}
}

546
cpu/atxmega/periph/gpio.c Normal file
View File

@ -0,0 +1,546 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level GPIO driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <stdio.h>
#include <avr/interrupt.h>
#include "cpu.h"
#include "periph_conf.h"
#include "periph/gpio.h"
#include "bitarithm.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief GPIO port base
*
* GPIO_PORT_BASE resides in the IO address space and must be 16 bits.
*/
#define GPIO_PORT_BASE ((uint16_t)&PORTA)
/**
* @brief GPIO port structure offset
*
* The PORT_t struct it is not complete filled and it is necessary define the
* address offset manually.
*/
#define GPIO_PORT_OFFSET (0x20)
static gpio_isr_ctx_t config_ctx[GPIO_EXT_INT_NUMOF];
static uint8_t config_irq[GPIO_EXT_INT_NUMOF];
/**
* @brief Extract the pin number of the given pin
*/
static inline uint8_t _pin_mask(gpio_t pin)
{
return (pin & 0xff);
}
/**
* @brief Extract the port number of the given pin
*/
static inline uint8_t _port_num(gpio_t pin)
{
return (pin >> 8) & 0x0f;
}
/**
* @brief Generate the PORTx address of the give pin in the IO address space
*/
static inline PORT_t *_port_addr(gpio_t pin)
{
uint8_t port_num = _port_num(pin);
uint16_t port_addr = GPIO_PORT_BASE + (port_num * GPIO_PORT_OFFSET);
return (PORT_t *) port_addr;
}
static inline void _print_config(gpio_t pin)
{
PORT_t *port = _port_addr(pin);
uint8_t pin_mask = _pin_mask(pin);
volatile uint8_t *pin_ctrl = &port->PIN0CTRL;
DEBUG("PORT: 0x%04x, PIN: 0x%02x\n", (uint16_t)port, pin_mask);
DEBUG("DIR: 0x%02x, IN: 0x%02x, OUT: 0x%02x\n", port->DIR, port->IN, port->OUT);
DEBUG("INTCTRL: 0x%02x\nINTFLAGS: 0x%02x\n", port->INTCTRL, port->INTFLAGS);
DEBUG("INT0MASK: 0x%02x\nINT1MASK: 0x%02x\n", port->INT0MASK, port->INT1MASK);
for (uint8_t p = 0; p < 8; p++) {
DEBUG("PIN%dCTRL: 0x%02x\n", p, pin_ctrl[p]);
}
}
static inline void _gpio_pinctrl_set(gpio_t pin, gpio_mode_t mode,
gpio_flank_t flank, gpio_cb_t cb,
void *arg)
{
uint8_t pin_mask = _pin_mask(pin);
uint8_t port_num = _port_num(pin);
PORT_t *port = _port_addr(pin);
volatile uint8_t *pin_ctrl = &port->PIN0CTRL;
uint8_t irq_state;
uint8_t in_sense_cfg;
uint8_t pins;
uint8_t pin_idx;
in_sense_cfg = (mode & ~PORT_ISC_gm);
in_sense_cfg |= (mode & GPIO_ANALOG)
? PORT_ISC_INPUT_DISABLE_gc
: ((flank >> 4) & PORT_ISC_gm);
pins = pin_mask;
pin_idx = 0;
if (mode & GPIO_OUT) {
port->DIRSET = pin_mask;
}
else {
port->DIRCLR = pin_mask;
}
irq_state = irq_disable();
while (pins) {
pins = bitarithm_test_and_clear(pins, &pin_idx);
pin_ctrl[pin_idx] = in_sense_cfg;
}
if (flank & GPIO_INT_DISABLED_ALL) {
config_ctx[port_num + PORT_MAX].cb = NULL;
config_ctx[port_num + PORT_MAX].arg = NULL;
config_irq[port_num + PORT_MAX] = 0;
config_ctx[port_num].cb = NULL;
config_ctx[port_num].arg = NULL;
config_irq[port_num] = 0;
port->INTCTRL = 0;
port->INT1MASK = 0;
port->INT0MASK = 0;
port->INTFLAGS = PORT_INT1IF_bm | PORT_INT0IF_bm;
}
else if (flank & GPIO_INT1_VCT) {
config_ctx[port_num + PORT_MAX].cb = cb;
config_ctx[port_num + PORT_MAX].arg = arg;
config_irq[port_num + PORT_MAX] = (flank & GPIO_LVL_HIGH)
<< PORT_INT1LVL_gp;
if ((flank & GPIO_LVL_HIGH) == GPIO_LVL_OFF) {
port->INT1MASK &= ~pin_mask;
}
else {
port->INT1MASK |= pin_mask;
}
port->INTFLAGS = PORT_INT1IF_bm;
/* Get mask from INT 0 and apply new INT 1 mask */
port->INTCTRL = (port->INTCTRL & PORT_INT0LVL_gm)
| config_irq[port_num + PORT_MAX];
}
else {
config_ctx[port_num].cb = cb;
config_ctx[port_num].arg = arg;
config_irq[port_num] = (flank & GPIO_LVL_HIGH)
<< PORT_INT0LVL_gp;
if ((flank & GPIO_LVL_HIGH) == GPIO_LVL_OFF) {
port->INT0MASK &= ~pin_mask;
}
else {
port->INT0MASK |= pin_mask;
}
port->INTFLAGS = PORT_INT0IF_bm;
/* Get mask from INT 1 and apply new INT 0 mask */
port->INTCTRL = (port->INTCTRL & PORT_INT1LVL_gm)
| config_irq[port_num];
}
irq_restore(irq_state);
}
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
DEBUG("gpio_init pin = 0x%02x mode = 0x%02x\n", pin, mode);
_gpio_pinctrl_set(pin, mode, GPIO_INT_DISABLED_ALL, NULL, NULL);
return 0;
}
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
DEBUG("gpio_init_int pin = 0x%02x mode = 0x%02x flank = 0x%02x\n", pin,
mode, flank);
if (mode & GPIO_ANALOG) {
DEBUG("Pin can't be ANALOG (input buffer disabled) and INT at same time.\n");
return -1;
}
_gpio_pinctrl_set(pin, mode, flank, cb, arg);
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
return 0;
}
void gpio_irq_enable(gpio_t pin)
{
DEBUG("gpio_irq_enable pin = 0x%04x \n", pin);
uint8_t pin_mask = _pin_mask(pin);
uint8_t port_num = _port_num(pin);
PORT_t *port = _port_addr(pin);
if (port->INT1MASK & pin_mask) {
port->INTFLAGS = PORT_INT1IF_bm;
port->INTCTRL |= config_irq[port_num + PORT_MAX];
}
if (port->INT0MASK & pin_mask) {
port->INTFLAGS = PORT_INT0IF_bm;
port->INTCTRL |= config_irq[port_num];
}
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
}
void gpio_irq_disable(gpio_t pin)
{
DEBUG("gpio_irq_disable pin = 0x%04x \n", pin);
uint8_t pin_mask = _pin_mask(pin);
PORT_t *port = _port_addr(pin);
if (port->INT1MASK & pin_mask) {
port->INTCTRL &= ~PORT_INT1LVL_gm;
}
if (port->INT0MASK & pin_mask) {
port->INTCTRL &= ~PORT_INT0LVL_gm;
}
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
}
int gpio_read(gpio_t pin)
{
PORT_t *port = _port_addr(pin);
uint8_t pin_mask = _pin_mask(pin);
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
return port->IN & pin_mask;
}
void gpio_set(gpio_t pin)
{
DEBUG("gpio_set pin = 0x%04x \n", pin);
PORT_t *port = _port_addr(pin);
uint8_t pin_mask = _pin_mask(pin);
port->OUTSET = pin_mask;
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
}
void gpio_clear(gpio_t pin)
{
DEBUG("gpio_clear pin = 0x%04x \n", pin);
PORT_t *port = _port_addr(pin);
uint8_t pin_mask = _pin_mask(pin);
port->OUTCLR = pin_mask;
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
}
void gpio_toggle(gpio_t pin)
{
DEBUG("gpio_toggle pin = 0x%04x \n", pin);
PORT_t *port = _port_addr(pin);
uint8_t pin_mask = _pin_mask(pin);
port->OUTTGL = pin_mask;
if (IS_ACTIVE(ENABLE_DEBUG)) {
_print_config(pin);
}
}
void gpio_write(gpio_t pin, int value)
{
DEBUG("gpio_write pin = 0x%04x, value = 0x%02x \n", pin, value);
if (value) {
gpio_set(pin);
}
else {
gpio_clear(pin);
}
}
static inline void irq_handler(uint8_t port_num, uint8_t isr_vct_num)
{
avr8_enter_isr();
DEBUG("irq_handler port = 0x%02x, vct_num = %d \n", port_num, isr_vct_num);
if (isr_vct_num) {
port_num += PORT_MAX;
}
if (config_ctx[port_num].cb) {
config_ctx[port_num].cb(config_ctx[port_num].arg);
}
else {
DEBUG("WARNING! irq_handler without callback\n");
}
avr8_exit_isr();
}
#if defined(PORTA_INT0_vect)
ISR(PORTA_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_A, 0);
}
#endif
#if defined(PORTA_INT1_vect)
ISR(PORTA_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_A, 1);
}
#endif
#if defined(PORTB_INT0_vect)
ISR(PORTB_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_B, 0);
}
#endif
#if defined(PORTB_INT1_vect)
ISR(PORTB_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_B, 1);
}
#endif
#if defined(PORTC_INT0_vect)
ISR(PORTC_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_C, 0);
}
#endif
#if defined(PORTC_INT1_vect)
ISR(PORTC_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_C, 1);
}
#endif
#if defined(PORTD_INT0_vect)
ISR(PORTD_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_D, 0);
}
#endif
#if defined(PORTD_INT1_vect)
ISR(PORTD_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_D, 1);
}
#endif
#if defined(PORTE_INT0_vect)
ISR(PORTE_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_E, 0);
}
#endif
#if defined(PORTE_INT1_vect)
ISR(PORTE_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_E, 1);
}
#endif
#if defined(PORTF_INT0_vect)
ISR(PORTF_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_F, 0);
}
#endif
#if defined(PORTF_INT1_vect)
ISR(PORTF_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_F, 1);
}
#endif
#if defined(PORTG_INT0_vect)
ISR(PORTG_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_G, 0);
}
#endif
#if defined(PORTG_INT1_vect)
ISR(PORTG_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_G, 1);
}
#endif
#if defined(PORTH_INT0_vect)
ISR(PORTH_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_H, 0);
}
#endif
#if defined(PORTH_INT1_vect)
ISR(PORTH_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_H, 1);
}
#endif
#if defined(PORTJ_INT0_vect)
ISR(PORTJ_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_J, 0);
}
#endif
#if defined(PORTJ_INT1_vect)
ISR(PORTJ_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_J, 1);
}
#endif
#if defined(PORTK_INT0_vect)
ISR(PORTK_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_K, 0);
}
#endif
#if defined(PORTK_INT1_vect)
ISR(PORTK_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_K, 1);
}
#endif
#if defined(PORTL_INT0_vect)
ISR(PORTL_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_L, 0);
}
#endif
#if defined(PORTL_INT1_vect)
ISR(PORTL_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_L, 1);
}
#endif
#if defined(PORTM_INT0_vect)
ISR(PORTM_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_M, 0);
}
#endif
#if defined(PORTM_INT1_vect)
ISR(PORTM_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_M, 1);
}
#endif
#if defined(PORTN_INT0_vect)
ISR(PORTN_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_N, 0);
}
#endif
#if defined(PORTN_INT1_vect)
ISR(PORTN_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_N, 1);
}
#endif
#if defined(PORTP_INT0_vect)
ISR(PORTP_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_P, 0);
}
#endif
#if defined(PORTP_INT1_vect)
ISR(PORTP_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_P, 1);
}
#endif
#if defined(PORTQ_INT0_vect)
ISR(PORTQ_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_Q, 0);
}
#endif
#if defined(PORTQ_INT1_vect)
ISR(PORTQ_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_Q, 1);
}
#endif
#if defined(PORTR_INT0_vect)
ISR(PORTR_INT0_vect, ISR_BLOCK)
{
irq_handler(PORT_R, 0);
}
#endif
#if defined(PORTR_INT1_vect)
ISR(PORTR_INT1_vect, ISR_BLOCK)
{
irq_handler(PORT_R, 1);
}
#endif

78
cpu/atxmega/periph/nvm.c Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level NVM driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <avr/io.h>
#include "cpu_nvm.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief Read one byte using the Load Program Memory (LPM) instruction
* @internal
*
* This function sets the specified NVM_CMD, reads one byte using at the
* specified byte address with the LPM instruction. NVM_CMD is restored after
* use.
*
* @note This will disable interrupts during execution.
*
* @param nvm_cmd NVM command to load before running LPM
* @param address Byte offset into the signature row
*/
static inline uint8_t _nvm_read_byte(uint8_t nvm_cmd, uint16_t address)
{
uint8_t result;
__asm__ volatile (
"in __tmp_reg__, __SREG__ \n\t"
"cli \n\t"
"lds __zero_reg__, %3 \n\t"
"sts %3, %1 \n\t"
"lpm %0, %a2 \n\t"
"sts %3, __zero_reg__ \n\t"
"clr __zero_reg__ \n\t"
"out __SREG__, __tmp_reg__ \n\t"
:
"=&r" (result)
:
"r" (nvm_cmd), "e" (address), "m" (NVM_CMD)
: /* no clobbers */
);
return result;
}
/**
* @brief Read one byte from the production signature row
*
* This function reads one byte from the production signature row of the device
* at the given address.
*
* @note The execution can take some time. It is recommended not call this
* inside an interrupt service routine.
*
* @param address Byte offset into the signature row
*/
uint8_t nvm_read_production_signature_row(uint8_t address)
{
return _nvm_read_byte(NVM_CMD_READ_CALIB_ROW_gc, address);
}

80
cpu/atxmega/periph/pm.c Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level PM driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <avr/sleep.h>
#include "periph_conf.h"
#include "periph/pm.h"
#define ENABLE_DEBUG 0
#include "debug.h"
void pm_reboot(void)
{
DEBUG("Reboot Software Reset\n" );
/* XMEGA AU [MANUAL] p. 116 CTRL->Control register
* page 13 3.12.1 Sequence for write operation to protected I/O registers
* page 15 3.14.1 CCP Configuration Change Protection register
*/
/* Disable CCP for Protected IO registerand set new value*/
_PROTECTED_WRITE(RST_CTRL, RST_SWRST_bm);
while (1) {}
}
/*
* DEBUG may affect this routine.
*
* --- Do NOT add DEBUG macro here ---
*
*/
void pm_set(unsigned mode)
{
unsigned irq_state = irq_disable();
if (avr8_is_uart_tx_pending() && mode < 4) {
irq_restore(irq_state);
return;
}
switch (mode) {
case 0:
set_sleep_mode(SLEEP_SMODE_PDOWN_gc);
break;
case 1:
set_sleep_mode(SLEEP_SMODE_PSAVE_gc);
break;
case 2:
set_sleep_mode(SLEEP_SMODE_STDBY_gc);
break;
case 3:
set_sleep_mode(SLEEP_SMODE_ESTDBY_gc);
break;
default:
set_sleep_mode(SLEEP_SMODE_IDLE_gc);
}
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
irq_restore(irq_state);
}

533
cpu/atxmega/periph/timer.c Normal file
View File

@ -0,0 +1,533 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level TIMER driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <avr/interrupt.h>
#include <assert.h>
#include "cpu.h"
#include "thread.h"
#include "periph/timer.h"
#include "board.h"
#include "periph_conf.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief We have 7 possible prescaler values
*/
#define PRESCALE_NUMOF (7U)
/**
* @brief Possible prescaler values, encoded as 2 ^ val
*/
static const uint8_t prescalers[] = { 0, 1, 2, 3, 6, 8, 10 };
/**
* @brief Timer state context
*/
typedef struct {
timer_cb_t cb; /**< interrupt callback */
void *arg; /**< interrupt callback argument */
uint8_t prescaler; /**< remember the prescaler value */
uint8_t channels; /**< number of channels */
} ctx_t;
/**
* @brief Allocate memory for saving the device states
* @{
*/
#ifdef TIMER_NUMOF
static ctx_t ctx[TIMER_NUMOF] = { { NULL } };
#else
/* fallback if no timer is configured */
static ctx_t *ctx[] = { { NULL } };
#endif
/** @} */
static uint32_t _oneshot;
static inline void set_oneshot(tim_t tim, int chan)
{
_oneshot |= (1 << chan) << (TIMER_CH_MAX_NUMOF * tim);
}
static inline void clear_oneshot(tim_t tim, int chan)
{
_oneshot &= ~((1 << chan) << (TIMER_CH_MAX_NUMOF * tim));
}
static inline bool is_oneshot(tim_t tim, int chan)
{
return _oneshot & ((1 << chan) << (TIMER_CH_MAX_NUMOF * tim));
}
/**
* @brief Setup the given timer
*/
int timer_init(tim_t tim, unsigned long freq, timer_cb_t cb, void *arg)
{
DEBUG("timer.c: freq = %ld, Core Clock = %ld\n", freq, CLOCK_CORECLOCK);
TC0_t *dev;
uint8_t pre;
uint8_t ch;
assert(TIMER_CH_MAX_NUMOF * TIMER_NUMOF < 32);
/* make sure given device is valid */
if (tim >= TIMER_NUMOF) {
return -1;
}
/* figure out if freq is applicable */
for (pre = 0; pre < PRESCALE_NUMOF; pre++) {
if ((CLOCK_CORECLOCK >> prescalers[pre]) == freq) {
break;
}
}
if (pre == PRESCALE_NUMOF) {
DEBUG("timer.c: prescaling failed!\n");
return -1;
}
dev = timer_config[tim].dev;
/* stop and reset timer */
dev->CTRLA = 0; /* Stop */
dev->CTRLFSET = TC_CMD_RESET_gc; /* Force Reset */
/* save interrupt context and timer Prescaler */
ctx[tim].cb = cb;
ctx[tim].arg = arg;
ctx[tim].prescaler = (0x07 & (pre + 1));
/* Check enabled channels */
ctx[tim].channels = 0;
for (ch = 0; ch < TIMER_CH_MAX_NUMOF; ch++) {
if (timer_config[tim].int_lvl[ch] != CPU_INT_LVL_OFF) {
ctx[tim].channels++;
}
}
if (timer_config[tim].type != TC_TYPE_0
&& timer_config[tim].type != TC_TYPE_4) {
if (ctx[tim].channels > 2) {
DEBUG("timer.c: wrong number of channels. max value is 2.\n");
return -1;
}
}
if (timer_config[tim].type == TC_TYPE_2) {
DEBUG("timer.c: Timer version %d is current not supported.\n",
timer_config[tim].type);
return -1;
}
/* Normal Counter with rollover */
dev->CTRLB = TC_WGMODE_NORMAL_gc;
/* Compare or Capture disable all channels */
dev->INTCTRLB = 0;
/* Free running counter */
dev->PER = 0xFFFF;
DEBUG("timer.c: prescaler set to %d \n", ctx[tim].prescaler);
dev->CTRLA = ctx[tim].prescaler;
return 0;
}
int timer_set_absolute(tim_t tim, int channel, unsigned int value)
{
if (tim >= TIMER_NUMOF) {
return -1;
}
if (channel >= ctx[tim].channels) {
return -1;
}
DEBUG("Setting timer %i channel %i to %04x\n", tim, channel, value);
TC0_t *dev = timer_config[tim].dev;
dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
dev->INTFLAGS |= TC0_CCAIF_bm << channel;
uint8_t irq_state = irq_disable();
*(((uint16_t *)(&dev->CCA)) + channel) = (uint16_t)value;
irq_restore(irq_state);
set_oneshot(tim, channel);
dev->INTCTRLB |= (timer_config[tim].int_lvl[channel] << (channel * 2));
return 0;
}
int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags)
{
(void)flags;
if (tim >= TIMER_NUMOF) {
return -1;
}
if (channel > 0 || ctx[tim].channels != 1) {
DEBUG("Only channel 0 can be set as periodic and channels must be 1\n");
return -1;
}
DEBUG("Setting timer %i channel 0 to %i and flags %i (repeating)\n",
tim, value, flags);
TC0_t *dev = timer_config[tim].dev;
uint8_t irq_state = irq_disable();
dev->CTRLA = 0;
dev->CTRLFSET = TC_CMD_RESET_gc;
dev->CTRLB = TC_WGMODE_FRQ_gc;
dev->INTCTRLB = 0;
dev->INTFLAGS |= TC0_CCAIF_bm;
dev->CCA = (uint16_t)value;
dev->INTCTRLB = timer_config[tim].int_lvl[0];
dev->CTRLA = ctx[tim].prescaler;
clear_oneshot(tim, channel);
irq_restore(irq_state);
return 0;
}
int timer_clear(tim_t tim, int channel)
{
if (tim >= TIMER_NUMOF) {
return -1;
}
if (channel >= ctx[tim].channels) {
return -1;
}
DEBUG("timer_clear channel %d\n", channel );
TC0_t *dev = timer_config[tim].dev;
/* Compare or Capture Disable */
dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
/* Clear Interrupt Flag
* The CCxIF is automatically cleared when the corresponding
* interrupt vector is executed.*/
dev->INTFLAGS |= TC0_CCAIF_bm << channel;
return 0;
}
int timer_set(tim_t tim, int channel, unsigned int timeout)
{
if (tim >= TIMER_NUMOF) {
return -1;
}
if (channel >= ctx[tim].channels) {
return -1;
}
TC0_t *dev = timer_config[tim].dev;
/* Compare or Capture Disable */
dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
/* Clear Interrupt Flag */
dev->INTFLAGS |= TC0_CCAIF_bm << channel;
uint8_t irq_state = irq_disable();
/* set value to compare with rollover */
uint16_t absolute = dev->CNT + timeout;
*(((uint16_t *)(&dev->CCA)) + channel) = absolute;
irq_restore(irq_state);
set_oneshot(tim, channel);
/* Compare or Capture Enable */
dev->INTCTRLB |= (timer_config[tim].int_lvl[channel] << (channel * 2));
return 0;
}
unsigned int timer_read(tim_t tim)
{
if (tim >= TIMER_NUMOF) {
return -1;
}
DEBUG("timer_read\n");
return (unsigned int)timer_config[tim].dev->CNT;
}
void timer_stop(tim_t tim)
{
if (tim >= TIMER_NUMOF) {
return;
}
DEBUG("timer_stop\n");
timer_config[tim].dev->CTRLA = 0;
timer_config[tim].dev->CTRLFSET = TC_CMD_RESTART_gc;
}
void timer_start(tim_t tim)
{
if (tim >= TIMER_NUMOF) {
return;
}
DEBUG("timer_start\n");
timer_config[tim].dev->CTRLA = ctx[tim].prescaler;
}
#ifdef TIMER_NUMOF
static inline void _isr(tim_t tim, int channel)
{
avr8_enter_isr();
DEBUG("timer %d _isr channel %d\n", tim, channel);
if (is_oneshot(tim, channel)) {
timer_config[tim].dev->INTCTRLB &= ~(TC0_CCAINTLVL_gm << (channel * 2));
}
if (ctx[tim].cb) {
ctx[tim].cb(ctx[tim].arg, channel);
}
avr8_exit_isr();
}
#endif
#ifdef TIMER_0_ISRA
ISR(TIMER_0_ISRA, ISR_BLOCK)
{
_isr(0, 0);
}
#endif
#ifdef TIMER_0_ISRB
ISR(TIMER_0_ISRB, ISR_BLOCK)
{
_isr(0, 1);
}
#endif
#ifdef TIMER_0_ISRC
ISR(TIMER_0_ISRC, ISR_BLOCK)
{
_isr(0, 2);
}
#endif
#ifdef TIMER_0_ISRD
ISR(TIMER_0_ISRD, ISR_BLOCK)
{
_isr(0, 3);
}
#endif /* TIMER_0 */
#ifdef TIMER_1_ISRA
ISR(TIMER_1_ISRA, ISR_BLOCK)
{
_isr(1, 0);
}
#endif
#ifdef TIMER_1_ISRB
ISR(TIMER_1_ISRB, ISR_BLOCK)
{
_isr(1, 1);
}
#endif
#ifdef TIMER_1_ISRC
ISR(TIMER_1_ISRC, ISR_BLOCK)
{
_isr(1, 2);
}
#endif
#ifdef TIMER_1_ISRD
ISR(TIMER_1_ISRD, ISR_BLOCK)
{
_isr(1, 3);
}
#endif /* TIMER_1 */
#ifdef TIMER_2_ISRA
ISR(TIMER_2_ISRA, ISR_BLOCK)
{
_isr(2, 0);
}
#endif
#ifdef TIMER_2_ISRB
ISR(TIMER_2_ISRB, ISR_BLOCK)
{
_isr(2, 1);
}
#endif
#ifdef TIMER_2_ISRC
ISR(TIMER_2_ISRC, ISR_BLOCK)
{
_isr(2, 2);
}
#endif
#ifdef TIMER_2_ISRD
ISR(TIMER_2_ISRD, ISR_BLOCK)
{
_isr(2, 3);
}
#endif /* TIMER_2 */
#ifdef TIMER_3_ISRA
ISR(TIMER_3_ISRA, ISR_BLOCK)
{
_isr(3, 0);
}
#endif
#ifdef TIMER_3_ISRB
ISR(TIMER_3_ISRB, ISR_BLOCK)
{
_isr(3, 1);
}
#endif
#ifdef TIMER_3_ISRC
ISR(TIMER_3_ISRC, ISR_BLOCK)
{
_isr(3, 2);
}
#endif
#ifdef TIMER_3_ISRD
ISR(TIMER_3_ISRD, ISR_BLOCK)
{
_isr(3, 3);
}
#endif /* TIMER_3 */
#ifdef TIMER_4_ISRA
ISR(TIMER_4_ISRA, ISR_BLOCK)
{
_isr(4, 0);
}
#endif
#ifdef TIMER_4_ISRB
ISR(TIMER_4_ISRB, ISR_BLOCK)
{
_isr(4, 1);
}
#endif
#ifdef TIMER_4_ISRC
ISR(TIMER_4_ISRC, ISR_BLOCK)
{
_isr(4, 2);
}
#endif
#ifdef TIMER_4_ISRD
ISR(TIMER_4_ISRD, ISR_BLOCK)
{
_isr(4, 3);
}
#endif /* TIMER_4 */
#ifdef TIMER_5_ISRA
ISR(TIMER_5_ISRA, ISR_BLOCK)
{
_isr(5, 0);
}
#endif
#ifdef TIMER_5_ISRB
ISR(TIMER_5_ISRB, ISR_BLOCK)
{
_isr(5, 1);
}
#endif
#ifdef TIMER_5_ISRC
ISR(TIMER_5_ISRC, ISR_BLOCK)
{
_isr(5, 2);
}
#endif
#ifdef TIMER_5_ISRD
ISR(TIMER_5_ISRD, ISR_BLOCK)
{
_isr(5, 3);
}
#endif /* TIMER_5 */
#ifdef TIMER_6_ISRA
ISR(TIMER_6_ISRA, ISR_BLOCK)
{
_isr(6, 0);
}
#endif
#ifdef TIMER_6_ISRB
ISR(TIMER_6_ISRB, ISR_BLOCK)
{
_isr(6, 1);
}
#endif
#ifdef TIMER_6_ISRC
ISR(TIMER_6_ISRC, ISR_BLOCK)
{
_isr(6, 2);
}
#endif
#ifdef TIMER_6_ISRD
ISR(TIMER_6_ISRD, ISR_BLOCK)
{
_isr(6, 3);
}
#endif /* TIMER_6 */
#ifdef TIMER_7_ISRA
ISR(TIMER_7_ISRA, ISR_BLOCK)
{
_isr(7, 0);
}
#endif
#ifdef TIMER_7_ISRB
ISR(TIMER_7_ISRB, ISR_BLOCK)
{
_isr(7, 1);
}
#endif
#ifdef TIMER_7_ISRC
ISR(TIMER_7_ISRC, ISR_BLOCK)
{
_isr(7, 2);
}
#endif
#ifdef TIMER_7_ISRB
ISR(TIMER_7_ISRD, ISR_BLOCK)
{
_isr(7, 3);
}
#endif /* TIMER_7 */

425
cpu/atxmega/periph/uart.c Normal file
View File

@ -0,0 +1,425 @@
/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level UART driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
*
* Supports runtime calculation of the BSEL and BSCALE register values.
* Supports reconfiguring UART after a clock change.
*
* @}
*/
#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include "board.h"
#include "cpu.h"
#include "sched.h"
#include "thread.h"
#include "periph/uart.h"
#include "periph/gpio.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief UART Baudrate Tolerance
*
* The tolerance is expressed as baud rate percentage multiplied by 100.
* For bigger tolerances UART double frequency can be avoided, thus saving
* power.
*
* The default baud tolerance is 2%.
*/
#ifndef BAUD_TOL
#define BAUD_TOL 2 * 100
#endif
/**
* @brief Allocate memory to store the callback functions.
*/
static uart_isr_ctx_t isr_ctx[UART_NUMOF];
/**
* @brief Get the pointer to the base register of the given USART device
*
* @param[in] dev USART device identifier
*
* @return base register address
*/
static inline USART_t *dev(uart_t dev)
{
return uart_config[dev].dev;
}
static inline int8_t _check_bsel(uint32_t baud, uint32_t calcbaud,
int16_t *precision)
{
/* Avoid negative values and with precision of two positions
* after the decimal point for the deviation in percent
*
* result is the absolute deviation eg. 10 001
*
* MAX BAUD fper/2 = 16MHz a shift of 8 can be used to increase
* accuracy
*/
uint16_t pre;
if (baud < calcbaud) {
pre = (uint16_t)((calcbaud * 10000ULL) / baud);
}
else {
pre = (uint16_t)((baud * 10000ULL) / calcbaud);
}
if (pre < ((uint16_t)*precision)) {
*precision = pre;
return 1;
}
return 0;
}
static inline int16_t _xmega_bsel_bscale(uint32_t *fper, uint32_t *baud,
uint8_t clk2x, uint16_t *bsel,
int8_t *bscale)
{
uint32_t calcbaud = 0;
uint32_t deno = 0;
int16_t precision = UINT_MAX;
uint16_t loc_bsel = 0;
int8_t loc_bscale;
/* Some explanation for equation transformation
* bsel = ( (fper / (2^bscale*16*baud) ) - 1 )
* = ( (fper / (2^bscale*(16-8*clk2x)*baud) ) - 1 )
* = ( fper - (2^bscale*(16-8*clk2x)*baud) )
* / (2^bscale*(16-8*clk2x)*baud)
*
* deno = ( baud * (16-8*clk2x) * 2^bscale )
* = ( baud * (1<<(4-clk2x)) * (1<<bscale) )
* = ( baud<<( 4-clk2x +bscale));
*
* some rounding math explanation for unsigned integer
* ABS(x +0,5) = (x*10+ 5 )/(10)
* Equation above is as follows, using denominator as base to round
* bsel = ( x - y )/y
* = ( x - y +0,5*y )/y
* = ( 2*x - y )/(2*y)
* = ((x<<1)- y )/(y<<1)
*
* baud = (fper*1/2^bscale) / ((bsel+1)*16)
*/
for (loc_bscale = 0; loc_bscale <= 7; loc_bscale++) {
int32_t sub = 0;
deno = (*baud << (4 - clk2x + loc_bscale));
sub = (*fper << 1) - (deno);
if (sub <= 0) {
break;
}
loc_bsel = (sub / (deno << 1));
if (loc_bsel >= 4095) {
continue;
}
/* Omit division by 16 get higher accuracy at small baudrates*/
calcbaud = (*fper >> loc_bscale) / ((loc_bsel + 1) << (4 - clk2x));
if (_check_bsel(*baud, calcbaud, &precision)) {
*bsel = loc_bsel;
*bscale = loc_bscale;
}
}
/* More math for the negative equation
* bscale is negative so 1/2^bscale = 2^|bscale| which is a factor and
* not a division again runding the result before division with
* 0.5 * denominator
*
* bsel = 1/2^bscale *( fcpu / ( (16*baud)-1) )
* = ( fcpu*2^|bscale| - (16*baud)*2^|bscale| )/(16*baud)
* = ( fcpu*2^|bscale| - (16*baud)*2^|bscale| + 0.5*(16*baud) )/(16*baud)
*
* deno = (16/2*baud) = (baud<<(3-clk2x))
*
* bsel = ( (fcpu - (deno<<(1))<<|bscale|) + deno )/(deno<<1)
*
* Baud = (fper*1/2^bscale)/ (16*(bsel+1/2^bscale))
*/
for (loc_bscale = -1; loc_bscale >= -7; loc_bscale--) {
uint32_t num = 0;
deno = (*baud << (3 - clk2x));
num = ((*fper - (deno << 1)) << (-loc_bscale)) + deno;
num = (num / (deno << 1));
if (num >= 4095) {
break;
}
loc_bsel = (uint16_t)(num & 0xFFF);
/* Omit division by 16 get higher accuracy at small baudrates */
calcbaud = (*fper << (-loc_bscale))
/ ((loc_bsel + (1 << (-loc_bscale))) << (4 - clk2x));
if (_check_bsel(*baud, calcbaud, &precision)) {
*bsel = loc_bsel;
*bscale = loc_bscale;
}
}
return precision;
}
/**
* @brief Calculates bsel and bscale for a given periphery clock and baudrate.
* Limitation are the periphery clock maximum is 32MHz, unsigned int
* overflows if clock is bigger. And the periphery clock has to be not
* smaller then 1 when divided by 128.
*
* fper/128 !=0 must be divide able by 128
* fper*128 != uint23_max => 32MHz max fper
*/
static inline int16_t _xmega_calculate_bsel_bscale(uint32_t fcpu, uint32_t baud,
uint8_t *clk2x,
uint16_t *bsel,
int8_t *bscale)
{
int16_t precision = 0;
precision = _xmega_bsel_bscale(&fcpu, &baud, 0, bsel, bscale );
/* default 2% precision, required precision is at least 2% */
if (precision <= (10000 + BAUD_TOL)) {
return (precision - 10000);
}
/* Precision goal was not met calculate baudrate with uart frequency doubled */
precision = _xmega_bsel_bscale(&fcpu, &baud, 1, bsel, bscale );
*clk2x = 1;
return (precision - 10000);
}
static inline void _configure_pins(uart_t uart)
{
/* configure RX pin */
if (gpio_is_valid(uart_config[uart].rx_pin)) {
gpio_init(uart_config[uart].rx_pin, GPIO_IN);
}
/* configure TX pin */
if (gpio_is_valid(uart_config[uart].tx_pin)) {
gpio_set(uart_config[uart].tx_pin);
gpio_init(uart_config[uart].tx_pin, GPIO_OUT);
}
#ifdef MODULE_PERIPH_UART_HW_FC
/* TODO */
#endif
}
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{
int8_t bscale;
uint8_t clk2x;
uint16_t bsel;
/* make sure the given device is valid */
if (uart >= UART_NUMOF) {
return UART_NODEV;
}
uint16_t count = UINT16_MAX;
while (avr8_is_uart_tx_pending() && count--) {}
/* register interrupt context */
isr_ctx[uart].rx_cb = rx_cb;
isr_ctx[uart].arg = arg;
/* disable and reset UART */
dev(uart)->CTRLA = 0;
dev(uart)->CTRLB = 0;
dev(uart)->CTRLC = 0;
_configure_pins(uart);
/* configure UART to 8N1 mode */
dev(uart)->CTRLC = USART_CMODE_ASYNCHRONOUS_gc
| USART_PMODE_DISABLED_gc
| USART_CHSIZE_8BIT_gc;
/* set clock divider */
_xmega_calculate_bsel_bscale(CLOCK_CORECLOCK, baudrate, &clk2x,
&bsel, &bscale);
dev(uart)->BAUDCTRLA = (uint8_t)(bsel & 0x00ff);
dev(uart)->BAUDCTRLB = (bscale << USART_BSCALE_gp)
| ((uint8_t)((bsel & 0x0fff) >> 8));
if (clk2x == 1) {
dev(uart)->CTRLB |= USART_CLK2X_bm;
}
/* enable RX and TX Interrupts and set level*/
if (rx_cb) {
dev(uart)->CTRLA = (uart_config[uart].rx_int_lvl << USART_RXCINTLVL_gp)
| (uart_config[uart].tx_int_lvl << USART_TXCINTLVL_gp)
| (uart_config[uart].dre_int_lvl << USART_DREINTLVL_gp);
dev(uart)->CTRLB = USART_RXEN_bm | USART_TXEN_bm;
}
else {
/* only transmit */
dev(uart)->CTRLB = USART_TXEN_bm;
}
DEBUG("Set clk2x %" PRIu8 " bsel %" PRIu16 "bscale %" PRIi8 "\n",
clk2x, bsel, bscale);
return UART_OK;
}
void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
for (size_t i = 0; i < len; i++) {
while (!(dev(uart)->STATUS & USART_DREIF_bm)) {}
/* start of TX won't finish until no data in DATAn and transmit shift
register is empty */
uint8_t irq_state = irq_disable();
avr8_state |= AVR8_STATE_FLAG_UART_TX(uart);
irq_restore(irq_state);
dev(uart)->DATA = data[i];
}
}
void uart_poweron(uart_t uart)
{
(void)uart;
/* not implemented (yet) */
}
void uart_poweroff(uart_t uart)
{
(void)uart;
/* not implemented (yet) */
}
static inline void _rx_isr_handler(int num)
{
avr8_enter_isr();
if (isr_ctx[num].rx_cb) {
isr_ctx[num].rx_cb(isr_ctx[num].arg, dev(num)->DATA);
}
avr8_exit_isr();
}
static inline void _tx_isr_handler(int num)
{
avr8_enter_isr();
/* entire frame in the Transmit Shift Register has been shifted out and
there are no new data currently present in the transmit buffer */
avr8_state &= ~AVR8_STATE_FLAG_UART_TX(num);
avr8_exit_isr();
}
#ifdef UART_0_RXC_ISR
ISR(UART_0_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(0);
}
ISR(UART_0_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(0);
}
#endif /* UART_0_ISR */
#ifdef UART_1_RXC_ISR
ISR(UART_1_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(1);
}
ISR(UART_1_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(1);
}
#endif /* UART_1_ISR */
#ifdef UART_2_RXC_ISR
ISR(UART_2_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(2);
}
ISR(UART_2_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(2);
}
#endif /* UART_2_ISR */
#ifdef UART_3_RXC_ISR
ISR(UART_3_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(3);
}
ISR(UART_3_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(3);
}
#endif /* UART_3_ISR */
#ifdef UART_4_RXC_ISR
ISR(UART_4_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(4);
}
ISR(UART_4_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(4);
}
#endif /* UART_4_ISR */
#ifdef UART_5_RXC_ISR
ISR(UART_5_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(5);
}
ISR(UART_5_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(5);
}
#endif /* UART_5_ISR */
#ifdef UART_6_RXC_ISR
ISR(UART_6_RXC_ISR, ISR_BLOCK)
{
_rx_isr_handler(6);
}
ISR(UART_6_TXC_ISR, ISR_BLOCK)
{
_tx_isr_handler(6);
}
#endif /* UART_6_ISR */