From 694b83123aa484989b8dc19293a17d123a14baeb Mon Sep 17 00:00:00 2001 From: LucaMarche IZ1MLT Date: Wed, 30 May 2018 08:54:45 +0200 Subject: [PATCH] Update 48k version In to NXDN possible problem on sinc filter anc isinc filter, think they must be recalculated but do not know how to do it. On D-STAR, DMR, C4FM and P25 it work. --- CWIdTX.cpp | 7 +- CalDMR.cpp | 186 +++++- CalDMR.h | 18 +- CalDStarRX.cpp | 2 +- CalNXDN.cpp | 94 +++ CalNXDN.h | 44 ++ CalP25.cpp | 97 +++ CalP25.h | 45 ++ CalRSSI.cpp | 65 ++ CalRSSI.h | 38 ++ Config.h | 25 +- DMRDMORX.cpp | 161 +++-- DMRDMORX.h | 9 +- DMRDMOTX.cpp | 98 +-- DMRDMOTX.h | 19 +- DMRDefines.h | 26 +- DMRIdleRX.cpp | 39 +- DMRIdleRX.h | 4 +- DMRSlotRX.cpp | 121 ++-- DMRSlotRX.h | 7 +- DMRTX.cpp | 157 +++-- DMRTX.h | 41 +- DStarDefines.h | 54 +- DStarRX.cpp | 443 ++++++++----- DStarRX.h | 41 +- DStarTX.cpp | 84 +-- DStarTX.h | 19 +- Debug.h | 17 +- GitVersion.h | 1 + Globals.h | 41 +- IO.cpp | 392 +++++++++--- IO.h | 35 +- IODue.cpp | 26 +- IOSTM.cpp | 1331 ++++++++++++++++++++++++++++++---------- IOSTM_CMSIS.cpp | 442 +++++++++++++ IOTeensy.cpp | 75 ++- MMDVM.cpp | 30 +- MMDVM.ino | 23 +- MMDVM_STM32F4xx.coproj | 50 +- Makefile | 358 ++++++++--- Makefile.Arduino | 189 ++++++ Makefile.CMSIS | 151 +++++ NXDNDefines.h | 50 ++ NXDNRX.cpp | 405 ++++++++++++ NXDNRX.h | 69 +++ NXDNTX.cpp | 172 ++++++ NXDNTX.h | 53 ++ P25Defines.h | 29 +- P25RX.cpp | 621 +++++++++++++------ P25RX.h | 48 +- P25TX.cpp | 103 ++-- P25TX.h | 23 +- README.md | 4 +- RSSIRB.cpp | 2 - RSSIRB.h | 6 +- RingBuff.h | 47 ++ STM32Utils.h | 50 ++ SampleRB.cpp | 2 - SampleRB.h | 6 +- SerialArduino.cpp | 16 +- SerialPort.cpp | 318 ++++++++-- SerialPort.h | 18 +- SerialRB.cpp | 1 - SerialRB.h | 8 +- SerialSTM.cpp | 1114 +++++++++++++++++---------------- SerialSTM_CMSIS.cpp | 160 +++++ Utils.h | 6 +- YSFDefines.h | 8 +- YSFRX.cpp | 488 ++++++++++----- YSFRX.h | 37 +- YSFTX.cpp | 95 ++- YSFTX.h | 22 +- openocd.cfg | 2 + stm32f722_link.ld | 137 +++++ stm32f767_link.ld | 137 +++++ 75 files changed, 7038 insertions(+), 2324 deletions(-) create mode 100644 CalNXDN.cpp create mode 100644 CalNXDN.h create mode 100644 CalP25.cpp create mode 100644 CalP25.h create mode 100644 CalRSSI.cpp create mode 100644 CalRSSI.h create mode 100644 GitVersion.h create mode 100644 IOSTM_CMSIS.cpp create mode 100644 Makefile.Arduino create mode 100644 Makefile.CMSIS create mode 100644 NXDNDefines.h create mode 100644 NXDNRX.cpp create mode 100644 NXDNRX.h create mode 100644 NXDNTX.cpp create mode 100644 NXDNTX.h create mode 100644 RingBuff.h create mode 100644 STM32Utils.h create mode 100644 SerialSTM_CMSIS.cpp create mode 100644 openocd.cfg create mode 100644 stm32f722_link.ld create mode 100644 stm32f767_link.ld diff --git a/CWIdTX.cpp b/CWIdTX.cpp index ffe8ff5..36784db 100644 --- a/CWIdTX.cpp +++ b/CWIdTX.cpp @@ -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" @@ -26,7 +24,6 @@ q15_t TONE[] = { 0, 261, 518, 765, 1000, 1218, 1414, 1587, 1732, 1848, 1932, 1983, 2000, 1983, 1932, 1848, 1732, 1587, 1414, 1218, 1000, 765, 518, 261, 0, -261, -518, -765, -1000, -1218, -1414, -1587, -1732, -1848, -1932, -1983, -2000, -1983, -1932, -1848, -1732, -1587, -1414, -1218, -1000, -765, -518, -261}; - q15_t SILENCE[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -78,6 +75,8 @@ const struct { {'/', 0xEAE80000U, 16U}, {'?', 0xAEEA0000U, 18U}, {',', 0xEEAEE000U, 22U}, + {'-', 0xEAAE0000U, 18U}, + {'=', 0xEAB80000U, 16U}, {' ', 0x00000000U, 4U}, {0U, 0x00000000U, 0U} }; diff --git a/CalDMR.cpp b/CalDMR.cpp index 5622c1d..5a92fb0 100644 --- a/CalDMR.cpp +++ b/CalDMR.cpp @@ -21,19 +21,190 @@ #include "Globals.h" #include "CalDMR.h" +// Voice LC BS Header, CC: 1, srcID: 1, dstID: TG9 +const uint8_t VH_1K[] = {0x00U, + 0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U, + 0x60U, 0x84U, 0x6DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xDEU, 0x30U, 0x30U, + 0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU}; + +// Voice Term BS with LC, CC: 1, srcID: 1, dstID: TG9 +const uint8_t VT_1K[] = {0x00U, + 0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U, + 0xC0U, 0x84U, 0xADU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD9U, 0x65U, 0x24U, + 0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU}; + +// Voice LC MS Header, CC: 1, srcID: 1, dstID: TG9 +const uint8_t VH_DMO1K[] = {0x00U, + 0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U, + 0x60U, 0x84U, 0x6DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x7EU, 0x30U, 0x30U, + 0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU}; + +// Voice Term MS with LC, CC: 1, srcID: 1, dstID: TG9 +const uint8_t VT_DMO1K[] = {0x00U, + 0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U, + 0xC0U, 0x84U, 0xADU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x79U, 0x65U, 0x24U, + 0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU}; + +// Voice coding data + FEC, 1031 Hz Test Pattern +const uint8_t VOICE_1K[] = {0x00U, + 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU, 0xCEU, 0xA8U, + 0xFEU, 0x83U, 0xA0U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x0CU, 0xC4U, 0x58U, + 0x20U, 0x0AU, 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU}; + +// Embedded LC BS: CC: 1, srcID: 1, dstID: TG9 +const uint8_t SYNCEMB_1K[6][7] = { + {0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U}, // BS VOICE SYNC (audio seq 0) + {0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1) + {0x01U, 0x70U, 0x00U, 0x90U, 0x00U, 0x07U, 0x40U}, // EMB + Embedded LC2 (audio seq 2) + {0x01U, 0x70U, 0x00U, 0x31U, 0x40U, 0x07U, 0x40U}, // EMB + Embedded LC3 (audio seq 3) + {0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4) + {0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5) + +// Embedded LC MS: CC: 1, srcID: 1, dstID: TG9 +const uint8_t SYNCEMB_DMO1K[6][7] = { + {0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U}, // MS VOICE SYNC (audio seq 0) + {0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1) + {0x01U, 0x70U, 0x00U, 0x90U, 0x00U, 0x07U, 0x40U}, // EMB + Embedded LC2 (audio seq 2) + {0x01U, 0x70U, 0x00U, 0x31U, 0x40U, 0x07U, 0x40U}, // EMB + Embedded LC3 (audio seq 3) + {0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4) + {0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5) + +// Short LC: +// TS1: dstID: 0, ACTIVITY_NONE +// TS2: dstID: TG9, ACTIVITY_VOICE +const uint8_t SHORTLC_1K[] = {0x33U, 0x3AU, 0xA0U, 0x30U, 0x00U, 0x55U, 0xA6U, 0x5FU, 0x50U}; CCalDMR::CCalDMR() : -m_transmit(false) +m_transmit(false), +m_state(DMRCAL1K_IDLE), +m_frame_start(0U), +m_dmr1k(), +m_audioSeq(0) { + ::memcpy(m_dmr1k, VOICE_1K, DMR_FRAME_LENGTH_BYTES + 1U); } void CCalDMR::process() { - if (m_transmit) { - dmrTX.setCal(true); - dmrTX.process(); - } else { - dmrTX.setCal(false); + switch (m_modemState) { + case STATE_DMRCAL: + case STATE_LFCAL: + if (m_transmit) { + dmrTX.setCal(true); + dmrTX.process(); + } else { + dmrTX.setCal(false); + } + break; + case STATE_DMRCAL1K: + dmr1kcal(); + break; + case STATE_DMRDMO1K: + dmrdmo1k(); + break; + default: + break; + } +} + +void CCalDMR::createData1k(uint8_t n) +{ + for(uint8_t i = 0; i < 5U; i++) + m_dmr1k[i + 15U] = SYNCEMB_1K[n][i + 1U]; + + m_dmr1k[14U] &= 0xF0U; + m_dmr1k[20U] &= 0x0FU; + m_dmr1k[14U] |= SYNCEMB_1K[n][0] & 0x0FU; + m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U; +} + +void CCalDMR::createDataDMO1k(uint8_t n) +{ + for(uint8_t i = 0; i < 5U; i++) + m_dmr1k[i + 15U] = SYNCEMB_DMO1K[n][i + 1U]; + + m_dmr1k[14U] &= 0xF0U; + m_dmr1k[20U] &= 0x0FU; + m_dmr1k[14U] |= SYNCEMB_DMO1K[n][0] & 0x0FU; + m_dmr1k[20U] |= SYNCEMB_DMO1K[n][6] & 0xF0U; +} + +void CCalDMR::dmr1kcal() +{ + dmrTX.process(); + + uint16_t space = dmrTX.getSpace2(); + if (space < 1U) + return; + + switch (m_state) { + case DMRCAL1K_VH: + dmrTX.setColorCode(1U); + dmrTX.writeShortLC(SHORTLC_1K, 9U); + dmrTX.writeData2(VH_1K, DMR_FRAME_LENGTH_BYTES + 1U); + dmrTX.setStart(true); + m_state = DMRCAL1K_VOICE; + break; + case DMRCAL1K_VOICE: + createData1k(m_audioSeq); + dmrTX.writeData2(m_dmr1k, DMR_FRAME_LENGTH_BYTES + 1U); + if(m_audioSeq == 5U) { + m_audioSeq = 0U; + if(!m_transmit) + m_state = DMRCAL1K_VT; + } else + m_audioSeq++; + break; + case DMRCAL1K_VT: + dmrTX.writeData2(VT_1K, DMR_FRAME_LENGTH_BYTES + 1U); + m_frame_start = dmrTX.getFrameCount(); + m_state = DMRCAL1K_WAIT; + break; + case DMRCAL1K_WAIT: + if (dmrTX.getFrameCount() > (m_frame_start + 30U)) { + dmrTX.setStart(false); + dmrTX.resetFifo2(); + m_audioSeq = 0U; + m_state = DMRCAL1K_IDLE; + } + break; + default: + m_state = DMRCAL1K_IDLE; + break; + } +} + +void CCalDMR::dmrdmo1k() +{ + dmrDMOTX.process(); + + uint16_t space = dmrDMOTX.getSpace(); + if (space < 1U) + return; + + switch (m_state) { + case DMRCAL1K_VH: + dmrDMOTX.writeData(VH_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U); + m_state = DMRCAL1K_VOICE; + break; + case DMRCAL1K_VOICE: + createDataDMO1k(m_audioSeq); + dmrDMOTX.writeData(m_dmr1k, DMR_FRAME_LENGTH_BYTES + 1U); + if(m_audioSeq == 5U) { + m_audioSeq = 0U; + if(!m_transmit) + m_state = DMRCAL1K_VT; + } else + m_audioSeq++; + break; + case DMRCAL1K_VT: + dmrDMOTX.writeData(VT_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U); + m_state = DMRCAL1K_IDLE; + break; + default: + m_state = DMRCAL1K_IDLE; + m_audioSeq = 0U; + break; } } @@ -44,6 +215,9 @@ uint8_t CCalDMR::write(const uint8_t* data, uint8_t length) m_transmit = data[0U] == 1U; + if(m_transmit && m_state == DMRCAL1K_IDLE && (m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K)) + m_state = DMRCAL1K_VH; + return 0U; } diff --git a/CalDMR.h b/CalDMR.h index c3231c5..06ff6e6 100644 --- a/CalDMR.h +++ b/CalDMR.h @@ -23,16 +23,32 @@ #include "Config.h" #include "DMRDefines.h" +enum DMRCAL1K { + DMRCAL1K_IDLE, + DMRCAL1K_VH, + DMRCAL1K_VOICE, + DMRCAL1K_VT, + DMRCAL1K_WAIT +}; + class CCalDMR { public: CCalDMR(); void process(); + void dmr1kcal(); + void dmrdmo1k(); + void createData1k(uint8_t n); + void createDataDMO1k(uint8_t n); uint8_t write(const uint8_t* data, uint8_t length); private: - bool m_transmit; + bool m_transmit; + DMRCAL1K m_state; + uint32_t m_frame_start; + uint8_t m_dmr1k[DMR_FRAME_LENGTH_BYTES + 1U]; + uint8_t m_audioSeq; }; #endif diff --git a/CalDStarRX.cpp b/CalDStarRX.cpp index 32b6345..0336938 100644 --- a/CalDStarRX.cpp +++ b/CalDStarRX.cpp @@ -24,7 +24,7 @@ const unsigned int BUFFER_LENGTH = 200U; const uint32_t PLLMAX = 0x10000U; -const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH; +const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_SYMBOL_LENGTH; const uint32_t INC = PLLINC / 32U; // D-Star bit order version of 0x55 0x2D 0x16 diff --git a/CalNXDN.cpp b/CalNXDN.cpp new file mode 100644 index 0000000..c223f59 --- /dev/null +++ b/CalNXDN.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "CalNXDN.h" + +// NXDN 1031 Hz Test Pattern, RAN: 1, Unit ID: 1, Dst Group ID: 1, Outbound Direction +const uint8_t NXDN_CAL1K[4][49] = { + {0x00U, + 0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x7CU, 0xFAU, 0x0AU, 0x6EU, 0x8AU, 0x23U, 0x56U, 0xE8U, + 0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U, + 0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U, + 0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U, + 0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U}, + + {0x00U, + 0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x7CU, 0x6DU, 0xBBU, 0x0EU, 0xB3U, 0xA4U, 0x26U, 0xA8U, + 0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U, + 0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U, + 0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U, + 0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U}, + + {0x00U, + 0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x76U, 0x3AU, 0x1BU, 0x4AU, 0x81U, 0xA8U, 0xE2U, 0x80U, + 0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U, + 0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U, + 0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U, + 0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U}, + + {0x00U, + 0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x74U, 0x28U, 0x83U, 0x02U, 0xB0U, 0x2DU, 0x07U, 0xE2U, + 0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U, + 0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U, + 0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U, + 0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U}}; + +CCalNXDN::CCalNXDN() : +m_transmit(false), +m_state(NXDNCAL1K_IDLE), +m_audioSeq(0U) +{ +} + +void CCalNXDN::process() +{ + nxdnTX.process(); + + uint16_t space = nxdnTX.getSpace(); + if (space < 1U) + return; + + switch (m_state) { + case NXDNCAL1K_TX: + nxdnTX.writeData(NXDN_CAL1K[m_audioSeq], NXDN_FRAME_LENGTH_BYTES + 1U); + m_audioSeq = (m_audioSeq + 1U) % 4U; + if(!m_transmit) + m_state = NXDNCAL1K_IDLE; + break; + default: + m_state = NXDNCAL1K_IDLE; + m_audioSeq = 0U; + break; + } +} + +uint8_t CCalNXDN::write(const uint8_t* data, uint8_t length) +{ + if (length != 1U) + return 4U; + + m_transmit = data[0U] == 1U; + + if(m_transmit && m_state == NXDNCAL1K_IDLE) + m_state = NXDNCAL1K_TX; + + return 0U; +} + diff --git a/CalNXDN.h b/CalNXDN.h new file mode 100644 index 0000000..f0051ef --- /dev/null +++ b/CalNXDN.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CALNXDN_H) +#define CALNXDN_H + +#include "Config.h" + +enum NXDNCAL1K { + NXDNCAL1K_IDLE, + NXDNCAL1K_TX +}; + +class CCalNXDN { +public: + CCalNXDN(); + + void process(); + + uint8_t write(const uint8_t* data, uint8_t length); + +private: + bool m_transmit; + NXDNCAL1K m_state; + uint8_t m_audioSeq; +}; + +#endif + diff --git a/CalP25.cpp b/CalP25.cpp new file mode 100644 index 0000000..b1b9b6d --- /dev/null +++ b/CalP25.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "CalP25.h" + +// Recommended 1011 Hz test pattern for P25 Phase 1 (ANSI/TIA-102.CAAA) +// NAC: 0x293, srcID: 1, dstID: TG1 +unsigned char LDU1_1K[] = {0x00, + 0x55, 0x75, 0xF5, 0xFF, 0x77, 0xFF, 0x29, 0x35, 0x54, 0x7B, 0xCB, 0x19, 0x4D, 0x0D, 0xCE, 0x24, 0xA1, 0x24, + 0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB9, 0x18, 0x44, 0xFC, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xE4, 0xE2, 0x4A, 0x10, + 0x90, 0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4C, 0xFC, 0x16, 0x29, 0x62, 0x76, 0x0E, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x02, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x94, + 0x89, 0xD8, 0x39, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x38, 0x24, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4, + 0x18, 0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x70, 0xE2, 0x4A, 0x12, 0x40, + 0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x16, 0x29, 0x62, 0x76, 0x0E, 0x6D, 0xE5, 0xD5, 0x48, + 0xAD, 0xE3, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x08, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x24, + 0xD8, 0x3B, 0xA1, 0x41, 0xC2, 0xD2, 0xBA, 0x38, 0x90, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4, 0x60, + 0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0x94, 0xC8, 0xFB, 0x02, 0x35, 0xA4, 0xE2, 0x4A, 0x12, 0x43, 0x50, + 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x58, 0x29, 0x62, 0x76, 0x0E, 0xC0, 0x00, 0x00, 0x00, 0x0C, + 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB8, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xE4}; + +unsigned char LDU2_1K[] = {0x00, + 0x55, 0x75, 0xF5, 0xFF, 0x77, 0xFF, 0x29, 0x3A, 0xB8, 0xA4, 0xEF, 0xB0, 0x9A, 0x8A, 0xCE, 0x24, 0xA1, 0x24, + 0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB9, 0x18, 0x44, 0xFC, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xEC, 0xE2, 0x4A, 0x10, + 0x90, 0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4C, 0xFC, 0x16, 0x29, 0x62, 0x76, 0x0E, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x02, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x94, + 0x89, 0xD8, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x24, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4, + 0x18, 0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x4A, 0x12, 0x40, + 0xD4, 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x16, 0x29, 0x62, 0x76, 0x0E, 0xE0, 0xE0, 0x00, 0x00, + 0x00, 0x03, 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x08, 0xF8, 0x6E, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x24, + 0xD8, 0x39, 0xAE, 0x8B, 0x48, 0xB6, 0x49, 0x38, 0x90, 0xA1, 0x24, 0x35, 0x0C, 0xF0, 0x2F, 0x86, 0xE4, 0x60, + 0x44, 0xFF, 0x05, 0x8A, 0x58, 0x9D, 0x83, 0xB9, 0xA8, 0xF4, 0xF1, 0xFD, 0x60, 0xE2, 0x4A, 0x12, 0x43, 0x50, + 0x33, 0xC0, 0xBE, 0x1B, 0x91, 0x84, 0x4F, 0xF0, 0x58, 0x29, 0x62, 0x76, 0x0E, 0x40, 0x00, 0x00, 0x00, 0x0C, + 0x89, 0x28, 0x49, 0x0D, 0x43, 0x3C, 0x0B, 0xE1, 0xB8, 0x46, 0x11, 0x3F, 0xC1, 0x62, 0x96, 0x27, 0x60, 0xEC}; + +CCalP25::CCalP25() : +m_transmit(false), +m_state(P25CAL1K_IDLE) +{ +} + +void CCalP25::process() +{ + p25TX.process(); + + uint16_t space = p25TX.getSpace(); + if (space < 1U) + return; + + switch (m_state) { + case P25CAL1K_LDU1: + p25TX.writeData(LDU1_1K, P25_LDU_FRAME_LENGTH_BYTES + 1U); + m_state = P25CAL1K_LDU2; + break; + case P25CAL1K_LDU2: + p25TX.writeData(LDU2_1K, P25_LDU_FRAME_LENGTH_BYTES + 1U); + if(!m_transmit) + m_state = P25CAL1K_IDLE; + else + m_state = P25CAL1K_LDU1; + break; + default: + m_state = P25CAL1K_IDLE; + break; + } +} + +uint8_t CCalP25::write(const uint8_t* data, uint8_t length) +{ + if (length != 1U) + return 4U; + + m_transmit = data[0U] == 1U; + + if(m_transmit && m_state == P25CAL1K_IDLE) + m_state = P25CAL1K_LDU1; + + return 0U; +} + diff --git a/CalP25.h b/CalP25.h new file mode 100644 index 0000000..6374ed2 --- /dev/null +++ b/CalP25.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CALP25_H) +#define CALP25_H + +#include "Config.h" +#include "P25Defines.h" + +enum P25CAL1K { + P25CAL1K_IDLE, + P25CAL1K_LDU1, + P25CAL1K_LDU2 +}; + +class CCalP25 { +public: + CCalP25(); + + void process(); + + uint8_t write(const uint8_t* data, uint8_t length); + +private: + bool m_transmit; + P25CAL1K m_state; +}; + +#endif + diff --git a/CalRSSI.cpp b/CalRSSI.cpp new file mode 100644 index 0000000..175975f --- /dev/null +++ b/CalRSSI.cpp @@ -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; + } + } +} + diff --git a/CalRSSI.h b/CalRSSI.h new file mode 100644 index 0000000..2072243 --- /dev/null +++ b/CalRSSI.h @@ -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 + diff --git a/Config.h b/Config.h index 3eb0ed4..04056ea 100644 --- a/Config.h +++ b/Config.h @@ -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 @@ -27,7 +27,7 @@ // Frequencies such as 10.0 Mhz (48000 * 208.333) or 20 Mhz (48000 * 416.666) are not suitable. // // For 12 MHz -// #define EXTERNAL_OSC 12000000 +#define EXTERNAL_OSC 12000000 // For 12.288 MHz // #define EXTERNAL_OSC 12288000 // For 14.4 MHz @@ -45,21 +45,28 @@ // #define ARDUINO_DUE_PAPA // For the ZUM V1.0 and V1.0.1 boards pin layout -#define ARDUINO_DUE_ZUM_V10 +// #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 +#define SEND_RSSI_DATA // Use the modem as a serial repeater for Nextion displays // #define SERIAL_REPEATER -#endif +// To reduce CPU load, you can remove the DC blocker by commenting out the next line +// #define USE_DCBLOCKER +#endif diff --git a/DMRDMORX.cpp b/DMRDMORX.cpp index 5eefa23..bb2395c 100644 --- a/DMRDMORX.cpp +++ b/DMRDMORX.cpp @@ -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" @@ -26,10 +24,10 @@ const q15_t SCALING_FACTOR = 19505; // Q15(0.60) -const uint8_t MAX_SYNC_SYMBOLS_ERRS = 2U; -const uint8_t MAX_SYNC_BYTES_ERRS = 3U; +const uint8_t MAX_SYNC_SYMBOLS_ERRS = 4U; +const uint8_t MAX_SYNC_BYTES_ERRS = 6U; -const uint8_t MAX_SYNC_LOST_FRAMES = 13U; +const uint8_t MAX_SYNC_LOST_FRAMES = 26U; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -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 +} diff --git a/DMRDMORX.h b/DMRDMORX.h index 73b2545..344ffa2 100644 --- a/DMRDMORX.h +++ b/DMRDMORX.h @@ -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 - diff --git a/DMRDMOTX.cpp b/DMRDMOTX.cpp index ec29509..9e10c0a 100644 --- a/DMRDMOTX.cpp +++ b/DMRDMOTX.cpp @@ -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,22 +18,33 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// #define WANT_DEBUG - #include "Config.h" #include "Globals.h" #include "DMRSlotType.h" -// Generated using rcosdesign(0.2, 4, 10, 'sqrt') in MATLAB -static q15_t DMR_C4FSK_FILTER[] = {486, 39, -480, -1022, -1526, -1928, -2164, -2178, -1927, -1384, -548, 561, 1898, 3399, 4980, 6546, 7999, 9246, 10202, 10803, 11008, 10803, 10202, 9246, - 7999, 6546, 4980, 3399, 1898, 561, -548, -1384, -1927, -2178, -2164, -1928, -1526, -1022, -480, 39, 486, 0}; -const uint16_t DMR_C4FSK_FILTER_LEN = 42U; +// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179, + -1548, -1769, -1795, -1597, -1172, -544, 237, 1092, 1927, 2637, + 3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739, + -6442, -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822, + 19484, 23810, 27520, 30367, 32156, 32767, 32156, 30367, 27520, + 23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735, + -6483, -6442, -5739, -4544, -3043, -1431, 116, 1447, 2454, + 3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, -1597, + -1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10 +const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t DMR_LEVELA[] = { 395, 395, 395, 395, 395, 395, 395, 395, 395, 395}; -const q15_t DMR_LEVELB[] = { 131, 131, 131, 131, 131, 131, 131, 131, 131, 131}; -const q15_t DMR_LEVELC[] = {-131, -131, -131, -131, -131, -131, -131, -131, -131, -131}; -const q15_t DMR_LEVELD[] = {-395, -395, -395, -395, -395, -395, -395, -395, -395, -395}; +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(), @@ -41,14 +53,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, 90U * 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() @@ -56,13 +68,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; @@ -104,53 +120,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); } @@ -158,5 +155,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; } diff --git a/DMRDMOTX.h b/DMRDMOTX.h index f4a5e09..62ef2c1 100644 --- a/DMRDMOTX.h +++ b/DMRDMOTX.h @@ -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[90U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare - uint8_t m_poBuffer[1200U]; - 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); }; diff --git a/DMRDefines.h b/DMRDefines.h index 516ac2a..1bbeff0 100644 --- a/DMRDefines.h +++ b/DMRDefines.h @@ -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 @@ -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; diff --git a/DMRIdleRX.cpp b/DMRIdleRX.cpp index 8205150..17a4d1f 100644 --- a/DMRIdleRX.cpp +++ b/DMRIdleRX.cpp @@ -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" @@ -26,8 +24,8 @@ const q15_t SCALING_FACTOR = 19505; // Q15(0.60) -const uint8_t MAX_SYNC_SYMBOLS_ERRS = 2U; -const uint8_t MAX_SYNC_BYTES_ERRS = 3U; +const uint8_t MAX_SYNC_SYMBOLS_ERRS = 4U; +const uint8_t MAX_SYNC_BYTES_ERRS = 6U; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -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; } - diff --git a/DMRIdleRX.h b/DMRIdleRX.h index fbb82fa..922ea85 100644 --- a/DMRIdleRX.h +++ b/DMRIdleRX.h @@ -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 diff --git a/DMRSlotRX.cpp b/DMRSlotRX.cpp index 2b83353..a3d1b21 100644 --- a/DMRSlotRX.cpp +++ b/DMRSlotRX.cpp @@ -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" @@ -29,10 +27,10 @@ const uint16_t SCAN_END = 920U; const q15_t SCALING_FACTOR = 19505; // Q15(0.60) -const uint8_t MAX_SYNC_SYMBOLS_ERRS = 2U; -const uint8_t MAX_SYNC_BYTES_ERRS = 3U; +const uint8_t MAX_SYNC_SYMBOLS_ERRS = 4U; +const uint8_t MAX_SYNC_BYTES_ERRS = 6U; -const uint8_t MAX_SYNC_LOST_FRAMES = 13U; +const uint8_t MAX_SYNC_LOST_FRAMES = 26U; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -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,7 +101,8 @@ 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) m_bitBuffer[m_bitPtr] |= 0x01U; @@ -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 +} diff --git a/DMRSlotRX.h b/DMRSlotRX.h index 98acb4e..f75fae7 100644 --- a/DMRSlotRX.h +++ b/DMRSlotRX.h @@ -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[1900U]; 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 - diff --git a/DMRTX.cpp b/DMRTX.cpp index c10ac2a..9b168ee 100644 --- a/DMRTX.cpp +++ b/DMRTX.cpp @@ -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,23 +18,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// #define WANT_DEBUG - #include "Config.h" #include "Globals.h" #include "DMRSlotType.h" -// Generated using rcosdesign(0.2, 4, 10, 'sqrt') in MATLAB -static q15_t DMR_C4FSK_FILTER[] = {486, 39, -480, -1022, -1526, -1928, -2164, -2178, -1927, -1384, -548, 561, 1898, 3399, 4980, 6546, 7999, 9246, 10202, 10803, 11008, 10803, 10202, 9246, - 7999, 6546, 4980, 3399, 1898, 561, -548, -1384, -1927, -2178, -2164, -1928, -1526, -1022, -480, 39, 486, 0}; -const uint16_t DMR_C4FSK_FILTER_LEN = 42U; +// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179, + -1548, -1769, -1795, -1597, -1172, -544, 237, 1092, 1927, 2637, + 3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739, + -6442, -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822, + 19484, 23810, 27520, 30367, 32156, 32767, 32156, 30367, 27520, + 23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735, + -6483, -6442, -5739, -4544, -3043, -1431, 116, 1447, 2454, + 3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, -1597, + -1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10 +const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t DMR_LEVELA[] = { 395, 395, 395, 395, 395, 395, 395, 395, 395, 395}; -const q15_t DMR_LEVELB[] = { 131, 131, 131, 131, 131, 131, 131, 131, 131, 131}; -const q15_t DMR_LEVELC[] = {-131, -131, -131, -131, -131, -131, -131, -131, -131, -131}; -const q15_t DMR_LEVELD[] = {-395, -395, -395, -395, -395, -395, -395, -395, -395, -395}; +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, @@ -53,6 +59,9 @@ 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; +const uint32_t ABORT_COUNT = 6U; + CDMRTX::CDMRTX() : m_fifo(), m_modFilter(), @@ -66,20 +75,25 @@ m_markBuffer(), m_poBuffer(), m_poLen(0U), m_poPtr(0U), -m_count(0U), +m_frameCount(0U), +m_abortCount(), m_abort() { - ::memset(m_modState, 0x00U, 90U * 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); m_abort[0U] = false; m_abort[1U] = false; + + m_abortCount[0U] = 0U; + m_abortCount[1U] = 0U; } void CDMRTX::process() @@ -207,10 +221,12 @@ uint8_t CDMRTX::writeAbort(const uint8_t* data, uint8_t length) switch (data[0U]) { case 1U: + m_abortCount[0U] = 0U; m_abort[0U] = true; return 0U; case 2U: + m_abortCount[1U] = 0U; m_abort[1U] = true; return 0U; @@ -223,7 +239,9 @@ void CDMRTX::setStart(bool start) { m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE; - m_count = 0U; + m_frameCount = 0U; + m_abortCount[0U] = 0U; + m_abortCount[1U] = 0U; m_abort[0U] = false; m_abort[1U] = false; @@ -232,80 +250,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() >= DMR_FRAME_LENGTH_BYTES && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) { for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) { m_poBuffer[i] = m_fifo[slotIndex].get(); m_markBuffer[i] = MARK_NONE; @@ -325,17 +317,42 @@ void CDMRTX::createData(uint8_t slotIndex) void CDMRTX::createCal() { - for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) { - m_poBuffer[i] = 0x5FU; // +3, +3, -3, -3 pattern for deviation cal. - m_markBuffer[i] = MARK_NONE; + // 1.2 kHz sine wave generation + if (m_modemState == STATE_DMRCAL) { + for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) { + m_poBuffer[i] = 0x5FU; // +3, +3, -3, -3 pattern for deviation cal. + m_markBuffer[i] = MARK_NONE; + } + + m_poLen = DMR_FRAME_LENGTH_BYTES; + } + + // 80 Hz square wave generation + if (m_modemState == STATE_LFCAL) { + for (unsigned int i = 0U; i < 7U; i++) { + m_poBuffer[i] = 0x55U; // +3, +3, ... pattern + m_markBuffer[i] = MARK_NONE; + } + + m_poBuffer[7U] = 0x5FU; // +3, +3, -3, -3 pattern + + for (unsigned int i = 8U; i < 15U; i++) { + m_poBuffer[i] = 0xFFU; // -3, -3, ... pattern + m_markBuffer[i] = MARK_NONE; + } + + m_poLen = 15U; } - m_poLen = DMR_FRAME_LENGTH_BYTES; m_poPtr = 0U; } void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex) { + m_frameCount++; + m_abortCount[0U]++; + m_abortCount[1U]++; + if (m_cachPtr >= 12U) m_cachPtr = 0U; @@ -351,7 +368,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; @@ -387,3 +406,17 @@ void CDMRTX::setColorCode(uint8_t colorCode) slotType.encode(colorCode, DT_IDLE, m_idle); } +void CDMRTX::resetFifo1() +{ + m_fifo[0U].reset(); +} + +void CDMRTX::resetFifo2() +{ + m_fifo[1U].reset(); +} + +uint32_t CDMRTX::getFrameCount() +{ + return m_frameCount; +} diff --git a/DMRTX.h b/DMRTX.h index 3e94e2c..1e2f617 100644 --- a/DMRTX.h +++ b/DMRTX.h @@ -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 @@ -48,27 +48,32 @@ public: void setCal(bool start); void process(); - - uint16_t getSpace1() const; - uint16_t getSpace2() const; + + void resetFifo1(); + void resetFifo2(); + uint32_t getFrameCount(); + + 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[90U]; // 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; + uint32_t m_abortCount[2U]; + bool m_abort[2U]; void createData(uint8_t slotIndex); void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex); diff --git a/DStarDefines.h b/DStarDefines.h index 7a7d8ec..0d5637d 100644 --- a/DStarDefines.h +++ b/DStarDefines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2015,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 @@ -19,30 +19,50 @@ #if !defined(DSTARDEFINES_H) #define DSTARDEFINES_H -const unsigned int DSTAR_RADIO_BIT_LENGTH = 10U; // At 48 kHz sample rate +const unsigned int DSTAR_RADIO_SYMBOL_LENGTH = 10U; // At 48 kHz sample rate -const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U; -const unsigned int DSTAR_HEADER_LENGTH_BITS = DSTAR_HEADER_LENGTH_BYTES * 8U; +const unsigned int DSTAR_HEADER_LENGTH_BYTES = 41U; -const unsigned int DSTAR_FEC_SECTION_LENGTH_BYTES = 83U; -const unsigned int DSTAR_FEC_SECTION_LENGTH_BITS = 660U; +const unsigned int DSTAR_FEC_SECTION_LENGTH_BYTES = 83U; +const unsigned int DSTAR_FEC_SECTION_LENGTH_SYMBOLS = 660U; +const unsigned int DSTAR_FEC_SECTION_LENGTH_SAMPLES = DSTAR_FEC_SECTION_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH; -const unsigned int DSTAR_DATA_LENGTH_BYTES = 12U; -const unsigned int DSTAR_DATA_LENGTH_BITS = DSTAR_DATA_LENGTH_BYTES * 8U; +const unsigned int DSTAR_DATA_LENGTH_BYTES = 12U; +const unsigned int DSTAR_DATA_LENGTH_SYMBOLS = DSTAR_DATA_LENGTH_BYTES * 8U; +const unsigned int DSTAR_DATA_LENGTH_SAMPLES = DSTAR_DATA_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH; -const uint8_t DSTAR_EOT_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -const unsigned int DSTAR_EOT_LENGTH_BYTES = 6U; -const unsigned int DSTAR_EOT_LENGTH_BITS = DSTAR_EOT_LENGTH_BYTES * 8U; +const unsigned int DSTAR_END_SYNC_LENGTH_BYTES = 6U; +const unsigned int DSTAR_END_SYNC_LENGTH_BITS = DSTAR_END_SYNC_LENGTH_BYTES * 8U; -const uint8_t DSTAR_DATA_SYNC_LENGTH_BYTES = 3U; -const uint8_t DSTAR_DATA_SYNC_LENGTH_BITS = DSTAR_DATA_SYNC_LENGTH_BYTES * 8U; +const unsigned int DSTAR_FRAME_SYNC_LENGTH_BYTES = 3U; +const unsigned int DSTAR_FRAME_SYNC_LENGTH_SYMBOLS = DSTAR_FRAME_SYNC_LENGTH_BYTES * 8U; +const unsigned int DSTAR_FRAME_SYNC_LENGTH_SAMPLES = DSTAR_FRAME_SYNC_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH; -const uint8_t DSTAR_DATA_SYNC_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16}; +const unsigned int DSTAR_DATA_SYNC_LENGTH_BYTES = 3U; +const unsigned int DSTAR_DATA_SYNC_LENGTH_SYMBOLS = DSTAR_DATA_SYNC_LENGTH_BYTES * 8U; +const unsigned int DSTAR_DATA_SYNC_LENGTH_SAMPLES = DSTAR_DATA_SYNC_LENGTH_SYMBOLS * DSTAR_RADIO_SYMBOL_LENGTH; -const uint8_t DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U; -const uint8_t DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U; +const uint8_t DSTAR_DATA_SYNC_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8, 0x55, 0x2D, 0x16}; -const uint8_t DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U}; +// D-Star bit order version of 0x55 0x6E 0x0A +const uint32_t DSTAR_FRAME_SYNC_DATA = 0x00557650U; +const uint32_t DSTAR_FRAME_SYNC_MASK = 0x00FFFFFFU; +const bool DSTAR_FRAME_SYNC_SYMBOLS[] = {false, true, false, true, false, true, false, true, false, true, true, true, false, true, true, false, false, true, false, true, false, false, false, false}; + +// D-Star bit order version of 0x55 0x2D 0x16 +const uint32_t DSTAR_DATA_SYNC_DATA = 0x00AAB468U; +const uint32_t DSTAR_DATA_SYNC_MASK = 0x00FFFFFFU; +const bool DSTAR_DATA_SYNC_SYMBOLS[] = {true, false, true, false, true, false, true, false, true, false, true, true, false, true, false, false, false, true, true, false, true, false, false, false}; + +// D-Star bit order version of 0x55 0x55 0xC8 0x7A +const uint32_t DSTAR_END_SYNC_DATA = 0xAAAA135EU; +const uint32_t DSTAR_END_SYNC_MASK = 0xFFFFFFFFU; +const uint8_t DSTAR_END_SYNC_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +const uint8_t DSTAR_SLOW_DATA_TYPE_TEXT = 0x40U; +const uint8_t DSTAR_SLOW_DATA_TYPE_HEADER = 0x50U; + +const uint8_t DSTAR_SCRAMBLER_BYTES[] = {0x70U, 0x4FU, 0x93U}; #endif diff --git a/DStarRX.cpp b/DStarRX.cpp index 81268cb..e8e8b0d 100644 --- a/DStarRX.cpp +++ b/DStarRX.cpp @@ -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,17 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// #define WANT_DEBUG - #include "Config.h" #include "Globals.h" #include "DStarRX.h" #include "Utils.h" -const unsigned int BUFFER_LENGTH = 200U; - -const uint32_t PLLMAX = 0x10000U; -const uint32_t PLLINC = PLLMAX / DSTAR_RADIO_BIT_LENGTH; -const uint32_t INC = PLLINC / 32U; - -const unsigned int MAX_SYNC_BITS = 50U * DSTAR_DATA_LENGTH_BITS; - -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; +const unsigned int MAX_FRAMES = 150U; // D-Star bit order version of 0x55 0x55 0x6E 0x0A const uint32_t FRAME_SYNC_DATA = 0x00557650U; const uint32_t FRAME_SYNC_MASK = 0x00FFFFFFU; -const uint8_t FRAME_SYNC_ERRS = 2U; +const uint8_t FRAME_SYNC_ERRS = 1U; // D-Star bit order version of 0x55 0x2D 0x16 const uint32_t DATA_SYNC_DATA = 0x00AAB468U; @@ -50,7 +37,7 @@ const uint8_t DATA_SYNC_ERRS = 2U; // D-Star bit order version of 0x55 0x55 0xC8 0x7A const uint32_t END_SYNC_DATA = 0xAAAA135EU; const uint32_t END_SYNC_MASK = 0xFFFFFFFFU; -const uint8_t END_SYNC_ERRS = 3U; +const uint8_t END_SYNC_ERRS = 1U; const uint8_t BIT_MASK_TABLE0[] = {0x7FU, 0xBFU, 0xDFU, 0xEFU, 0xF7U, 0xFBU, 0xFDU, 0xFEU}; const uint8_t BIT_MASK_TABLE1[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -244,14 +231,24 @@ const uint16_t CCITT_TABLE[] = { 0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U, 0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U}; +const uint16_t NOENDPTR = 9999U; + CDStarRX::CDStarRX() : -m_pll(0U), -m_prev(false), m_rxState(DSRXS_NONE), -m_patternBuffer(0x00U), -m_rxBuffer(), -m_rxBufferBits(0U), -m_dataBits(0U), +m_bitBuffer(), +m_headerBuffer(), +m_dataBuffer(), +m_bitPtr(0U), +m_headerPtr(0U), +m_dataPtr(0U), +m_startPtr(NOENDPTR), +m_syncPtr(NOENDPTR), +m_minSyncPtr(NOENDPTR), +m_maxSyncPtr(NOENDPTR), +m_maxFrameCorr(0), +m_maxDataCorr(0), +m_frameCount(0U), +m_countdown(0U), m_mar(0U), m_pathMetric(), m_pathMemory0(), @@ -259,142 +256,146 @@ m_pathMemory1(), m_pathMemory2(), m_pathMemory3(), m_fecOutput(), -m_samples(), -m_samplesPtr(0U) +m_rssiAccum(0U), +m_rssiCount(0U) { } void CDStarRX::reset() { - m_pll = 0U; - m_prev = false; - m_rxState = DSRXS_NONE; - m_patternBuffer = 0x00U; - m_rxBufferBits = 0U; - m_dataBits = 0U; - m_samplesPtr = 0U; + m_rxState = DSRXS_NONE; + m_headerPtr = 0U; + m_dataPtr = 0U; + m_bitPtr = 0U; + m_maxFrameCorr = 0; + m_maxDataCorr = 0; + m_startPtr = NOENDPTR; + m_syncPtr = NOENDPTR; + m_minSyncPtr = NOENDPTR; + m_maxSyncPtr = NOENDPTR; + m_frameCount = 0U; + m_countdown = 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]; + m_rssiAccum += rssi[i]; + m_rssiCount++; - bool bit = samples[i] < THRESHOLD; + q15_t sample = samples[i]; - if (bit != m_prev) { - if (m_pll < (PLLMAX / 2U)) - m_pll += INC; - else - m_pll -= INC; + m_bitBuffer[m_bitPtr] <<= 1; + if (sample < 0) + m_bitBuffer[m_bitPtr] |= 0x01U; + + m_dataBuffer[m_dataPtr] = sample; + + switch (m_rxState) { + case DSRXS_HEADER: + processHeader(sample); + break; + case DSRXS_DATA: + processData(); + break; + default: + processNone(sample); + break; } - m_prev = bit; + m_dataPtr++; + if (m_dataPtr >= DSTAR_DATA_LENGTH_SAMPLES) + m_dataPtr = 0U; - m_pll += PLLINC; - - if (m_pll >= PLLMAX) { - m_pll -= PLLMAX; - - switch (m_rxState) { - case DSRXS_NONE: - processNone(bit); - break; - case DSRXS_HEADER: - processHeader(bit); - break; - case DSRXS_DATA: - processData(bit); - break; - default: - break; - } - } - - m_samplesPtr++; - if (m_samplesPtr >= DSTAR_DATA_SYNC_LENGTH_BITS) - m_samplesPtr = 0U; + m_bitPtr++; + if (m_bitPtr >= DSTAR_RADIO_SYMBOL_LENGTH) + m_bitPtr = 0U; } } -void CDStarRX::processNone(bool bit) +void CDStarRX::processNone(q15_t sample) { - m_patternBuffer <<= 1; - if (bit) - m_patternBuffer |= 0x01U; - // Fuzzy matching of the frame sync sequence - if (countBits32((m_patternBuffer & FRAME_SYNC_MASK) ^ FRAME_SYNC_DATA) <= FRAME_SYNC_ERRS) { - DEBUG1("DStarRX: found frame sync in None"); + bool ret = correlateFrameSync(); + if (ret) { + m_countdown = 5U; + + m_headerBuffer[m_headerPtr] = sample; + m_headerPtr++; - ::memset(m_rxBuffer, 0x00U, DSTAR_FEC_SECTION_LENGTH_BYTES); - m_rxBufferBits = 0U; + m_rssiAccum = 0U; + m_rssiCount = 0U; m_rxState = DSRXS_HEADER; + return; } - // Exact matching of the data sync bit sequence - if (countBits32((m_patternBuffer & DATA_SYNC_MASK) ^ DATA_SYNC_DATA) == 0U) { + // Fuzzy matching of the data sync bit sequence + ret = correlateDataSync(); + if (ret) { DEBUG1("DStarRX: found data sync in None"); io.setDecode(true); io.setADCDetection(true); - serial.writeDStarData(DSTAR_DATA_SYNC_BYTES, DSTAR_DATA_LENGTH_BYTES); - - ::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U); - m_rxBufferBits = 0U; - - m_dataBits = 0U; - m_rxState = DSRXS_DATA; - return; + m_rxState = DSRXS_DATA; } } -void CDStarRX::processHeader(bool bit) +void CDStarRX::processHeader(q15_t sample) { - m_patternBuffer <<= 1; - if (bit) - m_patternBuffer |= 0x01U; + if (m_countdown > 0U) { + correlateFrameSync(); + m_countdown--; + } - WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit); - m_rxBufferBits++; + m_headerBuffer[m_headerPtr] = sample; + m_headerPtr++; // A full FEC header - if (m_rxBufferBits == DSTAR_FEC_SECTION_LENGTH_BITS) { - // Process the scrambling, interleaving and FEC, then return if the chcksum was correct - unsigned char header[DSTAR_HEADER_LENGTH_BYTES]; - bool ok = rxHeader(m_rxBuffer, header); - if (ok) { + if (m_headerPtr == (DSTAR_FEC_SECTION_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH)) { + uint8_t buffer[DSTAR_FEC_SECTION_LENGTH_BYTES]; + samplesToBits(m_headerBuffer, DSTAR_RADIO_SYMBOL_LENGTH, DSTAR_FEC_SECTION_LENGTH_SYMBOLS, buffer, DSTAR_FEC_SECTION_LENGTH_SAMPLES); + + // Process the scrambling, interleaving and FEC, then return true if the chcksum was correct + uint8_t header[DSTAR_HEADER_LENGTH_BYTES]; + bool ok = rxHeader(buffer, header); + if (!ok) { + // The checksum failed, return to looking for syncs + m_rxState = DSRXS_NONE; + m_maxFrameCorr = 0; + m_maxDataCorr = 0; + } else { io.setDecode(true); io.setADCDetection(true); - serial.writeDStarHeader(header, DSTAR_HEADER_LENGTH_BYTES); - - ::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U); - m_rxBufferBits = 0U; - - m_rxState = DSRXS_DATA; - m_dataBits = SYNC_POS - DSTAR_DATA_LENGTH_BITS + 1U; - } else { - // The checksum failed, return to looking for syncs - m_rxState = DSRXS_NONE; + writeRSSIHeader(header); } } + + // Ready to start the first data section + if (m_headerPtr == (DSTAR_FEC_SECTION_LENGTH_SAMPLES + 2U * DSTAR_RADIO_SYMBOL_LENGTH)) { + m_frameCount = 0U; + m_dataPtr = 0U; + + m_startPtr = 952U; + m_syncPtr = 942U; + m_maxSyncPtr = 944U; + m_minSyncPtr = 940U; + + DEBUG5("DStarRX: calc start/sync/max/min", m_startPtr, m_syncPtr, m_maxSyncPtr, m_minSyncPtr); + + m_rxState = DSRXS_DATA; + } } -void CDStarRX::processData(bool bit) +void CDStarRX::processData() { - m_patternBuffer <<= 1; - if (bit) - m_patternBuffer |= 0x01U; - - WRITE_BIT2(m_rxBuffer, m_rxBufferBits, bit); - m_rxBufferBits++; - // Fuzzy matching of the end frame sequences - if (countBits32((m_patternBuffer & END_SYNC_MASK) ^ END_SYNC_DATA) <= END_SYNC_ERRS) { + if (countBits32((m_bitBuffer[m_bitPtr] & DSTAR_END_SYNC_MASK) ^ DSTAR_END_SYNC_DATA) <= END_SYNC_ERRS) { DEBUG1("DStarRX: Found end sync in Data"); io.setDecode(false); @@ -402,44 +403,24 @@ void CDStarRX::processData(bool bit) serial.writeDStarEOT(); + m_maxFrameCorr = 0; + m_maxDataCorr = 0; + m_rxState = DSRXS_NONE; return; } // Fuzzy matching of the data sync bit sequence - 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; - } + if (m_minSyncPtr < m_maxSyncPtr) { + if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr) + correlateDataSync(); + } else { + if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr) + correlateDataSync(); } - // Check to see if the sync is arriving late - if (m_dataBits == SYNC_POS) { - for (uint8_t i = 1U; i <= 3U; i++) { - uint32_t syncMask = DATA_SYNC_MASK >> i; - uint32_t syncData = DATA_SYNC_DATA >> i; - if (countBits32((m_patternBuffer & syncMask) ^ syncData) <= DATA_SYNC_ERRS) { - DEBUG2("DStarRX: found data sync in Data, late", i); - m_rxBufferBits -= i; - m_dataBits -= i; - break; - } - } - } - - m_dataBits++; - // We've not seen a data sync for too long, signal RXLOST and change to RX_NONE - if (m_dataBits >= MAX_SYNC_BITS) { + if (m_frameCount >= MAX_FRAMES) { DEBUG1("DStarRX: data sync timed out, lost lock"); io.setDecode(false); @@ -447,23 +428,176 @@ void CDStarRX::processData(bool bit) serial.writeDStarLost(); + m_maxFrameCorr = 0; + m_maxDataCorr = 0; + m_rxState = DSRXS_NONE; return; } - // Send a data frame to the host if the required number of bits have been received, or if a data sync has been seen - if (m_rxBufferBits == DSTAR_DATA_LENGTH_BITS) { - if (syncSeen) { - m_rxBuffer[9U] = DSTAR_DATA_SYNC_BYTES[9U]; - m_rxBuffer[10U] = DSTAR_DATA_SYNC_BYTES[10U]; - m_rxBuffer[11U] = DSTAR_DATA_SYNC_BYTES[11U]; + // Send a data frame to the host if the required number of bits have been received + if (m_dataPtr == m_maxSyncPtr) { + uint8_t buffer[DSTAR_DATA_LENGTH_BYTES + 2U]; + samplesToBits(m_dataBuffer, m_startPtr, DSTAR_DATA_LENGTH_SYMBOLS, buffer, DSTAR_DATA_LENGTH_SAMPLES); + + if ((m_frameCount % 21U) == 0U) { + if (m_frameCount == 0U) { + buffer[9U] = DSTAR_DATA_SYNC_BYTES[9U]; + buffer[10U] = DSTAR_DATA_SYNC_BYTES[10U]; + buffer[11U] = DSTAR_DATA_SYNC_BYTES[11U]; + DEBUG5("DStarRX: found start/sync/max/min", m_startPtr, m_syncPtr, m_maxSyncPtr, m_minSyncPtr); + } + + writeRSSIData(buffer); + } else { + serial.writeDStarData(buffer, DSTAR_DATA_LENGTH_BYTES); } - serial.writeDStarData(m_rxBuffer, DSTAR_DATA_LENGTH_BYTES); + m_frameCount++; - // Start the next frame - ::memset(m_rxBuffer, 0x00U, DSTAR_DATA_LENGTH_BYTES + 2U); - m_rxBufferBits = 0U; + m_maxFrameCorr = 0; + m_maxDataCorr = 0; + } +} + +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::correlateFrameSync() +{ + if (countBits32((m_bitBuffer[m_bitPtr] & DSTAR_FRAME_SYNC_MASK) ^ DSTAR_FRAME_SYNC_DATA) <= FRAME_SYNC_ERRS) { + uint16_t ptr = m_dataPtr + DSTAR_DATA_LENGTH_SAMPLES - DSTAR_FRAME_SYNC_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH; + if (ptr >= DSTAR_DATA_LENGTH_SAMPLES) + ptr -= DSTAR_DATA_LENGTH_SAMPLES; + + q31_t corr = 0; + + for (uint8_t i = 0U; i < DSTAR_FRAME_SYNC_LENGTH_SYMBOLS; i++) { + q15_t val = m_dataBuffer[ptr]; + + if (DSTAR_FRAME_SYNC_SYMBOLS[i]) + corr -= val; + else + corr += val; + + ptr += DSTAR_RADIO_SYMBOL_LENGTH; + if (ptr >= DSTAR_DATA_LENGTH_SAMPLES) + ptr -= DSTAR_DATA_LENGTH_SAMPLES; + } + + if (corr > m_maxFrameCorr) { + m_maxFrameCorr = corr; + m_headerPtr = 0U; + return true; + } + } + + return false; +} + +bool CDStarRX::correlateDataSync() +{ + uint8_t maxErrs = 0U; + if (m_rxState == DSRXS_DATA) + maxErrs = DATA_SYNC_ERRS; + + if (countBits32((m_bitBuffer[m_bitPtr] & DSTAR_DATA_SYNC_MASK) ^ DSTAR_DATA_SYNC_DATA) <= maxErrs) { + uint16_t ptr = m_dataPtr + DSTAR_DATA_LENGTH_SAMPLES - DSTAR_DATA_SYNC_LENGTH_SAMPLES + DSTAR_RADIO_SYMBOL_LENGTH; + if (ptr >= DSTAR_DATA_LENGTH_SAMPLES) + ptr -= DSTAR_DATA_LENGTH_SAMPLES; + + q31_t corr = 0; + + for (uint8_t i = 0U; i < DSTAR_DATA_SYNC_LENGTH_SYMBOLS; i++) { + q15_t val = m_dataBuffer[ptr]; + + if (DSTAR_DATA_SYNC_SYMBOLS[i]) + corr -= val; + else + corr += val; + + ptr += DSTAR_RADIO_SYMBOL_LENGTH; + if (ptr >= DSTAR_DATA_LENGTH_SAMPLES) + ptr -= DSTAR_DATA_LENGTH_SAMPLES; + } + + if (corr > m_maxDataCorr) { + m_maxDataCorr = corr; + m_frameCount = 0U; + + m_syncPtr = m_dataPtr; + + m_startPtr = m_dataPtr + DSTAR_RADIO_SYMBOL_LENGTH; + if (m_startPtr >= DSTAR_DATA_LENGTH_SAMPLES) + m_startPtr -= DSTAR_DATA_LENGTH_SAMPLES; + + m_maxSyncPtr = m_syncPtr + 1U; + if (m_maxSyncPtr >= DSTAR_DATA_LENGTH_SAMPLES) + m_maxSyncPtr -= DSTAR_DATA_LENGTH_SAMPLES; + + m_minSyncPtr = m_syncPtr + DSTAR_DATA_LENGTH_SAMPLES - 1U; + if (m_minSyncPtr >= DSTAR_DATA_LENGTH_SAMPLES) + m_minSyncPtr -= DSTAR_DATA_LENGTH_SAMPLES; + + return true; + } + } + + return false; +} + +void CDStarRX::samplesToBits(const q15_t* inBuffer, uint16_t start, uint16_t count, uint8_t* outBuffer, uint16_t limit) +{ + for (uint16_t i = 0U; i < count; i++) { + q15_t sample = inBuffer[start]; + + if (sample < 0) + WRITE_BIT2(outBuffer, i, true); + else + WRITE_BIT2(outBuffer, i, false); + + start += DSTAR_RADIO_SYMBOL_LENGTH; + if (start >= limit) + start -= limit; } } @@ -682,4 +816,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]; } - diff --git a/DStarRX.h b/DStarRX.h index dd63ed3..50ddf79 100644 --- a/DStarRX.h +++ b/DStarRX.h @@ -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,18 +32,26 @@ 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(); private: - uint32_t m_pll; - bool m_prev; DSRX_STATE m_rxState; - uint32_t m_patternBuffer; - uint8_t m_rxBuffer[100U]; - unsigned int m_rxBufferBits; - unsigned int m_dataBits; + uint32_t m_bitBuffer[DSTAR_RADIO_SYMBOL_LENGTH]; + q15_t m_headerBuffer[DSTAR_FEC_SECTION_LENGTH_SAMPLES + 2U * DSTAR_RADIO_SYMBOL_LENGTH]; + q15_t m_dataBuffer[DSTAR_DATA_LENGTH_SAMPLES]; + uint16_t m_bitPtr; + uint16_t m_headerPtr; + uint16_t m_dataPtr; + uint16_t m_startPtr; + uint16_t m_syncPtr; + uint16_t m_minSyncPtr; + uint16_t m_maxSyncPtr; + q31_t m_maxFrameCorr; + q31_t m_maxDataCorr; + uint16_t m_frameCount; + uint8_t m_countdown; unsigned int m_mar; int m_pathMetric[4U]; unsigned int m_pathMemory0[42U]; @@ -51,12 +59,17 @@ 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; - - void processNone(bool bit); - void processHeader(bool bit); - void processData(bool bit); + uint32_t m_rssiAccum; + uint16_t m_rssiCount; + + void processNone(q15_t sample); + void processHeader(q15_t sample); + void processData(); + bool correlateFrameSync(); + bool correlateDataSync(); + void samplesToBits(const q15_t* inBuffer, uint16_t start, uint16_t count, uint8_t* outBuffer, uint16_t limit); + 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); diff --git a/DStarTX.cpp b/DStarTX.cpp index 08c466a..8447923 100644 --- a/DStarTX.cpp +++ b/DStarTX.cpp @@ -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,14 @@ const uint8_t BIT_SYNC = 0xAAU; const uint8_t FRAME_SYNC[] = {0xEAU, 0xA6U, 0x00U}; -// Generated using gaussfir(0.5, 4, 10) in MATLAB -static q15_t DSTAR_GMSK_FILTER[] = {1, 4, 15, 52, 151, 380, 832, 1579, 2599, 3710, 4594, 4933, 4594, 3710, 2599, 1579, 832, 380, 151, 52, 15, 4, 1, 0}; -const uint16_t DSTAR_GMSK_FILTER_LEN = 24U; +// Generated using gaussfir(0.35, 1, 10) in MATLAB +static q15_t GAUSSIAN_0_35_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1001, 1942, 3514, 5930, + 9333, 13699, 18751, 23938, 28499, 31644, 32767, 31644, + 28499, 23938, 18751, 13699, 9333, 5930, 3514, 1942, 1001}; // numTaps = 30, L = 10 +const uint16_t GAUSSIAN_0_35_FILTER_PHASE_LEN = 3U; // phaseLength = numTaps/L -const q15_t DSTAR_LEVEL0[] = {-808, -808, -808, -808, -808, -808, -808, -808, -808, -808}; -const q15_t DSTAR_LEVEL1[] = { 808, 808, 808, 808, 808, 808, 808, 808, 808, 808}; +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 +196,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, 80U * sizeof(q15_t)); + ::memset(m_modState, 0x00U, 15U * 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_SYMBOL_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 +215,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 +240,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(); @@ -258,8 +254,8 @@ void CDStarTX::process() m_buffer.get(); for (uint8_t j = 0U; j < 3U; j++) { - for (uint8_t i = 0U; i < DSTAR_EOT_LENGTH_BYTES; i++) - m_poBuffer[m_poLen++] = DSTAR_EOT_BYTES[i]; + for (uint8_t i = 0U; i < DSTAR_END_SYNC_LENGTH_BYTES; i++) + m_poBuffer[m_poLen++] = DSTAR_END_SYNC_BYTES[i]; } m_poPtr = 0U; @@ -268,11 +264,11 @@ void CDStarTX::process() if (m_poLen > 0U) { uint16_t space = io.getSpace(); - while (space > (8U * DSTAR_RADIO_BIT_LENGTH)) { + while (space > (8U * DSTAR_RADIO_SYMBOL_LENGTH)) { uint8_t c = m_poBuffer[m_poPtr++]; writeByte(c); - space -= 8U * DSTAR_RADIO_BIT_LENGTH; + space -= 8U * DSTAR_RADIO_SYMBOL_LENGTH; if (m_poPtr >= m_poLen) { m_poPtr = 0U; @@ -417,50 +413,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_SYMBOL_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; - - // 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); + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 8U); + + io.write(STATE_DSTAR, outBuffer, DSTAR_RADIO_SYMBOL_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); } diff --git a/DStarTX.h b/DStarTX.h index 2dc3360..0424761 100644 --- a/DStarTX.h +++ b/DStarTX.h @@ -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[80U]; // 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[15U]; // blockSize + phaseLength - 1, 8 + 3 - 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); diff --git a/Debug.h b/Debug.h index 667856d..599fbfd 100644 --- a/Debug.h +++ b/Debug.h @@ -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 diff --git a/GitVersion.h b/GitVersion.h new file mode 100644 index 0000000..c712f93 --- /dev/null +++ b/GitVersion.h @@ -0,0 +1 @@ +#define GITVERSION "0000000" diff --git a/Globals.h b/Globals.h index b0e29bc..e7fa8a1 100644 --- a/Globals.h +++ b/Globals.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,15 +19,22 @@ #if !defined(GLOBALS_H) #define GLOBALS_H -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) #include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" +#include "STM32Utils.h" #else #include #endif -#if defined(__SAM3X8E__) +#if defined(__SAM3X8E__) || defined(STM32F105xC) #define ARM_MATH_CM3 -#elif defined(STM32F4XX) || defined(STM32F4) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#elif defined(STM32F7XX) +#define ARM_MATH_CM7 +#elif defined(STM32F4XX) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) #define ARM_MATH_CM4 #else #error "Unknown processor type" @@ -41,8 +48,15 @@ enum MMDVM_STATE { STATE_DMR = 2, STATE_YSF = 3, STATE_P25 = 4, + STATE_NXDN = 5, // Dummy states start at 90 + STATE_NXDNCAL1K = 91, + STATE_DMRDMO1K = 92, + STATE_P25CAL1K = 93, + STATE_DMRCAL1K = 94, + STATE_LFCAL = 95, + STATE_RSSICAL = 96, STATE_CWID = 97, STATE_DMRCAL = 98, STATE_DSTARCAL = 99 @@ -60,9 +74,14 @@ enum MMDVM_STATE { #include "YSFTX.h" #include "P25RX.h" #include "P25TX.h" +#include "NXDNRX.h" +#include "NXDNTX.h" #include "CalDStarRX.h" #include "CalDStarTX.h" #include "CalDMR.h" +#include "CalP25.h" +#include "CalNXDN.h" +#include "CalRSSI.h" #include "CWIdTX.h" #include "Debug.h" #include "IO.h" @@ -73,8 +92,8 @@ const uint8_t MARK_NONE = 0x00U; const uint16_t RX_BLOCK_SIZE = 2U; -const uint16_t TX_RINGBUFFER_SIZE = 500U; -const uint16_t RX_RINGBUFFER_SIZE = 600U; +const uint16_t TX_RINGBUFFER_SIZE = 1000U; +const uint16_t RX_RINGBUFFER_SIZE = 1200U; extern MMDVM_STATE m_modemState; @@ -82,15 +101,13 @@ extern bool m_dstarEnable; extern bool m_dmrEnable; extern bool m_ysfEnable; extern bool m_p25Enable; +extern bool m_nxdnEnable; 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; @@ -110,9 +127,15 @@ extern CYSFTX ysfTX; extern CP25RX p25RX; extern CP25TX p25TX; +extern CNXDNRX nxdnRX; +extern CNXDNTX nxdnTX; + extern CCalDStarRX calDStarRX; extern CCalDStarTX calDStarTX; extern CCalDMR calDMR; +extern CCalP25 calP25; +extern CCalNXDN calNXDN; +extern CCalRSSI calRSSI; extern CCWIdTX cwIdTX; diff --git a/IO.cpp b/IO.cpp index a861e46..9ef3b7a 100644 --- a/IO.cpp +++ b/IO.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -18,20 +18,44 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// #define WANT_DEBUG - #include "Config.h" #include "Globals.h" #include "IO.h" -// Generated using rcosdesign(0.2, 4, 10, 'sqrt') in MATLAB -static q15_t C4FSK_FILTER[] = {486, 39, -480, -1022, -1526, -1928, -2164, -2178, -1927, -1384, -548, 561, 1898, 3399, 4980, 6546, 7999, 9246, 10202, 10803, 11008, 10803, 10202, 9246, - 7999, 6546, 4980, 3399, 1898, 561, -548, -1384, -1927, -2178, -2164, -1928, -1526, -1022, -480, 39, 486, 0}; -const uint16_t C4FSK_FILTER_LEN = 42U; +// Generated using [b, a] = butter(1, 0.0005) in MATLAB +static q31_t DC_FILTER[] = {1685306, 0, 1685306, 0, 2144113034, 0}; // {b0, 0, b1, b2, -a1, -a2} +const uint32_t DC_FILTER_STAGES = 1U; // One Biquad stage + +// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {284, 198, 73, -78, -240, -393, -517, -590, -599, -533, -391, -181, 79, 364, 643, 880, 1041, 1097, 1026, 819, + 483, 39, -477, -1016, -1516, -1915, -2150, -2164, -1914, -1375, -545, 557, 1886, 3376, 4946, 6502, 7946, 9184, + 10134, 10731, 10935, 10731, 10134, 9184, 7946, 6502, 4946, 3376, 1886, 557, -545, -1375, -1914, -2164, -2150, + -1915, -1516, -1016, -477, 39, 483, 819, 1026, 1097, 1041, 880, 643, 364, 79, -181, -391, -533, -599, -590, + -517, -393, -240, -78, 73, 198, 284, 0}; +const uint16_t RRC_0_2_FILTER_LEN = 82U; + +// Generated using rcosdesign(0.2, 8, 20, 'sqrt') in MATLAB +static q15_t NXDN_0_2_FILTER[] = {201, 174, 140, 99, 52, 0, -55, -112, -170, -226, -278, -325, -365, -397, -417, -427, -424, -407, -377, -333, -277, -208, + -128, -40, 56, 156, 258, 358, 455, 544, 622, 687, 736, 766, 775, 762, 725, 664, 579, 471, 342, 193, 27, -151, -338, -528, + -718, -901, -1072, -1225, -1354, -1454, -1520, -1547, -1530, -1466, -1353, -1189, -972, -704, -385, -18, 394, 846, 1333, + 1850, 2388, 2940, 3498, 4053, 4598, 5122, 5619, 6079, 6494, 6859, 7166, 7410, 7588, 7696, 7732, 7696, 7588, 7410, 7166, + 6859, 6494, 6079, 5619, 5122, 4598, 4053, 3498, 2940, 2388, 1850, 1333, 846, 394, -18, -385, -704, -972, -1189, -1353, + -1466, -1530, -1547, -1520, -1454, -1354, -1225, -1072, -901, -718, -528, -338, -151, 27, 193, 342, 471, 579, 664, 725, + 762, 775, 766, 736, 687, 622, 544, 455, 358, 258, 156, 56, -40, -128, -208, -277, -333, -377, -407, -424, -427, -417, -397, + -365, -325, -278, -226, -170, -112, -55, 0, 52, 99, 140, 174, 201, 0}; +const uint16_t NXDN_0_2_FILTER_LEN = 162U; + +static q15_t NXDN_ISINC_FILTER[] = {790, -1085, -1073, -553, 747, 2341, 3156, 2152, -893, -4915, -7834, -7536, -3102, 4441, 12354, 17394, 17394, + 12354, 4441, -3102, -7536, -7834, -4915, -893, 2152, 3156, 2341, 747, -553, -1073, -1085, 790}; +const uint16_t NXDN_ISINC_FILTER_LEN = 32U; // Generated using gaussfir(0.5, 4, 10) in MATLAB -static q15_t GMSK_FILTER[] = {1, 4, 15, 52, 151, 380, 832, 1579, 2599, 3710, 4594, 4933, 4594, 3710, 2599, 1579, 832, 380, 151, 52, 15, 4, 1, 0}; -const uint16_t GMSK_FILTER_LEN = 24U; +//static q15_t GAUSSIAN_0_5_FILTER[] = {1, 4, 15, 52, 151, 380, 832, 1579, 2599, 3710, 4594, 4933, 4594, 3710, 2599, 1579, 832, 380, 151, 52, 15, 4, 1, 0}; +//const uint16_t GAUSSIAN_0_5_FILTER_LEN = 24U; + +// One symbol boxcar filter +static q15_t BOXCAR_FILTER[] = {6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 0, 0}; +const uint16_t BOXCAR_FILTER_LEN = 12U; const uint16_t DC_OFFSET = 2048U; @@ -40,10 +64,18 @@ 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_dcFilter(), +m_dcState(), +m_rrcFilter(), +//m_gaussianFilter(), +m_boxcarFilter(), +m_nxdnFilter(), +m_nxdnISincFilter(), +m_rrcState(), +//m_gaussianState(), +m_boxcarState(), +m_nxdnState(), +m_nxdnISincState(), m_pttInvert(false), m_rxLevel(128 * 128), m_cwIdTXLevel(128 * 128), @@ -51,27 +83,153 @@ m_dstarTXLevel(128 * 128), m_dmrTXLevel(128 * 128), m_ysfTXLevel(128 * 128), m_p25TXLevel(128 * 128), +m_nxdnTXLevel(128 * 128), +m_rxDCOffset(DC_OFFSET), +m_txDCOffset(DC_OFFSET), m_ledCount(0U), 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, 140U * sizeof(q15_t)); +// ::memset(m_gaussianState, 0x00U, 80U * sizeof(q15_t)); + ::memset(m_boxcarState, 0x00U, 60U * sizeof(q15_t)); + ::memset(m_nxdnState, 0x00U, 220U * sizeof(q15_t)); + ::memset(m_nxdnISincState, 0x00U, 60U * sizeof(q15_t)); + ::memset(m_dcState, 0x00U, 4U * sizeof(q31_t)); - m_C4FSKFilter.numTaps = C4FSK_FILTER_LEN; - m_C4FSKFilter.pState = m_C4FSKState; - m_C4FSKFilter.pCoeffs = C4FSK_FILTER; + m_dcFilter.numStages = DC_FILTER_STAGES; + m_dcFilter.pState = m_dcState; + m_dcFilter.pCoeffs = DC_FILTER; + m_dcFilter.postShift = 0; - m_GMSKFilter.numTaps = GMSK_FILTER_LEN; - m_GMSKFilter.pState = m_GMSKState; - m_GMSKFilter.pCoeffs = GMSK_FILTER; + m_rrcFilter.numTaps = RRC_0_2_FILTER_LEN; + m_rrcFilter.pState = m_rrcState; + m_rrcFilter.pCoeffs = RRC_0_2_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; + + m_nxdnFilter.numTaps = NXDN_0_2_FILTER_LEN; + m_nxdnFilter.pState = m_nxdnState; + m_nxdnFilter.pCoeffs = NXDN_0_2_FILTER; + + m_nxdnISincFilter.numTaps = NXDN_ISINC_FILTER_LEN; + m_nxdnISincFilter.pState = m_nxdnISincState; + m_nxdnISincFilter.pCoeffs = NXDN_ISINC_FILTER; initInt(); + + selfTest(); +} + +void CIO::selfTest() +{ + bool ledValue = false; + + for (uint8_t i = 0; i < 6; i++) { + ledValue = !ledValue; + + // We exclude PTT to avoid trigger the transmitter + setLEDInt(ledValue); + setCOSInt(ledValue); +#if defined(ARDUINO_MODE_PINS) + setDStarInt(ledValue); + setDMRInt(ledValue); + setYSFInt(ledValue); + setP25Int(ledValue); + setNXDNInt(ledValue); +#endif + delayInt(250); + } + +#if defined(ARDUINO_MODE_PINS) + setDStarInt(true); + setDMRInt(false); + setYSFInt(false); + setP25Int(false); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(false); + setP25Int(false); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(false); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(true); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(false); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(false); + setP25Int(false); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(false); + setYSFInt(false); + setP25Int(false); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(false); + setDMRInt(false); + setYSFInt(false); + setP25Int(false); + setNXDNInt(false); +#endif } void CIO::start() @@ -81,7 +239,6 @@ void CIO::start() startInt(); - m_count = 0U; m_started = true; setMode(); @@ -93,7 +250,7 @@ void CIO::process() if (m_started) { // Two seconds timeout if (m_watchdog >= 96000U) { - if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF) { + if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN) { if (m_modemState == STATE_DMR && m_tx) dmrTX.setStart(false); m_modemState = STATE_IDLE; @@ -128,11 +285,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; @@ -143,100 +298,146 @@ void CIO::process() if (m_detect && (sample == 0U || sample == 4095U)) m_adcOverflow++; - q15_t res1 = q15_t(sample) - DC_OFFSET; + q15_t res1 = q15_t(sample) - m_rxDCOffset; q31_t res2 = res1 * m_rxLevel; 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 defined(USE_DCBLOCKER) + q31_t q31Samples[RX_BLOCK_SIZE]; + ::arm_q15_to_q31(samples, q31Samples, RX_BLOCK_SIZE); + + q31_t dcValues[RX_BLOCK_SIZE]; + ::arm_biquad_cascade_df1_q31(&m_dcFilter, q31Samples, dcValues, RX_BLOCK_SIZE); + + q31_t dcLevel = 0; + for (uint8_t i = 0U; i < RX_BLOCK_SIZE; i++) + dcLevel += dcValues[i]; + dcLevel /= RX_BLOCK_SIZE; + + q15_t offset = q15_t(__SSAT((dcLevel >> 16), 16));; + + q15_t dcSamples[RX_BLOCK_SIZE]; + for (uint8_t i = 0U; i < RX_BLOCK_SIZE; i++) + dcSamples[i] = samples[i] - offset; +#endif + if (m_modemState == STATE_IDLE) { if (m_dstarEnable) { - q15_t GMSKVals[RX_BLOCK_SIZE + 1U]; - ::arm_fir_fast_q15(&m_GMSKFilter, samples, GMSKVals, blockSize); - - dstarRX.samples(GMSKVals, blockSize); + q15_t GMSKVals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, GMSKVals, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_boxcarFilter, samples, GMSKVals, RX_BLOCK_SIZE); +#endif + 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]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE); +#endif + p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); + } + + if (m_nxdnEnable) { + q15_t NXDNValsTmp[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_nxdnFilter, samples, NXDNValsTmp, RX_BLOCK_SIZE); +#endif + q15_t NXDNVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE); + + nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); + } + + if (m_dmrEnable || m_ysfEnable) { + q15_t RRCVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_rrcFilter, samples, RRCVals, RX_BLOCK_SIZE); + + if (m_ysfEnable) + ysfRX.samples(RRCVals, rssi, RX_BLOCK_SIZE); if (m_dmrEnable) { if (m_duplex) - dmrIdleRX.samples(C4FSKVals, blockSize); + dmrIdleRX.samples(RRCVals, RX_BLOCK_SIZE); else - dmrDMORX.samples(C4FSKVals, rssi, blockSize); + dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE); } - - if (m_ysfEnable) - ysfRX.samples(C4FSKVals, blockSize); - - if (m_p25Enable) - p25RX.samples(C4FSKVals, blockSize); } } 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); - - dstarRX.samples(GMSKVals, blockSize); + q15_t GMSKVals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, GMSKVals, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_boxcarFilter, samples, GMSKVals, RX_BLOCK_SIZE); +#endif + 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 DMRVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_rrcFilter, samples, DMRVals, 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(DMRVals, rssi, control, RX_BLOCK_SIZE); else - dmrIdleRX.samples(C4FSKVals, blockSize); + dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE); } else { - dmrDMORX.samples(C4FSKVals, rssi, blockSize); + dmrDMORX.samples(DMRVals, 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); - - ysfRX.samples(C4FSKVals, blockSize); + q15_t YSFVals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_rrcFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_rrcFilter, samples, YSFVals, RX_BLOCK_SIZE); +#endif + ysfRX.samples(YSFVals, 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]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE); +#endif + p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); + } + } else if (m_modemState == STATE_NXDN) { + if (m_nxdnEnable) { + q15_t NXDNValsTmp[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) + ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_nxdnFilter, samples, NXDNValsTmp, RX_BLOCK_SIZE); +#endif + q15_t NXDNVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE); - p25RX.samples(C4FSKVals, blockSize); + nxdnRX.samples(NXDNVals, 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_boxcarFilter, 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); } } } @@ -269,6 +470,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t case STATE_P25: txLevel = m_p25TXLevel; break; + case STATE_NXDN: + txLevel = m_nxdnTXLevel; + break; default: txLevel = m_cwIdTXLevel; break; @@ -277,7 +481,7 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t for (uint16_t i = 0U; i < length; i++) { q31_t res1 = samples[i] * txLevel; q15_t res2 = q15_t(__SSAT((res1 >> 15), 16)); - uint16_t res3 = uint16_t(res2 + DC_OFFSET); + uint16_t res3 = uint16_t(res2 + m_txDCOffset); // Detect DAC overflow if (res3 > 4095U) @@ -315,10 +519,11 @@ void CIO::setMode() setDMRInt(m_modemState == STATE_DMR); setYSFInt(m_modemState == STATE_YSF); setP25Int(m_modemState == STATE_P25); + setNXDNInt(m_modemState == STATE_NXDN); #endif } -void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel) +void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, int16_t txDCOffset, int16_t rxDCOffset) { m_pttInvert = pttInvert; @@ -328,6 +533,10 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_dmrTXLevel = q15_t(dmrTXLevel * 128); m_ysfTXLevel = q15_t(ysfTXLevel * 128); m_p25TXLevel = q15_t(p25TXLevel * 128); + m_nxdnTXLevel = q15_t(nxdnTXLevel * 128); + + m_rxDCOffset = DC_OFFSET + rxDCOffset; + m_txDCOffset = DC_OFFSET + txDCOffset; if (rxInvert) m_rxLevel = -m_rxLevel; @@ -337,6 +546,7 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_dmrTXLevel = -m_dmrTXLevel; m_ysfTXLevel = -m_ysfTXLevel; m_p25TXLevel = -m_p25TXLevel; + m_nxdnTXLevel = -m_nxdnTXLevel; } } @@ -345,11 +555,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; } @@ -369,6 +574,11 @@ void CIO::resetWatchdog() m_watchdog = 0U; } +uint32_t CIO::getWatchdog() +{ + return m_watchdog; +} + bool CIO::hasLockout() const { return m_lockout; diff --git a/IO.h b/IO.h index e8ecdce..e0dc29f 100644 --- a/IO.h +++ b/IO.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,9 +40,9 @@ 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); + void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnLevel, int16_t txDCOffset, int16_t rxDCOffset); void getOverflow(bool& adcOverflow, bool& dacOverflow); @@ -52,6 +52,9 @@ public: bool hasLockout() const; void resetWatchdog(); + uint32_t getWatchdog(); + + void selfTest(); private: bool m_started; @@ -60,10 +63,19 @@ 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[80U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare + arm_biquad_casd_df1_inst_q31 m_dcFilter; + q31_t m_dcState[4]; + + arm_fir_instance_q15 m_rrcFilter; + //arm_fir_instance_q15 m_gaussianFilter; + arm_fir_instance_q15 m_boxcarFilter; + arm_fir_instance_q15 m_nxdnFilter; + arm_fir_instance_q15 m_nxdnISincFilter; + q15_t m_rrcState[140U]; // NoTaps + BlockSize - 1, 82 + 20 - 1 plus some spare + //q15_t m_gaussianState[80U]; // NoTaps + BlockSize - 1, 24 + 20 - 1 plus some spare + q15_t m_boxcarState[60U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare + q15_t m_nxdnState[220U]; // NoTaps + BlockSize - 1, 162 + 20 - 1 plus some spare + q15_t m_nxdnISincState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare bool m_pttInvert; q15_t m_rxLevel; @@ -72,6 +84,10 @@ private: q15_t m_dmrTXLevel; q15_t m_ysfTXLevel; q15_t m_p25TXLevel; + q15_t m_nxdnTXLevel; + + uint16_t m_rxDCOffset; + uint16_t m_txDCOffset; uint32_t m_ledCount; bool m_ledValue; @@ -81,8 +97,6 @@ private: uint16_t m_adcOverflow; uint16_t m_dacOverflow; - uint32_t m_count; - volatile uint32_t m_watchdog; bool m_lockout; @@ -101,6 +115,9 @@ private: void setDMRInt(bool on); void setYSFInt(bool on); void setP25Int(bool on); + void setNXDNInt(bool on); + + void delayInt(unsigned int dly); }; #endif diff --git a/IODue.cpp b/IODue.cpp index 1db42e6..d24080f 100644 --- a/IODue.cpp +++ b/IODue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -33,6 +33,7 @@ #define PIN_DMR 17 #define PIN_YSF 18 #define PIN_P25 19 +#define PIN_NXDN 20 #define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #define ADC_ISR_EOC_Chan ADC_ISR_EOC7 #define ADC_CDR_Chan 7 @@ -46,6 +47,7 @@ #define PIN_DMR 8 #define PIN_YSF 7 #define PIN_P25 6 +#define PIN_NXDN 5 #define ADC_CHER_Chan (1<<13) // ADC on Due pin A11 - Due AD13 - (1 << 13) #define ADC_ISR_EOC_Chan ADC_ISR_EOC13 #define ADC_CDR_Chan 13 @@ -61,6 +63,7 @@ #define PIN_DMR 8 #define PIN_YSF 7 #define PIN_P25 6 +#define PIN_NXDN 5 #define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #define ADC_ISR_EOC_Chan ADC_ISR_EOC7 #define ADC_CDR_Chan 7 @@ -77,7 +80,7 @@ const uint16_t DC_OFFSET = 2048U; extern "C" { void ADC_Handler() { - io.interrupt(0U); + io.interrupt(); } } @@ -95,13 +98,14 @@ void CIO::initInt() pinMode(PIN_DMR, OUTPUT); pinMode(PIN_YSF, OUTPUT); pinMode(PIN_P25, OUTPUT); + pinMode(PIN_NXDN, OUTPUT); #endif } 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 @@ -164,7 +168,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; @@ -211,7 +215,7 @@ void CIO::setDStarInt(bool on) digitalWrite(PIN_DSTAR, on ? HIGH : LOW); } -void CIO::setDMRInt(bool on) +void CIO::setDMRInt(bool on) { digitalWrite(PIN_DMR, on ? HIGH : LOW); } @@ -221,10 +225,20 @@ void CIO::setYSFInt(bool on) digitalWrite(PIN_YSF, on ? HIGH : LOW); } -void CIO::setP25Int(bool on) +void CIO::setP25Int(bool on) { digitalWrite(PIN_P25, on ? HIGH : LOW); } +void CIO::setNXDNInt(bool on) +{ + digitalWrite(PIN_NXDN, on ? HIGH : LOW); +} + +void CIO::delayInt(unsigned int dly) +{ + delay(dly); +} + #endif diff --git a/IOSTM.cpp b/IOSTM.cpp index b8fb772..3dd9dd3 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM - * Copyright (C) 2016 by Andy Uribe CA6JAU + * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU + * Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,66 +22,82 @@ #include "Globals.h" #include "IO.h" -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) || defined(STM32F7XX) #if defined(STM32F4_DISCOVERY) /* Pin definitions for STM32F4 Discovery Board: -PTT PB13 output -COSLED PA7 output -LED PD15 output -COS PA5 input +PTT PB13 output P1 Pin37 +COSLED PA7 output P1 Pin17 +LED PD15 output P1 Pin47 +COS PA5 input P1 Pin15 -DSTAR PD12 output -DMR PD13 output -YSF PD14 output -P25 PD11 output +DSTAR PD12 output P1 Pin44 +DMR PD13 output P1 Pin45 +YSF PD14 output P1 Pin46 +P25 PD11 output P1 Pin43 +NXDN PD10 output P1 Pin42 -RX PA0 analog input -RSSI PA1 analog input -TX PA4 analog output +RX PA0 analog input P1 Pin12 +RSSI PA1 analog input P1 Pin11 +TX PA4 analog output P1 Pin16 -EXT_CLK PA15 input +EXT_CLK PA15 input P2 Pin40 */ -#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_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOD +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOD -#define PIN_DMR GPIO_Pin_13 -#define PORT_DMR GPIOD -#define RCC_Per_DMR RCC_AHB1Periph_GPIOD +#define PIN_DSTAR GPIO_Pin_12 +#define PORT_DSTAR GPIOD +#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOD -#define PIN_YSF GPIO_Pin_14 -#define PORT_YSF GPIOD -#define RCC_Per_YSF RCC_AHB1Periph_GPIOD +#define PIN_DMR GPIO_Pin_13 +#define PORT_DMR GPIOD +#define RCC_Per_DMR RCC_AHB1Periph_GPIOD -#define PIN_RX GPIO_Pin_0 -#define PIN_RX_CH ADC_Channel_0 +#define PIN_YSF GPIO_Pin_14 +#define PORT_YSF GPIOD +#define RCC_Per_YSF RCC_AHB1Periph_GPIOD -#define PIN_RSSI GPIO_Pin_1 -#define PIN_RSSI_CH ADC_Channel_1 +#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_PI) /* @@ -88,410 +105,1046 @@ 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 +NXDN PB1 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_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB -#define PIN_DMR GPIO_Pin_8 -#define PORT_DMR GPIOC -#define RCC_Per_DMR RCC_AHB1Periph_GPIOC +#define PIN_DSTAR GPIO_Pin_7 +#define PORT_DSTAR GPIOC +#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC -#define PIN_YSF GPIO_Pin_8 -#define PORT_YSF GPIOA -#define RCC_Per_YSF RCC_AHB1Periph_GPIOA +#define PIN_DMR GPIO_Pin_8 +#define PORT_DMR GPIOC +#define RCC_Per_DMR RCC_AHB1Periph_GPIOC -#define PIN_RX GPIO_Pin_0 -#define PIN_RX_CH ADC_Channel_0 +#define PIN_YSF GPIO_Pin_8 +#define PORT_YSF GPIOA +#define RCC_Per_YSF RCC_AHB1Periph_GPIOA -#define PIN_RSSI GPIO_Pin_7 -#define PIN_RSSI_CH ADC_Channel_7 +#define PIN_EXT_CLK GPIO_Pin_15 +#define SRC_EXT_CLK GPIO_PinSource15 +#define PORT_EXT_CLK GPIOA -#elif defined(STM32F4_NUCLEO) +#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(STM32F722_PI) /* -Pin definitions for STM32F4 Nucleo boards: +Pin definitions for STM32F722 Pi Board: -PTT PB13 output -COSLED PB14 output -LED PA5 output -COS PB15 input +PTT PB13 output +COSLED PB14 output +LED PB15 output +COS PC0 input -DSTAR PB10 output -DMR PB4 output -YSF PB5 output -P25 PB3 output +DSTAR PC7 output +DMR PC8 output +YSF PA8 output +P25 PC9 output +NXDN PB1 output -RX PA0 analog input -RSSI PA1 analog input -TX PA4 analog output +RX PA0 analog input +RSSI PA7 analog input +TX PA4 analog output -EXT_CLK PA15 input +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_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_5 -#define PORT_LED GPIOA -#define RCC_Per_LED RCC_AHB1Periph_GPIOA +#define PIN_LED GPIO_Pin_15 +#define PORT_LED GPIOB +#define RCC_Per_LED RCC_AHB1Periph_GPIOB -#define PIN_P25 GPIO_Pin_3 -#define PORT_P25 GPIOB -#define RCC_Per_P25 RCC_AHB1Periph_GPIOB +#define PIN_P25 GPIO_Pin_9 +#define PORT_P25 GPIOC +#define RCC_Per_P25 RCC_AHB1Periph_GPIOC -#define PIN_DSTAR GPIO_Pin_10 -#define PORT_DSTAR GPIOB -#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB +#define PIN_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB -#define PIN_DMR GPIO_Pin_4 -#define PORT_DMR GPIOB -#define RCC_Per_DMR RCC_AHB1Periph_GPIOB +#define PIN_DSTAR GPIO_Pin_7 +#define PORT_DSTAR GPIOC +#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC -#define PIN_YSF GPIO_Pin_5 -#define PORT_YSF GPIOB -#define RCC_Per_YSF RCC_AHB1Periph_GPIOB +#define PIN_DMR GPIO_Pin_8 +#define PORT_DMR GPIOC +#define RCC_Per_DMR RCC_AHB1Periph_GPIOC -#define PIN_RX GPIO_Pin_0 -#define PIN_RX_CH ADC_Channel_0 +#define PIN_YSF GPIO_Pin_8 +#define PORT_YSF GPIOA +#define RCC_Per_YSF RCC_AHB1Periph_GPIOA -#define PIN_RSSI GPIO_Pin_1 -#define PIN_RSSI_CH ADC_Channel_1 +#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_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_F4M) +/* +Pin definitions for MMDVM-F4M Pi-Hat F0DEI board: + +PTT PB13 output +COSLED PB14 output +LED PB15 output +COS PC0 input + +DSTAR PC7 output +DMR PC8 output +YSF PA8 output +P25 PC9 output +NXDN PB1 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_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_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_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB + +#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_YSF GPIO_Pin_8 +#define PORT_YSF GPIOA +#define RCC_Per_YSF RCC_AHB1Periph_GPIOA + +#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_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(STM32F722_F7M) +/* +Pin definitions for MMDVM-F7M Pi-Hat F0DEI board: + +PTT PB13 output +COSLED PB14 output +LED PB15 output +COS PC0 input + +DSTAR PC7 output +DMR PC8 output +YSF PA8 output +P25 PC9 output +NXDN PB1 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_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_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_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB + +#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_YSF GPIO_Pin_8 +#define PORT_YSF GPIOA +#define RCC_Per_YSF RCC_AHB1Periph_GPIOA + +#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_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 (ST Morpho header): + +PTT PB13 output CN10 Pin30 +COSLED PB14 output CN10 Pin28 +LED PA5 output CN10 Pin11 +COS PB15 input CN10 Pin26 + +DSTAR PB10 output CN10 Pin25 +DMR PB4 output CN10 Pin27 +YSF PB5 output CN10 Pin29 +P25 PB3 output CN10 Pin31 +NXDN PA10 output CN10 Pin33 + +MDSTAR PC4 output CN10 Pin34 +MDMR PC5 output CN10 Pin6 +MYSF PC2 output CN7 Pin35 +MP25 PC3 output CN7 Pin37 +MNXDN PC6 output CN10 Pin4 + +RX PA0 analog input CN7 Pin28 +RSSI PA1 analog input CN7 Pin30 +TX PA4 analog output CN7 Pin32 + +EXT_CLK PA15 input CN7 Pin17 +*/ + +#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_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_P25 GPIO_Pin_3 +#define PORT_P25 GPIOB +#define RCC_Per_P25 RCC_AHB1Periph_GPIOB + +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOA +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA + +#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_YSF GPIO_Pin_5 +#define PORT_YSF GPIOB +#define RCC_Per_YSF RCC_AHB1Periph_GPIOB + +#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_MNXDN GPIO_Pin_6 +#define PORT_MNXDN GPIOC +#define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC + +#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 CN9 Pin7 +COSLED PB3 output CN9 Pin4 +LED PB5 output CN9 Pin5 +COS PB4 input CN9 Pin6 + +DSTAR PA1 output CN8 Pin2 +DMR PA4 output CN8 Pin3 +YSF PB0 output CN8 Pin4 +P25 PC1 output CN8 Pin5 +NXDN PA3 output CN9 Pin1 + +RX PA0 analog input CN8 Pin1 +RSSI PC0 analog input CN8 Pin6 +TX PA5 analog output CN5 Pin6 + +EXT_CLK PB8 input CN5 Pin10 +*/ + +#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_NXDN GPIO_Pin_3 +#define PORT_NXDN GPIOA +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA + +#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_DISCOVERY, STM32F4_PI or STM32F4_NUCLEO need to be defined" +#error "Either STM32F4_NUCLEO_MORPHO_HEADER or STM32F4_NUCLEO_ARDUINO_HEADER need to be defined in Config.h" +#endif + +#elif defined(STM32F7_NUCLEO) +/* +Pin definitions for STM32F7 Nucleo boards (ST Morpho header): + +PTT PB13 output CN12 Pin30 +COSLED PB14 output CN12 Pin28 +LED PA5 output CN12 Pin11 +COS PB15 input CN12 Pin26 + +DSTAR PB10 output CN12 Pin25 +DMR PB4 output CN12 Pin27 +YSF PB5 output CN12 Pin29 +P25 PB3 output CN12 Pin31 +NXDN PA10 output CN12 Pin33 + +MDSTAR PC4 output CN12 Pin34 +MDMR PC5 output CN12 Pin6 +MYSF PC2 output CN11 Pin35 +MP25 PC3 output CN11 Pin37 +MNXDN PC6 output CN12 Pin4 + +RX PA0 analog input CN11 Pin28 +RSSI PA1 analog input CN11 Pin30 +TX PA4 analog output CN11 Pin32 + +EXT_CLK PA15 input CN11 Pin17 +*/ + +#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_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_P25 GPIO_Pin_3 +#define PORT_P25 GPIOB +#define RCC_Per_P25 RCC_AHB1Periph_GPIOB + +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOA +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA + +#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_YSF GPIO_Pin_5 +#define PORT_YSF GPIOB +#define RCC_Per_YSF RCC_AHB1Periph_GPIOB + +#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_MNXDN GPIO_Pin_6 +#define PORT_MNXDN GPIOC +#define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC + +#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_DVM) +/* +Pin definitions for STM32F4 STM32-DVM rev 3 Board: + +COS PB13 input +PTT PB12 output +COSLED PB4 output +LED PB3 output + +P25 PB8 output +NXDN PB9 output +DSTAR PB6 output +DMR PB5 output +YSF PB7 output + +RX PB0 analog input +RSSI PB1 analog input +TX PA4 analog output + +EXT_CLK PA15 input +*/ + +#define PIN_COS GPIO_Pin_13 +#define PORT_COS GPIOB +#define RCC_Per_COS RCC_AHB1Periph_GPIOB + + +#define PIN_PTT GPIO_Pin_12 +#define PORT_PTT GPIOB +#define RCC_Per_PTT RCC_AHB1Periph_GPIOB + +#define PIN_COSLED GPIO_Pin_4 +#define PORT_COSLED GPIOB +#define RCC_Per_COSLED RCC_AHB1Periph_GPIOB + +#define PIN_LED GPIO_Pin_3 +#define PORT_LED GPIOB +#define RCC_Per_LED RCC_AHB1Periph_GPIOB + +#define PIN_P25 GPIO_Pin_8 +#define PORT_P25 GPIOB +#define RCC_Per_P25 RCC_AHB1Periph_GPIOB + +#define PIN_NXDN GPIO_Pin_9 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB + +#define PIN_DSTAR GPIO_Pin_6 +#define PORT_DSTAR GPIOB +#define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB + +#define PIN_DMR GPIO_Pin_5 +#define PORT_DMR GPIOB +#define RCC_Per_DMR RCC_AHB1Periph_GPIOB + +#define PIN_YSF GPIO_Pin_7 +#define PORT_YSF GPIOB +#define RCC_Per_YSF RCC_AHB1Periph_GPIOB + +#define PIN_EXT_CLK GPIO_Pin_15 +#define SRC_EXT_CLK GPIO_PinSource15 +#define PORT_EXT_CLK GPIOA + +#define PIN_RX GPIO_Pin_0 +#define PIN_RX_CH ADC_Channel_8 +#define PORT_RX GPIOB +#define RCC_Per_RX RCC_AHB1Periph_GPIOB + +#define PIN_RSSI GPIO_Pin_1 +#define PIN_RSSI_CH ADC_Channel_9 +#define PORT_RSSI GPIOB +#define RCC_Per_RSSI RCC_AHB1Periph_GPIOB + +#define PIN_TX GPIO_Pin_4 +#define PIN_TX_CH DAC_Channel_1 + +#else +#error "Either STM32F4_DISCOVERY, STM32F4_PI, STM32F722_PI, STM32F4_F4M, STM32F722_F7M, STM32F4_DVM, STM32F4_NUCLEO or STM32F7_NUCLEO need to be defined" #endif const uint16_t DC_OFFSET = 2048U; -// Sampling frequency -#define SAMP_FREQ 48000 +// Sampling frequency +#define SAMP_FREQ 48000 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; - - // 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); +{ + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_StructInit(&GPIO_InitStruct); + GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; - // 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); + // 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); - // 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); + // 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); - // 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); + // 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); #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); + + // NXDN pin + RCC_AHB1PeriphClockCmd(RCC_Per_NXDN, ENABLE); + GPIO_InitStruct.GPIO_Pin = PIN_NXDN; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(PORT_NXDN, &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); + + // NXDN mode pin + RCC_AHB1PeriphClockCmd(RCC_Per_MNXDN, ENABLE); + GPIO_InitStruct.GPIO_Pin = PIN_MNXDN; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(PORT_MNXDN, &GPIO_InitStruct); #endif } void CIO::startInt() { - if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != RESET)) - io.interrupt(0U); - - // Init the ADC - GPIO_InitTypeDef GPIO_InitStruct; - ADC_InitTypeDef ADC_InitStructure; - ADC_CommonInitTypeDef ADC_CommonInitStructure; + if ((ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) != RESET)) + io.interrupt(); - GPIO_StructInit(&GPIO_InitStruct); - ADC_CommonStructInit(&ADC_CommonInitStructure); - ADC_StructInit(&ADC_InitStructure); + // Init the ADC + GPIO_InitTypeDef GPIO_InitStruct; + ADC_InitTypeDef ADC_InitStructure; + ADC_CommonInitTypeDef ADC_CommonInitStructure; - // Enable ADC clock - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); -#if defined(SEND_RSSI_DATA) - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE); -#else - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); -#endif - -#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); + GPIO_StructInit(&GPIO_InitStruct); + ADC_CommonStructInit(&ADC_CommonInitStructure); + ADC_StructInit(&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); - - // 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); + // 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); #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 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 the DAC - DAC_InitTypeDef DAC_InitStructure; - - GPIO_StructInit(&GPIO_InitStruct); - DAC_StructInit(&DAC_InitStructure); - - // GPIOA clock enable - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, 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); - - 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); - - // 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); -#endif - - TIM_TimeBaseInitTypeDef timerInitStructure; - TIM_TimeBaseStructInit (&timerInitStructure); - - // TIM2 output frequency -#if defined(EXTERNAL_OSC) - timerInitStructure.TIM_Prescaler = (uint16_t) ((EXTERNAL_OSC/(2*SAMP_FREQ)) - 1); -#else - 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); - -#if defined(EXTERNAL_OSC) - // Enable external clock (PA15) - TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00); + // Init ADCs in dual mode (RSSI), div clock by two +#if defined(SEND_RSSI_DATA) + ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult; #else - // Enable internal clock - TIM_InternalClockConfig(TIM2); + 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) + 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); #endif - // Enable TIM2 - TIM_Cmd(TIM2, ENABLE); - // Enable TIM2 interrupt - TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); + // Init the DAC + DAC_InitTypeDef DAC_InitStructure; - NVIC_InitTypeDef nvicStructure; - nvicStructure.NVIC_IRQChannel = TIM2_IRQn; - nvicStructure.NVIC_IRQChannelPreemptionPriority = 0; - nvicStructure.NVIC_IRQChannelSubPriority = 1; - nvicStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_Init(&nvicStructure); + GPIO_StructInit(&GPIO_InitStruct); + DAC_StructInit(&DAC_InitStructure); - GPIO_ResetBits(PORT_COSLED, PIN_COSLED); - GPIO_SetBits(PORT_LED, PIN_LED); + // GPIOA clock enable + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + // DAC Periph clock enable + RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); + + // 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(PIN_TX_CH, &DAC_InitStructure); + DAC_Cmd(PIN_TX_CH, ENABLE); + + // Init the timer + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); + +#if defined(EXTERNAL_OSC) && !(defined(STM32F4_PI) || defined(STM32F722_PI)) + // 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); + + // TIM2 output frequency +#if defined(EXTERNAL_OSC) && !(defined(STM32F4_PI) || defined(STM32F722_PI)) + timerInitStructure.TIM_Prescaler = (uint16_t) ((EXTERNAL_OSC/(2*SAMP_FREQ)) - 1); + timerInitStructure.TIM_Period = 1; +#else + timerInitStructure.TIM_Prescaler = (uint16_t) ((SystemCoreClock/(6*SAMP_FREQ)) - 1); + timerInitStructure.TIM_Period = 2; +#endif + + timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up; + timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; + timerInitStructure.TIM_RepetitionCounter = 0; + TIM_TimeBaseInit(TIM2, &timerInitStructure); + +#if defined(EXTERNAL_OSC) && !(defined(STM32F4_PI) || defined(STM32F722_PI)) + // Enable external clock + TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00); +#else + // Enable internal clock + TIM_InternalClockConfig(TIM2); +#endif + + // 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); + + 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_ReadInputDataBit(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) +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) +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 +} + +void CIO::setNXDNInt(bool on) +{ + GPIO_WriteBit(PORT_NXDN, PIN_NXDN, on ? Bit_SET : Bit_RESET); +#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO) + GPIO_WriteBit(PORT_MNXDN, PIN_MNXDN, on ? Bit_SET : Bit_RESET); +#endif +} + +// Simple delay function for STM32 +// Example from: http://thehackerworkshop.com/?p=1209 +void CIO::delayInt(unsigned int dly) +{ +#if defined(STM32F7XX) + unsigned int loopsPerMillisecond = (SystemCoreClock/1000); +#else + unsigned int loopsPerMillisecond = (SystemCoreClock/1000) / 3; +#endif + + for (; dly > 0; dly--) + { + asm volatile //this routine waits (approximately) one millisecond + ( + "mov r3, %[loopsPerMillisecond] \n\t" //load the initial loop counter + "loop: \n\t" + "subs r3, #1 \n\t" + "bne loop \n\t" + + : //empty output list + : [loopsPerMillisecond] "r" (loopsPerMillisecond) //input to the asm routine + : "r3", "cc" //clobber list + ); + } } #endif - diff --git a/IOSTM_CMSIS.cpp b/IOSTM_CMSIS.cpp new file mode 100644 index 0000000..be55665 --- /dev/null +++ b/IOSTM_CMSIS.cpp @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2016 by Jim McLaughlin KI6ZUM + * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU + * Copyright (C) 2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2017 by Wojciech Krutnik N0CALL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "IO.h" + + +#if defined(STM32F1) + +#if defined(STM32F1_POG) +/* +Pin definitions for STM32F1 POG Board: + +PTT PB12 output +COSLED PB5 output +LED PB4 output +COS PB13 input/PD + +DSTAR PB7 output +DMR PB6 output +YSF PB8 output +P25 PB9 output +NXDN PB10 output + +RX PB0 analog input (ADC1_8) +RSSI PB1 analog input (ADC2_9) +TX PA4 analog output (DAC_OUT1) + +EXT_CLK PA15 input (AF: TIM2_CH1_ETR) + +USART1_TXD PA9 output (AF) +USART1_RXD PA10 input (AF) + +*/ + +#define PIN_PTT 12 +#define PORT_PTT GPIOB +#define BB_PTT *((bitband_t)BITBAND_PERIPH(&PORT_PTT->ODR, PIN_PTT)) +#define PIN_COSLED 5 +#define PORT_COSLED GPIOB +#define BB_COSLED *((bitband_t)BITBAND_PERIPH(&PORT_COSLED->ODR, PIN_COSLED)) +#define PIN_LED 4 +#define PORT_LED GPIOB +#define BB_LED *((bitband_t)BITBAND_PERIPH(&PORT_LED->ODR, PIN_LED)) +#define PIN_COS 13 +#define PORT_COS GPIOB +#define BB_COS *((bitband_t)BITBAND_PERIPH(&PORT_COS->IDR, PIN_COS)) + +#define PIN_DSTAR 7 +#define PORT_DSTAR GPIOB +#define BB_DSTAR *((bitband_t)BITBAND_PERIPH(&PORT_DSTAR->ODR, PIN_DSTAR)) +#define PIN_DMR 6 +#define PORT_DMR GPIOB +#define BB_DMR *((bitband_t)BITBAND_PERIPH(&PORT_DMR->ODR, PIN_DMR)) +#define PIN_YSF 8 +#define PORT_YSF GPIOB +#define BB_YSF *((bitband_t)BITBAND_PERIPH(&PORT_YSF->ODR, PIN_YSF)) +#define PIN_P25 9 +#define PORT_P25 GPIOB +#define BB_P25 *((bitband_t)BITBAND_PERIPH(&PORT_P25->ODR, PIN_P25)) +#define PIN_NXDN 10 +#define PORT_NXDN GPIOB +#define BB_NXDN *((bitband_t)BITBAND_PERIPH(&PORT_NXDN->ODR, PIN_NXDN)) + +#define PIN_RX 0 +#define PIN_RX_ADC_CH 8 +#define PORT_RX GPIOB +#define PIN_RSSI 1 +#define PIN_RSSI_ADC_CH 9 +#define PORT_RSSI GPIOB +#define PIN_TX 4 +#define PIN_TX_DAC_CH 1 +#define PORT_TX GPIOA + +#define PIN_EXT_CLK 15 +#define SRC_EXT_CLK 15 +#define PORT_EXT_CLK GPIOA + +#define PIN_USART1_TXD 9 +#define PORT_USART1_TXD GPIOA +#define PIN_USART1_RXD 10 +#define PORT_USART1_RXD GPIOA + +#else // defined(STM32F1_POG) +#error "Either STM32F1_POG, or sth need to be defined" +#endif // defined(STM32F1_POG) + +const uint16_t DC_OFFSET = 2048U; + +// Sampling frequency +#define SAMP_FREQ 48000 + +extern "C" { + void TIM2_IRQHandler() { + if ((TIM2->SR & TIM_SR_UIF) == TIM_SR_UIF) { + TIM2->SR = ~TIM_SR_UIF; // clear UI flag + io.interrupt(); + } + } +} + + +void delay(uint32_t cnt) +{ + while(cnt--) + asm("nop"); +} + +// source: http://www.freddiechopin.info/ +void GPIOConfigPin(GPIO_TypeDef *port_ptr, uint32_t pin, uint32_t mode_cnf_value) +{ + volatile uint32_t *cr_ptr; + uint32_t cr_value; + + cr_ptr = &port_ptr->CRL; // configuration of pins [0,7] is in CRL + + if (pin >= 8) // is pin in [8; 15]? + { // configuration of pins [8,15] is in CRH + cr_ptr++; // advance to next struct element CRL -> CRH + pin -= 8; // crop the pin number + } + + cr_value = *cr_ptr; // localize the CRL / CRH value + + cr_value &= ~(0xF << (pin * 4)); // clear the MODE and CNF fields (now that pin is an analog input) + cr_value |= (mode_cnf_value << (pin * 4)); // save new MODE and CNF value for desired pin + + *cr_ptr = cr_value; // save localized value to CRL / CRL +} + +#if defined(STM32F1_POG) +void FancyLEDEffect() +{ + bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25}; + + for(int i=0; i<7; i++){ + *foo[i] = 0x01; + } + GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1); + *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x00; + delay(SystemCoreClock/1000*100); + for(int i=0; i<7; i++){ + *foo[i] = 0x00; + } + *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01; + delay(SystemCoreClock/1000*20); + *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x00; + delay(SystemCoreClock/1000*10); + *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01; + + *foo[0] = 0x01; + for(int i=1; i<7; i++){ + delay(SystemCoreClock/1000*10); + *foo[i-1] = 0x00; + *foo[i] = 0x01; + } + for(int i=5; i>=0; i--){ + delay(SystemCoreClock/1000*10); + *foo[i+1] = 0x00; + *foo[i] = 0x01; + } + delay(SystemCoreClock/1000*10); + *foo[5+1-6] = 0x00; + *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x00; + delay(SystemCoreClock/1000*10); + *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01; + GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1|GPIO_CRL_CNF0_1); + delay(SystemCoreClock/1000*50); +} +#endif + +static inline void GPIOInit() +{ + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN + | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN + | RCC_APB2ENR_AFIOEN; // enable all GPIOs + + // set all ports to input with pull-down + GPIOA->CRL = 0x88888888; + GPIOA->CRH = 0x88888888; + GPIOA->ODR = 0; + GPIOB->CRL = 0x88888888; + GPIOB->CRH = 0x88888888; + GPIOB->ODR = 0; + GPIOC->CRL = 0x88888888; + GPIOC->CRH = 0x88888888; + GPIOC->ODR = 0; + GPIOD->CRL = 0x88888888; + GPIOD->CRH = 0x88888888; + GPIOD->ODR = 0; + + // configure ports + GPIOConfigPin(PORT_PTT, PIN_PTT, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_COSLED, PIN_COSLED, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_LED, PIN_LED, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_COS, PIN_COS, GPIO_CRL_CNF0_1); + + GPIOConfigPin(PORT_DSTAR, PIN_DSTAR, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_DMR, PIN_DMR, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_YSF, PIN_YSF, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_P25, PIN_P25, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_NXDN, PIN_NXDN, 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_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1|GPIO_CRL_CNF0_1); + GPIOConfigPin(PORT_USART1_RXD, PIN_USART1_RXD, GPIO_CRL_CNF0_0); + + AFIO->MAPR = (AFIO->MAPR & ~AFIO_MAPR_SWJ_CFG) | AFIO_MAPR_SWJ_CFG_1; +} + + +static inline void ADCInit() +{ + RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_ADCPRE_Msk) + | RCC_CFGR_ADCPRE_DIV6; // ADC clock divided by 6 (72MHz/6 = 12MHz) + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; +#if defined(SEND_RSSI_DATA) + RCC->APB2ENR |= RCC_APB2ENR_ADC2EN; +#endif + + // Init ADCs in dual mode (RSSI) +#if defined(SEND_RSSI_DATA) + ADC1->CR1 = ADC_CR1_DUALMOD_1|ADC_CR1_DUALMOD_2; // Regular simultaneous mode +#endif + + // Set sampling time to 7.5 cycles + if (PIN_RX_ADC_CH < 10) { + ADC1->SMPR2 = ADC_SMPR2_SMP0_0 << (3*PIN_RX_ADC_CH); + } else { + ADC1->SMPR1 = ADC_SMPR1_SMP10_0 << (3*PIN_RX_ADC_CH); + } +#if defined(SEND_RSSI_DATA) + if (PIN_RSSI_ADC_CH < 10) { + ADC2->SMPR2 = ADC_SMPR2_SMP0_0 << (3*PIN_RSSI_ADC_CH); + } else { + ADC2->SMPR1 = ADC_SMPR1_SMP10_0 << (3*PIN_RSSI_ADC_CH); + } +#endif + + // Set conversion channel (single conversion) + ADC1->SQR3 = PIN_RX_ADC_CH; +#if defined(SEND_RSSI_DATA) + ADC2->SQR3 = PIN_RSSI_ADC_CH; +#endif + + // Enable ADC + ADC1->CR2 |= ADC_CR2_ADON; + +#if defined(SEND_RSSI_DATA) + // Enable ADC2 + ADC2->CR2 |= ADC_CR2_ADON; +#endif + + // Start calibration + delay(6*2); + ADC1->CR2 |= ADC_CR2_CAL; + while((ADC1->CR2 & ADC_CR2_CAL) == ADC_CR2_CAL) + ; +#if defined(SEND_RSSI_DATA) + ADC2->CR2 |= ADC_CR2_CAL; + while((ADC2->CR2 & ADC_CR2_CAL) == ADC_CR2_CAL) + ; +#endif + + // Trigger first conversion + ADC1->CR2 |= ADC_CR2_ADON; +#if defined(SEND_RSSI_DATA) + ADC2->CR2 |= ADC_CR2_ADON; +#endif +} + + +static inline void DACInit() +{ + RCC->APB1ENR |= RCC_APB1ENR_DACEN; + + DAC->CR = DAC_CR_EN1; +} + + +static inline void TimerInit() +{ + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; + +#if defined(EXTERNAL_OSC) + // Enable external clock, prescaler /4 + TIM2->SMCR = TIM_SMCR_ECE | TIM_SMCR_ETPS_1; +#endif + + // TIM2 output frequency + TIM2->PSC = 0; +#if defined(EXTERNAL_OSC) + // TimerCount = ExternalOsc + // /4 (external clock prescaler) + // /SAMP_FREQ + TIM2->ARR = (uint16_t) ((EXTERNAL_OSC/(4*SAMP_FREQ)) - 1); +#else + TIM2->ARR = (uint16_t) ((SystemCoreClock/(SAMP_FREQ)) - 1); +#endif + + // Enable TIse 1%-tolerance comM2 interrupt + TIM2->DIER = TIM_DIER_UIE; + NVIC_EnableIRQ(TIM2_IRQn); + + // Enable TIM2 + TIM2->CR1 |= TIM_CR1_CEN; +} + + +void CIO::initInt() +{ + GPIOInit(); + ADCInit(); + DACInit(); +#if defined(STM32F1_POG) + FancyLEDEffect(); +#endif +} + +void CIO::startInt() +{ + TimerInit(); + + BB_COSLED = 0; + BB_LED = 1; +} + +void CIO::interrupt() +{ + 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 defined(SEND_RSSI_DATA) + bitband_t rssi_adon = (bitband_t)BITBAND_PERIPH(&ADC2->CR2, ADC_CR2_ADON_Pos); +#endif + + if (*eoc) { + // trigger next conversion + *adon = 1; +#if defined(SEND_RSSI_DATA) + *rssi_adon = 1; +#endif + + m_txBuffer.get(sample, 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; +} + +void CIO::setNXDNInt(bool on) +{ + BB_NXDN = !!on; +} + +void CIO::delayInt(unsigned int dly) +{ + delay(dly); +} + +#endif diff --git a/IOTeensy.cpp b/IOTeensy.cpp index c19f357..ba7f7d1 100644 --- a/IOTeensy.cpp +++ b/IOTeensy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +34,11 @@ #define PIN_DMR 10 #define PIN_YSF 11 #define PIN_P25 12 +#if defined(__MK20DX256__) +#define PIN_NXDN 2 +#else +#define PIN_NXDN 24 +#endif #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 @@ -45,15 +50,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() @@ -70,6 +68,7 @@ void CIO::initInt() pinMode(PIN_DMR, OUTPUT); pinMode(PIN_YSF, OUTPUT); pinMode(PIN_P25, OUTPUT); + pinMode(PIN_NXDN, OUTPUT); #endif } @@ -116,7 +115,6 @@ void CIO::startInt() ADC1_CLP2 + ADC1_CLP1 + ADC1_CLP0; sum1 = (sum1 / 2U) | 0x8000U; ADC1_PG = sum1; - #endif #if defined(EXTERNAL_OSC) @@ -149,42 +147,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) - - 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 = ADC_SC1_AIEN | PIN_RSSI; //start the next RSSI conversion - -#else - m_rssiBuffer.put(0U); -#endif - - - m_watchdog++; + 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); +#endif - - + m_watchdog++; } bool CIO::getCOSInt() @@ -227,4 +216,14 @@ void CIO::setP25Int(bool on) digitalWrite(PIN_P25, on ? HIGH : LOW); } +void CIO::setNXDNInt(bool on) +{ + digitalWrite(PIN_NXDN, on ? HIGH : LOW); +} + +void CIO::delayInt(unsigned int dly) +{ + delay(dly); +} + #endif diff --git a/MMDVM.cpp b/MMDVM.cpp index 9a410ff..d4a12a9 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 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(STM32F7XX) || defined(STM32F105xC) #include "Config.h" #include "Globals.h" @@ -30,15 +30,13 @@ bool m_dstarEnable = true; bool m_dmrEnable = true; bool m_ysfEnable = true; bool m_p25Enable = true; +bool m_nxdnEnable = true; 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,9 +53,15 @@ CYSFTX ysfTX; CP25RX p25RX; CP25TX p25TX; +CNXDNRX nxdnRX; +CNXDNTX nxdnTX; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; +CCalP25 calP25; +CCalNXDN calNXDN; +CCalRSSI calRSSI; CCWIdTX cwIdTX; @@ -92,14 +96,23 @@ void loop() if (m_p25Enable && m_modemState == STATE_P25) p25TX.process(); + if (m_nxdnEnable && m_modemState == STATE_NXDN) + nxdnTX.process(); + if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); - if (m_modemState == STATE_DMRCAL) + if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) calDMR.process(); - if (m_modemState == STATE_IDLE) - cwIdTX.process(); + if (m_modemState == STATE_P25CAL1K) + calP25.process(); + + if (m_modemState == STATE_NXDNCAL1K) + calNXDN.process(); + + if (m_modemState == STATE_IDLE) + cwIdTX.process(); } int main() @@ -111,4 +124,3 @@ int main() } #endif - diff --git a/MMDVM.ino b/MMDVM.ino index 7b30b41..0b0f852 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML * * This program is free software; you can redistribute it and/or modify @@ -27,15 +27,13 @@ bool m_dstarEnable = true; bool m_dmrEnable = true; bool m_ysfEnable = true; bool m_p25Enable = true; +bool m_nxdnEnable = true; 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; @@ -52,9 +50,15 @@ CYSFTX ysfTX; CP25RX p25RX; CP25TX p25TX; +CNXDNRX nxdnRX; +CNXDNTX nxdnTX; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; +CCalP25 calP25; +CCalNXDN calNXDN; +CCalRSSI calRSSI; CCWIdTX cwIdTX; @@ -89,12 +93,21 @@ void loop() if (m_p25Enable && m_modemState == STATE_P25) p25TX.process(); + if (m_nxdnEnable && m_modemState == STATE_NXDN) + nxdnTX.process(); + if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); - if (m_modemState == STATE_DMRCAL) + if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) calDMR.process(); + if (m_modemState == STATE_P25CAL1K) + calP25.process(); + + if (m_modemState == STATE_NXDNCAL1K) + calNXDN.process(); + if (m_modemState == STATE_IDLE) cwIdTX.process(); } diff --git a/MMDVM_STM32F4xx.coproj b/MMDVM_STM32F4xx.coproj index 30657cc..b5562eb 100644 --- a/MMDVM_STM32F4xx.coproj +++ b/MMDVM_STM32F4xx.coproj @@ -1,7 +1,7 @@ - - - + + + - + @@ -155,8 +155,8 @@ - - + + - - + + @@ -242,34 +244,40 @@ + + - + + + + + - + - - + + @@ -277,16 +285,19 @@ + - + + + @@ -295,18 +306,22 @@ + + + + - + @@ -315,6 +330,7 @@ + @@ -331,8 +347,10 @@ + + \ No newline at end of file diff --git a/Makefile b/Makefile index a97316d..bb8e3b5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 2016 by Andy Uribe CA6JAU +# Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU # Copyright (C) 2016 by Jim McLaughlin KI6ZUM # This program is free software; you can redistribute it and/or modify @@ -15,6 +15,54 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# MMDVM source files +MMDVM_PATH=. + +# STM32 library paths +F4_LIB_PATH=./STM32F4XX_Lib +F7_LIB_PATH=./STM32F7XX_Lib + +# MCU external clock frequency (Hz) +CLK_MMDVM_PI=12000000 +CLK_NUCLEO=8000000 + +# Directory Structure +BINDIR=bin +OBJDIR_F4=obj_f4 +OBJDIR_F7=obj_f7 + +# Output files +BINELF_F4=mmdvm_f4.elf +BINHEX_F4=mmdvm_f4.hex +BINBIN_F4=mmdvm_f4.bin +BINELF_F7=mmdvm_f7.elf +BINHEX_F7=mmdvm_f7.hex +BINBIN_F7=mmdvm_f7.bin + +# Header directories +INC_F4= . $(F4_LIB_PATH)/CMSIS/Include/ $(F4_LIB_PATH)/Device/ $(F4_LIB_PATH)/STM32F4xx_StdPeriph_Driver/include/ +INCLUDES_F4=$(INC_F4:%=-I%) +INC_F7= . $(F7_LIB_PATH)/CMSIS/Include/ $(F7_LIB_PATH)/Device/ $(F7_LIB_PATH)/STM32F7xx_StdPeriph_Driver/inc/ +INCLUDES_F7=$(INC_F7:%=-I%) + +# CMSIS libraries +INCLUDES_LIBS_F4=$(F4_LIB_PATH)/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a +INCLUDES_LIBS_F7=$(F7_LIB_PATH)/CMSIS/Lib/GCC/libarm_cortexM7lfsp_math.a + +# STM32F4 Standard Peripheral Libraries source path +STD_LIB_F4=$(F4_LIB_PATH)/STM32F4xx_StdPeriph_Driver/source + +# STM32F7 Standard Peripheral Libraries source path +STD_LIB_F7=$(F7_LIB_PATH)/STM32F7xx_StdPeriph_Driver/src + +# STM32F4 system source path +SYS_DIR_F4=$(F4_LIB_PATH)/Device +STARTUP_DIR_F4=$(F4_LIB_PATH)/Device/startup + +# STM32F7 system source path +SYS_DIR_F7=$(F7_LIB_PATH)/Device +STARTUP_DIR_F7=$(F7_LIB_PATH)/Device/startup + # GNU ARM Embedded Toolchain CC=arm-none-eabi-gcc CXX=arm-none-eabi-g++ @@ -27,147 +75,279 @@ NM=arm-none-eabi-nm SIZE=arm-none-eabi-size A2L=arm-none-eabi-addr2line -# Directory Structure -BINDIR=bin - -# Find source files # "SystemRoot" is only defined in Windows 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 - MDBIN=md $@ + CLEANCMD=del /S *.o *.hex *.bin *.elf GitVersion.h + MDDIRS=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 - MDBIN=md $@ + CLEANCMD=del /S *.o *.hex *.bin *.elf GitVersion.h + MDDIRS=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) - MDBIN=mkdir $@ + CLEANCMD=rm -f $(OBJ_F4) $(OBJ_F7) $(BINDIR)/*.hex $(BINDIR)/*.bin $(BINDIR)/*.elf GitVersion.h + MDDIRS=mkdir $@ endif # Default reference oscillator frequencies ifndef $(OSC) ifeq ($(MAKECMDGOALS),pi) - OSC=12000000 + OSC=$(CLK_MMDVM_PI) + else ifeq ($(MAKECMDGOALS),pi-f722) + OSC=$(CLK_MMDVM_PI) else - OSC=8000000 + OSC=$(CLK_NUCLEO) endif endif -# Find header directories -INC= . STM32F4XX_Lib/CMSIS/Include/ STM32F4XX_Lib/Device/ STM32F4XX_Lib/STM32F4xx_StdPeriph_Driver/include/ -INCLUDES=$(INC:%=-I%) +# Build object lists +CXXSRC=$(wildcard $(MMDVM_PATH)/*.cpp) +CSRC_STD_F4=$(wildcard $(STD_LIB_F4)/*.c) +SYS_F4=$(wildcard $(SYS_DIR_F4)/*.c) +STARTUP_F4=$(wildcard $(STARTUP_DIR_F4)/*.c) +CSRC_STD_F7=$(wildcard $(STD_LIB_F7)/*.c) +SYS_F7=$(wildcard $(SYS_DIR_F7)/*.c) +STARTUP_F7=$(wildcard $(STARTUP_DIR_F7)/*.c) +OBJ_F4=$(CXXSRC:$(MMDVM_PATH)/%.cpp=$(OBJDIR_F4)/%.o) $(CSRC_STD_F4:$(STD_LIB_F4)/%.c=$(OBJDIR_F4)/%.o) $(SYS_F4:$(SYS_DIR_F4)/%.c=$(OBJDIR_F4)/%.o) $(STARTUP_F4:$(STARTUP_DIR_F4)/%.c=$(OBJDIR_F4)/%.o) +OBJ_F7=$(CXXSRC:$(MMDVM_PATH)/%.cpp=$(OBJDIR_F7)/%.o) $(CSRC_STD_F7:$(STD_LIB_F7)/%.c=$(OBJDIR_F7)/%.o) $(SYS_F7:$(SYS_DIR_F7)/%.c=$(OBJDIR_F7)/%.o) $(STARTUP_F7:$(STARTUP_DIR_F7)/%.c=$(OBJDIR_F7)/%.o) -# Find libraries -INCLUDES_LIBS=STM32F4XX_Lib/CMSIS/Lib/GCC/libarm_cortexM4lf_math.a -LINK_LIBS= +# MCU flags +MCFLAGS_F4=-mcpu=cortex-m4 -mthumb -mlittle-endian -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb-interwork +MCFLAGS_F7=-mcpu=cortex-m7 -mthumb -mlittle-endian -mfpu=fpv5-sp-d16 -mfloat-abi=hard -mthumb-interwork -# Create object list -OBJECTS=$(ASOURCES:%.s=%.o) -OBJECTS+=$(CSOURCES:%.c=%.o) -OBJECTS+=$(CXXSOURCES:%.cpp=%.o) - -# Define output files ELF & IHEX -BINELF=outp.elf -BINHEX=outp.hex -BINBIN=outp.bin - -# MCU FLAGS -MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian \ --mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb-interwork - -# COMPILE FLAGS +# Compile flags # STM32F4 Discovery board: -DEFS_DIS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_DISCOVERY -DHSE_VALUE=$(OSC) -# Pi board: -DEFS_PI=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DARDUINO_MODE_PINS -DSEND_RSSI_DATA -DSERIAL_REPEATER -DHSE_VALUE=$(OSC) -# STM32F4 Nucleo 446 board: -DEFS_NUCLEO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_NUCLEO -DHSE_VALUE=$(OSC) +DEFS_DIS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F40_41xxx -DSTM32F4_DISCOVERY -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# MMDVM-Pi board: +DEFS_PI=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# MMDVM-F4M F0DEI board: +DEFS_F4M=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_F4M -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# STM32F4 Nucleo-64 F446RE board: +DEFS_NUCLEO=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_NUCLEO -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# STM32F7 Nucleo-144 F767ZI board: +DEFS_NUCLEO_F767=-DUSE_HAL_DRIVER -DSTM32F767xx -DSTM32F7XX -DSTM32F7_NUCLEO -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# MMDVM-Pi F722 board: +DEFS_PI_F722=-DUSE_HAL_DRIVER -DSTM32F722xx -DSTM32F7XX -DSTM32F722_PI -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# MMDVM-F7M F0DEI board: +DEFS_F7M=-DUSE_HAL_DRIVER -DSTM32F722xx -DSTM32F7XX -DSTM32F722_F7M -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE +# STM32F4 DVM board: +DEFS_DVM=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_DVM -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE -CFLAGS=-c $(MCFLAGS) $(INCLUDES) -CXXFLAGS=-c $(MCFLAGS) $(INCLUDES) +# Build compiler flags +CFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4) +CXXFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4) +CFLAGS_F7=-c $(MCFLAGS_F7) $(INCLUDES_F7) +CXXFLAGS_F7=-c $(MCFLAGS_F7) $(INCLUDES_F7) -# LINKER FLAGS -LDSCRIPT=stm32f4xx_link.ld -LDFLAGS =-T $(LDSCRIPT) $(MCFLAGS) --specs=nosys.specs $(INCLUDES_LIBS) $(LINK_LIBS) +# Linker flags +LDFLAGS_F4 =-T stm32f4xx_link.ld $(MCFLAGS_F4) --specs=nosys.specs $(INCLUDES_LIBS_F4) +LDFLAGS_F767 =-T stm32f767_link.ld $(MCFLAGS_F7) --specs=nosys.specs $(INCLUDES_LIBS_F7) +LDFLAGS_F722 =-T stm32f722_link.ld $(MCFLAGS_F7) --specs=nosys.specs $(INCLUDES_LIBS_F7) + +# Common flags +CFLAGS=-Os -ffunction-sections -fdata-sections -fno-builtin -Wno-implicit -DCUSTOM_NEW -DNO_EXCEPTIONS +CXXFLAGS=-Os -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin -fno-rtti -DCUSTOM_NEW -DNO_EXCEPTIONS +LDFLAGS=-Os --specs=nano.specs # Build Rules -.PHONY: all release dis pi nucleo debug clean +.PHONY: all release dis pi pi_f722 f4m nucleo f767 dvm clean -# Default target: STM32F4 Discovery board -all: dis +# Default target: Nucleo-64 F446RE board +all: nucleo -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 +pi: GitVersion.h +pi: CFLAGS+=$(CFLAGS_F4) $(DEFS_PI) +pi: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_PI) +pi: LDFLAGS+=$(LDFLAGS_F4) +pi: release_f4 -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 +pi-f722: GitVersion.h +pi-f722: CFLAGS+=$(CFLAGS_F7) $(DEFS_PI_F722) +pi-f722: CXXFLAGS+=$(CXXFLAGS_F7) $(DEFS_PI_F722) +pi-f722: LDFLAGS+=$(LDFLAGS_F722) +pi-f722: release_f7 -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 -dis: release +f4m: GitVersion.h +f4m: CFLAGS+=$(CFLAGS_F4) $(DEFS_F4M) +f4m: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_F4M) +f4m: LDFLAGS+=$(LDFLAGS_F4) +f4m: release_f4 -debug: CFLAGS+=-g -debug: CXXFLAGS+=-g -debug: LDFLAGS+=-g -debug: release +f7m: GitVersion.h +f7m: CFLAGS+=$(CFLAGS_F7) $(DEFS_F7M) +f7m: CXXFLAGS+=$(CXXFLAGS_F7) $(DEFS_F7M) +f7m: LDFLAGS+=$(LDFLAGS_F722) +f7m: release_f7 -release: $(BINDIR) -release: $(BINDIR)/$(BINHEX) -release: $(BINDIR)/$(BINBIN) +nucleo: GitVersion.h +nucleo: CFLAGS+=$(CFLAGS_F4) $(DEFS_NUCLEO) +nucleo: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_NUCLEO) +nucleo: LDFLAGS+=$(LDFLAGS_F4) +nucleo: release_f4 + +dis: GitVersion.h +dis: CFLAGS+=$(CFLAGS_F4) $(DEFS_DIS) +dis: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_DIS) +dis: LDFLAGS+=$(LDFLAGS_F4) +dis: release_f4 + +f767: GitVersion.h +f767: CFLAGS+=$(CFLAGS_F7) $(DEFS_NUCLEO_F767) +f767: CXXFLAGS+=$(CXXFLAGS_F7) $(DEFS_NUCLEO_F767) +f767: LDFLAGS+=$(LDFLAGS_F767) +f767: release_f7 + +dvm: GitVersion.h +dvm: CFLAGS+=$(CFLAGS_F4) $(DEFS_DVM) +dvm: CXXFLAGS+=$(CXXFLAGS_F4) $(DEFS_DVM) +dvm: LDFLAGS+=$(LDFLAGS_F4) +dvm: release_f4 + +release_f4: $(BINDIR) +release_f4: $(OBJDIR_F4) +release_f4: $(BINDIR)/$(BINHEX_F4) +release_f4: $(BINDIR)/$(BINBIN_F4) + +release_f7: $(BINDIR) +release_f7: $(OBJDIR_F7) +release_f7: $(BINDIR)/$(BINHEX_F7) +release_f7: $(BINDIR)/$(BINBIN_F7) $(BINDIR): - $(MDBIN) + $(MDDIRS) -$(BINDIR)/$(BINHEX): $(BINDIR)/$(BINELF) +$(OBJDIR_F4): + $(MDDIRS) + +$(OBJDIR_F7): + $(MDDIRS) + +$(BINDIR)/$(BINHEX_F4): $(BINDIR)/$(BINELF_F4) $(CP) -O ihex $< $@ @echo "Objcopy from ELF to IHEX complete!\n" -$(BINDIR)/$(BINBIN): $(BINDIR)/$(BINELF) +$(BINDIR)/$(BINBIN_F4): $(BINDIR)/$(BINELF_F4) $(CP) -O binary $< $@ @echo "Objcopy from ELF to BINARY complete!\n" -$(BINDIR)/$(BINELF): $(OBJECTS) - $(CXX) $(OBJECTS) $(LDFLAGS) -o $@ +$(BINDIR)/$(BINELF_F4): $(OBJ_F4) + $(CXX) $(OBJ_F4) $(LDFLAGS) -o $@ @echo "Linking complete!\n" - $(SIZE) $(BINDIR)/$(BINELF) + $(SIZE) $(BINDIR)/$(BINELF_F4) -%.o: %.cpp +$(BINDIR)/$(BINHEX_F7): $(BINDIR)/$(BINELF_F7) + $(CP) -O ihex $< $@ + @echo "Objcopy from ELF to IHEX complete!\n" + +$(BINDIR)/$(BINBIN_F7): $(BINDIR)/$(BINELF_F7) + $(CP) -O binary $< $@ + @echo "Objcopy from ELF to BINARY complete!\n" + +$(BINDIR)/$(BINELF_F7): $(OBJ_F7) + $(CXX) $(OBJ_F7) $(LDFLAGS) -o $@ + @echo "Linking complete!\n" + $(SIZE) $(BINDIR)/$(BINELF_F7) + +$(OBJDIR_F4)/%.o: $(MMDVM_PATH)/%.cpp $(CXX) $(CXXFLAGS) $< -o $@ @echo "Compiled "$<"!\n" -%.o: %.c +$(OBJDIR_F7)/%.o: $(MMDVM_PATH)/%.cpp + $(CXX) $(CXXFLAGS) $< -o $@ + @echo "Compiled "$<"!\n" + +$(OBJDIR_F4)/%.o: $(STD_LIB_F4)/%.c $(CC) $(CFLAGS) $< -o $@ @echo "Compiled "$<"!\n" -%.o: %.s +$(OBJDIR_F7)/%.o: $(STD_LIB_F7)/%.c $(CC) $(CFLAGS) $< -o $@ - @echo "Assambled "$<"!\n" + @echo "Compiled "$<"!\n" + +$(OBJDIR_F4)/%.o: $(SYS_DIR_F4)/%.c + $(CC) $(CFLAGS) $< -o $@ + @echo "Compiled "$<"!\n" + +$(OBJDIR_F4)/%.o: $(STARTUP_DIR_F4)/%.c + $(CC) $(CFLAGS) $< -o $@ + @echo "Compiled "$<"!\n" + +$(OBJDIR_F7)/%.o: $(SYS_DIR_F7)/%.c + $(CC) $(CFLAGS) $< -o $@ + @echo "Compiled "$<"!\n" + +$(OBJDIR_F7)/%.o: $(STARTUP_DIR_F7)/%.c + $(CC) $(CFLAGS) $< -o $@ + @echo "Compiled "$<"!\n" clean: $(CLEANCMD) - + deploy: ifneq ($(wildcard /usr/bin/openocd),) - /usr/bin/openocd -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/$(BINELF) verify reset exit" + /usr/bin/openocd -f /usr/share/openocd/scripts/interface/stlink-v2-1.cfg -f /usr/share/openocd/scripts/target/stm32f4x.cfg -c "program bin/$(BINELF_F4) verify reset exit" endif ifneq ($(wildcard /usr/local/bin/openocd),) - /usr/local/bin/openocd -f /usr/local/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/$(BINELF) verify reset exit" + /usr/local/bin/openocd -f /usr/local/share/openocd/scripts/interface/stlink-v2-1.cfg -f /usr/local/share/openocd/scripts/target/stm32f4x.cfg -c "program bin/$(BINELF_F4) verify reset exit" 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" + /opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/interface/stlink-v2-1.cfg -f /opt/openocd/share/openocd/scripts/target/stm32f4x.cfg -c "program bin/$(BINELF_F4) verify reset exit" +endif + +deploy-f7: +ifneq ($(wildcard /usr/bin/openocd),) + /usr/bin/openocd -f /usr/share/openocd/scripts/interface/stlink-v2-1.cfg -f /usr/share/openocd/scripts/target/stm32f7x.cfg -c "program bin/$(BINELF_F7) verify reset exit" +endif + +ifneq ($(wildcard /usr/local/bin/openocd),) + /usr/local/bin/openocd -f /usr/local/share/openocd/scripts/interface/stlink-v2-1.cfg -f /usr/local/share/openocd/scripts/target/stm32f7x.cfg -c "program bin/$(BINELF_F7) verify reset exit" +endif + +ifneq ($(wildcard /opt/openocd/bin/openocd),) + /opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/interface/stlink-v2-1.cfg -f /opt/openocd/share/openocd/scripts/target/stm32f7x.cfg -c "program bin/$(BINELF_F7) 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/stm32ld /dev/ttyAMA0 57600 bin/$(BINBIN_F4) + /usr/local/bin/stm32flash -v -w bin/$(BINBIN_F4) -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/stm32ld /dev/ttyAMA0 57600 bin/$(BINBIN_F4) + /usr/bin/stm32flash -v -w bin/$(BINBIN_F4) -g 0x0 -R -c /dev/ttyAMA0 +endif + +deploy-f4m: deploy-pi +deploy-dvm: deploy-pi + +deploy-pi-f7: +ifneq ($(wildcard /usr/local/bin/stm32flash),) + -/usr/local/bin/stm32flash -i 20,-21,21:-20,21 /dev/ttyAMA0 + -/usr/local/bin/stm32ld /dev/ttyAMA0 57600 bin/$(BINBIN_F7) + /usr/local/bin/stm32flash -v -w bin/$(BINBIN_F7) -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/stm32ld /dev/ttyAMA0 57600 bin/$(BINBIN_F7) + /usr/bin/stm32flash -v -w bin/$(BINBIN_F7) -g 0x0 -R -c /dev/ttyAMA0 +endif + +deploy-f7m: deploy-pi-f7 + +# 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 diff --git a/Makefile.Arduino b/Makefile.Arduino new file mode 100644 index 0000000..6046810 --- /dev/null +++ b/Makefile.Arduino @@ -0,0 +1,189 @@ +#!/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 $(TMPDIR)/$(PROJNAME).elf $(TMPDIR)/$(PROJNAME).bin + +#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: compile + 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: diff --git a/Makefile.CMSIS b/Makefile.CMSIS new file mode 100644 index 0000000..a4ee604 --- /dev/null +++ b/Makefile.CMSIS @@ -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:=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 /dev/$(BL_PORT) + $(SIZE) $(ELF) + +.PHONY: run +run: $(HEX) $(ELF) + openocd -f openocd.cfg \ + -c "init" -c "reset" -c "exit" + +.PHONY: debug +debug: $(ELF) + ./debug.sh $(ELF) + +.PHONY: clean +clean: + $(RM) $(OBJ) $(HEX) $(ELF) $(DIS) $(MAP) $(DEPENDS) + +# 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) diff --git a/NXDNDefines.h b/NXDNDefines.h new file mode 100644 index 0000000..3d02fb3 --- /dev/null +++ b/NXDNDefines.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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(NXDNDEFINES_H) +#define NXDNDEFINES_H + +const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 20U; // At 48 kHz sample rate + +const unsigned int NXDN_FRAME_LENGTH_BITS = 384U; +const unsigned int NXDN_FRAME_LENGTH_BYTES = NXDN_FRAME_LENGTH_BITS / 8U; +const unsigned int NXDN_FRAME_LENGTH_SYMBOLS = NXDN_FRAME_LENGTH_BITS / 2U; +const unsigned int NXDN_FRAME_LENGTH_SAMPLES = NXDN_FRAME_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH; + +const unsigned int NXDN_FSW_LENGTH_BITS = 20U; +const unsigned int NXDN_FSW_LENGTH_SYMBOLS = NXDN_FSW_LENGTH_BITS / 2U; +const unsigned int NXDN_FSW_LENGTH_SAMPLES = NXDN_FSW_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH; + +const uint8_t NXDN_FSW_BYTES[] = {0xCDU, 0xF5U, 0x90U}; +const uint8_t NXDN_FSW_BYTES_MASK[] = {0xFFU, 0xFFU, 0xF0U}; +const uint8_t NXDN_FSW_BYTES_LENGTH = 3U; + +const uint32_t NXDN_FSW_BITS = 0x000CDF59U; +const uint32_t NXDN_FSW_BITS_MASK = 0x000FFFFFU; + +// C D F 5 9 +// 11 00 11 01 11 11 01 01 10 01 +// -3 +1 -3 +3 -3 -3 +3 +3 -1 +3 + +const int8_t NXDN_FSW_SYMBOLS_VALUES[] = {-3, +1, -3, +3, -3, -3, +3, +3, -1, +3}; + +const uint16_t NXDN_FSW_SYMBOLS = 0x014DU; +const uint16_t NXDN_FSW_SYMBOLS_MASK = 0x03FFU; + +#endif + diff --git a/NXDNRX.cpp b/NXDNRX.cpp new file mode 100644 index 0000000..e9bd41b --- /dev/null +++ b/NXDNRX.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 "NXDNRX.h" +#include "Utils.h" + +const q15_t SCALING_FACTOR = 18750; // Q15(0.55) + +const uint8_t MAX_FSW_BIT_START_ERRS = 1U; +const uint8_t MAX_FSW_BIT_RUN_ERRS = 3U; + +const uint8_t MAX_FSW_SYMBOLS_ERRS = 2U; + +const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) + +const uint8_t NOAVEPTR = 99U; + +const uint16_t NOENDPTR = 9999U; + +const unsigned int MAX_FSW_FRAMES = 5U + 1U; + +CNXDNRX::CNXDNRX() : +m_state(NXDNRXS_NONE), +m_bitBuffer(), +m_buffer(), +m_bitPtr(0U), +m_dataPtr(0U), +m_startPtr(NOENDPTR), +m_endPtr(NOENDPTR), +m_fswPtr(NOENDPTR), +m_minFSWPtr(NOENDPTR), +m_maxFSWPtr(NOENDPTR), +m_maxCorr(0), +m_lostCount(0U), +m_countdown(0U), +m_centre(), +m_centreVal(0), +m_threshold(), +m_thresholdVal(0), +m_averagePtr(NOAVEPTR), +m_rssiAccum(0U), +m_rssiCount(0U) +{ +} + +void CNXDNRX::reset() +{ + m_state = NXDNRXS_NONE; + m_dataPtr = 0U; + m_bitPtr = 0U; + m_maxCorr = 0; + m_averagePtr = NOAVEPTR; + m_startPtr = NOENDPTR; + m_endPtr = NOENDPTR; + m_fswPtr = NOENDPTR; + m_minFSWPtr = NOENDPTR; + m_maxFSWPtr = NOENDPTR; + m_centreVal = 0; + m_thresholdVal = 0; + m_lostCount = 0U; + m_countdown = 0U; + m_rssiAccum = 0U; + m_rssiCount = 0U; +} + +void CNXDNRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length) +{ + for (uint8_t i = 0U; i < length; i++) { + q15_t sample = samples[i]; + + m_rssiAccum += rssi[i]; + m_rssiCount++; + + m_bitBuffer[m_bitPtr] <<= 1; + if (sample < 0) + m_bitBuffer[m_bitPtr] |= 0x01U; + + m_buffer[m_dataPtr] = sample; + + switch (m_state) { + case NXDNRXS_DATA: + processData(sample); + break; + default: + processNone(sample); + break; + } + + m_dataPtr++; + if (m_dataPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_dataPtr = 0U; + + m_bitPtr++; + if (m_bitPtr >= NXDN_RADIO_SYMBOL_LENGTH) + m_bitPtr = 0U; + } +} + +void CNXDNRX::processNone(q15_t sample) +{ + bool ret = correlateFSW(); + if (ret) { + // On the first sync, start the countdown to the state change + if (m_countdown == 0U) { + m_rssiAccum = 0U; + m_rssiCount = 0U; + + io.setDecode(true); + io.setADCDetection(true); + + m_averagePtr = NOAVEPTR; + + m_countdown = 5U; + } + } + + if (m_countdown > 0U) + m_countdown--; + + if (m_countdown == 1U) { + m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_maxFSWPtr = m_fswPtr + 1U; + if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_state = NXDNRXS_DATA; + m_countdown = 0U; + } +} + +void CNXDNRX::processData(q15_t sample) +{ + if (m_minFSWPtr < m_maxFSWPtr) { + if (m_dataPtr >= m_minFSWPtr && m_dataPtr <= m_maxFSWPtr) + correlateFSW(); + } else { + if (m_dataPtr >= m_minFSWPtr || m_dataPtr <= m_maxFSWPtr) + correlateFSW(); + } + + if (m_dataPtr == m_endPtr) { + // Only update the centre and threshold if they are from a good sync + if (m_lostCount == MAX_FSW_FRAMES) { + m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_maxFSWPtr = m_fswPtr + 1U; + if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + } + + calculateLevels(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS); + + DEBUG4("NXDNRX: sync found pos/centre/threshold", m_fswPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 3U]; + samplesToBits(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + // We've not seen a data sync for too long, signal RXLOST and change to RX_NONE + m_lostCount--; + if (m_lostCount == 0U) { + DEBUG1("NXDNRX: sync timed out, lost lock"); + + io.setDecode(false); + io.setADCDetection(false); + + serial.writeNXDNLost(); + + m_state = NXDNRXS_NONE; + m_endPtr = NOENDPTR; + m_averagePtr = NOAVEPTR; + m_countdown = 0U; + m_maxCorr = 0; + } else { + frame[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; + writeRSSIData(frame); + m_maxCorr = 0; + } + } +} + +bool CNXDNRX::correlateFSW() +{ + if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_FSW_SYMBOLS_MASK) ^ NXDN_FSW_SYMBOLS) <= MAX_FSW_SYMBOLS_ERRS) { + uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) + ptr -= NXDN_FRAME_LENGTH_SAMPLES; + + q31_t corr = 0; + q15_t min = 16000; + q15_t max = -16000; + + for (uint8_t i = 0U; i < NXDN_FSW_LENGTH_SYMBOLS; i++) { + q15_t val = m_buffer[ptr]; + + if (val > max) + max = val; + if (val < min) + min = val; + + switch (NXDN_FSW_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 += NXDN_RADIO_SYMBOL_LENGTH; + if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) + ptr -= NXDN_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 + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (startPtr >= NXDN_FRAME_LENGTH_SAMPLES) + startPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + uint8_t sync[NXDN_FSW_BYTES_LENGTH]; + samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); + + uint8_t maxErrs; + if (m_state == NXDNRXS_NONE) + maxErrs = MAX_FSW_BIT_START_ERRS; + else + maxErrs = MAX_FSW_BIT_RUN_ERRS; + + uint8_t errs = 0U; + for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) + errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]); + + if (errs <= maxErrs) { + m_maxCorr = corr; + m_lostCount = MAX_FSW_FRAMES; + m_fswPtr = m_dataPtr; + + m_startPtr = startPtr; + + m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES - 1U; + if (m_endPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_endPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + return true; + } + } + } + + return false; +} + +void CNXDNRX::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 += NXDN_RADIO_SYMBOL_LENGTH; + if (start >= NXDN_FRAME_LENGTH_SAMPLES) + start -= NXDN_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("NXDNRX: 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 CNXDNRX::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 += NXDN_RADIO_SYMBOL_LENGTH; + if (start >= NXDN_FRAME_LENGTH_SAMPLES) + start -= NXDN_FRAME_LENGTH_SAMPLES; + } +} + +void CNXDNRX::writeRSSIData(uint8_t* data) +{ +#if defined(SEND_RSSI_DATA) + if (m_rssiCount > 0U) { + uint16_t rssi = m_rssiAccum / m_rssiCount; + + data[49U] = (rssi >> 8) & 0xFFU; + data[50U] = (rssi >> 0) & 0xFFU; + + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 3U); + } else { + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U); + } +#else + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U); +#endif + + m_rssiAccum = 0U; + m_rssiCount = 0U; +} + diff --git a/NXDNRX.h b/NXDNRX.h new file mode 100644 index 0000000..1bff2ac --- /dev/null +++ b/NXDNRX.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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(NXDNRX_H) +#define NXDNRX_H + +#include "Config.h" +#include "NXDNDefines.h" + +enum NXDNRX_STATE { + NXDNRXS_NONE, + NXDNRXS_DATA +}; + +class CNXDNRX { +public: + CNXDNRX(); + + void samples(const q15_t* samples, uint16_t* rssi, uint8_t length); + + void reset(); + +private: + NXDNRX_STATE m_state; + uint16_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH]; + q15_t m_buffer[NXDN_FRAME_LENGTH_SAMPLES]; + uint16_t m_bitPtr; + uint16_t m_dataPtr; + uint16_t m_startPtr; + uint16_t m_endPtr; + uint16_t m_fswPtr; + uint16_t m_minFSWPtr; + uint16_t m_maxFSWPtr; + q31_t m_maxCorr; + uint16_t m_lostCount; + uint8_t m_countdown; + q15_t m_centre[16U]; + q15_t m_centreVal; + q15_t m_threshold[16U]; + q15_t m_thresholdVal; + uint8_t m_averagePtr; + uint32_t m_rssiAccum; + uint16_t m_rssiCount; + + void processNone(q15_t sample); + void processData(q15_t sample); + bool correlateFSW(); + 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 + diff --git a/NXDNTX.cpp b/NXDNTX.cpp new file mode 100644 index 0000000..de2dbc7 --- /dev/null +++ b/NXDNTX.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX + * Copyright (C) 2017 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "NXDNTX.h" + +#include "NXDNDefines.h" + +// Generated using rcosdesign(0.2, 8, 20, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 174, 140, 99, 52, 0, -55, -112, -170, -226, -278, -325, -365, -397, -417, -427, -424, -407, -377, -333, -277, -208, + -128, -40, 56, 156, 258, 358, 455, 544, 622, 687, 736, 766, 775, 762, 725, 664, 579, 471, 342, 193, 27, -151, -338, -528, + -718, -901, -1072, -1225, -1354, -1454, -1520, -1547, -1530, -1466, -1353, -1189, -972, -704, -385, -18, 394, 846, 1333, + 1850, 2388, 2940, 3498, 4053, 4598, 5122, 5619, 6079, 6494, 6859, 7166, 7410, 7588, 7696, 7732, 7696, 7588, 7410, 7166, + 6859, 6494, 6079, 5619, 5122, 4598, 4053, 3498, 2940, 2388, 1850, 1333, 846, 394, -18, -385, -704, -972, -1189, -1353, + -1466, -1530, -1547, -1520, -1454, -1354, -1225, -1072, -901, -718, -528, -338, -151, 27, 193, 342, 471, 579, 664, 725, + 762, 775, 766, 736, 687, 622, 544, 455, 358, 258, 156, 56, -40, -128, -208, -277, -333, -377, -407, -424, -427, -417, -397, + -365, -325, -278, -226, -170, -112, -55, 0, 52, 99, 140, 174, 201}; // numTaps = 180, L = 20 +const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L + +static q15_t NXDN_SINC_FILTER[] = {572, -1003, -253, 254, 740, 1290, 1902, 2527, 3090, 3517, 3747, 3747, 3517, 3090, 2527, 1902, + 1290, 740, 254, -253, -1003, 572}; +const uint16_t NXDN_SINC_FILTER_LEN = 22U; + +const q15_t NXDN_LEVELA = 735; +const q15_t NXDN_LEVELB = 245; +const q15_t NXDN_LEVELC = -245; +const q15_t NXDN_LEVELD = -735; + +const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; +const uint8_t NXDN_SYNC = 0x5FU; + +CNXDNTX::CNXDNTX() : +m_buffer(4000U), +m_modFilter(), +m_sincFilter(), +m_modState(), +m_sincState(), +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_txDelay(240U) // 200ms +{ + ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); + ::memset(m_sincState, 0x00U, 70U * sizeof(q15_t)); + + m_modFilter.L = NXDN_RADIO_SYMBOL_LENGTH; + m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN; + m_modFilter.pCoeffs = RRC_0_2_FILTER; + m_modFilter.pState = m_modState; + + m_sincFilter.numTaps = NXDN_SINC_FILTER_LEN; + m_sincFilter.pState = m_sincState; + m_sincFilter.pCoeffs = NXDN_SINC_FILTER; +} + +void CNXDNTX::process() +{ + if (m_buffer.getData() == 0U && m_poLen == 0U) + return; + + if (m_poLen == 0U) { + if (!m_tx) { + for (uint16_t i = 0U; i < m_txDelay; i++) + m_poBuffer[m_poLen++] = NXDN_SYNC; + m_poBuffer[m_poLen++] = NXDN_PREAMBLE[0U]; + m_poBuffer[m_poLen++] = NXDN_PREAMBLE[1U]; + m_poBuffer[m_poLen++] = NXDN_PREAMBLE[2U]; + } else { + for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) { + uint8_t c = m_buffer.get(); + m_poBuffer[m_poLen++] = c; + } + } + + m_poPtr = 0U; + } + + if (m_poLen > 0U) { + uint16_t space = io.getSpace(); + + while (space > (4U * NXDN_RADIO_SYMBOL_LENGTH)) { + uint8_t c = m_poBuffer[m_poPtr++]; + writeByte(c); + + space -= 4U * NXDN_RADIO_SYMBOL_LENGTH; + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + return; + } + } + } +} + +uint8_t CNXDNTX::writeData(const uint8_t* data, uint8_t length) +{ + if (length != (NXDN_FRAME_LENGTH_BYTES + 1U)) + return 4U; + + uint16_t space = m_buffer.getSpace(); + if (space < NXDN_FRAME_LENGTH_BYTES) + return 5U; + + for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) + m_buffer.put(data[i + 1U]); + + return 0U; +} + +void CNXDNTX::writeByte(uint8_t c) +{ + q15_t inBuffer[4U]; + q15_t intBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; + q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; + + const uint8_t MASK = 0xC0U; + + for (uint8_t i = 0U; i < 4U; i++, c <<= 2) { + switch (c & MASK) { + case 0xC0U: + inBuffer[i] = NXDN_LEVELA; + break; + case 0x80U: + inBuffer[i] = NXDN_LEVELB; + break; + case 0x00U: + inBuffer[i] = NXDN_LEVELC; + break; + default: + inBuffer[i] = NXDN_LEVELD; + break; + } + } + + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U); + + ::arm_fir_fast_q15(&m_sincFilter, intBuffer, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); + + io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); +} + +void CNXDNTX::setTXDelay(uint8_t delay) +{ + m_txDelay = 300U + uint16_t(delay) * 6U; // 500ms + tx delay + + if (m_txDelay > 1200U) + m_txDelay = 1200U; +} + +uint8_t CNXDNTX::getSpace() const +{ + return m_buffer.getSpace() / NXDN_FRAME_LENGTH_BYTES; +} + diff --git a/NXDNTX.h b/NXDNTX.h new file mode 100644 index 0000000..eec74d4 --- /dev/null +++ b/NXDNTX.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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(NXDNTX_H) +#define NXDNTX_H + +#include "Config.h" + +#include "SerialRB.h" + +class CNXDNTX { +public: + CNXDNTX(); + + uint8_t writeData(const uint8_t* data, uint8_t length); + + void process(); + + void setTXDelay(uint8_t delay); + + uint8_t getSpace() const; + +private: + CSerialRB m_buffer; + arm_fir_interpolate_instance_q15 m_modFilter; + arm_fir_instance_q15 m_sincFilter; + q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare + q15_t m_sincState[70U]; // NoTaps + BlockSize - 1, 22 + 40 - 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); +}; + +#endif + diff --git a/P25Defines.h b/P25Defines.h index 897dfb8..0e8a95e 100644 --- a/P25Defines.h +++ b/P25Defines.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2016 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Bryan Biedenkapp * * 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 +25,37 @@ const unsigned int P25_RADIO_SYMBOL_LENGTH = 10U; // At 48 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_TSDU_FRAME_LENGTH_BYTES = 45U; +const unsigned int P25_TSDU_FRAME_LENGTH_BITS = P25_TSDU_FRAME_LENGTH_BYTES * 8U; +const unsigned int P25_TSDU_FRAME_LENGTH_SYMBOLS = P25_TSDU_FRAME_LENGTH_BYTES * 4U; +const unsigned int P25_TSDU_FRAME_LENGTH_SAMPLES = P25_TSDU_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_BYTES = 8U; +const unsigned int P25_NID_LENGTH_BITS = P25_NID_LENGTH_BYTES * 8U; +const unsigned int P25_NID_LENGTH_SYMBOLS = P25_NID_LENGTH_BYTES * 4U; +const unsigned int P25_NID_LENGTH_SAMPLES = 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,8 +67,18 @@ 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; +const uint8_t P25_DUID_HDU = 0x00U; // Header Data Unit +const uint8_t P25_DUID_TDU = 0x03U; // Simple Terminator Data Unit +const uint8_t P25_DUID_LDU1 = 0x05U; // Logical Link Data Unit 1 +const uint8_t P25_DUID_TSDU = 0x07U; // Trunking System Data Unit +const uint8_t P25_DUID_LDU2 = 0x0AU; // Logical Link Data Unit 2 +const uint8_t P25_DUID_PDU = 0x0CU; // Packet Data Unit +const uint8_t P25_DUID_TDULC = 0x0FU; // Terminator Data Unit with Link Control + #endif diff --git a/P25RX.cpp b/P25RX.cpp index bb7ad25..54d4237 100644 --- a/P25RX.cpp +++ b/P25RX.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2016 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Bryan Biedenkapp * * 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 +17,270 @@ * 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 CORRELATION_COUNTDOWN = 10U;//5U; -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_BIT_START_ERRS = 2U; +const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U; -const uint8_t SYNC_SYMBOL_ERRS = 0U; +const uint8_t MAX_SYNC_SYMBOLS_ERRS = 2U; -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_duid(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; + m_duid = 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_pll += PLLINC; - - if (m_pll >= PLLMAX) { - m_pll -= PLLMAX; - - if (m_state == P25RXS_NONE) - processNone(samples[i]); - else - processData(samples[i]); + m_dataPtr++; + if (m_dataPtr >= P25_LDU_FRAME_LENGTH_SAMPLES) { + m_dataPtr = 0U; + m_duid = 0U; } + + 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 = CORRELATION_COUNTDOWN; } } - 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) { + uint16_t nidStartPtr = m_hdrStartPtr + P25_SYNC_LENGTH_SAMPLES; + if (nidStartPtr >= P25_LDU_FRAME_LENGTH_SAMPLES) + nidStartPtr -= P25_LDU_FRAME_LENGTH_SAMPLES; - m_outBuffer[0U] = 0x01U; - serial.writeP25Hdr(m_outBuffer, P25_HDR_FRAME_LENGTH_BYTES + 1U); + uint8_t nid[2U]; + samplesToBits(nidStartPtr, (2U * 4U), nid, 0U, m_centreVal, m_thresholdVal); + // DEBUG3("P25RX: nid (b0 - b1)", nid[0U], nid[1U]); - // 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]; + m_duid = nid[1U] & 0x0F; - m_lostCount = MAX_SYNC_FRAMES; - m_bufferPtr = P25_SYNC_LENGTH_BITS; + switch (m_duid) { + case P25_DUID_HDU: { + calculateLevels(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS); + + DEBUG4("P25RX: sync found in Hdr pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[P25_HDR_FRAME_LENGTH_BYTES + 1U]; + samplesToBits(m_hdrStartPtr, P25_HDR_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + frame[0U] = 0x01U; + serial.writeP25Hdr(frame, P25_HDR_FRAME_LENGTH_BYTES + 1U); + } + break; + case P25_DUID_TSDU: { + calculateLevels(m_hdrStartPtr, P25_TSDU_FRAME_LENGTH_SYMBOLS); + + DEBUG4("P25RX: sync found in TSDU pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[P25_TSDU_FRAME_LENGTH_BYTES + 1U]; + samplesToBits(m_hdrStartPtr, P25_TSDU_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + frame[0U] = 0x01U; + serial.writeP25Hdr(frame, P25_TSDU_FRAME_LENGTH_BYTES + 1U); + } + break; + case P25_DUID_TDU: { + calculateLevels(m_hdrStartPtr, P25_TERM_FRAME_LENGTH_SYMBOLS); + + DEBUG4("P25RX: sync found in TDU pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[P25_TERM_FRAME_LENGTH_BYTES + 1U]; + samplesToBits(m_hdrStartPtr, P25_TERM_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + frame[0U] = 0x01U; + serial.writeP25Hdr(frame, P25_TERM_FRAME_LENGTH_BYTES + 1U); + } + break; + case P25_DUID_TDULC: { + calculateLevels(m_hdrStartPtr, P25_TERMLC_FRAME_LENGTH_SYMBOLS); + + DEBUG4("P25RX: sync found in TDULC pos/centre/threshold", m_hdrSyncPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[P25_TERMLC_FRAME_LENGTH_BYTES + 1U]; + samplesToBits(m_hdrStartPtr, P25_TERMLC_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + frame[0U] = 0x01U; + serial.writeP25Hdr(frame, P25_TERMLC_FRAME_LENGTH_BYTES + 1U); + } + break; + default: + break; } + + 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 +291,235 @@ 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; + m_duid = 0U; + } 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; +} diff --git a/P25RX.h b/P25RX.h index 691763d..7cab1dd 100644 --- a/P25RX.h +++ b/P25RX.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Bryan Biedenkapp * * 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 +25,50 @@ 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; + uint8_t m_duid; 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 - diff --git a/P25TX.cpp b/P25TX.cpp index 70567e5..737adef 100644 --- a/P25TX.cpp +++ b/P25TX.cpp @@ -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,37 +17,39 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// #define WANT_DEBUG - #include "Config.h" #include "Globals.h" #include "P25TX.h" #include "P25Defines.h" -// Generated using rcosdesign(0.2, 4, 10, 'sqrt') in MATLAB -static q15_t P25_C4FSK_FILTER[] = {486, 39, -480, -1022, -1526, -1928, -2164, -2178, -1927, -1384, -548, 561, 1898, 3399, 4980, 6546, 7999, 9246, 10202, 10803, 11008, 10803, 10202, 9246, - 7999, 6546, 4980, 3399, 1898, 561, -548, -1384, -1927, -2178, -2164, -1928, -1526, -1022, -480, 39, 486, 0}; -const uint16_t P25_C4FSK_FILTER_LEN = 42U; +// Generated using rcosdesign(0.2, 8, 10, 'normal') in MATLAB +static q15_t RC_0_2_FILTER[] = {-444, -897, -1311, -1636, -1825, -1840, -1659, -1278, -712, 0, + 800, 1613, 2354, 2936, 3277, 3310, 2994, 2315, 1296, 0, -1478, + -3011, -4448, -5627, -6386, -6580, -6090, -4839, -2800, 0, 3474, + 7482, 11835, 16311, 20666, 24651, 28032, 30607, 32219, 32767, + 32219, 30607, 28032, 24651, 20666, 16311, 11835, 7482, 3474, 0, + -2800, -4839, -6090, -6580, -6386, -5627, -4448, -3011, -1478, + 0, 1296, 2315, 2994, 3310, 3277, 2936, 2354, 1613, 800, 0, -712, + -1278, -1659, -1840, -1825, -1636, -1311, -897, -444, 0}; // numTaps = 80, L = 10 +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[] = {1294, -2251, 4312, -8402, 20999, 20999, -8402, 4312, -2251, 1294}; +const uint16_t LOWPASS_FILTER_LEN = 10U; -const q15_t P25_LEVELA[] = { 305, 305, 305, 305, 305, 305, 305, 305, 305, 305}; -const q15_t P25_LEVELB[] = { 102, 102, 102, 102, 102, 102, 102, 102, 102, 102}; -const q15_t P25_LEVELC[] = {-102, -102, -102, -102, -102, -102, -102, -102, -102, -102}; -const q15_t P25_LEVELD[] = {-305, -305, -305, -305, -305, -305, -305, -305, -305, -305}; +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; CP25TX::CP25TX() : -m_buffer(1500U), +m_buffer(4000U), m_modFilter(), m_lpFilter(), m_modState(), @@ -54,19 +57,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, 90U * sizeof(q15_t)); - ::memset(m_lpState, 0x00U, 90U * sizeof(q15_t)); + ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); + ::memset(m_lpState, 0x00U, 60U * 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() @@ -76,8 +79,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 { @@ -127,61 +128,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; } diff --git a/P25TX.h b/P25TX.h index ef93f2e..1d542f9 100644 --- a/P25TX.h +++ b/P25TX.h @@ -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[90U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare - q15_t m_lpState[90U]; // 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[60U]; // NoTaps + BlockSize - 1, 32 + 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); }; diff --git a/README.md b/README.md index 030f94c..e596822 100644 --- a/README.md +++ b/README.md @@ -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, P25, and NXDN 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. diff --git a/RSSIRB.cpp b/RSSIRB.cpp index 98a519b..7607773 100644 --- a/RSSIRB.cpp +++ b/RSSIRB.cpp @@ -22,7 +22,6 @@ Boston, MA 02110-1301, USA. CRSSIRB::CRSSIRB(uint16_t length) : m_length(length), -m_rssi(NULL), m_head(0U), m_tail(0U), m_full(false), @@ -101,4 +100,3 @@ bool CRSSIRB::hasOverflowed() return overflow; } - diff --git a/RSSIRB.h b/RSSIRB.h index 111a3ef..f3db3de 100644 --- a/RSSIRB.h +++ b/RSSIRB.h @@ -21,8 +21,12 @@ Boston, MA 02110-1301, USA. #if !defined(RSSIRB_H) #define RSSIRB_H -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) #include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" #include #else #include diff --git a/RingBuff.h b/RingBuff.h new file mode 100644 index 0000000..cc15605 --- /dev/null +++ b/RingBuff.h @@ -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 . + * + * 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 diff --git a/STM32Utils.h b/STM32Utils.h new file mode 100644 index 0000000..9175324 --- /dev/null +++ b/STM32Utils.h @@ -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 + +/* 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 diff --git a/SampleRB.cpp b/SampleRB.cpp index dd9154f..5fa2ef6 100644 --- a/SampleRB.cpp +++ b/SampleRB.cpp @@ -22,8 +22,6 @@ Boston, MA 02110-1301, USA. CSampleRB::CSampleRB(uint16_t length) : m_length(length), -m_samples(NULL), -m_control(NULL), m_head(0U), m_tail(0U), m_full(false), diff --git a/SampleRB.h b/SampleRB.h index 16d3441..e0b6269 100644 --- a/SampleRB.h +++ b/SampleRB.h @@ -21,8 +21,12 @@ Boston, MA 02110-1301, USA. #if !defined(SAMPLERB_H) #define SAMPLERB_H -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) #include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" #include #else #include diff --git a/SerialArduino.cpp b/SerialArduino.cpp index e723fee..7240a41 100644 --- a/SerialArduino.cpp +++ b/SerialArduino.cpp @@ -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) { diff --git a/SerialPort.cpp b/SerialPort.cpp index 41588c1..d3ccfb6 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013,2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2013,2015,2016,2017,2018 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; @@ -56,21 +60,35 @@ const uint8_t MMDVM_P25_HDR = 0x30U; const uint8_t MMDVM_P25_LDU = 0x31U; const uint8_t MMDVM_P25_LOST = 0x32U; +const uint8_t MMDVM_NXDN_DATA = 0x40U; +const uint8_t MMDVM_NXDN_LOST = 0x41U; + const uint8_t MMDVM_ACK = 0x70U; const uint8_t MMDVM_NAK = 0x7FU; const uint8_t MMDVM_SERIAL = 0x80U; +const uint8_t MMDVM_TRANSPARENT = 0x90U; + const uint8_t MMDVM_DEBUG1 = 0xF1U; const uint8_t MMDVM_DEBUG2 = 0xF2U; 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 48kHz (D-Star/DMR/System Fusion/P25/CW Id)"; +#define DESCRIPTION "MMDVM 20180517 TCXO 48Khz (D-Star/DMR/System Fusion/P25/NXDN)" #else -const uint8_t HARDWARE[] = "MMDVM 20161124 48kHz (D-Star/DMR/System Fusion/P25/CW Id)"; +#define DESCRIPTION "MMDVM 20180517 48Khz (D-Star/DMR/System Fusion/P25/NXDN)" +#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 +97,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() { } @@ -128,6 +148,8 @@ void CSerialPort::getStatus() reply[3U] |= 0x04U; if (m_p25Enable) reply[3U] |= 0x08U; + if (m_nxdnEnable) + reply[3U] |= 0x10U; reply[4U] = uint8_t(m_modemState); @@ -182,7 +204,12 @@ void CSerialPort::getStatus() else reply[10U] = 0U; - writeInt(1U, reply, 11); + if (m_nxdnEnable) + reply[11U] = nxdnTX.getSpace(); + else + reply[11U] = 0U; + + writeInt(1U, reply, 12); } void CSerialPort::getVersion() @@ -206,18 +233,22 @@ void CSerialPort::getVersion() uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) { - if (length < 13U) + if (length < 16U) return 4U; bool rxInvert = (data[0U] & 0x01U) == 0x01U; bool txInvert = (data[0U] & 0x02U) == 0x02U; bool pttInvert = (data[0U] & 0x04U) == 0x04U; + bool ysfLoDev = (data[0U] & 0x08U) == 0x08U; bool simplex = (data[0U] & 0x80U) == 0x80U; + m_debug = (data[0U] & 0x10U) == 0x10U; + bool dstarEnable = (data[1U] & 0x01U) == 0x01U; bool dmrEnable = (data[1U] & 0x02U) == 0x02U; bool ysfEnable = (data[1U] & 0x04U) == 0x04U; bool p25Enable = (data[1U] & 0x08U) == 0x08U; + bool nxdnEnable = (data[1U] & 0x10U) == 0x10U; uint8_t txDelay = data[2U]; if (txDelay > 50U) @@ -225,7 +256,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_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -235,6 +266,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) return 4U; if (modemState == STATE_P25 && !p25Enable) return 4U; + if (modemState == STATE_NXDN && !nxdnEnable) + return 4U; uint8_t rxLevel = data[4U]; @@ -244,36 +277,31 @@ 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]; uint8_t ysfTXLevel = data[11U]; uint8_t p25TXLevel = data[12U]; + int16_t txDCOffset = int16_t(data[13U]) - 128; + int16_t rxDCOffset = int16_t(data[14U]) - 128; + + uint8_t nxdnTXLevel = data[15U]; + m_modemState = modemState; m_dstarEnable = dstarEnable; m_dmrEnable = dmrEnable; m_ysfEnable = ysfEnable; m_p25Enable = p25Enable; + m_nxdnEnable = nxdnEnable; m_duplex = !simplex; dstarTX.setTXDelay(txDelay); ysfTX.setTXDelay(txDelay); p25TX.setTXDelay(txDelay); dmrDMOTX.setTXDelay(txDelay); + nxdnTX.setTXDelay(txDelay); dmrTX.setColorCode(colorCode); dmrRX.setColorCode(colorCode); @@ -281,7 +309,9 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) dmrDMORX.setColorCode(colorCode); dmrIdleRX.setColorCode(colorCode); - io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel); + ysfTX.setLoDev(ysfLoDev); + + io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, txDCOffset, rxDCOffset); io.start(); @@ -298,7 +328,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_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -308,6 +338,8 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) return 4U; if (modemState == STATE_P25 && !m_p25Enable) return 4U; + if (modemState == STATE_NXDN && !m_nxdnEnable) + return 4U; setMode(modemState); @@ -322,6 +354,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_DSTAR: @@ -331,6 +364,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_YSF: @@ -340,6 +374,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrRX.reset(); dstarRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_P25: @@ -349,6 +384,17 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrRX.reset(); dstarRX.reset(); ysfRX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_NXDN: + DEBUG1("Mode set to NXDN"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); cwIdTX.reset(); break; case STATE_DSTARCAL: @@ -359,6 +405,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_DMRCAL: @@ -369,6 +416,73 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.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(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_LFCAL: + DEBUG1("Mode set to 80 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_DMRCAL1K: + DEBUG1("Mode set to DMR BS 1031 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_P25CAL1K: + DEBUG1("Mode set to P25 1011 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_DMRDMO1K: + DEBUG1("Mode set to DMR MS 1031 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_NXDNCAL1K: + DEBUG1("Mode set to NXDN 1031 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; default: @@ -403,6 +517,10 @@ void CSerialPort::process() m_ptr = 1U; m_len = 0U; } + else { + m_ptr = 0U; + m_len = 0U; + } } else if (m_ptr == 1U) { // Handle the frame length m_len = m_buffer[m_ptr] = c; @@ -448,8 +566,12 @@ void CSerialPort::process() case MMDVM_CAL_DATA: if (m_modemState == STATE_DSTARCAL) err = calDStarTX.write(m_buffer + 3U, m_len - 3U); - if (m_modemState == STATE_DMRCAL) + if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) err = calDMR.write(m_buffer + 3U, m_len - 3U); + if (m_modemState == STATE_P25CAL1K) + err = calP25.write(m_buffer + 3U, m_len - 3U); + if (m_modemState == STATE_NXDNCAL1K) + err = calNXDN.write(m_buffer + 3U, m_len - 3U); if (err == 0U) { sendACK(); } else { @@ -625,9 +747,29 @@ void CSerialPort::process() } break; + case MMDVM_NXDN_DATA: + if (m_nxdnEnable) { + if (m_modemState == STATE_IDLE || m_modemState == STATE_NXDN) + err = nxdnTX.writeData(m_buffer + 3U, m_len - 3U); + } + if (err == 0U) { + if (m_modemState == STATE_IDLE) + setMode(STATE_NXDN); + } else { + DEBUG2("Received invalid NXDN data", err); + sendNAK(err); + } + break; + + case MMDVM_TRANSPARENT: + // Do nothing on the MMDVM. + 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 @@ -643,8 +785,26 @@ void CSerialPort::process() } } + if (io.getWatchdog() >= 96000U) { + m_ptr = 0U; + m_len = 0U; + } + #if defined(SERIAL_REPEATER) - // Drain any incoming serial data + // 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 @@ -872,6 +1032,46 @@ void CSerialPort::writeP25Lost() writeInt(1U, reply, 3); } +void CSerialPort::writeNXDNData(const uint8_t* data, uint8_t length) +{ + if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE) + return; + + if (!m_nxdnEnable) + return; + + uint8_t reply[130U]; + + reply[0U] = MMDVM_FRAME_START; + reply[1U] = 0U; + reply[2U] = MMDVM_NXDN_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::writeNXDNLost() +{ + if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE) + return; + + if (!m_nxdnEnable) + return; + + uint8_t reply[3U]; + + reply[0U] = MMDVM_FRAME_START; + reply[1U] = 3U; + reply[2U] = MMDVM_NXDN_LOST; + + writeInt(1U, reply, 3); +} + void CSerialPort::writeCalData(const uint8_t* data, uint8_t length) { if (m_modemState != STATE_DSTARCAL) @@ -892,8 +1092,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 +1134,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 +1157,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 +1183,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 +1212,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 +1241,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); -} - diff --git a/SerialPort.h b/SerialPort.h index 455dfdb..728b6b3 100644 --- a/SerialPort.h +++ b/SerialPort.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include "Config.h" #include "Globals.h" +#include "SerialRB.h" class CSerialPort { @@ -46,7 +47,11 @@ public: void writeP25Ldu(const uint8_t* data, uint8_t length); void writeP25Lost(); + void writeNXDNData(const uint8_t* data, uint8_t length); + void writeNXDNLost(); + 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 +59,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,6 +77,7 @@ 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); }; diff --git a/SerialRB.cpp b/SerialRB.cpp index 5f917d5..f6b1578 100644 --- a/SerialRB.cpp +++ b/SerialRB.cpp @@ -22,7 +22,6 @@ Boston, MA 02110-1301, USA. CSerialRB::CSerialRB(uint16_t length) : m_length(length), -m_buffer(NULL), m_head(0U), m_tail(0U), m_full(false) diff --git a/SerialRB.h b/SerialRB.h index 1671b23..4378cc9 100644 --- a/SerialRB.h +++ b/SerialRB.h @@ -21,14 +21,18 @@ Boston, MA 02110-1301, USA. #if !defined(SERIALRB_H) #define SERIALRB_H -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) #include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" #include #else #include #endif -const uint16_t SERIAL_RINGBUFFER_SIZE = 370U; +const uint16_t SERIAL_RINGBUFFER_SIZE = 2000U; class CSerialRB { public: diff --git a/SerialSTM.cpp b/SerialSTM.cpp index 904ec10..9eeb53e 100644 --- a/SerialSTM.cpp +++ b/SerialSTM.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM - * Copyright (C) 2016 by Andy Uribe CA6JAU + * Copyright (C) 2016,2017,2018 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 @@ -26,31 +27,31 @@ Pin definitions: - Host communication: -USART1 - TXD PA9 - RXD PA10 (Pi board) -USART2 - TXD PA2 - RXD PA3 (Nucleo board) +USART1 - TXD PA9 - RXD PA10 (MMDVM-Pi board, MMDVM-Pi F722 board, MMDVM-F4M board, STM32F722-F7M board, STM32F4-DVM board) +USART2 - TXD PA2 - RXD PA3 (Nucleo64 F446RE board, Morpho or Arduino header) USART3 - TXD PC10 - RXD PC11 (Discovery board) +USART3 - TXD PD8 - RXD PD9 (Nucleo144 F767ZI board) - Serial repeater: -UART5 - TXD PC12 - RXD PD2 - +USART1 - TXD PA9 - RXD PA10 (Nucleo with Arduino header) +UART5 - TXD PC12 - RXD PD2 (Discovery, MMDVM-Pi, MMDVM-Pi F722 board, MMDVM-F4M board, STM32F722-F7M board, STM32F4-DVM board, Nucleo64 with Morpho header and Nucleo144 F767ZI) */ -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) || defined(STM32F7XX) -#define TX_SERIAL_FIFO_SIZE 256U -#define RX_SERIAL_FIFO_SIZE 256U +#define TX_SERIAL_FIFO_SIZE 512U +#define RX_SERIAL_FIFO_SIZE 512U extern "C" { - void USART1_IRQHandler(); - void USART2_IRQHandler(); - void USART3_IRQHandler(); - void UART5_IRQHandler(); + void USART1_IRQHandler(); + void USART2_IRQHandler(); + void USART3_IRQHandler(); + void UART5_IRQHandler(); } /* ************* USART1 ***************** */ -#if defined(STM32F4_PI) +#if defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_F7M) || defined(STM32F722_PI) || defined(STM32F4_DVM) || (defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)) -volatile uint32_t intcount1; volatile uint8_t TXSerialfifo1[TX_SERIAL_FIFO_SIZE]; volatile uint8_t RXSerialfifo1[RX_SERIAL_FIFO_SIZE]; volatile uint16_t TXSerialfifohead1, TXSerialfifotail1; @@ -59,178 +60,182 @@ volatile uint16_t RXSerialfifohead1, RXSerialfifotail1; // Init queues void TXSerialfifoinit1() { - TXSerialfifohead1 = 0U; - TXSerialfifotail1 = 0U; + TXSerialfifohead1 = 0U; + TXSerialfifotail1 = 0U; } void RXSerialfifoinit1() { - RXSerialfifohead1 = 0U; - RXSerialfifotail1 = 0U; + RXSerialfifohead1 = 0U; + RXSerialfifotail1 = 0U; } // How full is queue // TODO decide if how full or how empty is preferred info to return uint16_t TXSerialfifolevel1() { - uint32_t tail = TXSerialfifotail1; - uint32_t head = TXSerialfifohead1; + uint32_t tail = TXSerialfifotail1; + uint32_t head = TXSerialfifohead1; - if (tail > head) - return TX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return TX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } uint16_t RXSerialfifolevel1() { - uint32_t tail = RXSerialfifotail1; - uint32_t head = RXSerialfifohead1; + uint32_t tail = RXSerialfifotail1; + uint32_t head = RXSerialfifohead1; - if (tail > head) - return RX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return RX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } // Flushes the transmit shift register // warning: this call is blocking void TXSerialFlush1() { - // wait until the TXE shows the shift register is empty - while (USART_GetITStatus(USART1, USART_FLAG_TXE)) - ; + // wait until the TXE shows the shift register is empty + while (USART_GetITStatus(USART1, USART_FLAG_TXE)) + ; } uint8_t TXSerialfifoput1(uint8_t next) { - if (TXSerialfifolevel1() < TX_SERIAL_FIFO_SIZE) { - TXSerialfifo1[TXSerialfifohead1] = next; + if (TXSerialfifolevel1() < TX_SERIAL_FIFO_SIZE) { + TXSerialfifo1[TXSerialfifohead1] = next; - TXSerialfifohead1++; - if (TXSerialfifohead1 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifohead1 = 0U; + TXSerialfifohead1++; + if (TXSerialfifohead1 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifohead1 = 0U; - // make sure transmit interrupts are enabled as long as there is data to send - USART_ITConfig(USART1, USART_IT_TXE, ENABLE); - return 1U; - } else { - return 0U; // signal an overflow occurred by returning a zero count - } + // make sure transmit interrupts are enabled as long as there is data to send + USART_ITConfig(USART1, USART_IT_TXE, ENABLE); + return 1U; + } else { + return 0U; // signal an overflow occurred by returning a zero count + } } void USART1_IRQHandler() { - uint8_t c; + uint8_t c; - if (USART_GetITStatus(USART1, USART_IT_RXNE)) { - c = (uint8_t) USART_ReceiveData(USART1); + if (USART_GetITStatus(USART1, USART_IT_RXNE)) { + c = (uint8_t) USART_ReceiveData(USART1); - if (RXSerialfifolevel1() < RX_SERIAL_FIFO_SIZE) { - RXSerialfifo1[RXSerialfifohead1] = c; + if (RXSerialfifolevel1() < RX_SERIAL_FIFO_SIZE) { + RXSerialfifo1[RXSerialfifohead1] = c; - RXSerialfifohead1++; - if (RXSerialfifohead1 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifohead1 = 0U; - } else { - // TODO - do something if rx fifo is full? - } + RXSerialfifohead1++; + if (RXSerialfifohead1 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifohead1 = 0U; + } else { + // TODO - do something if rx fifo is full? + } - USART_ClearITPendingBit(USART1, USART_IT_RXNE); - intcount1++; - } + USART_ClearITPendingBit(USART1, USART_IT_RXNE); + } - if (USART_GetITStatus(USART1, USART_IT_TXE)) { - c = 0U; + if (USART_GetITStatus(USART1, USART_IT_TXE)) { + c = 0U; - if (TXSerialfifohead1 != TXSerialfifotail1) { // if the fifo is not empty - c = TXSerialfifo1[TXSerialfifotail1]; + if (TXSerialfifohead1 != TXSerialfifotail1) { // if the fifo is not empty + c = TXSerialfifo1[TXSerialfifotail1]; - TXSerialfifotail1++; - if (TXSerialfifotail1 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifotail1 = 0U; + TXSerialfifotail1++; + if (TXSerialfifotail1 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifotail1 = 0U; - USART_SendData(USART1, c); - } else { // if there's no more data to transmit then turn off TX interrupts - USART_ITConfig(USART1, USART_IT_TXE, DISABLE); - } + USART_SendData(USART1, c); + } else { // if there's no more data to transmit then turn off TX interrupts + USART_ITConfig(USART1, USART_IT_TXE, DISABLE); + } - USART_ClearITPendingBit(USART1, USART_IT_TXE); - } + USART_ClearITPendingBit(USART1, USART_IT_TXE); + } } void InitUSART1(int speed) { - // USART1 - TXD PA9 - RXD PA10 - pins on mmdvm pi board - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; + // USART1 - TXD PA9 - RXD PA10 - pins on mmdvm pi board + GPIO_InitTypeDef GPIO_InitStructure; + USART_InitTypeDef USART_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); - // USART IRQ init - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_Init(&NVIC_InitStructure); + // USART IRQ init + NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_Init(&NVIC_InitStructure); - // Configure USART as alternate function - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // Tx | Rx - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOA, &GPIO_InitStructure); + // Configure USART as alternate function + GPIO_StructInit(&GPIO_InitStructure); + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // Tx | Rx + GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed; + GPIO_Init(GPIOA, &GPIO_InitStructure); - // Configure USART baud rate - USART_StructInit(&USART_InitStructure); - USART_InitStructure.USART_BaudRate = speed; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(USART1, &USART_InitStructure); + // Configure USART baud rate + USART_StructInit(&USART_InitStructure); + USART_InitStructure.USART_BaudRate = speed; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_Init(USART1, &USART_InitStructure); - USART_Cmd(USART1, ENABLE); + USART_Cmd(USART1, ENABLE); - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); + USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); - // initialize the fifos - TXSerialfifoinit1(); - RXSerialfifoinit1(); + // initialize the fifos + TXSerialfifoinit1(); + RXSerialfifoinit1(); } -uint8_t AvailUSART1(void) +uint8_t AvailUSART1() { - if (RXSerialfifolevel1() > 0U) - return 1U; - else - return 0U; + if (RXSerialfifolevel1() > 0U) + return 1U; + else + return 0U; } -uint8_t ReadUSART1(void) +int AvailForWriteUSART1() { - uint8_t data_c = RXSerialfifo1[RXSerialfifotail1]; + return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel1(); +} - RXSerialfifotail1++; - if (RXSerialfifotail1 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifotail1 = 0U; +uint8_t ReadUSART1() +{ + uint8_t data_c = RXSerialfifo1[RXSerialfifotail1]; - return data_c; + RXSerialfifotail1++; + if (RXSerialfifotail1 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifotail1 = 0U; + + return data_c; } void WriteUSART1(const uint8_t* data, uint16_t length) { - for (uint16_t i = 0U; i < length; i++) - TXSerialfifoput1(data[i]); - - USART_ITConfig(USART1, USART_IT_TXE, ENABLE); + for (uint16_t i = 0U; i < length; i++) + TXSerialfifoput1(data[i]); + + USART_ITConfig(USART1, USART_IT_TXE, ENABLE); } #endif @@ -238,7 +243,6 @@ void WriteUSART1(const uint8_t* data, uint16_t length) /* ************* USART2 ***************** */ #if defined(STM32F4_NUCLEO) -volatile uint32_t intcount2; volatile uint8_t TXSerialfifo2[TX_SERIAL_FIFO_SIZE]; volatile uint8_t RXSerialfifo2[RX_SERIAL_FIFO_SIZE]; volatile uint16_t TXSerialfifohead2, TXSerialfifotail2; @@ -247,186 +251,189 @@ volatile uint16_t RXSerialfifohead2, RXSerialfifotail2; // Init queues void TXSerialfifoinit2() { - TXSerialfifohead2 = 0U; - TXSerialfifotail2 = 0U; + TXSerialfifohead2 = 0U; + TXSerialfifotail2 = 0U; } void RXSerialfifoinit2() { - RXSerialfifohead2 = 0U; - RXSerialfifotail2 = 0U; + RXSerialfifohead2 = 0U; + RXSerialfifotail2 = 0U; } // How full is queue // TODO decide if how full or how empty is preferred info to return uint16_t TXSerialfifolevel2() { - uint32_t tail = TXSerialfifotail2; - uint32_t head = TXSerialfifohead2; + uint32_t tail = TXSerialfifotail2; + uint32_t head = TXSerialfifohead2; - if (tail > head) - return TX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return TX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } uint16_t RXSerialfifolevel2() { - uint32_t tail = RXSerialfifotail2; - uint32_t head = RXSerialfifohead2; + uint32_t tail = RXSerialfifotail2; + uint32_t head = RXSerialfifohead2; - if (tail > head) - return RX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return RX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } // Flushes the transmit shift register // warning: this call is blocking void TXSerialFlush2() { - // wait until the TXE shows the shift register is empty - while (USART_GetITStatus(USART2, USART_FLAG_TXE)) - ; + // wait until the TXE shows the shift register is empty + while (USART_GetITStatus(USART2, USART_FLAG_TXE)) + ; } uint8_t TXSerialfifoput2(uint8_t next) { - if (TXSerialfifolevel2() < TX_SERIAL_FIFO_SIZE) { - TXSerialfifo2[TXSerialfifohead2] = next; + if (TXSerialfifolevel2() < TX_SERIAL_FIFO_SIZE) { + TXSerialfifo2[TXSerialfifohead2] = next; - TXSerialfifohead2++; - if (TXSerialfifohead2 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifohead2 = 0U; + TXSerialfifohead2++; + if (TXSerialfifohead2 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifohead2 = 0U; - // make sure transmit interrupts are enabled as long as there is data to send - USART_ITConfig(USART2, USART_IT_TXE, ENABLE); - return 1U; - } else { - return 0U; // signal an overflow occurred by returning a zero count - } + // make sure transmit interrupts are enabled as long as there is data to send + USART_ITConfig(USART2, USART_IT_TXE, ENABLE); + return 1U; + } else { + return 0U; // signal an overflow occurred by returning a zero count + } } void USART2_IRQHandler() { - uint8_t c; + uint8_t c; - if (USART_GetITStatus(USART2, USART_IT_RXNE)) { - c = (uint8_t) USART_ReceiveData(USART2); + if (USART_GetITStatus(USART2, USART_IT_RXNE)) { + c = (uint8_t) USART_ReceiveData(USART2); - if (RXSerialfifolevel2() < RX_SERIAL_FIFO_SIZE) { - RXSerialfifo2[RXSerialfifohead2] = c; + if (RXSerialfifolevel2() < RX_SERIAL_FIFO_SIZE) { + RXSerialfifo2[RXSerialfifohead2] = c; - RXSerialfifohead2++; - if (RXSerialfifohead2 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifohead2 = 0U; - } else { - // TODO - do something if rx fifo is full? - } + RXSerialfifohead2++; + if (RXSerialfifohead2 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifohead2 = 0U; + } else { + // TODO - do something if rx fifo is full? + } - USART_ClearITPendingBit(USART2, USART_IT_RXNE); - intcount2++; - } + USART_ClearITPendingBit(USART2, USART_IT_RXNE); + } - if (USART_GetITStatus(USART2, USART_IT_TXE)) { - c = 0U; + if (USART_GetITStatus(USART2, USART_IT_TXE)) { + c = 0U; - if (TXSerialfifohead2 != TXSerialfifotail2) { // if the fifo is not empty - c = TXSerialfifo2[TXSerialfifotail2]; + if (TXSerialfifohead2 != TXSerialfifotail2) { // if the fifo is not empty + c = TXSerialfifo2[TXSerialfifotail2]; - TXSerialfifotail2++; - if (TXSerialfifotail2 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifotail2 = 0U; + TXSerialfifotail2++; + if (TXSerialfifotail2 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifotail2 = 0U; - USART_SendData(USART2, c); - } else { // if there's no more data to transmit then turn off TX interrupts - USART_ITConfig(USART2, USART_IT_TXE, DISABLE); - } + USART_SendData(USART2, c); + } else { // if there's no more data to transmit then turn off TX interrupts + USART_ITConfig(USART2, USART_IT_TXE, DISABLE); + } - USART_ClearITPendingBit(USART2, USART_IT_TXE); - } + USART_ClearITPendingBit(USART2, USART_IT_TXE); + } } void InitUSART2(int speed) { - // USART2 - TXD PA2 - RXD PA3 - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; + // USART2 - TXD PA2 - RXD PA3 + GPIO_InitTypeDef GPIO_InitStructure; + USART_InitTypeDef USART_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); - // USART IRQ init - NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_Init(&NVIC_InitStructure); + // USART IRQ init + NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_Init(&NVIC_InitStructure); - // Configure USART as alternate function - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; // Tx | Rx - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOA, &GPIO_InitStructure); + // Configure USART as alternate function + GPIO_StructInit(&GPIO_InitStructure); + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; // Tx | Rx + GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed; + GPIO_Init(GPIOA, &GPIO_InitStructure); - // Configure USART baud rate - USART_StructInit(&USART_InitStructure); - USART_InitStructure.USART_BaudRate = speed; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(USART2, &USART_InitStructure); + // Configure USART baud rate + USART_StructInit(&USART_InitStructure); + USART_InitStructure.USART_BaudRate = speed; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_Init(USART2, &USART_InitStructure); - USART_Cmd(USART2, ENABLE); + USART_Cmd(USART2, ENABLE); - USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); - // initialize the fifos - TXSerialfifoinit2(); - RXSerialfifoinit2(); + // initialize the fifos + TXSerialfifoinit2(); + RXSerialfifoinit2(); } -uint8_t AvailUSART2(void) +uint8_t AvailUSART2() { - if (RXSerialfifolevel2() > 0U) - return 1U; - else - return 0U; + if (RXSerialfifolevel2() > 0U) + return 1U; + else + return 0U; } -uint8_t ReadUSART2(void) +int AvailForWriteUSART2() { - uint8_t data_c = RXSerialfifo2[RXSerialfifotail2]; + return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel2(); +} - RXSerialfifotail2++; - if (RXSerialfifotail2 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifotail2 = 0U; +uint8_t ReadUSART2() +{ + uint8_t data_c = RXSerialfifo2[RXSerialfifotail2]; - return data_c; + RXSerialfifotail2++; + if (RXSerialfifotail2 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifotail2 = 0U; + + return data_c; } void WriteUSART2(const uint8_t* data, uint16_t length) { - for (uint16_t i = 0U; i < length; i++) - TXSerialfifoput2(data[i]); - - USART_ITConfig(USART2, USART_IT_TXE, ENABLE); + for (uint16_t i = 0U; i < length; i++) + TXSerialfifoput2(data[i]); + + USART_ITConfig(USART2, USART_IT_TXE, ENABLE); } #endif /* ************* USART3 ***************** */ -#if defined(STM32F4_DISCOVERY) || defined(STM32F4_PI) +#if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO) -volatile uint32_t intcount3; volatile uint8_t TXSerialfifo3[TX_SERIAL_FIFO_SIZE]; volatile uint8_t RXSerialfifo3[RX_SERIAL_FIFO_SIZE]; volatile uint16_t TXSerialfifohead3, TXSerialfifotail3; @@ -435,185 +442,206 @@ volatile uint16_t RXSerialfifohead3, RXSerialfifotail3; // Init queues void TXSerialfifoinit3() { - TXSerialfifohead3 = 0U; - TXSerialfifotail3 = 0U; + TXSerialfifohead3 = 0U; + TXSerialfifotail3 = 0U; } void RXSerialfifoinit3() { - RXSerialfifohead3 = 0U; - RXSerialfifotail3 = 0U; + RXSerialfifohead3 = 0U; + RXSerialfifotail3 = 0U; } // How full is queue // TODO decide if how full or how empty is preferred info to return uint16_t TXSerialfifolevel3() { - uint32_t tail = TXSerialfifotail3; - uint32_t head = TXSerialfifohead3; + uint32_t tail = TXSerialfifotail3; + uint32_t head = TXSerialfifohead3; - if (tail > head) - return TX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return TX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } uint16_t RXSerialfifolevel3() { - uint32_t tail = RXSerialfifotail3; - uint32_t head = RXSerialfifohead3; + uint32_t tail = RXSerialfifotail3; + uint32_t head = RXSerialfifohead3; - if (tail > head) - return RX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return RX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } // Flushes the transmit shift register // warning: this call is blocking void TXSerialFlush3() { - // wait until the TXE shows the shift register is empty - while (USART_GetITStatus(USART3, USART_FLAG_TXE)) - ; + // wait until the TXE shows the shift register is empty + while (USART_GetITStatus(USART3, USART_FLAG_TXE)) + ; } uint8_t TXSerialfifoput3(uint8_t next) { - if (TXSerialfifolevel3() < TX_SERIAL_FIFO_SIZE) { - TXSerialfifo3[TXSerialfifohead3] = next; + if (TXSerialfifolevel3() < TX_SERIAL_FIFO_SIZE) { + TXSerialfifo3[TXSerialfifohead3] = next; - TXSerialfifohead3++; - if (TXSerialfifohead3 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifohead3 = 0U; + TXSerialfifohead3++; + if (TXSerialfifohead3 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifohead3 = 0U; - // make sure transmit interrupts are enabled as long as there is data to send - USART_ITConfig(USART3, USART_IT_TXE, ENABLE); - return 1U; - } else { - return 0U; // signal an overflow occurred by returning a zero count - } + // make sure transmit interrupts are enabled as long as there is data to send + USART_ITConfig(USART3, USART_IT_TXE, ENABLE); + return 1U; + } else { + return 0U; // signal an overflow occurred by returning a zero count + } } void USART3_IRQHandler() { - uint8_t c; + uint8_t c; - if (USART_GetITStatus(USART3, USART_IT_RXNE)) { - c = (uint8_t) USART_ReceiveData(USART3); + if (USART_GetITStatus(USART3, USART_IT_RXNE)) { + c = (uint8_t) USART_ReceiveData(USART3); - if (RXSerialfifolevel3() < RX_SERIAL_FIFO_SIZE) { - RXSerialfifo3[RXSerialfifohead3] = c; + if (RXSerialfifolevel3() < RX_SERIAL_FIFO_SIZE) { + RXSerialfifo3[RXSerialfifohead3] = c; - RXSerialfifohead3++; - if (RXSerialfifohead3 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifohead3 = 0U; - } else { - // TODO - do something if rx fifo is full? - } + RXSerialfifohead3++; + if (RXSerialfifohead3 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifohead3 = 0U; + } else { + // TODO - do something if rx fifo is full? + } - USART_ClearITPendingBit(USART3, USART_IT_RXNE); - intcount3++; - } + USART_ClearITPendingBit(USART3, USART_IT_RXNE); + } - if (USART_GetITStatus(USART3, USART_IT_TXE)) { - c = 0U; + if (USART_GetITStatus(USART3, USART_IT_TXE)) { + c = 0U; - if (TXSerialfifohead3 != TXSerialfifotail3) { // if the fifo is not empty - c = TXSerialfifo3[TXSerialfifotail3]; + if (TXSerialfifohead3 != TXSerialfifotail3) { // if the fifo is not empty + c = TXSerialfifo3[TXSerialfifotail3]; - TXSerialfifotail3++; - if (TXSerialfifotail3 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifotail3 = 0U; + TXSerialfifotail3++; + if (TXSerialfifotail3 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifotail3 = 0U; - USART_SendData(USART3, c); - } else { // if there's no more data to transmit then turn off TX interrupts - USART_ITConfig(USART3, USART_IT_TXE, DISABLE); - } + USART_SendData(USART3, c); + } else { // if there's no more data to transmit then turn off TX interrupts + USART_ITConfig(USART3, USART_IT_TXE, DISABLE); + } - USART_ClearITPendingBit(USART3, USART_IT_TXE); - } + USART_ClearITPendingBit(USART3, USART_IT_TXE); + } } +#if defined(STM32F7_NUCLEO) +// USART3 - TXD PD8 - RXD PD9 +#define USART3_GPIO_PinSource_TX GPIO_PinSource8 +#define USART3_GPIO_PinSource_RX GPIO_PinSource9 +#define USART3_GPIO_Pin_TX GPIO_Pin_8 +#define USART3_GPIO_Pin_RX GPIO_Pin_9 +#define USART3_GPIO_PORT GPIOD +#define USART3_RCC_Periph RCC_AHB1Periph_GPIOD +#else +// USART3 - TXD PC10 - RXD PC11 +#define USART3_GPIO_PinSource_TX GPIO_PinSource10 +#define USART3_GPIO_PinSource_RX GPIO_PinSource11 +#define USART3_GPIO_Pin_TX GPIO_Pin_10 +#define USART3_GPIO_Pin_RX GPIO_Pin_11 +#define USART3_GPIO_PORT GPIOC +#define USART3_RCC_Periph RCC_AHB1Periph_GPIOC +#endif + void InitUSART3(int speed) { - // USART3 - TXD PC10 - RXD PC11 - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; + GPIO_InitTypeDef GPIO_InitStructure; + USART_InitTypeDef USART_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); - GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3); - GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3); + RCC_AHB1PeriphClockCmd(USART3_RCC_Periph, ENABLE); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); + GPIO_PinAFConfig(USART3_GPIO_PORT, USART3_GPIO_PinSource_TX, GPIO_AF_USART3); + GPIO_PinAFConfig(USART3_GPIO_PORT, USART3_GPIO_PinSource_RX, GPIO_AF_USART3); - // USART IRQ init - NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_Init(&NVIC_InitStructure); + // USART IRQ init + NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_Init(&NVIC_InitStructure); - // Configure USART as alternate function - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // Tx | Rx - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOC, &GPIO_InitStructure); + // Configure USART as alternate function + GPIO_StructInit(&GPIO_InitStructure); + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Pin = USART3_GPIO_Pin_TX | USART3_GPIO_Pin_RX; // Tx | Rx + GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed; + GPIO_Init(USART3_GPIO_PORT, &GPIO_InitStructure); - // Configure USART baud rate - USART_StructInit(&USART_InitStructure); - USART_InitStructure.USART_BaudRate = speed; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(USART3, &USART_InitStructure); + // Configure USART baud rate + USART_StructInit(&USART_InitStructure); + USART_InitStructure.USART_BaudRate = speed; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_Init(USART3, &USART_InitStructure); - USART_Cmd(USART3, ENABLE); + USART_Cmd(USART3, ENABLE); - USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); + USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); - // initialize the fifos - TXSerialfifoinit3(); - RXSerialfifoinit3(); + // initialize the fifos + TXSerialfifoinit3(); + RXSerialfifoinit3(); } -uint8_t AvailUSART3(void) +uint8_t AvailUSART3() { - if (RXSerialfifolevel3() > 0U) - return 1U; - else - return 0U; + if (RXSerialfifolevel3() > 0U) + return 1U; + else + return 0U; } -uint8_t ReadUSART3(void) +int AvailForWriteUSART3() { - uint8_t data_c = RXSerialfifo3[RXSerialfifotail3]; + return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel3(); +} - RXSerialfifotail3++; - if (RXSerialfifotail3 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifotail3 = 0U; +uint8_t ReadUSART3() +{ + uint8_t data_c = RXSerialfifo3[RXSerialfifotail3]; - return data_c; + RXSerialfifotail3++; + if (RXSerialfifotail3 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifotail3 = 0U; + + return data_c; } void WriteUSART3(const uint8_t* data, uint16_t length) { - for (uint16_t i = 0U; i < length; i++) - TXSerialfifoput3(data[i]); - - USART_ITConfig(USART3, USART_IT_TXE, ENABLE); + for (uint16_t i = 0U; i < length; i++) + TXSerialfifoput3(data[i]); + + USART_ITConfig(USART3, USART_IT_TXE, ENABLE); } #endif /* ************* UART5 ***************** */ +#if !(defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER)) -volatile uint32_t intcount5; volatile uint8_t TXSerialfifo5[TX_SERIAL_FIFO_SIZE]; volatile uint8_t RXSerialfifo5[RX_SERIAL_FIFO_SIZE]; volatile uint16_t TXSerialfifohead5, TXSerialfifotail5; @@ -622,269 +650,313 @@ volatile uint16_t RXSerialfifohead5, RXSerialfifotail5; // Init queues void TXSerialfifoinit5() { - TXSerialfifohead5 = 0U; - TXSerialfifotail5 = 0U; + TXSerialfifohead5 = 0U; + TXSerialfifotail5 = 0U; } void RXSerialfifoinit5() { - RXSerialfifohead5 = 0U; - RXSerialfifotail5 = 0U; + RXSerialfifohead5 = 0U; + RXSerialfifotail5 = 0U; } // How full is queue // TODO decide if how full or how empty is preferred info to return uint16_t TXSerialfifolevel5() { - uint32_t tail = TXSerialfifotail5; - uint32_t head = TXSerialfifohead5; + uint32_t tail = TXSerialfifotail5; + uint32_t head = TXSerialfifohead5; - if (tail > head) - return TX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return TX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } uint16_t RXSerialfifolevel5() { - uint32_t tail = RXSerialfifotail5; - uint32_t head = RXSerialfifohead5; + uint32_t tail = RXSerialfifotail5; + uint32_t head = RXSerialfifohead5; - if (tail > head) - return RX_SERIAL_FIFO_SIZE + head - tail; - else - return head - tail; + if (tail > head) + return RX_SERIAL_FIFO_SIZE + head - tail; + else + return head - tail; } // Flushes the transmit shift register // warning: this call is blocking void TXSerialFlush5() { - // wait until the TXE shows the shift register is empty - while (USART_GetITStatus(UART5, USART_FLAG_TXE)) - ; + // wait until the TXE shows the shift register is empty + while (USART_GetITStatus(UART5, USART_FLAG_TXE)) + ; } uint8_t TXSerialfifoput5(uint8_t next) { - if (TXSerialfifolevel5() < TX_SERIAL_FIFO_SIZE) { - TXSerialfifo5[TXSerialfifohead5] = next; + if (TXSerialfifolevel5() < TX_SERIAL_FIFO_SIZE) { + TXSerialfifo5[TXSerialfifohead5] = next; - TXSerialfifohead5++; - if (TXSerialfifohead5 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifohead5 = 0U; + TXSerialfifohead5++; + if (TXSerialfifohead5 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifohead5 = 0U; - // make sure transmit interrupts are enabled as long as there is data to send - USART_ITConfig(UART5, USART_IT_TXE, ENABLE); - return 1U; - } else { - return 0U; // signal an overflow occurred by returning a zero count - } + // make sure transmit interrupts are enabled as long as there is data to send + USART_ITConfig(UART5, USART_IT_TXE, ENABLE); + return 1U; + } else { + return 0U; // signal an overflow occurred by returning a zero count + } } void UART5_IRQHandler() { - uint8_t c; + uint8_t c; - if (USART_GetITStatus(UART5, USART_IT_RXNE)) { - c = (uint8_t) USART_ReceiveData(UART5); + if (USART_GetITStatus(UART5, USART_IT_RXNE)) { + c = (uint8_t) USART_ReceiveData(UART5); - if (RXSerialfifolevel5() < RX_SERIAL_FIFO_SIZE) { - RXSerialfifo5[RXSerialfifohead5] = c; + if (RXSerialfifolevel5() < RX_SERIAL_FIFO_SIZE) { + RXSerialfifo5[RXSerialfifohead5] = c; - RXSerialfifohead5++; - if (RXSerialfifohead5 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifohead5 = 0U; - } else { - // TODO - do something if rx fifo is full? - } + RXSerialfifohead5++; + if (RXSerialfifohead5 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifohead5 = 0U; + } else { + // TODO - do something if rx fifo is full? + } - USART_ClearITPendingBit(UART5, USART_IT_RXNE); - intcount5++; - } + USART_ClearITPendingBit(UART5, USART_IT_RXNE); + } - if (USART_GetITStatus(UART5, USART_IT_TXE)) { - c = 0U; + if (USART_GetITStatus(UART5, USART_IT_TXE)) { + c = 0U; - if (TXSerialfifohead5 != TXSerialfifotail5) { // if the fifo is not empty - c = TXSerialfifo5[TXSerialfifotail5]; + if (TXSerialfifohead5 != TXSerialfifotail5) { // if the fifo is not empty + c = TXSerialfifo5[TXSerialfifotail5]; - TXSerialfifotail5++; - if (TXSerialfifotail5 >= TX_SERIAL_FIFO_SIZE) - TXSerialfifotail5 = 0U; + TXSerialfifotail5++; + if (TXSerialfifotail5 >= TX_SERIAL_FIFO_SIZE) + TXSerialfifotail5 = 0U; - USART_SendData(UART5, c); - } else { // if there's no more data to transmit then turn off TX interrupts + USART_SendData(UART5, c); + } else { // if there's no more data to transmit then turn off TX interrupts USART_ITConfig(UART5, USART_IT_TXE, DISABLE); - } + } - USART_ClearITPendingBit(UART5, USART_IT_TXE); - } + USART_ClearITPendingBit(UART5, USART_IT_TXE); + } } void InitUART5(int speed) { - // UART5 - TXD PC12 - RXD PD2 - GPIO_InitTypeDef GPIO_InitStructure; - USART_InitTypeDef USART_InitStructure; - NVIC_InitTypeDef NVIC_InitStructure; + // UART5 - TXD PC12 - RXD PD2 + GPIO_InitTypeDef GPIO_InitStructure; + USART_InitTypeDef USART_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); - RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE); - GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5); - GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE); + GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_UART5); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_UART5); - // USART IRQ init - NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn; - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; - NVIC_Init(&NVIC_InitStructure); + // USART IRQ init + NVIC_InitStructure.NVIC_IRQChannel = UART5_IRQn; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_Init(&NVIC_InitStructure); - // Configure USART as alternate function - GPIO_StructInit(&GPIO_InitStructure); - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // Tx - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOC, &GPIO_InitStructure); + // Configure USART as alternate function + GPIO_StructInit(&GPIO_InitStructure); + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // Tx + GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed; + GPIO_Init(GPIOC, &GPIO_InitStructure); - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // Rx - GPIO_Init(GPIOD, &GPIO_InitStructure); + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // Rx + GPIO_Init(GPIOD, &GPIO_InitStructure); - // Configure USART baud rate - USART_StructInit(&USART_InitStructure); - USART_InitStructure.USART_BaudRate = speed; - USART_InitStructure.USART_WordLength = USART_WordLength_8b; - USART_InitStructure.USART_StopBits = USART_StopBits_1; - USART_InitStructure.USART_Parity = USART_Parity_No; - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; - USART_Init(UART5, &USART_InitStructure); + // Configure USART baud rate + USART_StructInit(&USART_InitStructure); + USART_InitStructure.USART_BaudRate = speed; + USART_InitStructure.USART_WordLength = USART_WordLength_8b; + USART_InitStructure.USART_StopBits = USART_StopBits_1; + USART_InitStructure.USART_Parity = USART_Parity_No; + USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; + USART_Init(UART5, &USART_InitStructure); - USART_Cmd(UART5, ENABLE); + USART_Cmd(UART5, ENABLE); - USART_ITConfig(UART5, USART_IT_RXNE, ENABLE); + USART_ITConfig(UART5, USART_IT_RXNE, ENABLE); - // initialize the fifos - TXSerialfifoinit5(); - RXSerialfifoinit5(); + // initialize the fifos + TXSerialfifoinit5(); + RXSerialfifoinit5(); } -uint8_t AvailUART5(void) +uint8_t AvailUART5() { - if (RXSerialfifolevel5() > 0U) - return 1U; - else - return 0U; + if (RXSerialfifolevel5() > 0U) + return 1U; + else + return 0U; } -uint8_t ReadUART5(void) +int AvailForWriteUART5() { - uint8_t data_c = RXSerialfifo5[RXSerialfifotail5]; + return TX_SERIAL_FIFO_SIZE - TXSerialfifolevel5(); +} - RXSerialfifotail5++; - if (RXSerialfifotail5 >= RX_SERIAL_FIFO_SIZE) - RXSerialfifotail5 = 0U; +uint8_t ReadUART5() +{ + uint8_t data_c = RXSerialfifo5[RXSerialfifotail5]; - return data_c; + RXSerialfifotail5++; + if (RXSerialfifotail5 >= RX_SERIAL_FIFO_SIZE) + RXSerialfifotail5 = 0U; + + return data_c; } void WriteUART5(const uint8_t* data, uint16_t length) { - for (uint16_t i = 0U; i < length; i++) - TXSerialfifoput5(data[i]); - - USART_ITConfig(UART5, USART_IT_TXE, ENABLE); + for (uint16_t i = 0U; i < length; i++) + TXSerialfifoput5(data[i]); + + USART_ITConfig(UART5, USART_IT_TXE, ENABLE); } +#endif ///////////////////////////////////////////////////////////////// void CSerialPort::beginInt(uint8_t n, int speed) { - switch (n) { - case 1U: - #if defined(STM32F4_DISCOVERY) - InitUSART3(speed); - #elif defined(STM32F4_PI) - InitUSART1(speed); - #elif defined(STM32F4_NUCLEO) - InitUSART2(speed); - #endif - break; - case 3U: - InitUART5(speed); - break; - default: - break; - } + switch (n) { + case 1U: + #if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO) + InitUSART3(speed); + #elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM) + InitUSART1(speed); + #elif defined(STM32F4_NUCLEO) + InitUSART2(speed); + #endif + break; + case 3U: + #if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER) + InitUSART1(speed); + #else + InitUART5(speed); + #endif + break; + default: + break; + } } int CSerialPort::availableInt(uint8_t n) -{ - switch (n) { +{ + switch (n) { case 1U: - #if defined(STM32F4_DISCOVERY) - return AvailUSART3(); - #elif defined(STM32F4_PI) - return AvailUSART1(); - #elif defined(STM32F4_NUCLEO) - return AvailUSART2(); - #endif - case 3U: - return AvailUART5(); + #if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO) + return AvailUSART3(); + #elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM) + return AvailUSART1(); + #elif defined(STM32F4_NUCLEO) + return AvailUSART2(); + #endif + case 3U: + #if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER) + return AvailUSART1(); + #else + return AvailUART5(); + #endif default: - return false; - } + return 0; + } +} + +int CSerialPort::availableForWriteInt(uint8_t n) +{ + switch (n) { + case 1U: + #if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO) + return AvailForWriteUSART3(); + #elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM) + return AvailForWriteUSART1(); + #elif defined(STM32F4_NUCLEO) + return AvailForWriteUSART2(); + #endif + case 3U: + #if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER) + return AvailForWriteUSART1(); + #else + return AvailForWriteUART5(); + #endif + default: + return 0; + } } uint8_t CSerialPort::readInt(uint8_t n) -{ - switch (n) { - case 1U: - #if defined(STM32F4_DISCOVERY) - return ReadUSART3(); - #elif defined(STM32F4_PI) - return ReadUSART1(); - #elif defined(STM32F4_NUCLEO) - return ReadUSART2(); - #endif - case 3U: - return ReadUART5(); - default: - return 0U; - } +{ + switch (n) { + case 1U: + #if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO) + return ReadUSART3(); + #elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM) + return ReadUSART1(); + #elif defined(STM32F4_NUCLEO) + return ReadUSART2(); + #endif + case 3U: + #if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER) + return ReadUSART1(); + #else + return ReadUART5(); + #endif + default: + return 0U; + } } void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush) { - switch (n) { - case 1U: - #if defined(STM32F4_DISCOVERY) - WriteUSART3(data, length); - if (flush) - TXSerialFlush3(); - #elif defined(STM32F4_PI) - WriteUSART1(data, length); - if (flush) - TXSerialFlush1(); - #elif defined(STM32F4_NUCLEO) - WriteUSART2(data, length); - if (flush) - TXSerialFlush2(); - #endif - break; - case 3U: - WriteUART5(data, length); - if (flush) - TXSerialFlush5(); - break; - default: - break; - } + switch (n) { + case 1U: + #if defined(STM32F4_DISCOVERY) || defined(STM32F7_NUCLEO) + WriteUSART3(data, length); + if (flush) + TXSerialFlush3(); + #elif defined(STM32F4_PI) || defined(STM32F4_F4M) || defined(STM32F722_PI) || defined(STM32F722_F7M) || defined(STM32F4_DVM) + WriteUSART1(data, length); + if (flush) + TXSerialFlush1(); + #elif defined(STM32F4_NUCLEO) + WriteUSART2(data, length); + if (flush) + TXSerialFlush2(); + #endif + break; + case 3U: + #if defined(STM32F4_NUCLEO) && defined(STM32F4_NUCLEO_ARDUINO_HEADER) + WriteUSART1(data, length); + if (flush) + TXSerialFlush1(); + #else + WriteUART5(data, length); + if (flush) + TXSerialFlush5(); + #endif + break; + default: + break; + } } #endif - diff --git a/SerialSTM_CMSIS.cpp b/SerialSTM_CMSIS.cpp new file mode 100644 index 0000000..38127ac --- /dev/null +++ b/SerialSTM_CMSIS.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2017 by Wojciech Krutnik N0CALL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "RingBuff.h" + +#include "SerialPort.h" + +/* +Pin definitions (configuration in IOSTM_CMSIS.c): + +- Host communication: +USART1 - TXD PA9 - RXD PA10 + +*/ + + +#if defined(STM32F1) + +// BaudRate calculator macro +// source: STM32 HAL Library (stm32f1xx_hal_usart.h) +#define USART_DIV(__PCLK__, __BAUD__) (((__PCLK__)*25)/(4*(__BAUD__))) +#define USART_DIVMANT(__PCLK__, __BAUD__) (USART_DIV((__PCLK__), (__BAUD__))/100) +#define USART_DIVFRAQ(__PCLK__, __BAUD__) (((USART_DIV((__PCLK__), (__BAUD__)) - (USART_DIVMANT((__PCLK__), (__BAUD__)) * 100)) * 16 + 50) / 100) +#define USART_BRR(__PCLK__, __BAUD__) ((USART_DIVMANT((__PCLK__), (__BAUD__)) << 4)|(USART_DIVFRAQ((__PCLK__), (__BAUD__)) & 0x0F)) + +#define USART_BUFFER_SIZE 256U +DECLARE_RINGBUFFER_TYPE(USARTBuffer, USART_BUFFER_SIZE); + +/* ************* USART1 ***************** */ +static volatile USARTBuffer_t txBuffer1={.size=USART_BUFFER_SIZE}; +static volatile USARTBuffer_t rxBuffer1={.size=USART_BUFFER_SIZE}; + + +extern "C" { + bitband_t txe = (bitband_t)BITBAND_PERIPH(&USART1->SR, USART_SR_TXE_Pos); + bitband_t rxne = (bitband_t)BITBAND_PERIPH(&USART1->SR, USART_SR_RXNE_Pos); + bitband_t txeie = (bitband_t)BITBAND_PERIPH(&USART1->CR1, USART_CR1_TXEIE_Pos); + + RAMFUNC void USART1_IRQHandler() + { + /* Transmitting data */ + if(*txe){ + if(!(RINGBUFF_EMPTY(txBuffer1))){ + USART1->DR = RINGBUFF_READ(txBuffer1); + }else{ /* Buffer empty */ + *txeie = 0; /* Don't send further data */ + } + } + + /* Receiving data */ + if(*rxne){ + RINGBUFF_WRITE(rxBuffer1, USART1->DR); + } + } +} + + +void USART1Init(int speed) +{ + RCC->APB2ENR |= RCC_APB2ENR_USART1EN; + + USART1->BRR = USART_BRR(72000000UL, speed); + + USART1->CR1 = USART_CR1_UE | USART_CR1_TE | + USART_CR1_RE | USART_CR1_RXNEIE; // Enable USART and RX interrupt + NVIC_EnableIRQ(USART1_IRQn); +} + +RAMFUNC void USART1TxData(const uint8_t* data, uint16_t length) +{ + NVIC_DisableIRQ(USART1_IRQn); + + /* Check remaining space in buffer */ + if(RINGBUFF_COUNT(txBuffer1) + length > RINGBUFF_SIZE(txBuffer1)){ + NVIC_EnableIRQ(USART1_IRQn); + return; + } + + /* Append data to buffer */ + while(length--){ + RINGBUFF_WRITE(txBuffer1, *(data++)); + } + + /* Start sending data */ + USART1->CR1 |= USART_CR1_TXEIE; + + NVIC_EnableIRQ(USART1_IRQn); +} + + +///////////////////////////////////////////////////////////////// + +void CSerialPort::beginInt(uint8_t n, int speed) +{ + switch (n) { + case 1U: + USART1Init(speed); + break; + default: + break; + } +} + +int CSerialPort::availableInt(uint8_t n) +{ + switch (n) { + case 1U: + return !RINGBUFF_EMPTY(rxBuffer1); + default: + return false; + } +} + +uint8_t CSerialPort::readInt(uint8_t n) +{ + switch (n) { + case 1U: + return RINGBUFF_READ(rxBuffer1); + default: + return 0U; + } +} + +void CSerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush) +{ + bitband_t tc = (bitband_t)BITBAND_PERIPH(&USART1->SR, USART_SR_TC_Pos); + + switch (n) { + case 1U: + USART1TxData(data, length); + *tc = 0; + + if (flush) { + while (!RINGBUFF_EMPTY(txBuffer1) || !tc) + ; + } + break; + default: + break; + } +} + +#endif diff --git a/Utils.h b/Utils.h index d158c6b..95cb1d5 100644 --- a/Utils.h +++ b/Utils.h @@ -19,8 +19,12 @@ #if !defined(UTILS_H) #define UTILS_H -#if defined(STM32F4XX) || defined(STM32F4) +#if defined(STM32F4XX) #include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" #include #else #include diff --git a/YSFDefines.h b/YSFDefines.h index 9e65f4a..4dc73b6 100644 --- a/YSFDefines.h +++ b/YSFDefines.h @@ -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 = 10U; // At 48 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; diff --git a/YSFRX.cpp b/YSFRX.cpp index cfe4fab..88c6f83 100644 --- a/YSFRX.cpp +++ b/YSFRX.cpp @@ -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; +} diff --git a/YSFRX.h b/YSFRX.h index 8f8f770..f8abfc2 100644 --- a/YSFRX.h +++ b/YSFRX.h @@ -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_syncPtr; + uint16_t m_minSyncPtr; + uint16_t m_maxSyncPtr; + 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 diff --git a/YSFTX.cpp b/YSFTX.cpp index ee9c1e8..2cb3a5e 100644 --- a/YSFTX.cpp +++ b/YSFTX.cpp @@ -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,42 +17,53 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// #define WANT_DEBUG - #include "Config.h" #include "Globals.h" #include "YSFTX.h" #include "YSFDefines.h" -// Generated using rcosdesign(0.2, 4, 10, 'sqrt') in MATLAB -static q15_t YSF_C4FSK_FILTER[] = {486, 39, -480, -1022, -1526, -1928, -2164, -2178, -1927, -1384, -548, 561, 1898, 3399, 4980, 6546, 7999, 9246, 10202, 10803, 11008, 10803, 10202, 9246, - 7999, 6546, 4980, 3399, 1898, 561, -548, -1384, -1927, -2178, -2164, -1928, -1526, -1022, -480, 39, 486, 0}; -const uint16_t YSF_C4FSK_FILTER_LEN = 42U; +// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179, + -1548, -1769, -1795, -1597, -1172, -544, 237, 1092, 1927, 2637, + 3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739, + -6442, -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822, + 19484, 23810, 27520, 30367, 32156, 32767, 32156, 30367, 27520, + 23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735, + -6483, -6442, -5739, -4544, -3043, -1431, 116, 1447, 2454, + 3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, -1597, + -1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10 +const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t YSF_LEVELA[] = { 499, 499, 499, 499, 499, 499, 499, 499, 499, 499}; -const q15_t YSF_LEVELB[] = { 166, 166, 166, 166, 166, 166, 166, 166, 166, 166}; -const q15_t YSF_LEVELC[] = {-166, -166, -166, -166, -166, -166, -166, -166, -166, -166}; -const q15_t YSF_LEVELD[] = {-499, -499, -499, -499, -499, -499, -499, -499, -499, -499}; +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; CYSFTX::CYSFTX() : -m_buffer(1500U), +m_buffer(4000U), m_modFilter(), m_modState(), m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(240U), // 200ms -m_count(0U) +m_loDev(false) { - ::memset(m_modState, 0x00U, 90U * 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() @@ -61,8 +73,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 { @@ -110,59 +120,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; +} + diff --git a/YSFTX.h b/YSFTX.h index d961e02..01211a5 100644 --- a/YSFTX.h +++ b/YSFTX.h @@ -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[90U]; // 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); }; diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 0000000..b00e2bf --- /dev/null +++ b/openocd.cfg @@ -0,0 +1,2 @@ +source [find interface/stlink-v2.cfg] +source [find target/stm32f1x.cfg] diff --git a/stm32f722_link.ld b/stm32f722_link.ld new file mode 100644 index 0000000..2ae8313 --- /dev/null +++ b/stm32f722_link.ld @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Required amount of heap and stack */ +_min_heap_size = 0x1000; +_min_stack_size = 0x0800; + +/* The entry point in the interrupt vector table */ +ENTRY(Reset_Handler) + +/* Memory areas */ +MEMORY +{ + ROM (rx) : ORIGIN = 0x08000000, LENGTH = 512K /* FLASH */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 256K /* Main RAM */ +} + +/* Stack start address (end of 256K RAM) */ +_estack = ORIGIN(RAM) + LENGTH(RAM); + +SECTIONS +{ + .text : + { + /* The interrupt vector table */ + . = ALIGN(4); + KEEP(*(.isr_vector .isr_vector.*)) + + /* The program code */ + . = ALIGN(4); + *(.text .text*) + *(.rodata .rodata*) + + /* ARM-Thumb code */ + *(.glue_7) *(.glue_7t) + + . = ALIGN(4); + KEEP(*(.init)) + KEEP(*(.fini)) + + /* EABI C++ global constructors support */ + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + /* EABI C++ global constructors support */ + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + /* EABI C++ global constructors support */ + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + } > ROM + + /* ARM sections containing exception unwinding information */ + .ARM.extab : { + __extab_start = .; + *(.ARM.extab* .gnu.linkonce.armextab.*) + __extab_end = .; + } > ROM + + /* ARM index entries for section unwinding */ + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > ROM + + /* Start address for the initialization values of the .data section */ + _sidata = .; + + /* The .data section (initialized data) */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = . ; /* Start address for the .data section */ + *(.data .data*) + + . = ALIGN(4); + _edata = . ; /* End address for the .data section */ + } > RAM + + /* The .bss section (uninitialized data) */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* Start address for the .bss section */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = . ; /* End address for the .bss section */ + __bss_end__ = _ebss; + } > RAM + + /* Space for heap and stack */ + .heap_stack : + { + end = . ; /* 'end' symbol defines heap location */ + _end = end ; + . = . + _min_heap_size; /* Additional space for heap and stack */ + . = . + _min_stack_size; + } > RAM + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } +} diff --git a/stm32f767_link.ld b/stm32f767_link.ld new file mode 100644 index 0000000..3f82067 --- /dev/null +++ b/stm32f767_link.ld @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Required amount of heap and stack */ +_min_heap_size = 0x1000; +_min_stack_size = 0x0800; + +/* The entry point in the interrupt vector table */ +ENTRY(Reset_Handler) + +/* Memory areas */ +MEMORY +{ + ROM (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* FLASH */ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 512K /* Main RAM */ +} + +/* Stack start address (end of 512K RAM) */ +_estack = ORIGIN(RAM) + LENGTH(RAM); + +SECTIONS +{ + .text : + { + /* The interrupt vector table */ + . = ALIGN(4); + KEEP(*(.isr_vector .isr_vector.*)) + + /* The program code */ + . = ALIGN(4); + *(.text .text*) + *(.rodata .rodata*) + + /* ARM-Thumb code */ + *(.glue_7) *(.glue_7t) + + . = ALIGN(4); + KEEP(*(.init)) + KEEP(*(.fini)) + + /* EABI C++ global constructors support */ + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + /* EABI C++ global constructors support */ + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + /* EABI C++ global constructors support */ + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + } > ROM + + /* ARM sections containing exception unwinding information */ + .ARM.extab : { + __extab_start = .; + *(.ARM.extab* .gnu.linkonce.armextab.*) + __extab_end = .; + } > ROM + + /* ARM index entries for section unwinding */ + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > ROM + + /* Start address for the initialization values of the .data section */ + _sidata = .; + + /* The .data section (initialized data) */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = . ; /* Start address for the .data section */ + *(.data .data*) + + . = ALIGN(4); + _edata = . ; /* End address for the .data section */ + } > RAM + + /* The .bss section (uninitialized data) */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* Start address for the .bss section */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = . ; /* End address for the .bss section */ + __bss_end__ = _ebss; + } > RAM + + /* Space for heap and stack */ + .heap_stack : + { + end = . ; /* 'end' symbol defines heap location */ + _end = end ; + . = . + _min_heap_size; /* Additional space for heap and stack */ + . = . + _min_stack_size; + } > RAM + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } +}