Commenting
This commit is contained in:
parent
0c4bc75be9
commit
017a1e6887
190
Modem/afsk.c
190
Modem/afsk.c
|
@ -1,17 +1,31 @@
|
||||||
#include "afsk.h"
|
//////////////////////////////////////////////////////
|
||||||
#include "config.h"
|
// First things first, all the includes we need //
|
||||||
#include "hardware.h"
|
//////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include <drv/timer.h>
|
#include "afsk.h" // We need the header file for the modem
|
||||||
#include <cfg/module.h>
|
#include "config.h" // This stores basic configuration
|
||||||
|
#include "hardware.h" // Hardware functions are nice to have too :)
|
||||||
|
|
||||||
#include <cpu/power.h>
|
#include <drv/timer.h> // Timer driver from BertOS
|
||||||
#include <cpu/pgm.h>
|
//FIXME: is this needed ? #include <cfg/module.h>
|
||||||
#include <struct/fifobuf.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// Sine table for DAC DDS
|
#include <cpu/power.h> // Power management from BertOS
|
||||||
#define SIN_LEN 512 // Length of a full wave. Table is 1/4 wave.
|
#include <cpu/pgm.h> // Access to PROGMEM from BertOS
|
||||||
|
#include <struct/fifobuf.h> // FIFO buffer implementation from BertOS
|
||||||
|
#include <string.h> // String operations, primarily used for memset function
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
// Definitions and some useful macros //
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Sine table for Direct Digital Synthesis DAC
|
||||||
|
// Since it would be inefficient to calculate a sine value each
|
||||||
|
// time we process a sample, we store the values in program memory
|
||||||
|
// as a look-up table. We only need to store values for a quarter
|
||||||
|
// wave, since we can easily reconstruct the entire 512 values
|
||||||
|
// from only these 128 values.
|
||||||
|
#define SIN_LEN 512
|
||||||
static const uint8_t PROGMEM sin_table[] =
|
static const uint8_t PROGMEM sin_table[] =
|
||||||
{
|
{
|
||||||
128, 129, 131, 132, 134, 135, 137, 138, 140, 142, 143, 145, 146, 148, 149, 151,
|
128, 129, 131, 132, 134, 135, 137, 138, 140, 142, 143, 145, 146, 148, 149, 151,
|
||||||
|
@ -26,17 +40,44 @@ static const uint8_t PROGMEM sin_table[] =
|
||||||
|
|
||||||
|
|
||||||
// Calculate any sine value from quarter wave sine table
|
// Calculate any sine value from quarter wave sine table
|
||||||
|
// The reason we declare this inline is to eliminate an extra
|
||||||
|
// call for the code. The code is essentially inserted directly
|
||||||
|
// in the calling functions code. This makes stuff faster :)
|
||||||
INLINE uint8_t sinSample(uint16_t i) {
|
INLINE uint8_t sinSample(uint16_t i) {
|
||||||
|
// Make sure that the index asked for is in the correct range
|
||||||
ASSERT(i < SIN_LEN);
|
ASSERT(i < SIN_LEN);
|
||||||
|
// First we make a new index value, and restrict it to only
|
||||||
|
// the first half-wave of the sine.
|
||||||
uint16_t newI = i % (SIN_LEN/2);
|
uint16_t newI = i % (SIN_LEN/2);
|
||||||
|
// We then check if this new index is larger than the first
|
||||||
|
// quarter wave. If it is, we don't have the value for this
|
||||||
|
// index directly, but we can figure it out by subtracting
|
||||||
|
// the new index from a half wave, effectively wrapping us
|
||||||
|
// back into the same place on the wave, whithin the quarter
|
||||||
|
// wave we have data for, only with the inverse sign. If the
|
||||||
|
// index was actually in the first quarter, we don't need to
|
||||||
|
// do anything.
|
||||||
newI = (newI >= (SIN_LEN/4)) ? (SIN_LEN/2 - newI -1) : newI;
|
newI = (newI >= (SIN_LEN/4)) ? (SIN_LEN/2 - newI -1) : newI;
|
||||||
|
// Now we just need to read the value from program memory
|
||||||
uint8_t sine = pgm_read8(&sin_table[newI]);
|
uint8_t sine = pgm_read8(&sin_table[newI]);
|
||||||
|
// And flip the sign (+/-) if the original index was greater
|
||||||
|
// than a half wave.
|
||||||
return (i >= (SIN_LEN/2)) ? (255 - sine) : sine;
|
return (i >= (SIN_LEN/2)) ? (255 - sine) : sine;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for signal transition. Used for phase sync.
|
// A very basic macro that just checks whether the last bit
|
||||||
|
// of a whatever is passed into it differ. This is used in the
|
||||||
|
// next macro.
|
||||||
#define BITS_DIFFER(bits1, bits2) (((bits1)^(bits2)) & 0x01)
|
#define BITS_DIFFER(bits1, bits2) (((bits1)^(bits2)) & 0x01)
|
||||||
#define EDGE_FOUND(bits) BITS_DIFFER((bits), (bits) >> 1)
|
|
||||||
|
// This macro is used to look for signal transitions. We need
|
||||||
|
// to identify these to keep the phase of our demodulator in
|
||||||
|
// sync with the incoming signal. Each time we find a signal
|
||||||
|
// transition on the physical medium, we adjust the phase of
|
||||||
|
// the demodulator.
|
||||||
|
// The macro effectively looks at the two least significant
|
||||||
|
// bits in a stream and returns true if they differ.
|
||||||
|
#define TRANSITION_FOUND(bits) BITS_DIFFER((bits), (bits) >> 1)
|
||||||
|
|
||||||
// Phase sync constants
|
// Phase sync constants
|
||||||
#define PHASE_BITS 8
|
#define PHASE_BITS 8
|
||||||
|
@ -51,35 +92,79 @@ INLINE uint8_t sinSample(uint16_t i) {
|
||||||
#define SPACE_INC (uint16_t)(DIV_ROUND(SIN_LEN * (uint32_t)SPACE_FREQ, CONFIG_AFSK_DAC_SAMPLERATE))
|
#define SPACE_INC (uint16_t)(DIV_ROUND(SIN_LEN * (uint32_t)SPACE_FREQ, CONFIG_AFSK_DAC_SAMPLERATE))
|
||||||
|
|
||||||
// HDLC flag bytes
|
// HDLC flag bytes
|
||||||
#define HDLC_FLAG 0x7E
|
#define HDLC_FLAG 0x7E // An HDLC_FLAG is used to signify the start or end of a frame
|
||||||
#define HDLC_RESET 0x7F
|
#define HDLC_RESET 0x7F // An HDLC_RESET is used to abruptly stop or reset a transmission
|
||||||
#define AX25_ESC 0x1B
|
#define AX25_ESC 0x1B // We use the AX.25 escape character for escaping bit sequences in
|
||||||
|
// the actual data. This is similar to escaping an " character in a
|
||||||
|
// string enclosed by "s.
|
||||||
|
|
||||||
// Check that sample rate is divisible by bitrate
|
// Check that sample rate is divisible by bitrate.
|
||||||
|
// If this is not the case, all of our algorithms will
|
||||||
|
// fail horribly and we will cry.
|
||||||
STATIC_ASSERT(!(CONFIG_AFSK_DAC_SAMPLERATE % BITRATE));
|
STATIC_ASSERT(!(CONFIG_AFSK_DAC_SAMPLERATE % BITRATE));
|
||||||
|
|
||||||
|
// How many samples it takes to encode or decode one bit
|
||||||
|
// on the physical medium.
|
||||||
#define DAC_SAMPLESPERBIT (CONFIG_AFSK_DAC_SAMPLERATE / BITRATE)
|
#define DAC_SAMPLESPERBIT (CONFIG_AFSK_DAC_SAMPLERATE / BITRATE)
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// Link Layer Control and Demodulation //
|
// Link Layer Control and Demodulation //
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// hdlcParse /////////////////////////////////////////
|
||||||
|
// This function looks at the raw bits demodulated from
|
||||||
|
// the physical medium and tries to parse actual data
|
||||||
|
// packets from the bitstream. Note that at this level,
|
||||||
|
// we don't really try to discriminate when a packet
|
||||||
|
// starts or ends, or where the payload is. We only try
|
||||||
|
// to detect that a transmission is taking place, then
|
||||||
|
// synchronise to the start and end of the transmitted
|
||||||
|
// bytes, and push these up to the data-link layer, in
|
||||||
|
// this example the MP.x protocol. It is then the
|
||||||
|
// protocols job to actually recreate the full packet.
|
||||||
|
// Also note that the data is not "pushed" per se, but
|
||||||
|
// stored in a FIFO buffer, that the protocol must
|
||||||
|
// continously read to recreate the received packets.
|
||||||
static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo) {
|
static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo) {
|
||||||
|
// Initialise a return value. We start with the
|
||||||
|
// assumption that all is going to end well :)
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
|
|
||||||
|
// Bitshift our byte of demodulated bits to
|
||||||
|
// the left by one bit, to make room for the
|
||||||
|
// next incoming bit
|
||||||
hdlc->demodulatedBits <<= 1;
|
hdlc->demodulatedBits <<= 1;
|
||||||
|
// And then put the newest bit from the
|
||||||
|
// demodulator into the byte.
|
||||||
hdlc->demodulatedBits |= bit ? 1 : 0;
|
hdlc->demodulatedBits |= bit ? 1 : 0;
|
||||||
|
|
||||||
// Check if we have received a HDLC flag (01111110)
|
// Now we'll look at the last 8 received bits, and
|
||||||
|
// check if we have received a HDLC flag (01111110)
|
||||||
if (hdlc->demodulatedBits == HDLC_FLAG) {
|
if (hdlc->demodulatedBits == HDLC_FLAG) {
|
||||||
|
// If we have, check that our output buffer is
|
||||||
|
// not full.
|
||||||
if (!fifo_isfull(fifo)) {
|
if (!fifo_isfull(fifo)) {
|
||||||
|
// If it isn't, we'll push the HDLC_FLAG into
|
||||||
|
// the buffer and indicate that we are now
|
||||||
|
// receiving data. For bling we also turn
|
||||||
|
// on the RX LED.
|
||||||
fifo_push(fifo, HDLC_FLAG);
|
fifo_push(fifo, HDLC_FLAG);
|
||||||
hdlc->receiving = true;
|
hdlc->receiving = true;
|
||||||
|
LED_RX_ON();
|
||||||
} else {
|
} else {
|
||||||
|
// If the buffer is full, we have a problem
|
||||||
|
// and abort by setting the return value to
|
||||||
|
// false and stopping the here.
|
||||||
ret = false;
|
ret = false;
|
||||||
hdlc->receiving = false;
|
hdlc->receiving = false;
|
||||||
|
LED_RX_OFF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Everytime we receive a HDLC_FLAG, we reset the
|
||||||
|
// storage for our current incoming byte and bit
|
||||||
|
// position in that byte. This effectively
|
||||||
|
// synchronises our parsing to the start and end
|
||||||
|
// of the received bytes.
|
||||||
hdlc->currentByte = 0;
|
hdlc->currentByte = 0;
|
||||||
hdlc->bitIndex = 0;
|
hdlc->bitIndex = 0;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -87,42 +172,78 @@ static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo) {
|
||||||
|
|
||||||
// Check if we have received a RESET flag (01111111)
|
// Check if we have received a RESET flag (01111111)
|
||||||
if ((hdlc->demodulatedBits & HDLC_RESET) == HDLC_RESET) {
|
if ((hdlc->demodulatedBits & HDLC_RESET) == HDLC_RESET) {
|
||||||
|
// If we have, something probably went wrong at the
|
||||||
|
// transmitting end, and we abort the reception.
|
||||||
hdlc->receiving = false;
|
hdlc->receiving = false;
|
||||||
|
LED_RX_OFF();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are just receiving noise, don't bother with anything
|
// If we have not yet seen a HDLC_FLAG indicating that
|
||||||
|
// a transmission is actually taking place, don't bother
|
||||||
|
// with anything.
|
||||||
if (!hdlc->receiving)
|
if (!hdlc->receiving)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
// First check if what we are seeing is a stuffed bit
|
// First check if what we are seeing is a stuffed bit.
|
||||||
|
// Since the different HDLC control characters like
|
||||||
|
// HDLC_FLAG, HDLC_RESET and such could also occur in
|
||||||
|
// a normal data stream, we employ a method known as
|
||||||
|
// "bit stuffing". All control characters have more than
|
||||||
|
// 5 ones in a row, so if the transmitting party detects
|
||||||
|
// this sequence in the _data_ to be transmitted, it inserts
|
||||||
|
// a zero to avoid the receiving party interpreting it as
|
||||||
|
// a control character. Therefore, if we detect such a
|
||||||
|
// "stuffed bit", we simply ignore it and wait for the
|
||||||
|
// next bit to come in.
|
||||||
|
//
|
||||||
|
// We do the detection by applying an AND bit-mask to the
|
||||||
|
// stream of demodulated bits. This mask is 00111111 (0x3f)
|
||||||
|
// if the result of the operation is 00111110 (0x3e), we
|
||||||
|
// have detected a stuffed bit.
|
||||||
if ((hdlc->demodulatedBits & 0x3f) == 0x3e)
|
if ((hdlc->demodulatedBits & 0x3f) == 0x3e)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
// If we have an actual 1 bit, push this to the current byte
|
// If we have an actual 1 bit, push this to the current byte
|
||||||
|
// If it's a zero, we don't need to do anything, since the
|
||||||
|
// bit is initialized to zero when we bitshifted earlier.
|
||||||
if (hdlc->demodulatedBits & 0x01)
|
if (hdlc->demodulatedBits & 0x01)
|
||||||
hdlc->currentByte |= 0x80;
|
hdlc->currentByte |= 0x80;
|
||||||
|
|
||||||
// Increment the bitIndex and check if we have a complete byte
|
// Increment the bitIndex and check if we have a complete byte
|
||||||
if (++hdlc->bitIndex >= 8) {
|
if (++hdlc->bitIndex >= 8) {
|
||||||
// If we have a HDLC control character,
|
// If we have a HDLC control character, put a AX.25 escape
|
||||||
// put a AX.25 escape in the received data
|
// in the received data. We know we need to do this,
|
||||||
|
// because at this point we must have already seen a HDLC
|
||||||
|
// flag, meaning that this control character is the result
|
||||||
|
// of a bitstuffed byte that is equal to said control
|
||||||
|
// character, but is actually part of the data stream.
|
||||||
|
// By inserting the escape character, we tell the protocol
|
||||||
|
// layer that this is not an actual control character, but
|
||||||
|
// data.
|
||||||
if ((hdlc->currentByte == HDLC_FLAG ||
|
if ((hdlc->currentByte == HDLC_FLAG ||
|
||||||
hdlc->currentByte == HDLC_RESET ||
|
hdlc->currentByte == HDLC_RESET ||
|
||||||
hdlc->currentByte == AX25_ESC)) {
|
hdlc->currentByte == AX25_ESC)) {
|
||||||
|
// We also need to check that our received data buffer
|
||||||
|
// is not full before putting more data in
|
||||||
if (!fifo_isfull(fifo)) {
|
if (!fifo_isfull(fifo)) {
|
||||||
fifo_push(fifo, AX25_ESC);
|
fifo_push(fifo, AX25_ESC);
|
||||||
} else {
|
} else {
|
||||||
|
// If it is, abort and return false
|
||||||
hdlc->receiving = false;
|
hdlc->receiving = false;
|
||||||
|
LED_RX_OFF();
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push the actual byte to the received data FIFO
|
// Push the actual byte to the received data FIFO,
|
||||||
|
// if it isn't full.
|
||||||
if (!fifo_isfull(fifo)) {
|
if (!fifo_isfull(fifo)) {
|
||||||
fifo_push(fifo, hdlc->currentByte);
|
fifo_push(fifo, hdlc->currentByte);
|
||||||
} else {
|
} else {
|
||||||
|
// If it is, well, you know by now!
|
||||||
hdlc->receiving = false;
|
hdlc->receiving = false;
|
||||||
|
LED_RX_OFF();
|
||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +260,15 @@ static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adcISR ////////////////////////////////////////////
|
||||||
|
// This is the Interrupt Service Routine for the
|
||||||
|
// Analog to Digital Conversion. It is called 9600
|
||||||
|
// times each second to analyze the sample taken from
|
||||||
|
// the physical medium. The job of this routine is
|
||||||
|
// to detect whether we have a "mark" or "space"
|
||||||
|
// frequency present on the baseband (the physical
|
||||||
|
// medium). The result of this analysis will then
|
||||||
|
// be passed to the HDLC parser in form of a 1 or a 0
|
||||||
void afsk_adc_isr(Afsk *afsk, int8_t currentSample) {
|
void afsk_adc_isr(Afsk *afsk, int8_t currentSample) {
|
||||||
// To determine the received frequency, and thereby
|
// To determine the received frequency, and thereby
|
||||||
// the bit of the sample, we multiply the sample by
|
// the bit of the sample, we multiply the sample by
|
||||||
|
@ -162,7 +292,7 @@ void afsk_adc_isr(Afsk *afsk, int8_t currentSample) {
|
||||||
// If there is a signal transition, recalibrate
|
// If there is a signal transition, recalibrate
|
||||||
// sampling phase
|
// sampling phase
|
||||||
|
|
||||||
if (EDGE_FOUND(afsk->sampledBits)) {
|
if (TRANSITION_FOUND(afsk->sampledBits)) {
|
||||||
if (afsk->currentPhase < PHASE_THRESHOLD) {
|
if (afsk->currentPhase < PHASE_THRESHOLD) {
|
||||||
afsk->currentPhase += PHASE_INC;
|
afsk->currentPhase += PHASE_INC;
|
||||||
} else {
|
} else {
|
||||||
|
@ -196,7 +326,7 @@ void afsk_adc_isr(Afsk *afsk, int8_t currentSample) {
|
||||||
// We use the EDGE_FOUND function to determine this.
|
// We use the EDGE_FOUND function to determine this.
|
||||||
// We also check the return of the Link Control parser
|
// We also check the return of the Link Control parser
|
||||||
// to check if an error occured.
|
// to check if an error occured.
|
||||||
if (!hdlcParse(&afsk->hdlc, !EDGE_FOUND(afsk->actualBits), &afsk->rxFifo)) {
|
if (!hdlcParse(&afsk->hdlc, !TRANSITION_FOUND(afsk->actualBits), &afsk->rxFifo)) {
|
||||||
afsk->status |= RX_OVERRUN;
|
afsk->status |= RX_OVERRUN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +345,8 @@ static void afsk_txStart(Afsk *afsk) {
|
||||||
afsk->phaseAcc = 0;
|
afsk->phaseAcc = 0;
|
||||||
afsk->bitstuffCount = 0;
|
afsk->bitstuffCount = 0;
|
||||||
afsk->sending = true;
|
afsk->sending = true;
|
||||||
|
LED_TX_ON();
|
||||||
|
PTT_ON();
|
||||||
afsk->preambleLength = DIV_ROUND(CONFIG_AFSK_PREAMBLE_LEN * BITRATE, 8000);
|
afsk->preambleLength = DIV_ROUND(CONFIG_AFSK_PREAMBLE_LEN * BITRATE, 8000);
|
||||||
AFSK_DAC_IRQ_START(afsk->dacPin);
|
AFSK_DAC_IRQ_START(afsk->dacPin);
|
||||||
}
|
}
|
||||||
|
@ -233,6 +365,8 @@ uint8_t afsk_dac_isr(Afsk *afsk) {
|
||||||
if (fifo_isempty(&afsk->txFifo) && afsk->tailLength == 0) {
|
if (fifo_isempty(&afsk->txFifo) && afsk->tailLength == 0) {
|
||||||
AFSK_DAC_IRQ_STOP(afsk->dacPin);
|
AFSK_DAC_IRQ_STOP(afsk->dacPin);
|
||||||
afsk->sending = false;
|
afsk->sending = false;
|
||||||
|
LED_TX_OFF();
|
||||||
|
PTT_OFF();
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
// Reset the bitstuff counter if we have just sent
|
// Reset the bitstuff counter if we have just sent
|
||||||
|
@ -262,6 +396,8 @@ uint8_t afsk_dac_isr(Afsk *afsk) {
|
||||||
if (fifo_isempty(&afsk->txFifo)) {
|
if (fifo_isempty(&afsk->txFifo)) {
|
||||||
AFSK_DAC_IRQ_STOP(afsk->dacPin);
|
AFSK_DAC_IRQ_STOP(afsk->dacPin);
|
||||||
afsk->sending = false;
|
afsk->sending = false;
|
||||||
|
LED_TX_OFF();
|
||||||
|
PTT_OFF();
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
afsk->currentOutputByte = fifo_pop(&afsk->txFifo);
|
afsk->currentOutputByte = fifo_pop(&afsk->txFifo);
|
||||||
|
@ -408,7 +544,9 @@ void afsk_init(Afsk *afsk, int _adcPin, int _dacPin) {
|
||||||
// Init DAC & ADC
|
// Init DAC & ADC
|
||||||
AFSK_ADC_INIT(_adcPin, afsk);
|
AFSK_ADC_INIT(_adcPin, afsk);
|
||||||
AFSK_DAC_INIT(_dacPin, afsk);
|
AFSK_DAC_INIT(_dacPin, afsk);
|
||||||
AFSK_STROBE_INIT();
|
LED_TX_INIT();
|
||||||
|
LED_RX_INIT();
|
||||||
|
PTT_INIT();
|
||||||
|
|
||||||
DB(afsk->fd._type = KFT_AFSK);
|
DB(afsk->fd._type = KFT_AFSK);
|
||||||
afsk->fd.write = afsk_write;
|
afsk->fd.write = afsk_write;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
|
#include "afsk.h"
|
||||||
|
|
||||||
#include <net/afsk.h>
|
|
||||||
#include <cpu/irq.h>
|
#include <cpu/irq.h>
|
||||||
|
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
@ -48,7 +48,7 @@ DECLARE_ISR(ADC_vect) {
|
||||||
TIFR1 = BV(ICF1);
|
TIFR1 = BV(ICF1);
|
||||||
afsk_adc_isr(context, ((int16_t)((ADC) >> 2) - 128));
|
afsk_adc_isr(context, ((int16_t)((ADC) >> 2) - 128));
|
||||||
if (hw_afsk_dac_isr)
|
if (hw_afsk_dac_isr)
|
||||||
PORTD = afsk_dac_isr(context) & 0xF0;
|
PORTD = (afsk_dac_isr(context) & 0xF0) | BV(3);
|
||||||
else
|
else
|
||||||
PORTD = 128;
|
PORTD = 128;
|
||||||
}
|
}
|
|
@ -13,13 +13,21 @@ void hw_afsk_dacInit(int ch, struct Afsk *_ctx);
|
||||||
// ADC initialization
|
// ADC initialization
|
||||||
#define AFSK_ADC_INIT(ch, ctx) hw_afsk_adcInit(ch, ctx)
|
#define AFSK_ADC_INIT(ch, ctx) hw_afsk_adcInit(ch, ctx)
|
||||||
|
|
||||||
// LED on/off (pin 13)
|
// LED TX/RX on/off (pin 9/10)
|
||||||
#define AFSK_STROBE_INIT() do { DDRB |= BV(5); } while (0)
|
#define LED_TX_INIT() do { DDRB |= BV(1); } while (0)
|
||||||
#define AFSK_STROBE_ON() do { PORTB |= BV(5); } while (0)
|
#define LED_TX_ON() do { PORTB |= BV(1); } while (0)
|
||||||
#define AFSK_STROBE_OFF() do { PORTB &= ~BV(5); } while (0)
|
#define LED_TX_OFF() do { PORTB &= ~BV(1); } while (0)
|
||||||
|
|
||||||
|
#define LED_RX_INIT() do { DDRB |= BV(2); } while (0)
|
||||||
|
#define LED_RX_ON() do { PORTB |= BV(2); } while (0)
|
||||||
|
#define LED_RX_OFF() do { PORTB &= ~BV(2); } while (0)
|
||||||
|
|
||||||
|
#define PTT_INIT() do { DDRD |= BV(3); } while (0)
|
||||||
|
#define PTT_ON() do { PORTD |= BV(3); } while (0)
|
||||||
|
#define PTT_OFF() do { PORTD &= ~BV(3); } while (0)
|
||||||
|
|
||||||
// Initialization, start and stop for DAC
|
// Initialization, start and stop for DAC
|
||||||
#define AFSK_DAC_INIT(ch, ctx) do { (void)ch, (void)ctx; DDRD |= 0xF0; DDRB |= BV(3); } while (0)
|
#define AFSK_DAC_INIT(ch, ctx) do { (void)ch, (void)ctx; DDRD |= 0xF4; DDRB |= BV(3); } while (0)
|
||||||
#define AFSK_DAC_IRQ_START(ch) do { (void)ch; extern bool hw_afsk_dac_isr; PORTB |= BV(3); hw_afsk_dac_isr = true; } while (0)
|
#define AFSK_DAC_IRQ_START(ch) do { (void)ch; extern bool hw_afsk_dac_isr; PORTB |= BV(3); hw_afsk_dac_isr = true; } while (0)
|
||||||
#define AFSK_DAC_IRQ_STOP(ch) do { (void)ch; extern bool hw_afsk_dac_isr; PORTB &= ~BV(3); hw_afsk_dac_isr = false; } while (0)
|
#define AFSK_DAC_IRQ_STOP(ch) do { (void)ch; extern bool hw_afsk_dac_isr; PORTB &= ~BV(3); hw_afsk_dac_isr = false; } while (0)
|
||||||
|
|
||||||
|
|
89
Modem/main.c
89
Modem/main.c
|
@ -1,79 +1,120 @@
|
||||||
|
|
||||||
#include <cpu/irq.h>
|
#include <cpu/irq.h> // Interrupt functionality from BertOS
|
||||||
#include <cfg/debug.h>
|
#include <cfg/debug.h> // Debug configuration from BertOS
|
||||||
|
|
||||||
|
#include <drv/ser.h> // Serial driver from BertOS
|
||||||
|
#include <drv/timer.h> // Timer driver from BertOS
|
||||||
|
|
||||||
|
#include <stdio.h> // Standard input/output
|
||||||
|
#include <string.h> // String operations
|
||||||
|
|
||||||
#include "afsk.h" // Header for AFSK modem
|
#include "afsk.h" // Header for AFSK modem
|
||||||
#include "protocol/mp1.h" // Header for MP.1 protocol
|
#include "protocol/mp1.h" // Header for MP.1 protocol
|
||||||
|
|
||||||
#include <drv/ser.h>
|
static Afsk afsk; // Declare a AFSK modem struct
|
||||||
#include <drv/timer.h>
|
static MP1 mp1; // Declare a protocol struct
|
||||||
|
static Serial ser; // Declare a serial interface struct
|
||||||
|
|
||||||
#include <stdio.h>
|
#define ADC_CH 0 // Define which channel (pin) we want
|
||||||
#include <string.h>
|
// for the ADC (this is A0 on arduino)
|
||||||
|
|
||||||
static Afsk afsk;
|
#define TEST_TX false // Whether we should send test packets
|
||||||
static Serial ser;
|
// periodically, plus what to send:
|
||||||
|
#define TEST_PACKET "Test MP1 AFSK Packet. This is a test. 1234567890. ABCDEFGHIJKLMNOPQRSTUVWXYZ."
|
||||||
|
|
||||||
static MP1 mp1;
|
|
||||||
|
|
||||||
#define ADC_CH 0
|
static uint8_t serialBuffer[MP1_MAX_FRAME_LENGTH]; // This is a buffer for incoming serial data
|
||||||
|
static int sbyte; // For holding byte read from serial port
|
||||||
|
static int serialLen = 0; // Counter for counting length of data from serial
|
||||||
|
static bool sertx = false; // Flag signifying whether it's time to send data
|
||||||
|
// Received on the serial port.
|
||||||
|
|
||||||
#define TEST_PACKET "Test MP1 AFSK Packet! This one is longer and probably more prone to errors..."
|
|
||||||
|
|
||||||
static uint8_t serialBuffer[MP1_MAX_FRAME_LENGTH];
|
|
||||||
static int sbyte;
|
|
||||||
static uint8_t serialLen = 0;
|
|
||||||
static bool sertx = false;
|
|
||||||
|
|
||||||
|
// This is a callback we register with the protocol,
|
||||||
|
// so we can process each packet as they are decoded.
|
||||||
|
// Right now it just prints the packet to the serial port.
|
||||||
static void mp1Callback(struct MP1Packet *packet) {
|
static void mp1Callback(struct MP1Packet *packet) {
|
||||||
kfile_printf(&ser.fd, "%.*s\r\n", packet->dataLength, packet->data);
|
kfile_printf(&ser.fd, "%.*s\r\n", packet->dataLength, packet->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple initialization function.
|
||||||
static void init(void)
|
static void init(void)
|
||||||
{
|
{
|
||||||
|
// Enable interrupts
|
||||||
IRQ_ENABLE;
|
IRQ_ENABLE;
|
||||||
|
// Initialize BertOS debug bridge
|
||||||
kdbg_init();
|
kdbg_init();
|
||||||
kprintf("Init\n");
|
kprintf("Init\n");
|
||||||
|
|
||||||
|
// Initialize hardware timers
|
||||||
timer_init();
|
timer_init();
|
||||||
|
|
||||||
afsk_init(&afsk, ADC_CH, 0);
|
// Initialize serial comms on UART0,
|
||||||
mp1Init(&mp1, &afsk.fd, mp1Callback);
|
// which is the hardware serial on arduino
|
||||||
|
|
||||||
ser_init(&ser, SER_UART0);
|
ser_init(&ser, SER_UART0);
|
||||||
ser_setbaudrate(&ser, 57600);
|
ser_setbaudrate(&ser, 57600);
|
||||||
//ser_settimeouts(&ser, 0, 0);
|
|
||||||
|
// Create a modem context
|
||||||
|
afsk_init(&afsk, ADC_CH, 0);
|
||||||
|
// ... and a protocol context with the modem
|
||||||
|
mp1Init(&mp1, &afsk.fd, mp1Callback);
|
||||||
|
|
||||||
|
// That's all!
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
// Start by running the main initialization
|
||||||
init();
|
init();
|
||||||
|
// Record the current tick count for time-keeping
|
||||||
ticks_t start = timer_clock();
|
ticks_t start = timer_clock();
|
||||||
|
|
||||||
|
// Go into ye good ol' infinite loop
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
// First we instruct the protocol to check for
|
||||||
|
// incoming data
|
||||||
mp1Poll(&mp1);
|
mp1Poll(&mp1);
|
||||||
|
|
||||||
|
// We then read a byte from the serial port.
|
||||||
|
// Notice that we use "_nowait" since we can't
|
||||||
|
// have this blocking execution until a byte
|
||||||
|
// comes in.
|
||||||
sbyte = ser_getchar_nowait(&ser);
|
sbyte = ser_getchar_nowait(&ser);
|
||||||
|
// If there was actually some data waiting for us
|
||||||
|
// there, let's se what it tastes like :)
|
||||||
if (sbyte != EOF) {
|
if (sbyte != EOF) {
|
||||||
if (serialLen < MP1_MAX_FRAME_LENGTH && sbyte != 138) {
|
// If we have not yet surpassed the maximum frame length
|
||||||
|
// and the byte is not a "transmit" (newline) character,
|
||||||
|
// we should store it for transmission.
|
||||||
|
if ((serialLen < MP1_MAX_FRAME_LENGTH) && (sbyte != 138)) {
|
||||||
|
// Put the read byte into the buffer;
|
||||||
serialBuffer[serialLen] = sbyte;
|
serialBuffer[serialLen] = sbyte;
|
||||||
|
// Increment the read length counter
|
||||||
serialLen++;
|
serialLen++;
|
||||||
} else {
|
} else {
|
||||||
|
// If one of the above conditions were actually the
|
||||||
|
// case, it means we have to transmit, se we set
|
||||||
|
// transmission flag to true.
|
||||||
sertx = true;
|
sertx = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether we should send data in our serial buffer
|
||||||
if (sertx) {
|
if (sertx) {
|
||||||
|
// If we should, pass the buffer to the protocol's
|
||||||
|
// send function.
|
||||||
mp1Send(&mp1, serialBuffer, serialLen);
|
mp1Send(&mp1, serialBuffer, serialLen);
|
||||||
|
// Reset the transmission flag and length counter
|
||||||
sertx = false;
|
sertx = false;
|
||||||
serialLen = 0;
|
serialLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Periodically send test data
|
// Periodically send test data if we should do so
|
||||||
if (false && timer_clock() - start > ms_to_ticks(4000L))
|
if (TEST_TX && timer_clock() - start > ms_to_ticks(15000L)) {
|
||||||
{
|
// Reset the timer counter;
|
||||||
start = timer_clock();
|
start = timer_clock();
|
||||||
|
// And send a test packet!
|
||||||
mp1Send(&mp1, TEST_PACKET, sizeof(TEST_PACKET));
|
mp1Send(&mp1, TEST_PACKET, sizeof(TEST_PACKET));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
#include "mp1.h"
|
#include "mp1.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <drv/ser.h>
|
#include <drv/ser.h>
|
||||||
//#include <ctype.h>
|
|
||||||
|
|
||||||
static void mp1Decode(MP1 *mp1) {
|
static void mp1Decode(MP1 *mp1) {
|
||||||
|
// This decode function is basic and bare minimum.
|
||||||
|
// It does nothing more than extract the data
|
||||||
|
// payload from the buffer and put it into a struct
|
||||||
|
// for further processing.
|
||||||
MP1Packet packet; // A decoded packet struct
|
MP1Packet packet; // A decoded packet struct
|
||||||
uint8_t *buffer = mp1->buffer; // Get the buffer from the protocol context
|
uint8_t *buffer = mp1->buffer; // Get the buffer from the protocol context
|
||||||
|
|
||||||
|
// Set the payload length of the packet to the counted
|
||||||
|
// length minus 1, so we remove the checksum
|
||||||
packet.dataLength = mp1->packetLength - 1;
|
packet.dataLength = mp1->packetLength - 1;
|
||||||
packet.data = buffer;
|
packet.data = buffer;
|
||||||
|
|
||||||
|
// If a callback have been specified, let's
|
||||||
|
// call it and pass the decoded packet
|
||||||
if (mp1->callback) mp1->callback(&packet);
|
if (mp1->callback) mp1->callback(&packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// The Poll function reads data from the modem, handles //
|
||||||
|
// frame recognition and passes data on to higher layers //
|
||||||
|
// if valid packets are found //
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
void mp1Poll(MP1 *mp1) {
|
void mp1Poll(MP1 *mp1) {
|
||||||
int byte;
|
int byte;
|
||||||
|
|
||||||
|
@ -26,13 +38,13 @@ void mp1Poll(MP1 *mp1) {
|
||||||
// frame length, which means the flag signifies
|
// frame length, which means the flag signifies
|
||||||
// the end of the packet. Pass control to the
|
// the end of the packet. Pass control to the
|
||||||
// decoder.
|
// decoder.
|
||||||
kprintf("Got checksum: %d.\n", mp1->buffer[mp1->packetLength-1]);
|
// kprintf("Got checksum: %d.\n", mp1->buffer[mp1->packetLength-1]);
|
||||||
if ((mp1->checksum_in & 0xff) == 0x00) {
|
if ((mp1->checksum_in & 0xff) == 0x00) {
|
||||||
//kprintf("Correct checksum. Found %d.\n", mp1->buffer[mp1->packetLength-1]);
|
//kprintf("Correct checksum. Found %d.\n", mp1->buffer[mp1->packetLength-1]);
|
||||||
mp1Decode(mp1);
|
mp1Decode(mp1);
|
||||||
} else {
|
} else {
|
||||||
// Checksum was incorrect
|
// Checksum was incorrect
|
||||||
mp1Decode(mp1);
|
//mp1Decode(mp1);
|
||||||
//kprintf("Incorrect checksum. Found %d.\n", mp1->buffer[mp1->packetLength]);
|
//kprintf("Incorrect checksum. Found %d.\n", mp1->buffer[mp1->packetLength]);
|
||||||
//kprintf("should be %d", mp1->checksum_in);
|
//kprintf("should be %d", mp1->checksum_in);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +139,7 @@ void mp1Send(MP1 *mp1, const void *_buffer, size_t length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write checksum to end of packet
|
// Write checksum to end of packet
|
||||||
kprintf("Checksum of this packet is %d\n", mp1->checksum_out);
|
kprintf("Sending packet with checksum %d\n", mp1->checksum_out);
|
||||||
mp1Putbyte(mp1, mp1->checksum_out);
|
mp1Putbyte(mp1, mp1->checksum_out);
|
||||||
|
|
||||||
// Transmit a HDLC_FLAG to signify end of TX
|
// Transmit a HDLC_FLAG to signify end of TX
|
||||||
|
@ -137,6 +149,8 @@ void mp1Send(MP1 *mp1, const void *_buffer, size_t length) {
|
||||||
void mp1Init(MP1 *mp1, KFile *modem, mp1_callback_t callback) {
|
void mp1Init(MP1 *mp1, KFile *modem, mp1_callback_t callback) {
|
||||||
// Allocate memory for our protocol "object"
|
// Allocate memory for our protocol "object"
|
||||||
memset(mp1, 0, sizeof(*mp1));
|
memset(mp1, 0, sizeof(*mp1));
|
||||||
|
// Set references to our modem "object" and
|
||||||
|
// a callback for when a packet has been decoded
|
||||||
mp1->modem = modem;
|
mp1->modem = modem;
|
||||||
mp1->callback = callback;
|
mp1->callback = callback;
|
||||||
}
|
}
|
|
@ -1,2 +1,2 @@
|
||||||
#define VERS_BUILD 308
|
#define VERS_BUILD 357
|
||||||
#define VERS_HOST "vixen"
|
#define VERS_HOST "vixen"
|
||||||
|
|
Loading…
Reference in New Issue