【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
实验二百零七:I2C红色8*8LED点阵模块VK16k33驱动1088BS树莓派物联网可扩展编程
项目十九:Arduino 的小型音频可视化
实验开源代码
- /*
- 【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
- 实验二百零七:I2C红色8*8LED点阵模块VK16k33驱动1088BS树莓派物联网可扩展编程
- 项目十九:Arduino 的小型音频可视化
- 模块接线:
- - 3.3V 至麦克风放大器 + 和 Arduino AREF 引脚 <-- 重要!
- MAX9814 Arduino
- VCC 3.3V
- GND GND
- OUT A0
-
- VK16k33 UNO
- VIN 5V
- GND GND
- SCL A5
- SDA A4
- */
-
- // 重要提示:FFT_N 应该在 ffft.h 中 #defined 为 128。
- #include <avr/pgmspace.h>
- #include <ffft.h>
- #include <math.h>
- #include <Wire.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_LEDBackpack.h>
-
- // Microphone connects to Analog Pin 0. Corresponding ADC channel number
- // varies among boards...it's ADC0 on Uno and Mega, ADC7 on Leonardo.
- // Other boards may require different settings; refer to datasheet.
- #ifdef __AVR_ATmega32U4__
- #define ADC_CHANNEL 7
- #else
- #define ADC_CHANNEL 0
- #endif
-
- int16_t capture[FFT_N]; // Audio capture buffer
- complex_t bfly_buff[FFT_N]; // FFT "butterfly" buffer
- uint16_t spectrum[FFT_N / 2]; // Spectrum output buffer
- volatile byte samplePos = 0; // Buffer position counter
-
- byte
- peak[8], // Peak level of each column; used for falling dots
- dotCount = 0, // Frame counter for delaying dot-falling speed
- colCount = 0; // Frame counter for storing past column data
- int
- col[8][10], // Column levels for the prior 10 frames
- minLvlAvg[8], // For dynamic adjustment of low & high ends of graph,
- maxLvlAvg[8], // pseudo rolling averages for the prior few frames.
- colDiv[8]; // Used when filtering FFT output to 8 columns
-
- /*
- These tables were arrived at through testing, modeling and trial and error,
- exposing the unit to assorted music and sounds. But there's no One Perfect
- EQ Setting to Rule Them All, and the graph may respond better to some
- inputs than others. The software works at making the graph interesting,
- but some columns will always be less lively than others, especially
- comparing live speech against ambient music of varying genres.
- */
- static const uint8_t PROGMEM
- // This is low-level noise that's subtracted from each FFT output column:
- noise[64] = { 8, 6, 6, 5, 3, 4, 4, 4, 3, 4, 4, 3, 2, 3, 3, 4,
- 2, 1, 2, 1, 3, 2, 3, 2, 1, 2, 3, 1, 2, 3, 4, 4,
- 3, 2, 2, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4
- },
- // These are scaling quotients for each FFT output column, sort of a
- // graphic EQ in reverse. Most music is pretty heavy at the bass end.
- eq[64] = {
- 255, 175, 218, 225, 220, 198, 147, 99, 68, 47, 33, 22, 14, 8, 4, 2,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- },
- // When filtering down to 8 columns, these tables contain indexes
- // and weightings of the FFT spectrum output values to use. Not all
- // buckets are used -- the bottom-most and several at the top are
- // either noisy or out of range or generally not good for a graph.
- col0data[] = { 2, 1, // # of spectrum bins to merge, index of first
- 111, 8
- }, // Weights for each bin
- col1data[] = { 4, 1, // 4 bins, starting at index 1
- 19, 186, 38, 2
- }, // Weights for 4 bins. Got it now?
- col2data[] = { 5, 2,
- 11, 156, 118, 16, 1
- },
- col3data[] = { 8, 3,
- 5, 55, 165, 164, 71, 18, 4, 1
- },
- col4data[] = { 11, 5,
- 3, 24, 89, 169, 178, 118, 54, 20, 6, 2, 1
- },
- col5data[] = { 17, 7,
- 2, 9, 29, 70, 125, 172, 185, 162, 118, 74,
- 41, 21, 10, 5, 2, 1, 1
- },
- col6data[] = { 25, 11,
- 1, 4, 11, 25, 49, 83, 121, 156, 180, 185,
- 174, 149, 118, 87, 60, 40, 25, 16, 10, 6,
- 4, 2, 1, 1, 1
- },
- col7data[] = { 37, 16,
- 1, 2, 5, 10, 18, 30, 46, 67, 92, 118,
- 143, 164, 179, 185, 184, 174, 158, 139, 118, 97,
- 77, 60, 45, 34, 25, 18, 13, 9, 7, 5,
- 3, 2, 2, 1, 1, 1, 1
- },
- // And then this points to the start of the data for each of the columns:
- * const colData[] = {
- col0data, col1data, col2data, col3data,
- col4data, col5data, col6data, col7data
- };
-
- Adafruit_BicolorMatrix matrix = Adafruit_BicolorMatrix();
-
- void setup() {
- uint8_t i, j, nBins, binNum, *data;
-
- memset(peak, 0, sizeof(peak));
- memset(col , 0, sizeof(col));
-
- for (i = 0; i < 8; i++) {
- minLvlAvg[i] = 0;
- maxLvlAvg[i] = 512;
- data = (uint8_t *)pgm_read_word(&colData[i]);
- nBins = pgm_read_byte(&data[0]) + 2;
- binNum = pgm_read_byte(&data[1]);
- for (colDiv[i] = 0, j = 2; j < nBins; j++)
- colDiv[i] += pgm_read_byte(&data[j]);
- }
-
- matrix.begin(0x70);
-
- // Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles/conversion
- ADMUX = ADC_CHANNEL; // Channel sel, right-adj, use AREF pin
- ADCSRA = _BV(ADEN) | // ADC enable
- _BV(ADSC) | // ADC start
- _BV(ADATE) | // Auto trigger
- _BV(ADIE) | // Interrupt enable
- _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
- ADCSRB = 0; // Free run mode, no high MUX bit
- DIDR0 = 1 << ADC_CHANNEL; // Turn off digital input for ADC pin
- TIMSK0 = 0; // Timer0 off
-
- sei(); // Enable interrupts
- }
-
- void loop() {
- uint8_t i, x, L, *data, nBins, binNum, weighting, c;
- uint16_t minLvl, maxLvl;
- int level, y, sum;
-
- while (ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish
-
- fft_input(capture, bfly_buff); // Samples -> complex #s
- samplePos = 0; // Reset sample counter
- ADCSRA |= _BV(ADIE); // Resume sampling interrupt
- fft_execute(bfly_buff); // Process complex data
- fft_output(bfly_buff, spectrum); // Complex -> spectrum
-
- // Remove noise and apply EQ levels
- for (x = 0; x < FFT_N / 2; x++) {
- L = pgm_read_byte(&noise[x]);
- spectrum[x] = (spectrum[x] <= L) ? 0 :
- (((spectrum[x] - L) * (256L - pgm_read_byte(&eq[x]))) >> 8);
- }
-
- // Fill background w/colors, then idle parts of columns will erase
- matrix.fillRect(0, 0, 8, 3, LED_RED); // Upper section
- matrix.fillRect(0, 3, 8, 2, LED_YELLOW); // Mid
- matrix.fillRect(0, 5, 8, 3, LED_GREEN); // Lower section
-
- // Downsample spectrum output to 8 columns:
- for (x = 0; x < 8; x++) {
- data = (uint8_t *)pgm_read_word(&colData[x]);
- nBins = pgm_read_byte(&data[0]) + 2;
- binNum = pgm_read_byte(&data[1]);
- for (sum = 0, i = 2; i < nBins; i++)
- sum += spectrum[binNum++] * pgm_read_byte(&data[i]); // Weighted
- col[x][colCount] = sum / colDiv[x]; // Average
- minLvl = maxLvl = col[x][0];
- for (i = 1; i < 10; i++) { // Get range of prior 10 frames
- if (col[x][i] < minLvl) minLvl = col[x][i];
- else if (col[x][i] > maxLvl) maxLvl = col[x][i];
- }
- // minLvl and maxLvl indicate the extents of the FFT output, used
- // for vertically scaling the output graph (so it looks interesting
- // regardless of volume level). If they're too close together though
- // (e.g. at very low volume levels) the graph becomes super coarse
- // and 'jumpy'...so keep some minimum distance between them (this
- // also lets the graph go to zero when no sound is playing):
- if ((maxLvl - minLvl) < 8) maxLvl = minLvl + 8;
- minLvlAvg[x] = (minLvlAvg[x] * 7 + minLvl) >> 3; // Dampen min/max levels
- maxLvlAvg[x] = (maxLvlAvg[x] * 7 + maxLvl) >> 3; // (fake rolling average)
-
- // Second fixed-point scale based on dynamic min/max levels:
- level = 10L * (col[x][colCount] - minLvlAvg[x]) /
- (long)(maxLvlAvg[x] - minLvlAvg[x]);
-
- // Clip output and convert to byte:
- if (level < 0L) c = 0;
- else if (level > 10) c = 10; // Allow dot to go a couple pixels off top
- else c = (uint8_t)level;
-
- if (c > peak[x]) peak[x] = c; // Keep dot on top
-
- if (peak[x] <= 0) { // Empty column?
- matrix.drawLine(x, 0, x, 7, LED_OFF);
- continue;
- } else if (c < 8) { // Partial column?
- matrix.drawLine(x, 0, x, 7 - c, LED_OFF);
- }
-
- // The 'peak' dot color varies, but doesn't necessarily match
- // the three screen regions...yellow has a little extra influence.
- y = 8 - peak[x];
- if (y < 2) matrix.drawPixel(x, y, LED_RED);
- else if (y < 6) matrix.drawPixel(x, y, LED_YELLOW);
- else matrix.drawPixel(x, y, LED_GREEN);
- }
-
- matrix.writeDisplay();
-
- // Every third frame, make the peak pixels drop by 1:
- if (++dotCount >= 3) {
- dotCount = 0;
- for (x = 0; x < 8; x++) {
- if (peak[x] > 0) peak[x]--;
- }
- }
-
- if (++colCount >= 10) colCount = 0;
- }
-
- ISR(ADC_vect) { // Audio-sampling interrupt
- static const int16_t noiseThreshold = 4;
- int16_t sample = ADC; // 0-1023
-
- capture[samplePos] =
- ((sample > (512 - noiseThreshold)) &&
- (sample < (512 + noiseThreshold))) ? 0 :
- sample - 512; // Sign-convert for FFT; -512 to +511
-
- if (++samplePos >= FFT_N) ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off
- }
复制代码
|