diff --git a/.cproject b/.cproject index 9795aef..6dcb8d2 100644 --- a/.cproject +++ b/.cproject @@ -1,193 +1,398 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/Src/digipeater.c b/Core/Src/digipeater.c index 14424a3..bf87e46 100644 --- a/Core/Src/digipeater.c +++ b/Core/Src/digipeater.c @@ -454,6 +454,8 @@ void DigiStoreDeDupe(uint8_t *buf, uint16_t size) while((buf[i] & 1) == 0) //look for path end bit (skip path) { i++; + if(i == size) + return; } i++; diff --git a/Core/Src/kiss.c b/Core/Src/kiss.c index 8421629..a879327 100644 --- a/Core/Src/kiss.c +++ b/Core/Src/kiss.c @@ -95,6 +95,26 @@ void KissParse(Uart *port, uint8_t data) } } + uint16_t pathEnd = 0; + + //find path end bit (C-bit) + for(uint16_t i = 0; i < port->kissBufferHead; i++) + { + if((port->kissBuffer[i + 1] & 1) != 0) + { + pathEnd = i + 1; + break; + } + } + + //C-bit must lay on a 7 byte boundary (every path element is 7 bytes long) + if(pathEnd % 7) + { + port->kissBufferHead = 0; + return; + } + + __disable_irq(); port->kissProcessingOngoing = 1; port->kissTempBufferHead = 0; diff --git a/Src/ax25.c b/Src/ax25.c deleted file mode 100644 index 805366b..0000000 --- a/Src/ax25.c +++ /dev/null @@ -1,578 +0,0 @@ -/* -Copyright 2020-2023 Piotr Wilkon - -This file is part of VP-Digi. - -VP-Digi is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -VP-Digi is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with VP-Digi. If not, see . -*/ - -#include "ax25.h" -#include -#include "drivers/modem.h" -#include "common.h" -#include "drivers/systick.h" -#include -#include "digipeater.h" - -struct Ax25ProtoConfig Ax25Config; - - -#define FRAME_MAX_COUNT (10) //max count of frames in buffer -#define FRAME_BUFFER_SIZE (FRAME_MAX_COUNT * AX25_FRAME_MAX_SIZE) //circular frame buffer length - -#define STATIC_HEADER_FLAG_COUNT 4 //number of flags sent before each frame -#define STATIC_FOOTER_FLAG_COUNT 8 //number of flags sent after each frame - -#define MAX_TRANSMIT_RETRY_COUNT 8 //max number of retries if channel is busy - -struct FrameHandle -{ - uint16_t start; - uint16_t size; - uint16_t signalLevel; -}; - -static uint8_t rxBuffer[FRAME_BUFFER_SIZE]; //circular buffer for received frames -static uint16_t rxBufferHead = 0; //circular RX buffer write index -static struct FrameHandle rxFrame[FRAME_MAX_COUNT]; -static uint8_t rxFrameHead = 0; -static uint8_t rxFrameTail = 0; -static bool rxFrameBufferFull = false; - -static uint8_t txBuffer[FRAME_BUFFER_SIZE]; //circular TX frame buffer -static uint16_t txBufferHead = 0; //circular TX buffer write index -static uint16_t txBufferTail = 0; -static struct FrameHandle txFrame[FRAME_MAX_COUNT]; -static uint8_t txFrameHead = 0; -static uint8_t txFrameTail = 0; -static bool txFrameBufferFull = false; - -static uint8_t frameReceived; //a bitmap of receivers that received the frame - - -enum TxStage -{ - TX_STAGE_IDLE, - TX_STAGE_PREAMBLE, - TX_STAGE_HEADER_FLAGS, - TX_STAGE_DATA, - TX_STAGE_CRC, - TX_STAGE_FOOTER_FLAGS, - TX_STAGE_TAIL, -}; - -enum TxInitStage -{ - TX_INIT_OFF, - TX_INIT_WAITING, - TX_INIT_TRANSMITTING -}; - -static uint8_t txByte = 0; //current TX byte -static uint16_t txByteIdx = 0; //current TX byte index -static int8_t txBitIdx = 0; //current bit index in txByte -static uint16_t txDelayElapsed = 0; //counter of TXDelay bytes already sent -static uint8_t txFlagsElapsed = 0; //counter of flag bytes already sent -static uint8_t txCrcByteIdx = 0; //currently transmitted byte of CRC -static uint8_t txBitstuff = 0; //bit-stuffing counter -static uint16_t txTailElapsed; //counter of TXTail bytes already sent -static uint16_t txCrc = 0xFFFF; //current CRC -static uint32_t txQuiet = 0; //quit time + current tick value -static uint8_t txRetries = 0; //number of TX retries -static enum TxInitStage txInitStage; //current TX initialization stage -static enum TxStage txStage; //current TX stage - -struct RxState -{ - uint16_t crc; //current CRC - uint8_t frame[AX25_FRAME_MAX_SIZE]; //raw frame buffer - uint16_t frameIdx; //index for raw frame buffer - uint8_t receivedByte; //byte being currently received - uint8_t receivedBitIdx; //bit index for recByte - uint8_t rawData; //raw data being currently received - enum Ax25RxStage rx; //current RX stage - uint8_t frameReceived; //frame received flag -}; - -static volatile struct RxState rxState[MODEM_DEMODULATOR_COUNT]; - -static uint16_t lastCrc = 0; //CRC of the last received frame. If not 0, a frame was successfully received -static uint16_t rxMultiplexDelay = 0; //simple delay for decoder multiplexer to avoid receiving the same frame twice - -static uint16_t txDelay; //number of TXDelay bytes to send -static uint16_t txTail; //number of TXTail bytes to send - -static uint8_t outputFrameBuffer[AX25_FRAME_MAX_SIZE]; - -#define GET_FREE_SIZE(max, head, tail) (((head) < (tail)) ? ((tail) - (head)) : ((max) - (head) + (tail))) -#define GET_USED_SIZE(max, head, tail) (max - GET_FREE_SIZE(max, head, tail)) - -/** - * @brief Recalculate CRC for one bit - * @param bit Input bit - * @param *crc CRC pointer - */ -static void calculateCRC(uint8_t bit, uint16_t *crc) -{ - uint16_t xor_result; - xor_result = *crc ^ bit; - *crc >>= 1; - if (xor_result & 0x0001) - { - *crc ^= 0x8408; - } -} - -uint8_t Ax25GetReceivedFrameBitmap(void) -{ - return frameReceived; -} - -void Ax25ClearReceivedFrameBitmap(void) -{ - frameReceived = 0; -} - - -void *Ax25WriteTxFrame(uint8_t *data, uint16_t size) -{ - if((GET_FREE_SIZE(FRAME_BUFFER_SIZE, txBufferHead, txBufferTail) < size) || txFrameBufferFull) - { - return NULL; - } - - txFrame[txFrameHead].size = size; - txFrame[txFrameHead].start = txBufferHead; - for(uint16_t i = 0; i < size; i++) - { - txBuffer[txBufferHead++] = data[i]; - txBufferHead %= FRAME_BUFFER_SIZE; - } - void *ret = &txFrame[txFrameHead]; - __disable_irq(); - txFrameHead++; - txFrameHead %= FRAME_MAX_COUNT; - if(txFrameHead == txFrameTail) - txFrameBufferFull = true; - __enable_irq(); - return ret; -} - - -bool Ax25ReadNextRxFrame(uint8_t **dst, uint16_t *size, uint16_t *signalLevel) -{ - if((rxFrameHead == rxFrameTail) && !rxFrameBufferFull) - { - return false; - } - - *dst = outputFrameBuffer; - - for(uint16_t i = 0; i < rxFrame[rxFrameTail].size; i++) - { - (*dst)[i] = rxBuffer[(rxFrame[rxFrameTail].start + i) % FRAME_BUFFER_SIZE]; - } - - *signalLevel = rxFrame[rxFrameTail].signalLevel; - *size = rxFrame[rxFrameTail].size; - - __disable_irq(); - rxFrameBufferFull = false; - rxFrameTail++; - rxFrameTail %= FRAME_MAX_COUNT; - __enable_irq(); - return true; -} - -enum Ax25RxStage Ax25GetRxStage(uint8_t modem) -{ - return rxState[modem].rx; -} - - -void Ax25BitParse(uint8_t bit, uint8_t modem) -{ - if(lastCrc != 0) //there was a frame received - { - rxMultiplexDelay++; - if(rxMultiplexDelay > (4 * MODEM_DEMODULATOR_COUNT)) //hold it for a while and wait for other decoders to receive the frame - { - lastCrc = 0; - rxMultiplexDelay = 0; - for(uint8_t i = 0; i < MODEM_DEMODULATOR_COUNT; i++) - { - frameReceived |= ((rxState[i].frameReceived > 0) << i); - rxState[i].frameReceived = 0; - } - } - - } - - - struct RxState *rx = (struct RxState*)&(rxState[modem]); - - rx->rawData <<= 1; //store incoming bit - rx->rawData |= (bit > 0); - - - if(rx->rawData == 0x7E) //HDLC flag received - { - if(rx->rx == RX_STAGE_FRAME) //if we are in frame, this is the end of the frame - { - if((rx->frameIdx > 15)) //correct frame must be at least 16 bytes long - { - uint16_t i = 0; - for(; i < rx->frameIdx - 2; i++) //look for path end bit - { - if(rx->frame[i] & 1) - break; - } - - //if non-APRS frames are not allowed, check if this frame has control=0x03 and PID=0xF0 - - if(Ax25Config.allowNonAprs || (((rx->frame[i + 1] == 0x03) && (rx->frame[i + 2] == 0xf0)))) - { - if((rx->frame[rx->frameIdx - 2] == ((rx->crc & 0xFF) ^ 0xFF)) && (rx->frame[rx->frameIdx - 1] == (((rx->crc >> 8) & 0xFF) ^ 0xFF))) //check CRC - { - rx->frameReceived = 1; - rx->frameIdx -= 2; //remove CRC - if(rx->crc != lastCrc) //the other decoder has not received this frame yet, so store it in main frame buffer - { - lastCrc = rx->crc; //store CRC of this frame - - if(!rxFrameBufferFull) //if enough space, store the frame - { - rxFrame[rxFrameHead].start = rxBufferHead; - rxFrame[rxFrameHead].signalLevel = ModemGetRMS(modem); - __disable_irq(); - rxFrame[rxFrameHead++].size = rx->frameIdx; - rxFrameHead %= FRAME_MAX_COUNT; - if(rxFrameHead == rxFrameTail) - rxFrameBufferFull = true; - __enable_irq(); - - for(uint16_t i = 0; i < rx->frameIdx; i++) - { - rxBuffer[rxBufferHead++] = rx->frame[i]; - rxBufferHead %= FRAME_BUFFER_SIZE; - } - - } - } - } - } - - } - - } - rx->rx = RX_STAGE_FLAG; - ModemClearRMS(modem); - rx->receivedByte = 0; - rx->receivedBitIdx = 0; - rx->frameIdx = 0; - rx->crc = 0xFFFF; - return; - } - - - if((rx->rawData & 0x7F) == 0x7F) //received 7 consecutive ones, this is an error - { - rx->rx = RX_STAGE_FLAG; - ModemClearRMS(modem); - rx->receivedByte = 0; - rx->receivedBitIdx = 0; - rx->frameIdx = 0; - rx->crc = 0xFFFF; - return; - } - - - - if(rx->rx == RX_STAGE_IDLE) //not in a frame, don't go further - return; - - - if((rx->rawData & 0x3F) == 0x3E) //dismiss bit 0 added by bit stuffing - return; - - if(rx->rawData & 0x01) //received bit 1 - rx->receivedByte |= 0x80; //store it - - if(++rx->receivedBitIdx >= 8) //received full byte - { - if(rx->frameIdx > AX25_FRAME_MAX_SIZE) //frame is too long - { - rx->rx = RX_STAGE_IDLE; - ModemClearRMS(modem); - rx->receivedByte = 0; - rx->receivedBitIdx = 0; - rx->frameIdx = 0; - rx->crc = 0xFFFF; - return; - } - if(rx->frameIdx >= 2) //more than 2 bytes received, calculate CRC - { - for(uint8_t i = 0; i < 8; i++) - { - calculateCRC((rx->frame[rx->frameIdx - 2] >> i) & 1, &(rx->crc)); - } - } - rx->rx = RX_STAGE_FRAME; - rx->frame[rx->frameIdx++] = rx->receivedByte; //store received byte - rx->receivedByte = 0; - rx->receivedBitIdx = 0; - } - else - rx->receivedByte >>= 1; -} - - -uint8_t Ax25GetTxBit(void) -{ - if(txBitIdx == 8) - { - txBitIdx = 0; - if(txStage == TX_STAGE_PREAMBLE) //transmitting preamble (TXDelay) - { - if(txDelayElapsed < txDelay) //still transmitting - { - txByte = 0x7E; - txDelayElapsed++; - } - else //now transmit initial flags - { - txDelayElapsed = 0; - txStage = TX_STAGE_HEADER_FLAGS; - } - - } - if(txStage == TX_STAGE_HEADER_FLAGS) //transmitting initial flags - { - if(txFlagsElapsed < STATIC_HEADER_FLAG_COUNT) - { - txByte = 0x7E; - txFlagsElapsed++; - } - else - { - txFlagsElapsed = 0; - txStage = TX_STAGE_DATA; //transmit data - } - } - if(txStage == TX_STAGE_DATA) //transmitting normal data - { -transmitNormalData: - __disable_irq(); - if((txFrameHead != txFrameTail) || txFrameBufferFull) - { - __enable_irq(); - if(txByteIdx < txFrame[txFrameTail].size) //send buffer - { - txByte = txBuffer[(txFrame[txFrameTail].start + txByteIdx) % FRAME_BUFFER_SIZE]; - txByteIdx++; - } - else //end of buffer, send CRC - { - txStage = TX_STAGE_CRC; //transmit CRC - txCrcByteIdx = 0; - } - } - else //no more frames - { - __enable_irq(); - txByteIdx = 0; - txBitIdx = 0; - txStage = TX_STAGE_TAIL; - } - } - if(txStage == TX_STAGE_CRC) //transmitting CRC - { - if(txCrcByteIdx <= 1) - { - txByte = (txCrc & 0xFF) ^ 0xFF; - txCrc >>= 8; - txCrcByteIdx++; - } - else - { - txCrc = 0xFFFF; - txStage = TX_STAGE_FOOTER_FLAGS; //now transmit flags - txFlagsElapsed = 0; - } - - } - if(txStage == TX_STAGE_FOOTER_FLAGS) - { - if(txFlagsElapsed < STATIC_FOOTER_FLAG_COUNT) - { - txByte = 0x7E; - txFlagsElapsed++; - } - else - { - txFlagsElapsed = 0; - txByteIdx = 0; - txStage = TX_STAGE_DATA; //return to normal data transmission stage. There might be a next frame to transmit - __disable_irq(); - txFrameBufferFull = false; - txFrameTail++; - txFrameTail %= FRAME_MAX_COUNT; - __enable_irq(); - goto transmitNormalData; - } - } - if(txStage == TX_STAGE_TAIL) //transmitting tail - { - if(txTailElapsed < txTail) - { - txByte = 0x7E; - txTailElapsed++; - } - else //tail transmitted, stop transmission - { - txTailElapsed = 0; - txStage = TX_STAGE_IDLE; - txCrc = 0xFFFF; - txBitstuff = 0; - txByte = 0; - txInitStage = TX_INIT_OFF; - txBufferTail = txBufferHead; - ModemTransmitStop(); - return 0; - } - } - - } - - uint8_t txBit = 0; - - if((txStage == TX_STAGE_DATA) || (txStage == TX_STAGE_CRC)) //transmitting normal data or CRC - { - if(txBitstuff == 5) //5 consecutive ones transmitted - { - txBit = 0; //transmit bit-stuffed 0 - txBitstuff = 0; - } - else - { - if(txByte & 1) //1 being transmitted - { - txBitstuff++; //increment bit stuffing counter - txBit = 1; - } - else - { - txBit = 0; - txBitstuff = 0; //0 being transmitted, reset bit stuffing counter - } - if(txStage == TX_STAGE_DATA) //calculate CRC only for normal data - calculateCRC(txByte & 1, &txCrc); - - txByte >>= 1; - txBitIdx++; - } - } - else //transmitting preamble or flags, don't calculate CRC, don't use bit stuffing - { - txBit = txByte & 1; - txByte >>= 1; - txBitIdx++; - } - return txBit; -} - -/** - * @brief Initialize transmission and start when possible - */ -void Ax25TransmitBuffer(void) -{ - if(txInitStage == TX_INIT_WAITING) - return; - if(txInitStage == TX_INIT_TRANSMITTING) - return; - - if((txFrameHead != txFrameTail) || txFrameBufferFull) - { - txQuiet = (SysTickGet() + (Ax25Config.quietTime / SYSTICK_INTERVAL) + Random(0, 200 / SYSTICK_INTERVAL)); //calculate required delay - txInitStage = TX_INIT_WAITING; - } -} - - - -/** - * @brief Start transmission immediately - * @warning Transmission should be initialized using Ax25_transmitBuffer - */ -static void transmitStart(void) -{ - txCrc = 0xFFFF; //initial CRC value - txStage = TX_STAGE_PREAMBLE; - txByte = 0; - txBitIdx = 0; - txFlagsElapsed = 0; - ModemTransmitStart(); -} - - -/** - * @brief Start transmitting when possible - * @attention Must be continuously polled in main loop - */ -void Ax25TransmitCheck(void) -{ - if(txInitStage == TX_INIT_OFF) //TX not initialized at all, nothing to transmit - return; - if(txInitStage == TX_INIT_TRANSMITTING) //already transmitting - return; - - if(ModemIsTxTestOngoing()) //TX test is enabled, wait for now - return; - - if(txQuiet < SysTickGet()) //quit time has elapsed - { - if(!ModemDcdState()) //channel is free - { - txInitStage = TX_INIT_TRANSMITTING; //transmit right now - txRetries = 0; - transmitStart(); - } - else //channel is busy - { - if(txRetries == MAX_TRANSMIT_RETRY_COUNT) //timeout - { - txInitStage = TX_INIT_TRANSMITTING; //transmit right now - txRetries = 0; - transmitStart(); - } - else //still trying - { - txQuiet = SysTickGet() + Random(100 / SYSTICK_INTERVAL, 500 / SYSTICK_INTERVAL); //try again after some random time - txRetries++; - } - } - } -} - -void Ax25Init(void) -{ - txCrc = 0xFFFF; - - memset((void*)rxState, 0, sizeof(rxState)); - for(uint8_t i = 0; i < (sizeof(rxState) / sizeof(rxState[0])); i++) - rxState[i].crc = 0xFFFF; - - txDelay = ((float)Ax25Config.txDelayLength / (8.f * 1000.f / (float)MODEM_BAUDRATE)); //change milliseconds to byte count - txTail = ((float)Ax25Config.txTailLength / (8.f * 1000.f / (float)MODEM_BAUDRATE)); -} diff --git a/Src/drivers/modem.c b/Src/drivers/modem.c deleted file mode 100644 index c27fa6b..0000000 --- a/Src/drivers/modem.c +++ /dev/null @@ -1,711 +0,0 @@ -/* -Copyright 2020-2023 Piotr Wilkon - -This file is part of VP-Digi. - -VP-Digi is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -VP-Digi is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with VP-Digi. If not, see . -*/ - -#include "drivers/modem.h" -#include "drivers/systick.h" -#include "ax25.h" -#include "stm32f1xx.h" -#include -#include -#include "common.h" - - -/* - * Configuration for PLL-based data carrier detection - * DCD_MAXPULSE is the maximum value of the DCD pulse counter - * DCD_THRES is the threshold value of the DCD pulse counter. When reached the input signal is assumed to be valid - * DCD_MAXPULSE and DCD_THRES difference sets the DCD "inertia" so that the DCD state won't change rapidly when a valid signal is present - * DCD_DEC is the DCD pulse counter decrementation value when symbol changes too far from PLL counter zero - * DCD_INC is the DCD pulse counter incrementation value when symbol changes near the PLL counter zero - * DCD_PLLTUNE is the DCD timing coefficient when symbol changes, pll_counter = pll_counter * DCD_PLLTUNE - * The DCD mechanism is described in afsk_demod(). - * All values were selected by trial and error - */ -#define DCD_MAXPULSE 100 -#define DCD_THRES 30 -#define DCD_DEC 1 -#define DCD_INC 7 -#define DCD_PLLTUNE 0 - -#define N 8 //samples per symbol -#define DAC_SINE_SIZE 32 //DAC sine table size -#define PLLINC 536870912 //PLL tick increment value -#define PLLLOCKED 0.74 //PLL adjustment value when locked -#define PLLNOTLOCKED 0.50 //PLL adjustment value when not locked - -#define PTT_ON GPIOB->BSRR = GPIO_BSRR_BS7 -#define PTT_OFF GPIOB->BSRR = GPIO_BSRR_BR7 -#define DCD_ON (GPIOC->BSRR = GPIO_BSRR_BR13) -#define DCD_OFF (GPIOC->BSRR = GPIO_BSRR_BS13) - -struct ModemDemodConfig ModemConfig; - -static enum ModemTxTestMode txTestState; //current TX test mode -static uint16_t dacSine[DAC_SINE_SIZE]; //sine samples for DAC -static uint8_t dacSineIdx; //current sine sample index -static uint16_t samples[4]; //very raw received samples, filled directly by DMA -static uint8_t currentSymbol; //current symbol for NRZI encoding -static uint8_t markFreq; //mark frequency (inter-sample interval) -static uint8_t spaceFreq; //space frequency (inter-sample interval) -static uint16_t baudRate; //baudrate -static int32_t coeffHiI[N], coeffLoI[N], coeffHiQ[N], coeffLoQ[N]; //correlator IQ coefficients -static uint8_t dcd = 0; //multiplexed DCD state from both demodulators - - -/** - * @brief BPF filter with 2200 Hz tone 6 dB preemphasis (it actually attenuates 1200 Hz tone by 6 dB) - */ -static const int16_t bpfCoeffs[8] = -{ - 728, - -13418, - -554, - 19493, - -554, - -13418, - 728, - 2104 -}; - -/** - * @brief BPF filter with 2200 Hz tone 6 dB deemphasis - */ -static const int16_t invBpfCoeffs[8] = -{ - -10513, - -10854, - 9589, - 23884, - 9589, - -10854, - -10513, - -879 -}; - -#define BPF_TAPS (sizeof(bpfCoeffs) / sizeof(*bpfCoeffs) > sizeof(invBpfCoeffs) / sizeof(*invBpfCoeffs) ? \ - sizeof(bpfCoeffs) / sizeof(*bpfCoeffs) : sizeof(invBpfCoeffs) / sizeof(*invBpfCoeffs)) - -/** - * @brief Output LPF filter to remove data faster than 1200 baud - * It actually is a 600 Hz filter: symbols can change at 1200 Hz, but it takes 2 "ticks" to return to the same symbol - that's why it's 600 Hz - */ -static const int16_t lpfCoeffs[15] = -{ - -6128, - -5974, - -2503, - 4125, - 12679, - 21152, - 27364, - 29643, - 27364, - 21152, - 12679, - 4125, - -2503, - -5974, - -6128 -}; - -#define LPF_TAPS (sizeof(lpfCoeffs) / sizeof(*lpfCoeffs)) - - -struct DemodState -{ - enum ModemEmphasis emphasis; //preemphasis/deemphasis - uint8_t rawSymbols; //raw, unsynchronized symbols - uint8_t syncSymbols; //synchronized symbols - int16_t rawSample[BPF_TAPS]; //input (raw) samples - int32_t rxSample[BPF_TAPS]; //rx samples after pre/deemphasis filter - uint8_t rxSampleIdx; //index for the array above - int64_t lpfSample[LPF_TAPS]; //rx samples after final filtering - uint8_t dcd : 1; //DCD state - uint64_t RMSenergy; //frame energy counter (sum of samples squared) - uint32_t RMSsampleCount; //number of samples for RMS - int32_t pll; //bit recovery PLL counter - int32_t lastPll; //last bit recovery PLL counter value - int32_t dcdPll; //DCD PLL main counter - uint8_t dcdLastSymbol; //last symbol for DCD - uint8_t dcdCounter; //DCD "pulse" counter (incremented when RX signal is correct) -}; - -static volatile struct DemodState demodState[MODEM_DEMODULATOR_COUNT]; - -static void decode(uint8_t symbol, uint8_t demod); -static int32_t demodulate(int16_t sample, struct DemodState *dem); -static void setPtt(uint8_t state); - -uint8_t ModemDcdState(void) -{ - return dcd; -} - -uint8_t ModemIsTxTestOngoing(void) -{ - if(txTestState != TEST_DISABLED) - return 1; - - return 0; -} - -void ModemClearRMS(uint8_t modem) -{ - - demodState[modem].RMSenergy = 0; - demodState[modem].RMSsampleCount = 0; - -} - -uint16_t ModemGetRMS(uint8_t modem) -{ - return sqrtf((float)demodState[modem].RMSenergy / (float)demodState[modem].RMSsampleCount); -} - -enum ModemEmphasis ModemGetFilterType(uint8_t modem) -{ - return demodState[modem].emphasis; -} - -/** - * @brief Set DCD LED - * @param[in] state 0 - OFF, 1 - ON - */ -static void setDcd(uint8_t state) -{ - if(state) - { - GPIOC->BSRR = GPIO_BSRR_BR13; - GPIOB->BSRR = GPIO_BSRR_BS5; - } - else - { - GPIOC->BSRR = GPIO_BSRR_BS13; - GPIOB->BSRR = GPIO_BSRR_BR5; - } -} - - -/** - * @brief ISR for demodulator - * Called at 9600 Hz by DMA - */ -void DMA1_Channel2_IRQHandler(void) __attribute__ ((interrupt)); -void DMA1_Channel2_IRQHandler(void) -{ - if(DMA1->ISR & DMA_ISR_TCIF2) - { - DMA1->IFCR |= DMA_IFCR_CTCIF2; - - int32_t sample = ((samples[0] + samples[1] + samples[2] + samples[3]) >> 1) - 4095; //calculate input sample (decimation) - - uint8_t partialDcd = 0; - - for(uint8_t i = 0; i < MODEM_DEMODULATOR_COUNT; i++) - { - uint8_t symbol = (demodulate(sample, (struct DemodState*)&demodState[i]) > 0); //demodulate sample - decode(symbol, i); //recover bits, decode NRZI and call higher level function - if(demodState[i].dcd) - partialDcd |= 1; - } - - if(partialDcd) //DCD on any of the demodulators - { - dcd = 1; - setDcd(1); - } - else //no DCD on both demodulators - { - dcd = 0; - setDcd(0); - } - } -} - - - -/** - * @brief ISR for pushing DAC samples - */ -void TIM1_UP_IRQHandler(void) __attribute__ ((interrupt)); -void TIM1_UP_IRQHandler(void) -{ - TIM1->SR &= ~TIM_SR_UIF; - - if(ModemConfig.usePWM) - { - TIM4->CCR1 = dacSine[dacSineIdx]; - } - else - { - GPIOB->ODR &= ~0xF000; //zero 4 oldest bits - GPIOB->ODR |= (dacSine[dacSineIdx] << 12); //write sample to 4 oldest bits - } - - dacSineIdx++; - dacSineIdx &= (DAC_SINE_SIZE - 1); -} - - -/** - * @brief ISR for baudrate generator timer. NRZI encoding is done here. - */ -void TIM3_IRQHandler(void) __attribute__ ((interrupt)); -void TIM3_IRQHandler(void) -{ - TIM3->SR &= ~TIM_SR_UIF; - - if(txTestState == TEST_DISABLED) //transmitting normal data - { - if(Ax25GetTxBit() == 0) //get next bit and check if it's 0 - { - currentSymbol ^= 1; //change symbol - NRZI encoding - } - //if 1, no symbol change - } - else //transmit test mode - { - currentSymbol ^= 1; //change symbol - } - - TIM1->CNT = 0; - - if(currentSymbol) //current symbol is space - TIM1->ARR = spaceFreq; - else //mark - TIM1->ARR = markFreq; - -} - - -/** - * @brief Demodulate received sample (4x oversampling) - * @param[in] sample Received sample - * @param[in] *dem Demodulator state - * @return Current tone (0 or 1) - */ -static int32_t demodulate(int16_t sample, struct DemodState *dem) -{ - - dem->RMSenergy += ((sample >> 1) * (sample >> 1)); //square the sample and add it to the sum - dem->RMSsampleCount++; //increment number of samples - - if(dem->emphasis != EMPHASIS_NONE) //preemphasis/deemphasis is used - { - int32_t out = 0; //filtered output - - for(uint8_t i = BPF_TAPS - 1; i > 0; i--) - dem->rawSample[i] = dem->rawSample[i - 1]; //shift old samples - - dem->rawSample[0] = sample; //store new sample - for(uint8_t i = 0; i < BPF_TAPS; i++) - { - if(dem->emphasis == PREEMPHASIS) - out += bpfCoeffs[i] * dem->rawSample[i]; //use preemphasis - else - out += invBpfCoeffs[i] * dem->rawSample[i]; //use deemphasis - } - dem->rxSample[dem->rxSampleIdx] = (out >> 15); //store filtered sample - } - else //no pre/deemphasis - { - dem->rxSample[dem->rxSampleIdx] = sample; //store incoming sample - } - - dem->rxSampleIdx = (dem->rxSampleIdx + 1) % BPF_TAPS; //increment sample pointer and wrap around if needed - - int64_t outLoI = 0, outLoQ = 0, outHiI = 0, outHiQ = 0; //output values after correlating - - for(uint8_t i = 0; i < N; i++) { - int32_t t = dem->rxSample[(dem->rxSampleIdx + i) % BPF_TAPS]; //read sample - outLoI += t * coeffLoI[i]; //correlate sample - outLoQ += t * coeffLoQ[i]; - outHiI += t * coeffHiI[i]; - outHiQ += t * coeffHiQ[i]; - } - - uint64_t hi = 0, lo = 0; - - hi = ((outHiI >> 12) * (outHiI >> 12)) + ((outHiQ >> 12) * (outHiQ >> 12)); //calculate output tone levels - lo = ((outLoI >> 12) * (outLoI >> 12)) + ((outLoQ >> 12) * (outLoQ >> 12)); - - //DCD using PLL - //PLL is running nominally at 1200 Hz (= baudrate) - //PLL timer is counting up and eventually overflows to a minimal negative value - //so it crosses zero in the middle - //tone change should happen somewhere near this zero-crossing (in ideal case of exactly same TX and RX baudrates) - //nothing is ideal, so we need to have some region around zero where tone change is expected - //if tone changed inside this region, then we add something to the DCD pulse counter (and adjust counter phase for the counter to be closer to 0) - //if tone changes outside this region, then we subtract something from the DCD pulse counter - //if some DCD pulse threshold is reached, then we claim that the incoming signal is correct and set DCD flag - //when configured properly, it's generally immune to noise, as the detected tone changes much faster than 1200 baud - //it's also important to set some maximum value for DCD counter, otherwise the DCD is "sticky" - - dem->dcdPll = (signed)((unsigned)(dem->dcdPll) + ((unsigned)PLLINC)); //keep PLL ticking at the frequency equal to baudrate - - uint8_t dcdSymbol = (hi > lo); //get current symbol - - if(dcdSymbol != dem->dcdLastSymbol) //tone changed - { - if(abs(dem->dcdPll) < PLLINC) //tone change occurred near zero - dem->dcdCounter += DCD_INC; //increase DCD counter - else //tone change occurred far from zero - { - if(dem->dcdCounter >= DCD_DEC) //avoid overflow - dem->dcdCounter -= DCD_DEC; //decrease DCD counter - } - dem->dcdPll = (int)(dem->dcdPll * DCD_PLLTUNE); //adjust PLL - } - - dem->dcdLastSymbol = dcdSymbol; //store last symbol for symbol change detection - - if(dem->dcdCounter > DCD_MAXPULSE) //maximum DCD counter value reached - dem->dcdCounter = DCD_MAXPULSE; //avoid "sticky" DCD and counter overflow - - if(dem->dcdCounter > DCD_THRES) //DCD threshold reached - dem->dcd = 1; //DCD! - else //below DCD threshold - dem->dcd = 0; //no DCD - - //filter out signal faster than 1200 baud - int64_t out = 0; - - for(uint8_t i = LPF_TAPS - 1; i > 0; i--) - dem->lpfSample[i] = dem->lpfSample[i - 1]; - - dem->lpfSample[0] = (int64_t)hi - (int64_t)lo; - for(uint8_t i = 0; i < LPF_TAPS; i++) - { - out += lpfCoeffs[i] * dem->lpfSample[i]; - } - - return out > 0; -} - - - - -/** - * @brief Decode received symbol: bit recovery, NRZI decoding and pass the decoded bit to higher level protocol - * @param[in] symbol Received symbol - * @param demod Demodulator index - */ -static void decode(uint8_t symbol, uint8_t demod) -{ - struct DemodState *dem = (struct DemodState*)&demodState[demod]; - - //This function provides bit/clock recovery and NRZI decoding - //Bit recovery is based on PLL which is described in the function above (DCD PLL) - //Current symbol is sampled at PLL counter overflow, so symbol transition should occur at PLL counter zero - dem->lastPll = dem->pll; //store last clock state - - dem->pll = (signed)((unsigned)(dem->pll) + (unsigned)PLLINC); //keep PLL running - - dem->rawSymbols <<= 1; //store received unsynchronized symbol - dem->rawSymbols |= (symbol & 1); - - - if ((dem->pll < 0) && (dem->lastPll > 0)) //PLL counter overflow, sample symbol, decode NRZI and process in higher layer - { - dem->syncSymbols <<= 1; //shift recovered (received, synchronized) bit register - - uint8_t t = dem->rawSymbols & 0x07; //take last three symbols for sampling. Seems that 1 symbol is not enough, but 3 symbols work well - if(t == 0b111 || t == 0b110 || t == 0b101 || t == 0b011) //if there are 2 or 3 ones, then the received symbol is 1 - { - dem->syncSymbols |= 1; //push to recovered symbols register - } - //if there 2 or 3 zeros, no need to add anything to the register - - //NRZI decoding - if (((dem->syncSymbols & 0x03) == 0b11) || ((dem->syncSymbols & 0x03) == 0b00)) //two last symbols are the same - no symbol transition - decoded bit 1 - { - Ax25BitParse(1, demod); - } - else //symbol transition - decoded bit 0 - { - Ax25BitParse(0, demod); - } - } - - if(((dem->rawSymbols & 0x03) == 0b10) || ((dem->rawSymbols & 0x03) == 0b01)) //if there was a symbol transition, adjust PLL - { - - if(Ax25GetRxStage(demod) != RX_STAGE_FRAME) //not in a frame - { - dem->pll = (int)(dem->pll * PLLNOTLOCKED); //adjust PLL faster - } - else //in a frame - { - dem->pll = (int)(dem->pll * PLLLOCKED); //adjust PLL slower - } - } - -} - - - -void ModemTxTestStart(enum ModemTxTestMode type) -{ - if(txTestState != TEST_DISABLED) //TX test is already running - ModemTxTestStop(); //stop this test - - setPtt(1); //PTT on - txTestState = type; - - //DAC timer - TIM1->PSC = 17; //72/18=4 MHz - TIM1->DIER = TIM_DIER_UIE; //enable interrupt - TIM1->CR1 |= TIM_CR1_CEN; //enable timer - - TIM2->CR1 &= ~TIM_CR1_CEN; //disable RX timer - - NVIC_DisableIRQ(DMA1_Channel2_IRQn); //disable RX DMA interrupt - NVIC_EnableIRQ(TIM1_UP_IRQn); //enable timer 1 for PWM - - if(type == TEST_MARK) - { - TIM1->ARR = markFreq; - } else if(type == TEST_SPACE) - { - TIM1->ARR = spaceFreq; - } - else //alternating tones - { - //enable baudrate generator - TIM3->PSC = 71; //72/72=1 MHz - TIM3->DIER = TIM_DIER_UIE; //enable interrupt - TIM3->ARR = baudRate; //set timer interval - TIM3->CR1 = TIM_CR1_CEN; //enable timer - NVIC_EnableIRQ(TIM3_IRQn); //enable interrupt in NVIC - } -} - - -void ModemTxTestStop(void) -{ - txTestState = TEST_DISABLED; - - TIM3->CR1 &= ~TIM_CR1_CEN; //turn off timers - TIM1->CR1 &= ~TIM_CR1_CEN; - TIM2->CR1 |= TIM_CR1_CEN; //enable RX timer - - NVIC_DisableIRQ(TIM3_IRQn); - NVIC_DisableIRQ(TIM1_UP_IRQn); - NVIC_EnableIRQ(DMA1_Channel2_IRQn); - - setPtt(0); //PTT off -} - - -void ModemTransmitStart(void) -{ - setPtt(1); //PTT on - - TIM1->PSC = 17; - TIM1->DIER |= TIM_DIER_UIE; - - - TIM3->PSC = 71; - TIM3->DIER |= TIM_DIER_UIE; - TIM3->ARR = baudRate; - - TIM3->CR1 = TIM_CR1_CEN; - TIM1->CR1 = TIM_CR1_CEN; - TIM2->CR1 &= ~TIM_CR1_CEN; - - NVIC_DisableIRQ(DMA1_Channel2_IRQn); - NVIC_EnableIRQ(TIM1_UP_IRQn); - NVIC_EnableIRQ(TIM3_IRQn); -} - - -/** - * @brief Stop TX and go back to RX - */ -void ModemTransmitStop(void) -{ - - TIM2->CR1 |= TIM_CR1_CEN; - TIM3->CR1 &= ~TIM_CR1_CEN; - TIM1->CR1 &= ~TIM_CR1_CEN; - - NVIC_DisableIRQ(TIM1_UP_IRQn); - NVIC_DisableIRQ(TIM3_IRQn); - NVIC_EnableIRQ(DMA1_Channel2_IRQn); - - setPtt(0); - - TIM4->CCR1 = 44; //set around 50% duty cycle -} - -/** - * @brief Controls PTT output - * @param[in] state 0 - PTT off, 1 - PTT on - */ -static void setPtt(uint8_t state) -{ - if(state) - PTT_ON; - else - PTT_OFF; -} - - - -/** - * @brief Initialize AFSK module - */ -void ModemInit(void) -{ - /** - * TIM1 is used for pushing samples to DAC (R2R or PWM) at 4 MHz - * TIM3 is the baudrate generator for TX running at 1 MHz - * TIM4 is the PWM generator with no software interrupt - * TIM2 is the RX sampling timer with no software interrupt, but it directly calls DMA - */ - - RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; - RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; - RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; - RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; - RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; - RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; - RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; - RCC->AHBENR |= RCC_AHBENR_DMA1EN; - - - GPIOC->CRH |= GPIO_CRH_MODE13_1; //DCD LED on PC13 - GPIOC->CRH &= ~GPIO_CRH_MODE13_0; - GPIOC->CRH &= ~GPIO_CRH_CNF13; - - GPIOB->CRH &= ~0xFFFF0000; //R2R output on PB12-PB15 - GPIOB->CRH |= 0x22220000; - - GPIOA->CRL &= ~GPIO_CRL_CNF0; //ADC input on PA0 - GPIOA->CRL &= ~GPIO_CRL_MODE0; - - - GPIOB->CRL |= GPIO_CRL_MODE7_1; //PTT output on PB7 - GPIOB->CRL &= ~GPIO_CRL_MODE7_0; - GPIOB->CRL &= ~GPIO_CRL_CNF7; - - GPIOB->CRL |= GPIO_CRL_MODE5_1; //2nd DCD LED on PB5 - GPIOB->CRL &= ~GPIO_CRL_MODE5_0; - GPIOB->CRL &= ~GPIO_CRL_CNF5; - - - RCC->CFGR |= RCC_CFGR_ADCPRE_1; //ADC prescaler /6 - RCC->CFGR &= ~RCC_CFGR_ADCPRE_0; - - ADC1->CR2 |= ADC_CR2_CONT; //continuous conversion - ADC1->CR2 |= ADC_CR2_EXTSEL; - ADC1->SQR1 &= ~ADC_SQR1_L; //1 conversion - ADC1->SMPR2 |= ADC_SMPR2_SMP0_2; //41.5 cycle sampling - ADC1->SQR3 &= ~ADC_SQR3_SQ1; //channel 0 is first in the sequence - ADC1->CR2 |= ADC_CR2_ADON; //ADC on - - ADC1->CR2 |= ADC_CR2_RSTCAL; //calibrate ADC - while(ADC1->CR2 & ADC_CR2_RSTCAL) - ; - ADC1->CR2 |= ADC_CR2_CAL; - while(ADC1->CR2 & ADC_CR2_CAL) - ; - - ADC1->CR2 |= ADC_CR2_EXTTRIG; - ADC1->CR2 |= ADC_CR2_SWSTART; //start ADC conversion - - //prepare DMA - DMA1_Channel2->CCR |= DMA_CCR_MSIZE_0; //16 bit memory region - DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE_1; - DMA1_Channel2->CCR |= DMA_CCR_PSIZE_0; - DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE_1; - - DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC| DMA_CCR_TCIE; //circular mode, memory increment and interrupt - DMA1_Channel2->CNDTR = 4; //4 samples - DMA1_Channel2->CPAR = (uint32_t)&(ADC1->DR); //ADC data register address - DMA1_Channel2->CMAR = (uint32_t)samples; //sample buffer address - DMA1_Channel2->CCR |= DMA_CCR_EN; //enable DMA - - NVIC_EnableIRQ(DMA1_Channel2_IRQn); - - TIM2->PSC = 17; //72/18=4 MHz - TIM2->DIER |= TIM_DIER_UDE; //enable calling DMA on timer tick - TIM2->ARR = 103; //4MHz / 104 =~38400 Hz (4*9600 Hz for 4x oversampling) - TIM2->CR1 |= TIM_CR1_CEN; //enable timer - - markFreq = 4000000 / (DAC_SINE_SIZE * (uint32_t)MODEM_MARK_FREQUENCY) - 1; //set mark frequency - spaceFreq = 4000000 / (DAC_SINE_SIZE * (uint32_t)MODEM_SPACE_FREQUENCY) - 1; //set space frequency - baudRate = 1000000 / (uint32_t)MODEM_BAUDRATE - 1; //set baudrate - - for(uint8_t i = 0; i < N; i++) //calculate correlator coefficients - { - coeffLoI[i] = 4095.f * cosf(2.f * 3.1416f * (float)i / (float)N * MODEM_MARK_FREQUENCY / MODEM_BAUDRATE); - coeffLoQ[i] = 4095.f * sinf(2.f * 3.1416f * (float)i / (float)N * MODEM_MARK_FREQUENCY / MODEM_BAUDRATE); - coeffHiI[i] = 4095.f * cosf(2.f * 3.1416f * (float)i / (float)N * MODEM_SPACE_FREQUENCY / MODEM_BAUDRATE); - coeffHiQ[i] = 4095.f * sinf(2.f * 3.1416f * (float)i / (float)N * MODEM_SPACE_FREQUENCY / MODEM_BAUDRATE); - } - - - for(uint8_t i = 0; i < DAC_SINE_SIZE; i++) //calculate DAC sine samples - { - if(ModemConfig.usePWM) - dacSine[i] = ((sinf(2.f * 3.1416f * (float)i / (float)DAC_SINE_SIZE) + 1.f) * 45.f); - else - dacSine[i] = ((7.f * sinf(2.f * 3.1416f * (float)i / (float)DAC_SINE_SIZE)) + 8.f); - } - - if(ModemConfig.flatAudioIn) //when used with flat audio input, use deemphasis and flat modems - { - demodState[0].emphasis = EMPHASIS_NONE; - demodState[1].emphasis = DEEMPHASIS; - } - else //when used with normal (filtered) audio input, use flat and preemphasis modems - { - demodState[0].emphasis = EMPHASIS_NONE; - demodState[1].emphasis = PREEMPHASIS; - } - - if(ModemConfig.usePWM) - { - RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; //configure timer - - GPIOB->CRL |= GPIO_CRL_CNF6_1; //configure pin for PWM - GPIOB->CRL |= GPIO_CRL_MODE6; - GPIOB->CRL &= ~GPIO_CRL_CNF6_0; - - //set up PWM generation - TIM4->PSC = 7; //72MHz/8=9MHz - TIM4->ARR = 90; //9MHz/90=100kHz - - TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; - TIM4->CCER |= TIM_CCER_CC1E; - TIM4->CCR1 = 44; //initial duty cycle - - TIM4->CR1 |= TIM_CR1_CEN; - } - -} - - - - - -/** - * @} - */ diff --git a/Src/drivers/uart.c b/Src/drivers/uart.c deleted file mode 100644 index 69c544d..0000000 --- a/Src/drivers/uart.c +++ /dev/null @@ -1,226 +0,0 @@ -/* -Copyright 2020-2023 Piotr Wilkon - -This file is part of VP-Digi. - -VP-Digi is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -VP-Digi is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with VP-Digi. If not, see . -*/ - -#include "drivers/uart.h" -#include "drivers/systick.h" -#include "terminal.h" -#include "ax25.h" -#include "common.h" -#include -#include "digipeater.h" -#include "kiss.h" - -Uart Uart1, Uart2, UartUsb; - -static void handleInterrupt(Uart *port) -{ - if(port->port->SR & USART_SR_RXNE) //byte received - { - port->port->SR &= ~USART_SR_RXNE; - uint8_t data = port->port->DR; - port->rxBuffer[port->rxBufferHead++] = data; //store it - port->rxBufferHead %= UART_BUFFER_SIZE; - - KissParse(port, data); - TermHandleSpecial(port); - } - if(port->port->SR & USART_SR_IDLE) //line is idle, end of data reception - { - port->port->DR; //reset idle flag by dummy read - if(port->rxBufferHead != 0) - { - if(((port->rxBuffer[port->rxBufferHead - 1] == '\r') || (port->rxBuffer[port->rxBufferHead - 1] == '\n'))) //data ends with \r or \n, process as data - { - port->rxType = DATA_TERM; - } - } - } - if(port->port->SR & USART_SR_TXE) //TX buffer empty - { - if((port->txBufferHead != port->txBufferTail) || port->txBufferFull) //if there is anything to transmit - { - port->port->DR = port->txBuffer[port->txBufferTail++]; //push it to the register - port->txBufferTail %= UART_BUFFER_SIZE; - port->txBufferFull = 0; - } - else //nothing more to be transmitted - { - port->port->CR1 &= ~USART_CR1_TXEIE; - } - } -} - -void USART1_IRQHandler(void) __attribute__ ((interrupt)); -void USART1_IRQHandler(void) -{ - handleInterrupt(&Uart1); -} - -void USART2_IRQHandler(void) __attribute__ ((interrupt)); -void USART2_IRQHandler(void) -{ - handleInterrupt(&Uart2); -} - - -void UartSendByte(Uart *port, uint8_t data) -{ - if(!port->enabled) - return; - - if(port->isUsb) - { - CDC_Transmit_FS(&data, 1); - } - else - { - while(port->txBufferFull) - ; - port->txBuffer[port->txBufferHead++] = data; - port->txBufferHead %= UART_BUFFER_SIZE; - if(port->txBufferHead == port->txBufferTail) - port->txBufferFull = 1; - if(0 == (port->port->CR1 & USART_CR1_TXEIE)) - port->port->CR1 |= USART_CR1_TXEIE; - } -} - - -void UartSendString(Uart *port, void *data, uint16_t len) -{ - if(0 == len) - len = strlen((char*)data); - - for(uint16_t i = 0; i < len; i++) - { - UartSendByte(port, ((uint8_t*)data)[i]); - } -} - - -static unsigned int findHighestPosition(unsigned int n) -{ - unsigned int i = 1; - while((i * 10) <= n) - i *= 10; - - return i; -} - -void UartSendNumber(Uart *port, int32_t n) -{ - if(n < 0) - UartSendByte(port, '-'); - n = abs(n); - unsigned int position = findHighestPosition(n); - while(position) - { - unsigned int number = n / position; - UartSendByte(port, (number + 48)); - n -= (number * position); - position /= 10; - } -} - -void UartInit(Uart *port, USART_TypeDef *uart, uint32_t baud) -{ - port->port = uart; - port->baudrate = baud; - port->rxType = DATA_NOTHING; - port->rxBufferHead = 0; - port->txBufferHead = 0; - port->txBufferTail = 0; - port->txBufferFull = 0; - port->kissBufferHead = 0; - port->mode = MODE_KISS; - port->enabled = 0; - port->lastRxBufferHead = 0; - memset(port->rxBuffer, 0, sizeof(port->rxBuffer)); - memset(port->txBuffer, 0, sizeof(port->txBuffer)); - memset(port->kissBuffer, 0, sizeof(port->kissBuffer)); -} - - -void UartConfig(Uart *port, uint8_t state) -{ - if(port->port == USART1) - { - RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; - RCC->APB2ENR |= RCC_APB2ENR_USART1EN; - GPIOA->CRH |= GPIO_CRH_MODE9_1; - GPIOA->CRH &= ~GPIO_CRH_CNF9_0; - GPIOA->CRH |= GPIO_CRH_CNF9_1; - GPIOA->CRH |= GPIO_CRH_CNF10_0; - GPIOA->CRH &= ~GPIO_CRH_CNF10_1; - - USART1->BRR = (SystemCoreClock / (port->baudrate)); - - if(state) - USART1->CR1 |= USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_IDLEIE; - else - USART1->CR1 &= (~USART_CR1_RXNEIE) & (~USART_CR1_TE) & (~USART_CR1_RE) & (~USART_CR1_UE) & (~USART_CR1_IDLEIE); - - NVIC_SetPriority(USART1_IRQn, 2); - if(state) - NVIC_EnableIRQ(USART1_IRQn); - else - NVIC_DisableIRQ(USART1_IRQn); - - port->enabled = state > 0; - port->isUsb = 0; - } - else if(port->port == USART2) - { - RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; - RCC->APB1ENR |= RCC_APB1ENR_USART2EN; - GPIOA->CRL |= GPIO_CRL_MODE2_1; - GPIOA->CRL &= ~GPIO_CRL_CNF2_0; - GPIOA->CRL |= GPIO_CRL_CNF2_1; - GPIOA->CRL |= GPIO_CRL_CNF3_0; - GPIOA->CRL &= ~GPIO_CRL_CNF3_1; - - USART2->BRR = (SystemCoreClock / (port->baudrate * 2)); - if(state) - USART2->CR1 |= USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_IDLEIE; - else - USART2->CR1 &= (~USART_CR1_RXNEIE) & (~USART_CR1_TE) & (~USART_CR1_RE) & (~USART_CR1_UE) & (~USART_CR1_IDLEIE); - - NVIC_SetPriority(USART2_IRQn, 2); - if(state) - NVIC_EnableIRQ(USART2_IRQn); - else - NVIC_DisableIRQ(USART2_IRQn); - - port->enabled = state > 0; - port->isUsb = 0; - } - else - { - port->isUsb = 1; - port->enabled = state > 0; - } - -} - - -void UartClearRx(Uart *port) -{ - port->rxBufferHead = 0; - port->rxType = DATA_NOTHING; -}