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:
parent
d041199825
commit
1a88f0bad6
82
cpu/atxmega/Kconfig
Normal file
82
cpu/atxmega/Kconfig
Normal 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
105
cpu/atxmega/Kconfig.XMEGAA
Normal 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
|
25
cpu/atxmega/Kconfig.XMEGAB
Normal file
25
cpu/atxmega/Kconfig.XMEGAB
Normal 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
|
45
cpu/atxmega/Kconfig.XMEGAC
Normal file
45
cpu/atxmega/Kconfig.XMEGAC
Normal 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
|
55
cpu/atxmega/Kconfig.XMEGAD
Normal file
55
cpu/atxmega/Kconfig.XMEGAD
Normal 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
|
17
cpu/atxmega/Kconfig.XMEGAE
Normal file
17
cpu/atxmega/Kconfig.XMEGAE
Normal 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
7
cpu/atxmega/Makefile
Normal 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
11
cpu/atxmega/Makefile.dep
Normal 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
|
11
cpu/atxmega/Makefile.features
Normal file
11
cpu/atxmega/Makefile.features
Normal 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
|
38
cpu/atxmega/Makefile.include
Normal file
38
cpu/atxmega/Makefile.include
Normal 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
132
cpu/atxmega/atxmega_cpu.c
Normal 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
10
cpu/atxmega/doc.txt
Normal 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
|
||||
*/
|
75
cpu/atxmega/include/cpu_clock.h
Normal file
75
cpu/atxmega/include/cpu_clock.h
Normal 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 */
|
||||
/** @} */
|
76
cpu/atxmega/include/cpu_conf.h
Normal file
76
cpu/atxmega/include/cpu_conf.h
Normal 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 */
|
||||
/** @} */
|
65
cpu/atxmega/include/cpu_nvm.h
Normal file
65
cpu/atxmega/include/cpu_nvm.h
Normal 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 */
|
||||
/** @} */
|
247
cpu/atxmega/include/periph_cpu.h
Normal file
247
cpu/atxmega/include/periph_cpu.h
Normal 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 */
|
||||
/** @} */
|
3
cpu/atxmega/periph/Makefile
Normal file
3
cpu/atxmega/periph/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = atxmega_periph
|
||||
|
||||
include $(RIOTMAKE)/periph.mk
|
64
cpu/atxmega/periph/cpuid.c
Normal file
64
cpu/atxmega/periph/cpuid.c
Normal 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
546
cpu/atxmega/periph/gpio.c
Normal 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
78
cpu/atxmega/periph/nvm.c
Normal 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
80
cpu/atxmega/periph/pm.c
Normal 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
533
cpu/atxmega/periph/timer.c
Normal 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
425
cpu/atxmega/periph/uart.c
Normal 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 */
|
Loading…
Reference in New Issue
Block a user