added preliminary AD5791 driver

This commit is contained in:
Markus Rosenstihl 2015-11-25 16:43:12 +00:00
parent a02eb49fa1
commit 17ac75d98b
3 changed files with 306 additions and 0 deletions

View File

@ -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 <cstdio>
#include <cmath>
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
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<state_sequent*>(&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<state_sequent*>(*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<state*>(*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<analogout*>(*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="<<PFG_aout->dac_value<<std::endl;
// check the length of the state
if (this_state->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<int> 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 (i<DAC_BIT_DEPTH) {
last_seen_bit=dac_word[i];
last_seen_bit_count=1;
}
} // finished writing
else
last_seen_bit_count+=1; // same bit value, so continue
} // end of bit loop
register_ttls->ttls = 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
}

View File

@ -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

View File

@ -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)