diff --git a/.gitignore b/.gitignore
index 2f7b9bb8..b2985253 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,11 +2,11 @@
*.a
radiochat
radiopiglatin
-testax5043
testax5043tx
testax5043rx
testax5043init
testax50432freq
+testafsktx
doc/html/
doc/latex/
/.metadata/
diff --git a/Makefile b/Makefile
index 3909727b..edfe8c48 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ all: testax5043rx
all: testax5043tx
all: testax50432freq
all: testax5043init
+all: testafsktx
rebuild: clean
rebuild: all
@@ -18,6 +19,7 @@ clean:
rm -f testax5043tx
rm -f testax50432freq
rm -f testax5043init
+ rm -f testafsktx
rm -f libax5043.a
rm -f */*.o
rm -f */*/*.o
@@ -67,6 +69,12 @@ testax50432freq: libax5043.a
testax50432freq: transmit2freq/transmit2freq_main.o
gcc -o testax50432freq -L./ transmit2freq/transmit2freq_main.o -lwiringPi -lax5043
+testafsktx: libax5043.a
+testafsktx: afsktx/ax25.o
+testafsktx: afsktx/ax5043.o
+testafsktx: afsktx/main.o
+ gcc -o testafsktx -L./ afsktx/ax25.o afsktx/ax5043.o afsktx/main.o -lwiringPi -lax5043
+
ax5043/generated/configcommon.o: ax5043/generated/configcommon.c
ax5043/generated/configcommon.o: ax5043/generated/configrx.h
ax5043/generated/configcommon.o: ax5043/generated/configtx.h
@@ -243,3 +251,24 @@ init/init_main.o: ax5043/spi/ax5043spi.h
init/init_main.o: ax5043/spi/ax5043spi_p.h
cd init; gcc -I ../ax5043 -pedantic -Wconversion -Wall -Wextra -c init_main.c; cd ..
+afsktx/ax25.o: afsktx/ax25.c
+afsktx/ax25.o: afsktx/ax25.h
+afsktx/ax25.o: afsktx/ax5043.h
+afsktx/ax25.o: afsktx/status.h
+ cd afsktx; gcc -I ../ax5043 -pedantic -Wconversion -Wall -Wextra -c ax25.c; cd ..
+
+afsktx/ax5043.o: afsktx/ax5043.c
+afsktx/ax5043.o: afsktx/ax25.h
+afsktx/ax5043.o: afsktx/ax5043.h
+afsktx/ax5043.o: afsktx/status.h
+afsktx/ax5043.o: afsktx/utils.h
+afsktx/ax5043.o: ax5043/spi/ax5043spi.h
+ cd afsktx; gcc -I ../ax5043 -pedantic -Wconversion -Wall -Wextra -c ax5043.c; cd ..
+
+afsktx/main.o: afsktx/main.c
+afsktx/main.o: afsktx/status.h
+afsktx/main.o: afsktx/ax5043.h
+afsktx/main.o: afsktx/ax25.h
+afsktx/main.o: ax5043/spi/ax5043spi.h
+ cd afsktx; gcc -I ../ax5043 -pedantic -Wconversion -Wall -Wextra -c main.c; cd ..
+
diff --git a/afsktx/ax25.c b/afsktx/ax25.c
new file mode 100644
index 00000000..fd53871f
--- /dev/null
+++ b/afsktx/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) (dest_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/afsktx/ax25.h b/afsktx/ax25.h
new file mode 100644
index 00000000..2784c93d
--- /dev/null
+++ b/afsktx/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/afsktx/ax5043.c b/afsktx/ax5043.c
new file mode 100644
index 00000000..a6763050
--- /dev/null
+++ b/afsktx/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/afsktx/ax5043.h b/afsktx/ax5043.h
new file mode 100644
index 00000000..855466cc
--- /dev/null
+++ b/afsktx/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/afsktx/main.c b/afsktx/main.c
new file mode 100644
index 00000000..d9012fa3
--- /dev/null
+++ b/afsktx/main.c
@@ -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 *) "CALLXX", '2', (uint8_t *) "CALLXX", '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!!!";
+
+ /* 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/afsktx/status.h b/afsktx/status.h
new file mode 100644
index 00000000..9a212e96
--- /dev/null
+++ b/afsktx/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/afsktx/utils.h b/afsktx/utils.h
new file mode 100644
index 00000000..f35ee2d0
--- /dev/null
+++ b/afsktx/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_ */
diff --git a/ax5043/spi/ax5043spi.c b/ax5043/spi/ax5043spi.c
index 2392185b..1ae1277d 100644
--- a/ax5043/spi/ax5043spi.c
+++ b/ax5043/spi/ax5043spi.c
@@ -27,85 +27,289 @@
#include
#include
+#define MAX_SPI_WRITE_SIZE (512)
+
int spiChannel = -1;
int spiSpeed = -1;
void setSpiChannel(int newSpiChannel) {
- spiChannel = newSpiChannel;
+ spiChannel = newSpiChannel;
}
void setSpiSpeed(int newSpiSpeed) {
- spiSpeed = newSpiSpeed;
+ spiSpeed = newSpiSpeed;
}
void initializeSpi() {
- //printf("INFO: Initializing SPI\n");
-
- if (spiChannel < 0) {
- fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
- exit(EXIT_FAILURE);
- }
- if (spiSpeed < 0) {
- fprintf(stderr, "ERROR: invalid SPI speed %d\n", spiSpeed);
- exit(EXIT_FAILURE);
- }
+ //printf("INFO: Initializing SPI\n");
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+ if (spiSpeed < 0) {
+ fprintf(stderr, "ERROR: invalid SPI speed %d\n", spiSpeed);
+ exit(EXIT_FAILURE);
+ }
- int fd;
+ int fd;
- wiringPiSetup();
+ wiringPiSetup();
- fd = wiringPiSPISetup(spiChannel, spiSpeed);
- if (fd < 0) {
- fprintf(stderr, "ERROR: Cannot open SPI bus with error %d, %s\n",
- errno, strerror(errno));
- exit(EXIT_FAILURE);
- }
+ fd = wiringPiSPISetup(spiChannel, spiSpeed);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: Cannot open SPI bus with error %d, %s\n",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
- //printf("INFO: Finished initializing SPI\n");
+ //printf("INFO: Finished initializing SPI\n");
}
void ax5043WriteReg(uint16_t reg, uint8_t val) {
- uint8_t buf[3];
- int result;
-
- if (spiChannel < 0) {
- fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
- exit(EXIT_FAILURE);
- }
-
- buf[0] = (unsigned char)(0x00f0 | ((reg & 0xf00) >> 8));
- buf[1] = (reg & 0xff);
- buf[2] = val & 0xff;
-
- result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
- if (result < 0) {
- fprintf(stderr, "Failed to write the register with result %d and error %s\n",
- result, strerror(result));
- exit(EXIT_FAILURE);
- }
+ uint8_t buf[3];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0xF0;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = val & 0xff;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to write the register with result %d and error %s\n",
+ result, strerror(result));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void ax5043WriteReg2(uint16_t reg, uint16_t val) {
+ uint8_t buf[4];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0xF0;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = (val >> 8) & 0xff;
+ buf[3] = val & 0xff;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to write the register with result %d and error %s\n",
+ result, strerror(result));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void ax5043WriteReg3(uint16_t reg, uint32_t val) {
+ uint8_t buf[5];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0xF0;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = (val >> 16)& 0xff;
+ buf[3] = (val >> 8) & 0xff;
+ buf[4] = val & 0xff;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to write the register with result %d and error %s\n",
+ result, strerror(result));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void ax5043WriteReg4(uint16_t reg, uint32_t val) {
+ uint8_t buf[6];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0xF0;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = (val >> 24) & 0xff;
+ buf[3] = (val >> 16) & 0xff;
+ buf[4] = (val >> 8) & 0xff;
+ buf[5] = val & 0xff;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to write the register with result %d and error %s\n",
+ result, strerror(result));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void ax5043WriteRegN(uint16_t reg, const uint8_t *in, uint32_t len) {
+ uint8_t buf[MAX_SPI_WRITE_SIZE + 2];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+ if (len > MAX_SPI_WRITE_SIZE) {
+ fprintf(stderr,
+ "ERROR: attempting to write too much data to SPI channel (max of %d): %d\n",
+ MAX_SPI_WRITE_SIZE, len);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0xF0;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ for (uint32_t i = 0; i < len; ++i) {
+ buf[i + 2] = *(in + i);
+ }
+
+ result = wiringPiSPIDataRW(spiChannel, buf, len + 2);
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to write the register with result %d and error %s\n",
+ result, strerror(result));
+ exit(EXIT_FAILURE);
+ }
}
uint8_t ax5043ReadReg(uint16_t reg) {
- uint8_t buf[3];
- int result;
-
- if (spiChannel < 0) {
- fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
- exit(EXIT_FAILURE);
- }
-
- buf[0] = (unsigned char)(0x0070 | ((reg & 0xf00) >> 8));
- buf[1] = (reg & 0xff);
- buf[2] = 0x0000;
-
- result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
- if (result < 0) {
- fprintf(stderr, "Failed to read register with result = %d and error %s\n",
- result, strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- //printf("DEBUG: read value: %d\n", (int)buf[2]);
- return (buf[2] & 0xff);
+ uint8_t buf[3];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0x70;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = 0x0000;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to read register with result = %d and error %s\n",
+ result, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ //printf("DEBUG: read value: %d\n", (int)buf[2]);
+ return (buf[2]);
+}
+
+uint16_t ax5043ReadReg2(uint16_t reg) {
+ uint8_t buf[4];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0x70;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = 0x0000;
+ buf[3] = 0x0000;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to read register with result = %d and error %s\n",
+ result, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ //printf("DEBUG: read value: %d\n", (int)buf[2]);
+ return (buf[3]) | (buf[2] << 8);
+}
+
+uint32_t ax5043ReadReg3(uint16_t reg) {
+ uint8_t buf[5];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0x70;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = 0x0000;
+ buf[3] = 0x0000;
+ buf[4] = 0x0000;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to read register with result = %d and error %s\n",
+ result, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ //printf("DEBUG: read value: %d\n", (int)buf[2]);
+ return (buf[4]) | (buf[3] << 8) | (buf[2] << 16);
+}
+
+uint32_t ax5043ReadReg4(uint16_t reg) {
+ uint8_t buf[6];
+ int result;
+
+ if (spiChannel < 0) {
+ fprintf(stderr, "ERROR: invalid SPI channel %d\n", spiChannel);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t mask = 0x70;
+
+ buf[0] = mask | (~mask & (reg >> 8));
+ buf[1] = (reg & 0xff);
+ buf[2] = 0x0000;
+ buf[3] = 0x0000;
+ buf[4] = 0x0000;
+ buf[5] = 0x0000;
+
+ result = wiringPiSPIDataRW(spiChannel, buf, sizeof(buf));
+ if (result < 0) {
+ fprintf(stderr,
+ "Failed to read register with result = %d and error %s\n",
+ result, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ //printf("DEBUG: read value: %d\n", (int)buf[2]);
+ return (buf[5]) | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
}
diff --git a/ax5043/spi/ax5043spi_p.h b/ax5043/spi/ax5043spi_p.h
index c81c0d8e..40d5e105 100644
--- a/ax5043/spi/ax5043spi_p.h
+++ b/ax5043/spi/ax5043spi_p.h
@@ -16,7 +16,7 @@
\file ax5043spi_p.h
\brief Provides an abstraction layer for the SPI interface communicating to the digital transceiver
-*/
+ */
#ifndef AX5043SPI_P_H_
#define AX5043SPI_P_H_
@@ -27,47 +27,96 @@
#define SPI_SPEED (32000000) //!< The default SPI bus speed for the digital transceiver
/*! \fn void setSpiChannel(int newSpiChannel)
- \brief Set the SPI channel for the digital transceiver.
+ \brief Set the SPI channel for the digital transceiver.
- setSpiChannel must be called before initializeSpi(). The default is SPI_CHANNEL.
- \param newSpiChannel The SPI channel for the digital transceiver.
- \sa SPI_CHANNEL
- \sa initializeSpi
-*/
+ setSpiChannel must be called before initializeSpi(). The default is SPI_CHANNEL.
+ \param newSpiChannel The SPI channel for the digital transceiver.
+ \sa SPI_CHANNEL
+ \sa initializeSpi
+ */
void setSpiChannel(int newSpiChannel);
/*! \fn void setSpiSpeed(int newSpiSpeed)
- \brief Set the SPI bus speed for the digital transceiver.
-
-
- setSpiSpeed must be called before initializeSpi(). The default is SPI_SPEED.
- \param newSpiSpeed The SPI bus speed for the digital transceiver.
- \sa SPI_SPEED
- \sa initializeSpi
-*/
+ \brief Set the SPI bus speed for the digital transceiver.
+
+
+ setSpiSpeed must be called before initializeSpi(). The default is SPI_SPEED.
+ \param newSpiSpeed The SPI bus speed for the digital transceiver.
+ \sa SPI_SPEED
+ \sa initializeSpi
+ */
void setSpiSpeed(int newSpiSpeed);
/*! \fn void initializeSpi()
- \brief Initilize the SPI bus to communicate with the digital transceiver.
+ \brief Initilize the SPI bus to communicate with the digital transceiver.
- setSpiChannel() and setSpiSpeed() must both be called before initializeSPI().
- \sa setSpiChannel
- \sa setSpiSpeed
-*/
+ setSpiChannel() and setSpiSpeed() must both be called before initializeSPI().
+ \sa setSpiChannel
+ \sa setSpiSpeed
+ */
void initializeSpi(void);
/*! \fn void ax5043WriteReg(uint16_t reg, uint8_t val)
- \brief Write a value to an AX5043 register.
- \param reg The register to write.
- \param val The value to right to the register.
-*/
+ \brief Write a value to an AX5043 register.
+ \param reg The register to write.
+ \param val The value to right to the register.
+ */
void ax5043WriteReg(uint16_t reg, uint8_t val);
+/*! \fn void ax5043WriteReg2(uint16_t reg, uint16_t val)
+ \brief Write a two byte value to an AX5043 register.
+ \param reg The register to write.
+ \param val The value to right to the register.
+ */
+void ax5043WriteReg2(uint16_t reg, uint16_t val);
+
+/*! \fn void ax5043WriteReg3(uint16_t reg, uint32_t val)
+ \brief Write a three byte value to an AX5043 register.
+ \param reg The register to write.
+ \param val The value to right to the register.
+ */
+void ax5043WriteReg3(uint16_t reg, uint32_t val);
+
+/*! \fn void ax5043WriteReg4(uint16_t reg, uint32_t val)
+ \brief Write a four byte value to an AX5043 register.
+ \param reg The register to write.
+ \param val The value to right to the register.
+ */
+void ax5043WriteReg4(uint16_t reg, uint32_t val);
+
+/*! \fn void ax5043WriteRegN(uint16_t reg, const uint8_t *in, uint32_t len)
+ \brief Write a four byte value to an AX5043 register.
+ \param reg The register to write.
+ \param val The value to right to the register.
+ */
+void ax5043WriteRegN(uint16_t reg, const uint8_t *in, uint32_t len);
+
/*! \fn uint8_t ax5043ReadReg(uint16_t reg)
- \brief Read a value from an AX5043 register.
- \param reg The register to read.
- \return The value read from the register.
-*/
+ \brief Read a one byte value from an AX5043 register.
+ \param reg The register to read.
+ \return The value read from the register.
+ */
uint8_t ax5043ReadReg(uint16_t reg);
+/*! \fn uint16_t ax5043ReadReg2(uint16_t reg)
+ \brief Read a two value from an AX5043 register.
+ \param reg The register to read.
+ \return The value read from the register.
+ */
+uint16_t ax5043ReadReg2(uint16_t reg);
+
+/*! \fn uint32_t ax5043ReadReg3(uint16_t reg)
+ \brief Read a three byte value from an AX5043 register.
+ \param reg The register to read.
+ \return The value read from the register.
+ */
+uint32_t ax5043ReadReg3(uint16_t reg);
+
+/*! \fn uint32_t ax5043ReadReg4(uint16_t reg)
+ \brief Read a four byte value from an AX5043 register.
+ \param reg The register to read.
+ \return The value read from the register.
+ */
+uint32_t ax5043ReadReg4(uint16_t reg);
+
#endif /* AX5043SPI_P_H_ */