Thursday, January 29, 2009

Weeling Project (cont...)

Finally I start to look in deep to the weeling ratio (I intend to buid an ECU capable to show me if rear weel is spinning by flashing a LED).
The SW must do:
6. LED must blink (flash) proportionally to the spinning ratio:
  • slow spin -> LED is blinking slow
  • high spin -> LED is blinking very fast

The Angular speed is:

while the Tangential speed is:

where r (weel radius) can be derived from the weel circonference lenght:
r = 2 x PI x r

Therefore we have:

what I mean in the picture is that at low speed the front and rear weels can not slip, therefore at low speed it can be calculated the
WeelRatio = RPM_REAR / RPM_FRONT.

The SW must be updated then to calculate such ratio every time the bike is running slow, for example every time the bike speed is between 10 Kh/h and 20 Km/h and save it to a dedicated static variable, as for example:
float fWeelRatio;

During fast accellerating phase, the instant weels ratio can be compared with the stored fWeelRatio variable and the LED can be driven proportionally as described in the following draw:

The thresholds (i.e. 120%, 130%, etc..) can be used defined in a configuration flash area without the need to recompile the project each time.
The software will be updated soon...

Wednesday, January 28, 2009

Weeling Project (Cont...)

Recently I update the software adding the feature to store the odometer distance into the internal Flash.
This are the added static variables:

unsigned int uiDistanceTrip; // ODO meter distance (reset at SW start)
unsigned int uiDistanceOdo; // Total Distance meter (stored in Flash memory)

During the drive also the estimated distance is calculated multiplying the RPM by the weel circonference lenght

// Disntance Calculation
uiDistanceTrip += ( (float) (uiRPM_REAR) * CFR ); // to get Km, divide by 1000

Now, once the bike is stop for at least 3 min, the total distance will be written to the internal Flash:

case STATE_IDLE:
// Initialize the Derivative calculation
uiOldRPM_FRONT = uiRPM_FRONT;
uiOldRPM_REAR = uiRPM_REAR;

// If Bike Speed is greater then 5.0 Km/h then change state to DRIVE
if ( uiSpeed > 50 )
uiWeelingStatus = STATE_DRIVE;

// Reset the Gear shift counter
iGearShift = 0;

// Check since how long the Bike is in IDE state: if more then 300 sec then store ODO into Flash
if ( iGPT_Counter == 300 )
{
uiWeelingStatus = STATE_OFF;
// Update the Total Distance Km
uiDistanceOdo += uiDistanceTrip;
// Reset the Trip meter
uiDistanceTrip = 0;
// Flash the Total Distance Km
EEPROMWrite ( uiDistanceOdo );

}

break;

Afterward the internal status will be set to STATE_OFF. STATE_OFF? Yes, I had to add a new status to be safe and do not continuosly program the flash when the bike is in the pit-lane.
This is the new STate Machine description:

Sunday, January 25, 2009

XE164

"Weeling Project" is progressing in different directions: speed sensor, display and software. The day by day learnng is forcing me to reconsider each day the project's decisions, forst of all, the selected uC. In fact the problem to use the XC164 USB stick is the limited number of avalable I/Os, so it is ok for small projects, but not for the "Weeling Project".
Thanks to my friend Ronny, I got this nice little board:


it is called XE164 KeyChain and provide the soldered XE164 uC on a small pcb with all the IOs routed to two connectors on the board side.

Feature list are available in the Infineon site: XE164 , also available in Italian language XE164 Italian
Here some documents:
Data Sheet, DAvE DIP files (in order to configure DAvE to use the XE164) , Display Driver, Garmin GPS Driver, SD Card Driver

Developmet Kit with CAN-open driver

Monday, January 19, 2009

Speed Sensor

The Weeling Project requires to capture the Rear Weel and Front Weel speed (rotation per minute), therefore I had to think about a method to sense them.
For the Rear Weel, there is no problem , since the bike alreay have a dedicated sensor that goes into the engine control unit (ECU), so, I just need a pick-up and a simple signal conditioning circuit: I think a 100kOhm and 2 diodes as protection circuit for the uC input would be enough.
About the Front Weel speed I plan to use an Inductive Speed Sensor as shown:

It must be mounted as shown:

How it works:
The Inductive Speed Sensor sense the metallic bolts closing each time they comes closer. It is recommended to lave 2mm dinstance between the sensor and the bolts. The nice stuff is that the sensor has a LED on the back-side, so, each time it sense the bolt it also display it.
The sensor has 3 terminals: Vcc, GND and one output. The sensor is NPN, so can be connected in common emiter circuit configuration.
In the next days I will start to play with it and I'll try some input circuits.

Saturday, January 10, 2009

Display IT !

Also during xMas holidays, I have worked on the Display hardware. Since my goal is to use "Weeling" in the race, I need to have something clearly readable: so I prefer to use "old-fashon" 7-segment LED displays and not LCDs. The LED displays are much more lighty, insensible to sun light (if mounted below a red coloured plexiglas glass) and glamour.
I look around and I found the following proposed circuit:


    (taken from FiserTek ).
    As you can see, the circuit is quite simple: there is only one BCD to 7-segment driver (the famous HC4511, HC4511) connected in parallel to several 7-segment displays (the schematic just shows 4 of it, but I connected 6, and more could be connected). Each 7-segment display (displays must be common Kathode) are enabled by the T1, T2, ... BC337 transistors driven from the SV3 signals (CPU outputs).

    In my Weeling Project, I need 6 digits:
    • 4 digits for distance meter (odometer): RED colour
    • 2 digits for cooling water temperature: GREEN colour


    As you can see from the picture, I had no time to create an own pcb, instead I use multi-purpose pcb and I solder it using tiny wires (I know is not nice...).
    So, the total amount of CPU ouputs are:
    • 4 outputs for driving the BCD digit (SV1 connector)
    • 6 outputs for driving the respective BC337 transistors

    The 4 RED digits can be used for the distance meter if the bike speed is below 5 KM/h (so, every time the Weeling Status is "IDLE"), or for Gear indication if speed is above. Please note that due to the BC337 transistors, the hardware is so flexible to permit CPU to drive not all displays: so is possible to keep off some digits if not needed in some conditions (i.e. is not needed to see the distance meter during the ride, it is more important to display other information).
    After soldering and a simple debug (no issues found!), I develop the CPU software to drive it. It is quite simple, it just needs to:
    • configure the neededs ports as outputs
    • decode the digit since I could not use just one I/O port (function "Segmenti()")
    • in the mail endless loop, display the digits one after the other

    Function Segmenti():


    void Segmenti(char num)
    {
    switch(num)
    {
    case 0:
    P2_DATA = 0x00;
    P3_DATA = 0x00;
    break;
    case 1:
    P2_DATA = 0x01;
    P3_DATA = 0x00;
    break;
    case 2:
    P2_DATA = 0x00;
    P3_DATA = 0x40;
    break;
    case 3:
    P2_DATA = 0x01;
    P3_DATA = 0x40;
    break;
    case 4:
    P2_DATA = 0x00;
    P3_DATA = 0x08;
    break;
    case 5:
    P2_DATA = 0x01;
    P3_DATA = 0x08;
    break;
    case 6:
    P2_DATA = 0x00;
    P3_DATA = 0x48;
    break;
    case 7:
    P2_DATA = 0x01;
    P3_DATA = 0x48;
    break;
    case 8:
    P2_DATA = 0x00;
    P3_DATA = 0x20;
    break;
    case 9:
    P2_DATA = 0x01;
    P3_DATA = 0x20;
    break;
    }
    }

    Debug Function:



    while (1)
    {
    //MyDelay();
    //P1_DATA = 0x00; // LED ON

    P1_DATA = 0x80;
    Segmenti( (char) temp);

    //MyDelay();
    P1_DATA = 0x40;
    Segmenti( (char) temp+1);

    if ( i > 65534 ) {
    temp++;
    i = 0;
    }
    if ( temp > 8) {
    temp = 0;
    P1_DATA = 0x20; // LED OFF
    }
    i++;

    }

    This is the working Display during debug phase:

    Sunday, January 4, 2009

    Chrono Timer "Reverse Engineering"

    My friend Baldo ask me to check his chrono, so I took the time to perform some reverse engineering. Let's see what a chrono is: "digital chronometer which can determine lap times using a infrared transponder". Seems to be asy, but how it works?!? You must know that it is composed of two parts: the chrono has to be mounted on the bike and the transponder has to be placed in the pit-lane. A coded infrared beam start and stop the chrono at every bike laps. Typically, the infrared transponder has the selection of transmission channel (i.e. channel 0, 1, ... , 9) so to be immune to signals arriving from other transmitters or sun rays.
    But how it works really?
    The chrono is composed of two parts: the IR receiver and the chrono main unit. I have disassembled the IR receiver, and I have discovered that it contains a quite common components: TSOP1736
    The TSOP17xx – series are miniaturized receivers for infrared remote control systems. PIN diode and preamplifier are assembled on lead frame, the epoxy package is designed as IR filter. The demodulated output signal can directly be decoded by a microprocessor. TSOP17.. is the standard IR remote control receiver series, supporting all major transmission codes.
    In order to discover how it works, I have soldered a cable on the output pin (#3) and I have connected to an oscilloscope, the following video could explain the experiment set-up:


    (I admit that the video is not so clear, I have to improve...)
    Anyhow, the stuff is that the transponder has 10 selectable channels (from 0 to 9), using the oscilloscope is possible to determine the bit-stream and try to understand it. Here what I found:
    - each bit-stream is 10.3 msec long
    - each bit-stream starts with a start sequence Low High Low (Low takes 400usec, High takes 600usec, Low takes 400usec)
    - each bit-stream codes the selected channel

    Channel 0:

    Channel 0 (code 0) is the simpler transmission, where there is no other transition then the start sequence:


    Channel 2:


    Channel 4:


    Channel 6:
    Channel #6 is coded as 4 + 2:

    as shown:


    Channel 7:



    Channel 8:



    Channel 9:



    At the end is nothing really "so difficult", therefore if you would like to build up your own IR transmitter / chrono timer, you have now the basis on how to do it...

    Weeling Project (Con't)

    During xMas holidays, I've been thinking about my Weeling Project, I had the impression that something was not yet ok and also simulations show me that sometime the RPM calculaion is not precise enough. The Weeling Project goal is to show how much the rear weel is weeling (spinning) in respect to the front weel, but if the HW / SW would be not enough precise, I fear that all will be for nothing at the end.
    Therefore I had the idea to define a state machine for the Weeling Project in order to filter the spurious signals. Let's see what I mean:


    Therefore I defined 5 states:
    0) IDLE (or neutral gear): when the bike is running below 5 Km/h
    1) DRIVE: when the bike is moving
    2) ACCELLERATING: when the bike is accelerating
    3) BOOST: when the bike is accellerating very very fast. In this state the Weeling factor will be calculated (in the other states not, so to filter spurious impulses)
    4) BRAKE: when the bike is braking (speed variation is negative or used gear is decreased)
    From the state diagrams it is clear how the states are connected each other, I had to take some decision and I can not promise that I'll not change it (expecially I'm not really sure if the transition from BOOST back to ACCELLERATING makes really sense). In any case the SW is:

    #define STATE_IDLE 0 // Neutral Gear
    #define STATE_DRIVE 1 // Drive slow (V > 5 Km/h)
    #define STATE_BOOST 3 // dV / dT >> xx
    #define STATE_BRAKE 4 // dV / dT <>

    void main(void)
    {
    // USER CODE BEGIN (Main,2)
    unsigned int uiWeeling; // ratio between rear / front weels RPM
    unsigned int uiTmp;
    unsigned int bTransmit; // loop counter
    // USER CODE END

    MAIN_vInit();

    // USER CODE BEGIN (Main,4)
    // T4 As RPM Generator
    GPT1_vStopTmr(GPT1_TIMER_4);
    GPT12E_T4= T4_25ms;
    GPT1_vStartTmr(GPT1_TIMER_4);

    // Clear T6 and Start
    GPT2_vClearTmr(GPT2_TIMER_6);
    GPT2_vStartTmr(GPT2_TIMER_6);

    // LED constantly on to indicate that SW is not yet started
    P1L_P3 = 1;

    // RS232 Start Message
    printf ( "WEELING is RUNNING !!!\n\n" ); /* the 'printf' function call */
    ASC0_ubTxDataReady();
    bTransmit = FALSE;

    while (1)
    {
    // Skip First 5 execution to stabilize acquisition
    if (iGPT_Counter > 5)
    {
    // Derivate Calculation
    iDelta_FRONT = (int) ( uiRPM_FRONT - uiOldRPM_FRONT );
    iDelta_REAR = (int) ( uiRPM_REAR - uiOldRPM_REAR );
    if ( iDelta_REAR > 10 )
    bTransmit = TRUE; // In case of a weeling Comunication is started

    uiOldRPM_FRONT = uiRPM_FRONT;
    uiOldRPM_REAR = uiRPM_REAR;

    // Speed Calculation
    uiSpeed = ( (float) (uiRPM_REAR) * 7.9 ); // to get the first decimal digit, divide by 10

    // Calculate the actual inserted Gear and Display it
    uiTmp = (unsigned int) ( ( (float) uiRPM_ENGINE * 1000 ) / ( (float) uiRPM_REAR ) );
    CalculateGear (uiTmp);

    // Update current State Machine Status
    uiOldWeelingStatus = uiWeelingStatus;
    switch (uiWeelingStatus)
    {
    case STATE_IDLE: // :0
    // Initialize the Derivative calculation
    uiOldRPM_FRONT = uiRPM_FRONT;
    uiOldRPM_REAR = uiRPM_REAR;
    iGearShift = 0;

    if ( uiSpeed > 50 )
    uiWeelingStatus = STATE_DRIVE;
    break;

    case STATE_DRIVE: // :1
    if ( iGearShift > 0 )
    uiWeelingStatus = STATE_ACCELLERATING;
    if ( iGearShift < uiweelingstatus =" STATE_BRAKE;" uiweelingstatus =" STATE_IDLE;"> 0 )
    uiWeelingStatus = STATE_BOOST;
    if ( iDelta_REAR >= 10 )
    uiWeelingStatus = STATE_BOOST;
    if ( iGearShift < uiweelingstatus =" STATE_BRAKE;" uiweeling =" ((float)uiRPM_REAR" igearshift =" 0;" uiweelingstatus =" STATE_ACCELLERATING;" uiweelingstatus =" STATE_BRAKE;"> 0 )
    uiWeelingStatus = STATE_ACCELLERATING;
    if ( (uiSpeed < 50) (uiGEAR == 0) )
    uiWeelingStatus = STATE_IDLE;
    break;
    }
    if (uiOldWeelingStatus != uiWeelingStatus)
    bTransmit = TRUE; // In case of Status Change Comunication is started

    // Transmit the measured values to RS232 for debug purpose
    if ( bTransmit )
    {
    printf ( "\n\nSTATE: %d\n", uiWeelingStatus);
    printf ( "SPEED: %3.1f [Km/h]\n", (float)(uiSpeed/10.0) );
    printf ( "ENGINE RPM: %4.2f [RPM]\n", (float)(uiRPM_ENGINE*60.0) );
    printf ( "GEAR: %d\n", uiGEAR );

    printf ( "FRONT WEEL RPM: %4.2f [RPM]\n", (float)(uiRPM_FRONT*60.0) );
    printf ( "REAR WEEL RPM: %4.2f [RPM]\n", (float)(uiRPM_REAR*60.0) );

    printf ( "FRONT WEEL D/Dt: %d [RPM]\n", iDelta_FRONT );
    printf ( "REAR WEEL D/Dt: %d [RPM]\n", iDelta_REAR );
    printf ( "WEELING RATIO: %3.2f [%%]\n", (float)(uiWeeling/10.0) ); // in percentage

    // no transmission for the following loops
    bTransmit = FALSE;
    }
    }

    }
    // USER CODE END

    } // End of function main


    As visible in the main() function, I have also improved the RS232 transmission in order to transmit only when something is happening. The printed messages are:

    Since the above picture is not really readable, I copy here an example:

    STATE: 2
    SPEED: 72.6 [Km/h]
    ENGINE RPM: 7020.00 [RPM]
    GEAR: 2
    FRONT WEEL RPM: 9120.00 [RPM]
    REAR WEEL RPM: 5520.00 [RPM]
    FRONT WEEL D/Dt: 0 [RPM]
    REAR WEEL D/Dt: 0 [RPM]
    WEELING RATIO: 60.50 [%]

    As it is visible the front weel speed is a too high, this is due to the fact that I executed the "start ACCELLERATION" simulation macro that change only the rear weel speed without changing the front weel and the engine speed (since I do not know how to do it with Keil), here the macro code:

    signal void accUp (void)
    {
    float frequency; // pulse frequency in Hz
    int gear; // gear used
    int pulses; // 1000 pulses per simulation

    frequency = 63; // Hz
    gear = 1;
    pulses = 0;
    printf("Rear: %f [Hz]\n", frequency);

    while(gear < 7)
    {
    swatch (0.5 / frequency);
    PORT5 |= (1 << 12); // set pin 5.12
    swatch (0.5 / frequency);
    PORT5 &= ~(1 << 12); // reset pin 5.12
    pulses = pulses + 1;
    if (pulses > 700)
    {
    pulses = 0;
    gear = gear + 1;
    if (gear == 2) frequency = 92;
    else if (gear == 3) frequency = 115;
    else if (gear == 4) frequency = 134;
    else if (gear == 5) frequency = 150;
    else if (gear == 6) frequency = 165;
    // printf("Rear: %f [Hz]\n", frequency);
    }
    }

    }


    Where the REAR WEEL speed starts from 63 Hz (= 3775 RPM, first gear) and increases to the second, third, ... gear incresing the rear weel speed accordly. This macro is very useful since permit to simulate all the gears from 1 to 6. For the viceversa, I use:

    signal void accDown (void)
    {
    float frequency; // pulse frequency in Hz
    int gear; // gear used
    int pulses; // 1000 pulses per simulation

    frequency = 165; // Hz
    gear = 6;
    pulses = 0;
    printf("Rear: %f [Hz]\n", frequency);

    while(gear > 0)
    {
    swatch (0.5 / frequency);
    PORT5 |= (1 << 12); // set pin 5.12
    swatch (0.5 / frequency);
    PORT5 &= ~(1 << 12); // reset pin 5.12
    pulses = pulses + 1;
    if (pulses > 500)
    {
    pulses = 0;
    gear = gear - 1;
    if (gear == 1) frequency = 63;
    else if (gear == 2) frequency = 92;
    else if (gear == 3) frequency = 115;
    else if (gear == 4) frequency = 134;
    else if (gear == 5) frequency = 150;
    else if (gear == 6) frequency = 165;
    // printf("Rear: %f [Hz]\n", frequency);
    }
    }
    }


    As you can see at the end is quite easy to create own simulation scripts, clearly after accUp() and accDown() I have also created an accUpDownUpDown() (for space reasons I do not attach the macro code, if you are interested, just write to me, I'll mail you).
    As summary of the various simulations, I have compiled:

    Overall I've tested various gears, speeds and States (of the state machine), but not all...


    (meanwhile I'm simulating the SW, I also stared the HW, so stay tuned!!!)