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
//Public variable definitions
uint16_t g_gain = POT_GAIN_30k;
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
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);
static void lsv_dma_callback(void);
static void lsv_dma_callback1(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;
void send_data_uint16(uint16_t data){
/**
* Sends uint16 data over USB in order in memory (should be LSB first for AVR - little endian)
*
* @param data 16-bit data
* @return Nothing.
*/
udi_cdc_putc(((unsigned char *)(&data))[0]);
udi_cdc_putc(((unsigned char *)(&data))[1]);
}
void send_data_int32(int32_t data){
/**
* Sends int32 data over USB in order in memory (should be LSB first for AVR - little endian)
*
* @param data 32-bit data
* @return Nothing.
*/
udi_cdc_putc(((unsigned char *)(&data))[0]);
udi_cdc_putc(((unsigned char *)(&data))[1]);
udi_cdc_putc(((unsigned char *)(&data))[2]);
udi_cdc_putc(((unsigned char *)(&data))[3]);
}
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\r");
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
arch_ioport_set_port_dir(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, IOPORT_DIR_OUTPUT);
arch_ioport_set_port_dir(IOPORT_PORTD, PIN4_bm, IOPORT_DIR_OUTPUT);
arch_ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
arch_ioport_set_port_dir(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, IOPORT_DIR_OUTPUT);
arch_ioport_set_port_dir(IOPORT_PORTD, PIN4_bm, IOPORT_DIR_OUTPUT);
arch_ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm|PIN6_bm|PIN7_bm, PIN3_bm|PIN6_bm|PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
#endif
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:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
Michael DM Dryden
committed
Michael DM Dryden
committed
case POT_GAIN_30M:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 30M\n\r");
break;
Michael DM Dryden
committed
case POT_GAIN_3M:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 3M\n\r");
break;
Michael DM Dryden
committed
case POT_GAIN_300k:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 300k\n\r");
break;
Michael DM Dryden
committed
case POT_GAIN_30k:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 30k\n\r");
break;
Michael DM Dryden
committed
case POT_GAIN_3k:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 3k\n\r");
break;
Michael DM Dryden
committed
case POT_GAIN_300:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 300\n\r");
break;
Michael DM Dryden
committed
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
case POT_GAIN_100:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 100\n\r");
break;
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
case POT_GAIN_100M:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 100M\n\r");
break;
case POT_GAIN_30M:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 30M\n\r");
break;
case POT_GAIN_3M:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 3M\n\r");
break;
case POT_GAIN_300k:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, 0);
printf("#INFO: 300k\n\r");
break;
case POT_GAIN_30k:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, 0);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 30k\n\r");
break;
case POT_GAIN_3k:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 3k\n\r");
break;
case POT_GAIN_0:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 0\n\r");
break;
case POT_GAIN_100:
arch_ioport_set_port_level(IOPORT_PORTB, PIN6_bm|PIN7_bm, PIN6_bm|PIN7_bm);
arch_ioport_set_port_level(IOPORT_PORTD, PIN4_bm, PIN4_bm);
printf("#INFO: 100\n\r");
break;
#endif
Michael DM Dryden
committed
default:
printf("#WAR: Invalid pot gain.\n\r");
break;
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
arch_ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm, PIN3_bm|PIN4_bm|PIN5_bm);
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
arch_ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, PIN2_bm|PIN4_bm|PIN5_bm);
#endif
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
arch_ioport_set_port_level(IOPORT_PORTB, PIN3_bm|PIN4_bm|PIN5_bm, 0);
#endif
#if BOARD_VER_MAJOR == 1 && BOARD_VER_MINOR == 2
arch_ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, 0);
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
arch_ioport_set_port_level(IOPORT_PORTB, PIN2_bm|PIN3_bm|PIN4_bm|PIN5_bm, 0);
}
void ocp_exp_start(void){
/**
* U3C closed
*/
arch_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);
printf("##ABORT\n\r");
goto aborting;
}
}
Michael DM Dryden
committed
if (time_old != RTC.CNT){
time_old = RTC.CNT;
printf("#%u\n\r",time_old);
}
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);
printf("##ABORT\n\r");
goto aborting;
}
}
Michael DM Dryden
committed
if (time_old != RTC.CNT){
time_old = RTC.CNT;
printf("#%u\n\r",time_old);
}
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\r");
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;
printf("S\n\r"); //signal end of scan
Michael DM Dryden
committed
}
printf("D\n\r"); //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\r");
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();
tc_enable(&TCC1);
ads1255_sync();
tc_enable(&TCC0);
tc_set_overflow_interrupt_callback(&TCC0, tcf0_ovf_callback);
tc_set_overflow_interrupt_callback(&TCC1, tce1_ovf_callback_lsv);
tc_set_cca_interrupt_callback(&TCC1, 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_TCC0_OVF_gc;
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
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
if (temp_div <= 1)
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV1_gc);
else if (temp_div == 2){
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV2_gc);
timer_period /= 2;
}
else if (temp_div <= 4){
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV4_gc);
timer_period /= 4;
}
else if (temp_div <= 8){
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV8_gc);
timer_period /= 8;
}
else if (temp_div <= 64){
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV64_gc);
timer_period /= 64;
}
else if (temp_div <= 256){
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV256_gc);
timer_period /= 256;
}
else if (temp_div <= 1024){
tc_write_clock_source(&TCC0,TC_CLKSEL_DIV1024_gc);
timer_period /= 1024;
}
else{
printf("ERR: Frequency/ADC rate is too low\n\r");
Michael DM Dryden
committed
return ret;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
//printf("Period:%lu\n\r", timer_period);
Michael DM Dryden
committed
ads1255_wakeup();
tc_write_period(&TCC1, 0xffff);
tc_write_period(&TCC0, (uint16_t)timer_period);
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
// //DMA
// struct dma_channel_config dmach_conf;
// memset(&dmach_conf, 0, sizeof(dmach_conf));
//
// dma_channel_set_burst_length(&dmach_conf, DMA_CH_BURSTLEN_1BYTE_gc);
// dma_channel_set_transfer_count(&dmach_conf, 1);
//
// dma_channel_set_src_reload_mode(&dmach_conf, DMA_CH_SRCRELOAD_TRANSACTION_gc);
// dma_channel_set_dest_reload_mode(&dmach_conf, DMA_CH_DESTRELOAD_NONE_gc);
//
// dma_channel_set_src_dir_mode(&dmach_conf, DMA_CH_SRCDIR_FIXED_gc);
// dma_channel_set_source_address(&dmach_conf, (uint16_t)(uintptr_t)&TCC1.CNTH);
//
// dma_channel_set_dest_dir_mode(&dmach_conf, DMA_CH_DESTDIR_FIXED_gc);
// dma_channel_set_destination_address(&dmach_conf, (uint16_t)(uintptr_t)&USARTC1.DATA);
//
// dma_channel_set_trigger_source(&dmach_conf, DMA_CH_TRIGSRC_OFF_gc);
// dma_channel_set_single_shot(&dmach_conf);
//
// dma_set_callback(0, (dma_callback_t)lsv_dma_callback);
// dma_channel_set_interrupt_level(&dmach_conf, DMA_INT_LVL_HI);
//
// dma_channel_write_config(0, &dmach_conf);
//
// dma_set_callback(1, (dma_callback_t)lsv_dma_callback1);
// dma_channel_set_source_address(&dmach_conf, (uint16_t)(uintptr_t)&TCC1.CNTL);
// dma_channel_write_config(1, &dmach_conf);
}
//
// dma_channel_enable(0);
Michael DM Dryden
committed
TCC1.CNT = dacindex_start;
if (stop > start)
{
up = 1;
tc_set_direction(&TCC1, TC_UP);
}
else
{
up = -1;
tc_set_direction(&TCC1, TC_DOWN);
}
tc_write_cc(&TCC1, TC_CCA, dacindex_stop);
tc_enable_cc_channels(&TCC1, TC_CCAEN);
TCC0.CNT = 0;
Michael DM Dryden
committed
tc_set_cca_interrupt_level(&TCC1, TC_INT_LVL_HI); //Stop experiment
Michael DM Dryden
committed
tc_set_overflow_interrupt_level(&TCC0, TC_OVFINTLVL_LO_gc); //Set DAC
PORTD.INTCTRL = PORT_INT0LVL_MED_gc; //ADC read
Michael DM Dryden
committed
tc_write_clock_source(&TCC1, TC_CLKSEL_EVCH0_gc);
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(&TCC0);
TCC0.CNT = 0x0;
tc_disable(&TCC1);
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.
*/
static uint16_t last_value = 0;
uint32_t current = TCC1.CNT;
Michael DM Dryden
committed
send_data_uint16((current+last_value)>>1); //DAC value is average of current and last timer - approximation of center of averaging window
send_data_int32(result);
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(TCC1.CNT);
Michael DM Dryden
committed
}
static void tce1_ovf_callback_lsv(void){
PORTD.INTCTRL = PORT_INT0LVL_OFF_gc;
tc_set_overflow_interrupt_level(&TCC0, TC_OVFINTLVL_OFF_gc);
tc_set_overflow_interrupt_level(&TCC1, TC_OVFINTLVL_OFF_gc);
up = 0;
return;
}
static void lsv_cca_callback(void){
PORTD.INTCTRL = PORT_INT0LVL_OFF_gc;
tc_set_overflow_interrupt_level(&TCC0, TC_OVFINTLVL_OFF_gc);
tc_set_cca_interrupt_level(&TCC1, TC_INT_LVL_OFF);
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
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
*/
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(&TCC0);
ads1255_mux(ADS_MUX_POT);
ads1255_rdatac();
ads1255_wakeup();
tc_write_period(&TCC0,0xffff);
tc_write_clock_source(&TCC0, TC_CLKSEL_EVCH0_gc);
tc_set_direction(&TCC0, TC_UP);
up = 1;
if (time_seconds >= 1){ //only enable interrupt if non-zero timeout specified
tc_set_cca_interrupt_callback(&TCC0, ca_cca_callback);
tc_write_cc(&TCC0, TC_CCA, time_seconds-1);
tc_enable_cc_channels(&TCC0, TC_CCAEN);
Michael DM Dryden
committed
tc_clear_cc_interrupt(&TCC0, TC_CCA);
Michael DM Dryden
committed
tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_MED);
}
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;
Michael DM Dryden
committed
TCC0.CNT = 0;
Michael DM Dryden
committed
while (up !=0){
if (udi_cdc_is_rx_ready()){
if (getchar() == 'a'){
ca_cca_callback();
printf("##ABORT\n\r");
goto aborting;
}
}
}
aborting:
tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_OFF);
tc_write_clock_source(&TCC0, TC_CLKSEL_OFF_gc);
tc_disable(&TCC0);
volt_exp_stop();
ads1255_standby();
return;
}
#endif
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(&TCC0);
tc_set_cca_interrupt_callback(&TCC0, ca_cca_callback);
ads1255_rdatac();
ads1255_wakeup();
tc_write_period(&TCC0,0xffff);
tc_write_clock_source(&TCC0, TC_CLKSEL_EVCH0_gc);
tc_set_direction(&TCC0, TC_UP);
tc_enable_cc_channels(&TCC0, TC_CCAEN);
Michael DM Dryden
committed
tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_MED);
Michael DM Dryden
committed
Michael DM Dryden
committed
TCC0.CNT = 0;
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(&TCC0, TC_CCA, TCC0.CNT+step_seconds[i]-1);
RTC.CNT=0;
max5443_set_voltage1(step_dac[i]);
Michael DM Dryden
committed
printf("#DAC: %u\n\r", 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();
printf("##ABORT\n\r");
goto aborting;
}
}
}
Michael DM Dryden
committed
}
Michael DM Dryden
committed
aborting:
tc_set_cca_interrupt_level(&TCC0, TC_INT_LVL_OFF);
tc_write_clock_source(&TCC0, TC_CLKSEL_OFF_gc);
tc_disable(&TCC0);
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){
printf("B\n");
send_data_uint16(TCC0.CNT);
send_data_uint16(RTC.CNT);
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(&TCF0);
tc_enable(&TCC0);
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);
Michael DM Dryden
committed
set_timer_period(period, &TCF0);
set_timer_period(adc_period, &TCC0);
Michael DM Dryden
committed
ads1255_wakeup();
ads1255_standby();
Michael DM Dryden
committed
Michael DM Dryden
committed
Michael DM Dryden
committed
do{
TCF0.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
TCF0.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
printf("S\n\r"); //signal end of scan
} while (scans-- > 1); //will underflow after comparison for scans = 0 , but shouldn't matter
printf("D\n\r"); //signal end of experiment
aborting:
volt_exp_stop();
Michael DM Dryden
committed
tc_write_clock_source(&TCF0, TC_CLKSEL_OFF_gc);
tc_disable(&TCF0);
TCF0.CNT = 0;
tc_write_clock_source(&TCC0, TC_CLKSEL_OFF_gc);
tc_disable(&TCC0);
TCC0.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.
* @param dacindex_step Step voltage in dac index.
* @param dacindex_pulse_height Pulse amplitude in dac index.
* @param direction Scan direction - 1 for up, 0 for down.
*/
Michael DM Dryden
committed
int32_t forward = 0;
int32_t reverse = 0;
uint16_t lastindex = 0;
if (direction == 1)
max5443_set_voltage1(dacindex+dacindex_pulse_height);
else
max5443_set_voltage1(dacindex-dacindex_pulse_height);
while ((dacindex <= dacindex_stop && direction == 1) || (dacindex >= dacindex_stop && direction == 0)){
tc_clear_overflow(&TCF0);
tc_clear_overflow(&TCC0);
Michael DM Dryden
committed
while (!tc_is_overflow(&TCC0)){ //ADC tc overflow
Michael DM Dryden
committed
if (udi_cdc_is_rx_ready()){ //check for abort signal over USB
Michael DM Dryden
committed
if (getchar() == 'a')
Michael DM Dryden
committed
return 1;
}
Michael DM Dryden
committed
}
ads1255_wakeup();
while (ioport_pin_is_high(IOPORT_CREATE_PIN(PORTD, 5)));
forward = ads1255_read_single24();
ads1255_standby();
while (!tc_is_overflow(&TCF0)); //wait for end of half-cycle
TCC0.CNT = 0;
Michael DM Dryden
committed
if (direction == 1) //switch voltage to other half of cycle
max5443_set_voltage1(dacindex-dacindex_pulse_height);
Michael DM Dryden
committed
else
Michael DM Dryden
committed
max5443_set_voltage1(dacindex+dacindex_pulse_height);
Michael DM Dryden
committed
Michael DM Dryden
committed
tc_clear_overflow(&TCF0); //reset timer OVF
tc_clear_overflow(&TCC0);
Michael DM Dryden
committed
while (!tc_is_overflow(&TCC0)){ //ADC tc overflow
if (udi_cdc_is_rx_ready()){ //check for abort signal over USB
Michael DM Dryden
committed
if (getchar() == 'a')
Michael DM Dryden
committed
}
Michael DM Dryden
committed
}
ads1255_wakeup();
while (ioport_pin_is_high(IOPORT_CREATE_PIN(PORTD, 5)));
reverse = ads1255_read_single24();
ads1255_standby();
while (!tc_is_overflow(&TCF0)); //wait for end of half-cycle
TCC0.CNT = 0;
Michael DM Dryden
committed
lastindex = dacindex;
//increment dacindex
if (direction == 1){
dacindex += dacindex_step;
Michael DM Dryden
committed
max5443_set_voltage1(dacindex+dacindex_pulse_height);
Michael DM Dryden
committed
}
Michael DM Dryden
committed
Michael DM Dryden
committed
else{
dacindex -= dacindex_step;
max5443_set_voltage1(dacindex-dacindex_pulse_height);
}
//data output
printf("B\n");
send_data_uint16(lastindex);
send_data_int32(forward);
send_data_int32(reverse);
printf("\n");
Michael DM Dryden
committed
}
Michael DM Dryden
committed
return 0;
Michael DM Dryden
committed
}
Michael DM Dryden
committed
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
void dpv_experiment(int16_t start, int16_t stop, uint16_t step, uint16_t pulse_height, uint16_t pulse_period, uint16_t pulse_width){
/**
* Perform a DPV 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 pulse_period Pulse period in ms.
* @param pulse_width Pulse width in ms.
*/
uint8_t direction;
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));
uint32_t cpu_period;
uint32_t cpu_width;
if (start < stop)
direction = 1;
else
direction = 0;
tc_enable(&TCF0);
tc_enable(&TCC0);
Michael DM Dryden
committed
//calculate time to ADC trigger
cpu_period = ceil((double)pulse_period*1e-3*F_CPU);
uint32_t adc_period = ceil((((double)pulse_period*1e-3)-(double)(sample_delay_ms_100div/1e5))*F_CPU);
uint16_t divider = set_timer_period(cpu_period, &TCF0);
uint16_t adc_divider = set_timer_period(adc_period, &TCC0);
Michael DM Dryden
committed
cpu_width = (double)pulse_width*1e-3*F_CPU;
uint32_t adc_width = ceil((((double)pulse_width*1e-3)-(double)(sample_delay_ms_100div/1e5))*F_CPU);
tc_write_cc(&TCF0, TC_CCA, (uint16_t)(cpu_width/divider));
Michael DM Dryden
committed
tc_enable_cc_channels(&TCF0, TC_CCAEN);
tc_write_cc(&TCC0, TC_CCA, (uint16_t)(adc_width/adc_divider));
tc_enable_cc_channels(&TCC0, TC_CCAEN);
Michael DM Dryden
committed
ads1255_wakeup();
ads1255_standby();
Michael DM Dryden
committed
TCF0.CNT = 0;
TCC0.CNT = 0;
Michael DM Dryden
committed
if (_dpv_singledir(dacindex_start, dacindex_stop, dacindex_pulse_height, dacindex_step, direction))
goto aborting; //function will return non-zero if abort called over USB
printf("D\n\r"); //signal end of experiment
aborting:
volt_exp_stop();