UHSDR/UHSDR-active-devel/mchf-eclipse/drivers/audio/codec/uhsdr_hw_i2s.c
2022-11-08 16:13:55 +01:00

251 lines
6.5 KiB
C

/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
// Common
#include "uhsdr_board_config.h"
#include "uhsdr_board.h"
#include "profiling.h"
#include "uhsdr_hw_i2s.h"
#include "audio_driver.h"
#ifdef UI_BRD_MCHF
#include "i2s.h"
#endif
#ifdef UI_BRD_OVI40
#include "sai.h"
#endif
typedef struct
{
IqSample_t out[2*IQ_BLOCK_SIZE];
IqSample_t in[2*IQ_BLOCK_SIZE];
} dma_iq_buffer_t;
typedef struct
{
AudioSample_t out[2*AUDIO_BLOCK_SIZE];
AudioSample_t in[2*AUDIO_BLOCK_SIZE];
} dma_audio_buffer_t;
// we do something tricky here:
// if we have a single codec both buffers are in fact the same, so we use a union
// if we have two codecs we use a struct hence two separate buffers
typedef
#if CODEC_NUM == 1
union
#else
struct
#endif
{
dma_iq_buffer_t iq_buf;
dma_audio_buffer_t audio_buf;
} I2S_DmaBuffers_t;
static __UHSDR_DMAMEM I2S_DmaBuffers_t dma;
void UhsdrHwI2s_Codec_ClearTxDmaBuffer()
{
memset((void*)&dma.iq_buf.out, 0, sizeof(dma.iq_buf.out));
}
// #define PROFILE_APP
static void MchfHw_Codec_HandleBlock(uint16_t which)
{
#ifdef PROFILE_EVENTS
// we stop during interrupt
// at the end we start again
#ifdef PROFILE_APP
profileCycleCount_stop();
#else
profileTimedEventStart(ProfileAudioInterrupt);
#endif
#endif
#ifdef EXEC_PROFILING
// Profiling pin (high level)
GPIOE->BSRRL = GPIO_Pin_10;
#endif
ts.audio_int_counter++; // generating a time base for encoder handling
// Transfer complete interrupt
// Point to 2nd half of buffers
const size_t sz = IQ_BLOCK_SIZE;
const uint16_t offset = which == 0?sz:0;
AudioSample_t *audio;
IqSample_t *iq;
if (ts.txrx_mode != TRX_MODE_TX)
{
iq = &dma.iq_buf.in[offset];
audio = &dma.audio_buf.out[offset];
}
else
{
audio = &dma.audio_buf.in[offset];
iq = &dma.iq_buf.out[offset];
}
AudioSample_t *audioDst = &dma.audio_buf.out[offset];
// Handle
AudioDriver_I2SCallback(audio, iq, audioDst, sz);
#ifdef EXEC_PROFILING
// Profiling pin (low level)
GPIOE->BSRRH = GPIO_Pin_10;
#endif
#ifdef PROFILE_EVENTS
// we stopped during interrupt
// now we start again
#ifdef PROFILE_APP
profileCycleCount_start();
#else
profileTimedEventStop(ProfileAudioInterrupt);
#endif
#endif
}
#ifdef UI_BRD_MCHF
/**
* @brief HAL Handler for Codec DMA Interrupt
*/
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
MchfHw_Codec_HandleBlock(0);
}
/**
* @brief HAL Handler for Codec DMA Interrupt
*/
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
MchfHw_Codec_HandleBlock(1);
}
#endif
#ifdef UI_BRD_OVI40
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hi2s)
{
if (hi2s == &hsai_BlockA2)
{
MchfHw_Codec_HandleBlock(0);
}
}
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hi2s)
{
if (hi2s == &hsai_BlockA2)
{
MchfHw_Codec_HandleBlock(1);
}
}
#endif
#if defined(UI_BRD_OVI40) && defined(USE_32_IQ_BITS) && defined(USE_32_AUDIO_BITS)
static void UhsdrHWI2s_Sai32Bits(SAI_HandleTypeDef* hsai)
{
hsai->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hsai->hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
HAL_DMA_Init(hsai->hdmarx);
HAL_SAI_InitProtocol(hsai, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2);
}
static void UhsdrHWI2s_Sai24Bits(SAI_HandleTypeDef* hsai)
{
hsai->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hsai->hdmarx->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
HAL_DMA_Init(hsai->hdmarx);
HAL_SAI_InitProtocol(hsai, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_24BIT, 2);
}
#endif
static void UhsdrHwI2s_SetBitWidth(void)
{
#if defined(USE_32_IQ_BITS)
#if defined(UI_BRD_MCHF)
hi2s3.Init.DataFormat = I2S_DATAFORMAT_32B;
HAL_I2S_Init(&hi2s3);
#endif
#if defined(UI_BRD_OVI40)
if(ts.codecCS4270_present)
{
UhsdrHWI2s_Sai24Bits(&hsai_BlockA2);
UhsdrHWI2s_Sai24Bits(&hsai_BlockB2);
}
else
{
UhsdrHWI2s_Sai32Bits(&hsai_BlockA2);
UhsdrHWI2s_Sai32Bits(&hsai_BlockB2);
}
#endif
#endif
#if defined(USE_32_AUDIO_BITS)
#if defined(UI_BRD_OVI40)
UhsdrHWI2s_Sai32Bits(&hsai_BlockA1);
UhsdrHWI2s_Sai32Bits(&hsai_BlockB1);
#endif
#endif
}
void UhsdrHwI2s_Codec_StartDMA()
{
UhsdrHwI2s_SetBitWidth();
#ifdef UI_BRD_MCHF
HAL_I2SEx_TransmitReceive_DMA(&hi2s3,(uint16_t*)dma.iq_buf.out,(uint16_t*)dma.iq_buf.in,sizeof(dma.iq_buf.in)/sizeof(dma.iq_buf.in[0].l));
#endif
#ifdef UI_BRD_OVI40
// we clean the buffers since we don't know if we are in a "cleaned" memory segement
memset((void*)&dma.audio_buf,0,sizeof(dma.audio_buf));
memset((void*)&dma.iq_buf,0,sizeof(dma.iq_buf));
HAL_SAI_Receive_DMA(&hsai_BlockA1,(uint8_t*)dma.audio_buf.in,sizeof(dma.audio_buf.in)/sizeof(dma.audio_buf.in[0].l));
HAL_SAI_Transmit_DMA(&hsai_BlockB1,(uint8_t*)dma.audio_buf.out,sizeof(dma.audio_buf.out)/sizeof(dma.audio_buf.out[0].l));
HAL_SAI_Receive_DMA(&hsai_BlockA2,(uint8_t*)dma.iq_buf.in,sizeof(dma.iq_buf.in)/sizeof(dma.iq_buf.in[0].l));
HAL_SAI_Transmit_DMA(&hsai_BlockB2,(uint8_t*)dma.iq_buf.out,sizeof(dma.iq_buf.out)/sizeof(dma.iq_buf.out[0].l));
#endif
}
void UhsdrHwI2s_Codec_StopDMA(void)
{
#ifdef UI_BRD_MCHF
HAL_I2S_DMAStop(&hi2s3);
#endif
#ifdef UI_BRD_OVI40
HAL_SAI_DMAStop(&hsai_BlockA1);
HAL_SAI_DMAStop(&hsai_BlockB1);
HAL_SAI_DMAStop(&hsai_BlockA2);
HAL_SAI_DMAStop(&hsai_BlockB2);
#endif
}