View previous topic :: View next topic |
Author |
Message |
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Wed Dec 17, 2008 4:49 am Post subject: 8 chan servo controller for a tiny2313 - 1 uSec resolution |
|
|
Hello!
I wanted to control a few servos via rs232 serial so I wrote the following code. Let me know if anyone finds it useful.
I'm also planning on writing a 16 channel controller for the 28pin mega parts (8,16,168) with lots more features. Let me know what kinds of things you would like it do and I will see if I can add them.
Regards,
-Glen
NOTE! Code updated 2009-03-01
Code: | '==============================================================================
' Project: Servo Controller v2.3
' Created: 2008-11-16
' Edited: 2009-03-01
' By: Glen Aidukas [BahBots.com]
' Compiler: Bascom-avr V1.11.9.3 NOTE: Make sure Optimize code is turned on!
'
' Description:
' Servo controller for controlling up to 8 channels on an Atmel tiny2313.
' It has 1 uSecond resolution and can save the profile to eram.
' Note: internal fuse bits set for 8MHz RC clock source.
'
' Usage syntax: (CR= Carriage Return [Enter key])
' $Icx+CR I= ID, c= channel number 1-8, x= uSeconds (250-2750)
' $Is+CR Show current settings
' $Iw+CR Save current settings to eram
' $Ir+CR Load profile from eram
' $I@xCR Assign the new controller ID (x).
' Examples:
' $031500 Set chan# 3 to 1500 uSeconds (servo center) for controller "0".
' $0@a Change controller ID to "a" for controller currently set to "0".
' $as Show current channel settings for controller "a".
' $aw Write corrent channel settings to eram to be used at reboot.
' $ar Read from eram the last saved settings.
' Responses:
' "$" CMD completed ok!
' "!" Error with command or value!
'
' Release Notes:
' - This version has been updated to handle continuous commands comming
' in at up to 57600 baud! Baud can only be changed at compile time!!
' - Several other small cleanups.
'==============================================================================
'=====[ Compiler Directives ]==================================================
$crystal= 8000000 ' set via fuse bits to internal RC at 8megs
$regfile= "ATtiny2313.DAT"
$baud= 9600 ' can be up to 57600
$hwstack= 32
$swstack= 14
$framesize= 24
'------------------------------------------------------------------------------
'=====[ Define vars ]==========================================================
dim channel (8) as word ' array of channel's pulse value
dim ch as byte ' used for character input & temp var
dim x as byte ' used for row indexing
dim i as word ' used for temp value
dim Command_Flag as byte ' used for when there is a CR sent
dim 20mSec_Flag as bit ' used to signal when to refresh
' the folling are the EERAM saved profile locations.
dim echannel (8) as eram word ' the user set target value for the first row
dim eSetFlag as eram byte ' if set to '$' then eram is ok
dim myID as eram byte ' Controller ID
'------------------------------------------------------------------------------
'=====[ Set IO Pins ]==========================================================
''' set data direction registors for io pins
DDRB = &B11111111
'------------------------------------------------------------------------------
'=====[ Setup Timer0 for Tick isr ]============================================
''' Timer0 is used to create a flag at every 20 uSeconds
Config Timer0 = Timer , Prescale = 1024 , Compare A = Disconnect , Clear Timer = 1
' crystal / Prescale / CountWanted = reload value
' 8000000 / 1024 / 50 = 156 (50.08 Hz)
OCR0A = 156
On OC0A 20uSec_isr
Enable Timer0
Enable OC0A
'------------------------------------------------------------------------------
'=====[ Setup Timer1 ]=========================================================
''' Timer1 is used for the uSecond counter for each pulse to the servos
config Timer1 = Timer , Prescale = 8 ' setup timer1 for 1 uSecond steps
stop timer1
on Compare1a Compare1a_isr nosave
enable Compare1a
'------------------------------------------------------------------------------
'=====[ Misc. config ]=========================================================
Config Serialin = Buffered , Size = 26 , Bytematch = 13 ' Serial Buffering
for x = 1 to 8 ' load starting values for each channel
if eSetFlag = "$" then ' check for valid eram values
channel (x ) = echannel (x ) ' if flag is good then load from eram
else
channel (x ) = 1500 ' set to middle position for all servos
end if
next x
if myID = 255 then myID = 48 ' assign '0' on first use.
ch = myID
enable interrupts
' show Banner & Controller ID
print "{010}{013}BahBots.com - Tiny Servo Controller V2.3"
print "ID: " ; chr(ch )
'------------------------------------------------------------------------------
' =====[ Main Loop ]===========================================================
do
for x = 0 to 7 ' loop through each channel
stop timer1
timer1 = 0 ' reset timer1 count
Compare1a = channel (x + 1) ' set the count for channel x
start timer1 '
set PortB.x ' and set the bit for the given channel
while PortB <> 0 ' wait for the isr to clear the bit
if Command_Flag > 0 then gosub ProcessCMD ' Process new commands
Wend
next
while 20mSec_Flag = 0 ' wait till its time for the next round
if Command_Flag > 0 then gosub ProcessCMD ' process any new commands
Wend
20mSec_Flag = 0 ' reset the timer0 flag
loop
' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'=====[ Compare1a_isr ISR routine ]============================================
Compare1a_isr :
' clear all the bits. This is faster then decoding the individual bit needed.
$asm
!CBI PortB, 0
!CBI PortB, 1
!CBI PortB, 2
!CBI PortB, 3
!CBI PortB, 4
!CBI PortB, 5
!CBI PortB, 6
!CBI PortB, 7
$end Asm
Return
'------------------------------------------------------------------------------
'=====[ 20uSec_isr ISR routine ]===============================================
20uSec_isr :
20mSec_Flag = 1 ' Triger every 20 uSecs. (50Hz)
Return
'------------------------------------------------------------------------------
'=====[ Charage Return Detected ]==============================================
Serial0charmatch :
incr Command_Flag ' increment for each new command.
return
'------------------------------------------------------------------------------
'=====[ Process Serial Commands ]==============================================
ProcessCMD :
decr Command_Flag ' decrement for each command processed.
do ' look for start char "$" then continue.
ch = inkey() ' if not found in buffer then return.
if ch = 0 then return ' this will clear any bad chars.
loop until ch = "$" ' once we see the '$' then continue.
ch = inkey() ' first chr after $ is the controller ID
if ch <> myID then return ' check for proper ID
ch = inkey() ' now get the command or chan # (1-
SELECT CASE ch ' decode command
case 49 to 56: ' Channels# 1 to 8
input I Noecho ' get the uSecs for the servo
if i < 250 or i > 2750 then ' check bounds
print "!bad value" ' Bad value!
else
channel (ch - 48) = i ' good value
print "$"
end if
case "s": ' show the list of channel values.
for ch = 1 to 8
print "CH#" ; ch ; ":" ; channel (ch )
next
case "w": ' Write settings to eram
for ch = 1 to 8
echannel (ch ) = channel (ch )
next
eSetFlag = "$" ' set the eSetFlag to show eram is good
print "$W" ' tell the world its saved!
case "r": ' Restore settings from eram
if eSetFlag = "$" then ' only if the eSetFlag is set properly
for ch = 1 to 8
channel (ch ) = echannel (ch )
next
print "$R" ' show that the settings are restored
else
print "!" ' show error, the flag was not set...
end if
case "@": ' assign a new ID to this controller!
ch = inkey()
myID = ch
print "$New ID: " ; chr(ch )
case else:
Print "!CmdErr" ' Bad command received...
END SELECT
Return
'------------------------------------------------------------------------------ |
Last edited by glena on Wed Jan 19, 2011 9:35 pm; edited 5 times in total |
|
Back to top |
|
|
PaulC
Joined: 09 Jan 2008 Posts: 122 Location: Ireland
|
Posted: Wed Dec 17, 2008 10:51 am Post subject: thankyou for sharing |
|
|
looks quite useful will use this
great work
thank you _________________ Bascom Avr 2.0.7.3
Works for me
|
|
Back to top |
|
|
Jan_Huygh
Joined: 24 Nov 2005 Posts: 26 Location: VILVOORDE
|
Posted: Mon Dec 22, 2008 7:33 pm Post subject: |
|
|
First of all ... thank you for submitting your code. I have the intention to write a servo controller and your code will sure help to come to results faster.
I have the intention to use the TWI for the communication with a main processor. (another Atmel)
If I undesrtand your code correctly you generate the servo pulses one after the other (just like an RC receiver would do I agree).
BUT in a worst case situation all your servos might need a 2ms pulse. In that siyuation your code will still work with 8 servos. It will work with up to 10 servos...but you get in to trouble when you accept loger than 2ms pulses and/or when you have to handle more than 10 servos.
What I think would work with a high count of sevos (>100 ?) would be:
Start the pulse for all servos at the same time.
Sort the servos according to the moment the pulse needs to stop. (you have about 1 ms to do so since the minimum puls lenght is about 1 ms)
Keep track of the elapsed time.
Switch the pulse for the correct servo off when time has come to do so.
Doing so you will spend 2ms on all the servo pulses (even if you have many servos) and have 18ms left to do something else.
...sorry for wasting your time if I misunderstood your code
Jan |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Mon Dec 22, 2008 9:22 pm Post subject: |
|
|
Jan_Huygh,
I wrote this code to allow up to 2750 uSecs on all servos. If all add up to more then 50 mSecs it will work fine but would result in longer then 50 mSecond updates. This should not be an issue for most servos. Under most situation it will stay at 50 mSec updates.
As for adding more servos, I had thought of doing a sort routine but found that with the cpu running at 8MHz it would mean that there is only 8 cycles per 1uSec. If there are two or more servos that have the same setting then there is not enough cpu cycles to do the resets in time and would cause some jitter.
My main concern with the servo controller was to make sure that the timing was as exact as possible and all my testing showed that it was very clean with the code the way it is. I ran into this issue while working on a 16 channel version for the mega8.
It might be possible to do what you were saying if the cpu was running at 20MHz and code was in assembler but I'm not good with assembler so maybe someone else could look into maybe doing that.
It might be possible to add a few more pins (channels) but you might run into flash space issues on the tiny2313.
I hope that what is here is useful to you and your projects!
-Glen |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Mon Dec 22, 2008 9:48 pm Post subject: |
|
|
I was just thinking it would be easy to make this controller addressable.
I could do this by adding one more char to the command line. In this situation you would just use the Tx line from the controlling micro and connect it to the Rx line of all the receivers. You would not be able to see the responses but that's probably not an issue for most needs.
You would first need to set the ID of each controller by connecting it alone and issuing a special command to assign its address. It would then be saved in ERam and remember its address after power loss.
I think this would be better then TWI (I2c) as Serial has much lower over head then TWI.
Let me know if there is any interest in this. If there is I will add it.
-Glen |
|
Back to top |
|
|
Jan_Huygh
Joined: 24 Nov 2005 Posts: 26 Location: VILVOORDE
|
Posted: Tue Dec 23, 2008 9:58 am Post subject: |
|
|
Glen,
Thank you for your response. As stated before, yes your code is very helpful to me.
You sure have more experience in this matter than I have. For sure your experience with BASCOM is better than mine. I welcome your posting and your pursue to make as many people as possible take advantage of your investment.
I have just a reflection on your commitment to stick to 1µs precision.
I don’t think you need that precision.
A servo takes a pulse between 1 and 2 ms. That delivers with most servos a turn of 90° of the servo arm. With a 100µs precision that would still deliver you a below 1° precision. I believe that is good enough for any practical application but indeed the servo might show some jitter. I expect that a 10µs precision will deliver you jitter free operation. Even with the 100µs precision I would expect that since the error will be repeated in every pulse to that servo …you will not see jitter…so the only downside I see is that you will have “only” about 1° precision. With 100µs you will not need assembler, BASCOM will do.
About your idea to connect multiple AVRs to one master using the serial communication (just connect the send line, do not use receive and use a chip address), that sure is a solution for most potential users who need more than 8 or 10 servos. However I intend to build modules to be used with an AVR based mother board. Some modules will need bi-directional communication… reason why I believe I will stick to my intention to use the TWI interface.
Jan |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Wed Dec 24, 2008 6:50 pm Post subject: |
|
|
Jan_Huygh,
The main reason I wrote the servo controller was I was not happy with the resolution of the Config Servos command that comes with Bascom. It is only good for 10 uSecond resolution. If that is all you need then I would take a look at it.
See here: http://avrhelp.mcselec.com/index.html?config_servos.htm
Also, the code that I wrote uses all of the 2k program space in a tiny2313 part so there is no chance of adding TWI support on it.
If 10 uSeconds is good enough for your needs then try the internal Servo code provided in Bascom. You will probably also need to use a part with more then 2k of program space to support TWI. I also think you may need the TWI client library (I think there is a charge for this) but I have no experience with it.
Have fun and good luck with your projects!
-Glen |
|
Back to top |
|
|
teccs
Joined: 14 Oct 2006 Posts: 69
|
Posted: Sun Jan 16, 2011 2:50 am Post subject: How can this work? |
|
|
How can this line in your code work when ch is dimensioned as a byte and not a string?
loop until ch = "$" |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Mon Jan 17, 2011 5:12 pm Post subject: |
|
|
teccs,
In this case the compiler just interprets the "$" as a single character (or byte). I write it this way so that its more human readable then using decimal 36 or 24 in hex.
-Glen _________________ http://bahbots.com |
|
Back to top |
|
|
hgrueneis
Joined: 04 Apr 2009 Posts: 902 Location: A-4786 Brunnenthal
|
Posted: Wed Jan 19, 2011 8:18 am Post subject: |
|
|
Very nice work!
Just a few remarks not meant to deminish your work.
It is 20 msec.(milliseconds) not 20 usec.(microseconds)..in your comment and subs.
In designing and programing RC controls the limit to do a number of channels with a refresh of 25 msec. is about 11 to 12 if the max. pulsewidth is supposed to be 2.1 msec. If it is 2.0 msec. no problem.
With an atmega168 I control about 20 servos with the calculation of 4 channels from the radio (different from your purpose) which are read by a separate 168 with TWI interface. All chnnels are very stable at a resolution of about 8 usec (no asm code and no use of bascom servo).
I have found that a higher resolution with most servos is not effective unless you use the very high end digital servos.
With 8 usec. you get about a 0.5 deg. resolution on a 60 deg. servo, which is enough in most RC cases.
All of the RC remotes transmit in serial (one after an other) pulse mode.
If there are more channels than 12, they have to either stretch the refresh or transmit commands and use a decoder (MCU) in the receiver.
I just do not see an application for RC control from for example a laptop with keyboard input.
It might work for stationary equipment, maybe a boat but not a plane.
Just my own thoughts
Hubert |
|
Back to top |
|
|
glena
Joined: 25 Jul 2007 Posts: 284 Location: PA near Philly
|
Posted: Wed Jan 19, 2011 9:53 pm Post subject: |
|
|
hgrueneis,
Thanks for the 20uSec_Flag issue, I changed it to 20mSec_Flag. I'm bad at catching these typos sometimes.
The main desire here for this code was to off load the controlling of servos from the main board to a small tiny2313 chip and just send it the values needed and move on to other things until I need to change the value again for a given servo.
One project I have that uses this is a RoboMagellan bot that has three servos and lots of other sensors and controls. I did not want to burden the main controller with simple servo updates. I just need to send a simple command out the serial port and the servo controller does the busy work for my. Since this was written to answer to only its ID, I use the serial transmit line from the main controller as a simple but and I also connect a serial to Graphic LCD device I made. This way, with just one port from the main controller, I can control 8 servos (or more with a second servo controller) and also update the LCD display.
See: http://mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&t=6457&highlight=serial+graphic&sid=94226f9c09849caa0212034b0f8cce29 for more info on the Serial to Graphic LCD display.
As for resolution, Yes I know that cheap servos have a deadband of anywhere between 4 and up to more then 10uSecs but doing it at 1uSec resolution was easy stable and simpler to understand the values being sent (IE: 1500uSec = 1.5mSecs).
While more then 8 servos is needed for some projects, you can easily put 2, 3 or more controllers (each with unique ID) on the same single uart line from the main controller for 8, 16 , 24 or more servos all from a simple tiny2313 chip.
Another nice thing is if some of the servos are far from the others or from the controller, then you just run Power, Ground and the Tx line from the main controller to were the servos are with one of the Tiny2313 controllers next to them.
Thanks for your feedback!
-Glen _________________ http://bahbots.com |
|
Back to top |
|
|
Kiedro
Joined: 08 Mar 2006 Posts: 81
|
Posted: Mon Mar 14, 2011 12:43 pm Post subject: |
|
|
Hi all,
a servo-controller is a nice thing if the goal is to deliberate a main processer from the load of a low level task. In Bascom, this load very often comes from a frequent use of e.g. timer based interrupts. There is substantial code overhead created by the compiler when using ISRs due to the fact that BASCOM pushes and pops all of its internal registers to the stack each time an ISR is called. This makes BASCOM user-friendly and safe but at the cost of speed of ISR handling. Kühnel's BASCOM book takes a deep look into this problem and even gives a recipe how to correct the timing to make it more precise. He recommends the NOSAVE option, but this calls for a very thorough observation of register changes and may lead to the opinion NOSAVE = not safe (enough for me). While servo-timing IS NOT really time-critical for a micro, the error by overhead may accumulate in cases where pulse trains need to be synthesized.
Just to complete the discussion: There is a very easy way to avoid timer based interrupts in servo signal generation and to arrive at rock-stable, jitter free, and perfectly precise servo signals with 2000 steps per millisec at 16 MHz (or 1000/ms at 8 MHz). Zero processor load and true background operation by hardware warranted, however, at the cost of using 16-bit timers and their output compare pins. Atmel engineers call if fast pwm (mode 14) in the ATMEGA series or Single Slope PWM in the Xmega series: See an Xmega snippet here: http://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&t=8174&highlight=natalius . The total number of servos which can be be controlled is given by the number of 16 bit timers multiplied by the number of output compare pins per timer. So 6 for ATMEGA128, 12 for ATMEGA1280/2560 and 24 for ATXMEGA128A1. This is the way it is done in the AR7212 receiver (Application note 172 and AR7212 forum).
Natalius
Here is a little snippet which shows the basics:
Code: |
'----------------------------------------------------------------------------
'-------- RC SERVO PULSE GENERATION BY FAST PWM (MODE 14)
'-------- Just a snippet for ATMEGA16 and 2 servos by Natalius Kiedro
'----------------------------------------------------------------------------
$regfile = "m16def.dat"
$crystal = 8000000
$baud = 19200
$hwstack = 32
$swstack = 10
$framesize = 40
''At 8 MHZ and prescale 8 we have 1000 steps per millisec
''(1) Set output compare (OCRxy) pins as output
Ddrd = &B00110000 ''PD4, PD5 as output
''(2) Set TOP (Counter at 20ms) into ICRx (Input Capture Register)
Icr1h = High(20000)
Icr1l = Low(20000)
''(3) Set OCRxA, OCRxB for Servo1, Servo2
Ocr1ah = High(1500) 'center position
Ocr1al = Low(1500)
Ocr1bh = High(1500) 'center position
Ocr1bl = Low(1500)
''(4) Set FAST PWM,Mode 14, with prescale 8, and start
Tccr1a = &B10100010 'COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
Tccr1b = &B00011010 'ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
'COM1.. bits define pin behave at compare match
'WGM1.. bits define PWM mode: Table 47 in ATMEL PDF
'CS1.. bits define prescaler: Table 48
Dim Servo1 As Word
Dim Servo2 As Word
Servo1 = 1000
Do
If Servo1 = 2000 Then Servo1 = 1000 'slow move and fast jump
Incr Servo1
Servo2 = Servo1
Ocr1ah = High(servo1)
Ocr1al = Low(servo1)
Ocr1bh = High(servo2)
Ocr1bl = Low(servo2)
Waitms 21 '21sec for 1000 steps
Loop
|
|
|
Back to top |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Mon Mar 14, 2011 1:11 pm Post subject: |
|
|
Thanks for the code.
I would like to add the following :
When hardware is available, it should be used. It is better to use TX/RX for serial COM than using a SW UART. But not all micro's have a UART.
When you have free timers and the mode is supported (only new avr's) it is better to use it. There is also a CONFIG SERVO command which will generate servo signals. It does use little HW stack/registers. And you can use any pin you like. When the clock speed is high enough, you can also have a lot of servo's. Not so long ago the software servo code was rewritten.
One disadvantage of software servo is that other interrupts (that take a long time) will interrupt the signal. _________________ Mark |
|
Back to top |
|
|
luizabbadia
Joined: 11 Dec 2007 Posts: 112 Location: rio de janeiro
|
Posted: Sun Jul 10, 2011 5:27 pm Post subject: |
|
|
Thanks for your code Kiedro!
I tried it and noticed that when using this code to control a hacked servo for rotation , you can have many speed levels!
This is very nice because one can design a very useful PID controller when using this type of servo.I have one robot project
using a compass and autoheading but I had only normal speed and 75% options for the servos using normal code.I´ll adapt it
to your fast pwm mode solution to get more control over this robot.
Please see this video.
http://www.youtube.com/watch?v=hgJI6iv6vuU[
Nice!!
PS I´ve uploaded this video with "FAST PWM" tittle ,
but sincerely I´don´t know if it´s legal to post this
forum thread link (MCSELEC Forum) and Kiedro´s
name.Please tell me if it´s OK that I´ll do it.Mark? Kiedro? |
|
Back to top |
|
|
albertsm
Joined: 09 Apr 2004 Posts: 5913 Location: Holland
|
Posted: Sun Jul 10, 2011 8:53 pm Post subject: |
|
|
sure, no problem
I love to see these video's of projects. _________________ Mark |
|
Back to top |
|
|
|