HR_Cx000: driver refactoring

- removed the Cx000_uSpiBusy() function.
- HR_Cx000 class is no more singleton.
- added constructor parameters for USPI interface.
pull/292/head
Silvano Seva 2024-06-07 07:40:32 +02:00
rodzic eda8d5d835
commit c34e4462c2
7 zmienionych plików z 60 dodań i 185 usunięć

Wyświetl plik

@ -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 * * Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO * * Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO * * Silvano Seva IU2KWO *

Wyświetl plik

@ -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 * * Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO * * Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO * * Silvano Seva IU2KWO *
@ -63,10 +63,6 @@ template< class M >
void HR_Cx000< M >::terminate() void HR_Cx000< M >::terminate()
{ {
gpio_setPin(DMR_SLEEP); 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); 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 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;
}

Wyświetl plik

@ -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 * * Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO * * Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO * * Silvano Seva IU2KWO *

Wyświetl plik

@ -140,7 +140,6 @@ template< class M >
void HR_Cx000< M >::terminate() void HR_Cx000< M >::terminate()
{ {
gpio_setPin(DMR_SLEEP); gpio_setPin(DMR_SLEEP);
gpio_setMode(DMR_CS, INPUT);
} }
template< class M > template< class M >

Wyświetl plik

@ -52,9 +52,7 @@ template class HR_Cx000 < C6000_SpiOpModes >;
template< class M > template< class M >
void HR_Cx000< M >::init() void HR_Cx000< M >::init()
{ {
gpio_setMode(DMR_CS, OUTPUT);
gpio_setMode(DMR_SLEEP, OUTPUT); gpio_setMode(DMR_SLEEP, OUTPUT);
gpio_setPin(DMR_SLEEP); gpio_setPin(DMR_SLEEP);
delayMs(10); delayMs(10);
@ -117,7 +115,6 @@ template< class M >
void HR_Cx000< M >::terminate() void HR_Cx000< M >::terminate()
{ {
gpio_setPin(DMR_SLEEP); gpio_setPin(DMR_SLEEP);
gpio_setMode(DMR_CS, INPUT);
} }
template< class M > 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, 0x34, 0x98); // FM bpf enabled, 25kHz bandwidth
writeReg(M::CONFIG, 0x26, 0xFD); // Undocumented register, enable FM receive 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;
}

Wyświetl plik

@ -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 * * Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO * * Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO * * Silvano Seva IU2KWO *
@ -26,11 +26,6 @@
#include "HR_C5000.h" #include "HR_C5000.h"
#include "HR_C6000.h" #include "HR_C6000.h"
bool Cx000_uSpiBusy()
{
return (gpio_readPin(DMR_CS) == 0) ? true : false;
}
template <> template <>
void HR_Cx000< C5000_SpiOpModes >::setDacGain(int8_t gain) 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); 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() 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); delayUs(2);
gpio_setPin(DMR_CS); gpioPin_set(&cs);
delayUs(2); delayUs(2);
spi_release(spi);
} }
FmConfig operator |(FmConfig lhs, FmConfig rhs) FmConfig operator |(FmConfig lhs, FmConfig rhs)

Wyświetl plik

@ -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 * * Niccolò Izzo IU2KIN *
* Frederik Saraci IU2NRO * * Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO * * Silvano Seva IU2KWO *
@ -21,29 +21,12 @@
#ifndef HRCx000_H #ifndef HRCx000_H
#define HRCx000_H #define HRCx000_H
#include <peripherals/gpio.h>
#include <peripherals/spi.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#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. * Configuration options for analog FM mode.
* Each option is tied to a particular bit of the Configuration register 0x34. * 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. 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; class ScopedChipSelect;
/**
* Generic driver for HR_C5000/HR_C6000 "baseband" chip.
*/
template< class M > template< class M >
class HR_Cx000 class HR_Cx000
{ {
public: 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; // Configure chip select pin
return Cx000; gpioPin_setMode(&uCs, OUTPUT);
gpioPin_set(&uCs);
} }
/** /**
@ -101,6 +78,7 @@ public:
~HR_Cx000() ~HR_Cx000()
{ {
terminate(); terminate();
gpioPin_setMode(&uCs, INPUT);
} }
/** /**
@ -212,15 +190,6 @@ public:
private: private:
/**
* Constructor.
*/
HR_Cx000()
{
// Being a singleton class, uSPI is initialised only once.
uSpi_init();
}
/** /**
* Helper function for register writing. * Helper function for register writing.
* *
@ -230,10 +199,14 @@ private:
*/ */
void writeReg(const M opMode, const uint8_t addr, const uint8_t value) void writeReg(const M opMode, const uint8_t addr, const uint8_t value)
{ {
ScopedChipSelect cs; uint8_t data[3];
(void) uSpi_sendRecv(static_cast< uint8_t >(opMode));
(void) uSpi_sendRecv(addr); data[0] = static_cast< uint8_t >(opMode);
(void) uSpi_sendRecv(value); 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) uint8_t readReg(const M opMode, const uint8_t addr)
{ {
ScopedChipSelect cs; uint8_t cmd[3];
(void) uSpi_sendRecv(static_cast< uint8_t >(opMode) | 0x80); uint8_t ret[3];
(void) uSpi_sendRecv(addr);
return uSpi_sendRecv(0x00); 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) void sendSequence(const uint8_t *seq, const size_t len)
{ {
ScopedChipSelect cs; ScopedChipSelect cs(uSpi, uCs);
for(size_t i = 0; i < len; i++) spi_send(uSpi, seq, len);
{
(void) uSpi_sendRecv(seq[i]);
}
} }
/** const struct spiDevice *uSpi;
* Initialise the low-level driver which manages "user" SPI interface, that const struct gpioPin uCs;
* 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);
}; };
/** /**
@ -299,19 +265,23 @@ public:
/** /**
* Constructor. * Constructor.
* When called it brings the HR_C5000/HR_C6000 chip select to logical low, * When called it acquires exclusive ownership on the "user" SPI bus and
* selecting it. * 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. * Destructor.
* When called it brings the HR_C5000/HR_C6000 chip select to logical high, * 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(); ~ScopedChipSelect();
private:
const struct spiDevice *spi;
const struct gpioPin& cs;
}; };
#endif // __cplusplus
#endif // HRCx000_H #endif // HRCx000_H