mirror of
https://github.com/adrcs/ip400.git
synced 2025-04-20 18:53:43 +03:00
Initial R.04 code
This commit is contained in:
parent
418d345a71
commit
eb70cb939e
58
code/IP400/Inc/dataq.h
Normal file
58
code/IP400/Inc/dataq.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
Module: <module description here>
|
||||||
|
|
||||||
|
File Name: queue.h
|
||||||
|
|
||||||
|
Date Created: Jan 9, 2025
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: <what it does>
|
||||||
|
|
||||||
|
Copyright © 2024-25, Alberta Digital Radio Communications Society,
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef INC_DATAQ_H_
|
||||||
|
#define INC_DATAQ_H_
|
||||||
|
|
||||||
|
#ifndef __CCALL
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define __CCALL extern "C"
|
||||||
|
#else
|
||||||
|
#define __CCALL
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Q_TYPECAST (struct qelem *)
|
||||||
|
|
||||||
|
// queue structure
|
||||||
|
typedef struct qelem {
|
||||||
|
struct qelem *q_forw;
|
||||||
|
struct qelem *q_back;
|
||||||
|
} QUEUE_ELEM;
|
||||||
|
|
||||||
|
// frame data queue
|
||||||
|
typedef struct frame_data_queue_t {
|
||||||
|
struct frame_data_queue_t *q_forw; // forwared pointer
|
||||||
|
struct frame_data_queue_t *q_back; // backward pointer
|
||||||
|
void *frame; // IP400 Frame
|
||||||
|
uint16_t length; // length (in some cases)
|
||||||
|
} FRAME_QUEUE;
|
||||||
|
|
||||||
|
// queue functions
|
||||||
|
BOOL enqueFrame(FRAME_QUEUE *que, IP400_FRAME *fr);
|
||||||
|
IP400_FRAME *dequeFrame(FRAME_QUEUE *que);
|
||||||
|
|
||||||
|
// queue management
|
||||||
|
void insque (struct qelem *elem, struct qelem *pred);
|
||||||
|
void remque (struct qelem *elem);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_DATAQ_H_ */
|
195
code/IP400/Inc/frame.h
Normal file
195
code/IP400/Inc/frame.h
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
Module: <module description here>
|
||||||
|
|
||||||
|
File Name: frame.h
|
||||||
|
|
||||||
|
Date Created: Jan 8, 2025
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: <what it does>
|
||||||
|
|
||||||
|
Copyright © 2024-25, Alberta Digital Radio Communications Society,
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef FRAME_H_
|
||||||
|
#define FRAME_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
// frame defines
|
||||||
|
#define N_CALL 4 // octets in the excess-40 compressed callsign
|
||||||
|
#define MAX_CALL 6 // max callsign size
|
||||||
|
#define EXT_CALL 4*MAX_CALL // extended call sign field
|
||||||
|
#define PAYLOAD_MIN 56 // min octets in payload
|
||||||
|
#define PAYLOAD_MAX 1053 // max octets in payload
|
||||||
|
#define MAX_CALL_BUFFER 20 // max buffering for callsign in payload
|
||||||
|
#define N_FEC 4 // number of bytes in the FEC
|
||||||
|
#define BROADCAST_ADDR 0xFF // broadcast address
|
||||||
|
|
||||||
|
#define MAX_HOP_COUNT 15 // max hop count
|
||||||
|
|
||||||
|
// radio error register
|
||||||
|
#define SEQ_COMPLETE_ERR 0x8000 // Sequencer error
|
||||||
|
#define SEQ_ACT_TIMEOUT 0x4000 // Sequencer action timeout
|
||||||
|
#define PLL_CALAMP_ERR 0x0800 // VCO amplitude calibration error
|
||||||
|
#define PLL_CALFREQ_ERR 0x0400 // VCO frequency calibration error
|
||||||
|
#define PLL_UNLOCK_ERR 0x0200 // PLL is unlocked
|
||||||
|
#define PLL_LOCK_FAIL 0x0100 // PLL lock failure
|
||||||
|
#define DBM_FIFO_ERR 0x0020 // Data buffer failure
|
||||||
|
#define N_RADIO_ERRS 7 // number of the above
|
||||||
|
|
||||||
|
// radio FSM states
|
||||||
|
enum fsm_states_e {
|
||||||
|
FSM_IDLE=0, // idle
|
||||||
|
FSM_ENA_RF_REG, // enable RF registers
|
||||||
|
FSM_WAIT_ACTIVE2, // wait for active 2
|
||||||
|
FSM_ACTIVE2, // active 2
|
||||||
|
FSM_ENA_CURR, // enable current
|
||||||
|
FSM_SYNTH_SETUP, // synth setup
|
||||||
|
FSM_CALIB_VCO, // VCO calibration
|
||||||
|
FSM_LOCKRXTX, // lock Rx and Rx
|
||||||
|
FSM_LOCKONTX, // lock on Rx
|
||||||
|
FSM_EN_PA, // enable PA
|
||||||
|
FSM_TX, // transmit
|
||||||
|
FSM_PA_DWN_ANA, // Analog power down
|
||||||
|
FSM_END_TX, // end transmit
|
||||||
|
FSM_LOCKONRX, // lock on Rx
|
||||||
|
FSM_EN_RX, // Enable Rx
|
||||||
|
FSM_EN_LNA, // enable LNA
|
||||||
|
FSM_RX, // recieve
|
||||||
|
FSM_END_RX, // end rx
|
||||||
|
FSM_SYNTH_PWDN, // synth power down
|
||||||
|
FSM_N_FSM_STATES
|
||||||
|
};
|
||||||
|
|
||||||
|
// header flags
|
||||||
|
typedef struct frame_flags_t {
|
||||||
|
unsigned hop_count: 4; // hop count
|
||||||
|
unsigned coding: 4; // coding method
|
||||||
|
unsigned compression: 2; // compression method
|
||||||
|
unsigned unused: 1; // unused
|
||||||
|
unsigned srcExt: 1; // source call is extended
|
||||||
|
unsigned destExt: 1; // dest call is extended
|
||||||
|
unsigned command: 1; // command packet
|
||||||
|
unsigned noconnect: 1; // connectionless
|
||||||
|
unsigned repeat: 1; // repeat flag
|
||||||
|
} IP400_FLAGS;
|
||||||
|
|
||||||
|
// callsign field
|
||||||
|
typedef struct callsign_t {
|
||||||
|
union {
|
||||||
|
uint8_t bytes[N_CALL]; // compressed callsign
|
||||||
|
uint32_t encoded;
|
||||||
|
} callbytes;
|
||||||
|
uint16_t port; // port information
|
||||||
|
} IP400_CALL;
|
||||||
|
|
||||||
|
// complete frame
|
||||||
|
typedef struct ip400_frame_t {
|
||||||
|
IP400_CALL source; // source call sign
|
||||||
|
IP400_CALL dest; // destination call sign
|
||||||
|
union {
|
||||||
|
IP400_FLAGS flags; // flag bit field
|
||||||
|
uint16_t allflags; // all flags
|
||||||
|
} flagfld;
|
||||||
|
uint32_t seqNum; // packet sequence number
|
||||||
|
void *buf; // data to send
|
||||||
|
uint16_t length; // data length
|
||||||
|
} IP400_FRAME;
|
||||||
|
|
||||||
|
// min/max payload sizes
|
||||||
|
#define IP_400_CALL_SIZE N_CALL + sizeof(uint16_t)
|
||||||
|
#define IP_400_FLAG_SIZE sizeof(uint16_t)
|
||||||
|
#define IP_400_HDR_SIZE (2*IP_400_CALL_SIZE + IP_400_FLAG_SIZE)
|
||||||
|
#define IP_400_LEN_SIZE sizeof(uint16_t)
|
||||||
|
#define MIN_FRAME_SIZE sizeof(IP400_FRAME) + PAYLOAD_MIN + N_FEC
|
||||||
|
#define MAX_FRAME_SIZE MIN_FRAME_SIZE - PAYLOAD_MIN + PAYLOAD_MAX
|
||||||
|
// packet types
|
||||||
|
enum {
|
||||||
|
UTF8_TEXT_PACKET=0, // Text packet
|
||||||
|
COMPRESSED_AUDIO, // compressed audio packet
|
||||||
|
COMPREESSD_VIDEO, // compressed video packet
|
||||||
|
DATA_PACKET, // data packet
|
||||||
|
PING_PACKET, // ping packet
|
||||||
|
IP_ENCAPSULATED, // IP encapsulated packet
|
||||||
|
AX_25_PACKET, // AX.25 encapsulated packet
|
||||||
|
RFC4733_DTMF, // DTMF packet
|
||||||
|
DMR_FRAME, // DMR Frame
|
||||||
|
DSTAR_FRAME, // Dstar Frame
|
||||||
|
P25_FRAME, // TIA project 25
|
||||||
|
NXDN_FRAME, // NXDN
|
||||||
|
M17_FRAME, // M17
|
||||||
|
TBD_1,
|
||||||
|
TBD_2,
|
||||||
|
LOCAL_COMMAND // local command frame
|
||||||
|
};
|
||||||
|
|
||||||
|
// audio compression types
|
||||||
|
enum {
|
||||||
|
AUDIO_RAW, // raw 16-bit PCM
|
||||||
|
AUDIO_ULAW, // mu law compression
|
||||||
|
AUDIO_CODEC2, // codec 2 encoded
|
||||||
|
AUDIO_AMBE // AMBE encoded
|
||||||
|
};
|
||||||
|
|
||||||
|
// H.246 Video compression
|
||||||
|
enum {
|
||||||
|
H264_240_180_24, // H.264: 240x180, 24FPS
|
||||||
|
H264_320_240_24, // H.264: 320x240, 24FPS
|
||||||
|
H264_480_360_12, // H.264: 480x360, 12FPS
|
||||||
|
H264_640_480_6 // H.264: 640x480, 6FPS
|
||||||
|
};
|
||||||
|
|
||||||
|
// callsign fields
|
||||||
|
enum {
|
||||||
|
SRC_CALLSIGN=0, // dest for encode is source callsign
|
||||||
|
DEST_CALLSIGN // dest for encode is dest callsign
|
||||||
|
};
|
||||||
|
|
||||||
|
// frame stats
|
||||||
|
typedef struct frame_stats_t {
|
||||||
|
uint32_t TxFrameCnt; // transmit frame count
|
||||||
|
uint32_t RxFrameCnt; // good receive frame count
|
||||||
|
uint32_t CRCErrors; // CRC Errors
|
||||||
|
uint32_t TimeOuts; // Timeouts
|
||||||
|
uint32_t lastRSSI; // last RSSI reading
|
||||||
|
uint32_t framesOK; // processed frames
|
||||||
|
uint32_t dropped; // rejected frames
|
||||||
|
uint32_t duplicates; // duplicates
|
||||||
|
uint32_t nBeacons; // number of beacons processed
|
||||||
|
uint32_t nRepeated; // repeated frames
|
||||||
|
} FRAME_STATS;
|
||||||
|
|
||||||
|
// packet types
|
||||||
|
#define ICMP_TYPE 1 // control message type
|
||||||
|
#define ENC_IP_TYPE 4 // encapsulated IP
|
||||||
|
#define AUDIO_TYPE 11 // audio packets
|
||||||
|
#define TEXT_TYPE 17 // port for text chat (UDP)
|
||||||
|
#define VIDEO_TYPE 75 // H.264 video
|
||||||
|
#define ENC_AX25_TYPE 93 // encapsulated AX25 packets
|
||||||
|
|
||||||
|
// references
|
||||||
|
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset);
|
||||||
|
BOOL callDecode(IP400_CALL *encCall, char *callsign, uint16_t *port);
|
||||||
|
|
||||||
|
// frame senders
|
||||||
|
BOOL SendTextFrame(char *srcCall, uint16_t srcPort, char *destCall, uint16_t dstPort, char *buf, uint16_t length, BOOL repeat);
|
||||||
|
void SendBeaconFrame(char *srcCall, uint8_t *payload, int bcnlen);
|
||||||
|
|
||||||
|
//
|
||||||
|
BOOL EnqueChatFrame(void *Frame, int length); // queue a chat frame
|
||||||
|
FRAME_STATS *GetFrameStats(void); // return the frame stats
|
||||||
|
uint32_t GetRadioStatus(void); // get the radio status
|
||||||
|
uint8_t GetFSMState(void); // get FSM state
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* FRAME_H_ */
|
43
code/IP400/Inc/led.h
Normal file
43
code/IP400/Inc/led.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: led.h
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 26, 2025
|
||||||
|
|
||||||
|
Description: Definitions for the LED module
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#ifndef INC_LED_H_
|
||||||
|
#define INC_LED_H_
|
||||||
|
|
||||||
|
// LED functions
|
||||||
|
enum {
|
||||||
|
|
||||||
|
BICOLOR_OFF=0, // turn LED off
|
||||||
|
BICOLOR_RED, // red mode steady
|
||||||
|
BICOLOR_RED_FLASH, // red mode flashing
|
||||||
|
BICOLOR_GREEN, // green mode steady
|
||||||
|
BICOLOR_GREEN_FLASH, // green mode flashing
|
||||||
|
BICOLOR_RED_GREEN, // alternate RED/GREEN
|
||||||
|
TX_LED_ON, // tx LED on
|
||||||
|
TX_LED_OFF, // tx LED OFF
|
||||||
|
N_LED_MODE // modes1
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetLEDMode(uint8_t mode);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_LED_H_ */
|
62
code/IP400/Inc/newlib-freertos.h
Normal file
62
code/IP400/Inc/newlib-freertos.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.6.2
|
||||||
|
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_NEWLIB_FREERTOS_H
|
||||||
|
#define INC_NEWLIB_FREERTOS_H
|
||||||
|
|
||||||
|
/* Note Newlib support has been included by popular demand, but is not
|
||||||
|
* used by the FreeRTOS maintainers themselves. FreeRTOS is not
|
||||||
|
* responsible for resulting newlib operation. User must be familiar with
|
||||||
|
* newlib and must provide system-wide implementations of the necessary
|
||||||
|
* stubs. Be warned that (at the time of writing) the current newlib design
|
||||||
|
* implements a system-wide malloc() that must be provided with locks.
|
||||||
|
*
|
||||||
|
* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
|
||||||
|
* for additional information. */
|
||||||
|
|
||||||
|
#include <reent.h>
|
||||||
|
|
||||||
|
#define configUSE_C_RUNTIME_TLS_SUPPORT 1
|
||||||
|
|
||||||
|
#ifndef configTLS_BLOCK_TYPE
|
||||||
|
#define configTLS_BLOCK_TYPE struct _reent
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configINIT_TLS_BLOCK
|
||||||
|
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) _REENT_INIT_PTR( &( xTLSBlock ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configSET_TLS_BLOCK
|
||||||
|
#define configSET_TLS_BLOCK( xTLSBlock ) ( _impure_ptr = &( xTLSBlock ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef configDEINIT_TLS_BLOCK
|
||||||
|
#define configDEINIT_TLS_BLOCK( xTLSBlock ) _reclaim_reent( &( xTLSBlock ) )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* INC_NEWLIB_FREERTOS_H */
|
132
code/IP400/Inc/setup.h
Normal file
132
code/IP400/Inc/setup.h
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: setup.h
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 13, 2025
|
||||||
|
|
||||||
|
Description: Definitions for setup data
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#ifndef INC_SETUP_H_
|
||||||
|
#define INC_SETUP_H_
|
||||||
|
|
||||||
|
#include <stm32wl3x_hal_mrsubg.h>
|
||||||
|
|
||||||
|
#include "frame.h"
|
||||||
|
#include "usart.h"
|
||||||
|
|
||||||
|
#define __USE_SETUP_PARAMS 1 // set to 1 to use setup parameters
|
||||||
|
|
||||||
|
// defined elsewhere
|
||||||
|
extern char *modTypes[];
|
||||||
|
extern char *paModes[];
|
||||||
|
|
||||||
|
typedef struct setup_flags_t {
|
||||||
|
unsigned fsk: 1; // can run FSK
|
||||||
|
unsigned ofdm: 1; // can run OFDM
|
||||||
|
unsigned aredn: 1; // is an AREDN node
|
||||||
|
unsigned repeat: 1; // repeat mode default
|
||||||
|
unsigned ext: 1; // use extended callsign
|
||||||
|
unsigned rate: 3; // data rate in use
|
||||||
|
} SETUP_FLAGS;
|
||||||
|
|
||||||
|
// data rates in the flag field (FSK mode)
|
||||||
|
enum {
|
||||||
|
FSK_1200,
|
||||||
|
FSK_9600,
|
||||||
|
FSK_56K,
|
||||||
|
FSK_100K,
|
||||||
|
FSK_200K,
|
||||||
|
FSK_400K,
|
||||||
|
FSK_600K
|
||||||
|
};
|
||||||
|
|
||||||
|
// setup data struct
|
||||||
|
typedef struct setup_data_t {
|
||||||
|
SETUP_FLAGS flags; // flags
|
||||||
|
char stnCall[MAX_CALL]; // station call sign
|
||||||
|
char extCall[EXT_CALL]; // extended call sign
|
||||||
|
char latitude[10]; // latititude
|
||||||
|
char longitude[10]; // longitude
|
||||||
|
char gridSq[10]; // grid square
|
||||||
|
uint16_t beaconInt; // beacon interval
|
||||||
|
} SETUP_DATA;
|
||||||
|
|
||||||
|
// Radio setup struct
|
||||||
|
typedef struct radio_setup_t {
|
||||||
|
uint32_t lFrequencyBase; /*!< Specifies the base carrier frequency (in Hz) */
|
||||||
|
MRSubGModSelect xModulationSelect; /*!< Specifies the modulation @ref MRSubGModSelect */
|
||||||
|
uint32_t lDatarate; /*!< Specifies the datarate expressed in sps.*/
|
||||||
|
uint32_t lFreqDev; /*!< Specifies the frequency deviation expressed in Hz. */
|
||||||
|
uint32_t lBandwidth; /*!< Specifies the channel filter bandwidth expressed in Hz. */
|
||||||
|
uint8_t dsssExp; /*!< Specifies the DSSS spreading exponent. Use 0 to disable DSSS. */
|
||||||
|
uint8_t outputPower; /*!< PA value to write expressed in dBm. */
|
||||||
|
MRSubG_PA_DRVMode PADrvMode; /*!< PA drive mode. */
|
||||||
|
int16_t rxSquelch; // rx squelch level
|
||||||
|
} RADIO_SETUP;
|
||||||
|
|
||||||
|
// setup struct
|
||||||
|
typedef struct stn_params_t {
|
||||||
|
SETUP_DATA setup_data; // basic setup data
|
||||||
|
RADIO_SETUP radio_setup; // radio setup
|
||||||
|
uint8_t FirmwareVerMajor; // firmware major rev
|
||||||
|
uint8_t FirmwareVerMinor; // firmware minor vers
|
||||||
|
uint32_t Magic; // magic number: "DEBEADEF"
|
||||||
|
uint32_t SetupCRC; // CRC
|
||||||
|
} STN_PARAMS;
|
||||||
|
|
||||||
|
// beacon header
|
||||||
|
typedef union {
|
||||||
|
struct beacon_hdr_t {
|
||||||
|
SETUP_FLAGS flags;
|
||||||
|
uint8_t txPower;
|
||||||
|
} setup;
|
||||||
|
uint8_t hdrBytes[sizeof(struct beacon_hdr_t)];
|
||||||
|
} BEACON_HEADER;
|
||||||
|
|
||||||
|
#define SETUP_MAGIC 0xDEBEADEF // magic number
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
STN_PARAMS params;
|
||||||
|
uint8_t bytes[sizeof(STN_PARAMS)];
|
||||||
|
uint32_t flashwords[sizeof(STN_PARAMS)/sizeof(uint32_t)];
|
||||||
|
} SETUP_MEMORY;
|
||||||
|
|
||||||
|
extern SETUP_MEMORY setup_memory;
|
||||||
|
extern SETUP_MEMORY def_params;
|
||||||
|
extern CRC_HandleTypeDef hcrc;
|
||||||
|
|
||||||
|
// validations used by key entry
|
||||||
|
#if US
|
||||||
|
#define MIN_FREQ 420000000 // min freq (US only)
|
||||||
|
#else
|
||||||
|
#define MIN_FREQ 430000000 // min freq (CAN only)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// links in
|
||||||
|
void printStationSetup(void); // print setup struct
|
||||||
|
void printRadioSetup(void); // print radio setup
|
||||||
|
char *GetMyCall(void); // return the station's callsign
|
||||||
|
STN_PARAMS *GetStationParams(void); // get the station params
|
||||||
|
BOOL CompareToMyCall(char *call);
|
||||||
|
//
|
||||||
|
BOOL VerifySetup(void);
|
||||||
|
BOOL ReadSetup(void);
|
||||||
|
BOOL WriteSetup(void);
|
||||||
|
void SetDefSetup(void);
|
||||||
|
BOOL UpdateSetup(void);
|
||||||
|
|
||||||
|
#endif /* INC_SETUP_H_ */
|
18
code/IP400/Inc/streambuffer.h
Normal file
18
code/IP400/Inc/streambuffer.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* streambuffer.h
|
||||||
|
*
|
||||||
|
* Created on: Jan 11, 2025
|
||||||
|
* Author: MartinA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_STREAMBUFFER_H_
|
||||||
|
#define INC_STREAMBUFFER_H_
|
||||||
|
|
||||||
|
// data buffer defs
|
||||||
|
typedef uint8_t DATA_ELEMENT; // data element
|
||||||
|
#define BUFFER_EMPTY(x) (x.nDataBytes == 0)
|
||||||
|
#define BUFFER_NO_DATA 0
|
||||||
|
#define bufferSIZE 2048 // large buffer
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_STREAMBUFFER_H_ */
|
40
code/IP400/Inc/tod.h
Normal file
40
code/IP400/Inc/tod.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: tod.h
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 23, 2025
|
||||||
|
|
||||||
|
Description: <decription here?
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#ifndef INC_TOD_H_
|
||||||
|
#define INC_TOD_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// TOD struct
|
||||||
|
typedef struct tod_t {
|
||||||
|
uint8_t Seconds;
|
||||||
|
uint8_t Minutes;
|
||||||
|
uint8_t Hours;
|
||||||
|
} TIMEOFDAY;
|
||||||
|
|
||||||
|
// links in
|
||||||
|
void TOD_10SecTimer(void); // 10 second timer
|
||||||
|
void getTOD(TIMEOFDAY *time);
|
||||||
|
BOOL setTOD(char *todString);
|
||||||
|
|
||||||
|
#endif /* INC_TOD_H_ */
|
54
code/IP400/Inc/types.h
Normal file
54
code/IP400/Inc/types.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
Module: Type definitions
|
||||||
|
|
||||||
|
File Name: types.h
|
||||||
|
|
||||||
|
Date Created: Jan 9, 2025
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: Useful data types
|
||||||
|
|
||||||
|
Copyright © 2024-25, Alberta Digital Radio Communications Society,
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef INC_TYPES_H_
|
||||||
|
#define INC_TYPES_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// boolean type
|
||||||
|
#ifndef BOOL
|
||||||
|
typedef uint8_t BOOL; // boolean
|
||||||
|
typedef uint8_t BOOLEAN; // alternate
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE ((BOOL)1U)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE ((BOOL)0U)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __CCALL
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define __CCALL extern "C"
|
||||||
|
#else
|
||||||
|
#define __CCALL
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// macros
|
||||||
|
#define islower(c) ((c >= 'a') && (c <= 'z'))
|
||||||
|
#define toupper(c) (c-0x20)
|
||||||
|
|
||||||
|
#endif /* INC_TYPES_H_ */
|
54
code/IP400/Inc/usart.h
Normal file
54
code/IP400/Inc/usart.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: usart.h
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 12, 2025
|
||||||
|
|
||||||
|
Description: <decription here?
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef INC_USART_H_
|
||||||
|
#define INC_USART_H_
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
// definitions
|
||||||
|
typedef uint32_t UART_TIMEOUT_T; // uart timer type
|
||||||
|
typedef uint16_t BUFFER_SIZE_T; // buffer size type
|
||||||
|
typedef uint8_t DATA_ELEMENT; // buffer data element
|
||||||
|
|
||||||
|
// uart HAL handle
|
||||||
|
extern UART_HandleTypeDef huart1;
|
||||||
|
|
||||||
|
// logger defines
|
||||||
|
#define LOG_NOTICE 0 // notice
|
||||||
|
#define LOG_ERROR 1 // error
|
||||||
|
#define LOG_SEVERE 2 // severe error
|
||||||
|
|
||||||
|
void logger(int severity, char *format, ...);
|
||||||
|
|
||||||
|
// API calls
|
||||||
|
void USART_RxBuffer_reset(void);
|
||||||
|
size_t databuffer_bytesInBuffer(void);
|
||||||
|
DATA_ELEMENT databuffer_get(UART_TIMEOUT_T timeout);
|
||||||
|
BOOL databuffer_contains(const char *tag, UART_TIMEOUT_T rx_timeout, BOOL saveData, char *SaveBuffer);
|
||||||
|
BOOL USART_Send_String(const char *string, size_t len);
|
||||||
|
BOOL USART_Send_Char(const char c);
|
||||||
|
void USART_Print_string(char *format, ...);
|
||||||
|
|
||||||
|
#endif /* INC_USART_H_ */
|
39
code/IP400/Inc/utils.h
Normal file
39
code/IP400/Inc/utils.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: utils.h
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 20, 2025
|
||||||
|
|
||||||
|
Description: Utility routines
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) 2024-25 Alberta Digital Radio Communications Society
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#ifndef INC_UTILS_H_
|
||||||
|
#define INC_UTILS_H_
|
||||||
|
|
||||||
|
// replacement for missing itoa
|
||||||
|
int ascii2Dec(char *dec);
|
||||||
|
|
||||||
|
// and its double counterpart...
|
||||||
|
double ascii2double(char *val);
|
||||||
|
|
||||||
|
// check an entry for floating point
|
||||||
|
BOOL isfloat(char *val);
|
||||||
|
|
||||||
|
// useful in parsing NMEA sentences
|
||||||
|
int explode_string(char *str, char *strp[], int limit, char delim, char quote);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* INC_UTILS_H_ */
|
244
code/IP400/Src/beacon.c
Normal file
244
code/IP400/Src/beacon.c
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: beacon.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: Beacon task. Sends a beacon frame periodically, based on timer in the setup
|
||||||
|
structure. The beacon include position data in a readable format, which
|
||||||
|
can come from a GPS receiver or the setup struct. Lat/Long data
|
||||||
|
is sent in DDMM.MMMMM format.
|
||||||
|
|
||||||
|
Define __ENABLE_GPS to enable the code.
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <stm32wl3x_hal.h>
|
||||||
|
|
||||||
|
#include "frame.h"
|
||||||
|
#include "tasks.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "tod.h"
|
||||||
|
|
||||||
|
// config
|
||||||
|
#define __ENABLE_GPS 0 // set to 1 if you have GPS attached
|
||||||
|
#define __SPEED_DAEMON 0 // send beacon every 5 seconds for testing purposes
|
||||||
|
|
||||||
|
// local defines
|
||||||
|
#define MAX_BEACON 80
|
||||||
|
#define GPS_FIX_LEN 20 // gps fix length
|
||||||
|
// NMEA GGA Sentence fields
|
||||||
|
#define GGA_TAG 0 // message tag
|
||||||
|
#define GGA_TIMESTAMP 1 // time of fix
|
||||||
|
#define GGA_LATITUDE 2 // latitude
|
||||||
|
#define GGA_NS_HEMI 3 // latitude hemisphere
|
||||||
|
#define GGA_LONGITUDE 4 // longitude
|
||||||
|
#define GGA_EW_HEMI 5 // longitude hemisphere
|
||||||
|
// hemispheres
|
||||||
|
enum {
|
||||||
|
N_HEMI=0, // North
|
||||||
|
S_HEMI, // South
|
||||||
|
E_HEMI, // East
|
||||||
|
W_HEMI, // West
|
||||||
|
N_HEMIS
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t hemispheres[N_HEMIS] = {
|
||||||
|
'N', 'S', 'E', 'W'
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t timerInitValue; // value to initialize timer
|
||||||
|
uint32_t timerCtrValue; // current counter
|
||||||
|
|
||||||
|
BEACON_HEADER beacon_hdr; // beacon header
|
||||||
|
uint8_t bcnPayload[MAX_BEACON]; // beacon payload
|
||||||
|
|
||||||
|
BOOL gpsMessageRx = FALSE;
|
||||||
|
|
||||||
|
#if __ENABLE_GPS
|
||||||
|
#define GPS_BUFFER_SIZE 200
|
||||||
|
// LPUART for GPS receiver
|
||||||
|
extern hlpuart1 UART_HandleTypeDef; // HAL Handle to LPUART
|
||||||
|
uint8_t GPSMsgBuf[GPS_BUFFER_SIZE];
|
||||||
|
char GPSLat[GPS_FIX_LEN];
|
||||||
|
char GPSLong[GPS_FIX_LEN];
|
||||||
|
char GPSFixTime[GPS_FIX_LEN];
|
||||||
|
BOOL haveGPSFix = FALSE;
|
||||||
|
char *gpsFlds[GPS_FIX_LEN];
|
||||||
|
#else
|
||||||
|
TIMEOFDAY wallClockTime;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// fwd refs here..
|
||||||
|
void GPSFormat(char *buffer, double value, uint8_t hePos, uint8_t heNeg);
|
||||||
|
|
||||||
|
// initialization: calculate the init value in
|
||||||
|
// quanta of MAIN_TASK_SCHED
|
||||||
|
void Beacon_Task_init(void)
|
||||||
|
{
|
||||||
|
// higher speed for testing...
|
||||||
|
#if __SPEED_DAEMON
|
||||||
|
timerInitValue = 5 *1000/MAIN_TASK_SCHED; // every 5 seconds for testing
|
||||||
|
timerCtrValue = 0;
|
||||||
|
#else
|
||||||
|
uint32_t timerTick = 60 * 1000/MAIN_TASK_SCHED;
|
||||||
|
timerInitValue = setup_memory.params.setup_data.beaconInt * timerTick;
|
||||||
|
timerCtrValue = 0; // set to timerInitValue to wait
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GPS Init
|
||||||
|
#if __ENABLE_GPS
|
||||||
|
HAL_LPUART_Receive_DMA(&hlpuart1, (uint8_t*)GPSMsgBuf, GPS_BUFFER_SIZE);
|
||||||
|
gpsMessageRx = FALSE;
|
||||||
|
haveGPSFix = FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// this runs every MAIN_TASK_SCHED ms
|
||||||
|
void Beacon_Task_exec(void)
|
||||||
|
{
|
||||||
|
#if __ENABLE_GPS
|
||||||
|
if(gpsMessageRx) {
|
||||||
|
gpsMessageRx = FALSE;
|
||||||
|
ifprocessGPSMessage(GPSMsgBuf, GPS_BUFFER_SIZE))
|
||||||
|
haveGPSFix = TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(timerCtrValue > 0) {
|
||||||
|
timerCtrValue--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timerCtrValue = timerInitValue;
|
||||||
|
|
||||||
|
// start with the header
|
||||||
|
beacon_hdr.setup.flags = setup_memory.params.setup_data.flags;
|
||||||
|
beacon_hdr.setup.txPower = setup_memory.params.radio_setup.outputPower;
|
||||||
|
uint8_t *buf = bcnPayload;
|
||||||
|
|
||||||
|
// brute force copy: compiler rounds SETUP_FLAGS to 32 bits
|
||||||
|
*buf++ = beacon_hdr.hdrBytes[0];
|
||||||
|
*buf++ = beacon_hdr.hdrBytes[4];
|
||||||
|
|
||||||
|
char *pPayload = (char *)buf;
|
||||||
|
char *p2 = pPayload;
|
||||||
|
|
||||||
|
#if __ENABLE_GPS
|
||||||
|
//GPS generated payload
|
||||||
|
if(haveGPSFix) {
|
||||||
|
strcpy(pPayload, "GPS,");
|
||||||
|
strcat(pPayload, GPSLat);
|
||||||
|
strcat(pPayload, GPSLong);
|
||||||
|
strcat(pPayload, GPSFixTime);
|
||||||
|
} else {
|
||||||
|
#else
|
||||||
|
// setup struct generated payload
|
||||||
|
strcpy(pPayload, "FXD,");
|
||||||
|
pPayload += strlen(pPayload);
|
||||||
|
double dlat = ascii2double(setup_memory.params.setup_data.latitude);
|
||||||
|
GPSFormat(pPayload, dlat, N_HEMI, S_HEMI);
|
||||||
|
strcat(pPayload, ",");
|
||||||
|
pPayload += strlen(pPayload);
|
||||||
|
double dlong = ascii2double(setup_memory.params.setup_data.longitude);
|
||||||
|
GPSFormat(pPayload, dlong, E_HEMI, W_HEMI);
|
||||||
|
strcat(pPayload, ",");
|
||||||
|
pPayload += strlen(pPayload);
|
||||||
|
|
||||||
|
// Use RTC for time
|
||||||
|
getTOD(&wallClockTime);
|
||||||
|
sprintf(pPayload, "%02d%02d%02d", wallClockTime.Hours, wallClockTime.Minutes, wallClockTime.Seconds);
|
||||||
|
strcat(pPayload, ",");
|
||||||
|
|
||||||
|
// home grid square
|
||||||
|
strcat(pPayload, setup_memory.params.setup_data.gridSq);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// firmware version
|
||||||
|
int pos = strlen((char *)p2);
|
||||||
|
buf[pos++] = def_params.params.FirmwareVerMajor + '0';
|
||||||
|
buf[pos++] = def_params.params.FirmwareVerMinor + '0';
|
||||||
|
buf[pos++] = '\0'; // null terminated
|
||||||
|
|
||||||
|
// time to send a beacon frame..
|
||||||
|
SendBeaconFrame(setup_memory.params.setup_data.stnCall, bcnPayload, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Format the +/-ddd.dddd lat/long format into DDMM.MMMMM format
|
||||||
|
*/
|
||||||
|
void GPSFormat(char *buffer, double value, uint8_t hePos, uint8_t heNeg)
|
||||||
|
{
|
||||||
|
// Set hemi, get abs value
|
||||||
|
uint8_t hemi = value > 0.00 ? hePos : heNeg;
|
||||||
|
value = fabs(value);
|
||||||
|
|
||||||
|
// separate whole and fractional
|
||||||
|
int whole = (int)value;
|
||||||
|
double fract = value - (double)whole;
|
||||||
|
|
||||||
|
// calculate minutes and fraction
|
||||||
|
double dmin = 60.0 * fract;
|
||||||
|
dmin = round(dmin * 100.0)/100.0;
|
||||||
|
|
||||||
|
int min = floor(dmin);
|
||||||
|
int ifract = (int)ceil((dmin-min) * 100000);
|
||||||
|
|
||||||
|
sprintf(buffer, "%d%02d.%05d%c", whole, min, ifract, hemispheres[hemi]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __ENABLE_GPS
|
||||||
|
/*
|
||||||
|
* Process an inbound GPS message
|
||||||
|
*/
|
||||||
|
BOOL processGPSMessage(GPSMsgBuf, GPS_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
int limit = (int)strlen(GPSMsgBuf);
|
||||||
|
nParams = explode_string(GPSMsgBuf, gpsFlds, limit, ',', '"');
|
||||||
|
if (nParams == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
//brute force position fix
|
||||||
|
if(strcmp(gpsFlds[GGA_TAG[strlen(GGA_TAG)-strlen("GGA")]],"GGA"))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
strpy(GPSFixTime, gpsFlds[GGA_TIMESTAMP]);
|
||||||
|
|
||||||
|
strcpy(GPSLat, gpsFlds[GGA_LATITUDE]);
|
||||||
|
strcat(GPSLat, gpsFlds[GGA_NS_HEMI]);
|
||||||
|
|
||||||
|
strcpy(GPSLong, gpsFlds[GGA_LONGITUDE]);
|
||||||
|
strcat(GPSLong, gpsFlds[GGA_EW_HEMI]);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* callback from GPS receiver when rx is complete
|
||||||
|
*/
|
||||||
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
|
{
|
||||||
|
HAL_LPUART_Receive_DMA(&hlpuart1, (uint8_t*)GPSMsgBuf, GPS_BUFFER_SIZE);
|
||||||
|
gpsMessageRx = TRUE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
194
code/IP400/Src/callsign.c
Normal file
194
code/IP400/Src/callsign.c
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: IP400
|
||||||
|
|
||||||
|
Module: Compress and expand a callsign
|
||||||
|
|
||||||
|
File Name: callsign.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 9, 2025
|
||||||
|
|
||||||
|
Description: Comresses an ASCII string of callsign characters. A callsign can
|
||||||
|
have up to 6 characters to fit into the four byte field, if longer
|
||||||
|
the rest are placed in the payload of the data frame. Callsigns can
|
||||||
|
be extended with a '-' character, in this case the call is removed
|
||||||
|
and padded before conversion, and the remainder is also in the
|
||||||
|
frame payload.
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "frame.h"
|
||||||
|
|
||||||
|
#define RADIX_40 40 // alphabet radix
|
||||||
|
|
||||||
|
// Radix 40 callsign alphabet
|
||||||
|
char alphabet[RADIX_40] = {
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
|
||||||
|
// 10 11 12 13 14 15 16 17 18 19
|
||||||
|
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
|
||||||
|
|
||||||
|
// 20 21 22 23 24 25 26 27 28 29
|
||||||
|
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||||
|
|
||||||
|
// 30 31 32 33 34 35 36 37 38 39
|
||||||
|
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '(', ')', '-'
|
||||||
|
};
|
||||||
|
|
||||||
|
// encode a char into the alphabet
|
||||||
|
uint32_t alphaEncode(char byte)
|
||||||
|
{
|
||||||
|
byte = islower(byte) ? toupper(byte) : byte;
|
||||||
|
|
||||||
|
for(uint32_t i=0;i<RADIX_40;i++) {
|
||||||
|
if(alphabet[i] == byte)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode a byte back into ASCII
|
||||||
|
char alphaDecode(uint32_t alpha)
|
||||||
|
{
|
||||||
|
// numeric
|
||||||
|
if((alpha >= 0) && (alpha <= 9))
|
||||||
|
return '0' + alpha;
|
||||||
|
|
||||||
|
// special cases: see alphabet table
|
||||||
|
|
||||||
|
switch(alpha) {
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
return ' ';
|
||||||
|
|
||||||
|
case 37:
|
||||||
|
return '(';
|
||||||
|
|
||||||
|
case 38:
|
||||||
|
return ')';
|
||||||
|
|
||||||
|
case 39:
|
||||||
|
return '@';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 'A' + (alpha - 11);
|
||||||
|
}
|
||||||
|
return ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
void EncodeChunk(char *src, int len, uint32_t *enc)
|
||||||
|
{
|
||||||
|
uint32_t chunk=alphaEncode(src[0]);
|
||||||
|
|
||||||
|
// less than 2 characters
|
||||||
|
if(len < 2) {
|
||||||
|
*enc = chunk;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 or more
|
||||||
|
for(int i=1;i<len;i++) {
|
||||||
|
uint32_t current = alphaEncode(src[i]);
|
||||||
|
chunk = current + chunk*RADIX_40;
|
||||||
|
}
|
||||||
|
*enc = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode a callsign: if less than max_call, then just encode into the frame,
|
||||||
|
// else continue in the data portion of the frame
|
||||||
|
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset)
|
||||||
|
{
|
||||||
|
int len = strlen(callsign);
|
||||||
|
|
||||||
|
uint32_t encChunk;
|
||||||
|
uint32_t *p = (uint32_t *)frame->buf;
|
||||||
|
p += offset;
|
||||||
|
|
||||||
|
if(dest == DEST_CALLSIGN)
|
||||||
|
frame->dest.port = port;
|
||||||
|
else
|
||||||
|
frame->source.port = port;
|
||||||
|
|
||||||
|
// broadcast address
|
||||||
|
if(!strcmp(callsign, "FFFF")) {
|
||||||
|
if(dest == DEST_CALLSIGN)
|
||||||
|
frame->dest.callbytes.encoded = 0xFFFFFFFF;
|
||||||
|
else
|
||||||
|
frame->source.callbytes.encoded = 0xFFFFFFFF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure the callsign is padded out to at least 6 characters
|
||||||
|
char paddedCall[50];
|
||||||
|
strcpy(paddedCall, callsign);
|
||||||
|
strcat(paddedCall, " ");
|
||||||
|
|
||||||
|
// non-broadcast: break it up into chunks of 6 characters
|
||||||
|
int nChunks = len/MAX_CALL;
|
||||||
|
if ((len % MAX_CALL) > 0)
|
||||||
|
nChunks++;
|
||||||
|
|
||||||
|
char *cll = paddedCall;
|
||||||
|
|
||||||
|
for(int k=0;k<nChunks;k++) {
|
||||||
|
EncodeChunk(cll, MAX_CALL, &encChunk);
|
||||||
|
if(k==0) {
|
||||||
|
if(dest == DEST_CALLSIGN)
|
||||||
|
frame->dest.callbytes.encoded = encChunk;
|
||||||
|
else
|
||||||
|
frame->source.callbytes.encoded = encChunk;
|
||||||
|
// callsign less than or equal to MAX_CALL
|
||||||
|
if(nChunks == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*p++ = encChunk;
|
||||||
|
cll += MAX_CALL;
|
||||||
|
if(dest == DEST_CALLSIGN)
|
||||||
|
frame->flagfld.flags.destExt = TRUE;
|
||||||
|
else
|
||||||
|
frame->flagfld.flags.srcExt = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p++ = 0xFF000000;
|
||||||
|
// return the offset in the buffer
|
||||||
|
return (p-(uint32_t *)frame->buf) + sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode a callsign
|
||||||
|
BOOL callDecode(IP400_CALL *encCall, char *callsign, uint16_t *port)
|
||||||
|
{
|
||||||
|
char tmpBuf[10], *p = tmpBuf;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
uint32_t encoded = encCall->callbytes.encoded;
|
||||||
|
for(i=0;i<MAX_CALL;i++) {
|
||||||
|
*p++ = alphaDecode(encoded % RADIX_40);
|
||||||
|
encoded /= RADIX_40;
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
for(i=strlen(tmpBuf)-1;i>=0;i--)
|
||||||
|
*callsign++ = tmpBuf[i];
|
||||||
|
|
||||||
|
*callsign = '\0';
|
||||||
|
*port = encCall->port;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
287
code/IP400/Src/chat.c
Normal file
287
code/IP400/Src/chat.c
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: chat.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: This code contains the chat application. It gathers key until the CR
|
||||||
|
is hit to send a packet. Two internal operations are exit and set the
|
||||||
|
destination.
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "setup.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "usart.h"
|
||||||
|
#include "streambuffer.h"
|
||||||
|
#include "dataq.h"
|
||||||
|
|
||||||
|
#define MAX_KEY 140 // max keys in a buffer
|
||||||
|
#define MAX_DEST 20 // max chars in dest callsign
|
||||||
|
#define BROADCAST "FFFF" // broadcast address
|
||||||
|
|
||||||
|
static char keyBuffer[MAX_KEY]; // buffer for keystrokes
|
||||||
|
uint8_t keyPos; // current position
|
||||||
|
|
||||||
|
#define KEY_EOL 0x0D // carriage return
|
||||||
|
#define KEY_RPT 0x12 // change repeat status
|
||||||
|
#define KEY_EXIT 0x1A // exit key
|
||||||
|
#define KEY_ESC 0x1B // escape key
|
||||||
|
#define KEY_DEL 0x7F // delete key
|
||||||
|
#define KEY_BKSP 0x08 // backspace key
|
||||||
|
#define KEY_DUMP 0x04 // toggle dump mode
|
||||||
|
|
||||||
|
char dest_call[MAX_DEST]; // broadcast destination
|
||||||
|
char *dp = dest_call; // pointer to dest call characters
|
||||||
|
char *entCall = "Enter Destination Callsign";
|
||||||
|
char *rptMode[] = {
|
||||||
|
"Repeat mode->off",
|
||||||
|
"Repeat mode->on"
|
||||||
|
};
|
||||||
|
|
||||||
|
char *dumpStrings[] = {
|
||||||
|
"Dump mode->off",
|
||||||
|
"Dump mode->on"
|
||||||
|
};
|
||||||
|
|
||||||
|
char *welcome = "Welcome to chat. ESC to set destination, CTRL/R to toggle repeat, CTRL/Z to exit";
|
||||||
|
|
||||||
|
BOOL destEnt = FALSE; // not entering destination
|
||||||
|
BOOL repeat = TRUE; // repeating by default
|
||||||
|
BOOL deleteMode = FALSE; // delete mode
|
||||||
|
BOOL welcomed = FALSE; // welcome mat is out
|
||||||
|
BOOL dumpMode = FALSE;
|
||||||
|
|
||||||
|
FRAME_QUEUE chatQueue; // queue for inbound frames
|
||||||
|
|
||||||
|
// fwd refs
|
||||||
|
void sendLine(char *buffer, int len);
|
||||||
|
void PrintFrame(IP400_FRAME *FrameBytes);
|
||||||
|
|
||||||
|
// init entry
|
||||||
|
void Chat_Task_init(void)
|
||||||
|
{
|
||||||
|
strcpy(dest_call, BROADCAST);
|
||||||
|
destEnt = FALSE;
|
||||||
|
chatQueue.q_forw = &chatQueue;
|
||||||
|
chatQueue.q_back = &chatQueue;
|
||||||
|
keyPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat_Task_welcome(void)
|
||||||
|
{
|
||||||
|
// start with welcome message
|
||||||
|
USART_Print_string("%s\r\n", welcome);
|
||||||
|
USART_Print_string("%s; ", rptMode[repeat]);
|
||||||
|
|
||||||
|
if(!strcmp(dest_call, BROADCAST))
|
||||||
|
USART_Print_string("%s; Destination callsign->(Broadcast)\r\n\n");
|
||||||
|
else
|
||||||
|
USART_Print_string("%s; Destination callsign->%s\r\n\n", dest_call);
|
||||||
|
|
||||||
|
welcomed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* place a frame on the queue frame content is copied
|
||||||
|
* to alloc'd memory
|
||||||
|
*/
|
||||||
|
BOOL EnqueChatFrame(void *raw, int length)
|
||||||
|
{
|
||||||
|
IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)raw;
|
||||||
|
uint8_t *frameBuffer;
|
||||||
|
|
||||||
|
// allocate an IP400 frame
|
||||||
|
if((qFrame=malloc(sizeof(IP400_FRAME)))== NULL)
|
||||||
|
return FALSE;
|
||||||
|
memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME));
|
||||||
|
|
||||||
|
// alloc the data portion of the frame
|
||||||
|
if((frameBuffer=malloc(SrcFrame->length)) == NULL) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(frameBuffer, (uint8_t *)SrcFrame->buf, SrcFrame->length);
|
||||||
|
qFrame->buf = frameBuffer;
|
||||||
|
qFrame->length = SrcFrame->length;
|
||||||
|
|
||||||
|
if(!enqueFrame(&chatQueue, qFrame))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* chat task main entry
|
||||||
|
* return: TRUE if return to main menu is required
|
||||||
|
* FALSE: more to go
|
||||||
|
*/
|
||||||
|
BOOL Chat_Task_exec(void)
|
||||||
|
{
|
||||||
|
if(!welcomed)
|
||||||
|
Chat_Task_welcome();
|
||||||
|
|
||||||
|
char c;
|
||||||
|
int nBytesinBuff;
|
||||||
|
IP400_FRAME *fr;
|
||||||
|
|
||||||
|
// process any inbound frames first..
|
||||||
|
if((fr=dequeFrame(&chatQueue)) != NULL) {
|
||||||
|
PrintFrame(fr);
|
||||||
|
free(fr->buf);
|
||||||
|
free(fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for(int i=0;i<nBytesinBuff;i++) {
|
||||||
|
|
||||||
|
c=databuffer_get(0);
|
||||||
|
|
||||||
|
if(deleteMode) {
|
||||||
|
if((c != KEY_DEL) && (c != KEY_BKSP)) {
|
||||||
|
USART_Print_string("\\%c", c);
|
||||||
|
if(keyPos < MAX_KEY)
|
||||||
|
keyBuffer[keyPos++] = c;
|
||||||
|
deleteMode = FALSE;
|
||||||
|
} else {
|
||||||
|
if(keyPos > 0) {
|
||||||
|
USART_Print_string("%c", keyBuffer[--keyPos]);
|
||||||
|
} else {
|
||||||
|
USART_Print_string("\\\r\n");
|
||||||
|
deleteMode = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// processing a key
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
// CTRL/R: change repeat flag
|
||||||
|
case KEY_RPT:
|
||||||
|
repeat = repeat ? FALSE : TRUE;
|
||||||
|
char *r = rptMode[repeat];
|
||||||
|
USART_Print_string("%s\r\n",r);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// CTRL/D: change dump mode
|
||||||
|
case KEY_DUMP:
|
||||||
|
dumpMode = dumpMode ? FALSE : TRUE;
|
||||||
|
char *d = dumpStrings[dumpMode];
|
||||||
|
USART_Print_string("%s\r\n",d);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// escape key: get a destination call sign
|
||||||
|
case KEY_ESC:
|
||||||
|
if(keyPos == 0) {
|
||||||
|
USART_Print_string("%s->", entCall);
|
||||||
|
dp = dest_call;
|
||||||
|
destEnt = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// EOL key: sent the packet
|
||||||
|
case KEY_EOL:
|
||||||
|
USART_Print_string("\r\n");
|
||||||
|
if(destEnt) {
|
||||||
|
int cpyLen = keyPos > MAX_CALL ? MAX_CALL : keyPos;
|
||||||
|
strncpy(dest_call, keyBuffer, cpyLen);
|
||||||
|
destEnt = FALSE;
|
||||||
|
} else {
|
||||||
|
keyBuffer[keyPos++] = '\0';
|
||||||
|
sendLine(keyBuffer, keyPos);
|
||||||
|
}
|
||||||
|
keyPos = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_EXIT:
|
||||||
|
welcomed = FALSE;
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
case KEY_DEL:
|
||||||
|
case KEY_BKSP:
|
||||||
|
if(keyPos > 0) {
|
||||||
|
USART_Print_string("\\%c", keyBuffer[--keyPos]);
|
||||||
|
deleteMode = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
USART_Send_Char(c);
|
||||||
|
if(keyPos < MAX_KEY)
|
||||||
|
keyBuffer[keyPos++] = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a line of text
|
||||||
|
void sendLine(char *buffer, int len)
|
||||||
|
{
|
||||||
|
SendTextFrame(setup_memory.params.setup_data.stnCall, TEXT_TYPE, dest_call, TEXT_TYPE, buffer, len, repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a received frame on the console
|
||||||
|
*/
|
||||||
|
void PrintFrame(IP400_FRAME *FrameBytes)
|
||||||
|
{
|
||||||
|
char printBuf[250];
|
||||||
|
char decCall[100];
|
||||||
|
uint16_t port;
|
||||||
|
uint16_t dataLen = FrameBytes->length;
|
||||||
|
|
||||||
|
// dump mode for header debugging
|
||||||
|
if(dumpMode) {
|
||||||
|
/* print the received data */
|
||||||
|
USART_Print_string("RX - Data received: [ ");
|
||||||
|
|
||||||
|
for(uint8_t i=0; i<sizeof(IP400_FRAME); i++)
|
||||||
|
USART_Print_string("%02x ", FrameBytes[i]);
|
||||||
|
|
||||||
|
USART_Print_string("]\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// source call
|
||||||
|
callDecode(&FrameBytes->source, decCall, &port);
|
||||||
|
USART_Print_string("%s(%d)>", decCall, port);
|
||||||
|
|
||||||
|
// dest call
|
||||||
|
if((FrameBytes->dest.callbytes.bytes[0] == BROADCAST_ADDR) &&
|
||||||
|
(FrameBytes->dest.callbytes.bytes[1] == BROADCAST_ADDR)) {
|
||||||
|
USART_Print_string("BROADCAST(%d)", FrameBytes->dest.port);
|
||||||
|
} else {
|
||||||
|
callDecode(&FrameBytes->dest, decCall, &port);
|
||||||
|
USART_Print_string("%s(%d)", decCall, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flags
|
||||||
|
USART_Print_string("[%d:%04d]:", FrameBytes->flagfld.flags.hop_count, FrameBytes->seqNum);
|
||||||
|
|
||||||
|
// now dump the data
|
||||||
|
memcpy(printBuf, FrameBytes->buf, dataLen);
|
||||||
|
printBuf[dataLen] = '\0';
|
||||||
|
USART_Print_string("%s\r\n", printBuf);
|
||||||
|
|
||||||
|
}
|
67
code/IP400/Src/dataq.c
Normal file
67
code/IP400/Src/dataq.c
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: dataq.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: This module enques and deques an IP400 Frame type on a queue
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "dataq.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enque a frame. The frame data buffer must have
|
||||||
|
* alredy been allocated
|
||||||
|
*/
|
||||||
|
BOOL enqueFrame(FRAME_QUEUE *que, IP400_FRAME *fr)
|
||||||
|
{
|
||||||
|
FRAME_QUEUE *f;
|
||||||
|
if((f = malloc(sizeof(FRAME_QUEUE))) == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// set the frame buffer
|
||||||
|
f->frame = fr;
|
||||||
|
f->length = fr->length + sizeof(IP400_FRAME);
|
||||||
|
|
||||||
|
insque((QUEUE_ELEM *)f, (QUEUE_ELEM *)que->q_back);
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deqeue a frame
|
||||||
|
* Returns null if no frame is one the queue
|
||||||
|
* Does NOT dealloc data or frame or q
|
||||||
|
*/
|
||||||
|
IP400_FRAME *dequeFrame(FRAME_QUEUE *que)
|
||||||
|
{
|
||||||
|
IP400_FRAME *ipFrame;
|
||||||
|
|
||||||
|
if(que->q_back == que)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FRAME_QUEUE *f = que->q_forw;
|
||||||
|
remque((struct qelem *)f);
|
||||||
|
|
||||||
|
ipFrame = f->frame;
|
||||||
|
|
||||||
|
free(f);
|
||||||
|
return ipFrame;
|
||||||
|
}
|
527
code/IP400/Src/frame.c
Normal file
527
code/IP400/Src/frame.c
Normal file
|
@ -0,0 +1,527 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: IP400
|
||||||
|
|
||||||
|
Module: Frame transmit and receive tasks
|
||||||
|
|
||||||
|
File Name: frame.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Creation Date: Jan 8, 2025
|
||||||
|
|
||||||
|
Description: Handle the transmission and reception of frames
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <stm32wl3x_hal_mrsubg.h>
|
||||||
|
#if _BOARD_TYPE==NUCLEO_BOARD
|
||||||
|
#include <stm32wl3x_nucleo.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <semphr.h>
|
||||||
|
|
||||||
|
#include "frame.h"
|
||||||
|
#include "dataq.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "tasks.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
// local defines
|
||||||
|
#define RX_TIMEOUT 2 // return to OS after 5 ms timeout
|
||||||
|
|
||||||
|
// conditionals
|
||||||
|
#define __DUMP_BEACON 0 // dump a beacon frame to console using chat
|
||||||
|
|
||||||
|
// transmit states
|
||||||
|
enum {
|
||||||
|
TX_IDLE=0, // idle - waiting for work
|
||||||
|
TX_SENDING, // sending a frame
|
||||||
|
TX_DONE // done
|
||||||
|
};
|
||||||
|
|
||||||
|
// locals
|
||||||
|
uint8_t txState; // transmitter state
|
||||||
|
uint8_t radioCmd; // current radio command
|
||||||
|
uint8_t prevCmd; // previous state
|
||||||
|
FRAME_QUEUE txQueue; // transmitter frame queue
|
||||||
|
uint32_t subgIRQStatus; // interrupt status
|
||||||
|
BOOL txDone; // transmitter is done
|
||||||
|
BOOL rxReady; // rx is ready...
|
||||||
|
FRAME_STATS Stats; // collected stats
|
||||||
|
uint32_t nextSeq; // next frame sequence number
|
||||||
|
|
||||||
|
// from setup...
|
||||||
|
int16_t rxSquelch; // rx squlech
|
||||||
|
|
||||||
|
// internal fwd references
|
||||||
|
void QueueTxFrame(IP400_FRAME *txframe); // send a frame
|
||||||
|
void EnableRx(void); // enable the rx
|
||||||
|
|
||||||
|
// receive raw frame: we are only interested
|
||||||
|
static uint8_t rawTxFrame[MAX_FRAME_SIZE];
|
||||||
|
static uint8_t rawRxFrame[MAX_FRAME_SIZE];
|
||||||
|
|
||||||
|
// processed frames
|
||||||
|
static IP400_FRAME rFrame;
|
||||||
|
|
||||||
|
// intialize the transmit task
|
||||||
|
void Frame_task_init(void)
|
||||||
|
{
|
||||||
|
txState = TX_IDLE;
|
||||||
|
txQueue.q_forw = &txQueue;
|
||||||
|
txQueue.q_back = &txQueue;
|
||||||
|
|
||||||
|
// init stats and counters
|
||||||
|
memset(&Stats, 0, sizeof(FRAME_STATS));
|
||||||
|
nextSeq = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
// set Rx threshold
|
||||||
|
STN_PARAMS *params = GetStationParams();
|
||||||
|
rxSquelch = params->radio_setup.rxSquelch;
|
||||||
|
HAL_MRSubG_SetRSSIThreshold(rxSquelch);
|
||||||
|
|
||||||
|
// enable the interrupt
|
||||||
|
__HAL_MRSUBG_SET_RFSEQ_IRQ_ENABLE(
|
||||||
|
MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_OK_E
|
||||||
|
| MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_TX_DONE_E
|
||||||
|
| MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_TIMEOUT_E
|
||||||
|
| MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_CRC_ERROR_E
|
||||||
|
);
|
||||||
|
HAL_NVIC_EnableIRQ(MR_SUBG_IRQn);
|
||||||
|
|
||||||
|
// enable the Rx
|
||||||
|
rxReady = FALSE;
|
||||||
|
EnableRx();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return the stats
|
||||||
|
*/
|
||||||
|
FRAME_STATS *GetFrameStats(void)
|
||||||
|
{
|
||||||
|
return &Stats;
|
||||||
|
}
|
||||||
|
// return the radio status
|
||||||
|
uint32_t GetRadioStatus(void)
|
||||||
|
{
|
||||||
|
uint32_t radioStats = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_STATUS_DETAIL);
|
||||||
|
return radioStats;
|
||||||
|
}
|
||||||
|
// get the FSM state
|
||||||
|
uint8_t GetFSMState(void)
|
||||||
|
{
|
||||||
|
uint32_t fsmState = READ_REG(MR_SUBG_GLOB_STATUS->RADIO_FSM_INFO);
|
||||||
|
return (uint8_t)(fsmState & MR_SUBG_GLOB_STATUS_RADIO_FSM_INFO_RADIO_FSM_STATE_Msk);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Send a text frame. Buf is not malloc'ed, can be static in ram
|
||||||
|
*/
|
||||||
|
BOOL SendTextFrame(char *srcCall, uint16_t srcPort, char *destCall, uint16_t dstPort, char *buf, uint16_t length, BOOL repeat)
|
||||||
|
{
|
||||||
|
IP400_FRAME *txFrame;
|
||||||
|
|
||||||
|
if((txFrame=malloc(sizeof(IP400_FRAME))) == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if((txFrame->buf=malloc(length + MAX_CALL_BUFFER)) == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
txFrame->flagfld.allflags = 0; // start with all flags cleared
|
||||||
|
|
||||||
|
// format the header
|
||||||
|
uint8_t offset = callEncode(srcCall, srcPort, txFrame, SRC_CALLSIGN, 0);
|
||||||
|
offset = callEncode(destCall, dstPort, txFrame, DEST_CALLSIGN, offset);
|
||||||
|
|
||||||
|
// copy the payload in
|
||||||
|
uint8_t *f = (uint8_t *)txFrame->buf;
|
||||||
|
f += offset*sizeof(uint32_t);
|
||||||
|
memcpy(f, (const uint8_t *)buf, length);
|
||||||
|
|
||||||
|
txFrame->length = length + offset;
|
||||||
|
txFrame->flagfld.flags.hop_count = 0;
|
||||||
|
txFrame->flagfld.flags.coding |= UTF8_TEXT_PACKET;
|
||||||
|
txFrame->flagfld.flags.repeat = repeat;
|
||||||
|
txFrame->seqNum = nextSeq++;
|
||||||
|
|
||||||
|
QueueTxFrame(txFrame);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compose and send a beacon frame (ping frame with broadcast destination
|
||||||
|
*/
|
||||||
|
void SendBeaconFrame(char *srcCall, uint8_t *payload, int bcnlen)
|
||||||
|
{
|
||||||
|
IP400_FRAME *bcnFrame;
|
||||||
|
|
||||||
|
if((bcnFrame=malloc(sizeof(IP400_FRAME))) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if((bcnFrame->buf=malloc(bcnlen + MAX_CALL_BUFFER)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bcnFrame->flagfld.allflags = 0; // start with all flags cleared
|
||||||
|
|
||||||
|
// broadcast frame
|
||||||
|
uint8_t offset = callEncode(srcCall, ICMP_TYPE, bcnFrame, SRC_CALLSIGN, 0);
|
||||||
|
callEncode("FFFF", 0, bcnFrame, DEST_CALLSIGN, 0);
|
||||||
|
|
||||||
|
// adjust starting data point, add payload
|
||||||
|
uint8_t *f = (uint8_t *)bcnFrame->buf;
|
||||||
|
f += offset*sizeof(uint32_t);
|
||||||
|
memcpy(f, payload, bcnlen);
|
||||||
|
|
||||||
|
bcnFrame->length = bcnlen + offset;
|
||||||
|
bcnFrame->flagfld.flags.hop_count = 0;
|
||||||
|
bcnFrame->flagfld.flags.coding |= PING_PACKET;
|
||||||
|
bcnFrame->flagfld.flags.repeat = TRUE;
|
||||||
|
bcnFrame->seqNum = nextSeq++;
|
||||||
|
|
||||||
|
QueueTxFrame(bcnFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if a frame came from me
|
||||||
|
// returns true if I originated the frame
|
||||||
|
BOOL FrameisMine(IP400_FRAME *frame)
|
||||||
|
{
|
||||||
|
char decCall[30];
|
||||||
|
uint16_t port;;
|
||||||
|
|
||||||
|
// check the originator call sign
|
||||||
|
callDecode(&frame->source, decCall, &port);
|
||||||
|
return(CompareToMyCall(decCall));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Repeat a frame. Do not send it if we were the originator
|
||||||
|
*/
|
||||||
|
void RepeatFrame(IP400_FRAME *frame)
|
||||||
|
{
|
||||||
|
IP400_FRAME *rptFrame;
|
||||||
|
|
||||||
|
// copy the frame
|
||||||
|
if((rptFrame=malloc(sizeof(IP400_FRAME))) == NULL)
|
||||||
|
return;
|
||||||
|
memcpy(rptFrame, frame, sizeof(IP400_FRAME));
|
||||||
|
|
||||||
|
// copy the data
|
||||||
|
rptFrame->length = frame->length;
|
||||||
|
if((rptFrame->buf=malloc(frame->length)) == NULL)
|
||||||
|
return;
|
||||||
|
memcpy(rptFrame->buf, frame->buf, frame->length);
|
||||||
|
|
||||||
|
// tick the hop count
|
||||||
|
rptFrame->flagfld.flags.hop_count += 1;
|
||||||
|
|
||||||
|
Stats.nRepeated++;
|
||||||
|
QueueTxFrame(rptFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* queue a frame for transmission by the tx task
|
||||||
|
*/
|
||||||
|
void QueueTxFrame(IP400_FRAME *txframe)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
FRAME_QUEUE *f;
|
||||||
|
if((f = malloc(sizeof(FRAME_QUEUE))) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set the frame buffer and sequence number
|
||||||
|
f->frame = txframe;
|
||||||
|
|
||||||
|
insque((QUEUE_ELEM *)f, (QUEUE_ELEM *)txQueue.q_back);
|
||||||
|
*/
|
||||||
|
enqueFrame(&txQueue, txframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable the receiver
|
||||||
|
*/
|
||||||
|
void EnableRx(void)
|
||||||
|
{
|
||||||
|
__HAL_MRSUBG_SET_RX_MODE(RX_NORMAL);
|
||||||
|
__HAL_MRSUBG_SET_DATABUFFER_SIZE(MAX_FRAME_SIZE);
|
||||||
|
MR_SUBG_GLOB_STATIC->DATABUFFER0_PTR = (uint32_t)&rawRxFrame;
|
||||||
|
|
||||||
|
// set the command
|
||||||
|
radioCmd = CMD_RX;
|
||||||
|
__HAL_MRSUBG_STROBE_CMD(radioCmd);
|
||||||
|
|
||||||
|
SetLEDMode(BICOLOR_GREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main entry for tx task. Pick frames from the transmit queue
|
||||||
|
*/
|
||||||
|
void Frame_Txtask_exec(void)
|
||||||
|
{
|
||||||
|
static IP400_FRAME *tFrame;
|
||||||
|
int frameLen = 0;
|
||||||
|
|
||||||
|
switch(txState) {
|
||||||
|
|
||||||
|
// idle: waiting for work
|
||||||
|
case TX_IDLE:
|
||||||
|
|
||||||
|
/*
|
||||||
|
// wait for a frame to send
|
||||||
|
if(txQueue.q_back == &txQueue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// get a frane from the top of the queue
|
||||||
|
FRAME_QUEUE *f = txQueue.q_forw;
|
||||||
|
remque((struct qelem *)f);
|
||||||
|
|
||||||
|
// this would be better as a union..
|
||||||
|
int frameLen = f->length;
|
||||||
|
tFrame = f->frame;
|
||||||
|
*/
|
||||||
|
|
||||||
|
tFrame = dequeFrame(&txQueue);
|
||||||
|
|
||||||
|
if(tFrame != NULL) {
|
||||||
|
frameLen = tFrame->length;
|
||||||
|
uint8_t *rawFrame = (uint8_t *)rawTxFrame;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the raw frame bytes: see IP400_FRAME struct
|
||||||
|
*/
|
||||||
|
// Source call + port (6 bytes)
|
||||||
|
memcpy(rawFrame, (uint8_t *)&tFrame->source, IP_400_CALL_SIZE);
|
||||||
|
rawFrame += IP_400_CALL_SIZE;
|
||||||
|
// Dest call + port (6 bytes)
|
||||||
|
memcpy(rawFrame, (uint8_t *)&tFrame->dest, IP_400_CALL_SIZE);
|
||||||
|
rawFrame += IP_400_CALL_SIZE;
|
||||||
|
// flag byte (2 byte)
|
||||||
|
memcpy(rawFrame, (uint8_t *)&tFrame->flagfld, IP_400_FLAG_SIZE);
|
||||||
|
rawFrame += IP_400_FLAG_SIZE;
|
||||||
|
// frame sequence number (4 bytes)
|
||||||
|
memcpy(rawFrame, (uint32_t *)&tFrame->seqNum, sizeof(uint32_t));
|
||||||
|
rawFrame += sizeof(uint32_t);
|
||||||
|
// frame length (2 bytes)
|
||||||
|
memcpy(rawFrame, (uint8_t *)&tFrame->length, sizeof(uint16_t));
|
||||||
|
rawFrame += IP_400_LEN_SIZE;
|
||||||
|
|
||||||
|
// and now the data...
|
||||||
|
if((tFrame->buf != NULL) && (tFrame->length != 0))
|
||||||
|
memcpy(rawFrame, tFrame->buf, tFrame->length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free the allocations in the reverse order...
|
||||||
|
if(tFrame != NULL) {
|
||||||
|
if(tFrame->buf != NULL)
|
||||||
|
free(tFrame->buf);
|
||||||
|
free(tFrame);
|
||||||
|
} else {
|
||||||
|
return; // nothing to send...
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_MRSubG_PktBasicSetPayloadLength(frameLen);
|
||||||
|
|
||||||
|
// abort the current rx operation
|
||||||
|
if(radioCmd == CMD_RX) {
|
||||||
|
__HAL_MRSUBG_STROBE_CMD(CMD_SABORT);
|
||||||
|
uint32_t reject=0, abortDone=0;
|
||||||
|
do {
|
||||||
|
subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS);
|
||||||
|
reject = subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_COMMAND_REJECTED_F;
|
||||||
|
abortDone = subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_SABORT_DONE_F;
|
||||||
|
} while ((abortDone == 0) && (reject == 0));
|
||||||
|
if(abortDone)
|
||||||
|
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_SABORT_DONE_F);
|
||||||
|
if(reject)
|
||||||
|
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_COMMAND_REJECTED_F);
|
||||||
|
}
|
||||||
|
|
||||||
|
__HAL_MRSUBG_SET_DATABUFFER0_POINTER((uint32_t)rawTxFrame);
|
||||||
|
__HAL_MRSUBG_SET_TX_MODE(TX_NORMAL);
|
||||||
|
|
||||||
|
txDone = FALSE;
|
||||||
|
prevCmd = radioCmd;
|
||||||
|
radioCmd = CMD_TX;
|
||||||
|
__HAL_MRSUBG_STROBE_CMD(radioCmd);
|
||||||
|
|
||||||
|
// set tx indication: bicolor off and Tx on
|
||||||
|
SetLEDMode(BICOLOR_OFF);
|
||||||
|
SetLEDMode(TX_LED_ON);
|
||||||
|
|
||||||
|
txState = TX_SENDING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// sending a frame
|
||||||
|
case TX_SENDING:
|
||||||
|
// still busy sending
|
||||||
|
if(!txDone)
|
||||||
|
return;
|
||||||
|
|
||||||
|
txState = TX_DONE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// done
|
||||||
|
case TX_DONE:
|
||||||
|
// restart the receiver, if needed
|
||||||
|
EnableRx();
|
||||||
|
|
||||||
|
SetLEDMode(TX_LED_OFF);
|
||||||
|
txState = TX_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main entry of the rx task
|
||||||
|
*/
|
||||||
|
void Frame_Rxtask_exec(void)
|
||||||
|
{
|
||||||
|
// wait for completion..
|
||||||
|
if(!rxReady)
|
||||||
|
return;
|
||||||
|
rxReady = FALSE;
|
||||||
|
|
||||||
|
uint8_t *RxRaw = rawRxFrame;
|
||||||
|
uint8_t *cpyDest;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do the opposite of the transmitter...
|
||||||
|
*/
|
||||||
|
// Source call + port (6 bytes)
|
||||||
|
cpyDest = (uint8_t *)&rFrame.source.callbytes.bytes;
|
||||||
|
memcpy(cpyDest, RxRaw, IP_400_CALL_SIZE);
|
||||||
|
RxRaw += IP_400_CALL_SIZE;
|
||||||
|
|
||||||
|
// Dest call + port (6 bytes)
|
||||||
|
cpyDest = (uint8_t *)&rFrame.dest.callbytes.bytes;
|
||||||
|
memcpy(cpyDest, RxRaw, IP_400_CALL_SIZE);
|
||||||
|
RxRaw += IP_400_CALL_SIZE;
|
||||||
|
|
||||||
|
// flag byte (2 byte)
|
||||||
|
cpyDest = (uint8_t *)&rFrame.flagfld.allflags;
|
||||||
|
memcpy(cpyDest, RxRaw, IP_400_FLAG_SIZE);
|
||||||
|
RxRaw += IP_400_FLAG_SIZE;
|
||||||
|
|
||||||
|
// frame sequence number (4 bytes)
|
||||||
|
cpyDest = (uint8_t *)&rFrame.seqNum;
|
||||||
|
memcpy(cpyDest, RxRaw, sizeof(uint32_t));
|
||||||
|
RxRaw += sizeof(uint32_t);
|
||||||
|
|
||||||
|
// frame length (2 bytes)
|
||||||
|
cpyDest = (uint8_t *)&rFrame.length;
|
||||||
|
memcpy(cpyDest, RxRaw, sizeof(uint16_t));
|
||||||
|
RxRaw += IP_400_LEN_SIZE;
|
||||||
|
|
||||||
|
rFrame.buf = RxRaw;
|
||||||
|
|
||||||
|
// only interested in the 'to' port at this point...
|
||||||
|
uint32_t length = __HAL_MRSUBG_GET_DATABUFFER_SIZE();
|
||||||
|
|
||||||
|
// find a reason to reject a frame...
|
||||||
|
BOOL isMine, isUniqueFrame;
|
||||||
|
isMine = FrameisMine(&rFrame);
|
||||||
|
if(!isMine)
|
||||||
|
isUniqueFrame = Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI);
|
||||||
|
else
|
||||||
|
isUniqueFrame = FALSE;
|
||||||
|
|
||||||
|
// process the frame if it is not mine and unique
|
||||||
|
if(isUniqueFrame && !isMine) {
|
||||||
|
|
||||||
|
switch(rFrame.source.port) {
|
||||||
|
|
||||||
|
// process a beacon frame
|
||||||
|
case ICMP_TYPE:
|
||||||
|
Mesh_ProcessBeacon((void *)&rFrame, Stats.lastRSSI);
|
||||||
|
#if __DUMP_BEACON
|
||||||
|
EnqueChatFrame((void *)&rFrame, length);
|
||||||
|
#endif
|
||||||
|
Stats.nBeacons++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TEXT_TYPE:
|
||||||
|
EnqueChatFrame((void *)&rFrame, length);
|
||||||
|
Stats.framesOK++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// these will be supported later....
|
||||||
|
case VIDEO_TYPE:
|
||||||
|
case ENC_AX25_TYPE:
|
||||||
|
case ENC_IP_TYPE:
|
||||||
|
case AUDIO_TYPE:
|
||||||
|
default:
|
||||||
|
Stats.dropped++;
|
||||||
|
logger(LOG_ERROR, "Frame Received for unknown port: %d\r\n", rFrame.source.port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeat the frame if the repeat flag is set and the hop count is not exhausted
|
||||||
|
if(!isMine) {
|
||||||
|
if(rFrame.flagfld.flags.repeat && (rFrame.flagfld.flags.hop_count < MAX_HOP_COUNT))
|
||||||
|
RepeatFrame(&rFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update stats
|
||||||
|
if(!isUniqueFrame)
|
||||||
|
Stats.duplicates++;
|
||||||
|
if(isMine)
|
||||||
|
Stats.dropped++;
|
||||||
|
|
||||||
|
// restart the receiver
|
||||||
|
EnableRx();
|
||||||
|
|
||||||
|
}
|
||||||
|
// frame interrupt callback
|
||||||
|
void HAL_MRSubG_IRQ_Callback(void)
|
||||||
|
{
|
||||||
|
subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS);
|
||||||
|
|
||||||
|
// Process transmitter interrupts
|
||||||
|
if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F) {
|
||||||
|
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F);
|
||||||
|
Stats.TxFrameCnt++;
|
||||||
|
txDone = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process receiver interrupts
|
||||||
|
if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F) {
|
||||||
|
Stats.CRCErrors++;
|
||||||
|
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F) {
|
||||||
|
Stats.TimeOuts++;
|
||||||
|
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F ) {
|
||||||
|
Stats.RxFrameCnt++;
|
||||||
|
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F);
|
||||||
|
Stats.lastRSSI = READ_REG_FIELD(MR_SUBG_GLOB_STATUS->RX_INDICATOR, MR_SUBG_GLOB_STATUS_RX_INDICATOR_RSSI_LEVEL_ON_SYNC);
|
||||||
|
// shut off Rx until we are done with the frame
|
||||||
|
//__HAL_MRSUBG_STROBE_CMD(CMD_NOP);
|
||||||
|
|
||||||
|
rxReady = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnableRx();
|
||||||
|
}
|
48
code/IP400/Src/insque.c
Normal file
48
code/IP400/Src/insque.c
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/* insque(3C) routines
|
||||||
|
This file is in the public domain. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
@deftypefn Supplemental void insque (struct qelem *@var{elem}, @
|
||||||
|
struct qelem *@var{pred})
|
||||||
|
@deftypefnx Supplemental void remque (struct qelem *@var{elem})
|
||||||
|
|
||||||
|
Routines to manipulate queues built from doubly linked lists. The
|
||||||
|
@code{insque} routine inserts @var{elem} in the queue immediately
|
||||||
|
after @var{pred}. The @code{remque} routine removes @var{elem} from
|
||||||
|
its containing queue. These routines expect to be passed pointers to
|
||||||
|
structures which have as their first members a forward pointer and a
|
||||||
|
back pointer, like this prototype (although no prototype is provided):
|
||||||
|
|
||||||
|
@example
|
||||||
|
struct qelem @{
|
||||||
|
struct qelem *q_forw;
|
||||||
|
struct qelem *q_back;
|
||||||
|
char q_data[];
|
||||||
|
@};
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct qelem {
|
||||||
|
struct qelem *q_forw;
|
||||||
|
struct qelem *q_back;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void insque (struct qelem *elem, struct qelem *pred)
|
||||||
|
{
|
||||||
|
elem -> q_forw = pred -> q_forw;
|
||||||
|
pred -> q_forw -> q_back = elem;
|
||||||
|
elem -> q_back = pred;
|
||||||
|
pred -> q_forw = elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void remque (struct qelem *elem)
|
||||||
|
{
|
||||||
|
elem -> q_forw -> q_back = elem -> q_back;
|
||||||
|
elem -> q_back -> q_forw = elem -> q_forw;
|
||||||
|
}
|
289
code/IP400/Src/led.c
Normal file
289
code/IP400/Src/led.c
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: led.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: Handler for the LED's on nucleo or PI boards
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
/*
|
||||||
|
* notes for different implementations.
|
||||||
|
* On the Pi there are two LED's a dedicated red and a bicolor red-green.
|
||||||
|
* TX: On when transmitting
|
||||||
|
* BICOLOR: Green solid when Rx is enabled
|
||||||
|
* Red solid when HAL error or NMI occurred
|
||||||
|
*
|
||||||
|
* On the NUCLEO board, there are three, red, green and blue
|
||||||
|
* BLUE (LD1) Duplicats the TX LED
|
||||||
|
* GREEN (LD2) Duplicates the GREEN bi-color function
|
||||||
|
* RED (LD3) Duplicates the RED Bi-color function
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "led.h"
|
||||||
|
#include "usart.h"
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
// include LED defs from the right place
|
||||||
|
#if _BOARD_TYPE==NUCLEO_BOARD
|
||||||
|
#include <stm32wl3x_nucleo.h>
|
||||||
|
#else
|
||||||
|
#include <main.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// config
|
||||||
|
#define REVERSE_LEADS 1 // reverse Bi-color LED leads
|
||||||
|
|
||||||
|
// local defines
|
||||||
|
#define FLASH_OFF 0 // LED flashing: off state
|
||||||
|
#define FLASH_ON 1 // LED flashing: on state
|
||||||
|
|
||||||
|
// timer for 1/2 second when flashing
|
||||||
|
#define LED_TIMER 5 // timer
|
||||||
|
#define TEST_TIMER 20 // test timer
|
||||||
|
|
||||||
|
// internals
|
||||||
|
void SetLEDMode(uint8_t mode);
|
||||||
|
void LED_SetOff(void);
|
||||||
|
void LED_SetRed(void);
|
||||||
|
void LED_SetGreen(void);
|
||||||
|
void LED_SetError(void);
|
||||||
|
void setTxLED(BOOL state);
|
||||||
|
|
||||||
|
// vars
|
||||||
|
BOOL ToggleEnable; // led toggling enabled
|
||||||
|
uint8_t ledColour; // direction (colour)
|
||||||
|
uint8_t ledMode; // led mode
|
||||||
|
uint8_t ledState; // state on/off
|
||||||
|
uint8_t ledTimer; // timer for led test
|
||||||
|
uint8_t testNum; // test number
|
||||||
|
uint8_t testTimer; // test timer
|
||||||
|
uint8_t saveMode; // saved mode
|
||||||
|
|
||||||
|
//
|
||||||
|
// Notes on timer setup for STM32H732
|
||||||
|
// Prescaler value of 0 allows 64MHz clock to drive timer
|
||||||
|
// 1 reduces it to 32MHz
|
||||||
|
// Period divider of 6400 yeilds 100uSec time base
|
||||||
|
//
|
||||||
|
#define N_LED 5
|
||||||
|
struct led_tests_t {
|
||||||
|
char *testName;
|
||||||
|
uint8_t testMode;
|
||||||
|
} LEDTests[N_LED] = {
|
||||||
|
{"Bicolor RED On", BICOLOR_RED },
|
||||||
|
{"Bicolor GREEN On", BICOLOR_GREEN },
|
||||||
|
{"Bicolor off", BICOLOR_OFF },
|
||||||
|
{"Tx LED On", TX_LED_ON },
|
||||||
|
{"Tx LED Off", TX_LED_OFF }
|
||||||
|
};
|
||||||
|
// Initialization
|
||||||
|
void Led_Task_Init(void)
|
||||||
|
{
|
||||||
|
ToggleEnable = FALSE;
|
||||||
|
ledColour = BICOLOR_RED;
|
||||||
|
ledState = BICOLOR_OFF;
|
||||||
|
ledTimer = LED_TIMER;
|
||||||
|
LED_SetOff();
|
||||||
|
setTxLED(FALSE);
|
||||||
|
testTimer = 0;
|
||||||
|
testNum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We handle the flashing here...
|
||||||
|
// for the bicolor led, we can be solid red, solid green,
|
||||||
|
// or red-green flashing
|
||||||
|
void Led_Task_Exec(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(ToggleEnable) {
|
||||||
|
|
||||||
|
if(ledTimer != 0) {
|
||||||
|
ledTimer--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ledTimer--;
|
||||||
|
|
||||||
|
// blink LED if enabled
|
||||||
|
switch(ledMode) {
|
||||||
|
|
||||||
|
case BICOLOR_RED_FLASH:
|
||||||
|
case BICOLOR_GREEN_FLASH:
|
||||||
|
ledState = (ledState == FLASH_ON) ? FLASH_OFF : FLASH_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BICOLOR_RED_GREEN:
|
||||||
|
ledState = (ledState == FLASH_ON) ? FLASH_OFF : FLASH_ON;
|
||||||
|
ledColour = (ledColour == BICOLOR_RED) ? BICOLOR_GREEN : BICOLOR_RED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the LED state
|
||||||
|
if(ledState == FLASH_OFF) {
|
||||||
|
LED_SetOff();
|
||||||
|
} else {
|
||||||
|
if(ledColour == BICOLOR_RED)
|
||||||
|
LED_SetRed();
|
||||||
|
else
|
||||||
|
LED_SetGreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// led test mode
|
||||||
|
BOOL LedTest(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(testTimer == 0) {
|
||||||
|
if(testNum == 0)
|
||||||
|
saveMode = ledMode;
|
||||||
|
if(testNum == N_LED) {
|
||||||
|
testNum = 0;
|
||||||
|
SetLEDMode(saveMode);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
USART_Print_string("%s\r\n", LEDTests[testNum].testName);
|
||||||
|
SetLEDMode(LEDTests[testNum].testMode);
|
||||||
|
testTimer = TEST_TIMER;
|
||||||
|
testNum++;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
testTimer--;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API routines: set the LED mode
|
||||||
|
void SetLEDMode(uint8_t mode)
|
||||||
|
{
|
||||||
|
ledMode = mode;
|
||||||
|
|
||||||
|
switch(ledMode) {
|
||||||
|
|
||||||
|
case BICOLOR_OFF:
|
||||||
|
LED_SetOff();
|
||||||
|
ToggleEnable = FALSE;
|
||||||
|
ledState = FLASH_OFF;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BICOLOR_RED:
|
||||||
|
LED_SetRed();
|
||||||
|
ToggleEnable = FALSE;
|
||||||
|
ledColour = BICOLOR_RED;
|
||||||
|
ledState = FLASH_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BICOLOR_RED_FLASH:
|
||||||
|
LED_SetRed();
|
||||||
|
ToggleEnable = TRUE;
|
||||||
|
ledColour = BICOLOR_RED;
|
||||||
|
ledState = FLASH_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BICOLOR_GREEN:
|
||||||
|
LED_SetGreen();
|
||||||
|
ToggleEnable = FALSE;
|
||||||
|
ledColour = BICOLOR_GREEN;
|
||||||
|
ledState = FLASH_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BICOLOR_GREEN_FLASH:
|
||||||
|
LED_SetGreen();
|
||||||
|
ToggleEnable = TRUE;
|
||||||
|
ledColour = BICOLOR_GREEN;
|
||||||
|
ledState = FLASH_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BICOLOR_RED_GREEN:
|
||||||
|
LED_SetRed();
|
||||||
|
ToggleEnable = TRUE;
|
||||||
|
ledColour = BICOLOR_RED;
|
||||||
|
ledState = FLASH_ON;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TX_LED_ON:
|
||||||
|
setTxLED(TRUE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TX_LED_OFF:
|
||||||
|
setTxLED(FALSE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the actual updates
|
||||||
|
void LED_SetOff(void)
|
||||||
|
{
|
||||||
|
#if _BOARD_TYPE
|
||||||
|
BSP_LED_Off(LED_RED);
|
||||||
|
BSP_LED_Off(LED_GREEN);
|
||||||
|
BSP_LED_Off(LED_BLUE);
|
||||||
|
#else
|
||||||
|
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
|
||||||
|
HAL_GPIO_WritePin(LED_A_GPIO_Port, LED_A_Pin, GPIO_PIN_RESET);
|
||||||
|
HAL_GPIO_WritePin(LED_K_GPIO_Port, LED_K_Pin, GPIO_PIN_RESET);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTxLED(BOOL state)
|
||||||
|
{
|
||||||
|
#if _BOARD_TYPE
|
||||||
|
if(state)
|
||||||
|
BSP_LED_On(LED_BLUE);
|
||||||
|
else
|
||||||
|
BSP_LED_Off(LED_BLUE);
|
||||||
|
#else
|
||||||
|
if(state)
|
||||||
|
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
|
||||||
|
else
|
||||||
|
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// set fwd direction for bidir LED
|
||||||
|
#if REVERSE_LEADS
|
||||||
|
void LED_SetGreen(void)
|
||||||
|
#else
|
||||||
|
void LED_SetRed(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if _BOARD_TYPE
|
||||||
|
BSP_LED_On(LED_GREEN);
|
||||||
|
#else
|
||||||
|
HAL_GPIO_WritePin(LED_A_GPIO_Port, LED_A_Pin, GPIO_PIN_SET);
|
||||||
|
HAL_GPIO_WritePin(LED_K_GPIO_Port, LED_K_Pin, GPIO_PIN_RESET);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// set bwd direction for bidir LED
|
||||||
|
#if REVERSE_LEADS
|
||||||
|
void LED_SetRed(void)
|
||||||
|
#else
|
||||||
|
void LED_SetGreen(void)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if _BOARD_TYPE
|
||||||
|
BSP_LED_On(LED_RED);
|
||||||
|
#else
|
||||||
|
HAL_GPIO_WritePin(LED_A_GPIO_Port, LED_A_Pin, GPIO_PIN_RESET);
|
||||||
|
HAL_GPIO_WritePin(LED_K_GPIO_Port, LED_K_Pin, GPIO_PIN_SET);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
63
code/IP400/Src/logger.c
Normal file
63
code/IP400/Src/logger.c
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: MMDLTX
|
||||||
|
|
||||||
|
Module: Logger
|
||||||
|
|
||||||
|
File Name: audio.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Revision: 1.00
|
||||||
|
|
||||||
|
Description: Log an error message. On the nucleo, we send it out to the
|
||||||
|
console UAR/T, on the E04, it goes out to the LPUART which
|
||||||
|
is connected to the VCOM port.
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "usart.h"
|
||||||
|
|
||||||
|
char errmsg[200];
|
||||||
|
|
||||||
|
// need to figure out how to talk to UART on board...
|
||||||
|
void logger(int severity, char *format, ...)
|
||||||
|
{
|
||||||
|
|
||||||
|
// process the arg list
|
||||||
|
va_list argptr;
|
||||||
|
va_start(argptr, format);
|
||||||
|
vsprintf(errmsg,format, argptr);
|
||||||
|
va_end(argptr);
|
||||||
|
|
||||||
|
switch(severity) {
|
||||||
|
|
||||||
|
case LOG_NOTICE:
|
||||||
|
USART_Print_string("Notice: %s\r\n", errmsg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOG_ERROR:
|
||||||
|
USART_Print_string("Notice: %s\r\n", errmsg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOG_SEVERE:
|
||||||
|
USART_Print_string("Notice: %s\r\n", errmsg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
768
code/IP400/Src/menu.c
Normal file
768
code/IP400/Src/menu.c
Normal file
|
@ -0,0 +1,768 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: menu.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: Menu handler
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "usart.h"
|
||||||
|
#include "streambuffer.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "tasks.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "tod.h"
|
||||||
|
|
||||||
|
// menu state
|
||||||
|
uint8_t menuState; // menu state
|
||||||
|
enum {
|
||||||
|
MENU_OFF, // off
|
||||||
|
MENU_SHOWING, // showing the menu
|
||||||
|
MENU_SELECTING, // getting a selection
|
||||||
|
MENU_SELECTED, // item has been selected
|
||||||
|
MENU_PAUSED // pausing a while...
|
||||||
|
};
|
||||||
|
|
||||||
|
// key entry state
|
||||||
|
uint8_t entryState; // state of keyboard entry
|
||||||
|
enum {
|
||||||
|
NO_ENTRY=0, // no entry yet..
|
||||||
|
ENTERING, // entering a value
|
||||||
|
VALIDATING, // validating the entry
|
||||||
|
};
|
||||||
|
|
||||||
|
// menu items
|
||||||
|
#define NO_ITEM -1 // no item selected
|
||||||
|
|
||||||
|
// DEC VT100 Escape sequences
|
||||||
|
#define ASCII_TAB 0x09
|
||||||
|
#define ASCII_RET 0x0D
|
||||||
|
#define ASCII_ESC 0x1B // escape char
|
||||||
|
#define DEC_LEADIN '[' // lead-in character
|
||||||
|
#define MAX_SEQ 6 // max sequence length
|
||||||
|
|
||||||
|
// control codes..
|
||||||
|
enum {
|
||||||
|
CONTROL_CLEAR=0, // clear screen
|
||||||
|
CONTROL_HOME, // cursor home
|
||||||
|
CONTROL_DOUBLE_TOP, // double wide and height (top)
|
||||||
|
CONTROL_DOUBLE_BOTTOM, // double wide and height (bottom)
|
||||||
|
CONTROL_SINGLE, // single wide and height
|
||||||
|
NCONTROL_CODES // number of conrol codes
|
||||||
|
};
|
||||||
|
|
||||||
|
struct controlCodes_t {
|
||||||
|
char sequence[MAX_SEQ]; // sequence
|
||||||
|
uint8_t len; // length
|
||||||
|
} controlCodes[NCONTROL_CODES] = {
|
||||||
|
{{ASCII_ESC, DEC_LEADIN, '2', 'J'}, 4},
|
||||||
|
{{ASCII_ESC, DEC_LEADIN, 'H'}, 3},
|
||||||
|
{{ASCII_ESC, '#', '3'}, 3},
|
||||||
|
{{ASCII_ESC, '#', '4'}, 3},
|
||||||
|
{{ASCII_ESC, '#', '5'}, 3},
|
||||||
|
};
|
||||||
|
|
||||||
|
// strings common to all menu types
|
||||||
|
char *whatItem = "??Try again->";
|
||||||
|
char *menuSpacer = "\r\n\n";
|
||||||
|
char *pauseString = "Hit enter to continue->";
|
||||||
|
char *selectItem = "Select an item->";
|
||||||
|
|
||||||
|
static char menu[100]; // Buffer for file items
|
||||||
|
int sel_item = 0; // selected item
|
||||||
|
uint8_t activeMenu; // active menu
|
||||||
|
|
||||||
|
// forward refs in this module
|
||||||
|
void printMenu(void); // print the menu
|
||||||
|
int getMenuItem(void); // get a menu item
|
||||||
|
void sendControlCode(uint8_t code);
|
||||||
|
BOOL pause(void);
|
||||||
|
void Print_Frame_stats(FRAME_STATS *stats);
|
||||||
|
void Print_Radio_errors(uint32_t errs);
|
||||||
|
void Print_FSM_state(uint8_t state);
|
||||||
|
uint8_t getEntry(int activeMenu, int item);
|
||||||
|
|
||||||
|
// list of menus
|
||||||
|
enum {
|
||||||
|
MAIN_MENU=0, // main menu
|
||||||
|
RADIO_MENU, // radio params menu
|
||||||
|
STATION_MENU, // Station params menu
|
||||||
|
N_MENUS // number of menus
|
||||||
|
};
|
||||||
|
|
||||||
|
// menu return functions
|
||||||
|
enum {
|
||||||
|
RET_MORE=0, // more to do
|
||||||
|
RET_DONE, // done
|
||||||
|
RET_PAUSE // pause before leaving
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first part of this code contains the menus and processing routines
|
||||||
|
* handle the various menu options
|
||||||
|
* return TRUE if done, else FALSE
|
||||||
|
*/
|
||||||
|
uint8_t printAllSetup(void)
|
||||||
|
{
|
||||||
|
TIMEOFDAY tod;
|
||||||
|
getTOD(&tod);
|
||||||
|
|
||||||
|
USART_Print_string("Current firmware is at %d.%d\r\n",def_params.params.FirmwareVerMajor,
|
||||||
|
def_params.params.FirmwareVerMinor);
|
||||||
|
USART_Print_string("System time is %02d:%02d:%02d\r\n\n", tod.Hours, tod.Minutes, tod.Seconds);
|
||||||
|
|
||||||
|
printStationSetup();
|
||||||
|
printRadioSetup();
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
uint8_t printStnSetup(void)
|
||||||
|
{
|
||||||
|
printStationSetup();
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
uint8_t printRadSetup(void)
|
||||||
|
{
|
||||||
|
printRadioSetup();
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
// list mesh status
|
||||||
|
uint8_t listMesh(void)
|
||||||
|
{
|
||||||
|
Mesh_ListStatus();
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enter chat mode
|
||||||
|
uint8_t chatMode(void)
|
||||||
|
{
|
||||||
|
if(Chat_Task_exec())
|
||||||
|
return RET_PAUSE;
|
||||||
|
return RET_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump the rx stats
|
||||||
|
uint8_t showstats(void)
|
||||||
|
{
|
||||||
|
Print_Frame_stats(GetFrameStats());
|
||||||
|
Print_Radio_errors(GetRadioStatus());
|
||||||
|
Print_FSM_state(GetFSMState());
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the radio entry mode
|
||||||
|
uint8_t setRadio(void)
|
||||||
|
{
|
||||||
|
activeMenu = RADIO_MENU;
|
||||||
|
menuState = MENU_OFF;
|
||||||
|
return RET_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the station entry mode
|
||||||
|
uint8_t setStation(void)
|
||||||
|
{
|
||||||
|
activeMenu = STATION_MENU;
|
||||||
|
menuState = MENU_OFF;
|
||||||
|
return RET_DONE;
|
||||||
|
}
|
||||||
|
// enter chat mode
|
||||||
|
uint8_t ledTest(void)
|
||||||
|
{
|
||||||
|
if(LedTest())
|
||||||
|
return RET_PAUSE;
|
||||||
|
return RET_MORE;
|
||||||
|
}
|
||||||
|
// write the flash memory
|
||||||
|
uint8_t writeSetup(void)
|
||||||
|
{
|
||||||
|
if(!UpdateSetup()) {
|
||||||
|
USART_Print_string("Error in writing flash: setup may be corrupt\r\n");
|
||||||
|
} else {
|
||||||
|
USART_Print_string("Flash written successfully\r\n");
|
||||||
|
}
|
||||||
|
menuState = MENU_OFF;
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
// exit the current menu
|
||||||
|
uint8_t exitMenu(void)
|
||||||
|
{
|
||||||
|
activeMenu = MAIN_MENU;
|
||||||
|
menuState = MENU_OFF;
|
||||||
|
return RET_DONE;
|
||||||
|
}
|
||||||
|
// set a parameter value
|
||||||
|
uint8_t setParam(void)
|
||||||
|
{
|
||||||
|
return(getEntry(activeMenu, sel_item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main menu definition
|
||||||
|
* 1) define the number of line items
|
||||||
|
* 2) create menuitms struct
|
||||||
|
* 3) add to menucontents struct
|
||||||
|
*/
|
||||||
|
struct menuItems_t {
|
||||||
|
char *menuLine; // text of menu line
|
||||||
|
char selChar; // character to select it
|
||||||
|
uint8_t (*func)(void); // processing function
|
||||||
|
};
|
||||||
|
|
||||||
|
// main menu
|
||||||
|
#define N_MAINMENU 10 // number of lines in the menu
|
||||||
|
struct menuItems_t mainMenu[N_MAINMENU] = {
|
||||||
|
{ "List setup parameters\r\n", 'A', printAllSetup },
|
||||||
|
{ "Mesh Status\r\n", 'B', listMesh },
|
||||||
|
{ "Chat Mode\r\n", 'C', chatMode },
|
||||||
|
{ "Dump Frame stats\r\n", 'D', showstats },
|
||||||
|
{ "LED test\r\n", 'L', ledTest },
|
||||||
|
{ "Set Radio Parameters\r\n", 'R', setRadio },
|
||||||
|
{ "Set Station Parameters\r\n", 'S', setStation },
|
||||||
|
{ "Set clock (HH:MM)\r\n\n", 'T', setParam },
|
||||||
|
{ "Write Setup Values\r\n", 'W', writeSetup },
|
||||||
|
{ "Exit\r\n\n", 'X', exitMenu }
|
||||||
|
};
|
||||||
|
|
||||||
|
// radio menu
|
||||||
|
#define N_RADIOMENU 8
|
||||||
|
struct menuItems_t radioMenu[N_RADIOMENU] = {
|
||||||
|
{ "RF Frequency\r\n", 'A', setParam },
|
||||||
|
{ "Data Rate\r\n", 'B', setParam },
|
||||||
|
{ "Peak Deviation\r\n", 'C', setParam },
|
||||||
|
{ "Channel Filter BW\r\n", 'D', setParam },
|
||||||
|
{ "Output Power (dBm)\r\n", 'E', setParam },
|
||||||
|
{ "Rx Squelch (dBm)\r\n\n", 'F', setParam },
|
||||||
|
{ "List Settings\r\n", 'L', printRadSetup },
|
||||||
|
{ "Return to main menu\r\n\n", 'X', exitMenu }
|
||||||
|
};
|
||||||
|
|
||||||
|
// these need to correspond to the items above
|
||||||
|
|
||||||
|
// station menu
|
||||||
|
#define N_STATIONMENU 8
|
||||||
|
struct menuItems_t stationMenu[N_STATIONMENU] = {
|
||||||
|
{ "Callsign\r\n", 'A', setParam },
|
||||||
|
{ "Latitude\r\n", 'B', setParam },
|
||||||
|
{ "Longitude\r\n", 'C', setParam },
|
||||||
|
{ "Grid Square\r\n", 'D', setParam },
|
||||||
|
{ "Repeat Default\r\n", 'E', setParam },
|
||||||
|
{ "Beacon Interval\r\n\n", 'F', setParam },
|
||||||
|
{ "List Settings\r\n", 'L', printStnSetup },
|
||||||
|
{ "Return to main menu\r\n\n", 'X', exitMenu }
|
||||||
|
};
|
||||||
|
|
||||||
|
// menu contents
|
||||||
|
struct menuContents_t {
|
||||||
|
char *title; // title of the menu
|
||||||
|
int nMenuLines; // number of lines
|
||||||
|
struct menuItems_t *menus; // menu items
|
||||||
|
} menuContents[N_MENUS] = {
|
||||||
|
#if _BOARD_TYPE==NUCLEO_BOARD
|
||||||
|
{ " IP400 Nucleo Main menu\r\n", N_MAINMENU, mainMenu },
|
||||||
|
#else
|
||||||
|
{ " IP400 Pi menu\r\n", N_MAINMENU, mainMenu },
|
||||||
|
#endif
|
||||||
|
{ " Radio setup menu\r\n", N_RADIOMENU, radioMenu },
|
||||||
|
{ " Station Setup menu\r\n", N_STATIONMENU, stationMenu }
|
||||||
|
};
|
||||||
|
|
||||||
|
// menu (main) task
|
||||||
|
void Menu_Task_Init(void)
|
||||||
|
{
|
||||||
|
// start off with the menu showing
|
||||||
|
activeMenu = MAIN_MENU;
|
||||||
|
menuState = MENU_SHOWING;
|
||||||
|
entryState = NO_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a control code
|
||||||
|
void sendControlCode(uint8_t code)
|
||||||
|
{
|
||||||
|
USART_Send_String(controlCodes[code].sequence, controlCodes[code].len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a control code
|
||||||
|
void sendTextString(char *string)
|
||||||
|
{
|
||||||
|
strcpy(menu, string);
|
||||||
|
USART_Send_String(menu, strlen(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
// process our time slot
|
||||||
|
void Menu_Task_Exec(void)
|
||||||
|
{
|
||||||
|
int nBytesinBuff = 0;
|
||||||
|
char c;
|
||||||
|
struct menuItems_t *m;
|
||||||
|
|
||||||
|
switch(menuState) {
|
||||||
|
|
||||||
|
// if there is a return in the console buffer,
|
||||||
|
// bring up the menu
|
||||||
|
case MENU_OFF:
|
||||||
|
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||||
|
return;
|
||||||
|
for(int i=0;i<nBytesinBuff;i++)
|
||||||
|
if((c=databuffer_get(0)) == ASCII_RET)
|
||||||
|
menuState = MENU_SHOWING;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case MENU_SHOWING:
|
||||||
|
printMenu();
|
||||||
|
menuState = MENU_SELECTING;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case MENU_SELECTING:
|
||||||
|
if((sel_item=getMenuItem()) == NO_ITEM)
|
||||||
|
return;
|
||||||
|
menuState = MENU_SELECTED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MENU_SELECTED:
|
||||||
|
m=menuContents[activeMenu].menus;
|
||||||
|
m += sel_item;
|
||||||
|
switch((*m->func)()) {
|
||||||
|
|
||||||
|
case RET_MORE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RET_PAUSE:
|
||||||
|
sendTextString(pauseString);
|
||||||
|
menuState = MENU_PAUSED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RET_DONE:
|
||||||
|
menuState = MENU_SHOWING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MENU_PAUSED:
|
||||||
|
if(pause())
|
||||||
|
menuState = MENU_SHOWING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print the main menu
|
||||||
|
void printMenu(void)
|
||||||
|
{
|
||||||
|
struct menuItems_t *m;
|
||||||
|
|
||||||
|
// title
|
||||||
|
sendControlCode(CONTROL_CLEAR);
|
||||||
|
sendControlCode(CONTROL_HOME);
|
||||||
|
|
||||||
|
#if _BOARD_TYPE==NUCLEO_BOARD
|
||||||
|
// only use double wide mode with Nucleo
|
||||||
|
sendControlCode(CONTROL_DOUBLE_TOP);
|
||||||
|
sendTextString(menuContents[activeMenu].title);
|
||||||
|
sendControlCode(CONTROL_DOUBLE_BOTTOM);
|
||||||
|
sendTextString(menuContents[activeMenu].title);
|
||||||
|
sendControlCode(CONTROL_SINGLE);
|
||||||
|
sendTextString(menuSpacer);
|
||||||
|
#else
|
||||||
|
sendTextString(menuContents[activeMenu].title);
|
||||||
|
sendTextString(menuSpacer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// lines
|
||||||
|
int nMenuLines = menuContents[activeMenu].nMenuLines;
|
||||||
|
for(int i=0;i<nMenuLines;i++) {
|
||||||
|
m=menuContents[activeMenu].menus;
|
||||||
|
m += i;
|
||||||
|
menu[0] = m->selChar;
|
||||||
|
strcpy(&menu[1], ") ");
|
||||||
|
strcat(menu, m->menuLine);
|
||||||
|
USART_Send_String(menu, strlen(menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
// selection
|
||||||
|
sendTextString(selectItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a menu item and dispatch the correct processing routine
|
||||||
|
int getMenuItem(void)
|
||||||
|
{
|
||||||
|
int nBytesinBuff =0;
|
||||||
|
struct menuItems_t *m;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||||
|
return NO_ITEM;
|
||||||
|
|
||||||
|
for(int i=0;i<nBytesinBuff;i++) {
|
||||||
|
if((c=databuffer_get(0)) != BUFFER_NO_DATA) {
|
||||||
|
|
||||||
|
// translate and echo
|
||||||
|
c = islower(c) ? toupper(c) : c;
|
||||||
|
USART_Send_Char(c);
|
||||||
|
|
||||||
|
// find the correct processing routine
|
||||||
|
int nMenuLines = menuContents[activeMenu].nMenuLines;
|
||||||
|
for(int j=0;j<nMenuLines;j++) {
|
||||||
|
m=menuContents[activeMenu].menus;
|
||||||
|
m += j;
|
||||||
|
if(m->selChar == c) {
|
||||||
|
sendTextString(menuSpacer);
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendTextString(whatItem);
|
||||||
|
return NO_ITEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL pause(void)
|
||||||
|
{
|
||||||
|
if(databuffer_bytesInBuffer() == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if(databuffer_get(0) == ASCII_RET)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This part pertains to getting an entry and validating it..
|
||||||
|
*/
|
||||||
|
|
||||||
|
// keys in entry mode
|
||||||
|
#define KEY_EOL 0x0D // carriage return
|
||||||
|
#define KEY_ESC 0x1B // escape key
|
||||||
|
#define KEY_DEL 0x7F // delete key
|
||||||
|
#define KEY_BKSP 0x08 // backspace key
|
||||||
|
|
||||||
|
#define MAX_ENTRY 40 // max entry chars
|
||||||
|
|
||||||
|
BOOL delMode = FALSE;
|
||||||
|
int pos = 0;
|
||||||
|
char keyBuffer[MAX_ENTRY];
|
||||||
|
|
||||||
|
// forward refs
|
||||||
|
uint8_t validateEntry(int activeMenu, int item, char *keyBuffer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a key entry: basically stolen from chat.c
|
||||||
|
*/
|
||||||
|
uint8_t getKeyEntry(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
char c;
|
||||||
|
int nBytesinBuff;
|
||||||
|
|
||||||
|
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||||
|
return RET_MORE;
|
||||||
|
|
||||||
|
for(int i=0;i<nBytesinBuff;i++) {
|
||||||
|
|
||||||
|
c=databuffer_get(0);
|
||||||
|
|
||||||
|
if(delMode) {
|
||||||
|
if((c != KEY_DEL) && (c != KEY_BKSP)) {
|
||||||
|
USART_Print_string("\\%c", c);
|
||||||
|
if(pos < MAX_ENTRY)
|
||||||
|
keyBuffer[pos++] = c;
|
||||||
|
delMode = FALSE;
|
||||||
|
} else {
|
||||||
|
if(pos > 0) {
|
||||||
|
USART_Print_string("%c", keyBuffer[--pos]);
|
||||||
|
} else {
|
||||||
|
USART_Print_string("\\\r\n");
|
||||||
|
delMode = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// processing a key
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
// EOL key: sent the packet
|
||||||
|
case KEY_EOL:
|
||||||
|
USART_Print_string("\r\n");
|
||||||
|
keyBuffer[pos++] = '\0';
|
||||||
|
return RET_DONE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// escape key: abort the entry
|
||||||
|
case KEY_ESC:
|
||||||
|
return RET_PAUSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_DEL:
|
||||||
|
case KEY_BKSP:
|
||||||
|
USART_Print_string("\\%c", keyBuffer[--pos]);
|
||||||
|
delMode = TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
USART_Send_Char(c);
|
||||||
|
if(pos < MAX_ENTRY)
|
||||||
|
keyBuffer[pos++] = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RET_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get an entry and validate it
|
||||||
|
*/
|
||||||
|
uint8_t getEntry(int activeMenu, int item)
|
||||||
|
{
|
||||||
|
struct menuItems_t *m;
|
||||||
|
|
||||||
|
switch(entryState) {
|
||||||
|
|
||||||
|
case NO_ENTRY:
|
||||||
|
delMode = FALSE;
|
||||||
|
pos = 0;
|
||||||
|
entryState = ENTERING;
|
||||||
|
m=menuContents[activeMenu].menus;
|
||||||
|
m += item;
|
||||||
|
USART_Print_string("%s->", m->menuLine);
|
||||||
|
return RET_MORE;
|
||||||
|
|
||||||
|
case ENTERING:
|
||||||
|
int keyStat = getKeyEntry();
|
||||||
|
if(keyStat == RET_DONE) {
|
||||||
|
entryState = VALIDATING;
|
||||||
|
return RET_MORE;
|
||||||
|
}
|
||||||
|
return keyStat;
|
||||||
|
|
||||||
|
case VALIDATING:
|
||||||
|
entryState = NO_ENTRY;
|
||||||
|
return validateEntry(activeMenu, item, keyBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RET_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// data types we are updating
|
||||||
|
enum {
|
||||||
|
uint8_type, // uint8 field
|
||||||
|
int16_type, // int16 field
|
||||||
|
uint32_type, // uint32 field
|
||||||
|
float_type, // floating point
|
||||||
|
char_type, // character type
|
||||||
|
field_type // field type
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct to hold validation values
|
||||||
|
typedef struct field_validator_t {
|
||||||
|
int MinVal; // minimum value
|
||||||
|
int MaxVal; // maximum value
|
||||||
|
void *setupVal; // pointer to setup value
|
||||||
|
int type; // type of entry
|
||||||
|
uint32_t scalar; // scalar to convert to decimal
|
||||||
|
} FIELD_VALIDATOR;
|
||||||
|
|
||||||
|
// validators for radio mmenu
|
||||||
|
FIELD_VALIDATOR radioValidators[] = {
|
||||||
|
{ MIN_FREQ, 450000000, &setup_memory.params.radio_setup.lFrequencyBase, uint32_type, 1000000 },
|
||||||
|
{ 9600, 600000, &setup_memory.params.radio_setup.lDatarate, uint32_type, 1000 },
|
||||||
|
{ 12500, 150000, &setup_memory.params.radio_setup.lFreqDev, uint32_type, 1000 },
|
||||||
|
{ 2600, 1600000, &setup_memory.params.radio_setup.lBandwidth, uint32_type, 1000 },
|
||||||
|
{ 0, 20, &setup_memory.params.radio_setup.outputPower, uint8_type, 1 },
|
||||||
|
{ -115, 0, &setup_memory.params.radio_setup.rxSquelch, int16_type, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
FIELD_VALIDATOR stationValidators[] = {
|
||||||
|
{ 4, 6, &setup_memory.params.setup_data.stnCall, char_type, 0 },
|
||||||
|
{ 2, 14, &setup_memory.params.setup_data.latitude, char_type, 0 },
|
||||||
|
{ 2, 14, &setup_memory.params.setup_data.longitude, char_type, 0 },
|
||||||
|
{ 6, 6, &setup_memory.params.setup_data.gridSq, char_type, 0 },
|
||||||
|
{ 0, 0x8, &setup_memory.params.setup_data.flags, field_type, 0 },
|
||||||
|
{ 1, 100, &setup_memory.params.setup_data.beaconInt, int16_type, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t validateEntry(int activeMenu, int item, char *keyBuffer)
|
||||||
|
{
|
||||||
|
|
||||||
|
int newValue, min, max;
|
||||||
|
|
||||||
|
//NB: the cases here must jive with the menu items
|
||||||
|
switch(activeMenu) {
|
||||||
|
|
||||||
|
case MAIN_MENU: // the only menu item here is the clock
|
||||||
|
setTOD(keyBuffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RADIO_MENU:
|
||||||
|
// convert a floating point entry to the required decimal
|
||||||
|
if(isfloat(keyBuffer))
|
||||||
|
newValue = (int)(ascii2double(keyBuffer)*radioValidators[item].scalar);
|
||||||
|
else newValue = ascii2Dec(keyBuffer);
|
||||||
|
max = radioValidators[item].MaxVal;
|
||||||
|
min = radioValidators[item].MinVal;
|
||||||
|
if((newValue<min) || (newValue>max)) {
|
||||||
|
USART_Print_string("Must be in the range of %d to %d\r\n", min, max);
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
switch(radioValidators[item].type){
|
||||||
|
|
||||||
|
case uint8_type:
|
||||||
|
uint8_t *v8 = (uint8_t *)radioValidators[item].setupVal;
|
||||||
|
*v8 = (uint8_t)newValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case int16_type:
|
||||||
|
int16_t *v16 = (int16_t *)radioValidators[item].setupVal;
|
||||||
|
*v16 = (int16_t)newValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case uint32_type:
|
||||||
|
uint32_t *v32 = (uint32_t *)radioValidators[item].setupVal;
|
||||||
|
*v32 = (uint32_t)newValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATION_MENU:
|
||||||
|
switch(stationValidators[item].type){
|
||||||
|
|
||||||
|
case int16_type:
|
||||||
|
newValue = ascii2Dec(keyBuffer);
|
||||||
|
max = stationValidators[item].MaxVal;
|
||||||
|
min = stationValidators[item].MinVal;
|
||||||
|
if((newValue<min) || (newValue>max)) {
|
||||||
|
USART_Print_string("Must be in the range of %d to %d\r\n", min, max);
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
uint16_t *v8 = (uint16_t *)stationValidators[item].setupVal;
|
||||||
|
*v8 = (uint16_t)newValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case char_type:
|
||||||
|
size_t len = strlen(keyBuffer);
|
||||||
|
max = stationValidators[item].MaxVal;
|
||||||
|
min = stationValidators[item].MinVal;
|
||||||
|
if((len<min) || (len>max)) {
|
||||||
|
USART_Print_string("String must be %d to %d in length\r\n", min, max);
|
||||||
|
return RET_PAUSE;
|
||||||
|
}
|
||||||
|
strcpy((char *)stationValidators[item].setupVal, keyBuffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case field_type:
|
||||||
|
uint8_t val = keyBuffer[0] == 'T' ? 1 : 0;
|
||||||
|
uint8_t *f8= (uint8_t *)stationValidators[item].setupVal;
|
||||||
|
if(val)
|
||||||
|
*f8 |= (uint8_t)stationValidators[item].MinVal;
|
||||||
|
else
|
||||||
|
*f8 &= (uint8_t)stationValidators[item].MinVal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// falls to here when done...
|
||||||
|
return RET_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print the frame stats
|
||||||
|
*/
|
||||||
|
void Print_Frame_stats(FRAME_STATS *stats)
|
||||||
|
{
|
||||||
|
USART_Print_string("Frame Statistics\r\n\n");
|
||||||
|
|
||||||
|
USART_Print_string("Transmitted frames->%d\r\n", stats->TxFrameCnt);
|
||||||
|
USART_Print_string("CRC Errors->%d\r\n", stats->CRCErrors);
|
||||||
|
USART_Print_string("Rx Timeouts->%d\r\n", stats->TimeOuts);
|
||||||
|
USART_Print_string("Frames with good CRC->%d\r\n", stats->RxFrameCnt);
|
||||||
|
USART_Print_string("Beacon frames->%d\r\n", stats->nBeacons);
|
||||||
|
USART_Print_string("Repeated frames->%d\r\n", stats->nRepeated);
|
||||||
|
|
||||||
|
USART_Print_string("Processed Frames->%d\r\n", stats->RxFrameCnt);
|
||||||
|
USART_Print_string("Dropped frames->%d\r\n", stats->dropped);
|
||||||
|
USART_Print_string("Duplicate frames->%d\r\n", stats->duplicates);
|
||||||
|
USART_Print_string("Repeated frames->%d\r\n", stats->duplicates);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Print the radio error stats
|
||||||
|
*/
|
||||||
|
struct radio_errs_t {
|
||||||
|
uint32_t mask;
|
||||||
|
char *errmsg;
|
||||||
|
} radio_errs[N_RADIO_ERRS] = {
|
||||||
|
{ SEQ_COMPLETE_ERR, "Sequencer Error" },
|
||||||
|
{ SEQ_ACT_TIMEOUT, "Sequencer action timeout" },
|
||||||
|
{ PLL_CALAMP_ERR, "VCO Amplitude calibration error" },
|
||||||
|
{ PLL_CALFREQ_ERR, "VCO Frequency calibration error" },
|
||||||
|
{ PLL_UNLOCK_ERR, "PLL Unlocked" },
|
||||||
|
{ PLL_LOCK_FAIL, "PLL Lock failure" },
|
||||||
|
{ DBM_FIFO_ERR, "Data buffer FIFO error" }
|
||||||
|
};
|
||||||
|
//
|
||||||
|
void Print_Radio_errors(uint32_t errs)
|
||||||
|
{
|
||||||
|
if(errs == 0){
|
||||||
|
USART_Print_string("No radio errors\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(int i=0;i<N_RADIO_ERRS;i++)
|
||||||
|
if(errs & radio_errs[i].mask)
|
||||||
|
USART_Print_string("%s\r\n", radio_errs[i].errmsg);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Print the FSM state
|
||||||
|
*/
|
||||||
|
char *fsm_states[FSM_N_FSM_STATES] = {
|
||||||
|
"Idle",
|
||||||
|
"Enable RF registers",
|
||||||
|
"Wait for active 2",
|
||||||
|
"Active 2",
|
||||||
|
"Enable current",
|
||||||
|
"Synth setup",
|
||||||
|
"VCO calibration",
|
||||||
|
"Lock Rx and Rx",
|
||||||
|
"Llock on Rx",
|
||||||
|
"Enable PA",
|
||||||
|
"Transmit",
|
||||||
|
"Analog power down",
|
||||||
|
"End transmit",
|
||||||
|
"lock on Rx",
|
||||||
|
"Enable Rx",
|
||||||
|
"Enable LNA",
|
||||||
|
"Recieve",
|
||||||
|
"End rx",
|
||||||
|
"Synth power down"
|
||||||
|
};
|
||||||
|
void Print_FSM_state(uint8_t state)
|
||||||
|
{
|
||||||
|
if(state > FSM_N_FSM_STATES) {
|
||||||
|
USART_Print_string("\r\n?Unknown FSM state\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
USART_Print_string("\r\nRadio FSM State: %d: %s\r\n", state, fsm_states[state]);
|
||||||
|
}
|
262
code/IP400/Src/mesh.c
Normal file
262
code/IP400/Src/mesh.c
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: mesh.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: This code builds and lists the mesh status
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "tasks.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "usart.h"
|
||||||
|
#include "dataq.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "tod.h"
|
||||||
|
|
||||||
|
#define EXPIRY_TIME 100 // seconds since last heard
|
||||||
|
#define RSSI_SCALAR 161 // 95-Gain(rx), appros 65
|
||||||
|
|
||||||
|
char *FSKSpeeds[] = {
|
||||||
|
"FSK 1200",
|
||||||
|
"FSK 9600",
|
||||||
|
"FSK_56K",
|
||||||
|
"FSK_100K",
|
||||||
|
"FSK_200K",
|
||||||
|
"FSK_300K",
|
||||||
|
"FSK_400K",
|
||||||
|
"FSK_600K"
|
||||||
|
};
|
||||||
|
|
||||||
|
// mesh entry struct
|
||||||
|
struct mesh_entry_t {
|
||||||
|
struct mesh_entry_t *q_forw; // forward pointer
|
||||||
|
struct mesh_entry_t *q_back; // backward pointer
|
||||||
|
IP400_CALL encCall; // encoded callsign
|
||||||
|
uint32_t nextSeq; // next sequence number
|
||||||
|
int16_t lastRssi; // signal strength
|
||||||
|
uint8_t txPower; // transmit power
|
||||||
|
SETUP_FLAGS capabilities; // capabilities
|
||||||
|
TIMEOFDAY lastHeard; // last heard
|
||||||
|
uint8_t hopCount; // hop count
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since V0.4, the raw rx frame has already been decoded
|
||||||
|
* however, the capabilities are in the data buffer
|
||||||
|
* this may look cumbersome but it grows if the flags do
|
||||||
|
*/
|
||||||
|
BEACON_HEADER mesh_bcn_hdr; // beacon header
|
||||||
|
struct mesh_entry_t meshHead; // head of mesh chain
|
||||||
|
int nMeshEntries = 0;
|
||||||
|
uint32_t seqNum; // sequence number received
|
||||||
|
|
||||||
|
// forward refs in this module
|
||||||
|
struct mesh_entry_t *findCall(IP400_CALL *call);
|
||||||
|
void AddMeshEntry(IP400_FRAME *frameData, int16_t rssi, BOOL isBeacon);
|
||||||
|
|
||||||
|
// task initialization
|
||||||
|
void Mesh_Task_Init(void)
|
||||||
|
{
|
||||||
|
meshHead.q_forw = &meshHead;
|
||||||
|
meshHead.q_back = &meshHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
// process a beacon packet: if we don't have it, enter it
|
||||||
|
// otherwise update the last time heard
|
||||||
|
void Mesh_ProcessBeacon(void *rxFrame, uint32_t rssi)
|
||||||
|
{
|
||||||
|
struct mesh_entry_t *newEntry;
|
||||||
|
IP400_FRAME *frameData = (IP400_FRAME *)rxFrame;
|
||||||
|
|
||||||
|
TIMEOFDAY current;
|
||||||
|
getTOD(¤t);
|
||||||
|
|
||||||
|
int16_t actRSSI = rssi/2 - RSSI_SCALAR;
|
||||||
|
|
||||||
|
// see if we already know about it
|
||||||
|
// if so, just update last heard, expected sequence and rssi
|
||||||
|
// NB: firt frame is sent with an all '1's sequence number
|
||||||
|
if((newEntry = findCall(&frameData->source)) != NULL) {
|
||||||
|
|
||||||
|
// if it is repeated frame with a higher hop count, ignore it
|
||||||
|
if(newEntry->hopCount < frameData->flagfld.flags.hop_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
newEntry->lastHeard.Hours = current.Hours;
|
||||||
|
newEntry->lastHeard.Minutes = current.Minutes;
|
||||||
|
newEntry->lastHeard.Seconds = current.Seconds;
|
||||||
|
newEntry->lastRssi = actRSSI;
|
||||||
|
newEntry->nextSeq = frameData->seqNum == 0xFFFFFFFF ? 0 : ++frameData->seqNum;
|
||||||
|
|
||||||
|
//ugly, but necessary
|
||||||
|
memset(mesh_bcn_hdr.hdrBytes, 0, sizeof(BEACON_HEADER));
|
||||||
|
uint8_t *p = (uint8_t *)frameData->buf;
|
||||||
|
mesh_bcn_hdr.hdrBytes[0] = *p++;
|
||||||
|
mesh_bcn_hdr.hdrBytes[4] = *p;
|
||||||
|
|
||||||
|
newEntry->capabilities = mesh_bcn_hdr.setup.flags;
|
||||||
|
newEntry->txPower = mesh_bcn_hdr.setup.txPower;
|
||||||
|
|
||||||
|
// all done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// beacon from a new station
|
||||||
|
AddMeshEntry(frameData, actRSSI, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* add a new station to the mesh table
|
||||||
|
*/
|
||||||
|
void AddMeshEntry(IP400_FRAME *frameData, int16_t actRSSI, BOOL isBeacon)
|
||||||
|
{
|
||||||
|
struct mesh_entry_t *newEntry;
|
||||||
|
|
||||||
|
TIMEOFDAY current;
|
||||||
|
getTOD(¤t);
|
||||||
|
|
||||||
|
// create a new entry
|
||||||
|
if((newEntry = (struct mesh_entry_t *)malloc(sizeof(struct mesh_entry_t))) == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab call and capabilities
|
||||||
|
newEntry->encCall = frameData->source;
|
||||||
|
newEntry->lastHeard.Hours = current.Hours;
|
||||||
|
newEntry->lastHeard.Minutes = current.Minutes;
|
||||||
|
newEntry->lastHeard.Seconds = current.Seconds;
|
||||||
|
newEntry->nextSeq = frameData->seqNum == 0xFFFFFFFF ? 0 : ++frameData->seqNum;
|
||||||
|
newEntry->lastRssi = actRSSI;
|
||||||
|
newEntry->txPower = 0;
|
||||||
|
newEntry->hopCount = frameData->flagfld.flags.hop_count;
|
||||||
|
|
||||||
|
if(isBeacon) {
|
||||||
|
memcpy(mesh_bcn_hdr.hdrBytes, (struct beacon_hdr_t *)frameData->buf, sizeof(BEACON_HEADER));
|
||||||
|
newEntry->capabilities = mesh_bcn_hdr.setup.flags;
|
||||||
|
newEntry->txPower = mesh_bcn_hdr.setup.txPower;
|
||||||
|
} else {
|
||||||
|
memset(&newEntry->capabilities, 0, sizeof(SETUP_FLAGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert this at the end of the queue
|
||||||
|
insque((struct qelem *)newEntry, (struct qelem *)meshHead.q_back);
|
||||||
|
nMeshEntries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reject a duplicate frame that may have been repeated
|
||||||
|
* TRUE: keep it, FALSE: reject it
|
||||||
|
*/
|
||||||
|
BOOL Mesh_Accept_Frame(void *rxFrame, uint32_t rssi)
|
||||||
|
{
|
||||||
|
IP400_FRAME *frameData = (IP400_FRAME *)rxFrame;
|
||||||
|
struct mesh_entry_t *meshEntry;
|
||||||
|
|
||||||
|
if((meshEntry = findCall(&frameData->source)) != NULL) {
|
||||||
|
// rebooted
|
||||||
|
if(frameData->seqNum == 0xFFFFFFFF)
|
||||||
|
meshEntry->nextSeq = 0;
|
||||||
|
// reject if the seq is lower
|
||||||
|
if(frameData->seqNum < meshEntry->nextSeq)
|
||||||
|
return FALSE;
|
||||||
|
meshEntry->nextSeq = frameData->seqNum + 1;
|
||||||
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
// ok to process: but don't know the sender yet, so add him
|
||||||
|
int16_t actRSSI = rssi/2 - RSSI_SCALAR;
|
||||||
|
AddMeshEntry(frameData, actRSSI, FALSE);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a callsign in the list
|
||||||
|
struct mesh_entry_t *findCall(IP400_CALL *call)
|
||||||
|
{
|
||||||
|
struct mesh_entry_t *elem = meshHead.q_forw;
|
||||||
|
|
||||||
|
for(int i=0;i<nMeshEntries;i++) {
|
||||||
|
if(elem->encCall.callbytes.encoded == call->callbytes.encoded)
|
||||||
|
return elem;
|
||||||
|
elem = elem->q_forw;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char capabilties[50];
|
||||||
|
// return the capabilites of a node
|
||||||
|
char *GetCapabilities(SETUP_FLAGS cap)
|
||||||
|
{
|
||||||
|
mesh_bcn_hdr.setup.flags = cap;
|
||||||
|
|
||||||
|
if(mesh_bcn_hdr.hdrBytes[0] == 0) {
|
||||||
|
strcpy(capabilties, "Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
// modes
|
||||||
|
if(cap.fsk) {
|
||||||
|
sprintf(capabilties, "%s", FSKSpeeds[cap.rate]);
|
||||||
|
}
|
||||||
|
else if(cap.ofdm) {
|
||||||
|
strcat(capabilties, " OFDM");
|
||||||
|
}
|
||||||
|
else if(cap.aredn) {
|
||||||
|
strcat(capabilties, "AREDN");
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeat mode is on..
|
||||||
|
if(cap.repeat) {
|
||||||
|
strcat(capabilties, " RPT");
|
||||||
|
}
|
||||||
|
|
||||||
|
return capabilties;
|
||||||
|
}
|
||||||
|
|
||||||
|
// list the mesh status: walk the mesh entries
|
||||||
|
void Mesh_ListStatus(void)
|
||||||
|
{
|
||||||
|
USART_Print_string("Stations Heard: %d\r\n", nMeshEntries);
|
||||||
|
if(nMeshEntries == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// process the list
|
||||||
|
struct mesh_entry_t *elem = meshHead.q_forw;
|
||||||
|
char decodedCall[20];
|
||||||
|
uint16_t port;
|
||||||
|
|
||||||
|
USART_Print_string("Call(Port)\tRSSI\tNext Seq\tLast Heard\tHops\tCapabilities\r\n");
|
||||||
|
|
||||||
|
for(int i=0;i<nMeshEntries;i++) {
|
||||||
|
callDecode(&elem->encCall, decodedCall, &port);
|
||||||
|
USART_Print_string("%s(%d)\t%-03d\t%04d\t\t%02d:%02d:%02d\t%d\t%s %d dBm\r\n",
|
||||||
|
decodedCall, port,
|
||||||
|
elem->lastRssi,
|
||||||
|
elem->nextSeq,
|
||||||
|
elem->lastHeard.Hours, elem->lastHeard.Minutes, elem->lastHeard.Seconds,
|
||||||
|
elem->hopCount,
|
||||||
|
GetCapabilities(elem->capabilities), elem->txPower);
|
||||||
|
elem = elem->q_forw;
|
||||||
|
}
|
||||||
|
USART_Print_string("\r\n\n");
|
||||||
|
}
|
299
code/IP400/Src/setup.c
Normal file
299
code/IP400/Src/setup.c
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: setup.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: Holds and displays the setup data
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <main.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "usart.h"
|
||||||
|
|
||||||
|
#define USE_HAL
|
||||||
|
|
||||||
|
// flash stuff
|
||||||
|
#define FLASH_PAGE_ADDR ((uint32_t)0x1007F800)
|
||||||
|
#define FLASH_PAGE_NUM 127
|
||||||
|
static FLASH_EraseInitTypeDef EraseInitStruct;
|
||||||
|
|
||||||
|
// Default Setup Data
|
||||||
|
SETUP_MEMORY setup_memory;
|
||||||
|
|
||||||
|
SETUP_MEMORY def_params = {
|
||||||
|
.params.setup_data.flags = { 1, 0, 0 , 1, 0, FSK_100K }, // LSB specified first
|
||||||
|
.params.setup_data.stnCall ="NOCALL",
|
||||||
|
.params.setup_data.gridSq = "DO21vd",
|
||||||
|
.params.setup_data.latitude = "51.08",
|
||||||
|
.params.setup_data.longitude = "-114.10",
|
||||||
|
.params.setup_data.beaconInt = 5,
|
||||||
|
//
|
||||||
|
.params.radio_setup.lFrequencyBase = 445750000,
|
||||||
|
.params.radio_setup.xModulationSelect = MOD_4FSK,
|
||||||
|
.params.radio_setup.lDatarate = 100000,
|
||||||
|
.params.radio_setup.lFreqDev = 25000,
|
||||||
|
.params.radio_setup.lBandwidth = 200000,
|
||||||
|
.params.radio_setup.dsssExp = 0,
|
||||||
|
.params.radio_setup.outputPower = 14,
|
||||||
|
.params.radio_setup.PADrvMode = PA_DRV_TX_HP,
|
||||||
|
.params.radio_setup.rxSquelch = -95,
|
||||||
|
//
|
||||||
|
.params.FirmwareVerMajor = 0, // current rev is 0.4
|
||||||
|
.params.FirmwareVerMinor = 4,
|
||||||
|
.params.Magic = SETUP_MAGIC,
|
||||||
|
.params.SetupCRC = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
char prtBuf[100];
|
||||||
|
|
||||||
|
// enum fields in setup struct
|
||||||
|
// modulation type
|
||||||
|
char *modTypes[] = {
|
||||||
|
"2FSK",
|
||||||
|
"4FSK",
|
||||||
|
"2GFSK05",
|
||||||
|
"2GFSK1",
|
||||||
|
"4GFSK05",
|
||||||
|
"4GFSK1",
|
||||||
|
"ASK",
|
||||||
|
"OOK",
|
||||||
|
"POLAR",
|
||||||
|
"CW"
|
||||||
|
};
|
||||||
|
|
||||||
|
// PA modes
|
||||||
|
char *paModes[] = {
|
||||||
|
"TX 10dBm Max",
|
||||||
|
"HP 14dBm Max",
|
||||||
|
"TX_HP 20dBm Max"
|
||||||
|
};
|
||||||
|
|
||||||
|
// return the setup struct
|
||||||
|
STN_PARAMS *GetStationParams(void) // get the station params
|
||||||
|
{
|
||||||
|
return &setup_memory.params;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare call to station callsign
|
||||||
|
// returns true if callsigns match
|
||||||
|
BOOL CompareToMyCall(char *call)
|
||||||
|
{
|
||||||
|
if(!strncmp(call, setup_memory.params.setup_data.stnCall, MAX_CALL))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print the setup struct
|
||||||
|
*/
|
||||||
|
void printStationSetup(void)
|
||||||
|
{
|
||||||
|
// station callsign first
|
||||||
|
USART_Print_string("Station Callsign->%s\r\n", setup_memory.params.setup_data.stnCall);
|
||||||
|
if(setup_memory.params.setup_data.flags.ext)
|
||||||
|
USART_Print_string("Extended Callsign->%s\r\n");
|
||||||
|
|
||||||
|
USART_Print_string("Latitude->%s\r\n", setup_memory.params.setup_data.latitude);
|
||||||
|
USART_Print_string("Longitude->%s\r\n", setup_memory.params.setup_data.longitude);
|
||||||
|
USART_Print_string("Grid Square->%s\r\n", setup_memory.params.setup_data.gridSq);
|
||||||
|
|
||||||
|
USART_Print_string("Capabilities->");
|
||||||
|
if(setup_memory.params.setup_data.flags.fsk)
|
||||||
|
USART_Print_string("FSK ");
|
||||||
|
if(setup_memory.params.setup_data.flags.ofdm)
|
||||||
|
USART_Print_string("OFDM ");
|
||||||
|
if(setup_memory.params.setup_data.flags.aredn)
|
||||||
|
USART_Print_string("AREDN ");
|
||||||
|
if(setup_memory.params.setup_data.flags.repeat)
|
||||||
|
USART_Print_string("\r\nRepeat mode on by default\r\n");
|
||||||
|
else
|
||||||
|
USART_Print_string("\r\nRepeat mode off by default\r\n");
|
||||||
|
USART_Print_string("Beacon Interval->%d mins\r\n\n", setup_memory.params.setup_data.beaconInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printRadioSetup(void)
|
||||||
|
{
|
||||||
|
// dump the radio init struct
|
||||||
|
uint16_t fWhole = setup_memory.params.radio_setup.lFrequencyBase/1e6;
|
||||||
|
uint16_t fFract = setup_memory.params.radio_setup.lFrequencyBase/1e3 - fWhole*1e3;
|
||||||
|
USART_Print_string("RF Frequency->%d.%d MHz\r\n", fWhole, fFract);
|
||||||
|
|
||||||
|
USART_Print_string("Modulation method->%s\r\n", modTypes[setup_memory.params.radio_setup.xModulationSelect]);
|
||||||
|
|
||||||
|
uint16_t dWhole = setup_memory.params.radio_setup.lDatarate/1000;
|
||||||
|
uint16_t dFract = setup_memory.params.radio_setup.lDatarate - dWhole*1000;
|
||||||
|
USART_Print_string("Data Rate->%d.%d Kbps\r\n", dWhole, dFract);
|
||||||
|
|
||||||
|
uint16_t pWhole = setup_memory.params.radio_setup.lFreqDev/1000;
|
||||||
|
uint16_t pFract = setup_memory.params.radio_setup.lFreqDev - pWhole*1000;
|
||||||
|
USART_Print_string("Peak Deviation->%d.%d KHz\r\n", pWhole, pFract);
|
||||||
|
|
||||||
|
uint16_t bWhole = setup_memory.params.radio_setup.lBandwidth/1000;
|
||||||
|
uint16_t bFract = setup_memory.params.radio_setup.lBandwidth - bWhole*1000;
|
||||||
|
USART_Print_string("Channel Filter Bandwidth->%d.%d KHz\r\n", bWhole, bFract);
|
||||||
|
|
||||||
|
USART_Print_string("Output Power->%d dBm\r\n", setup_memory.params.radio_setup.outputPower);
|
||||||
|
USART_Print_string("PA Mode->%s\r\n", paModes[setup_memory.params.radio_setup.PADrvMode]);
|
||||||
|
USART_Print_string("Rx Squelch->%d\r\n\n\n", setup_memory.params.radio_setup.rxSquelch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code manages saving and reading the setup params
|
||||||
|
*/
|
||||||
|
// internals
|
||||||
|
uint32_t CalcSetupCRC(void);
|
||||||
|
|
||||||
|
BOOL UpdateSetup(void)
|
||||||
|
{
|
||||||
|
// update CRC before writing
|
||||||
|
setup_memory.params.SetupCRC = CalcSetupCRC();
|
||||||
|
|
||||||
|
if(WriteSetup() != HAL_OK) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ReadSetup()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!VerifySetup()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE; // only one iteration required
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the current setup record is valid
|
||||||
|
BOOL VerifySetup(void)
|
||||||
|
{
|
||||||
|
// if the magic number matches, then all is well
|
||||||
|
if(setup_memory.params.Magic != SETUP_MAGIC)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
uint32_t SetupCRC = CalcSetupCRC();
|
||||||
|
|
||||||
|
if(SetupCRC == setup_memory.params.SetupCRC)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the default setup
|
||||||
|
void SetDefSetup(void)
|
||||||
|
{
|
||||||
|
memcpy((void *)&setup_memory.bytes, (const void *)&def_params.bytes, sizeof(SETUP_MEMORY));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read setup from Flash memory
|
||||||
|
// data is stored in the last flash page
|
||||||
|
BOOL ReadSetup(void)
|
||||||
|
{
|
||||||
|
__IO uint32_t data32 = 0;;
|
||||||
|
|
||||||
|
uint32_t memAddr = FLASH_PAGE_ADDR;
|
||||||
|
|
||||||
|
uint32_t *dst_addr = setup_memory.flashwords;
|
||||||
|
uint16_t nwords = sizeof(STN_PARAMS)/sizeof(uint32_t);
|
||||||
|
|
||||||
|
while(nwords--){
|
||||||
|
data32 = *(__IO uint32_t *)memAddr;
|
||||||
|
*dst_addr++ = data32;
|
||||||
|
memAddr += sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the setup to OTP memory
|
||||||
|
HAL_StatusTypeDef WriteSetup(void)
|
||||||
|
{
|
||||||
|
HAL_StatusTypeDef status=0;
|
||||||
|
uint32_t PageError;
|
||||||
|
|
||||||
|
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||||
|
|
||||||
|
// erase it first
|
||||||
|
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||||
|
EraseInitStruct.Page = FLASH_PAGE_NUM;
|
||||||
|
EraseInitStruct.NbPages = 1;
|
||||||
|
if ((status=HAL_FLASHEx_Erase(&EraseInitStruct, &PageError)) != HAL_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// waste some time...
|
||||||
|
for(int i=0;i<1000;i++);
|
||||||
|
|
||||||
|
// now write
|
||||||
|
uint32_t memAddr = FLASH_PAGE_ADDR;
|
||||||
|
uint32_t *src_addr = setup_memory.flashwords;
|
||||||
|
uint16_t nwords = sizeof(SETUP_MEMORY)/sizeof(uint32_t);
|
||||||
|
|
||||||
|
while(nwords--)
|
||||||
|
{
|
||||||
|
uint32_t data32 = *(__IO uint32_t *)src_addr++;
|
||||||
|
|
||||||
|
if ((status=HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, memAddr, data32)) != HAL_OK)
|
||||||
|
return status;
|
||||||
|
memAddr += sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcuate the setup CRC
|
||||||
|
uint32_t CalcSetupCRC(void)
|
||||||
|
{
|
||||||
|
uint32_t CRCValue;
|
||||||
|
#ifdef USE_HAL
|
||||||
|
CRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)&setup_memory.bytes, sizeof(setup_memory) - sizeof(uint32_t));
|
||||||
|
#else
|
||||||
|
|
||||||
|
uint32_t cnt, count = sizeof(setup_memory) - sizeof(uint32_t);
|
||||||
|
uint8_t *arr = setup_memory.bytes;
|
||||||
|
|
||||||
|
/* Reset CRC data register if necessary */
|
||||||
|
CRC->CR = CRC_CR_RESET;
|
||||||
|
|
||||||
|
|
||||||
|
/* Calculate number of 32-bit blocks */
|
||||||
|
cnt = count >> 2;
|
||||||
|
|
||||||
|
/* Calculate */
|
||||||
|
while (cnt--) {
|
||||||
|
/* Set new value */
|
||||||
|
CRC->DR = *(uint32_t *)arr;
|
||||||
|
|
||||||
|
/* Increase by 4 */
|
||||||
|
arr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate remaining data as 8-bit */
|
||||||
|
cnt = count % 4;
|
||||||
|
|
||||||
|
/* Calculate */
|
||||||
|
while (cnt--) {
|
||||||
|
/* Set new value */
|
||||||
|
*((uint8_t *)&CRC->DR) = *arr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return data */
|
||||||
|
CRCValue = CRC->DR;
|
||||||
|
#endif
|
||||||
|
return(CRCValue);
|
||||||
|
}
|
80
code/IP400/Src/tod.c
Normal file
80
code/IP400/Src/tod.c
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: tod.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: Time of Day clock
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include "types.h"
|
||||||
|
#include "tod.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
TIMEOFDAY tod = { 0, 0, 0 }; // where we hold the TOD
|
||||||
|
|
||||||
|
// routines
|
||||||
|
void TOD_10SecTimer(void) // 10 second timer
|
||||||
|
{
|
||||||
|
// handle seconds
|
||||||
|
tod.Seconds += 10;
|
||||||
|
if(tod.Seconds >= 60) {
|
||||||
|
tod.Minutes += tod.Seconds/60;
|
||||||
|
tod.Seconds %= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle minutes
|
||||||
|
if(tod.Minutes >= 60) {
|
||||||
|
tod.Hours += tod.Minutes/60;
|
||||||
|
tod.Minutes %= 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle hours
|
||||||
|
if(tod.Hours > 24)
|
||||||
|
tod.Hours %= 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return pointer to Time of Day
|
||||||
|
void getTOD(TIMEOFDAY *time)
|
||||||
|
{
|
||||||
|
time->Hours = tod.Hours;
|
||||||
|
time->Minutes = tod.Minutes;
|
||||||
|
time->Seconds = tod.Seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the TOD from HH:MM string
|
||||||
|
// somewhat brute force parser
|
||||||
|
BOOL setTOD(char *todString)
|
||||||
|
{
|
||||||
|
char *todValues[5];
|
||||||
|
|
||||||
|
int nParams = explode_string(todString, todValues, 5, ':', '"');
|
||||||
|
if(nParams != 2)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
int nHours = ascii2Dec(todValues[0]);
|
||||||
|
if((nHours < 0) || (nHours > 24))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
int nMins = ascii2Dec(todValues[1]);
|
||||||
|
if((nHours < 0) || (nHours > 60))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
tod.Hours = nHours;
|
||||||
|
tod.Minutes = nMins;
|
||||||
|
tod.Seconds = 0;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
205
code/IP400/Src/usart.c
Normal file
205
code/IP400/Src/usart.c
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: IP400 Modem
|
||||||
|
|
||||||
|
File Name: usart.c
|
||||||
|
|
||||||
|
Author: Martin C. Alcock, VE6VH
|
||||||
|
|
||||||
|
Revision: 1.05
|
||||||
|
|
||||||
|
Description: API for USART handling
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <cmsis_os2.h>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <semphr.h>
|
||||||
|
|
||||||
|
#include "stream_buffer.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "streambuffer.h"
|
||||||
|
#include "usart.h"
|
||||||
|
|
||||||
|
// local defines
|
||||||
|
#define RX_TIMEOUT 10000 // 10 second rx timeout
|
||||||
|
#define TX_TIMEOUT 1000 // 1s transmit timeout
|
||||||
|
|
||||||
|
// stream buffer
|
||||||
|
StreamBufferHandle_t USART_RxBuffer; // handle to buffer
|
||||||
|
StaticStreamBuffer_t USART_StreamBuffer;
|
||||||
|
|
||||||
|
// UART/T Support
|
||||||
|
SemaphoreHandle_t txCompleted; // tx completed semaphore
|
||||||
|
static DATA_ELEMENT USARTRxChar[10]; // last received character
|
||||||
|
|
||||||
|
uint8_t buffer_data[bufferSIZE];
|
||||||
|
char usartPrintBuffer[200];
|
||||||
|
|
||||||
|
// fwd refs here...
|
||||||
|
void USART_Receive_char(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* API for the rx data buffer
|
||||||
|
*/
|
||||||
|
void USART_API_init(void)
|
||||||
|
{
|
||||||
|
USART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, buffer_data, &USART_StreamBuffer);
|
||||||
|
USART_RxBuffer_reset();
|
||||||
|
|
||||||
|
// create the tx completed semaphore
|
||||||
|
txCompleted = xSemaphoreCreateBinary();
|
||||||
|
|
||||||
|
// start the rx process
|
||||||
|
USART_Receive_char();
|
||||||
|
}
|
||||||
|
|
||||||
|
void USART_RxBuffer_reset(void)
|
||||||
|
{
|
||||||
|
xStreamBufferReset(USART_RxBuffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the number of byte in the buffer
|
||||||
|
size_t databuffer_bytesInBuffer(void)
|
||||||
|
{
|
||||||
|
size_t nBytes = xStreamBufferBytesAvailable(USART_RxBuffer);
|
||||||
|
return nBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a byte from the buffer: blocks if timeout is zero,
|
||||||
|
// else returns BUFFER_NO_DATA if timeout exceeded
|
||||||
|
DATA_ELEMENT databuffer_get(UART_TIMEOUT_T timeout)
|
||||||
|
{
|
||||||
|
DATA_ELEMENT retval;
|
||||||
|
|
||||||
|
TickType_t tickTimeout;
|
||||||
|
|
||||||
|
if(timeout == 0)
|
||||||
|
tickTimeout = portMAX_DELAY;
|
||||||
|
else
|
||||||
|
tickTimeout = pdMS_TO_TICKS(timeout);
|
||||||
|
|
||||||
|
if(xStreamBufferReceive(USART_RxBuffer, &retval, 1, tickTimeout) == 0)
|
||||||
|
return BUFFER_NO_DATA;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if the buffer contains a keyword, save data up to it if needed
|
||||||
|
BOOL databuffer_contains(const char *tag, UART_TIMEOUT_T rx_timeout, BOOL saveData, char *SaveBuffer)
|
||||||
|
{
|
||||||
|
DATA_ELEMENT c;
|
||||||
|
|
||||||
|
uint8_t tagSize = (uint8_t)(strlen(tag) & 0xff);
|
||||||
|
uint8_t tagLen = tagSize;
|
||||||
|
|
||||||
|
const char *tagAddr = tag;
|
||||||
|
|
||||||
|
while ((c = databuffer_get(rx_timeout)) != BUFFER_NO_DATA) {
|
||||||
|
if (c == *tagAddr) {
|
||||||
|
if (--tagLen == 0) {
|
||||||
|
if(saveData) {
|
||||||
|
*SaveBuffer++ = c;
|
||||||
|
*SaveBuffer = '\0';
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
tagAddr++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tagAddr = tag;
|
||||||
|
tagLen = tagSize;
|
||||||
|
}
|
||||||
|
if(saveData)
|
||||||
|
*SaveBuffer++ = c;
|
||||||
|
}
|
||||||
|
if(saveData)
|
||||||
|
*SaveBuffer = '\0';
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Low level HAL interaction functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// send a packet (with timeout)
|
||||||
|
BOOL USART_Send_String(const char *string, size_t len)
|
||||||
|
{
|
||||||
|
// send using DMA
|
||||||
|
uint16_t dataLen = (uint16_t)len;
|
||||||
|
if((HAL_UART_Transmit_DMA(&huart1, (const uint8_t *)string, dataLen)) != HAL_OK)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// wait for completion..
|
||||||
|
if((xSemaphoreTake(txCompleted, pdMS_TO_TICKS(TX_TIMEOUT))) == pdTRUE)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send a packet (with timeout)
|
||||||
|
BOOL USART_Send_Char(const char c)
|
||||||
|
{
|
||||||
|
|
||||||
|
// send using interrupt
|
||||||
|
HAL_UART_Transmit_IT(&huart1, (const uint8_t *)&c, 1);
|
||||||
|
|
||||||
|
// wait for completion..
|
||||||
|
if((xSemaphoreTake(txCompleted, pdMS_TO_TICKS(TX_TIMEOUT))) == pdTRUE)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print a string to the UAR/T
|
||||||
|
void USART_Print_string(char *format, ...)
|
||||||
|
{
|
||||||
|
// process the arg list
|
||||||
|
va_list argptr;
|
||||||
|
va_start(argptr, format);
|
||||||
|
vsprintf(usartPrintBuffer,format, argptr);
|
||||||
|
va_end(argptr);
|
||||||
|
|
||||||
|
USART_Send_String(usartPrintBuffer, strlen(usartPrintBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Transmit completed: trigger semaphore
|
||||||
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
|
{
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
xSemaphoreGiveFromISR(txCompleted, &xHigherPriorityTaskWoken);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive a byte with DMA, wait for DMA completed interrupt
|
||||||
|
void USART_Receive_char(void)
|
||||||
|
{
|
||||||
|
HAL_UART_Receive_IT(&huart1,(uint8_t *)USARTRxChar,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback function when rx is completed: overrides previous
|
||||||
|
// __weak definition
|
||||||
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||||
|
{
|
||||||
|
|
||||||
|
// do not store any control chars
|
||||||
|
xStreamBufferSendFromISR(USART_RxBuffer, USARTRxChar, 1, NULL);
|
||||||
|
HAL_UART_Receive_IT(&huart1,(uint8_t *)USARTRxChar,1);
|
||||||
|
}
|
||||||
|
|
131
code/IP400/Src/utils.c
Normal file
131
code/IP400/Src/utils.c
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
/*---------------------------------------------------------------------------
|
||||||
|
Project: WL33_NUCLEO_UART
|
||||||
|
|
||||||
|
File Name: utils.c
|
||||||
|
|
||||||
|
Author: MartinA
|
||||||
|
|
||||||
|
Description: utility routines
|
||||||
|
|
||||||
|
This program 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 2 of the License, or
|
||||||
|
(at your option) any later version, provided this copyright notice
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Copyright (c) Alberta Digital Radio Communications Society
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
// ascii to decimal
|
||||||
|
int ascii2Dec(char *dec)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
int sgn = 1;
|
||||||
|
while (*dec) {
|
||||||
|
if(*dec == '-')
|
||||||
|
sgn = -1;
|
||||||
|
else retval = retval*10 + (*dec - '0');
|
||||||
|
dec++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval*sgn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if an entry is floating point
|
||||||
|
BOOL isfloat(char *val)
|
||||||
|
{
|
||||||
|
while(*val)
|
||||||
|
if(*val++ == '.')
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert an ascii string to a double
|
||||||
|
// until we encounter the decimal place, treat it the same as an integer.
|
||||||
|
// after that, scale each digit down appropriately
|
||||||
|
//
|
||||||
|
double ascii2double(char *val)
|
||||||
|
{
|
||||||
|
double retval = 0;
|
||||||
|
int power = -1, inc = 1;
|
||||||
|
int sgn = 1;
|
||||||
|
|
||||||
|
while(*val) {
|
||||||
|
|
||||||
|
switch(*val) {
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
sgn = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
power = -1;
|
||||||
|
inc = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(inc > 0) {
|
||||||
|
retval = retval*10 + (*val - '0');
|
||||||
|
} else {
|
||||||
|
retval += pow(10.0, power) * (*val - '0');
|
||||||
|
power += inc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
val++;
|
||||||
|
}
|
||||||
|
return retval * sgn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Your basic linux-stle (argv, argc) parser based on delimiters
|
||||||
|
// string is destroyed
|
||||||
|
int explode_string(char *str, char *strp[], int limit, char delim, char quote)
|
||||||
|
{
|
||||||
|
int i,l,inquo;
|
||||||
|
|
||||||
|
inquo = 0;
|
||||||
|
i = 0;
|
||||||
|
strp[i++] = str;
|
||||||
|
if (!*str)
|
||||||
|
{
|
||||||
|
strp[0] = 0;
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
for(l = 0; *str && (l < limit) ; str++)
|
||||||
|
{
|
||||||
|
if(quote)
|
||||||
|
{
|
||||||
|
if (*str == quote)
|
||||||
|
{
|
||||||
|
if (inquo)
|
||||||
|
{
|
||||||
|
*str = 0;
|
||||||
|
inquo = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strp[i - 1] = str + 1;
|
||||||
|
inquo = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((*str == delim) && (!inquo))
|
||||||
|
{
|
||||||
|
*str = 0;
|
||||||
|
l++;
|
||||||
|
strp[i++] = str + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strp[i] = 0;
|
||||||
|
return(i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue