diff --git a/meson.build b/meson.build index 4782d0ba..dd8d2920 100644 --- a/meson.build +++ b/meson.build @@ -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'] diff --git a/openrtx/include/core/threads.h b/openrtx/include/core/threads.h index 3cc54f60..4fd83bf7 100644 --- a/openrtx/include/core/threads.h +++ b/openrtx/include/core/threads.h @@ -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 diff --git a/openrtx/src/rtx/OpMode_FM.cpp b/openrtx/src/rtx/OpMode_FM.cpp index 0eec9244..78cf1575 100644 --- a/openrtx/src/rtx/OpMode_FM.cpp +++ b/openrtx/src/rtx/OpMode_FM.cpp @@ -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 diff --git a/platform/drivers/audio/audio_MDx.c b/platform/drivers/audio/audio_MDx.cpp similarity index 78% rename from platform/drivers/audio/audio_MDx.c rename to platform/drivers/audio/audio_MDx.cpp index 1d554587..190686fc 100644 --- a/platform/drivers/audio/audio_MDx.c +++ b/platform/drivers/audio/audio_MDx.cpp @@ -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 * ***************************************************************************/ +#include #include #include #include #include #include +#include +#include #include "toneGenerator_MDx.h" #include "stm32_pwm.h" #include "stm32_adc.h" +#ifdef PLATFORM_MDUV3x0 +#include +#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; diff --git a/platform/drivers/baseband/HR_C6000_UV3x0.cpp b/platform/drivers/baseband/HR_C6000_UV3x0.cpp index 46da53f4..afb2fe01 100644 --- a/platform/drivers/baseband/HR_C6000_UV3x0.cpp +++ b/platform/drivers/baseband/HR_C6000_UV3x0.cpp @@ -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 } diff --git a/platform/drivers/baseband/radio_UV3x0.cpp b/platform/drivers/baseband/radio_UV3x0.cpp index 9ea77403..68e5a3a7 100644 --- a/platform/drivers/baseband/radio_UV3x0.cpp +++ b/platform/drivers/baseband/radio_UV3x0.cpp @@ -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); diff --git a/platform/targets/MD-UV3x0/hwconfig.c b/platform/targets/MD-UV3x0/hwconfig.c index c916d01e..3c646bb9 100644 --- a/platform/targets/MD-UV3x0/hwconfig.c +++ b/platform/targets/MD-UV3x0/hwconfig.c @@ -21,9 +21,13 @@ #include #include #include +#include #include #include + +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)