Search code examples
cpic

PIC based MCU coding to make a speedometer


I am trying to make a speedometer cum odometer. I'm using a PIC based MCU. The speed I'm getting is as an analog input. (at 0V, I get 0 Km/hr and at 25V, I get 25Km/hr). I can easily measure the speed using the ADC conversion. The issue I'm facing is how do I measure the distance here. Does anyone have any ideas for the same?

One thing I can think of is having a delay of 100ms in the code and multiplying the instantaneous speed by 100mS. Since all other function execution should take a maximum of 1-5mS (including ADC conversion and sending bits to LCD displays etc), I can neglect that time and consider the distance here. But this method is not very exact. Does anyone have a better solution?

PFA the basic code

// need to multiply with the reference voltage as well (value*5/4096)
currentSpeedVoltage = ADC_Read(9);

//(0.001220703125 = 5/4096)
currentSpeedVoltage = (currentSpeedVoltage * 0.001220703125);

// assumed here that at 18 Volts, we show the speed as 25 Km/hr.
currentSpeed = (currentSpeedVoltage*25)/18;

// converting km/hr to m/s and adding the distance travelled in 100mS.
distance += (currentSpeedx100x5*(pow(10,-5))/18)/1000;

Solution

  • normally odometers work by other means such as pulses from known distances round the wheel, but if you're stuck with these limitations then I think you're going in the right direction, only one problem might be the delay that you intend to use, I'd use a timer interrupt instead,

    Use a timer interrupt

    By delay, I'm guessing you mean the following:

    while ( true ) 
    {
       delayMs(100);
       DoADCStuff();
    }
    

    This won't work because the next delay will start after DoADCStuff. If possible, use a timer module and timer interrupt. Because you haven't specified a PIC part, I'm giving very generic code here.

    // choose a timer module i.e. 0, 1, etc. Read the datasheet for more info.
    TxCON.PS = 0x...; // set your prescale to allow preload to fit in 8-bit or 16-bit depending on PIC type
    PRx = 0x...; // set your 8-bit or 16-bit preload value to 100ms
    TMRxIF = 0;
    TMRxIE = 1;
    TxCON.ON = 1;
    // Don't forget to enable global/peripheral interrupts if required
    

    Prefereably use Microchip Code Configurator or MCC Harmony to help set up the timer. Read the compiler datasheet (.e.g XC8, XC16 or XC32, depending on which PIC you choose) to find out the appropriate interrupt syntax.

    void ... timerX_interrupt()
    {
       // do your update speedometer stuff here
       ReadADC(9);
    }
    
    

    Implement trapezoidal rule.

    I'm suggesting distance in meters since your speed is in km/h

    Code:

      currentSpeed = readADC(9);  
      currentSpeed_In_Meters_Per_Sec = currentSpeed * ... // do conversion stuff here
      // Trapezoidal rule:
      distance_In_Meters += deltaTime_In_Sec * (prevSpeed_In_Meters_Per_Sec + currentSpeed_In_Meters_Per_Sec )/2;
      prevSpeed_In_Meters_Per_Sec = currentSpeed_In_Meters_Per_Sec; // update prev speed for trapezoidal rule usage
    

    To fully implement using a timer interrupt

    #include <stdbool.h>
    #include <stdint.h>
    
    #define DELTA_T 0.1 // 100 ms
    
    volatile bool new_ADC_valid = false;
    volatile uint16_t new_ADC_value = 0x0000;
    double distance = 0;
    
    void ... timerInterrupt()
    {
      new_ADC_value = readADC(9);  
      new_ADC_valid = true; 
    }
    
    void main ()
    {   
       uint16_t previous_speed = 0x0000;
       //
       // initialise timer and start it here
       //
       while ( 1 ) 
       {
          if ( new_ADC_valid )
          {
             // convert ADC to meters per second
             double current_speed = (double)new_ADC_value;
             new_ADC_valid = false;
             current_speed = // do conversions here
             // DELTA_T is constant in seconds
             // trapezoidal rule:
             distance += DELTA_T * ((current_speed - previous_speed)/2);
             previous_speed = current_speed;        
          }
       }
    }
    
    

    General comments:

    • I'd try and avoid using pow() function with constants and instead just use the constant result; I know, I know compilers should do this but I don't know how smart they are especially the free kind.

    • Make any magic numbers #defines with names so that they mean something, I know it is commented, but it is also helpful to have.

    • 4095 (0xFFF) will probably be your 5V as opposed to 4096, check your ADC datasheet for clarification

    • You'd probably want to have the PIC running on an external oscillator for more accurate timing