mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-27 00:09:36 -05:00
a22dc150bc
Also address GCC 6.2 not allowing constexpr from reinterpret_cast<> values.
245 lines
4.5 KiB
C++
245 lines
4.5 KiB
C++
/*
|
|
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
|
*
|
|
* This file is part of PortaPack.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifndef __I2S_H__
|
|
#define __I2S_H__
|
|
|
|
#include "hal.h"
|
|
|
|
#include "utility.hpp"
|
|
|
|
namespace lpc43xx {
|
|
namespace i2s {
|
|
|
|
enum class WordWidth {
|
|
Bits8 = 0x0,
|
|
Bits16 = 0x1,
|
|
Bits32 = 0x3,
|
|
};
|
|
|
|
enum class ClockSelect {
|
|
FractionalDivider = 0x0,
|
|
BaseAudioClkOrExternalMCLK = 0x01,
|
|
OtherMCLK = 0x2,
|
|
};
|
|
|
|
struct DAO {
|
|
WordWidth wordwidth;
|
|
uint32_t mono;
|
|
uint32_t stop;
|
|
uint32_t reset;
|
|
uint32_t ws_sel;
|
|
uint32_t ws_halfperiod;
|
|
uint32_t mute;
|
|
|
|
constexpr operator uint32_t() const {
|
|
return
|
|
((toUType(wordwidth) & 3) << 0)
|
|
| ((mono & 1) << 2)
|
|
| ((stop & 1) << 3)
|
|
| ((reset & 1) << 4)
|
|
| ((ws_sel & 1) << 5)
|
|
| ((ws_halfperiod & 0x1ff) << 6)
|
|
| ((mute & 1) << 15)
|
|
;
|
|
}
|
|
};
|
|
|
|
struct DAI {
|
|
WordWidth wordwidth;
|
|
uint32_t mono;
|
|
uint32_t stop;
|
|
uint32_t reset;
|
|
uint32_t ws_sel;
|
|
uint32_t ws_halfperiod;
|
|
|
|
constexpr operator uint32_t() const {
|
|
return
|
|
((toUType(wordwidth) & 3) << 0)
|
|
| ((mono & 1) << 2)
|
|
| ((stop & 1) << 3)
|
|
| ((reset & 1) << 4)
|
|
| ((ws_sel & 1) << 5)
|
|
| ((ws_halfperiod & 0x1ff) << 6)
|
|
;
|
|
}
|
|
};
|
|
|
|
struct MCLKRate {
|
|
uint32_t x_divider;
|
|
uint32_t y_divider;
|
|
|
|
constexpr operator uint32_t() const {
|
|
return
|
|
((y_divider & 0xff) << 0)
|
|
| ((x_divider & 0xff) << 8)
|
|
;
|
|
}
|
|
};
|
|
|
|
struct BitRate {
|
|
uint32_t bitrate;
|
|
|
|
constexpr operator uint32_t() const {
|
|
return ((bitrate & 0x3f) << 0);
|
|
}
|
|
};
|
|
|
|
struct Mode {
|
|
ClockSelect clksel;
|
|
uint32_t four_pin;
|
|
uint32_t mclk_out_en;
|
|
|
|
constexpr operator uint32_t() const {
|
|
return
|
|
((toUType(clksel) & 3) << 0)
|
|
| ((four_pin & 1) << 2)
|
|
| ((mclk_out_en & 1) << 3)
|
|
;
|
|
}
|
|
};
|
|
|
|
struct DMA {
|
|
uint32_t rx_enable;
|
|
uint32_t tx_enable;
|
|
size_t rx_depth;
|
|
size_t tx_depth;
|
|
|
|
constexpr operator uint32_t() const {
|
|
return
|
|
((rx_enable & 1) << 0)
|
|
| ((tx_enable & 1) << 1)
|
|
| ((rx_depth & 0xf) << 8)
|
|
| ((tx_depth & 0xf) << 16)
|
|
;
|
|
}
|
|
};
|
|
|
|
struct ConfigTX {
|
|
uint32_t dao;
|
|
uint32_t txrate;
|
|
uint32_t txbitrate;
|
|
uint32_t txmode;
|
|
};
|
|
|
|
struct ConfigRX {
|
|
uint32_t dai;
|
|
uint32_t rxrate;
|
|
uint32_t rxbitrate;
|
|
uint32_t rxmode;
|
|
};
|
|
|
|
struct ConfigDMA {
|
|
uint32_t dma1;
|
|
uint32_t dma2;
|
|
};
|
|
|
|
template<uint32_t BaseAddress>
|
|
class I2S {
|
|
public:
|
|
static void configure(
|
|
const ConfigTX& config_tx,
|
|
const ConfigRX& config_rx
|
|
) {
|
|
reset();
|
|
|
|
/* I2S operates in master mode, use PLL0AUDIO as MCLK source for TX. */
|
|
/* NOTE: Documentation of CREG6 is quite confusing. Refer to "I2S clocking and
|
|
* pin connections" and other I2S diagrams for more clarity.
|
|
*/
|
|
if( &p() == LPC_I2S0 ) {
|
|
LPC_CREG->CREG6 |=
|
|
(1U << 12)
|
|
| (1U << 13)
|
|
;
|
|
}
|
|
if( &p() == LPC_I2S1 ) {
|
|
LPC_CREG->CREG6 |=
|
|
(1U << 14)
|
|
| (1U << 15)
|
|
;
|
|
}
|
|
|
|
p().DAO = config_tx.dao;
|
|
p().TXRATE = config_tx.txrate;
|
|
p().TXBITRATE = config_tx.txbitrate;
|
|
p().TXMODE = config_tx.txmode;
|
|
|
|
p().DAI = config_rx.dai;
|
|
p().RXRATE = config_rx.rxrate;
|
|
p().RXBITRATE = config_rx.rxbitrate;
|
|
p().RXMODE = config_rx.rxmode;
|
|
}
|
|
|
|
static void configure(
|
|
const ConfigTX& config_tx,
|
|
const ConfigRX& config_rx,
|
|
const ConfigDMA& config_dma
|
|
) {
|
|
configure(config_tx, config_rx);
|
|
|
|
p().DMA1 = config_dma.dma1;
|
|
p().DMA2 = config_dma.dma2;
|
|
}
|
|
|
|
static void rx_start() {
|
|
p().DAI &= ~(1U << 3);
|
|
}
|
|
|
|
static void rx_stop() {
|
|
p().DAI |= (1U << 3);
|
|
}
|
|
|
|
static void tx_start() {
|
|
p().DAO &= ~(1U << 3);
|
|
}
|
|
|
|
static void tx_stop() {
|
|
p().DAO |= (1U << 3);
|
|
}
|
|
|
|
static void tx_mute() {
|
|
p().DAO |= (1U << 15);
|
|
}
|
|
|
|
static void tx_unmute() {
|
|
p().DAO &= ~(1U << 15);
|
|
}
|
|
|
|
private:
|
|
static void reset() {
|
|
p().DAO |= (1U << 4);
|
|
p().DAI |= (1U << 4);
|
|
}
|
|
|
|
static LPC_I2S_Type& p() {
|
|
return *reinterpret_cast<LPC_I2S_Type*>(BaseAddress);
|
|
}
|
|
};
|
|
|
|
using i2s0 = I2S<LPC_I2S0_BASE>;
|
|
using i2s1 = I2S<LPC_I2S1_BASE>;
|
|
|
|
} /* namespace i2s */
|
|
} /* namespace lpc43xx */
|
|
|
|
#endif/*__I2S_H__*/
|