/* * Hamlib KIT backend - Elektor SDR USB (5/07) receiver description * Copyright (c) 2007-2010 by Stephane Fillod * * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "hamlib/rig.h" #include "token.h" #include "kit.h" #ifdef _WIN32 #define USE_FTDI_DLL #elif defined(HAVE_LIBUSB) && (defined(HAVE_LIBUSB_H) || defined(HAVE_LIBUSB_1_0_LIBUSB_H)) #define USE_LIBUSB #endif /* * Compile this model only if libusb is available * or if .DLL is available under Windows */ #if defined(USE_FTDI_DLL) || defined(USE_LIBUSB) static int elektor507_init(RIG *rig); static int elektor507_cleanup(RIG *rig); static int elektor507_open(RIG *rig); static int elektor507_set_freq(RIG *rig, vfo_t vfo, freq_t freq); static int elektor507_get_freq(RIG *rig, vfo_t vfo, freq_t *freq); static int elektor507_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val); static int elektor507_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val); static int elektor507_set_ant(RIG *rig, vfo_t vfo, ant_t ant, value_t option); static int elektor507_get_ant(RIG *rig, vfo_t vfo, ant_t dummy, value_t *option, ant_t *ant_curr, ant_t *ant_tx, ant_t *ant_rx); static int elektor507_set_conf(RIG *rig, token_t token, const char *val); static int elektor507_get_conf(RIG *rig, token_t token, char *val); static const char *elektor507_get_info(RIG *rig); /* * I2C addresses */ #define CY_I2C_RAM_ADR 210 #define CY_I2C_EEPROM_ADR 208 /* * I2C registers */ #define CLKOE_REG 0x09 #define DIV1_REG 0x0c #define DIV2_REG 0x47 #define XTALCTL_REG 0x12 #define CAPLOAD_REG 0x13 #define PUMPCOUNTERS_REG 0x40 #define CLKSRC_REG 0x44 static int cy_update_pll(RIG *rig, unsigned char IICadr); static int i2c_write_regs(RIG *rig, unsigned char IICadr, int reg_count, unsigned char reg_adr, unsigned char reg_val1, unsigned char reg_val2, unsigned char reg_val3); #define i2c_write_reg(rig, IICadr, reg_adr, reg_val) \ i2c_write_regs(rig, IICadr, 1, reg_adr, reg_val, 0, 0) #ifdef USE_FTDI_DLL #ifdef HAVE_WINDOWS_H #include #endif #ifdef HAVE_WINBASE_H #include #endif #include #define ELEKTOR507_DLL "FTD2XX.dll" /* Some type definitions needed for dll access */ typedef enum { FT_OK = 0, FT_INVALID_HANDLE = 1, FT_DEVICE_NOT_FOUND = 2, FT_DEVICE_NOT_OPENED = 3, FT_IO_ERROR = 4, FT_INSUFFICIENT_RESOURCES = 5, FT_INVALID_PARAMETER = 6, FT_SUCCESS = FT_OK, FT_INVALID_BAUD_RATE = 7, FT_DEVICE_NOT_OPENED_FOR_ERASE = 8, FT_DEVICE_NOT_OPENED_FOR_WRITE = 9, FT_FAILED_TO_WRITE_DEVICE = 10, FT_EEPROM_READ_FAILED = 11, FT_EEPROM_WRITE_FAILED = 12, FT_EEPROM_ERASE_FAILED = 13, FT_EEPROM_NOT_PRESENT = 14, FT_EEPROM_NOT_PROGRAMMED = 15, FT_INVALID_ARGS = 16, FT_OTHER_ERROR = 17, } FT_Result; typedef FT_Result(__stdcall *FNCFT_Open)(int Index, unsigned long *ftHandle); typedef FT_Result(__stdcall *FNCFT_Close)(unsigned long ftHandle); typedef FT_Result(__stdcall *FNCFT_SetBitMode)(unsigned long ftHandle, unsigned char Mask, unsigned char Enable); typedef FT_Result(__stdcall *FNCFT_SetBaudRate)(unsigned long ftHandle, unsigned long BaudRate); typedef FT_Result(__stdcall *FNCFT_Write)(unsigned long ftHandle, void *FTOutBuf, unsigned long BufferSize, int *ResultPtr); struct elektor507_extra_priv_data { HMODULE dll; FNCFT_Open FT_Open; FNCFT_Close FT_Close; FNCFT_SetBitMode FT_SetBitMode; FNCFT_SetBaudRate FT_SetBaudRate; FNCFT_Write FT_Write; unsigned long ftHandle; }; #elif defined(USE_LIBUSB) #include #ifdef HAVE_LIBUSB_H # include #elif defined HAVE_LIBUSB_1_0_LIBUSB_H # include #endif #define USB_VID_FTDI 0x0403 /* Future Technology Devices International */ #define USB_PID_FTDI_FT232 0x6001 /* FT232R 8-bit FIFO */ #define FTDI_IN_EP 0x02 #define FTDI_USB_WRITE_TIMEOUT 5000 struct elektor507_extra_priv_data { /* empty with libusb */ }; #endif /* defaults */ #define OSCFREQ 10000 /* kHz unit -> MHz(10) */ #define XTAL_CAL 128 #define TOK_OSCFREQ TOKEN_BACKEND(1) #define TOK_XTALCAL TOKEN_BACKEND(2) static const struct confparams elektor507_cfg_params[] = { { TOK_OSCFREQ, "osc_freq", "Oscillator freq", "Oscillator frequency in Hz", "10000000", RIG_CONF_NUMERIC, { .n = { 0, MHz(30), 1 } } }, { TOK_XTALCAL, "xtal_cal", "Xtal Cal", "Crystal calibration", "132", RIG_CONF_NUMERIC, { .n = { 0, 255, 1 } } }, { RIG_CONF_END, NULL, } }; /* * Common data struct */ struct elektor507_priv_data { struct elektor507_extra_priv_data extra_priv; unsigned xtal_cal; /* 0..255 (-150ppm..150ppm) */ unsigned osc_freq; /* kHz */ #define ANT_AUTO RIG_ANT_1 #define ANT_EXT RIG_ANT_2 #define ANT_TEST_CLK RIG_ANT_3 ant_t ant; /* current antenna */ /* CY PLL stuff. * This is Qtotal & Ptotal values here. */ int P, Q, Div1N; /* FTDI comm stuff */ unsigned char FT_port; int Buf_adr; #define FT_OUT_BUFFER_MAX 1024 unsigned char FT_Out_Buffer[FT_OUT_BUFFER_MAX]; }; #ifdef USE_FTDI_DLL int elektor507_init(RIG *rig) { struct elektor507_priv_data *priv; struct elektor507_extra_priv_data *extra_priv; priv = (struct elektor507_priv_data *)calloc(sizeof(struct elektor507_priv_data), 1); if (!priv) { /* whoops! memory shortage! */ return -RIG_ENOMEM; } priv->xtal_cal = XTAL_CAL; priv->osc_freq = OSCFREQ; priv->ant = ANT_AUTO; /* DIV1N set to safe default */ priv->Div1N = 8; priv->P = 8; priv->Q = 2; extra_priv = &priv->extra_priv; /* Try to load required dll */ extra_priv->dll = LoadLibrary(ELEKTOR507_DLL); if (!extra_priv->dll) { rig_debug(RIG_DEBUG_ERR, "%s: Unable to LoadLibrary %s\n", __func__, ELEKTOR507_DLL); free(priv); return -RIG_EIO; /* huh! */ } /* * Get process addresses from dll for function access */ /* Open_USB_Device */ extra_priv->FT_Open = (FNCFT_Open) GetProcAddress(extra_priv->dll, "FT_Open"); /* Close_USB_Device */ extra_priv->FT_Close = (FNCFT_Close) GetProcAddress(extra_priv->dll, "FT_Close"); /* Set_USB_Device_BitMode */ extra_priv->FT_SetBitMode = (FNCFT_SetBitMode) GetProcAddress(extra_priv->dll, "FT_SetBitMode"); /* Set_USB_Device_BaudRate */ extra_priv->FT_SetBaudRate = (FNCFT_SetBaudRate) GetProcAddress(extra_priv->dll, "FT_SetBaudRate"); /* Write_USB_Device_Buffer */ extra_priv->FT_Write = (FNCFT_Write) GetProcAddress(extra_priv->dll, "FT_Write"); rig->state.priv = (void *)priv; return RIG_OK; } int elektor507_ftdi_write_data(RIG *rig, void *FTOutBuf, unsigned long BufferSize) { struct elektor507_extra_priv_data *extra_priv = &((struct elektor507_priv_data *)rig->state.priv)->extra_priv; FT_Result ret; int Result; rig_debug(RIG_DEBUG_TRACE, "%s called, %d bytes\n", __func__, (int)BufferSize); /* Open FTDI */ ret = extra_priv->FT_Open(0, &extra_priv->ftHandle); if (ret != FT_OK) { return -RIG_EIO; } ret = extra_priv->FT_SetBitMode(extra_priv->ftHandle, 0xff, 1); if (ret != FT_OK) { return -RIG_EIO; } ret = extra_priv->FT_SetBaudRate(extra_priv->ftHandle, 38400); if (ret != FT_OK) { return -RIG_EIO; } ret = extra_priv->FT_Write(extra_priv->ftHandle, FTOutBuf, BufferSize, &Result); if (ret != FT_OK) { rig_debug(RIG_DEBUG_ERR, "FT_Write failed: %d, Result: %d\n", ret, Result); return -RIG_EIO; } ret = extra_priv->FT_Close(extra_priv->ftHandle); if (ret != FT_OK) { return -RIG_EIO; } return RIG_OK; } int elektor507_cleanup(RIG *rig) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; /* Clean up the dll access */ if (priv) { FreeLibrary(priv->extra_priv.dll); } if (rig->state.priv) { free(rig->state.priv); } rig->state.priv = NULL; return RIG_OK; } const char *elektor507_get_info(RIG *rig) { static char buf[64]; SNPRINTF(buf, sizeof(buf), "Elektor SDR USB w/ FTDI DLL"); return buf; } #elif defined(USE_LIBUSB) /* * The libusb code is inspired by libftdi: * http://www.intra2net.com/de/produkte/opensource/ftdi/ */ int elektor507_init(RIG *rig) { hamlib_port_t *rp = &rig->state.rigport; struct elektor507_priv_data *priv; rig->state.priv = (struct elektor507_priv_data *)calloc(sizeof(struct elektor507_priv_data), 1); if (!rig->state.priv) { /* whoops! memory shortage! */ return -RIG_ENOMEM; } priv = rig->state.priv; priv->xtal_cal = XTAL_CAL; priv->osc_freq = OSCFREQ; priv->ant = ANT_AUTO; /* DIV1N set to safe default */ priv->Div1N = 8; priv->P = 8; priv->Q = 2; rp->parm.usb.vid = USB_VID_FTDI; rp->parm.usb.pid = USB_PID_FTDI_FT232; rp->parm.usb.conf = 1; rp->parm.usb.iface = 0; rp->parm.usb.alt = 0; /* necessary ? */ return RIG_OK; } int elektor507_cleanup(RIG *rig) { if (!rig) { return -RIG_EINVAL; } if (rig->state.priv) { free(rig->state.priv); } rig->state.priv = NULL; return RIG_OK; } /* Rem: not reentrant */ const char *elektor507_get_info(RIG *rig) { static char buf[64]; libusb_device_handle *udh = rig->state.rigport.handle; struct libusb_device_descriptor desc; /* always succeeds since libusb-1.0.16 */ libusb_get_device_descriptor(libusb_get_device(udh), &desc); SNPRINTF(buf, sizeof(buf), "USB dev %04d", desc.bcdDevice); return buf; } int elektor507_libusb_setup(RIG *rig) { libusb_device_handle *udh = rig->state.rigport.handle; int ret; unsigned short index = 0, usb_val; rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); /* Reset the ftdi device */ #if 1 ret = libusb_control_transfer(udh, 0x40, 0, 0, index, NULL, 0, FTDI_USB_WRITE_TIMEOUT); if (ret != 0) { rig_debug(RIG_DEBUG_ERR, "%s: libusb_control_transfer reset failed: %s\n", __func__, libusb_error_name(ret)); return -RIG_EIO; } #endif /* * Enable bitbang mode */ usb_val = 0xff; /* low byte: bitmask */ usb_val |= (0x01 << 8); /* Basic bitbang_mode: 0x01 */ ret = libusb_control_transfer(udh, 0x40, 0x0B, usb_val, index, NULL, 0, FTDI_USB_WRITE_TIMEOUT); if (ret != 0) { rig_debug(RIG_DEBUG_ERR, "%s: libusb_control_transfer bitbangmode failed: %s\n", __func__, libusb_error_name(ret)); return -RIG_EIO; } /* * Set baudrate * 9600 x4 because of bitbang mode */ usb_val = 49230; /* magic value for 38400 bauds */ index = 0; ret = libusb_control_transfer(udh, 0x40, 3, usb_val, index, NULL, 0, FTDI_USB_WRITE_TIMEOUT); if (ret != 0) { rig_debug(RIG_DEBUG_ERR, "%s: libusb_control_transfer baudrate failed: %s\n", __func__, libusb_error_name(ret)); return -RIG_EIO; } return RIG_OK; } int elektor507_ftdi_write_data(RIG *rig, void *FTOutBuf, unsigned long BufferSize) { libusb_device_handle *udh = rig->state.rigport.handle; int ret, actual_length; rig_debug(RIG_DEBUG_TRACE, "%s called, %lu bytes\n", __func__, BufferSize); ret = libusb_bulk_transfer(udh, FTDI_IN_EP, FTOutBuf, BufferSize, &actual_length, FTDI_USB_WRITE_TIMEOUT); if (ret < 0) { rig_debug(RIG_DEBUG_ERR, "usb_bulk_write failed: %s\n", libusb_error_name(ret)); return -RIG_EIO; } return RIG_OK; } #endif /* USE_LIBUSB */ #define ELEKTOR507_MODES (RIG_MODE_USB) /* USB is for SDR */ #define ELEKTOR507_FUNC (RIG_FUNC_NONE) #define ELEKTOR507_LEVEL_ALL (RIG_LEVEL_ATT) #define ELEKTOR507_PARM_ALL (RIG_PARM_NONE) #define ELEKTOR507_VFO (RIG_VFO_A) /* * - Auto-filter antenna (K3) * - External antenna (PC1) * - Internal TEST_CLK (5 MHz) */ #define ELEKTOR507_ANT (RIG_ANT_1|RIG_ANT_2|RIG_ANT_3) /* * Elektor SDR USB (5/07) receiver description * * This kit is a QSD based on a CY27EE16ZE PLL. * The receiver is controlled via USB (through FTDI FT232R). * * Original article: * http://www.elektor.com/magazines/2007/may/software-defined-radio.91527.lynkx * * Author (Burkhard Kainka) page, in german: * http://www.b-kainka.de/sdrusb.html */ const struct rig_caps elektor507_caps = { RIG_MODEL(RIG_MODEL_ELEKTOR507), .model_name = "Elektor SDR-USB", .mfg_name = "Elektor", .version = "20200112.0", .copyright = "LGPL", .status = RIG_STATUS_STABLE, .rig_type = RIG_TYPE_TUNER, .ptt_type = RIG_PTT_NONE, .dcd_type = RIG_DCD_NONE, #ifdef USE_LIBUSB .port_type = RIG_PORT_USB, #else .port_type = RIG_PORT_NONE, #endif .serial_rate_min = 9600, .serial_rate_max = 9600, .serial_data_bits = 8, .serial_stop_bits = 1, .serial_parity = RIG_PARITY_NONE, .serial_handshake = RIG_HANDSHAKE_NONE, .write_delay = 0, .post_write_delay = 0, .timeout = 200, .retry = 0, .has_get_func = ELEKTOR507_FUNC, .has_set_func = ELEKTOR507_FUNC, .has_get_level = ELEKTOR507_LEVEL_ALL, .has_set_level = RIG_LEVEL_SET(ELEKTOR507_LEVEL_ALL), .has_get_parm = ELEKTOR507_PARM_ALL, .has_set_parm = RIG_PARM_SET(ELEKTOR507_PARM_ALL), .level_gran = {}, .parm_gran = {}, .ctcss_list = NULL, .dcs_list = NULL, .preamp = { RIG_DBLST_END }, .attenuator = { 10, 20, RIG_DBLST_END }, .max_rit = Hz(0), .max_xit = Hz(0), .max_ifshift = Hz(0), .targetable_vfo = 0, .transceive = RIG_TRN_OFF, .bank_qty = 0, .chan_desc_sz = 0, .chan_list = { RIG_CHAN_END, }, .rx_range_list1 = { {kHz(30), MHz(30) - kHz(1), ELEKTOR507_MODES, -1, -1, ELEKTOR507_VFO, ELEKTOR507_ANT}, RIG_FRNG_END, }, .tx_range_list1 = { RIG_FRNG_END, }, .rx_range_list2 = { {kHz(30), MHz(30) - kHz(1), ELEKTOR507_MODES, -1, -1, ELEKTOR507_VFO, ELEKTOR507_ANT}, RIG_FRNG_END, }, .tx_range_list2 = { RIG_FRNG_END, }, .tuning_steps = { {ELEKTOR507_MODES, kHz(1)}, RIG_TS_END, }, /* mode/filter list, remember: order matters! */ .filters = { {RIG_MODE_USB, kHz(24)}, /* bandpass may be more */ RIG_FLT_END, }, .cfgparams = elektor507_cfg_params, .rig_init = elektor507_init, .rig_cleanup = elektor507_cleanup, .rig_open = elektor507_open, .set_conf = elektor507_set_conf, .get_conf = elektor507_get_conf, .set_freq = elektor507_set_freq, .get_freq = elektor507_get_freq, .set_level = elektor507_set_level, .get_level = elektor507_get_level, .set_ant = elektor507_set_ant, .get_ant = elektor507_get_ant, .get_info = elektor507_get_info, .hamlib_check_rig_caps = HAMLIB_CHECK_RIG_CAPS }; int elektor507_set_conf(RIG *rig, token_t token, const char *val) { struct elektor507_priv_data *priv; freq_t freq; priv = (struct elektor507_priv_data *)rig->state.priv; switch (token) { case TOK_OSCFREQ: sscanf(val, "%"SCNfreq, &freq); priv->osc_freq = freq / kHz(1); break; case TOK_XTALCAL: sscanf(val, "%u", &priv->xtal_cal); break; default: return -RIG_EINVAL; } return RIG_OK; } int elektor507_get_conf2(RIG *rig, token_t token, char *val, int val_len) { struct elektor507_priv_data *priv; priv = (struct elektor507_priv_data *)rig->state.priv; switch (token) { case TOK_OSCFREQ: SNPRINTF(val, val_len, "%"PRIfreq, priv->osc_freq * kHz(1)); break; case TOK_XTALCAL: SNPRINTF(val, val_len, "%u", priv->xtal_cal); break; default: return -RIG_EINVAL; } return RIG_OK; } int elektor507_get_conf(RIG *rig, token_t token, char *val) { return elektor507_get_conf2(rig, token, val, 128); } int elektor507_open(RIG *rig) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int ret; rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); /* * Setup the FT232R. */ #ifdef USE_LIBUSB ret = elektor507_libusb_setup(rig); if (ret != RIG_OK) { return ret; } #endif /* Init the FT232R port to SCL/SDA high, Mux A0, Att 0 */ priv->FT_port = 0x03; /* * Setup the CY27EE16ZE PLL. */ /* Enable only CLOCK5. CLOCK3 will be on demand in set_ant() */ ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, CLKOE_REG, 0x20); if (ret != 0) { return ret; } /* DIV1N set to safe default */ priv->Div1N = 8; ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, DIV1_REG, priv->Div1N); if (ret != 0) { return ret; } #if 0 /* Xtal gain setting */ ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, XTALCTL_REG, 0x32); if (ret != 0) { return ret; } /* CapLoad set to middle */ ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, CAPLOAD_REG, priv->xtal_cal); if (ret != 0) { return ret; } #endif /* CLKSRC: CLOCK3=DIV2CLK/2, CLOCK5=DIV1CLK/DIV1N */ ret = i2c_write_regs(rig, CY_I2C_RAM_ADR, 3, CLKSRC_REG, 0x02, 0x8e, 0x47); if (ret != 0) { return ret; } /* DIV2SRC from REF */ ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, DIV2_REG, 0x88); if (ret != 0) { return ret; } return RIG_OK; } #define FREQ_ALGORITHM 3 /* use AC6SL version 3-Aug-2010 */ #if FREQ_ALGORITHM == 1 /* this used to be ORIG_ALGORITHM */ static void find_P_Q_DIV1N(struct elektor507_priv_data *priv, freq_t freq) { int Freq; double Min, VCO; int p, q, q_max; Freq = freq / kHz(1); if (Freq > 19 && Freq < 60) { priv->Div1N = (2500 + Freq / 2) / Freq + 128; priv->P = 1000; priv->Q = 40; return; } else if (Freq > 59 && Freq < 801) { priv->Div1N = 125; priv->P = Freq * 2; priv->Q = 40; return; } else if (Freq > 800 && Freq < 2001) { priv->Div1N = 50; priv->P = Freq; priv->Q = 50; return; } else if (Freq > 2000 && Freq < 4001) { priv->Div1N = 25; } else if (Freq > 4000 && Freq < 10001) { priv->Div1N = 10; } else if (Freq > 10000 && Freq < 20001) { priv->Div1N = 5; } else if (Freq > 20000 && Freq < 30001) { priv->Div1N = 4; } Min = priv->osc_freq; freq /= kHz(1); /* * Q:2..129 * P:8..2055, best 16..1023 (because of Pump) For stable operation: + REF/Qtotal must not fall below 250kHz ( + P*(REF/Qtotal) must not be above 400 MHz or below 100 MHz */ #if 1 q_max = priv->osc_freq / 250; #else q_max = 100; #endif for (q = q_max; q >= 10; q--) { for (p = 500; p <= 2000; p++) { VCO = ((double)priv->osc_freq / q) * p; if (fabs(4 * freq - VCO / priv->Div1N) < Min) { Min = fabs(4 * freq - VCO / priv->Div1N); priv->Q = q; priv->P = p; } } } VCO = ((double)priv->osc_freq / priv->Q) * priv->P; if (VCO < 100e3 || VCO > 400e3) rig_debug(RIG_DEBUG_VERBOSE, "%s: Unstable parameters for VCO=%.1f\n", __func__, VCO); } #endif /* ORIG_ALGORITHM */ #if FREQ_ALGORITHM == 2 /* this used to be default alternative to ORIG_ALGORITHM */ static void find_P_Q_DIV1N(struct elektor507_priv_data *priv, freq_t freq) { double Min, VCO, freq4; int div1n_min, div1n_max; int p, q, div1n, q_max; Min = priv->osc_freq; freq4 = freq * 4 / kHz(1); #define vco_min 100e3 #define vco_max 500e3 /* * Q:2..129 * P:8..2055, best 16..1023 (because of Pump) For stable operation: + REF/Qtotal must not fall below 250kHz ( + P*(REF/Qtotal) must not be above 400 MHz or below 100 MHz */ #if 1 q_max = priv->osc_freq / 250; #else q_max = 100; #endif div1n_min = vco_min / freq4; if (div1n_min < 2) { div1n_min = 2; } else if (div1n_min > 127) { div1n_min = 127; } div1n_max = vco_max / freq4; if (div1n_max > 127) { div1n_max = 127; } else if (div1n_max < 2) { div1n_max = 2; } for (div1n = div1n_min; div1n <= div1n_max; div1n++) { // P/Qtotal = FREQ4*DIV1N/REF // (Q*int(r) + frac(r)*Q)/Q for (q = q_max; q >= 2; q--) { p = q * freq4 * div1n / priv->osc_freq; #if 1 if (p < 16 || p > 1023) { continue; } #endif VCO = ((double)priv->osc_freq / q) * p; #if 1 if (VCO < vco_min || VCO > vco_max) { continue; } #endif if (fabs(freq4 - VCO / div1n) < Min) { Min = fabs(freq4 - VCO / div1n); priv->Div1N = div1n; priv->Q = q; priv->P = p; } } } VCO = ((double)priv->osc_freq / priv->Q) * priv->P; if (VCO < vco_min || VCO > 400e3) rig_debug(RIG_DEBUG_VERBOSE, "%s: Unstable parameters for VCO=%.1f\n", __func__, VCO); } #endif /* default alternative to ORIG_ALGORITHM */ #if FREQ_ALGORITHM == 3 /* AC6SL version 5-Aug-2010 */ /* search valid (P,Q,N) for closest match to requested frequency */ static void find_P_Q_DIV1N( struct elektor507_priv_data *priv, freq_t freq) { #define VCO_MIN 100000000 #define VCO_MAX 400000000 int Ptotal; int Qtotal, Qmax = 40; int Div1N; double PmulREFdivQ; double Ref = priv->osc_freq * 1000.0; double freq4 = freq * 4; double newdelta, delta = fabs((priv->P * (Ref / priv->Q) / priv->Div1N) - freq4); /* For stable operation: Ref/Qtotal must not fall below 250kHz */ /* Qmax = (int) ( Ref / 250000); */ for (Qtotal = 2; Qtotal <= Qmax; Qtotal++) { double REFdivQ = (Ref / Qtotal); /* For stable operation: Ptotal*(Ref/Qtotal) must be ... */ int Pmin = (int)(VCO_MIN / REFdivQ); /* ... >= 100mHz */ int Pmax = (int)(VCO_MAX / REFdivQ); /* ... <= 400mHz */ for (Ptotal = Pmin; Ptotal <= Pmax; Ptotal++) { PmulREFdivQ = Ptotal * REFdivQ; Div1N = (int)((PmulREFdivQ + freq4 / 2) / freq4); if (Div1N < 2) { Div1N = 2; } if (Div1N > 127) { Div1N = 127; } newdelta = fabs((PmulREFdivQ / Div1N) - freq4); if (newdelta < delta) { /* save best (P,Q,N) */ delta = newdelta; priv->P = Ptotal; priv->Q = Qtotal; priv->Div1N = Div1N; } } } } #endif /* AC6SL version 5-Aug-2010 */ int elektor507_set_freq(RIG *rig, vfo_t vfo, freq_t freq) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; freq_t final_freq; int ret = 0; if (priv->ant == ANT_AUTO) { int Mux; /* Automatically select appropriate filter */ if (freq <= kHz(1600)) { /* Select A1, low pass, fc=1.6MHz */ Mux = 1; } else { /* Select A2, high pass */ Mux = 2; } priv->FT_port &= 0x63; //0,1 = I2C, 2,3,4=MUX, 5,6=Attenuator priv->FT_port |= Mux << 2; } find_P_Q_DIV1N(priv, freq); /* Compute PLL parameters */ elektor507_get_freq(rig, vfo, &final_freq); rig_debug(RIG_DEBUG_VERBOSE, "%s: Freq=%.0f kHz, delta=%d Hz, Div1N=%d, P=%d, Q=%d, FREQ_ALGORITHM=%d\n", __func__, freq / kHz(1), (int)(final_freq - freq), priv->Div1N, priv->P, priv->Q, FREQ_ALGORITHM); if ((double)priv->osc_freq / priv->Q < 250) rig_debug(RIG_DEBUG_WARN, "%s: Unstable parameters for REF/Qtotal=%.1f\n", __func__, (double)priv->osc_freq / priv->Q); ret = cy_update_pll(rig, CY_I2C_RAM_ADR); return (ret != 0) ? -RIG_EIO : RIG_OK; } int elektor507_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; double VCO; VCO = ((double)priv->osc_freq * kHz(1)) / priv->Q * priv->P; /* Div by 4 because of QSD */ *freq = (VCO / priv->Div1N) / 4; return RIG_OK; } int elektor507_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int ret = 0; int att = 0; switch (level) { case RIG_LEVEL_ATT: /* val.i */ /* FTDI: DSR, DCD */ switch (val.i) { case 0: att = 0; break; case 10: att = 1; break; case 20: att = 2; break; default: return -RIG_EINVAL; } priv->FT_port &= 0x1f; priv->FT_port |= (att & 0x3) << 5; ret = elektor507_ftdi_write_data(rig, &priv->FT_port, 1); break; default: return -RIG_EINVAL; } return (ret != 0) ? -RIG_EIO : RIG_OK; } int elektor507_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int ret = 0; switch (level) { case RIG_LEVEL_ATT: switch ((priv->FT_port >> 5) & 3) { case 0: val->i = 0; break; case 1: val->i = 10; break; case 2: val->i = 20; break; default: ret = -RIG_EINVAL; } break; default: return -RIG_EINVAL; } return (ret != 0) ? -RIG_EIO : RIG_OK; } int elektor507_set_ant(RIG *rig, vfo_t vfo, ant_t ant, value_t option) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int ret, Mux; rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); /* * FTDI: RTS, CTS, DTR * * A4,A5,A6 are not connected * * ANT1->A1/A2, ANT2->A3, ANT3->A7 */ switch (ant) { case RIG_ANT_1: Mux = 0; break; /* Mux will be updated upon next set_freq */ case RIG_ANT_2: Mux = 3; break; /* ANT_EXT */ case RIG_ANT_3: Mux = 7; break; /* ANT_TEST_CLK */ default: return -RIG_EINVAL; } priv->ant = ant; priv->FT_port &= 0x63; //0,1 = I2C, 2,3,4=MUX, 5,6=Attenuator priv->FT_port |= Mux << 2; #if 0 ret = elektor507_ftdi_write_data(rig, &priv->FT_port, 1); #else /* Enable CLOCK3 on demand */ ret = i2c_write_reg(rig, CY_I2C_RAM_ADR, CLKOE_REG, 0x20 | (ant == RIG_ANT_3 ? 0x04 : 0)); #endif return (ret != 0) ? -RIG_EIO : RIG_OK; } int elektor507_get_ant(RIG *rig, vfo_t vfo, ant_t dummy, value_t *option, ant_t *ant_curr, ant_t *ant_tx, ant_t *ant_rx) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; *ant_curr = priv->ant; return RIG_OK; } /* * Update the PLL counters */ static int cy_update_pll(RIG *rig, unsigned char IICadr) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int P0, R40, R41, R42; unsigned char Div1N; unsigned char Clk3_src; int Pump; int ret; /* * PLL Pump setting according to table 9 */ if (priv->P < 45) { Pump = 0; } else if (priv->P < 480) { Pump = 1; } else if (priv->P < 640) { Pump = 2; } else if (priv->P < 800) { Pump = 3; } else { Pump = 4; } P0 = priv->P & 0x01; R40 = (((priv->P >> 1) - 4) >> 8) | (Pump << 2) | 0xc0; R41 = ((priv->P >> 1) - 4) & 0xff; R42 = (priv->Q - 2) | (P0 << 7); ret = i2c_write_regs(rig, IICadr, 3, PUMPCOUNTERS_REG, R40, R41, R42); if (ret != 0) { return ret; } switch (priv->Div1N) { case 2: /* Fixed /2 divider option */ Clk3_src = 0x80; Div1N = 8; break; case 3: /* Fixed /3 divider option */ Clk3_src = 0xc0; Div1N = 6; break; default: Div1N = priv->Div1N; Clk3_src = 0x40; } ret = i2c_write_reg(rig, IICadr, DIV1_REG, Div1N); if (ret != 0) { return ret; } /* Set 2 low bits of CLKSRC for CLOCK5. DIV1CLK is set already */ ret = i2c_write_reg(rig, IICadr, CLKSRC_REG + 2, Clk3_src | 0x07); if (ret != 0) { return ret; } return RIG_OK; } static void ftdi_SCL(RIG *rig, int d) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; if (priv->Buf_adr >= FT_OUT_BUFFER_MAX) { return; } /* * FTDI RXD->SCL */ if (d == 0) { priv->FT_port &= ~0x02; } else { priv->FT_port |= 0x02; } priv->FT_Out_Buffer[priv->Buf_adr++] = priv->FT_port; } static void ftdi_SDA(RIG *rig, int d) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; if (priv->Buf_adr >= FT_OUT_BUFFER_MAX) { return; } /* * FTDI TXD->SDA */ if (d == 0) { priv->FT_port &= ~0x01; } else { priv->FT_port |= 0x01; } priv->FT_Out_Buffer[priv->Buf_adr++] = priv->FT_port; } static void ftdi_I2C_Init(RIG *rig) { ftdi_SCL(rig, 1); ftdi_SDA(rig, 1); /* SCL=1, SDA=1 */ } static void ftdi_I2C_Start(RIG *rig) { ftdi_SDA(rig, 0); /* SDA=0 */ ftdi_SCL(rig, 0); /* SCL=0 */ } static void ftdi_I2C_Stop(RIG *rig) { ftdi_SCL(rig, 0); ftdi_SDA(rig, 0); /* SCL=0, SDA=0 */ ftdi_SCL(rig, 1); /* SCL=1 */ ftdi_SDA(rig, 1); /* SDA=1 */ } /* Acknowledge: SCL=0, SDA=0 SCL=1 SCL=0 No Acknowledge: SCL=0, SDA=1 SCL=1 SCL=0 */ static void ftdi_I2C_Write_Byte(RIG *rig, unsigned char c) { int i; for (i = 7; i >= 0; i--) { ftdi_SDA(rig, c & (1 << i)); /* SDA value */ ftdi_SCL(rig, 1); ftdi_SCL(rig, 0); } ftdi_SDA(rig, 1); ftdi_SCL(rig, 1); ftdi_SCL(rig, 0); } int i2c_write_regs(RIG *rig, unsigned char IICadr, int reg_count, unsigned char reg_adr, unsigned char reg_val1, unsigned char reg_val2, unsigned char reg_val3) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int ret; /* Start with a new buffer */ priv->Buf_adr = 0; ftdi_I2C_Init(rig); ftdi_I2C_Start(rig); ftdi_I2C_Write_Byte(rig, IICadr); ftdi_I2C_Write_Byte(rig, reg_adr); if (reg_count >= 1) { ftdi_I2C_Write_Byte(rig, reg_val1); } if (reg_count >= 2) { ftdi_I2C_Write_Byte(rig, reg_val2); } if (reg_count >= 3) { ftdi_I2C_Write_Byte(rig, reg_val3); } ftdi_I2C_Stop(rig); //usleep(10000); ret = elektor507_ftdi_write_data(rig, priv->FT_Out_Buffer, priv->Buf_adr); if (ret != 0) { return -RIG_EIO; } return 0; } #if 0 static const unsigned char ftdi_code[256] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x34, 0x08, 0x5a, 0x24/*0x6f*/, 0x00, 0x14, 0x0a, 0x00, 0x08, 0x88, 0x50, 0x04, 0x32, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x2b, 0x17, 0x00, 0xfe, 0xfe, 0x7f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32, 0x10, 0x32 }; int load_ftdi_code(RIG *rig, unsigned char IICadr, const unsigned char code[]) { struct elektor507_priv_data *priv = (struct elektor507_priv_data *) rig->state.priv; int ret; int i, j; rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); for (i = 0; i < 16; i++) { /* Start with a new buffer */ priv->Buf_adr = 0; ftdi_I2C_Init(rig); ftdi_I2C_Start(rig); ftdi_I2C_Write_Byte(rig, IICadr); ftdi_I2C_Write_Byte(rig, i * 16); for (j = 0; j < 16; j++) { ftdi_I2C_Write_Byte(rig, code[i * 16 + j]); } ftdi_I2C_Stop(rig); ret = elektor507_ftdi_write_data(rig, priv->FT_Out_Buffer, priv->Buf_adr); if (ret != 0) { return -RIG_EIO; } } return RIG_OK; } #endif #endif /* defined(USE_FTDI_DLL) || defined(USE_LIBUSB) */