diff options
Diffstat (limited to 'avr')
| -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 |
11 files changed, 2085 insertions, 0 deletions
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 |
