From 0535bb980c8e37f6e20633e3d2ca8c66fd3cc6f1 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 7 Jul 2015 15:38:02 -0400 Subject: [PATCH 01/33] stmhal/pin_named_pins.c: Add pin_find_af_type function --- stmhal/Makefile | 1 + stmhal/boards/PYBV10/mpconfigboard.h | 2 + stmhal/i2s.c | 960 +++++++++++++++++++++++++++ stmhal/i2s.h | 31 + stmhal/main.c | 9 + stmhal/modpyb.c | 4 + stmhal/mpconfigport.h | 4 + stmhal/pin.h | 1 + stmhal/pin_defs_stmhal.h | 8 +- stmhal/pin_named_pins.c | 10 + stmhal/qstrdefsport.h | 34 + 11 files changed, 1060 insertions(+), 4 deletions(-) create mode 100644 stmhal/i2s.c create mode 100644 stmhal/i2s.h diff --git a/stmhal/Makefile b/stmhal/Makefile index 8ee3f458f2af0..8b2a52d0717b0 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -162,6 +162,7 @@ SRC_C = \ servo.c \ dac.c \ adc.c \ + i2s.c \ $(wildcard boards/$(BOARD)/*.c) SRC_O = \ diff --git a/stmhal/boards/PYBV10/mpconfigboard.h b/stmhal/boards/PYBV10/mpconfigboard.h index fece7333082ce..2760baa25bb26 100644 --- a/stmhal/boards/PYBV10/mpconfigboard.h +++ b/stmhal/boards/PYBV10/mpconfigboard.h @@ -16,6 +16,8 @@ #define MICROPY_HW_ENABLE_SPI2 (1) #define MICROPY_HW_ENABLE_SPI3 (0) #define MICROPY_HW_ENABLE_CAN (1) +#define MICROPY_HW_ENABLE_I2S2 (1) +#define MICROPY_HW_ENABLE_I2S3 (0) // HSE is 8MHz #define MICROPY_HW_CLK_PLLM (8) diff --git a/stmhal/i2s.c b/stmhal/i2s.c new file mode 100644 index 0000000000000..144445f0c9a53 --- /dev/null +++ b/stmhal/i2s.c @@ -0,0 +1,960 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Bryan Morrissey + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "py/nlr.h" +#include "py/runtime.h" +#include "py/objstr.h" +#include "py/objlist.h" +#include "irq.h" +#include "pin.h" +#include "led.h" // For led_toggle(n) debugging +#include "genhdr/pins.h" +#include "dma.h" +#include "bufhelper.h" +#include "i2s.h" +#include MICROPY_HAL_H + +/// \moduleref pyb +/// \class I2S - Inter-IC-Sound, a protocol to transfer isochronous audio data +/// +/// I2S is a serial protocol for sending and receiving audio. At the physical +/// level there are 3 or 4 lines: Bit Clock, Word Select, and 1 or 2 data lines +/// for simplex or duplex operation. In addition, I2S can optionally provide a +/// Master Clock (MCLK) signal to drive an external codec, or accept an I2S +/// clock input signal (I2S_CLKIN) from an external source to drive I2SPLL. +/// +/// TODO: Provide detailed synopsis of I2S +/// - Usage example +/// - Methods list + +// Possible DMA configurations for I2S busses: +// SPI2 RX: DMA1_Stream3.CHANNEL_0 +// SPI2 TX: DMA1_Stream4.CHANNEL_0 +// I2S2_EXT RX: DMA1_Stream3.CHANNEL_3 +// I2S2_EXT TX: DMA1_Stream4.CHANNEL_2 +// SPI3 RX: DMA1_Stream0.CHANNEL_0 or DMA1_Stream2.CHANNEL_0 +// SPI3 TX: DMA1_Stream5.CHANNEL_0 or DMA1_Stream7.CHANNEL_0 +// I2S3_EXT RX: DMA1_Stream0.CHANNEL_3 or DMA1_Stream2.CHANNEL_2 +// I2S3_EXT TX: DMA1_Stream5.CHANNEL_2 + +typedef struct _pyb_i2s_obj_t { + mp_obj_base_t base; + I2S_HandleTypeDef i2s; + DMA_Stream_TypeDef *tx_dma_stream; + uint32_t tx_dma_channel; + DMA_Stream_TypeDef *rx_dma_stream; + uint32_t rx_dma_channel; + // pins are Bit Clock, Word Select, TX Data, RX Data, and MCK Out + const pin_obj_t *pins[5]; + mp_int_t i2s_id : 8; + bool is_enabled : 1; + bool is_master : 1; + bool is_duplex : 1; + bool base_is_tx : 1; // base instance SPIx is either tx or rx +} pyb_i2s_obj_t; + +// Note: This is where spi.c defines SPI_HandleTypeDef SPIHandle[1,2,3] +// as well as initializing the static pyb_spi_obj[] array - +// probably not needed here? + +// TODO: more testing to determine optimal DMA configuration +const DMA_InitTypeDef dma_init_struct_i2s = { + .Channel = 0, + .Direction = 0, + .PeriphInc = DMA_PINC_DISABLE, + .MemInc = DMA_MINC_ENABLE, + .PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD, + .MemDataAlignment = DMA_MDATAALIGN_HALFWORD, + .Mode = DMA_NORMAL, + .Priority = DMA_PRIORITY_HIGH, + //.FIFOMode = DMA_FIFOMODE_DISABLE, + .FIFOMode = DMA_FIFOMODE_ENABLE, + //.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL, + .FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL, + .MemBurst = DMA_MBURST_SINGLE, + .PeriphBurst = DMA_PBURST_SINGLE +}; + +// i2s_init0 is direct crib from stmhal/can.c - can_init0() +void i2s_init0(void) { + for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_i2s_obj_all)); i++) { + MP_STATE_PORT(pyb_i2s_obj_all)[i] = NULL; + } +} + +// assumes init parameters are set up correctly +STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { + // init the GPIO lines + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Mode = GPIO_MODE_AF_PP; + GPIO_InitStructure.Speed = GPIO_SPEED_FAST; + GPIO_InitStructure.Pull = GPIO_PULLUP; // see spi.c for dealing with CPOL + + // TODO: + // - Use STM32F4DISC to make sure that I2S3 works with that board's codec - + // (I2S3 conflicts with SDIO and LED's on pyboard and therefore is disabled) + // - Provide configuration to enable I2S_CKIN if it available (only if SDIO + // is not configured) + + // If I2Sx_SD (SPIx) and I2SxEXT_SD pins are both given, then + // init full duplex mode. + // - If only I2Sx_SD is given, init simplex mode + // - SD pin is configured as either Rx or Tx in pyb_i2s_init_helper + // based on postion in list; if ext_SD pin is present we set full-duplex + // option, and HAL_I2S_Init automatically sets up the ext I2Sxext + // instance as opposite direction (Rx or Tx) to I2Sx + + if (0) { +#if MICROPY_HW_ENABLE_I2S2 + } else if (i2s_obj->i2s_id == 2) { + i2s_obj->i2s.Instance = SPI2; + __SPI2_CLK_ENABLE(); + // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 + if (i2s_obj->is_duplex) { + if (i2s_obj->base_is_tx) { + // SPI2 instance is Tx + i2s_obj->tx_dma_stream = DMA1_Stream4; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + // I2S2_ext instance is Rx + i2s_obj->rx_dma_stream = DMA1_Stream3; + i2s_obj->rx_dma_channel = DMA_CHANNEL_3; + } else { + // I2S2_ext instance is Rx + i2s_obj->tx_dma_stream = DMA1_Stream4; + i2s_obj->tx_dma_channel = DMA_CHANNEL_2; + // SPI2 instance is Rx + i2s_obj->rx_dma_stream = DMA1_Stream3; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } + } else { + // simplex: set up both DMA streams for SPI2, only one will be used + i2s_obj->tx_dma_stream = DMA1_Stream4; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + i2s_obj->rx_dma_stream = DMA1_Stream3; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } + // Configure MCLK output pin if selected; MCLK for I2S2 is PC6, AF5 + i2s_obj->pins[4] = (i2s_obj->i2s.Init.MCLKOutput == + I2S_MCLKOUTPUT_ENABLE) ? &pin_C6 : MP_OBJ_NULL; + // Initialize GPIO pins for I2S operation + for (uint i = 0; i < 5; i++) { + if (i2s_obj->pins[i] != MP_OBJ_NULL) { + GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; + if (i2s_obj->pins[i] == &pin_B14 || i2s_obj->pins[i] == &pin_C2) { + GPIO_InitStructure.Alternate = GPIO_AF6_I2S2ext; + } else { + GPIO_InitStructure.Alternate = GPIO_AF5_SPI2; + } + HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); + } + } +#endif +#if MICROPY_HW_ENABLE_I2S3 + } else if (i2s_obj->i2s_id == 3) { + i2s_obj->i2s.Instance = SPI3; + __SPI3_CLK_ENABLE(); + // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 + // alternate stream configurations commented out + if (i2s_obj->is_duplex) { + if (i2s_obj->base_is_tx) { + // SPI3 instance is TX + i2s_obj->tx_dma_stream = DMA1_Stream5; + //i2s_obj->tx_dma_stream = DMA1_Stream7; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + // I2S3_ext instance is RX (DMA1_Stream0.CHANNEL_3 or DMA1_Stream2.CHANNEL_2) + i2s_obj->rx_dma_stream = DMA1_Stream0; + i2s_obj->rx_dma_channel = DMA_CHANNEL_3; + //i2s_obj->rx_dma_stream = DMA1_Stream2; + //i2s_obj->rx_dma_channel = DMA_CHANNEL_2; + } else { + // I2S3_ext instance is TX + i2s_obj->tx_dma_stream = DMA1_Stream5; + i2s_obj->tx_dma_channel = DMA_CHANNEL_2; + // SPI3 instance is RX + i2s_obj->rx_dma_stream = DMA1_Stream0; + //i2s_obj->rx_dma_stream = DMA1_Stream2; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } + } else { + // Simplex: set up both DMA streams for SPI3, only one will be used + i2s_obj->tx_dma_stream = DMA1_Stream5; + //i2s_obj->tx_dma_stream = DMA1_Stream7; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + i2s_obj->rx_dma_stream = DMA1_Stream0; + //i2s_obj->rx_dma_stream = DMA1_Stream2; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } + + // Configure MCLK output pin, if selected; MCLK for I2S3 is PC7, AF6 + i2s_obj->pins[4] = (i2s_obj->i2s.Init.MCLKOutput == + I2S_MCLKOUTPUT_ENABLE) ? &pin_C7 : MP_OBJ_NULL; + // Initialize GPIO pins for I2S operation + for (uint i = 0; i < 5; i++) { + if (i2s_obj->pins[i] != MP_OBJ_NULL) { + GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; + if (i2s_obj->pins[i] == &pin_B4) { + GPIO_InitStructure.Alternate = GPIO_AF7_I2S3ext; + } else if (i2s_obj->pins[i] == &pin_C11) { + GPIO_InitStructure.Alternate = GPIO_AF5_I2S3ext; + } else { + GPIO_InitStructure.Alternate = GPIO_AF6_SPI3; + HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); + } + } + } +#endif + } else { + // invalid i2s_id number; shouldn't get here as i2s object should not + // have been created without setting a valid i2s instance number + return false; + } + + // Configure and enable I2SPLL: + // TODO: This may not be the correct method to initialize and activate + // the I2SPLL, needs more testing + __HAL_RCC_PLLI2S_DISABLE(); + if (i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_TX || + i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX ) { + // TODO - calculate values based on available parameters + __HAL_RCC_PLLI2S_CONFIG(384, 5); + __HAL_RCC_PLLI2S_ENABLE(); + } + + if (HAL_I2S_Init(&i2s_obj->i2s) != HAL_OK) { + // This message is redundant, exception will be raised by return value + printf("OSError: HAL_I2S_Init failed\n"); + return false; + } else { + dma_invalidate_channel(i2s_obj->tx_dma_stream, i2s_obj->tx_dma_channel); + dma_invalidate_channel(i2s_obj->rx_dma_stream, i2s_obj->rx_dma_channel); + i2s_obj->is_enabled = true; + return true; + } +} + +// this is needed because i2s_deinit invokes pyb_i2s_deinit - should it be +// other way around? +STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in); + +// i2s_deinit is direct crib from stmhal/can.c - can_deinit() +// unregister all interrupt sources +void i2s_deinit(void) { + for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_i2s_obj_all)); i++) { + pyb_i2s_obj_t *i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i]; + if (i2s_obj != NULL) { + pyb_i2s_deinit(i2s_obj); + } + } +} + +// TODO: This is currently enabled, but eventually I2S should be entirely +// non-blocking, using DMA with callbacks +STATIC HAL_StatusTypeDef i2s_wait_dma_finished(I2S_HandleTypeDef *i2s, uint32_t timeout) { + uint32_t start = HAL_GetTick(); + while (HAL_I2S_GetState(i2s) != HAL_I2S_STATE_READY) { + if (HAL_GetTick() - start >= timeout) { + return HAL_TIMEOUT; + } + __WFI(); + } + return HAL_OK; +} + +// Debug placeholders for DMA callback methods +void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { + //printf("I2S-TxHalfCplt\n"); + //led_toggle(2); +} + +void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { + printf("I2S-TxCplt\n"); + //led_toggle(3); +} + +void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { + //printf("I2S-RxHalfCplt\n"); +} + +void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { + printf("I2S-RxCplt\n"); +} + +void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { + //printf("I2S-Error\n"); + //led_toggle(4); +} + +/******************************************************************************/ +/* Micro Python bindings */ + +// spi.c provides *spi_get_handle, probably don't need to provide *i2s_get_handle + +STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + pyb_i2s_obj_t *self = self_in; + + mp_printf(print, "I2S(%u on [", self->i2s_id); + for (int i = 0; i < 4; i++) { + if (self->pins[i] != MP_OBJ_NULL) { + mp_printf(print, "%q ", self->pins[i]->name); + } else { + mp_print_str(print, "None "); + } + } + mp_print_str(print, "\b]"); + if (self->is_enabled) { + if (self->i2s.Init.Mode == I2S_MODE_MASTER_TX || + self->i2s.Init.Mode == I2S_MODE_MASTER_RX) { + mp_print_str(print, ", I2S.MASTER, MCLK "); + if (self->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { + mp_printf(print, "on %q", self->pins[4]->name); + } else { + mp_print_str(print, "off"); + } + mp_printf(print, ", freq=%u", self->i2s.Init.AudioFreq); + } else if (self->i2s.Init.Mode == I2S_MODE_SLAVE_TX || + self->i2s.Init.Mode == I2S_MODE_SLAVE_RX) { + mp_print_str(print, ", I2S.SLAVE"); + } else { + // Shouldn't get here if self->is_enabled=true + } + mp_printf(print, ", standard=%u, format=%u, polarity=%u", + self->i2s.Init.Standard, self->i2s.Init.DataFormat, + self->i2s.Init.CPOL); + } + mp_print_str(print, ")"); +} + +/// \method init(mode, standard=I2S.PHILIPS, dataformat=I2S._16B_EXTENDED, +/// polarity=I2S.LOW, audiofreq=48000, +/// clksrc=I2S.PLL, mclkout=I2S.DISABLE) +/// +/// Initialise the I2S bus with the given parameters: +/// +/// - `mode` must be either `I2S.MASTER` or `I2S.SLAVE`. +/// - `standard` can be `PHILIPS`, `MSB`, `LSB`, `PCM_SHORT`, or `PCM_LONG`. +/// - `dataformat` can be `_16B`, `_16B_EXTENDED`, `_24B`, or `_32B`. +/// - `polarity` can be `HIGH` or `LOW`. +/// - Options only relevant to master mode: +/// - `audiofreq` can be any common audio sampling frequency, default is 48000. +/// - `clksrc` can be `PLL` or `EXTERNAL`. +/// - `mclkout` can be `ENABLE` or `DISABLE` +STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, + const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */} }, + { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_STANDARD_PHILIPS} }, + { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_DATAFORMAT_16B_EXTENDED} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CPOL_LOW} }, + // Include option for setting I2SPLL parameters directly? + { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, + { MP_QSTR_clksrc, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CLOCK_PLL} }, + { MP_QSTR_mclkout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_MCLKOUTPUT_DISABLE} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // set the I2S configuration values + memset(&self->i2s, 0, sizeof(self->i2s)); + I2S_InitTypeDef *init = &self->i2s.Init; + + // init->Mode can be MASTER_TX, MASTER_RX, SLAVE_TX, or SLAVE_RX; + if (args[0].u_int == 0) { + init->Mode = self->base_is_tx ? I2S_MODE_MASTER_TX : I2S_MODE_MASTER_RX; + } else { + init->Mode = self->base_is_tx ? I2S_MODE_SLAVE_TX : I2S_MODE_SLAVE_RX; + } + + init->Standard = args[1].u_int; + init->DataFormat = args[2].u_int; + init->CPOL = args[3].u_int; + init->AudioFreq = args[4].u_int; + init->MCLKOutput = args[6].u_int; + init->FullDuplexMode = self->is_duplex ? \ + I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; + + // -------------Possible bug in HAL------------------- + // According to the datasheet (RM0090, Sec 28.4.6 - I2S Slave Mode) in Slave + // mode there is no need to enable I2SPLL to generate a clock for the I2S + // interface; the CLK and WS pins receive clock signals from another master + // to drive the data transfer. + // HOWEVER, HAL_I2S_Init returns an error if the I2SPLL is not enabled and + // ClockSource != I2S_CLOCK_EXTERNAL + // This appears to be incorrect behavior; the parameter I2S_CLOCK_EXTERNAL + // is intended to set pin PC9 as I2S_CKIN, an external source for I2SPLL. + // So I don't think that this work around is really correct here: + if (args[0].u_int) { + // Slave mode + init->ClockSource = I2S_CLOCK_EXTERNAL; + } else { + init->ClockSource = args[5].u_int; + } + + // init the I2S bus + if (!i2s_init(self)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S port %d init failed", self->i2s_id)); + } + + return mp_const_none; +} + +/// \classmethod \constructor(bus, ...) +/// +/// Construct an I2S object with the given list of pins. +/// I2S requires a clock pin (CK), a word select pin (WS) and either +/// one or two data pins. +/// The pins can be passed as either a list or a tuple with the format +/// [Clock, Word Select, TX Data, RX Data] +/// If two data pins are provided, one must be an SD pin and the other must +/// be a corresponding SDext pin; the I2S port will be initialized in duplex +/// (TX + RX) mode. +/// If only one data pin is provided, it must be an SD (not SDext) pin. The +/// I2S port will be initialized in simplex mode, with the direction determined +/// by the position of the data pin in the list. +/// Examples: +/// pin_list = ['B10','B12','C3','C2'] - Duplex mode, C3 as Tx, C2 as Rx +/// pin_list = ['B10','B12',0,'C2'] - Simplex mode, C2 as RX +/// pin_list = ['Y6','Y5','Y7','Y8'] - Use board name strings +/// pin_list = ('Y6','Y5','Y7','Y8') - Tuple works as well as list +/// Mixed list of strings and objects: +/// pin_list = [pyb.Pin.cpu.B10,'B12','Y7','pyb.Pin.board.Y8'] +/// +/// Valid pins for I2S2 on the pyboard: +/// CK - PB13 / Y6, PB10 / Y9 (SPI2 SCK) +/// WS - PB12 / Y5, PB9 / Y4 (SPI2 NSS) +/// SD - PB15 / Y8, PC3 / X22 (SPI2 MOSI) +/// SDext - PB14 / Y7, PC2 / X21 (SPI2 MISO) +/// MCK - PC6 / Y1 +/// +/// The I2S3 port is disabled by default on the pyboard, as its pins would +/// conflict with the SD Card and other pyboard functions. It can be enabled +/// for the STM32F4DISCOVERY port, which has an I2S codec on I2S3. +/// +/// CK - PC10 / NA (SD_D2), PB3 / X17 (USR SW) (SPI3 SCK) +/// WS - PA4 / X5, PA15 / P3 (YELLOW LED) (SPI3 NSS) +/// SD - PC12 / NA (SD_CK), PB5 / NA (MMA_AVDD) (SPI3 MOSI) +/// SDext - PC11 / NA (SD_D3), PB4 / P2 (BLUE LED) (SPI3 MISO) +/// MCK - PC7 (Y2) +/// +/// With no additional parameters, the I2S object is created but not +/// initialised (it has the settings from the last initialisation of +/// the bus, if any). If extra arguments are given, the bus is initialised. +/// See `init` for parameters of initialisation. + +STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, + mp_uint_t n_kw, const mp_obj_t *args) { + + // If n_args == 0 and I2S2 is enabled, set default pin list to the SPI2 + // pins on the pyboard (Y6, Y5, Y7, Y8): +#ifdef MICROPY_HW_ENABLE_I2S2 + mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true); +#else + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); +#endif + + // get array of pin identifiers, could be name strings or pin objects + mp_uint_t array_len = 0; + mp_obj_t *pin_names; + if (n_args == 0) { +#ifdef MICROPY_HW_ENABLE_I2S2 + static const pin_obj_t *default_pins[4] = {&pin_B13, &pin_B12, &pin_B15, &pin_B14}; + pin_names = (mp_obj_t)default_pins; + array_len = 4; +#endif + } else { + mp_obj_get_array(args[0], &array_len, &pin_names); + } + if (array_len != 4) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Pin list requires 4 items, %d given", + array_len)); + } + + // Get array of pin objects: False / empty values are valid for pins[2:3], + // empty values get set to MP_OBJ_NULL + const pin_obj_t *pins[4]; + for (int i = 0; i < 4; i++) { + if (mp_obj_is_true(pin_names[i])) { + pins[i] = pin_find(pin_names[i]); + } else { + pins[i] = MP_OBJ_NULL; + } + // can also enable this information fromn within uPy: pyb.Pin.debug(True) + if (0) { // DEBUG - print list of pin objects + mp_obj_print((mp_obj_t)pins[i], PRINT_STR); + printf("\n"); + } + } + + // Check if pins represents a valid I2S port configuration: + // Each entry of pins[] array (0=CK, 1=WS, 2=TX, 3=RX) is checked to + // determine if it is valid for its designated function. + // Of the two entries for data pins (2-TX and 3-RX) exactly one + // of them be a valid base SD pin for the same I2S port as CK and WS; the + // other can be either a valid SDext pin to select duplex mode, or empty + // to select simplex mode. + // Is there a more elegant way to do this? Maybe by directly querying each + // pin to see if there is an AF for I2S associated with it? + // How much information should be conveyed through the error codes? + mp_uint_t i2s_id = 0; + mp_uint_t err_code = 0; + bool is_duplex; + bool base_is_tx; + if (0) { +#if MICROPY_HW_ENABLE_I2S2 + } else if ((pins[0] == &pin_B10 || pins[0] == &pin_B13) && + (pins[1] == &pin_B12 || pins[1] == &pin_B9)) { + // pins[0:1] are valid CLK and WS pins of I2S2, set i2s_id + i2s_id = 2; + if (pins[2] == &pin_B15 || pins[2] == &pin_C3) { + // pins[2] is valid SD pin; config as TX + base_is_tx = true; + if (pins[3] == &pin_B14 || pins[3] == &pin_C2) { + // pins[3] is valid SDext pin; duplex mode + is_duplex = true; + } else if (pins[3] == MP_OBJ_NULL) { + is_duplex = false; + } else { + err_code = 2; // pins[3] is not valid + } + } else if (pins[3] == &pin_B15 || pins[3] == &pin_C3) { + // pins[3] is valid SD pin; config as RX + base_is_tx = false; + if (pins[2] == &pin_B14 || pins[2] == &pin_C2) { + // pins[2] is valid SDext pin; duplex mode + is_duplex = true; + } else if (pins[2] == MP_OBJ_NULL) { + is_duplex = false; + } else { + err_code = 3; // pins[2] is not valid + } + } else { + err_code = 4; // No valid SD pin for I2S2 + } +#endif +#if MICROPY_HW_ENABLE_I2S3 + } else if ((pins[0] == &pin_B3 || pins[0] == &pin_C10) && + (pins[1] == &pin_A4 || pins[1] == &pin_A15)) { + // pins[0:1] are valid CLK and WS pins of I2S3, set i2s_id + i2s_id = 3; + if (pins[2] == &pin_B5 || pins[2] == &pin_C12) { + // pins[2] is valid SD pin; config as TX + base_is_tx = true; + if (pins[3] == &pin_B4 || pins[3] == &pin_C11) { + is_duplex = true; + } else if (pins[3] == MP_OBJ_NULL) { + is_duplex = false; + } else { + err_code = 5; // pins[3] is not valid + } + } else if (pins[3] == &pin_B5 || pins[3] == &pin_C12) { + // pins[3] is valid SD pin; config as RX + base_is_tx = false; + if (pins[2] == &pin_B4 || pins[2] == &pin_C11) { + is_duplex = true; + } else if (pins[2] == MP_OBJ_NULL) { + is_duplex = false; + } else { + err_code = 6; // pins[2] is not valid + } + } else { + err_code = 7; // No valid SD pin for I2S3 + } +#endif + } else { + err_code = 1; // pins[0:1] not valid clock and WS for available I2S ports + } + if (err_code) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Invalid pins for I2S, err: %d", err_code)); + } + + // get I2S object + pyb_i2s_obj_t *i2s_obj; + if (MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - 2] == NULL) { + // create new I2S object + i2s_obj = m_new_obj(pyb_i2s_obj_t); + i2s_obj->base.type = &pyb_i2s_type; + i2s_obj->i2s_id = i2s_id; + i2s_obj->is_enabled = false; + MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - 2] = i2s_obj; + } else { + i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - 2]; + } + + i2s_obj->is_duplex = is_duplex; + i2s_obj->base_is_tx = base_is_tx; + for (int i = 0; i < 4; i++) { + i2s_obj->pins[i] = pins[i]; + } + + if (n_args > 1 || n_kw > 0) { + // start the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_i2s_init_helper(i2s_obj, n_args - 1, args + 1, &kw_args); + } + + return (mp_obj_t)i2s_obj; +} + + +////////////////////////////////////// + +STATIC mp_obj_t pyb_i2s_init(mp_uint_t n_args, const mp_obj_t *args, + mp_map_t *kw_args) { + return pyb_i2s_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_init_obj, 1, pyb_i2s_init); + +// TODO: init and deinit follow model from stmhal/can.c; should is follow logic +// closer to spi_init and spi_deinit instead? + +/// \method deinit() +/// Turn off the I2S bus. +STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in) { + pyb_i2s_obj_t *self = self_in; + self->is_enabled = false; + HAL_I2S_DeInit(&self->i2s); + if (0) { +#if MICROPY_HW_ENABLE_I2S2 + } else if (self->i2s.Instance == SPI2) { + __SPI2_FORCE_RESET(); + __SPI2_RELEASE_RESET(); + __SPI2_CLK_DISABLE(); +#endif +#if MICROPY_HW_ENABLE_I2S3 + } else if (self->i2s.Instance == SPI3) { + __SPI3_FORCE_RESET(); + __SPI3_RELEASE_RESET(); + __SPI3_CLK_DISABLE(); +#endif + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_deinit_obj, pyb_i2s_deinit); + +// TODO - adapt docstrings copied from spi.c for i2s + +/// \method send(send, *, timeout=5000) +/// Send data on the bus: +/// +/// - `send` is the data to send (an integer to send, or a buffer object). +/// - `timeout` is the timeout in milliseconds to wait for the send. +/// +/// Return value: `None`. +STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, + mp_map_t *kw_args) { + // skeleton copied from spi.c + static const mp_arg_t allowed_args[] = { + { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + pyb_i2s_obj_t *self = pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // 'send' can be used in either simplex or duplex mode, but only if the + // base i2s instance is in TX mode - in duplex mode, the extended instance + // must transmit using send_recv + + // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle send on I2Sx_EXT + // interfaces when available instead of raising error? + if (!self->base_is_tx) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) base instance not in transmit mode", self->i2s_id)); + } + + // get the buffer to send from + mp_buffer_info_t bufinfo; + uint8_t data[1]; + pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data); + + // TODO - implement 24-bit and 32-bit data width cases for all methods + // send the data + HAL_StatusTypeDef status; + if (query_irq() == IRQ_STATE_DISABLED) { + status = HAL_I2S_Transmit(&self->i2s, bufinfo.buf, bufinfo.len / 2, args[1].u_int); + } else { + DMA_HandleTypeDef tx_dma; + dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &tx_dma; + self->i2s.hdmarx = NULL; + status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); + if (status == HAL_OK) { + led_toggle(1); + status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); + } + dma_deinit(&tx_dma); + } + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_obj, 1, pyb_i2s_send); + +// TODO - adapt docstrings copied from spi.c for i2s + +/// \method recv(recv, *, timeout=5000) +/// +/// Receive data on the bus: +/// +/// - `recv` can be an integer, which is the number of bytes to receive, +/// or a mutable buffer, which will be filled with received bytes. +/// - `timeout` is the timeout in milliseconds to wait for the receive. +/// +/// Return value: if `recv` is an integer then a new buffer of the bytes received, +/// otherwise the same buffer that was passed in to `recv`. +STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, + mp_map_t *kw_args) { + // TODO check transmission size for I2S + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + pyb_i2s_obj_t *self = pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // 'recv' can be used in either simplex or duplex mode, but only if the + // base i2s instance is in RX mode - in duplex mode, the extended instance + // must receive using send_recv + + // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT + // interfaces when available instead of raising error? + if (self->base_is_tx) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) base instance not in receive mode", self->i2s_id)); + } + + // get the buffer to receive into + vstr_t vstr; + mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &vstr); + + // Only 16-bit data transfers are implemented + // TODO - implement 24-bit and 32-bit data transfers for all methods + // receive the data + HAL_StatusTypeDef status; + if (query_irq() == IRQ_STATE_DISABLED) { + status = HAL_I2S_Receive(&self->i2s, (uint16_t*)vstr.buf, + vstr.len / 2, args[1].u_int); + } else { + DMA_HandleTypeDef rx_dma; + dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); + self->i2s.hdmarx = &rx_dma; + self->i2s.hdmatx = NULL; + status = HAL_I2S_Receive_DMA(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2); + if (status == HAL_OK) { + status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); + } + dma_deinit(&rx_dma); + } + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + // return the received data + if (o_ret != MP_OBJ_NULL) { + return o_ret; + } else { + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_recv_obj, 1, pyb_i2s_recv); + +// TODO - adapt docstrings copied from spi.c for i2s + +/// \method send_recv(send, recv=None, *, timeout=5000) +/// +/// Send and receive data on the bus at the same time: +/// +/// - `send` is the data to send (an integer to send, or a buffer object). +/// - `recv` is a mutable buffer which will be filled with received bytes. +/// It can be the same as `send`, or omitted. If omitted, a new buffer will +/// be created. +/// - `timeout` is the timeout in milliseconds to wait for the receive. +/// +/// Return value: the buffer with the received bytes. +STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, + mp_map_t *kw_args) { + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + pyb_i2s_obj_t *self = pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT + // interfaces when available instead of raising error? + //if (!self->is_duplex) { + if (self->i2s.Init.FullDuplexMode != I2S_FULLDUPLEXMODE_ENABLE) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not in duplex mode", self->i2s_id)); + } + + // get buffers to send from/receive to + mp_buffer_info_t bufinfo_send; + uint8_t data_send[1]; + mp_buffer_info_t bufinfo_recv; + vstr_t vstr_recv; + mp_obj_t o_ret; + + if (args[0].u_obj == args[1].u_obj) { + // same object for send and receive, it must be a r/w buffer + mp_get_buffer_raise(args[0].u_obj, &bufinfo_send, MP_BUFFER_RW); + bufinfo_recv = bufinfo_send; + o_ret = args[0].u_obj; + } else { + // get the buffer to send from + pyb_buf_get_for_send(args[0].u_obj, &bufinfo_send, data_send); + + // get the buffer to receive into + if (args[1].u_obj == MP_OBJ_NULL) { + // only send argument given, so create a fresh buffer of the send length + vstr_init_len(&vstr_recv, bufinfo_send.len); + bufinfo_recv.len = vstr_recv.len; + bufinfo_recv.buf = vstr_recv.buf; + o_ret = MP_OBJ_NULL; + } else { + // recv argument given + mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE); + if (bufinfo_recv.len != bufinfo_send.len) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "recv must be same length as send")); + } + o_ret = args[1].u_obj; + } + } + + // TODO - implement 24-bit and 32-bit data width cases for all methods + // send and receive the data + HAL_StatusTypeDef status; + if (query_irq() == IRQ_STATE_DISABLED) { + status = HAL_I2SEx_TransmitReceive(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, + bufinfo_send.len / 2, args[2].u_int); + } else { + DMA_HandleTypeDef tx_dma, rx_dma; + dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &tx_dma; + dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); + self->i2s.hdmarx = &rx_dma; + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, bufinfo_send.buf, + bufinfo_recv.buf, bufinfo_send.len / 2); + if (status == HAL_OK) { + status = i2s_wait_dma_finished(&self->i2s, args[2].u_int); + } + dma_deinit(&tx_dma); + dma_deinit(&rx_dma); + } + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + // return the received data + if (o_ret != MP_OBJ_NULL) { + return o_ret; + } else { + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr_recv); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); +///////////// + +STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { + // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_i2s_init_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_i2s_deinit_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_i2s_send_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_i2s_recv_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_i2s_send_recv_obj }, + + // class constants + /// \constant MASTER - for initialising the bus to master mode + /// \constant SLAVE - for initialising the bus to slave mode + { MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(0) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE), MP_OBJ_NEW_SMALL_INT(1) }, + + // set format standard for I2S data + { MP_OBJ_NEW_QSTR(MP_QSTR_PHILIPS), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_PHILIPS) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MSB), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_MSB) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LSB), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_LSB) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PCM_SHORT), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_PCM_SHORT) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PCM_LONG), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_PCM_LONG) }, + + // set data and frame length + { MP_OBJ_NEW_QSTR(MP_QSTR__16B), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_16B) }, + { MP_OBJ_NEW_QSTR(MP_QSTR__16B_EXTENDED), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_16B_EXTENDED) }, + { MP_OBJ_NEW_QSTR(MP_QSTR__24B), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_24B) }, + { MP_OBJ_NEW_QSTR(MP_QSTR__32B), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_32B) }, + + // set CLK and WS polarity, clock source, and Master Clock output (enable/disable) + { MP_OBJ_NEW_QSTR(MP_QSTR_HIGH), MP_OBJ_NEW_SMALL_INT(I2S_CPOL_HIGH) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LOW), MP_OBJ_NEW_SMALL_INT(I2S_CPOL_LOW) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PLL), MP_OBJ_NEW_SMALL_INT(I2S_CLOCK_PLL) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_EXTERNAL), MP_OBJ_NEW_SMALL_INT(I2S_CLOCK_EXTERNAL) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ENABLE), MP_OBJ_NEW_SMALL_INT(I2S_MCLKOUTPUT_ENABLE) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_DISABLE), MP_OBJ_NEW_SMALL_INT(I2S_MCLKOUTPUT_DISABLE) }, + +}; + +STATIC MP_DEFINE_CONST_DICT(pyb_i2s_locals_dict, pyb_i2s_locals_dict_table); + +const mp_obj_type_t pyb_i2s_type = { + { &mp_type_type }, + .name = MP_QSTR_I2S, + .print = pyb_i2s_print, + .make_new = pyb_i2s_make_new, + .locals_dict = (mp_obj_t)&pyb_i2s_locals_dict, +}; diff --git a/stmhal/i2s.h b/stmhal/i2s.h new file mode 100644 index 0000000000000..2c020a6d19710 --- /dev/null +++ b/stmhal/i2s.h @@ -0,0 +1,31 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Bryan Morrissey + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +extern const mp_obj_type_t pyb_i2s_type; + +void i2s_init0(void); +void i2s_deinit(void); diff --git a/stmhal/main.c b/stmhal/main.c index ffbd6cfa960a8..40cb917093c28 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -44,6 +44,7 @@ #include "gccollect.h" #include "readline.h" #include "i2c.h" +#include "i2s.h" #include "spi.h" #include "uart.h" #include "timer.h" @@ -485,6 +486,10 @@ int main(void) { spi_init0(); pyb_usb_init0(); +#if MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 + i2s_init0(); +#endif + // Initialise the local flash filesystem. // Create it if needed, mount in on /flash, and set it as current dir. init_flash_fs(reset_mode); @@ -626,6 +631,10 @@ int main(void) { #if MICROPY_HW_ENABLE_CAN can_deinit(); #endif +#if MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 + i2s_deinit(); +#endif + first_soft_reset = false; goto soft_reset; diff --git a/stmhal/modpyb.c b/stmhal/modpyb.c index be71f3f80ed88..0f79ee55e117f 100644 --- a/stmhal/modpyb.c +++ b/stmhal/modpyb.c @@ -48,6 +48,7 @@ #include "rng.h" #include "rtc.h" #include "i2c.h" +#include "i2s.h" #include "spi.h" #include "uart.h" #include "can.h" @@ -196,6 +197,9 @@ STATIC const mp_map_elem_t pyb_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_I2C), (mp_obj_t)&pyb_i2c_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_SPI), (mp_obj_t)&pyb_spi_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_UART), (mp_obj_t)&pyb_uart_type }, +#if MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 + { MP_OBJ_NEW_QSTR(MP_QSTR_I2S), (mp_obj_t)&pyb_i2s_type }, +#endif #if MICROPY_HW_ENABLE_CAN { MP_OBJ_NEW_QSTR(MP_QSTR_CAN), (mp_obj_t)&pyb_can_type }, #endif diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h index 8a22ee4610f8d..e7ce8e87a793a 100644 --- a/stmhal/mpconfigport.h +++ b/stmhal/mpconfigport.h @@ -180,6 +180,10 @@ extern const struct _mp_obj_module_t mp_module_network; \ /* for user-mountable block device */ \ struct _fs_user_mount_t *fs_user_mount; \ + /* pointers to all I2S objects (if they have been created) */ \ + /* We define only those I2S objects that are enabled by the port, */ \ + /* each extra pointer costs 20 bytes of text and 4 bytes of BSS space */ \ + struct _pyb_i2s_obj_t *pyb_i2s_obj_all[MICROPY_HW_ENABLE_I2S2 + MICROPY_HW_ENABLE_I2S3]; \ \ /* list of registered NICs */ \ mp_obj_list_t mod_network_nic_list; \ diff --git a/stmhal/pin.h b/stmhal/pin.h index 492803871f36c..cfebd5c0a988b 100644 --- a/stmhal/pin.h +++ b/stmhal/pin.h @@ -91,5 +91,6 @@ uint32_t pin_get_af(const pin_obj_t *pin); const pin_obj_t *pin_find(mp_obj_t user_obj); const pin_obj_t *pin_find_named_pin(const mp_obj_dict_t *named_pins, mp_obj_t name); const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit); +const pin_af_obj_t *pin_find_af_type(const pin_obj_t *pin, uint8_t type); const pin_af_obj_t *pin_find_af_by_index(const pin_obj_t *pin, mp_uint_t af_idx); const pin_af_obj_t *pin_find_af_by_name(const pin_obj_t *pin, const char *name); diff --git a/stmhal/pin_defs_stmhal.h b/stmhal/pin_defs_stmhal.h index 80c967016d56f..3e64d3e117415 100644 --- a/stmhal/pin_defs_stmhal.h +++ b/stmhal/pin_defs_stmhal.h @@ -62,10 +62,10 @@ enum { AF_PIN_TYPE_TIM_ETR, AF_PIN_TYPE_TIM_BKIN, - AF_PIN_TYPE_I2C_SDA = 0, + AF_PIN_TYPE_I2C_SDA, AF_PIN_TYPE_I2C_SCL, - AF_PIN_TYPE_USART_TX = 0, + AF_PIN_TYPE_USART_TX, AF_PIN_TYPE_USART_RX, AF_PIN_TYPE_USART_CTS, AF_PIN_TYPE_USART_RTS, @@ -75,12 +75,12 @@ enum { AF_PIN_TYPE_UART_CTS = AF_PIN_TYPE_USART_CTS, AF_PIN_TYPE_UART_RTS = AF_PIN_TYPE_USART_RTS, - AF_PIN_TYPE_SPI_MOSI = 0, + AF_PIN_TYPE_SPI_MOSI, AF_PIN_TYPE_SPI_MISO, AF_PIN_TYPE_SPI_SCK, AF_PIN_TYPE_SPI_NSS, - AF_PIN_TYPE_I2S_CK = 0, + AF_PIN_TYPE_I2S_CK, AF_PIN_TYPE_I2S_MCK, AF_PIN_TYPE_I2S_SD, AF_PIN_TYPE_I2S_WS, diff --git a/stmhal/pin_named_pins.c b/stmhal/pin_named_pins.c index 9e5f9593b4d19..4533a840c1a94 100644 --- a/stmhal/pin_named_pins.c +++ b/stmhal/pin_named_pins.c @@ -69,6 +69,16 @@ const pin_af_obj_t *pin_find_af(const pin_obj_t *pin, uint8_t fn, uint8_t unit) return NULL; } +const pin_af_obj_t *pin_find_af_type(const pin_obj_t *pin, uint8_t type) { + const pin_af_obj_t *af = pin->af; + for (mp_uint_t i = 0; i < pin->num_af; i++, af++) { + if (af->type == type) { + return af; + } + } + return NULL; +} + const pin_af_obj_t *pin_find_af_by_index(const pin_obj_t *pin, mp_uint_t af_idx) { const pin_af_obj_t *af = pin->af; for (mp_uint_t i = 0; i < pin->num_af; i++, af++) { diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 5cb2557a7a104..eb3cfbabce973 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -346,6 +346,40 @@ Q(SLAVE) Q(MSB) Q(LSB) +// for I2S class +#if MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 +Q(I2S) +Q(init) +Q(deinit) +Q(send) +Q(recv) +Q(send_recv) +Q(mode) +Q(standard) +Q(dataformat) +Q(mclkout) +Q(audiofreq) +Q(polarity) +Q(clksrc) +Q(MASTER) +Q(SLAVE) +Q(PHILIPS) +Q(MSB) +Q(LSB) +Q(PCM_SHORT) +Q(PCM_LONG) +Q(_16B) +Q(_16B_EXTENDED) +Q(_24B) +Q(_32B) +Q(HIGH) +Q(LOW) +Q(PLL) +Q(EXTERNAL) +Q(ENABLE) +Q(DISABLE) +#endif + // for Accel object Q(Accel) Q(x) From 367d678d222c915f54c146b7288fa046f1c0bf74 Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 2 Jul 2015 11:55:21 -0400 Subject: [PATCH 02/33] stmhal/boards: Configuration files for Myriad2 audio processing board --- stmhal/boards/MYRIAD2/mpconfigboard.h | 70 ++++ stmhal/boards/MYRIAD2/mpconfigboard.mk | 2 + stmhal/boards/MYRIAD2/pins.csv | 59 +++ stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h | 409 +++++++++++++++++++++ 4 files changed, 540 insertions(+) create mode 100644 stmhal/boards/MYRIAD2/mpconfigboard.h create mode 100644 stmhal/boards/MYRIAD2/mpconfigboard.mk create mode 100644 stmhal/boards/MYRIAD2/pins.csv create mode 100644 stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h diff --git a/stmhal/boards/MYRIAD2/mpconfigboard.h b/stmhal/boards/MYRIAD2/mpconfigboard.h new file mode 100644 index 0000000000000..a6c90b8af823c --- /dev/null +++ b/stmhal/boards/MYRIAD2/mpconfigboard.h @@ -0,0 +1,70 @@ +#define MICROPY_HW_BOARD_NAME "Myriad2" +#define MICROPY_HW_MCU_NAME "STM32F405RG" +#define MICROPY_PY_SYS_PLATFORM "pyboard" + +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_SDCARD (1) +#define MICROPY_HW_HAS_MMA7660 (0) //blm +#define MICROPY_HW_HAS_LIS3DSH (0) +#define MICROPY_HW_HAS_LCD (0) //blm +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_TIMER (1) +#define MICROPY_HW_ENABLE_SERVO (0) //blm +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_SPI1 (1) +#define MICROPY_HW_ENABLE_SPI2 (1) +#define MICROPY_HW_ENABLE_SPI3 (0) +#define MICROPY_HW_ENABLE_CAN (0) //blm +#define MICROPY_HW_ENABLE_I2S2 (1) +#define MICROPY_HW_ENABLE_I2S3 (0) + +// HSE is 24MHz +#define MICROPY_HW_CLK_PLLM (24) +#define MICROPY_HW_CLK_PLLN (336) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (7) + +// The pyboard has a 32kHz crystal for the RTC +#define MICROPY_HW_RTC_USE_LSE (1) + +// UART config +#define MICROPY_HW_UART1_NAME "XB" +#define MICROPY_HW_UART1_PORT (GPIOB) +#define MICROPY_HW_UART1_PINS (GPIO_PIN_6 | GPIO_PIN_7) +#define MICROPY_HW_UART4_NAME "XA" +#define MICROPY_HW_UART4_PORT (GPIOA) +#define MICROPY_HW_UART4_PINS (GPIO_PIN_0 | GPIO_PIN_1) + +// I2C busses +#define MICROPY_HW_I2C1_NAME "X" +#define MICROPY_HW_I2C1_SCL (pin_B6) +#define MICROPY_HW_I2C1_SDA (pin_B7) + +// SPI busses - enabled but not really available on Myriad +#define MICROPY_HW_SPI1_NAME "X" +#define MICROPY_HW_SPI2_NAME "Y" + +// USRSW has no pullup or pulldown, and pressing the switch makes the input go low +#define MICROPY_HW_USRSW_PIN (pin_A13) +#define MICROPY_HW_USRSW_PULL (GPIO_PULLUP) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING) +#define MICROPY_HW_USRSW_PRESSED (0) + +// The Myriad2 has 4 LEDs +#define MICROPY_HW_LED1 (pin_C4) // LED1 +#define MICROPY_HW_LED2 (pin_C5) // LED2 +#define MICROPY_HW_LED3 (pin_B5) // red +#define MICROPY_HW_LED4 (pin_B4) // green +#define MICROPY_HW_LED_OTYPE (GPIO_MODE_OUTPUT_OD) +#define MICROPY_HW_LED_ON(pin) (pin->gpio->BSRRH = pin->pin_mask) +#define MICROPY_HW_LED_OFF(pin) (pin->gpio->BSRRL = pin->pin_mask) + +// SD card detect switch +#define MICROPY_HW_SDCARD_DETECT_PIN (pin_A8) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_PULLUP) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) + +// USB config +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +#define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) diff --git a/stmhal/boards/MYRIAD2/mpconfigboard.mk b/stmhal/boards/MYRIAD2/mpconfigboard.mk new file mode 100644 index 0000000000000..3c2bdab98bfb8 --- /dev/null +++ b/stmhal/boards/MYRIAD2/mpconfigboard.mk @@ -0,0 +1,2 @@ +AF_FILE = boards/stm32f405_af.csv +LD_FILE = boards/stm32f405.ld diff --git a/stmhal/boards/MYRIAD2/pins.csv b/stmhal/boards/MYRIAD2/pins.csv new file mode 100644 index 0000000000000..be3df042d1706 --- /dev/null +++ b/stmhal/boards/MYRIAD2/pins.csv @@ -0,0 +1,59 @@ +X1,PA0 +X2,PA1 +X3,PA2 +X4,PA3 +X5,PA4 +X6,PA5 +X7,PA6 +X8,PA7 +X9,PB6 +X10,PB7 +X11,PC4 +X12,PC5 +X13,Reset +X14,GND +X15,3.3V +X16,VIN +X17,PB3 +X18,PC13 +X19,PC0 +X20,PC1 +X21,PC2 +X22,PC3 +X23,A3.3V +X24,AGND +Y1,PC6 +Y2,PC7 +Y3,PB8 +Y4,PB9 +Y5,PB12 +Y6,PB13 +Y7,PB14 +Y8,PB15 +Y9,PB10 +Y10,PB11 +Y11,PB0 +Y12,PB1 +Y13,Reset +Y14,GND +Y15,3.3V +Y16,VIN +LED1,PC4 +LED2,PC5 +LED3,PB5 +LED4,PB4 +SW,PA13 +MMA_INT,PB2 +MMA_AVDD,PB5 +SD_D0,PC8 +SD_D1,PC9 +SD_D2,PC10 +SD_D3,PC11 +SD_CMD,PD2 +SD_CK,PC12 +SD,PA8 +SD_SW,PA8 +USB_VBUS,PA9 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 diff --git a/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h b/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h new file mode 100644 index 0000000000000..a8517c778e003 --- /dev/null +++ b/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h @@ -0,0 +1,409 @@ +/** + ****************************************************************************** + * @file stm32f4xx_hal_conf.h + * @author MCD Application Team + * @version V1.1.0 + * @date 19-June-2014 + * @brief HAL configuration file. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2014 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_HAL_CONF_H +#define __STM32F4xx_HAL_CONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +#define STM32F405xx +#define USE_USB_FS + +/* ########################## Module Selection ############################## */ +/** + * @brief This is the list of modules to be used in the HAL driver + */ +#define HAL_MODULE_ENABLED +#define HAL_ADC_MODULE_ENABLED +#define HAL_CAN_MODULE_ENABLED +#define HAL_CRC_MODULE_ENABLED +#define HAL_CRYP_MODULE_ENABLED +#define HAL_DAC_MODULE_ENABLED +#define HAL_DCMI_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +/* #define HAL_DMA2D_MODULE_ENABLED */ +#define HAL_ETH_MODULE_ENABLED +#define HAL_FLASH_MODULE_ENABLED +#define HAL_NAND_MODULE_ENABLED +#define HAL_NOR_MODULE_ENABLED +#define HAL_PCCARD_MODULE_ENABLED +#define HAL_SRAM_MODULE_ENABLED +/* #define HAL_SDRAM_MODULE_ENABLED */ +#define HAL_HASH_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_I2C_MODULE_ENABLED +#define HAL_I2S_MODULE_ENABLED +#define HAL_IWDG_MODULE_ENABLED +#define HAL_LTDC_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +/* #define HAL_SAI_MODULE_ENABLED */ +#define HAL_SD_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED +#define HAL_IRDA_MODULE_ENABLED +#define HAL_SMARTCARD_MODULE_ENABLED +#define HAL_WWDG_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +#define HAL_HCD_MODULE_ENABLED + + +/* ########################## HSE/HSI Values adaptation ##################### */ +/** + * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSE is used as system clock source, directly or through the PLL). + */ +#if !defined (HSE_VALUE) + /// #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ + #define HSE_VALUE ((uint32_t)24000000) /*!< Value of the External oscillator in Hz */ +#endif /* HSE_VALUE */ + +#if !defined (HSE_STARTUP_TIMEOUT) + #define HSE_STARTUP_TIMEOUT ((uint32_t)5000) /*!< Time out for HSE start up, in ms */ +#endif /* HSE_STARTUP_TIMEOUT */ + +/** + * @brief Internal High Speed oscillator (HSI) value. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSI is used as system clock source, directly or through the PLL). + */ +#if !defined (HSI_VALUE) + #define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/ +#endif /* HSI_VALUE */ + +/** + * @brief Internal Low Speed oscillator (LSI) value. + */ +#if !defined (LSI_VALUE) + #define LSI_VALUE ((uint32_t)40000) +#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz + The real value may vary depending on the variations + in voltage and temperature. */ +/** + * @brief External Low Speed oscillator (LSE) value. + */ +#if !defined (LSE_VALUE) + #define LSE_VALUE ((uint32_t)32768) /*!< Value of the External Low Speed oscillator in Hz */ +#endif /* LSE_VALUE */ + +/** + * @brief External clock source for I2S peripheral + * This value is used by the I2S HAL module to compute the I2S clock source + * frequency, this source is inserted directly through I2S_CKIN pad. + */ +#if !defined (EXTERNAL_CLOCK_VALUE) + #define EXTERNAL_CLOCK_VALUE ((uint32_t)12288000) /*!< Value of the Internal oscillator in Hz*/ +#endif /* EXTERNAL_CLOCK_VALUE */ + +/* Tip: To avoid modifying this file each time you need to use different HSE, + === you can define the HSE value in your toolchain compiler preprocessor. */ + +/* ########################### System Configuration ######################### */ +/** + * @brief This is the HAL system configuration section + */ +#define VDD_VALUE ((uint32_t)3300) /*!< Value of VDD in mv */ +#define TICK_INT_PRIORITY ((uint32_t)0x00) /*!< tick interrupt priority */ +#define USE_RTOS 0 +#define PREFETCH_ENABLE 1 +#define INSTRUCTION_CACHE_ENABLE 1 +#define DATA_CACHE_ENABLE 1 + +/* ########################## Assert Selection ############################## */ +/** + * @brief Uncomment the line below to expanse the "assert_param" macro in the + * HAL drivers code + */ +/* #define USE_FULL_ASSERT 1 */ + +/* ################## Ethernet peripheral configuration ##################### */ + +/* Section 1 : Ethernet peripheral configuration */ + +/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */ +#define MAC_ADDR0 2 +#define MAC_ADDR1 0 +#define MAC_ADDR2 0 +#define MAC_ADDR3 0 +#define MAC_ADDR4 0 +#define MAC_ADDR5 0 + +/* Definition of the Ethernet driver buffers size and count */ +#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */ +#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */ +#define ETH_RXBUFNB ((uint32_t)4) /* 4 Rx buffers of size ETH_RX_BUF_SIZE */ +#define ETH_TXBUFNB ((uint32_t)4) /* 4 Tx buffers of size ETH_TX_BUF_SIZE */ + +/* Section 2: PHY configuration section */ + +/* DP83848 PHY Address*/ +#define DP83848_PHY_ADDRESS 0x01 +/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/ +#define PHY_RESET_DELAY ((uint32_t)0x000000FF) +/* PHY Configuration delay */ +#define PHY_CONFIG_DELAY ((uint32_t)0x00000FFF) + +#define PHY_READ_TO ((uint32_t)0x0000FFFF) +#define PHY_WRITE_TO ((uint32_t)0x0000FFFF) + +/* Section 3: Common PHY Registers */ + +#define PHY_BCR ((uint16_t)0x00) /*!< Transceiver Basic Control Register */ +#define PHY_BSR ((uint16_t)0x01) /*!< Transceiver Basic Status Register */ + +#define PHY_RESET ((uint16_t)0x8000) /*!< PHY Reset */ +#define PHY_LOOPBACK ((uint16_t)0x4000) /*!< Select loop-back mode */ +#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100) /*!< Set the full-duplex mode at 100 Mb/s */ +#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000) /*!< Set the half-duplex mode at 100 Mb/s */ +#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100) /*!< Set the full-duplex mode at 10 Mb/s */ +#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000) /*!< Set the half-duplex mode at 10 Mb/s */ +#define PHY_AUTONEGOTIATION ((uint16_t)0x1000) /*!< Enable auto-negotiation function */ +#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200) /*!< Restart auto-negotiation function */ +#define PHY_POWERDOWN ((uint16_t)0x0800) /*!< Select the power down mode */ +#define PHY_ISOLATE ((uint16_t)0x0400) /*!< Isolate PHY from MII */ + +#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */ +#define PHY_LINKED_STATUS ((uint16_t)0x0004) /*!< Valid link established */ +#define PHY_JABBER_DETECTION ((uint16_t)0x0002) /*!< Jabber condition detected */ + +/* Section 4: Extended PHY Registers */ + +#define PHY_SR ((uint16_t)0x10) /*!< PHY status register Offset */ +#define PHY_MICR ((uint16_t)0x11) /*!< MII Interrupt Control Register */ +#define PHY_MISR ((uint16_t)0x12) /*!< MII Interrupt Status and Misc. Control Register */ + +#define PHY_LINK_STATUS ((uint16_t)0x0001) /*!< PHY Link mask */ +#define PHY_SPEED_STATUS ((uint16_t)0x0002) /*!< PHY Speed mask */ +#define PHY_DUPLEX_STATUS ((uint16_t)0x0004) /*!< PHY Duplex mask */ + +#define PHY_MICR_INT_EN ((uint16_t)0x0002) /*!< PHY Enable interrupts */ +#define PHY_MICR_INT_OE ((uint16_t)0x0001) /*!< PHY Enable output interrupt events */ + +#define PHY_MISR_LINK_INT_EN ((uint16_t)0x0020) /*!< Enable Interrupt on change of link status */ +#define PHY_LINK_INTERRUPT ((uint16_t)0x2000) /*!< PHY link status interrupt mask */ + +/* Includes ------------------------------------------------------------------*/ +/** + * @brief Include module's header file + */ + +#ifdef HAL_RCC_MODULE_ENABLED + #include "stm32f4xx_hal_rcc.h" +#endif /* HAL_RCC_MODULE_ENABLED */ + +#ifdef HAL_GPIO_MODULE_ENABLED + #include "stm32f4xx_hal_gpio.h" +#endif /* HAL_GPIO_MODULE_ENABLED */ + +#ifdef HAL_DMA_MODULE_ENABLED + #include "stm32f4xx_hal_dma.h" +#endif /* HAL_DMA_MODULE_ENABLED */ + +#ifdef HAL_CORTEX_MODULE_ENABLED + #include "stm32f4xx_hal_cortex.h" +#endif /* HAL_CORTEX_MODULE_ENABLED */ + +#ifdef HAL_ADC_MODULE_ENABLED + #include "stm32f4xx_hal_adc.h" +#endif /* HAL_ADC_MODULE_ENABLED */ + +#ifdef HAL_CAN_MODULE_ENABLED + #include "stm32f4xx_hal_can.h" +#endif /* HAL_CAN_MODULE_ENABLED */ + +#ifdef HAL_CRC_MODULE_ENABLED + #include "stm32f4xx_hal_crc.h" +#endif /* HAL_CRC_MODULE_ENABLED */ + +#ifdef HAL_CRYP_MODULE_ENABLED + #include "stm32f4xx_hal_cryp.h" +#endif /* HAL_CRYP_MODULE_ENABLED */ + +#ifdef HAL_DMA2D_MODULE_ENABLED + #include "stm32f4xx_hal_dma2d.h" +#endif /* HAL_DMA2D_MODULE_ENABLED */ + +#ifdef HAL_DAC_MODULE_ENABLED + #include "stm32f4xx_hal_dac.h" +#endif /* HAL_DAC_MODULE_ENABLED */ + +#ifdef HAL_DCMI_MODULE_ENABLED + #include "stm32f4xx_hal_dcmi.h" +#endif /* HAL_DCMI_MODULE_ENABLED */ + +#ifdef HAL_ETH_MODULE_ENABLED + #include "stm32f4xx_hal_eth.h" +#endif /* HAL_ETH_MODULE_ENABLED */ + +#ifdef HAL_FLASH_MODULE_ENABLED + #include "stm32f4xx_hal_flash.h" +#endif /* HAL_FLASH_MODULE_ENABLED */ + +#ifdef HAL_SRAM_MODULE_ENABLED + #include "stm32f4xx_hal_sram.h" +#endif /* HAL_SRAM_MODULE_ENABLED */ + +#ifdef HAL_NOR_MODULE_ENABLED + #include "stm32f4xx_hal_nor.h" +#endif /* HAL_NOR_MODULE_ENABLED */ + +#ifdef HAL_NAND_MODULE_ENABLED + #include "stm32f4xx_hal_nand.h" +#endif /* HAL_NAND_MODULE_ENABLED */ + +#ifdef HAL_PCCARD_MODULE_ENABLED + #include "stm32f4xx_hal_pccard.h" +#endif /* HAL_PCCARD_MODULE_ENABLED */ + +#ifdef HAL_SDRAM_MODULE_ENABLED + #include "stm32f4xx_hal_sdram.h" +#endif /* HAL_SDRAM_MODULE_ENABLED */ + +#ifdef HAL_HASH_MODULE_ENABLED + #include "stm32f4xx_hal_hash.h" +#endif /* HAL_HASH_MODULE_ENABLED */ + +#ifdef HAL_I2C_MODULE_ENABLED + #include "stm32f4xx_hal_i2c.h" +#endif /* HAL_I2C_MODULE_ENABLED */ + +#ifdef HAL_I2S_MODULE_ENABLED + #include "stm32f4xx_hal_i2s.h" +#endif /* HAL_I2S_MODULE_ENABLED */ + +#ifdef HAL_IWDG_MODULE_ENABLED + #include "stm32f4xx_hal_iwdg.h" +#endif /* HAL_IWDG_MODULE_ENABLED */ + +#ifdef HAL_LTDC_MODULE_ENABLED + #include "stm32f4xx_hal_ltdc.h" +#endif /* HAL_LTDC_MODULE_ENABLED */ + +#ifdef HAL_PWR_MODULE_ENABLED + #include "stm32f4xx_hal_pwr.h" +#endif /* HAL_PWR_MODULE_ENABLED */ + +#ifdef HAL_RNG_MODULE_ENABLED + #include "stm32f4xx_hal_rng.h" +#endif /* HAL_RNG_MODULE_ENABLED */ + +#ifdef HAL_RTC_MODULE_ENABLED + #include "stm32f4xx_hal_rtc.h" +#endif /* HAL_RTC_MODULE_ENABLED */ + +#ifdef HAL_SAI_MODULE_ENABLED + #include "stm32f4xx_hal_sai.h" +#endif /* HAL_SAI_MODULE_ENABLED */ + +#ifdef HAL_SD_MODULE_ENABLED + #include "stm32f4xx_hal_sd.h" +#endif /* HAL_SD_MODULE_ENABLED */ + +#ifdef HAL_SPI_MODULE_ENABLED + #include "stm32f4xx_hal_spi.h" +#endif /* HAL_SPI_MODULE_ENABLED */ + +#ifdef HAL_TIM_MODULE_ENABLED + #include "stm32f4xx_hal_tim.h" +#endif /* HAL_TIM_MODULE_ENABLED */ + +#ifdef HAL_UART_MODULE_ENABLED + #include "stm32f4xx_hal_uart.h" +#endif /* HAL_UART_MODULE_ENABLED */ + +#ifdef HAL_USART_MODULE_ENABLED + #include "stm32f4xx_hal_usart.h" +#endif /* HAL_USART_MODULE_ENABLED */ + +#ifdef HAL_IRDA_MODULE_ENABLED + #include "stm32f4xx_hal_irda.h" +#endif /* HAL_IRDA_MODULE_ENABLED */ + +#ifdef HAL_SMARTCARD_MODULE_ENABLED + #include "stm32f4xx_hal_smartcard.h" +#endif /* HAL_SMARTCARD_MODULE_ENABLED */ + +#ifdef HAL_WWDG_MODULE_ENABLED + #include "stm32f4xx_hal_wwdg.h" +#endif /* HAL_WWDG_MODULE_ENABLED */ + +#ifdef HAL_PCD_MODULE_ENABLED + #include "stm32f4xx_hal_pcd.h" +#endif /* HAL_PCD_MODULE_ENABLED */ + +#ifdef HAL_HCD_MODULE_ENABLED + #include "stm32f4xx_hal_hcd.h" +#endif /* HAL_HCD_MODULE_ENABLED */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F4xx_HAL_CONF_H */ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ From befe4dbfca14636d7229d77100a507b8ae877726 Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 2 Jul 2015 13:59:27 -0400 Subject: [PATCH 03/33] stmhal/boards/STM32F4DISC: Fix mpconfigboard.h and pins.h so that build on Discovery board passes Note that pin B3 was omitted from STM32F4DISC/pins.csv; not sure why this was because the pin appears to be present on the board. --- stmhal/boards/STM32F4DISC/mpconfigboard.h | 2 ++ stmhal/boards/STM32F4DISC/pins.csv | 1 + 2 files changed, 3 insertions(+) diff --git a/stmhal/boards/STM32F4DISC/mpconfigboard.h b/stmhal/boards/STM32F4DISC/mpconfigboard.h index 30a05ef972b0e..96454bcfe75ec 100644 --- a/stmhal/boards/STM32F4DISC/mpconfigboard.h +++ b/stmhal/boards/STM32F4DISC/mpconfigboard.h @@ -17,6 +17,8 @@ #define MICROPY_HW_ENABLE_SPI2 (1) #define MICROPY_HW_ENABLE_SPI3 (0) #define MICROPY_HW_ENABLE_CAN (1) +#define MICROPY_HW_ENABLE_I2S2 (1) +#define MICROPY_HW_ENABLE_I2S3 (1) // HSE is 8MHz #define MICROPY_HW_CLK_PLLM (8) diff --git a/stmhal/boards/STM32F4DISC/pins.csv b/stmhal/boards/STM32F4DISC/pins.csv index 4049fef7d919c..dbd265aebe1b7 100644 --- a/stmhal/boards/STM32F4DISC/pins.csv +++ b/stmhal/boards/STM32F4DISC/pins.csv @@ -59,6 +59,7 @@ PD4,PD4 PD5,PD5 PD6,PD6 PD7,PD7 +PB3,PB3 PB4,PB4 PB5,PB5 PB6,PB6 From e8f6cc79a5bcc6e253821af5e01dc9139eab15ca Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 6 Jul 2015 14:22:02 -0400 Subject: [PATCH 04/33] stmhal/i2s.c: Add macro guards to prevent i2s.c from compiling when both I2S instances are disabled --- stmhal/i2s.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 144445f0c9a53..f5f184daa663c 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -30,6 +30,9 @@ #include "py/nlr.h" #include "py/runtime.h" + +#if MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 + #include "py/objstr.h" #include "py/objlist.h" #include "irq.h" @@ -399,7 +402,7 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, init->CPOL = args[3].u_int; init->AudioFreq = args[4].u_int; init->MCLKOutput = args[6].u_int; - init->FullDuplexMode = self->is_duplex ? \ + init->FullDuplexMode = self->is_duplex ? I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; // -------------Possible bug in HAL------------------- @@ -599,17 +602,22 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, "Invalid pins for I2S, err: %d", err_code)); } +#if (MICROPY_HW_ENABLE_I2S3) && !(MICROPY_HW_ENABLE_I2S2) +#define I2S_OBJECT_OFFSET (3) +#else +#define I2S_OBJECT_OFFSET (2) +#endif // get I2S object pyb_i2s_obj_t *i2s_obj; - if (MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - 2] == NULL) { + if (MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET] == NULL) { // create new I2S object i2s_obj = m_new_obj(pyb_i2s_obj_t); i2s_obj->base.type = &pyb_i2s_type; i2s_obj->i2s_id = i2s_id; i2s_obj->is_enabled = false; - MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - 2] = i2s_obj; + MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET] = i2s_obj; } else { - i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - 2]; + i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET]; } i2s_obj->is_duplex = is_duplex; @@ -958,3 +966,5 @@ const mp_obj_type_t pyb_i2s_type = { .make_new = pyb_i2s_make_new, .locals_dict = (mp_obj_t)&pyb_i2s_locals_dict, }; + +#endif // MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 From bd48a8203119ce023f8da69dd9032f6256ca902c Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 14 Jul 2015 13:57:54 -0400 Subject: [PATCH 05/33] stmhal/boards/PYBV10/mpconfigboard.h: Enable I2S3 (for testing only) --- stmhal/boards/PYBV10/mpconfigboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stmhal/boards/PYBV10/mpconfigboard.h b/stmhal/boards/PYBV10/mpconfigboard.h index 2760baa25bb26..2281064a61a75 100644 --- a/stmhal/boards/PYBV10/mpconfigboard.h +++ b/stmhal/boards/PYBV10/mpconfigboard.h @@ -17,7 +17,7 @@ #define MICROPY_HW_ENABLE_SPI3 (0) #define MICROPY_HW_ENABLE_CAN (1) #define MICROPY_HW_ENABLE_I2S2 (1) -#define MICROPY_HW_ENABLE_I2S3 (0) +#define MICROPY_HW_ENABLE_I2S3 (1) // HSE is 8MHz #define MICROPY_HW_CLK_PLLM (8) From 6a0a8fccdcf5ff148b02e735c86ba21d7c413ab3 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 14 Jul 2015 14:06:32 -0400 Subject: [PATCH 06/33] stmhal/pin_defs_stmhal.h: Add comment for a minor fix --- stmhal/pin_defs_stmhal.h | 1 + 1 file changed, 1 insertion(+) diff --git a/stmhal/pin_defs_stmhal.h b/stmhal/pin_defs_stmhal.h index 3e64d3e117415..6a8ea551d2226 100644 --- a/stmhal/pin_defs_stmhal.h +++ b/stmhal/pin_defs_stmhal.h @@ -95,6 +95,7 @@ enum { #define GPIO_AF5_I2S3 GPIO_AF5_I2S3ext #define GPIO_AF6_I2S2 GPIO_AF6_I2S2ext #define GPIO_AF6_I2S3 GPIO_AF6_SPI3 +// Note: 'GPIO_AF7_SPI2' is not defined in the HAL; there is no AF7 for SPI2 #define GPIO_AF7_I2S2 GPIO_AF7_SPI2 #define GPIO_AF7_I2S3 GPIO_AF7_I2S3ext From e441993cd59f1be839f27224e1db0d62641904b1 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 14 Jul 2015 14:10:58 -0400 Subject: [PATCH 07/33] stmhal/i2s.c: Redesign pin configuration to eliminate hard-coded pin values Functional commit, but original code left in comments to be removed in next commit --- stmhal/i2s.c | 287 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 229 insertions(+), 58 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index f5f184daa663c..38e812d4add70 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -76,6 +76,7 @@ typedef struct _pyb_i2s_obj_t { uint32_t rx_dma_channel; // pins are Bit Clock, Word Select, TX Data, RX Data, and MCK Out const pin_obj_t *pins[5]; + // const pin_af_obj_t *af[5]; mp_int_t i2s_id : 8; bool is_enabled : 1; bool is_master : 1; @@ -83,6 +84,8 @@ typedef struct _pyb_i2s_obj_t { bool base_is_tx : 1; // base instance SPIx is either tx or rx } pyb_i2s_obj_t; +typedef enum { BCK, WS, TX, RX, MCK, NUM_PINS } i2s_pin_t; + // Note: This is where spi.c defines SPI_HandleTypeDef SPIHandle[1,2,3] // as well as initializing the static pyb_spi_obj[] array - // probably not needed here? @@ -163,6 +166,15 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { i2s_obj->rx_dma_stream = DMA1_Stream3; i2s_obj->rx_dma_channel = DMA_CHANNEL_0; } + + + // REVAMP THIS SECTION TO USE PROVIDED AF VLAUES; NO LONGER + // SEPARATE BETWEEN I2S2 and I2S3 + + // MCLK now gets set in make_new; no longer needed here. + // decide whether to init or not here + + /* // Configure MCLK output pin if selected; MCLK for I2S2 is PC6, AF5 i2s_obj->pins[4] = (i2s_obj->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) ? &pin_C6 : MP_OBJ_NULL; @@ -178,6 +190,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); } } + */ #endif #if MICROPY_HW_ENABLE_I2S3 } else if (i2s_obj->i2s_id == 3) { @@ -214,7 +227,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { //i2s_obj->rx_dma_stream = DMA1_Stream2; i2s_obj->rx_dma_channel = DMA_CHANNEL_0; } - + /* // Configure MCLK output pin, if selected; MCLK for I2S3 is PC7, AF6 i2s_obj->pins[4] = (i2s_obj->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) ? &pin_C7 : MP_OBJ_NULL; @@ -232,6 +245,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { } } } + */ #endif } else { // invalid i2s_id number; shouldn't get here as i2s object should not @@ -239,6 +253,28 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { return false; } + // New GPIO initialization section, supercedes the separate I2S2/3 sections + // Compiles! Try it tomorrow, try to edit out the mess above! + + // Seems to work! Check with logic analyzer and some more pin combinations + + int num_pins = (i2s_obj->i2s.Init.MCLKOutput == + I2S_MCLKOUTPUT_ENABLE) ? NUM_PINS : NUM_PINS - 1; + + for (int i = 0; i < num_pins; i++) { + if (i2s_obj->pins[i] != MP_OBJ_NULL) { + GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; + const pin_af_obj_t *af = pin_find_af(i2s_obj->pins[i], AF_FN_I2S, i2s_obj->i2s_id); + // Here I just bypass the GPIO_AFx_I2Sx macros; they are just descriptive + // names for the same integer stored in af->idx + GPIO_InitStructure.Alternate = (uint8_t)af->idx; + HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); + } + } + + + + // Configure and enable I2SPLL: // TODO: This may not be the correct method to initialize and activate // the I2SPLL, needs more testing @@ -505,20 +541,40 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, // Get array of pin objects: False / empty values are valid for pins[2:3], // empty values get set to MP_OBJ_NULL - const pin_obj_t *pins[4]; + // pins[0:3] set from user-provided array; pins[4] is MCK which is fixed to be + // C6 for I2S2 and C7 for I2S3: + const pin_obj_t *pins[5]; for (int i = 0; i < 4; i++) { if (mp_obj_is_true(pin_names[i])) { pins[i] = pin_find(pin_names[i]); } else { pins[i] = MP_OBJ_NULL; } - // can also enable this information fromn within uPy: pyb.Pin.debug(True) - if (0) { // DEBUG - print list of pin objects + + // DEBUG - print list of pin objects. This information can also + // be accessed from within uPy with the command pyb.Pin.debug(True) + if (0) { mp_obj_print((mp_obj_t)pins[i], PRINT_STR); printf("\n"); } } + //DEBUG + /* const pin_af_obj_t *af1 = pin_find_af(pins[0], (uint8_t)AF_FN_I2S, (uint8_t)2); */ + /* if (af1 == NULL) { */ + /* const pin_af_obj_t *af1 = pin_find_af(pins[0], (uint8_t)AF_FN_I2S, (uint8_t)3); */ + /* if (af1 == NULL) { */ + /* printf("Not an I2S pin\n"); */ + /* } else { */ + /* printf("%s\n", qstr_str(af1->name)); */ + /* printf("I2S 3\n"); */ + /* } */ + /* } else { */ + /* printf("%s\n", qstr_str(af1->name)); */ + /* printf("I2S 2\n"); */ + /* } */ + + // Check if pins represents a valid I2S port configuration: // Each entry of pins[] array (0=CK, 1=WS, 2=TX, 3=RX) is checked to // determine if it is valid for its designated function. @@ -526,82 +582,181 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, // of them be a valid base SD pin for the same I2S port as CK and WS; the // other can be either a valid SDext pin to select duplex mode, or empty // to select simplex mode. - // Is there a more elegant way to do this? Maybe by directly querying each - // pin to see if there is an AF for I2S associated with it? - // How much information should be conveyed through the error codes? + // Is there a more elegant way to do this? + // --------> YES!!! <------------------- + // We now query each pin to see if there is an AF for I2S associated with it. + // Error codes replaced with specific error messages. mp_uint_t i2s_id = 0; - mp_uint_t err_code = 0; - bool is_duplex; - bool base_is_tx; - if (0) { + // mp_uint_t err_code = 0; + bool is_duplex = false; + bool base_is_tx = false; + const pin_af_obj_t *pin_af[5]; + // const pin_obj_t *pin_err[1]; + //pin_err[0] = NULL; + qstr pin_err = 0; + + + // redesign pin validation code to use pin_find_af (it works now!) + // create a typedef enum to use as indices for pins[] array + // test first pin to see if it is I2S, which instance and if that instance is supported + + /* pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 2); */ + /* if ((pin_af[BCK] != NULL) && MICROPY_HW_ENABLE_I2S2) { */ + /* i2s_id = 2; */ + /* } else if (MICROPY_HW_ENABLE_I2S3) { */ + /* pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 3); */ + /* if (pin_af[BCK] != NULL) { */ + /* i2s_id = 3; */ + /* } */ + /* } */ + + // Check first entry in pins[] to determine which I2S port it belongs to + // The af will be non-NULL for at most one of these tests: #if MICROPY_HW_ENABLE_I2S2 - } else if ((pins[0] == &pin_B10 || pins[0] == &pin_B13) && - (pins[1] == &pin_B12 || pins[1] == &pin_B9)) { - // pins[0:1] are valid CLK and WS pins of I2S2, set i2s_id + pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 2); + if (pin_af[BCK]->type == AF_PIN_TYPE_I2S_CK) { i2s_id = 2; - if (pins[2] == &pin_B15 || pins[2] == &pin_C3) { - // pins[2] is valid SD pin; config as TX - base_is_tx = true; - if (pins[3] == &pin_B14 || pins[3] == &pin_C2) { - // pins[3] is valid SDext pin; duplex mode - is_duplex = true; - } else if (pins[3] == MP_OBJ_NULL) { - is_duplex = false; - } else { - err_code = 2; // pins[3] is not valid - } - } else if (pins[3] == &pin_B15 || pins[3] == &pin_C3) { - // pins[3] is valid SD pin; config as RX - base_is_tx = false; - if (pins[2] == &pin_B14 || pins[2] == &pin_C2) { - // pins[2] is valid SDext pin; duplex mode - is_duplex = true; - } else if (pins[2] == MP_OBJ_NULL) { - is_duplex = false; - } else { - err_code = 3; // pins[2] is not valid - } - } else { - err_code = 4; // No valid SD pin for I2S2 - } + pins[MCK] = &pin_C6; + } #endif #if MICROPY_HW_ENABLE_I2S3 - } else if ((pins[0] == &pin_B3 || pins[0] == &pin_C10) && - (pins[1] == &pin_A4 || pins[1] == &pin_A15)) { - // pins[0:1] are valid CLK and WS pins of I2S3, set i2s_id + pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 3); + if (pin_af[BCK]->type == AF_PIN_TYPE_I2S_CK) { i2s_id = 3; - if (pins[2] == &pin_B5 || pins[2] == &pin_C12) { - // pins[2] is valid SD pin; config as TX + pins[MCK] = &pin_C7; + } +#endif + + if (i2s_id == 0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "%q not an I2S BCK pin", pins[BCK]->name)); + } else { + for (int i = 0; i < NUM_PINS /* = 5 */; i++) { + pin_af[i] = pin_find_af(pins[i], AF_FN_I2S, i2s_id); + } + } + + for (int i = 0; i < NUM_PINS; i++) { + if (pins[i]) { + printf("pin = %s, af = %s\n", qstr_str(pins[i]->name), qstr_str(pin_af[i]->name)); + } else { + printf("pin = %s, af = %s\n", "no pin", "no af"); + } + } + + if (pin_af[WS]->type == AF_PIN_TYPE_I2S_WS) { + // Clock and Word Select pins are valid + // printf("AF Test works!\n"); + if (pin_af[TX]->type == AF_PIN_TYPE_I2S_SD) { base_is_tx = true; - if (pins[3] == &pin_B4 || pins[3] == &pin_C11) { + if (pin_af[RX]->type == AF_PIN_TYPE_I2S_EXTSD) { is_duplex = true; - } else if (pins[3] == MP_OBJ_NULL) { + } else if (pins[RX] == MP_OBJ_NULL) { is_duplex = false; } else { - err_code = 5; // pins[3] is not valid + //pin_err[0] = pins[RX]; + pin_err = pins[RX]->name; + // err_code = 2; // RX pin not valid } - } else if (pins[3] == &pin_B5 || pins[3] == &pin_C12) { - // pins[3] is valid SD pin; config as RX + } else if (pin_af[RX]->type == AF_PIN_TYPE_I2S_SD) { base_is_tx = false; - if (pins[2] == &pin_B4 || pins[2] == &pin_C11) { + if (pin_af[TX]->type == AF_PIN_TYPE_I2S_EXTSD) { is_duplex = true; - } else if (pins[2] == MP_OBJ_NULL) { + } else if (pins[TX] == MP_OBJ_NULL) { is_duplex = false; } else { - err_code = 6; // pins[2] is not valid + //pin_err[0] = pins[TX]; + pin_err = pins[TX]->name; + // err_code = 3; // TX pin not valid } } else { - err_code = 7; // No valid SD pin for I2S3 + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "no valid SD pin for I2S%d", i2s_id)); + // err_code = 4; // No valid SD pin } -#endif } else { - err_code = 1; // pins[0:1] not valid clock and WS for available I2S ports + //pin_err[0] = pins[WS]; + pin_err = pins[WS]->name; + // err_code = 1; } - if (err_code) { + + if (pin_err) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Invalid pins for I2S, err: %d", err_code)); + "%q not valid for I2S%d", + //pin_err[0]->name, i2s_id)); + pin_err, i2s_id)); } + +/* if (0) { */ +/* #if MICROPY_HW_ENABLE_I2S2 */ +/* } else if ((pins[0] == &pin_B10 || pins[0] == &pin_B13) && */ +/* (pins[1] == &pin_B12 || pins[1] == &pin_B9)) { */ +/* // pins[0:1] are valid CLK and WS pins of I2S2, set i2s_id */ +/* i2s_id = 2; */ +/* if (pins[2] == &pin_B15 || pins[2] == &pin_C3) { */ +/* // pins[2] is valid SD pin; config as TX */ +/* base_is_tx = true; */ +/* if (pins[3] == &pin_B14 || pins[3] == &pin_C2) { */ +/* // pins[3] is valid SDext pin; duplex mode */ +/* is_duplex = true; */ +/* } else if (pins[3] == MP_OBJ_NULL) { */ +/* is_duplex = false; */ +/* } else { */ +/* err_code = 2; // pins[3] is not valid */ +/* } */ +/* } else if (pins[3] == &pin_B15 || pins[3] == &pin_C3) { */ +/* // pins[3] is valid SD pin; config as RX */ +/* base_is_tx = false; */ +/* if (pins[2] == &pin_B14 || pins[2] == &pin_C2) { */ +/* // pins[2] is valid SDext pin; duplex mode */ +/* is_duplex = true; */ +/* } else if (pins[2] == MP_OBJ_NULL) { */ +/* is_duplex = false; */ +/* } else { */ +/* err_code = 3; // pins[2] is not valid */ +/* } */ +/* } else { */ +/* err_code = 4; // No valid SD pin for I2S2 */ +/* } */ +/* #endif */ +/* #if MICROPY_HW_ENABLE_I2S3 */ +/* } else if ((pins[0] == &pin_B3 || pins[0] == &pin_C10) && */ +/* (pins[1] == &pin_A4 || pins[1] == &pin_A15)) { */ +/* // pins[0:1] are valid CLK and WS pins of I2S3, set i2s_id */ +/* i2s_id = 3; */ +/* if (pins[2] == &pin_B5 || pins[2] == &pin_C12) { */ +/* // pins[2] is valid SD pin; config as TX */ +/* base_is_tx = true; */ +/* if (pins[3] == &pin_B4 || pins[3] == &pin_C11) { */ +/* is_duplex = true; */ +/* } else if (pins[3] == MP_OBJ_NULL) { */ +/* is_duplex = false; */ +/* } else { */ +/* err_code = 5; // pins[3] is not valid */ +/* } */ +/* } else if (pins[3] == &pin_B5 || pins[3] == &pin_C12) { */ +/* // pins[3] is valid SD pin; config as RX */ +/* base_is_tx = false; */ +/* if (pins[2] == &pin_B4 || pins[2] == &pin_C11) { */ +/* is_duplex = true; */ +/* } else if (pins[2] == MP_OBJ_NULL) { */ +/* is_duplex = false; */ +/* } else { */ +/* err_code = 6; // pins[2] is not valid */ +/* } */ +/* } else { */ +/* err_code = 7; // No valid SD pin for I2S3 */ +/* } */ +/* #endif */ +/* } else { */ +/* err_code = 1; // pins[0:1] not valid clock and WS for available I2S ports */ +/* } */ + /* if (err_code) { */ + /* nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, */ + /* "Invalid pins for I2S, err: %d", err_code)); */ + /* } */ + #if (MICROPY_HW_ENABLE_I2S3) && !(MICROPY_HW_ENABLE_I2S2) #define I2S_OBJECT_OFFSET (3) #else @@ -622,10 +777,26 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, i2s_obj->is_duplex = is_duplex; i2s_obj->base_is_tx = base_is_tx; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < NUM_PINS; i++) { i2s_obj->pins[i] = pins[i]; + // i2s_obj->af[i] = pin_af[i]; } + // DEBUG: + // const pin_af_obj_t *af = pin_find_af(pins[0], AF_FN_SPI, i2s_obj->i2s_id); // works + // const pin_af_obj_t *af = pin_find_af_type(pins[0], AF_PIN_TYPE_I2S_CK); // works after renumbering + // const pin_af_obj_t *af = pin_find_af_type(pins[0], AF_PIN_TYPE_SPI_SCK); // also works! + + + /* const pin_af_obj_t *af = pin_find_af_type(pins[1], AF_PIN_TYPE_I2S_WS); */ + /* if (af != NULL) { */ + /* printf("AF name = %s\n", qstr_str(af->name)); */ + /* printf("AF type = %d\n", af->type); */ + /* printf("clock pin found\n"); */ + /* } else { */ + /* printf("no clock pin\n"); */ + /* } */ + if (n_args > 1 || n_kw > 0) { // start the peripheral mp_map_t kw_args; From efa405264ad060f52e723110d6477c1118bea357 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 14 Jul 2015 14:59:43 -0400 Subject: [PATCH 08/33] stmhal/i2s.c: Clean out unused codeafter refactor --- stmhal/i2s.c | 210 +++++---------------------------------------------- 1 file changed, 18 insertions(+), 192 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 38e812d4add70..ec261d42887e7 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -73,10 +73,8 @@ typedef struct _pyb_i2s_obj_t { DMA_Stream_TypeDef *tx_dma_stream; uint32_t tx_dma_channel; DMA_Stream_TypeDef *rx_dma_stream; - uint32_t rx_dma_channel; - // pins are Bit Clock, Word Select, TX Data, RX Data, and MCK Out + uint32_t rx_dma_channel; const pin_obj_t *pins[5]; - // const pin_af_obj_t *af[5]; mp_int_t i2s_id : 8; bool is_enabled : 1; bool is_master : 1; @@ -84,6 +82,7 @@ typedef struct _pyb_i2s_obj_t { bool base_is_tx : 1; // base instance SPIx is either tx or rx } pyb_i2s_obj_t; +// pins are Bit Clock, Word Select, TX Data, RX Data, and Master Clock Out typedef enum { BCK, WS, TX, RX, MCK, NUM_PINS } i2s_pin_t; // Note: This is where spi.c defines SPI_HandleTypeDef SPIHandle[1,2,3] @@ -167,37 +166,13 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { i2s_obj->rx_dma_channel = DMA_CHANNEL_0; } - - // REVAMP THIS SECTION TO USE PROVIDED AF VLAUES; NO LONGER - // SEPARATE BETWEEN I2S2 and I2S3 - - // MCLK now gets set in make_new; no longer needed here. - // decide whether to init or not here - - /* - // Configure MCLK output pin if selected; MCLK for I2S2 is PC6, AF5 - i2s_obj->pins[4] = (i2s_obj->i2s.Init.MCLKOutput == - I2S_MCLKOUTPUT_ENABLE) ? &pin_C6 : MP_OBJ_NULL; - // Initialize GPIO pins for I2S operation - for (uint i = 0; i < 5; i++) { - if (i2s_obj->pins[i] != MP_OBJ_NULL) { - GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; - if (i2s_obj->pins[i] == &pin_B14 || i2s_obj->pins[i] == &pin_C2) { - GPIO_InitStructure.Alternate = GPIO_AF6_I2S2ext; - } else { - GPIO_InitStructure.Alternate = GPIO_AF5_SPI2; - } - HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); - } - } - */ #endif #if MICROPY_HW_ENABLE_I2S3 } else if (i2s_obj->i2s_id == 3) { i2s_obj->i2s.Instance = SPI3; __SPI3_CLK_ENABLE(); // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 - // alternate stream configurations commented out + // I2S3 has alternate stream configurations, listed here in comments if (i2s_obj->is_duplex) { if (i2s_obj->base_is_tx) { // SPI3 instance is TX @@ -227,25 +202,6 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { //i2s_obj->rx_dma_stream = DMA1_Stream2; i2s_obj->rx_dma_channel = DMA_CHANNEL_0; } - /* - // Configure MCLK output pin, if selected; MCLK for I2S3 is PC7, AF6 - i2s_obj->pins[4] = (i2s_obj->i2s.Init.MCLKOutput == - I2S_MCLKOUTPUT_ENABLE) ? &pin_C7 : MP_OBJ_NULL; - // Initialize GPIO pins for I2S operation - for (uint i = 0; i < 5; i++) { - if (i2s_obj->pins[i] != MP_OBJ_NULL) { - GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; - if (i2s_obj->pins[i] == &pin_B4) { - GPIO_InitStructure.Alternate = GPIO_AF7_I2S3ext; - } else if (i2s_obj->pins[i] == &pin_C11) { - GPIO_InitStructure.Alternate = GPIO_AF5_I2S3ext; - } else { - GPIO_InitStructure.Alternate = GPIO_AF6_SPI3; - HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); - } - } - } - */ #endif } else { // invalid i2s_id number; shouldn't get here as i2s object should not @@ -254,10 +210,10 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { } // New GPIO initialization section, supercedes the separate I2S2/3 sections - // Compiles! Try it tomorrow, try to edit out the mess above! - // Seems to work! Check with logic analyzer and some more pin combinations + // TODO - needs better description + // If master clock is not enabled, its pin will not get initialized int num_pins = (i2s_obj->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) ? NUM_PINS : NUM_PINS - 1; @@ -272,9 +228,6 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { } } - - - // Configure and enable I2SPLL: // TODO: This may not be the correct method to initialize and activate // the I2SPLL, needs more testing @@ -559,59 +512,24 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, } } - //DEBUG - /* const pin_af_obj_t *af1 = pin_find_af(pins[0], (uint8_t)AF_FN_I2S, (uint8_t)2); */ - /* if (af1 == NULL) { */ - /* const pin_af_obj_t *af1 = pin_find_af(pins[0], (uint8_t)AF_FN_I2S, (uint8_t)3); */ - /* if (af1 == NULL) { */ - /* printf("Not an I2S pin\n"); */ - /* } else { */ - /* printf("%s\n", qstr_str(af1->name)); */ - /* printf("I2S 3\n"); */ - /* } */ - /* } else { */ - /* printf("%s\n", qstr_str(af1->name)); */ - /* printf("I2S 2\n"); */ - /* } */ - - // Check if pins represents a valid I2S port configuration: - // Each entry of pins[] array (0=CK, 1=WS, 2=TX, 3=RX) is checked to + // Each entry of pins[] array (0=BCK, 1=WS, 2=TX, 3=RX) is checked to // determine if it is valid for its designated function. - // Of the two entries for data pins (2-TX and 3-RX) exactly one - // of them be a valid base SD pin for the same I2S port as CK and WS; the + // Of the two entries for data pins (2-TX and 3-RX) exactly one of them + // must be a valid base SD pin for the same I2S port as BCK and WS; the // other can be either a valid SDext pin to select duplex mode, or empty // to select simplex mode. - // Is there a more elegant way to do this? - // --------> YES!!! <------------------- - // We now query each pin to see if there is an AF for I2S associated with it. - // Error codes replaced with specific error messages. + + const pin_af_obj_t *pin_af[5]; mp_uint_t i2s_id = 0; - // mp_uint_t err_code = 0; bool is_duplex = false; bool base_is_tx = false; - const pin_af_obj_t *pin_af[5]; - // const pin_obj_t *pin_err[1]; - //pin_err[0] = NULL; qstr pin_err = 0; - - // redesign pin validation code to use pin_find_af (it works now!) - // create a typedef enum to use as indices for pins[] array - // test first pin to see if it is I2S, which instance and if that instance is supported - - /* pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 2); */ - /* if ((pin_af[BCK] != NULL) && MICROPY_HW_ENABLE_I2S2) { */ - /* i2s_id = 2; */ - /* } else if (MICROPY_HW_ENABLE_I2S3) { */ - /* pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 3); */ - /* if (pin_af[BCK] != NULL) { */ - /* i2s_id = 3; */ - /* } */ - /* } */ - - // Check first entry in pins[] to determine which I2S port it belongs to - // The af will be non-NULL for at most one of these tests: + // Check pins[BCK] (first entry in array) to determine which I2S instance + // it belongs to. The pin will have an I2S af for at most one of the + // available ports. Each I2S instance has one pin that can be used for MCK, + // this gets added to the array here as the I2S instance is selected. #if MICROPY_HW_ENABLE_I2S2 pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 2); if (pin_af[BCK]->type == AF_PIN_TYPE_I2S_CK) { @@ -636,6 +554,7 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, } } +#if (0) for (int i = 0; i < NUM_PINS; i++) { if (pins[i]) { printf("pin = %s, af = %s\n", qstr_str(pins[i]->name), qstr_str(pin_af[i]->name)); @@ -643,10 +562,10 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, printf("pin = %s, af = %s\n", "no pin", "no af"); } } +#endif if (pin_af[WS]->type == AF_PIN_TYPE_I2S_WS) { // Clock and Word Select pins are valid - // printf("AF Test works!\n"); if (pin_af[TX]->type == AF_PIN_TYPE_I2S_SD) { base_is_tx = true; if (pin_af[RX]->type == AF_PIN_TYPE_I2S_EXTSD) { @@ -654,9 +573,7 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, } else if (pins[RX] == MP_OBJ_NULL) { is_duplex = false; } else { - //pin_err[0] = pins[RX]; pin_err = pins[RX]->name; - // err_code = 2; // RX pin not valid } } else if (pin_af[RX]->type == AF_PIN_TYPE_I2S_SD) { base_is_tx = false; @@ -665,104 +582,29 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, } else if (pins[TX] == MP_OBJ_NULL) { is_duplex = false; } else { - //pin_err[0] = pins[TX]; pin_err = pins[TX]->name; - // err_code = 3; // TX pin not valid } } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "no valid SD pin for I2S%d", i2s_id)); - // err_code = 4; // No valid SD pin } } else { - //pin_err[0] = pins[WS]; pin_err = pins[WS]->name; - // err_code = 1; } if (pin_err) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "%q not valid for I2S%d", - //pin_err[0]->name, i2s_id)); pin_err, i2s_id)); } -/* if (0) { */ -/* #if MICROPY_HW_ENABLE_I2S2 */ -/* } else if ((pins[0] == &pin_B10 || pins[0] == &pin_B13) && */ -/* (pins[1] == &pin_B12 || pins[1] == &pin_B9)) { */ -/* // pins[0:1] are valid CLK and WS pins of I2S2, set i2s_id */ -/* i2s_id = 2; */ -/* if (pins[2] == &pin_B15 || pins[2] == &pin_C3) { */ -/* // pins[2] is valid SD pin; config as TX */ -/* base_is_tx = true; */ -/* if (pins[3] == &pin_B14 || pins[3] == &pin_C2) { */ -/* // pins[3] is valid SDext pin; duplex mode */ -/* is_duplex = true; */ -/* } else if (pins[3] == MP_OBJ_NULL) { */ -/* is_duplex = false; */ -/* } else { */ -/* err_code = 2; // pins[3] is not valid */ -/* } */ -/* } else if (pins[3] == &pin_B15 || pins[3] == &pin_C3) { */ -/* // pins[3] is valid SD pin; config as RX */ -/* base_is_tx = false; */ -/* if (pins[2] == &pin_B14 || pins[2] == &pin_C2) { */ -/* // pins[2] is valid SDext pin; duplex mode */ -/* is_duplex = true; */ -/* } else if (pins[2] == MP_OBJ_NULL) { */ -/* is_duplex = false; */ -/* } else { */ -/* err_code = 3; // pins[2] is not valid */ -/* } */ -/* } else { */ -/* err_code = 4; // No valid SD pin for I2S2 */ -/* } */ -/* #endif */ -/* #if MICROPY_HW_ENABLE_I2S3 */ -/* } else if ((pins[0] == &pin_B3 || pins[0] == &pin_C10) && */ -/* (pins[1] == &pin_A4 || pins[1] == &pin_A15)) { */ -/* // pins[0:1] are valid CLK and WS pins of I2S3, set i2s_id */ -/* i2s_id = 3; */ -/* if (pins[2] == &pin_B5 || pins[2] == &pin_C12) { */ -/* // pins[2] is valid SD pin; config as TX */ -/* base_is_tx = true; */ -/* if (pins[3] == &pin_B4 || pins[3] == &pin_C11) { */ -/* is_duplex = true; */ -/* } else if (pins[3] == MP_OBJ_NULL) { */ -/* is_duplex = false; */ -/* } else { */ -/* err_code = 5; // pins[3] is not valid */ -/* } */ -/* } else if (pins[3] == &pin_B5 || pins[3] == &pin_C12) { */ -/* // pins[3] is valid SD pin; config as RX */ -/* base_is_tx = false; */ -/* if (pins[2] == &pin_B4 || pins[2] == &pin_C11) { */ -/* is_duplex = true; */ -/* } else if (pins[2] == MP_OBJ_NULL) { */ -/* is_duplex = false; */ -/* } else { */ -/* err_code = 6; // pins[2] is not valid */ -/* } */ -/* } else { */ -/* err_code = 7; // No valid SD pin for I2S3 */ -/* } */ -/* #endif */ -/* } else { */ -/* err_code = 1; // pins[0:1] not valid clock and WS for available I2S ports */ -/* } */ - /* if (err_code) { */ - /* nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, */ - /* "Invalid pins for I2S, err: %d", err_code)); */ - /* } */ - #if (MICROPY_HW_ENABLE_I2S3) && !(MICROPY_HW_ENABLE_I2S2) #define I2S_OBJECT_OFFSET (3) #else #define I2S_OBJECT_OFFSET (2) #endif - // get I2S object + // get I2S object and set initialization flags pyb_i2s_obj_t *i2s_obj; if (MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET] == NULL) { // create new I2S object @@ -779,24 +621,8 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, i2s_obj->base_is_tx = base_is_tx; for (int i = 0; i < NUM_PINS; i++) { i2s_obj->pins[i] = pins[i]; - // i2s_obj->af[i] = pin_af[i]; } - // DEBUG: - // const pin_af_obj_t *af = pin_find_af(pins[0], AF_FN_SPI, i2s_obj->i2s_id); // works - // const pin_af_obj_t *af = pin_find_af_type(pins[0], AF_PIN_TYPE_I2S_CK); // works after renumbering - // const pin_af_obj_t *af = pin_find_af_type(pins[0], AF_PIN_TYPE_SPI_SCK); // also works! - - - /* const pin_af_obj_t *af = pin_find_af_type(pins[1], AF_PIN_TYPE_I2S_WS); */ - /* if (af != NULL) { */ - /* printf("AF name = %s\n", qstr_str(af->name)); */ - /* printf("AF type = %d\n", af->type); */ - /* printf("clock pin found\n"); */ - /* } else { */ - /* printf("no clock pin\n"); */ - /* } */ - if (n_args > 1 || n_kw > 0) { // start the peripheral mp_map_t kw_args; @@ -895,7 +721,7 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, self->i2s.hdmarx = NULL; status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); if (status == HAL_OK) { - led_toggle(1); + //led_toggle(1); status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } dma_deinit(&tx_dma); From af5d8eb54ccc95964fba702d03d28c518c0cdb75 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 20 Jul 2015 23:22:40 -0400 Subject: [PATCH 09/33] stmhal/Makfile: Restore entries for HAL I2S files. These were lost in the last rebase, after the HAL directory for the F2 processors were brought in. --- stmhal/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stmhal/Makefile b/stmhal/Makefile index 8b2a52d0717b0..55548859a0d1c 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -182,6 +182,8 @@ SRC_HAL = $(addprefix $(HAL_DIR)/src/stm32$(MCU_SERIES)xx_,\ hal_flash_ex.c \ hal_gpio.c \ hal_i2c.c \ + hal_i2s.c \ + hal_i2s_ex.c \ hal_pcd.c \ hal_pcd_ex.c \ hal_pwr.c \ From 503e7f13784dde0a2a8d07ac08d5e609d290b851 Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 23 Jul 2015 11:21:49 -0400 Subject: [PATCH 10/33] stmhal: STM32F4DISC: Enable HAL I2S module --- stmhal/boards/STM32F4DISC/stm32f4xx_hal_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stmhal/boards/STM32F4DISC/stm32f4xx_hal_conf.h b/stmhal/boards/STM32F4DISC/stm32f4xx_hal_conf.h index dffaaa9062223..5b5359667b16d 100644 --- a/stmhal/boards/STM32F4DISC/stm32f4xx_hal_conf.h +++ b/stmhal/boards/STM32F4DISC/stm32f4xx_hal_conf.h @@ -71,7 +71,7 @@ /* #define HAL_HASH_MODULE_ENABLED */ #define HAL_GPIO_MODULE_ENABLED #define HAL_I2C_MODULE_ENABLED -/* #define HAL_I2S_MODULE_ENABLED */ +#define HAL_I2S_MODULE_ENABLED /* #define HAL_IWDG_MODULE_ENABLED */ /* #define HAL_LTDC_MODULE_ENABLED */ #define HAL_PWR_MODULE_ENABLED From 7cb8464a047c6cc2f701910aadb3e3cb6d98e299 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 27 Jul 2015 18:07:37 -0400 Subject: [PATCH 11/33] stmhal: i2s: Rework parameter configuration to use more builtin constants and few qstr's --- stmhal/i2s.c | 39 +++++++++++++++++---------------------- stmhal/qstrdefsport.h | 8 -------- 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index ec261d42887e7..940dca80f477c 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -221,8 +221,8 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { if (i2s_obj->pins[i] != MP_OBJ_NULL) { GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; const pin_af_obj_t *af = pin_find_af(i2s_obj->pins[i], AF_FN_I2S, i2s_obj->i2s_id); - // Here I just bypass the GPIO_AFx_I2Sx macros; they are just descriptive - // names for the same integer stored in af->idx + assert(af != NULL); + // Alt function is set using af->idx instead of GPIO_AFx_I2Sx macros GPIO_InitStructure.Alternate = (uint8_t)af->idx; HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); } @@ -361,13 +361,13 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */} }, + { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_STANDARD_PHILIPS} }, - { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_DATAFORMAT_16B_EXTENDED} }, - { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CPOL_LOW} }, - // Include option for setting I2SPLL parameters directly? + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, { MP_QSTR_clksrc, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CLOCK_PLL} }, - { MP_QSTR_mclkout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_MCLKOUTPUT_DISABLE} }, + { MP_QSTR_mclkout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + // Include option for setting I2SPLL parameters directly? }; // parse args @@ -386,11 +386,17 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, init->Mode = self->base_is_tx ? I2S_MODE_SLAVE_TX : I2S_MODE_SLAVE_RX; } - init->Standard = args[1].u_int; - init->DataFormat = args[2].u_int; - init->CPOL = args[3].u_int; + if (args[1].u_int = 0) { init->DataFormat = I2S_DATAFORMAT_16B; } + else if (args[1].u_int = 16) { init->DataFormat = I2S_DATAFORMAT_16B_EXTENDED; } + else if (args[1].u_int = 24) { init->DataFormat = I2S_DATAFORMAT_24B; } + else if (args[1].u_int = 32) { init->DataFormat = I2S_DATAFORMAT_32B; } + else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "%d not a valid data format", args[2].u_int)); } + + init->Standard = args[2].u_int; + init->CPOL = args[3].u_int ? I2S_CPOL_HIGH : I2S_CPOL_LOW; init->AudioFreq = args[4].u_int; - init->MCLKOutput = args[6].u_int; + init->MCLKOutput = args[6].u_int ? I2S_MCLKOUTPUT_ENABLE : I2S_MCLKOUTPUT_DISABLE; init->FullDuplexMode = self->is_duplex ? I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; @@ -938,20 +944,9 @@ STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_PCM_SHORT), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_PCM_SHORT) }, { MP_OBJ_NEW_QSTR(MP_QSTR_PCM_LONG), MP_OBJ_NEW_SMALL_INT(I2S_STANDARD_PCM_LONG) }, - // set data and frame length - { MP_OBJ_NEW_QSTR(MP_QSTR__16B), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_16B) }, - { MP_OBJ_NEW_QSTR(MP_QSTR__16B_EXTENDED), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_16B_EXTENDED) }, - { MP_OBJ_NEW_QSTR(MP_QSTR__24B), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_24B) }, - { MP_OBJ_NEW_QSTR(MP_QSTR__32B), MP_OBJ_NEW_SMALL_INT(I2S_DATAFORMAT_32B) }, - - // set CLK and WS polarity, clock source, and Master Clock output (enable/disable) - { MP_OBJ_NEW_QSTR(MP_QSTR_HIGH), MP_OBJ_NEW_SMALL_INT(I2S_CPOL_HIGH) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_LOW), MP_OBJ_NEW_SMALL_INT(I2S_CPOL_LOW) }, + // set I2S clock source { MP_OBJ_NEW_QSTR(MP_QSTR_PLL), MP_OBJ_NEW_SMALL_INT(I2S_CLOCK_PLL) }, { MP_OBJ_NEW_QSTR(MP_QSTR_EXTERNAL), MP_OBJ_NEW_SMALL_INT(I2S_CLOCK_EXTERNAL) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_ENABLE), MP_OBJ_NEW_SMALL_INT(I2S_MCLKOUTPUT_ENABLE) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_DISABLE), MP_OBJ_NEW_SMALL_INT(I2S_MCLKOUTPUT_DISABLE) }, - }; STATIC MP_DEFINE_CONST_DICT(pyb_i2s_locals_dict, pyb_i2s_locals_dict_table); diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index eb3cfbabce973..2ccfaa3c8515b 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -368,16 +368,8 @@ Q(MSB) Q(LSB) Q(PCM_SHORT) Q(PCM_LONG) -Q(_16B) -Q(_16B_EXTENDED) -Q(_24B) -Q(_32B) -Q(HIGH) -Q(LOW) Q(PLL) Q(EXTERNAL) -Q(ENABLE) -Q(DISABLE) #endif // for Accel object From 5a94b80aa19d716dedcab5605f4301716856e601 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 27 Jul 2015 18:58:38 -0400 Subject: [PATCH 12/33] stmhal/i2s.c: Change pyb_i2s_print function so that text matches kwarg names by reusing existing qstr's --- stmhal/i2s.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 940dca80f477c..cecd6840681af 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -239,6 +239,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { __HAL_RCC_PLLI2S_ENABLE(); } + if (HAL_I2S_Init(&i2s_obj->i2s) != HAL_OK) { // This message is redundant, exception will be raised by return value printf("OSError: HAL_I2S_Init failed\n"); @@ -311,7 +312,7 @@ void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_i2s_obj_t *self = self_in; - mp_printf(print, "I2S(%u on [", self->i2s_id); + mp_printf(print, "I2S(%u, pins=[", self->i2s_id); for (int i = 0; i < 4; i++) { if (self->pins[i] != MP_OBJ_NULL) { mp_printf(print, "%q ", self->pins[i]->name); @@ -319,26 +320,36 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_print_str(print, "None "); } } - mp_print_str(print, "\b]"); + mp_print_str(print, "\b], I2S."); if (self->is_enabled) { if (self->i2s.Init.Mode == I2S_MODE_MASTER_TX || self->i2s.Init.Mode == I2S_MODE_MASTER_RX) { - mp_print_str(print, ", I2S.MASTER, MCLK "); + mp_printf(print, "%q, %q ", MP_QSTR_MASTER, MP_QSTR_mclkout); if (self->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { mp_printf(print, "on %q", self->pins[4]->name); } else { mp_print_str(print, "off"); } - mp_printf(print, ", freq=%u", self->i2s.Init.AudioFreq); + mp_printf(print, ", %q=%u", MP_QSTR_audiofreq, self->i2s.Init.AudioFreq); } else if (self->i2s.Init.Mode == I2S_MODE_SLAVE_TX || self->i2s.Init.Mode == I2S_MODE_SLAVE_RX) { - mp_print_str(print, ", I2S.SLAVE"); + mp_printf(print, "%q", MP_QSTR_SLAVE); } else { // Shouldn't get here if self->is_enabled=true } - mp_printf(print, ", standard=%u, format=%u, polarity=%u", - self->i2s.Init.Standard, self->i2s.Init.DataFormat, - self->i2s.Init.CPOL); + + qstr standard = 0; + if (self->i2s.Init.Standard == I2S_STANDARD_PHILIPS) { standard = MP_QSTR_PHILIPS; } + else if (self->i2s.Init.Standard == I2S_STANDARD_MSB) { standard = MP_QSTR_MSB; } + else if (self->i2s.Init.Standard == I2S_STANDARD_LSB) { standard = MP_QSTR_LSB; } + else if (self->i2s.Init.Standard == I2S_STANDARD_PCM_SHORT) { standard = MP_QSTR_PCM_SHORT; } + else if (self->i2s.Init.Standard == I2S_STANDARD_PCM_LONG) { standard = MP_QSTR_PCM_LONG; } + else { /* shouldn't get here */ } + mp_printf(print, ", %q=%u, %q=%q, ", + MP_QSTR_polarity, self->i2s.Init.CPOL, MP_QSTR_standard, standard); + int n_data_bits = ((self->i2s.Init.DataFormat >> 1) + 2) * 8; + mp_printf(print, "%q=%uB%s", MP_QSTR_dataformat, n_data_bits, + self->i2s.Init.DataFormat == I2S_DATAFORMAT_16B_EXTENDED ? "_EXT" : ""); } mp_print_str(print, ")"); } @@ -361,7 +372,7 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */} }, - { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} /* 16B EXTENDED*/}, { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_STANDARD_PHILIPS} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, @@ -386,10 +397,10 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, init->Mode = self->base_is_tx ? I2S_MODE_SLAVE_TX : I2S_MODE_SLAVE_RX; } - if (args[1].u_int = 0) { init->DataFormat = I2S_DATAFORMAT_16B; } - else if (args[1].u_int = 16) { init->DataFormat = I2S_DATAFORMAT_16B_EXTENDED; } - else if (args[1].u_int = 24) { init->DataFormat = I2S_DATAFORMAT_24B; } - else if (args[1].u_int = 32) { init->DataFormat = I2S_DATAFORMAT_32B; } + if (args[1].u_int == 0) { init->DataFormat = I2S_DATAFORMAT_16B; } + else if (args[1].u_int == 16) { init->DataFormat = I2S_DATAFORMAT_16B_EXTENDED; } + else if (args[1].u_int == 24) { init->DataFormat = I2S_DATAFORMAT_24B; } + else if (args[1].u_int == 32) { init->DataFormat = I2S_DATAFORMAT_32B; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "%d not a valid data format", args[2].u_int)); } From ab3d9336ba9315a990f701a7913404f119aff172 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 27 Jul 2015 23:30:18 -0400 Subject: [PATCH 13/33] stmhal/i2s.c: Adjust parameter initialization and update comments in pyb_i2s_init_helper() --- stmhal/i2s.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index cecd6840681af..7ac234d42570a 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -354,25 +354,25 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_print_str(print, ")"); } -/// \method init(mode, standard=I2S.PHILIPS, dataformat=I2S._16B_EXTENDED, -/// polarity=I2S.LOW, audiofreq=48000, -/// clksrc=I2S.PLL, mclkout=I2S.DISABLE) +/// \method init(mode, standard=I2S.PHILIPS, dataformat=0, +/// polarity=0, audiofreq=48000, +/// clksrc=I2S.PLL, mclkout=0) /// /// Initialise the I2S bus with the given parameters: /// /// - `mode` must be either `I2S.MASTER` or `I2S.SLAVE`. /// - `standard` can be `PHILIPS`, `MSB`, `LSB`, `PCM_SHORT`, or `PCM_LONG`. -/// - `dataformat` can be `_16B`, `_16B_EXTENDED`, `_24B`, or `_32B`. -/// - `polarity` can be `HIGH` or `LOW`. +/// - `dataformat` can be 0 (16B_EXTENDED), 16, 24, or 32 (bits). +/// - `polarity` can be 0 for clock steady state low, or 1 for steady state high. /// - Options only relevant to master mode: /// - `audiofreq` can be any common audio sampling frequency, default is 48000. /// - `clksrc` can be `PLL` or `EXTERNAL`. -/// - `mclkout` can be `ENABLE` or `DISABLE` +/// - `mclkout` can be 0 to disable or 1 to enable. STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */} }, - { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} /* 16B EXTENDED*/}, + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */ } }, + { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 /* 16B EXTENDED */ } }, { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_STANDARD_PHILIPS} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, @@ -397,8 +397,8 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, init->Mode = self->base_is_tx ? I2S_MODE_SLAVE_TX : I2S_MODE_SLAVE_RX; } - if (args[1].u_int == 0) { init->DataFormat = I2S_DATAFORMAT_16B; } - else if (args[1].u_int == 16) { init->DataFormat = I2S_DATAFORMAT_16B_EXTENDED; } + if (args[1].u_int == 0) { init->DataFormat = I2S_DATAFORMAT_16B_EXTENDED; } + else if (args[1].u_int == 16) { init->DataFormat = I2S_DATAFORMAT_16B; } else if (args[1].u_int == 24) { init->DataFormat = I2S_DATAFORMAT_24B; } else if (args[1].u_int == 32) { init->DataFormat = I2S_DATAFORMAT_32B; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, From c57c9e3affb1d7f496c507178422ac9a4e742fae Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 18 Aug 2015 14:59:03 -0400 Subject: [PATCH 14/33] stmhal/boards/MYRIAD2: Update built files for MYRIAD2 --- stmhal/boards/MYRIAD2/mpconfigboard.mk | 2 ++ stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h | 33 +++++++++++----------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/stmhal/boards/MYRIAD2/mpconfigboard.mk b/stmhal/boards/MYRIAD2/mpconfigboard.mk index 3c2bdab98bfb8..5734c66904e96 100644 --- a/stmhal/boards/MYRIAD2/mpconfigboard.mk +++ b/stmhal/boards/MYRIAD2/mpconfigboard.mk @@ -1,2 +1,4 @@ +MCU_SERIES = f4 +CMSIS_MCU = STM32F405xx AF_FILE = boards/stm32f405_af.csv LD_FILE = boards/stm32f405.ld diff --git a/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h b/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h index a8517c778e003..639d4b5afff91 100644 --- a/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h +++ b/stmhal/boards/MYRIAD2/stm32f4xx_hal_conf.h @@ -46,7 +46,6 @@ /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ -#define STM32F405xx #define USE_USB_FS /* ########################## Module Selection ############################## */ @@ -56,25 +55,25 @@ #define HAL_MODULE_ENABLED #define HAL_ADC_MODULE_ENABLED #define HAL_CAN_MODULE_ENABLED -#define HAL_CRC_MODULE_ENABLED -#define HAL_CRYP_MODULE_ENABLED +/* #define HAL_CRC_MODULE_ENABLED */ +/* #define HAL_CRYP_MODULE_ENABLED */ #define HAL_DAC_MODULE_ENABLED -#define HAL_DCMI_MODULE_ENABLED +/* #define HAL_DCMI_MODULE_ENABLED */ #define HAL_DMA_MODULE_ENABLED /* #define HAL_DMA2D_MODULE_ENABLED */ -#define HAL_ETH_MODULE_ENABLED +/* #define HAL_ETH_MODULE_ENABLED */ #define HAL_FLASH_MODULE_ENABLED -#define HAL_NAND_MODULE_ENABLED -#define HAL_NOR_MODULE_ENABLED -#define HAL_PCCARD_MODULE_ENABLED -#define HAL_SRAM_MODULE_ENABLED +/* #define HAL_NAND_MODULE_ENABLED */ +/* #define HAL_NOR_MODULE_ENABLED */ +/* #define HAL_PCCARD_MODULE_ENABLED */ +/* #define HAL_SRAM_MODULE_ENABLED */ /* #define HAL_SDRAM_MODULE_ENABLED */ -#define HAL_HASH_MODULE_ENABLED +/* #define HAL_HASH_MODULE_ENABLED */ #define HAL_GPIO_MODULE_ENABLED #define HAL_I2C_MODULE_ENABLED #define HAL_I2S_MODULE_ENABLED -#define HAL_IWDG_MODULE_ENABLED -#define HAL_LTDC_MODULE_ENABLED +/* #define HAL_IWDG_MODULE_ENABLED */ +/* #define HAL_LTDC_MODULE_ENABLED */ #define HAL_PWR_MODULE_ENABLED #define HAL_RCC_MODULE_ENABLED #define HAL_RNG_MODULE_ENABLED @@ -84,13 +83,13 @@ #define HAL_SPI_MODULE_ENABLED #define HAL_TIM_MODULE_ENABLED #define HAL_UART_MODULE_ENABLED -#define HAL_USART_MODULE_ENABLED -#define HAL_IRDA_MODULE_ENABLED -#define HAL_SMARTCARD_MODULE_ENABLED -#define HAL_WWDG_MODULE_ENABLED +/* #define HAL_USART_MODULE_ENABLED */ +/* #define HAL_IRDA_MODULE_ENABLED */ +/* #define HAL_SMARTCARD_MODULE_ENABLED */ +/* #define HAL_WWDG_MODULE_ENABLED */ #define HAL_CORTEX_MODULE_ENABLED #define HAL_PCD_MODULE_ENABLED -#define HAL_HCD_MODULE_ENABLED +/* #define HAL_HCD_MODULE_ENABLED */ /* ########################## HSE/HSI Values adaptation ##################### */ From 90907ac214484807e26f15a1e8c87ed42c52b873 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 18 Aug 2015 23:22:06 -0400 Subject: [PATCH 15/33] stmhal/i2s.c: Add stub methods more i2s.stream_out and stream_in These methods don't really do anything yet --- stmhal/i2s.c | 103 ++++++++++++++++++++++++++++++++++++++++++ stmhal/qstrdefsport.h | 2 + 2 files changed, 105 insertions(+) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 7ac234d42570a..5ad544b749ad6 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -35,6 +35,8 @@ #include "py/objstr.h" #include "py/objlist.h" +#include "py/stream.h" +#include "file.h" // for stream methods? #include "irq.h" #include "pin.h" #include "led.h" // For led_toggle(n) debugging @@ -934,6 +936,105 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); ///////////// +///// Streaming methods: +// Taken from stream.c: +#if MICROPY_STREAMS_NON_BLOCK +// TODO: This is POSIX-specific (but then POSIX is the only real thing, +// and anything else just emulates it, right?) +#include +#define is_nonblocking_error(errno) ((errno) == EAGAIN || (errno) == EWOULDBLOCK) +#else +#define is_nonblocking_error(errno) (0) +#endif + +#define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) + + +STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, + mp_map_t *kw_args) { + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_stream_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + // pyb_i2s_obj_t *self = pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; + // Check that 'stream' provides an mp_stream_p_t and a read + // Note that 'read' will be present even if the stream opened in write-mode + if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Object type %s not a readable stream", + mp_obj_get_type_str(stream))); + } + + mp_int_t sz = 1024; + vstr_t vstr; + vstr_init_len(&vstr, sz); + int error; + mp_uint_t out_sz = stream->type->stream_p->read(stream, vstr.buf, sz, &error); + + /* mp_obj_t buffer = stream_read(2, args[0].u_obj, 1024); */ + /* // mp_obj_t buffer = file_obj_read(stream, 1024); */ + /* // get the buffer to send from */ + /* mp_buffer_info_t bufinfo; */ + /* uint8_t data[1]; */ + /* pyb_buf_get_for_send(buffer, &bufinfo, data); */ + if (out_sz == MP_STREAM_ERROR) { + vstr_clear(&vstr); + if (is_nonblocking_error(error)) { + // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read + // "If the object is in non-blocking mode and no bytes are available, + // None is returned." + // This is actually very weird, as naive truth check will treat + // this as EOF. + return mp_const_none; + } + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } else { + vstr.len = out_sz; + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + } + + //return stream; + /* vstr.len = out_sz; */ + /* return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream->type->stream_p), &vstr); */ + //return (mp_obj_t)out_sz; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); + +STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, + mp_map_t *kw_args) { + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_stream_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + // pyb_i2s_obj_t *self = pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t stream = args[0].u_obj; + // Got the file; the following check doesn't do anything. + // need to check if the handle has a 'write' method + if (stream == NULL /* || stream->type->stream_p->write == NULL */) { + // CPython: io.UnsupportedOperation, OSError subclass + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Operation not supported")); + } + + return stream; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); + + STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_i2s_init_obj }, @@ -941,6 +1042,8 @@ STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_i2s_send_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_i2s_recv_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_i2s_send_recv_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_stream_out), (mp_obj_t)&pyb_i2s_stream_out_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_stream_in), (mp_obj_t)&pyb_i2s_stream_in_obj }, // class constants /// \constant MASTER - for initialising the bus to master mode diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 2ccfaa3c8515b..d0511b2684d75 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -370,6 +370,8 @@ Q(PCM_SHORT) Q(PCM_LONG) Q(PLL) Q(EXTERNAL) +Q(stream_out) +Q(stream_in) #endif // for Accel object From d7fb034360441472f968b833d85800b944a1c69e Mon Sep 17 00:00:00 2001 From: blmorris Date: Fri, 21 Aug 2015 16:10:27 -0400 Subject: [PATCH 16/33] stmhal/i2s.c: First commit with (semi-)working stream_out function --- stmhal/i2s.c | 115 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 25 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 5ad544b749ad6..b8532eaec10eb 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -69,19 +69,27 @@ // I2S3_EXT RX: DMA1_Stream0.CHANNEL_3 or DMA1_Stream2.CHANNEL_2 // I2S3_EXT TX: DMA1_Stream5.CHANNEL_2 +#define AUDIOBUFFER_BYTES 8192 + typedef struct _pyb_i2s_obj_t { mp_obj_base_t base; I2S_HandleTypeDef i2s; DMA_Stream_TypeDef *tx_dma_stream; uint32_t tx_dma_channel; DMA_Stream_TypeDef *rx_dma_stream; - uint32_t rx_dma_channel; + uint32_t rx_dma_channel; + mp_obj_base_t *dstream_tx; + mp_obj_base_t *dstream_rx; const pin_obj_t *pins[5]; + // audio buffers placed before smaller struct members to keep word-aligned + uint16_t audiobuf_tx[2][AUDIOBUFFER_BYTES / 8]; + uint16_t audiobuf_rx[2][AUDIOBUFFER_BYTES / 8]; mp_int_t i2s_id : 8; bool is_enabled : 1; bool is_master : 1; bool is_duplex : 1; bool base_is_tx : 1; // base instance SPIx is either tx or rx + bool pp_ptr : 1; // ping-pong pointer for double buffers } pyb_i2s_obj_t; // pins are Bit Clock, Word Select, TX Data, RX Data, and Master Clock Out @@ -100,7 +108,7 @@ const DMA_InitTypeDef dma_init_struct_i2s = { .PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD, .MemDataAlignment = DMA_MDATAALIGN_HALFWORD, .Mode = DMA_NORMAL, - .Priority = DMA_PRIORITY_HIGH, + .Priority = DMA_PRIORITY_VERY_HIGH, //.FIFOMode = DMA_FIFOMODE_DISABLE, .FIFOMode = DMA_FIFOMODE_ENABLE, //.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL, @@ -282,6 +290,27 @@ STATIC HAL_StatusTypeDef i2s_wait_dma_finished(I2S_HandleTypeDef *i2s, uint32_t return HAL_OK; } + +STATIC void stream_out_buffering(pyb_i2s_obj_t *self) { + int buf_sz = AUDIOBUFFER_BYTES / 4; + int error; + + HAL_StatusTypeDef status; + status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + self->pp_ptr = !(self->pp_ptr); + + mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + + if (out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } +} + // Debug placeholders for DMA callback methods void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { //printf("I2S-TxHalfCplt\n"); @@ -289,8 +318,21 @@ void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { } void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { - printf("I2S-TxCplt\n"); - //led_toggle(3); + led_state(1, 1); + int rp_index = 0; + // If both I2S instances are enabled, set root pointer index to 1 if I2S + // instance is SPI3/I2S3; otherwise index is 0 +#if MICROPY_HW_ENABLE_I2S2 && MICROPY_HW_ENABLE_I2S3 + if (hi2s->Instance == SPI3) { + rp_index = 1; + } +#endif + + pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; + stream_out_buffering(self); + + //printf("I2S-TxCplt\n"); + led_state(1, 0); } void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { @@ -298,7 +340,7 @@ void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { } void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { - printf("I2S-RxCplt\n"); + //printf("I2S-RxCplt\n"); } void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { @@ -959,34 +1001,36 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, }; // parse args - // pyb_i2s_obj_t *self = pos_args[0]; + pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + // Re-work this to use the mp_obj_get_type idiom (more clarity): struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; + //mp_obj_t stream = args[0].u_obj; + //mp_obj_type_t *type = mp_obj_get_type(stream); // Check that 'stream' provides an mp_stream_p_t and a read // Note that 'read' will be present even if the stream opened in write-mode + //if (type->stream_p == NULL || type->stream_p->read == NULL) { if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Object type %s not a readable stream", mp_obj_get_type_str(stream))); } - mp_int_t sz = 1024; - vstr_t vstr; - vstr_init_len(&vstr, sz); + self->dstream_tx = stream; + + + + int buf_sz = AUDIOBUFFER_BYTES / 4; int error; - mp_uint_t out_sz = stream->type->stream_p->read(stream, vstr.buf, sz, &error); - - /* mp_obj_t buffer = stream_read(2, args[0].u_obj, 1024); */ - /* // mp_obj_t buffer = file_obj_read(stream, 1024); */ - /* // get the buffer to send from */ - /* mp_buffer_info_t bufinfo; */ - /* uint8_t data[1]; */ - /* pyb_buf_get_for_send(buffer, &bufinfo, data); */ + self->pp_ptr = 0; + + //mp_uint_t out_sz = stream->type->stream_p->read(stream, tx_buf, sz, &error); + mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + if (out_sz == MP_STREAM_ERROR) { - vstr_clear(&vstr); if (is_nonblocking_error(error)) { // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read // "If the object is in non-blocking mode and no bytes are available, @@ -996,18 +1040,39 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, return mp_const_none; } nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); - } else { - vstr.len = out_sz; - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } - //return stream; - /* vstr.len = out_sz; */ - /* return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream->type->stream_p), &vstr); */ - //return (mp_obj_t)out_sz; + DMA_HandleTypeDef tx_dma; + dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &tx_dma; + self->i2s.hdmarx = NULL; + + // A hack to make sure that slave-mode transfers don't start on the wrong clock: + if (!(self->is_master)) { + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} + } + + stream_out_buffering(self); + ///////////////////// + /* HAL_StatusTypeDef status; */ + /* status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); */ + /* if (status != HAL_OK) { */ + /* mp_hal_raise(status); */ + /* } */ + + /* self->pp_ptr = !(self->pp_ptr); */ + /* out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); */ + /* if (out_sz == MP_STREAM_ERROR) { */ + /* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ + /* } */ + ////////////////// + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); + STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { From 4cf0c21176e33a64ccfadce2f83a5e265a98e315 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 24 Aug 2015 15:30:39 -0400 Subject: [PATCH 17/33] stmhal/i2s.c: Whitespace cleanup and untabify --- stmhal/i2s.c | 796 +++++++++++++++++++++++++-------------------------- 1 file changed, 397 insertions(+), 399 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index b8532eaec10eb..68360f8211e7b 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -78,18 +78,18 @@ typedef struct _pyb_i2s_obj_t { uint32_t tx_dma_channel; DMA_Stream_TypeDef *rx_dma_stream; uint32_t rx_dma_channel; - mp_obj_base_t *dstream_tx; - mp_obj_base_t *dstream_rx; + mp_obj_base_t *dstream_tx; + mp_obj_base_t *dstream_rx; const pin_obj_t *pins[5]; - // audio buffers placed before smaller struct members to keep word-aligned - uint16_t audiobuf_tx[2][AUDIOBUFFER_BYTES / 8]; - uint16_t audiobuf_rx[2][AUDIOBUFFER_BYTES / 8]; + // audio buffers placed before smaller struct members to keep word-aligned + uint16_t audiobuf_tx[2][AUDIOBUFFER_BYTES / 8]; + uint16_t audiobuf_rx[2][AUDIOBUFFER_BYTES / 8]; mp_int_t i2s_id : 8; bool is_enabled : 1; bool is_master : 1; bool is_duplex : 1; bool base_is_tx : 1; // base instance SPIx is either tx or rx - bool pp_ptr : 1; // ping-pong pointer for double buffers + bool pp_ptr : 1; // ping-pong pointer for double buffers } pyb_i2s_obj_t; // pins are Bit Clock, Word Select, TX Data, RX Data, and Master Clock Out @@ -120,7 +120,7 @@ const DMA_InitTypeDef dma_init_struct_i2s = { // i2s_init0 is direct crib from stmhal/can.c - can_init0() void i2s_init0(void) { for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_i2s_obj_all)); i++) { - MP_STATE_PORT(pyb_i2s_obj_all)[i] = NULL; + MP_STATE_PORT(pyb_i2s_obj_all)[i] = NULL; } } @@ -149,74 +149,73 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { if (0) { #if MICROPY_HW_ENABLE_I2S2 } else if (i2s_obj->i2s_id == 2) { - i2s_obj->i2s.Instance = SPI2; - __SPI2_CLK_ENABLE(); - // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 - if (i2s_obj->is_duplex) { - if (i2s_obj->base_is_tx) { - // SPI2 instance is Tx - i2s_obj->tx_dma_stream = DMA1_Stream4; - i2s_obj->tx_dma_channel = DMA_CHANNEL_0; - // I2S2_ext instance is Rx - i2s_obj->rx_dma_stream = DMA1_Stream3; - i2s_obj->rx_dma_channel = DMA_CHANNEL_3; - } else { - // I2S2_ext instance is Rx - i2s_obj->tx_dma_stream = DMA1_Stream4; - i2s_obj->tx_dma_channel = DMA_CHANNEL_2; - // SPI2 instance is Rx - i2s_obj->rx_dma_stream = DMA1_Stream3; - i2s_obj->rx_dma_channel = DMA_CHANNEL_0; - } - } else { - // simplex: set up both DMA streams for SPI2, only one will be used - i2s_obj->tx_dma_stream = DMA1_Stream4; - i2s_obj->tx_dma_channel = DMA_CHANNEL_0; - i2s_obj->rx_dma_stream = DMA1_Stream3; - i2s_obj->rx_dma_channel = DMA_CHANNEL_0; - } - + i2s_obj->i2s.Instance = SPI2; + __SPI2_CLK_ENABLE(); + // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 + if (i2s_obj->is_duplex) { + if (i2s_obj->base_is_tx) { + // SPI2 instance is Tx + i2s_obj->tx_dma_stream = DMA1_Stream4; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + // I2S2_ext instance is Rx + i2s_obj->rx_dma_stream = DMA1_Stream3; + i2s_obj->rx_dma_channel = DMA_CHANNEL_3; + } else { + // I2S2_ext instance is Rx + i2s_obj->tx_dma_stream = DMA1_Stream4; + i2s_obj->tx_dma_channel = DMA_CHANNEL_2; + // SPI2 instance is Rx + i2s_obj->rx_dma_stream = DMA1_Stream3; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } + } else { + // simplex: set up both DMA streams for SPI2, only one will be used + i2s_obj->tx_dma_stream = DMA1_Stream4; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + i2s_obj->rx_dma_stream = DMA1_Stream3; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } #endif #if MICROPY_HW_ENABLE_I2S3 } else if (i2s_obj->i2s_id == 3) { - i2s_obj->i2s.Instance = SPI3; - __SPI3_CLK_ENABLE(); - // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 - // I2S3 has alternate stream configurations, listed here in comments - if (i2s_obj->is_duplex) { - if (i2s_obj->base_is_tx) { - // SPI3 instance is TX - i2s_obj->tx_dma_stream = DMA1_Stream5; - //i2s_obj->tx_dma_stream = DMA1_Stream7; - i2s_obj->tx_dma_channel = DMA_CHANNEL_0; - // I2S3_ext instance is RX (DMA1_Stream0.CHANNEL_3 or DMA1_Stream2.CHANNEL_2) - i2s_obj->rx_dma_stream = DMA1_Stream0; - i2s_obj->rx_dma_channel = DMA_CHANNEL_3; - //i2s_obj->rx_dma_stream = DMA1_Stream2; - //i2s_obj->rx_dma_channel = DMA_CHANNEL_2; - } else { - // I2S3_ext instance is TX - i2s_obj->tx_dma_stream = DMA1_Stream5; - i2s_obj->tx_dma_channel = DMA_CHANNEL_2; - // SPI3 instance is RX - i2s_obj->rx_dma_stream = DMA1_Stream0; - //i2s_obj->rx_dma_stream = DMA1_Stream2; - i2s_obj->rx_dma_channel = DMA_CHANNEL_0; - } - } else { - // Simplex: set up both DMA streams for SPI3, only one will be used - i2s_obj->tx_dma_stream = DMA1_Stream5; - //i2s_obj->tx_dma_stream = DMA1_Stream7; - i2s_obj->tx_dma_channel = DMA_CHANNEL_0; - i2s_obj->rx_dma_stream = DMA1_Stream0; - //i2s_obj->rx_dma_stream = DMA1_Stream2; - i2s_obj->rx_dma_channel = DMA_CHANNEL_0; - } + i2s_obj->i2s.Instance = SPI3; + __SPI3_CLK_ENABLE(); + // configure DMA streams - see RM0090 section 10.3.3, Tables 42 & 43 + // I2S3 has alternate stream configurations, listed here in comments + if (i2s_obj->is_duplex) { + if (i2s_obj->base_is_tx) { + // SPI3 instance is TX + i2s_obj->tx_dma_stream = DMA1_Stream5; + //i2s_obj->tx_dma_stream = DMA1_Stream7; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + // I2S3_ext instance is RX (DMA1_Stream0.CHANNEL_3 or DMA1_Stream2.CHANNEL_2) + i2s_obj->rx_dma_stream = DMA1_Stream0; + i2s_obj->rx_dma_channel = DMA_CHANNEL_3; + //i2s_obj->rx_dma_stream = DMA1_Stream2; + //i2s_obj->rx_dma_channel = DMA_CHANNEL_2; + } else { + // I2S3_ext instance is TX + i2s_obj->tx_dma_stream = DMA1_Stream5; + i2s_obj->tx_dma_channel = DMA_CHANNEL_2; + // SPI3 instance is RX + i2s_obj->rx_dma_stream = DMA1_Stream0; + //i2s_obj->rx_dma_stream = DMA1_Stream2; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } + } else { + // Simplex: set up both DMA streams for SPI3, only one will be used + i2s_obj->tx_dma_stream = DMA1_Stream5; + //i2s_obj->tx_dma_stream = DMA1_Stream7; + i2s_obj->tx_dma_channel = DMA_CHANNEL_0; + i2s_obj->rx_dma_stream = DMA1_Stream0; + //i2s_obj->rx_dma_stream = DMA1_Stream2; + i2s_obj->rx_dma_channel = DMA_CHANNEL_0; + } #endif } else { - // invalid i2s_id number; shouldn't get here as i2s object should not - // have been created without setting a valid i2s instance number - return false; + // invalid i2s_id number; shouldn't get here as i2s object should not + // have been created without setting a valid i2s instance number + return false; } // New GPIO initialization section, supercedes the separate I2S2/3 sections @@ -225,17 +224,17 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { // If master clock is not enabled, its pin will not get initialized int num_pins = (i2s_obj->i2s.Init.MCLKOutput == - I2S_MCLKOUTPUT_ENABLE) ? NUM_PINS : NUM_PINS - 1; + I2S_MCLKOUTPUT_ENABLE) ? NUM_PINS : NUM_PINS - 1; for (int i = 0; i < num_pins; i++) { - if (i2s_obj->pins[i] != MP_OBJ_NULL) { - GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; - const pin_af_obj_t *af = pin_find_af(i2s_obj->pins[i], AF_FN_I2S, i2s_obj->i2s_id); - assert(af != NULL); - // Alt function is set using af->idx instead of GPIO_AFx_I2Sx macros - GPIO_InitStructure.Alternate = (uint8_t)af->idx; - HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); - } + if (i2s_obj->pins[i] != MP_OBJ_NULL) { + GPIO_InitStructure.Pin = i2s_obj->pins[i]->pin_mask; + const pin_af_obj_t *af = pin_find_af(i2s_obj->pins[i], AF_FN_I2S, i2s_obj->i2s_id); + assert(af != NULL); + // Alt function is set using af->idx instead of GPIO_AFx_I2Sx macros + GPIO_InitStructure.Alternate = (uint8_t)af->idx; + HAL_GPIO_Init(i2s_obj->pins[i]->gpio, &GPIO_InitStructure); + } } // Configure and enable I2SPLL: @@ -243,22 +242,21 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { // the I2SPLL, needs more testing __HAL_RCC_PLLI2S_DISABLE(); if (i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_TX || - i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX ) { - // TODO - calculate values based on available parameters - __HAL_RCC_PLLI2S_CONFIG(384, 5); - __HAL_RCC_PLLI2S_ENABLE(); + i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX ) { + // TODO - calculate values based on available parameters + __HAL_RCC_PLLI2S_CONFIG(384, 5); + __HAL_RCC_PLLI2S_ENABLE(); } - if (HAL_I2S_Init(&i2s_obj->i2s) != HAL_OK) { - // This message is redundant, exception will be raised by return value - printf("OSError: HAL_I2S_Init failed\n"); - return false; + // This message is redundant, exception will be raised by return value + printf("OSError: HAL_I2S_Init failed\n"); + return false; } else { - dma_invalidate_channel(i2s_obj->tx_dma_stream, i2s_obj->tx_dma_channel); - dma_invalidate_channel(i2s_obj->rx_dma_stream, i2s_obj->rx_dma_channel); - i2s_obj->is_enabled = true; - return true; + dma_invalidate_channel(i2s_obj->tx_dma_stream, i2s_obj->tx_dma_channel); + dma_invalidate_channel(i2s_obj->rx_dma_stream, i2s_obj->rx_dma_channel); + i2s_obj->is_enabled = true; + return true; } } @@ -270,10 +268,10 @@ STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in); // unregister all interrupt sources void i2s_deinit(void) { for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_i2s_obj_all)); i++) { - pyb_i2s_obj_t *i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i]; - if (i2s_obj != NULL) { - pyb_i2s_deinit(i2s_obj); - } + pyb_i2s_obj_t *i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i]; + if (i2s_obj != NULL) { + pyb_i2s_deinit(i2s_obj); + } } } @@ -282,33 +280,32 @@ void i2s_deinit(void) { STATIC HAL_StatusTypeDef i2s_wait_dma_finished(I2S_HandleTypeDef *i2s, uint32_t timeout) { uint32_t start = HAL_GetTick(); while (HAL_I2S_GetState(i2s) != HAL_I2S_STATE_READY) { - if (HAL_GetTick() - start >= timeout) { - return HAL_TIMEOUT; - } - __WFI(); + if (HAL_GetTick() - start >= timeout) { + return HAL_TIMEOUT; + } + __WFI(); } return HAL_OK; } STATIC void stream_out_buffering(pyb_i2s_obj_t *self) { - int buf_sz = AUDIOBUFFER_BYTES / 4; + int buf_sz = AUDIOBUFFER_BYTES / 4; int error; - - HAL_StatusTypeDef status; - status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); + HAL_StatusTypeDef status; + status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); if (status != HAL_OK) { - mp_hal_raise(status); + mp_hal_raise(status); } - self->pp_ptr = !(self->pp_ptr); + self->pp_ptr = !(self->pp_ptr); - mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); - if (out_sz == MP_STREAM_ERROR) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); - } + if (out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } } // Debug placeholders for DMA callback methods @@ -318,21 +315,21 @@ void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { } void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { - led_state(1, 1); - int rp_index = 0; - // If both I2S instances are enabled, set root pointer index to 1 if I2S - // instance is SPI3/I2S3; otherwise index is 0 + led_state(1, 1); + int rp_index = 0; + // If both I2S instances are enabled, set root pointer index to 1 if I2S + // instance is SPI3/I2S3; otherwise index is 0 #if MICROPY_HW_ENABLE_I2S2 && MICROPY_HW_ENABLE_I2S3 - if (hi2s->Instance == SPI3) { - rp_index = 1; - } + if (hi2s->Instance == SPI3) { + rp_index = 1; + } #endif - - pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; - stream_out_buffering(self); - - //printf("I2S-TxCplt\n"); - led_state(1, 0); + + pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; + stream_out_buffering(self); + + //printf("I2S-TxCplt\n"); + led_state(1, 0); } void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { @@ -358,42 +355,42 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_printf(print, "I2S(%u, pins=[", self->i2s_id); for (int i = 0; i < 4; i++) { - if (self->pins[i] != MP_OBJ_NULL) { - mp_printf(print, "%q ", self->pins[i]->name); - } else { - mp_print_str(print, "None "); - } + if (self->pins[i] != MP_OBJ_NULL) { + mp_printf(print, "%q ", self->pins[i]->name); + } else { + mp_print_str(print, "None "); + } } mp_print_str(print, "\b], I2S."); if (self->is_enabled) { - if (self->i2s.Init.Mode == I2S_MODE_MASTER_TX || - self->i2s.Init.Mode == I2S_MODE_MASTER_RX) { - mp_printf(print, "%q, %q ", MP_QSTR_MASTER, MP_QSTR_mclkout); - if (self->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { - mp_printf(print, "on %q", self->pins[4]->name); - } else { - mp_print_str(print, "off"); - } - mp_printf(print, ", %q=%u", MP_QSTR_audiofreq, self->i2s.Init.AudioFreq); - } else if (self->i2s.Init.Mode == I2S_MODE_SLAVE_TX || - self->i2s.Init.Mode == I2S_MODE_SLAVE_RX) { - mp_printf(print, "%q", MP_QSTR_SLAVE); - } else { - // Shouldn't get here if self->is_enabled=true - } - - qstr standard = 0; - if (self->i2s.Init.Standard == I2S_STANDARD_PHILIPS) { standard = MP_QSTR_PHILIPS; } - else if (self->i2s.Init.Standard == I2S_STANDARD_MSB) { standard = MP_QSTR_MSB; } - else if (self->i2s.Init.Standard == I2S_STANDARD_LSB) { standard = MP_QSTR_LSB; } - else if (self->i2s.Init.Standard == I2S_STANDARD_PCM_SHORT) { standard = MP_QSTR_PCM_SHORT; } - else if (self->i2s.Init.Standard == I2S_STANDARD_PCM_LONG) { standard = MP_QSTR_PCM_LONG; } - else { /* shouldn't get here */ } - mp_printf(print, ", %q=%u, %q=%q, ", - MP_QSTR_polarity, self->i2s.Init.CPOL, MP_QSTR_standard, standard); - int n_data_bits = ((self->i2s.Init.DataFormat >> 1) + 2) * 8; - mp_printf(print, "%q=%uB%s", MP_QSTR_dataformat, n_data_bits, - self->i2s.Init.DataFormat == I2S_DATAFORMAT_16B_EXTENDED ? "_EXT" : ""); + if (self->i2s.Init.Mode == I2S_MODE_MASTER_TX || + self->i2s.Init.Mode == I2S_MODE_MASTER_RX) { + mp_printf(print, "%q, %q ", MP_QSTR_MASTER, MP_QSTR_mclkout); + if (self->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { + mp_printf(print, "on %q", self->pins[4]->name); + } else { + mp_print_str(print, "off"); + } + mp_printf(print, ", %q=%u", MP_QSTR_audiofreq, self->i2s.Init.AudioFreq); + } else if (self->i2s.Init.Mode == I2S_MODE_SLAVE_TX || + self->i2s.Init.Mode == I2S_MODE_SLAVE_RX) { + mp_printf(print, "%q", MP_QSTR_SLAVE); + } else { + // Shouldn't get here if self->is_enabled=true + } + + qstr standard = 0; + if (self->i2s.Init.Standard == I2S_STANDARD_PHILIPS) { standard = MP_QSTR_PHILIPS; } + else if (self->i2s.Init.Standard == I2S_STANDARD_MSB) { standard = MP_QSTR_MSB; } + else if (self->i2s.Init.Standard == I2S_STANDARD_LSB) { standard = MP_QSTR_LSB; } + else if (self->i2s.Init.Standard == I2S_STANDARD_PCM_SHORT) { standard = MP_QSTR_PCM_SHORT; } + else if (self->i2s.Init.Standard == I2S_STANDARD_PCM_LONG) { standard = MP_QSTR_PCM_LONG; } + else { /* shouldn't get here */ } + mp_printf(print, ", %q=%u, %q=%q, ", + MP_QSTR_polarity, self->i2s.Init.CPOL, MP_QSTR_standard, standard); + int n_data_bits = ((self->i2s.Init.DataFormat >> 1) + 2) * 8; + mp_printf(print, "%q=%uB%s", MP_QSTR_dataformat, n_data_bits, + self->i2s.Init.DataFormat == I2S_DATAFORMAT_16B_EXTENDED ? "_EXT" : ""); } mp_print_str(print, ")"); } @@ -407,28 +404,28 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki /// - `mode` must be either `I2S.MASTER` or `I2S.SLAVE`. /// - `standard` can be `PHILIPS`, `MSB`, `LSB`, `PCM_SHORT`, or `PCM_LONG`. /// - `dataformat` can be 0 (16B_EXTENDED), 16, 24, or 32 (bits). -/// - `polarity` can be 0 for clock steady state low, or 1 for steady state high. +/// - `polarity` can be 0 for clock steady state low, or 1 for steady state high. /// - Options only relevant to master mode: /// - `audiofreq` can be any common audio sampling frequency, default is 48000. /// - `clksrc` can be `PLL` or `EXTERNAL`. /// - `mclkout` can be 0 to disable or 1 to enable. STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, - const mp_obj_t *pos_args, mp_map_t *kw_args) { + const mp_obj_t *pos_args, mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */ } }, - { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 /* 16B EXTENDED */ } }, - { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_STANDARD_PHILIPS} }, - { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, - { MP_QSTR_clksrc, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CLOCK_PLL} }, - { MP_QSTR_mclkout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - // Include option for setting I2SPLL parameters directly? + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0 /* MASTER */ } }, + { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 /* 16B EXTENDED */ } }, + { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_STANDARD_PHILIPS} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, + { MP_QSTR_clksrc, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CLOCK_PLL} }, + { MP_QSTR_mclkout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + // Include option for setting I2SPLL parameters directly? }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, - MP_ARRAY_SIZE(allowed_args), allowed_args, args); + MP_ARRAY_SIZE(allowed_args), allowed_args, args); // set the I2S configuration values memset(&self->i2s, 0, sizeof(self->i2s)); @@ -436,9 +433,9 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, // init->Mode can be MASTER_TX, MASTER_RX, SLAVE_TX, or SLAVE_RX; if (args[0].u_int == 0) { - init->Mode = self->base_is_tx ? I2S_MODE_MASTER_TX : I2S_MODE_MASTER_RX; + init->Mode = self->base_is_tx ? I2S_MODE_MASTER_TX : I2S_MODE_MASTER_RX; } else { - init->Mode = self->base_is_tx ? I2S_MODE_SLAVE_TX : I2S_MODE_SLAVE_RX; + init->Mode = self->base_is_tx ? I2S_MODE_SLAVE_TX : I2S_MODE_SLAVE_RX; } if (args[1].u_int == 0) { init->DataFormat = I2S_DATAFORMAT_16B_EXTENDED; } @@ -446,14 +443,14 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, else if (args[1].u_int == 24) { init->DataFormat = I2S_DATAFORMAT_24B; } else if (args[1].u_int == 32) { init->DataFormat = I2S_DATAFORMAT_32B; } else { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "%d not a valid data format", args[2].u_int)); } + "%d not a valid data format", args[2].u_int)); } init->Standard = args[2].u_int; init->CPOL = args[3].u_int ? I2S_CPOL_HIGH : I2S_CPOL_LOW; init->AudioFreq = args[4].u_int; init->MCLKOutput = args[6].u_int ? I2S_MCLKOUTPUT_ENABLE : I2S_MCLKOUTPUT_DISABLE; init->FullDuplexMode = self->is_duplex ? - I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; + I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; // -------------Possible bug in HAL------------------- // According to the datasheet (RM0090, Sec 28.4.6 - I2S Slave Mode) in Slave @@ -466,16 +463,16 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, // is intended to set pin PC9 as I2S_CKIN, an external source for I2SPLL. // So I don't think that this work around is really correct here: if (args[0].u_int) { - // Slave mode - init->ClockSource = I2S_CLOCK_EXTERNAL; + // Slave mode + init->ClockSource = I2S_CLOCK_EXTERNAL; } else { - init->ClockSource = args[5].u_int; + init->ClockSource = args[5].u_int; } // init the I2S bus if (!i2s_init(self)) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S port %d init failed", self->i2s_id)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S port %d init failed", self->i2s_id)); } return mp_const_none; @@ -525,7 +522,7 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, /// See `init` for parameters of initialisation. STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, - mp_uint_t n_kw, const mp_obj_t *args) { + mp_uint_t n_kw, const mp_obj_t *args) { // If n_args == 0 and I2S2 is enabled, set default pin list to the SPI2 // pins on the pyboard (Y6, Y5, Y7, Y8): @@ -540,17 +537,17 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_obj_t *pin_names; if (n_args == 0) { #ifdef MICROPY_HW_ENABLE_I2S2 - static const pin_obj_t *default_pins[4] = {&pin_B13, &pin_B12, &pin_B15, &pin_B14}; - pin_names = (mp_obj_t)default_pins; - array_len = 4; + static const pin_obj_t *default_pins[4] = {&pin_B13, &pin_B12, &pin_B15, &pin_B14}; + pin_names = (mp_obj_t)default_pins; + array_len = 4; #endif } else { - mp_obj_get_array(args[0], &array_len, &pin_names); + mp_obj_get_array(args[0], &array_len, &pin_names); } if (array_len != 4) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Pin list requires 4 items, %d given", - array_len)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Pin list requires 4 items, %d given", + array_len)); } // Get array of pin objects: False / empty values are valid for pins[2:3], @@ -559,18 +556,18 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, // C6 for I2S2 and C7 for I2S3: const pin_obj_t *pins[5]; for (int i = 0; i < 4; i++) { - if (mp_obj_is_true(pin_names[i])) { - pins[i] = pin_find(pin_names[i]); - } else { - pins[i] = MP_OBJ_NULL; - } - - // DEBUG - print list of pin objects. This information can also - // be accessed from within uPy with the command pyb.Pin.debug(True) - if (0) { - mp_obj_print((mp_obj_t)pins[i], PRINT_STR); - printf("\n"); - } + if (mp_obj_is_true(pin_names[i])) { + pins[i] = pin_find(pin_names[i]); + } else { + pins[i] = MP_OBJ_NULL; + } + + // DEBUG - print list of pin objects. This information can also + // be accessed from within uPy with the command pyb.Pin.debug(True) + if (0) { + mp_obj_print((mp_obj_t)pins[i], PRINT_STR); + printf("\n"); + } } // Check if pins represents a valid I2S port configuration: @@ -594,69 +591,69 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, #if MICROPY_HW_ENABLE_I2S2 pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 2); if (pin_af[BCK]->type == AF_PIN_TYPE_I2S_CK) { - i2s_id = 2; - pins[MCK] = &pin_C6; + i2s_id = 2; + pins[MCK] = &pin_C6; } #endif #if MICROPY_HW_ENABLE_I2S3 pin_af[BCK] = pin_find_af(pins[BCK], AF_FN_I2S, 3); if (pin_af[BCK]->type == AF_PIN_TYPE_I2S_CK) { - i2s_id = 3; - pins[MCK] = &pin_C7; + i2s_id = 3; + pins[MCK] = &pin_C7; } #endif if (i2s_id == 0) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "%q not an I2S BCK pin", pins[BCK]->name)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "%q not an I2S BCK pin", pins[BCK]->name)); } else { - for (int i = 0; i < NUM_PINS /* = 5 */; i++) { - pin_af[i] = pin_find_af(pins[i], AF_FN_I2S, i2s_id); - } + for (int i = 0; i < NUM_PINS /* = 5 */; i++) { + pin_af[i] = pin_find_af(pins[i], AF_FN_I2S, i2s_id); + } } #if (0) for (int i = 0; i < NUM_PINS; i++) { - if (pins[i]) { - printf("pin = %s, af = %s\n", qstr_str(pins[i]->name), qstr_str(pin_af[i]->name)); - } else { - printf("pin = %s, af = %s\n", "no pin", "no af"); - } + if (pins[i]) { + printf("pin = %s, af = %s\n", qstr_str(pins[i]->name), qstr_str(pin_af[i]->name)); + } else { + printf("pin = %s, af = %s\n", "no pin", "no af"); + } } #endif if (pin_af[WS]->type == AF_PIN_TYPE_I2S_WS) { - // Clock and Word Select pins are valid - if (pin_af[TX]->type == AF_PIN_TYPE_I2S_SD) { - base_is_tx = true; - if (pin_af[RX]->type == AF_PIN_TYPE_I2S_EXTSD) { - is_duplex = true; - } else if (pins[RX] == MP_OBJ_NULL) { - is_duplex = false; - } else { - pin_err = pins[RX]->name; - } - } else if (pin_af[RX]->type == AF_PIN_TYPE_I2S_SD) { - base_is_tx = false; - if (pin_af[TX]->type == AF_PIN_TYPE_I2S_EXTSD) { - is_duplex = true; - } else if (pins[TX] == MP_OBJ_NULL) { - is_duplex = false; - } else { - pin_err = pins[TX]->name; - } - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "no valid SD pin for I2S%d", i2s_id)); - } + // Clock and Word Select pins are valid + if (pin_af[TX]->type == AF_PIN_TYPE_I2S_SD) { + base_is_tx = true; + if (pin_af[RX]->type == AF_PIN_TYPE_I2S_EXTSD) { + is_duplex = true; + } else if (pins[RX] == MP_OBJ_NULL) { + is_duplex = false; + } else { + pin_err = pins[RX]->name; + } + } else if (pin_af[RX]->type == AF_PIN_TYPE_I2S_SD) { + base_is_tx = false; + if (pin_af[TX]->type == AF_PIN_TYPE_I2S_EXTSD) { + is_duplex = true; + } else if (pins[TX] == MP_OBJ_NULL) { + is_duplex = false; + } else { + pin_err = pins[TX]->name; + } + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "no valid SD pin for I2S%d", i2s_id)); + } } else { - pin_err = pins[WS]->name; + pin_err = pins[WS]->name; } if (pin_err) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "%q not valid for I2S%d", - pin_err, i2s_id)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "%q not valid for I2S%d", + pin_err, i2s_id)); } @@ -668,27 +665,27 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, // get I2S object and set initialization flags pyb_i2s_obj_t *i2s_obj; if (MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET] == NULL) { - // create new I2S object - i2s_obj = m_new_obj(pyb_i2s_obj_t); - i2s_obj->base.type = &pyb_i2s_type; - i2s_obj->i2s_id = i2s_id; - i2s_obj->is_enabled = false; - MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET] = i2s_obj; + // create new I2S object + i2s_obj = m_new_obj(pyb_i2s_obj_t); + i2s_obj->base.type = &pyb_i2s_type; + i2s_obj->i2s_id = i2s_id; + i2s_obj->is_enabled = false; + MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET] = i2s_obj; } else { - i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET]; + i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET]; } i2s_obj->is_duplex = is_duplex; i2s_obj->base_is_tx = base_is_tx; for (int i = 0; i < NUM_PINS; i++) { - i2s_obj->pins[i] = pins[i]; + i2s_obj->pins[i] = pins[i]; } if (n_args > 1 || n_kw > 0) { - // start the peripheral - mp_map_t kw_args; - mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - pyb_i2s_init_helper(i2s_obj, n_args - 1, args + 1, &kw_args); + // start the peripheral + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pyb_i2s_init_helper(i2s_obj, n_args - 1, args + 1, &kw_args); } return (mp_obj_t)i2s_obj; @@ -697,8 +694,7 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, ////////////////////////////////////// -STATIC mp_obj_t pyb_i2s_init(mp_uint_t n_args, const mp_obj_t *args, - mp_map_t *kw_args) { +STATIC mp_obj_t pyb_i2s_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { return pyb_i2s_init_helper(args[0], n_args - 1, args + 1, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_init_obj, 1, pyb_i2s_init); @@ -715,15 +711,15 @@ STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in) { if (0) { #if MICROPY_HW_ENABLE_I2S2 } else if (self->i2s.Instance == SPI2) { - __SPI2_FORCE_RESET(); - __SPI2_RELEASE_RESET(); - __SPI2_CLK_DISABLE(); + __SPI2_FORCE_RESET(); + __SPI2_RELEASE_RESET(); + __SPI2_CLK_DISABLE(); #endif #if MICROPY_HW_ENABLE_I2S3 } else if (self->i2s.Instance == SPI3) { - __SPI3_FORCE_RESET(); - __SPI3_RELEASE_RESET(); - __SPI3_CLK_DISABLE(); + __SPI3_FORCE_RESET(); + __SPI3_RELEASE_RESET(); + __SPI3_CLK_DISABLE(); #endif } return mp_const_none; @@ -740,18 +736,18 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_deinit_obj, pyb_i2s_deinit); /// /// Return value: `None`. STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, - mp_map_t *kw_args) { + mp_map_t *kw_args) { // skeleton copied from spi.c static const mp_arg_t allowed_args[] = { - { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; // parse args pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, - MP_ARRAY_SIZE(allowed_args), allowed_args, args); + MP_ARRAY_SIZE(allowed_args), allowed_args, args); // 'send' can be used in either simplex or duplex mode, but only if the // base i2s instance is in TX mode - in duplex mode, the extended instance @@ -760,8 +756,9 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle send on I2Sx_EXT // interfaces when available instead of raising error? if (!self->base_is_tx) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) base instance not in transmit mode", self->i2s_id)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) base instance not in transmit mode", + self->i2s_id)); } // get the buffer to send from @@ -773,23 +770,23 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, // send the data HAL_StatusTypeDef status; if (query_irq() == IRQ_STATE_DISABLED) { - status = HAL_I2S_Transmit(&self->i2s, bufinfo.buf, bufinfo.len / 2, args[1].u_int); + status = HAL_I2S_Transmit(&self->i2s, bufinfo.buf, bufinfo.len / 2, args[1].u_int); } else { - DMA_HandleTypeDef tx_dma; - dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &tx_dma; - self->i2s.hdmarx = NULL; - status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); - if (status == HAL_OK) { - //led_toggle(1); - status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); - } - dma_deinit(&tx_dma); + DMA_HandleTypeDef tx_dma; + dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &tx_dma; + self->i2s.hdmarx = NULL; + status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); + if (status == HAL_OK) { + //led_toggle(1); + status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); + } + dma_deinit(&tx_dma); } if (status != HAL_OK) { - mp_hal_raise(status); + mp_hal_raise(status); } return mp_const_none; @@ -809,19 +806,19 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_obj, 1, pyb_i2s_send); /// Return value: if `recv` is an integer then a new buffer of the bytes received, /// otherwise the same buffer that was passed in to `recv`. STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, - mp_map_t *kw_args) { + mp_map_t *kw_args) { // TODO check transmission size for I2S static const mp_arg_t allowed_args[] = { - { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; // parse args pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, - MP_ARRAY_SIZE(allowed_args), allowed_args, args); + MP_ARRAY_SIZE(allowed_args), allowed_args, args); // 'recv' can be used in either simplex or duplex mode, but only if the // base i2s instance is in RX mode - in duplex mode, the extended instance @@ -830,8 +827,9 @@ STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT // interfaces when available instead of raising error? if (self->base_is_tx) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) base instance not in receive mode", self->i2s_id)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) base instance not in receive mode", + self->i2s_id)); } // get the buffer to receive into @@ -843,30 +841,30 @@ STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, // receive the data HAL_StatusTypeDef status; if (query_irq() == IRQ_STATE_DISABLED) { - status = HAL_I2S_Receive(&self->i2s, (uint16_t*)vstr.buf, - vstr.len / 2, args[1].u_int); + status = HAL_I2S_Receive(&self->i2s, (uint16_t*)vstr.buf, + vstr.len / 2, args[1].u_int); } else { - DMA_HandleTypeDef rx_dma; - dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, - self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmarx = &rx_dma; - self->i2s.hdmatx = NULL; - status = HAL_I2S_Receive_DMA(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2); - if (status == HAL_OK) { - status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); - } - dma_deinit(&rx_dma); + DMA_HandleTypeDef rx_dma; + dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); + self->i2s.hdmarx = &rx_dma; + self->i2s.hdmatx = NULL; + status = HAL_I2S_Receive_DMA(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2); + if (status == HAL_OK) { + status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); + } + dma_deinit(&rx_dma); } if (status != HAL_OK) { - mp_hal_raise(status); + mp_hal_raise(status); } // return the received data if (o_ret != MP_OBJ_NULL) { - return o_ret; + return o_ret; } else { - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_recv_obj, 1, pyb_i2s_recv); @@ -885,26 +883,26 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_recv_obj, 1, pyb_i2s_recv); /// /// Return value: the buffer with the received bytes. STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, - mp_map_t *kw_args) { + mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; // parse args pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, - MP_ARRAY_SIZE(allowed_args), allowed_args, args); + MP_ARRAY_SIZE(allowed_args), allowed_args, args); // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT // interfaces when available instead of raising error? //if (!self->is_duplex) { if (self->i2s.Init.FullDuplexMode != I2S_FULLDUPLEXMODE_ENABLE) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) not in duplex mode", self->i2s_id)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not in duplex mode", self->i2s_id)); } // get buffers to send from/receive to @@ -915,64 +913,64 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, mp_obj_t o_ret; if (args[0].u_obj == args[1].u_obj) { - // same object for send and receive, it must be a r/w buffer - mp_get_buffer_raise(args[0].u_obj, &bufinfo_send, MP_BUFFER_RW); - bufinfo_recv = bufinfo_send; - o_ret = args[0].u_obj; + // same object for send and receive, it must be a r/w buffer + mp_get_buffer_raise(args[0].u_obj, &bufinfo_send, MP_BUFFER_RW); + bufinfo_recv = bufinfo_send; + o_ret = args[0].u_obj; } else { - // get the buffer to send from - pyb_buf_get_for_send(args[0].u_obj, &bufinfo_send, data_send); - - // get the buffer to receive into - if (args[1].u_obj == MP_OBJ_NULL) { - // only send argument given, so create a fresh buffer of the send length - vstr_init_len(&vstr_recv, bufinfo_send.len); - bufinfo_recv.len = vstr_recv.len; - bufinfo_recv.buf = vstr_recv.buf; - o_ret = MP_OBJ_NULL; - } else { - // recv argument given - mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE); - if (bufinfo_recv.len != bufinfo_send.len) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "recv must be same length as send")); - } - o_ret = args[1].u_obj; - } + // get the buffer to send from + pyb_buf_get_for_send(args[0].u_obj, &bufinfo_send, data_send); + + // get the buffer to receive into + if (args[1].u_obj == MP_OBJ_NULL) { + // only send argument given, so create a fresh buffer of the send length + vstr_init_len(&vstr_recv, bufinfo_send.len); + bufinfo_recv.len = vstr_recv.len; + bufinfo_recv.buf = vstr_recv.buf; + o_ret = MP_OBJ_NULL; + } else { + // recv argument given + mp_get_buffer_raise(args[1].u_obj, &bufinfo_recv, MP_BUFFER_WRITE); + if (bufinfo_recv.len != bufinfo_send.len) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "recv must be same length as send")); + } + o_ret = args[1].u_obj; + } } // TODO - implement 24-bit and 32-bit data width cases for all methods // send and receive the data HAL_StatusTypeDef status; if (query_irq() == IRQ_STATE_DISABLED) { - status = HAL_I2SEx_TransmitReceive(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, - bufinfo_send.len / 2, args[2].u_int); + status = HAL_I2SEx_TransmitReceive(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, + bufinfo_send.len / 2, args[2].u_int); } else { - DMA_HandleTypeDef tx_dma, rx_dma; - dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &tx_dma; - dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, - self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmarx = &rx_dma; - status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, bufinfo_send.buf, - bufinfo_recv.buf, bufinfo_send.len / 2); - if (status == HAL_OK) { - status = i2s_wait_dma_finished(&self->i2s, args[2].u_int); - } - dma_deinit(&tx_dma); - dma_deinit(&rx_dma); + DMA_HandleTypeDef tx_dma, rx_dma; + dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &tx_dma; + dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); + self->i2s.hdmarx = &rx_dma; + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, bufinfo_send.buf, + bufinfo_recv.buf, bufinfo_send.len / 2); + if (status == HAL_OK) { + status = i2s_wait_dma_finished(&self->i2s, args[2].u_int); + } + dma_deinit(&tx_dma); + dma_deinit(&rx_dma); } if (status != HAL_OK) { - mp_hal_raise(status); + mp_hal_raise(status); } // return the received data if (o_ret != MP_OBJ_NULL) { - return o_ret; + return o_ret; } else { - return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr_recv); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr_recv); } } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); @@ -993,42 +991,42 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, - mp_map_t *kw_args) { + mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_stream_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_stream_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; // parse args pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, - MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // Re-work this to use the mp_obj_get_type idiom (more clarity): - struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; - //mp_obj_t stream = args[0].u_obj; - //mp_obj_type_t *type = mp_obj_get_type(stream); - // Check that 'stream' provides an mp_stream_p_t and a read - // Note that 'read' will be present even if the stream opened in write-mode - //if (type->stream_p == NULL || type->stream_p->read == NULL) { - if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { + MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Re-work this to use the mp_obj_get_type idiom (more clarity): + struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; + //mp_obj_t stream = args[0].u_obj; + //mp_obj_type_t *type = mp_obj_get_type(stream); + // Check that 'stream' provides an mp_stream_p_t and a read + // Note that 'read' will be present even if the stream opened in write-mode + //if (type->stream_p == NULL || type->stream_p->read == NULL) { + if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Object type %s not a readable stream", - mp_obj_get_type_str(stream))); + "Object type %s not a readable stream", + mp_obj_get_type_str(stream))); } - self->dstream_tx = stream; + self->dstream_tx = stream; - int buf_sz = AUDIOBUFFER_BYTES / 4; + int buf_sz = AUDIOBUFFER_BYTES / 4; int error; - self->pp_ptr = 0; + self->pp_ptr = 0; - //mp_uint_t out_sz = stream->type->stream_p->read(stream, tx_buf, sz, &error); - mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + //mp_uint_t out_sz = stream->type->stream_p->read(stream, tx_buf, sz, &error); + mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); if (out_sz == MP_STREAM_ERROR) { if (is_nonblocking_error(error)) { @@ -1042,60 +1040,60 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } - DMA_HandleTypeDef tx_dma; - dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &tx_dma; - self->i2s.hdmarx = NULL; - - // A hack to make sure that slave-mode transfers don't start on the wrong clock: - if (!(self->is_master)) { - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} - } - - stream_out_buffering(self); - ///////////////////// - /* HAL_StatusTypeDef status; */ - /* status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); */ - /* if (status != HAL_OK) { */ - /* mp_hal_raise(status); */ - /* } */ - - /* self->pp_ptr = !(self->pp_ptr); */ - /* out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); */ - /* if (out_sz == MP_STREAM_ERROR) { */ - /* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ - /* } */ - ////////////////// + DMA_HandleTypeDef tx_dma; + dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &tx_dma; + self->i2s.hdmarx = NULL; + + // A hack to make sure that slave-mode transfers don't start on the wrong clock: + if (!(self->is_master)) { + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} + } + + stream_out_buffering(self); + ///////////////////// + /* HAL_StatusTypeDef status; */ + /* status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); */ + /* if (status != HAL_OK) { */ + /* mp_hal_raise(status); */ + /* } */ + + /* self->pp_ptr = !(self->pp_ptr); */ + /* out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); */ + /* if (out_sz == MP_STREAM_ERROR) { */ + /* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ + /* } */ + ////////////////// return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, - mp_map_t *kw_args) { + mp_map_t *kw_args) { static const mp_arg_t allowed_args[] = { - { MP_QSTR_stream_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_stream_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; // parse args // pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, - MP_ARRAY_SIZE(allowed_args), allowed_args, args); + MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t stream = args[0].u_obj; - // Got the file; the following check doesn't do anything. - // need to check if the handle has a 'write' method + mp_obj_t stream = args[0].u_obj; + // Got the file; the following check doesn't do anything. + // need to check if the handle has a 'write' method if (stream == NULL /* || stream->type->stream_p->write == NULL */) { // CPython: io.UnsupportedOperation, OSError subclass nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Operation not supported")); } - return stream; + return stream; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); From f0489a0342e6b56060eccba3ee5188c97a07ba48 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 25 Aug 2015 16:33:32 -0400 Subject: [PATCH 18/33] stmhal/i2s.c: I2S stream_out function working, not crashing uPy anymore --- stmhal/i2s.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 68360f8211e7b..d328c95474b27 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -78,6 +78,8 @@ typedef struct _pyb_i2s_obj_t { uint32_t tx_dma_channel; DMA_Stream_TypeDef *rx_dma_stream; uint32_t rx_dma_channel; + DMA_HandleTypeDef tx_dma; + DMA_HandleTypeDef rx_dma; mp_obj_base_t *dstream_tx; mp_obj_base_t *dstream_rx; const pin_obj_t *pins[5]; @@ -327,7 +329,7 @@ void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; stream_out_buffering(self); - + //printf("I2S-TxCplt\n"); led_state(1, 0); } @@ -1019,13 +1021,15 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, self->dstream_tx = stream; - + //INFO: + printf("pyb I2S object size = %d bytes\n", sizeof(pyb_i2s_obj_t)); + printf("DMA Handle struct size = %d bytes\n", sizeof(DMA_HandleTypeDef)); + printf("I2S Handle struct size = %d bytes\n", sizeof(I2S_HandleTypeDef)); int buf_sz = AUDIOBUFFER_BYTES / 4; int error; self->pp_ptr = 0; - //mp_uint_t out_sz = stream->type->stream_p->read(stream, tx_buf, sz, &error); mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); if (out_sz == MP_STREAM_ERROR) { @@ -1040,10 +1044,10 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } - DMA_HandleTypeDef tx_dma; - dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + //DMA_HandleTypeDef tx_dma; + dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &tx_dma; + self->i2s.hdmatx = &self->tx_dma; self->i2s.hdmarx = NULL; // A hack to make sure that slave-mode transfers don't start on the wrong clock: @@ -1053,19 +1057,7 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, } stream_out_buffering(self); - ///////////////////// - /* HAL_StatusTypeDef status; */ - /* status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); */ - /* if (status != HAL_OK) { */ - /* mp_hal_raise(status); */ - /* } */ - - /* self->pp_ptr = !(self->pp_ptr); */ - /* out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); */ - /* if (out_sz == MP_STREAM_ERROR) { */ - /* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ - /* } */ - ////////////////// + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); @@ -1097,7 +1089,6 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); - STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_i2s_init_obj }, From 9d1b8d1ac1ec4607a8f9fab0de0a68228b6f37ab Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 25 Aug 2015 23:16:15 -0400 Subject: [PATCH 19/33] stmhal/i2s.c: Add DMA pause, resume, stop methods. Stop may not yet work correctly --- stmhal/i2s.c | 36 ++++++++++++++++++++++++++++++++++++ stmhal/qstrdefsport.h | 3 +++ 2 files changed, 39 insertions(+) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index d328c95474b27..ecf377bf9036f 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -1089,6 +1089,39 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); +STATIC mp_obj_t pyb_i2s_pause(mp_obj_t self_in){ + pyb_i2s_obj_t *self = self_in; + HAL_StatusTypeDef status; + status = HAL_I2S_DMAPause(&self->i2s); + if (status != HAL_OK) { + mp_hal_raise(status); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_pause_obj, pyb_i2s_pause); + +STATIC mp_obj_t pyb_i2s_resume(mp_obj_t self_in){ + pyb_i2s_obj_t *self = self_in; + HAL_StatusTypeDef status; + status = HAL_I2S_DMAResume(&self->i2s); + if (status != HAL_OK) { + mp_hal_raise(status); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_resume_obj, pyb_i2s_resume); + +STATIC mp_obj_t pyb_i2s_stop(mp_obj_t self_in){ + pyb_i2s_obj_t *self = self_in; + HAL_StatusTypeDef status; + status = HAL_I2S_DMAStop(&self->i2s); + if (status != HAL_OK) { + mp_hal_raise(status); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_stop_obj, pyb_i2s_stop); + STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_i2s_init_obj }, @@ -1098,6 +1131,9 @@ STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_i2s_send_recv_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_stream_out), (mp_obj_t)&pyb_i2s_stream_out_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_stream_in), (mp_obj_t)&pyb_i2s_stream_in_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pause), (mp_obj_t)&pyb_i2s_pause_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_resume), (mp_obj_t)&pyb_i2s_resume_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_stop), (mp_obj_t)&pyb_i2s_stop_obj }, // class constants /// \constant MASTER - for initialising the bus to master mode diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index d0511b2684d75..84aa65a447352 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -372,6 +372,9 @@ Q(PLL) Q(EXTERNAL) Q(stream_out) Q(stream_in) +Q(pause) +Q(resume) +Q(stop) #endif // for Accel object From d3c4b870686cf727fab19db8bb6851eacba1f39f Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 27 Aug 2015 14:56:25 -0400 Subject: [PATCH 20/33] stmhal/i2s.c: Small cleanup, add comment re: HAL_I2S_DMAStop not working --- stmhal/i2s.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index ecf377bf9036f..b3a01b5effd9b 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -1006,14 +1006,13 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // Re-work this to use the mp_obj_get_type idiom (more clarity): - struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; - //mp_obj_t stream = args[0].u_obj; - //mp_obj_type_t *type = mp_obj_get_type(stream); + //struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; + mp_obj_t stream = args[0].u_obj; + mp_obj_type_t *type = mp_obj_get_type(stream); // Check that 'stream' provides an mp_stream_p_t and a read // Note that 'read' will be present even if the stream opened in write-mode - //if (type->stream_p == NULL || type->stream_p->read == NULL) { - if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { + if (type->stream_p == NULL || type->stream_p->read == NULL) { + //if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Object type %s not a readable stream", mp_obj_get_type_str(stream))); @@ -1021,11 +1020,6 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, self->dstream_tx = stream; - //INFO: - printf("pyb I2S object size = %d bytes\n", sizeof(pyb_i2s_obj_t)); - printf("DMA Handle struct size = %d bytes\n", sizeof(DMA_HandleTypeDef)); - printf("I2S Handle struct size = %d bytes\n", sizeof(I2S_HandleTypeDef)); - int buf_sz = AUDIOBUFFER_BYTES / 4; int error; self->pp_ptr = 0; @@ -1052,6 +1046,7 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, // A hack to make sure that slave-mode transfers don't start on the wrong clock: if (!(self->is_master)) { + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} } @@ -1112,6 +1107,11 @@ STATIC mp_obj_t pyb_i2s_resume(mp_obj_t self_in){ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_resume_obj, pyb_i2s_resume); STATIC mp_obj_t pyb_i2s_stop(mp_obj_t self_in){ + // Currently this method always throws an error when called from REPL; + // can't even print 'status': + // Traceback (most recent call last): + // File "", line 1, in + // OSError: 16 pyb_i2s_obj_t *self = self_in; HAL_StatusTypeDef status; status = HAL_I2S_DMAStop(&self->i2s); From c53e15f976402b1e6d07ba7a5995f6fcb4ccf077 Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 27 Aug 2015 15:08:57 -0400 Subject: [PATCH 21/33] stmhal/i2s.c: Update buffer-oriented methods to move DMA handles from the stack to the i2s instance object --- stmhal/i2s.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index b3a01b5effd9b..6eb7dacfc4f6f 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -774,17 +774,17 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, if (query_irq() == IRQ_STATE_DISABLED) { status = HAL_I2S_Transmit(&self->i2s, bufinfo.buf, bufinfo.len / 2, args[1].u_int); } else { - DMA_HandleTypeDef tx_dma; - dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + // DMA_HandleTypeDef tx_dma; + dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &tx_dma; + self->i2s.hdmatx = &self->tx_dma; self->i2s.hdmarx = NULL; status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); if (status == HAL_OK) { //led_toggle(1); status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } - dma_deinit(&tx_dma); + dma_deinit(&self->tx_dma); } if (status != HAL_OK) { @@ -846,16 +846,16 @@ STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, status = HAL_I2S_Receive(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2, args[1].u_int); } else { - DMA_HandleTypeDef rx_dma; - dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + //DMA_HandleTypeDef rx_dma; + dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmarx = &rx_dma; + self->i2s.hdmarx = &self->rx_dma; self->i2s.hdmatx = NULL; status = HAL_I2S_Receive_DMA(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2); if (status == HAL_OK) { status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } - dma_deinit(&rx_dma); + dma_deinit(&self->rx_dma); } if (status != HAL_OK) { @@ -948,20 +948,20 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, status = HAL_I2SEx_TransmitReceive(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len / 2, args[2].u_int); } else { - DMA_HandleTypeDef tx_dma, rx_dma; - dma_init(&tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + //DMA_HandleTypeDef tx_dma, rx_dma; + dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &tx_dma; - dma_init(&rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + self->i2s.hdmatx = &self->tx_dma; + dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmarx = &rx_dma; + self->i2s.hdmarx = &self->rx_dma; status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len / 2); if (status == HAL_OK) { status = i2s_wait_dma_finished(&self->i2s, args[2].u_int); } - dma_deinit(&tx_dma); - dma_deinit(&rx_dma); + dma_deinit(&self->tx_dma); + dma_deinit(&self->rx_dma); } if (status != HAL_OK) { From 561e9fbaae5d321de213ccd1f68813cb006dcd42 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 14 Sep 2015 16:03:35 -0400 Subject: [PATCH 22/33] stmhal/i2s.c: Fix broken i2s.stop() function; stop stream_out transfer when tx_stream reaches end of data. Also initial commit for i2s.stream_in(); not working yet. --- stmhal/i2s.c | 153 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 114 insertions(+), 39 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 6eb7dacfc4f6f..0c5a6326cf54f 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -82,6 +82,7 @@ typedef struct _pyb_i2s_obj_t { DMA_HandleTypeDef rx_dma; mp_obj_base_t *dstream_tx; mp_obj_base_t *dstream_rx; + mp_uint_t out_sz; const pin_obj_t *pins[5]; // audio buffers placed before smaller struct members to keep word-aligned uint16_t audiobuf_tx[2][AUDIOBUFFER_BYTES / 8]; @@ -92,6 +93,7 @@ typedef struct _pyb_i2s_obj_t { bool is_duplex : 1; bool base_is_tx : 1; // base instance SPIx is either tx or rx bool pp_ptr : 1; // ping-pong pointer for double buffers + bool stop_flag : 1; } pyb_i2s_obj_t; // pins are Bit Clock, Word Select, TX Data, RX Data, and Master Clock Out @@ -242,6 +244,8 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { // Configure and enable I2SPLL: // TODO: This may not be the correct method to initialize and activate // the I2SPLL, needs more testing + // For an idea how to do this, see lines 457-494 of + // STM32Cube_FW_F4_V1.5.0/Drivers/BSP/STM32F4-Discovery/stm32f4_discovery_audio.c __HAL_RCC_PLLI2S_DISABLE(); if (i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_TX || i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX ) { @@ -258,6 +262,9 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { dma_invalidate_channel(i2s_obj->tx_dma_stream, i2s_obj->tx_dma_channel); dma_invalidate_channel(i2s_obj->rx_dma_stream, i2s_obj->rx_dma_channel); i2s_obj->is_enabled = true; + // TODO - This may be redundant, as stop_flag should be reset whenever + // a transfer is initiated: + i2s_obj->stop_flag = false; return true; } } @@ -290,12 +297,16 @@ STATIC HAL_StatusTypeDef i2s_wait_dma_finished(I2S_HandleTypeDef *i2s, uint32_t return HAL_OK; } - -STATIC void stream_out_buffering(pyb_i2s_obj_t *self) { +STATIC void stream_out_buffer_handler(pyb_i2s_obj_t *self) { int buf_sz = AUDIOBUFFER_BYTES / 4; int error; HAL_StatusTypeDef status; - status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], buf_sz / 2); + if(self->stop_flag){ + status = HAL_I2S_DMAStop(&self->i2s); + self->stop_flag = false; + } else { + status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->out_sz / 2); + } if (status != HAL_OK) { mp_hal_raise(status); @@ -303,17 +314,17 @@ STATIC void stream_out_buffering(pyb_i2s_obj_t *self) { self->pp_ptr = !(self->pp_ptr); - mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); - if (out_sz == MP_STREAM_ERROR) { + // self->out_sz == 0 means dstream_tx is empty, so set stop_flag to end transmit + if (self->out_sz == 0) { + self->stop_flag = true; + } else if (self->out_sz == MP_STREAM_ERROR) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } } -// Debug placeholders for DMA callback methods void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { - //printf("I2S-TxHalfCplt\n"); - //led_toggle(2); } void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { @@ -328,18 +339,50 @@ void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { #endif pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; - stream_out_buffering(self); + stream_out_buffer_handler(self); //printf("I2S-TxCplt\n"); led_state(1, 0); } +STATIC void stream_in_buffer_handler(pyb_i2s_obj_t *self) { + int buf_sz = AUDIOBUFFER_BYTES / 4; + int error; + HAL_StatusTypeDef status; + // We toggle the ping-pong pointer immediately to continue to receive data before + // writing out the buffer that was filled by the last receive call + self->pp_ptr = !(self->pp_ptr); + status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + mp_uint_t out_sz = self->dstream_rx->type->stream_p->write(self->dstream_rx, &self->audiobuf_rx[!(self->pp_ptr)], buf_sz, &error); + + if (out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } +} + void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { - //printf("I2S-RxHalfCplt\n"); } void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { + led_state(2, 1); + int rp_index = 0; + // If both I2S instances are enabled, set root pointer index to 1 if I2S + // instance is SPI3/I2S3; otherwise index is 0 +#if MICROPY_HW_ENABLE_I2S2 && MICROPY_HW_ENABLE_I2S3 + if (hi2s->Instance == SPI3) { + rp_index = 1; + } +#endif + + pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; + stream_in_buffer_handler(self); //printf("I2S-RxCplt\n"); + led_state(2, 0); } void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { @@ -1018,15 +1061,15 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_obj_get_type_str(stream))); } - self->dstream_tx = stream; - int buf_sz = AUDIOBUFFER_BYTES / 4; int error; + self->dstream_tx = stream; self->pp_ptr = 0; + self->stop_flag = false; - mp_uint_t out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); - if (out_sz == MP_STREAM_ERROR) { + if (self->out_sz == MP_STREAM_ERROR) { if (is_nonblocking_error(error)) { // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read // "If the object is in non-blocking mode and no bytes are available, @@ -1038,26 +1081,34 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } - //DMA_HandleTypeDef tx_dma; - dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &self->tx_dma; - self->i2s.hdmarx = NULL; - - // A hack to make sure that slave-mode transfers don't start on the wrong clock: + // Busy-wait for a few word-select cycles to make sure that slave-mode + // transfers don't start on the wrong clock level when in slave mode - + // seems to work when placed before call to dma_init. + // TODO: this could get stuck permanently if there is no master clock + // signal present on the WS pin; need a timeout and error: if (!(self->is_master)) { while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} } - stream_out_buffering(self); + //DMA_HandleTypeDef tx_dma; + dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, + self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); + self->i2s.hdmatx = &self->tx_dma; + self->i2s.hdmarx = NULL; + + stream_out_buffer_handler(self); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); +// stream_in not working yet; committing initial code +// note that stream_in and stream_out are currently monopolizing the +// DMA callbacks; need to detect if stream or buffer methods are being used + STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -1067,20 +1118,53 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, }; // parse args - // pyb_i2s_obj_t *self = pos_args[0]; + pyb_i2s_obj_t *self = pos_args[0]; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_obj_t stream = args[0].u_obj; - // Got the file; the following check doesn't do anything. - // need to check if the handle has a 'write' method - if (stream == NULL /* || stream->type->stream_p->write == NULL */) { - // CPython: io.UnsupportedOperation, OSError subclass - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Operation not supported")); + mp_obj_type_t *type = mp_obj_get_type(stream); + // Check that 'stream' provides 'mp_stream_p_t' and 'write' + // Note that 'write' will be present even if the stream opened in read-mode (?) + if (type->stream_p == NULL || type->stream_p->write == NULL) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Object type %s not a writeable stream", + mp_obj_get_type_str(stream))); } - return stream; + self->dstream_rx = stream; + + // NOTE: stream_in is not the simple switching of rx/tx and read/write methods + // from stream_out: we also need to reverse order of operations; the receive must + // fill the buffer _before_ we write out to a writable stream, and data must be + // written out after the last receive + + dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, + self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); + self->i2s.hdmatx = NULL; + self->i2s.hdmarx = &self->rx_dma; + + // A hack to make sure that slave-mode transfers don't start on the wrong clock: + // NOTE- DOESN'T WORK YET! + if (!(self->is_master)) { + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} + } + + int buf_sz = AUDIOBUFFER_BYTES / 4; + self->pp_ptr = 0; + HAL_StatusTypeDef status; + status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + //stream_in_buffer_handler(self); + + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); @@ -1107,17 +1191,8 @@ STATIC mp_obj_t pyb_i2s_resume(mp_obj_t self_in){ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_resume_obj, pyb_i2s_resume); STATIC mp_obj_t pyb_i2s_stop(mp_obj_t self_in){ - // Currently this method always throws an error when called from REPL; - // can't even print 'status': - // Traceback (most recent call last): - // File "", line 1, in - // OSError: 16 pyb_i2s_obj_t *self = self_in; - HAL_StatusTypeDef status; - status = HAL_I2S_DMAStop(&self->i2s); - if (status != HAL_OK) { - mp_hal_raise(status); - } + self->stop_flag = true; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_stop_obj, pyb_i2s_stop); From 12d7a08dec2fbfb5efea3d99e4b3d912a514e1be Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 17 Sep 2015 11:55:25 -0400 Subject: [PATCH 23/33] stmhal/i2s.c: Get DMA init synchronized with I2S clock; now seems to work! Some comment changes, also debug statement to figure out why receive to file wasn't working - the problem is found, but requires a major refactor to fix. --- stmhal/i2s.c | 549 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 378 insertions(+), 171 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 0c5a6326cf54f..103a4fa9f9902 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -30,6 +30,7 @@ #include "py/nlr.h" #include "py/runtime.h" +#include "py/gc.h" #if MICROPY_HW_ENABLE_I2S2 || MICROPY_HW_ENABLE_I2S3 @@ -71,6 +72,39 @@ #define AUDIOBUFFER_BYTES 8192 +typedef enum { + INACTIVE = 0x00, + BUFFER_WR = 0x01, + BUFFER_RD = 0x02, + BUFFER_RD_WR = 0x03, + BUF_STR_DIV = 0x04, // (xfer_state >= BUF_STR_DIV) indicates streaming + STREAM_WR = 0x04, + STREAM_RD = 0x08, + STREAM_RD_WR = 0x0C, +} xfer_state_t; + +// For buffer transfers, we want to be able to use the buffer methods write, read, and write_readinto +// as the transfer initiation function as well as the callback function +// When a transfer is initiated, the state will be INACTIVE, so the full initialization occurs. +// after that, when the functions are incorporated into callbacks, the mode will be active, and the +// initialization can be skipped. The mode provides information as to the last function called, so if +// the callback is changed on the fly, (to go from a pure write to start recording as well) the +// functions can respond accordingly. + +// Also, each function (read, write, write_readinto, and stream_out and stream_in) will check to +// make sure that any ongoing transfer is of the same type (buffer or stream) and will throw an +// exception without interrupting the ongoing transfer if called. + +// If the stop() function is passed an argument, it can set flags to stop part of a transfer (read or write) +// while allowing the other part to continue uninterrupted + +typedef enum { + I2S_SIG_NONE, + I2S_SIG_STOP_RX, + I2S_SIG_STOP_TX, + I2S_SIG_STOP_ALL, +} xfer_signal_t; + typedef struct _pyb_i2s_obj_t { mp_obj_base_t base; I2S_HandleTypeDef i2s; @@ -83,17 +117,19 @@ typedef struct _pyb_i2s_obj_t { mp_obj_base_t *dstream_tx; mp_obj_base_t *dstream_rx; mp_uint_t out_sz; + mp_obj_t callback; + xfer_state_t xfer_state; + xfer_signal_t xfer_signal; const pin_obj_t *pins[5]; - // audio buffers placed before smaller struct members to keep word-aligned + // audio buffers placed before smaller struct members to keep word-alignment uint16_t audiobuf_tx[2][AUDIOBUFFER_BYTES / 8]; uint16_t audiobuf_rx[2][AUDIOBUFFER_BYTES / 8]; mp_int_t i2s_id : 8; bool is_enabled : 1; - bool is_master : 1; + // bool is_master : 1; bool is_duplex : 1; bool base_is_tx : 1; // base instance SPIx is either tx or rx bool pp_ptr : 1; // ping-pong pointer for double buffers - bool stop_flag : 1; } pyb_i2s_obj_t; // pins are Bit Clock, Word Select, TX Data, RX Data, and Master Clock Out @@ -241,16 +277,29 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { } } - // Configure and enable I2SPLL: - // TODO: This may not be the correct method to initialize and activate - // the I2SPLL, needs more testing - // For an idea how to do this, see lines 457-494 of + // Configure and enable I2SPLL - I2S_MASTER modes only: + // References: see table 126 of RM0090, also lines 457-494 of // STM32Cube_FW_F4_V1.5.0/Drivers/BSP/STM32F4-Discovery/stm32f4_discovery_audio.c + // 48kHz family is accurate for 8, 16, 24, and 48kHz but not 32 or 96 + // 44.1kHz family is accurate for 11.025, 22.05 and 44.1kHz but not 88.2 + // TODO: support more of the commonly-used frequencies and account for 16/32 bit frames + // Also: Refactor to use macros as provided by stm32f4xx_hal_rcc_ex.h __HAL_RCC_PLLI2S_DISABLE(); if (i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_TX || i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX ) { - // TODO - calculate values based on available parameters - __HAL_RCC_PLLI2S_CONFIG(384, 5); + if (i2s_obj->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { + if ((i2s_obj->i2s.Init.AudioFreq & 0x7) == 0) { + __HAL_RCC_PLLI2S_CONFIG(258, 3); // 8, 16, 24, 48kHz with mclkout; not 32 or 96 + } else { + __HAL_RCC_PLLI2S_CONFIG(271, 2); // 11.025, 22.05, 44.1kHz with mclkout; not 88.2 + } + } else { + if ((i2s_obj->i2s.Init.AudioFreq & 0x7) == 0) { + __HAL_RCC_PLLI2S_CONFIG(384, 5); + } else { + __HAL_RCC_PLLI2S_CONFIG(429, 4); + } + } __HAL_RCC_PLLI2S_ENABLE(); } @@ -259,22 +308,29 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { printf("OSError: HAL_I2S_Init failed\n"); return false; } else { + // Reset and initialize Tx and Rx DMA channels + // TODO: Currently both DMA's are initialized regardless of whether I2S + // is instantiated as simplex or duplex - should we check instead? + + // Reset and initialize tx DMA dma_invalidate_channel(i2s_obj->tx_dma_stream, i2s_obj->tx_dma_channel); + dma_init(&i2s_obj->tx_dma, i2s_obj->tx_dma_stream, &dma_init_struct_i2s, + i2s_obj->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &i2s_obj->i2s); + i2s_obj->i2s.hdmatx = &i2s_obj->tx_dma; + + // Reset and initialize rx DMA dma_invalidate_channel(i2s_obj->rx_dma_stream, i2s_obj->rx_dma_channel); + dma_init(&i2s_obj->rx_dma, i2s_obj->rx_dma_stream, &dma_init_struct_i2s, + i2s_obj->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &i2s_obj->i2s); + i2s_obj->i2s.hdmarx = &i2s_obj->rx_dma; + i2s_obj->is_enabled = true; - // TODO - This may be redundant, as stop_flag should be reset whenever - // a transfer is initiated: - i2s_obj->stop_flag = false; + i2s_obj->xfer_state = INACTIVE; return true; } } -// this is needed because i2s_deinit invokes pyb_i2s_deinit - should it be -// other way around? STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in); - -// i2s_deinit is direct crib from stmhal/can.c - can_deinit() -// unregister all interrupt sources void i2s_deinit(void) { for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_i2s_obj_all)); i++) { pyb_i2s_obj_t *i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i]; @@ -297,103 +353,126 @@ STATIC HAL_StatusTypeDef i2s_wait_dma_finished(I2S_HandleTypeDef *i2s, uint32_t return HAL_OK; } -STATIC void stream_out_buffer_handler(pyb_i2s_obj_t *self) { - int buf_sz = AUDIOBUFFER_BYTES / 4; - int error; - HAL_StatusTypeDef status; - if(self->stop_flag){ - status = HAL_I2S_DMAStop(&self->i2s); - self->stop_flag = false; - } else { - status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->out_sz / 2); - } - - if (status != HAL_OK) { - mp_hal_raise(status); - } - - self->pp_ptr = !(self->pp_ptr); - - self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); +STATIC HAL_StatusTypeDef i2s_bus_sync(pyb_i2s_obj_t *self, uint32_t polarity, uint32_t timeout) { + // In I2S Slave mode, force a busy-wait for at least one full word-select + // cycle to sync with WS clock before initiating a transfer. + bool pol = polarity & 1; + uint32_t start = HAL_GetTick(); + // TODO: This test for master is used frequently - make it an inline function? + if (self->i2s.Init.Mode != I2S_MODE_MASTER_TX && + self->i2s.Init.Mode != I2S_MODE_MASTER_RX) { - // self->out_sz == 0 means dstream_tx is empty, so set stop_flag to end transmit - if (self->out_sz == 0) { - self->stop_flag = true; - } else if (self->out_sz == MP_STREAM_ERROR) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == pol) { + if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } + } + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == !pol) { + if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } + } + while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == pol) { + if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } + } } + return HAL_OK; } -void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { -} +STATIC void i2s_stream_handler(pyb_i2s_obj_t *self); +STATIC mp_obj_t pyb_i2s_callback(mp_obj_t self_in, mp_obj_t callback); void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { - led_state(1, 1); - int rp_index = 0; - // If both I2S instances are enabled, set root pointer index to 1 if I2S - // instance is SPI3/I2S3; otherwise index is 0 + led_state(1, 1); //DEBUG + // I2S root pointer index is 1 if both I2S instances are enabled and I2S + // instance is SPI3/I2S3; otherwise index is 0: + pyb_i2s_obj_t *self; + if (0) { #if MICROPY_HW_ENABLE_I2S2 && MICROPY_HW_ENABLE_I2S3 - if (hi2s->Instance == SPI3) { - rp_index = 1; - } + } else if (hi2s->Instance == SPI3) { + self = MP_STATE_PORT(pyb_i2s_obj_all)[1]; #endif - - pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; - stream_out_buffer_handler(self); - - //printf("I2S-TxCplt\n"); - led_state(1, 0); -} - -STATIC void stream_in_buffer_handler(pyb_i2s_obj_t *self) { - int buf_sz = AUDIOBUFFER_BYTES / 4; - int error; - HAL_StatusTypeDef status; - // We toggle the ping-pong pointer immediately to continue to receive data before - // writing out the buffer that was filled by the last receive call - self->pp_ptr = !(self->pp_ptr); - status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); - - if (status != HAL_OK) { - mp_hal_raise(status); + } else { + self = MP_STATE_PORT(pyb_i2s_obj_all)[0]; } - mp_uint_t out_sz = self->dstream_rx->type->stream_p->write(self->dstream_rx, &self->audiobuf_rx[!(self->pp_ptr)], buf_sz, &error); - - if (out_sz == MP_STREAM_ERROR) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + if (self->xfer_state >= BUF_STR_DIV) { + i2s_stream_handler(self); + } else if (self->xfer_state != INACTIVE) { + // buffer transfer, call user-defined callback + if (self->callback != mp_const_none) { + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + // mp_call_function_1 wasn't working -- need to investigate + // mp_call_function_1(self->callback, self) + mp_call_function_0(self->callback); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->callback = mp_const_none; + // DMA_HandleTypeDef dma = self->tx_dma; + // __HAL_DMA_DISABLE(&dma); + printf("uncaught exception in I2S(%u) DMA interrupt handler\n", self->i2s_id); + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } + gc_unlock(); + } } -} -void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) { + //printf("I2S-TxCplt\n"); + led_state(1, 0); //DEBUG } void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { - led_state(2, 1); - int rp_index = 0; - // If both I2S instances are enabled, set root pointer index to 1 if I2S - // instance is SPI3/I2S3; otherwise index is 0 + led_state(2, 1); // DEBUG + // I2S root pointer index is 1 if both I2S instances are enabled and I2S + // instance is SPI3/I2S3; otherwise index is 0: + pyb_i2s_obj_t *self; + if (0) { #if MICROPY_HW_ENABLE_I2S2 && MICROPY_HW_ENABLE_I2S3 - if (hi2s->Instance == SPI3) { - rp_index = 1; - } + } else if (hi2s->Instance == SPI3) { + self = MP_STATE_PORT(pyb_i2s_obj_all)[1]; #endif + } else { + self = MP_STATE_PORT(pyb_i2s_obj_all)[0]; + } - pyb_i2s_obj_t *self = MP_STATE_PORT(pyb_i2s_obj_all)[rp_index]; - stream_in_buffer_handler(self); - //printf("I2S-RxCplt\n"); - led_state(2, 0); + // In duplex mode, both HAL_I2S_TxCpltCallback and HAL_I2S_RxCpltCallback will + // be triggered. This callback is defined to handle Rx-only transfers; it is + // skipped for duplex transfers so callback code is only processed once: + if (!self->is_duplex) { + if (self->xfer_state >= BUF_STR_DIV) { + i2s_stream_handler(self); + } else if (self->xfer_state != INACTIVE) { + if (self->callback != mp_const_none) { + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + // mp_call_function_1 wasn't working -- need to investigate + // mp_call_function_1(self->callback, self) + mp_call_function_0(self->callback); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->callback = mp_const_none; + // DMA_HandleTypeDef dma = self->rx_dma; + // __HAL_DMA_DISABLE(&dma); + printf("uncaught exception in I2S(%u) DMA interrupt handler\n", self->i2s_id); + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } + gc_unlock(); + } + } + } + led_state(2, 0); //DEBUG } -void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { - //printf("I2S-Error\n"); - //led_toggle(4); -} +// Unused I2S callback stubs +void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {} +void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {} +void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) {} /******************************************************************************/ /* Micro Python bindings */ -// spi.c provides *spi_get_handle, probably don't need to provide *i2s_get_handle +// spi.c provides *spi_get_handle, do we need to provide *i2s_get_handle ? STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_i2s_obj_t *self = self_in; @@ -440,9 +519,9 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_print_str(print, ")"); } -/// \method init(mode, standard=I2S.PHILIPS, dataformat=0, -/// polarity=0, audiofreq=48000, -/// clksrc=I2S.PLL, mclkout=0) +/// \method init(mode, dataformat=0, standard=I2S.PHILIPS, +/// polarity=0, audiofreq=48000, clksrc=I2S.PLL, +/// mclkout=0, callback=none) /// /// Initialise the I2S bus with the given parameters: /// @@ -464,7 +543,8 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, { MP_QSTR_audiofreq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_AUDIOFREQ_48K} }, { MP_QSTR_clksrc, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_CLOCK_PLL} }, { MP_QSTR_mclkout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - // Include option for setting I2SPLL parameters directly? + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + // TODO: Include option for setting I2SPLL parameters directly? }; // parse args @@ -497,6 +577,13 @@ STATIC mp_obj_t pyb_i2s_init_helper(pyb_i2s_obj_t *self, mp_uint_t n_args, init->FullDuplexMode = self->is_duplex ? I2S_FULLDUPLEXMODE_ENABLE : I2S_FULLDUPLEXMODE_DISABLE; + // If an I2S object had previously registered callback, the default is to + // keep that callback. Is this correct, or should the default be to set the + // callback to None on init? + if (args[7].u_obj != mp_const_none) { + pyb_i2s_callback(self, args[7].u_obj); + } + // -------------Possible bug in HAL------------------- // According to the datasheet (RM0090, Sec 28.4.6 - I2S Slave Mode) in Slave // mode there is no need to enable I2SPLL to generate a clock for the I2S @@ -720,6 +807,7 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, i2s_obj = MP_STATE_PORT(pyb_i2s_obj_all)[i2s_id - I2S_OBJECT_OFFSET]; } + i2s_obj->callback = mp_const_none; i2s_obj->is_duplex = is_duplex; i2s_obj->base_is_tx = base_is_tx; for (int i = 0; i < NUM_PINS; i++) { @@ -736,22 +824,19 @@ STATIC mp_obj_t pyb_i2s_make_new(mp_obj_t type_in, mp_uint_t n_args, return (mp_obj_t)i2s_obj; } - -////////////////////////////////////// - STATIC mp_obj_t pyb_i2s_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { return pyb_i2s_init_helper(args[0], n_args - 1, args + 1, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_init_obj, 1, pyb_i2s_init); -// TODO: init and deinit follow model from stmhal/can.c; should is follow logic -// closer to spi_init and spi_deinit instead? - /// \method deinit() /// Turn off the I2S bus. STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in) { + pyb_i2s_callback(self_in, mp_const_none); pyb_i2s_obj_t *self = self_in; self->is_enabled = false; + dma_deinit(&self->tx_dma); + dma_deinit(&self->rx_dma); HAL_I2S_DeInit(&self->i2s); if (0) { #if MICROPY_HW_ENABLE_I2S2 @@ -806,28 +891,36 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, self->i2s_id)); } + if (self->xfer_state > BUF_STR_DIV) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "Buffer op not allowed while streaming")); + } + self->xfer_state |= BUFFER_WR; // get the buffer to send from mp_buffer_info_t bufinfo; uint8_t data[1]; pyb_buf_get_for_send(args[0].u_obj, &bufinfo, data); - // TODO - implement 24-bit and 32-bit data width cases for all methods // send the data HAL_StatusTypeDef status; + status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } + // TODO - implement 24-bit and 32-bit data width cases for all methods if (query_irq() == IRQ_STATE_DISABLED) { status = HAL_I2S_Transmit(&self->i2s, bufinfo.buf, bufinfo.len / 2, args[1].u_int); } else { - // DMA_HandleTypeDef tx_dma; - dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &self->tx_dma; - self->i2s.hdmarx = NULL; + /* dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, */ + /* self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); */ + /* self->i2s.hdmatx = &self->tx_dma; */ + /* self->i2s.hdmarx = NULL; */ status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); if (status == HAL_OK) { //led_toggle(1); status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } - dma_deinit(&self->tx_dma); + /* dma_deinit(&self->tx_dma); */ } if (status != HAL_OK) { @@ -877,28 +970,35 @@ STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, self->i2s_id)); } + if (self->xfer_state > BUF_STR_DIV) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "Buffer op not allowed while streaming")); + } + self->xfer_state |= BUFFER_RD; // get the buffer to receive into vstr_t vstr; mp_obj_t o_ret = pyb_buf_get_for_recv(args[0].u_obj, &vstr); - // Only 16-bit data transfers are implemented - // TODO - implement 24-bit and 32-bit data transfers for all methods // receive the data HAL_StatusTypeDef status; + status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } + // TODO - implement 24-bit and 32-bit data transfers for all methods if (query_irq() == IRQ_STATE_DISABLED) { status = HAL_I2S_Receive(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2, args[1].u_int); } else { - //DMA_HandleTypeDef rx_dma; - dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, - self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmarx = &self->rx_dma; - self->i2s.hdmatx = NULL; + /* dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, */ + /* self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); */ + /* self->i2s.hdmarx = &self->rx_dma; */ + /* self->i2s.hdmatx = NULL; */ status = HAL_I2S_Receive_DMA(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2); if (status == HAL_OK) { status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } - dma_deinit(&self->rx_dma); + /* dma_deinit(&self->rx_dma); */ } if (status != HAL_OK) { @@ -944,12 +1044,16 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT // interfaces when available instead of raising error? - //if (!self->is_duplex) { if (self->i2s.Init.FullDuplexMode != I2S_FULLDUPLEXMODE_ENABLE) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) not in duplex mode", self->i2s_id)); } + if (self->xfer_state > BUF_STR_DIV) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "Buffer op not allowed while streaming")); + } + self->xfer_state = BUFFER_RD_WR; // get buffers to send from/receive to mp_buffer_info_t bufinfo_send; uint8_t data_send[1]; @@ -984,27 +1088,30 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, } } - // TODO - implement 24-bit and 32-bit data width cases for all methods // send and receive the data HAL_StatusTypeDef status; + status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } + // TODO - implement 24-bit and 32-bit data width cases for all methods if (query_irq() == IRQ_STATE_DISABLED) { status = HAL_I2SEx_TransmitReceive(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len / 2, args[2].u_int); } else { - //DMA_HandleTypeDef tx_dma, rx_dma; - dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &self->tx_dma; - dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, - self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmarx = &self->rx_dma; + /* dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, */ + /* self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); */ + /* self->i2s.hdmatx = &self->tx_dma; */ + /* dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, */ + /* self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); */ + /* self->i2s.hdmarx = &self->rx_dma; */ status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len / 2); if (status == HAL_OK) { status = i2s_wait_dma_finished(&self->i2s, args[2].u_int); } - dma_deinit(&self->tx_dma); - dma_deinit(&self->rx_dma); + /* dma_deinit(&self->tx_dma); */ + /* dma_deinit(&self->rx_dma); */ } if (status != HAL_OK) { @@ -1019,13 +1126,13 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, } } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); -///////////// -///// Streaming methods: -// Taken from stream.c: +/**** I2S streaming methods: ****/ + +// These defs are taken from stream.c, included here b/c +// MICROPY_STREAMS_NON_BLOCK is defined for stmhal +// TODO: maybe move these defs to py/stream.h? #if MICROPY_STREAMS_NON_BLOCK -// TODO: This is POSIX-specific (but then POSIX is the only real thing, -// and anything else just emulates it, right?) #include #define is_nonblocking_error(errno) ((errno) == EAGAIN || (errno) == EWOULDBLOCK) #else @@ -1049,56 +1156,62 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - //struct _mp_obj_base_t *stream = (struct _mp_obj_base_t *)args[0].u_obj; + if (!(self->is_duplex || self->base_is_tx)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not in tx or duplex mode", + self->i2s_id)); + } + + if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not ready", + self->i2s_id)); + } + mp_obj_t stream = args[0].u_obj; mp_obj_type_t *type = mp_obj_get_type(stream); // Check that 'stream' provides an mp_stream_p_t and a read // Note that 'read' will be present even if the stream opened in write-mode if (type->stream_p == NULL || type->stream_p->read == NULL) { - //if (stream->type->stream_p == NULL || stream->type->stream_p->read == NULL) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Object type %s not a readable stream", mp_obj_get_type_str(stream))); } + if ((self->xfer_state != INACTIVE) && (self->xfer_state < BUF_STR_DIV)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "Stream op not allowed during buffer xfer")); + } + self->xfer_state |= STREAM_WR; + self->xfer_signal = I2S_SIG_NONE; int buf_sz = AUDIOBUFFER_BYTES / 4; int error; self->dstream_tx = stream; self->pp_ptr = 0; - self->stop_flag = false; self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); if (self->out_sz == MP_STREAM_ERROR) { if (is_nonblocking_error(error)) { - // https://docs.python.org/3.4/library/io.html#io.RawIOBase.read - // "If the object is in non-blocking mode and no bytes are available, - // None is returned." - // This is actually very weird, as naive truth check will treat - // this as EOF. + // nonblocking error behavior copied from py/stream.c: stream_read() + // see https://docs.python.org/3.4/library/io.html#io.RawIOBase.read return mp_const_none; } nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } - // Busy-wait for a few word-select cycles to make sure that slave-mode - // transfers don't start on the wrong clock level when in slave mode - - // seems to work when placed before call to dma_init. - // TODO: this could get stuck permanently if there is no master clock - // signal present on the WS pin; need a timeout and error: - if (!(self->is_master)) { - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} - } + /* dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, */ + /* self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); */ + /* self->i2s.hdmatx = &self->tx_dma; */ + /* self->i2s.hdmarx = NULL; */ - //DMA_HandleTypeDef tx_dma; - dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, - self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); - self->i2s.hdmatx = &self->tx_dma; - self->i2s.hdmarx = NULL; + // Sync to I2S bus and start sending data: + HAL_StatusTypeDef status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } - stream_out_buffer_handler(self); + i2s_stream_handler(self); return mp_const_none; } @@ -1123,6 +1236,13 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if (!(self->is_duplex || !(self->base_is_tx))) { + + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not in rx or duplex mode", + self->i2s_id)); + } + mp_obj_t stream = args[0].u_obj; mp_obj_type_t *type = mp_obj_get_type(stream); // Check that 'stream' provides 'mp_stream_p_t' and 'write' @@ -1133,40 +1253,108 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_obj_get_type_str(stream))); } + if ((self->xfer_state != INACTIVE) && (self->xfer_state < BUF_STR_DIV)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "Stream op not allowed during buffer xfer")); + } + self->xfer_state |= STREAM_RD; + self->xfer_signal = I2S_SIG_NONE; + int buf_sz = AUDIOBUFFER_BYTES / 4; self->dstream_rx = stream; + self->pp_ptr = 0; // NOTE: stream_in is not the simple switching of rx/tx and read/write methods // from stream_out: we also need to reverse order of operations; the receive must // fill the buffer _before_ we write out to a writable stream, and data must be // written out after the last receive - dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, - self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); - self->i2s.hdmatx = NULL; - self->i2s.hdmarx = &self->rx_dma; + /* dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, */ + /* self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); */ + /* self->i2s.hdmatx = NULL; */ + /* self->i2s.hdmarx = &self->rx_dma; */ - // A hack to make sure that slave-mode transfers don't start on the wrong clock: - // NOTE- DOESN'T WORK YET! - if (!(self->is_master)) { - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 1) {;} - while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == 0) {;} + HAL_StatusTypeDef status; + status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } + + if (!self->is_duplex) { + status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + } else { + memset(&self->audiobuf_tx[0], 0, buf_sz); + memset(&self->audiobuf_tx[1], 0, buf_sz); + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], + self->audiobuf_rx[self->pp_ptr], buf_sz / 2); } + if (status != HAL_OK) { + mp_hal_raise(status); + } + + //i2s_stream_handler(self); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); + +STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { + // TODO: Move the complexity for streaming - in, out, both - to this function. + // The stream functions validate the streams and pass them to the objects + // also set flags so that the callback handler knows if it is streaming in, out, or both + // stop can also take arguments; it can stop a stream in and a stream out separately + // with no args, stop will stop everything but leave the file handles open. int buf_sz = AUDIOBUFFER_BYTES / 4; - self->pp_ptr = 0; + int error; HAL_StatusTypeDef status; - status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + // Eventually this will stop rx and tx selectively as well, perhaps using a switch-case. + // That will set xfer_state as required to be read by rest of function. + if(self->xfer_signal == I2S_SIG_STOP_ALL) { + status = HAL_I2S_DMAStop(&self->i2s); + self->xfer_state = INACTIVE; + self->xfer_signal = I2S_SIG_NONE; + } else { + status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->out_sz / 2); + } if (status != HAL_OK) { mp_hal_raise(status); } - //stream_in_buffer_handler(self); + self->pp_ptr = !(self->pp_ptr); - return mp_const_none; + self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + + // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to end + // transmit on next callback: + if (self->out_sz == 0) { + self->xfer_signal = I2S_SIG_STOP_ALL; // eventually I2S_SIG_STOP_TX + } else if (self->out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } } -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); + +/* STATIC void stream_in_buffer_handler(pyb_i2s_obj_t *self) { */ +/* int buf_sz = AUDIOBUFFER_BYTES / 4; */ +/* int error; */ +/* HAL_StatusTypeDef status; */ +/* // We toggle the ping-pong pointer immediately to continue to receive data before */ +/* // writing out the buffer that was filled by the last receive call */ +/* self->pp_ptr = !(self->pp_ptr); */ +/* status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); */ + +/* if (status != HAL_OK) { */ +/* mp_hal_raise(status); */ +/* } */ + +/* mp_uint_t out_sz = self->dstream_rx->type->stream_p->write(self->dstream_rx, &self->audiobuf_rx[!(self->pp_ptr)], buf_sz, &error); */ + +/* printf("stream in buffer handler: out_sz = %d", out_sz); */ + +/* if (out_sz == MP_STREAM_ERROR) { */ +/* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ +/* } */ +/* } */ STATIC mp_obj_t pyb_i2s_pause(mp_obj_t self_in){ pyb_i2s_obj_t *self = self_in; @@ -1192,11 +1380,29 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_resume_obj, pyb_i2s_resume); STATIC mp_obj_t pyb_i2s_stop(mp_obj_t self_in){ pyb_i2s_obj_t *self = self_in; - self->stop_flag = true; + self->xfer_signal = I2S_SIG_STOP_ALL; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_stop_obj, pyb_i2s_stop); +STATIC mp_obj_t pyb_i2s_callback(mp_obj_t self_in, mp_obj_t callback) { + // Stub method to set a Python callback + // TODO: determine whether any interrupts need to be disabled; maybe not, + // since I anticipate that I2S callbacks may be changed on the fly to + // manage the behavior of long buffer transfers + pyb_i2s_obj_t *self = self_in; + if (callback == mp_const_none) { + self->callback = mp_const_none; + } else if (mp_obj_is_callable(callback)) { + self->callback = callback; + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "callback must be None or a callable object")); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_i2s_callback_obj, pyb_i2s_callback); + + STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { // instance methods { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_i2s_init_obj }, @@ -1209,6 +1415,7 @@ STATIC const mp_map_elem_t pyb_i2s_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_pause), (mp_obj_t)&pyb_i2s_pause_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_resume), (mp_obj_t)&pyb_i2s_resume_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_stop), (mp_obj_t)&pyb_i2s_stop_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_callback), (mp_obj_t)&pyb_i2s_callback_obj }, // class constants /// \constant MASTER - for initialising the bus to master mode From 2352620487195d9a49b6b2aa0fc5b6f783950d8a Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 19 Oct 2015 13:37:00 -0400 Subject: [PATCH 24/33] stmhal/i2s.c: Use new mp_is_nonblocking_error from stream.h --- stmhal/i2s.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 103a4fa9f9902..278fc4e53bc15 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -27,6 +27,8 @@ #include #include +// used by mp_is_nonblocking_error: +#include #include "py/nlr.h" #include "py/runtime.h" @@ -1128,20 +1130,9 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); /**** I2S streaming methods: ****/ - -// These defs are taken from stream.c, included here b/c -// MICROPY_STREAMS_NON_BLOCK is defined for stmhal -// TODO: maybe move these defs to py/stream.h? -#if MICROPY_STREAMS_NON_BLOCK -#include -#define is_nonblocking_error(errno) ((errno) == EAGAIN || (errno) == EWOULDBLOCK) -#else -#define is_nonblocking_error(errno) (0) -#endif - +// Taken from stream.c; not currently used: #define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) - STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -1192,7 +1183,7 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); if (self->out_sz == MP_STREAM_ERROR) { - if (is_nonblocking_error(error)) { + if (mp_is_nonblocking_error(error)) { // nonblocking error behavior copied from py/stream.c: stream_read() // see https://docs.python.org/3.4/library/io.html#io.RawIOBase.read return mp_const_none; From d29d45a1eb2dd9894952a7a6e68116c361a8c650 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 19 Oct 2015 16:28:59 -0400 Subject: [PATCH 25/33] stmhal/i2s.c: Refactor a few common checks into inline functions --- stmhal/i2s.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 278fc4e53bc15..c80fab4781b53 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -159,6 +159,19 @@ const DMA_InitTypeDef dma_init_struct_i2s = { .PeriphBurst = DMA_PBURST_SINGLE }; +static inline bool i2s_has_tx(pyb_i2s_obj_t *i2s_obj) { + return (i2s_obj->is_duplex || i2s_obj->base_is_tx); +} + +static inline bool i2s_has_rx(pyb_i2s_obj_t *i2s_obj) { + return (i2s_obj->is_duplex || !(i2s_obj->base_is_tx)); +} + +static inline bool i2s_is_master(pyb_i2s_obj_t *i2s_obj) { + return (i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_TX || + i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX); +} + // i2s_init0 is direct crib from stmhal/can.c - can_init0() void i2s_init0(void) { for (int i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(pyb_i2s_obj_all)); i++) { @@ -287,8 +300,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { // TODO: support more of the commonly-used frequencies and account for 16/32 bit frames // Also: Refactor to use macros as provided by stm32f4xx_hal_rcc_ex.h __HAL_RCC_PLLI2S_DISABLE(); - if (i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_TX || - i2s_obj->i2s.Init.Mode == I2S_MODE_MASTER_RX ) { + if (i2s_is_master(i2s_obj)) { if (i2s_obj->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { if ((i2s_obj->i2s.Init.AudioFreq & 0x7) == 0) { __HAL_RCC_PLLI2S_CONFIG(258, 3); // 8, 16, 24, 48kHz with mclkout; not 32 or 96 @@ -360,10 +372,7 @@ STATIC HAL_StatusTypeDef i2s_bus_sync(pyb_i2s_obj_t *self, uint32_t polarity, ui // cycle to sync with WS clock before initiating a transfer. bool pol = polarity & 1; uint32_t start = HAL_GetTick(); - // TODO: This test for master is used frequently - make it an inline function? - if (self->i2s.Init.Mode != I2S_MODE_MASTER_TX && - self->i2s.Init.Mode != I2S_MODE_MASTER_RX) { - + if (!i2s_is_master(self)) { while(GPIO_read_pin(self->pins[WS]->gpio, self->pins[WS]->pin) == pol) { if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } } @@ -489,8 +498,7 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki } mp_print_str(print, "\b], I2S."); if (self->is_enabled) { - if (self->i2s.Init.Mode == I2S_MODE_MASTER_TX || - self->i2s.Init.Mode == I2S_MODE_MASTER_RX) { + if (i2s_is_master(self)) { mp_printf(print, "%q, %q ", MP_QSTR_MASTER, MP_QSTR_mclkout); if (self->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { mp_printf(print, "on %q", self->pins[4]->name); @@ -498,11 +506,8 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_print_str(print, "off"); } mp_printf(print, ", %q=%u", MP_QSTR_audiofreq, self->i2s.Init.AudioFreq); - } else if (self->i2s.Init.Mode == I2S_MODE_SLAVE_TX || - self->i2s.Init.Mode == I2S_MODE_SLAVE_RX) { - mp_printf(print, "%q", MP_QSTR_SLAVE); } else { - // Shouldn't get here if self->is_enabled=true + mp_printf(print, "%q", MP_QSTR_SLAVE); } qstr standard = 0; @@ -1046,6 +1051,7 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT // interfaces when available instead of raising error? + // TODO - Coudl this test be wrapped in a simple inline function to replace the 'is_duplex' flag if (self->i2s.Init.FullDuplexMode != I2S_FULLDUPLEXMODE_ENABLE) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) not in duplex mode", self->i2s_id)); @@ -1147,13 +1153,12 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - if (!(self->is_duplex || self->base_is_tx)) { + // check configuration and current state of I2S bus + if (!i2s_has_tx(self)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) not in tx or duplex mode", + "I2S(%d) not configured for tx", self->i2s_id)); - } - - if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { + } else if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) not ready", self->i2s_id)); @@ -1227,8 +1232,7 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - if (!(self->is_duplex || !(self->base_is_tx))) { - + if (!i2s_has_rx(self)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) not in rx or duplex mode", self->i2s_id)); From d219b97034b2bc192a566433d36208daa45ba63d Mon Sep 17 00:00:00 2001 From: blmorris Date: Wed, 21 Oct 2015 16:22:05 -0400 Subject: [PATCH 26/33] stmhal/i2s.c: Lots of small changes on the way to getting stream_in and duplex to work Biggest functional change is that I2S DMA transfers will restart automatically if a callback occurs prematurely. --- stmhal/i2s.c | 275 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 165 insertions(+), 110 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index c80fab4781b53..5a319bbe418fe 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -80,9 +80,9 @@ typedef enum { BUFFER_RD = 0x02, BUFFER_RD_WR = 0x03, BUF_STR_DIV = 0x04, // (xfer_state >= BUF_STR_DIV) indicates streaming - STREAM_WR = 0x04, - STREAM_RD = 0x08, - STREAM_RD_WR = 0x0C, + STREAM_OUT = 0x04, + STREAM_IN = 0x08, + STREAM_IN_OUT = 0x0C, } xfer_state_t; // For buffer transfers, we want to be able to use the buffer methods write, read, and write_readinto @@ -317,11 +317,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { __HAL_RCC_PLLI2S_ENABLE(); } - if (HAL_I2S_Init(&i2s_obj->i2s) != HAL_OK) { - // This message is redundant, exception will be raised by return value - printf("OSError: HAL_I2S_Init failed\n"); - return false; - } else { + if (HAL_I2S_Init(&i2s_obj->i2s) == HAL_OK) { // Reset and initialize Tx and Rx DMA channels // TODO: Currently both DMA's are initialized regardless of whether I2S // is instantiated as simplex or duplex - should we check instead? @@ -337,11 +333,13 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { dma_init(&i2s_obj->rx_dma, i2s_obj->rx_dma_stream, &dma_init_struct_i2s, i2s_obj->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &i2s_obj->i2s); i2s_obj->i2s.hdmarx = &i2s_obj->rx_dma; - - i2s_obj->is_enabled = true; + i2s_obj->xfer_state = INACTIVE; - return true; + i2s_obj->is_enabled = true; + } else { + i2s_obj->is_enabled = false; } + return i2s_obj->is_enabled; } STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in); @@ -403,6 +401,20 @@ void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { self = MP_STATE_PORT(pyb_i2s_obj_all)[0]; } + // The HAL_I2S_TxCpltCallback and RxCpltCallback should only get called after + // a transfer is completed and the I2S bus is in the HAL_I2S_STATE_READY state. + // However, the first time that HAL_I2S_TransmitReceiveDMA() gets called after + // initializing the I2S bus, the callback gets triggered even though the I2S + // bus is not in a ready state. This will cause a hard crash the next time + // HAL_I2S_TransmitReceiveDMA() gets called. + // Checking the state and calling HAL_I2S_DMAStop() before calling the + // function again seems to resolve the problem; it also seems a bit hacky - + // would be nice to figure out why this happens and how to prevent it. + if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { + HAL_I2S_DMAStop(&self->i2s); + printf("I2S transfer stop and restart\n"); // DEBUG + } + if (self->xfer_state >= BUF_STR_DIV) { i2s_stream_handler(self); } else if (self->xfer_state != INACTIVE) { @@ -449,9 +461,9 @@ void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { // be triggered. This callback is defined to handle Rx-only transfers; it is // skipped for duplex transfers so callback code is only processed once: if (!self->is_duplex) { - if (self->xfer_state >= BUF_STR_DIV) { + if (self->xfer_state == STREAM_IN) { i2s_stream_handler(self); - } else if (self->xfer_state != INACTIVE) { + } else if (self->xfer_state > INACTIVE && self->xfer_state < BUF_STR_DIV) { if (self->callback != mp_const_none) { gc_lock(); nlr_buf_t nlr; @@ -478,7 +490,9 @@ void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { // Unused I2S callback stubs void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {} void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {} -void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) {} +void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { + printf("I2S Error\n"); +} /******************************************************************************/ /* Micro Python bindings */ @@ -496,12 +510,13 @@ STATIC void pyb_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki mp_print_str(print, "None "); } } - mp_print_str(print, "\b], I2S."); + mp_print_str(print, "\b]"); if (self->is_enabled) { + mp_print_str(print, ", I2S."); if (i2s_is_master(self)) { mp_printf(print, "%q, %q ", MP_QSTR_MASTER, MP_QSTR_mclkout); if (self->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) { - mp_printf(print, "on %q", self->pins[4]->name); + mp_printf(print, "on %q", self->pins[MCK]->name); } else { mp_print_str(print, "off"); } @@ -841,10 +856,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_init_obj, 1, pyb_i2s_init); STATIC mp_obj_t pyb_i2s_deinit(mp_obj_t self_in) { pyb_i2s_callback(self_in, mp_const_none); pyb_i2s_obj_t *self = self_in; - self->is_enabled = false; - dma_deinit(&self->tx_dma); - dma_deinit(&self->rx_dma); - HAL_I2S_DeInit(&self->i2s); + if (self->is_enabled) { + dma_deinit(&self->tx_dma); + dma_deinit(&self->rx_dma); + HAL_I2S_DeInit(&self->i2s); + self->is_enabled = false; + } if (0) { #if MICROPY_HW_ENABLE_I2S2 } else if (self->i2s.Instance == SPI2) { @@ -877,7 +894,7 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, // skeleton copied from spi.c static const mp_arg_t allowed_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, }; // parse args @@ -956,7 +973,7 @@ STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, static const mp_arg_t allowed_args[] = { { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, }; // parse args @@ -1040,7 +1057,7 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, static const mp_arg_t allowed_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, }; // parse args @@ -1057,7 +1074,7 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, "I2S(%d) not in duplex mode", self->i2s_id)); } - if (self->xfer_state > BUF_STR_DIV) { + if (self->xfer_state >= BUF_STR_DIV) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Buffer op not allowed while streaming")); } @@ -1136,7 +1153,7 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); /**** I2S streaming methods: ****/ -// Taken from stream.c; not currently used: +// Taken from stream.c; could use to make sure stream is opened in binary mode? #define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, @@ -1153,17 +1170,6 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // check configuration and current state of I2S bus - if (!i2s_has_tx(self)) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) not configured for tx", - self->i2s_id)); - } else if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) not ready", - self->i2s_id)); - } - mp_obj_t stream = args[0].u_obj; mp_obj_type_t *type = mp_obj_get_type(stream); // Check that 'stream' provides an mp_stream_p_t and a read @@ -1174,41 +1180,62 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_obj_get_type_str(stream))); } - if ((self->xfer_state != INACTIVE) && (self->xfer_state < BUF_STR_DIV)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "Stream op not allowed during buffer xfer")); + // make sure that I2S bus is configured for transmit: + if (!i2s_has_tx(self)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not configured for tx", + self->i2s_id)); } - self->xfer_state |= STREAM_WR; - self->xfer_signal = I2S_SIG_NONE; - int buf_sz = AUDIOBUFFER_BYTES / 4; - int error; - self->dstream_tx = stream; - self->pp_ptr = 0; + // If I2S state is not 'ready', raise an error _unless_ the current transaction is a + // simplex stream_in(); in that case set signals and stream handle to begin duplex + // stream_in + stream_out with next callback: - self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + /* if ((self->xfer_state != INACTIVE) && (self->xfer_state < BUF_STR_DIV)) { */ + /* nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, */ + /* "Stream op not allowed during buffer xfer")); */ + /* } */ - if (self->out_sz == MP_STREAM_ERROR) { - if (mp_is_nonblocking_error(error)) { - // nonblocking error behavior copied from py/stream.c: stream_read() - // see https://docs.python.org/3.4/library/io.html#io.RawIOBase.read - return mp_const_none; + if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { + if (self->xfer_state != STREAM_IN) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) bus busy", + self->i2s_id)); + } else { + // attach stream handle to i2s object and update state for stream_handler + self->xfer_signal = I2S_SIG_NONE; + self->xfer_state |= STREAM_OUT; + self->dstream_tx = stream; + } + } else { + // set up for simplex stream_out + self->xfer_signal = I2S_SIG_NONE; + self->xfer_state = STREAM_OUT; + int buf_sz = AUDIOBUFFER_BYTES / 4; + int error; + self->dstream_tx = stream; + self->pp_ptr = 0; + + self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, + &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + + if (self->out_sz == MP_STREAM_ERROR) { + if (mp_is_nonblocking_error(error)) { + // nonblocking error behavior copied from py/stream.c: stream_read() + // see https://docs.python.org/3.4/library/io.html#io.RawIOBase.read + return mp_const_none; + } + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); - } - /* dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, */ - /* self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); */ - /* self->i2s.hdmatx = &self->tx_dma; */ - /* self->i2s.hdmarx = NULL; */ + // Sync to I2S bus and start sending data: + HAL_StatusTypeDef status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } - // Sync to I2S bus and start sending data: - HAL_StatusTypeDef status = i2s_bus_sync(self, 0 /*polarity*/, 100); - if (status != HAL_OK) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + i2s_stream_handler(self); } - i2s_stream_handler(self); - return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); @@ -1232,12 +1259,6 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - if (!i2s_has_rx(self)) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "I2S(%d) not in rx or duplex mode", - self->i2s_id)); - } - mp_obj_t stream = args[0].u_obj; mp_obj_type_t *type = mp_obj_get_type(stream); // Check that 'stream' provides 'mp_stream_p_t' and 'write' @@ -1248,45 +1269,62 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_obj_get_type_str(stream))); } - if ((self->xfer_state != INACTIVE) && (self->xfer_state < BUF_STR_DIV)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, - "Stream op not allowed during buffer xfer")); - } - self->xfer_state |= STREAM_RD; - self->xfer_signal = I2S_SIG_NONE; - int buf_sz = AUDIOBUFFER_BYTES / 4; - self->dstream_rx = stream; - self->pp_ptr = 0; - - // NOTE: stream_in is not the simple switching of rx/tx and read/write methods - // from stream_out: we also need to reverse order of operations; the receive must - // fill the buffer _before_ we write out to a writable stream, and data must be - // written out after the last receive - - /* dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, */ - /* self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); */ - /* self->i2s.hdmatx = NULL; */ - /* self->i2s.hdmarx = &self->rx_dma; */ - - HAL_StatusTypeDef status; - status = i2s_bus_sync(self, 0 /*polarity*/, 100); - if (status != HAL_OK) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + // make sure that I2S bus is configured for receive: + if (!i2s_has_rx(self)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) not configured for rx", + self->i2s_id)); } + // If I2S state is not 'ready', raise an error _unless_ the current transaction is a + // simplex stream_in(); in that case set signals and stream handle to begin duplex + // stream_in + stream_out with next callback: - if (!self->is_duplex) { - status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { + if (self->xfer_state != STREAM_OUT) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "I2S(%d) bus busy", + self->i2s_id)); + } else { + // attach stream handle to i2s object and update state for stream_handler + self->xfer_signal = I2S_SIG_NONE; + self->xfer_state |= STREAM_IN; + self->dstream_rx = stream; + } } else { - memset(&self->audiobuf_tx[0], 0, buf_sz); - memset(&self->audiobuf_tx[1], 0, buf_sz); - status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], - self->audiobuf_rx[self->pp_ptr], buf_sz / 2); - } - if (status != HAL_OK) { - mp_hal_raise(status); - } + // set up for simplex stream_in + self->xfer_signal = I2S_SIG_NONE; + self->xfer_state = STREAM_IN; + int buf_sz = AUDIOBUFFER_BYTES / 4; + self->dstream_rx = stream; + self->pp_ptr = 0; + + // NOTE: stream_in is not the simple switching of rx/tx and read/write methods + // from stream_out - the order of operations also must be reversed; the receive + // must fill the buffer _before_ the buffer is written out a stream, and data + // must be written out after the last receive. stream_in initiates the streaming + // process by calling HAL_I2S_Receive_DMA or HAL_I2SEx_TransmitReceive_DMA, and + // any data received is written to dstream_rx by stream_handler when it is + // invoked via HAL I2S callback + + // Sync to I2S bus and start receiving data: + HAL_StatusTypeDef status = i2s_bus_sync(self, 0 /*polarity*/, 100); + if (status != HAL_OK) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); + } - //i2s_stream_handler(self); + if (!self->is_duplex) { + status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + } else { + // clear transmit buffers to use duplex function to begin simplex stream_in: + memset(&self->audiobuf_tx[0], 0, buf_sz); + memset(&self->audiobuf_tx[1], 0, buf_sz); + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], + self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + } + if (status != HAL_OK) { + mp_hal_raise(status); + } + } return mp_const_none; } @@ -1302,20 +1340,37 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { int buf_sz = AUDIOBUFFER_BYTES / 4; int error; HAL_StatusTypeDef status; + // Eventually this will stop rx and tx selectively as well, perhaps using a switch-case. // That will set xfer_state as required to be read by rest of function. - if(self->xfer_signal == I2S_SIG_STOP_ALL) { + if (self->xfer_signal == I2S_SIG_STOP_ALL) { status = HAL_I2S_DMAStop(&self->i2s); self->xfer_state = INACTIVE; self->xfer_signal = I2S_SIG_NONE; - } else { - status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->out_sz / 2); + } else if (self->xfer_signal == I2S_SIG_STOP_RX) { + self->xfer_state &= !STREAM_IN; + self->xfer_signal = I2S_SIG_NONE; + } else if (self->xfer_signal == I2S_SIG_STOP_TX) { + self->xfer_state &= !STREAM_OUT; + self->xfer_signal = I2S_SIG_NONE; } - if (status != HAL_OK) { - mp_hal_raise(status); + if (self->xfer_state == STREAM_OUT) { + if (self->is_duplex) { + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, + self->audiobuf_tx[self->pp_ptr], + self->audiobuf_rx[self->pp_ptr], + self->out_sz / 2); + } else { + status = HAL_I2S_Transmit_DMA(&self->i2s, + self->audiobuf_tx[self->pp_ptr], + self->out_sz / 2); + } + if (status != HAL_OK) { + mp_hal_raise(status); + } } - + self->pp_ptr = !(self->pp_ptr); self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); From 1816081ff02f00d262b7bbd4b0418d1d49e00e26 Mon Sep 17 00:00:00 2001 From: blmorris Date: Fri, 23 Oct 2015 15:26:36 -0400 Subject: [PATCH 27/33] stmhal/i2s.c: First commit with operational stream_in() function! --- stmhal/i2s.c | 110 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 24 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 5a319bbe418fe..6c023bc048065 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -72,7 +72,9 @@ // I2S3_EXT RX: DMA1_Stream0.CHANNEL_3 or DMA1_Stream2.CHANNEL_2 // I2S3_EXT TX: DMA1_Stream5.CHANNEL_2 -#define AUDIOBUFFER_BYTES 8192 +// Duplex streaming seems to require at least this much; there are hiccups at 8192 +// It may require some testing to determine the ideal value +#define AUDIOBUFFER_BYTES 12288 typedef enum { INACTIVE = 0x00, @@ -151,10 +153,10 @@ const DMA_InitTypeDef dma_init_struct_i2s = { .MemDataAlignment = DMA_MDATAALIGN_HALFWORD, .Mode = DMA_NORMAL, .Priority = DMA_PRIORITY_VERY_HIGH, - //.FIFOMode = DMA_FIFOMODE_DISABLE, - .FIFOMode = DMA_FIFOMODE_ENABLE, - //.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL, - .FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL, + .FIFOMode = DMA_FIFOMODE_DISABLE, + //.FIFOMode = DMA_FIFOMODE_ENABLE, + .FIFOThreshold = DMA_FIFO_THRESHOLD_FULL, + //.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL, .MemBurst = DMA_MBURST_SINGLE, .PeriphBurst = DMA_PBURST_SINGLE }; @@ -1201,7 +1203,8 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, "I2S(%d) bus busy", self->i2s_id)); } else { - // attach stream handle to i2s object and update state for stream_handler + // attach stream handle to i2s object and update state for + // stream_handler to add stream_out to ongoing stream transaction self->xfer_signal = I2S_SIG_NONE; self->xfer_state |= STREAM_OUT; self->dstream_tx = stream; @@ -1233,7 +1236,22 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); } - i2s_stream_handler(self); + if (self->is_duplex) { + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, + self->audiobuf_tx[self->pp_ptr], + self->audiobuf_rx[self->pp_ptr], + buf_sz / 2); + } else { + status = HAL_I2S_Transmit_DMA(&self->i2s, + self->audiobuf_tx[self->pp_ptr], + buf_sz / 2); + } + + if (status != HAL_OK) { + mp_hal_raise(status); + } + + //i2s_stream_handler(self); } return mp_const_none; @@ -1285,7 +1303,8 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, "I2S(%d) bus busy", self->i2s_id)); } else { - // attach stream handle to i2s object and update state for stream_handler + // attach stream handle to i2s object and update state for + // stream_handler to add stream_in to ongoing stream transaction self->xfer_signal = I2S_SIG_NONE; self->xfer_state |= STREAM_IN; self->dstream_rx = stream; @@ -1312,15 +1331,20 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); } - if (!self->is_duplex) { - status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); - } else { + if (self->is_duplex) { // clear transmit buffers to use duplex function to begin simplex stream_in: memset(&self->audiobuf_tx[0], 0, buf_sz); memset(&self->audiobuf_tx[1], 0, buf_sz); - status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], - self->audiobuf_rx[self->pp_ptr], buf_sz / 2); + status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, + self->audiobuf_tx[self->pp_ptr], + self->audiobuf_rx[self->pp_ptr], + buf_sz / 2); + } else { + status = HAL_I2S_Receive_DMA(&self->i2s, + self->audiobuf_rx[self->pp_ptr], + buf_sz / 2); } + if (status != HAL_OK) { mp_hal_raise(status); } @@ -1340,6 +1364,7 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { int buf_sz = AUDIOBUFFER_BYTES / 4; int error; HAL_StatusTypeDef status; + // Eventually this will stop rx and tx selectively as well, perhaps using a switch-case. // That will set xfer_state as required to be read by rest of function. @@ -1355,33 +1380,70 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { self->xfer_signal = I2S_SIG_NONE; } - if (self->xfer_state == STREAM_OUT) { + // DMA transfer: + if (self->xfer_state != INACTIVE) { + self->pp_ptr = !(self->pp_ptr); if (self->is_duplex) { status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->audiobuf_rx[self->pp_ptr], self->out_sz / 2); - } else { + + } else if (self->xfer_state == STREAM_OUT) { status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->out_sz / 2); + + } else if (self->xfer_state == STREAM_IN) { + status = HAL_I2S_Receive_DMA(&self->i2s, + self->audiobuf_rx[self->pp_ptr], + self->out_sz / 2); + + } else { + // TODO: clean up and raise an error? + // shouldn't get here in normal operation + status = HAL_ERROR; } + if (status != HAL_OK) { mp_hal_raise(status); } } - - self->pp_ptr = !(self->pp_ptr); - self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); + /* if (self->xfer_state && STREAM_IN) { */ + /* tmp_len = 0; */ + /* } */ + + bool buf_ptr = !(self->pp_ptr); - // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to end - // transmit on next callback: - if (self->out_sz == 0) { - self->xfer_signal = I2S_SIG_STOP_ALL; // eventually I2S_SIG_STOP_TX - } else if (self->out_sz == MP_STREAM_ERROR) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + if ((self->xfer_state & STREAM_OUT) != 0) { + self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, + &self->audiobuf_tx[buf_ptr], + buf_sz, &error); + + // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to end + // transmit on next callback: + if (self->out_sz == 0) { + self->xfer_signal = I2S_SIG_STOP_TX; // eventually I2S_SIG_STOP_TX + } else if (self->out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } } + + if ((self->xfer_state & STREAM_IN) != 0) { + self->out_sz = self->dstream_rx->type->stream_p->write(self->dstream_rx, + &self->audiobuf_rx[buf_ptr], + buf_sz, &error); + + // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to end + // transmit on next callback: + if (self->out_sz == 0) { + self->xfer_signal = I2S_SIG_STOP_RX; // eventually I2S_SIG_STOP_TX + } else if (self->out_sz == MP_STREAM_ERROR) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } + } + } /* STATIC void stream_in_buffer_handler(pyb_i2s_obj_t *self) { */ From 0e008770a619d7e6880f6e0ff2c75efc33da0569 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 27 Oct 2015 16:40:55 -0400 Subject: [PATCH 28/33] stmhal/i2s.c: stream_in(f) and stream_out(f) functions both working. Both functions can be started and stopped independently of each other. Comment cleanup in preparation for review and merging. --- stmhal/i2s.c | 218 +++++++++++++++++++++------------------------------ 1 file changed, 90 insertions(+), 128 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 6c023bc048065..d91ed15bb1338 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -27,9 +27,7 @@ #include #include -// used by mp_is_nonblocking_error: -#include - +#include // used by mp_is_nonblocking_error #include "py/nlr.h" #include "py/runtime.h" #include "py/gc.h" @@ -39,10 +37,10 @@ #include "py/objstr.h" #include "py/objlist.h" #include "py/stream.h" -#include "file.h" // for stream methods? +#include "file.h" #include "irq.h" #include "pin.h" -#include "led.h" // For led_toggle(n) debugging +#include "led.h" // For debugging using led_toggle(n) #include "genhdr/pins.h" #include "dma.h" #include "bufhelper.h" @@ -58,9 +56,6 @@ /// Master Clock (MCLK) signal to drive an external codec, or accept an I2S /// clock input signal (I2S_CLKIN) from an external source to drive I2SPLL. /// -/// TODO: Provide detailed synopsis of I2S -/// - Usage example -/// - Methods list // Possible DMA configurations for I2S busses: // SPI2 RX: DMA1_Stream3.CHANNEL_0 @@ -87,20 +82,23 @@ typedef enum { STREAM_IN_OUT = 0x0C, } xfer_state_t; -// For buffer transfers, we want to be able to use the buffer methods write, read, and write_readinto -// as the transfer initiation function as well as the callback function -// When a transfer is initiated, the state will be INACTIVE, so the full initialization occurs. -// after that, when the functions are incorporated into callbacks, the mode will be active, and the -// initialization can be skipped. The mode provides information as to the last function called, so if -// the callback is changed on the fly, (to go from a pure write to start recording as well) the -// functions can respond accordingly. +// For buffer transfers, we want to be able to use the buffer methods write, +// read, and write_readinto as the transfer initiation function as well as the +// callback function. +// When a transfer is initiated, the state will be INACTIVE, so the full +// initialization occurs. After that, when the functions are incorporated into +// callbacks, the mode will be active, and the initialization can be skipped. +// The mode provides information as to the last function called, so if +// the callback is changed on the fly, (to go from a pure write to start +// recording as well) the functions can respond accordingly. -// Also, each function (read, write, write_readinto, and stream_out and stream_in) will check to -// make sure that any ongoing transfer is of the same type (buffer or stream) and will throw an -// exception without interrupting the ongoing transfer if called. +// Also, each function (read, write, write_readinto, and stream_out and stream_in) +// will check to make sure that any ongoing transfer is of the same type (buffer +// or stream) and will throw an exception without interrupting the ongoing transfer +// if called. -// If the stop() function is passed an argument, it can set flags to stop part of a transfer (read or write) -// while allowing the other part to continue uninterrupted +// If the stop() function is passed an argument, it can set flags to stop part of +// a transfer (read or write) while allowing the other part to continue uninterrupted typedef enum { I2S_SIG_NONE, @@ -125,15 +123,14 @@ typedef struct _pyb_i2s_obj_t { xfer_state_t xfer_state; xfer_signal_t xfer_signal; const pin_obj_t *pins[5]; - // audio buffers placed before smaller struct members to keep word-alignment + // audio buffers placed before smaller struct members to keep word-alignment: uint16_t audiobuf_tx[2][AUDIOBUFFER_BYTES / 8]; uint16_t audiobuf_rx[2][AUDIOBUFFER_BYTES / 8]; mp_int_t i2s_id : 8; bool is_enabled : 1; - // bool is_master : 1; bool is_duplex : 1; bool base_is_tx : 1; // base instance SPIx is either tx or rx - bool pp_ptr : 1; // ping-pong pointer for double buffers + bool pp_ptr : 1; // ping-pong pointer for double buffers } pyb_i2s_obj_t; // pins are Bit Clock, Word Select, TX Data, RX Data, and Master Clock Out @@ -275,10 +272,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { return false; } - // New GPIO initialization section, supercedes the separate I2S2/3 sections - // Seems to work! Check with logic analyzer and some more pin combinations - // TODO - needs better description - + // GPIO Pin initialization // If master clock is not enabled, its pin will not get initialized int num_pins = (i2s_obj->i2s.Init.MCLKOutput == I2S_MCLKOUTPUT_ENABLE) ? NUM_PINS : NUM_PINS - 1; @@ -299,6 +293,7 @@ STATIC bool i2s_init(pyb_i2s_obj_t *i2s_obj) { // STM32Cube_FW_F4_V1.5.0/Drivers/BSP/STM32F4-Discovery/stm32f4_discovery_audio.c // 48kHz family is accurate for 8, 16, 24, and 48kHz but not 32 or 96 // 44.1kHz family is accurate for 11.025, 22.05 and 44.1kHz but not 88.2 + // TODO: support more of the commonly-used frequencies and account for 16/32 bit frames // Also: Refactor to use macros as provided by stm32f4xx_hal_rcc_ex.h __HAL_RCC_PLLI2S_DISABLE(); @@ -354,8 +349,9 @@ void i2s_deinit(void) { } } -// TODO: This is currently enabled, but eventually I2S should be entirely -// non-blocking, using DMA with callbacks +// TODO: This is currently enabled to support the buffer-oriented methods send, +// recv, and send_recv. Eventually I2S should be entirely non-blocking, using +// DMA with callbacks STATIC HAL_StatusTypeDef i2s_wait_dma_finished(I2S_HandleTypeDef *i2s, uint32_t timeout) { uint32_t start = HAL_GetTick(); while (HAL_I2S_GetState(i2s) != HAL_I2S_STATE_READY) { @@ -440,8 +436,6 @@ void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { gc_unlock(); } } - - //printf("I2S-TxCplt\n"); led_state(1, 0); //DEBUG } @@ -489,7 +483,7 @@ void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { led_state(2, 0); //DEBUG } -// Unused I2S callback stubs +// I2S HalfCplt and Error callback stubs, currently unused void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {} void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {} void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) { @@ -910,7 +904,7 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, // must transmit using send_recv // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle send on I2Sx_EXT - // interfaces when available instead of raising error? + // interfaces when available instead of raising error if (!self->base_is_tx) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) base instance not in transmit mode", @@ -937,16 +931,10 @@ STATIC mp_obj_t pyb_i2s_send(mp_uint_t n_args, const mp_obj_t *pos_args, if (query_irq() == IRQ_STATE_DISABLED) { status = HAL_I2S_Transmit(&self->i2s, bufinfo.buf, bufinfo.len / 2, args[1].u_int); } else { - /* dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, */ - /* self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); */ - /* self->i2s.hdmatx = &self->tx_dma; */ - /* self->i2s.hdmarx = NULL; */ status = HAL_I2S_Transmit_DMA(&self->i2s, bufinfo.buf, bufinfo.len / 2); if (status == HAL_OK) { - //led_toggle(1); status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } - /* dma_deinit(&self->tx_dma); */ } if (status != HAL_OK) { @@ -1016,15 +1004,10 @@ STATIC mp_obj_t pyb_i2s_recv(mp_uint_t n_args, const mp_obj_t *pos_args, status = HAL_I2S_Receive(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2, args[1].u_int); } else { - /* dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, */ - /* self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); */ - /* self->i2s.hdmarx = &self->rx_dma; */ - /* self->i2s.hdmatx = NULL; */ status = HAL_I2S_Receive_DMA(&self->i2s, (uint16_t*)vstr.buf, vstr.len / 2); if (status == HAL_OK) { status = i2s_wait_dma_finished(&self->i2s, args[1].u_int); } - /* dma_deinit(&self->rx_dma); */ } if (status != HAL_OK) { @@ -1070,7 +1053,8 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, // TODO - invoke HAL_I2SEx_TransmitReceive_DMA to handle recv on I2Sx_EXT // interfaces when available instead of raising error? - // TODO - Coudl this test be wrapped in a simple inline function to replace the 'is_duplex' flag + // TODO - Could this test be wrapped in a simple inline function to replace + // the pyb_i2s_obj->is_duplex flag? if (self->i2s.Init.FullDuplexMode != I2S_FULLDUPLEXMODE_ENABLE) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) not in duplex mode", self->i2s_id)); @@ -1126,19 +1110,11 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, status = HAL_I2SEx_TransmitReceive(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len / 2, args[2].u_int); } else { - /* dma_init(&self->tx_dma, self->tx_dma_stream, &dma_init_struct_i2s, */ - /* self->tx_dma_channel, DMA_MEMORY_TO_PERIPH, &self->i2s); */ - /* self->i2s.hdmatx = &self->tx_dma; */ - /* dma_init(&self->rx_dma, self->rx_dma_stream, &dma_init_struct_i2s, */ - /* self->rx_dma_channel, DMA_PERIPH_TO_MEMORY, &self->i2s); */ - /* self->i2s.hdmarx = &self->rx_dma; */ status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, bufinfo_send.buf, bufinfo_recv.buf, bufinfo_send.len / 2); if (status == HAL_OK) { status = i2s_wait_dma_finished(&self->i2s, args[2].u_int); } - /* dma_deinit(&self->tx_dma); */ - /* dma_deinit(&self->rx_dma); */ } if (status != HAL_OK) { @@ -1154,6 +1130,7 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); + /**** I2S streaming methods: ****/ // Taken from stream.c; could use to make sure stream is opened in binary mode? #define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) @@ -1188,15 +1165,9 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, "I2S(%d) not configured for tx", self->i2s_id)); } - // If I2S state is not 'ready', raise an error _unless_ the current transaction is a - // simplex stream_in(); in that case set signals and stream handle to begin duplex - // stream_in + stream_out with next callback: - - /* if ((self->xfer_state != INACTIVE) && (self->xfer_state < BUF_STR_DIV)) { */ - /* nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, */ - /* "Stream op not allowed during buffer xfer")); */ - /* } */ + // TODO: Change this check to determine the I2S state from the HAL state, + // not pyb_i2s_obj->xfer_state if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { if (self->xfer_state != STREAM_IN) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, @@ -1250,19 +1221,12 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, if (status != HAL_OK) { mp_hal_raise(status); } - - //i2s_stream_handler(self); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); - -// stream_in not working yet; committing initial code -// note that stream_in and stream_out are currently monopolizing the -// DMA callbacks; need to detect if stream or buffer methods are being used - STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -1317,6 +1281,13 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, self->dstream_rx = stream; self->pp_ptr = 0; + // TODO: This code is copied in HAL callback function, it should probably be + // factored out as a more sophisticated state check: + if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { + HAL_I2S_DMAStop(&self->i2s); + //printf("I2S transfer stop and restart\n"); // DEBUG + } + // NOTE: stream_in is not the simple switching of rx/tx and read/write methods // from stream_out - the order of operations also must be reversed; the receive // must fill the buffer _before_ the buffer is written out a stream, and data @@ -1355,33 +1326,35 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_in_obj, 1, pyb_i2s_stream_in); STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { - // TODO: Move the complexity for streaming - in, out, both - to this function. - // The stream functions validate the streams and pass them to the objects - // also set flags so that the callback handler knows if it is streaming in, out, or both - // stop can also take arguments; it can stop a stream in and a stream out separately - // with no args, stop will stop everything but leave the file handles open. - + // i2s_stream_handler manages HAL_I2S_*_DMA functions and data transfers + // between streams and buffers: int buf_sz = AUDIOBUFFER_BYTES / 4; int error; HAL_StatusTypeDef status; - - // Eventually this will stop rx and tx selectively as well, perhaps using a switch-case. - // That will set xfer_state as required to be read by rest of function. + // Set xfer_state as indicated by xfer_signal: if (self->xfer_signal == I2S_SIG_STOP_ALL) { - status = HAL_I2S_DMAStop(&self->i2s); self->xfer_state = INACTIVE; self->xfer_signal = I2S_SIG_NONE; } else if (self->xfer_signal == I2S_SIG_STOP_RX) { - self->xfer_state &= !STREAM_IN; + if (self->xfer_state == STREAM_OUT || self->xfer_state == STREAM_IN_OUT) { + self->xfer_state = STREAM_OUT; + } else { + self->xfer_state = INACTIVE; + } self->xfer_signal = I2S_SIG_NONE; } else if (self->xfer_signal == I2S_SIG_STOP_TX) { - self->xfer_state &= !STREAM_OUT; + if (self->xfer_state == STREAM_IN || self->xfer_state == STREAM_IN_OUT) { + self->xfer_state = STREAM_IN; + } else { + self->xfer_state = INACTIVE; + } self->xfer_signal = I2S_SIG_NONE; } // DMA transfer: - if (self->xfer_state != INACTIVE) { + if (self->xfer_state >= BUF_STR_DIV) { + // xfer_state indicates active stream transfer self->pp_ptr = !(self->pp_ptr); if (self->is_duplex) { status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, @@ -1404,16 +1377,16 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { // shouldn't get here in normal operation status = HAL_ERROR; } + } else { + // If xfer_state indicates active buffer transfer (it shouldn't get here + // in that case) or INACTIVE, all stream transfers are stopped. + status = HAL_I2S_DMAStop(&self->i2s); + } - if (status != HAL_OK) { - mp_hal_raise(status); - } + if (status != HAL_OK) { + mp_hal_raise(status); } - /* if (self->xfer_state && STREAM_IN) { */ - /* tmp_len = 0; */ - /* } */ - bool buf_ptr = !(self->pp_ptr); if ((self->xfer_state & STREAM_OUT) != 0) { @@ -1429,45 +1402,20 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } } - - if ((self->xfer_state & STREAM_IN) != 0) { - self->out_sz = self->dstream_rx->type->stream_p->write(self->dstream_rx, - &self->audiobuf_rx[buf_ptr], - buf_sz, &error); - // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to end - // transmit on next callback: - if (self->out_sz == 0) { - self->xfer_signal = I2S_SIG_STOP_RX; // eventually I2S_SIG_STOP_TX - } else if (self->out_sz == MP_STREAM_ERROR) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + if ((self->xfer_state & STREAM_IN) != 0) { + // TODO: mp_stream_write is an available public function, but there is no + // mp_stream_read - is there something equivalent that I can use? + // Also need to figure out correct way to handle errors and end transfer for stream_in + mp_obj_t ret = mp_stream_write(self->dstream_rx, &self->audiobuf_rx[buf_ptr], buf_sz); + if ((int) ret < buf_sz) { + // A write less than buf_sz is probably because the filesystem is full; + // stop the stream_in + self->xfer_signal = I2S_SIG_STOP_RX; } - } - + } } -/* STATIC void stream_in_buffer_handler(pyb_i2s_obj_t *self) { */ -/* int buf_sz = AUDIOBUFFER_BYTES / 4; */ -/* int error; */ -/* HAL_StatusTypeDef status; */ -/* // We toggle the ping-pong pointer immediately to continue to receive data before */ -/* // writing out the buffer that was filled by the last receive call */ -/* self->pp_ptr = !(self->pp_ptr); */ -/* status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], buf_sz / 2); */ - -/* if (status != HAL_OK) { */ -/* mp_hal_raise(status); */ -/* } */ - -/* mp_uint_t out_sz = self->dstream_rx->type->stream_p->write(self->dstream_rx, &self->audiobuf_rx[!(self->pp_ptr)], buf_sz, &error); */ - -/* printf("stream in buffer handler: out_sz = %d", out_sz); */ - -/* if (out_sz == MP_STREAM_ERROR) { */ -/* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ -/* } */ -/* } */ - STATIC mp_obj_t pyb_i2s_pause(mp_obj_t self_in){ pyb_i2s_obj_t *self = self_in; HAL_StatusTypeDef status; @@ -1490,16 +1438,30 @@ STATIC mp_obj_t pyb_i2s_resume(mp_obj_t self_in){ } STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_resume_obj, pyb_i2s_resume); -STATIC mp_obj_t pyb_i2s_stop(mp_obj_t self_in){ - pyb_i2s_obj_t *self = self_in; - self->xfer_signal = I2S_SIG_STOP_ALL; +STATIC mp_obj_t pyb_i2s_stop(mp_uint_t n_args, const mp_obj_t *args){ + pyb_i2s_obj_t *self = args[0]; + if (n_args > 1) { + const char *p = mp_obj_str_get_str(args[1]); + qstr signal = qstr_from_str(p); + if (signal == MP_QSTR_stream_in) { + self->xfer_signal |= I2S_SIG_STOP_RX; + } else if (signal == MP_QSTR_stream_out) { + self->xfer_signal |= I2S_SIG_STOP_TX; + } else { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "%s not a valid stop argument", p)); + } + } else { + self->xfer_signal = I2S_SIG_STOP_ALL; + } + printf("stp %d,%d", self->xfer_signal, self->xfer_state); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_stop_obj, pyb_i2s_stop); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_i2s_stop_obj, 1, 2, pyb_i2s_stop); STATIC mp_obj_t pyb_i2s_callback(mp_obj_t self_in, mp_obj_t callback) { // Stub method to set a Python callback - // TODO: determine whether any interrupts need to be disabled; maybe not, + // TODO: determine whether interrupts need to be disabled; maybe not, // since I anticipate that I2S callbacks may be changed on the fly to // manage the behavior of long buffer transfers pyb_i2s_obj_t *self = self_in; From abd534e7d16e48523c299677aab70863f43522af Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 3 Nov 2015 10:26:21 -0500 Subject: [PATCH 29/33] stmhal/i2s.c: Initial implementation of 'len' arg to stream_in/_out Also implements Python-defined callbacks for stream and buffer transfers that finish without being exlpicitly stopped --- stmhal/i2s.c | 141 +++++++++++++++++++++++++----------------- stmhal/qstrdefsport.h | 1 + 2 files changed, 84 insertions(+), 58 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index d91ed15bb1338..e67b25d3afb02 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -105,6 +105,10 @@ typedef enum { I2S_SIG_STOP_RX, I2S_SIG_STOP_TX, I2S_SIG_STOP_ALL, + I2S_SIG_STOP_EOF_DIV, + I2S_SIG_EOF_RX, + I2S_SIG_EOF_TX, + I2S_SIG_EOF_ALL, } xfer_signal_t; typedef struct _pyb_i2s_obj_t { @@ -118,6 +122,8 @@ typedef struct _pyb_i2s_obj_t { DMA_HandleTypeDef rx_dma; mp_obj_base_t *dstream_tx; mp_obj_base_t *dstream_rx; + mp_int_t stream_tx_len; + mp_int_t stream_rx_len; mp_uint_t out_sz; mp_obj_t callback; xfer_state_t xfer_state; @@ -385,6 +391,25 @@ STATIC HAL_StatusTypeDef i2s_bus_sync(pyb_i2s_obj_t *self, uint32_t polarity, ui STATIC void i2s_stream_handler(pyb_i2s_obj_t *self); STATIC mp_obj_t pyb_i2s_callback(mp_obj_t self_in, mp_obj_t callback); +STATIC void i2s_handle_mp_callback(pyb_i2s_obj_t *self) { + if (self->callback != mp_const_none) { + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(self->callback, self); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->callback = mp_const_none; + // DMA_HandleTypeDef dma = self->tx_dma; + // __HAL_DMA_DISABLE(&dma); + printf("uncaught exception in I2S(%u) DMA interrupt handler\n", self->i2s_id); + mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); + } + gc_unlock(); + } +} + void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { led_state(1, 1); //DEBUG // I2S root pointer index is 1 if both I2S instances are enabled and I2S @@ -417,24 +442,7 @@ void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { i2s_stream_handler(self); } else if (self->xfer_state != INACTIVE) { // buffer transfer, call user-defined callback - if (self->callback != mp_const_none) { - gc_lock(); - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - // mp_call_function_1 wasn't working -- need to investigate - // mp_call_function_1(self->callback, self) - mp_call_function_0(self->callback); - nlr_pop(); - } else { - // Uncaught exception; disable the callback so it doesn't run again. - self->callback = mp_const_none; - // DMA_HandleTypeDef dma = self->tx_dma; - // __HAL_DMA_DISABLE(&dma); - printf("uncaught exception in I2S(%u) DMA interrupt handler\n", self->i2s_id); - mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); - } - gc_unlock(); - } + i2s_handle_mp_callback(self); } led_state(1, 0); //DEBUG } @@ -460,24 +468,7 @@ void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) { if (self->xfer_state == STREAM_IN) { i2s_stream_handler(self); } else if (self->xfer_state > INACTIVE && self->xfer_state < BUF_STR_DIV) { - if (self->callback != mp_const_none) { - gc_lock(); - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - // mp_call_function_1 wasn't working -- need to investigate - // mp_call_function_1(self->callback, self) - mp_call_function_0(self->callback); - nlr_pop(); - } else { - // Uncaught exception; disable the callback so it doesn't run again. - self->callback = mp_const_none; - // DMA_HandleTypeDef dma = self->rx_dma; - // __HAL_DMA_DISABLE(&dma); - printf("uncaught exception in I2S(%u) DMA interrupt handler\n", self->i2s_id); - mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); - } - gc_unlock(); - } + i2s_handle_mp_callback(self); } } led_state(2, 0); //DEBUG @@ -878,7 +869,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2s_deinit_obj, pyb_i2s_deinit); // TODO - adapt docstrings copied from spi.c for i2s -/// \method send(send, *, timeout=5000) +/// \method send(send, *, timeout=100) /// Send data on the bus: /// /// - `send` is the data to send (an integer to send, or a buffer object). @@ -947,7 +938,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_obj, 1, pyb_i2s_send); // TODO - adapt docstrings copied from spi.c for i2s -/// \method recv(recv, *, timeout=5000) +/// \method recv(recv, *, timeout=100) /// /// Receive data on the bus: /// @@ -1025,7 +1016,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_recv_obj, 1, pyb_i2s_recv); // TODO - adapt docstrings copied from spi.c for i2s -/// \method send_recv(send, recv=None, *, timeout=5000) +/// \method send_recv(send, recv=None, *, timeout=100) /// /// Send and receive data on the bus at the same time: /// @@ -1042,7 +1033,7 @@ STATIC mp_obj_t pyb_i2s_send_recv(mp_uint_t n_args, const mp_obj_t *pos_args, static const mp_arg_t allowed_args[] = { { MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; // parse args @@ -1140,7 +1131,9 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, static const mp_arg_t allowed_args[] = { { MP_QSTR_stream_out, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, + { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + // TODO: Allow length to have units other than n_samples }; // parse args @@ -1178,12 +1171,14 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, // stream_handler to add stream_out to ongoing stream transaction self->xfer_signal = I2S_SIG_NONE; self->xfer_state |= STREAM_OUT; + self->stream_tx_len = args[2].u_int * 4; // n_samples -> n_bytes self->dstream_tx = stream; } } else { // set up for simplex stream_out self->xfer_signal = I2S_SIG_NONE; self->xfer_state = STREAM_OUT; + self->stream_tx_len = args[2].u_int * 4; // n_samples -> n_bytes int buf_sz = AUDIOBUFFER_BYTES / 4; int error; self->dstream_tx = stream; @@ -1232,7 +1227,9 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, static const mp_arg_t allowed_args[] = { { MP_QSTR_stream_in, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, + { MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + // TODO: Allow length to have units other than n_samples }; // parse args @@ -1271,12 +1268,14 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, // stream_handler to add stream_in to ongoing stream transaction self->xfer_signal = I2S_SIG_NONE; self->xfer_state |= STREAM_IN; + self->stream_rx_len = args[2].u_int * 4; // n_samples -> n_bytes self->dstream_rx = stream; } } else { // set up for simplex stream_in self->xfer_signal = I2S_SIG_NONE; self->xfer_state = STREAM_IN; + self->stream_rx_len = args[2].u_int * 4; // n_samples -> n_bytes int buf_sz = AUDIOBUFFER_BYTES / 4; self->dstream_rx = stream; self->pp_ptr = 0; @@ -1333,24 +1332,30 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { HAL_StatusTypeDef status; // Set xfer_state as indicated by xfer_signal: - if (self->xfer_signal == I2S_SIG_STOP_ALL) { + int sig = self->xfer_signal; + int state = self->xfer_state; + if (sig == I2S_SIG_STOP_ALL || sig == I2S_SIG_EOF_ALL) { self->xfer_state = INACTIVE; self->xfer_signal = I2S_SIG_NONE; - } else if (self->xfer_signal == I2S_SIG_STOP_RX) { - if (self->xfer_state == STREAM_OUT || self->xfer_state == STREAM_IN_OUT) { + } else if (sig == I2S_SIG_STOP_RX || sig == I2S_SIG_EOF_RX) { + if (state == STREAM_OUT || state == STREAM_IN_OUT) { self->xfer_state = STREAM_OUT; } else { self->xfer_state = INACTIVE; } self->xfer_signal = I2S_SIG_NONE; - } else if (self->xfer_signal == I2S_SIG_STOP_TX) { - if (self->xfer_state == STREAM_IN || self->xfer_state == STREAM_IN_OUT) { + } else if (sig == I2S_SIG_STOP_TX || sig == I2S_SIG_EOF_TX) { + if (state == STREAM_IN || state == STREAM_IN_OUT) { self->xfer_state = STREAM_IN; } else { self->xfer_state = INACTIVE; } self->xfer_signal = I2S_SIG_NONE; } + // If I2S_SIG_EOF_* then invoke user-defined callback + if (sig > I2S_SIG_STOP_EOF_DIV) { + i2s_handle_mp_callback(self); + } // DMA transfer: if (self->xfer_state >= BUF_STR_DIV) { @@ -1394,26 +1399,46 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { &self->audiobuf_tx[buf_ptr], buf_sz, &error); - // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to end - // transmit on next callback: - if (self->out_sz == 0) { - self->xfer_signal = I2S_SIG_STOP_TX; // eventually I2S_SIG_STOP_TX + if (self->stream_tx_len > -1) { + if (self->stream_tx_len >= self->out_sz) { + self->stream_tx_len -= self->out_sz; + } else { + self->stream_tx_len = 0; + } + } + // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to + // indicate end of file + if (self->out_sz == 0 || self->stream_tx_len == 0) { + self->xfer_signal = I2S_SIG_EOF_TX; } else if (self->out_sz == MP_STREAM_ERROR) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } } - if ((self->xfer_state & STREAM_IN) != 0) { // TODO: mp_stream_write is an available public function, but there is no // mp_stream_read - is there something equivalent that I can use? // Also need to figure out correct way to handle errors and end transfer for stream_in mp_obj_t ret = mp_stream_write(self->dstream_rx, &self->audiobuf_rx[buf_ptr], buf_sz); - if ((int) ret < buf_sz) { - // A write less than buf_sz is probably because the filesystem is full; - // stop the stream_in - self->xfer_signal = I2S_SIG_STOP_RX; + if (mp_obj_is_integer(ret)) { + // stream_in is half as long as stream_out, possibly b/c of mixup b/t uPy ints + // and machine ints? + mp_int_t r = (mp_int_t) ret; + if (self->stream_rx_len > -1) { + if (self->stream_rx_len >= r) { + self->stream_rx_len -= r; + } else { + self->stream_rx_len = 0; + } + } + if (r < buf_sz || self->stream_rx_len == 0) { + // A write smaller than buf_sz is probably because the filesystem is full: + self->xfer_signal = I2S_SIG_EOF_RX; + } + } else { + // If ret isn't an integer it indicates a stream write error, stop rx: + self->xfer_signal = I2S_SIG_EOF_RX; } - } + } } STATIC mp_obj_t pyb_i2s_pause(mp_obj_t self_in){ @@ -1454,7 +1479,7 @@ STATIC mp_obj_t pyb_i2s_stop(mp_uint_t n_args, const mp_obj_t *args){ } else { self->xfer_signal = I2S_SIG_STOP_ALL; } - printf("stp %d,%d", self->xfer_signal, self->xfer_state); + //printf("stp %d,%d", self->xfer_signal, self->xfer_state); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_i2s_stop_obj, 1, 2, pyb_i2s_stop); diff --git a/stmhal/qstrdefsport.h b/stmhal/qstrdefsport.h index 84aa65a447352..aff6a8a134a72 100644 --- a/stmhal/qstrdefsport.h +++ b/stmhal/qstrdefsport.h @@ -361,6 +361,7 @@ Q(mclkout) Q(audiofreq) Q(polarity) Q(clksrc) +Q(len) Q(MASTER) Q(SLAVE) Q(PHILIPS) From 0622ae15df21566b8706444abbe375997543d674 Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 3 Nov 2015 16:12:01 -0500 Subject: [PATCH 30/33] stmhal/i2s.c: Replace #include MICROPY_HAL_H with "py/mphal.h" --- stmhal/i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index e67b25d3afb02..f72e3ca5d9d0e 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -37,6 +37,7 @@ #include "py/objstr.h" #include "py/objlist.h" #include "py/stream.h" +#include "py/mphal.h" #include "file.h" #include "irq.h" #include "pin.h" @@ -45,7 +46,6 @@ #include "dma.h" #include "bufhelper.h" #include "i2s.h" -#include MICROPY_HAL_H /// \moduleref pyb /// \class I2S - Inter-IC-Sound, a protocol to transfer isochronous audio data From c5ff1fd9dfb970db71adcd3f42d2d155a07dfed8 Mon Sep 17 00:00:00 2001 From: blmorris Date: Thu, 5 Nov 2015 14:53:28 -0500 Subject: [PATCH 31/33] stmhal/i2s.c: Update internal stream read and write methods --- stmhal/i2s.c | 155 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 49 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index f72e3ca5d9d0e..8907fceb01027 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -124,7 +124,8 @@ typedef struct _pyb_i2s_obj_t { mp_obj_base_t *dstream_rx; mp_int_t stream_tx_len; mp_int_t stream_rx_len; - mp_uint_t out_sz; + mp_uint_t last_stream_read_sz; + mp_uint_t last_stream_write_sz; mp_obj_t callback; xfer_state_t xfer_state; xfer_signal_t xfer_signal; @@ -1126,6 +1127,49 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_send_recv_obj, 1, pyb_i2s_send_recv); // Taken from stream.c; could use to make sure stream is opened in binary mode? #define STREAM_CONTENT_TYPE(stream) (((stream)->is_text) ? &mp_type_str : &mp_type_bytes) +// helper functions for stream operations; to be removed if similar functions get +// incorporated into stream.[ch] +typedef enum { + MP_STREAM_OP_READ, + MP_STREAM_OP_WRITE, + MP_STREAM_OP_IOCTL, +} mp_stream_op_t; + +mp_obj_t mp_stream_op_supported(mp_obj_t self_in, mp_stream_op_t op) { + struct _mp_obj_base_t *o = (struct _mp_obj_base_t *)self_in; + if (o->type->stream_p == NULL) { + // CPython: io.UnsupportedOperation, OSError subclass + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "stream object required")); + } else if (op == MP_STREAM_OP_READ && o->type->stream_p->read == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "object with stream.read required")); + } else if (op == MP_STREAM_OP_WRITE && o->type->stream_p->write == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "object with stream.write required")); + } else if (op == MP_STREAM_OP_IOCTL && o->type->stream_p->ioctl == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, + "object with stream.ioctl required")); + } + return o; +} + +mp_obj_t mp_stream_read(mp_obj_t self_in, void *buf, mp_uint_t len) { + // Supported op check included here for protability and to be equivalent to + // mp_stream_write; some stream read methods will now have redundant checks: + struct _mp_obj_base_t *o = mp_stream_op_supported(self_in, MP_STREAM_OP_READ); + int error; + mp_uint_t out_sz = o->type->stream_p->read(self_in, buf, len, &error); + if (out_sz == MP_STREAM_ERROR) { + if (mp_is_nonblocking_error(error)) { + return mp_const_none; + } + nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + } else { + return MP_OBJ_NEW_SMALL_INT(out_sz); + } +} + STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -1142,15 +1186,7 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t stream = args[0].u_obj; - mp_obj_type_t *type = mp_obj_get_type(stream); - // Check that 'stream' provides an mp_stream_p_t and a read - // Note that 'read' will be present even if the stream opened in write-mode - if (type->stream_p == NULL || type->stream_p->read == NULL) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Object type %s not a readable stream", - mp_obj_get_type_str(stream))); - } + mp_obj_t stream = mp_stream_op_supported(args[0].u_obj, MP_STREAM_OP_READ); // make sure that I2S bus is configured for transmit: if (!i2s_has_tx(self)) { @@ -1184,10 +1220,10 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, self->dstream_tx = stream; self->pp_ptr = 0; - self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, + self->last_stream_read_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); - if (self->out_sz == MP_STREAM_ERROR) { + if (self->last_stream_read_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(error)) { // nonblocking error behavior copied from py/stream.c: stream_read() // see https://docs.python.org/3.4/library/io.html#io.RawIOBase.read @@ -1238,15 +1274,18 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t stream = args[0].u_obj; - mp_obj_type_t *type = mp_obj_get_type(stream); - // Check that 'stream' provides 'mp_stream_p_t' and 'write' - // Note that 'write' will be present even if the stream opened in read-mode (?) - if (type->stream_p == NULL || type->stream_p->write == NULL) { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "Object type %s not a writeable stream", - mp_obj_get_type_str(stream))); - } + + mp_obj_t stream = mp_stream_op_supported(args[0].u_obj, MP_STREAM_OP_WRITE); + + /* mp_obj_t stream = args[0].u_obj; */ + /* mp_obj_type_t *type = mp_obj_get_type(stream); */ + /* // Check that 'stream' provides 'mp_stream_p_t' and 'write' */ + /* // Note that 'write' will be present even if the stream opened in read-mode (?) */ + /* if (type->stream_p == NULL || type->stream_p->write == NULL) { */ + /* nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, */ + /* "Object type %s not a writeable stream", */ + /* mp_obj_get_type_str(stream))); */ + /* } */ // make sure that I2S bus is configured for receive: if (!i2s_has_rx(self)) { @@ -1328,7 +1367,7 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { // i2s_stream_handler manages HAL_I2S_*_DMA functions and data transfers // between streams and buffers: int buf_sz = AUDIOBUFFER_BYTES / 4; - int error; + // int error; HAL_StatusTypeDef status; // Set xfer_state as indicated by xfer_signal: @@ -1365,17 +1404,18 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->audiobuf_rx[self->pp_ptr], - self->out_sz / 2); + self->last_stream_read_sz / 2); } else if (self->xfer_state == STREAM_OUT) { status = HAL_I2S_Transmit_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], - self->out_sz / 2); + self->last_stream_read_sz / 2); } else if (self->xfer_state == STREAM_IN) { + // TODO - Simplex receive should not use last read or write size! status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], - self->out_sz / 2); + self->last_stream_read_sz / 2); } else { // TODO: clean up and raise an error? @@ -1395,42 +1435,59 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { bool buf_ptr = !(self->pp_ptr); if ((self->xfer_state & STREAM_OUT) != 0) { - self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, - &self->audiobuf_tx[buf_ptr], - buf_sz, &error); - - if (self->stream_tx_len > -1) { - if (self->stream_tx_len >= self->out_sz) { - self->stream_tx_len -= self->out_sz; - } else { - self->stream_tx_len = 0; + /* self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, */ + /* &self->audiobuf_tx[buf_ptr], */ + /* buf_sz, &error); */ + + /* if (self->stream_tx_len > -1) { */ + /* if (self->stream_tx_len >= self->out_sz) { */ + /* self->stream_tx_len -= self->out_sz; */ + /* } else { */ + /* self->stream_tx_len = 0; */ + /* } */ + /* } */ + /* // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to */ + /* // indicate end of file */ + /* if (self->out_sz == 0 || self->stream_tx_len == 0) { */ + /* self->xfer_signal = I2S_SIG_EOF_TX; */ + /* } else if (self->out_sz == MP_STREAM_ERROR) { */ + /* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ + /* } */ + mp_obj_t ret = mp_stream_read(self->dstream_tx, &self->audiobuf_tx[buf_ptr], buf_sz); + if (mp_obj_is_integer(ret)) { + // maybe now this should be self->out_sz = mp_obj_get_int(ret) + // or maybe need a last_read_sz and last_write_sz? + self->last_stream_read_sz = mp_obj_get_int(ret); + if (self->stream_tx_len > -1) { + if (self->stream_tx_len >= self->last_stream_read_sz) { + self->stream_tx_len -= self->last_stream_read_sz; + } else { + self->stream_tx_len = 0; + } } - } - // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to - // indicate end of file - if (self->out_sz == 0 || self->stream_tx_len == 0) { + if (self->last_stream_read_sz < buf_sz || self->stream_tx_len == 0) { + // A read smaller than buf_sz is because we are at EOF: + self->xfer_signal = I2S_SIG_EOF_TX; + } + } else { + // If ret isn't an integer it indicates a stream read error, stop tx: self->xfer_signal = I2S_SIG_EOF_TX; - } else if (self->out_sz == MP_STREAM_ERROR) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); } + } if ((self->xfer_state & STREAM_IN) != 0) { - // TODO: mp_stream_write is an available public function, but there is no - // mp_stream_read - is there something equivalent that I can use? - // Also need to figure out correct way to handle errors and end transfer for stream_in + // Need to figure out correct way to handle errors and end transfer for stream_in mp_obj_t ret = mp_stream_write(self->dstream_rx, &self->audiobuf_rx[buf_ptr], buf_sz); if (mp_obj_is_integer(ret)) { - // stream_in is half as long as stream_out, possibly b/c of mixup b/t uPy ints - // and machine ints? - mp_int_t r = (mp_int_t) ret; + self->last_stream_write_sz = mp_obj_get_int(ret); if (self->stream_rx_len > -1) { - if (self->stream_rx_len >= r) { - self->stream_rx_len -= r; + if (self->stream_rx_len >= self->last_stream_write_sz) { + self->stream_rx_len -= self->last_stream_write_sz; } else { self->stream_rx_len = 0; } } - if (r < buf_sz || self->stream_rx_len == 0) { + if (self->last_stream_write_sz < buf_sz || self->stream_rx_len == 0) { // A write smaller than buf_sz is probably because the filesystem is full: self->xfer_signal = I2S_SIG_EOF_RX; } From 28008640b79bc5861e47cdd509fcac68b78fa2c0 Mon Sep 17 00:00:00 2001 From: blmorris Date: Mon, 9 Nov 2015 15:37:58 -0500 Subject: [PATCH 32/33] stmhal/i2s.c: Milestone - basic streaming functionality is working Can now do the following with stream_in and stream_out: stream_in(f) or stream_in(f, len=n_samples) stream_out(f) or stream_out(f, len=n_samples) stop(), stop('stream_in'), stop('stream_out') stream_in and stream_out can operate concurrently, and can be started and stopped independently of each other --- stmhal/i2s.c | 145 +++++++++++++++++++++------------------------------ 1 file changed, 59 insertions(+), 86 deletions(-) diff --git a/stmhal/i2s.c b/stmhal/i2s.c index 8907fceb01027..4655fb6c787d9 100644 --- a/stmhal/i2s.c +++ b/stmhal/i2s.c @@ -436,7 +436,7 @@ void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) { // would be nice to figure out why this happens and how to prevent it. if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { HAL_I2S_DMAStop(&self->i2s); - printf("I2S transfer stop and restart\n"); // DEBUG + // printf("I2S transfer stop and restart\n"); // DEBUG } if (self->xfer_state >= BUF_STR_DIV) { @@ -1195,8 +1195,9 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, self->i2s_id)); } - // TODO: Change this check to determine the I2S state from the HAL state, - // not pyb_i2s_obj->xfer_state + // If I2S state is not 'ready', raise an error _unless_ the current transaction is a + // simplex stream_in(); in that case set signals and stream handle to begin duplex + // stream_in + stream_out with next callback: if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { if (self->xfer_state != STREAM_IN) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, @@ -1207,29 +1208,39 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, // stream_handler to add stream_out to ongoing stream transaction self->xfer_signal = I2S_SIG_NONE; self->xfer_state |= STREAM_OUT; - self->stream_tx_len = args[2].u_int * 4; // n_samples -> n_bytes + self->stream_tx_len = args[2].u_int; self->dstream_tx = stream; } } else { // set up for simplex stream_out self->xfer_signal = I2S_SIG_NONE; self->xfer_state = STREAM_OUT; - self->stream_tx_len = args[2].u_int * 4; // n_samples -> n_bytes + self->stream_tx_len = args[2].u_int; int buf_sz = AUDIOBUFFER_BYTES / 4; - int error; self->dstream_tx = stream; self->pp_ptr = 0; - self->last_stream_read_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, - &self->audiobuf_tx[self->pp_ptr], buf_sz, &error); - - if (self->last_stream_read_sz == MP_STREAM_ERROR) { - if (mp_is_nonblocking_error(error)) { - // nonblocking error behavior copied from py/stream.c: stream_read() - // see https://docs.python.org/3.4/library/io.html#io.RawIOBase.read - return mp_const_none; + // If stream length is specified: + if ((self->stream_tx_len > -1) && (buf_sz / 4 > self->stream_tx_len)) { + buf_sz = self->stream_tx_len * 4; + } + mp_obj_t ret = mp_stream_read(self->dstream_tx, &self->audiobuf_tx[self->pp_ptr], buf_sz); + if (mp_obj_is_integer(ret)) { + self->last_stream_read_sz = mp_obj_get_int(ret); + // decrement length if it was specified + if (self->stream_tx_len > -1) { + if (self->stream_tx_len >= self->last_stream_read_sz / 4) { + self->stream_tx_len -= self->last_stream_read_sz / 4; + } else { + self->stream_tx_len = 0; + } } - nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); + if (self->last_stream_read_sz == 0) { + self->xfer_signal = I2S_SIG_EOF_TX; + } + } else { + // If ret isn't an integer it indicates a stream read error, stop tx: + self->xfer_signal = I2S_SIG_EOF_TX; } // Sync to I2S bus and start sending data: @@ -1238,22 +1249,8 @@ STATIC mp_obj_t pyb_i2s_stream_out(mp_uint_t n_args, const mp_obj_t *pos_args, nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "No I2S bus clock")); } - if (self->is_duplex) { - status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, - self->audiobuf_tx[self->pp_ptr], - self->audiobuf_rx[self->pp_ptr], - buf_sz / 2); - } else { - status = HAL_I2S_Transmit_DMA(&self->i2s, - self->audiobuf_tx[self->pp_ptr], - buf_sz / 2); - } - - if (status != HAL_OK) { - mp_hal_raise(status); - } + i2s_stream_handler(self); } - return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2s_stream_out_obj, 1, pyb_i2s_stream_out); @@ -1274,29 +1271,18 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_obj_t stream = mp_stream_op_supported(args[0].u_obj, MP_STREAM_OP_WRITE); - /* mp_obj_t stream = args[0].u_obj; */ - /* mp_obj_type_t *type = mp_obj_get_type(stream); */ - /* // Check that 'stream' provides 'mp_stream_p_t' and 'write' */ - /* // Note that 'write' will be present even if the stream opened in read-mode (?) */ - /* if (type->stream_p == NULL || type->stream_p->write == NULL) { */ - /* nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, */ - /* "Object type %s not a writeable stream", */ - /* mp_obj_get_type_str(stream))); */ - /* } */ - // make sure that I2S bus is configured for receive: if (!i2s_has_rx(self)) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "I2S(%d) not configured for rx", self->i2s_id)); } + // If I2S state is not 'ready', raise an error _unless_ the current transaction is a - // simplex stream_in(); in that case set signals and stream handle to begin duplex + // simplex stream_out(); in that case set signals and stream handle to begin duplex // stream_in + stream_out with next callback: - if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { if (self->xfer_state != STREAM_OUT) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, @@ -1307,25 +1293,18 @@ STATIC mp_obj_t pyb_i2s_stream_in (mp_uint_t n_args, const mp_obj_t *pos_args, // stream_handler to add stream_in to ongoing stream transaction self->xfer_signal = I2S_SIG_NONE; self->xfer_state |= STREAM_IN; - self->stream_rx_len = args[2].u_int * 4; // n_samples -> n_bytes + self->stream_rx_len = args[2].u_int; self->dstream_rx = stream; } } else { // set up for simplex stream_in self->xfer_signal = I2S_SIG_NONE; self->xfer_state = STREAM_IN; - self->stream_rx_len = args[2].u_int * 4; // n_samples -> n_bytes + self->stream_rx_len = args[2].u_int; int buf_sz = AUDIOBUFFER_BYTES / 4; self->dstream_rx = stream; self->pp_ptr = 0; - // TODO: This code is copied in HAL callback function, it should probably be - // factored out as a more sophisticated state check: - if (HAL_I2S_GetState(&self->i2s) != HAL_I2S_STATE_READY) { - HAL_I2S_DMAStop(&self->i2s); - //printf("I2S transfer stop and restart\n"); // DEBUG - } - // NOTE: stream_in is not the simple switching of rx/tx and read/write methods // from stream_out - the order of operations also must be reversed; the receive // must fill the buffer _before_ the buffer is written out a stream, and data @@ -1367,7 +1346,6 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { // i2s_stream_handler manages HAL_I2S_*_DMA functions and data transfers // between streams and buffers: int buf_sz = AUDIOBUFFER_BYTES / 4; - // int error; HAL_StatusTypeDef status; // Set xfer_state as indicated by xfer_signal: @@ -1391,7 +1369,7 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { } self->xfer_signal = I2S_SIG_NONE; } - // If I2S_SIG_EOF_* then invoke user-defined callback + // If sig == I2S_SIG_EOF_* then invoke user-defined callback if (sig > I2S_SIG_STOP_EOF_DIV) { i2s_handle_mp_callback(self); } @@ -1401,10 +1379,16 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { // xfer_state indicates active stream transfer self->pp_ptr = !(self->pp_ptr); if (self->is_duplex) { + int sz; + if (self->xfer_state == STREAM_IN) { + sz = buf_sz / 2; + } else { + sz = self->last_stream_read_sz / 2; + } status = HAL_I2SEx_TransmitReceive_DMA(&self->i2s, self->audiobuf_tx[self->pp_ptr], self->audiobuf_rx[self->pp_ptr], - self->last_stream_read_sz / 2); + sz); } else if (self->xfer_state == STREAM_OUT) { status = HAL_I2S_Transmit_DMA(&self->i2s, @@ -1415,7 +1399,7 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { // TODO - Simplex receive should not use last read or write size! status = HAL_I2S_Receive_DMA(&self->i2s, self->audiobuf_rx[self->pp_ptr], - self->last_stream_read_sz / 2); + buf_sz / 2); } else { // TODO: clean up and raise an error? @@ -1435,60 +1419,49 @@ STATIC void i2s_stream_handler(pyb_i2s_obj_t *self) { bool buf_ptr = !(self->pp_ptr); if ((self->xfer_state & STREAM_OUT) != 0) { - /* self->out_sz = self->dstream_tx->type->stream_p->read(self->dstream_tx, */ - /* &self->audiobuf_tx[buf_ptr], */ - /* buf_sz, &error); */ - - /* if (self->stream_tx_len > -1) { */ - /* if (self->stream_tx_len >= self->out_sz) { */ - /* self->stream_tx_len -= self->out_sz; */ - /* } else { */ - /* self->stream_tx_len = 0; */ - /* } */ - /* } */ - /* // self->out_sz == 0 means dstream_tx is empty, so set xfer_signal to */ - /* // indicate end of file */ - /* if (self->out_sz == 0 || self->stream_tx_len == 0) { */ - /* self->xfer_signal = I2S_SIG_EOF_TX; */ - /* } else if (self->out_sz == MP_STREAM_ERROR) { */ - /* nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(error))); */ - /* } */ + if ((self->stream_tx_len > -1) && (buf_sz / 4 > self->stream_tx_len)) { + buf_sz = self->stream_tx_len * 4; + } mp_obj_t ret = mp_stream_read(self->dstream_tx, &self->audiobuf_tx[buf_ptr], buf_sz); if (mp_obj_is_integer(ret)) { - // maybe now this should be self->out_sz = mp_obj_get_int(ret) - // or maybe need a last_read_sz and last_write_sz? self->last_stream_read_sz = mp_obj_get_int(ret); + // decrement tx length if it was specified: if (self->stream_tx_len > -1) { - if (self->stream_tx_len >= self->last_stream_read_sz) { - self->stream_tx_len -= self->last_stream_read_sz; + if (self->stream_tx_len >= self->last_stream_read_sz / 4) { + self->stream_tx_len -= self->last_stream_read_sz / 4; } else { self->stream_tx_len = 0; } } - if (self->last_stream_read_sz < buf_sz || self->stream_tx_len == 0) { - // A read smaller than buf_sz is because we are at EOF: + if (self->last_stream_read_sz == 0) { + // A read of 0 length happens when at the end of the file stream or because + // self->stream_tx_len == 0, either way set TX signal to EOF: self->xfer_signal = I2S_SIG_EOF_TX; } } else { // If ret isn't an integer it indicates a stream read error, stop tx: self->xfer_signal = I2S_SIG_EOF_TX; } - } if ((self->xfer_state & STREAM_IN) != 0) { - // Need to figure out correct way to handle errors and end transfer for stream_in + buf_sz = AUDIOBUFFER_BYTES / 4; + if ((self->stream_rx_len > -1) && (buf_sz / 4 > self->stream_rx_len)) { + buf_sz = self->stream_rx_len * 4; + } mp_obj_t ret = mp_stream_write(self->dstream_rx, &self->audiobuf_rx[buf_ptr], buf_sz); if (mp_obj_is_integer(ret)) { self->last_stream_write_sz = mp_obj_get_int(ret); + // decrement rx length if it was specified: if (self->stream_rx_len > -1) { - if (self->stream_rx_len >= self->last_stream_write_sz) { - self->stream_rx_len -= self->last_stream_write_sz; + if (self->stream_rx_len >= self->last_stream_write_sz / 4) { + self->stream_rx_len -= self->last_stream_write_sz / 4; } else { self->stream_rx_len = 0; } } - if (self->last_stream_write_sz < buf_sz || self->stream_rx_len == 0) { - // A write smaller than buf_sz is probably because the filesystem is full: + if (self->last_stream_write_sz == 0) { + // A write of 0 length happens when the filesystem is full or when + // self->stream_rx_len == 0, either way set RX signal to EOF: self->xfer_signal = I2S_SIG_EOF_RX; } } else { From cd5556909883e8083a770c22f72b7908a430d95d Mon Sep 17 00:00:00 2001 From: blmorris Date: Tue, 1 Dec 2015 16:23:39 -0500 Subject: [PATCH 33/33] Enable Hardfault handler output for debugging --- stmhal/boards/PYBV10/mpconfigboard.h | 6 +++++- stmhal/stm32_it.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/stmhal/boards/PYBV10/mpconfigboard.h b/stmhal/boards/PYBV10/mpconfigboard.h index 2281064a61a75..6010f220bf49e 100644 --- a/stmhal/boards/PYBV10/mpconfigboard.h +++ b/stmhal/boards/PYBV10/mpconfigboard.h @@ -17,7 +17,7 @@ #define MICROPY_HW_ENABLE_SPI3 (0) #define MICROPY_HW_ENABLE_CAN (1) #define MICROPY_HW_ENABLE_I2S2 (1) -#define MICROPY_HW_ENABLE_I2S3 (1) +#define MICROPY_HW_ENABLE_I2S3 (0) // HSE is 8MHz #define MICROPY_HW_CLK_PLLM (8) @@ -94,3 +94,7 @@ // MMA accelerometer config #define MICROPY_HW_MMA_AVDD_PIN (pin_B5) + +// Add hardware UART REPL for debugging +#define MICROPY_HW_UART_REPL PYB_UART_4 +#define MICROPY_HW_UART_REPL_BAUD 115200 diff --git a/stmhal/stm32_it.c b/stmhal/stm32_it.c index 5f96c6083b928..e0eb50ce592b3 100644 --- a/stmhal/stm32_it.c +++ b/stmhal/stm32_it.c @@ -89,7 +89,7 @@ extern PCD_HandleTypeDef pcd_handle; // Set the following to 1 to get some more information on the Hard Fault // More information about decoding the fault registers can be found here: // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0646a/Cihdjcfc.html -#define REPORT_HARD_FAULT_REGS 0 +#define REPORT_HARD_FAULT_REGS 1 #if REPORT_HARD_FAULT_REGS