Skip to content

Commit 7768a66

Browse files
committed
Added IKS02A1_Audio_Button example
1 parent f45085e commit 7768a66

File tree

6 files changed

+342
-4
lines changed

6 files changed

+342
-4
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ with a X-NUCLEO-IKS01A3 and one among X-NUCLEO-S2868A1, X-NUCLEO-S2868A2 and X-N
3030
provided by the X-NUCLEO-IKS01A3 between two NUCLEO boards connected each other through S2-LP based Expansion Boards; in order to send the sensor data
3131
you just need to push the button of the NUCLEO board; in order to see the received sensor data you just need to open a hyperterminal connected to
3232
the Virtual COM port of the Nucleo board; the LED of the NUCLEO board will blink after every successful transmission or reception.
33+
* X_NUCLEO_IKS02A1_Audio_Button: This application provides a simple example of recording audio from a X-NUCLEO-IKS02A1, converting from PDM to PCM, then saving it as a wave file to an SD Card.
3334

3435
## Dependencies
3536

@@ -58,7 +59,7 @@ The FP_Examples library requires the following STM32duino libraries (link to the
5859
* STM32duino STTS751: https://github.com/stm32duino/STTS751
5960
* STM32duino S2-LP: https://github.com/stm32duino/S2-LP
6061
* STM32duino M95640-R: https://github.com/stm32duino/M95640-R
61-
62+
* STM32duino X-NUCLEO-IKS02A1 Audio: https://github.com/stm32duino/X-NUCLEO-IKS02A1-Audio
6263

6364
## Documentation
6465

@@ -82,7 +83,7 @@ The datasheets of the several components are available at
8283
* https://www.st.com/content/st_com/en/products/mems-and-sensors/temperature-sensors/stts751.html
8384
* https://www.st.com/content/st_com/en/products/wireless-transceivers-mcus-and-modules/sub-1ghz-rf/s2-lp.html
8485
* https://www.st.com/content/st_com/en/products/memories/serial-eeprom/standard-serial-eeprom/standard-spi-eeprom/m95640-r.html
85-
86+
* https://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-sense-hw/x-nucleo-iks02a1.html
8687

8788

8889

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/* Includes ------------------------------------------------------------------------*/
2+
#include <WaveEncoder.h>
3+
#include <String.h>
4+
5+
/* Class Implementation -----------------------------------------------------------------------------*/
6+
/* Constructor */
7+
WaveClass::WaveClass()
8+
{
9+
}
10+
11+
/* Function */
12+
13+
/**
14+
* @brief Initialize header
15+
* @param pHeader pointer to a buffer with the parameter of the header
16+
* @param SampleRate value of the sample rate of the sensor
17+
* @param BitsPerSample value of the bits per sample of the sensor
18+
*/
19+
void WaveClass::header_init(uint8_t *pHeader, uint32_t SampleRate, uint32_t BitsPerSample)
20+
{
21+
/* Write chunkID, must be 'RIFF' ------------------------------------------*/
22+
pHeader[0] = 'R';
23+
pHeader[1] = 'I';
24+
pHeader[2] = 'F';
25+
pHeader[3] = 'F';
26+
27+
/* Write the file length ---------------------------------------------------
28+
/* The sampling time: this value will be be written back at the end of the
29+
* recording opearation.
30+
* Example: 44 Bytes = 0x000A17FC, byte[7]=0x00, byte[4]=0xFC
31+
*/
32+
pHeader[4] = 0x2C;
33+
pHeader[5] = 0x00;
34+
pHeader[6] = 0x00;
35+
pHeader[7] = 0x00;
36+
37+
/* Write the file format, must be 'WAVE' -----------------------------------*/
38+
pHeader[8] = 'W';
39+
pHeader[9] = 'A';
40+
pHeader[10] = 'V';
41+
pHeader[11] = 'E';
42+
43+
/* Write the format chunk, must be'fmt ' -----------------------------------*/
44+
pHeader[12] = 'f';
45+
pHeader[13] = 'm';
46+
pHeader[14] = 't';
47+
pHeader[15] = ' ';
48+
49+
/* Write the length of the 'fmt' data, must be 0x10 ------------------------*/
50+
pHeader[16] = 0x10;
51+
pHeader[17] = 0x00;
52+
pHeader[18] = 0x00;
53+
pHeader[19] = 0x00;
54+
55+
/* Write the audio format, must be 0x01 (PCM) ------------------------------*/
56+
pHeader[20] = 0x01;
57+
pHeader[21] = 0x00;
58+
59+
/* Write the number of channels, ie. 0x01 (Mono) ---------------------------*/
60+
pHeader[22] = 0x01;
61+
pHeader[23] = 0x00;
62+
63+
/* Write the Sample Rate in Hz ---------------------------------------------*/
64+
/* Write Little Endian ie. 8000 = 0x00001F40 => byte[24]=0x40, byte[27]=0x00 */
65+
66+
pHeader[24] = (uint8_t)((SampleRate & 0xFF));
67+
pHeader[25] = (uint8_t)((SampleRate >> 8) & 0xFF);
68+
pHeader[26] = (uint8_t)((SampleRate >> 16) & 0xFF);
69+
pHeader[27] = (uint8_t)((SampleRate >> 24) & 0xFF);
70+
71+
/* Write the Byte Rate // SampleRate * (BitPerSample/8) -----------------------------------------------------*/
72+
73+
uint32_t ByteRate = SampleRate * (BitsPerSample / 8);
74+
pHeader[28] = (uint8_t)((ByteRate & 0xFF));
75+
pHeader[29] = (uint8_t)((ByteRate >> 8) & 0xFF);
76+
pHeader[30] = (uint8_t)((ByteRate >> 16) & 0xFF);
77+
pHeader[31] = (uint8_t)((ByteRate >> 24) & 0xFF);
78+
79+
/* Write the block alignment -----------------------------------------------*/
80+
uint32_t BlockAlign = (BitsPerSample / 8);
81+
pHeader[32] = BlockAlign; //NbrChannels * (BitPerSample/8)
82+
pHeader[33] = 0x00;
83+
84+
/* Write the number of bits per sample (16)-------------------------------------*/
85+
pHeader[34] = BitsPerSample;
86+
pHeader[35] = 0x00;
87+
88+
/* Write the Data chunk, must be 'data' ------------------------------------*/
89+
pHeader[36] = 'd';
90+
pHeader[37] = 'a';
91+
pHeader[38] = 't';
92+
pHeader[39] = 'a';
93+
94+
/* Write the number of sample data NumSamples * Ch * (BitsPerSample/8)-----------------------------------------*/
95+
/* This variable will be written back at the end of the recording operation -> 0 */
96+
pHeader[40] = 0x00;
97+
pHeader[41] = 0x00;
98+
pHeader[42] = 0x00;
99+
pHeader[43] = 0x00;
100+
}
101+
102+
/**
103+
* @brief Update header
104+
* @param pHeader pointer to a buffer with the parameter of the header
105+
* @param byteswritten pointer to the value of the bytes written
106+
*/
107+
void WaveClass::header_update(uint8_t *pHeader, uint32_t *byteswritten)
108+
{
109+
/* Write the file length ----------------------------------------------------*/
110+
/* The sampling time: this value will be be written back at the end of the
111+
recording opearation. Example: 661500 Btyes = 0x000A17FC, byte[7]=0x00, byte[4]=0xFC */
112+
pHeader[4] = (uint8_t) * byteswritten; //<-- man mano che scrivo salva qua dentro i byte scritti
113+
pHeader[5] = (uint8_t)(*byteswritten >> 8);
114+
pHeader[6] = (uint8_t)(*byteswritten >> 16);
115+
pHeader[7] = (uint8_t)(*byteswritten >> 24);
116+
/* Write the number of sample data -----------------------------------------*/
117+
/* This variable will be written back at the end of the recording operation */
118+
pHeader[40] = (uint8_t)(*byteswritten - 44);
119+
pHeader[41] = (uint8_t)((*byteswritten - 44) >> 8);
120+
pHeader[42] = (uint8_t)((*byteswritten - 44) >> 16);
121+
pHeader[43] = (uint8_t)((*byteswritten - 44) >> 24);
122+
123+
}
124+
125+
WaveClass WAV;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/* Define to prevent recursive inclusion -------------------------------------*/
2+
#ifndef __WAVE_H__
3+
#define __WAVE_H__
4+
5+
/* Includes --------------------------------------------------------------------------------------*/
6+
#include <Arduino.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
10+
typedef enum {
11+
WAV_OK = 0,
12+
WAV_ERROR = -1
13+
} WAVStatus;
14+
15+
/* Class Declaration -----------------------------------------------------------------------------*/
16+
class WaveClass {
17+
public:
18+
/* Constructor */
19+
WaveClass();
20+
21+
/* Function */
22+
void header_init(uint8_t *pHeader, uint32_t SampleRate, uint32_t BitsPerSample);
23+
void header_update(uint8_t *pHeader, uint32_t *byteswritten);
24+
25+
};
26+
27+
extern WaveClass WAV;
28+
#endif
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/* This example was developed for Nucleo F401RE or L476RG only. If you want to use other Nucleo or Architecture
2+
* you have to implement the dedicated file to manage the low level PDM part
3+
*/
4+
#if !defined(ARDUINO_NUCLEO_L476RG) && !defined(ARDUINO_NUCLEO_F401RE)
5+
#error "This example is only for STM32 Nucleo-F401RE or Nucleo-L476RG!"
6+
#endif
7+
8+
#include "Arduino.h"
9+
#include "app_config.h"
10+
#include "PDM.h"
11+
#include "pdm2pcm.h"
12+
#include "WaveEncoder.h"
13+
#include "SD.h"
14+
#include <CMSIS_DSP.h>
15+
16+
/* PDM */
17+
uint16_t sampleBuffer[(((AUDIO_IN_FREQ / 8) * MAX_AUDIO_IN_CHANNEL_NBR_PER_IF * N_MS_PER_INTERRUPT)/2)] = {0};
18+
int count = 1;
19+
String file_name = "";
20+
21+
/* Button */
22+
bool is_start = false; /* true means start, false means stop */
23+
volatile bool button = false;
24+
25+
#ifdef USER_BTN
26+
const int buttonPin = USER_BTN;
27+
#else
28+
const int buttonPin = 5;
29+
#endif
30+
31+
int PushButtonState = LOW;
32+
33+
/* PDM2PCM */
34+
#define PCM_BUFLEN (AUDIO_IN_FREQ/AUDIO_IN_DECIMATOR_FACTOR) /* AUDIO_IN_FREQ = 1280, AUDIO_IN_DECIMATOR_FACTOR = 80 so PCM_BUFLEN = 1280/80 = 16*/
35+
int16_t pcmSamples[PCM_BUFLEN*N_MS_PER_INTERRUPT];
36+
37+
/* SD Card */
38+
File myFile;
39+
uint8_t pHeader[44];
40+
uint32_t byteswritten = 44;
41+
42+
void setup() {
43+
/* Serial */
44+
Serial.begin(115200);
45+
46+
/* Button Event Setting */
47+
pinMode(buttonPin,INPUT_PULLUP);
48+
attachInterrupt(digitalPinToInterrupt(buttonPin), play_stop, FALLING);
49+
50+
/* Check what is the Push Button State when the button is not pressed. It can change across families */
51+
PushButtonState = (digitalRead(buttonPin)) ? LOW : HIGH;
52+
53+
/* SD Card Initialization */
54+
if (!SD.begin(12000000, 10)) {
55+
Serial.println("Failed to inizialize SD!\n");
56+
while(1);
57+
}
58+
59+
/* Enable and Start */
60+
if (PDM.Begin() != PDM_OK)
61+
{
62+
Serial.println("Failed to start PDM!\n");
63+
}
64+
else
65+
{
66+
Serial.println("PDM correctly initialized!\n");
67+
}
68+
69+
/* Function to do with data */
70+
PDM.onReceive(foo);
71+
72+
/* Initialize PDM2PCM */
73+
pdm2pcm_init(BYTE_LEFT_MSB, PDM_ENDIANNESS_BE, SINC4);
74+
if(pdm2pcm_volume(5)) //set volume (0-6)
75+
Serial.println("Volume not correct!\n");
76+
}
77+
78+
void loop() {
79+
if(button){
80+
/* Debouncing */
81+
delay(50);
82+
83+
/* Wait until the button is released */
84+
while ((digitalRead(buttonPin) == PushButtonState));
85+
86+
/* Debouncing */
87+
delay(50);
88+
89+
if(!is_start){
90+
91+
Serial.println("Setting File");
92+
93+
/*Inizialization File */
94+
file_name = "sample_" + String(count++) + ".wav";
95+
byteswritten = 44;
96+
97+
/*Inizialization File */
98+
if (SD.exists(file_name)) {
99+
SD.remove(file_name);
100+
Serial.println("File Already Exists - File Removed");
101+
}
102+
103+
if (!(myFile = SD.open(file_name, FILE_WRITE))) {
104+
Serial.println("Failed to Inizialize File\n");
105+
while(1);
106+
}
107+
108+
WAV.header_init(pHeader, 16000, 16);
109+
110+
myFile.write(pHeader, 44);
111+
myFile.close();
112+
Serial.flush();
113+
114+
is_start = true;
115+
Serial.println("RECORDING");
116+
PDM.Record(sampleBuffer);
117+
118+
}else{
119+
is_start = false;
120+
if (PDM.Stop() == PDM_ERROR)
121+
{
122+
Serial.println("PDM_ERROR");
123+
}
124+
Serial.println("STOP");
125+
126+
/* Update and Writing header */
127+
if (!(myFile = SD.open(file_name, O_WRITE))) {
128+
Serial.println("Failed to Open File\n");
129+
while(1);
130+
}
131+
132+
WAV.header_update(pHeader, &byteswritten);
133+
if (!myFile.seek(0)) {
134+
Serial.println("Failed to Update Header\n");
135+
while(1);
136+
}
137+
myFile.write(pHeader, 44);
138+
myFile.close();
139+
140+
}
141+
button = false;
142+
}
143+
}
144+
145+
146+
extern "C" void foo()
147+
{
148+
for (uint32_t i = 0; i < N_MS_PER_INTERRUPT; i++)
149+
{
150+
pdm2pcm((uint8_t *) & (sampleBuffer[i * ((BLOCK_SIZE/8)/2)]), &pcmSamples[i * PCM_BUFLEN], BLOCK_SIZE);
151+
}
152+
153+
/* Writing on SD */
154+
myFile = SD.open(file_name, O_WRITE);
155+
myFile.seek(byteswritten);
156+
for (uint32_t i = 0; i < PCM_BUFLEN * N_MS_PER_INTERRUPT; i++)
157+
{
158+
uint16_t data = (uint16_t)pcmSamples[i];
159+
160+
myFile.write((uint8_t)data);
161+
myFile.write((uint8_t)(data>>8));
162+
163+
}
164+
165+
byteswritten += PCM_BUFLEN * N_MS_PER_INTERRUPT * sizeof(int16_t);
166+
myFile.flush();
167+
myFile.close();
168+
}
169+
170+
void play_stop()
171+
{
172+
button = true;
173+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef __APP_CONFIG_H__
2+
#define __APP_CONFIG_H__
3+
4+
/* Override the settings inside the library of the number of milliseconds per interrupt*/
5+
#ifndef N_MS_PER_INTERRUPT
6+
7+
#define N_MS_PER_INTERRUPT (100)
8+
9+
#endif
10+
11+
#endif /* __APP_CONFIG_H__ */

library.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
name=STM32duino FP_Examples
2-
version=1.4.0
2+
version=1.5.0
33
author=STMicroelectronics
44
maintainer=stm32duino
55
sentence=Provides several Function Packs that combine the usage of several X-NUCLEO boards
66
paragraph=This library provides several Function Packs that combine the usage of several X-NUCLEO boards together with a NUCLEO board
77
category=Other
88
url=https://github.com/stm32duino/FP_Examples
99
architectures=stm32
10-
depends=STM32duino LSM6DSL, STM32duino LSM303AGR, STM32duino LPS22HB, STM32duino VL6180X, STM32duino VL53L0X, STM32duino VL53L1X, STM32duino Proximity Gesture, STM32duino X-NUCLEO-6180XA1, STM32duino X-NUCLEO-53L0A1, STM32duino X-NUCLEO-53L1A1, STM32duino SPBTLE-RF, STM32duino X-NUCLEO-IHM02A1, STM32duino X-NUCLEO-LED61A1, STM32duino LPS25HB, STM32duino LSM6DS3, STM32duino LSM6DSO, STM32duino LIS2DW12, STM32duino LIS2MDL, STM32duino HTS221, STM32duino LPS22HH, STM32duino STTS751, STM32duino S2-LP, STM32duino M95640-R
10+
depends=STM32duino LSM6DSL, STM32duino LSM303AGR, STM32duino LPS22HB, STM32duino VL6180X, STM32duino VL53L0X, STM32duino VL53L1X, STM32duino Proximity Gesture, STM32duino X-NUCLEO-6180XA1, STM32duino X-NUCLEO-53L0A1, STM32duino X-NUCLEO-53L1A1, STM32duino SPBTLE-RF, STM32duino X-NUCLEO-IHM02A1, STM32duino X-NUCLEO-LED61A1, STM32duino LPS25HB, STM32duino LSM6DS3, STM32duino LSM6DSO, STM32duino LIS2DW12, STM32duino LIS2MDL, STM32duino HTS221, STM32duino LPS22HH, STM32duino STTS751, STM32duino S2-LP, STM32duino M95640-R, STM32duino X-NUCLEO-IKS02A1 Audio

0 commit comments

Comments
 (0)