Working RTTY! And new gps stuff

rocketry
Richard Eoin Meadows 2014-08-30 21:11:35 +01:00
rodzic d477adeecc
commit 299725405d
13 zmienionych plików z 741 dodań i 349 usunięć

Wyświetl plik

@ -25,134 +25,13 @@
#ifndef GPS_H
#define GPS_H
/* We use the packed attribute so we can copy direct to structs */
#define __PACKED__ __attribute__((packed))
void gps_update();
/**
* UBX CFG ANT Antenna Control Settings
*/
volatile struct ubx_cfg_ant {
uint16_t flags;
uint16_t pins;
} __PACKED__ ubx_cfg_ant;
/**
* UBX CFG NAV5 Navigation Engine Settings
*/
volatile struct ubx_cfg_nav5 {
uint16_t mask;
uint8_t dynModel;
uint8_t fixMode;
int32_t fixedAlt;
uint32_t fixedAltVar;
int8_t minElev;
uint8_t drLimit;
uint16_t pDop;
uint16_t tDop;
uint16_t pAcc;
uint16_t tAcc;
uint8_t staticHoldThresh;
uint8_t dgpsTimeOut;
uint32_t res2;
uint32_t res3;
uint32_t res4;
} __PACKED__ ubx_cfg_nav5;
/**
* UBX CFG TP TimePulse Parameters
*/
volatile struct ubx_cfg_tp {
uint32_t interval;
uint32_t length;
int8_t status;
uint8_t timeRef;
uint8_t flags;
uint8_t res;
int16_t antennaCableDelay;
int16_t rfGroupDelay;
int32_t userDelay;
} __PACKED__ ubx_cfg_tp;
/**
* UBX CFG TP5 TimePulse Parameters
*/
volatile struct ubx_cfg_tp5 {
uint8_t tpIdx;
uint8_t res0;
uint16_t res1;
int16_t antCableDelay;
int16_t rfGroupDelay;
uint32_t freqPeriod;
uint32_t freqPeriodLoc;
uint32_t pulseLenRatio;
uint32_t pulseLenRatioLock;
int32_t userConfigDelay;
uint32_t flags;
} __PACKED__ ubx_cfg_tp5;
/**
* UBX NAV POSLLH Geodetic Position Solution
*/
volatile struct ubx_nav_posllh {
uint32_t iTOW;
int32_t lon;
int32_t lat;
int32_t height;
int32_t hMSL;
uint32_t hAcc;
uint32_t vAcc;
} __PACKED__ ubx_nav_posllh;
/**
* UBX NAV TIMEUTC
*/
volatile struct ubx_nav_timeutc {
uint32_t iTOW;
uint32_t tAcc;
int32_t nano;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
uint8_t valid;
} __PACKED__ ubx_nav_timeutc;
/**
* UBX NAV SOL Navigation Solution Information
*/
volatile struct ubx_nav_sol {
uint32_t iTOW;
int32_t fTOW;
uint16_t week;
uint8_t gpsFix;
uint8_t flags;
int32_t ecefX;
int32_t ecefY;
int32_t ecefZ;
uint32_t pAcc;
int32_t ecefVX;
int32_t ecefVY;
int32_t ecefVZ;
uint32_t sAcc;
uint16_t pDOP;
uint8_t res1;
uint8_t numSV;
uint32_t res2;
} __PACKED__ ubx_nav_sol;
struct ubx_nav_posllh gps_get_nav_posllh();
struct ubx_nav_sol gps_get_nav_sol();
struct ubx_nav_timeutc gps_get_nav_timeutc();
/**
* UBX Dynamic Platform Model
*/
enum {
UBX_PLATFORM_MODEL_PORTABLE = 0,
UBX_PLATFORM_MODEL_STATIONARY = 2,
UBX_PLATFORM_MODEL_PEDESTRIAN = 3,
UBX_PLATFORM_MODEL_AUTOMOTIVE = 4,
UBX_PLATFORM_MODEL_SEA = 5,
UBX_PLATFORM_MODEL_AIRBORNE_1G = 6,
UBX_PLATFORM_MODEL_AIRBORNE_2G = 7,
UBX_PLATFORM_MODEL_AIRBORNE_4G = 8,
};
void usart_loopback_test(void);
char* gps_get_frame(void);
void gps_init(void);
void usart_loopback_test(void);
#endif /* GPS_H */

Wyświetl plik

@ -76,8 +76,7 @@
#define GPS_TIME_PIN PIN_PA28
#define GPS_TIME_PINMUX PINMUX_PA28H_GCLK_IO0
#define GPS_SERCOM_MUX USART_RX_1_TX_0_XCK_1
#define GPS_TIMEPULSE_FREQ 4000000
// 32768
#define GPS_TIMEPULSE_FREQ 1000
#define GPS_PLATFORM_MODEL UBX_PLATFORM_MODEL_AIRBORNE_1G
/**

Wyświetl plik

@ -0,0 +1,32 @@
/*
* Bit-bangs RTTY
* Copyright (C) 2014 Richard Meadows <richardeoin>
*
* 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.
*/
#ifndef RTTY_H
#define RTTY_H
int rtty_active(void);
int rtty_set_string(char* string, uint32_t length);
void rtty_tick(void);
#endif /* RTTY_H */

Wyświetl plik

@ -35,7 +35,7 @@ void si4060_shutdown(void);
#define XO_FREQ 16000000UL
#define RF_FREQ_HZ 434600000.0f
#define RF_DEV_HZ 100.0f
#define RF_DEV_HZ 200.0f
#define F_INT (2 * XO_FREQ / 8)
#define FDIV_INTE ( (RF_FREQ_HZ / F_INT) - 1)

Wyświetl plik

@ -394,6 +394,19 @@ enum system_clock_apb_bus {
SYSTEM_CLOCK_APB_APBC,
};
/**
* Possible NVM flash wait state settings
*/
enum system_wait_states {
/** Wait state maximum frequencies at 1.8V */
SYSTEM_WAIT_STATE_1_8V_14MHZ = 0,
SYSTEM_WAIT_STATE_1_8V_28MHZ = 1,
SYSTEM_WAIT_STATE_1_8V_42MHZ = 2,
SYSTEM_WAIT_STATE_1_8V_48MHZ = 3,
/** Wait state maximum frequencies at 3.3V */
SYSTEM_WAIT_STATE_3_3V_24MHZ = 0,
SYSTEM_WAIT_STATE_3_3V_48MHZ = 1,
};
void system_clock_source_osc8m_set_config(enum system_osc8m_div prescaler,
bool run_in_standby,
@ -819,7 +832,7 @@ void system_clock_init(void);
*
* \param[in] wait_states Number of wait states to use for internal flash
*/
static inline void system_flash_set_waitstates(uint8_t wait_states)
static inline void system_flash_set_waitstates(const enum system_wait_states wait_states)
{
assert(NVMCTRL_CTRLB_RWS((uint32_t)wait_states) ==
((uint32_t)wait_states << NVMCTRL_CTRLB_RWS_Pos));

Wyświetl plik

@ -543,9 +543,9 @@ void tc_set_top_value (Tc* const hw,
const uint32_t top_value);
void tc_enable_events(Tc* const hw,
struct tc_events *const events);
struct tc_events *const events);
void tc_disable_events(Tc* const hw,
struct tc_events *const events);
struct tc_events *const events);
enum tc_status_t tc_init(Tc* const hw,
enum gclk_generator source_clock,

Wyświetl plik

@ -0,0 +1,240 @@
/*
* UBX Message definitions
* Copyright (C) 2014 Richard Meadows <richardeoin>
*
* 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.
*/
#ifndef UBX_MESSAGES_H
#define UBX_MESSAGES_H
#include "samd20.h"
/** We use the packed attribute so we can copy direct to structs */
#define __PACKED__ __attribute__((packed))
/** UBX Message IDs are 16-bit types */
typedef uint16_t ubx_message_id_t;
/** Used for storing the state of each packet */
enum ubx_packet_state {
UBX_PACKET_WAITING,
UBX_PACKET_ACK,
UBX_PACKET_NACK,
};
/** Generic UBX Message Type. Each message type extended is from this */
typedef struct {
ubx_message_id_t id;
enum ubx_packet_state state;
} ubx_message_t;
/** UBX Class Types */
enum {
UBX_NAV = 0x01,
UBX_RXM = 0x02,
UBX_INF = 0x04,
UBX_ACK = 0x05,
UBX_CFG = 0x06,
UBX_MON = 0x0A,
UBX_AID = 0x0B,
UBX_TIM = 0x0D,
UBX_ESF = 0x10,
};
/**
* =============================================================================
* UBX CFG Message Types =======================================================
* =============================================================================
*/
/**
* UBX CFG ANT Antenna Control Settings
*/
__PACKED__ struct ubx_cfg_ant {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint16_t flags;
uint16_t pins;
} payload;
};
/**
* UBX CFG NAV5 Navigation Engine Settings
*/
__PACKED__ struct ubx_cfg_nav5 {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint16_t mask;
uint8_t dynModel;
uint8_t fixMode;
int32_t fixedAlt;
uint32_t fixedAltVar;
int8_t minElev;
uint8_t drLimit;
uint16_t pDop;
uint16_t tDop;
uint16_t pAcc;
uint16_t tAcc;
uint8_t staticHoldThresh;
uint8_t dgpsTimeOut;
uint32_t res2;
uint32_t res3;
uint32_t res4;
} payload;
};
/**
* UBX CFG TP TimePulse Parameters
*/
__PACKED__ struct ubx_cfg_tp {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint32_t interval;
uint32_t length;
int8_t status;
uint8_t timeRef;
uint8_t flags;
uint8_t res;
int16_t antennaCableDelay;
int16_t rfGroupDelay;
int32_t userDelay;
} payload;
};
/**
* UBX CFG TP5 TimePulse Parameters
*/
__PACKED__ struct ubx_cfg_tp5 {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint8_t tpIdx;
uint8_t res0;
uint16_t res1;
int16_t antCableDelay;
int16_t rfGroupDelay;
uint32_t freqPeriod;
uint32_t freqPeriodLoc;
uint32_t pulseLenRatio;
uint32_t pulseLenRatioLock;
int32_t userConfigDelay;
uint32_t flags;
} payload;
};
/**
* UBX CFG PRT Polls the configuration for one I/O Port
*/
__PACKED__ struct ubx_cfg_prt {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint8_t portID;
uint8_t res0;
uint16_t txReady;
uint32_t mode;
uint32_t baudRate;
uint16_t inProtoMask;
uint16_t outProtoMask;
uint16_t flags;
uint16_t res3;
} payload;
};
/**
* UBX Dynamic Platform Model
*/
enum {
UBX_PLATFORM_MODEL_PORTABLE = 0,
UBX_PLATFORM_MODEL_STATIONARY = 2,
UBX_PLATFORM_MODEL_PEDESTRIAN = 3,
UBX_PLATFORM_MODEL_AUTOMOTIVE = 4,
UBX_PLATFORM_MODEL_SEA = 5,
UBX_PLATFORM_MODEL_AIRBORNE_1G = 6,
UBX_PLATFORM_MODEL_AIRBORNE_2G = 7,
UBX_PLATFORM_MODEL_AIRBORNE_4G = 8,
};
/**
* =============================================================================
* UBX NAV Message Types =======================================================
* =============================================================================
*/
/**
* UBX NAV POSLLH Geodetic Position Solution
*/
__PACKED__ struct ubx_nav_posllh {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint32_t iTOW;
int32_t lon;
int32_t lat;
int32_t height;
int32_t hMSL;
uint32_t hAcc;
uint32_t vAcc;
} payload;
};
/**
* UBX NAV TIMEUTC
*/
__PACKED__ struct ubx_nav_timeutc {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint32_t iTOW;
uint32_t tAcc;
int32_t nano;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
uint8_t valid;
} payload;
};
/**
* UBX NAV SOL Navigation Solution Information
*/
__PACKED__ struct ubx_nav_sol {
ubx_message_id_t id;
enum ubx_packet_state state;
struct {
uint32_t iTOW;
int32_t fTOW;
uint16_t week;
uint8_t gpsFix;
uint8_t flags;
int32_t ecefX;
int32_t ecefY;
int32_t ecefZ;
uint32_t pAcc;
int32_t ecefVX;
int32_t ecefVY;
int32_t ecefVZ;
uint32_t sAcc;
uint16_t pDOP;
uint8_t res1;
uint8_t numSV;
uint32_t res2;
} payload;
};
#endif /* UBX_MESSAGES_H */

Wyświetl plik

@ -33,45 +33,14 @@
#include "sercom/sercom.h"
#include "sercom/usart.h"
#include "util/dbuffer.h"
#include "ubx_messages.h"
#include "gps.h"
/**
* UBX Constants
*/
const uint16_t ubx_header = (0xB5 | (0x62 << 8));
/**
* UBX Class Types
*/
enum {
UBX_NAV = 0x01,
UBX_RXM = 0x02,
UBX_INF = 0x04,
UBX_ACK = 0x05,
UBX_CFG = 0x06,
UBX_MON = 0x0A,
UBX_AID = 0x0B,
UBX_TIM = 0x0D,
UBX_ESF = 0x10,
};
/**
* UBX NAV Message Types
*/
enum {
UBX_NAV_POSLLH = (UBX_NAV | (0x02 << 8)),
UBX_NAV_SOL = (UBX_NAV | (0x06 << 8)),
UBX_NAV_TIMEGPS = (UBX_NAV | (0x20 << 8)),
UBX_NAV_TIMEUTC = (UBX_NAV | (0x21 << 8)),
};
/**
* UBX CFG Message Types
*/
enum {
UBX_CFG_PRT = (UBX_CFG | (0x00 << 8)),
UBX_CFG_TP = (UBX_CFG | (0x07 << 8)),
UBX_CFG_ANT = (UBX_CFG | (0x13 << 8)),
UBX_CFG_NAV5 = (UBX_CFG | (0x24 << 8)),
UBX_CFG_TP5 = (UBX_CFG | (0x31 << 8)),
};
/**
* UBX ACK Message Types
*/
@ -80,8 +49,10 @@ enum {
UBX_ACK_ACK = (UBX_ACK | (0x01 << 8)),
};
/**
* Internal buffers and things
*/
#define UBX_BUFFER_LEN 0x80
#define SFD1 0xB5
#define SFD2 0x62
@ -94,104 +65,102 @@ int32_t ubx_index = SFD_WAITING;
uint16_t ubx_payload_length = 0;
uint8_t ubx_irq_buffer[UBX_BUFFER_LEN];
#define _send_buffer(tx_data, length) \
usart_write_buffer_wait(GPS_SERCOM, tx_data, length)
#define _get_buffer(rx_data, length) \
usart_read_buffer_wait(GPS_SERCOM, rx_data, length)
/**
* UBX Messages
*/
volatile struct ubx_cfg_ant ubx_cfg_ant = { .id = (UBX_CFG | (0x07 << 8)) };
volatile struct ubx_cfg_nav5 ubx_cfg_nav5 = { .id = (UBX_CFG | (0x24 << 8)) };
volatile struct ubx_cfg_tp ubx_cfg_tp = { .id = (UBX_CFG | (0x07 << 8)) };
volatile struct ubx_cfg_tp5 ubx_cfg_tp5 = { .id = (UBX_CFG | (0x31 << 8)) };
volatile struct ubx_cfg_prt ubx_cfg_prt = { .id = (UBX_CFG | (0x00 << 8)) };
volatile struct ubx_nav_posllh ubx_nav_posllh = { .id = (UBX_NAV | (0x02 << 8)) };
volatile struct ubx_nav_timeutc ubx_nav_timeutc = { .id = (UBX_NAV | (0x21 << 8)) };
volatile struct ubx_nav_sol ubx_nav_sol = { .id = (UBX_NAV | (0x06 << 8)) };
/**
* UBX Message Type List
*/
volatile ubx_message_t* const ubx_messages[] = {
(ubx_message_t*)&ubx_cfg_ant,
(ubx_message_t*)&ubx_cfg_nav5,
(ubx_message_t*)&ubx_cfg_tp,
(ubx_message_t*)&ubx_cfg_tp5,
(ubx_message_t*)&ubx_cfg_prt,
(ubx_message_t*)&ubx_nav_posllh,
(ubx_message_t*)&ubx_nav_timeutc,
(ubx_message_t*)&ubx_nav_sol};
/**
* Flags for pending ubx pakcets
* Platform specific handlers
*/
enum ubx_packet_state {
UBX_PACKET_WAITING,
UBX_PACKET_ACK,
UBX_PACKET_NACK,
};
enum ubx_packet_state _ubx_cfg_tp_state;
enum ubx_packet_state _ubx_cfg_tp5_state;
#define _send_buffer(tx_data, length) \
usart_write_buffer_wait(GPS_SERCOM, tx_data, length)
#define _error_handler(error_type) \
/* TODO */
/**
* Processes UBX ack/nack packets
*/
void ubx_process_ack(uint16_t message, enum ubx_packet_state state)
void ubx_process_ack(ubx_message_id_t message_id, enum ubx_packet_state state)
{
switch (message) {
case UBX_CFG_TP:
_ubx_cfg_tp_state = state;
break;
case UBX_CFG_TP5:
_ubx_cfg_tp5_state = state;
break;
default:
break;
for (uint32_t i = 0; i < sizeof(ubx_messages)/sizeof(ubx_message_t*); i++) {
if (message_id == ubx_messages[i]->id) { /* Match! */
/* Set the message state */
ubx_messages[i]->state = state;
}
}
}
/**
* Macro for the function below
*/
#define UBX_POPULATE_STRUCT(ubx_type) \
if (payload_length == sizeof(ubx_type)) { \
memcpy((void*)&ubx_type, frame + 4, payload_length); \
}
/**
* Process a single ubx frame. Runs in the IRQ so should be short and sweet.
*/
void ubx_process_frame(uint8_t* frame)
{
uint16_t* frame16 = (uint16_t*)frame;
uint16_t message_id = frame16[0];
uint16_t payload_length = frame16[1];
/* Checksum.. */
switch (frame[0]) { /* Switch by Class */
case UBX_NAV:
switch (frame16[0]) {
case UBX_NAV_SOL: /* Navigation Solution Information */
UBX_POPULATE_STRUCT(ubx_nav_sol);
break;
case UBX_NAV_TIMEUTC: /* UTC Time Solution */
UBX_POPULATE_STRUCT(ubx_nav_timeutc);
break;
case UBX_NAV_POSLLH: /* Geodetic Position Solution */
UBX_POPULATE_STRUCT(ubx_nav_posllh);
break;
/** Parse the message ID */
if (message_id == UBX_ACK_ACK) {
/* Ack */
ubx_process_ack(frame16[2], UBX_PACKET_ACK);
} else if (message_id == UBX_ACK_NACK) {
/* Nack */
ubx_process_ack(frame16[2], UBX_PACKET_NACK);
} else {
/** Otherwise it could be a message frame, search for a type */
for (uint32_t i = 0; i < sizeof(ubx_messages)/sizeof(ubx_message_t*); i++) {
if (message_id == ubx_messages[i]->id) { // Match!
/* Populate struct */
memcpy((void*)(ubx_messages[i]+1), frame + 4, payload_length);
return;
}
break;
case UBX_CFG:
switch (frame16[0]) {
case UBX_CFG_NAV5: /* Navigation Engine Settings */
UBX_POPULATE_STRUCT(ubx_cfg_nav5);
break;
case UBX_CFG_ANT: /* Antenna Control Settings */
UBX_POPULATE_STRUCT(ubx_cfg_ant);
break;
case UBX_CFG_TP: /* TimePulse Parameters */
UBX_POPULATE_STRUCT(ubx_cfg_tp);
break;
case UBX_CFG_TP5: /* TimePulse Parameters */
UBX_POPULATE_STRUCT(ubx_cfg_tp5);
break;
}
break;
case UBX_ACK:
if (payload_length == 2) { /* All ACK packets should have a payload len of 2 */
switch (frame16[0]) {
case UBX_ACK_ACK:
ubx_process_ack(frame16[2], UBX_PACKET_ACK);
break;
case UBX_ACK_NACK:
ubx_process_ack(frame16[2], UBX_PACKET_NACK);
break;
}
}
break;
default:
break;
}
}
/* Unknown frame */
}
/**
* Rx Callback. Processes a stream of
* Rx Callback. Processes a stream of usart bytes
*/
void gps_rx_callback(SercomUsart* const hw, uint16_t data)
{
@ -259,26 +228,32 @@ uint16_t _ubx_checksum(uint8_t* data, uint8_t len)
/**
* Sends a standard UBX message
*/
void _ubx_send_message(uint16_t message, uint8_t* payload, uint16_t length)
void _ubx_send_message(ubx_message_t* message, uint8_t* payload, uint16_t length)
{
uint8_t ubx[UBX_BUFFER_LEN];
uint8_t* ubx_buffer = ubx;
/* Copy little endian */
memcpy(ubx_buffer, &ubx_header, 2); ubx_buffer += 2; /* Header */
memcpy(ubx_buffer, &message, 2); ubx_buffer += 2; /* Message Type */
memcpy(ubx_buffer, &message->id, 2); ubx_buffer += 2; /* Message Type */
memcpy(ubx_buffer, &length, 2); ubx_buffer += 2; /* Length */
memcpy(ubx_buffer, payload, length); ubx_buffer += length; /* Payload*/
memcpy(ubx_buffer, payload, length); ubx_buffer += length; /* Payload */
uint16_t checksum = _ubx_checksum(ubx + 2, length + 4);
memcpy(ubx_buffer, &checksum, 2); ubx_buffer += 2; /* Checksum */
_send_buffer(ubx, length + 8);
}
/**
* Sends a UBX Poll Request
* Polls the GPS for packets
*/
void _ubx_poll(uint16_t message) {
void _ubx_poll(ubx_message_t* message) {
/* Clear the packet state */
message->state = UBX_PACKET_WAITING;
_ubx_send_message(message, NULL, 0);
/* Wait for acknoledge */
while (message->state == UBX_PACKET_WAITING);
}
/**
@ -286,36 +261,65 @@ void _ubx_poll(uint16_t message) {
*/
void gps_disable_nmea(void)
{
for (int i = 0; i < 1000*100; i++);
ubx_cfg_prt.payload.portID = 1;
ubx_cfg_prt.payload.res0 = 0;
ubx_cfg_prt.payload.txReady = 0;
ubx_cfg_prt.payload.mode = 0x08D0; /* 8 bit, No Parity */
ubx_cfg_prt.payload.baudRate = 9600;
ubx_cfg_prt.payload.inProtoMask = 0x7; /* UBX */
ubx_cfg_prt.payload.outProtoMask = 0x1; /* UBX */
ubx_cfg_prt.payload.flags = 0;
uint8_t setNMEAoff[] = {
0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x80, 0x25,
0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
};
_ubx_send_message(UBX_CFG_PRT, setNMEAoff, sizeof(setNMEAoff)/sizeof(uint8_t));
_ubx_send_message((ubx_message_t*)&ubx_cfg_prt,
(uint8_t*)&ubx_cfg_prt.payload,
sizeof(ubx_cfg_prt.payload));
for (int i = 0; i < 1000*100; i++);
}
/**
* Check the navigation status to determine the quality of the
* fix currently held by the receiver with a NAV-STATUS message.
* Sends messages to the GPS to update the messages we want
*/
void gps_check_lock()
void gps_update()
{
/* Send the poll request */
_ubx_poll(UBX_NAV_SOL);
_ubx_send_message((ubx_message_t*)&ubx_nav_posllh, NULL, 0);
_ubx_send_message((ubx_message_t*)&ubx_nav_sol, NULL, 0);
_ubx_send_message((ubx_message_t*)&ubx_nav_timeutc, NULL, 0);
}
/**
* Return the latest received messages
*/
struct ubx_nav_posllh gps_get_nav_posllh()
{
return ubx_nav_posllh;
}
struct ubx_nav_sol gps_get_nav_sol()
{
return ubx_nav_sol;
}
struct ubx_nav_timeutc gps_get_nav_timeutc()
{
return ubx_nav_timeutc;
}
/**
* Verify that the uBlox 6 GPS receiver is set to the <1g airborne
* navigaion mode.
*/
void gps_check_nav(void)
void gps_set_platform_model(void)
{
/* Send the poll request */
_ubx_poll(UBX_CFG_NAV5);
_ubx_poll((ubx_message_t*)&ubx_cfg_nav5);
/* If we need to update */
if (ubx_cfg_nav5.payload.dynModel != GPS_PLATFORM_MODEL) {
/* Update */
ubx_cfg_nav5.payload.dynModel = GPS_PLATFORM_MODEL;
/* Send */
_ubx_send_message((ubx_message_t*)&ubx_cfg_nav5,
(uint8_t*)&ubx_cfg_nav5.payload,
sizeof(ubx_cfg_nav5.payload));
}
}
/**
@ -323,60 +327,42 @@ void gps_check_nav(void)
*/
void gps_set_timepulse(void)
{
/* Clear the packet state */
_ubx_cfg_tp_state = UBX_PACKET_WAITING;
/* Send the Request */
_ubx_poll(UBX_CFG_TP);
_ubx_poll((ubx_message_t*)&ubx_cfg_tp);
/* Define the settings we want */
struct ubx_cfg_tp timepulse;
memset(&timepulse, 0, sizeof(ubx_cfg_tp));
timepulse.interval = 2; /* 2µS */
timepulse.length = 1; /* 1µS */
timepulse.status = 1; /* On, Positive */
timepulse.timeRef = 1; /* Align GPS time */
timepulse.flags = 0x1; /* Run outside lock */
timepulse.antennaCableDelay = 50; /* 50 nS */
ubx_cfg_tp.payload.interval = 2; /* 2µS */
ubx_cfg_tp.payload.length = 1; /* 1µS */
ubx_cfg_tp.payload.status = 1; /* On, Positive */
ubx_cfg_tp.payload.timeRef = 1; /* Align GPS time */
ubx_cfg_tp.payload.flags = 0x1; /* Run outside lock */
ubx_cfg_tp.payload.antennaCableDelay = 50; /* 50 nS */
/* Wait for acknoledge */
while (_ubx_cfg_tp_state == UBX_PACKET_WAITING);
/* Compare with current settings */
if (memcmp((void*)&ubx_cfg_tp, &timepulse, sizeof(ubx_cfg_tp)) != 0) {
/* Write the new settings */
_ubx_send_message(UBX_CFG_TP, (uint8_t*)&timepulse, sizeof(ubx_cfg_tp));
}
/* Write the new settings */
_ubx_send_message((ubx_message_t*)&ubx_cfg_tp,
(uint8_t*)&ubx_cfg_tp.payload,
sizeof(ubx_cfg_tp.payload));
}
/**
* Set the GPS timepulse settings using the CFG_TP5 message
*/
void gps_set_timepulse_five(uint32_t frequency)
{
/* Clear the packet state */
_ubx_cfg_tp5_state = UBX_PACKET_WAITING;
/* Send the Request */
_ubx_poll(UBX_CFG_TP5);
_ubx_poll((ubx_message_t*)&ubx_cfg_tp5);
/* Define the settings we want */
struct ubx_cfg_tp5 timepulse5;
memset(&timepulse5, 0, sizeof(ubx_cfg_tp5));
timepulse5.tpIdx = 0;
timepulse5.antCableDelay = 50; /* 50 nS */
ubx_cfg_tp5.payload.tpIdx = 0;
ubx_cfg_tp5.payload.antCableDelay = 50; /* 50 nS */
/* GPS time, duty cyle, frequency, lock to GPS, active */
timepulse5.flags = 0x80 | 0x8 | 0x3;
timepulse5.freqPeriod = frequency;
timepulse5.pulseLenRatio = 0x80000000; /* 50 % duty cycle*/
ubx_cfg_tp5.payload.flags = 0x80 | 0x8 | 0x3;
ubx_cfg_tp5.payload.freqPeriod = frequency;
ubx_cfg_tp5.payload.pulseLenRatio = 0x80000000; /* 50 % duty cycle*/
/* Wait for acknoledge */
while (_ubx_cfg_tp5_state == UBX_PACKET_WAITING);
/* Compare with current settings */
if (memcmp((void*)&ubx_cfg_tp5, &timepulse5, sizeof(ubx_cfg_tp5)) != 0) {
/* Write the new settings */
_ubx_send_message(UBX_CFG_TP5, (uint8_t*)&timepulse5, sizeof(ubx_cfg_tp5));
}
/* Write the new settings */
_ubx_send_message((ubx_message_t*)&ubx_cfg_tp5,
(uint8_t*)&ubx_cfg_tp5.payload,
sizeof(ubx_cfg_tp5.payload));
}
/**
@ -420,13 +406,8 @@ void gps_init(void)
/* Incoming ubx messages are handled in an irq */
usart_register_rx_callback(GPS_SERCOM, gps_rx_callback, 0);
/* Fill some configuration structures */
gps_check_lock();
_ubx_poll(UBX_CFG_ANT);
/* */
gps_check_nav();
/* Set the platform model */
gps_set_platform_model();
/* Set the timepulse */
gps_set_timepulse_five(GPS_TIMEPULSE_FREQ);
@ -473,4 +454,3 @@ void usart_loopback_test(void)
semihost_printf("Rx'ed: 0x%02x\n", data);
}

Wyświetl plik

@ -31,11 +31,14 @@
#include "system/system.h"
#include "sercom/usart.h"
#include "system/port.h"
#include "tc/tc_driver.h"
#include "gps.h"
#include "timepulse.h"
//#include "si406x.h"
#include "si4060.h"
#include "spi_bitbang.h"
#include "rtty.h"
#include "system/interrupt.h"
void si4060_hw_init(void)
{
@ -58,23 +61,65 @@ void si4060_hw_init(void)
/* Put the SEL pin in reset */
_si406x_cs_disable();
/* Configure the serial port */
spi_bitbang_init(SI406X_SERCOM_MOSI_PIN,
SI406X_SERCOM_MISO_PIN,
SI406X_SERCOM_SCK_PIN);
}
void si4060_gpio_init()
{
/* Configure the GPIO and IRQ pins */
port_pin_set_config(SI406X_GPIO0_PIN,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
port_pin_set_output_level(SI406X_GPIO0_PIN, 0);
/* Configure the serial port */
spi_bitbang_init(SI406X_SERCOM_MOSI_PIN,
SI406X_SERCOM_MISO_PIN,
SI406X_SERCOM_SCK_PIN);
/* Configure the GPIO and IRQ pins */
port_pin_set_config(SI406X_GPIO1_PIN,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
port_pin_set_output_level(SI406X_GPIO1_PIN, 0);
}
void set_timer(uint32_t time)
{
bool capture_channel_enables[] = {false, false};
uint32_t compare_channel_values[] = {time, 0x0000};
tc_init(TC2,
GCLK_GENERATOR_0,
TC_COUNTER_SIZE_32BIT,
TC_CLOCK_PRESCALER_DIV1,
TC_WAVE_GENERATION_NORMAL_FREQ,
TC_RELOAD_ACTION_GCLK,
TC_COUNT_DIRECTION_UP,
TC_WAVEFORM_INVERT_OUTPUT_NONE,
false, /* Oneshot = false */
false, /* Run in standby = false */
0x0000, /* Initial value */
time+1, /* Top value */
capture_channel_enables, /* Capture Channel Enables */
compare_channel_values); /* Compare Channels Values */
struct tc_events ev;
memset(&ev, 0, sizeof(ev));
ev.generate_event_on_compare_channel[0] = true;
ev.event_action = TC_EVENT_ACTION_RETRIGGER;
tc_enable_events(TC2, &ev);
irq_register_handler(TC2_IRQn, 3);
tc_enable(TC2);
tc_start_counter(TC2);
}
int main(void)
{
/* Clock up to 28MHz with 1 wait state */
system_flash_set_waitstates(1);
/* Clock up to 14MHz with 0 wait states */
system_flash_set_waitstates(SYSTEM_WAIT_STATE_1_8V_14MHZ);
/* Up the clock rate to 4MHz */
system_clock_source_osc8m_set_config(SYSTEM_OSC8M_DIV_2, /* Prescaler */
@ -87,39 +132,19 @@ int main(void)
/* Get the current CPU Clock */
SystemCoreClock = system_cpu_clock_get_hz();
/* Set LED0 as output */
//PORTA.DIRSET.reg = (1 << SI406X_HF_CLK_PIN);
/* Configure the SysTick for cpu/1000 output*//*for 50ms interrupts */
//SysTick_Config(500); //SystemCoreClock / 20);
/* Configure Sleep Mode */
system_set_sleepmode(SYSTEM_SLEEPMODE_IDLE_0);
//TODO: system_set_sleepmode(SYSTEM_SLEEPMODE_STANDBY);
semihost_printf("Hello World %fHz\n", RF_FREQ_HZ);
/* Initialise GPS */
gps_init();
/* Wait for GPS timepulse to stabilise */
for (int i = 0; i < 1000*100; i++);
/* For the moment output GCLK_MAIN / 2 on HF CLK */
switch_gclk_main_to_timepulse();
/* Wait for GCLK to stabilise */
for (int i = 0; i < 1000*100; i++);
//half_glck_main_on_hf_clk();
timepulse_init();
/* Wait for HF CLK to stabilise */
for (int i = 0; i < 1000*100; i++);
semihost_printf("GCLK_MAIN = %d\n", gclk_main_frequency());
/* Drop the CPU clock to 1.5Mhz */
//system_cpu_clock_set_divider(SYSTEM_MAIN_CLOCK_DIV_16);
/* Configure the SysTick for 50Hz triggering */
SysTick_Config(SystemCoreClock / 50);
/* Initialise Si4060 */
si4060_hw_init();
@ -133,30 +158,42 @@ int main(void)
while(1);
}
si4060_power_up();
si4060_setup(MOD_TYPE_CW);
si4060_start_tx(0);
/* si4060_power_up(); */
/* si4060_setup(MOD_TYPE_2FSK); */
uint32_t ll = 0;
/* si4060_gpio_init(); */
/* si4060_start_tx(0); */
/* Set the watchdog timer. On 32kHz ULP internal clock */
/* wdt_set_config(true, /\* Lock WDT *\/ */
/* true, /\* Enable WDT *\/ */
/* GCLK_GENERATOR_4, /\* Clock Source *\/ */
/* WDT_PERIOD_16384CLK, /\* Timeout Period *\/ */
/* WDT_PERIOD_NONE, /\* Window Period *\/ */
/* WDT_PERIOD_NONE); /\* Early Warning Period *\/ */
while (1) {
semihost_printf("State is %d %d\n", si4060_get_state(), ll++);
si4060_get_freq();
for (int i = 0; i < 1000*10; i++);
port_pin_set_output_level(SI406X_GPIO0_PIN, 0);
//si4060_start_tx(0);
for (int i = 0; i < 1000*10; i++);
port_pin_set_output_level(SI406X_GPIO0_PIN, 1);
/* Send the last packet */
while (rtty_active());
/* Send requests to the gps */
gps_update();
/* Wait between frames */
for (int i = 0; i < 100*1000; i++);
/* Set the next packet */
set_telemetry_string();
//system_sleep();
}
}
/**
* Called at 50Hz
*/
void SysTick_Handler(void)
{
/* Toggle LED0 */
//PORTA.OUTTGL.reg = (1 << SI406X_HF_CLK_PIN);
/* Output RTTY */
rtty_tick();
}

139
firmware/src/rtty.c 100644
Wyświetl plik

@ -0,0 +1,139 @@
/*
* Bit-bangs RTTY
* Copyright (C) 2014 Richard Meadows <richardeoin>
*
* 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.
*/
#include <string.h>
#include "samd20.h"
#include "hw_config.h"
#include "system/port.h"
/**
* Interface to the physical world.
*/
#define RTTY_ACTIVATE()
#define RTTY_DEACTIVATE()
#define RTTY_SET(b) port_pin_set_output_level(SI406X_GPIO1_PIN, b);
#define RTTY_NEXT()
/**
* Formatting 8N2
*/
#define ASCII_BITS 8
#define BITS_PER_CHAR 11
/**
* Output String
*/
#define RTTY_STRING_MAX 0x200
/**
* Where we currently are in the rtty output byte
*
* 0 = Start Bit
* 1-8 = Data Bit
* 10 = Stop Bit
* 11 = Stop Bit
*/
uint8_t rtty_phase;
/**
* Where we are in the current output string
*/
uint32_t rtty_index;
/**
* Details of the string that is currently being output
*/
char rtty_string[RTTY_STRING_MAX];
uint32_t rtty_string_length = 0;
/**
* Returns 1 if we're currently outputting.
*/
int rtty_active(void) {
return (rtty_string_length > 0);
}
/**
* Sets an output string.
*
* Returns 0 on success, 1 if a string is already active or 2 if the
* specified string was too long.
*/
int rtty_set_string(char* string, uint32_t length) {
if (length > RTTY_STRING_MAX) return 2; // To long
if (!rtty_active()) {
// Copy
memcpy(rtty_string, string, length);
rtty_string_length = length;
// Initialise
rtty_index = 0;
rtty_phase = 0;
return 0; // Success
} else {
return 1; // Already active
}
}
/**
* Called at the baud rate, outputs bits of rtty
*/
void rtty_tick(void) {
if (rtty_active()) {
RTTY_ACTIVATE();
if (rtty_phase == 0) { // Start
// Low
//RTTY_SET(0);
port_pin_set_output_level(SI406X_GPIO1_PIN, 0);
} else if (rtty_phase < ASCII_BITS + 1) {
// Data
if ((rtty_string[rtty_index] >> (rtty_phase - 1)) & 1) {
//RTTY_SET(1);
port_pin_set_output_level(SI406X_GPIO1_PIN, 1);
} else {
//RTTY_SET(0);
port_pin_set_output_level(SI406X_GPIO1_PIN, 0);
}
} else if (rtty_phase < BITS_PER_CHAR) { // Stop
// High
//RTTY_SET(1);
port_pin_set_output_level(SI406X_GPIO1_PIN, 1);
}
rtty_phase++;
if (rtty_phase >= BITS_PER_CHAR) { // Next character
rtty_phase = 0; rtty_index++; RTTY_NEXT();
if (rtty_index >= rtty_string_length) { // All done, deactivate
rtty_string_length = 0; // Deactivate
}
}
} else {
// RTTY_DEACTIVATE();
}
}

Wyświetl plik

@ -88,7 +88,7 @@ void si4060_power_up(void) {
spi_select();
spi_write(CMD_POWER_UP);
spi_write(FUNC);
spi_write(0x01);/* TCXO used */
spi_write(0x00);/* TCXO not used */
spi_write((uint8_t) (XO_FREQ >> 24));
spi_write((uint8_t) (XO_FREQ >> 16));
spi_write((uint8_t) (XO_FREQ >> 8));
@ -393,8 +393,8 @@ void si4060_setup(uint8_t mod_type) {
GLOBAL_CONFIG,
GLOBAL_RESERVED | POWER_MODE_HIGH_PERF | SEQUENCER_MODE_FAST);
/* set up GPIOs */
si4060_gpio_pin_cfg(GPIO_MODE_DONOTHING,
GPIO_MODE_DONOTHING,
si4060_gpio_pin_cfg(GPIO_MODE_INPUT,
PULL_CTL + GPIO_MODE_INPUT,
GPIO_MODE_DONOTHING,
PULL_CTL + GPIO_MODE_INPUT,
DRV_STRENGTH_HIGH);
@ -409,7 +409,7 @@ void si4060_setup(uint8_t mod_type) {
/* use 2FSK from async GPIO0 */
si4060_set_property_8(PROP_MODEM,
MODEM_MOD_TYPE,
(mod_type & 0x07) | MOD_SOURCE_DIRECT | MOD_GPIO_0 | MOD_DIRECT_MODE_ASYNC);
(mod_type & 0x07) | MOD_SOURCE_DIRECT | MOD_GPIO_1 | MOD_DIRECT_MODE_ASYNC);
/* setup frequency deviation */
si4060_set_property_24(PROP_MODEM,
MODEM_FREQ_DEV,

Wyświetl plik

@ -506,7 +506,7 @@ void tc_set_top_value(Tc* const hw,
* \param[in] events Struct containing flags of events to enable
*/
void tc_enable_events(Tc* const hw,
struct tc_events *const events)
struct tc_events *const events)
{
/* Sanity check arguments */
assert(hw);
@ -679,7 +679,7 @@ enum tc_status_t tc_init(Tc* const hw,
/* Write Start Value */
tc_set_count_value(hw, value);
/* Write Top Value - Only cool for 8 bit!*/
/* Write Top Value */
tc_set_top_value(hw, top_value);
/* Write Compare Values */

Wyświetl plik

@ -0,0 +1,73 @@
/*
* Telemetry strings and formatting
* Copyright (C) 2014 Richard Meadows <richardeoin>
*
* 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.
*/
#include "samd20.h"
#include "semihosting.h"
#include "gps.h"
#include "ubx_messages.h"
#define SEMIHOST_LOG
char telemetry_string[0x200];
/**
*
*/
void set_telemetry_string(void)
{
/* Time */
struct ubx_nav_timeutc time = gps_get_nav_timeutc();
uint8_t hours = time.payload.hour;
uint8_t minutes = time.payload.min;
uint8_t seconds = time.payload.sec;
/* GPS Position */
struct ubx_nav_posllh pos = gps_get_nav_posllh();
double lat_fmt = (double)pos.payload.lat / 10000000.0;
double lon_fmt = (double)pos.payload.lon / 10000000.0;
uint32_t altitude = pos.payload.height / 1000;
/* GPS Status */
struct ubx_nav_sol sol = gps_get_nav_sol();
uint8_t fix_type = sol.payload.gpsFix;
uint8_t satillite_count = sol.payload.numSV;
/* Analogue */
#ifdef SEMIHOST_LOG
semihost_printf("%02.7f,%03.7f,%ld", lat_fmt, lon_fmt, altitude);
semihost_printf("Fix: %d Sats: %d\n", fix_type, satillite_count);
semihost_printf("%02u:%02u:%02u\n", hours, minutes, seconds);
#else
/* sprintf */
sprintf(telemetry_string, "$$emf,%02.7f,%03.7f,%ld,%u",
lat_fmt, lon_fmt, altitude, ubx_nav_sol.numSV);
rtty_set_string(telemetry_string, strlen(telemetry_string));
#endif
}