/** * \file * \brief Implementation of the Driver for the AD5791 * Digital to Analog Converter Board * * * The board inverts all digital inputs. */ #include "AD5791.h" #include "../../core/states.h" #include #include #include #include #include #include using std::reverse; using std::cout; using std::vector; #ifndef TIMING #define TIMING 9e-8 #endif // The bit depth of the DAC #define DAC_DATA_BITS 20 #define DAC_CONTROL_BITS 4 // The default channel configuration #define LE_BIT 17 #define DATA_BIT 18 #define CLK_BIT 16 // Input shift register addresses // Write to DAC register 0001 #define WRITE_DAC 1 // Write to the control register 0010 #define WRITE_CONTROL_REGISTER 2 // Write to the clearcode register 0011 #define WRITE_CLEARCODE_REGISTER 3 // Write to the software control register 0100 #define WRITE_SOFTWARE_CONTROL_REGISTER 4 // software control register settings // Setting this bit to a 1 returns the AD5791 to its power-on state. #define RESET 1 // Setting this bit to a 1 sets the DAC register to a user defined value (see Table 13) and updates the DAC output. The output // value depends on the DAC register coding that is being used, either binary or twos complement. #define CLR 2 // Setting this bit to a 1 updates the DAC register and consequently the DAC output. #define LDAC 4 // control register settings // Linearity error compensation for varying reference input spans. #define LINCOMP_10V 0*1<<9 + 0*1<<8 + 0*1<<7 + 0*1<<6 #define LINCOMP_12V 1*1<<9 + 0*1<<8 + 0*1<<7 + 1*1<<6 #define LINCOMP_16V 1*1<<9 + 0*1<<8 + 1*1<<7 + 0*1<<6 #define LINCOMP_19V 1*1<<9 + 0*1<<8 + 1*1<<7 + 1*1<<6 #define LINCOMP_20V 1*1<<9 + 1*1<<8 + 0*1<<7 + 0*1<<6 // SDO pin enable/disable control. #define SDODIS 32 // DAC register coding select. #define DAC_CODING_SELECT 16 // DAC tristate control. #define DACTRI 8 // Output ground clamp control. (set by default) #define OPGND 4 // Output amplifier configuration control. (set by default) #define RBUF 2 AD5791::AD5791(int myid) : id(myid) { dac_value = 0; set_latch_bit(LE_BIT); set_data_bit(DATA_BIT); set_clock_bit(CLK_BIT); } AD5791::~AD5791() {} // This sets the dac_value void AD5791::set_dac(signed dw) { dac_value = dw; } void AD5791::set_latch_bit(int bit) { latch_bit = bit; } void AD5791::set_data_bit(int bit) { data_bit = bit; } void AD5791::set_clock_bit(int bit) { clock_bit = bit; } // This sets the DAC void AD5791::set_dac(state &experiment) { state_sequent *exp_sequence = dynamic_cast(&experiment); if (exp_sequence == NULL) // is a very state on top level, todo: change interface throw pfg_exception("cannot work on a single state, sorry (todo: change interface)"); else { for (state_sequent::iterator child_state = exp_sequence->begin(); child_state != exp_sequence->end(); ++child_state) { set_dac_recursive(*exp_sequence, child_state); } std::cout << "first state"<< std::endl; // Set DAC to 0 at start of experiment set_dac_to_zero(exp_sequence, exp_sequence->begin()); // but first initialize DAC init_dac(exp_sequence, exp_sequence->begin()); // And at the end of the experiment set DAC to zero set_dac_to_zero(exp_sequence, exp_sequence->end()); } } /** * push one bit to the DAC, this function returns a state_sequent one can add * * @param bit * @return */ state_sequent* AD5791::tx_bit(unsigned int bit) { state_sequent* tx = new state_sequent(); ttlout* ttl_state = new ttlout(); state s(TIMING); s.push_back(ttl_state); ttl_state->ttls = bit*(1<push_back(s.copy_new()); ttl_state->ttls = (1<push_back(s.copy_new()); return tx; } state_sequent* AD5791::select_input_shift_register(unsigned int register_address){ state_sequent* tx = new state_sequent(); int count = DAC_CONTROL_BITS; while (register_address != 0 and count !=0 ) { tx->push_back(tx_bit(register_address & 1)); register_address >>= 1; count--; } return tx; } state_sequent* AD5791::write_serial_register(unsigned int register_value){ state_sequent* tx = new state_sequent(); vector dac_word; // generate the bit pattern, this is MSB last for (int j = 0; j < DAC_DATA_BITS; j++) { int bit = register_value & 1; dac_word.push_back(bit); register_value >>= 1; } // iterate over the dac_word from end to beginning, this is MSB first // TODO: Run Length Encoding (maybe better on higher level, i.e. full state sequence?) for (vector::reverse_iterator current_bit = dac_word.rbegin(); current_bit != dac_word.rend(); ++current_bit){ tx->push_back(tx_bit(*current_bit)); } return tx; } void AD5791::set_dac_to_zero(state_sequent* exp_sequence, state::iterator where) { // 0001 0000 0000 0000 0000 0000 exp_sequence->insert(where, select_input_shift_register(WRITE_DAC)); exp_sequence->insert(where, write_serial_register(0)); } void AD5791::init_dac(state_sequent *exp_sequence, state::iterator where) { exp_sequence->insert(where, select_input_shift_register(WRITE_CONTROL_REGISTER)); exp_sequence->insert(where, write_serial_register(0)); } // This loops recursive through the state tree void AD5791::set_dac_recursive(state_sequent &the_sequence, state::iterator &the_state) { state_sequent *a_sequence = dynamic_cast(*the_state); // Am I a sequence? Yes? Go one sequence further if (a_sequence != NULL) { for (state_sequent::iterator child_state = a_sequence->begin(); child_state != a_sequence->end(); ++child_state) set_dac_recursive(*a_sequence, child_state); } // I am not a sequence, but a state else { state *this_state = dynamic_cast(*the_state); if (this_state == NULL) throw pfg_exception("state_atom in state_sequent not expected"); analogout *dac_analog_out = NULL; // find an analogout section with suitable id state::iterator pos = this_state->begin(); while (pos != this_state->end()) { // state members loop analogout *aout = dynamic_cast(*pos); // initialize new analogout from state if (aout != NULL) std::cout << aout->id << std::endl; // This is for me, analogout is != NULL (there is an analogout) and has my ID if (aout != NULL && aout->id == id) { if (dac_analog_out == NULL) { // save the information of this aout dac_analog_out = aout; } // there is no place for me here else { delete aout; throw pfg_exception("found another DAC section, ignoring"); } // remove the analog out section this_state->erase(pos++); } else { std::cout << "nope" << std::endl; ++pos; } } // state members loop if (dac_analog_out != NULL) { // state modifications //std::cout<<"found a analog out section, value="<dac_value<length < TIMING * (DAC_CONTROL_BITS + DAC_DATA_BITS) * 2 + 1) throw pfg_exception("AD5791: time is too short to save DAC information"); else { // copy of original state state *register_state = new state(*this_state); ttlout *register_ttls = new ttlout(); register_ttls->id = 0; register_state->length = TIMING; register_state->push_back(register_ttls); if (dac_analog_out->dac_value > (pow(2.0, int(DAC_DATA_BITS - 1)) - 1)) throw pfg_exception("dac_value too high"); if (abs(dac_analog_out->dac_value) > pow(2.0, int(DAC_DATA_BITS - 1))) throw pfg_exception("dac_value too low"); state_sequent *input = new state_sequent(); state_sequent *dacword = new state_sequent(); input = select_input_shift_register(WRITE_DAC); dacword = write_serial_register(dac_analog_out->dac_value); register_state->push_back(input); register_state->push_back(dacword); // shorten the remaining state this_state->length -= input->length + dacword->length; // and add LE high to this state ttlout *ttls = new ttlout(); ttls->ttls = 1 << latch_bit; this_state->push_front(ttls); // cleanup delete input; delete dacword; delete register_state; delete dac_analog_out; } // state was long enough to work on } else { ttlout *le_ttls = new ttlout(); // le_ttls->ttls = 1 << latch_bit; this_state->push_back(le_ttls); } // end of state modifications } // I was a state }