|
1 | 1 | /*
|
2 |
| - * This is an example to read analog data at high frequency using the I2S peripheral |
3 |
| - * Run a wire between pins 27 & 32 |
4 |
| - * The readings from the device will be 12bit (0-4096) |
5 |
| - */ |
| 2 | + This example demonstrates I2S ADC capability to sample high frequency analog signals. |
| 3 | + The PWM signal generated with ledc is only for ease of use when first trying out. |
| 4 | + To sample the generated signal connect default pins 27(PWM) and 32(Sampling) together. |
| 5 | + If you do not wish to generate PWM simply comment out the definition of constant GENERATE_PWM |
| 6 | + Try to change the PWM_DUTY_PERCENT and see how to averaged value changes. |
| 7 | +
|
| 8 | + The maximum for I2S ADC sampling frequency is 5MHz (value 5000000), however there will be many values repeated because the real |
| 9 | + sampling frequency is much lower - |
| 10 | +
|
| 11 | + By default this example will print values compatible with Arduino plotter |
| 12 | + 1. signal - all values |
| 13 | + 2. signal - averaged value |
| 14 | +
|
| 15 | + You can change the number of sample over which is the signal averaged by changing value of AVERAGE_EVERY_N_SAMPLES |
| 16 | + If you comment the definition altogether the averaging will not be performed nor printed. |
| 17 | +
|
| 18 | + If you do not wish to print every value, simply comment definition of constant PRINT_ALL_VALUES |
| 19 | +
|
| 20 | + Note: ESP prints messages at startup which will pollute Arduino IDE Serial plotter legend. |
| 21 | + To avoid this pollution, start the plotter after startup (op restart) |
| 22 | +*/ |
6 | 23 | #include <driver/i2s.h>
|
7 | 24 |
|
8 |
| -#define I2S_SAMPLE_RATE 78125 |
9 |
| -#define ADC_INPUT ADC1_CHANNEL_4 //pin 32 |
10 |
| -#define OUTPUT_PIN 27 |
11 |
| -#define OUTPUT_VALUE 3800 |
12 |
| -#define READ_DELAY 9000 //microseconds |
| 25 | +// I2S |
| 26 | +#define I2S_SAMPLE_RATE (277777) // Max sampling frequency = 277.777 kHz |
| 27 | +#define ADC_INPUT (ADC1_CHANNEL_4) //pin 32 |
| 28 | +#define I2S_DMA_BUF_LEN (1024) |
| 29 | + |
| 30 | +// PWM |
| 31 | +#define GENERATE_PWM |
| 32 | +#define OUTPUT_PIN (27) |
| 33 | +#define PWM_FREQUENCY ((I2S_SAMPLE_RATE)/4) |
| 34 | +#define PWM_DUTY_PERCENT (50) |
| 35 | +#define PWM_RESOLUTION_BITS (2) // Lower bit resolution enables higher frequency |
| 36 | +#define PWM_DUTY_VALUE ((((1<<(PWM_RESOLUTION_BITS)))*(PWM_DUTY_PERCENT))/100) // Duty value used for setup function based on resolution |
13 | 37 |
|
14 |
| -uint16_t adc_reading; |
| 38 | +// Sample post processing |
| 39 | +#define PRINT_ALL_VALUES |
| 40 | +#define AVERAGE_EVERY_N_SAMPLES (100) |
15 | 41 |
|
16 |
| -void i2sInit() |
17 |
| -{ |
18 |
| - i2s_config_t i2s_config = { |
| 42 | +void i2sInit(){ |
| 43 | + i2s_config_t i2s_config = { |
19 | 44 | .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
20 | 45 | .sample_rate = I2S_SAMPLE_RATE, // The format of the signal using ADC_BUILT_IN
|
21 | 46 | .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB
|
22 | 47 | .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
|
23 |
| - .communication_format = I2S_COMM_FORMAT_I2S_MSB, |
| 48 | + .communication_format = I2S_COMM_FORMAT_STAND_I2S, |
24 | 49 | .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
25 |
| - .dma_buf_count = 4, |
26 |
| - .dma_buf_len = 8, |
| 50 | + .dma_buf_count = 8, |
| 51 | + .dma_buf_len = I2S_DMA_BUF_LEN, |
27 | 52 | .use_apll = false,
|
28 | 53 | .tx_desc_auto_clear = false,
|
29 | 54 | .fixed_mclk = 0
|
30 |
| - }; |
31 |
| - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); |
32 |
| - i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT); |
33 |
| - i2s_adc_enable(I2S_NUM_0); |
34 |
| -} |
| 55 | + }; |
| 56 | + Serial.printf("Attempting to setup I2S ADC with sampling frequency %d Hz\n", I2S_SAMPLE_RATE); |
| 57 | + if(ESP_OK != i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL)){ |
| 58 | + Serial.printf("Error installing I2S. Halt!"); |
| 59 | + while(1); |
| 60 | + } |
| 61 | + if(ESP_OK != i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT)){ |
| 62 | + Serial.printf("Error setting up ADC. Halt!"); |
| 63 | + while(1); |
| 64 | + } |
| 65 | + if(ESP_OK != adc1_config_channel_atten(ADC_INPUT, ADC_ATTEN_DB_11)){ |
| 66 | + Serial.printf("Error setting up ADC attenuation. Halt!"); |
| 67 | + while(1); |
| 68 | + } |
35 | 69 |
|
36 |
| -void reader(void *pvParameters) { |
37 |
| - uint32_t read_counter = 0; |
38 |
| - uint64_t read_sum = 0; |
39 |
| -// The 4 high bits are the channel, and the data is inverted |
40 |
| - uint16_t offset = (int)ADC_INPUT * 0x1000 + 0xFFF; |
41 |
| - size_t bytes_read; |
42 |
| - while(1){ |
43 |
| - uint16_t buffer[2] = {0}; |
44 |
| - i2s_read(I2S_NUM_0, &buffer, sizeof(buffer), &bytes_read, 15); |
45 |
| - //Serial.printf("%d %d\n", offset - buffer[0], offset - buffer[1]); |
46 |
| - if (bytes_read == sizeof(buffer)) { |
47 |
| - read_sum += offset - buffer[0]; |
48 |
| - read_sum += offset - buffer[1]; |
49 |
| - read_counter++; |
50 |
| - } else { |
51 |
| - Serial.println("buffer empty"); |
52 |
| - } |
53 |
| - if (read_counter == I2S_SAMPLE_RATE) { |
54 |
| - adc_reading = read_sum / I2S_SAMPLE_RATE / 2; |
55 |
| - //Serial.printf("avg: %d millis: ", adc_reading); |
56 |
| - //Serial.println(millis()); |
57 |
| - read_counter = 0; |
58 |
| - read_sum = 0; |
59 |
| - i2s_adc_disable(I2S_NUM_0); |
60 |
| - delay(READ_DELAY); |
61 |
| - i2s_adc_enable(I2S_NUM_0); |
62 |
| - } |
| 70 | + if(ESP_OK != i2s_adc_enable(I2S_NUM_0)){ |
| 71 | + Serial.printf("Error enabling ADC. Halt!"); |
| 72 | + while(1); |
63 | 73 | }
|
| 74 | + Serial.printf("I2S ADC setup ok\n"); |
64 | 75 | }
|
65 | 76 |
|
66 | 77 | void setup() {
|
67 | 78 | Serial.begin(115200);
|
68 |
| - // Put a signal out on pin |
69 |
| - uint32_t freq = ledcSetup(0, I2S_SAMPLE_RATE, 10); |
70 |
| - Serial.printf("Output frequency: %d\n", freq); |
71 |
| - ledcWrite(0, OUTPUT_VALUE/4); |
| 79 | + |
| 80 | +#ifdef GENERATE_PWM |
| 81 | + // PWM setup |
| 82 | + Serial.printf("Setting up PWM: frequency = %d; resolution bits %d; Duty cycle = %d; duty value = %d, Output pin = %d\n", PWM_FREQUENCY, PWM_RESOLUTION_BITS, PWM_DUTY_PERCENT, PWM_DUTY_VALUE, OUTPUT_PIN); |
| 83 | + uint32_t freq = ledcSetup(0, PWM_FREQUENCY, PWM_RESOLUTION_BITS); |
| 84 | + if(freq != PWM_FREQUENCY){ |
| 85 | + Serial.printf("Error setting up PWM. Halt!"); |
| 86 | + while(1); |
| 87 | + } |
72 | 88 | ledcAttachPin(OUTPUT_PIN, 0);
|
| 89 | + ledcWrite(0, PWM_DUTY_VALUE); |
| 90 | + Serial.printf("PWM setup ok\n"); |
| 91 | +#endif |
| 92 | + |
73 | 93 | // Initialize the I2S peripheral
|
74 | 94 | i2sInit();
|
75 |
| - // Create a task that will read the data |
76 |
| - xTaskCreatePinnedToCore(reader, "ADC_reader", 2048, NULL, 1, NULL, 1); |
77 | 95 | }
|
78 | 96 |
|
79 |
| -void loop() { |
80 |
| - delay(1020); |
81 |
| - Serial.printf("ADC reading: %d\n", adc_reading); |
82 |
| - delay(READ_DELAY); |
| 97 | +void loop(){ |
| 98 | +// The 4 high bits are the channel, and the data is inverted |
| 99 | + size_t bytes_read; |
| 100 | + uint16_t buffer[I2S_DMA_BUF_LEN] = {0}; |
| 101 | + |
| 102 | +#ifdef AVERAGE_EVERY_N_SAMPLES |
| 103 | + uint32_t read_counter = 0; |
| 104 | + uint32_t averaged_reading = 0; |
| 105 | + uint64_t read_sum = 0; |
| 106 | +#endif |
| 107 | + |
| 108 | + while(1){ |
| 109 | + i2s_read(I2S_NUM_0, &buffer, sizeof(buffer), &bytes_read, 15); |
| 110 | + //Serial.printf("read %d Bytes\n", bytes_read); |
| 111 | + |
| 112 | + for(int i = 0; i < bytes_read/2; ++i){ |
| 113 | +#ifdef PRINT_ALL_VALUES |
| 114 | + //Serial.printf("[%d] = %d\n", i, buffer[i] & 0x0FFF); // Print with indexes |
| 115 | + Serial.printf("Signal:%d ", buffer[i] & 0x0FFF); // Print compatible with Arduino Plotter |
| 116 | +#endif |
| 117 | +#ifdef AVERAGE_EVERY_N_SAMPLES |
| 118 | + read_sum += buffer[i] & 0x0FFF; |
| 119 | + ++read_counter; |
| 120 | + if(read_counter == AVERAGE_EVERY_N_SAMPLES){ |
| 121 | + averaged_reading = read_sum / AVERAGE_EVERY_N_SAMPLES; |
| 122 | + //Serial.printf("averaged_reading = %d over %d samples\n", averaged_reading, read_counter); // Print with additional info |
| 123 | + Serial.printf("Averaged_signal:%d", averaged_reading); // Print compatible with Arduino Plotter |
| 124 | + read_counter = 0; |
| 125 | + read_sum = 0; |
| 126 | + } |
| 127 | +#endif |
| 128 | +#if defined(PRINT_ALL_VALUES) || defined (AVERAGE_EVERY_N_SAMPLES) |
| 129 | + Serial.printf("\n"); |
| 130 | +#endif |
| 131 | + } // for |
| 132 | + } // while |
83 | 133 | }
|
0 commit comments