MDUV3x0: using HR_C6000 for MCU to speaker audio
This commit is contained in:
parent
2c3f9c50c5
commit
2d0bf51873
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ¶m);
|
||||
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;
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue