Skip to content

Commit e2565e2

Browse files
committed
mimxrt10xx: Add PWMAudioOut
.. via a peripheral known as the "MQS" (medium quality sound). It uses an ~192kHz PWM signal to generate audio. It sounds OK on a small speaker with no amplifier. There's a small pop when starting/stopping audio, as is typical.
1 parent b50c80e commit e2565e2

File tree

13 files changed

+246
-9
lines changed

13 files changed

+246
-9
lines changed

ports/mimxrt10xx/common-hal/audiobusio/I2SOut.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@
2626

2727
#pragma once
2828

29+
#if CIRCUITPY_AUDIOBUSIO_I2SOUT
2930
#include "supervisor/background_callback.h"
3031
#include "common-hal/microcontroller/Pin.h"
3132

3233
#include "common-hal/audiobusio/__init__.h"
3334

3435
// Some boards don't implement I2SOut, so suppress any routines from here.
35-
#if CIRCUITPY_AUDIOBUSIO_I2SOUT
36-
37-
#include "sdk/drivers/sai/fsl_sai.h"
3836

3937
typedef struct {
4038
mp_obj_base_t base;

ports/mimxrt10xx/common-hal/audiobusio/__init__.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ static void i2s_fill_buffer(i2s_t *self) {
284284

285285
static void i2s_callback_fun(void *self_in) {
286286
i2s_t *self = self_in;
287+
mp_printf(&mp_plat_print, ".");
287288
i2s_fill_buffer(self);
288289
}
289290

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <stdint.h>
28+
#include <string.h>
29+
30+
#include "mpconfigport.h"
31+
32+
#include "py/gc.h"
33+
#include "py/mperrno.h"
34+
#include "py/runtime.h"
35+
#include "common-hal/audiobusio/__init__.h"
36+
#include "common-hal/audiopwmio/PWMAudioOut.h"
37+
#include "shared-bindings/audiopwmio/PWMAudioOut.h"
38+
#include "shared-bindings/microcontroller/Pin.h"
39+
#include "supervisor/shared/translate/translate.h"
40+
#include "supervisor/shared/tick.h"
41+
42+
// Where required we use identifier names that are required by NXP's
43+
// API, even though they do not conform to the naming standards that Adafruit
44+
// strives to adhere to. https://www.adafruit.com/blacklivesmatter
45+
#include "sdk/drivers/sai/fsl_sai.h"
46+
47+
STATIC void config_periph_pin(const mcu_periph_obj_t *periph) {
48+
if (!periph) {
49+
return;
50+
}
51+
if (periph->pin->mux_reg) {
52+
IOMUXC_SetPinMux(
53+
periph->pin->mux_reg, periph->mux_mode,
54+
periph->input_reg, periph->input_idx,
55+
0,
56+
1);
57+
}
58+
59+
IOMUXC_SetPinConfig(0, 0, 0, 0,
60+
periph->pin->cfg_reg,
61+
IOMUXC_SW_PAD_CTL_PAD_HYS(0)
62+
| IOMUXC_SW_PAD_CTL_PAD_PUS(0)
63+
| IOMUXC_SW_PAD_CTL_PAD_PUE(0)
64+
| IOMUXC_SW_PAD_CTL_PAD_PKE(1)
65+
| IOMUXC_SW_PAD_CTL_PAD_ODE(0)
66+
| IOMUXC_SW_PAD_CTL_PAD_SPEED(2)
67+
| IOMUXC_SW_PAD_CTL_PAD_DSE(4)
68+
| IOMUXC_SW_PAD_CTL_PAD_SRE(0));
69+
}
70+
71+
static void config_mqs(void) {
72+
CCM->CCGR0 = (CCM->CCGR0 & (~CCM_CCGR0_CG2_MASK)) | CCM_CCGR0_CG2(3); /* Enable MQS hmclk. */
73+
74+
IOMUXC_MQSEnterSoftwareReset(IOMUXC_GPR, true); /* Reset MQS. */
75+
IOMUXC_MQSEnterSoftwareReset(IOMUXC_GPR, false); /* Release reset MQS. */
76+
IOMUXC_MQSEnable(IOMUXC_GPR, true); /* Enable MQS. */
77+
IOMUXC_MQSConfig(IOMUXC_GPR, kIOMUXC_MqsPwmOverSampleRate64, 0u); /* 98.304MHz/64/(0+1) = 1.536MHz
78+
Higher frequency PWM involves less low frequen cy harmonic.*/
79+
80+
}
81+
82+
// Caller validates that pins are free.
83+
void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t *self,
84+
const mcu_pin_obj_t *left_channel, const mcu_pin_obj_t *right_channel, uint16_t default_value) {
85+
86+
int instance = -1;
87+
const mcu_periph_obj_t *left_periph = find_pin_function(mcu_mqs_left_list, left_channel, &instance, MP_QSTR_left_channel);
88+
const mcu_periph_obj_t *right_periph = find_pin_function(mcu_mqs_right_list, right_channel, &instance, MP_QSTR_right_channel);
89+
90+
mp_printf(&mp_plat_print, "note: peripheral %d\n", instance);
91+
sai_transceiver_t config;
92+
SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, 1U << 0u);
93+
config.frameSync.frameSyncEarly = false;
94+
config.frameSync.frameSyncPolarity = kSAI_PolarityActiveHigh;
95+
// config.syncMode = kSAI_ModeAsync;
96+
config.fifo.fifoPacking = kSAI_FifoPackingDisabled;
97+
// These identifier names are required by NXP's API, even though they do
98+
// not conform to the naming standards that Adafruit strives to adhere to.
99+
// https://www.adafruit.com/blacklivesmatter
100+
// config.masterSlave = kSAI_Master;
101+
port_i2s_initialize(&self->i2s, instance, &config);
102+
103+
self->left_channel = left_channel;
104+
self->right_channel = right_channel;
105+
claim_pin(left_channel);
106+
claim_pin(right_channel);
107+
config_periph_pin(left_periph);
108+
config_periph_pin(right_periph);
109+
config_mqs();
110+
}
111+
112+
bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t *self) {
113+
return port_i2s_deinited(&self->i2s);
114+
}
115+
116+
void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t *self) {
117+
if (common_hal_audiopwmio_pwmaudioout_deinited(self)) {
118+
return;
119+
}
120+
121+
port_i2s_deinit(&self->i2s);
122+
123+
common_hal_reset_pin(self->left_channel);
124+
self->left_channel = NULL;
125+
126+
common_hal_reset_pin(self->right_channel);
127+
self->right_channel = NULL;
128+
129+
IOMUXC_MQSEnterSoftwareReset(IOMUXC_GPR, true); /* Reset MQS. */
130+
CCM->CCGR0 = CCM->CCGR0 & (~CCM_CCGR0_CG2_MASK); /* Disable MQS hmclk. */
131+
}
132+
133+
void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t *self,
134+
mp_obj_t sample, bool loop) {
135+
if (common_hal_audiopwmio_pwmaudioout_get_playing(self)) {
136+
common_hal_audiopwmio_pwmaudioout_stop(self);
137+
}
138+
port_i2s_play(&self->i2s, sample, loop);
139+
}
140+
141+
void common_hal_audiopwmio_pwmaudioout_pause(audiopwmio_pwmaudioout_obj_t *self) {
142+
port_i2s_pause(&self->i2s);
143+
}
144+
145+
void common_hal_audiopwmio_pwmaudioout_resume(audiopwmio_pwmaudioout_obj_t *self) {
146+
port_i2s_resume(&self->i2s);
147+
}
148+
149+
bool common_hal_audiopwmio_pwmaudioout_get_paused(audiopwmio_pwmaudioout_obj_t *self) {
150+
return port_i2s_get_paused(&self->i2s);
151+
}
152+
153+
void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t *self) {
154+
port_i2s_stop(&self->i2s);
155+
}
156+
157+
bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t *self) {
158+
return port_i2s_get_playing(&self->i2s);
159+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#pragma once
28+
29+
#if CIRCUITPY_AUDIOBUSIO_I2SOUT
30+
#include "supervisor/background_callback.h"
31+
#include "common-hal/microcontroller/Pin.h"
32+
33+
#include "common-hal/audiobusio/__init__.h"
34+
35+
// Some boards don't implement I2SOut, so suppress any routines from here.
36+
37+
typedef struct {
38+
mp_obj_base_t base;
39+
i2s_t i2s;
40+
const mcu_pin_obj_t *left_channel, *right_channel;
41+
} audiopwmio_pwmaudioout_obj_t;
42+
43+
#endif

ports/mimxrt10xx/common-hal/audiopwmio/__init__.c

Whitespace-only changes.

ports/mimxrt10xx/common-hal/audiopwmio/__init__.h

Whitespace-only changes.

ports/mimxrt10xx/mpconfigport.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ CIRCUITPY_AUDIOCORE = 1
1717
CIRCUITPY_AUDIOIO = 0
1818
CIRCUITPY_AUDIOMIXER = 1
1919
CIRCUITPY_AUDIOMP3 = 1
20+
CIRCUITPY_AUDIOPWMIO = 1
2021
CIRCUITPY_BUSDEVICE = 1
2122
CIRCUITPY_COUNTIO = 0
2223
CIRCUITPY_FREQUENCYIO = 0

ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1011/periph.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,25 +169,31 @@ const mcu_pwm_obj_t mcu_pwm_list[20] = {
169169

170170
const mcu_periph_obj_t mcu_sai_rx_bclk_list[] = {
171171
PERIPH_PIN(1, 0, 0, 0, &pin_GPIO_08),
172-
PERIPH_PIN(2, 1, 0, 0, &pin_GPIO_00),
172+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_00),
173173
};
174174
const mcu_periph_obj_t mcu_sai_rx_data0_list[] = {
175175
PERIPH_PIN(1, 0, 0, 0, &pin_GPIO_03),
176-
PERIPH_PIN(2, 1, 0, 0, &pin_GPIO_SD_03),
176+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_SD_03),
177177
};
178178
const mcu_periph_obj_t mcu_sai_rx_sync_list[] = {
179179
PERIPH_PIN(1, 0, 0, 0, &pin_GPIO_02),
180-
PERIPH_PIN(2, 1, 0, 0, &pin_GPIO_SD_04),
180+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_SD_04),
181181
};
182182
const mcu_periph_obj_t mcu_sai_tx_bclk_list[] = {
183183
PERIPH_PIN(1, 0, 0, 0, &pin_GPIO_06),
184-
PERIPH_PIN(2, 1, 0, 0, &pin_GPIO_SD_01),
184+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_SD_01),
185185
};
186186
const mcu_periph_obj_t mcu_sai_tx_data0_list[] = {
187187
PERIPH_PIN(1, 0, 0, 0, &pin_GPIO_04),
188-
PERIPH_PIN(2, 1, 0, 0, &pin_GPIO_SD_02),
188+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_SD_02),
189189
};
190190
const mcu_periph_obj_t mcu_sai_tx_sync_list[] = {
191191
PERIPH_PIN(1, 0, 0, 0, &pin_GPIO_07),
192-
PERIPH_PIN(2, 1, 0, 0, &pin_GPIO_SD_00),
192+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_SD_00),
193+
};
194+
const mcu_periph_obj_t mcu_mqs_left_list[] = {
195+
PERIPH_PIN(3, 4, 0, 0, &pin_GPIO_AD_01),
196+
};
197+
const mcu_periph_obj_t mcu_mqs_right_list[] = {
198+
PERIPH_PIN(3, 4, 0, 0, &pin_GPIO_AD_02),
193199
};

ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1011/periph.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,7 @@ extern const mcu_periph_obj_t mcu_sai_tx_bclk_list[2];
5454
extern const mcu_periph_obj_t mcu_sai_tx_data0_list[2];
5555
extern const mcu_periph_obj_t mcu_sai_tx_sync_list[2];
5656

57+
extern const mcu_periph_obj_t mcu_mqs_left_list[1];
58+
extern const mcu_periph_obj_t mcu_mqs_right_list[1];
59+
5760
#endif // MICROPY_INCLUDED_MIMXRT10XX_MIMXRT1011_PERIPHERALS_MIMXRT1011_PERIPH_H

ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1021/periph.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,13 @@ const mcu_periph_obj_t mcu_sai_tx_sync_list[] = {
311311
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_EMC_34),
312312
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_SD_B1_07),
313313
};
314+
const mcu_periph_obj_t mcu_mqs_left_list[] = {
315+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_AD_B0_07),
316+
PERIPH_PIN(3, 2, 0, 0, &pin_GPIO_EMC_17),
317+
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_EMC_38),
318+
};
319+
const mcu_periph_obj_t mcu_mqs_right_list[] = {
320+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_AD_B0_06),
321+
PERIPH_PIN(3, 2, 0, 0, &pin_GPIO_EMC_16),
322+
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_EMC_37),
323+
};

ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1021/periph.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,6 @@ extern const mcu_periph_obj_t mcu_sai_rx_sync_list[7];
5353
extern const mcu_periph_obj_t mcu_sai_tx_bclk_list[7];
5454
extern const mcu_periph_obj_t mcu_sai_tx_data0_list[7];
5555
extern const mcu_periph_obj_t mcu_sai_tx_sync_list[7];
56+
57+
extern const mcu_periph_obj_t mcu_mqs_left_list[3];
58+
extern const mcu_periph_obj_t mcu_mqs_right_list[3];

ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1062/periph.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,3 +362,13 @@ const mcu_periph_obj_t mcu_sai_tx_sync_list[] = {
362362
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_EMC_39),
363363
PERIPH_PIN(3, 8, 0, 0, &pin_GPIO_SD_B1_02),
364364
};
365+
const mcu_periph_obj_t mcu_mqs_left_list[] = {
366+
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_EMC_14),
367+
PERIPH_PIN(3, 2, 0, 0, &pin_GPIO_B0_01),
368+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_AD_B0_05),
369+
};
370+
const mcu_periph_obj_t mcu_mqs_right_list[] = {
371+
PERIPH_PIN(3, 3, 0, 0, &pin_GPIO_EMC_13),
372+
PERIPH_PIN(3, 2, 0, 0, &pin_GPIO_B0_00),
373+
PERIPH_PIN(3, 1, 0, 0, &pin_GPIO_AD_B0_04),
374+
};

ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1062/periph.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,7 @@ extern const mcu_periph_obj_t mcu_sai_tx_bclk_list[7];
5454
extern const mcu_periph_obj_t mcu_sai_tx_data0_list[7];
5555
extern const mcu_periph_obj_t mcu_sai_tx_sync_list[7];
5656

57+
extern const mcu_periph_obj_t mcu_mqs_left_list[3];
58+
extern const mcu_periph_obj_t mcu_mqs_right_list[3];
59+
5760
#endif // MICROPY_INCLUDED_MIMXRT10XX_MIMXRT1062_PERIPHERALS_MIMXRT1011_PERIPH_H

0 commit comments

Comments
 (0)