experiment.c 36.5 KiB
Newer Older
/*
 * experiment.c
 *
 * Created: 01/10/2012 10:59:48 PM
 *  Author: mdryden
 */ 
/**
 * @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.
 */
#include "shutter.h"
#include <util/atomic.h>
//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
//uint16_t set_timer_period(uint32_t period, volatile void *tc);
static void precond_rtc_callback(uint32_t time);
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);
static void ca_cca_callback(void);
static void portd_int0_ca(void);
static uint8_t _swv_singledir(uint16_t dacindex, uint16_t dacindex_stop, uint16_t dacindex_pulse_height, uint16_t dacindex_step, uint8_t direction);
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's avatar
Michael DM Dryden committed
static void pmt_idle(void);

//interrupt callback setup
typedef void (*port_callback_t) (void);

static port_callback_t portd_int0_callback;
static port_callback_t portd_int1_callback;

void experiment_handler(char command[]){
Michael DM Dryden's avatar
 
Michael DM Dryden committed
    static uint16_t u1, u2, u3, u4, u5, u6;
    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};
        case 'A': //ADS Buffer/rate/PGA values from ads1255.h
            sscanf(command+1, "%hhx%hhx%hhx",&o1,&o2,&o3);
            printf("#A: %x %x %x\n",o1,o2,o3);
            sscanf(command+1, "%u%hhu",&g_gain, &g_short);
            printf("#G: %u %u\n", g_gain, g_short);
            pot_set_gain(); //uses global g_gain, so no params
            break;
            
        case 'L': //LSV - start, stop, slope
Michael DM Dryden's avatar
 
Michael DM Dryden committed
            if (settings.settings.dac_units_true) {
                sscanf(command+1, "%u%u%u%u%u%u%u",&pct1,&pct2,&pcv1,&pcv2,&u1,&u2,&u3);
                precond(pcv1,pct1,pcv2,pct2);
                lsv_experiment(u1,u2,u3,2);
            } else {
                sscanf(command+1, "%u%u%i%i%i%i%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&u1);
                
                //start
                u2 = ceil(p1*(65536/(double)3000)+32768);
                //stop
                u3 = ceil(p2*(65536/(double)3000)+32768);
                
                precond(pcv1,pct1,pcv2,pct2);
                lsv_experiment(u2,u3,u1,2);
            }
Michael DM Dryden's avatar
 
Michael DM Dryden committed
            if (settings.settings.dac_units_true) {
                sscanf(command+1, "%u%u%i%i%u%u%u%hhu%u",&pct1,&pct2,&pcv1,&pcv2,&u1,&u2,&u3,&p5,&u4);
                precond(pcv1,pct1,pcv2,pct2);
                cv_experiment(u1,u2,u3,p5,u4);
            } else {
                sscanf(command+1, "%u%u%i%i%i%i%i%hhu%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&p3,&p5,&u4);
                
                //v1
                u1 = ceil(p1*(65536/(double)3000)+32768);
                //v2
                u2 = ceil(p2*(65536/(double)3000)+32768);
                //start
                u3 = ceil(p3*(65536/(double)3000)+32768);
                //slope
                u4 = ceil(u4*(65536/(double)3000));
                
                precond(pcv1,pct1,pcv2,pct2);
                cv_experiment(u1,u2,u3,p5,u4);
            }
            break;
            
        case 'S': //SWV - start, stop, step size, pulse_height, frequency, scans
Michael DM Dryden's avatar
 
Michael DM Dryden committed
            if (settings.settings.dac_units_true) {
                sscanf(command+1, "%u%u%i%i%u%u%u%u%u%hhu",&pct1,&pct2,&pcv1,&pcv2,&u1,&u2,&u3,&u4,&u5,&p5);
                precond(pcv1,pct1,pcv2,pct2);
                swv_experiment(u1,u2,u3,u4,u5,p5);
            } else {
                sscanf(command+1, "%u%u%i%i%i%i%u%u%u%hhu",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&u1,&u2,&u3,&p5);
                
                //start
                uint16_t start = ceil(p1*(65536/(double)3000)+32768);
                //stop
                uint16_t stop = ceil(p2*(65536/(double)3000)+32768);
                //step
                u1 = ceil(u1*(65536/(double)3000));
                //pulse_height
                u2 = ceil(u2*(65536/(double)3000));
                
                precond(pcv1,pct1,pcv2,pct2);
                swv_experiment(start,stop,u1,u2,u3,p5);
            }
            break;
            
        case 'D': //DPV - start, stop, step size, pulse_height, period, width
Michael DM Dryden's avatar
 
Michael DM Dryden committed
            if (settings.settings.dac_units_true) {
                sscanf(command+1, "%u%u%i%i%u%u%u%u%u%u",&pct1,&pct2,&pcv1,&pcv2,&u1,&u2,&u3,&u4,&u5,&u6);
                precond(pcv1,pct1,pcv2,pct2);
                dpv_experiment(u1,u2,u3,u4,u5,u6);
            } else {
                sscanf(command+1, "%u%u%i%i%i%i%u%u%u%u",&pct1,&pct2,&pcv1,&pcv2,&p1,&p2,&u1,&u2,&u3,&u4);
                
                //start
                uint16_t start = ceil(p1*(65536/(double)3000)+32768);
                //stop
                uint16_t stop = ceil(p2*(65536/(double)3000)+32768);
                //step
                u1 = ceil(u1*(65536/(double)3000));
                //pulse_height
                u2 = ceil(u2*(65536/(double)3000));
                
                precond(pcv1,pct1,pcv2,pct2);
                dpv_experiment(start,stop,u1,u2,u3,u4);
            }
Michael DM Dryden's avatar
Michael DM Dryden committed
        case 'M': //PMT idle mode - holds voltage at 0 V with no data output
            pmt_idle();
            break;
            
        #if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR >= 2
        case 'P': //potentiometry - time, OCP/poteniometry
            sscanf(command+1, "%u%hhu",&pct1, &o1);
            pot_experiment(pct1, o1);
            break;
        #endif
            
        case 'R': //CA - steps, step_dac[], step_seconds[]
        {
            sscanf(command+1, "%hhu%hhu",&p5, &o1); //get number of steps
            printf("#INFO: Steps: %u\n", p5);
            
            //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");
            //request additional parameters from computer
            printf("@RQP %u\n", 2*(uint16_t)p5);
            
            uint16_t i;
            uint8_t result;
                while (1) {
                    result = scanf("%u", &step_dac[i]); // get voltage steps (dac units)
                    if (result == 1) {
                        printf("@RCVC %u\n", step_dac[i]);
                        break;
                    }
                    else {
                        printf("@RCVE\n");
                    }
                }
                while (1) {
                    result = scanf("%u", &step_seconds[i]); // get time steps (seconds)
                    if (result == 1) {
                        printf("@RCVC %u\n", step_seconds[i]);
                        break;
                    }
                    else {
                        printf("@RCVE\n");
                    }
                }
            printf("@RCP %u\n", 2*(uint16_t)p5);
            
            printf("#INFO: TCS_check: %hhu\n", o1);
                if (settings.settings.tcs_enabled > 0){
                    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]);
                    printf("#ERR: Ambient light sensor disabled.\n");
            }
            
            ca_experiment(p5, step_dac, step_seconds);
            
            //free arrays
            free(step_dac);
            free(step_seconds);
            
            break;
        case 'Z': //Shutter sync
            sscanf(command+1, "%lg", &p6);
            shutter_cont(p6);
            break;
            
        case 'z':
            shutter_cont_stop();
            break;
            
        case '1':
            shutter_close();
            break;
            
        case '2':
            shutter_open();
            break;
            
            printf("#ERR: Experiment command %c not recognized\n", command[0]);
uint16_t set_timer_period(uint32_t period, volatile void *tc)
	/**
	 * 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.
	 */
	
	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);
	}
	else if (temp_div <= 4){
		tc_write_clock_source(tc, TC_CLKSEL_DIV4_gc);
	}
	else if (temp_div <= 8){
		tc_write_clock_source(tc,TC_CLKSEL_DIV8_gc);
	}
	else if (temp_div <= 64){
		tc_write_clock_source(tc,TC_CLKSEL_DIV64_gc);
	}
	else if (temp_div <= 256){
		tc_write_clock_source(tc,TC_CLKSEL_DIV256_gc);
	}
	else if (temp_div <= 1024){
		tc_write_clock_source(tc,TC_CLKSEL_DIV1024_gc);
		printf("#Frequency/ADC rate is too low\n");
	/**
	 * Initializes AVR port directions and levels
	 *
	 * @return Nothing.
	 */
Michael DM Dryden's avatar
Michael DM Dryden committed
	#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
		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);
Michael DM Dryden's avatar
Michael DM Dryden committed
	#endif
	#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
		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);
Michael DM Dryden's avatar
 
Michael DM Dryden committed
		ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
		ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
Michael DM Dryden's avatar
Michael DM Dryden committed
	#endif
	/**
	 * Sets iV gain according to current g_gain value
	 *
	 * @return Nothing.
	 */
	switch (g_gain){
Michael DM Dryden's avatar
Michael DM Dryden committed
		#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
			case POT_GAIN_500M:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 500M\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_30M:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 30M\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_3M:	
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 3M\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_300k:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 300k\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_30k:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 30k\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_3k:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 3k\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_300:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 300\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
Michael DM Dryden's avatar
Michael DM Dryden committed
			case POT_GAIN_100:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 100\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
		#endif
		
		#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
			case POT_GAIN_100M:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 100M\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_30M:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 30M\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_3M:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 3M\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_300k:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
				printf("#INFO: 300k\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_30k:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 30k\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_3k:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 3k\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_0:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 0\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
				
			case POT_GAIN_100:
				ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
				ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
				printf("#INFO: 100\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
		#endif
Michael DM Dryden's avatar
Michael DM Dryden committed
			default:
				printf("#WAR: Invalid pot gain.\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
				break;
void volt_exp_start(void){
Michael DM Dryden's avatar
Michael DM Dryden committed
	#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);
Michael DM Dryden's avatar
Michael DM Dryden committed
	#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);
Michael DM Dryden's avatar
Michael DM Dryden committed
	#endif
    
    delay_ms(100); // Make sure WE circuit is connected before control voltage applied
    ioport_set_pin_level(PIN_POT_CE, 1);
        ioport_set_pin_level(PIN_POT_2ELECTRODE, 1);
        ioport_set_pin_level(PIN_POT_2ELECTRODE, 0);
Michael DM Dryden's avatar
Michael DM Dryden committed
	 * Disconnects measurement cell.
Michael DM Dryden's avatar
Michael DM Dryden committed
	#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 1
		ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN5_bm, 0);
Michael DM Dryden's avatar
Michael DM Dryden committed
	#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's avatar
Michael DM Dryden committed
	#endif
    delay_ms(100);  // Make sure WE is last to disconnect
    ioport_set_pin_level(PIN_POT_WE, 0);
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR >= 2
void pot_exp_start(void){
	/**
	 * All switches open.
	 */
	ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, 0);
	ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, PIN4_bm);
Michael DM Dryden's avatar
 
Michael DM Dryden committed
void precond(uint16_t v1, uint16_t t1, uint16_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).
	 */
	
	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
		max5443_set_voltage1(v1);
		rtc_set_alarm(t1);
		RTC.CNT = 0;
		volt_exp_start();
		while (up){
			if (udi_cdc_is_rx_ready()){
				if (getchar() == 'a'){
					precond_rtc_callback(t1);
					printf("##ABORT\n");
				printf("#%u\n",time_old);
		max5443_set_voltage1(v2);
		rtc_set_alarm(t2);
		RTC.CNT = 0;
		volt_exp_start();
		while (up){
			if (udi_cdc_is_rx_ready()){
				if (getchar() == 'a'){
					precond_rtc_callback(t2);
					printf("##ABORT\n");
				printf("#%u\n",time_old);
		return;
}

static void precond_rtc_callback(uint32_t time){
	up = 0;
	RTC.INTCTRL |= RTC_COMPINTLVL_OFF_gc;
}

Michael DM Dryden's avatar
 
Michael DM Dryden committed
void cv_experiment(uint16_t v1, uint16_t v2, uint16_t start, uint8_t scans, uint16_t slope){
	/**
	 * Perform a CV experiment.
	 *
	 * Calls lsv_experiment several times to make a CV experiment.
Michael DM Dryden's avatar
 
Michael DM Dryden committed
	 * @param v1 Vertex 1 in dac units.
	 * @param v2 Vertex 2 in dac units.
	 * @param start Start voltage in dac units.
Michael DM Dryden's avatar
 
Michael DM Dryden committed
	 * @param slope Scan rate in dac units/s.
	if((start < v1 && start < v2) || (start > v1 && start > v2)){
		printf("#ERR: Start must be within [v1, v2]\n");
			if (lsv_experiment(start,v1,slope,firstrun) == 1)
				return;
		if (lsv_experiment(v1,v2,slope,firstrun) == 1)
			return;
			if(lsv_experiment(v2,start,slope,firstrun) == 1)
				return;
        printf("S\n"); //signal end of scan
	printf("D\n"); //signal end of experiment
Michael DM Dryden's avatar
 
Michael DM Dryden committed
uint8_t lsv_experiment(uint16_t start, uint16_t stop, uint16_t slope, int8_t first_run){
	/**
	 * Perform a LSV experiment.
	 *
	 * Uses porte_int0_lsv to output to USB.
Michael DM Dryden's avatar
 
Michael DM Dryden committed
	 * @param start Start potential in dac units.
	 * @param stop Stop potential in dac units.
	 * @param slope Scan rate in dac units/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's avatar
 
Michael DM Dryden committed
	if(start==stop)
		printf("#ERR: Experiment parameters outside limits\n");
Michael DM Dryden's avatar
 
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's avatar
 
Michael DM Dryden committed
	max5443_set_voltage1(start);
		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);
		EVSYS.CH0MUX = EVSYS_CHMUX_TCD0_OVF_gc;
Michael DM Dryden's avatar
 
Michael DM Dryden committed
		timer_period = ceil(1/(double)slope*(F_CPU));
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV1_gc);
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV2_gc);
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV4_gc);
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV8_gc);
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV64_gc);
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV256_gc);
			tc_write_clock_source(&EXP_TC0_0,TC_CLKSEL_DIV1024_gc);
			printf("ERR: Frequency/ADC rate is too low\n");
		tc_write_period(&EXP_TC1_0, 0xffff);
		tc_write_period(&EXP_TC0_0, (uint16_t)timer_period);
Michael DM Dryden's avatar
 
Michael DM Dryden committed
	EXP_TC1_0.CNT = start;
		tc_set_direction(&EXP_TC1_0, TC_UP);
		tc_set_direction(&EXP_TC1_0, TC_DOWN);
Michael DM Dryden's avatar
 
Michael DM Dryden committed
	tc_write_cc(&EXP_TC1_0, TC_CCA, stop);
	tc_enable_cc_channels(&EXP_TC1_0, TC_CCAEN);
	EXP_TC0_0.CNT = 0;
	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
	PORTD.INTCTRL = PORT_INT0LVL_MED_gc; //ADC read
	tc_write_clock_source(&EXP_TC1_0, TC_CLKSEL_EVCH0_gc);
	//Experiment run with interrupts
	while (up != 0){
		if (udi_cdc_is_rx_ready()){
			if (getchar()=='a'){
				tce1_ovf_callback_lsv();
				ret = 1;
				goto aborting;
			tc_disable(&EXP_TC0_0);
			EXP_TC0_0.CNT = 0x0;
			tc_disable(&EXP_TC1_0);
	uint32_t current = EXP_TC1_0.CNT;
	data.index = (current+last_value)>>1; //DAC value is average of current and last timer - approximation of center of averaging window
	
Michael DM Dryden's avatar
Michael DM Dryden committed
	printf("B\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
	printf("\n");
	max5443_set_voltage1(EXP_TC1_0.CNT);
}

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);
	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);
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR >= 2
void pot_experiment(uint16_t time_seconds, uint8_t exp_type){
	 * @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
	 */
	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);
	
	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);
	
	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);
	
	if (exp_type == POT_OCP)
	{
		ocp_exp_start();
	}
	else if (exp_type == POT_POTENT)
	{
		pot_exp_start();
	}
	
	while (up !=0){
		if (udi_cdc_is_rx_ready()){
			if (getchar() == 'a'){
				ca_cca_callback();
				printf("##ABORT\n");
		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's avatar
Michael DM Dryden committed
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'){
                printf("##ABORT\n");
Michael DM Dryden's avatar
Michael DM Dryden committed
                // Doesn't disconnect control line to minimize time this is floating
                return;
            }
        }
    }
}

void ca_experiment(uint16_t steps, uint16_t step_dac[], uint16_t step_seconds[]){
	/**
	 * 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.
	 */
	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);
	tc_set_cca_interrupt_callback(&EXP_TC0_0, ca_cca_callback);
	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);
		tc_write_cc(&EXP_TC0_0, TC_CCA, EXP_TC0_0.CNT+step_seconds[i]-1);
		printf("#DAC: %u\n", step_dac[i]);
		while (up !=0){
			if (udi_cdc_is_rx_ready()){
				if (getchar() == 'a'){
					ca_cca_callback();
					printf("##ABORT\n");
		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);
	data.time1 = EXP_TC0_0.CNT;
	data.time2 = RTC.CNT;
	data.current = ads1255_read_fast24();

	/**
	 * Interrupt handler for CA. Triggers when counter matches CC to stop potential step.
	 *
	 */