Guide to GIGA R1 Advanced ADC/DAC and Audio Features

Learn how to use the ADC/DAC features, along with useful examples on how to generate waveforms and play audio from a file.

In the GIGA R1, you can find the powerful STM32H747XI, a dual-core 32-bit Arm® Cortex® microcontroller from STMicroelectronics; this is the same microcontroller found in the Portenta H7 board.

In this guide, we will focus on the advanced ADC/DAC features, utilizing the Arduino_AdvancedAnalog library. The examples found in this guide can be used to:

  • Set up and read ADCs with specific parameters (resolution, sample rate, number of samples per channel, queue depth).
  • Set up and write to a DAC channel with specific parameters (resolution, frequency, number of samples per channel, queue depth).
  • Generate specific waveforms through input via serial commands (triangle, square, sine, sawtooth waves) as well as adjusting the frequency.
  • Read and play audio files (
    .wav
    ) from a USB stick (connected to USB-A) to a speaker, using the audio jack.

Important note: the GIGA R1 does NOT have an amplifying circuit onboard. Connecting speakers that does not have an amplifier can damage the DAC and the board itself.

Hardware & Software Needed

And for specific examples:

  • USB Mass Storage Device (USB Stick).
  • Auxiliary Cable.
  • Speaker with a built-in amplifier.

ADC/DAC Pins and Connectors

The GIGA gives you access to more pins than any other Arduino board accessible for makers. Many have unique features; we will focus on the pins that have audio features or can be used for developing audio applications. Audio pins and connectors in the GIGA can be divided into three important groups:

  • Analog-to-Digital Converters (ADC) pins
  • Digital-to-Analog Converters (DAC) pins
  • Tip, Ring, Ring, Sleeve (TRRS) 3.5mm jack

The image below shows the position of the audio pins and connectors of the GIGA R1:

ADC/DAC pins and connectors of the GIGA R1
ADC/DAC pins and connectors of the GIGA R1

The table below explains the full functionality of the listed on it; notice that some pins have more than one functionality, such as

DAC0
,
DAC1
,
CANRX
, and
CANTX
:

PinFunctionality
A0ADC
A1ADC
A2ADC
A3ADC
A4ADC
A5ADC
A6ADC
A7ADC
A8ADC
A9ADC
A10ADC
A11ADC
DAC0ADC and DAC
DAC1ADC and DAC
CANRXADC and CAN RX
CANTXADC and CAN TX

Pins

A7
,
DAC0
, and
DAC1
can also be accessed via the built-in TRRS 3.5mm jack.
DAC0
is connected to ring 1 (right channel),
DAC1
is connected to the tip (left channel), and
A7
is connected to ring 2 (microphone) of the jack, as shown in the schematic below:

GIGA R1 TRRS 3.5mm jack schematic
GIGA R1 TRRS 3.5mm jack schematic

Analog-to-Digital Converter (ADC)

An analog-to-digital converter (ADC) is a device that converts an analog voltage, or signal, into digital data. The GIGA R1 microcontroller, the STM32H747XI, embeds three ADCs whose resolution can be configured to 8, 10, 12, 14, or 16 bits. Each ADC shares up to 20 external channels that can be accessed in the GIGA R1 board through pins

A0
,
A1
,
A2
,
A3
,
A4
,
A5
,
A6
,
A7
,
A8
,
A9
,
A10
, and
A11
; pins
DAC0
,
DAC1
,
CANRX
, and
CANTX
can also be used as ADCs.

ADC pins of the GIGA R1
ADC pins of the GIGA R1

A7
is connected to ring 2 (microphone) of the jack, as shown in the schematic below:

GIGA R1 3.5mm input jack schematic
GIGA R1 3.5mm input jack schematic

The GIGA R1 ADCs can be used with the built-in analog input/output functions of the Arduino programming language, though they only provide the basic functionalities of the ADCs. To use all of the capabilities of the DACs from the GIGA R1, we can use the Arduino_AdvancedAnalog library from Arduino. Let's check some interesting examples that show some capabilities of the GIGA R1 ADCs!

Multi Channel ADC

The following example code show how to use two GIGA R1 ADCs simultaneously with the Arduino_AdvancedAnalog library from Arduino:

1// This example shows how to use 2 ADC simultaneously.
2#include <Arduino_AdvancedAnalog.h>
3
4AdvancedADC adc1(A0);
5AdvancedADC adc2(A1);
6uint64_t last_millis = 0;
7
8void setup() {
9 Serial.begin(9600);
10
11 // Resolution, sample rate, number of samples per channel, queue depth.
12 if (!adc1.begin(AN_RESOLUTION_16, 16000, 32, 64)) {
13 Serial.println("Failed to start analog acquisition!");
14 while (1);
15 }
16
17 if (!adc2.begin(AN_RESOLUTION_16, 8000, 32, 64)) {
18 Serial.println("Failed to start analog acquisition!");
19 while (1);
20 }
21}
22
23void adc_print_buf(AdvancedADC &adc) {
24 if (adc.available()) {
25 SampleBuffer buf = adc.read();
26
27 // Print first sample.
28 Serial.println(buf[0]);
29
30 // Release the buffer to return it to the pool.
31 buf.release();
32 }
33}
34
35void loop() {
36 if (millis() - last_millis > 1) {
37 adc_print_buf(adc1);
38 adc_print_buf(adc2);
39 last_millis = millis();
40 }
41}

ADC Serial Plotter Example

The following example code shows how to use two GIGA R1 ADCs simultaneously with the Arduino_AdvancedAnalog library from Arduino and displays the readings via the Serial Plotter of the Arduino IDE:

1#include <Arduino_AdvancedAnalog.h>
2
3AdvancedADC adc(A0, A1);
4uint64_t last_millis = 0;
5
6void setup() {
7 Serial.begin(9600);
8
9 // Resolution, sample rate, number of samples per channel, queue depth.
10 if (!adc.begin(AN_RESOLUTION_16, 16000, 32, 128)) {
11 Serial.println("Failed to start analog acquisition!");
12 while (1);
13 }
14}
15
16void loop() {
17 if (adc.available()) {
18 SampleBuffer buf = adc.read();
19 // Process the buffer.
20 if ((millis() - last_millis) > 20) {
21 Serial.println(buf[0]); // Sample from first channel
22 Serial.println(buf[1]); // Sample from second channel
23 last_millis = millis();
24 }
25 // Release the buffer to return it to the pool.
26 buf.release();
27 }
28}

Digital-to-Analog Converters (DAC)

A digital-to-analog converter (DAC) is a device that has a function opposite to that of the analog-to-digital converter (ADC); a DAC converts digital data to an analog voltage. The GIGA R1 microcontroller, the STM32H747XI, features two 12-bit buffered DAC channels that can convert two digital signals into two analog voltage signals. Some of the features of the DACs found in the GIGA R1 are the following:

  • 8-bit or 12-bit monotonic output
  • Left or right data alignment in 12-bit mode
  • Dual DAC channel independent or simultaneous conversions
  • DMA capability for each channel
  • External triggers for conversion
  • Input voltage reference or internal voltage reference
  • Analog waveform generation

The GIGA R1 DACs are named

DAC0
and
DAC1
; they can be found on pins
A12
and
A13
correspondingly, as shown in the image below:

DAC0, DAC1 and the 3.5mm input jack of the GIGA R1
DAC0, DAC1 and the 3.5mm input jack of the GIGA R1

Besides pins

A12
and
A13
,
DAC0
and
DAC1
can also be accessed via the built-in TRRS 3.5mm jack.
DAC0
is connected to the right channel (tip), while
DAC1
is connected to the left channel (ring) of the input jack as shown in the schematic below:

GIGA R1 3.5mm input jack schematic
GIGA R1 3.5mm input jack schematic

Waveform Generation with the GIGA R1 DACs

Waveform generation is an exciting application used in audio systems, for example, synthesizers, for audio signal generation.

The following example shows how to output an 8kHz square wave on

DAC0
:

1// This example outputs an 8KHz square wave on A12/DAC0.
2#include <Arduino_AdvancedAnalog.h>
3
4AdvancedDAC dac0(A12);
5
6void setup() {
7 Serial.begin(9600);
8
9 while (!Serial) {
10
11 }
12
13 if (!dac0.begin(AN_RESOLUTION_12, 8000, 32, 64)) {
14 Serial.println("Failed to start DAC0 !");
15 while (1);
16 }
17}
18
19void dac_output_sq(AdvancedDAC &dac_out) {
20 if (dac_out.available()) {
21
22 // Get a free buffer for writing.
23 SampleBuffer buf = dac_out.dequeue();
24
25 // Write data to buffer.
26 for (int i=0; i<buf.size(); i++) {
27 buf.data()[i] = (i % 2 == 0) ? 0: 0xfff;
28 }
29
30 // Write the buffer to DAC.
31 dac_out.write(buf);
32 }
33}
34
35void loop() {
36 dac_output_sq(dac0);
37}

DAC Multi Channel Square Waves

It is also possible to simultaneously output at both DAC channels,

DAC0
and
DAC1
. The following example generates an 8kHz square wave on
DAC0
, while a 16kHz square wave is generated on
DAC1
:

1// This example outputs an 8KHz square wave on A12/DAC0 and 16KHz square wave on ADC13/DAC1.
2#include <Arduino_AdvancedAnalog.h>
3
4AdvancedDAC dac0(A12);
5AdvancedDAC dac1(A13);
6
7void setup() {
8 Serial.begin(9600);
9
10 while (!Serial) {
11
12 }
13
14 if (!dac0.begin(AN_RESOLUTION_12, 8000, 32, 64)) {
15 Serial.println("Failed to start DAC0 !");
16 while (1);
17 }
18
19 if (!dac1.begin(AN_RESOLUTION_12, 16000, 32, 64)) {
20 Serial.println("Failed to start DAC1 !");
21 while (1);
22 }
23}
24
25void dac_output_sq(AdvancedDAC &dac_out) {
26 if (dac_out.available()) {
27
28 // Get a free buffer for writing.
29 SampleBuffer buf = dac_out.dequeue();
30
31 // Write data to buffer.
32 for (int i=0; i<buf.size(); i++) {
33 buf.data()[i] = (i % 2 == 0) ? 0: 0xfff;
34 }
35
36 // Write the buffer to DAC.
37 dac_out.write(buf);
38 }
39}
40
41void loop() {
42 dac_output_sq(dac0);
43 dac_output_sq(dac1);
44}

DAC Sine Wave

A 32kHz sine wave output on

DAC0
can be generated using the following example:

1// This example outputs a 32KHz sine wave on A12/DAC1.
2#include <Arduino_AdvancedAnalog.h>
3
4AdvancedDAC dac0(A12);
5
6uint16_t lut[] = {
7 0x0800,0x08c8,0x098f,0x0a52,0x0b0f,0x0bc5,0x0c71,0x0d12,0x0da7,0x0e2e,0x0ea6,0x0f0d,0x0f63,0x0fa7,0x0fd8,0x0ff5,
8 0x0fff,0x0ff5,0x0fd8,0x0fa7,0x0f63,0x0f0d,0x0ea6,0x0e2e,0x0da7,0x0d12,0x0c71,0x0bc5,0x0b0f,0x0a52,0x098f,0x08c8,
9 0x0800,0x0737,0x0670,0x05ad,0x04f0,0x043a,0x038e,0x02ed,0x0258,0x01d1,0x0159,0x00f2,0x009c,0x0058,0x0027,0x000a,
10 0x0000,0x000a,0x0027,0x0058,0x009c,0x00f2,0x0159,0x01d1,0x0258,0x02ed,0x038e,0x043a,0x04f0,0x05ad,0x0670,0x0737
11};
12
13static size_t lut_size = sizeof(lut) / sizeof(lut[0]);
14
15void setup() {
16 Serial.begin(9600);
17 if (!dac0.begin(AN_RESOLUTION_12, 32000 * lut_size, 64, 128)) {
18 Serial.println("Failed to start DAC1 !");
19 while (1);
20 }
21}
22
23void loop() {
24 static size_t lut_offs = 0;
25
26 if (dac0.available()) {
27 // Get a free buffer for writing.
28 SampleBuffer buf = dac0.dequeue();
29
30 // Write data to buffer.
31 for (size_t i=0; i<buf.size(); i++, lut_offs++) {
32 buf[i] = lut[lut_offs % lut_size];
33 }
34
35 // Write the buffer to DAC.
36 dac0.write(buf);
37 }
38}

Waveform Generation Based on Input

The following example allows you to switch between several waveforms via a serial interface. Uploading the example will allow you to change it based on the character input.

  • t
    : generatestriangle wave
  • q
    : square wave
  • s
    : sine wave
  • r
    : sawtooth wave
  • +
    : increase frequency
  • -
    : decrease frequency
1// This example generates different waveforms based on user input on A12/DAC1.
2
3#include <Arduino_AdvancedAnalog.h>
4
5#define N_SAMPLES (256)
6#define DEFAULT_FREQUENCY (16000)
7
8AdvancedDAC dac0(A12);
9uint8_t SAMPLES_BUFFER[N_SAMPLES];
10size_t dac_frequency = DEFAULT_FREQUENCY;
11
12void generate_waveform(int cmd)
13{
14 switch (cmd) {
15 case 't':
16 // Triangle wave
17 Serial.print("Waveform: Triangle ");
18 for (int i=0; i<N_SAMPLES; i++){
19 SAMPLES_BUFFER[i] = abs((i % 255) - 127);
20 }
21 break;
22
23 case 'q':
24 // Square wave
25 Serial.print("Waveform: Square ");
26 for (int i=0; i<N_SAMPLES; i++){
27 SAMPLES_BUFFER[i] = (i % 255) < 127 ? 127 : 0;
28 }
29 break;
30
31 case 's':
32 // Sine wave
33 Serial.print("Waveform: Sine ");
34 for (int i=0; i<N_SAMPLES; i++){
35 SAMPLES_BUFFER[i] = sin(2 * 3.14 * (i / (float) N_SAMPLES)) * 127 + 127;
36 }
37 break;
38
39 case 'r':
40 // Sawtooth
41 Serial.print("Waveform: Sawtooth");
42 for (int i=0; i<N_SAMPLES; i++){
43 SAMPLES_BUFFER[i] = i;
44 }
45 break;
46
47 case '+':
48 case '-':
49 Serial.print("Current frequency: ");
50
51 if (cmd == '+' && dac_frequency < 64000) {
52 dac_frequency *= 2;
53 } else if (cmd == '-' && dac_frequency > 1000) {
54 dac_frequency /= 2;
55 } else {
56 break;
57 }
58
59 dac0.stop();
60 delay(500);
61 if (!dac0.begin(AN_RESOLUTION_8, dac_frequency * N_SAMPLES, N_SAMPLES, 32)) {
62 Serial.println("Failed to start DAC1 !");
63 }
64 delay(500);
65 break;
66
67 default:
68 Serial.print("Unknown command ");
69 Serial.println((char) cmd);
70 return;
71 }
72
73 Serial.print(dac_frequency/1000);
74 Serial.println("KHz");
75}
76
77void setup() {
78 Serial.begin(115200);
79
80 while (!Serial) {
81
82 }
83
84
85 Serial.println("Enter a command:");
86 Serial.println("t: Triangle wave");
87 Serial.println("q: Square wave");
88 Serial.println("s: Sine wave");
89 Serial.println("r: Sawtooth wave");
90 Serial.println("+: Increase frequency");
91 Serial.println("-: Decrease frequency");
92
93 generate_waveform('s');
94
95 // DAC initialization
96 if (!dac0.begin(AN_RESOLUTION_8, DEFAULT_FREQUENCY * N_SAMPLES, N_SAMPLES, 32)) {
97 Serial.println("Failed to start DAC1 !");
98 while (1);
99 }
100}
101
102void loop() {
103 if (Serial.available() > 0) {
104 int cmd = Serial.read();
105 if (cmd != '\n') {
106 generate_waveform(cmd);
107 }
108 }
109
110 if (dac0.available()) {
111 // Get a free buffer for writing.
112 SampleBuffer buf = dac0.dequeue();
113
114 // Write data to buffer.
115 for (size_t i=0; i<buf.size(); i++) {
116 buf[i] = SAMPLES_BUFFER[i];
117 }
118
119 dac0.write(buf);
120 }
121}

Audio Playback

The GIGA R1 12-bit DAC channels can also be used to read

.wav
files from a USB stick and stream them directly to a speaker.

For this example, you will need:

  • A speaker, that has a built in amplifier.
  • A USB mass storage device (USB stick).*
  • Arduino_USBHostMbed5 library installed.

*USB mass storage devices connected needs to be formatted with the FAT32 as a file system, using the MBR partitioning scheme. Read more in the USB Mass Storage section.

USB Stick Configuration

The Arduino_AdvancedAnalog library contains the necessary functions that enable us to use the advanced capabilities of the GIGA R1 DACs.

To read

.wav
files from the USB stick we are using the Arduino_USBHostMbed5 library. It is important that the USB stick is formatted properly, and that we define its name in the sketch. In this case, we name it
USB_DRIVE
, and is defined like this:

1mbed::FATFileSystem usb("USB_DRIVE");

Another line of code defines the audio file to be played. Here we need to write the name of the

.wav
file we want to play, but also the directory (which is
/USB_DRIVE
in this case).

1file = fopen("/USB_DRIVE/AUDIO_SAMPLE.wav", "rb");

Play Single Audio File

The following example plays a single

.wav
file named
AUDIO_SAMPLE.wav
from a USB mass storage device named
USB_DRIVE
.

The file is read, processed and writes the values to a buffer, which is then outputted via the DAC0 channel. The DAC0 channel is connected to the 3.5mm audio jack, and when connecting it to a speaker, it will play the file once.

Note that to start the sketch, you need to open Serial Monitor due to the

while(!Serial)
blocker in the
setup()
. Information on the operation is printed in the monitor, as well as errors if the operation fails.

1/*
2 * GIGA R1 - Audio Playback
3 * Simple wav format audio playback via 12-Bit DAC output by reading from a USB drive.
4 * In order for this sketch to work you need to rename 'USB_DRIVE' to the name of your USB stick drive.
5 * Furthermore you need to store the provided audio file AUDIO_SAMPLE.wav on it.
6*/
7
8#include <Arduino_AdvancedAnalog.h>
9#include <DigitalOut.h>
10#include <Arduino_USBHostMbed5.h>
11#include <FATFileSystem.h>
12
13AdvancedDAC dac0(A12);
14
15USBHostMSD msd;
16mbed::FATFileSystem usb("USB_DRIVE");
17
18FILE * file = nullptr;
19int sample_size = 0;
20int samples_count = 0;
21
22
23void setup()
24{
25 Serial.begin(115200);
26 while (!Serial);
27
28 /* Enable power for HOST USB connector. */
29 pinMode(PA_15, OUTPUT);
30 digitalWrite(PA_15, HIGH);
31
32 Serial.println("Please connect a USB stick to the GIGA's USB port ...");
33 while (!msd.connect()) delay(100);
34
35 Serial.println("Mounting USB device ...");
36 int const rc_mount = usb.mount(&msd);
37 if (rc_mount)
38 {
39 Serial.print("Error mounting USB device ");
40 Serial.println(rc_mount);
41 return;
42 }
43
44 Serial.println("Opening audio file ...");
45
46 /* 16-bit PCM Mono 16kHz realigned noise reduction */
47 file = fopen("/USB_DRIVE/AUDIO_SAMPLE.wav", "rb");
48 if (file == nullptr)
49 {
50 Serial.print("Error opening audio file: ");
51 Serial.println(strerror(errno));
52 return;
53 }
54
55 Serial.println("Reading audio header ...");
56 struct wav_header_t
57 {
58 char chunkID[4]; //"RIFF" = 0x46464952
59 unsigned long chunkSize; //28 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes] + sum(sizeof(chunk.id) + sizeof(chunk.size) + chunk.size)
60 char format[4]; //"WAVE" = 0x45564157
61 char subchunk1ID[4]; //"fmt " = 0x20746D66
62 unsigned long subchunk1Size; //16 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes]
63 unsigned short audioFormat;
64 unsigned short numChannels;
65 unsigned long sampleRate;
66 unsigned long byteRate;
67 unsigned short blockAlign;
68 unsigned short bitsPerSample;
69 };
70
71 wav_header_t header;
72 fread(&header, sizeof(header), 1, file);
73
74 Serial.println("WAV File Header read:");
75 char msg[64] = {0};
76 snprintf(msg, sizeof(msg), "File Type: %s", header.chunkID);
77 Serial.println(msg);
78 snprintf(msg, sizeof(msg), "File Size: %ld", header.chunkSize);
79 Serial.println(msg);
80 snprintf(msg, sizeof(msg), "WAV Marker: %s", header.format);
81 Serial.println(msg);
82 snprintf(msg, sizeof(msg), "Format Name: %s", header.subchunk1ID);
83 Serial.println(msg);
84 snprintf(msg, sizeof(msg), "Format Length: %ld", header.subchunk1Size);
85 Serial.println(msg);
86 snprintf(msg, sizeof(msg), "Format Type: %hd", header.audioFormat);
87 Serial.println(msg);
88 snprintf(msg, sizeof(msg), "Number of Channels: %hd", header.numChannels);
89 Serial.println(msg);
90 snprintf(msg, sizeof(msg), "Sample Rate: %ld", header.sampleRate);
91 Serial.println(msg);
92 snprintf(msg, sizeof(msg), "Sample Rate * Bits/Sample * Channels / 8: %ld", header.byteRate);
93 Serial.println(msg);
94 snprintf(msg, sizeof(msg), "Bits per Sample * Channels / 8: %hd", header.blockAlign);
95 Serial.println(msg);
96 snprintf(msg, sizeof(msg), "Bits per Sample: %hd", header.bitsPerSample);
97 Serial.println(msg);
98
99 /* Find the data section of the WAV file. */
100 struct chunk_t
101 {
102 char ID[4];
103 unsigned long size;
104 };
105
106 chunk_t chunk;
107 snprintf(msg, sizeof(msg), "id\t" "size");
108 Serial.println(msg);
109 /* Find data chunk. */
110 while (true)
111 {
112 fread(&chunk, sizeof(chunk), 1, file);
113 snprintf(msg, sizeof(msg), "%c%c%c%c\t" "%li", chunk.ID[0], chunk.ID[1], chunk.ID[2], chunk.ID[3], chunk.size);
114 Serial.println(msg);
115 if (*(unsigned int *) &chunk.ID == 0x61746164)
116 break;
117 /* Skip chunk data bytes. */
118 fseek(file, chunk.size, SEEK_CUR);
119 }
120
121 /* Determine number of samples. */
122 sample_size = header.bitsPerSample / 8;
123 samples_count = chunk.size * 8 / header.bitsPerSample;
124 snprintf(msg, sizeof(msg), "Sample size = %i", sample_size); Serial.println(msg);
125 snprintf(msg, sizeof(msg), "Samples count = %i", samples_count); Serial.println(msg);
126
127 /* Configure the advanced DAC. */
128 if (!dac0.begin(AN_RESOLUTION_12, header.sampleRate * 2, 256, 16))
129 {
130 Serial.println("Failed to start DAC1 !");
131 return;
132 }
133}
134
135void loop()
136{
137 if (dac0.available() && !feof(file))
138 {
139 /* Read data from file. */
140 uint16_t sample_data[256] = {0};
141 fread(sample_data, sample_size, 256, file);
142
143 /* Get a free buffer for writing. */
144 SampleBuffer buf = dac0.dequeue();
145
146 /* Write data to buffer. */
147 for (size_t i = 0; i < buf.size(); i++)
148 {
149 /* Scale down to 12 bit. */
150 uint16_t const dac_val = ((static_cast<unsigned int>(sample_data[i])+32768)>>4) & 0x0fff;
151 buf[i] = dac_val;
152 }
153
154 /* Write the buffer to DAC. */
155 dac0.write(buf);
156 }
157}

Loop Multiple Audio Files

This example is similar to the Play Single Audio File example, but with some key changes:

  • This example uses multiple audio files.
  • The file read is moved to a separate function,
    configFile()
    , as it will be continuously called from the sketch.
  • Instead of playing a file once, it keeps looping it.
  • The BOOT0 (
    PC_13
    ) button (right next to the audio jack) is used as a regular pushbutton to loop through the audio files.
  • Pressing the button changes the file played.

The files used are called

DRUM.wav
,
WARP.wav
,
BASS.wav
and
SHAKE.wav
. They are very short (around 1 second). These needs to be present on the USB stick used.

You can download them from this link.

1/*
2 * GIGA R1 - Audio Playback
3 * Simple wav format audio playback via 12-Bit DAC output by reading from a USB drive.
4 * In order for this sketch to work you need to rename 'USB_DRIVE' to the name of your USB stick drive.
5 * Furthermore you need to store the provided audio file AUDIO_SAMPLE.wav on it.
6*/
7
8#include <Arduino_AdvancedAnalog.h>
9#include <DigitalOut.h>
10#include <Arduino_USBHostMbed5.h>
11#include <FATFileSystem.h>
12
13AdvancedDAC dac0(A12);
14
15USBHostMSD msd;
16mbed::FATFileSystem usb("usb");
17
18FILE *file = nullptr;
19int sample_size = 0;
20int samples_count = 0;
21
22int swapFile;
23
24void setup() {
25 Serial.begin(115200);
26 while (!Serial)
27 ;
28
29 /* Enable power for HOST USB connector. */
30
31 pinMode(PA_15, OUTPUT);
32 digitalWrite(PA_15, HIGH);
33
34 Serial.println("Please connect a USB stick to the GIGA's USB port ...");
35 while (!msd.connect()) delay(100);
36
37 Serial.println("Mounting USB device ...");
38 int const rc_mount = usb.mount(&msd);
39 if (rc_mount) {
40 Serial.print("Error mounting USB device ");
41 Serial.println(rc_mount);
42 return;
43 }
44 configFile();
45}
46
47void loop() {
48 if (dac0.available() && !feof(file)) {
49 /* Read data from file. */
50 uint16_t sample_data[256] = { 0 };
51 fread(sample_data, sample_size, 256, file);
52
53 /* Get a free buffer for writing. */
54 SampleBuffer buf = dac0.dequeue();
55
56 /* Write data to buffer. */
57 for (size_t i = 0; i < buf.size(); i++) {
58 /* Scale down to 12 bit. */
59 uint16_t const dac_val = ((static_cast<unsigned int>(sample_data[i]) + 32768) >> 4) & 0x0fff;
60 buf[i] = dac_val;
61 }
62
63 /* Write the buffer to DAC. */
64 dac0.write(buf);
65
66 if(feof(file)){
67 fclose(file);
68 configFile();
69 }
70 }
71
72 int buttonState = digitalRead(PC_13);
73
74 if (buttonState == 1) {
75 swapFile = swapFile + 1;
76 if (swapFile == 4) {
77 swapFile = 0;
78 }
79 delay(500);
80 configFile();
81 }
82}
83
84
85void configFile() {
86 Serial.println("Opening audio file ...");
87
88 /* 16-bit PCM Mono 16kHz realigned noise reduction */
89 if (swapFile == 0) {
90 file = fopen("/usb/DRUM.wav", "rb");
91 } else if (swapFile == 1) {
92 file = fopen("/usb/WARP.wav", "rb");
93 } else if (swapFile == 2) {
94 file = fopen("/usb/BASS.wav", "rb");
95 } else if (swapFile == 3) {
96 file = fopen("/usb/SHAKE.wav", "rb");
97 }
98
99 if (file == nullptr) {
100 Serial.print("Error opening audio file: ");
101 Serial.println(strerror(errno));
102 return;
103 }
104
105 Serial.println("Reading audio header ...");
106 struct wav_header_t {
107 char chunkID[4]; //"RIFF" = 0x46464952
108 unsigned long chunkSize; //28 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes] + sum(sizeof(chunk.id) + sizeof(chunk.size) + chunk.size)
109 char format[4]; //"WAVE" = 0x45564157
110 char subchunk1ID[4]; //"fmt " = 0x20746D66
111 unsigned long subchunk1Size; //16 [+ sizeof(wExtraFormatBytes) + wExtraFormatBytes]
112 unsigned short audioFormat;
113 unsigned short numChannels;
114 unsigned long sampleRate;
115 unsigned long byteRate;
116 unsigned short blockAlign;
117 unsigned short bitsPerSample;
118 };
119
120 wav_header_t header;
121 fread(&header, sizeof(header), 1, file);
122
123 Serial.println("WAV File Header read:");
124 char msg[64] = { 0 };
125 snprintf(msg, sizeof(msg), "File Type: %s", header.chunkID);
126 Serial.println(msg);
127 snprintf(msg, sizeof(msg), "File Size: %ld", header.chunkSize);
128 Serial.println(msg);
129 snprintf(msg, sizeof(msg), "WAV Marker: %s", header.format);
130 Serial.println(msg);
131 snprintf(msg, sizeof(msg), "Format Name: %s", header.subchunk1ID);
132 Serial.println(msg);
133 snprintf(msg, sizeof(msg), "Format Length: %ld", header.subchunk1Size);
134 Serial.println(msg);
135 snprintf(msg, sizeof(msg), "Format Type: %hd", header.audioFormat);
136 Serial.println(msg);
137 snprintf(msg, sizeof(msg), "Number of Channels: %hd", header.numChannels);
138 Serial.println(msg);
139 snprintf(msg, sizeof(msg), "Sample Rate: %ld", header.sampleRate);
140 Serial.println(msg);
141 snprintf(msg, sizeof(msg), "Sample Rate * Bits/Sample * Channels / 8: %ld", header.byteRate);
142 Serial.println(msg);
143 snprintf(msg, sizeof(msg), "Bits per Sample * Channels / 8: %hd", header.blockAlign);
144 Serial.println(msg);
145 snprintf(msg, sizeof(msg), "Bits per Sample: %hd", header.bitsPerSample);
146 Serial.println(msg);
147
148 /* Find the data section of the WAV file. */
149 struct chunk_t {
150 char ID[4];
151 unsigned long size;
152 };
153
154 chunk_t chunk;
155 snprintf(msg, sizeof(msg), "id\t"
156 "size");
157 Serial.println(msg);
158 /* Find data chunk. */
159 while (true) {
160 fread(&chunk, sizeof(chunk), 1, file);
161 snprintf(msg, sizeof(msg), "%c%c%c%c\t"
162 "%li",
163 chunk.ID[0], chunk.ID[1], chunk.ID[2], chunk.ID[3], chunk.size);
164 Serial.println(msg);
165 if (*(unsigned int *)&chunk.ID == 0x61746164)
166 break;
167 /* Skip chunk data bytes. */
168 fseek(file, chunk.size, SEEK_CUR);
169 }
170
171 /* Determine number of samples. */
172 sample_size = header.bitsPerSample / 8;
173 samples_count = chunk.size * 8 / header.bitsPerSample;
174 snprintf(msg, sizeof(msg), "Sample size = %i", sample_size);
175 Serial.println(msg);
176 snprintf(msg, sizeof(msg), "Samples count = %i", samples_count);
177 Serial.println(msg);
178
179 /* Configure the advanced DAC. */
180 if (!dac0.begin(AN_RESOLUTION_12, header.sampleRate, 256, 16)) {
181 Serial.println("Failed to start DAC1 !");
182 return;
183 }
184}

Pulse Density Modulation Support

Pulse Density Support (PDM) is a form of modulation used to represent analog information into digital information; PDM uses a high-frequency stream of 1-bit digital samples. In PDM, a large cluster of ones represents a positive amplitude, while a large cluster of zeros represents a negative amplitude.

The GIGA R1 PDM support can be used with the built-in PDM library. Let's check an interesting example that shows of how to read a PDM microphone wwith the GIGA R1:

1#include <PDM.h>
2
3// Default number of output channels
4static const char channels = 1;
5
6// Default PCM output frequency
7static const int frequency = 16000;
8
9// Buffer to read samples into, each sample is 16-bits
10short sampleBuffer[128];
11
12// Number of audio samples read
13volatile int samplesRead;
14
15void setup() {
16 Serial.begin(9600);
17 while (!Serial);
18
19 // Configure the callback function and gain
20 PDM.onReceive(onPDMdata);
21 PDM.setGain(30);
22
23 // Initialize PDM microphone in mono mode, and a 16 kHz sample rate:
24 if (!PDM.begin(channels, frequency)) {
25 Serial.println("Failed to start PDM!");
26 while (1);
27 }
28}
29
30void loop() {
31 // Wait for samples to be read
32 if (samplesRead) {
33
34 // Print samples to the Serial Monitor
35 for (int i = 0; i < samplesRead; i++) {
36 if(channels == 2) {
37 Serial.print("L:");
38 Serial.print(sampleBuffer[i]);
39 Serial.print(" R:");
40 i++;
41 }
42 Serial.println(sampleBuffer[i]);
43 }
44
45 samplesRead = 0;
46 }
47}
48
49// Callback function
50void onPDMdata() {
51 int bytesAvailable = PDM.available();
52 PDM.read(sampleBuffer, bytesAvailable);
53 samplesRead = bytesAvailable / 2;
54}

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.