/* LD19.c * Jonathan Valvano * Date: Nov 5, 2025 LiDAR Sensor LD19 distance sensor The LD19 consists mainly of a laser ranging core, a wireless power transmission unit, a wireless communication unit, an angle measuring unit, a motor drive unit and a mechanical housing Range 20mm to 12m Sampling rate 10 Hz Accuracy: 45mm Standard deviation 10mm Resolution: 15mm Angular resolution: 0.8deg (540 measurements per rotation) */ #include #include "../RTOS_Labs_common/LD19.h" #include "../inc/Clock.h" #include "../inc/LaunchPad.h" // There are four possible UART3 Rx pins that could be used // Sensor board uses PB13 (UART3) and PB12=0 (GPIO output) // LiDAR Sensor LD19 on RTOS sensor board // Pin LD19 MSPM0 // 1 Tx PB13 RxD: is UART3 Rx (LD19 to MSPM0) baud=230400 bps // 2 PWM PB12 GPIO write a 0 to run fastest (MSPM0 to LD19) // 3 GND ground // 4 P5V 5V // UART3 is shared between LD19 and TFLuna3 (can have either but not both) uint32_t LostDataLD19; #define LD19_SIZE 64 // usable size is 63 #define LD19_MESSAGESIZE 47 // LD19 packet is 47 bytes, assuming length remains 12 // prototypes for private functions void LD19Fifo_Init(void); uint32_t LD19Fifo_Put(uint8_t data); uint32_t LD19Fifo_Get(uint8_t *datapt); int LD19Index; uint8_t LD19LastByte; uint32_t StartAngle,EndAngle,Speed,Distance[12],Intensity[12],Previous; // 0 for looking for 54 2C // 2-47 filling the TFLunaDataMessage with 47-byte message //int LD19Mode; // 0 for FIFO and 1 for 540 array uint8_t LD19DataMessage[64]; // 47 should be enough //uint16_t Starts[256],Stops[256],Times[256],II=0; // number of angles LD19NUM 540 //uint16_t Distances[540]; uint16_t *Distances; int BadCnt=0; // index error int BadDistance=0; // too low uint32_t BadIntensity; /* Scope Measurements Continuous packets Baud = 230400 bps Packet Length = 47 bytes, 2.034ms long (470bits/230400bps = 2.039ms) Packet period = 2.447ms Packet frequency = 409 packets/sec Packet payload = 12 measurements Measurement frequency =12 measurements/2.447ms = 4904measurements/sec 540 measurements per rotation 4904 measurements/sec / 540 measurements/rotation = 9.08 rotations/sec Overhead: Interrupts every three frames, 130.4us (30bits/230400 bps) 15 interrupts take 3.55us (overhead 2.7%) 1 interrupt takes 13.4us (overhead 10%) average overhead = (15*2.7%+10%)/16 = 3.2% Header:The length is 1 byte, and the value is fixed at 0x54, indicating the beginning of the data packet; VerLen:The length is 1 byte, the upper three bits indicate the packet type, which is currently fixed at 1, and the lower five bits indicate the number of measurement points in a packet, which is currently fixed at 12, so the byte value is fixed at 0x2C Speed:The length is 2 bytes (little endian), the unit is degrees per second, indicating the speed of the lidar, e.g., 2152 degrees per second; Start angle: The length is 2 bytes (little endian), and the unit is 0.01 degrees, indicating the starting angle of the data packet point; e.g., 32427, or 324.27 degrees Data:Indicates measurement data, a measurement data length is 3 bytes, 12 measurements Distance: 2 bytes (little endian) in mm, e.g., 1234 means 1234mm Intensity: 1 byte, the typical value of the signal strength value is around 200 End angle: The length is 2 bytes (little endian), and the unit is 0.01 degrees, indicating the end angle of the data packet point; e.g., 33470, or 334.7 degrees; Timestamp:The length is 2 bytes, the unit is milliseconds, and the maximum is 30000. When it reaches 30000, it will be counted again CRC check:The length is 1 byte, obtained from the verification of all the previous data except itself. */ // UART3 is in power Domain PD1 // for 32MHz bus clock, UART3 clock is 32MHz // for 40MHz bus clock, UART3 clock is MCLK 40MHz // for 80MHz bus clock, UART3 clock is MCLK 80MHz //------------LD19_Init------------ // Initialize the UART3 for 230400 baud rate (assuming 80 MHz clock), // 8 bit word length, no parity bits, one stop bit, FIFOs enabled // Input: function 0 for debug, 1 for real time // Output: none void LD19_Init(uint16_t *d){ // do not reset or activate PortA, already done in LaunchPad_Init // RSTCLR to GPIOA and UART3 peripherals // bits 31-24 unlock key 0xB1 // bit 1 is Clear reset sticky bit // bit 0 is reset gpio port // GPIOA->GPRCM.RSTCTL = (uint32_t)0xB1000003; // called previously UART3->GPRCM.RSTCTL = 0xB1000003; // Enable power to GPIOA and UART3 peripherals // PWREN // bits 31-24 unlock key 0x26 // bit 0 is Enable Power // GPIOA->GPRCM.PWREN = (uint32_t)0x26000001; // called previously UART3->GPRCM.PWREN = 0x26000001; Clock_Delay(24); // time for uart to power up // the following code selects PB13 to use UART3 IOMUX->SECCFG.PINCM[PB13INDEX] = 0x00040082; //bit 18 INENA input enable //bit 7 PC connected //bits 5-0=2 for UART3_Rx // configure PB12 to be GPIO output IOMUX->SECCFG.PINCM[PB12INDEX] = 0x00000081; //bit 7 PC connected //bits 5-0=1 for GPIO GPIOB->DOE31_0 |= (1<<12); // enable output GPIOB->DOUTCLR31_0 = (1<<12); // PWM=0, fastest sampling UART3->CLKSEL = 0x08; // bus clock UART3->CLKDIV = 0x00; // no divide UART3->CTL0 &= ~0x01; // disable UART3 UART3->CTL0 = 0x00020018; // bit 17 FEN=1 enable FIFO // bits 16-15 HSE=00 16x oversampling // bit 14 CTSEN=0 no CTS hardware // bit 13 RTSEN=0 no RTS hardware // bit 12 RTS=0 not RTS // bits 10-8 MODE=000 normal // bits 6-4 TXE=001 enable TxD // bit 3 RXE=1 enable TxD // bit 2 LBE=0 no loop back // bit 0 ENABLE 0 is disable, 1 to enable if(Clock_Freq() == 40000000){ // 40000000/16 = 2,500,000 Hz // Baud = 230400 // 2,500,000/230400 = 10.8506944444 // divider = 10+54/64 = 10.84375 UART3->IBRD = 10; UART3->FBRD = 54; // baud =2,500,000/10.84375 = 230,547.55 }else if (Clock_Freq() == 32000000){ // 32000000/16 = 2,000,000 // Baud = 230400 // 2,000,000/230400 = 8.6805555 // divider = 8+43/64 = 8.671875 UART3->IBRD = 8; UART3->FBRD = 43; // 230,630.63 }else if (Clock_Freq() == 80000000){ // 80000000/16 = 5,000,000 Hz // Baud = 230400 // 5,000,000/230400 = 21.701388 // divider = 21+45/64 = 21.703125 UART3->IBRD = 21; UART3->FBRD = 45; // baud =5,000,000/21.703125 = 230,381.569 }else return; //LD19Mode = mode; Distances = d; LD19Index = 0; // looking for 0x54 0x2C LD19LastByte = 0; LD19DataMessage[0] = 0x54; LD19DataMessage[1] = 0x2C; LD19Fifo_Init(); LostDataLD19 = 0; UART3->LCRH = 0x00000030; // bits 5-4 WLEN=11 8 bits // bit 3 STP2=0 1 stop // bit 2 EPS=0 parity select // bit 1 PEN=0 no parity // bit 0 BRK=0 no break UART3->CPU_INT.IMASK = 0x0401; // bit 11 TXINT no // bit 10 RXINT yes // bit 0 Receive timeout, yes UART3->IFLS = 0x0232; // bits 11-8 RXTOSEL receiver timeout select 2 (0xF highest) // bits 6-4 RXIFLSEL 3 is greater than or equal to 3/4 full (three elements) // bits 2-0 TXIFLSEL 2 is less than or equal to half NVIC->ICPR[0] = 1<<3; // UART3 is IRQ 3 NVIC->ISER[0] = 1<<3; NVIC->IP[0] = (NVIC->IP[0]&(~0xFF000000))|(1<<30); // set priority (bits 31-30) IRQ 3 UART3->CTL0 |= 0x01; // enable UART3 } // pointer to most recent array of 540 measurements //uint16_t *LD19_GetDistances(void){ // return Distances; //} // copy from hardware RX FIFO to software LD19Fifo // stop when hardware RX FIFO is empty // data are lost if software LD19Fifo is full void static copyHardwareToSoftwareLD19(void){ uint8_t letter; if(Distances==0){ // raw data mode while((UART3->STAT&0x04) == 0){// empty RX hardware FIFO letter = UART3->RXDATA; if((LD19Fifo_Put(letter))==0){ LostDataLD19++; } } }else{ while((UART3->STAT&0x04)==0){ // empty RX hardware FIFO letter = UART3->RXDATA; if(LD19Index == 0){ // looking for sequence 0x54,0x2C if((letter == 0x2C)&&(LD19LastByte == 0x54)){ LD19Index = 2; // looking for message } LD19LastByte = letter; }else{ LD19DataMessage[LD19Index] = letter; LD19Index++; if(LD19Index == LD19_MESSAGESIZE){ // GPIOB->DOUTTGL31_0 = BLUE; // PB22 // Speed = LD19DataMessage[2]+(LD19DataMessage[3]<<8); StartAngle = LD19DataMessage[4]+(LD19DataMessage[5]<<8); //EndAngle = LD19DataMessage[42]+(LD19DataMessage[43]<<8); // StartAngle goes from 0 to 35999 // index goes from 0 to 540-1 (539() //uint32_t index = (540*StartAngle)/36000; // 0 to 539 uint32_t findex = (983*StartAngle)>>16; if(findex>=540) { findex = 540-1; BadCnt++; // never happens } for(int i=0;i<12;i++){ uint16_t d = LD19DataMessage[6+3*i]+(LD19DataMessage[7+3*i]<<8); if(d < 10){ BadDistance++; // happens a lot, might be low intensity BadIntensity = LD19DataMessage[8+3*i]; if(findex==0){ d = Distances[539]; if(d > 10){ Distances[findex+i] = d; } }else{ d = Distances[findex+i-1]; if(d > 10){ Distances[findex+i] = Distances[findex+i-1]; } } }else{ Distances[findex+i] = d; } // if(findex > 270){ // GPIOA->DOUTSET31_0 = 1; // GPIOB->DOUTSET31_0 = BLUE; // PB22 // } // if(findex < 200){ // GPIOA->DOUTCLR31_0 = 1; // GPIOB->DOUTCLR31_0 = BLUE; // PB22 // } // Distance[i] = d; // Intensity[i] = LD19DataMessage[8+3*i]; } /* Starts[II] = StartAngle; Stops[II] = EndAngle; Times[II] = LD19DataMessage[44]+(LD19DataMessage[45]<<8); II = (II+1)&0xFF; */ LD19LastByte = 0; // get ready for next message LD19Index = 0; } } } } } //------------LD19_InChar------------ // Wait for new serial port input // Input: none // Output: ASCII code for key typed uint8_t LD19_InChar(void){uint32_t status; uint8_t letter; do{ status = LD19Fifo_Get(&letter); }while(status==0); return(letter); } void UART3_IRQHandler(void){ uint32_t status; status = UART3->CPU_INT.IIDX; // reading clears bit in RIS // GPIOA->DOUTTGL31_0 = 1; GPIOB->DOUTSET31_0 = BLUE; // PB22 if(status == 0x01){ // 0x01 receive timeout copyHardwareToSoftwareLD19(); }else if(status == 0x0B){ // 0x0B receive copyHardwareToSoftwareLD19(); } GPIOB->DOUTCLR31_0 = BLUE; // PB22 } // Declare state variables for FiFo // size, buffer, put and get indexes int32_t static LD19PutI; // Index to put new int32_t static LD19GetI; // Index of oldest uint8_t static LD19Fifo[LD19_SIZE]; // *********** LD19Fifo_Init********** // Initializes a software LD19Fifo of a // fixed size and sets up indexes for // put and get operations void LD19Fifo_Init(void){ LD19PutI = LD19GetI = 0; } // *********** LD19Fifo_Put********** // Adds an element to the LD19FIFO // Input: data is character to be inserted // Output: 1 for success, data properly saved // 0 for failure, LD19Fifo is full uint32_t LD19Fifo_Put(uint8_t data){ if(((LD19PutI+1)&(LD19_SIZE-1)) == LD19GetI){ return 0; } LD19Fifo[LD19PutI] = data; LD19PutI = (LD19PutI+1)&(LD19_SIZE-1); return 1; } // *********** LD19Fifo_Get********** // Gets an element from the LD19FIFO // Input: pointer to empty 8-bit variable // Output: If the LD19FIFO is empty return 0 // If the LD19FIFO has data, remove it, and put in *datapt, return 1 uint32_t LD19Fifo_Get(uint8_t *datapt){ if(LD19GetI == LD19PutI){ return 0; } *datapt = LD19Fifo[LD19GetI]; LD19GetI = (LD19GetI+1)&(LD19_SIZE-1); return 1; } //------------LD19_InStatus------------ // Returns how much data available for reading from LD19 FIFO // Input: none // Output: number of elements in receive FIFO uint32_t LD19_InStatus(void){ return ((LD19PutI - LD19GetI)&(LD19_SIZE-1)); }