Full Version : Connecting LEGO Sensors to AVR (BASIC)
avr >>ROBOTS, AUTOMOTIVE & MOTOR CONTROL >>Connecting LEGO Sensors to AVR (BASIC)


AVR_Admin- 05-08-2006
Lego construction kits are amazing toys. With a few standardized parts one can quickly build fairly rugged structures. Add a computer, motors and sensors; one can build up prototype or final robots. The LEGO Mindstorm kits, with the LEGO RCX Brick computer add the final pieces needed for building robots: motors, gears and sensors that easily snap together to rapidly build robots.

The standard LEGO brick, however, has serious limitations. No more than three sensors can be connected at a time (not strictly true, but true if you want full functionality from each sensor) and the programming language has limited access to the capabilities of the internal CPU. I am used to programming in C and assembly and find the expressive power of the native LEGO languages (Robotics Invention System and Robolab) restrictive.

Because of the popularity of the LEGO Mindstorm kits (I suspect many adults purchased them for their kids, but ended up owning them; that is my excuse!) there has been considerable activity in extending the basic LEGO system. There are web sites detailing how LEGO sensors work and how to create your own special sensors. There are alternative firmware and high level languages available that liberate the creator from the limitations of the stock LEGO RCX controller. For more sophisticated LEGO based robots, more powerful computers and home-brew sensors replace the LEGO Brick and Mindstorm sensors. This combination is popular for teaching robotics at the college level.

In short, it seems that either one uses the LEGO RCX brick and tries to get around it’s limitations through alternate software, or one dumps all the Mindstorm parts and uses custom controllers & sensors with the LEGO parts when building robots. I never saw a third option of using stock LEGO sensors with alternate computers. This didn’t seem right since the LEGO Mindstorm sensors are rugged, inexpensive, easily available, and are designed to work with LEGO parts.

Having an extended Mindstorm kit at home, with plenty of extra sensors, I became intrigued by the idea of using the stock LEGO parts with my own AVR Robot Control board (www.Barello.net/ARC); I wondered if the standard 9v LEGO sensors would work with +5v logic of a simple microcontroller. After performing some quick tests I found that LEGO sensors work fine with a +5v system. All one needs is an Analog input (ADC) and the ability to control whether an I/O port is an input or output.

This article describes interfacing LEGO Mindstorm sensors with an Atmel AVR microprocessor (www.atmel.com) using the BASCOM (www.mcselec.com) basic compiler. The techniques described should work with PIC and HC11 style microcontrollers using a variety of languages as long as they support switching an analog I/O port from an output to an input.

Link to Site: http://www.barello.net/ARC/projects/LEGO/index.htm

CODE

'
' Function declarations for LEGOLIB.BAS
'
' Include this near the top of your source file.
'
Declare Sub Initlego
Declare Sub Dolegoencoder(byval Port As Byte , State As Byte , Encoder As Integer)
Declare Function Light(byval Port As Byte) As Byte
Declare Function Switch(byval Port As Byte) As Byte

CODE

'
' LEGO Rotation Sensor test code
'
' Encoder on PA1 and switch/light sensor on PA2 and Switch on PA3
'
' The lego rotation sensor is queried roughly 122hz
'
' NOTE WELL! You need to configure BASCOM for a larger hw stack than
' the default 32 bytes.  Go to Options->Compiler->Chip and enter in at
' least 48 for the stack size. Real applications might need more.
'
$regfile = "m16def.dat"
$crystal = 8000000
$baud = 19200
$include "legolibdefine.bas"
'
' Configure AVR Timer for 122 hz interrupt rate: 8mhz / 256 / 256
' The second 256 is due to the 8 bit counter simply rolling over.
'
Config Timer0 = Timer , Prescale = 256
On Timer0 Timer0_isr                                        ' Our timer interrupt hanlder
Enable Timer0
Enable Interrupts
'
' Configure "Program" led on ARC board as an output
'
Config Pinb.4 = Output
Prog_led Alias Portb.4
'
' Define ports which we will connect the sensors.
'
Const Encoderport = 1
Const Lightport = 2
Const Switchport = 3

Call Initlego()
'
' My_encoder and Encoder state are needed to track an encoder.
' If a second encoder is being used, declare a second set of values.
'
Dim My_encoder As Integer , My_encoderstate As Byte

'
' Do loop prints out only when Encoder changes value.  Note, Encoder
' is only modified in the interrupt handler, below.  Also note the
' enabling and disabling of interrupts to protect access to "my_encoder"
'
Dim Temp As Integer , Sys_timer As Byte

Print
Print "LEGO Sensor test application"

' Uncomment "If" and "End if" if you don't want continuous output

Do
  Disable Interrupts
'   If My_encoder <> Temp Then
     Temp = My_encoder
     Enable Interrupts                                     ' Enable interrupts before printing
     Print " Encoder="; Temp;
     Print " Switch="; Switch(switchport);
     Print " Light="; Light(lightport);
     Print
'   End If
  Enable Interrupts
  Idle                                                     ' Sleep until next interrupt.
Loop
End
'
' Interrupt handler for timer.  This is called at roughly 122hz, based upon
' the timer0 setup.
'
Timer0_isr:
'
' State machine to flash LED at 1 hz.
'
   Incr Sys_timer
   If Sys_timer = 25 Then
       Prog_led = 0                                        'enable low output (ON)
   Elseif Sys_timer = 122 Then
       Prog_led = 1                                        'disable output (OFF)
       Sys_timer = 0                                       'reset timer
   End If
   '
   Call Dolegoencoder(encoderport , My_encoderstate , My_encoder)
   '
   Return
'
$include "legolib.bas"

'end program

CODE

'
' LEGO Interface library
'
' All lego sesors connected to PORTA, which is the
' ADC input port.
'
'
Declare Function Readport(port As Byte) As Integer
Declare Sub Powerport(port As Byte)

'
Dim Enc_tmp As Byte , Light_tmp As Integer
'
' Initialize the ADC for fast conversion. If accuracy is important in your application
' don't call this routine and set up the ADC per Atmel specifications.  Each
' LEGO sensor function will take a little longer.
'
Sub Initlego()
  ' Note: atmel doesn't recommend running the ADC this fast
  ' but as far as I can tell it works fine.  The results will be noiser
  ' but only by 2-4 counts.
  Config Adc = Single , Prescaler = 8
  Start Adc
End Sub
'
' Read and decode the LEGO rotation sensor.
'
' The speed at which the encoder can be read is determined
' by the call rate.  LEGO specifies the encoder as capable of
' 300 rpm (4800 transitions/min or 80/sec) so call this routine
' at least 100 times/second.
'
' Parameters: Port bit #, Previous state, Encoder value
' Returns: Nothing
' Modifies: Previous state and Encoder Value
'
Sub Dolegoencoder(byval Port As Byte , State As Byte , Encoder As Integer)
  Light_tmp = Readport(port)
  Select Case Light_tmp
  Case Is > 850 :
        Enc_tmp = 2
        If State = 1 Then Goto Inc_encoder
        If State = 3 Then Goto Dec_encoder
  Case Is > 700 :
        Enc_tmp = 1
        If State = 0 Then Goto Inc_encoder
        If State = 2 Then Goto Dec_encoder
  Case Is > 450 :
        Enc_tmp = 3
        If State = 2 Then Goto Inc_encoder
        If State = 0 Then Goto Dec_encoder
  Case Else :
        Enc_tmp = 0
        If State = 3 Then Goto Inc_encoder
        If State = 1 Then Goto Dec_encoder
  End Select
  Goto No_encoder
Inc_encoder:
  Encoder = Encoder + 2
Dec_encoder:
  Encoder = Encoder - 1
No_encoder:
  State = Enc_tmp
  Call Powerport(port)
End Sub

'
' Read a LEGO light sensor.
'
' The raw output varies from ~600 to ~900, depending.  I clip the
' output and shift it & invert so low numbers == dark and high numbers
' equal light.  If you parallel a switch on this port a switch closure
' will read as 0.  If only a switch is on this port, it will read 20 (open)
' and 0 (closed)
'
' Parameters: Port Bit Value
' Returns: Cooked Sensor Reading
' Modifies: nothing (but port i/o values)
'
Function Light(byval Port As Byte) As Byte
  Light_tmp = Readport(port)
  If Light_tmp < 200 Then
     Light = 0
  Elseif Light_tmp < 620 Then
     Light = 255
  Elseif Light_tmp > 855 Then
     Light = 20
  Else
     Light = 875 - Light_tmp
  End If
  Call Powerport(port)
End Function

'
' Read LEGO switch
'
' functionally redundant with Light(), but
' consumes less power as the outputs are not driven.
' NB - calling this will mess up logic for Light().  Use
' one or the other but not both.  This routine returns
' either a 0 or a 1 (closed, open, respectively)
'
Function Switch(byval Port As Byte) As Byte
  Light_tmp = Readport(port)
  Switch = Pina.port
End Function
'
' Restore power to a port after reading it.
'
Sub Powerport(port As Byte)
  Porta.port = 1
  Ddra.port = 1
End Sub
'
' In order to keep code size to a minimum I put all common
' functions into this routine as I could.
'
Function Readport(port As Byte) As Integer
  Ddra.port = 0
  Porta.port = 0
  Waitus 10
  Readport = Getadc(port)
End Function

'end program



Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.