Configuring Kinetis Software Development Kit (KSDK) to measure distance with ultrasonic transducers

Showing results for 
Search instead for 
Did you mean: 

Configuring Kinetis Software Development Kit (KSDK) to measure distance with ultrasonic transducers

No ratings

Configuring Kinetis Software Development Kit (KSDK) to measure distance with ultrasonic transducers

For this demo, KSDK was configured to measure distance with a FRDM-K64F and the 255-400Sx16-ROX ultrasonic transducers. The transmitter transducer sends an ultrasound’s signal that travels through the air, after clashing with any object, the signal bounces and starts its way back until it gets to the receiver transducer. The time it takes for the ultrasound to arrive to the receiver can be use to measure the distance from the transducers to the object, because for a larger distance, the ultrasound’s travel time will be longer. When the receiver gets the ultrasound it generates an output signal, whose intensity is related to the distance to the reflective object, the signal is more intense for closer distances. The demo works for a range from 15 to 100 cm, if the distance to reflective object isn’t between this range, the results shown wouldn’t be reliable.

The following figure shows the application’s block diagram


Figure 1. Block Diagram


     The schematic diagram for the application is shown in the following figure


Figure 2. Schematic Diagram


     The electrical connections needed to implement the demo are presented by this table


Table 1. Electrical connections


     The application’s flow diagram is displayed by the following figure


Figure 3. Flow Diagram for Distance’s Calculation


     The initial configuration for the FTM0 is presented in the following code snippet


void vfnFTM0_Config(void) {     ftm_user_config_t ftm0Info;          configure_ftm_pins(FTM_INSTANCE_0);          FTM_DRV_Init(FTM_INSTANCE_0, &ftm0Info);     FTM_DRV_SetTimeOverflowIntCmd(FTM_INSTANCE_0, true);     FTM_HAL_SetTofFreq(g_ftmBaseAddr[FTM_INSTANCE_0], TOF_FREQ_VALUE);          FTM_DRV_PwmStart(FTM_INSTANCE_0, &highTrueParam, HIGH_TRUE_PWM_CH);     FTM_DRV_PwmStart(FTM_INSTANCE_0, &lowTrueParam, LOW_TRUE_PWM_CH);     bPwmOnFlag = 1; }


The FTM0 was configured to generate two different signals of PWM that complement each other in order to duplicate their voltage’s range.

First of all, it was necessary to configure the FTM0’s pins of the board, so that the PWM signals could be used externally to feed the ultrasonic Tx transducer. Then the FTM driver was initialized, the timer overflow interrupt was enabled and the NUMTOF was set to 14 so that just 15 PWM pulses were generated each time.

Finally, the PWM was started at FTM0, generating a high true signal for channel 0 and low true for channel 1, these configurations were set from the structures highTrueParam and lowTrueParam of type ftm_pwm_param_t, in which the signals are configured as edge aligned, frequency of 40 kHz and the duty cycle is set to 50%.


     The FTM0 IRQ Handler function was modified to match with the demo requirements


void FTM0_DRV_MyIRQHandler(void) {     FTM_DRV_IRQHandler(0U);     bDeadTimeCount++;     if(bPwmOnFlag)     {         FTM0_OUTMASK ^= PWM_CHANNELS_OUTMASK;         swPWMDeparture_CountVal = FTM2_CNT;         FTM_HAL_SetSoftwareTriggerCmd(g_ftmBaseAddr[0], true);         bPwmOnFlag = 0;     }     else if((!bPwmOnFlag) && (bDeadTimeCount == 50))     {         vfnStartPwm();         bDeadTimeCount = 0;     } }


After every 15 FTM0’s overflows this function was accessed and every 50 access to it determined the PWM dead time, which refers to the time between the sending of a PWM signal and the next one. The PWM dead time is necessary to avoid the clash of a signal being transmitted by the Tx transducer and the one that is returning to enter to the Rx, this clash could affect the genuine signal that bounced against the reflective object and also affect the distance measure’s results.

If the PWM signals were active when entering to this function, then they should be disabled with the FTM0 outmask, this would assure that the sending pulses are just 15 and it would be harder that the returning bounced signal clashes with a prolonged sending signal. After disabling the PWM bPWMOnFlag is set to 0.

On the other hand, if the bPWMOnFlag is disabled and the bDeadTimeCount has achieved the corresponding counts, the PWM is restarted and the bDeadTimeCount is set to 0.


The FTM2’s initial configurations are stated on the following code snippet

void vfnFTM2_Config(void) {     ftm_user_config_t ftm2Info;          configure_ftm_pins(FTM_INSTANCE_2);     FTM_DRV_Init(FTM_INSTANCE_2, &ftm2Info);     FTM_HAL_SetClockPs(g_ftmBaseAddr[FTM_INSTANCE_2], kFtmDividedBy16);     FTM_DRV_SetTimeOverflowIntCmd(FTM_INSTANCE_2, true);          vfnInputCaptureStart(); }


For the FTM2, which was used for input capture mode, in this function were configured their pins, the driver was initialized, the clock prescaler was set to 16 and the timer overflow interrupt was enabled before calling to the function vfnInputCaptureStart(), in which are established more specific configurations for the input capture.


The FTM2 IRQHandler was also modified to trigger the required actions for the demo’s functionality

void FTM2_DRV_MyIRQHandler(void) {     if ((FTM2_SC & 0x80))     {         bFtm2OverflowCount++;         FTM_HAL_ClearTimerOverflow(g_ftmBaseAddr[2]);     }     else     {         FTM_HAL_DisableTimerOverflowInt (g_ftmBaseAddr[2]);         CMP_DRV_Stop(1);         vfnGetPwmArriveTime();         vfnGetPwmTravelTime();         FTM_HAL_ClearChnEventStatus(g_ftmBaseAddr[2], 0);         bFtm2OverflowCount = 0;     } }


This function is accessed whether a timer overflow occurs or if a rising edge is detected by the input capture.

If an overflow occurred then the overflow bit is clear and bFtm2OverflowCount’s value is increased by 1, this count is important for the signal’s travel time calculation.

On the contrary, if the IRQ handler function is accessed because of the detection of a rising edge by the input capture, the timer overflow interrupt is disabled to avoid the count of time after the edge detection and to assure the correct travel time calculation. After storing the FTM2 count value as the arrive time of the signal, the function vfnGetPwmTravelTime() is called to calculate the total period of time that the signal spent on its travel through the air. Additionally the bFtm2OverflowCount is set to 0.


     The comparator’s configurations are displayed on this function


void vfnCMP1_Config(void) {     cmp_user_config_t cmpParam =      {           .hystersisMode = kCmpHystersisOfLevel0, /*!< Set the hysteresis level. */           .pinoutEnable = true, /*!< Enable outputting the CMP1 to pin. */           .pinoutUnfilteredEnable = false, /*!< Disable outputting unfiltered result to CMP1. */           .invertEnable = false, /*!< Disable inverting the comparator's result. */           .highSpeedEnable = false, /*!< Disable working in speed mode. */       #if FSL_FEATURE_CMP_HAS_DMA           .dmaEnable = true, /*!< Enable using DMA. */       #endif /* FSL_FEATURE_CMP_HAS_DMA */           .risingIntEnable = true, /*!< Enable using CMP1 rising interrupt. */           .fallingIntEnable = false, /*!< Disable using CMP1 falling interrupt. */           .plusChnMux = kCmpInputChn1, /*!< Set the Plus side input to comparator. */           .minusChnMux = kCmpInputChn3, /*!< Set the Minus side input to comparator. */       #if FSL_FEATURE_CMP_HAS_TRIGGER_MODE           .triggerEnable = true, /*!< Enable triggering mode.  */       #endif /* FSL_FEATURE_CMP_HAS_TRIGGER_MODE */       #if FSL_FEATURE_CMP_HAS_PASS_THROUGH_MODE           .passThroughEnable = true  /*!< Enable using pass through mode. */       #endif /* FSL_FEATURE_CMP_HAS_PASS_THROUGH_MODE */     };          cmp_sample_filter_config_t cmpFilterParam =     {         .workMode = kCmpSampleWithFilteredMode, /*!< Sample/Filter's work mode. */         .useExtSampleOrWindow = false, /*!< Switcher to use external WINDOW/SAMPLE signal. */         .filterClkDiv = 32, /*!< Filter's prescaler which divides from the bus clock.  */         .filterCount =  kCmpFilterCountSampleOf7 /*!< Sample count for filter. See "cmp_filter_counter_mode_t". */     };       cmp_state_t cmpState;     configure_cmp_pins(CMP_INSTANCE_1);     CMP_DRV_Init(CMP_INSTANCE_1, &cmpParam, &cmpState);     CMP_DRV_ConfigSampleFilter(CMP_INSTANCE_1, &cmpFilterParam);     CMP_DRV_Start(CMP_INSTANCE_1); }


The initial configuration’s parameters for the comparator are stated on the structure cmpParam. First of all, the CMP1 input and output was enabled, the rising interrupt was enabled too. In addition, channel 1 was set as the plus side input of the comparator; this channel is the one that receives the output of the ultrasonic Rx transducer. On the other hand, channel 3 was configured as the minus side input of the comparator, this channel corresponds to the 12-bit DAC module that sets the comparison value. Finally, the trigger and the pass through mode were enabled.

It was necessary to stabilize the comparators results, so a sample filter was configured in sample with filter mode, the filter’s prescaler for clock was set to 32 and the filter count sample was set to 7 samples.

  After establishing all these parameters, the comparator’s pins were configured, the driver was initialized, the filter was configured to the CMP1 and the driver was started.


     The 12-bit DAC was configured to generate the comparison value, as shown in this code snippet


void vfnDAC12_Config(void) {     dac_user_config_t dacParam;          // Set configuration for basic operation     DAC_DRV_StructInitUserConfigNormal(&dacParam);     // Initialize DAC with basic configuration     DAC_DRV_Init(DAC_INSTANCE_0, &dacParam);     // Set DAC's Comparison Value     DAC_DRV_Output(DAC_INSTANCE_0, CMP_DAC_VALUE); }


     The DAC’s configuration was simple, the default configurations were used for this one, the driver was initialized and the comparison value was set to 4020. This value was selected according to the Rx transducer’s output signal, the comparison value was determined by the levels achieved, so that the demo could be able to detect a reflective object in all the distance’s range (15 to 100 cm).


     The initialization for input capture mode are shown in this function


void vfnInputCaptureStart(void) {     FTM_HAL_SetChnEdgeLevel(g_ftmBaseAddr[FTM_INSTANCE_2], INPUT_CAPTURE_CH, INPUT_CAPTURE_RISING_EDGE);     FTM_HAL_SetMod(g_ftmBaseAddr[FTM_INSTANCE_2], FTM2_MOD_VALUE);     FTM_HAL_EnableChnInt(g_ftmBaseAddr[FTM_INSTANCE_2], INPUT_CAPTURE_CH);     FTM_HAL_SetChnInputCaptureFilter(g_ftmBaseAddr[FTM_INSTANCE_2], INPUT_CAPTURE_CH, INPUT_CAPTURE_FILTER_VAL);     FTM_HAL_SetCountReinitSyncCmd(g_ftmBaseAddr[FTM_INSTANCE_2], true);     FTM_HAL_SetCounterInitVal(g_ftmBaseAddr[FTM_INSTANCE_2], FTM2_COUNTER_INIT_VALUE);     FTM_HAL_SetClockSource(g_ftmBaseAddr[FTM_INSTANCE_2], kClock_source_FTM_SystemClk); }


     The input capture’s configurations for the demo were: rising edge mode, the MOD value was set to its maximum (0xFFFF), the FMT2_CH0’s interrupt was enabled, the filter on the input was enabled to help in the stabilization of the signal being analyzed, the counter’s init value is set to 0 and finally, the system clock is selected as the clock source.


     The function vfnStartPwm is the one that enables the PWM sending


void vfnStartPwm(void) {     FTM2_CNT = FTM2_COUNTER_INIT_VALUE;     FTM0_OUTMASK ^= PWM_CHANNELS_OUTMASK;     bPwmOnFlag = 1;     FTM_HAL_SetSoftwareTriggerCmd(g_ftmBaseAddr[FTM_INSTANCE_0], true);     FTM_HAL_EnableTimerOverflowInt (g_ftmBaseAddr[FTM_INSTANCE_2]);     CMP_DRV_Start(CMP_INSTANCE_1); }


     Before enabling the PWM signals, the FTM2 count value is stored as the sending time of the signal, then the FTM0 outmask de PWM channels, the bPWMOnFlag is set to 1 and the comparator driver is start in order to wait the Rx transducer’s response.


     The signal’s travel time calculation algorithm is presented in the following code snippet


void vfnGetPwmTravelTime(void) {     uint32_t wTimeDifference = 0;     static uint8_t bAux_OFCount = 0;     static uint32_t waTimeBuffer[TIME_SAMPLES];     static uint8_t bTimeSamples = 0;          bAux_OFCount = (bFtm2OverflowCount-1);          if(bFtm2OverflowCount != 0)     {         if(bAux_OFCount != 0)         {             wTimeDifference = (FTM2_MOD_VALUE * bAux_OFCount);         }         else         {             wTimeDifference = 0;         }         wTimeDifference = (wTimeDifference + (FTM2_MOD_VALUE - swPWMDeparture_CountVal));         wTimeDifference = (wTimeDifference + swPWMArrive_CountVal);     }     else     {         wTimeDifference = (swPWMArrive_CountVal - swPWMDeparture_CountVal);     }          waTimeBuffer[bTimeSamples] = wTimeDifference;     if(bTimeSamples != TIME_SAMPLES)     {         bTimeSamples++;     }     else     {         wAverageTravelTime = dwGetTravelTimeAverage(TIME_SAMPLES, waTimeBuffer);         vfnGetCurrentDistance();         bTimeSamples = 0;     } }


The travel time’s calculation algorithm is simple. If just one overflow occurred during the signal’s traveling period, the time difference is calculated as the difference of the FTM2_MOD_VALUE and the FTM2 counter value at the departure time, plus the counter value at the arriving time. Otherwise, if more than one overflow occurred then the time difference is the same as the last case, but adding the product of the FTM2_MOD_VALUE and the overflow’s counts minus 1. The simplest case is the one there haven’t been overflows, the time difference is calculated subtracting the departure counter value from the arriving counter value.

After obtaining 20 samples of travel time an average of all these values is calculated so that the time data is the most reliable possible.


The distance calculation algorithm was implemented on the following function


void vfnGetCurrentDistance(void) {     static uint64_t dwDistance = 0;     uint8_t b_m = 3;     uint32_t w_b = 27130000;     uint32_t w_FixedPointAux = 100000;     uint16_t swTimeBase = 265;          // 265ns per FTM2 Count          wAverageTravelTime >>= 1;        wAverageTravelTime = (wAverageTravelTime*swTimeBase);          dwDistance = (wAverageTravelTime * w_FixedPointAux);     dwDistance = (dwDistance * b_m);     dwDistance = (dwDistance / w_FixedPointAux);     dwDistance = (dwDistance - w_b);          bDistanceIntegers = (dwDistance / w_FixedPointAux);       bDistanceUnits = (dwDistance % w_FixedPointAux);     bDistanceUnits = (bDistanceUnits / 10);          bDistanceReadyFlag = 1; }


The distance calculation is based on the linear behavior of the transducers’ response. Some samples of the signal’s travel time were taken for distances from 15 to 80 cm and according to that data, it was calculated a linear equation that describes the calculated travel time in function of the distance to the reflective object (See Figure 4 below).

After getting the time difference, in FTM2 counts, between the departure and the arriving time of the signal, it was necessary to divide that number of counts, because that time difference corresponds to the travel of the signal to the reflective object and back to the transducers. Furthermore, it was required to convert those counts to real time, and according to the configurations of the FTM2, each of its counts was equal to 265 ns.

The rest of the algorithm consists just on the use of the linear equation the travel time as variable, multiplying by the corresponding slope (b_m) and adding the intersection with the y-axis (w_b).

The real values of the slope and the intersection with the y-axis were fixed in order to avoid the use of float variables.


The following graphic shows the characteristic linear behavior of the relation between the signal’s travel time and the distance from the transducers to the reflective object


Figure 4. Characteristic curve’s graphic


The application test’s results are shown in the following table


1The error for the Distance Measured is approximately ± 2 cm for short distances and ± 5 cm for the longer ones

Table 2. Test’s Results


     Steps to include the ultrasonic distance measurer demo in KSDK


In order to include this demo in the KSDK structure, the files need to be copied into the correct place. The ultrasonic_distance_measurer folder should be copied into the <KSDK_install_dir>/demos folder.

If the folder is copied to a wrong location, paths in the project and makefiles will be broken. When the copy is complete you should have the following locations as paths in your system:

    • <KSDK_install_dir>/demos/ultrasonic_distance_measurer/iar
    • <KSDK_install_dir>/demos/ultrasonic_distance_measurer/kds
    • <KSDK_install_dir>/demos/ultrasonic_distance_measurer/src

In addition, to build and run the demo, it is necessary to download one of the supported Integrated Development Enviroment (IDE) by the demo:

Once the project is opened in one of the supported IDEs, remember to build the KSDK library before building the project, if it was located at the right place no errors should appear, start a debug session and run the demo. The results of the distance measurement will be shown by UART on a console (use 115 200 as Baud rate) and at FreeMASTER, just as in the following example where the reflective object was located at 35 cm from the ultrasonic transducers:


Figure 5. Example of the distance measured being shown in a console


Figure 6. Example of the distance measure results at FreeMASTER

     FreeMASTER configuration

For visualizing the application’s result on FreeMASTER it is necessary to configure the corresponding type of connection for the FRDM-K64F:

          1. Open FreeMaster.

          2. Go to File/Open Project.

          3. Open the Ultrasonic Distance Measurer project from <KSDK_install_dir>/demos/ultrasonic_distance_measurer/ FreeMaster.

          4. Go to Project/Options.


       On the Comm tab, make sure the option ‘Plug-in Module’ is marked and select the corresponding type of connection.


Figure 7. Corresponding configurations FRDM-K64F’s connection at FreeMASTER


It is also necessary to select the corresponding MAP file for the IDE in which will be tested the demo, so:

          1. Go to the MAP Files tab.

          2. Select the MAP File for the IAR or the KDS project.

*Make sure that the default path matches with the one where is located the MAP file of the demo at your PC. If not, you can modify the path by clicking on the ‘…’ button (see Figure and selecting the correct path to the MAP file:

    • <KSDK_install_dir>/demos/ultrasonic_distance_measurer/iar/frdmk64f/debug/ultrasonic_distance_measurer_frdmk64f.out
    • <KSDK_install_dir>/demos/ultrasonic_distance_measurer/kds/frdmk64f/Debug/ultrasonic_distance_measurer_frdmk64f.elf

  Click on ‘OK’ to save the changes.



Figure 8. Selection of the MAP File for each IDE supported by the demo


Enjoy the demo!

Labels (1)
Version history
Revision #:
2 of 2
Last update:
‎09-10-2020 03:33 AM
Updated by: