Compare commits

...
81 Commits
Author SHA1 Message Date
Jonathan Naylor 046e1df161 Merge pull request #362 from n0mjs710/link-audio-fidelity
FM link-mode network audio: anti-alias/reconstruction filtering
2026-08-16 17:36:20 +01:00
Cort Buffington d70ab85bae Copy-construct the ds/us filters from m_filterStage1/2/3
Same coefficients, no need to retype them. CFMDirectFormI's copy ctor
already copies coefficients and (zeroed, at this point) state.
2026-08-13 13:52:06 -05:00
Cort Buffington 384c5d559d Reconstruction filter after ext-audio upsampling in FM mode
CFMUpSampler zero-stuffs 8kHz to 24kHz with no lowpass after it,
leaving imaging artifacts in the reconstructed ext audio. Filter it
with a second, separately-stated pass of the same 300-2700Hz design
already used for TX shaping, right where it comes out of the upsampler.
2026-08-12 07:43:23 -05:00
Cort Buffington 4c7e8fbe46 Anti-alias filter before RF->network downsampling in FM mode
The 24kHz->8kHz decimation in CFMDownSampler had no lowpass ahead of
it, aliasing everything above 4kHz back into the audio band. Reuse
the existing 300-2700Hz filter design (separate instance/state) ahead
of all three addSample() call sites in FM.cpp.

Also: reset() wasn't clearing the ring buffer or in-progress sample
pack, so stale audio could bleed into the next transmission. And
addSample() packed samples into 12 bits with no saturation, so an
out-of-range value corrupted the neighbouring sample.
2026-08-11 09:57:47 -05:00
Jonathan Naylor 894ba76432 Merge pull request #361 from n0mjs710/link-mode-ext-gap
Link mode ext audio gap
2026-08-11 15:02:32 +01:00
Cort Buffington 8a89804fda Refactor FM_LINK_EXT_GAP_MS and improve comments
Updated the FM_LINK_EXT_GAP_MS constant declaration and added comments for clarity.
2026-08-11 08:55:22 -05:00
Cort Buffington 8bd467598c Debounce link-mode external-audio underrun before dropping PTT
Under [FM] LinkMode=1, linkStateMachine() set m_extSignal false the
instant a single ext-audio sample went missing, with no debounce --
unlike the non-link duplex/simplex paths, which already tolerate a
brief gap via the RELAYING_EXT -> RELAYING_WAIT_EXT / m_ackDelayTimer
pattern before declaring a real loss. Since IO::process() drops PTT
the moment the downstream TX buffer runs dry, that zero-debounce cutoff
meant even a very brief, late-but-recoverable gap from the gateway
(scheduling jitter, a late UDP frame, etc.) could cascade into a full
PTT drop-and-rekey cycle -- far more audible than the underlying gap
itself.

Adds m_extGapTimer, mirroring the existing pattern: on the first missed
sample, start the timer instead of immediately clearing m_extSignal; a
gap that recovers before FM_LINK_EXT_GAP_MS (default 60ms) expires
never affects m_extSignal at all, so the transmitter never notices.
Only a gap that outlasts the timer is treated as a genuine end of
transmission, exactly as before.

linkSamples()'s currentExtSample is now zero-initialized, since
m_extSignal can now stay true for a few samples past an underrun --
silence, not stack garbage, must be what gets played out to the modem
during that window.
2026-08-09 11:42:20 -05:00
Jonathan Naylor 715d3f5839 Merge pull request #359 from chancellor1101/bugfixes
Fix several small bugs: STM UART repeater port, FM noise squelch, Due FM LED, DMR trunking state
2026-07-13 12:07:39 +01:00
Clint Chance 68252f15f5 Fix the FM 25kHz calibration debug message.
It said 10kHz, a copy and paste from the case above.
2026-07-12 16:45:24 -05:00
Clint Chance 96061a6013 Make DMR trunking state handling order-independent.
The CSBK slot type was only encoded into the ALOHA frame inside
setColorCode(), so it took effect only if a reconfigure happened after
an ALOHA frame had already been received; on the usual command ordering
(SET_CONFIG then ALOHA) the transmitted slot type depended entirely on
the host-supplied bytes. Remember the colour code and stamp the slot
type in writeAloha() as well.

Also clear m_controlChannel when trunking is disabled, so the host has
a way to stop control channel transmission other than rebooting, and
fix a stray tab introduced in createCACH().
2026-07-12 16:45:24 -05:00
Clint Chance 90220cbb9d Fix the FM mode LED pin guard on the Arduino Due.
The pinMode() call for PIN_FM was guarded by USE_ALTERNATE_POCSAG_LEDS
instead of USE_ALTERNATE_FM_LEDS, a copy and paste error. The Teensy
version and the setFMInt() implementation in this file both use the FM
macro.
2026-07-12 16:42:58 -05:00
Clint Chance 2eb6f7fd06 Initialise and reset the FM noise squelch invalid counter.
m_invalidCount was missing from the constructor initialiser list and was
read uninitialised in process(), making the squelch behaviour after
power-up indeterminate. reset() also cleared neither hysteresis counter,
so state leaked across squelch resets.
2026-07-12 16:42:58 -05:00
Clint Chance a1794b44ed Fix RX interrupt flag being cleared on the wrong USART in STMUART.
handleIRQ() hardcoded USART1 when clearing the RXNE pending bit, so on
boards where the repeater/display UART is not USART1 (Nucleo, Discovery,
F767, UART5-based boards) the flag was cleared on the wrong peripheral.
Use the m_usart member, matching the TXE handling below.

Also fix flush(): it passed a flag constant to USART_GetITStatus and
span while TXE was set, which returns immediately since TXE indicates
the data register is already empty. Wait for the software TX FIFO to
drain and then for the TC flag, which indicates the shift register has
actually emptied.
2026-07-12 16:42:47 -05:00
Jonathan Naylor 57e692ad8c Small cleanups of the DMR trunking code. 2026-04-24 16:54:23 +01:00
Jonathan Naylor 841d7505c2 Merge pull request #358 from qradiolink/trunking_patches
Add support for DMR tier III trunking
2026-04-24 14:50:28 +01:00
adrian 549dfd7258 Change DMR ALOHA header to 0x17 2026-04-19 11:32:07 +03:00
adrian 30ba4c4c55 Fix merge gone wrong in DMRTX.cpp 2026-04-19 11:12:37 +03:00
adrian a696fd454c DMR trunking patches to DMRTX and SerialPort 2026-04-18 11:29:48 +03:00
Jonathan Naylor 3fd84d5ed4 Fix merge compilation issue. 2026-02-19 15:59:46 +00:00
Jonathan Naylor 78216afbb7 Clean up the ring buffer. 2026-02-18 13:40:05 +00:00
Jonathan Naylor 0308bfc6c7 Merge branch 'master' into mqtt 2026-02-18 13:36:22 +00:00
Jonathan Naylor 278c69152a Merge pull request #356 from F4FXL/C++11-Flags 2026-01-20 20:31:18 +00:00
Geoffrey Merck 8e144e81d0 Add C++11 standard flag to compiler flags
Debian Trixie ships with arm-none-eabi-g++ v14.2.1 which defaults to c++17, this forces compilation with c++11
2026-01-20 20:29:04 +01:00
Jonathan Naylor 5dd59fc473 Merge branch 'master' into mqtt 2025-08-28 17:00:21 +01:00
Jonathan Naylor 5fb5986d50 Remove M17 and AX.25 2025-08-27 18:35:30 +01:00
Jonathan Naylor 824c9b9852 Widened the size of serial data going into M17 and FM. 2025-01-13 15:05:28 +00:00
Jonathan Naylor f324f96db7 Increase the number of frames before YSF declares a signal to be lost. 2024-11-21 17:50:15 +00:00
Jonathan Naylor 15227d9cec Merge branch 'master' into mqtt 2024-10-02 16:10:09 +01:00
Jonathan Naylor c3246dbcb7 Merge branch 'master' of https://github.com/g4klx/MMDVM 2024-10-02 16:05:48 +01:00
Jonathan Naylor 65c51bf849 Merge branch 'master' into mqtt 2024-08-01 12:55:06 +01:00
Jonathan Naylor edd60caad3 Remove the deprecated register keyword. 2024-07-31 19:25:59 +01:00
Jonathan Naylor b8a0f7b1cf Merge pull request #343 from mytechguyri/master
Added option to configure Serial Repeater baud rate
2024-03-10 18:28:44 +00:00
John Tetreault 28e564738e Added option to configure Serial Repeater baud rate so end user can set to 115,200 for high speed Nextion display 2024-03-10 14:19:21 -04:00
Jonathan Naylor a7fddc37e1 Don't send a status when the FM repeater is reset, it causes a loop. 2023-11-18 16:45:36 +00:00
Jonathan Naylor a8bf99037e Merge branch 'master' into mqtt 2023-11-09 19:13:12 +00:00
Jonathan Naylor 029e6df4b9 Remove support for STM32F1* boards, they're just too slow. 2023-11-09 19:08:15 +00:00
Jonathan Naylor 85d3086d4c Restart the RSSI ADC conversion in the STM32 interrupt to match the signal ADC. 2023-10-10 20:06:50 +01:00
Jonathan Naylor ccf3b7301e Make the type correct for silence frames. 2023-10-10 20:03:01 +01:00
Jonathan Naylor 6b3a811ae7 Simplify the serial repeater code. 2023-10-05 19:49:17 +01:00
Jonathan Naylor 508018c89c Add the MMDVM serial protocol document from December 2015. 2023-09-22 16:51:03 +01:00
Jonathan Naylor 3e079f58ad Add RSSI reporting to FM and AX.25 2023-08-02 17:34:52 +01:00
Jonathan Naylor 7ef0d992d9 Improve the status reporting for FM mode. 2023-01-18 14:30:02 +00:00
Jonathan Naylor bf23a94038 Merge pull request #334 from n0mjs710/master 2023-01-09 15:17:17 +00:00
Cort Buffington b2755864ef Adding support for WA0EDA STM32-DVM-MTR2K v2.0d with FM LED, and a (commented) line to compile for 500000 serial port speed to support the WA0EDA MODEMs and others that use Armbian + AllWinner H2/H3 SOC that does not support 4600800. 2023-01-09 07:06:42 -06:00
Jonathan Naylor 7276ca3929 Update Version.h 2022-11-21 19:14:24 +00:00
Jonathan Naylor d14b9dede3 Update Version.h 2022-11-21 19:14:14 +00:00
Jonathan Naylor de87d77ad6 Merge pull request #330 from spongegovo/pocsagax25_fix
Fixed Serial protocol issue where MMDVMHost could not send POCSAG or AX25 to the MMDVM
2022-11-21 19:13:21 +00:00
spongegovo 2626ad5580 Update SerialPort.cpp 2022-11-20 09:15:59 +11:00
Jonathan Naylor 031b078e76 Merge pull request #327 from shawnchain/fix_ax25_dac_overflow_error 2022-09-13 14:09:12 +01:00
Shawn Chain 5d987969a9 The AX25 sine value ranges from 0-4095, and inside io.write() will perform the calculation (value * q15_t(txLevel) >> 15) which may exceed 4096 and cause the DAC Overflow error 2022-09-13 20:18:03 +08:00
Jonathan Naylor f898390956 Bump the version date. 2021-12-20 23:00:13 +00:00
Jonathan Naylor f8d1cc7aed Fix crash on long data frames. 2021-12-20 22:57:40 +00:00
Jonathan Naylor cf9fa93cb9 Include the ARM copyright in the README file. 2021-12-18 22:38:26 +00:00
Jonathan Naylor 69adaf2ebb Update SerialTeensy.cpp 2021-12-18 22:34:24 +00:00
Jonathan Naylor 7f4ae790ba Update STM32Utils.h 2021-12-18 22:31:48 +00:00
Jonathan Naylor 416fc8fc8b Update the README. 2021-12-13 10:31:10 +00:00
Jonathan Naylor 5b79356ddb Merge pull request #324 from 1tilen/master
Added PB15 to comment with pins, changed STM32F4 to STM32F7 in line 23
2021-11-14 19:51:29 +00:00
Tilen 26fc87aca6 Added PB15 to comment with pins, changed STM32F4 to STM32F7 in line 23 2021-11-14 19:21:35 +01:00
Jonathan Naylor 54745130c2 Add the transmit function. 2021-10-27 21:28:43 +01:00
Jonathan Naylor f65d52bf2f Add M17 calibration mode. 2021-10-27 19:37:58 +01:00
Jonathan Naylor 5800b33f21 Split the DMR and YSF filtering 2021-10-26 20:51:21 +01:00
Jonathan Naylor 105dba2c27 Merge branch 'master' of github.com:g4klx/MMDVM 2021-10-26 17:43:51 +01:00
Jonathan Naylor 777cc9f2ea Fix typo 2021-10-26 17:43:34 +01:00
Jonathan Naylor e879b4615a Merge branch 'master' of https://github.com/g4klx/MMDVM 2021-10-25 20:03:36 +01:00
Jonathan Naylor 8d69fc59be Update the README file 2021-10-25 20:03:17 +01:00
Jonathan Naylor 2f4e9cc269 Remove unused configuration option 2021-10-25 19:47:31 +01:00
Jonathan Naylor ee07f76bd5 Split the repeater and link mode audio paths. 2021-09-30 18:16:51 +01:00
Jonathan Naylor d063a1da1e Change the EOT handling on TX. 2021-09-19 14:25:12 +01:00
Jonathan Naylor 6a4dad4c44 Change the LinkMode state machine. 2021-09-06 22:50:40 +01:00
Jonathan Naylor ed6bd66fab Rename SimpleMode to LinkMode. 2021-09-06 21:53:01 +01:00
Jonathan Naylor 1a07a6f0d1 Add more details to the simple FM state machine. 2021-09-05 19:46:01 +01:00
Jonathan Naylor 70bf47f93e Add a bery simple FM mode for internet gatewaying. 2021-09-05 19:06:56 +01:00
Jonathan Naylor a5eaafc7e0 Change a method name. 2021-08-26 17:39:52 +01:00
Jonathan Naylor 2200945857 Change the EOF sync pattern for simplicity. 2021-08-26 16:50:18 +01:00
Jonathan Naylor 63a8e61a45 Update to send and receive the new EOF marker. 2021-08-25 21:11:09 +01:00
Jonathan Naylor 7275f65e44 Reset the transmit levels to be correct. 2021-08-23 21:07:17 +01:00
Jonathan Naylor 2aabdb61ab Remove the last traces of M17 packet mode and fix a buffer overrun bug. 2021-08-08 18:55:09 +01:00
Jonathan Naylor 96c0e5b141 Fix compiler warnings when building for POG style boards. 2021-07-19 21:20:57 +01:00
Jonathan Naylor 9c883cfafd Revert "Restore SerialRB for POG builds."
This reverts commit 8a1ab88c3a.
2021-07-01 22:58:59 +01:00
Jonathan Naylor 8a1ab88c3a Restore SerialRB for POG builds. 2021-07-01 22:28:59 +01:00
Jonathan Naylor f4e1c0b401 Fix a potential issue with reporting the wrong command type in ACK and NAK messages. 2021-06-17 16:55:59 +01:00
62 changed files with 719 additions and 5001 deletions
+2
View File
@@ -1,3 +1,5 @@
.pio
.vscode
*.o
obj/
bin/
-3
View File
@@ -4,6 +4,3 @@
[submodule "STM32F7XX_Lib"]
path = STM32F7XX_Lib
url = https://github.com/juribeparada/STM32F7XX_Lib.git
[submodule "STM32F10X_Lib"]
path = STM32F10X_Lib
url = https://github.com/shawnchain/STM32F10X_Lib.git
-36
View File
@@ -1,36 +0,0 @@
/*
* Copyright (C) 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(AX25DEFINES_H)
#define AX25DEFINES_H
const uint8_t AX25_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample rate
const uint8_t AX25_FRAME_START = 0x7EU;
const uint8_t AX25_FRAME_END = 0x7EU;
const uint8_t AX25_FRAME_ABORT = 0xFEU;
const uint8_t AX25_MAX_ONES = 5U;
const uint16_t AX25_MIN_FRAME_LENGTH = 17U; // Callsign (7) + Callsign (7) + Control (1) + Checksum (2)
const uint16_t AX25_MAX_FRAME_LENGTH = 330U; // Callsign (7) + Callsign (7) + 8 Digipeaters (56) +
// Control (1) + PID (1) + Data (256) + Checksum (2)
#endif
-319
View File
@@ -1,319 +0,0 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright 2015-2019 Mobilinkd LLC <[email protected]>
*
* 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"
#if defined(MODE_AX25)
#include "Globals.h"
#include "AX25Demodulator.h"
#include "AX25Defines.h"
const float32_t SAMPLE_RATE = 24000.0F;
const float32_t SYMBOL_RATE = 1200.0F;
const uint16_t DELAY_LEN = 11U;
const float32_t SAMPLES_PER_SYMBOL = SAMPLE_RATE / SYMBOL_RATE;
const float32_t PLL_LIMIT = SAMPLES_PER_SYMBOL / 2.0F;
const uint32_t LPF_FILTER_LEN = 48U;
q15_t LPF_FILTER_COEFFS[] = {
-2, -8, -17, -28, -40, -47, -47, -34,
-5, 46, 122, 224, 354, 510, 689, 885,
1092, 1302, 1506, 1693, 1856, 1987, 2077, 2124,
2124, 2077, 1987, 1856, 1693, 1506, 1302, 1092,
885, 689, 510, 354, 224, 122, 46, -5,
-34, -47, -47, -40, -28, -17, -8, -2
};
// Lock low-pass filter taps (80Hz Bessel)
// scipy.signal:
// b, a = bessel(4, [80.0/(1200/2)], 'lowpass')
//
const uint8_t PLL_IIR_SIZE = 5U;
const float32_t PLL_LOCK_B[] = {
1.077063e-03,4.308253e-03,6.462379e-03,4.308253e-03,1.077063e-03};
const float32_t PLL_LOCK_A[] = {
1.000000e+00,-2.774567e+00,2.962960e+00,-1.437990e+00,2.668296e-01};
// 64 Hz loop filter.
// scipy.signal:
// loop_coeffs = firwin(9, [64.0/(1200/2)], width = None,
// pass_zero = True, scale = True, window='hann')
//
const uint32_t PLL_FILTER_LEN = 7U;
float32_t PLL_FILTER_COEFFS[] = {3.196252e-02F, 1.204223e-01F, 2.176819e-01F, 2.598666e-01F, 2.176819e-01F, 1.204223e-01F, 3.196252e-02F};
CAX25Demodulator::CAX25Demodulator(int8_t n) :
m_frame(),
m_twist(n),
m_lpfFilter(),
m_lpfState(),
m_delayLine(NULL),
m_delayPos(0U),
m_nrziState(false),
m_pllFilter(),
m_pllState(),
m_pllLast(false),
m_pllBits(1U),
m_pllCount(0.0F),
m_pllJitter(0.0F),
m_pllDCD(false),
m_iirHistory(),
m_hdlcOnes(0U),
m_hdlcFlag(false),
m_hdlcBuffer(0U),
m_hdlcBits(0U),
m_hdlcState(AX25_IDLE)
{
m_delayLine = new bool[DELAY_LEN];
m_lpfFilter.numTaps = LPF_FILTER_LEN;
m_lpfFilter.pState = m_lpfState;
m_lpfFilter.pCoeffs = LPF_FILTER_COEFFS;
m_pllFilter.numTaps = PLL_FILTER_LEN;
m_pllFilter.pState = m_pllState;
m_pllFilter.pCoeffs = PLL_FILTER_COEFFS;
for (uint8_t i = 0U; i < PLL_IIR_SIZE; i++)
m_iirHistory[i] = 0.0F;
}
bool CAX25Demodulator::process(q15_t* samples, uint8_t length, CAX25Frame& frame)
{
bool result = false;
q15_t fa[RX_BLOCK_SIZE];
m_twist.process(samples, fa, RX_BLOCK_SIZE);
int16_t buffer[RX_BLOCK_SIZE];
for (uint8_t i = 0; i < length; i++) {
bool level = (fa[i] >= 0);
bool delayed = delay(level);
buffer[i] = (int16_t(level ^ delayed) << 1) - 1;
}
q15_t fc[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_lpfFilter, buffer, fc, RX_BLOCK_SIZE);
for (uint8_t i = 0; i < length; i++) {
bool bit = fc[i] >= 0;
bool sample = PLL(bit);
if (sample) {
// We will only ever get one frame because there are
// not enough bits in a block for more than one.
if (result) {
HDLC(NRZI(bit));
} else {
result = HDLC(NRZI(bit));
if (result) {
// Copy the frame data.
::memcpy(frame.m_data, m_frame.m_data, AX25_MAX_PACKET_LEN);
frame.m_length = m_frame.m_length;
frame.m_fcs = m_frame.m_fcs;
m_frame.m_length = 0U;
}
}
}
}
return result;
}
bool CAX25Demodulator::delay(bool b)
{
bool r = m_delayLine[m_delayPos];
m_delayLine[m_delayPos++] = b;
if (m_delayPos >= DELAY_LEN)
m_delayPos = 0U;
return r;
}
bool CAX25Demodulator::NRZI(bool b)
{
bool result = (b == m_nrziState);
m_nrziState = b;
return result;
}
bool CAX25Demodulator::PLL(bool input)
{
bool sample = false;
if (input != m_pllLast || m_pllBits > 16U) {
// Record transition.
m_pllLast = input;
if (m_pllCount > PLL_LIMIT)
m_pllCount -= SAMPLES_PER_SYMBOL;
float32_t adjust = m_pllBits > 16U ? 5.0F : 0.0F;
float32_t offset = m_pllCount / float32_t(m_pllBits);
float32_t jitter;
::arm_fir_f32(&m_pllFilter, &offset, &jitter, 1U);
float32_t absOffset = adjust;
if (offset < 0.0F)
absOffset -= offset;
else
absOffset += offset;
m_pllJitter = iir(absOffset);
m_pllCount -= jitter / 2.0F;
m_pllBits = 1U;
} else {
if (m_pllCount > PLL_LIMIT) {
sample = true;
m_pllCount -= SAMPLES_PER_SYMBOL;
m_pllBits++;
}
}
m_pllCount += 1.0F;
return sample;
}
bool CAX25Demodulator::HDLC(bool b)
{
if (m_hdlcOnes == AX25_MAX_ONES) {
if (b) {
// flag byte
m_hdlcFlag = true;
} else {
// bit stuffing...
m_hdlcFlag = false;
m_hdlcOnes = 0U;
return false;
}
}
m_hdlcBuffer >>= 1;
m_hdlcBuffer |= b ? 128U : 0U;
m_hdlcBits++; // Free-running until Sync byte.
if (b)
m_hdlcOnes++;
else
m_hdlcOnes = 0U;
if (m_hdlcFlag) {
bool result = false;
switch (m_hdlcBuffer) {
case AX25_FRAME_END:
if (m_frame.m_length >= AX25_MIN_FRAME_LENGTH) {
result = m_frame.checkCRC();
if (!result)
m_frame.m_length = 0U;
} else {
m_frame.m_length = 0U;
}
m_hdlcState = AX25_SYNC;
m_hdlcFlag = false;
m_hdlcBits = 0U;
break;
case AX25_FRAME_ABORT:
// Frame aborted
m_frame.m_length = 0U;
m_hdlcState = AX25_IDLE;
m_hdlcFlag = false;
m_hdlcBits = 0U;
break;
default:
break;
}
return result;
}
switch (m_hdlcState) {
case AX25_IDLE:
break;
case AX25_SYNC:
if (m_hdlcBits == 8U) { // 8th bit.
// Start of frame data.
m_hdlcState = AX25_RECEIVE;
m_frame.append(m_hdlcBuffer);
m_hdlcBits = 0U;
}
break;
case AX25_RECEIVE:
if (m_hdlcBits == 8U) { // 8th bit.
m_frame.append(m_hdlcBuffer);
m_hdlcBits = 0U;
}
break;
default:
break;
}
return false;
}
void CAX25Demodulator::setTwist(int8_t n)
{
m_twist.setTwist(n);
}
bool CAX25Demodulator::isDCD()
{
if (m_pllJitter <= (SAMPLES_PER_SYMBOL * 0.03F))
m_pllDCD = true;
else if (m_pllJitter >= (SAMPLES_PER_SYMBOL * 0.15F))
m_pllDCD = false;
return m_pllDCD;
}
float32_t CAX25Demodulator::iir(float32_t input)
{
for (int8_t i = int8_t(PLL_IIR_SIZE) - 1; i != 0; i--)
m_iirHistory[i] = m_iirHistory[i - 1];
m_iirHistory[0] = input;
for (uint8_t i = 1U; i < PLL_IIR_SIZE; i++)
m_iirHistory[0] -= PLL_LOCK_A[i] * m_iirHistory[i];
float32_t result = 0.0F;
for (uint8_t i = 0U; i < PLL_IIR_SIZE; i++)
result += PLL_LOCK_B[i] * m_iirHistory[i];
return result;
}
#endif
-77
View File
@@ -1,77 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#if !defined(AX25Demodulator_H)
#define AX25Demodulator_H
#include "AX25Frame.h"
#include "AX25Twist.h"
enum AX25_STATE {
AX25_IDLE,
AX25_SYNC,
AX25_RECEIVE
};
class CAX25Demodulator {
public:
CAX25Demodulator(int8_t n);
bool process(q15_t* samples, uint8_t length, CAX25Frame& frame);
void setTwist(int8_t n);
bool isDCD();
private:
CAX25Frame m_frame;
CAX25Twist m_twist;
arm_fir_instance_q15 m_lpfFilter;
q15_t m_lpfState[70U]; // NoTaps + BlockSize - 1, 48 + 20 - 1 plus some spare
bool* m_delayLine;
uint16_t m_delayPos;
bool m_nrziState;
arm_fir_instance_f32 m_pllFilter;
float32_t m_pllState[20U]; // NoTaps + BlockSize - 1, 7 + 1 - 1 plus some spare
bool m_pllLast;
uint8_t m_pllBits;
float32_t m_pllCount;
float32_t m_pllJitter;
bool m_pllDCD;
float32_t m_iirHistory[5U];
uint16_t m_hdlcOnes;
bool m_hdlcFlag;
uint16_t m_hdlcBuffer;
uint16_t m_hdlcBits;
AX25_STATE m_hdlcState;
bool delay(bool b);
bool NRZI(bool b);
bool PLL(bool b);
bool HDLC(bool b);
float32_t iir(float32_t input);
};
#endif
#endif
-127
View File
@@ -1,127 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#include "Globals.h"
#include "AX25Frame.h"
const uint16_t CCITT_TABLE[] = {
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78 };
CAX25Frame::CAX25Frame(const uint8_t* data, uint16_t length) :
m_data(),
m_length(0U),
m_fcs(0U)
{
for (uint16_t i = 0U; i < length && i < (AX25_MAX_PACKET_LEN - 2U); i++)
m_data[m_length++] = data[i];
}
CAX25Frame::CAX25Frame() :
m_data(),
m_length(0U),
m_fcs(0U)
{
}
bool CAX25Frame::append(uint16_t c)
{
if (m_length == AX25_MAX_PACKET_LEN)
return false;
m_data[m_length++] = uint8_t(c);
return true;
}
bool CAX25Frame::checkCRC()
{
union {
uint16_t crc16;
uint8_t crc8[2U];
};
crc16 = 0xFFFFU;
for (uint16_t i = 0U; i < (m_length - 2U); i++)
crc16 = uint16_t(crc8[1U]) ^ CCITT_TABLE[crc8[0U] ^ m_data[i]];
crc16 = ~crc16;
if (crc8[0U] == m_data[m_length - 2U] && crc8[1U] == m_data[m_length - 1U]) {
m_fcs = crc16;
return true;
} else {
return false;
}
}
void CAX25Frame::addCRC()
{
union {
uint16_t crc16;
uint8_t crc8[2U];
};
crc16 = 0xFFFFU;
for (uint16_t i = 0U; i < m_length; i++)
crc16 = uint16_t(crc8[1U]) ^ CCITT_TABLE[crc8[0U] ^ m_data[i]];
crc16 = ~crc16;
m_fcs = crc16;
m_data[m_length++] = crc8[0U];
m_data[m_length++] = crc8[1U];
}
#endif
-47
View File
@@ -1,47 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#if !defined(AX25Frame_H)
#define AX25Frame_H
const uint16_t AX25_MAX_PACKET_LEN = 300U;
class CAX25Frame {
public:
CAX25Frame(const uint8_t* data, uint16_t length);
CAX25Frame();
bool append(uint16_t c);
bool checkCRC();
void addCRC();
uint8_t m_data[AX25_MAX_PACKET_LEN];
uint16_t m_length;
uint16_t m_fcs;
};
#endif
#endif
-211
View File
@@ -1,211 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#include "Globals.h"
#include "AX25RX.h"
/*
* Generated with Scipy Filter, 152 coefficients, 1100-2300Hz bandpass,
* Hann window, starting and ending 0 value coefficients removed.
*
* np.array(
* firwin2(152,
* [
* 0.0,
* 1000.0/(sample_rate/2),
* 1100.0/(sample_rate/2),
* 2350.0/(sample_rate/2),
* 2500.0/(sample_rate/2),
* 1.0
* ],
* [0,0,1,1,0,0],
* antisymmetric = False,
* window='hann') * 32768,
* dtype=int)[10:-10]
*/
const uint32_t FILTER_LEN = 130U;
q15_t FILTER_COEFFS[] = {
5, 12, 18, 21, 19, 11, -2, -15, -25, -27,
-21, -11, -3, -5, -19, -43, -69, -83, -73, -35,
27, 98, 155, 180, 163, 109, 39, -20, -45, -26,
23, 74, 89, 39, -81, -247, -407, -501, -480, -334,
-92, 175, 388, 479, 429, 275, 99, 5, 68, 298,
626, 913, 994, 740, 115, -791, -1770, -2544, -2847, -2509,
-1527, -76, 1518, 2875, 3653, 3653, 2875, 1518, -76, -1527,
-2509, -2847, -2544, -1770, -791, 115, 740, 994, 913, 626,
298, 68, 5, 99, 275, 429, 479, 388, 175, -92,
-334, -480, -501, -407, -247, -81, 39, 89, 74, 23,
-26, -45, -20, 39, 109, 163, 180, 155, 98, 27,
-35, -73, -83, -69, -43, -19, -5, -3, -11, -21,
-27, -25, -15, -2, 11, 19, 21, 18, 12, 5
};
CAX25RX::CAX25RX() :
m_filter(),
m_state(),
m_demod1(3),
m_demod2(6),
m_demod3(9),
m_lastFCS(0U),
m_count(0U),
m_slotTime(30U),
m_slotCount(0U),
m_pPersist(128U),
m_dcd(false),
m_canTX(false),
m_x(1U),
m_a(0xB7U),
m_b(0x73U),
m_c(0xF6U)
{
m_filter.numTaps = FILTER_LEN;
m_filter.pState = m_state;
m_filter.pCoeffs = FILTER_COEFFS;
initRand();
}
void CAX25RX::samples(q15_t* samples, uint8_t length)
{
q15_t output[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_filter, samples, output, RX_BLOCK_SIZE);
m_count++;
CAX25Frame frame;
bool ret = m_demod1.process(output, length, frame);
if (ret) {
if (frame.m_fcs != m_lastFCS || m_count > 2U) {
m_lastFCS = frame.m_fcs;
m_count = 0U;
serial.writeAX25Data(frame.m_data, frame.m_length - 2U);
}
DEBUG1("Decoder 1 reported");
}
ret = m_demod2.process(output, length, frame);
if (ret) {
if (frame.m_fcs != m_lastFCS || m_count > 2U) {
m_lastFCS = frame.m_fcs;
m_count = 0U;
serial.writeAX25Data(frame.m_data, frame.m_length - 2U);
}
DEBUG1("Decoder 2 reported");
}
ret = m_demod3.process(output, length, frame);
if (ret) {
if (frame.m_fcs != m_lastFCS || m_count > 2U) {
m_lastFCS = frame.m_fcs;
m_count = 0U;
serial.writeAX25Data(frame.m_data, frame.m_length - 2U);
}
DEBUG1("Decoder 3 reported");
}
m_slotCount += RX_BLOCK_SIZE;
if (m_slotCount >= m_slotTime) {
m_slotCount = 0U;
bool dcd1 = m_demod1.isDCD();
bool dcd2 = m_demod2.isDCD();
bool dcd3 = m_demod3.isDCD();
if (dcd1 || dcd2 || dcd3) {
if (!m_dcd) {
io.setDecode(true);
io.setADCDetection(true);
m_dcd = true;
}
m_canTX = false;
} else {
if (m_dcd) {
io.setDecode(false);
io.setADCDetection(false);
m_dcd = false;
}
m_canTX = m_pPersist >= rand();
}
}
}
bool CAX25RX::canTX() const
{
return m_canTX;
}
void CAX25RX::setParams(int8_t twist, uint8_t slotTime, uint8_t pPersist)
{
m_demod1.setTwist(twist - 3);
m_demod2.setTwist(twist);
m_demod3.setTwist(twist + 3);
m_slotTime = slotTime * 240U; // Slot time in samples
m_pPersist = pPersist;
}
// Taken from https://www.electro-tech-online.com/threads/ultra-fast-pseudorandom-number-generator-for-8-bit.124249/
//X ABC Algorithm Random Number Generator for 8-Bit Devices:
//This is a small PRNG, experimentally verified to have at least a 50 million byte period
//by generating 50 million bytes and observing that there were no overapping sequences and repeats.
//This generator passes serial correlation, entropy , Monte Carlo Pi value, arithmetic mean,
//And many other statistical tests. This generator may have a period of up to 2^32, but this has
//not been verified.
//
// By XORing 3 bytes into the a,b, and c registers, you can add in entropy from
//an external source easily.
//
//This generator is free to use, but is not suitable for cryptography due to its short period(by //cryptographic standards) and simple construction. No attempt was made to make this generator
// suitable for cryptographic use.
//
//Due to the use of a constant counter, the generator should be resistant to latching up.
//A significant performance gain is had in that the x variable is only ever incremented.
//
//Only 4 bytes of ram are needed for the internal state, and generating a byte requires 3 XORs , //2 ADDs, one bit shift right , and one increment. Difficult or slow operations like multiply, etc
//were avoided for maximum speed on ultra low power devices.
void CAX25RX::initRand() //Can also be used to seed the rng with more entropy during use.
{
m_a = (m_a ^ m_c ^ m_x);
m_b = (m_b + m_a);
m_c = (m_c + (m_b >> 1) ^ m_a);
}
uint8_t CAX25RX::rand()
{
m_x++; //x is incremented every round and is not affected by any other variable
m_a = (m_a ^ m_c ^ m_x); //note the mix of addition and XOR
m_b = (m_b + m_a); //And the use of very few instructions
m_c = (m_c + (m_b >> 1) ^ m_a); //the right shift is to ensure that high-order bits from b can affect
return uint8_t(m_c); //low order bits of other variables
}
#endif
-63
View File
@@ -1,63 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#if !defined(AX25RX_H)
#define AX25RX_H
#include "AX25Demodulator.h"
class CAX25RX {
public:
CAX25RX();
void samples(q15_t* samples, uint8_t length);
void setParams(int8_t twist, uint8_t slotTime, uint8_t pPersist);
bool canTX() const;
private:
arm_fir_instance_q15 m_filter;
q15_t m_state[160U]; // NoTaps + BlockSize - 1, 130 + 20 - 1 plus some spare
CAX25Demodulator m_demod1;
CAX25Demodulator m_demod2;
CAX25Demodulator m_demod3;
uint16_t m_lastFCS;
uint32_t m_count;
uint32_t m_slotTime;
uint32_t m_slotCount;
uint8_t m_pPersist;
bool m_dcd;
bool m_canTX;
uint8_t m_x;
uint8_t m_a;
uint8_t m_b;
uint8_t m_c;
void initRand();
uint8_t rand();
};
#endif
#endif
-191
View File
@@ -1,191 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#include "Globals.h"
#include "AX25TX.h"
#include "AX25Defines.h"
#include "AX25Frame.h"
const uint8_t START_FLAG[] = { AX25_FRAME_START };
const uint8_t END_FLAG[] = { AX25_FRAME_END };
const uint8_t BIT_MASK_TABLE1[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE1[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE1[(i)&7])
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE1[(i)&7])
const uint8_t BIT_MASK_TABLE2[] = { 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U };
#define WRITE_BIT2(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE2[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE2[(i)&7])
#define READ_BIT2(p,i) (p[(i)>>3] & BIT_MASK_TABLE2[(i)&7])
const uint16_t AUDIO_TABLE_LEN = 120U;
const q15_t AUDIO_TABLE_DATA[] = {
0, 214, 428, 641, 851, 1060, 1265, 1468, 1666, 1859, 2048, 2230, 2407, 2577, 2740, 2896, 3043, 3182, 3313, 3434, 3546, 3649,
3741, 3823, 3895, 3955, 4006, 4045, 4073, 4089, 4095, 4089, 4073, 4045, 4006, 3955, 3895, 3823, 3741, 3649, 3546, 3434, 3313,
3182, 3043, 2896, 2740, 2577, 2407, 2230, 2048, 1859, 1666, 1468, 1265, 1060, 851, 641, 428, 214, 0, -214, -428, -641, -851,
-1060, -1265, -1468, -1666, -1859, -2047, -2230, -2407, -2577, -2740, -2896, -3043, -3182, -3313, -3434, -3546, -3649, -3741,
-3823, -3895, -3955, -4006, -4045, -4073, -4089, -4095, -4089, -4073, -4045, -4006, -3955, -3895, -3823, -3741, -3649, -3546,
-3434, -3313, -3182, -3043, -2896, -2740, -2577, -2407, -2230, -2047, -1859, -1666, -1468, -1265, -1060, -851, -641, -428, -214
};
CAX25TX::CAX25TX() :
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(360U),
m_tablePtr(0U),
m_nrzi(false)
{
}
void CAX25TX::process()
{
if (m_poLen == 0U)
return;
if (!m_duplex) {
if (m_poPtr == 0U) {
bool tx = ax25RX.canTX();
if (!tx)
return;
}
}
uint16_t space = io.getSpace();
while (space > AX25_RADIO_SYMBOL_LENGTH) {
bool b = READ_BIT1(m_poBuffer, m_poPtr) != 0U;
m_poPtr++;
writeBit(b);
space -= AX25_RADIO_SYMBOL_LENGTH;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
}
uint8_t CAX25TX::writeData(const uint8_t* data, uint16_t length)
{
CAX25Frame frame(data, length);
frame.addCRC();
m_poLen = 0U;
m_poPtr = 0U;
m_nrzi = false;
m_tablePtr = 0U;
// Add TX delay
for (uint16_t i = 0U; i < m_txDelay; i++, m_poLen++) {
bool preamble = NRZI(false);
WRITE_BIT1(m_poBuffer, m_poLen, preamble);
}
// Add the Start Flag
for (uint16_t i = 0U; i < 8U; i++, m_poLen++) {
bool b1 = READ_BIT1(START_FLAG, i) != 0U;
bool b2 = NRZI(b1);
WRITE_BIT1(m_poBuffer, m_poLen, b2);
}
uint8_t ones = 0U;
for (uint16_t i = 0U; i < (frame.m_length * 8U); i++) {
bool b1 = READ_BIT2(frame.m_data, i) != 0U;
bool b2 = NRZI(b1);
WRITE_BIT1(m_poBuffer, m_poLen, b2);
m_poLen++;
if (b1) {
ones++;
if (ones == AX25_MAX_ONES) {
// Bit stuffing
bool b = NRZI(false);
WRITE_BIT1(m_poBuffer, m_poLen, b);
m_poLen++;
ones = 0U;
}
} else {
ones = 0U;
}
}
// Add the End Flag
for (uint16_t i = 0U; i < 8U; i++, m_poLen++) {
bool b1 = READ_BIT1(END_FLAG, i) != 0U;
bool b2 = NRZI(b1);
WRITE_BIT1(m_poBuffer, m_poLen, b2);
}
return 0U;
}
void CAX25TX::writeBit(bool b)
{
q15_t buffer[AX25_RADIO_SYMBOL_LENGTH];
for (uint8_t i = 0U; i < AX25_RADIO_SYMBOL_LENGTH; i++) {
q15_t value = AUDIO_TABLE_DATA[m_tablePtr];
if (b) {
// De-emphasise the lower frequency by 6dB
value >>= 2;
m_tablePtr += 6U;
} else {
m_tablePtr += 11U;
}
buffer[i] = value;
if (m_tablePtr >= AUDIO_TABLE_LEN)
m_tablePtr -= AUDIO_TABLE_LEN;
}
io.write(STATE_AX25, buffer, AX25_RADIO_SYMBOL_LENGTH);
}
void CAX25TX::setTXDelay(uint8_t delay)
{
m_txDelay = delay * 12U;
}
uint8_t CAX25TX::getSpace() const
{
return m_poLen == 0U ? 255U : 0U;
}
bool CAX25TX::NRZI(bool b)
{
if (!b)
m_nrzi = !m_nrzi;
return m_nrzi;
}
#endif
-53
View File
@@ -1,53 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#if !defined(AX25TX_H)
#define AX25TX_H
class CAX25TX {
public:
CAX25TX();
uint8_t writeData(const uint8_t* data, uint16_t length);
void process();
void setTXDelay(uint8_t delay);
uint8_t getSpace() const;
private:
uint8_t m_poBuffer[600U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint16_t m_txDelay;
uint16_t m_tablePtr;
bool m_nrzi;
void writeBit(bool b);
bool NRZI(bool b);
};
#endif
#endif
-316
View File
@@ -1,316 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#include "Globals.h"
#include "AX25Twist.h"
// 1200Hz = -12dB, 2200Hz = 0dB; 3381Hz cutoff; cosine.
q15_t dB12[] = {
176,
-812,
-3916,
-7586,
23536,
-7586,
-3916,
-812,
176
};
// 1200Hz = -11dB, 2200Hz = 0dB; 3258Hz cutoff; cosine.
q15_t dB11[] = {
121,
-957,
-3959,
-7383,
23871,
-7383,
-3959,
-957,
121
};
// 1200Hz = -10dB, 2200Hz = 0dB; 3118Hz cutoff; cosine.
q15_t dB10[] = {
56,
-1110,
-3987,
-7141,
24254,
-7141,
-3987,
-1110,
56
};
// 1200Hz = -9dB, 2200Hz = 0dB; 2959Hz cutoff; cosine.
q15_t dB9[] = {
-19,
-1268,
-3994,
-6856,
24688,
-6856,
-3994,
-1268,
-19
};
// 1200Hz = -8dB, 2200Hz = 0dB; 2778Hz cutoff; cosine.
q15_t dB8[] = {
-104,
-1424,
-3968,
-6516,
25182,
-6516,
-3968,
-1424,
-104
};
// 1200Hz = -7dB, 2200Hz = 0dB; 2573Hz cutoff; cosine.
q15_t dB7[] = {
-196,
-1565,
-3896,
-6114,
25742,
-6114,
-3896,
-1565,
-196
};
// 1200Hz = -6dB, 2200Hz = 0dB; 2343Hz cutoff; cosine.
q15_t dB6[] = {
-288,
-1676,
-3761,
-5642,
26370,
-5642,
-3761,
-1676,
-288
};
// 1200Hz = -5dB, 2200Hz = 0dB; 2085Hz cutoff; cosine.
q15_t dB5[] = {
-370,
-1735,
-3545,
-5088,
27075,
-5088,
-3545,
-1735,
-370
};
// 1200Hz = -4dB, 2200Hz = 0dB; 1790Hz cutoff; cosine.
q15_t dB4[] = {
-432,
-1715,
-3220,
-4427,
27880,
-4427,
-3220,
-1715,
-432
};
// 1200Hz = -3dB, 2200Hz = 0dB; 1456Hz cutoff; cosine.
q15_t dB3[] = {
-452,
-1582,
-2759,
-3646,
28792,
-3646,
-2759,
-1582,
-452
};
// 1200Hz = -2dB, 2200Hz = 0dB; 1070Hz cutoff; cosine.
q15_t dB2[] = {
-408,
-1295,
-2123,
-2710,
29846,
-2710,
-2123,
-1295,
-408
};
// 1200Hz = -1dB, 2200Hz = 0dB; 605Hz cutoff; cosine.
q15_t dB1[] = {
-268,
-795,
-1244,
-1546,
31116,
-1546,
-1244,
-795,
-268
};
q15_t dB0[] = {
0,
0,
0,
0,
32767,
0,
0,
0,
0,
};
// 1200Hz = 0dB, 2200Hz = -1dB; 4130Hz cutoff; cosine.
q15_t dB_1[] = {
-419,
-177,
3316,
8650,
11278,
8650,
3316,
-177,
-419
};
// 1200Hz = 0dB, 2200Hz = -2dB; 3190Hz cutoff; cosine.
q15_t dB_2[] = {
-90,
1033,
3975,
7267,
8711,
7267,
3975,
1033,
-90
};
// 1200Hz = 0dB, 2200Hz = -3dB; 2330Hz cutoff; cosine.
q15_t dB_3[] = {
292,
1680,
3752,
5615,
6362,
5615,
3752,
1680,
292
};
// 1200Hz = 0dB, 2200Hz = -4dB; 2657Hz cutoff; boxcar.
q15_t dB_4[] = {
917,
3024,
5131,
6684,
7255,
6684,
5131,
3024,
917
};
// 1200Hz = 0dB, 2200Hz = -5dB; 2360Hz cutoff; boxcar.
q15_t dB_5[] = {
1620,
3339,
4925,
6042,
6444,
6042,
4925,
3339,
1620
};
// 1200Hz = 0dB, 2200Hz = -6dB; 2067Hz cutoff; boxcar.
q15_t dB_6[] = {
2161,
3472,
4605,
5373,
5644,
5373,
4605,
3472,
2161
};
q15_t* coeffs[] = {
dB12,
dB11,
dB10,
dB9,
dB8,
dB7,
dB6,
dB5,
dB4,
dB3,
dB2,
dB1,
dB0,
dB_1,
dB_2,
dB_3,
dB_4,
dB_5,
dB_6
};
CAX25Twist::CAX25Twist(int8_t n) :
m_filter(),
m_state()
{
setTwist(n);
}
void CAX25Twist::process(q15_t* in, q15_t* out, uint8_t length)
{
::arm_fir_fast_q15(&m_filter, in, out, length);
}
void CAX25Twist::setTwist(int8_t n)
{
uint8_t twist = uint8_t(n + 6);
m_filter.numTaps = 9;
m_filter.pState = m_state;
m_filter.pCoeffs = coeffs[twist];
}
#endif
-42
View File
@@ -1,42 +0,0 @@
/*
* Copyright (C) 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"
#if defined(MODE_AX25)
#if !defined(AX25Twist_H)
#define AX25Twist_H
class CAX25Twist {
public:
CAX25Twist(int8_t n);
void process(q15_t* in, q15_t* out, uint8_t length);
void setTwist(int8_t n);
private:
arm_fir_instance_q15 m_filter;
q15_t m_state[40U]; // NoTaps + BlockSize - 1, 9 + 20 - 1 plus some spare
};
#endif
#endif
+11 -24
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2025 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
@@ -20,7 +20,7 @@
#define CONFIG_H
// Allow for the selection of which modes to compile into the firmware. This is particularly useful for processors
// which have limited code space and processing power like the STM32F103, which is found on older/cheaper boards.
// which have limited code space and processing power.
// Enable D-Star support.
#define MODE_DSTAR
@@ -38,18 +38,12 @@
#define MODE_NXDN
#define USE_NXDN_BOXCAR
// Enable M17 support.
#define MODE_M17
// Enable POCSAG support.
#define MODE_POCSAG
// Enable FM support.
#define MODE_FM
// Enable AX.25 support, this is only enabled if FM is also enabled.
#define MODE_AX25
// Allow for the use of high quality external clock oscillators
// The number is the frequency of the oscillator in Hertz.
//
@@ -58,7 +52,7 @@
// Frequencies such as 10.0 Mhz (48000 * 208.333) or 20 Mhz (48000 * 416.666) are not suitable.
//
// For 12 MHz
// #define EXTERNAL_OSC 12000000
#define EXTERNAL_OSC 12000000
// For 12.288 MHz
// #define EXTERNAL_OSC 12288000
// For 14.4 MHz
@@ -66,13 +60,11 @@
// 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
// Select a baud rate for host communication. The faster speeds are needed for external FM to work.
// #define SERIAL_SPEED 115200 // Suitable for most older boards (Arduino Due, etc). External FM will NOT work with this!
// #define SERIAL_SPEED 230400 // Only works on newer boards like fast M4, M7, Teensy 3.x. External FM might work with this
#define SERIAL_SPEED 460800 // Only works on newer boards like fast M4, M7, Teensy 3.x. External FM should work with this
//#define SERIAL_SPEED 500000 // Used with newer boards and Armbian on AllWinner SOCs (H2, H3) that do not support 460800
// Use pins to output the current mode via LEDs
#define MODE_LEDS
@@ -80,13 +72,8 @@
// For the original Arduino Due pin layout
// #define ARDUINO_DUE_PAPA
#if defined(STM32F1)
// For the SQ6POG board
#define STM32F1_POG
#else
// For the ZUM V1.0 and V1.0.1 boards pin layout
// #define ARDUINO_DUE_ZUM_V10
#endif
// For the SP8NTH board
// #define ARDUINO_DUE_NTH
@@ -107,6 +94,9 @@
// Use the modem as a serial repeater for Nextion displays
#define SERIAL_REPEATER
// Set the baud rate of the modem serial repeater for Nextion displays
#define SERIAL_REPEATER_BAUD_RATE 9600
// Use the modem as an I2C repeater for OLED displays
// #define I2C_REPEATER
@@ -120,9 +110,6 @@
// 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
+64 -11
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2017 by Andy Uribe CA6JAU
* Copyright (C) 2022,2023,2026 by Adrian Musceac YO8RZZ
*
* 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
@@ -42,6 +43,12 @@ const uint8_t IDLE_DATA[] =
0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U,
0xE4U, 0x65U, 0x17U, 0x1BU, 0x48U, 0xCAU, 0x6DU, 0x4FU, 0xC6U, 0x10U, 0xB4U};
// DT Terminator with LC, empty
const uint8_t TERMINATOR_DATA[] =
{0x00U, 0xCCU, 0x01U, 0x80U, 0x01U, 0x94U, 0x01U, 0x48U, 0x08U, 0xB0U, 0x08U,
0x80U, 0x04U, 0xADU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD9U, 0x65U, 0x90U,
0x01U, 0x78U, 0x00U, 0xD0U, 0x08U, 0x40U, 0x14U, 0x00U, 0x0DU, 0x80U, 0x15U};
const uint8_t CACH_INTERLEAVE[] =
{ 1U, 2U, 3U, 5U, 6U, 7U, 9U, 10U, 11U, 13U, 15U, 16U, 17U, 19U, 20U, 21U, 23U,
25U, 26U, 27U, 29U, 30U, 31U, 33U, 34U, 35U, 37U, 39U, 40U, 41U, 43U, 44U, 45U, 47U,
@@ -65,6 +72,7 @@ m_modFilter(),
m_modState(),
m_state(DMRTXSTATE_IDLE),
m_idle(),
m_aloha(),
m_cachPtr(0U),
m_shortLC(),
m_newShortLC(),
@@ -74,7 +82,10 @@ m_poLen(0U),
m_poPtr(0U),
m_frameCount(0U),
m_abortCount(),
m_abort()
m_abort(),
m_controlChannel(false),
m_trunking(false),
m_colorCode(0U)
{
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
@@ -211,6 +222,23 @@ uint8_t CDMRTX::writeShortLC(const uint8_t* data, uint16_t length)
return 0U;
}
uint8_t CDMRTX::writeAloha(const uint8_t* data, uint16_t length)
{
if (length != DMR_FRAME_LENGTH_BYTES)
return 4U;
::memcpy(m_aloha, data, length);
// Stamp the slot type with the current colour code so that the result
// doesn't depend on the ordering of the ALOHA and SET_CONFIG commands.
CDMRSlotType slotType;
slotType.encode(m_colorCode, DT_CSBK, m_aloha);
m_controlChannel = true;
return 0U;
}
uint8_t CDMRTX::writeAbort(const uint8_t* data, uint16_t length)
{
if (length != 1U)
@@ -294,7 +322,7 @@ uint8_t CDMRTX::getSpace2() const
void CDMRTX::createData(uint8_t slotIndex)
{
if (m_fifo[slotIndex].getData() >= DMR_FRAME_LENGTH_BYTES && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) {
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_markBuffer[i] = MARK_NONE;
@@ -303,7 +331,11 @@ void CDMRTX::createData(uint8_t slotIndex)
m_abort[slotIndex] = false;
// Transmit an idle message
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = m_idle[i];
if ((slotIndex == 0U) && m_controlChannel && m_trunking)
m_poBuffer[i] = m_aloha[i];
else
m_poBuffer[i] = m_idle[i];
m_markBuffer[i] = MARK_NONE;
}
}
@@ -354,10 +386,14 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
m_cachPtr = 0U;
if (m_cachPtr == 0U) {
if (m_fifo[0U].getData() == 0U && m_fifo[1U].getData() == 0U)
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
else
if ((m_fifo[0U].getData() == 0U) && (m_fifo[1U].getData() == 0U)) {
if (m_trunking)
::memcpy(m_shortLC, m_newShortLC, 12U);
else
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
} else {
::memcpy(m_shortLC, m_newShortLC, 12U);
}
}
::memcpy(m_poBuffer, m_shortLC + m_cachPtr, 3U);
@@ -367,8 +403,8 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
bool at = false;
if (m_frameCount >= STARTUP_COUNT)
at = m_fifo[rxSlotIndex].getData() > 0U;
bool tc = txSlotIndex == 1U;
at = (m_fifo[rxSlotIndex].getData() > 0U);
bool tc = (txSlotIndex == 1U);
bool ls0 = true; // For 1 and 2
bool ls1 = true;
@@ -397,10 +433,20 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
void CDMRTX::setColorCode(uint8_t colorCode)
{
::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES);
m_colorCode = colorCode;
if (m_trunking)
::memcpy(m_idle, TERMINATOR_DATA, DMR_FRAME_LENGTH_BYTES);
else
::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES);
CDMRSlotType slotType;
slotType.encode(colorCode, DT_IDLE, m_idle);
if (m_trunking) {
slotType.encode(colorCode, DT_TERMINATOR_WITH_LC, m_idle);
slotType.encode(colorCode, DT_CSBK, m_aloha);
} else {
slotType.encode(colorCode, DT_IDLE, m_idle);
}
}
void CDMRTX::resetFifo1()
@@ -418,5 +464,12 @@ uint32_t CDMRTX::getFrameCount()
return m_frameCount;
}
#endif
void CDMRTX::setTrunking(bool trunking)
{
m_trunking = trunking;
if (!trunking)
m_controlChannel = false;
}
#endif
+8 -2
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015,2016,2017,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2026 by Adrian Musceac YO8RZZ
*
* 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
@@ -45,10 +46,12 @@ public:
uint8_t writeData2(const uint8_t* data, uint16_t length);
uint8_t writeShortLC(const uint8_t* data, uint16_t length);
uint8_t writeAloha(const uint8_t* data, uint16_t length);
uint8_t writeAbort(const uint8_t* data, uint16_t length);
void setStart(bool start);
void setCal(bool start);
void setTrunking(bool trunking);
void process();
@@ -62,11 +65,12 @@ public:
void setColorCode(uint8_t colorCode);
private:
CRingBuffer<uint8_t> m_fifo[2U];
CRingBuffer<uint8_t> 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;
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
uint8_t m_aloha[DMR_FRAME_LENGTH_BYTES];
uint8_t m_cachPtr;
uint8_t m_shortLC[12U];
uint8_t m_newShortLC[12U];
@@ -77,6 +81,9 @@ private:
uint32_t m_frameCount;
uint32_t m_abortCount[2U];
bool m_abort[2U];
bool m_controlChannel;
bool m_trunking;
uint8_t m_colorCode;
void createData(uint8_t slotIndex);
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
@@ -87,4 +94,3 @@ private:
#endif
#endif
+340 -45
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2021,2023,2025 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
@@ -27,14 +27,21 @@ const uint16_t FM_TX_BLOCK_SIZE = 100U;
const uint16_t FM_SERIAL_BLOCK_SIZE = 80U;//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;
const uint16_t FM_LINK_EXT_GAP_MS = 60U;// How long a link-mode external-audio underrun is tolerated before it is
// treated as a real end of transmission (see linkStateMachine()).
const uint8_t FS_LISTENING = 0U;
const uint8_t FS_KERCHUNK_RF = 1U;
const uint8_t FS_RELAYING_RF = 2U;
const uint8_t FS_RELAYING_WAIT_RF = 3U;
const uint8_t FS_TIMEOUT_RF = 4U;
const uint8_t FS_TIMEOUT_WAIT_RF = 5U;
const uint8_t FS_KERCHUNK_EXT = 6U;
const uint8_t FS_RELAYING_EXT = 7U;
const uint8_t FS_RELAYING_WAIT_EXT = 8U;
const uint8_t FS_TIMEOUT_EXT = 9U;
const uint8_t FS_TIMEOUT_WAIT_EXT = 10U;
const uint8_t FS_HANG = 11U;
/*
* 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(),
@@ -55,14 +62,21 @@ m_kerchunkTimer(),
m_ackMinTimer(),
m_ackDelayTimer(),
m_hangTimer(),
m_statusTimer(),
m_reverseTimer(),
m_extGapTimer(),
m_needReverse(false),
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_dsFilterStage1(m_filterStage1),//same coefficients, separate state
m_dsFilterStage2(m_filterStage2),
m_dsFilterStage3(m_filterStage3),
m_usFilterStage1(m_filterStage1),//same coefficients, separate state
m_usFilterStage2(m_filterStage2),
m_usFilterStage3(m_filterStage3),
m_blanking(),
m_accessMode(1U),
m_linkMode(false),
m_cosInvert(false),
m_noiseSquelch(false),
m_rfAudioBoost(1U),
@@ -72,15 +86,27 @@ m_extEnabled(false),
m_rxLevel(1),
m_inputRFRB(2401U), // 100ms of audio + 1 sample
m_outputRFRB(2400U), // 100ms of audio
m_inputExtRB()
m_inputExtRB(),
m_rfSignal(false),
m_extSignal(false),
m_rssiAccum(0U),
m_rssiCount(0U)
{
m_statusTimer.setTimeout(1U, 0U);
m_reverseTimer.setTimeout(0U, 150U);
m_extGapTimer.setTimeout(0U, FM_LINK_EXT_GAP_MS);
insertDelay(100U);
}
void CFM::samples(bool cos, q15_t* samples, uint8_t length)
void CFM::samples(bool cos, q15_t* samples, const uint16_t* rssi, uint8_t length)
{
if (m_linkMode)
linkSamples(cos, samples, length);
else
repeaterSamples(cos, samples, rssi, length);
}
void CFM::repeaterSamples(bool cos, q15_t* samples, const uint16_t* rssi, uint8_t length)
{
if (m_cosInvert)
cos = !cos;
@@ -89,6 +115,11 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length)
uint8_t i = 0U;
for (; i < length; i++) {
if (m_state == FS_RELAYING_RF) {
m_rssiAccum += rssi[i];
m_rssiCount++;
}
// ARMv7-M has hardware integer division
q15_t currentRFSample = q15_t((q31_t(samples[i]) << 8) / m_rxLevel);
@@ -97,6 +128,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length)
q15_t currentExtSample;
bool inputExt = m_inputExtRB.getSample(currentExtSample);//always consume the external input data so it does not overflow
currentExtSample = m_usFilterStage3.filter(m_usFilterStage2.filter(m_usFilterStage1.filter(currentExtSample)));
inputExt = inputExt && m_extEnabled;
switch (m_accessMode) {
@@ -178,8 +210,10 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length)
if (m_duplex) {
if (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF || m_state == FS_RELAYING_EXT || m_state == FS_KERCHUNK_EXT) {
currentSample = m_blanking.process(currentSample);
if (m_extEnabled && (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF))
m_downSampler.addSample(currentSample);
if (m_extEnabled && (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF)) {
q15_t dsSample = m_dsFilterStage3.filter(m_dsFilterStage2.filter(m_dsFilterStage1.filter(currentSample)));
m_downSampler.addSample(dsSample);
}
currentSample *= currentBoost;
} else {
@@ -189,9 +223,11 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length)
if (m_state == FS_RELAYING_EXT || m_state == FS_KERCHUNK_EXT) {
currentSample *= currentBoost;
} else {
if (m_extEnabled && (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF))
m_downSampler.addSample(currentSample);
continue;
if (m_extEnabled && (m_state == FS_RELAYING_RF || m_state == FS_KERCHUNK_RF)) {
q15_t dsSample = m_dsFilterStage3.filter(m_dsFilterStage2.filter(m_dsFilterStage1.filter(currentSample)));
m_downSampler.addSample(dsSample);
}
continue;
}
}
@@ -219,6 +255,112 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length)
}
}
void CFM::linkSamples(bool cos, q15_t* samples, uint8_t length)
{
if (m_cosInvert)
cos = !cos;
clock(length);
uint8_t i = 0U;
for (; i < length; i++) {
q15_t currentRFSample = q15_t((q31_t(samples[i]) << 8) / m_rxLevel);
if (m_noiseSquelch)
cos = m_squelch.process(currentRFSample);
// Zero-initialized: getSample() leaves this untouched on underrun, and
// with the ext-gap debounce below, m_extSignal can now stay true for a
// few samples past that point
q15_t currentExtSample = 0;
bool inputExt = m_inputExtRB.getSample(currentExtSample);//always consume the external input data so it does not overflow
currentExtSample = m_usFilterStage3.filter(m_usFilterStage2.filter(m_usFilterStage1.filter(currentExtSample)));
inputExt = inputExt && m_extEnabled;
switch (m_accessMode) {
case 0U:
if (!inputExt && !cos && m_modemState != STATE_FM)
continue;
else
stateMachine(cos, inputExt);
break;
case 1U: {
bool ctcss = m_ctcssRX.process(currentRFSample);
// Delay the audio by 100ms to better match the CTCSS detector output
m_inputRFRB.put(currentRFSample);
m_inputRFRB.get(currentRFSample);
if (!inputExt && !ctcss && m_modemState != STATE_FM) {
// No CTCSS detected, just carry on
continue;
} else if ((inputExt || ctcss) && m_modemState != STATE_FM) {
// We had CTCSS or external input
stateMachine(ctcss, inputExt);
if (m_state == FS_LISTENING)
continue;
} else {
stateMachine(ctcss, inputExt);
}
}
break;
case 2U: {
bool ctcss = m_ctcssRX.process(currentRFSample);
if (!inputExt && !ctcss && m_modemState != STATE_FM) {
// No CTCSS detected, just carry on
continue;
} else if ((inputExt || (ctcss && cos)) && m_modemState != STATE_FM) {
// We had CTCSS or external input
stateMachine(ctcss && cos, inputExt);
if (m_state == FS_LISTENING)
continue;
} else {
stateMachine(ctcss && cos, inputExt);
}
}
break;
default: {
bool ctcss = m_ctcssRX.process(currentRFSample);
if (!inputExt && !ctcss && m_modemState != STATE_FM) {
// No CTCSS detected, just carry on
continue;
} else if ((inputExt || (ctcss && cos)) && m_modemState != STATE_FM) {
// We had CTCSS or external input
stateMachine(ctcss && cos, inputExt);
if (m_state == FS_LISTENING)
continue;
} else {
stateMachine(cos, inputExt);
}
}
break;
}
if (m_modemState != STATE_FM)
continue;
if (m_rfSignal && m_extEnabled) {
q15_t currentSample = m_blanking.process(currentRFSample);
q15_t dsSample = m_dsFilterStage3.filter(m_dsFilterStage2.filter(m_dsFilterStage1.filter(currentSample)));
m_downSampler.addSample(dsSample);
}
if (!m_extSignal)
continue;
q15_t currentSample = currentExtSample * m_extAudioBoost;
currentSample = m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)));
currentSample += m_ctcssTX.getAudio(m_reverseTimer.isRunning());
m_outputRFRB.put(currentSample);
}
}
void CFM::process()
{
uint16_t space = io.getSpace();
@@ -271,7 +413,6 @@ void CFM::reset()
m_ackMinTimer.stop();
m_ackDelayTimer.stop();
m_hangTimer.stop();
m_statusTimer.stop();
m_reverseTimer.stop();
m_ctcssRX.reset();
@@ -284,9 +425,17 @@ void CFM::reset()
m_inputExtRB.reset();
m_downSampler.reset();
m_dsFilterStage1.reset();
m_dsFilterStage2.reset();
m_dsFilterStage3.reset();
m_usFilterStage1.reset();
m_usFilterStage2.reset();
m_usFilterStage3.reset();
m_squelch.reset();
m_needReverse = false;
m_rfSignal = false;
m_extSignal = false;
}
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)
@@ -317,9 +466,10 @@ 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, uint8_t hangTime, uint8_t accessMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, 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 linkMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel)
{
m_accessMode = accessMode;
m_linkMode = linkMode;
m_cosInvert = cosInvert;
m_noiseSquelch = noiseSquelch;
@@ -356,10 +506,14 @@ uint8_t CFM::setExt(const char* ack, uint8_t audioBoost, uint8_t speed, uint16_t
void CFM::stateMachine(bool validRFSignal, bool validExtSignal)
{
if (m_duplex)
duplexStateMachine(validRFSignal, validExtSignal);
else
simplexStateMachine(validRFSignal, validExtSignal);
if (m_linkMode) {
linkStateMachine(validRFSignal, validExtSignal);
} else {
if (m_duplex)
duplexStateMachine(validRFSignal, validExtSignal);
else
simplexStateMachine(validRFSignal, validExtSignal);
}
}
void CFM::simplexStateMachine(bool validRFSignal, bool validExtSignal)
@@ -470,13 +624,8 @@ void CFM::clock(uint8_t length)
m_ackMinTimer.clock(length);
m_ackDelayTimer.clock(length);
m_hangTimer.clock(length);
m_statusTimer.clock(length);
m_reverseTimer.clock(length);
if (m_statusTimer.isRunning() && m_statusTimer.hasExpired()) {
serial.writeFMStatus(m_state);
m_statusTimer.start();
}
m_extGapTimer.clock(length);
}
void CFM::listeningStateDuplex(bool validRFSignal, bool validExtSignal)
@@ -485,12 +634,19 @@ void CFM::listeningStateDuplex(bool validRFSignal, bool validExtSignal)
if (m_kerchunkTimer.getTimeout() > 0U) {
DEBUG1("State to KERCHUNK_RF");
m_state = FS_KERCHUNK_RF;
serial.writeFMStatus(m_state);
m_kerchunkTimer.start();
if (m_callsignAtStart && !m_callsignAtLatch)
sendCallsign();
} else {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
m_rssiAccum = 0U;
m_rssiCount = 0U;
if (m_callsignAtStart)
sendCallsign();
}
@@ -505,20 +661,21 @@ void CFM::listeningStateDuplex(bool validRFSignal, bool validExtSignal)
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;
serial.writeFMStatus(m_state);
m_kerchunkTimer.start();
if (m_callsignAtStart && !m_callsignAtLatch)
sendCallsign();
} else {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
if (m_callsignAtStart)
sendCallsign();
}
@@ -530,9 +687,6 @@ void CFM::listeningStateDuplex(bool validRFSignal, bool validExtSignal)
m_callsignTimer.start();
m_reverseTimer.stop();
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
}
}
@@ -542,26 +696,22 @@ void CFM::listeningStateSimplex(bool validRFSignal, bool validExtSignal)
if (validRFSignal) {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
io.setDecode(true);
io.setADCDetection(true);
m_timeoutTimer.start();
m_reverseTimer.stop();
m_statusTimer.start();
serial.writeFMStatus(m_state);
} else if (validExtSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
insertSilence(50U);
m_timeoutTimer.start();
m_reverseTimer.stop();
m_statusTimer.start();
serial.writeFMStatus(m_state);
}
}
@@ -571,6 +721,11 @@ void CFM::kerchunkRFStateDuplex(bool validSignal)
if (m_kerchunkTimer.hasExpired()) {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
m_rssiAccum = 0U;
m_rssiCount = 0U;
m_kerchunkTimer.stop();
if (m_callsignAtStart && m_callsignAtLatch) {
sendCallsign();
@@ -583,11 +738,12 @@ void CFM::kerchunkRFStateDuplex(bool validSignal)
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_kerchunkTimer.stop();
m_timeoutTimer.stop();
m_ackMinTimer.stop();
m_callsignTimer.stop();
m_statusTimer.stop();
m_needReverse = true;
if (m_extEnabled)
serial.writeFMEOT();
@@ -597,9 +753,21 @@ void CFM::kerchunkRFStateDuplex(bool validSignal)
void CFM::relayingRFStateDuplex(bool validSignal)
{
if (validSignal) {
#if defined(SEND_RSSI_DATA)
if (m_rssiCount >= 24000U) {
uint16_t rssi = m_rssiAccum / m_rssiCount;
serial.writeFMRSSI(rssi);
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
#endif
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT_RF");
m_state = FS_TIMEOUT_RF;
serial.writeFMStatus(m_state);
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_timeoutTone.start();
@@ -613,6 +781,8 @@ void CFM::relayingRFStateDuplex(bool validSignal)
DEBUG1("State to RELAYING_WAIT_RF");
m_state = FS_RELAYING_WAIT_RF;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
if (m_extEnabled)
@@ -631,6 +801,7 @@ void CFM::relayingRFStateSimplex(bool validSignal)
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT_RF");
m_state = FS_TIMEOUT_RF;
serial.writeFMStatus(m_state);
m_timeoutTimer.stop();
@@ -643,6 +814,8 @@ void CFM::relayingRFStateSimplex(bool validSignal)
DEBUG1("State to RELAYING_WAIT_RF");
m_state = FS_RELAYING_WAIT_RF;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
if (m_extEnabled)
@@ -658,11 +831,17 @@ void CFM::relayingRFWaitStateDuplex(bool validSignal)
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
m_rssiAccum = 0U;
m_rssiCount = 0U;
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
serial.writeFMStatus(m_state);
if (m_ackMinTimer.isRunning()) {
if (m_ackMinTimer.hasExpired()) {
@@ -696,11 +875,15 @@ void CFM::relayingRFWaitStateSimplex(bool validSignal)
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
}
@@ -713,6 +896,8 @@ void CFM::kerchunkExtStateDuplex(bool validSignal)
if (m_kerchunkTimer.hasExpired()) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
m_kerchunkTimer.stop();
if (m_callsignAtStart && m_callsignAtLatch) {
sendCallsign();
@@ -722,11 +907,12 @@ void CFM::kerchunkExtStateDuplex(bool validSignal)
} else {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_kerchunkTimer.stop();
m_timeoutTimer.stop();
m_ackMinTimer.stop();
m_callsignTimer.stop();
m_statusTimer.stop();
m_needReverse = true;
}
}
@@ -737,6 +923,8 @@ void CFM::relayingExtStateDuplex(bool validSignal)
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT_EXT");
m_state = FS_TIMEOUT_EXT;
serial.writeFMStatus(m_state);
m_ackMinTimer.stop();
m_timeoutTimer.stop();
m_timeoutTone.start();
@@ -744,6 +932,7 @@ void CFM::relayingExtStateDuplex(bool validSignal)
} else {
DEBUG1("State to RELAYING_WAIT_EXT");
m_state = FS_RELAYING_WAIT_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
}
@@ -759,12 +948,14 @@ void CFM::relayingExtStateSimplex(bool validSignal)
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
DEBUG1("State to TIMEOUT_EXT");
m_state = FS_TIMEOUT_EXT;
serial.writeFMStatus(m_state);
m_timeoutTimer.stop();
}
} else {
DEBUG1("State to RELAYING_WAIT_EXT");
m_state = FS_RELAYING_WAIT_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
}
}
@@ -774,11 +965,13 @@ void CFM::relayingExtWaitStateDuplex(bool validSignal)
if (validSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
serial.writeFMStatus(m_state);
if (m_ackMinTimer.isRunning()) {
if (m_ackMinTimer.hasExpired()) {
@@ -809,11 +1002,14 @@ void CFM::relayingExtWaitStateSimplex(bool validSignal)
if (validSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
m_needReverse = true;
@@ -829,6 +1025,11 @@ void CFM::hangStateDuplex(bool validRFSignal, bool validExtSignal)
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
m_rssiAccum = 0U;
m_rssiCount = 0U;
DEBUG1("Stop ack");
m_rfAck.stop();
m_extAck.stop();
@@ -836,6 +1037,8 @@ void CFM::hangStateDuplex(bool validRFSignal, bool validExtSignal)
} else if (validExtSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
DEBUG1("Stop ack");
m_rfAck.stop();
m_extAck.stop();
@@ -844,8 +1047,9 @@ void CFM::hangStateDuplex(bool validRFSignal, bool validExtSignal)
if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_hangTimer.stop();
m_statusTimer.stop();
if (m_callsignAtEnd)
sendCallsign();
@@ -869,6 +1073,7 @@ void CFM::timeoutRFStateDuplex(bool validSignal)
DEBUG1("State to TIMEOUT_WAIT_RF");
m_state = FS_TIMEOUT_WAIT_RF;
serial.writeFMStatus(m_state);
if (m_callsignAtEnd)
sendCallsign();
@@ -890,6 +1095,7 @@ void CFM::timeoutRFStateSimplex(bool validSignal)
DEBUG1("State to TIMEOUT_WAIT_RF");
m_state = FS_TIMEOUT_WAIT_RF;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
}
@@ -903,11 +1109,14 @@ void CFM::timeoutRFWaitStateDuplex(bool validSignal)
DEBUG1("State to TIMEOUT_RF");
m_state = FS_TIMEOUT_RF;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
serial.writeFMStatus(m_state);
m_timeoutTone.stop();
DEBUG1("Send RF ack");
m_rfAck.start();
@@ -932,11 +1141,14 @@ void CFM::timeoutRFWaitStateSimplex(bool validSignal)
DEBUG1("State to TIMEOUT_RF");
m_state = FS_TIMEOUT_RF;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
}
@@ -948,6 +1160,7 @@ void CFM::timeoutExtStateDuplex(bool validSignal)
if (!validSignal) {
DEBUG1("State to TIMEOUT_WAIT_EXT");
m_state = FS_TIMEOUT_WAIT_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
}
@@ -962,6 +1175,7 @@ void CFM::timeoutExtStateSimplex(bool validSignal)
if (!validSignal) {
DEBUG1("State to TIMEOUT_WAIT_EXT");
m_state = FS_TIMEOUT_WAIT_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.start();
}
}
@@ -971,11 +1185,14 @@ void CFM::timeoutExtWaitStateDuplex(bool validSignal)
if (validSignal) {
DEBUG1("State to TIMEOUT_EXT");
m_state = FS_TIMEOUT_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to HANG");
m_state = FS_HANG;
serial.writeFMStatus(m_state);
m_timeoutTone.stop();
DEBUG1("Send Ext ack");
m_extAck.start();
@@ -997,11 +1214,14 @@ void CFM::timeoutExtWaitStateSimplex(bool validSignal)
if (validSignal) {
DEBUG1("State to TIMEOUT_EXT");
m_state = FS_TIMEOUT_EXT;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
} else {
if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
m_ackDelayTimer.stop();
m_timeoutTimer.stop();
m_needReverse = true;
@@ -1009,6 +1229,81 @@ void CFM::timeoutExtWaitStateSimplex(bool validSignal)
}
}
void CFM::linkStateMachine(bool validRFSignal, bool validExtSignal)
{
if (validRFSignal && !m_rfSignal) {
io.setDecode(true);
io.setADCDetection(true);
if (!m_extSignal) {
DEBUG1("State to RELAYING_RF");
m_state = FS_RELAYING_RF;
serial.writeFMStatus(m_state);
}
m_rfSignal = true;
}
if (validExtSignal && !m_extSignal) {
if (!m_rfSignal) {
DEBUG1("State to RELAYING_EXT");
m_state = FS_RELAYING_EXT;
serial.writeFMStatus(m_state);
}
insertSilence(50U);
m_extSignal = true;
}
if (validExtSignal && m_extGapTimer.isRunning()) {
// Recovered before the gap timer committed to a real loss below --
// m_extSignal was never flipped, so there is nothing else to undo.
m_extGapTimer.stop();
}
if (!validRFSignal && m_rfSignal) {
io.setDecode(false);
io.setADCDetection(false);
if (!m_extSignal) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
}
m_rfSignal = false;
if (m_extEnabled)
serial.writeFMEOT();
}
if (!validExtSignal && m_extSignal) {
// A missed/late sample used to flip m_extSignal false right here, on
// the very first bad sample, with zero debounce -- that is what
// actually keys the transmitter off (IO::process() drops PTT the
// instant the downstream TX buffer runs dry, see IO.cpp). A gap well
// under FM_LINK_EXT_GAP_MS is less offensive than the PTT
// drop-and-rekey it triggers. Give it a short grace period
// mirroring the RELAYING_EXT/RELAYING_WAIT_EXT pattern the
// non-link duplex/simplex paths already use for exactly this
// situation (see relayingExtStateDuplex()/relayingExtWaitStateDuplex()).
if (!m_extGapTimer.isRunning()) {
m_extGapTimer.start();
} else if (m_extGapTimer.hasExpired()) {
if (!m_rfSignal) {
DEBUG1("State to LISTENING");
m_state = FS_LISTENING;
serial.writeFMStatus(m_state);
}
m_needReverse = true;
m_extSignal = false;
m_extGapTimer.stop();
}
}
}
void CFM::sendCallsign()
{
if (m_holdoffTimer.isRunning()) {
@@ -1035,7 +1330,7 @@ uint8_t CFM::getSpace() const
return m_inputExtRB.getSpace() / FM_SERIAL_BLOCK_SIZE_BYTES;
}
uint8_t CFM::writeData(const uint8_t* data, uint8_t length)
uint8_t CFM::writeData(const uint8_t* data, uint16_t length)
{
//todo check if length is a multiple of 3
m_inputExtRB.addData(data, length);
+22 -21
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2021,2023,2025 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
@@ -35,27 +35,12 @@
#include "FMUpSampler.h"
#include "FMNoiseSquelch.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_HANG
};
class CFM {
public:
CFM();
void samples(bool cos, q15_t* samples, uint8_t length);
void samples(bool cos, q15_t* samples, const uint16_t* rssi, uint8_t length);
void process();
@@ -63,12 +48,12 @@ 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, uint8_t hangTime, uint8_t accessMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel);
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 linkMode, bool cosInvert, bool noiseSquelch, uint8_t squelchHighThreshold, uint8_t squelchLowThreshold, 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 writeData(const uint8_t* data, uint16_t length);
private:
CFMKeyer m_callsign;
@@ -78,7 +63,7 @@ private:
CFMCTCSSTX m_ctcssTX;
CFMNoiseSquelch m_squelch;
CFMTimeout m_timeoutTone;
FM_STATE m_state;
uint8_t m_state;
bool m_callsignAtStart;
bool m_callsignAtEnd;
bool m_callsignAtLatch;
@@ -89,14 +74,21 @@ private:
CFMTimer m_ackMinTimer;
CFMTimer m_ackDelayTimer;
CFMTimer m_hangTimer;
CFMTimer m_statusTimer;
CFMTimer m_reverseTimer;
CFMTimer m_extGapTimer; // debounce for link-mode ext-audio underrun, see linkStateMachine()
bool m_needReverse;
CFMDirectFormI m_filterStage1;
CFMDirectFormI m_filterStage2;
CFMDirectFormI m_filterStage3;
CFMDirectFormI m_dsFilterStage1; // anti-alias filter for the downsampler, own state
CFMDirectFormI m_dsFilterStage2;
CFMDirectFormI m_dsFilterStage3;
CFMDirectFormI m_usFilterStage1; // reconstruction filter for the upsampler, own state
CFMDirectFormI m_usFilterStage2;
CFMDirectFormI m_usFilterStage3;
CFMBlanking m_blanking;
uint8_t m_accessMode;
bool m_linkMode;
bool m_cosInvert;
bool m_noiseSquelch;
q15_t m_rfAudioBoost;
@@ -107,9 +99,16 @@ private:
CRingBuffer<q15_t> m_inputRFRB;
CRingBuffer<q15_t> m_outputRFRB;
CFMUpSampler m_inputExtRB;
bool m_rfSignal;
bool m_extSignal;
uint32_t m_rssiAccum;
uint16_t m_rssiCount;
void stateMachine(bool validRFSignal, bool validExtSignal);
void repeaterSamples(bool cos, q15_t* samples, const uint16_t* rssi, uint8_t length);
void linkSamples(bool cos, q15_t* samples, uint8_t length);
void duplexStateMachine(bool validRFSignal, bool validExtSignal);
void listeningStateDuplex(bool validRFSignal, bool validExtSignal);
void kerchunkRFStateDuplex(bool validSignal);
@@ -135,6 +134,8 @@ private:
void timeoutExtStateSimplex(bool validSignal);
void timeoutExtWaitStateSimplex(bool validSignal);
void linkStateMachine(bool validRFSignal, bool validExtSignal);
void clock(uint8_t length);
void sendCallsign();
+6 -11
View File
@@ -42,8 +42,7 @@ class CFMDirectFormI
public:
// convenience function which takes the a0 argument but ignores it!
CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2,
const q31_t, const q31_t a1, const q31_t a2)
CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2, const q31_t, const q31_t a1, const q31_t a2)
{
// FIR coefficients
c_b0 = b0;
@@ -57,21 +56,21 @@ public:
CFMDirectFormI(const CFMDirectFormI &my)
{
// delay line
// delay line
m_x2 = my.m_x2; // x[n-2]
m_y2 = my.m_y2; // y[n-2]
m_x1 = my.m_x1; // x[n-1]
m_y1 = my.m_y1; // y[n-1]
// coefficients
// coefficients
c_b0 = my.c_b0;
c_b1 = my.c_b1;
c_b2 = my.c_b2; // FIR
c_a1 = my.c_a1;
c_a2 = my.c_a2; // IIR
}
}
void reset ()
void reset()
{
m_x1 = 0;
m_x2 = 0;
@@ -83,11 +82,7 @@ public:
inline q15_t filter(const q15_t in)
{
// calculate the output
register q31_t out_upscaled = c_b0 * in
+ c_b1 * m_x1
+ c_b2 * m_x2
- c_a1 * m_y1
- c_a2 * m_y2;
q31_t out_upscaled = c_b0 * in + c_b1 * m_x1 + c_b2 * m_x2 - c_a1 * m_y1 - c_a2 * m_y2;
q15_t out = __SSAT(out_upscaled >> 15, 15);
+3
View File
@@ -34,6 +34,7 @@ m_sampleIndex(0U)
void CFMDownSampler::addSample(q15_t sample)
{
sample = __SSAT(sample, 12);//clamp before packing to 12 bits
uint32_t usample = uint32_t(int32_t(sample) + 2048);
//only take one of three samples
switch(m_sampleIndex){
@@ -74,6 +75,8 @@ uint16_t CFMDownSampler::getData()
void CFMDownSampler::reset()
{
m_sampleIndex = 0U;
m_samplePack = 0U;
m_ringBuffer.reset();
}
#endif
+4 -1
View File
@@ -36,7 +36,8 @@ m_count(0U),
m_q0(0),
m_q1(0),
m_state(false),
m_validCount(0U)
m_validCount(0U),
m_invalidCount(0U)
{
}
@@ -127,6 +128,8 @@ void CFMNoiseSquelch::reset()
m_q1 = 0;
m_state = false;
m_count = 0U;
m_validCount = 0U;
m_invalidCount = 0U;
}
#endif
+3 -24
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2025 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
@@ -23,15 +23,12 @@
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#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)
#if defined(__SAM3X8E__)
#define ARM_MATH_CM3
#elif defined(STM32F7XX)
#define ARM_MATH_CM7
@@ -51,9 +48,7 @@ enum MMDVM_STATE {
STATE_P25 = 4,
STATE_NXDN = 5,
STATE_POCSAG = 6,
STATE_M17 = 7,
STATE_FM = 10,
STATE_AX25 = 11,
// Dummy states start at 90
STATE_NXDNCAL1K = 91,
@@ -87,8 +82,6 @@ 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"
@@ -101,8 +94,6 @@ enum MMDVM_STATE {
#include "CalPOCSAG.h"
#include "CalRSSI.h"
#include "CWIdTX.h"
#include "AX25RX.h"
#include "AX25TX.h"
#include "Debug.h"
#include "IO.h"
#include "FM.h"
@@ -116,7 +107,7 @@ const uint16_t RX_BLOCK_SIZE = 2U;
const uint16_t TX_RINGBUFFER_SIZE = 500U;
const uint16_t RX_RINGBUFFER_SIZE = 600U;
#if defined(STM32F105xC) || defined(__MK20DX256__)
#if defined(__MK20DX256__)
const uint16_t TX_BUFFER_LEN = 2000U;
#else
const uint16_t TX_BUFFER_LEN = 4000U;
@@ -130,9 +121,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_ax25Enable;
extern bool m_duplex;
@@ -185,21 +174,11 @@ extern CPOCSAGTX pocsagTX;
extern CCalPOCSAG calPOCSAG;
#endif
#if defined(MODE_M17)
extern CM17RX m17RX;
extern CM17TX m17TX;
#endif
#if defined(MODE_FM)
extern CFM fm;
extern CCalFM calFM;
#endif
#if defined(MODE_AX25)
extern CAX25RX ax25RX;
extern CAX25TX ax25TX;
#endif
extern CCalRSSI calRSSI;
extern CCWIdTX cwIdTX;
+35 -116
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2023,2025,2026 by Jonathan Naylor G4KLX
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
* Copyright (C) 2016 by Colin Durbridge G4EML
*
@@ -36,14 +36,6 @@ static q15_t RRC_0_2_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 147
const uint16_t RRC_0_2_FILTER_LEN = 42U;
#endif
#if defined(MODE_M17)
// 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;
#endif
#if defined(MODE_NXDN)
#if defined(USE_NXDN_BOXCAR)
// One symbol boxcar filter
@@ -91,9 +83,13 @@ m_dcState(),
m_gaussianFilter(),
m_gaussianState(),
#endif
#if defined(MODE_DMR) || defined(MODE_YSF)
m_rrc02Filter(),
m_rrc02State(),
#if defined(MODE_DMR)
m_rrc02Filter1(),
m_rrc02State1(),
#endif
#if defined(MODE_YSF)
m_rrc02Filter2(),
m_rrc02State2(),
#endif
#if defined(MODE_P25)
m_boxcar5Filter(),
@@ -110,10 +106,6 @@ m_nxdnState(),
m_nxdnISincState(),
#endif
#endif
#if defined(MODE_M17)
m_rrc05Filter(),
m_rrc05State(),
#endif
m_pttInvert(false),
m_rxLevel(128 * 128),
m_cwIdTXLevel(128 * 128),
@@ -122,10 +114,8 @@ 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_ax25TXLevel(128 * 128),
m_rxDCOffset(DC_OFFSET),
m_txDCOffset(DC_OFFSET),
m_useCOSAsLockout(false),
@@ -138,7 +128,7 @@ m_watchdog(0U),
m_lockout(false)
{
#if defined(USE_DCBLOCKER)
::memset(m_dcState, 0x00U, 4U * sizeof(q31_t));
::memset(m_dcState, 0x00U, 4U * sizeof(q31_t));
m_dcFilter.numStages = DC_FILTER_STAGES;
m_dcFilter.pState = m_dcState;
m_dcFilter.pCoeffs = DC_FILTER;
@@ -152,11 +142,18 @@ m_lockout(false)
m_gaussianFilter.pCoeffs = GAUSSIAN_0_5_FILTER;
#endif
#if defined(MODE_DMR) || defined(MODE_YSF)
::memset(m_rrc02State, 0x00U, 70U * sizeof(q15_t));
m_rrc02Filter.numTaps = RRC_0_2_FILTER_LEN;
m_rrc02Filter.pState = m_rrc02State;
m_rrc02Filter.pCoeffs = RRC_0_2_FILTER;
#if defined(MODE_DMR)
::memset(m_rrc02State1, 0x00U, 70U * sizeof(q15_t));
m_rrc02Filter1.numTaps = RRC_0_2_FILTER_LEN;
m_rrc02Filter1.pState = m_rrc02State1;
m_rrc02Filter1.pCoeffs = RRC_0_2_FILTER;
#endif
#if defined(MODE_YSF)
::memset(m_rrc02State2, 0x00U, 70U * sizeof(q15_t));
m_rrc02Filter2.numTaps = RRC_0_2_FILTER_LEN;
m_rrc02Filter2.pState = m_rrc02State2;
m_rrc02Filter2.pCoeffs = RRC_0_2_FILTER;
#endif
#if defined(MODE_P25)
@@ -186,13 +183,6 @@ m_lockout(false)
#endif
#endif
#if defined(MODE_M17)
::memset(m_rrc05State, 0x00U, 70U * sizeof(q15_t));
m_rrc05Filter.numTaps = RRC_0_5_FILTER_LEN;
m_rrc05Filter.pState = m_rrc05State;
m_rrc05Filter.pCoeffs = RRC_0_5_FILTER;
#endif
initInt();
selfTest();
@@ -216,9 +206,6 @@ void CIO::selfTest()
#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
@@ -237,9 +224,6 @@ void CIO::selfTest()
#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
@@ -262,11 +246,6 @@ 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);
@@ -285,11 +264,6 @@ 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);
@@ -327,7 +301,7 @@ 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_M17 || 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_POCSAG) {
#if defined(MODE_DMR)
if (m_modemState == STATE_DMR && m_tx)
dmrTX.setStart(false);
@@ -458,7 +432,7 @@ void CIO::process()
#if defined(MODE_DMR)
if (m_dmrEnable) {
q15_t DMRVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_rrc02Filter, samples, DMRVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter1, samples, DMRVals, RX_BLOCK_SIZE);
if (m_duplex)
dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE);
@@ -471,43 +445,21 @@ void CIO::process()
if (m_ysfEnable) {
q15_t YSFVals[RX_BLOCK_SIZE];
#if defined(USE_DCBLOCKER)
::arm_fir_fast_q15(&m_rrc02Filter, dcSamples, YSFVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter2, dcSamples, YSFVals, RX_BLOCK_SIZE);
#else
::arm_fir_fast_q15(&m_rrc02Filter, samples, YSFVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter2, samples, YSFVals, RX_BLOCK_SIZE);
#endif
ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE);
}
#endif
#if defined(MODE_M17)
if (m_m17Enable) {
q15_t RRCVals[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);
}
#endif
#if defined(MODE_FM)
if (m_fmEnable) {
bool cos = getCOSInt();
#if defined(USE_DCBLOCKER)
fm.samples(cos, dcSamples, RX_BLOCK_SIZE);
fm.samples(cos, dcSamples, rssi, RX_BLOCK_SIZE);
#else
fm.samples(cos, samples, RX_BLOCK_SIZE);
#endif
}
#endif
#if defined(MODE_FM) && defined(MODE_AX25)
if (m_ax25Enable) {
#if defined(USE_DCBLOCKER)
ax25RX.samples(dcSamples, RX_BLOCK_SIZE);
#else
ax25RX.samples(samples, RX_BLOCK_SIZE);
fm.samples(cos, samples, rssi, RX_BLOCK_SIZE);
#endif
}
#endif
@@ -531,7 +483,7 @@ void CIO::process()
else if (m_modemState == STATE_DMR) {
if (m_dmrEnable) {
q15_t DMRVals[RX_BLOCK_SIZE];
::arm_fir_fast_q15(&m_rrc02Filter, samples, DMRVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter1, samples, DMRVals, RX_BLOCK_SIZE);
if (m_duplex) {
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
@@ -551,9 +503,9 @@ void CIO::process()
if (m_ysfEnable) {
q15_t YSFVals[RX_BLOCK_SIZE];
#if defined(USE_DCBLOCKER)
::arm_fir_fast_q15(&m_rrc02Filter, dcSamples, YSFVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter2, dcSamples, YSFVals, RX_BLOCK_SIZE);
#else
::arm_fir_fast_q15(&m_rrc02Filter, samples, YSFVals, RX_BLOCK_SIZE);
::arm_fir_fast_q15(&m_rrc02Filter2, samples, YSFVals, RX_BLOCK_SIZE);
#endif
ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE);
}
@@ -598,37 +550,13 @@ void CIO::process()
}
#endif
#if defined(MODE_M17)
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);
}
}
#endif
#if defined(MODE_FM)
else if (m_modemState == STATE_FM) {
bool cos = getCOSInt();
#if defined(USE_DCBLOCKER)
fm.samples(cos, dcSamples, RX_BLOCK_SIZE);
#if defined(MODE_AX25)
if (m_ax25Enable)
ax25RX.samples(dcSamples, RX_BLOCK_SIZE);
#endif
fm.samples(cos, dcSamples, rssi, RX_BLOCK_SIZE);
#else
fm.samples(cos, samples, RX_BLOCK_SIZE);
#if defined(MODE_AX25)
if (m_ax25Enable)
ax25RX.samples(samples, RX_BLOCK_SIZE);
#endif
fm.samples(cos, samples, rssi, RX_BLOCK_SIZE);
#endif
}
#endif
@@ -680,18 +608,12 @@ 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;
case STATE_FM:
txLevel = m_fmTXLevel;
break;
case STATE_AX25:
txLevel = m_ax25TXLevel;
break;
default:
txLevel = m_cwIdTXLevel;
break;
@@ -743,9 +665,9 @@ void CIO::setMode(MMDVM_STATE state)
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;
default: break;
}
switch (state) {
@@ -754,16 +676,16 @@ void CIO::setMode(MMDVM_STATE state)
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;
default: 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 m17TXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, uint8_t ax25TXLevel, int16_t txDCOffset, int16_t rxDCOffset, bool useCOSAsLockout)
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, bool useCOSAsLockout)
{
m_pttInvert = pttInvert;
@@ -774,10 +696,8 @@ 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_ax25TXLevel = q15_t(ax25TXLevel * 128);
m_rxDCOffset = DC_OFFSET + rxDCOffset;
m_txDCOffset = DC_OFFSET + txDCOffset;
@@ -793,7 +713,6 @@ 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;
}
}
+11 -13
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2025 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
@@ -46,7 +46,7 @@ public:
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 m17TXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, uint8_t ax25TXLevel, int16_t txDCOffset, int16_t rxDCOffset, bool useCOSAsLockout);
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, bool useCOSAsLockout);
void getOverflow(bool& adcOverflow, bool& dacOverflow);
@@ -81,9 +81,14 @@ private:
q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
#endif
#if defined(MODE_DMR) || defined(MODE_YSF)
arm_fir_instance_q15 m_rrc02Filter;
q15_t m_rrc02State[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
#if defined(MODE_DMR)
arm_fir_instance_q15 m_rrc02Filter1;
q15_t m_rrc02State1[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
#endif
#if defined(MODE_YSF)
arm_fir_instance_q15 m_rrc02Filter2;
q15_t m_rrc02State2[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
#endif
#if defined(MODE_P25)
@@ -103,11 +108,6 @@ private:
#endif
#endif
#if defined(MODE_M17)
arm_fir_instance_q15 m_rrc05Filter;
q15_t m_rrc05State[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
#endif
bool m_pttInvert;
q15_t m_rxLevel;
q15_t m_cwIdTXLevel;
@@ -116,10 +116,8 @@ 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;
q15_t m_ax25TXLevel;
uint16_t m_rxDCOffset;
uint16_t m_txDCOffset;
@@ -154,10 +152,10 @@ 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);
};
#endif
+2 -15
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
* Copyright (C) 2016 by Colin Durbridge G4EML
*
@@ -107,13 +107,10 @@ 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
#if !defined(USE_ALTERNATE_POCSAG_LEDS)
#if !defined(USE_ALTERNATE_FM_LEDS)
pinMode(PIN_FM, OUTPUT);
#endif
#endif
@@ -256,16 +253,6 @@ 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)
+8 -39
View File
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018,2020,2023,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2019,2020 by BG5HHP
*
* This program is free software; you can redistribute it and/or modify
@@ -106,14 +106,6 @@ 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);
@@ -165,14 +157,6 @@ 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);
@@ -365,10 +349,16 @@ void CIO::interrupt()
#endif
}
// trigger next ADC1
// Trigger next ADC1
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
ADC_SoftwareStartConv(ADC1);
#if defined(SEND_RSSI_DATA)
// Trigger next ADC2
ADC_ClearFlag(ADC2, ADC_FLAG_EOC);
ADC_SoftwareStartConv(ADC2);
#endif
m_rxBuffer.put(sample);
m_rssiBuffer.put(rawRSSI);
@@ -456,27 +446,6 @@ 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)
-500
View File
@@ -1,500 +0,0 @@
/*
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
* Copyright (C) 2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Wojciech Krutnik N0CALL
*
* 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 "IO.h"
#if defined(STM32F1)
#if defined(STM32F1_POG)
/*
Pin definitions for STM32F1 POG Board:
PTT PB12 output
COSLED PB5 output
LED PB4 output
COS PB13 input/PD
DSTAR PB7 output
DMR PB6 output
YSF PB8 output
P25 PB9 output
NXDN PB10 output
POCSAG PB11 output
RX PB0 analog input (ADC1_8)
RSSI PB1 analog input (ADC2_9)
TX PA4 analog output (DAC_OUT1)
EXT_CLK PA15 input (AF: TIM2_CH1_ETR)
USART1_TXD PA9 output (AF)
USART1_RXD PA10 input (AF)
*/
#define PIN_PTT 12
#define PORT_PTT GPIOB
#define BB_PTT *((bitband_t)BITBAND_PERIPH(&PORT_PTT->ODR, PIN_PTT))
#define PIN_COSLED 5
#define PORT_COSLED GPIOB
#define BB_COSLED *((bitband_t)BITBAND_PERIPH(&PORT_COSLED->ODR, PIN_COSLED))
#define PIN_LED 4
#define PORT_LED GPIOB
#define BB_LED *((bitband_t)BITBAND_PERIPH(&PORT_LED->ODR, PIN_LED))
#define PIN_COS 13
#define PORT_COS GPIOB
#define BB_COS *((bitband_t)BITBAND_PERIPH(&PORT_COS->IDR, PIN_COS))
#define PIN_DSTAR 7
#define PORT_DSTAR GPIOB
#define BB_DSTAR *((bitband_t)BITBAND_PERIPH(&PORT_DSTAR->ODR, PIN_DSTAR))
#define PIN_DMR 6
#define PORT_DMR GPIOB
#define BB_DMR *((bitband_t)BITBAND_PERIPH(&PORT_DMR->ODR, PIN_DMR))
#define PIN_YSF 8
#define PORT_YSF GPIOB
#define BB_YSF *((bitband_t)BITBAND_PERIPH(&PORT_YSF->ODR, PIN_YSF))
#define PIN_P25 9
#define PORT_P25 GPIOB
#define BB_P25 *((bitband_t)BITBAND_PERIPH(&PORT_P25->ODR, PIN_P25))
#define PIN_NXDN 10
#define PORT_NXDN GPIOB
#define BB_NXDN *((bitband_t)BITBAND_PERIPH(&PORT_NXDN->ODR, PIN_NXDN))
#define PIN_POCSAG 11
#define PORT_POCSAG GPIOB
#define BB_POCSAG *((bitband_t)BITBAND_PERIPH(&PORT_POCSAG->ODR, PIN_POCSAG))
#define PIN_RX 0
#define PIN_RX_ADC_CH 8
#define PORT_RX GPIOB
#define PIN_RSSI 1
#define PIN_RSSI_ADC_CH 9
#define PORT_RSSI GPIOB
#define PIN_TX 4
#define PIN_TX_DAC_CH 1
#define PORT_TX GPIOA
#define PIN_EXT_CLK 15
#define SRC_EXT_CLK 15
#define PORT_EXT_CLK GPIOA
#define PIN_USART1_TXD 9
#define PORT_USART1_TXD GPIOA
#define PIN_USART1_RXD 10
#define PORT_USART1_RXD GPIOA
#else // defined(STM32F1_POG)
#error "Either STM32F1_POG, or sth need to be defined"
#endif // defined(STM32F1_POG)
const uint16_t DC_OFFSET = 2048U;
// Sampling frequency
#define SAMP_FREQ 24000
extern "C" {
void TIM2_IRQHandler() {
if ((TIM2->SR & TIM_SR_UIF) == TIM_SR_UIF) {
TIM2->SR = ~TIM_SR_UIF; // clear UI flag
io.interrupt();
}
}
}
void delay(uint32_t cnt)
{
while(cnt--)
asm("nop");
}
// source: http://www.freddiechopin.info/
void GPIOConfigPin(GPIO_TypeDef *port_ptr, uint32_t pin, uint32_t mode_cnf_value)
{
volatile uint32_t *cr_ptr;
uint32_t cr_value;
cr_ptr = &port_ptr->CRL; // configuration of pins [0,7] is in CRL
if (pin >= 8) // is pin in [8; 15]?
{ // configuration of pins [8,15] is in CRH
cr_ptr++; // advance to next struct element CRL -> CRH
pin -= 8; // crop the pin number
}
cr_value = *cr_ptr; // localize the CRL / CRH value
cr_value &= ~(0xF << (pin * 4)); // clear the MODE and CNF fields (now that pin is an analog input)
cr_value |= (mode_cnf_value << (pin * 4)); // save new MODE and CNF value for desired pin
*cr_ptr = cr_value; // save localized value to CRL / CRL
}
#if defined(STM32F1_POG)
void FancyLEDEffect()
{
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<7; i++){
*foo[i] = 0x00;
}
*((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01;
delay(SystemCoreClock/1000*20);
*((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;
*foo[0] = 0x01;
for(int i=1; i<7; i++){
delay(SystemCoreClock/1000*10);
*foo[i-1] = 0x00;
*foo[i] = 0x01;
}
for(int i=5; i>=0; i--){
delay(SystemCoreClock/1000*10);
*foo[i+1] = 0x00;
*foo[i] = 0x01;
}
delay(SystemCoreClock/1000*10);
*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;
GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1|GPIO_CRL_CNF0_1);
delay(SystemCoreClock/1000*50);
}
#endif
static inline void GPIOInit()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN
| RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN
| RCC_APB2ENR_AFIOEN; // enable all GPIOs
// set all ports to input with pull-down
GPIOA->CRL = 0x88888888;
GPIOA->CRH = 0x88888888;
GPIOA->ODR = 0;
GPIOB->CRL = 0x88888888;
GPIOB->CRH = 0x88888888;
GPIOB->ODR = 0;
GPIOC->CRL = 0x88888888;
GPIOC->CRH = 0x88888888;
GPIOC->ODR = 0;
GPIOD->CRL = 0x88888888;
GPIOD->CRH = 0x88888888;
GPIOD->ODR = 0;
// configure ports
GPIOConfigPin(PORT_PTT, PIN_PTT, GPIO_CRL_MODE0_1);
GPIOConfigPin(PORT_COSLED, PIN_COSLED, GPIO_CRL_MODE0_1);
GPIOConfigPin(PORT_LED, PIN_LED, GPIO_CRL_MODE0_1);
GPIOConfigPin(PORT_COS, PIN_COS, GPIO_CRL_CNF0_1);
GPIOConfigPin(PORT_DSTAR, PIN_DSTAR, GPIO_CRL_MODE0_1);
GPIOConfigPin(PORT_DMR, PIN_DMR, GPIO_CRL_MODE0_1);
GPIOConfigPin(PORT_YSF, PIN_YSF, GPIO_CRL_MODE0_1);
GPIOConfigPin(PORT_P25, PIN_P25, GPIO_CRL_MODE0_1);
#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
GPIOConfigPin(PORT_RX, PIN_RX, 0);
#if defined(SEND_RSSI_DATA)
GPIOConfigPin(PORT_RSSI, PIN_RSSI, 0);
#endif
GPIOConfigPin(PORT_TX, PIN_TX, 0);
#if defined(EXTERNAL_OSC)
#if defined(STM32F1_POG)
GPIOConfigPin(PORT_EXT_CLK, PIN_EXT_CLK, GPIO_CRL_CNF0_0);
AFIO->MAPR = (AFIO->MAPR & ~AFIO_MAPR_TIM2_REMAP) | AFIO_MAPR_TIM2_REMAP_0;
#endif
#endif
GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1|GPIO_CRL_CNF0_1);
GPIOConfigPin(PORT_USART1_RXD, PIN_USART1_RXD, GPIO_CRL_CNF0_0);
AFIO->MAPR = (AFIO->MAPR & ~AFIO_MAPR_SWJ_CFG) | AFIO_MAPR_SWJ_CFG_1;
}
static inline void ADCInit()
{
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_ADCPRE_Msk)
| RCC_CFGR_ADCPRE_DIV6; // ADC clock divided by 6 (72MHz/6 = 12MHz)
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
#if defined(SEND_RSSI_DATA)
RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
#endif
// Init ADCs in dual mode (RSSI)
#if defined(SEND_RSSI_DATA)
ADC1->CR1 = ADC_CR1_DUALMOD_1|ADC_CR1_DUALMOD_2; // Regular simultaneous mode
#endif
// Set sampling time to 7.5 cycles
if (PIN_RX_ADC_CH < 10) {
ADC1->SMPR2 = ADC_SMPR2_SMP0_0 << (3*PIN_RX_ADC_CH);
} else {
ADC1->SMPR1 = ADC_SMPR1_SMP10_0 << (3*PIN_RX_ADC_CH);
}
#if defined(SEND_RSSI_DATA)
if (PIN_RSSI_ADC_CH < 10) {
ADC2->SMPR2 = ADC_SMPR2_SMP0_0 << (3*PIN_RSSI_ADC_CH);
} else {
ADC2->SMPR1 = ADC_SMPR1_SMP10_0 << (3*PIN_RSSI_ADC_CH);
}
#endif
// Set conversion channel (single conversion)
ADC1->SQR3 = PIN_RX_ADC_CH;
#if defined(SEND_RSSI_DATA)
ADC2->SQR3 = PIN_RSSI_ADC_CH;
#endif
// Enable ADC
ADC1->CR2 |= ADC_CR2_ADON;
#if defined(SEND_RSSI_DATA)
// Enable ADC2
ADC2->CR2 |= ADC_CR2_ADON;
#endif
// Start calibration
delay(6*2);
ADC1->CR2 |= ADC_CR2_CAL;
while((ADC1->CR2 & ADC_CR2_CAL) == ADC_CR2_CAL)
;
#if defined(SEND_RSSI_DATA)
ADC2->CR2 |= ADC_CR2_CAL;
while((ADC2->CR2 & ADC_CR2_CAL) == ADC_CR2_CAL)
;
#endif
// Trigger first conversion
ADC1->CR2 |= ADC_CR2_ADON;
#if defined(SEND_RSSI_DATA)
ADC2->CR2 |= ADC_CR2_ADON;
#endif
}
static inline void DACInit()
{
RCC->APB1ENR |= RCC_APB1ENR_DACEN;
DAC->CR = DAC_CR_EN1;
}
static inline void TimerInit()
{
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
#if defined(EXTERNAL_OSC)
// Enable external clock, prescaler /4
TIM2->SMCR = TIM_SMCR_ECE | TIM_SMCR_ETPS_1;
#endif
// TIM2 output frequency
TIM2->PSC = 0;
#if defined(EXTERNAL_OSC)
// TimerCount = ExternalOsc
// /4 (external clock prescaler)
// /SAMP_FREQ
TIM2->ARR = (uint16_t) ((EXTERNAL_OSC/(4*SAMP_FREQ)) - 1);
#else
TIM2->ARR = (uint16_t) ((SystemCoreClock/(SAMP_FREQ)) - 1);
#endif
// Enable TIse 1%-tolerance comM2 interrupt
TIM2->DIER = TIM_DIER_UIE;
NVIC_EnableIRQ(TIM2_IRQn);
// Enable TIM2
TIM2->CR1 |= TIM_CR1_CEN;
}
void CIO::initInt()
{
GPIOInit();
ADCInit();
DACInit();
#if defined(STM32F1_POG)
FancyLEDEffect();
#endif
}
void CIO::startInt()
{
TimerInit();
BB_COSLED = 0;
BB_LED = 1;
}
void CIO::interrupt()
{
TSample sample = {DC_OFFSET, MARK_NONE};
#if defined(SEND_RSSI_DATA)
uint16_t rawRSSI = 0U;
#endif
bitband_t eoc = (bitband_t)BITBAND_PERIPH(&ADC1->SR, ADC_SR_EOS_Pos);
bitband_t adon = (bitband_t)BITBAND_PERIPH(&ADC1->CR2, ADC_CR2_ADON_Pos);
#if defined(SEND_RSSI_DATA)
bitband_t rssi_adon = (bitband_t)BITBAND_PERIPH(&ADC2->CR2, ADC_CR2_ADON_Pos);
#endif
if (*eoc) {
// trigger next conversion
*adon = 1;
#if defined(SEND_RSSI_DATA)
*rssi_adon = 1;
#endif
m_txBuffer.get(sample);
DAC->DHR12R1 = sample.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);
#if defined(SEND_RSSI_DATA)
rawRSSI = ADC2->DR;
m_rssiBuffer.put(rawRSSI);
#endif
m_watchdog++;
}
}
bool CIO::getCOSInt()
{
return BB_COS;
}
void CIO::setLEDInt(bool on)
{
BB_LED = !!on;
}
void CIO::setPTTInt(bool on)
{
BB_PTT = !!on;
}
void CIO::setCOSInt(bool on)
{
BB_COSLED = !!on;
}
void CIO::setDStarInt(bool on)
{
BB_DSTAR = !!on;
}
void CIO::setDMRInt(bool on)
{
BB_DMR = !!on;
}
void CIO::setYSFInt(bool on)
{
BB_YSF = !!on;
}
void CIO::setP25Int(bool on)
{
BB_P25 = !!on;
}
void CIO::setNXDNInt(bool on)
{
#if defined(USE_ALTERNATE_NXDN_LEDS)
BB_YSF = !!on;
BB_P25 = !!on;
#else
BB_NXDN = !!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)
BB_DSTAR = !!on;
BB_DMR = !!on;
#else
BB_POCSAG = !!on;
#endif
}
void CIO::setFMInt(bool on)
{
#if defined(USE_ALTERNATE_FM_LEDS)
BB_DSTAR = !!on;
BB_YSF = !!on;
#endif
}
void CIO::delayInt(unsigned int dly)
{
delay(dly);
}
uint8_t CIO::getCPU() const
{
return 2U;
}
void CIO::getUDID(uint8_t* buffer)
{
#if defined(STM32F105xC)
::memcpy(buffer, (void *)0x1FFFF7E8, 12U);
#endif
}
#endif
+1 -14
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2020,2025 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
@@ -75,9 +75,6 @@ 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
@@ -240,16 +237,6 @@ 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)
-68
View File
@@ -1,68 +0,0 @@
/*
* 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_LINK_SETUP_SYNC_BYTES[] = {0x55U, 0xF7U};
const uint8_t M17_STREAM_SYNC_BYTES[] = {0xFFU, 0x5DU};
const uint8_t M17_PACKET_SYNC_BYTES[] = {0x75U, 0xFFU};
const uint8_t M17_SYNC_BYTES_LENGTH = 2U;
const uint16_t M17_LINK_SETUP_SYNC_BITS = 0x55F7U;
const uint16_t M17_STREAM_SYNC_BITS = 0xFF5DU;
const uint16_t M17_PACKET_SYNC_BITS = 0x75FFU;
// 5 5 F 7
// 01 01 01 01 11 11 01 11
// +3 +3 +3 +3 -3 -3 +3 -3
const int8_t M17_LINK_SETUP_SYNC_SYMBOLS_VALUES[] = {+3, +3, +3, +3, -3, -3, +3, -3};
const uint8_t M17_LINK_SETUP_SYNC_SYMBOLS = 0xF2U;
// F F 5 D
// 11 11 11 11 01 01 11 01
// -3 -3 -3 -3 +3 +3 -3 +3
const int8_t M17_STREAM_SYNC_SYMBOLS_VALUES[] = {-3, -3, -3, -3, +3, +3, -3, +3};
const uint8_t M17_STREAM_SYNC_SYMBOLS = 0x0DU;
// 7 5 F F
// 01 11 01 01 11 11 11 11
// +3 -3 +3 +3 -3 -3 -3 -3
const int8_t M17_PACKET_SYNC_SYMBOLS_VALUES[] = {+3, -3, +3, +3, -3, -3, -3, -3};
const uint8_t M17_PACKET_SYNC_SYMBOLS = 0xB0U;
#endif
-498
View File
@@ -1,498 +0,0 @@
/*
* Copyright (C) 2009-2017,2020,2021 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"
#if defined(MODE_M17)
#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 = 0U;
const uint8_t MAX_SYNC_BIT_RUN_ERRS = 2U;
const uint8_t MAX_SYNC_SYMBOL_START_ERRS = 0U;
const uint8_t MAX_SYNC_SYMBOL_RUN_ERRS = 1U;
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 = 3U + 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_nextState(M17RXS_NONE),
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_nextState = M17RXS_NONE;
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_LINK_SETUP:
case M17RXS_STREAM:
case M17RXS_PACKET:
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 ret1 = correlateSync(M17_LINK_SETUP_SYNC_SYMBOLS, M17_LINK_SETUP_SYNC_SYMBOLS_VALUES, M17_LINK_SETUP_SYNC_BYTES, MAX_SYNC_SYMBOL_START_ERRS, MAX_SYNC_BIT_START_ERRS);
bool ret2 = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_START_ERRS, MAX_SYNC_BIT_START_ERRS);
// bool ret3 = correlateSync(M17_PACKET_SYNC_SYMBOLS, M17_PACKET_SYNC_SYMBOLS_VALUES, M17_PACKET_SYNC_BYTES, MAX_SYNC_SYMBOL_START_ERRS, MAX_SYNC_BIT_START_ERRS);
if (ret1 || ret2 /* || ret3 */ ) {
// 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 (ret1) m_nextState = M17RXS_LINK_SETUP;
if (ret2) m_nextState = M17RXS_STREAM;
// if (ret3) m_nextState = M17RXS_PACKET;
}
}
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 = m_nextState;
m_countdown = 0U;
m_nextState = M17RXS_NONE;
}
}
void CM17RX::processData(q15_t sample)
{
if (m_minSyncPtr < m_maxSyncPtr) {
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr) {
bool ret1 = correlateSync(M17_LINK_SETUP_SYNC_SYMBOLS, M17_LINK_SETUP_SYNC_SYMBOLS_VALUES, M17_LINK_SETUP_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
bool ret2 = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
// bool ret3 = correlateSync(M17_PACKET_SYNC_SYMBOLS, M17_PACKET_SYNC_SYMBOLS_VALUES, M17_PACKET_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
if (ret1) m_state = M17RXS_LINK_SETUP;
if (ret2) m_state = M17RXS_STREAM;
// if (ret3) m_state = M17RXS_PACKET;
}
} else {
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr) {
bool ret1 = correlateSync(M17_LINK_SETUP_SYNC_SYMBOLS, M17_LINK_SETUP_SYNC_SYMBOLS_VALUES, M17_LINK_SETUP_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
bool ret2 = correlateSync(M17_STREAM_SYNC_SYMBOLS, M17_STREAM_SYNC_SYMBOLS_VALUES, M17_STREAM_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
// bool ret3 = correlateSync(M17_PACKET_SYNC_SYMBOLS, M17_PACKET_SYNC_SYMBOLS_VALUES, M17_PACKET_SYNC_BYTES, MAX_SYNC_SYMBOL_RUN_ERRS, MAX_SYNC_BIT_RUN_ERRS);
if (ret1) m_state = M17RXS_LINK_SETUP;
if (ret2) m_state = M17RXS_STREAM;
// if (ret3) m_state = M17RXS_PACKET;
}
}
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);
switch (m_state) {
case M17RXS_LINK_SETUP:
DEBUG4("M17RX: link setup sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
break;
case M17RXS_STREAM:
DEBUG4("M17RX: stream sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
break;
case M17RXS_PACKET:
DEBUG4("M17RX: packet sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
break;
}
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 stream 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_nextState = M17RXS_NONE;
m_maxCorr = 0;
} else {
frame[0U] = 0x00U;
frame[0U] |= m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
switch (m_state) {
case M17RXS_LINK_SETUP:
writeRSSILinkSetup(frame);
break;
case M17RXS_STREAM:
writeRSSIStream(frame);
break;
case M17RXS_PACKET:
writeRSSIPacket(frame);
break;
}
m_maxCorr = 0;
m_nextState = M17RXS_NONE;
}
}
}
bool CM17RX::correlateSync(uint8_t syncSymbols, const int8_t* syncSymbolValues, const uint8_t* syncBytes, uint8_t maxSymbolErrs, uint8_t maxBitErrs)
{
if (countBits8(m_bitBuffer[m_bitPtr] ^ syncSymbols) <= maxSymbolErrs) {
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 (syncSymbolValues[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 errs = 0U;
for (uint8_t i = 0U; i < M17_SYNC_BYTES_LENGTH; i++)
errs += countBits8(sync[i] ^ syncBytes[i]);
if (errs <= maxBitErrs) {
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::writeRSSILinkSetup(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.writeM17LinkSetup(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
serial.writeM17LinkSetup(data, M17_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeM17LinkSetup(data, M17_FRAME_LENGTH_BYTES + 1U);
#endif
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
void CM17RX::writeRSSIStream(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.writeM17Stream(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
serial.writeM17Stream(data, M17_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeM17Stream(data, M17_FRAME_LENGTH_BYTES + 1U);
#endif
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
void CM17RX::writeRSSIPacket(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.writeM17Packet(data, M17_FRAME_LENGTH_BYTES + 3U);
} else {
serial.writeM17Packet(data, M17_FRAME_LENGTH_BYTES + 1U);
}
#else
serial.writeM17Packet(data, M17_FRAME_LENGTH_BYTES + 1U);
#endif
m_rssiAccum = 0U;
m_rssiCount = 0U;
}
#endif
-79
View File
@@ -1,79 +0,0 @@
/*
* Copyright (C) 2015,2016,2017,2020,2021 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"
#if defined(MODE_M17)
#if !defined(M17RX_H)
#define M17RX_H
#include "M17Defines.h"
enum M17RX_STATE {
M17RXS_NONE,
M17RXS_LINK_SETUP,
M17RXS_STREAM,
M17RXS_PACKET
};
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;
M17RX_STATE m_nextState;
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(uint8_t syncSymbols, const int8_t* syncSymbolValues, const uint8_t* syncBytes, uint8_t maxSymbolErrs, uint8_t maxBitErrs);
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 writeRSSILinkSetup(uint8_t* data);
void writeRSSIStream(uint8_t* data);
void writeRSSIPacket(uint8_t* data);
};
#endif
#endif
-190
View File
@@ -1,190 +0,0 @@
/*
* 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"
#if defined(MODE_M17)
#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 = 0U;
m_buffer.get(c);
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;
}
#endif
-60
View File
@@ -1,60 +0,0 @@
/*
* 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.
*/
#include "Config.h"
#if defined(MODE_M17)
#if !defined(M17TX_H)
#define M17TX_H
#include "RingBuffer.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:
CRingBuffer<uint8_t> 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
#endif
+3 -24
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Mathis Schmieder DB9MAT
* Copyright (C) 2016 by Colin Durbridge G4EML
*
@@ -18,7 +18,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if defined(STM32F4XX) || defined(STM32F7XX) || defined(STM32F105xC)
#if defined(STM32F4XX) || defined(STM32F7XX)
#include "Config.h"
#include "Globals.h"
@@ -31,10 +31,8 @@ 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;
bool m_ax25Enable = true;
bool m_duplex = true;
@@ -79,11 +77,6 @@ CNXDNTX nxdnTX;
CCalNXDN calNXDN;
#endif
#if defined(MODE_M17)
CM17RX m17RX;
CM17TX m17TX;
#endif
#if defined(MODE_POCSAG)
CPOCSAGTX pocsagTX;
CCalPOCSAG calPOCSAG;
@@ -94,11 +87,6 @@ CFM fm;
CCalFM calFM;
#endif
#if defined(MODE_AX25)
CAX25RX ax25RX;
CAX25TX ax25TX;
#endif
CCalRSSI calRSSI;
CCWIdTX cwIdTX;
@@ -147,21 +135,11 @@ void loop()
nxdnTX.process();
#endif
#if defined(MODE_M17)
if (m_m17Enable && m_modemState == STATE_M17)
m17TX.process();
#endif
#if defined(MODE_POCSAG)
if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy()))
pocsagTX.process();
#endif
#if defined(MODE_AX25)
if (m_ax25Enable && (m_modemState == STATE_IDLE || m_modemState == STATE_FM))
ax25TX.process();
#endif
#if defined(MODE_FM)
if (m_fmEnable && m_modemState == STATE_FM)
fm.process();
@@ -210,3 +188,4 @@ int main()
}
#endif
+1 -23
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
*
* This program is free software; you can redistribute it and/or modify
@@ -28,10 +28,8 @@ 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;
bool m_ax25Enable = true;
bool m_duplex = true;
@@ -76,11 +74,6 @@ CNXDNTX nxdnTX;
CCalNXDN calNXDN;
#endif
#if defined(MODE_M17)
CM17RX m17RX;
CM17TX m17TX;
#endif
#if defined(MODE_POCSAG)
CPOCSAGTX pocsagTX;
CCalPOCSAG calPOCSAG;
@@ -91,11 +84,6 @@ CFM fm;
CCalFM calFM;
#endif
#if defined(MODE_AX25)
CAX25RX ax25RX;
CAX25TX ax25TX;
#endif
CCalRSSI calRSSI;
CCWIdTX cwIdTX;
@@ -144,21 +132,11 @@ void loop()
nxdnTX.process();
#endif
#if defined(MODE_M17)
if (m_m17Enable && m_modemState == STATE_M17)
m17TX.process();
#endif
#if defined(MODE_POCSAG)
if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy()))
pocsagTX.process();
#endif
#if defined(MODE_AX25)
if (m_ax25Enable && (m_modemState == STATE_IDLE || m_modemState == STATE_FM))
ax25TX.process();
#endif
#if defined(MODE_FM)
if (m_fmEnable && m_modemState == STATE_FM)
fm.process();
Binary file not shown.
+2 -2
View File
@@ -149,9 +149,9 @@ DEFS_EDA_446=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_EDA_446
# Build compiler flags
CFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4)
CXXFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4)
CXXFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4) -std=c++11
CFLAGS_F7=-c $(MCFLAGS_F7) $(INCLUDES_F7)
CXXFLAGS_F7=-c $(MCFLAGS_F7) $(INCLUDES_F7)
CXXFLAGS_F7=-c $(MCFLAGS_F7) $(INCLUDES_F7) -std=c++11
# Linker flags
LDFLAGS_F4 =-T stm32f4xx_link.ld $(MCFLAGS_F4) --specs=nosys.specs $(INCLUDES_LIBS_F4)
-165
View File
@@ -1,165 +0,0 @@
# The name for the project
TARGET:=mmdvm
# The CPU architecture (will be used for -mcpu)
MCPU:=cortex-m3
MCU:=STM32F105xC
# The source files of the project
CSRC:=
CXXSRC:=$(wildcard *.cpp)
# Include directory for the system include files and system source file
SYSDIR:=system_stm32f1xx
SYSSRC:=$(SYSDIR)/system_stm32f1xx.c
# Other include directories
INCDIR:=
# Definitions
CDEFS:=
CXXDEFS:=
# The name of the startup file which matches to the architecture (MCPU)
STARTUP:=$(SYSDIR)/startup_stm32f105xc.S
STARTUP_DEFS=
# Include directory for CMSIS
CMSISDIR:=STM32F10X_Lib/CMSIS
# Libraries
LIBDIR:=
LIBS:=-larm_cortexM3l_math
# Name of the linker script
LDSCRIPT:=$(SYSDIR)/gcc.ld
# Target objects and binaries directory
OBJDIR:=obj
BINDIR:=bin
# Port definition for programming via bootloader (using stm32flash)
BL_PORT:=ttyUSB0
# Internal Variables
ELF:=$(BINDIR)/$(TARGET).elf
HEX:=$(BINDIR)/$(TARGET).hex
DIS:=$(BINDIR)/$(TARGET).dis
MAP:=$(BINDIR)/$(TARGET).map
OBJ:=$(CSRC:%.c=$(OBJDIR)/%.o) $(CXXSRC:%.cpp=$(OBJDIR)/%.o)
OBJ+=$(SYSSRC:$(SYSDIR)/%.c=$(OBJDIR)/%.o) $(STARTUP:$(SYSDIR)/%.S=$(OBJDIR)/%.o)
# Replace standard build tools by arm tools
CC:=arm-none-eabi-gcc
CXX:=arm-none-eabi-g++
AS:=arm-none-eabi-gcc
LD:=arm-none-eabi-g++
OBJCOPY:=arm-none-eabi-objcopy
OBJDUMP:=arm-none-eabi-objdump
SIZE:=arm-none-eabi-size
# Common flags
COMMON_FLAGS =-mthumb -mlittle-endian -mcpu=$(MCPU)
COMMON_FLAGS+= -Wall
COMMON_FLAGS+= -I. -I$(CMSISDIR)/Include -I$(CMSISDIR)/Device/ST/STM32F1xx/Include -I$(SYSDIR)
COMMON_FLAGS+= $(addprefix -I,$(INCDIR))
COMMON_FLAGS+= -D$(MCU) -DMADEBYMAKEFILE -DSTM32F1
COMMON_FLAGS+= -Os -flto -ffunction-sections -fdata-sections
COMMON_FLAGS+= -g
# Assembler flags
ASFLAGS:=$(COMMON_FLAGS)
# C flags
CFLAGS:=$(COMMON_FLAGS) $(addprefix -D,$(CDEFS))
CFLAGS+= -std=gnu11 -nostdlib
# CXX flags
CXXFLAGS:=$(COMMON_FLAGS) $(addprefix -D,$(CXXDEFS))
CXXFLAGS+= -nostdlib -fno-exceptions -fno-rtti
# LD flags
LDFLAGS:=$(COMMON_FLAGS) -Wl,--gc-sections -Wl,-Map=$(MAP) -Wl,--no-wchar-size-warning
LDFLAGS+= --specs=nosys.specs --specs=nano.specs
LDFLAGS+= -L$(CMSISDIR)/Lib/GCC/ $(addprefix -L,$(LIBDIR))
LDLIBS:=-T$(LDSCRIPT) $(LIBS)
# Dependecies
DEPENDS:=$(CSRC:%.c=$(OBJDIR)/%.d) $(CXXSRC:%.cpp=$(OBJDIR)/%.d)
# Additional Suffixes
.SUFFIXES: .elf .hex
# Targets
.PHONY: all
all: GitVersion.h $(DIS) $(HEX)
$(SIZE) $(ELF)
.PHONY: program
program: $(HEX) $(ELF)
openocd -f openocd.cfg \
-c "program $(HEX) verify reset exit"
$(SIZE) $(ELF)
.PHONY: program_bl
program_bl: $(HEX) $(ELF)
stm32flash -w $(HEX) -v /dev/$(BL_PORT)
$(SIZE) $(ELF)
.PHONY: run
run: $(HEX) $(ELF)
openocd -f openocd.cfg \
-c "init" -c "reset" -c "exit"
.PHONY: debug
debug: $(ELF)
./debug.sh $(ELF)
.PHONY: clean
clean:
$(RM) $(OBJ) $(HEX) $(ELF) $(DIS) $(MAP) $(DEPENDS) GitVersion.h
# implicit rules
.elf.hex:
$(OBJCOPY) -O ihex $< $@
$(OBJDIR)/%.o: %.c
$(CC) -MMD $(CFLAGS) -c $< -o $@
$(OBJDIR)/%.o: %.cpp
$(CXX) -MMD $(CXXFLAGS) -c $< -o $@
$(OBJDIR)/%.o: $(SYSDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
$(OBJDIR)/%.o: $(SYSDIR)/%.S
$(AS) $(ASFLAGS) -c $< -o $@
# explicit rules
$(OBJDIR):
mkdir -p $(OBJDIR)
$(BINDIR):
mkdir -p $(BINDIR)
$(ELF): $(OBJDIR) $(BINDIR) $(OBJ)
$(LD) $(OBJ) $(LDFLAGS) $(LDLIBS) -o $@
$(DIS): $(ELF)
$(OBJDUMP) -S $< > $@
# include dependecies
-include $(DEPENDS)
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifdef SYSTEMROOT
echo #define GITVERSION "0000000" > $@
else ifdef SystemRoot
echo #define GITVERSION "0000000" > $@
else
ifneq ("$(wildcard .git/index)","")
echo "#define GITVERSION \"$(shell git rev-parse --short HEAD)\"" > $@
else
echo "#define GITVERSION \"0000000\"" > $@
endif
endif
+1 -1
View File
@@ -175,7 +175,7 @@ void CNXDNTX::writeByte(uint8_t c)
void CNXDNTX::writeSilence()
{
q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U};
q15_t inBuffer[4U] = {0, 0, 0, 0};
q15_t intBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U];
q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U];
+1 -1
View File
@@ -173,7 +173,7 @@ void CP25TX::writeByte(uint8_t c)
void CP25TX::writeSilence()
{
q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U};
q15_t inBuffer[4U] = {0, 0, 0, 0};
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
+32 -14
View File
@@ -1,17 +1,35 @@
This is the source code of the MMDVM firmware that supports D-Star, DMR,
System Fusion, P25, NXDN, POCSAG, and FM 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, 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.
It runs on the Arduino Due, the ST-Micro STM32F4xxx and STM32F7xxx processors, as well as the Teensy 3.5/3.6. What these platforms have in common is the use of an ARM Cortex-M3, M4, or M7 processors with a minimum clock speed greater of 80 MHz, and access to at least one analogue to digital converter, one digital to analogue converter, as well as a number of GPIO pins.
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.
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 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 primarily intended for amateur and educational use.
Portions of the ARM support code include the following copyright:
Copyright (c) 2011 - 2013 ARM LIMITED
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-47
View File
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2017 Wojciech Krutnik N0CALL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* FIFO ring buffer
* source:
* http://stackoverflow.com/questions/6822548/correct-way-of-implementing-a-uart-receive-buffer-in-a-small-arm-microcontroller
* (modified)
*
*/
#if !defined(RINGBUFF_H)
#define RINGBUFF_H
#define RINGBUFF_SIZE(ringBuff) (ringBuff.size) /* serial buffer in bytes (power 2) */
#define RINGBUFF_MASK(ringBuff) (ringBuff.size-1U) /* buffer size mask */
/* Buffer read / write macros */
#define RINGBUFF_RESET(ringBuff) (ringBuff).rdIdx = ringBuff.wrIdx = 0
#define RINGBUFF_WRITE(ringBuff, dataIn) (ringBuff).data[RINGBUFF_MASK(ringBuff) & ringBuff.wrIdx++] = (dataIn)
#define RINGBUFF_READ(ringBuff) ((ringBuff).data[RINGBUFF_MASK(ringBuff) & ((ringBuff).rdIdx++)])
#define RINGBUFF_EMPTY(ringBuff) ((ringBuff).rdIdx == (ringBuff).wrIdx)
#define RINGBUFF_FULL(ringBuff) ((RINGBUFF_MASK(ringBuff) & ringBuff.rdIdx) == (RINGBUFF_MASK(ringBuff) & ringBuff.wrIdx))
#define RINGBUFF_COUNT(ringBuff) (RINGBUFF_MASK(ringBuff) & ((ringBuff).wrIdx - (ringBuff).rdIdx))
/* Buffer type */
#define DECLARE_RINGBUFFER_TYPE(name, _size) \
typedef struct{ \
uint32_t size; \
uint32_t wrIdx; \
uint32_t rdIdx; \
uint8_t data[_size]; \
}name##_t
#endif
+4 -7
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2026 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* This program is free software; you can redistribute it and/or modify
@@ -24,15 +24,12 @@
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#include <cstddef>
#else
#include <Arduino.h>
#undef PI
#endif
#if defined(__SAM3X8E__) || defined(STM32F105xC)
#if defined(__SAM3X8E__)
#define ARM_MATH_CM3
#elif defined(STM32F7XX)
#define ARM_MATH_CM7
@@ -53,7 +50,7 @@ public:
uint16_t getData() const;
bool put(TDATATYPE item) volatile;
bool put(const TDATATYPE& item) volatile;
bool get(TDATATYPE& item) volatile;
@@ -74,4 +71,4 @@ private:
#include "RingBuffer.impl.h"
#endif
#endif
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2026 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA
*
* This program is free software; you can redistribute it and/or modify
@@ -56,7 +56,7 @@ 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
template <typename TDATATYPE> bool CRingBuffer<TDATATYPE>::put(const TDATATYPE& item) volatile
{
if (m_full) {
m_overflow = true;
Submodule STM32F10X_Lib deleted from 417e0c2f4a
+3 -1
View File
@@ -1,5 +1,7 @@
/*
* source: http://mightydevices.com/?p=144
* Copyright (C) 2017 by Wojciech Krutnik N0CALL
*
* Source: http://mightydevices.com/?p=144
*
* 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
+7 -3
View File
@@ -61,7 +61,7 @@ void CSTMUART::handleIRQ()
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);
USART_ClearITPendingBit(m_usart, USART_IT_RXNE);
}
if (USART_GetITStatus(m_usart, USART_IT_TXE)) {
@@ -82,8 +82,12 @@ 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))
// wait until the TX FIFO has drained
while (!m_txFifo.isEmpty())
;
// wait until the TC flag shows the shift register is empty
while (USART_GetFlagStatus(m_usart, USART_FLAG_TC) == RESET)
;
}
+124 -353
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013,2015-2021 by Jonathan Naylor G4KLX
* Copyright (C) 2013,2015-2021,2023,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
*
* This program is free software; you can redistribute it and/or modify
@@ -45,6 +45,7 @@ const uint8_t MMDVM_DSTAR_DATA = 0x11U;
const uint8_t MMDVM_DSTAR_LOST = 0x12U;
const uint8_t MMDVM_DSTAR_EOT = 0x13U;
const uint8_t MMDVM_DMR_ALOHA = 0x17U;
const uint8_t MMDVM_DMR_DATA1 = 0x18U;
const uint8_t MMDVM_DMR_LOST1 = 0x19U;
const uint8_t MMDVM_DMR_DATA2 = 0x1AU;
@@ -63,15 +64,8 @@ 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_LINK_SETUP = 0x45U;
const uint8_t MMDVM_M17_STREAM = 0x46U;
const uint8_t MMDVM_M17_PACKET = 0x47U;
const uint8_t MMDVM_M17_LOST = 0x48U;
const uint8_t MMDVM_POCSAG_DATA = 0x50U;
const uint8_t MMDVM_AX25_DATA = 0x55U;
const uint8_t MMDVM_FM_PARAMS1 = 0x60U;
const uint8_t MMDVM_FM_PARAMS2 = 0x61U;
const uint8_t MMDVM_FM_PARAMS3 = 0x62U;
@@ -79,6 +73,7 @@ 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_FM_RSSI = 0x68U;
const uint8_t MMDVM_ACK = 0x70U;
const uint8_t MMDVM_NAK = 0x7FU;
@@ -139,33 +134,31 @@ m_buffer(),
m_ptr(0U),
m_len(0U),
m_debug(false),
m_serialData(),
m_lastSerialAvail(0),
m_lastSerialAvailCount(0U),
m_i2CData()
m_lastSerialAvailCount(0U)
{
}
void CSerialPort::sendACK()
void CSerialPort::sendACK(uint8_t type)
{
uint8_t reply[4U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 4U;
reply[2U] = MMDVM_ACK;
reply[3U] = m_buffer[2U];
reply[3U] = type;
writeInt(1U, reply, 4);
}
void CSerialPort::sendNAK(uint8_t err)
void CSerialPort::sendNAK(uint8_t type, uint8_t err)
{
uint8_t reply[5U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 5U;
reply[2U] = MMDVM_NAK;
reply[3U] = m_buffer[2U];
reply[3U] = type;
reply[4U] = err;
writeInt(1U, reply, 5);
@@ -263,14 +256,7 @@ void CSerialPort::getStatus()
reply[11U] = 0U;
#endif
#if defined(MODE_M17)
if (m_m17Enable)
reply[12U] = m17TX.getSpace();
else
reply[12U] = 0U;
#else
reply[12U] = 0U;
#endif
#if defined(MODE_FM)
if (m_fmEnable)
@@ -281,27 +267,18 @@ void CSerialPort::getStatus()
reply[13U] = 0U;
#endif
reply[14U] = 0x00U;
reply[15U] = 0x00U;
#if defined(MODE_POCSAG)
if (m_pocsagEnable)
reply[16U] = pocsagTX.getSpace();
reply[14U] = pocsagTX.getSpace();
else
reply[16U] = 0U;
reply[14U] = 0U;
#else
reply[16U] = 0U;
#endif
#if defined(MODE_AX25)
if (m_ax25Enable)
reply[17U] = ax25TX.getSpace();
else
reply[17U] = 0U;
#else
reply[17U] = 0U;
reply[14U] = 0U;
#endif
reply[15U] = 0U;
reply[16U] = 0x00U;
reply[17U] = 0x00U;
reply[18U] = 0x00U;
reply[19U] = 0x00U;
@@ -335,9 +312,6 @@ void CSerialPort::getVersion()
#if defined(MODE_NXDN)
reply[4U] |= 0x10U;
#endif
#if defined(MODE_M17)
reply[4U] |= 0x20U;
#endif
#if defined(MODE_FM)
reply[4U] |= 0x40U;
#endif
@@ -346,9 +320,6 @@ void CSerialPort::getVersion()
#if defined(MODE_POCSAG)
reply[5U] |= 0x01U;
#endif
#if defined(MODE_AX25)
reply[5U] |= 0x02U;
#endif
// CPU type/manufacturer. 0=Atmel ARM, 1=NXP ARM, 2=St-Micro ARM
reply[6U] = io.getCPU();
@@ -374,22 +345,35 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
bool txInvert = (data[0U] & 0x02U) == 0x02U;
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
#if defined(MODE_YSF)
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
#endif
bool useCOSAsLockout = (data[0U] & 0x20U) == 0x20U;
bool simplex = (data[0U] & 0x80U) == 0x80U;
m_debug = (data[0U] & 0x10U) == 0x10U;
#if defined(MODE_DSTAR)
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
#endif
#if defined(MODE_DMR)
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
#endif
#if defined(MODE_YSF)
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
#endif
#if defined(MODE_P25)
bool p25Enable = (data[1U] & 0x08U) == 0x08U;
#endif
#if defined(MODE_NXDN)
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
#endif
#if defined(MODE_FM)
bool fmEnable = (data[1U] & 0x20U) == 0x20U;
bool m17Enable = (data[1U] & 0x40U) == 0x40U;
#endif
#if defined(MODE_POCSAG)
bool pocsagEnable = (data[2U] & 0x01U) == 0x01U;
bool ax25Enable = (data[2U] & 0x02U) == 0x02U;
#endif
uint8_t txDelay = data[3U];
if (txDelay > 50U)
@@ -397,7 +381,9 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
MMDVM_STATE modemState = MMDVM_STATE(data[4U]);
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)
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)
return 4U;
#if defined(MODE_DSTAR)
@@ -440,14 +426,6 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
return 4U;
#endif
#if defined(MODE_M17)
if (modemState == STATE_M17 && !m17Enable)
return 4U;
#else
if (modemState == STATE_M17)
return 4U;
#endif
#if defined(MODE_POCSAG)
if (modemState == STATE_POCSAG && !pocsagEnable)
return 4U;
@@ -475,29 +453,27 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
uint8_t ysfTXLevel = data[11U];
uint8_t p25TXLevel = data[12U];
uint8_t nxdnTXLevel = data[13U];
uint8_t m17TXLevel = data[14U];
uint8_t pocsagTXLevel = data[15U];
uint8_t fmTXLevel = data[16U];
uint8_t ax25TXLevel = data[17U];
#if defined(MODE_YSF)
uint8_t ysfTXHang = data[20U];
#endif
#if defined(MODE_P25)
uint8_t p25TXHang = data[21U];
#endif
#if defined(MODE_NXDN)
uint8_t nxdnTXHang = data[22U];
uint8_t m17TXHang = data[23U];
#endif
#if defined(MODE_DMR)
bool trunking = (data[25U] & 128U) == 128U;
uint8_t colorCode = data[26U];
if (colorCode > 15U)
return 4U;
uint8_t dmrDelay = data[27U];
int8_t ax25RXTwist = int8_t(data[28U]) - 128;
if (ax25RXTwist < -4 || ax25RXTwist > 10)
return 4U;
uint8_t ax25TXDelay = data[29U];
uint8_t ax25SlotTime = data[30U];
uint8_t ax25PPersist = data[31U];
#endif
setMode(modemState);
@@ -510,7 +486,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
#if defined(MODE_DMR)
m_dmrEnable = dmrEnable;
dmrDMOTX.setTXDelay(txDelay);
dmrTX.setTrunking(trunking);
dmrTX.setColorCode(colorCode);
dmrRX.setColorCode(colorCode);
dmrRX.setDelay(dmrDelay);
@@ -532,25 +508,15 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint16_t length)
nxdnTX.setTXDelay(txDelay);
nxdnTX.setParams(nxdnTXHang);
#endif
#if defined(MODE_M17)
m_m17Enable = m17Enable;
m17TX.setTXDelay(txDelay);
m17TX.setParams(m17TXHang);
#endif
#if defined(MODE_POCSAG)
m_pocsagEnable = pocsagEnable;
pocsagTX.setTXDelay(txDelay);
#endif
#if defined(MODE_AX25)
m_ax25Enable = ax25Enable;
ax25TX.setTXDelay(ax25TXDelay);
ax25RX.setParams(ax25RXTwist, ax25SlotTime, ax25PPersist);
#endif
#if defined(MODE_FM)
m_fmEnable = fmEnable;
#endif
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, m17TXLevel, pocsagTXLevel, fmTXLevel, ax25TXLevel, txDCOffset, rxDCOffset, useCOSAsLockout);
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset, useCOSAsLockout);
io.start();
@@ -620,6 +586,7 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint16_t length)
uint8_t hangTime = data[7U];
uint8_t accessMode = data[8U] & 0x0FU;
bool linkMode = (data[8U] & 0x20U) == 0x20U;
bool noiseSquelch = (data[8U] & 0x40U) == 0x40U;
bool cosInvert = (data[8U] & 0x80U) == 0x80U;
@@ -630,7 +597,7 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint16_t length)
uint8_t squelchHighThreshold = data[12U];
uint8_t squelchLowThreshold = data[13U];
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, cosInvert, noiseSquelch, squelchHighThreshold, squelchLowThreshold, rfAudioBoost, maxDev, rxLevel);
return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, linkMode, cosInvert, noiseSquelch, squelchHighThreshold, squelchLowThreshold, rfAudioBoost, maxDev, rxLevel);
}
uint8_t CSerialPort::setFMParams4(const uint8_t* data, uint16_t length)
@@ -663,7 +630,9 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint16_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_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)
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)
return 4U;
#if defined(MODE_DSTAR)
@@ -706,14 +675,6 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint16_t length)
return 4U;
#endif
#if defined(MODE_M17)
if (modemState == STATE_M17 && !m_m17Enable)
return 4U;
#else
if (modemState == STATE_M17)
return 4U;
#endif
#if defined(MODE_POCSAG)
if (modemState == STATE_POCSAG && !m_pocsagEnable)
return 4U;
@@ -753,9 +714,6 @@ 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;
@@ -787,7 +745,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
DEBUG1("Mode set to FM 20Khz Calibrate");
break;
case STATE_FMCAL25K:
DEBUG1("Mode set to FM 10Khz Calibrate");
DEBUG1("Mode set to FM 25Khz Calibrate");
break;
case STATE_FMCAL30K:
DEBUG1("Mode set to FM 30Khz Calibrate");
@@ -837,11 +795,6 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
nxdnRX.reset();
#endif
#if defined(MODE_M17)
if (modemState != STATE_M17)
m17RX.reset();
#endif
#if defined(MODE_FM)
if (modemState != STATE_FM)
fm.reset();
@@ -857,7 +810,11 @@ void CSerialPort::start()
beginInt(1U, SERIAL_SPEED);
#if defined(SERIAL_REPEATER)
beginInt(3U, 9600);
#if defined(SERIAL_REPEATER_BAUD_RATE)
beginInt(3U, SERIAL_REPEATER_BAUD_RATE);
#else
beginInt(3U, 9600);
#endif
#endif
#if defined(I2C_REPEATER)
beginInt(10U, 9600);
@@ -915,20 +872,6 @@ void CSerialPort::process()
}
#if defined(SERIAL_REPEATER)
// Write any outgoing serial data
uint16_t serialSpace = m_serialData.getData();
if (serialSpace > 0U) {
int avail = availableForWriteInt(3U);
if (avail < serialSpace)
serialSpace = avail;
for (uint16_t i = 0U; i < serialSpace; i++) {
uint8_t c = 0U;
m_serialData.get(c);
writeInt(3U, &c, 1U);
}
}
// Read any incoming serial data, and send out in batches
int serialAvail = availableForReadInt(3U);
if ((serialAvail > 0 && serialAvail == m_lastSerialAvail && m_lastSerialAvailCount >= MAX_SERIAL_COUNT) || (serialAvail >= MAX_SERIAL_DATA)) {
@@ -939,29 +882,13 @@ void CSerialPort::process()
}
writeSerialData(buffer, serialAvail - m_lastSerialAvail);
m_lastSerialAvailCount = 0U;
} else if (serialAvail > 0U && serialAvail == m_lastSerialAvail) {
} else if (serialAvail > 0 && serialAvail == m_lastSerialAvail) {
m_lastSerialAvailCount++;
} else {
m_lastSerialAvail = serialAvail;
m_lastSerialAvailCount = 0U;
}
#endif
#if defined(I2C_REPEATER)
// Write any outgoing serial data
uint16_t i2CSpace = m_i2CData.getData();
if (i2CSpace > 0U) {
int avail = availableForWriteInt(10U);
if (avail < i2CSpace)
i2CSpace = avail;
for (uint16_t i = 0U; i < i2CSpace; i++) {
uint8_t c = 0U;
m_i2CData.get(c);
writeInt(10U, &c, 1U);
}
}
#endif
}
void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t length)
@@ -980,61 +907,61 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
case MMDVM_SET_CONFIG:
err = setConfig(buffer, length);
if (err == 0U)
sendACK();
sendACK(type);
else
sendNAK(err);
sendNAK(type, err);
break;
case MMDVM_SET_MODE:
err = setMode(buffer, length);
if (err == 0U)
sendACK();
sendACK(type);
else
sendNAK(err);
sendNAK(type, err);
break;
case MMDVM_SET_FREQ:
sendACK();
sendACK(type);
break;
#if defined(MODE_FM)
case MMDVM_FM_PARAMS1:
err = setFMParams1(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 1", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_FM_PARAMS2:
err = setFMParams2(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 2", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_FM_PARAMS3:
err = setFMParams3(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 3", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_FM_PARAMS4:
err = setFMParams4(buffer, length);
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid FM params 4", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#else
@@ -1042,7 +969,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
case MMDVM_FM_PARAMS2:
case MMDVM_FM_PARAMS3:
case MMDVM_FM_PARAMS4:
sendACK();
sendACK(type);
break;
#endif
@@ -1072,10 +999,10 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = calPOCSAG.write(buffer, length);
#endif
if (err == 0U) {
sendACK();
sendACK(type);
} else {
DEBUG2("Received invalid calibration data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1085,7 +1012,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = cwIdTX.write(buffer, length);
if (err != 0U) {
DEBUG2("Invalid CW Id data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1100,7 +1027,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star header", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1114,7 +1041,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1128,7 +1055,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star EOT", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1146,7 +1073,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DMR);
} else {
DEBUG2("Received invalid DMR data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1164,7 +1091,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_DMR);
} else {
DEBUG2("Received invalid DMR data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1185,7 +1112,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
}
if (err != 0U) {
DEBUG2("Received invalid DMR start", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1194,7 +1121,16 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = dmrTX.writeShortLC(buffer, length);
if (err != 0U) {
DEBUG2("Received invalid DMR Short LC", err);
sendNAK(err);
sendNAK(type, err);
}
break;
case MMDVM_DMR_ALOHA:
if (m_dmrEnable)
err = dmrTX.writeAloha(buffer, length);
if (err != 0U) {
DEBUG2("Received invalid DMR ALOHA", err);
sendNAK(type, err);
}
break;
@@ -1203,7 +1139,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
err = dmrTX.writeAbort(buffer, length);
if (err != 0U) {
DEBUG2("Received invalid DMR Abort", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1219,7 +1155,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_YSF);
} else {
DEBUG2("Received invalid System Fusion data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1235,7 +1171,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_P25);
} else {
DEBUG2("Received invalid P25 header", err);
sendNAK(err);
sendNAK(type, err);
}
break;
@@ -1249,7 +1185,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_P25);
} else {
DEBUG2("Received invalid P25 LDU", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1265,51 +1201,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_NXDN);
} else {
DEBUG2("Received invalid NXDN data", err);
sendNAK(err);
}
break;
#endif
#if defined(MODE_M17)
case MMDVM_M17_LINK_SETUP:
if (m_m17Enable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_M17)
err = m17TX.writeData(buffer, length);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 link setup data", err);
sendNAK(err);
}
break;
case MMDVM_M17_STREAM:
if (m_m17Enable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_M17)
err = m17TX.writeData(buffer, length);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 stream data", err);
sendNAK(err);
}
break;
case MMDVM_M17_PACKET:
if (m_m17Enable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_M17)
err = m17TX.writeData(buffer, length);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_M17);
} else {
DEBUG2("Received invalid M17 packet data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1325,7 +1217,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_POCSAG);
} else {
DEBUG2("Received invalid POCSAG data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1341,20 +1233,7 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
setMode(STATE_FM);
} else {
DEBUG2("Received invalid FM data", err);
sendNAK(err);
}
break;
#endif
#if defined(MODE_AX25)
case MMDVM_AX25_DATA:
if (m_ax25Enable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_FM)
err = ax25TX.writeData(buffer, length);
}
if (err != 0U) {
DEBUG2("Received invalid AX.25 data", err);
sendNAK(err);
sendNAK(type, err);
}
break;
#endif
@@ -1365,24 +1244,20 @@ void CSerialPort::processMessage(uint8_t type, const uint8_t* buffer, uint16_t l
break;
#if defined(SERIAL_REPEATER)
case MMDVM_SERIAL_DATA: {
for (uint16_t i = 0U; i < length; i++)
m_serialData.put(buffer[i]);
}
case MMDVM_SERIAL_DATA:
writeInt(3U, buffer, length);
break;
#endif
#if defined(I2C_REPEATER)
case MMDVM_I2C_DATA: {
for (uint16_t i = 0U; i < length; i++)
m_i2CData.put(buffer[i]);
}
case MMDVM_I2C_DATA:
writeInt(10U, buffer, length);
break;
#endif
default:
// Handle this, send a NAK back
sendNAK(1U);
sendNAK(type, 1U);
break;
}
@@ -1662,94 +1537,6 @@ void CSerialPort::writeNXDNLost()
}
#endif
#if defined(MODE_M17)
void CSerialPort::writeM17LinkSetup(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
if (!m_m17Enable)
return;
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_M17_LINK_SETUP;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
writeInt(1U, reply, count);
}
void CSerialPort::writeM17Stream(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
if (!m_m17Enable)
return;
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_M17_STREAM;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
writeInt(1U, reply, count);
}
void CSerialPort::writeM17Packet(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
if (!m_m17Enable)
return;
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_M17_PACKET;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
writeInt(1U, reply, count);
}
void CSerialPort::writeM17Lost()
{
if (m_modemState != STATE_M17 && m_modemState != STATE_IDLE)
return;
if (!m_m17Enable)
return;
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = MMDVM_M17_LOST;
writeInt(1U, reply, 3);
}
#endif
#if defined(MODE_FM)
void CSerialPort::writeFMData(const uint8_t* data, uint16_t length)
{
@@ -1768,7 +1555,7 @@ void CSerialPort::writeFMData(const uint8_t* data, uint16_t length)
reply[2U] = (length + 4U) - 255U;
reply[3U] = MMDVM_FM_DATA;
for (uint8_t i = 0U; i < length; i++)
for (uint16_t i = 0U; i < length; i++)
reply[i + 4U] = data[i];
writeInt(1U, reply, length + 4U);
@@ -1776,7 +1563,7 @@ void CSerialPort::writeFMData(const uint8_t* data, uint16_t length)
reply[1U] = length + 3U;
reply[2U] = MMDVM_FM_DATA;
for (uint8_t i = 0U; i < length; i++)
for (uint16_t i = 0U; i < length; i++)
reply[i + 3U] = data[i];
writeInt(1U, reply, length + 3U);
@@ -1801,6 +1588,25 @@ void CSerialPort::writeFMStatus(uint8_t status)
writeInt(1U, reply, 4U);
}
void CSerialPort::writeFMRSSI(uint16_t rssi)
{
if (m_modemState != STATE_FM && m_modemState != STATE_IDLE)
return;
if (!m_fmEnable)
return;
uint8_t reply[10U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 5U;
reply[2U] = MMDVM_FM_RSSI;
reply[3U] = (rssi >> 8) & 0xFFU;
reply[4U] = (rssi >> 0) & 0xFFU;
writeInt(1U, reply, 5U);
}
void CSerialPort::writeFMEOT()
{
if (m_modemState != STATE_FM && m_modemState != STATE_IDLE)
@@ -1819,40 +1625,6 @@ void CSerialPort::writeFMEOT()
}
#endif
#if defined(MODE_AX25)
void CSerialPort::writeAX25Data(const uint8_t* data, uint16_t length)
{
if (m_modemState != STATE_FM && m_modemState != STATE_IDLE)
return;
if (!m_ax25Enable)
return;
uint8_t reply[512U];
reply[0U] = MMDVM_FRAME_START;
if (length > 252U) {
reply[1U] = 0U;
reply[2U] = (length + 4U) - 255U;
reply[3U] = MMDVM_AX25_DATA;
for (uint8_t i = 0U; i < length; i++)
reply[i + 4U] = data[i];
writeInt(1U, reply, length + 4U);
} else {
reply[1U] = length + 3U;
reply[2U] = MMDVM_AX25_DATA;
for (uint8_t i = 0U; i < length; i++)
reply[i + 3U] = data[i];
writeInt(1U, reply, length + 3U);
}
}
#endif
#if defined(SERIAL_REPEATER)
void CSerialPort::writeSerialData(const uint8_t* data, uint8_t length)
{
@@ -2072,7 +1844,7 @@ void CSerialPort::writeDebugDump(const uint8_t* data, uint16_t length)
reply[2U] = (length + 4U) - 255U;
reply[3U] = MMDVM_DEBUG_DUMP;
for (uint8_t i = 0U; i < length; i++)
for (uint16_t i = 0U; i < length; i++)
reply[i + 4U] = data[i];
writeInt(1U, reply, length + 4U);
@@ -2080,10 +1852,9 @@ void CSerialPort::writeDebugDump(const uint8_t* data, uint16_t length)
reply[1U] = length + 3U;
reply[2U] = MMDVM_DEBUG_DUMP;
for (uint8_t i = 0U; i < length; i++)
for (uint16_t i = 0U; i < length; i++)
reply[i + 3U] = data[i];
writeInt(1U, reply, length + 3U);
}
}
+5 -16
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2023,2025 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
@@ -64,20 +64,10 @@ public:
void writeNXDNLost();
#endif
#if defined(MODE_M17)
void writeM17LinkSetup(const uint8_t* data, uint8_t length);
void writeM17Stream(const uint8_t* data, uint8_t length);
void writeM17Packet(const uint8_t* data, uint8_t length);
void writeM17Lost();
#endif
#if defined(MODE_AX25)
void writeAX25Data(const uint8_t* data, uint16_t length);
#endif
#if defined(MODE_FM)
void writeFMData(const uint8_t* data, uint16_t length);
void writeFMStatus(uint8_t status);
void writeFMRSSI(uint16_t rssi);
void writeFMEOT();
#endif
@@ -104,13 +94,11 @@ private:
uint16_t m_ptr;
uint16_t m_len;
bool m_debug;
CRingBuffer<uint8_t> m_serialData;
int m_lastSerialAvail;
uint16_t m_lastSerialAvailCount;
CRingBuffer<uint8_t> m_i2CData;
void sendACK();
void sendNAK(uint8_t err);
void sendACK(uint8_t type);
void sendNAK(uint8_t type, uint8_t err);
void getStatus();
void getVersion();
uint8_t setConfig(const uint8_t* data, uint16_t length);
@@ -134,3 +122,4 @@ private:
};
#endif
+1 -1
View File
@@ -332,7 +332,7 @@ int CSerialPort::availableForReadInt(uint8_t n)
switch (n) {
case 1U:
#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO)
return m_USART3.availble();//AvailUSART3();
return m_USART3.available();//AvailUSART3();
#elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F722_RPT_HAT) || defined(STM32F4_DVM) || defined(STM32F7_DVM) || defined(STM32F4_EDA_405) || defined(STM32F4_EDA_446)
return m_USART1.available();//AvailUSART1();
#elif defined(STM32F4_NUCLEO) || defined(STM32F4_RPT_HAT_TGO)
-170
View File
@@ -1,170 +0,0 @@
/*
* Copyright (C) 2017 by Wojciech Krutnik N0CALL
*
* 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 "RingBuff.h"
#include "SerialPort.h"
/*
Pin definitions (configuration in IOSTM_CMSIS.c):
- Host communication:
USART1 - TXD PA9 - RXD PA10
*/
#if defined(STM32F1)
// BaudRate calculator macro
// source: STM32 HAL Library (stm32f1xx_hal_usart.h)
#define USART_DIV(__PCLK__, __BAUD__) (((__PCLK__)*25)/(4*(__BAUD__)))
#define USART_DIVMANT(__PCLK__, __BAUD__) (USART_DIV((__PCLK__), (__BAUD__))/100)
#define USART_DIVFRAQ(__PCLK__, __BAUD__) (((USART_DIV((__PCLK__), (__BAUD__)) - (USART_DIVMANT((__PCLK__), (__BAUD__)) * 100)) * 16 + 50) / 100)
#define USART_BRR(__PCLK__, __BAUD__) ((USART_DIVMANT((__PCLK__), (__BAUD__)) << 4)|(USART_DIVFRAQ((__PCLK__), (__BAUD__)) & 0x0F))
#define USART_BUFFER_SIZE 256U
DECLARE_RINGBUFFER_TYPE(USARTBuffer, USART_BUFFER_SIZE);
/* ************* USART1 ***************** */
static volatile USARTBuffer_t txBuffer1={.size=USART_BUFFER_SIZE};
static volatile USARTBuffer_t rxBuffer1={.size=USART_BUFFER_SIZE};
extern "C" {
bitband_t txe = (bitband_t)BITBAND_PERIPH(&USART1->SR, USART_SR_TXE_Pos);
bitband_t rxne = (bitband_t)BITBAND_PERIPH(&USART1->SR, USART_SR_RXNE_Pos);
bitband_t txeie = (bitband_t)BITBAND_PERIPH(&USART1->CR1, USART_CR1_TXEIE_Pos);
RAMFUNC void USART1_IRQHandler()
{
/* Transmitting data */
if(*txe){
if(!(RINGBUFF_EMPTY(txBuffer1))){
USART1->DR = RINGBUFF_READ(txBuffer1);
}else{ /* Buffer empty */
*txeie = 0; /* Don't send further data */
}
}
/* Receiving data */
if(*rxne){
RINGBUFF_WRITE(rxBuffer1, USART1->DR);
}
}
}
void USART1Init(int speed)
{
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
USART1->BRR = USART_BRR(72000000UL, speed);
USART1->CR1 = USART_CR1_UE | USART_CR1_TE |
USART_CR1_RE | USART_CR1_RXNEIE; // Enable USART and RX interrupt
NVIC_EnableIRQ(USART1_IRQn);
}
RAMFUNC void USART1TxData(const uint8_t* data, uint16_t length)
{
NVIC_DisableIRQ(USART1_IRQn);
/* Check remaining space in buffer */
if(RINGBUFF_COUNT(txBuffer1) + length > RINGBUFF_SIZE(txBuffer1)){
NVIC_EnableIRQ(USART1_IRQn);
return;
}
/* Append data to buffer */
while(length--){
RINGBUFF_WRITE(txBuffer1, *(data++));
}
/* Start sending data */
USART1->CR1 |= USART_CR1_TXEIE;
NVIC_EnableIRQ(USART1_IRQn);
}
/////////////////////////////////////////////////////////////////
void CSerialPort::beginInt(uint8_t n, int speed)
{
switch (n) {
case 1U:
USART1Init(speed);
break;
default:
break;
}
}
int CSerialPort::availableForReadInt(uint8_t n)
{
switch (n) {
case 1U:
return !RINGBUFF_EMPTY(rxBuffer1);
default:
return false;
}
}
int CSerialPort::availableForWriteInt(uint8_t n)
{
switch (n) {
case 1U:
return !RINGBUFF_FULL(txBuffer1);
default:
return false;
}
}
uint8_t CSerialPort::readInt(uint8_t n)
{
switch (n) {
case 1U:
return RINGBUFF_READ(rxBuffer1);
default:
return 0U;
}
}
void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush)
{
bitband_t tc = (bitband_t)BITBAND_PERIPH(&USART1->SR, USART_SR_TC_Pos);
switch (n) {
case 1U:
USART1TxData(data, length);
*tc = 0;
if (flush) {
while (!RINGBUFF_EMPTY(txBuffer1) || !tc)
;
}
break;
default:
break;
}
}
#endif
+2
View File
@@ -1,4 +1,6 @@
/*
* Copyright (C) 2017 by Chris Huitema
*
* 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
-3
View File
@@ -23,9 +23,6 @@
#include "stm32f4xx.h"
#elif defined(STM32F7XX)
#include "stm32f7xx.h"
#elif defined(STM32F105xC)
#include "stm32f1xx.h"
#include <cstddef>
#else
#include <Arduino.h>
#endif
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2021,2022,2023,2025,2026 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
@@ -19,7 +19,6 @@
#if !defined(VERSION_H)
#define VERSION_H
#define VERSION "20210508"
#define VERSION "20260424"
#endif
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2017,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2017,2020,2024 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
@@ -39,7 +39,7 @@ const uint8_t NOAVEPTR = 99U;
const uint16_t NOENDPTR = 9999U;
const unsigned int MAX_SYNC_FRAMES = 1U + 1U;
const unsigned int MAX_SYNC_FRAMES = 4U + 1U;
CYSFRX::CYSFRX() :
m_state(YSFRXS_NONE),
+1 -1
View File
@@ -166,7 +166,7 @@ void CYSFTX::writeByte(uint8_t c)
void CYSFTX::writeSilence()
{
q15_t inBuffer[4U] = {0x00U, 0x00U, 0x00U, 0x00U};
q15_t inBuffer[4U] = {0, 0, 0, 0};
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U];
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
-98
View File
@@ -1,98 +0,0 @@
#!/bin/bash
###############################################################################
#
# mmdvmmenu.sh
#
# Copyright (C) 2016 by Paul Nannery KC2VRJ
#
# 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.
#
###############################################################################
#
# On a Linux based system, such as a Raspberry Pi, this script will perform
# Modification of the Config.h file for most options. It makes a Back up when
# you start the script if none is present. You must recompile and load firmware
# onto the Arduino Due if changes are made.
#
###############################################################################
#
# CONFIGURATION
#
# Location of Config.h
conf=Config.h
#Location of backup file
confbak=Config.h.bak
################################################################################
#
# Do not edit below here
#
###############################################################################
# Check for backup file and make one if not present
if [ ! -f $confbak ];then
cp -f $conf $confbak
fi
while :
do
clear
cat<<EOF
==============================================================
MMDVM Configuration Options
--------------------------------------------------------------
Please enter your choice:
(1) Enable 12.000 MHZ Clock
(2) Enable 12.288 MHZ Clock
(3) Enable 14.400 MHz Clock
(4) Enable 19.200 MHz Clock
(5) Use the COS to lockout the modem
(6) Use pins to output the current mode
(7) Use layout for the PAPA board
(8) Use layout for ZUM V1.0 and V1.0.1 boards
(9) Use layout for SP8NTH board
(0) Use modem as display driver
(A) Return to Default
(Q)uit
---------------------------------------------------------------
EOF
read -n1 -s
case "$REPLY" in
"1") sed -e 's/\/\/ #define EXTERNAL_OSC 12000000/#define EXTERNAL_OSC 12000000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "12.000 MHz clock enabled";;
"2") sed -e 's/\/\/ #define EXTERNAL_OSC 12288000/#define EXTERNAL_OSC 12288000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "12.288 MHz clock enabled";;
"3") sed -e 's/\/\/ #define EXTERNAL_OSC 14400000/#define EXTERNAL_OSC 14400000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "14.400 MHz clock enabled";;
"4") sed -e 's/\/\/ #define EXTERNAL_OSC 19200000/#define EXTERNAL_OSC 19200000/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "19.200 MHz clock enabled";;
"5") sed -e 's/\/\/ #define USE_COS_AS_LOCKOUT /#define USE_COS_AS_LOCKOUT/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "COS as Lockout enabled";;
"6") sed -e 's/\/\/ #define MODE_LEDS/#define MODE_LEDS/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Mode pins enabled";;
"7") sed -e 's/\/\/ #define ARDUINO_DUE_PAPA/#define ARDUINO_DUE_PAPA/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for the PAPA board enabled";;
"8") sed -e 's/\/\/ #define ARDUINO_DUE_ZUM_V10/#define ARDUINO_DUE_ZUM_V10/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for ZUM V1.0 and V1.0.1 boards enabled";;
"9") sed -e 's/\/\/ #define ARDUINO_DUE_NTH/#define ARDUINO_DUE_NTH/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Layout for SP8NTH board enabled";;
"0") sed -e 's/\/\/ #define SERIAL_REPEATER/#define SERIAL_REPEATER/' $conf > $conf.tmp && mv -f $conf.tmp $conf && echo "Modem display driver enabled";;
"A") mv -f $confbak $conf ;;
"a") mv -f $confbak $conf ;;
"Q") echo "If any changes are made you need to (re-)upload the firmware to MMDVM" && exit;;
"q") echo "If any changes are made you need to (re-)upload the firmware to MMDVM" && exit;;
* ) echo "invalid option" ;;
esac
sleep 1
done
+4 -4
View File
@@ -33,7 +33,7 @@ DSTAR PB6 output
DMR PB5 output
YSF PB7 output
POCSAG PC10 output
FM PC11 output
FM PB14 output
RX PB0 analog input
RSSI PB1 analog input
@@ -83,9 +83,9 @@ EXT_CLK PA15 input
#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_FM GPIO_Pin_14
#define PORT_FM GPIOB
#define RCC_Per_FM RCC_AHB1Periph_GPIOB
#define PIN_EXT_CLK GPIO_Pin_15
#define SRC_EXT_CLK GPIO_PinSource15
+2 -2
View File
@@ -20,7 +20,7 @@
#define _PINS_F7_STM32DVM_V5_H
/*
Pin definitions for STM32F4 STM32-DVM rev 5 Board:
Pin definitions for STM32F7 STM32-DVM rev 5 Board:
COS PB13 input
PTT PB12 output
@@ -33,6 +33,7 @@ DSTAR PB6 output
DMR PB5 output
YSF PB7 output
POCSAG PC10 output (Not Valid)
FM PB14 output
RX PB0 analog input
RSSI PB1 analog input
@@ -45,7 +46,6 @@ EXT_CLK PA15 input
#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
-187
View File
@@ -1,187 +0,0 @@
/* Linker script to configure memory regions.
* STM32F105RB
*/
MEMORY
{
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
/* Linker script to place sections and symbol values. Should be used together
* with other linker script that defines memory regions FLASH and RAM.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
*
* It defines following symbols, which code can use without definition:
* __exidx_start
* __exidx_end
* __copy_table_start__
* __copy_table_end__
* __zero_table_start__
* __zero_table_end__
* __etext
* __data_start__
* __preinit_array_start
* __preinit_array_end
* __init_array_start
* __init_array_end
* __fini_array_start
* __fini_array_end
* __data_end__
* __bss_start__
* __bss_end__
* __end__
* end
* __HeapLimit
* __StackLimit
* __StackTop
* __stack
*/
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
KEEP(*(.isr_vector))
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* To copy multiple ROM to RAM sections,
* uncomment .copy.table section and,
* define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */
/*
.copy.table :
{
. = ALIGN(4);
__copy_table_start__ = .;
LONG (__etext)
LONG (__data_start__)
LONG (__data_end__ - __data_start__)
LONG (__etext2)
LONG (__data2_start__)
LONG (__data2_end__ - __data2_start__)
__copy_table_end__ = .;
} > FLASH
*/
/* To clear multiple BSS sections,
* uncomment .zero.table section and,
* define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */
/*
.zero.table :
{
. = ALIGN(4);
__zero_table_start__ = .;
LONG (__bss_start__)
LONG (__bss_end__ - __bss_start__)
LONG (__bss2_start__)
LONG (__bss2_end__ - __bss2_start__)
__zero_table_end__ = .;
} > FLASH
*/
__etext = .;
.data : AT (__etext)
{
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM
.bss :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (COPY):
{
__end__ = .;
PROVIDE(end = .);
*(.heap*)
__HeapLimit = .;
} > RAM
/* .stack_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later */
.stack_dummy (COPY):
{
*(.stack*)
} > RAM
/* Set stack top to end of RAM, and stack limit move down by
* size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}
-427
View File
@@ -1,427 +0,0 @@
/* File: startup_ARMCM3.S
* Purpose: startup file for Cortex-M3 devices. Should use with
* GCC for ARM Embedded Processors
* Version: V2.0
* Date: 16 August 2013
*/
/* Copyright (c) 2011 - 2013 ARM LIMITED
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of ARM nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------*/
#if defined(STM32F105xC)
.syntax unified
.arch armv7-m
.section .stack
.align 3
#ifdef __STACK_SIZE
.equ Stack_Size, __STACK_SIZE
#else
.equ Stack_Size, 0xc00
#endif
.globl __StackTop
.globl __StackLimit
__StackLimit:
.space Stack_Size
.size __StackLimit, . - __StackLimit
__StackTop:
.size __StackTop, . - __StackTop
.section .heap
.align 3
#ifdef __HEAP_SIZE
.equ Heap_Size, __HEAP_SIZE
#else
.equ Heap_Size, 0
#endif
.globl __HeapBase
.globl __HeapLimit
__HeapBase:
.if Heap_Size
.space Heap_Size
.endif
.size __HeapBase, . - __HeapBase
__HeapLimit:
.size __HeapLimit, . - __HeapLimit
.equ BootRAM, 0xF1E0F85F
.section .isr_vector
.align 2
.globl __isr_vector
__isr_vector:
.long __StackTop /* Top of Stack */
.long Reset_Handler /* Reset Handler */
.long NMI_Handler /* NMI Handler */
.long HardFault_Handler /* Hard Fault Handler */
.long MemManage_Handler /* MPU Fault Handler */
.long BusFault_Handler /* Bus Fault Handler */
.long UsageFault_Handler /* Usage Fault Handler */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long 0 /* Reserved */
.long SVC_Handler /* SVCall Handler */
.long DebugMon_Handler /* Debug Monitor Handler */
.long 0 /* Reserved */
.long PendSV_Handler /* PendSV Handler */
.long SysTick_Handler /* SysTick Handler */
/* External interrupts */
.long WWDG_IRQHandler
.long PVD_IRQHandler
.long TAMPER_IRQHandler
.long RTC_IRQHandler
.long FLASH_IRQHandler
.long RCC_IRQHandler
.long EXTI0_IRQHandler
.long EXTI1_IRQHandler
.long EXTI2_IRQHandler
.long EXTI3_IRQHandler
.long EXTI4_IRQHandler
.long DMA1_Channel1_IRQHandler
.long DMA1_Channel2_IRQHandler
.long DMA1_Channel3_IRQHandler
.long DMA1_Channel4_IRQHandler
.long DMA1_Channel5_IRQHandler
.long DMA1_Channel6_IRQHandler
.long DMA1_Channel7_IRQHandler
.long ADC1_2_IRQHandler
.long CAN1_TX_IRQHandler
.long CAN1_RX0_IRQHandler
.long CAN1_RX1_IRQHandler
.long CAN1_SCE_IRQHandler
.long EXTI9_5_IRQHandler
.long TIM1_BRK_IRQHandler
.long TIM1_UP_IRQHandler
.long TIM1_TRG_COM_IRQHandler
.long TIM1_CC_IRQHandler
.long TIM2_IRQHandler
.long TIM3_IRQHandler
.long TIM4_IRQHandler
.long I2C1_EV_IRQHandler
.long I2C1_ER_IRQHandler
.long I2C2_EV_IRQHandler
.long I2C2_ER_IRQHandler
.long SPI1_IRQHandler
.long SPI2_IRQHandler
.long USART1_IRQHandler
.long USART2_IRQHandler
.long USART3_IRQHandler
.long EXTI15_10_IRQHandler
.long RTC_Alarm_IRQHandler
.long OTG_FS_WKUP_IRQHandler
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long TIM5_IRQHandler
.long SPI3_IRQHandler
.long UART4_IRQHandler
.long UART5_IRQHandler
.long TIM6_IRQHandler
.long TIM7_IRQHandler
.long DMA2_Channel1_IRQHandler
.long DMA2_Channel2_IRQHandler
.long DMA2_Channel3_IRQHandler
.long DMA2_Channel4_IRQHandler
.long DMA2_Channel5_IRQHandler
.long 0
.long 0
.long CAN2_TX_IRQHandler
.long CAN2_RX0_IRQHandler
.long CAN2_RX1_IRQHandler
.long CAN2_SCE_IRQHandler
.long OTG_FS_IRQHandler
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long BootRAM /* @0x1E0. This is for boot in RAM mode for
STM32F10x Connectivity line Devices. */
.size __isr_vector, . - __isr_vector
.text
.thumb
.thumb_func
.align 2
.globl Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Firstly it copies data from read only memory to RAM. There are two schemes
* to copy. One can copy more than one sections. Another can only copy
* one section. The former scheme needs more instructions and read-only
* data to implement than the latter.
* Macro __STARTUP_COPY_MULTIPLE is used to choose between two schemes. */
#ifdef __STARTUP_COPY_MULTIPLE
/* Multiple sections scheme.
*
* Between symbol address __copy_table_start__ and __copy_table_end__,
* there are array of triplets, each of which specify:
* offset 0: LMA of start of a section to copy from
* offset 4: VMA of start of a section to copy to
* offset 8: size of the section to copy. Must be multiply of 4
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r4, =__copy_table_start__
ldr r5, =__copy_table_end__
.L_loop0:
cmp r4, r5
bge .L_loop0_done
ldr r1, [r4]
ldr r2, [r4, #4]
ldr r3, [r4, #8]
.L_loop0_0:
subs r3, #4
ittt ge
ldrge r0, [r1, r3]
strge r0, [r2, r3]
bge .L_loop0_0
adds r4, #12
b .L_loop0
.L_loop0_done:
#else
/* Single section scheme.
*
* The ranges of copy from/to are specified by following symbols
* __etext: LMA of start of the section to copy from. Usually end of text
* __data_start__: VMA of start of the section to copy to
* __data_end__: VMA of end of the section to copy to
*
* All addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__etext
ldr r2, =__data_start__
ldr r3, =__data_end__
.L_loop1:
cmp r2, r3
ittt lt
ldrlt r0, [r1], #4
strlt r0, [r2], #4
blt .L_loop1
#endif /*__STARTUP_COPY_MULTIPLE */
/* This part of work usually is done in C library startup code. Otherwise,
* define this macro to enable it in this startup.
*
* There are two schemes too. One can clear multiple BSS sections. Another
* can only clear one section. The former is more size expensive than the
* latter.
*
* Define macro __STARTUP_CLEAR_BSS_MULTIPLE to choose the former.
* Otherwise efine macro __STARTUP_CLEAR_BSS to choose the later.
*/
#ifdef __STARTUP_CLEAR_BSS_MULTIPLE
/* Multiple sections scheme.
*
* Between symbol address __copy_table_start__ and __copy_table_end__,
* there are array of tuples specifying:
* offset 0: Start of a BSS section
* offset 4: Size of this BSS section. Must be multiply of 4
*/
ldr r3, =__zero_table_start__
ldr r4, =__zero_table_end__
.L_loop2:
cmp r3, r4
bge .L_loop2_done
ldr r1, [r3]
ldr r2, [r3, #4]
movs r0, 0
.L_loop2_0:
subs r2, #4
itt ge
strge r0, [r1, r2]
bge .L_loop2_0
adds r3, #8
b .L_loop2
.L_loop2_done:
#elif defined (__STARTUP_CLEAR_BSS)
/* Single BSS section scheme.
*
* The BSS section is specified by following symbols
* __bss_start__: start of the BSS section.
* __bss_end__: end of the BSS section.
*
* Both addresses must be aligned to 4 bytes boundary.
*/
ldr r1, =__bss_start__
ldr r2, =__bss_end__
movs r0, 0
.L_loop3:
cmp r1, r2
itt lt
strlt r0, [r1], #4
blt .L_loop3
#endif /* __STARTUP_CLEAR_BSS_MULTIPLE || __STARTUP_CLEAR_BSS */
#ifndef __NO_SYSTEM_INIT
bl SystemInit
#endif
#ifndef __START
#define __START _start
#endif
bl __START
.pool
.size Reset_Handler, . - Reset_Handler
.align 1
.thumb_func
.weak Default_Handler
.type Default_Handler, %function
Default_Handler:
b .
.size Default_Handler, . - Default_Handler
/* Macro to define default handlers. Default handler
* will be weak symbol and just dead loops. They can be
* overwritten by other handlers */
.macro def_irq_handler handler_name
.weak \handler_name
.set \handler_name, Default_Handler
.endm
def_irq_handler NMI_Handler
def_irq_handler HardFault_Handler
def_irq_handler MemManage_Handler
def_irq_handler BusFault_Handler
def_irq_handler UsageFault_Handler
def_irq_handler SVC_Handler
def_irq_handler DebugMon_Handler
def_irq_handler PendSV_Handler
def_irq_handler SysTick_Handler
def_irq_handler WWDG_IRQHandler
def_irq_handler PVD_IRQHandler
def_irq_handler TAMPER_IRQHandler
def_irq_handler RTC_IRQHandler
def_irq_handler FLASH_IRQHandler
def_irq_handler RCC_IRQHandler
def_irq_handler EXTI0_IRQHandler
def_irq_handler EXTI1_IRQHandler
def_irq_handler EXTI2_IRQHandler
def_irq_handler EXTI3_IRQHandler
def_irq_handler EXTI4_IRQHandler
def_irq_handler DMA1_Channel1_IRQHandler
def_irq_handler DMA1_Channel2_IRQHandler
def_irq_handler DMA1_Channel3_IRQHandler
def_irq_handler DMA1_Channel4_IRQHandler
def_irq_handler DMA1_Channel5_IRQHandler
def_irq_handler DMA1_Channel6_IRQHandler
def_irq_handler DMA1_Channel7_IRQHandler
def_irq_handler ADC1_2_IRQHandler
def_irq_handler CAN1_TX_IRQHandler
def_irq_handler CAN1_RX0_IRQHandler
def_irq_handler CAN1_RX1_IRQHandler
def_irq_handler CAN1_SCE_IRQHandler
def_irq_handler EXTI9_5_IRQHandler
def_irq_handler TIM1_BRK_IRQHandler
def_irq_handler TIM1_UP_IRQHandler
def_irq_handler TIM1_TRG_COM_IRQHandler
def_irq_handler TIM1_CC_IRQHandler
def_irq_handler TIM2_IRQHandler
def_irq_handler TIM3_IRQHandler
def_irq_handler TIM4_IRQHandler
def_irq_handler I2C1_EV_IRQHandler
def_irq_handler I2C1_ER_IRQHandler
def_irq_handler I2C2_EV_IRQHandler
def_irq_handler I2C2_ER_IRQHandler
def_irq_handler SPI1_IRQHandler
def_irq_handler SPI2_IRQHandler
def_irq_handler USART1_IRQHandler
def_irq_handler USART2_IRQHandler
def_irq_handler USART3_IRQHandler
def_irq_handler EXTI15_10_IRQHandler
def_irq_handler RTC_Alarm_IRQHandler
def_irq_handler OTG_FS_WKUP_IRQHandler
def_irq_handler TIM5_IRQHandler
def_irq_handler SPI3_IRQHandler
def_irq_handler UART4_IRQHandler
def_irq_handler UART5_IRQHandler
def_irq_handler TIM6_IRQHandler
def_irq_handler TIM7_IRQHandler
def_irq_handler DMA2_Channel1_IRQHandler
def_irq_handler DMA2_Channel2_IRQHandler
def_irq_handler DMA2_Channel3_IRQHandler
def_irq_handler DMA2_Channel4_IRQHandler
def_irq_handler DMA2_Channel5_IRQHandler
def_irq_handler CAN2_TX_IRQHandler
def_irq_handler CAN2_RX0_IRQHandler
def_irq_handler CAN2_RX1_IRQHandler
def_irq_handler CAN2_SCE_IRQHandler
def_irq_handler OTG_FS_IRQHandler
.end
#endif
-227
View File
@@ -1,227 +0,0 @@
/**
******************************************************************************
* @file system_stm32f1xx.c
* @author MCD Application Team, Wojciech Krutnik
* @version V2.2.2
* @date 26-June-2015
* @brief CMSIS Cortex-M0 Device Peripheral Access Layer System Source File.
*
* 1. This file provides two functions and one global variable to be called from
* user application:
* - SystemInit(): This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f1xx.s" file.
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
*
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
*
* 2. After each device reset the HSI (8 MHz) is used as system clock source.
* Then SystemInit() function is called, in "startup_stm32f1xx.s" file, to
* configure the system clock before to branch to main program.
*
* 3. This file configures the system clock and flash as follows:
*=============================================================================
* Supported STM32F1xx device
*-----------------------------------------------------------------------------
* System Clock source | PLL
*-----------------------------------------------------------------------------
* SYSCLK | 72MHz
*-----------------------------------------------------------------------------
* HCLK | 72Mhz
*-----------------------------------------------------------------------------
* PCLK1 | 36MHz
*-----------------------------------------------------------------------------
* PCLK2 | 72MHz
*-----------------------------------------------------------------------------
* AHB Prescaler | 1
*-----------------------------------------------------------------------------
* APB1 Prescaler | 2
*-----------------------------------------------------------------------------
* APB2 Prescaler | 1
*-----------------------------------------------------------------------------
* PLLSRC | HSE/1
*-----------------------------------------------------------------------------
* PLLMUL | 9
*-----------------------------------------------------------------------------
* HSE | 8MHz
*-----------------------------------------------------------------------------
* Flash Latency(WS) | 2
*-----------------------------------------------------------------------------
* Prefetch Buffer | ON
*-----------------------------------------------------------------------------
*=============================================================================
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
#if defined(STM32F105xC)
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f1xx_system
* @{
*/
/** @addtogroup STM32F1xx_System_Private_Includes
* @{
*/
#include "stm32f1xx.h"
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Defines
* @{
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSE_VALUE */
#if !defined (HSI_VALUE)
#define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSI_VALUE */
/**
* @}
*/
/** @addtogroup STM32f1xx_System_Private_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Variables
* @{
*/
uint32_t SystemCoreClock = 48000000;
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_FunctionPrototypes
* @{
*/
static void SetSysClock(void);
/**
* @}
*/
/** @addtogroup STM32F1xx_System_Private_Functions
* @{
*/
/**
* @brief Setup the microcontroller system.
* Initialize the default HSI clock source, vector table location and the PLL configuration is reset.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* Enable Prefetch Buffer, 2 wait states */
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY_1;
/* Configure the System clock frequency, AHB/APBx prescalers */
SetSysClock();
}
/**
* @brief Configures the System clock frequency and AHB/APBx prescalers
* settings.
* @param None
* @retval None
*/
static void SetSysClock(void)
{
/* Enable HSE and Clock Security System */
RCC->CR |= RCC_CR_CSSON | RCC_CR_HSEON;
/* Wait for HSE startup */
while((RCC->CR & RCC_CR_HSERDY) == 0)
;
/* Enable PLL with 9x multiplier and HSE clock source
* APB1 prescaler /2 */
RCC->CFGR = (RCC->CFGR & (~RCC_CFGR_PLLMULL)) | RCC_CFGR_PLLMULL9
| RCC_CFGR_PLLSRC;
RCC->CR |= RCC_CR_PLLON;
while((RCC->CR & RCC_CR_PLLRDY) == 0)
;
/* HCLK = SYSCLK */
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_HPRE) | RCC_CFGR_HPRE_DIV1;
/* PCLK1 = HCLK/2; PCLK2 = HCLK */
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_PPRE1|RCC_CFGR_PPRE2))
| RCC_CFGR_PPRE1_DIV2;
/* Switch SYSCLK to PLL */
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/