aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOtto Mattik <otto@mattik.org>2021-07-08 18:10:55 +0200
committerOtto Mattik <otto@mattik.org>2021-07-08 18:10:55 +0200
commitda34d97efb21719b2b332f8c60b2750d11bcde1f (patch)
tree2de9fe89f6d79b8ebfcde64c5e86204e904aedf2
downloadarmen-master.tar.gz
armen-master.zip
git: update to v1.0HEADv1.0master
-rw-r--r--armen.c25
-rw-r--r--armen.h197
-rw-r--r--armen.mak125
-rw-r--r--avr/adc.c188
-rw-r--r--avr/core.S397
-rw-r--r--avr/core.h29
-rw-r--r--avr/gpio.c162
-rw-r--r--avr/pcint.c247
-rw-r--r--avr/pwm.c223
-rw-r--r--avr/tools.mak25
-rw-r--r--avr/uart.c42
-rw-r--r--avr/uart.h33
-rw-r--r--avr/usart.c569
-rw-r--r--avr/usi.c170
-rw-r--r--core.c319
-rw-r--r--core.h113
-rw-r--r--gpio.c54
-rw-r--r--mdep.S18
-rw-r--r--sem.c78
-rw-r--r--thread.c158
-rw-r--r--uart.c161
21 files changed, 3333 insertions, 0 deletions
diff --git a/armen.c b/armen.c
new file mode 100644
index 0000000..37fc04a
--- /dev/null
+++ b/armen.c
@@ -0,0 +1,25 @@
+/************************************************************************/
+/* */
+/* File: armen.c */
+/* Description: a simple wrapper for OS */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#include "core.c"
+#include "thread.c"
+#include "gpio.c"
+#if defined(UART) && (UART != 0)
+ #include "uart.c"
+#endif
+#ifdef USE_SEM
+ #include "sem.c"
+#endif
+
diff --git a/armen.h b/armen.h
new file mode 100644
index 0000000..404d05a
--- /dev/null
+++ b/armen.h
@@ -0,0 +1,197 @@
+/************************************************************************/
+/* */
+/* - armen a tiny operating system - */
+/* */
+/* File: armen.h */
+/* Description: armen api */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen" is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef _ARMEN_H_
+#define _ARMEN_H_
+
+#include <stdint.h>
+
+#define FALSE 0
+#define TRUE 1
+
+/*
+ * events
+*/
+#define EVENT_UART0 0x0001
+#define EVENT_UART1 0x0002
+#define EVENT_UART2 0x0004
+#define EVENT_UART3 0x0008
+#define EVENT_TIMER1 0x0010
+#define EVENT_TIMER2 0x0020
+#define EVENT_INT0 0x0040
+#define EVENT_INT1 0x0080
+#define EVENT_INT2 0x0100
+#define EVENT_PCINT0 0x0200
+#define EVENT_PCINT1 0x0400
+#define EVENT_PCINT2 0x0800
+#define EVENT_USER 0x2000
+
+#define EVENT_PCINT EVENT_PCINT0
+
+/*
+ * gpio
+*/
+#define GPIO_INPUT 0
+#define GPIO_OUTPUT 1
+
+#define GPIO_LOW 0
+#define GPIO_HIGH 1
+
+#define GPIO0 0
+#define GPIO1 1
+#define GPIO2 2
+#define GPIO3 3
+#define GPIO4 4
+#define GPIO5 5
+#define GPIO6 6
+#define GPIO7 7
+#define GPIO8 8
+#define GPIO9 9
+#define GPIO10 10
+#define GPIO11 11
+#define GPIO12 12
+#define GPIO13 13
+#define GPIO14 14
+#define GPIO15 15
+#define GPIO16 16
+#define GPIO17 17
+#define GPIO18 18
+#define GPIO19 19
+#define GPIO20 20
+#define GPIO21 21
+#define GPIO22 22
+#define GPIO23 23
+
+/*
+ * uart features
+*/
+#if defined(UART) && (UART != 0)
+ #define B1200 0
+ #define B2400 1
+ #define B4800 2
+ #define B9600 3
+ #define B19200 4
+ #define B38400 5
+ #define B57600 6
+ #define B115200 7
+ #define BAUDRATE 7
+ #define ECHOO 8 /* echo on output */
+#endif
+
+/*
+ * analog conversion
+*/
+#ifdef USE_ADC
+ #define ADC_REF_VCC 0
+ #define ADC_REF_EXT 1
+ #define ADC_REF_INT1V1 2
+ #define ADC_REF_INT2V2 3
+ #define ADC_REF_INT2V56 3
+ #define ADC_REF_INT4V096 4
+#endif
+
+/*
+ * pulse witdth modulation
+*/
+#ifdef USE_PWM
+ #define PWM_SCALE_1 1
+ #define PWM_SCALE_8 2
+ #define PWM_SCALE_64 3
+ #define PWM_SCALE_256 4
+ #define PWM_SCALE_1024 5
+#endif
+
+/*
+ * pin change interruptions
+*/
+#ifdef USE_PCINT
+ #define INT_LOW 0
+ #define INT_CHANGE 1
+ #define INT_RISING 2
+ #define INT_FALLING 3
+ #define INT_DISABLE 4
+#endif
+
+typedef int8_t thread_t;
+typedef uint32_t sem_t;
+#ifdef USE_BIG_TIMER
+typedef uint32_t timer_t;
+#else
+typedef uint16_t timer_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void armen_start( void );
+
+extern uint16_t wait_event( uint16_t events );
+extern void send_user_event( void );
+extern timer_t get_timer( uint8_t timer );
+extern int8_t set_timer( uint8_t timer, timer_t msecs );
+extern void sleep( timer_t msecs );
+extern void delay( uint16_t usecs );
+extern uint32_t get_time( void );
+
+extern thread_t thread_self( void );
+extern thread_t thread_create( void* (*start)(void*), void* arg );
+extern void thread_exit( void* retval );
+#ifdef USE_FULL_THREAD
+extern void thread_cancel( thread_t thread );
+extern void thread_join( thread_t thread, void** retval );
+extern uint8_t thread_nice( thread_t thread, uint8_t priority );
+#endif
+
+extern uint8_t test_and_set( void* lock );
+#ifdef USE_SEM
+extern void sem_init( sem_t* sem, uint8_t value );
+extern void sem_wait( sem_t* sem );
+extern void sem_post( sem_t* sem );
+#endif
+
+extern void gpio_pin_mode( uint8_t gpio, uint8_t mode, ... );
+extern int8_t gpio_pin_get( uint8_t gpio );
+extern void gpio_pin_set( uint8_t gpio, uint8_t value );
+#ifdef USE_ADC
+extern void gpio_adc_ref( uint8_t ref );
+extern int16_t gpio_adc_get( uint8_t gpio );
+#endif
+#ifdef USE_PWM
+extern void gpio_pwm_freq( uint8_t scale );
+extern void gpio_pwm_set( uint8_t gpio, uint8_t percent );
+#endif
+#ifdef USE_PCINT
+extern void gpio_int_enable( uint8_t interrupt, uint8_t mode );
+extern void gpio_pcint_enable( uint8_t gpio, uint8_t enable );
+#endif
+
+#if defined(UART) && (UART != 0)
+extern int8_t uart_open( uint8_t device, uint8_t config );
+extern int8_t uart_close( uint8_t device );
+extern int8_t uart_read( uint8_t device, uint8_t* byte );
+extern int8_t uart_write( uint8_t device, uint8_t byte );
+extern char uart_getchar( uint8_t device );
+extern void uart_putchar( uint8_t device, char c );
+extern void uart_putstr( uint8_t device, char* s );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/armen.mak b/armen.mak
new file mode 100644
index 0000000..628c7f8
--- /dev/null
+++ b/armen.mak
@@ -0,0 +1,125 @@
+# directory of armen files
+ifndef ARMEN_DIR
+ $(eval ARMEN_DIR = $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
+endif
+
+# arduino uno by default
+ifndef T_CPU
+ MODEL = arduino
+ T_CPU = atmega328p
+ F_CPU = 16000000UL
+endif
+
+# cpu target
+ifeq ($(T_CPU), $(filter $(T_CPU), atiny84 attiny841 atiny85 attiny1634 atmega328p atmega2560))
+ TARGET = avr
+else
+ $(error Unknown cpu target '$(T_CPU)')
+endif
+
+# directory for compilation
+ifndef BUILD_DIR
+ BUILD_DIR = $(ARMEN_DIR)/build/armen/$(T_CPU)
+ $(warning BUILD_DIR not defined, set to $(BUILD_DIR))
+endif
+
+# tools for target
+include $(ARMEN_DIR)/$(TARGET)/tools.mak
+
+# arduino boards
+ifeq ($(MODEL), arduino)
+ ARMEN_FLAGS += -DARDUINO
+ CFLAGS += -Wl,-u,vfprintf -lprintf_min
+ ifdef UART
+ $(eval UART = $(shell expr $(UART) + 1))
+ else
+ UART = 1
+ endif
+endif
+
+# armen core files
+ARMEN_OBJS = $(BUILD_DIR)/mdep.o $(BUILD_DIR)/armen.o
+ARMEN_CORE_S = $(ARMEN_DIR)/mdep.S $(ARMEN_DIR)/$(TARGET)/core.S
+ARMEN_CORE_C = $(ARMEN_DIR)/armen.c $(ARMEN_DIR)/core.c\
+ $(ARMEN_DIR)/thread.c $(ARMEN_DIR)/gpio.c $(ARMEN_DIR)/$(TARGET)/gpio.c
+
+# option: all thread functions
+ifdef FULL_THREAD
+ ifneq ($(FULL_THREAD), 0)
+ ARMEN_FLAGS += -DUSE_FULL_THREAD
+ endif
+endif
+
+# option: max count of thread(s)
+ifdef THREAD
+ ifneq ($(THREAD), 0)
+ ARMEN_FLAGS += -DTHREAD=$(THREAD)
+ endif
+endif
+
+# option: size of timer(s)
+ifdef BIG_TIMER
+ ifneq ($(BIG_TIMER), 0)
+ ARMEN_FLAGS += -DUSE_BIG_TIMER
+ endif
+endif
+
+# option: count of timer(s)
+ifdef TIMER
+ ifeq ($(TIMER), 2)
+ ARMEN_FLAGS += -DTIMER=$(TIMER)
+ endif
+endif
+
+# option: use semaphore
+ifdef SEM
+ ifneq ($(SEM), 0)
+ ARMEN_FLAGS += -DUSE_SEM
+ ARMEN_CORE_C += $(ARMEN_DIR)/sem.c
+ endif
+endif
+
+# option: count of uart(s)
+ifdef UART
+ ifneq ($(UART), 0)
+ ARMEN_FLAGS += -DUART=$(UART)
+ ARMEN_CORE_C += $(ARMEN_DIR)/uart.c $(ARMEN_DIR)/$(TARGET)/uart.c\
+ $(ARMEN_DIR)/$(TARGET)/usart.c $(ARMEN_DIR)/$(TARGET)/usi.c
+ endif
+endif
+
+# option: use analog conversion
+ifdef ADC
+ ifneq ($(ADC), 0)
+ ARMEN_FLAGS += -DUSE_ADC
+ ARMEN_CORE_C += $(ARMEN_DIR)/$(TARGET)/adc.c
+ endif
+endif
+
+# option: use pulse with modulation
+ifdef PWM
+ ifneq ($(PWM), 0)
+ ARMEN_FLAGS += -DUSE_PWM
+ ARMEN_CORE_C += $(ARMEN_DIR)/$(TARGET)/pwm.c
+ endif
+endif
+
+# option: use [pin change] interruption(s)
+ifdef PCINT
+ ifneq ($(PCINT), 0)
+ ARMEN_FLAGS += -DUSE_PCINT
+ ARMEN_CORE_C += $(ARMEN_DIR)/$(TARGET)/pcint.c
+ ifeq ($(PCINT), 2)
+ ARMEN_FLAGS += -DPCINT_PER_PORT
+ endif
+ endif
+endif
+
+# build armen core
+$(BUILD_DIR)/mdep.o: $(ARMEN_CORE_S)
+ @if [ ! -d $(BUILD_DIR) ]; then mkdir -p $(BUILD_DIR); fi
+ $(CC) $(ASFLAGS) $(ARMEN_FLAGS) -I$(ARMEN_DIR) -c -o $@ $<
+
+$(BUILD_DIR)/armen.o: $(ARMEN_CORE_C) $(ARMEN_DIR)/armen.h $(ARMEN_DIR)/core.h
+ @if [ ! -d $(BUILD_DIR) ]; then mkdir -p $(BUILD_DIR); fi
+ $(CC) -Wno-int-conversion $(CFLAGS) $(ARMEN_FLAGS) -I$(ARMEN_DIR) -c -o $@ $<
diff --git a/avr/adc.c b/avr/adc.c
new file mode 100644
index 0000000..c21bb8a
--- /dev/null
+++ b/avr/adc.c
@@ -0,0 +1,188 @@
+/************************************************************************/
+/* */
+/* File: avr/adc.c */
+/* Description: analog to digital conversion driver. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_GPIO_C
+ #error "do not use this code directly, it must be included via gpio.c"
+#endif
+
+static uint8_t adc_def = 0;
+static uint8_t adc_ref = 0;
+
+/*
+ * set refefence voltage for analog conversion
+ *
+ * it is assumed than if AVCC exist, it is connected to
+ * +5V supply and AREF is decoupled by a capacitor.
+ *
+ * +5V
+ * |
+ * +--> AVCC
+ *
+ * +--> AREF
+ * |
+ * ---
+ * 100nF
+ * ---
+ * |
+ * GND
+*/
+void gpio_adc_ref( uint8_t ref )
+{
+#ifdef CHECK_PARAM
+ if( ref >= ADC_REF_VCC
+ #if defined(__AVR_ATtiny841__)
+ && ADC_REF_INT4V096
+ #elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny85V__)\
+ || defined(__AVR_ATmega2560__)
+ && ref <= ADC_REF_INT2V56
+ #else /* __ATtiny1634__ || __ATmega328P__ */
+ && ref <= ADC_REF_INT1V1
+ #endif
+#endif
+ )
+ {
+#if defined(__AVR_ATtiny841__)
+ if( ref == ADC_REF_VCC )
+ adc_ref = 0;
+ else if( ref == ADC_REF_EXT )
+ adc_ref = (1<<REFS2);
+ else if( ref == ADC_REF_INT1V1 )
+ adc_ref = (1<<REFS0);
+ else if( ref == ADC_REF_INT2V2 )
+ adc_ref = (1<<REFS1);
+ else
+ adc_ref = (1<<REFS1) | (1<<REFS0);
+#elif defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny85V__)
+ if( ref == ADC_REF_VCC )
+ adc_ref = 0;
+ else if( ref == ADC_REF_EXT )
+ adc_ref = (1<<REFS0);
+ else if( ref == ADC_REF_INT1V1 )
+ adc_ref = (1<<REFS1);
+ else
+ adc_ref = (1<<REFS2) | (1<<REFS1);
+#elif defined(__AVR_ATtiny1634__)
+ if( ref == ADC_REF_VCC )
+ adc_ref = 0;
+ else if( ref == ADC_REF_EXT )
+ adc_ref = (1<<REFS0);
+ else
+ adc_ref = (1<<REFS1);
+#elif defined(__AVR_ATmega2560__)
+ if( ref == ADC_REF_VCC )
+ adc_ref = (1<<REFS0);
+ else if( ref == ADC_REF_EXT )
+ adc_ref = 0;
+ else if( ref == ADC_REF_INT1V1 )
+ adc_ref = (1<<REFS1);
+ else
+ adc_ref = (1<<REFS1) | (1<<REFS0);
+#else /* __ATmega328P__ */
+ if( ref == ADC_REF_VCC )
+ adc_ref = (1<<REFS0);
+ else if( ref == ADC_REF_EXT )
+ adc_ref = 0;
+ else
+ adc_ref = (1<<REFS1) | (1<<REFS0);
+#endif
+ adc_def = 1;
+ }
+}
+
+/*
+ * read value of an analog gpio pin
+ *
+ * input:
+ * gpio gpio pin
+ * output:
+ * adc conversion if success, otherwise -1
+*/
+int16_t gpio_adc_get( uint8_t gpio )
+{
+ uint16_t value = -1;
+ uint8_t prescale =
+#if (FCPU <= 400000UL)
+ 1;
+#elif (F_CPU <= 800000UL)
+ 2;
+#elif (F_CPU <= 1600000UL)
+ 3;
+#elif (F_CPU <= 3200000UL)
+ 4;
+#elif (F_CPU <= 6400000UL)
+ 5;
+#elif (F_CPU <= 12800000UL)
+ 6;
+#else
+ 7;
+#endif
+
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ {
+#if defined(__AVR_ATtiny84__)
+ if( gpio <= 7 )
+ {
+#elif defined(__AVR_ATtiny85__)
+ if( gpio >= 2 && gpio <= 5 )
+ {
+ if( gpio == 2 || gpio == 4)
+ gpio >>= 1;
+ else if( gpio == 5 )
+ gpio = 0;
+#elif defined(__AVR_ATtiny1634__)
+ if( (gpio >= 3 && gpio <= 11)
+ || (gpio >= 16 && gpio <= 18) )
+ {
+ gpio -= (gpio >= 16 ? 7 : 3);
+#elif defined(__AVR_ATmega328P__)
+ /* ADC6 and ADC7 are not implemented */
+ if( gpio >= 8 && gpio <= 13 )
+ {
+ gpio -= 8;
+#elif defined(__AVR_ATmega2560__)
+ if( gpio >= 40 && gpio <= 47 )
+ gpio -= 40;
+ else if( gpio >= 72 && gpio <= 79 )
+ gpio -= 64;
+ else
+ return( value );
+ {
+#else
+ {
+#endif
+ if( adc_def == 0 )
+ gpio_adc_ref( ADC_REF_VCC );
+
+#if defined(__AVR_ATtiny841__)
+ ADMUXA = gpio;
+ ADMUXB = adc_ref;
+#elif defined(__AVR_ATmega2560__)
+ ADMUX = adc_ref | (gpio & 0x07);
+ ADCSRB &= ~(1<<MUX5);
+ if( gpio > 8 )
+ ADCSRB |= (1<<MUX5);
+#else /* __AVR_ATtiny85__ || __AVR_ATtiny1634__ || __ATmega328P__ */
+ ADMUX = adc_ref | gpio;
+#endif
+
+ ADCSRA = (1<<ADEN) | (1<<ADSC) | prescale;
+ while( ADCSRA & (1<<ADSC) ) ;
+ value = ADCW;
+ }
+ }
+ return( value );
+}
diff --git a/avr/core.S b/avr/core.S
new file mode 100644
index 0000000..da35058
--- /dev/null
+++ b/avr/core.S
@@ -0,0 +1,397 @@
+/************************************************************************/
+/* */
+/* File: avr/core.s */
+/* Description: cpu dependent (or optimized) part of kernel */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef F_CPU
+ #error "cpu clock frequency is not defined"
+#endif
+
+#if (F_CPU == 16000000UL)
+ #define CLOCK_PRESCALE 3
+ #define CLOCK_TICKS 250
+#elif (F_CPU == 12000000UL)
+ #define CLOCK_PRESCALE 3
+ #define CLOCK_TICKS 187
+#elif (F_CPU == 8000000UL)
+ #define CLOCK_PRESCALE 3
+ #define CLOCK_TICKS 125
+#elif (F_CPU == 4000000UL)
+ #define CLOCK_PRESCALE 3
+ #define CLOCK_TICKS 62
+#else
+ #error "no parameters for the clock frequency"
+#endif
+
+#define SPH 0x3E
+#define SPL 0x3D
+#define SREG 0x3F
+
+#include <avr/io.h>
+
+#ifdef __AVR_HAVE_JMP_CALL__
+ #define JMP jmp
+ #define CALL call
+#else
+ #define JMP rjmp
+ #define CALL rcall
+#endif
+
+ .text
+
+ /*
+ * here we go ...
+ * in:
+ * out:
+ */
+ .global armen_start
+armen_start:
+ cli
+ /*
+ * initialize timer 0 to 1kHz (timer 1 if tiny85)
+ */
+#if defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny85V__)
+ ldi r24, _BV(CTC1)|_BV(CS13)|CLOCK_PRESCALE
+ sts TCCR1, r24 ; CTC mode + prescaler
+ ldi r24, CLOCK_TICKS
+ sts OCR1A, r24 ; counter A
+ ldi r24, _BV(OCIE1A)
+ sts TIMSK, r24 ; enable comparator
+#else
+ ldi r24, _BV(WGM01)
+ sts TCCR0A, r24 ; CTC mode
+ ldi r24, CLOCK_PRESCALE
+ sts TCCR0B, r24 ; prescaler
+ ldi r24, CLOCK_TICKS
+ sts OCR0A, r24 ; counter A
+ ldi r24, _BV(OCIE0A)
+ #ifdef TIMSK
+ sts TIMSK, r24 ; enable comparator
+ #else
+ sts TIMSK0, r24 ; enable comparator
+ #endif
+#endif
+ /*
+ * initialize the os
+ */
+ ldi r23, hi8(RAMEND)
+ ldi r22, lo8(RAMEND) ; arg2 = end of ram (stack)
+ ldi r25, hi8(__bss_end)
+ ldi r24, lo8(__bss_end) ; arg1 = end of bss (heap)
+ CALL _sys_init ; see core.c
+ sei
+ ret
+
+ /*
+ * clock (1 ms)
+ * in:
+ * out:
+ */
+ .global TIMER0_COMPA_vect
+TIMER0_COMPA_vect:
+ CALL _save_context
+ push r24
+ push r25
+ CALL _sys_clock ; see core.c
+ pop r25
+ pop r24
+.L1:
+ CALL _scheduler
+.L2:
+ /*
+ * restore context
+ */
+ pop r0
+ out SREG, r0
+ pop r0
+ pop r1
+ pop r2
+ pop r3
+ pop r4
+ pop r5
+ pop r6
+ pop r7
+ pop r8
+ pop r9
+ pop r10
+ pop r11
+ pop r12
+ pop r13
+ pop r14
+ pop r15
+ pop r16
+ pop r17
+ pop r18
+ pop r19
+ pop r20
+ pop r21
+ pop r22
+ pop r23
+ pop r24
+ pop r25
+ pop r26
+ pop r27
+ pop r28
+ pop r29
+ pop r30
+ pop r31
+ reti ; works well because is same as ret (for stack)
+ ; differ only in global interrupt flag
+
+ /*
+ * call the scheduler
+ * in:
+ * out:
+ */
+ .global _schedule
+_schedule:
+ CALL _save_context
+ JMP .L1
+
+ /*
+ * set initial thread context
+ * in:
+ * r24/r25 entry point (WARNING: must be in first flash 128 kb)
+ * r22/r23 thread call parameter
+ * r20/r21 stack pointer
+ * out:
+ * new SP
+ */
+ .global _init_context
+_init_context:
+ push ZH
+ push ZL
+ push r17
+ push r16
+ ldi r16, 33 ; size for all general + SREG registers
+#ifdef __AVR_3_BYTE_PC__
+ subi r20, 38
+#else
+ subi r20, 36 ; add 2 PC size for ret(i) and _thread_exit
+#endif
+ sbci r21, 0
+ mov ZH, r21
+ mov ZL, r20 ; top of new stack
+ clr r17
+.L3:
+ st Z+, r17 ; set all registers to 0
+ subi r16, 1
+ brne .L3
+#ifdef __AVR_3_BYTE_PC__
+ st Z+, r17
+#endif
+ st Z+, r25
+ st Z+, r24 ; entry point for ret(i)
+ ldi r25, hi8(_thread_exit)
+ ldi r24, lo8(_thread_exit)
+#ifdef __AVR_3_BYTE_PC__
+ st Z+, r17
+#endif
+ st Z+, r25
+ st Z+, r24 ; entry point for _thread_exit
+ cpi r22, 0
+ cpc r23, r17
+ breq .L4
+ mov ZH, r21
+ mov ZL, r20
+ std Z+24, r22
+ std Z+25, r23 ; set parameter in r24/r25 location
+.L4:
+ mov r25, r21
+ mov r24, r20
+ subi r24, 1
+ sbci r25, 0 ; new SP value
+ pop r16
+ pop r17
+ pop ZL
+ pop ZH
+ ret
+
+ /*
+ * save all general + SREG registers
+ * in:
+ * out:
+ * new SP
+ */
+ .global _save_context
+_save_context:
+#ifdef __AVR_3_BYTE_PC__
+ pop r1 ; bits 17-22 of PC
+#endif
+ push YH
+ push YL
+ push r27
+ push r26
+ push r25 ; registers sets in the right place
+ in r25, SREG ; save SREG
+ in YH, SPH
+ in YL, SPL
+ adiw YL, 6
+ ld r27, Y+
+ ld r26, Y+ ; save PC for return
+ st -Y, r31
+ st -Y, r30 ; set Z instead of PC in stack
+ push r24
+ push r23
+ push r22
+ push r21
+ push r20
+ push r19
+ push r18
+ push r17
+ push r16
+ push r15
+ push r14
+ push r13
+ push r12
+ push r11
+ push r10
+ push r9
+ push r8
+ push r7
+ push r6
+ push r5
+ push r4
+ push r3
+ push r2
+#ifdef __AVR_3_BYTE_PC__
+ clr r2
+ push r2
+#else
+ push r1
+#endif
+ push r0
+ push r25 ; store SREG
+ in r25, SPH
+ in r24, SPL ; return new SP
+ push r26
+ push r27 ; restore PC (context: 32+1+(2|3) bytes)
+#ifdef __AVR_3_BYTE_PC__
+ push r1
+#endif
+ ret
+
+ /*
+ * switch thread context (called by scheduler)
+ * in:
+ * r24/r25 new stack pointer
+ * out:
+ * no return
+ */
+ .global _restore_context
+_restore_context:
+#ifdef __AVR_3_BYTE_PC__
+ pop r0
+ pop r0
+#endif
+ pop r0
+ pop r0 ; PC stacked at _restore_context call
+ pop r0
+ pop r0 ; PC stacked at _scheduler call
+ out SPH, r25
+ out SPL, r24
+ JMP .L2
+
+ /*
+ * disable interruptions
+ * in:
+ * out:
+ * interruption flag
+ */
+ .global _disable_intr
+_disable_intr:
+ in r24, SREG
+ cli
+ andi r24, 0x80
+ ret
+
+ /*
+ * enable interruptions
+ * in:
+ * r24 interruption flag
+ * out:
+ */
+ .global _enable_intr
+_enable_intr:
+ cpi r24, 0
+ breq .L5
+ sei
+.L5:
+ ret
+
+ /*
+ * delay loops
+ * in:
+ * r24/r25 count of microseconds
+ * out:
+ */
+ .global delay
+delay:
+#if (F_CPU > 12000000UL)
+ nop
+ nop
+ nop
+ nop ; 4 cycles
+#endif
+#if (F_CPU > 8000000UL)
+ nop
+ nop
+ nop
+ nop ; 4 cycles
+#endif
+#if (F_CPU > 4000000UL)
+ nop
+ nop
+ nop
+ nop ; 4 cycles
+#endif
+ sbiw r24, 1 ; 2 cycles
+ brne delay ; 2 cycles or 1 if false
+ ret
+
+ /*
+ * this thread is elected when all others are suspend
+ * it do nothing (set cpu in sleep mode ???)
+ * in:
+ * out:
+ */
+ .global _idle
+_idle:
+ rjmp _idle
+
+ /*
+ * test and set a mutex (atomic operation)
+ * in:
+ * r24/r25 address of mutex (byte)
+ * out:
+ * previous state of mutex
+ */
+ .global test_and_set
+test_and_set:
+ push ZH
+ push ZL
+ mov ZH, r25
+ mov ZL, r24
+#ifdef XMEGA
+ ldi r24, 1
+ xch Z, r24
+#else
+ ldi r25, 1
+ cli
+ ld r24, Z
+ st Z, r25
+ sei
+#endif
+ pop ZL
+ pop ZH
+ ret ; r24 = previous state of mutex
diff --git a/avr/core.h b/avr/core.h
new file mode 100644
index 0000000..8e0feb8
--- /dev/null
+++ b/avr/core.h
@@ -0,0 +1,29 @@
+/************************************************************************/
+/* */
+/* File: avr/core.h */
+/* Description: definitions for AVR core */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef _AVR_CORE_H_
+#define _AVR_CORE_H_
+
+#ifndef F_CPU
+ #error "cpu clock frequency is not defined"
+#endif
+
+#ifdef __AVR_3_BYTE_PC__
+ #define IDLE_STACK_SIZE 45 /* 39 + 2 calls */
+#else
+ #define IDLE_STACK_SIZE 41 /* 37 + 2 calls */
+#endif
+
+#endif
diff --git a/avr/gpio.c b/avr/gpio.c
new file mode 100644
index 0000000..8a5885f
--- /dev/null
+++ b/avr/gpio.c
@@ -0,0 +1,162 @@
+/************************************************************************/
+/* */
+/* File: avr/gpio.c */
+/* Description: general purpose input/output driver. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_GPIO_C
+ #error "do not use this code directly, it must be included via gpio.c"
+#endif
+
+#include <stdint.h>
+#include <avr/io.h>
+
+#if defined(__AVR_ATtiny84__)
+ #define GPIO_MAX_PORT 2
+#elif defined(__AVR_ATtiny841__)
+ #define GPIO_MAX_PORT 2
+#elif defined(__AVR_ATtiny85__)
+ #define GPIO_MAX_PORT 1
+#elif defined(__AVR_ATtiny1634__)
+ #define GPIO_MAX_PORT 3
+#elif defined(__AVR_ATmega328P__)
+ #define GPIO_MAX_PORT 3
+#elif defined(__AVR_ATmega2560__)
+ #define GPIO_MAX_PORT 11
+#else
+ #error "no or unavailable cpu defined"
+#endif
+
+static struct {
+ uint8_t* pin;
+ uint8_t* ddr;
+ uint8_t* port;
+} ports[GPIO_MAX_PORT] = {
+#if defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny841__)
+ { 0x0039, 0x003A, 0x003B },
+ { 0x0036, 0x0037, 0x0038 }
+#elif defined(__AVR_ATtiny85__)
+ { 0x0016, 0x0017, 0x0018 }
+#elif defined(__AVR_ATtiny1634__)
+ { 0x002F, 0x0030, 0x0031 },
+ { 0x002B, 0x002C, 0x002D },
+ { 0x0027, 0x0028, 0x0029 }
+#elif defined(__AVR_ATmega328P__)
+ { 0x0023, 0x0024, 0x0025 },
+ { 0x0026, 0x0027, 0x0028 },
+ { 0x0029, 0x002A, 0x002B }
+#elif defined(__AVR_ATmega2560__)
+ { 0x0020, 0x0021, 0x0022 },
+ { 0x0023, 0x0024, 0x0025 },
+ { 0x0026, 0x0027, 0x0028 },
+ { 0x0029, 0x002A, 0x002B },
+ { 0x002C, 0x002D, 0x002E },
+ { 0x002F, 0x0030, 0x0031 },
+ { 0x0032, 0x0033, 0x0034 },
+ { 0x0100, 0x0101, 0x0102 },
+ { 0x0104, 0x0104, 0x0105 },
+ { 0x0106, 0x0107, 0x0108 },
+ { 0x0109, 0x010A, 0x010B }
+#endif
+};
+
+/*
+ * read state of a gpio pin
+ *
+ * input:
+ * gpio gpio pin
+ * output:
+ * low or high if success, otherwise -1
+*/
+int8_t gpio_pin_get( uint8_t gpio )
+{
+#ifdef CHECK_PARAM
+ if( gpio >= (GPIO_MAX_PORT << 3) )
+ return( -1 );
+#endif
+ return( (*ports[gpio >> 3].pin >> (gpio & 7)) & 1 );
+}
+
+/*
+ * set state of a gpio pin
+ *
+ * input:
+ * gpio gpio pin
+ * value low or high
+*/
+void gpio_pin_set( uint8_t gpio, uint8_t value )
+{
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ {
+ uint8_t port = gpio >> 3;
+ uint8_t mask = 1 << (gpio & 7);
+
+ if( value != 0 )
+ *ports[gpio >> 3].port |= mask;
+ else
+ *ports[gpio >> 3].port &= ~mask;
+ }
+}
+
+/*
+ * set a gpio pin as input
+ *
+ * input:
+ * gpio gpio pin
+*/
+void gpio_pin_mode_input( uint8_t gpio )
+{
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ *ports[gpio >> 3].ddr &= ~(1 << (gpio & 7));
+}
+
+/*
+ * set a gpio pin as output
+ *
+ * input:
+ * gpio gpio pin
+ * value initial value, low or high
+*/
+void gpio_pin_mode_output( uint8_t gpio, uint8_t value )
+{
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ {
+ uint8_t port = gpio >> 3;
+ uint8_t mask = 1 << (gpio & 7);
+
+ if( value != 0 )
+ *ports[port].port |= mask;
+ else
+ *ports[port].port &= ~mask;
+ *ports[port].ddr |= mask;
+ }
+}
+
+#ifdef USE_ADC
+ #include "avr/adc.c"
+#endif
+
+#ifdef USE_PWM
+ #include "avr/pwm.c"
+#endif
+
+#ifdef USE_PCINT
+ #include "avr/pcint.c"
+#endif
+
diff --git a/avr/pcint.c b/avr/pcint.c
new file mode 100644
index 0000000..4dea0c7
--- /dev/null
+++ b/avr/pcint.c
@@ -0,0 +1,247 @@
+/************************************************************************/
+/* */
+/* File: avr/pcint.c */
+/* Description: pin change interruption driver. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_GPIO_C
+ #error "do not use this code directly, it must be included via gpio.c"
+#endif
+
+#include "avr/core.h"
+#include <avr/interrupt.h>
+
+#if defined(__AVR_ATtiny84__)
+ #define GPIO_MAX_PCINT 2
+ #define GPIO_MAX_INT 1
+#elif defined(__AVR_ATtiny841__)
+ #define GPIO_MAX_PCINT 2
+ #define GPIO_MAX_INT 1
+#elif defined(__AVR_ATtiny85__)
+ #define GPIO_MAX_PCINT 1
+ #define GPIO_MAX_INT 1
+#elif defined(__AVR_ATtiny1634__)
+ #define GPIO_MAX_PCINT 3
+ #define GPIO_MAX_INT 1
+#elif defined(__AVR_ATmega328P__)
+ #define GPIO_MAX_PCINT 3
+ #define GPIO_MAX_INT 2
+#elif defined(__AVR_ATmega2560__)
+ #define GPIO_MAX_PCINT 3
+ #define GPIO_MAX_INT 3 /* really 8, limited to 3 by events mask */
+#endif
+
+static struct {
+ uint8_t* pcmsk;
+ uint8_t pcie;
+} pcints[GPIO_MAX_PCINT] = {
+#if defined(__AVR_ATtiny85__)
+ { 0x0015, 1<<PCIE0 }
+#elif defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny841__)
+ { 0x0032, 1<<PCIE0 },
+ { 0x0040, 1<<PCIE1 }
+#elif defined(__AVR_ATtiny1634__)
+ { 0x0047, 1<<PCIE0 },
+ { 0x0048, 1<<PCIE1 },
+ { 0x0049, 1<<PCIE2 }
+#elif defined(__AVR_ATmega328P__)\
+ || defined(__AVR_ATmega2560__)
+ { 0x006B, 1<<PCIE0 },
+ { 0x006C, 1<<PCIE1 },
+ { 0x006D, 1<<PCIE2 }
+ #endif
+};
+
+/*
+ * interrupt vectors
+*/
+ISR( INT0_vect )
+{
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+#ifdef GIMSK
+ if( (MCUCR & 0x03) == INT_LOW )
+ GIMSK &= ~(1 << INT0);
+#else
+ if( (EICRA & 0x03) == INT_LOW )
+ EIMSK &= ~1;
+#endif
+ _wakeup( EVENT_INT0, 0 );
+ _enable_intr( iflag );
+}
+
+#if (GPIO_MAX_INT >= 2)
+ ISR( INT1_vect )
+ {
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+ if( (EICRA & 0x0C) == INT_LOW )
+ EIMSK &= ~2;
+ _wakeup( EVENT_INT1, 0 );
+ _enable_intr( iflag );
+ }
+#endif
+#if (GPIO_MAX_INT == 3)
+ ISR( INT2_vect )
+ {
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+ if( (EICRA & 0x30) == INT_LOW )
+ EIMSK &= ~4;
+ _wakeup( EVENT_INT2, 0 );
+ _enable_intr( iflag );
+ }
+#endif
+
+ISR( PCINT0_vect )
+{
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+ _wakeup( EVENT_PCINT0, 0 );
+ _enable_intr( iflag );
+}
+
+#ifndef PCINT_PER_PORT
+ #if (GPIO_MAX_PCINT >= 2)
+ ISR( PCINT1_vect, ISR_ALIASOF( PCINT0_vect ) );
+ #endif
+ #if (GPIO_MAX_PCINT == 3)
+ ISR( PCINT2_vect, ISR_ALIASOF( PCINT0_vect ) );
+ #endif
+#else
+ #if (GPIO_MAX_PCINT >= 2)
+ ISR( PCINT1_vect )
+ {
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+ _wakeup( EVENT_PCINT1, 0 );
+ _enable_intr( iflag );
+ }
+ #endif
+ #if (GPIO_MAX_PCINT == 3)
+ ISR( PCINT2_vect )
+ {
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+ _wakeup( EVENT_PCINT2, 0 );
+ _enable_intr( iflag );
+ }
+ #endif
+#endif
+
+/*
+ * enable/disable interrupt on gpio pin change
+ *
+ * input:
+ * interrupt interrupt number (0, 1, 2)
+ * mode interrupt mode (LOW, CHANGE, RISING, FALLING, DISABLE)
+*/
+void gpio_int_enable( uint8_t interrupt, uint8_t mode )
+{
+#ifdef CHECK_PARAM
+ if( interrupt < GPIO_MAX_INT && mode <= INT_DISABLE )
+#endif
+ {
+ if( mode == INT_DISABLE )
+/*
+#if defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny85__)\
+ || defined(__AVR_ATtiny841__)\
+ || defined(__AVR_ATtiny1634__)
+*/
+#ifdef GIMSK
+ GIMSK &= ~(1 << INT0);
+ else
+ {
+ MCUCR |= mode;
+ GIMSK |= (1 << INT0);
+ }
+#else
+ EIMSK &= ~(1 << interrupt);
+ else
+ {
+ #if 0
+ #if defined(__AVR_ATmega2560__)
+ if( interrupt >= 4 )
+ EICRB |= (mode << (interrupt-4));
+ else
+ #endif
+ #endif
+ EICRA |= (mode << interrupt);
+ EIMSK |= (1 << interrupt);
+ }
+#endif
+ }
+}
+
+/*
+ * enable/disable interrupt on gpio pin change
+ *
+ * input:
+ * gpio gpio pin
+ * enable true or false
+*/
+void gpio_pcint_enable( uint8_t gpio, uint8_t enable )
+{
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ {
+#if defined(__AVR_ATtiny1634__)
+ if( gpio > 11 )
+ gpio += 4;
+ if( gpio < 22 )
+#elif defined(__AVR_ATmega328P__)
+ if( gpio != 15 )
+#elif defined(__AVR_ATmega2560__)
+ if( gpio >= 8 && gpio <= 15 )
+ gpio -= 8;
+ else if( gpio == 32 )
+ gpio = 8;
+ else if( gpio >= 64 && gpio <= 70 )
+ gpio -= 55;
+ else if( gpio >= 72 && gpio <= 79 )
+ gpio -= 56;
+ else
+ return;
+#endif
+ {
+ uint8_t port = gpio >> 3;
+ uint8_t mask = 1 << (gpio & 7);
+
+ if( enable != 0 )
+ {
+ *pcints[port].pcmsk |= mask;
+/*
+#if defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny85__)\
+ || defined(__AVR_ATtiny841__)\
+ || defined(__AVR_ATtiny1634__)
+*/
+#ifdef GIMSK
+ GIMSK |= pcints[port].pcie;
+#else
+ PCICR |= pcints[port].pcie;
+#endif
+ }
+ else
+ *pcints[port].pcmsk &= ~mask;
+ }
+ }
+}
diff --git a/avr/pwm.c b/avr/pwm.c
new file mode 100644
index 0000000..6860053
--- /dev/null
+++ b/avr/pwm.c
@@ -0,0 +1,223 @@
+/************************************************************************/
+/* */
+/* File: avr/pwm.c */
+/* Description: pulse width modulation driver. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_GPIO_C
+ #error "do not use this code directly, it must be included via gpio.c"
+#endif
+
+#if defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny85__)
+ #if defined(UART) && (UART != 0)
+ #error "pwm and uart, used together, are incompatible"
+ #endif
+#endif
+
+#if defined(__AVR_ATtiny84__)
+ #define GPIO_MAX_PWM 1
+#elif defined(__AVR_ATtiny841__)
+ #define GPIO_MAX_PWM 2
+#elif defined(__AVR_ATtiny85__)
+ #define GPIO_MAX_PWM 1
+#elif defined(__AVR_ATtiny1634__)
+ #define GPIO_MAX_PWM 1
+#elif defined(__AVR_ATmega328P__)
+ #define GPIO_MAX_PWM 2
+#elif defined(__AVR_ATmega2560__)
+ #define GPIO_MAX_PWM 5
+#endif
+
+/*
+ __AVR_ATtiny84__
+ t1, 16 bits, OC1A=PA6/6, OC1B=PA5/5
+ __AVR_ATtiny85__
+ t1, 8 bits, OC1A=PB1/1, OC1B=PB4/4
+ __AVR_ATtiny841__
+ t1, 16 bits, OC1A=TOCC1|TOCC3|TOCC5|TOCC7, OC1B=TOCC0|TOCC2|TOCC4|TOCC6
+ t2, 16 bits, OC2A=TOCC1|TOCC3|TOCC5|TOCC7, OC2B=TOCC0|TOCC2|TOCC4|TOCC6
+ __AVR_ATtiny1634__
+ t1, 16 bits, OC1A=PC0/16, OC1B=PB3/11
+ __AVR_ATmega328P__
+ t1, 16 bits, OC1A=PB1/1, OC1B=PB2/2
+ t2, 8 bits, OC2A=PB3/3, OC2B=PD3/19
+ __AVR_ATmega2560__
+ t1, 16 bits, OC1A=PB5/13, OC1B=PB6/14, 0C1C=PB7/15
+ t2, 8 bits, OC2A=PB4/12, OC2B=PH6/62
+ t3, 16 bits, OC3A=PE3/35, OC3B=PE4/36, OC3C=PE5/37
+ t4, 16 bits, OC4A=PH3/59, OC4B=PH4/60, OC4C=PH5/61
+ t5, 16 bits, OC5A=PL3/83, OC5B=PL4/84, OC5C=PL5/85
+*/
+static uint8_t pwm_freq = 0;
+
+static struct {
+ uint8_t* tccra;
+ uint8_t* tccrb;
+ uint8_t* ocra;
+ uint8_t* ocrb;
+ uint8_t* ocrc;
+ uint8_t modea;
+ uint8_t modeb;
+ uint8_t bytes;
+} pwms[GPIO_MAX_PWM] = {
+#if defined(__AVR_ATtiny84__)
+ { 0x004F, 0x004E, 0x004A, 0x0048, 0x0000,
+ (1<<COM1A1)|(1<<COM1B1)|(1<<WGM10), (1<<WGM12), 2 }
+#elif defined(__AVR_ATtiny85__)
+ { 0x002A, 0x0033, 0x0029, 0x0028, 0x0000,
+ (1<<COM0A1)(1<<COM0B1)|(1<<WGM01)|(1<<WGM00), 0, 1 }
+#elif defined(__AVR_ATtiny841__)
+ { 0x004F, 0x004E, 0x004A, 0x0048, 0x0000,
+ (1<<COM1A1)|(1<<COM1B1)|(1<<WGM10), (1<<WGM12), 2 },
+ #if 0
+ { 0x00CA, 0x00C9, 0x00C4, 0x00C2, 0x0000,
+ (1<<COM2A1)|(1<<COM2B1)|(1<<WGM20), (1<<WGM22), 2 }
+ #endif
+#elif defined(__AVR_ATtiny1634__)
+ { 0x0072, 0x0071, 0x006C, 0x006A, 0x0000,
+ (1<<COM1A1)|(1<<COM1B1)|(1<<WGM10), (1<<WGM12), 2 }
+#elif defined(__AVR_ATmega328P__)
+ { 0x0080, 0x0081, 0x0088, 0x008A, 0x0000,
+ (1<<COM1A1)|(1<<COM1B1)|(1<<WGM10), (1<<WGM12), 2 },
+ { 0x00B0, 0x00B1, 0x00B3, 0x00B4, 0x0000,
+ (1<<COM2A1)|(1<<COM2B1)|(1<<WGM21)|(1<<WGM20), 0, 1 }
+#elif defined(__AVR_ATmega2560__)
+ { 0x0080, 0x0081, 0x0088, 0x008A, 0x008C,
+ (1<<COM1A1)|(1<<COM1B1)|(1<<COM1C1)|(1<<WGM10), (1<<WGM12), 2 },
+ { 0x00B0, 0x00B1, 0x00B3, 0x00B4, 0x0000,
+ (1<<COM2A1)|(1<<COM2B1)|(1<<WGM21)|(1<<WGM20), 0, 1 },
+ { 0x0090, 0x0091, 0x0098, 0x009A, 0x009C,
+ (1<<COM3A1)|(1<<COM3B1)|(1<<COM3C1)|(1<<WGM30), (1<<WGM32), 2 },
+ { 0x00A0, 0x00A1, 0x00A8, 0x00AB, 0x00AC,
+ (1<<COM4A1)|(1<<COM4B1)|(1<<COM4C1)|(1<<WGM40), (1<<WGM42), 2 },
+ { 0x0120, 0x0121, 0x0128, 0x012A, 0x012C,
+ (1<<COM5A1)|(1<<COM5B1)|(1<<COM5C1)|(1<<WGM50), (1<<WGM52), 2 }
+#endif
+};
+
+void gpio_pwm_freq( uint8_t scale )
+{
+#ifdef CHECK_PARAM
+ if( scale >= 1 && scale <= 5 )
+#endif
+ {
+ uint8_t i;
+
+ /* set fast pwm (8 bits) mode with OCnA and 0CnB cleared on compare match */
+ for( i = 0; i < GPIO_MAX_PWM; i++ )
+ {
+ *pwms[i].tccra = pwms[i].modea;
+#if defined(__AVR_ATmega328P__)\
+ || defined(__AVR_ATmega2560__)
+ if( i == 1 && scale > 2 )
+ {
+ uint8_t value;
+ if( scale == 3 )
+ value = 4;
+ else if( scale == 4 )
+ value = 6;
+ else
+ value = 7;
+ *pwms[i].tccrb = pwms[i].modeb | value;
+ else
+#endif
+ *pwms[i].tccrb = pwms[i].modeb | scale;
+ *pwms[i].ocra = 0;
+ *pwms[i].ocrb = 0;
+ if( pwms[i].ocrc != 0 )
+ *pwms[i].ocrc = 0;
+ }
+ }
+}
+
+void gpio_pwm_set( uint8_t gpio, uint8_t percent )
+{
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ {
+ uint8_t timer = 0;
+
+#if defined(__AVR_ATtiny84__)
+ if( gpio == 6 || gpio == 5 )
+ {
+ gpio = 6 - gpio;
+#elif defined(__AVR_ATtiny85__)
+ if( gpio == 1 || gpio == 4 )
+ {
+ gpio = (gpio == 1 ? 0 : 1);
+#elif defined(__AVR_ATtiny841__)
+ if( gpio <= 7 )
+ {
+ /* OC1A=TOCC1|TOCC3|TOCC5|TOCC7
+ OC1B=TOCC0|TOCC2|TOCC4|TOCC6 */
+ if( gpio <= 3 )
+ TOCPMSA0 |= (01 << (gpio << 2));
+ else
+ TOCPMSA1 |= (01 << ((gpio - 4) << 2));
+ TOCPMCOE |= (1 << gpio);
+ gpio = ((gpio & 1) == 1 ? 0 : 1);
+#elif defined(__AVR_ATtiny1634__)
+ if( gpio == 16 || gpio == 11 )
+ {
+ gpio = (gpio == 16 ? 0 : 1);
+#elif defined(__AVR_ATmega328P__)
+ if( gpio == 1 || gpio == 2 )
+ gpio -= 1;
+ else if( gpio == 3 || gpio == 19 )
+ {
+ gpio = (gpio == 3 ? 0 : 1);
+ timer = 1;
+ }
+ else
+ return;
+ {
+#elif defined(__AVR_ATmega2560__)
+ if( gpio >= 13 || gpio <= 15 )
+ gpio -= 13;
+ else if( gpio == 12 || gpio == 62 )
+ {
+ gpio = (gpio == 12 ? 0 : 1);
+ timer = 1;
+ }
+ else if( gpio >= 35 && gpio <= 37 )
+ {
+ gpio -= 35;
+ timer = 2;
+ }
+ else if( gpio >= 59 && gpio <= 61 )
+ {
+ gpio -= 59;
+ timer = 3;
+ }
+ else if( gpio >= 83 && gpio <= 85 )
+ {
+ gpio -= 83;
+ timer = 4;
+ }
+ else
+ return;
+ {
+#endif
+ percent = (255 * percent) / 100;
+ if( gpio == 0 )
+ *pwms[timer].ocra = percent;
+#if defined(__AVR_ATmega2560__)
+ else if( gpio == 2 )
+ *pwms[timer].ocrc = percent;
+#endif
+ else
+ *pwms[timer].ocrb = percent;
+ }
+ }
+}
diff --git a/avr/tools.mak b/avr/tools.mak
new file mode 100644
index 0000000..ee3a506
--- /dev/null
+++ b/avr/tools.mak
@@ -0,0 +1,25 @@
+CC = avr-gcc
+LD = avr-ld
+AS = avr-as
+AR = avr-ar
+FS = avr-size
+FC = avr-strip
+OC = avr-objcopy
+OD = avr-objdump
+DBG = avr-gdb
+PRG = avrdude
+SIM = simavr
+
+CFLAGS = -mmcu=$(T_CPU) -Os
+LDFLAGS =
+ASFLAGS = -mmcu=$(T_CPU) -Os
+PRGFLAGS += -C /etc/avrdude.conf -p `echo $(T_CPU)|awk '{print substr($$1,3,1)substr($$1,7)}'` -D
+
+%.o: %.s
+ $(AS) $(ASFLAGS) -o $@ $<
+
+%.hex: %.elf
+ $(OC) -O ihex $< $@
+
+%.lst: %.elf
+ $(OD) -z -h -j .text -j .data -j .bss -S $< >$@
diff --git a/avr/uart.c b/avr/uart.c
new file mode 100644
index 0000000..2a22181
--- /dev/null
+++ b/avr/uart.c
@@ -0,0 +1,42 @@
+/************************************************************************/
+/* */
+/* File: avr/uart.c */
+/* Description: serial line device. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_UART_C
+ #error "do not use this code directly, it must be included via uart.c"
+#endif
+
+#if defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny85__)
+ #include "avr/usi.c"
+#else
+ #include "avr/usart.c"
+#endif
+
+void _uart_init( void )
+{
+ uint8_t i;
+
+ for( i = 0; i < ARMEN_UARTS; i++ )
+ {
+ devices[i].open = _uart_open;
+ devices[i].close = _uart_close;
+ devices[i].read = _uart_read;
+ devices[i].write = _uart_write;
+ #ifdef UART_IOCTL
+ devices[i].ioctl = _uart_ioctl;
+ #endif
+ }
+}
+
diff --git a/avr/uart.h b/avr/uart.h
new file mode 100644
index 0000000..adfc884
--- /dev/null
+++ b/avr/uart.h
@@ -0,0 +1,33 @@
+/************************************************************************/
+/* */
+/* File: avr/uart.h */
+/* Description: serial line device. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef _AVR_UART_H_
+#define _AVR_UART_H_
+
+ #if defined(__AVR_ATtiny84__)\
+ || defined(__AVR_ATtiny85__)
+ #define ARMEN_UART_MAX 0
+ #elif defined(__AVR_ATmega328P__)
+ #define ARMEN_UART_MAX 1
+ #elif defined(__AVR_ATtiny841__)\
+ || defined(__AVR_ATtiny1634__)
+ #define ARMEN_UART_MAX 2
+ #elif defined(__AVR_ATmega2560__)
+ #define ARMEN_UART_MAX 4
+ #else
+ #define ARMEN_UART_MAX 0
+ #endif
+
+#endif
diff --git a/avr/usart.c b/avr/usart.c
new file mode 100644
index 0000000..ed5dabf
--- /dev/null
+++ b/avr/usart.c
@@ -0,0 +1,569 @@
+/************************************************************************/
+/* */
+/* File: avr/usart.c */
+/* Description: serial line device. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_UART_C
+ #error "do not use this code directly, it must be included via avr_uart.c"
+#endif
+
+/*
+#define OUTPUT_INTERRUPT
+*/
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+/*
+ * the uart is configured with 8 data bits, no parity and 1 stop bit.
+*/
+
+#if defined(__AVR_ATtiny841__)
+
+ #define UART0_INPUT_PIN PA2 /* receive */
+ #define UART0_OUTPUT_PIN PA1 /* transmit */
+ #define UART0_CONFIG_PINS() (\
+ DDRA = (DDRA|(1<<UART0_OUTPUT_PIN))\
+ & ~(1<<UART0_INPUT_PIN)\
+ )
+ #define UART1_INPUT_PIN PA4 /* receive */
+ #define UART1_OUTPUT_PIN PA5 /* transmit */
+ #define UART1_CONFIG_PINS() (\
+ DDRA = (DDRA|(1<<UART1_OUTPUT_PIN))\
+ & ~(1<<UART1_INPUT_PIN)\
+ )
+#elif defined(__AVR_ATtiny1634__)
+
+ #define UART0_INPUT_PIN PA7 /* receive */
+ #define UART0_OUTPUT_PIN PB0 /* transmit */
+ #define UART0_CONFIG_PINS() (\
+ DDRA = (DDRA|(1<<UART0_OUTPUT_PIN))\
+ & ~(1<<UART0_INPUT_PIN)\
+ )
+ #define UART1_INPUT_PIN PB1 /* receive */
+ #define UART1_OUTPUT_PIN PB2 /* transmit */
+ #define UART1_CONFIG_PINS() (\
+ DDRB = (DDRB|(1<<UART1_OUTPUT_PIN))\
+ & ~(1<<UART1_INPUT_PIN)\
+ )
+#elif defined(__AVR_ATmega328P__)
+
+ #define UART0_INPUT_PIN PD0 /* receive (uno:0) */
+ #define UART0_OUTPUT_PIN PD1 /* transmit (uno:1) */
+ #define UART0_CONFIG_PINS() (\
+ DDRD = (DDRD|(1<<UART0_OUTPUT_PIN))\
+ & ~(1<<UART0_INPUT_PIN)\
+ )
+#elif defined(__AVR_ATmega2560__)
+
+ #define UART0_INPUT_PIN PE0
+ #define UART0_OUTPUT_PIN PE1
+ #define UART0_CONFIG_PINS() (\
+ DDRE = (DDRE|(1<<UART0_OUTPUT_PIN))\
+ & ~(1<<UART0_INPUT_PIN)\
+ )
+ #define UART1_INPUT_PIN PD2 /* receive (mega:19) */
+ #define UART1_OUTPUT_PIN PD3 /* transmit (mega:18) */
+ #define UART1_CONFIG_PINS() (\
+ DDRD = (DDRD|(1<<UART1_OUTPUT_PIN))\
+ & ~(1<<UART1_INPUT_PIN)\
+ )
+ #define UART2_INPUT_PIN PH0 /* receive (mega:17) */
+ #define UART2_OUTPUT_PIN PH1 /* transmit (mega:16) */
+ #define UART2_CONFIG_PINS() (\
+ DDRH = (DDRH|(1<<UART2_OUTPUT_PIN))\
+ & ~(1<<UART2_INPUT_PIN)\
+ )
+ #define UART3_INPUT_PIN PJ0 /* receive (mega:15) */
+ #define UART3_OUTPUT_PIN PJ1 /* transmit (mega:14) */
+ #define UART3_CONFIG_PINS() (\
+ DDRJ = (DDRJ|(1<<UART3_OUTPUT_PIN))\
+ & ~(1<<UART3_INPUT_PIN)\
+ )
+#else
+ #error "no or unavailable cpu defined"
+#endif
+
+#if defined(USART_RX_vect)
+ #define UART0_INPUT_INT USART_RX_vect
+ #define UART0_OUTPUT_INT USART_UDRE_vect
+#else
+ #define UART0_INPUT_INT USART0_RX_vect
+ #define UART0_OUTPUT_INT USART_UDRE_vect
+#endif
+
+#define UART0_RX_BYTE UDR0
+#define UART0_RX_INT_ENABLE() (UCSR0B |= (1<<RXCIE0))
+#define UART0_RX_INT_DISABLE() (UCSR0B &= ~(1<<RXCIE0))
+#define UART0_TX_BYTE UDR0
+#define UART0_TX_INT_ENABLE() (UCSR0B |= (1<<UDRIE0))
+#define UART0_TX_INT_DISABLE() (UCSR0B &= ~(1<<UDRIE0))
+#define UART0_TX_NOT_READY() ((UCSR0A & (1<<UDRE0))==0)
+#define UART0_CONFIG_BAUDRATE(b) (\
+ UCSR0A = (1<<U2X0),\
+ UBRR0H = (b)>>8, UBRR0L = (b)&0xFF\
+ )
+#define UART0_CONFIG_DEVICE() (\
+ UCSR0B = (1<<RXEN0)|(1<<TXEN0),\
+ UCSR0C = (1<<UCSZ00)|(1<<UCSZ01)\
+ )
+#define UART0_CONFIG(b) (\
+ UART0_CONFIG_PINS(),\
+ UART0_CONFIG_BAUDRATE(b),\
+ UART0_CONFIG_DEVICE()\
+ )
+
+
+#define UART1_INPUT_INT USART1_RX_vect
+#define UART1_OUTPUT_INT USART1_UDRE_vect
+
+#define UART1_RX_BYTE UDR1
+#define UART1_RX_INT_ENABLE() (UCSR1B |= (1<<RXCIE1))
+#define UART1_RX_INT_DISABLE() (UCSR1B &= ~(1<<RXCIE1))
+#define UART1_TX_BYTE UDR1
+#define UART1_TX_INT_ENABLE() (UCSR1B |= (1<<UDRIE1))
+#define UART1_TX_INT_DISABLE() (UCSR1B &= ~(1<<UDRIE1))
+#define UART1_TX_NOT_READY() ((UCSR1A & (1<<UDRE1))==0)
+#define UART1_CONFIG_BAUDRATE(b) (\
+ UCSR1A = (1<<U2X1),\
+ UBRR1H = (b)>>8, UBRR1L = (b)&0xFF\
+ )
+#define UART1_CONFIG_DEVICE() (\
+ UCSR1B = (1<<RXEN1)|(1<<TXEN1),\
+ UCSR1C = (1<<UCSZ10)|(1<<UCSZ11)\
+ )
+#define UART1_CONFIG(b) (\
+ UART1_CONFIG_PINS(),\
+ UART1_CONFIG_BAUDRATE(b),\
+ UART1_CONFIG_DEVICE()\
+ )
+
+
+#define UART2_INPUT_INT USART2_RX_vect
+#define UART2_OUTPUT_INT USART2_UDRE_vect
+
+#define UART2_RX_BYTE UDR2
+#define UART2_RX_INT_ENABLE() (UCSR2B |= (1<<RXCIE2))
+#define UART2_RX_INT_DISABLE() (UCSR2B &= ~(1<<RXCIE2))
+#define UART2_TX_BYTE UDR2
+#define UART2_TX_INT_ENABLE() (UCSR2B |= (1<<UDRIE2))
+#define UART2_TX_INT_DISABLE() (UCSR2B &= ~(1<<UDRIE2))
+#define UART2_TX_NOT_READY() ((UCSR2A & (1<<UDRE2))==0)
+#define UART2_CONFIG_BAUDRATE(b) (\
+ UCSR2A = (1<<U2X2),\
+ UBRR2H = (b)>>8, UBRR2L = (b)&0xFF\
+ )
+#define UART2_CONFIG_DEVICE() (\
+ UCSR2B = (1<<RXEN2)|(1<<TXEN2),\
+ UCSR2C = (1<<UCSZ10)|(1<<UCSZ11)\
+ )
+#define UART2_CONFIG(b) (\
+ UART2_CONFIG_PINS(),\
+ UART2_CONFIG_BAUDRATE(b),\
+ UART2_CONFIG_DEVICE()\
+ )
+
+
+#define UART3_INPUT_INT USART3_RX_vect
+#define UART3_OUTPUT_INT USART3_UDRE_vect
+
+#define UART3_RX_BYTE UDR3
+#define UART3_RX_INT_ENABLE() (UCSR3B |= (1<<RXCIE3))
+#define UART3_RX_INT_DISABLE() (UCSR3B &= ~(1<<RXCIE3))
+#define UART3_TX_BYTE UDR3
+#define UART3_TX_INT_ENABLE() (UCSR3B |= (1<<UDRIE3))
+#define UART3_TX_INT_DISABLE() (UCSR3B &= ~(1<<UDRIE3))
+#define UART3_TX_NOT_READY() ((UCSR3A & (1<<UDRE3))==0)
+#define UART3_CONFIG_BAUDRATE(b) (\
+ UCSR3A = (1<<U2X3),\
+ UBRR3H = (b)>>8, UBRR3L = (b)&0xFF\
+ )
+#define UART3_CONFIG_DEVICE() (\
+ UCSR3B = (1<<RXEN3)|(1<<TXEN3),\
+ UCSR3C = (1<<UCSZ10)|(1<<UCSZ11)\
+ )
+#define UART3_CONFIG(b) (\
+ UART3_CONFIG_PINS(),\
+ UART3_CONFIG_BAUDRATE(b),\
+ UART3_CONFIG_DEVICE()\
+ )
+
+static uint16_t uart_bauds[8] = {
+#if (F_CPU == 16000000UL )
+ 1666, 832, 416, 207, 103, 51, 34, 16
+#elif (F_CPU == 12000000UL )
+ 1249, 624, 312, 155, 77, 38, 25, 12
+#elif (F_CPU == 8000000UL )
+ 832, 416, 207, 103, 51, 34, 16, 8
+#elif (F_CPU == 4000000UL )
+ 416, 207, 103, 51, 25, 16, 8, 3
+#else
+ #error "no parameters for the clock frequency"
+#endif
+};
+
+#define UBRR_VALUE(b) (((F_CPU)+4UL*(b))/(8UL*(b))-1UL)
+
+#define UART_OPEN 0x80
+#define UART_ECHOO_CHECK 0x40
+#define UART_TX_PROGRESS 0x20
+#define UART_CONFIG_MASK 0x0F
+
+static struct {
+ uint8_t state; /* state and configuration */
+ uint8_t cd_byte; /* received byte if collision detection */
+ uint8_t rx_head; /* fifo index */
+ uint8_t rx_tail; /* " " */
+ uint8_t rx_buffer[UART_RX_BUFFER_SIZE+1];
+} volatile uarts[ARMEN_UARTS];
+
+static void _uart_recv_byte( uint8_t device, uint8_t byte )
+{
+ if( uarts[device].state & UART_ECHOO_CHECK )
+ {
+ uarts[device].cd_byte = byte;
+ uarts[device].state &= ~UART_ECHOO_CHECK;
+ }
+ else
+ {
+ uint8_t head;
+
+ head = uarts[device].rx_head + 1;
+ if( head == (UART_RX_BUFFER_SIZE + 1) )
+ head = 0;
+ if( head != uarts[device].rx_tail )
+ {
+ uarts[device].rx_buffer[head] = byte;
+ uarts[device].rx_head = head;
+ _wakeup( EVENT_UART0 + device, 0 );
+ }
+ }
+}
+
+/*
+ * occurs at end of byte reception
+*/
+ISR( UART0_INPUT_INT )
+{
+ uint8_t byte = UART0_RX_BYTE;
+ _uart_recv_byte( 0, byte );
+}
+
+#if (ARMEN_UARTS >= 2)
+ISR( UART1_INPUT_INT )
+{
+ uint8_t byte = UART1_RX_BYTE;
+ _uart_recv_byte( 1, byte );
+}
+#endif
+
+#if (ARMEN_UARTS >= 3)
+ISR( UART2_INPUT_INT )
+{
+ uint8_t byte = UART2_RX_BYTE;
+ _uart_recv_byte( 2, byte );
+}
+#endif
+
+#if (ARMEN_UARTS == 4)
+ISR( UART3_INPUT_INT )
+{
+ uint8_t byte = UART3_RX_BYTE;
+ _uart_recv_byte( 3, byte );
+}
+#endif
+
+/*
+ * occurs at end of byte transmission
+*/
+#ifdef OUTPUT_INTERRUPT
+ ISR( UART0_OUTPUT_INT )
+ {
+ uarts[0].state &= ~UART_TX_PROGRESS;
+ UART0_TX_INT_DISABLE( );
+ }
+
+ #if (ARMEN_UARTS >= 2)
+ ISR( UART1_OUTPUT_INT )
+ {
+ uarts[1].state &= ~UART_TX_PROGRESS;
+ UART1_TX_INT_DISABLE( );
+ }
+ #endif
+
+ #if (ARMEN_UARTS >= 3)
+ ISR( UART2_OUTPUT_INT )
+ {
+ uarts[2].state &= ~UART_TX_PROGRESS;
+ UART2_TX_INT_DISABLE( );
+ }
+ #endif
+
+ #if (ARMEN_UARTS == 4)
+ ISR( UART3_OUTPUT_INT )
+ {
+ uarts[3].state &= ~UART_TX_PROGRESS;
+ UART3_TX_INT_DISABLE( );
+ }
+ #endif
+#endif
+
+/*
+ * initialize device
+ *
+ * input:
+ * device identifier
+ * config uart configuration
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t _uart_open( uint8_t device, uint8_t config )
+{
+ uint8_t bauds = config & BAUDRATE;
+ uint16_t ubrr = uart_bauds[bauds];
+
+#ifdef CHECK_PARAM
+ if( (uarts[device].state & UART_OPEN) != 0 || bauds > B115200 )
+ return( -1 );
+#endif
+ uarts[device].rx_head = uarts[device].rx_tail = 0;
+ uarts[device].state = (config & UART_CONFIG_MASK) | UART_OPEN;
+ uarts[device].rx_head = uarts[device].rx_tail = 0;
+ if( device == 0 )
+ {
+ UART0_CONFIG( ubrr );
+ UART0_RX_INT_ENABLE( );
+ }
+#if (ARMEN_UARTS >= 2)
+ else if( device == 1 )
+ {
+ UART1_CONFIG( ubrr );
+ UART1_RX_INT_ENABLE( );
+ }
+#endif
+#if (ARMEN_UARTS >= 3)
+ else if( device == 2 )
+ {
+ UART2_CONFIG( ubrr );
+ UART2_RX_INT_ENABLE( );
+ }
+#endif
+#if (ARMEN_UARTS == 4)
+ else
+ {
+ UART3_CONFIG( ubrr );
+ UART3_RX_INT_ENABLE( );
+ }
+#endif
+}
+
+/*
+ * release device
+ *
+ * input:
+ * device identifier
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t _uart_close( uint8_t device )
+{
+#ifdef CHECK_PARAM
+ if( (uarts[device].state & UART_OPEN) == 0 )
+ return( -1 );
+#endif
+ if( device == 0 )
+ {
+ UART0_RX_INT_DISABLE( );
+#ifdef OUTPUT_INTERRUPT
+ UART0_TX_INT_DISABLE( );
+#endif
+ }
+#if (ARMEN_UARTS >= 2)
+ else if( device == 1 )
+ {
+ UART1_RX_INT_DISABLE( );
+ #ifdef OUTPUT_INTERRUPT
+ UART1_TX_INT_DISABLE( );
+ #endif
+ }
+#endif
+#if (ARMEN_UARTS >= 3)
+ else if( device == 2 )
+ {
+ UART2_RX_INT_DISABLE( );
+ #ifdef OUTPUT_INTERRUPT
+ UART2_TX_INT_DISABLE( );
+ #endif
+ }
+#endif
+#if (ARMEN_UARTS == 4)
+ else
+ {
+ UART3_RX_INT_DISABLE( );
+ #ifdef OUTPUT_INTERRUPT
+ UART3_TX_INT_DISABLE( );
+ #endif
+ }
+#endif
+ uarts[device].state = 0;
+ uarts[device].rx_head = uarts[device].rx_tail = 0;
+ return( 0 );
+}
+
+/*
+ * read from device
+ *
+ * input:
+ * device identifier
+ * byte address of byte to read
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t _uart_read( uint8_t device, uint8_t* byte )
+{
+ uint8_t tail;
+
+#ifdef CHECK_PARAM
+ if( (uarts[device].state & UART_OPEN) == 0 || byte == (uint8_t*)0 )
+ return( -1 );
+#endif
+ tail = uarts[device].rx_tail;
+ if( tail == uarts[device].rx_head )
+ wait_event( EVENT_UART0 + device );
+ if( ++tail == (UART_RX_BUFFER_SIZE + 1) )
+ tail = 0;
+ *byte = uarts[device].rx_buffer[tail];
+ uarts[device].rx_tail = tail;
+ return( 0 );
+}
+
+/*
+ * write to device
+ *
+ * input:
+ * device identifier
+ * byte byte to write
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t _uart_write( uint8_t device, uint8_t byte )
+{
+ if( (uarts[device].state & UART_OPEN) == 0 )
+ return( -1 );
+
+#ifdef OUTPUT_INTERRUPT
+ uarts[device].state |= UART_TX_PROGRESS;
+#endif
+ if( uarts[device].state & ECHOO )
+ uarts[device].state |= UART_ECHOO_CHECK;
+ if( device == 0 )
+ {
+#ifdef OUTPUT_INTERRUPT
+ UART0_TX_BYTE = byte;
+ UART0_TX_INT_ENABLE( );
+#else
+ while( UART0_TX_NOT_READY( ) ) ;
+ UART0_TX_BYTE = byte;
+#endif
+ }
+#if (ARMEN_UARTS >= 2)
+ else if( device == 1 )
+ {
+ #ifdef OUTPUT_INTERRUPT
+ UART1_TX_BYTE = byte;
+ UART1_TX_INT_ENABLE( );
+ #else
+ while( UART1_TX_NOT_READY( ) ) ;
+ UART1_TX_BYTE = byte;
+ #endif
+ }
+#endif
+#if (ARMEN_UARTS >= 3)
+ else if( device == 2 )
+ {
+ #ifdef OUTPUT_INTERRUPT
+ UART2_TX_BYTE = byte;
+ UART2_TX_INT_ENABLE( );
+ #else
+ while( UART2_TX_NOT_READY( ) ) ;
+ UART2_TX_BYTE = byte;
+ #endif
+ }
+#endif
+#if (ARMEN_UARTS == 4)
+ else
+ {
+ #ifdef OUTPUT_INTERRUPT
+ UART3_TX_BYTE = byte;
+ UART3_TX_INT_ENABLE( );
+ #else
+ while( UART3_TX_NOT_READY( ) ) ;
+ UART3_TX_BYTE = byte;
+ #endif
+ }
+#endif
+
+#ifdef OUTPUT_INTERRUPT
+ while( uarts[device].state & UART_TX_PROGRESS ) ;
+#else
+ if( device == 0 )
+ while( UART0_TX_NOT_READY( ) ) ;
+ #if (ARMEN_UARTS >= 2)
+ else if( device == 1 )
+ while( UART1_TX_NOT_READY( ) ) ;
+ #endif
+ #if (ARMEN_UARTS >= 3)
+ else if( device == 2 )
+ while( UART2_TX_NOT_READY( ) ) ;
+ #endif
+ #if (ARMEN_UARTS == 4)
+ else
+ while( UART3_TX_NOT_READY( ) ) ;
+ #endif
+#endif
+ if( uarts[device].state & ECHOO )
+ {
+ while( uarts[device].state & UART_ECHOO_CHECK ) ;
+ if( uarts[device].cd_byte != byte )
+ return( -1 );
+ }
+ return( 0 );
+}
+
+/*
+ * device special feature
+ *
+ * input:
+ * device identifier
+ * request device dependent
+ * args list of arguments
+ * output:
+ * 0 if success, otherwise -1
+*/
+#ifdef UART_IOCTL
+static int8_t _uart_ioctl( uint8_t device, uint8_t request, va_list args )
+{
+ if( (uarts[device].state & UART_OPEN) != 0 )
+ {
+ if( request == UART_GETP )
+ {
+ uint8_t* p = va_arg( args, uint8_t* );
+
+ *p = uarts[device].state & UART_CONFIG_MASK;
+ return( 0 )
+ }
+ }
+ return( -1 );
+}
+#endif
+
diff --git a/avr/usi.c b/avr/usi.c
new file mode 100644
index 0000000..cbc913e
--- /dev/null
+++ b/avr/usi.c
@@ -0,0 +1,170 @@
+/************************************************************************/
+/* */
+/* File: avr/usi.c */
+/* Description: serial line device. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef AVR_UART_C
+ #error "do not use this code directly, it must be included via avr_uart.c"
+#endif
+
+#ifdef USE_PWM
+ #error "pwm and uart, used together, are incompatible"
+#endif
+
+/* TO DO: implement serial line via usi interface */
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+/*
+ * this code is based on the AVR307 publication.
+ * it use the timer 1 (16 bits on ATtiny84, 8 bits on ATtiny85).
+ * the uart is configured with 8 data bits, no parity and 1 stop bit.
+ * the used pins are DI (input) and DO (output).
+*/
+#if defined(__AVR_ATtiny84__)
+
+ #define USI_INPUT_PIN PA6 /* receive */
+ #define USI_OUTPUT_PIN PA5 /* transmit */
+ #define USI_CONFIG_PINS() (\
+ PORTA |= (1<<USI_INPUT_PIN),\
+ DDRA = (DDRA\
+ | (1<<USI_OUTPUT_PIN))\
+ & ~(1<<USI_INPUT_PIN)\
+ )
+ #define USI_INPUT_START_BIT() (PINA & (1<<USI_INPUT_PIN) ? 0 : 1)
+ #define USI_CONFIG_TIMER(p) (\
+ TIMSK1 = 0, TCCR1A = 0,\
+ TCCR1B = (1<<ICNC1) | (p)\
+ )
+ #define USI_CONFIG_DEVICE() ()
+
+ #define USI_INPUT_CAPTURE_INT PCINT6_vect
+
+#elif defined(__AVR_ATtiny85__)
+
+ #define USI_INPUT_PIN PB0 /* receive */
+ #define USI_OUTPUT_PIN PB1 /* transmit */
+ #define USI_CONFIG_PINS() (\
+ PORTB |= (1<<USI_INPUT_PIN),\
+ DDRB = (DDRB\
+ | (1<<USI_OUTPUT_PIN))\
+ & ~(1<<USI_INPUT_PIN)\
+ )
+ #define USI_INPUT_START_BIT() (PINB & (1<<USI_INPUT_PIN) ? 0 : 1)
+ #define USI_CONFIG_TIMER(p) (TIMSK = 0, TCCR1 = (p))
+ #define USI_CONFIG_DEVICE() ()
+
+ #define USI_INPUT_CAPTURE_INT PCINT0_vect
+
+#else
+ #error "no or unavailable cpu defined"
+#endif
+
+#define USI_OVERFLOW_INTERRUPT USI_OVF_vect
+#define TIMER_OVERFLOW_INTERRUPT TIMER1_OVF_vect
+
+#define CONFIG_RS485(p) (CONFIG_RS485_DRIVER(), \
+ CONFIG_TIMER_PRESCALE(p), \
+ CONFIG_USI_DEVICE())
+
+#define USI_RX_BUFFER_SIZE 16
+#define UART_ECHOO_CHECK 0x40
+
+/*
+ * reverse bits order lsb<->msb
+ *
+ * input:
+ * byte byte to reverse
+ * output:
+ * reversed byte
+*/
+static uint8_t reverse_byte( uint8_t byte )
+{
+ byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
+ byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
+ byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
+ return( byte );
+}
+
+/*
+ * initialize device
+ *
+ * input:
+ * device identifier (always 0)
+ * config uart configuration
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t uart_open( uint8_t device, uint8_t config )
+{
+ return( -1 );
+}
+
+/*
+ * release device
+ *
+ * input:
+ * device identifier
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t uart_close( uint8_t device )
+{
+ return( -1 );
+}
+
+/*
+ * read from device
+ *
+ * input:
+ * device identifier (always 0)
+ * byte address of byte to read
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t uart_read( uint8_t device, uint8_t* byte )
+{
+ return( -1 );
+}
+
+/*
+ * write to device
+ *
+ * input:
+ * device identifier (always 0)
+ * byte byte to write
+ * output:
+ * 0 if success, otherwise -1
+*/
+static int8_t uart_write( uint8_t device, uint8_t byte )
+{
+ return( -1 );
+}
+
+/*
+ * device special feature
+ *
+ * input:
+ * device identifier (always 0)
+ * request device dependent
+ * args list of arguments
+ * output:
+ * 0 if success, otherwise -1
+*/
+#ifdef UART_IOCTL
+static int8_t uart_ioctl( uint8_t device, uint8_t request, va_list args )
+{
+ return( -1 );
+}
+#endif
diff --git a/core.c b/core.c
new file mode 100644
index 0000000..6b00e3b
--- /dev/null
+++ b/core.c
@@ -0,0 +1,319 @@
+/************************************************************************/
+/* */
+/* File: core.c */
+/* Description: kernel entry point */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#include "core.h"
+
+static uint32_t _time;
+
+extern void* main( void* );
+
+/*
+ * called from armen_start (see assembly code)
+ *
+ * input:
+ * ram_start beginning of free ram
+ * ram_end end of ram
+ * output:
+*/
+void _sys_init( void* ram_start, void* ram_end )
+{
+ register uint8_t i;
+ register uint16_t stack_per_thread;
+ register void* stack;
+
+ /*
+ * initialize stacks in table of threads
+ */
+ stack_per_thread = (ram_end - ram_start - IDLE_STACK_SIZE) / (ARMEN_THREADS - 1);
+ stack = ram_end;
+ for( i = 1; i < ARMEN_THREADS; i++ )
+ {
+ _threads[i].istack = stack;
+ stack -= stack_per_thread;
+ }
+
+ /*
+ * thread0 = idle
+ */
+ _threads[0].istack = stack;
+ _threads[0].cstack = _init_context( _idle, 0, stack );
+ _threads[0].rank =
+ _threads[0].order = 255;
+ _threads[0].entry = _idle;
+ _threads[0].status = TS_RUNNING;
+
+ /*
+ * thread1 = main
+ */
+ _threads[1].cstack = _threads[1].istack;
+ _threads[1].rank = 127;
+ _threads[1].entry = main;
+ _threads[1].status = TS_RUNNING;
+ _cur_thread = 1;
+
+#if defined(UART) && (UART != 0)
+ _uart_init( );
+#endif
+}
+
+/*
+ * called by timer every millisecond (see assembly code)
+ *
+ * input:
+ * output:
+*/
+void _sys_clock( void )
+{
+ register uint8_t i;
+ register _thread_t* pthread;
+
+ ++_time;
+
+ /*
+ * update priorities
+ */
+ for( i = 1; i < ARMEN_THREADS; i++ )
+ {
+ pthread = &_threads[i];
+
+ if( pthread->status == TS_UNUSED )
+ continue;
+ if( pthread->status == TS_RUNNING )
+ {
+ if( i == _cur_thread )
+ {
+ if( pthread->order != 254 )
+ ++pthread->order;
+ }
+ else
+ {
+ if( pthread->order != 0 )
+ --pthread->order;
+ }
+ }
+
+ /*
+ * update thread timer(s)
+ */
+ else
+ {
+ if( pthread->timer1 != 0 && --pthread->timer1 == 0
+ && (pthread->events & EVENT_TIMER1) != 0
+ )
+ {
+ pthread->events &= ~EVENT_TIMER1;
+ pthread->event |= EVENT_TIMER1;
+ pthread->order = 0;
+ pthread->status = TS_RUNNING;
+ }
+#if (ARMEN_TIMERS == 2)
+ if( pthread->timer2 != 0 && --pthread->timer2 == 0
+ && (pthread->events & EVENT_TIMER2) != 0
+ )
+ {
+ pthread->events &= ~EVENT_TIMER2;
+ pthread->event |= EVENT_TIMER2;
+ pthread->order = 0;
+ pthread->status = TS_RUNNING;
+ }
+#endif
+ }
+ }
+}
+
+/*
+ * thread election
+ *
+ * input:
+ * sp current value of stack pointer
+ * output:
+*/
+void _scheduler( void* sp )
+{
+ register uint8_t i, rank, order, elected;
+ register _thread_t* pthread;
+
+ elected = 0;
+ rank = order = 255;
+ _threads[_cur_thread].cstack = sp;
+ for( i = 1; i < ARMEN_THREADS; i++ )
+ {
+ pthread = &_threads[i];
+
+ if( pthread->status != TS_RUNNING
+ || pthread->order > order
+ || (pthread->order == order && pthread->rank > rank ) )
+ continue;
+ elected = i;
+ rank = pthread->rank;
+ order = pthread->order;
+ }
+
+ if( elected != _cur_thread )
+ {
+ _cur_thread = elected;
+ _restore_context( _threads[elected].cstack );
+ /* no return */
+ }
+}
+
+/*
+ * get current time (milliseconds since start-up)
+ *
+ * input:
+ * output:
+ * count of milliseconds
+*/
+uint32_t get_time( void )
+{
+ return( _time );
+}
+
+/*
+ * suspend execution of the calling thread
+ *
+ * input:
+ * events mask of events
+ * output:
+ * event occured
+*/
+uint16_t wait_event( uint16_t events )
+{
+ uint16_t ret = 0;
+
+ if( events != 0 )
+ {
+ do
+ {
+ _disable_intr( );
+ _threads[_cur_thread].events |= events;
+ _threads[_cur_thread].status = TS_SUSPENDED;
+ _schedule( );
+ }
+ while( (_threads[_cur_thread].event & events) == 0 ) ;
+
+ ret = _threads[_cur_thread].event & events;
+ _threads[_cur_thread].event &= ~events;
+ }
+ return( ret );
+}
+
+/*
+ * current value of timer
+ *
+ * input:
+ * timer timer identifier
+ * output:
+ * timer value if success, otherwise -1
+*/
+timer_t get_timer( uint8_t timer )
+{
+#ifdef CHECK_PARAM
+ if( timer < 1 || timer > ARMEN_TIMERS )
+ return( (timer_t)-1 );
+#endif
+#if (ARMEN_TIMERS == 2)
+ if( timer == 2 )
+ return( _threads[_cur_thread].timer2 );
+#endif
+ return( _threads[_cur_thread].timer1 );
+}
+
+/*
+ * suspend current thread during a period
+ *
+ * input:
+ * timer timer identifier
+ * msecs period in milliseconds
+ * output:
+ * 0 if success, otherwise -1
+*/
+int8_t set_timer( uint8_t timer, timer_t msecs )
+{
+#ifdef CHECK_PARAM
+ if( timer < 1 || timer > ARMEN_TIMERS )
+ return( -1 );
+#endif
+#if (ARMEN_TIMERS == 2)
+ if( timer == 2 )
+ {
+ _threads[_cur_thread].timer2 = msecs;
+ if( msecs != 0 )
+ _threads[_cur_thread].events &= ~EVENT_TIMER2;
+ else
+ _threads[_cur_thread].events |= EVENT_TIMER2;
+ }
+ else
+#endif
+ {
+ _threads[_cur_thread].timer1 = msecs;
+ if( msecs != 0 )
+ _threads[_cur_thread].events &= ~EVENT_TIMER1;
+ else
+ _threads[_cur_thread].events |= EVENT_TIMER1;
+ }
+ return( 0 );
+}
+void sleep( timer_t msecs )
+{
+ if( msecs != 0 )
+ {
+ _threads[_cur_thread].events |= EVENT_TIMER1;
+ _threads[_cur_thread].timer1 = msecs;
+ wait_event( EVENT_TIMER1 );
+ }
+}
+
+/*
+ * wake up thread(s) on event
+ *
+ * input:
+ * event reason for wake up
+ * param depends on event
+ * output:
+*/
+void _wakeup( uint16_t event, void* param )
+{
+ register uint8_t i;
+ register _thread_t* pthread;
+
+ for( i = 1; i < ARMEN_THREADS; i++ )
+ {
+ pthread = &_threads[i];
+
+ if( pthread->status != TS_SUSPENDED
+ || (pthread->events & event) == 0
+#if defined(USE_SEM)
+ || (event == EVENT_SEM && pthread->sem != param)
+#endif
+ || (event == EVENT_JOIN && pthread->joinid != _cur_thread) )
+ continue;
+
+ if( event == EVENT_JOIN )
+ pthread->param = param;
+ pthread->events &= ~event;
+ pthread->event |= event;
+ pthread->order = 0;
+ pthread->status = TS_RUNNING;
+ }
+}
+void send_user_event( void )
+{
+ register uint8_t iflag;
+
+ iflag = _disable_intr( );
+ _wakeup( EVENT_USER, 0 );
+ _enable_intr( iflag );
+}
diff --git a/core.h b/core.h
new file mode 100644
index 0000000..6dd5348
--- /dev/null
+++ b/core.h
@@ -0,0 +1,113 @@
+/************************************************************************/
+/* */
+/* File: core.h */
+/* Description: armen internals */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef _ARMEN_CORE_H_
+#define _ARMEN_CORE_H_
+
+#include "armen.h"
+
+#ifdef __AVR_ARCH__
+ #include "avr/core.h"
+#endif
+
+/*
+ * number of threads
+*/
+#define ARMEN_THREAD_MIN 2
+#define ARMEN_THREAD_MAX 8
+#if defined(THREAD)
+ #if (THREAD < 0) || (THREAD > (ARMEN_THREAD_MAX-ARMEN_THREAD_MIN))
+ #error "invalid number of threads"
+ #endif
+#else
+ #define THREAD 1
+#endif
+#define ARMEN_THREADS (ARMEN_THREAD_MIN+THREAD)
+
+/*
+ * number of timers
+*/
+#define ARMEN_TIMER_MIN 1
+#define ARMEN_TIMER_MAX 2
+#if defined(TIMER)
+ #if (TIMER < ARMEN_TIMER_MIN) || (TIMER > ARMEN_TIMER_MAX)
+ #error "invalid number of timers"
+ #endif
+ #define ARMEN_TIMERS TIMER
+#else
+ #define ARMEN_TIMERS ARMEN_TIMER_MIN
+#endif
+
+typedef struct {
+ uint8_t lock;
+ uint8_t value;
+ uint8_t count;
+ uint8_t pad;
+} _sem_t;
+
+typedef struct {
+ uint8_t status; /* status (see below) */
+ uint8_t rank; /* priority set by user */
+ uint8_t order; /* priority set by kernel */
+ uint8_t joinid; /* thread where waiting for termination */
+ uint16_t events; /* events for whitch thread is suspended */
+ uint16_t event; /* last event */
+ timer_t timer1; /* timer counter 1 (used by sleep function) */
+#if (ARMEN_TIMERS == 2)
+ timer_t timer2; /* timer counter 2 */
+#endif
+ void* (*entry)(void*); /* thread entry point */
+ void* param; /* calling argument or returned value */
+ void* istack; /* initial value of stack pointer */
+ void* cstack; /* current stack pointer */
+#if defined(USE_SEM)
+ void* sem; /* semaphore on thread is waiting */
+#endif
+} _thread_t;
+
+/*
+ * status of a thread
+*/
+#define TS_UNUSED 0
+#define TS_RUNNING 1
+#define TS_SUSPENDED 2
+
+/*
+ * events (see also armen.h)
+*/
+#define EVENT_JOIN 0x4000
+#define EVENT_SEM 0x8000
+
+extern uint8_t volatile _cur_thread; /* id of current thread */
+extern _thread_t _threads[]; /* table of threads */
+
+extern void _thread_exit( void );
+
+extern void* _idle( void* );
+extern void _schedule( void );
+extern uint8_t _disable_intr( void );
+extern void _enable_intr( uint8_t flag );
+extern void* _init_context( void* (*entry)(void*), void* param, void* sp );
+extern void* _save_context( void );
+extern void _restore_context( void* sp );
+
+extern void _sys_init( void* ram_start, void* ram_end );
+extern void _sys_clock( void );
+extern void _scheduler( void* sp );
+extern void _wakeup( uint16_t event, void* param );
+
+extern void _uart_init( void );
+
+#endif
diff --git a/gpio.c b/gpio.c
new file mode 100644
index 0000000..700dced
--- /dev/null
+++ b/gpio.c
@@ -0,0 +1,54 @@
+/************************************************************************/
+/* */
+/* File: gpio.c */
+/* Description: general purpose input/output driver. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#include <stdarg.h>
+#include "armen.h"
+
+#ifdef __AVR_ARCH__
+ #define AVR_GPIO_C
+ #include "avr/gpio.c"
+#endif
+
+/*
+ * initialize a gpio pin
+ *
+ * input:
+ * gpio gpio pin
+ * mode input or output
+*/
+void gpio_pin_mode( uint8_t gpio, uint8_t mode, ... )
+{
+#ifdef CHECK_PARAM
+ if( gpio < (GPIO_MAX_PORT << 3) )
+#endif
+ {
+ if( mode == GPIO_INPUT )
+ gpio_pin_mode_input( gpio );
+ else
+#ifdef CHECK_PARAM
+ if( mode == GPIO_OUTPUT )
+#endif
+ {
+ va_list args;
+ uint8_t value;
+
+ va_start( args, mode );
+ value = (uint8_t)va_arg( args, int );
+ gpio_pin_mode_output( gpio, value );
+ va_end( args );
+ }
+ }
+}
+
diff --git a/mdep.S b/mdep.S
new file mode 100644
index 0000000..ebaedf8
--- /dev/null
+++ b/mdep.S
@@ -0,0 +1,18 @@
+/************************************************************************/
+/* */
+/* File: mdep.S */
+/* Description: machine dependent code */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifdef __AVR_ARCH__
+ #include "avr/core.S"
+#endif
diff --git a/sem.c b/sem.c
new file mode 100644
index 0000000..c34e798
--- /dev/null
+++ b/sem.c
@@ -0,0 +1,78 @@
+/************************************************************************/
+/* */
+/* File: sem.c */
+/* Description: semaphore P and V operations. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifdef USE_SEM
+
+#include "core.h"
+
+/*
+ * initialize a semaphore
+ *
+ * input:
+ * sem address of semaphore
+ * value initial value for semaphore
+ * output:
+*/
+void sem_init( sem_t* sem, uint8_t value )
+{
+ ((_sem_t*)sem)->lock = 0;
+ ((_sem_t*)sem)->value =
+ ((_sem_t*)sem)->count = value;
+}
+
+/*
+ * P operation
+ *
+ * input:
+ * sem address of semaphore
+ * output:
+ * O if success, otherwise -1
+*/
+void sem_wait( sem_t* sem )
+{
+ while( test_and_set( sem ) != 0 ) ;
+ if( ((_sem_t*)sem)->count == 0 )
+ {
+ ((_sem_t*)sem)->lock = 0;
+ _threads[_cur_thread].sem = sem;
+ wait_event( EVENT_SEM );
+ }
+ else
+ {
+ --((_sem_t*)sem)->count;
+ ((_sem_t*)sem)->lock = 0;
+ }
+}
+
+/*
+ * V operation
+ *
+ * input:
+ * sem address of semaphore
+ * output:
+ * O if success, otherwise -1
+*/
+void sem_post( sem_t* sem )
+{
+ while( test_and_set( sem ) != 0 ) ;
+ if( ((_sem_t*)sem)->count != ((_sem_t*)sem)->value )
+ {
+ ++((_sem_t*)sem)->count;
+ _wakeup( EVENT_SEM, sem );
+ }
+ ((_sem_t*)sem)->lock = 0;
+}
+
+#endif
diff --git a/thread.c b/thread.c
new file mode 100644
index 0000000..46e0d42
--- /dev/null
+++ b/thread.c
@@ -0,0 +1,158 @@
+/************************************************************************/
+/* */
+/* File: thread.c */
+/* Description: thread implementation. */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#include "core.h"
+
+uint8_t volatile _cur_thread;
+_thread_t _threads[ARMEN_THREADS];
+
+/*
+ * identifier of calling thread
+ *
+ * input:
+ * output:
+ * thread identifier
+*/
+thread_t thread_self( void )
+{
+ return( _cur_thread );
+}
+
+/*
+ * create a thread
+ *
+ * input:
+ * start start routine of new thread
+ * arg argument of start routine
+ * output:
+ * thread identifier if success, otherwise -1
+*/
+thread_t thread_create( void* (*start)(void*), void* arg )
+{
+ register uint8_t i, iflag;
+
+ iflag = _disable_intr( );
+ for( i = 1; i < ARMEN_THREADS; i++ )
+ {
+ register _thread_t* pthread = &_threads[i];
+
+ if( pthread->status == TS_UNUSED )
+ {
+ pthread->rank = 127;
+ pthread->order = 0;
+ pthread->entry = start;
+ pthread->param = arg;
+ pthread->cstack = _init_context( start, arg, pthread->istack );
+ pthread->status = TS_RUNNING;
+ break;
+ }
+ }
+ _enable_intr( iflag );
+ return( i == ARMEN_THREADS ? -1 : i );
+}
+
+/*
+ * terminate the calling thread
+ *
+ * input:
+ * thread thread identifier
+ * retval value will be returned (see thread_join)
+ * output:
+*/
+static void _thread_exit_( thread_t thread, void* retval )
+{
+ _disable_intr( );
+ _threads[thread].status = TS_UNUSED;
+ _wakeup( EVENT_JOIN, retval );
+ _schedule( );
+}
+void _thread_exit( void )
+{
+ _thread_exit_( _cur_thread, 0 );
+}
+void thread_exit( void* retval )
+{
+ _thread_exit_( _cur_thread, retval );
+}
+
+#ifdef USE_FULL_THREAD
+
+ /*
+ * send a cancellation request to a thread
+ *
+ * input:
+ * thread thread identifier
+ * output:
+ */
+ void thread_cancel( thread_t thread )
+ {
+ if(
+ #ifdef CHECK_PARAM
+ thread > 0 && thread < ARMEN_THREADS &&
+ #endif
+ thread != _cur_thread
+ && _threads[thread].status != TS_UNUSED )
+ _thread_exit_( thread, (void*)-1 );
+ }
+
+ /*
+ * wait for termination of a thread
+ *
+ * input:
+ * thread thread identifier
+ * retval address of exit value (see thread_exit)
+ * output:
+ */
+ void thread_join( thread_t thread, void** retval )
+ {
+ if(
+ #ifdef CHECK_PARAM
+ thread > 0 && thread < ARMEN_THREADS &&
+ #endif
+ thread != _cur_thread
+ && _threads[thread].status != TS_UNUSED )
+ {
+ wait_event( EVENT_JOIN );
+ _schedule( );
+ if( retval != (void**)0)
+ *retval = _threads[_cur_thread].param;
+ }
+ }
+
+ /*
+ * change the priority of a thread
+ *
+ * input:
+ * thread thread identifier
+ * priority new priority (1 hightest to 254 lowest)
+ * if 0, just return the current priority
+ * output:
+ * priority if success, otherwise -1
+ */
+ uint8_t thread_nice( thread_t thread, uint8_t priority )
+ {
+ if(
+ #ifdef CHECK_PARAM
+ thread <= 0 || thread >= ARMEN_THREADS ||
+ #endif
+ _threads[thread].status == TS_UNUSED )
+ return( 255 );
+
+ if( priority != 0 )
+ _threads[thread].rank = priority;
+ return( _threads[thread].rank );
+ }
+
+#endif
diff --git a/uart.c b/uart.c
new file mode 100644
index 0000000..f758cfb
--- /dev/null
+++ b/uart.c
@@ -0,0 +1,161 @@
+/************************************************************************/
+/* */
+/* File: uart.c */
+/* Description: simple wrapper to serial line device(s). */
+/* Version: 1.0 */
+/* Author: Otto Mattik */
+/* */
+/* (C)Copyright Otto Mattik 2014-2021. */
+/* */
+/* This file is a part of 'armen' (a tiny operating system). */
+/* 'armen' is distributed under the CeCILL-V2.1 licence. For more */
+/* details about this licence, please visit the website cecill.info */
+/* */
+/************************************************************************/
+
+#ifndef F_CPU
+ #error "cpu clock frequency is not defined"
+#endif
+
+#include <stdint.h>
+#include <stdarg.h>
+#include "core.h"
+
+#ifdef __AVR_ARCH__
+ #include "avr/uart.h"
+#endif
+
+#if defined(UART) && (UART > 0) && (ARMEN_UART_MAX != 0)
+
+ #if (UART > ARMEN_UART_MAX)
+ #undef UART
+ #define UART ARMEN_UART_MAX
+ #endif
+ #define ARMEN_UARTS UART
+
+ #ifndef UART_RX_BUFFER_SIZE
+ #define UART_RX_BUFFER_SIZE 4
+ #endif
+
+ static struct {
+ int8_t (*open)(uint8_t,uint8_t);
+ int8_t (*close)(uint8_t);
+ int8_t (*read)(uint8_t,uint8_t*);
+ int8_t (*write)(uint8_t,uint8_t);
+ #ifdef UART_IOCTL
+ int8_t (*ioctl)(uint8_t,uint8_t,va_list);
+ #endif
+ } devices[ARMEN_UARTS];
+
+ #ifdef __AVR_ARCH__
+ #define AVR_UART_C
+ #include "avr/uart.c"
+ #endif
+
+ int8_t uart_open( uint8_t device, uint8_t config )
+ {
+ #ifdef CHECK_PARAM
+ if( device >= ARMEN_UARTS )
+ return( -1 );
+ #endif
+ return( devices[device].open( device, config ) );
+ }
+
+ int8_t uart_close( uint8_t device )
+ {
+ #ifdef CHECK_PARAM
+ if( device >= ARMEN_UARTS )
+ return( -1 );
+ #endif
+ return( devices[device].close( device ) );
+ }
+
+ int8_t uart_read( uint8_t device, uint8_t* byte )
+ {
+ #ifdef CHECK_PARAM
+ if( device >= ARMEN_UARTS )
+ return( -1 );
+ #endif
+ return( devices[device].read( device, byte ) );
+ }
+
+ int8_t uart_write( uint8_t device, uint8_t byte )
+ {
+ #ifdef CHECK_PARAM
+ if( device >= ARMEN_UARTS )
+ return( -1 );
+ #endif
+ return( devices[device].write( device, byte ) );
+ }
+
+ #ifdef UART_IOCTL
+ int8_t uart_ioctl( uint8_t device, uint8_t request, ... )
+ {
+ int ret;
+ va_list args;
+
+ #ifdef CHECK_PARAM
+ if( device >= ARMEN_UARTS )
+ return( -1 );
+ #endif
+ va_start( args, request );
+ ret = devices[device].ioctl( device, request, args );
+ va_end( args );
+ return( ret );
+ }
+ #endif
+
+ char uart_getchar( uint8_t device )
+ {
+ char c;
+
+ #ifdef CHECK_PARAM
+ if( device >= ARMEN_UARTS )
+ return( -1 );
+ #endif
+
+ if( devices[device].read( device, &c ) < 0 )
+ c = -1;
+ return( (char)c );
+ }
+
+ void uart_putchar( uint8_t device, char c )
+ {
+ #ifdef CHECK_PARAM
+ if( device < ARMEN_UARTS )
+ #endif
+ devices[device].write( device, c );
+ }
+
+ void uart_putstr( uint8_t device, char* s )
+ {
+ #ifdef CHECK_PARAM
+ if( device < ARMEN_UARTS )
+ #endif
+ while( *s != 0 )
+ devices[device].write( device, *s++ );
+ }
+
+ #ifdef ARDUINO
+ #include <stddef.h>
+
+ extern int vsnprintf(char *__s, size_t __n, const char *__fmt, va_list ap);
+
+ void uart_printf( uint8_t device, char* fmt, ... )
+ {
+ va_list args;
+ static char buffer[80];
+
+ #ifdef CHECK_PARAM
+ if( device < ARMEN_UARTS )
+ #endif
+ {
+ va_start( args, fmt );
+ vsnprintf( buffer, sizeof( buffer ), fmt, args );
+ va_end( args );
+ uart_putstr( device, buffer );
+ }
+ }
+ #endif
+
+#endif