Speed sensor

I wanted to use two separate current sensing IC’s one for each motor instead of the one from the elektor design. Günter Gerold connects these to a separate Atmega168 and I connected mine in the same way so that i could also use his Bascom software for this secondary processor.

The elektor design has the ACS755 SCB-100 current sensing IC which is now obsolete. So instead I used ACS758 SCB 100 which is compatible to this one, and i used two of these.
Maybe it would have been better to use ACS758 SCB-50 to get more accuracy but i had two 100 amp units lying around.
The Atmega168 talks to the main processor board via I2C which is the reason that the SCL and SDA connectors have been brought out to the RS232 connector on the main Zzaag microprocessor CPU..
In the picture on the left you can see the additional Atmega168 which is interfacing with both the gearsonsors and the current sensors.
At the top left you can see the connector which attaches to the main microprocessor PCB and the FDTI programming connector.
The connectors at the bottom left are those for the gearsensors themselves.

The software is essentially the same as on the Gerold – Elektronik website except that two LEDs have been added which blink each time an interrupt is received from the geartooth sensors. The LEDs are connected to PB4 and PB5 respectively. This rather simplifies debugging asthe LEDs visualise if the program “sees” the geartooth sensors. The software caters for sensors of the type ATS667 or ATS665 which do not have sense direction but also the ATS657 which has direction sensing onboard. I have experimented with both the ATS667 and the ATS657 with thanks to Allegro who did send me samples.
I finally ended up using the ATS657 because of the direction sensing that this chip has.
The software is written in Bascom but is small enough to be usable in the freeware Bascom compiler. So there was no reason to change this to Arduino and i left it as is.
Below is the circuit diagram of the additional hardware. You can see the gearsensor connections and the current sensing ACS758 SCB-100.
As previously said the software i used for the gearsensor CPU was almost identical to that of Gerold – Elektronik. It is compiled using the freeware bascom compiler. This creates a HEX file which can be loaded into the Atmega168 using avrdude in the same way as programming a new bootloader into an arduino. I use the very nice YCIT GUI which you can download from here. A picture of the settings can be seen on the left. In the three pictures below you can see the geartoothsensor circuit boards. The one on the right is the ATS667 mounted in place on the motor in the same way as on the Gerold – Elektronik website. I used a small piece of board to mount the device on. You can also look at the Gerold – Elektronik website to see how it can be mounted. In the article WheelieGT Elektor also did an article on the subject with very good description as to how to mount etc.
The two pictures below on the left show the ATS657 circuit which is copied from the Elektor Wheelie forum. I simply wired this on a small piece of veroboard and it worked without modification. In order to test this software you can connect the I2C connection to the main Zzaag CPU via the 2 * 5 connector and run the following arduino program on it which visualises the data.

[spoiler]
// Test for communication with Gearsensor.bas
// This is a slave I2c program listening to 8bit I2c code 0x40
// This is 0x20 or 32 in 7 bit code leaving the LSB as read. Wire deals with this 
// automatically so don't bother changing
// It simply reads the relevant data from the Atmega168 running the Bascom program
// mentioned above.
// The data is coded as follows
//  Byte 1 and 2: Speed left
//  Byte 3 and 4: Speed right
//  Byte 5 and 6: Current left
//  Byte 7 and 8: Current right
//  Byte 9 Bit 6: left Direction
//  Byte 9 Bit 7: No Direction Flag left
//  Byte 9 Bit 3: right Direction
//  Byte 9 Bit 4: No Direction Flag right
// I have tested both ATS667 and ATS657 so with and without direction data. Gearsensor.bas measures the pulses and draws
// a conclusion about which direction.
// The Atmega32 program on the CPU print needs to be adapted to take care of this.
// I now use ATS657 which also generates direction data.
// I also added two LED's to PORTB.4 and PORTB.5 which toggle on every interrupt.
// One on INT0 and one on INT1. This way you can see the pulses arriving from the sensor. Very handy!!!

#include <Wire.h>

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop()
{
  Wire.requestFrom(32, 9);    // request 9 bytes from slave device 32
  int i=0;
  while(Wire.available())    // 
  { 
    i++;
    int c = Wire.receive(); // receive a byte as integer
    Serial.print(c,HEX); Serial.print(" "); // print the HEX number
    if (i==9) { 
    Serial.print(" left = ");
    if ((c & 128) == 128) {Serial.print("None ");}
    if ((c &  64) == 64) {Serial.print("F");} else {Serial.print("B");}
    Serial.print(" right = ");
    if ((c & 16) == 16) {Serial.print("None ");}
    if ((c & 8) == 8) {Serial.print("F");} else {Serial.print("B");}
    }
  }
  Serial.println();
  delay(500);
}
[/spoiler] [spoiler]
'---------------------------------------------------------------------------
' _____ _ _ _ _ _ _ _ _ _
' | __| |___| |_| |_ ___ ___ | | | | |_ ___ ___| |_|___
' | __| | -_| '_| _| . | _| | | | | | -_| -_| | | -_|
' |_____|_|___|_,_|_| |___|_| |_____|_|_|___|___|_|_|___|
'
' (c)2009 Elektor International Media B.V.
'---------------------------------------------------------------------------
'Version: Speed100 - Peripheral Measurements for Elektor
'---------------------------------------------------------------------------
'Purpose:
' Measurement of Speed, Current and direction of rotation for the Elektor-Wheelie
' Byte 1 and 2: Speed left
' Byte 3 and 4: Speed right
' Byte 5 and 6: Current left
' Byte 7 and 8: Current right
' Byte 9 Bit 6: left Direction
' Byte 9 Bit 7: No Direction Flag left
' Byte 9 Bit 3: right Direction
' Byte 9 Bit 4: No Direction Flag right
'---------------------------------------------------------------------------
'Description:
' Until now only speed is calculated out of gear tooth sensor sensors
'---------------------------------------------------------------------------'
'Changes Log:
' Speed090: 11.12.2009
' First code by Günter Gerold
' Speed091: 11.12.2009
' Handling of Timer1 overflow by Thomas Scherer
' Speed092: 12.12.2009
' Fallback to Wordmax if no Interrupts after 2 Timer1 Overflows
' Speed093: 13.12.2009
' Fallback to 10000 if no Interrupts after 228,48 ms (slower than 0,333 km/h)
' Timer0 and Timer2
' Speed094: 11.07.2010
' Measure the currents of the Sensors.
' for a short time of the ADC we dont wait for the result
' if the ADC is ready we store the value in the interrupt and switch the channel.
' Speed100: 25.12.2010
' detection of the drivingdirection with the ATS657 45µs-Pulse = forward, 90µs Pulse = backward
'---------------------------------------------------------------------------
'ToDo:
' nothing
'---------------------------------------------------------------------------
'Contact:
' Please post ideas, improvements and code into this thread in the Elektor forum:
' http://www.elektor.de/forum/foren-ubersicht/foren-zu-elektor-projekten/elektorwheelie.996934.lynkx
' Günter Gerold: tv@gerold-online.de
'---------------------------------------------------------------------------
$regfile = "m168def.dat"
$crystal = 16000000
$baud = 57600

'timeout when waiting for pulses
Config Timer0 = Timer , Prescale = 1024
On Timer0 Int_timer0
Start Timer0
'measure the distance between two pulses (speed)
Config Timer1 = Timer , Prescale = 1024 'Time base: timer clock = 15625Hz = 64µs/pulse
Start Timer1
'measure the width of the pulse (direction)
Config Timer2 = Timer , Prescale = 64 '4µs per Tick
Start Timer2
'Sensor inputs generate interrupts at rising and falling flank
Config Int0 = Change 'Interrupt for the left tooth sensor
Config Int1 = Change 'Interrupt for the right tooth sensor
On Int0 Int_0
On Int1 Int_1
Led0 Alias Portb.5
Led1 Alias Portb.4
Config Led0 = Output
Config Led1 = Output
Led0 = 0
Led1 = 0
'measure Currentsensors
On Adc On_adc 'ADC Interrupt
Admux = &B01000000
Adcsra = &B11001111
Dim Cur_ch As Bit

'all Data stored in an Array, so its easy to send with I²C
Dim Data_array(10) As Byte
Dim Speed_left As Word At Data_array(1) Overlay
Dim Speed_right As Word At Data_array(3) Overlay
Dim Curr_left As Word At Data_array(5) Overlay
Dim Curr_right As Word At Data_array(7) Overlay
Dim Direction_byte As Byte At Data_array(9) Overlay

Dim Count_left_old As Word 'detect Timer1 overflow
Dim Count_right_old As Word 'detect Timer1 overflow
Dim Timer_left As Word 'Value of Timer1 left motor
Dim Timer_right As Word 'Value of Timer1 right motor
Dim Timer0_overflow_l As Byte
Dim Timer0_overflow_r As Byte
Dim Timer2_up_l As Byte
Dim Timer2_down_l As Byte
Dim Timer2_up_r As Byte
Dim Timer2_down_r As Byte
Dim Left_direction As Byte
Dim Right_direction As Byte
Dim Cnt As Byte
Dim Twi_control As Byte
Dim Twi_status As Byte

Const Wordmax = 65535
Const Bytemax = 256

Enable Interrupts
Enable Int0
Enable Int1
Enable Timer0
Enable Timer2
Twsr = 0
Twdr = &HFF
Twar = &H40 'Our I²C Adress
Twcr = &B01000100 'Hardware TWI Slave Transmitter
Speed_left = 10000
Speed_right = 10000
Do
If Timer0_overflow_l > 14 Then '229ms and no Toothinterrupt (255 x 1024 x 14 x 62,5ns = 229ms), we don't wait longer and set a value.
Timer0_overflow_l = 0
Speed_left = 10000 'Set 1/Speed to MAX = 0,12 km/h
Set Direction_byte.7 'no Direction
End If
If Timer0_overflow_r > 14 Then
Timer0_overflow_r = 0
Speed_right = 10000
Set Direction_byte.4 'no Direction
End If

Select Case Right_direction
Case 5 To 17 'CW detected
Set Direction_byte.3
Reset Direction_byte.4
Case 18 To 28 'CCW detected
Reset Direction_byte.3
Reset Direction_byte.4
Case 38 To 53 'no Direction detected
Set Direction_byte.4
End Select
Select Case Left_direction
Case 5 To 17 'CW detected
Set Direction_byte.6
Reset Direction_byte.7
Case 18 To 28 'CCW detected
Reset Direction_byte.6
Reset Direction_byte.7
Case 38 To 53 'no Direction detected
Set Direction_byte.7
End Select
Twi_control = Twcr And &H80
If Twi_control = &H80 Then 'Interruptflag of the Hardware TWI is set
Twi_status = Twsr And &HF8
Select Case Twi_status
Case &HA8: 'Send the first Byte
Twdr = Data_array(1)
Twcr = &B11000100
Cnt = 2
Case &HB8: 'Send additional Bytes
Twdr = Data_array(cnt)
Incr Cnt
Twcr = &B11000100
Case Else : 'Ignore other Commands
Twcr = &B11000100
End Select
If Cnt = 10 Then Adcsra.6 = 1 'If all Data is send via I²C then start the ADC for new current values
End If
Loop

Int_0:
Toggle Led0 'A changing Flank was detected from the left Sensor
If Pind.2 = 1 Then 'the rising Flank
Timer0_overflow_l = 0
Timer_left = Timer1 'Timer1 measures the distance between the pulses
Timer2_up_l = Timer2 'Timer2 measures the width of the pulse
If Timer_left >= Count_left_old Then
Speed_left = Timer_left - Count_left_old
Else
Speed_left = Wordmax - Count_left_old 'Overflow of Timer1
Speed_left = Speed_left + Timer_left
End If
Count_left_old = Timer_left 'store old Value
If Speed_left > 10000 Then Speed_left = 10000
Else 'the falling Flank
Timer2_down_l = Timer2 'calculate the width of the pulse
If Timer2_down_l > Timer2_up_l Then
Left_direction = Timer2_down_l - Timer2_up_l
Else
Left_direction = Bytemax - Timer2_up_l
Left_direction = Left_direction + Timer2_down_l
End If
End If
Return

Int_1:
Toggle Led1 'A changing Flank was detected from the left Sensor
If Pind.3 = 1 Then 'the rising Flank
Timer0_overflow_r = 0
Timer_right = Timer1
Timer2_up_r = Timer2
If Timer_right >= Count_right_old Then
Speed_right = Timer_right - Count_right_old
Else 'Overflow of Timer1
Speed_right = Wordmax - Count_right_old
Speed_right = Speed_right + Timer_right
End If
Count_right_old = Timer_right
If Speed_right > 10000 Then Speed_right = 10000
Else 'the falling Flank
Timer2_down_r = Timer2 'calculate the width of the pulse
If Timer2_down_r > Timer2_up_r Then
Right_direction = Timer2_down_r - Timer2_up_r
Else
Right_direction = Bytemax - Timer2_up_r
Right_direction = Right_direction + Timer2_down_r
End If
End If
Return

Int_timer0: 'count Timer0 Overflows for Timeout of the Wheels
Incr Timer0_overflow_l
Incr Timer0_overflow_r
Return

On_adc: 'ADC is ready and have a new value
If Cur_ch = 0 Then 'value from the left Sensor
Curr_left = Adc
Toggle Cur_ch 'switch to the right sensor
Admux.0 = Cur_ch
Adcsra.6 = 1 'start ADC to get the value from the right sensor
Else
Curr_right = Adc
Toggle Cur_ch
Admux.0 = Cur_ch 'switch to the left sensor, but dont start the ADC. This was made later in the mainloop.
End If
Return
[/spoiler]

Download the files from here Gearsensor.pde  Gearsensor3.bas

segwayprogrammersegwayprototypesegwaygearsensordetail

segwaygearsensorbacksegwaygearsensorfrontsegwaygeartoothschematic

Below is the data from the gearsensor.pde program.
The data is coded as follows:
Byte 1 and 2: Speed left
Byte 3 and 4: Speed right
Byte 5 and 6: Current left
Byte 7 and 8: Current right
Byte 9 Bit 6: left Direction
Byte 9 Bit 7: No Direction Flag left
Byte 9 Bit 3: right Direction
Byte 9 Bit 4: No Direction Flag right
In the screenshot of the output on the right you can see the impact of turning the wheels by hand backwards B and forwards F. The word None means that between turning forward and backwards there was no direction flag set. You can also see in the first four bytes the speed of both wheels.

segwaygearsensoroutput

segway schematic

segway bascom

Leave a Reply