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;
        }
}

 

Attachments

    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