From 835ae8e265c247890c47445d890965990042b58f Mon Sep 17 00:00:00 2001 From: Rob Riggs Date: Fri, 10 Jan 2020 12:05:24 -0600 Subject: [PATCH] Start of 9600 baud support. --- TNC/AFSKModulator.hpp | 47 +++++- TNC/Afsk1200Demodulator.cpp | 31 ++++ TNC/Afsk1200Demodulator.hpp | 150 ++++++++++++++++++ TNC/AudioInput.cpp | 261 +++++-------------------------- TNC/AudioLevel.cpp | 4 +- TNC/Demodulator.cpp | 10 ++ TNC/Demodulator.hpp | 72 +++++++++ TNC/DigitalPLL.hpp | 6 +- TNC/FilterCoefficients.cpp | 35 +++++ TNC/FilterCoefficients.hpp | 22 +-- TNC/Fsk9600Demodulator.cpp | 54 +++++++ TNC/Fsk9600Demodulator.hpp | 117 ++++++++++++++ TNC/Fsk9600Modulator.cpp | 16 ++ TNC/Fsk9600Modulator.hpp | 216 +++++++++++++++++++++++++ TNC/HDLCEncoder.hpp | 14 +- TNC/IOEventTask.cpp | 304 +++--------------------------------- TNC/KissHardware.cpp | 204 +++++++----------------- TNC/KissHardware.hpp | 44 +++--- TNC/Log.cpp | 21 ++- TNC/Log.h | 19 +-- TNC/Modulator.hpp | 93 +++++++++++ TNC/ModulatorTask.cpp | 29 +++- TNC/ModulatorTask.hpp | 7 +- 23 files changed, 1030 insertions(+), 746 deletions(-) create mode 100644 TNC/Afsk1200Demodulator.cpp create mode 100644 TNC/Afsk1200Demodulator.hpp create mode 100644 TNC/Demodulator.cpp create mode 100644 TNC/Demodulator.hpp create mode 100644 TNC/FilterCoefficients.cpp create mode 100644 TNC/Fsk9600Demodulator.cpp create mode 100644 TNC/Fsk9600Demodulator.hpp create mode 100644 TNC/Fsk9600Modulator.cpp create mode 100644 TNC/Fsk9600Modulator.hpp create mode 100644 TNC/Modulator.hpp diff --git a/TNC/AFSKModulator.hpp b/TNC/AFSKModulator.hpp index 7f31e04..6dcd974 100644 --- a/TNC/AFSKModulator.hpp +++ b/TNC/AFSKModulator.hpp @@ -8,6 +8,7 @@ #include "PTT.hpp" #include "Log.h" +#include "Modulator.hpp" #include "stm32l4xx_hal.h" #include "cmsis_os.h" @@ -62,7 +63,8 @@ const int16_t sin_table[SIN_TABLE_LEN] = { }; -struct AFSKModulator { +struct AFSKModulator : Modulator +{ static const size_t DAC_BUFFER_LEN = 44; static const size_t BIT_LEN = DAC_BUFFER_LEN / 2; @@ -84,7 +86,24 @@ struct AFSKModulator { buffer_[i] = 2048; } - void set_volume(uint16_t v) + void init(const kiss::Hardware& hw) override + { + set_twist(hw.tx_twist); + + // Configure 80MHz clock for 26.4ksps. + htim7.Init.Period = 3029; + if (HAL_TIM_Base_Init(&htim7) != HAL_OK) + { + ERROR("htim7 init failed"); + CxxErrorHandler(); + } + } + + void deinit() override + { + } + + void set_gain(uint16_t v) override { v = std::max(256, v); v = std::min(4096, v); @@ -103,7 +122,8 @@ struct AFSKModulator { void set_twist(uint8_t twist) {twist_ = twist;} - void send(bool bit) { + void send(bool bit) override + { switch (running_) { case -1: fill_first(bit); @@ -122,7 +142,8 @@ struct AFSKModulator { } } - void fill(uint16_t* buffer, bool bit) { + void fill(uint16_t* buffer, bool bit) + { for (size_t i = 0; i != BIT_LEN; i++) { int s = sin_table[pos_]; s -= 2048; @@ -149,15 +170,18 @@ struct AFSKModulator { } } - void fill_first(bool bit) { + void fill_first(bool bit) override + { fill(buffer_, bit); } - void fill_last(bool bit) { + void fill_last(bool bit) override + { fill(buffer_ + BIT_LEN, bit); } - void empty() { + void empty() override + { switch (running_) { case 1: running_ = 0; @@ -174,7 +198,8 @@ struct AFSKModulator { } } - void abort() { + void abort() override + { running_ = -1; HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); HAL_TIM_Base_Stop(&htim7); @@ -184,6 +209,12 @@ struct AFSKModulator { // Drain the queue. while (osMessageGet(dacOutputQueueHandle_, 0).status == osEventMessage); } + + float bits_per_ms() const override + { + return 1.2f; + } + }; }} // mobilinkd::tnc diff --git a/TNC/Afsk1200Demodulator.cpp b/TNC/Afsk1200Demodulator.cpp new file mode 100644 index 0000000..85d347e --- /dev/null +++ b/TNC/Afsk1200Demodulator.cpp @@ -0,0 +1,31 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#include "Afsk1200Demodulator.hpp" + +namespace mobilinkd { namespace tnc { + +afsk1200::emphasis_filter_type Afsk1200Demodulator::filter_1; +afsk1200::emphasis_filter_type Afsk1200Demodulator::filter_2; +afsk1200::emphasis_filter_type Afsk1200Demodulator::filter_3; + +afsk1200::Demodulator Afsk1200Demodulator::demod1(26400, Afsk1200Demodulator::filter_1); +afsk1200::Demodulator Afsk1200Demodulator::demod2(26400, Afsk1200Demodulator::filter_2); +afsk1200::Demodulator Afsk1200Demodulator::demod3(26400, Afsk1200Demodulator::filter_3); + +const q15_t Afsk1200Demodulator::bpf_coeffs[FILTER_TAP_NUM] = { + 4, 0, -5, -10, -13, -12, -9, -4, -2, -4, -12, -26, + -41, -52, -51, -35, -3, 39, 83, 117, 131, 118, 83, 36, + -6, -32, -30, -3, 36, 67, 66, 19, -74, -199, -323, -408, + -421, -344, -187, 17, 218, 364, 417, 369, 247, 106, 14, 26, + 166, 407, 676, 865, 866, 605, 68, -675, -1484, -2171, -2547, -2471, +-1895, -882, 394, 1692, 2747, 3337, 3337, 2747, 1692, 394, -882, -1895, +-2471, -2547, -2171, -1484, -675, 68, 605, 866, 865, 676, 407, 166, + 26, 14, 106, 247, 369, 417, 364, 218, 17, -187, -344, -421, + -408, -323, -199, -74, 19, 66, 67, 36, -3, -30, -32, -6, + 36, 83, 118, 131, 117, 83, 39, -3, -35, -51, -52, -41, + -26, -12, -4, -2, -4, -9, -12, -13, -10, -5, 0, 4, +}; + +}} // mobilinkd::tnc + diff --git a/TNC/Afsk1200Demodulator.hpp b/TNC/Afsk1200Demodulator.hpp new file mode 100644 index 0000000..cd818c2 --- /dev/null +++ b/TNC/Afsk1200Demodulator.hpp @@ -0,0 +1,150 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#pragma once + +#include "Demodulator.hpp" +#include "AfskDemodulator.hpp" +#include "FirFilter.hpp" +#include "FilterCoefficients.hpp" +#include "KissHardware.hpp" +#include "HdlcFrame.hpp" + +namespace mobilinkd { namespace tnc { + +struct Afsk1200Demodulator : IDemodulator +{ + /* + * Generated with Scipy Filter, 152 coefficients, 1100-2300Hz bandpass, + * Hann window, starting and ending 0 value coefficients removed. + * + * np.array( + * firwin2(152, + * [ + * 0.0, + * 1000.0/(sample_rate/2), + * 1100.0/(sample_rate/2), + * 2350.0/(sample_rate/2), + * 2500.0/(sample_rate/2), + * 1.0 + * ], + * [0,0,1,1,0,0], + * antisymmetric = False, + * window='hann') * 32768, + * dtype=int)[10:-10] + */ + static constexpr size_t FILTER_TAP_NUM = 132; + static const q15_t bpf_coeffs[FILTER_TAP_NUM]; + + static afsk1200::emphasis_filter_type filter_1; + static afsk1200::emphasis_filter_type filter_2; + static afsk1200::emphasis_filter_type filter_3; + + static afsk1200::Demodulator demod1; + static afsk1200::Demodulator demod2; + static afsk1200::Demodulator demod3; + + uint16_t last_fcs{0}; + uint32_t last_counter{0}; + uint32_t counter{0}; + bool locked_{false}; + + virtual ~Afsk1200Demodulator() {} + + void start() override + { + // rx_twist is 6dB for discriminator input and 0db for de-emphasized input. + auto twist = kiss::settings().rx_twist; + + filter_1.init(*filter::fir::AfskFilters[twist + 3]); + filter_2.init(*filter::fir::AfskFilters[twist + 6]); + filter_3.init(*filter::fir::AfskFilters[twist + 9]); + + last_fcs = 0; + last_counter = 0; + counter = 0; + + demod_filter.init(bpf_coeffs); + startADC(3029); + } + + void stop() override + { + stopADC(); + locked_ = false; + } + + hdlc::IoFrame* operator()(const q15_t* samples) override + { + hdlc::IoFrame* result = nullptr; + + q15_t* filtered = demod_filter(const_cast(samples)); + + ++counter; + +#if 1 + auto frame1 = demod1(filtered, audio::ADC_BUFFER_SIZE); + if (frame1) + { + if (frame1->fcs() != last_fcs or counter > last_counter + 2) + { + last_fcs = frame1->fcs(); + last_counter = counter; + result = frame1; + } + else + { + hdlc::release (frame1); + } + } +#endif + +#if 1 + auto frame2 = demod2(filtered, audio::ADC_BUFFER_SIZE); + if (frame2) + { + if (frame2->fcs() != last_fcs or counter > last_counter + 2) + { + last_fcs = frame2->fcs(); + last_counter = counter; + result = frame2; + } + else + { + hdlc::release(frame2); + } + } +#endif + +#if 1 + auto frame3 = demod3(filtered, audio::ADC_BUFFER_SIZE); + if (frame3) + { + if (frame3->fcs() != last_fcs or counter > last_counter + 2) + { + last_fcs = frame3->fcs(); + last_counter = counter; + result = frame3; + } + else + { + hdlc::release(frame3); + } + } +#endif + locked_ = demod1.locked() or demod2.locked() or demod3.locked(); + return result; + } + + bool locked() const override + { + return locked_; + } + + size_t size() const override + { + return audio::ADC_BUFFER_SIZE; + } +}; + +}} // mobilinkd::tnc diff --git a/TNC/AudioInput.cpp b/TNC/AudioInput.cpp index 2bde7e5..9b08022 100644 --- a/TNC/AudioInput.cpp +++ b/TNC/AudioInput.cpp @@ -2,7 +2,9 @@ // All rights reserved. #include "AudioInput.hpp" -#include "AfskDemodulator.hpp" +#include "Demodulator.hpp" +#include "Afsk1200Demodulator.hpp" +#include "Fsk9600Demodulator.hpp" #include "AudioLevel.hpp" #include "Log.h" #include "KissHardware.hpp" @@ -10,10 +12,9 @@ #include "HdlcFrame.hpp" #include "memory.hpp" #include "IirFilter.hpp" -#include "FilterCoefficients.hpp" #include "PortInterface.hpp" #include "Goertzel.h" -#include "DCD.h" +#include "Led.h" #include "arm_math.h" #include "stm32l4xx_hal.h" @@ -93,10 +94,6 @@ extern "C" void startAudioInputTask(void const*) { DEBUG("POLL_AMPLIFIED_INPUT_LEVEL"); pollAmplifiedInputLevel(); break; - case POLL_BATTERY_LEVEL: - DEBUG("POLL_BATTERY_LEVEL"); - pollBatteryLevel(); - break; case POLL_TWIST_LEVEL: DEBUG("POLL_TWIST_LEVEL"); pollInputTwist(); @@ -132,96 +129,33 @@ extern "C" void startAudioInputTask(void const*) { namespace mobilinkd { namespace tnc { namespace audio { -/* - * Generated with Scipy Filter, 152 coefficients, 1100-2300Hz bandpass, - * Hann window, starting and ending 0 value coefficients removed. - * - * np.array( - * firwin2(152, - * [ - * 0.0, - * 1000.0/(sample_rate/2), - * 1100.0/(sample_rate/2), - * 2350.0/(sample_rate/2), - * 2500.0/(sample_rate/2), - * 1.0 - * ], - * [0,0,1,1,0,0], - * antisymmetric = False, - * window='hann') * 32768, - * dtype=int)[10:-10] - */ -constexpr size_t FILTER_TAP_NUM = 132; -const q15_t bpf_coeffs[] = { - 4, 0, -5, -10, -13, -12, -9, -4, -2, -4, -12, -26, - -41, -52, -51, -35, -3, 39, 83, 117, 131, 118, 83, 36, - -6, -32, -30, -3, 36, 67, 66, 19, -74, -199, -323, -408, - -421, -344, -187, 17, 218, 364, 417, 369, 247, 106, 14, 26, - 166, 407, 676, 865, 866, 605, 68, -675, -1484, -2171, -2547, -2471, --1895, -882, 394, 1692, 2747, 3337, 3337, 2747, 1692, 394, -882, -1895, --2471, -2547, -2171, -1484, -675, 68, 605, 866, 865, 676, 407, 166, - 26, 14, 106, 247, 369, 417, 364, 218, 17, -187, -344, -421, - -408, -323, -199, -74, 19, 66, 67, 36, -3, -30, -32, -6, - 36, 83, 118, 131, 117, 83, 39, -3, -35, -51, -52, -41, - -26, -12, -4, -2, -4, -9, -12, -13, -10, -5, 0, 4, -}; - uint32_t adc_buffer[ADC_BUFFER_SIZE]; // Two samples per element. - -typedef Q15FirFilter audio_filter_type; - -audio_filter_type audio_filter; - -mobilinkd::tnc::afsk1200::emphasis_filter_type filter_1; -mobilinkd::tnc::afsk1200::emphasis_filter_type filter_2; -mobilinkd::tnc::afsk1200::emphasis_filter_type filter_3; - -mobilinkd::tnc::afsk1200::Demodulator& getDemod1(const TFirCoefficients<9>& f) __attribute__((noinline)); -mobilinkd::tnc::afsk1200::Demodulator& getDemod2(const TFirCoefficients<9>& f) __attribute__((noinline)); -mobilinkd::tnc::afsk1200::Demodulator& getDemod3(const TFirCoefficients<9>& f) __attribute__((noinline)); - -mobilinkd::tnc::afsk1200::Demodulator& getDemod1(const TFirCoefficients<9>& f) { - filter_1.init(f); - static mobilinkd::tnc::afsk1200::Demodulator instance(26400, filter_1); - return instance; -} - -mobilinkd::tnc::afsk1200::Demodulator& getDemod2(const TFirCoefficients<9>& f) { - filter_2.init(f); - static mobilinkd::tnc::afsk1200::Demodulator instance(26400, filter_2); - return instance; -} - -mobilinkd::tnc::afsk1200::Demodulator& getDemod3(const TFirCoefficients<9>& f) { - filter_3.init(f); - static mobilinkd::tnc::afsk1200::Demodulator instance(26400, filter_3); - return instance; -} - q15_t normalized[ADC_BUFFER_SIZE]; +Afsk1200Demodulator afsk1200; +Fsk9600Demodulator fsk9600; + void demodulatorTask() { DEBUG("enter demodulatorTask"); - audio_filter.init(bpf_coeffs); - - // rx_twist is 6dB for discriminator input and 0db for de-emphasized input. - auto twist = kiss::settings().rx_twist; - - mobilinkd::tnc::afsk1200::Demodulator& demod1 = getDemod1(*filter::fir::AfskFilters[twist + 3]); - mobilinkd::tnc::afsk1200::Demodulator& demod2 = getDemod2(*filter::fir::AfskFilters[twist + 6]); - mobilinkd::tnc::afsk1200::Demodulator& demod3 = getDemod3(*filter::fir::AfskFilters[twist + 9]); - - startADC(AUDIO_IN); - - mobilinkd::tnc::hdlc::IoFrame* frame = 0; - - uint16_t last_fcs = 0; - uint32_t last_counter = 0; - uint32_t counter = 0; - bool dcd_status{false}; + IDemodulator* demodulator{nullptr}; + + switch (kiss::settings().modem_type) + { + case kiss::Hardware::ModemType::AFSK1200: + demodulator = &afsk1200; + break; + case kiss::Hardware::ModemType::FSK9600: + demodulator = &fsk9600; + break; + default: + ERROR("Invalid demodulator"); + return; + } + + demodulator->start(); while (true) { osEvent peek = osMessagePeek(audioInputQueueHandle, 0); @@ -232,81 +166,36 @@ void demodulatorTask() { continue; } - ++counter; - + led_other_on(); auto block = (adc_pool_type::chunk_type*) evt.value.p; auto samples = (int16_t*) block->buffer; arm_offset_q15(samples, 0 - virtual_ground, normalized, ADC_BUFFER_SIZE); adcPool.deallocate(block); - q15_t* audio = audio_filter(normalized); -#if 1 - frame = demod1(audio, ADC_BUFFER_SIZE); - if (frame) { - if (frame->fcs() != last_fcs or counter > last_counter + 2) { - auto save_fcs = frame->fcs(); - if (osMessagePut(ioEventQueueHandle, (uint32_t) frame, 1) == osOK) { - last_fcs = save_fcs; - last_counter = counter; - } else { - hdlc::release(frame); - } - } - else { + auto frame = (*demodulator)(normalized); + if (frame) + { + if (osMessagePut(ioEventQueueHandle, (uint32_t) frame, 1) != osOK) + { hdlc::release(frame); } } -#endif -#if 1 - frame = demod2(audio, ADC_BUFFER_SIZE); - if (frame) { - if (frame->fcs() != last_fcs or counter > last_counter + 2) { - auto save_fcs = frame->fcs(); - if (osMessagePut(ioEventQueueHandle, (uint32_t) frame, 1) == osOK) { - last_fcs = save_fcs; - last_counter = counter; - } else { - hdlc::release(frame); - } - } - else { - hdlc::release(frame); - } - } -#endif - -#if 1 - frame = demod3(audio, ADC_BUFFER_SIZE); - if (frame) { - if (frame->fcs() != last_fcs or counter > last_counter + 2) { - auto save_fcs = frame->fcs(); - if (osMessagePut(ioEventQueueHandle, (uint32_t) frame, 1) == osOK) { - last_fcs = save_fcs; - last_counter = counter; - } else { - hdlc::release(frame); - } - } - else { - hdlc::release(frame); - } - } -#endif - bool new_dcd_status = demod1.locked() or demod2.locked() or demod3.locked(); - if (new_dcd_status xor dcd_status) { - dcd_status = new_dcd_status; + if (demodulator->locked() xor dcd_status) { + dcd_status = demodulator->locked(); if (dcd_status) { - dcd_on(); + led_dcd_on(); } else { - dcd_off(); + led_dcd_off(); } } + led_other_off(); } - stopADC(); - dcd_off(); + demodulator->stop(); + + led_dcd_off(); DEBUG("exit demodulatorTask"); } @@ -732,82 +621,6 @@ void pollAmplifiedInputLevel() { DEBUG("exit pollAmplifiedInputLevel"); } -void pollBatteryLevel() { - DEBUG("enter pollBatteryLevel"); - - ADC_ChannelConfTypeDef sConfig; - - sConfig.Channel = ADC_CHANNEL_VREFINT; - sConfig.Rank = ADC_REGULAR_RANK_1; - sConfig.SingleDiff = ADC_SINGLE_ENDED; - sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5; - sConfig.OffsetNumber = ADC_OFFSET_NONE; - sConfig.Offset = 0; - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) - CxxErrorHandler(); - - htim6.Init.Period = 48000; - if (HAL_TIM_Base_Init(&htim6) != HAL_OK) CxxErrorHandler(); - - if (HAL_TIM_Base_Start(&htim6) != HAL_OK) - CxxErrorHandler(); - - if (HAL_ADC_Start(&hadc1) != HAL_OK) CxxErrorHandler(); - if (HAL_ADC_PollForConversion(&hadc1, 3) != HAL_OK) CxxErrorHandler(); - auto vrefint = HAL_ADC_GetValue(&hadc1); - if (HAL_ADC_Stop(&hadc1) != HAL_OK) CxxErrorHandler(); - - // Disable battery charging while measuring battery voltage. - auto usb_ce = gpio::USB_CE::get(); - gpio::USB_CE::on(); - - gpio::BAT_DIVIDER::off(); - HAL_Delay(1); - - sConfig.Channel = ADC_CHANNEL_15; - if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) - CxxErrorHandler(); - - uint32_t vbat = 0; - if (HAL_ADC_Start(&hadc1) != HAL_OK) CxxErrorHandler(); - for (size_t i = 0; i != 8; ++i) - { - if (HAL_ADC_PollForConversion(&hadc1, 1) != HAL_OK) CxxErrorHandler(); - vbat += HAL_ADC_GetValue(&hadc1); - } - - vbat /= 8; - - if (HAL_ADC_Stop(&hadc1) != HAL_OK) CxxErrorHandler(); - if (HAL_TIM_Base_Stop(&htim6) != HAL_OK) - CxxErrorHandler(); - - htim6.Init.Period = 1817; - if (HAL_TIM_Base_Init(&htim6) != HAL_OK) CxxErrorHandler(); - - HAL_Delay(1); - - gpio::BAT_DIVIDER::on(); - if (!usb_ce) gpio::USB_CE::off(); // Restore battery charging state. - - INFO("Vref = %lu", vrefint); - INFO("Vbat = %lu (raw)", vbat); - - // Order of operations is important to avoid underflow. - vbat *= 6600; - vbat /= (vref + 1); - - INFO("Vbat = %lumV", vbat); - - uint8_t data[3]; - data[0] = kiss::hardware::GET_BATTERY_LEVEL; - data[1] = (vbat >> 8) & 0xFF; - data[2] = (vbat & 0xFF); - - ioport->write(data, 3, 6, 10); - DEBUG("exit pollBatteryLevel"); -} - #if 0 void stop() { osDelay(100); diff --git a/TNC/AudioLevel.cpp b/TNC/AudioLevel.cpp index ea7f3f4..52a2969 100644 --- a/TNC/AudioLevel.cpp +++ b/TNC/AudioLevel.cpp @@ -6,7 +6,7 @@ #include "KissHardware.hpp" #include "ModulatorTask.hpp" #include "GPIO.hpp" -#include "LEDIndicator.h" +#include "Led.h" #include "ModulatorTask.hpp" #include "main.h" @@ -199,7 +199,7 @@ void setAudioOutputLevel() } else { gpio::AUDIO_OUT_ATTEN::off(); } - getModulator().set_volume(r); + getModulator().set_gain(r); } }}} // mobilinkd::tnc::audio diff --git a/TNC/Demodulator.cpp b/TNC/Demodulator.cpp new file mode 100644 index 0000000..6e43c95 --- /dev/null +++ b/TNC/Demodulator.cpp @@ -0,0 +1,10 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#include "Demodulator.hpp" + +namespace mobilinkd { namespace tnc { + +audio_filter_t demod_filter; + +}} // mobilinkd::tnc diff --git a/TNC/Demodulator.hpp b/TNC/Demodulator.hpp new file mode 100644 index 0000000..ca690f9 --- /dev/null +++ b/TNC/Demodulator.hpp @@ -0,0 +1,72 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#pragma once + +#include "HdlcFrame.hpp" +#include "FirFilter.hpp" +#include "AudioInput.hpp" + +#include + +#include + +namespace mobilinkd { namespace tnc { + +constexpr size_t FILTER_TAP_NUM = 132; + +using demod_filter_t = std::function; +using demodulator_t = std::function; +using audio_filter_t = Q15FirFilter; + +extern audio_filter_t demod_filter; + +struct IDemodulator +{ + virtual void start() = 0; + virtual void stop() = 0; + virtual hdlc::IoFrame* operator()(const q15_t* samples) = 0; + virtual bool locked() const = 0; + virtual size_t size() const = 0; + + virtual ~IDemodulator() {} + + static void startADC(uint32_t period) { + ADC_ChannelConfTypeDef sConfig; + + sConfig.Channel = AUDIO_IN; + sConfig.Rank = ADC_REGULAR_RANK_1; + sConfig.SingleDiff = ADC_SINGLE_ENDED; + sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5; + sConfig.OffsetNumber = ADC_OFFSET_NONE; + sConfig.Offset = 0; + if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) + CxxErrorHandler(); + + htim6.Init.Period = period; + htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if (HAL_TIM_Base_Init(&htim6) != HAL_OK) + { + CxxErrorHandler(); + } + + if (HAL_TIM_Base_Start(&htim6) != HAL_OK) + { + CxxErrorHandler(); + } + if (HAL_ADC_Start_DMA(&hadc1, audio::adc_buffer, + audio::ADC_BUFFER_SIZE * 2) != HAL_OK) + { + CxxErrorHandler(); + } + } + + static void stopADC() { + if (HAL_ADC_Stop_DMA(&hadc1) != HAL_OK) + CxxErrorHandler(); + if (HAL_TIM_Base_Stop(&htim6) != HAL_OK) + CxxErrorHandler(); + } +}; + +}} // mobilinkd::tnc diff --git a/TNC/DigitalPLL.hpp b/TNC/DigitalPLL.hpp index 802dd28..b0c6720 100644 --- a/TNC/DigitalPLL.hpp +++ b/TNC/DigitalPLL.hpp @@ -31,9 +31,11 @@ struct PLLResult { // const std::array lock_b = { 1.077063e-03,4.308253e-03,6.462379e-03,4.308253e-03,1.077063e-03, +// 4.33403839e-07, 1.73361536e-06, 2.60042303e-06, 1.73361536e-06, 4.33403839e-07 }; const std::array lock_a = { 1.000000e+00,-2.774567e+00,2.962960e+00,-1.437990e+00,2.668296e-01, +// 1.0, -3.83776867, 5.52505504, -3.53636636, 0.84908692 }; // 64 Hz loop filter. @@ -43,7 +45,9 @@ const std::array lock_a = { // const std::array loop_coeffs = { // 0.08160962754214955, 0.25029850550446403, 0.3361837339067726, 0.2502985055044641, 0.08160962754214969 - 3.196252e-02,1.204223e-01,2.176819e-01,2.598666e-01,2.176819e-01,1.204223e-01,3.196252e-02 +// 3.196252e-02,1.204223e-01,2.176819e-01,2.598666e-01,2.176819e-01,1.204223e-01,3.196252e-02 +// 0.03653765, 0.12493, 0.21345609, 0.25015251, 0.21345609, 0.12493, 0.03653765 + 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0 }; } // pll diff --git a/TNC/FilterCoefficients.cpp b/TNC/FilterCoefficients.cpp new file mode 100644 index 0000000..d335794 --- /dev/null +++ b/TNC/FilterCoefficients.cpp @@ -0,0 +1,35 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#include "FilterCoefficients.hpp" + +namespace mobilinkd { namespace tnc { namespace filter { + +namespace fir { + +const TFirCoefficients<9>* AfskFilters[19] = { + &dB_6, + &dB_5, + &dB_4, + &dB_3, + &dB_2, + &dB_1, + &dB0, + &dB1, + &dB2, + &dB3, + &dB4, + &dB5, + &dB6, + &dB7, + &dB8, + &dB9, + &dB10, + &dB11, + &dB12 +}; + +} // fir + + +}}} // mobilinkd::tnc::filter diff --git a/TNC/FilterCoefficients.hpp b/TNC/FilterCoefficients.hpp index 5711dfc..f422526 100644 --- a/TNC/FilterCoefficients.hpp +++ b/TNC/FilterCoefficients.hpp @@ -297,27 +297,7 @@ const TFirCoefficients<9> dB_6 = { } }; -const TFirCoefficients<9>* AfskFilters[] = { - &dB_6, - &dB_5, - &dB_4, - &dB_3, - &dB_2, - &dB_1, - &dB0, - &dB1, - &dB2, - &dB3, - &dB4, - &dB5, - &dB6, - &dB7, - &dB8, - &dB9, - &dB10, - &dB11, - &dB12 -}; +extern const TFirCoefficients<9>* AfskFilters[19]; } // fir diff --git a/TNC/Fsk9600Demodulator.cpp b/TNC/Fsk9600Demodulator.cpp new file mode 100644 index 0000000..2c0689f --- /dev/null +++ b/TNC/Fsk9600Demodulator.cpp @@ -0,0 +1,54 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#include "Fsk9600Demodulator.hpp" + +namespace mobilinkd { namespace tnc { + +#if 0 +// 96k +const q15_t Fsk9600Demodulator::bpf_coeffs[FILTER_TAP_NUM] = { + -3, -3, -3, -3, -3, -2, -2, -3, -4, -7, -10, -14, -17, -20, + -22, -23, -22, -21, -19, -17, -17, -18, -20, -24, -27, -29, -28, -25, + -20, -13, -7, -6, -10, -24, -46, -75, -108, -138, -158, -160, -140, -94, + -26, 55, 140, 210, 249, 240, 174, 49, -126, -332, -539, -710, -804, -784, + -624, -309, 156, 750, 1430, 2140, 2816, 3394, 3816, 4038, 4038, 3816, 3394, 2816, + 2140, 1430, 750, 156, -309, -624, -784, -804, -710, -539, -332, -126, 49, 174, + 240, 249, 210, 140, 55, -26, -94, -140, -160, -158, -138, -108, -75, -46, + -24, -10, -6, -7, -13, -20, -25, -28, -29, -27, -24, -20, -18, -17, + -17, -19, -21, -22, -23, -22, -20, -17, -14, -10, -7, -4, -3, -2, + -2, -3, -3, -3, -3, -3 +}; + +// 144k +const q15_t Fsk9600Demodulator::bpf_coeffs[FILTER_TAP_NUM] = { + -2, -3, -3, -4, -5, -6, -6, -7, -7, -7, -7, -6, -6, -6, + -6, -7, -10, -14, -19, -26, -35, -44, -54, -63, -70, -75, -76, -73, + -64, -50, -30, -6, 21, 49, 76, 99, 115, 120, 112, 89, 50, -3, + -71, -150, -235, -321, -400, -465, -509, -523, -501, -437, -329, -174, 25, 265, + 539, 839, 1153, 1468, 1771, 2049, 2289, 2480, 2612, 2680, 2680, 2612, 2480, 2289, + 2049, 1771, 1468, 1153, 839, 539, 265, 25, -174, -329, -437, -501, -523, -509, + -465, -400, -321, -235, -150, -71, -3, 50, 89, 112, 120, 115, 99, 76, + 49, 21, -6, -30, -50, -64, -73, -76, -75, -70, -63, -54, -44, -35, + -26, -19, -14, -10, -7, -6, -6, -6, -6, -7, -7, -7, -7, -6, + -6, -5, -4, -3, -3, -2 +}; +#endif + +// 192k +const q15_t Fsk9600Demodulator::bpf_coeffs[FILTER_TAP_NUM] = { + -1, -2, -3, -4, -6, -8, -11, -14, -17, -20, -24, -27, -30, -31, + -32, -32, -30, -26, -21, -14, -5, 4, 14, 26, 37, 47, 55, 60, + 62, 59, 51, 37, 17, -8, -39, -76, -117, -161, -206, -250, -292, -327, + -355, -372, -376, -365, -336, -289, -222, -136, -29, 95, 237, 394, 562, 738, + 919, 1099, 1274, 1440, 1591, 1725, 1837, 1924, 1983, 2013, 2013, 1983, 1924, 1837, + 1725, 1591, 1440, 1274, 1099, 919, 738, 562, 394, 237, 95, -29, -136, -222, + -289, -336, -365, -376, -372, -355, -327, -292, -250, -206, -161, -117, -76, -39, + -8, 17, 37, 51, 59, 62, 60, 55, 47, 37, 26, 14, 4, -5, + -14, -21, -26, -30, -32, -32, -31, -30, -27, -24, -20, -17, -14, -11, + -8, -6, -4, -3, -2, -1 +}; + + +}} // mobilinkd::tnc + diff --git a/TNC/Fsk9600Demodulator.hpp b/TNC/Fsk9600Demodulator.hpp new file mode 100644 index 0000000..3af9a03 --- /dev/null +++ b/TNC/Fsk9600Demodulator.hpp @@ -0,0 +1,117 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#pragma once + +#include "Demodulator.hpp" +#include "AudioLevel.hpp" +#include "AudioInput.hpp" +#include "DigitalPLL.hpp" +#include "NRZI.hpp" +#include "HdlcDecoder.hpp" + +namespace mobilinkd { namespace tnc { + +struct Descrambler +{ + uint32_t state{0}; + + bool operator()(bool bit) + { + bool result = (bit ^ (state >> 16) ^ (state >> 11)) & 1; + state = ((state << 1) | (bit & 1)) & 0x1FFFF; + return result; + } +}; + +struct Fsk9600Demodulator : IDemodulator +{ + /* + * Generated with Scipy Filter, 152 coefficients, 55-4900Hz bandpass, + * Hann window, starting and ending minimal value coefficients removed. + * + * np.array( + * firwin2(152, + * [ + * 0.0, + * 40.0/(sample_rate/2), + * 55.0/(sample_rate/2), + * 4900.0/(sample_rate/2), + * 7200.0/(sample_rate/2), + * 1.0 + * ], + * [0,0,1,1,0,0], + * antisymmetric = False, + * window='hann') * 32768, + * dtype=int)[10:-10] + */ + static constexpr size_t FILTER_TAP_NUM = 132; + static const q15_t bpf_coeffs[FILTER_TAP_NUM]; + + BaseDigitalPLL pll_{192000,9600}; + bool locked_{false}; + Descrambler lfsr_; + libafsk::NRZI nrzi_; + hdlc::NewDecoder hdlc_decoder_; + + virtual ~Fsk9600Demodulator() {} + + void start() override + { + demod_filter.init(bpf_coeffs); + hadc1.Init.OversamplingMode = DISABLE; + if (HAL_ADC_Init(&hadc1) != HAL_OK) + { + _Error_Handler(__FILE__, __LINE__); + } + startADC(416); + } + + void stop() override + { + stopADC(); + locked_ = false; + } + + hdlc::IoFrame* operator()(const q15_t* samples) override + { + hdlc::IoFrame* result = nullptr; + + auto filtered = demod_filter(const_cast(samples)); + + for (size_t i = 0; i != audio::ADC_BUFFER_SIZE; ++i) + { + auto sample = filtered[i]; + + bool bit = sample >= 0; + auto pll = pll_(bit); + + if (pll.sample) + { + locked_ = pll.locked; + + // We will only ever get one frame because there are + // not enough bits in a block for more than one. + if (result) { + auto tmp = hdlc_decoder_(nrzi_.decode(lfsr_(bit)), true); + if (tmp) hdlc::release(tmp); + } else { + result = hdlc_decoder_(nrzi_.decode(lfsr_(bit)), true); + } + } + } + return result; + } + + bool locked() const override + { + return locked_; + } + + constexpr size_t size() const override + { + return audio::ADC_BUFFER_SIZE; + } +}; + +}} // mobilinkd::tnc diff --git a/TNC/Fsk9600Modulator.cpp b/TNC/Fsk9600Modulator.cpp new file mode 100644 index 0000000..c461db6 --- /dev/null +++ b/TNC/Fsk9600Modulator.cpp @@ -0,0 +1,16 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#include "Fsk9600Modulator.hpp" + +namespace mobilinkd { namespace tnc { + +#if 0 +const std::array Fsk9600Modulator::cos_table = { + 2047, 2020, 1937, 1801, 1616, 1387, 1120, 822, 502, 169, + -169, -502, -822, -1120, -1387, -1616, -1801, -1937, -2020, -2048 +}; + +#endif + +}} // mobilinkd::tnc diff --git a/TNC/Fsk9600Modulator.hpp b/TNC/Fsk9600Modulator.hpp new file mode 100644 index 0000000..f9e491a --- /dev/null +++ b/TNC/Fsk9600Modulator.hpp @@ -0,0 +1,216 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#pragma once + +#include "Modulator.hpp" + +#include +#include +#include + +namespace mobilinkd { namespace tnc { + +struct Scrambler +{ + uint32_t state{0}; + + bool operator()(bool bit) + { + bool result = (bit ^ (state >> 16) ^ (state >> 11)) & 1; + state = ((state << 1) | result) & 0x1FFFF; + return result; + } +}; + +struct Fsk9600Modulator : Modulator +{ + static constexpr int8_t DAC_BUFFER_LEN = 40; + static constexpr int8_t BIT_LEN = DAC_BUFFER_LEN / 2; + + static constexpr std::array cos_table{ + 2047, 2020, 1937, 1801, 1616, 1387, 1120, 822, 502, 169, + -169, -502, -822, -1120, -1387, -1616, -1801, -1937, -2020, -2048 + }; + + enum class Level { ZERO, HIGH, LOW }; + enum class State { STOPPED, STARTING, RUNNING, STOPPING }; + + osMessageQId dacOutputQueueHandle_{0}; + PTT* ptt_{nullptr}; + uint16_t volume_{4096}; + std::array buffer_; + Level level{Level::HIGH}; + State state{State::STOPPED}; + Scrambler lfsr; + + Fsk9600Modulator(osMessageQId queue, PTT* ptt) + : dacOutputQueueHandle_(queue), ptt_(ptt) + {} + + ~Fsk9600Modulator() override {} + + void init(const kiss::Hardware& hw) override + { + for (auto& x : buffer_) x = 2048; + + state = State::STOPPED; + level = Level::HIGH; + + // Configure 80MHz clock for 192ksps. + htim7.Init.Period = 416; + if (HAL_TIM_Base_Init(&htim7) != HAL_OK) + { + ERROR("htim7 init failed"); + CxxErrorHandler(); + } + } + + void deinit() override + { + state = State::STOPPED; + HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); + HAL_TIM_Base_Stop(&htim7); + ptt_->off(); + } + + void set_gain(uint16_t level) override + { + auto v = std::max(256, level); + v = std::min(4096, v); + volume_ = v; + } + + void set_ptt(PTT* ptt) override + { + if (state != State::STOPPED) + { + ERROR("PTT change while not stopped"); + CxxErrorHandler(); + } + ptt_ = ptt; + ptt_->off(); + } + + void send(bool bit) override + { + auto scrambled = lfsr(bit); + + switch (state) + { + case State::STOPPING: + case State::STOPPED: + fill_first(scrambled); + state = State::STARTING; + break; + case State::STARTING: + fill_last(scrambled); + state = State::RUNNING; + ptt_->on(); + HAL_TIM_Base_Start(&htim7); + HAL_DAC_Start_DMA( + &hdac1, DAC_CHANNEL_1, + reinterpret_cast(buffer_.data()), buffer_.size(), + DAC_ALIGN_12B_R); + break; + case State::RUNNING: + osMessagePut(dacOutputQueueHandle_, scrambled, osWaitForever); + break; + } + } + + // DAC DMA interrupt functions. + + void fill_first(bool bit) override + { + fill(buffer_.data(), bit); + } + + void fill_last(bool bit) override + { + fill(buffer_.data() + BIT_LEN, bit); + } + + void empty() override + { + switch (state) + { + case State::STARTING: + // fall-through + case State::RUNNING: + state = State::STOPPING; + break; + case State::STOPPING: + state = State::STOPPED; + HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); + HAL_TIM_Base_Stop(&htim7); + ptt_->off(); + break; + case State::STOPPED: + break; + } + state = State::STOPPING; + level = Level::HIGH; + } + + void abort() override + { + state = State::STOPPED; + HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1); + HAL_TIM_Base_Stop(&htim7); + ptt_->off(); + + // Drain the queue. + while (osMessageGet(dacOutputQueueHandle_, 0).status == osEventMessage); + } + + float bits_per_ms() const override + { + return 9.6f; + } + +private: + + uint16_t adjust_level(int32_t sample) const + { + sample *= volume_; + sample >>= 12; + sample += 2048; + return sample; + } + + void fill(uint16_t* buffer, bool bit) + { + switch (level) + { + case Level::HIGH: + if (bit) + { + std::fill(buffer, buffer + BIT_LEN, adjust_level(2047)); + } + else + { + std::transform(cos_table.begin(), cos_table.end(), buffer, + [this](auto x){return adjust_level(x);}); + level = Level::LOW; + } + break; + case Level::LOW: + if (bit) + { + std::transform(cos_table.rbegin(), cos_table.rend(), buffer, + [this](auto x){return adjust_level(x);}); + level = Level::HIGH; + } + else + { + std::fill(buffer, buffer + BIT_LEN, adjust_level(-2048)); + } + break; + default: + CxxErrorHandler(); + } + } +}; + +}} // mobilinkd::tnc diff --git a/TNC/HDLCEncoder.hpp b/TNC/HDLCEncoder.hpp index ea8ccf8..592701b 100644 --- a/TNC/HDLCEncoder.hpp +++ b/TNC/HDLCEncoder.hpp @@ -4,14 +4,14 @@ #ifndef INC_HDLCENCODER_HPP_ #define INC_HDLCENCODER_HPP_ -#include "AFSKModulator.hpp" +#include "Modulator.hpp" #include "HdlcFrame.hpp" #include "NRZI.hpp" #include "PTT.hpp" #include "GPIO.hpp" #include "KissHardware.hpp" #include "AudioInput.hpp" -#include "DCD.h" +#include "Led.h" #include "main.h" @@ -47,11 +47,11 @@ struct Encoder { NRZI nrzi_; uint16_t crc_; osMessageQId input_; - AFSKModulator* modulator_; + Modulator* modulator_; volatile bool running_; bool send_delay_; // Avoid sending the preamble for back-to-back frames. - Encoder(osMessageQId input, AFSKModulator* output) + Encoder(osMessageQId input, Modulator* output) : tx_delay_(kiss::settings().txdelay), tx_tail_(kiss::settings().txtail) , p_persist_(kiss::settings().ppersist), slot_time_(kiss::settings().slot) , duplex_(kiss::settings().duplex), state_(state_type::STATE_IDLE) @@ -131,7 +131,7 @@ struct Encoder { // Wait until we can transmit. If we cannot transmit for 10s // drop the frame. - if (!dcd()) { + if (!led_dcd_status()) { // Channel is clear... send now. return true; } @@ -142,7 +142,7 @@ struct Encoder { counter += slot_time_; if (rng_() < p_persist_) { - if (!dcd()) { + if (!led_dcd_status()) { // Channel is clear... send now. return true; } @@ -192,7 +192,7 @@ struct Encoder { } void send_delay() { - const size_t tmp = (tx_delay_ * 3) / 2; + const size_t tmp = tx_delay_ * modulator_->bits_per_ms(); for (size_t i = 0; i != tmp; i++) { send_raw(IDLE); } diff --git a/TNC/IOEventTask.cpp b/TNC/IOEventTask.cpp index 148fafc..9cf2b36 100644 --- a/TNC/IOEventTask.cpp +++ b/TNC/IOEventTask.cpp @@ -1,4 +1,4 @@ -// Copyright 2018 Rob Riggs +// Copyright 2017-2019 Rob Riggs // All rights reserved. #include "AudioLevel.hpp" @@ -12,27 +12,14 @@ #include "Kiss.hpp" #include "KissHardware.hpp" #include "ModulatorTask.hpp" -#include "UsbPort.hpp" -#include "SerialPort.hpp" -#include "NullPort.hpp" -#include "LEDIndicator.h" -#include "bm78.h" +#include "SerialPort.h" +#include "Led.h" +#include "main.h" #include "stm32l4xx_hal.h" -#include "usbd_cdc_if.h" -#include "usb_device.h" -#include "usbd_core.h" #include "cmsis_os.h" extern osMessageQId hdlcOutputQueueHandle; -extern PCD_HandleTypeDef hpcd_USB_FS; -extern osTimerId usbShutdownTimerHandle; - -extern "C" void stop2(void); -extern "C" void shutdown(void const * argument); -extern "C" void startLedBlinkerTask(void const*); - -volatile int cdc_connected{0}; static PTT getPttStyle(const mobilinkd::tnc::kiss::Hardware& hardware) { @@ -43,78 +30,30 @@ void startIOEventTask(void const*) { using namespace mobilinkd::tnc; - if (!go_back_to_sleep) { - indicate_on(); + initSerial(); + openSerial(); - print_startup_banner(); - } + print_startup_banner(); auto& hardware = kiss::settings(); - - if (! hardware.load() or reset_requested or !hardware.crc_ok()) + if (!hardware.load() or !hardware.crc_ok()) { - if (reset_requested) { - INFO("Hardware reset requested."); - } - hardware.init(); hardware.store(); } + hardware.init(); osMutexRelease(hardwareInitMutexHandle); - if (!go_back_to_sleep) { + hardware.debug(); - hardware.debug(); + audio::init_log_volume(); + audio::setAudioOutputLevel(); + audio::setAudioInputLevels(); + setPtt(getPttStyle(hardware)); - audio::init_log_volume(); - audio::setAudioOutputLevel(); - audio::setAudioInputLevels(); - setPtt(getPttStyle(hardware)); - - // Cannot enable these interrupts until we start the io loop because - // they send messages on the queue. - HAL_NVIC_SetPriority(SW_POWER_EXTI_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(SW_POWER_EXTI_IRQn); - - HAL_NVIC_SetPriority(SW_BOOT_EXTI_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(SW_BOOT_EXTI_IRQn); - - HAL_NVIC_SetPriority(EXTI4_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(EXTI4_IRQn); - - HAL_NVIC_SetPriority(EXTI9_5_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); - - // FIXME: this is probably not right - if (HAL_GPIO_ReadPin(BT_STATE2_GPIO_Port, BT_STATE2_Pin) == GPIO_PIN_RESET) - { - DEBUG("BT Connected at start"); - openSerial(); - INFO("BT Opened"); - indicate_connected_via_ble(); - } - else - { - indicate_waiting_to_connect(); - } - } else { - if (!usb_wake_state) { - DEBUG("USB disconnected -- shutdown"); - shutdown(0); - } else { - DEBUG("USB connected -- negotiate"); - HAL_GPIO_WritePin(BT_SLEEP_GPIO_Port, BT_SLEEP_Pin, - GPIO_PIN_RESET); - osTimerStart(usbShutdownTimerHandle, 5000); - } - } - - HAL_NVIC_SetPriority(USB_POWER_EXTI_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(USB_POWER_EXTI_IRQn); - - uint32_t power_button_counter{0}; - uint32_t power_button_duration{0}; + osMessagePut(audioInputQueueHandle, mobilinkd::tnc::audio::DEMODULATOR, + osWaitForever); /* Infinite loop */ for (;;) @@ -127,117 +66,16 @@ void startIOEventTask(void const*) if (cmd < FLASH_BASE) // Assumes FLASH_BASE < SRAM_BASE. { switch (cmd) { - case CMD_USB_CDC_CONNECT: - if (!cdc_connected && openCDC()) - { - cdc_connected = true; - // Disable Bluetooth Module - HAL_NVIC_DisableIRQ(EXTI4_IRQn); - HAL_NVIC_DisableIRQ(EXTI9_5_IRQn); - HAL_GPIO_WritePin(BT_SLEEP_GPIO_Port, BT_SLEEP_Pin, - GPIO_PIN_RESET); - INFO("CDC Opened"); - indicate_connected_via_usb(); - osMessagePut(audioInputQueueHandle, - audio::DEMODULATOR, osWaitForever); - } - break; - case CMD_USB_DISCONNECTED: - INFO("VBUS Lost"); - charging_enabled = 0; - if (powerOffViaUSB()) { - shutdown(0); // ***NO RETURN*** - } else { - hpcd_USB_FS.Instance->BCDR = 0; - HAL_PCD_MspDeInit(&hpcd_USB_FS); - HAL_GPIO_WritePin(USB_CE_GPIO_Port, USB_CE_Pin, GPIO_PIN_SET); - if (ioport != getUsbPort()) - { - break; - } - } - [[ fallthrough ]]; // when the CDC part was connected. - case CMD_USB_CDC_DISCONNECT: - if (cdc_connected) { - cdc_connected = false; - osMessagePut(audioInputQueueHandle, audio::IDLE, - osWaitForever); - kiss::getAFSKTestTone().stop(); - closeCDC(); - INFO("CDC Closed"); - - // Enable Bluetooth Module - HAL_GPIO_WritePin(BT_SLEEP_GPIO_Port, BT_SLEEP_Pin, - GPIO_PIN_SET); - bm78_wait_until_ready(); - - HAL_NVIC_EnableIRQ(EXTI4_IRQn); - HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); - - indicate_waiting_to_connect(); - } - break; - case CMD_POWER_BUTTON_DOWN: - INFO("Power Down"); - power_button_counter = osKernelSysTick(); - HAL_GPIO_WritePin(VDD_EN_GPIO_Port, VDD_EN_Pin, GPIO_PIN_SET); - osMessagePut(audioInputQueueHandle, audio::IDLE, - osWaitForever); - break; - case CMD_POWER_BUTTON_UP: - DEBUG("Power Up"); - if (power_button_counter == 0) break; // reset_requested - power_button_duration = osKernelSysTick() - power_button_counter; - DEBUG("Button pressed for %lums", power_button_duration); - shutdown(0); // ***NO RETURN*** - break; - case CMD_BOOT_BUTTON_DOWN: - DEBUG("BOOT Down"); - // If the TNC has USB power, reboot. The boot pin is being - // held so it will boot into the bootloader. This is a bit - // of a hack, since we really should check if the port is a - // standard USB port and not just a charging port. - if (gpio::USB_POWER::get() and ioport == getNullPort()) - { - HAL_NVIC_SystemReset(); - } - break; - case CMD_BOOT_BUTTON_UP: - DEBUG("BOOT Up"); + case CMD_USER_BUTTON_DOWN: + INFO("Button Down"); osMessagePut(audioInputQueueHandle, - audio::AUTO_ADJUST_INPUT_LEVEL, + mobilinkd::tnc::audio::AUTO_ADJUST_INPUT_LEVEL, osWaitForever); - if (ioport != getNullPort()) - { - osMessagePut(audioInputQueueHandle, - audio::DEMODULATOR, osWaitForever); - } - else - { - osMessagePut(audioInputQueueHandle, - audio::IDLE, osWaitForever); - } + osMessagePut(audioInputQueueHandle, + mobilinkd::tnc::audio::DEMODULATOR, osWaitForever); break; - case CMD_BT_CONNECT: - DEBUG("BT Connect"); - if (openSerial()) - { - osMessagePut(audioInputQueueHandle, - audio::DEMODULATOR, osWaitForever); - INFO("BT Opened"); - indicate_connected_via_ble(); - HAL_PCD_EP_SetStall(&hpcd_USB_FS, CDC_CMD_EP); - } - break; - case CMD_BT_DISCONNECT: - INFO("BT Disconnect"); - closeSerial(); - indicate_waiting_to_connect(); - HAL_PCD_EP_ClrStall(&hpcd_USB_FS, CDC_CMD_EP); - osMessagePut(audioInputQueueHandle, audio::IDLE, - osWaitForever); - kiss::getAFSKTestTone().stop(); - INFO("BT Closed"); + case CMD_USER_BUTTON_UP: + DEBUG("Button Up"); break; case CMD_SET_PTT_SIMPLEX: getModulator().set_ptt(&simplexPtt); @@ -245,82 +83,6 @@ void startIOEventTask(void const*) case CMD_SET_PTT_MULTIPLEX: getModulator().set_ptt(&multiplexPtt); break; - case CMD_SHUTDOWN: - INFO("STOP mode"); - shutdown(0); - INFO("RUN mode"); - HAL_GPIO_WritePin(BT_SLEEP_GPIO_Port, BT_SLEEP_Pin, GPIO_PIN_SET); - audio::setAudioOutputLevel(); - audio::setAudioInputLevels(); - bm78_wait_until_ready(); - - HAL_NVIC_SetPriority(EXTI4_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(EXTI4_IRQn); - - HAL_NVIC_SetPriority(EXTI9_5_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); - - HAL_NVIC_SetPriority(SW_BOOT_EXTI_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(SW_BOOT_EXTI_IRQn); - - HAL_NVIC_SetPriority(USB_POWER_EXTI_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(USB_POWER_EXTI_IRQn); - - HAL_NVIC_SetPriority(SW_POWER_EXTI_IRQn, 6, 0); - HAL_NVIC_EnableIRQ(SW_POWER_EXTI_IRQn); - - break; - case CMD_USB_CONNECTED: - INFO("VBUS Detected"); - MX_USB_DEVICE_Init(); - HAL_PCD_MspInit(&hpcd_USB_FS); - hpcd_USB_FS.Instance->BCDR = 0; - HAL_PCDEx_ActivateBCD(&hpcd_USB_FS); - HAL_PCDEx_BCD_VBUSDetect(&hpcd_USB_FS); - break; - case CMD_USB_CHARGE_ENABLE: - INFO("USB charging enabled"); - HAL_GPIO_WritePin(USB_CE_GPIO_Port, USB_CE_Pin, GPIO_PIN_RESET); - charging_enabled = 1; - if (go_back_to_sleep) shutdown(0); - break; - case CMD_USB_DISCOVERY_COMPLETE: - INFO("USB discovery complete"); - osTimerStop(usbShutdownTimerHandle); - USBD_Start(&hUsbDeviceFS); - initCDC(); - break; - case CMD_USB_DISCOVERY_ERROR: - // This happens when powering VBUS from a bench supply. - osTimerStop(usbShutdownTimerHandle); - HAL_PCDEx_DeActivateBCD(&hpcd_USB_FS); - if (HAL_GPIO_ReadPin(USB_POWER_GPIO_Port, USB_POWER_Pin) == GPIO_PIN_SET) - { - INFO("Not a recognized USB charging device"); - INFO("USB charging enabled"); - HAL_GPIO_WritePin(USB_CE_GPIO_Port, USB_CE_Pin, GPIO_PIN_RESET); - charging_enabled = 1; - } - if (go_back_to_sleep) shutdown(0); - break; - case CMD_BT_DEEP_SLEEP: - INFO("BT deep sleep"); - break; - case CMD_BT_ACCESS: - INFO("BT access enabled"); - break; - case CMD_BT_TX: - INFO("BT transmit"); - break; - case CMD_BT_IDLE: - INFO("BT idle"); - break; - case CMD_USB_SUSPEND: - INFO("USB suspend"); - break; - case CMD_USB_RESUME: - INFO("USB resume"); - break; default: WARN("unknown command = %04x", static_cast(cmd)); break; @@ -334,19 +96,16 @@ void startIOEventTask(void const*) switch (frame->source()) { case IoFrame::RF_DATA: - DEBUG("RF frame"); +// DEBUG("RF frame"); if (!ioport->write(frame, 100)) { ERROR("Timed out sending frame"); - // The frame has been passed to the write() call. It owns it now. - // hdlc::release(frame); } break; case IoFrame::SERIAL_DATA: - DEBUG("Serial frame"); +// DEBUG("Serial frame"); if ((frame->type() & 0x0F) == IoFrame::DATA) { - kiss::getAFSKTestTone().stop(); if (osMessagePut(hdlcOutputQueueHandle, reinterpret_cast(frame), osWaitForever) != osOK) @@ -361,7 +120,7 @@ void startIOEventTask(void const*) } break; case IoFrame::DIGI_DATA: - DEBUG("Digi frame"); +// DEBUG("Digi frame"); if (osMessagePut(hdlcOutputQueueHandle, reinterpret_cast(frame), osWaitForever) != osOK) @@ -376,14 +135,6 @@ void startIOEventTask(void const*) } } -void startLedBlinkerTask(void const*) -{ - for (;;) - { - osDelay(4500); - } -} - namespace mobilinkd { namespace tnc { @@ -396,9 +147,6 @@ void print_startup_banner() mobilinkd::tnc::kiss::FIRMWARE_VERSION); INFO("CPU core clock: %luHz", SystemCoreClock); INFO(" Device UID: %08lX %08lX %08lX", uid[0], uid[1], uid[2]); - INFO(" MAC Address: %02X:%02X:%02X:%02X:%02X:%02X", - mac_address[0], mac_address[1], mac_address[2], - mac_address[3], mac_address[4], mac_address[5]) uint8_t* version_ptr = (uint8_t*) 0x1FFF6FF2; diff --git a/TNC/KissHardware.cpp b/TNC/KissHardware.cpp index 8297997..318553f 100644 --- a/TNC/KissHardware.cpp +++ b/TNC/KissHardware.cpp @@ -11,23 +11,12 @@ #include #include -extern I2C_HandleTypeDef hi2c1; -extern RTC_HandleTypeDef hrtc; - -int powerOnViaUSB(void) -{ - return mobilinkd::tnc::kiss::settings().options & KISS_OPTION_VIN_POWER_ON; -} - -int powerOffViaUSB(void) -{ - return mobilinkd::tnc::kiss::settings().options & KISS_OPTION_VIN_POWER_OFF; -} +extern I2C_HandleTypeDef hi2c3; namespace mobilinkd { namespace tnc { namespace kiss { -const char FIRMWARE_VERSION[] = "1.1.6"; -const char HARDWARE_VERSION[] = "Mobilinkd TNC3 2.1.1"; +const char FIRMWARE_VERSION[] = "1.0.1"; +const char HARDWARE_VERSION[] = "Mobilinkd Nucleo32 Breadboard TNC"; Hardware& settings() { @@ -35,48 +24,6 @@ Hardware& settings() return instance; } -const uint8_t* get_rtc_datetime() -{ - static uint8_t buffer[8]; // YYMMDDWWHHMMSS - - RTC_TimeTypeDef sTime; - RTC_DateTypeDef sDate; - - HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BCD); - HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BCD); - - buffer[0] = sDate.Year; - buffer[1] = sDate.Month; - buffer[2] = sDate.Date; - buffer[3] = sDate.WeekDay; - buffer[4] = sTime.Hours; - buffer[5] = sTime.Minutes; - buffer[6] = sTime.Seconds; - buffer[7] = 0; - - return buffer; -} - -// TODO: determine why this is now necessary. -void set_rtc_datetime(const uint8_t* buffer) __attribute__((optimize("-O0"))); - -void set_rtc_datetime(const uint8_t* buffer) -{ - RTC_TimeTypeDef sTime; - RTC_DateTypeDef sDate; - - sDate.Year = buffer[0]; - sDate.Month = buffer[1]; - sDate.Date = buffer[2]; - sDate.WeekDay = buffer[3]; - sTime.Hours = buffer[4]; - sTime.Minutes = buffer[5]; - sTime.Seconds = buffer[6]; - - HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD); - HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD); -} - void Hardware::set_txdelay(uint8_t value) { txdelay = value; update_crc(); @@ -98,9 +45,7 @@ void Hardware::set_duplex(uint8_t value) { update_crc(); } -#if 1 - - void reply8(uint8_t cmd, uint8_t result) { +void reply8(uint8_t cmd, uint8_t result) { uint8_t data[2] { cmd, result }; ioport->write(data, 2, 6, osWaitForever); } @@ -119,16 +64,13 @@ inline void reply(uint8_t cmd, const uint8_t* data, uint16_t len) { } inline void reply_ext(uint8_t ext, uint8_t cmd, const uint8_t* data, uint16_t len) { - auto buffer = (uint8_t*) malloc(len + 2); - if (buffer == nullptr) return; + uint8_t* buffer = static_cast(alloca(len + 2)); buffer[0] = ext; buffer[1] = cmd; for (uint16_t i = 0; i != len and data[i] != 0; i++) buffer[i + 2] = data[i]; ioport->write(buffer, len + 2, 6, osWaitForever); - free(buffer); } -#endif void Hardware::get_alias(uint8_t alias) { uint8_t result[14]; @@ -147,7 +89,6 @@ void Hardware::set_alias(const hdlc::IoFrame* frame) { UNUSED(frame); } - void Hardware::announce_input_settings() { reply16(hardware::GET_INPUT_GAIN, input_gain); @@ -155,12 +96,12 @@ void Hardware::announce_input_settings() } AFSKTestTone& getAFSKTestTone() { - static AFSKTestTone testTone; - return testTone; + static AFSKTestTone testTone; + return testTone; } -void Hardware::handle_request(hdlc::IoFrame* frame) { - +void Hardware::handle_request(hdlc::IoFrame* frame) +{ auto it = frame->begin(); uint8_t command = *it++; @@ -178,14 +119,12 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { switch (command) { -#if 1 case hardware::SAVE: case hardware::SAVE_EEPROM_SETTINGS: update_crc(); store(); reply8(hardware::OK, hardware::SAVE_EEPROM_SETTINGS); break; -#endif case hardware::POLL_INPUT_LEVEL: DEBUG("POLL_INPUT_VOLUME"); reply8(hardware::POLL_INPUT_LEVEL, 0); @@ -195,9 +134,9 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { osWaitForever); break; case hardware::STREAM_INPUT_LEVEL: - DEBUG("STREAM_INPUT_VOLUME"); - osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL, - osWaitForever); + DEBUG("STREAM_INPUT_VOLUME"); + osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL, + osWaitForever); break; case hardware::GET_BATTERY_LEVEL: DEBUG("GET_BATTERY_LEVEL"); @@ -235,17 +174,17 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { osMessagePut(audioInputQueueHandle, audio::DEMODULATOR, osWaitForever); break; - case hardware::SET_OUTPUT_GAIN: + DEBUG("SET_OUTPUT_VOLUME"); output_gain = *it << 8; ++it; output_gain += *it; - DEBUG("SET_OUTPUT_GAIN = %d", output_gain); + DEBUG("SET_OUTPUT_GAIN = %d", int(output_gain)); audio::setAudioOutputLevel(); update_crc(); [[fallthrough]]; case hardware::GET_OUTPUT_GAIN: - DEBUG("GET_OUTPUT_GAIN"); + DEBUG("GET_OUTPUT_VOLUME"); reply16(hardware::GET_OUTPUT_GAIN, output_gain); break; @@ -274,34 +213,23 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { break; case hardware::ADJUST_INPUT_LEVELS: - DEBUG("ADJUST_INPUT_LEVELS"); - osMessagePut(audioInputQueueHandle, audio::AUTO_ADJUST_INPUT_LEVEL, - osWaitForever); - osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL, - osWaitForever); + DEBUG("ADJUST_INPUT_LEVELS"); + osMessagePut(audioInputQueueHandle, audio::AUTO_ADJUST_INPUT_LEVEL, + osWaitForever); + osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL, + osWaitForever); break; -#if 0 case hardware::SET_VERBOSITY: DEBUG("SET_VERBOSITY"); log_level = *it ? Log::Level::debug : Log::Level::warn; Log().setLevel(*it ? Log::Level::debug : Log::Level::warn); + update_crc(); [[fallthrough]]; case hardware::GET_VERBOSITY: DEBUG("GET_VERBOSITY"); reply8(hardware::GET_VERBOSITY, log_level == Log::Level::debug); break; - - case hardware::SET_LOWPASS_FREQ: - lowpass_freq = (*it++ << 8); - lowpass_freq = *it; - // lowpass_freq = antiAliasFilter.setFilterFreq(lowpass_freq); - audio::adcState = audio::UPDATE_SETTINGS; - case hardware::GET_LOWPASS_FREQ: - reply16(hardware::GET_LOWPASS_FREQ, lowpass_freq); - break; -#endif - case hardware::SET_INPUT_GAIN: input_gain = *it << 8; ++it; @@ -317,7 +245,6 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { DEBUG("GET_INPUT_GAIN"); reply16(hardware::GET_INPUT_GAIN, input_gain); break; - case hardware::SET_INPUT_TWIST: DEBUG("SET_INPUT_TWIST"); rx_twist = *it; @@ -337,7 +264,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { if (tx_twist < 0) tx_twist = 0; if (tx_twist > 100) tx_twist = 100; DEBUG("SET_OUTPUT_TWIST: %d", int(tx_twist)); - getModulator().set_twist(uint8_t(tx_twist)); + getModulator().init(*this); update_crc(); [[fallthrough]]; case hardware::GET_OUTPUT_TWIST: @@ -355,22 +282,18 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { DEBUG("GET_TXDELAY"); reply8(hardware::GET_TXDELAY, txdelay); break; - case hardware::GET_PERSIST: DEBUG("GET_PERSIST"); reply8(hardware::GET_PERSIST, ppersist); break; - case hardware::GET_TIMESLOT: DEBUG("GET_TIMESLOT"); reply8(hardware::GET_TIMESLOT, slot); break; - case hardware::GET_TXTAIL: DEBUG("GET_TXTAIL"); reply8(hardware::GET_TXTAIL, txtail); break; - case hardware::GET_DUPLEX: DEBUG("GET_DUPLEX"); reply8(hardware::GET_DUPLEX, duplex); @@ -381,7 +304,6 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply(hardware::GET_FIRMWARE_VERSION, (uint8_t*) FIRMWARE_VERSION, sizeof(FIRMWARE_VERSION) - 1); break; - case hardware::GET_HARDWARE_VERSION: DEBUG("GET_HARDWARE_VERSION"); reply(hardware::GET_HARDWARE_VERSION, (uint8_t*) HARDWARE_VERSION, @@ -391,17 +313,17 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { case hardware::GET_SERIAL_NUMBER: DEBUG("GET_SERIAL_NUMBER"); reply(hardware::GET_SERIAL_NUMBER, (uint8_t*) serial_number_64, - sizeof(serial_number_64) - 1); + sizeof(serial_number_64) - 1); break; case hardware::SET_PTT_CHANNEL: DEBUG("SET_PTT_CHANNEL"); if (*it) { - options &= ~KISS_OPTION_PTT_SIMPLEX; - osMessagePut(ioEventQueueHandle, CMD_SET_PTT_MULTIPLEX, osWaitForever); + options &= ~KISS_OPTION_PTT_SIMPLEX; + osMessagePut(ioEventQueueHandle, CMD_SET_PTT_MULTIPLEX, osWaitForever); } else { - options |= KISS_OPTION_PTT_SIMPLEX; - osMessagePut(ioEventQueueHandle, CMD_SET_PTT_SIMPLEX, osWaitForever); + options |= KISS_OPTION_PTT_SIMPLEX; + osMessagePut(ioEventQueueHandle, CMD_SET_PTT_SIMPLEX, osWaitForever); } update_crc(); break; @@ -441,20 +363,12 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { options & KISS_OPTION_VIN_POWER_ON ? 1 : 0); break; - case hardware::SET_DATETIME: - DEBUG("SET_DATETIME"); - set_rtc_datetime(&*it); - [[fallthrough]]; - case hardware::GET_DATETIME: - DEBUG("GET_DATETIME"); - reply(hardware::GET_DATETIME, get_rtc_datetime(), 7); - break; - case hardware::GET_CAPABILITIES: DEBUG("GET_CAPABILITIES"); reply16(hardware::GET_CAPABILITIES, - hardware::CAP_EEPROM_SAVE|hardware::CAP_BATTERY_LEVEL| - hardware::CAP_ADJUST_INPUT|hardware::CAP_DFU_FIRMWARE); + hardware::CAP_EEPROM_SAVE| + hardware::CAP_ADJUST_INPUT| + hardware::CAP_DFU_FIRMWARE); break; case hardware::GET_ALL_VALUES: @@ -471,9 +385,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply(hardware::GET_HARDWARE_VERSION, (uint8_t*) HARDWARE_VERSION, sizeof(HARDWARE_VERSION) - 1); reply(hardware::GET_SERIAL_NUMBER, (uint8_t*) serial_number_64, - sizeof(serial_number_64) - 1); - reply8(hardware::GET_USB_POWER_OFF, options & KISS_OPTION_VIN_POWER_OFF ? 1 : 0); - reply8(hardware::GET_USB_POWER_ON, options & KISS_OPTION_VIN_POWER_ON ? 1 : 0); + sizeof(serial_number_64) - 1); reply16(hardware::GET_OUTPUT_GAIN, output_gain); reply8(hardware::GET_OUTPUT_TWIST, tx_twist); reply16(hardware::GET_INPUT_GAIN, input_gain); @@ -486,23 +398,18 @@ void Hardware::handle_request(hdlc::IoFrame* frame) { reply8(hardware::GET_PTT_CHANNEL, options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1); reply16(hardware::GET_CAPABILITIES, - hardware::CAP_EEPROM_SAVE|hardware::CAP_BATTERY_LEVEL| - hardware::CAP_ADJUST_INPUT|hardware::CAP_DFU_FIRMWARE); + hardware::CAP_EEPROM_SAVE| + hardware::CAP_ADJUST_INPUT| + hardware::CAP_DFU_FIRMWARE); reply16(hardware::GET_MIN_INPUT_GAIN, 0); // Constants for this FW reply16(hardware::GET_MAX_INPUT_GAIN, 4); // Constants for this FW reply8(hardware::GET_MIN_INPUT_TWIST, -3); // Constants for this FW reply8(hardware::GET_MAX_INPUT_TWIST, 9); // Constants for this FW - reply(hardware::GET_MAC_ADDRESS, mac_address, sizeof(mac_address)); - reply(hardware::GET_DATETIME, get_rtc_datetime(), 7); - if (*error_message) { - reply(hardware::GET_ERROR_MSG, (uint8_t*) error_message, sizeof(error_message)); - } - break; + break; case hardware::EXTENDED_CMD: handle_ext_request(frame); break; - default: ERROR("Unknown hardware request"); } @@ -538,24 +445,20 @@ bool Hardware::load() { INFO("Loading settings from EEPROM"); - auto tmp = std::make_unique(); + Hardware tmp; - if (!tmp) return false; + memset(&tmp, 0, sizeof(Hardware)); - memset(tmp.get(), 0, sizeof(Hardware)); - - if (!I2C_Storage::load(*tmp)) { - ERROR("Load from EEPROM failed."); + if (!I2C_Storage::load(tmp)) { + ERROR("EEPROM read failed"); return false; } - if (tmp->crc_ok()) + if (tmp.crc_ok()) { - memcpy(this, tmp.get(), sizeof(Hardware)); - DEBUG("Load from EEPROM succeeded."); + memcpy(this, &tmp, sizeof(Hardware)); return true; } - ERROR("EEPROM CRC error"); return false; } @@ -565,7 +468,7 @@ bool Hardware::store() const INFO("Saving settings to EEPROM"); if (!I2C_Storage::store(*this)) { - ERROR("Store to EEPROM failed."); + ERROR("EEPROM write failed"); return false; } @@ -576,25 +479,25 @@ bool Hardware::store() const bool I2C_Storage::load(void* ptr, size_t len) { - if (HAL_I2C_Init(&hi2c1) != HAL_OK) CxxErrorHandler(); + if (HAL_I2C_Init(&hi2c3) != HAL_OK) CxxErrorHandler(); DEBUG("Attempting to read %d bytes from EEPROM...", len); uint32_t timeout = 1000; // systicks... milliseconds auto tmp = static_cast(ptr); - auto result = HAL_I2C_Mem_Read(&hi2c1, i2c_address, 0, + auto result = HAL_I2C_Mem_Read(&hi2c3, i2c_address, 0, I2C_MEMADD_SIZE_16BIT, tmp, len, timeout); if (result != HAL_OK) CxxErrorHandler(); - if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) CxxErrorHandler(); + if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler(); return true; } bool I2C_Storage::store(const void* ptr, size_t len) { - if (HAL_I2C_Init(&hi2c1) != HAL_OK) CxxErrorHandler(); + if (HAL_I2C_Init(&hi2c3) != HAL_OK) CxxErrorHandler(); auto tmp = const_cast(static_cast(ptr)); @@ -602,10 +505,10 @@ bool I2C_Storage::store(const void* ptr, size_t len) size_t remaining = len; while (remaining > page_size) { - auto result = HAL_I2C_Mem_Write(&hi2c1, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, page_size, 20); + auto result = HAL_I2C_Mem_Write(&hi2c3, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, page_size, 20); if (result != HAL_OK) { - ERROR("EEPROM write block error = %lu.", hi2c1.ErrorCode); - if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) CxxErrorHandler(); + ERROR("EEPROM write block error = %lu.", hi2c3.ErrorCode); + if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler(); return false; } osDelay(write_time); @@ -614,10 +517,10 @@ bool I2C_Storage::store(const void* ptr, size_t len) } while (remaining) { - auto result = HAL_I2C_Mem_Write(&hi2c1, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, remaining, 20); + auto result = HAL_I2C_Mem_Write(&hi2c3, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, remaining, 20); if (result != HAL_OK) { - ERROR("EEPROM write remainder error = %lu.", hi2c1.ErrorCode); - if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) CxxErrorHandler(); + ERROR("EEPROM write remainder error = %lu.", hi2c3.ErrorCode); + if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler(); return false; } osDelay(write_time); @@ -625,11 +528,10 @@ bool I2C_Storage::store(const void* ptr, size_t len) remaining = 0; } - if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) CxxErrorHandler(); + if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler(); return true; } - }}} // mobilinkd::tnc::kiss diff --git a/TNC/KissHardware.hpp b/TNC/KissHardware.hpp index 527aff9..b4bfb7e 100644 --- a/TNC/KissHardware.hpp +++ b/TNC/KissHardware.hpp @@ -1,10 +1,8 @@ -// Copyright 2015 Mobilinkd LLC +// Copyright 2015-2019 Mobilinkd LLC // All rights reserved. -#ifndef MOBILINKD__TNC__KISS_HARDWARE_HPP_ -#define MOBILINKD__TNC__KISS_HARDWARE_HPP_ +#pragma once -#include "KissHardware.h" #include "Log.h" #include "HdlcFrame.hpp" #include "AFSKTestTone.hpp" @@ -159,6 +157,9 @@ constexpr const uint8_t EXT_GET_BEACONS = 12; ///< Number of beacons supported constexpr const uint8_t EXT_GET_BEACON = 13; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) constexpr const uint8_t EXT_SET_BEACON = 14; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text) +constexpr const uint8_t EXT_GET_MYCALL = 16; ///< MYCALL callsign = 8 characters. right padded with NUL. +constexpr const uint8_t EXT_SET_MYCALL = 17; ///< MYCALL callsign = 8 characters. right padded with NUL. + constexpr const uint8_t MODEM_TYPE_1200 = 1; constexpr const uint8_t MODEM_TYPE_300 = 2; constexpr const uint8_t MODEM_TYPE_9600 = 3; @@ -169,11 +170,11 @@ constexpr const uint8_t MODEM_TYPE_MFSK16 = 6; // Boolean options. #define KISS_OPTION_CONN_TRACK 0x01 #define KISS_OPTION_VERBOSE 0x02 -#define KISS_OPTION_VIN_POWER_ON 0x04 // Power on when plugged into USB +#define KISS_OPTION_VIN_POWER_ON 0x04 // Power on when plugged in to USB #define KISS_OPTION_VIN_POWER_OFF 0x08 // Power off when unplugged from USB #define KISS_OPTION_PTT_SIMPLEX 0x10 // Simplex PTT (the default) -const char TOCALL[] = "APML30"; // Update for every feature change. +const char TOCALL[] = "APML00"; // Update for every feature change. } // hardware @@ -214,19 +215,19 @@ struct Hardware PSK31 }; - uint8_t txdelay; ///< How long in 10mS units to wait for TX to settle before starting data - uint8_t ppersist; ///< Likelihood of taking the channel when its not busy - uint8_t slot; ///< How long in 10mS units to wait between sampling the channel to see if free + uint8_t txdelay; ///< How long in 10mS units to wait for TX to settle before starting data + uint8_t ppersist; ///< Likelihood of taking the channel when its not busy + uint8_t slot; ///< How long in 10mS units to wait between sampling the channel to see if free uint8_t txtail; ///< How long in 10mS units to wait after the data before keying off the transmitter uint8_t duplex; ///< Ignore current channel activity - just key up - uint8_t modem_type; ///< Modem type. - uint16_t output_gain; ///< output volume (0-256). - uint16_t input_gain; ///< input volume (0-256). - int8_t tx_twist; ///< 0 to 100 (50 = even). - int8_t rx_twist; ///< 0, 3, 6 dB - uint8_t log_level; ///< Log level (0 - 4 : debug - severe). + uint8_t modem_type; ///< Modem type. + uint16_t output_gain; ///< output volume (0-256). + uint16_t input_gain; ///< input volume (0-256). + int8_t tx_twist; ///< 0 to 100 (50 = even). + int8_t rx_twist; ///< 0, 3, 6 dB + uint8_t log_level; ///< Log level (0 - 4 : debug - severe). - uint16_t options; ///< boolean options + uint16_t options; ///< boolean options /// Callsign. Pad unused with NUL. uint8_t mycall[CALLSIGN_LEN]; @@ -246,7 +247,7 @@ struct Hardware void update_crc() { checksum = crc(); - INFO("EEPROM checksum = %04x", checksum); + INFO("EEPROM checksum = %04xs", checksum); } bool crc_ok() const { @@ -269,7 +270,7 @@ struct Hardware slot = 10; txtail = 1; duplex = 0; - modem_type = ModemType::AFSK1200; + modem_type = ModemType::FSK9600; output_gain = 63; input_gain = 0; // 0-4 on TNC3 tx_twist = 50; @@ -290,6 +291,7 @@ struct Hardware } void debug() { +#ifdef KISS_LOGGING DEBUG("Hardware Settings (size=%d):", sizeof(Hardware)); DEBUG("TX Delay: %d", (int)txdelay); DEBUG("P* Persistence: %d", (int)ppersist); @@ -322,7 +324,8 @@ struct Hardware DEBUG(" text: %s", (char*)b.text); DEBUG(" frequency (secs): %d", (int)b.seconds); } - DEBUG("Checksum: %04x", checksum); + DEBUG("Checksum: %04xs", checksum); +#endif } bool load(); @@ -347,7 +350,6 @@ struct Hardware extern Hardware& settings(); - struct I2C_Storage { constexpr static const uint16_t i2c_address{EEPROM_ADDRESS}; @@ -375,5 +377,3 @@ void reply8(uint8_t cmd, uint8_t result) __attribute__((noinline)); void reply16(uint8_t cmd, uint16_t result) __attribute__((noinline)); }}} // mobilinkd::tnc::kiss - -#endif // INMOBILINKD__TNC__KISS_HARDWARE_HPP_C_KISSHARDWARE_HPP_ diff --git a/TNC/Log.cpp b/TNC/Log.cpp index a913a31..11436a8 100644 --- a/TNC/Log.cpp +++ b/TNC/Log.cpp @@ -2,18 +2,25 @@ // All rights reserved. #include -#include -#include +#include "PortInterface.hpp" void log_(int level, const char* fmt, ...) { - if (level < mobilinkd::tnc::log().level_) return; + if (!mobilinkd::tnc::ioport) return; + va_list args; va_start(args, fmt); - vprintf(fmt, args); + char* buffer = 0; + int len = vasiprintf(&buffer, fmt, args); va_end(args); - printf("\r\n"); + + if (len >= 0) { + mobilinkd::tnc::ioport->write((uint8_t*)buffer, len, 7, 10); + free(buffer); + } else { + mobilinkd::tnc::ioport->write((uint8_t*) "Allocation Error\r\n", 18, 7, 10); + } } namespace mobilinkd { namespace tnc { @@ -21,13 +28,13 @@ namespace mobilinkd { namespace tnc { #ifdef KISS_LOGGING Log& log(void) { - static Log log(Log::Level::debug); + static Log log(Log::Level::info); return log; } #endif -#if 0 +#if 1 void Log::log(Level level, const char* fmt, ...) { if (level < level_) return; diff --git a/TNC/Log.h b/TNC/Log.h index 4f92b7b..7fc1a38 100644 --- a/TNC/Log.h +++ b/TNC/Log.h @@ -20,19 +20,12 @@ extern "C" { void log_(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); -#ifndef KISS_LOG_LEVEL -#define KISS_LOG_LEVEL 1 -#endif - #ifdef KISS_LOGGING - -#define LOG(level, ...) if(level >= KISS_LOG_LEVEL) log_(level, __VA_ARGS__); - -#define DEBUG(...) LOG(0, __VA_ARGS__) -#define INFO(...) LOG(1, __VA_ARGS__) -#define WARN(...) LOG(2, __VA_ARGS__) -#define ERROR(...) LOG(3, __VA_ARGS__) -#define SEVERE(...) LOG(4, __VA_ARGS__) +#define DEBUG(...) log_(0, __VA_ARGS__) +#define INFO(...) log_(1, __VA_ARGS__) +#define WARN(...) log_(2, __VA_ARGS__) +#define ERROR(...) log_(3, __VA_ARGS__) +#define SEVERE(...) log_(4, __VA_ARGS__) #else #define DEBUG(...) #define INFO(...) @@ -54,7 +47,7 @@ struct Log { Level level_; Log() - : level_(Level::debug) + : level_(Level::info) {} Log(Level level) diff --git a/TNC/Modulator.hpp b/TNC/Modulator.hpp new file mode 100644 index 0000000..9d3982c --- /dev/null +++ b/TNC/Modulator.hpp @@ -0,0 +1,93 @@ +// Copyright 2020 Rob Riggs +// All rights reserved. + +#pragma once + +#include "PTT.hpp" +#include "KissHardware.hpp" + +#include "cmsis_os.h" + +#include + +extern osMessageQId hdlcOutputQueueHandle; +extern osMessageQId dacOutputQueueHandle; +extern TIM_HandleTypeDef htim7; +extern DAC_HandleTypeDef hdac1; + +namespace mobilinkd { namespace tnc { + +/** + * The modulator has three distinct interfaces. The configuration interface + * which is used to initialize the modulator, the bit sending interface used + * by the HDLC encoder to transmit the bits, and the modulation interface + * used by the DAC DMA engine to get the analog values output by the modem. + */ +struct Modulator +{ + virtual ~Modulator() {} + + /** + * Implement all functionality required to configure the hardware and + * the modulator. For example, configuring the timer used by the DAC + * should be done here. + */ + virtual void init(const kiss::Hardware& hw) = 0; + + /** + * Implement all functionality required to deactivate the hardware and + * the modulator. For example, disabling the timer used by the DAC + * should be done here. + */ + virtual void deinit() = 0; + + virtual void set_gain(uint16_t level) = 0; + + /** + * Set the PTT controller used by the modulator. It is only legal + * to call this when the modulator is stopped. + * + * @param ptt + */ + virtual void set_ptt(PTT* ptt) = 0; + + /** + * Send a single bit. + * + * @param bit + */ + virtual void send(bool bit) = 0; + + /// The next three functions are called by the DAC DMA interrupt handler. + + /** + * Fill the first half of the DAC DMA buffer. + * + * @warning This function is called in an interrupt context. + * + * @param bit + */ + virtual void fill_first(bool bit) = 0; + + /** + * Fill the second half of the DAC DMA buffer. + * + * @warning This function is called in an interrupt context. + * + * @param bit + */ + virtual void fill_last(bool bit) = 0; + + /** + * The DAC bit buffer is empty. There are no more bits to process. + * + * @warning This function is called in an interrupt context. + */ + virtual void empty() = 0; + + virtual void abort() = 0; + + virtual float bits_per_ms() const = 0; +}; + +}} // mobilinkd::tnc diff --git a/TNC/ModulatorTask.cpp b/TNC/ModulatorTask.cpp index 286a52f..73d7136 100644 --- a/TNC/ModulatorTask.cpp +++ b/TNC/ModulatorTask.cpp @@ -2,13 +2,15 @@ // All rights reserved. #include "ModulatorTask.hpp" +#include "Fsk9600Modulator.hpp" +#include "AFSKModulator.hpp" #include "KissHardware.hpp" #include "main.h" mobilinkd::tnc::SimplexPTT simplexPtt; mobilinkd::tnc::MultiplexPTT multiplexPtt; -mobilinkd::tnc::AFSKModulator* modulator; +mobilinkd::tnc::Modulator* modulator; mobilinkd::tnc::hdlc::Encoder* encoder; // DMA Conversion half complete. @@ -34,9 +36,22 @@ extern "C" void HAL_DAC_DMAUnderrunCallbackCh1(DAC_HandleTypeDef*) { modulator->abort(); } -mobilinkd::tnc::AFSKModulator& getModulator() { - static mobilinkd::tnc::AFSKModulator instance(dacOutputQueueHandle, &simplexPtt); - return instance; +mobilinkd::tnc::Modulator& getModulator() +{ + using namespace mobilinkd::tnc; + + static AFSKModulator afsk1200modulator(dacOutputQueueHandle, &simplexPtt); + static Fsk9600Modulator fsk9600modulator(dacOutputQueueHandle, &simplexPtt); + + switch (kiss::settings().modem_type) + { + case kiss::Hardware::ModemType::FSK9600: + return fsk9600modulator; + case kiss::Hardware::ModemType::AFSK1200: + return afsk1200modulator; + default: + _Error_Handler(__FILE__, __LINE__); + } } mobilinkd::tnc::hdlc::Encoder& getEncoder() { @@ -61,9 +76,9 @@ void updatePtt() using namespace mobilinkd::tnc::kiss; if (settings().options & KISS_OPTION_PTT_SIMPLEX) - modulator->set_ptt(&simplexPtt); + getModulator().set_ptt(&simplexPtt); else - modulator->set_ptt(&multiplexPtt); + getModulator().set_ptt(&multiplexPtt); } void startModulatorTask(void const*) { @@ -78,7 +93,7 @@ void startModulatorTask(void const*) { updatePtt(); - modulator->set_twist(settings().tx_twist); + getModulator().init(settings()); encoder->tx_delay(settings().txdelay); encoder->p_persist(settings().ppersist); diff --git a/TNC/ModulatorTask.hpp b/TNC/ModulatorTask.hpp index 9f9ccfe..6f4693f 100644 --- a/TNC/ModulatorTask.hpp +++ b/TNC/ModulatorTask.hpp @@ -6,7 +6,7 @@ #define MOBILINKD__MODULATOR_TASK_HPP_ #include "HDLCEncoder.hpp" -#include "AFSKModulator.hpp" +#include "Modulator.hpp" #include "PTT.hpp" #include "cmsis_os.h" @@ -17,10 +17,7 @@ extern "C" { extern mobilinkd::tnc::SimplexPTT simplexPtt; extern mobilinkd::tnc::MultiplexPTT multiplexPtt; -extern mobilinkd::tnc::AFSKModulator* modulator; -extern mobilinkd::tnc::hdlc::Encoder* encoder; - -mobilinkd::tnc::AFSKModulator& getModulator(); +mobilinkd::tnc::Modulator& getModulator(); mobilinkd::tnc::hdlc::Encoder& getEncoder(); void startModulatorTask(void const * argument);