-
1Step 1
A Simple Spectrum Analyser
What you will need
- A multimeter (better with a logic probe)
- A single generator (1 kHz square wave generator)
- A soldering station and accessories (solder, desoldering wick etc.)
- A Digispark microprocessor board and USB connection cable)
- An Arduino Development Environment (with the Digispark driveres and libraries installed).
- A Nokia LCD display
- 4x 10k resistors.
- 2x 0.1uF decoupling capacitors
- 2x 1n4148 diodes
- A strip of pin headers
- A length of hookup wire (0.6 mm tinned solid copper wire).
- A two pin shunt (it shorts out two pins)
- A piece of strip-board (~45mm x 90mm)
-
2Step 2
Preparing the Digispark and Arduino IDE
The Digispark is not the easiest Arduino development board to use. There is a custom version of the Arduino IDE you can use but it is old (version 1.06!). Better to bite the bullet and use the latest Arduino IDE version and install the drivers and libraries. Search for Digispark drivers or just go to: https://digistump.com/wiki/digispark/tutorials/connecting. Not everything on this site is current so you experience may differ. You can get the Win10 drivers from: https://github.com/digistump/DigistumpArduino/releases/download/1.6.7/Digistump.Drivers.zip.
You may need to disable windows driver signage to install the drivers (I have driver signage off by default so I am not sure if the drivers are signed or not).
Windows may not recognise your Digispark! If so try using a USB 2 port (rather than a USB 3 port) or routing through a USB hub (best option).
Also note that the Digispark is only connected to your PC when uploading a file and when asked to by the uploading software. It is not like an ARduino UNO or Nano.
Once you have set up the software test which Digispark version you have (Model A or Model B).
Be patient it does work!
Problems with Cheap Digispark Clones
The cheap Digispark clones usually don't have PB5 or A0/D5 (the reset pin) turned on. You can test this by tring to use PB5 (D5) as an output (try toggling an LED).
Although last version of the Spectrum Analyser published has this fault, it would be much more useful if the "fuses" are set to activate PB5.
To do so download XLoader (http://xloader.russemotto.com/). Add the following batch file to the directory:
Echo Read the signature should be: 0x1e930b Echo avrdude -P com5 -b 19200 -c avrisp -p attiny85 -n pause Echo Echo Ready to write fuses? Kill window if not! pause avrdude -P com5 -b 19200 -p attiny85 -c avrisp -U hfuse:w:0x5F:m pause
Notes:
- "com5" will need to be edited for you PC/Digispark port!
- When you run the batch file, check that the reported signatures match before proceeding!
-
3Step 3
Schematic
Here is my strip-layout for the version that I built:
Notes:
- The three inline black circles (pins) above the resistors are for the two pin shunt. Short the upper pins and the LCD lights up. Short the lower pins and the LCD does not light.
- The project can use power from the PC via the USB port and/or power from the Vin or 5v pins.
- If you set the PB5 fuses you can use D5/A0.
- I have folded the ground pin of the Digispark in order to fit it to the strip-board.
- Finally the Digispark is elevated about 0.1" so that the USB plug will fit between the strip-board and the Digispark USB plug!
-
4Step 4
Software
The code can be downloaded from the project downloads page. But here it is:
#include <avr/pgmspace.h> const static char ASCII[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, // 20 0x00, 0x00, 0x5f, 0x00, 0x00, // 21 ! 0x00, 0x07, 0x00, 0x07, 0x00, // 22 " 0x14, 0x7f, 0x14, 0x7f, 0x14, // 23 # 0x24, 0x2a, 0x7f, 0x2a, 0x12, // 24 $ 0x23, 0x13, 0x08, 0x64, 0x62, // 25 % 0x36, 0x49, 0x55, 0x22, 0x50, // 26 & 0x00, 0x05, 0x03, 0x00, 0x00, // 27 ' 0x00, 0x1c, 0x22, 0x41, 0x00, // 28 ( 0x00, 0x41, 0x22, 0x1c, 0x00, // 29 ) 0x14, 0x08, 0x3e, 0x08, 0x14, // 2a * 0x08, 0x08, 0x3e, 0x08, 0x08, // 2b + 0x00, 0x50, 0x30, 0x00, 0x00, // 2c , 0x08, 0x08, 0x08, 0x08, 0x08, // 2d - 0x00, 0x60, 0x60, 0x00, 0x00, // 2e . 0x20, 0x10, 0x08, 0x04, 0x02, // 2f / 0x3e, 0x51, 0x49, 0x45, 0x3e, // 30 0 0x00, 0x42, 0x7f, 0x40, 0x00, // 31 1 0x42, 0x61, 0x51, 0x49, 0x46, // 32 2 0x21, 0x41, 0x45, 0x4b, 0x31, // 33 3 0x18, 0x14, 0x12, 0x7f, 0x10, // 34 4 0x27, 0x45, 0x45, 0x45, 0x39, // 35 5 0x3c, 0x4a, 0x49, 0x49, 0x30, // 36 6 0x01, 0x71, 0x09, 0x05, 0x03, // 37 7 0x36, 0x49, 0x49, 0x49, 0x36, // 38 8 0x06, 0x49, 0x49, 0x29, 0x1e, // 39 9 0x00, 0x36, 0x36, 0x00, 0x00, // 3a : 0x00, 0x56, 0x36, 0x00, 0x00, // 3b ; 0x08, 0x14, 0x22, 0x41, 0x00, // 3c < 0x14, 0x14, 0x14, 0x14, 0x14, // 3d = 0x00, 0x41, 0x22, 0x14, 0x08, // 3e > 0x02, 0x01, 0x51, 0x09, 0x06, // 3f ? 0x32, 0x49, 0x79, 0x41, 0x3e, // 40 @ 0x7e, 0x11, 0x11, 0x11, 0x7e, // 41 A 0x7f, 0x49, 0x49, 0x49, 0x36, // 42 B 0x3e, 0x41, 0x41, 0x41, 0x22, // 43 C 0x7f, 0x41, 0x41, 0x22, 0x1c, // 44 D 0x7f, 0x49, 0x49, 0x49, 0x41, // 45 E 0x7f, 0x09, 0x09, 0x09, 0x01, // 46 F 0x3e, 0x41, 0x49, 0x49, 0x7a, // 47 G 0x7f, 0x08, 0x08, 0x08, 0x7f, // 48 H 0x00, 0x41, 0x7f, 0x41, 0x00, // 49 I 0x20, 0x40, 0x41, 0x3f, 0x01, // 4a J 0x7f, 0x08, 0x14, 0x22, 0x41, // 4b K 0x7f, 0x40, 0x40, 0x40, 0x40, // 4c L 0x7f, 0x02, 0x0c, 0x02, 0x7f, // 4d M 0x7f, 0x04, 0x08, 0x10, 0x7f, // 4e N 0x3e, 0x41, 0x41, 0x41, 0x3e, // 4f O 0x7f, 0x09, 0x09, 0x09, 0x06, // 50 P 0x3e, 0x41, 0x51, 0x21, 0x5e, // 51 Q 0x7f, 0x09, 0x19, 0x29, 0x46, // 52 R 0x46, 0x49, 0x49, 0x49, 0x31, // 53 S 0x01, 0x01, 0x7f, 0x01, 0x01, // 54 T 0x3f, 0x40, 0x40, 0x40, 0x3f, // 55 U 0x1f, 0x20, 0x40, 0x20, 0x1f, // 56 V 0x3f, 0x40, 0x38, 0x40, 0x3f, // 57 W 0x63, 0x14, 0x08, 0x14, 0x63, // 58 X 0x07, 0x08, 0x70, 0x08, 0x07, // 59 Y 0x61, 0x51, 0x49, 0x45, 0x43, // 5a Z 0x00, 0x7f, 0x41, 0x41, 0x00, // 5b [ 0x02, 0x04, 0x08, 0x10, 0x20, // 5c ¥ 0x00, 0x41, 0x41, 0x7f, 0x00, // 5d ] 0x04, 0x02, 0x01, 0x02, 0x04, // 5e ^ 0x40, 0x40, 0x40, 0x40, 0x40, // 5f _ 0x00, 0x01, 0x02, 0x04, 0x00, // 60 ` 0x20, 0x54, 0x54, 0x54, 0x78, // 61 a 0x7f, 0x48, 0x44, 0x44, 0x38, // 62 b 0x38, 0x44, 0x44, 0x44, 0x20, // 63 c 0x38, 0x44, 0x44, 0x48, 0x7f, // 64 d 0x38, 0x54, 0x54, 0x54, 0x18, // 65 e 0x08, 0x7e, 0x09, 0x01, 0x02, // 66 f 0x0c, 0x52, 0x52, 0x52, 0x3e, // 67 g 0x7f, 0x08, 0x04, 0x04, 0x78, // 68 h 0x00, 0x44, 0x7d, 0x40, 0x00, // 69 i 0x20, 0x40, 0x44, 0x3d, 0x00, // 6a j 0x7f, 0x10, 0x28, 0x44, 0x00, // 6b k 0x00, 0x41, 0x7f, 0x40, 0x00, // 6c l 0x7c, 0x04, 0x18, 0x04, 0x78, // 6d m 0x7c, 0x08, 0x04, 0x04, 0x78, // 6e n 0x38, 0x44, 0x44, 0x44, 0x38, // 6f o 0x7c, 0x14, 0x14, 0x14, 0x08, // 70 p 0x08, 0x14, 0x14, 0x18, 0x7c, // 71 q 0x7c, 0x08, 0x04, 0x04, 0x08, // 72 r 0x48, 0x54, 0x54, 0x54, 0x20, // 73 s 0x04, 0x3f, 0x44, 0x40, 0x20, // 74 t 0x3c, 0x40, 0x40, 0x20, 0x7c, // 75 u 0x1c, 0x20, 0x40, 0x20, 0x1c, // 76 v 0x3c, 0x40, 0x30, 0x40, 0x3c, // 77 w 0x44, 0x28, 0x10, 0x28, 0x44, // 78 x 0x0c, 0x50, 0x50, 0x50, 0x3c, // 79 y 0x44, 0x64, 0x54, 0x4c, 0x44, // 7a z 0x00, 0x08, 0x36, 0x41, 0x00, // 7b { 0x00, 0x00, 0x7f, 0x00, 0x00, // 7c | 0x00, 0x41, 0x36, 0x08, 0x00, // 7d } 0x10, 0x08, 0x08, 0x10, 0x08, // 7e 0x78, 0x46, 0x41, 0x46, 0x78 // 7f }; // Define LCD Pins #define CLK 2 #define DIN 0 #define DC 1 #define CE 5 // Not used #define RST 3 void writeStrLCD(char *charPtr) { while(*charPtr) WriteCharLCD(*charPtr++); } void WriteCharLCD(char charPtr) { int ofs=(charPtr-32)*5; for (int i=ofs;i<ofs+5;i++) writeDataLCD(pgm_read_byte(&(ASCII[i]))); writeDataLCD(0x00); } void writeDataLCD(byte data) { digitalWrite(DC, HIGH); // DC pin is low for commands shiftOut(DIN, CLK, MSBFIRST, data); // Transmit serial data } void setCursorLCD(int x, int y) { writeCmdLCD(0x80|x); // Column. writeCmdLCD(0x40|y); // Row. } void writeCmdLCD(byte cmd) { digitalWrite(DC, LOW); // DC pin is low for commands shiftOut(DIN, CLK, MSBFIRST, cmd); // Transmit serial data } void drawMagn(int x, int m) { byte t; if (m<0) m=0; if (m>47) m=47; for (int y=5;y>=0;y--) { writeCmdLCD(0x80|x); writeCmdLCD(0x40|y); if (m>7) { t=0xff; m=m-8; } else if (m>0) { t=0xff<<(8-m); m=0; } else { t=0; } // Add frequency ticks if ((y==0)&&(x==0)) t=t|0x1f; if ((y==0)&&(x==6)) t=t|0x07; if ((y==0)&&(x==13)) t=t|0x07; if ((y==0)&&(x==19)) t=t|0x07; if ((y==0)&&(x==26)) t=t|0x07; if ((y==0)&&(x==32)) t=t|0x1f; if ((y==0)&&(x==38)) t=t|0x07; if ((y==0)&&(x==45)) t=t|0x07; if ((y==0)&&(x==51)) t=t|0x07; if ((y==0)&&(x==58)) t=t|0x07; if ((y==0)&&(x==64)) t=t|0x1f; // Add magnitude scale lines if (y%2==0) t=t|0x08; writeDataLCD(t); } } void initLCD() { pinMode(RST, OUTPUT); pinMode(DC, OUTPUT); pinMode(DIN, OUTPUT); pinMode(CLK, OUTPUT); digitalWrite(RST, LOW); digitalWrite(RST, HIGH); writeCmdLCD(0x21); // LCD extended commands writeCmdLCD(0xbf); // set LCD Vop (contrast) writeCmdLCD(0x04); // set temp coefficent writeCmdLCD(0x14); // LCD bias mode 1:48 writeCmdLCD(0x20); // LCD basic commands writeCmdLCD(0x0C); // LCD normal video // Clear LCD for (int i=0;i<504;i++) writeDataLCD(0x00); } // Audio Spectrum Analyser #define SampleInput A2 // Name the sample input pin #define SampleFreq 61500 // Nominal sample frequency #define BandWidth 500 // Nominal bandwidth #define SampleNumber 250 // Number of samples (~2*SampleFreq/BandWidth+1) #define MaxFreq 10000 // Max analysis frequency #define CPUFreq 16900000 // Frequency is not 16MHz for the DigiSpark // Define various ADC prescaler const unsigned char PS_16=(1<<ADPS2); const unsigned char PS_32=(1<<ADPS2)|(1<<ADPS0); const unsigned char PS_64=(1<<ADPS2)|(1<<ADPS1); const unsigned char PS_128=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // Setup the serial port and pin 2 void setup() { // Setup the ADC pinMode(SampleInput,INPUT); ADCSRA&=~PS_128; // Remove bits set by Arduino library // Set prescaler // ADCSRA|=PS_64; // 64 prescaler (250 kHz assuming a 16MHz clock) // ADCSRA|=PS_32; // 32 prescaler (500 kHz assuming a 16MHz clock) ADCSRA|=PS_16; // 16 prescaler (1 MHz assuming a 16MHz clock) initLCD(); setCursorLCD(0,0);writeStrLCD("Spectrum"); // Project setCursorLCD(0,1);writeStrLCD("Analyser"); setCursorLCD(0,2);writeStrLCD("0-10 kHz"); // Range setCursorLCD(0,4);writeStrLCD("agp.cooper"); // Author setCursorLCD(0,5);writeStrLCD("@gmail.com"); delay(2000); } void loop() { static byte samples[SampleNumber]; // Sample array pointer static long sampleFreq; // Live sample frequency long freq; // Frequency of interest float s; // Goertzel variables float s_prev; float s_prev2; float coeff; float magn; int f; unsigned long ts; unsigned long tf; if (true) { // Sychronise the start of sampling with data slicer int a0,a1; a0=1023; for (int i=0;i<SampleNumber;i+=2) { a1=analogRead(SampleInput); a0=(a0*13+a1*3)/16; if (a1>a0+3) break; } ts=micros(); for (int i=0;i<SampleNumber;i++) samples[i]=(byte)(analogRead(SampleInput)>>2); tf=micros(); sampleFreq=1000000*SampleNumber/(tf-ts); sampleFreq=sampleFreq*(CPUFreq/16000)/1000; // Fix for Digispark clock speed } else { // Generate test samples for (int i=0;i<SampleNumber;i++) { samples[i]=(byte)(30*(sin(2*M_PI*i*5000/sampleFreq)+sin(2*M_PI*i*2500/sampleFreq)+sin(2*M_PI*i*7500/sampleFreq)+sin(2*M_PI*i*10000/sampleFreq))+127); } sampleFreq=SampleFreq; } /* Goertzel Algorithm refer to https://en.wikipedia.org/wiki/Goertzel_algorithm */ // Set Hamming Window for (int i=0;i<SampleNumber;i++) samples[i]=(byte)(samples[i]*(0.54-0.46*cos(2*M_PI*i/(SampleNumber-1)))); // Scan frequencies freq=0; for (f=0;f<=64;f++) { freq+=MaxFreq/64; coeff=2*cos(2*M_PI*freq/sampleFreq); s_prev=0; s_prev2=0; for (int i=0;i<SampleNumber;i++) { s=0.01960784*samples[i]+s_prev*coeff-s_prev2; s_prev2=s_prev; s_prev=s; } // Get magnitude magn=2*sqrt(s_prev2*s_prev2+s_prev*s_prev-coeff*s_prev*s_prev2)/SampleNumber; // Display maginitude in dB drawMagn(f,(int)(36+16*log10(magn+0.001))); } // Add Scale labels setCursorLCD(66,0); writeStrLCD("+10"); setCursorLCD(66,2); writeStrLCD("-10"); setCursorLCD(66,4); writeStrLCD("-30"); setCursorLCD(66,5); writeStrLCD("10k"); }
You may need to edit:
#define CPUFreq 16900000 // Frequency is not 16MHz for the DigiSpark
for your Digispark if the frequency seems to be off.
The code is not very complicated you should be able to workout what I have done if you spend some time reviewing it.
One area that I want to look at is increasing the sampling frequency to 2 MHz. The original intent of the code was to analyse ultrasonic signals.
Regards AlanX
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
hi please there is any possibility to implement this code into arduino UNO i don't want use DigiSpark !!
Are you sure? yes | no