diff --git a/meson.build b/meson.build
index af55ec4a..7bad9a54 100644
--- a/meson.build
+++ b/meson.build
@@ -18,7 +18,8 @@ openrtx_src = ['openrtx/src/main.c',
openrtx_inc = ['openrtx/include/interfaces',
'openrtx/include/fonts',
- 'platform/drivers/ADC']
+ 'platform/drivers/ADC',
+ 'platform/drivers/tones']
## RTOS
rtos_src = ['rtos/uC-OS3/Source/__dbg_uCOS-III.c',
@@ -96,7 +97,7 @@ stm32f405_inc = ['platform/mcu/CMSIS/Include',
'rtos/uC-OS3/Ports/ARM-Cortex-M/ARMv7-M',
'rtos/uC-CPU/ARM-Cortex-M/ARMv7-M']
-stm32f405_def = {'STM32F40_41xxx': '', 'HSE_VALUE':'8000000'}
+stm32f405_def = {'STM32F40_41xxx': '', 'HSE_VALUE':'8000000'}
##
## Platform specializations
@@ -132,6 +133,7 @@ endif
md380_src = src + stm32f405_src + ['platform/drivers/display/HX83XX_MDxx380.c',
'platform/drivers/keyboard/keyboard_MDxx380.c',
'platform/drivers/ADC/ADC1_MDxx380.c',
+ 'platform/drivers/tones/toneGenerator_MDxx380.c',
'platform/targets/MD380/platform.c',
'openrtx/src/graphics/graphics_rgb565.c']
diff --git a/platform/drivers/tones/toneGenerator_MDxx380.c b/platform/drivers/tones/toneGenerator_MDxx380.c
new file mode 100644
index 00000000..e907ba6d
--- /dev/null
+++ b/platform/drivers/tones/toneGenerator_MDxx380.c
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, *
+ * Niccolò Izzo IU2KIN *
+ * Frederik Saraci IU2NRO *
+ * Silvano Seva IU2KWO *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see *
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Sine table for PWM-based sinewave generation, containing 256 samples over one
+ * period of a 64Hz sinewave. This gives a PWM base frequency of 16.384kHz.
+ */
+uint8_t sineTable[] =
+{
+ 128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,176,179,182,
+ 185,188,190,193,196,198,201,203,206,208,211,213,215,218,220,222,224,226,228,
+ 230,232,234,235,237,238,240,241,243,244,245,246,248,249,250,250,251,252,253,
+ 253,254,254,254,255,255,255,255,255,255,255,254,254,254,253,253,252,251,250,
+ 250,249,248,246,245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,
+ 220,218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,176,173,
+ 170,167,165,162,158,155,152,149,146,143,140,137,134,131,128,124,121,118,115,
+ 112,109,106,103,100,97,93,90,88,85,82,79,76,73,70,67,65,62,59,57,54,52,49,47,
+ 44,42,40,37,35,33,31,29,27,25,23,21,20,18,17,15,14,12,11,10,9,7,6,5,5,4,3,2,
+ 2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,17,18,20,21,23,
+ 25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,65,67,70,73,76,79,82,85,88,
+ 90,93,97,100,103,106,109,112,115,118,121,124
+};
+
+/*
+ * Correction factor to compensate for the (slight) difference between the exact
+ * sampling frequency of the sine table and the PWM frequency generated by the
+ * timer. This difference causes a frequency error in the resulting output of
+ * both CTCSS and "beep" signals.
+ * To compensate for this, simply multiply the target frequency by this
+ * correction factor.
+ */
+const float freqCorrFactor = 16384.0f/16406.25;
+const uint32_t baseSineFreq = 64;
+
+uint32_t toneTableIndex = 0; /* Current sine table index for CTCSS generator */
+uint32_t toneTableIncr = 0; /* CTCSS sine table index increment per tick */
+
+uint32_t beepTableIndex = 0; /* Current sine table index for "beep" generator */
+uint32_t beepTableIncr = 0; /* "beep" sine table index increment per tick */
+uint32_t beepTimerCount = 0; /* Downcounter for timed "beep" */
+
+void __attribute__((used)) TIM3_IRQHandler()
+{
+ toneTableIndex += toneTableIncr;
+ beepTableIndex += beepTableIncr;
+
+ TIM3->CCR2 = sineTable[(toneTableIndex >> 16) & 0xFF];
+ TIM3->CCR3 = sineTable[(beepTableIndex >> 16) & 0xFF];
+ TIM3->SR = 0;
+
+ if(beepTimerCount > 0)
+ {
+ beepTimerCount--;
+ if(beepTimerCount == 0)
+ {
+ TIM3->CCER &= ~TIM_CCER_CC3E;
+ }
+ }
+
+ /* Shutdown timer if both compare channels are inactive */
+ if((TIM3->CCER & (TIM_CCER_CC2E | TIM_CCER_CC3E)) == 0)
+ {
+ TIM3->CR1 &= ~TIM_CR1_CEN;
+ }
+}
+
+void toneGen_init()
+{
+ /*
+ * Configure GPIOs:
+ * - CTCSS output is on PC7 (on MD380), that is TIM3-CH2, AF2
+ * - "beep" output is on PC8 (on MD380), that is TIM3-CH3, AF2
+ */
+ gpio_setMode(CTCSS_OUT, ALTERNATE);
+ gpio_setMode(BEEP_OUT, ALTERNATE);
+ gpio_setAlternateFunction(CTCSS_OUT, 2);
+ gpio_setAlternateFunction(BEEP_OUT, 2);
+
+ /*
+ * Timer configuration:
+ * - APB1 frequency = 42MHz, with 1:10 prescaler we have Ftick = 4.2MHz
+ * - ARR = 255 (8-bit PWM), gives an update rate of 16.406kHz
+ * - Nominal update rate is 16.384kHz -> error = +22.25Hz
+ */
+ RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
+
+ TIM3->ARR = 0xFF;
+ TIM3->PSC = 9;
+ TIM3->CCMR1 = TIM_CCMR1_OC2M_2 /* CH2 in PWM mode 1, preload enabled */
+ | TIM_CCMR1_OC2M_1
+ | TIM_CCMR1_OC2PE;
+ TIM3->CCMR2 = TIM_CCMR2_OC3M_2 /* The same for CH3 */
+ | TIM_CCMR2_OC3M_1
+ | TIM_CCMR2_OC3PE;
+ TIM3->DIER |= TIM_DIER_UIE; /* Interrupt on counter update */
+ TIM3->CR1 |= TIM_CR1_ARPE; /* Enable auto preload on reload */
+
+ NVIC_SetPriority(TIM3_IRQn, 10);
+ NVIC_EnableIRQ(TIM3_IRQn);
+}
+
+void toneGen_shutdown()
+{
+ RCC->APB1ENR &= ~RCC_APB1ENR_TIM3EN;
+ gpio_setMode(CTCSS_OUT, INPUT);
+ gpio_setMode(BEEP_OUT, INPUT);
+}
+
+void toneGen_setToneFreq(float toneFreq)
+{
+ /*
+ * Convert to 16.16 fixed point number, then divide by the frequency of
+ * sinewave stored in the PWM table
+ */
+ float dividend = toneFreq * freqCorrFactor * 65536.0f;
+ toneTableIncr = ((uint32_t) dividend)/baseSineFreq;
+}
+
+void toneGen_toneOn()
+{
+ TIM3->CCER |= TIM_CCER_CC2E;
+ TIM3->CR1 |= TIM_CR1_CEN;
+}
+
+void toneGen_toneOff()
+{
+ TIM3->CCER &= ~TIM_CCER_CC2E;
+}
+
+void toneGen_setBeepFreq(float beepFreq)
+{
+ float dividend = beepFreq * freqCorrFactor * 65536.0f;
+ beepTableIncr = ((uint32_t) dividend)/baseSineFreq;
+}
+
+void toneGen_beepOn()
+{
+ TIM3->CCER |= TIM_CCER_CC3E;
+ TIM3->CR1 |= TIM_CR1_CEN;
+}
+
+void toneGen_beepOff()
+{
+ TIM3->CCER &= ~TIM_CCER_CC3E;
+}
+
+void toneGen_timedBeep(uint16_t duration)
+{
+ /*
+ * Duration is in milliseconds, while counter update rate is 16.406kHz.
+ * Thus, the value for downcounter is (duration * 16.406)/1000.
+ */
+ beepTimerCount = (duration * 16406)/1000;
+ TIM3->CCER |= TIM_CCER_CC3E;
+ TIM3->CR1 |= TIM_CR1_CEN;
+}
diff --git a/platform/drivers/tones/toneGenerator_MDxx380.h b/platform/drivers/tones/toneGenerator_MDxx380.h
new file mode 100644
index 00000000..6f3c855a
--- /dev/null
+++ b/platform/drivers/tones/toneGenerator_MDxx380.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, *
+ * Niccolò Izzo IU2KIN *
+ * Frederik Saraci IU2NRO *
+ * Silvano Seva IU2KWO *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, see *
+ ***************************************************************************/
+
+#ifndef TONE_GENERATOR_H
+#define TONE_GENERATOR_H
+
+#include
+
+/**
+ * Tone generator for MDxx380 family, used for both CTCSS tones and user
+ * interface "beeps".
+ * This driver uses TIM3 of STM32F405 mcu in PWM mode to generate sinewaves
+ * using a precomputed sine table.
+ */
+
+/**
+ * Initialise tone generator.
+ */
+void toneGen_init();
+
+/**
+ * Terminate tone generator.
+ */
+void toneGen_shutdown();
+
+/**
+ * Set frequency for CTCSS tone generation.
+ * @param toneFreq: CTCSS tone frequency.
+ */
+void toneGen_setToneFreq(float toneFreq);
+
+/**
+ * Activate generation of CTCSS tone.
+ */
+void toneGen_toneOn();
+
+/**
+ * Terminate generation of CTCSS tone.
+ */
+void toneGen_toneOff();
+
+/**
+ * Set frequency for user interface "beep".
+ * @param beepFreq: frequency of "beep" tone.
+ */
+void toneGen_setBeepFreq(float beepFreq);
+
+/**
+ * Activate generation of "beep" tone.
+ */
+void toneGen_beepOn();
+
+/**
+ * Terminate generation of "beep" tone.
+ */
+void toneGen_beepOff();
+
+/**
+ * Activate generation of "beep" tone with automatic termination after a given
+ * amount of time.
+ * @param duration: duration of "beep" tone, in milliseconds.
+ */
+void toneGen_timedBeep(uint16_t duration);
+
+#endif /* TONE_GENERATOR_H */
diff --git a/platform/targets/MD380/hwconfig.h b/platform/targets/MD380/hwconfig.h
index 636000bc..9bea489b 100644
--- a/platform/targets/MD380/hwconfig.h
+++ b/platform/targets/MD380/hwconfig.h
@@ -71,4 +71,8 @@
#define KB_ROW2 GPIOD,2 /* K2 */
#define KB_ROW3 GPIOD,3 /* K3 */
+/* Tone generator */
+#define CTCSS_OUT GPIOC,7 /* System "beep" */
+#define BEEP_OUT GPIOC,8 /* CTCSS tone */
+
#endif