/* * Funcgen firmware * Signal timer * * Copyright (C) 2007-2009 Michael Buesch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "timer.h" #include "util.h" #include "main.h" #include #include extern const uint8_t PROGMEM table_sine[]; extern const uint8_t PROGMEM table_triang[]; /* Lookup table for the active waveform (if used). * The array is _guaranteed_ to be at SRAM address 0x100. * This allows some optimizations in the lowlevel generator. */ static uint8_t active_generator_table[256] __attribute__((section(".generatortab"))); /* We abuse some I/O registers for some core generator state variables, * because an in/out is faster than an lds/sts. * So doing I/O to an MMIO location is twice as fast as accessing SRAM. * Of course, we need to select the I/O registers _carefully_ so the sideeffects * don't bite us. */ /* The function pointer to the active generator. */ #define R_GENERATOR_FUNC_PTR_LO UBRRL #define R_GENERATOR_FUNC_PTR_HI TWBR /* The generator statemachine counter. */ #define R_GENERATOR_STATE TWAR /* The generator statemachine increment step. */ #define R_GENERATOR_STEP OSCCAL /* Storage for the saved SREG in the ISR. */ #define R_SAVED_SREG MCUCR ISR(TIMER1_COMPA_vect) __naked; ISR(TIMER1_COMPA_vect) { __asm__ __volatile__( " ; Generator interrupt handler \n" " push r30 \n" " push r31 \n" " in r30, __SREG__ \n" " out %[_SAVED_SREG], r30 \n" " in r30, %[_GEN_FUNC_LO] \n" " in r31, %[_GEN_FUNC_HI] \n" " ijmp ; Jump to the real handler \n" : /* none */ : [_GEN_FUNC_LO] "M" (_SFR_IO_ADDR(R_GENERATOR_FUNC_PTR_LO)) , [_GEN_FUNC_HI] "M" (_SFR_IO_ADDR(R_GENERATOR_FUNC_PTR_HI)) , [_SAVED_SREG] "M" (_SFR_IO_ADDR(R_SAVED_SREG)) ); } #define GENERATOR_EPILOGUE \ " ; Generator epilogue (start) \n"\ " in r30, %[_SAVED_SREG] \n"\ " out __SREG__, r30 \n"\ " pop r31 \n"\ " pop r30 \n"\ " reti \n"\ " ; Generator epilogue (end) \n" /* Generic table based signal generator */ static void __naked __used generator_generic_table(void) { /* The signal table is at SRAM address 0x100, so we can efficiently * use the generator_state as index into the table. */ __asm__ __volatile__( " in r31, %[_GEN_STEP] \n" " in r30, %[_GEN_STATE] \n" " add r30, r31 \n" " out %[_GEN_STATE], r30 \n" " ldi r31, 0x01 \n" " ld r31, Z \n" " out %[_DAC_PORT], r31 \n" GENERATOR_EPILOGUE : /* none */ : [_DAC_PORT] "M" (_SFR_IO_ADDR(DAC_PORT)) , [_GEN_STATE] "M" (_SFR_IO_ADDR(R_GENERATOR_STATE)) , [_GEN_STEP] "M" (_SFR_IO_ADDR(R_GENERATOR_STEP)) , [_SAVED_SREG] "M" (_SFR_IO_ADDR(R_SAVED_SREG)) : "memory" ); } /* Square signal generator */ static void __naked __used generator_square(void) { __asm__ __volatile__( " in r30, %[_GEN_STATE] \n" " in r31, %[_GEN_STEP] \n" " add r30, r31 \n" " out %[_GEN_STATE], r30 \n" " clr r31 \n" " cpi r30, 0x7F \n" " brlo low \n" " com r31 /* high */ \n" " low: \n" " out %[_DAC_PORT], r31 \n" GENERATOR_EPILOGUE : /* none */ : [_DAC_PORT] "M" (_SFR_IO_ADDR(DAC_PORT)) , [_GEN_STATE] "M" (_SFR_IO_ADDR(R_GENERATOR_STATE)) , [_GEN_STEP] "M" (_SFR_IO_ADDR(R_GENERATOR_STEP)) , [_SAVED_SREG] "M" (_SFR_IO_ADDR(R_SAVED_SREG)) : "memory" ); } /* Sawtooth signal generator */ static void __naked __used generator_sawt(void) { __asm__ __volatile__( " in r30, %[_GEN_STATE] \n" " in r31, %[_GEN_STEP] \n" " add r30, r31 \n" " out %[_GEN_STATE], r30 \n" " out %[_DAC_PORT], r30 \n" GENERATOR_EPILOGUE : /* none */ : [_DAC_PORT] "M" (_SFR_IO_ADDR(DAC_PORT)) , [_GEN_STATE] "M" (_SFR_IO_ADDR(R_GENERATOR_STATE)) , [_GEN_STEP] "M" (_SFR_IO_ADDR(R_GENERATOR_STEP)) , [_SAVED_SREG] "M" (_SFR_IO_ADDR(R_SAVED_SREG)) : "memory" ); } /* Update the hardware prescaler, based on the software selection. */ static void prescaler_update(uint16_t prescaler) { uint16_t cr; cr = TCCR1B & ~((1 << CS10) | (1 << CS11) | (1 << CS12)); switch (prescaler) { case PRESCALER_1: cr |= (1 << CS10) | (0 << CS11) | (0 << CS12); break; case PRESCALER_8: cr |= (0 << CS10) | (1 << CS11) | (0 << CS12); break; case PRESCALER_64: cr |= (1 << CS10) | (1 << CS11) | (0 << CS12); break; case PRESCALER_256: cr |= (0 << CS10) | (0 << CS11) | (1 << CS12); break; case PRESCALER_1024: cr |= (1 << CS10) | (0 << CS11) | (1 << CS12); break; default: BUG_ON(1); } TCCR1B = cr; } static inline void set_generator_function(void (*func)(void)) { R_GENERATOR_FUNC_PTR_LO = (uint16_t)func; R_GENERATOR_FUNC_PTR_HI = ((uint16_t)func) >> 8; } void generator_reconfigure(uint8_t waveform, uint16_t ocr, uint16_t prescaler, uint8_t step) { uint8_t sreg; sreg = irq_disable_save(); switch (waveform) { case WAV_SINE: memcpy_P(active_generator_table, table_sine, sizeof(active_generator_table)); set_generator_function(generator_generic_table); break; case WAV_SQUARE: set_generator_function(generator_square); break; case WAV_TRIANG: memcpy_P(active_generator_table, table_triang, sizeof(active_generator_table)); set_generator_function(generator_generic_table); break; case WAV_SAWT: set_generator_function(generator_sawt); break; default: BUG_ON(1); } R_GENERATOR_STEP = step; R_GENERATOR_STATE = 0; prescaler_update(prescaler); OCR1A = ocr; irq_restore(sreg); }