first commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# KissTnc ARDUINO #
|
||||
|
||||
## Description ##
|
||||
|
||||
KissTnc is a C++ implementation of the TNC software for Arduino. It can be used for AX.25 / packet radio as well as for APRS applications. It is tested on the Arduino Nano and is compatible with MicroAPRS and PA4RAZ APRS projects. The sketch and all used cpp and header files can be opened in the Arduino IDE.
|
||||
|
||||
## Hardware ##
|
||||
|
||||
Hardware can be made around the Nano or other Arduino board, using the schematic added to this repository.
|
||||
By default the following pins are used:
|
||||
|
||||
- RX LED pin D10
|
||||
- TX LED pin D9
|
||||
- RX only input D8
|
||||
- PTT control pin D3
|
||||
- Low level Input select pin D2
|
||||
- The used analog input is A0
|
||||
- The four pins for generating the AFSK modulation signal are pin D4, D5, D6 and D7.
|
||||
|
||||
The PTT, LED, RX only and level selection pins can be changed in the sketch by editing the defines in KissTnc.ino.
|
||||
|
||||
The squelch on the radio can stay open, detection of incomming data (and collision prevention) is done by software.
|
||||
If the audio level from the transceiver is low (below 1V peak-to-peak), the TNC can be set in the low level mode. To run in low level mode, connect D2 to 5V, and connect R10 to Ref instead of 5V.
|
||||
|
||||
Rx only mode can be activated by connecting the RX only input (pin D9) to GND via a switch.
|
||||
|
||||
## KISS Protocol ##
|
||||
|
||||
KissTnc is compatible with the KISS protocol as described at:
|
||||
<http://www.ax25.net/kiss.aspx>
|
||||
|
||||
The following KISS commands are implemented:
|
||||
|
||||
- CMD_DATA_FRAME (0x00)
|
||||
- CMD_TX_DELAY (0x01)
|
||||
- CMD_P (0x02)
|
||||
- CMD_SLOT_TIME (0x03)
|
||||
- CMD_FULL_DUPLEX (0x05)
|
||||
|
||||
For testing purpose an additional command is implemented:
|
||||
|
||||
- CMD_LOOPBACK_TEST (0xfe)
|
||||
|
||||
When enabling this function, packets transmitted from the host are routed back to the receiving part of the software. Instead of transmitting the packets, a constant 1200Hz tone is audible at the modulation output. After any change of the code, the software can be tested by doing a loop back test. The packet returned back to the host must be the same as the original ones. The signal set on the modulation output must be a stable 1200Hz (+/-1) tone. If this frequency is not 1200Hz or is not stable, the ISR is not returning on time.
|
||||
|
||||
Following is not implemented:
|
||||
|
||||
- CMD_TX_TAIL (0x04) (obsolete)
|
||||
- CMD_SET_HARDWARE (0x06)
|
||||
- CMD_RETURN (0xff)
|
||||
Binary file not shown.
@@ -0,0 +1,43 @@
|
||||
#include "afskSampler.h"
|
||||
|
||||
HdlcAfskRx* g_afskRx;
|
||||
HdlcAfskTx* g_afskTx;
|
||||
byte loopBackSample;
|
||||
|
||||
AfskSampler::AfskSampler(int lowLevelSelectPin, HdlcAfskRx* afskRx, HdlcAfskTx* afskTx) {
|
||||
g_afskRx = afskRx;
|
||||
g_afskTx = afskTx;
|
||||
|
||||
boolean lowLevelMode = false;
|
||||
if (lowLevelSelectPin > 0 ) {
|
||||
pinMode(lowLevelSelectPin, INPUT);
|
||||
lowLevelMode = digitalRead(lowLevelSelectPin) == HIGH;
|
||||
}
|
||||
|
||||
ADCSRA = 0;
|
||||
ADCSRB = 0;
|
||||
ADMUX |= (0 & 0x07); // pin A0
|
||||
if (lowLevelMode) {
|
||||
ADMUX |= (1 << REFS1); // set internal reference voltage to 1.1V
|
||||
}
|
||||
ADMUX |= (1 << REFS0); // set reference voltage internal
|
||||
ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
|
||||
|
||||
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescaler 9600 Hz
|
||||
ADCSRA |= (1 << ADATE); // enable auto trigger
|
||||
ADCSRA |= (1 << ADIE); // enable interrupts when measurement complete
|
||||
ADCSRA |= (1 << ADEN); // enable ADC
|
||||
ADCSRA |= (1 << ADSC); // start ADC measurements
|
||||
}
|
||||
|
||||
ISR(ADC_vect) {
|
||||
if (g_afskTx->loopbackTest) {
|
||||
g_afskRx->decodeSample(loopBackSample);
|
||||
}
|
||||
else {
|
||||
g_afskRx->decodeSample(ADCH);
|
||||
}
|
||||
loopBackSample = g_afskTx->encodeSample();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef AFSK_SAMPLER_H
|
||||
#define AFSK_SAMPLER_H
|
||||
|
||||
#include "hdlcAfskTx.h"
|
||||
#include "hdlcAfskRx.h"
|
||||
|
||||
class AfskSampler {
|
||||
public:
|
||||
AfskSampler(int lowLevelSelectPin, HdlcAfskRx* afskRx, HdlcAfskTx* afskTx);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "hdlcAfskRx.h"
|
||||
|
||||
HdlcAfskRx::HdlcAfskRx(HdlcFrameBuffer* rxBuffer) {
|
||||
_rxBuffer = rxBuffer;
|
||||
}
|
||||
|
||||
void HdlcAfskRx::decodeSample(byte sampleVal) {
|
||||
int intAnVal = sampleVal - 127;
|
||||
|
||||
_rxFilter[0][0] = _rxFilter[0][1];
|
||||
_rxFilter[0][1] = (_rxAnSamples[_histAnSampleIndex] * intAnVal) >> 2;
|
||||
_rxFilter[1][0] = _rxFilter[1][1];
|
||||
_rxFilter[1][1] = _rxFilter[0][0] + _rxFilter[0][1] + (_rxFilter[1][0] >> 1);
|
||||
_rxAnSamples[_rxAnSampleIndex] = intAnVal;
|
||||
_histAnSampleIndex++;
|
||||
if (_histAnSampleIndex > MAX_RX_AN_SAMPLE_INDEX) {
|
||||
_histAnSampleIndex = 0;
|
||||
}
|
||||
_rxAnSampleIndex++;
|
||||
if (_rxAnSampleIndex > MAX_RX_AN_SAMPLE_INDEX) {
|
||||
_rxAnSampleIndex = 0;
|
||||
}
|
||||
_spaceMarkSamples <<= 1;
|
||||
_spaceMarkSamples |= (_rxFilter[1][1] > 0) ? 1 : 0;
|
||||
|
||||
if (
|
||||
(
|
||||
( (_spaceMarkSamples)^(_spaceMarkSamples >> 2) ) & SPACE_MARK_TRANSISION_MASK
|
||||
) == SPACE_MARK_TRANSISION_MASK
|
||||
) {
|
||||
if (_currentPhase < HALF_WINDOW_SAMPLE_COUNT) {
|
||||
_currentPhase += 1;
|
||||
}
|
||||
else {
|
||||
_currentPhase -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
_currentPhase += RX_AN_SAMPLE_COUNT;
|
||||
if (_currentPhase >= WINDOW_SAMPLE_COUNT) {
|
||||
_currentPhase %= WINDOW_SAMPLE_COUNT;
|
||||
byte bits = _spaceMarkSamples & 0x07;
|
||||
setNextMark(
|
||||
(bits == 0x07 || // 111
|
||||
bits == 0x06 || // 110
|
||||
bits == 0x05 || // 101
|
||||
bits == 0x03) // 011
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcAfskRx::setNextMark(boolean spaceMark) {
|
||||
_prevSpace = _currentSpace;
|
||||
_currentSpace = spaceMark;
|
||||
|
||||
boolean flagDetect = false;
|
||||
|
||||
switch (stufState) {
|
||||
case STUF_NO_STUF:
|
||||
_afskShiftInReg >>= 1;
|
||||
_rxBitCount++;
|
||||
if (_rxBitCount > 8) {
|
||||
_rxBitCount = 1;
|
||||
}
|
||||
_afskShiftInReg |= (_prevSpace == _currentSpace) ? 0x80 : 0;
|
||||
flagDetect = _afskShiftInReg == HDLC_FLAG;
|
||||
if ((_afskShiftInReg == STUF1) || (_afskShiftInReg == STUF2)) {
|
||||
stufState = STUF_SKIP;
|
||||
}
|
||||
break;
|
||||
case STUF_SKIP:
|
||||
_afskShiftInReg |= (_prevSpace == _currentSpace) ? 0x80 : 0;
|
||||
stufState = STUF_SKIP_CHECKS;
|
||||
break;
|
||||
case STUF_SKIP_CHECKS:
|
||||
_afskShiftInReg >>= 1;
|
||||
_rxBitCount++;
|
||||
if (_rxBitCount > 8) {
|
||||
_rxBitCount = 1;
|
||||
}
|
||||
_afskShiftInReg |= (_prevSpace == _currentSpace) ? 0x80 : 0;
|
||||
stufState = STUF_NO_STUF;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stufState == STUF_SKIP) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_frameRxState) {
|
||||
case RXST_IDLE:
|
||||
if (flagDetect) {
|
||||
_rxBitCount = 0;
|
||||
_frameRxState = RXST_SYNC1;
|
||||
}
|
||||
break;
|
||||
case RXST_SYNC1:
|
||||
if (_rxBitCount > 7) {
|
||||
if (flagDetect) {
|
||||
_rxBuffer->frameReset();
|
||||
_frameRxState = RXST_SYNC2;
|
||||
}
|
||||
else {
|
||||
_frameRxState = RXST_IDLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RXST_SYNC2:
|
||||
if (_rxBitCount > 7) {
|
||||
if (!flagDetect) {
|
||||
_frameRxState = RXST_START;
|
||||
receiving = true;
|
||||
if (!_rxBuffer->push(_afskShiftInReg)) {
|
||||
_frameRxState = RXST_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RXST_START:
|
||||
if (flagDetect) {
|
||||
_rxBitCount = 0;
|
||||
_frameRxState = RXST_SYNC1;
|
||||
receiving = false;
|
||||
_rxBuffer->frameEnd();
|
||||
}
|
||||
else if (_rxBitCount > 7) {
|
||||
if (!_rxBuffer->push(_afskShiftInReg)) {
|
||||
_frameRxState = RXST_IDLE;
|
||||
receiving = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef HDLC_AFSK_RX_H
|
||||
#define HDLC_AFSK_RX_H
|
||||
|
||||
#include "hdlcFramesBuffer.h"
|
||||
|
||||
#define STUF_NO_STUF 0
|
||||
#define STUF_SKIP 1
|
||||
#define STUF_SKIP_CHECKS 2
|
||||
|
||||
#define RXST_IDLE 0
|
||||
#define RXST_SYNC1 1
|
||||
#define RXST_SYNC2 2
|
||||
#define RXST_START 3
|
||||
|
||||
#define STUF1 0x7c
|
||||
#define STUF2 0x7d
|
||||
|
||||
#define RX_AN_SAMPLE_COUNT 8
|
||||
#define MAX_RX_AN_SAMPLE_INDEX 7
|
||||
#define WINDOW_SAMPLE_COUNT 64
|
||||
#define HALF_WINDOW_SAMPLE_COUNT 32
|
||||
#define SPACE_MARK_TRANSISION_MASK 0x03
|
||||
|
||||
class HdlcAfskRx {
|
||||
public:
|
||||
HdlcAfskRx(HdlcFrameBuffer* rxBuffer);
|
||||
void decodeSample(byte sampleVal);
|
||||
boolean receiving = false;
|
||||
private:
|
||||
HdlcFrameBuffer* _rxBuffer;
|
||||
int _rxFilter[2][2];
|
||||
int _rxAnSamples[RX_AN_SAMPLE_COUNT];
|
||||
int _rxAnSampleIndex = 0;
|
||||
int _histAnSampleIndex = 4;
|
||||
byte _spaceMarkSamples = 0x00;
|
||||
int _currentPhase = 0;
|
||||
boolean _prevSpace = false;
|
||||
boolean _currentSpace = false;
|
||||
byte _afskShiftInReg = 0x00;
|
||||
int stufState = STUF_NO_STUF;
|
||||
int _rxBitCount = 0;
|
||||
int _frameRxState = RXST_IDLE;
|
||||
void setNextMark(boolean spaceMark);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
#include "hdlcAfskTx.h"
|
||||
|
||||
const byte sinusTable[SIN_TABLE_SIZE] PROGMEM = { //idx 0=0deg, 1=7.5deg, 2=15deg, 3=22.5deg, etc.
|
||||
0x80, 0x90, 0xa0, 0xb0, 0xbf, 0xcd, 0xda, 0xe5, 0xee, 0xf5, 0xfb, 0xfe, // 0.. 82.5deg
|
||||
0xff, 0xfe, 0xfb, 0xf5, 0xee, 0xe5, 0xda, 0xcd, 0xbf, 0xb0, 0xa0, 0x90, // 90..172.5deg
|
||||
0x80, 0x6f, 0x5f, 0x4f, 0x40, 0x32, 0x25, 0x1a, 0x11, 0x0a, 0x04, 0x01, // 180..262.5deg
|
||||
0x00, 0x01, 0x04, 0x0a, 0x11, 0x1a, 0x25, 0x32, 0x40, 0x4f, 0x5f, 0x6f // 270..352.5deg
|
||||
};
|
||||
|
||||
HdlcAfskTx::HdlcAfskTx(HdlcFrameBuffer* txBuffer) {
|
||||
_txBuffer = txBuffer;
|
||||
for (int idx=0; idx < AFSK_PIN_COUNT; idx++) {
|
||||
pinMode(_pins[idx], OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
byte HdlcAfskTx::encodeSample() {
|
||||
if (_ready) { return ANALOG_ZERO; }
|
||||
|
||||
byte loopBackSample = ANALOG_ZERO;
|
||||
if (loopbackTest) {
|
||||
PORTD = (pgm_read_byte(&sinusTable[_n7_5degTest]) & AFSK_MASK) | (PORTD & NON_AFSK_MASK);
|
||||
_n7_5degTest += MARK_SAMPLE_DEG_STEP;
|
||||
if (_n7_5degTest >= SIN_TABLE_SIZE) {
|
||||
_n7_5degTest -= SIN_TABLE_SIZE;
|
||||
}
|
||||
loopBackSample = pgm_read_byte(&sinusTable[_n7_5deg]);
|
||||
}
|
||||
else {
|
||||
PORTD = (pgm_read_byte(&sinusTable[_n7_5deg]) & AFSK_MASK) | (PORTD & NON_AFSK_MASK);
|
||||
}
|
||||
|
||||
if (_mark) {
|
||||
_n7_5deg += MARK_SAMPLE_DEG_STEP;
|
||||
}
|
||||
else {
|
||||
_n7_5deg += SPACE_SAMPLE_DEG_STEP;
|
||||
}
|
||||
if (_n7_5deg >= SIN_TABLE_SIZE) {
|
||||
_n7_5deg -= SIN_TABLE_SIZE;
|
||||
}
|
||||
|
||||
if (_runToZero) {
|
||||
if (_n7_5deg < SPACE_SAMPLE_DEG_STEP) {
|
||||
_n7_5deg = 0;
|
||||
_runToZero = false;
|
||||
_ready = true;
|
||||
PORTD = ANALOG_ZERO;
|
||||
loopBackSample = ANALOG_ZERO;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_bitSampleCounter++;
|
||||
if (_bitSampleCounter > MAX_BIT_SAMPLE_INDEX) {
|
||||
_bitSampleCounter = 0;
|
||||
if (getNextMark(&_mark)) {
|
||||
_runToZero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return loopBackSample;
|
||||
}
|
||||
|
||||
void HdlcAfskTx::start() {
|
||||
if (_ready) {
|
||||
_mark = true;
|
||||
_afskByte = HDLC_FLAG;
|
||||
_afskBitMask = MASK_RESET_VALUE;
|
||||
_afskBitPtr = 0;
|
||||
_preampCountDown = _preambleCount;
|
||||
_enableStuffBit = false;
|
||||
_stuffBit = false;
|
||||
_oneCounter = 0;
|
||||
_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean HdlcAfskTx::getReady() {
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void HdlcAfskTx::setTxDelay(int val) {
|
||||
_preambleCount = val * 12 / 80;
|
||||
}
|
||||
|
||||
boolean HdlcAfskTx::getNextMark(boolean* current) {
|
||||
if (_afskBitPtr == 0) {
|
||||
boolean frameEnd=false;
|
||||
switch (frameTxState) {
|
||||
case TX_FRAME_PREAMBLE:
|
||||
_afskByte = HDLC_FLAG;
|
||||
_enableStuffBit = false;
|
||||
_preampCountDown--;
|
||||
if (_preampCountDown < 1) {
|
||||
frameTxState = TX_FRAME_BEGIN;
|
||||
}
|
||||
break;
|
||||
case TX_FRAME_BEGIN:
|
||||
if (_txBuffer->more()) {
|
||||
_afskByte = HDLC_FLAG;
|
||||
_enableStuffBit = false;
|
||||
frameTxState = TX_FRAME_DATA;
|
||||
}
|
||||
else {
|
||||
frameTxState = TX_FRAME_PREAMBLE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TX_FRAME_DATA:
|
||||
if (_txBuffer->pop(&_afskByte, &frameEnd)) {
|
||||
_enableStuffBit = true;
|
||||
if (frameEnd) {
|
||||
frameTxState = TX_FRAME_END;
|
||||
}
|
||||
}
|
||||
else {
|
||||
frameTxState = TX_FRAME_END;
|
||||
}
|
||||
break;
|
||||
case TX_FRAME_END:
|
||||
_afskByte = HDLC_FLAG;
|
||||
_enableStuffBit = false;
|
||||
frameTxState = TX_FRAME_BEGIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean nextBit;
|
||||
if (_stuffBit) {
|
||||
nextBit = false;
|
||||
_stuffBit = false;
|
||||
}
|
||||
else {
|
||||
nextBit = (_afskByte & _afskBitMask) != 0;
|
||||
if (nextBit) {
|
||||
_oneCounter++;
|
||||
if (_oneCounter > MAX_ONES_WITHOUT_STUFFING) {
|
||||
_oneCounter = 0;
|
||||
_stuffBit = _enableStuffBit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_oneCounter = 0;
|
||||
}
|
||||
_afskBitPtr++;
|
||||
_afskBitMask <<= 1;
|
||||
if (_afskBitPtr > MAX_BIT_INDEX) {
|
||||
_afskBitPtr = 0;
|
||||
_afskBitMask = MASK_RESET_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nextBit) {
|
||||
*current = !*current;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef HDLC_AFSK_TX_H
|
||||
#define HDLC_AFSK_TX_H
|
||||
|
||||
#include "hdlcFramesBuffer.h"
|
||||
|
||||
#define AFSK_PIN_COUNT 4
|
||||
#define AFSK_PIN_4 4
|
||||
#define AFSK_PIN_5 5
|
||||
#define AFSK_PIN_6 6
|
||||
#define AFSK_PIN_7 7
|
||||
#define AFSK_MASK 0xf0
|
||||
#define NON_AFSK_MASK 0x0f
|
||||
|
||||
#define SIN_TABLE_SIZE 48
|
||||
#define MARK_SAMPLE_DEG_STEP 6
|
||||
#define SPACE_SAMPLE_DEG_STEP 11
|
||||
#define MAX_BIT_SAMPLE_INDEX 7
|
||||
|
||||
#define ANALOG_ZERO 0x80
|
||||
#define MASK_RESET_VALUE 0x01
|
||||
#define MAX_BIT_INDEX 7
|
||||
|
||||
#define TX_FRAME_PREAMBLE 0
|
||||
#define TX_FRAME_BEGIN 1
|
||||
#define TX_FRAME_DATA 2
|
||||
#define TX_FRAME_END 3
|
||||
|
||||
#define MAX_ONES_WITHOUT_STUFFING 4
|
||||
#define DEFAULT_PREAMBLE_COUNT 75
|
||||
|
||||
class HdlcAfskTx {
|
||||
public:
|
||||
HdlcAfskTx(HdlcFrameBuffer* txBuffer);
|
||||
boolean loopbackTest=false;
|
||||
byte encodeSample();
|
||||
void start();
|
||||
boolean getReady();
|
||||
void setTxDelay(int val);
|
||||
private:
|
||||
HdlcFrameBuffer* _txBuffer;
|
||||
int _pins[AFSK_PIN_COUNT] = {AFSK_PIN_7, AFSK_PIN_6, AFSK_PIN_5, AFSK_PIN_4};
|
||||
int _n7_5deg = 0;
|
||||
int _n7_5degTest = 0;
|
||||
int _bitSampleCounter = 0;
|
||||
boolean _mark = true;
|
||||
boolean _runToZero = false;
|
||||
boolean _ready = true;
|
||||
byte _afskByte = HDLC_FLAG;
|
||||
byte _afskBitMask = MASK_RESET_VALUE;
|
||||
int frameTxState = TX_FRAME_PREAMBLE;
|
||||
int _afskBitPtr = 0;
|
||||
int _preambleCount = DEFAULT_PREAMBLE_COUNT;
|
||||
int _preampCountDown = DEFAULT_PREAMBLE_COUNT;
|
||||
boolean _enableStuffBit = false;
|
||||
boolean _stuffBit = false;
|
||||
int _oneCounter = 0;
|
||||
boolean getNextMark(boolean* current);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "hdlcCrc.h"
|
||||
|
||||
HdlcCrc::HdlcCrc(HdlcCrcTable* crcTable) {
|
||||
_crcTable = crcTable;
|
||||
}
|
||||
|
||||
void HdlcCrc::resetFcs() {
|
||||
_fcs = CRC_RESET_VAL;
|
||||
_calcFcs[0] = _calcFcs[1];
|
||||
_calcFcs[1] = _fcs;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
void HdlcCrc::calcFcs(byte newByte) {
|
||||
_fcs = (_fcs >> 8) ^ _crcTable->getValue((_fcs ^ newByte) & 0xff);
|
||||
_rxFcs[0] = _rxFcs[1];
|
||||
_rxFcs[1] = newByte;
|
||||
_calcFcs[0] = _calcFcs[1];
|
||||
_calcFcs[1] = _calcFcs[2];
|
||||
_calcFcs[2] = _fcs;
|
||||
_count++;
|
||||
}
|
||||
|
||||
byte HdlcCrc::getFcsL() {
|
||||
return (_fcs & 0xff) ^ 0xff;
|
||||
}
|
||||
|
||||
byte HdlcCrc::getFcsH() {
|
||||
return (_fcs >> 8) ^ 0xff;
|
||||
}
|
||||
|
||||
boolean HdlcCrc::checkFcs() {
|
||||
boolean result = false;
|
||||
byte calcFcsL = (_calcFcs[0] & 0xff) ^ 0xff;
|
||||
byte calcFcsH = (_calcFcs[0] >> 8) ^ 0xff;
|
||||
|
||||
if (_count > 2) {
|
||||
if (_rxFcs[0] == calcFcsL) {
|
||||
if (_rxFcs[1] == calcFcsH) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef HDLC_CRC_H
|
||||
#define HDLC_CRC_H
|
||||
|
||||
#include "hdlcCrcTable.h"
|
||||
|
||||
#define CRC_RESET_VAL 0xFFFF
|
||||
|
||||
class HdlcCrc {
|
||||
public:
|
||||
HdlcCrc(HdlcCrcTable* crcTable);
|
||||
void resetFcs();
|
||||
void calcFcs(byte newByte);
|
||||
byte getFcsL();
|
||||
byte getFcsH();
|
||||
boolean checkFcs();
|
||||
private:
|
||||
HdlcCrcTable* _crcTable;
|
||||
uint16_t _fcs = CRC_RESET_VAL;
|
||||
uint16_t _calcFcs[3];
|
||||
byte _rxFcs[2];
|
||||
int _count = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "hdlcCrcTable.h"
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
const uint16_t crcTable[256] PROGMEM = {
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||||
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
||||
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
||||
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||||
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
||||
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
||||
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
||||
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
||||
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
||||
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
||||
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
||||
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
||||
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||||
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
||||
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
||||
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
||||
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
||||
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
||||
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
||||
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
||||
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
||||
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||||
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
||||
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
||||
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
||||
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
||||
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
||||
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
||||
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
||||
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
||||
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||||
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
|
||||
};
|
||||
|
||||
HdlcCrcTable::HdlcCrcTable() {
|
||||
}
|
||||
|
||||
uint16_t HdlcCrcTable::getValue(byte crcTableIndex) {
|
||||
return pgm_read_word(&crcTable[crcTableIndex]);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef HDLC_CRC_TABLE_H
|
||||
#define HDLC_CRC_TABLE_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#define CRC_RESET_VAL 0xFFFF
|
||||
|
||||
class HdlcCrcTable {
|
||||
public:
|
||||
HdlcCrcTable();
|
||||
uint16_t getValue(byte crcTableIndex);
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
#include "hdlcFramesBuffer.h"
|
||||
|
||||
HdlcFrameBuffer::HdlcFrameBuffer(boolean tx, HdlcCrcTable* crcTable, int bufferSize) {
|
||||
_tx = tx;
|
||||
_crc = new HdlcCrc(crcTable);
|
||||
_bufferData = new byte[bufferSize+6];
|
||||
_bufferData[0] = 0;
|
||||
_bufferData[1] = 0;
|
||||
}
|
||||
|
||||
boolean HdlcFrameBuffer::push(byte in) {
|
||||
if (spaceAvailable()) {
|
||||
_bufferData[_pushPosition] = in;
|
||||
incrementPushPosition();
|
||||
_pushFrameDataCounter++;
|
||||
_crc->calcFcs(in);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean HdlcFrameBuffer::pop(byte* out, boolean* frameEnd) {
|
||||
*frameEnd = false;
|
||||
if (more()) {
|
||||
*out = _bufferData[_popPosition];
|
||||
incrementPopPosition(1);
|
||||
_popFrameSize--;
|
||||
if (_popFrameSize < 1) {
|
||||
*frameEnd = true;
|
||||
if (!_tx) {
|
||||
incrementPopPosition(2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean HdlcFrameBuffer::more() {
|
||||
if (_popFrameSize < 1) {
|
||||
_popFrameSize = (_bufferData[_popFrameSizeHPos] << 8) + _bufferData[_popFrameSizeLPos];
|
||||
if (_popFrameSize > 0) {
|
||||
_bufferData[_popFrameSizeHPos] = 0;
|
||||
_bufferData[_popFrameSizeLPos] = 0;
|
||||
_popFrameSizeHPos += _popFrameSize + 2;
|
||||
_popFrameSizeLPos += _popFrameSize + 2;
|
||||
if (_popFrameSizeHPos >= BUFFER_SIZE) {
|
||||
_popFrameSizeHPos -= BUFFER_SIZE;
|
||||
}
|
||||
if (_popFrameSizeLPos >= BUFFER_SIZE) {
|
||||
_popFrameSizeLPos -= BUFFER_SIZE;
|
||||
}
|
||||
incrementPopPosition(2);
|
||||
if (!_tx) {
|
||||
_popFrameSize -=2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (_popFrameSize > 0);
|
||||
}
|
||||
|
||||
void HdlcFrameBuffer::frameReset(){
|
||||
_pushPosition = _pushFrameSizeLPos;
|
||||
incrementPushPosition();
|
||||
_pushFrameDataCounter = 0;
|
||||
_crc->resetFcs();
|
||||
}
|
||||
|
||||
void HdlcFrameBuffer::frameEnd() {
|
||||
if (spaceAvailable()) {
|
||||
if (_tx) {
|
||||
byte fcsL = _crc->getFcsL();
|
||||
byte fcsH = _crc->getFcsH();
|
||||
push(fcsL);
|
||||
push(fcsH);
|
||||
|
||||
startNewFrame();
|
||||
}
|
||||
else {
|
||||
if(_crc->checkFcs()) {
|
||||
startNewFrame();
|
||||
}
|
||||
else {
|
||||
frameReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
frameReset();
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcFrameBuffer::startNewFrame() {
|
||||
_bufferData[_pushFrameSizeHPos] = (_pushFrameDataCounter >> 8) & 0xff;
|
||||
_bufferData[_pushFrameSizeLPos] = (_pushFrameDataCounter & 0xff);
|
||||
_pushFrameDataCounter = 0;
|
||||
|
||||
_pushFrameSizeHPos = _pushPosition;
|
||||
incrementPushPosition();
|
||||
|
||||
_pushFrameSizeLPos = _pushPosition;
|
||||
incrementPushPosition();
|
||||
|
||||
_bufferData[_pushFrameSizeHPos] = 0;
|
||||
_bufferData[_pushFrameSizeLPos] = 0;
|
||||
_crc->resetFcs();
|
||||
}
|
||||
|
||||
void HdlcFrameBuffer::incrementPushPosition() {
|
||||
_pushPosition++;
|
||||
if (_pushPosition >= BUFFER_SIZE) {
|
||||
_pushPosition=0;
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcFrameBuffer::incrementPopPosition(int count) {
|
||||
_popPosition += count;
|
||||
if (_popPosition >= BUFFER_SIZE) {
|
||||
_popPosition -= BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
boolean HdlcFrameBuffer::spaceAvailable() {
|
||||
int space = _popPosition - _pushPosition;
|
||||
if (space <= 0) {
|
||||
space += BUFFER_SIZE;
|
||||
}
|
||||
return space>4;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef HDLC_FRAMES_BUFFER_H
|
||||
#define HDLC_FRAMES_BUFFER_H
|
||||
|
||||
#include "hdlcCrc.h"
|
||||
|
||||
#define HDLC_FLAG 0x7e
|
||||
|
||||
#define BUFFER_SIZE 500
|
||||
|
||||
class HdlcFrameBuffer {
|
||||
public:
|
||||
HdlcFrameBuffer(boolean tx, HdlcCrcTable* crcTable, int bufferSize);
|
||||
boolean push(byte in);
|
||||
boolean pop(byte* out, boolean* frameEnd);
|
||||
boolean more();
|
||||
void frameReset();
|
||||
void frameEnd();
|
||||
private:
|
||||
HdlcCrc* _crc;
|
||||
byte* _bufferData;
|
||||
boolean _tx;
|
||||
int _pushFrameSizeHPos = 0;
|
||||
int _pushFrameSizeLPos = 1;
|
||||
int _pushPosition = 2;
|
||||
int _pushFrameDataCounter = 0;
|
||||
int _popFrameSizeHPos = 0;
|
||||
int _popFrameSizeLPos = 1;
|
||||
int _popPosition = 0;
|
||||
int _popFrameSize = 0;
|
||||
void startNewFrame();
|
||||
void incrementPushPosition();
|
||||
void incrementPopPosition(int count);
|
||||
boolean spaceAvailable();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "hdlcTnc.h"
|
||||
|
||||
HdlcTnc::HdlcTnc(
|
||||
int rxIndicationPin, int txIndicationPin, int rxOnlyPin, int pttPin,
|
||||
int lowLevelSelectPin, int rxBufferSize, int txBufferSize
|
||||
) {
|
||||
_crcTable = new HdlcCrcTable();
|
||||
_rxBuffer = new HdlcFrameBuffer(false, _crcTable, rxBufferSize);
|
||||
_txBuffer = new HdlcFrameBuffer(true , _crcTable, txBufferSize);
|
||||
_afskRx = new HdlcAfskRx(_rxBuffer);
|
||||
_afskTx = new HdlcAfskTx(_txBuffer);
|
||||
_afskSampler = new AfskSampler(lowLevelSelectPin, _afskRx, _afskTx);
|
||||
if (rxIndicationPin > 0 ) {
|
||||
pinMode(rxIndicationPin, OUTPUT);
|
||||
_rxIndicationPin = rxIndicationPin;
|
||||
}
|
||||
if (txIndicationPin > 0 ) {
|
||||
pinMode(txIndicationPin, OUTPUT);
|
||||
_txIndicationPin = txIndicationPin;
|
||||
}
|
||||
if (rxOnlyPin > 0 ) {
|
||||
pinMode(rxOnlyPin, INPUT_PULLUP);
|
||||
_rxOnlyPin = rxOnlyPin;
|
||||
}
|
||||
if (pttPin > 0 ) {
|
||||
pinMode(pttPin, OUTPUT);
|
||||
_pttPin = pttPin;
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcTnc::hdlcTask() {
|
||||
if (_afskRx->receiving) {
|
||||
digitalWrite(_rxIndicationPin, HIGH);
|
||||
}
|
||||
else {
|
||||
digitalWrite(_rxIndicationPin, LOW);
|
||||
}
|
||||
|
||||
switch(_hdlcState) {
|
||||
case HS_IDLE:
|
||||
if (_txBuffer->more()) {
|
||||
if (_fullDuplex) {
|
||||
setPtt(true);
|
||||
_afskTx->start();
|
||||
_hdlcState = HS_TX_FRAME;
|
||||
}
|
||||
else {
|
||||
if (_afskRx->receiving) {
|
||||
_hdlcState = HS_WAIT_FOR_FREE_CHANNEL;
|
||||
}
|
||||
else {
|
||||
setPtt(true);
|
||||
_afskTx->start();
|
||||
_hdlcState = HS_TX_FRAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HS_WAIT_FOR_FREE_CHANNEL:
|
||||
if (!_afskRx->receiving) {
|
||||
if (random(RANDOM_VAL_LIMIT) > _persistence) {
|
||||
_randomWaitTimeOutTime = millis() + _slotTime;
|
||||
_hdlcState = HS_RANDOM_WAIT;
|
||||
}
|
||||
else {
|
||||
setPtt(true);
|
||||
_afskTx->start();
|
||||
_hdlcState = HS_TX_FRAME;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HS_RANDOM_WAIT:
|
||||
if (_afskRx->receiving) {
|
||||
_hdlcState = HS_WAIT_FOR_FREE_CHANNEL;
|
||||
}
|
||||
else {
|
||||
if (millis() > _randomWaitTimeOutTime) {
|
||||
setPtt(true);
|
||||
_afskTx->start();
|
||||
_hdlcState = HS_TX_FRAME;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HS_TX_FRAME:
|
||||
if (_afskTx->getReady()) {
|
||||
setPtt(false);
|
||||
_hdlcState = HS_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcTnc::addByteToTxBuf(byte newByte) {
|
||||
if (digitalRead(_rxOnlyPin) == HIGH) {
|
||||
_txBuffer->push(newByte);
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcTnc::addFrameEndToTxBuf() {
|
||||
if (digitalRead(_rxOnlyPin) == HIGH) {
|
||||
_txBuffer->frameEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcTnc::resetTxFrame() {
|
||||
_txBuffer->frameReset();
|
||||
}
|
||||
|
||||
boolean HdlcTnc::getRxByte(byte* rxByte, boolean* frameEnd) {
|
||||
return _rxBuffer->pop(rxByte, frameEnd);
|
||||
}
|
||||
|
||||
void HdlcTnc::setTxDelay(int val) {
|
||||
_afskTx->setTxDelay(val);
|
||||
}
|
||||
|
||||
void HdlcTnc::setPersistence(byte val) {
|
||||
_persistence = val;
|
||||
}
|
||||
|
||||
void HdlcTnc::setSlotTime(int val) {
|
||||
_slotTime = val;
|
||||
}
|
||||
|
||||
void HdlcTnc::setFullDuplex(boolean val) {
|
||||
_fullDuplex = val;
|
||||
}
|
||||
|
||||
void HdlcTnc::setLoopbackTest(boolean on) {
|
||||
_afskTx->loopbackTest = on;
|
||||
}
|
||||
|
||||
void HdlcTnc::setPtt(boolean on) {
|
||||
if (on) {
|
||||
digitalWrite(_pttPin, HIGH);
|
||||
digitalWrite(_txIndicationPin, HIGH);
|
||||
}
|
||||
else {
|
||||
digitalWrite(_pttPin, LOW);
|
||||
digitalWrite(_txIndicationPin, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef HDLC_TNC_H
|
||||
#define HDLC_TNC_H
|
||||
|
||||
#include "hdlcAfskRx.h"
|
||||
#include "hdlcAfskTx.h"
|
||||
#include "afskSampler.h"
|
||||
|
||||
#define DEFAULT_PERSISTENCE 63
|
||||
#define DEFAULT_SLOT_TIME 100
|
||||
|
||||
#define RANDOM_VAL_LIMIT 256
|
||||
|
||||
#define HS_IDLE 0
|
||||
#define HS_WAIT_FOR_FREE_CHANNEL 1
|
||||
#define HS_RANDOM_WAIT 2
|
||||
#define HS_TX_FRAME 3
|
||||
|
||||
class HdlcTnc {
|
||||
public:
|
||||
HdlcTnc(
|
||||
int rxIndicationPin, int txIndicationPin, int rxOnlyPin, int pttPin,
|
||||
int lowLevelSelectPin, int rxBufferSize, int txBufferSize
|
||||
);
|
||||
void hdlcTask();
|
||||
void addByteToTxBuf(byte newByte);
|
||||
void addFrameEndToTxBuf();
|
||||
void resetTxFrame();
|
||||
boolean getRxByte(byte* rxByte, boolean* frameEnd);
|
||||
void setTxDelay(int val);
|
||||
void setPersistence(byte val);
|
||||
void setSlotTime(int val);
|
||||
void setFullDuplex(boolean val);
|
||||
void setLoopbackTest(boolean On);
|
||||
private:
|
||||
HdlcCrcTable* _crcTable;
|
||||
HdlcFrameBuffer* _rxBuffer;
|
||||
HdlcFrameBuffer* _txBuffer;
|
||||
HdlcAfskRx* _afskRx;
|
||||
HdlcAfskTx* _afskTx;
|
||||
AfskSampler* _afskSampler;
|
||||
int _rxIndicationPin = 0;
|
||||
int _txIndicationPin = 0;
|
||||
int _pttPin = 0;
|
||||
int _rxOnlyPin = 0;
|
||||
byte _persistence = DEFAULT_PERSISTENCE;
|
||||
int _slotTime = DEFAULT_SLOT_TIME;
|
||||
boolean _fullDuplex = false;
|
||||
int _hdlcState = HS_IDLE;
|
||||
unsigned long _randomWaitTimeOutTime = 0;
|
||||
void setPtt(boolean on);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "kiss.h"
|
||||
|
||||
Kiss::Kiss(
|
||||
int baud,
|
||||
int rxIndicationPin, int txIndicationPin, int rxOnlyPin, int pttPin,
|
||||
int lowLevelSelectPin, int rxBufferSize, int txBufferSize
|
||||
) {
|
||||
_baud = baud;
|
||||
_hdlcTnc = new HdlcTnc(
|
||||
rxIndicationPin, txIndicationPin, rxOnlyPin, pttPin, lowLevelSelectPin,
|
||||
rxBufferSize, txBufferSize
|
||||
);
|
||||
}
|
||||
|
||||
void Kiss::begin() {
|
||||
Serial.begin(_baud);
|
||||
}
|
||||
|
||||
void Kiss::kissTask() {
|
||||
_hdlcTnc->hdlcTask();
|
||||
if (Serial.available() > 0) {
|
||||
processKissInByte(Serial.read());
|
||||
}
|
||||
|
||||
boolean frameEnd;
|
||||
byte rxByte;
|
||||
if (_hdlcTnc->getRxByte(&rxByte, &frameEnd)) {
|
||||
processKissOutByte(rxByte, frameEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void Kiss::processKissOutByte(byte newByte, boolean frameEnd) {
|
||||
if (_kissOutFrameEnd) {
|
||||
Serial.write(FEND);
|
||||
Serial.write(CMD_DATA_FRAME);
|
||||
_kissOutFrameEnd = false;
|
||||
}
|
||||
if (newByte == FEND) {
|
||||
Serial.write(FESC);
|
||||
Serial.write(TFEND);
|
||||
}
|
||||
else if (newByte == FESC) {
|
||||
Serial.write(FESC);
|
||||
Serial.write(TFESC);
|
||||
}
|
||||
else {
|
||||
Serial.write(newByte);
|
||||
}
|
||||
if (frameEnd) {
|
||||
Serial.write(FEND);
|
||||
_kissOutFrameEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Kiss::processKissInByte(byte newByte) {
|
||||
switch (_kissInState) {
|
||||
case KS_UNDEF:
|
||||
if (newByte == FEND) {
|
||||
_kissCommand = CMD_UNDEF;
|
||||
_kissInState = KS_GET_CMD;
|
||||
}
|
||||
break;
|
||||
case KS_GET_CMD:
|
||||
if (newByte != FEND) {
|
||||
if ((newByte < MAX_KISS_CMD) || (newByte > MIN_KISS_CMD)) {
|
||||
_kissCommand = newByte;
|
||||
_kissInState = KS_GET_DATA;
|
||||
}
|
||||
else {
|
||||
_kissCommand = CMD_UNDEF;
|
||||
_kissInState = KS_UNDEF;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KS_GET_DATA:
|
||||
if (newByte == FESC) {
|
||||
_kissInState = KS_ESCAPE;
|
||||
}
|
||||
else if (newByte == FEND) {
|
||||
sendCommandToTnc();
|
||||
_kissCommand = CMD_UNDEF;
|
||||
_kissInState = KS_GET_CMD;
|
||||
}
|
||||
else {
|
||||
storeDataByte(newByte);
|
||||
}
|
||||
break;
|
||||
case KS_ESCAPE:
|
||||
if (newByte == TFEND) {
|
||||
storeDataByte(FEND);
|
||||
_kissInState = KS_GET_DATA;
|
||||
}
|
||||
else if (newByte == TFESC) {
|
||||
storeDataByte(FESC);
|
||||
_kissInState = KS_GET_DATA;
|
||||
}
|
||||
else {
|
||||
_hdlcTnc->resetTxFrame();
|
||||
_kissCommand = CMD_UNDEF;
|
||||
_kissInState = KS_UNDEF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Kiss::storeDataByte(byte newByte) {
|
||||
if (_kissCommand == CMD_DATA_FRAME) {
|
||||
_hdlcTnc->addByteToTxBuf(newByte);
|
||||
}
|
||||
else {
|
||||
_tncParam = newByte;
|
||||
}
|
||||
}
|
||||
|
||||
void Kiss::sendCommandToTnc() {
|
||||
switch (_kissCommand) {
|
||||
case CMD_DATA_FRAME:
|
||||
_hdlcTnc->addFrameEndToTxBuf();
|
||||
break;
|
||||
case CMD_TX_DELAY:
|
||||
_hdlcTnc->setTxDelay(_tncParam * TIME_10_MS_UNITS);
|
||||
break;
|
||||
case CMD_P:
|
||||
_hdlcTnc->setPersistence(_tncParam);
|
||||
break;
|
||||
case CMD_SLOT_TIME:
|
||||
_hdlcTnc->setSlotTime(_tncParam * TIME_10_MS_UNITS);
|
||||
break;
|
||||
case CMD_TX_TAIL:
|
||||
//obsolete
|
||||
break;
|
||||
case CMD_FULL_DUPLEX:
|
||||
_hdlcTnc->setFullDuplex(_tncParam != 0);
|
||||
break;
|
||||
case CMD_SET_HARDWARE:
|
||||
//not implemented
|
||||
break;
|
||||
case CMD_LOOPBACK_TEST:
|
||||
_hdlcTnc->setLoopbackTest(_tncParam > 0);
|
||||
break;
|
||||
case CMD_RETURN:
|
||||
//not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef KISS_H
|
||||
#define KISS_H
|
||||
|
||||
#include "hdlcTnc.h"
|
||||
|
||||
#define FEND 0xC0
|
||||
#define FESC 0xDB
|
||||
#define TFEND 0xDC
|
||||
#define TFESC 0xDD
|
||||
|
||||
#define KS_UNDEF 0
|
||||
#define KS_GET_CMD 1
|
||||
#define KS_GET_DATA 2
|
||||
#define KS_ESCAPE 3
|
||||
|
||||
#define TIME_10_MS_UNITS 10
|
||||
|
||||
#define CMD_DATA_FRAME 0x00
|
||||
#define CMD_TX_DELAY 0x01
|
||||
#define CMD_P 0x02
|
||||
#define CMD_SLOT_TIME 0x03
|
||||
#define CMD_TX_TAIL 0x04
|
||||
#define CMD_FULL_DUPLEX 0x05
|
||||
#define CMD_SET_HARDWARE 0x06
|
||||
#define MAX_KISS_CMD 0x07
|
||||
#define CMD_UNDEF 0x80
|
||||
#define MIN_KISS_CMD 0xFD
|
||||
#define CMD_LOOPBACK_TEST 0xFE
|
||||
#define CMD_RETURN 0xFF
|
||||
|
||||
#define LCD_ROW_COUNT 4
|
||||
#define LCD_COL_COUNT 20
|
||||
#define EMPTY_ROW " "
|
||||
|
||||
class Kiss {
|
||||
public:
|
||||
Kiss(
|
||||
int bdRate,
|
||||
int rxIndicationPin, int txIndicationPin, int rxOnlyPin, int pttPin, int lowLevelSelectPin,
|
||||
int rxBufferSize, int txBufferSize
|
||||
);
|
||||
void begin();
|
||||
void kissTask();
|
||||
private:
|
||||
HdlcTnc* _hdlcTnc;
|
||||
int _baud;
|
||||
int _kissInState = KS_UNDEF;
|
||||
byte _kissCommand = CMD_UNDEF;
|
||||
byte _tncParam;
|
||||
boolean _kissOutFrameEnd = true;
|
||||
boolean _readAddress = true;
|
||||
void processKissOutByte(byte newByte, boolean frameEnd);
|
||||
void processKissInByte(byte newByte);
|
||||
void storeDataByte(byte newByte);
|
||||
void sendCommandToTnc();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "kiss.h"
|
||||
|
||||
#define KISS_BD 9600
|
||||
#define RX_LED 3 // 13 for PI4RAZ APRS_iGate
|
||||
#define TX_LED 9
|
||||
#define RX_ONLY 8 // 10 for PI4RAZ APRS_iGate (not used)
|
||||
#define PTT_PIN 10
|
||||
#define LOW_LEVEL_SELECT 2
|
||||
#define RX_BUFFER_SIZE 500
|
||||
#define TX_BUFFER_SIZE 500
|
||||
|
||||
Kiss kiss(
|
||||
KISS_BD,
|
||||
RX_LED, TX_LED, RX_ONLY, PTT_PIN, LOW_LEVEL_SELECT,
|
||||
RX_BUFFER_SIZE, TX_BUFFER_SIZE
|
||||
);
|
||||
|
||||
void setup() {
|
||||
kiss.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
kiss.kissTask();
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user