From 0cc47c208f80e2a7f62418e956c8eb89ae8fecdc Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 13 Jun 2023 04:33:14 +0200 Subject: [PATCH 01/11] boards/atmega8: new board --- boards/atmega8/Kconfig | 25 +++++ boards/atmega8/Makefile | 5 + boards/atmega8/Makefile.dep | 1 + boards/atmega8/Makefile.features | 9 ++ boards/atmega8/Makefile.include | 16 +++ boards/atmega8/doc.txt | 99 +++++++++++++++++++ boards/atmega8/include/board.h | 63 ++++++++++++ boards/atmega8/include/periph_conf.h | 45 +++++++++ .../include/periph_conf_atmega_common.h | 25 ++++- cpu/atmega8/Kconfig | 34 +++++++ cpu/atmega8/Makefile | 7 ++ cpu/atmega8/Makefile.dep | 1 + cpu/atmega8/Makefile.features | 1 + cpu/atmega8/Makefile.include | 7 ++ cpu/atmega8/doc.txt | 10 ++ cpu/atmega8/include/default_timer_config.h | 46 +++++++++ cpu/atmega8/include/periph_cpu.h | 98 ++++++++++++++++++ cpu/atmega_common/atmega_cpu.c | 6 +- .../include/atmega_regs_common.h | 44 ++++++++- cpu/atmega_common/include/cpu_conf.h | 6 ++ cpu/atmega_common/include/periph_cpu_common.h | 10 ++ cpu/atmega_common/periph/adc.c | 9 +- cpu/atmega_common/periph/gpio.c | 26 ++++- cpu/atmega_common/periph/i2c.c | 8 ++ cpu/atmega_common/periph/pwm.c | 45 +++++++++ cpu/atmega_common/periph/spi.c | 10 +- cpu/atmega_common/periph/timer.c | 39 +++++++- cpu/atmega_common/periph/uart.c | 27 ++++- cpu/avr8_common/avr8_cpu.c | 6 ++ cpu/avr8_common/include/cpu.h | 4 + cpu/avr8_common/ldscripts/avr4.ld | 27 +++++ cpu/avr8_common/startup.c | 12 ++- 32 files changed, 753 insertions(+), 18 deletions(-) create mode 100644 boards/atmega8/Kconfig create mode 100644 boards/atmega8/Makefile create mode 100644 boards/atmega8/Makefile.dep create mode 100644 boards/atmega8/Makefile.features create mode 100644 boards/atmega8/Makefile.include create mode 100644 boards/atmega8/doc.txt create mode 100644 boards/atmega8/include/board.h create mode 100644 boards/atmega8/include/periph_conf.h create mode 100644 cpu/atmega8/Kconfig create mode 100644 cpu/atmega8/Makefile create mode 100644 cpu/atmega8/Makefile.dep create mode 100644 cpu/atmega8/Makefile.features create mode 100644 cpu/atmega8/Makefile.include create mode 100644 cpu/atmega8/doc.txt create mode 100644 cpu/atmega8/include/default_timer_config.h create mode 100644 cpu/atmega8/include/periph_cpu.h create mode 100644 cpu/avr8_common/ldscripts/avr4.ld diff --git a/boards/atmega8/Kconfig b/boards/atmega8/Kconfig new file mode 100644 index 000000000000..420dc8185737 --- /dev/null +++ b/boards/atmega8/Kconfig @@ -0,0 +1,25 @@ +# Copyright (c) 2020 HAW Hamburg +# +# 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 BOARD + default "atmega8" if BOARD_ATMEGA8 + +config BOARD_ATMEGA8 + bool + default y + select CPU_MODEL_ATMEGA8 + # Put defined MCU peripherals here (in alphabetical order) + select HAS_PERIPH_ADC + select HAS_PERIPH_GPIO + select HAS_PERIPH_I2C + select HAS_PERIPH_PWM + select HAS_PERIPH_SPI + select HAS_PERIPH_TIMER + select HAS_PERIPH_UART + # Various other features (if any) + select MODULE_BOARDS_COMMON_ATMEGA if TEST_KCONFIG + +source "$(RIOTBOARD)/common/atmega/Kconfig" diff --git a/boards/atmega8/Makefile b/boards/atmega8/Makefile new file mode 100644 index 000000000000..3134740b39ff --- /dev/null +++ b/boards/atmega8/Makefile @@ -0,0 +1,5 @@ +MODULE = board + +DIRS = $(RIOTBOARD)/common/atmega + +include $(RIOTBASE)/Makefile.base diff --git a/boards/atmega8/Makefile.dep b/boards/atmega8/Makefile.dep new file mode 100644 index 000000000000..3d1c295b9b21 --- /dev/null +++ b/boards/atmega8/Makefile.dep @@ -0,0 +1 @@ +USEMODULE += boards_common_atmega diff --git a/boards/atmega8/Makefile.features b/boards/atmega8/Makefile.features new file mode 100644 index 000000000000..da674867d947 --- /dev/null +++ b/boards/atmega8/Makefile.features @@ -0,0 +1,9 @@ +CPU = atmega8 + +FEATURES_PROVIDED += periph_adc +FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_i2c +FEATURES_PROVIDED += periph_pwm +FEATURES_PROVIDED += periph_spi +FEATURES_PROVIDED += periph_timer +FEATURES_PROVIDED += periph_uart diff --git a/boards/atmega8/Makefile.include b/boards/atmega8/Makefile.include new file mode 100644 index 000000000000..df08c470af1a --- /dev/null +++ b/boards/atmega8/Makefile.include @@ -0,0 +1,16 @@ +# configure the terminal program +PORT_LINUX ?= /dev/ttyUSB0 +PORT_DARWIN ?= $(firstword $(sort $(wildcard /dev/tty.usbmodem*))) +BAUD ?= 9600 +ATMEGA8_CLOCK ?= + +# Allow overwriting programmer via env variables without affecting other boards +PROGRAMMER_BOARD_ATMEGA8 ?= usbasp +# ICSP programmer to use for avrdude +AVRDUDE_PROGRAMMER ?= $(PROGRAMMER_BOARD_ATMEGA8) + +ifneq (,$(ATMEGA8_CLOCK)) + CFLAGS += -DCLOCK_CORECLOCK=$(ATMEGA8_CLOCK) +endif + +include $(RIOTBOARD)/common/atmega/Makefile.include diff --git a/boards/atmega8/doc.txt b/boards/atmega8/doc.txt new file mode 100644 index 000000000000..13acb87ad7db --- /dev/null +++ b/boards/atmega8/doc.txt @@ -0,0 +1,99 @@ +/** +@defgroup boards_atmega8 Standalone ATmega8 +@ingroup boards +@brief Support for using the ATmega8 as standalone board + +## Overview + +As the ATmega8 can run from the internal oscillator, placing it on a breadboard, +connecting an USB-UART adapter and power is enough to run RIOT on it. (An ISP +programmer will be needed to program it; or to program a bootloader to +subsequently allow programming via UART.) + +### MCU +| MCU | ATmega8 | +|:------------- |:--------------------------------------------- | +| Family | AVR/ATmega | +| Vendor | Microchip (previously Atmel) | +| RAM | 1KiB | +| Flash | 8KiB | +| EEPROM | 512B | +| Frequency | 1MHz/8MHz (up to 16MHz with external clock) | +| Timers | 3 (2x 8bit, 1x 16bit) | +| ADCs | 6 analog input pins | +| UARTs | 1 | +| SPIs | 1 | +| I2Cs | 1 (called TWI) | +| Vcc | 4.5V - 5.5V (ATmega8), 2.7V - 5.5V (ATmega8L) | +| Datasheet | [Official datasheet](https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_datasheet.pdf) | + +### Pinout + +\htmlonly\endhtmlonly +@image html "https://camo.githubusercontent.com/c55beef2f138da61fe671a1e4a307ff4ffbc318d/68747470733a2f2f692e696d6775722e636f6d2f715849456368542e6a7067" "Pinout of the ATmega328p"
+ +All credit for above pinout image goes to https://github.com/MCUdude/MiniCore#pinout + +### Clock Frequency + +The ATmega8 has an internal oscillators clocked at 1MHz that allow it to be +operated without any external clock source or crystal. By default the fuses are +configured to use the internal oscillator and an operating mode resulting in a +clock speed of 1MHz. By setting the `CKSEL` fuses to 0100 the clock will operate +at 8MHz without an external clock source. This can be done like this: + + avrdude -c usbasp -p m8 -B 32 -U lfuse:w:0xe4:m + +(Replace `usbasp` with the ISP programmer you are using. The `-B 32` might +be needed on some ISP programmers to communicate with slow ATmega MCUs. It will +not be needed anymore after the clock device has been disabled.) + +This "board" is configured to use 8MHz as core clock, so that the ATmega8 +runs at the highest frequency possible without external clock sources. + +By setting the environment variable `ATMEGA8_CLOCK` to a custom frequency in +Hz (e.g. `1000000` for 1MHz), this core clock can be changed easily. Refer to +the datasheet on how to configure the ATmega8 to use an external crystal, an +external clock source or the clock divider. + +### Relation Between Supply Voltage, Clock Frequency and Power Consumption + +A higher supply voltage results in a higher current drawn. Thus, lower power +consumption can be achieved by using a lower supply voltage. However, higher +clock frequencies require higher supply voltages for reliable operation. + +The lowest possible supply voltage at 8 MHz is 2.7V (Atmega8L) or +4.5V (Atmega8). + +## Flashing the Device + +In order to flash the ATmega8 without a bootloader, an ISP programmer is +needed. Connect the programmer as follows: + +| ISP pin | ATmega8 pin | +|:-------- |:-------------- | +| MISO | 18/PB4/MISO | +| VCC | 7/VCC | +| SCK | 19/PB5/SCK | +| MOSI | 17/PB3/MOSI | +| RESET | 1/RESET | +| Ground | 22/GND | + +The tool `avrdude` needs to be installed. When using the `usbasp` running + + make BOARD=atmega8 flash + +will take care of everything. To use the programmer `` instead, run + + make BOARD=atmega8 PROGRAMMER= flash + +## Serial Terminal + +Connect a TTL adapter with pins 2/RXD and 3/TXD an run + + make BOARD=atmega8 term + +Please note that the supply voltage should be compatible with the logic level of +the TTL adapter. Usually everything between 3.3 V and 5 V should work. + + */ diff --git a/boards/atmega8/include/board.h b/boards/atmega8/include/board.h new file mode 100644 index 000000000000..965e4448811c --- /dev/null +++ b/boards/atmega8/include/board.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * 2016 Laurent Navet + * 2019 Otto-von-Guericke-Universität Magdeburg + * 2023 Hugues Larrive + * + * 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 boards_atmega8 + * @{ + * + * @file + * @brief Board specific definitions for the standalone ATmega8 "board" + * + * @author Marian Buschsieweke + * @author Hinnerk van Bruinehsen + * @author Laurent Navet + * @author Hugues Larrive + */ + +#ifndef BOARD_H +#define BOARD_H + +#include "cpu.h" +#include "periph_conf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name STDIO configuration + * + * As the CPU is too slow to handle 115200 baud, we set the default + * baudrate to 9600 for this board + * @{ + */ +#define STDIO_UART_BAUDRATE (9600U) +/** @} */ + +/** + * @name xtimer configuration values + * @{ + */ +#define XTIMER_WIDTH (16) +#if CLOCK_CORECLOCK > 4000000UL +#define XTIMER_HZ (CLOCK_CORECLOCK / 64) +#else +#define XTIMER_HZ (CLOCK_CORECLOCK / 8) +#endif +#define XTIMER_BACKOFF (40) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* BOARD_H */ +/** @} */ diff --git a/boards/atmega8/include/periph_conf.h b/boards/atmega8/include/periph_conf.h new file mode 100644 index 000000000000..ba64fb366961 --- /dev/null +++ b/boards/atmega8/include/periph_conf.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg + * 2023 Hugues Larrive + * + * 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 boards_atmega8 + * @{ + * + * @file + * @brief Peripheral MCU configuration for the ATmega8 standalone "board" + * + * @author Marian Buschsieweke + * @author Hugues Larrive + */ + +#ifndef PERIPH_CONF_H +#define PERIPH_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Clock configuration + * @{ + */ +#ifndef CLOCK_CORECLOCK +/* Using 8MHz internal oscillator as default clock source */ +#define CLOCK_CORECLOCK (8000000UL) +#endif +/** @} */ + +#ifdef __cplusplus +} +#endif + +#include "periph_conf_atmega_common.h" + +#endif /* PERIPH_CONF_H */ +/** @} */ diff --git a/boards/common/atmega/include/periph_conf_atmega_common.h b/boards/common/atmega/include/periph_conf_atmega_common.h index c2779b141069..8ba78f5c6366 100644 --- a/boards/common/atmega/include/periph_conf_atmega_common.h +++ b/boards/common/atmega/include/periph_conf_atmega_common.h @@ -6,6 +6,7 @@ * 2017 HAW Hamburg, Dimitri Nahm * 2018 Matthew Blue * 2019 Otto-von-Guericke-Universität Magdeburg + * 2023 Hugues Larrive * * 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 @@ -28,6 +29,7 @@ * @author Matthew Blue * @author Francisco Acosta * @author Marian Buschsieweke + * @author Hugues Larrive */ #ifndef PERIPH_CONF_ATMEGA_COMMON_H @@ -71,6 +73,12 @@ extern "C" { #define UART_1 MEGA_UART1 #define UART_1_ISR USART1_RX_vect #define UART_1_ISR_TX USART1_TX_vect +#elif defined(CPU_ATMEGA8) + #define UART_NUMOF (1U) + + #define UART_0 MEGA_UART + #define UART_0_ISR USART_RXC_vect + #define UART_0_ISR_TX USART_TXC_vect #elif defined(CPU_ATMEGA328P) #define UART_NUMOF (1U) @@ -156,7 +164,8 @@ extern "C" { */ #ifndef ADC_NUMOF #if defined(CPU_ATMEGA128RFA1) || defined(CPU_ATMEGA256RFR2) || defined(CPU_ATMEGA328P) || \ - defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA1284P) || defined(CPU_ATMEGA32U4) + defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA1284P) || defined(CPU_ATMEGA32U4) || \ + defined(CPU_ATMEGA8) #define ADC_NUMOF (8U) #elif defined (CPU_ATMEGA2560) #define ADC_NUMOF (16U) @@ -180,7 +189,9 @@ extern "C" { * @{ */ #ifndef PWM_NUMOF -#if defined(CPU_ATMEGA328P) +#if defined(CPU_ATMEGA8) + #define PWM_PINS_CH0 { GPIO_PIN(PORT_B, 3), GPIO_UNDEF } +#elif defined(CPU_ATMEGA328P) #define PWM_PINS_CH0 { GPIO_PIN(PORT_D, 6), GPIO_PIN(PORT_D, 5) } #define PWM_PINS_CH1 { GPIO_PIN(PORT_B, 3), GPIO_PIN(PORT_D, 3) } #elif defined(CPU_ATMEGA1281) @@ -200,8 +211,9 @@ extern "C" { #if defined(CPU_ATMEGA32U4) || defined(CPU_ATMEGA328P) || \ defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA1284P) || \ - defined(CPU_ATMEGA2560) + defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA8) static const pwm_conf_t pwm_conf[] = { +#ifndef CPU_ATMEGA8 { .dev = MINI_TIMER0, .pin_ch = PWM_PINS_CH0, @@ -213,6 +225,13 @@ extern "C" { .pin_ch = PWM_PINS_CH1, .div = MINI_TIMER2_DIV, } +#endif +#else /* CPU_ATMEGA8 */ + { + .dev = MINI_TIMER2, + .pin_ch = PWM_PINS_CH0, + .div = MINI_TIMER2_DIV, + }, #endif }; diff --git a/cpu/atmega8/Kconfig b/cpu/atmega8/Kconfig new file mode 100644 index 000000000000..aa1c3c6ff1b5 --- /dev/null +++ b/cpu/atmega8/Kconfig @@ -0,0 +1,34 @@ +# Copyright (c) 2020 HAW Hamburg +# +# 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_FAM_ATMEGA8 + bool + select CPU_COMMON_ATMEGA + +## CPU Models +config CPU_MODEL_ATMEGA8 + bool + select CPU_FAM_ATMEGA8 + select HAS_CPU_ATMEGA8 + +## Definition of specific features +config HAS_CPU_ATMEGA8 + bool + help + Indicates that a 'atmega8' cpu is being used. + +## Common CPU symbols +config CPU_FAM + default "atmega8" if CPU_FAM_ATMEGA8 + +config CPU_MODEL + default "atmega8" if CPU_MODEL_ATMEGA8 + +config CPU + default "atmega8" if CPU_FAM_ATMEGA8 + +source "$(RIOTCPU)/atmega_common/Kconfig" diff --git a/cpu/atmega8/Makefile b/cpu/atmega8/Makefile new file mode 100644 index 000000000000..8b5bf236d32d --- /dev/null +++ b/cpu/atmega8/Makefile @@ -0,0 +1,7 @@ +# define the module that is build +MODULE = cpu + +# add a list of subdirectories, that should also be build +DIRS = $(RIOTCPU)/atmega_common/ + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/atmega8/Makefile.dep b/cpu/atmega8/Makefile.dep new file mode 100644 index 000000000000..b43170c8360a --- /dev/null +++ b/cpu/atmega8/Makefile.dep @@ -0,0 +1 @@ +include $(RIOTCPU)/atmega_common/Makefile.dep diff --git a/cpu/atmega8/Makefile.features b/cpu/atmega8/Makefile.features new file mode 100644 index 000000000000..ec626a3bf954 --- /dev/null +++ b/cpu/atmega8/Makefile.features @@ -0,0 +1 @@ +include $(RIOTCPU)/atmega_common/Makefile.features diff --git a/cpu/atmega8/Makefile.include b/cpu/atmega8/Makefile.include new file mode 100644 index 000000000000..c0393b2e76e2 --- /dev/null +++ b/cpu/atmega8/Makefile.include @@ -0,0 +1,7 @@ +RAM_LEN = 1K +ROM_LEN = 8K + +LINKER_SCRIPT ?= avr4.ld + +# CPU depends on the atmega common module, so include it +include $(RIOTCPU)/atmega_common/Makefile.include diff --git a/cpu/atmega8/doc.txt b/cpu/atmega8/doc.txt new file mode 100644 index 000000000000..8980bacb9391 --- /dev/null +++ b/cpu/atmega8/doc.txt @@ -0,0 +1,10 @@ +/** + * @defgroup cpu_atmega8 Atmel ATmega8 + * @ingroup cpu + * @brief Implementation of Atmel's ATmega8 MCU + */ + +/** + * @defgroup cpu_atmega8_definitions Atmel ATmega8 Definitions + * @ingroup cpu_atmega8 + */ diff --git a/cpu/atmega8/include/default_timer_config.h b/cpu/atmega8/include/default_timer_config.h new file mode 100644 index 000000000000..686c2bbde1a8 --- /dev/null +++ b/cpu/atmega8/include/default_timer_config.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * 2016 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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_atmega8 + * @{ + * + * @file + * @brief Default timer configuration + * + * @author René Herthel + * @author Hauke Petersen + * @author Hugues Larrive + */ + +#ifndef DEFAULT_TIMER_CONFIG_H +#define DEFAULT_TIMER_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef TIMER_NUMOF +#define TIMER_NUMOF (1U) +#define TIMER_CHANNEL_NUMOF (2) + +#define TIMER_0 MEGA_TIMER1 +#define TIMER_0_MASK &TIMSK +#define TIMER_0_FLAG &TIFR +#define TIMER_0_ISRA TIMER1_COMPA_vect +#define TIMER_0_ISRB TIMER1_COMPB_vect +#endif /* TIMER_NUMOF */ + +#ifdef __cplusplus +} +#endif + +#endif /* DEFAULT_TIMER_CONFIG_H */ +/** @} */ diff --git a/cpu/atmega8/include/periph_cpu.h b/cpu/atmega8/include/periph_cpu.h new file mode 100644 index 000000000000..d8666c83ed56 --- /dev/null +++ b/cpu/atmega8/include/periph_cpu.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * 2016 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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_atmega8 + * @{ + * + * @file + * @brief CPU specific definitions for internal peripheral handling + * + * @author René Herthel + * @author Hauke Petersen + * @author Hugues Larrive + */ + +#ifndef PERIPH_CPU_H +#define PERIPH_CPU_H + +#include "periph_cpu_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Define a CPU specific GPIO pin generator macro + */ +#define GPIO_PIN(x, y) ((x << 4) | y) + +/** + * @brief Available ports on the ATmega8 family + */ +enum { + PORT_B = 1, /**< port B */ + PORT_C = 2, /**< port C */ + PORT_D = 3 /**< port D */ +}; + +/** + * @brief Available external interrupt pins on the ATmega8 family + * + * In order of their interrupt number. + */ +#define CPU_ATMEGA_EXT_INTS { GPIO_PIN(PORT_D, 2), \ + GPIO_PIN(PORT_D, 3) } + +/** + * @brief Get the interrupt vector number of the given GPIO pin + */ +static inline uint8_t atmega_pin2exti(uint8_t port_num, uint8_t pin_num) +{ + (void)port_num; + return pin_num - 2; +} + +/** + * @brief Check if the given pin can be used as external interrupt + */ +static inline bool atmega_has_pin_exti(uint8_t port_num, uint8_t pin_num) +{ + if (port_num == PORT_D) { + return ((pin_num == 2) || (pin_num == 3)); + } + + return false; +} + +/** + * @name Defines for the I2C interface + * @{ + */ +#define I2C_PORT_REG PORTC +#define I2C_PIN_MASK (1 << PORTC4) | (1 << PORTC5) +/** @} */ + +/** + * @name EEPROM configuration + * @{ + */ +#define EEPROM_SIZE (512U) /* 512B */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#include "periph_conf.h" +#include "default_timer_config.h" + +#endif /* PERIPH_CPU_H */ +/** @} */ diff --git a/cpu/atmega_common/atmega_cpu.c b/cpu/atmega_common/atmega_cpu.c index 52d93bf4c58d..7bca49792684 100644 --- a/cpu/atmega_common/atmega_cpu.c +++ b/cpu/atmega_common/atmega_cpu.c @@ -3,6 +3,7 @@ * 2017 RWTH Aachen, Josua Arndt * 2018 Matthew Blue * 2021 Gerson Fernando Budke + * 2023 Hugues Larrive * * 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 @@ -22,6 +23,7 @@ * @author Matthew Blue * @author Francisco Acosta * @author Gerson Fernando Budke + * @author Hugues Larrive * * @} */ @@ -58,17 +60,19 @@ void avr8_reset_cause(void) DEBUG("Watchdog reset!\n"); } } -#if !defined (CPU_ATMEGA328P) +#if !defined (CPU_ATMEGA328P) && !defined (CPU_ATMEGA8) if (mcusr_mirror & (1 << JTRF)) { DEBUG("JTAG reset!\n"); } #endif } +#if !defined (CPU_ATMEGA8) void __attribute__((weak)) avr8_clk_init(void) { atmega_set_prescaler(CPU_ATMEGA_CLK_SCALE_INIT); } +#endif /* This is a vector which is aliased to __vector_default, * the vector executed when an ISR fires with no accompanying diff --git a/cpu/atmega_common/include/atmega_regs_common.h b/cpu/atmega_common/include/atmega_regs_common.h index 5f2785f8ea23..cc732079c90f 100644 --- a/cpu/atmega_common/include/atmega_regs_common.h +++ b/cpu/atmega_common/include/atmega_regs_common.h @@ -2,6 +2,7 @@ * Copyright (C) 2016 Freie Universität Berlin * 2016 INRIA * 2017 Thomas Perrot + * 2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * @author Hauke Petersen * @author Francisco Acosta * @author Thomas Perrot + * @author Hugues Larrive * */ @@ -42,6 +44,7 @@ extern "C" { /** * @brief Timer register map */ +#ifdef TCCR1C typedef struct { REG8 CRA; /**< control A */ REG8 CRB; /**< control B */ @@ -51,21 +54,39 @@ typedef struct { REG16 ICR; /**< input capture */ REG16 OCR[3]; /**< output compare */ } mega_timer_t; +#else /* atmega8 */ +typedef struct { + REG16 ICR; /**< input capture */ + REG16 OCR[2]; /**< output compare */ + REG16 CNT; /**< counter */ + REG8 CRB; /**< control B */ + REG8 CRA; /**< control A */ +} mega_timer_t; +#endif /** * @brief 8-bit timer register map */ +#ifndef TCCR2 typedef struct { REG8 CRA; /**< control A */ REG8 CRB; /**< control B */ REG8 CNT; /**< counter */ REG8 OCR[2]; /**< output compare */ } mini_timer_t; +#else /* atmega8 */ +typedef struct { + REG8 OCR; /**< output compare */ + REG8 CNT; /**< counter */ + REG8 CR; /**< control */ +} mini_timer_t; +#endif /** * @brief UART register map */ typedef struct { +#ifndef CPU_ATMEGA8 REG8 CSRA; /**< control and status register A */ REG8 CSRB; /**< control and status register B */ REG8 CSRC; /**< control and status register C */ @@ -76,6 +97,16 @@ typedef struct { #endif REG16 BRR; /**< baud rate register */ REG8 DR; /**< data register */ +#else /* atmega8 */ + REG8 BRRL; /**< baud rate register low byte */ + REG8 CSRB; /**< control and status register B */ + REG8 CSRA; /**< control and status register A */ + REG8 DR; /**< data register */ + REG8 padding[19]; /**< 3 SPI + 3 PORTD + 3 PORTC + 3 PORTB + * + 3 reserved + 4 EEPROM = 19 */ + REG8 CSRC; /**< control and status register C shared + * with baud rate register high byte */ +#endif } mega_uart_t; /** @@ -87,14 +118,20 @@ typedef struct { #define MINI_TIMER0_DIV TIMER_DIV1_8_64_128_1024 #endif -#if defined(TCCR1A) +#if defined(TCCR1C) #define MEGA_TIMER1_BASE (uint16_t *)(&TCCR1A) #define MEGA_TIMER1 ((mega_timer_t *)MEGA_TIMER1_BASE) +#elif defined(TCCR1A) /* atmega8 */ +#define MEGA_TIMER1_BASE (uint16_t *)(&ICR1L) +#define MEGA_TIMER1 ((mega_timer_t *)MEGA_TIMER1_BASE) #endif #if defined(TCCR2A) #define MINI_TIMER2 ((mini_timer_t *)(uint16_t *)(&TCCR2A)) #define MINI_TIMER2_DIV TIMER_DIV1_8_32_64_128_256_1024 +#elif defined(TCCR2) /* atmega8 */ +#define MINI_TIMER2 ((mini_timer_t *)(uint16_t *)(&OCR2)) +#define MINI_TIMER2_DIV TIMER_DIV1_8_32_64_128_256_1024 #endif #if defined(TCCR3A) @@ -117,6 +154,11 @@ typedef struct { * @brief Peripheral register definitions and instances * @{ */ +#if defined(UCSRA) +#define MEGA_UART_BASE ((uint16_t *)(&UBRRL)) +#define MEGA_UART ((mega_uart_t *)MEGA_UART_BASE) +#endif + #if defined(UCSR0A) #define MEGA_UART0_BASE ((uint16_t *)(&UCSR0A)) #define MEGA_UART0 ((mega_uart_t *)MEGA_UART0_BASE) diff --git a/cpu/atmega_common/include/cpu_conf.h b/cpu/atmega_common/include/cpu_conf.h index 8cb9dd85608c..0b8f7201159d 100644 --- a/cpu/atmega_common/include/cpu_conf.h +++ b/cpu/atmega_common/include/cpu_conf.h @@ -2,6 +2,7 @@ * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen * 2017 RWTH Aachen, Josua Arndt * 2018 Matthew Blue + * 2023 Hugues Larrive * * 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 @@ -20,6 +21,7 @@ * @author Josua Arndt * @author Steffen Robertz * @author Matthew Blue + * @author Hugues Larrive */ #ifndef CPU_CONF_H @@ -44,7 +46,11 @@ extern "C" { * @{ */ #ifndef THREAD_STACKSIZE_DEFAULT +#ifndef CPU_ATMEGA8 #define THREAD_STACKSIZE_DEFAULT (512) +#else +#define THREAD_STACKSIZE_DEFAULT (256) +#endif #endif /* keep THREAD_STACKSIZE_IDLE > THREAD_EXTRA_STACKSIZE_PRINTF diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 5d2b04f24024..42350454a832 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -2,6 +2,7 @@ * Copyright (C) 2015 HAW Hamburg * 2016 Freie Universität Berlin * 2016 INRIA + * 2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * @author René Herthel * @author Hauke Petersen * @author Francisco Acosta + * @author Hugues Larrive */ #ifndef PERIPH_CPU_COMMON_H @@ -61,7 +63,11 @@ typedef uint8_t gpio_t; * * Must be identical to the address of `PINA` provided by avr/io.h */ +#ifdef CPU_ATMEGA8 +#define ATMEGA_GPIO_BASE_A (0x39) +#else #define ATMEGA_GPIO_BASE_A (0x20) +#endif /** * @brief Base of the GPIO port G register as memory address * @@ -137,7 +143,11 @@ typedef struct { static inline atmega_gpio_port_t *atmega_gpio_port(uint8_t port_num) { static const uintptr_t base_addr = (uintptr_t)ATMEGA_GPIO_BASE_A; +#ifdef CPU_ATMEGA8 + uintptr_t res = base_addr - port_num * sizeof(atmega_gpio_port_t); +#else uintptr_t res = base_addr + port_num * sizeof(atmega_gpio_port_t); +#endif /* GPIO ports up to (including) G are mapped in the I/O address space, * port H and higher (if present) are mapped in a different contiguous * region afterwards (e.g. 0x100 for ATmega2560). */ diff --git a/cpu/atmega_common/periph/adc.c b/cpu/atmega_common/periph/adc.c index 6585c617083d..c81cf9183b8f 100644 --- a/cpu/atmega_common/periph/adc.c +++ b/cpu/atmega_common/periph/adc.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2016 Laurent Navet * 2017 HAW Hamburg, Dimitri Nahm + * 2023 Hugues Larrive * * 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 @@ -17,6 +18,7 @@ * @author Laurent Navet * @author Dimitri Nahm * @author Sebastian Meiling + * @author Hugues Larrive * @} */ @@ -52,6 +54,7 @@ int adc_init(adc_t line) _prep(); +#if !defined(CPU_ATMEGA8) /* Disable corresponding Digital input */ if (line < 8) { DIDR0 |= (1 << line); @@ -61,9 +64,10 @@ int adc_init(adc_t line) DIDR2 |= (1 << (line - 8)); } #endif +#endif /* !CPU_ATMEGA8 */ /* Set ADC-pin as input */ -#if defined(CPU_ATMEGA328P) +#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA8) DDRC &= ~(1 << line); PORTC &= ~(1 << line); #elif defined(CPU_ATMEGA1284P) @@ -110,7 +114,8 @@ int32_t adc_sample(adc_t line, adc_res_t res) _prep(); /* set conversion channel */ -#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA1281) || defined(CPU_ATMEGA1284P) || defined(CPU_ATMEGA32U4) +#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA1281) || \ + defined(CPU_ATMEGA1284P) || defined(CPU_ATMEGA32U4) || defined(CPU_ATMEGA8) ADMUX &= 0xf0; ADMUX |= line; #elif defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA128RFA1) || defined(CPU_ATMEGA256RFR2) diff --git a/cpu/atmega_common/periph/gpio.c b/cpu/atmega_common/periph/gpio.c index 2cca6e464f1e..bcde240e7fe9 100644 --- a/cpu/atmega_common/periph/gpio.c +++ b/cpu/atmega_common/periph/gpio.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2015 HAW Hamburg * 2016 INRIA - + * 2023 Hugues Larrive * * 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 @@ -22,6 +22,7 @@ * @author Robert Hartung * @author Torben Petersen * @author Marian Buschsieweke + * @author Hugues Larrive * * @} */ @@ -313,20 +314,30 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, cli(); /* enable interrupt number int_num */ +#ifndef CPU_ATMEGA8 EIFR |= (1 << int_num); EIMSK |= (1 << int_num); +#else /* atmega8 */ + GIFR |= (1 << (INTF0 + int_num)); + GICR |= (1 << (INT0 + int_num)); +#endif /* apply flank to interrupt number int_num */ - #if defined(EICRB) +#if defined(EICRB) if (int_num >= 4) { EICRB &= ~(0x3 << ((int_num % 4) * 2)); EICRB |= (flank << ((int_num % 4) * 2)); } else - #endif +#endif { +#ifndef CPU_ATMEGA8 EICRA &= ~(0x3 << (int_num * 2)); EICRA |= (flank << (int_num * 2)); +#else /* atmega8 */ + MCUCR &= ~(0x3 << (int_num * 2)); + MCUCR |= (flank << (int_num * 2)); +#endif } /* set callback */ @@ -341,13 +352,22 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, void gpio_irq_enable(gpio_t pin) { +#ifndef CPU_ATMEGA8 EIFR |= (1 << _int_num(pin)); EIMSK |= (1 << _int_num(pin)); +#else /* atmega8 */ + GIFR |= (1 << (INTF0 + _int_num(pin))); + GICR |= (1 << (INTF0 + _int_num(pin))); +#endif } void gpio_irq_disable(gpio_t pin) { +#ifndef CPU_ATMEGA8 EIMSK &= ~(1 << _int_num(pin)); +#else /* atmega8 */ + GICR &= ~(1 << (INTF0 + _int_num(pin))); +#endif } static inline void irq_handler(uint8_t int_num) diff --git a/cpu/atmega_common/periph/i2c.c b/cpu/atmega_common/periph/i2c.c index 3b4a0d251e14..8cdc20e4dc9e 100644 --- a/cpu/atmega_common/periph/i2c.c +++ b/cpu/atmega_common/periph/i2c.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Hamburg University of Applied Sciences, Dimitri Nahm + * 2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * * @author Dimitri Nahm * @author Laurent Navet + * @author Hugues Larrive * * @} */ @@ -47,7 +49,9 @@ static int _start(uint8_t address, uint8_t rw_flag); static int _write(const uint8_t *data, int length); static void _stop(void); +#ifndef CPU_ATMEGA8 static void i2c_poweron(i2c_t dev); +#endif static mutex_t locks[I2C_NUMOF]; @@ -119,8 +123,10 @@ void i2c_init(i2c_t dev) /* set pull-up on SCL and SDA */ I2C_PORT_REG |= (I2C_PIN_MASK); +#ifndef CPU_ATMEGA8 /* enable I2C clock */ i2c_poweron(dev); +#endif /* disable device */ TWCR &= ~(1 << TWEN); @@ -234,12 +240,14 @@ void i2c_release(i2c_t dev) mutex_unlock(&locks[dev]); } +#ifndef CPU_ATMEGA8 static void i2c_poweron(i2c_t dev) { assert(dev < I2C_NUMOF); (void) dev; power_twi_enable(); } +#endif static int _start(uint8_t address, uint8_t rw_flag) { diff --git a/cpu/atmega_common/periph/pwm.c b/cpu/atmega_common/periph/pwm.c index 6f1d970777c0..9c5aa5f3aa11 100644 --- a/cpu/atmega_common/periph/pwm.c +++ b/cpu/atmega_common/periph/pwm.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2017 Víctor Ariño + * 2023 Hugues Larrive * * 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 @@ -15,6 +16,7 @@ * @brief Low-level PWM driver implementation * * @author Víctor Ariño + * @author Hugues Larrive * * @} */ @@ -35,8 +37,12 @@ #define COMA1 7 static struct { +#ifndef CPU_ATMEGA8 uint8_t CRA; uint8_t CRB; +#else + uint8_t CR; +#endif uint8_t res; } state[PWM_NUMOF]; @@ -60,6 +66,7 @@ static inline uint8_t get_prescaler(pwm_t dev, uint32_t *scale) return pre; } +#ifndef CPU_ATMEGA8 static inline void compute_cra_and_crb(pwm_t dev, uint8_t pre) { uint8_t cra = (1 << WGM1) | (1 << WGM0); @@ -79,16 +86,25 @@ static inline void compute_cra_and_crb(pwm_t dev, uint8_t pre) state[dev].CRA = cra; state[dev].CRB = crb; } +#endif static inline void apply_config(pwm_t dev) { +#ifndef CPU_ATMEGA8 pwm_conf[dev].dev->CRA = state[dev].CRA; pwm_conf[dev].dev->CRB = state[dev].CRB; +#else + pwm_conf[dev].dev->CR = state[dev].CR; +#endif if (pwm_conf[dev].pin_ch[0] == GPIO_UNDEF) { /* If channel 0 is not used, variable resolutions can be used for * channel 1 */ +#ifndef CPU_ATMEGA8 pwm_conf[dev].dev->OCR[0] = state[dev].res; +#else + pwm_conf[dev].dev->OCR = state[dev].res; +#endif } } @@ -101,11 +117,17 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) assert(!(res != 256 && pwm_conf[dev].pin_ch[0] != GPIO_UNDEF)); /* disable PWM */ +#ifndef CPU_ATMEGA8 pwm_conf[dev].dev->CRA = 0x00; pwm_conf[dev].dev->CRB = 0x00; pwm_conf[dev].dev->OCR[0] = 0; pwm_conf[dev].dev->OCR[1] = 0; +#else + pwm_conf[dev].dev->CR = 0x00; + pwm_conf[dev].dev->OCR = 0; +#endif +#ifndef CPU_ATMEGA8 /* disable power reduction */ if (dev) { power_timer2_enable(); @@ -113,6 +135,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) else { power_timer0_enable(); } +#endif /* find out prescaler */ uint32_t scale = (CLOCK_CORECLOCK / (freq * (uint32_t)res)); @@ -121,7 +144,12 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) /* Compute configuration and store it in the state. (The state is needed * for later calls to pwm_poweron().)*/ +#ifndef CPU_ATMEGA8 compute_cra_and_crb(dev, pre); +#else + uint8_t cr = (1 << WGM21) | (1 << WGM20) | (1 << COM21) | pre; + state[dev].CR = cr; +#endif state[dev].res = res - 1; /* Apply configuration stored in state */ @@ -155,18 +183,30 @@ uint8_t pwm_channels(pwm_t dev) void pwm_set(pwm_t dev, uint8_t ch, uint16_t value) { +#ifdef CPU_ATMEGA8 + (void)ch; +#endif assert(dev < PWM_NUMOF && ch <= 1 && pwm_conf[dev].pin_ch[ch] != GPIO_UNDEF); if (value > state[dev].res) { +#ifndef CPU_ATMEGA8 pwm_conf[dev].dev->OCR[ch] = state[dev].res; +#else + pwm_conf[dev].dev->OCR = state[dev].res; +#endif } else { +#ifndef CPU_ATMEGA8 pwm_conf[dev].dev->OCR[ch] = value; +#else + pwm_conf[dev].dev->OCR = value; +#endif } } void pwm_poweron(pwm_t dev) { assert(dev < PWM_NUMOF); +#ifndef CPU_ATMEGA8 /* disable power reduction */ if (dev) { power_timer2_enable(); @@ -174,6 +214,7 @@ void pwm_poweron(pwm_t dev) else { power_timer0_enable(); } +#endif apply_config(dev); } @@ -181,6 +222,7 @@ void pwm_poweron(pwm_t dev) void pwm_poweroff(pwm_t dev) { assert(dev < PWM_NUMOF); +#ifndef CPU_ATMEGA8 pwm_conf[dev].dev->CRA = 0x00; pwm_conf[dev].dev->CRB = 0x00; /* disable timers to lower power consumption */ @@ -190,6 +232,9 @@ void pwm_poweroff(pwm_t dev) else { power_timer0_disable(); } +#else + pwm_conf[dev].dev->CR = 0x00; +#endif if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) { gpio_clear(pwm_conf[dev].pin_ch[0]); diff --git a/cpu/atmega_common/periph/spi.c b/cpu/atmega_common/periph/spi.c index cac04cfdeb40..0f9e1f50737f 100644 --- a/cpu/atmega_common/periph/spi.c +++ b/cpu/atmega_common/periph/spi.c @@ -3,6 +3,7 @@ * 2016 Freie Universität Berlin * 2017 Hamburg University of Applied Sciences * 2017 Thomas Perrot + * 2023 Hugues Larrive * * 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 @@ -21,6 +22,7 @@ * @author Hauke Petersen * @author Dimitri Nahm * @author Thomas Perrot + * @author Hugues Larrive * * @} */ @@ -43,8 +45,10 @@ static mutex_t lock = MUTEX_INIT; void spi_init(spi_t bus) { assert(bus == 0); +#ifndef CPU_ATMEGA8 /* power off the SPI peripheral */ power_spi_disable(); +#endif /* trigger the pin configuration */ spi_init_pins(bus); } @@ -56,7 +60,7 @@ void spi_init_pins(spi_t bus) #if defined (CPU_ATMEGA2560) || defined (CPU_ATMEGA1281) DDRB |= ((1 << DDB2) | (1 << DDB1) | (1 << DDB0)); #endif -#if defined (CPU_ATMEGA328P) +#if defined (CPU_ATMEGA328P) || defined (CPU_ATMEGA8) DDRB |= ((1 << DDB2) | (1 << DDB3) | (1 << DDB5)); #endif #if defined (CPU_ATMEGA1284P) @@ -86,7 +90,9 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* lock the bus and power on the SPI peripheral */ mutex_lock(&lock); +#ifndef CPU_ATMEGA8 power_spi_enable(); +#endif /* configure as master, with given mode and clock */ SPSR = (clk >> S2X_SHIFT); @@ -102,7 +108,9 @@ void spi_release(spi_t bus) (void)bus; /* power off and release the bus */ SPCR &= ~(1 << SPE); +#ifndef CPU_ATMEGA8 power_spi_disable(); +#endif mutex_unlock(&lock); } diff --git a/cpu/atmega_common/periph/timer.c b/cpu/atmega_common/periph/timer.c index 6fc9350e359f..87e6b5617d6f 100644 --- a/cpu/atmega_common/periph/timer.c +++ b/cpu/atmega_common/periph/timer.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * 2023 Hugues Larrive * * 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 @@ -16,6 +17,7 @@ * * @author Hauke Petersen * @author Hinnerk van Bruinehsen + * @author Hugues Larrive * * @} */ @@ -134,7 +136,9 @@ int timer_init(tim_t tim, uint32_t freq, timer_cb_t cb, void *arg) /* stop and reset timer */ ctx[tim].dev->CRA = 0; ctx[tim].dev->CRB = 0; +#ifndef CPU_ATMEGA8 ctx[tim].dev->CRC = 0; +#endif ctx[tim].dev->CNT = 0; /* save interrupt context and timer mode */ @@ -158,10 +162,17 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value) unsigned state = irq_disable(); ctx[tim].dev->OCR[channel] = (uint16_t)value; +#ifndef CPU_ATMEGA8 /* clear spurious IRQs, if any */ *ctx[tim].flag = (1 << (channel + OCF1A)); /* unmask IRQ */ *ctx[tim].mask |= (1 << (channel + OCIE1A)); +#else /* atmega8 */ + /* clear spurious IRQs, if any */ + *ctx[tim].flag = (1 << (OCF1A - channel)); + /* unmask IRQ */ + *ctx[tim].mask |= (1 << (OCIE1A - channel)); +#endif set_oneshot(tim, channel); irq_restore(state); @@ -179,17 +190,28 @@ int timer_set(tim_t tim, int channel, unsigned int timeout) unsigned absolute = ctx[tim].dev->CNT + timeout; ctx[tim].dev->OCR[channel] = absolute; +#ifndef CPU_ATMEGA8 /* clear spurious IRQs, if any */ *ctx[tim].flag = (1 << (channel + OCF1A)); /* unmask IRQ */ *ctx[tim].mask |= (1 << (channel + OCIE1A)); +#else /* atmega8 */ + /* clear spurious IRQs, if any */ + *ctx[tim].flag = (1 << (OCF1A - channel)); + /* unmask IRQ */ + *ctx[tim].mask |= (1 << (OCIE1A - channel)); +#endif set_oneshot(tim, channel); if ((absolute - ctx[tim].dev->CNT) > timeout) { /* Timer already expired. Trigger the interrupt now and loop until it * is triggered. */ +#ifndef CPU_ATMEGA8 while (!(*ctx[tim].flag & (1 << (OCF1A + channel)))) { +#else + while (!(*ctx[tim].flag & (1 << (OCF1A - channel)))) { +#endif ctx[tim].dev->OCR[channel] = ctx[tim].dev->CNT; } } @@ -215,10 +237,17 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags ctx[tim].dev->OCR[channel] = (uint16_t)value; +#ifndef CPU_ATMEGA8 /* clear spurious IRQs, if any */ *ctx[tim].flag = (1 << (channel + OCF1A)); /* unmask IRQ */ - *ctx[tim].mask |= (1 << (channel + OCIE1A)); + *ctx[tim].mask |= (1 << (channel + OCIE1A)); +#else /* atmega8 */ + /* clear spurious IRQs, if any */ + *ctx[tim].flag = (1 << (OCF1A - channel)); + /* unmask IRQ */ + *ctx[tim].mask |= (1 << (OCIE1A - channel)); +#endif clear_oneshot(tim, channel); @@ -253,7 +282,11 @@ int timer_clear(tim_t tim, int channel) return -1; } +#ifndef CPU_ATMEGA8 *ctx[tim].mask &= ~(1 << (channel + OCIE1A)); +#else + *ctx[tim].mask &= ~(1 << (OCIE1A - channel)); +#endif return 0; } @@ -293,7 +326,11 @@ static inline void _isr(tim_t tim, int chan) avr8_enter_isr(); if (is_oneshot(tim, chan)) { +#ifndef CPU_ATMEGA8 *ctx[tim].mask &= ~(1 << (chan + OCIE1A)); +#else + *ctx[tim].mask &= ~(1 << (OCIE1A - chan)); +#endif } ctx[tim].cb(ctx[tim].arg, chan); diff --git a/cpu/atmega_common/periph/uart.c b/cpu/atmega_common/periph/uart.c index 5e304ec05f63..1c47b586dc0f 100644 --- a/cpu/atmega_common/periph/uart.c +++ b/cpu/atmega_common/periph/uart.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen * 2017 Thomas Perrot + * 2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * @author Hauke Petersen * @author Hinnerk van Bruinehsen * @author Thomas Perrot + * @author Hugues Larrive * * * Support static BAUD rate calculation using STDIO_UART_BAUDRATE. @@ -83,9 +85,16 @@ static uart_isr_ctx_t isr_ctx[UART_NUMOF]; static void _update_brr(uart_t uart, uint16_t brr, bool double_speed) { +#ifndef CPU_ATMEGA8 dev[uart]->BRR = brr; +#else /* on atmega8 BRRH is shared with CSRC */ + dev[uart]->CSRC = (brr >> 8); + dev[uart]->BRRL = (uint8_t)(brr & 0x00ff); +#endif if (double_speed) { -#ifdef CPU_ATMEGA32U4 +#if defined(CPU_ATMEGA8) + dev[uart]->CSRA |= (1 << U2X); +#elif defined(CPU_ATMEGA32U4) dev[uart]->CSRA |= (1 << U2X1); #else dev[uart]->CSRA |= (1 << U2X0); @@ -136,7 +145,9 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) dev[uart]->CSRA = 0; /* configure UART to 8N1 mode */ -#ifdef CPU_ATMEGA32U4 +#if defined(CPU_ATMEGA8) + dev[uart]->CSRC = (1 << UCSZ0) | (1 << UCSZ1); +#elif defined(CPU_ATMEGA32U4) dev[uart]->CSRC = (1 << UCSZ10) | (1 << UCSZ11); #else dev[uart]->CSRC = (1 << UCSZ00) | (1 << UCSZ01); @@ -146,14 +157,18 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) /* enable RX and TX and their respective interrupt */ if (rx_cb) { -#ifdef CPU_ATMEGA32U4 +#if defined(CPU_ATMEGA8) + dev[uart]->CSRB = ((1 << RXCIE) | (1 << TXCIE) | (1 << RXEN) | (1 << TXEN)); +#elif defined(CPU_ATMEGA32U4) dev[uart]->CSRB = ((1 << RXCIE1) | (1 << TXCIE1) | (1 << RXEN1) | (1 << TXEN1)); #else dev[uart]->CSRB = ((1 << RXCIE0) | (1 << TXCIE0) | (1 << RXEN0) | (1 << TXEN0)); #endif } else { -#ifdef CPU_ATMEGA32U4 +#if defined(CPU_ATMEGA8) + dev[uart]->CSRB = ((1 << TXEN) | (1 << TXCIE)); +#elif defined(CPU_ATMEGA32U4) dev[uart]->CSRB = ((1 << TXEN1) | (1 << TXCIE1)); #else dev[uart]->CSRB = ((1 << TXEN0) | (1 << TXCIE0)); @@ -166,7 +181,9 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) void uart_write(uart_t uart, const uint8_t *data, size_t len) { for (size_t i = 0; i < len; i++) { -#ifdef CPU_ATMEGA32U4 +#if defined(CPU_ATMEGA8) + while (!(dev[uart]->CSRA & (1 << UDRE))) {}; +#elif defined(CPU_ATMEGA32U4) while (!(dev[uart]->CSRA & (1 << UDRE1))) {}; #else while (!(dev[uart]->CSRA & (1 << UDRE0))) {} diff --git a/cpu/avr8_common/avr8_cpu.c b/cpu/avr8_common/avr8_cpu.c index 2323236e9736..1c43d168bdc1 100644 --- a/cpu/avr8_common/avr8_cpu.c +++ b/cpu/avr8_common/avr8_cpu.c @@ -3,6 +3,7 @@ * 2017 RWTH Aachen, Josua Arndt * 2018 Matthew Blue * 2021 Gerson Fernando Budke + * 2023 Hugues Larrive * * 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 @@ -22,6 +23,7 @@ * @author Matthew Blue * @author Francisco Acosta * @author Gerson Fernando Budke + * @author Hugues Larrive * * @} */ @@ -31,7 +33,9 @@ #include #include "cpu.h" +#if !defined (CPU_ATMEGA8) #include "cpu_clock.h" +#endif #include "board.h" #include "irq.h" #include "periph/init.h" @@ -99,7 +103,9 @@ void cpu_init(void) wdt_reset(); /* should not be nececessary as done in bootloader */ wdt_disable(); /* but when used without bootloader this is needed */ +#ifndef CPU_ATMEGA8 avr8_clk_init(); +#endif /* Initialize stdio before periph_init() to allow use of DEBUG() there */ #ifdef MODULE_AVR_LIBC_EXTRA diff --git a/cpu/avr8_common/include/cpu.h b/cpu/avr8_common/include/cpu.h index 18c4dcee7141..bd219f2c6480 100644 --- a/cpu/avr8_common/include/cpu.h +++ b/cpu/avr8_common/include/cpu.h @@ -3,6 +3,7 @@ * 2014 Freie Universität Berlin, Hinnerk van Bruinehsen * 2018 RWTH Aachen, Josua Arndt * 2021 Gerson Fernando Budke + * 2023 Hugues Larrive * * 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 @@ -26,6 +27,7 @@ * @author Kaspar Schleiser * @author Josua Arndt * @author Gerson Fernando Budke + * @author Hugues Larrive * */ @@ -38,7 +40,9 @@ #include #include "architecture.h" #include "cpu_conf.h" +#if !defined (CPU_ATMEGA8) #include "cpu_clock.h" +#endif #include "sched.h" #include "thread.h" diff --git a/cpu/avr8_common/ldscripts/avr4.ld b/cpu/avr8_common/ldscripts/avr4.ld new file mode 100644 index 000000000000..aa85ea3c82e3 --- /dev/null +++ b/cpu/avr8_common/ldscripts/avr4.ld @@ -0,0 +1,27 @@ +/* Copyright (C) 2014-2015 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr") +OUTPUT_ARCH(avr:4) + +__TEXT_REGION_LENGTH__ = DEFINED(__TEXT_REGION_LENGTH__) ? __TEXT_REGION_LENGTH__ : 8K; +__DATA_REGION_LENGTH__ = DEFINED(__DATA_REGION_LENGTH__) ? __DATA_REGION_LENGTH__ : 0xffa0; +__EEPROM_REGION_LENGTH__ = DEFINED(__EEPROM_REGION_LENGTH__) ? __EEPROM_REGION_LENGTH__ : 64K; +__FUSE_REGION_LENGTH__ = DEFINED(__FUSE_REGION_LENGTH__) ? __FUSE_REGION_LENGTH__ : 1K; +__LOCK_REGION_LENGTH__ = DEFINED(__LOCK_REGION_LENGTH__) ? __LOCK_REGION_LENGTH__ : 1K; +__SIGNATURE_REGION_LENGTH__ = DEFINED(__SIGNATURE_REGION_LENGTH__) ? __SIGNATURE_REGION_LENGTH__ : 1K; +__USER_SIGNATURE_REGION_LENGTH__ = DEFINED(__USER_SIGNATURE_REGION_LENGTH__) ? __USER_SIGNATURE_REGION_LENGTH__ : 1K; + +MEMORY +{ + text (rx) : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__ + data (rw!x) : ORIGIN = 0x800060, LENGTH = __DATA_REGION_LENGTH__ + eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = __EEPROM_REGION_LENGTH__ + fuse (rw!x) : ORIGIN = 0x820000, LENGTH = __FUSE_REGION_LENGTH__ + lock (rw!x) : ORIGIN = 0x830000, LENGTH = __LOCK_REGION_LENGTH__ + signature (rw!x) : ORIGIN = 0x840000, LENGTH = __SIGNATURE_REGION_LENGTH__ + user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = __USER_SIGNATURE_REGION_LENGTH__ +} + +INCLUDE avr_common.ld diff --git a/cpu/avr8_common/startup.c b/cpu/avr8_common/startup.c index 22c45f2376a9..94d3956d0b7e 100644 --- a/cpu/avr8_common/startup.c +++ b/cpu/avr8_common/startup.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen * 2021 Gerson Fernando Budke + * 2023 Hugues Larrive * * 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 @@ -18,6 +19,7 @@ * @author Josua Arndt * @author Steffen Robertz * @author Gerson Fernando Budke + * @author Hugues Larrive * @} */ @@ -48,7 +50,7 @@ extern void __libc_init_array(void); * avr-libc normally uses the .init9 section for a call to main. This call * seems to be not replaceable without hacking inside the library. We * circumvent the call to main by using section .init7 to call the function - * reset_handler which therefore is the real entry point and section .init8 + * reset_handler which therefore is the real entry point and section .init8 * which should never be reached but just in case jumps to exit. * This way there should be no way to call main directly. */ @@ -57,12 +59,20 @@ void init8_ovr(void) __attribute__((section(".init8"))); __attribute__((used, naked)) void init7_ovr(void) { +#ifdef CPU_ATMEGA8 + __asm__ ("rjmp reset_handler"); +#else __asm__ ("call reset_handler"); +#endif } __attribute__((used, naked)) void init8_ovr(void) { +#ifdef CPU_ATMEGA8 + __asm__ ("rjmp exit"); +#else __asm__ ("jmp exit"); +#endif } /** From 66a37bba55289749d9ef74052306e5ca08aa969d Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Sat, 17 Jun 2023 06:36:36 +0200 Subject: [PATCH 02/11] tests/atmega8: streamlined test bettery --- tests/atmega8/adc/Makefile | 9 + tests/atmega8/adc/README.md | 13 + tests/atmega8/adc/main.c | 60 +++ tests/atmega8/gpio/Makefile | 17 + tests/atmega8/gpio/main.c | 162 ++++++++ tests/atmega8/i2c/Makefile | 15 + tests/atmega8/i2c/hd44780_avr/Makefile | 1 + tests/atmega8/i2c/hd44780_avr/hd44780_avr.c | 351 ++++++++++++++++++ tests/atmega8/i2c/hd44780_avr/hd44780_avr.h | 213 +++++++++++ .../i2c/hd44780_avr/hd44780_avr_internal.h | 101 +++++ .../i2c/hd44780_avr/hd44780_avr_params.h | 110 ++++++ tests/atmega8/i2c/main.c | 45 +++ tests/atmega8/pwm/Makefile | 9 + tests/atmega8/pwm/README.md | 8 + tests/atmega8/pwm/main.c | 97 +++++ tests/atmega8/shell/Makefile | 16 + tests/atmega8/shell/ReadMe.txt | 14 + tests/atmega8/shell/main.c | 94 +++++ tests/atmega8/spi/Makefile | 15 + tests/atmega8/spi/main.c | 214 +++++++++++ 20 files changed, 1564 insertions(+) create mode 100644 tests/atmega8/adc/Makefile create mode 100644 tests/atmega8/adc/README.md create mode 100644 tests/atmega8/adc/main.c create mode 100644 tests/atmega8/gpio/Makefile create mode 100644 tests/atmega8/gpio/main.c create mode 100644 tests/atmega8/i2c/Makefile create mode 100644 tests/atmega8/i2c/hd44780_avr/Makefile create mode 100644 tests/atmega8/i2c/hd44780_avr/hd44780_avr.c create mode 100644 tests/atmega8/i2c/hd44780_avr/hd44780_avr.h create mode 100644 tests/atmega8/i2c/hd44780_avr/hd44780_avr_internal.h create mode 100644 tests/atmega8/i2c/hd44780_avr/hd44780_avr_params.h create mode 100644 tests/atmega8/i2c/main.c create mode 100644 tests/atmega8/pwm/Makefile create mode 100644 tests/atmega8/pwm/README.md create mode 100644 tests/atmega8/pwm/main.c create mode 100644 tests/atmega8/shell/Makefile create mode 100644 tests/atmega8/shell/ReadMe.txt create mode 100644 tests/atmega8/shell/main.c create mode 100644 tests/atmega8/spi/Makefile create mode 100644 tests/atmega8/spi/main.c diff --git a/tests/atmega8/adc/Makefile b/tests/atmega8/adc/Makefile new file mode 100644 index 000000000000..a75b46960448 --- /dev/null +++ b/tests/atmega8/adc/Makefile @@ -0,0 +1,9 @@ +RIOTBASE ?= $(CURDIR)/../../.. +BOARD ?= atmega8 +PORT ?= /dev/ttyACM0 + +include ../../Makefile.tests_common + +FEATURES_REQUIRED = periph_adc + +include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/adc/README.md b/tests/atmega8/adc/README.md new file mode 100644 index 000000000000..7ad2990acf2f --- /dev/null +++ b/tests/atmega8/adc/README.md @@ -0,0 +1,13 @@ +Expected result +=============== +When running this test, you should see the samples of all configured ADC lines +continuously streamed to std-out. + +Background +========== +This test application will initialize each configured ADC lines to sample with +10-bit accuracy. Once configured the application will continuously convert each +available channel and print the conversion results to std-out. + +For verification of the output connect the ADC pins to known voltage levels +and compare the output. diff --git a/tests/atmega8/adc/main.c b/tests/atmega8/adc/main.c new file mode 100644 index 000000000000..57736cf8750a --- /dev/null +++ b/tests/atmega8/adc/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014-2015 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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 tests + * @{ + * + * @file + * @brief Test application for peripheral ADC drivers + * + * @author Hauke Petersen + * @author Hugues Larrive + * + * @} + */ + +#include + +#include "periph/adc.h" + +#define RES ADC_RES_10BIT + +int main(void) +{ + int sample = 0; + + puts("\nRIOT ADC peripheral driver test\n"); + puts("This test will sample all available ADC lines with\n" + "a 10-bit resolution and print the sampled results to STDIO\n\n"); + + /* initialize all available ADC lines */ + for (unsigned i = 0; i < ADC_NUMOF; i++) { + if (adc_init(ADC_LINE(i)) < 0) { + printf("Initialization of ADC_LINE(%u) failed\n", i); + return 1; + } else { + printf("Successfully initialized ADC_LINE(%u)\n", i); + } + } + + while (1) { + for (unsigned i = 0; i < ADC_NUMOF; i++) { + sample = adc_sample(ADC_LINE(i), RES); + if (sample < 0) { + printf("ADC_LINE(%u): selected resolution not applicable\n", i); + } else { + printf("%i\t", sample); + } + } + printf("\n"); + } + + return 0; +} diff --git a/tests/atmega8/gpio/Makefile b/tests/atmega8/gpio/Makefile new file mode 100644 index 000000000000..d0174894c503 --- /dev/null +++ b/tests/atmega8/gpio/Makefile @@ -0,0 +1,17 @@ +RIOTBASE ?= $(CURDIR)/../../.. +BOARD ?= atmega8 +PORT ?= /dev/ttyACM0 + +include ../../Makefile.tests_common + +FEATURES_REQUIRED += periph_gpio + +USEMODULE += shell + +CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE +DISABLE_MODULE += core_msg +DEVELHELP = 0 +CFLAGS += -DTHREAD_STACKSIZE_IDLE=74 +CFLAGS += -DTHREAD_STACKSIZE_MAIN=168 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/gpio/main.c b/tests/atmega8/gpio/main.c new file mode 100644 index 000000000000..05997e67078e --- /dev/null +++ b/tests/atmega8/gpio/main.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2014,2017 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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 tests + * @{ + * + * @file + * @brief Test application for GPIO peripheral drivers + * + * @author Hauke Petersen + * @author Hugues Larrive + * + * @} + */ + +#include +#include + +#include "shell.h" +#include "periph/gpio.h" + +#define SHELL_BUFSIZE (16) + +static int init_pin(int argc, char **argv, gpio_mode_t mode) +{ + int po, pi; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + po = atoi(argv[1]); + pi = atoi(argv[2]); + + if (gpio_init(GPIO_PIN(po, pi), mode) < 0) { + printf("Error to initialize GPIO_PIN(%i, %02i)\n", po, pi); + return 1; + } + + return 0; +} + +static int init_out(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OUT); +} + +static int init_in(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN); +} + +static int init_in_pu(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN_PU); +} + +static int init_in_pd(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN_PD); +} + +static int init_od(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OD); +} + +static int init_od_pu(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OD_PU); +} + +static int cmd_read(int argc, char **argv) +{ + int port, pin; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + port = atoi(argv[1]); + pin = atoi(argv[2]); + + if (gpio_read(GPIO_PIN(port, pin))) { + printf("GPIO_PIN(%i.%02i) is HIGH\n", port, pin); + } + else { + printf("GPIO_PIN(%i.%02i) is LOW\n", port, pin); + } + + return 0; +} + +static int cmd_set(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_set(GPIO_PIN(atoi(argv[1]), atoi(argv[2]))); + + return 0; +} + +static int cmd_clear(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_clear(GPIO_PIN(atoi(argv[1]), atoi(argv[2]))); + + return 0; +} + +static int cmd_toggle(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_toggle(GPIO_PIN(atoi(argv[1]), atoi(argv[2]))); + + return 0; +} + +static const shell_command_t shell_commands[] = { + { "init_out", "init as output (push-pull mode)", init_out }, + { "init_in", "init as input w/o pull resistor", init_in }, + { "init_in_pu", "init as input with pull-up", init_in_pu }, + { "init_in_pd", "init as input with pull-down", init_in_pd }, + { "init_od", "init as output (open-drain without pull resistor)", init_od }, + { "init_od_pu", "init as output (open-drain with pull-up)", init_od_pu }, + { "read", "read pin status", cmd_read }, + { "set", "set pin to HIGH", cmd_set }, + { "clear", "set pin to LOW", cmd_clear }, + { "toggle", "toggle pin", cmd_toggle }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("GPIO peripheral driver test\n"); + + /* start the shell */ + char line_buf[SHELL_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_BUFSIZE); + + return 0; +} diff --git a/tests/atmega8/i2c/Makefile b/tests/atmega8/i2c/Makefile new file mode 100644 index 000000000000..386ba022d91e --- /dev/null +++ b/tests/atmega8/i2c/Makefile @@ -0,0 +1,15 @@ +RIOTBASE ?= $(CURDIR)/../../.. +BOARD ?= atmega8 +PORT ?= /dev/ttyACM0 + +include ../../Makefile.tests_common + +USEMODULE += pcf8574a +EXTERNAL_MODULE_DIRS += ./ +USEMODULE += hd44780_avr + +CFLAGS += -DHD44780_PARAM_COLS=20 +CFLAGS += -DHD44780_PARAM_ROWS=4 +CFLAGS += -DPCF8574A_BASE_ADDR=0x3F + +include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/i2c/hd44780_avr/Makefile b/tests/atmega8/i2c/hd44780_avr/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/tests/atmega8/i2c/hd44780_avr/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/atmega8/i2c/hd44780_avr/hd44780_avr.c b/tests/atmega8/i2c/hd44780_avr/hd44780_avr.c new file mode 100644 index 000000000000..8166b97b939a --- /dev/null +++ b/tests/atmega8/i2c/hd44780_avr/hd44780_avr.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * 2023 Hugues Larrive + * + * 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 drivers_hd44780 + * + * @{ + * @file + * @brief Driver for the HD44780 LCD + * + * @note The display is also known as LCM1602C from Arduino kits + * + * @author Sebastian Meiling + * @author Hugues Larrive + * + * @} + */ + +#include + +#include "log.h" +#ifdef MODULE_PCF857X +#include "pcf857x.h" +#include "pcf857x_params.h" +#else +#include "periph/gpio.h" +#endif +/** As xtimer was too big to fit in atmega8's 8 Kbytes flash memory, this + * driver was hacked to remove xtimer dependency */ +#define F_CPU (CLOCK_CORECLOCK) +#define __DELAY_BACKWARD_COMPATIBLE__ +#include "util/delay.h" +static void xtimer_usleep(uint32_t us) +{ + _delay_us(us); +} +#define ZTIMER_USEC 0 + +#include "hd44780_avr.h" +#include "hd44780_avr_internal.h" +/************************** end of the hack ****************************/ + + +#ifdef MODULE_PCF857X +static pcf857x_t _pcf857x_dev; +#endif + +static inline void _command(const hd44780_t *dev, uint8_t value); +static void _pulse(const hd44780_t *dev); +static void _send(const hd44780_t *dev, uint8_t value, hd44780_state_t state); +static void _write_bits(const hd44780_t *dev, uint8_t bits, uint8_t value); + +static inline void _gpio_set(gpio_t pin); +static inline void _gpio_clear(gpio_t pin); +static inline int _gpio_init(gpio_t pin, gpio_mode_t mode); + +/** + * @brief Send a command to the display + * + * @param[in] dev device descriptor of display to initialize + * @param[in] value the command + */ +static inline void _command(const hd44780_t *dev, uint8_t value) +{ + _send(dev, value, HD44780_OFF); +} + +/** + * @brief Send a pulse to the display to enable new state + * + * @param[in] dev device descriptor of display to initialize + */ +static void _pulse(const hd44780_t *dev) +{ + _gpio_clear(dev->p.enable); + xtimer_usleep(HD44780_PULSE_WAIT_SHORT); + _gpio_set(dev->p.enable); + xtimer_usleep(HD44780_PULSE_WAIT_SHORT); + _gpio_clear(dev->p.enable); + xtimer_usleep(HD44780_PULSE_WAIT_LONG); +} + +/** + * @brief Send a data value to the display + * + * @param[in] dev device descriptor of display to initialize + * @param[in] value the data value, either char or command + * @param[in] state send state, to distinguish chars and commands + */ +static void _send(const hd44780_t *dev, uint8_t value, hd44780_state_t state) +{ + (state == HD44780_ON) ? _gpio_set(dev->p.rs) : _gpio_clear(dev->p.rs); + /* if RW pin is available, set it to LOW */ + if (gpio_is_valid(dev->p.rw)) { + _gpio_clear(dev->p.rw); + } + /* write data in 8Bit or 4Bit mode */ + if (dev->flag & HD44780_8BITMODE) { + _write_bits(dev, 8, value); + } + else { + _write_bits(dev, 4, value>>4); + _write_bits(dev, 4, value); + } +} + +static void _write_bits(const hd44780_t *dev, uint8_t bits, uint8_t value) +{ + for (unsigned i = 0; i < bits; ++i) { + if ((value >> i) & 0x01) { + _gpio_set(dev->p.data[i]); + } + else { + _gpio_clear(dev->p.data[i]); + } + } + _pulse(dev); +} + +static inline int _gpio_init(gpio_t pin, gpio_mode_t mode) +{ +#ifdef MODULE_PCF857X + return pcf857x_gpio_init(&_pcf857x_dev, pin, mode); +#else + return gpio_init(pin, mode); +#endif +} + +static inline void _gpio_set(gpio_t pin) +{ +#ifdef MODULE_PCF857X + pcf857x_gpio_set(&_pcf857x_dev, pin); +#else + gpio_set(pin); +#endif +} + +static inline void _gpio_clear(gpio_t pin) +{ +#ifdef MODULE_PCF857X + pcf857x_gpio_clear(&_pcf857x_dev, pin); +#else + gpio_clear(pin); +#endif +} + +int hd44780_init(hd44780_t *dev, const hd44780_params_t *params) +{ + /* write config params to device descriptor */ + dev->p = *params; + /* verify cols and rows */ + if ((dev->p.cols > HD44780_MAX_COLS) || (dev->p.rows > HD44780_MAX_ROWS) + || (dev->p.rows * dev->p.cols > 80)) { + LOG_ERROR("hd44780_init: invalid LCD size!\n"); + return -1; + } + dev->flag = 0; + /* set mode depending on configured pins */ + if (gpio_is_valid(dev->p.data[4])) { + dev->flag |= HD44780_8BITMODE; + } + else { + dev->flag |= HD44780_4BITMODE; + } + /* set flag for 1 or 2 row mode, 4 rows are 2 rows split half */ + if (dev->p.rows > 1) { + dev->flag |= HD44780_2LINE; + } + else { + dev->flag |= HD44780_1LINE; + } + /* set char size to 5x8 as default, 5x10 is hardly used by LCDs anyway */ + dev->flag |= HD44780_5x8DOTS; + /* calc and set row offsets, depending on number of columns */ + dev->roff[0] = 0x00; + dev->roff[1] = 0x40; + dev->roff[2] = 0x00 + dev->p.cols; + dev->roff[3] = 0x40 + dev->p.cols; + +#ifdef MODULE_PCF857X + /* + * TODO: We need an approach for defining and initializing the PCF8574. + * With this approach, only one PCF8574 could exist in the system + */ + pcf857x_init(&_pcf857x_dev, &pcf857x_params[0]); +#endif + + _gpio_init(dev->p.rs, GPIO_OUT); + /* RW (read/write) of LCD not required, set it to GPIO_UNDEF */ + if (gpio_is_valid(dev->p.rw)) { + _gpio_init(dev->p.rw, GPIO_OUT); + } + _gpio_init(dev->p.enable, GPIO_OUT); + /* configure all data pins as output */ + for (int i = 0; i < ((dev->flag & HD44780_8BITMODE) ? 8 : 4); ++i) { + _gpio_init(dev->p.data[i], GPIO_OUT); + } + /* see hitachi HD44780 datasheet pages 45/46 for init specs */ + xtimer_usleep(HD44780_INIT_WAIT_XXL); + _gpio_clear(dev->p.rs); + _gpio_clear(dev->p.enable); + if (gpio_is_valid(dev->p.rw)) { + _gpio_clear(dev->p.rw); + } + /* put the LCD into 4 bit or 8 bit mode */ + if (!(dev->flag & HD44780_8BITMODE)) { + /* see hitachi HD44780 datasheet figure 24, pg 46 */ + _write_bits(dev, 4, 0x03); + xtimer_usleep(HD44780_INIT_WAIT_LONG); + + _write_bits(dev, 4, 0x03); + xtimer_usleep(HD44780_INIT_WAIT_LONG); + + _write_bits(dev, 4, 0x03); + xtimer_usleep(HD44780_INIT_WAIT_SHORT); + + _write_bits(dev, 4, 0x02); + } else { + /* see hitachi HD44780 datasheet page 45 figure 23 */ + _command(dev, HD44780_FUNCTIONSET | dev->flag); + xtimer_usleep(HD44780_INIT_WAIT_LONG); /* wait more than 4.1ms */ + + _command(dev, HD44780_FUNCTIONSET | dev->flag); + xtimer_usleep(HD44780_INIT_WAIT_SHORT); + + _command(dev, HD44780_FUNCTIONSET | dev->flag); + } + _command(dev, HD44780_FUNCTIONSET | dev->flag); + + /* turn the display on with no cursor or blinking default, and clear */ + dev->ctrl = HD44780_DISPLAYON | HD44780_CURSOROFF | HD44780_BLINKOFF; + hd44780_display(dev, HD44780_ON); + hd44780_clear(dev); + /* Initialize to default text direction for western languages */ + dev->mode = HD44780_ENTRYLEFT | HD44780_ENTRYSHIFTDECREMENT; + _command(dev, HD44780_ENTRYMODESET | dev->mode); + + return 0; +} + +void hd44780_clear(const hd44780_t *dev) +{ + _command(dev, HD44780_CLEARDISPLAY); + xtimer_usleep(HD44780_CMD_WAIT); +} + +void hd44780_home(const hd44780_t *dev) +{ + _command(dev, HD44780_RETURNHOME); + xtimer_usleep(HD44780_CMD_WAIT); +} + +void hd44780_set_cursor(const hd44780_t *dev, uint8_t col, uint8_t row) +{ + if (row >= dev->p.rows) { + row = dev->p.rows - 1; + } + _command(dev, HD44780_SETDDRAMADDR | (col + dev->roff[row])); +} + +void hd44780_display(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->ctrl |= HD44780_DISPLAYON; + } + else { + dev->ctrl &= ~HD44780_DISPLAYON; + } + _command(dev, HD44780_DISPLAYCONTROL | dev->ctrl); +} + +void hd44780_cursor(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->ctrl |= HD44780_CURSORON; + } + else { + dev->ctrl &= ~HD44780_CURSORON; + } + _command(dev, HD44780_DISPLAYCONTROL | dev->ctrl); +} + +void hd44780_blink(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->ctrl |= HD44780_BLINKON; + } + else { + dev->ctrl &= ~HD44780_BLINKON; + } + _command(dev, HD44780_DISPLAYCONTROL | dev->ctrl); +} + +void hd44780_scroll_left(const hd44780_t *dev) +{ + _command(dev, HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVELEFT); +} + +void hd44780_scroll_right(const hd44780_t *dev) { + _command(dev, HD44780_CURSORSHIFT | HD44780_DISPLAYMOVE | HD44780_MOVERIGHT); +} + +void hd44780_left2right(hd44780_t *dev) +{ + dev->mode |= HD44780_ENTRYLEFT; + _command(dev, HD44780_ENTRYMODESET | dev->mode); +} + +void hd44780_right2left(hd44780_t *dev) +{ + dev->mode &= ~HD44780_ENTRYLEFT; + _command(dev, HD44780_ENTRYMODESET | dev->mode); +} + +void hd44780_autoscroll(hd44780_t *dev, hd44780_state_t state) +{ + if (state == HD44780_ON) { + dev->mode |= HD44780_ENTRYSHIFTINCREMENT; + } + else { + dev->mode &= ~HD44780_ENTRYSHIFTINCREMENT; + } + _command(dev, HD44780_ENTRYMODESET | dev->mode); +} + +void hd44780_create_char(const hd44780_t *dev, uint8_t location, uint8_t charmap[]) +{ + location &= 0x7; /* 8 locations (0-7) possible */ + _command(dev, HD44780_SETCGRAMADDR | (location << 3)); + for (unsigned i = 0; i < HD44780_CGRAM_SIZE; ++i) { + hd44780_write(dev, charmap[i]); + } +} + +void hd44780_write(const hd44780_t *dev, uint8_t value) +{ + _send(dev, value, HD44780_ON); +} + +void hd44780_print(const hd44780_t *dev, const char *data ) +{ + while (*data != '\0') { + hd44780_write(dev, *data++); + } +} diff --git a/tests/atmega8/i2c/hd44780_avr/hd44780_avr.h b/tests/atmega8/i2c/hd44780_avr/hd44780_avr.h new file mode 100644 index 000000000000..a8b8f2a3571e --- /dev/null +++ b/tests/atmega8/i2c/hd44780_avr/hd44780_avr.h @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * 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. + */ + +/** + * @defgroup drivers_hd44780 HD44780 LCD driver + * @ingroup drivers_display + * @brief Driver for the Hitachi HD44780 LCD driver + * + * @note The driver currently supports direct addressing, no I2C + * + * @{ + * + * @file + * @brief Interface definition for the HD44780 LCD driver + * + * @author Sebastian Meiling + */ +#ifndef HD44780_AVR_H +#define HD44780_AVR_H + +#include + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximal number of columns supported by the driver + */ +#define HD44780_MAX_COLS (40U) + +/** + * @brief Maximal number of rows supported by the driver + */ +#define HD44780_MAX_ROWS (4U) + +/** + * @brief Number of data pins for communication 4 or 8. + */ +#define HD44780_MAX_PINS (8U) + +/** + * @brief Size of RAM for custom chars + * + * Generally the driver could support 8 chars of size 5x8 or 4 of size 5x10. + * However, most displays only use the former, which is (hard wired) default. + */ +#define HD44780_CGRAM_SIZE (8U) + +/** + * @brief Parameters needed for device initialization + */ +typedef struct { + uint8_t cols; /**< number of LCD cols */ + uint8_t rows; /**< number of LCD rows */ + gpio_t rs; /**< rs gpio pin */ + gpio_t rw; /**< rw gpio pin */ + gpio_t enable; /**< enable gpio pin */ + gpio_t data[HD44780_MAX_PINS]; /**< data gpio pins */ +} hd44780_params_t; + +/** + * @brief Device descriptor for HD44780 LCD + */ +typedef struct { + hd44780_params_t p; /**< LCD config parameters */ + uint8_t flag; /**< LCD functional flags */ + uint8_t ctrl; /**< LCD control flags */ + uint8_t mode; /**< LCD mode flags */ + uint8_t roff[HD44780_MAX_ROWS]; /**< offsets for LCD rows */ +} hd44780_t; + +/** + * @brief Simple state values + */ +typedef enum { + HD44780_OFF, /**< disable feature */ + HD44780_ON /**< enable feature */ +} hd44780_state_t; + +/** + * @brief Initialize the given driver + * + * @param[out] dev device descriptor of display to initialize + * @param[in] params configuration parameters + * + * @return 0 on success, otherwise error + */ +int hd44780_init(hd44780_t *dev, const hd44780_params_t *params); + +/** + * @brief Clear display, delete all chars + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_clear(const hd44780_t *dev); + +/** + * @brief Reset cursor to row 0 and column 0 + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_home(const hd44780_t *dev); + +/** + * @brief Set cursor to specific position in column and row + * + * @param[in] dev device descriptor of LCD + * @param[in] col column position + * @param[in] row row position + */ +void hd44780_set_cursor(const hd44780_t *dev, uint8_t col, uint8_t row); + +/** + * @brief Turn display on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state display on or off + */ +void hd44780_display(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Show cursor, on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state cursor on or off + */ +void hd44780_cursor(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Blink cursor, on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state blink on or off + */ +void hd44780_blink(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Enable left scrolling + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_scroll_left(const hd44780_t *dev); + +/** + * @brief Enable right scrolling + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_scroll_right(const hd44780_t *dev); + +/** + * @brief Set display direction left to right + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_left2right(hd44780_t *dev); + +/** + * @brief Set display direction right to left + * + * @param[in] dev device descriptor of LCD + */ +void hd44780_right2left(hd44780_t *dev); + +/** + * @brief Display autoscroll on or off + * + * @param[in] dev device descriptor of LCD + * @param[in] state scroll on or off + */ +void hd44780_autoscroll(hd44780_t *dev, hd44780_state_t state); + +/** + * @brief Create and store a custom character on display memory + * + * @param[in] dev device descriptor of LCD + * @param[in] location memory location + * @param[in] charmap character bitmap + * + * @pre charmap has to be of size HD44780_CGRAM_SIZE, which is 8 by default + */ +void hd44780_create_char(const hd44780_t *dev, uint8_t location, uint8_t charmap[]); + +/** + * @brief Write a single character on the LCD + * + * @param[in] dev device descriptor of LCD + * @param[in] value the character to print, i.e., memory location + */ +void hd44780_write(const hd44780_t *dev, uint8_t value); + +/** + * @brief Write a string on the LCD + * + * @param[in] dev device descriptor of LCD + * @param[in] data the string to print + */ +void hd44780_print(const hd44780_t *dev, const char *data); + +#ifdef __cplusplus +} +#endif + +#endif /* HD44780_AVR_H */ +/** @} */ diff --git a/tests/atmega8/i2c/hd44780_avr/hd44780_avr_internal.h b/tests/atmega8/i2c/hd44780_avr/hd44780_avr_internal.h new file mode 100644 index 000000000000..178b6873cc5f --- /dev/null +++ b/tests/atmega8/i2c/hd44780_avr/hd44780_avr_internal.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * 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 drivers_hd44780 + * + * @{ + * @file + * @brief Internal config and parameters for the HD44780 display + * + * @author Sebastian Meiling + */ + +#ifndef HD44780_AVR_INTERNAL_H +#define HD44780_AVR_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name HD44780 LCD commands + * @{ + */ +#define HD44780_CLEARDISPLAY (0x01) +#define HD44780_RETURNHOME (0x02) +#define HD44780_ENTRYMODESET (0x04) +#define HD44780_DISPLAYCONTROL (0x08) +#define HD44780_CURSORSHIFT (0x10) +#define HD44780_FUNCTIONSET (0x20) +#define HD44780_SETCGRAMADDR (0x40) +#define HD44780_SETDDRAMADDR (0x80) +/** @} */ + +/** + * @name HD44780 LCD entry modes flags + * @{ + */ +#define HD44780_ENTRYRIGHT (0x00) +#define HD44780_ENTRYLEFT (0x02) +#define HD44780_ENTRYSHIFTINCREMENT (0x01) +#define HD44780_ENTRYSHIFTDECREMENT (0x00) +/** @} */ + +/** + * @name HD44780 LCD control flags + * @{ + */ +#define HD44780_DISPLAYON (0x04) +#define HD44780_DISPLAYOFF (0x00) +#define HD44780_CURSORON (0x02) +#define HD44780_CURSOROFF (0x00) +#define HD44780_BLINKON (0x01) +#define HD44780_BLINKOFF (0x00) +/** @} */ + +/** + * @name HD44780 display and cursor shift flags + * @{ + */ +#define HD44780_DISPLAYMOVE (0x08) +#define HD44780_CURSORMOVE (0x00) +#define HD44780_MOVERIGHT (0x04) +#define HD44780_MOVELEFT (0x00) +/**@}*/ + +/** + * @name HD44780 LCD functional flags + * @{ + */ +#define HD44780_8BITMODE (0x10) +#define HD44780_4BITMODE (0x00) +#define HD44780_2LINE (0x08) +#define HD44780_1LINE (0x00) +#define HD44780_5x10DOTS (0x04) +#define HD44780_5x8DOTS (0x00) +/** @} */ + +/** + * @name HD44780 LCD timings + * @{ + */ +#define HD44780_CMD_WAIT (2000U) +#define HD44780_INIT_WAIT_XXL (50000U) +#define HD44780_INIT_WAIT_LONG (4500U) +#define HD44780_INIT_WAIT_SHORT (150U) +#define HD44780_PULSE_WAIT_SHORT (1U) +#define HD44780_PULSE_WAIT_LONG (100U) +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* HD44780_AVR_INTERNAL_H */ +/** @} */ diff --git a/tests/atmega8/i2c/hd44780_avr/hd44780_avr_params.h b/tests/atmega8/i2c/hd44780_avr/hd44780_avr_params.h new file mode 100644 index 000000000000..e402f76d9b27 --- /dev/null +++ b/tests/atmega8/i2c/hd44780_avr/hd44780_avr_params.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * 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 drivers_hd44780 + * + * @{ + * @file + * @brief Pinout config for the HD44780 display + * + * @author Sebastian Meiling + */ +#ifndef HD44780_AVR_PARAMS_H +#define HD44780_AVR_PARAMS_H + +#include "board.h" +#ifdef MODULE_PCF857X +#include "pcf857x.h" +#else +#include "periph/gpio.h" +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* The default pins corresponds to Arduino pins on the Arduino Zero board. */ + +#ifndef HD44780_PARAM_COLS +#define HD44780_PARAM_COLS (16U) +#endif +#ifndef HD44780_PARAM_ROWS +#define HD44780_PARAM_ROWS (2U) +#endif + +#ifndef MODULE_PCF857X + +#ifndef HD44780_PARAM_PIN_RS +#define HD44780_PARAM_PIN_RS GPIO_PIN(0, 14) /* Arduino D2 */ +#endif +#ifndef HD44780_PARAM_PIN_RW +#define HD44780_PARAM_PIN_RW GPIO_UNDEF +#endif +#ifndef HD44780_PARAM_PIN_ENABLE +#define HD44780_PARAM_PIN_ENABLE GPIO_PIN(0, 9) /* Arduino D3 */ +#endif +#ifndef HD44780_PARAM_PINS_DATA +#define HD44780_PARAM_PINS_DATA { GPIO_PIN(0, 8), /* Arduino D4 */ \ + GPIO_PIN(0, 15), /* Arduino D5 */ \ + GPIO_PIN(0, 20), /* Arduino D6 */ \ + GPIO_PIN(0, 21), /* Arduino D7 */ \ + GPIO_UNDEF, \ + GPIO_UNDEF, \ + GPIO_UNDEF, \ + GPIO_UNDEF } +#endif + +#else /* !MODULE_PCF857X */ + +#ifndef HD44780_PARAM_PIN_RS +#define HD44780_PARAM_PIN_RS PCF857X_GPIO_PIN(0, 0) /* Bit 0 */ +#endif +#ifndef HD44780_PARAM_PIN_RW +#define HD44780_PARAM_PIN_RW PCF857X_GPIO_PIN(0, 1) /* Bit 1 */ +#endif +#ifndef HD44780_PARAM_PIN_ENABLE +#define HD44780_PARAM_PIN_ENABLE PCF857X_GPIO_PIN(0, 2) /* Bit 2 */ +#endif +#ifndef HD44780_PARAM_PINS_DATA +#define HD44780_PARAM_PINS_DATA { PCF857X_GPIO_PIN(0, 4), /* Bit 4 */ \ + PCF857X_GPIO_PIN(0, 5), /* Bit 5 */ \ + PCF857X_GPIO_PIN(0, 6), /* Bit 6 */ \ + PCF857X_GPIO_PIN(0, 7), /* Bit 7 */ \ + GPIO_UNDEF, \ + GPIO_UNDEF, \ + GPIO_UNDEF, \ + GPIO_UNDEF } +#endif + +#endif /* !MODULE_PCF857X */ + +#ifndef HD44780_PARAMS +#define HD44780_PARAMS { .cols = HD44780_PARAM_COLS, \ + .rows = HD44780_PARAM_ROWS, \ + .rs = HD44780_PARAM_PIN_RS, \ + .rw = HD44780_PARAM_PIN_RW, \ + .enable = HD44780_PARAM_PIN_ENABLE, \ + .data = HD44780_PARAM_PINS_DATA } +#endif + +/** + * @brief LCM1602C configuration + */ +static const hd44780_params_t hd44780_params[] = +{ + HD44780_PARAMS, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* HD44780_AVR_PARAMS_H */ +/** @} */ diff --git a/tests/atmega8/i2c/main.c b/tests/atmega8/i2c/main.c new file mode 100644 index 000000000000..c828bc930cc7 --- /dev/null +++ b/tests/atmega8/i2c/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * 2023 Hugues Larrive + * + * 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 tests + * @{ + * + * @file + * @brief Application to test i2c on atmega8 using an hacked + * hd44780 LCD driver and a pcf8574a i2c I/O expander. + * + * @author Sebastian Meiling + * @author Hugues Larrive + * + * @} + */ + +#include + +#include "hd44780_avr/hd44780_avr.h" +#include "hd44780_avr/hd44780_avr_params.h" + +int main(void) +{ + hd44780_t dev; + /* init display */ + hd44780_init(&dev, &hd44780_params[0]); + hd44780_clear(&dev); + hd44780_home(&dev); + hd44780_print(&dev, "Test i2c on atmega8"); + hd44780_set_cursor(&dev, 3, 1); + hd44780_print(&dev, "RIOT is here!"); + hd44780_set_cursor(&dev, 0, 2); + hd44780_print(&dev, "You can read this so"); + hd44780_set_cursor(&dev, 2, 3); + hd44780_print(&dev, "i2c is working!"); + + return 0; +} diff --git a/tests/atmega8/pwm/Makefile b/tests/atmega8/pwm/Makefile new file mode 100644 index 000000000000..8ea3df5a4a63 --- /dev/null +++ b/tests/atmega8/pwm/Makefile @@ -0,0 +1,9 @@ +RIOTBASE ?= $(CURDIR)/../../.. +BOARD ?= atmega8 +PORT ?= /dev/ttyACM0 + +include ../../Makefile.tests_common + +FEATURES_REQUIRED = periph_pwm + +include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/pwm/README.md b/tests/atmega8/pwm/README.md new file mode 100644 index 000000000000..23b0c052e0d5 --- /dev/null +++ b/tests/atmega8/pwm/README.md @@ -0,0 +1,8 @@ +Expected result +=============== +If everything is running as supposed to, you should see a 1KHz PWM with oscillating duty cycle +on each channel of the selected PWM device. + +Background +========== +Test for the low-level PWM driver. diff --git a/tests/atmega8/pwm/main.c b/tests/atmega8/pwm/main.c new file mode 100644 index 000000000000..b470bf3bfb70 --- /dev/null +++ b/tests/atmega8/pwm/main.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2014-2015 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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 tests + * @{ + * + * @file + * @brief Test for low-level PWM drivers + * + * This test initializes the given PWM device to run at 1KHz with a 1000 step + * resolution. + * + * The PWM is then continuously oscillating it's duty cycle between 0% to 100% + * every 1s on every channel. + * + * @author Hauke Petersen + * @author Semjon Kerner + * @author Hugues Larrive + * + * @} + */ + +#include +#include +#include +#include + +#include "periph/pwm.h" + +#define F_CPU (CLOCK_CORECLOCK) +#define __DELAY_BACKWARD_COMPATIBLE__ +#include "util/delay.h" + +#define OSC_INTERVAL (400) /* 400us */ +#define OSC_STEP (10) +#define OSC_MODE PWM_LEFT +#define OSC_FREQU (1000U) +#define OSC_STEPS (256U) +#define PWR_SLEEP (1U) + +static uint32_t initialized; + +static int _oscillate(void) +{ + int state = 0; + int step = OSC_STEP; + + puts("\nRIOT PWM test"); + puts("Connect an LED or scope to PWM pins to see something.\n"); + + printf("Available PWM device between 0 and %d\n", PWM_NUMOF - 1); + for (unsigned i = 0; i < PWM_NUMOF; i++) { + uint32_t real_f = pwm_init(PWM_DEV(i), OSC_MODE, OSC_FREQU, OSC_STEPS); + if (real_f == 0) { + printf("Error: initializing PWM_%u.\n", i); + return 1; + } + else { + printf("Initialized PWM_%u @ %" PRIu32 "Hz.\n", i, real_f); + } + } + + puts("\nLetting the PWM pin (PB3 on atmega8) oscillate now..."); + while (1) { + for (unsigned i = 0; i < PWM_NUMOF; i++) { + for (uint8_t chan = 0; chan < pwm_channels(PWM_DEV(i)); chan++) { + pwm_set(PWM_DEV(i), chan, state); + } + } + + state += step; + if (state <= 0 || state >= (int)OSC_STEPS) { + step = -step; + } + + _delay_us(OSC_INTERVAL); + } + + return 0; +} + +int main(void) +{ + puts("PWM peripheral driver test\n"); + initialized = 0; + + _oscillate(); + + return 0; +} diff --git a/tests/atmega8/shell/Makefile b/tests/atmega8/shell/Makefile new file mode 100644 index 000000000000..dce740465bfe --- /dev/null +++ b/tests/atmega8/shell/Makefile @@ -0,0 +1,16 @@ +RIOTBASE ?= $(CURDIR)/../../.. +BOARD ?= atmega8 +PORT ?= /dev/ttyACM0 + +include ../../Makefile.tests_common + +USEMODULE += shell_cmds_default +#~ USEMODULE += ps +DEVELHELP = 0 + +CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE +DISABLE_MODULE += core_msg +CFLAGS += -DTHREAD_STACKSIZE_IDLE=74 +CFLAGS += -DTHREAD_STACKSIZE_MAIN=150 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/shell/ReadMe.txt b/tests/atmega8/shell/ReadMe.txt new file mode 100644 index 000000000000..16bf30e2416f --- /dev/null +++ b/tests/atmega8/shell/ReadMe.txt @@ -0,0 +1,14 @@ +This application shows how to use own or the system shell commands. In order to use +the system shell commands: + +1. Additionally to the module: shell, shell_commands, + the module for the corresponding system command is to include, e.g. + module ps for the ps command (cf. the Makefile in the application root + directory). +2. Start the shell like this: + 2.1 reserve buffer: + char line_buf[SHELL_DEFAULT_BUFSIZE]; + 2.1a run shell only with system commands: + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + 2.1b run shell with provided commands in addition to system commands: + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); diff --git a/tests/atmega8/shell/main.c b/tests/atmega8/shell/main.c new file mode 100644 index 000000000000..aaf48314752a --- /dev/null +++ b/tests/atmega8/shell/main.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2013 Kaspar Schleiser + * Copyright (C) 2013 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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. + */ + +/** + * @file + * @brief shows how to set up own and use the system shell commands. + * By typing help in the serial console, all the supported commands + * are listed. + * + * @author Kaspar Schleiser + * @author Zakaria Kasmi + * @author Hugues Larrive + * + */ + +#include +#include + +#include "shell.h" + +/* define buffer to be used by the shell. Note: This is intentionally + * smaller than 64 bytes, as the EDBG integrated UART bridge of the samr21-xpro + * (and likely all other EDBG boards) drops chars when sending more than 64 + * bytes at a time. This results in the buffer overflow test failing. */ +static char line_buf[16]; + +static int print_teststart(int argc, char **argv) +{ + (void)argc; + (void)argv; + printf("[TEST_START]\n"); + + return 0; +} + +static int print_testend(int argc, char **argv) +{ + (void)argc; + (void)argv; + printf("[TEST_END]\n"); + + return 0; +} + +static int print_shell_bufsize(int argc, char **argv) +{ + (void)argc; + (void)argv; + printf("%d\n", sizeof(line_buf)); + + return 0; +} + +static int print_echo(int argc, char **argv) +{ + for (int i = 0; i < argc; ++i) { + printf("\"%s\"", argv[i]); + } + puts(""); + + return 0; +} + +static int print_empty(int argc, char **argv) +{ + (void)argc; + (void)argv; + return 0; +} + +static const shell_command_t shell_commands[] = { + { "start_test", "starts a test", print_teststart }, + { "end_test", "ends a test", print_testend }, + { "bufsize", "Get the shell's buffer size", print_shell_bufsize }, + { "echo", "prints the input command", print_echo }, + { "empty", "print nothing on command", print_empty }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + printf("test_shell.\n"); + + shell_run(shell_commands, line_buf, sizeof(line_buf)); + + return 0; +} diff --git a/tests/atmega8/spi/Makefile b/tests/atmega8/spi/Makefile new file mode 100644 index 000000000000..6a133d9ee59c --- /dev/null +++ b/tests/atmega8/spi/Makefile @@ -0,0 +1,15 @@ +RIOTBASE ?= $(CURDIR)/../../.. +BOARD ?= atmega8 +PORT ?= /dev/ttyACM0 + +include ../../Makefile.tests_common + +FEATURES_REQUIRED += periph_spi + +DEVELHELP = 0 +CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE +DISABLE_MODULE += core_msg +CFLAGS += -DTHREAD_STACKSIZE_IDLE=64 +CFLAGS += -DTHREAD_STACKSIZE_MAIN=128 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/spi/main.c b/tests/atmega8/spi/main.c new file mode 100644 index 000000000000..545f939a8db3 --- /dev/null +++ b/tests/atmega8/spi/main.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * 2023 Hugues Larrive + * + * 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 tests + * @{ + * + * @file + * @brief Application for testing low-level SPI driver implementations + * + * This implementation covers both, master and slave configurations. + * + * @author Hauke Petersen + * @author Hugues Larrive + * + * @} + */ + +#include +#include +#include + +#include "shell.h" +#include "ps.h" +#include "periph/spi.h" + +#define DEFAULT_SPI_CS_PORT 1 +#define DEFAULT_SPI_CS_PIN 2 + +/** + * @brief Some parameters used for benchmarking + */ +#define BENCH_REDOS (1000) +#define BENCH_SMALL (2) +#define BENCH_LARGE (100) +#define BENCH_PAYLOAD ('b') +#define BENCH_REGADDR (0x23) + +/** + * @brief Benchmark buffers + */ +static uint8_t bench_wbuf[BENCH_LARGE]; +static uint8_t bench_rbuf[BENCH_LARGE]; + +static struct { + spi_t dev; + spi_mode_t mode; + spi_clk_t clk; + spi_cs_t cs; +} spiconf; + +int cmd_init(void) +{ + spiconf.dev = SPI_DEV(0); + spiconf.mode = SPI_MODE_0; + spiconf.clk = SPI_CLK_1MHZ; + spiconf.cs = (spi_cs_t)GPIO_PIN(DEFAULT_SPI_CS_PORT, DEFAULT_SPI_CS_PIN); + spi_init_cs(spiconf.dev, spiconf.cs); + return 0; +} + +int cmd_bench(void) +{ + uint8_t in; + uint8_t out = (uint8_t)BENCH_PAYLOAD; + + if (spiconf.dev == SPI_UNDEF) { + puts("error: SPI is not initialized, please initialize bus first"); + return 1; + } + + /* prepare buffer */ + memset(bench_wbuf, BENCH_PAYLOAD, BENCH_LARGE); + + /* get access to the bus */ + spi_acquire(spiconf.dev, spiconf.cs, spiconf.mode, spiconf.clk); + + puts("### Running some benchmarks ###"); + + /* 1 - write 1000 times 1 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + in = spi_transfer_byte(spiconf.dev, spiconf.cs, false, out); + (void)in; + } + printf(" 1 - write %i times %i byte\n", BENCH_REDOS, 1); + + /* 2 - write 1000 times 2 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, NULL, BENCH_SMALL); + } + printf(" 2 - write %i times %i byte\n", BENCH_REDOS, BENCH_SMALL); + + /* 3 - write 1000 times 100 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, NULL, BENCH_LARGE); + } + printf(" 3 - write %i times %i byte\n", BENCH_REDOS, BENCH_LARGE); + + /* 4 - write 1000 times 1 byte to register */ + for (int i = 0; i < BENCH_REDOS; i++) { + in = spi_transfer_reg(spiconf.dev, spiconf.cs, BENCH_REGADDR, out); + (void)in; + } + printf(" 4 - write %i times %i byte to register\n", BENCH_REDOS, 1); + + /* 5 - write 1000 times 2 byte to register */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, NULL, BENCH_SMALL); + } + printf(" 5 - write %i times %i byte to register\n", BENCH_REDOS, BENCH_SMALL); + + /* 6 - write 1000 times 100 byte to register */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, NULL, BENCH_LARGE); + } + printf(" 6 - write %i times %i byte to register\n", BENCH_REDOS, BENCH_LARGE); + + /* 7 - read 1000 times 2 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + NULL, bench_rbuf, BENCH_SMALL); + } + printf(" 7 - read %i times %i byte\n", BENCH_REDOS, BENCH_SMALL); + + /* 8 - read 1000 times 100 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + NULL, bench_rbuf, BENCH_LARGE); + } + printf(" 8 - read %i times %i byte\n", BENCH_REDOS, BENCH_LARGE); + + /* 9 - read 1000 times 2 byte from register */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + NULL, bench_rbuf, BENCH_SMALL); + } + printf(" 9 - read %i times %i byte from register\n", BENCH_REDOS, BENCH_SMALL); + + /* 10 - read 1000 times 100 byte from register */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + NULL, bench_rbuf, BENCH_LARGE); + } + printf("10 - read %i times %i byte from register\n", BENCH_REDOS, BENCH_LARGE); + + /* 11 - transfer 1000 times 2 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, bench_rbuf, BENCH_SMALL); + } + printf("11 - transfer %i times %i byte\n", BENCH_REDOS, BENCH_SMALL); + + /* 12 - transfer 1000 times 100 byte */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_bytes(spiconf.dev, spiconf.cs, false, + bench_wbuf, bench_rbuf, BENCH_LARGE); + } + printf("12 - transfer %i times %i byte\n", BENCH_REDOS, BENCH_LARGE); + + /* 13 - transfer 1000 times 2 byte from/to register */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, bench_rbuf, BENCH_SMALL); + } + printf("13 - transfer %i times %i byte to register\n", BENCH_REDOS, BENCH_SMALL); + + /* 14 - transfer 1000 times 100 byte from/to register */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_transfer_regs(spiconf.dev, spiconf.cs, BENCH_REGADDR, + bench_wbuf, bench_rbuf, BENCH_LARGE); + } + printf("14 - transfer %i times %i byte to register\n", BENCH_REDOS, BENCH_LARGE); + + /* 15 - release & acquire the bus 1000 times */ + for (int i = 0; i < BENCH_REDOS; i++) { + spi_release(spiconf.dev); + spi_acquire(spiconf.dev, spiconf.cs, spiconf.mode, spiconf.clk); + } + printf("15 - acquire/release %i times\n", BENCH_REDOS); + + spi_release(spiconf.dev); + puts("### All runs complete ###\n"); + + return 0; +} + +int main(void) +{ + puts("SPI peripheral driver test"); + + printf("There are %i SPI devices configured for your platform.\n", + (int)SPI_NUMOF); + + /* reset local SPI configuration */ + spiconf.dev = SPI_UNDEF; + + cmd_init(); + + while (1) { + cmd_bench(); + } + + return 0; +} From 0d1187e6edfa236421d4bd1563cf3a52458b78a7 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Sat, 17 Jun 2023 20:24:09 +0200 Subject: [PATCH 03/11] cpu/atmega_common: checking features instead of CPU #1 --- cpu/atmega8/include/cpu_conf.h | 93 +++++++++++++++++++ cpu/atmega_common/atmega_cpu.c | 4 +- .../include/atmega_regs_common.h | 8 +- cpu/atmega_common/include/cpu_conf.h | 6 -- cpu/atmega_common/include/periph_cpu_common.h | 8 +- cpu/atmega_common/periph/adc.c | 13 +-- cpu/atmega_common/periph/gpio.c | 16 ++-- cpu/atmega_common/periph/i2c.c | 6 +- cpu/atmega_common/periph/pwm.c | 40 ++++---- 9 files changed, 142 insertions(+), 52 deletions(-) create mode 100644 cpu/atmega8/include/cpu_conf.h diff --git a/cpu/atmega8/include/cpu_conf.h b/cpu/atmega8/include/cpu_conf.h new file mode 100644 index 000000000000..bc0b2b9ce803 --- /dev/null +++ b/cpu/atmega8/include/cpu_conf.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * 2017 RWTH Aachen, Josua Arndt + * 2018 Matthew Blue + * 2023 Hugues Larrive + * + * 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_atmega_common + * @{ + * + * @file + * @brief Implementation specific CPU configuration options + * + * @author Hauke Petersen + * @author Hinnerk van Bruinehsen + * @author Josua Arndt + * @author Steffen Robertz + * @author Matthew Blue + * @author Hugues Larrive + */ + +#ifndef CPU_CONF_H +#define CPU_CONF_H + +#include "atmega_regs_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef THREAD_EXTRA_STACKSIZE_PRINTF +#define THREAD_EXTRA_STACKSIZE_PRINTF (32) +#endif + +/** + * @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 (128) +#endif + +/* keep THREAD_STACKSIZE_IDLE > THREAD_EXTRA_STACKSIZE_PRINTF + * to avoid not printing of debug in interrupts + */ +#ifndef THREAD_STACKSIZE_IDLE +#if MODULE_XTIMER || MODULE_ZTIMER || MODULE_ZTIMER64 +/* For AVR no ISR stack is used, hence an IRQ will victimize the stack of + * whatever thread happens to be running with the IRQ kicks in. If more than + * trivial stuff is needed to be done in ISRs (e.g. when soft timers are used), + * the idle stack will overflow. + */ +#define THREAD_STACKSIZE_IDLE (192) +#else +#define THREAD_STACKSIZE_IDLE (96) +#endif +#endif +/** @} */ + +/** + * @brief Attribute for memory sections required by SRAM PUF + */ +#define PUF_SRAM_ATTRIBUTES __attribute__((used, section(".noinit"))) + +/** + * @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) + +#ifndef DOXYGEN +#define HAS_FLASH_UTILS_ARCH 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CPU_CONF_H */ +/** @} */ diff --git a/cpu/atmega_common/atmega_cpu.c b/cpu/atmega_common/atmega_cpu.c index 7bca49792684..ee9f72e37f09 100644 --- a/cpu/atmega_common/atmega_cpu.c +++ b/cpu/atmega_common/atmega_cpu.c @@ -67,12 +67,12 @@ void avr8_reset_cause(void) #endif } -#if !defined (CPU_ATMEGA8) void __attribute__((weak)) avr8_clk_init(void) { +#if defined(CLKPR) atmega_set_prescaler(CPU_ATMEGA_CLK_SCALE_INIT); -} #endif +} /* This is a vector which is aliased to __vector_default, * the vector executed when an ISR fires with no accompanying diff --git a/cpu/atmega_common/include/atmega_regs_common.h b/cpu/atmega_common/include/atmega_regs_common.h index cc732079c90f..827f9273eb07 100644 --- a/cpu/atmega_common/include/atmega_regs_common.h +++ b/cpu/atmega_common/include/atmega_regs_common.h @@ -67,20 +67,18 @@ typedef struct { /** * @brief 8-bit timer register map */ -#ifndef TCCR2 typedef struct { +#if ((defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B))) REG8 CRA; /**< control A */ REG8 CRB; /**< control B */ REG8 CNT; /**< counter */ REG8 OCR[2]; /**< output compare */ -} mini_timer_t; -#else /* atmega8 */ -typedef struct { +#elif defined(TCCR2) REG8 OCR; /**< output compare */ REG8 CNT; /**< counter */ REG8 CR; /**< control */ -} mini_timer_t; #endif +} mini_timer_t; /** * @brief UART register map diff --git a/cpu/atmega_common/include/cpu_conf.h b/cpu/atmega_common/include/cpu_conf.h index 0b8f7201159d..8cb9dd85608c 100644 --- a/cpu/atmega_common/include/cpu_conf.h +++ b/cpu/atmega_common/include/cpu_conf.h @@ -2,7 +2,6 @@ * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen * 2017 RWTH Aachen, Josua Arndt * 2018 Matthew Blue - * 2023 Hugues Larrive * * 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 @@ -21,7 +20,6 @@ * @author Josua Arndt * @author Steffen Robertz * @author Matthew Blue - * @author Hugues Larrive */ #ifndef CPU_CONF_H @@ -46,11 +44,7 @@ extern "C" { * @{ */ #ifndef THREAD_STACKSIZE_DEFAULT -#ifndef CPU_ATMEGA8 #define THREAD_STACKSIZE_DEFAULT (512) -#else -#define THREAD_STACKSIZE_DEFAULT (256) -#endif #endif /* keep THREAD_STACKSIZE_IDLE > THREAD_EXTRA_STACKSIZE_PRINTF diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 42350454a832..00d0ebdca54b 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -64,7 +64,11 @@ typedef uint8_t gpio_t; * Must be identical to the address of `PINA` provided by avr/io.h */ #ifdef CPU_ATMEGA8 -#define ATMEGA_GPIO_BASE_A (0x39) +#define GPIO_PORT_DESCENDENT +#endif + +#ifdef GPIO_PORT_DESCENDENT +#define ATMEGA_GPIO_BASE_A (0x39) #else #define ATMEGA_GPIO_BASE_A (0x20) #endif @@ -143,7 +147,7 @@ typedef struct { static inline atmega_gpio_port_t *atmega_gpio_port(uint8_t port_num) { static const uintptr_t base_addr = (uintptr_t)ATMEGA_GPIO_BASE_A; -#ifdef CPU_ATMEGA8 +#ifdef GPIO_PORT_DESCENDENT uintptr_t res = base_addr - port_num * sizeof(atmega_gpio_port_t); #else uintptr_t res = base_addr + port_num * sizeof(atmega_gpio_port_t); diff --git a/cpu/atmega_common/periph/adc.c b/cpu/atmega_common/periph/adc.c index c81cf9183b8f..0a1180503fb5 100644 --- a/cpu/atmega_common/periph/adc.c +++ b/cpu/atmega_common/periph/adc.c @@ -54,17 +54,17 @@ int adc_init(adc_t line) _prep(); -#if !defined(CPU_ATMEGA8) +#if defined(DIDR0) /* Disable corresponding Digital input */ if (line < 8) { DIDR0 |= (1 << line); } -#if defined(CPU_ATMEGA2560) +#if defined(DIDR2) else { DIDR2 |= (1 << (line - 8)); } #endif -#endif /* !CPU_ATMEGA8 */ +#endif /* Set ADC-pin as input */ #if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA8) @@ -114,11 +114,11 @@ int32_t adc_sample(adc_t line, adc_res_t res) _prep(); /* set conversion channel */ -#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA1281) || \ - defined(CPU_ATMEGA1284P) || defined(CPU_ATMEGA32U4) || defined(CPU_ATMEGA8) +#if defined(ADMUX) +#if !defined(MUX5) ADMUX &= 0xf0; ADMUX |= line; -#elif defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA128RFA1) || defined(CPU_ATMEGA256RFR2) +#else if (line < 8) { ADCSRB &= ~(1 << MUX5); ADMUX &= 0xf0; @@ -129,6 +129,7 @@ int32_t adc_sample(adc_t line, adc_res_t res) ADMUX &= 0xf0; ADMUX |= (line-8); } +#endif #endif /* Start a new conversion. By default, this conversion will diff --git a/cpu/atmega_common/periph/gpio.c b/cpu/atmega_common/periph/gpio.c index bcde240e7fe9..a81c94b265b3 100644 --- a/cpu/atmega_common/periph/gpio.c +++ b/cpu/atmega_common/periph/gpio.c @@ -314,10 +314,10 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, cli(); /* enable interrupt number int_num */ -#ifndef CPU_ATMEGA8 +#if defined(EIFR) && defined(EIMSK) EIFR |= (1 << int_num); EIMSK |= (1 << int_num); -#else /* atmega8 */ +#elif defined(GIFR) && defined(GICR) GIFR |= (1 << (INTF0 + int_num)); GICR |= (1 << (INT0 + int_num)); #endif @@ -331,10 +331,10 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, else #endif { -#ifndef CPU_ATMEGA8 +#if defined(EICRA) EICRA &= ~(0x3 << (int_num * 2)); EICRA |= (flank << (int_num * 2)); -#else /* atmega8 */ +#elif defined(MCUCR) MCUCR &= ~(0x3 << (int_num * 2)); MCUCR |= (flank << (int_num * 2)); #endif @@ -352,10 +352,10 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, void gpio_irq_enable(gpio_t pin) { -#ifndef CPU_ATMEGA8 +#if defined(EIFR) && defined(EIMSK) EIFR |= (1 << _int_num(pin)); EIMSK |= (1 << _int_num(pin)); -#else /* atmega8 */ +#elif defined(GIFR) && defined(GICR) GIFR |= (1 << (INTF0 + _int_num(pin))); GICR |= (1 << (INTF0 + _int_num(pin))); #endif @@ -363,9 +363,9 @@ void gpio_irq_enable(gpio_t pin) void gpio_irq_disable(gpio_t pin) { -#ifndef CPU_ATMEGA8 +#if defined(EIMSK) EIMSK &= ~(1 << _int_num(pin)); -#else /* atmega8 */ +#elif defined(GICR) GICR &= ~(1 << (INTF0 + _int_num(pin))); #endif } diff --git a/cpu/atmega_common/periph/i2c.c b/cpu/atmega_common/periph/i2c.c index 8cdc20e4dc9e..ff3278ea8ce6 100644 --- a/cpu/atmega_common/periph/i2c.c +++ b/cpu/atmega_common/periph/i2c.c @@ -49,7 +49,7 @@ static int _start(uint8_t address, uint8_t rw_flag); static int _write(const uint8_t *data, int length); static void _stop(void); -#ifndef CPU_ATMEGA8 +#ifdef PRR static void i2c_poweron(i2c_t dev); #endif @@ -123,7 +123,7 @@ void i2c_init(i2c_t dev) /* set pull-up on SCL and SDA */ I2C_PORT_REG |= (I2C_PIN_MASK); -#ifndef CPU_ATMEGA8 +#ifdef PRR /* enable I2C clock */ i2c_poweron(dev); #endif @@ -240,7 +240,7 @@ void i2c_release(i2c_t dev) mutex_unlock(&locks[dev]); } -#ifndef CPU_ATMEGA8 +#ifdef PRR static void i2c_poweron(i2c_t dev) { assert(dev < I2C_NUMOF); diff --git a/cpu/atmega_common/periph/pwm.c b/cpu/atmega_common/periph/pwm.c index 9c5aa5f3aa11..7a0fb6122acd 100644 --- a/cpu/atmega_common/periph/pwm.c +++ b/cpu/atmega_common/periph/pwm.c @@ -37,10 +37,10 @@ #define COMA1 7 static struct { -#ifndef CPU_ATMEGA8 +#if (defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B)) uint8_t CRA; uint8_t CRB; -#else +#elif defined(TCCR2) uint8_t CR; #endif uint8_t res; @@ -66,7 +66,7 @@ static inline uint8_t get_prescaler(pwm_t dev, uint32_t *scale) return pre; } -#ifndef CPU_ATMEGA8 +#if (defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B)) static inline void compute_cra_and_crb(pwm_t dev, uint8_t pre) { uint8_t cra = (1 << WGM1) | (1 << WGM0); @@ -90,19 +90,19 @@ static inline void compute_cra_and_crb(pwm_t dev, uint8_t pre) static inline void apply_config(pwm_t dev) { -#ifndef CPU_ATMEGA8 +#if (defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B)) pwm_conf[dev].dev->CRA = state[dev].CRA; pwm_conf[dev].dev->CRB = state[dev].CRB; -#else +#elif defined(TCCR2) pwm_conf[dev].dev->CR = state[dev].CR; #endif if (pwm_conf[dev].pin_ch[0] == GPIO_UNDEF) { /* If channel 0 is not used, variable resolutions can be used for * channel 1 */ -#ifndef CPU_ATMEGA8 +#if (defined(OCR0A) && defined(OCR0B)) || (defined(OCR2A) && defined(OCR2B)) pwm_conf[dev].dev->OCR[0] = state[dev].res; -#else +#elif defined(OCR2) pwm_conf[dev].dev->OCR = state[dev].res; #endif } @@ -117,17 +117,17 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) assert(!(res != 256 && pwm_conf[dev].pin_ch[0] != GPIO_UNDEF)); /* disable PWM */ -#ifndef CPU_ATMEGA8 +#if (defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B)) pwm_conf[dev].dev->CRA = 0x00; pwm_conf[dev].dev->CRB = 0x00; pwm_conf[dev].dev->OCR[0] = 0; pwm_conf[dev].dev->OCR[1] = 0; -#else +#elif defined(TCCR2) pwm_conf[dev].dev->CR = 0x00; pwm_conf[dev].dev->OCR = 0; #endif -#ifndef CPU_ATMEGA8 +#ifdef PRR /* disable power reduction */ if (dev) { power_timer2_enable(); @@ -144,9 +144,9 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) /* Compute configuration and store it in the state. (The state is needed * for later calls to pwm_poweron().)*/ -#ifndef CPU_ATMEGA8 +#if (defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B)) compute_cra_and_crb(dev, pre); -#else +#elif defined(TCCR2) uint8_t cr = (1 << WGM21) | (1 << WGM20) | (1 << COM21) | pre; state[dev].CR = cr; #endif @@ -183,21 +183,21 @@ uint8_t pwm_channels(pwm_t dev) void pwm_set(pwm_t dev, uint8_t ch, uint16_t value) { -#ifdef CPU_ATMEGA8 +#ifdef OCR2 (void)ch; #endif assert(dev < PWM_NUMOF && ch <= 1 && pwm_conf[dev].pin_ch[ch] != GPIO_UNDEF); if (value > state[dev].res) { -#ifndef CPU_ATMEGA8 +#if (defined(OCR0A) && defined(OCR0B)) || (defined(OCR2A) && defined(OCR2B)) pwm_conf[dev].dev->OCR[ch] = state[dev].res; -#else +#elif defined(OCR2) pwm_conf[dev].dev->OCR = state[dev].res; #endif } else { -#ifndef CPU_ATMEGA8 +#if (defined(OCR0A) && defined(OCR0B)) || (defined(OCR2A) && defined(OCR2B)) pwm_conf[dev].dev->OCR[ch] = value; -#else +#elif defined(OCR2) pwm_conf[dev].dev->OCR = value; #endif } @@ -206,7 +206,7 @@ void pwm_set(pwm_t dev, uint8_t ch, uint16_t value) void pwm_poweron(pwm_t dev) { assert(dev < PWM_NUMOF); -#ifndef CPU_ATMEGA8 +#ifdef PRR /* disable power reduction */ if (dev) { power_timer2_enable(); @@ -222,7 +222,7 @@ void pwm_poweron(pwm_t dev) void pwm_poweroff(pwm_t dev) { assert(dev < PWM_NUMOF); -#ifndef CPU_ATMEGA8 +#if (defined(TCCR0A) && defined(TTCR0B)) || (defined(TCCR2A) && defined(TCCR2B)) pwm_conf[dev].dev->CRA = 0x00; pwm_conf[dev].dev->CRB = 0x00; /* disable timers to lower power consumption */ @@ -232,7 +232,7 @@ void pwm_poweroff(pwm_t dev) else { power_timer0_disable(); } -#else +#elif defined(TCCR2) pwm_conf[dev].dev->CR = 0x00; #endif From 55705db529ac061d37f72e9ec137c6e4f52e7a29 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Sat, 17 Jun 2023 20:26:27 +0200 Subject: [PATCH 04/11] fixup! tests/atmega8: streamlined test battery --- tests/atmega8/gpio/Makefile | 2 -- tests/atmega8/shell/Makefile | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/atmega8/gpio/Makefile b/tests/atmega8/gpio/Makefile index d0174894c503..034530740f40 100644 --- a/tests/atmega8/gpio/Makefile +++ b/tests/atmega8/gpio/Makefile @@ -11,7 +11,5 @@ USEMODULE += shell CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE DISABLE_MODULE += core_msg DEVELHELP = 0 -CFLAGS += -DTHREAD_STACKSIZE_IDLE=74 -CFLAGS += -DTHREAD_STACKSIZE_MAIN=168 include $(RIOTBASE)/Makefile.include diff --git a/tests/atmega8/shell/Makefile b/tests/atmega8/shell/Makefile index dce740465bfe..5dd8e56d9d80 100644 --- a/tests/atmega8/shell/Makefile +++ b/tests/atmega8/shell/Makefile @@ -5,12 +5,10 @@ PORT ?= /dev/ttyACM0 include ../../Makefile.tests_common USEMODULE += shell_cmds_default -#~ USEMODULE += ps -DEVELHELP = 0 +USEMODULE += ps +DEVELHELP = 1 CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE DISABLE_MODULE += core_msg -CFLAGS += -DTHREAD_STACKSIZE_IDLE=74 -CFLAGS += -DTHREAD_STACKSIZE_MAIN=150 include $(RIOTBASE)/Makefile.include From a822109460daa56985da3e3b4a22655cd4f222e7 Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Sun, 18 Jun 2023 12:17:33 +0200 Subject: [PATCH 05/11] cpu/avr8_common: Add support to threadless idle Signed-off-by: Gerson Fernando Budke --- cpu/avr8_common/Kconfig | 1 + cpu/avr8_common/Makefile.features | 1 + cpu/avr8_common/avr8_cpu.c | 4 ++++ cpu/avr8_common/thread_arch.c | 16 ++++++++++++++++ 4 files changed, 22 insertions(+) diff --git a/cpu/avr8_common/Kconfig b/cpu/avr8_common/Kconfig index 54bd222d82ac..e6240df54a68 100644 --- a/cpu/avr8_common/Kconfig +++ b/cpu/avr8_common/Kconfig @@ -10,6 +10,7 @@ config CPU_ARCH_AVR8 bool select HAS_ARCH_8BIT select HAS_ARCH_AVR8 + select HAS_NO_IDLE_THREAD select MODULE_MALLOC_THREAD_SAFE if TEST_KCONFIG select MODULE_TINY_STRERROR_AS_STRERROR if TEST_KCONFIG diff --git a/cpu/avr8_common/Makefile.features b/cpu/avr8_common/Makefile.features index 1c5acda5cd1d..e5f25277b785 100644 --- a/cpu/avr8_common/Makefile.features +++ b/cpu/avr8_common/Makefile.features @@ -1,3 +1,4 @@ FEATURES_PROVIDED += arch_8bit FEATURES_PROVIDED += arch_avr8 +FEATURES_PROVIDED += no_idle_thread FEATURES_PROVIDED += cpp diff --git a/cpu/avr8_common/avr8_cpu.c b/cpu/avr8_common/avr8_cpu.c index 1c43d168bdc1..bd5d817cf3ac 100644 --- a/cpu/avr8_common/avr8_cpu.c +++ b/cpu/avr8_common/avr8_cpu.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "cpu.h" #if !defined (CPU_ATMEGA8) @@ -107,6 +108,9 @@ void cpu_init(void) avr8_clk_init(); #endif + /* Set default sleep mode for LPM or threadless idle */ + set_sleep_mode(SLEEP_MODE_IDLE); + /* Initialize stdio before periph_init() to allow use of DEBUG() there */ #ifdef MODULE_AVR_LIBC_EXTRA avr8_stdio_init(); diff --git a/cpu/avr8_common/thread_arch.c b/cpu/avr8_common/thread_arch.c index 768583a93f72..92f05f66d4f5 100644 --- a/cpu/avr8_common/thread_arch.c +++ b/cpu/avr8_common/thread_arch.c @@ -26,6 +26,7 @@ */ #include +#include #include "thread.h" #include "sched.h" @@ -427,3 +428,18 @@ __attribute__((always_inline)) static inline void avr8_context_restore(void) "out __SREG__, __tmp_reg__ \n\t" "pop __tmp_reg__ \n\t"); } + +void sched_arch_idle(void) +{ +#ifdef MODULE_PM_LAYERED + void pm_set_lowest(void); + pm_set_lowest(); +#else + unsigned irq_state = irq_disable(); + sleep_enable(); + sei(); + sleep_cpu(); + sleep_disable(); + irq_restore(irq_state); +#endif +} From 0b50058051e8360bbba303e515589f743bb30a4e Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 20 Jun 2023 16:07:05 +0200 Subject: [PATCH 06/11] cpu/atmega8: stacksize adjustement --- cpu/atmega8/include/cpu_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpu/atmega8/include/cpu_conf.h b/cpu/atmega8/include/cpu_conf.h index bc0b2b9ce803..b187ebf9af66 100644 --- a/cpu/atmega8/include/cpu_conf.h +++ b/cpu/atmega8/include/cpu_conf.h @@ -34,7 +34,7 @@ extern "C" { #endif #ifndef THREAD_EXTRA_STACKSIZE_PRINTF -#define THREAD_EXTRA_STACKSIZE_PRINTF (32) +#define THREAD_EXTRA_STACKSIZE_PRINTF (64) #endif /** From 45294554000b917fe807216e2f9e761f90228578 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 20 Jun 2023 16:07:33 +0200 Subject: [PATCH 07/11] tests/atmega8: stacksize adjustement --- tests/atmega8/spi/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/atmega8/spi/Makefile b/tests/atmega8/spi/Makefile index 6a133d9ee59c..35ef7bc2d63a 100644 --- a/tests/atmega8/spi/Makefile +++ b/tests/atmega8/spi/Makefile @@ -9,7 +9,5 @@ FEATURES_REQUIRED += periph_spi DEVELHELP = 0 CFLAGS += -DNDEBUG -DLOG_LEVEL=LOG_NONE DISABLE_MODULE += core_msg -CFLAGS += -DTHREAD_STACKSIZE_IDLE=64 -CFLAGS += -DTHREAD_STACKSIZE_MAIN=128 include $(RIOTBASE)/Makefile.include From 6d3917313b86803e18a57be3f522750bb3ea6195 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 20 Jun 2023 16:09:35 +0200 Subject: [PATCH 08/11] cpu/atmega_common: checking features instead of CPU #2 --- cpu/atmega_common/atmega_cpu.c | 6 +-- .../include/atmega_regs_common.h | 6 +-- cpu/atmega_common/include/periph_cpu_common.h | 20 ++++++-- cpu/atmega_common/periph/adc.c | 31 ++++++++---- cpu/atmega_common/periph/i2c.c | 8 +--- cpu/atmega_common/periph/pwm.c | 4 +- cpu/atmega_common/periph/spi.c | 12 ++--- cpu/atmega_common/periph/timer.c | 42 ++++++++--------- cpu/atmega_common/periph/uart.c | 47 ++++++++++--------- 9 files changed, 98 insertions(+), 78 deletions(-) diff --git a/cpu/atmega_common/atmega_cpu.c b/cpu/atmega_common/atmega_cpu.c index ee9f72e37f09..6a806c63a642 100644 --- a/cpu/atmega_common/atmega_cpu.c +++ b/cpu/atmega_common/atmega_cpu.c @@ -60,7 +60,7 @@ void avr8_reset_cause(void) DEBUG("Watchdog reset!\n"); } } -#if !defined (CPU_ATMEGA328P) && !defined (CPU_ATMEGA8) +#if defined(JTRF) if (mcusr_mirror & (1 << JTRF)) { DEBUG("JTAG reset!\n"); } @@ -89,7 +89,7 @@ ISR(BADISR_vect) { avr8_reset_cause(); -#if defined (CPU_ATMEGA256RFR2) +#if defined(CPU_ATMEGA256RFR2) printf("IRQ_STATUS %#02x\nIRQ_STATUS1 %#02x\n", (unsigned int)IRQ_STATUS, (unsigned int)IRQ_STATUS1); @@ -105,7 +105,7 @@ ISR(BADISR_vect) core_panic(PANIC_GENERAL_ERROR, "BADISR"); } -#if defined(CPU_ATMEGA128RFA1) || defined (CPU_ATMEGA256RFR2) +#if defined(BAT_LOW_vect) ISR(BAT_LOW_vect, ISR_BLOCK) { avr8_enter_isr(); diff --git a/cpu/atmega_common/include/atmega_regs_common.h b/cpu/atmega_common/include/atmega_regs_common.h index 827f9273eb07..8567deb8c9b8 100644 --- a/cpu/atmega_common/include/atmega_regs_common.h +++ b/cpu/atmega_common/include/atmega_regs_common.h @@ -84,18 +84,18 @@ typedef struct { * @brief UART register map */ typedef struct { -#ifndef CPU_ATMEGA8 +#if defined(UCSR0A) || defined(UCSR1A) REG8 CSRA; /**< control and status register A */ REG8 CSRB; /**< control and status register B */ REG8 CSRC; /**< control and status register C */ -#ifdef CPU_ATMEGA32U4 +#ifdef UCSR1D /* 32u4 */ REG8 CSRD; /**< control and status register D */ #else REG8 reserved; /**< reserved */ #endif REG16 BRR; /**< baud rate register */ REG8 DR; /**< data register */ -#else /* atmega8 */ +#elif defined(UCSRA) /* atmega8 */ REG8 BRRL; /**< baud rate register low byte */ REG8 CSRB; /**< control and status register B */ REG8 CSRA; /**< control and status register A */ diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 00d0ebdca54b..02aea16981ca 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -63,15 +63,27 @@ typedef uint8_t gpio_t; * * Must be identical to the address of `PINA` provided by avr/io.h */ -#ifdef CPU_ATMEGA8 +#if (defined(OCF1A) && defined(OCF1B) && (OCF1A > OCF1B)) \ + || (defined(PUD) && (PUD != 4)) || (defined(INT0) && (INT0 == 6)) + /* match with 65 devices against 61 for (PORTB == _SFR_IO8(0x18)) which + * did not work here anyway */ #define GPIO_PORT_DESCENDENT #endif #ifdef GPIO_PORT_DESCENDENT -#define ATMEGA_GPIO_BASE_A (0x39) +#ifdef _AVR_ATTINY1634_H_INCLUDED +/* the only one that requires particular treatment! */ +#define ATMEGA_GPIO_BASE_A (0x2F) #else -#define ATMEGA_GPIO_BASE_A (0x20) -#endif +/* oll other port descendent, including : + - _AVR_IO8534_ (only have port A but with 0x18 address) ; + - _AVR_IOAT94K_H_ (only have ports D and E) ; + - _AVR_IOTN28_H_ (only have ports A and D). */ +#define ATMEGA_GPIO_BASE_A (0x39) +#endif /* _AVR_ATTINY1634_H_INCLUDED */ +#else /* !GPIO_PORT_DESCENDENT */ +#define ATMEGA_GPIO_BASE_A (0x20) +#endif /* GPIO_PORT_DESCENDENT */ /** * @brief Base of the GPIO port G register as memory address * diff --git a/cpu/atmega_common/periph/adc.c b/cpu/atmega_common/periph/adc.c index 0a1180503fb5..46b8ac341bd9 100644 --- a/cpu/atmega_common/periph/adc.c +++ b/cpu/atmega_common/periph/adc.c @@ -67,24 +67,39 @@ int adc_init(adc_t line) #endif /* Set ADC-pin as input */ -#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA8) +#if !defined(PORTA) && defined(PC0) + /* 328p and 8 do not have PORTA, on 32u4 pins are named differently + * and it only have PORTC6 and PORTC7 */ DDRC &= ~(1 << line); PORTC &= ~(1 << line); -#elif defined(CPU_ATMEGA1284P) +#elif defined(PORTA) && !defined(DIDR2) + /* 1284p do not have DIDR2 */ DDRA &= ~(1 << line); PORTA &= ~(1 << line); -#elif defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA1281) +#elif defined(PORTF) if (line < 8) { DDRF &= ~(1 << line); PORTF &= ~(1 << line); } -#if defined(CPU_ATMEGA2560) +#if defined(PORTK) else { - DDRK &= ~(1 << (line-8)); - PORTK &= ~(1 << (line-8)); + DDRK &= ~(1 << (line - 8)); + PORTK &= ~(1 << (line - 8)); } -#endif /* CPU_ATMEGA2560 */ -#endif /* CPU_ATMEGA328P */ +#elif defined(PORTF0) && !defined(PORTF2) && !defined(PORTF3) + /* 32u4 do not have PORTF2 and PORTF3 */ + else if (line == 8) { + DDRD &= ~(1 << PORTD4); + PORTD &= ~(1 << PORTD4); + else if (line < 11) { + DDRD &= ~(1 << (line - 3)); + PORTD &= ~(1 << (line - 3)); + else { + DDRB &= ~(1 << (line - 7)); + PORTB &= ~(1 << (line - 7)); + } +#endif /* PORTK */ +#endif /* PORTF */ /* set clock prescaler to get the maximal possible ADC clock value */ for (uint32_t clk_div = 1; clk_div < 8; ++clk_div) { diff --git a/cpu/atmega_common/periph/i2c.c b/cpu/atmega_common/periph/i2c.c index ff3278ea8ce6..118eb0964896 100644 --- a/cpu/atmega_common/periph/i2c.c +++ b/cpu/atmega_common/periph/i2c.c @@ -49,9 +49,7 @@ static int _start(uint8_t address, uint8_t rw_flag); static int _write(const uint8_t *data, int length); static void _stop(void); -#ifdef PRR static void i2c_poweron(i2c_t dev); -#endif static mutex_t locks[I2C_NUMOF]; @@ -123,10 +121,8 @@ void i2c_init(i2c_t dev) /* set pull-up on SCL and SDA */ I2C_PORT_REG |= (I2C_PIN_MASK); -#ifdef PRR /* enable I2C clock */ i2c_poweron(dev); -#endif /* disable device */ TWCR &= ~(1 << TWEN); @@ -240,14 +236,14 @@ void i2c_release(i2c_t dev) mutex_unlock(&locks[dev]); } -#ifdef PRR static void i2c_poweron(i2c_t dev) { assert(dev < I2C_NUMOF); (void) dev; +#ifdef PRTWI power_twi_enable(); -} #endif +} static int _start(uint8_t address, uint8_t rw_flag) { diff --git a/cpu/atmega_common/periph/pwm.c b/cpu/atmega_common/periph/pwm.c index 7a0fb6122acd..9bfaa020054e 100644 --- a/cpu/atmega_common/periph/pwm.c +++ b/cpu/atmega_common/periph/pwm.c @@ -127,7 +127,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) pwm_conf[dev].dev->OCR = 0; #endif -#ifdef PRR +#if defined(PRT2) || defined(PRTIM2) || defined(PRT0) || defined(PRTIM0) /* disable power reduction */ if (dev) { power_timer2_enable(); @@ -206,7 +206,7 @@ void pwm_set(pwm_t dev, uint8_t ch, uint16_t value) void pwm_poweron(pwm_t dev) { assert(dev < PWM_NUMOF); -#ifdef PRR +#if defined(PRT2) || defined(PRTIM2) || defined(PRT0) || defined(PRTIM0) /* disable power reduction */ if (dev) { power_timer2_enable(); diff --git a/cpu/atmega_common/periph/spi.c b/cpu/atmega_common/periph/spi.c index 0f9e1f50737f..4231f4929baa 100644 --- a/cpu/atmega_common/periph/spi.c +++ b/cpu/atmega_common/periph/spi.c @@ -45,7 +45,7 @@ static mutex_t lock = MUTEX_INIT; void spi_init(spi_t bus) { assert(bus == 0); -#ifndef CPU_ATMEGA8 +#ifdef PRSPI /* power off the SPI peripheral */ power_spi_disable(); #endif @@ -57,13 +57,13 @@ void spi_init_pins(spi_t bus) { (void)bus; /* set SPI pins as output */ -#if defined (CPU_ATMEGA2560) || defined (CPU_ATMEGA1281) +#if defined(CPU_ATMEGA2560) || defined(CPU_ATMEGA1281) DDRB |= ((1 << DDB2) | (1 << DDB1) | (1 << DDB0)); #endif -#if defined (CPU_ATMEGA328P) || defined (CPU_ATMEGA8) +#if defined(CPU_ATMEGA328P) || defined(CPU_ATMEGA8) DDRB |= ((1 << DDB2) | (1 << DDB3) | (1 << DDB5)); #endif -#if defined (CPU_ATMEGA1284P) +#if defined(CPU_ATMEGA1284P) DDRB |= ((1 << DDB4) | (1 << DDB5) | (1 << DDB7)); #endif #if defined(CPU_ATMEGA128RFA1) || defined(CPU_ATMEGA256RFR2) @@ -90,7 +90,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* lock the bus and power on the SPI peripheral */ mutex_lock(&lock); -#ifndef CPU_ATMEGA8 +#ifdef PRSPI power_spi_enable(); #endif @@ -108,7 +108,7 @@ void spi_release(spi_t bus) (void)bus; /* power off and release the bus */ SPCR &= ~(1 << SPE); -#ifndef CPU_ATMEGA8 +#ifdef PRSPI power_spi_disable(); #endif mutex_unlock(&lock); diff --git a/cpu/atmega_common/periph/timer.c b/cpu/atmega_common/periph/timer.c index 87e6b5617d6f..1534df5e7184 100644 --- a/cpu/atmega_common/periph/timer.c +++ b/cpu/atmega_common/periph/timer.c @@ -136,7 +136,7 @@ int timer_init(tim_t tim, uint32_t freq, timer_cb_t cb, void *arg) /* stop and reset timer */ ctx[tim].dev->CRA = 0; ctx[tim].dev->CRB = 0; -#ifndef CPU_ATMEGA8 +#ifdef TCCR1C ctx[tim].dev->CRC = 0; #endif ctx[tim].dev->CNT = 0; @@ -162,12 +162,12 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value) unsigned state = irq_disable(); ctx[tim].dev->OCR[channel] = (uint16_t)value; -#ifndef CPU_ATMEGA8 +#if defined(OCF1A) && defined(OCF1B) && (OCF1A < OCF1B) /* clear spurious IRQs, if any */ - *ctx[tim].flag = (1 << (channel + OCF1A)); + *ctx[tim].flag = (1 << (OCF1A + channel)); /* unmask IRQ */ - *ctx[tim].mask |= (1 << (channel + OCIE1A)); -#else /* atmega8 */ + *ctx[tim].mask |= (1 << (OCIE1A + channel)); +#elif defined(OCF1A) && defined(OCF1B) && (OCF1A > OCF1B) /* clear spurious IRQs, if any */ *ctx[tim].flag = (1 << (OCF1A - channel)); /* unmask IRQ */ @@ -190,12 +190,12 @@ int timer_set(tim_t tim, int channel, unsigned int timeout) unsigned absolute = ctx[tim].dev->CNT + timeout; ctx[tim].dev->OCR[channel] = absolute; -#ifndef CPU_ATMEGA8 +#if defined(OCF1A) && defined(OCF1B) && (OCF1A < OCF1B) /* clear spurious IRQs, if any */ - *ctx[tim].flag = (1 << (channel + OCF1A)); + *ctx[tim].flag = (1 << (OCF1A + channel)); /* unmask IRQ */ - *ctx[tim].mask |= (1 << (channel + OCIE1A)); -#else /* atmega8 */ + *ctx[tim].mask |= (1 << (OCIE1A + channel)); +#elif defined(OCF1A) && defined(OCF1B) && (OCF1A > OCF1B) /* clear spurious IRQs, if any */ *ctx[tim].flag = (1 << (OCF1A - channel)); /* unmask IRQ */ @@ -207,9 +207,9 @@ int timer_set(tim_t tim, int channel, unsigned int timeout) /* Timer already expired. Trigger the interrupt now and loop until it * is triggered. */ -#ifndef CPU_ATMEGA8 +#if defined(OCF1A) && defined(OCF1B) && (OCF1A < OCF1B) while (!(*ctx[tim].flag & (1 << (OCF1A + channel)))) { -#else +#elif defined(OCF1A) && defined(OCF1B) && (OCF1A > OCF1B) while (!(*ctx[tim].flag & (1 << (OCF1A - channel)))) { #endif ctx[tim].dev->OCR[channel] = ctx[tim].dev->CNT; @@ -237,12 +237,12 @@ int timer_set_periodic(tim_t tim, int channel, unsigned int value, uint8_t flags ctx[tim].dev->OCR[channel] = (uint16_t)value; -#ifndef CPU_ATMEGA8 +#if defined(OCF1A) && defined(OCF1B) && (OCF1A < OCF1B) /* clear spurious IRQs, if any */ - *ctx[tim].flag = (1 << (channel + OCF1A)); + *ctx[tim].flag = (1 << (OCF1A + channel)); /* unmask IRQ */ - *ctx[tim].mask |= (1 << (channel + OCIE1A)); -#else /* atmega8 */ + *ctx[tim].mask |= (1 << (OCIE1A + channel)); +#elif defined(OCF1A) && defined(OCF1B) && (OCF1A > OCF1B) /* clear spurious IRQs, if any */ *ctx[tim].flag = (1 << (OCF1A - channel)); /* unmask IRQ */ @@ -282,9 +282,9 @@ int timer_clear(tim_t tim, int channel) return -1; } -#ifndef CPU_ATMEGA8 - *ctx[tim].mask &= ~(1 << (channel + OCIE1A)); -#else +#if defined(OCIE1A) && defined(OCIE1B) && (OCIE1A < OCIE1B) + *ctx[tim].mask &= ~(1 << (OCIE1A + channel)); +#elif defined(OCIE1A) && defined(OCIE1B) && (OCIE1A > OCIE1B) *ctx[tim].mask &= ~(1 << (OCIE1A - channel)); #endif @@ -326,11 +326,7 @@ static inline void _isr(tim_t tim, int chan) avr8_enter_isr(); if (is_oneshot(tim, chan)) { -#ifndef CPU_ATMEGA8 - *ctx[tim].mask &= ~(1 << (chan + OCIE1A)); -#else - *ctx[tim].mask &= ~(1 << (OCIE1A - chan)); -#endif + timer_clear(tim, chan); } ctx[tim].cb(ctx[tim].arg, chan); diff --git a/cpu/atmega_common/periph/uart.c b/cpu/atmega_common/periph/uart.c index 1c47b586dc0f..f5858505149f 100644 --- a/cpu/atmega_common/periph/uart.c +++ b/cpu/atmega_common/periph/uart.c @@ -85,19 +85,20 @@ static uart_isr_ctx_t isr_ctx[UART_NUMOF]; static void _update_brr(uart_t uart, uint16_t brr, bool double_speed) { -#ifndef CPU_ATMEGA8 +#if defined(UCSR0A) || defined(UCSR1A) dev[uart]->BRR = brr; -#else /* on atmega8 BRRH is shared with CSRC */ +#elif defined(UCSRA) /* atmega8 */ + /* on atmega8 BRRH is shared with CSRC */ dev[uart]->CSRC = (brr >> 8); dev[uart]->BRRL = (uint8_t)(brr & 0x00ff); #endif if (double_speed) { -#if defined(CPU_ATMEGA8) +#if defined(U2X) /* atmega8 */ dev[uart]->CSRA |= (1 << U2X); -#elif defined(CPU_ATMEGA32U4) - dev[uart]->CSRA |= (1 << U2X1); -#else +#elif defined(U2X0) dev[uart]->CSRA |= (1 << U2X0); +#elif defined(U2X1) /* atmega32u4 */ + dev[uart]->CSRA |= (1 << U2X1); #endif } } @@ -138,40 +139,40 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) isr_ctx[uart].arg = arg; /* disable and reset UART */ -#ifdef CPU_ATMEGA32U4 +#ifdef UCSR1D /* 32u4 */ dev[uart]->CSRD = 0; #endif dev[uart]->CSRB = 0; dev[uart]->CSRA = 0; /* configure UART to 8N1 mode */ -#if defined(CPU_ATMEGA8) +#if defined(UCSZ0) && defined(UCSZ1) /* atmega8 */ dev[uart]->CSRC = (1 << UCSZ0) | (1 << UCSZ1); -#elif defined(CPU_ATMEGA32U4) - dev[uart]->CSRC = (1 << UCSZ10) | (1 << UCSZ11); -#else +#elif defined(UCSZ00) && defined(UCSZ01) dev[uart]->CSRC = (1 << UCSZ00) | (1 << UCSZ01); +#elif defined(UCSZ10) && defined(UCSZ11) /* 32u4 */ + dev[uart]->CSRC = (1 << UCSZ10) | (1 << UCSZ11); #endif /* set clock divider */ _set_brr(uart, baudrate); /* enable RX and TX and their respective interrupt */ if (rx_cb) { -#if defined(CPU_ATMEGA8) +#if defined(RXCIE) /* atmega8 */ dev[uart]->CSRB = ((1 << RXCIE) | (1 << TXCIE) | (1 << RXEN) | (1 << TXEN)); -#elif defined(CPU_ATMEGA32U4) - dev[uart]->CSRB = ((1 << RXCIE1) | (1 << TXCIE1) | (1 << RXEN1) | (1 << TXEN1)); -#else +#elif defined(RXCIE0) dev[uart]->CSRB = ((1 << RXCIE0) | (1 << TXCIE0) | (1 << RXEN0) | (1 << TXEN0)); +#elif defined(RXCIE1) /* 32u4 */ + dev[uart]->CSRB = ((1 << RXCIE1) | (1 << TXCIE1) | (1 << RXEN1) | (1 << TXEN1)); #endif } else { -#if defined(CPU_ATMEGA8) +#if defined(TXEN) /* atmega8 */ dev[uart]->CSRB = ((1 << TXEN) | (1 << TXCIE)); -#elif defined(CPU_ATMEGA32U4) - dev[uart]->CSRB = ((1 << TXEN1) | (1 << TXCIE1)); -#else +#elif defined(TXEN0) dev[uart]->CSRB = ((1 << TXEN0) | (1 << TXCIE0)); +#elif defined(TXEN1) /* 32u4 */ + dev[uart]->CSRB = ((1 << TXEN1) | (1 << TXCIE1)); #endif } @@ -181,12 +182,12 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) void uart_write(uart_t uart, const uint8_t *data, size_t len) { for (size_t i = 0; i < len; i++) { -#if defined(CPU_ATMEGA8) +#if defined(UDRE) /* atmega8 */ while (!(dev[uart]->CSRA & (1 << UDRE))) {}; -#elif defined(CPU_ATMEGA32U4) - while (!(dev[uart]->CSRA & (1 << UDRE1))) {}; -#else +#elif defined(UDRE0) while (!(dev[uart]->CSRA & (1 << UDRE0))) {} +#elif defined(UDRE1) /* 32u4 */ + while (!(dev[uart]->CSRA & (1 << UDRE1))) {}; #endif /* start of TX won't finish until no data in UDRn and transmit shift register is empty */ From c3b534474ed8822857d30a1c3261b07b32130c07 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 20 Jun 2023 16:48:49 +0200 Subject: [PATCH 09/11] fixup! cpu/atmega_common: checking features instead of CPU #2 --- cpu/atmega_common/periph/adc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cpu/atmega_common/periph/adc.c b/cpu/atmega_common/periph/adc.c index 46b8ac341bd9..a68692c36913 100644 --- a/cpu/atmega_common/periph/adc.c +++ b/cpu/atmega_common/periph/adc.c @@ -72,22 +72,21 @@ int adc_init(adc_t line) * and it only have PORTC6 and PORTC7 */ DDRC &= ~(1 << line); PORTC &= ~(1 << line); -#elif defined(PORTA) && !defined(DIDR2) - /* 1284p do not have DIDR2 */ +#elif defined(PORTA) && !defined(DIDR2) /* 1284p do not have DIDR2 */ DDRA &= ~(1 << line); PORTA &= ~(1 << line); -#elif defined(PORTF) +#elif defined(PORTF) /* 2560 and 1281 */ if (line < 8) { DDRF &= ~(1 << line); PORTF &= ~(1 << line); } -#if defined(PORTK) +#if defined(PORTK) /* 2560 */ else { DDRK &= ~(1 << (line - 8)); PORTK &= ~(1 << (line - 8)); } -#elif defined(PORTF0) && !defined(PORTF2) && !defined(PORTF3) - /* 32u4 do not have PORTF2 and PORTF3 */ +#elif defined(PORTF0) && !defined(PORTF2) && !defined(PORTF3) + /* 32u4 do not have PORTF2 and PORTF3 */ else if (line == 8) { DDRD &= ~(1 << PORTD4); PORTD &= ~(1 << PORTD4); @@ -131,9 +130,10 @@ int32_t adc_sample(adc_t line, adc_res_t res) /* set conversion channel */ #if defined(ADMUX) #if !defined(MUX5) + /* atmega8 ; 328p ; 1281 ; 1284p ; 32u4 */ ADMUX &= 0xf0; ADMUX |= line; -#else +#else /* 2560 ; 128rfa1 ; 256rfr2 */ if (line < 8) { ADCSRB &= ~(1 << MUX5); ADMUX &= 0xf0; From ae089af56b438a6ae0bbdb8518dbe85f7f4c5b08 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 20 Jun 2023 17:17:58 +0200 Subject: [PATCH 10/11] cpu/avr8_common: rollback changes --- cpu/atmega_common/include/cpu_clock.h | 4 ++++ cpu/avr8_common/avr8_cpu.c | 4 ---- cpu/avr8_common/include/cpu.h | 2 -- cpu/avr8_common/startup.c | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cpu/atmega_common/include/cpu_clock.h b/cpu/atmega_common/include/cpu_clock.h index 797f6107ea15..09120281065e 100644 --- a/cpu/atmega_common/include/cpu_clock.h +++ b/cpu/atmega_common/include/cpu_clock.h @@ -63,11 +63,15 @@ enum { static inline void atmega_set_prescaler(uint8_t clk_scale) { /* Enable clock change */ +#ifdef CLKPR /* Must be assignment to set all other bits to zero, see datasheet */ CLKPR = (1 << CLKPCE); /* Write clock within 4 cycles */ CLKPR = clk_scale; +#else + (void) clk_scale; +#endif } #ifdef __cplusplus diff --git a/cpu/avr8_common/avr8_cpu.c b/cpu/avr8_common/avr8_cpu.c index bd5d817cf3ac..c7b664efe006 100644 --- a/cpu/avr8_common/avr8_cpu.c +++ b/cpu/avr8_common/avr8_cpu.c @@ -34,9 +34,7 @@ #include #include "cpu.h" -#if !defined (CPU_ATMEGA8) #include "cpu_clock.h" -#endif #include "board.h" #include "irq.h" #include "periph/init.h" @@ -104,9 +102,7 @@ void cpu_init(void) wdt_reset(); /* should not be nececessary as done in bootloader */ wdt_disable(); /* but when used without bootloader this is needed */ -#ifndef CPU_ATMEGA8 avr8_clk_init(); -#endif /* Set default sleep mode for LPM or threadless idle */ set_sleep_mode(SLEEP_MODE_IDLE); diff --git a/cpu/avr8_common/include/cpu.h b/cpu/avr8_common/include/cpu.h index bd219f2c6480..bd91807df4ba 100644 --- a/cpu/avr8_common/include/cpu.h +++ b/cpu/avr8_common/include/cpu.h @@ -40,9 +40,7 @@ #include #include "architecture.h" #include "cpu_conf.h" -#if !defined (CPU_ATMEGA8) #include "cpu_clock.h" -#endif #include "sched.h" #include "thread.h" diff --git a/cpu/avr8_common/startup.c b/cpu/avr8_common/startup.c index 94d3956d0b7e..61afffa2c98b 100644 --- a/cpu/avr8_common/startup.c +++ b/cpu/avr8_common/startup.c @@ -59,7 +59,7 @@ void init8_ovr(void) __attribute__((section(".init8"))); __attribute__((used, naked)) void init7_ovr(void) { -#ifdef CPU_ATMEGA8 +#if (ROM_LEN <= 0x2000) __asm__ ("rjmp reset_handler"); #else __asm__ ("call reset_handler"); @@ -68,7 +68,7 @@ __attribute__((used, naked)) void init7_ovr(void) __attribute__((used, naked)) void init8_ovr(void) { -#ifdef CPU_ATMEGA8 +#if (ROM_LEN <= 0x2000) __asm__ ("rjmp exit"); #else __asm__ ("jmp exit"); From e375c21abb9152eb49ea0b7498c8b5dc0d05e455 Mon Sep 17 00:00:00 2001 From: Hugues Larrive Date: Tue, 20 Jun 2023 17:25:06 +0200 Subject: [PATCH 11/11] fixup! cpu/avr8_common: rollback changes --- cpu/avr8_common/include/cpu.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpu/avr8_common/include/cpu.h b/cpu/avr8_common/include/cpu.h index bd91807df4ba..18c4dcee7141 100644 --- a/cpu/avr8_common/include/cpu.h +++ b/cpu/avr8_common/include/cpu.h @@ -3,7 +3,6 @@ * 2014 Freie Universität Berlin, Hinnerk van Bruinehsen * 2018 RWTH Aachen, Josua Arndt * 2021 Gerson Fernando Budke - * 2023 Hugues Larrive * * 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 @@ -27,7 +26,6 @@ * @author Kaspar Schleiser * @author Josua Arndt * @author Gerson Fernando Budke - * @author Hugues Larrive * */