summaryrefslogtreecommitdiff
path: root/software/avr.lib/rda1846.c
diff options
context:
space:
mode:
Diffstat (limited to 'software/avr.lib/rda1846.c')
-rw-r--r--software/avr.lib/rda1846.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/software/avr.lib/rda1846.c b/software/avr.lib/rda1846.c
new file mode 100644
index 0000000..43e66ff
--- /dev/null
+++ b/software/avr.lib/rda1846.c
@@ -0,0 +1,485 @@
+/*
+ * spreadspace avr utils
+ *
+ *
+ * Copyright (C) 2013-2014 Christian Pointner <equinox@spreadspace.org>
+ *
+ * This file is part of spreadspace avr utils.
+ *
+ * spreadspace avr utils 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
+ * any later version.
+ *
+ * spreadspace avr utils 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 spreadspace avr utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "LUFA/Drivers/Peripheral/TWI.h"
+#include <stdio.h>
+
+#include "rda1846.h"
+#include "rda1846_defines.h"
+
+static uint8_t rda1846_write_register_raw(const uint8_t addr, const uint16_t data)
+{
+ if(TWI_StartTransmission(RDA1846_CHIP_ADDR | TWI_ADDRESS_WRITE,10) != TWI_ERROR_NoError)
+ return 1;
+
+ if(!TWI_SendByte(addr | RDA1846_ADDR_W))
+ goto i2c_error;
+ if(!TWI_SendByte((uint8_t)(data>>8)))
+ goto i2c_error;
+ if(!TWI_SendByte((uint8_t)data))
+ goto i2c_error;
+
+ TWI_StopTransmission();
+ return 0;
+
+i2c_error:
+ TWI_StopTransmission();
+ printf("I2C error (write_register_raw)\r\n");
+ return 1;
+}
+
+static uint8_t rda1846_read_register_raw(const uint8_t addr, uint16_t* data)
+{
+ if(TWI_StartTransmission(RDA1846_CHIP_ADDR | TWI_ADDRESS_WRITE,10) != TWI_ERROR_NoError)
+ return 1;
+ if(!TWI_SendByte(addr | RDA1846_ADDR_R))
+ goto i2c_error;
+
+ if(TWI_StartTransmission(RDA1846_CHIP_ADDR | TWI_ADDRESS_READ,10) != TWI_ERROR_NoError)
+ goto i2c_error;
+ uint8_t tmp;
+ if(!TWI_ReceiveByte(&tmp, 0))
+ goto i2c_error;
+ *data = tmp << 8;
+ if(!TWI_ReceiveByte(&tmp, 1))
+ goto i2c_error;
+ *data |= tmp;
+
+ TWI_StopTransmission();
+ return 0;
+
+i2c_error:
+ TWI_StopTransmission();
+ printf("I2C error (read_register_raw)\r\n");
+ return 1;
+}
+
+static uint8_t rda1846_write_register(const uint8_t addr, const uint16_t data)
+{
+ if(addr < RDA1846_ADDR_LIMIT)
+ return rda1846_write_register_raw(addr, data);
+
+ if(addr > RDA1846_ADDR_LIMIT) {
+ uint8_t ret = rda1846_write_register_raw(RDA1846_ADDR_LIMIT, 1);
+ if(ret) return ret;
+
+ ret = rda1846_write_register_raw(addr, data);
+ if(ret) return ret;
+
+ return rda1846_write_register_raw(RDA1846_ADDR_LIMIT, 0);
+ }
+ return 1;
+}
+
+static uint8_t rda1846_read_register(const uint8_t addr, uint16_t* data)
+{
+ if(addr < RDA1846_ADDR_LIMIT)
+ return rda1846_read_register_raw(addr, data);
+
+ if(addr > RDA1846_ADDR_LIMIT) {
+ uint8_t ret = rda1846_write_register_raw(RDA1846_ADDR_LIMIT, 1);
+ if(ret) return ret;
+
+ ret = rda1846_read_register_raw(addr - RDA1846_ADDR_LIMIT + 1, data);
+ if(ret) return ret;
+
+ return rda1846_write_register_raw(RDA1846_ADDR_LIMIT, 0);
+ }
+ return 1;
+}
+
+static rf_band_t rf_band_;
+static channel_bw_t channel_bw_;
+static rda1846_state_t state_;
+
+/*
+ * EXTERNAL INTERFACE
+ */
+
+char* rda1846_rf_band_to_string(rf_band_t b)
+{
+ switch(b)
+ {
+ case b_2m: return "2m (134-174 MHz)";
+ case b_1m5: return "1.5m (200-260 MHz)";
+ case b_70cm: return "70cm (400-520 MHz)";
+ }
+ return "";
+}
+
+char* rda1846_channel_bw_to_string(channel_bw_t bw)
+{
+ switch(bw)
+ {
+ case bw_12k5: return "12,5 kHz";
+ case bw_25k: return "25 kHz";
+ }
+ return "";
+}
+
+char* rda1846_state_to_string(rda1846_state_t s)
+{
+ switch(s)
+ {
+ case powerdown: return "powerdown";
+ case idle: return "idle";
+ case receive: return "receiving";
+ case transmit: return "transmitting";
+ }
+ return "";
+}
+
+void rda1846_init(void)
+{
+ TWI_Init(TWI_BIT_PRESCALE_1, TWI_BITLENGTH_FROM_FREQ(1, 200000));
+}
+
+void rda1846_reg_init(void)
+{
+ rda1846_write_register(RDA1846_REG_CTL, RDA1846_CTL_SOFT_RST);
+ rda1846_write_register(RDA1846_REG_CTL, RDA1846_CTL_PDN);
+
+ rda1846_write_register(RDA1846_REG_XTAL, RDA1846_XTAL_FREQ);
+ rda1846_write_register(RDA1846_REG_ADCLK, RDA1846_ADCLK_FREQ);
+ rda1846_write_register(RDA1846_REG_CLK_MODE, RDA1846_CLK_MODE);
+
+ rda1846_write_register(RDA1846_REG_RF_BAND, RDA1846_RF_BAND_2M);
+ rf_band_ = b_2m;
+
+ rda1846_write_register(RDA1846_REG_CTL, RDA1846_CTL_CH_12K5 | RDA1846_CTL_RX_M_TX_M | RDA1846_CTL_PDN);
+ channel_bw_ = bw_12k5;
+ state_ = idle;
+
+ rda1846_write_register(RDA1846_REG_TX_VOICE, RDA1846_TX_VOICE_NONE);
+// rda1846_write_register(RDA1846_REG_TX_VOICE, RDA1846_TX_VOICE_MIC);
+// rda1846_write_register(RDA1846_REG_TX_VOICE, RDA1846_TX_VOICE_TONE2);
+// rda1846_write_register(RDA1846_REG_DTMF_T2, 6689);
+
+ rda1846_write_register(RDA1846_REG_DTMF_C01, (RDA1846_DTMF_C0 << 8) | RDA1846_DTMF_C1);
+ rda1846_write_register(RDA1846_REG_DTMF_C23, (RDA1846_DTMF_C1 << 8) | RDA1846_DTMF_C3);
+ rda1846_write_register(RDA1846_REG_DTMF_C45, (RDA1846_DTMF_C3 << 8) | RDA1846_DTMF_C5);
+ rda1846_write_register(RDA1846_REG_DTMF_C67, (RDA1846_DTMF_C6 << 8) | RDA1846_DTMF_C7);
+ rda1846_write_register(RDA1846_REG_DTMF_CTL, RDA1846_DTMF_DUAL | RDA1846_DTMF_EN);
+ rda1846_write_register(RDA1846_REG_INT, RDA1846_INT_DTMF_IDLE);
+
+ rda1846_gpio_default();
+}
+
+void rda1846_soft_reset(void)
+{
+ rda1846_write_register(RDA1846_REG_CTL, RDA1846_CTL_SOFT_RST);
+}
+
+
+
+void rda1846_set_band(rf_band_t b)
+{
+ uint16_t data = RDA1846_RF_BAND_2M;
+ if(b == b_1m5) data = RDA1846_RF_BAND_1M5;
+ if(b == b_70cm) data = RDA1846_RF_BAND_70CM;
+ if(!rda1846_write_register(RDA1846_REG_RF_BAND, data))
+ rf_band_ = b;
+}
+
+rf_band_t rda1846_get_band(void)
+{
+ return rf_band_;
+}
+
+void rda1846_set_bw(channel_bw_t bw)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_CTL, &data))
+ return;
+
+ data &= RDA1846_CTL_CH_RESET;
+ data |= bw == bw_12k5 ? RDA1846_CTL_CH_12K5 : RDA1846_CTL_CH_25K;
+ if(!rda1846_write_register(RDA1846_REG_CTL, data))
+ channel_bw_ = bw;
+}
+
+channel_bw_t rda1846_get_bw(void)
+{
+ return channel_bw_;
+}
+
+uint8_t rda1846_set_freq_kHz(int32_t freq)
+{
+ if((rf_band_ == b_2m && (freq < RDA1846_BAND_2M_LOW || freq > RDA1846_BAND_2M_HIGH)) ||
+ (rf_band_ == b_1m5 && (freq < RDA1846_BAND_1M5_LOW || freq > RDA1846_BAND_1M5_HIGH)) ||
+ (rf_band_ == b_70cm && (freq < RDA1846_BAND_70CM_LOW || freq > RDA1846_BAND_70CM_HIGH)) )
+ return 1;
+
+ freq = freq<<3;
+ uint8_t ret = rda1846_write_register(RDA1846_REG_FREQH, ((freq>>16) & 0x00003FFF));
+ if(ret)
+ return 1;
+
+ return rda1846_write_register(RDA1846_REG_FREQL, (freq & 0x0000FFFF));
+}
+
+static int32_t corr_freq(int32_t f)
+{
+ float corr = RDA1846_FREQ_CORR_2M;
+ if(rf_band_ == b_1m5) corr = RDA1846_FREQ_CORR_1M5;
+ if(rf_band_ == b_70cm) corr = RDA1846_FREQ_CORR_70CM;
+ return (int32_t)(((float)f)/corr);
+}
+
+int32_t rda1846_get_freq_kHz(void)
+{
+ if(state_ == powerdown || state_ == idle)
+ return -1;
+
+ uint16_t data;
+ uint8_t ret = rda1846_read_register(RDA1846_REG_FREQH, &data);
+ if(ret)
+ return -1;
+
+ int32_t freq = (((uint32_t)(data & 0x3FFF))<<16);
+ ret = rda1846_read_register(RDA1846_REG_FREQL, &data);
+ if(ret)
+ return -1;
+
+ freq = corr_freq((freq+data)>>3);
+ return (freq / 8) + ((freq % 8) < 4 ? 0 : 1);
+}
+
+
+
+void rda1846_powerdown(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_CTL, &data))
+ return;
+
+ data &= ~(RDA1846_CTL_TX | RDA1846_CTL_RX | RDA1846_CTL_CHIP_CAL | RDA1846_CTL_PDN);
+ if(!rda1846_write_register(RDA1846_REG_CTL, data))
+ state_ = powerdown;
+}
+
+void rda1846_idle(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_CTL, &data))
+ return;
+
+ data &= ~(RDA1846_CTL_TX | RDA1846_CTL_RX | RDA1846_CTL_CHIP_CAL);
+ data |= RDA1846_CTL_PDN;
+ if(!rda1846_write_register(RDA1846_REG_CTL, data))
+ state_ = idle;
+}
+
+void rda1846_receive(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_CTL, &data))
+ return;
+
+ data &= ~(RDA1846_CTL_TX);
+ data |= RDA1846_CTL_RX | RDA1846_CTL_CHIP_CAL | RDA1846_CTL_PDN;
+ if(!rda1846_write_register(RDA1846_REG_CTL, data))
+ state_ = receive;
+}
+
+void rda1846_transmit(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_CTL, &data))
+ return;
+
+ data &= ~(RDA1846_CTL_RX);
+ data |= RDA1846_CTL_TX | RDA1846_CTL_CHIP_CAL | RDA1846_CTL_PDN;
+ if(!rda1846_write_register(RDA1846_REG_CTL, data))
+ state_ = transmit;
+}
+
+rda1846_state_t rda1846_get_state(void)
+{
+ return state_;
+}
+
+
+
+uint16_t rda1846_get_dtmf(uint8_t* idx1, uint8_t* idx2, uint8_t* code, uint8_t* valid)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_DTMF_OUT, &data))
+ return 0xFFFF;
+
+ if(idx1) *idx1 = (uint8_t)((data & 0x0700)>>8);
+ if(idx2) *idx2 = (uint8_t)((data & 0x00E0)>>5);
+ if(code) *code = (uint8_t)(data & 0x000F);
+ if(valid) *valid = data & 0x0010 ? 0 : 1;
+
+ return data;
+}
+
+void rda1846_clear_int(void)
+{
+ rda1846_write_register(0x00, 0x1846);
+}
+
+
+
+void rda1846_set_volume(int8_t vol)
+{
+ uint16_t data;
+ if(vol < -30 || vol > 0)
+ return;
+
+ if(vol >= -15)
+ data = 0x000F | (((vol + 15)<<4) & 0xF0);
+ else
+ data = (vol + 30) & 0x0F;
+
+ rda1846_write_register(RDA1846_REG_RX_VOICE, data);
+}
+
+int8_t rda1846_get_volume(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_RX_VOICE, &data))
+ return 1;
+
+ return (int8_t)(((data & 0x00F0)>>4) + (data & 0x000F)) - 30;
+}
+
+
+
+void rda1846_gpio_default(void)
+{
+ rda1846_write_register(RDA1846_REG_GPIO, RDA1846_GPIO_7_VOX | RDA1846_GPIO_6_SQ |
+ RDA1846_GPIO_5_TXON_RF | RDA1846_GPIO_4_RXON_RF |
+ RDA1846_GPIO_2_INT);
+}
+
+void rda1846_gpio_AA(void)
+{
+ rda1846_write_register(RDA1846_REG_GPIO, RDA1846_GPIO_7_HIGH | RDA1846_GPIO_6_LOW |
+ RDA1846_GPIO_5_HIGH | RDA1846_GPIO_4_LOW |
+ RDA1846_GPIO_3_HIGH | RDA1846_GPIO_2_LOW |
+ RDA1846_GPIO_1_HIGH | RDA1846_GPIO_0_LOW);
+}
+
+void rda1846_gpio_55(void)
+{
+ rda1846_write_register(RDA1846_REG_GPIO, RDA1846_GPIO_7_LOW | RDA1846_GPIO_6_HIGH |
+ RDA1846_GPIO_5_LOW | RDA1846_GPIO_4_HIGH |
+ RDA1846_GPIO_3_LOW | RDA1846_GPIO_2_HIGH |
+ RDA1846_GPIO_1_LOW | RDA1846_GPIO_0_HIGH);
+}
+
+void rda1846_gpio_off(void)
+{
+ rda1846_write_register(RDA1846_REG_GPIO, RDA1846_GPIO_7_HI_Z | RDA1846_GPIO_6_HI_Z |
+ RDA1846_GPIO_5_HI_Z | RDA1846_GPIO_4_HI_Z |
+ RDA1846_GPIO_3_HI_Z | RDA1846_GPIO_2_HI_Z |
+ RDA1846_GPIO_1_HI_Z | RDA1846_GPIO_0_HI_Z);
+}
+
+
+
+int16_t rda1846_get_rssi(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_RSSI, &data))
+ return -1;
+
+ return ((int16_t)(data & 0x03FF) - 135*8);
+}
+
+int16_t rda1846_get_vssi(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_VSSI, &data))
+ return -1;
+
+ return (int16_t)data;
+}
+
+uint16_t rda1846_get_flags(void)
+{
+ uint16_t data;
+ if(rda1846_read_register(RDA1846_REG_FLAG, &data))
+ return 0xFFFF;
+
+ return data & RDA1846_FLAGS_MASK;
+}
+
+
+
+static char* rda1846_regaddr_to_string(uint8_t addr)
+{
+ switch(addr) {
+ case RDA1846_REG_CTL: return "CTL";
+ case RDA1846_REG_GPIO: return "GPIO";
+ case RDA1846_REG_INT: return "INT";
+ case RDA1846_REG_FLAG: return "FLAG";
+ case RDA1846_REG_RSSI: return "RSSI";
+ case RDA1846_REG_VSSI: return "VSSI";
+ case RDA1846_REG_CLK_MODE: return "CLK_MODE";
+ case RDA1846_REG_XTAL: return "XTAL";
+ case RDA1846_REG_ADCLK: return "ADCLK";
+ case RDA1846_REG_RF_BAND: return "RF_BAND";
+ case RDA1846_REG_FREQH: return "FREQH";
+ case RDA1846_REG_FREQL: return "FREQL";
+ case RDA1846_REG_PA_BIAS: return "PA_BIAS";
+ case RDA1846_REG_TX_VOICE: return "TX_VOICE";
+ case RDA1846_REG_VOX_OPEN: return "VOX_OPEN";
+ case RDA1846_REG_VOX_SHUT: return "VOX_SHUT";
+ case RDA1846_REG_SUBAUDIO: return "SUBAUDIO";
+ case RDA1846_REG_RX_VOICE: return "RX_VOICE";
+ case RDA1846_REG_SQ_OPEN: return "SQ_OPEN";
+ case RDA1846_REG_SQ_SHUT: return "SQ_SHUT";
+ case RDA1846_REG_DTMF_CTL: return "DTMF_CTL";
+ case RDA1846_REG_DTMF_T1: return "DTMF_T1";
+ case RDA1846_REG_DTMF_T2: return "DTMF_T2";
+ case RDA1846_REG_DTMF_C01: return "DTMF_C01";
+ case RDA1846_REG_DTMF_C23: return "DTMF_C23";
+ case RDA1846_REG_DTMF_C45: return "DTMF_C45";
+ case RDA1846_REG_DTMF_C67: return "DTMF_C67";
+ case RDA1846_REG_DTMF_OUT: return "DTMF_OUT";
+ default: return "unknown";
+ }
+}
+
+void rda1846_dump_register(void)
+{
+ printf("RDA1846: register dump\r\n");
+
+ const uint8_t regs[] = { RDA1846_REG_CLK_MODE, RDA1846_REG_XTAL, RDA1846_REG_ADCLK,
+ RDA1846_REG_RF_BAND, RDA1846_REG_FREQH, RDA1846_REG_FREQL,
+ RDA1846_REG_CTL, RDA1846_REG_INT, RDA1846_REG_GPIO,
+ RDA1846_REG_PA_BIAS, RDA1846_REG_TX_VOICE, RDA1846_REG_VOX_OPEN, RDA1846_REG_VOX_SHUT,
+ RDA1846_REG_SUBAUDIO, RDA1846_REG_RX_VOICE, RDA1846_REG_SQ_OPEN, RDA1846_REG_SQ_SHUT,
+ RDA1846_REG_DTMF_CTL, RDA1846_REG_DTMF_OUT, RDA1846_REG_DTMF_T1, RDA1846_REG_DTMF_T2,
+ RDA1846_REG_RSSI, RDA1846_REG_VSSI, RDA1846_REG_FLAG };
+
+ int i;
+ for(i=0; i<sizeof(regs); ++i) {
+ uint16_t data;
+ if(rda1846_read_register(regs[i], &data))
+ data = 0xFFFF;
+ printf(" 0x%02X (%s): 0x%04X\r\n", regs[i], rda1846_regaddr_to_string(regs[i]), data);
+ }
+}