ATtiny13 – disco lights using DFT

This is my another “disco lights” project which is an upgraded version of ATtiny13 – dance lights with DFT. This time I used a full implementation of optimized DFT algorithm with reduced memory access to compute a power spectrum of audio signal. This version has also 3-channel lights but visual effects are much-much better!

Required Parts

Circuit Diagram

Software

This code is written in C and can be compiled using the avr-gcc. All information about how to compile this project is here.

/**
 * Copyright (c) 2019, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com>
 * ATtiny13/026
 * Disco lights using DFT (Discrete Fourier Transformation)
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#define LED_LOW              PB0
#define LED_MID              PB1
#define LED_HIGH             PB2

#define LOW_THRESHOLD        (48000UL)
#define MID_THRESHOLD        (128)
#define HIGH_THRESHOLD       (64)

#define N                    (7) // N-points (DFT)
#define B                    (3 * N / 4) // b-value (DFT)

const int8_t W[N] = {10, 6, -2, -9, -9, -2, 6}; // twiddle factors (DFT)
int8_t samples[N]; // raw samples (ADC)
uint16_t power[N>>1]; // power spectrum (DFT)
volatile uint8_t counter = 0;

static void dft(void);

int
main(void)
{

	/* setup */
	DDRB |= _BV(LED_LOW)|_BV(LED_MID)|_BV(LED_HIGH); // set LED pins as OUTPUT
	ADCSRA |= _BV(ADPS2)|_BV(ADPS0); // set ADC division factor to 256
	ADCSRA |= _BV(ADEN)|_BV(ADIE); // enable ADC interrupt
	ADMUX = _BV(MUX1); // set ADC4 (PB4) as audio input
	ADMUX |= _BV(ADLAR); // left adjust of ADC result
	sei(); // enable global interrupts

	ADCSRA |= _BV(ADSC); // start first signal acquisition

	/* loop */
	while (1) {
		if (counter == N) {
			dft(); // do some DSP

			/* LOW freqency band */
			if (power[0] > LOW_THRESHOLD) {
				PORTB |= _BV(LED_LOW);
			} else {
				PORTB &= ~_BV(LED_LOW);
			}

			/* MID freqency band */
			if (power[1] > MID_THRESHOLD) {
				PORTB |= _BV(LED_MID);
                        } else {
                                PORTB &= ~_BV(LED_MID);
                        }

			/* HIGH frequency band */
			if (power[2] > HIGH_THRESHOLD) {
                                PORTB |= _BV(LED_HIGH);
                        } else {
                                PORTB &= ~_BV(LED_HIGH);
                        }

			counter = 0; // reset samples counter
			ADCSRA |= _BV(ADSC); // trigger next signal acqusistion
		}
	}
}

ISR(ADC_vect)
{

	if (counter < N) {
                samples[counter++] = ADCH - 128; // read raw sample <-128; 127>
		ADCSRA |= _BV(ADSC); // trigger next signal acquisition
        }
}

/**
 * Twiddle-factor-based DFT algorithm with reduced memory access.
 */
void
dft(void)
{
	uint8_t a, b, i, j;
	int16_t re[N];
	int16_t im[N];

	for (i = 0; i < N; ++i) {
		re[i] = 0;
		im[i] = 0;
	}

        for (i = 0; i < (N>>1); ++i) {
                a = 0;
		b = B;
                for (j = 0; j < N; ++j) {
                        re[i] += W[a%N] * samples[j];
			im[i] -= W[b%N] * samples[j];
                        a += i;
			b += i;
                }
		power[i] = (re[i] * re[i] + im[i] * im[i]) >> 4;
        }
}

 

6 thoughts on “ATtiny13 – disco lights using DFT

  1. Hi, great little project!
    it’s working for me. I did the upload using Arduino IDE.
    I am using an arduino nano to program attiny with Arduino IDE software.

    Board: DiY ATtiny13 (in my case, does work only by choosing this board; otherwise, there is an DFP error)
    Programmer: Arduino as ISP

    If you have no 100k trim resistor, just use something below 100k. I am using 68k.

    thanks a lot!

  2. Yes, should be fine. I’ve edited this page few days ago and I forgot to change function definition. Sorry for that. Now is up to date.

  3. I use MkCLIPSE
    logs:
    main.c:45:13: warning: implicit declaration of function ‘dft’ [-Wimplicit-function-declaration]
    dft(); // do some DSP
    ^
    main.c: At top level:
    main.c:87:1: warning: conflicting types for ‘dft’
    dft(void)
    ^
    main.c:45:13: note: previous implicit declaration of ‘dft’ was here
    dft(); // do some DSP
    ^
    main.c:26:13: warning: ‘fft’ declared ‘static’ but never defined [-Wunused-function]
    static void fft(void);

    I fixed the code, but I’m not sure it’s okay (now I can compile)

    #include
    #include

    #define LED_LOW PB0
    #define LED_MID PB1
    #define LED_HIGH PB2

    #define LOW_THRESHOLD (48000UL)
    #define MID_THRESHOLD (128)
    #define HIGH_THRESHOLD (64)

    #define N (7) // N-points (FFT)
    #define B (3 * N / 4) // b-value (FFT)

    const int8_t W[N] = {10, 6, -2, -9, -9, -2, 6}; // twiddle factors (FFT)
    int8_t samples[N]; // raw samples (ADC)
    uint16_t power[N>>1]; // power spectrum (FFT)
    volatile uint8_t counter = 0;

    ISR(ADC_vect)
    {

    if (counter < N) {
    samples[counter++] = ADCH – 128; // read raw sample
    ADCSRA |= _BV(ADSC); // trigger next signal acquisition
    }
    }

    static void
    dft(void)
    {
    uint8_t a, b, i, j;
    int16_t re[N];
    int16_t im[N];

    for (i = 0; i < N; ++i) {
    re[i] = 0;
    im[i] = 0;
    }

    for (i = 0; i >1); ++i) {
    a = 0;
    b = B;
    for (j = 0; j > 4;
    }
    }

    int
    main(void)
    {

    /* setup */
    DDRB |= _BV(LED_LOW)|_BV(LED_MID)|_BV(LED_HIGH); // set LED pins as OUTPUT
    ADCSRA |= _BV(ADPS2)|_BV(ADPS0); // set ADC division factor to 256
    ADCSRA |= _BV(ADEN)|_BV(ADIE); // enable ADC interrupt
    ADMUX = _BV(MUX1); // set ADC4 (PB4) as audio input
    ADMUX |= _BV(ADLAR); // left adjust of ADC result
    sei(); // enable global interrupts

    ADCSRA |= _BV(ADSC); // start first signal acquisition

    /* loop */
    while (1) {
    if (counter == N) {
    dft(); // do some DSP

    /* LOW freqency band */
    if (power[0] > LOW_THRESHOLD) {
    PORTB |= _BV(LED_LOW);
    } else {
    PORTB &= ~_BV(LED_LOW);
    }

    /* MID freqency band */
    if (power[1] > MID_THRESHOLD) {
    PORTB |= _BV(LED_MID);
    } else {
    PORTB &= ~_BV(LED_MID);
    }

    /* HIGH frequency band */
    if (power[2] > HIGH_THRESHOLD) {
    PORTB |= _BV(LED_HIGH);
    } else {
    PORTB &= ~_BV(LED_HIGH);
    }

    counter = 0; // reset samples counter
    ADCSRA |= _BV(ADSC); // trigger next signal acqusistion
    }
    }
    }

  4. Hi
    I have a code problem, my compiler shows a bug in “dft(); // do some DSP ”
    How can I solve this?

    • Hi Jacek,
      could provide some logs describing this error?

      Btw. in a december edition an EP (Elektronika Praktyczna) will publish my article about optimized DFT for microcontrollers. If it’s something you would like to dive deeper then I recommend to read it.
      /L

Leave a Comment