adc_fft.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Sample from the ADC continuously at a particular sample rate
  2. // and then compute an FFT over the data
  3. //
  4. // much of this code is from pico-examples/adc/dma_capture/dma_capture.c
  5. // the rest is written by Alex Wulff (www.AlexWulff.com)
  6. #include <stdio.h>
  7. #include <math.h>
  8. #include "pico/stdlib.h"
  9. #include "hardware/adc.h"
  10. #include "hardware/dma.h"
  11. #include "kiss_fftr.h"
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include "pico/stdlib.h"
  15. #include "hardware/pio.h"
  16. #include "hardware/clocks.h"
  17. #include "ws2812.pio.h"
  18. #define IS_RGBW false
  19. #define NUM_PIXELS 150
  20. #ifdef PICO_DEFAULT_WS2812_PIN
  21. #define WS2812_PIN PICO_DEFAULT_WS2812_PIN
  22. #else
  23. // default to pin 2 if the board doesn't have a default WS2812 pin defined
  24. #define WS2812_PIN 6
  25. #endif
  26. static inline void put_pixel(uint32_t pixel_grb) {
  27. pio_sm_put_blocking(pio0, 0, pixel_grb << 8u);
  28. }
  29. // set this to determine sample rate
  30. // 0 = 500,000 Hz
  31. // 960 = 50,000 Hz
  32. // 9600 = 5,000 Hz
  33. #define CLOCK_DIV 960
  34. #define FSAMP 50000
  35. // Channel 0 is GPIO26
  36. #define CAPTURE_CHANNEL 0
  37. #define LED_PIN 25
  38. // BE CAREFUL: anything over about 9000 here will cause things
  39. // to silently break. The code will compile and upload, but due
  40. // to memory issues nothing will work properly
  41. #define NSAMP 4096
  42. // globals
  43. dma_channel_config cfg;
  44. uint dma_chan;
  45. float freqs[NSAMP];
  46. float freqsInLog[NSAMP];
  47. float power[NSAMP / 2];
  48. float powerInLog[NUM_PIXELS];
  49. void setup();
  50. void sample(uint8_t *capture_buf);
  51. int main() {
  52. uint8_t cap_buf[NSAMP];
  53. kiss_fft_scalar fft_in[NSAMP]; // kiss_fft_scalar is a float
  54. kiss_fft_cpx fft_out[NSAMP];
  55. kiss_fftr_cfg cfg = kiss_fftr_alloc(NSAMP, false, 0, 0);
  56. // setup ports and outputs
  57. setup();
  58. printf("Started\n");
  59. while (1) {
  60. //printf("Loop\n");
  61. // get NSAMP samples at FSAMP
  62. sample(cap_buf);
  63. // fill fourier transform input while subtracting DC component
  64. uint64_t sum = 0;
  65. for (int i = 0; i < NSAMP; i++) { sum += cap_buf[i]; }
  66. float avg = (float) sum / NSAMP;
  67. for (int i = 0; i < NSAMP; i++) { fft_in[i] = (float) cap_buf[i] - avg; }
  68. // compute fast fourier transform
  69. kiss_fftr(cfg, fft_in, fft_out);
  70. // compute power and calculate max freq component
  71. // any frequency bin over NSAMP/2 is aliased (nyquist sampling theorum)
  72. for (int i = 0; i < NSAMP / 2; i++) {
  73. power[i] = fft_out[i].r * fft_out[i].r + fft_out[i].i * fft_out[i].i;
  74. }
  75. float f_max = FSAMP;
  76. float f_res = f_max / NSAMP;
  77. for (int i = 0; i < NUM_PIXELS; i++) {
  78. float lowFreq = freqsInLog[i];
  79. float highFreq;
  80. if (i != NUM_PIXELS - 1) {
  81. highFreq = freqsInLog[i + 1];
  82. } else {
  83. highFreq = FSAMP / 2;
  84. }
  85. int lowInd = lowFreq / f_res;
  86. int highInd = highFreq / f_res;
  87. float totalPower = 0;
  88. for (int j = lowInd; j < highInd + 1; j++) {
  89. totalPower += power[j];
  90. }
  91. //printf("lowInd = %d, highInd = %d, lowFreq = %f, highFreq = %f, freq[lowInd] = %f, freq[highInd] = %f\n", lowInd, highInd, lowFreq, highFreq, freqs[lowInd], freqs[highInd]);
  92. float div_power = totalPower / (highInd + 1 - lowInd);
  93. powerInLog[i] = 20 * log(fmax(div_power - 1000000, 1));
  94. }
  95. //for (int i = 0; i < NUM_PIXELS - 1; i++) {
  96. // printf("Power for freq %f to %f = %f (%f)\n", freqsInLog[i], freqsInLog[i + 1], powerInLog[i], power[i]);
  97. //}
  98. for (int i = 0; i < NUM_PIXELS; i++) {
  99. uint32_t value = (uint32_t)(fmin(255, powerInLog[i]/400.0 * 255));
  100. //printf("Color = %d, pixel = %d\n", value, i);
  101. put_pixel(value);
  102. }
  103. sleep_ms(50);
  104. }
  105. // should never get here
  106. kiss_fft_free(cfg);
  107. }
  108. void sample(uint8_t *capture_buf) {
  109. adc_fifo_drain();
  110. adc_run(false);
  111. dma_channel_configure(dma_chan, &cfg,
  112. capture_buf, // dst
  113. &adc_hw->fifo, // src
  114. NSAMP, // transfer count
  115. true // start immediately
  116. );
  117. gpio_put(LED_PIN, 1);
  118. adc_run(true);
  119. dma_channel_wait_for_finish_blocking(dma_chan);
  120. gpio_put(LED_PIN, 0);
  121. }
  122. void setup() {
  123. stdio_init_all();
  124. // todo get free sm
  125. PIO pio = pio0;
  126. int sm = 0;
  127. uint offset = pio_add_program(pio, &ws2812_program);
  128. ws2812_program_init(pio, sm, offset, WS2812_PIN, 800000, IS_RGBW);
  129. //gpio_init(LED_PIN);
  130. //gpio_set_dir(LED_PIN, GPIO_OUT);
  131. adc_gpio_init(26 + CAPTURE_CHANNEL);
  132. adc_init();
  133. adc_select_input(CAPTURE_CHANNEL);
  134. adc_fifo_setup(
  135. true, // Write each completed conversion to the sample FIFO
  136. true, // Enable DMA data request (DREQ)
  137. 1, // DREQ (and IRQ) asserted when at least 1 sample present
  138. false, // We won't see the ERR bit because of 8 bit reads; disable.
  139. true // Shift each sample to 8 bits when pushing to FIFO
  140. );
  141. // set sample rate
  142. adc_set_clkdiv(CLOCK_DIV);
  143. sleep_ms(1000);
  144. // Set up the DMA to start transferring data as soon as it appears in FIFO
  145. uint dma_chan = dma_claim_unused_channel(true);
  146. cfg = dma_channel_get_default_config(dma_chan);
  147. // Reading from constant address, writing to incrementing byte addresses
  148. channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
  149. channel_config_set_read_increment(&cfg, false);
  150. channel_config_set_write_increment(&cfg, true);
  151. // Pace transfers based on availability of ADC samples
  152. channel_config_set_dreq(&cfg, DREQ_ADC);
  153. printf("Starting\n");
  154. // calculate frequencies of each bin
  155. float f_max = FSAMP;
  156. float f_res = f_max / NSAMP;
  157. for (int i = 0; i < NSAMP; i++) {
  158. freqs[i] = f_res * i;
  159. }
  160. // Or 20kHz?
  161. float max_power = log(FSAMP / 2.0);
  162. float min_power = log(80);
  163. for (int i = 0; i < NUM_PIXELS; i++) {
  164. freqsInLog[i] = exp(i / (float) NUM_PIXELS * (max_power - min_power) + min_power);
  165. printf("freqsInLog[%d] = %f.1\n", i, freqsInLog[i]);
  166. }
  167. }