From bd8c49fb7ffa703de0205a64bbeba371ba7a50eb Mon Sep 17 00:00:00 2001 From: Alan Johnston Date: Sat, 22 Sep 2018 11:05:55 -0400 Subject: [PATCH] Adding my copy of AFSK 1200 X.25 --- afsk/ax25.c | 94 ++++ afsk/ax25.h | 62 +++ afsk/ax5043.c | 1126 ++++++++++++++++++++++++++++++++++++++++++ afsk/ax5043.h | 582 ++++++++++++++++++++++ afsk/main.c | 92 ++++ afsk/main.c.bk | 92 ++++ afsk/main.c.x25_only | 85 ++++ afsk/status.h | 37 ++ afsk/utils.h | 54 ++ 9 files changed, 2224 insertions(+) create mode 100644 afsk/ax25.c create mode 100644 afsk/ax25.h create mode 100644 afsk/ax5043.c create mode 100644 afsk/ax5043.h create mode 100644 afsk/main.c create mode 100644 afsk/main.c.bk create mode 100644 afsk/main.c.x25_only create mode 100644 afsk/status.h create mode 100644 afsk/utils.h diff --git a/afsk/ax25.c b/afsk/ax25.c new file mode 100644 index 00000000..15b127c2 --- /dev/null +++ b/afsk/ax25.c @@ -0,0 +1,94 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Libre Space Foundation + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ax25.h" +#include +#include "ax5043.h" +#include "status.h" + +static uint8_t __tx_buffer[MAX_FRAME_LEN]; + +/** + * Creates the header field of the AX.25 frame + * @param conf the AX.25 handle + * @param dest_addr the destination callsign address + * @param dest_ssid the destination SSID + * @param src_addr the callsign of the source + * @param src_ssid the source SSID + * @param preamble_len the number of the AX.25 repetitions in the preamble + * @param postamble_len the number of the AX.25 repetitions in the postamble + */ +int ax25_init(ax25_conf_t *conf, const uint8_t *dest_addr, uint8_t dest_ssid, + const uint8_t *src_addr, uint8_t src_ssid, uint8_t preamble_len, + uint8_t postamble_len) { + uint16_t i = 0; + + if (!conf || !dest_addr || !src_addr || preamble_len < 4 + || !postamble_len) { + return -PQWS_INVALID_PARAM; + } + + conf->preamble_len = preamble_len; + conf->postable_len = postamble_len; + uint8_t *out = conf->addr_field; + + for (i = 0; i < strnlen((char *) dest_addr, AX25_CALLSIGN_MAX_LEN); i++) { + *out++ = (uint8_t) (dest_addr[i] << 1); + } + /* + * Perhaps the destination callsign was smaller that the maximum allowed. + * In this case the leftover bytes should be filled with space + */ + for (; i < AX25_CALLSIGN_MAX_LEN; i++) { + *out++ = ' ' << 1; + } + /* Apply SSID, reserved and C bit */ + /* FIXME: C bit is set to 0 implicitly */ + *out++ = (uint8_t) ((0x0F & dest_ssid) << 1) | 0x60; + //*out++ = ((0b1111 & dest_ssid) << 1) | 0b01100000; + + for (i = 0; i < strnlen((char *) src_addr, AX25_CALLSIGN_MAX_LEN); i++) { + *out++ = (uint8_t) (src_addr[i] << 1); + } + for (; i < AX25_CALLSIGN_MAX_LEN; i++) { + *out++ = ' ' << 1; + } + /* Apply SSID, reserved and C bit. As this is the last address field + * the trailing bit is set to 1. + */ + /* FIXME: C bit is set to 0 implicitly */ + *out++ = (uint8_t) ((0x0F & src_ssid) << 1) | 0x61; + //*out++ = ((0b1111 & src_ssid) << 1) | 0b01100001; + conf->addr_field_len = AX25_MIN_ADDR_LEN; + return PQWS_SUCCESS; +} + +int ax25_tx_frame(ax25_conf_t *hax25, ax5043_conf_t *hax, + const uint8_t *payload, uint32_t len) { + if (!hax25 || !hax || !payload || !len) { + return -PQWS_INVALID_PARAM; + } + + memcpy(__tx_buffer, hax25->addr_field, hax25->addr_field_len); + memcpy(__tx_buffer + hax25->addr_field_len, payload, len); + + return ax5043_tx_frame(hax, __tx_buffer, len + hax25->addr_field_len, + hax25->preamble_len, hax25->postable_len, 1000); +} diff --git a/afsk/ax25.h b/afsk/ax25.h new file mode 100644 index 00000000..2784c93d --- /dev/null +++ b/afsk/ax25.h @@ -0,0 +1,62 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Libre Space Foundation + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef AX25_H_ +#define AX25_H_ + +#include +#include "ax5043.h" + +#define AX25_MAX_ADDR_LEN 28 +#define AX25_MAX_FRAME_LEN 256 +#define AX25_MIN_ADDR_LEN 14 +#define AX25_SYNC_FLAG 0x7E +#define AX25_MIN_CTRL_LEN 1 +#define AX25_MAX_CTRL_LEN 2 +#define AX25_CALLSIGN_MAX_LEN 6 +#define AX25_CALLSIGN_MIN_LEN 2 +#define AX25_PREAMBLE_LEN 16 +#define AX25_POSTAMBLE_LEN 16 + +/** + * AX.25 Frame types + */ +typedef enum { + AX25_I_FRAME, //!< AX25_I_FRAME Information frame + AX25_S_FRAME, //!< AX25_S_FRAME Supervisory frame + AX25_U_FRAME, //!< AX25_U_FRAME Unnumbered frame + AX25_UI_FRAME /**!< AX25_UI_FRAME Unnumbered information frame */ +} ax25_frame_type_t; + +typedef struct { + uint8_t preamble_len; + uint8_t postable_len; + uint8_t addr_field[AX25_MAX_ADDR_LEN]; + uint32_t addr_field_len; +} ax25_conf_t; + +int +ax25_init(ax25_conf_t *conf, const uint8_t *dest_addr, uint8_t dest_ssid, + const uint8_t *src_addr, uint8_t src_ssid, uint8_t preamble_len, + uint8_t postamble_len); +int ax25_tx_frame(ax25_conf_t *hax25, ax5043_conf_t *hax, + const uint8_t *payload, uint32_t len); + +#endif /* AX25_H_ */ diff --git a/afsk/ax5043.c b/afsk/ax5043.c new file mode 100644 index 00000000..a6763050 --- /dev/null +++ b/afsk/ax5043.c @@ -0,0 +1,1126 @@ +/* + * A sample application transmitting AFSK 1200 baud + * + * Portions Copyright (C) 2018 Libre Space Foundation + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include "ax25.h" +#include "ax5043.h" +#include "status.h" +#include "utils.h" +#include "spi/ax5043spi.h" + +static uint8_t single_fifo_access = 0; + +static uint8_t __tx_buf[MAX_FRAME_LEN]; +static size_t __tx_buf_idx = 0; +static uint8_t __tx_fifo_chunk[AX5043_FIFO_MAX_SIZE]; +static uint32_t __tx_remaining = 0; + +/** + * FIFO command for the preamble. The third byte corresponds the length of + * the preamble and is set by the TX routine for every frame + */ +static uint8_t __preamble_cmd[4] = { AX5043_FIFO_REPEATDATA_CMD, +AX5043_FIFO_PKTSTART | AX5043_FIFO_RAW | AX5043_FIFO_NOCRC, 0, +AX25_SYNC_FLAG }; + +/** + * FIFO command for the postable. The third byte corresponds the length of + * the postable and is set by the TX routine for every frame + */ +static uint8_t __postamble_cmd[4] = { +AX5043_FIFO_REPEATDATA_CMD, +AX5043_FIFO_PKTSTART | AX5043_FIFO_PKTEND | AX5043_FIFO_RAW | AX5043_FIFO_NOCRC, + 0, AX25_SYNC_FLAG }; + +/** + * Indicates if a TX is currently active + */ +static volatile uint8_t __tx_active = 0; + +static ax5043_conf_t *__ax5043_conf = NULL; + +static inline int set_tx_black_magic_regs(); + +/** + * Checks if the AX5043 handler is valid + * @param conf the AX5043 configuration handler pointer + * @return 1 if it is valid 0 otherwise + */ +static uint8_t is_ax5043_conf_valid(ax5043_conf_t *conf) { + if (!conf || !conf->f_xtal) { + return 0; + } + return 1; +} + +/** + * Resets the AX5043 + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +int ax5043_reset(ax5043_conf_t *conf) { + int ret; + uint8_t val; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + conf->rf_init = 0; + usleep(100); + + /* Reset the chip using the appropriate register */ + val = BIT(7); + ret = ax5043_spi_write_8(conf, AX5043_REG_PWRMODE, val); + if (ret) { + return ret; + } + usleep(100); + /* Clear the reset bit, but keep REFEN and XOEN */ + ret = ax5043_spi_read_8(conf, &val, AX5043_REG_PWRMODE); + if (ret) { + return ret; + } + val &= (BIT(6) | BIT(5)); + ret = ax5043_spi_write_8(conf, AX5043_REG_PWRMODE, val); + if (ret) { + return ret; + } + usleep(100); + + ret = ax5043_set_power_mode(conf, POWERDOWN); + if (ret) { + return ret; + } + return PQWS_SUCCESS; +} + +/** + * Initialization routine for the AX5043 IC + * @param conf the AX5043 configuration handler + * @param spi the SPI handler + * @param f_xtal the frequency of the crystal or the TCXO + * @param vco the VCO mode + * @return 0 on success or appropriate negative error code + */ +int ax5043_init(ax5043_conf_t *conf, uint32_t f_xtal, vco_mode_t vco) { + int ret; + uint8_t revision; + uint8_t val; + + if (!conf) { + return -PQWS_INVALID_PARAM; + } + + /* Set the initial parameters */ + memset(conf, 0, sizeof(ax5043_conf_t)); + + switch (vco) { + case VCO_INTERNAL: + case VCO_EXTERNAL: + conf->vco = vco; + break; + default: + return -PQWS_INVALID_PARAM; + } + + conf->rf_init = 0; + conf->freqsel = -1; + conf->f_xtal = f_xtal; + if (conf->f_xtal > 24800000) { + conf->f_xtaldiv = 2; + } else { + conf->f_xtaldiv = 1; + } + + ret = ax5043_reset(conf); + if (ret) { + return ret; + } + + /* Try first to read the revision register of the AX5043 */ + ret = ax5043_spi_read_8(conf, &revision, AX5043_REG_REV); + if (ret) { + return ret; + } + + if (revision != AX5043_REV) { + return -PQWS_NO_RF_FOUND; + } + + /* To ensure communication try to write and read the scratch register */ + val = AX5043_SCRATCH_TEST; + ret = ax5043_spi_write_8(conf, AX5043_REG_SCRATCH, val); + if (ret) { + return ret; + } + + val = 0x0; + ret = ax5043_spi_read_8(conf, &val, AX5043_REG_SCRATCH); + if (ret) { + return ret; + } + + if (val != AX5043_SCRATCH_TEST) { + return -PQWS_NO_RF_FOUND; + } + + ret = ax5043_set_pll_params(conf); + if (ret) { + return ret; + } + + /* Write the performance register F35 based on the XTAL frequency */ + if (conf->f_xtaldiv == 1) { + ret = ax5043_spi_write_8(conf, 0xF35, 0x10); + } else { + ret = ax5043_spi_write_8(conf, 0xF35, 0x11); + } + if (ret) { + return ret; + } + + /* FIFO maximum chunk */ + ret = ax5043_spi_write_8(conf, AX5043_REG_PKTCHUNKSIZE, + AX5043_PKTCHUNKSIZE_240); + if (ret) { + return ret; + } + + /* Set RF parameters */ + ret = ax5043_freqsel(conf, FREQA_MODE); + if (ret) { + return ret; + } + + /* + * We use APRS for all transmitted frames. APRS is encapsulated in a + * AX.25 frame. For 9600 baudrate is FSK9600 G3RUH compatible modem + */ + ret = ax5043_aprs_framing_setup(conf); + if (ret) { + return ret; + } + + /* Setup TX only related parameters */ + ret = ax5043_conf_tx_path(conf); + if (ret) { + return ret; + } + + /* Set an internal copy for the ax5042_wait_for_transmit function */ + __ax5043_conf = conf; + + if (ret) { + return ret; + } + ax5043_enable_pwramp(conf, AX5043_EXT_PA_DISABLE); + return PQWS_SUCCESS; +} + +/** + * Performs TX specific configuration of the AX5043 + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +int ax5043_conf_tx_path(ax5043_conf_t *conf) { + int ret; + + ret = ax5043_set_tx_synth(conf); + if (ret) { + return ret; + } + + ret = ax5043_set_tx_baud(conf, TX_BAUDRATE); + if (ret) { + return ret; + } + + ret = ax5043_set_tx_freq(conf, TX_FREQ_HZ); + if (ret) { + return ret; + } + + /* Our TX is on single ended mode */ + //ret = ax5043_spi_write_8(conf, AX5043_REG_MODCFGA, + //AX5043_TX_SINGLE_ENDED); + //if (ret) { + // return ret; + //} + + /* Our TX is on double ended mode */ + ret = ax5043_spi_write_8(conf, AX5043_REG_MODCFGA, + AX5043_TX_DIFFERENTIAL); + if (ret) { + return ret; + } + + /* Set the rest of the performance registers for TX */ + ret = set_tx_black_magic_regs(conf); + if (ret) { + return ret; + } + + /* + * As our board has an external PA, reduce the output power to reduce + * the excess bandwidth emissions + */ + //ret = ax5043_spi_write_16(conf, AX5043_REG_TXPWRCOEFFB1, 0x01FF); + //if (ret) { + //return ret; + //} + + // Not using a PA, transmit half power + ret = ax5043_spi_write_16(conf, AX5043_REG_TXPWRCOEFFB1, 0x07FF); + if (ret) { + return ret; + } + + return PQWS_SUCCESS; +} + +/** + * Sets the power mode of the AX5043 + * @param conf the AX5043 configuration handler + * @param mode the power mode + * @return 0 on success or appropriate negative error code + */ +int ax5043_set_power_mode(ax5043_conf_t *conf, power_mode_t mode) { + int ret; + uint8_t val; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + /* Read the contents of the register */ + ret = ax5043_spi_read_8(conf, &val, AX5043_REG_PWRMODE); + if (ret) { + return ret; + } + + /* Keep REFEN and XOEN values */ + val &= (BIT(6) | BIT(5)); + + switch (mode) { + case POWERDOWN: + val |= AX5043_POWERDOWN; + break; + case DEEPSLEEP: + val |= AX5043_DEEPSLEEP; + break; + case STANDBY: + val |= AX5043_STANDBY; + break; + case FIFO_ENABLED: + val |= AX5043_FIFO_ENABLED; + break; + case RECEIVE_MODE: + val |= AX5043_RECEIVE_MODE; + break; + case RECEIVER_RUNNING: + val |= AX5043_RECEIVER_RUNNING; + break; + case RECEIVER_WOR: + val |= AX5043_RECEIVER_WOR; + break; + case TRANSMIT_MODE: + val |= AX5043_TRANSMIT_MODE; + break; + case FULLTX: + val |= AX5043_FULLTX; + break; + default: + return -PQWS_INVALID_PARAM; + } + return ax5043_spi_write_8(conf, AX5043_REG_PWRMODE, val); +} + +/** + * Sets the RF frequency of the TX. If the previous TX frequency is + * further enough than the new one, this function performs automatically + * auto-ranging. + * + * @param conf the AX5043 configuration handler + * @param freq the target RF frequency + * @return 0 on success or appropriate negative error code + */ +int ax5043_set_tx_freq(ax5043_conf_t *conf, uint32_t freq) { + int ret; + uint32_t prev_freq; + uint32_t reg_val; + uint8_t rfdiv = 0; + uint8_t pllcodediv = 0; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + /* Check the frequency range. The actual range depends on the VCO used */ + switch (conf->vco) { + case VCO_INTERNAL: + if (freq >= MIN_RF_FREQ_INT_VCO_RFDIV0 + && freq <= MAX_RF_FREQ_INT_VCO_RFDIV0) { + rfdiv = AX5043_RFDIV0; + } else if (freq >= MIN_RF_FREQ_INT_VCO_RFDIV1 + && freq <= MAX_RF_FREQ_INT_VCO_RFDIV1) { + rfdiv = AX5043_RFDIV1; + } else { + return -PQWS_INVALID_PARAM; + } + break; + case VCO_EXTERNAL: + fprintf(stderr, "ERROR: Unexpected use of external VCO\n"); + if (freq >= MIN_RF_FREQ_EXT_VCO_RFDIV0 + && freq <= MAX_RF_FREQ_EXT_VCO_RFDIV0) { + rfdiv = AX5043_RFDIV0; + } else if (freq >= MIN_RF_FREQ_EXT_VCO_RFDIV1 + && freq <= MAX_RF_FREQ_EXT_VCO_RFDIV1) { + rfdiv = AX5043_RFDIV1; + } else { + return -PQWS_INVALID_PARAM; + } + break; + default: + return -PQWS_INVALID_PARAM; + } + prev_freq = conf->tx_freq; + pllcodediv = rfdiv | (uint8_t) (conf->vco << 4); + + // Added by Jonathan Brandenburg + // Have an external inductor + pllcodediv |= BIT(5); + + ret = ax5043_spi_write_8(conf, AX5043_REG_PLLVCODIV, pllcodediv); + if (ret) { + return ret; + } + + /* Write properly the F34 performance register based on the RFDIV*/ + if (rfdiv == AX5043_RFDIV1) { + ret = ax5043_spi_write_8(conf, 0xF34, 0x28); + } else { + ret = ax5043_spi_write_8(conf, 0xF34, 0x08); + } + if (ret) { + return ret; + } + + /* + * Set the RF frequency + * Frequency should be avoided to be a multiple integer of the crystal + * frequency, so we always set to 1 the LSB + */ + reg_val = ((uint32_t) (((float) freq / (float) conf->f_xtal) * (1 << 24)) + | 0x1); + if (conf->freqsel == FREQA_MODE) { + ret = ax5043_spi_write_32(conf, AX5043_REG_FREQA3, reg_val); + } else { + ret = ax5043_spi_write_32(conf, AX5043_REG_FREQB3, reg_val); + } + if (ret) { + return ret; + } + + /* Considered that the frequency successfully changed */ + conf->tx_freq = freq; + + /* If the frequency difference is great enough perform autoranging */ + if (freq + 25000000 > prev_freq || freq - 25000000 < prev_freq) { + ax5043_autoranging(conf); + } + + return PQWS_SUCCESS; +} + +/** + * Set the TX baudrate + * @param conf the AX5043 configuration handler + * @param baud the baudrate + * @return 0 on success or negative error code + */ +int ax5043_set_tx_baud(ax5043_conf_t *conf, uint32_t baud) { + int ret = PQWS_SUCCESS; + uint32_t val; + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + val = (uint32_t) ((((float) baud) / (float) conf->f_xtal) * (1 << 24)) + | 0x1; + ret = ax5043_spi_write_24(conf, AX5043_REG_TXRATE2, val); + if (ret) { + return ret; + } + + conf->tx_baudrate = baud; + + /* Set the deviation to standard 3 KHz for FM */ + // For AFSK, FSKDEV = 0.858785 * fDeviation / fXTAL * 2^24 + // + val = (uint32_t) ((0.858785 * 3000.0f / (float) conf->f_xtal) * (1 << 24)) + | 0x1; + ret = ax5043_spi_write_24(conf, AX5043_REG_FSKDEV2, val); + if (ret) { + return ret; + } + + return PQWS_SUCCESS; +} + +/** + * Sets the currently used frequency registers (A or B) + * @param conf the AX5043 configuration handler + * @param f the frequency mode (A or B) + * @return 0 on success or appropriate negative error code + */ +int ax5043_freqsel(ax5043_conf_t *conf, freq_mode_t f) { + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + if (f != FREQA_MODE && f != FREQB_MODE) { + return -PQWS_INVALID_PARAM; + } + conf->freqsel = f; + return PQWS_SUCCESS; +} + +/** + * Sets the TX frequency synthesizer related configuration registers. + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +int ax5043_set_tx_synth(ax5043_conf_t *conf) { + int ret; + uint8_t val; + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + switch (conf->freqsel) { + case FREQA_MODE: + val = 0x0; + break; + case FREQB_MODE: + val = 1 << 7; + break; + default: + return -PQWS_INVALID_PARAM; + } + + /* Bypass external filter and use 100 kHZ loop bandwidth */ + val |= BIT(3) | BIT(0); + ret = ax5043_spi_write_8(conf, AX5043_REG_PLLLOOP, val); + if (ret) { + return ret; + } + + /* + * Set the charge pump current based on the loop bandwidth + * 68 uA @ 100 kHZ + */ + ret = ax5043_spi_write_8(conf, AX5043_REG_PLLCPI, (uint8_t) (68 / 8.5)); + if (ret) { + return ret; + } + ret = ax5043_spi_write_8(conf, AX5043_REG_XTALCAP, 0); + return ret; +} + +/** + * Sets the PLL related configuration registers. + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +int ax5043_set_pll_params(ax5043_conf_t *conf) { + int ret; + uint8_t i = 8; + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + /* Set VCO to manual */ + ret = ax5043_spi_write_8(conf, AX5043_REG_PLLVCOI, + AX5043_PLLVCOI_MANUAL | (1250 / 50)); + if (ret) { + return ret; + } + + /* + * According to the manual PLL ranging clock should be less than 1/10 + * of the PLL loop bandwidth. The smallest PLL bandwidth configuration + * is 100 kHz. + */ + // This this next line contains an error + //while (conf->f_xtal / (uint32_t) (1 << i) > 10000) { + while (conf->f_xtal / (uint32_t) (1 << i) > 100000) { + i++; + } + i = i > 15 ? 15 : i; + i = i < 8 ? 8 : i; + ret = ax5043_spi_write_8(conf, AX5043_REG_PLLRNGCLK, (uint8_t) (i - 8)); + return ret; +} + +/** + * Performs auto-ranging using the frequency registers configured by + * ax5043_freqsel(). + * + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +int ax5043_autoranging(ax5043_conf_t *conf) { + int ret = PQWS_SUCCESS; + uint16_t pllranging_reg; + uint8_t val = 0; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + switch (conf->freqsel) { + case FREQA_MODE: + pllranging_reg = AX5043_REG_PLLRANGINGA; + break; + case FREQB_MODE: + pllranging_reg = AX5043_REG_PLLRANGINGB; + break; + default: + return -PQWS_INVALID_PARAM; + } + + /* Write the initial VCO setting and start autoranging */ + val = BIT(4) | AX5043_VCOR_INIT; + ret = ax5043_spi_write_8(conf, pllranging_reg, val); + if (ret) { + return ret; + } + + usleep(10); + val = 0; + /* Wait until the autoranging is complete */ + while ((val & BIT(4)) == 0) { + ret = ax5043_spi_read_8(conf, &val, pllranging_reg); + if (ret) { + return ret; + } + } + + if (val & BIT(5)) { + return -PQWS_AX5043_AUTORANGING_ERROR; + } + + return PQWS_SUCCESS; +} + +/** + * + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +int ax5043_aprs_framing_setup(ax5043_conf_t *conf) { + int ret = PQWS_SUCCESS; + uint8_t val = 0; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + /* Set modulation */ + val = AX5043_MODULATION_AFSK; + ret = ax5043_spi_write_8(conf, AX5043_REG_MODULATION, val); + if (ret) { + return ret; + } + + // To set the space frequency, 1070 + // 1070 * 2^18 / fXTAL -> 1070 * 2^18 / 16000000 -> 18 -> 0x12 + + // To set the space frequency, 1200 + // 1200 * 2^18 / fXTAL -> 1200 * 2^18 / 16000000 -> 20 -> 0x14 + + ret = ax5043_spi_write_16(conf, AX5043_REG_AFSKSPACE1, 0x24); + if (ret) { + return ret; + } + + // To set the mark frequency, 1270 + // 1270 * 2^18 / fXTAL -> 1270 * 2^18 / 16000000 -> 21 -> 0x15 + + // To set the mark frequency, 1400 + // 1400 * 2^18 / fXTAL -> 1400 * 2^18 / 16000000 -> 23 -> 0x17 + + ret = ax5043_spi_write_16(conf, AX5043_REG_AFSKMARK1, 0x14); + if (ret) { + return ret; + } + + /* + * As we do not use any external filter, try to filter from + * the AX5043 the signal + */ + ret = ax5043_spi_write_8(conf, AX5043_REG_MODCFGF, + AX5043_FREQSHAPE_GAUSSIAN_BT_05); + if (ret) { + return ret; + } + + /* Set HDLC encoding: Differential = 1, Inverse = 1, Scrambling = 1 */ + //ax5043_spi_write_8(conf, AX5043_REG_ENCODING, + /* Set HDLC encoding: Differential = 1, Inverse = 1, Scrambling = 1 */ + ax5043_spi_write_8(conf, AX5043_REG_ENCODING, + AX5043_ENC_DIFF | AX5043_ENC_INV); + + /* HDLC framing */ + ax5043_spi_write_8(conf, AX5043_REG_FRAMING, + AX5043_HDLC_FRAMING | AX5043_CRC16_CCITT); + return ret; +} + +static int __tx_frame_end(ax5043_conf_t *conf) { + int ret; + + ax5043_enable_pwramp(conf, AX5043_EXT_PA_DISABLE); + + /* Set AX5043 to power down mode */ + ret = ax5043_set_power_mode(conf, POWERDOWN); + __tx_active = 0; + return ret; +} + +static int __tx_frame(ax5043_conf_t *conf, const uint8_t *in, uint32_t len, + uint8_t preamble_len, uint8_t postamble_len, uint32_t timeout_ms) { + int ret = PQWS_SUCCESS; + uint8_t data_cmd[3] = { AX5043_FIFO_VARIABLE_DATA_CMD, 0, 0 }; + size_t chunk_size = 0; + size_t avail; + uint8_t val; + uint32_t start = millis(); + + /* + * Apply preamble and postamble repetition length. Rest of the fields should + * remain unaltered + */ + __preamble_cmd[2] = preamble_len; + __postamble_cmd[2] = postamble_len; + + memcpy(__tx_fifo_chunk, __preamble_cmd, sizeof(__preamble_cmd)); + chunk_size = sizeof(__preamble_cmd); + __tx_buf_idx = 0; + + /* + * Always leave some space for the postamble. This greatly reduces the + * complexity of dealing with some corner cases + */ + avail = min_ul( + AX5043_FIFO_MAX_SIZE - sizeof(__preamble_cmd) - sizeof(data_cmd) + - sizeof(__postamble_cmd), len); + if (len == avail) { + data_cmd[1] = (uint8_t) (len + 1); + data_cmd[2] = AX5043_FIFO_PKTEND; + __tx_remaining = 0; + memcpy(__tx_fifo_chunk + chunk_size, data_cmd, sizeof(data_cmd)); + chunk_size += sizeof(data_cmd); + memcpy(__tx_fifo_chunk + chunk_size, in, len); + chunk_size += len; + /* + * At this point we are sure that the whole frame + postamble can fit in + * the FIFO chunk + */ + memcpy(__tx_fifo_chunk + chunk_size, __postamble_cmd, + sizeof(__postamble_cmd)); + chunk_size += sizeof(__postamble_cmd); + single_fifo_access = 1; + } else { + data_cmd[1] = (uint8_t) (avail + 1); + data_cmd[2] = 0; + memcpy(__tx_fifo_chunk + chunk_size, data_cmd, sizeof(data_cmd)); + chunk_size += sizeof(data_cmd); + memcpy(__tx_fifo_chunk + chunk_size, in, avail); + chunk_size += avail; + + memcpy(__tx_buf, in + avail, len - avail); + __tx_remaining = (uint32_t) (len - avail); + single_fifo_access = 0; + } + + /* Set AX5043 to FULLTX mode */ + ret = ax5043_set_power_mode(conf, FULLTX); + if (ret) { + return ret; + } + + ax5043_spi_wait_xtal(conf, 100); + + /* Wait for the FIFO to become ready */ + val = 0; + while (!val) { + ax5043_spi_read_8(conf, &val, AX5043_REG_POWSTAT); + /* Select only the modem power state */ + val &= AX5043_SVMODEM; + if (millis() - start > timeout_ms) { + ret = -PQWS_TIMEOUT; + break; + } + } + + /* Fire-up the first data to the FIFO */ + ret = ax5043_spi_write(conf, AX5043_REG_FIFODATA, __tx_fifo_chunk, + (uint32_t) chunk_size); + if (ret) { + return ret; + } + __tx_active = 1; + /* Commit to FIFO ! */ + ret = ax5043_spi_write_8(conf, AX5043_REG_FIFOSTAT, AX5043_FIFO_COMMIT_CMD); + + return ret; +} + +int ax5043_tx_frame(ax5043_conf_t *conf, const uint8_t *in, uint32_t len, + uint8_t preamble_len, uint8_t postamble_len, uint32_t timeout_ms) { + int ret = 0; + + /* Wait for the previous frame to be transmitted */ + while (__tx_active) { + ret++; + } + + ret = ax5043_enable_pwramp(conf, AX5043_EXT_PA_ENABLE); + if (ret) { + return ret; + } + + ret = __tx_frame(conf, in, len, preamble_len, postamble_len, timeout_ms); + return ret; +} + +/** + * Wait the crystal to become ready + * @param conf the AX5043 configuration handler + * @param timeout_ms the timeout in milliseconds + * @return 0 on success or appropriate negative error code + */ +int ax5043_spi_wait_xtal(ax5043_conf_t *conf, uint32_t timeout_ms) { + int ret; + uint8_t val = 0x0; + uint32_t start = millis(); + + while (!val) { + ret = ax5043_spi_read_8(conf, &val, AX5043_REG_XTALSTATUS); + if (ret) { + return ret; + } + if ((millis() - start) > timeout_ms) { + return -PQWS_TIMEOUT; + } + } + return 0; +} + +int ax5043_spi_read_8(ax5043_conf_t *conf, uint8_t *out, uint16_t reg) { + int ret = PQWS_SUCCESS; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + *out = ax5043ReadReg(reg); + + return ret; +} + +int ax5043_spi_read_16(ax5043_conf_t *conf, uint16_t *out, uint16_t reg) { + int ret = PQWS_SUCCESS; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + *out = ax5043ReadReg2(reg); + + return ret; +} + +int ax5043_spi_read_24(ax5043_conf_t *conf, uint32_t *out, uint16_t reg) { + int ret = PQWS_SUCCESS; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + *out = ax5043ReadReg3(reg); + + return ret; +} + +int ax5043_spi_read_32(ax5043_conf_t *conf, uint32_t *out, uint16_t reg) { + int ret = PQWS_SUCCESS; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + *out = ax5043ReadReg4(reg); + + return ret; +} + +int ax5043_spi_write(ax5043_conf_t *conf, uint16_t reg, const uint8_t *in, + uint32_t len) { + int ret = PQWS_SUCCESS; + + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + ax5043WriteRegN(reg, in, len); + + return ret; +} + +int ax5043_spi_write_8(ax5043_conf_t *conf, uint16_t reg, uint8_t in) { + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + //printf("Reg\t%04x\t%02x\n", reg, in); + + ax5043WriteReg(reg, in); + + return PQWS_SUCCESS; +} + +int ax5043_spi_write_16(ax5043_conf_t *conf, uint16_t reg, uint16_t in) { + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + //printf("Reg\t%04x\t%02x\n", reg, (in >> 8)&0xFF); + //printf("Reg\t%04x\t%02x\n", reg+1, (in >> 0)&0xFF); + + ax5043WriteReg2(reg, in); + + return PQWS_SUCCESS; +} + +int ax5043_spi_write_24(ax5043_conf_t *conf, uint16_t reg, uint32_t in) { + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + //printf("Reg\t%04x\t%02x\n", reg, (in >> 16)&0xFF); + //printf("Reg\t%04x\t%02x\n", reg+1, (in >> 8)&0xFF); + //printf("Reg\t%04x\t%02x\n", reg+2, (in >> 0)&0xFF); + + ax5043WriteReg3(reg, in); + + return PQWS_SUCCESS; +} + +int ax5043_spi_write_32(ax5043_conf_t *conf, uint16_t reg, uint32_t in) { + if (!is_ax5043_conf_valid(conf)) { + return -PQWS_INVALID_PARAM; + } + + //printf("Reg\t%04x\t%02x\n", reg, (in >> 24)&0xFF); + //printf("Reg\t%04x\t%02x\n", reg+1, (in >> 16)&0xFF); + //printf("Reg\t%04x\t%02x\n", reg+2, (in >> 8)&0xFF); + //printf("Reg\t%04x\t%02x\n", reg+3, (in >> 0)&0xFF); + + ax5043WriteReg4(reg, in); + + return PQWS_SUCCESS; +} + +/** + * Sets properly some undocumented TX registers + * @param conf the AX5043 configuration handler + * @return 0 on success or appropriate negative error code + */ +static inline int set_tx_black_magic_regs(ax5043_conf_t *conf) { + int ret; + ret = ax5043_spi_write_8(conf, 0xF00, 0x0F); + if (ret) { + return ret; + } + + ret = ax5043_spi_write_8(conf, 0xF0C, 0x0); + if (ret) { + return ret; + } + + // Added by Jonathan Brandenburg + ret = ax5043_spi_write_8(conf, 0xF0D, 0x03); + if (ret) { + return ret; + } + + // Added by Jonathan Brandenburg + ret = ax5043_spi_write_8(conf, 0xF10, 0x03); + if (ret) { + return ret; + } + + // The following line is used for a TCXO + //ret = ax5043_spi_write_8(conf, 0xF11, 0x0); + // The following line is used for a crystal + ret = ax5043_spi_write_8(conf, 0xF11, 0x07); + if (ret) { + return ret; + } + + ret = ax5043_spi_write_8(conf, 0xF1C, 0x07); + if (ret) { + return ret; + } + + ret = ax5043_spi_write_8(conf, 0xF44, 0x24); + if (ret) { + return ret; + } + + /* Dafuq? Got it from RadioLab */ + ret = ax5043_spi_write_8(conf, 0xF18, 0x06); + return ret; +} + +/** + * Enables/Disables the power amplifier pin + * @param conf the AX5043 configuration handler + * @param enable 1 to enable 0 to disable + * @return 0 on success or appropriate negative error code + */ +int ax5043_enable_pwramp(ax5043_conf_t *conf, uint8_t enable) { + int ret; + ax5043_set_antsel(conf, enable); + ret = ax5043_spi_write_8(conf, AX5043_REG_PWRAMP, ~enable & 0x1); + + if (ret) { + usleep(PWRAMP_RAMP_PERIOD_US); + } + return ret; +} + +int ax5043_set_antsel(ax5043_conf_t *conf, uint8_t val) { + return ax5043_spi_write_8(conf, AX5043_REG_PINFUNCANTSEL, val & 0x1); +} + +/** + * Wait for the AX5043 to finish transmitting, putting new data in the FIFO as space becomes available + * @return 0 on success, or appropriate negative error code + */ +int ax5043_wait_for_transmit() { + if (!single_fifo_access) { + while (__tx_active) { + static int transmittedPostamble = 0; + + //usleep(100); + + int ret; + uint8_t data_cmd[3] = { AX5043_FIFO_VARIABLE_DATA_CMD, 0, 0 }; + size_t avail; + size_t chunk_size; + + if (!__ax5043_conf) { + return -PQWS_INVALID_PARAM; + } + + /* Determine if TX is done */ + uint8_t radiostate = 0; + ret = ax5043_spi_read_8(__ax5043_conf, &radiostate, + AX5043_REG_RADIOSTATE); + if (ret) { + return ret; + } + radiostate &= 0x0f; + + if (radiostate == 0) { + /* tx is done */ + __tx_frame_end(__ax5043_conf); + transmittedPostamble = 0; + printf("INFO: TX done\n"); + return PQWS_SUCCESS; + } + + /* Determine FIFO free space */ + uint16_t fifofree = 0; + ret = ax5043_spi_read_16(__ax5043_conf, &fifofree, + AX5043_REG_FIFOFREE1); + if (ret) { + return ret; + } + + /* If FIFO has free space fill in data */ + if (fifofree > AX5043_FIFO_FREE_THR && (__tx_remaining || !transmittedPostamble)) { + + /* Always left some space for the postamble for a simplified logic */ + avail = min_ul( + AX5043_FIFO_FREE_THR - sizeof(data_cmd) - sizeof(__postamble_cmd), + __tx_remaining); + + data_cmd[1] = (uint8_t) (avail + 1); + chunk_size = sizeof(data_cmd) + avail; + memcpy(__tx_fifo_chunk + sizeof(data_cmd), __tx_buf + __tx_buf_idx, + avail); + + if (avail == __tx_remaining) { + transmittedPostamble = 1; + + data_cmd[2] = AX5043_FIFO_PKTEND; + memcpy(__tx_fifo_chunk + chunk_size, __postamble_cmd, + sizeof(__postamble_cmd)); + chunk_size += sizeof(__postamble_cmd); + } + memcpy(__tx_fifo_chunk, data_cmd, sizeof(data_cmd)); + ax5043_spi_write(__ax5043_conf, AX5043_REG_FIFODATA, + __tx_fifo_chunk, (uint32_t) chunk_size); + /* Commit to FIFO ! */ + ret = ax5043_spi_write_8(__ax5043_conf, AX5043_REG_FIFOSTAT, + AX5043_FIFO_COMMIT_CMD); + + __tx_remaining -= (uint32_t) avail; + __tx_buf_idx += avail; + + } + } + } else { + while (__tx_active) { + int ret; + /* Determine if TX is done */ + uint8_t radiostate = 0; + ret = ax5043_spi_read_8(__ax5043_conf, &radiostate, + AX5043_REG_RADIOSTATE); + if (ret) { + return ret; + } + radiostate &= 0x0f; + if (radiostate == 0) { + /* tx is done */ + __tx_active = 0; + printf("INFO: TX done\n"); + } + } + } + + return PQWS_SUCCESS; +} diff --git a/afsk/ax5043.h b/afsk/ax5043.h new file mode 100644 index 00000000..855466cc --- /dev/null +++ b/afsk/ax5043.h @@ -0,0 +1,582 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Libre Space Foundation + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef AX5043_H_ +#define AX5043_H_ + +#include + +#define FREQUENCY_OFFSET -80000 + +#define APRS_VHF 440390000 + +/****************************************************************************** + ************************* RF Configuration *********************************** + *****************************************************************************/ +#define RX_FREQ_HZ (APRS_VHF + FREQUENCY_OFFSET) +#define TX_FREQ_HZ (APRS_VHF + FREQUENCY_OFFSET) + +/* Reference Oscillator frequency */ +#define XTAL_FREQ_HZ 16000000 + +/** + * The maximum allowed frame size + */ +#define MAX_FRAME_LEN 1024 + +#define RX_BAUDRATE 1200 +#define TX_BAUDRATE 1200 + +#define MIN_RF_FREQ_INT_VCO_RFDIV0 800000000 +#define MAX_RF_FREQ_INT_VCO_RFDIV0 1050000000 + +#define MIN_RF_FREQ_INT_VCO_RFDIV1 (MIN_RF_FREQ_INT_VCO_RFDIV0 / 2) +#define MAX_RF_FREQ_INT_VCO_RFDIV1 (MAX_RF_FREQ_INT_VCO_RFDIV0 / 2) + +#define MIN_RF_FREQ_EXT_VCO_RFDIV0 54000000 +#define MAX_RF_FREQ_EXT_VCO_RFDIV0 525000000 + +#define MIN_RF_FREQ_EXT_VCO_RFDIV1 (MIN_RF_FREQ_EXT_VCO_RFDIV0 / 2) +#define MAX_RF_FREQ_EXT_VCO_RFDIV1 (MAX_RF_FREQ_EXT_VCO_RFDIV0 / 2) + +/** + * Ramp up/Ramp down period of the power amplifier in microseconds + */ +#define PWRAMP_RAMP_PERIOD_US 200 + +/****************************************************************************** + ******************** AX5043 control SPI registers *************************** + *****************************************************************************/ + +/* Status and test registers */ +#define AX5043_REG_REV 0x0 +#define AX5043_REG_SCRATCH 0x1 + +/* Power and voltage regulator */ +#define AX5043_REG_PWRMODE 0x2 +#define AX5043_REG_POWSTAT 0x3 +#define AX5043_REG_POWSTICKYSTAT 0x4 +#define AX5043_REG_POWIRQMASK 0x5 + +/* Interrupt control */ +#define AX5043_REG_IRQMASK1 0x6 +#define AX5043_REG_IRQMASK0 0x7 +#define AX5043_REG_RADIOEVENTMASK1 0x8 +#define AX5043_REG_RADIOEVENTMASK0 0x9 + +#define AX5043_REG_IRQREQUEST1 0xC +#define AX5043_REG_IRQREQUEST0 0xD +#define AX5043_REG_RADIOEVENTREQ1 0xE +#define AX5043_REG_RADIOEVENTREQ0 0xF + +/* Modulation and framing */ +#define AX5043_REG_MODULATION 0x010 +#define AX5043_REG_ENCODING 0x011 +#define AX5043_REG_FRAMING 0x012 +#define AX5043_REG_CRCINIT3 0x014 +#define AX5043_REG_CRCINIT2 0x015 +#define AX5043_REG_CRCINIT1 0x016 +#define AX5043_REG_CRCINIT0 0x017 + +/* FEC */ +#define AX5043_REG_FEC 0x018 +#define AX5043_REG_FECSYNC 0x019 +#define AX5043_REG_FECSTATUS 0x01A + +/* Status */ +#define AX5043_REG_RADIOSTATE 0x01C +#define AX5043_REG_XTALSTATUS 0x01D + +/* Pin configuration */ +#define AX5043_REG_PINSTATE 0x20 +#define AX5043_REG_PINFUNCSYSCLK 0x21 +#define AX5043_REG_PINFUNCDCLK 0x22 +#define AX5043_REG_PINFUNCDATA 0x23 +#define AX5043_REG_PINFUNCIRQ 0x24 +#define AX5043_REG_PINFUNCANTSEL 0x25 +#define AX5043_REG_PINFUNCPWRAMP 0x26 +#define AX5043_REG_PWRAMP 0x27 + +/* FIFO control */ +#define AX5043_REG_FIFOSTAT 0x28 +#define AX5043_REG_FIFODATA 0x29 +#define AX5043_REG_FIFOCOUNT1 0x2A +#define AX5043_REG_FIFOCOUNT0 0x2B +#define AX5043_REG_FIFOFREE1 0x2C +#define AX5043_REG_FIFOFREE0 0x2D +#define AX5043_REG_FIFOTHRESH1 0x2E +#define AX5043_REG_FIFOTHRESH0 0x2F + +/* Frequency Synthesizer */ +#define AX5043_REG_PLLLOOP 0x30 +#define AX5043_REG_PLLCPI 0x31 +#define AX5043_REG_PLLVCODIV 0x32 +#define AX5043_REG_PLLRANGINGA 0x33 +#define AX5043_REG_FREQA3 0x34 +#define AX5043_REG_FREQA2 0x35 +#define AX5043_REG_FREQA1 0x36 +#define AX5043_REG_FREQA0 0x37 +#define AX5043_REG_PLLLOOPBOOST 0x38 +#define AX5043_REG_PLLCPIBOOST 0x39 +#define AX5043_REG_PLLRANGINGB 0x3B +#define AX5043_REG_FREQB3 0x3C +#define AX5043_REG_FREQB2 0x3D +#define AX5043_REG_FREQB1 0x3E +#define AX5043_REG_FREQB0 0x3F + +/* RSSI */ +#define AX5043_REG_RSSI 0x40 +#define AX5043_REG_BGNDRSSI 0x41 +#define AX5043_REG_DIVERSITY 0x42 +#define AX5043_REG_AGCCOUNTER 0x43 + +/* Receiver Tracking */ +#define AX5043_REG_TRKDATARATE2 0x45 +#define AX5043_REG_TRKDATARATE1 0x46 +#define AX5043_REG_TRKDATARATE0 0x47 +#define AX5043_REG_TRKAMPL1 0x48 +#define AX5043_REG_TRKAMPL0 0x49 +#define AX5043_REG_TRKPHASE1 0x4A +#define AX5043_REG_TRKPHASE0 0x4B +#define AX5043_REG_TRKRFFREQ2 0x4D +#define AX5043_REG_TRKRFFREQ1 0x4E +#define AX5043_REG_TRKRFFREQ0 0x4F +#define AX5043_REG_TRKFREQ1 0x50 +#define AX5043_REG_TRKFREQ0 0x51 +#define AX5043_REG_TRKFSKDEMOD1 0x52 +#define AX5043_REG_TRKFSKDEMOD0 0x53 + +/* Timers */ +#define AX5043_REG_TIMER2 0x59 +#define AX5043_REG_TIMER1 0x5A +#define AX5043_REG_TIMER0 0x5B + +/* Wakeup timer */ +#define AX5043_REG_WAKEUPTIMER1 0x68 +#define AX5043_REG_WAKEUPTIMER0 0x69 +#define AX5043_REG_WAKEUP1 0x6A +#define AX5043_REG_WAKEUP0 0x6B +#define AX5043_REG_WAKEUPFREQ1 0x6C +#define AX5043_REG_WAKEUPFREQ0 0x6D +#define AX5043_REG_WAKEUPXOEARLY 0x6E + +/* PHY related registers*/ +#define AX5043_REG_IFFREQ1 0x100 +#define AX5043_REG_IFFREQ0 0x101 +#define AX5043_REG_DECIMATION 0x102 +#define AX5043_REG_RXDATARATE2 0x103 +#define AX5043_REG_RXDATARATE1 0x104 +#define AX5043_REG_RXDATARATE0 0x105 +#define AX5043_REG_MAXDROFFSET2 0x106 +#define AX5043_REG_MAXDROFFSET1 0x107 +#define AX5043_REG_MAXDROFFSET0 0x108 +#define AX5043_REG_MAXRFOFFSET2 0x109 +#define AX5043_REG_MAXRFOFFSET1 0x10A +#define AX5043_REG_MAXRFOFFSET0 0x10B +#define AX5043_REG_FSKDMAX1 0x10C +#define AX5043_REG_FSKDMAX0 0x10D +#define AX5043_REG_FSKDMIN1 0x10E +#define AX5043_REG_FSKDMIN0 0x10F +#define AX5043_REG_AFSKSPACE1 0x110 +#define AX5043_REG_AFSKSPACE0 0x111 +#define AX5043_REG_AFSKMARK1 0x112 +#define AX5043_REG_AFSKMARK0 0x113 +#define AX5043_REG_AFSKCTRL 0x114 +#define AX5043_REG_AMPLFILTER 0x115 +#define AX5043_REG_FREQUENCYLEAK 0x116 +#define AX5043_REG_RXPARAMSETS 0x117 +#define AX5043_REG_RXPARAMCURSET 0x118 + +/* Receiver Parameter Set 0 */ +#define AX5043_REG_AGCGAIN0 0x120 +#define AX5043_REG_AGCTARGET0 0x121 +#define AX5043_REG_AGCAHYST0 0x122 +#define AX5043_REG_AGCMINMAX0 0x123 +#define AX5043_REG_TIMEGAIN0 0x124 +#define AX5043_REG_DRGAIN0 0x125 +#define AX5043_REG_PHASEGAIN0 0x126 +#define AX5043_REG_FREQGAINA0 0x127 +#define AX5043_REG_FREQGAINB0 0x128 +#define AX5043_REG_FREQGAINC0 0x129 +#define AX5043_REG_FREQGAIND0 0x12A +#define AX5043_REG_AMPLGAIN0 0x12B +#define AX5043_REG_FREQDEV10 0x12C +#define AX5043_REG_FREQDEV00 0x12D +#define AX5043_REG_FOURFSK0 0x12E +#define AX5043_REG_BBOFFSRES0 0x12F + +/* Receiver Parameter Set 1 */ +#define AX5043_REG_AGCGAIN1 0x130 +#define AX5043_REG_AGCTARGET1 0x131 +#define AX5043_REG_AGCAHYST1 0x132 +#define AX5043_REG_AGCMINMAX1 0x133 +#define AX5043_REG_TIMEGAIN1 0x134 +#define AX5043_REG_DRGAIN1 0x135 +#define AX5043_REG_PHASEGAIN1 0x136 +#define AX5043_REG_FREQGAINA1 0x137 +#define AX5043_REG_FREQGAINB1 0x138 +#define AX5043_REG_FREQGAINC1 0x139 +#define AX5043_REG_FREQGAIND1 0x13A +#define AX5043_REG_AMPLGAIN1 0x13B +#define AX5043_REG_FREQDEV11 0x13C +#define AX5043_REG_FREQDEV01 0x13D +#define AX5043_REG_FOURFSK1 0x13E +#define AX5043_REG_BBOFFSRES1 0x13F + +/* Receiver Parameter Set 2 */ +#define AX5043_REG_AGCGAIN2 0x140 +#define AX5043_REG_AGCTARGET2 0x141 +#define AX5043_REG_AGCAHYST2 0x142 +#define AX5043_REG_AGCMINMAX2 0x143 +#define AX5043_REG_TIMEGAIN2 0x144 +#define AX5043_REG_DRGAIN2 0x145 +#define AX5043_REG_PHASEGAIN2 0x146 +#define AX5043_REG_FREQGAINA2 0x147 +#define AX5043_REG_FREQGAINB2 0x148 +#define AX5043_REG_FREQGAINC2 0x149 +#define AX5043_REG_FREQGAIND2 0x14A +#define AX5043_REG_AMPLGAIN2 0x14B +#define AX5043_REG_FREQDEV12 0x14C +#define AX5043_REG_FREQDEV02 0x14D +#define AX5043_REG_FOURFSK2 0x14E +#define AX5043_REG_BBOFFSRES2 0x14F + +/* Receiver Parameter Set 3 */ +#define AX5043_REG_AGCGAIN3 0x150 +#define AX5043_REG_AGCTARGET3 0x151 +#define AX5043_REG_AGCAHYST3 0x152 +#define AX5043_REG_AGCMINMAX3 0x153 +#define AX5043_REG_TIMEGAIN3 0x154 +#define AX5043_REG_DRGAIN3 0x155 +#define AX5043_REG_PHASEGAIN3 0x156 +#define AX5043_REG_FREQGAINA3 0x157 +#define AX5043_REG_FREQGAINB3 0x158 +#define AX5043_REG_FREQGAINC3 0x159 +#define AX5043_REG_FREQGAIND3 0x15A +#define AX5043_REG_AMPLGAIN3 0x15B +#define AX5043_REG_FREQDEV13 0x15C +#define AX5043_REG_FREQDEV03 0x15D +#define AX5043_REG_FOURFSK3 0x15E +#define AX5043_REG_BBOFFSRES3 0x15F + +/* Transmitter Parameters */ +#define AX5043_REG_MODCFGF 0x160 +#define AX5043_REG_FSKDEV2 0x161 +#define AX5043_REG_FSKDEV1 0x162 +#define AX5043_REG_FSKDEV0 0x163 +#define AX5043_REG_MODCFGA 0x164 +#define AX5043_REG_TXRATE2 0x165 +#define AX5043_REG_TXRATE1 0x166 +#define AX5043_REG_TXRATE0 0x167 +#define AX5043_REG_TXPWRCOEFFA1 0x168 +#define AX5043_REG_TXPWRCOEFFA0 0x169 +#define AX5043_REG_TXPWRCOEFFB1 0x16A +#define AX5043_REG_TXPWRCOEFFB0 0x16B +#define AX5043_REG_TXPWRCOEFFC1 0x16C +#define AX5043_REG_TXPWRCOEFFC0 0x16D +#define AX5043_REG_TXPWRCOEFFD1 0x16E +#define AX5043_REG_TXPWRCOEFFD0 0x16F +#define AX5043_REG_TXPWRCOEFFE1 0x170 +#define AX5043_REG_TXPWRCOEFFE0 0x171 + +/* PLL parameters */ +#define AX5043_REG_PLLVCOI 0x180 +#define AX5043_REG_PLLVCOIR 0x181 +#define AX5043_REG_PLLLOCKDET 0x182 +#define AX5043_REG_PLLRNGCLK 0x183 + +/* Crystal Oscillator */ +#define AX5043_REG_XTALCAP 0x184 + +/* Baseband */ +#define AX5043_REG_BBTUNE 0x188 +#define AX5043_REG_BBOFFSCAP 0x189 + +/* MAC parameters */ + +/* Packet Format */ +#define AX5043_REG_PKTADDRCFG 0x200 +#define AX5043_REG_PKTLENCFG 0x201 +#define AX5043_REG_PKTLENOFFSET 0x202 +#define AX5043_REG_PKTMAXLEN 0x203 +#define AX5043_REG_PKTADDR3 0x204 +#define AX5043_REG_PKTADDR2 0x205 +#define AX5043_REG_PKTADDR1 0x206 +#define AX5043_REG_PKTADDR0 0x207 +#define AX5043_REG_PKTADDRMASK3 0x208 +#define AX5043_REG_PKTADDRMASK2 0x209 +#define AX5043_REG_PKTADDRMASK1 0x20A +#define AX5043_REG_PKTADDRMASK0 0x20B + +/* Pattern Match */ +#define AX5043_REG_MATCH0PAT3 0x210 +#define AX5043_REG_MATCH0PAT2 0x211 +#define AX5043_REG_MATCH0PAT1 0x212 +#define AX5043_REG_MATCH0PAT0 0x213 +#define AX5043_REG_MATCH0LEN 0x214 +#define AX5043_REG_MATCH0MIN 0x215 +#define AX5043_REG_MATCH0MAX 0x216 +#define AX5043_REG_MATCH1PAT1 0x218 +#define AX5043_REG_MATCH1PAT0 0x219 +#define AX5043_REG_MATCH1LEN 0x21C +#define AX5043_REG_MATCH1MIN 0x21D +#define AX5043_REG_MATCH1MAX 0x21E + +/* Packet Controller */ +#define AX5043_REG_TMGTXBOOST 0x220 +#define AX5043_REG_TMGTXSETTLE 0x221 +#define AX5043_REG_TMGRXBOOST 0x223 +#define AX5043_REG_TMGRXSETTLE 0x224 +#define AX5043_REG_TMGRXOFFSACQ 0x225 +#define AX5043_REG_TMGRXCOARSEAGC 0x226 +#define AX5043_REG_TMGRXAGC 0x227 +#define AX5043_REG_TMGRXRSSI 0x228 +#define AX5043_REG_TMGRXPREAMBLE1 0x229 +#define AX5043_REG_TMGRXPREAMBLE2 0x22A +#define AX5043_REG_TMGRXPREAMBLE3 0x22B +#define AX5043_REG_RSSIREFERENCE 0x22C +#define AX5043_REG_RSSIABSTHR 0x22D +#define AX5043_REG_BGNDRSSIGAIN 0x22E +#define AX5043_REG_BGNDRSSITHR 0x22F +#define AX5043_REG_PKTCHUNKSIZE 0x230 +#define AX5043_REG_PKTMISCFLAGS 0x231 +#define AX5043_REG_PKTSTOREFLAGS 0x232 +#define AX5043_REG_PKTACCEPTFLAGS 0x233 + +/* Special Functions */ + +/* General Purpose ADC */ +#define AX5043_REG_GPADCCTRL 0x300 +#define AX5043_REG_GPADCPERIOD 0x301 +#define AX5043_REG_GPADC13VALUE1 0x308 +#define AX5043_REG_GPADC13VALUE0 0x309 + +/* Low Power Oscillator Calibration */ +#define AX5043_REG_LPOSCCONFIG 0x310 +#define AX5043_REG_LPOSCSTATUS 0x311 +#define AX5043_REG_LPOSCKFILT1 0x312 +#define AX5043_REG_LPOSCKFILT0 0x313 +#define AX5043_REG_LPOSCREF1 0x314 +#define AX5043_REG_LPOSCREF0 0x315 +#define AX5043_REG_LPOSCFREQ1 0x316 +#define AX5043_REG_LPOSCFREQ0 0x317 +#define AX5043_REG_LPOSCPER1 0x318 +#define AX5043_REG_LPOSCPER0 0x319 + +/* Performance Tuning Registers */ +#define AX5043_REG_XTALDIV 0xF35 + +/****************************************************************************** + ************************* Register values ************************************ + *****************************************************************************/ +#define AX5043_REV 0x51 +#define AX5043_SCRATCH_TEST 0xAA + +/* Power modes */ +#define AX5043_POWERDOWN 0x0 +#define AX5043_DEEPSLEEP BIT(0) +#define AX5043_STANDBY (BIT(2) | BIT(0)) +#define AX5043_FIFO_ENABLED (BIT(2) | BIT(1) |BIT(0)) +#define AX5043_RECEIVE_MODE (BIT(3)) +#define AX5043_RECEIVER_RUNNING (BIT(3) | BIT(0)) +#define AX5043_RECEIVER_WOR (BIT(3) | BIT(1) | BIT(0)) +#define AX5043_TRANSMIT_MODE (BIT(3) | BIT(2)) +#define AX5043_TRANSMIT_RUNNING (BIT(3) | BIT(2) | BIT(0)) +#define AX5043_FULLTX AX5043_TRANSMIT_RUNNING + +#define AX5043_PLLVCOI_MANUAL BIT(7) + +/** + * Modem Domain Voltage Regulator Ready + */ +#define AX5043_SVMODEM BIT(3) + +/** + * Init value for the VCO prior starting an autoranging + */ +#define AX5043_VCOR_INIT 8 + +#define AX5043_RFDIV0 0x0 +#define AX5043_RFDIV1 BIT(2) + +#define AX5043_FREQSHAPE_EXT_FILTER 0x0 +#define AX5043_FREQSHAPE_INVALID 0x1 +#define AX5043_FREQSHAPE_GAUSSIAN_BT_03 0x2 +#define AX5043_FREQSHAPE_GAUSSIAN_BT_05 0x3 + +/** + * FSK modulation mode + */ +#define AX5043_MODULATION_FSK BIT(3) + +/** + * AFSK modulation mode + */ +#define AX5043_MODULATION_AFSK (BIT(3)|BIT(1)) + +#define AX5043_ENC_INV BIT(0) +#define AX5043_ENC_DIFF BIT(1) +#define AX5043_ENC_SCRAM BIT(2) + +/** + * HDLC Framing mode + */ +#define AX5043_HDLC_FRAMING BIT(2) + +/** + * HDLC compliant CRC16 + */ +#define AX5043_CRC16_CCITT BIT(4) + +/** + * Set the FIFO to variable length data mode + */ +#define AX5043_FIFO_VARIABLE_DATA_CMD 0xe1 + +#define AX5043_FIFO_REPEATDATA_CMD (BIT(6) | BIT(5) | BIT(1)) + +/** + * FIFO commit command + */ +#define AX5043_FIFO_COMMIT_CMD BIT(2) +#define AX5043_FIFO_PKTSTART BIT(0) +#define AX5043_FIFO_PKTEND BIT(1) +#define AX5043_FIFO_NOCRC BIT(3) +#define AX5043_FIFO_RAW BIT(4) + +/** + * Maximum chuck that can be committed on the FIFO + */ +#define AX5043_PKTCHUNKSIZE_240 0xd + +#define AX5043_FIFO_MAX_SIZE 240 + +/** + * When this threshold of free bytes in the TX FIFO is reached, + * we'll put more data in the FIFO + */ +#define AX5043_FIFO_FREE_THR 128 + +/** + * TX antenna transmission mode + */ +#define AX5043_TX_SINGLE_ENDED BIT(1) +#define AX5043_TX_DIFFERENTIAL BIT(0) + +/** + * External PA Control + */ + +#define AX5043_EXT_PA_ENABLE 1 +#define AX5043_EXT_PA_DISABLE 0 + +/** + * Frequency mode A or B actually selects at which registers + * the frequency configuration should be written. + * + * This is quite handy for different RX/TX frequencies, to avoid + * writing every time the two different frequency configurations. + */ +typedef enum { + FREQA_MODE = 0, //!< FREQA_MODE + FREQB_MODE = 1 //!< FREQB_MODE +} freq_mode_t; + +typedef enum { + VCO_INTERNAL = 0, VCO_EXTERNAL = 1 +} vco_mode_t; + +typedef enum { + POWERDOWN, + DEEPSLEEP, + STANDBY, + FIFO_ENABLED, + RECEIVE_MODE, + RECEIVER_RUNNING, + RECEIVER_WOR, + TRANSMIT_MODE, + FULLTX +} power_mode_t; + +typedef struct { + uint32_t tx_freq; + uint32_t rx_freq; + uint32_t f_xtal; + uint8_t f_xtaldiv; + uint32_t tx_baudrate; + uint32_t rx_baudrate; + uint8_t rf_init; + freq_mode_t freqsel; + vco_mode_t vco; +} ax5043_conf_t; + +int ax5043_reset(ax5043_conf_t *conf); + +int ax5043_init(ax5043_conf_t *conf, uint32_t f_xtal, vco_mode_t vco); + +int ax5043_conf_tx_path(ax5043_conf_t *conf); + +int ax5043_set_tx_freq(ax5043_conf_t *conf, uint32_t freq); + +int ax5043_set_power_mode(ax5043_conf_t *conf, power_mode_t mode); + +int ax5043_set_tx_baud(ax5043_conf_t *conf, uint32_t baud); + +int ax5043_freqsel(ax5043_conf_t *conf, freq_mode_t f); + +int ax5043_set_tx_synth(ax5043_conf_t *conf); + +int ax5043_set_pll_params(ax5043_conf_t *conf); + +int ax5043_autoranging(ax5043_conf_t *conf); + +int ax5043_aprs_framing_setup(ax5043_conf_t *conf); + +int ax5043_tx_frame(ax5043_conf_t *conf, const uint8_t *in, uint32_t len, + uint8_t preamble_len, uint8_t postamble_len, uint32_t timeout_ms); + +int ax5043_spi_wait_xtal(ax5043_conf_t *conf, uint32_t timeout_ms); + +int ax5043_spi_read_8(ax5043_conf_t *conf, uint8_t *out, uint16_t reg); + +int ax5043_spi_read_16(ax5043_conf_t *conf, uint16_t *out, uint16_t reg); + +int ax5043_spi_read_24(ax5043_conf_t *conf, uint32_t *out, uint16_t reg); + +int ax5043_spi_read_32(ax5043_conf_t *conf, uint32_t *out, uint16_t reg); + +int ax5043_spi_write(ax5043_conf_t *conf, uint16_t reg, const uint8_t *in, + uint32_t len); + +int ax5043_spi_write_8(ax5043_conf_t *conf, uint16_t reg, uint8_t in); + +int ax5043_spi_write_16(ax5043_conf_t *conf, uint16_t reg, uint16_t in); + +int ax5043_spi_write_24(ax5043_conf_t *conf, uint16_t reg, uint32_t in); + +int ax5043_spi_write_32(ax5043_conf_t *conf, uint16_t reg, uint32_t in); + +int ax5043_enable_pwramp(ax5043_conf_t *conf, uint8_t enable); + +int ax5043_set_antsel(ax5043_conf_t *conf, uint8_t val); + +int ax5043_wait_for_transmit(); + +#endif /* AX5043_H_ */ diff --git a/afsk/main.c b/afsk/main.c new file mode 100644 index 00000000..a85269dc --- /dev/null +++ b/afsk/main.c @@ -0,0 +1,92 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "status.h" +#include "ax5043.h" +#include "ax25.h" +#include "spi/ax5043spi.h" + +ax5043_conf_t hax5043; +ax25_conf_t hax25; + +static void init_rf(); +void config_x25(); +void trans_x25(); + +int main(void) { + setSpiChannel(SPI_CHANNEL); + setSpiSpeed(SPI_SPEED); + initializeSpi(); + + int ret; + uint8_t data[1024]; + // 0x03 is a UI frame + // 0x0F is no Level 3 protocol + const char *str = "\x03\x0fThis is an AX.25 Packet from CubeSatSim!!!"; + + /* Infinite loop */ + for (;;) { + sleep(2); + + // send X.25 packet + + init_rf(); + + ax25_init(&hax25, (uint8_t *) "CQ", '2', (uint8_t *) "DX", '2', + AX25_PREAMBLE_LEN, + AX25_POSTAMBLE_LEN); + + + printf("INFO: Transmitting X.25 packet\n"); + + memcpy(data, str, strnlen(str, 256)); + ret = ax25_tx_frame(&hax25, &hax5043, data, strnlen(str, 256)); + if (ret) { + fprintf(stderr, + "ERROR: Failed to transmit AX.25 frame with error code %d\n", + ret); + exit(EXIT_FAILURE); + } + ax5043_wait_for_transmit(); + if (ret) { + fprintf(stderr, + "ERROR: Failed to transmit entire AX.25 frame with error code %d\n", + ret); + exit(EXIT_FAILURE); + } + + } + + return 0; +} + +static void init_rf() { + int ret; + ret = ax5043_init(&hax5043, XTAL_FREQ_HZ, VCO_INTERNAL); + if (ret != PQWS_SUCCESS) { + fprintf(stderr, + "ERROR: Failed to initialize AX5043 with error code %d\n", ret); + exit(EXIT_FAILURE); + } +} + diff --git a/afsk/main.c.bk b/afsk/main.c.bk new file mode 100644 index 00000000..a85269dc --- /dev/null +++ b/afsk/main.c.bk @@ -0,0 +1,92 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "status.h" +#include "ax5043.h" +#include "ax25.h" +#include "spi/ax5043spi.h" + +ax5043_conf_t hax5043; +ax25_conf_t hax25; + +static void init_rf(); +void config_x25(); +void trans_x25(); + +int main(void) { + setSpiChannel(SPI_CHANNEL); + setSpiSpeed(SPI_SPEED); + initializeSpi(); + + int ret; + uint8_t data[1024]; + // 0x03 is a UI frame + // 0x0F is no Level 3 protocol + const char *str = "\x03\x0fThis is an AX.25 Packet from CubeSatSim!!!"; + + /* Infinite loop */ + for (;;) { + sleep(2); + + // send X.25 packet + + init_rf(); + + ax25_init(&hax25, (uint8_t *) "CQ", '2', (uint8_t *) "DX", '2', + AX25_PREAMBLE_LEN, + AX25_POSTAMBLE_LEN); + + + printf("INFO: Transmitting X.25 packet\n"); + + memcpy(data, str, strnlen(str, 256)); + ret = ax25_tx_frame(&hax25, &hax5043, data, strnlen(str, 256)); + if (ret) { + fprintf(stderr, + "ERROR: Failed to transmit AX.25 frame with error code %d\n", + ret); + exit(EXIT_FAILURE); + } + ax5043_wait_for_transmit(); + if (ret) { + fprintf(stderr, + "ERROR: Failed to transmit entire AX.25 frame with error code %d\n", + ret); + exit(EXIT_FAILURE); + } + + } + + return 0; +} + +static void init_rf() { + int ret; + ret = ax5043_init(&hax5043, XTAL_FREQ_HZ, VCO_INTERNAL); + if (ret != PQWS_SUCCESS) { + fprintf(stderr, + "ERROR: Failed to initialize AX5043 with error code %d\n", ret); + exit(EXIT_FAILURE); + } +} + diff --git a/afsk/main.c.x25_only b/afsk/main.c.x25_only new file mode 100644 index 00000000..7a67a49c --- /dev/null +++ b/afsk/main.c.x25_only @@ -0,0 +1,85 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "status.h" +#include "ax5043.h" +#include "ax25.h" +#include "spi/ax5043spi.h" + +ax5043_conf_t hax5043; +ax25_conf_t hax25; + +static void init_rf(); + +int main(void) { + setSpiChannel(SPI_CHANNEL); + setSpiSpeed(SPI_SPEED); + initializeSpi(); + + init_rf(); + + ax25_init(&hax25, (uint8_t *) "CQ", '2', (uint8_t *) "DX", '2', + AX25_PREAMBLE_LEN, + AX25_POSTAMBLE_LEN); + + int ret; + uint8_t data[1024]; + // 0x03 is a UI frame + // 0x0F is no Level 3 protocol + const char *str = "\x03\x0fThis is an AX.25 Packet from CubeSatSim!!!"; + + /* Infinite loop */ + for (;;) { + sleep(2); + printf("INFO: Transmitting a packet\n"); + + memcpy(data, str, strnlen(str, 256)); + ret = ax25_tx_frame(&hax25, &hax5043, data, strnlen(str, 256)); + if (ret) { + fprintf(stderr, + "ERROR: Failed to transmit AX.25 frame with error code %d\n", + ret); + exit(EXIT_FAILURE); + } + ax5043_wait_for_transmit(); + if (ret) { + fprintf(stderr, + "ERROR: Failed to transmit entire AX.25 frame with error code %d\n", + ret); + exit(EXIT_FAILURE); + } + + } + + return 0; +} + +static void init_rf() { + int ret; + ret = ax5043_init(&hax5043, XTAL_FREQ_HZ, VCO_INTERNAL); + if (ret != PQWS_SUCCESS) { + fprintf(stderr, + "ERROR: Failed to initialize AX5043 with error code %d\n", ret); + exit(EXIT_FAILURE); + } +} diff --git a/afsk/status.h b/afsk/status.h new file mode 100644 index 00000000..9a212e96 --- /dev/null +++ b/afsk/status.h @@ -0,0 +1,37 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Libre Space Foundation + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef STATUS_H_ +#define STATUS_H_ + +/** + * Error codes of the PQWS. Users should return the negative of the error + * codes. NO_ERROR is set to zero. + */ +typedef enum { + PQWS_SUCCESS = 0, //!< All ok! + PQWS_INVALID_PARAM, //!< An invalid parameter was given + PQWS_MAX_SPI_TRANSFER_ERROR, //!< The requested SPI data transfer was larger than supported + PQWS_NO_RF_FOUND, //!< No suitable RF chip found + PQWS_AX5043_AUTORANGING_ERROR, //!< Auto ranging failed on AX5043 + PQWS_TIMEOUT //!< A timeout occurred +} pqws_error_t; + +#endif /* STATUS_H_ */ diff --git a/afsk/utils.h b/afsk/utils.h new file mode 100644 index 00000000..f35ee2d0 --- /dev/null +++ b/afsk/utils.h @@ -0,0 +1,54 @@ +/* + * A sample application transmitting AFSK at 1200 baud + * + * Portions Copyright (C) 2018 Libre Space Foundation + * Portions Copyright (C) 2018 Jonathan Brandenburg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef UTILS_H_ +#define UTILS_H_ + +#include + +#define BIT(x) (1 << x) + +static inline size_t min_ul(size_t x, size_t y) { + size_t ret = x < y ? x : y; + return ret; +} + +/** + * + * @return the milliseconds from the start of the program. + * Time is handled based on TIM2 timer. + */ +static inline uint32_t millis() { + static struct timeval tv_start; + static int tv_start_initialized = 0; + if (!tv_start_initialized) { + gettimeofday(&tv_start, NULL); + } + + struct timeval tv_current; + gettimeofday(&tv_current, NULL); + + uint32_t millis_elapsed = 0; + millis_elapsed = (uint32_t) (tv_current.tv_sec - tv_start.tv_sec) * 1000; + millis_elapsed += (uint32_t) (tv_current.tv_usec - tv_start.tv_usec) / 1000; + return millis_elapsed; +} + +#endif /* UTILS_H_ */