diff options
Diffstat (limited to 'software/avr.lib/rda1846.c')
-rw-r--r-- | software/avr.lib/rda1846.c | 485 |
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); + } +} |