From bd11c5ee8307af68ca330c17a2799fae3e5ba801 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 27 Apr 2014 18:54:01 +0200 Subject: [PATCH] Direct serial line in/out. P-persistent CSMA. SLIP compatibility. --- Modem/config.h | 10 ++-- Modem/hardware.c | 14 ++++-- Modem/hardware.h | 3 ++ Modem/main.c | 113 +++++++++++++++++++++++++++++-------------- Modem/protocol/mp1.c | 67 ++++++++++++++++++++++--- Modem/protocol/mp1.h | 14 +++++- bertos/drv/ser.c | 8 +++ bertos/drv/ser.h | 1 + buildrev.h | 2 +- 9 files changed, 180 insertions(+), 52 deletions(-) diff --git a/Modem/config.h b/Modem/config.h index c67b011..794b85e 100644 --- a/Modem/config.h +++ b/Modem/config.h @@ -3,11 +3,13 @@ #define FSK_CFG // Debug & test options -#define SERIAL_DEBUG true -#define PASSALL true +#define SERIAL_DEBUG false +#define PASSALL false #define AUTOREPLY false // Modem options +#define TX_MAXWAIT 2UL // How many milliseconds should pass with no + // no incoming data before it is transmitted #define CONFIG_AFSK_RX_BUFLEN 64 // The size of the modems receive buffer #define CONFIG_AFSK_TX_BUFLEN 64 // The size of the modems transmit buffer #define CONFIG_AFSK_DAC_SAMPLERATE 9600 // The samplerate of the DAC. Note that @@ -17,7 +19,7 @@ #define CONFIG_AFSK_RXTIMEOUT 0 // How long a read operation from the modem // will wait for data before timing out. -#define CONFIG_AFSK_PREAMBLE_LEN 450UL // The length of the packet preamble in milliseconds -#define CONFIG_AFSK_TRAILER_LEN 20UL // The length of the packet tail in milliseconds +#define CONFIG_AFSK_PREAMBLE_LEN 350UL // The length of the packet preamble in milliseconds +#define CONFIG_AFSK_TRAILER_LEN 50UL // The length of the packet tail in milliseconds #endif \ No newline at end of file diff --git a/Modem/hardware.c b/Modem/hardware.c index 1102b85..439ae01 100644 --- a/Modem/hardware.c +++ b/Modem/hardware.c @@ -111,8 +111,10 @@ void hw_afsk_adcInit(int ch, Afsk *_modem) // "ADC_vect". This lets the processor know what to do // when all the timing and configuration we just set up // finally* ends up triggering the interrupt. +bool hw_ptt_on; bool hw_afsk_dac_isr; DECLARE_ISR(ADC_vect) { + TIFR1 = BV(ICF1); // Call the routine for analysing the captured sample @@ -131,7 +133,7 @@ DECLARE_ISR(ADC_vect) { // We also need to check if we're supposed to spit // out some modulated data to the DAC. - if (hw_afsk_dac_isr) + if (hw_afsk_dac_isr) { // If there is, it's easy to actually do so. We // calculate what the sample should be in the // DAC ISR, and apply the bitmask 11110000. This @@ -143,12 +145,18 @@ DECLARE_ISR(ADC_vect) { // by the PORTD register. This is the PTT pin // which tells the radio to open it transmitter. PORTD = (afsk_dac_isr(modem) & 0xF0) | BV(3); - else + } else { // If we're not supposed to transmit anything, we // keep quiet by continously sending 128, which // when converted to an AC waveform by the DAC, // equates to a steady, unchanging 0 volts. - PORTD = 128; + if (hw_ptt_on) { + PORTD = 136; + } else { + PORTD = 128; + } + } + } diff --git a/Modem/hardware.h b/Modem/hardware.h index 2e8d430..a1ba622 100644 --- a/Modem/hardware.h +++ b/Modem/hardware.h @@ -53,4 +53,7 @@ void hw_afsk_dacInit(int ch, struct Afsk *_ctx); #define AFSK_DAC_IRQ_START() do { extern bool hw_afsk_dac_isr; PORTD |= BV(3); hw_afsk_dac_isr = true; } while (0) #define AFSK_DAC_IRQ_STOP() do { extern bool hw_afsk_dac_isr; PORTD &= ~BV(3); hw_afsk_dac_isr = false; } while (0) +#define AFSK_HW_PTT_ON() do { extern bool hw_ptt_on; hw_ptt_on = true; } while (0) +#define AFSK_HW_PTT_OFF() do { extern bool hw_ptt_on; hw_ptt_on = false; } while (0) + #endif diff --git a/Modem/main.c b/Modem/main.c index 691d18e..2759796 100644 --- a/Modem/main.c +++ b/Modem/main.c @@ -33,9 +33,10 @@ static Serial ser; // Declare a serial interface struct #define TEST_TX_INTERVAL 10000L -static uint8_t serialBuffer[MP1_MAX_FRAME_LENGTH]; // This is a buffer for incoming serial data +static uint8_t serialBuffer[MP1_MAX_DATA_SIZE]; // This is a buffer for incoming serial data + static int sbyte; // For holding byte read from serial port -static size_t serialLen = 0; // Counter for counting length of data from serial +static size_t 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. @@ -47,13 +48,19 @@ static bool sertx = false; // Flag signifying whether it's time to send da // 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) { - kfile_printf(&ser.fd, "%.*s\n", packet->dataLength, packet->data); + if (SERIAL_DEBUG) { + kfile_printf(&ser.fd, "%.*s\n", packet->dataLength, packet->data); - if (AUTOREPLY && packet->data[0]-128 == 'R' && packet->data[1]-128 == 'Q') { - timer_delay(1000); - - uint8_t output[sizeof(TEST_PACKET)] = TEST_PACKET; - mp1Send(&mp1, output, sizeof(TEST_PACKET)); + if (AUTOREPLY && packet->data[0]-128 == 'R' && packet->data[1]-128 == 'Q') { + timer_delay(1000); + + uint8_t output[sizeof(TEST_PACKET)] = TEST_PACKET; + mp1Send(&mp1, output, sizeof(TEST_PACKET)); + } + } else { + for (unsigned long i = 0; i < packet->dataLength; i++) { + kfile_putc(packet->data[i], &ser.fd); + } } } @@ -69,7 +76,7 @@ static void init(void) // Initialize serial comms on UART0, // which is the hardware serial on arduino ser_init(&ser, SER_UART0); - ser_setbaudrate(&ser, 115200); + ser_setbaudrate(&ser, 9600); // For some reason BertOS sets the serial // to 7 bit characters by default. We set @@ -99,50 +106,82 @@ int main(void) 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); // If there was actually some data waiting for us // there, let's se what it tastes like :) - if (sbyte != EOF) { - // 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 != 10)) { - // Put the read byte into the buffer; - serialBuffer[serialLen] = sbyte; - // Increment the read length counter - serialLen++; + if (ser_available(&ser)) { + // 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); + + // If SERIAL_DEBUG is specified we'll handle + // serial data as direct human input and only + // transmit when we get a LF character + if (SERIAL_DEBUG) { + // 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_DATA_SIZE) && (sbyte != 10)) { + // Put the read byte into the buffer; + serialBuffer[serialLen] = sbyte; + // Increment the read length counter + serialLen++; + } 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; + } } else { - // If one of the above conditions were actually the - // case, it means we have to transmit, se we set - // transmission flag to true. + // Otherwise we assume the modem is running + // in automated mode, and we push out data + // as it becomes available. We either transmit + // immediately when the max frame length has + // been reached, or when we get no input for + // a certain amount of time. + + if (serialLen < MP1_MAX_DATA_SIZE-1) { + // Put the read byte into the buffer; + serialBuffer[serialLen] = sbyte; + // Increment the read length counter + serialLen++; + } else { + // If max frame length has been reached + // we need to transmit. + serialBuffer[serialLen] = sbyte; + serialLen++; + sertx = true; + } + + start = timer_clock(); + } + } else { + if (!SERIAL_DEBUG && serialLen > 0 && timer_clock() - start > ms_to_ticks(TX_MAXWAIT)) { sertx = true; } } // Check whether we should send data in our serial buffer - if (sertx) { - // If we should, pass the buffer to the protocol's - // send function. - - mp1Send(&mp1, serialBuffer, serialLen); - - // Reset the transmission flag and length counter - sertx = false; - serialLen = 0; + if (sertx) { + // Wait until incoming packets are done + if (!mp1CarrierSense(&mp1)) { + // And then send the data + mp1Send(&mp1, serialBuffer, serialLen); + + // Reset the transmission flag and length counter + sertx = false; + serialLen = 0; + } } // Periodically send test data if we should do so - if (TEST_TX && timer_clock() - start > ms_to_ticks(TEST_TX_INTERVAL)) { + if (SERIAL_DEBUG && TEST_TX && timer_clock() - start > ms_to_ticks(TEST_TX_INTERVAL)) { // Reset the timer counter; start = timer_clock(); // And send a test packet! uint8_t output[sizeof(TEST_PACKET)] = TEST_PACKET; mp1Send(&mp1, output, sizeof(TEST_PACKET)); - kprintf("TX done\n"); } } return 0; diff --git a/Modem/protocol/mp1.c b/Modem/protocol/mp1.c index 9e506b8..7883f24 100644 --- a/Modem/protocol/mp1.c +++ b/Modem/protocol/mp1.c @@ -1,8 +1,10 @@ #include "mp1.h" #include "hardware.h" #include "config.h" +#include // Used for random #include #include +#include // Timer driver from BertOS #include "compression/heatshrink_encoder.h" #include "compression/heatshrink_decoder.h" @@ -51,7 +53,7 @@ static uint8_t mp1ParityBlock(uint8_t first, uint8_t other) { return parity; } -// This deode function retrieves the buffer of +// This decode function retrieves the buffer of // received, deinterleaved and error-corrected // bytes, inspects the header and determines // whether there is padding to be removed, and @@ -79,7 +81,7 @@ static void mp1Decode(MP1 *mp1) { // Set the payload length of the packet to the counted // length minus 1, so we remove the checksum - packet.dataLength = mp1->packetLength - 2 - (header & 0x01)*padding; + packet.dataLength = mp1->packetLength - 2 - (header & MP1_HEADER_PADDED)*padding; // Check if we have received a compressed packet if (header & MP1_HEADER_COMPRESSION) { @@ -89,11 +91,20 @@ static void mp1Decode(MP1 *mp1) { size_t decompressedSize = decompress(buffer, packet.dataLength); if (SERIAL_DEBUG) kprintf("[DS=%d]", decompressedSize); packet.dataLength = decompressedSize; - memcpy(buffer, compressionBuffer, decompressedSize); + memcpy(mp1->buffer, compressionBuffer, decompressedSize); + } else { + // If the packet was not compressed, we shift + // the data in our buffer back down to the actual + // beginning of the buffer array, since we incremented + // the pointer address for removing the header and + // padding. + for (unsigned long i = 0; i < packet.dataLength; i++) { + mp1->buffer[i] = buffer[i]; + } } // Set the data field of the packet to our buffer - packet.data = buffer; + packet.data = mp1->buffer; // If a callback have been specified, let's // call it and pass the decoded packet @@ -112,7 +123,9 @@ void mp1Poll(MP1 *mp1) { // Read bytes from the modem until we reach EOF while ((byte = kfile_getc(mp1->modem)) != EOF) { - // We have a byte, increment our read counter + // We read something from the modem, so we + // set the settleTimer + mp1->settleTimer = timer_clock(); ///////////////////////////////////////////// // This following block handles forward // @@ -127,6 +140,7 @@ void mp1Poll(MP1 *mp1) { if ((mp1->reading && (byte != AX25_ESC )) || (mp1->reading && (mp1->escape && (byte == AX25_ESC || byte == HDLC_FLAG || byte == HDLC_RESET)))) { + // We have a byte, increment our read counter mp1->readLength++; // Check if we have read three bytes. If we @@ -269,6 +283,10 @@ void mp1Poll(MP1 *mp1) { // frame length, which means the flag signifies // the end of the packet. Pass control to the // decoder. + // + // We also set the settle timer to indicate + // the time the frame completed reading. + mp1->settleTimer = timer_clock(); if ((mp1->checksum_in & 0xff) == 0x00) { if (SERIAL_DEBUG) kprintf("[CHK-OK] [C=%d] ", mp1->correctionsMade); mp1Decode(mp1); @@ -322,7 +340,6 @@ void mp1Poll(MP1 *mp1) { // byte in the buffer. When we have collected 3 // bytes, they will be processed by the error // correction part above. - mp1->buffer[mp1->packetLength++] = byte; } else { // If not, we have a problem: The buffer has overrun @@ -381,6 +398,13 @@ static void mp1Putbyte(MP1 *mp1, uint8_t byte) { // to be transmitted, and structures it into // a valid packet. void mp1Send(MP1 *mp1, void *_buffer, size_t length) { + // Open transmitter and wait for MP1_TXDELAY msecs + AFSK_HW_PTT_ON(); + ticks_t start = timer_clock(); + while (timer_clock() - start < ms_to_ticks(MP1_TXDELAY)) { + cpu_relax(); + } + // Get the transmit data buffer uint8_t *buffer = (uint8_t *)_buffer; @@ -493,6 +517,9 @@ void mp1Send(MP1 *mp1, void *_buffer, size_t length) { // And transmit a HDLC_FLAG to signify // end of the transmission. kfile_putc(HDLC_FLAG, mp1->modem); + + // Turn off manual PTT + AFSK_HW_PTT_OFF(); } // This function will simply initialize @@ -505,6 +532,34 @@ void mp1Init(MP1 *mp1, KFile *modem, mp1_callback_t callback) { // a callback for when a packet has been decoded mp1->modem = modem; mp1->callback = callback; + mp1->settleTimer = timer_clock(); + mp1->randomSeed = 0; +} + +// A simple form of P-persistent CSMA. +// Everytime we have heard activity +// on the channel, we wait at least +// MP1_SETTLE_TIME milliseconds after the +// activity has ceased. We then pick a random +// number, and if it is less than +// MP1_P_PERSISTENCE, we transmit. +bool mp1CarrierSense(MP1 *mp1) { + if (mp1->randomSeed == 0) { + mp1->randomSeed = timer_clock(); + srand(mp1->randomSeed); + } + + if (timer_clock() - mp1->settleTimer > ms_to_ticks(MP1_SETTLE_TIME)) { + uint8_t r = rand() % 255; + if (r < MP1_P_PERSISTENCE) { + return false; + } else { + mp1->settleTimer = timer_clock(); + return true; + } + } else { + return true; + } } // A handy debug function that can determine diff --git a/Modem/protocol/mp1.h b/Modem/protocol/mp1.h index 26746ca..e46fa5c 100644 --- a/Modem/protocol/mp1.h +++ b/Modem/protocol/mp1.h @@ -6,11 +6,20 @@ // Frame sizing & checksum #define MP1_INTERLEAVE_SIZE 12 +#define MP1_MAX_FRAME_LENGTH 22 * MP1_INTERLEAVE_SIZE +#define MP1_HEADER_SIZE 1 +#define MP1_CHECKSUM_SIZE 1 +#define MP1_MAX_DATA_SIZE MP1_MAX_FRAME_LENGTH - MP1_HEADER_SIZE - MP1_CHECKSUM_SIZE #define MP1_MIN_FRAME_LENGTH MP1_INTERLEAVE_SIZE #define MP1_DATA_BLOCK_SIZE ((MP1_INTERLEAVE_SIZE/3)*2) -#define MP1_MAX_FRAME_LENGTH 250 #define MP1_CHECKSUM_INIT 0xAA +// These two parameters are used for +// P-persistent CSMA +#define MP1_SETTLE_TIME 100UL // The minimum wait time before considering sending +#define MP1_P_PERSISTENCE 85UL // The probability (between 0 and 255) for sending +#define MP1_TXDELAY 150UL // Delay between turning on the transmitter and sending + // We need to know some basic HDLC flag bytes #define HDLC_FLAG 0x7E #define HDLC_RESET 0x7F @@ -44,10 +53,12 @@ typedef struct MP1 { uint8_t checksum_out; // Rolling checksum for outgoing packets bool reading; // True when we have seen a HDLC flag bool escape; // We need to know if we are in an escape sequence + ticks_t settleTimer; // Timer used for carrier sense settling long correctionsMade; // A counter for how many corrections were made to a packet uint8_t interleaveCounter; // Keeps track of when we have received an entire interleaved block uint8_t interleaveOut[MP1_INTERLEAVE_SIZE]; // A buffer for interleaving bytes before they are sent uint8_t interleaveIn[MP1_INTERLEAVE_SIZE]; // A buffer for storing interleaved bytes before they are deinterleaved + uint8_t randomSeed; // A seed for the pseudo-random number generator } MP1; // A struct encapsulating a network packet @@ -61,6 +72,7 @@ void mp1Init(MP1 *mp1, KFile *modem, mp1_callback_t callback); void mp1Read(MP1 *mp1, int byte); void mp1Poll(MP1 *mp1); void mp1Send(MP1 *mp1, void *_buffer, size_t length); +bool mp1CarrierSense(MP1 *mp1); int freeRam(void); size_t compress(uint8_t *input, size_t length); diff --git a/bertos/drv/ser.c b/bertos/drv/ser.c index c9c3b0f..dba43a7 100644 --- a/bertos/drv/ser.c +++ b/bertos/drv/ser.c @@ -189,6 +189,14 @@ int ser_getchar_nowait(struct Serial *fd) return (int)(unsigned char)fifo_pop_locked(&fd->rxfifo); } +bool ser_available(struct Serial *fd) { + if (fifo_isempty_locked(&fd->rxfifo)) { + return false; + } else { + return true; + } +} + /** diff --git a/bertos/drv/ser.h b/bertos/drv/ser.h index 861fdf1..2e14d00 100644 --- a/bertos/drv/ser.h +++ b/bertos/drv/ser.h @@ -197,6 +197,7 @@ void ser_setparity(struct Serial *fd, int parity); void ser_settimeouts(struct Serial *fd, mtime_t rxtimeout, mtime_t txtimeout); void ser_resync(struct Serial *fd, mtime_t delay); int ser_getchar_nowait(struct Serial *fd); +bool ser_available(struct Serial *fd); void ser_purgeRx(struct Serial *fd); void ser_purgeTx(struct Serial *fd); diff --git a/buildrev.h b/buildrev.h index 01b67e4..35b47f5 100644 --- a/buildrev.h +++ b/buildrev.h @@ -1,2 +1,2 @@ -#define VERS_BUILD 1401 +#define VERS_BUILD 1560 #define VERS_HOST "shard"