Start of 9600 baud support.

fsk9600
Rob Riggs 2020-01-10 12:05:24 -06:00
rodzic 9f523247bf
commit 835ae8e265
23 zmienionych plików z 1030 dodań i 746 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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

Wyświetl plik

@ -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_

Wyświetl plik

@ -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;

Wyświetl plik

@ -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)

93
TNC/Modulator.hpp 100644
Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);