/** * MIT License * * Copyright (c) 2020 Jimmy Pentz * * Reach me at: github.com/jgpentz, jpentz1(at)gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sells * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* ADAR1000 4-Channel, X Band and Ku Band Beamformer */ // ---------------------------------------------------------------------------- // Includes // ---------------------------------------------------------------------------- #include "main.h" #include "stm32f7xx_hal.h" #include "stm32f7xx_hal_spi.h" #include "stm32f7xx_hal_gpio.h" #include "adar1000.h" // ---------------------------------------------------------------------------- // Preprocessor Definitions and Constants // ---------------------------------------------------------------------------- // VM_GAIN is 15 dB of gain in 128 steps. ~0.12 dB per step. // A 15 dB attenuator can be applied on top of these values. const uint8_t VM_GAIN[128] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, }; // VM_I and VM_Q are the settings for the vector modulator. 128 steps in 360 degrees. ~2.813 degrees per step. const uint8_t VM_I[128] = { 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3E, 0x3E, 0x3D, 0x3D, 0x3C, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x30, 0x2F, 0x2E, 0x2C, 0x2B, 0x2A, 0x28, 0x27, 0x25, 0x24, 0x22, 0x21, 0x01, 0x03, 0x04, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1C, 0x1D, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1D, 0x1C, 0x1C, 0x1B, 0x1A, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x10, 0x0F, 0x0E, 0x0C, 0x0B, 0x0A, 0x08, 0x07, 0x05, 0x04, 0x02, 0x01, 0x21, 0x23, 0x24, 0x26, 0x27, 0x28, 0x2A, 0x2B, 0x2D, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3C, 0x3C, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, }; const uint8_t VM_Q[128] = { 0x20, 0x21, 0x23, 0x24, 0x26, 0x27, 0x28, 0x2A, 0x2B, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3A, 0x3A, 0x3B, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3D, 0x3C, 0x3C, 0x3C, 0x3B, 0x3A, 0x3A, 0x39, 0x38, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x31, 0x30, 0x2F, 0x2E, 0x2D, 0x2B, 0x2A, 0x28, 0x27, 0x26, 0x24, 0x23, 0x21, 0x20, 0x01, 0x03, 0x04, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x19, 0x1A, 0x1A, 0x1B, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1D, 0x1C, 0x1C, 0x1C, 0x1B, 0x1A, 0x1A, 0x19, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0B, 0x0A, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, }; // ---------------------------------------------------------------------------- // Function Definitions // ---------------------------------------------------------------------------- /** * @brief Initialize the ADC on the ADAR by setting the ADC with a 2 MHz clk, * and then enable it. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @warning This is setup to only read temperature sensor data, not the power detectors. */ void Adar_AdcInit(const AdarDevice * p_adar, uint8_t broadcast) { uint8_t data; data = ADAR1000_ADC_2MHZ_CLK | ADAR1000_ADC_EN; Adar_Write(p_adar, REG_ADC_CONTROL, data, broadcast); } /** * @brief Read a byte of data from the ADAR. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns a byte of data that has been converted from the temperature sensor. * * @warning This is setup to only read temperature sensor data, not the power detectors. */ uint8_t Adar_AdcRead(const AdarDevice * p_adar, uint8_t broadcast) { uint8_t data; // Start the ADC conversion Adar_Write(p_adar, REG_ADC_CONTROL, ADAR1000_ADC_ST_CONV, broadcast); // This is blocking for now... wait until data is converted, then read it while (!(Adar_Read(p_adar, REG_ADC_CONTROL) & 0x01)) { } data = Adar_Read(p_adar, REG_ADC_OUT); return(data); } /** * @brief Requests the device info from a specific ADAR and stores it in the * provided AdarDeviceInfo struct. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param info[out] Struct that contains the device info fields. * * @return Returns ADAR_ERROR_NOERROR if information was successfully received and stored in the struct. */ uint8_t Adar_GetDeviceInfo(const AdarDevice * p_adar, AdarDeviceInfo * info) { *((uint8_t *)info) = Adar_Read(p_adar, 0x002); info->chip_type = Adar_Read(p_adar, 0x003); info->product_id = ((uint16_t)Adar_Read(p_adar, 0x004)) << 8; info->product_id |= ((uint16_t)Adar_Read(p_adar, 0x005)) & 0x00ff; info->scratchpad = Adar_Read(p_adar, 0x00A); info->spi_rev = Adar_Read(p_adar, 0x00B); info->vendor_id = ((uint16_t)Adar_Read(p_adar, 0x00C)) << 8; info->vendor_id |= ((uint16_t)Adar_Read(p_adar, 0x00D)) & 0x00ff; info->rev_id = Adar_Read(p_adar, 0x045); return(ADAR_ERROR_NOERROR); } /** * @brief Read the data that is stored in a single ADAR register. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param mem_addr Memory address of the register you wish to read from. * * @return Returns the byte of data that is stored in the desired register. * * @warning This function will clear ADDR_ASCN bits. * @warning The ADAR does not allow for block reads. */ uint8_t Adar_Read(const AdarDevice * p_adar, uint32_t mem_addr) { uint8_t instruction[3]; // Set SDO active Adar_Write(p_adar, REG_INTERFACE_CONFIG_A, INTERFACE_CONFIG_A_SDO_ACTIVE, 0); instruction[0] = 0x80 | ((p_adar->dev_addr & 0x03) << 5); instruction[0] |= ((0xff00 & mem_addr) >> 8); instruction[1] = (0xff & mem_addr); instruction[2] = 0x00; p_adar->Transfer(instruction, p_adar->p_rx_buffer, ADAR1000_RD_SIZE); // Set SDO Inactive Adar_Write(p_adar, REG_INTERFACE_CONFIG_A, 0, 0); return(p_adar->p_rx_buffer[2]); } /** * @brief Block memory write to an ADAR device. * * @pre ADDR_ASCN bits in register zero must be set! * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param mem_addr Memory address of the register you wish to read from. * @param p_data Pointer to block of data to transfer (must have two unused bytes preceding the data for instruction). * @param size Size of data in bytes, including the two additional leading bytes. * * @warning First two bytes of data will be corrupted if you do not provide two unused leading bytes! */ void Adar_ReadBlock(const AdarDevice * p_adar, uint16_t mem_addr, uint8_t * p_data, uint32_t size) { // Set SDO active Adar_Write(p_adar, REG_INTERFACE_CONFIG_A, INTERFACE_CONFIG_A_SDO_ACTIVE | INTERFACE_CONFIG_A_ADDR_ASCN, 0); // Prepare command p_data[0] = 0x80 | ((p_adar->dev_addr & 0x03) << 5); p_data[0] |= ((mem_addr) >> 8) & 0x1F; p_data[1] = (0xFF & mem_addr); // Start the transfer p_adar->Transfer(p_data, p_data, size); Adar_Write(p_adar, REG_INTERFACE_CONFIG_A, 0, 0); // Return nothing since we assume this is non-blocking and won't wait around } /** * @brief Sets the Rx/Tx bias currents for the LNA, VM, and VGA to be in either * low power setting or nominal setting. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param p_bias[in] An AdarBiasCurrents struct filled with bias settings * as seen in the datasheet Table 6. SPI Settings for * Different Power Modules * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERR_NOERROR if the bias currents were set */ uint8_t Adar_SetBiasCurrents(const AdarDevice * p_adar, AdarBiasCurrents * p_bias, uint8_t broadcast) { uint8_t bias = 0; // RX LNA/VGA/VM bias bias = (p_bias->rx_lna & 0x0f); Adar_Write(p_adar, REG_BIAS_CURRENT_RX_LNA, bias, broadcast); // RX LNA bias bias = (p_bias->rx_vga & 0x07 << 3) | (p_bias->rx_vm & 0x07); Adar_Write(p_adar, REG_BIAS_CURRENT_RX, bias, broadcast); // RX VM/VGA bias // TX VGA/VM/DRV bias bias = (p_bias->tx_vga & 0x07 << 3) | (p_bias->tx_vm & 0x07); Adar_Write(p_adar, REG_BIAS_CURRENT_TX, bias, broadcast); // TX VM/VGA bias bias = (p_bias->tx_drv & 0x07); Adar_Write(p_adar, REG_BIAS_CURRENT_TX_DRV, bias, broadcast); // TX DRV bias return(ADAR_ERROR_NOERROR); } /** * @brief Set the bias ON and bias OFF voltages for the four PA's and one LNA. * * @pre This will set all 5 bias ON values and all 5 bias OFF values at once. * To enable these bias values, please see the data sheet and ensure that the BIAS_CTRL, * LNA_BIAS_OUT_EN, TR_SOURCE, TX_EN, RX_EN, TR (input to chip), and PA_ON (input to chip) * bits have all been properly set. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param bias_on_voltage Array that contains the bias ON voltages. * @param bias_off_voltage Array that contains the bias OFF voltages. * * @return Returns ADAR_ERR_NOERROR if the bias currents were set */ uint8_t Adar_SetBiasVoltages(const AdarDevice * p_adar, uint8_t bias_on_voltage[5], uint8_t bias_off_voltage[5]) { Adar_SetBit(p_adar, 0x30, 6, BROADCAST_OFF); Adar_SetBit(p_adar, 0x31, 2, BROADCAST_OFF); Adar_SetBit(p_adar, 0x38, 5, BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH1_BIAS_ON,bias_on_voltage[0], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH2_BIAS_ON,bias_on_voltage[1], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH3_BIAS_ON,bias_on_voltage[2], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH4_BIAS_ON,bias_on_voltage[3], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH1_BIAS_OFF,bias_off_voltage[0], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH2_BIAS_OFF,bias_off_voltage[1], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH3_BIAS_OFF,bias_off_voltage[2], BROADCAST_OFF); Adar_Write(p_adar, REG_PA_CH4_BIAS_OFF,bias_off_voltage[3], BROADCAST_OFF); Adar_SetBit(p_adar, 0x30, 4, BROADCAST_OFF); Adar_SetBit(p_adar, 0x30, 6, BROADCAST_OFF); Adar_SetBit(p_adar, 0x31, 2, BROADCAST_OFF); Adar_SetBit(p_adar, 0x38, 5, BROADCAST_OFF); Adar_Write(p_adar, REG_LNA_BIAS_ON,bias_on_voltage[4], BROADCAST_OFF); Adar_Write(p_adar, REG_LNA_BIAS_OFF,bias_off_voltage[4], BROADCAST_OFF); Adar_ResetBit(p_adar, 0x30, 7, BROADCAST_OFF); Adar_SetBit(p_adar, 0x31, 2, BROADCAST_OFF); Adar_SetBit(p_adar, 0x31, 4, BROADCAST_OFF); Adar_SetBit(p_adar, 0x31, 7, BROADCAST_OFF); return(ADAR_ERROR_NOERROR); } /** * @brief Setup the ADAR to use settings that are transferred over SPI. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERR_NOERROR if the bias currents were set */ uint8_t Adar_SetRamBypass(const AdarDevice * p_adar, uint8_t broadcast) { uint8_t data; data = (MEM_CTRL_BIAS_RAM_BYPASS | MEM_CTRL_BEAM_RAM_BYPASS); Adar_Write(p_adar, REG_MEM_CTL, data, broadcast); return(ADAR_ERROR_NOERROR); } /** * @brief Set the VGA gain value of a Receive channel in dB. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param channel Channel in which to set the gain (1-4). * @param vga_gain_db Gain to be applied to the channel, ranging from 0 - 30 dB. * (Intended operation >16 dB). * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERROR_NOERROR if the gain was successfully set. * ADAR_ERROR_FAILED if an invalid channel was selected. * * @warning 0 dB or 15 dB step attenuator may also be turned on, which is why intended operation is >16 dB. */ uint8_t Adar_SetRxVgaGain(const AdarDevice * p_adar, uint8_t channel, uint8_t vga_gain_db, uint8_t broadcast) { uint8_t vga_gain_bits = (uint8_t)(255*vga_gain_db/16); uint32_t mem_addr = 0; if((channel == 0) || (channel > 4)) { return(ADAR_ERROR_FAILED); } mem_addr = REG_CH1_RX_GAIN + (channel & 0x03); // Set gain Adar_Write(p_adar, mem_addr, vga_gain_bits, broadcast); // Load the new setting Adar_Write(p_adar, REG_LOAD_WORKING, 0x1, broadcast); return(ADAR_ERROR_NOERROR); } /** * @brief Set the phase of a given receive channel using the I/Q vector modulator. * * @pre According to the given @param phase, this sets the polarity (bit 5) and gain (bits 4-0) * of the @param channel, and then loads them into the working register. * A vector modulator I/Q look-up table has been provided at the beginning of this library. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param channel Channel in which to set the gain (1-4). * @param phase Byte that is used to set the polarity (bit 5) and gain (bits 4-0). * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERROR_NOERROR if the phase was successfully set. * ADAR_ERROR_FAILED if an invalid channel was selected. * * @note To obtain your phase: * phase = degrees * 128; * phase /= 360; */ uint8_t Adar_SetRxPhase(const AdarDevice * p_adar, uint8_t channel, uint8_t phase, uint8_t broadcast) { uint8_t i_val = 0; uint8_t q_val = 0; uint32_t mem_addr_i, mem_addr_q; if((channel == 0) || (channel > 4)) { return(ADAR_ERROR_FAILED); } //phase = phase % 128; i_val = VM_I[phase]; q_val = VM_Q[phase]; mem_addr_i = REG_CH1_RX_PHS_I + (channel & 0x03) * 2; mem_addr_q = REG_CH1_RX_PHS_Q + (channel & 0x03) * 2; Adar_Write(p_adar, mem_addr_i, i_val, broadcast); Adar_Write(p_adar, mem_addr_q, q_val, broadcast); Adar_Write(p_adar, REG_LOAD_WORKING, 0x1, broadcast); return(ADAR_ERROR_NOERROR); } /** * @brief Set the VGA gain value of a Tx channel in dB. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERROR_NOERROR if the bias was successfully set. * ADAR_ERROR_FAILED if an invalid channel was selected. * * @warning 0 dB or 15 dB step attenuator may also be turned on, which is why intended operation is >16 dB. */ uint8_t Adar_SetTxBias(const AdarDevice * p_adar, uint8_t broadcast) { uint8_t vga_bias_bits; uint8_t drv_bias_bits; uint32_t mem_vga_bias; uint32_t mem_drv_bias; mem_vga_bias = REG_BIAS_CURRENT_TX; mem_drv_bias = REG_BIAS_CURRENT_TX_DRV; // Set bias to nom vga_bias_bits = 0x2D; drv_bias_bits = 0x06; // Set bias Adar_Write(p_adar, mem_vga_bias, vga_bias_bits, broadcast); // Set bias Adar_Write(p_adar, mem_drv_bias, drv_bias_bits, broadcast); // Load the new setting Adar_Write(p_adar, REG_LOAD_WORKING, 0x2, broadcast); return(ADAR_ERROR_NOERROR); } /** * @brief Set the VGA gain value of a Tx channel. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param channel Tx channel in which to set the gain, ranging from 1 - 4. * @param gain Gain to be applied to the channel, ranging from 0 - 127, * plus the MSb 15dB attenuator (Intended operation >16 dB). * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERROR_NOERROR if the gain was successfully set. * ADAR_ERROR_FAILED if an invalid channel was selected. * * @warning 0 dB or 15 dB step attenuator may also be turned on, which is why intended operation is >16 dB. */ uint8_t Adar_SetTxVgaGain(const AdarDevice * p_adar, uint8_t channel, uint8_t gain, uint8_t broadcast) { uint32_t mem_addr; if((channel == 0) || (channel > 4)) { return(ADAR_ERROR_FAILED); } mem_addr = REG_CH1_TX_GAIN + (channel & 0x03); // Set gain Adar_Write(p_adar, mem_addr, gain, broadcast); // Load the new setting Adar_Write(p_adar, REG_LOAD_WORKING, LD_WRK_REGS_LDTX_OVERRIDE, broadcast); return(ADAR_ERROR_NOERROR); } /** * @brief Set the phase of a given transmit channel using the I/Q vector modulator. * * @pre According to the given @param phase, this sets the polarity (bit 5) and gain (bits 4-0) * of the @param channel, and then loads them into the working register. * A vector modulator I/Q look-up table has been provided at the beginning of this library. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param channel Channel in which to set the gain (1-4). * @param phase Byte that is used to set the polarity (bit 5) and gain (bits 4-0). * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. * * @return Returns ADAR_ERROR_NOERROR if the phase was successfully set. * ADAR_ERROR_FAILED if an invalid channel was selected. * * @note To obtain your phase: * phase = degrees * 128; * phase /= 360; */ uint8_t Adar_SetTxPhase(const AdarDevice * p_adar, uint8_t channel, uint8_t phase, uint8_t broadcast) { uint8_t i_val = 0; uint8_t q_val = 0; uint32_t mem_addr_i, mem_addr_q; if((channel == 0) || (channel > 4)) { return(ADAR_ERROR_FAILED); } //phase = phase % 128; i_val = VM_I[phase]; q_val = VM_Q[phase]; mem_addr_i = REG_CH1_TX_PHS_I + (channel & 0x03) * 2; mem_addr_q = REG_CH1_TX_PHS_Q + (channel & 0x03) * 2; Adar_Write(p_adar, mem_addr_i, i_val, broadcast); Adar_Write(p_adar, mem_addr_q, q_val, broadcast); Adar_Write(p_adar, REG_LOAD_WORKING, 0x1, broadcast); return(ADAR_ERROR_NOERROR); } /** * @brief Reset the whole ADAR device. * * @param p_adar[in] ADAR pointer Which specifies the device and what function * to use for SPI transfer. */ void Adar_SoftReset(const AdarDevice * p_adar) { uint8_t instruction[3]; instruction[0] = ((p_adar->dev_addr & 0x03) << 5); instruction[1] = 0x00; instruction[2] = 0x81; p_adar->Transfer(instruction, NULL, sizeof(instruction)); } /** * @brief Reset ALL ADAR devices in the SPI chain. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. */ void Adar_SoftResetAll(const AdarDevice * p_adar) { uint8_t instruction[3]; instruction[0] = 0x08; instruction[1] = 0x00; instruction[2] = 0x81; p_adar->Transfer(instruction, NULL, sizeof(instruction)); } /** * @brief Write a byte of @param data to the register located at @param mem_addr. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param mem_addr Memory address of the register you wish to read from. * @param data Byte of data to be stored in the register. * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain if this set to BROADCAST_ON. * * @warning If writing the same data to multiple registers, use ADAR_WriteBlock. */ void Adar_Write(const AdarDevice * p_adar, uint32_t mem_addr, uint8_t data, uint8_t broadcast) { uint8_t instruction[3]; if (broadcast) { instruction[0] = 0x08; } else { instruction[0] = ((p_adar->dev_addr & 0x03) << 5); } instruction[0] |= (0x1F00 & mem_addr) >> 8; instruction[1] = (0xFF & mem_addr); instruction[2] = data; p_adar->Transfer(instruction, NULL, sizeof(instruction)); } /** * @brief Block memory write to an ADAR device. * * @pre ADDR_ASCN BITS IN REGISTER ZERO MUST BE SET! * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param mem_addr Memory address of the register you wish to read from. * @param p_data[in] Pointer to block of data to transfer (must have two unused bytes preceding the data for instruction). * @param size Size of data in bytes, including the two additional leading bytes. * * @warning First two bytes of data will be corrupted if you do not provide two unused leading bytes! */ void Adar_WriteBlock(const AdarDevice * p_adar, uint16_t mem_addr, uint8_t * p_data, uint32_t size) { // Prepare command p_data[0] = ((p_adar->dev_addr & 0x03) << 5); p_data[0] |= ((mem_addr) >> 8) & 0x1F; p_data[1] = (0xFF & mem_addr); // Start the transfer p_adar->Transfer(p_data, NULL, size); // Return nothing since we assume this is non-blocking and won't wait around } /** * @brief Set contents of the INTERFACE_CONFIG_A register. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param flags #INTERFACE_CONFIG_A_SOFTRESET, #INTERFACE_CONFIG_A_LSB_FIRST, * #INTERFACE_CONFIG_A_ADDR_ASCN, #INTERFACE_CONFIG_A_SDO_ACTIVE * @param broadcast Send the message as a broadcast to all ADARs in the SPI chain * if this set to BROADCAST_ON. */ void Adar_WriteConfigA(const AdarDevice * p_adar, uint8_t flags, uint8_t broadcast) { Adar_Write(p_adar, 0x00, flags, broadcast); } /** * @brief Write a byte of @param data to the register located at @param mem_addr and * then read from the device and verify that the register was correctly set. * * @param p_adar[in] Adar pointer Which specifies the device and what function * to use for SPI transfer. * @param mem_addr Memory address of the register you wish to read from. * @param data Byte of data to be stored in the register. * * @return Returns the number of attempts that it took to successfully write to a register, * starting from zero. * @warning This function currently only supports writes to a single regiter in a single ADAR. */ uint8_t Adar_WriteVerify(const AdarDevice * p_adar, uint32_t mem_addr, uint8_t data) { uint8_t rx_data; for (uint8_t ii = 0; ii < 3; ii++) { Adar_Write(p_adar, mem_addr, data, 0); // Can't read back from an ADAR with HW address 0 if (!((p_adar->dev_addr) % 4)) { return(ADAR_ERROR_INVALIDADDR); } rx_data = Adar_Read(p_adar, mem_addr); if (rx_data == data) { return(ii); } } return(ADAR_ERROR_FAILED); } void Adar_SetBit(const AdarDevice * p_adar, uint32_t mem_addr, uint8_t bit, uint8_t broadcast) { uint8_t temp = Adar_Read(p_adar, mem_addr); uint8_t data = temp|(1<