diff options
-rw-r--r-- | armen.c | 25 | ||||
-rw-r--r-- | armen.h | 197 | ||||
-rw-r--r-- | armen.mak | 125 | ||||
-rw-r--r-- | avr/adc.c | 188 | ||||
-rw-r--r-- | avr/core.S | 397 | ||||
-rw-r--r-- | avr/core.h | 29 | ||||
-rw-r--r-- | avr/gpio.c | 162 | ||||
-rw-r--r-- | avr/pcint.c | 247 | ||||
-rw-r--r-- | avr/pwm.c | 223 | ||||
-rw-r--r-- | avr/tools.mak | 25 | ||||
-rw-r--r-- | avr/uart.c | 42 | ||||
-rw-r--r-- | avr/uart.h | 33 | ||||
-rw-r--r-- | avr/usart.c | 569 | ||||
-rw-r--r-- | avr/usi.c | 170 | ||||
-rw-r--r-- | core.c | 319 | ||||
-rw-r--r-- | core.h | 113 | ||||
-rw-r--r-- | gpio.c | 54 | ||||
-rw-r--r-- | mdep.S | 18 | ||||
-rw-r--r-- | sem.c | 78 | ||||
-rw-r--r-- | thread.c | 158 | ||||
-rw-r--r-- | uart.c | 161 |
21 files changed, 3333 insertions, 0 deletions
@@ -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 + @@ -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 @@ -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 ); +} @@ -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 @@ -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 ); + } + } +} + @@ -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 @@ -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 @@ -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 |