283 lines
7.4 KiB
C
283 lines
7.4 KiB
C
#include "ch.h"
|
|
#include "hal.h"
|
|
#include "chprintf.h"
|
|
#include "stm32f4xx.h"
|
|
|
|
#include "sound.h"
|
|
#include "i2s_bits.h"
|
|
#include "cs43l22_pins.h"
|
|
#include "cs43l22_commands.h"
|
|
|
|
|
|
/* I2C driver configuration */
|
|
static const I2CConfig _i2s_i2c_config = {
|
|
OPMODE_I2C , // Operation mode
|
|
I2S_OI2C_SPEED , // Clock frequency
|
|
FAST_DUTY_CYCLE_2 // Duty cycle
|
|
};
|
|
|
|
|
|
/* Initialise one of the I2C pads */
|
|
#define _i2s_init_i2c_pad(port,pad) \
|
|
palSetPadMode( port , pad , \
|
|
PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_OSPEED_MID2 \
|
|
| PAL_MODE_ALTERNATE(4) )
|
|
|
|
/* Initialise an I2S pad */
|
|
#define _i2s_init_i2s_pad(port,pad) \
|
|
palSetPadMode( port , pad , \
|
|
PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_MID2 \
|
|
| PAL_STM32_ALTERNATE( I2S_OUT_FUNCTION ) )
|
|
|
|
|
|
/* Currently playing? */
|
|
static int _snd_playing;
|
|
/* May write next buffer? */
|
|
static int _snd_next_ok;
|
|
/* Last initialised buffer */
|
|
static int _snd_init_buffer;
|
|
/* Buffers */
|
|
static s16 _snd_buffers[ 2 * SND_BUFSIZE ];
|
|
|
|
/* Compute a buffer's address */
|
|
#define _snd_buffer_address(idx) \
|
|
(& _snd_buffers[ (idx) * SND_BUFSIZE ] )
|
|
|
|
|
|
#ifdef USE_DMA_FOR_SEXY_BEEPS
|
|
/* SPI transfer callback, used when buffers need to be rotated */
|
|
static void _cs43l22_spi_callback( SPIDriver * driver , u32 flags )
|
|
{
|
|
if ( driver != &SPID3 || !( flags & STM32_DMA_ISR_TCIF ) ) {
|
|
return;
|
|
}
|
|
extern u32 isrFlags;
|
|
isrFlags = flags;
|
|
_snd_next_ok = TRUE;
|
|
}
|
|
#endif // USE_DMA_FOR_SEXY_BEEPS
|
|
|
|
|
|
|
|
/*
|
|
* Initialise GPIO ports to handle:
|
|
* - the CS43L22's I2C channel,
|
|
* - the CS43L22's I2S channel.
|
|
*/
|
|
static void _i2s_init_gpio( void )
|
|
{
|
|
// Reset pin
|
|
palSetPadMode( I2S_ORESET_PORT , I2S_ORESET_PAD ,
|
|
PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_MID2 );
|
|
|
|
// SDL/SDA pins
|
|
_i2s_init_i2c_pad( I2C_OSCL_PORT , I2C_OSCL_PAD );
|
|
_i2s_init_i2c_pad( I2C_OSDA_PORT , I2C_OSDA_PAD );
|
|
|
|
// I2S WS/MCK/SCK/SD pins
|
|
_i2s_init_i2s_pad( I2S_OWS_PORT , I2S_OWS_PAD );
|
|
_i2s_init_i2s_pad( I2S_OMCK_PORT , I2S_OMCK_PAD );
|
|
_i2s_init_i2s_pad( I2S_OSCK_PORT , I2S_OSCK_PAD );
|
|
_i2s_init_i2s_pad( I2S_OSD_PORT , I2S_OSD_PAD );
|
|
}
|
|
|
|
|
|
/* Reset the CS43L22 */
|
|
static void _i2s_reset_output( void )
|
|
{
|
|
palClearPad( I2S_ORESET_PORT , I2S_ORESET_PAD );
|
|
chThdSleep( I2S_ORESET_DELAY );
|
|
palSetPad( I2S_ORESET_PORT , I2S_ORESET_PAD );
|
|
chThdSleep( I2S_ORESET_DELAY );
|
|
}
|
|
|
|
|
|
/* Send a command to the CS43L22 through I2C */
|
|
static msg_t _cs43l22_set( u8 reg , u8 value )
|
|
{
|
|
u8 txBuffer[ 2 ];
|
|
txBuffer[0] = reg;
|
|
txBuffer[1] = value;
|
|
msg_t rv = i2cMasterTransmitTimeout(
|
|
& I2S_OI2C_DRIVER , I2S_OI2C_ADDRESS ,
|
|
txBuffer , 2 ,
|
|
NULL , 0 ,
|
|
I2S_OI2C_TIMEOUT );
|
|
if ( rv ) {
|
|
chprintf( (void*)&SD2 ,
|
|
"I2C 0x%0.2x <- 0x%0.2x ERROR %d\r\n" ,
|
|
reg , value , rv );
|
|
chprintf( (void*)&SD2 ,
|
|
" status = 0x%x\r\n" ,
|
|
i2cGetErrors( & I2S_OI2C_DRIVER ) );
|
|
} else {
|
|
chprintf( (void*)&SD2 ,
|
|
"I2C 0x%0.2x <- 0x%0.2x OK\r\n" ,
|
|
reg , value );
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
/* Get a register from the CS43L22 through I2C */
|
|
static u8 _cs43l22_get( u8 reg )
|
|
{
|
|
u8 data;
|
|
msg_t rv = i2cMasterTransmitTimeout(
|
|
& I2S_OI2C_DRIVER , I2S_OI2C_ADDRESS ,
|
|
® , 1 ,
|
|
&data , 1 ,
|
|
I2S_OI2C_TIMEOUT );
|
|
if ( rv ) {
|
|
chprintf( (void*)&SD2 ,
|
|
"I2C 0x%0.2x >- ??? ERROR %d\r\n" ,
|
|
reg , rv );
|
|
chprintf( (void*)&SD2 ,
|
|
" status = 0x%x\r\n" ,
|
|
i2cGetErrors( & I2S_OI2C_DRIVER ) );
|
|
}
|
|
return data;
|
|
}
|
|
|
|
|
|
/* Initialise the CS43L22 through its I2C interface */
|
|
static void _cs43l22_init( void )
|
|
{
|
|
i2cStart( &( I2S_OI2C_DRIVER ) , &_i2s_i2c_config );
|
|
|
|
// Make sure the device is powered down
|
|
_cs43l22_set( CS43L22_REG_PWR_CTL1 , CS43L22_PWR1_DOWN );
|
|
// Activate headphone channels
|
|
_cs43l22_set( CS43L22_REG_PWR_CTL2 ,
|
|
CS43L22_PWR2_SPKA_OFF | CS43L22_PWR2_SPKB_OFF
|
|
| CS43L22_PWR2_HDA_ON | CS43L22_PWR2_HDB_ON );
|
|
// Set serial clock
|
|
_cs43l22_set( CS43L22_REG_CLOCK_CTL , CS43L22_CLK_AUTO_ON
|
|
| CS43L22_CLK_MCDIV_ON );
|
|
// Set input data format
|
|
_cs43l22_set( CS43L22_REG_INT_CTL1 , CS43L22_IC1_SLAVE
|
|
| CS43L22_IC1_SCPOL_OFF | CS43L22_IC1_DSP_OFF
|
|
| CS43L22_IC1_DIF_I2S | CS43L22_IC1_AWL_32 );
|
|
// Fire it up
|
|
_cs43l22_set( CS43L22_REG_PWR_CTL1 , CS43L22_PWR1_UP );
|
|
// Analog soft ramp/zero cross disabled
|
|
_cs43l22_set( CS43L22_REG_AZCSR ,
|
|
CS43L22_AZCSR_SRB_OFF | CS43L22_AZCSR_SRA_OFF
|
|
| CS43L22_AZCSR_ZCB_OFF | CS43L22_AZCSR_ZCA_OFF );
|
|
// Digital soft ramp disabled
|
|
_cs43l22_set( CS43L22_REG_MISC_CTL , CS43L22_MISC_DEEMPHASIS_ON );
|
|
// Limiter: no soft ramp/zero cross, no attack level
|
|
_cs43l22_set( CS43L22_REG_LIM_CTL1 , CS43L22_LIM1_SRD_OFF
|
|
| CS43L22_LIM1_ZCD_OFF );
|
|
// Initial volume and tone controls
|
|
_cs43l22_set( CS43L22_REG_TONE_CTL , 0xf );
|
|
_cs43l22_set( CS43L22_REG_PCM_A , 0x00 );
|
|
_cs43l22_set( CS43L22_REG_PCM_B , 0x00 );
|
|
sndOutputVolume( 200 );
|
|
}
|
|
|
|
|
|
/* Initialise the I2S interface to the CS43L22 */
|
|
static void _cs43l22_init_i2s( u32 frequency )
|
|
{
|
|
rccEnableSPI3(FALSE);
|
|
|
|
u32 plln = ( RCC->PLLI2SCFGR & RCC_PLLI2SCFGR_PLLI2SN ) >> 6;
|
|
u32 pllr = ( RCC->PLLI2SCFGR & RCC_PLLI2SCFGR_PLLI2SR ) >> 28;
|
|
u32 pllm = (u32)( RCC->PLLCFGR & RCC_PLLCFGR_PLLM );
|
|
u32 clock = (u32)( ( ( HSE_VALUE / pllm ) * plln ) / pllr );
|
|
chprintf( (void*)&SD2 , "PLL REG=0x%.x N=%d R=%d M=%d CLK = %d\r\n" ,
|
|
RCC->PLLI2SCFGR , plln , pllr , pllm , clock );
|
|
|
|
u16 frq = (u16)(( ( ( clock / 256 ) * 10 ) / frequency ) + 5 ) / 10;
|
|
chprintf( (void*)&SD2 , "frq base = 0x%x\r\n" , frq );
|
|
if ( ( frq & 1 ) == 0 ) {
|
|
frq = frq >> 1;
|
|
} else {
|
|
frq = STM32F4_I2S_PR_ODD | ( frq >> 1 );
|
|
}
|
|
chprintf( (void*)&SD2 , "frq = 0x%x\r\n" , frq );
|
|
|
|
*((u32*)(SPI3_BASE + STM32F4_I2S_OFFS_CFG)) =
|
|
STM32F4_I2S_CFG_MODE_I2S
|
|
| STM32F4_I2S_CFG_STD_I2S
|
|
| STM32F4_I2S_CFG_CFG_MS_TX
|
|
| STM32F4_I2S_CFG_RCPOL_OFF
|
|
| STM32F4_I2S_CFG_DLEN_16
|
|
| STM32F4_I2S_CFG_CLEN_16;
|
|
*((u32*)(SPI3_BASE + STM32F4_I2S_OFFS_PR)) = frq
|
|
| STM32F4_I2S_PR_MCLK_ON;
|
|
*((u32*)(SPI3_BASE + STM32F4_I2S_OFFS_CFG)) |= STM32F4_I2S_CFG_ENABLED;
|
|
}
|
|
|
|
#ifdef USE_DMA_FOR_SEXY_BEEPS
|
|
static void _cs43l22_init_dma( void )
|
|
{
|
|
dmaStreamAllocate( SPID3.dmatx , 1 ,
|
|
(stm32_dmaisr_t) _cs43l22_spi_callback ,
|
|
&SPID3 );
|
|
SPID3.txdmamode |= ( STM32_DMA_CR_PSIZE_HWORD | 0x30000
|
|
| STM32_DMA_CR_MINC | STM32_DMA_CR_MSIZE_HWORD
|
|
| STM32_DMA_CR_DBM | STM32_DMA_CR_TCIE );
|
|
dmaStreamSetMode( SPID3.dmatx , SPID3.txdmamode );
|
|
dmaStreamSetFIFO( SPID3.dmatx , STM32_DMA_FCR_DMDIS
|
|
| STM32_DMA_FCR_FTH_HALF );
|
|
dmaStreamSetPeripheral( SPID3.dmatx , &( SPID3.spi->DR ) );
|
|
SPID3.spi->CR2 |= SPI_CR2_TXDMAEN;
|
|
}
|
|
#endif // USE_DMA_FOR_SEXY_BEEPS
|
|
|
|
|
|
void sndInit( u32 frequency )
|
|
{
|
|
_i2s_init_gpio( );
|
|
_i2s_reset_output( );
|
|
_cs43l22_init( );
|
|
_cs43l22_init_i2s( frequency );
|
|
#ifdef USE_DMA_FOR_SEXY_BEEPS
|
|
_cs43l22_init_dma( );
|
|
#endif // USE_DMA_FOR_SEXY_BEEPS
|
|
_snd_next_ok = TRUE;
|
|
}
|
|
|
|
|
|
void sndOutputVolume( u8 volume )
|
|
{
|
|
if ( volume > 0xe6 ) {
|
|
volume -= 0xe7;
|
|
} else {
|
|
volume += 0x19;
|
|
}
|
|
_cs43l22_set( CS43L22_REG_MASTER_VOLUME_A , volume );
|
|
_cs43l22_set( CS43L22_REG_MASTER_VOLUME_B , volume );
|
|
}
|
|
|
|
s16 * sndGetBuffer( void )
|
|
{
|
|
if ( ! _snd_next_ok ) {
|
|
return NULL;
|
|
}
|
|
|
|
int next_write;
|
|
if ( _snd_playing ) {
|
|
next_write = ( SPID3.dmatx->stream->CR & 0x80000 )
|
|
? 0 : 1;
|
|
_snd_next_ok = FALSE;
|
|
} else if ( _snd_init_buffer == 0 ) {
|
|
next_write = _snd_init_buffer++;
|
|
} else {
|
|
next_write = _snd_init_buffer++;
|
|
_snd_playing = TRUE;
|
|
_snd_next_ok = FALSE;
|
|
dmaStreamSetMemory0( SPID3.dmatx , _snd_buffer_address(0) );
|
|
dmaStreamSetMemory1( SPID3.dmatx , _snd_buffer_address(1) );
|
|
dmaStreamSetTransactionSize( SPID3.dmatx , SND_BUFSIZE );
|
|
dmaStreamEnable( SPID3.dmatx );
|
|
}
|
|
return _snd_buffer_address( next_write );
|
|
}
|
|
|
|
u8 sndGetStatus( void )
|
|
{
|
|
return _cs43l22_get( CS43L22_REG_STATUS );
|
|
}
|