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 *
* Frederik Saraci IU2NRO *
* 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 *
* 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;
}

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 *
* Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO *

Wyświetl plik

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

Wyświetl plik

@ -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;
}

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 *
* 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)

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 *
* Frederik Saraci IU2NRO *
* Silvano Seva IU2KWO *
@ -21,29 +21,12 @@
#ifndef HRCx000_H
#define HRCx000_H
#include <peripherals/gpio.h>
#include <peripherals/spi.h>
#include <stdbool.h>
#include <stdint.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.
* 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