CW output on a version fitted with a 16MHz crystal

rocketry
Richard Eoin Meadows 2014-08-27 19:57:03 +01:00
rodzic 4c908d1448
commit 847d674373
18 zmienionych plików z 1892 dodań i 77 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 60 KiB

Wyświetl plik

@ -56,6 +56,36 @@ volatile struct ubx_cfg_nav5 {
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
*/
@ -106,6 +136,19 @@ volatile struct ubx_nav_sol {
uint32_t res2;
} __PACKED__ ubx_nav_sol;
/**
* 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);

Wyświetl plik

@ -76,8 +76,19 @@
#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 24000000
// 32768
#define GPS_PLATFORM_MODEL UBX_PLATFORM_MODEL_AIRBORNE_1G
/* Loopback Testing */
/**
* DFLL48
*/
#define DFLL48M_GCLK GCLK_GENERATOR_0
#define DFLL48M_CLK 48000000
/**
* USART Loopback Testing
*/
#define USART_MUX_LOOPBACK USART_RX_0_TX_0_XCK_1
/**
@ -102,21 +113,25 @@
/**
* Radio
*/
#define RADIO_SERCOM (SercomSpi*)SERCOM3
#define RADIO_SERCOM_MOSI_PIN PIN_PA19
#define RADIO_SERCOM_MOSI_PINMUX PINMUX_PA19D_SERCOM3_PAD3
#define RADIO_SERCOM_MISO_PIN PIN_PA22
#define RADIO_SERCOM_MISO_PINMUX PINMUX_PA22C_SERCOM3_PAD0
#define RADIO_SERCOM_SCK_PIN PIN_PA23
#define RADIO_SERCOM_SCK_PINMUX PINMUX_PA23C_SERCOM3_PAD1
#define RADIO_SEL_PIN PIN_PA18
#define RADIO_IRQ_PIN PIN_PA24
#define RADIO_IRQ_PINMUX PINMUX_PA24A_EIC_EXTINT12
#define RADIO_HF_CLK_PIN PIN_PA17
#define RADIO_HF_CLK_PINMUX PINMUX_PA17H_GCLK_IO3
#define RADIO_SDN_PIN PIN_PA16
#define RADIO_GPIO0_PIN PIN_PA27
#define RADIO_GPIO1_PIN PIN_PA25 /* Shared with LED */
#define SI406X_SERCOM (SercomSpi*)SERCOM3
#define SI406X_SERCOM_MOSI_PIN PIN_PA19
#define SI406X_SERCOM_MOSI_PINMUX PINMUX_PA19D_SERCOM3_PAD3
#define SI406X_SERCOM_MISO_PIN PIN_PA22
#define SI406X_SERCOM_MISO_PINMUX PINMUX_PA22C_SERCOM3_PAD0
#define SI406X_SERCOM_SCK_PIN PIN_PA23
#define SI406X_SERCOM_SCK_PINMUX PINMUX_PA23C_SERCOM3_PAD1
#define SI406X_SERCOM_MUX SPI_SIGNAL_MUX_SETTING_D
#define SI406X_SEL_PIN PIN_PA18
#define SI406X_IRQ_PIN PIN_PA24
#define SI406X_IRQ_PINMUX PINMUX_PA24A_EIC_EXTINT12
#define SI406X_HF_GCLK GCLK_GENERATOR_3
#define SI406X_HF_CLK_PIN PIN_PA17
#define SI406X_HF_CLK_PINMUX PINMUX_PA17H_GCLK_IO3
/* Currently half GPS TIMEPULSE */
#define SI406X_HF_FREQUENCY (GPS_TIMEPULSE_FREQ / 2)
#define SI406X_SDN_PIN PIN_PA16
#define SI406X_GPIO0_PIN PIN_PA27
#define SI406X_GPIO1_PIN PIN_PA25 /* Shared with LED */
/**
* SWD

Wyświetl plik

@ -0,0 +1,221 @@
/*
* si4060 software library
*
* Stefan Biereigel
*
*/
#ifndef SI4060_H_
#define SI4060_H_
#include <inttypes.h>
#include "samd20.h"
#include "system/port.h"
#include "hw_config.h"
/**
* Chip Select. Active Low (High = Inactive, Low = Active)
*/
#define _si406x_cs_enable() \
port_pin_set_output_level(SI406X_SEL_PIN, 0)
#define _si406x_cs_disable() \
port_pin_set_output_level(SI406X_SEL_PIN, 1)
#define spi_select _si406x_cs_enable
#define spi_deselect _si406x_cs_disable
#define spi_write spi_bitbang_transfer
#define spi_read() spi_bitbang_transfer(0xff)
#define __delay_cycles(n) do { \
for (int cyc = 0; cyc < n; cyc++) { __NOP(); } \
} while(0)
void si4060_shutdown(void);
#define XO_FREQ 16000000UL
#define RF_FREQ_HZ 434600000.0f
#define RF_DEV_HZ 100.0f
#define F_INT (2 * XO_FREQ / 8)
#define FDIV_INTE ( (RF_FREQ_HZ / F_INT) - 1)
#define FDIV_FRAC ( (RF_FREQ_HZ - F_INT*(int)FDIV_INTE) * ((uint32_t)1 << 19) ) / F_INT
#define FDEV ( ( ( (uint32_t)1 << 19) * 8 * RF_DEV_HZ)/ (2*XO_FREQ))
/* function prototypes */
void si4060_shutdown (void);
void si4060_wakeup (void);
void si4060_reset (void);
void si4060_power_up (void);
void si4060_nop (void);
uint8_t si4060_get_state(void);
void si4060_get_freq(void);
void si4060_start_tx (uint8_t channel);
void si4060_stop_tx (void);
void si4060_setup (uint8_t mod_type);
uint8_t si4060_get_property_8 (uint8_t group, uint8_t prop);
uint16_t si4060_part_info (void);
/* ===== command definitions ===== */
#define CMD_NOP 0x00
#define CMD_PART_INFO 0x01
#define CMD_POWER_UP 0x02
#define CMD_SET_PROPERTY 0x11
#define CMD_GET_PROPERTY 0x12
#define CMD_GPIO_PIN_CFG 0x13
#define CMD_START_TX 0x31
#define CMD_REQUEST_STATE 0x33
#define CMD_CHANGE_STATE 0x34
#define CMD_READ_CMD_BUF 0x44
/* ===== device states ===== */
#define STATE_NOCHANGE 0x00
#define STATE_SLEEP 0x01
#define STATE_SPI_ACTIVE 0x02
#define STATE_READY 0x03
#define STATE_TX_TUNE 0x05
#define STATE_TXA 0x07
/* ===== property group definitions ===== */
#define PROP_GLOBAL 0x00
#define PROP_INT_CTL 0x01
#define PROP_FRR_CTL 0x02
#define PROP_PREAMBLE 0x10
#define PROP_SYNC 0x11
#define PROP_PKT 0x12
#define PROP_MODEM 0x20
#define PROP_PA 0x22
#define PROP_SYNTH 0x23
#define PROP_FREQ_CONTROL 0x40
/* ===== property definitions ===== */
/* global properties */
#define GLOBAL_XO_TUNE 0x00
#define GLOBAL_CONFIG 0x03
/* preamble properties */
#define PREAMBLE_TX_LENGTH 0x00
/* sync properties */
#define SYNC_CONFIG 0x11
/* modem properties */
#define MODEM_MOD_TYPE 0x00
#define MODEM_FREQ_DEV 0x0a
#define MODEM_FREQ_OFFSET 0x0d
#define MODEM_CLKGEN_BAND 0x51
/* PA properties */
#define PA_MODE 0x00
#define PA_PWR_LVL 0x01
#define PA_BIAS_CLKDUTY 0x02
/* synthesizer properties */
#define SYNTH_PFDCP_CPFF 0x00
#define SYNTH_PFDCP_CPINT 0x01
#define SYNTH_VCO_KV 0x02
#define SYNTH_LPFILT3 0x03
#define SYNTH_LPFILT2 0x04
#define SYNTH_LPFILT1 0x05
#define SYNTH_LPFILT0 0x06
#define SYNTH_VCO_KVCAL 0x07
/* frequency control properties */
/* INTE shall be decreased by 1, because FRAC shall be between 1 and 2 */
#define FREQ_CONTROL_INTE 0x00
/* FRAC shall be added to 2**19, to ensure MSB is set! */
#define FREQ_CONTROL_FRAC 0x01
#define FREQ_CONTROL_CHANNEL_STEP_SIZE 0x04
#define FREQ_CONTROL_W_SIZE 0x06
/* ===== command arguments ===== */
/* POWER_UP arguments */
/* byte 1 */
#define PATCH ( 0x01 << 7) /* set patch mode */
#define FUNC 0x01 /* power on device */
/* byte 2 */
#define TCXO 0x01/* select if TCXO (1) or crystal (0) is used */
/* GPIO_PIN_CFG arguments */
/* bytes 1 .. 6 */
#define PULL_CTL 0x40 /* enable or disable pull-up resistor */
/* bytes 1 .. 4 */
#define GPIO_MODE_DONOTHING 0x00/* pin behaviour is not changed */
#define GPIO_MODE_TRISTATE 0x01/* input and output drivers are disabled */
#define GPIO_MODE_DRIVE0 0x02/* CMOS output "low" */
#define GPIO_MODE_DRIVE1 0x03/* CMOS output "high" */
#define GPIO_MODE_INPUT 0x04/* GPIO is input, for TXDATA etc, function is not configured here */
#define GPIO_MODE_32K_CLK 0x05/* outputs the 32kHz CLK when selected in CLK32_CLK_SEL */
#define GPIO_MODE_BOOT_CLK 0x06/* outputs boot clock when SPI_ACTIVE */
#define GPIO_MODE_DIV_CLK 0x07/* outputs divided xtal clk */
#define GPIO_MODE_CTS 0x08/* output, '1' when device is ready to accept new command */
#define GPIO_MODE_INV_CNT 0x09/* output, inverted CTS */
#define GPIO_MODE_CMD_OVERLAP 0x0a/* output, '1' if a command was issued while not ready */
#define GPIO_MODE_SDO 0x0b/* output, serial data out for SPI */
#define GPIO_MODE_POR 0x0c/* output, '0' while in POR state */
#define GPIO_MODE_CAL_WUT 0x0d/* output, '1' on expiration of wake up timer */
#define GPIO_MODE_WUT 0x0e/* wake up timer output */
#define GPIO_MODE_EN_PA 0x0f/* output, '1' when PA is enabled */
#define GPIO_MODE_TX_DATA_CLK 0x10/* data clock output, for TX direct sync mode */
#define GPIO_MODE_TX_DATA 0x11/* data output from TX FIFO, for debugging purposes */
#define GPIO_MODE_IN_SLEEP 0x12/* output, '0' when in sleep state */
#define GPIO_MODE_TX_STATE 0x13/* output, '1' when in TX state */
#define GPIO_MODE_TX_FIFO_EMPTY 0x14/* output, '1' when FIFO is empty */
#define GPIO_MODE_LOW_BATT 0x15/* output, '1' if low battery is detected */
/* byte 5 omitted - no IRQ support */
#define NIRQ_MODE_DONOTHING 0x00
/* byte 6 omitted - no SDO reconfiguration support */
#define SDO_MODE_DONOTHING 0x00
/* byte 7 */
#define DRV_STRENGTH_HIGH ( 0x00 << 5)
#define DRV_STRENGTH_MED_HIGH ( 0x01 << 5)
#define DRV_STRENGTH_MED_LOW ( 0x02 << 5)
#define DRV_STRENGTH_LOW ( 0x03 << 5)
/* START_TX arguments */
/* byte 2 */
#define START_TX_TXC_STATE_NOCHANGE ( 0x00 << 4)
#define START_TX_TXC_STATE_SLEEP ( 0x01 << 4)
#define START_TX_TXC_STATE_SPI_ACTIVE ( 0x02 << 4)
#define START_TX_TXC_STATE_READY ( 0x03 << 4)
#define START_TX_RETRANSMIT_0 ( 0x00 << 2)/* send data that has been written to the TX FIFO */
#define START_TX_START_IMM ( 0x00 << 0)/* start transmission immediately */
/* ===== property values ===== */
/* GLOBAL_CONFIG values */
#define GLOBAL_RESERVED ( 0x01 << 6) /* shall be set to 1 */
#define POWER_MODE_LOW_POWER 0x00/* default */
#define POWER_MODE_HIGH_PERF 0x01
#define SEQUENCER_MODE_FAST ( 0x00 << 5)/* default */
#define SEQUENCER_MODE_GUARANT ( 0x01 << 5)
/* SYNC_CONFIG values */
#define SYNC_XMIT ( 0x00 << 7)/* default */
#define SYNC_NO_XMIT ( 0x01 << 7)
/* MODEM_MOD_TYPE values */
#define MOD_TYPE_CW 0x00
#define MOD_TYPE_OOK 0x01
#define MOD_TYPE_2FSK 0x02/* default */
#define MOD_TYPE_2GFSK 0x03
#define MOD_TYPE_4FSK 0x04
#define MOD_TYPE_4GFSK 0x05
#define MOD_SOURCE_PACKET ( 0x00 << 3) /* default */
#define MOD_SOURCE_DIRECT ( 0x01 << 3)
#define MOD_SOURCE_PSEUDO ( 0x02 << 3)
#define MOD_GPIO_0 ( 0x00 << 5) /* default */
#define MOD_GPIO_1 ( 0x01 << 5)
#define MOD_GPIO_2 ( 0x02 << 5)
#define MOD_GPIO_3 ( 0x03 << 5)
#define MOD_DIRECT_MODE_SYNC ( 0x00 << 7) /* default */
#define MOD_DIRECT_MODE_ASYNC ( 0x01 << 7)
/* MODEM_CLKGEN_BAND values */
#define SY_SEL_0 ( 0x00 << 3) /* low power */
#define SY_SEL_1 ( 0x01 << 3) /* default */
#define FVCO_DIV_4 0x00 /* default */
#define FVCO_DIV_6 0x01
#define FVCO_DIV_8 0x02 /* for 70cm ISM band */
#define FVCO_DIV_12 0x03
#define FVCO_DIV_16 0x04
#define FVCO_DIV_24 0x05
#define FVCO_DIV_24_2 0x06
#define FVCO_DIV_24_3 0x07
/* PA_MODE values*/
/* PA_BIAS_CLKDUTY values */
#define PA_BIAS_CLKDUTY_SIN_25 (0x03 << 6) /* for si4060 */
#define PA_BIAS_CLKDUTY_DIFF_50 (0x00 << 6) /* for si4063 */
#endif

Wyświetl plik

@ -0,0 +1,31 @@
/*
* <one line to give the program's name and a brief idea of what it does.>
* 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 SI406X_H
#define SI406X_H
void si406x_init(void);
void spi_loopback_test(void);
#endif /* SI406X_H */

Wyświetl plik

@ -0,0 +1,161 @@
/*
* Definitions and macros for the Si406x
* 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 SI406X_DEFS_H
#define SI406X_DEFS_H
/**
* Si406x Boot Commands
*/
enum {
SI_CMD_POWER_UP = 0x02,
};
/**
* Si406x Common Commands
*/
enum {
SI_CMD_NOP = 0x00,
SI_CMD_PART_INFO = 0x01,
SI_CMD_FUNC_INFO = 0x10,
SI_CMD_SET_PROPERTY = 0x11,
SI_CMD_GET_PROPERTY = 0x12,
SI_CMD_GPIO_PIN_CFG = 0x13,
SI_CMD_FIFO_INFO = 0x15,
SI_CMD_GET_INT_STATUS = 0x20,
SI_CMD_REQUEST_DEVICE_STATE = 0x33,
SI_CMD_CHANGE_STATE = 0x34,
SI_CMD_READ_CMD_BUFF = 0x44,
SI_CMD_FRR_A_READ = 0x50,
SI_CMD_FRR_B_READ = 0x51,
SI_CMD_FRR_C_READ = 0x53,
SI_CMD_FRR_D_READ = 0x57,
};
/**
* Si406x Tx Commands
*/
enum {
SI_CMD_START_TX = 0x31,
SI_CMD_WRITE_TX_FIFO = 0x66,
};
/**
* Si406x Rx Commands
*/
enum {
SI_CMD_PACKET_INFO = 0x16,
SI_CMD_GET_MODEM_STATUS = 0x22,
SI_CMD_START_RX = 0x32,
SI_CMD_RX_HOP = 0x36,
SI_CMD_READ_RX_FIFO = 0x77,
};
/**
* Si406x Advanced Commands
*/
enum {
SI_CMD_GET_ADC_READING = 0x14,
SI_CMD_PROTOCOL_CFG = 0x18,
SI_CMD_GET_PH_STATUS = 0x21,
SI_CMD_GET_CHIP_STATUS = 0x23,
};
/**
* Si406x State Change Commands
*/
enum {
SI_STATE_CHANGE_NOCHANGE = (0 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_SLEEP = (1 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_SPI_ACTIVE = (2 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_READY = (3 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_TX_TUNE = (5 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_RX_TUNE = (6 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_TX = (7 << 8) | SI_CMD_CHANGE_STATE,
SI_STATE_CHANGE_RX = (8 << 8) | SI_CMD_CHANGE_STATE,
};
/**
* Generic SPI Send / Receive for the Si406x
*/
void _si406x_transfer(int tx_count, int rx_count, const uint8_t *data);
/**
* Chip Select. Active Low (High = Inactive, Low = Active)
*/
#define _si406x_cs_enable() \
port_pin_set_output_level(SI406X_SEL_PIN, 0)
#define _si406x_cs_disable() \
port_pin_set_output_level(SI406X_SEL_PIN, 1)
/**
* Shutdown. Active High (High = Shutdown, Low = Run)
*/
#define _si406x_sdn_enable() \
port_pin_set_output_level(SI406X_SDN_PIN, 1)
#define _si406x_sdn_disable() \
port_pin_set_output_level(SI406X_SDN_PIN, 0)
/**
* HF Clock
*/
#define _si406x_hf_clock_enable(void) \
/* TODO: Clock is always enabled */
#define _si406x_hf_clock_disable(void) \
/* TODO: Clock is always enabled */
/**
* Convenience transfer functions
*/
static void _si406x_transfer_uint16(uint16_t value)
{
_si406x_transfer(2, 0, (uint8_t*)&value);
}
/**
* State changes
*/
#define si406x_state_ready() \
_si406x_transfer_uint16(SI_STATE_CHANGE_READY)
/**
* Change to TX tune state
*/
#define si406x_state_tx_tune() \
_si406x_transfer_uint16(SI_STATE_CHANGE_TX_TUNE)
/**
* Change to RX tune state
*/
#define si406x_state_rx_tune() \
_si406x_transfer_uint16(SI_STATE_CHANGE_RX_TUNE)
/**
* Change to TX state
*/
#define si406x_state_tx() \
_si406x_transfer_uint16(SI_STATE_CHANGE_TX)
/**
* Change to RX state
*/
#define si406x_state_rx() \
_si406x_transfer_uint16(SI_STATE_CHANGE_RX)
#endif /* SI406X_DEFS_H */

Wyświetl plik

@ -0,0 +1,33 @@
/*
* SPI bit-banging! Yay
* 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 SPI_BITBANG_H
#define SPI_BITBANG_H
void spi_bitbang_init(const uint8_t mosi,
const uint8_t miso,
const uint8_t sck);
uint8_t spi_bitbang_transfer(const uint8_t byte);
#endif /* SPI_BITBANG_H */

Wyświetl plik

@ -82,7 +82,7 @@
# define CONF_CLOCK_OSC32K_RUN_IN_STANDBY false
/* SYSTEM_CLOCK_SOURCE_DFLL configuration - Digital Frequency Locked Loop */
# define CONF_CLOCK_DFLL_ENABLE true
# define CONF_CLOCK_DFLL_ENABLE false
# define CONF_CLOCK_DFLL_LOOP_MODE SYSTEM_CLOCK_DFLL_LOOP_MODE_OPEN
# define CONF_CLOCK_DFLL_ON_DEMAND false

Wyświetl plik

@ -272,10 +272,6 @@
* device is two when running in 32-bit mode and four in 8-, and 16-bit modes.
*/
//#include <clock.h>
//#include <gclk.h>
//#include <pinmux.h>
#include "samd20.h"
#include "tc.h"
#include <stdbool.h>
@ -522,15 +518,15 @@ struct tc_pwm_channel {
};
static inline void tc_enable(Tc* const hw);
static inline void tc_disable(Tc* const hw);
void tc_enable(Tc* const hw);
void tc_disable(Tc* const hw);
static inline void tc_start_counter(Tc* const hw);
static inline void tc_stop_counter(Tc* const hw);
void tc_start_counter(Tc* const hw);
void tc_stop_counter(Tc* const hw);
static inline uint32_t tc_get_status(Tc* const hw);
static inline void tc_clear_status(Tc* const hw,
const uint32_t status_flags);
uint32_t tc_get_status(Tc* const hw);
void tc_clear_status(Tc* const hw,
const uint32_t status_flags);
void tc_set_count_value(Tc* const hw, const uint32_t count);
uint32_t tc_get_count_value(Tc* const hw);
@ -546,9 +542,9 @@ void tc_reset(Tc* const hw);
void tc_set_top_value (Tc* const hw,
const uint32_t top_value);
static inline void tc_enable_events(Tc* const hw,
void tc_enable_events(Tc* const hw,
struct tc_events *const events);
static inline void tc_disable_events(Tc* const hw,
void tc_disable_events(Tc* const hw,
struct tc_events *const events);
enum tc_status_t tc_init(Tc* const hw,

Wyświetl plik

@ -0,0 +1,31 @@
/*
* Functions for turning the GPS timepulse into a HF Clock
* 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 TIMEPULSE_H
#define TIMEPULSE_H
void timepulse_init(void);
void switch_gclk_main_to_timepulse(void);
#endif /* TIMEPULSE_H */

Wyświetl plik

@ -67,8 +67,10 @@ enum {
*/
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
@ -77,19 +79,6 @@ enum {
UBX_ACK_NACK = (UBX_ACK | (0x00 << 8)),
UBX_ACK_ACK = (UBX_ACK | (0x01 << 8)),
};
/**
* 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,
};
#define UBX_BUFFER_LEN 0x80
@ -110,7 +99,33 @@ uint8_t ubx_irq_buffer[UBX_BUFFER_LEN];
#define _get_buffer(rx_data, length) \
usart_read_buffer_wait(GPS_SERCOM, rx_data, length)
/**
* Flags for pending ubx pakcets
*/
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;
/**
* Processes UBX ack/nack packets
*/
void ubx_process_ack(uint16_t message, 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;
}
}
/**
* Macro for the function below
*/
@ -118,7 +133,6 @@ uint8_t ubx_irq_buffer[UBX_BUFFER_LEN];
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.
*/
@ -150,14 +164,25 @@ void ubx_process_frame(uint8_t* frame)
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:
switch (frame16[0]) {
case UBX_ACK_ACK:
break;
case UBX_ACK_NACK:
break;
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:
@ -293,6 +318,67 @@ void gps_check_nav(void)
_ubx_poll(UBX_CFG_NAV5);
}
/**
* Set the GPS timepulse settings using the CFG_TP message
*/
void gps_set_timepulse(void)
{
/* Clear the packet state */
_ubx_cfg_tp_state = UBX_PACKET_WAITING;
/* Send the Request */
_ubx_poll(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 */
/* 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));
}
}
/**
* 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);
/* 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 */
/* GPS time, duty cyle, frequency, lock to GPS, active */
timepulse5.flags = 0x80 | 0x8 | 0x3;
timepulse5.freqPeriod = frequency;
timepulse5.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));
}
}
/**
* Init
*/
@ -334,9 +420,16 @@ void gps_init(void)
/* Incoming ubx messages are handled in an irq */
usart_register_rx_callback(GPS_SERCOM, gps_rx_callback, 0);
gps_check_nav();
/* Fill some configuration structures */
gps_check_lock();
_ubx_poll(UBX_CFG_ANT);
/* */
gps_check_nav();
/* Set the timepulse */
gps_set_timepulse_five(GPS_TIMEPULSE_FREQ);
}

Wyświetl plik

@ -30,49 +30,129 @@
#include "hw_config.h"
#include "system/system.h"
#include "sercom/usart.h"
#include "system/port.h"
#include "gps.h"
#include "timepulse.h"
//#include "si406x.h"
#include "si4060.h"
#include "spi_bitbang.h"
void si4060_hw_init(void)
{
/* Configure the SDN pin */
port_pin_set_config(SI406X_SDN_PIN,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Put the SI406x in shutdown */
//_si406x_sdn_enable();
si4060_shutdown();
/* Configure the SDN pin */
port_pin_set_config(SI406X_SEL_PIN,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Put the SEL pin in reset */
_si406x_cs_disable();
/* 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);
}
int main(void)
{
SystemInit();
/* Clock up to 28MHz with 1 wait state */
system_flash_set_waitstates(1);
system_clock_source_osc8m_set_config(SYSTEM_OSC8M_DIV_1, /* Prescaler */
/* Up the clock rate to 4MHz */
system_clock_source_osc8m_set_config(SYSTEM_OSC8M_DIV_2, /* Prescaler */
false, /* Run in Standby */
false); /* Run on Demand */
/* Update the value of SystemCoreClock */
SystemCoreClockUpdate();
/* Restart the GCLK Module */
system_gclk_init();
/* Get the current CPU Clock */
SystemCoreClock = system_cpu_clock_get_hz();
/* Set LED0 as output */
PORTA.DIRSET.reg = (1 << LED0_PIN);
//PORTA.DIRSET.reg = (1 << SI406X_HF_CLK_PIN);
/* Configure the SysTick for 50ms interrupts */
SysTick_Config(SystemCoreClock / 20);
/* 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);
volatile double d, dd;
semihost_printf("Hello World %fHz\n", RF_FREQ_HZ);
d = 2;
dd = sqrt(d);
semihost_printf("Hello World %f\n", dd);
/* 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();
//half_glck_main_on_hf_clk();
/* 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);
/* Initialise Si4060 */
si4060_hw_init();
/* reset the radio chip from shutdown */
si4060_reset();
/* check radio communication */
int i = si4060_part_info();
if (i != 0x4063) {
while(1);
}
si4060_power_up();
si4060_setup(MOD_TYPE_CW);
si4060_start_tx(0);
uint32_t ll = 0;
while (1) {
//system_sleep();
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);
for (int i = 0; i < 1000*1000; i++);
gps_check_lock();
//system_sleep();
}
}
void SysTick_Handler(void)
{
/* Toggle LED0 */
PORTA.OUTTGL.reg = (1 << LED0_PIN);
//PORTA.OUTTGL.reg = (1 << SI406X_HF_CLK_PIN);
}

Wyświetl plik

@ -0,0 +1,443 @@
/*
* si4060 software library
*
* API description: see EZRadioPRO-API-v1.1.2.zip
*
* Stefan Biereigel
*
*/
#include <inttypes.h>
#include "spi_bitbang.h"
#include "si4060.h"
#include "samd20.h"
#include "semihosting.h"
#include "hw_config.h"
#include "system/port.h"
/*
* si4060_shutdown
*
* makes the Si4060 go to shutdown state.
* all register content is lost.
*/
void si4060_shutdown(void) {
//P1OUT |= SI_SHDN;
port_pin_set_output_level(SI406X_SDN_PIN, 1);
/* wait 10us */
__delay_cycles(2000);
}
/*
* si4060_wakeup
*
* wakes up the Si4060 from shutdown state.
* si4060_power_up and si4060_setup have to be called afterwards
*/
void si4060_wakeup(void) {
//P1OUT &= ~SI_SHDN;
port_pin_set_output_level(SI406X_SDN_PIN, 0);
/* wait 20ms */
__delay_cycles(20000);
}
/*
* si4060_reset
*
* cleanly does the POR as specified in datasheet
*/
void si4060_reset(void) {
si4060_shutdown();
si4060_wakeup();
}
/*
* si4060_read_cmd_buf
*
* reads the Si4060 command buffer from via SPI
*
* deselect: whether to deselect the slave after reading the response or not.
* any command reading subsequent bytes after the CTS should use this
* function to get CTS and continue doing spi_read afterwards
* and finally deselecting the slave.
*
* returns:the value of the first command buffer byte (i.e. CTS or not)
*/
uint8_t si4060_read_cmd_buf(uint8_t deselect) {
uint8_t ret;
spi_select();
spi_write(CMD_READ_CMD_BUF);
ret = spi_read();
if (deselect) {
spi_deselect();
}
return ret;
}
/*
* si4060_power_up
*
* powers up the Si4060 by issuing the POWER_UP command
*
* warning: the si4060 can lock after issuing this command if input clock
* is not available for the internal RC oscillator calibration.
*/
void si4060_power_up(void) {
/* wait for CTS */
while (si4060_read_cmd_buf(1) != 0xff);
spi_select();
spi_write(CMD_POWER_UP);
spi_write(FUNC);
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));
spi_write((uint8_t) XO_FREQ);
spi_deselect();
/* wait for CTS */
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_change_state
*
* changes the internal state machine state of the Si4060
*/
void si4060_change_state(uint8_t state) {
spi_select();
spi_write(CMD_CHANGE_STATE);
spi_write(state);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_nop
*
* implements the NOP command on the Si4060
*/
void si4060_nop(void) {
spi_select();
spi_write(CMD_NOP);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_set_property_8
*
* sets an 8 bit (1 byte) property in the Si4060
*
* group:the group number of the property
* prop:the number (index) of the property
* val:the value to set
*/
void si4060_set_property_8(uint8_t group, uint8_t prop, uint8_t val) {
spi_select();
spi_write(CMD_SET_PROPERTY);
spi_write(group);
spi_write(1);
spi_write(prop);
spi_write(val);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_get_property_8
*
* gets an 8 bit (1 byte) property in the Si4060
*
* group:the group number of the property
* prop:the number (index) of the property
*
* returns:the value of the property
*/
uint8_t si4060_get_property_8(uint8_t group, uint8_t prop) {
uint8_t temp = 0;
spi_select();
spi_write(CMD_GET_PROPERTY);
spi_write(group);
spi_write(1);
spi_write(prop);
spi_deselect();
while (temp != 0xff) {
temp = si4060_read_cmd_buf(0);
if (temp != 0xff) {
spi_deselect();
}
}
temp = spi_read(); /* read property */
spi_deselect();
return temp;
}
/*
* si4060_set_property_16
*
* sets an 16 bit (2 byte) property in the Si4060
*
* group:the group number of the property
* prop:the number (index) of the property
* val:the value to set
*/
void si4060_set_property_16(uint8_t group, uint8_t prop, uint16_t val) {
spi_select();
spi_write(CMD_SET_PROPERTY);
spi_write(group);
spi_write(2);
spi_write(prop);
spi_write(val >> 8);
spi_write(val);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_set_property_24
*
* sets an 24 bit (3 byte) property in the Si4060
*
* group:the group number of the property
* prop:the number (index) of the property
* val:the value to set
*/
void si4060_set_property_24(uint8_t group, uint8_t prop, uint32_t val) {
spi_select();
spi_write(CMD_SET_PROPERTY);
spi_write(group);
spi_write(3);
spi_write(prop);
spi_write(val >> 16);
spi_write(val >> 8);
spi_write(val);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_set_property_32
*
* sets an 32 bit (4 byte) property in the Si4060
*
* group:the group number of the property
* prop:the number (index) of the property
* val:the value to set
*/
void si4060_set_property_32(uint8_t group, uint8_t prop, uint32_t val) {
spi_select();
spi_write(CMD_SET_PROPERTY);
spi_write(group);
spi_write(4);
spi_write(prop);
spi_write(val >> 24);
spi_write(val >> 16);
spi_write(val >> 8);
spi_write(val);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_gpio_pin_cfg
*
* configures the GPIOs on the Si4060
* see the GPIO_*-defines for reference
*
* gpio(0..3):setting flags for respective GPIOs
* drvstrength:the driver strength
*/
void si4060_gpio_pin_cfg(uint8_t gpio0, uint8_t gpio1, uint8_t gpio2, uint8_t gpio3, uint8_t drvstrength) {
spi_select();
spi_write(CMD_GPIO_PIN_CFG);
spi_write(gpio0);
spi_write(gpio1);
spi_write(gpio2);
spi_write(gpio3);
spi_write(NIRQ_MODE_DONOTHING);
spi_write(SDO_MODE_DONOTHING);
spi_write(drvstrength);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_part_info
*
* gets the PART_ID from the Si4060
* this can be used to check for successful communication.
* as the SPI bus returns 0xFF (MISO=high) when no slave is connected,
* reading CTS can not verify communication to the Si4060.
*
* returns:the PART_ID - should be 0x4060
*/
uint16_t si4060_part_info(void) {
uint16_t temp;
temp = 0;
spi_select();
spi_write(CMD_PART_INFO);
spi_deselect();
/* do not deselect after reading CTS */
while (temp != 0xff) {
temp = si4060_read_cmd_buf(0);
if (temp != 0xff) {
spi_deselect();
}
}
spi_read(); /* ignore CHIPREV */
temp = spi_read(); /* read PART[0] */
temp = temp << 8;
temp |= spi_read(); /* read PART[1] */
spi_deselect();
return temp;
}
/**
* si4060_get_state
*
* gets the current state of the Si4060
*/
uint8_t si4060_get_state(void)
{
uint16_t temp;
temp = 0;
spi_select();
spi_write(CMD_REQUEST_STATE);
spi_deselect();
/* do not deselect after reading CTS */
while (temp != 0xff) {
temp = si4060_read_cmd_buf(0);
if (temp != 0xff) {
spi_deselect();
}
}
temp = spi_read() & 0x0F; /* Get MAIN_STATE */
spi_deselect();
return temp;
}
/**
* si4060_get_freq
*/
void si4060_get_freq(void)
{
uint32_t inte, frac;
uint16_t temp;
spi_select();
spi_write(CMD_GET_PROPERTY);
spi_write(PROP_FREQ_CONTROL);
spi_write(0x4);
spi_write(FREQ_CONTROL_INTE);
spi_deselect();
/* do not deselect after reading CTS */
while (temp != 0xff) {
temp = si4060_read_cmd_buf(0);
if (temp != 0xff) {
spi_deselect();
}
}
inte = spi_read(); /* Get FREQ_CONTROL_INTE */
frac = (spi_read() << 16); /* Get FREQ_CONTROL_FRAC MSB */
frac |= (spi_read() << 8); /* Get FREQ_CONTROL_FRAC */
frac |= (spi_read() << 0); /* Get FREQ_CONTROL_FRAC LSB */
spi_deselect();
semihost_printf("INTE = 0x%02x, FRAC = 0x%06x\n", inte, frac);
}
/*
* si4060_start_tx
*
* starts transmission by the Si4060.
*
* channel:the channel to start transmission on
*/
void si4060_start_tx(uint8_t channel) {
spi_select();
spi_write(CMD_START_TX);
spi_write(channel);
spi_write(START_TX_TXC_STATE_SLEEP | START_TX_RETRANSMIT_0 | START_TX_START_IMM);
/* set length to 0 for direct mode (is this correct?) */
spi_write(0x00);
spi_write(0x00);
spi_deselect();
while (si4060_read_cmd_buf(1) != 0xff);
}
/*
* si4060_stop_tx
*
* makes the Si4060 stop all transmissions by transistioning to SLEEP state
*/
void si4060_stop_tx(void) {
si4060_change_state(STATE_SLEEP);
}
/*
* si4060_setup
*
* initializes the Si4060 by setting all neccesary internal registers.
* has to be called after si4060_power_up.
*
* mod_type:the type of modulation to use, use the MODEM_MOD_TYPE values (MOD_TYPE_*)
*/
void si4060_setup(uint8_t mod_type) {
/* set high performance mode */
si4060_set_property_8(PROP_GLOBAL,
GLOBAL_CONFIG,
GLOBAL_RESERVED | POWER_MODE_HIGH_PERF | SEQUENCER_MODE_FAST);
/* set up GPIOs */
si4060_gpio_pin_cfg(GPIO_MODE_DONOTHING,
GPIO_MODE_DONOTHING,
GPIO_MODE_DONOTHING,
PULL_CTL + GPIO_MODE_INPUT,
DRV_STRENGTH_HIGH);
/* disable preamble */
si4060_set_property_8(PROP_PREAMBLE,
PREAMBLE_TX_LENGTH,
0);
/* do not transmit sync word */
si4060_set_property_8(PROP_SYNC,
SYNC_CONFIG,
SYNC_NO_XMIT);
/* 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);
/* setup frequency deviation */
si4060_set_property_24(PROP_MODEM,
MODEM_FREQ_DEV,
(uint32_t)FDEV);
/* setup frequency deviation offset */
si4060_set_property_16(PROP_MODEM,
MODEM_FREQ_OFFSET,
0x0000);
/* setup divider to 8 (for 70cm ISM band */
si4060_set_property_8(PROP_MODEM,
MODEM_CLKGEN_BAND,
SY_SEL_1 | FVCO_DIV_8);
/* set up the PA power level */
si4060_set_property_8(PROP_PA,
PA_PWR_LVL,
0x3f);
/* set up the PA duty cycle */
si4060_set_property_8(PROP_PA,
PA_BIAS_CLKDUTY,
PA_BIAS_CLKDUTY_DIFF_50);
/* set up the integer divider */
si4060_set_property_8(PROP_FREQ_CONTROL,
FREQ_CONTROL_INTE,
(uint8_t)(FDIV_INTE));
/* set up the fractional divider */
si4060_set_property_24(PROP_FREQ_CONTROL,
FREQ_CONTROL_FRAC,
(uint32_t)(FDIV_FRAC));
/* TODO set the channel step size (if SPI frequency changing is used) */
}

Wyświetl plik

@ -0,0 +1,348 @@
/*
* Functions for controlling Si406x radios
* 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 "system/port.h"
#include "spi_bitbang.h"
#include "si406x_defs.h"
#include "hw_config.h"
#define RADIO_FREQ 434600000
// Quite low power
#define RADIO_PWR 0x7f
#define VCXO_FREQ SI406X_HF_FREQUENCY
uint32_t active_freq = RADIO_FREQ;
uint8_t active_pwr = RADIO_PWR;
uint16_t si446x_powerlevel = RADIO_PWR;
/**
* Generic SPI Send / Receive for the Si406x
*/
void _si406x_transfer(int tx_count, int rx_count, const uint8_t *data)
{
uint8_t response[100];
/* Send command */
_si406x_cs_enable();
for (int i = 0; i < tx_count; i++) {
spi_bitbang_transfer(data[i]);
}
_si406x_cs_disable();
/**
* Poll CTS. From the docs:
*
* READ_CMD_BUFF is used to poll the CTS signal via the SPI bus. The
* NSEL line should be pulled low, followed by sending the
* READ_CMD_BUFF command on SDI. While NSEL remains asserted low, an
* additional eight clock pulses are sent on SCLK and the CTS
* response byte is read on SDO. If the CTS response byte is not
* 0xFF, the host MCU should pull NSEL high and repeat the polling
* procedure.
*/
for (int i = 0; i < 20000; i++); // 20µS
_si406x_cs_enable();
do {
/* Issue READ_CMD_BUFF */
spi_bitbang_transfer(SI_CMD_READ_CMD_BUFF);
response[0] = spi_bitbang_transfer(0xFF);
/* If the reply is 0xFF, read the response */
if (response[0] == 0xFF) break;
/* Otherwise repeat the procedure */
_si406x_cs_disable();
for (int i = 0; i < 200; i++); // 20µS
_si406x_cs_enable();
} while (1); /* TODO: Timeout? */
/**
* Read response. From the docs:
*
* If the CTS response byte is 0xFF, the host MCU should keep NSEL
* asserted low and provide additional clock cycles on SCLK to read
* out as many response bytes (on SDO) as necessary. The host MCU
* should pull NSEL high upon completion of reading the response
* stream.
*/
for (int i = 1; i < rx_count; i++) {
response[i] = spi_bitbang_transfer(0xFF);
}
_si406x_cs_disable();
}
/**
* Set the Si406x synthesiser to the given frequency
*/
static void si406x_set_frequency(uint32_t frequency)
{
uint8_t outdiv, band;
if (frequency < 705000000UL) {
outdiv = 6; band = 1;
}
if (frequency < 525000000UL) {
if (VCXO_FREQ < 24000000) { // clock < 24mhz
outdiv = 4; band = 0;
} else {
outdiv = 8; band = 2;
}
}
if (frequency < 353000000UL) {
outdiv = 12; band = 3;
}
if (frequency < 239000000UL) {
outdiv = 16; band = 4;
}
if (frequency < 177000000UL) {
outdiv = 24; band = 5;
}
uint32_t f_pfd = 2 * VCXO_FREQ / outdiv;
uint16_t n = ((uint16_t)(frequency / f_pfd)) - 1;
float ratio = (float)frequency / (float)f_pfd;
float rest = ratio - (float)n;
uint32_t m = (uint32_t)(rest * 524288UL); // 2^19
// set the band parameter
uint8_t sy_sel = 8;
uint8_t set_band_property_command[] = {0x11, 0x20, 0x01, 0x51, (band + sy_sel)};
// send parameters
_si406x_transfer(5, 1, set_band_property_command);
// Set the pll parameters
uint16_t m2 = m / 0x10000;
uint16_t m1 = (m - m2 * 0x10000) / 0x100;
uint16_t m0 = (m - m2 * 0x10000 - m1 * 0x100);
// assemble parameter string
const uint8_t set_frequency_control_inte[] = {0x11, 0x40, 0x04, 0x00, n, m2, m1, m0};
// const uint8_t set_frequency_control_inte[] = {
// 0x11, 0x40, 0x08, 0x00, n, m2, m1, m0, 0x0B, 0x61, 0x20, 0xFA};
//const uint8_t set_frequency_control_inte[] = {0x11, 0x40, 0x08, 0x00, n, m2, m1, m0, 0x0B, 0x61, 0x20, 0xFA};
// send parameters
_si406x_transfer(8, 1, set_frequency_control_inte);
// Read parameters
const uint8_t get_frequency_control[] = { 0x12, 0x40, 0x04, 0x00 };
_si406x_transfer(4, 5, get_frequency_control);
}
void si406x_init(void)
{
/* Configure the SDN pin */
port_pin_set_config(SI406X_SDN_PIN,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Put the SI406x in shutdown */
_si406x_sdn_enable();
/* Configure the SDN pin */
port_pin_set_config(SI406X_SEL_PIN,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Put the SEL pin in reset */
_si406x_cs_disable();
/* Configure the GPIO and IRQ pins */
/* Configure the serial port */
spi_bitbang_init(SI406X_SERCOM_MOSI_PIN,
SI406X_SERCOM_MISO_PIN,
SI406X_SERCOM_SCK_PIN);
/* Boot */
for (int i = 0; i < 15*10000; i++); // 15ms
_si406x_sdn_disable();
for (int i = 0; i < 15*10000; i++); // 15ms
const uint8_t PART_INFO_command[] = {0x01}; // Part Info
_si406x_transfer(1, 9, PART_INFO_command);
}
/**
* Sets the modem into CW mode
*/
static void si446xSetModem(void)
{
// Set to CW mode
const uint8_t set_modem_mod_type_command[] = {0x11, 0x20, 0x01, 0x00, 0x00};
_si406x_transfer(5, 1, set_modem_mod_type_command);
}
/**
* Sets the tx power
*/
void si446xSetPower(uint8_t pwr)
{
// Set the Power
// uint8_t set_pa_pwr_lvl_property_command[] = {0x11, 0x22, 0x01, 0x01, pwr};
// _si406x_transfer(5, 1, set_pa_pwr_lvl_property_command);
}
// Config reset ----------------------------------------------------------
static void si446xReset(void)
{
// _si406x_hf_clock_enable();
_si406x_sdn_enable(); // active high shutdown = reset
for (int i = 0; i < 15*10000; i++); // 15ms
_si406x_sdn_disable(); // booting
for (int i = 0; i < 15*10000; i++); // 15ms
const uint8_t PART_INFO_command[] = {0x01}; // Part Info
_si406x_transfer(1, 9, PART_INFO_command);
//divide VCXO_FREQ into its bytes; MSB first
uint16_t x3 = VCXO_FREQ / 0x1000000;
uint16_t x2 = (VCXO_FREQ - x3 * 0x1000000) / 0x10000;
uint16_t x1 = (VCXO_FREQ - x3 * 0x1000000 - x2 * 0x10000) / 0x100;
uint16_t x0 = (VCXO_FREQ - x3 * 0x1000000 - x2 * 0x10000 - x1 * 0x100);
/* ---- POWER_UP ---- */
/* no patch, boot main app. img, FREQ_VCXO, return 1 byte */
/* */
const uint8_t init_command[] = {0x02, 0x01, 0x01, x3, x2, x1, x0};
_si406x_transfer(7, 1 , init_command);
// Clear all pending interrupts and get the interrupt status back
const uint8_t get_int_status_command[] = {0x20, 0x00, 0x00, 0x00};
_si406x_transfer(4, 9, get_int_status_command);
// cliPrint("Radio ready\n");
const uint8_t set_int_ctrl_enable[] = {0x11, 0x01, 0x01, 0x00, 0x00};
_si406x_transfer(5, 1, set_int_ctrl_enable);
// cliPrint("Setting no Interrupts (see WDS)\n");
// Set all GPIOs LOW; Link NIRQ to CTS; Link SDO to MISO; Max drive strength
const uint8_t gpio_pin_cfg_command[] = {0x13, 0x02, 0x02, 0x02, 0x02, 0x08, 0x0B, 0x00};
_si406x_transfer(8, 8, gpio_pin_cfg_command);
// cliPrint("LEDs should be switched off at this point\n");
//const uint8_t set_global_config1[] = {0x11, 0x00, 0x01, 0x03, 0x60};
//_si406x_transfer(5, 1, set_global_config1);
// Sequencer Mode = Fast, Fifo = Half Duplex
// cliPrint("Setting special global Config 1 changes (see WDS)\n");
// const uint8_t set_global_xo_tune_command[] = {0x11, 0x00, 0x01, 0x00, 0x00};
//_si406x_transfer(5, 1, set_global_xo_tune_command);
// cliPrint("Setting no additional capacitance on VXCO\n");
si406x_set_frequency(active_freq);
si446xSetPower(active_pwr);
si446xSetModem();
// cliPrint("CW mode set\n");
si406x_state_tx_tune();
// cliPrint("TX tune\n");
}
void si446xPTTOn(void)
{
si446xReset();
// turn on the LED (GPIO0) to indicate TX
// Set GPIOs LOW; Link NIRQ to CTS; Link SDO to MISO; Max drive strength
const uint8_t gpio_pin_cfg_command2[] = {0x13, 0x02, 0x02, 0x02, 0x02, 0x08, 0x0B, 0x00};
_si406x_transfer(8, 1, gpio_pin_cfg_command2);
si406x_state_tx();
const uint8_t get_state[] = {0x33};
_si406x_transfer(1, 3, get_state);
}
void si446xPTTOff(void)
{
si406x_state_ready();
// turn off the LED (GPIO1)
// Set GPIO0 HIGH
const uint8_t gpio_pin_cfg_command0[] = {0x13, 0x03, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00};
_si406x_transfer(8, 1, gpio_pin_cfg_command0);
_si406x_sdn_enable(); // active high = shutdown
_si406x_hf_clock_disable(); // keep enabled for now
}
/**
* Quick and dirty loopback test. Should print 0x34
*/
void spi_loopback_test(void)
{
/* spi_init(SI406X_SERCOM, */
/* SPI_MODE_MASTER, /\** SPI mode *\/ */
/* SPI_DATA_ORDER_MSB, /\** Data order *\/ */
/* SPI_TRANSFER_MODE_0, /\** Transfer mode *\/ */
/* SI406X_SERCOM_MUX, /\** Mux setting *\/ */
/* SPI_CHARACTER_SIZE_8BIT, /\** SPI character size *\/ */
/* false, /\** Enabled in sleep *\/ */
/* true, /\** Enable receiver *\/ */
/* 100000, /\** Master - Baud rate *\/ */
/* 0, /\** Slave - Frame format *\/ */
/* 0, /\** Slave - Address mode *\/ */
/* 0, /\** Slave - Address *\/ */
/* 0, /\** Slave - Address mask *\/ */
/* false, /\** Slave - Preload data *\/ */
/* GCLK_GENERATOR_0, /\** GCLK generator to use *\/ */
/* SI406X_SERCOM_MOSI_PINMUX, /\** Pinmux *\/ */
/* SI406X_SERCOM_MISO_PINMUX, /\** Pinmux *\/ */
/* SI406X_SERCOM_SCK_PINMUX, /\** Pinmux *\/ */
/* PINMUX_UNUSED); /\** Pinmux *\/ */
/* Init loopback */
spi_bitbang_init(SI406X_SERCOM_MOSI_PIN,
SI406X_SERCOM_MOSI_PIN,
SI406X_SERCOM_SCK_PIN);
/* Enable */
/* Test transfer */
uint8_t data = spi_bitbang_transfer(0x34);
/* Print result */
semihost_printf("Rx'ed: 0x%02x\n", data);
}

Wyświetl plik

@ -0,0 +1,104 @@
/*
* SPI bit-banging! Yay
* 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 "system/port.h"
/** Should be at least 20ns or something. Why am I bit-banging this? */
#define BIT_DELAY __NOP
#define BIT_TOGGLE(bit) do { \
PORTA.OUTTGL.reg = bit; } while (0)
#define BIT_SET(bit) do { \
PORTA.OUTSET.reg = bit; } while (0)
#define BIT_CLEAR(bit) do { \
PORTA.OUTCLR.reg = bit; } while (0)
#define BIT_READ(bit) (PORTA.IN.reg & bit)
uint32_t mosi_mask = 0;
uint32_t miso_mask = 0;
uint32_t sck_mask = 0;
void spi_bitbang_init(const uint8_t mosi,
const uint8_t miso,
const uint8_t sck)
{
/* Set the masks */
mosi_mask = (1 << mosi);
miso_mask = (1 << miso);
sck_mask = (1 << sck);
/* Configure the output pins */
if (mosi == miso) { /* Loopback */
port_pin_set_config(mosi,
PORT_PIN_DIR_OUTPUT_WTH_READBACK,/* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
} else { /* No loopback */
port_pin_set_config(mosi,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
port_pin_set_config(miso,
PORT_PIN_DIR_INPUT, /* Direction */
PORT_PIN_PULL_UP, /* Pull */
false); /* Powersave */
}
port_pin_set_config(sck,
PORT_PIN_DIR_OUTPUT, /* Direction */
PORT_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Set output pins to default values */
BIT_SET(mosi_mask);
BIT_CLEAR(sck_mask);
}
uint8_t spi_bitbang_transfer(uint8_t byte)
{
for (uint8_t counter = 0; counter < 8; counter++) {
/* Set output data */
if (byte & 0x80) {
BIT_SET(mosi_mask);
} else {
BIT_CLEAR(mosi_mask);
}
byte <<= 1;
BIT_DELAY();
/* Latch Data into Slave */
BIT_TOGGLE(sck_mask);
BIT_DELAY();
/* Read Data */
if (BIT_READ(miso_mask)) {
byte |= 0x1;
}
/* Slave shifts out next data bit */
BIT_TOGGLE(sck_mask);
}
return byte;
}

Wyświetl plik

@ -379,6 +379,7 @@ void system_clock_source_dfll_set_config(
rev &= DSU_DID_REVISION_Msk;
rev = rev >> DSU_DID_REVISION_Pos;
/* Calculate the Value register */
if (rev < _SYSTEM_MCU_REVISION_D) {
val =
_SYSTEM_OLD_DFLLVAL_COARSE(coarse_value) |
@ -389,6 +390,7 @@ void system_clock_source_dfll_set_config(
_SYSTEM_NEW_DFLLVAL_FINE(fine_value);
}
/* Calculate the Control register */
control =
(uint32_t)wakeup_lock |
(uint32_t)stable_tracking |
@ -396,8 +398,8 @@ void system_clock_source_dfll_set_config(
(uint32_t)chill_cycle |
((uint32_t)on_demand << SYSCTRL_DFLLCTRL_ONDEMAND_Pos);
/* Set the Multiplication register for closed loop mode */
if (loop_mode == SYSTEM_CLOCK_DFLL_LOOP_MODE_CLOSED) {
if(rev < _SYSTEM_MCU_REVISION_D) {
mul =
_SYSTEM_OLD_DFLLMUL_CSTEP(coarse_max_step) |

Wyświetl plik

@ -71,7 +71,7 @@
* \note When the counter is configured to re-trigger on an event, the counter
* will not start until the start function is used.
*/
static inline void tc_enable(Tc* const hw)
void tc_enable(Tc* const hw)
{
assert(hw);
@ -83,7 +83,7 @@ static inline void tc_enable(Tc* const hw)
/**
* Disables a TC module and stops the counter.
*/
static inline void tc_disable(Tc* const hw)
void tc_disable(Tc* const hw)
{
assert(hw);
@ -96,7 +96,7 @@ static inline void tc_disable(Tc* const hw)
/**
* Starts or restarts an initialized TC module's counter.
*/
static inline void tc_start_counter(Tc* const hw)
void tc_start_counter(Tc* const hw)
{
assert(hw);
@ -114,7 +114,7 @@ static inline void tc_start_counter(Tc* const hw)
* counting up, or max or the top value if the counter was counting
* down when stopped.
*/
static inline void tc_stop_counter(Tc* const hw)
void tc_stop_counter(Tc* const hw)
{
assert(hw);
@ -135,7 +135,7 @@ static inline void tc_stop_counter(Tc* const hw)
* \retval TC_STATUS_CAPTURE_OVERFLOW Timer capture data has overflowed
* \retval TC_STATUS_COUNT_OVERFLOW Timer count value has overflowed
*/
static inline uint32_t tc_get_status(Tc* const hw)
uint32_t tc_get_status(Tc* const hw)
{
assert(hw);
@ -174,8 +174,8 @@ static inline uint32_t tc_get_status(Tc* const hw)
*
* \param[in] status_flags Bitmask of \c TC_STATUS_* flags to clear
*/
static inline void tc_clear_status(Tc* const hw,
const uint32_t status_flags)
void tc_clear_status(Tc* const hw,
const uint32_t status_flags)
{
assert(hw);
@ -336,7 +336,6 @@ void tc_set_compare_value(Tc* const hw,
const uint32_t compare)
{
assert(hw);
assert(compare);
WAIT_FOR_SYNC(hw);
@ -506,7 +505,7 @@ void tc_set_top_value(Tc* const hw,
*
* \param[in] events Struct containing flags of events to enable
*/
static inline void tc_enable_events(Tc* const hw,
void tc_enable_events(Tc* const hw,
struct tc_events *const events)
{
/* Sanity check arguments */
@ -545,7 +544,7 @@ static inline void tc_enable_events(Tc* const hw,
*
* \param[in] events Struct containing flags of events to disable
*/
static inline void tc_disable_events(Tc* const hw,
void tc_disable_events(Tc* const hw,
struct tc_events *const events)
{
assert(hw);

Wyświetl plik

@ -0,0 +1,215 @@
/*
* Functions for turning the GPS timepulse into a HF Clock
* 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 "system/clock.h"
#include "system/gclk.h"
#include "system/pinmux.h"
#include "tc/tc_driver.h"
#include "hw_config.h"
#define DFLL48_MUL (DFLL48M_CLK / GPS_TIMEPULSE_FREQ)
/* Check that DFLL48_MUL is an integer */
//#if ((DFLL48M_CLK * 100000000) / GPS_TIMEPULSE_FREQ != (DFLL48_MUL * 100000000))
//#error DFLL48M_CLK must be a integer multiple of GPS_TIMEPULSE_FREQ!
//#endif
void timepulse_init(void)
{
/* Set up the DFLL GCLK channel */
system_gclk_chan_set_config(SYSCTRL_GCLK_ID_DFLL48, DFLL48M_GCLK);
system_gclk_chan_enable(SYSCTRL_GCLK_ID_DFLL48);
/* Configure DFLL48 */
system_clock_source_dfll_set_config(
SYSTEM_CLOCK_DFLL_LOOP_MODE_CLOSED, /* Loop Mode */
false, /* On demand */
SYSTEM_CLOCK_DFLL_QUICK_LOCK_DISABLE, /* Quick Lock */
SYSTEM_CLOCK_DFLL_CHILL_CYCLE_ENABLE, /* Chill Cycle */
SYSTEM_CLOCK_DFLL_WAKEUP_LOCK_KEEP, /* Lock during wakeup */
SYSTEM_CLOCK_DFLL_STABLE_TRACKING_TRACK_AFTER_LOCK,
0x1f / 4, /* Open Loop - Coarse calibration value */
0xff / 4, /* Open Loop - Fine calibration value */
1, /* Closed Loop - Coarse Maximum step */
1, /* Closed Loop - Fine Maximum step */
DFLL48_MUL); /* Frequency Multiplication Factor */
/* Enable DFLL48 */
system_clock_source_enable(SYSTEM_CLOCK_SOURCE_DFLL);
/* Wait for it to be ready */
while(!system_clock_source_is_ready(SYSTEM_CLOCK_SOURCE_DFLL));
/* system_clock_source_xosc_set_config(SYSTEM_CLOCK_EXTERNAL_CLOCK, */
/* SYSTEM_XOSC_STARTUP_16384, */
/* true, /\* Auto gain control *\/ */
/* 16000000UL, /\* Frequency *\/ */
/* true, /\* Run in Standby *\/ */
/* false); /\* Run on demand *\/ */
/* system_clock_source_enable(SYSTEM_CLOCK_SOURCE_XOSC); */
/* Configure the HF GCLK */
system_gclk_gen_set_config(SI406X_HF_GCLK,
GCLK_SOURCE_DFLL48M, /* Source */
false, /* High When Disabled */
48, /* Division Factor */
false, /* Run in standby */
true); /* Output Pin Enable */
/* Configure the output pin */
system_pinmux_pin_set_config(SI406X_HF_CLK_PINMUX >> 16, /* GPIO Pin */
SI406X_HF_CLK_PINMUX & 0xFFFF, /* Mux Position */
SYSTEM_PINMUX_PIN_DIR_INPUT, /* Direction */
SYSTEM_PINMUX_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Enable the HF GCLK */
system_gclk_gen_enable(SI406X_HF_GCLK);
}
/**
* Switches GCLK_MAIN (a.k.a. GCLK0) to the gps timepulse
*/
void switch_gclk_main_to_timepulse(void)
{
/* Enable GCLK_IO[0] */
system_pinmux_pin_set_config(GPS_TIME_PINMUX >> 16, /* GPIO Pin */
GPS_TIME_PINMUX & 0xFFFF, /* Mux Position */
SYSTEM_PINMUX_PIN_DIR_INPUT, /* Direction */
SYSTEM_PINMUX_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
/* Switch GCLK_MAIN to GCLK_IO[0] */
system_gclk_gen_set_config(GCLK_GENERATOR_0, /* GCLK 0 */
GCLK_SOURCE_GCLKIN,/* Source from pin */
false, /* High When Disabled */
1, /* Division Factor */
true, /* Run in standby */
true); /* Output Pin Enable */
/* Wait for switch? */
}
/**
* Outputs GCLK0 div 2 on the HF CLK pin
*/
void half_glck_main_on_hf_clk(void)
{
bool capture_channel_enables[] = {true, true};
uint32_t compare_channel_values[] = {0x0000, 0x0000};
tc_init(TC2,
GCLK_GENERATOR_0,
TC_COUNTER_SIZE_8BIT,
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 */
0x0000, /* Top value */
capture_channel_enables, /* Capture Channel Enables */
compare_channel_values); /* Compare Channels Values */
/* Enable the output pin */
system_pinmux_pin_set_config(PINMUX_PA17F_TC2_WO1 >> 16, /* GPIO Pin */
PINMUX_PA17F_TC2_WO1 & 0xFFFF, /* Mux Position */
SYSTEM_PINMUX_PIN_DIR_INPUT, /* Direction */
SYSTEM_PINMUX_PIN_PULL_NONE, /* Pull */
false); /* Powersave */
tc_enable(TC2);
tc_start_counter(TC2);
}
/**
* Returns the current GCLK_MAIN frequency, as measured against OSC8M
*/
uint32_t gclk_main_frequency(void)
{
uint32_t osc8m_frequency = 8000000UL >> SYSCTRL->OSC8M.bit.PRESC;
/* Configure GCLK Gen 6 as reference */
system_gclk_gen_set_config(GCLK_GENERATOR_6,
GCLK_SOURCE_OSC8M, /* Source */
false, /* High When Disabled */
4, /* Division Factor */
false, /* Run in standby */
false); /* Output Pin Enable */
/* Enable GCLK 6 */
system_gclk_gen_enable(GCLK_GENERATOR_6);
/* Timer 0 free runs on GLCK 0 */
bool t0_capture_channel_enables[] = {false, false};
uint32_t t0_compare_channel_values[] = {0x0000, 0x0000};
tc_init(TC0,
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, /* Run in standby */
0x0000, /* Initial value */
0xFFFFFFFF, /* Top value */
t0_capture_channel_enables, /* Capture Channel Enables */
t0_compare_channel_values); /* Compare Channels Values */
/* Timer 3 counts 10000 cycles of GLCK 6 */
bool t1_capture_channel_enables[] = {false, false};
uint32_t t1_compare_channel_values[] = {10000, 0x0000};
tc_init(TC3,
GCLK_GENERATOR_6,
TC_COUNTER_SIZE_16BIT,
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, /* Run in standby */
0x0000, /* Initial value */
0xFFFF, /* Top value */
t1_capture_channel_enables, /* Capture Channel Enables */
t1_compare_channel_values); /* Compare Channels Values */
tc_enable(TC0);
tc_enable(TC3);
tc_start_counter(TC0);
tc_start_counter(TC3);
/* Wait 10000 cycles of GCLK 6 */
while (!(tc_get_status(TC3) & TC_STATUS_CHANNEL_0_MATCH));
uint32_t gclk_main_count = tc_get_count_value(TC0) - 50;
return gclk_main_count / 10;
}