/* Tiny Function Generator - see http://www.technoblogy.com/show?20W6 David Johnson-Davies - www.technoblogy.com - 18th February 2018 ATtiny85 @ 8 MHz (internal oscillator; BOD disabled) CC BY 4.0 Licensed under a Creative Commons Attribution 4.0 International license: http://creativecommons.org/licenses/by/4.0/ */ #include // Don't initialise these on reset int Wave __attribute__ ((section (".noinit"))); unsigned int Freq __attribute__ ((section (".noinit"))); typedef void (*wavefun_t)(); // Direct Digital Synthesis ********************************************** volatile unsigned int Acc, Jump; void SetupDDS () { // Enable 64 MHz PLL and use as source for Timer1 PLLCSR = 1<> 8; } void Square () { Acc = Acc + Jump; int8_t temp = Acc>>8; OCR1A = temp>>7; } void Rectangle () { Acc = Acc + Jump; int8_t temp = Acc>>8; temp = temp & temp<<1; OCR1A = temp>>7; } void Triangle () { int8_t temp, mask; Acc = Acc + Jump; temp = Acc>>8; mask = temp>>7; temp = temp ^ mask; OCR1A = temp<<1; } void Chainsaw () { int8_t temp, mask, top; Acc = Acc + Jump; temp = Acc>>8; mask = temp>>7; top = temp & 0x80; temp = (temp ^ mask) | top; OCR1A = temp; } void Pulse () { Acc = Acc + Jump; int8_t temp = Acc>>8; temp = temp & temp<<1 & temp<<2 & temp<<3; OCR1A = temp>>7; } void Noise () { int8_t temp = Acc & 1; Acc = Acc >> 1; if (temp == 0) Acc = Acc ^ 0xB400; OCR1A = Acc; } const int nWaves = 7; wavefun_t Waves[nWaves] = {Triangle, Sawtooth, Square, Rectangle, Pulse, Chainsaw, Noise}; wavefun_t Wavefun; ISR(TIMER0_COMPA_vect) { Wavefun(); } // OLED I2C 128 x 32 monochrome display ********************************************** const int OLEDAddress = 0x3C; // Initialisation sequence for OLED module int const InitLen = 24; const unsigned char Init[InitLen] PROGMEM = { 0xAE, // Display off 0xD5, // Set display clock 0x80, // Recommended value 0xA8, // Set multiplex 0x1F, 0xD3, // Set display offset 0x00, 0x40, // Zero start line 0x8D, // Charge pump 0x14, 0x20, // Memory mode 0x01, // Vertical addressing 0xA1, // 0xA0/0xA1 flip horizontally 0xC8, // 0xC0/0xC8 flip vertically 0xDA, // Set comp ins 0x02, 0x81, // Set contrast 0x7F, // 0x00 to 0xFF 0xD9, // Set pre charge 0xF1, 0xDB, // Set vcom detect 0x40, 0xA6, // Normal (0xA7=Inverse) 0xAF // Display on }; const int data = 0x40; const int single = 0x80; const int command = 0x00; void InitDisplay () { Wire.beginTransmission(OLEDAddress); Wire.write(command); for (uint8_t c=0; c>8); } } } Wire.endTransmission(); } uint8_t DigitChar (unsigned int number, unsigned int divisor) { return (number/divisor) % 10; } // Display waveform icon void PlotIcon (int wave, int line, int column) { PlotChar(Icons+2*wave, line, column); column = column + Scale; PlotChar(Icons+2*wave+1, line, column); } // Display a 5-digit frequency starting at line, column void PlotFreq (unsigned int freq, int line, int column) { boolean dig = false; for (unsigned int d=10000; d>0; d=d/10) { char c = DigitChar(freq, d); if (c == 0 && !dig) c = Space; else dig = true; PlotChar(c, line, column); column = column + Scale; } PlotChar(Hz, line, column); column = column + Scale; PlotChar(Hz+1, line, column); } // Rotary encoder ********************************************** const int EncoderA = 3; const int EncoderB = 4; const int MinFreq = 1; // Hz const int MaxFreq = 5000; // Hz volatile int a0; volatile int c0; volatile int Count = 0; void SetupRotaryEncoder () { pinMode(EncoderA, INPUT_PULLUP); pinMode(EncoderB, INPUT_PULLUP); PCMSK = 1<= 1000) step = 100; else if (Freq >=100) step = 10; Freq = max(min((Freq + (Up ? step : -step)), MaxFreq), MinFreq); PlotFreq(Freq, 1, 7); Jump = Freq*4; } // Pin change interrupt service routine ISR (PCINT0_vect) { int a = PINB>>EncoderA & 1; int b = PINB>>EncoderB & 1; if (a != a0) { // A changed a0 = a; if (b != c0) { c0 = b; ChangeValue(a == b); } } } // Setup ********************************************** void setup() { Wire.begin(); // Is it a power-on reset? if (MCUSR & 1) { Wave = 0; Freq = 100; // Start with 100Hz Triangle InitDisplay(); ClearDisplay(); } else Wave = (Wave+1) % nWaves; Wavefun = Waves[Wave]; MCUSR = 0; SetupDDS(); SetupRotaryEncoder(); Jump = Freq*4; PlotFreq(Freq, 1, 7); PlotIcon(Wave, 1, 0); } // Everything done by interrupts void loop() { }