Regarding CTMU operation in PIC18F45K22

Thread Starter

Lokheshkumar A

Joined Oct 16, 2016
13
Hi,

I'm building a prototype of Laser based distance measurement device for the range of over 50m. So, I'm trying to use CTMU module present in PIC18F45K22 to find the time difference between two pulses on CTED1 and CTED2. I'm referring datasheet to get some background on how to code it but have little knowledge on how to proceed further. Can anyone help me with code/algorithm with steps involved ??
 

Thread Starter

Lokheshkumar A

Joined Oct 16, 2016
13
Hi everyone,
Thank you all for your valuable comments. I have attached my code here. I would like you all to review it and say whether it works to find time interval between two signals. My plan is to declare CTED 1and2 as inputs and initiate the transmitter using CTMUCONLbits.EDG1STAT = 1 and current source stops when while(!CTMUCONLbits.EDG2STAT) become false. Will my flow of code works or what am I missing in this ??

C:
#include <p18f45k22.h>

#define COUNT 500 //@ 8MHz = 125uS.
#define ETIME COUNT*2.5 //time in uS
#define DELAY for(i=0;i<COUNT;i++)
#define RCAL .027 //R value is 4200000 (4.2M)
//scaled so that result is in
//1/100th of uA
#define ADSCALE 1023 //for unsigned conversion 10 sig bits
#define ADREF 3.3 //Vdd connected to A/D Vr+

int i;
unsigned int Vread = 0;
float CTMUISrc = 0,CTMUCap = 0; //float values stored for calcs
float Vcal = 0, time_delay = 0, Vavg = 0;

void ctmu_setup(void)
{
//CTMUCONH/1 - CTMU Control registers
CTMUCONH = 0b00000101; //make sure CTMU is disabled
CTMUCONL = 0b11011100;

//CTMUICON - CTMU Current Control Register
CTMUICON = 0x01; //0.55uA, Nominal - No Adjustment

TRISB = 0;
TRISBbits.TRISB2 =1; // input for CTMU 1 edge
TRISBbits.TRISB3=1; // input for CTMU 2 edge

/**************************************************************************/
//Set up AD converter;
/**************************************************************************/
TRISA=0x04; //set channel 2 as an input

// Configure AN2 as an analog channel
ANSELAbits.ANSA2=1;
TRISAbits.TRISA2=1;

// ADCON2
ADCON2bits.ADFM=1; // Results format 1= Right justified
ADCON2bits.ACQT=1; // Acquition time 7 = 20TAD 2 = 4TAD 1=2TAD
ADCON2bits.ADCS=2; // Clock conversion bits 6= FOSC/64 2=FOSC/32
// ADCON1
ADCON1bits.PVCFG0 =0; // Vref+ = AVdd
ADCON1bits.NVCFG1 =0; // Vref- = AVss
// ADCON0
ADCON0bits.CHS=2; // Select ADC channel
ADCON0bits.ADON=1; // Turn on ADC

}

void current_calibration(void)
{

int j = 0; //index for loop
float VTot=0;

//assume CTMU and A/D have been set up correctly
//see Example 25-1 for CTMU & A/D setup
CTMUCONHbits.CTMUEN = 1; //Enable the CTMU
CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
CTMUCONLbits.EDG2STAT = 0;
for(j=0;j<10;j++)
{
CTMUCONHbits.IDISSEN = 1; //drain charge on the circuit
DELAY; //wait 125us
CTMUCONHbits.IDISSEN = 0; //end drain of circuit

CTMUCONLbits.EDG1STAT = 1; //Begin charging the circuit
//using CTMU current source
DELAY; //wait for 125us
CTMUCONLbits.EDG1STAT = 0; //Stop charging circuit

PIR1bits.ADIF = 0; //make sure A/D Int not set
ADCON0bits.GO=1; //and begin A/D conv.
while(!PIR1bits.ADIF); //Wait for A/D convert complete

Vread = ADRES; //Get the value from the A/D
PIR1bits.ADIF = 0; //Clear A/D Interrupt Flag
VTot += Vread; //Add the reading to the total
}

Vavg = (float)(VTot/10.000); //Average of 10 readings
Vcal = (float)(Vavg/ADSCALE*ADREF);
CTMUISrc = Vcal/RCAL; //CTMUISrc is in 1/100ths of uA
}

void cap_calibration(void)
{
int j = 0; //index for loop
float VTot=0;
//assume CTMU and A/D have been set up correctly
//see Example 25-1 for CTMU & A/D setup
CTMUCONHbits.CTMUEN = 1; //Enable the CTMU
CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
CTMUCONLbits.EDG2STAT = 0;
for(j=0;j<10;j++)
{
CTMUCONHbits.IDISSEN = 1; //drain charge on the circuit
DELAY; //wait 125us
CTMUCONHbits.IDISSEN = 0; //end drain of circuit

CTMUCONLbits.EDG1STAT = 1; //Begin charging the circuit
//using CTMU current source
DELAY; //wait for 125us
CTMUCONLbits.EDG1STAT = 0; //Stop charging circuit

PIR1bits.ADIF = 0; //make sure A/D Int not set
ADCON0bits.GO=1; //and begin A/D conv.
while(!PIR1bits.ADIF); //Wait for A/D convert complete


Vread = ADRES; //Get the value from the A/D
PIR1bits.ADIF = 0; //Clear A/D Interrupt Flag
VTot += Vread; //Add the reading to the total
}

Vavg = (float)(VTot/10.000); //Average of 10 readings
Vcal = (float)(Vavg/ADSCALE*ADREF);
CTMUISrc = Vcal/RCAL; //CTMUISrc is in 1/100ths of uA
CTMUCap = (CTMUISrc*ETIME/Vcal)/100;
}
int main()
{

ctmu_setup();
current_calibration();
cap_calibration();

CTMUCONHbits.CTMUEN = 1; //Enable the CTMU
CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
CTMUCONLbits.EDG2STAT = 0;

CTMUCONHbits.IDISSEN = 1; //drain charge on the circuit
DELAY; //wait 125us
CTMUCONHbits.IDISSEN = 0; //end drain of circuit
//ANSA = 0x00;
//ANSB = 0x00;

CTMUCONLbits.EDG1STAT = 1; //Begin charging the circuit
while(!CTMUCONLbits.EDG2STAT); //Stop charging circuit
PIR1bits.ADIF = 0; //make sure A/D Int not set
ADCON0bits.GO=1; //and begin A/D conv.
while(!PIR1bits.ADIF); //Wait for A/D convert complete
Vavg = ADRES; //Get the value from the A/D
PIR1bits.ADIF = 0; //Clear A/D Interrupt Flag
Vcal = Vavg*ADREF/ADSCALE;
time_delay = Vcal*(CTMUCap/CTMUISrc) * 1000;
}
Moderators note : Insterted txt in code tags.
 

Attachments

Thread Starter

Lokheshkumar A

Joined Oct 16, 2016
13
Another CTMU time/distance project link. This uses the PIC45K80 for 0.5ns time resolution but the basic PIC45K22 CTMU sequencing code will be similar. If you want to go with a 16-bit PIC you might look a the PIC24FV series as its CTMU has a possible 0.2ns time resolution floor.
http://forum.allaboutcircuits.com/threads/light-link-system-tester.114228/
Hi, I have managed to code with respective to PIC18F45K22. Can you review the code and give suggestions whether it works or what else should I improve upon ??
 

Thread Starter

Lokheshkumar A

Joined Oct 16, 2016
13
Have you tried it in the simulator?
Hi,
Yeah ! I did it with PIC18F45K22 but my only problem I'm facing is not able to find time between two edges. Please check if my flow is correct.
1. Set CTMUCONLbits.EDG1STAT=1 >> Will it set CTED1 pin high ??
2. Wait CTMUCONLbits.EDG2STAT is set >> Will CTED2 pin set EDGE2STAT ?
3. Remaining steps.. ADC part.
Is my understanding in this correct or what am I missing in this ??
 

nsaspook

Joined Aug 27, 2009
13,265
This is mainly what I did with the 45K80 code I linked earlier. The CTMU edge trigger starts a series of interrupt driven events after the start_ctmu() call. The L.ctmu_data flag is 'low' when the process is complete.
Code:
/*
Some defines

#define TEMP_DIODE 29 // internal chip temperature diode
#define CTMU_CHAN 28 // mux disconnected for lowest possible measurement capacitor
#define CTMU_ZERO 54
#define CTMU_2M 74
#define CTMU_RES_PS 500 // 500ps per ADC count
#define CTMU_ADC_METER 10 // counts per meter

* CTMU init fragment
*/
/* CTMU pins setup for serial ports round trip time measurements */
TRISBbits.TRISB2 = HIGH; //CTED1 input pin: from PIC TX1 data output pin for starting edge to light link transmitter
TRISBbits.TRISB3 = HIGH; //CTED2 input pin: from light link RX2 receiver output for stopping edge
//CTMUCONH/1 - CTMU Control registers
CTMUCONH = 0x04; //make sure CTMU is disabled and ready for edge 1 before 2
CTMUICON = 0x03; //.55uA*100, Nominal - No Adjustment default
CTMUCONLbits.EDG1SEL = 3; // Set Edge CTED1
CTMUCONLbits.EDG2SEL = 2; // CTED2
CTMUCONLbits.EDG1POL = HIGH; // Set Edge
CTMUCONLbits.EDG2POL = HIGH; // positive edges
CTMUCONHbits.CTMUEN = HIGH; //Enable the CTMU
CTMUCONHbits.IDISSEN = HIGH; // drain the circuit
CTMUCONHbits.CTTRIG = LOW; // disable trigger

ADC init fragment
OpenADC(ADC_FOSC_64 & ADC_RIGHT_JUST & ADC_20_TAD, ADC_CH0 & ADC_INT_ON, ADC_REF_VDD_VSS); // open ADC channel defaults
ANCON0 = 0b00000011; // analog bit enables
ANCON1 = 0; // analog bit enables
// ADCON2, tweaks for time measurement results
ADCON2bits.ADFM = 1; // Results format 1= Right justified
ADCON2bits.ACQT = 1; // Acquition time 7 = 20TAD 2 = 4TAD 1=2TAD
ADCON2bits.ADCS = 6; // Clock conversion bits 6= FOSC/64 2=FOSC/32
// ADCON1
ADCON1bits.VCFG = 3; // Vref+ = 4.096
ADCON1bits.VNCFG = 0; // Vref- = AVss
ADCON1bits.CHSN = 0; // single ended
ADCON0bits.CHS = CTMU_CHAN; // Select ADC
ADCON0bits.ADON = 1; // Turn on ADC

Measurement start function

/*
* ADC results from build calibration
* value with 6 inch jumper 54 as the 'zero' CTED1 to CTED2 = 23ns
* value with 2M jumper 74, CTED1 to CTED2 = 33ns
* ~5ns & 10 counts per meter, ~0.5ns timing resolution
*/
void start_ctmu(void)
{
L.ctmu_data = HIGH; // DATA ready flag is LOW when sequence is complete with data
L.ctmu_data_temp = LOW;
ADCON1bits.VCFG = 3; // Vref+ = 4.096
ADCON0bits.CHS = CTMU_CHAN; // Select ADC channel
CTMUCONHbits.CTMUEN = LOW;
CTMUICON = 0x03; // 55uA
CTMUCONHbits.CTMUEN = HIGH;
CTMUCONHbits.IDISSEN = HIGH; // start drain
wdtdelay(100); // short time to drain the internal cap for measurements
CTMUCONHbits.IDISSEN = LOW; // end drain
PIE3bits.CTMUIE = HIGH; //enable interrupt on edges
CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
CTMUCONLbits.EDG2STAT = 0;
CTMUCONHbits.EDGEN = HIGH; // start looking at external edges
}

In the high ISR we look for the CTMU  edge trigger and start the ADC conversion

if (PIR3bits.CTMUIF) { // CTED (tx1) is high then CTED2 (rx2)went high
PIR3bits.CTMUIF = LOW; // clear CTMU flag
if (L.ctmu_data) { // ready for new data flag
ADCON0bits.GO = HIGH; // and begin A/D conv, will set adc int flag when done.
DLED4 = !DLED4; debug pin toggle
CTMUCONHbits.CTMUEN = LOW;
CTMUICON = 0x00; // current off
PIE3bits.CTMUIE = LOW; // disable interrupt
CTMUCONHbits.EDGEN = LOW;
CTMUCONLbits.EDG1STAT = 0; // Set Edge status bits to zero
CTMUCONLbits.EDG2STAT = 0;
}
}

the CTMU flag started ADC conversion is complete so now we have a ADC interrupt function for when that happens in the high isr

if (PIR1bits.ADIF) { // ADC conversion complete flag
DLED2 = S_ON; // debug pin toggle
PIR1bits.ADIF = LOW;
adc_count++; // just keep count
if (L.ctmu_data) {
L.ctmu_adc = ADRES; // load the ADC value from the CTMU timing edges
L.ctmu_data = LOW;
} else {
if (L.ctmu_data_temp) {
L.pic_temp = ADRES;
L.ctmu_data_temp = LOW;// load the ADC value from the CTMU timing edges
}
adc_buffer[L.adc_chan] = ADRES;// load the ADC value from the CTMU timing edges
}
DLED2 = S_OFF;
}
 
Last edited:
Top