Compare commits

..
30 Commits
Author SHA1 Message Date
Jonathan Naylor 60f87072ba Rescale the TX filter for M17. 2020-10-15 09:40:35 +01:00
Jonathan Naylor f2d2033733 Add the sync and RRC 0.5 data and code. 2020-10-14 13:29:23 +01:00
Jonathan Naylor 0334b5a791 Handle the M17 mode LED pins(-ish) 2020-10-14 11:55:31 +01:00
Jonathan Naylor a62fd814ae Begin M17 support. 2020-10-14 10:52:30 +01:00
Jonathan Naylor 2509ab5ba1 Fix the self-test LEDs. 2020-09-01 13:27:29 +01:00
Jonathan Naylor fda1e3dcf4 More LED fixes. 2020-09-01 09:57:49 +01:00
Jonathan Naylor 632ddda68b Change the handling of the mode LEDs. 2020-08-31 16:06:59 +01:00
Jonathan Naylor fb85505433 Merge pull request #296 from msraya/patch-1
YSF pictures can go through modem
2020-08-03 08:43:11 +01:00
Manuel Sánchez Raya 823b91c97d Update YSFRX.cpp
Lower MAX_SYNC_FRAMES to 2, so decoder can recover and Yaesu pictures can pass through the modem without problem.
2020-08-03 09:17:07 +02:00
Jonathan Naylor a60ecb3e57 Merge pull request #295 from n0mjs710/master
Added pin definition for FM mode LED (WA0EDA Boards)
2020-07-21 16:21:08 +01:00
Cort Buffington a2978d9e91 Removed a local change to not contaminate the repo 2020-07-21 08:27:02 -05:00
Cort Buffington e304f5cce2 Added pin assignment for FM mode LED (WA0EDA) 2020-07-21 08:19:18 -05:00
Jonathan Naylor 99a7df7c13 Merge pull request #294 from n0mjs710/master
Add support for WA0EDA MTR2000 & MASTR III boards
2020-07-21 06:08:08 +01:00
Cort Buffington 6dfe1a9271 Add support for WA0EDA MTR2000 & MASTR3 boards 2020-07-20 17:11:29 -05:00
Jonathan Naylor 1ac0628c1c Improve the code efficiency slightly. 2020-07-14 14:54:47 +01:00
Jonathan Naylor dc41fd83d2 Test the use of a reverse burst at the end of a transmission. 2020-07-14 12:20:42 +01:00
Jonathan Naylor 20013e3adc Make Use COS As Lockout a run-time parameter and allow for different access modes. 2020-07-13 17:13:01 +01:00
Jonathan Naylor ccdbe126e0 Fix the timeout tones. 2020-07-12 15:22:35 +01:00
Jonathan Naylor f8d8f85841 Small simplification of the timeout tones. 2020-07-12 10:47:43 +01:00
Jonathan Naylor 598d32287d Add alternate FM LEDs for the POG board. 2020-06-01 16:55:13 +01:00
Jonathan Naylor 53d8cfa360 Revert IOSTM_CMSIS.cpp to 5c9abf2 2020-06-01 16:51:59 +01:00
Jonathan Naylor 7fabddd7f1 Update the README file. 2020-05-21 11:11:19 +01:00
Jonathan Naylor 2cf2ce4dfa Fix the TX Hang handling. 2020-05-20 15:16:12 +01:00
Jonathan Naylor 5f29808b80 Set the COS LED correctly. 2020-05-19 12:09:44 +01:00
Jonathan Naylor 063b21f999 Try and make the closing callsign appear. 2020-05-18 11:09:56 +01:00
Jonathan Naylor 89b8641955 Merge pull request #278 from shawnchain/drcc_serial_bugfix
Drcc serial bugfix
2020-05-18 06:34:09 +01:00
Shawn Chain 6397c720e8 Generate bin map file for analysis 2020-05-18 12:19:22 +08:00
Shawn Chain 50c1e5aa3f update pin defs for drcc/nqf board 2020-05-18 12:18:44 +08:00
Shawn Chain 970d02773f Fix serial no write bug on DRCC board 2020-05-18 12:18:17 +08:00
Jonathan Naylor ff375abb0e Hold off the mode change until the ack and callsign have been
transmitted.
2020-05-14 12:10:37 +01:00
66 changed files with 2949 additions and 1392 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ void CCalFM::process()
{
const TONE_TABLE* entry = NULL;
if (m_modemState != m_lastState)
if (m_modemState!=m_lastState)
{
switch (m_modemState) {
case STATE_FMCAL10K:
+3 -8
View File
@@ -35,14 +35,6 @@
// For 19.2 MHz
// #define EXTERNAL_OSC 19200000
// Use a higher baudrate for host communication. Required for FM network !
#define SERIAL_SPEED 115200 //suitable for most older boards (Arduino, Due STM32F1_POG etc). External FM will NOT work with this !
// #define SERIAL_SPEED 230400 // Only works on newer board M4, M7, Teensy. External FM might work with this
// #define SERIAL_SPEED 460800 // Only works on newer board M4, M7, Teensy. External FM should work with this
// Allow the use of the COS line to lockout the modem
// #define USE_COS_AS_LOCKOUT
// Use pins to output the current mode via LEDs
// #define MODE_LEDS
@@ -86,6 +78,9 @@
// Use the YSF and P25 LEDs for NXDN
// #define USE_ALTERNATE_NXDN_LEDS
// Use the D-Star and P25 LEDs for M17
// #define USE_ALTERNATE_M17_LEDS
// Use the D-Star and DMR LEDs for POCSAG
// #define USE_ALTERNATE_POCSAG_LEDS
+1 -1
View File
@@ -74,7 +74,7 @@ void CDMRDMOTX::process()
m_poLen = m_txDelay;
} else {
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
m_fifo.get(m_poBuffer[i]);
m_poBuffer[i] = m_fifo.get();
for (unsigned int i = 0U; i < 39U; i++)
m_poBuffer[i + DMR_FRAME_LENGTH_BYTES] = PR_FILL[i];
+2 -2
View File
@@ -23,7 +23,7 @@
#include "Config.h"
#include "DMRDefines.h"
#include "RingBuffer.h"
#include "SerialRB.h"
class CDMRDMOTX {
public:
@@ -38,7 +38,7 @@ public:
uint8_t getSpace() const;
private:
CRingBuffer<uint8_t> m_fifo;
CSerialRB m_fifo;
arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
uint8_t m_poBuffer[1200U];
+1 -1
View File
@@ -293,7 +293,7 @@ void CDMRTX::createData(uint8_t slotIndex)
{
if (m_fifo[slotIndex].getData() >= DMR_FRAME_LENGTH_BYTES && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) {
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_fifo[slotIndex].get(m_poBuffer[i]);
m_poBuffer[i] = m_fifo[slotIndex].get();
m_markBuffer[i] = MARK_NONE;
}
} else {
+2 -2
View File
@@ -23,7 +23,7 @@
#include "Config.h"
#include "DMRDefines.h"
#include "RingBuffer.h"
#include "SerialRB.h"
enum DMRTXSTATE {
DMRTXSTATE_IDLE,
@@ -59,7 +59,7 @@ public:
void setColorCode(uint8_t colorCode);
private:
CRingBuffer<uint8_t> m_fifo[2U];
CSerialRB m_fifo[2U];
arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
DMRTXSTATE m_state;
+5 -8
View File
@@ -216,13 +216,12 @@ void CDStarTX::process()
for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = BIT_SYNC;
} else {
uint8_t dummy;
// Pop the type byte off
m_buffer.get(dummy);
m_buffer.get();
uint8_t header[DSTAR_HEADER_LENGTH_BYTES];
for (uint8_t i = 0U; i < DSTAR_HEADER_LENGTH_BYTES; i++)
m_buffer.get(header[i]);
header[i] = m_buffer.get();
uint8_t buffer[86U];
txHeader(header, buffer + 2U);
@@ -240,19 +239,17 @@ void CDStarTX::process()
if (type == DSTAR_DATA && m_poLen == 0U) {
// Pop the type byte off
uint8_t dummy;
m_buffer.get(dummy);
m_buffer.get();
for (uint8_t i = 0U; i < DSTAR_DATA_LENGTH_BYTES; i++)
m_buffer.get(m_poBuffer[m_poLen++]);
m_poBuffer[m_poLen++] = m_buffer.get();
m_poPtr = 0U;
}
if (type == DSTAR_EOT && m_poLen == 0U) {
// Pop the type byte off
uint8_t dummy;
m_buffer.get(dummy);
m_buffer.get();
for (uint8_t j = 0U; j < 3U; j++) {
for (uint8_t i = 0U; i < DSTAR_EOT_LENGTH_BYTES; i++)
+2 -2
View File
@@ -21,7 +21,7 @@
#include "Config.h"
#include "RingBuffer.h"
#include "SerialRB.h"
class CDStarTX {
public:
@@ -38,7 +38,7 @@ public:
uint8_t getSpace() const;
private:
CRingBuffer<uint8_t> m_buffer;
CSerialRB m_buffer;
arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[20U]; // blockSize + phaseLength - 1, 8 + 9 - 1 plus some spare
uint8_t m_poBuffer[600U];
+236 -495
View File
@@ -20,16 +20,17 @@
#include "Globals.h"
#include "FM.h"
const uint16_t FM_TX_BLOCK_SIZE = 100U;
const uint16_t FM_SERIAL_BLOCK_SIZE = 84U;//this is the number of sample pairs to send over serial. One sample pair is 3bytes.
//three times this value shall never exceed 252
const uint16_t FM_SERIAL_BLOCK_SIZE_BYTES = FM_SERIAL_BLOCK_SIZE * 3U;
/*
* Access Mode values are:
* 0 - Carrier access with COS
* 1 - CTCSS only access without COS
* 2 - CTCSS only access with COS
* 3 - CTCSS only access with COS to start, then carrier access with COS
*/
CFM::CFM() :
m_callsign(),
m_rfAck(),
m_extAck(),
m_ctcssRX(),
m_ctcssTX(),
m_timeoutTone(),
@@ -41,156 +42,124 @@ m_callsignTimer(),
m_timeoutTimer(),
m_holdoffTimer(),
m_kerchunkTimer(),
m_kerchunkTX(true),
m_ackMinTimer(),
m_ackDelayTimer(),
m_hangTimer(),
m_statusTimer(),
m_reverseTimer(),
m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),//3rd order Cheby Filter 300 to 2700Hz, 0.2dB passband ripple, sampling rate 24kHz
m_filterStage2(32768, 0,-32768, 32768, -50339, 19052),
m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460),
m_blanking(),
m_useCOS(true),
m_accessMode(1U),
m_cosInvert(false),
m_rfAudioBoost(1U),
m_extAudioBoost(1U),
m_downsampler(400U),// 100 ms of audio
m_extEnabled(false),
m_downsampler(128U),//Size might need adjustement
m_rxLevel(1),
m_inputRFRB(2401U), // 100ms of audio + 1 sample
m_outputRFRB(2400U), // 100ms of audio
m_inputExtRB()
m_inputRB(4800U), // 200ms of audio
m_outputRB(2400U) // 100ms of audio
{
m_statusTimer.setTimeout(1U, 0U);
m_reverseTimer.setTimeout(0U, 150U);
insertDelay(100U);
}
void CFM::samples(bool cos, q15_t* samples, uint8_t length)
void CFM::samples(bool cos, const q15_t* samples, uint8_t length)
{
if (m_useCOS) {
if (m_cosInvert)
cos = !cos;
} else {
cos = true;
}
if (m_cosInvert)
cos = !cos;
clock(length);
uint8_t i = 0U;
for (; i < length; i++) {
// ARMv7-M has hardware integer division
q15_t currentRFSample = q15_t((q31_t(samples[i]) << 8) / m_rxLevel);
uint8_t ctcssState = m_ctcssRX.process(currentRFSample);
q15_t currentSample = q15_t((q31_t(samples[i]) << 8) / m_rxLevel);
if (!m_useCOS) {
// Delay the audio by 100ms to better match the CTCSS detector output
m_inputRFRB.put(currentRFSample);
m_inputRFRB.get(currentRFSample);
switch (m_accessMode) {
case 0U:
stateMachine(cos);
break;
case 1U: {
uint8_t ctcssState = m_ctcssRX.process(currentSample);
// Delay the audio by 100ms to better match the CTCSS detector output
m_inputRB.put(currentSample);
m_inputRB.get(currentSample);
if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) {
// Not enough samples to determine if you have CTCSS, just carry on
} else {
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS);
}
}
break;
case 2U: {
uint8_t ctcssState = m_ctcssRX.process(currentSample);
if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) {
// Not enough samples to determine if you have CTCSS, just carry on
} else {
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos);
}
}
break;
default: {
uint8_t ctcssState = m_ctcssRX.process(currentSample);
if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) {
// Not enough samples to determine if you have CTCSS, just carry on
} else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) {
// We had enough samples for CTCSS and we are in some other mode than FM
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos);
} else {
stateMachine(cos);
}
}
break;
}
q15_t currentExtSample;
bool inputExt = m_inputExtRB.getSample(currentExtSample);//always consume the external input data so it does not overflow
inputExt = inputExt && m_extEnabled;
if (!inputExt && (CTCSS_NOT_READY(ctcssState)) && m_modemState != STATE_FM) {
//Not enough samples to determine if you have CTCSS, just carry on. But only if we haven't any external data in the queue
if (m_modemState != STATE_FM)
continue;
} else if ((inputExt || CTCSS_READY(ctcssState)) && m_modemState != STATE_FM) {
//we had enough samples for CTCSS and we are in some other mode than FM
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos, inputExt);
if (m_modemState != STATE_FM)
continue;
} else if ((inputExt || CTCSS_NOT_READY(ctcssState)) && m_modemState == STATE_FM && i == length - 1) {
//Not enough samples for CTCSS but we already are in FM, trigger the state machine
//but do not trigger the state machine on every single sample, save CPU!
bool validCTCSS = CTCSS_VALID(ctcssState);
stateMachine(validCTCSS && cos, inputExt);
}
q15_t currentSample = currentRFSample;
q15_t currentBoost = m_rfAudioBoost;
if (m_state == FS_RELAYING_EXT || m_state == FS_KERCHUNK_EXT) {
currentSample = currentExtSample;
currentBoost = m_extAudioBoost;
}
// Only let RF audio through when relaying RF audio
if (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF || m_state == FS_RELAYING_EXT || m_state == FS_KERCHUNK_EXT) {
// Only let audio through when relaying audio
if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) {
// m_downsampler.addSample(currentSample);
currentSample = m_blanking.process(currentSample);
if (m_extEnabled && (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF))
m_downsampler.addSample(currentSample);
currentSample *= currentBoost;
currentSample *= m_rfAudioBoost;
} else {
currentSample = 0;
}
if (!m_callsign.isRunning() && !m_extAck.isRunning())
if (!m_callsign.isRunning())
currentSample += m_rfAck.getHighAudio();
if (!m_callsign.isRunning() && !m_rfAck.isRunning())
currentSample += m_extAck.getHighAudio();
if (!m_rfAck.isRunning() && !m_extAck.isRunning()) {
if (!m_rfAck.isRunning()) {
if (m_state == FS_LISTENING)
currentSample += m_callsign.getHighAudio();
else
currentSample += m_callsign.getLowAudio();
}
if (!m_callsign.isRunning() && !m_rfAck.isRunning() && !m_extAck.isRunning())
currentSample += m_timeoutTone.getAudio();
currentSample = m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)));
currentSample += m_ctcssTX.getAudio();
if (!m_callsign.isRunning() && !m_rfAck.isRunning())
currentSample += m_timeoutTone.getAudio();
m_outputRFRB.put(currentSample);
currentSample += m_ctcssTX.getAudio(m_reverseTimer.isRunning());
m_outputRB.put(currentSample);
}
}
void CFM::process()
{
uint16_t space = io.getSpace();
uint16_t length = m_outputRFRB.getData();
if (space > 10U && length >= FM_TX_BLOCK_SIZE ) {
space -= 2U;
if (length > FM_TX_BLOCK_SIZE)
length = FM_TX_BLOCK_SIZE;
if (space > FM_TX_BLOCK_SIZE)
space = FM_TX_BLOCK_SIZE;
if (length > space)
length = space;
q15_t samples[FM_TX_BLOCK_SIZE];
for (uint16_t i = 0U; i < length; i++) {
q15_t sample = 0;
m_outputRFRB.get(sample);
samples[i] = sample;
}
io.write(STATE_FM, samples, length);
}
if (m_extEnabled) {
uint16_t length = m_downsampler.getData();
if (length >= FM_SERIAL_BLOCK_SIZE) {
if (length > FM_SERIAL_BLOCK_SIZE)
length = FM_SERIAL_BLOCK_SIZE;
TSamplePairPack serialSamples[FM_SERIAL_BLOCK_SIZE];
for (uint16_t j = 0U; j < length; j++)
m_downsampler.getPackedData(serialSamples[j]);
serial.writeFMData((uint8_t*)serialSamples, length * sizeof(TSamplePairPack));
}
}
q15_t sample;
while (io.getSpace() >= 3U && m_outputRB.get(sample))
io.write(STATE_FM, &sample, 1U);
}
void CFM::reset()
@@ -203,18 +172,14 @@ void CFM::reset()
m_ackMinTimer.stop();
m_ackDelayTimer.stop();
m_hangTimer.stop();
m_statusTimer.stop();
m_reverseTimer.stop();
m_ctcssRX.reset();
m_rfAck.stop();
m_extAck.stop();
m_callsign.stop();
m_timeoutTone.stop();
m_outputRFRB.reset();
m_inputExtRB.reset();
m_downsampler.reset();
m_outputRB.reset();
}
uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch)
@@ -245,18 +210,15 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_
return m_rfAck.setParams(rfAck, speed, frequency, level, level);
}
uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, bool kerchunkTX, uint8_t hangTime, bool useCOS, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel)
uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel)
{
m_useCOS = useCOS;
m_cosInvert = cosInvert;
m_accessMode = accessMode;
m_cosInvert = cosInvert;
m_rfAudioBoost = q15_t(rfAudioBoost);
m_timeoutTimer.setTimeout(timeout, 0U);
m_kerchunkTimer.setTimeout(kerchunkTime, 0U);
m_kerchunkTX = kerchunkTX;
m_hangTimer.setTimeout(hangTime, 0U);
m_timeoutTone.setParams(timeoutLevel);
@@ -271,61 +233,49 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque
return m_ctcssTX.setParams(ctcssFrequency, ctcssLevel);
}
uint8_t CFM::setExt(const char* ack, uint8_t audioBoost, uint8_t speed, uint16_t frequency, uint8_t level)
{
m_extEnabled = true;
m_extAudioBoost = q15_t(audioBoost);
return m_extAck.setParams(ack, speed, frequency, level, level);
}
void CFM::stateMachine(bool validRFSignal, bool validExtSignal)
void CFM::stateMachine(bool validSignal)
{
switch (m_state) {
case FS_LISTENING:
listeningState(validRFSignal, validExtSignal);
listeningState(validSignal);
break;
case FS_KERCHUNK_RF:
kerchunkRFState(validRFSignal);
case FS_KERCHUNK:
kerchunkState(validSignal);
break;
case FS_RELAYING_RF:
relayingRFState(validRFSignal);
case FS_RELAYING:
relayingState(validSignal);
break;
case FS_RELAYING_WAIT_RF:
relayingRFWaitState(validRFSignal);
case FS_RELAYING_WAIT:
relayingWaitState(validSignal);
break;
case FS_TIMEOUT_RF:
timeoutRFState(validRFSignal);
case FS_TIMEOUT:
timeoutState(validSignal);
break;
case FS_TIMEOUT_WAIT_RF:
timeoutRFWaitState(validRFSignal);
break;
case FS_KERCHUNK_EXT:
kerchunkExtState(validExtSignal);
break;
case FS_RELAYING_EXT:
relayingExtState(validExtSignal);
break;
case FS_RELAYING_WAIT_EXT:
relayingExtWaitState(validExtSignal);
break;
case FS_TIMEOUT_EXT:
timeoutExtState(validExtSignal);
break;
case FS_TIMEOUT_WAIT_EXT:
timeoutExtWaitState(validExtSignal);
case FS_TIMEOUT_WAIT:
timeoutWaitState(validSignal);
break;
case FS_HANG:
hangState(validRFSignal, validExtSignal);
hangState(validSignal);
break;
default:
break;
}
if (m_state == FS_LISTENING && !m_rfAck.isWanted() && !m_extAck.isWanted() && !m_callsign.isWanted()) {
m_outputRFRB.reset();
m_downsampler.reset();
if (m_state == FS_LISTENING && m_modemState == STATE_FM) {
if (!m_callsign.isWanted() && !m_rfAck.isWanted() && !m_reverseTimer.isRunning())
m_reverseTimer.start();
if (!m_callsign.isWanted() && !m_rfAck.isWanted() && m_reverseTimer.isRunning() && m_reverseTimer.hasExpired()) {
DEBUG1("Change to STATE_IDLE");
m_modemState = STATE_IDLE;
m_callsignTimer.stop();
m_timeoutTimer.stop();
m_kerchunkTimer.stop();
m_ackMinTimer.stop();
m_ackDelayTimer.stop();
m_hangTimer.stop();
m_reverseTimer.stop();
}
}
}
@@ -338,302 +288,149 @@ void CFM::clock(uint8_t length)
m_ackMinTimer.clock(length);
m_ackDelayTimer.clock(length);
m_hangTimer.clock(length);
m_statusTimer.clock(length);
if (m_statusTimer.isRunning() && m_statusTimer.hasExpired()) {
serial.writeFMStatus(m_state);
m_statusTimer.start();
}
m_reverseTimer.clock(length);
}
void CFM::listeningState(bool validRFSignal, bool validExtSignal)
void CFM::listeningState(bool validSignal)
{
if (validRFSignal) {
if (validSignal) {
if (m_kerchunkTimer.getTimeout() > 0U) {
DEBUG1("State to KERCHUNK_RF");
m_state = FS_KERCHUNK_RF;
DEBUG1("State to KERCHUNK");
m_state = FS_KERCHUNK;
m_kerchunkTimer.start();
if (m_callsignAtStart && !m_callsignAtLatch)
sendCallsign();
} else {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
if (m_callsignAtStart)
sendCallsign();
}
if (m_state == FS_RELAYING_RF || (m_state == FS_KERCHUNK_RF && m_kerchunkTX)) {
insertSilence(50U);
insertSilence(50U);
beginRelaying();
m_callsignTimer.start();
io.setDecode(true);
io.setADCDetection(true);
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
} else if (validExtSignal) {
if (m_kerchunkTimer.getTimeout() > 0U) {
DEBUG1("State to KERCHUNK_EXT");
m_state = FS_KERCHUNK_EXT;
m_kerchunkTimer.start();
if (m_callsignAtStart && !m_callsignAtLatch)
sendCallsign();
} else {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
if (m_callsignAtStart)
sendCallsign();
}
if (m_state == FS_RELAYING_EXT || (m_state == FS_KERCHUNK_EXT && m_kerchunkTX)) {
insertSilence(50U);
beginRelaying();
m_callsignTimer.start();
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
}
}
void CFM::kerchunkRFState(bool validSignal)
{
if (validSignal) {
if (m_kerchunkTimer.hasExpired()) {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
m_kerchunkTimer.stop();
if (!m_kerchunkTX) {
insertSilence(50U);
beginRelaying();
m_callsignTimer.start();
io.setDecode(true);
io.setADCDetection(true);
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
if (m_callsignAtStart && m_callsignAtLatch) {
sendCallsign();
m_callsignTimer.start();
}
}
} else {
io.setDecode(false);
io.setADCDetection(false);
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_kerchunkTimer.stop();
m_timeoutTimer.stop();
m_ackMinTimer.stop();
m_callsignTimer.stop();
m_statusTimer.stop();
if (m_extEnabled)
serial.writeFMEOT();
}
}
void CFM::relayingRFState(bool validSignal)
{
if (validSignal) {
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT_RF");
m_state = FS_TIMEOUT_RF;
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_timeoutTone.start();
if (m_extEnabled)
serial.writeFMEOT();
}
} else {
io.setDecode(false);
io.setADCDetection(false);
DEBUG1("State to RELAYING_WAIT_RF");
m_state = FS_RELAYING_WAIT_RF;
m_ackDelayTimer.start();
if (m_extEnabled)
serial.writeFMEOT();
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::relayingRFWaitState(bool validSignal)
{
if (validSignal) {
io.setDecode(true);
io.setADCDetection(true);
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
if (m_ackMinTimer.isRunning()) {
if (m_ackMinTimer.hasExpired()) {
DEBUG1("Send RF ack");
m_rfAck.start();
m_ackMinTimer.stop();
}
} else {
DEBUG1("Send RF ack");
m_rfAck.start();
m_ackMinTimer.stop();
}
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
m_hangTimer.start();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::kerchunkExtState(bool validSignal)
{
if (validSignal) {
if (m_kerchunkTimer.hasExpired()) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
m_kerchunkTimer.stop();
if (!m_kerchunkTX) {
insertSilence(50U);
beginRelaying();
m_callsignTimer.start();
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
if (m_callsignAtStart && m_callsignAtLatch) {
sendCallsign();
m_callsignTimer.start();
}
}
} else {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_kerchunkTimer.stop();
m_timeoutTimer.stop();
m_ackMinTimer.stop();
m_callsignTimer.stop();
m_statusTimer.stop();
}
}
void CFM::relayingExtState(bool validSignal)
{
if (validSignal) {
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT_EXT");
m_state = FS_TIMEOUT_EXT;
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_timeoutTone.start();
}
} else {
DEBUG1("State to RELAYING_WAIT_EXT");
m_state = FS_RELAYING_WAIT_EXT;
m_ackDelayTimer.start();
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::relayingExtWaitState(bool validSignal)
{
if (validSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
if (m_ackMinTimer.isRunning()) {
if (m_ackMinTimer.hasExpired()) {
DEBUG1("Send Ext ack");
m_extAck.start();
m_ackMinTimer.stop();
}
} else {
DEBUG1("Send Ext ack");
m_extAck.start();
m_ackMinTimer.stop();
}
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
m_hangTimer.start();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::hangState(bool validRFSignal, bool validExtSignal)
{
if (validRFSignal) {
io.setDecode(true);
io.setADCDetection(true);
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
DEBUG1("Stop ack");
m_rfAck.stop();
m_extAck.stop();
beginRelaying();
} else if (validExtSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
m_callsignTimer.start();
m_reverseTimer.stop();
io.setDecode(true);
io.setADCDetection(true);
DEBUG1("Change to STATE_FM");
m_modemState = STATE_FM;
}
}
void CFM::kerchunkState(bool validSignal)
{
if (validSignal) {
if (m_kerchunkTimer.hasExpired()) {
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
m_kerchunkTimer.stop();
if (m_callsignAtStart && m_callsignAtLatch) {
sendCallsign();
m_callsignTimer.start();
}
}
} else {
io.setDecode(false);
io.setADCDetection(false);
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_kerchunkTimer.stop();
m_timeoutTimer.stop();
m_ackMinTimer.stop();
m_callsignTimer.stop();
}
}
void CFM::relayingState(bool validSignal)
{
if (validSignal) {
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT");
m_state = FS_TIMEOUT;
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_timeoutTone.start();
}
} else {
io.setDecode(false);
io.setADCDetection(false);
DEBUG1("State to RELAYING_WAIT");
m_state = FS_RELAYING_WAIT;
m_ackDelayTimer.start();
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::relayingWaitState(bool validSignal)
{
if (validSignal) {
io.setDecode(true);
io.setADCDetection(true);
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
if (m_ackMinTimer.isRunning()) {
if (m_ackMinTimer.hasExpired()) {
DEBUG1("Send ack");
m_rfAck.start();
m_ackMinTimer.stop();
}
} else {
DEBUG1("Send ack");
m_rfAck.start();
m_ackMinTimer.stop();
}
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
m_hangTimer.start();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::hangState(bool validSignal)
{
if (validSignal) {
io.setDecode(true);
io.setADCDetection(true);
DEBUG1("State to RELAYING");
m_state = FS_RELAYING;
DEBUG1("Stop ack");
m_rfAck.stop();
m_extAck.stop();
beginRelaying();
} else {
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
m_hangTimer.stop();
m_statusTimer.stop();
if (m_callsignAtEnd)
sendCallsign();
m_callsignTimer.stop();
}
}
@@ -643,18 +440,14 @@ void CFM::hangState(bool validRFSignal, bool validExtSignal)
}
}
void CFM::timeoutRFState(bool validSignal)
void CFM::timeoutState(bool validSignal)
{
if (!validSignal) {
io.setDecode(false);
io.setADCDetection(false);
DEBUG1("State to TIMEOUT_WAIT_RF");
m_state = FS_TIMEOUT_WAIT_RF;
if (m_callsignAtEnd)
sendCallsign();
DEBUG1("State to TIMEOUT_WAIT");
m_state = FS_TIMEOUT_WAIT;
m_ackDelayTimer.start();
}
@@ -664,21 +457,21 @@ void CFM::timeoutRFState(bool validSignal)
}
}
void CFM::timeoutRFWaitState(bool validSignal)
void CFM::timeoutWaitState(bool validSignal)
{
if (validSignal) {
io.setDecode(true);
io.setADCDetection(true);
DEBUG1("State to TIMEOUT_RF");
m_state = FS_TIMEOUT_RF;
DEBUG1("State to TIMEOUT");
m_state = FS_TIMEOUT;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
m_timeoutTone.stop();
DEBUG1("Send RF ack");
DEBUG1("Send ack");
m_rfAck.start();
m_ackDelayTimer.stop();
m_ackMinTimer.stop();
@@ -693,45 +486,6 @@ void CFM::timeoutRFWaitState(bool validSignal)
}
}
void CFM::timeoutExtState(bool validSignal)
{
if (!validSignal) {
DEBUG1("State to TIMEOUT_WAIT_EXT");
m_state = FS_TIMEOUT_WAIT_EXT;
m_ackDelayTimer.start();
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::timeoutExtWaitState(bool validSignal)
{
if (validSignal) {
DEBUG1("State to TIMEOUT_EXT");
m_state = FS_TIMEOUT_EXT;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
m_timeoutTone.stop();
DEBUG1("Send Ext ack");
m_extAck.start();
m_ackDelayTimer.stop();
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_hangTimer.start();
}
}
if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) {
sendCallsign();
m_callsignTimer.start();
}
}
void CFM::sendCallsign()
{
if (m_holdoffTimer.isRunning()) {
@@ -752,25 +506,12 @@ void CFM::beginRelaying()
m_ackMinTimer.start();
}
uint8_t CFM::getSpace() const
{
// The amount of free space for receiving external audio, in frames.
return m_inputExtRB.getSpace() / FM_SERIAL_BLOCK_SIZE_BYTES;
}
uint8_t CFM::writeData(const uint8_t* data, uint8_t length)
{
//todo check if length is a multiple of 3
m_inputExtRB.addData(data, length);
return 0U;
}
void CFM::insertDelay(uint16_t ms)
{
uint32_t nSamples = ms * 24U;
for (uint32_t i = 0U; i < nSamples; i++)
m_inputRFRB.put(0);
m_inputRB.put(0);
}
void CFM::insertSilence(uint16_t ms)
@@ -778,5 +519,5 @@ void CFM::insertSilence(uint16_t ms)
uint32_t nSamples = ms * 24U;
for (uint32_t i = 0U; i < nSamples; i++)
m_outputRFRB.put(0);
m_outputRB.put(0);
}
+22 -41
View File
@@ -27,32 +27,28 @@
#include "FMTimeout.h"
#include "FMKeyer.h"
#include "FMTimer.h"
#include "RingBuffer.h"
#include "FMRB.h"
#include "FMDirectForm1.h"
#include "FMDownsampler.h"
#include "FMUpSampler.h"
enum FM_STATE {
FS_LISTENING,
FS_KERCHUNK_RF,
FS_RELAYING_RF,
FS_RELAYING_WAIT_RF,
FS_TIMEOUT_RF,
FS_TIMEOUT_WAIT_RF,
FS_KERCHUNK_EXT,
FS_RELAYING_EXT,
FS_RELAYING_WAIT_EXT,
FS_TIMEOUT_EXT,
FS_TIMEOUT_WAIT_EXT,
FS_KERCHUNK,
FS_RELAYING,
FS_RELAYING_WAIT,
FS_TIMEOUT,
FS_TIMEOUT_WAIT,
FS_HANG
};
class CFM {
public:
CFM();
void samples(bool cos, q15_t* samples, uint8_t length);
void samples(bool cos, const q15_t* samples, uint8_t length);
void process();
@@ -60,17 +56,11 @@ public:
uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch);
uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level);
uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, bool kerchunkTX, uint8_t hangTime, bool useCOS, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel);
uint8_t setExt(const char* ack, uint8_t audioBoost, uint8_t speed, uint16_t frequency, uint8_t level);
uint8_t getSpace() const;
uint8_t writeData(const uint8_t* data, uint8_t length);
uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssHighThreshold, uint8_t ctcssLowThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, uint8_t accessMode, bool cosInvert, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel);
private:
CFMKeyer m_callsign;
CFMKeyer m_rfAck;
CFMKeyer m_extAck;
CFMCTCSSRX m_ctcssRX;
CFMCTCSSTX m_ctcssTX;
CFMTimeout m_timeoutTone;
@@ -82,39 +72,30 @@ private:
CFMTimer m_timeoutTimer;
CFMTimer m_holdoffTimer;
CFMTimer m_kerchunkTimer;
bool m_kerchunkTX;
CFMTimer m_ackMinTimer;
CFMTimer m_ackDelayTimer;
CFMTimer m_hangTimer;
CFMTimer m_statusTimer;
CFMTimer m_reverseTimer;
CFMDirectFormI m_filterStage1;
CFMDirectFormI m_filterStage2;
CFMDirectFormI m_filterStage3;
CFMBlanking m_blanking;
bool m_useCOS;
uint8_t m_accessMode;
bool m_cosInvert;
q15_t m_rfAudioBoost;
q15_t m_extAudioBoost;
CFMDownsampler m_downsampler;
bool m_extEnabled;
q15_t m_rxLevel;
CRingBuffer<q15_t> m_inputRFRB;
CRingBuffer<q15_t> m_outputRFRB;
CFMUpSampler m_inputExtRB;
CFMRB m_inputRB;
CFMRB m_outputRB;
void stateMachine(bool validRFSignal, bool validExtSignal);
void listeningState(bool validRFSignal, bool validExtSignal);
void kerchunkRFState(bool validSignal);
void relayingRFState(bool validSignal);
void relayingRFWaitState(bool validSignal);
void timeoutRFState(bool validSignal);
void timeoutRFWaitState(bool validSignal);
void kerchunkExtState(bool validSignal);
void relayingExtState(bool validSignal);
void relayingExtWaitState(bool validSignal);
void timeoutExtState(bool validSignal);
void timeoutExtWaitState(bool validSignal);
void hangState(bool validRFSignal, bool validExtSignal);
void stateMachine(bool validSignal);
void listeningState(bool validSignal);
void kerchunkState(bool validSignal);
void relayingState(bool validSignal);
void relayingWaitState(bool validSignal);
void timeoutState(bool validSignal);
void timeoutWaitState(bool validSignal);
void hangState(bool validSignal);
void clock(uint8_t length);
+6 -3
View File
@@ -115,11 +115,14 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level)
return 0U;
}
q15_t CFMCTCSSTX::getAudio()
q15_t CFMCTCSSTX::getAudio(bool reverse)
{
q15_t sample = m_values[m_n++];
if(m_n >= m_length)
if (m_n >= m_length)
m_n = 0U;
return sample;
if (reverse)
return -sample;
else
return sample;
}
+1 -1
View File
@@ -27,7 +27,7 @@ public:
uint8_t setParams(uint8_t frequency, uint8_t level);
q15_t getAudio();
q15_t getAudio(bool reverse);
private:
q15_t* m_values;
+1 -1
View File
@@ -29,7 +29,7 @@ THE SOFTWARE.
// based on https://raw.githubusercontent.com/berndporr/iir_fixed_point/master/DirectFormI.h
#include <cstdint>
#include "Globals.h"
#ifndef DIRECTFORMI_H_
#define DIRECTFORMI_H_
+11 -24
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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
@@ -17,19 +16,20 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "RingBuffer.h"
template <typename TDATATYPE> CRingBuffer<TDATATYPE>::CRingBuffer(uint16_t length) :
#include "FMDownsampleRB.h"
CFMDownsampleRB::CFMDownsampleRB(uint16_t length) :
m_length(length),
m_head(0U),
m_tail(0U),
m_full(false),
m_overflow(false)
{
m_buffer = new TDATATYPE[length];
m_samples = new uint8_t[length];
}
template <typename TDATATYPE> uint16_t CRingBuffer<TDATATYPE>::getSpace() const
uint16_t CFMDownsampleRB::getSpace() const
{
uint16_t n = 0U;
@@ -46,7 +46,7 @@ template <typename TDATATYPE> uint16_t CRingBuffer<TDATATYPE>::getSpace() const
return n;
}
template <typename TDATATYPE> uint16_t CRingBuffer<TDATATYPE>::getData() const
uint16_t CFMDownsampleRB::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
@@ -56,14 +56,14 @@ template <typename TDATATYPE> uint16_t CRingBuffer<TDATATYPE>::getData() const
return m_length - m_tail + m_head;
}
template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::put(TDATATYPE item) volatile
bool CFMDownsampleRB::put(uint8_t sample)
{
if (m_full) {
m_overflow = true;
return false;
}
m_buffer[m_head] = item;
m_samples[m_head] = sample;
m_head++;
if (m_head >= m_length)
@@ -75,17 +75,12 @@ template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::put(TDATATYPE item) v
return true;
}
template <typename TDATATYPE> TDATATYPE CRingBuffer<TDATATYPE>::peek() const
{
return m_buffer[m_tail];
}
template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::get(TDATATYPE& item) volatile
bool CFMDownsampleRB::get(uint8_t& sample)
{
if (m_head == m_tail && !m_full)
return false;
item = m_buffer[m_tail];
sample = m_samples[m_tail];
m_full = false;
@@ -96,7 +91,7 @@ template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::get(TDATATYPE& item)
return true;
}
template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::hasOverflowed()
bool CFMDownsampleRB::hasOverflowed()
{
bool overflow = m_overflow;
@@ -104,11 +99,3 @@ template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::hasOverflowed()
return overflow;
}
template <typename TDATATYPE> void CRingBuffer<TDATATYPE>::reset()
{
m_head = 0U;
m_tail = 0U;
m_full = false;
m_overflow = false;
}
+13 -22
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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
@@ -17,8 +16,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(RINGBUFFER_H)
#define RINGBUFFER_H
#if !defined(FMDOWNSAMPLERB_H)
#define FMDOWNSAMPLERB_H
#if defined(STM32F4XX)
#include "stm32f4xx.h"
@@ -29,7 +28,6 @@
#include <cstddef>
#else
#include <Arduino.h>
#undef PI
#endif
#if defined(__SAM3X8E__) || defined(STM32F105xC)
@@ -44,34 +42,27 @@
#include <arm_math.h>
template <typename TDATATYPE>
class CRingBuffer {
class CFMDownsampleRB {
public:
CRingBuffer(uint16_t length = 370U);
CFMDownsampleRB(uint16_t length);
uint16_t getSpace() const;
uint16_t getData() const;
bool put(TDATATYPE item) volatile;
bool put(uint8_t sample);
bool get(TDATATYPE& item) volatile;
TDATATYPE peek() const;
bool get(uint8_t& sample);
bool hasOverflowed();
void reset();
private:
uint16_t m_length;
TDATATYPE* m_buffer;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
bool m_overflow;
uint16_t m_length;
volatile uint8_t* m_samples;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
bool m_overflow;
};
#include "RingBuffer.impl.h"
#endif
#endif
+31 -42
View File
@@ -20,55 +20,44 @@
#include "Config.h"
#include "FMDownsampler.h"
CFMDownsampler::CFMDownsampler(uint16_t length) :
m_ringBuffer(length),
m_samplePack(0U),
m_samplePackPointer(NULL),
m_sampleIndex(0U)
m_ringBuffer(length),//length might need tweaking
m_packIndex(0),
m_downSampleIndex(0)
{
m_samplePackPointer = (uint8_t*)&m_samplePack;
m_samplePack = 0;
}
void CFMDownsampler::addSample(q15_t sample)
{
uint32_t usample = uint32_t(int32_t(sample) + 2048);
//only take one of three samples
switch(m_sampleIndex){
case 0:
m_samplePack = usample << 12;
break;
case 3:{
m_samplePack |= usample;
//we did not use MSB; skip it
TSamplePairPack pair{m_samplePackPointer[0U], m_samplePackPointer[1U], m_samplePackPointer[2U]};
//only take one of three samples
if(m_downSampleIndex == 0) {
switch(m_packIndex){
case 0:
m_samplePack = int32_t(sample) << 12;
break;
case 1:{
m_samplePack |= int32_t(sample);
//we did not use MSB; skip it
m_ringBuffer.put(m_samplePackBytes[1]);
m_ringBuffer.put(m_samplePackBytes[2]);
m_ringBuffer.put(m_samplePackBytes[3]);
m_ringBuffer.put(pair);
m_samplePack = 0U;//reset the sample pack
m_samplePack = 0;
}
break;
default:
//should never happen
break;
}
m_packIndex++;
if(m_packIndex >= 2)
m_packIndex = 0;
}
break;
default:
//Just skip this sample
break;
}
m_sampleIndex++;
if(m_sampleIndex >= 6U)//did we pack two samples ?
m_sampleIndex = 0U;
}
bool CFMDownsampler::getPackedData(TSamplePairPack& data)
{
return m_ringBuffer.get(data);
}
uint16_t CFMDownsampler::getData()
{
return m_ringBuffer.getData();
}
void CFMDownsampler::reset()
{
m_sampleIndex = 0U;
m_downSampleIndex++;
if(m_downSampleIndex >= 3)
m_downSampleIndex = 0;
}
+10 -9
View File
@@ -21,22 +21,23 @@
#define FMDOWNSAMPLER_H
#include "Config.h"
#include "RingBuffer.h"
#include "FMSamplePairPack.h"
#include "FMDownsampleRB.h"
class CFMDownsampler {
public:
CFMDownsampler(uint16_t length);
void addSample(q15_t sample);
bool getPackedData(TSamplePairPack& data);
uint16_t getData();
void reset();
inline bool getPackedData(uint8_t& data){ return m_ringBuffer.get(data); };
inline bool hasOverflowed() { return m_ringBuffer.hasOverflowed(); };
private:
CRingBuffer<TSamplePairPack> m_ringBuffer;
uint32_t m_samplePack;
uint8_t* m_samplePackPointer;
uint8_t m_sampleIndex;
CFMDownsampleRB m_ringBuffer;
union {
int32_t m_samplePack;
int8_t m_samplePackBytes[4];
};
uint8_t m_packIndex;
uint8_t m_downSampleIndex;
};
#endif
+110
View File
@@ -0,0 +1,110 @@
/*
TX fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "FMRB.h"
CFMRB::CFMRB(uint16_t length) :
m_length(length),
m_head(0U),
m_tail(0U),
m_full(false),
m_overflow(false)
{
m_samples = new q15_t[length];
}
uint16_t CFMRB::getSpace() const
{
uint16_t n = 0U;
if (m_tail == m_head)
n = m_full ? 0U : m_length;
else if (m_tail < m_head)
n = m_length - m_head + m_tail;
else
n = m_tail - m_head;
if (n > m_length)
n = 0U;
return n;
}
uint16_t CFMRB::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
else if (m_tail < m_head)
return m_head - m_tail;
else
return m_length - m_tail + m_head;
}
bool CFMRB::put(q15_t sample)
{
if (m_full) {
m_overflow = true;
return false;
}
m_samples[m_head] = sample;
m_head++;
if (m_head >= m_length)
m_head = 0U;
if (m_head == m_tail)
m_full = true;
return true;
}
bool CFMRB::get(q15_t& sample)
{
if (m_head == m_tail && !m_full)
return false;
sample = m_samples[m_tail];
m_full = false;
m_tail++;
if (m_tail >= m_length)
m_tail = 0U;
return true;
}
bool CFMRB::hasOverflowed()
{
bool overflow = m_overflow;
m_overflow = false;
return overflow;
}
void CFMRB::reset()
{
m_head = 0U;
m_tail = 0U;
m_full = false;
m_overflow = false;
}
+72
View File
@@ -0,0 +1,72 @@
/*
Serial fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#if !defined(FMRB_H)
#define FMRB_H
#if defined(STM32F4XX)
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#include <cstddef>
#else
#include <Arduino.h>
#endif
#if defined(__SAM3X8E__) || defined(STM32F105xC)
#define ARM_MATH_CM3
#elif defined(STM32F7XX)
#define ARM_MATH_CM7
#elif defined(STM32F4XX) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define ARM_MATH_CM4
#else
#error "Unknown processor type"
#endif
#include <arm_math.h>
class CFMRB {
public:
CFMRB(uint16_t length);
uint16_t getSpace() const;
uint16_t getData() const;
bool put(q15_t sample);
bool get(q15_t& sample);
bool hasOverflowed();
void reset();
private:
uint16_t m_length;
volatile q15_t* m_samples;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
bool m_overflow;
};
#endif
-29
View File
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(TSAMPLEPACK_H)
#define TSAMPLEPACK_H
struct TSamplePairPack {
uint8_t byte0;
uint8_t byte1;
uint8_t byte2;
};
#endif
+5 -7
View File
@@ -36,24 +36,22 @@ m_n(0U)
void CFMTimeout::setParams(uint8_t level)
{
m_level = q15_t(level * 128);
m_level = q15_t(level * 5);
}
q15_t CFMTimeout::getAudio()
{
q15_t sample = 0U;
q15_t sample = 0;
if (!m_running)
return sample;
if (m_pos > 12000U) {
q31_t sample = BUSY_AUDIO[m_n] * m_level;
sample = q15_t(__SSAT((sample >> 15), 16));
if (m_pos >= 12000U) {
q31_t sample31 = BUSY_AUDIO[m_n] * m_level;
sample = q15_t(__SSAT((sample31 >> 15), 16));
m_n++;
if (m_n >= BUSY_AUDIO_LEN)
m_n = 0U;
} else {
sample = 0U;
}
m_pos++;
-97
View File
@@ -1,97 +0,0 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "FMUpSampler.h"
const uint32_t FM_UPSAMPLE_MASK = 0x00000FFFU;
CFMUpSampler::CFMUpSampler() :
m_upSampleIndex(0),
m_pack(0U),
m_packPointer(NULL),
m_samples(3600U), //300ms of 12 bit 8kHz audio
m_running(false)
{
m_packPointer = (uint8_t*)&m_pack;
}
void CFMUpSampler::reset()
{
m_upSampleIndex = 0U;
m_pack = 0U;
m_samples.reset();
m_running = false;
}
void CFMUpSampler::addData(const uint8_t* data, uint16_t length)
{
TSamplePairPack* packPointer = (TSamplePairPack*)data;
TSamplePairPack* packPointerEnd = packPointer + (length / 3U);
while(packPointer != packPointerEnd) {
m_samples.put(*packPointer);
packPointer++;
}
if(!m_running)
m_running = m_samples.getData() > 300U;//75ms of audio
}
bool CFMUpSampler::getSample(q15_t& sample)
{
if(!m_running)
return false;
switch (m_upSampleIndex)
{
case 0: {
TSamplePairPack pairPack;
if(!m_samples.get(pairPack)) {
m_running = false;
return false;
}
m_pack = 0U;
m_packPointer = (uint8_t*)&m_pack;
m_packPointer[0U] = pairPack.byte0;
m_packPointer[1U] = pairPack.byte1;
m_packPointer[2U] = pairPack.byte2;
sample = q15_t(m_pack >> 12) - 2048;
break;
}
case 3:
sample = q15_t(m_pack & FM_UPSAMPLE_MASK) - 2048;
break;
default:
sample = 0;
break;
}
m_upSampleIndex++;
if(m_upSampleIndex >= 6U)
m_upSampleIndex = 0U;
return true;
}
uint16_t CFMUpSampler::getSpace() const
{
//return available space in bytes
return m_samples.getSpace() * sizeof(TSamplePairPack);
}
-48
View File
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(FMUPSAMPLER_H)
#define FMUPSAMPLER_H
#include "Config.h"
#include "RingBuffer.h"
#include "FMSamplePairPack.h"
class CFMUpSampler {
public:
CFMUpSampler();
void reset();
void addData(const uint8_t* data, uint16_t length);
bool getSample(q15_t& sample);
uint16_t getSpace() const;
private:
uint8_t m_upSampleIndex;
uint32_t m_pack;
uint8_t * m_packPointer;
CRingBuffer<TSamplePairPack> m_samples;
bool m_running;
};
#endif
+7 -1
View File
@@ -28,7 +28,6 @@
#include "STM32Utils.h"
#else
#include <Arduino.h>
#undef PI //Undefine PI to get rid of annoying warning as it is also defined in arm_math.h.
#endif
#if defined(__SAM3X8E__) || defined(STM32F105xC)
@@ -51,6 +50,7 @@ enum MMDVM_STATE {
STATE_P25 = 4,
STATE_NXDN = 5,
STATE_POCSAG = 6,
STATE_M17 = 7,
STATE_FM = 10,
// Dummy states start at 90
@@ -85,6 +85,8 @@ enum MMDVM_STATE {
#include "YSFTX.h"
#include "P25RX.h"
#include "P25TX.h"
#include "M17RX.h"
#include "M17TX.h"
#include "NXDNRX.h"
#include "NXDNTX.h"
#include "POCSAGTX.h"
@@ -124,6 +126,7 @@ extern bool m_ysfEnable;
extern bool m_p25Enable;
extern bool m_nxdnEnable;
extern bool m_pocsagEnable;
extern bool m_m17Enable;
extern bool m_fmEnable;
extern bool m_duplex;
@@ -155,6 +158,9 @@ extern CNXDNTX nxdnTX;
extern CPOCSAGTX pocsagTX;
extern CM17RX m17RX;
extern CM17TX m17TX;
extern CFM fm;
extern CCalDStarRX calDStarRX;
+138 -48
View File
@@ -32,6 +32,12 @@ static q15_t RRC_0_2_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 147
-553, -847, -731, -340, 104, 401, 0};
const uint16_t RRC_0_2_FILTER_LEN = 42U;
// Generated using rcosdesign(0.5, 8, 5, 'sqrt') in MATLAB
static q15_t RRC_0_5_FILTER[] = {-147, -88, 72, 220, 223, 46, -197, -285, -79, 334, 623, 390, -498, -1691, -2363, -1556, 1284, 5872, 11033,
15109, 16656, 15109, 11033, 5872, 1284, -1556, -2363, -1691, -498, 390, 623, 334, -79, -285, -197, 46, 223,
220, 72, -88, -147, 0};
const uint16_t RRC_0_5_FILTER_LEN = 42U;
// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB
static q15_t NXDN_0_2_FILTER[] = {284, 198, 73, -78, -240, -393, -517, -590, -599, -533, -391, -181, 79, 364, 643, 880, 1041, 1097, 1026, 819,
483, 39, -477, -1016, -1516, -1915, -2150, -2164, -1914, -1375, -545, 557, 1886, 3376, 4946, 6502, 7946, 9184,
@@ -61,12 +67,14 @@ m_txBuffer(TX_RINGBUFFER_SIZE),
m_rssiBuffer(RX_RINGBUFFER_SIZE),
m_dcFilter(),
m_dcState(),
m_rrcFilter(),
m_rrc02Filter(),
m_rrc05Filter(),
m_gaussianFilter(),
m_boxcarFilter(),
m_nxdnFilter(),
m_nxdnISincFilter(),
m_rrcState(),
m_rrc02State(),
m_rrc05State(),
m_gaussianState(),
m_boxcarState(),
m_nxdnState(),
@@ -79,10 +87,12 @@ m_dmrTXLevel(128 * 128),
m_ysfTXLevel(128 * 128),
m_p25TXLevel(128 * 128),
m_nxdnTXLevel(128 * 128),
m_m17TXLevel(128 * 128),
m_pocsagTXLevel(128 * 128),
m_fmTXLevel(128 * 128),
m_rxDCOffset(DC_OFFSET),
m_txDCOffset(DC_OFFSET),
m_useCOSAsLockout(false),
m_ledCount(0U),
m_ledValue(true),
m_detect(false),
@@ -91,7 +101,8 @@ m_dacOverflow(0U),
m_watchdog(0U),
m_lockout(false)
{
::memset(m_rrcState, 0x00U, 70U * sizeof(q15_t));
::memset(m_rrc02State, 0x00U, 70U * sizeof(q15_t));
::memset(m_rrc05State, 0x00U, 70U * sizeof(q15_t));
::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t));
::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t));
::memset(m_nxdnState, 0x00U, 110U * sizeof(q15_t));
@@ -103,9 +114,13 @@ m_lockout(false)
m_dcFilter.pCoeffs = DC_FILTER;
m_dcFilter.postShift = 0;
m_rrcFilter.numTaps = RRC_0_2_FILTER_LEN;
m_rrcFilter.pState = m_rrcState;
m_rrcFilter.pCoeffs = RRC_0_2_FILTER;
m_rrc02Filter.numTaps = RRC_0_2_FILTER_LEN;
m_rrc02Filter.pState = m_rrc02State;
m_rrc02Filter.pCoeffs = RRC_0_2_FILTER;
m_rrc05Filter.numTaps = RRC_0_5_FILTER_LEN;
m_rrc05Filter.pState = m_rrc05State;
m_rrc05Filter.pCoeffs = RRC_0_5_FILTER;
m_gaussianFilter.numTaps = GAUSSIAN_0_5_FILTER_LEN;
m_gaussianFilter.pState = m_gaussianState;
@@ -132,7 +147,7 @@ void CIO::selfTest()
{
bool ledValue = false;
for (uint8_t i = 0; i < 6; i++) {
for (uint8_t i = 0U; i < 6U; i++) {
ledValue = !ledValue;
// We exclude PTT to avoid trigger the transmitter
@@ -143,21 +158,40 @@ void CIO::selfTest()
setDMRInt(ledValue);
setYSFInt(ledValue);
setP25Int(ledValue);
#if !defined(USE_ALTERNATE_NXDN_LEDS)
setNXDNInt(ledValue);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
setM17Int(ledValue);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
setPOCSAGInt(ledValue);
#endif
#if !defined(USE_ALTERNATE_FM_LEDS)
setFMInt(ledValue);
#endif
#endif
delayInt(250);
}
#if defined(MODE_LEDS)
setDStarInt(true);
setDStarInt(false);
setDMRInt(false);
setYSFInt(false);
setP25Int(false);
#if !defined(USE_ALTERNATE_NXDN_LEDS)
setNXDNInt(false);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
setM17Int(false);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
setPOCSAGInt(false);
#endif
#if !defined(USE_ALTERNATE_FM_LEDS)
setFMInt(false);
#endif
setDStarInt(true);
delayInt(250);
setDMRInt(true);
@@ -173,6 +207,11 @@ void CIO::selfTest()
setNXDNInt(true);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
delayInt(250);
setM17Int(true);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
delayInt(250);
setPOCSAGInt(true);
@@ -191,6 +230,11 @@ void CIO::selfTest()
setPOCSAGInt(false);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
delayInt(250);
setM17Int(false);
#endif
#if !defined(USE_ALTERNATE_NXDN_LEDS)
delayInt(250);
setNXDNInt(false);
@@ -219,7 +263,7 @@ void CIO::start()
m_started = true;
setMode();
setMode(STATE_IDLE);
}
void CIO::process()
@@ -228,11 +272,10 @@ void CIO::process()
if (m_started) {
// Two seconds timeout
if (m_watchdog >= 48000U) {
if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN || m_modemState == STATE_POCSAG) {
if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN || m_modemState == STATE_M17 || m_modemState == STATE_POCSAG) {
if (m_modemState == STATE_DMR && m_tx)
dmrTX.setStart(false);
m_modemState = STATE_IDLE;
setMode();
setMode(STATE_IDLE);
}
m_watchdog = 0U;
@@ -256,15 +299,13 @@ void CIO::process()
return;
}
#if defined(USE_COS_AS_LOCKOUT)
m_lockout = getCOSInt();
#endif
if (m_useCOSAsLockout)
m_lockout = getCOSInt();
// Switch off the transmitter if needed
if (m_txBuffer.getData() == 0U && m_tx) {
m_tx = false;
setPTTInt(m_pttInvert ? true : false);
DEBUG1("TX OFF");
}
if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) {
@@ -273,16 +314,15 @@ void CIO::process()
uint16_t rssi[RX_BLOCK_SIZE];
for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) {
TSample sample;
m_rxBuffer.get(sample);
control[i] = sample.control;
uint16_t sample;
m_rxBuffer.get(sample, control[i]);
m_rssiBuffer.get(rssi[i]);
// Detect ADC overflow
if (m_detect && (sample.sample == 0U || sample.sample == 4095U))
if (m_detect && (sample == 0U || sample == 4095U))
m_adcOverflow++;
q15_t res1 = q15_t(sample.sample) - m_rxDCOffset;
q15_t res1 = q15_t(sample) - m_rxDCOffset;
q31_t res2 = res1 * m_rxLevel;
samples[i] = q15_t(__SSAT((res2 >> 15), 16));
}
@@ -343,19 +383,34 @@ void CIO::process()
nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE);
}
if (m_dmrEnable || m_ysfEnable) {
if (m_dmrEnable) {
q15_t DMRVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_rrc02Filter, samples, DMRVals, RX_BLOCK_SIZE);
if (m_duplex)
dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE);
else
dmrDMORX.samples(DMRVals, rssi, RX_BLOCK_SIZE);
}
if (m_ysfEnable) {
q15_t YSFVals[RX_BLOCK_SIZE];
#if defined(USE_DCBLOCKER)
::arm_fir_fast_q15(&m_rrc02Filter, dcSamples, YSFVals, RX_BLOCK_SIZE);
#else
::arm_fir_fast_q15(&m_rrc02Filter, samples, YSFVals, RX_BLOCK_SIZE);
#endif
ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE);
}
if (m_m17Enable) {
q15_t RRCVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_rrcFilter, samples, RRCVals, RX_BLOCK_SIZE);
if (m_ysfEnable)
ysfRX.samples(RRCVals, rssi, RX_BLOCK_SIZE);
if (m_dmrEnable) {
if (m_duplex)
dmrIdleRX.samples(RRCVals, RX_BLOCK_SIZE);
else
dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE);
}
#if defined(USE_DCBLOCKER)
::arm_fir_fast_q15(&m_rrc05Filter, dcSamples, RRCVals, RX_BLOCK_SIZE);
#else
::arm_fir_fast_q15(&m_rrc05Filter, samples, RRCVals, RX_BLOCK_SIZE);
#endif
m17RX.samples(RRCVals, rssi, RX_BLOCK_SIZE);
}
if (m_fmEnable) {
@@ -379,7 +434,7 @@ void CIO::process()
} else if (m_modemState == STATE_DMR) {
if (m_dmrEnable) {
q15_t DMRVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_rrcFilter, samples, DMRVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter, samples, DMRVals, RX_BLOCK_SIZE);
if (m_duplex) {
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
@@ -395,9 +450,9 @@ void CIO::process()
if (m_ysfEnable) {
q15_t YSFVals[RX_BLOCK_SIZE];
#if defined(USE_DCBLOCKER)
::arm_fir_fast_q15(&m_rrcFilter, dcSamples, YSFVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter, dcSamples, YSFVals, RX_BLOCK_SIZE);
#else
::arm_fir_fast_q15(&m_rrcFilter, samples, YSFVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter, samples, YSFVals, RX_BLOCK_SIZE);
#endif
ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE);
}
@@ -424,6 +479,16 @@ void CIO::process()
nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE);
}
} else if (m_modemState == STATE_M17) {
if (m_m17Enable) {
q15_t M17Vals[RX_BLOCK_SIZE];
#if defined(USE_DCBLOCKER)
::arm_fir_fast_q15(&m_rrc05Filter, dcSamples, M17Vals, RX_BLOCK_SIZE);
#else
::arm_fir_fast_q15(&m_rrc05Filter, samples, M17Vals, RX_BLOCK_SIZE);
#endif
m17RX.samples(M17Vals, rssi, RX_BLOCK_SIZE);
}
} else if (m_modemState == STATE_FM) {
bool cos = getCOSInt();
#if defined(USE_DCBLOCKER)
@@ -454,7 +519,6 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t
if (!m_tx) {
m_tx = true;
setPTTInt(m_pttInvert ? false : true);
DEBUG1("TX ON");
}
q15_t txLevel = 0;
@@ -474,6 +538,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t
case STATE_NXDN:
txLevel = m_nxdnTXLevel;
break;
case STATE_M17:
txLevel = m_m17TXLevel;
break;
case STATE_POCSAG:
txLevel = m_pocsagTXLevel;
break;
@@ -495,9 +562,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t
m_dacOverflow++;
if (control == NULL)
m_txBuffer.put({res3, MARK_NONE});
m_txBuffer.put(res3, MARK_NONE);
else
m_txBuffer.put({res3, control[i]});
m_txBuffer.put(res3, control[i]);
}
}
@@ -519,20 +586,39 @@ void CIO::setADCDetection(bool detect)
m_detect = detect;
}
void CIO::setMode()
void CIO::setMode(MMDVM_STATE state)
{
if (state == m_modemState)
return;
#if defined(MODE_LEDS)
setDStarInt(m_modemState == STATE_DSTAR);
setDMRInt(m_modemState == STATE_DMR);
setYSFInt(m_modemState == STATE_YSF);
setP25Int(m_modemState == STATE_P25);
setNXDNInt(m_modemState == STATE_NXDN);
setPOCSAGInt(m_modemState == STATE_POCSAG);
setFMInt(m_modemState == STATE_FM);
switch (m_modemState) {
case STATE_DSTAR: setDStarInt(false); break;
case STATE_DMR: setDMRInt(false); break;
case STATE_YSF: setYSFInt(false); break;
case STATE_P25: setP25Int(false); break;
case STATE_NXDN: setNXDNInt(false); break;
case STATE_M17: setM17Int(false); break;
case STATE_POCSAG: setPOCSAGInt(false); break;
case STATE_FM: setFMInt(false); break;
}
switch (state) {
case STATE_DSTAR: setDStarInt(true); break;
case STATE_DMR: setDMRInt(true); break;
case STATE_YSF: setYSFInt(true); break;
case STATE_P25: setP25Int(true); break;
case STATE_NXDN: setNXDNInt(true); break;
case STATE_M17: setM17Int(true); break;
case STATE_POCSAG: setPOCSAGInt(true); break;
case STATE_FM: setFMInt(true); break;
}
#endif
m_modemState = state;
}
void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset)
void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t m17TXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset, bool useCOSAsLockout)
{
m_pttInvert = pttInvert;
@@ -543,11 +629,14 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx
m_ysfTXLevel = q15_t(ysfTXLevel * 128);
m_p25TXLevel = q15_t(p25TXLevel * 128);
m_nxdnTXLevel = q15_t(nxdnTXLevel * 128);
m_m17TXLevel = q15_t(m17TXLevel * 128);
m_pocsagTXLevel = q15_t(pocsagTXLevel * 128);
m_fmTXLevel = q15_t(fmTXLevel * 128);
m_rxDCOffset = DC_OFFSET + rxDCOffset;
m_txDCOffset = DC_OFFSET + txDCOffset;
m_useCOSAsLockout = useCOSAsLockout;
if (rxInvert)
m_rxLevel = -m_rxLevel;
@@ -558,6 +647,7 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx
m_ysfTXLevel = -m_ysfTXLevel;
m_p25TXLevel = -m_p25TXLevel;
m_nxdnTXLevel = -m_nxdnTXLevel;
m_m17TXLevel = -m_m17TXLevel;
m_pocsagTXLevel = -m_pocsagTXLevel;
}
}
+17 -15
View File
@@ -21,12 +21,8 @@
#include "Globals.h"
#include "RingBuffer.h"
struct TSample {
volatile uint16_t sample;
volatile uint8_t control;
};
#include "SampleRB.h"
#include "RSSIRB.h"
class CIO {
public:
@@ -42,11 +38,11 @@ public:
void setDecode(bool dcd);
void setADCDetection(bool detect);
void setMode();
void setMode(MMDVM_STATE state);
void interrupt();
void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset);
void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t m17TXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset, bool useCOSAsLockout);
void getOverflow(bool& adcOverflow, bool& dacOverflow);
@@ -61,23 +57,25 @@ public:
void selfTest();
private:
bool m_started;
bool m_started;
CRingBuffer<TSample> m_rxBuffer;
CRingBuffer<TSample> m_txBuffer;
CRingBuffer<uint16_t> m_rssiBuffer;
CSampleRB m_rxBuffer;
CSampleRB m_txBuffer;
CRSSIRB m_rssiBuffer;
arm_biquad_casd_df1_inst_q31 m_dcFilter;
q31_t m_dcState[4];
arm_fir_instance_q15 m_rrcFilter;
arm_fir_instance_q15 m_rrc02Filter;
arm_fir_instance_q15 m_rrc05Filter;
arm_fir_instance_q15 m_gaussianFilter;
arm_fir_instance_q15 m_boxcarFilter;
arm_fir_instance_q15 m_nxdnFilter;
arm_fir_instance_q15 m_nxdnISincFilter;
q15_t m_rrcState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
q15_t m_rrc02State[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
q15_t m_rrc05State[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare
q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare
q15_t m_nxdnState[110U]; // NoTaps + BlockSize - 1, 82 + 20 - 1 plus some spare
q15_t m_nxdnISincState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare
@@ -89,12 +87,15 @@ private:
q15_t m_ysfTXLevel;
q15_t m_p25TXLevel;
q15_t m_nxdnTXLevel;
q15_t m_m17TXLevel;
q15_t m_pocsagTXLevel;
q15_t m_fmTXLevel;
uint16_t m_rxDCOffset;
uint16_t m_txDCOffset;
bool m_useCOSAsLockout;
uint32_t m_ledCount;
bool m_ledValue;
@@ -123,6 +124,7 @@ private:
void setP25Int(bool on);
void setNXDNInt(bool on);
void setPOCSAGInt(bool on);
void setM17Int(bool on);
void setFMInt(bool on);
void delayInt(unsigned int dly);
+19 -5
View File
@@ -107,6 +107,9 @@ void CIO::initInt()
#if !defined(USE_ALTERNATE_NXDN_LEDS)
pinMode(PIN_NXDN, OUTPUT);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
pinMode(PIN_M17, OUTPUT);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
pinMode(PIN_POCSAG, OUTPUT);
#endif
@@ -185,13 +188,14 @@ void CIO::startInt()
void CIO::interrupt()
{
if ((ADC->ADC_ISR & ADC_ISR_EOC_Chan) == ADC_ISR_EOC_Chan) { // Ensure there was an End-of-Conversion and we read the ISR reg
TSample sample = {DC_OFFSET, MARK_NONE};
uint8_t control = MARK_NONE;
uint16_t sample = DC_OFFSET;
m_txBuffer.get(sample);
DACC->DACC_CDR = sample.sample;
m_txBuffer.get(sample, control);
DACC->DACC_CDR = sample;
sample.sample = ADC->ADC_CDR[ADC_CDR_Chan];
m_rxBuffer.put(sample);
sample = ADC->ADC_CDR[ADC_CDR_Chan];
m_rxBuffer.put(sample, control);
#if defined(SEND_RSSI_DATA)
m_rssiBuffer.put(ADC->ADC_CDR[RSSI_CDR_Chan]);
@@ -253,6 +257,16 @@ void CIO::setNXDNInt(bool on)
#endif
}
void CIO::setM17Int(bool on)
{
#if defined(USE_ALTERNATE_M17_LEDS)
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
digitalWrite(PIN_P25, on ? HIGH : LOW);
#else
digitalWrite(PIN_M17, on ? HIGH : LOW);
#endif
}
void CIO::setPOCSAGInt(bool on)
{
#if defined(USE_ALTERNATE_POCSAG_LEDS)
+4 -1
View File
@@ -62,9 +62,12 @@
#elif defined(DRCC_DVM_NQF)
#include "pins/pins_f4_drcc_nqf.h"
#elif defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
#include "pins/pins_f4_stm32eda.h"
#else
#error "A valid board type macro need to be defined."
#endif
#endif //#ifndef _IO_PINS_H
#endif //#ifndef _IO_PINS_H
+45 -7
View File
@@ -106,6 +106,14 @@ void CIO::initInt()
GPIO_Init(PORT_NXDN, &GPIO_InitStruct);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
// M17 pin
RCC_AHB1PeriphClockCmd(RCC_Per_M17, ENABLE);
GPIO_InitStruct.GPIO_Pin = PIN_M17;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(PORT_M17, &GPIO_InitStruct);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
// POCSAG pin
RCC_AHB1PeriphClockCmd(RCC_Per_POCSAG, ENABLE);
@@ -157,6 +165,14 @@ void CIO::initInt()
GPIO_Init(PORT_MNXDN, &GPIO_InitStruct);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
// M17 mode pin
RCC_AHB1PeriphClockCmd(RCC_Per_MM17, ENABLE);
GPIO_InitStruct.GPIO_Pin = PIN_MM17;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(PORT_MM17, &GPIO_InitStruct);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
// POCSAG mode pin
RCC_AHB1PeriphClockCmd(RCC_Per_MPOCSAG, ENABLE);
@@ -326,24 +342,25 @@ void CIO::startInt()
void CIO::interrupt()
{
TSample sample = {DC_OFFSET, MARK_NONE};
uint8_t control = MARK_NONE;
uint16_t sample = DC_OFFSET;
uint16_t rawRSSI = 0U;
m_txBuffer.get(sample);
m_txBuffer.get(sample, control);
// Send the value to the DAC
#if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)
DAC_SetChannel2Data(DAC_Align_12b_R, sample.sample);
DAC_SetChannel2Data(DAC_Align_12b_R, sample);
#else
DAC_SetChannel1Data(DAC_Align_12b_R, sample.sample);
DAC_SetChannel1Data(DAC_Align_12b_R, sample);
#endif
// Read value from ADC1 and ADC2
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)) {
// shouldn't be still in reset at this point so null the sample value?
sample.sample = 0U;
sample = 0U;
} else {
sample.sample = ADC_GetConversionValue(ADC1);
sample = ADC_GetConversionValue(ADC1);
#if defined(SEND_RSSI_DATA)
rawRSSI = ADC_GetConversionValue(ADC2);
#endif
@@ -353,7 +370,7 @@ void CIO::interrupt()
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
ADC_SoftwareStartConv(ADC1);
m_rxBuffer.put(sample);
m_rxBuffer.put(sample, control);
m_rssiBuffer.put(rawRSSI);
m_watchdog++;
@@ -440,6 +457,27 @@ void CIO::setNXDNInt(bool on)
#endif
}
void CIO::setM17Int(bool on)
{
#if defined(MODE_LEDS)
#if defined(USE_ALTERNATE_M17_LEDS)
GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET);
GPIO_WriteBit(PORT_P25, PIN_P25, on ? Bit_SET : Bit_RESET);
#else
GPIO_WriteBit(PORT_M17, PIN_M17, on ? Bit_SET : Bit_RESET);
#endif
#endif
#if defined(MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && (defined(STM32F4_NUCLEO) || defined(STM32F722_RPT_HAT))
#if defined(USE_ALTERNATE_M17_LEDS)
GPIO_WriteBit(PORT_MDSTAR, PIN_MDSTAR, on ? Bit_SET : Bit_RESET);
GPIO_WriteBit(PORT_MP25, PIN_MP25, on ? Bit_SET : Bit_RESET);
#else
GPIO_WriteBit(PORT_MM17, PIN_MM17, on ? Bit_SET : Bit_RESET);
#endif
#endif
}
void CIO::setPOCSAGInt(bool on)
{
#if defined(MODE_LEDS)
+32 -50
View File
@@ -41,7 +41,6 @@ YSF PB8 output
P25 PB9 output
NXDN PB10 output
POCSAG PB11 output
FM PB12 output
RX PB0 analog input (ADC1_8)
RSSI PB1 analog input (ADC2_9)
@@ -85,9 +84,6 @@ USART1_RXD PA10 input (AF)
#define PIN_POCSAG 11
#define PORT_POCSAG GPIOB
#define BB_POCSAG *((bitband_t)BITBAND_PERIPH(&PORT_POCSAG->ODR, PIN_POCSAG))
#define PIN_FM 14
#define PORT_FM GPIOB
#define BB_FM *((bitband_t)BITBAND_PERIPH(&PORT_FM->ODR, PIN_FM))
#define PIN_RX 0
#define PIN_RX_ADC_CH 8
@@ -158,35 +154,16 @@ void GPIOConfigPin(GPIO_TypeDef *port_ptr, uint32_t pin, uint32_t mode_cnf_value
#if defined(STM32F1_POG)
void FancyLEDEffect()
{
int ledCount = 10;
bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25,
#if defined(USE_ALTERNATE_NXDN_LEDS)
NULL,
#else
&BB_NXDN,
#endif
#if defined(USE_ALTERNATE_POCSAG_LEDS)
NULL,
#else
&BB_POCSAG,
#endif
#if defined(USE_ALTERNATE_FM_LEDS)
NULL};
#else
&BB_FM};
#endif
for(int i=0; i<ledCount; i++){
if(foo[i] != NULL)
*foo[i] = 0x01;
bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25};
for(int i=0; i<7; i++){
*foo[i] = 0x01;
}
GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1);
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x00;
delay(SystemCoreClock/1000*100);
for(int i=0; i<ledCount; i++){
if(foo[i] != NULL)
*foo[i] = 0x00;
for(int i=0; i<7; i++){
*foo[i] = 0x00;
}
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01;
delay(SystemCoreClock/1000*20);
@@ -195,22 +172,18 @@ void FancyLEDEffect()
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01;
*foo[0] = 0x01;
for(int i=1; i<ledCount; i++){
for(int i=1; i<7; i++){
delay(SystemCoreClock/1000*10);
if (foo[i-1] != NULL)
*foo[i-1] = 0x00;
if (foo[i] != NULL)
*foo[i] = 0x01;
*foo[i-1] = 0x00;
*foo[i] = 0x01;
}
for(int i=ledCount - 2; i>=0; i--) {
for(int i=5; i>=0; i--){
delay(SystemCoreClock/1000*10);
if (foo[i+1] != NULL)
*foo[i+1] = 0x00;
if (foo[i] != NULL)
*foo[i] = 0x01;
*foo[i+1] = 0x00;
*foo[i] = 0x01;
}
delay(SystemCoreClock/1000*10);
*foo[0] = 0x00;
*foo[5+1-6] = 0x00;
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x00;
delay(SystemCoreClock/1000*10);
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01;
@@ -252,12 +225,12 @@ static inline void GPIOInit()
#if !defined(USE_ALTERNATE_NXDN_LEDS)
GPIOConfigPin(PORT_NXDN, PIN_NXDN, GPIO_CRL_MODE0_1);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
GPIOConfigPin(PORT_M17, PIN_M17, GPIO_CRL_MODE0_1);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
GPIOConfigPin(PORT_POCSAG, PIN_POCSAG, GPIO_CRL_MODE0_1);
#endif
#if !defined(USE_ALTERNATE_FM_LEDS)
GPIOConfigPin(PORT_FM, PIN_FM, GPIO_CRL_MODE0_1);
#endif
GPIOConfigPin(PORT_RX, PIN_RX, 0);
#if defined(SEND_RSSI_DATA)
@@ -397,7 +370,8 @@ void CIO::startInt()
void CIO::interrupt()
{
TSample sample = {DC_OFFSET, MARK_NONE};
uint8_t control = MARK_NONE;
uint16_t sample = DC_OFFSET;
#if defined(SEND_RSSI_DATA)
uint16_t rawRSSI = 0U;
#endif
@@ -414,12 +388,12 @@ void CIO::interrupt()
*rssi_adon = 1;
#endif
m_txBuffer.get(sample);
DAC->DHR12R1 = sample.sample; // Send the value to the DAC
m_txBuffer.get(sample, control);
DAC->DHR12R1 = sample; // Send the value to the DAC
// Read value from ADC1 and ADC2
sample.sample = ADC1->DR; // read conversion result; EOC is cleared by this read
m_rxBuffer.put(sample);
sample = ADC1->DR; // read conversion result; EOC is cleared by this read
m_rxBuffer.put(sample, control);
#if defined(SEND_RSSI_DATA)
rawRSSI = ADC2->DR;
m_rssiBuffer.put(rawRSSI);
@@ -479,6 +453,16 @@ void CIO::setNXDNInt(bool on)
#endif
}
void CIO::setM17Int(bool on)
{
#if defined(USE_ALTERNATE_M17_LEDS)
BB_DSTAR = !!on;
BB_P25 = !!on;
#else
BB_M17 = !!on;
#endif
}
void CIO::setPOCSAGInt(bool on)
{
#if defined(USE_ALTERNATE_POCSAG_LEDS)
@@ -494,8 +478,6 @@ void CIO::setFMInt(bool on)
#if defined(USE_ALTERNATE_FM_LEDS)
BB_DSTAR = !!on;
BB_YSF = !!on;
#else
BB_FM = !!on;
#endif
}
+19 -5
View File
@@ -75,6 +75,9 @@ void CIO::initInt()
#if !defined(USE_ALTERNATE_NXDN_LEDS)
pinMode(PIN_NXDN, OUTPUT);
#endif
#if !defined(USE_ALTERNATE_M17_LEDS)
pinMode(PIN_M17, OUTPUT);
#endif
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
pinMode(PIN_POCSAG, OUTPUT);
#endif
@@ -161,14 +164,15 @@ void CIO::startInt()
void CIO::interrupt()
{
TSample sample = {DC_OFFSET, MARK_NONE};
uint8_t control = MARK_NONE;
uint16_t sample = DC_OFFSET;
m_txBuffer.get(sample);
*(int16_t *)&(DAC0_DAT0L) = sample.sample;
m_txBuffer.get(sample, control);
*(int16_t *)&(DAC0_DAT0L) = sample;
if ((ADC0_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
sample.sample = ADC0_RA;
m_rxBuffer.put(sample);
sample = ADC0_RA;
m_rxBuffer.put(sample, control);
}
#if defined(SEND_RSSI_DATA)
@@ -237,6 +241,16 @@ void CIO::setNXDNInt(bool on)
#endif
}
void CIO::setM17Int(bool on)
{
#if defined(USE_ALTERNATE_M17_LEDS)
digitalWrite(PIN_DSTAR, on ? HIGH : LOW);
digitalWrite(PIN_P25, on ? HIGH : LOW);
#else
digitalWrite(PIN_M17, on ? HIGH : LOW);
#endif
}
void CIO::setPOCSAGInt(bool on)
{
#if defined(USE_ALTERNATE_POCSAG_LEDS)
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016,2017,2018,2020 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(M17DEFINES_H)
#define M17DEFINES_H
const unsigned int M17_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
const unsigned int M17_FRAME_LENGTH_BITS = 384U;
const unsigned int M17_FRAME_LENGTH_BYTES = M17_FRAME_LENGTH_BITS / 8U;
const unsigned int M17_FRAME_LENGTH_SYMBOLS = M17_FRAME_LENGTH_BITS / 2U;
const unsigned int M17_FRAME_LENGTH_SAMPLES = M17_FRAME_LENGTH_SYMBOLS * M17_RADIO_SYMBOL_LENGTH;
const unsigned int M17_SYNC_LENGTH_BITS = 16U;
const unsigned int M17_SYNC_LENGTH_SYMBOLS = M17_SYNC_LENGTH_BITS / 2U;
const unsigned int M17_SYNC_LENGTH_SAMPLES = M17_SYNC_LENGTH_SYMBOLS * M17_RADIO_SYMBOL_LENGTH;
const uint8_t M17_SYNC_BYTES[] = {0x32U, 0x43U};
const uint8_t M17_SYNC_BYTES_LENGTH = 2U;
const uint16_t M17_SYNC_BITS = 0x3243U;
// 3 2 4 3
// 00 11 00 10 01 00 00 11
// +1 -3 +1 -1 +3 +1 +1 -3
const int8_t M17_SYNC_SYMBOLS_VALUES[] = {+1, -3, +1, -1, +3, +1, +1, -3};
const uint8_t M17_SYNC_SYMBOLS = 0xAEU;
#endif
+404
View File
@@ -0,0 +1,404 @@
/*
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Config.h"
#include "Globals.h"
#include "M17RX.h"
#include "Utils.h"
const q15_t SCALING_FACTOR = 18750; // Q15(0.55)
const uint8_t MAX_SYNC_BIT_START_ERRS = 2U;
const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U;
const uint8_t MAX_SYNC_SYMBOLS_ERRS = 3U;
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
const uint8_t NOAVEPTR = 99U;
const uint16_t NOENDPTR = 9999U;
const unsigned int MAX_SYNC_FRAMES = 1U + 1U;
CM17RX::CM17RX() :
m_state(M17RXS_NONE),
m_bitBuffer(),
m_buffer(),
m_bitPtr(0U),
m_dataPtr(0U),
m_startPtr(NOENDPTR),
m_endPtr(NOENDPTR),
m_syncPtr(NOENDPTR),
m_minSyncPtr(NOENDPTR),
m_maxSyncPtr(NOENDPTR),
m_maxCorr(0),
m_lostCount(0U),
m_countdown(0U),
m_centre(),
m_centreVal(0),
m_threshold(),
m_thresholdVal(0),
m_averagePtr(NOAVEPTR),
m_rssiAccum(0U),
m_rssiCount(0U)
{
}
void CM17RX::reset()
{
m_state = M17RXS_NONE;
m_dataPtr = 0U;
m_bitPtr = 0U;
m_maxCorr = 0;
m_averagePtr = NOAVEPTR;
m_startPtr = NOENDPTR;
m_endPtr = NOENDPTR;
m_syncPtr = NOENDPTR;
m_minSyncPtr = NOENDPTR;
m_maxSyncPtr = NOENDPTR;
m_centreVal = 0;
m_thresholdVal = 0;
m_lostCount = 0U;
m_countdown = 0U;
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
void CM17RX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
{
for (uint8_t i = 0U; i < length; i++) {
q15_t sample = samples[i];
m_rssiAccum += rssi[i];
m_rssiCount++;
m_bitBuffer[m_bitPtr] <<= 1;
if (sample < 0)
m_bitBuffer[m_bitPtr] |= 0x01U;
m_buffer[m_dataPtr] = sample;
switch (m_state) {
case M17RXS_DATA:
processData(sample);
break;
default:
processNone(sample);
break;
}
m_dataPtr++;
if (m_dataPtr >= M17_FRAME_LENGTH_SAMPLES)
m_dataPtr = 0U;
m_bitPtr++;
if (m_bitPtr >= M17_RADIO_SYMBOL_LENGTH)
m_bitPtr = 0U;
}
}
void CM17RX::processNone(q15_t sample)
{
bool ret = correlateSync();
if (ret) {
// On the first sync, start the countdown to the state change
if (m_countdown == 0U) {
m_rssiAccum = 0U;
m_rssiCount = 0U;
io.setDecode(true);
io.setADCDetection(true);
m_averagePtr = NOAVEPTR;
m_countdown = 5U;
}
}
if (m_countdown > 0U)
m_countdown--;
if (m_countdown == 1U) {
m_minSyncPtr = m_syncPtr + M17_FRAME_LENGTH_SAMPLES - 1U;
if (m_minSyncPtr >= M17_FRAME_LENGTH_SAMPLES)
m_minSyncPtr -= M17_FRAME_LENGTH_SAMPLES;
m_maxSyncPtr = m_syncPtr + 1U;
if (m_maxSyncPtr >= M17_FRAME_LENGTH_SAMPLES)
m_maxSyncPtr -= M17_FRAME_LENGTH_SAMPLES;
m_state = M17RXS_DATA;
m_countdown = 0U;
}
}
void CM17RX::processData(q15_t sample)
{
if (m_minSyncPtr < m_maxSyncPtr) {
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
correlateSync();
} else {
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
correlateSync();
}
if (m_dataPtr == m_endPtr) {
// Only update the centre and threshold if they are from a good sync
if (m_lostCount == MAX_SYNC_FRAMES) {
m_minSyncPtr = m_syncPtr + M17_FRAME_LENGTH_SAMPLES - 1U;
if (m_minSyncPtr >= M17_FRAME_LENGTH_SAMPLES)
m_minSyncPtr -= M17_FRAME_LENGTH_SAMPLES;
m_maxSyncPtr = m_syncPtr + 1U;
if (m_maxSyncPtr >= M17_FRAME_LENGTH_SAMPLES)
m_maxSyncPtr -= M17_FRAME_LENGTH_SAMPLES;
}
calculateLevels(m_startPtr, M17_FRAME_LENGTH_SYMBOLS);
DEBUG4("M17RX: sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
uint8_t frame[M17_FRAME_LENGTH_BYTES + 3U];
samplesToBits(m_startPtr, M17_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
m_lostCount--;
if (m_lostCount == 0U) {
DEBUG1("M17RX: sync timed out, lost lock");
io.setDecode(false);
io.setADCDetection(false);
serial.writeM17Lost();
m_state = M17RXS_NONE;
m_endPtr = NOENDPTR;
m_averagePtr = NOAVEPTR;
m_countdown = 0U;
m_maxCorr = 0;
} else {
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
writeRSSIData(frame);
m_maxCorr = 0;
}
}
}
bool CM17RX::correlateSync()
{
if (countBits8(m_bitBuffer[m_bitPtr] ^ M17_SYNC_SYMBOLS) <= MAX_SYNC_SYMBOLS_ERRS) {
uint16_t ptr = m_dataPtr + M17_FRAME_LENGTH_SAMPLES - M17_SYNC_LENGTH_SAMPLES + M17_RADIO_SYMBOL_LENGTH;
if (ptr >= M17_FRAME_LENGTH_SAMPLES)
ptr -= M17_FRAME_LENGTH_SAMPLES;
q31_t corr = 0;
q15_t min = 16000;
q15_t max = -16000;
for (uint8_t i = 0U; i < M17_SYNC_LENGTH_SYMBOLS; i++) {
q15_t val = m_buffer[ptr];
if (val > max)
max = val;
if (val < min)
min = val;
switch (M17_SYNC_SYMBOLS_VALUES[i]) {
case +3:
corr -= (val + val + val);
break;
case +1:
corr -= val;
break;
case -1:
corr += val;
break;
default: // -3
corr += (val + val + val);
break;
}
ptr += M17_RADIO_SYMBOL_LENGTH;
if (ptr >= M17_FRAME_LENGTH_SAMPLES)
ptr -= M17_FRAME_LENGTH_SAMPLES;
}
if (corr > m_maxCorr) {
if (m_averagePtr == NOAVEPTR) {
m_centreVal = (max + min) >> 1;
q31_t v1 = (max - m_centreVal) * SCALING_FACTOR;
m_thresholdVal = q15_t(v1 >> 15);
}
uint16_t startPtr = m_dataPtr + M17_FRAME_LENGTH_SAMPLES - M17_SYNC_LENGTH_SAMPLES + M17_RADIO_SYMBOL_LENGTH;
if (startPtr >= M17_FRAME_LENGTH_SAMPLES)
startPtr -= M17_FRAME_LENGTH_SAMPLES;
uint8_t sync[M17_SYNC_BYTES_LENGTH];
samplesToBits(startPtr, M17_SYNC_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal);
uint8_t maxErrs;
if (m_state == M17RXS_NONE)
maxErrs = MAX_SYNC_BIT_START_ERRS;
else
maxErrs = MAX_SYNC_BIT_RUN_ERRS;
uint8_t errs = 0U;
for (uint8_t i = 0U; i < M17_SYNC_BYTES_LENGTH; i++)
errs += countBits8(sync[i] ^ M17_SYNC_BYTES[i]);
if (errs <= maxErrs) {
m_maxCorr = corr;
m_lostCount = MAX_SYNC_FRAMES;
m_syncPtr = m_dataPtr;
m_startPtr = startPtr;
m_endPtr = m_dataPtr + M17_FRAME_LENGTH_SAMPLES - M17_SYNC_LENGTH_SAMPLES - 1U;
if (m_endPtr >= M17_FRAME_LENGTH_SAMPLES)
m_endPtr -= M17_FRAME_LENGTH_SAMPLES;
return true;
}
}
}
return false;
}
void CM17RX::calculateLevels(uint16_t start, uint16_t count)
{
q15_t maxPos = -16000;
q15_t minPos = 16000;
q15_t maxNeg = 16000;
q15_t minNeg = -16000;
for (uint16_t i = 0U; i < count; i++) {
q15_t sample = m_buffer[start];
if (sample > 0) {
if (sample > maxPos)
maxPos = sample;
if (sample < minPos)
minPos = sample;
} else {
if (sample < maxNeg)
maxNeg = sample;
if (sample > minNeg)
minNeg = sample;
}
start += M17_RADIO_SYMBOL_LENGTH;
if (start >= M17_FRAME_LENGTH_SAMPLES)
start -= M17_FRAME_LENGTH_SAMPLES;
}
q15_t posThresh = (maxPos + minPos) >> 1;
q15_t negThresh = (maxNeg + minNeg) >> 1;
q15_t centre = (posThresh + negThresh) >> 1;
q15_t threshold = posThresh - centre;
DEBUG5("M17RX: pos/neg/centre/threshold", posThresh, negThresh, centre, threshold);
if (m_averagePtr == NOAVEPTR) {
for (uint8_t i = 0U; i < 16U; i++) {
m_centre[i] = centre;
m_threshold[i] = threshold;
}
m_averagePtr = 0U;
} else {
m_centre[m_averagePtr] = centre;
m_threshold[m_averagePtr] = threshold;
m_averagePtr++;
if (m_averagePtr >= 16U)
m_averagePtr = 0U;
}
m_centreVal = 0;
m_thresholdVal = 0;
for (uint8_t i = 0U; i < 16U; i++) {
m_centreVal += m_centre[i];
m_thresholdVal += m_threshold[i];
}
m_centreVal >>= 4;
m_thresholdVal >>= 4;
}
void CM17RX::samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold)
{
for (uint16_t i = 0U; i < count; i++) {
q15_t sample = m_buffer[start] - centre;
if (sample < -threshold) {
WRITE_BIT1(buffer, offset, false);
offset++;
WRITE_BIT1(buffer, offset, true);
offset++;
} else if (sample < 0) {
WRITE_BIT1(buffer, offset, false);
offset++;
WRITE_BIT1(buffer, offset, false);
offset++;
} else if (sample < threshold) {
WRITE_BIT1(buffer, offset, true);
offset++;
WRITE_BIT1(buffer, offset, false);
offset++;
} else {
WRITE_BIT1(buffer, offset, true);
offset++;
WRITE_BIT1(buffer, offset, true);
offset++;
}
start += M17_RADIO_SYMBOL_LENGTH;
if (start >= M17_FRAME_LENGTH_SAMPLES)
start -= M17_FRAME_LENGTH_SAMPLES;
}
}
void CM17RX::writeRSSIData(uint8_t* data)
{
#if defined(SEND_RSSI_DATA)
if (m_rssiCount > 0U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
data[121U] = (rssi >> 8) & 0xFFU;
data[122U] = (rssi >> 0) & 0xFFU;
serial.writeM17Data(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
serial.writeM17Data(data, M17_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeM17Data(data, M17_FRAME_LENGTH_BYTES + 1U);
#endif
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(M17RX_H)
#define M17RX_H
#include "Config.h"
#include "M17Defines.h"
enum M17RX_STATE {
M17RXS_NONE,
M17RXS_DATA
};
class CM17RX {
public:
CM17RX();
void samples(const q15_t* samples, uint16_t* rssi, uint8_t length);
void reset();
private:
M17RX_STATE m_state;
uint8_t m_bitBuffer[M17_RADIO_SYMBOL_LENGTH];
q15_t m_buffer[M17_FRAME_LENGTH_SAMPLES];
uint16_t m_bitPtr;
uint16_t m_dataPtr;
uint16_t m_startPtr;
uint16_t m_endPtr;
uint16_t m_syncPtr;
uint16_t m_minSyncPtr;
uint16_t m_maxSyncPtr;
q31_t m_maxCorr;
uint16_t m_lostCount;
uint8_t m_countdown;
q15_t m_centre[16U];
q15_t m_centreVal;
q15_t m_threshold[16U];
q15_t m_thresholdVal;
uint8_t m_averagePtr;
uint32_t m_rssiAccum;
uint16_t m_rssiCount;
void processNone(q15_t sample);
void processData(q15_t sample);
bool correlateSync();
void calculateLevels(uint16_t start, uint16_t count);
void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
void writeRSSIData(uint8_t* data);
};
#endif
+183
View File
@@ -0,0 +1,183 @@
/*
* Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Config.h"
#include "Globals.h"
#include "M17TX.h"
#include "M17Defines.h"
// Generated using rcosdesign(0.5, 8, 5, 'sqrt') in MATLAB
static q15_t RRC_0_5_FILTER[] = {0, 0, 0, 0, -290, -174, 142, 432, 438, 90, -387, -561, -155, 658, 1225, 767,
-980, -3326, -4648, -3062, 2527, 11552, 21705, 29724, 32767, 29724, 21705,
11552, 2527, -3062, -4648, -3326, -980, 767, 1225, 658, -155, -561, -387, 90,
438, 432, 142, -174, -290}; // numTaps = 45, L = 5
const uint16_t RRC_0_5_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
const q15_t M17_LEVELA = 1683;
const q15_t M17_LEVELB = 561;
const q15_t M17_LEVELC = -561;
const q15_t M17_LEVELD = -1683;
const uint8_t M17_START_SYNC = 0x77U;
const uint8_t M17_END_SYNC = 0xFFU;
const uint8_t M17_HANG = 0x00U;
CM17TX::CM17TX() :
m_buffer(TX_BUFFER_LEN),
m_modFilter(),
m_modState(),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U), // 200ms
m_txHang(4800U), // 4s
m_txCount(0U)
{
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
m_modFilter.L = M17_RADIO_SYMBOL_LENGTH;
m_modFilter.phaseLength = RRC_0_5_FILTER_PHASE_LEN;
m_modFilter.pCoeffs = RRC_0_5_FILTER;
m_modFilter.pState = m_modState;
}
void CM17TX::process()
{
// If we have M17 data to transmit, do so.
if (m_poLen == 0U && m_buffer.getData() > 0U) {
if (!m_tx) {
for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = M17_START_SYNC;
} else {
for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++) {
uint8_t c = m_buffer.get();
m_poBuffer[m_poLen++] = c;
}
}
m_poPtr = 0U;
}
if (m_poLen > 0U) {
// Transmit M17 data.
uint16_t space = io.getSpace();
while (space > (4U * M17_RADIO_SYMBOL_LENGTH)) {
uint8_t c = m_poBuffer[m_poPtr++];
writeByte(c);
// Reduce space and reset the hang timer.
space -= 4U * M17_RADIO_SYMBOL_LENGTH;
if (m_duplex)
m_txCount = m_txHang;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
} else if (m_txCount > 0U) {
// Transmit silence until the hang timer has expired.
uint16_t space = io.getSpace();
while (space > (4U * M17_RADIO_SYMBOL_LENGTH)) {
writeSilence();
space -= 4U * M17_RADIO_SYMBOL_LENGTH;
m_txCount--;
if (m_txCount == 0U)
return;
}
}
}
uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
if (length != (M17_FRAME_LENGTH_BYTES + 1U))
return 4U;
uint16_t space = m_buffer.getSpace();
if (space < M17_FRAME_LENGTH_BYTES)
return 5U;
for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
m_buffer.put(data[i + 1U]);
return 0U;
}
void CM17TX::writeByte(uint8_t c)
{
q15_t inBuffer[4U];
q15_t outBuffer[M17_RADIO_SYMBOL_LENGTH * 4U];
const uint8_t MASK = 0xC0U;
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
switch (c & MASK) {
case 0xC0U:
inBuffer[i] = M17_LEVELA;
break;
case 0x80U:
inBuffer[i] = M17_LEVELB;
break;
case 0x00U:
inBuffer[i] = M17_LEVELC;
break;
default:
inBuffer[i] = M17_LEVELD;
break;
}
}
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
io.write(STATE_M17, outBuffer, M17_RADIO_SYMBOL_LENGTH * 4U);
}
void CM17TX::writeSilence()
{
q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U};
q15_t outBuffer[M17_RADIO_SYMBOL_LENGTH * 4U];
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
io.write(STATE_M17, outBuffer, M17_RADIO_SYMBOL_LENGTH * 4U);
}
void CM17TX::setTXDelay(uint8_t delay)
{
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
if (m_txDelay > 1200U)
m_txDelay = 1200U;
}
uint8_t CM17TX::getSpace() const
{
return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}
void CM17TX::setParams(uint8_t txHang)
{
m_txHang = txHang * 1200U;
}
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(M17TX_H)
#define M17TX_H
#include "Config.h"
#include "SerialRB.h"
class CM17TX {
public:
CM17TX();
uint8_t writeData(const uint8_t* data, uint8_t length);
void process();
void setTXDelay(uint8_t delay);
uint8_t getSpace() const;
void setParams(uint8_t txHang);
private:
CSerialRB m_buffer;
arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
uint8_t m_poBuffer[1200U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint16_t m_txDelay;
uint32_t m_txHang;
uint32_t m_txCount;
void writeByte(uint8_t c);
void writeSilence();
};
#endif
+7
View File
@@ -31,6 +31,7 @@ bool m_dmrEnable = true;
bool m_ysfEnable = true;
bool m_p25Enable = true;
bool m_nxdnEnable = true;
bool m_m17Enable = true;
bool m_pocsagEnable = true;
bool m_fmEnable = true;
@@ -58,6 +59,9 @@ CP25TX p25TX;
CNXDNRX nxdnRX;
CNXDNTX nxdnTX;
CM17RX m17RX;
CM17TX m17TX;
CPOCSAGTX pocsagTX;
CFM fm;
@@ -107,6 +111,9 @@ void loop()
if (m_nxdnEnable && m_modemState == STATE_NXDN)
nxdnTX.process();
if (m_m17Enable && m_modemState == STATE_M17)
m17TX.process();
if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy()))
pocsagTX.process();
+7
View File
@@ -28,6 +28,7 @@ bool m_dmrEnable = true;
bool m_ysfEnable = true;
bool m_p25Enable = true;
bool m_nxdnEnable = true;
bool m_m17Enable = true;
bool m_pocsagEnable = true;
bool m_fmEnable = true;
@@ -55,6 +56,9 @@ CP25TX p25TX;
CNXDNRX nxdnRX;
CNXDNTX nxdnTX;
CM17RX m17RX;
CM17TX m17TX;
CPOCSAGTX pocsagTX;
CFM fm;
@@ -104,6 +108,9 @@ void loop()
if (m_nxdnEnable && m_modemState == STATE_NXDN)
nxdnTX.process();
if (m_m17Enable && m_modemState == STATE_M17)
m17TX.process();
if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy()))
pocsagTX.process();
+19 -1
View File
@@ -139,6 +139,10 @@ DEFS_DVM=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_DVM -DHSE_VA
DEFS_RPT_HAT_TGO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_RPT_HAT_TGO -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
# DRCC_DVM BG7NQF board:
DEFS_DRCC_DVM=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DDRCC_DVM -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
# WA0EDA F405 MTR2K, MASTR3 board:
DEFS_EDA_405=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_EDA_405 -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
# WA0EDA F446 MTR2K, MASTR3 board:
DEFS_EDA_446=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_EDA_446 -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
# Build compiler flags
@@ -155,7 +159,7 @@ LDFLAGS_F722 =-T stm32f722_link.ld $(MCFLAGS_F7) --specs=nosys.specs $(INCLUDES_
# Common flags
CFLAGS=-Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
CXXFLAGS=-Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
LDFLAGS=-Os --specs=nano.specs
LDFLAGS=-Os --specs=nano.specs -Wl,-Map=bin/mmdvm.map
# Build Rules
.PHONY: all release dis pi pi-f722 f4m nucleo f767 dvm drcc_nqf clean
@@ -229,6 +233,18 @@ drcc_nqf: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_DRCC_DVM) -DDRCC_DVM_NQF
drcc_nqf: LDFLAGS+=$(LDFLAGS_F4)
drcc_nqf: release_f4
eda405: GitVersion.h
eda405: CFLAGS+=$(CFLAGS_F4) $(DEFS_EDA_405)
eda405: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_EDA_405)
eda405: LDFLAGS+=$(LDFLAGS_F4)
eda405: release_f4
eda446: GitVersion.h
eda446: CFLAGS+=$(CFLAGS_F4) $(DEFS_EDA_446)
eda446: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_EDA_446)
eda446: LDFLAGS+=$(LDFLAGS_F4)
eda446: release_f4
release_f4: $(BINDIR)
release_f4: $(OBJDIR_F4)
release_f4: $(BINDIR)/$(BINHEX_F4)
@@ -350,6 +366,8 @@ endif
deploy-f4m: deploy-pi
deploy-dvm: deploy-pi
deploy-eda405: deploy-pi
deploy-eda446: deploy-pi
deploy-pi-f7:
ifneq ($(wildcard /usr/local/bin/stm32flash),)
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/make
# makefile for the arduino due (works with arduino IDE 1.6.12)
# makefile for the arduino due (works with arduino IDE 1.6.11)
#
# The original file can be found at https://github.com/pauldreik/arduino-due-makefile
#
@@ -34,7 +34,7 @@ OBJCOPY:=$(ADIR)/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/bin/arm-n
C:=$(CC)
#SAM:=arduino/sam/
SAM:=$(ADIR)/packages/arduino/hardware/sam/1.6.12
SAM:=$(ADIR)/packages/arduino/hardware/sam/1.6.11
#CMSIS:=arduino/sam/system/CMSIS/
#LIBSAM:=arduino/sam/system/libsam
TMPDIR:=$(PWD)/build
+1 -2
View File
@@ -81,8 +81,7 @@ void CNXDNTX::process()
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[2U];
} else {
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) {
uint8_t c = 0U;
m_buffer.get(c);
uint8_t c = m_buffer.get();
m_poBuffer[m_poLen++] = c;
}
}
+2 -2
View File
@@ -21,7 +21,7 @@
#include "Config.h"
#include "RingBuffer.h"
#include "SerialRB.h"
class CNXDNTX {
public:
@@ -38,7 +38,7 @@ public:
void setParams(uint8_t txHang);
private:
CRingBuffer<uint8_t> m_buffer;
CSerialRB m_buffer;
arm_fir_interpolate_instance_q15 m_modFilter;
arm_fir_instance_q15 m_sincFilter;
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
+2 -4
View File
@@ -75,11 +75,9 @@ void CP25TX::process()
for (uint16_t i = 0U; i < m_txDelay; i++)
m_poBuffer[m_poLen++] = P25_START_SYNC;
} else {
uint8_t length;
m_buffer.get(length);
uint8_t length = m_buffer.get();
for (uint8_t i = 0U; i < length; i++) {
uint8_t c = 0U;
m_buffer.get(c);
uint8_t c = m_buffer.get();
m_poBuffer[m_poLen++] = c;
}
}
+2 -2
View File
@@ -21,7 +21,7 @@
#include "Config.h"
#include "RingBuffer.h"
#include "SerialRB.h"
class CP25TX {
public:
@@ -38,7 +38,7 @@ public:
void setParams(uint8_t txHang);
private:
CRingBuffer<uint8_t> m_buffer;
CSerialRB m_buffer;
arm_fir_interpolate_instance_q15 m_modFilter;
arm_fir_instance_q15 m_lpFilter;
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
+3 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2018 by Jonathan Naylor G4KLX
*
* 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
@@ -61,8 +61,7 @@ void CPOCSAGTX::process()
m_poBuffer[m_poLen++] = POCSAG_SYNC;
} else {
for (uint8_t i = 0U; i < POCSAG_FRAME_LENGTH_BYTES; i++) {
uint8_t c = 0U;
m_buffer.get(c);
uint8_t c = m_buffer.get();
m_poBuffer[m_poLen++] = c;
}
}
@@ -147,3 +146,4 @@ uint8_t CPOCSAGTX::getSpace() const
{
return m_buffer.getSpace() / POCSAG_FRAME_LENGTH_BYTES;
}
+2 -2
View File
@@ -21,7 +21,7 @@
#include "Config.h"
#include "RingBuffer.h"
#include "SerialRB.h"
class CPOCSAGTX {
public:
@@ -40,7 +40,7 @@ public:
bool busy();
private:
CRingBuffer<uint8_t> m_buffer;
CSerialRB m_buffer;
arm_fir_instance_q15 m_modFilter;
q15_t m_modState[170U]; // NoTaps + BlockSize - 1, 6 + 160 - 1 plus some spare
uint8_t m_poBuffer[200U];
+14 -4
View File
@@ -1,7 +1,17 @@
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion, P25, NXDN, and POCSAG modes.
This is the source code of the MMDVM firmware that supports D-Star, DMR,
System Fusion, P25, NXDN, POCSAG, and FM modes.
It runs on the Arduino Due, the ST-Micro STM32F1xxx, STM32F4xxx and STM32F7xxx processors, as well as the Teensy 3.1/3.2/3.5/3.6. What these platforms have in common is the use of an ARM Cortex-M3 or M4 processor with a clock speed greater than 70 MHz, and access to at least one analogue to digital converter and one digital to analogue converter.
It runs on the Arduino Due, the ST-Micro STM32F1xxx, STM32F4xxx and STM32F7xxx
processors, as well as the Teensy 3.1/3.2/3.5/3.6. What these platforms have in
common is the use of an ARM Cortex-M3, M4, or M7 processors with a clock speed
greater than 70 MHz, and access to at least one analogue to digital converter
and one digital to analogue converter.
In order to build this software for the Arduino Due, you will need to edit a file within the Arduino GUI and that is detailed in the BUILD.txt file. The STM32 support is being supplied via the Coocox IDE with ARM GCC. The Teensy support uses Teensyduino.
In order to build this software for the Arduino Due, you will need to edit a
file within the Arduino GUI and that is detailed in the BUILD.txt file. The
STM32 support is being supplied via the ARM GCC compiler. The Teensy support
uses Teensyduino.
This software is licenced under the GPL v2 and is intended for amateur and educational use only. Use of this software for commercial purposes is strictly forbidden.
This software is licenced under the GPL v2 and is intended for amateur and
educational use only. Use of this software for commercial purposes is strictly
forbidden.
+102
View File
@@ -0,0 +1,102 @@
/*
TX fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "RSSIRB.h"
CRSSIRB::CRSSIRB(uint16_t length) :
m_length(length),
m_head(0U),
m_tail(0U),
m_full(false),
m_overflow(false)
{
m_rssi = new uint16_t[length];
}
uint16_t CRSSIRB::getSpace() const
{
uint16_t n = 0U;
if (m_tail == m_head)
n = m_full ? 0U : m_length;
else if (m_tail < m_head)
n = m_length - m_head + m_tail;
else
n = m_tail - m_head;
if (n > m_length)
n = 0U;
return n;
}
uint16_t CRSSIRB::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
else if (m_tail < m_head)
return m_head - m_tail;
else
return m_length - m_tail + m_head;
}
bool CRSSIRB::put(uint16_t rssi)
{
if (m_full) {
m_overflow = true;
return false;
}
m_rssi[m_head] = rssi;
m_head++;
if (m_head >= m_length)
m_head = 0U;
if (m_head == m_tail)
m_full = true;
return true;
}
bool CRSSIRB::get(uint16_t& rssi)
{
if (m_head == m_tail && !m_full)
return false;
rssi = m_rssi[m_tail];
m_full = false;
m_tail++;
if (m_tail >= m_length)
m_tail = 0U;
return true;
}
bool CRSSIRB::hasOverflowed()
{
bool overflow = m_overflow;
m_overflow = false;
return overflow;
}
+59
View File
@@ -0,0 +1,59 @@
/*
Serial fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#if !defined(RSSIRB_H)
#define RSSIRB_H
#if defined(STM32F4XX)
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#include <cstddef>
#else
#include <Arduino.h>
#endif
class CRSSIRB {
public:
CRSSIRB(uint16_t length);
uint16_t getSpace() const;
uint16_t getData() const;
bool put(uint16_t rssi);
bool get(uint16_t& rssi);
bool hasOverflowed();
private:
uint16_t m_length;
volatile uint16_t* m_rssi;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
bool m_overflow;
};
#endif
-100
View File
@@ -1,100 +0,0 @@
/*
* Copyright (c) 2020 by Jonathan Naylor G4KLX
* Copyright (c) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if defined(STM32F4XX) || defined(STM32F7XX)
#include "STMUART.h"
CSTMUART::CSTMUART() :
m_usart(NULL)
{
}
void CSTMUART::init(USART_TypeDef* usart)
{
m_usart = usart;
}
void CSTMUART::write(const uint8_t * data, uint16_t length)
{
if(length == 0U || m_usart == NULL)
return;
m_txFifo.put(data[0]);
USART_ITConfig(m_usart, USART_IT_TXE, ENABLE);//switch TX IRQ is on
for(uint16_t i = 1U; i < length; i++) {
m_txFifo.put(data[i]);
}
USART_ITConfig(m_usart, USART_IT_TXE, ENABLE);//make sure TX IRQ is on
}
uint8_t CSTMUART::read()
{
return m_rxFifo.get();
}
void CSTMUART::handleIRQ()
{
if(m_usart == NULL)
return;
if (USART_GetITStatus(m_usart, USART_IT_RXNE)) {
if(!m_rxFifo.isFull())
m_rxFifo.put((uint8_t) USART_ReceiveData(m_usart));
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
if (USART_GetITStatus(m_usart, USART_IT_TXE)) {
if(!m_txFifo.isEmpty())
USART_SendData(m_usart, m_txFifo.get());
USART_ClearITPendingBit(m_usart, USART_IT_TXE);
if (m_txFifo.isEmpty()) // if there's no more data to transmit then turn off TX interrupts
USART_ITConfig(m_usart, USART_IT_TXE, DISABLE);
}
}
// Flushes the transmit shift register
// warning: this call is blocking
void CSTMUART::flush()
{
if(m_usart == NULL)
return;
// wait until the TXE shows the shift register is empty
while (USART_GetITStatus(m_usart, USART_FLAG_TXE))
;
}
uint16_t CSTMUART::available()
{
return m_rxFifo.isEmpty() ? 0U : 1U;
}
uint16_t CSTMUART::availableForWrite()
{
return m_txFifo.isFull() ? 0U : 1U;
}
#endif
-91
View File
@@ -1,91 +0,0 @@
/*
* Copyright (c) 2020 by Jonathan Naylor G4KLX
* Copyright (c) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if defined(STM32F4XX) || defined(STM32F7XX)
#if !defined(STMUART_H)
#define STMUART_H
#if defined(STM32F4XX)
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#endif
const uint16_t BUFFER_SIZE = 2048U; //needs to be a power of 2 !
const uint16_t BUFFER_MASK = BUFFER_SIZE - 1;
class CSTMUARTFIFO {
public:
CSTMUARTFIFO() :
m_head(0U),
m_tail(0U)
{
}
void put(uint8_t data)
{
m_buffer[BUFFER_MASK & (m_head++)] = data;
}
uint8_t get()
{
return m_buffer[BUFFER_MASK & (m_tail++)];
}
void reset()
{
m_tail = 0U;
m_head = 0U;
}
bool isEmpty()
{
return m_tail == m_head;
}
bool isFull()
{
return ((m_head + 1U) & BUFFER_MASK) == (m_tail & BUFFER_MASK);
}
private:
volatile uint8_t m_buffer[BUFFER_SIZE];
volatile uint16_t m_head;
volatile uint16_t m_tail;
};
class CSTMUART {
public:
CSTMUART();
void init(USART_TypeDef* usart);
void write(const uint8_t * data, uint16_t length);
uint8_t read();
void handleIRQ();
void flush();
uint16_t available();
uint16_t availableForWrite();
private:
USART_TypeDef * m_usart;
CSTMUARTFIFO m_rxFifo;
CSTMUARTFIFO m_txFifo;
};
#endif
#endif
+106
View File
@@ -0,0 +1,106 @@
/*
TX fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "SampleRB.h"
CSampleRB::CSampleRB(uint16_t length) :
m_length(length),
m_head(0U),
m_tail(0U),
m_full(false),
m_overflow(false)
{
m_samples = new uint16_t[length];
m_control = new uint8_t[length];
}
uint16_t CSampleRB::getSpace() const
{
uint16_t n = 0U;
if (m_tail == m_head)
n = m_full ? 0U : m_length;
else if (m_tail < m_head)
n = m_length - m_head + m_tail;
else
n = m_tail - m_head;
if (n > m_length)
n = 0U;
return n;
}
uint16_t CSampleRB::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
else if (m_tail < m_head)
return m_head - m_tail;
else
return m_length - m_tail + m_head;
}
bool CSampleRB::put(uint16_t sample, uint8_t control)
{
if (m_full) {
m_overflow = true;
return false;
}
m_samples[m_head] = sample;
m_control[m_head] = control;
m_head++;
if (m_head >= m_length)
m_head = 0U;
if (m_head == m_tail)
m_full = true;
return true;
}
bool CSampleRB::get(uint16_t& sample, uint8_t& control)
{
if (m_head == m_tail && !m_full)
return false;
sample = m_samples[m_tail];
control = m_control[m_tail];
m_full = false;
m_tail++;
if (m_tail >= m_length)
m_tail = 0U;
return true;
}
bool CSampleRB::hasOverflowed()
{
bool overflow = m_overflow;
m_overflow = false;
return overflow;
}
+60
View File
@@ -0,0 +1,60 @@
/*
Serial fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#if !defined(SAMPLERB_H)
#define SAMPLERB_H
#if defined(STM32F4XX)
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#include <cstddef>
#else
#include <Arduino.h>
#endif
class CSampleRB {
public:
CSampleRB(uint16_t length);
uint16_t getSpace() const;
uint16_t getData() const;
bool put(uint16_t sample, uint8_t control);
bool get(uint16_t& sample, uint8_t& control);
bool hasOverflowed();
private:
uint16_t m_length;
volatile uint16_t* m_samples;
volatile uint8_t* m_control;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
bool m_overflow;
};
#endif
+65 -96
View File
@@ -63,15 +63,14 @@ const uint8_t MMDVM_P25_LOST = 0x32U;
const uint8_t MMDVM_NXDN_DATA = 0x40U;
const uint8_t MMDVM_NXDN_LOST = 0x41U;
const uint8_t MMDVM_M17_DATA = 0x45U;
const uint8_t MMDVM_M17_LOST = 0x46U;
const uint8_t MMDVM_POCSAG_DATA = 0x50U;
const uint8_t MMDVM_FM_PARAMS1 = 0x60U;
const uint8_t MMDVM_FM_PARAMS2 = 0x61U;
const uint8_t MMDVM_FM_PARAMS3 = 0x62U;
const uint8_t MMDVM_FM_PARAMS4 = 0x63U;
const uint8_t MMDVM_FM_DATA = 0x65U;
const uint8_t MMDVM_FM_STATUS = 0x66U;
const uint8_t MMDVM_FM_EOT = 0x67U;
const uint8_t MMDVM_ACK = 0x70U;
const uint8_t MMDVM_NAK = 0x7FU;
@@ -107,7 +106,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U;
#define HW_TYPE "MMDVM"
#endif
#define DESCRIPTION "20200620 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)"
#define DESCRIPTION "20201015 (D-Star/DMR/System Fusion/P25/NXDN/M17/POCSAG/FM)"
#if defined(GITVERSION)
#define concat(h, a, b, c) h " " a " " b " GitID #" c ""
@@ -180,6 +179,8 @@ void CSerialPort::getStatus()
reply[3U] |= 0x20U;
if (m_fmEnable)
reply[3U] |= 0x40U;
if (m_m17Enable)
reply[3U] |= 0x80U;
reply[4U] = uint8_t(m_modemState);
@@ -244,8 +245,8 @@ void CSerialPort::getStatus()
else
reply[12U] = 0U;
if (m_fmEnable)
reply[13U] = fm.getSpace();
if (m_m17Enable)
reply[13U] = m17TX.getSpace();
else
reply[13U] = 0U;
@@ -273,14 +274,15 @@ void CSerialPort::getVersion()
uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
{
if (length < 21U)
if (length < 23U)
return 4U;
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
bool txInvert = (data[0U] & 0x02U) == 0x02U;
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
bool simplex = (data[0U] & 0x80U) == 0x80U;
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
bool txInvert = (data[0U] & 0x02U) == 0x02U;
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
bool useCOSAsLockout = (data[0U] & 0x20U) == 0x20U;
bool simplex = (data[0U] & 0x80U) == 0x80U;
m_debug = (data[0U] & 0x10U) == 0x10U;
@@ -291,6 +293,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
bool pocsagEnable = (data[1U] & 0x20U) == 0x20U;
bool fmEnable = (data[1U] & 0x40U) == 0x40U;
bool m17Enable = (data[1U] & 0x80U) == 0x80U;
uint8_t txDelay = data[2U];
if (txDelay > 50U)
@@ -298,7 +301,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
MMDVM_STATE modemState = MMDVM_STATE(data[3U]);
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL && modemState != STATE_FMCAL10K && modemState != STATE_FMCAL12K && modemState != STATE_FMCAL15K && modemState != STATE_FMCAL20K && modemState != STATE_FMCAL25K && modemState != STATE_FMCAL30K)
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_M17 && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL && modemState != STATE_FMCAL10K && modemState != STATE_FMCAL12K && modemState != STATE_FMCAL15K && modemState != STATE_FMCAL20K && modemState != STATE_FMCAL25K && modemState != STATE_FMCAL30K)
return 4U;
if (modemState == STATE_DSTAR && !dstarEnable)
return 4U;
@@ -310,6 +313,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
return 4U;
if (modemState == STATE_NXDN && !nxdnEnable)
return 4U;
if (modemState == STATE_M17 && !m17Enable)
return 4U;
if (modemState == STATE_POCSAG && !pocsagEnable)
return 4U;
if (modemState == STATE_FM && !fmEnable)
@@ -344,6 +349,9 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
uint8_t nxdnTXHang = data[20U];
uint8_t m17TXLevel = data[21U];
uint8_t m17TXHang = data[22U];
setMode(modemState);
m_dstarEnable = dstarEnable;
@@ -351,6 +359,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
m_ysfEnable = ysfEnable;
m_p25Enable = p25Enable;
m_nxdnEnable = nxdnEnable;
m_m17Enable = m17Enable;
m_pocsagEnable = pocsagEnable;
m_fmEnable = fmEnable;
m_duplex = !simplex;
@@ -360,6 +369,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
p25TX.setTXDelay(txDelay);
dmrDMOTX.setTXDelay(txDelay);
nxdnTX.setTXDelay(txDelay);
m17TX.setTXDelay(txDelay);
pocsagTX.setTXDelay(txDelay);
dmrTX.setColorCode(colorCode);
@@ -371,8 +381,9 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
ysfTX.setParams(ysfLoDev, ysfTXHang);
p25TX.setParams(p25TXHang);
nxdnTX.setParams(nxdnTXHang);
m17TX.setParams(m17TXHang);
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset);
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, m17TXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset, useCOSAsLockout);
io.start();
@@ -440,34 +451,14 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length)
uint8_t kerchunkTime = data[6U];
uint8_t hangTime = data[7U];
bool useCOS = (data[8U] & 0x01U) == 0x01U;
bool cosInvert = (data[8U] & 0x02U) == 0x02U;
bool kerchunkTX = (data[8U] & 0x04U) == 0x04U;
uint8_t accessMode = data[8U] & 0x7FU;
bool cosInvert = (data[8U] & 0x80U) == 0x80U;
uint8_t rfAudioBoost = data[9U];
uint8_t maxDev = data[10U];
uint8_t rxLevel = data[11U];
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, kerchunkTX, hangTime, useCOS, cosInvert, rfAudioBoost, maxDev, rxLevel);
}
uint8_t CSerialPort::setFMParams4(const uint8_t* data, uint8_t length)
{
if (length < 4U)
return 4U;
uint8_t audioBoost = data[0U];
uint8_t speed = data[1U];
uint16_t frequency = data[2U] * 10U;
uint8_t level = data[3U];
char ack[50U];
uint8_t n = 0U;
for (uint8_t i = 4U; i < length; i++, n++)
ack[n] = data[i];
ack[n] = '\0';
return fm.setExt(ack, audioBoost, speed, frequency, level);
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, cosInvert, rfAudioBoost, maxDev, rxLevel);
}
uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
@@ -480,7 +471,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
if (modemState == m_modemState)
return 0U;
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL && modemState != STATE_FMCAL10K && modemState != STATE_FMCAL12K && modemState != STATE_FMCAL15K && modemState != STATE_FMCAL20K && modemState != STATE_FMCAL25K && modemState != STATE_FMCAL30K)
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_M17 && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL && modemState != STATE_FMCAL10K && modemState != STATE_FMCAL12K && modemState != STATE_FMCAL15K && modemState != STATE_FMCAL20K && modemState != STATE_FMCAL25K && modemState != STATE_FMCAL30K)
return 4U;
if (modemState == STATE_DSTAR && !m_dstarEnable)
return 4U;
@@ -492,6 +483,8 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
return 4U;
if (modemState == STATE_NXDN && !m_nxdnEnable)
return 4U;
if (modemState == STATE_M17 && !m_m17Enable)
return 4U;
if (modemState == STATE_POCSAG && !m_pocsagEnable)
return 4U;
if (modemState == STATE_FM && !m_fmEnable)
@@ -520,6 +513,9 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
case STATE_NXDN:
DEBUG1("Mode set to NXDN");
break;
case STATE_M17:
DEBUG1("Mode set to M17");
break;
case STATE_POCSAG:
DEBUG1("Mode set to POCSAG");
break;
@@ -591,19 +587,20 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
if (modemState != STATE_NXDN)
nxdnRX.reset();
if (modemState != STATE_M17)
m17RX.reset();
if (modemState != STATE_FM)
fm.reset();
cwIdTX.reset();
m_modemState = modemState;
io.setMode();
io.setMode(modemState);
}
void CSerialPort::start()
{
beginInt(1U, SERIAL_SPEED);
beginInt(1U, 115200);
#if defined(SERIAL_REPEATER)
beginInt(3U, 9600);
@@ -698,16 +695,6 @@ void CSerialPort::process()
}
break;
case MMDVM_FM_PARAMS4:
err = setFMParams4(m_buffer + 3U, m_len - 3U);
if (err == 0U) {
sendACK();
} else {
DEBUG2("Received invalid FM params 4", err);
sendNAK(err);
}
break;
case MMDVM_CAL_DATA:
if (m_modemState == STATE_DSTARCAL)
err = calDStarTX.write(m_buffer + 3U, m_len - 3U);
@@ -910,6 +897,20 @@ void CSerialPort::process()
}
break;
case MMDVM_M17_DATA:
if (m_m17Enable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_M17)
err = m17TX.writeData(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 data", err);
sendNAK(err);
}
break;
case MMDVM_POCSAG_DATA:
if (m_pocsagEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_POCSAG)
@@ -924,20 +925,6 @@ void CSerialPort::process()
}
break;
case MMDVM_FM_DATA:
if (m_fmEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_FM)
err = fm.writeData(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_FM);
} else {
DEBUG2("Received invalid FM data", err);
sendNAK(err);
}
break;
case MMDVM_TRANSPARENT:
case MMDVM_QSO_INFO:
// Do nothing on the MMDVM.
@@ -1250,19 +1237,19 @@ void CSerialPort::writeNXDNLost()
writeInt(1U, reply, 3);
}
void CSerialPort::writeFMData(const uint8_t* data, uint8_t length)
void CSerialPort::writeM17Data(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_FM && m_modemState != STATE_IDLE)
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
if (!m_fmEnable)
if (!m_m17Enable)
return;
uint8_t reply[255U];
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_FM_DATA;
reply[2U] = MMDVM_M17_DATA;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
@@ -1273,39 +1260,21 @@ void CSerialPort::writeFMData(const uint8_t* data, uint8_t length)
writeInt(1U, reply, count);
}
void CSerialPort::writeFMStatus(uint8_t status)
void CSerialPort::writeM17Lost()
{
if (m_modemState != STATE_FM && m_modemState != STATE_IDLE)
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
if (!m_fmEnable)
if (!m_m17Enable)
return;
uint8_t reply[10U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 4U;
reply[2U] = MMDVM_FM_STATUS;
reply[3U] = status;
writeInt(1U, reply, 4U);
}
void CSerialPort::writeFMEOT()
{
if (m_modemState != STATE_FM && m_modemState != STATE_IDLE)
return;
if (!m_fmEnable)
return;
uint8_t reply[10U];
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = MMDVM_FM_EOT;
reply[2U] = MMDVM_M17_LOST;
writeInt(1U, reply, 3U);
writeInt(1U, reply, 3);
}
void CSerialPort::writeCalData(const uint8_t* data, uint8_t length)
+4 -10
View File
@@ -21,11 +21,7 @@
#include "Config.h"
#include "Globals.h"
#include "RingBuffer.h"
#if !defined(SERIAL_SPEED)
#define SERIAL_SPEED 115200
#endif
#include "SerialRB.h"
class CSerialPort {
@@ -54,9 +50,8 @@ public:
void writeNXDNData(const uint8_t* data, uint8_t length);
void writeNXDNLost();
void writeFMData(const uint8_t* data, uint8_t length);
void writeFMStatus(uint8_t status);
void writeFMEOT();
void writeM17Data(const uint8_t* data, uint8_t length);
void writeM17Lost();
void writeCalData(const uint8_t* data, uint8_t length);
void writeRSSIData(const uint8_t* data, uint8_t length);
@@ -72,7 +67,7 @@ private:
uint8_t m_ptr;
uint8_t m_len;
bool m_debug;
CRingBuffer<uint8_t> m_repeat;
CSerialRB m_repeat;
void sendACK();
void sendNAK(uint8_t err);
@@ -84,7 +79,6 @@ private:
uint8_t setFMParams1(const uint8_t* data, uint8_t length);
uint8_t setFMParams2(const uint8_t* data, uint8_t length);
uint8_t setFMParams3(const uint8_t* data, uint8_t length);
uint8_t setFMParams4(const uint8_t* data, uint8_t length);
// Hardware versions
void beginInt(uint8_t n, int speed);
+100
View File
@@ -0,0 +1,100 @@
/*
Serial RB control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "SerialRB.h"
CSerialRB::CSerialRB(uint16_t length) :
m_length(length),
m_head(0U),
m_tail(0U),
m_full(false)
{
m_buffer = new uint8_t[length];
}
void CSerialRB::reset()
{
m_head = 0U;
m_tail = 0U;
m_full = false;
}
uint16_t CSerialRB::getSpace() const
{
uint16_t n = 0U;
if (m_tail == m_head)
n = m_full ? 0U : m_length;
else if (m_tail < m_head)
n = m_length - m_head + m_tail;
else
n = m_tail - m_head;
if (n > m_length)
n = 0U;
return n;
}
uint16_t CSerialRB::getData() const
{
if (m_tail == m_head)
return m_full ? m_length : 0U;
else if (m_tail < m_head)
return m_head - m_tail;
else
return m_length - m_tail + m_head;
}
bool CSerialRB::put(uint8_t c)
{
if (m_full)
return false;
m_buffer[m_head] = c;
m_head++;
if (m_head >= m_length)
m_head = 0U;
if (m_head == m_tail)
m_full = true;
return true;
}
uint8_t CSerialRB::peek() const
{
return m_buffer[m_tail];
}
uint8_t CSerialRB::get()
{
uint8_t value = m_buffer[m_tail];
m_full = false;
m_tail++;
if (m_tail >= m_length)
m_tail = 0U;
return value;
}
+62
View File
@@ -0,0 +1,62 @@
/*
Serial fifo control - Copyright (C) KI6ZUM 2015
Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#if !defined(SERIALRB_H)
#define SERIALRB_H
#if defined(STM32F4XX)
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#include <cstddef>
#else
#include <Arduino.h>
#endif
const uint16_t SERIAL_RINGBUFFER_SIZE = 370U;
class CSerialRB {
public:
CSerialRB(uint16_t length = SERIAL_RINGBUFFER_SIZE);
uint16_t getSpace() const;
uint16_t getData() const;
void reset();
bool put(uint8_t c);
uint8_t peek() const;
uint8_t get();
private:
uint16_t m_length;
volatile uint8_t* m_buffer;
volatile uint16_t m_head;
volatile uint16_t m_tail;
volatile bool m_full;
};
#endif
+590 -54
View File
@@ -39,7 +39,9 @@ UART5 - TXD PC12 - RXD PD2 (Discovery, MMDVM-Pi, MMDVM-Pi F722 board, MMDVM-F4M
#if defined(STM32F4XX) || defined(STM32F7XX)
#include "STMUART.h"
#define TX_SERIAL_FIFO_SIZE 512U
#define RX_SERIAL_FIFO_SIZE 512U
extern "C" {
void USART1_IRQHandler();
void USART2_IRQHandler();
@@ -48,14 +50,113 @@ extern "C" {
}
/* ************* USART1 ***************** */
#if defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_F7M) || defined(STM32F722_PI) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || (defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)) || defined(DRCC_DVM)
#if defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_F7M) || defined(STM32F722_PI) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || (defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)) || defined(DRCC_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
volatile uint8_t TXSerialfifo1[TX_SERIAL_FIFO_SIZE];
volatile uint8_t RXSerialfifo1[RX_SERIAL_FIFO_SIZE];
volatile uint16_t TXSerialfifohead1, TXSerialfifotail1;
volatile uint16_t RXSerialfifohead1, RXSerialfifotail1;
static CSTMUART m_USART1;
// Init queues
void TXSerialfifoinit1()
{
TXSerialfifohead1 = 0U;
TXSerialfifotail1 = 0U;
}
void RXSerialfifoinit1()
{
RXSerialfifohead1 = 0U;
RXSerialfifotail1 = 0U;
}
// How full is queue
// TODO decide if how full or how empty is preferred info to return
uint16_t TXSerialfifolevel1()
{
uint32_t tail = TXSerialfifotail1;
uint32_t head = TXSerialfifohead1;
if (tail > head)
return TX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
uint16_t RXSerialfifolevel1()
{
uint32_t tail = RXSerialfifotail1;
uint32_t head = RXSerialfifohead1;
if (tail > head)
return RX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
// Flushes the transmit shift register
// warning: this call is blocking
void TXSerialFlush1()
{
// wait until the TXE shows the shift register is empty
while (USART_GetITStatus(USART1, USART_FLAG_TXE))
;
}
uint8_t TXSerialfifoput1(uint8_t next)
{
if (TXSerialfifolevel1() < TX_SERIAL_FIFO_SIZE) {
TXSerialfifo1[TXSerialfifohead1] = next;
TXSerialfifohead1++;
if (TXSerialfifohead1 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifohead1 = 0U;
// make sure transmit interrupts are enabled as long as there is data to send
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
return 1U;
} else {
return 0U; // signal an overflow occurred by returning a zero count
}
}
void USART1_IRQHandler()
{
m_USART1.handleIRQ();
uint8_t c;
if (USART_GetITStatus(USART1, USART_IT_RXNE)) {
c = (uint8_t) USART_ReceiveData(USART1);
if (RXSerialfifolevel1() < RX_SERIAL_FIFO_SIZE) {
RXSerialfifo1[RXSerialfifohead1] = c;
RXSerialfifohead1++;
if (RXSerialfifohead1 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifohead1 = 0U;
} else {
// TODO - do something if rx fifo is full?
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
if (USART_GetITStatus(USART1, USART_IT_TXE)) {
c = 0U;
if (TXSerialfifohead1 != TXSerialfifotail1) { // if the fifo is not empty
c = TXSerialfifo1[TXSerialfifotail1];
TXSerialfifotail1++;
if (TXSerialfifotail1 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifotail1 = 0U;
USART_SendData(USART1, c);
} else { // if there's no more data to transmit then turn off TX interrupts
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
USART_ClearITPendingBit(USART1, USART_IT_TXE);
}
}
void InitUSART1(int speed)
@@ -100,7 +201,41 @@ void InitUSART1(int speed)
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
m_USART1.init(USART1);
// initialize the fifos
TXSerialfifoinit1();
RXSerialfifoinit1();
}
uint8_t AvailUSART1()
{
if (RXSerialfifolevel1() > 0U)
return 1U;
else
return 0U;
}
int AvailForWriteUSART1()
{
return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel1();
}
uint8_t ReadUSART1()
{
uint8_t data_c = RXSerialfifo1[RXSerialfifotail1];
RXSerialfifotail1++;
if (RXSerialfifotail1 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifotail1 = 0U;
return data_c;
}
void WriteUSART1(const uint8_t* data, uint16_t length)
{
for (uint16_t i = 0U; i < length; i++)
TXSerialfifoput1(data[i]);
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
}
#endif
@@ -108,12 +243,111 @@ void InitUSART1(int speed)
/* ************* USART2 ***************** */
#if defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO) || defined(DRCC_DVM)
static CSTMUART m_USART2;
volatile uint8_t TXSerialfifo2[TX_SERIAL_FIFO_SIZE];
volatile uint8_t RXSerialfifo2[RX_SERIAL_FIFO_SIZE];
volatile uint16_t TXSerialfifohead2, TXSerialfifotail2;
volatile uint16_t RXSerialfifohead2, RXSerialfifotail2;
// Init queues
void TXSerialfifoinit2()
{
TXSerialfifohead2 = 0U;
TXSerialfifotail2 = 0U;
}
void RXSerialfifoinit2()
{
RXSerialfifohead2 = 0U;
RXSerialfifotail2 = 0U;
}
// How full is queue
// TODO decide if how full or how empty is preferred info to return
uint16_t TXSerialfifolevel2()
{
uint32_t tail = TXSerialfifotail2;
uint32_t head = TXSerialfifohead2;
if (tail > head)
return TX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
uint16_t RXSerialfifolevel2()
{
uint32_t tail = RXSerialfifotail2;
uint32_t head = RXSerialfifohead2;
if (tail > head)
return RX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
// Flushes the transmit shift register
// warning: this call is blocking
void TXSerialFlush2()
{
// wait until the TXE shows the shift register is empty
while (USART_GetITStatus(USART2, USART_FLAG_TXE))
;
}
uint8_t TXSerialfifoput2(uint8_t next)
{
if (TXSerialfifolevel2() < TX_SERIAL_FIFO_SIZE) {
TXSerialfifo2[TXSerialfifohead2] = next;
TXSerialfifohead2++;
if (TXSerialfifohead2 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifohead2 = 0U;
// make sure transmit interrupts are enabled as long as there is data to send
USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
return 1U;
} else {
return 0U; // signal an overflow occurred by returning a zero count
}
}
void USART2_IRQHandler()
{
m_USART2.handleIRQ();
uint8_t c;
if (USART_GetITStatus(USART2, USART_IT_RXNE)) {
c = (uint8_t) USART_ReceiveData(USART2);
if (RXSerialfifolevel2() < RX_SERIAL_FIFO_SIZE) {
RXSerialfifo2[RXSerialfifohead2] = c;
RXSerialfifohead2++;
if (RXSerialfifohead2 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifohead2 = 0U;
} else {
// TODO - do something if rx fifo is full?
}
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
if (USART_GetITStatus(USART2, USART_IT_TXE)) {
c = 0U;
if (TXSerialfifohead2 != TXSerialfifotail2) { // if the fifo is not empty
c = TXSerialfifo2[TXSerialfifotail2];
TXSerialfifotail2++;
if (TXSerialfifotail2 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifotail2 = 0U;
USART_SendData(USART2, c);
} else { // if there's no more data to transmit then turn off TX interrupts
USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
}
USART_ClearITPendingBit(USART2, USART_IT_TXE);
}
}
void InitUSART2(int speed)
@@ -158,7 +392,41 @@ void InitUSART2(int speed)
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
m_USART2.init(USART2);
// initialize the fifos
TXSerialfifoinit2();
RXSerialfifoinit2();
}
uint8_t AvailUSART2()
{
if (RXSerialfifolevel2() > 0U)
return 1U;
else
return 0U;
}
int AvailForWriteUSART2()
{
return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel2();
}
uint8_t ReadUSART2()
{
uint8_t data_c = RXSerialfifo2[RXSerialfifotail2];
RXSerialfifotail2++;
if (RXSerialfifotail2 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifotail2 = 0U;
return data_c;
}
void WriteUSART2(const uint8_t* data, uint16_t length)
{
for (uint16_t i = 0U; i < length; i++)
TXSerialfifoput2(data[i]);
USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
}
#endif
@@ -166,11 +434,111 @@ void InitUSART2(int speed)
/* ************* USART3 ***************** */
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
static CSTMUART m_USART3;
volatile uint8_t TXSerialfifo3[TX_SERIAL_FIFO_SIZE];
volatile uint8_t RXSerialfifo3[RX_SERIAL_FIFO_SIZE];
volatile uint16_t TXSerialfifohead3, TXSerialfifotail3;
volatile uint16_t RXSerialfifohead3, RXSerialfifotail3;
// Init queues
void TXSerialfifoinit3()
{
TXSerialfifohead3 = 0U;
TXSerialfifotail3 = 0U;
}
void RXSerialfifoinit3()
{
RXSerialfifohead3 = 0U;
RXSerialfifotail3 = 0U;
}
// How full is queue
// TODO decide if how full or how empty is preferred info to return
uint16_t TXSerialfifolevel3()
{
uint32_t tail = TXSerialfifotail3;
uint32_t head = TXSerialfifohead3;
if (tail > head)
return TX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
uint16_t RXSerialfifolevel3()
{
uint32_t tail = RXSerialfifotail3;
uint32_t head = RXSerialfifohead3;
if (tail > head)
return RX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
// Flushes the transmit shift register
// warning: this call is blocking
void TXSerialFlush3()
{
// wait until the TXE shows the shift register is empty
while (USART_GetITStatus(USART3, USART_FLAG_TXE))
;
}
uint8_t TXSerialfifoput3(uint8_t next)
{
if (TXSerialfifolevel3() < TX_SERIAL_FIFO_SIZE) {
TXSerialfifo3[TXSerialfifohead3] = next;
TXSerialfifohead3++;
if (TXSerialfifohead3 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifohead3 = 0U;
// make sure transmit interrupts are enabled as long as there is data to send
USART_ITConfig(USART3, USART_IT_TXE, ENABLE);
return 1U;
} else {
return 0U; // signal an overflow occurred by returning a zero count
}
}
void USART3_IRQHandler()
{
m_USART3.handleIRQ();
uint8_t c;
if (USART_GetITStatus(USART3, USART_IT_RXNE)) {
c = (uint8_t) USART_ReceiveData(USART3);
if (RXSerialfifolevel3() < RX_SERIAL_FIFO_SIZE) {
RXSerialfifo3[RXSerialfifohead3] = c;
RXSerialfifohead3++;
if (RXSerialfifohead3 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifohead3 = 0U;
} else {
// TODO - do something if rx fifo is full?
}
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
if (USART_GetITStatus(USART3, USART_IT_TXE)) {
c = 0U;
if (TXSerialfifohead3 != TXSerialfifotail3) { // if the fifo is not empty
c = TXSerialfifo3[TXSerialfifotail3];
TXSerialfifotail3++;
if (TXSerialfifotail3 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifotail3 = 0U;
USART_SendData(USART3, c);
} else { // if there's no more data to transmit then turn off TX interrupts
USART_ITConfig(USART3, USART_IT_TXE, DISABLE);
}
USART_ClearITPendingBit(USART3, USART_IT_TXE);
}
}
#if defined(STM32F7_NUCLEO)
@@ -232,7 +600,41 @@ void InitUSART3(int speed)
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
m_USART3.init(USART3);
// initialize the fifos
TXSerialfifoinit3();
RXSerialfifoinit3();
}
uint8_t AvailUSART3()
{
if (RXSerialfifolevel3() > 0U)
return 1U;
else
return 0U;
}
int AvailForWriteUSART3()
{
return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel3();
}
uint8_t ReadUSART3()
{
uint8_t data_c = RXSerialfifo3[RXSerialfifotail3];
RXSerialfifotail3++;
if (RXSerialfifotail3 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifotail3 = 0U;
return data_c;
}
void WriteUSART3(const uint8_t* data, uint16_t length)
{
for (uint16_t i = 0U; i < length; i++)
TXSerialfifoput3(data[i]);
USART_ITConfig(USART3, USART_IT_TXE, ENABLE);
}
#endif
@@ -240,11 +642,111 @@ void InitUSART3(int speed)
/* ************* UART5 ***************** */
#if !(defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER))
static CSTMUART m_UART5;
volatile uint8_t TXSerialfifo5[TX_SERIAL_FIFO_SIZE];
volatile uint8_t RXSerialfifo5[RX_SERIAL_FIFO_SIZE];
volatile uint16_t TXSerialfifohead5, TXSerialfifotail5;
volatile uint16_t RXSerialfifohead5, RXSerialfifotail5;
// Init queues
void TXSerialfifoinit5()
{
TXSerialfifohead5 = 0U;
TXSerialfifotail5 = 0U;
}
void RXSerialfifoinit5()
{
RXSerialfifohead5 = 0U;
RXSerialfifotail5 = 0U;
}
// How full is queue
// TODO decide if how full or how empty is preferred info to return
uint16_t TXSerialfifolevel5()
{
uint32_t tail = TXSerialfifotail5;
uint32_t head = TXSerialfifohead5;
if (tail > head)
return TX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
uint16_t RXSerialfifolevel5()
{
uint32_t tail = RXSerialfifotail5;
uint32_t head = RXSerialfifohead5;
if (tail > head)
return RX_SERIAL_FIFO_SIZE + head - tail;
else
return head - tail;
}
// Flushes the transmit shift register
// warning: this call is blocking
void TXSerialFlush5()
{
// wait until the TXE shows the shift register is empty
while (USART_GetITStatus(UART5, USART_FLAG_TXE))
;
}
uint8_t TXSerialfifoput5(uint8_t next)
{
if (TXSerialfifolevel5() < TX_SERIAL_FIFO_SIZE) {
TXSerialfifo5[TXSerialfifohead5] = next;
TXSerialfifohead5++;
if (TXSerialfifohead5 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifohead5 = 0U;
// make sure transmit interrupts are enabled as long as there is data to send
USART_ITConfig(UART5, USART_IT_TXE, ENABLE);
return 1U;
} else {
return 0U; // signal an overflow occurred by returning a zero count
}
}
void UART5_IRQHandler()
{
m_UART5.handleIRQ();
uint8_t c;
if (USART_GetITStatus(UART5, USART_IT_RXNE)) {
c = (uint8_t) USART_ReceiveData(UART5);
if (RXSerialfifolevel5() < RX_SERIAL_FIFO_SIZE) {
RXSerialfifo5[RXSerialfifohead5] = c;
RXSerialfifohead5++;
if (RXSerialfifohead5 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifohead5 = 0U;
} else {
// TODO - do something if rx fifo is full?
}
USART_ClearITPendingBit(UART5, USART_IT_RXNE);
}
if (USART_GetITStatus(UART5, USART_IT_TXE)) {
c = 0U;
if (TXSerialfifohead5 != TXSerialfifotail5) { // if the fifo is not empty
c = TXSerialfifo5[TXSerialfifotail5];
TXSerialfifotail5++;
if (TXSerialfifotail5 >= TX_SERIAL_FIFO_SIZE)
TXSerialfifotail5 = 0U;
USART_SendData(UART5, c);
} else { // if there's no more data to transmit then turn off TX interrupts
USART_ITConfig(UART5, USART_IT_TXE, DISABLE);
}
USART_ClearITPendingBit(UART5, USART_IT_TXE);
}
}
void InitUART5(int speed)
@@ -293,7 +795,41 @@ void InitUART5(int speed)
USART_ITConfig(UART5, USART_IT_RXNE, ENABLE);
m_UART5.init(UART5);
// initialize the fifos
TXSerialfifoinit5();
RXSerialfifoinit5();
}
uint8_t AvailUART5()
{
if (RXSerialfifolevel5() > 0U)
return 1U;
else
return 0U;
}
int AvailForWriteUART5()
{
return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel5();
}
uint8_t ReadUART5()
{
uint8_t data_c = RXSerialfifo5[RXSerialfifotail5];
RXSerialfifotail5++;
if (RXSerialfifotail5 >= RX_SERIAL_FIFO_SIZE)
RXSerialfifotail5 = 0U;
return data_c;
}
void WriteUART5(const uint8_t* data, uint16_t length)
{
for (uint16_t i = 0U; i < length; i++)
TXSerialfifoput5(data[i]);
USART_ITConfig(UART5, USART_IT_TXE, ENABLE);
}
#endif
@@ -305,7 +841,7 @@ void CSerialPort::beginInt(uint8_t n, int speed)
case 1U:
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
InitUSART3(speed);
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM)
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
InitUSART1(speed);
#elif defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO)
InitUSART2(speed);
@@ -332,21 +868,21 @@ int CSerialPort::availableInt(uint8_t n)
switch (n) {
case 1U:
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
return m_USART3.availble();//AvailUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM)
return m_USART1.available();//AvailUSART1();
return AvailUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
return AvailUSART1();
#elif defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO)
return m_USART2.available();//AvailUSART2();
return AvailUSART2();
#elif defined(DRCC_DVM)
return m_USART1.available();//AvailUSART1();
return AvailUSART1();
#endif
case 3U:
#if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)
return m_USART1.available(); //AvailUSART1();
return AvailUSART1();
#elif defined(DRCC_DVM)
return m_USART2.available(); //AvailUSART2();
return AvailUSART2();
#else
return m_UART5.available();//AvailUART5();
return AvailUART5();
#endif
default:
return 0;
@@ -358,21 +894,21 @@ int CSerialPort::availableForWriteInt(uint8_t n)
switch (n) {
case 1U:
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
return m_USART3.availableForWrite(); //AvailForWriteUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM)
return m_USART1.availableForWrite(); //AvailForWriteUSART1();
return AvailForWriteUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
return AvailForWriteUSART1();
#elif defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO)
return m_USART2.availableForWrite();//AvailForWriteUSART2();
return AvailForWriteUSART2();
#elif defined(DRCC_DVM)
return m_USART1.availableForWrite();//AvailForWriteUSART1();
return AvailForWriteUSART1();
#endif
case 3U:
#if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)
return m_USART1.availableForWrite(); //AvailForWriteUSART1();
return AvailForWriteUSART1();
#elif defined(DRCC_DVM)
return m_USART2.availableForWrite();//AvailForWriteUSART2();
return AvailForWriteUSART2();
#else
return m_UART5.availableForWrite();//AvailForWriteUART5();
return AvailForWriteUART5();
#endif
default:
return 0;
@@ -384,21 +920,21 @@ uint8_t CSerialPort::readInt(uint8_t n)
switch (n) {
case 1U:
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
return m_USART3.read();//ReadUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM)
return m_USART1.read();//ReadUSART1();
return ReadUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
return ReadUSART1();
#elif defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO)
return m_USART2.read();//ReadUSART2();
return ReadUSART2();
#elif defined(DRCC_DVM)
return m_USART1.read();//ReadUSART1();
return ReadUSART1();
#endif
case 3U:
#if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)
return m_USART1.read();//ReadUSART1();
return ReadUSART1();
#elif defined(DRCC_DVM)
return m_USART2.read();//ReadUSART2();
return ReadUSART2();
#else
return m_UART5.read();//ReadUART5();
return ReadUART5();
#endif
default:
return 0U;
@@ -410,36 +946,36 @@ void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool
switch (n) {
case 1U:
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
m_USART3.write(data, length); //WriteUSART3(data, length);
WriteUSART3(data, length);
if (flush)
m_USART3.flush();//TXSerialFlush3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM)
m_USART1.write(data, length);//WriteUSART1(data, length);
TXSerialFlush3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
WriteUSART1(data, length);
if (flush)
m_USART1.flush();//TXSerialFlush1();
TXSerialFlush1();
#elif defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO)
m_USART2.write(data, length);//WriteUSART2(data, length);
WriteUSART2(data, length);
if (flush)
m_USART2.flush();//TXSerialFlush2();
TXSerialFlush2();
#elif defined(DRCC_DVM)
m_USART1.write(data, length);//WriteUSART1(data, length);
WriteUSART1(data, length);
if (flush)
m_USART1.flush();//TXSerialFlush1();
TXSerialFlush1();
#endif
break;
case 3U:
#if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)
m_USART1.write(data, length); //WriteUSART1(data, length);
WriteUSART1(data, length);
if (flush)
m_USART1.flush();
TXSerialFlush1();
#elif defined(DRCC_DVM)
m_USART2.write(data, length);//WriteUSART2(data, length);
WriteUSART2(data, length);
if (flush)
m_USART2.flush();//TXSerialFlush2();
TXSerialFlush2();
#else
m_UART5.write(data, length);//WriteUART5(data, length);
WriteUART5(data, length);
if (flush)
m_UART5.flush();//TXSerialFlush5();
TXSerialFlush5();
#endif
break;
default:
+8 -8
View File
@@ -17,30 +17,30 @@ f2 = 2700
rp = 0.2
# scaling factor in bits, do not change !
q = 15
q = 0
# scaling factor as facor...
scaling_factor = 2**q
# let's generate a sequence of 2nd order IIR filters
sos = signal.cheby1(3,rp,[f1, f2],'bandpass', output='sos', fs=fs)
#os = signal.cheby1(4, rp, f2, 'lowpass', output='sos', fs=fs) #deemphasis filter
#sos = signal.cheby1(1, rp, 2122, 'lowpass', output='sos', fs=fs) #deemphasis filter
#sos = signal.cheby1(1, rp, 2122, 'highpass', output='sos', fs=fs) #deemphasis filter
sosrounded = np.round((sos) * scaling_factor)
#sos = np.round((sos) * scaling_factor)
# print coefficients
for biquad in sosrounded:
for biquad in sos:
for coeff in biquad:
print(int(coeff),",",sep="",end="")
#print((coeff),",",sep="",end="")
#print(int(coeff),",",sep="",end="")
print((coeff),",",sep="",end="")
print("")
# plot the frequency response
b,a = signal.sos2tf(sos)
w,h = signal.freqz(b,a, worN=2048)
w,h = signal.freqz(b,a)
pl.plot(w/np.pi/2*fs,20*np.log(np.abs(h)))
pl.xlabel('frequency/Hz');
pl.ylabel('gain/dB');
pl.ylim(top=1,bottom=-30);
pl.ylim(top=1,bottom=-20);
pl.xlim(left=250, right=12000);
pl.show()
+9
View File
@@ -30,6 +30,15 @@ uint8_t countBits8(uint8_t bits)
return BITS_TABLE[bits];
}
uint8_t countBits16(uint16_t bits)
{
uint8_t* p = (uint8_t*)&bits;
uint8_t n = 0U;
n += BITS_TABLE[p[0U]];
n += BITS_TABLE[p[1U]];
return n;
}
uint8_t countBits32(uint32_t bits)
{
uint8_t* p = (uint8_t*)&bits;
+3 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
*
* 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
@@ -32,6 +32,8 @@
uint8_t countBits8(uint8_t bits);
uint8_t countBits16(uint16_t bits);
uint8_t countBits32(uint32_t bits);
uint8_t countBits64(uint64_t bits);
+1 -1
View File
@@ -36,7 +36,7 @@ const uint8_t NOAVEPTR = 99U;
const uint16_t NOENDPTR = 9999U;
const unsigned int MAX_SYNC_FRAMES = 4U + 1U;
const unsigned int MAX_SYNC_FRAMES = 1U + 1U;
CYSFRX::CYSFRX() :
m_state(YSFRXS_NONE),
+1 -2
View File
@@ -73,8 +73,7 @@ void CYSFTX::process()
m_poBuffer[m_poLen++] = YSF_START_SYNC;
} else {
for (uint8_t i = 0U; i < YSF_FRAME_LENGTH_BYTES; i++) {
uint8_t c = 0U;
m_buffer.get(c);
uint8_t c = m_buffer.get();
m_poBuffer[m_poLen++] = c;
}
}
+2 -2
View File
@@ -21,7 +21,7 @@
#include "Config.h"
#include "RingBuffer.h"
#include "SerialRB.h"
class CYSFTX {
public:
@@ -38,7 +38,7 @@ public:
void setParams(bool on, uint8_t txHang);
private:
CRingBuffer<uint8_t> m_buffer;
CSerialRB m_buffer;
arm_fir_interpolate_instance_q15 m_modFilter;
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
uint8_t m_poBuffer[1200U];
+33 -29
View File
@@ -22,36 +22,40 @@
/*
Pin definitions for DRCC_DVM BG7NQF board rev1
PTT PB12 output
LED_PTT PB4 output
LED_COS PB5 output
LED_SRV PB10 output
COS PB13 input
TX/PTT_LED PB12 output
RX/COS_LED PB5 output
STATUS_LED PB10 output
DSTAR N/A
DMR N/A
YSF N/A
P25 N/A
NXDN N/A
POCSAG N/A
COS_IN PB13 input
MDSTAR PB14 output
MDMR PB8 output
MYSF PB9 output
MP25 PB15 output
MNXDN N/A
MPOCSAG N/A
DSTAR N/A
DMR N/A
YSF N/A
P25 N/A
NXDN N/A
POCSAG N/A
RX PB0 analog input
RSSI PB1 analog input
TX PA4 analog output
MDMR/BIT0 PB8 output
MYSF/BIT1 PB9 output
MDSTAR/BIT2 PB14 output
MP25/BIT3 PB15 output Generic Mode Pins
MNXDN N/A
MPOCSAG N/A
EXT_CLK PA15 input
RX PA0 analog input
RSSI PA1 analog input
TX PA4 analog output
UART1_TX PA9 output
UART1_RX PA10 output
UART2_TX PA2 output
UART2_RX PA3 output
EXT_CLK PA15 input
UART1_TX PA9 output
UART1_RX PA10 output Host Data Communication
UART2_TX PA2 output
UART2_RX PA3 output Nextion Data Communication
I2C1_SCL PB6 output
I2C1_SDA PB7 output OLED Data Communication as master
*/
@@ -124,13 +128,13 @@ UART2_RX PA3 output
#define PIN_RX GPIO_Pin_0
#define PIN_RX_CH ADC_Channel_0
#define PORT_RX GPIOB
#define RCC_Per_RX RCC_AHB1Periph_GPIOB
#define PORT_RX GPIOA
#define RCC_Per_RX RCC_AHB1Periph_GPIOA
#define PIN_RSSI GPIO_Pin_1
#define PIN_RSSI_CH ADC_Channel_1
#define PORT_RSSI GPIOB
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOB
#define PORT_RSSI GPIOA
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOA
#define PIN_TX GPIO_Pin_4
#define PIN_TX_CH DAC_Channel_1
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2019,2020 by BG5HHP
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _PINS_F4_STM32EDA
#define _PINS_F4_STM32EDA
/*
Pin definitions for STM32F4 STM32-DVM-MTR2K & STM32-DVM-MASTR3:
COS PB13 input
PTT PB12 output
COSLED PB4 output
LED PB3 output
P25 PB8 output
NXDN PB9 output
DSTAR PB6 output
DMR PB5 output
YSF PB7 output
POCSAG PC10 output
FM PC11 output
RX PB0 analog input
RSSI PB1 analog input
TX PA4 analog output
EXT_CLK PA15 input
*/
#define PIN_COS GPIO_Pin_13
#define PORT_COS GPIOB
#define RCC_Per_COS RCC_AHB1Periph_GPIOB
#define PIN_PTT GPIO_Pin_12
#define PORT_PTT GPIOB
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
#define PIN_COSLED GPIO_Pin_4
#define PORT_COSLED GPIOB
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
#define PIN_LED GPIO_Pin_3
#define PORT_LED GPIOB
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
#define PIN_P25 GPIO_Pin_8
#define PORT_P25 GPIOB
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
#define PIN_NXDN GPIO_Pin_9
#define PORT_NXDN GPIOB
#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB
#define PIN_DSTAR GPIO_Pin_6
#define PORT_DSTAR GPIOB
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
#define PIN_DMR GPIO_Pin_5
#define PORT_DMR GPIOB
#define RCC_Per_DMR RCC_AHB1Periph_GPIOB
#define PIN_YSF GPIO_Pin_7
#define PORT_YSF GPIOB
#define RCC_Per_YSF RCC_AHB1Periph_GPIOB
#define PIN_POCSAG GPIO_Pin_10
#define PORT_POCSAG GPIOC
#define RCC_Per_POCSAG RCC_AHB1Periph_GPIOC
#define PIN_FM GPIO_Pin_11
#define PORT_FM GPIOC
#define RCC_Per_FM RCC_AHB1Periph_GPIOC
#define PIN_EXT_CLK GPIO_Pin_15
#define SRC_EXT_CLK GPIO_PinSource15
#define PORT_EXT_CLK GPIOA
#define PIN_RX GPIO_Pin_0
#define PIN_RX_CH ADC_Channel_8
#define PORT_RX GPIOB
#define RCC_Per_RX RCC_AHB1Periph_GPIOB
#define PIN_RSSI GPIO_Pin_1
#define PIN_RSSI_CH ADC_Channel_9
#define PORT_RSSI GPIOB
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOB
#define PIN_TX GPIO_Pin_4
#define PIN_TX_CH DAC_Channel_1
#endif