├── .github └── stale.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── hello_analog_microphone │ ├── CMakeLists.txt │ └── main.c ├── hello_pdm_microphone │ ├── CMakeLists.txt │ └── main.c └── usb_microphone │ ├── CMakeLists.txt │ ├── main.c │ ├── tusb_config.h │ ├── usb_descriptors.c │ ├── usb_microphone.c │ └── usb_microphone.h ├── pico_sdk_import.cmake └── src ├── OpenPDM2PCM ├── LICENSE.txt ├── OpenPDMFilter.c └── OpenPDMFilter.h ├── analog_microphone.c ├── include └── pico │ ├── analog_microphone.h │ └── pdm_microphone.h ├── pdm_microphone.c └── pdm_microphone.pio /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - help wanted 10 | # Label to use when marking an issue as stale 11 | staleLabel: stale 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | # Never stale pull requests 20 | only: issues -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | 13 | build/ 14 | 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # initialize pico_sdk from GIT 4 | # (note this can come from environment, CMake cache etc) 5 | # set(PICO_SDK_FETCH_FROM_GIT on) 6 | 7 | # pico_sdk_import.cmake is a single file copied from this SDK 8 | # note: this must happen before project() 9 | include(pico_sdk_import.cmake) 10 | 11 | project(pico_microphone) 12 | 13 | # initialize the Pico SDK 14 | pico_sdk_init() 15 | 16 | add_library(pico_pdm_microphone INTERFACE) 17 | 18 | target_sources(pico_pdm_microphone INTERFACE 19 | ${CMAKE_CURRENT_LIST_DIR}/src/pdm_microphone.c 20 | ${CMAKE_CURRENT_LIST_DIR}/src/OpenPDM2PCM/OpenPDMFilter.c 21 | ) 22 | 23 | target_include_directories(pico_pdm_microphone INTERFACE 24 | ${CMAKE_CURRENT_LIST_DIR}/src/include 25 | ) 26 | 27 | pico_generate_pio_header(pico_pdm_microphone ${CMAKE_CURRENT_LIST_DIR}/src/pdm_microphone.pio) 28 | 29 | target_link_libraries(pico_pdm_microphone INTERFACE pico_stdlib hardware_dma hardware_pio) 30 | 31 | 32 | add_library(pico_analog_microphone INTERFACE) 33 | 34 | target_sources(pico_analog_microphone INTERFACE 35 | ${CMAKE_CURRENT_LIST_DIR}/src/analog_microphone.c 36 | ) 37 | 38 | target_include_directories(pico_analog_microphone INTERFACE 39 | ${CMAKE_CURRENT_LIST_DIR}/src/include 40 | ) 41 | 42 | target_link_libraries(pico_analog_microphone INTERFACE pico_stdlib hardware_adc hardware_dma) 43 | 44 | add_subdirectory("examples/hello_analog_microphone") 45 | add_subdirectory("examples/hello_pdm_microphone") 46 | add_subdirectory("examples/usb_microphone") 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microphone Library for Pico 2 | 3 | Capture audio from a microphone on your [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/) or any [RP2040](https://www.raspberrypi.org/products/rp2040/) based board. 🎤 4 | 5 | 6 | ## Hardware 7 | 8 | * RP2040 board 9 | * [Raspberry Pi Pico](https://www.raspberrypi.org/products/raspberry-pi-pico/) 10 | * Microphones 11 | * Analog 12 | * [Electret Microphone Amplifier - MAX9814 with Auto Gain Control](https://www.adafruit.com/product/1713) 13 | * PDM 14 | * [Adafruit PDM MEMS Microphone Breakout](https://www.adafruit.com/product/3492) 15 | 16 | ### Default Pinout 17 | 18 | #### Analog Microphone 19 | 20 | | Raspberry Pi Pico / RP2040 | Analog Microphone | 21 | | -------------------------- | ----------------- | 22 | | 3.3V | VCC | 23 | | GND | GND | 24 | | GPIO 26 | OUT | 25 | 26 | #### PDM Microphone 27 | 28 | | Raspberry Pi Pico / RP2040 | PDM Microphone | 29 | | -------------------------- | ----------------- | 30 | | 3.3V | VCC | 31 | | GND | GND | 32 | | GND | SEL | 33 | | GPIO 2 | DAT | 34 | | GPIO 3 | CLK | 35 | 36 | GPIO pins are configurable in examples or API. 37 | 38 | ## Examples 39 | 40 | See [examples](examples/) folder. 41 | 42 | 43 | ## Cloning 44 | 45 | ```sh 46 | git clone https://github.com/ArmDeveloperEcosystem/microphone-library-for-pico.git 47 | ``` 48 | 49 | ## Building 50 | 51 | 1. [Set up the Pico C/C++ SDK](https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf) 52 | 2. Set `PICO_SDK_PATH` 53 | ```sh 54 | export PICO_SDK_PATH=/path/to/pico-sdk 55 | ``` 56 | 3. Create `build` dir, run `cmake` and `make`: 57 | ``` 58 | mkdir build 59 | cd build 60 | cmake .. -DPICO_BOARD=pico 61 | make 62 | ``` 63 | 4. Copy example `.uf2` to Pico when in BOOT mode. 64 | 65 | ## License 66 | 67 | [Apache-2.0 License](LICENSE) 68 | 69 | ## Acknowledgements 70 | 71 | This project was created on behalf of the [Arm Software Developers](https://developer.arm.com/) team, follow them on Twitter: [@ArmSoftwareDev](https://twitter.com/armsoftwaredev) and YouTube: [Arm Software Developers](https://www.youtube.com/channel/UCHUAckhCfRom2EHDGxwhfOg) for more resources! 72 | 73 | The [OpenPDM2PCM](https://os.mbed.com/teams/ST/code/X_NUCLEO_CCA02M1//file/53f8b511f2a1/Middlewares/OpenPDM2PCM/) library is used to filter raw PDM data into PCM. The [TinyUSB](https://github.com/hathach/tinyusb) library is used in the `usb_microphone` example. 74 | 75 | --- 76 | 77 | Disclaimer: This is not an official Arm product. 78 | -------------------------------------------------------------------------------- /examples/hello_analog_microphone/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(hello_analog_microphone 5 | main.c 6 | ) 7 | 8 | target_link_libraries(hello_analog_microphone pico_analog_microphone) 9 | 10 | # enable usb output, disable uart output 11 | pico_enable_stdio_usb(hello_analog_microphone 1) 12 | pico_enable_stdio_uart(hello_analog_microphone 0) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(hello_analog_microphone) 16 | -------------------------------------------------------------------------------- /examples/hello_analog_microphone/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * 7 | * This examples captures data from an analog microphone using a sample 8 | * rate of 8 kHz and prints the sample values over the USB serial 9 | * connection. 10 | */ 11 | 12 | #include 13 | 14 | #include "pico/stdlib.h" 15 | #include "pico/analog_microphone.h" 16 | #include "tusb.h" 17 | 18 | // configuration 19 | const struct analog_microphone_config config = { 20 | // GPIO to use for input, must be ADC compatible (GPIO 26 - 28) 21 | .gpio = 26, 22 | 23 | // bias voltage of microphone in volts 24 | .bias_voltage = 1.25, 25 | 26 | // sample rate in Hz 27 | .sample_rate = 8000, 28 | 29 | // number of samples to buffer 30 | .sample_buffer_size = 256, 31 | }; 32 | 33 | // variables 34 | int16_t sample_buffer[256]; 35 | volatile int samples_read = 0; 36 | 37 | void on_analog_samples_ready() 38 | { 39 | // callback from library when all the samples in the library 40 | // internal sample buffer are ready for reading 41 | samples_read = analog_microphone_read(sample_buffer, 256); 42 | } 43 | 44 | int main( void ) 45 | { 46 | // initialize stdio and wait for USB CDC connect 47 | stdio_init_all(); 48 | while (!tud_cdc_connected()) { 49 | tight_loop_contents(); 50 | } 51 | 52 | printf("hello analog microphone\n"); 53 | 54 | // initialize the analog microphone 55 | if (analog_microphone_init(&config) < 0) { 56 | printf("analog microphone initialization failed!\n"); 57 | while (1) { tight_loop_contents(); } 58 | } 59 | 60 | // set callback that is called when all the samples in the library 61 | // internal sample buffer are ready for reading 62 | analog_microphone_set_samples_ready_handler(on_analog_samples_ready); 63 | 64 | // start capturing data from the analog microphone 65 | if (analog_microphone_start() < 0) { 66 | printf("PDM microphone start failed!\n"); 67 | while (1) { tight_loop_contents(); } 68 | } 69 | 70 | while (1) { 71 | // wait for new samples 72 | while (samples_read == 0) { tight_loop_contents(); } 73 | 74 | // store and clear the samples read from the callback 75 | int sample_count = samples_read; 76 | samples_read = 0; 77 | 78 | // loop through any new collected samples 79 | for (int i = 0; i < sample_count; i++) { 80 | printf("%d\n", sample_buffer[i]); 81 | } 82 | } 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /examples/hello_pdm_microphone/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(hello_pdm_microphone 5 | main.c 6 | ) 7 | 8 | target_link_libraries(hello_pdm_microphone pico_pdm_microphone) 9 | 10 | # enable usb output, disable uart output 11 | pico_enable_stdio_usb(hello_pdm_microphone 1) 12 | pico_enable_stdio_uart(hello_pdm_microphone 0) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(hello_pdm_microphone) 16 | -------------------------------------------------------------------------------- /examples/hello_pdm_microphone/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * This examples captures data from a PDM microphone using a sample 7 | * rate of 8 kHz and prints the sample values over the USB serial 8 | * connection. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "pico/stdlib.h" 15 | #include "pico/pdm_microphone.h" 16 | #include "tusb.h" 17 | 18 | // configuration 19 | const struct pdm_microphone_config config = { 20 | // GPIO pin for the PDM DAT signal 21 | .gpio_data = 2, 22 | 23 | // GPIO pin for the PDM CLK signal 24 | .gpio_clk = 3, 25 | 26 | // PIO instance to use 27 | .pio = pio0, 28 | 29 | // PIO State Machine instance to use 30 | .pio_sm = 0, 31 | 32 | // sample rate in Hz 33 | .sample_rate = 8000, 34 | 35 | // number of samples to buffer 36 | .sample_buffer_size = 256, 37 | }; 38 | 39 | // variables 40 | int16_t sample_buffer[256]; 41 | volatile int samples_read = 0; 42 | 43 | void on_pdm_samples_ready() 44 | { 45 | // callback from library when all the samples in the library 46 | // internal sample buffer are ready for reading 47 | samples_read = pdm_microphone_read(sample_buffer, 256); 48 | } 49 | 50 | int main( void ) 51 | { 52 | // initialize stdio and wait for USB CDC connect 53 | stdio_init_all(); 54 | while (!tud_cdc_connected()) { 55 | tight_loop_contents(); 56 | } 57 | 58 | printf("hello PDM microphone\n"); 59 | 60 | // initialize the PDM microphone 61 | if (pdm_microphone_init(&config) < 0) { 62 | printf("PDM microphone initialization failed!\n"); 63 | while (1) { tight_loop_contents(); } 64 | } 65 | 66 | // set callback that is called when all the samples in the library 67 | // internal sample buffer are ready for reading 68 | pdm_microphone_set_samples_ready_handler(on_pdm_samples_ready); 69 | 70 | // start capturing data from the PDM microphone 71 | if (pdm_microphone_start() < 0) { 72 | printf("PDM microphone start failed!\n"); 73 | while (1) { tight_loop_contents(); } 74 | } 75 | 76 | while (1) { 77 | // wait for new samples 78 | while (samples_read == 0) { tight_loop_contents(); } 79 | 80 | // store and clear the samples read from the callback 81 | int sample_count = samples_read; 82 | samples_read = 0; 83 | 84 | // loop through any new collected samples 85 | for (int i = 0; i < sample_count; i++) { 86 | printf("%d\n", sample_buffer[i]); 87 | } 88 | } 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /examples/usb_microphone/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # rest of your project 4 | add_executable(usb_microphone 5 | main.c 6 | usb_descriptors.c 7 | usb_microphone.c 8 | ) 9 | 10 | target_include_directories(usb_microphone PRIVATE ${CMAKE_CURRENT_LIST_DIR}) 11 | 12 | target_link_libraries(usb_microphone PRIVATE tinyusb_device tinyusb_board pico_pdm_microphone) 13 | 14 | # create map/bin/hex/uf2 file in addition to ELF. 15 | pico_add_extra_outputs(usb_microphone) 16 | -------------------------------------------------------------------------------- /examples/usb_microphone/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * This examples creates a USB Microphone device using the TinyUSB 7 | * library and captures data from a PDM microphone using a sample 8 | * rate of 16 kHz, to be sent the to PC. 9 | * 10 | * The USB microphone code is based on the TinyUSB audio_test example. 11 | * 12 | * https://github.com/hathach/tinyusb/tree/master/examples/device/audio_test 13 | */ 14 | 15 | #include "pico/pdm_microphone.h" 16 | 17 | #include "usb_microphone.h" 18 | 19 | // configuration 20 | const struct pdm_microphone_config config = { 21 | .gpio_data = 2, 22 | .gpio_clk = 3, 23 | .pio = pio0, 24 | .pio_sm = 0, 25 | .sample_rate = SAMPLE_RATE, 26 | .sample_buffer_size = SAMPLE_BUFFER_SIZE, 27 | }; 28 | 29 | // variables 30 | uint16_t sample_buffer[SAMPLE_BUFFER_SIZE]; 31 | 32 | // callback functions 33 | void on_pdm_samples_ready(); 34 | void on_usb_microphone_tx_ready(); 35 | 36 | int main(void) 37 | { 38 | // initialize and start the PDM microphone 39 | pdm_microphone_init(&config); 40 | pdm_microphone_set_samples_ready_handler(on_pdm_samples_ready); 41 | pdm_microphone_start(); 42 | 43 | // initialize the USB microphone interface 44 | usb_microphone_init(); 45 | usb_microphone_set_tx_ready_handler(on_usb_microphone_tx_ready); 46 | 47 | while (1) { 48 | // run the USB microphone task continuously 49 | usb_microphone_task(); 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | void on_pdm_samples_ready() 56 | { 57 | // Callback from library when all the samples in the library 58 | // internal sample buffer are ready for reading. 59 | // 60 | // Read new samples into local buffer. 61 | pdm_microphone_read(sample_buffer, SAMPLE_BUFFER_SIZE); 62 | } 63 | 64 | void on_usb_microphone_tx_ready() 65 | { 66 | // Callback from TinyUSB library when all data is ready 67 | // to be transmitted. 68 | // 69 | // Write local buffer to the USB microphone 70 | usb_microphone_write(sample_buffer, sizeof(sample_buffer)); 71 | } 72 | -------------------------------------------------------------------------------- /examples/usb_microphone/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by compiler flags for flexibility 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | #if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX 43 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) 44 | #else 45 | #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE 46 | #endif 47 | 48 | #ifndef CFG_TUSB_OS 49 | #define CFG_TUSB_OS OPT_OS_NONE 50 | #endif 51 | 52 | #ifndef CFG_TUSB_DEBUG 53 | #define CFG_TUSB_DEBUG 0 54 | #endif 55 | 56 | // CFG_TUSB_DEBUG is defined by compiler in DEBUG build 57 | // #define CFG_TUSB_DEBUG 0 58 | 59 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 60 | * Tinyusb use follows macros to declare transferring memory so that they can be put 61 | * into those specific section. 62 | * e.g 63 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 64 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 65 | */ 66 | #ifndef CFG_TUSB_MEM_SECTION 67 | #define CFG_TUSB_MEM_SECTION 68 | #endif 69 | 70 | #ifndef CFG_TUSB_MEM_ALIGN 71 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 72 | #endif 73 | 74 | //-------------------------------------------------------------------- 75 | // DEVICE CONFIGURATION 76 | //-------------------------------------------------------------------- 77 | 78 | #ifndef CFG_TUD_ENDPOINT0_SIZE 79 | #define CFG_TUD_ENDPOINT0_SIZE 64 80 | #endif 81 | 82 | //------------- CLASS -------------// 83 | #define CFG_TUD_CDC 0 84 | #define CFG_TUD_MSC 0 85 | #define CFG_TUD_HID 0 86 | #define CFG_TUD_MIDI 0 87 | #define CFG_TUD_AUDIO 1 88 | #define CFG_TUD_VENDOR 0 89 | 90 | //-------------------------------------------------------------------- 91 | // AUDIO CLASS DRIVER CONFIGURATION 92 | //-------------------------------------------------------------------- 93 | 94 | // Have a look into audio_device.h for all configurations 95 | 96 | #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN 97 | #define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) 98 | #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer 99 | 100 | #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 101 | #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below 102 | #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor! 103 | #define CFG_TUD_AUDIO_EP_SZ_IN (16 + 1) * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX // 16 Samples (16 kHz) x 2 Bytes/Sample x 1 Channel 104 | #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN // Maximum EP IN size for all AS alternate settings used 105 | #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif /* _TUSB_CONFIG_H_ */ 112 | -------------------------------------------------------------------------------- /examples/usb_microphone/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | 28 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 29 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 30 | * 31 | * Auto ProductID layout's Bitmap: 32 | * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB] 33 | */ 34 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 35 | #define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 36 | _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) ) 37 | 38 | //--------------------------------------------------------------------+ 39 | // Device Descriptors 40 | //--------------------------------------------------------------------+ 41 | tusb_desc_device_t const desc_device = 42 | { 43 | .bLength = sizeof(tusb_desc_device_t), 44 | .bDescriptorType = TUSB_DESC_DEVICE, 45 | .bcdUSB = 0x0200, 46 | 47 | // Use Interface Association Descriptor (IAD) for CDC 48 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 49 | .bDeviceClass = TUSB_CLASS_MISC, 50 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 51 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 52 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 53 | 54 | .idVendor = 0xCafe, 55 | .idProduct = USB_PID, 56 | .bcdDevice = 0x0100, 57 | 58 | .iManufacturer = 0x01, 59 | .iProduct = 0x02, 60 | .iSerialNumber = 0x03, 61 | 62 | .bNumConfigurations = 0x01 63 | }; 64 | 65 | // Invoked when received GET DEVICE DESCRIPTOR 66 | // Application return pointer to descriptor 67 | uint8_t const * tud_descriptor_device_cb(void) 68 | { 69 | return (uint8_t const *) &desc_device; 70 | } 71 | 72 | //--------------------------------------------------------------------+ 73 | // Configuration Descriptor 74 | //--------------------------------------------------------------------+ 75 | enum 76 | { 77 | ITF_NUM_AUDIO_CONTROL = 0, 78 | ITF_NUM_AUDIO_STREAMING, 79 | ITF_NUM_TOTAL 80 | }; 81 | 82 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_DESC_LEN) 83 | 84 | #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX 85 | // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number 86 | // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... 87 | #define EPNUM_AUDIO 0x03 88 | #else 89 | #define EPNUM_AUDIO 0x01 90 | #endif 91 | 92 | uint8_t const desc_configuration[] = 93 | { 94 | // Interface count, string index, total length, attribute, power in mA 95 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), 96 | 97 | // Interface number, string index, EP Out & EP In address, EP size 98 | TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN) 99 | }; 100 | 101 | // Invoked when received GET CONFIGURATION DESCRIPTOR 102 | // Application return pointer to descriptor 103 | // Descriptor contents must exist long enough for transfer to complete 104 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 105 | { 106 | (void) index; // for multiple configurations 107 | return desc_configuration; 108 | } 109 | 110 | //--------------------------------------------------------------------+ 111 | // String Descriptors 112 | //--------------------------------------------------------------------+ 113 | 114 | // array of pointer to string descriptors 115 | char const* string_desc_arr [] = 116 | { 117 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 118 | "PaniRCorp", // 1: Manufacturer 119 | "MicNode", // 2: Product 120 | "123456", // 3: Serials, should use chip ID 121 | "UAC2", // 4: Audio Interface 122 | }; 123 | 124 | static uint16_t _desc_str[32]; 125 | 126 | // Invoked when received GET STRING DESCRIPTOR request 127 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 128 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 129 | { 130 | (void) langid; 131 | 132 | uint8_t chr_count; 133 | 134 | if ( index == 0) 135 | { 136 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 137 | chr_count = 1; 138 | }else 139 | { 140 | // Convert ASCII string into UTF-16 141 | 142 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 143 | 144 | const char* str = string_desc_arr[index]; 145 | 146 | // Cap at max char 147 | chr_count = strlen(str); 148 | if ( chr_count > 31 ) chr_count = 31; 149 | 150 | for(uint8_t i=0; ibRequest == AUDIO_CS_REQ_CUR); 83 | 84 | // Page 91 in UAC2 specification 85 | uint8_t channelNum = TU_U16_LOW(p_request->wValue); 86 | uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); 87 | uint8_t ep = TU_U16_LOW(p_request->wIndex); 88 | 89 | (void) channelNum; (void) ctrlSel; (void) ep; 90 | 91 | return false; // Yet not implemented 92 | } 93 | 94 | // Invoked when audio class specific set request received for an interface 95 | bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) 96 | { 97 | (void) rhport; 98 | (void) pBuff; 99 | 100 | // We do not support any set range requests here, only current value requests 101 | TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); 102 | 103 | // Page 91 in UAC2 specification 104 | uint8_t channelNum = TU_U16_LOW(p_request->wValue); 105 | uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); 106 | uint8_t itf = TU_U16_LOW(p_request->wIndex); 107 | 108 | (void) channelNum; (void) ctrlSel; (void) itf; 109 | 110 | return false; // Yet not implemented 111 | } 112 | 113 | // Invoked when audio class specific set request received for an entity 114 | bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) 115 | { 116 | (void) rhport; 117 | 118 | // Page 91 in UAC2 specification 119 | uint8_t channelNum = TU_U16_LOW(p_request->wValue); 120 | uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); 121 | uint8_t itf = TU_U16_LOW(p_request->wIndex); 122 | uint8_t entityID = TU_U16_HIGH(p_request->wIndex); 123 | 124 | (void) itf; 125 | 126 | // We do not support any set range requests here, only current value requests 127 | TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR); 128 | 129 | // If request is for our feature unit 130 | if ( entityID == 2 ) 131 | { 132 | switch ( ctrlSel ) 133 | { 134 | case AUDIO_FU_CTRL_MUTE: 135 | // Request uses format layout 1 136 | TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t)); 137 | 138 | mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur; 139 | 140 | TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum); 141 | 142 | return true; 143 | 144 | case AUDIO_FU_CTRL_VOLUME: 145 | // Request uses format layout 2 146 | TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t)); 147 | 148 | volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur; 149 | 150 | TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum); 151 | 152 | return true; 153 | 154 | // Unknown/Unsupported control 155 | default: 156 | TU_BREAKPOINT(); 157 | return false; 158 | } 159 | } 160 | return false; // Yet not implemented 161 | } 162 | 163 | // Invoked when audio class specific get request received for an EP 164 | bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request) 165 | { 166 | (void) rhport; 167 | 168 | // Page 91 in UAC2 specification 169 | uint8_t channelNum = TU_U16_LOW(p_request->wValue); 170 | uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); 171 | uint8_t ep = TU_U16_LOW(p_request->wIndex); 172 | 173 | (void) channelNum; (void) ctrlSel; (void) ep; 174 | 175 | // return tud_control_xfer(rhport, p_request, &tmp, 1); 176 | 177 | return false; // Yet not implemented 178 | } 179 | 180 | // Invoked when audio class specific get request received for an interface 181 | bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) 182 | { 183 | (void) rhport; 184 | 185 | // Page 91 in UAC2 specification 186 | uint8_t channelNum = TU_U16_LOW(p_request->wValue); 187 | uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); 188 | uint8_t itf = TU_U16_LOW(p_request->wIndex); 189 | 190 | (void) channelNum; (void) ctrlSel; (void) itf; 191 | 192 | return false; // Yet not implemented 193 | } 194 | 195 | // Invoked when audio class specific get request received for an entity 196 | bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) 197 | { 198 | (void) rhport; 199 | 200 | // Page 91 in UAC2 specification 201 | uint8_t channelNum = TU_U16_LOW(p_request->wValue); 202 | uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); 203 | // uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since we have only one audio function implemented, we do not need the itf value 204 | uint8_t entityID = TU_U16_HIGH(p_request->wIndex); 205 | 206 | // Input terminal (Microphone input) 207 | if (entityID == 1) 208 | { 209 | switch (ctrlSel) 210 | { 211 | case AUDIO_TE_CTRL_CONNECTOR:; 212 | // The terminal connector control only has a get request with only the CUR attribute. 213 | 214 | audio_desc_channel_cluster_t ret; 215 | 216 | // Those are dummy values for now 217 | ret.bNrChannels = 1; 218 | ret.bmChannelConfig = 0; 219 | ret.iChannelNames = 0; 220 | 221 | TU_LOG2(" Get terminal connector\r\n"); 222 | 223 | return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*)&ret, sizeof(ret)); 224 | 225 | // Unknown/Unsupported control selector 226 | default: TU_BREAKPOINT(); return false; 227 | } 228 | } 229 | 230 | // Feature unit 231 | if (entityID == 2) 232 | { 233 | switch (ctrlSel) 234 | { 235 | case AUDIO_FU_CTRL_MUTE: 236 | // Audio control mute cur parameter block consists of only one byte - we thus can send it right away 237 | // There does not exist a range parameter block for mute 238 | TU_LOG2(" Get Mute of channel: %u\r\n", channelNum); 239 | return tud_control_xfer(rhport, p_request, &mute[channelNum], 1); 240 | 241 | case AUDIO_FU_CTRL_VOLUME: 242 | 243 | switch (p_request->bRequest) 244 | { 245 | case AUDIO_CS_REQ_CUR: 246 | TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); 247 | return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum])); 248 | case AUDIO_CS_REQ_RANGE: 249 | TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum); 250 | 251 | // Copy values - only for testing - better is version below 252 | audio_control_range_2_n_t(1) ret; 253 | 254 | ret.wNumSubRanges = 1; 255 | ret.subrange[0].bMin = -90; // -90 dB 256 | ret.subrange[0].bMax = 90; // +90 dB 257 | ret.subrange[0].bRes = 1; // 1 dB steps 258 | 259 | return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*)&ret, sizeof(ret)); 260 | 261 | // Unknown/Unsupported control 262 | default: TU_BREAKPOINT(); return false; 263 | } 264 | 265 | // Unknown/Unsupported control 266 | default: TU_BREAKPOINT(); return false; 267 | } 268 | } 269 | 270 | // Clock Source unit 271 | if (entityID == 4) 272 | { 273 | switch (ctrlSel) 274 | { 275 | case AUDIO_CS_CTRL_SAM_FREQ: 276 | 277 | // channelNum is always zero in this case 278 | 279 | switch (p_request->bRequest) 280 | { 281 | case AUDIO_CS_REQ_CUR: 282 | TU_LOG2(" Get Sample Freq.\r\n"); 283 | return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq)); 284 | case AUDIO_CS_REQ_RANGE: 285 | TU_LOG2(" Get Sample Freq. range\r\n"); 286 | return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng)); 287 | 288 | // Unknown/Unsupported control 289 | default: TU_BREAKPOINT(); return false; 290 | } 291 | 292 | case AUDIO_CS_CTRL_CLK_VALID: 293 | // Only cur attribute exists for this request 294 | TU_LOG2(" Get Sample Freq. valid\r\n"); 295 | return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid)); 296 | 297 | // Unknown/Unsupported control 298 | default: TU_BREAKPOINT(); return false; 299 | } 300 | } 301 | 302 | TU_LOG2(" Unsupported entity: %d\r\n", entityID); 303 | return false; // Yet not implemented 304 | } 305 | 306 | bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) 307 | { 308 | (void) rhport; 309 | (void) itf; 310 | (void) ep_in; 311 | (void) cur_alt_setting; 312 | 313 | if (usb_microphone_tx_ready_handler) 314 | { 315 | usb_microphone_tx_ready_handler(); 316 | } 317 | 318 | return true; 319 | } 320 | 321 | bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) 322 | { 323 | (void) rhport; 324 | (void) n_bytes_copied; 325 | (void) itf; 326 | (void) ep_in; 327 | (void) cur_alt_setting; 328 | 329 | return true; 330 | } 331 | 332 | bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request) 333 | { 334 | (void) rhport; 335 | (void) p_request; 336 | 337 | return true; 338 | } 339 | -------------------------------------------------------------------------------- /examples/usb_microphone/usb_microphone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _USB_MICROPHONE_H_ 9 | #define _USB_MICROPHONE_H_ 10 | 11 | #include "tusb.h" 12 | 13 | #ifndef SAMPLE_RATE 14 | #define SAMPLE_RATE ((CFG_TUD_AUDIO_EP_SZ_IN / 2) - 1) * 1000 15 | #endif 16 | 17 | #ifndef SAMPLE_BUFFER_SIZE 18 | #define SAMPLE_BUFFER_SIZE ((CFG_TUD_AUDIO_EP_SZ_IN/2) - 1) 19 | #endif 20 | 21 | typedef void (*usb_microphone_tx_ready_handler_t)(void); 22 | 23 | void usb_microphone_init(); 24 | void usb_microphone_set_tx_ready_handler(usb_microphone_tx_ready_handler_t handler); 25 | void usb_microphone_task(); 26 | uint16_t usb_microphone_write(const void * data, uint16_t len); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /src/OpenPDM2PCM/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | Repository toolbox 206 | Import into Mbed Studio 207 | Export to desktop IDE 208 | Repository details 209 | Type: Library 210 | Created: 27 Apr 2017 211 | Imports: 122 212 | Forks: 1 213 | Commits: 27 214 | Dependents: 4 215 | Dependencies: 3 216 | Followers: 387 217 | The code in this repository is Apache licensed. 218 | Components 219 | X-NUCLEO-CCA02M1 Digital MEMS Microphones Expansion Board. 220 | -------------------------------------------------------------------------------- /src/OpenPDM2PCM/OpenPDMFilter.c: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * @file OpenPDMFilter.c 4 | * @author CL 5 | * @version V1.0.0 6 | * @date 9-September-2015 7 | * @brief Open PDM audio software decoding Library. 8 | * This Library is used to decode and reconstruct the audio signal 9 | * produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx). 10 | ******************************************************************************* 11 | * @attention 12 | * 13 | *

© COPYRIGHT 2018 STMicroelectronics

14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | ******************************************************************************* 27 | */ 28 | 29 | 30 | /* Includes ------------------------------------------------------------------*/ 31 | 32 | #include "OpenPDMFilter.h" 33 | 34 | 35 | /* Variables -----------------------------------------------------------------*/ 36 | 37 | uint32_t div_const = 0; 38 | int64_t sub_const = 0; 39 | uint32_t sinc[DECIMATION_MAX * SINCN]; 40 | uint32_t sinc1[DECIMATION_MAX]; 41 | uint32_t sinc2[DECIMATION_MAX * 2]; 42 | uint32_t coef[SINCN][DECIMATION_MAX]; 43 | #ifdef USE_LUT 44 | int32_t lut[256][DECIMATION_MAX / 8][SINCN]; 45 | #endif 46 | 47 | 48 | /* Functions -----------------------------------------------------------------*/ 49 | 50 | #ifdef USE_LUT 51 | int32_t filter_table_mono_64(uint8_t *data, uint8_t sincn) 52 | { 53 | return (int32_t) 54 | lut[data[0]][0][sincn] + 55 | lut[data[1]][1][sincn] + 56 | lut[data[2]][2][sincn] + 57 | lut[data[3]][3][sincn] + 58 | lut[data[4]][4][sincn] + 59 | lut[data[5]][5][sincn] + 60 | lut[data[6]][6][sincn] + 61 | lut[data[7]][7][sincn]; 62 | } 63 | int32_t filter_table_stereo_64(uint8_t *data, uint8_t sincn) 64 | { 65 | return (int32_t) 66 | lut[data[0]][0][sincn] + 67 | lut[data[2]][1][sincn] + 68 | lut[data[4]][2][sincn] + 69 | lut[data[6]][3][sincn] + 70 | lut[data[8]][4][sincn] + 71 | lut[data[10]][5][sincn] + 72 | lut[data[12]][6][sincn] + 73 | lut[data[14]][7][sincn]; 74 | } 75 | int32_t filter_table_mono_128(uint8_t *data, uint8_t sincn) 76 | { 77 | return (int32_t) 78 | lut[data[0]][0][sincn] + 79 | lut[data[1]][1][sincn] + 80 | lut[data[2]][2][sincn] + 81 | lut[data[3]][3][sincn] + 82 | lut[data[4]][4][sincn] + 83 | lut[data[5]][5][sincn] + 84 | lut[data[6]][6][sincn] + 85 | lut[data[7]][7][sincn] + 86 | lut[data[8]][8][sincn] + 87 | lut[data[9]][9][sincn] + 88 | lut[data[10]][10][sincn] + 89 | lut[data[11]][11][sincn] + 90 | lut[data[12]][12][sincn] + 91 | lut[data[13]][13][sincn] + 92 | lut[data[14]][14][sincn] + 93 | lut[data[15]][15][sincn]; 94 | } 95 | int32_t filter_table_stereo_128(uint8_t *data, uint8_t sincn) 96 | { 97 | return (int32_t) 98 | lut[data[0]][0][sincn] + 99 | lut[data[2]][1][sincn] + 100 | lut[data[4]][2][sincn] + 101 | lut[data[6]][3][sincn] + 102 | lut[data[8]][4][sincn] + 103 | lut[data[10]][5][sincn] + 104 | lut[data[12]][6][sincn] + 105 | lut[data[14]][7][sincn] + 106 | lut[data[16]][8][sincn] + 107 | lut[data[18]][9][sincn] + 108 | lut[data[20]][10][sincn] + 109 | lut[data[22]][11][sincn] + 110 | lut[data[24]][12][sincn] + 111 | lut[data[26]][13][sincn] + 112 | lut[data[28]][14][sincn] + 113 | lut[data[30]][15][sincn]; 114 | } 115 | int32_t (* filter_tables_64[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_64, filter_table_stereo_64}; 116 | int32_t (* filter_tables_128[2]) (uint8_t *data, uint8_t sincn) = {filter_table_mono_128, filter_table_stereo_128}; 117 | #else 118 | int32_t filter_table(uint8_t *data, uint8_t sincn, TPDMFilter_InitStruct *param) 119 | { 120 | uint8_t c, i; 121 | uint16_t data_index = 0; 122 | uint32_t *coef_p = &coef[sincn][0]; 123 | int32_t F = 0; 124 | uint8_t decimation = param->Decimation; 125 | uint8_t channels = param->In_MicChannels; 126 | 127 | for (i = 0; i < decimation; i += 8) { 128 | c = data[data_index]; 129 | F += ((c >> 7) ) * coef_p[i ] + 130 | ((c >> 6) & 0x01) * coef_p[i + 1] + 131 | ((c >> 5) & 0x01) * coef_p[i + 2] + 132 | ((c >> 4) & 0x01) * coef_p[i + 3] + 133 | ((c >> 3) & 0x01) * coef_p[i + 4] + 134 | ((c >> 2) & 0x01) * coef_p[i + 5] + 135 | ((c >> 1) & 0x01) * coef_p[i + 6] + 136 | ((c ) & 0x01) * coef_p[i + 7]; 137 | data_index += channels; 138 | } 139 | return F; 140 | } 141 | #endif 142 | 143 | void convolve(uint32_t Signal[/* SignalLen */], unsigned short SignalLen, 144 | uint32_t Kernel[/* KernelLen */], unsigned short KernelLen, 145 | uint32_t Result[/* SignalLen + KernelLen - 1 */]) 146 | { 147 | uint16_t n; 148 | 149 | for (n = 0; n < SignalLen + KernelLen - 1; n++) 150 | { 151 | unsigned short kmin, kmax, k; 152 | 153 | Result[n] = 0; 154 | 155 | kmin = (n >= KernelLen - 1) ? n - (KernelLen - 1) : 0; 156 | kmax = (n < SignalLen - 1) ? n : SignalLen - 1; 157 | 158 | for (k = kmin; k <= kmax; k++) { 159 | Result[n] += Signal[k] * Kernel[n - k]; 160 | } 161 | } 162 | } 163 | 164 | void Open_PDM_Filter_Init(TPDMFilter_InitStruct *Param) 165 | { 166 | uint16_t i, j; 167 | int64_t sum = 0; 168 | 169 | uint8_t decimation = Param->Decimation; 170 | 171 | for (i = 0; i < SINCN; i++) { 172 | Param->Coef[i] = 0; 173 | Param->bit[i] = 0; 174 | } 175 | for (i = 0; i < decimation; i++) { 176 | sinc1[i] = 1; 177 | } 178 | 179 | Param->OldOut = Param->OldIn = Param->OldZ = 0; 180 | Param->LP_ALFA = (Param->LP_HZ != 0 ? (uint16_t) (Param->LP_HZ * 256 / (Param->LP_HZ + Param->Fs / (2 * 3.14159))) : 0); 181 | Param->HP_ALFA = (Param->HP_HZ != 0 ? (uint16_t) (Param->Fs * 256 / (2 * 3.14159 * Param->HP_HZ + Param->Fs)) : 0); 182 | 183 | Param->FilterLen = decimation * SINCN; 184 | sinc[0] = 0; 185 | sinc[decimation * SINCN - 1] = 0; 186 | convolve(sinc1, decimation, sinc1, decimation, sinc2); 187 | convolve(sinc2, decimation * 2 - 1, sinc1, decimation, &sinc[1]); 188 | for(j = 0; j < SINCN; j++) { 189 | for (i = 0; i < decimation; i++) { 190 | coef[j][i] = sinc[j * decimation + i]; 191 | sum += sinc[j * decimation + i]; 192 | } 193 | } 194 | 195 | sub_const = sum >> 1; 196 | div_const = sub_const * Param->MaxVolume / 32768 / FILTER_GAIN; 197 | div_const = (div_const == 0 ? 1 : div_const); 198 | 199 | #ifdef USE_LUT 200 | /* Look-Up Table. */ 201 | uint16_t c, d, s; 202 | for (s = 0; s < SINCN; s++) 203 | { 204 | uint32_t *coef_p = &coef[s][0]; 205 | for (c = 0; c < 256; c++) 206 | for (d = 0; d < decimation / 8; d++) 207 | lut[c][d][s] = ((c >> 7) ) * coef_p[d * 8 ] + 208 | ((c >> 6) & 0x01) * coef_p[d * 8 + 1] + 209 | ((c >> 5) & 0x01) * coef_p[d * 8 + 2] + 210 | ((c >> 4) & 0x01) * coef_p[d * 8 + 3] + 211 | ((c >> 3) & 0x01) * coef_p[d * 8 + 4] + 212 | ((c >> 2) & 0x01) * coef_p[d * 8 + 5] + 213 | ((c >> 1) & 0x01) * coef_p[d * 8 + 6] + 214 | ((c ) & 0x01) * coef_p[d * 8 + 7]; 215 | } 216 | #endif 217 | } 218 | 219 | void Open_PDM_Filter_64(uint8_t* data, uint16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param) 220 | { 221 | uint8_t i, data_out_index; 222 | uint8_t channels = Param->In_MicChannels; 223 | uint8_t data_inc = ((DECIMATION_MAX >> 4) * channels); 224 | int64_t Z, Z0, Z1, Z2; 225 | int64_t OldOut, OldIn, OldZ; 226 | 227 | OldOut = Param->OldOut; 228 | OldIn = Param->OldIn; 229 | OldZ = Param->OldZ; 230 | 231 | #ifdef USE_LUT 232 | uint8_t j = channels - 1; 233 | #endif 234 | 235 | for (i = 0, data_out_index = 0; i < Param->Fs / 1000; i++, data_out_index += channels) { 236 | #ifdef USE_LUT 237 | Z0 = filter_tables_64[j](data, 0); 238 | Z1 = filter_tables_64[j](data, 1); 239 | Z2 = filter_tables_64[j](data, 2); 240 | #else 241 | Z0 = filter_table(data, 0, Param); 242 | Z1 = filter_table(data, 1, Param); 243 | Z2 = filter_table(data, 2, Param); 244 | #endif 245 | 246 | Z = Param->Coef[1] + Z2 - sub_const; 247 | Param->Coef[1] = Param->Coef[0] + Z1; 248 | Param->Coef[0] = Z0; 249 | 250 | OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8; 251 | OldIn = Z; 252 | OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8; 253 | 254 | Z = OldZ * volume; 255 | Z = RoundDiv(Z, div_const); 256 | Z = SaturaLH(Z, -32700, 32700); 257 | 258 | dataOut[data_out_index] = Z; 259 | data += data_inc; 260 | } 261 | 262 | Param->OldOut = OldOut; 263 | Param->OldIn = OldIn; 264 | Param->OldZ = OldZ; 265 | } 266 | 267 | void Open_PDM_Filter_128(uint8_t* data, uint16_t* dataOut, uint16_t volume, TPDMFilter_InitStruct *Param) 268 | { 269 | uint8_t i, data_out_index; 270 | uint8_t channels = Param->In_MicChannels; 271 | uint8_t data_inc = ((DECIMATION_MAX >> 3) * channels); 272 | int64_t Z, Z0, Z1, Z2; 273 | int64_t OldOut, OldIn, OldZ; 274 | 275 | OldOut = Param->OldOut; 276 | OldIn = Param->OldIn; 277 | OldZ = Param->OldZ; 278 | 279 | #ifdef USE_LUT 280 | uint8_t j = channels - 1; 281 | #endif 282 | 283 | for (i = 0, data_out_index = 0; i < Param->Fs / 1000; i++, data_out_index += channels) { 284 | #ifdef USE_LUT 285 | Z0 = filter_tables_128[j](data, 0); 286 | Z1 = filter_tables_128[j](data, 1); 287 | Z2 = filter_tables_128[j](data, 2); 288 | #else 289 | Z0 = filter_table(data, 0, Param); 290 | Z1 = filter_table(data, 1, Param); 291 | Z2 = filter_table(data, 2, Param); 292 | #endif 293 | 294 | Z = Param->Coef[1] + Z2 - sub_const; 295 | Param->Coef[1] = Param->Coef[0] + Z1; 296 | Param->Coef[0] = Z0; 297 | 298 | OldOut = (Param->HP_ALFA * (OldOut + Z - OldIn)) >> 8; 299 | OldIn = Z; 300 | OldZ = ((256 - Param->LP_ALFA) * OldZ + Param->LP_ALFA * OldOut) >> 8; 301 | 302 | Z = OldZ * volume; 303 | Z = RoundDiv(Z, div_const); 304 | Z = SaturaLH(Z, -32700, 32700); 305 | 306 | dataOut[data_out_index] = Z; 307 | data += data_inc; 308 | } 309 | 310 | Param->OldOut = OldOut; 311 | Param->OldIn = OldIn; 312 | Param->OldZ = OldZ; 313 | } 314 | 315 | -------------------------------------------------------------------------------- /src/OpenPDM2PCM/OpenPDMFilter.h: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * @file OpenPDMFilter.h 4 | * @author CL 5 | * @version V1.0.0 6 | * @date 9-September-2015 7 | * @brief Header file for Open PDM audio software decoding Library. 8 | * This Library is used to decode and reconstruct the audio signal 9 | * produced by ST MEMS microphone (MP45Dxxx, MP34Dxxx). 10 | ******************************************************************************* 11 | * @attention 12 | * 13 | *

© COPYRIGHT 2018 STMicroelectronics

14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | ******************************************************************************* 27 | */ 28 | 29 | 30 | /* Define to prevent recursive inclusion -------------------------------------*/ 31 | 32 | #ifndef __OPENPDMFILTER_H 33 | #define __OPENPDMFILTER_H 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | 40 | /* Includes ------------------------------------------------------------------*/ 41 | 42 | #include 43 | 44 | 45 | /* Definitions ---------------------------------------------------------------*/ 46 | 47 | /* 48 | * Enable to use a Look-Up Table to improve performances while using more FLASH 49 | * and RAM memory. 50 | * Note: Without Look-Up Table up to stereo@16KHz configuration is supported. 51 | */ 52 | #define USE_LUT 53 | 54 | #define SINCN 3 55 | #define DECIMATION_MAX 128 56 | #ifdef PICO_BUILD 57 | #define FILTER_GAIN Param->Gain 58 | #else 59 | #define FILTER_GAIN 16 60 | #endif 61 | 62 | #define HTONS(A) ((((uint16_t)(A) & 0xff00) >> 8) | \ 63 | (((uint16_t)(A) & 0x00ff) << 8)) 64 | #define RoundDiv(a, b) (((a)>0)?(((a)+(b)/2)/(b)):(((a)-(b)/2)/(b))) 65 | #define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N))) 66 | 67 | 68 | /* Types ---------------------------------------------------------------------*/ 69 | 70 | typedef struct { 71 | /* Public */ 72 | float LP_HZ; 73 | float HP_HZ; 74 | uint16_t Fs; 75 | uint8_t In_MicChannels; 76 | uint8_t Out_MicChannels; 77 | uint8_t Decimation; 78 | uint8_t MaxVolume; 79 | #ifdef PICO_BUILD 80 | uint8_t Gain; 81 | #endif 82 | /* Private */ 83 | uint32_t Coef[SINCN]; 84 | uint16_t FilterLen; 85 | int64_t OldOut, OldIn, OldZ; 86 | uint16_t LP_ALFA; 87 | uint16_t HP_ALFA; 88 | uint16_t bit[5]; 89 | uint16_t byte; 90 | } TPDMFilter_InitStruct; 91 | 92 | 93 | /* Exported functions ------------------------------------------------------- */ 94 | 95 | void Open_PDM_Filter_Init(TPDMFilter_InitStruct *init_struct); 96 | void Open_PDM_Filter_64(uint8_t* data, uint16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct); 97 | void Open_PDM_Filter_128(uint8_t* data, uint16_t* data_out, uint16_t mic_gain, TPDMFilter_InitStruct *init_struct); 98 | 99 | #ifdef __cplusplus 100 | } 101 | #endif 102 | 103 | #endif // __OPENPDMFILTER_H 104 | 105 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 106 | 107 | -------------------------------------------------------------------------------- /src/analog_microphone.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "hardware/adc.h" 12 | #include "hardware/clocks.h" 13 | #include "hardware/dma.h" 14 | #include "hardware/irq.h" 15 | 16 | #include "pico/analog_microphone.h" 17 | 18 | #define ANALOG_RAW_BUFFER_COUNT 2 19 | 20 | static struct { 21 | struct analog_microphone_config config; 22 | int dma_channel; 23 | uint16_t* raw_buffer[ANALOG_RAW_BUFFER_COUNT]; 24 | volatile int raw_buffer_write_index; 25 | volatile int raw_buffer_read_index; 26 | uint buffer_size; 27 | int16_t bias; 28 | uint dma_irq; 29 | analog_samples_ready_handler_t samples_ready_handler; 30 | } analog_mic; 31 | 32 | static void analog_dma_handler(); 33 | 34 | int analog_microphone_init(const struct analog_microphone_config* config) { 35 | memset(&analog_mic, 0x00, sizeof(analog_mic)); 36 | memcpy(&analog_mic.config, config, sizeof(analog_mic.config)); 37 | 38 | if (config->gpio < 26 || config->gpio > 29) { 39 | return -1; 40 | } 41 | 42 | size_t raw_buffer_size = config->sample_buffer_size * sizeof(analog_mic.raw_buffer[0][0]); 43 | 44 | analog_mic.buffer_size = config->sample_buffer_size; 45 | analog_mic.bias = ((int16_t)((config->bias_voltage * 4095) / 3.3)); 46 | 47 | for (int i = 0; i < ANALOG_RAW_BUFFER_COUNT; i++) { 48 | analog_mic.raw_buffer[i] = malloc(raw_buffer_size); 49 | if (analog_mic.raw_buffer[i] == NULL) { 50 | analog_microphone_deinit(); 51 | 52 | return -1; 53 | } 54 | } 55 | 56 | analog_mic.dma_channel = dma_claim_unused_channel(true); 57 | if (analog_mic.dma_channel < 0) { 58 | analog_microphone_deinit(); 59 | 60 | return -1; 61 | } 62 | 63 | float clk_div = (clock_get_hz(clk_adc) / (1.0 * config->sample_rate)) - 1; 64 | 65 | dma_channel_config dma_channel_cfg = dma_channel_get_default_config(analog_mic.dma_channel); 66 | 67 | channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_16); 68 | channel_config_set_read_increment(&dma_channel_cfg, false); 69 | channel_config_set_write_increment(&dma_channel_cfg, true); 70 | channel_config_set_dreq(&dma_channel_cfg, DREQ_ADC); 71 | 72 | analog_mic.dma_irq = DMA_IRQ_0; 73 | 74 | dma_channel_configure( 75 | analog_mic.dma_channel, 76 | &dma_channel_cfg, 77 | analog_mic.raw_buffer[0], 78 | &adc_hw->fifo, 79 | analog_mic.buffer_size, 80 | false 81 | ); 82 | 83 | adc_gpio_init(config->gpio); 84 | 85 | adc_init(); 86 | adc_select_input(config->gpio - 26); 87 | adc_fifo_setup( 88 | true, // Write each completed conversion to the sample FIFO 89 | true, // Enable DMA data request (DREQ) 90 | 1, // DREQ (and IRQ) asserted when at least 1 sample present 91 | false, // We won't see the ERR bit because of 8 bit reads; disable. 92 | false // Don't shift each sample to 8 bits when pushing to FIFO 93 | ); 94 | 95 | adc_set_clkdiv(clk_div); 96 | } 97 | 98 | void analog_microphone_deinit() { 99 | for (int i = 0; i < ANALOG_RAW_BUFFER_COUNT; i++) { 100 | if (analog_mic.raw_buffer[i]) { 101 | free(analog_mic.raw_buffer[i]); 102 | 103 | analog_mic.raw_buffer[i] = NULL; 104 | } 105 | } 106 | 107 | if (analog_mic.dma_channel > -1) { 108 | dma_channel_unclaim(analog_mic.dma_channel); 109 | 110 | analog_mic.dma_channel = -1; 111 | } 112 | } 113 | 114 | int analog_microphone_start() { 115 | irq_set_enabled(analog_mic.dma_irq, true); 116 | irq_set_exclusive_handler(analog_mic.dma_irq, analog_dma_handler); 117 | 118 | if (analog_mic.dma_irq == DMA_IRQ_0) { 119 | dma_channel_set_irq0_enabled(analog_mic.dma_channel, true); 120 | } else if (analog_mic.dma_irq == DMA_IRQ_1) { 121 | dma_channel_set_irq1_enabled(analog_mic.dma_channel, true); 122 | } else { 123 | return -1; 124 | } 125 | 126 | analog_mic.raw_buffer_write_index = 0; 127 | analog_mic.raw_buffer_read_index = 0; 128 | 129 | dma_channel_transfer_to_buffer_now( 130 | analog_mic.dma_channel, 131 | analog_mic.raw_buffer[0], 132 | analog_mic.buffer_size 133 | ); 134 | 135 | adc_run(true); // start running the adc 136 | } 137 | 138 | void analog_microphone_stop() { 139 | adc_run(false); // stop running the adc 140 | 141 | dma_channel_abort(analog_mic.dma_channel); 142 | 143 | if (analog_mic.dma_irq == DMA_IRQ_0) { 144 | dma_channel_set_irq0_enabled(analog_mic.dma_channel, false); 145 | } else if (analog_mic.dma_irq == DMA_IRQ_1) { 146 | dma_channel_set_irq1_enabled(analog_mic.dma_channel, false); 147 | } 148 | 149 | irq_set_enabled(analog_mic.dma_irq, false); 150 | } 151 | 152 | static void analog_dma_handler() { 153 | // clear IRQ 154 | if (analog_mic.dma_irq == DMA_IRQ_0) { 155 | dma_hw->ints0 = (1u << analog_mic.dma_channel); 156 | } else if (analog_mic.dma_irq == DMA_IRQ_1) { 157 | dma_hw->ints1 = (1u << analog_mic.dma_channel); 158 | } 159 | 160 | // get the current buffer index 161 | analog_mic.raw_buffer_read_index = analog_mic.raw_buffer_write_index; 162 | 163 | // get the next capture index to send the dma to start 164 | analog_mic.raw_buffer_write_index = (analog_mic.raw_buffer_write_index + 1) % ANALOG_RAW_BUFFER_COUNT; 165 | 166 | // give the channel a new buffer to write to and re-trigger it 167 | dma_channel_transfer_to_buffer_now( 168 | analog_mic.dma_channel, 169 | analog_mic.raw_buffer[analog_mic.raw_buffer_write_index], 170 | analog_mic.buffer_size 171 | ); 172 | 173 | if (analog_mic.samples_ready_handler) { 174 | analog_mic.samples_ready_handler(); 175 | } 176 | } 177 | 178 | void analog_microphone_set_samples_ready_handler(analog_samples_ready_handler_t handler) { 179 | analog_mic.samples_ready_handler = handler; 180 | } 181 | 182 | int analog_microphone_read(int16_t* buffer, size_t samples) { 183 | if (samples > analog_mic.config.sample_buffer_size) { 184 | samples = analog_mic.config.sample_buffer_size; 185 | } 186 | 187 | if (analog_mic.raw_buffer_write_index == analog_mic.raw_buffer_read_index) { 188 | return 0; 189 | } 190 | 191 | uint16_t* in = analog_mic.raw_buffer[analog_mic.raw_buffer_read_index]; 192 | int16_t* out = buffer; 193 | int16_t bias = analog_mic.bias; 194 | 195 | analog_mic.raw_buffer_read_index++; 196 | 197 | for (int i = 0; i < samples; i++) { 198 | *out++ = *in++ - bias; 199 | } 200 | 201 | return samples; 202 | } 203 | -------------------------------------------------------------------------------- /src/include/pico/analog_microphone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _PICO_ANALOG_MICROPHONE_H_ 9 | #define _PICO_ANALOG_MICROPHONE_H_ 10 | 11 | typedef void (*analog_samples_ready_handler_t)(void); 12 | 13 | struct analog_microphone_config { 14 | uint gpio; 15 | float bias_voltage; 16 | uint sample_rate; 17 | uint sample_buffer_size; 18 | }; 19 | 20 | int analog_microphone_init(const struct analog_microphone_config* config); 21 | void analog_microphone_deinit(); 22 | 23 | int analog_microphone_start(); 24 | void analog_microphone_stop(); 25 | 26 | void analog_microphone_set_samples_ready_handler(analog_samples_ready_handler_t handler); 27 | 28 | int analog_microphone_read(int16_t* buffer, size_t samples); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/include/pico/pdm_microphone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _PICO_PDM_MICROPHONE_H_ 9 | #define _PICO_PDM_MICROPHONE_H_ 10 | 11 | #include "hardware/pio.h" 12 | 13 | typedef void (*pdm_samples_ready_handler_t)(void); 14 | 15 | struct pdm_microphone_config { 16 | uint gpio_data; 17 | uint gpio_clk; 18 | PIO pio; 19 | uint pio_sm; 20 | uint sample_rate; 21 | uint sample_buffer_size; 22 | }; 23 | 24 | int pdm_microphone_init(const struct pdm_microphone_config* config); 25 | void pdm_microphone_deinit(); 26 | 27 | int pdm_microphone_start(); 28 | void pdm_microphone_stop(); 29 | 30 | void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler); 31 | void pdm_microphone_set_filter_max_volume(uint8_t max_volume); 32 | void pdm_microphone_set_filter_gain(uint8_t gain); 33 | void pdm_microphone_set_filter_volume(uint16_t volume); 34 | 35 | int pdm_microphone_read(int16_t* buffer, size_t samples); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/pdm_microphone.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "hardware/clocks.h" 12 | #include "hardware/dma.h" 13 | #include "hardware/irq.h" 14 | 15 | #include "OpenPDM2PCM/OpenPDMFilter.h" 16 | 17 | #include "pdm_microphone.pio.h" 18 | 19 | #include "pico/pdm_microphone.h" 20 | 21 | #define PDM_DECIMATION 64 22 | #define PDM_RAW_BUFFER_COUNT 2 23 | 24 | static struct { 25 | struct pdm_microphone_config config; 26 | int dma_channel; 27 | uint8_t* raw_buffer[PDM_RAW_BUFFER_COUNT]; 28 | volatile int raw_buffer_write_index; 29 | volatile int raw_buffer_read_index; 30 | uint raw_buffer_size; 31 | uint dma_irq; 32 | TPDMFilter_InitStruct filter; 33 | uint16_t filter_volume; 34 | pdm_samples_ready_handler_t samples_ready_handler; 35 | } pdm_mic; 36 | 37 | static void pdm_dma_handler(); 38 | 39 | int pdm_microphone_init(const struct pdm_microphone_config* config) { 40 | memset(&pdm_mic, 0x00, sizeof(pdm_mic)); 41 | memcpy(&pdm_mic.config, config, sizeof(pdm_mic.config)); 42 | 43 | if (config->sample_buffer_size % (config->sample_rate / 1000)) { 44 | return -1; 45 | } 46 | 47 | pdm_mic.raw_buffer_size = config->sample_buffer_size * (PDM_DECIMATION / 8); 48 | 49 | for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) { 50 | pdm_mic.raw_buffer[i] = malloc(pdm_mic.raw_buffer_size); 51 | if (pdm_mic.raw_buffer[i] == NULL) { 52 | pdm_microphone_deinit(); 53 | 54 | return -1; 55 | } 56 | } 57 | 58 | pdm_mic.dma_channel = dma_claim_unused_channel(true); 59 | if (pdm_mic.dma_channel < 0) { 60 | pdm_microphone_deinit(); 61 | 62 | return -1; 63 | } 64 | 65 | uint pio_sm_offset = pio_add_program(config->pio, &pdm_microphone_data_program); 66 | 67 | float clk_div = clock_get_hz(clk_sys) / (config->sample_rate * PDM_DECIMATION * 4.0); 68 | 69 | pdm_microphone_data_init( 70 | config->pio, 71 | config->pio_sm, 72 | pio_sm_offset, 73 | clk_div, 74 | config->gpio_data, 75 | config->gpio_clk 76 | ); 77 | 78 | dma_channel_config dma_channel_cfg = dma_channel_get_default_config(pdm_mic.dma_channel); 79 | 80 | channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_8); 81 | channel_config_set_read_increment(&dma_channel_cfg, false); 82 | channel_config_set_write_increment(&dma_channel_cfg, true); 83 | channel_config_set_dreq(&dma_channel_cfg, pio_get_dreq(config->pio, config->pio_sm, false)); 84 | 85 | pdm_mic.dma_irq = DMA_IRQ_0; 86 | 87 | dma_channel_configure( 88 | pdm_mic.dma_channel, 89 | &dma_channel_cfg, 90 | pdm_mic.raw_buffer[0], 91 | &config->pio->rxf[config->pio_sm], 92 | pdm_mic.raw_buffer_size, 93 | false 94 | ); 95 | 96 | pdm_mic.filter.Fs = config->sample_rate; 97 | pdm_mic.filter.LP_HZ = config->sample_rate / 2; 98 | pdm_mic.filter.HP_HZ = 10; 99 | pdm_mic.filter.In_MicChannels = 1; 100 | pdm_mic.filter.Out_MicChannels = 1; 101 | pdm_mic.filter.Decimation = PDM_DECIMATION; 102 | pdm_mic.filter.MaxVolume = 64; 103 | pdm_mic.filter.Gain = 16; 104 | 105 | pdm_mic.filter_volume = pdm_mic.filter.MaxVolume; 106 | } 107 | 108 | void pdm_microphone_deinit() { 109 | for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) { 110 | if (pdm_mic.raw_buffer[i]) { 111 | free(pdm_mic.raw_buffer[i]); 112 | 113 | pdm_mic.raw_buffer[i] = NULL; 114 | } 115 | } 116 | 117 | if (pdm_mic.dma_channel > -1) { 118 | dma_channel_unclaim(pdm_mic.dma_channel); 119 | 120 | pdm_mic.dma_channel = -1; 121 | } 122 | } 123 | 124 | int pdm_microphone_start() { 125 | irq_set_enabled(pdm_mic.dma_irq, true); 126 | irq_set_exclusive_handler(pdm_mic.dma_irq, pdm_dma_handler); 127 | 128 | if (pdm_mic.dma_irq == DMA_IRQ_0) { 129 | dma_channel_set_irq0_enabled(pdm_mic.dma_channel, true); 130 | } else if (pdm_mic.dma_irq == DMA_IRQ_1) { 131 | dma_channel_set_irq1_enabled(pdm_mic.dma_channel, true); 132 | } else { 133 | return -1; 134 | } 135 | 136 | Open_PDM_Filter_Init(&pdm_mic.filter); 137 | 138 | pio_sm_set_enabled( 139 | pdm_mic.config.pio, 140 | pdm_mic.config.pio_sm, 141 | true 142 | ); 143 | 144 | pdm_mic.raw_buffer_write_index = 0; 145 | pdm_mic.raw_buffer_read_index = 0; 146 | 147 | dma_channel_transfer_to_buffer_now( 148 | pdm_mic.dma_channel, 149 | pdm_mic.raw_buffer[0], 150 | pdm_mic.raw_buffer_size 151 | ); 152 | 153 | pio_sm_set_enabled( 154 | pdm_mic.config.pio, 155 | pdm_mic.config.pio_sm, 156 | true 157 | ); 158 | } 159 | 160 | void pdm_microphone_stop() { 161 | pio_sm_set_enabled( 162 | pdm_mic.config.pio, 163 | pdm_mic.config.pio_sm, 164 | false 165 | ); 166 | 167 | dma_channel_abort(pdm_mic.dma_channel); 168 | 169 | if (pdm_mic.dma_irq == DMA_IRQ_0) { 170 | dma_channel_set_irq0_enabled(pdm_mic.dma_channel, false); 171 | } else if (pdm_mic.dma_irq == DMA_IRQ_1) { 172 | dma_channel_set_irq1_enabled(pdm_mic.dma_channel, false); 173 | } 174 | 175 | irq_set_enabled(pdm_mic.dma_irq, false); 176 | } 177 | 178 | static void pdm_dma_handler() { 179 | // clear IRQ 180 | if (pdm_mic.dma_irq == DMA_IRQ_0) { 181 | dma_hw->ints0 = (1u << pdm_mic.dma_channel); 182 | } else if (pdm_mic.dma_irq == DMA_IRQ_1) { 183 | dma_hw->ints1 = (1u << pdm_mic.dma_channel); 184 | } 185 | 186 | // get the current buffer index 187 | pdm_mic.raw_buffer_read_index = pdm_mic.raw_buffer_write_index; 188 | 189 | // get the next capture index to send the dma to start 190 | pdm_mic.raw_buffer_write_index = (pdm_mic.raw_buffer_write_index + 1) % PDM_RAW_BUFFER_COUNT; 191 | 192 | // give the channel a new buffer to write to and re-trigger it 193 | dma_channel_transfer_to_buffer_now( 194 | pdm_mic.dma_channel, 195 | pdm_mic.raw_buffer[pdm_mic.raw_buffer_write_index], 196 | pdm_mic.raw_buffer_size 197 | ); 198 | 199 | if (pdm_mic.samples_ready_handler) { 200 | pdm_mic.samples_ready_handler(); 201 | } 202 | } 203 | 204 | void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler) { 205 | pdm_mic.samples_ready_handler = handler; 206 | } 207 | 208 | void pdm_microphone_set_filter_max_volume(uint8_t max_volume) { 209 | pdm_mic.filter.MaxVolume = max_volume; 210 | } 211 | 212 | void pdm_microphone_set_filter_gain(uint8_t gain) { 213 | pdm_mic.filter.Gain = gain; 214 | } 215 | 216 | void pdm_microphone_set_filter_volume(uint16_t volume) { 217 | pdm_mic.filter_volume = volume; 218 | } 219 | 220 | int pdm_microphone_read(int16_t* buffer, size_t samples) { 221 | int filter_stride = (pdm_mic.filter.Fs / 1000); 222 | samples = (samples / filter_stride) * filter_stride; 223 | 224 | if (samples > pdm_mic.config.sample_buffer_size) { 225 | samples = pdm_mic.config.sample_buffer_size; 226 | } 227 | 228 | if (pdm_mic.raw_buffer_write_index == pdm_mic.raw_buffer_read_index) { 229 | return 0; 230 | } 231 | 232 | uint8_t* in = pdm_mic.raw_buffer[pdm_mic.raw_buffer_read_index]; 233 | int16_t* out = buffer; 234 | 235 | pdm_mic.raw_buffer_read_index++; 236 | 237 | for (int i = 0; i < samples; i += filter_stride) { 238 | #if PDM_DECIMATION == 64 239 | Open_PDM_Filter_64(in, out, pdm_mic.filter_volume, &pdm_mic.filter); 240 | #elif PDM_DECIMATION == 128 241 | Open_PDM_Filter_128(in, out, pdm_mic.filter_volume, &pdm_mic.filter); 242 | #else 243 | #error "Unsupported PDM_DECIMATION value!" 244 | #endif 245 | 246 | in += filter_stride * (PDM_DECIMATION / 8); 247 | out += filter_stride; 248 | } 249 | 250 | return samples; 251 | } 252 | -------------------------------------------------------------------------------- /src/pdm_microphone.pio: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | .program pdm_microphone_data 9 | .side_set 1 10 | .wrap_target 11 | nop side 0 12 | in pins, 1 side 0 13 | push iffull noblock side 1 14 | nop side 1 15 | .wrap 16 | 17 | % c-sdk { 18 | 19 | static inline void pdm_microphone_data_init(PIO pio, uint sm, uint offset, float clk_div, uint data_pin, uint clk_pin) { 20 | pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, false); 21 | pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true); 22 | 23 | pio_sm_config c = pdm_microphone_data_program_get_default_config(offset); 24 | 25 | sm_config_set_sideset_pins(&c, clk_pin); 26 | sm_config_set_in_pins(&c, data_pin); 27 | 28 | pio_gpio_init(pio, clk_pin); 29 | pio_gpio_init(pio, data_pin); 30 | 31 | sm_config_set_in_shift(&c, false, false, 8); 32 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); 33 | 34 | sm_config_set_clkdiv(&c, clk_div); 35 | 36 | pio_sm_init(pio, sm, offset, &c); 37 | } 38 | %} 39 | --------------------------------------------------------------------------------