Fix AFSK modem after FSK changes. Revert changes to DigitalPLL. Remove intrusive diagnostics in AudioInput, along with dead code. Clean up FIR filter code. Add initial passall support. Disable LSCO when transmitting in debug mode. Set fixed mid-range DAC output in FSK modulator when not transmitting to eliminate DC offset. New way to compute IDLE bytes for TX Delay. Clean up HDLC decoder, improve HDLC diagnostics, add Passall support. Add new extended response types for modem support. Update API version to 0x0201. Update firmware version to 2.0.0b1.

kf7r_9600_experimental
Rob Riggs 2020-02-22 19:35:11 -06:00
rodzic 631953cc30
commit 79f145ccd9
26 zmienionych plików z 532 dodań i 777 usunięć

Wyświetl plik

@ -1,4 +1,4 @@
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
// Copyright 2015-2020 Mobilinkd LLC <rob@mobilinkd.com>
// All rights reserved.
#pragma once
@ -75,7 +75,7 @@ struct AFSKModulator : Modulator
PTT* ptt_;
uint8_t twist_{50};
uint16_t volume_{4096};
uint16_t buffer_[DAC_BUFFER_LEN];
std::array<uint16_t, DAC_BUFFER_LEN> buffer_;
AFSKModulator(osMessageQId queue, PTT* ptt)
: dacOutputQueueHandle_(queue), ptt_(ptt)
@ -88,13 +88,28 @@ struct AFSKModulator : Modulator
{
set_twist(hw.tx_twist);
// Configure 80MHz clock for 26.4ksps.
htim7.Init.Period = 3029;
// Configure 48MHz clock for 26.4ksps.
htim7.Init.Period = 1817;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
{
ERROR("htim7 init failed");
CxxErrorHandler();
}
DAC_ChannelConfTypeDef sConfig;
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
CxxErrorHandler();
}
if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048) != HAL_OK) CxxErrorHandler();
if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) CxxErrorHandler();
}
void deinit() override
@ -120,19 +135,21 @@ struct AFSKModulator : Modulator
void set_twist(uint8_t twist) {twist_ = twist;}
void send(bool bit) override
{
void send(bool bit) override
{
switch (running_) {
case -1:
ptt_->on();
#ifdef KISS_LOGGING
HAL_RCCEx_DisableLSCO();
#endif
fill_first(bit);
running_ = 0;
break;
case 0:
fill_last(bit);
running_ = 1;
ptt_->on();
HAL_TIM_Base_Start(&htim7);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) buffer_, DAC_BUFFER_LEN, DAC_ALIGN_12B_R);
start_conversion();
break;
case 1:
osMessagePut(dacOutputQueueHandle_, bit, osWaitForever);
@ -171,12 +188,12 @@ struct AFSKModulator : Modulator
void fill_first(bool bit) override
{
fill(buffer_, bit);
fill(buffer_.data(), bit);
}
void fill_last(bool bit) override
{
fill(buffer_ + BIT_LEN, bit);
fill(buffer_.data() + BIT_LEN, bit);
}
void empty() override
@ -187,10 +204,12 @@ struct AFSKModulator : Modulator
break;
case 0:
running_ = -1;
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_TIM_Base_Stop(&htim7);
stop_conversion();
ptt_->off();
pos_ = 0;
#ifdef KISS_LOGGING
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
break;
case -1:
break;
@ -200,10 +219,12 @@ struct AFSKModulator : Modulator
void abort() override
{
running_ = -1;
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_TIM_Base_Stop(&htim7);
stop_conversion();
ptt_->off();
pos_ = 0;
#ifdef KISS_LOGGING
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
// Drain the queue.
while (osMessageGet(dacOutputQueueHandle_, 0).status == osEventMessage);
@ -213,6 +234,33 @@ struct AFSKModulator : Modulator
{
return 1.2f;
}
private:
/**
* Configure the DAC for timer-based DMA conversion, start the timer,
* and start DMA to DAC.
*/
void start_conversion()
{
DAC_ChannelConfTypeDef sConfig;
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_T7_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
CxxErrorHandler();
}
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);
}
};
}} // mobilinkd::tnc

Wyświetl plik

@ -3,6 +3,7 @@
#include "AFSKTestTone.hpp"
#include "ModulatorTask.hpp"
#include "Modulator.hpp"
void startAfskToneTask(void const* arg)
{

Wyświetl plik

@ -15,6 +15,68 @@ afsk1200::Demodulator Afsk1200Demodulator::demod1(26400, Afsk1200Demodulator::fi
afsk1200::Demodulator Afsk1200Demodulator::demod2(26400, Afsk1200Demodulator::filter_2);
afsk1200::Demodulator Afsk1200Demodulator::demod3(26400, Afsk1200Demodulator::filter_3);
hdlc::IoFrame* Afsk1200Demodulator::operator()(const q15_t* samples)
{
hdlc::IoFrame* result = nullptr;
q15_t* filtered = demod_filter.filter(const_cast<q15_t* >(samples));
++counter;
#if 1
auto frame1 = demod1(filtered, ADC_BLOCK_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, ADC_BLOCK_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, ADC_BLOCK_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;
}
/*
* Return twist as a the difference in dB between mark and space. The
* expected values are about 0dB for discriminator output and about 5.5dB

Wyświetl plik

@ -35,14 +35,15 @@ struct Afsk1200Demodulator : IDemodulator
*/
static constexpr size_t FILTER_TAP_NUM = 132;
static constexpr uint32_t ADC_BLOCK_SIZE = afsk1200::ADC_BUFFER_SIZE;
static constexpr uint32_t SAMPLE_RATE = 26400;
static_assert(audio::ADC_BUFFER_SIZE >= ADC_BLOCK_SIZE);
static constexpr uint32_t SAMPLE_RATE = 26400;
static constexpr uint16_t VREF = 16383;
using audio_filter_t = Q15FirFilter<ADC_BLOCK_SIZE, FILTER_TAP_NUM>;
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;
@ -76,6 +77,7 @@ struct Afsk1200Demodulator : IDemodulator
counter = 0;
demod_filter.init(bpf_coeffs);
passall(kiss::settings().options & KISS_OPTION_PASSALL);
hadc1.Init.OversamplingMode = ENABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
@ -103,67 +105,7 @@ struct Afsk1200Demodulator : IDemodulator
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, ADC_BLOCK_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, ADC_BLOCK_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, ADC_BLOCK_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;
}
hdlc::IoFrame* operator()(const q15_t* samples) override;
float readTwist() override;
@ -176,6 +118,13 @@ struct Afsk1200Demodulator : IDemodulator
{
return ADC_BLOCK_SIZE;
}
void passall(bool enable) override
{
demod1.hdlc_decoder_.passall = enable;
demod2.hdlc_decoder_.passall = enable;
demod3.hdlc_decoder_.passall = enable;
}
};
}} // mobilinkd::tnc

Wyświetl plik

@ -1,4 +1,4 @@
// Copyright 2017-2019 Rob Riggs <rob@mobilinkd.com>
// Copyright 2017-2020 Rob Riggs <rob@mobilinkd.com>
// All rights reserved.
#include "AfskDemodulator.hpp"

Wyświetl plik

@ -48,11 +48,11 @@ struct Demodulator {
emphasis_filter_type& audio_filter_;
libafsk::FixedDelayLine<40> delay_line_;
DPLL pll_;
Q15FirFilter<audio::ADC_BUFFER_SIZE, LPF_FILTER_LEN> lpf_filter_;
Q15FirFilter<ADC_BUFFER_SIZE, LPF_FILTER_LEN> lpf_filter_;
libafsk::NRZI nrzi_;
hdlc::NewDecoder hdlc_decoder_;
bool locked_;
q15_t buffer_[audio::ADC_BUFFER_SIZE];
q15_t buffer_[ADC_BUFFER_SIZE];
Demodulator(size_t sample_rate, emphasis_filter_type& c)
: sample_rate_(sample_rate)

Wyświetl plik

@ -176,8 +176,6 @@ void demodulatorTask() {
continue;
}
gpio::BAT_DIVIDER::off();
auto block = (adc_pool_type::chunk_type*) evt.value.p;
auto samples = (int16_t*) block->buffer;
@ -201,7 +199,6 @@ void demodulatorTask() {
dcd_off();
}
}
gpio::BAT_DIVIDER::on();
}
demodulator->stop();
@ -412,152 +409,6 @@ void pollInputTwist()
DEBUG("exit pollInputTwist");
}
#if 0
void streamAverageInputTwist()
{
DEBUG("enter streamAverageInputTwist");
constexpr uint32_t channel = AUDIO_IN;
startADC(channel);
uint32_t acount = 0;
float g700 = 0.0f;
float g1200 = 0.0f;
float g1700 = 0.0f;
float g2200 = 0.0f;
float g2700 = 0.0f;
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf700(700.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf1200(1200.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf1700(1700.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf2200(2200.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf2700(2700.0);
while (true) {
osEvent peek = osMessagePeek(audioInputQueueHandle, 0);
if (peek.status == osEventMessage) break;
acount++;
uint32_t count = 0;
while (count < TWIST_SAMPLE_SIZE) {
osEvent evt = osMessageGet(adcInputQueueHandle, osWaitForever);
if (evt.status != osEventMessage) continue;
count += ADC_BUFFER_SIZE;
auto block = (adc_pool_type::chunk_type*) evt.value.v;
uint16_t* data = (uint16_t*) block->buffer;
gf700(data, ADC_BUFFER_SIZE);
gf1200(data, ADC_BUFFER_SIZE);
gf1700(data, ADC_BUFFER_SIZE);
gf2200(data, ADC_BUFFER_SIZE);
gf2700(data, ADC_BUFFER_SIZE);
adcPool.deallocate(block);
}
g700 += 10.0 * log10(gf700);
g1200 += 10.0 * log10(gf1200);
g1700 += 10.0 * log10(gf1700);
g2200 += 10.0 * log10(gf2200);
g2700 += 10.0 * log10(gf2700);
char* buffer = 0;
// @TODO: Make re-entrant printf work (or convert to fixed-point).
int len = asiprintf_r(
&buffer,
"_%f, %f, %f, %f, %f\r\n",
g700 / acount,
g1200 / acount,
g1700 / acount,
g2200 / acount,
g2700 / acount);
if (len > 0) {
buffer[0] = kiss::hardware::POLL_INPUT_TWIST;
ioport->write((uint8_t*)buffer, len - 1, 6, 10);
free(buffer);
}
gf700.reset();
gf1200.reset();
gf1700.reset();
gf2200.reset();
gf2700.reset();
}
stopADC();
DEBUG("exit streamAverageInputTwist");
}
void streamInstantInputTwist()
{
DEBUG("enter streamInstantInputTwist");
constexpr uint32_t channel = AUDIO_IN;
startADC(channel);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf700(700.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf1200(1200.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf1700(1700.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf2200(2200.0);
GoertzelFilter<TWIST_SAMPLE_SIZE, 26400> gf2700(2700.0);
while (true) {
osEvent peek = osMessagePeek(audioInputQueueHandle, 0);
if (peek.status == osEventMessage) break;
uint32_t count = 0;
while (count < TWIST_SAMPLE_SIZE) {
osEvent evt = osMessageGet(adcInputQueueHandle, osWaitForever);
if (evt.status != osEventMessage) continue;
count += ADC_BUFFER_SIZE;
auto block = (adc_pool_type::chunk_type*) evt.value.v;
uint16_t* data = (uint16_t*) block->buffer;
gf700(data, ADC_BUFFER_SIZE);
gf1200(data, ADC_BUFFER_SIZE);
gf1700(data, ADC_BUFFER_SIZE);
gf2200(data, ADC_BUFFER_SIZE);
gf2700(data, ADC_BUFFER_SIZE);
adcPool.deallocate(block);
}
char* buffer = 0;
// @TODO: Make re-entrant printf work (or convert to fixed-point).
int len = asiprintf_r(
&buffer,
"_%f, %f, %f, %f, %f\r\n",
10.0 * log10(gf700),
10.0 * log10(gf1200),
10.0 * log10(gf1700),
10.0 * log10(gf2200),
10.0 * log10(gf2700));
if (len > 0) {
buffer[0] = kiss::hardware::POLL_INPUT_TWIST;
ioport->write((uint8_t*)buffer, len - 1, 6, 10);
free(buffer);
}
gf700.reset();
gf1200.reset();
gf1700.reset();
gf2200.reset();
gf2700.reset();
}
stopADC();
DEBUG("exit streamInstantInputTwist");
}
#endif
void streamAmplifiedInputLevels() {
DEBUG("enter streamAmplifiedInputLevels");
streamLevels(AUDIO_IN, kiss::hardware::POLL_INPUT_LEVEL);

Wyświetl plik

@ -5,6 +5,7 @@
#include "AudioInput.hpp"
#include "KissHardware.hpp"
#include "ModulatorTask.hpp"
#include "Modulator.hpp"
#include "GPIO.hpp"
#include "LEDIndicator.h"
#include "ModulatorTask.hpp"

Wyświetl plik

@ -3,6 +3,7 @@
#include "DCD.h"
#include "LEDIndicator.h"
#include "GPIO.hpp"
bool& dcd_status()
{

Wyświetl plik

@ -41,4 +41,13 @@ void IDemodulator::startADC(uint32_t period, uint32_t block_size)
}
}
void IDemodulator::stopADC()
{
if (HAL_ADC_Stop_DMA(&hadc1) != HAL_OK)
CxxErrorHandler();
if (HAL_TIM_Base_Stop(&htim6) != HAL_OK)
CxxErrorHandler();
}
}} // mobilinkd::tnc

Wyświetl plik

@ -28,16 +28,21 @@ struct IDemodulator
virtual bool locked() const = 0;
virtual size_t size() const = 0;
/**
* Tell the demodulator to return all "passable" HDLC frames. These
* are frames which consist of an even multiple of eight bits and are
* up to 330 bytes, but which do not have a valid checksum.
*
* @param enable is true when enabled and false when disabled. The
* default state is disabled.
*/
virtual void passall(bool enable) = 0;
virtual ~IDemodulator() {}
static void startADC(uint32_t period, uint32_t block_size);
static void stopADC() {
if (HAL_ADC_Stop_DMA(&hadc1) != HAL_OK)
CxxErrorHandler();
if (HAL_TIM_Base_Stop(&htim6) != HAL_OK)
CxxErrorHandler();
}
static void stopADC();
};
}} // mobilinkd::tnc

Wyświetl plik

@ -1,5 +1,7 @@
#ifndef MOBILINKD__DIGITAL_PLL_H_
#define MOBILINKD__DIGITAL_PLL_H_
// Copyright 2015-2020 Mobilinkd LLC <rob@mobilinkd.com>
// All rights reserved.
#pragma once
#include "Hysteresis.hpp"
#include "IirFilter.hpp"
@ -43,8 +45,7 @@ constexpr std::array<float, 5> lock_a = {
//
constexpr 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
0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0
3.196252e-02,1.204223e-01,2.176819e-01,2.598666e-01,2.176819e-01,1.204223e-01,3.196252e-02
};
} // pll
@ -73,8 +74,8 @@ struct BaseDigitalPLL
BaseDigitalPLL(float_type sample_rate, float_type symbol_rate)
: sample_rate_(sample_rate), symbol_rate_(symbol_rate)
, sps_(sample_rate / symbol_rate)
, limit_(sps_ / float_type(2.0))
, lock_(sps_ * float_type(0.03), sps_ * float_type(0.15), 1, 0)
, limit_(sps_ / 2.0)
, lock_(sps_ * 0.03, sps_ * 0.15, 1, 0)
, last_(false), count_(0), sample_(false)
, jitter_(0.0), bits_(1)
{}
@ -128,6 +129,3 @@ typedef BaseDigitalPLL<double> DigitalPLL;
typedef BaseDigitalPLL<float> FastDigitalPLL;
}} // mobilinkd::tnc
#endif // MOBILINKD__DIGITAL_PLL_H_

Wyświetl plik

@ -1,8 +1,7 @@
// Copyright 2015 Rob Riggs <rob@mobilinkd.com>
// Copyright 2015-2020 Rob Riggs <rob@mobilinkd.com>
// All rights reserved.
#ifndef MOBILINKD__TNC__FILTER_COEFFICIENTS_HPP_
#define MOBILINKD__TNC__FILTER_COEFFICIENTS_HPP_
#pragma once
#include "IirFilter.hpp"
#include "FirFilter.hpp"
@ -303,5 +302,3 @@ extern const TFirCoefficients<9>* AfskFilters[19];
}}} // mobilinkd::tnc::filter
#endif // MOBILINKD__TNC__FILTER_COEFFICIENTS_HPP_

Wyświetl plik

@ -1,8 +1,7 @@
// Copyright 2015-2019 Rob Riggs <rob@mobilinkd.com>
// Copyright 2015-2020 Rob Riggs <rob@mobilinkd.com>
// All rights reserved.
#ifndef MOBILINKD__TNC__FIR_FILTER_H_
#define MOBILINKD__TNC__FIR_FILTER_H_
#pragma once
#include <AudioLevel.hpp>
@ -114,25 +113,16 @@ struct Q15FirFilter {
{
vgnd_ = audio::virtual_ground;
filter_taps = taps;
arm_fir_init_q15(&instance, FILTER_SIZE, const_cast<q15_t*>(filter_taps), // WTF ARM?!?
arm_fir_init_q15(&instance, FILTER_SIZE,
const_cast<q15_t*>(filter_taps),
filter_state, BLOCK_SIZE);
}
// ADC input
q15_t* operator()(q15_t* input) // __attribute__((section(".bss2")))
q15_t* filter(const q15_t* input)
{
arm_fir_fast_q15(&instance, input, filter_output, BLOCK_SIZE);
return filter_output;
}
// LPF input
q15_t* filter(q15_t* input) // __attribute__((section(".bss2")))
{
arm_fir_fast_q15(&instance, input, filter_output, BLOCK_SIZE);
arm_fir_fast_q15(&instance, const_cast<q15_t*>(input), filter_output, BLOCK_SIZE);
return filter_output;
}
};
}} // mobilinkd::tnc
#endif // MOBILINKD__TNC__FIR_FILTER_H_

Wyświetl plik

@ -11,7 +11,7 @@ hdlc::IoFrame* Fsk9600Demodulator::operator()(const q15_t* samples)
{
hdlc::IoFrame* result = nullptr;
auto filtered = demod_filter(const_cast<q15_t* >(samples));
auto filtered = demod_filter.filter(const_cast<q15_t* >(samples));
for (size_t i = 0; i != ADC_BLOCK_SIZE; ++i)
{

Wyświetl plik

@ -55,17 +55,13 @@ struct Fsk9600Demodulator : IDemodulator
auto const& bpf_coeffs = bpf_bank[kiss::settings().rx_twist + 3];
const q15_t* bpf = bpf_coeffs.data();
demod_filter.init(bpf);
passall(kiss::settings().options & KISS_OPTION_PASSALL);
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
CxxErrorHandler();
}
//
// if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
// {
// CxxErrorHandler();
// }
ADC_ChannelConfTypeDef sConfig;
@ -102,6 +98,11 @@ struct Fsk9600Demodulator : IDemodulator
{
return ADC_BLOCK_SIZE;
}
void passall(bool enable) override
{
hdlc_decoder_.passall = enable;
}
};
}} // mobilinkd::tnc

Wyświetl plik

@ -27,6 +27,7 @@ struct Fsk9600Modulator : Modulator
{
static constexpr int8_t DAC_BUFFER_LEN = 40;
static constexpr int8_t BIT_LEN = DAC_BUFFER_LEN / 2;
static constexpr uint16_t VREF = 4095;
static constexpr std::array<int16_t, BIT_LEN> cos_table{
2047, 2020, 1937, 1801, 1616, 1387, 1120, 822, 502, 169,
@ -54,6 +55,8 @@ struct Fsk9600Modulator : Modulator
{
for (auto& x : buffer_) x = 2048;
(void) hw; // unused
state = State::STOPPED;
level = Level::HIGH;
@ -64,12 +67,27 @@ struct Fsk9600Modulator : Modulator
ERROR("htim7 init failed");
CxxErrorHandler();
}
DAC_ChannelConfTypeDef sConfig;
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
CxxErrorHandler();
}
if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048) != HAL_OK) CxxErrorHandler();
if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK) CxxErrorHandler();
}
void deinit() override
{
state = State::STOPPED;
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_DAC_Stop(&hdac1, DAC_CHANNEL_1);
HAL_TIM_Base_Stop(&htim7);
ptt_->off();
}
@ -100,18 +118,18 @@ struct Fsk9600Modulator : Modulator
{
case State::STOPPING:
case State::STOPPED:
ptt_->on();
#ifdef KISS_LOGGING
HAL_RCCEx_DisableLSCO();
#endif
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);
start_conversion();
break;
case State::RUNNING:
osMessagePut(dacOutputQueueHandle_, scrambled, osWaitForever);
@ -142,24 +160,27 @@ struct Fsk9600Modulator : Modulator
break;
case State::STOPPING:
state = State::STOPPED;
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_TIM_Base_Stop(&htim7);
stop_conversion();
ptt_->off();
level = Level::HIGH;
#ifdef KISS_LOGGING
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
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);
stop_conversion();
ptt_->off();
level = Level::HIGH;
#ifdef KISS_LOGGING
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
// Drain the queue.
while (osMessageGet(dacOutputQueueHandle_, 0).status == osEventMessage);
}
@ -171,6 +192,31 @@ struct Fsk9600Modulator : Modulator
private:
/**
* Configure the DAC for timer-based DMA conversion, start the timer,
* and start DMA to DAC.
*/
void start_conversion()
{
DAC_ChannelConfTypeDef sConfig;
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_T7_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
CxxErrorHandler();
}
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);
}
uint16_t adjust_level(int32_t sample) const
{
sample *= volume_;

Wyświetl plik

@ -1,10 +1,10 @@
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
// Copyright 2015-2020 Mobilinkd LLC <rob@mobilinkd.com>
// All rights reserved.
#ifndef INC_HDLCENCODER_HPP_
#define INC_HDLCENCODER_HPP_
#pragma once
#include "Modulator.hpp"
#include "ModulatorTask.hpp"
#include "HdlcFrame.hpp"
#include "NRZI.hpp"
#include "PTT.hpp"
@ -51,12 +51,12 @@ struct Encoder {
volatile bool running_;
bool send_delay_; // Avoid sending the preamble for back-to-back frames.
Encoder(osMessageQId input, Modulator* output)
Encoder(osMessageQId input)
: 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)
, ones_(0), nrzi_(), crc_()
, input_(input), modulator_(output)
, input_(input), modulator_(&getModulator())
, running_(false), send_delay_(true)
{}
@ -67,11 +67,17 @@ struct Encoder {
state_ = state_type::STATE_IDLE;
osEvent evt = osMessageGet(input_, osWaitForever);
if (evt.status == osEventMessage) {
tx_delay_ = kiss::settings().txdelay;
tx_tail_ = kiss::settings().txtail;
p_persist_ = kiss::settings().ppersist;
slot_time_ = kiss::settings().slot;
duplex_ = kiss::settings().duplex;
auto frame = (IoFrame*) evt.value.p;
process(frame);
// See if we have back-to-back frames.
evt = osMessagePeek(input_, 0);
if (evt.status != osEventMessage) {
send_raw(IDLE);
send_raw(IDLE);
send_delay_ = true;
if (!duplex_) {
@ -95,6 +101,11 @@ struct Encoder {
int p_persist() const { return p_persist_; }
void p_persist(int value) { p_persist_ = value; }
void updateModulator()
{
modulator_ = &(getModulator());
}
state_type status() const {return state_; }
void stop() { running_ = false; }
@ -192,7 +203,8 @@ struct Encoder {
}
void send_delay() {
const size_t tmp = tx_delay_ * modulator_->bits_per_ms();
const size_t tmp = tx_delay_ * 1.25 * modulator_->bits_per_ms();
INFO("Sending %u IDLE bytes", tmp);
for (size_t i = 0; i != tmp; i++) {
send_raw(IDLE);
}
@ -239,5 +251,3 @@ struct Encoder {
};
}}} // mobilinkd::tnc::hdlc
#endif // INC_HDLCENCODER_HPP_

Wyświetl plik

@ -7,48 +7,6 @@
namespace mobilinkd { namespace tnc { namespace hdlc {
Decoder::Decoder(bool pass_all)
: state_(SEARCH), ones_(0), buffer_(0), frame_(acquire())
, bits_(0), passall_(pass_all), ready_(false)
{}
IoFrame* Decoder::operator()(bool bit, bool pll)
{
IoFrame* result = nullptr;
// It appears that the ioFramePool may not get initialized in the proper
// order during some builds.
if (nullptr == frame_) frame_ = acquire();
if (nullptr == frame_) return result;
if (not pll) {
if ((state_ == FRAMING) and (frame_->size() >= 15) and passall_) {
frame_->parse_fcs();
if (passall_ or frame_->ok()) {
result = frame_;
ready_ = false;
frame_ = acquire();
return result;
}
}
reset();
} else {
if (process(bit)) {
ready_ = false;
frame_->parse_fcs();
if (passall_ or frame_->ok()) {
result = frame_;
frame_ = acquire();
if (frame_) start_hunt();
return result;
}
frame_->clear();
}
}
return result;
}
NewDecoder::optional_result_type NewDecoder::operator()(bool input, bool pll_lock)
{
optional_result_type result = nullptr;
@ -56,9 +14,8 @@ NewDecoder::optional_result_type NewDecoder::operator()(bool input, bool pll_loc
auto status = process(input, pll_lock);
if (status)
{
// INFO("Frame Status = %02x, size = %d, CRC = %04x",
// int(status), int(packet->size()), int(packet->crc()));
if (((status & STATUS_OK) || passall) && packet->size() > 10)
INFO("HDLC decode status = 0x%02x, bits = %d", int(status), int(report_bits));
if (can_pass(status) and packet->size() > 10)
{
result = packet;
packet = nullptr;
@ -74,6 +31,8 @@ uint8_t NewDecoder::process(bool input, bool pll_lock)
{
uint8_t result_code = 0;
if (state == State::IDLE and not pll_lock) return result_code;
while (packet == nullptr) {
packet = ioFramePool().acquire();
if (!packet) osThreadYield();
@ -104,9 +63,24 @@ uint8_t NewDecoder::process(bool input, bool pll_lock)
if (flag) {
switch (buffer) {
case 0x7E:
if (packet->size()) {
if (packet->size() > 0) {
// We have started decoding a packet.
packet->parse_fcs();
result_code = packet->ok() ? STATUS_OK : STATUS_CRC_ERROR;
report_bits = bits;
if (packet->ok()) {
// CRC is OK, assume a good packet, even if there are
// extraneous bits decoded.
result_code = STATUS_OK;
} else if (bits == 8) {
// Otherwise, if there is a CRC error but we are on
// an even byte boundary, flag a CRC error. This is
// used by the "pass all" rule.
result_code = STATUS_CRC_ERROR;
} else {
// Extraneous bits mean we have a framing error.
// We should not pass this frame up the stack.
result_code = STATUS_FRAME_ERROR;
}
}
state = State::SYNC;
flag = 0;
@ -127,7 +101,8 @@ uint8_t NewDecoder::process(bool input, bool pll_lock)
return result_code;
}
switch (state) {
switch (state)
{
case State::IDLE:
break;
@ -149,10 +124,29 @@ uint8_t NewDecoder::process(bool input, bool pll_lock)
}
} else {
// PLL unlocked.
if (packet->size()) {
// Note the rules here are the same as above.
report_bits = bits;
if (packet->size() > 2)
{
packet->parse_fcs();
result_code = packet->ok() ? STATUS_OK | STATUS_NO_CARRIER : STATUS_NO_CARRIER;
if (packet->ok())
{
result_code = STATUS_OK;
}
else if (bits == 8)
{
result_code = STATUS_CRC_ERROR;
}
else
{
result_code = STATUS_NO_CARRIER;
}
}
else
{
result_code = STATUS_NO_CARRIER;
}
if (state != State::IDLE) {
buffer = 0;
flag = 0;

Wyświetl plik

@ -1,8 +1,7 @@
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
// Copyright 2015-2020 Mobilinkd LLC <rob@mobilinkd.com>
// All rights reserved.
#ifndef MOBILINKD___HDLC_DECODER_HPP_
#define MOBILINKD___HDLC_DECODER_HPP_
#pragma once
#include "HdlcFrame.hpp"
@ -10,320 +9,6 @@
namespace mobilinkd { namespace tnc { namespace hdlc {
struct Decoder
{
static const uint16_t FLAG = 0x7E00;
static const uint16_t ABORT = 0x7F;
static const uint16_t IDLE = 0xFF;
enum state {SEARCH, HUNT, FRAMING};
state state_;
int ones_;
uint16_t buffer_;
IoFrame* frame_;
int bits_;
bool passall_;
bool ready_;
Decoder(bool pass_all = false);
void reset() {
state_ = SEARCH;
ones_ = 0;
buffer_ = 0;
if (frame_) frame_->clear();
ready_ = false;
}
bool process(bool bit)
{
switch (state_)
{
case SEARCH:
search(bit);
break;
case HUNT:
hunt(bit);
break;
case FRAMING:
frame(bit);
break;
default:
reset();
break;
}
return ready_;
}
/**
* Process a bit. If the bit results in a frame, a result set containing
* the frame, the FCS, and a flag indicating whether it is valid is
* returned. If HDLC was contructed with passall=false, the flag returned
* is always true as only valid frames are returned.
*
* When PLL is passed, when true it indicates that the bit should be
* processed, and when false indicates that any frame in progress should
* be dropped and the state reset to SEARCH.
*/
IoFrame* operator()(bool bit, bool pll);
void add_bit(bool bit)
{
const uint16_t BIT = 0x8000;
buffer_ >>= 1;
buffer_ |= (BIT * int(bit));
bits_ += 1;
}
char get_char()
{
char result = (buffer_ & 0xFF);
return result;
}
void consume_byte()
{
const uint16_t MASK = 0xFF00;
buffer_ &= MASK;
bits_ -= 8;
}
void consume_bit()
{
const uint16_t MASK = 0xFF00;
uint16_t tmp = (buffer_ & 0x7F);
tmp <<= 1;
buffer_ &= MASK;
buffer_ |= tmp;
bits_ -= 1;
}
void start_search()
{
state_ = SEARCH;
}
bool have_flag()
{
const uint16_t MASK = 0xFF00;
return (buffer_ & MASK) == FLAG;
}
void start_hunt()
{
state_ = HUNT;
bits_ = 0;
buffer_ = 0;
}
void search(bool bit)
{
add_bit(bit);
if (have_flag())
{
start_hunt();
}
}
bool have_frame_error()
{
switch (buffer_ & 0xFF00)
{
case 0xFF00:
case 0xFE00:
case 0xFC00:
case 0x7F00:
case 0x7E00:
case 0x3F00:
return true;
default:
return false;
}
}
bool have_bogon()
{
const uint16_t MASK = 0xFF00;
if (bits_ != 8) return false;
const uint16_t test = (buffer_ & MASK);
switch (test)
{
case 0xFF00:
case 0xFE00:
case 0x7F00:
return true;
default:
return false;
}
}
void start_frame()
{
state_ = FRAMING;
frame_->clear();
ones_ = 0;
buffer_ &= 0xFF00;
}
void hunt(bool bit)
{
const uint16_t MASK = 0xFF00;
add_bit(bit);
buffer_ &= MASK;
if (bits_ != 8) return;
if (have_flag()) {
start_hunt();
return;
}
if (have_bogon()) {
start_search();
return;
}
if (not have_frame_error()) {
start_frame();
return;
}
start_search();
}
void frame(bool bit)
{
add_bit(bit);
if (ones_ < 5) {
if (buffer_ & 0x80) ones_ += 1;
else ones_ = 0;
if (bits_ == 16) {
if (frame_->push_back(get_char())) {
consume_byte();
} else {
// Allocation error.
frame_->clear();
start_search();
}
}
if (have_flag()) {
if (frame_->size() >= 15) {
ready_ = true;
}
}
} else {
// 5 ones in a row means the next one should be 0 and be skipped.
if ((buffer_ & 0x80) == 0) {
ones_ = 0;
consume_bit();
return;
} else {
// Framing error. Drop the frame. If there is a FLAG
// in the buffer, go into HUNT otherwise SEARCH.
if (frame_->size() > 15) {
ready_ = true;
return;
}
if ((buffer_ >> (16 - bits_) & 0xFF) == 0x7E) {
// Cannot call start_hunt() here because we need
// to preserve buffer state.
bits_ -= 8;
state_ = HUNT;
frame_->clear();
} else {
start_search();
}
}
}
}
bool frame_end()
{
uint16_t tmp = (buffer_ >> (16 - bits_));
return (tmp & 0xFF) == FLAG;
}
bool frame_abort()
{
uint16_t tmp = (buffer_ >> (16 - bits_));
return (tmp & 0x7FFF) == 0x7FFF;
}
void abort_frame()
{
bits_ = 8;
buffer_ &= 0xFF00;
frame_->clear();
}
bool ready() const
{
return ready_;
}
};
#if 0
Decoder::Decoder(bool pass_all)
: state_(SEARCH), ones_(0), buffer_(0), frame_(acquire())
, bits_(0), passall_(pass_all), ready_(false)
{}
IoFrame* Decoder::operator()(bool bit, bool pll)
{
IoFrame* result = nullptr;
// It appears that the ioFramePool may not get initialized in the proper
// order during some builds.
if (nullptr == frame_) frame_ = acquire();
if (not pll) {
if ((state_ == FRAMING) and (frame_->size() > 15) and passall_) {
frame_->parse_fcs();
if (passall_ or frame_->ok()) {
result = frame_;
ready_ = false;
frame_ = acquire();
return result;
}
}
reset();
} else {
if (process(bit)) {
ready_ = false;
frame_->parse_fcs();
if (passall_ or frame_->ok()) {
result = frame_;
frame_ = acquire();
return result;
}
frame_->clear();
}
}
return result;
}
#endif
struct NewDecoder
{
enum class State {IDLE, SYNC, RECEIVE};
@ -338,15 +23,22 @@ struct NewDecoder
static constexpr uint8_t STATUS_NO_CARRIER{0x10};
static constexpr uint8_t STATUS_CRC_ERROR{0x20};
const uint16_t VALID_CRC = 0xf0b8;
static constexpr uint16_t VALID_CRC = 0xf0b8;
State state{State::IDLE};
uint8_t buffer{0};
uint8_t bits{0};
uint8_t report_bits{0};
uint8_t ones{0};
bool flag{0};
/**
* Tell the demodulator to return all "passable" HDLC frames. These
* are frames which consist of an even multiple of eight bits and are
* up to 330 bytes, but which do not have a valid checksum.
*/
bool passall{false};
frame_type* packet{nullptr};
@ -355,10 +47,13 @@ struct NewDecoder
: passall(pass_all)
{}
bool can_pass(uint8_t status) const
{
return status == STATUS_OK or (passall and status == STATUS_CRC_ERROR);
}
optional_result_type operator()(bool input, bool pll_lock);
uint8_t process(bool input, bool pll_lock);
};
}}} // mobilinkd::tnc::hdlc
#endif // MOBILINKD___HDLC_DECODER_HPP_

Wyświetl plik

@ -12,6 +12,7 @@
#include "Kiss.hpp"
#include "KissHardware.hpp"
#include "ModulatorTask.hpp"
#include "Modulator.hpp"
#include "UsbPort.hpp"
#include "SerialPort.hpp"
#include "NullPort.hpp"
@ -60,7 +61,6 @@ void startIOEventTask(void const*)
hardware.init();
hardware.store();
}
// hardware.init();
osMutexRelease(hardwareInitMutexHandle);
@ -107,7 +107,6 @@ void startIOEventTask(void const*)
DEBUG("USB connected -- negotiate");
HAL_GPIO_WritePin(BT_SLEEP_GPIO_Port, BT_SLEEP_Pin,
GPIO_PIN_RESET);
// SysClock48();
osTimerStart(usbShutdownTimerHandle, 5000);
}
}

Wyświetl plik

@ -6,7 +6,9 @@
#include "AudioInput.hpp"
#include "AudioLevel.hpp"
#include "IOEventTask.h"
#include <ModulatorTask.hpp>
#include "ModulatorTask.hpp"
#include "Modulator.hpp"
#include "HDLCEncoder.hpp"
#include <memory>
#include <array>
@ -27,17 +29,10 @@ int powerOffViaUSB(void)
namespace mobilinkd { namespace tnc { namespace kiss {
const char FIRMWARE_VERSION[] = "2.0.0a1";
const char FIRMWARE_VERSION[] = "2.0.0b1";
const char HARDWARE_VERSION[] = "Mobilinkd TNC3 2.1.1";
const std::array<const char*, 4> modem_type_lookup = {
"NOT SET",
"AFSK1200",
"AFSK300",
"FSK9600",
};
Hardware& settings()
{
static Hardware instance __attribute__((section(".bss3")));
@ -107,9 +102,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);
}
@ -127,17 +120,33 @@ inline void reply(uint8_t cmd, const uint8_t* data, uint16_t len) {
ioport->write(buffer, len + 1, 6, osWaitForever);
}
inline void reply_ext(uint8_t ext, uint8_t cmd, const uint8_t* data, uint16_t len) {
auto buffer = static_cast<uint8_t*>(alloca(len + 2));
template <size_t N>
void reply_ext(const std::array<uint8_t, N>& cmd, const uint8_t* data, uint16_t len) {
auto buffer = static_cast<uint8_t*>(alloca(len + N));
if (buffer == nullptr) return;
buffer[0] = ext;
buffer[1] = cmd;
std::copy(std::begin(cmd), std::end(cmd), buffer);
for (uint16_t i = 0; i != len and data[i] != 0; i++)
buffer[i + 2] = data[i];
buffer[i + N] = data[i];
ioport->write(buffer, len + 2, 6, osWaitForever);
free(buffer);
}
#endif
template <size_t N>
inline void ext_reply(const std::array<uint8_t, N>& cmd, uint8_t result) {
std::array<uint8_t, 1 + N> buffer;
std::copy(std::begin(cmd), std::end(cmd), std::begin(buffer));
buffer[N] = result;
ioport->write(buffer.data(), N + 1, 6, osWaitForever);
}
template <size_t M, size_t N>
void ext_reply(const std::array<uint8_t, M>& cmd, const std::array<uint8_t, N>& result) {
std::array<uint8_t, M + N> data;
std::copy(std::begin(cmd), std::end(cmd), std::begin(data));
std::copy(std::begin(result), std::end(result), std::begin(data) + M);
ioport->write(data.data(), M + N, 6, osWaitForever);
}
void Hardware::get_alias(uint8_t alias) {
uint8_t result[14];
@ -149,7 +158,7 @@ void Hardware::get_alias(uint8_t alias) {
result[11] = aliases[alias].insert_id;
result[12] = aliases[alias].preempt;
result[13] = aliases[alias].hops;
reply_ext(hardware::EXTENDED_CMD, hardware::EXT_GET_ALIASES, result, 14);
reply_ext(hardware::EXT_GET_ALIASES, result, 14);
}
void Hardware::set_alias(const hdlc::IoFrame* frame) {
@ -187,14 +196,13 @@ 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);
@ -385,26 +393,6 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
reply8(hardware::GET_DUPLEX, duplex);
break;
case hardware::SET_MODEM_TYPE:
DEBUG("SET_MODEM_TYPE");
if ((*it > 0) and (*it < 4))
{
modem_type = *it;
DEBUG(modem_type_lookup[*it]);
update_crc();
}
else
{
ERROR("Unknown modem type");
}
osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS,
osWaitForever);
[[fallthrough]];
case hardware::GET_MODEM_TYPE:
DEBUG("GET_MODEM_TYPE");
reply8(hardware::GET_MODEM_TYPE, modem_type);
break;
case hardware::GET_FIRMWARE_VERSION:
DEBUG("GET_FIRMWARE_VERSION");
reply(hardware::GET_FIRMWARE_VERSION, (uint8_t*) FIRMWARE_VERSION,
@ -440,6 +428,20 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1);
break;
case hardware::SET_PASSALL:
DEBUG("SET_PASSALL");
if (!*it) {
options &= ~KISS_OPTION_PASSALL;
} else {
options |= KISS_OPTION_PASSALL;
}
update_crc();
break;
case hardware::GET_PASSALL:
DEBUG("GET_PASSALL");
reply8(hardware::GET_PASSALL, options & KISS_OPTION_PASSALL ? 1 : 0);
break;
case hardware::SET_USB_POWER_OFF:
DEBUG("SET_USB_POWER_OFF");
if (*it) {
@ -491,8 +493,6 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
reply16(hardware::GET_API_VERSION, hardware::KISS_API_VERSION);
osMessagePut(audioInputQueueHandle, audio::POLL_BATTERY_LEVEL,
osWaitForever);
osMessagePut(audioInputQueueHandle, audio::POLL_TWIST_LEVEL,
osWaitForever);
osMessagePut(audioInputQueueHandle, audio::IDLE,
osWaitForever);
reply(hardware::GET_FIRMWARE_VERSION, (uint8_t*) FIRMWARE_VERSION,
@ -514,6 +514,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
reply8(hardware::GET_DUPLEX, duplex);
reply8(hardware::GET_PTT_CHANNEL,
options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1);
reply8(hardware::GET_PASSALL, options & KISS_OPTION_PASSALL ? 1 : 0);
reply16(hardware::GET_CAPABILITIES,
hardware::CAP_EEPROM_SAVE|hardware::CAP_BATTERY_LEVEL|
hardware::CAP_ADJUST_INPUT|hardware::CAP_DFU_FIRMWARE);
@ -523,43 +524,60 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
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);
ext_reply(hardware::EXT_GET_MODEM_TYPE, modem_type);
ext_reply(hardware::EXT_GET_MODEM_TYPES, supported_modem_types);
if (*error_message) {
reply(hardware::GET_ERROR_MSG, (uint8_t*) error_message, sizeof(error_message));
}
break;
case hardware::EXTENDED_CMD:
handle_ext_request(frame);
break;
default:
ERROR("Unknown hardware request");
if (command > 0xC0)
{
handle_ext_request(frame);
}
else
{
ERROR("Unknown hardware request");
}
}
}
inline void ext_reply(uint8_t cmd, uint8_t result) {
uint8_t data[3] { hardware::EXTENDED_CMD, cmd, result };
ioport->write(data, 3, 6);
}
void Hardware::handle_ext_request(hdlc::IoFrame* frame) {
auto it = frame->begin();
++it;
// Currently only supports 2-byte extended commands.
uint8_t ext_command = *it++;
switch (ext_command) {
case hardware::EXT_GET_MODEM_TYPE:
case hardware::EXT_SET_MODEM_TYPE[1]:
DEBUG("SET_MODEM_TYPE");
if ((*it == hardware::MODEM_TYPE_1200)
or (*it == hardware::MODEM_TYPE_9600))
{
modem_type = *it;
DEBUG(modem_type_lookup[modem_type]);
update_crc();
}
else
{
ERROR("Unsupported modem type");
}
osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS,
osWaitForever);
updateModulator();
[[fallthrough]];
case hardware::EXT_GET_MODEM_TYPE[1]:
DEBUG("EXT_GET_MODEM_TYPE");
ext_reply(hardware::EXT_GET_MODEM_TYPE, 1);
ext_reply(hardware::EXT_GET_MODEM_TYPE, modem_type);
break;
case hardware::EXT_SET_MODEM_TYPE:
DEBUG("EXT_SET_MODEM_TYPE");
ext_reply(hardware::EXT_OK, hardware::EXT_SET_MODEM_TYPE);
break;
case hardware::EXT_GET_MODEM_TYPES:
case hardware::EXT_GET_MODEM_TYPES[1]:
DEBUG("EXT_GET_MODEM_TYPES");
ext_reply(hardware::EXT_GET_MODEM_TYPES, 1);
ext_reply(hardware::EXT_GET_MODEM_TYPES, supported_modem_types);
break;
default:
ERROR("Unknown extended hardware request");
}
}

Wyświetl plik

@ -1,8 +1,7 @@
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
// Copyright 2015-2020 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"
@ -35,7 +34,7 @@ namespace hardware {
* The major version should be updated whenever non-backwards compatible
* changes to the API are made.
*/
constexpr const uint16_t KISS_API_VERSION = 0x0200;
constexpr const uint16_t KISS_API_VERSION = 0x0201;
constexpr const uint16_t CAP_DCD = 0x0100;
constexpr const uint16_t CAP_SQUELCH = 0x0200;
@ -94,9 +93,6 @@ constexpr const uint8_t GET_TIMESLOT = 35;
constexpr const uint8_t GET_TXTAIL = 36;
constexpr const uint8_t GET_DUPLEX = 37;
constexpr const uint8_t SET_MODEM_TYPE = 38;
constexpr const uint8_t GET_MODEM_TYPE = 39;
constexpr const uint8_t GET_FIRMWARE_VERSION = 40;
constexpr const uint8_t GET_HARDWARE_VERSION = 41;
constexpr const uint8_t SAVE_EEPROM_SETTINGS = 42;
@ -129,6 +125,9 @@ constexpr const uint8_t GET_BT_POWER_OFF = 78;
constexpr const uint8_t SET_PTT_CHANNEL = 79; // Which PTT line to use (currently 0 or 1,
constexpr const uint8_t GET_PTT_CHANNEL = 80; // multiplex or simplex)
constexpr const uint8_t SET_PASSALL = 81; // Allow invalid CRC through when
constexpr const uint8_t GET_PASSALL = 82; // true (1).
constexpr const uint8_t GET_MIN_OUTPUT_TWIST = 119; ///< int8_t (may be negative).
constexpr const uint8_t GET_MAX_OUTPUT_TWIST = 120; ///< int8_t (may be negative).
constexpr const uint8_t GET_MIN_INPUT_TWIST = 121; ///< int8_t (may be negative).
@ -140,34 +139,47 @@ constexpr const uint8_t GET_CAPABILITIES = 126; ///< Send all capabilities.
constexpr const uint8_t GET_ALL_VALUES = 127; ///< Send all settings & versions.
/**
* Extended commands are two+ bytes in length. They start at 80:00
* and go through BF:FF (14 significant bits), then proceed to C0:00:00
* through CF:FF:FF (20 more significant bits).
* Extended commands are two+ bytes in length. They follow the model used
* by UTF-8 to extend the command set. Extended commands start at C1:80
* and go through DF:BF (11 significant bits), then proceed to E0:80:80
* through EF:BF:BF (16 significant bits).
*
* If needed, the commands can be extended to 9 nibbles (D0 - DF),
* 13 nibbles (E0-EF) and 17 nibbles (F0-FF).
* To avoid special KISS characters, we skip the following byte values in
* the first byte:
*
* - 0xC0 / FEND
* - 0xDB / FESC
* - 0xDC / TFEND
* - 0xDD / TFESC
*/
constexpr const uint8_t EXTENDED_CMD = 128;
constexpr const uint8_t EXTENDED_CMD = 0xC1;
constexpr const uint8_t EXT_OK = 0;
constexpr const uint8_t EXT_GET_MODEM_TYPE = 1;
constexpr const uint8_t EXT_SET_MODEM_TYPE = 2;
constexpr const uint8_t EXT_GET_MODEM_TYPES = 3; ///< Return a list of supported modem types
constexpr const uint8_t EXT_OK = 0x80;
constexpr const uint8_t EXT_GET_ALIASES = 8; ///< Number of aliases supported
constexpr const uint8_t EXT_GET_ALIAS = 9; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
constexpr const uint8_t EXT_SET_ALIAS = 10; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
constexpr std::array<uint8_t, 2> EXT_GET_MODEM_TYPE = {0xC1, 0x81};
constexpr std::array<uint8_t, 2> EXT_SET_MODEM_TYPE = {0xC1, 0x82};
constexpr std::array<uint8_t, 2> EXT_GET_MODEM_TYPES = {0xC1, 0x83}; ///< Return a list of supported modem types
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 std::array<uint8_t, 2> EXT_GET_ALIASES = {0xC1, 0x88}; ///< Number of aliases supported
constexpr std::array<uint8_t, 2> EXT_GET_ALIAS = {0xC1, 0x89}; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
constexpr std::array<uint8_t, 2> EXT_SET_ALIAS = {0xC1, 0x0A}; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
constexpr const uint8_t MODEM_TYPE_1200 = 1;
constexpr const uint8_t MODEM_TYPE_300 = 2;
constexpr const uint8_t MODEM_TYPE_9600 = 3;
constexpr const uint8_t MODEM_TYPE_PSK31 = 4;
constexpr const uint8_t MODEM_TYPE_OFDM = 5;
constexpr const uint8_t MODEM_TYPE_MFSK16 = 6;
constexpr std::array<uint8_t, 2> EXT_GET_BEACON_SLOTS = {0xC1, 0x8C}; ///< Number of beacons supported
constexpr std::array<uint8_t, 2> EXT_GET_BEACON = {0xC1, 0x8D}; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text)
constexpr std::array<uint8_t, 2> EXT_SET_BEACON = {0xC1, 0x8E}; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text)
/*
* Modem type values 0x00 - 0x7F are single-byte types. Modem type values
* starting at 0xC0 are multi-byte values, and should follow the model used
* by UTF-8 to extend the modem types, should that ever be necessary.
*/
constexpr uint8_t MODEM_TYPE_1200 = 1;
constexpr uint8_t MODEM_TYPE_300 = 2;
constexpr uint8_t MODEM_TYPE_9600 = 3;
constexpr uint8_t MODEM_TYPE_PSK31 = 4;
constexpr uint8_t MODEM_TYPE_OFDM = 5;
constexpr uint8_t MODEM_TYPE_MFSK16 = 6;
// Boolean options.
#define KISS_OPTION_CONN_TRACK 0x01
@ -175,6 +187,7 @@ constexpr const uint8_t MODEM_TYPE_MFSK16 = 6;
#define KISS_OPTION_VIN_POWER_ON 0x04 // Power on when plugged into USB
#define KISS_OPTION_VIN_POWER_OFF 0x08 // Power off when unplugged from USB
#define KISS_OPTION_PTT_SIMPLEX 0x10 // Simplex PTT (the default)
#define KISS_OPTION_PASSALL 0x20 // Ignore invalid CRC.
const char TOCALL[] = "APML30"; // Update for every feature change.
@ -210,11 +223,24 @@ const size_t NUMBER_OF_BEACONS = 4; // 680 bytes
*/
struct Hardware
{
static constexpr std::array<const char*, 4> modem_type_lookup = {
"NOT SET",
"AFSK1200",
"AFSK300",
"FSK9600",
};
// This must match the constants defined above.
enum ModemType {
AFSK1200 = 1,
AFSK300,
FSK9600,
PSK31
AFSK1200 = hardware::MODEM_TYPE_1200,
AFSK300 = hardware::MODEM_TYPE_300,
FSK9600 = hardware::MODEM_TYPE_9600,
PSK31 = hardware::MODEM_TYPE_PSK31
};
static constexpr std::array<uint8_t, 2> supported_modem_types = {
hardware::MODEM_TYPE_1200,
hardware::MODEM_TYPE_9600
};
uint8_t txdelay; ///< How long in 10mS units to wait for TX to settle before starting data
@ -272,7 +298,7 @@ struct Hardware
slot = 10;
txtail = 1;
duplex = 0;
modem_type = ModemType::FSK9600;
modem_type = ModemType::AFSK1200;
output_gain = 63;
input_gain = 0; // 0-4 on TNC3
tx_twist = 50;
@ -299,13 +325,13 @@ struct Hardware
DEBUG("Slot Time: %d", (int)slot);
DEBUG("TX Tail: %d", (int)txtail);
DEBUG("Duplex: %d", (int)duplex);
DEBUG("Modem Type: %d", (int)modem_type);
DEBUG("Modem Type: %s", modem_type_lookup[modem_type]);
DEBUG("TX Gain: %d", (int)output_gain);
DEBUG("RX Gain: %d", (int)input_gain);
DEBUG("TX Twist: %d", (int)tx_twist);
DEBUG("RX Twist: %d", (int)rx_twist);
DEBUG("Log Level: %d", (int)log_level);
DEBUG("Options: %d", (int)options);
DEBUG("Options: %04hx", options);
DEBUG("MYCALL: %s", (char*) mycall);
DEBUG("Dedupe time (secs): %d", (int)dedupe_seconds);
DEBUG("Aliases:");
@ -325,7 +351,7 @@ struct Hardware
DEBUG(" text: %s", (char*)b.text);
DEBUG(" frequency (secs): %d", (int)b.seconds);
}
DEBUG("Checksum: %04x", checksum);
DEBUG("Checksum: %04hx", checksum);
}
bool load();
@ -350,7 +376,6 @@ struct Hardware
extern Hardware& settings();
struct I2C_Storage
{
constexpr static const uint16_t i2c_address{EEPROM_ADDRESS};
@ -378,5 +403,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_

Wyświetl plik

@ -6,6 +6,7 @@
#include "PTT.hpp"
#include "KissHardware.hpp"
#include "stm32l4xx_hal.h"
#include "cmsis_os.h"
#include <cstdint>
@ -88,6 +89,44 @@ struct Modulator
virtual void abort() = 0;
virtual float bits_per_ms() const = 0;
protected:
/**
* Stop the DMA conversion and the timer. Configure DAC for no
* trigger and set the DAC level to exactly mid-level.
*
* @note The DAC is set to mid-level to ensure the audio coupling
* capacitor is kept biased to ensure that there is no DC level
* ramp when we start conversions. This is bad for FSK.
*/
void stop_conversion()
{
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_TIM_Base_Stop(&htim7);
DAC_ChannelConfTypeDef sConfig;
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
CxxErrorHandler();
}
if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048) != HAL_OK)
{
CxxErrorHandler();
}
if (HAL_DAC_Start(&hdac1, DAC_CHANNEL_1) != HAL_OK)
{
CxxErrorHandler();
}
}
};
}} // mobilinkd::tnc

Wyświetl plik

@ -2,6 +2,8 @@
// All rights reserved.
#include "ModulatorTask.hpp"
#include "HDLCEncoder.hpp"
#include "Modulator.hpp"
#include "Fsk9600Modulator.hpp"
#include "AFSKModulator.hpp"
#include "KissHardware.hpp"
@ -50,12 +52,12 @@ mobilinkd::tnc::Modulator& getModulator()
case kiss::Hardware::ModemType::AFSK1200:
return afsk1200modulator;
default:
_Error_Handler(__FILE__, __LINE__);
CxxErrorHandler();
}
}
mobilinkd::tnc::hdlc::Encoder& getEncoder() {
static mobilinkd::tnc::hdlc::Encoder instance(hdlcOutputQueueHandle, &getModulator());
static mobilinkd::tnc::hdlc::Encoder instance(hdlcOutputQueueHandle);
return instance;
}
@ -81,6 +83,16 @@ void updatePtt()
getModulator().set_ptt(&multiplexPtt);
}
void updateModulator()
{
using namespace mobilinkd::tnc::kiss;
modulator = &getModulator();
modulator->init(settings());
updatePtt();
encoder->updateModulator();
}
void startModulatorTask(void const*) {
using namespace mobilinkd::tnc::kiss;

Wyświetl plik

@ -1,12 +1,8 @@
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
// Copyright 2015-2020 Mobilinkd LLC <rob@mobilinkd.com>
// All rights reserved.
#pragma once
#ifndef MOBILINKD__MODULATOR_TASK_HPP_
#define MOBILINKD__MODULATOR_TASK_HPP_
#include "HDLCEncoder.hpp"
#include "Modulator.hpp"
#include "PTT.hpp"
#include "cmsis_os.h"
@ -14,10 +10,21 @@
extern "C" {
#endif
namespace mobilinkd { namespace tnc {
class Modulator;
namespace hdlc {
class Encoder;
}
}}
extern mobilinkd::tnc::SimplexPTT simplexPtt;
extern mobilinkd::tnc::MultiplexPTT multiplexPtt;
mobilinkd::tnc::Modulator& getModulator();
mobilinkd::tnc::hdlc::Encoder& getEncoder();
void startModulatorTask(void const * argument);
@ -26,9 +33,8 @@ enum class PTT {SIMPLEX, MULTIPLEX};
void setPtt(PTT ptt);
void updatePtt(void);
void updateModulator(void);
#ifdef __cplusplus
}
#endif
#endif // MOBILINKD__MODULATOR_TASK_HPP_