diff --git a/drivers/DAC-AD5791/AD5791.cpp b/drivers/DAC-AD5791/AD5791.cpp new file mode 100644 index 0000000..1dc40b0 --- /dev/null +++ b/drivers/DAC-AD5791/AD5791.cpp @@ -0,0 +1,231 @@ +/** + * \file + * \brief Implementation of the Driver for the AD5791 + * Digital to Analog Converter Board + * + * This board was first used in the PFG, and gets now + * adopted by the Field-Cycling spectrometers. + * + * The board inverts all digital inputs. + * This driver partially honors this. + * Especially the data input is not yet inverted. + * -# Inverting the data line is equivalent to inverting + * the value and add/substracting one. + * -# There are different versions of the board around, + * where one board "undoes" the inversion by having an + * inverting op-amp after the DAC. + * -# The digital units have to be calibrated to physical + * units anyway. A sign doesn't hurt much. + * + * This is still a current discussion topic and might + * change. + */ + +#include "AD5791.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_BIT_DEPTH 24 + +// The channel configuration +#define DATA_BIT 18//18 +#define CLK_BIT 16//16 + +AD5791::AD5791(int myid): id(myid) { + dac_value = 0; + set_latch_bit(17); +} + +AD5791::~AD5791() {} + +// This sets the dac_value +void AD5791::set_dac(signed dw) { + dac_value = dw; +} + +void AD5791::set_latch_bit(int le_bit) +{ + latch_bit = le_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()); + // And at the end of the experiment + set_dac_to_zero(exp_sequence, exp_sequence->end()); + } +} + +void AD5791::set_dac_to_zero(state_sequent* exp_sequence, state::iterator where) +{ + state s(TIMING); + ttlout* le=new ttlout(); + le->id=0; + s.push_front(le); + state_sequent* rep_sequence=new state_sequent(); + rep_sequence->repeat=DAC_BIT_DEPTH; + le->ttls = 0 + ( 1 << CLK_BIT ); + rep_sequence->push_back(s.copy_new()); + le->ttls = 0 ; + rep_sequence->push_back(s.copy_new()); + exp_sequence->insert(where, rep_sequence); + //read in the word (41st pulse) + le->ttls=0; + exp_sequence->insert(where, s.copy_new()); + // 42nd pulse + // // the state should be 2ms long + // s.length = 2e-3-41*TIMING; + le->ttls= ( 1 << latch_bit ); + exp_sequence->insert(where, s.copy_new()); +} + +// 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* PFG_aout = 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 + // This is for me, analogout is != NULL (there is an analogout) and has my ID + if (aout!=NULL && aout->id == id) { + if (PFG_aout == NULL) { + // save the informations + PFG_aout = aout; + } + // there is no place for me here + else { + throw pfg_exception( "found another DAC section, ignoring"); + delete aout; + } + // remove the analog out section + this_state->erase(pos++); + } + else { + ++pos; + } + } // state members loop + + if (PFG_aout != NULL) { // state modifications + //std::cout<<"found a analog out section, value="<dac_value<length < TIMING*DAC_BIT_DEPTH*2 + 1) + throw pfg_exception( "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 (PFG_aout->dac_value > (pow(2.0, int(DAC_BIT_DEPTH-1))-1) ) + throw pfg_exception("dac_value too high"); + if ( abs(PFG_aout->dac_value) > pow(2.0, int(DAC_BIT_DEPTH-1)) ) + throw pfg_exception("dac_value too low"); + // now, insert the ttl information + PFG_aout->dac_value += 1<<20; + vector dac_word; + for (int j = 0; j < DAC_BIT_DEPTH ; j++) { + int bit = PFG_aout->dac_value & 1; + // // invert the bit + //bit = (bit == 0); + dac_word.push_back(bit); + //cout << dac_word[j]; + PFG_aout->dac_value >>= 1; + } + // need one clock cycle to read in bit + // latch enable (LE) should always be high while doing so + // except for the last bit + // reverse the bit pattern + reverse(dac_word.begin(), dac_word.end()); + + // do run length encoding, grouping same bit values in loops + int last_seen_bit=dac_word[0]; + int last_seen_bit_count=1; + for (int i = 1; i < DAC_BIT_DEPTH+1; i++) { + if (i==DAC_BIT_DEPTH || last_seen_bit!=dac_word[i]) { + // so we have to write the bits + // either because the previous were different or we are finished + if (last_seen_bit_count>1) { + // insert a loop + state_sequent* rep_sequence=new state_sequent(); + rep_sequence->repeat=last_seen_bit_count; + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit); + rep_sequence->push_back(register_state->copy_new()); + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << latch_bit); + rep_sequence->push_back(register_state->copy_new()); + the_sequence.insert(the_state, rep_sequence); + } else { + // no loop necessary, insert two states + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit); + the_sequence.insert(the_state, register_state->copy_new()); + register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << latch_bit); + the_sequence.insert(the_state, register_state->copy_new()); + } + // reset counter and bits if we are not finished + if (ittls = 0; + the_sequence.insert(the_state,register_state->copy_new()); + + // shorten the remaining state + // and add LE high to this state + ttlout* ttls=new ttlout(); + // 42nd pulse + this_state->length -= TIMING*2*DAC_BIT_DEPTH + 1; + ttls->ttls = 1 << latch_bit; + this_state->push_front(ttls); + + // cleanup + delete register_state; + delete PFG_aout; + } // 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 +} diff --git a/drivers/DAC-AD5791/AD5791.h b/drivers/DAC-AD5791/AD5791.h new file mode 100644 index 0000000..aa9290c --- /dev/null +++ b/drivers/DAC-AD5791/AD5791.h @@ -0,0 +1,48 @@ +/* + Markus Rosenstihl 2005 Nov + */ +#ifndef AD5791_H +#define AD5791_H + +#include "core/states.h" +#include "drivers/pfggen.h" + +/** + * \ingroup drivers + */ +class AD5791: public GenericDAC { +protected: + /// The channel for the latch signal + int latch_bit; + +public: + int id; + // default constructor + AD5791(int myid=0); + virtual void set_dac(signed dw); +// virtual void set_dac_ttls(signed value); + + void set_latch_bit(int le_bit); + + /** + inserts necessary serial data transmission to set the dac, at the end of experiment reset the dac + + assumes, the root sequence is not repeated, because the reset sequence is appended to the root sequence! + */ + virtual void set_dac(state& experiment); + + // destructor + virtual ~AD5791(); + + private: + void set_dac_recursive(state_sequent& the_sequence, state::iterator& the_state); + void set_dac_to_zero(state_sequent* exp_sequence, state::iterator where); + void set_dac_control(state_sequent* exp_sequence, state::iterator where, int input_shift_register); +}; + +/*class pfg_exception: public std::string { +public: + pfg_exception(const std::string& s): std::string(s){} +}; +*/ +#endif diff --git a/drivers/DAC-AD5791/Makefile b/drivers/DAC-AD5791/Makefile new file mode 100644 index 0000000..e6dfdca --- /dev/null +++ b/drivers/DAC-AD5791/Makefile @@ -0,0 +1,27 @@ +CXX=g++ +CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic +CXXCPPFLAGS=-I. -I../.. +LDFLAGS=-lexpat -lxerces-c + +.PHONY: all clean install + +all: AD5791.o DAC_test.exe + +../../tools/add_endline.exe: ../../tools/add_endline.cpp + $(CXX) $< -o $@ + +../../core/core.a: + $(MAKE) -C ../../core core.a + +clean: ../../tools/add_endline.exe + for f in AD5791.cpp AD5791.h DAC_test.cpp; do $< $$f; done + rm -f *.exe *.o *~ core.* + +AD5791.o: AD5791.cpp AD5791.h + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +DAC_test.o: DAC_test.cpp + $(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< + +DAC_test.exe: DAC_test.o AD5791.o ../../core/core.a + $(CXX) -o $@ $^ $(LDFLAGS)