kopia lustrzana https://github.com/mobilinkd/tnc3-firmware
Start of 9600 baud support.
rodzic
9f523247bf
commit
835ae8e265
|
@ -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<uint16_t>(256, v);
|
||||
v = std::min<uint16_t>(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
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// 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
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// 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<q15_t* >(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
|
|
@ -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<ADC_BUFFER_SIZE, FILTER_TAP_NUM> 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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "Demodulator.hpp"
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
audio_filter_t demod_filter;
|
||||
|
||||
}} // mobilinkd::tnc
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "HdlcFrame.hpp"
|
||||
#include "FirFilter.hpp"
|
||||
#include "AudioInput.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <arm_math.h>
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
constexpr size_t FILTER_TAP_NUM = 132;
|
||||
|
||||
using demod_filter_t = std::function<q15_t*(q15_t*, size_t)>;
|
||||
using demodulator_t = std::function<hdlc::IoFrame*(q15_t*)>;
|
||||
using audio_filter_t = Q15FirFilter<audio::ADC_BUFFER_SIZE, FILTER_TAP_NUM>;
|
||||
|
||||
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
|
|
@ -31,9 +31,11 @@ struct PLLResult {
|
|||
//
|
||||
const std::array<float, 5> 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<float, 5> 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<float, 5> lock_a = {
|
|||
//
|
||||
const std::array<float, 7> 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
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// 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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// 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
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// 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<float> 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<q15_t* >(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
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "Fsk9600Modulator.hpp"
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
#if 0
|
||||
const std::array<int16_t, BIT_LEN> 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
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modulator.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
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<int16_t, BIT_LEN> 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<uint16_t, DAC_BUFFER_LEN> 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<uint16_t>(256, level);
|
||||
v = std::min<uint16_t>(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<uint32_t*>(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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2017-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// 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<unsigned int>(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<uint32_t>(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<uint32_t>(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;
|
||||
|
||||
|
|
|
@ -11,23 +11,12 @@
|
|||
#include <memory>
|
||||
#include <cstdio>
|
||||
|
||||
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<uint8_t*>(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>();
|
||||
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<uint8_t*>(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<uint8_t*>(static_cast<const uint8_t*>(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
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// 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_
|
||||
|
|
21
TNC/Log.cpp
21
TNC/Log.cpp
|
@ -2,18 +2,25 @@
|
|||
// All rights reserved.
|
||||
|
||||
#include <Log.h>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#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;
|
||||
|
|
19
TNC/Log.h
19
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)
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2020 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PTT.hpp"
|
||||
#include "KissHardware.hpp"
|
||||
|
||||
#include "cmsis_os.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Ładowanie…
Reference in New Issue