mirror of
https://github.com/g4klx/MMDVM.git
synced 2026-10-16 16:54:12 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b68017411 | ||
|
|
a2cf35629b | ||
|
|
2506690e98 | ||
|
|
7e15d1ac70 | ||
|
|
0fb75f974e | ||
|
|
99ac6146d9 | ||
|
|
6161d298f7 | ||
|
|
7f181133c7 | ||
|
|
1d6ca99df9 | ||
|
|
baed701a6c | ||
|
|
8b31a53e4e | ||
|
|
f6f1198873 | ||
|
|
4b17bcd991 | ||
|
|
852785b616 | ||
|
|
1034670ac4 | ||
|
|
ff8bf96abb | ||
|
|
edc53fc347 | ||
|
|
3760404af9 | ||
|
|
68dfdf0c21 | ||
|
|
5bd01645fa | ||
|
|
f31cceac18 | ||
|
|
1b48c4de1c | ||
|
|
a6ed1e9667 | ||
|
|
c7eb072d2f | ||
|
|
caa73c6c4a | ||
|
|
9386fb2e11 | ||
|
|
9c9b759db5 | ||
|
|
cfb6e31e2f | ||
|
|
4f6f519dc7 | ||
|
|
fe8e8329ad | ||
|
|
38bcf1f4c4 | ||
|
|
6a1c06576b | ||
|
|
27b3773f00 | ||
|
|
871c328914 | ||
|
|
fa251afdcb | ||
|
|
27618658a9 | ||
|
|
bb42bdc422 | ||
|
|
f15fcc17ec | ||
|
|
59a9b3018f | ||
|
|
07b7801821 | ||
|
|
3d430cbe44 | ||
|
|
fd27ffa464 | ||
|
|
42517d691e | ||
|
|
53f51820fa | ||
|
|
61a86b7f06 | ||
|
|
696964a3a4 | ||
|
|
23228a0027 | ||
|
|
b4bef6e7c3 | ||
|
|
d2548f9e84 | ||
|
|
4a01e9b3a1 | ||
|
|
8a245e7313 | ||
|
|
c132a58d2e | ||
|
|
9cf91ddc7f | ||
|
|
016ed333ec | ||
|
|
453fb33b76 | ||
|
|
9c1410eaae | ||
|
|
5d1a54eee5 | ||
|
|
4f516a827f | ||
|
|
acb2004843 | ||
|
|
0f43670066 | ||
|
|
ab3cdbc277 | ||
|
|
9efe38d825 | ||
|
|
132cde0864 | ||
|
|
bc969ff5e5 | ||
|
|
ee3f2a0e6d | ||
|
|
421031323d | ||
|
|
6118653053 | ||
|
|
1a5bc9cf8c | ||
|
|
06ef230ad5 | ||
|
|
9d4779e4c3 | ||
|
|
d5531177f2 | ||
|
|
f2c50083d8 | ||
|
|
838d813ab9 | ||
|
|
dffc6f0a5a | ||
|
|
7a042c10ad | ||
|
|
0c44b8ca25 | ||
|
|
6f29fe62b1 | ||
|
|
937c0b8b97 | ||
|
|
941d77bd51 | ||
|
|
6a0fa19c07 | ||
|
|
a14ebca9e0 | ||
|
|
61b4093aec | ||
|
|
555197b0a2 | ||
|
|
bb6a554919 | ||
|
|
c0941c4856 | ||
|
|
e93b264d9c | ||
|
|
fcdbb8cda0 | ||
|
|
a4ff8920e5 | ||
|
|
db1c01f4dc | ||
|
|
ab50521f74 | ||
|
|
5843ca0e3d | ||
|
|
6b0868b8f2 | ||
|
|
a49871cd8b | ||
|
|
78f5d34373 | ||
|
|
9bcaba0419 | ||
|
|
a25b634366 | ||
|
|
4039f576ae | ||
|
|
03aa405b41 | ||
|
|
546243eec6 | ||
|
|
a033ee66fd | ||
|
|
70dfb0964e | ||
|
|
ab09848afd | ||
|
|
ed7b57d618 | ||
|
|
e8010137f7 | ||
|
|
c2d5c31db1 | ||
|
|
5f2375e99e | ||
|
|
fd56870d22 | ||
|
|
5bc21fa192 | ||
|
|
33293258f3 | ||
|
|
aee9cac546 | ||
|
|
c9b553bc2e | ||
|
|
12dc9183da | ||
|
|
8c38341c17 | ||
|
|
418b63b9ca | ||
|
|
e697a91710 | ||
|
|
e04057a5b5 | ||
|
|
e17d771d41 | ||
|
|
38516074dd | ||
|
|
3814d4243c | ||
|
|
bcbfb66131 | ||
|
|
9560c77358 | ||
|
|
785cb5584b | ||
|
|
917b303a0f | ||
|
|
80d659d7d0 | ||
|
|
90111dc43d | ||
|
|
9110f5404f | ||
|
|
df1210eaea | ||
|
|
a9b761d1dc | ||
|
|
8c970b26db | ||
|
|
d89b68ff76 | ||
|
|
7fafd762d2 | ||
|
|
a6affb765e | ||
|
|
2f9eb3523d | ||
|
|
3b5e1afb54 | ||
|
|
8b019d49fd | ||
|
|
d92a00ae6e | ||
|
|
b83d13fe3a | ||
|
|
bc6c650973 | ||
|
|
1bb474dfef | ||
|
|
18ec4bafb6 | ||
|
|
b2c490e0bd | ||
|
|
4221ac45b1 | ||
|
|
ec5100bf6f | ||
|
|
ac4249bbb1 | ||
|
|
f3a3132a6c | ||
|
|
4bc0f36b49 | ||
|
|
9d1fbcfc73 | ||
|
|
609c2ead53 | ||
|
|
37daa04a6c | ||
|
|
20a4cf0378 | ||
|
|
693f517aec | ||
|
|
4549027e95 | ||
|
|
b3a1961279 | ||
|
|
afae6e552c | ||
|
|
5ee6a0d6db | ||
|
|
49e0d13967 | ||
|
|
53067d36d0 | ||
|
|
a5568efd51 | ||
|
|
d4d1f8282b | ||
|
|
79b0b141a2 | ||
|
|
850978a954 | ||
|
|
f97e53f1eb | ||
|
|
dfd2a4db38 | ||
|
|
eab91cca3a | ||
|
|
1e7c06de45 | ||
|
|
b15933bf8a | ||
|
|
df5ab3de60 | ||
|
|
9a7b07027a | ||
|
|
8b13a3c8a6 | ||
|
|
1ce71bcd62 | ||
|
|
ed13addd3d | ||
|
|
3e937a71d9 | ||
|
|
a59da0ebd2 | ||
|
|
33f4d35e2b | ||
|
|
048888f45b | ||
|
|
57fde3afcc | ||
|
|
8777c2b29c | ||
|
|
8330ed7dcf | ||
|
|
9f47e67630 | ||
|
|
d0f320a862 | ||
|
|
a05eaee855 | ||
|
|
4b5f496a0c | ||
|
|
fca53dc67e | ||
|
|
52fbd4f6ce | ||
|
|
d22c86e4d8 | ||
|
|
cc1b6d1367 | ||
|
|
2ff955631f | ||
|
|
f66e32fbb4 | ||
|
|
33527dd3e9 | ||
|
|
442dbe0b6a | ||
|
|
fc200c4cd7 | ||
|
|
7266f5c53b | ||
|
|
957d0d8d34 | ||
|
|
c69c573000 | ||
|
|
cb30a8d357 | ||
|
|
38f6d7d9d8 | ||
|
|
461e7c942e | ||
|
|
773cda818d | ||
|
|
625a6c1a79 | ||
|
|
414f3eb193 | ||
|
|
bef0d9cfd6 | ||
|
|
1a270642cd | ||
|
|
75eccb751e | ||
|
|
2c76d67098 | ||
|
|
0585d817cb | ||
|
|
5d0d2d0bd9 |
@@ -0,0 +1,6 @@
|
||||
*.o
|
||||
obj/
|
||||
bin/
|
||||
STM32F4XX_Lib/
|
||||
GitVersion.h
|
||||
build/
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,8 +17,6 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "CWIdTX.h"
|
||||
@@ -77,6 +75,8 @@ const struct {
|
||||
{'/', 0xEAE80000U, 16U},
|
||||
{'?', 0xAEEA0000U, 18U},
|
||||
{',', 0xEEAEE000U, 22U},
|
||||
{'-', 0xEAAE0000U, 18U},
|
||||
{'=', 0xEAB80000U, 16U},
|
||||
{' ', 0x00000000U, 4U},
|
||||
{0U, 0x00000000U, 0U}
|
||||
};
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "CalRSSI.h"
|
||||
#include "Utils.h"
|
||||
|
||||
CCalRSSI::CCalRSSI() :
|
||||
m_count(0U),
|
||||
m_accum(0U),
|
||||
m_min(0xFFFFU),
|
||||
m_max(0x0000U)
|
||||
{
|
||||
}
|
||||
|
||||
void CCalRSSI::samples(const uint16_t* rssi, uint8_t length)
|
||||
{
|
||||
for (uint16_t i = 0U; i < length; i++) {
|
||||
uint16_t ss = rssi[i];
|
||||
|
||||
m_accum += ss;
|
||||
|
||||
if (ss > m_max)
|
||||
m_max = ss;
|
||||
if (ss < m_min)
|
||||
m_min = ss;
|
||||
|
||||
m_count++;
|
||||
if (m_count >= 24000U) {
|
||||
uint16_t ave = m_accum / m_count;
|
||||
|
||||
uint8_t buffer[6U];
|
||||
buffer[0U] = (m_max >> 8) & 0xFFU;
|
||||
buffer[1U] = (m_max >> 0) & 0xFFU;
|
||||
buffer[2U] = (m_min >> 8) & 0xFFU;
|
||||
buffer[3U] = (m_min >> 0) & 0xFFU;
|
||||
buffer[4U] = (ave >> 8) & 0xFFU;
|
||||
buffer[5U] = (ave >> 0) & 0xFFU;
|
||||
|
||||
serial.writeRSSIData(buffer, 6U);
|
||||
|
||||
m_count = 0U;
|
||||
m_accum = 0U;
|
||||
m_min = 0xFFFFU;
|
||||
m_max = 0x0000U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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(CALRSSI_H)
|
||||
#define CALRSSI_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
class CCalRSSI {
|
||||
public:
|
||||
CCalRSSI();
|
||||
|
||||
void samples(const uint16_t* rssi, uint8_t length);
|
||||
|
||||
private:
|
||||
uint32_t m_count;
|
||||
uint32_t m_accum;
|
||||
uint16_t m_min;
|
||||
uint16_t m_max;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -47,13 +47,18 @@
|
||||
// For the ZUM V1.0 and V1.0.1 boards pin layout
|
||||
#define ARDUINO_DUE_ZUM_V10
|
||||
|
||||
// For the SQ6POG board
|
||||
// #define STM32F1_POG
|
||||
|
||||
// For the SP8NTH board
|
||||
// #define ARDUINO_DUE_NTH
|
||||
|
||||
// To use wider C4FSK filters for DMR, System Fusion and P25 on transmit
|
||||
// #define WIDE_C4FSK_FILTERS_TX
|
||||
// To use wider C4FSK filters for DMR, System Fusion and P25 on receive
|
||||
// #define WIDE_C4FSK_FILTERS_RX
|
||||
// For ST Nucleo-64 STM32F446RE board
|
||||
// #define STM32F4_NUCLEO_MORPHO_HEADER
|
||||
// #define STM32F4_NUCLEO_ARDUINO_HEADER
|
||||
|
||||
// Use separate mode pins to switch external filters/bandwidth for example
|
||||
// #define STM32F4_NUCLEO_MODE_PINS
|
||||
|
||||
// Pass RSSI information to the host
|
||||
// #define SEND_RSSI_DATA
|
||||
@@ -62,4 +67,3 @@
|
||||
// #define SERIAL_REPEATER
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+70
-83
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,8 +16,6 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DMRDMORX.h"
|
||||
@@ -59,8 +57,7 @@ m_colorCode(0U),
|
||||
m_state(DMORXS_NONE),
|
||||
m_n(0U),
|
||||
m_type(0U),
|
||||
m_rssiCount(0U),
|
||||
m_rssi(0U)
|
||||
m_rssi()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -73,7 +70,6 @@ void CDMRDMORX::reset()
|
||||
m_state = DMORXS_NONE;
|
||||
m_startPtr = 0U;
|
||||
m_endPtr = NOENDPTR;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CDMRDMORX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
||||
@@ -89,6 +85,7 @@ void CDMRDMORX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t leng
|
||||
bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
{
|
||||
m_buffer[m_dataPtr] = sample;
|
||||
m_rssi[m_dataPtr] = rssi;
|
||||
|
||||
m_bitBuffer[m_bitPtr] <<= 1;
|
||||
if (sample < 0)
|
||||
@@ -97,9 +94,6 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
if (m_state == DMORXS_NONE) {
|
||||
correlateSync(true);
|
||||
} else {
|
||||
// Grab the RSSI data during the frame
|
||||
if (m_state == DMORXS_VOICE && m_dataPtr == m_syncPtr)
|
||||
m_rssi = rssi;
|
||||
|
||||
uint16_t min = m_syncPtr + DMO_BUFFER_LENGTH_SAMPLES - 1U;
|
||||
uint16_t max = m_syncPtr + 1U;
|
||||
@@ -148,7 +142,7 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
switch (dataType) {
|
||||
case DT_DATA_HEADER:
|
||||
DEBUG4("DMRDMORX: data header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_state = DMORXS_DATA;
|
||||
m_type = 0x00U;
|
||||
break;
|
||||
@@ -157,32 +151,32 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
case DT_RATE_1_DATA:
|
||||
if (m_state == DMORXS_DATA) {
|
||||
DEBUG4("DMRDMORX: data payload found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_type = dataType;
|
||||
}
|
||||
break;
|
||||
case DT_VOICE_LC_HEADER:
|
||||
DEBUG4("DMRDMORX: voice header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_state = DMORXS_VOICE;
|
||||
break;
|
||||
case DT_VOICE_PI_HEADER:
|
||||
if (m_state == DMORXS_VOICE) {
|
||||
DEBUG4("DMRDMORX: voice pi header found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
}
|
||||
m_state = DMORXS_VOICE;
|
||||
break;
|
||||
case DT_TERMINATOR_WITH_LC:
|
||||
if (m_state == DMORXS_VOICE) {
|
||||
DEBUG4("DMRDMORX: voice terminator found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
reset();
|
||||
}
|
||||
break;
|
||||
default: // DT_CSBK
|
||||
DEBUG4("DMRDMORX: csbk found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
reset();
|
||||
break;
|
||||
}
|
||||
@@ -190,22 +184,7 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
} else if (m_control == CONTROL_VOICE) {
|
||||
// Voice sync
|
||||
DEBUG4("DMRDMORX: voice sync found pos/centre/threshold", m_syncPtr, centre, threshold);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
// Send RSSI data approximately every second
|
||||
if (m_rssiCount == 2U) {
|
||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
||||
m_rssiCount++;
|
||||
if (m_rssiCount >= 16U)
|
||||
m_rssiCount = 0U;
|
||||
#else
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
writeRSSIData(frame);
|
||||
m_state = DMORXS_VOICE;
|
||||
m_syncCount = 0U;
|
||||
m_n = 0U;
|
||||
@@ -225,26 +204,12 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
} else {
|
||||
frame[0U] = ++m_n;
|
||||
}
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
// Send RSSI data approximately every second
|
||||
if (m_rssiCount == 2U) {
|
||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
||||
m_rssiCount++;
|
||||
if (m_rssiCount >= 16U)
|
||||
m_rssiCount = 0U;
|
||||
#else
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
} else if (m_state == DMORXS_DATA) {
|
||||
if (m_type != 0x00U) {
|
||||
frame[0U] = CONTROL_DATA | m_type;
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,16 +232,13 @@ bool CDMRDMORX::processSample(q15_t sample, uint16_t rssi)
|
||||
|
||||
void CDMRDMORX::correlateSync(bool first)
|
||||
{
|
||||
uint8_t errs1 = countBits32((m_bitBuffer[m_bitPtr] & DMR_SYNC_SYMBOLS_MASK) ^ DMR_S2_DATA_SYNC_SYMBOLS);
|
||||
uint8_t errs2 = countBits32((m_bitBuffer[m_bitPtr] & DMR_SYNC_SYMBOLS_MASK) ^ DMR_MS_DATA_SYNC_SYMBOLS);
|
||||
uint8_t errs = countBits32((m_bitBuffer[m_bitPtr] & DMR_SYNC_SYMBOLS_MASK) ^ DMR_MS_DATA_SYNC_SYMBOLS);
|
||||
|
||||
// The voice sync is the complement of the data sync
|
||||
bool data1 = (errs1 <= MAX_SYNC_SYMBOLS_ERRS);
|
||||
bool data2 = (errs2 <= MAX_SYNC_SYMBOLS_ERRS);
|
||||
bool voice1 = (errs1 >= (DMR_SYNC_LENGTH_SYMBOLS - MAX_SYNC_SYMBOLS_ERRS));
|
||||
bool voice2 = (errs2 >= (DMR_SYNC_LENGTH_SYMBOLS - MAX_SYNC_SYMBOLS_ERRS));
|
||||
bool data = (errs <= MAX_SYNC_SYMBOLS_ERRS);
|
||||
bool voice = (errs >= (DMR_SYNC_LENGTH_SYMBOLS - MAX_SYNC_SYMBOLS_ERRS));
|
||||
|
||||
if (data1 || data2 || voice1 || voice2) {
|
||||
if (data || voice) {
|
||||
uint16_t ptr = m_dataPtr + DMO_BUFFER_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES + DMR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DMO_BUFFER_LENGTH_SAMPLES)
|
||||
ptr -= DMO_BUFFER_LENGTH_SAMPLES;
|
||||
@@ -285,23 +247,34 @@ void CDMRDMORX::correlateSync(bool first)
|
||||
q15_t min = 16000;
|
||||
q15_t max = -16000;
|
||||
|
||||
uint32_t mask = 0x00800000U;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++, mask >>= 1) {
|
||||
bool b;
|
||||
if (data1 || voice1)
|
||||
b = (DMR_S2_DATA_SYNC_SYMBOLS & mask) == mask;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_buffer[ptr];
|
||||
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
|
||||
int8_t corrVal;
|
||||
if (data)
|
||||
corrVal = DMR_MS_DATA_SYNC_SYMBOLS_VALUES[i];
|
||||
else
|
||||
b = (DMR_MS_DATA_SYNC_SYMBOLS & mask) == mask;
|
||||
corrVal = DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[i];
|
||||
|
||||
if (m_buffer[ptr] > max)
|
||||
max = m_buffer[ptr];
|
||||
if (m_buffer[ptr] < min)
|
||||
min = m_buffer[ptr];
|
||||
|
||||
if (data1 || data2)
|
||||
corr += b ? -m_buffer[ptr] : m_buffer[ptr];
|
||||
else // if (voice)
|
||||
corr += b ? m_buffer[ptr] : -m_buffer[ptr];
|
||||
switch (corrVal) {
|
||||
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 += DMR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DMO_BUFFER_LENGTH_SAMPLES)
|
||||
@@ -321,21 +294,16 @@ void CDMRDMORX::correlateSync(bool first)
|
||||
|
||||
samplesToBits(ptr, DMR_SYNC_LENGTH_SYMBOLS, sync, 4U, centre, threshold);
|
||||
|
||||
if (data1 || data2) {
|
||||
if (data) {
|
||||
uint8_t errs = 0U;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) {
|
||||
if (data1)
|
||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_S2_DATA_SYNC_BYTES[i]);
|
||||
else
|
||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]);
|
||||
}
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
|
||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]);
|
||||
|
||||
if (errs <= MAX_SYNC_BYTES_ERRS) {
|
||||
if (first) {
|
||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||
m_averagePtr = 0U;
|
||||
m_rssiCount = 0U;
|
||||
} else {
|
||||
m_threshold[m_averagePtr] = threshold;
|
||||
m_centre[m_averagePtr] = centre;
|
||||
@@ -359,19 +327,14 @@ void CDMRDMORX::correlateSync(bool first)
|
||||
}
|
||||
} else { // if (voice1 || voice2)
|
||||
uint8_t errs = 0U;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) {
|
||||
if (voice1)
|
||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_S2_VOICE_SYNC_BYTES[i]);
|
||||
else
|
||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]);
|
||||
}
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
|
||||
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]);
|
||||
|
||||
if (errs <= MAX_SYNC_BYTES_ERRS) {
|
||||
if (first) {
|
||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||
m_averagePtr = 0U;
|
||||
m_rssiCount = 0U;
|
||||
} else {
|
||||
m_threshold[m_averagePtr] = threshold;
|
||||
m_centre[m_averagePtr] = centre;
|
||||
@@ -436,3 +399,27 @@ void CDMRDMORX::setColorCode(uint8_t colorCode)
|
||||
m_colorCode = colorCode;
|
||||
}
|
||||
|
||||
void CDMRDMORX::writeRSSIData(uint8_t* frame)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
// Calculate RSSI average over a burst period. We don't take into account 2.5 ms at the beginning and 2.5 ms at the end
|
||||
uint16_t start = m_startPtr + DMR_SYNC_LENGTH_SAMPLES / 2U;
|
||||
|
||||
uint32_t accum = 0U;
|
||||
for (uint16_t i = 0U; i < (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES); i++) {
|
||||
accum += m_rssi[start];
|
||||
|
||||
start++;
|
||||
if (start >= DMO_BUFFER_LENGTH_SAMPLES)
|
||||
start -= DMO_BUFFER_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
uint16_t avg = accum / (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES);
|
||||
frame[34U] = (avg >> 8) & 0xFFU;
|
||||
frame[35U] = (avg >> 0) & 0xFFU;
|
||||
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||
#else
|
||||
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -58,13 +58,12 @@ private:
|
||||
DMORX_STATE m_state;
|
||||
uint8_t m_n;
|
||||
uint8_t m_type;
|
||||
uint16_t m_rssiCount;
|
||||
uint16_t m_rssi;
|
||||
uint16_t m_rssi[DMO_BUFFER_LENGTH_SAMPLES];
|
||||
|
||||
bool processSample(q15_t sample, uint16_t rssi);
|
||||
void correlateSync(bool first);
|
||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
void writeRSSIData(uint8_t* frame);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+42
-56
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
* 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
|
||||
@@ -17,30 +18,27 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DMRSlotType.h"
|
||||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 22U;
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 42U;
|
||||
#endif
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640};
|
||||
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213};
|
||||
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213};
|
||||
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640};
|
||||
const q15_t DMR_LEVELA = 1362;
|
||||
const q15_t DMR_LEVELB = 454;
|
||||
const q15_t DMR_LEVELC = -454;
|
||||
const q15_t DMR_LEVELD = -1362;
|
||||
|
||||
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])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint8_t DMR_SYNC = 0x5FU;
|
||||
|
||||
CDMRDMOTX::CDMRDMOTX() :
|
||||
m_fifo(),
|
||||
@@ -49,14 +47,14 @@ m_modState(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_count(0U)
|
||||
m_txDelay(240U) // 200ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = DMR_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
|
||||
m_modFilter.L = DMR_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = RRC_0_2_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
void CDMRDMOTX::process()
|
||||
@@ -64,13 +62,17 @@ void CDMRDMOTX::process()
|
||||
if (m_poLen == 0U && m_fifo.getData() > 0U) {
|
||||
if (!m_tx) {
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = 0x00U;
|
||||
m_poBuffer[i] = DMR_SYNC;
|
||||
|
||||
m_poLen = m_txDelay;
|
||||
} else {
|
||||
for (unsigned int i = 0U; i < 72U; i++)
|
||||
m_poBuffer[m_poLen++] = 0x00U;
|
||||
m_poBuffer[i] = DMR_SYNC;
|
||||
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++)
|
||||
m_poBuffer[i] = m_fifo.get();
|
||||
m_poBuffer[i + 39U] = m_fifo.get();
|
||||
|
||||
m_poLen = 72U;
|
||||
}
|
||||
|
||||
m_poPtr = 0U;
|
||||
@@ -112,53 +114,34 @@ uint8_t CDMRDMOTX::writeData(const uint8_t* data, uint8_t length)
|
||||
|
||||
void CDMRDMOTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELC;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_DMR, outBuffer, blockSize);
|
||||
io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
uint16_t CDMRDMOTX::getSpace() const
|
||||
uint8_t CDMRDMOTX::getSpace() const
|
||||
{
|
||||
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
@@ -166,5 +149,8 @@ uint16_t CDMRDMOTX::getSpace() const
|
||||
void CDMRDMOTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -35,17 +35,16 @@ public:
|
||||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_fifo;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[80U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
uint32_t m_count;
|
||||
CSerialRB m_fifo;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_txDelay;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
||||
+12
-12
@@ -61,32 +61,32 @@ const uint8_t DMR_MS_DATA_SYNC_BYTES[] = {0x0DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0
|
||||
const uint8_t DMR_MS_VOICE_SYNC_BYTES[] = {0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U};
|
||||
const uint8_t DMR_BS_DATA_SYNC_BYTES[] = {0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U};
|
||||
const uint8_t DMR_BS_VOICE_SYNC_BYTES[] = {0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U};
|
||||
const uint8_t DMR_S1_DATA_SYNC_BYTES[] = {0x0FU, 0x7FU, 0xDDU, 0x5DU, 0xDFU, 0xD5U, 0x50U};
|
||||
const uint8_t DMR_S1_VOICE_SYNC_BYTES[] = {0x05U, 0xD5U, 0x77U, 0xF7U, 0x75U, 0x7FU, 0xF0U};
|
||||
const uint8_t DMR_S2_DATA_SYNC_BYTES[] = {0x0DU, 0x75U, 0x57U, 0xF5U, 0xFFU, 0x7FU, 0x50U};
|
||||
const uint8_t DMR_S2_VOICE_SYNC_BYTES[] = {0x07U, 0xDFU, 0xFDU, 0x5FU, 0x55U, 0xD5U, 0xF0U};
|
||||
const uint8_t DMR_SYNC_BYTES_MASK[] = {0x0FU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xF0U};
|
||||
|
||||
const uint64_t DMR_MS_DATA_SYNC_BITS = 0x0000D5D7F77FD757U;
|
||||
const uint64_t DMR_MS_VOICE_SYNC_BITS = 0x00007F7D5DD57DFDU;
|
||||
const uint64_t DMR_BS_DATA_SYNC_BITS = 0x0000DFF57D75DF5DU;
|
||||
const uint64_t DMR_BS_VOICE_SYNC_BITS = 0x0000755FD7DF75F7U;
|
||||
const uint64_t DMR_S1_DATA_SYNC_BITS = 0x0000F7FDD5DDFD55U;
|
||||
const uint64_t DMR_S1_VOICE_SYNC_BITS = 0x00005D577F7757FFU;
|
||||
const uint64_t DMR_S2_DATA_SYNC_BITS = 0x0000D7557F5FF7F5U;
|
||||
const uint64_t DMR_S2_VOICE_SYNC_BITS = 0x00007DFFD5F55D5FU;
|
||||
const uint64_t DMR_SYNC_BITS_MASK = 0x0000FFFFFFFFFFFFU;
|
||||
|
||||
const uint32_t DMR_MS_DATA_SYNC_SYMBOLS = 0x0076286EU;
|
||||
const uint32_t DMR_MS_VOICE_SYNC_SYMBOLS = 0x0089D791U;
|
||||
const uint32_t DMR_BS_DATA_SYNC_SYMBOLS = 0x00439B4DU;
|
||||
const uint32_t DMR_BS_VOICE_SYNC_SYMBOLS = 0x00BC64B2U;
|
||||
const uint32_t DMR_S1_DATA_SYNC_SYMBOLS = 0x0021751FU;
|
||||
const uint32_t DMR_S1_VOICE_SYNC_SYMBOLS = 0x00DE8AE0U;
|
||||
const uint32_t DMR_S2_DATA_SYNC_SYMBOLS = 0x006F8C23U;
|
||||
const uint32_t DMR_S2_VOICE_SYNC_SYMBOLS = 0x009073DCU;
|
||||
const uint32_t DMR_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
|
||||
|
||||
// D 5 D 7 F 7 7 F D 7 5 7
|
||||
// 11 01 01 01 11 01 01 11 11 11 01 11 01 11 11 11 11 01 01 11 01 01 01 11
|
||||
// -3 +3 +3 +3 -3 +3 +3 -3 -3 -3 +3 -3 +3 -3 -3 -3 -3 +3 +3 -3 +3 +3 +3 -3
|
||||
|
||||
const int8_t DMR_MS_DATA_SYNC_SYMBOLS_VALUES[] = {-3, +3, +3, +3, -3, +3, +3, -3, -3, -3, +3, -3, +3, -3, -3, -3, -3, +3, +3, -3, +3, +3, +3, -3};
|
||||
|
||||
// 7 F 7 D 5 D D 5 7 D F D
|
||||
// 01 11 11 11 01 11 11 01 01 01 11 01 11 01 01 01 01 11 11 01 11 11 11 01
|
||||
// +3 -3 -3 -3 +3 -3 -3 +3 +3 +3 -3 +3 -3 +3 +3 +3 +3 -3 -3 +3 -3 -3 -3 +3
|
||||
|
||||
const int8_t DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[] = {+3, -3, -3, -3, +3, -3, -3, +3, +3, +3, -3, +3, -3, +3, +3, +3, +3, -3, -3, +3, -3, -3, -3, +3};
|
||||
|
||||
const uint8_t DT_VOICE_PI_HEADER = 0U;
|
||||
const uint8_t DT_VOICE_LC_HEADER = 1U;
|
||||
const uint8_t DT_TERMINATOR_WITH_LC = 2U;
|
||||
|
||||
+22
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,8 +16,6 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DMRIdleRX.h"
|
||||
@@ -84,16 +82,28 @@ void CDMRIdleRX::processSample(q15_t sample)
|
||||
q15_t max = -16000;
|
||||
q15_t min = 16000;
|
||||
|
||||
uint32_t mask = 0x00800000U;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++, mask >>= 1) {
|
||||
bool b = (DMR_MS_DATA_SYNC_SYMBOLS & mask) == mask;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_buffer[ptr];
|
||||
|
||||
if (m_buffer[ptr] > max)
|
||||
max = m_buffer[ptr];
|
||||
if (m_buffer[ptr] < min)
|
||||
min = m_buffer[ptr];
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
|
||||
corr += b ? -m_buffer[ptr] : m_buffer[ptr];
|
||||
switch (DMR_MS_DATA_SYNC_SYMBOLS_VALUES[i]) {
|
||||
case +3:
|
||||
corr -= (val + val + val);
|
||||
break;
|
||||
case +1:
|
||||
corr -= val;
|
||||
break;
|
||||
case -1:
|
||||
corr += val;
|
||||
break;
|
||||
default: // -3
|
||||
corr += (val + val + val);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += DMR_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= DMR_FRAME_LENGTH_SAMPLES)
|
||||
@@ -150,7 +160,7 @@ void CDMRIdleRX::processSample(q15_t sample)
|
||||
}
|
||||
|
||||
m_endPtr = NOENDPTR;
|
||||
m_maxCorr = 0U;
|
||||
m_maxCorr = 0;
|
||||
}
|
||||
|
||||
m_dataPtr++;
|
||||
@@ -199,4 +209,3 @@ void CDMRIdleRX::setColorCode(uint8_t colorCode)
|
||||
{
|
||||
m_colorCode = colorCode;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -43,8 +43,8 @@ private:
|
||||
q15_t m_threshold;
|
||||
uint8_t m_colorCode;
|
||||
|
||||
void processSample(q15_t sample);
|
||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
void processSample(q15_t sample);
|
||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+55
-58
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,8 +16,6 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DMRSlotRX.h"
|
||||
@@ -65,8 +63,7 @@ m_delay(0U),
|
||||
m_state(DMRRXS_NONE),
|
||||
m_n(0U),
|
||||
m_type(0U),
|
||||
m_rssiCount(0U),
|
||||
m_rssi(0U)
|
||||
m_rssi()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -91,7 +88,6 @@ void CDMRSlotRX::reset()
|
||||
m_state = DMRRXS_NONE;
|
||||
m_startPtr = 0U;
|
||||
m_endPtr = NOENDPTR;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
@@ -105,6 +101,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
return m_state != DMRRXS_NONE;
|
||||
|
||||
m_buffer[m_dataPtr] = sample;
|
||||
m_rssi[m_dataPtr] = rssi;
|
||||
|
||||
m_bitBuffer[m_bitPtr] <<= 1;
|
||||
if (sample < 0)
|
||||
@@ -114,9 +111,6 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
if (m_dataPtr >= SCAN_START && m_dataPtr <= SCAN_END)
|
||||
correlateSync(true);
|
||||
} else {
|
||||
// Grab the RSSI data during the frame
|
||||
if (m_state == DMRRXS_VOICE && m_dataPtr == m_syncPtr)
|
||||
m_rssi = rssi;
|
||||
|
||||
uint16_t min = m_syncPtr - 1U;
|
||||
uint16_t max = m_syncPtr + 1U;
|
||||
@@ -151,7 +145,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
switch (dataType) {
|
||||
case DT_DATA_HEADER:
|
||||
DEBUG5("DMRSlotRX: data header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_state = DMRRXS_DATA;
|
||||
m_type = 0x00U;
|
||||
break;
|
||||
@@ -160,33 +154,33 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
case DT_RATE_1_DATA:
|
||||
if (m_state == DMRRXS_DATA) {
|
||||
DEBUG5("DMRSlotRX: data payload found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_type = dataType;
|
||||
}
|
||||
break;
|
||||
case DT_VOICE_LC_HEADER:
|
||||
DEBUG5("DMRSlotRX: voice header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_state = DMRRXS_VOICE;
|
||||
break;
|
||||
case DT_VOICE_PI_HEADER:
|
||||
if (m_state == DMRRXS_VOICE) {
|
||||
DEBUG5("DMRSlotRX: voice pi header found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
}
|
||||
m_state = DMRRXS_VOICE;
|
||||
break;
|
||||
case DT_TERMINATOR_WITH_LC:
|
||||
if (m_state == DMRRXS_VOICE) {
|
||||
DEBUG5("DMRSlotRX: voice terminator found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_state = DMRRXS_NONE;
|
||||
m_endPtr = NOENDPTR;
|
||||
}
|
||||
break;
|
||||
default: // DT_CSBK
|
||||
DEBUG5("DMRSlotRX: csbk found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
m_state = DMRRXS_NONE;
|
||||
m_endPtr = NOENDPTR;
|
||||
break;
|
||||
@@ -195,22 +189,7 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
} else if (m_control == CONTROL_VOICE) {
|
||||
// Voice sync
|
||||
DEBUG5("DMRSlotRX: voice sync found slot/pos/centre/threshold", m_slot ? 2U : 1U, m_syncPtr, centre, threshold);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
// Send RSSI data approximately every second
|
||||
if (m_rssiCount == 2U) {
|
||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
||||
m_rssiCount++;
|
||||
if (m_rssiCount >= 16U)
|
||||
m_rssiCount = 0U;
|
||||
#else
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
writeRSSIData(frame);
|
||||
m_state = DMRRXS_VOICE;
|
||||
m_syncCount = 0U;
|
||||
m_n = 0U;
|
||||
@@ -231,26 +210,12 @@ bool CDMRSlotRX::processSample(q15_t sample, uint16_t rssi)
|
||||
} else {
|
||||
frame[0U] = ++m_n;
|
||||
}
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
// Send RSSI data approximately every second
|
||||
if (m_rssiCount == 2U) {
|
||||
frame[34U] = (m_rssi >> 8) & 0xFFU;
|
||||
frame[35U] = (m_rssi >> 0) & 0xFFU;
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
||||
m_rssiCount++;
|
||||
if (m_rssiCount >= 16U)
|
||||
m_rssiCount = 0U;
|
||||
#else
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
} else if (m_state == DMRRXS_DATA) {
|
||||
if (m_type != 0x00U) {
|
||||
frame[0U] = CONTROL_DATA | m_type;
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
writeRSSIData(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,19 +245,34 @@ void CDMRSlotRX::correlateSync(bool first)
|
||||
q15_t min = 16000;
|
||||
q15_t max = -16000;
|
||||
|
||||
uint32_t mask = 0x00800000U;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++, mask >>= 1) {
|
||||
bool b = (DMR_MS_DATA_SYNC_SYMBOLS & mask) == mask;
|
||||
for (uint8_t i = 0U; i < DMR_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_buffer[ptr];
|
||||
|
||||
if (m_buffer[ptr] > max)
|
||||
max = m_buffer[ptr];
|
||||
if (m_buffer[ptr] < min)
|
||||
min = m_buffer[ptr];
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
|
||||
int8_t corrVal;
|
||||
if (data)
|
||||
corr += b ? -m_buffer[ptr] : m_buffer[ptr];
|
||||
else // if (voice)
|
||||
corr += b ? m_buffer[ptr] : -m_buffer[ptr];
|
||||
corrVal = DMR_MS_DATA_SYNC_SYMBOLS_VALUES[i];
|
||||
else
|
||||
corrVal = DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[i];
|
||||
|
||||
switch (corrVal) {
|
||||
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 += DMR_RADIO_SYMBOL_LENGTH;
|
||||
}
|
||||
@@ -317,7 +297,6 @@ void CDMRSlotRX::correlateSync(bool first)
|
||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||
m_averagePtr = 0U;
|
||||
m_rssiCount = 0U;
|
||||
} else {
|
||||
m_threshold[m_averagePtr] = threshold;
|
||||
m_centre[m_averagePtr] = centre;
|
||||
@@ -343,7 +322,6 @@ void CDMRSlotRX::correlateSync(bool first)
|
||||
m_threshold[0U] = m_threshold[1U] = m_threshold[2U] = m_threshold[3U] = threshold;
|
||||
m_centre[0U] = m_centre[1U] = m_centre[2U] = m_centre[3U] = centre;
|
||||
m_averagePtr = 0U;
|
||||
m_rssiCount = 0U;
|
||||
} else {
|
||||
m_threshold[m_averagePtr] = threshold;
|
||||
m_centre[m_averagePtr] = centre;
|
||||
@@ -403,3 +381,22 @@ void CDMRSlotRX::setDelay(uint8_t delay)
|
||||
m_delay = delay;
|
||||
}
|
||||
|
||||
void CDMRSlotRX::writeRSSIData(uint8_t* frame)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
// Calculate RSSI average over a burst period. We don't take into account 2.5 ms at the beginning and 2.5 ms at the end
|
||||
uint16_t start = m_startPtr + DMR_SYNC_LENGTH_SAMPLES / 2U;
|
||||
|
||||
uint32_t accum = 0U;
|
||||
for (uint16_t i = 0U; i < (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES); i++)
|
||||
accum += m_rssi[start++];
|
||||
|
||||
uint16_t avg = accum / (DMR_FRAME_LENGTH_SAMPLES - DMR_SYNC_LENGTH_SAMPLES);
|
||||
frame[34U] = (avg >> 8) & 0xFFU;
|
||||
frame[35U] = (avg >> 0) & 0xFFU;
|
||||
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 3U);
|
||||
#else
|
||||
serial.writeDMRData(m_slot, frame, DMR_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -62,12 +62,11 @@ private:
|
||||
DMRRX_STATE m_state;
|
||||
uint8_t m_n;
|
||||
uint8_t m_type;
|
||||
uint16_t m_rssiCount;
|
||||
uint16_t m_rssi;
|
||||
uint16_t m_rssi[900U];
|
||||
|
||||
void correlateSync(bool first);
|
||||
void samplesToBits(uint16_t start, uint8_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
void writeRSSIData(uint8_t* frame);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
* 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
|
||||
@@ -17,31 +18,22 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DMRSlotType.h"
|
||||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 22U;
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t DMR_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t DMR_C4FSK_FILTER_LEN = 42U;
|
||||
#endif
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DMR_LEVELA[] = { 640, 640 , 640, 640, 640};
|
||||
const q15_t DMR_LEVELB[] = { 213, 213, 213, 213, 213};
|
||||
const q15_t DMR_LEVELC[] = {-213, -213, -213, -213, -213};
|
||||
const q15_t DMR_LEVELD[] = {-640, -640, -640, -640, -640};
|
||||
const q15_t DMR_LEVELA = 1362;
|
||||
const q15_t DMR_LEVELB = 454;
|
||||
const q15_t DMR_LEVELC = -454;
|
||||
const q15_t DMR_LEVELD = -1362;
|
||||
|
||||
// The PR FILL and Data Sync pattern.
|
||||
// The PR FILL and BS Data Sync pattern.
|
||||
const uint8_t IDLE_DATA[] =
|
||||
{0x53U, 0xC2U, 0x5EU, 0xABU, 0xA8U, 0x67U, 0x1DU, 0xC7U, 0x38U, 0x3BU, 0xD9U,
|
||||
0x36U, 0x00U, 0x0DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD0U, 0x03U, 0xF6U,
|
||||
@@ -61,6 +53,8 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02
|
||||
#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])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint32_t STARTUP_COUNT = 20U;
|
||||
|
||||
CDMRTX::CDMRTX() :
|
||||
m_fifo(),
|
||||
m_modFilter(),
|
||||
@@ -74,14 +68,15 @@ m_markBuffer(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_count(0U),
|
||||
m_frameCount(0U),
|
||||
m_abort()
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = DMR_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.pCoeffs = DMR_C4FSK_FILTER;
|
||||
m_modFilter.L = DMR_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = RRC_0_2_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
|
||||
::memcpy(m_newShortLC, EMPTY_SHORT_LC, 12U);
|
||||
::memcpy(m_shortLC, EMPTY_SHORT_LC, 12U);
|
||||
@@ -231,7 +226,7 @@ void CDMRTX::setStart(bool start)
|
||||
{
|
||||
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
|
||||
|
||||
m_count = 0U;
|
||||
m_frameCount = 0U;
|
||||
|
||||
m_abort[0U] = false;
|
||||
m_abort[1U] = false;
|
||||
@@ -240,80 +235,54 @@ void CDMRTX::setStart(bool start)
|
||||
void CDMRTX::setCal(bool start)
|
||||
{
|
||||
m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE;
|
||||
|
||||
m_count = 0U;
|
||||
}
|
||||
|
||||
void CDMRTX::writeByte(uint8_t c, uint8_t control)
|
||||
{
|
||||
q15_t inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t outBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += DMR_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, DMR_LEVELA, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, DMR_LEVELB, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, DMR_LEVELC, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELC;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, DMR_LEVELD, DMR_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DMR_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
|
||||
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
uint8_t controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U];
|
||||
::memset(controlBuffer, MARK_NONE, DMR_RADIO_SYMBOL_LENGTH * 4U * sizeof(uint8_t));
|
||||
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U] = control;
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += DMR_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[DMR_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
for (int8_t i = DMR_RADIO_SYMBOL_LENGTH * 4U - 1; i >= 0; i--)
|
||||
controlBuffer[i + 1] = controlBuffer[i];
|
||||
blockSize++;
|
||||
} else {
|
||||
controlBuffer[DMR_RADIO_SYMBOL_LENGTH * 2U - 1U] = control;
|
||||
for (uint8_t i = 0U; i < (DMR_RADIO_SYMBOL_LENGTH * 4U - 1U); i++)
|
||||
controlBuffer[i] = controlBuffer[i + 1U];
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_DMR, outBuffer, blockSize, controlBuffer);
|
||||
io.write(STATE_DMR, outBuffer, DMR_RADIO_SYMBOL_LENGTH * 4U, controlBuffer);
|
||||
}
|
||||
|
||||
uint16_t CDMRTX::getSpace1() const
|
||||
uint8_t CDMRTX::getSpace1() const
|
||||
{
|
||||
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
|
||||
uint16_t CDMRTX::getSpace2() const
|
||||
uint8_t CDMRTX::getSpace2() const
|
||||
{
|
||||
return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
|
||||
}
|
||||
|
||||
void CDMRTX::createData(uint8_t slotIndex)
|
||||
{
|
||||
if (m_fifo[slotIndex].getData()> 0U) {
|
||||
if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) {
|
||||
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
|
||||
m_poBuffer[i] = m_fifo[slotIndex].get();
|
||||
m_markBuffer[i] = MARK_NONE;
|
||||
@@ -344,6 +313,8 @@ void CDMRTX::createCal()
|
||||
|
||||
void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
||||
{
|
||||
m_frameCount++;
|
||||
|
||||
if (m_cachPtr >= 12U)
|
||||
m_cachPtr = 0U;
|
||||
|
||||
@@ -359,7 +330,9 @@ void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
|
||||
m_markBuffer[1U] = MARK_NONE;
|
||||
m_markBuffer[2U] = rxSlotIndex == 1U ? MARK_SLOT1 : MARK_SLOT2;
|
||||
|
||||
bool at = m_fifo[rxSlotIndex].getData() > 0U;
|
||||
bool at = false;
|
||||
if (m_frameCount >= STARTUP_COUNT)
|
||||
at = m_fifo[rxSlotIndex].getData() > 0U;
|
||||
bool tc = txSlotIndex == 1U;
|
||||
bool ls0 = true; // For 1 and 2
|
||||
bool ls1 = true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -49,26 +49,26 @@ public:
|
||||
|
||||
void process();
|
||||
|
||||
uint16_t getSpace1() const;
|
||||
uint16_t getSpace2() const;
|
||||
uint8_t getSpace1() const;
|
||||
uint8_t getSpace2() const;
|
||||
|
||||
void setColorCode(uint8_t colorCode);
|
||||
|
||||
private:
|
||||
CSerialRB m_fifo[2U];
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
DMRTXSTATE m_state;
|
||||
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
||||
uint8_t m_cachPtr;
|
||||
uint8_t m_shortLC[12U];
|
||||
uint8_t m_newShortLC[12U];
|
||||
uint8_t m_markBuffer[40U];
|
||||
uint8_t m_poBuffer[40U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_count;
|
||||
bool m_abort[2U];
|
||||
CSerialRB m_fifo[2U];
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
DMRTXSTATE m_state;
|
||||
uint8_t m_idle[DMR_FRAME_LENGTH_BYTES];
|
||||
uint8_t m_cachPtr;
|
||||
uint8_t m_shortLC[12U];
|
||||
uint8_t m_newShortLC[12U];
|
||||
uint8_t m_markBuffer[40U];
|
||||
uint8_t m_poBuffer[40U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint32_t m_frameCount;
|
||||
bool m_abort[2U];
|
||||
|
||||
void createData(uint8_t slotIndex);
|
||||
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
|
||||
|
||||
+91
-23
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,8 +17,6 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DStarRX.h"
|
||||
@@ -35,7 +34,9 @@ const unsigned int SYNC_POS = 21U * DSTAR_DATA_LENGTH_BITS;
|
||||
const unsigned int SYNC_SCAN_START = SYNC_POS - 3U;
|
||||
const unsigned int SYNC_SCAN_END = SYNC_POS + 3U;
|
||||
|
||||
const q15_t THRESHOLD = 0;
|
||||
// Generated using [b, a] = butter(1, 0.001) in MATLAB
|
||||
static q31_t DC_FILTER[] = {3367972, 0, 3367972, 0, 2140747704, 0}; // {b0, 0, b1, b2, -a1, -a2}
|
||||
const uint32_t DC_FILTER_STAGES = 1U; // One Biquad stage
|
||||
|
||||
// D-Star bit order version of 0x55 0x55 0x6E 0x0A
|
||||
const uint32_t FRAME_SYNC_DATA = 0x00557650U;
|
||||
@@ -259,9 +260,17 @@ m_pathMemory1(),
|
||||
m_pathMemory2(),
|
||||
m_pathMemory3(),
|
||||
m_fecOutput(),
|
||||
m_samples(),
|
||||
m_samplesPtr(0U)
|
||||
m_rssiAccum(0U),
|
||||
m_rssiCount(0U),
|
||||
m_dcFilter(),
|
||||
m_dcState()
|
||||
{
|
||||
::memset(m_dcState, 0x00U, 4U * sizeof(q31_t));
|
||||
|
||||
m_dcFilter.numStages = DC_FILTER_STAGES;
|
||||
m_dcFilter.pState = m_dcState;
|
||||
m_dcFilter.pCoeffs = DC_FILTER;
|
||||
m_dcFilter.postShift = 0;
|
||||
}
|
||||
|
||||
void CDStarRX::reset()
|
||||
@@ -272,15 +281,29 @@ void CDStarRX::reset()
|
||||
m_patternBuffer = 0x00U;
|
||||
m_rxBufferBits = 0U;
|
||||
m_dataBits = 0U;
|
||||
m_samplesPtr = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CDStarRX::samples(const q15_t* samples, uint8_t length)
|
||||
void CDStarRX::samples(const q15_t* samples, const uint16_t* rssi, uint8_t length)
|
||||
{
|
||||
for (uint16_t i = 0U; i < length; i++) {
|
||||
m_samples[m_samplesPtr] = samples[i];
|
||||
q31_t dc_level = 0;
|
||||
q31_t dcVals[20];
|
||||
q31_t intSamp[20];
|
||||
|
||||
bool bit = samples[i] < THRESHOLD;
|
||||
::arm_q15_to_q31((q15_t*)samples, intSamp, length);
|
||||
::arm_biquad_cascade_df1_q31(&m_dcFilter, intSamp, dcVals, length);
|
||||
|
||||
for (uint8_t i = 0U; i < length; i++)
|
||||
dc_level += dcVals[i];
|
||||
|
||||
dc_level /= length;
|
||||
|
||||
for (uint16_t i = 0U; i < length; i++) {
|
||||
m_rssiAccum += rssi[i];
|
||||
m_rssiCount++;
|
||||
|
||||
bool bit = samples[i] < (q15_t) (dc_level >> 16);
|
||||
|
||||
if (bit != m_prev) {
|
||||
if (m_pll < (PLLMAX / 2U))
|
||||
@@ -310,10 +333,6 @@ void CDStarRX::samples(const q15_t* samples, uint8_t length)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_samplesPtr++;
|
||||
if (m_samplesPtr >= DSTAR_DATA_SYNC_LENGTH_BITS)
|
||||
m_samplesPtr = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,6 +349,9 @@ void CDStarRX::processNone(bool bit)
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_FEC_SECTION_LENGTH_BYTES);
|
||||
m_rxBufferBits = 0U;
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
m_rxState = DSRXS_HEADER;
|
||||
return;
|
||||
}
|
||||
@@ -341,7 +363,12 @@ void CDStarRX::processNone(bool bit)
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
serial.writeDStarData(DSTAR_DATA_SYNC_BYTES, DSTAR_DATA_LENGTH_BYTES);
|
||||
// Suppress RSSI on the dummy sync message
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
::memcpy(m_rxBuffer, DSTAR_DATA_SYNC_BYTES, DSTAR_DATA_LENGTH_BYTES);
|
||||
writeRSSIData(m_rxBuffer);
|
||||
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
m_rxBufferBits = 0U;
|
||||
@@ -370,7 +397,7 @@ void CDStarRX::processHeader(bool bit)
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES);
|
||||
writeRSSIHeader(header);
|
||||
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
m_rxBufferBits = 0U;
|
||||
@@ -410,12 +437,11 @@ void CDStarRX::processData(bool bit)
|
||||
bool syncSeen = false;
|
||||
if (m_dataBits >= SYNC_SCAN_START && m_dataBits <= (SYNC_POS + 1U)) {
|
||||
if (countBits32((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA) <= DATA_SYNC_ERRS) {
|
||||
#if defined(WANT_DEBUG)
|
||||
if (m_dataBits < SYNC_POS)
|
||||
DEBUG2("DStarRX: found data sync in Data, early", SYNC_POS - m_dataBits);
|
||||
else
|
||||
DEBUG1("DStarRX: found data sync in Data");
|
||||
#endif
|
||||
|
||||
m_rxBufferBits = DSTAR_DATA_LENGTH_BITS;
|
||||
m_dataBits = 0U;
|
||||
syncSeen = true;
|
||||
@@ -457,9 +483,10 @@ void CDStarRX::processData(bool bit)
|
||||
m_rxBuffer[9U] = DSTAR_DATA_SYNC_BYTES[9U];
|
||||
m_rxBuffer[10U] = DSTAR_DATA_SYNC_BYTES[10U];
|
||||
m_rxBuffer[11U] = DSTAR_DATA_SYNC_BYTES[11U];
|
||||
}
|
||||
|
||||
serial.writeDStarData(m_rxBuffer, DSTAR_DATA_LENGTH_BYTES);
|
||||
writeRSSIData(m_rxBuffer);
|
||||
} else {
|
||||
serial.writeDStarData(m_rxBuffer, DSTAR_DATA_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
// Start the next frame
|
||||
::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
@@ -467,6 +494,48 @@ void CDStarRX::processData(bool bit)
|
||||
}
|
||||
}
|
||||
|
||||
void CDStarRX::writeRSSIHeader(unsigned char* header)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
if (m_rssiCount > 0U) {
|
||||
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||
|
||||
header[41U] = (rssi >> 8) & 0xFFU;
|
||||
header[42U] = (rssi >> 0) & 0xFFU;
|
||||
|
||||
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES + 2U);
|
||||
} else {
|
||||
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES + 0U);
|
||||
}
|
||||
#else
|
||||
serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES + 0U);
|
||||
#endif
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CDStarRX::writeRSSIData(unsigned char* data)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
if (m_rssiCount > 0U) {
|
||||
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||
|
||||
data[12U] = (rssi >> 8) & 0xFFU;
|
||||
data[13U] = (rssi >> 0) & 0xFFU;
|
||||
|
||||
serial.writeDStarData(data, DSTAR_DATA_LENGTH_BYTES + 2U);
|
||||
} else {
|
||||
serial.writeDStarData(data, DSTAR_DATA_LENGTH_BYTES + 0U);
|
||||
}
|
||||
#else
|
||||
serial.writeDStarData(data, DSTAR_DATA_LENGTH_BYTES + 0U);
|
||||
#endif
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
bool CDStarRX::rxHeader(uint8_t* in, uint8_t* out)
|
||||
{
|
||||
int i;
|
||||
@@ -682,4 +751,3 @@ bool CDStarRX::checksum(const uint8_t* header) const
|
||||
|
||||
return crc8[0U] == header[DSTAR_HEADER_LENGTH_BYTES - 2U] && crc8[1U] == header[DSTAR_HEADER_LENGTH_BYTES - 1U];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,7 +32,7 @@ class CDStarRX {
|
||||
public:
|
||||
CDStarRX();
|
||||
|
||||
void samples(const q15_t* samples, uint8_t length);
|
||||
void samples(const q15_t* samples, const uint16_t* rssi, uint8_t length);
|
||||
|
||||
void reset();
|
||||
|
||||
@@ -51,12 +51,16 @@ private:
|
||||
unsigned int m_pathMemory2[42U];
|
||||
unsigned int m_pathMemory3[42U];
|
||||
uint8_t m_fecOutput[42U];
|
||||
q15_t m_samples[DSTAR_DATA_SYNC_LENGTH_BITS];
|
||||
uint8_t m_samplesPtr;
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
arm_biquad_casd_df1_inst_q31 m_dcFilter;
|
||||
q31_t m_dcState[4];
|
||||
|
||||
void processNone(bool bit);
|
||||
void processHeader(bool bit);
|
||||
void processData(bool bit);
|
||||
void writeRSSIHeader(unsigned char* header);
|
||||
void writeRSSIData(unsigned char* data);
|
||||
bool rxHeader(uint8_t* in, uint8_t* out);
|
||||
void acs(int* metric);
|
||||
void viterbiDecode(int* data);
|
||||
|
||||
+25
-47
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,8 +17,6 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "DStarTX.h"
|
||||
@@ -28,12 +27,12 @@ const uint8_t BIT_SYNC = 0xAAU;
|
||||
|
||||
const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U};
|
||||
|
||||
// Generated using gaussfir(0.5, 4, 5) in MATLAB
|
||||
static q15_t DSTAR_GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
||||
const uint16_t DSTAR_GMSK_FILTER_LEN = 12U;
|
||||
// Generated using gaussfir(0.35, 1, 5) in MATLAB
|
||||
static q15_t GAUSSIAN_0_35_FILTER[] = {0, 0, 0, 0, 1001, 3514, 9333, 18751, 28499, 32767, 28499, 18751, 9333, 3514, 1001}; // numTaps = 15, L = 5
|
||||
const uint16_t GAUSSIAN_0_35_FILTER_PHASE_LEN = 3U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t DSTAR_LEVEL0[] = {-800, -800, -800, -800, -800};
|
||||
const q15_t DSTAR_LEVEL1[] = { 800, 800, 800, 800, 800};
|
||||
const q15_t DSTAR_LEVEL0 = -841;
|
||||
const q15_t DSTAR_LEVEL1 = 841;
|
||||
|
||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
@@ -195,14 +194,14 @@ m_modState(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(60U), // 100ms
|
||||
m_count(0U)
|
||||
m_txDelay(60U) // 100ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 60U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 20U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = DSTAR_GMSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.pCoeffs = DSTAR_GMSK_FILTER;
|
||||
m_modFilter.L = DSTAR_RADIO_BIT_LENGTH;
|
||||
m_modFilter.phaseLength = GAUSSIAN_0_35_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = GAUSSIAN_0_35_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
void CDStarTX::process()
|
||||
@@ -214,8 +213,6 @@ void CDStarTX::process()
|
||||
|
||||
if (type == DSTAR_HEADER && m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
m_count = 0U;
|
||||
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = BIT_SYNC;
|
||||
} else {
|
||||
@@ -241,9 +238,6 @@ void CDStarTX::process()
|
||||
}
|
||||
|
||||
if (type == DSTAR_DATA && m_poLen == 0U) {
|
||||
if (!m_tx)
|
||||
m_count = 0U;
|
||||
|
||||
// Pop the type byte off
|
||||
m_buffer.get();
|
||||
|
||||
@@ -417,50 +411,34 @@ void CDStarTX::txHeader(const uint8_t* in, uint8_t* out) const
|
||||
|
||||
void CDStarTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U];
|
||||
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U + 1U];
|
||||
q15_t inBuffer[8U];
|
||||
q15_t outBuffer[DSTAR_RADIO_BIT_LENGTH * 8U];
|
||||
|
||||
uint8_t mask = 0x01U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 8U; i++, p += DSTAR_RADIO_BIT_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 8U; i++) {
|
||||
if ((c & mask) == mask)
|
||||
::memcpy(p, DSTAR_LEVEL0, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DSTAR_LEVEL0;
|
||||
else
|
||||
::memcpy(p, DSTAR_LEVEL1, DSTAR_RADIO_BIT_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = DSTAR_LEVEL1;
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
uint16_t blockSize = DSTAR_RADIO_BIT_LENGTH * 8U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 8U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += DSTAR_RADIO_BIT_LENGTH * 8U;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U] = inBuffer[DSTAR_RADIO_BIT_LENGTH * 8U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_DSTAR, outBuffer, blockSize);
|
||||
io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_BIT_LENGTH * 8U);
|
||||
}
|
||||
|
||||
void CDStarTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 150U + uint16_t(delay) * 6U; // 250ms + tx delay
|
||||
m_txDelay = 300U + uint16_t(delay) * 6U; // 250ms + tx delay
|
||||
|
||||
if (m_txDelay > 600U)
|
||||
m_txDelay = 600U;
|
||||
}
|
||||
|
||||
uint16_t CDStarTX::getSpace() const
|
||||
uint8_t CDStarTX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / (DSTAR_DATA_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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,17 +35,16 @@ public:
|
||||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[60U]; // NoTaps + BlockSize - 1, 12 + 40 - 1 plus some spare
|
||||
uint8_t m_poBuffer[500U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay; // In bytes
|
||||
uint32_t m_count;
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[20U]; // blockSize + phaseLength - 1, 8 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[600U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay; // In bytes
|
||||
|
||||
void txHeader(const uint8_t* in, uint8_t* out) const;
|
||||
void writeByte(uint8_t c);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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,9 +20,6 @@
|
||||
#define DEBUG_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#if defined(WANT_DEBUG)
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#define DEBUG1(a) serial.writeDebug((a))
|
||||
@@ -30,18 +27,6 @@
|
||||
#define DEBUG3(a,b,c) serial.writeDebug((a),(b),(c))
|
||||
#define DEBUG4(a,b,c,d) serial.writeDebug((a),(b),(c),(d))
|
||||
#define DEBUG5(a,b,c,d,e) serial.writeDebug((a),(b),(c),(d),(e))
|
||||
#define ASSERT(a) serial.writeAssert((a),#a,__FILE__,__LINE__)
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG1(a)
|
||||
#define DEBUG2(a,b)
|
||||
#define DEBUG3(a,b,c)
|
||||
#define DEBUG4(a,b,c,d)
|
||||
#define DEBUG5(a,b,c,d,e)
|
||||
#define ASSERT(a)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
|
||||
#if defined(STM32F4XX) || defined(STM32F4)
|
||||
#include "stm32f4xx.h"
|
||||
#elif defined(STM32F105xC)
|
||||
#include "stm32f1xx.h"
|
||||
#include "STM32Utils.h"
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#if defined(__SAM3X8E__)
|
||||
#if defined(__SAM3X8E__) || defined(STM32F105xC)
|
||||
#define ARM_MATH_CM3
|
||||
#elif defined(STM32F4XX) || defined(STM32F4) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
||||
#define ARM_MATH_CM4
|
||||
@@ -43,6 +46,7 @@ enum MMDVM_STATE {
|
||||
STATE_P25 = 4,
|
||||
|
||||
// Dummy states start at 90
|
||||
STATE_RSSICAL = 96,
|
||||
STATE_CWID = 97,
|
||||
STATE_DMRCAL = 98,
|
||||
STATE_DSTARCAL = 99
|
||||
@@ -63,6 +67,7 @@ enum MMDVM_STATE {
|
||||
#include "CalDStarRX.h"
|
||||
#include "CalDStarTX.h"
|
||||
#include "CalDMR.h"
|
||||
#include "CalRSSI.h"
|
||||
#include "CWIdTX.h"
|
||||
#include "Debug.h"
|
||||
#include "IO.h"
|
||||
@@ -88,9 +93,6 @@ extern bool m_duplex;
|
||||
extern bool m_tx;
|
||||
extern bool m_dcd;
|
||||
|
||||
extern uint32_t m_sampleCount;
|
||||
extern bool m_sampleInsert;
|
||||
|
||||
extern CSerialPort serial;
|
||||
extern CIO io;
|
||||
|
||||
@@ -113,6 +115,7 @@ extern CP25TX p25TX;
|
||||
extern CCalDStarRX calDStarRX;
|
||||
extern CCalDStarTX calDStarTX;
|
||||
extern CCalDMR calDMR;
|
||||
extern CCalRSSI calRSSI;
|
||||
|
||||
extern CCWIdTX cwIdTX;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
@@ -18,28 +18,23 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "IO.h"
|
||||
|
||||
#if defined(WIDE_C4FSK_FILTERS_RX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t C4FSK_FILTER_LEN = 22U;
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
static q15_t RRC_0_2_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t C4FSK_FILTER_LEN = 42U;
|
||||
#endif
|
||||
const uint16_t RRC_0_2_FILTER_LEN = 42U;
|
||||
|
||||
// Generated using gaussfir(0.5, 4, 5) in MATLAB
|
||||
static q15_t GMSK_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
||||
const uint16_t GMSK_FILTER_LEN = 12U;
|
||||
static q15_t GAUSSIAN_0_5_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0};
|
||||
const uint16_t GAUSSIAN_0_5_FILTER_LEN = 12U;
|
||||
|
||||
// One symbol boxcar filter
|
||||
static q15_t BOXCAR_FILTER[] = {3000, 3000, 3000, 3000, 3000, 0};
|
||||
const uint16_t BOXCAR_FILTER_LEN = 6U;
|
||||
|
||||
const uint16_t DC_OFFSET = 2048U;
|
||||
|
||||
@@ -48,10 +43,12 @@ m_started(false),
|
||||
m_rxBuffer(RX_RINGBUFFER_SIZE),
|
||||
m_txBuffer(TX_RINGBUFFER_SIZE),
|
||||
m_rssiBuffer(RX_RINGBUFFER_SIZE),
|
||||
m_C4FSKFilter(),
|
||||
m_GMSKFilter(),
|
||||
m_C4FSKState(),
|
||||
m_GMSKState(),
|
||||
m_rrcFilter(),
|
||||
m_gaussianFilter(),
|
||||
m_boxcarFilter(),
|
||||
m_rrcState(),
|
||||
m_gaussianState(),
|
||||
m_boxcarState(),
|
||||
m_pttInvert(false),
|
||||
m_rxLevel(128 * 128),
|
||||
m_cwIdTXLevel(128 * 128),
|
||||
@@ -64,20 +61,24 @@ m_ledValue(true),
|
||||
m_detect(false),
|
||||
m_adcOverflow(0U),
|
||||
m_dacOverflow(0U),
|
||||
m_count(0U),
|
||||
m_watchdog(0U),
|
||||
m_lockout(false)
|
||||
{
|
||||
::memset(m_C4FSKState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_GMSKState, 0x00U, 40U * sizeof(q15_t));
|
||||
::memset(m_rrcState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t));
|
||||
::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t));
|
||||
|
||||
m_C4FSKFilter.numTaps = C4FSK_FILTER_LEN;
|
||||
m_C4FSKFilter.pState = m_C4FSKState;
|
||||
m_C4FSKFilter.pCoeffs = C4FSK_FILTER;
|
||||
m_rrcFilter.numTaps = RRC_0_2_FILTER_LEN;
|
||||
m_rrcFilter.pState = m_rrcState;
|
||||
m_rrcFilter.pCoeffs = RRC_0_2_FILTER;
|
||||
|
||||
m_GMSKFilter.numTaps = GMSK_FILTER_LEN;
|
||||
m_GMSKFilter.pState = m_GMSKState;
|
||||
m_GMSKFilter.pCoeffs = GMSK_FILTER;
|
||||
m_gaussianFilter.numTaps = GAUSSIAN_0_5_FILTER_LEN;
|
||||
m_gaussianFilter.pState = m_gaussianState;
|
||||
m_gaussianFilter.pCoeffs = GAUSSIAN_0_5_FILTER;
|
||||
|
||||
m_boxcarFilter.numTaps = BOXCAR_FILTER_LEN;
|
||||
m_boxcarFilter.pState = m_boxcarState;
|
||||
m_boxcarFilter.pCoeffs = BOXCAR_FILTER;
|
||||
|
||||
initInt();
|
||||
}
|
||||
@@ -89,7 +90,6 @@ void CIO::start()
|
||||
|
||||
startInt();
|
||||
|
||||
m_count = 0U;
|
||||
m_started = true;
|
||||
|
||||
setMode();
|
||||
@@ -136,11 +136,9 @@ void CIO::process()
|
||||
}
|
||||
|
||||
if (m_rxBuffer.getData() >= RX_BLOCK_SIZE) {
|
||||
q15_t samples[RX_BLOCK_SIZE + 1U];
|
||||
uint8_t control[RX_BLOCK_SIZE + 1U];
|
||||
uint16_t rssi[RX_BLOCK_SIZE + 1U];
|
||||
|
||||
uint8_t blockSize = RX_BLOCK_SIZE;
|
||||
q15_t samples[RX_BLOCK_SIZE];
|
||||
uint8_t control[RX_BLOCK_SIZE];
|
||||
uint16_t rssi[RX_BLOCK_SIZE];
|
||||
|
||||
for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) {
|
||||
uint16_t sample;
|
||||
@@ -156,95 +154,81 @@ void CIO::process()
|
||||
samples[i] = q15_t(__SSAT((res2 >> 15), 16));
|
||||
}
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += RX_BLOCK_SIZE;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
blockSize++;
|
||||
samples[RX_BLOCK_SIZE] = 0;
|
||||
for (int8_t i = RX_BLOCK_SIZE - 1; i >= 0; i--)
|
||||
control[i + 1] = control[i];
|
||||
} else {
|
||||
blockSize--;
|
||||
for (uint8_t i = 0U; i < (RX_BLOCK_SIZE - 1U); i++)
|
||||
control[i] = control[i + 1U];
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_lockout)
|
||||
return;
|
||||
|
||||
if (m_modemState == STATE_IDLE) {
|
||||
if (m_dstarEnable) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
dstarRX.samples(GMSKVals, blockSize);
|
||||
dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_dmrEnable || m_ysfEnable || m_p25Enable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
if (m_p25Enable) {
|
||||
q15_t P25Vals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE);
|
||||
|
||||
p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_dmrEnable || m_ysfEnable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_dmrEnable) {
|
||||
if (m_duplex)
|
||||
dmrIdleRX.samples(C4FSKVals, blockSize);
|
||||
dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
|
||||
dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (m_ysfEnable)
|
||||
ysfRX.samples(C4FSKVals, blockSize);
|
||||
|
||||
if (m_p25Enable)
|
||||
p25RX.samples(C4FSKVals, blockSize);
|
||||
ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DSTAR) {
|
||||
if (m_dstarEnable) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
dstarRX.samples(GMSKVals, blockSize);
|
||||
dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DMR) {
|
||||
if (m_dmrEnable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
if (m_duplex) {
|
||||
// If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs
|
||||
if (m_tx)
|
||||
dmrRX.samples(C4FSKVals, rssi, control, blockSize);
|
||||
dmrRX.samples(C4FSKVals, rssi, control, RX_BLOCK_SIZE);
|
||||
else
|
||||
dmrIdleRX.samples(C4FSKVals, blockSize);
|
||||
dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE);
|
||||
} else {
|
||||
dmrDMORX.samples(C4FSKVals, rssi, blockSize);
|
||||
dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
} else if (m_modemState == STATE_YSF) {
|
||||
if (m_ysfEnable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
ysfRX.samples(C4FSKVals, blockSize);
|
||||
ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_P25) {
|
||||
if (m_p25Enable) {
|
||||
q15_t C4FSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_C4FSKFilter, samples, C4FSKVals, blockSize);
|
||||
q15_t P25Vals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE);
|
||||
|
||||
p25RX.samples(C4FSKVals, blockSize);
|
||||
p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
} else if (m_modemState == STATE_DSTARCAL) {
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE + 1U];
|
||||
::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize);
|
||||
q15_t GMSKVals[RX_BLOCK_SIZE];
|
||||
::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE);
|
||||
|
||||
calDStarRX.samples(GMSKVals, blockSize);
|
||||
calDStarRX.samples(GMSKVals, RX_BLOCK_SIZE);
|
||||
} else if (m_modemState == STATE_RSSICAL) {
|
||||
calRSSI.samples(rssi, RX_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,11 +337,6 @@ void CIO::getOverflow(bool& adcOverflow, bool& dacOverflow)
|
||||
adcOverflow = m_adcOverflow > 0U;
|
||||
dacOverflow = m_dacOverflow > 0U;
|
||||
|
||||
#if defined(WANT_DEBUG)
|
||||
if (m_adcOverflow > 0U || m_dacOverflow > 0U)
|
||||
DEBUG3("IO: adc/dac", m_adcOverflow, m_dacOverflow);
|
||||
#endif
|
||||
|
||||
m_adcOverflow = 0U;
|
||||
m_dacOverflow = 0U;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
void setADCDetection(bool detect);
|
||||
void setMode();
|
||||
|
||||
void interrupt(uint8_t source);
|
||||
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);
|
||||
|
||||
@@ -60,10 +60,12 @@ private:
|
||||
CSampleRB m_txBuffer;
|
||||
CRSSIRB m_rssiBuffer;
|
||||
|
||||
arm_fir_instance_q15 m_C4FSKFilter;
|
||||
arm_fir_instance_q15 m_GMSKFilter;
|
||||
q15_t m_C4FSKState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
q15_t m_GMSKState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
|
||||
arm_fir_instance_q15 m_rrcFilter;
|
||||
arm_fir_instance_q15 m_gaussianFilter;
|
||||
arm_fir_instance_q15 m_boxcarFilter;
|
||||
q15_t m_rrcState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare
|
||||
q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare
|
||||
|
||||
bool m_pttInvert;
|
||||
q15_t m_rxLevel;
|
||||
@@ -81,8 +83,6 @@ private:
|
||||
uint16_t m_adcOverflow;
|
||||
uint16_t m_dacOverflow;
|
||||
|
||||
uint32_t m_count;
|
||||
|
||||
volatile uint32_t m_watchdog;
|
||||
|
||||
bool m_lockout;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015 by Jim Mclaughlin KI6ZUM
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
@@ -51,6 +51,8 @@
|
||||
#define ADC_CDR_Chan 13
|
||||
#define DACC_MR_USER_SEL_Chan DACC_MR_USER_SEL_CHANNEL1 // DAC on Due DAC1
|
||||
#define DACC_CHER_Chan DACC_CHER_CH1
|
||||
#define RSSI_CHER_Chan (1<<1) // ADC on Due pin A6 - Due AD1 - (1 << 1)
|
||||
#define RSSI_CDR_Chan 1
|
||||
#elif defined(ARDUINO_DUE_NTH)
|
||||
#define PIN_COS A7
|
||||
#define PIN_PTT A8
|
||||
@@ -64,6 +66,8 @@
|
||||
#define ADC_CDR_Chan 7
|
||||
#define DACC_MR_USER_SEL_Chan DACC_MR_USER_SEL_CHANNEL0 // DAC on Due DAC0
|
||||
#define DACC_CHER_Chan DACC_CHER_CH0
|
||||
#define RSSI_CHER_Chan (1<<1) // ADC on Due pin A6 - Due AD1 - (1 << 1)
|
||||
#define RSSI_CDR_Chan 1
|
||||
#else
|
||||
#error "Either ARDUINO_DUE_PAPA, ARDUINO_DUE_ZUM_V10, or ARDUINO_DUE_NTH need to be defined"
|
||||
#endif
|
||||
@@ -73,7 +77,7 @@ const uint16_t DC_OFFSET = 2048U;
|
||||
extern "C" {
|
||||
void ADC_Handler()
|
||||
{
|
||||
io.interrupt(0U);
|
||||
io.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,14 +101,17 @@ void CIO::initInt()
|
||||
void CIO::startInt()
|
||||
{
|
||||
if (ADC->ADC_ISR & ADC_ISR_EOC_Chan) // Ensure there was an End-of-Conversion and we read the ISR reg
|
||||
io.interrupt(0U);
|
||||
io.interrupt();
|
||||
|
||||
// Set up the ADC
|
||||
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt vector
|
||||
ADC->ADC_IDR = 0xFFFFFFFF; // Disable interrupts
|
||||
ADC->ADC_IER = ADC_CHER_Chan; // Enable End-Of-Conv interrupt
|
||||
ADC->ADC_CHDR = 0xFFFF; // Disable all channels
|
||||
ADC->ADC_CHER = ADC_CHER_Chan; // Enable just one channel
|
||||
ADC->ADC_CHER = ADC_CHER_Chan; // Enable rx input channel
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
ADC->ADC_CHER |= RSSI_CHER_Chan; // and RSSI input
|
||||
#endif
|
||||
ADC->ADC_CGR = 0x15555555; // All gains set to x1
|
||||
ADC->ADC_COR = 0x00000000; // All offsets off
|
||||
ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN; // 1 = trig source TIO from TC0
|
||||
@@ -157,7 +164,7 @@ void CIO::startInt()
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
}
|
||||
|
||||
void CIO::interrupt(uint8_t source)
|
||||
void CIO::interrupt()
|
||||
{
|
||||
if ((ADC->ADC_ISR & ADC_ISR_EOC_Chan) == ADC_ISR_EOC_Chan) { // Ensure there was an End-of-Conversion and we read the ISR reg
|
||||
uint8_t control = MARK_NONE;
|
||||
@@ -169,7 +176,11 @@ void CIO::interrupt(uint8_t source)
|
||||
sample = ADC->ADC_CDR[ADC_CDR_Chan];
|
||||
m_rxBuffer.put(sample, control);
|
||||
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
m_rssiBuffer.put(ADC->ADC_CDR[RSSI_CDR_Chan]);
|
||||
#else
|
||||
m_rssiBuffer.put(0U);
|
||||
#endif
|
||||
|
||||
m_watchdog++;
|
||||
}
|
||||
@@ -216,4 +227,3 @@ void CIO::setP25Int(bool on)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
||||
* Copyright (C) 2016 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2017 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
|
||||
@@ -28,59 +29,70 @@
|
||||
Pin definitions for STM32F4 Discovery Board:
|
||||
|
||||
PTT PB13 output
|
||||
COSLED PA7 output
|
||||
LED PD15 output
|
||||
COS PA5 input
|
||||
COSLED PA7 output
|
||||
LED PD15 output
|
||||
COS PA5 input
|
||||
|
||||
DSTAR PD12 output
|
||||
DMR PD13 output
|
||||
YSF PD14 output
|
||||
P25 PD11 output
|
||||
DSTAR PD12 output
|
||||
DMR PD13 output
|
||||
YSF PD14 output
|
||||
P25 PD11 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA1 analog input
|
||||
TX PA4 analog output
|
||||
RX PA0 analog input
|
||||
RSSI PA1 analog input
|
||||
TX PA4 analog output
|
||||
|
||||
EXT_CLK PA15 input
|
||||
*/
|
||||
|
||||
#define PIN_COS GPIO_Pin_5
|
||||
#define PORT_COS GPIOA
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOA
|
||||
#define PIN_COS GPIO_Pin_5
|
||||
#define PORT_COS GPIOA
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_COSLED GPIO_Pin_7
|
||||
#define PORT_COSLED GPIOA
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOA
|
||||
#define PIN_COSLED GPIO_Pin_7
|
||||
#define PORT_COSLED GPIOA
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_LED GPIO_Pin_15
|
||||
#define PORT_LED GPIOD
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOD
|
||||
#define PIN_LED GPIO_Pin_15
|
||||
#define PORT_LED GPIOD
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_P25 GPIO_Pin_11
|
||||
#define PORT_P25 GPIOD
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOD
|
||||
#define PIN_P25 GPIO_Pin_11
|
||||
#define PORT_P25 GPIOD
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_12
|
||||
#define PORT_DSTAR GPIOD
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOD
|
||||
#define PIN_DSTAR GPIO_Pin_12
|
||||
#define PORT_DSTAR GPIOD
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_DMR GPIO_Pin_13
|
||||
#define PORT_DMR GPIOD
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOD
|
||||
#define PIN_DMR GPIO_Pin_13
|
||||
#define PORT_DMR GPIOD
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_YSF GPIO_Pin_14
|
||||
#define PORT_YSF GPIOD
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOD
|
||||
#define PIN_YSF GPIO_Pin_14
|
||||
#define PORT_YSF GPIOD
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOD
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PIN_EXT_CLK GPIO_Pin_15
|
||||
#define SRC_EXT_CLK GPIO_PinSource15
|
||||
#define PORT_EXT_CLK GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_1
|
||||
#define PIN_RSSI_CH ADC_Channel_1
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PORT_RX GPIOA
|
||||
#define RCC_Per_RX RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_1
|
||||
#define PIN_RSSI_CH ADC_Channel_1
|
||||
#define PORT_RSSI GPIOA
|
||||
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_TX GPIO_Pin_4
|
||||
#define PIN_TX_CH DAC_Channel_1
|
||||
|
||||
#elif defined(STM32F4_PI)
|
||||
/*
|
||||
@@ -88,117 +100,238 @@ Pin definitions for STM32F4 Pi Board:
|
||||
|
||||
PTT PB13 output
|
||||
COSLED PB14 output
|
||||
LED PB15 output
|
||||
COS PC0 input
|
||||
LED PB15 output
|
||||
COS PC0 input
|
||||
|
||||
DSTAR PC7 output
|
||||
DMR PC8 output
|
||||
YSF PA8 output
|
||||
P25 PC9 output
|
||||
DSTAR PC7 output
|
||||
DMR PC8 output
|
||||
YSF PA8 output
|
||||
P25 PC9 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA7 analog input
|
||||
TX PA4 analog output
|
||||
RX PA0 analog input
|
||||
RSSI PA7 analog input
|
||||
TX PA4 analog output
|
||||
|
||||
EXT_CLK PA15 input
|
||||
*/
|
||||
|
||||
#define PIN_COS GPIO_Pin_0
|
||||
#define PORT_COS GPIOC
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOC
|
||||
#define PIN_COS GPIO_Pin_0
|
||||
#define PORT_COS GPIOC
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_COSLED GPIO_Pin_14
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
#define PIN_COSLED GPIO_Pin_14
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_LED GPIO_Pin_15
|
||||
#define PORT_LED GPIOB
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
|
||||
#define PIN_LED GPIO_Pin_15
|
||||
#define PORT_LED GPIOB
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_P25 GPIO_Pin_9
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
#define PIN_P25 GPIO_Pin_9
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_7
|
||||
#define PORT_DSTAR GPIOC
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
||||
#define PIN_DSTAR GPIO_Pin_7
|
||||
#define PORT_DSTAR GPIOC
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_DMR GPIO_Pin_8
|
||||
#define PORT_DMR GPIOC
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOC
|
||||
#define PIN_DMR GPIO_Pin_8
|
||||
#define PORT_DMR GPIOC
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_YSF GPIO_Pin_8
|
||||
#define PORT_YSF GPIOA
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOA
|
||||
#define PIN_YSF GPIO_Pin_8
|
||||
#define PORT_YSF GPIOA
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PIN_EXT_CLK GPIO_Pin_15
|
||||
#define SRC_EXT_CLK GPIO_PinSource15
|
||||
#define PORT_EXT_CLK GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_7
|
||||
#define PIN_RSSI_CH ADC_Channel_7
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PORT_RX GPIOA
|
||||
#define RCC_Per_RX RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_7
|
||||
#define PIN_RSSI_CH ADC_Channel_7
|
||||
#define PORT_RSSI GPIOA
|
||||
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_TX GPIO_Pin_4
|
||||
#define PIN_TX_CH DAC_Channel_1
|
||||
|
||||
#elif defined(STM32F4_NUCLEO)
|
||||
|
||||
#if defined(STM32F4_NUCLEO_MORPHO_HEADER)
|
||||
/*
|
||||
Pin definitions for STM32F4 Nucleo boards:
|
||||
Pin definitions for STM32F4 Nucleo boards (ST Morpho header):
|
||||
|
||||
PTT PB13 output
|
||||
COSLED PB14 output
|
||||
LED PA5 output
|
||||
COS PB15 input
|
||||
PTT PB13 output
|
||||
COSLED PB14 output
|
||||
LED PA5 output
|
||||
COS PB15 input
|
||||
|
||||
DSTAR PB10 output
|
||||
DMR PB4 output
|
||||
YSF PB5 output
|
||||
P25 PB3 output
|
||||
DSTAR PB10 output
|
||||
DMR PB4 output
|
||||
YSF PB5 output
|
||||
P25 PB3 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PA1 analog input
|
||||
TX PA4 analog output
|
||||
MDSTAR PC4 output
|
||||
MDMR PC5 outout
|
||||
MYSF PC2 output
|
||||
MP25 PC3 output
|
||||
|
||||
EXT_CLK PA15 input
|
||||
RX PA0 analog input
|
||||
RSSI PA1 analog input
|
||||
TX PA4 analog output
|
||||
|
||||
EXT_CLK PA15 input
|
||||
*/
|
||||
|
||||
#define PIN_COS GPIO_Pin_15
|
||||
#define PORT_COS GPIOB
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOB
|
||||
#define PIN_COS GPIO_Pin_15
|
||||
#define PORT_COS GPIOB
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
#define PIN_PTT GPIO_Pin_13
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_COSLED GPIO_Pin_14
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
#define PIN_COSLED GPIO_Pin_14
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_LED GPIO_Pin_5
|
||||
#define PORT_LED GPIOA
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOA
|
||||
#define PIN_LED GPIO_Pin_5
|
||||
#define PORT_LED GPIOA
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_P25 GPIO_Pin_3
|
||||
#define PORT_P25 GPIOB
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
|
||||
#define PIN_P25 GPIO_Pin_3
|
||||
#define PORT_P25 GPIOB
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_10
|
||||
#define PORT_DSTAR GPIOB
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
|
||||
#define PIN_DSTAR GPIO_Pin_10
|
||||
#define PORT_DSTAR GPIOB
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_DMR GPIO_Pin_4
|
||||
#define PORT_DMR GPIOB
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOB
|
||||
#define PIN_DMR GPIO_Pin_4
|
||||
#define PORT_DMR GPIOB
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_YSF GPIO_Pin_5
|
||||
#define PORT_YSF GPIOB
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOB
|
||||
#define PIN_YSF GPIO_Pin_5
|
||||
#define PORT_YSF GPIOB
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS)
|
||||
#define PIN_MP25 GPIO_Pin_3
|
||||
#define PORT_MP25 GPIOC
|
||||
#define RCC_Per_MP25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_1
|
||||
#define PIN_RSSI_CH ADC_Channel_1
|
||||
#define PIN_MDSTAR GPIO_Pin_4
|
||||
#define PORT_MDSTAR GPIOC
|
||||
#define RCC_Per_MDSTAR RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_MDMR GPIO_Pin_5
|
||||
#define PORT_MDMR GPIOC
|
||||
#define RCC_Per_MDMR RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_MYSF GPIO_Pin_2
|
||||
#define PORT_MYSF GPIOC
|
||||
#define RCC_Per_MYSF RCC_AHB1Periph_GPIOC
|
||||
#endif
|
||||
|
||||
#define PIN_EXT_CLK GPIO_Pin_15
|
||||
#define SRC_EXT_CLK GPIO_PinSource15
|
||||
#define PORT_EXT_CLK GPIOA
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PORT_RX GPIOA
|
||||
#define RCC_Per_RX RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_1
|
||||
#define PIN_RSSI_CH ADC_Channel_1
|
||||
#define PORT_RSSI GPIOA
|
||||
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_TX GPIO_Pin_4
|
||||
#define PIN_TX_CH DAC_Channel_1
|
||||
|
||||
#elif defined(STM32F4_NUCLEO_ARDUINO_HEADER)
|
||||
/*
|
||||
Pin definitions for STM32F4 Nucleo boards (Arduino header):
|
||||
|
||||
PTT PB10 output
|
||||
COSLED PB3 output
|
||||
LED PB5 output
|
||||
COS PB4 input
|
||||
|
||||
DSTAR PA1 output
|
||||
DMR PA4 output
|
||||
YSF PB0 output
|
||||
P25 PC1 output
|
||||
|
||||
RX PA0 analog input
|
||||
RSSI PC0 analog input
|
||||
TX PA5 analog output
|
||||
|
||||
EXT_CLK PB8 input
|
||||
*/
|
||||
|
||||
#define PIN_COS GPIO_Pin_4
|
||||
#define PORT_COS GPIOB
|
||||
#define RCC_Per_COS RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_PTT GPIO_Pin_10
|
||||
#define PORT_PTT GPIOB
|
||||
#define RCC_Per_PTT RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_COSLED GPIO_Pin_3
|
||||
#define PORT_COSLED GPIOB
|
||||
#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_LED GPIO_Pin_5
|
||||
#define PORT_LED GPIOB
|
||||
#define RCC_Per_LED RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_P25 GPIO_Pin_1
|
||||
#define PORT_P25 GPIOC
|
||||
#define RCC_Per_P25 RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_DSTAR GPIO_Pin_1
|
||||
#define PORT_DSTAR GPIOA
|
||||
#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_DMR GPIO_Pin_4
|
||||
#define PORT_DMR GPIOA
|
||||
#define RCC_Per_DMR RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_YSF GPIO_Pin_0
|
||||
#define PORT_YSF GPIOB
|
||||
#define RCC_Per_YSF RCC_AHB1Periph_GPIOB
|
||||
|
||||
#define PIN_EXT_CLK GPIO_Pin_8
|
||||
#define SRC_EXT_CLK GPIO_PinSource8
|
||||
#define PORT_EXT_CLK GPIOB
|
||||
|
||||
#define PIN_RX GPIO_Pin_0
|
||||
#define PIN_RX_CH ADC_Channel_0
|
||||
#define PORT_RX GPIOA
|
||||
#define RCC_Per_RX RCC_AHB1Periph_GPIOA
|
||||
|
||||
#define PIN_RSSI GPIO_Pin_0
|
||||
#define PIN_RSSI_CH ADC_Channel_10
|
||||
#define PORT_RSSI GPIOC
|
||||
#define RCC_Per_RSSI RCC_AHB1Periph_GPIOC
|
||||
|
||||
#define PIN_TX GPIO_Pin_5
|
||||
#define PIN_TX_CH DAC_Channel_2
|
||||
|
||||
#else
|
||||
#error "Either STM32F4_NUCLEO_MORPHO_HEADER or STM32F4_NUCLEO_ARDUINO_HEADER need to be defined in Config.h"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "Either STM32F4_DISCOVERY, STM32F4_PI or STM32F4_NUCLEO need to be defined"
|
||||
@@ -207,291 +340,335 @@ EXT_CLK PA15 input
|
||||
const uint16_t DC_OFFSET = 2048U;
|
||||
|
||||
// Sampling frequency
|
||||
#define SAMP_FREQ 24000
|
||||
#define SAMP_FREQ 24000
|
||||
|
||||
extern "C" {
|
||||
void TIM2_IRQHandler() {
|
||||
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
|
||||
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
|
||||
io.interrupt(0U);
|
||||
}
|
||||
}
|
||||
void TIM2_IRQHandler() {
|
||||
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
|
||||
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
|
||||
io.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CIO::initInt()
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
GPIO_StructInit(&GPIO_InitStruct);
|
||||
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
GPIO_StructInit(&GPIO_InitStruct);
|
||||
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
|
||||
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
|
||||
|
||||
// PTT pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_PTT, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_PTT;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_PTT, &GPIO_InitStruct);
|
||||
// PTT pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_PTT, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_PTT;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_PTT, &GPIO_InitStruct);
|
||||
|
||||
// COSLED pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_COSLED, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_COSLED;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_COSLED, &GPIO_InitStruct);
|
||||
// COSLED pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_COSLED, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_COSLED;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_COSLED, &GPIO_InitStruct);
|
||||
|
||||
// LED pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_LED, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_LED;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_LED, &GPIO_InitStruct);
|
||||
// LED pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_LED, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_LED;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_LED, &GPIO_InitStruct);
|
||||
|
||||
// Init the input pins PIN_COS
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_COS, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_COS;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_Init(PORT_COS, &GPIO_InitStruct);
|
||||
// Init the input pins PIN_COS
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_COS, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_COS;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
|
||||
GPIO_Init(PORT_COS, &GPIO_InitStruct);
|
||||
|
||||
#if defined(ARDUINO_MODE_PINS)
|
||||
// DSTAR pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_DSTAR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_DSTAR;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_DSTAR, &GPIO_InitStruct);
|
||||
// DSTAR pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_DSTAR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_DSTAR;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_DSTAR, &GPIO_InitStruct);
|
||||
|
||||
// DMR pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_DMR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_DMR;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_DMR, &GPIO_InitStruct);
|
||||
// DMR pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_DMR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_DMR;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_DMR, &GPIO_InitStruct);
|
||||
|
||||
// YSF pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_YSF, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_YSF;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_YSF, &GPIO_InitStruct);
|
||||
// YSF pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_YSF, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_YSF;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_YSF, &GPIO_InitStruct);
|
||||
|
||||
// P25 pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_P25, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_P25;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_P25, &GPIO_InitStruct);
|
||||
// P25 pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_P25, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_P25;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_P25, &GPIO_InitStruct);
|
||||
#endif
|
||||
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
// DSTAR mode pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_MDSTAR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_MDSTAR;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_MDSTAR, &GPIO_InitStruct);
|
||||
|
||||
// DMR mode pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_MDMR, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_MDMR;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_MDMR, &GPIO_InitStruct);
|
||||
|
||||
// YSF mode pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_MYSF, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_MYSF;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_MYSF, &GPIO_InitStruct);
|
||||
|
||||
// P25 mode pin
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_MP25, ENABLE);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_MP25;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
|
||||
GPIO_Init(PORT_MP25, &GPIO_InitStruct);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::startInt()
|
||||
{
|
||||
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != RESET))
|
||||
io.interrupt(0U);
|
||||
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != RESET))
|
||||
io.interrupt();
|
||||
|
||||
// Init the ADC
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
ADC_InitTypeDef ADC_InitStructure;
|
||||
ADC_CommonInitTypeDef ADC_CommonInitStructure;
|
||||
// Init the ADC
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
ADC_InitTypeDef ADC_InitStructure;
|
||||
ADC_CommonInitTypeDef ADC_CommonInitStructure;
|
||||
|
||||
GPIO_StructInit(&GPIO_InitStruct);
|
||||
ADC_CommonStructInit(&ADC_CommonInitStructure);
|
||||
ADC_StructInit(&ADC_InitStructure);
|
||||
GPIO_StructInit(&GPIO_InitStruct);
|
||||
ADC_CommonStructInit(&ADC_CommonInitStructure);
|
||||
ADC_StructInit(&ADC_InitStructure);
|
||||
|
||||
// Enable ADC1 clock
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_RX, ENABLE);
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
|
||||
// Enable ADC1 GPIO
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_RX;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL ;
|
||||
GPIO_Init(PORT_RX, &GPIO_InitStruct);
|
||||
|
||||
// Enable ADC clock
|
||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);
|
||||
// Enable ADC2 clock
|
||||
RCC_AHB1PeriphClockCmd(RCC_Per_RSSI, ENABLE);
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
|
||||
// Enable ADC2 GPIO
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_RSSI;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL ;
|
||||
GPIO_Init(PORT_RSSI, &GPIO_InitStruct);
|
||||
#endif
|
||||
|
||||
// Init ADCs in dual mode (RSSI), div clock by two
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
|
||||
#else
|
||||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
|
||||
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
|
||||
#endif
|
||||
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
|
||||
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
|
||||
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
|
||||
ADC_CommonInit(&ADC_CommonInitStructure);
|
||||
|
||||
// Init ADC1 and ADC2: 12bit, single-conversion
|
||||
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
|
||||
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
||||
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
||||
ADC_InitStructure.ADC_ExternalTrigConvEdge = 0;
|
||||
ADC_InitStructure.ADC_ExternalTrigConv = 0;
|
||||
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
ADC_InitStructure.ADC_NbrOfConversion = 1;
|
||||
|
||||
ADC_Init(ADC1, &ADC_InitStructure);
|
||||
|
||||
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
|
||||
ADC_RegularChannelConfig(ADC1, PIN_RX_CH, 1, ADC_SampleTime_3Cycles);
|
||||
|
||||
// Enable ADC1
|
||||
ADC_Cmd(ADC1, ENABLE);
|
||||
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_RX | PIN_RSSI;
|
||||
#else
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_RX;
|
||||
#endif
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL ;
|
||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
ADC_Init(ADC2, &ADC_InitStructure);
|
||||
|
||||
// Init ADCs in dual mode (RSSI), div clock by two
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
|
||||
#else
|
||||
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
|
||||
#endif
|
||||
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
|
||||
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
|
||||
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
|
||||
ADC_CommonInit(&ADC_CommonInitStructure);
|
||||
ADC_EOCOnEachRegularChannelCmd(ADC2, ENABLE);
|
||||
ADC_RegularChannelConfig(ADC2, PIN_RSSI_CH, 1, ADC_SampleTime_3Cycles);
|
||||
|
||||
// Init ADC1 and ADC2: 12bit, single-conversion
|
||||
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
|
||||
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
||||
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
||||
ADC_InitStructure.ADC_ExternalTrigConvEdge = 0;
|
||||
ADC_InitStructure.ADC_ExternalTrigConv = 0;
|
||||
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
ADC_InitStructure.ADC_NbrOfConversion = 1;
|
||||
|
||||
ADC_Init(ADC1, &ADC_InitStructure);
|
||||
|
||||
ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
|
||||
ADC_RegularChannelConfig(ADC1, PIN_RX_CH, 1, ADC_SampleTime_3Cycles);
|
||||
|
||||
// Enable ADC1
|
||||
ADC_Cmd(ADC1, ENABLE);
|
||||
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
ADC_Init(ADC2, &ADC_InitStructure);
|
||||
|
||||
ADC_EOCOnEachRegularChannelCmd(ADC2, ENABLE);
|
||||
ADC_RegularChannelConfig(ADC2, PIN_RSSI_CH, 1, ADC_SampleTime_3Cycles);
|
||||
|
||||
// Enable ADC2
|
||||
ADC_Cmd(ADC2, ENABLE);
|
||||
// Enable ADC2
|
||||
ADC_Cmd(ADC2, ENABLE);
|
||||
#endif
|
||||
|
||||
// Init the DAC
|
||||
DAC_InitTypeDef DAC_InitStructure;
|
||||
// Init the DAC
|
||||
DAC_InitTypeDef DAC_InitStructure;
|
||||
|
||||
GPIO_StructInit(&GPIO_InitStruct);
|
||||
DAC_StructInit(&DAC_InitStructure);
|
||||
GPIO_StructInit(&GPIO_InitStruct);
|
||||
DAC_StructInit(&DAC_InitStructure);
|
||||
|
||||
// GPIOA clock enable
|
||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
||||
// GPIOA clock enable
|
||||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
|
||||
|
||||
// DAC Periph clock enable
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
|
||||
// DAC Periph clock enable
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
|
||||
|
||||
// GPIO CONFIGURATION of DAC Pins (PA4)
|
||||
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
// GPIO CONFIGURATION of DAC Pin
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_TX;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
|
||||
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
|
||||
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
|
||||
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
|
||||
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
|
||||
DAC_Cmd(DAC_Channel_1, ENABLE);
|
||||
DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
|
||||
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
|
||||
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
|
||||
DAC_Init(PIN_TX_CH, &DAC_InitStructure);
|
||||
DAC_Cmd(PIN_TX_CH, ENABLE);
|
||||
|
||||
// Init the timer
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
// Init the timer
|
||||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
|
||||
|
||||
#if defined(EXTERNAL_OSC)
|
||||
// Configure GPIO PA15 as external TIM2 clock source
|
||||
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_TIM2);
|
||||
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
// Configure a GPIO as external TIM2 clock source
|
||||
GPIO_PinAFConfig(PORT_EXT_CLK, SRC_EXT_CLK, GPIO_AF_TIM2);
|
||||
GPIO_InitStruct.GPIO_Pin = PIN_EXT_CLK;
|
||||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
|
||||
GPIO_Init(PORT_EXT_CLK, &GPIO_InitStruct);
|
||||
#endif
|
||||
|
||||
TIM_TimeBaseInitTypeDef timerInitStructure;
|
||||
TIM_TimeBaseStructInit (&timerInitStructure);
|
||||
TIM_TimeBaseInitTypeDef timerInitStructure;
|
||||
TIM_TimeBaseStructInit (&timerInitStructure);
|
||||
|
||||
// TIM2 output frequency
|
||||
// TIM2 output frequency
|
||||
#if defined(EXTERNAL_OSC)
|
||||
timerInitStructure.TIM_Prescaler = (uint16_t) ((EXTERNAL_OSC/(2*SAMP_FREQ)) - 1);
|
||||
timerInitStructure.TIM_Prescaler = (uint16_t) ((EXTERNAL_OSC/(2*SAMP_FREQ)) - 1);
|
||||
#else
|
||||
timerInitStructure.TIM_Prescaler = (uint16_t) ((SystemCoreClock/(4*SAMP_FREQ)) - 1);
|
||||
timerInitStructure.TIM_Prescaler = (uint16_t) ((SystemCoreClock/(4*SAMP_FREQ)) - 1);
|
||||
#endif
|
||||
|
||||
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
timerInitStructure.TIM_Period = 1;
|
||||
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||
timerInitStructure.TIM_RepetitionCounter = 0;
|
||||
TIM_TimeBaseInit(TIM2, &timerInitStructure);
|
||||
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
timerInitStructure.TIM_Period = 1;
|
||||
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||
timerInitStructure.TIM_RepetitionCounter = 0;
|
||||
TIM_TimeBaseInit(TIM2, &timerInitStructure);
|
||||
|
||||
#if defined(EXTERNAL_OSC)
|
||||
// Enable external clock (PA15)
|
||||
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
|
||||
// Enable external clock
|
||||
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
|
||||
#else
|
||||
// Enable internal clock
|
||||
TIM_InternalClockConfig(TIM2);
|
||||
// Enable internal clock
|
||||
TIM_InternalClockConfig(TIM2);
|
||||
#endif
|
||||
|
||||
// Enable TIM2
|
||||
TIM_Cmd(TIM2, ENABLE);
|
||||
// Enable TIM2 interrupt
|
||||
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
|
||||
// Enable TIM2
|
||||
TIM_Cmd(TIM2, ENABLE);
|
||||
// Enable TIM2 interrupt
|
||||
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
|
||||
|
||||
NVIC_InitTypeDef nvicStructure;
|
||||
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
|
||||
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvicStructure.NVIC_IRQChannelSubPriority = 1;
|
||||
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvicStructure);
|
||||
NVIC_InitTypeDef nvicStructure;
|
||||
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
|
||||
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
|
||||
nvicStructure.NVIC_IRQChannelSubPriority = 1;
|
||||
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
|
||||
NVIC_Init(&nvicStructure);
|
||||
|
||||
GPIO_ResetBits(PORT_COSLED, PIN_COSLED);
|
||||
GPIO_SetBits(PORT_LED, PIN_LED);
|
||||
GPIO_ResetBits(PORT_COSLED, PIN_COSLED);
|
||||
GPIO_SetBits(PORT_LED, PIN_LED);
|
||||
}
|
||||
|
||||
void CIO::interrupt(uint8_t source)
|
||||
void CIO::interrupt()
|
||||
{
|
||||
uint8_t control = MARK_NONE;
|
||||
uint16_t sample = DC_OFFSET;
|
||||
uint16_t rawRSSI = 0U;
|
||||
uint8_t control = MARK_NONE;
|
||||
uint16_t sample = DC_OFFSET;
|
||||
uint16_t rawRSSI = 0U;
|
||||
|
||||
m_txBuffer.get(sample, control);
|
||||
m_txBuffer.get(sample, control);
|
||||
|
||||
// Send the value to the DAC
|
||||
DAC_SetChannel1Data(DAC_Align_12b_R, sample);
|
||||
|
||||
// Read value from ADC1 and ADC2
|
||||
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)) {
|
||||
// shouldn't be still in reset at this point so null the sample value?
|
||||
sample = 0U;
|
||||
} else {
|
||||
sample = ADC_GetConversionValue(ADC1);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
rawRSSI = ADC_GetConversionValue(ADC2);
|
||||
// Send the value to the DAC
|
||||
#if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)
|
||||
DAC_SetChannel2Data(DAC_Align_12b_R, sample);
|
||||
#else
|
||||
DAC_SetChannel1Data(DAC_Align_12b_R, sample);
|
||||
#endif
|
||||
}
|
||||
|
||||
// trigger next ADC1
|
||||
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
|
||||
ADC_SoftwareStartConv(ADC1);
|
||||
// Read value from ADC1 and ADC2
|
||||
if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)) {
|
||||
// shouldn't be still in reset at this point so null the sample value?
|
||||
sample = 0U;
|
||||
} else {
|
||||
sample = ADC_GetConversionValue(ADC1);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
rawRSSI = ADC_GetConversionValue(ADC2);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_rxBuffer.put(sample, control);
|
||||
m_rssiBuffer.put(rawRSSI);
|
||||
// trigger next ADC1
|
||||
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
|
||||
ADC_SoftwareStartConv(ADC1);
|
||||
|
||||
m_watchdog++;
|
||||
m_rxBuffer.put(sample, control);
|
||||
m_rssiBuffer.put(rawRSSI);
|
||||
|
||||
m_watchdog++;
|
||||
}
|
||||
|
||||
bool CIO::getCOSInt()
|
||||
{
|
||||
return GPIO_ReadOutputDataBit(PORT_COS, PIN_COS) == Bit_SET;
|
||||
return GPIO_ReadOutputDataBit(PORT_COS, PIN_COS) == Bit_SET;
|
||||
}
|
||||
|
||||
void CIO::setLEDInt(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_LED, PIN_LED, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_LED, PIN_LED, on ? Bit_SET : Bit_RESET);
|
||||
}
|
||||
|
||||
void CIO::setPTTInt(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_PTT, PIN_PTT, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_PTT, PIN_PTT, on ? Bit_SET : Bit_RESET);
|
||||
}
|
||||
|
||||
void CIO::setCOSInt(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_COSLED, PIN_COSLED, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_COSLED, PIN_COSLED, on ? Bit_SET : Bit_RESET);
|
||||
}
|
||||
|
||||
void CIO::setDStarInt(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MDSTAR, PIN_MDSTAR, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setDMRInt(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_DMR, PIN_DMR, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_DMR, PIN_DMR, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MDMR, PIN_MDMR, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setYSFInt(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_YSF, PIN_YSF, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_YSF, PIN_YSF, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MYSF, PIN_MYSF, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::setP25Int(bool on)
|
||||
{
|
||||
GPIO_WriteBit(PORT_P25, PIN_P25, on ? Bit_SET : Bit_RESET);
|
||||
GPIO_WriteBit(PORT_P25, PIN_P25, on ? Bit_SET : Bit_RESET);
|
||||
#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO)
|
||||
GPIO_WriteBit(PORT_MP25, PIN_MP25, on ? Bit_SET : Bit_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
|
||||
* Copyright (C) 2016, 2017 by Andy Uribe CA6JAU
|
||||
* Copyright (C) 2017 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
|
||||
|
||||
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)
|
||||
|
||||
USART2_TXD PA2 output (AF)
|
||||
USART2_RXD PA3 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_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_USART2_TXD 2
|
||||
#define PORT_USART2_TXD GPIOA
|
||||
#define PIN_USART2_RXD 3
|
||||
#define PORT_USART2_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
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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_USART2_TXD, PIN_USART2_TXD, GPIO_CRL_MODE0_1|GPIO_CRL_CNF0_1);
|
||||
GPIOConfigPin(PORT_USART2_RXD, PIN_USART2_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;
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void CIO::startInt()
|
||||
{
|
||||
TimerInit();
|
||||
|
||||
BB_COSLED = 0;
|
||||
BB_LED = 1;
|
||||
}
|
||||
|
||||
void CIO::interrupt()
|
||||
{
|
||||
uint8_t control = MARK_NONE;
|
||||
uint16_t sample = DC_OFFSET;
|
||||
uint16_t rawRSSI = 0U;
|
||||
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 (*eoc) {
|
||||
// trigger next conversion
|
||||
*adon = 1;
|
||||
|
||||
m_txBuffer.get(sample, control);
|
||||
DAC->DHR12R1 = sample; // Send the value to the DAC
|
||||
|
||||
// Read value from ADC1 and ADC2
|
||||
sample = ADC1->DR; // read conversion result; EOC is cleared by this read
|
||||
m_rxBuffer.put(sample, control);
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
rawRSSI = ADC2->DR;
|
||||
m_rssiBuffer.put(rawRSSI);
|
||||
#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;
|
||||
}
|
||||
|
||||
#endif
|
||||
+28
-42
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 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
|
||||
@@ -34,8 +34,8 @@
|
||||
#define PIN_DMR 10
|
||||
#define PIN_YSF 11
|
||||
#define PIN_P25 12
|
||||
#define PIN_ADC 5 // A0, Pin 14
|
||||
#define PIN_RSSI 8 // A2, Pin 16
|
||||
#define PIN_ADC 5 // A0, Pin 14
|
||||
#define PIN_RSSI 4 // Teensy 3.5/3.6, A16, Pin 35. Teensy 3.1/3.2, A17, Pin 28
|
||||
|
||||
#define PDB_CHnC1_TOS 0x0100
|
||||
#define PDB_CHnC1_EN 0x0001
|
||||
@@ -45,15 +45,8 @@ const uint16_t DC_OFFSET = 2048U;
|
||||
extern "C" {
|
||||
void adc0_isr()
|
||||
{
|
||||
io.interrupt(0U);
|
||||
io.interrupt();
|
||||
}
|
||||
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
void adc1_isr()
|
||||
{
|
||||
io.interrupt(1U);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CIO::initInt()
|
||||
@@ -77,17 +70,17 @@ void CIO::startInt()
|
||||
{
|
||||
// Initialise the DAC
|
||||
SIM_SCGC2 |= SIM_SCGC2_DAC0;
|
||||
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 1.2V VDDA is DACREF_2
|
||||
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // 3.3V VDDA is DACREF_2
|
||||
|
||||
// Initialise ADC0
|
||||
SIM_SCGC6 |= SIM_SCGC6_ADC0;
|
||||
ADC0_CFG1 = ADC_CFG1_ADIV(1) | ADC_CFG1_ADICLK(1) | // Single-ended 12 bits, long sample time
|
||||
ADC_CFG1_MODE(1) | ADC_CFG1_ADLSMP;
|
||||
ADC0_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(2); // Select channels ADxxxb
|
||||
ADC0_SC2 = ADC_SC2_REFSEL(1) | ADC_SC2_ADTRG; // Voltage ref internal, hardware trigger
|
||||
ADC0_SC2 = ADC_SC2_REFSEL(0) | ADC_SC2_ADTRG; // Voltage ref external, hardware trigger
|
||||
ADC0_SC3 = ADC_SC3_AVGE | ADC_SC3_AVGS(0); // Enable averaging, 4 samples
|
||||
|
||||
ADC0_SC3 = ADC_SC3_CAL;
|
||||
ADC0_SC3 |= ADC_SC3_CAL;
|
||||
while (ADC0_SC3 & ADC_SC3_CAL) // Wait for calibration
|
||||
;
|
||||
|
||||
@@ -105,10 +98,10 @@ void CIO::startInt()
|
||||
ADC1_CFG1 = ADC_CFG1_ADIV(1) | ADC_CFG1_ADICLK(1) | // Single-ended 12 bits, long sample time
|
||||
ADC_CFG1_MODE(1) | ADC_CFG1_ADLSMP;
|
||||
ADC1_CFG2 = ADC_CFG2_MUXSEL | ADC_CFG2_ADLSTS(2); // Select channels ADxxxb
|
||||
ADC1_SC2 = ADC_SC2_REFSEL(1); // Voltage ref internal, software trigger
|
||||
ADC1_SC2 = ADC_SC2_REFSEL(0); // Voltage ref external, software trigger
|
||||
ADC1_SC3 = ADC_SC3_AVGE | ADC_SC3_AVGS(0); // Enable averaging, 4 samples
|
||||
|
||||
ADC1_SC3 = ADC_SC3_CAL;
|
||||
ADC1_SC3 |= ADC_SC3_CAL;
|
||||
while (ADC1_SC3 & ADC_SC3_CAL) // Wait for calibration
|
||||
;
|
||||
|
||||
@@ -116,14 +109,11 @@ void CIO::startInt()
|
||||
ADC1_CLP2 + ADC1_CLP1 + ADC1_CLP0;
|
||||
sum1 = (sum1 / 2U) | 0x8000U;
|
||||
ADC1_PG = sum1;
|
||||
|
||||
NVIC_ENABLE_IRQ(IRQ_ADC1);
|
||||
#endif
|
||||
|
||||
#if defined(EXTERNAL_OSC)
|
||||
// Set ADC0 to trigger from the LPTMR at 24 kHz
|
||||
SIM_SOPT7 = SIM_SOPT7_ADC0ALTTRGEN | // Enable ADC0 alternate trigger
|
||||
SIM_SOPT7_ADC0PRETRGSEL | // Enable ADC0 pre-trigger
|
||||
SIM_SOPT7_ADC0TRGSEL(14); // Trigger ADC0 by LPTMR0
|
||||
|
||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(3);
|
||||
@@ -151,37 +141,33 @@ void CIO::startInt()
|
||||
digitalWrite(PIN_LED, HIGH);
|
||||
}
|
||||
|
||||
void CIO::interrupt(uint8_t source)
|
||||
void CIO::interrupt()
|
||||
{
|
||||
if (source == 0U) { // ADC0
|
||||
uint8_t control = MARK_NONE;
|
||||
uint16_t sample = DC_OFFSET;
|
||||
uint8_t control = MARK_NONE;
|
||||
uint16_t sample = DC_OFFSET;
|
||||
|
||||
m_txBuffer.get(sample, control);
|
||||
*(int16_t *)&(DAC0_DAT0L) = sample;
|
||||
m_txBuffer.get(sample, control);
|
||||
*(int16_t *)&(DAC0_DAT0L) = sample;
|
||||
|
||||
if ((ADC0_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
|
||||
sample = ADC0_RA;
|
||||
m_rxBuffer.put(sample, control);
|
||||
if ((ADC0_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
|
||||
sample = ADC0_RA;
|
||||
m_rxBuffer.put(sample, control);
|
||||
}
|
||||
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
ADC1_SC1A = ADC_SC1_AIEN | PIN_RSSI;
|
||||
if ((ADC1_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
|
||||
uint16_t rssi = ADC1_RA;
|
||||
m_rssiBuffer.put(rssi);
|
||||
} else {
|
||||
m_rssiBuffer.put(0U);
|
||||
}
|
||||
|
||||
ADC1_SC1A = PIN_RSSI; // Start the next RSSI conversion
|
||||
#else
|
||||
m_rssiBuffer.put(0U);
|
||||
m_rssiBuffer.put(0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_watchdog++;
|
||||
}
|
||||
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
if (source == 1U) { // ADC1
|
||||
if ((ADC1_SC1A & ADC_SC1_COCO) == ADC_SC1_COCO) {
|
||||
uint16_t rssi = ADC1_RA;
|
||||
m_rssiBuffer.put(rssi);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_watchdog++;
|
||||
}
|
||||
|
||||
bool CIO::getCOSInt()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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(STM32F4)
|
||||
#if defined(STM32F4XX) || defined(STM32F4) || defined(STM32F105xC)
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
@@ -36,9 +36,6 @@ bool m_duplex = true;
|
||||
bool m_tx = false;
|
||||
bool m_dcd = false;
|
||||
|
||||
uint32_t m_sampleCount = 0U;
|
||||
bool m_sampleInsert = false;
|
||||
|
||||
CDStarRX dstarRX;
|
||||
CDStarTX dstarTX;
|
||||
|
||||
@@ -58,6 +55,7 @@ CP25TX p25TX;
|
||||
CCalDStarRX calDStarRX;
|
||||
CCalDStarTX calDStarTX;
|
||||
CCalDMR calDMR;
|
||||
CCalRSSI calRSSI;
|
||||
|
||||
CCWIdTX cwIdTX;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -33,9 +33,6 @@ bool m_duplex = true;
|
||||
bool m_tx = false;
|
||||
bool m_dcd = false;
|
||||
|
||||
uint32_t m_sampleCount = 0U;
|
||||
bool m_sampleInsert = false;
|
||||
|
||||
CDStarRX dstarRX;
|
||||
CDStarTX dstarTX;
|
||||
|
||||
@@ -55,6 +52,7 @@ CP25TX p25TX;
|
||||
CCalDStarRX calDStarRX;
|
||||
CCalDStarTX calDStarTX;
|
||||
CCalDMR calDMR;
|
||||
CCalRSSI calRSSI;
|
||||
|
||||
CCWIdTX cwIdTX;
|
||||
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
<File name="DMRDMOTX.cpp" path="DMRDMOTX.cpp" type="1"/>
|
||||
<File name="DMRRX.cpp" path="DMRRX.cpp" type="1"/>
|
||||
<File name="YSFRX.cpp" path="YSFRX.cpp" type="1"/>
|
||||
<File name="CalRSSI.cpp" path="CalRSSI.cpp" type="1"/>
|
||||
<File name="STM32F4XX_Lib/Device/stm32f4xx.h" path="STM32F4XX_Lib/Device/stm32f4xx.h" type="1"/>
|
||||
<File name="SampleRB.cpp" path="SampleRB.cpp" type="1"/>
|
||||
<File name="CalDStarTX.h" path="CalDStarTX.h" type="1"/>
|
||||
@@ -261,6 +262,7 @@
|
||||
<File name="SampleRB.h" path="SampleRB.h" type="1"/>
|
||||
<File name="YSFTX.h" path="YSFTX.h" type="1"/>
|
||||
<File name="DMRRX.h" path="DMRRX.h" type="1"/>
|
||||
<File name="CalRSSI.h" path="CalRSSI.h" type="1"/>
|
||||
<File name="IOTeensy.cpp" path="IOTeensy.cpp" type="1"/>
|
||||
<File name="MMDVM.cpp" path="MMDVM.cpp" type="1"/>
|
||||
<File name="STM32F4XX_Lib/CMSIS/Include/core_cmSimd.h" path="STM32F4XX_Lib/CMSIS/Include/core_cmSimd.h" type="1"/>
|
||||
|
||||
@@ -36,19 +36,19 @@ ifdef SYSTEMROOT
|
||||
ASOURCES=$(shell dir /S /B *.s)
|
||||
CSOURCES=$(shell dir /S /B *.c)
|
||||
CXXSOURCES=$(shell dir /S /B *.cpp)
|
||||
CLEANCMD=del /S *.o *.hex *.bin *.elf
|
||||
CLEANCMD=del /S *.o *.hex *.bin *.elf GitVersion.h
|
||||
MDBIN=md $@
|
||||
else ifdef SystemRoot
|
||||
ASOURCES=$(shell dir /S /B *.s)
|
||||
CSOURCES=$(shell dir /S /B *.c)
|
||||
CXXSOURCES=$(shell dir /S /B *.cpp)
|
||||
CLEANCMD=del /S *.o *.hex *.bin *.elf
|
||||
CLEANCMD=del /S *.o *.hex *.bin *.elf GitVersion.h
|
||||
MDBIN=md $@
|
||||
else
|
||||
ASOURCES=$(shell find . -name '*.s')
|
||||
CSOURCES=$(shell find . -name '*.c')
|
||||
CXXSOURCES=$(shell find . -name '*.cpp')
|
||||
CLEANCMD=rm -f $(OBJECTS) $(BINDIR)/$(BINELF) $(BINDIR)/$(BINHEX) $(BINDIR)/$(BINBIN)
|
||||
CLEANCMD=rm -f $(OBJECTS) $(BINDIR)/$(BINELF) $(BINDIR)/$(BINHEX) $(BINDIR)/$(BINBIN) GitVersion.h
|
||||
MDBIN=mkdir $@
|
||||
endif
|
||||
|
||||
@@ -85,11 +85,11 @@ MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian \
|
||||
|
||||
# COMPILE FLAGS
|
||||
# STM32F4 Discovery board:
|
||||
DEFS_DIS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_DISCOVERY -DHSE_VALUE=$(OSC)
|
||||
DEFS_DIS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_DISCOVERY -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
# Pi board:
|
||||
DEFS_PI=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DARDUINO_MODE_PINS -DSEND_RSSI_DATA -DSERIAL_REPEATER -DHSE_VALUE=$(OSC)
|
||||
DEFS_PI=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DARDUINO_MODE_PINS -DSEND_RSSI_DATA -DSERIAL_REPEATER -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
# STM32F4 Nucleo 446 board:
|
||||
DEFS_NUCLEO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_NUCLEO -DHSE_VALUE=$(OSC)
|
||||
DEFS_NUCLEO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_NUCLEO -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
|
||||
|
||||
CFLAGS=-c $(MCFLAGS) $(INCLUDES)
|
||||
CXXFLAGS=-c $(MCFLAGS) $(INCLUDES)
|
||||
@@ -101,19 +101,22 @@ LDFLAGS =-T $(LDSCRIPT) $(MCFLAGS) --specs=nosys.specs $(INCLUDES_LIBS) $(LINK_L
|
||||
# Build Rules
|
||||
.PHONY: all release dis pi nucleo debug clean
|
||||
|
||||
# Default target: STM32F4 Discovery board
|
||||
all: dis
|
||||
# Default target: STM32F4 Nucleo F446RE board
|
||||
all: nucleo
|
||||
|
||||
pi: GitVersion.h
|
||||
pi: CFLAGS+=$(DEFS_PI) -Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
|
||||
pi: CXXFLAGS+=$(DEFS_PI) -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
|
||||
pi: LDFLAGS+=-Os --specs=nano.specs
|
||||
pi: release
|
||||
|
||||
nucleo: GitVersion.h
|
||||
nucleo: CFLAGS+=$(DEFS_NUCLEO) -Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
|
||||
nucleo: CXXFLAGS+=$(DEFS_NUCLEO) -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
|
||||
nucleo: LDFLAGS+=-Os --specs=nano.specs
|
||||
nucleo: release
|
||||
|
||||
dis: GitVersion.h
|
||||
dis: CFLAGS+=$(DEFS_DIS) -Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS
|
||||
dis: CXXFLAGS+=$(DEFS_DIS) -Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS
|
||||
dis: LDFLAGS+=-Os --specs=nano.specs
|
||||
@@ -171,3 +174,28 @@ endif
|
||||
ifneq ($(wildcard /opt/openocd/bin/openocd),)
|
||||
/opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/$(BINELF) verify reset exit"
|
||||
endif
|
||||
|
||||
deploy-pi:
|
||||
ifneq ($(wildcard /usr/local/bin/stm32flash),)
|
||||
-/usr/local/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0
|
||||
/usr/local/bin/stm32flash -v -w bin/outp.bin -g 0x0 -R -c /dev/ttyAMA0
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard /usr/bin/stm32flash),)
|
||||
-/usr/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0
|
||||
/usr/bin/stm32flash -v -w bin/outp.bin -g 0x0 -R -c /dev/ttyAMA0
|
||||
endif
|
||||
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/make
|
||||
# makefile for the arduino due (works with arduino IDE 1.6.11)
|
||||
#
|
||||
# The original file can be found at https://github.com/pauldreik/arduino-due-makefile
|
||||
#
|
||||
# USAGE: put this file in the same dir as your .ino file is.
|
||||
# configure the PORT variable and ADIR at the top of the file
|
||||
# to match your local configuration.
|
||||
# Type make upload to compile and upload.
|
||||
#
|
||||
|
||||
#user specific settings:
|
||||
#where to find the IDE
|
||||
ADIR:=$(HOME)/.arduino15
|
||||
#which serial port to use (add a file with SUBSYSTEMS=="usb",
|
||||
#ATTRS{product}=="Arduino Due Prog. Port", ATTRS{idProduct}=="003d",
|
||||
#ATTRS{idVendor}=="2341", SYMLINK+="arduino_due" in /etc/udev/rules.d/
|
||||
#to get this working). Do not prefix the port with /dev/, just take
|
||||
#the basename.
|
||||
PORT:=ttyACM0
|
||||
#if you want to verify the bossac upload, define this to -v
|
||||
VERIFY:=-v
|
||||
|
||||
|
||||
#end of user configuration.
|
||||
|
||||
|
||||
#then some general settings. They should not be necessary to modify.
|
||||
#CXX:=$(ADIR)/tools/g++_arm_none_eabi/bin/arm-none-eabi-g++
|
||||
CXX:=$(ADIR)/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/bin/arm-none-eabi-g++
|
||||
#CC:=$(ADIR)/tools/g++_arm_none_eabi/bin/arm-none-eabi-gcc
|
||||
CC:=$(ADIR)/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/bin/arm-none-eabi-gcc
|
||||
OBJCOPY:=$(ADIR)/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/bin/arm-none-eabi-objcopy
|
||||
|
||||
C:=$(CC)
|
||||
#SAM:=arduino/sam/
|
||||
SAM:=$(ADIR)/packages/arduino/hardware/sam/1.6.11
|
||||
#CMSIS:=arduino/sam/system/CMSIS/
|
||||
#LIBSAM:=arduino/sam/system/libsam
|
||||
TMPDIR:=$(PWD)/build
|
||||
AR:=$(ADIR)/tools/g++_arm_none_eabi/bin/arm-none-eabi-ar
|
||||
AR:=$(ADIR)/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/bin/arm-none-eabi-ar
|
||||
|
||||
|
||||
#all these values are hard coded and should maybe be configured somehow else,
|
||||
#like olikraus does in his makefile.
|
||||
DEFINES:=-Dprintf=iprintf -DF_CPU=84000000 -DARDUINO=10611 -D__SAM3X8E__ -DUSB_PID=0x003e -DUSB_VID=0x2341 -DUSBCON \
|
||||
-DARDUINO_SAM_DUE -DARDUINO_ARCH_SAM '-DUSB_MANUFACTURER="Arduino LLC"' '-DUSB_PRODUCT="Arduino Due"' \
|
||||
-DMADEBYMAKEFILE
|
||||
|
||||
INCLUDES:=-I$(SAM)/system/libsam -I$(SAM)/system/CMSIS/CMSIS/Include/ \
|
||||
-I$(SAM)/system/CMSIS/Device/ATMEL/ -I$(SAM)/cores/arduino \
|
||||
-I$(SAM)/variants/arduino_due_x
|
||||
|
||||
#also include the current dir for convenience
|
||||
INCLUDES += -I.
|
||||
|
||||
#compilation flags common to both c and c++
|
||||
COMMON_FLAGS:=-g -Os -w -ffunction-sections -fdata-sections -nostdlib \
|
||||
--param max-inline-insns-single=500 -mcpu=cortex-m3 -mthumb \
|
||||
-fno-threadsafe-statics
|
||||
#for compiling c (do not warn, this is not our code)
|
||||
CFLAGS:=$(COMMON_FLAGS) -std=gnu11
|
||||
#for compiling c++
|
||||
CXXFLAGS:=$(COMMON_FLAGS) -fno-rtti -fno-exceptions -std=gnu++11 -Wall -Wextra
|
||||
|
||||
#let the results be named after the project
|
||||
PROJNAME:=$(shell basename *.ino .ino)
|
||||
|
||||
#we will make a new mainfile from the ino file.
|
||||
NEWMAINFILE:=$(TMPDIR)/$(PROJNAME).ino.cpp
|
||||
|
||||
#our own sourcefiles is the (converted) ino file and any local cpp files
|
||||
MYSRCFILES:=$(NEWMAINFILE) $(shell ls *.cpp 2>/dev/null)
|
||||
MYOBJFILES:=$(addsuffix .o,$(addprefix $(TMPDIR)/,$(notdir $(MYSRCFILES))))
|
||||
|
||||
#These source files are the ones forming core.a
|
||||
CORESRCXX:=$(shell ls ${SAM}/cores/arduino/*.cpp ${SAM}/cores/arduino/USB/*.cpp ${SAM}/variants/arduino_due_x/variant.cpp)
|
||||
CORESRC:=$(shell ls ${SAM}/cores/arduino/*.c)
|
||||
|
||||
#hey this one is needed too: $(SAM)/cores/arduino/wiring_pulse_asm.S" add -x assembler-with-cpp
|
||||
#and this one: /1.6.11/cores/arduino/avr/dtostrf.c but it seems to work
|
||||
#anyway, probably because I do not use that functionality.
|
||||
|
||||
#convert the core source files to object files. assume no clashes.
|
||||
COREOBJSXX:=$(addprefix $(TMPDIR)/core/,$(notdir $(CORESRCXX)) )
|
||||
COREOBJSXX:=$(addsuffix .o,$(COREOBJSXX))
|
||||
COREOBJS:=$(addprefix $(TMPDIR)/core/,$(notdir $(CORESRC)) )
|
||||
COREOBJS:=$(addsuffix .o,$(COREOBJS))
|
||||
|
||||
default:
|
||||
@echo default rule, does nothing. Try make compile or make upload.
|
||||
|
||||
#This rule is good to just make sure stuff compiles, without having to wait
|
||||
#for bossac.
|
||||
compile: GitVersion.h
|
||||
compile: $(TMPDIR)/$(PROJNAME).elf
|
||||
|
||||
#This is a make rule template to create object files from the source files.
|
||||
# arg 1=src file
|
||||
# arg 2=object file
|
||||
# arg 3= XX if c++, empty if c
|
||||
define OBJ_template
|
||||
$(2): $(1)
|
||||
$(C$(3)) -MD -c $(C$(3)FLAGS) $(DEFINES) $(INCLUDES) $(1) -o $(2)
|
||||
endef
|
||||
#now invoke the template both for c++ sources
|
||||
$(foreach src,$(CORESRCXX), $(eval $(call OBJ_template,$(src),$(addsuffix .o,$(addprefix $(TMPDIR)/core/,$(notdir $(src)))),XX) ) )
|
||||
#...and for c sources:
|
||||
$(foreach src,$(CORESRC), $(eval $(call OBJ_template,$(src),$(addsuffix .o,$(addprefix $(TMPDIR)/core/,$(notdir $(src)))),) ) )
|
||||
|
||||
#and our own c++ sources
|
||||
$(foreach src,$(MYSRCFILES), $(eval $(call OBJ_template,$(src),$(addsuffix .o,$(addprefix $(TMPDIR)/,$(notdir $(src)))),XX) ) )
|
||||
|
||||
|
||||
clean:
|
||||
test ! -d $(TMPDIR) || rm -rf $(TMPDIR)
|
||||
$(RM) GitVersion.h
|
||||
|
||||
.PHONY: .FORCE upload default
|
||||
|
||||
$(TMPDIR):
|
||||
mkdir -p $(TMPDIR)
|
||||
|
||||
$(TMPDIR)/core:
|
||||
mkdir -p $(TMPDIR)/core
|
||||
|
||||
#creates the cpp file from the .ino file
|
||||
$(NEWMAINFILE): $(PROJNAME).ino
|
||||
cat $(SAM)/cores/arduino/main.cpp > $(NEWMAINFILE)
|
||||
cat $(PROJNAME).ino >> $(NEWMAINFILE)
|
||||
echo 'extern "C" void __cxa_pure_virtual() {while (true);}' >> $(NEWMAINFILE)
|
||||
|
||||
#include the dependencies for our own files
|
||||
-include $(MYOBJFILES:.o=.d)
|
||||
|
||||
#create the core library from the core objects. Do this EXACTLY as the
|
||||
#arduino IDE does it, seems *really* picky about this.
|
||||
#Sorry for the hard coding.
|
||||
$(TMPDIR)/core.a: $(TMPDIR)/core $(COREOBJS) $(COREOBJSXX)
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/wiring_shift.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/wiring_analog.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/itoa.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/cortex_handlers.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/hooks.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/wiring.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/WInterrupts.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/syscalls_sam3.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/iar_calls_sam3.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/wiring_digital.c.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/Print.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/USARTClass.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/WString.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/PluggableUSB.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/USBCore.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/CDC.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/wiring_pulse.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/UARTClass.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/main.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/new.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/watchdog.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/Stream.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/RingBuffer.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/IPAddress.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/Reset.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/WMath.cpp.o
|
||||
$(AR) rcs $(TMPDIR)/core.a $(TMPDIR)/core/variant.cpp.o
|
||||
|
||||
#link our own object files with core to form the elf file
|
||||
$(TMPDIR)/$(PROJNAME).elf: $(TMPDIR)/core.a $(TMPDIR)/core/syscalls_sam3.c.o $(MYOBJFILES)
|
||||
$(CC) -mcpu=cortex-m3 -mthumb -Os -Wl,--gc-sections -T$(SAM)/variants/arduino_due_x/linker_scripts/gcc/flash.ld -Wl,-Map,$(NEWMAINFILE).map -o $@ -L$(TMPDIR) -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--start-group -u _sbrk -u link -u _close -u _fstat -u _isatty -u _lseek -u _read -u _write -u _exit -u kill -u _getpid $(MYOBJFILES) $(TMPDIR)/core/variant.cpp.o $(SAM)/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a $(SAM)/system/CMSIS/CMSIS/Lib/GCC/libarm_cortexM3l_math.a $(TMPDIR)/core.a -Wl,--end-group -lm -gcc
|
||||
|
||||
#copy from the hex to our bin file (why?)
|
||||
$(TMPDIR)/$(PROJNAME).bin: $(TMPDIR)/$(PROJNAME).elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
|
||||
#upload to the arduino by first resetting it (stty) and the running bossac
|
||||
upload: $(TMPDIR)/$(PROJNAME).bin
|
||||
stty -F /dev/$(PORT) cs8 1200 hupcl
|
||||
$(ADIR)/packages/arduino/tools/bossac/1.6.1-arduino/bossac -i --port=$(PORT) -U false -e -w $(VERIFY) -b $(TMPDIR)/$(PROJNAME).bin -R
|
||||
|
||||
# Export the current git version if the index file exists, else 000...
|
||||
GitVersion.h: .FORCE
|
||||
ifneq ("$(wildcard .git/index)","")
|
||||
echo "#define GITVERSION \"$(shell git rev-parse --short HEAD)\"" > $@
|
||||
else
|
||||
echo "#define GITVERSION \"0000000\"" > $@
|
||||
endif
|
||||
|
||||
.FORCE:
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
# 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:=/opt/STM32Cube_FW_F1_V1.4.0/Drivers/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:=/dev/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)
|
||||
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: $(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 $(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)
|
||||
|
||||
# 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)
|
||||
+11
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 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
|
||||
@@ -24,25 +24,31 @@ const unsigned int P25_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||
const unsigned int P25_HDR_FRAME_LENGTH_BYTES = 99U;
|
||||
const unsigned int P25_HDR_FRAME_LENGTH_BITS = P25_HDR_FRAME_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_HDR_FRAME_LENGTH_SYMBOLS = P25_HDR_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_HDR_FRAME_LENGTH_SAMPLES = P25_HDR_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_LDU_FRAME_LENGTH_BYTES = 216U;
|
||||
const unsigned int P25_LDU_FRAME_LENGTH_BITS = P25_LDU_FRAME_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_LDU_FRAME_LENGTH_SYMBOLS = P25_LDU_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_LDU_FRAME_LENGTH_SAMPLES = P25_LDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_TERMLC_FRAME_LENGTH_BYTES = 54U;
|
||||
const unsigned int P25_TERMLC_FRAME_LENGTH_BITS = P25_TERMLC_FRAME_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_TERMLC_FRAME_LENGTH_SYMBOLS = P25_TERMLC_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_TERMLC_FRAME_LENGTH_SAMPLES = P25_TERMLC_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_TERM_FRAME_LENGTH_BYTES = 18U;
|
||||
const unsigned int P25_TERM_FRAME_LENGTH_BITS = P25_TERM_FRAME_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_TERM_FRAME_LENGTH_SYMBOLS = P25_TERM_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_TERM_FRAME_LENGTH_SAMPLES = P25_TERM_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_SYNC_LENGTH_BYTES = 6U;
|
||||
const unsigned int P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U;
|
||||
const unsigned int P25_SYNC_LENGTH_SYMBOLS = P25_SYNC_LENGTH_BYTES * 4U;
|
||||
const unsigned int P25_SYNC_LENGTH_SAMPLES = P25_SYNC_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int P25_NID_LENGTH_BITS = 64U;
|
||||
const unsigned int P25_NID_LENGTH_SYMBOLS = 32U;
|
||||
const unsigned int P25_NID_LENGTH_BITS = 64U;
|
||||
const unsigned int P25_NID_LENGTH_SYMBOLS = 32U;
|
||||
const unsigned int P25_NID_LENGTH_SAMPLESS = P25_NID_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const uint8_t P25_SYNC_BYTES[] = {0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU};
|
||||
const uint8_t P25_SYNC_BYTES_LENGTH = 6U;
|
||||
@@ -54,6 +60,8 @@ const uint64_t P25_SYNC_BITS_MASK = 0x0000FFFFFFFFFFFFU;
|
||||
// 01 01 01 01 01 11 01 01 11 11 01 01 11 11 11 11 01 11 01 11 11 11 11 11
|
||||
// +3 +3 +3 +3 +3 -3 +3 +3 -3 -3 +3 +3 -3 -3 -3 -3 +3 -3 +3 -3 -3 -3 -3 -3
|
||||
|
||||
const int8_t P25_SYNC_SYMBOLS_VALUES[] = {+3, +3, +3, +3, +3, -3, +3, +3, -3, -3, +3, +3, -3, -3, -3, -3, +3, -3, +3, -3, -3, -3, -3, -3};
|
||||
|
||||
const uint32_t P25_SYNC_SYMBOLS = 0x00FB30A0U;
|
||||
const uint32_t P25_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,227 +16,213 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "P25RX.h"
|
||||
#include "Utils.h"
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.57)
|
||||
|
||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.55)
|
||||
const uint8_t MAX_SYNC_BIT_START_ERRS = 2U;
|
||||
const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U;
|
||||
|
||||
const uint32_t PLLMAX = 0x10000U;
|
||||
const uint32_t PLLINC = PLLMAX / P25_RADIO_SYMBOL_LENGTH;
|
||||
const uint32_t INC = PLLINC / 32U;
|
||||
const uint8_t MAX_SYNC_SYMBOLS_ERRS = 2U;
|
||||
|
||||
const uint8_t SYNC_SYMBOL_ERRS = 0U;
|
||||
|
||||
const uint8_t SYNC_BIT_START_ERRS = 2U;
|
||||
const uint8_t SYNC_BIT_RUN_ERRS = 4U;
|
||||
|
||||
const unsigned int MAX_SYNC_FRAMES = 3U + 1U;
|
||||
|
||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
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])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint8_t NOAVEPTR = 99U;
|
||||
|
||||
const uint16_t NOENDPTR = 9999U;
|
||||
|
||||
const unsigned int MAX_SYNC_FRAMES = 4U + 1U;
|
||||
|
||||
CP25RX::CP25RX() :
|
||||
m_pll(0U),
|
||||
m_prev(false),
|
||||
m_state(P25RXS_NONE),
|
||||
m_bitBuffer(0x00U),
|
||||
m_symbols(),
|
||||
m_outBuffer(),
|
||||
m_buffer(NULL),
|
||||
m_bufferPtr(0U),
|
||||
m_symbolPtr(0U),
|
||||
m_bitBuffer(),
|
||||
m_buffer(),
|
||||
m_bitPtr(0U),
|
||||
m_dataPtr(0U),
|
||||
m_hdrStartPtr(NOENDPTR),
|
||||
m_lduStartPtr(NOENDPTR),
|
||||
m_lduEndPtr(NOENDPTR),
|
||||
m_minSyncPtr(NOENDPTR),
|
||||
m_maxSyncPtr(NOENDPTR),
|
||||
m_hdrSyncPtr(NOENDPTR),
|
||||
m_lduSyncPtr(NOENDPTR),
|
||||
m_maxCorr(0),
|
||||
m_lostCount(0U),
|
||||
m_centre(0),
|
||||
m_threshold(0)
|
||||
m_countdown(0U),
|
||||
m_centre(),
|
||||
m_centreVal(0),
|
||||
m_threshold(),
|
||||
m_thresholdVal(0),
|
||||
m_averagePtr(NOAVEPTR),
|
||||
m_rssiAccum(0U),
|
||||
m_rssiCount(0U)
|
||||
{
|
||||
m_buffer = m_outBuffer + 1U;
|
||||
}
|
||||
|
||||
void CP25RX::reset()
|
||||
{
|
||||
m_pll = 0U;
|
||||
m_prev = false;
|
||||
m_state = P25RXS_NONE;
|
||||
m_bitBuffer = 0x00U;
|
||||
m_bufferPtr = 0U;
|
||||
m_symbolPtr = 0U;
|
||||
m_lostCount = 0U;
|
||||
m_centre = 0;
|
||||
m_threshold = 0;
|
||||
m_state = P25RXS_NONE;
|
||||
m_dataPtr = 0U;
|
||||
m_bitPtr = 0U;
|
||||
m_maxCorr = 0;
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_hdrStartPtr = NOENDPTR;
|
||||
m_lduStartPtr = NOENDPTR;
|
||||
m_lduEndPtr = NOENDPTR;
|
||||
m_hdrSyncPtr = NOENDPTR;
|
||||
m_lduSyncPtr = NOENDPTR;
|
||||
m_minSyncPtr = NOENDPTR;
|
||||
m_maxSyncPtr = NOENDPTR;
|
||||
m_centreVal = 0;
|
||||
m_thresholdVal = 0;
|
||||
m_lostCount = 0U;
|
||||
m_countdown = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CP25RX::samples(const q15_t* samples, uint8_t length)
|
||||
void CP25RX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
|
||||
{
|
||||
for (uint16_t i = 0U; i < length; i++) {
|
||||
bool bit = samples[i] < 0;
|
||||
for (uint8_t i = 0U; i < length; i++) {
|
||||
q15_t sample = samples[i];
|
||||
|
||||
if (bit != m_prev) {
|
||||
if (m_pll < (PLLMAX / 2U))
|
||||
m_pll += INC;
|
||||
else
|
||||
m_pll -= INC;
|
||||
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 P25RXS_HDR:
|
||||
processHdr(sample);
|
||||
break;
|
||||
case P25RXS_LDU:
|
||||
processLdu(sample);
|
||||
break;
|
||||
default:
|
||||
processNone(sample);
|
||||
break;
|
||||
}
|
||||
|
||||
m_prev = bit;
|
||||
m_dataPtr++;
|
||||
if (m_dataPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_dataPtr = 0U;
|
||||
|
||||
m_pll += PLLINC;
|
||||
|
||||
if (m_pll >= PLLMAX) {
|
||||
m_pll -= PLLMAX;
|
||||
|
||||
if (m_state == P25RXS_NONE)
|
||||
processNone(samples[i]);
|
||||
else
|
||||
processData(samples[i]);
|
||||
}
|
||||
m_bitPtr++;
|
||||
if (m_bitPtr >= P25_RADIO_SYMBOL_LENGTH)
|
||||
m_bitPtr = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CP25RX::processNone(q15_t sample)
|
||||
{
|
||||
m_symbolBuffer <<= 1;
|
||||
if (sample < 0)
|
||||
m_symbolBuffer |= 0x01U;
|
||||
|
||||
m_symbols[m_symbolPtr] = sample;
|
||||
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits32((m_symbolBuffer & P25_SYNC_SYMBOLS_MASK) ^ P25_SYNC_SYMBOLS) <= SYNC_SYMBOL_ERRS) {
|
||||
q15_t max = -16000;
|
||||
q15_t min = 16000;
|
||||
|
||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_symbols[i];
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
}
|
||||
|
||||
q15_t centre = (max + min) >> 1;
|
||||
|
||||
q31_t v1 = (max - centre) * SCALING_FACTOR;
|
||||
q15_t threshold = q15_t(v1 >> 15);
|
||||
|
||||
uint16_t ptr = m_symbolPtr + 1U;
|
||||
if (ptr >= P25_SYNC_LENGTH_SYMBOLS)
|
||||
ptr = 0U;
|
||||
|
||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t sample = m_symbols[ptr] - centre;
|
||||
|
||||
if (sample < -threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x01U;
|
||||
} else if (sample < 0) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x00U;
|
||||
} else if (sample < threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x02U;
|
||||
} else {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x03U;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
if (ptr >= P25_SYNC_LENGTH_SYMBOLS)
|
||||
ptr = 0U;
|
||||
}
|
||||
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS) <= SYNC_BIT_START_ERRS) {
|
||||
DEBUG5("P25RX: sync found in None min/max/centre/threshold", min, max, centre, threshold);
|
||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_BYTES; i++)
|
||||
m_buffer[i] = P25_SYNC_BYTES[i];
|
||||
m_centre = centre;
|
||||
m_threshold = threshold;
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
m_bufferPtr = P25_SYNC_LENGTH_BITS;
|
||||
m_state = P25RXS_DATA;
|
||||
bool ret = correlateSync();
|
||||
if (ret) {
|
||||
// On the first sync, start the countdown to the state change
|
||||
if (m_countdown == 0U) {
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
m_averagePtr = NOAVEPTR;
|
||||
|
||||
m_countdown = 5U;
|
||||
}
|
||||
}
|
||||
|
||||
m_symbolPtr++;
|
||||
if (m_symbolPtr >= P25_SYNC_LENGTH_SYMBOLS)
|
||||
m_symbolPtr = 0U;
|
||||
if (m_countdown > 0U)
|
||||
m_countdown--;
|
||||
|
||||
if (m_countdown == 1U) {
|
||||
// These are the sync positions for the following LDU after a HDR
|
||||
m_minSyncPtr = m_hdrSyncPtr + P25_HDR_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_hdrSyncPtr + P25_HDR_FRAME_LENGTH_SAMPLES + 1U;
|
||||
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_state = P25RXS_HDR;
|
||||
m_countdown = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CP25RX::processData(q15_t sample)
|
||||
void CP25RX::processHdr(q15_t sample)
|
||||
{
|
||||
sample -= m_centre;
|
||||
|
||||
if (sample < -m_threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x01U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
} else if (sample < 0) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x00U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
} else if (sample < m_threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x02U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
if (m_minSyncPtr < m_maxSyncPtr) {
|
||||
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
|
||||
correlateSync();
|
||||
} else {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x03U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
|
||||
correlateSync();
|
||||
}
|
||||
|
||||
// Search for an early sync to indicate an LDU following a header
|
||||
if (m_bufferPtr >= (P25_HDR_FRAME_LENGTH_BITS + P25_SYNC_LENGTH_BITS - 1U) && m_bufferPtr <= (P25_HDR_FRAME_LENGTH_BITS + P25_SYNC_LENGTH_BITS + 1U)) {
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS) <= SYNC_BIT_RUN_ERRS) {
|
||||
DEBUG2("P25RX: found LDU sync in Data, pos", m_bufferPtr - P25_SYNC_LENGTH_BITS);
|
||||
if (m_dataPtr == m_maxSyncPtr) {
|
||||
if (m_hdrSyncPtr != m_lduSyncPtr) {
|
||||
calculateLevels(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
m_outBuffer[0U] = 0x01U;
|
||||
serial.writeP25Hdr(m_outBuffer, P25_HDR_FRAME_LENGTH_BYTES + 1U);
|
||||
DEBUG4("P25RX: sync found in Hdr pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
// Restore the sync that's now in the wrong place
|
||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_BYTES; i++)
|
||||
m_buffer[i] = P25_SYNC_BYTES[i];
|
||||
uint8_t frame[P25_HDR_FRAME_LENGTH_BYTES + 1U];
|
||||
samplesToBits(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
m_bufferPtr = P25_SYNC_LENGTH_BITS;
|
||||
frame[0U] = 0x01U;
|
||||
serial.writeP25Hdr(frame, P25_HDR_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
|
||||
m_minSyncPtr = m_lduSyncPtr + P25_LDU_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_lduSyncPtr + 1U;
|
||||
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_state = P25RXS_LDU;
|
||||
m_maxCorr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CP25RX::processLdu(q15_t sample)
|
||||
{
|
||||
if (m_minSyncPtr < m_maxSyncPtr) {
|
||||
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
|
||||
correlateSync();
|
||||
} else {
|
||||
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
|
||||
correlateSync();
|
||||
}
|
||||
|
||||
// Only search for a sync in the right place +-2 symbols
|
||||
if (m_bufferPtr >= (P25_SYNC_LENGTH_BITS - 2U) && m_bufferPtr <= (P25_SYNC_LENGTH_BITS + 2U)) {
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits64((m_bitBuffer & P25_SYNC_BITS_MASK) ^ P25_SYNC_BITS) <= SYNC_BIT_RUN_ERRS) {
|
||||
DEBUG2("P25RX: found sync in Data, pos", m_bufferPtr - P25_SYNC_LENGTH_BITS);
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
m_bufferPtr = P25_SYNC_LENGTH_BITS;
|
||||
if (m_dataPtr == m_lduEndPtr) {
|
||||
// Only update the centre and threshold if they are from a good sync
|
||||
if (m_lostCount == MAX_SYNC_FRAMES) {
|
||||
m_minSyncPtr = m_lduSyncPtr + P25_LDU_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_lduSyncPtr + 1U;
|
||||
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
// Send a data frame to the host if the required number of bits have been received
|
||||
if (m_bufferPtr == P25_LDU_FRAME_LENGTH_BITS) {
|
||||
calculateLevels(m_lduStartPtr, P25_LDU_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("P25RX: sync found in Ldu pos/centre/threshold", m_lduSyncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[P25_LDU_FRAME_LENGTH_BYTES + 3U];
|
||||
samplesToBits(m_lduStartPtr, P25_LDU_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
||||
m_lostCount--;
|
||||
if (m_lostCount == 0U) {
|
||||
@@ -247,16 +233,234 @@ void CP25RX::processData(q15_t sample)
|
||||
|
||||
serial.writeP25Lost();
|
||||
|
||||
m_state = P25RXS_NONE;
|
||||
} else {
|
||||
m_outBuffer[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
||||
|
||||
serial.writeP25Ldu(m_outBuffer, P25_LDU_FRAME_LENGTH_BYTES + 1U);
|
||||
|
||||
// Start the next frame
|
||||
::memset(m_outBuffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U);
|
||||
m_bufferPtr = 0U;
|
||||
m_state = P25RXS_NONE;
|
||||
m_lduEndPtr = NOENDPTR;
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_countdown = 0U;
|
||||
m_maxCorr = 0;
|
||||
} else {
|
||||
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
||||
writeRSSILdu(frame);
|
||||
m_maxCorr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CP25RX::correlateSync()
|
||||
{
|
||||
if (countBits32((m_bitBuffer[m_bitPtr] & P25_SYNC_SYMBOLS_MASK) ^ P25_SYNC_SYMBOLS) <= MAX_SYNC_SYMBOLS_ERRS) {
|
||||
uint16_t ptr = m_dataPtr + P25_LDU_FRAME_LENGTH_SAMPLES - P25_SYNC_LENGTH_SAMPLES + P25_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
ptr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
q31_t corr = 0;
|
||||
q15_t min = 16000;
|
||||
q15_t max = -16000;
|
||||
|
||||
for (uint8_t i = 0U; i < P25_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_buffer[ptr];
|
||||
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
|
||||
switch (P25_SYNC_SYMBOLS_VALUES[i]) {
|
||||
case +3:
|
||||
corr -= (val + val + val);
|
||||
break;
|
||||
case +1:
|
||||
corr -= val;
|
||||
break;
|
||||
case -1:
|
||||
corr += val;
|
||||
break;
|
||||
default: // -3
|
||||
corr += (val + val + val);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += P25_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
ptr -= P25_LDU_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 + P25_LDU_FRAME_LENGTH_SAMPLES - P25_SYNC_LENGTH_SAMPLES + P25_RADIO_SYMBOL_LENGTH;
|
||||
if (startPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
startPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
uint8_t sync[P25_SYNC_BYTES_LENGTH];
|
||||
samplesToBits(startPtr, P25_SYNC_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t maxErrs;
|
||||
if (m_state == P25RXS_NONE)
|
||||
maxErrs = MAX_SYNC_BIT_START_ERRS;
|
||||
else
|
||||
maxErrs = MAX_SYNC_BIT_RUN_ERRS;
|
||||
|
||||
uint8_t errs = 0U;
|
||||
for (uint8_t i = 0U; i < P25_SYNC_BYTES_LENGTH; i++)
|
||||
errs += countBits8(sync[i] ^ P25_SYNC_BYTES[i]);
|
||||
|
||||
if (errs <= maxErrs) {
|
||||
m_maxCorr = corr;
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
|
||||
m_lduSyncPtr = m_dataPtr;
|
||||
|
||||
// These are the positions of the start and end of an LDU
|
||||
m_lduStartPtr = startPtr;
|
||||
|
||||
m_lduEndPtr = m_dataPtr + P25_LDU_FRAME_LENGTH_SAMPLES - P25_SYNC_LENGTH_SAMPLES - 1U;
|
||||
if (m_lduEndPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_lduEndPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
if (m_state == P25RXS_NONE) {
|
||||
m_hdrSyncPtr = m_dataPtr;
|
||||
|
||||
// This is the position of the start of a HDR
|
||||
m_hdrStartPtr = startPtr;
|
||||
|
||||
// These are the range of positions for a sync for an LDU following a HDR
|
||||
m_minSyncPtr = m_dataPtr + P25_HDR_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_dataPtr + P25_HDR_FRAME_LENGTH_SAMPLES + 1U;
|
||||
if (m_maxSyncPtr >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CP25RX::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 += P25_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
start -= P25_LDU_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("P25RX: 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 CP25RX::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 += P25_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= P25_LDU_FRAME_LENGTH_SAMPLES)
|
||||
start -= P25_LDU_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
void CP25RX::writeRSSILdu(uint8_t* ldu)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
if (m_rssiCount > 0U) {
|
||||
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||
|
||||
ldu[217U] = (rssi >> 8) & 0xFFU;
|
||||
ldu[218U] = (rssi >> 0) & 0xFFU;
|
||||
|
||||
serial.writeP25Ldu(ldu, P25_LDU_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeP25Ldu(ldu, P25_LDU_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
#else
|
||||
serial.writeP25Ldu(ldu, P25_LDU_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -24,35 +24,49 @@
|
||||
|
||||
enum P25RX_STATE {
|
||||
P25RXS_NONE,
|
||||
P25RXS_DATA
|
||||
P25RXS_HDR,
|
||||
P25RXS_LDU
|
||||
};
|
||||
|
||||
class CP25RX {
|
||||
public:
|
||||
CP25RX();
|
||||
|
||||
void samples(const q15_t* samples, uint8_t length);
|
||||
void samples(const q15_t* samples, uint16_t* rssi, uint8_t length);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
uint32_t m_pll;
|
||||
bool m_prev;
|
||||
P25RX_STATE m_state;
|
||||
uint32_t m_symbolBuffer;
|
||||
uint64_t m_bitBuffer;
|
||||
q15_t m_symbols[P25_SYNC_LENGTH_SYMBOLS];
|
||||
uint8_t m_outBuffer[P25_LDU_FRAME_LENGTH_BYTES + 3U];
|
||||
uint8_t* m_buffer;
|
||||
uint16_t m_bufferPtr;
|
||||
uint16_t m_symbolPtr;
|
||||
uint32_t m_bitBuffer[P25_RADIO_SYMBOL_LENGTH];
|
||||
q15_t m_buffer[P25_LDU_FRAME_LENGTH_SAMPLES];
|
||||
uint16_t m_bitPtr;
|
||||
uint16_t m_dataPtr;
|
||||
uint16_t m_hdrStartPtr;
|
||||
uint16_t m_lduStartPtr;
|
||||
uint16_t m_lduEndPtr;
|
||||
uint16_t m_minSyncPtr;
|
||||
uint16_t m_maxSyncPtr;
|
||||
uint16_t m_hdrSyncPtr;
|
||||
uint16_t m_lduSyncPtr;
|
||||
q31_t m_maxCorr;
|
||||
uint16_t m_lostCount;
|
||||
q15_t m_centre;
|
||||
q15_t m_threshold;
|
||||
uint8_t m_countdown;
|
||||
q15_t m_centre[16U];
|
||||
q15_t m_centreVal;
|
||||
q15_t m_threshold[16U];
|
||||
q15_t m_thresholdVal;
|
||||
uint8_t m_averagePtr;
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
|
||||
void processNone(q15_t sample);
|
||||
void processData(q15_t sample);
|
||||
void processHdr(q15_t sample);
|
||||
void processLdu(q15_t sample);
|
||||
bool correlateSync();
|
||||
void calculateLevels(uint16_t start, uint16_t count);
|
||||
void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
void writeRSSILdu(uint8_t* ldu);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 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
|
||||
@@ -16,40 +17,30 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "P25TX.h"
|
||||
|
||||
#include "P25Defines.h"
|
||||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t P25_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t P25_C4FSK_FILTER_LEN = 22U;
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t P25_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t P25_C4FSK_FILTER_LEN = 42U;
|
||||
#endif
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'normal') in MATLAB
|
||||
// numTaps = 40, L = 5
|
||||
static q15_t RC_0_2_FILTER[] = {-897, -1636, -1840, -1278, 0, 1613, 2936, 3310, 2315, 0, -3011, -5627, -6580, -4839,
|
||||
0, 7482, 16311, 24651, 30607, 32767, 30607, 24651, 16311, 7482, 0, -4839, -6580, -5627,
|
||||
-3011, 0, 2315, 3310, 2936, 1613, 0, -1278, -1840, -1636, -897, 0}; // numTaps = 40, L = 5
|
||||
const uint16_t RC_0_2_FILTER_PHASE_LEN = 8U; // phaseLength = numTaps/L
|
||||
|
||||
// Generated in MATLAB using the following commands, and then normalised for unity gain
|
||||
// shape2 = 'Inverse-sinc Lowpass';
|
||||
// d2 = fdesign.interpolator(2, shape2);
|
||||
// d2 = fdesign.interpolator(1, shape2);
|
||||
// h2 = design(d2, 'SystemObject', true);
|
||||
static q15_t P25_LP_FILTER[] = {170, 401, 340, -203, -715, -478, 281, 419, -440, -1002, -103, 1114, 528, -1389, -1520, 1108, 2674, -388, -4662,
|
||||
-2132, 9168, 20241, 20241, 9168, -2132, -4662, -388, 2674, 1108, -1520, -1389, 528, 1114, -103, -1002, -440, 419,
|
||||
281, -478, -715, -203, 340, 401, 170};
|
||||
const uint16_t P25_LP_FILTER_LEN = 44U;
|
||||
static q15_t LOWPASS_FILTER[] = {1376, -2393, 4584, -8933, 22325, 22325, -8933, 4584, -2393, 1376};
|
||||
const uint16_t LOWPASS_FILTER_LEN = 10U;
|
||||
|
||||
const q15_t P25_LEVELA[] = { 495, 495, 495, 495, 495};
|
||||
const q15_t P25_LEVELB[] = { 165, 165, 165, 165, 165};
|
||||
const q15_t P25_LEVELC[] = {-165, -165, -165, -165, -165};
|
||||
const q15_t P25_LEVELD[] = {-495, -495, -495, -495, -495};
|
||||
const q15_t P25_LEVELA = 1260;
|
||||
const q15_t P25_LEVELB = 420;
|
||||
const q15_t P25_LEVELC = -420;
|
||||
const q15_t P25_LEVELD = -1260;
|
||||
|
||||
const uint8_t P25_START_SYNC = 0x77U;
|
||||
|
||||
@@ -62,19 +53,19 @@ m_lpState(),
|
||||
m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_count(0U)
|
||||
m_txDelay(240U) // 200ms
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
::memset(m_lpState, 0x00U, 70U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = P25_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.pCoeffs = P25_C4FSK_FILTER;
|
||||
m_modFilter.L = P25_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = RC_0_2_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = RC_0_2_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
|
||||
m_lpFilter.numTaps = P25_LP_FILTER_LEN;
|
||||
m_lpFilter.numTaps = LOWPASS_FILTER_LEN;
|
||||
m_lpFilter.pState = m_lpState;
|
||||
m_lpFilter.pCoeffs = P25_LP_FILTER;
|
||||
m_lpFilter.pCoeffs = LOWPASS_FILTER;
|
||||
}
|
||||
|
||||
void CP25TX::process()
|
||||
@@ -84,8 +75,6 @@ void CP25TX::process()
|
||||
|
||||
if (m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
m_count = 0U;
|
||||
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = P25_START_SYNC;
|
||||
} else {
|
||||
@@ -135,61 +124,45 @@ uint8_t CP25TX::writeData(const uint8_t* data, uint8_t length)
|
||||
|
||||
void CP25TX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t intBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
|
||||
q15_t outBuffer[P25_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += P25_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, P25_LEVELA, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELA;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, P25_LEVELB, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELB;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, P25_LEVELC, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELC;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, P25_LEVELD, P25_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = P25_LEVELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = P25_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += P25_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U);
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[P25_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, intBuffer, blockSize);
|
||||
|
||||
::arm_fir_fast_q15(&m_lpFilter, intBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_P25, outBuffer, blockSize);
|
||||
io.write(STATE_P25, outBuffer, P25_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CP25TX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
uint16_t CP25TX::getSpace() const
|
||||
uint8_t CP25TX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / P25_LDU_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 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
|
||||
@@ -33,19 +33,18 @@ public:
|
||||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
arm_fir_instance_q15 m_lpFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[920U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
uint32_t m_count;
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
arm_fir_instance_q15 m_lpFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion and P25.
|
||||
This is the source code of the MMDVM firmware that supports D-Star, DMR, System Fusion and P25 modes.
|
||||
|
||||
It runs on the Arduino Due and the ST-Micro STM32F4xx platforms. Support for the Teensy (3.1/3.2 and 3.5/3.6) is being added.
|
||||
It runs on the Arduino Due, the ST-Micro STM32F407-DISCO and STM32F446-NUCLEO, as well as the Teensy 3.1/3.2/3.5/3.6. What these platforms have in common is the use of an ARM Cortex-M3 or M4 processor with a clock speed greater than 70 MHz, and access to at least one analogue to digital converter and one digital to analogue converter. A Cortex-M7 processor is also probably adequate.
|
||||
|
||||
In order to build this software for the Arduino Due, you will need to edit a file within the Arduino GUI and that is detailed in the BUILD.txt file. The STM32 support is being supplied via the Coocox IDE with ARM GCC. The Teensy support is via the latest beta of Teensyduino.
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ Boston, MA 02110-1301, USA.
|
||||
#if defined(STM32F4XX) || defined(STM32F4)
|
||||
#include "stm32f4xx.h"
|
||||
#include <cstddef>
|
||||
#elif defined(STM32F105xC)
|
||||
#include "stm32f1xx.h"
|
||||
#include <cstddef>
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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) & ringFifo.rdIdx) == (RINGBUFF_MASK(ringBuff) & ringFifo.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
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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
|
||||
* 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(STM32UTILS_H)
|
||||
#define STM32UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ram function */
|
||||
#define RAMFUNC __attribute__ ((long_call, section (".data")))
|
||||
/* eeprom data */
|
||||
/* for placing variables in eeprom memory */
|
||||
#define EEMEM __attribute__((section(".eeprom")))
|
||||
|
||||
/* bitband type */
|
||||
typedef volatile uint32_t * const bitband_t;
|
||||
|
||||
/* base address for bit banding */
|
||||
#define BITBAND_SRAM_REF (0x20000000)
|
||||
/* base address for bit banding */
|
||||
#define BITBAND_SRAM_BASE (0x22000000)
|
||||
/* base address for bit banding */
|
||||
#define BITBAND_PERIPH_REF (0x40000000)
|
||||
/* base address for bit banding */
|
||||
#define BITBAND_PERIPH_BASE (0x42000000)
|
||||
|
||||
/* sram bit band */
|
||||
#define BITBAND_SRAM(address, bit) ((void*)(BITBAND_SRAM_BASE + \
|
||||
(((uint32_t)address) - BITBAND_SRAM_REF) * 32 + (bit) * 4))
|
||||
|
||||
/* periph bit band */
|
||||
#define BITBAND_PERIPH(address, bit) ((void *)(BITBAND_PERIPH_BASE + \
|
||||
(((uint32_t)address) - BITBAND_PERIPH_REF) * 32 + (bit) * 4))
|
||||
|
||||
#endif
|
||||
@@ -24,6 +24,9 @@ Boston, MA 02110-1301, USA.
|
||||
#if defined(STM32F4XX) || defined(STM32F4)
|
||||
#include "stm32f4xx.h"
|
||||
#include <cstddef>
|
||||
#elif defined(STM32F105xC)
|
||||
#include "stm32f1xx.h"
|
||||
#include <cstddef>
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
+15
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 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
|
||||
@@ -54,6 +54,20 @@ int CSerialPort::availableInt(uint8_t n)
|
||||
}
|
||||
}
|
||||
|
||||
int CSerialPort::availableForWriteInt(uint8_t n)
|
||||
{
|
||||
switch (n) {
|
||||
case 1U:
|
||||
return Serial.availableForWrite();
|
||||
case 2U:
|
||||
return Serial2.availableForWrite();
|
||||
case 3U:
|
||||
return Serial3.availableForWrite();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CSerialPort::readInt(uint8_t n)
|
||||
{
|
||||
switch (n) {
|
||||
|
||||
+93
-54
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013,2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2013,2015,2016,2017 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016 by Colin Durbridge G4EML
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,11 +17,14 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
|
||||
#if defined(MADEBYMAKEFILE)
|
||||
#include "GitVersion.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include "SerialPort.h"
|
||||
|
||||
const uint8_t MMDVM_FRAME_START = 0xE0U;
|
||||
@@ -33,6 +36,7 @@ const uint8_t MMDVM_SET_MODE = 0x03U;
|
||||
const uint8_t MMDVM_SET_FREQ = 0x04U;
|
||||
|
||||
const uint8_t MMDVM_CAL_DATA = 0x08U;
|
||||
const uint8_t MMDVM_RSSI_DATA = 0x09U;
|
||||
|
||||
const uint8_t MMDVM_SEND_CWID = 0x0AU;
|
||||
|
||||
@@ -67,10 +71,19 @@ const uint8_t MMDVM_DEBUG3 = 0xF3U;
|
||||
const uint8_t MMDVM_DEBUG4 = 0xF4U;
|
||||
const uint8_t MMDVM_DEBUG5 = 0xF5U;
|
||||
|
||||
|
||||
#if defined(EXTERNAL_OSC)
|
||||
const uint8_t HARDWARE[] = "MMDVM 20161124 TCXO (D-Star/DMR/System Fusion/P25/CW Id)";
|
||||
#define DESCRIPTION "MMDVM 20170406 TCXO (D-Star/DMR/System Fusion/P25/RSSI/CW Id)"
|
||||
#else
|
||||
const uint8_t HARDWARE[] = "MMDVM 20161124 (D-Star/DMR/System Fusion/P25/CW Id)";
|
||||
#define DESCRIPTION "MMDVM 20170406 (D-Star/DMR/System Fusion/P25/RSSI/CW Id)"
|
||||
#endif
|
||||
|
||||
#if defined(GITVERSION)
|
||||
#define concat(a, b) a " GitID #" b ""
|
||||
const char HARDWARE[] = concat(DESCRIPTION, GITVERSION);
|
||||
#else
|
||||
#define concat(a, b, c) a " (Build: " b " " c ")"
|
||||
const char HARDWARE[] = concat(DESCRIPTION, __TIME__, __DATE__);
|
||||
#endif
|
||||
|
||||
const uint8_t PROTOCOL_VERSION = 1U;
|
||||
@@ -79,7 +92,9 @@ const uint8_t PROTOCOL_VERSION = 1U;
|
||||
CSerialPort::CSerialPort() :
|
||||
m_buffer(),
|
||||
m_ptr(0U),
|
||||
m_len(0U)
|
||||
m_len(0U),
|
||||
m_debug(false),
|
||||
m_repeat()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -212,8 +227,11 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
|
||||
bool txInvert = (data[0U] & 0x02U) == 0x02U;
|
||||
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
|
||||
bool ysfLoDev = (data[0U] & 0x08U) == 0x08U;
|
||||
bool simplex = (data[0U] & 0x80U) == 0x80U;
|
||||
|
||||
m_debug = (data[0U] & 0x10U) == 0x10U;
|
||||
|
||||
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
|
||||
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
|
||||
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
|
||||
@@ -225,7 +243,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
|
||||
MMDVM_STATE modemState = MMDVM_STATE(data[3U]);
|
||||
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL)
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL)
|
||||
return 4U;
|
||||
if (modemState == STATE_DSTAR && !dstarEnable)
|
||||
return 4U;
|
||||
@@ -244,18 +262,6 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
|
||||
uint8_t dmrDelay = data[7U];
|
||||
|
||||
int8_t oscOffset = int8_t(data[8U]) - 128;
|
||||
if (oscOffset < 0) {
|
||||
m_sampleCount = 1000000U / uint32_t(-oscOffset);
|
||||
m_sampleInsert = false;
|
||||
} else if (oscOffset > 0) {
|
||||
m_sampleCount = 1000000U / uint32_t(oscOffset);
|
||||
m_sampleInsert = true;
|
||||
} else {
|
||||
m_sampleCount = 0U;
|
||||
m_sampleInsert = false;
|
||||
}
|
||||
|
||||
uint8_t cwIdTXLevel = data[5U];
|
||||
uint8_t dstarTXLevel = data[9U];
|
||||
uint8_t dmrTXLevel = data[10U];
|
||||
@@ -281,6 +287,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
|
||||
dmrDMORX.setColorCode(colorCode);
|
||||
dmrIdleRX.setColorCode(colorCode);
|
||||
|
||||
ysfTX.setLoDev(ysfLoDev);
|
||||
|
||||
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel);
|
||||
|
||||
io.start();
|
||||
@@ -298,7 +306,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
|
||||
if (modemState == m_modemState)
|
||||
return 0U;
|
||||
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL)
|
||||
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL)
|
||||
return 4U;
|
||||
if (modemState == STATE_DSTAR && !m_dstarEnable)
|
||||
return 4U;
|
||||
@@ -371,6 +379,16 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
|
||||
p25RX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
case STATE_RSSICAL:
|
||||
DEBUG1("Mode set to RSSI Calibrate");
|
||||
dmrIdleRX.reset();
|
||||
dmrDMORX.reset();
|
||||
dmrRX.reset();
|
||||
dstarRX.reset();
|
||||
ysfRX.reset();
|
||||
p25RX.reset();
|
||||
cwIdTX.reset();
|
||||
break;
|
||||
default:
|
||||
DEBUG1("Mode set to Idle");
|
||||
// STATE_IDLE
|
||||
@@ -626,8 +644,10 @@ void CSerialPort::process()
|
||||
break;
|
||||
|
||||
#if defined(SERIAL_REPEATER)
|
||||
case MMDVM_SERIAL:
|
||||
writeInt(3U, m_buffer + 3U, m_len - 3U);
|
||||
case MMDVM_SERIAL: {
|
||||
for (uint8_t i = 3U; i < m_len; i++)
|
||||
m_repeat.put(m_buffer[i]);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -644,9 +664,22 @@ void CSerialPort::process()
|
||||
}
|
||||
|
||||
#if defined(SERIAL_REPEATER)
|
||||
// Drain any incoming serial data
|
||||
while (availableInt(3U))
|
||||
readInt(3U);
|
||||
// Write any outgoing serial data
|
||||
uint16_t space = m_repeat.getData();
|
||||
if (space > 0U) {
|
||||
int avail = availableForWriteInt(3U);
|
||||
if (avail < space)
|
||||
space = avail;
|
||||
|
||||
for (uint16_t i = 0U; i < space; i++) {
|
||||
uint8_t c = m_repeat.get();
|
||||
writeInt(3U, &c, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
// Read any incoming serial data
|
||||
while (availableInt(3U))
|
||||
readInt(3U);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -892,8 +925,31 @@ void CSerialPort::writeCalData(const uint8_t* data, uint8_t length)
|
||||
writeInt(1U, reply, count);
|
||||
}
|
||||
|
||||
void CSerialPort::writeRSSIData(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (m_modemState != STATE_RSSICAL)
|
||||
return;
|
||||
|
||||
uint8_t reply[30U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
reply[1U] = 0U;
|
||||
reply[2U] = MMDVM_RSSI_DATA;
|
||||
|
||||
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::writeDebug(const char* text)
|
||||
{
|
||||
if (!m_debug)
|
||||
return;
|
||||
|
||||
uint8_t reply[130U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
@@ -911,6 +967,9 @@ void CSerialPort::writeDebug(const char* text)
|
||||
|
||||
void CSerialPort::writeDebug(const char* text, int16_t n1)
|
||||
{
|
||||
if (!m_debug)
|
||||
return;
|
||||
|
||||
uint8_t reply[130U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
@@ -931,6 +990,9 @@ void CSerialPort::writeDebug(const char* text, int16_t n1)
|
||||
|
||||
void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2)
|
||||
{
|
||||
if (!m_debug)
|
||||
return;
|
||||
|
||||
uint8_t reply[130U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
@@ -954,6 +1016,9 @@ void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2)
|
||||
|
||||
void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3)
|
||||
{
|
||||
if (!m_debug)
|
||||
return;
|
||||
|
||||
uint8_t reply[130U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
@@ -980,6 +1045,9 @@ void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n
|
||||
|
||||
void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4)
|
||||
{
|
||||
if (!m_debug)
|
||||
return;
|
||||
|
||||
uint8_t reply[130U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
@@ -1006,32 +1074,3 @@ void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n
|
||||
|
||||
writeInt(1U, reply, count, true);
|
||||
}
|
||||
|
||||
void CSerialPort::writeAssert(bool cond, const char* text, const char* file, long line)
|
||||
{
|
||||
if (cond)
|
||||
return;
|
||||
|
||||
uint8_t reply[200U];
|
||||
|
||||
reply[0U] = MMDVM_FRAME_START;
|
||||
reply[1U] = 0U;
|
||||
reply[2U] = MMDVM_DEBUG2;
|
||||
|
||||
uint8_t count = 3U;
|
||||
for (uint8_t i = 0U; text[i] != '\0'; i++, count++)
|
||||
reply[count] = text[i];
|
||||
|
||||
reply[count++] = ' ';
|
||||
|
||||
for (uint8_t i = 0U; file[i] != '\0'; i++, count++)
|
||||
reply[count] = file[i];
|
||||
|
||||
reply[count++] = (line >> 8) & 0xFF;
|
||||
reply[count++] = (line >> 0) & 0xFF;
|
||||
|
||||
reply[1U] = count;
|
||||
|
||||
writeInt(1U, reply, count, true);
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "SerialRB.h"
|
||||
|
||||
|
||||
class CSerialPort {
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
void writeP25Lost();
|
||||
|
||||
void writeCalData(const uint8_t* data, uint8_t length);
|
||||
void writeRSSIData(const uint8_t* data, uint8_t length);
|
||||
|
||||
void writeDebug(const char* text);
|
||||
void writeDebug(const char* text, int16_t n1);
|
||||
@@ -54,12 +56,12 @@ public:
|
||||
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3);
|
||||
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4);
|
||||
|
||||
void writeAssert(bool cond, const char* text, const char* file, long line);
|
||||
|
||||
private:
|
||||
uint8_t m_buffer[256U];
|
||||
uint8_t m_ptr;
|
||||
uint8_t m_len;
|
||||
uint8_t m_buffer[256U];
|
||||
uint8_t m_ptr;
|
||||
uint8_t m_len;
|
||||
bool m_debug;
|
||||
CSerialRB m_repeat;
|
||||
|
||||
void sendACK();
|
||||
void sendNAK(uint8_t err);
|
||||
@@ -72,9 +74,9 @@ private:
|
||||
// Hardware versions
|
||||
void beginInt(uint8_t n, int speed);
|
||||
int availableInt(uint8_t n);
|
||||
int availableForWriteInt(uint8_t n);
|
||||
uint8_t readInt(uint8_t n);
|
||||
void writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush = false);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ Boston, MA 02110-1301, USA.
|
||||
#if defined(STM32F4XX) || defined(STM32F4)
|
||||
#include "stm32f4xx.h"
|
||||
#include <cstddef>
|
||||
#elif defined(STM32F105xC)
|
||||
#include "stm32f1xx.h"
|
||||
#include <cstddef>
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
+568
-505
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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:
|
||||
USART2 - TXD PA2 - RXD PA3
|
||||
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
/* ************* USART2 ***************** */
|
||||
static volatile USARTBuffer_t txBuffer2={.size=USART_BUFFER_SIZE};
|
||||
static volatile USARTBuffer_t rxBuffer2={.size=USART_BUFFER_SIZE};
|
||||
|
||||
|
||||
extern "C" {
|
||||
bitband_t txe = (bitband_t)BITBAND_PERIPH(&USART2->SR, USART_SR_TXE_Pos);
|
||||
bitband_t rxne = (bitband_t)BITBAND_PERIPH(&USART2->SR, USART_SR_RXNE_Pos);
|
||||
bitband_t txeie = (bitband_t)BITBAND_PERIPH(&USART2->CR1, USART_CR1_TXEIE_Pos);
|
||||
|
||||
RAMFUNC void USART2_IRQHandler()
|
||||
{
|
||||
/* Transmitting data */
|
||||
if(*txe){
|
||||
if(!(RINGBUFF_EMPTY(txBuffer2))){
|
||||
USART2->DR = RINGBUFF_READ(txBuffer2);
|
||||
}else{ /* Buffer empty */
|
||||
*txeie = 0; /* Don't send further data */
|
||||
}
|
||||
}
|
||||
|
||||
/* Receiving data */
|
||||
if(*rxne){
|
||||
RINGBUFF_WRITE(rxBuffer2, USART2->DR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void USART2Init(int speed)
|
||||
{
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
|
||||
|
||||
USART2->BRR = USART_BRR(36000000UL, speed);
|
||||
|
||||
USART2->CR1 = USART_CR1_UE | USART_CR1_TE |
|
||||
USART_CR1_RE | USART_CR1_RXNEIE; // Enable USART and RX interrupt
|
||||
NVIC_EnableIRQ(USART2_IRQn);
|
||||
}
|
||||
|
||||
RAMFUNC void USART2TxData(const uint8_t* data, uint16_t length)
|
||||
{
|
||||
NVIC_DisableIRQ(USART2_IRQn);
|
||||
|
||||
/* Check remaining space in buffer */
|
||||
if(RINGBUFF_COUNT(txBuffer2) + length > RINGBUFF_SIZE(txBuffer2)){
|
||||
NVIC_EnableIRQ(USART2_IRQn);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Append data to buffer */
|
||||
while(length--){
|
||||
RINGBUFF_WRITE(txBuffer2, *(data++));
|
||||
}
|
||||
|
||||
/* Start sending data */
|
||||
USART2->CR1 |= USART_CR1_TXEIE;
|
||||
|
||||
NVIC_EnableIRQ(USART2_IRQn);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
void CSerialPort::beginInt(uint8_t n, int speed)
|
||||
{
|
||||
switch (n) {
|
||||
case 1U:
|
||||
USART2Init(speed);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int CSerialPort::availableInt(uint8_t n)
|
||||
{
|
||||
switch (n) {
|
||||
case 1U:
|
||||
return !RINGBUFF_EMPTY(rxBuffer2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t CSerialPort::readInt(uint8_t n)
|
||||
{
|
||||
switch (n) {
|
||||
case 1U:
|
||||
return RINGBUFF_READ(rxBuffer2);
|
||||
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(&USART2->SR, USART_SR_TC_Pos);
|
||||
|
||||
switch (n) {
|
||||
case 1U:
|
||||
USART2TxData(data, length);
|
||||
*tc = 0;
|
||||
|
||||
if (flush) {
|
||||
while (!RINGBUFF_EMPTY(txBuffer2) || !tc)
|
||||
;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,9 @@
|
||||
#if defined(STM32F4XX) || defined(STM32F4)
|
||||
#include "stm32f4xx.h"
|
||||
#include <cstddef>
|
||||
#elif defined(STM32F105xC)
|
||||
#include "stm32f1xx.h"
|
||||
#include <cstddef>
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2016,2017 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
|
||||
@@ -24,13 +24,16 @@ const unsigned int YSF_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||
const unsigned int YSF_FRAME_LENGTH_BYTES = 120U;
|
||||
const unsigned int YSF_FRAME_LENGTH_BITS = YSF_FRAME_LENGTH_BYTES * 8U;
|
||||
const unsigned int YSF_FRAME_LENGTH_SYMBOLS = YSF_FRAME_LENGTH_BYTES * 4U;
|
||||
const unsigned int YSF_FRAME_LENGTH_SAMPLES = YSF_FRAME_LENGTH_SYMBOLS * YSF_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int YSF_SYNC_LENGTH_BYTES = 5U;
|
||||
const unsigned int YSF_SYNC_LENGTH_BITS = YSF_SYNC_LENGTH_BYTES * 8U;
|
||||
const unsigned int YSF_SYNC_LENGTH_SYMBOLS = YSF_SYNC_LENGTH_BYTES * 4U;
|
||||
const unsigned int YSF_SYNC_LENGTH_SAMPLES = YSF_SYNC_LENGTH_SYMBOLS * YSF_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const unsigned int YSF_FICH_LENGTH_BITS = 200U;
|
||||
const unsigned int YSF_FICH_LENGTH_SYMBOLS = 100U;
|
||||
const unsigned int YSF_FICH_LENGTH_SAMPLES = YSF_FICH_LENGTH_SYMBOLS * YSF_RADIO_SYMBOL_LENGTH;
|
||||
|
||||
const uint8_t YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU};
|
||||
const uint8_t YSF_SYNC_BYTES_LENGTH = 5U;
|
||||
@@ -41,6 +44,9 @@ const uint64_t YSF_SYNC_BITS_MASK = 0x000000FFFFFFFFFFU;
|
||||
// D 4 7 1 C 9 6 3 4 D
|
||||
// 11 01 01 00 01 11 00 01 11 00 10 01 01 10 00 11 01 00 11 01
|
||||
// -3 +3 +3 +1 +3 -3 +1 +3 -3 +1 -1 +3 +3 -1 +3 -3 +3 +1 -3 +3
|
||||
|
||||
const int8_t YSF_SYNC_SYMBOLS_VALUES[] = {-3, +3, +3, +1, +3, -3, +1, +3, -3, +1, -1, +3, +3, -1, +3, -3, +3, +1, -3, +3};
|
||||
|
||||
const uint32_t YSF_SYNC_SYMBOLS = 0x0007B5ADU;
|
||||
const uint32_t YSF_SYNC_SYMBOLS_MASK = 0x000FFFFFU;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,209 +16,169 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "YSFRX.h"
|
||||
#include "Utils.h"
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
const q15_t SCALING_FACTOR = 18750; // Q15(0.55)
|
||||
|
||||
const uint32_t PLLMAX = 0x10000U;
|
||||
const uint32_t PLLINC = PLLMAX / YSF_RADIO_SYMBOL_LENGTH;
|
||||
const uint32_t INC = PLLINC / 32U;
|
||||
const uint8_t MAX_SYNC_BIT_START_ERRS = 2U;
|
||||
const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U;
|
||||
|
||||
const uint8_t SYNC_SYMBOL_ERRS = 0U;
|
||||
|
||||
const uint8_t SYNC_BIT_START_ERRS = 2U;
|
||||
const uint8_t SYNC_BIT_RUN_ERRS = 4U;
|
||||
|
||||
const unsigned int MAX_SYNC_FRAMES = 4U + 1U;
|
||||
const uint8_t MAX_SYNC_SYMBOLS_ERRS = 3U;
|
||||
|
||||
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint8_t NOAVEPTR = 99U;
|
||||
|
||||
const uint16_t NOENDPTR = 9999U;
|
||||
|
||||
const unsigned int MAX_SYNC_FRAMES = 4U + 1U;
|
||||
|
||||
CYSFRX::CYSFRX() :
|
||||
m_pll(0U),
|
||||
m_prev(false),
|
||||
m_state(YSFRXS_NONE),
|
||||
m_bitBuffer(0x00U),
|
||||
m_symbols(),
|
||||
m_outBuffer(),
|
||||
m_buffer(NULL),
|
||||
m_bufferPtr(0U),
|
||||
m_symbolPtr(0U),
|
||||
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_centre(0),
|
||||
m_threshold(0)
|
||||
m_countdown(0U),
|
||||
m_centre(),
|
||||
m_centreVal(0),
|
||||
m_threshold(),
|
||||
m_thresholdVal(0),
|
||||
m_averagePtr(NOAVEPTR),
|
||||
m_rssiAccum(0U),
|
||||
m_rssiCount(0U)
|
||||
{
|
||||
m_buffer = m_outBuffer + 1U;
|
||||
}
|
||||
|
||||
void CYSFRX::reset()
|
||||
{
|
||||
m_pll = 0U;
|
||||
m_prev = false;
|
||||
m_state = YSFRXS_NONE;
|
||||
m_bitBuffer = 0x00U;
|
||||
m_bufferPtr = 0U;
|
||||
m_symbolPtr = 0U;
|
||||
m_lostCount = 0U;
|
||||
m_centre = 0;
|
||||
m_threshold = 0;
|
||||
m_state = YSFRXS_NONE;
|
||||
m_dataPtr = 0U;
|
||||
m_bitPtr = 0U;
|
||||
m_maxCorr = 0;
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_startPtr = NOENDPTR;
|
||||
m_endPtr = NOENDPTR;
|
||||
m_syncPtr = NOENDPTR;
|
||||
m_minSyncPtr = NOENDPTR;
|
||||
m_maxSyncPtr = NOENDPTR;
|
||||
m_centreVal = 0;
|
||||
m_thresholdVal = 0;
|
||||
m_lostCount = 0U;
|
||||
m_countdown = 0U;
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
void CYSFRX::samples(const q15_t* samples, uint8_t length)
|
||||
void CYSFRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length)
|
||||
{
|
||||
for (uint16_t i = 0U; i < length; i++) {
|
||||
bool bit = samples[i] < 0;
|
||||
for (uint8_t i = 0U; i < length; i++) {
|
||||
q15_t sample = samples[i];
|
||||
|
||||
if (bit != m_prev) {
|
||||
if (m_pll < (PLLMAX / 2U))
|
||||
m_pll += INC;
|
||||
else
|
||||
m_pll -= INC;
|
||||
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 YSFRXS_DATA:
|
||||
processData(sample);
|
||||
break;
|
||||
default:
|
||||
processNone(sample);
|
||||
break;
|
||||
}
|
||||
|
||||
m_prev = bit;
|
||||
m_dataPtr++;
|
||||
if (m_dataPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
m_dataPtr = 0U;
|
||||
|
||||
m_pll += PLLINC;
|
||||
|
||||
if (m_pll >= PLLMAX) {
|
||||
m_pll -= PLLMAX;
|
||||
|
||||
if (m_state == YSFRXS_NONE)
|
||||
processNone(samples[i]);
|
||||
else
|
||||
processData(samples[i]);
|
||||
}
|
||||
m_bitPtr++;
|
||||
if (m_bitPtr >= YSF_RADIO_SYMBOL_LENGTH)
|
||||
m_bitPtr = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CYSFRX::processNone(q15_t sample)
|
||||
{
|
||||
m_symbolBuffer <<= 1;
|
||||
if (sample < 0)
|
||||
m_symbolBuffer |= 0x01U;
|
||||
|
||||
m_symbols[m_symbolPtr] = sample;
|
||||
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits32((m_symbolBuffer & YSF_SYNC_SYMBOLS_MASK) ^ YSF_SYNC_SYMBOLS) <= SYNC_SYMBOL_ERRS) {
|
||||
q15_t max = -16000;
|
||||
q15_t min = 16000;
|
||||
|
||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_symbols[i];
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
}
|
||||
|
||||
q15_t centre = (max + min) >> 1;
|
||||
|
||||
q31_t v1 = (max - centre) * SCALING_FACTOR;
|
||||
q15_t threshold = q15_t(v1 >> 15);
|
||||
|
||||
uint16_t ptr = m_symbolPtr + 1U;
|
||||
if (ptr >= YSF_SYNC_LENGTH_SYMBOLS)
|
||||
ptr = 0U;
|
||||
|
||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t sample = m_symbols[ptr] - centre;
|
||||
|
||||
if (sample < -threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x01U;
|
||||
} else if (sample < 0) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x00U;
|
||||
} else if (sample < threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x02U;
|
||||
} else {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x03U;
|
||||
}
|
||||
|
||||
ptr++;
|
||||
if (ptr >= YSF_SYNC_LENGTH_SYMBOLS)
|
||||
ptr = 0U;
|
||||
}
|
||||
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits64((m_bitBuffer & YSF_SYNC_BITS_MASK) ^ YSF_SYNC_BITS) <= SYNC_BIT_START_ERRS) {
|
||||
DEBUG5("YSFRX: sync found in None min/max/centre/threshold", min, max, centre, threshold);
|
||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_BYTES; i++)
|
||||
m_buffer[i] = YSF_SYNC_BYTES[i];
|
||||
m_centre = centre;
|
||||
m_threshold = threshold;
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
m_bufferPtr = YSF_SYNC_LENGTH_BITS;
|
||||
m_state = YSFRXS_DATA;
|
||||
bool ret = correlateSync();
|
||||
if (ret) {
|
||||
// On the first sync, start the countdown to the state change
|
||||
if (m_countdown == 0U) {
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
|
||||
io.setDecode(true);
|
||||
io.setADCDetection(true);
|
||||
|
||||
m_averagePtr = NOAVEPTR;
|
||||
|
||||
m_countdown = 5U;
|
||||
}
|
||||
}
|
||||
|
||||
m_symbolPtr++;
|
||||
if (m_symbolPtr >= YSF_SYNC_LENGTH_SYMBOLS)
|
||||
m_symbolPtr = 0U;
|
||||
if (m_countdown > 0U)
|
||||
m_countdown--;
|
||||
|
||||
if (m_countdown == 1U) {
|
||||
m_minSyncPtr = m_syncPtr + YSF_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_syncPtr + 1U;
|
||||
if (m_maxSyncPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_state = YSFRXS_DATA;
|
||||
m_countdown = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void CYSFRX::processData(q15_t sample)
|
||||
{
|
||||
sample -= m_centre;
|
||||
|
||||
if (sample < -m_threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x01U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
} else if (sample < 0) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x00U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
} else if (sample < m_threshold) {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x02U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, false);
|
||||
m_bufferPtr++;
|
||||
if (m_minSyncPtr < m_maxSyncPtr) {
|
||||
if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr)
|
||||
correlateSync();
|
||||
} else {
|
||||
m_bitBuffer <<= 2;
|
||||
m_bitBuffer |= 0x03U;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
WRITE_BIT1(m_buffer, m_bufferPtr, true);
|
||||
m_bufferPtr++;
|
||||
if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr)
|
||||
correlateSync();
|
||||
}
|
||||
|
||||
// Only search for a sync in the right place +-2 symbols
|
||||
if (m_bufferPtr >= (YSF_SYNC_LENGTH_BITS - 2U) && m_bufferPtr <= (YSF_SYNC_LENGTH_BITS + 2U)) {
|
||||
// Fuzzy matching of the data sync bit sequence
|
||||
if (countBits64((m_bitBuffer & YSF_SYNC_BITS_MASK) ^ YSF_SYNC_BITS) <= SYNC_BIT_RUN_ERRS) {
|
||||
DEBUG2("YSFRX: found sync in Data, pos", m_bufferPtr - YSF_SYNC_LENGTH_BITS);
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
m_bufferPtr = YSF_SYNC_LENGTH_BITS;
|
||||
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 + YSF_FRAME_LENGTH_SAMPLES - 1U;
|
||||
if (m_minSyncPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
m_minSyncPtr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
m_maxSyncPtr = m_syncPtr + 1U;
|
||||
if (m_maxSyncPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
m_maxSyncPtr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
// Send a data frame to the host if the required number of bits have been received
|
||||
if (m_bufferPtr == YSF_FRAME_LENGTH_BITS) {
|
||||
calculateLevels(m_startPtr, YSF_FRAME_LENGTH_SYMBOLS);
|
||||
|
||||
DEBUG4("YSFRX: sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t frame[YSF_FRAME_LENGTH_BYTES + 3U];
|
||||
samplesToBits(m_startPtr, YSF_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal);
|
||||
|
||||
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
|
||||
m_lostCount--;
|
||||
if (m_lostCount == 0U) {
|
||||
@@ -229,16 +189,216 @@ void CYSFRX::processData(q15_t sample)
|
||||
|
||||
serial.writeYSFLost();
|
||||
|
||||
m_state = YSFRXS_NONE;
|
||||
m_state = YSFRXS_NONE;
|
||||
m_endPtr = NOENDPTR;
|
||||
m_averagePtr = NOAVEPTR;
|
||||
m_countdown = 0U;
|
||||
m_maxCorr = 0;
|
||||
} else {
|
||||
m_outBuffer[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
||||
|
||||
serial.writeYSFData(m_outBuffer, YSF_FRAME_LENGTH_BYTES + 1U);
|
||||
|
||||
// Start the next frame
|
||||
::memset(m_outBuffer, 0x00U, YSF_FRAME_LENGTH_BYTES + 3U);
|
||||
m_bufferPtr = 0U;
|
||||
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U;
|
||||
writeRSSIData(frame);
|
||||
m_maxCorr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CYSFRX::correlateSync()
|
||||
{
|
||||
if (countBits32((m_bitBuffer[m_bitPtr] & YSF_SYNC_SYMBOLS_MASK) ^ YSF_SYNC_SYMBOLS) <= MAX_SYNC_SYMBOLS_ERRS) {
|
||||
uint16_t ptr = m_dataPtr + YSF_FRAME_LENGTH_SAMPLES - YSF_SYNC_LENGTH_SAMPLES + YSF_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
ptr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
q31_t corr = 0;
|
||||
q15_t min = 16000;
|
||||
q15_t max = -16000;
|
||||
|
||||
for (uint8_t i = 0U; i < YSF_SYNC_LENGTH_SYMBOLS; i++) {
|
||||
q15_t val = m_buffer[ptr];
|
||||
|
||||
if (val > max)
|
||||
max = val;
|
||||
if (val < min)
|
||||
min = val;
|
||||
|
||||
switch (YSF_SYNC_SYMBOLS_VALUES[i]) {
|
||||
case +3:
|
||||
corr -= (val + val + val);
|
||||
break;
|
||||
case +1:
|
||||
corr -= val;
|
||||
break;
|
||||
case -1:
|
||||
corr += val;
|
||||
break;
|
||||
default: // -3
|
||||
corr += (val + val + val);
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += YSF_RADIO_SYMBOL_LENGTH;
|
||||
if (ptr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
ptr -= YSF_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 + YSF_FRAME_LENGTH_SAMPLES - YSF_SYNC_LENGTH_SAMPLES + YSF_RADIO_SYMBOL_LENGTH;
|
||||
if (startPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
startPtr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
uint8_t sync[YSF_SYNC_BYTES_LENGTH];
|
||||
samplesToBits(startPtr, YSF_SYNC_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal);
|
||||
|
||||
uint8_t maxErrs;
|
||||
if (m_state == YSFRXS_NONE)
|
||||
maxErrs = MAX_SYNC_BIT_START_ERRS;
|
||||
else
|
||||
maxErrs = MAX_SYNC_BIT_RUN_ERRS;
|
||||
|
||||
uint8_t errs = 0U;
|
||||
for (uint8_t i = 0U; i < YSF_SYNC_BYTES_LENGTH; i++)
|
||||
errs += countBits8(sync[i] ^ YSF_SYNC_BYTES[i]);
|
||||
|
||||
if (errs <= maxErrs) {
|
||||
m_maxCorr = corr;
|
||||
m_lostCount = MAX_SYNC_FRAMES;
|
||||
m_syncPtr = m_dataPtr;
|
||||
|
||||
m_startPtr = startPtr;
|
||||
|
||||
m_endPtr = m_dataPtr + YSF_FRAME_LENGTH_SAMPLES - YSF_SYNC_LENGTH_SAMPLES - 1U;
|
||||
if (m_endPtr >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
m_endPtr -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CYSFRX::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 += YSF_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
start -= YSF_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("YSFRX: 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 CYSFRX::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 += YSF_RADIO_SYMBOL_LENGTH;
|
||||
if (start >= YSF_FRAME_LENGTH_SAMPLES)
|
||||
start -= YSF_FRAME_LENGTH_SAMPLES;
|
||||
}
|
||||
}
|
||||
|
||||
void CYSFRX::writeRSSIData(uint8_t* data)
|
||||
{
|
||||
#if defined(SEND_RSSI_DATA)
|
||||
if (m_rssiCount > 0U) {
|
||||
uint16_t rssi = m_rssiAccum / m_rssiCount;
|
||||
|
||||
data[121U] = (rssi >> 8) & 0xFFU;
|
||||
data[122U] = (rssi >> 0) & 0xFFU;
|
||||
|
||||
serial.writeYSFData(data, YSF_FRAME_LENGTH_BYTES + 3U);
|
||||
} else {
|
||||
serial.writeYSFData(data, YSF_FRAME_LENGTH_BYTES + 1U);
|
||||
}
|
||||
#else
|
||||
serial.writeYSFData(data, YSF_FRAME_LENGTH_BYTES + 1U);
|
||||
#endif
|
||||
|
||||
m_rssiAccum = 0U;
|
||||
m_rssiCount = 0U;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -31,27 +31,38 @@ class CYSFRX {
|
||||
public:
|
||||
CYSFRX();
|
||||
|
||||
void samples(const q15_t* samples, uint8_t length);
|
||||
void samples(const q15_t* samples, uint16_t* rssi, uint8_t length);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
uint32_t m_pll;
|
||||
bool m_prev;
|
||||
YSFRX_STATE m_state;
|
||||
uint32_t m_symbolBuffer;
|
||||
uint64_t m_bitBuffer;
|
||||
q15_t m_symbols[YSF_SYNC_LENGTH_SYMBOLS];
|
||||
uint8_t m_outBuffer[YSF_FRAME_LENGTH_BYTES + 3U];
|
||||
uint8_t* m_buffer;
|
||||
uint16_t m_bufferPtr;
|
||||
uint16_t m_symbolPtr;
|
||||
uint32_t m_bitBuffer[YSF_RADIO_SYMBOL_LENGTH];
|
||||
q15_t m_buffer[YSF_FRAME_LENGTH_SAMPLES];
|
||||
uint16_t m_bitPtr;
|
||||
uint16_t m_dataPtr;
|
||||
uint16_t m_startPtr;
|
||||
uint16_t m_endPtr;
|
||||
uint16_t m_minSyncPtr;
|
||||
uint16_t m_maxSyncPtr;
|
||||
uint16_t m_syncPtr;
|
||||
q31_t m_maxCorr;
|
||||
uint16_t m_lostCount;
|
||||
q15_t m_centre;
|
||||
q15_t m_threshold;
|
||||
uint8_t m_countdown;
|
||||
q15_t m_centre[16U];
|
||||
q15_t m_centreVal;
|
||||
q15_t m_threshold[16U];
|
||||
q15_t m_thresholdVal;
|
||||
uint8_t m_averagePtr;
|
||||
uint32_t m_rssiAccum;
|
||||
uint16_t m_rssiCount;
|
||||
|
||||
void processNone(q15_t sample);
|
||||
void processData(q15_t sample);
|
||||
bool correlateSync();
|
||||
void calculateLevels(uint16_t start, uint16_t count);
|
||||
void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold);
|
||||
void writeRSSIData(uint8_t* data);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2009-2017 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
|
||||
@@ -16,31 +17,27 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
// #define WANT_DEBUG
|
||||
|
||||
#include "Config.h"
|
||||
#include "Globals.h"
|
||||
#include "YSFTX.h"
|
||||
|
||||
#include "YSFDefines.h"
|
||||
|
||||
#if defined(WIDE_C4FSK_FILTERS_TX)
|
||||
// Generated using rcosdesign(0.2, 4, 5, 'sqrt') in MATLAB
|
||||
static q15_t YSF_C4FSK_FILTER[] = {688, -680, -2158, -3060, -2724, -775, 2684, 7041, 11310, 14425, 15565, 14425,
|
||||
11310, 7041, 2684, -775, -2724, -3060, -2158, -680, 688, 0};
|
||||
const uint16_t YSF_C4FSK_FILTER_LEN = 22U;
|
||||
#else
|
||||
// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB
|
||||
static q15_t YSF_C4FSK_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995,
|
||||
11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112,
|
||||
-553, -847, -731, -340, 104, 401, 0};
|
||||
const uint16_t YSF_C4FSK_FILTER_LEN = 42U;
|
||||
#endif
|
||||
static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442,
|
||||
-5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442,
|
||||
-4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5
|
||||
const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L
|
||||
|
||||
const q15_t YSF_LEVELA[] = { 809, 809, 809, 809, 809};
|
||||
const q15_t YSF_LEVELB[] = { 269, 269, 269, 269, 269};
|
||||
const q15_t YSF_LEVELC[] = {-269, -269, -269, -269, -269};
|
||||
const q15_t YSF_LEVELD[] = {-809, -809, -809, -809, -809};
|
||||
const q15_t YSF_LEVELA_HI = 1893;
|
||||
const q15_t YSF_LEVELB_HI = 631;
|
||||
const q15_t YSF_LEVELC_HI = -631;
|
||||
const q15_t YSF_LEVELD_HI = -1893;
|
||||
|
||||
const q15_t YSF_LEVELA_LO = 948;
|
||||
const q15_t YSF_LEVELB_LO = 316;
|
||||
const q15_t YSF_LEVELC_LO = -316;
|
||||
const q15_t YSF_LEVELD_LO = -948;
|
||||
|
||||
const uint8_t YSF_START_SYNC = 0x77U;
|
||||
const uint8_t YSF_END_SYNC = 0xFFU;
|
||||
@@ -53,13 +50,14 @@ m_poBuffer(),
|
||||
m_poLen(0U),
|
||||
m_poPtr(0U),
|
||||
m_txDelay(240U), // 200ms
|
||||
m_count(0U)
|
||||
m_loDev(false)
|
||||
{
|
||||
::memset(m_modState, 0x00U, 70U * sizeof(q15_t));
|
||||
::memset(m_modState, 0x00U, 16U * sizeof(q15_t));
|
||||
|
||||
m_modFilter.numTaps = YSF_C4FSK_FILTER_LEN;
|
||||
m_modFilter.pState = m_modState;
|
||||
m_modFilter.pCoeffs = YSF_C4FSK_FILTER;
|
||||
m_modFilter.L = YSF_RADIO_SYMBOL_LENGTH;
|
||||
m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN;
|
||||
m_modFilter.pCoeffs = RRC_0_2_FILTER;
|
||||
m_modFilter.pState = m_modState;
|
||||
}
|
||||
|
||||
void CYSFTX::process()
|
||||
@@ -69,8 +67,6 @@ void CYSFTX::process()
|
||||
|
||||
if (m_poLen == 0U) {
|
||||
if (!m_tx) {
|
||||
m_count = 0U;
|
||||
|
||||
for (uint16_t i = 0U; i < m_txDelay; i++)
|
||||
m_poBuffer[m_poLen++] = YSF_START_SYNC;
|
||||
} else {
|
||||
@@ -118,59 +114,48 @@ uint8_t CYSFTX::writeData(const uint8_t* data, uint8_t length)
|
||||
|
||||
void CYSFTX::writeByte(uint8_t c)
|
||||
{
|
||||
q15_t inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U + 1U];
|
||||
q15_t inBuffer[4U];
|
||||
q15_t outBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U];
|
||||
|
||||
const uint8_t MASK = 0xC0U;
|
||||
|
||||
q15_t* p = inBuffer;
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2, p += YSF_RADIO_SYMBOL_LENGTH) {
|
||||
for (uint8_t i = 0U; i < 4U; i++, c <<= 2) {
|
||||
switch (c & MASK) {
|
||||
case 0xC0U:
|
||||
::memcpy(p, YSF_LEVELA, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELA_LO : YSF_LEVELA_HI;
|
||||
break;
|
||||
case 0x80U:
|
||||
::memcpy(p, YSF_LEVELB, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELB_LO : YSF_LEVELB_HI;
|
||||
break;
|
||||
case 0x00U:
|
||||
::memcpy(p, YSF_LEVELC, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELC_LO : YSF_LEVELC_HI;
|
||||
break;
|
||||
default:
|
||||
::memcpy(p, YSF_LEVELD, YSF_RADIO_SYMBOL_LENGTH * sizeof(q15_t));
|
||||
inBuffer[i] = m_loDev ? YSF_LEVELD_LO : YSF_LEVELD_HI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t blockSize = YSF_RADIO_SYMBOL_LENGTH * 4U;
|
||||
::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U);
|
||||
|
||||
// Handle the case of the oscillator not being accurate enough
|
||||
if (m_sampleCount > 0U) {
|
||||
m_count += YSF_RADIO_SYMBOL_LENGTH * 4U;
|
||||
|
||||
if (m_count >= m_sampleCount) {
|
||||
if (m_sampleInsert) {
|
||||
inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U] = inBuffer[YSF_RADIO_SYMBOL_LENGTH * 4U - 1U];
|
||||
blockSize++;
|
||||
} else {
|
||||
blockSize--;
|
||||
}
|
||||
|
||||
m_count -= m_sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
::arm_fir_fast_q15(&m_modFilter, inBuffer, outBuffer, blockSize);
|
||||
|
||||
io.write(STATE_YSF, outBuffer, blockSize);
|
||||
io.write(STATE_YSF, outBuffer, YSF_RADIO_SYMBOL_LENGTH * 4U);
|
||||
}
|
||||
|
||||
void CYSFTX::setTXDelay(uint8_t delay)
|
||||
{
|
||||
m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay
|
||||
|
||||
if (m_txDelay > 1200U)
|
||||
m_txDelay = 1200U;
|
||||
}
|
||||
|
||||
uint16_t CYSFTX::getSpace() const
|
||||
uint8_t CYSFTX::getSpace() const
|
||||
{
|
||||
return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES;
|
||||
}
|
||||
|
||||
void CYSFTX::setLoDev(bool on)
|
||||
{
|
||||
m_loDev = on;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015,2016,2017 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
|
||||
@@ -33,17 +33,19 @@ public:
|
||||
|
||||
void setTXDelay(uint8_t delay);
|
||||
|
||||
uint16_t getSpace() const;
|
||||
uint8_t getSpace() const;
|
||||
|
||||
void setLoDev(bool on);
|
||||
|
||||
private:
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_instance_q15 m_modFilter;
|
||||
q15_t m_modState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare
|
||||
uint8_t m_poBuffer[920U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
uint32_t m_count;
|
||||
CSerialRB m_buffer;
|
||||
arm_fir_interpolate_instance_q15 m_modFilter;
|
||||
q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare
|
||||
uint8_t m_poBuffer[1200U];
|
||||
uint16_t m_poLen;
|
||||
uint16_t m_poPtr;
|
||||
uint16_t m_txDelay;
|
||||
bool m_loDev;
|
||||
|
||||
void writeByte(uint8_t c);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
source [find interface/stlink-v2.cfg]
|
||||
source [find target/stm32f1x.cfg]
|
||||
@@ -0,0 +1,187 @@
|
||||
/* 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")
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
/* 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
|
||||
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @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>© 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****/
|
||||
|
||||
Reference in New Issue
Block a user