diff --git a/platform/drivers/baseband/HR_C5000.h b/platform/drivers/baseband/HR_C5000.h index bafbc8f9..d6e70ea8 100644 --- a/platform/drivers/baseband/HR_C5000.h +++ b/platform/drivers/baseband/HR_C5000.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * diff --git a/platform/drivers/baseband/HR_C5000_MDx.cpp b/platform/drivers/baseband/HR_C5000_MDx.cpp index 7f22f514..c91f6632 100644 --- a/platform/drivers/baseband/HR_C5000_MDx.cpp +++ b/platform/drivers/baseband/HR_C5000_MDx.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2020 - 2023 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2020 - 2024 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * @@ -63,10 +63,6 @@ template< class M > void HR_Cx000< M >::terminate() { gpio_setPin(DMR_SLEEP); - gpio_setMode(DMR_CS, INPUT); - gpio_setMode(DMR_CLK, INPUT); - gpio_setMode(DMR_MOSI, INPUT); - gpio_setMode(DMR_MISO, INPUT); gpio_setMode(DMR_SLEEP, INPUT); } @@ -186,47 +182,3 @@ void HR_Cx000< M >::stopAnalogTx() { writeReg(M::CONFIG, 0x60, 0x00); // Disable both analog and DMR transmission } - -/* - * SPI interface driver - */ -template< class M > -void HR_Cx000< M >::uSpi_init() -{ - gpio_setMode(DMR_CS, OUTPUT); - gpio_setMode(DMR_CLK, OUTPUT); - gpio_setMode(DMR_MOSI, OUTPUT); - gpio_setMode(DMR_MISO, OUTPUT); - - // Deselect HR_C5000, idle state of the CS line. - gpio_setPin(DMR_CS); -} - -template< class M > -uint8_t HR_Cx000< M >::uSpi_sendRecv(const uint8_t value) -{ - gpio_clearPin(DMR_CLK); - - uint8_t incoming = 0; - - for(uint8_t cnt = 0; cnt < 8; cnt++) - { - gpio_setPin(DMR_CLK); - - if(value & (0x80 >> cnt)) - { - gpio_setPin(DMR_MOSI); - } - else - { - gpio_clearPin(DMR_MOSI); - } - - delayUs(1); - gpio_clearPin(DMR_CLK); - incoming = (incoming << 1) | gpio_readPin(DMR_MISO); - delayUs(1); - } - - return incoming; -} diff --git a/platform/drivers/baseband/HR_C6000.h b/platform/drivers/baseband/HR_C6000.h index 993ed85b..2a195f32 100644 --- a/platform/drivers/baseband/HR_C6000.h +++ b/platform/drivers/baseband/HR_C6000.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * diff --git a/platform/drivers/baseband/HR_C6000_GDx.cpp b/platform/drivers/baseband/HR_C6000_GDx.cpp index e7634540..54a8faef 100644 --- a/platform/drivers/baseband/HR_C6000_GDx.cpp +++ b/platform/drivers/baseband/HR_C6000_GDx.cpp @@ -140,7 +140,6 @@ template< class M > void HR_Cx000< M >::terminate() { gpio_setPin(DMR_SLEEP); - gpio_setMode(DMR_CS, INPUT); } template< class M > diff --git a/platform/drivers/baseband/HR_C6000_UV3x0.cpp b/platform/drivers/baseband/HR_C6000_UV3x0.cpp index f7266301..46da53f4 100644 --- a/platform/drivers/baseband/HR_C6000_UV3x0.cpp +++ b/platform/drivers/baseband/HR_C6000_UV3x0.cpp @@ -52,9 +52,7 @@ template class HR_Cx000 < C6000_SpiOpModes >; template< class M > void HR_Cx000< M >::init() { - gpio_setMode(DMR_CS, OUTPUT); gpio_setMode(DMR_SLEEP, OUTPUT); - gpio_setPin(DMR_SLEEP); delayMs(10); @@ -117,7 +115,6 @@ template< class M > void HR_Cx000< M >::terminate() { gpio_setPin(DMR_SLEEP); - gpio_setMode(DMR_CS, INPUT); } template< class M > @@ -226,47 +223,3 @@ void HR_Cx000< M >::stopAnalogTx() writeReg(M::CONFIG, 0x34, 0x98); // FM bpf enabled, 25kHz bandwidth writeReg(M::CONFIG, 0x26, 0xFD); // Undocumented register, enable FM receive } - -/* - * SPI interface driver - */ -template< class M > -void HR_Cx000< M >::uSpi_init() -{ - gpio_setMode(DMR_CS, OUTPUT); - gpio_setMode(DMR_CLK, OUTPUT); - gpio_setMode(DMR_MOSI, OUTPUT); - gpio_setMode(DMR_MISO, OUTPUT); - - // Deselect HR_C6000, idle state of the CS line. - gpio_setPin(DMR_CS); -} - -template< class M > -uint8_t HR_Cx000< M >::uSpi_sendRecv(const uint8_t value) -{ - gpio_clearPin(DMR_CLK); - - uint8_t incoming = 0; - - for(uint8_t cnt = 0; cnt < 8; cnt++) - { - gpio_setPin(DMR_CLK); - - if(value & (0x80 >> cnt)) - { - gpio_setPin(DMR_MOSI); - } - else - { - gpio_clearPin(DMR_MOSI); - } - - delayUs(1); - gpio_clearPin(DMR_CLK); - incoming = (incoming << 1) | gpio_readPin(DMR_MISO); - delayUs(1); - } - - return incoming; -} diff --git a/platform/drivers/baseband/HR_Cx000.cpp b/platform/drivers/baseband/HR_Cx000.cpp index edf4d9cb..42f13a1a 100644 --- a/platform/drivers/baseband/HR_Cx000.cpp +++ b/platform/drivers/baseband/HR_Cx000.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * @@ -26,11 +26,6 @@ #include "HR_C5000.h" #include "HR_C6000.h" -bool Cx000_uSpiBusy() -{ - return (gpio_readPin(DMR_CS) == 0) ? true : false; -} - template <> void HR_Cx000< C5000_SpiOpModes >::setDacGain(int8_t gain) { @@ -120,16 +115,22 @@ void HR_Cx000< C6000_SpiOpModes >::setInputGain(int8_t value) writeReg(C6000_SpiOpModes::CONFIG, 0xE4, regValue); } -ScopedChipSelect::ScopedChipSelect() +ScopedChipSelect::ScopedChipSelect(const struct spiDevice *spi, + const struct gpioPin& cs) : spi(spi), cs(cs) { - gpio_clearPin(DMR_CS); + spi_acquire(spi); + gpioPin_clear(&cs); } ScopedChipSelect::~ScopedChipSelect() { + // NOTE: it seems that, without the 2us delays before and after the nCS + // deassertion, the HR_C6000 doesn't work properly. At least this is what + // has been observed on the Retevis RT3s. delayUs(2); - gpio_setPin(DMR_CS); + gpioPin_set(&cs); delayUs(2); + spi_release(spi); } FmConfig operator |(FmConfig lhs, FmConfig rhs) diff --git a/platform/drivers/baseband/HR_Cx000.h b/platform/drivers/baseband/HR_Cx000.h index d076c39d..d3c76df2 100644 --- a/platform/drivers/baseband/HR_Cx000.h +++ b/platform/drivers/baseband/HR_Cx000.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * + * Copyright (C) 2021 - 2024 by Federico Amedeo Izzo IU2NUO, * * Niccolò Izzo IU2KIN * * Frederik Saraci IU2NRO * * Silvano Seva IU2KWO * @@ -21,29 +21,12 @@ #ifndef HRCx000_H #define HRCx000_H +#include +#include #include #include #include -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Check if "user" SPI bus, on some platforms shared between the baseband and - * other chips, is in use by the HR_Cx000 driver. This function is callable - * either from C or C++ sources. - * - * WARNING: this function is NOT thread safe! A proper critical section has to - * be set up to ensure it is accessed by one task at a time. - * - * @return true if SPI lines are being used by this driver. - */ -bool Cx000_uSpiBusy(); - -#ifdef __cplusplus -} - /** * Configuration options for analog FM mode. * Each option is tied to a particular bit of the Configuration register 0x34. @@ -66,32 +49,26 @@ enum class TxAudioSource LINE_IN ///< Audio source is "line in", e.g. tone generator. }; -/** - * Generic driver for HR_C5000/HR_C6000 "baseband" chip. - * - * - * WARNING: on some MDx devices the PLL and DMR chips share the SPI MOSI line, - * thus particular care has to be put to avoid them stomping reciprocally. - * This driver does not make any check if a SPI transfer is already in progress, - * deferring the correct bus management to higher level modules. However, - * a function returning true if the bus is currently in use by this driver is - * provided. - */ - class ScopedChipSelect; +/** + * Generic driver for HR_C5000/HR_C6000 "baseband" chip. + */ template< class M > class HR_Cx000 { public: /** - * \return a reference to the instance of the AT1846S class (singleton). + * Constructor. + * + * @param dev: pointer to chip's hardware interface descriptor. */ - static HR_Cx000& instance() + HR_Cx000(const struct spiDevice *uSpi, const struct gpioPin uCs) : uSpi(uSpi), uCs(uCs) { - static HR_Cx000< M > Cx000; - return Cx000; + // Configure chip select pin + gpioPin_setMode(&uCs, OUTPUT); + gpioPin_set(&uCs); } /** @@ -101,6 +78,7 @@ public: ~HR_Cx000() { terminate(); + gpioPin_setMode(&uCs, INPUT); } /** @@ -212,15 +190,6 @@ public: private: - /** - * Constructor. - */ - HR_Cx000() - { - // Being a singleton class, uSPI is initialised only once. - uSpi_init(); - } - /** * Helper function for register writing. * @@ -230,10 +199,14 @@ private: */ void writeReg(const M opMode, const uint8_t addr, const uint8_t value) { - ScopedChipSelect cs; - (void) uSpi_sendRecv(static_cast< uint8_t >(opMode)); - (void) uSpi_sendRecv(addr); - (void) uSpi_sendRecv(value); + uint8_t data[3]; + + data[0] = static_cast< uint8_t >(opMode); + data[1] = addr; + data[2] = value; + + ScopedChipSelect cs(uSpi, uCs); + spi_send(uSpi, data, 3); } /** @@ -245,10 +218,17 @@ private: */ uint8_t readReg(const M opMode, const uint8_t addr) { - ScopedChipSelect cs; - (void) uSpi_sendRecv(static_cast< uint8_t >(opMode) | 0x80); - (void) uSpi_sendRecv(addr); - return uSpi_sendRecv(0x00); + uint8_t cmd[3]; + uint8_t ret[3]; + + cmd[0] = 0x80 | static_cast< uint8_t >(opMode); + cmd[1] = addr; + cmd[2] = 0x00; + + ScopedChipSelect cs(uSpi, uCs); + spi_transfer(uSpi, cmd, ret, 3); + + return ret[2]; } /** @@ -260,26 +240,12 @@ private: */ void sendSequence(const uint8_t *seq, const size_t len) { - ScopedChipSelect cs; - for(size_t i = 0; i < len; i++) - { - (void) uSpi_sendRecv(seq[i]); - } + ScopedChipSelect cs(uSpi, uCs); + spi_send(uSpi, seq, len); } - /** - * Initialise the low-level driver which manages "user" SPI interface, that - * is the one used to configure the chipset functionalities. - */ - void uSpi_init(); - - /** - * Transfer one byte across the "user" SPI interface. - * - * @param value: value to be sent. - * @return incoming byte from the baseband chip. - */ - uint8_t uSpi_sendRecv(const uint8_t value); + const struct spiDevice *uSpi; + const struct gpioPin uCs; }; /** @@ -299,19 +265,23 @@ public: /** * Constructor. - * When called it brings the HR_C5000/HR_C6000 chip select to logical low, - * selecting it. + * When called it acquires exclusive ownership on the "user" SPI bus and + * brings the HR_C5000/HR_C6000 chip select to logical low. + * + * @param dev: pointer to device interface. */ - ScopedChipSelect(); + ScopedChipSelect(const struct spiDevice *spi, const struct gpioPin& cs); /** * Destructor. * When called it brings the HR_C5000/HR_C6000 chip select to logical high, - * deselecting it. + * and releases exclusive ownership on the "user" SPI bus. */ ~ScopedChipSelect(); + +private: + const struct spiDevice *spi; + const struct gpioPin& cs; }; -#endif // __cplusplus - #endif // HRCx000_H