Motorised trolly (skutter v0.3)


106 posts   Page 5 of 5   1, 2, 3, 4, 5
by morphy_richards » Fri Oct 19, 2012 8:10 pm
Getting there .. slowly.

I had half a H bridge (transistor switches to enable motor + and motor - (i.e. forward)) working earlier today but then I broke it somewhere by trying to add the other half in. I'll have to carefully build it up step by step testing each bit as I go along. Probably on a totally separate breadboard.

Wont be able to do anymore until next week at some point but I think I have everything together now in theory.
I've been struggling a bit with getting my head around talking to the MCP23008 and making it do what I want reliably. It's got lots of registers that need to be configured properly before it will do what I want it to do.

I've also gone back to using the Quick2Wire API for now, mainly because there's more in the way of example code to work from but it's been a bit of a slog. I'm still trying to decide whether to use smBus or Quick2wire. Smbus only works with Python 2 and Quick2wire with Python 3. Smbus is stable but runs on a (possibly) soon to be deprecated language, Quick2Wire runs on Python 3 which is probably going to be more widely used for education.

I've knocked up a fairly simple BigTrak motor control test program using Quick2Wire this evening but am yet to test it. It's based around the MCP23008 test code I was using earlier to fire off a single transistor and make the motor turn. Here it is ...

Code: Select all
#!/usr/bin/env python3
import quick2wire.i2c as i2c
import time
address = 0x20
#define all the registers
IODIR=0x00       # 0 = out 1 = in
IPOL=0x01       #input/output polarity on gpio. bit 7 -> bit 0. If bit set, gpio value will reflect the inverted value
GPINTEN=0x02    #interrupt on change. bit 7 -> bit0 If bit is set then will generate an interrupt if that pin changes
DEFVAL=0x03    #default value to compare against GPINTEN. bit 7 -> bit0. If bit is set, opposite value on corresponding pin will generate interrupt
INTCON=0x04    #interrupt control register. If bit is set then corresponding IO pin will be compared against that set in DEFVAL register
IOCON=0x05       #setup / config. bit 5 = sequential operation. bit 4 slew rate. bit 3 not used. bit 2 open drain. bit 1 interrupt polarity sets polarity of INT pin. ONly functions if open drain bit is clear
GPPU=0x06       #gppu pull up resistor, bit 7 -> bit 0. if bit set, if bit set and pin is input then this will pull up the pin with 100k resistor
INTF=0x07       #interrupt flag register. bit7 -> bit 0. bit set means the associated pin will generate an interrupt. A set bit in this register tells us which pin caused the interrupt. READ ONLY.
INTCAP=0x08    #interrupt capture. Captures GPIO value at time of interrupt. bit 7 -> bit 0. Remains unchanged until interrupt cleared via a read of INTCAP or GPIO
GPIO=0x09       #the GPIO. bit7 -> bit 0
OLAT=0x0A       #output latches

#reset the mcp23008
self.master.transaction(
   #set IODIR as OUTPUT
   i2c.writing_bytes(address, IODIR, 0b00000000))
#reset all the other registers
for reg in [IPOL, GPINTEN, DEFVAL, INTCON, IOCON, GPU, INTF, INTCAP, GPIO, OLAT]:
   self.master.transaction(
      i2c.writing_bytes(address, reg, 0b00000000))
      
#Set the GPIO's to turn on / off transistors in H bridge. See associated circuit diagram.
#GPIO 0 - 1 = motor 1 fwd.
#GPIO 1 - 1 = motor 1 fwd.
#GPIO 2 - 1 = motor 1 rev.
#GPIO 3 - 1 = motor 1 rev.
#GPIO 4 - 1 = motor 2 fwd
#GPIO 5 - 1 = motor 2 fwd
#GPIO 6 - 1 = motor 2 rev
#GPIO 7 - 1 = motor 2 rev

#-----------------IMPORTANT DANGER IMPORTANT-----------------
#IF GPIO 0, 1 is "1" THEN GPIO 2, 3 must be "0" ELSE transistor short circuit and "Bang!"
#IF GPIO 4, 5 is "1" THEN GPIO 6, 7 must be "0" ELSE transistor short circuit and "Bang!"
#------------------------------------------------------------
#set all GPIO off
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00000000))
#test motor 1 and motor 2 FWD for  3 secs
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00000011))
time.sleep(3)
#set all GPIO off
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00000000))
time.sleep(1)
#test motor 1 and motor 2 REV for 3 secs
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00001100))
time.sleep(3)
#set all GPIO off
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00000000))
time.sleep(1)
#test hard right turn for 1 sec
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b11000011))
time.sleep(1)
#test hard left turn for 1 sec
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00111100))
time.sleep(1)
#set all GPIO off
self.master.transaction(
      i2c.writing_bytes(address, GPIO, 0b00000000))


If it works, next steps will be to make the program take arguments of (motor direction, motor run time)
After that it would be good to control the speed (motor direction, motor speed %, motor run time)
I'm thinking of just using a very rough PWM to control the motor speed by specifying a percentage to modify the duty cycle with time.sleep but as I will also be using time.sleep to specify the motor run time I think that means I will need concurrent threads. Anyway, that's for later, one step at a time.

At some point I will add opto-encoders to calculate distance travelled (an LED shining through regular holes in a gear in the BigTrak to a receiving photo-diode) which will make the motor run time parameter redundant anyway.
User avatar
Posts: 852
Joined: Mon Mar 05, 2012 3:26 pm
Location: London
by BillyBag2 » Sat Oct 20, 2012 11:31 pm
I have used PCF8574s on another project, not a RPi. I think they are 3.3 compatible. They are easy to use.
Pi N Chips - pinchips.blogspot.co.uk
User avatar
Posts: 32
Joined: Fri Jun 15, 2012 7:11 am
by morphy_richards » Sun Oct 21, 2012 7:46 am
I think this (at the very bottom of this post) would be the equivalent smbus version ...
The
Code: Select all
if __name__ == "__main__": main()
line is interesting. It allows you to make code that either runs on it's own or that you can subsequently use in another program, so essentially you can use the BigTrakController.py as a software component in SkutterMasterMind.py.

This is still just for testing. I don't even know if it will work on my circuit or if this code has bugs yet. Working this out on my laptop at the min.

Instead of using
Code: Select all
bus.write_byte_data(address, GPIO, 0b00111100)
- this for example turns the 2 motors in opposite directions I wonder if it would be possible to define constants for each motor and the direction and then use simple arithmetic to combine the commands, or if it would be necessary to use bitwise.


Code: Select all
#! /usr/bin/python

import smbus import time


address = 0x20 #
#define all the registers
IODIR=0x00       # 0 = out 1 = in
IPOL=0x01       #input/output polarity on gpio. bit 7 -> bit 0. If bit set, gpio value will reflect the inverted value
GPINTEN=0x02    #interrupt on change. bit 7 -> bit0 If bit is set then will generate an interrupt if that pin changes
DEFVAL=0x03    #default value to compare against GPINTEN. bit 7 -> bit0. If bit is set, opposite value on corresponding pin will generate interrupt
INTCON=0x04    #interrupt control register. If bit is set then corresponding IO pin will be compared against that set in DEFVAL register
IOCON=0x05       #setup / config. bit 5 = sequential operation. bit 4 slew rate. bit 3 not used. bit 2 open drain. bit 1 interrupt polarity sets polarity of INT pin. ONly functions if open drain bit is clear
GPPU=0x06       #gppu pull up resistor, bit 7 -> bit 0. if bit set, if bit set and pin is input then this will pull up the pin with 100k resistor
INTF=0x07       #interrupt flag register. bit7 -> bit 0. bit set means the associated pin will generate an interrupt. A set bit in this register tells us which pin caused the interrupt. READ ONLY.
INTCAP=0x08    #interrupt capture. Captures GPIO value at time of interrupt. bit 7 -> bit 0. Remains unchanged until interrupt cleared via a read of INTCAP or GPIO
GPIO=0x09       #the GPIO. bit7 -> bit 0
OLAT=0x0A       #output latches

bus = smbus.SMBus(0)
#set IODIR as OUTPUT
bus.write_byte_data(address, IODIR, 0b00000000)
#reset all the other registers
for reg in [IPOL, GPINTEN, DEFVAL, INTCON, IOCON, GPU, INTF, INTCAP, GPIO, OLAT]:
   bus.write_byte_data(address, reg, 0b00000000)
#Set the GPIO's to turn on / off transistors in H bridge. See associated circuit diagram.
#GPIO 0 - 1 = motor 1 fwd.
#GPIO 1 - 1 = motor 1 fwd.
#GPIO 2 - 1 = motor 1 rev.
#GPIO 3 - 1 = motor 1 rev.
#GPIO 4 - 1 = motor 2 fwd
#GPIO 5 - 1 = motor 2 fwd
#GPIO 6 - 1 = motor 2 rev
#GPIO 7 - 1 = motor 2 rev

#-----------------IMPORTANT DANGER IMPORTANT-----------------
#IF GPIO 0, 1 is "1" THEN GPIO 2, 3 must be "0" ELSE transistor short circuit and "Bang!"
#IF GPIO 4, 5 is "1" THEN GPIO 6, 7 must be "0" ELSE transistor short circuit and "Bang!"
#------------------------------------------------------------
#set all GPIO off
bus.write_byte_data(address, GPIO, 0b00000000))
#test motor 1 and motor 2 FWD for  3 secs
bus.write_byte_data(address, GPIO, 0b00000011))
time.sleep(3)
#set all GPIO off
bus.write_byte_data(address, GPIO, 0b00000000))
time.sleep(1)
#test motor 1 and motor 2 REV for 3 secs
bus.write_byte_data(address, GPIO, 0b00001100)
time.sleep(3)
#set all GPIO off
bus.write_byte_data(address, GPIO, 0b00000000)
time.sleep(1)
#test hard right turn for 1 sec
bus.write_byte_data(address, GPIO, 0b11000011)
time.sleep(1)
#test hard left turn for 1 sec
bus.write_byte_data(address, GPIO, 0b00111100)
time.sleep(1)
#set all GPIO off
bus.write_byte_data(address, GPIO, 0b00000000)
      

if __name__ == "__main__": main()

User avatar
Posts: 852
Joined: Mon Mar 05, 2012 3:26 pm
Location: London
by morphy_richards » Mon Oct 22, 2012 2:30 pm
It works!
Using the smbus code posted above ....

Video here shows the Big Trak running around the floor. Actually took a few takes as it kept crashing into stuff!

Image
http://youtu.be/2stJB8pPyCQ

At the minute this is tethered to the Raspberry Pi on the desktop. Now I need to set it free! (Also need to transfer the circuit to stripboard)

For that I need to make a few regulators to allow me to use my 7.2 volt RC car battery to power the motors and the Pi. I also need to add a wifi dongle and make my own ad-hoc wireless network (I cant use the school one for this) to allow me to monitor and control stuff while I work on making it fully self aware ;)

I'm thinking of using two of theseLM2576T switching regulators. It looks like a really nice and simple circuit and these regulators are really cheap on ebay.

They will both be connected to the 7.2v battery in parallell, one will supply the Pi with a clean power supply, the other will provide 5v to the Big Trak motors. Keeping them separate like this should prevent any nastiness infecting the Pi's power.
User avatar
Posts: 852
Joined: Mon Mar 05, 2012 3:26 pm
Location: London
by morphy_richards » Fri Oct 26, 2012 3:37 pm
DIY motor driver - Quick circuit sketch and explanation...

Image

MCP23008 gpio expander chip is being used as it can deliver more power than the gpio on the Pi. Also this circuit will tie up a lot of gpio's.

The mcp is being powered from 5v as this supply can deliver more current than the Pi's 3v3 supply.

Both Pi and mcp use i2c so this is used to let one talk to the other, however the logic levels are different. (5v on mcp / 3v3 v on pi) 5v will damage the pi over time so the logic levels need converting.

This is done really simply. The 2 MOSFETs in the diagram on the sda and scl do this. You need to supply 5v on one side via a pull up resistor of some nominal value like 2k2. On the 3v3 / pi side the pull up resistors are built in.

Vss and vdd are the mcp power supply.
A0, A1, A2 are user definable address pins. I have tied these to ground to set this as 0,0,0.

The reset pin on mcp is tied to 5v. That means for normal circumstances we can just ignore it.

On the other side are the gpios. Starting at gpio0 at the bottom right and going up to gpio7 (8 in total).

The first 4 control the first H bridge which is a really nice and simple 2 way motor controller.

Each gpio is connected to a transistor "base" via a current limiting resistor. Turning on a gpio turns on the transistor, making it act like a switch... So...
Turning on gpio0 turns on the switch to motor +. Also turning on gpio1 turns on the switch from motor - to ground which completes the circuit and makes the motor run forwards.

Turning on gpio2 and gpio3 does the same thing except the power goes from power supply + to motor - which makes the motor run in reverse.

Across each transistor output is a reverse biased diode. This is needed because a motor can also be a generator. When you turn off the power and the motor keeps spinning for a bit it generates a reverse current which could damage the circuit. The diodes allow this to safely escape.

There are actually 2 H bridges to control 2 motors. The second is controlled by the other 4 gpios. I haven't drawn those in to keep the sketch simple.
User avatar
Posts: 852
Joined: Mon Mar 05, 2012 3:26 pm
Location: London
by pygmy_giant » Mon Nov 05, 2012 1:27 am
Posts: 1566
Joined: Sun Mar 04, 2012 12:49 am