MDUV3x0: using HR_C6000 for MCU to speaker audio

This commit is contained in:
Silvano Seva 2024-10-12 12:24:05 +02:00
parent 2c3f9c50c5
commit 2d0bf51873
7 changed files with 90 additions and 36 deletions

View File

@ -159,7 +159,7 @@ mdx_src = ['platform/drivers/ADC/ADC1_MDx.c',
'platform/drivers/NVM/W25Qx.c',
'platform/drivers/NVM/nvmem_settings_MDx.c',
'platform/drivers/NVM/nvmem_MDx.c',
'platform/drivers/audio/audio_MDx.c',
'platform/drivers/audio/audio_MDx.cpp',
'platform/drivers/baseband/HR_Cx000.cpp',
'platform/drivers/tones/toneGenerator_MDx.cpp',
'platform/drivers/SPI/spi_custom.c',
@ -361,6 +361,7 @@ mduv3x0_src = ['platform/drivers/CPS/cps_io_native_MDUV3x0.c',
'platform/drivers/display/HX8353_MD3x.cpp',
'platform/drivers/backlight/backlight_MDx.c',
'platform/drivers/chSelector/chSelector_UV3x0.c',
'platform/drivers/audio/Cx000_dac.cpp',
'platform/drivers/baseband/radio_UV3x0.cpp',
'platform/drivers/baseband/AT1846S_UV3x0.cpp',
'platform/drivers/baseband/HR_C6000_UV3x0.cpp']

View File

@ -29,6 +29,7 @@
#define UI_THREAD_STKSIZE 2048
#define RTX_THREAD_STKSIZE 512
#define CODEC2_THREAD_STKSIZE 16384
#define AUDIO_THREAD_STKSIZE 512
/**
* Thread priority levels, UNIX-like: lower level, higher thread priority

View File

@ -36,7 +36,7 @@
* provide the helper function below to keep the real volume level consistent
* with the knob position.
*/
#if defined(PLATFORM_MDUV3x0) || defined(PLATFORM_TTWRPLUS)
#if defined(PLATFORM_TTWRPLUS)
void _setVolume()
{
static uint8_t oldVolume = 0xFF;
@ -45,15 +45,8 @@ void _setVolume()
if(volume == oldVolume)
return;
#if defined(PLATFORM_MDUV3x0)
// Apply new volume level, map 0 - 255 range into -31 to 31
int8_t gain = ((int8_t) (volume / 4)) - 31;
C6000.setDacGain(gain);
#elif defined(PLATFORM_TTWRPLUS)
// AT1846S volume control is 4 bit
AT1846S::instance().setRxAudioGain(volume / 16, volume / 16);
#endif
oldVolume = volume;
}
#endif
@ -91,7 +84,7 @@ void OpMode_FM::update(rtxStatus_t *const status, const bool newCfg)
{
(void) newCfg;
#if defined(PLATFORM_MDUV3x0) || defined(PLATFORM_TTWRPLUS)
#if defined(PLATFORM_TTWRPLUS)
// Set output volume by changing the HR_C6000 DAC gain
_setVolume();
#endif

View File

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, *
* Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, *
* Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO *
@ -18,15 +18,24 @@
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <interfaces/platform.h>
#include <interfaces/delays.h>
#include <interfaces/audio.h>
#include <interfaces/radio.h>
#include <peripherals/gpio.h>
#include <hwconfig.h>
#include <threads.h>
#include <state.h>
#include "toneGenerator_MDx.h"
#include "stm32_pwm.h"
#include "stm32_adc.h"
#ifdef PLATFORM_MDUV3x0
#include <HR_C6000.h>
#include "Cx000_dac.h"
#endif
#define PATH(x,y) ((x << 4) | y)
static const uint8_t pathCompatibilityMatrix[9][9] =
@ -64,10 +73,48 @@ static const struct PwmChannelCfg stm32pwm_cfg =
stm32pwm_stopCbk
};
#ifdef PLATFORM_MDUV3x0
static void *audio_thread(void *arg)
{
(void) arg;
static uint8_t oldVolume = 0xFF;
unsigned long long now = getTick();
Cx000dac_init(&C6000);
while(state.devStatus != SHUTDOWN)
{
Cx000dac_task();
uint8_t volume = platform_getVolumeLevel();
if(volume != oldVolume)
{
// Apply new volume level, map 0 - 255 range into -31 to 31
int8_t gain = ((int8_t) (volume / 4)) - 31;
C6000.setDacGain(gain);
oldVolume = volume;
}
now += 4;
sleepUntil(now);
}
Cx000dac_terminate();
return NULL;
}
#endif
const struct audioDevice outputDevices[] =
{
{NULL, NULL, 0, SINK_MCU},
#ifdef PLATFORM_MDUV3x0
{&Cx000_dac_audio_driver, NULL, 0, SINK_SPK},
#else
{&stm32_pwm_audio_driver, &stm32pwm_cfg, 0, SINK_SPK},
#endif
{&stm32_pwm_audio_driver, &stm32pwm_cfg, 0, SINK_RTX},
};
@ -102,6 +149,25 @@ void audio_init()
stm32pwm_init();
stm32adc_init(STM32_ADC_ADC2);
#ifdef PLATFORM_MDUV3x0
gpio_setMode(DMR_CLK, OUTPUT);
gpio_setMode(DMR_MOSI, OUTPUT);
gpio_setMode(DMR_MISO, INPUT);
spi_init((const struct spiDevice *) &c6000_spi);
C6000.init();
pthread_attr_t attr;
pthread_t thread;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, AUDIO_THREAD_STKSIZE);
param.sched_priority = THREAD_PRIO_RT;
pthread_attr_setschedparam(&attr, &param);
pthread_create(&thread, &attr, audio_thread, NULL);
#endif
}
void audio_terminate()
@ -136,7 +202,11 @@ void audio_connect(const enum AudioSource source, const enum AudioSink sink)
radio_enableAfOutput();
break;
// MD-UV380 uses HR_C6000 for MCU->SPK audio output. Switching between
// incoming FM audio and DAC output is done automatically inside the IC.
#ifndef PLATFORM_MDUV3x0
case PATH(SOURCE_MCU, SINK_SPK):
#endif
case PATH(SOURCE_MCU, SINK_RTX):
gpio_setMode(BEEP_OUT, ALTERNATE | ALTERNATE_FUNC(2));
break;
@ -182,7 +252,9 @@ void audio_disconnect(const enum AudioSource source, const enum AudioSink sink)
radio_disableAfOutput();
break;
#ifndef PLATFORM_MDUV3x0
case PATH(SOURCE_MCU, SINK_SPK):
#endif
case PATH(SOURCE_MCU, SINK_RTX):
gpio_setMode(BEEP_OUT, INPUT); // Set output to Hi-Z
break;

View File

@ -202,7 +202,6 @@ void HR_Cx000< M >::startAnalogTx(const TxAudioSource source, const FmConfig cfg
writeReg(M::CONFIG, 0xC2, 0x00); // Codec AGC gain
writeReg(M::CONFIG, 0xE5, 0x1A); // Unknown (Default value = 0A)
writeReg(M::CONFIG, 0x25, 0x0E); // Undocumented Register
writeReg(M::CONFIG, 0x26, 0xFE); // Undocumented register Turns off FM receive
writeReg(M::CONFIG, 0x83, 0xFF); // Clear all Interrupts
writeReg(M::CONFIG, 0x87, 0x00); // Clear Int Masks
writeReg(M::CONFIG, 0xA1, 0x80); // FM_mod, all modes cleared
@ -221,5 +220,4 @@ void HR_Cx000< M >::stopAnalogTx()
writeReg(M::CONFIG, 0x60, 0x00); // Stop analog transmission
writeReg(M::CONFIG, 0xE0, 0xC9); // Codec enabled, LineIn1, LineOut2, I2S slave mode
writeReg(M::CONFIG, 0x34, 0x98); // FM bpf enabled, 25kHz bandwidth
writeReg(M::CONFIG, 0x26, 0xFD); // Undocumented register, enable FM receive
}

View File

@ -85,15 +85,10 @@ void radio_init(const rtxStatus_t *rtxState)
nvm_readCalibData(&calData);
/*
* Configure AT1846S and HR_C6000, keep AF output disabled at power on.
* Initialize AT1846S keep AF output disabled at power on.
* HR_C6000 is initialized in advance by the audio system.
*/
gpio_setMode(DMR_CLK, OUTPUT);
gpio_setMode(DMR_MOSI, OUTPUT);
gpio_setMode(DMR_MISO, INPUT);
spi_init((const struct spiDevice *) &c6000_spi);
at1846s.init();
C6000.init();
radio_disableAfOutput();
}
@ -150,14 +145,16 @@ bool radio_checkRxDigitalSquelch()
void radio_enableAfOutput()
{
// Bit 2 of register 0x36: enable voice channel in FM mode
// Undocumented register, bits [1:0] seem to enable/disable FM audio RX.
// TODO: AF output management for DMR mode
C6000.writeCfgRegister(0x36, 0x02);
// 0xFD enable FM receive.
C6000.writeCfgRegister(0x26, 0xFD);
}
void radio_disableAfOutput()
{
C6000.writeCfgRegister(0x36, 0x00);
// Undocumented register, disable FM receive
C6000.writeCfgRegister(0x26, 0xFE);
}
void radio_enableRx()
@ -174,18 +171,6 @@ void radio_enableRx()
at1846s.setFrequency(config->rxFrequency);
at1846s.setFuncMode(AT1846S_FuncMode::RX);
/*
* Force silencing of audio output when RX is enabled with M17 operating
* mode selected. Avoids the spillover of baseband signal towards the
* speaker.
*
* TODO: improve this solution.
*/
if(config->opMode == OPMODE_M17)
{
C6000.writeCfgRegister(0xE0, 0x00);
}
if(currRxBand == BND_VHF)
{
gpio_setPin(VHF_LNA_EN);

View File

@ -21,9 +21,13 @@
#include <spi_bitbang.h>
#include <spi_custom.h>
#include <hwconfig.h>
#include <pthread.h>
#include <pinmap.h>
#include <spi_stm32.h>
static pthread_mutex_t c6000_mutex;
/**
* SPI bitbang function for HR_C6000 command interface (U_SPI).
*
@ -67,5 +71,5 @@ static uint8_t spiC6000_func(const void *priv, uint8_t value)
return incoming;
}
SPI_CUSTOM_DEVICE_DEFINE(c6000_spi, spiC6000_func, NULL, NULL)
SPI_CUSTOM_DEVICE_DEFINE(c6000_spi, spiC6000_func, NULL, &c6000_mutex)
SPI_STM32_DEVICE_DEFINE(nvm_spi, SPI1, NULL)