added preliminary AD5791 driver
This commit is contained in:
parent
a02eb49fa1
commit
17ac75d98b
231
drivers/DAC-AD5791/AD5791.cpp
Normal file
231
drivers/DAC-AD5791/AD5791.cpp
Normal 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
|
||||
}
|
48
drivers/DAC-AD5791/AD5791.h
Normal file
48
drivers/DAC-AD5791/AD5791.h
Normal 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
|
27
drivers/DAC-AD5791/Makefile
Normal file
27
drivers/DAC-AD5791/Makefile
Normal 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)
|
Loading…
Reference in New Issue
Block a user