Go Back   EcoModder Forum > EcoModding > Instrumentation > OpenGauge / MPGuino FE computer
Register Now
 Register Now
 

Reply  Post New Thread
 
Submit Tools LinkBack Thread Tools
Old 06-11-2015, 02:41 AM   #61 (permalink)
EcoModding Apprentice
 
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
Each page has a display-update subroutine. Write your subroutine, then add it to the array:
Code:
const fptype LEDrenderPage [numLEDpages] = {&LEDnumBlankDisplay, &LEDnumOdoDistance, &LEDnumVehicleSpeed};
... and make sure:
Code:
#define numLEDpages 3
is correct.

  Reply With Quote
Alt Today
Popular topics

Other popular topics in this forum...

   
Old 06-13-2015, 12:51 AM   #62 (permalink)
EcoModding Lurker
 
Join Date: Jun 2015
Location: Australia
Posts: 18
Thanks: 3
Thanked 0 Times in 0 Posts
thanks!

After much effort i got the display responding to the code and displaying, however i have been using the digit series rather than the code you have written to display the numbers, which isnt working for me. if i try to get to to calculate the speed, it seems to not include the clock (time component) in the calculations and thus displays the odo distance..

im just wondering, to use the code at the bottom of the script, what is the command to use your code?
  Reply With Quote
Old 06-13-2015, 01:10 AM   #63 (permalink)
EcoModding Apprentice
 
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
Script? What do you mean?
  Reply With Quote
Old 06-13-2015, 05:58 AM   #64 (permalink)
EcoModding Lurker
 
Join Date: Jun 2015
Location: Australia
Posts: 18
Thanks: 3
Thanked 0 Times in 0 Posts
script.. yes :P

in V3, at the bottom you have set up the script to convert the numbers to binary and organize them onto the appropriate displays depending on whether 1, 10 or 100. im just wondering what would i have to quote to send the numbers to that part of the script?
  Reply With Quote
Old 06-13-2015, 06:37 AM   #65 (permalink)
EcoModding Apprentice
 
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
Ah.
Code:
ledDisplayNumerical(byte addr, int value, byte forceDig_dpSelect);
addr is the i2c address of the saa1064, value is the number you want to display, forceDig_dpSelect is a bit field that disables automatic leading-zero blanking (the first 4 bits) and controls the decimal point segments of the display (the last 4 bits). It relies on the values in findSegs() to decide which display segments (driver pins) need to be lit to create the requested character (decimal to 7-segment decoding) and saa1064Update() to actually push the formatted data to the chip.
  Reply With Quote
Old 06-13-2015, 09:15 AM   #66 (permalink)
EcoModding Lurker
 
Join Date: Jun 2015
Location: Australia
Posts: 18
Thanks: 3
Thanked 0 Times in 0 Posts
please excuse my ignorance.... but im really having trouble with this!

so i just substitute, for example vssSpeed, variable into the value, 0x70 for the address to get it to display the vss speed?

something like this:

Code:
void ledDisplayNumerical(byte(0x70), int (LEDvssSpeed), byte (forceDig_dpSelect))
{
**float LEDvssSpeed = 0;
**float distance = ( float(vssPulseCount) / float(vssPulsesPerUnitDistance) );
**if(vssCyclePeriod != 0) {LEDvssSpeed = ( vssCyclePeriodToSpeedNumerator / float(vssCyclePeriod) );}
**if(micros() > vssLastOnTime + vssTimeOut) {LEDvssSpeed = 0;}
**
  Reply With Quote
Old 06-13-2015, 11:18 AM   #67 (permalink)
EcoModding Apprentice
 
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
No, ledDisplayNumerical() is a subroutine. You use it as a command in your main program (loop) as you would digitalRead(), analogWrite() and so forth.
When you execute a subroutine, the program execution jumps down to the subroutine (the scripts you were referencing earlier), runs through the code once (or until it hits a return command) and then goes back and continues where it left off. Each time a subroutine is called, a new instance of the routine is created - nothing is carried over from the last run through unless it is passed in as an argument (explained below) and back out as a return, or the data is stored in a global variable (an int or byte or string or whatever declared at the top of your file, before setup() or loop(). If a variable is declared within a subroutine (or in its list of arguments), it is unique to that instance of the routine and is lost once the routine finishes.
Subroutines can call other subroutines (as is the case here), in which case they sort of stack up. When the sub-subroutine finishes, it goes back the the line that called it in the original subroutine. Once the original subroutine finishes, it goes back to the main program.
Subroutines can even call themselves (known as a recursive routine), though they need to be coded carefully to be sure the routine actually finishes doing what it's supposed to and not get stuck in an infinite loop of calling itself. Consideration also needs to be given to available memory. The variables used internally by a subroutine use RAM until the subroutine completes, so a recursive routine with many variables and sub-instances can quickly fill available RAM.
You pass arguments (relevant variables, strings, constants and such) to the subroutine in the parenthesis following the command. If the subroutine has more than one argument (inbound piece of data), they're separated by commas. In the case of ledDisplayNumerical(), the byte and ints in the first line are telling you what the subroutine is expecting as far as the kind of data you're handing it, and what names it gives those various pieces of data within the subroutine.

Example:
Code:
void LEDnumOdoDistance()
{
  ledDisplayNumerical(firstSaa1064Address, (10*vssPulseCount)/vssPulsesPerUnitDistance, 0x32);
}
LEDnumOdoDistance() is a subroutine that gets called by the main program (in a non-obvious way, i'll get to that). It only has one line of code that it executes:
Code:
ledDisplayNumerical(byte addr, int value, byte forceDig_dpSelect);
which has three arguments. "firstSaa1064Address" is a constant (stored in flash rather than RAM) byte (8 bits, written in hex as 0x##) which equals 0x70 as you seem to have figured out. "value" is an integer number that you want to display. If you want to display a float (something with a decimal point), you need to multiply it by 10 or 100 or whatever to get an integer you can display, then add the appropriate decimal point by flipping the appropriate bit of "forceDig_dpSelect".
Assuming the original subroutines (ledDisplayNumerical(), findSegs() and saa1064Update() ) are still intact and working, that should put your number on the display.

Going back to the non-obvious way that LEDnumOdoDistance() gets called, there's a constant declared at the top of the program:
Code:
const fptype LEDrenderPage [numLEDpages] = {&LEDnumBlankDisplay, &LEDnumOdoDistance, &LEDnumVehicleSpeed};
LEDrenderPage is an array - a variable name with multiple slots for information, though they all have to be the same type - int, byte, string or whatever. In this case, a custom data type is used: fptype. Don't ask me how that data type creation works - I begged the line of code out of a more knowledgable programmer. Anyway fptype lets you store a list of subroutines. To get the data back out of an array, you use it like a variable, but with [slot#] after the variable name: arrayName[4] would give you element 4 (5th slot, 0 is the 1st slot) in the array named "arrayName". You can also use a variable to select which array slot you want to access: arrayName[elementNumber].
In the actual program, you'll see:
Code:
LEDrenderPage[currentLEDPage]();
fptype isn't just a list of the names of subroutines, but their actual addresses. When you select an element from LEDrenderPage, it tells the processor where to go to execute that code. The parenthesis after the array name gets the processor to actually go there and run the subroutine.

All this lets you control what piece of data is being gathered, processed and displayed on the LED display by changing the value of currentLEDPage. The same thing is done with the main LCD display. Further, a 2-dimensional array (think a grid of slots - you feed it two numbers, the row and column of the grid, to get the piece of data you want) is used for the buttons' functions. The columns are the physical buttons 1 through 4 and a null (no buttons pressed) position. The rows are what page is being sent to the main display. That lets you change what the buttons do depending on the page that's being displayed... It would be silly to have a button dedicated to resetting the odometer when you're looking at a page with only MPG and fuel flow rate on it.
  Reply With Quote
The Following User Says Thank You to bobski For This Useful Post:
Theitguy (06-13-2015)
Old 06-13-2015, 08:23 PM   #68 (permalink)
EcoModding Lurker
 
Join Date: Jun 2015
Location: Australia
Posts: 18
Thanks: 3
Thanked 0 Times in 0 Posts
Wow!

Thankyou, i was a LONG way off..... haha...

so if i declare a variable in a calculation, can i just insert that variable like so?


void LEDnumOdoDistance()
{

float vssSpeed = 0;
float distance = ( float(vssPulseCount) / float(vssPulsesPerUnitDistance) );
if(vssCyclePeriod != 0) {vssSpeed = ( vssCyclePeriodToSpeedNumerator / float(vssCyclePeriod) );}
if(micros() > vssLastOnTime + vssTimeOut) {vssSpeed = 0;}

ledDisplayNumerical(firstSaa1064Address,vssSpeed, 0x32);
}
  Reply With Quote
Old 06-13-2015, 09:51 PM   #69 (permalink)
EcoModding Apprentice
 
Join Date: Jan 2010
Location: Newark, DE
Posts: 143

'91 CRX - '91 Honda CRX DX
90 day: 34.91 mpg (US)
Thanks: 0
Thanked 14 Times in 14 Posts
Yep, so long as vssSpeed isn't already declared as a global variable (looks like it's free to me).
Though you may want to copy/paste the whole subroutine and give it a unique name like LEDnumFloatVSSspeed(). The "float distance = " line isn't doing anything useful in this routine, so you can delete it. Then go up to the top of the program and add &LEDnumFloatVSSspeed to LEDrenderPage, and change #define numLEDpages to 4. Or you can just replace &LEDnumVehicleSpeed on the list and leave numLEDpages at 3.
  Reply With Quote
Old 06-13-2015, 10:41 PM   #70 (permalink)
EcoModding Lurker
 
Join Date: Jun 2015
Location: Australia
Posts: 18
Thanks: 3
Thanked 0 Times in 0 Posts
Ugh,

No matter what i do it still will only display the odo distance....

i just want to constantly display the speed on the LED display...

could you have a look for me?
I have attached the LEDVssSpeed routine just under the VssBaseinfopage..

[code]#include <LiquidCrystal.h> //support library for character LCDs
#include <Wire.h> //support library for I2C

#define numButtons 5 //numButtons should be 1 more than the actual number of buttons present to account for an idle state
#define numPages 3 //the number of information/menu pages - used to define the size of the function pointer arrays
#define numLEDpages 3

#define vssPulsesPerUnitDistance 2485 //vehicle-specific calibration information
#define vssTimeOut 250000 //how many microseconds to wait before deciding the car is stopped when receiving no VSS pulses (only affects displayed speed)

#define injectorOnePin 11
#define injectorOneActiveState HIGH //whether the injector input is active high or low (valid values HIGH and LOW)
#define injectorMicrosecsPerUnitVolume 200000000
#define injectorTimeOut 125000 //how many microseconds to wait before deciding the injectors aren't firing

#define lcdBacklightPin 10

#define distanceUnits "Kilometer"
#define distanceUnitsShort "Km"
#define speedUnits "Kilometer/Hour"
#define speedUnitsShort "KpH"
#define volumeUnits "Litre"
#define volumeUnitsShort "L"
#define flowUnits "Litres/Hour"
#define flowUnitsShort "LpH"
#define fuelEconomyUnits "Kilometers/Litre"
#define fuelEconomyUnitsShort "KpL"
#define engineSpeedUnits "Rev/Min"
#define engineSpeedUnitsShort "RpM"


typedef void (*fptype)(); //support for function pointers

int buttonState = 0; //variables used for UI button handling
int buttonPressed = 0;
/* buttonFunctions is an array of function pointers used to make the software functionality of the physical buttons
change depending on context. It is a 2D array, the first coordinate being the page number, the second being the
button functions (no buttons pressed, button1 pressed, button2 pressed and so on).*/
const fptype buttonFunctions [numPages] [numButtons] = { {&buttonVoidAction, &buttonVoidAction, &buttonVoidAction, &buttonVoidAction, &buttonNextPage},
{&buttonVoidAction, &buttonResetInjCumulative, &buttonVoidAction, &buttonVoidAction, &buttonNextPage},
{&buttonVoidAction, &buttonResetVssPulseCount, &buttonVoidAction, &buttonVoidAction, &buttonNextPage} };

int displayBrightness = 512; //sets illumination brightness on displays, 0 (off) to 1023 (full bright)

/* renderPage is an array of function pointers used to select which text and data to send to the LCD display. Each
pointer in the array corresponds to a page-print-function that can be called to print text and data to the display.*/
const fptype renderPage [numPages] = {&mpgBasicPage, &injectorBaseInfoPage, &vssBaseInfoPage};
int currentPage = 0; //which page to actively display
unsigned long screenUpdatePeriod = 100; //the time (in ms) to wait between LCD updates
unsigned long lastScreenUpdate = 0; //when the screen was last updated

const fptype LEDrenderPage [numLEDpages] = {&LEDnumVehicleSpeed};



const byte firstSaa1064Address = 0x70; //8-bit address with ADR tied to ground
const byte defaultSaa1064ControlByte = 0x37; //control byte: 9 mA drive current, no segment test, all digits active, multiplexed
unsigned long lastLedUpdate = 0;
unsigned long ledUpdatePeriod = 10;

unsigned long injOnToggleTime = 0; //variables used for injector pulsewidth and frequency measurement
unsigned long injCyclePeriod = 0;
unsigned long injLastOnToggleTime = 0;
unsigned long injOnInterval = 0;
unsigned long injCumulativeOnTime = 0;

unsigned long vssCyclePeriod = 0; //variables used for vehicle speed and distance measurement
unsigned long vssLastOnTime = 0;
unsigned long vssPulseCount = 0;
const float vssCyclePeriodToSpeedNumerator = 3600000000 / vssPulsesPerUnitDistance;

LiquidCrystal lcd( 8, 9, 4, 5, 6, 7 );


/*================================================= ================================================== ================*/
//The setup routine (runs at startup) and main program (loops constantly):

void setup()
{
attachInterrupt(0,singleInjectorInterrupt,CHANGE); //injector PWM measuring interrupt (pin 2)
attachInterrupt(1,basicVssInterrupt,RISING); //VSS measuring interrupt (pin 3)
pinMode(10,OUTPUT); //pin 11 is PWM backlight control for the LCD

Wire.begin(); //analog 4 and 5 are used for SDA and SCL of the I2C port

lcd.begin(16,2); //initialize the LCD as 16x2 characters
lcd.clear();
}


void loop()
{
buttonState = readAnalogButtonArray();
if(buttonState == 0) buttonPressed = 0; //this section takes action once per button press
else if(buttonPressed == 0)
{
buttonFunctions[currentPage][buttonState]();
buttonPressed = 1;
}

if(millis() >= lastScreenUpdate + screenUpdatePeriod) //this section updates the LCD screen at regular intervals
{
renderPage[currentPage]();
analogWrite(lcdBacklightPin, (displayBrightness << 4));
lastScreenUpdate = millis();
}

if(millis() >= lastLedUpdate + ledUpdatePeriod)
{
ledDisplayNumerical(firstSaa1064Address, (10*vssPulseCount)/vssPulsesPerUnitDistance, 0x32);
lastLedUpdate = millis();
}
}


/*================================================= ================================================== ================*/
//The following routines are interrupts.

void singleInjectorInterrupt()
{
if(digitalRead(injectorOnePin) == injectorOneActiveState)
{
injOnToggleTime = micros();
injCyclePeriod = injOnToggleTime - injLastOnToggleTime;
injLastOnToggleTime = injOnToggleTime;
}
else
{
injOnInterval = micros() - injOnToggleTime;
if(injOnInterval > 50000) { injOnInterval = 0; } //reject any erroneously long injector pulses
injCumulativeOnTime = injCumulativeOnTime + injOnInterval;
}
}


void basicVssInterrupt() //this interrupt counts pulses (distance) and measures cycle time (speed)
{
unsigned long vssNow = micros();
vssCyclePeriod = vssNow - vssLastOnTime;
vssLastOnTime = vssNow;
vssPulseCount++;
}


/*================================================= ================================================== ================*/
//The following are button-press functions, called to perform an action when a button is pressed.

void buttonVoidAction() { } // do nothing


void buttonNextPage()
{
lcd.clear();
currentPage++;
if(currentPage >= numPages) {currentPage = 0;}
if(currentPage < 0) {currentPage = 0;}
}


void buttonResetInjCumulative()
{
injCumulativeOnTime = 0;
}


void buttonResetVssPulseCount()
{
vssPulseCount = 0;
}


/*================================================= ================================================== ================*/
/* The following are LCD page-render functions. Each function should contain the commands necessary to print a page of
information to the LCD display.*/

void injectorBaseInfoPage() /*for 16x2 character displays. prints "### us" on the top line and "### ###" on the bottom.
cumulative microsecs, current pulse on microsecs and cycle microsecs, respectively*/
{
lcd.clear();

lcd.setCursor(0,0);
lcd.print(injCumulativeOnTime);
lcd.setCursor(14,0);
lcd.print("us");

if(micros() < injOnToggleTime + injectorTimeOut)
{
lcd.setCursor(0,1);
lcd.print(injOnInterval);

lcd.setCursor(8,1);
lcd.print(injCyclePeriod);
}
else
{
lcd.setCursor(0,1);
lcd.print("---");

lcd.setCursor(8,1);
lcd.print("---");
}
}


void vssBaseInfoPage() /*for 16x2 character displays. prints "### p ### Mi" on the top line and "### ### MPH" on
the bottom. pulses, miles, pulselength and speed respectively.*/
{
float vssSpeed = 0;
float distance = ( float(vssPulseCount) / float(vssPulsesPerUnitDistance) );
if(vssCyclePeriod != 0) {vssSpeed = ( vssCyclePeriodToSpeedNumerator / float(vssCyclePeriod) );}
if(micros() > vssLastOnTime + vssTimeOut) {vssSpeed = 0;}

lcd.clear();

lcd.setCursor(0,0);
lcd.print(vssPulseCount);
lcd.setCursor(6,0);
lcd.print("p");

lcd.setCursor(8,0);
lcd.print(distance);
lcd.setCursor(14,0);
lcd.print(distanceUnitsShort);

lcd.setCursor(0,1);
lcd.print(vssCyclePeriod);

lcd.setCursor(8,1);
lcd.print(vssSpeed);
lcd.setCursor(13,1);
lcd.print(speedUnitsShort);
}

void LEDnumVehicleSpeed()
{

float LEDvssSpeed = 0;
float distance = ( float(vssPulseCount) / float(vssPulsesPerUnitDistance) );
if(vssCyclePeriod != 0) {LEDvssSpeed = ( vssCyclePeriodToSpeedNumerator / float(vssCyclePeriod) );}
if(micros() > vssLastOnTime + vssTimeOut) {LEDvssSpeed = 0;}

ledDisplayNumerical(firstSaa1064Address,LEDvssSpee d, 0x32);
}

/*void LEDnumVehicleSpeed()
{
ledDisplayNumerical(firstSaa1064Address, (10*vssPulseCount)/vssPulsesPerUnitDistance, 0x32);
}*/










void mpgBasicPage() //for 16x2 character displays. prints "Trip: ### MPG" on the top line and "Inst: ### MPG" on the bottom
{
float tripMpg = ( (float(vssPulseCount) / vssPulsesPerUnitDistance) / (float(injCumulativeOnTime) / injectorMicrosecsPerUnitVolume) );
float instantMpg = ( (vssCyclePeriodToSpeedNumerator/float(vssCyclePeriod)) / float(3600000000 / (injectorMicrosecsPerUnitVolume)*(float(injOnInter val)/float(injCyclePeriod)))); //that's MPH/GPH fyi

lcd.clear();

lcd.setCursor(0,0);
lcd.print("Trip: ");
lcd.print(tripMpg);
lcd.setCursor(13,0);
lcd.print(fuelEconomyUnitsShort);

lcd.setCursor(0,1);
lcd.print("Inst: ");
if(micros() < injOnToggleTime + injectorTimeOut) {lcd.print(instantMpg);}
else if(micros() > vssLastOnTime + vssTimeOut) {lcd.print("0.00");}
else{lcd.print("---");}
lcd.setCursor(13,1);
lcd.print(fuelEconomyUnitsShort);
}


/*================================================= ================================================== ================*/
//The following are functions for and related to displaying data on LED displays (7-segment numerical, bar graph, etc)

void saa1064Update(byte addy, byte ctrl, byte data0, byte data1, byte data2, byte data3) //This function controls an SAA1064 I2C LED driver
{
Wire.beginTransmission(addy >> 1); //bit-shift the 8-bit address to 7-bit for wire.h
Wire.write(0x00); //byte-address pointer
Wire.write(ctrl); //control register byte
Wire.write(data0); //first display output byte (7 segs + decimal, 8 discreet LEDs or whatever)
Wire.write(data1); //second display output byte
Wire.write(data2); //third display output byte
Wire.write(data3); //fourth display output byte
Wire.endTransmission();
}


void ledDisplayNumerical(byte addr, int value, byte forceDig_dpSelect) //This routine formats numerical variables for display on a 4-digit 7-segment display
{
byte ctrlByte = (defaultSaa1064ControlByte & 0x0F) | ((displayBrightness >> 3) & 0x70);

byte digitOne = findSegs(value % 10); //break value up into its component numerical digits and convert them to 7-segment font codes
byte digitTwo = findSegs((value % 100) / 10);
byte digitThree = findSegs((value % 1000) / 100);
byte digitFour = findSegs((value % 10000) / 1000);

if((value < 1000) && ((forceDig_dpSelect & 0x80) == 0)) {digitFour = 0x00;} //the upper four bits of forceDig_dpSelect disable leading zero blanking digit by digit
if((value < 100) && ((forceDig_dpSelect & 0x40) == 0)) {digitThree = 0x00;}
if((value < 10) && ((forceDig_dpSelect & 0x20) == 0)) {digitTwo = 0x00;}
if((value < 1) && ((forceDig_dpSelect & 0x10) == 0)) {digitOne = 0x00;}

digitOne = digitOne | ((forceDig_dpSelect << 7) & 0x80); //the lower four bits of forceDig_dpSelect activate the decimal points
digitTwo = digitTwo | ((forceDig_dpSelect << 6) & 0x80);
digitThree = digitThree | ((forceDig_dpSelect << 5) & 0x80);
digitFour = digitFour | ((forceDig_dpSelect << 4) & 0x80);

//send digits serially to display chip. digit order is a matter of hardware wiring. play around with it if yours isn't coming out right
saa1064Update(addr, ctrlByte, digitFour, digitTwo, digitThree, digitOne);
}


byte findSegs(int alpha) //this routine converts a single digit decimal into a byte to drive a 7-segment display
{
switch(alpha)
{
case 0:
return 0x3F; /*These byte values are designed for a SAA1064 wired with P1-8 connected to a, b, c, d, e, f, g, dp of the first
digit (or pair of digits if multiplexing), respectively. P9-16 are similarly connected to the second digit */
case 1:
return 0x06;

case 2:
return 0x5B;

case 3:
return 0x4F;

case 4:
return 0x66;

case 5:
return 0x6D;

case 6:
return 0x7D;

case 7:
return 0x07;

case 8:
return 0x7F;

case 9:
return 0x6F;

default:
return 0x00;
}
}


/*================================================= ================================================== ================*/
//The following are miscellaneous functions.

int readAnalogButtonArray() //this function converts the value from the button voltage divider to a useful value
{
int analogIn = analogRead(0);

if(analogIn > 120)
{
if(analogIn > 380)
{
if(analogIn > 640)
{
if(analogIn > 900)
{
return 4;
}
return 3;
}
return 2;
}
return 1;
}
return 0;
}

[code]

  Reply With Quote
Reply  Post New Thread


Thread Tools




Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.
Content Relevant URLs by vBSEO 3.5.2
All content copyright EcoModder.com