Other Parts Discussed in Thread: MSP430FR5969,
Hi,
I'm using two TDC7200s to measure the ToF in a LiDAR application. Up until this moment, I've been able to correctly set the SPI interface and correctly configure the registers.The start signal is given simultaneously to both by a PWM signal at 6.5kHz.
After the interrupt is detected in the TDC pin, the msp430 reads the data from both TDCs . The problem is when I read the measurement registers. Firstly, I've confirmed and I can successfully read the configuration registers and get the result of the initial setting. Furthermore, the registers TIME1 and CALIBRATION1 and CALIBRATION2 can also be read, giving a calibrated LSB value around 56ns. However, the problem comes when I read the CLOCK_COUNTER1 and I get 0x00. The CLOCK_CNTR_OVF is set to 0x10 (MSB byte to 0x00) to count up to 1us with a 16MHz external clock signal supplied by an MSP430FR5969, while the COARSE_CNTR_OVF is set by default to 0xFFFF. Before reading this value, I read the INT_STATUS register and obtained 0x1D, meaning that the CLOCK_COUNTER1 overflowed. Whatsoever, when I try to read the value in the register before starting a new measurement, I always obtain 0x00.
The implemented code is the following:
#include <msp430fr5969.h>
#include <SPI.h>
float f_p = 5, duty_cycle = 0.65;
float ToF_1, ToF_2, calCount_1, calCount_2, normLSB_1, normLSB_2, range;
uint8_t restart[2] = {0b01000000, 0b10010011};
int restart_len = sizeof(restart)/sizeof(restart[0]);
uint8_t TIME1_1[3] = {}, CLOCK_COUNT_1_1[3] = {}, TIME2_1[3] = {}, CALIBRATION1_1[3] = {}, CALIBRATION2_1[3] = {}; // Buffers to load read values from TDC1
uint8_t TIME1_2[3] = {}, CLOCK_COUNT_1_2[3] = {}, TIME2_2[3] = {}, CALIBRATION1_2[3] = {}, CALIBRATION2_2[3] = {}; // Buffers to load read values from TDC1
void setup_PWM(float , float );
void setup_TDC();
void SPI_send(uint8_t *, int , int);
void SPI_receive(uint8_t *, uint8_t, int);
void ToF_read();
void setup() { // Setup the system
PM5CTL0 &= ~LOCKLPM5; // Disable the GPIO power-on default impedance mode
Serial.begin(115200);
P2DIR &= ~0x10; // Interrupt for TDCs
P2REN |= 0x10;
P2IES |= 0x10;
P2IFG &= ~0x10;
P2IE |= 0x10;
P3DIR |= 0x10; // Output SMCLK
P3SEL1 |= 0x10;
setup_TDC(); // Setup the TDCs
delay(2); // Stabilize
setup_PWM(f_p, duty_cycle);
}
void loop(){
// ToF calculation
calCount_1 = (int)(CALIBRATION2_1[0]*65536+CALIBRATION2_1[1]*256+CALIBRATION2_1[2])-(CALIBRATION1_1[0]*65536+CALIBRATION1_1[1]*256+CALIBRATION1_1[2]); //2 Calibration periods
normLSB_1 = (float)(62500/calCount_1); // In picoseconds or else there is not enough resolution
ToF_1 = (float)normLSB_1*0.001*(TIME1_1[0]*65536+TIME1_1[1]*256+TIME1_1[2]-TIME2_1[0]*65536-TIME2_1[1]*256-TIME2_1[2])+(CLOCK_COUNT_1_1[1]*256+CLOCK_COUNT_1_1[2])*62.5;
calCount_2 = (float)(CALIBRATION2_2[0]*65536+CALIBRATION2_2[1]*256+CALIBRATION2_2[2])-(CALIBRATION1_2[0]*65536+CALIBRATION1_2[1]*256+CALIBRATION1_2[2]); //2 Calibration periods
normLSB_2 = (float)(62500/calCount_2); // In picoseconds
ToF_2 = (float)normLSB_2*0.001*(TIME1_2[0]*65536+TIME1_2[1]*256+TIME1_2[2]-TIME2_2[0]*65536-TIME2_2[1]*256-TIME2_2[2])+(CLOCK_COUNT_1_2[1]*256+CLOCK_COUNT_1_2[2])*62.5;
range = (ToF_2 - ToF_1)*0.15; // range in meters (divided by 2 to account forth and back)
if(range>100) range = 100; // Limiting the system. In practice it won't even be near this range value
Serial.println(ToF_1); // Send distance via UART to PC
Serial.println(ToF_2);
Serial.println(range);
Serial.println(STATUS[0]);
}
#pragma vector=PORT2_VECTOR
__interrupt void PORT2_ISR_HOOK(void){
for(int i=0;i<8;i++) nop; // Guarantee both TDCs activate Interrupt
ToF_read(); // Receive data
P2IFG &= ~0x10;
P2IE |= 0x10; // Re-enable Interrupt
}
void setup_PWM(float f_pwm, float dc_pwm) {
P1DIR |= 0x04; // P1.2 as the PWM output (dedicated pin)
P1OUT &= ~0x04;
P1SEL0 |= 0x04;
TA1CTL |= TASSEL_2 | ID__1 | MC_1 | TACLR;
TA1CCTL1 = OUTMOD_7;
int T_0 = int(16000 / f_pwm);
int T_1 = int(dc_pwm * T_0);
TA1CCR0 = T_0;
TA1CCR1 = T_1;
}
void SPI_send(uint8_t *data, int len, int sel) {
(sel==1 ? P4OUT &= ~0x08 : (sel==2 ? P3OUT &= ~0x01 : P1OUT &= ~0x10));
while (len){
while(!(UCB0IFG & UCTXIFG));
UCB0TXBUF = *(data++);
while(!(UCB0IFG & UCTXIFG));
UCB0TXBUF = *(data++);
while (UCB0STAT & UCBUSY);
len-=2;
}
(sel==1 ? P4OUT |= 0x08 : (sel==2 ? P3OUT |= 0x01 : P1OUT |= 0x10));
}
void setup_TDC(){ // Configure both TDCs
P3DIR |= 0x01;
P3OUT |= 0x01;
P1DIR |= 0x10;
P1OUT |= 0x10;
UCB0CTLW0 = UCSWRST | UCSSEL_2;
UCB0CTLW0 |= UCCKPH | UCMSB | UCSYNC | UCMST;
UCB0CTLW0 &= ~UCCKPL;
P1DIR &= ~0b10000000;
P1DIR |= 0b01000000;
P1SEL1 |= 0b11000000;
P1SEL0 &= ~0b11000000;
P2DIR |= 0b00000100;
P2SEL1 |= 0b0000100;
P2SEL0 &= ~0b00000100;
UCB0BR0 = 1;
UCB0BR1 = 0;
UCB0CTLW0 &= ~UCSWRST;
uint8_t CONFIG1[] = {0b01000000, 0b10010011};
uint8_t CONFIG2[] = {0b01000001, 0b00000000};
uint8_t INT_MASK[] = {0b01000011, 0b00000101};
uint8_t CLOCK_CNTR_OVF_H[] = {0b01000110, 0b0000000};
uint8_t CLOCK_CNTR_OVF_L_1[] = {0b01000111, 0b00010000};
uint8_t CLOCK_CNTR_OVF_L_2[] = {0b01000111, 0b00100000};
int config_len = sizeof(CONFIG1)/sizeof(CONFIG1[0]);
SPI_send(CONFIG1,config_len,2);
SPI_send(CONFIG1,config_len,3);
SPI_send(CONFIG2,config_len,2);
SPI_send(CONFIG2,config_len,3);
SPI_send(INT_MASK,config_len,2);
SPI_send(INT_MASK,config_len,3);
SPI_send(CLOCK_CNTR_OVF_H,config_len,2);
SPI_send(CLOCK_CNTR_OVF_H,config_len,3);
SPI_send(CLOCK_CNTR_OVF_L_1,config_len,2);
SPI_send(CLOCK_CNTR_OVF_L_2,config_len,3);
}
void ToF_read(){ // Read data from TDC whenever an interrupt is detected in P2.4
// Receive data for TDC1 and TDC2
SPI_receive(TIME1_1,0x10,2);
SPI_receive(TIME1_2,0x10,3);
SPI_receive(CLOCK_COUNT_1_1,0x11,2);
SPI_receive(CLOCK_COUNT_1_2,0x11,3);
SPI_receive(TIME2_1,0x12,2);
SPI_receive(TIME2_2,0x12,3);
SPI_receive(CALIBRATION1_1,0x1B,2);
SPI_receive(CALIBRATION1_2,0x1B,3);
SPI_receive(CALIBRATION2_1,0x1C,2);
SPI_receive(CALIBRATION2_2,0x1C,3);
// Restart measurement
SPI_send(restart,restart_len,2);
SPI_send(restart,restart_len,3);
}
void SPI_receive(uint8_t *buffer, uint8_t addr, int sel){
(sel==2 ? P3OUT &= ~0x01 : P1OUT &= ~0x10);
while(!(UCB0IFG & UCTXIFG));
UCB0TXBUF = addr;
for(int w=0;w<3;w++){
while(!(UCB0IFG & UCTXIFG));
UCB0TXBUF = 0x00;
while (UCB0STAT & UCBUSY);
buffer[w] = UCB0RXBUF;
}
while (UCB0STAT & UCBUSY);
(sel==2 ? P3OUT |= 0x01 : P1OUT |= 0x10);
}
I've noticed another thing that may help. When I try the measurement in MODE 2, the register TIME1 does measure some values and I can read values other than 0. However, if I choose mode 1 I still can obtain a interrupt for COARSE_CNTR_OVF on INT_STATUS but when I read TIME1 I can only obtain 0x00 every time.
Another question, the input STOP pulse I'm having has a shape like below and I'm setting the STOP on the falling edge. Is there any problem ? Or the TDC automatically assumes a STOP if the STOP is kept at a high level?
Can anyone help?
Thanks in advance,
Joaquim