Newer
Older
Michael DM Dryden
committed
/*
* experiment.c
*
* Created: 01/10/2012 10:59:48 PM
* Author: mdryden
*/
Michael DM Dryden
committed
/**
* @file experiment.c
* @author Michael DM Dryden <mdryden@chem.utoronto.ca>
* @version 1.0
*
*
* @section DESCRIPTION
*
* Contains Functions for performing experiments and adjusting potentiostat settings other than DAC and ADC.
*/
Michael DM Dryden
committed
#include "experiment.h"
Michael DM Dryden
committed
#include "settings.h"
#include "tcs.h"
Michael DM Dryden
committed
#include "config/conf_board.h"
Michael DM Dryden
committed
Michael DM Dryden
committed
//Public variable definitions
uint16_t g_gain = POT_GAIN_30k;
Michael DM Dryden
committed
uint8_t g_short = 0;
Michael DM Dryden
committed
uint8_t autogain_enable = 1;
Michael DM Dryden
committed
//Private variables
volatile int32_t voltage = 0;
volatile uint16_t dacindex = 0;
uint16_t dacindex_stop = 0;
volatile int8_t up = 1;
volatile uint16_t iter = 0;
uint16_t* eis_ptr = 0;
volatile uint16_t cycles = 0;
volatile uint16_t samples = 0;
volatile uint16_t tcf0period = 0;
uint32_t skip_samples = 0;
//Private function declarations
Michael DM Dryden
committed
//uint16_t set_timer_period(uint32_t period, volatile void *tc);
Michael DM Dryden
committed
static void precond_rtc_callback(uint32_t time);
Michael DM Dryden
committed
static void porte_int0_lsv(void);
static void tcf0_ovf_callback(void);
static void tce1_ovf_callback_lsv(void);
static void lsv_cca_callback(void);
Michael DM Dryden
committed
static void ca_cca_callback(void);
static void portd_int0_ca(void);
Michael DM Dryden
committed
static uint8_t _swv_singledir(uint16_t dacindex, uint16_t dacindex_stop, uint16_t dacindex_pulse_height, uint16_t dacindex_step, uint8_t direction);
Michael DM Dryden
committed
static uint8_t _dpv_singledir(uint16_t dacindex, uint16_t dacindex_stop, uint16_t dacindex_pulse_height, uint16_t dacindex_step, uint8_t direction);
Michael DM Dryden
committed
//interrupt callback setup
typedef void (*port_callback_t) (void);
static port_callback_t portd_int0_callback;
static port_callback_t portd_int1_callback;
Michael DM Dryden
committed
void experiment_handler(char command){
static int16_t p1, p2, p3;
static uint16_t u1, u2, u3, u4;
static uint8_t p5, o1, o2, o3;
static int16_t pcv1, pcv2;
static uint16_t pct1, pct2;
uint16_t tcs_data[] = {0,0,0,0};
uint16_t tcs_data1[] = {0,0,0,0};
Michael DM Dryden
committed
switch (command){
case 'A': //ADS Buffer/rate/PGA values from ads1255.h
scanf("%hhx%hhx%hhx",&o1,&o2,&o3);
Michael DM Dryden
committed
ads1255_setup(o1, o2, o3);
break;
case 'G': //Gain
Michael DM Dryden
committed
scanf("%u%hhu",&g_gain, &g_short);
printf("#G: %u %u\n", g_gain, g_short);
Michael DM Dryden
committed
pot_set_gain(); //uses global g_gain, so no params
break;
case 'L': //LSV - start, stop, slope
scanf("%u%u%i%i%i%i%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&u1);
precond(pcv1,pct1,pcv2,pct2);
lsv_experiment(p1,p2,u1,2);
break;
case 'C': //CV - v1, v2, start, scans, slope
scanf("%u%u%i%i%i%i%i%hhu%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&p3,&p5,&u1);
precond(pcv1,pct1,pcv2,pct2);
cv_experiment(p1,p2,p3,p5,u1);
break;
case 'S': //SWV - start, stop, step size, pulse_height, frequency, scans
scanf("%u%u%i%i%i%i%u%u%u%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&u1,&u2,&u3,&u4);
precond(pcv1,pct1,pcv2,pct2);
swv_experiment(p1,p2,u1,u2,u3,u4);
break;
case 'D': //DPV - start, stop, step size, pulse_height, period, width
scanf("%u%u%i%i%i%i%u%u%u%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&u1,&u2,&u3,&u4);
precond(pcv1,pct1,pcv2,pct2);
dpv_experiment(p1,p2,u1,u2,u3,u4);
break;
case 'M': //PMT idle mode - holds voltage at 0 V with no data output
pmt_idle();
break;
Michael DM Dryden
committed
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR >= 2
case 'P': //potentiometry - time, OCP/poteniometry
scanf("%u%hhu",&pct1, &o1);
pot_experiment(pct1, o1);
break;
#endif
case 'R': //CA - steps, step_dac[], step_seconds[]
scanf("%hhu",&p5); //get number of steps
Michael DM Dryden
committed
//allocate arrays for steps
uint16_t * step_dac = malloc(p5*sizeof(uint16_t));
uint16_t * step_seconds = malloc(p5*sizeof(uint16_t));
//check for successful allocation
if (!step_dac || !step_seconds){
printf("#ERR: Could not allocate memory\n");
Michael DM Dryden
committed
break;
}
uint8_t i;
for (i=0; i<p5; i++){
scanf("%u", &step_dac[i]); // get voltage steps (dac units)
printf("#INFO: DAC: %u\n", step_dac[i]);
Michael DM Dryden
committed
}
for (i=0; i<p5; i++){
scanf("%u", &step_seconds[i]); //get step durations (seconds)
printf("#INFO: Time: %u\n", step_seconds[i]);
Michael DM Dryden
committed
}
scanf("%hhu", &o1);
printf("#INFO: TCS_check: %hhu\n", o1);
Michael DM Dryden
committed
if (o1 > 0) {
if (settings.settings.tcs_enabled > 0){
Michael DM Dryden
committed
tcs_readvalues(tcs_data);
tcs_readvalues(tcs_data1); // If sensor disconnected, second measurement should be exactly the same (unless 0 or saturated)
printf("#INFO: TCS0—%u %u %u %u\n", tcs_data[0], tcs_data[1], tcs_data[2], tcs_data[3]);
printf("#INFO: TCS1—%u %u %u %u\n", tcs_data1[0], tcs_data1[1], tcs_data1[2], tcs_data1[3]);
if (tcs_data[0] == tcs_data1[0]){
if (!(tcs_data[0] == 0 || tcs_data[0] == 65535)) {
printf("#ERR: Ambient light sensor seems to be disconnected \n");
if (tcs_data[0] > settings.settings.tcs_clear_threshold){
printf("#ERR: Ambient light exceeds threshold %u\n", tcs_data[0]);
printf("#INFO: TCS—%u %u %u %u\n", tcs_data[0], tcs_data[1], tcs_data[2], tcs_data[3]);
Michael DM Dryden
committed
return;
}
printf("#ERR: Ambient light sensor disabled.\n");
Michael DM Dryden
committed
}
Michael DM Dryden
committed
}
ca_experiment(p5, step_dac, step_seconds);
//free arrays
free(step_dac);
free(step_seconds);
break;
case 'Z': //Shutter sync
scanf("%lg",&p6);
shutter_cont(p6);
break;
case 'z':
shutter_cont_stop();
break;
case '1':
shutter_close();
break;
case '2':
shutter_open();
break;
Michael DM Dryden
committed
default:
printf("#ERR: Experiment command %c not recognized\n", command[0]);
Michael DM Dryden
committed
}
}
uint16_t set_timer_period(uint32_t period, volatile void *tc)
Michael DM Dryden
committed
{
/**
* Sets a suitable timer source and sets period for a 16-bit timer
*
* @param period 32-bit period in CPU cycles
* @param *tc pointer to timer to set
* @return divider used.
*/
Michael DM Dryden
committed
uint16_t temp_div = ceil((double)period/65536);
uint16_t divider = 0;
Michael DM Dryden
committed
if (temp_div == 1)
tc_write_clock_source(tc, TC_CLKSEL_DIV1_gc);
else if (temp_div == 2){
tc_write_clock_source(tc, TC_CLKSEL_DIV2_gc);
divider = 2;
Michael DM Dryden
committed
}
else if (temp_div <= 4){
tc_write_clock_source(tc, TC_CLKSEL_DIV4_gc);
divider = 4;
Michael DM Dryden
committed
}
else if (temp_div <= 8){
tc_write_clock_source(tc,TC_CLKSEL_DIV8_gc);
divider = 8;
Michael DM Dryden
committed
}
else if (temp_div <= 64){
tc_write_clock_source(tc,TC_CLKSEL_DIV64_gc);
divider = 64;
Michael DM Dryden
committed
}
else if (temp_div <= 256){
tc_write_clock_source(tc,TC_CLKSEL_DIV256_gc);
divider = 256;
Michael DM Dryden
committed
}
else if (temp_div <= 1024){
tc_write_clock_source(tc,TC_CLKSEL_DIV1024_gc);
divider = 1024;
Michael DM Dryden
committed
}
else{
printf("#Frequency/ADC rate is too low\n");
return 0;
Michael DM Dryden
committed
}
period /= divider;
Michael DM Dryden
committed
tc_write_period(tc, (uint16_t)period);
return divider;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
void pot_init(void){
Michael DM Dryden
committed
/**
* Initializes AVR port directions and levels
*
* @return Nothing.
*/
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
Michael DM Dryden
committed
ioport_set_port_dir(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, IOPORT_DIR_OUTPUT);
ioport_set_port_dir(IOPORT_PORTD, PIN4_bm, IOPORT_DIR_OUTPUT);
ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, PIN3_bm|PIN6_bm|PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
Michael DM Dryden
committed
ioport_set_port_dir(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, IOPORT_DIR_OUTPUT);
ioport_set_port_dir(IOPORT_PORTD, PIN4_bm, IOPORT_DIR_OUTPUT);
ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, PIN3_bm|PIN6_bm|PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
}
void pot_set_gain(void){
Michael DM Dryden
committed
/**
* Sets iV gain according to current g_gain value
*
* @return Nothing.
*/
switch (g_gain){
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
case POT_GAIN_500M:
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
break;
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
case POT_GAIN_100M:
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden
committed
Michael DM Dryden
committed
return;
}
}
void volt_exp_start(void){
Michael DM Dryden
committed
/**
* Connects measurement cell to rest of circuit.
*/
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm, PIN3_bm|PIN4_bm);
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, PIN2_bm|PIN4_bm);
delay_ms(100); // Make sure WE circuit is connected before control voltage applied
ioport_set_pin_level(PIN_POT_CE, 1);
Michael DM Dryden
committed
if (g_short == 1)
ioport_set_pin_level(PIN_POT_2ELECTRODE, 1);
Michael DM Dryden
committed
else
ioport_set_pin_level(PIN_POT_2ELECTRODE, 0);
Michael DM Dryden
committed
}
void volt_exp_stop(void){
Michael DM Dryden
committed
/**
Michael DM Dryden
committed
*/
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN5_bm, 0);
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN5_bm, 0);
Michael DM Dryden
committed
delay_ms(100); // Make sure WE is last to disconnect
ioport_set_pin_level(PIN_POT_WE, 0);
ioport_set_pin_level(PIN_POT_2ELECTRODE, 1);
Michael DM Dryden
committed
}
Michael DM Dryden
committed
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR >= 2
Michael DM Dryden
committed
void pot_exp_start(void){
/**
* All switches open.
*/
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, 0);
Michael DM Dryden
committed
}
void ocp_exp_start(void){
/**
* U3C closed
*/
Michael DM Dryden
committed
ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, PIN4_bm);
Michael DM Dryden
committed
}
Michael DM Dryden
committed
#endif
Michael DM Dryden
committed
Michael DM Dryden
committed
void precond(int16_t v1, uint16_t t1, int16_t v2, uint16_t t2){ //assumes potentiostat switches are already set
/**
* Performs experiment preconditioning.
*
* @param v1 First potential (DAC index).
* @param t1 First duration (s).
* @param v2 Second potential (DAC index).
* @param t2 Second duration (s).
*/
Michael DM Dryden
committed
uint16_t time_old = 0;
Michael DM Dryden
committed
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.PER = 65535;
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.CTRL = RTC_PRESCALER_DIV1024_gc; //1s tick
rtc_set_callback((rtc_callback_t)precond_rtc_callback);
up = 1;
//first potential
Michael DM Dryden
committed
if (t1 > 0){
Michael DM Dryden
committed
max5443_set_voltage1(v1);
rtc_set_alarm(t1);
RTC.CNT = 0;
Michael DM Dryden
committed
while (up){
if (udi_cdc_is_rx_ready()){
if (getchar() == 'a'){
precond_rtc_callback(t1);
Michael DM Dryden
committed
goto aborting;
}
}
Michael DM Dryden
committed
if (time_old != RTC.CNT){
time_old = RTC.CNT;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
}
}
up = 1;
Michael DM Dryden
committed
time_old = 0;
Michael DM Dryden
committed
Michael DM Dryden
committed
if (t2 > 0){
Michael DM Dryden
committed
max5443_set_voltage1(v2);
rtc_set_alarm(t2);
RTC.CNT = 0;
Michael DM Dryden
committed
while (up){
if (udi_cdc_is_rx_ready()){
if (getchar() == 'a'){
precond_rtc_callback(t2);
Michael DM Dryden
committed
goto aborting;
}
}
Michael DM Dryden
committed
if (time_old != RTC.CNT){
time_old = RTC.CNT;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
}
}
aborting:
volt_exp_stop();
Michael DM Dryden
committed
return;
}
static void precond_rtc_callback(uint32_t time){
up = 0;
RTC.INTCTRL |= RTC_COMPINTLVL_OFF_gc;
}
Michael DM Dryden
committed
void cv_experiment(int16_t v1, int16_t v2, int16_t start, uint8_t scans, uint16_t slope){
Michael DM Dryden
committed
/**
* Perform a CV experiment.
*
* Calls lsv_experiment several times to make a CV experiment.
* @param v1 Vertex 1 in mV.
* @param v2 Vertex 2 in mV.
* @param start Start voltage in mV.
* @param scans Number of scans.
* @param slope Scan rate in mV/s.
*/
Michael DM Dryden
committed
// check if start is [v1,v2]
Michael DM Dryden
committed
int8_t firstrun = 1;
Michael DM Dryden
committed
if((start < v1 && start < v2) || (start > v1 && start > v2)){
printf("#ERR: Start must be within [v1, v2]\n");
Michael DM Dryden
committed
return;
}
while(scans > 0){
if (start != v1){
Michael DM Dryden
committed
if (lsv_experiment(start,v1,slope,firstrun) == 1)
return;
Michael DM Dryden
committed
firstrun = 0;
}
Michael DM Dryden
committed
if (start == v2 && scans == 1)
firstrun = -1;
Michael DM Dryden
committed
if (lsv_experiment(v1,v2,slope,firstrun) == 1)
return;
Michael DM Dryden
committed
if (scans == 1)
firstrun = -1;
Michael DM Dryden
committed
if (start != v2)
Michael DM Dryden
committed
if(lsv_experiment(v2,start,slope,firstrun) == 1)
return;
Michael DM Dryden
committed
--scans;
firstrun = 0;
Michael DM Dryden
committed
}
printf("D\n"); //signal end of experiment
Michael DM Dryden
committed
return;
}
Michael DM Dryden
committed
uint8_t lsv_experiment(int16_t start, int16_t stop, uint16_t slope, int8_t first_run){
Michael DM Dryden
committed
/**
* Perform a LSV experiment.
*
* Uses porte_int0_lsv to output to USB.
* @param start Start potential in mV.
* @param stop Stop potential in mV.
* @param slope Scan rate in mV/s.
* @param first_run Keeps track of number of scans so potentiostat isn't initialized twice or disconnected when doing CV. Set to 2 for normal LSV.
*/
Michael DM Dryden
committed
uint8_t ret = 0;
Michael DM Dryden
committed
//check experiment limits
Michael DM Dryden
committed
if(start<-1500 || start>=1500 ||start==stop|| stop<-1500 || stop>=1500 || slope>7000)
Michael DM Dryden
committed
{
printf("#ERR: Experiment parameters outside limits\n");
Michael DM Dryden
committed
return ret;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
uint16_t dacindex_start = ceil(start*(65536/(double)3000)+32768);
dacindex_stop = ceil(stop*(65536/(double)3000)+32768);
Michael DM Dryden
committed
uint32_t timer_period;
uint16_t temp_div;
Michael DM Dryden
committed
Michael DM Dryden
committed
max5443_set_voltage1(dacindex_start);
if (first_run == 1 || first_run == 2){
Michael DM Dryden
committed
ads1255_rdatac();
ads1255_sync();
tc_enable(&EXP_TC0_0);
tc_enable(&EXP_TC1_0);
tc_set_overflow_interrupt_callback(&EXP_TC0_0, tcf0_ovf_callback);
tc_set_overflow_interrupt_callback(&EXP_TC1_0, tce1_ovf_callback_lsv);
tc_set_cca_interrupt_callback(&EXP_TC1_0, lsv_cca_callback);
Michael DM Dryden
committed
portd_int0_callback = porte_int0_lsv; //ADC read
Michael DM Dryden
committed
//set EVCH0 event
EVSYS.CH0MUX = EVSYS_CHMUX_TCD0_OVF_gc;
Michael DM Dryden
committed
EVSYS.CH0CTRL = 0;
Michael DM Dryden
committed
timer_period = ceil(1/((double)slope/(3000./65536))*(F_CPU));
temp_div = ceil(timer_period/65536.);
Michael DM Dryden
committed
if (temp_div <= 1)
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV1_gc);
Michael DM Dryden
committed
else if (temp_div == 2){
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV2_gc);
Michael DM Dryden
committed
timer_period /= 2;
}
else if (temp_div <= 4){
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV4_gc);
Michael DM Dryden
committed
timer_period /= 4;
}
else if (temp_div <= 8){
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV8_gc);
Michael DM Dryden
committed
timer_period /= 8;
}
else if (temp_div <= 64){
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV64_gc);
Michael DM Dryden
committed
timer_period /= 64;
}
else if (temp_div <= 256){
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV256_gc);
Michael DM Dryden
committed
timer_period /= 256;
}
else if (temp_div <= 1024){
tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV1024_gc);
Michael DM Dryden
committed
timer_period /= 1024;
}
else{
printf("ERR: Frequency/ADC rate is too low\n");
Michael DM Dryden
committed
return ret;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
ads1255_wakeup();
tc_write_period(&EXP_TC1_0, 0xffff);
tc_write_period(&EXP_TC0_0, (uint16_t)timer_period);
EXP_TC1_0.CNT = dacindex_start;
Michael DM Dryden
committed
if (stop > start)
{
up = 1;
tc_set_direction(&EXP_TC1_0, TC_UP);
Michael DM Dryden
committed
}
else
{
up = -1;
tc_set_direction(&EXP_TC1_0, TC_DOWN);
Michael DM Dryden
committed
}
tc_write_cc(&EXP_TC1_0, TC_CCA, dacindex_stop);
tc_enable_cc_channels(&EXP_TC1_0, TC_CCAEN);
EXP_TC0_0.CNT = 0;
Michael DM Dryden
committed
tc_set_cca_interrupt_level(&EXP_TC1_0, TC_INT_LVL_HI); //Stop experiment
tc_set_overflow_interrupt_level(&EXP_TC0_0, TC_OVFINTLVL_LO_gc); //Set DAC
Michael DM Dryden
committed
PORTD.INTCTRL = PORT_INT0LVL_MED_gc; //ADC read
Michael DM Dryden
committed
tc_write_clock_source(&EXP_TC1_0, TC_CLKSEL_EVCH0_gc);
Michael DM Dryden
committed
Michael DM Dryden
committed
//Experiment run with interrupts
while (up != 0){
if (udi_cdc_is_rx_ready()){
if (getchar()=='a'){
tce1_ovf_callback_lsv();
ret = 1;
goto aborting;
Michael DM Dryden
committed
}
}
}
Michael DM Dryden
committed
if (first_run == -1 || first_run == 2)
{
Michael DM Dryden
committed
aborting:
tc_disable(&EXP_TC0_0);
EXP_TC0_0.CNT = 0x0;
tc_disable(&EXP_TC1_0);
volt_exp_stop();
Michael DM Dryden
committed
ads1255_standby();
return ret;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
return ret;
Michael DM Dryden
committed
}
static void porte_int0_lsv(void){
Michael DM Dryden
committed
/**
* ISR for taking LSV measurements.
*/
Michael DM Dryden
committed
struct
{
uint16_t index;
int32_t result;
} data;
Michael DM Dryden
committed
data.result = ads1255_read_fast24();
static uint16_t last_value = 0;
uint32_t current = EXP_TC1_0.CNT;
Michael DM Dryden
committed
data.index = (current+last_value)>>1; //DAC value is average of current and last timer - approximation of center of averaging window
Michael DM Dryden
committed
udi_cdc_write_buf(&data, 6);
last_value = (uint16_t)current;
Michael DM Dryden
committed
Michael DM Dryden
committed
return;
Michael DM Dryden
committed
}
static void tcf0_ovf_callback(void){
max5443_set_voltage1(EXP_TC1_0.CNT);
Michael DM Dryden
committed
}
static void tce1_ovf_callback_lsv(void){
PORTD.INTCTRL = PORT_INT0LVL_OFF_gc;
tc_set_overflow_interrupt_level(&EXP_TC0_0, TC_OVFINTLVL_OFF_gc);
tc_set_overflow_interrupt_level(&EXP_TC1_0, TC_OVFINTLVL_OFF_gc);
Michael DM Dryden
committed
up = 0;
return;
}
static void lsv_cca_callback(void){
PORTD.INTCTRL = PORT_INT0LVL_OFF_gc;
tc_set_overflow_interrupt_level(&EXP_TC0_0, TC_OVFINTLVL_OFF_gc);
tc_set_cca_interrupt_level(&EXP_TC1_0, TC_INT_LVL_OFF);
Michael DM Dryden
committed
up = 0;
return;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR >= 2
Michael DM Dryden
committed
Michael DM Dryden
committed
void pot_experiment(uint16_t time_seconds, uint8_t exp_type){
Michael DM Dryden
committed
/**
Michael DM Dryden
committed
* Performs a potentiometry experiment.
Michael DM Dryden
committed
*
Michael DM Dryden
committed
* @param time_seconds Time until automatic stop. If 0, can only be canceled by abort signal.
* @param exp_type Type of experiment, POT_OCP for OCP, POT_POTENT for potentiometry
Michael DM Dryden
committed
*/
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.PER = 999;
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.CTRL = RTC_PRESCALER_DIV1_gc; //1ms tick
RTC.CNT = 0;
EVSYS.CH0MUX = EVSYS_CHMUX_RTC_OVF_gc; //EV CH0 -- RTC overflow 1s
portd_int0_callback = portd_int0_ca; //ADC interrupt
tc_enable(&EXP_TC0_0);
Michael DM Dryden
committed
ads1255_mux(ADS_MUX_POT);
ads1255_rdatac();
ads1255_wakeup();
tc_write_period(&EXP_TC0_0,0xffff);
tc_write_clock_source(&EXP_TC0_0, TC_CLKSEL_EVCH0_gc);
tc_set_direction(&EXP_TC0_0, TC_UP);
Michael DM Dryden
committed
up = 1;
if (time_seconds >= 1){ //only enable interrupt if non-zero timeout specified
tc_set_cca_interrupt_callback(&EXP_TC0_0, ca_cca_callback);
tc_write_cc(&EXP_TC0_0, TC_CCA, time_seconds-1);
tc_enable_cc_channels(&EXP_TC0_0, TC_CCAEN);
tc_clear_cc_interrupt(&EXP_TC0_0, TC_CCA);
tc_set_cca_interrupt_level(&EXP_TC0_0, TC_INT_LVL_MED);
Michael DM Dryden
committed
}
Michael DM Dryden
committed
if (exp_type == POT_OCP)
{
ocp_exp_start();
}
else if (exp_type == POT_POTENT)
{
pot_exp_start();
}
Michael DM Dryden
committed
RTC.CNT=0;
PORTD.INTCTRL = PORT_INT0LVL_LO_gc;
EXP_TC0_0.CNT = 0;
Michael DM Dryden
committed
while (up !=0){
if (udi_cdc_is_rx_ready()){
if (getchar() == 'a'){
ca_cca_callback();
Michael DM Dryden
committed
goto aborting;
}
}
}
aborting:
tc_set_cca_interrupt_level(&EXP_TC0_0, TC_INT_LVL_OFF);
tc_write_clock_source(&EXP_TC0_0, TC_CLKSEL_OFF_gc);
tc_disable(&EXP_TC0_0);
Michael DM Dryden
committed
volt_exp_stop();
ads1255_standby();
return;
}
#endif
void pmt_idle(void){
max5443_set_voltage1(65535);
ioport_set_pin_level(PIN_POT_2ELECTRODE, 1);
delay_ms(100);
ioport_set_pin_level(PIN_POT_CE, 1);
while (1){
if (udi_cdc_is_rx_ready()){
if (getchar() == 'a'){
// Doesn't disconnect control line to minimize time this is floating
return;
}
}
}
}
Michael DM Dryden
committed
void ca_experiment(uint16_t steps, uint16_t step_dac[], uint16_t step_seconds[]){
Michael DM Dryden
committed
/**
* Performs a chronoamperometry experiment.
*
* @param steps Total number of steps.
* @param step_dac[] Array containing DAC indices.
* @param step_seconds[] Array containing step durations in seconds.
*/
Michael DM Dryden
committed
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.PER = 999;
while (RTC.STATUS & RTC_SYNCBUSY_bm);
RTC.CTRL = RTC_PRESCALER_DIV1_gc; //1ms tick
RTC.CNT = 0;
Michael DM Dryden
committed
EVSYS.CH0MUX = EVSYS_CHMUX_RTC_OVF_gc; //EV CH0 -- RTC overflow 1s
Michael DM Dryden
committed
portd_int0_callback = portd_int0_ca; //ADC interrupt
Michael DM Dryden
committed
tc_enable(&EXP_TC0_0);
tc_set_cca_interrupt_callback(&EXP_TC0_0, ca_cca_callback);
Michael DM Dryden
committed
ads1255_rdatac();
ads1255_wakeup();
tc_write_period(&EXP_TC0_0,0xffff);
tc_write_clock_source(&EXP_TC0_0, TC_CLKSEL_EVCH0_gc);
tc_set_direction(&EXP_TC0_0, TC_UP);
tc_enable_cc_channels(&EXP_TC0_0, TC_CCAEN);
tc_set_cca_interrupt_level(&EXP_TC0_0, TC_INT_LVL_MED);
Michael DM Dryden
committed
EXP_TC0_0.CNT = 0;
Michael DM Dryden
committed
Michael DM Dryden
committed
max5443_set_voltage1(step_dac[0]);
Michael DM Dryden
committed
for (uint8_t i = 0; i < steps; ++i)
{
up = 1;
tc_write_cc(&EXP_TC0_0, TC_CCA, EXP_TC0_0.CNT+step_seconds[i]-1);
Michael DM Dryden
committed
RTC.CNT=0;
max5443_set_voltage1(step_dac[i]);
Michael DM Dryden
committed
PORTD.INTCTRL = PORT_INT0LVL_LO_gc;
Michael DM Dryden
committed
while (up !=0){
if (udi_cdc_is_rx_ready()){
if (getchar() == 'a'){
ca_cca_callback();
Michael DM Dryden
committed
goto aborting;
}
}
}
Michael DM Dryden
committed
}
Michael DM Dryden
committed
aborting:
tc_set_cca_interrupt_level(&EXP_TC0_0, TC_INT_LVL_OFF);
tc_write_clock_source(&EXP_TC0_0, TC_CLKSEL_OFF_gc);
tc_disable(&EXP_TC0_0);
volt_exp_stop();
Michael DM Dryden
committed
ads1255_standby();
Michael DM Dryden
committed
Michael DM Dryden
committed
return;
Michael DM Dryden
committed
}
static void portd_int0_ca(void){
Michael DM Dryden
committed
struct
{
uint16_t time1;
uint16_t time2;
int32_t current;
} data;
data.time1 = EXP_TC0_0.CNT;
Michael DM Dryden
committed
data.time2 = RTC.CNT;
data.current = ads1255_read_fast24();
Michael DM Dryden
committed
udi_cdc_write_buf(&data, 8);
Michael DM Dryden
committed
}
static void ca_cca_callback(void){
/**
* Interrupt handler for CA. Triggers when counter matches CC to stop potential step.
*
*/
Michael DM Dryden
committed
PORTD.INTCTRL = PORT_INT0LVL_OFF_gc;
up = 0;
return;
}
Michael DM Dryden
committed
void swv_experiment(int16_t start, int16_t stop, uint16_t step, uint16_t pulse_height, uint16_t frequency, uint16_t scans){
Michael DM Dryden
committed
/**
* Perform a SWV experiment
*
* @param start Start voltage in mV.
* @param stop Stop voltage in mV.
* @param step Step voltage in mV.
* @param pulse_height Pulse amplitude in mV.
* @param frequency Frequency in Hz.
Michael DM Dryden
committed
* @param scans Number of scans (0 for single direction mode)
Michael DM Dryden
committed
*/
Michael DM Dryden
committed
uint8_t direction;
Michael DM Dryden
committed
uint16_t dacindex_start = ceil((start)*(65536/(double)3000))+32768;
uint16_t dacindex_stop = ceil(stop*(65536/(double)3000))+32768;
uint16_t dacindex_step = ceil(step*(65536/(double)3000));
uint16_t dacindex_pulse_height = ceil(pulse_height*(65536/(double)3000));
Michael DM Dryden
committed
uint32_t period;
if (start < stop)
Michael DM Dryden
committed
direction = 1;
Michael DM Dryden
committed
else
Michael DM Dryden
committed
direction = 0;
Michael DM Dryden
committed
tc_enable(&EXP_TC0_1);
tc_enable(&EXP_TC0_0);
Michael DM Dryden
committed
frequency *= 2; //compensate for half-period triggers
//calculate time to ADC trigger
period = ceil((1/(double)frequency)*F_CPU);
uint32_t adc_period = ceil(((1/(double)frequency)-(double)(sample_delay_ms_100div/1e5))*F_CPU);
set_timer_period(period, &EXP_TC0_1);
set_timer_period(adc_period, &EXP_TC0_0);
Michael DM Dryden
committed
ads1255_wakeup();
ads1255_standby();
Michael DM Dryden
committed
Michael DM Dryden
committed
Michael DM Dryden
committed
do{
EXP_TC0_1.CNT = 0;
EXP_TC0_0.CNT = 0;
Michael DM Dryden
committed
if (_swv_singledir(dacindex_start, dacindex_stop, dacindex_pulse_height, dacindex_step, direction))
goto aborting; //function will return non-zero if abort called over USB
Michael DM Dryden
committed
Michael DM Dryden
committed
if (scans > 0){ //non-cyclic mode skips out after one direction
EXP_TC0_1.CNT = 0;
EXP_TC0_0.CNT = 0;
Michael DM Dryden
committed
if (_swv_singledir(dacindex_stop, dacindex_start, dacindex_pulse_height, dacindex_step, !direction)) //swap start and stop and invert direction for second half of scan
goto aborting;
}
Michael DM Dryden
committed
Michael DM Dryden
committed
} while (scans-- > 1); //will underflow after comparison for scans = 0 , but shouldn't matter
printf("D\n"); //signal end of experiment
Michael DM Dryden
committed
aborting:
volt_exp_stop();
tc_write_clock_source(&EXP_TC0_1, TC_CLKSEL_OFF_gc);
tc_disable(&EXP_TC0_1);
EXP_TC0_1.CNT = 0;
tc_write_clock_source(&EXP_TC0_0, TC_CLKSEL_OFF_gc);
tc_disable(&EXP_TC0_0);
EXP_TC0_0.CNT = 0;
Michael DM Dryden
committed
ads1255_standby();
return;
}
uint8_t _swv_singledir (uint16_t dacindex, uint16_t dacindex_stop, uint16_t dacindex_pulse_height, uint16_t dacindex_step, uint8_t direction){
Michael DM Dryden
committed
/**
* Internal function that performs a single direction sweep for SWV
*
* @param dacindex Starting voltage as dac index
* @param dacindex_stop Stop voltage in dac index.