From d95e36f43bf08067878f1aaf2e03ac5ed275da7e Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Thu, 15 Apr 2021 10:03:17 +0200 Subject: [PATCH] Moved to C++ and refactored AT1846S driver, grouping common code. A C-callable wrapper is also provided. TG-37 --- meson.build | 6 +- openrtx/include/datatypes.h | 8 + platform/drivers/baseband/AT1846S.c | 310 -------------- platform/drivers/baseband/AT1846S.h | 392 ++++++++++++------ platform/drivers/baseband/AT1846S_GDx.cpp | 217 ++++++++++ .../{AT1846S_UV3x0.c => AT1846S_UV3x0.cpp} | 302 ++++++++------ platform/drivers/baseband/AT1846S_wrapper.cpp | 124 ++++++ platform/drivers/baseband/AT1846S_wrapper.h | 189 +++++++++ platform/drivers/baseband/interfaces.h | 16 - platform/drivers/baseband/interfaces_GDx.c | 43 -- platform/drivers/baseband/interfaces_UV3x0.c | 52 --- platform/drivers/baseband/radio_GDx.c | 9 +- platform/drivers/baseband/radio_UV3x0.c | 9 +- platform/mcu/MK22FN512xxx12/drivers/I2C0.h | 8 + 14 files changed, 1005 insertions(+), 680 deletions(-) delete mode 100644 platform/drivers/baseband/AT1846S.c create mode 100644 platform/drivers/baseband/AT1846S_GDx.cpp rename platform/drivers/baseband/{AT1846S_UV3x0.c => AT1846S_UV3x0.cpp} (61%) create mode 100644 platform/drivers/baseband/AT1846S_wrapper.cpp create mode 100644 platform/drivers/baseband/AT1846S_wrapper.h diff --git a/meson.build b/meson.build index b29506bc..8d4c0b97 100644 --- a/meson.build +++ b/meson.build @@ -105,7 +105,8 @@ gdx_src = ['platform/drivers/NVM/W25Qx.c', 'platform/drivers/ADC/ADC0_GDx.c', 'platform/drivers/backlight/backlight_GDx.c', 'platform/drivers/baseband/radio_GDx.c', - 'platform/drivers/baseband/AT1846S.c', + 'platform/drivers/baseband/AT1846S_wrapper.cpp', + 'platform/drivers/baseband/AT1846S_GDx.cpp', 'platform/drivers/baseband/HR_C6000.c', 'platform/drivers/baseband/interfaces_GDx.c', 'platform/drivers/display/UC1701_GDx.c', @@ -234,7 +235,8 @@ mduv3x0_src = src + mdx_src + stm32f405_src + ['platform/drivers/NVM/nvmem_MDUV3 'platform/drivers/display/HX8353_MD3x.cpp', 'platform/drivers/chSelector/chSelector_UV3x0.c', 'platform/drivers/baseband/radio_UV3x0.c', - 'platform/drivers/baseband/AT1846S_UV3x0.c', + 'platform/drivers/baseband/AT1846S_wrapper.cpp', + 'platform/drivers/baseband/AT1846S_UV3x0.cpp', 'platform/drivers/baseband/HR_C6000_UV3x0.c', 'platform/drivers/baseband/interfaces_UV3x0.c'] diff --git a/openrtx/include/datatypes.h b/openrtx/include/datatypes.h index fe4ec7ab..066bad43 100644 --- a/openrtx/include/datatypes.h +++ b/openrtx/include/datatypes.h @@ -22,6 +22,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /** * \brief CTCSS and DCS type definition. * @@ -41,4 +45,8 @@ typedef unsigned int tone_t; */ typedef uint32_t freq_t; +#ifdef __cplusplus +} +#endif + #endif /* DATATYPES_H */ diff --git a/platform/drivers/baseband/AT1846S.c b/platform/drivers/baseband/AT1846S.c deleted file mode 100644 index 5e1e32dc..00000000 --- a/platform/drivers/baseband/AT1846S.c +++ /dev/null @@ -1,310 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * Based on the original work from Kai Ludwig, DG4KLU, and * - * Roger Clark, VK3KYY/G4KYF. * - * * - * 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 "interfaces.h" -#include "AT1846S.h" - -void _maskSetRegister(uint8_t reg, uint16_t mask, uint16_t value) -{ - uint16_t regVal = i2c_readReg16(reg); - regVal = (regVal & ~mask) | (value & mask); - i2c_writeReg16(reg, regVal); -} - -/* - * NOTE: after some main AT1846S parameters (frequency, bandwidth, ...) are changed, - * the chip must be "cycled" to make them effective. Cycling consists of turning - * both TX and RX off and then switch back to the desired functionality. - * - * The function _reloadConfig() provides an helper to do this. - */ -static inline void _reloadConfig() -{ - uint16_t funcMode = i2c_readReg16(0x30) & 0x0060; /* Get current op. status */ - _maskSetRegister(0x30, 0x0060, 0x0000); /* RX and TX off */ - _maskSetRegister(0x30, 0x0060, funcMode); /* Restore op. status */ -} - - -void AT1846S_init() -{ - i2c_writeReg16(0x30, 0x0001); /* Soft reset */ - delayMs(50); - - i2c_writeReg16(0x30, 0x0004); /* Chip enable */ - i2c_writeReg16(0x04, 0x0FD0); /* 26MHz crystal frequency */ - i2c_writeReg16(0x1F, 0x1000); /* Gpio6 squelch output */ - i2c_writeReg16(0x09, 0x03AC); - i2c_writeReg16(0x24, 0x0001); - i2c_writeReg16(0x31, 0x0031); - i2c_writeReg16(0x33, 0x45F5); /* AGC number */ - i2c_writeReg16(0x34, 0x2B89); /* RX digital gain */ - i2c_writeReg16(0x3F, 0x3263); /* RSSI 3 threshold */ - i2c_writeReg16(0x41, 0x470F); /* Tx digital gain */ - i2c_writeReg16(0x42, 0x1036); - i2c_writeReg16(0x43, 0x00BB); - i2c_writeReg16(0x44, 0x06FF); /* Tx digital gain */ - i2c_writeReg16(0x47, 0x7F2F); /* Soft mute */ - i2c_writeReg16(0x4E, 0x0082); - i2c_writeReg16(0x4F, 0x2C62); - i2c_writeReg16(0x53, 0x0094); - i2c_writeReg16(0x54, 0x2A3C); - i2c_writeReg16(0x55, 0x0081); - i2c_writeReg16(0x56, 0x0B02); - i2c_writeReg16(0x57, 0x1C00); /* Bypass RSSI low-pass */ - i2c_writeReg16(0x5A, 0x4935); /* SQ detection time */ - i2c_writeReg16(0x58, 0xBCCD); - i2c_writeReg16(0x62, 0x3263); /* Modulation detect tresh */ - i2c_writeReg16(0x4E, 0x2082); - i2c_writeReg16(0x63, 0x16AD); - i2c_writeReg16(0x30, 0x40A4); - delayMs(50); - - i2c_writeReg16(0x30, 0x40A6); /* Start calibration */ - delayMs(100); - i2c_writeReg16(0x30, 0x4006); /* Stop calibration */ - - delayMs(100); - - i2c_writeReg16(0x58, 0xBCED); - i2c_writeReg16(0x0A, 0x7BA0); /* PGA gain */ - i2c_writeReg16(0x41, 0x4731); /* Tx digital gain */ - i2c_writeReg16(0x44, 0x05FF); /* Tx digital gain */ - i2c_writeReg16(0x59, 0x09D2); /* Mixer gain */ - i2c_writeReg16(0x44, 0x05CF); /* Tx digital gain */ - i2c_writeReg16(0x44, 0x05CC); /* Tx digital gain */ - i2c_writeReg16(0x48, 0x1A32); /* Noise 1 threshold */ - i2c_writeReg16(0x60, 0x1A32); /* Noise 2 threshold */ - i2c_writeReg16(0x3F, 0x29D1); /* RSSI 3 threshold */ - i2c_writeReg16(0x0A, 0x7BA0); /* PGA gain */ - i2c_writeReg16(0x49, 0x0C96); /* RSSI SQL thresholds */ - i2c_writeReg16(0x33, 0x45F5); /* AGC number */ - i2c_writeReg16(0x41, 0x470F); /* Tx digital gain */ - i2c_writeReg16(0x42, 0x1036); - i2c_writeReg16(0x43, 0x00BB); -} - -void AT1846S_terminate() -{ - AT1846S_disableCtcss(); - AT1846S_setFuncMode(AT1846S_OFF); -} - -void AT1846S_setFrequency(const freq_t freq) -{ - /* The value to be written in registers is given by: 0.0016*freqency */ - uint32_t val = (freq/1000)*16; - uint16_t fHi = (val >> 16) & 0xFFFF; - uint16_t fLo = val & 0xFFFF; - - i2c_writeReg16(0x29, fHi); - i2c_writeReg16(0x2A, fLo); - - _reloadConfig(); -} - -void AT1846S_setBandwidth(const AT1846S_bw_t band) -{ - if(band == AT1846S_BW_25) - { - /* 25kHz bandwidth */ - i2c_writeReg16(0x15, 0x1F00); /* Tuning bit */ - i2c_writeReg16(0x32, 0x7564); /* AGC target power */ - i2c_writeReg16(0x3A, 0x44C3); /* Modulation detect sel */ - i2c_writeReg16(0x3F, 0x29D2); /* RSSI 3 threshold */ - i2c_writeReg16(0x3C, 0x0E1C); /* Peak detect threshold */ - i2c_writeReg16(0x48, 0x1E38); /* Noise 1 threshold */ - i2c_writeReg16(0x62, 0x3767); /* Modulation detect tresh */ - i2c_writeReg16(0x65, 0x248A); - i2c_writeReg16(0x66, 0xFF2E); /* RSSI comp and AFC range */ - i2c_writeReg16(0x7F, 0x0001); /* Switch to page 1 */ - i2c_writeReg16(0x06, 0x0024); /* AGC gain table */ - i2c_writeReg16(0x07, 0x0214); - i2c_writeReg16(0x08, 0x0224); - i2c_writeReg16(0x09, 0x0314); - i2c_writeReg16(0x0A, 0x0324); - i2c_writeReg16(0x0B, 0x0344); - i2c_writeReg16(0x0D, 0x1384); - i2c_writeReg16(0x0E, 0x1B84); - i2c_writeReg16(0x0F, 0x3F84); - i2c_writeReg16(0x12, 0xE0EB); - i2c_writeReg16(0x7F, 0x0000); /* Back to page 0 */ - _maskSetRegister(0x30, 0x3000, 0x3000); - } - else - { - /* 12.5kHz bandwidth */ - i2c_writeReg16(0x15, 0x1100); /* Tuning bit */ - i2c_writeReg16(0x32, 0x4495); /* AGC target power */ - i2c_writeReg16(0x3A, 0x40C3); /* Modulation detect sel */ - i2c_writeReg16(0x3F, 0x28D0); /* RSSI 3 threshold */ - i2c_writeReg16(0x3C, 0x0F1E); /* Peak detect threshold */ - i2c_writeReg16(0x48, 0x1DB6); /* Noise 1 threshold */ - i2c_writeReg16(0x62, 0x1425); /* Modulation detect tresh */ - i2c_writeReg16(0x65, 0x2494); - i2c_writeReg16(0x66, 0xEB2E); /* RSSI comp and AFC range */ - i2c_writeReg16(0x7F, 0x0001); /* Switch to page 1 */ - i2c_writeReg16(0x06, 0x0014); /* AGC gain table */ - i2c_writeReg16(0x07, 0x020C); - i2c_writeReg16(0x08, 0x0214); - i2c_writeReg16(0x09, 0x030C); - i2c_writeReg16(0x0A, 0x0314); - i2c_writeReg16(0x0B, 0x0324); - i2c_writeReg16(0x0C, 0x0344); - i2c_writeReg16(0x0D, 0x1344); - i2c_writeReg16(0x0E, 0x1B44); - i2c_writeReg16(0x0F, 0x3F44); - i2c_writeReg16(0x12, 0xE0EB); /* Back to page 0 */ - i2c_writeReg16(0x7F, 0x0000); - _maskSetRegister(0x30, 0x3000, 0x0000); - } - - _reloadConfig(); -} - -void AT1846S_setOpMode(const AT1846S_op_t mode) -{ - if(mode == AT1846S_OP_DMR) - { - /* DMR mode */ - i2c_writeReg16(0x3A, 0x00C2); - i2c_writeReg16(0x33, 0x45F5); - i2c_writeReg16(0x41, 0x4731); - i2c_writeReg16(0x42, 0x1036); - i2c_writeReg16(0x43, 0x00BB); - i2c_writeReg16(0x58, 0xBCFD); - i2c_writeReg16(0x44, 0x06CC); - i2c_writeReg16(0x40, 0x0031); - } - else - { - /* FM mode */ - i2c_writeReg16(0x33, 0x44A5); - i2c_writeReg16(0x41, 0x4431); - i2c_writeReg16(0x42, 0x10F0); - i2c_writeReg16(0x43, 0x00A9); - i2c_writeReg16(0x58, 0xBC05); - i2c_writeReg16(0x44, 0x06FF); - i2c_writeReg16(0x40, 0x0030); - - _maskSetRegister(0x57, 0x0001, 0x00); /* Audio feedback off */ - _maskSetRegister(0x3A, 0x7000, 0x4000); /* Select voice channel */ - } - - _reloadConfig(); -} - -void AT1846S_setFuncMode(const AT1846S_func_t mode) -{ - /* - * Functional mode is controlled by bits 5 (RX on) and 6 (TX on) in register - * 0x30. With a cast and shift we can do it easily... - * - * Note about sanity check: if value is greater than 0x0040 we are trying to - * turn on both RX and TX, which is NOT good. - */ - - uint16_t value = ((uint16_t) mode) << 5; - if(value > 0x0040) return; - _maskSetRegister(0x30, 0x0060, value); -} - -void AT1846S_enableTxCtcss(tone_t freq) -{ - i2c_writeReg16(0x4A, freq*10); - i2c_writeReg16(0x4B, 0x0000); - i2c_writeReg16(0x4C, 0x0000); - _maskSetRegister(0x4E, 0x0600, 0x0600); -} - -void AT1846S_disableCtcss() -{ - i2c_writeReg16(0x4A, 0x0000); - _maskSetRegister(0x4E, 0x0600, 0x0000); /* Disable TX CTCSS */ -} - -uint16_t AT1846S_readRSSI() -{ - return i2c_readReg16(0x1B); -} - -void AT1846S_setPgaGain(const uint8_t gain) -{ - uint16_t pga = (gain & 0x1F) << 6; - _maskSetRegister(0x0A, 0x07C0, pga); -} - -void AT1846S_setMicGain(const uint8_t gain) -{ - _maskSetRegister(0x41, 0x007F, ((uint16_t) gain)); -} - -void AT1846S_setAgcGain(const uint8_t gain) -{ - uint16_t agc = (gain & 0x0F) << 8; - _maskSetRegister(0x44, 0x0F00, agc); -} - -void AT1846S_setTxDeviation(const uint16_t dev) -{ - uint16_t value = (dev & 0x03FF) << 6; - _maskSetRegister(0x59, 0xFFC0, value); -} - -void AT1846S_setRxAudioGain(const uint8_t gainWb, const uint8_t gainNb) -{ - uint16_t value = (gainWb & 0x0F) << 8; - _maskSetRegister(0x44, 0x0F00, value); - _maskSetRegister(0x44, 0x000F, ((uint16_t) gainNb)); -} - -void AT1846S_setNoise1Thresholds(const uint8_t highTsh, const uint8_t lowTsh) -{ - uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); - i2c_writeReg16(0x48, value); -} - -void AT1846S_setNoise2Thresholds(const uint8_t highTsh, const uint8_t lowTsh) -{ - uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); - i2c_writeReg16(0x60, value); -} - -void AT1846S_setRssiThresholds(const uint8_t highTsh, const uint8_t lowTsh) -{ - uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); - i2c_writeReg16(0x3F, value); -} - -void AT1846S_setPaDrive(const uint8_t value) -{ - uint16_t pa = value << 11; - _maskSetRegister(0x0A, 0x7800, pa); -} - -void AT1846S_setAnalogSqlThresh(const uint8_t thresh) -{ - i2c_writeReg16(0x49, ((uint16_t) thresh)); -} diff --git a/platform/drivers/baseband/AT1846S.h b/platform/drivers/baseband/AT1846S.h index 78b28b3e..b7b7a012 100644 --- a/platform/drivers/baseband/AT1846S.h +++ b/platform/drivers/baseband/AT1846S.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * @@ -26,151 +26,311 @@ #include /** - * \enum AT1846S_bw_t Enumeration type defining the bandwidth settings supported - * by the AT1846S chip. + * Enumeration type defining the bandwidth settings supported by the AT1846S chip. */ -typedef enum +enum class AT1846S_BW : uint8_t { - AT1846S_BW_12P5 = 0, - AT1846S_BW_25 = 1 -} -AT1846S_bw_t; + _12P5 = 0, ///< 12.5kHz bandwidth. + _25 = 1 ///< 25kHz bandwidth. +}; /** - * \enum AT1846S_op_t Enumeration type defining the possible operating mode - * configurations for the AT1846S chip. + * Enumeration type defining the possible operating mode configurations for the + * AT1846S chip. */ -typedef enum +enum class AT1846S_OpMode : uint8_t { - AT1846S_OP_FM = 0, - AT1846S_OP_DMR = 1 -} -AT1846S_op_t; + FM = 0, ///< Analog FM operation. + DMR = 1 ///< DMR operation. +}; /** - * \enum AT1846S_func_t Enumeration type defining the AT1846S functional modes. + * Enumeration type defining the AT1846S functional modes. */ -typedef enum +enum class AT1846S_FuncMode : uint8_t { - AT1846S_OFF = 0, - AT1846S_RX = 1, - AT1846S_TX = 2, -} -AT1846S_func_t; + OFF = 0, ///< Both TX and RX off. + RX = 1, ///< RX enabled. + TX = 2, ///< TX enabled. +}; /** - * Initialise the AT146S chip. + * Low-level driver for AT1846S "radio on a chip" integrated circuit. */ -void AT1846S_init(); -/** - * Shut down the AT146S chip. - */ -void AT1846S_terminate(); +class AT1846S +{ +public: -/** - * Set the VCO frequency, either for transmission or reception. - * @param freq: VCO frequency. - */ -void AT1846S_setFrequency(const freq_t freq); + /** + * \return a reference to the instance of the AT1846S class (singleton). + */ + static AT1846S& instance() + { + static AT1846S instance; + return instance; + } -/** - * Set the transmission and reception bandwidth. - * @param band: bandwidth, from \enum AT1846S_bw_t. - */ -void AT1846S_setBandwidth(const AT1846S_bw_t band); + /** + * Destructor. + * When called it implicitly shuts down the AT146S chip. + */ + ~AT1846S() + { + terminate(); + } -/** - * Set the operating mode. - * @param mode: operating mode, from \enum AT1846S_op_t. - */ -void AT1846S_setOpMode(const AT1846S_op_t mode); + /** + * Initialise the AT146S chip. + */ + void init(); -/** - * Set the functional mode. - * @param mode: functional mode, from \enum AT1846S_func_t. - */ -void AT1846S_setFuncMode(const AT1846S_func_t mode); + /** + * Shut down the AT146S chip. + */ + inline void terminate() + { + disableCtcss(); + setFuncMode(AT1846S_FuncMode::OFF); + } -/** - * Enable the CTCSS tone for transmission. - * @param freq: CTCSS tone frequency. - */ -void AT1846S_enableTxCtcss(const tone_t freq); + /** + * Set the VCO frequency, either for transmission or reception. + * @param freq: VCO frequency. + */ + void setFrequency(const freq_t freq) + { + // The value to be written in registers is given by: 0.0016*freqency + uint32_t val = (freq/1000)*16; + uint16_t fHi = (val >> 16) & 0xFFFF; + uint16_t fLo = val & 0xFFFF; -/** - * Turn off both transmission CTCSS tone and reception CTCSS tone decoding. - */ -void AT1846S_disableCtcss(); + i2c_writeReg16(0x29, fHi); + i2c_writeReg16(0x2A, fLo); -/** - * Get current RSSI value. The raw value from the RSSI register is returned. - * @return current RSSI. - */ -uint16_t AT1846S_readRSSI(); + reloadConfig(); + } -/** - * Set the gain of internal programmable gain amplifier. - * @param gain: PGA gain. - */ -void AT1846S_setPgaGain(const uint8_t gain); + /** + * Set the transmission and reception bandwidth. + * @param band: bandwidth. + */ + void setBandwidth(const AT1846S_BW band); -/** - * Set microphone gain for transmission. - * @param gain: microphone gain. - */ -void AT1846S_setMicGain(const uint8_t gain); + /** + * Set the operating mode. + * @param mode: operating mode. + */ + void setOpMode(const AT1846S_OpMode mode); -/** - * Set maximum FM transmission deviation. - * @param dev: maximum allowed deviation. - */ -void AT1846S_setTxDeviation(const uint16_t dev); + /** + * Set the functional mode. + * @param mode: functional mode. + */ + void setFuncMode(const AT1846S_FuncMode mode) + { + /* + * Functional mode is controlled by bits 5 (RX on) and 6 (TX on) in + * register 0x30. With a cast and shift we can set it easily. + */ -/** - * Set the gain for internal automatic gain control system. - * @param gain: AGC gain. - */ -void AT1846S_setAgcGain(const uint8_t gain); + uint16_t value = static_cast< uint16_t >(mode) << 5; + maskSetRegister(0x30, 0x0060, value); + } -/** - * Set audio gain for recepion. - * @param gainWb: gain for wideband Rx (25kHz). - * @param gainNb: gain for narrowband Rx (12.5kHz). - */ -void AT1846S_setRxAudioGain(const uint8_t gainWb, const uint8_t gainNb); + /** + * Enable the CTCSS tone for transmission. + * @param freq: CTCSS tone frequency. + */ + void enableTxCtcss(const tone_t freq) + { + i2c_writeReg16(0x4A, freq*10); + i2c_writeReg16(0x4B, 0x0000); + i2c_writeReg16(0x4C, 0x0000); + maskSetRegister(0x4E, 0x0600, 0x0600); + } -/** - * Set noise1 thresholds for squelch opening and closing. - * @param highTsh: upper threshold. - * @param lowTsh: lower threshold. - */ -void AT1846S_setNoise1Thresholds(const uint8_t highTsh, const uint8_t lowTsh); + /** + * Turn off both transmission CTCSS tone and reception CTCSS tone decoding. + */ + inline void disableCtcss() + { + i2c_writeReg16(0x4A, 0x0000); + maskSetRegister(0x4E, 0x0600, 0x0000); // Disable TX CTCSS + } -/** - * Set noise2 thresholds for squelch opening and closing. - * @param highTsh: upper threshold. - * @param lowTsh: lower threshold. - */ -void AT1846S_setNoise2Thresholds(const uint8_t highTsh, const uint8_t lowTsh); + /** + * Get current RSSI value. + * @return current RSSI in dBm. + */ + inline int16_t readRSSI() + { + // RSSI value is contained in the upper 8 bits of register 0x1B. + return -137 + static_cast< int16_t >(i2c_readReg16(0x1B) >> 8); + } -/** - * Set RSSI thresholds for squelch opening and closing. - * @param highTsh: upper threshold. - * @param lowTsh: lower threshold. - */ -void AT1846S_setRssiThresholds(const uint8_t highTsh, const uint8_t lowTsh); + /** + * Set the gain of internal programmable gain amplifier. + * @param gain: PGA gain. + */ + inline void setPgaGain(const uint8_t gain) + { + uint16_t pga = (gain & 0x1F) << 6; + maskSetRegister(0x0A, 0x07C0, pga); + } -/** - * Set PA drive control bits. - * @param value: PA drive value. - */ -void AT1846S_setPaDrive(const uint8_t value); + /** + * Set microphone gain for transmission. + * @param gain: microphone gain. + */ + inline void setMicGain(const uint8_t gain) + { + maskSetRegister(0x41, 0x007F, ((uint16_t) gain)); + } -/** - * Set threshold for analog FM squelch opening. - * @param thresh: squelch threshold. - */ -void AT1846S_setAnalogSqlThresh(const uint8_t thresh); + /** + * Set maximum FM transmission deviation. + * @param dev: maximum allowed deviation. + */ + inline void setTxDeviation(const uint16_t dev) + { + uint16_t value = (dev & 0x03FF) << 6; + maskSetRegister(0x59, 0xFFC0, value); + } + + /** + * Set the gain for internal automatic gain control system. + * @param gain: AGC gain. + */ + inline void setAgcGain(const uint8_t gain) + { + uint16_t agc = (gain & 0x0F) << 8; + maskSetRegister(0x44, 0x0F00, agc); + } + + /** + * Set audio gain for recepion. + * @param gainWb: gain for wideband Rx (25kHz). + * @param gainNb: gain for narrowband Rx (12.5kHz). + */ + inline void setRxAudioGain(const uint8_t gainWb, const uint8_t gainNb) + { + uint16_t value = (gainWb & 0x0F) << 8; + maskSetRegister(0x44, 0x0F00, value); + maskSetRegister(0x44, 0x000F, ((uint16_t) gainNb)); + } + + /** + * Set noise1 thresholds for squelch opening and closing. + * @param highTsh: upper threshold. + * @param lowTsh: lower threshold. + */ + inline void setNoise1Thresholds(const uint8_t highTsh, const uint8_t lowTsh) + { + uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); + i2c_writeReg16(0x48, value); + } + + /** + * Set noise2 thresholds for squelch opening and closing. + * @param highTsh: upper threshold. + * @param lowTsh: lower threshold. + */ + inline void setNoise2Thresholds(const uint8_t highTsh, const uint8_t lowTsh) + { + uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); + i2c_writeReg16(0x60, value); + } + + /** + * Set RSSI thresholds for squelch opening and closing. + * @param highTsh: upper threshold. + * @param lowTsh: lower threshold. + */ + inline void setRssiThresholds(const uint8_t highTsh, const uint8_t lowTsh) + { + uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); + i2c_writeReg16(0x3F, value); + } + + /** + * Set PA drive control bits. + * @param value: PA drive value. + */ + inline void setPaDrive(const uint8_t value) + { + uint16_t pa = value << 11; + maskSetRegister(0x0A, 0x7800, pa); + } + + /** + * Set threshold for analog FM squelch opening. + * @param thresh: squelch threshold. + */ + inline void setAnalogSqlThresh(const uint8_t thresh) + { + i2c_writeReg16(0x49, ((uint16_t) thresh)); + } + +private: + + /** + * Constructor. + */ + AT1846S() + { + i2c_init(); + } + + /** + * Helper function to set/clear some specific bits in a register. + * + * @param reg: address of the register to be changed. + * @param mask: bitmask to select which bits to change. To modify the i-th + * bit in the register, set its value to "1" in the bitmask. + * @param value: New value for the masked bits. + */ + inline void maskSetRegister(const uint8_t reg, const uint16_t mask, + const uint16_t value) + { + uint16_t regVal = i2c_readReg16(reg); + regVal = (regVal & ~mask) | (value & mask); + i2c_writeReg16(reg, regVal); + } + + /** + * Helper function to be called to make effective some of the AT1846S + * configuration, when changed with TX or RX active. + * It has been observed that, to make effective a change in some of the main + * AT1846S parameters, the chip must be "power cycled" by turning it off and + * then switching back the previous functionality. + */ + inline void reloadConfig() + { + uint16_t funcMode = i2c_readReg16(0x30) & 0x0060; // Get current op. status + maskSetRegister(0x30, 0x0060, 0x0000); // RX and TX off + maskSetRegister(0x30, 0x0060, funcMode); // Restore op. status + } + + /** + * Initialise the I2C interface. + */ + void i2c_init(); + + /** + * Write one register via I2C interface. + * + * @param reg: address of the register to be written. + * @param value: value to be written to the register. + */ + void i2c_writeReg16(const uint8_t reg, const uint16_t value); + + /** + * Read one register via I2C interface. + * + * @param reg: address of the register to be read. + */ + uint16_t i2c_readReg16(const uint8_t reg); +}; #endif /* AT1846S_H */ diff --git a/platform/drivers/baseband/AT1846S_GDx.cpp b/platform/drivers/baseband/AT1846S_GDx.cpp new file mode 100644 index 00000000..cd9360e8 --- /dev/null +++ b/platform/drivers/baseband/AT1846S_GDx.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + * Copyright (C) 2021 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 "AT1846S.h" + +void AT1846S::init() +{ + i2c_writeReg16(0x30, 0x0001); // Soft reset + delayMs(50); + + i2c_writeReg16(0x30, 0x0004); // Chip enable + i2c_writeReg16(0x04, 0x0FD0); // 26MHz crystal frequency + i2c_writeReg16(0x1F, 0x1000); // Gpio6 squelch output + i2c_writeReg16(0x09, 0x03AC); + i2c_writeReg16(0x24, 0x0001); + i2c_writeReg16(0x31, 0x0031); + i2c_writeReg16(0x33, 0x45F5); // AGC number + i2c_writeReg16(0x34, 0x2B89); // RX digital gain + i2c_writeReg16(0x3F, 0x3263); // RSSI 3 threshold + i2c_writeReg16(0x41, 0x470F); // Tx digital gain + i2c_writeReg16(0x42, 0x1036); + i2c_writeReg16(0x43, 0x00BB); + i2c_writeReg16(0x44, 0x06FF); // Tx digital gain + i2c_writeReg16(0x47, 0x7F2F); // Soft mute + i2c_writeReg16(0x4E, 0x0082); + i2c_writeReg16(0x4F, 0x2C62); + i2c_writeReg16(0x53, 0x0094); + i2c_writeReg16(0x54, 0x2A3C); + i2c_writeReg16(0x55, 0x0081); + i2c_writeReg16(0x56, 0x0B02); + i2c_writeReg16(0x57, 0x1C00); // Bypass RSSI low-pass + i2c_writeReg16(0x5A, 0x4935); // SQ detection time + i2c_writeReg16(0x58, 0xBCCD); + i2c_writeReg16(0x62, 0x3263); // Modulation detect tresh + i2c_writeReg16(0x4E, 0x2082); + i2c_writeReg16(0x63, 0x16AD); + i2c_writeReg16(0x30, 0x40A4); + delayMs(50); + + i2c_writeReg16(0x30, 0x40A6); // Start calibration + delayMs(100); + i2c_writeReg16(0x30, 0x4006); // Stop calibration + + delayMs(100); + + i2c_writeReg16(0x58, 0xBCED); + i2c_writeReg16(0x0A, 0x7BA0); // PGA gain + i2c_writeReg16(0x41, 0x4731); // Tx digital gain + i2c_writeReg16(0x44, 0x05FF); // Tx digital gain + i2c_writeReg16(0x59, 0x09D2); // Mixer gain + i2c_writeReg16(0x44, 0x05CF); // Tx digital gain + i2c_writeReg16(0x44, 0x05CC); // Tx digital gain + i2c_writeReg16(0x48, 0x1A32); // Noise 1 threshold + i2c_writeReg16(0x60, 0x1A32); // Noise 2 threshold + i2c_writeReg16(0x3F, 0x29D1); // RSSI 3 threshold + i2c_writeReg16(0x0A, 0x7BA0); // PGA gain + i2c_writeReg16(0x49, 0x0C96); // RSSI SQL thresholds + i2c_writeReg16(0x33, 0x45F5); // AGC number + i2c_writeReg16(0x41, 0x470F); // Tx digital gain + i2c_writeReg16(0x42, 0x1036); + i2c_writeReg16(0x43, 0x00BB); +} + +void AT1846S::setBandwidth(const AT1846S_BW band) +{ + if(band == AT1846S_BW::_25) + { + // 25kHz bandwidth + i2c_writeReg16(0x15, 0x1F00); // Tuning bit + i2c_writeReg16(0x32, 0x7564); // AGC target power + i2c_writeReg16(0x3A, 0x44C3); // Modulation detect sel + i2c_writeReg16(0x3F, 0x29D2); // RSSI 3 threshold + i2c_writeReg16(0x3C, 0x0E1C); // Peak detect threshold + i2c_writeReg16(0x48, 0x1E38); // Noise 1 threshold + i2c_writeReg16(0x62, 0x3767); // Modulation detect tresh + i2c_writeReg16(0x65, 0x248A); + i2c_writeReg16(0x66, 0xFF2E); // RSSI comp and AFC range + i2c_writeReg16(0x7F, 0x0001); // Switch to page 1 + i2c_writeReg16(0x06, 0x0024); // AGC gain table + i2c_writeReg16(0x07, 0x0214); + i2c_writeReg16(0x08, 0x0224); + i2c_writeReg16(0x09, 0x0314); + i2c_writeReg16(0x0A, 0x0324); + i2c_writeReg16(0x0B, 0x0344); + i2c_writeReg16(0x0D, 0x1384); + i2c_writeReg16(0x0E, 0x1B84); + i2c_writeReg16(0x0F, 0x3F84); + i2c_writeReg16(0x12, 0xE0EB); + i2c_writeReg16(0x7F, 0x0000); // Back to page 0 + maskSetRegister(0x30, 0x3000, 0x3000); + } + else + { + // 12.5kHz bandwidth + i2c_writeReg16(0x15, 0x1100); // Tuning bit + i2c_writeReg16(0x32, 0x4495); // AGC target power + i2c_writeReg16(0x3A, 0x40C3); // Modulation detect sel + i2c_writeReg16(0x3F, 0x28D0); // RSSI 3 threshold + i2c_writeReg16(0x3C, 0x0F1E); // Peak detect threshold + i2c_writeReg16(0x48, 0x1DB6); // Noise 1 threshold + i2c_writeReg16(0x62, 0x1425); // Modulation detect tresh + i2c_writeReg16(0x65, 0x2494); + i2c_writeReg16(0x66, 0xEB2E); // RSSI comp and AFC range + i2c_writeReg16(0x7F, 0x0001); // Switch to page 1 + i2c_writeReg16(0x06, 0x0014); // AGC gain table + i2c_writeReg16(0x07, 0x020C); + i2c_writeReg16(0x08, 0x0214); + i2c_writeReg16(0x09, 0x030C); + i2c_writeReg16(0x0A, 0x0314); + i2c_writeReg16(0x0B, 0x0324); + i2c_writeReg16(0x0C, 0x0344); + i2c_writeReg16(0x0D, 0x1344); + i2c_writeReg16(0x0E, 0x1B44); + i2c_writeReg16(0x0F, 0x3F44); + i2c_writeReg16(0x12, 0xE0EB); // Back to page 0 + i2c_writeReg16(0x7F, 0x0000); + maskSetRegister(0x30, 0x3000, 0x0000); + } + + reloadConfig(); +} + +void AT1846S::setOpMode(const AT1846S_OpMode mode) +{ + if(mode == AT1846S_OpMode::DMR) + { + // DMR mode + i2c_writeReg16(0x3A, 0x00C2); + i2c_writeReg16(0x33, 0x45F5); + i2c_writeReg16(0x41, 0x4731); + i2c_writeReg16(0x42, 0x1036); + i2c_writeReg16(0x43, 0x00BB); + i2c_writeReg16(0x58, 0xBCFD); + i2c_writeReg16(0x44, 0x06CC); + i2c_writeReg16(0x40, 0x0031); + } + else + { + // FM mode + i2c_writeReg16(0x33, 0x44A5); + i2c_writeReg16(0x41, 0x4431); + i2c_writeReg16(0x42, 0x10F0); + i2c_writeReg16(0x43, 0x00A9); + i2c_writeReg16(0x58, 0xBC05); + i2c_writeReg16(0x44, 0x06FF); + i2c_writeReg16(0x40, 0x0030); + + maskSetRegister(0x57, 0x0001, 0x00); // Audio feedback off + maskSetRegister(0x3A, 0x7000, 0x4000); // Select voice channel + } + + reloadConfig(); +} + +/* + * Implementation of AT1846S I2C interface. + * + * NOTE: on GDx devices the I2C bus is shared between the EEPROM and the AT1846S, + * so we have to acquire exclusive ownership before exchanging data +*/ + +static constexpr uint8_t devAddr = 0xE2; + +void AT1846S::i2c_init() +{ + // I2C already init'd by platform support package +} + +void AT1846S::i2c_writeReg16(uint8_t reg, uint16_t value) +{ + /* + * Beware of endianness! + * When writing an AT1846S register, bits 15:8 must be sent first, followed + * by bits 7:0. + */ + + uint8_t buf[3]; + buf[0] = reg; + buf[1] = (value >> 8) & 0xFF; + buf[2] = value & 0xFF; + + i2c0_lockDeviceBlocking(); + i2c0_write(devAddr, buf, 3, true); + i2c0_releaseDevice(); +} + +uint16_t AT1846S::i2c_readReg16(uint8_t reg) +{ + uint16_t value = 0; + + i2c0_lockDeviceBlocking(); + i2c0_write(devAddr, ®, 1, false); + i2c0_read(devAddr, &value, 2); + i2c0_releaseDevice(); + + // Correction for AT1846S sending register data in big endian format + return __builtin_bswap16(value); +} diff --git a/platform/drivers/baseband/AT1846S_UV3x0.c b/platform/drivers/baseband/AT1846S_UV3x0.cpp similarity index 61% rename from platform/drivers/baseband/AT1846S_UV3x0.c rename to platform/drivers/baseband/AT1846S_UV3x0.cpp index dfac9521..6903a9fe 100644 --- a/platform/drivers/baseband/AT1846S_UV3x0.c +++ b/platform/drivers/baseband/AT1846S_UV3x0.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * @@ -18,35 +18,14 @@ * along with this program; if not, see * ***************************************************************************/ +#include #include -#include "interfaces.h" +#include #include "AT1846S.h" -void _maskSetRegister(uint8_t reg, uint16_t mask, uint16_t value) +void AT1846S::init() { - uint16_t regVal = i2c_readReg16(reg); - regVal = (regVal & ~mask) | (value & mask); - i2c_writeReg16(reg, regVal); -} - -/* - * NOTE: after some main AT1846S parameters (frequency, bandwidth, ...) are changed, - * the chip must be "cycled" to make them effective. Cycling consists of turning - * both TX and RX off and then switch back to the desired functionality. - * - * The function _reloadConfig() provides an helper to do this. - */ -static inline void _reloadConfig() -{ - uint16_t reg = i2c_readReg16(0x30); /* Get current op. status */ - i2c_writeReg16(0x30, reg & 0xFF9F); /* RX and TX off */ - i2c_writeReg16(0x30, reg); /* Restore op. status */ -} - - -void AT1846S_init() -{ - i2c_writeReg16(0x30, 0x0001); /* Soft reset */ + i2c_writeReg16(0x30, 0x0001); // Soft reset delayMs(160); i2c_writeReg16(0x30, 0x0004); // Set pdn_reg (power down pin) @@ -106,39 +85,19 @@ void AT1846S_init() // and set xtal_mode to 26MHz/13MHz delayMs(160); - i2c_writeReg16(0x30, 0x40A6); /* Start calibration */ + i2c_writeReg16(0x30, 0x40A6); // Start calibration delayMs(160); - i2c_writeReg16(0x30, 0x4006); /* Stop calibration */ + i2c_writeReg16(0x30, 0x4006); // Stop calibration delayMs(160); i2c_writeReg16(0x40, 0x0031); } -void AT1846S_terminate() +void AT1846S::setBandwidth(const AT1846S_BW band) { - AT1846S_disableCtcss(); - AT1846S_setFuncMode(AT1846S_OFF); -} - -void AT1846S_setFrequency(const freq_t freq) -{ - /* The value to be written in registers is given by: 0.0016*freqency */ - uint32_t val = (freq/1000)*16; - uint16_t fHi = (val >> 16) & 0xFFFF; - uint16_t fLo = val & 0xFFFF; - - i2c_writeReg16(0x05, 0x8763); - i2c_writeReg16(0x29, fHi); - i2c_writeReg16(0x2A, fLo); - - _reloadConfig(); -} - -void AT1846S_setBandwidth(const AT1846S_bw_t band) -{ - if(band == AT1846S_BW_25) + if(band == AT1846S_BW::_25) { - /* 25kHz bandwidth */ + // 25kHz bandwidth i2c_writeReg16(0x15, 0x1F00); i2c_writeReg16(0x32, 0x7564); i2c_writeReg16(0x3A, 0x04C3); @@ -162,11 +121,11 @@ void AT1846S_setBandwidth(const AT1846S_bw_t band) i2c_writeReg16(0x0F, 0x3F84); i2c_writeReg16(0x12, 0xE0EB); i2c_writeReg16(0x7F, 0x0000); - _maskSetRegister(0x30, 0x3000, 0x3000); + maskSetRegister(0x30, 0x3000, 0x3000); } else { - /* 12.5kHz bandwidth */ + // 12.5kHz bandwidth i2c_writeReg16(0x15, 0x1100); i2c_writeReg16(0x32, 0x4495); i2c_writeReg16(0x3A, 0x00C3); @@ -190,119 +149,208 @@ void AT1846S_setBandwidth(const AT1846S_bw_t band) i2c_writeReg16(0x0F, 0x3F44); i2c_writeReg16(0x12, 0xE0EB); i2c_writeReg16(0x7F, 0x0000); - _maskSetRegister(0x30, 0x3000, 0x0000); + maskSetRegister(0x30, 0x3000, 0x0000); } - _reloadConfig(); + reloadConfig(); } -void AT1846S_setOpMode(const AT1846S_op_t mode) +void AT1846S::setOpMode(const AT1846S_OpMode mode) { - if(mode == AT1846S_OP_DMR) + if(mode == AT1846S_OpMode::DMR) { - /* TODO: DMR mode */ + // TODO: DMR mode } else { - /* FM mode */ + // FM mode i2c_writeReg16(0x58, 0x9C1D); i2c_writeReg16(0x40, 0x0030); } - _reloadConfig(); + reloadConfig(); } -void AT1846S_setFuncMode(const AT1846S_func_t mode) +/* + * Implementation of AT1846S I2C interface. + * + * On MD-UV3x0 the I2C interface towars the AT1846S is not connected to any + * hardware peripheral and has to be implemented in software by bit-banging. + */ + +void _i2c_start(); +void _i2c_stop(); +void _i2c_write(uint8_t val); +uint8_t _i2c_read(bool ack); + +static constexpr uint8_t devAddr = 0x5C; + +void AT1846S::i2c_init() +{ + gpio_setMode(I2C_SDA, INPUT); + gpio_setMode(I2C_SCL, OUTPUT); + gpio_clearPin(I2C_SCL); +} + +void AT1846S::i2c_writeReg16(uint8_t reg, uint16_t value) { /* - * Functional mode is controlled by bits 5 (RX on) and 6 (TX on) in register - * 0x30. With a cast and shift we can do it easily... - * - * Note about sanity check: if value is greater than 0x0040 we are trying to - * turn on both RX and TX, which is NOT good. + * Beware of endianness! + * When writing an AT1846S register, bits 15:8 must be sent first, followed + * by bits 7:0. */ + uint8_t valHi = (value >> 8) & 0xFF; + uint8_t valLo = value & 0xFF; - uint16_t value = ((uint16_t) mode) << 5; - if(value > 0x0040) return; - _maskSetRegister(0x30, 0x0060, value); -// i2c_writeReg16(0x44, 0x4ff); + _i2c_start(); + _i2c_write(devAddr); + _i2c_write(reg); + _i2c_write(valHi); + _i2c_write(valLo); + _i2c_stop(); } -void AT1846S_enableTxCtcss(tone_t freq) +uint16_t AT1846S::i2c_readReg16(uint8_t reg) { - i2c_writeReg16(0x4A, freq*10); - i2c_writeReg16(0x4B, 0x0000); - i2c_writeReg16(0x4C, 0x0000); - _maskSetRegister(0x4E, 0x0600, 0x0600); + _i2c_start(); + _i2c_write(devAddr); + _i2c_write(reg); + _i2c_start(); + _i2c_write(devAddr | 0x01); + uint8_t valHi = _i2c_read(true); + uint8_t valLo = _i2c_read(false); + _i2c_stop(); + + return (valHi << 8) | valLo; } -void AT1846S_disableCtcss() +/* + * Software I2C routine + */ + +void _i2c_start() { - i2c_writeReg16(0x4A, 0x0000); - _maskSetRegister(0x4E, 0x0600, 0x0000); /* Disable TX CTCSS */ + gpio_setMode(I2C_SDA, OUTPUT); + + /* + * Lines commented to keep SCL high when idle + * + gpio_clearPin(I2C_SCL); + delayUs(2); + */ + + gpio_setPin(I2C_SDA); + delayUs(2); + + gpio_setPin(I2C_SCL); + delayUs(2); + + gpio_clearPin(I2C_SDA); + delayUs(2); + + gpio_clearPin(I2C_SCL); + delayUs(6); } -uint16_t AT1846S_readRSSI() +void _i2c_stop() { - return i2c_readReg16(0x1B); + gpio_setMode(I2C_SDA, OUTPUT); + + gpio_clearPin(I2C_SCL); + delayUs(2); + + gpio_clearPin(I2C_SDA); + delayUs(2); + + gpio_setPin(I2C_SCL); + delayUs(2); + + gpio_setPin(I2C_SDA); + delayUs(2); + + /* + * Lines commented to keep SCL high when idle + * + gpio_clearPin(I2C_SCL); + delayUs(2); + */ } -void AT1846S_setPgaGain(const uint8_t gain) +void _i2c_write(uint8_t val) { - uint16_t pga = (gain & 0x1F) << 6; - _maskSetRegister(0x0A, 0x07C0, pga); + gpio_setMode(I2C_SDA, OUTPUT); + + for(uint8_t i = 0; i < 8; i++) + { + gpio_clearPin(I2C_SCL); + delayUs(1); + + if(val & 0x80) + { + gpio_setPin(I2C_SDA); + } + else + { + gpio_clearPin(I2C_SDA); + } + + val <<= 1; + delayUs(1); + gpio_setPin(I2C_SCL); + delayUs(2); + } + + /* Ensure SCL is low before releasing SDA */ + gpio_clearPin(I2C_SCL); + + /* Clock cycle for slave ACK/NACK */ + gpio_setMode(I2C_SDA, INPUT_PULL_UP); + delayUs(2); + gpio_setPin(I2C_SCL); + delayUs(2); + gpio_clearPin(I2C_SCL); + delayUs(1); + + /* Asserting SDA pin allows to fastly bring the line to idle state */ + gpio_setPin(I2C_SDA); + gpio_setMode(I2C_SDA, OUTPUT); + delayUs(6); } -void AT1846S_setMicGain(const uint8_t gain) +uint8_t _i2c_read(bool ack) { - _maskSetRegister(0x41, 0x007F, ((uint16_t) gain)); -} + gpio_setMode(I2C_SDA, INPUT_PULL_UP); + gpio_clearPin(I2C_SCL); -void AT1846S_setAgcGain(const uint8_t gain) -{ - uint16_t agc = (gain & 0x0F) << 8; - _maskSetRegister(0x44, 0x0F00, agc); -} + uint8_t value = 0; + for(uint8_t i = 0; i < 8; i++) + { + delayUs(2); + gpio_setPin(I2C_SCL); + delayUs(2); -void AT1846S_setTxDeviation(const uint16_t dev) -{ - uint16_t value = (dev & 0x03FF) << 6; - _maskSetRegister(0x59, 0xFFC0, value); -} + value <<= 1; + value |= gpio_readPin(I2C_SDA); -void AT1846S_setRxAudioGain(const uint8_t gainWb, const uint8_t gainNb) -{ - uint16_t value = (gainWb & 0x0F) << 8; - _maskSetRegister(0x44, 0x0F00, value); - _maskSetRegister(0x44, 0x000F, ((uint16_t) gainNb)); -} + gpio_clearPin(I2C_SCL); + } -void AT1846S_setNoise1Thresholds(const uint8_t highTsh, const uint8_t lowTsh) -{ - uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); - i2c_writeReg16(0x48, value); -} + /* + * Set ACK/NACK state BEFORE putting SDA gpio to output mode. + * This avoids spurious spikes which can be interpreted as NACKs + */ + gpio_clearPin(I2C_SDA); + gpio_setMode(I2C_SDA, OUTPUT); + delayUs(2); + if(!ack) gpio_setPin(I2C_SDA); -void AT1846S_setNoise2Thresholds(const uint8_t highTsh, const uint8_t lowTsh) -{ - uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); - i2c_writeReg16(0x60, value); -} + /* Clock cycle for ACK/NACK */ + delayUs(2); + gpio_setPin(I2C_SCL); + delayUs(2); + gpio_clearPin(I2C_SCL); + delayUs(2); -void AT1846S_setRssiThresholds(const uint8_t highTsh, const uint8_t lowTsh) -{ - uint16_t value = ((highTsh & 0x1f) << 8) | (lowTsh & 0x1F); - i2c_writeReg16(0x3F, value); -} - -void AT1846S_setPaDrive(const uint8_t value) -{ - uint16_t pa = value << 11; - _maskSetRegister(0x0A, 0x7800, pa); -} - -void AT1846S_setAnalogSqlThresh(const uint8_t thresh) -{ - i2c_writeReg16(0x49, ((uint16_t) thresh)); + return value; } diff --git a/platform/drivers/baseband/AT1846S_wrapper.cpp b/platform/drivers/baseband/AT1846S_wrapper.cpp new file mode 100644 index 00000000..7335e0f9 --- /dev/null +++ b/platform/drivers/baseband/AT1846S_wrapper.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (C) 2021 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 "new/AT1846S.h" +#include "AT1846S.h" + +void AT1846S_init() +{ + AT1846S::instance().init(); +} + +void AT1846S_terminate() +{ + AT1846S::instance().terminate(); +} + +void AT1846S_setFrequency(const freq_t freq) +{ + AT1846S::instance().setFrequency(freq); +} + +void AT1846S_setBandwidth(const AT1846S_bw_t band) +{ + // Conversion is safe: the fields in AT1846S_bw_t enum and in AT1846S_BW + // enum class concide. + AT1846S::instance().setBandwidth(static_cast< AT1846S_BW >(band)); +} + +void AT1846S_setOpMode(const AT1846S_op_t mode) +{ + // Conversion is safe: the fields in AT1846S_op_t enum and in AT1846S_OpMode + // enum class concide. + AT1846S::instance().setOpMode(static_cast< AT1846S_OpMode >(mode)); +} + +void AT1846S_setFuncMode(const AT1846S_func_t mode) +{ + // Conversion is safe: the fields in AT1846S_func_t enum and in + // AT1846S_FuncMode enum class concide. + AT1846S::instance().setFuncMode(static_cast< AT1846S_FuncMode >(mode)); +} + +void AT1846S_enableTxCtcss(tone_t freq) +{ + AT1846S::instance().enableTxCtcss(freq); +} + +void AT1846S_disableCtcss() +{ + AT1846S::instance().disableCtcss(); +} + +int16_t AT1846S_readRSSI() +{ + return AT1846S::instance().readRSSI(); +} + +void AT1846S_setPgaGain(const uint8_t gain) +{ + AT1846S::instance().setPgaGain(gain); +} + +void AT1846S_setMicGain(const uint8_t gain) +{ + AT1846S::instance().setMicGain(gain); +} + +void AT1846S_setAgcGain(const uint8_t gain) +{ + AT1846S::instance().setAgcGain(gain); +} + +void AT1846S_setTxDeviation(const uint16_t dev) +{ + AT1846S::instance().setTxDeviation(dev); +} + +void AT1846S_setRxAudioGain(const uint8_t gainWb, const uint8_t gainNb) +{ + AT1846S::instance().setRxAudioGain(gainWb, gainNb); +} + +void AT1846S_setNoise1Thresholds(const uint8_t highTsh, const uint8_t lowTsh) +{ + AT1846S::instance().setNoise1Thresholds(highTsh, lowTsh); +} + +void AT1846S_setNoise2Thresholds(const uint8_t highTsh, const uint8_t lowTsh) +{ + AT1846S::instance().setNoise2Thresholds(highTsh, lowTsh); +} + +void AT1846S_setRssiThresholds(const uint8_t highTsh, const uint8_t lowTsh) +{ + AT1846S::instance().setRssiThresholds(highTsh, lowTsh); +} + +void AT1846S_setPaDrive(const uint8_t value) +{ + AT1846S::instance().setPaDrive(value); +} + +void AT1846S_setAnalogSqlThresh(const uint8_t thresh) +{ + AT1846S::instance().setAnalogSqlThresh(thresh); +} diff --git a/platform/drivers/baseband/AT1846S_wrapper.h b/platform/drivers/baseband/AT1846S_wrapper.h new file mode 100644 index 00000000..33caf450 --- /dev/null +++ b/platform/drivers/baseband/AT1846S_wrapper.h @@ -0,0 +1,189 @@ +/*************************************************************************** + * 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 AT1846S_H +#define AT1846S_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This file provides a C-callable wrapper for the AT1846S driver, which is + * written in C++. + */ + +/** + * \enum AT1846S_bw_t Enumeration type defining the bandwidth settings supported + * by the AT1846S chip. + */ +typedef enum +{ + AT1846S_BW_12P5 = 0, + AT1846S_BW_25 = 1 +} +AT1846S_bw_t; + +/** + * \enum AT1846S_op_t Enumeration type defining the possible operating mode + * configurations for the AT1846S chip. + */ +typedef enum +{ + AT1846S_OP_FM = 0, + AT1846S_OP_DMR = 1 +} +AT1846S_op_t; + +/** + * \enum AT1846S_func_t Enumeration type defining the AT1846S functional modes. + */ +typedef enum +{ + AT1846S_OFF = 0, + AT1846S_RX = 1, + AT1846S_TX = 2, +} +AT1846S_func_t; + +/** + * Initialise the AT146S chip. + */ +void AT1846S_init(); + +/** + * Shut down the AT146S chip. + */ +void AT1846S_terminate(); + +/** + * Set the VCO frequency, either for transmission or reception. + * @param freq: VCO frequency. + */ +void AT1846S_setFrequency(const freq_t freq); + +/** + * Set the transmission and reception bandwidth. + * @param band: bandwidth, from \enum AT1846S_bw_t. + */ +void AT1846S_setBandwidth(const AT1846S_bw_t band); + +/** + * Set the operating mode. + * @param mode: operating mode, from \enum AT1846S_op_t. + */ +void AT1846S_setOpMode(const AT1846S_op_t mode); + +/** + * Set the functional mode. + * @param mode: functional mode, from \enum AT1846S_func_t. + */ +void AT1846S_setFuncMode(const AT1846S_func_t mode); + +/** + * Enable the CTCSS tone for transmission. + * @param freq: CTCSS tone frequency. + */ +void AT1846S_enableTxCtcss(const tone_t freq); + +/** + * Turn off both transmission CTCSS tone and reception CTCSS tone decoding. + */ +void AT1846S_disableCtcss(); + +/** + * Get current RSSI value. + * @return current RSSI in dBm. + */ +int16_t AT1846S_readRSSI(); + +/** + * Set the gain of internal programmable gain amplifier. + * @param gain: PGA gain. + */ +void AT1846S_setPgaGain(const uint8_t gain); + +/** + * Set microphone gain for transmission. + * @param gain: microphone gain. + */ +void AT1846S_setMicGain(const uint8_t gain); + +/** + * Set maximum FM transmission deviation. + * @param dev: maximum allowed deviation. + */ +void AT1846S_setTxDeviation(const uint16_t dev); + +/** + * Set the gain for internal automatic gain control system. + * @param gain: AGC gain. + */ +void AT1846S_setAgcGain(const uint8_t gain); + +/** + * Set audio gain for recepion. + * @param gainWb: gain for wideband Rx (25kHz). + * @param gainNb: gain for narrowband Rx (12.5kHz). + */ +void AT1846S_setRxAudioGain(const uint8_t gainWb, const uint8_t gainNb); + +/** + * Set noise1 thresholds for squelch opening and closing. + * @param highTsh: upper threshold. + * @param lowTsh: lower threshold. + */ +void AT1846S_setNoise1Thresholds(const uint8_t highTsh, const uint8_t lowTsh); + +/** + * Set noise2 thresholds for squelch opening and closing. + * @param highTsh: upper threshold. + * @param lowTsh: lower threshold. + */ +void AT1846S_setNoise2Thresholds(const uint8_t highTsh, const uint8_t lowTsh); + +/** + * Set RSSI thresholds for squelch opening and closing. + * @param highTsh: upper threshold. + * @param lowTsh: lower threshold. + */ +void AT1846S_setRssiThresholds(const uint8_t highTsh, const uint8_t lowTsh); + +/** + * Set PA drive control bits. + * @param value: PA drive value. + */ +void AT1846S_setPaDrive(const uint8_t value); + +/** + * Set threshold for analog FM squelch opening. + * @param thresh: squelch threshold. + */ +void AT1846S_setAnalogSqlThresh(const uint8_t thresh); + +#ifdef __cplusplus +} +#endif + +#endif /* AT1846S_H */ diff --git a/platform/drivers/baseband/interfaces.h b/platform/drivers/baseband/interfaces.h index ccdf1240..9f69fcf0 100644 --- a/platform/drivers/baseband/interfaces.h +++ b/platform/drivers/baseband/interfaces.h @@ -30,22 +30,6 @@ * to be platform-agnostic, and the platform-specific communication busses. */ - -/** - * Initialise I2C subsystem. - */ -void i2c_init(); - -/** - * AT1846S specific: write a device register. - */ -void i2c_writeReg16(uint8_t reg, uint16_t value); - -/** - * AT1846S specific: read a device register. - */ -uint16_t i2c_readReg16(uint8_t reg); - /** * HR_C5000 and HR_C6000: initialise "user" SPI interface, the one for chip * configuration. diff --git a/platform/drivers/baseband/interfaces_GDx.c b/platform/drivers/baseband/interfaces_GDx.c index 17b73b16..30de6d34 100644 --- a/platform/drivers/baseband/interfaces_GDx.c +++ b/platform/drivers/baseband/interfaces_GDx.c @@ -27,49 +27,6 @@ #include #include "interfaces.h" -/* - * Implementation of AT1846S I2C interface. - * - * NOTE: on GDx devices the I2C bus is shared between the EEPROM and the AT1846S, - * so we have to acquire exclusive ownership before exchanging data - */ - -static const uint8_t devAddr = 0xE2; - -void i2c_init() -{ - /* I2C already init'd by platform support package */ -} - -void i2c_writeReg16(uint8_t reg, uint16_t value) -{ - /* - * Beware of endianness! - * When writing an AT1846S register, bits 15:8 must be sent first, followed - * by bits 7:0. - */ - uint8_t buf[3]; - buf[0] = reg; - buf[1] = (value >> 8) & 0xFF; - buf[2] = value & 0xFF; - - i2c0_lockDeviceBlocking(); - i2c0_write(devAddr, buf, 3, true); - i2c0_releaseDevice(); -} - -uint16_t i2c_readReg16(uint8_t reg) -{ - uint16_t value = 0; - - i2c0_lockDeviceBlocking(); - i2c0_write(devAddr, ®, 1, false); - i2c0_read(devAddr, &value, 2); - i2c0_releaseDevice(); - - /* Correction for AT1846S sending register data in big endian format */ - return __builtin_bswap16(value); -} /* * Implementation of HR_C6000 "user" SPI interface. diff --git a/platform/drivers/baseband/interfaces_UV3x0.c b/platform/drivers/baseband/interfaces_UV3x0.c index a808268d..2a262d3b 100644 --- a/platform/drivers/baseband/interfaces_UV3x0.c +++ b/platform/drivers/baseband/interfaces_UV3x0.c @@ -27,58 +27,6 @@ #include #include "interfaces.h" -void _i2c_start(); -void _i2c_stop(); -void _i2c_write(uint8_t val); -uint8_t _i2c_read(bool ack); - -/* - * Implementation of AT1846S I2C interface. - * - * NOTE: on GDx devices the I2C bus is shared between the EEPROM and the AT1846S, - * so we have to acquire exclusive ownership before exchanging data - */ - -static const uint8_t devAddr = 0x5C; - -void i2c_init() -{ - gpio_setMode(I2C_SDA, INPUT); - gpio_setMode(I2C_SCL, OUTPUT); - gpio_clearPin(I2C_SCL); -} - -void i2c_writeReg16(uint8_t reg, uint16_t value) -{ - /* - * Beware of endianness! - * When writing an AT1846S register, bits 15:8 must be sent first, followed - * by bits 7:0. - */ - uint8_t valHi = (value >> 8) & 0xFF; - uint8_t valLo = value & 0xFF; - - _i2c_start(); - _i2c_write(devAddr); - _i2c_write(reg); - _i2c_write(valHi); - _i2c_write(valLo); - _i2c_stop(); -} - -uint16_t i2c_readReg16(uint8_t reg) -{ - _i2c_start(); - _i2c_write(devAddr); - _i2c_write(reg); - _i2c_start(); - _i2c_write(devAddr | 0x01); - uint8_t valHi = _i2c_read(true); - uint8_t valLo = _i2c_read(false); - _i2c_stop(); - - return (valHi << 8) | valLo; -} /* * Implementation of HR_C6000 "user" SPI interface. diff --git a/platform/drivers/baseband/radio_GDx.c b/platform/drivers/baseband/radio_GDx.c index 8c9a9516..c90eb364 100644 --- a/platform/drivers/baseband/radio_GDx.c +++ b/platform/drivers/baseband/radio_GDx.c @@ -26,7 +26,7 @@ #include #include #include "HR_C6000.h" -#include "AT1846S.h" +#include "AT1846S_wrapper.h" const gdxCalibration_t *calData; /* Pointer to calibration data */ @@ -311,11 +311,6 @@ float radio_getRssi(const freq_t rxFreq) { (void) rxFreq; - /* - * RSSI and SNR are packed in a 16-bit value, with RSSI being the upper - * eight bits. - */ - uint16_t val = (AT1846S_readRSSI() >> 8); - int16_t rssi = -151 + ((int16_t) val); + int16_t rssi = AT1846S_readRSSI(); return ((float) rssi); } diff --git a/platform/drivers/baseband/radio_UV3x0.c b/platform/drivers/baseband/radio_UV3x0.c index 215ba5cd..b9d9e55f 100644 --- a/platform/drivers/baseband/radio_UV3x0.c +++ b/platform/drivers/baseband/radio_UV3x0.c @@ -25,7 +25,7 @@ #include #include #include "HR_C6000.h" -#include "AT1846S.h" +#include "AT1846S_wrapper.h" const mduv3x0Calib_t *calData; /* Pointer to calibration data */ @@ -277,11 +277,6 @@ float radio_getRssi(const freq_t rxFreq) { (void) rxFreq; - /* - * RSSI and SNR are packed in a 16-bit value, with RSSI being the upper - * eight bits. - */ - uint16_t val = (AT1846S_readRSSI() >> 8); - int16_t rssi = -151 + ((int16_t) val); + int16_t rssi = AT1846S_readRSSI(); return ((float) rssi); } diff --git a/platform/mcu/MK22FN512xxx12/drivers/I2C0.h b/platform/mcu/MK22FN512xxx12/drivers/I2C0.h index 9ba4947f..de50c24a 100644 --- a/platform/mcu/MK22FN512xxx12/drivers/I2C0.h +++ b/platform/mcu/MK22FN512xxx12/drivers/I2C0.h @@ -25,6 +25,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** * Initialise I2C peripheral with a bus clock frequency of ~100kHz. * NOTE: this driver does not configure the I2C GPIOs, which have to be put in @@ -82,4 +86,8 @@ void i2c0_lockDeviceBlocking(); */ void i2c0_releaseDevice(); +#ifdef __cplusplus +} +#endif + #endif /* I2C0_H */