Moved to C++ and refactored AT1846S driver, grouping common code. A C-callable wrapper is also provided. TG-37

This commit is contained in:
Silvano Seva 2021-04-15 10:03:17 +02:00
parent 18c0844842
commit d95e36f43b
14 changed files with 1005 additions and 680 deletions

View File

@ -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']

View File

@ -22,6 +22,10 @@
#include <stdint.h>
#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 */

View File

@ -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 <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <interfaces/delays.h>
#include <calibInfo_GDx.h>
#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));
}

View File

@ -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 <datatypes.h>
/**
* \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 */

View File

@ -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 <http://www.gnu.org/licenses/> *
**************************************************************************/
#include <interfaces/delays.h>
#include <I2C0.h>
#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, &reg, 1, false);
i2c0_read(devAddr, &value, 2);
i2c0_releaseDevice();
// Correction for AT1846S sending register data in big endian format
return __builtin_bswap16(value);
}

View File

@ -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 <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <interfaces/gpio.h>
#include <interfaces/delays.h>
#include "interfaces.h"
#include <hwconfig.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/> *
***************************************************************************/
#include <interfaces/delays.h>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/> *
***************************************************************************/
#ifndef AT1846S_H
#define AT1846S_H
#include <stdint.h>
#include <stdbool.h>
#include <datatypes.h>
#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 */

View File

@ -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.

View File

@ -27,49 +27,6 @@
#include <I2C0.h>
#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, &reg, 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.

View File

@ -27,58 +27,6 @@
#include <stdbool.h>
#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.

View File

@ -26,7 +26,7 @@
#include <hwconfig.h>
#include <string.h>
#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);
}

View File

@ -25,7 +25,7 @@
#include <calibUtils.h>
#include <hwconfig.h>
#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);
}

View File

@ -25,6 +25,10 @@
#include <stddef.h>
#include <stdbool.h>
#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 */