kopia lustrzana https://github.com/UU5JPP/Wolf-LITE
267 wiersze
9.0 KiB
C
267 wiersze
9.0 KiB
C
#include "stm32f4xx_hal.h"
|
|
#include "wm8731.h"
|
|
#include "trx_manager.h"
|
|
#include "i2c.h"
|
|
#include "lcd.h"
|
|
#include "agc.h"
|
|
#include "usbd_audio_if.h"
|
|
|
|
//Public variables
|
|
uint32_t WM8731_DMA_samples = 0; // count the number of samples passed to the audio codec
|
|
bool WM8731_DMA_state = true; // what part of the buffer we are working with, true - compleate; false - half
|
|
bool WM8731_Buffer_underrun = false; // lack of data in the buffer from the audio processor
|
|
IRAM1 int32_t CODEC_Audio_Buffer_RX[CODEC_AUDIO_BUFFER_SIZE] = {0}; // audio codec ring buffers
|
|
IRAM1 int32_t CODEC_Audio_Buffer_TX[CODEC_AUDIO_BUFFER_SIZE] = {0};
|
|
bool WM8731_Beeping; //Beeping flag
|
|
bool WM8731_Muting; //Muting flag
|
|
|
|
//Private variables
|
|
|
|
//Prototypes
|
|
static uint8_t WM8731_SendI2CCommand(uint8_t reg, uint8_t value); // send I2C command to codec
|
|
static HAL_StatusTypeDef HAL_I2S_TXRX_DMA(I2S_HandleTypeDef *hi2s, uint16_t *txData, uint16_t *rxData, uint16_t txSize, uint16_t rxSize); // Full-duplex implementation of I2S startup
|
|
static void I2SEx_Fix(I2S_HandleTypeDef *hi2s);
|
|
|
|
// start the I2S bus
|
|
void WM8731_start_i2s_and_dma(void)
|
|
{
|
|
WM8731_CleanBuffer();
|
|
if (HAL_I2S_GetState(&hi2s3) == HAL_I2S_STATE_READY)
|
|
HAL_I2S_TXRX_DMA(&hi2s3, (uint16_t *)&CODEC_Audio_Buffer_RX[0], (uint16_t *)&CODEC_Audio_Buffer_TX[0], CODEC_AUDIO_BUFFER_SIZE * 2, CODEC_AUDIO_BUFFER_SIZE); // 32bit rx spi, 16bit tx spi
|
|
}
|
|
|
|
// clear the audio codec and USB audio buffer
|
|
void WM8731_CleanBuffer(void)
|
|
{
|
|
memset(CODEC_Audio_Buffer_RX, 0x00, sizeof CODEC_Audio_Buffer_RX);
|
|
memset(CODEC_Audio_Buffer_TX, 0x00, sizeof CODEC_Audio_Buffer_TX);
|
|
memset(USB_AUDIO_rx_buffer_a, 0x00, sizeof USB_AUDIO_rx_buffer_a);
|
|
memset(USB_AUDIO_rx_buffer_b, 0x00, sizeof USB_AUDIO_rx_buffer_a);
|
|
memset(USB_AUDIO_tx_buffer, 0x00, sizeof USB_AUDIO_tx_buffer);
|
|
ResetAGC();
|
|
}
|
|
|
|
// send I2C command to codec
|
|
static uint8_t WM8731_SendI2CCommand(uint8_t reg, uint8_t value)
|
|
{
|
|
uint8_t st = 2;
|
|
uint8_t repeats = 0;
|
|
while (st != 0 && repeats < 3)
|
|
{
|
|
i2c_beginTransmission_u8(&I2C_WM8731, B8(0011010)); //I2C_ADDRESS_WM8731 00110100
|
|
i2c_write_u8(&I2C_WM8731, reg); // MSB
|
|
i2c_write_u8(&I2C_WM8731, value); // MSB
|
|
st = i2c_endTransmission(&I2C_WM8731);
|
|
if (st != 0)
|
|
repeats++;
|
|
HAL_Delay(1);
|
|
}
|
|
return st;
|
|
}
|
|
|
|
// switch to mixed RX-TX mode (for LOOP)
|
|
void WM8731_TXRX_mode(void) //loopback
|
|
{
|
|
WM8731_SendI2CCommand(B8(00000101), B8(11111111)); //R2 Left Headphone Out
|
|
WM8731_SendI2CCommand(B8(00000111), B8(11111111)); //R3 Right Headphone Out
|
|
WM8731_SendI2CCommand(B8(00001010), B8(00010000)); //R5 Digital Audio Path Control
|
|
if (TRX.InputType_LINE) //line
|
|
{
|
|
WM8731_SendI2CCommand(B8(00000000), B8(00010111)); //R0 Left Line In
|
|
WM8731_SendI2CCommand(B8(00000010), B8(00010111)); //R1 Right Line In
|
|
WM8731_SendI2CCommand(B8(00001000), B8(00010010)); //R4 Analogue Audio Path Control
|
|
WM8731_SendI2CCommand(B8(00001100), B8(01100010)); //R6 Power Down Control, internal crystal
|
|
}
|
|
if (TRX.InputType_MIC) //mic
|
|
{
|
|
WM8731_SendI2CCommand(B8(00000001), B8(10000000)); //R0 Left Line In
|
|
WM8731_SendI2CCommand(B8(00000011), B8(10000000)); //R1 Right Line In
|
|
if (TRX.MIC_BOOST)
|
|
WM8731_SendI2CCommand(B8(00001000), B8(00010101)); //R4 Analogue Audio Path Control
|
|
else
|
|
WM8731_SendI2CCommand(B8(00001000), B8(00010100)); //R4 Analogue Audio Path Control
|
|
WM8731_SendI2CCommand(B8(00001100), B8(01100001)); //R6 Power Down Control, internal crystal
|
|
}
|
|
}
|
|
|
|
void WM8731_Mute(void)
|
|
{
|
|
WM8731_Muting = true;
|
|
HAL_GPIO_WritePin(MUTE_GPIO_Port, MUTE_Pin, GPIO_PIN_RESET);
|
|
}
|
|
|
|
void WM8731_UnMute(void)
|
|
{
|
|
WM8731_Muting = false;
|
|
HAL_GPIO_WritePin(MUTE_GPIO_Port, MUTE_Pin, GPIO_PIN_SET);
|
|
}
|
|
|
|
void WM8731_Beep(void)
|
|
{
|
|
if(TRX.Beeper)
|
|
{
|
|
WM8731_Beeping = true;
|
|
HAL_Delay(50);
|
|
WM8731_Beeping = false;
|
|
}
|
|
}
|
|
|
|
// initialize the audio codec over I2C
|
|
void WM8731_Init(void)
|
|
{
|
|
if (WM8731_SendI2CCommand(B8(00011110), B8(00000000)) != 0) //R15 Reset Chip
|
|
{
|
|
sendToDebug_strln("[ERR] Audio codec not found");
|
|
LCD_showError("Audio codec init error", true);
|
|
}
|
|
WM8731_SendI2CCommand(B8(00000101), B8(10000000)); //R2 Left Headphone Out Mute
|
|
WM8731_SendI2CCommand(B8(00000111), B8(10000000)); //R3 Right Headphone Out Mute
|
|
WM8731_SendI2CCommand(B8(00001110), B8(00001110)); //R7 Digital Audio Interface Format, Codec Slave, 32bits, I2S Format, MSB-First left-1 justified
|
|
WM8731_SendI2CCommand(B8(00010000), B8(00000000)); //R8 Sampling Control normal mode, 256fs, SR=0 (MCLK@12.288Mhz, fs=48kHz))
|
|
WM8731_SendI2CCommand(B8(00010010), B8(00000001)); //R9 reactivate digital audio interface
|
|
WM8731_SendI2CCommand(B8(00000000), B8(10000000)); //R0 Left Line In
|
|
WM8731_SendI2CCommand(B8(00000010), B8(10000000)); //R1 Right Line In
|
|
WM8731_SendI2CCommand(B8(00001000), B8(00010110)); //R4 Analogue Audio Path Control
|
|
WM8731_SendI2CCommand(B8(00001010), B8(00010000)); //R5 Digital Audio Path Control
|
|
WM8731_SendI2CCommand(B8(00001100), B8(01100111)); //R6 Power Down Control
|
|
WM8731_UnMute();
|
|
}
|
|
|
|
// RX Buffer is fully sent to the codec
|
|
static void I2S_DMATxCplt(DMA_HandleTypeDef *hdma)
|
|
{
|
|
if (((I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent)->Instance == SPI3)
|
|
{
|
|
if (Processor_NeedRXBuffer) // if the audio codec did not provide data to the buffer, raise the error flag
|
|
WM8731_Buffer_underrun = true;
|
|
WM8731_DMA_state = true;
|
|
Processor_NeedRXBuffer = true;
|
|
if (CurrentVFO()->Mode == TRX_MODE_LOOPBACK)
|
|
Processor_NeedTXBuffer = true;
|
|
WM8731_DMA_samples += (CODEC_AUDIO_BUFFER_SIZE / 2);
|
|
}
|
|
}
|
|
|
|
// RX Buffer half sent to the codec
|
|
static void I2S_DMATxHalfCplt(DMA_HandleTypeDef *hdma)
|
|
{
|
|
if (((I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent)->Instance == SPI3)
|
|
{
|
|
if (Processor_NeedRXBuffer) // if the audio codec did not provide data to the buffer, raise the error flag
|
|
WM8731_Buffer_underrun = true;
|
|
WM8731_DMA_state = false;
|
|
Processor_NeedRXBuffer = true;
|
|
if (CurrentVFO()->Mode == TRX_MODE_LOOPBACK)
|
|
Processor_NeedTXBuffer = true;
|
|
WM8731_DMA_samples += (CODEC_AUDIO_BUFFER_SIZE / 2);
|
|
}
|
|
}
|
|
|
|
// TX Buffer is completely taken from the codec
|
|
static void I2S_DMARxCplt(DMA_HandleTypeDef *hdma)
|
|
{
|
|
I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
|
|
HAL_I2S_RxCpltCallback(hi2s);
|
|
}
|
|
|
|
// TX Buffer half received from the codec
|
|
static void I2S_DMARxHalfCplt(DMA_HandleTypeDef *hdma)
|
|
{
|
|
I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
|
|
HAL_I2S_RxHalfCpltCallback(hi2s);
|
|
}
|
|
|
|
// DMA I2S error
|
|
static void I2S_DMAError(DMA_HandleTypeDef *hdma)
|
|
{
|
|
I2S_HandleTypeDef *hi2s = (I2S_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent; /* Derogation MISRAC2012-Rule-11.5 */
|
|
|
|
/* Disable Rx and Tx DMA Request */
|
|
CLEAR_BIT(hi2s->Instance->CR2, (SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN));
|
|
hi2s->TxXferCount = (uint16_t)0UL;
|
|
hi2s->RxXferCount = (uint16_t)0UL;
|
|
|
|
hi2s->State = HAL_I2S_STATE_READY;
|
|
|
|
/* Set the error code and execute error callback*/
|
|
SET_BIT(hi2s->ErrorCode, HAL_I2S_ERROR_DMA);
|
|
|
|
/* Call user error callback */
|
|
HAL_I2S_ErrorCallback(hi2s);
|
|
}
|
|
|
|
static HAL_StatusTypeDef HAL_I2S_TXRX_DMA(I2S_HandleTypeDef *hi2s, uint16_t *txData, uint16_t *rxData, uint16_t txSize, uint16_t rxSize)
|
|
{
|
|
if ((rxData == NULL) || (txData == NULL) || (rxSize == 0UL) || (txSize == 0UL))
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Process Locked */
|
|
__HAL_LOCK(hi2s);
|
|
|
|
if (hi2s->State != HAL_I2S_STATE_READY)
|
|
{
|
|
__HAL_UNLOCK(hi2s);
|
|
return HAL_BUSY;
|
|
}
|
|
|
|
/* Set state and reset error code */
|
|
hi2s->pTxBuffPtr = txData;
|
|
hi2s->pRxBuffPtr = rxData;
|
|
|
|
hi2s->State = HAL_I2S_STATE_BUSY_TX_RX;
|
|
hi2s->ErrorCode = HAL_I2S_ERROR_NONE;
|
|
|
|
hi2s->TxXferSize = txSize;
|
|
hi2s->TxXferCount = txSize;
|
|
hi2s->RxXferSize = (rxSize << 1U);
|
|
hi2s->RxXferCount = (rxSize << 1U);
|
|
|
|
hi2s->hdmarx->XferHalfCpltCallback = I2S_DMARxHalfCplt;
|
|
hi2s->hdmarx->XferCpltCallback = I2S_DMARxCplt;
|
|
hi2s->hdmarx->XferErrorCallback = I2S_DMAError;
|
|
hi2s->hdmatx->XferHalfCpltCallback = I2S_DMATxHalfCplt;
|
|
hi2s->hdmatx->XferCpltCallback = I2S_DMATxCplt;
|
|
hi2s->hdmatx->XferErrorCallback = I2S_DMAError;
|
|
|
|
/* Enable the Rx DMA Stream/Channel */
|
|
if (HAL_OK != HAL_DMA_Start_IT(hi2s->hdmarx, (uint32_t)&I2SxEXT(hi2s->Instance)->DR, (uint32_t)hi2s->pRxBuffPtr, hi2s->RxXferSize))
|
|
{
|
|
// Update SPI error code
|
|
SET_BIT(hi2s->ErrorCode, HAL_I2S_ERROR_DMA);
|
|
hi2s->State = HAL_I2S_STATE_READY;
|
|
|
|
__HAL_UNLOCK(hi2s);
|
|
return HAL_ERROR;
|
|
}
|
|
if (HAL_OK != HAL_DMA_Start_IT(hi2s->hdmatx, (uint32_t)hi2s->pTxBuffPtr, (uint32_t)&hi2s->Instance->DR, hi2s->TxXferSize))
|
|
{
|
|
//Update SPI error code
|
|
SET_BIT(hi2s->ErrorCode, HAL_I2S_ERROR_DMA);
|
|
hi2s->State = HAL_I2S_STATE_READY;
|
|
|
|
__HAL_UNLOCK(hi2s);
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Enable Rx DMA Request */
|
|
SET_BIT(I2SxEXT(hi2s->Instance)->CR2, SPI_CR2_RXDMAEN);
|
|
|
|
/* Enable Tx DMA Request */
|
|
SET_BIT(hi2s->Instance->CR2, SPI_CR2_TXDMAEN);
|
|
|
|
/* Check if the I2S is already enabled */
|
|
if ((hi2s->Instance->I2SCFGR & SPI_I2SCFGR_I2SE) != SPI_I2SCFGR_I2SE)
|
|
{
|
|
/* Enable I2Sext(receiver) before enabling I2Sx peripheral */
|
|
__HAL_I2SEXT_ENABLE(hi2s);
|
|
|
|
/* Enable I2S peripheral after the I2Sext */
|
|
__HAL_I2S_ENABLE(hi2s);
|
|
}
|
|
|
|
__HAL_UNLOCK(hi2s);
|
|
return HAL_OK;
|
|
}
|