Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aac8146f00 | ||
|
|
35c974d4c0 | ||
|
|
27476815ab | ||
|
|
b02b9c4be8 |
@@ -161,7 +161,7 @@ void SerialUART::flush() {
|
||||
if (!_running || !m) {
|
||||
return;
|
||||
}
|
||||
uart_default_tx_wait_blocking();
|
||||
uart_tx_wait_blocking(_uart);
|
||||
}
|
||||
|
||||
size_t SerialUART::write(uint8_t c) {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
This example reads audio data from the on-board PDM microphones, and prints
|
||||
out the samples to the Serial console. The Serial Plotter built into the
|
||||
Arduino IDE can be used to plot the audio data (Tools -> Serial Plotter)
|
||||
|
||||
Circuit:
|
||||
- Arduino Nano 33 BLE board, or
|
||||
- Arduino Nano RP2040 Connect, or
|
||||
- Arduino Portenta H7 board plus Portenta Vision Shield
|
||||
|
||||
This example code is in the public domain.
|
||||
*/
|
||||
|
||||
#include <PDM.h>
|
||||
|
||||
// default number of output channels
|
||||
static const char channels = 1;
|
||||
|
||||
// default PCM output frequency
|
||||
static const int frequency = 16000;
|
||||
|
||||
// Buffer to read samples into, each sample is 16-bits
|
||||
short sampleBuffer[512];
|
||||
|
||||
// Number of audio samples read
|
||||
volatile int samplesRead;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
while (!Serial);
|
||||
|
||||
// Configure the data receive callback
|
||||
PDM.onReceive(onPDMdata);
|
||||
|
||||
// Optionally set the gain
|
||||
// Defaults to 20 on the BLE Sense and -10 on the Portenta Vision Shield
|
||||
// PDM.setGain(30);
|
||||
|
||||
// Initialize PDM with:
|
||||
// - one channel (mono mode)
|
||||
// - a 16 kHz sample rate for the Arduino Nano 33 BLE Sense
|
||||
// - a 32 kHz or 64 kHz sample rate for the Arduino Portenta Vision Shield
|
||||
if (!PDM.begin(channels, frequency)) {
|
||||
Serial.println("Failed to start PDM!");
|
||||
while (1);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Wait for samples to be read
|
||||
if (samplesRead) {
|
||||
|
||||
// Print samples to the serial monitor or plotter
|
||||
for (int i = 0; i < samplesRead; i++) {
|
||||
if(channels == 2) {
|
||||
Serial.print("L:");
|
||||
Serial.print(sampleBuffer[i]);
|
||||
Serial.print(" R:");
|
||||
i++;
|
||||
}
|
||||
Serial.println(sampleBuffer[i]);
|
||||
}
|
||||
|
||||
// Clear the read count
|
||||
samplesRead = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function to process the data from the PDM microphone.
|
||||
* NOTE: This callback is executed as part of an ISR.
|
||||
* Therefore using `Serial` to print messages inside this function isn't supported.
|
||||
* */
|
||||
void onPDMdata() {
|
||||
// Query the number of available bytes
|
||||
int bytesAvailable = PDM.available();
|
||||
|
||||
// Read into the sample buffer
|
||||
PDM.read(sampleBuffer, bytesAvailable);
|
||||
|
||||
// 16-bit, 2 bytes per sample
|
||||
samplesRead = bytesAvailable / 2;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map PDM
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
PDM KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
begin KEYWORD2
|
||||
end KEYWORD2
|
||||
|
||||
available KEYWORD2
|
||||
read KEYWORD2
|
||||
|
||||
onReceive KEYWORD2
|
||||
|
||||
setGain KEYWORD2
|
||||
setBufferSize KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,9 @@
|
||||
name=PDM
|
||||
version=1.0
|
||||
author=Arduino
|
||||
maintainer=Arduino <info@arduino.cc>
|
||||
sentence=Enables the communication with devices that use the PDM Bus. Specific implementation for nRF52.
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=
|
||||
architectures=rp2040
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Copyright (c) 2019 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _PDM_H_INCLUDED
|
||||
#define _PDM_H_INCLUDED
|
||||
|
||||
#include <Arduino.h>
|
||||
//#include <pinDefinitions.h>
|
||||
|
||||
#include "utility/PDMDoubleBuffer.h"
|
||||
|
||||
class PDMClass
|
||||
{
|
||||
public:
|
||||
PDMClass(int dinPin, int clkPin, int pwrPin);
|
||||
virtual ~PDMClass();
|
||||
|
||||
int begin(int channels, int sampleRate);
|
||||
void end();
|
||||
|
||||
virtual int available();
|
||||
virtual int read(void* buffer, size_t size);
|
||||
|
||||
void onReceive(void(*)(void));
|
||||
|
||||
//PORTENTA_H7 min -12 max 51
|
||||
//NANO 33 BLE SENSe min 0 max 80
|
||||
void setGain(int gain);
|
||||
void setBufferSize(int bufferSize);
|
||||
size_t getBufferSize();
|
||||
|
||||
// private:
|
||||
void IrqHandler(bool halftranfer);
|
||||
|
||||
private:
|
||||
int _dinPin;
|
||||
int _clkPin;
|
||||
int _pwrPin;
|
||||
|
||||
int _channels;
|
||||
int _samplerate;
|
||||
|
||||
int _gain;
|
||||
int _init;
|
||||
|
||||
PDMDoubleBuffer _doubleBuffer;
|
||||
|
||||
void (*_onReceive)(void);
|
||||
};
|
||||
#ifdef PIN_PDM_DIN
|
||||
extern PDMClass PDM;
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
*******************************************************************************
|
||||
* @file OpenPDMFilter.c
|
||||
* @author CL
|
||||
* @version V1.0.0
|
||||
* @date 9-September-2015
|
||||
* @brief Open PDM audio software decoding Library.
|
||||
* This Library is used to decode and reconstruct the audio signal
|
||||
* produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
|
||||
*******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
|
||||
#include "OpenPDMFilter.h"
|
||||
|
||||
|
||||
/* Variables -----------------------------------------------------------------*/
|
||||
|
||||
uint32_t div_const = 0;
|
||||
int64_t sub_const = 0;
|
||||
uint32_t sinc[DECIMATION_MAX * SINCN];
|
||||
uint32_t sinc1[DECIMATION_MAX];
|
||||
uint32_t sinc2[DECIMATION_MAX * 2];
|
||||
uint32_t coef[SINCN][DECIMATION_MAX];
|
||||
#ifdef USE_LUT
|
||||
int32_t lut[256][DECIMATION_MAX / 8][SINCN];
|
||||
#endif
|
||||
|
||||
|
||||
/* Functions -----------------------------------------------------------------*/
|
||||
|
||||
#ifdef USE_LUT
|
||||
int32_t filter_table_mono_64(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[1]][1][sincn] +
|
||||
lut[data[2]][2][sincn] +
|
||||
lut[data[3]][3][sincn] +
|
||||
lut[data[4]][4][sincn] +
|
||||
lut[data[5]][5][sincn] +
|
||||
lut[data[6]][6][sincn] +
|
||||
lut[data[7]][7][sincn];
|
||||
}
|
||||
int32_t filter_table_stereo_64(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[2]][1][sincn] +
|
||||
lut[data[4]][2][sincn] +
|
||||
lut[data[6]][3][sincn] +
|
||||
lut[data[8]][4][sincn] +
|
||||
lut[data[10]][5][sincn] +
|
||||
lut[data[12]][6][sincn] +
|
||||
lut[data[14]][7][sincn];
|
||||
}
|
||||
int32_t filter_table_mono_128(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[1]][1][sincn] +
|
||||
lut[data[2]][2][sincn] +
|
||||
lut[data[3]][3][sincn] +
|
||||
lut[data[4]][4][sincn] +
|
||||
lut[data[5]][5][sincn] +
|
||||
lut[data[6]][6][sincn] +
|
||||
lut[data[7]][7][sincn] +
|
||||
lut[data[8]][8][sincn] +
|
||||
lut[data[9]][9][sincn] +
|
||||
lut[data[10]][10][sincn] +
|
||||
lut[data[11]][11][sincn] +
|
||||
lut[data[12]][12][sincn] +
|
||||
lut[data[13]][13][sincn] +
|
||||
lut[data[14]][14][sincn] +
|
||||
lut[data[15]][15][sincn];
|
||||
}
|
||||
int32_t filter_table_stereo_128(uint8_t *data, uint8_t sincn)
|
||||
{
|
||||
return (int32_t)
|
||||
lut[data[0]][0][sincn] +
|
||||
lut[data[2]][1][sincn] +
|
||||
lut[data[4]][2][sincn] +
|
||||
lut[data[6]][3][sincn] +
|
||||
lut[data[8]][4][sincn] +
|
||||
lut[data[10]][5][sincn] +
|
||||
lut[data[12]][6][sincn] +
|
||||
lut[data[14]][7][sincn] +
|
||||
lut[data[16]][8][sincn] +
|
||||
lut[data[18]][9][sincn] +
|
||||
lut[data[20]][10][sincn] +
|
||||
lut[data[22]][11][sincn] +
|
||||
lut[data[24]][12][sincn] +
|
||||
lut[data[26]][13][sincn] +
|
||||
lut[data[28]][14][sincn] +
|
||||
lut[data[30]][15][sincn];
|
||||
}
|
||||
int32_t (* filter_tables_64[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_64, filter_table_stereo_64};
|
||||
int32_t (* filter_tables_128[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_128, filter_table_stereo_128};
|
||||
#else
|
||||
int32_t filter_table(uint8_t *data, uint8_t sincn, TPDMFilter_InitStruct *param)
|
||||
{
|
||||
uint8_t c, i;
|
||||
uint16_t data_index = 0;
|
||||
uint32_t *coef_p = &coef[sincn][0];
|
||||
int32_t F = 0;
|
||||
uint8_t decimation = param->Decimation;
|
||||
uint8_t channels = param->In_MicChannels;
|
||||
|
||||
for (i = 0; i < decimation; i += 8) {
|
||||
c = data[data_index];
|
||||
F += ((c >> 7) ) * coef_p[i ] +
|
||||
((c >> 6) & 0x01) * coef_p[i + 1] +
|
||||
((c >> 5) & 0x01) * coef_p[i + 2] +
|
||||
((c >> 4) & 0x01) * coef_p[i + 3] +
|
||||
((c >> 3) & 0x01) * coef_p[i + 4] +
|
||||
((c >> 2) & 0x01) * coef_p[i + 5] +
|
||||
((c >> 1) & 0x01) * coef_p[i + 6] +
|
||||
((c ) & 0x01) * coef_p[i + 7];
|
||||
data_index += channels;
|
||||
}
|
||||
return F;
|
||||
}
|
||||
#endif
|
||||
|
||||
void convolve(uint32_t Signal[/* SignalLen */], unsigned short SignalLen,
|
||||
uint32_t Kernel[/* KernelLen */], unsigned short KernelLen,
|
||||
uint32_t Result[/* SignalLen + KernelLen - 1 */])
|
||||
{
|
||||
uint16_t n;
|
||||
|
||||
for (n = 0; n < SignalLen + KernelLen - 1; n++)
|
||||
{
|
||||
unsigned short kmin, kmax, k;
|
||||
|
||||
Result[n] = 0;
|
||||
|
||||
kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0;
|
||||
kmax = (n < SignalLen - 1) ? n : SignalLen - 1;
|
||||
|
||||
for (k = kmin; k <= kmax; k++) {
|
||||
Result[n] += Signal[k] * Kernel[n - k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Open_PDM_Filter_Init(TPDMFilter_InitStruct *Param)
|
||||
{
|
||||
uint16_t i, j;
|
||||
int64_t sum = 0;
|
||||
|
||||
uint8_t decimation = Param->Decimation;
|
||||
|
||||
for (i = 0; i < SINCN; i++) {
|
||||
Param->Coef[i] = 0;
|
||||
Param->bit[i] = 0;
|
||||
}
|
||||
for (i = 0; i < decimation; i++) {
|
||||
sinc1[i] = 1;
|
||||
}
|
||||
|
||||
Param->OldOut = Param->OldIn = Param->OldZ = 0;
|
||||
Param->LP_ALFA = (Param->LP_HZ != 0 ? (uint16_t) (Param->LP_HZ * 256 / (Param->LP_HZ + Param->Fs / (2 * 3.14159))) : 0);
|
||||
Param->HP_ALFA = (Param->HP_HZ != 0 ? (uint16_t) (Param->Fs * 256 / (2 * 3.14159 * Param->HP_HZ + Param->Fs)) : 0);
|
||||
|
||||
Param->FilterLen = decimation * SINCN;
|
||||
sinc[0] = 0;
|
||||
sinc[decimation * SINCN - 1] = 0;
|
||||
convolve(sinc1, decimation, sinc1, decimation, sinc2);
|
||||
convolve(sinc2, decimation * 2 - 1, sinc1, decimation, &sinc[1]);
|
||||
for(j = 0; j < SINCN; j++) {
|
||||
for (i = 0; i < decimation; i++) {
|
||||
coef[j][i] = sinc[j * decimation + i];
|
||||
sum += sinc[j * decimation + i];
|
||||
}
|
||||
}
|
||||
|
||||
sub_const = sum >> 1;
|
||||
div_const = sub_const * Param->MaxVolume / 32768 / Param->filterGain;
|
||||
div_const = (div_const == 0 ? 1 : div_const);
|
||||
|
||||
#ifdef USE_LUT
|
||||
/* Look-Up Table. */
|
||||
uint16_t c, d, s;
|
||||
for (s = 0; s < SINCN; s++)
|
||||
{
|
||||
uint32_t *coef_p = &coef[s][0];
|
||||
for (c = 0; c < 256; c++)
|
||||
for (d = 0; d < decimation / 8; d++)
|
||||
lut[c][d][s] = ((c >> 7) ) * coef_p[d * 8 ] +
|
||||
((c >> 6) & 0x01) * coef_p[d * 8 + 1] +
|
||||
((c >> 5) & 0x01) * coef_p[d * 8 + 2] +
|
||||
((c >> 4) & 0x01) * coef_p[d * 8 + 3] +
|
||||
((c >> 3) & 0x01) * coef_p[d * 8 + 4] +
|
||||
((c >> 2) & 0x01) * coef_p[d * 8 + 5] +
|
||||
((c >> 1) & 0x01) * coef_p[d * 8 + 6] +
|
||||
((c ) & 0x01) * coef_p[d * 8 + 7];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Open_PDM_Filter_64(uint8_t* data, int16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
|
||||
{
|
||||
uint8_t i, data_out_index;
|
||||
uint8_t channels = Param->In_MicChannels;
|
||||
uint8_t data_inc = ((DECIMATION_MAX >> 4) * channels);
|
||||
int64_t Z, Z0, Z1, Z2;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
|
||||
OldOut = Param->OldOut;
|
||||
OldIn = Param->OldIn;
|
||||
OldZ = Param->OldZ;
|
||||
|
||||
#ifdef USE_LUT
|
||||
uint8_t j = channels - 1;
|
||||
#endif
|
||||
|
||||
for (i = 0, data_out_index = 0; i < Param->nSamples; i++, data_out_index += channels) {
|
||||
#ifdef USE_LUT
|
||||
Z0 = filter_tables_64[j](data, 0);
|
||||
Z1 = filter_tables_64[j](data, 1);
|
||||
Z2 = filter_tables_64[j](data, 2);
|
||||
#else
|
||||
Z0 = filter_table(data, 0, Param);
|
||||
Z1 = filter_table(data, 1, Param);
|
||||
Z2 = filter_table(data, 2, Param);
|
||||
#endif
|
||||
|
||||
Z = Param->Coef[1] + Z2 - sub_const;
|
||||
Param->Coef[1] = Param->Coef[0] + Z1;
|
||||
Param->Coef[0] = Z0;
|
||||
|
||||
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
|
||||
OldIn = Z;
|
||||
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
|
||||
|
||||
Z = OldZ * volume;
|
||||
Z = RoundDiv(Z, div_const);
|
||||
Z = SaturaLH(Z, -32700, 32700);
|
||||
|
||||
dataOut[data_out_index] = Z;
|
||||
data += data_inc;
|
||||
}
|
||||
|
||||
Param->OldOut = OldOut;
|
||||
Param->OldIn = OldIn;
|
||||
Param->OldZ = OldZ;
|
||||
}
|
||||
|
||||
void Open_PDM_Filter_128(uint8_t* data, int16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param)
|
||||
{
|
||||
uint8_t i, data_out_index;
|
||||
uint8_t channels = Param->In_MicChannels;
|
||||
uint8_t data_inc = ((DECIMATION_MAX >> 3) * channels);
|
||||
int64_t Z, Z0, Z1, Z2;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
|
||||
OldOut = Param->OldOut;
|
||||
OldIn = Param->OldIn;
|
||||
OldZ = Param->OldZ;
|
||||
|
||||
#ifdef USE_LUT
|
||||
uint8_t j = channels - 1;
|
||||
#endif
|
||||
|
||||
for (i = 0, data_out_index = 0; i < Param->nSamples; i++, data_out_index += channels) {
|
||||
#ifdef USE_LUT
|
||||
Z0 = filter_tables_128[j](data, 0);
|
||||
Z1 = filter_tables_128[j](data, 1);
|
||||
Z2 = filter_tables_128[j](data, 2);
|
||||
#else
|
||||
Z0 = filter_table(data, 0, Param);
|
||||
Z1 = filter_table(data, 1, Param);
|
||||
Z2 = filter_table(data, 2, Param);
|
||||
#endif
|
||||
|
||||
Z = Param->Coef[1] + Z2 - sub_const;
|
||||
Param->Coef[1] = Param->Coef[0] + Z1;
|
||||
Param->Coef[0] = Z0;
|
||||
|
||||
OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8;
|
||||
OldIn = Z;
|
||||
OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8;
|
||||
|
||||
Z = OldZ * volume;
|
||||
Z = RoundDiv(Z, div_const);
|
||||
Z = SaturaLH(Z, -32700, 32700);
|
||||
|
||||
dataOut[data_out_index] = Z;
|
||||
data += data_inc;
|
||||
}
|
||||
|
||||
Param->OldOut = OldOut;
|
||||
Param->OldIn = OldIn;
|
||||
Param->OldZ = OldZ;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
*******************************************************************************
|
||||
* @file OpenPDMFilter.h
|
||||
* @author CL
|
||||
* @version V1.0.0
|
||||
* @date 9-September-2015
|
||||
* @brief Header file for Open PDM audio software decoding Library.
|
||||
* This Library is used to decode and reconstruct the audio signal
|
||||
* produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx).
|
||||
*******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2018 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
|
||||
#ifndef __OPENPDMFILTER_H
|
||||
#define __OPENPDMFILTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* Definitions ---------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Enable to use a Look-Up Table to improve performances while using more FLASH
|
||||
* and RAM memory.
|
||||
* Note: Without Look-Up Table up to stereo@16KHz configuration is supported.
|
||||
*/
|
||||
#define USE_LUT
|
||||
|
||||
#define SINCN 3
|
||||
#define DECIMATION_MAX 128
|
||||
|
||||
#define HTONS(A) ((((uint16_t)(A) & 0xff00) >> 8) | \
|
||||
(((uint16_t)(A) & 0x00ff) << 8))
|
||||
#define RoundDiv(a, b) (((a)>0)?(((a)+(b)/2)/(b)):(((a)-(b)/2)/(b)))
|
||||
#define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N)))
|
||||
|
||||
|
||||
/* Types ---------------------------------------------------------------------*/
|
||||
|
||||
typedef struct {
|
||||
/* Public */
|
||||
float LP_HZ;
|
||||
float HP_HZ;
|
||||
uint16_t Fs;
|
||||
unsigned int nSamples;
|
||||
uint8_t In_MicChannels;
|
||||
uint8_t Out_MicChannels;
|
||||
uint8_t Decimation;
|
||||
uint8_t MaxVolume;
|
||||
/* Private */
|
||||
uint32_t Coef[SINCN];
|
||||
uint16_t FilterLen;
|
||||
int64_t OldOut, OldIn, OldZ;
|
||||
uint16_t LP_ALFA;
|
||||
uint16_t HP_ALFA;
|
||||
uint16_t bit[5];
|
||||
uint16_t byte;
|
||||
uint16_t filterGain;
|
||||
} TPDMFilter_InitStruct;
|
||||
|
||||
|
||||
/* Exported functions ------------------------------------------------------- */
|
||||
|
||||
void Open_PDM_Filter_Init(TPDMFilter_InitStruct *init_struct);
|
||||
void Open_PDM_Filter_64(uint8_t* data, int16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct);
|
||||
void Open_PDM_Filter_128(uint8_t* data, int16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __OPENPDMFILTER_H
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,205 @@
|
||||
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "PDM.h"
|
||||
#include "OpenPDMFilter.h"
|
||||
|
||||
extern "C" {
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/clocks.h"
|
||||
}
|
||||
#include "hardware/sync.h"
|
||||
#include "pdm.pio.h"
|
||||
|
||||
// Hardware peripherals used
|
||||
uint dmaChannel = 0;
|
||||
PIO pio = pio0;
|
||||
uint sm = 0;
|
||||
|
||||
// raw buffers contain PDM data
|
||||
#define RAW_BUFFER_SIZE 512 // should be a multiple of (decimation / 8)
|
||||
uint8_t rawBuffer0[RAW_BUFFER_SIZE];
|
||||
uint8_t rawBuffer1[RAW_BUFFER_SIZE];
|
||||
uint8_t* rawBuffer[2] = {rawBuffer0, rawBuffer1};
|
||||
volatile int rawBufferIndex = 0;
|
||||
|
||||
int decimation = 64;
|
||||
|
||||
// final buffer is the one to be filled with PCM data
|
||||
int16_t* volatile finalBuffer;
|
||||
|
||||
// OpenPDM filter used to convert PDM into PCM
|
||||
#define FILTER_GAIN 16
|
||||
TPDMFilter_InitStruct filter;
|
||||
|
||||
extern "C" {
|
||||
__attribute__((__used__)) void dmaHandler(void)
|
||||
{
|
||||
PDM.IrqHandler(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PDMClass::PDMClass(int dinPin, int clkPin, int pwrPin) :
|
||||
_dinPin(dinPin),
|
||||
_clkPin(clkPin),
|
||||
_pwrPin(pwrPin),
|
||||
_onReceive(NULL),
|
||||
_gain(-1),
|
||||
_channels(-1),
|
||||
_samplerate(-1),
|
||||
_init(-1)
|
||||
{
|
||||
}
|
||||
|
||||
PDMClass::~PDMClass()
|
||||
{
|
||||
}
|
||||
|
||||
int PDMClass::begin(int channels, int sampleRate)
|
||||
{
|
||||
//_channels = channels; // only one channel available
|
||||
|
||||
// clear the final buffers
|
||||
_doubleBuffer.reset();
|
||||
finalBuffer = (int16_t*)_doubleBuffer.data();
|
||||
int finalBufferLength = _doubleBuffer.availableForWrite() / sizeof(int16_t);
|
||||
_doubleBuffer.swap(0);
|
||||
|
||||
int rawBufferLength = RAW_BUFFER_SIZE / (decimation / 8);
|
||||
// Saturate number of samples. Remaining bytes are dropped.
|
||||
if (rawBufferLength > finalBufferLength) {
|
||||
rawBufferLength = finalBufferLength;
|
||||
}
|
||||
|
||||
/* Initialize Open PDM library */
|
||||
filter.Fs = sampleRate;
|
||||
filter.nSamples = rawBufferLength;
|
||||
filter.LP_HZ = sampleRate/2;
|
||||
filter.HP_HZ = 10;
|
||||
filter.In_MicChannels = 1;
|
||||
filter.Out_MicChannels = 1;
|
||||
filter.Decimation = decimation;
|
||||
if(_gain == -1) {
|
||||
_gain = FILTER_GAIN;
|
||||
}
|
||||
filter.filterGain = _gain;
|
||||
Open_PDM_Filter_Init(&filter);
|
||||
|
||||
// Configure PIO state machine
|
||||
float clkDiv = (float)clock_get_hz(clk_sys) / sampleRate / decimation / 2;
|
||||
uint offset = pio_add_program(pio, &pdm_pio_program);
|
||||
pdm_pio_program_init(pio, sm, offset, _clkPin, _dinPin, clkDiv);
|
||||
|
||||
// Wait for microphone
|
||||
delay(100);
|
||||
|
||||
// Configure DMA for transferring PIO rx buffer to raw buffers
|
||||
dma_channel_config c = dma_channel_get_default_config(dmaChannel);
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
channel_config_set_dreq(&c, pio_get_dreq(pio, sm, false));
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
|
||||
// Clear DMA interrupts
|
||||
dma_hw->ints0 = 1u << dmaChannel;
|
||||
// Enable DMA interrupts
|
||||
dma_channel_set_irq0_enabled(dmaChannel, true);
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, dmaHandler);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
dma_channel_configure(dmaChannel, &c,
|
||||
rawBuffer[rawBufferIndex], // Destinatinon pointer
|
||||
&pio->rxf[sm], // Source pointer
|
||||
RAW_BUFFER_SIZE, // Number of transfers
|
||||
true // Start immediately
|
||||
);
|
||||
|
||||
_init = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PDMClass::end()
|
||||
{
|
||||
dma_channel_abort(dmaChannel);
|
||||
pinMode(_clkPin, INPUT);
|
||||
}
|
||||
|
||||
int PDMClass::available()
|
||||
{
|
||||
//NVIC_DisableIRQ(DMA_IRQ_0n);
|
||||
//uint32_t interrupts = save_and_disable_interrupts();
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
size_t avail = _doubleBuffer.available();
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
//restore_interrupts(interrupts);
|
||||
//NVIC_EnableIRQ(DMA_IRQ_0n);
|
||||
return avail;
|
||||
}
|
||||
|
||||
int PDMClass::read(void* buffer, size_t size)
|
||||
{
|
||||
irq_set_enabled(DMA_IRQ_0, false);
|
||||
int read = _doubleBuffer.read(buffer, size);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
return read;
|
||||
}
|
||||
|
||||
void PDMClass::onReceive(void(*function)(void))
|
||||
{
|
||||
_onReceive = function;
|
||||
}
|
||||
|
||||
void PDMClass::setGain(int gain)
|
||||
{
|
||||
_gain = gain;
|
||||
if(_init == 1) {
|
||||
filter.filterGain = _gain;
|
||||
Open_PDM_Filter_Init(&filter);
|
||||
}
|
||||
}
|
||||
|
||||
void PDMClass::setBufferSize(int bufferSize)
|
||||
{
|
||||
_doubleBuffer.setSize(bufferSize);
|
||||
}
|
||||
|
||||
void PDMClass::IrqHandler(bool halftranfer)
|
||||
{
|
||||
static int cutSamples = 100;
|
||||
|
||||
// Clear the interrupt request.
|
||||
dma_hw->ints0 = 1u << dmaChannel;
|
||||
// Restart dma pointing to the other buffer
|
||||
int shadowIndex = rawBufferIndex ^ 1;
|
||||
dma_channel_set_write_addr(dmaChannel, rawBuffer[shadowIndex], true);
|
||||
|
||||
if (_doubleBuffer.available()) {
|
||||
// buffer overflow, stop
|
||||
return end();
|
||||
}
|
||||
|
||||
// fill final buffer with PCM samples
|
||||
Open_PDM_Filter_64(rawBuffer[rawBufferIndex], finalBuffer, 1, &filter);
|
||||
|
||||
if (cutSamples) {
|
||||
memset(finalBuffer, 0, cutSamples);
|
||||
cutSamples = 0;
|
||||
}
|
||||
|
||||
// swap final buffer and raw buffers' indexes
|
||||
finalBuffer = (int16_t*)_doubleBuffer.data();
|
||||
_doubleBuffer.swap(filter.nSamples * sizeof(int16_t));
|
||||
rawBufferIndex = shadowIndex;
|
||||
|
||||
if (_onReceive) {
|
||||
_onReceive();
|
||||
}
|
||||
}
|
||||
#ifdef PIN_PDM_DIN
|
||||
PDMClass PDM(PIN_PDM_DIN, PIN_PDM_CLK, -1);
|
||||
#endif // PIN_PDM_DIN
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
.program pdm_pio
|
||||
.side_set 1
|
||||
.wrap_target
|
||||
|
||||
; prova a cambiare edge
|
||||
|
||||
;sample on falling edge
|
||||
push iffull noblock side 1
|
||||
;nop side 1
|
||||
;nop side 0
|
||||
in pins, 1 side 0
|
||||
;nop side 0
|
||||
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
static inline void pdm_pio_program_init(PIO pio, uint sm, uint offset, uint clkPin, uint dataPin, float clkDiv) {
|
||||
pio_sm_config c = pdm_pio_program_get_default_config(offset);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
//sm_config_set_in_shift(&c, false, true, 8);
|
||||
sm_config_set_in_shift(&c, false, false, 8);
|
||||
|
||||
sm_config_set_in_pins(&c, dataPin);
|
||||
sm_config_set_sideset_pins(&c, clkPin);
|
||||
sm_config_set_clkdiv(&c, clkDiv);
|
||||
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, dataPin, 1, false);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, clkPin, 1, true);
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << clkPin) );
|
||||
//pio_gpio_init(pio, dataPin);
|
||||
pio_gpio_init(pio, clkPin);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
|
||||
%}
|
||||
@@ -0,0 +1,56 @@
|
||||
// -------------------------------------------------- //
|
||||
// This file is autogenerated by pioasm; do not edit! //
|
||||
// -------------------------------------------------- //
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
#include "hardware/pio.h"
|
||||
#endif
|
||||
|
||||
// ------- //
|
||||
// pdm_pio //
|
||||
// ------- //
|
||||
|
||||
#define pdm_pio_wrap_target 0
|
||||
#define pdm_pio_wrap 1
|
||||
|
||||
static const uint16_t pdm_pio_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x9040, // 0: push iffull noblock side 1
|
||||
0x4001, // 1: in pins, 1 side 0
|
||||
// .wrap
|
||||
};
|
||||
|
||||
#if !PICO_NO_HARDWARE
|
||||
static const struct pio_program pdm_pio_program = {
|
||||
.instructions = pdm_pio_program_instructions,
|
||||
.length = 2,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config pdm_pio_program_get_default_config(uint offset) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, offset + pdm_pio_wrap_target, offset + pdm_pio_wrap);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
static inline void pdm_pio_program_init(PIO pio, uint sm, uint offset, uint clkPin, uint dataPin, float clkDiv) {
|
||||
pio_sm_config c = pdm_pio_program_get_default_config(offset);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
//sm_config_set_in_shift(&c, false, true, 8);
|
||||
sm_config_set_in_shift(&c, false, false, 8);
|
||||
sm_config_set_in_pins(&c, dataPin);
|
||||
sm_config_set_sideset_pins(&c, clkPin);
|
||||
sm_config_set_clkdiv(&c, clkDiv);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, dataPin, 1, false);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, clkPin, 1, true);
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << clkPin) );
|
||||
//pio_gpio_init(pio, dataPin);
|
||||
pio_gpio_init(pio, clkPin);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "PDMDoubleBuffer.h"
|
||||
|
||||
PDMDoubleBuffer::PDMDoubleBuffer() :
|
||||
_size(DEFAULT_PDM_BUFFER_SIZE)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
PDMDoubleBuffer::~PDMDoubleBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::setSize(int size)
|
||||
{
|
||||
_size = size;
|
||||
reset();
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::getSize()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::reset()
|
||||
{
|
||||
_buffer[0] = (uint8_t*)realloc(_buffer[0], _size);
|
||||
_buffer[1] = (uint8_t*)realloc(_buffer[1], _size);
|
||||
|
||||
memset(_buffer[0], 0x00, _size);
|
||||
memset(_buffer[1], 0x00, _size);
|
||||
|
||||
_index = 0;
|
||||
_length[0] = 0;
|
||||
_length[1] = 0;
|
||||
_readOffset[0] = 0;
|
||||
_readOffset[1] = 0;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::availableForWrite()
|
||||
{
|
||||
return (_size - (_length[_index] - _readOffset[_index]));
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::write(const void *buffer, size_t size)
|
||||
{
|
||||
size_t space = availableForWrite();
|
||||
|
||||
if (size > space) {
|
||||
size = space;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&_buffer[_index][_length[_index]], buffer, size);
|
||||
|
||||
_length[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::read(void *buffer, size_t size)
|
||||
{
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
_readOffset[_index] += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::peek(void *buffer, size_t size)
|
||||
{
|
||||
size_t avail = available();
|
||||
|
||||
if (size > avail) {
|
||||
size = avail;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(buffer, &_buffer[_index][_readOffset[_index]], size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void* PDMDoubleBuffer::data()
|
||||
{
|
||||
return (void*)_buffer[_index];
|
||||
}
|
||||
|
||||
size_t PDMDoubleBuffer::available()
|
||||
{
|
||||
return _length[_index] - _readOffset[_index];
|
||||
}
|
||||
|
||||
void PDMDoubleBuffer::swap(int length)
|
||||
{
|
||||
if (_index == 0) {
|
||||
_index = 1;
|
||||
} else {
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
_length[_index] = length;
|
||||
_readOffset[_index] = 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright (c) 2016 Arduino LLC. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _PDM_DOUBLE_BUFFER_H_INCLUDED
|
||||
#define _PDM_DOUBLE_BUFFER_H_INCLUDED
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEFAULT_PDM_BUFFER_SIZE 512
|
||||
|
||||
class PDMDoubleBuffer
|
||||
{
|
||||
public:
|
||||
PDMDoubleBuffer();
|
||||
virtual ~PDMDoubleBuffer();
|
||||
|
||||
void setSize(int size);
|
||||
size_t getSize();
|
||||
|
||||
void reset();
|
||||
|
||||
size_t availableForWrite();
|
||||
size_t write(const void *buffer, size_t size);
|
||||
size_t read(void *buffer, size_t size);
|
||||
size_t peek(void *buffer, size_t size);
|
||||
void* data();
|
||||
size_t available();
|
||||
void swap(int length = 0);
|
||||
|
||||
private:
|
||||
uint8_t* _buffer[2] __attribute__((aligned (16)));
|
||||
int _size;
|
||||
volatile int _length[2];
|
||||
volatile int _readOffset[2];
|
||||
volatile int _index;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -234,8 +234,8 @@ protected:
|
||||
}
|
||||
|
||||
|
||||
static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) {
|
||||
uint8_t mode = 0;
|
||||
static int _getFlags(OpenMode openMode, AccessMode accessMode) {
|
||||
int mode = 0;
|
||||
if (openMode & OM_CREATE) {
|
||||
mode |= O_CREAT;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ void TwoWire::begin(uint8_t addr) {
|
||||
i2c_set_slave_mode(_i2c, true, addr);
|
||||
|
||||
// Our callback IRQ
|
||||
_i2c->hw->intr_mask = (1 << 10) | (1 << 9) | (1 << 5) | (1 << 2);
|
||||
_i2c->hw->intr_mask = (1 << 10) | (1 << 9) | (1 << 6) | (1 << 5) | (1 << 2);
|
||||
|
||||
int irqNo = I2C0_IRQ + i2c_hw_index(_i2c);
|
||||
irq_set_exclusive_handler(irqNo, i2c_hw_index(_i2c) == 0 ? _handler0 : _handler1);
|
||||
@@ -139,6 +139,7 @@ void TwoWire::begin(uint8_t addr) {
|
||||
}
|
||||
|
||||
void TwoWire::onIRQ() {
|
||||
Serial.printf("Wire.IRQ=%08x\n", _i2c->hw->intr_stat);
|
||||
if (_i2c->hw->intr_stat & (1 << 10)) {
|
||||
_buffLen = 0;
|
||||
_buffOff = 0;
|
||||
@@ -154,6 +155,10 @@ void TwoWire::onIRQ() {
|
||||
_slaveStartDet = false;
|
||||
_i2c->hw->clr_stop_det;
|
||||
}
|
||||
if (_i2c->hw->intr_stat & (1 << 6)) {
|
||||
// TX_ABRT
|
||||
_i2c->hw->clr_tx_abrt;
|
||||
}
|
||||
if (_i2c->hw->intr_stat & (1 << 5)) {
|
||||
// RD_REQ
|
||||
if (_onRequestCallback) {
|
||||
|
||||
@@ -26,6 +26,7 @@ function skip_ino()
|
||||
/p08_DigitalHourglass/
|
||||
/p13_TouchSensorLamp/
|
||||
/StringComparisonOperators/
|
||||
/PDMSerialPlotter/
|
||||
EOL
|
||||
echo $ino | grep -q -F "$skiplist"
|
||||
echo $(( 1 - $? ))
|
||||
|
||||
@@ -102,6 +102,11 @@ static const uint8_t SCK = PIN_SPI0_SCK;
|
||||
#define SPIWIFI_RESET (NINA_RESETN)
|
||||
#define SPIWIFI (SPI1)
|
||||
|
||||
// PDM Interfaces
|
||||
// ---------------
|
||||
#define PIN_PDM_CLK (D23)
|
||||
#define PIN_PDM_DIN (D22)
|
||||
|
||||
//Cose copiate a caso
|
||||
|
||||
#define SERIAL_PORT_USBVIRTUAL SerialUSB
|
||||
|
||||
Reference in New Issue
Block a user