That's great! I'll check it out, thanks!
Do you know if its complete? As in I can do everything with it that I can do with SX/B?
Yes, it is complete. You can compile for all sx devices. You have all the possibilities
that sxb 2.0 offers, except for large arrays on sx18/20/28 (limited to 16 bytes).
But there are libraries written (by me and others), particularly the Task library
that offers the same as sxb tasks.
At the moment I favor sxb over c4sx because I don't like the way cc1b handles
local variables. It uses staticly assigned variables (not a real stack) so you
will frequently use #pragma to set a bank for new locals (this is not automated).
Also, in the libraries I wrote you will see lots of assembly too.
One big advantage of C4SX is that it·natively supports libraries.
Edit:
C4SX does not offer all the commands that are builtin into sxb, like pulseIn, rcTime etc.
You will have to write libraries for those commands.
regards peter
Post Edited (Peter Verkaik) : 2/3/2009 11:27:00 PM GMT
1) The internal clock is just not accurate enough for reliable serial comms.
2) You can count cycles or use Guenther's SX/Sim. If you're using buffered serial it's mostly a "don't care" as it's happening in the background (unless you have a full buffer). When using straight SEROUT it takes 10xbit_time to send a byte. Bit time is 1/Baud. At 2400, for example, the bit time is 416 microseconds and a full byte takes 4.16 milliseconds.
3) True, but you'll find a balance. 20MHz seems to be good for battery-run apps (all BS2 Stamps run 20MHz or faster).
4) There's www.sxlist.com, but you'll have to do a bit of touch up to incorporate the code found there into your SX/B programs. I really don't think there's any true "plug and play" solution, though Peter Verkaik's very advanced approach to programming may come the closest.
Ugha said...
The encoders are single channel encoders. I'm going to use them to keep track of the tracks on my robot and use the information for a crude form of PID.
The two motors with encoders are for my tracks, the other two will be much more rarely used and control the robot's forearms.
If you are going to keep track of the main drive motors then I would seriously consider using a full quadrature encoder on each motor. It gives you speed and direction. That will give you much better control since the micro can get you right on target. With only one channel you only can tell if it rotates and just hope that it goes the right way. A good source for encoders are the non-optical Microsoft and Logitech mice. Also HP printers have some nice encoders. Just find a matching pair of old inkjet printers. I used them in my encoder article.
For the arms I would probably add the full encoders as well and at least one limit switch for the home position.
Ugha said...
I have a pair of encoders on each arm of the robot that I will be monitoring with a BS2, two IR transmitters/receivers for forward ranging and crude motion tracking and I'll also end up with two IR transmitters/receivers (Either a slot interrupt or maybe a homemade one using the IR pair that comes in the boebot kit) for the encoders mentioned above. I'm also toying with the idea of using a super-bright LED and CDS cell instead of IR encoders for the tracks (which will, of course, require RCTIME to monitor the resistance)
You'll want to watch the resolution of the encoders to ensure it doesn't exceed the polling interval you use on the stamp. Otherwise you will lose count since some may be missed. That was the whole point of the July article on encoder scaling. An SX28 sits in the middle to watch the encoders and adjusts the count so nothing is lost.
Ugha said...
The PWM control is duty cycle for the motors. I have the four motors connected to two SN754410 H-bridges and I intend to PWM the enable pins on the two chips to control the speed of the motors.
Using the SX chips you have some options. You can simulate PWm within the ISR routine. This would be fine for the SX28 or SX48 chips. When using the SX48 it has a pair of 16-bit timers that can generate the PWM signals so that the ISR can do even more things without worrying about PWM too. Any other I/O pins can be used for direction. I was using an L298 which I made to use a SN754410 footprint so I've already written SX48 code that can look at encoders and leverage the hardware to generate PWM. It hasn't been converted to SX/B 2.0 yet but it could be.
Ugha said...
At this time I'm planning on connecting two BS2s with the SX. One BS2 will handle all the I/O that I can't place on the SX, the other will be the "brain" and will contain the scripts and limited AI actions for the robot.
So I'll need the SX to buffer data from and to two sources.
I'm not quite sure what other sensors I may end up adding. A PIR is possible but I haven't decided for sure what I'll end up doing.
The IR devices and sensors are pretty dumb, excluding the Ping of course. (which does require some math on behalf of the SX).
I've already a couple of SX28s and I'd like to try to use them. Do you think my application is too intensive for anything but a SX48?
The main reasons for using the SX48 over the SX28 are:
- The SX48 has more Program space/RAM (only important if you are running out on the SX28)
- The SX48 has more I/O (useful if you are running low on pins)
- The SX48 has two 16-bit timers vs a single 8-bit timer.
I use both!
Ugha said...
Unfortunately I've never subscribed to Servo although I may eventually. Your applications sound great, I'd love to check them out. If I do sign up for Servo, I'll see what the back issues cost.
You don't have to subscribe to get their back issues. You can either buy the single issue or a CD by year with electronic versions. Just checkout their website.
Ugha said...
Some notes, I don't mind if there are some brief sub-second delays between serial transmission. In fact, besides the PWM speed control and syncing with the encoders to keep the path straight, nothing requires exact timing. This is just for fun, not a professional job.
I am willing to offload some more work from the SX to one of the two BS2s if needed, what time each job (PWM, serial buffer, ect) takes, which one is better suited to which processor, is something I have no clue about and one of the things I need help with.
From what you describe it may be easier to spread out the functions across a couple processors. I tend to use the SX chips as intelligent peripherals or for controlling the whole show on small projects.
It's good to think about the whole picture and how you want it to fit together but you may want to start prototyping each section, get that working, and then see how you want to combine them.
I don't understand why I'd need speed and direction.
If I'm signalling my H-bridge to go forward, why do I need to know that its going forward? I can imagine if it were coasting or something of the like, but I don't understand it on a smallscale
robot on which I will know the direction I command it to go.
These guys know far more than I ever will. HOWEVER, whenever Ugly Buster's motor controller's received a command to instruct a motor to turn in this direction or that direction, it ALWAYS did. Having a quadrature encoder report that fact would have been a huge waste of time.
A quadrature encoder would be just the ticket to DISCOVER which way a motor was turning. But, as far as I have ever been able to figure out, in the machines I have built, anyway, direction feedback is totally useless in the drive mechanism.
All of my gear motors have quadrature encoders. I only use a single channel. A single channel provides data that may be accumulated thereby yielding position information assuming no wheel slippage. An appropriate motor controller, operating under nominal circumstances, sets the speed.
You should get both because without it you don't really KNOW which way it it moving. You just see pulses and ASSUME it may happen to be going the direction you want/expect.
The other reason is accuracy. Even if you are going to control the speed how are you going to determine how far you've gone? By factoring in the direction you can get a much better idea on how far each wheel turned. Besides it seems to make all the calculations easier.
If you are just putting a low resolution encoder disk directly on a slow moving wheel then you can probably get by with just using a single channel for regulating speed.
There should be a quadrature example in the SX/B help file which can give you an idea how they are to work with.
In regard to using just a single channel of a quadrature setup, this can easily pick up extra pulses on the edge transitions. Just park a unit at the critical switch point and watch the counts accumulate due to noise or vibration. To ensure accuracy on the counts, you must take care of that in software or schmidt triggers, and this my be easy or more difficult, depending on speed of the pulses. Alternately this can be more easily and totally reliably handled in quadrature. That's why those encoders were invented. And inherently they give direction to boot.
If you don't care about inaccuracy, then a single channel will do. Only you can decide what is good enough.
You have certainly given me something to contemplate and, just as certainly, I appreciate it. It is, perhaps, that my machines are designed to operate indoors and, hence, ARE slow moving. Therefore, I "get" away with it.
I still don't understand how one might instruct an HB-25 to move in one direction and it MIGHT move in the opposite, though. Is it because of false inputs on floating pins which may be caused by real inputs on adjacent pins?
That's not what PJV means -- especially at "slow" speeds, the transition from 1->0 or 0->1 on an encoder will "jitter" -- I am having this exact issue with a single (rather than quadrature) encoder on a big 'bot. The quadrature encoding means that *always* when one encoder is crossing an edge, the other is in the middle of a stripe, so even if the edge is bouncing "back and forth" the "click" count is maintained -- basically the count will jiggle up and down during transitions, and remain accurate. With a single encoder, you accumulate counts without knowing direction (i.e. that wheel is jittering at the edge). Regardless, if it works, it's good!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ When the going gets weird, the weird turn pro. -- HST
Bill -- Whether or not you get better resolution depends on how you've been treating the single encoder click counts. Regardless, you will certainly get more stable and accurate counts. If you are taking full advantage of *every* edge with single encoder, that's where you get the errors (when the wheel is physically jittering there is no way to know if the edge is moving past or moving back and forth).
The solution, at trade off of halving your resolution, is that you only count the "click" you get two white-to-black edge changes in a row (or the opposite). A white to black edge change followed by a black to white edge change would be jitter (unless it is followed by a another "perfect" black to white edge change, in which case it's a direction change -- and as you point out, on a heavy 'bot you would theoretically "know" that you changed direction because you told your motors to do it. But it's still not very stable and kinda "hacky".
In any case, that was my thinking when I was lazy and didn't build quadrature encoders. Now I wish I had - the firmware of counting quadrature is much simpler, actually, and in the long run I'd have probably saved a lot of time and hassle.
To come full circle, there is already a quadrature encoder example in the SX/B documentation (help file). It's a perfect project to get going with an SX -- making a simple peripheral that counts your encoders, maybe does some light pre-processing on them (i.e. "pre-chew" numbers for direction, distance, total distance, etc, so your Stamp(s) don't have to do the work) and deliver it ready-to-go to your host. I do recommend serial tx/rx (SERIN/SEROUT) for communication. It's (relatively) easy and there are a wealth of examples for both the Stamp and the SX. This way your host can send a string using SEROUT like "!GENC" (get encoders) and expect, say 9 bytes back with current distance and speed (as words) for each motor, plus a byte of bit "flags", motor moving/not-moving, motor direction, for each motor.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ When the going gets weird, the weird turn pro. -- HST
Thank you for the detailed reply. I will definitely check out the SX/B help file. For some reason, when I wrote the program that blinked the LED, I did not get very deeply into the help file. [noparse]:)[/noparse]
Actually quadrature operation gives double the resolution of a single channel.
At any instant, with quadrature there is one of 4 possible states.... lo/lo· hi/lo· lo/hi· hi/hi. And the encoder moves through all four of them in one physical cog, and each of them can be detected by the micro. The "forward" sequence is· lo/lo· to· lo/hi· to· hi/hi· to· hi/lo· to lo/lo· and the "reverse" sequence is (not surprisingly)· lo/lo· to· hi/lo· to· hi/hi· to· lo/hi· to· lo/lo.
Four phase states per cog. And the "direction" is determined at any point by comparing the current state to the previous state.
Attached is a state machine implementation of a quadrature encoder for you to peruse. Assemble it and single step through it with the debugger or SXSim to see how easily it works.
The state lookup table is made up of all the possibilities of the previous state combined with the current state.
Assume RA.1 and RA.0 are inputs and connected to the two quadrature channels
Assume varable Prev holds the state of the previous sample.
Loop rl Prev
rl Prev ;make room for the new sample coming in
and Prev,#%0000_1100 ;get rid of any carry-ins and older samples
Sample mov w,RA ;get new sample to w
and w,#%0000_0011 ;disregard all except the two state bits
or Prev,w ;merge new sample in with previous sample
mov w,Prev ;get old/new state pairs
call Lookup ;lookup same/forward/reverse in Prev/New state table
add Position,w ;update position by one... positive or negative
(delay here to set he speed you want to sample at)
jmp Loop ;get next sample
Lookup add pc,w ;jump into table and return with +1 for forward, -1 for reverse, and 0 for no change
P00N00 retw 0 ;no change
P00N01 retw 1 ;forward
P00N10 retw -1 ;reverse
P00N11 retw 0 ;impossible
P01N00 retw -1 ;reverse
P01N01 retw 0 ;no change
P01N10 retw 0 ;impossible
P01N11 retw 1 ;forward
P10N00 retw 1 ;forward
P10N01 retw 0 ;impossible
P10N10 retw 0 ;no change
P10N11 retw -1 ;reverse
P11N00 retw 0 ;impossible
P11N01 retw -1 ;reverse
P11N10 retw 1 ;forward
P11N11 retw 0 ;no change
It compiles fine and I think I've got the logic right, but at this time I haven't tested it (I'd have to wait til tomorrow or the next day for various reasons).
I tried to figure out SXSim but its all greek to me.
Consider it a rough draft or outline. I have attempted no speed management and all issues of speed (Duration on PWMs, serial timeouts, ect) are random placeholders
because I've got to gather a great deal more information (How fast can a BS2 go from reading an input to receiving/sending serial, ect... and how long it takes an SX
to do various tasks).
I've attempted to create my own crude task engine to split up the various jobs the SX will have to do.
I may even end up rewriting it without using the ACK1 and ACK2 lines, just a single serial line to all three chips.
What I'd like is suggestions on how to include interrupts to prevent the SX from having to wait on the BS2s all the time, as well as advice on better methods to
accomplish this without use of assembly.
I understand I'll eventually have to learn assembly, but for now I'd like to stick with SX/B... even if it is slow or takes a lot of work-arounds.
I posted a framework earlier that will handle the serial IO and motor PWMs for you, all you have to do is add in your foreground logic. When you use inline SERIN and SEROUT you're consuming a lot of time and this will create problems for your application, especially since you want to have speed control over motors. On that... the PWM command is great from charging RC circuits (analog output) and dimming LEDs, but not so great with motors; the reason is that it only run while the instruction is active so your program will just be sending PWM bursts to the motors; probably not what you want.
I've attached an encoder demo that is from an old Parallax AppNote for the PIC -- I just coded it to my particular style. Since you want two encoders I wrote the program with two and have tested them using a parallel LCD. Works just hunky-dorey.
Notes:
* You can use INC x instead of x = x + 1 (same for DEC)
* When multiplying or dividing by powers of two (2, 4, 8, 16, ...) shift operators are more efficient
-- use x = x >> 1 instead of x = x / 2
* TASK is a keyword in SX/B 2.0 so you shouldn't use this as a variable name
* Even though the compiler is forgiving you should enclose subroutines in SUB/ENDSUB blocks and functions in FUNC/ENDFUNC blocks
JonnyMac:
Thanks for the input... the reason why I didn't use anything from your previous code samples is because I wanted to learn the actual SX/B language somewhat before I did the whole
copy and paste thing.
I will, of course, examine your samples closely and will most likely use elements from all of them.
I also appreciate the tips. I wasn't aware of SUB/ENDSUB and the like beforehand (I checked, the helpfile doesn't mention them).
Question... do you think I'm trying to do too much with the SX at my beginner stage? Should I offload the PWM of the four motors to one of the stamps?
Finally, I hope your not getting annoyed with me...
Most people would be quite happy just using your code and moving on to something else. I'm a little different, I want to UNDERSTAND every aspect of my project.
Including the code you've provided. So I guess you could say I'm doing it the hard way.
I guess what I'm saying is, I'll just have to crack down and learn a little assembly... if you and the others don't mind me annoying you with questions [noparse]:)[/noparse]
I can't seem to get one of the example programs you've provided to compile.
First, the Ugha_Robot.SXB you made for me has 27 errors... To start with is:
speed VAR pwmCtrl(0) ' 0 (stop) to 255 (full)
Is giving the error INVALID PARAMETER "("
Each line that has pwmCtrl(0) in it is also producing the same error. There are several other errors after it but I'm assuming it all has to do with the same thing.
At first I assumed my compiler was outdated, so I upgraded from v1.50.1 to v1.51.03 with no difference in the errors.
I honestly don't see what's wrong with that or the surrounding lines... therefore the problem must be something on my end.
Any idea what it might be?
Also related to the same area... I'm confused.
acc1 VAR pwmCtrl(9)
This aliases acc1 as the ninth element of pwmCtrl if I understand it correctly... but you only have pwmCtrl defined with 8 elements.
Am I missing something?
"Too much" is in the eye of the beholder -- you'll decide that. With the programming experience you have there's no reason you can build your project, just do it a step at a time. You may have noticed that my code samples are somewhat modular. They're not exactly plug-and-play but they're close enough that I can integrate a module into a new program, do the necessary tweaks for the project at hand, then move forward. No matter how many samples you get from me or others your involvement will be required.
Like you, I used code from others but spend time learning it, understanding it, then making it my own. The PWM code, for example. Guenther's original version presets the output then turns it right back off if it is not supposed to be on. I decided to add a few cycles to the routine to give absolutely clean output. Of course, I could only do this after understanding how the original version worked.
If you're in the gift-giving mood give yourself a Parallax Professional Development board and Guenther's book -- you'll be repaid in spades with the hours of fun you'll have and the ability to quickly develop a new project.
Whoops, there is an error; but I got away with something. In the original program I declared pwmCntrl as eight bytes but then defined 12 elements. Here's the rub: in the SX28 SX/B puts arrays of less than 16 elements into one contiguous bank, so the rxSerial array (defined after pwmCntrl) was set to the next physical bank in memory and didn't clobber the end of my pwm aliases.
Here's the compiler (2.00.08) output -- note how the aliases are correct despite my size error:
pwmCtrl EQU $30 ;pwmCtrl VAR Byte(8) BANK
speed EQU pwmCtrl+0 ; speed VAR pwmCtrl(0)
speed0 EQU pwmCtrl+0 ; speed0 VAR pwmCtrl(0)
speed1 EQU pwmCtrl+1 ; speed1 VAR pwmCtrl(1)
speed2 EQU pwmCtrl+2 ; speed2 VAR pwmCtrl(2)
speed3 EQU pwmCtrl+3 ; speed3 VAR pwmCtrl(3)
acc0 EQU pwmCtrl+8 ; acc0 VAR pwmCtrl(8)
acc1 EQU pwmCtrl+9 ; acc1 VAR pwmCtrl(9)
acc2 EQU pwmCtrl+10 ; acc2 VAR pwmCtrl(10)
acc3 EQU pwmCtrl+11 ; acc3 VAR pwmCtrl(11)
rxSerial EQU $50 ;rxSerial VAR Byte (14) BANK <-- note in next bank
I fixed the program (with encoders folded in). Note that the speed is 50MHz. According to SX/Sim the ISR takes about 130 cycles (max) -- at 50MHz there 325 cycles between interrupts so there's plenty of time left for the foreground to do its thing.
Rip, tear, dissect, toss what doesn't work -- I offer this stuff only as help as you may choose to use it.
Just a note regarding the quadrature decoder code that Peter (PJV) posted. The function he wrote needs to be called at a rate that is higher than the fastest possible frequency the quadrature encoder will ever generate. For example, if you have a 256 pulse per revolution encoder running at 1 revolution per second, it will generate a pulse train of 256 Hz. Unless you have a very high resolution encoder, it's not hard to sample at a rate higher than the encoder will generate. However, it's also important that the function be called on a regular and consistent basis. If there is even a single gap between calls to the encoder function that is too long and you miss an encoder state change, then you'll end up missing a creating errors in the encoder count.
Other than these simple caveats, the technique Peter (PJV) showed is the best way to decode an encoder, and never worry about jitter screwing up the count. It's especially nice for mechanical encoders as they also have switch bounce which is automatically taken care of by this technique.
I'm glad you brought that up.... you are abolutely correct.
As many on the forums here know, I generally run all my code in a multi-tasking manner under a very tight interrupt 1 to 10 uSec regime. I would therefore arrange to call this routine very cosistently at the appropriate speed, and never miss a pulse.
Given those real time techniques, I'm sure the things Ugha wants to do could all be readily handled by a single SX, and simultaneously to boot. Unfortunately (or, depending how you look at it, fortunately) he would have to learn assembler.
It's all a matter of learning, and to squeeze the most out of an SX, assembler is an absolute must...... but all in due time.
Couldn't pjv's code be made into an interrupt service routine which would make itself known as often as necessary?
The use of the phrase "to call this routine very cosistently" is what confused me. It SEEMS to this newbie that you would not have to call it at all if it called you.
PJV uses a really interesting scheduling structure which means that the interrupt does one thing: it sets a marker that the interrupt has occurred; this is the base timing in the system and everything cascades from there.
I've been experimenting with this style folded into SX/B -- not there yet but having fun. For grins I wrote an SX/B program that uses a modified version of PJVs encoder routine and a simplistic version of his scheduler. Note that I'm not good enough with Assembly to know if RETW will work across page boundaries and as that could happen with the placement of the ENCODER subroutine I changed the table call to the READ+offset (I used the code generated by SX/B and tweaked it).
The attached program scans an encoder every 10us, updates eight LEDs with the position every 100us, and toggles and LED every 250ms. It's happily running on my PDB so while perhaps not a perfect implementation of PJV's scheduling strategy, it is working.
retw tables must be in the lower half of codepages.
Good place is directly after interrupt routine, or after
initial jump to interrupt routine.
See attachement. This version should work.
Peter Verkaik... you are absolutely correct. Please do understand in my small example I was not trying to be perfect and deal with all issues and caveats, just the basic concepts for others to understand.
Jonny... nice going.·However, nowadays I prefer to use a counter in the interrupt that I empty in the base of the scheduler. Reason is that running multiple fast tasks, on occasion one could (but should not) overrun the base counter, and hence loose a tick. Obviously this would cause jitter, and loss of total tick counts. With a counter in the interrupt, such cannot happen as all ticks are accounted for, albeit a bit skewed in time but·a better solution because·long term timing stays perfect.
IntValue equ -100 ;2 uSec interrupt at 5o MHz .... use -50 for 1 uSec or -250 for 5 uSec
Interrupt
bank IntCtr ;not required if you park the counter in global memory
inc IntCtr ;increment each time we have an interrupt tick
(some code) ;some VERY short time critical code (such as ADC code) is permitted here, depending on the size of your base tick
mov w,#IntValue ;reload interrupt with negative number
retiw ;return to scheduler (or app)
Scheduler1
bank IntCtr ;not required if you park the counter in global memory
Scheduler2 test IntCtr ;test for non-zero
snz ;
jmp Scheduler2 ;wait here until counter is non-zero. ie at least one iterrupt has occurred
dec IntCtr ;
bank Timers ;
decsz Timer10uSec ;
jmp Scheduler2 ;wait for 10 uSec tick
mov Timer10uSec,#5 ;reload 10 uSec timer with 5 2uSec interrupts. This value must be adusted for other interrupt values
(your 10 uSec code -if any)
decsz Timer100uSec ;
jmp Scheduler2 ;wait for 100 uSec tick
mov Timer100uSec,#10 ;reload 100 uSec timer with 10 10 uSec ticks
(your 100 uSec code -if any)
decsz Timer1mSec ;
jmp Scheduler2 ;wait for 1 mSec tick
mov Timer1mSec,#10 ;reload 1mSec timer with 10 100 uSec ticks
(your 1 mSec code -if any)
(rest of scheduler counters in the same manner)
jmp Scheduler1 ;
Please realize this snippet is not complete... counter definitions, initialization etc. are required. But it should show the intended operating concept. I have not tested it, but I believe it is correct.
Actually, I find that serial routines work·just fine·using a 20 uSec (5x oversampling at 9600) sample clock, and I can ·run multiple receivers and transmitters at the same time (something of apparent interest to some posters here). Of course those MUST be non-blocking. IE do a little bit of work in each as required, and return to the scheduler.
If enough others are interested I'd be happy to spend some time developing those and posting them here.
I'm trying to hybridize your scheduler strategy with the way I've done things in the past, that is, UARTS in the interrupt. I study your state-driven UART and found that it uses fewer cycles per call (by design, of course) so I modified it a bit and stuck it back into the ISR. So far, so good. Working on the TX side now. My intent is to use the scheduler to handle the enqueuing and enqueuing of serial bytes.
I've tested the attached program at 38.4k serial RX which is the design max.
Comments
that sxb 2.0 offers, except for large arrays on sx18/20/28 (limited to 16 bytes).
But there are libraries written (by me and others), particularly the Task library
that offers the same as sxb tasks.
At the moment I favor sxb over c4sx because I don't like the way cc1b handles
local variables. It uses staticly assigned variables (not a real stack) so you
will frequently use #pragma to set a bank for new locals (this is not automated).
Also, in the libraries I wrote you will see lots of assembly too.
One big advantage of C4SX is that it·natively supports libraries.
Edit:
C4SX does not offer all the commands that are builtin into sxb, like pulseIn, rcTime etc.
You will have to write libraries for those commands.
regards peter
Post Edited (Peter Verkaik) : 2/3/2009 11:27:00 PM GMT
2) You can count cycles or use Guenther's SX/Sim. If you're using buffered serial it's mostly a "don't care" as it's happening in the background (unless you have a full buffer). When using straight SEROUT it takes 10xbit_time to send a byte. Bit time is 1/Baud. At 2400, for example, the bit time is 416 microseconds and a full byte takes 4.16 milliseconds.
3) True, but you'll find a balance. 20MHz seems to be good for battery-run apps (all BS2 Stamps run 20MHz or faster).
4) There's www.sxlist.com, but you'll have to do a bit of touch up to incorporate the code found there into your SX/B programs. I really don't think there's any true "plug and play" solution, though Peter Verkaik's very advanced approach to programming may come the closest.
If so how are they declared and accessed?
If you are going to keep track of the main drive motors then I would seriously consider using a full quadrature encoder on each motor. It gives you speed and direction. That will give you much better control since the micro can get you right on target. With only one channel you only can tell if it rotates and just hope that it goes the right way. A good source for encoders are the non-optical Microsoft and Logitech mice. Also HP printers have some nice encoders. Just find a matching pair of old inkjet printers. I used them in my encoder article.
For the arms I would probably add the full encoders as well and at least one limit switch for the home position.
You'll want to watch the resolution of the encoders to ensure it doesn't exceed the polling interval you use on the stamp. Otherwise you will lose count since some may be missed. That was the whole point of the July article on encoder scaling. An SX28 sits in the middle to watch the encoders and adjusts the count so nothing is lost.
Using the SX chips you have some options. You can simulate PWm within the ISR routine. This would be fine for the SX28 or SX48 chips. When using the SX48 it has a pair of 16-bit timers that can generate the PWM signals so that the ISR can do even more things without worrying about PWM too. Any other I/O pins can be used for direction. I was using an L298 which I made to use a SN754410 footprint so I've already written SX48 code that can look at encoders and leverage the hardware to generate PWM. It hasn't been converted to SX/B 2.0 yet but it could be.
The main reasons for using the SX48 over the SX28 are:
- The SX48 has more Program space/RAM (only important if you are running out on the SX28)
- The SX48 has more I/O (useful if you are running low on pins)
- The SX48 has two 16-bit timers vs a single 8-bit timer.
I use both!
You don't have to subscribe to get their back issues. You can either buy the single issue or a CD by year with electronic versions. Just checkout their website.
From what you describe it may be easier to spread out the functions across a couple processors. I tend to use the SX chips as intelligent peripherals or for controlling the whole show on small projects.
It's good to think about the whole picture and how you want it to fit together but you may want to start prototyping each section, get that working, and then see how you want to combine them.
Robert
If I'm signalling my H-bridge to go forward, why do I need to know that its going forward? I can imagine if it were coasting or something of the like, but I don't understand it on a smallscale
robot on which I will know the direction I command it to go.
These guys know far more than I ever will. HOWEVER, whenever Ugly Buster's motor controller's received a command to instruct a motor to turn in this direction or that direction, it ALWAYS did. Having a quadrature encoder report that fact would have been a huge waste of time.
A quadrature encoder would be just the ticket to DISCOVER which way a motor was turning. But, as far as I have ever been able to figure out, in the machines I have built, anyway, direction feedback is totally useless in the drive mechanism.
All of my gear motors have quadrature encoders. I only use a single channel. A single channel provides data that may be accumulated thereby yielding position information assuming no wheel slippage. An appropriate motor controller, operating under nominal circumstances, sets the speed.
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
The other reason is accuracy. Even if you are going to control the speed how are you going to determine how far you've gone? By factoring in the direction you can get a much better idea on how far each wheel turned. Besides it seems to make all the calculations easier.
If you are just putting a low resolution encoder disk directly on a slow moving wheel then you can probably get by with just using a single channel for regulating speed.
There should be a quadrature example in the SX/B help file which can give you an idea how they are to work with.
Robert
In regard to using just a single channel of a quadrature setup, this can easily pick up extra pulses on the edge transitions. Just park a unit at the critical switch point and watch the counts accumulate due to noise or vibration. To ensure accuracy on the counts, you must take care of that in software or schmidt triggers, and this my be easy or more difficult, depending on speed of the pulses. Alternately this can be more easily and totally reliably handled in quadrature. That's why those encoders were invented. And inherently they give direction to boot.
If you don't care about inaccuracy, then a single channel will do. Only you can decide what is good enough.
Cheers,
Peter (pjv)
You have certainly given me something to contemplate and, just as certainly, I appreciate it. It is, perhaps, that my machines are designed to operate indoors and, hence, ARE slow moving. Therefore, I "get" away with it.
I still don't understand how one might instruct an HB-25 to move in one direction and it MIGHT move in the opposite, though. Is it because of false inputs on floating pins which may be caused by real inputs on adjacent pins?
Thanks!
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
My robots have experienced jitter just as you described. This did not happen often, but I could not account for it until now.·Thanks!
I all my gear motors have quadrature encoders of which I use a single channel. Is it also possible to double the resolution by using both channels?
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
The solution, at trade off of halving your resolution, is that you only count the "click" you get two white-to-black edge changes in a row (or the opposite). A white to black edge change followed by a black to white edge change would be jitter (unless it is followed by a another "perfect" black to white edge change, in which case it's a direction change -- and as you point out, on a heavy 'bot you would theoretically "know" that you changed direction because you told your motors to do it. But it's still not very stable and kinda "hacky".
In any case, that was my thinking when I was lazy and didn't build quadrature encoders. Now I wish I had - the firmware of counting quadrature is much simpler, actually, and in the long run I'd have probably saved a lot of time and hassle.
To come full circle, there is already a quadrature encoder example in the SX/B documentation (help file). It's a perfect project to get going with an SX -- making a simple peripheral that counts your encoders, maybe does some light pre-processing on them (i.e. "pre-chew" numbers for direction, distance, total distance, etc, so your Stamp(s) don't have to do the work) and deliver it ready-to-go to your host. I do recommend serial tx/rx (SERIN/SEROUT) for communication. It's (relatively) easy and there are a wealth of examples for both the Stamp and the SX. This way your host can send a string using SEROUT like "!GENC" (get encoders) and expect, say 9 bytes back with current distance and speed (as words) for each motor, plus a byte of bit "flags", motor moving/not-moving, motor direction, for each motor.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
When the going gets weird, the weird turn pro. -- HST
1uffakind.com/robots/povBitMapBuilder.php
1uffakind.com/robots/resistorLadder.php
Post Edited (Zoot) : 2/4/2009 6:35:06 PM GMT
Thank you for the detailed reply. I will definitely check out the SX/B help file. For some reason, when I wrote the program that blinked the LED, I did not get very deeply into the help file. [noparse]:)[/noparse]
Ugly Buster weighs just south of 26 pounds.
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
Actually quadrature operation gives double the resolution of a single channel.
At any instant, with quadrature there is one of 4 possible states.... lo/lo· hi/lo· lo/hi· hi/hi. And the encoder moves through all four of them in one physical cog, and each of them can be detected by the micro. The "forward" sequence is· lo/lo· to· lo/hi· to· hi/hi· to· hi/lo· to lo/lo· and the "reverse" sequence is (not surprisingly)· lo/lo· to· hi/lo· to· hi/hi· to· lo/hi· to· lo/lo.
Four phase states per cog. And the "direction" is determined at any point by comparing the current state to the previous state.
Attached is a state machine implementation of a quadrature encoder for you to peruse. Assemble it and single step through it with the debugger or SXSim to see how easily it works.
The state lookup table is made up of all the possibilities of the previous state combined with the current state.
Cheers,
Peter (pjv)
It compiles fine and I think I've got the logic right, but at this time I haven't tested it (I'd have to wait til tomorrow or the next day for various reasons).
I tried to figure out SXSim but its all greek to me.
Consider it a rough draft or outline. I have attempted no speed management and all issues of speed (Duration on PWMs, serial timeouts, ect) are random placeholders
because I've got to gather a great deal more information (How fast can a BS2 go from reading an input to receiving/sending serial, ect... and how long it takes an SX
to do various tasks).
I've attempted to create my own crude task engine to split up the various jobs the SX will have to do.
I may even end up rewriting it without using the ACK1 and ACK2 lines, just a single serial line to all three chips.
What I'd like is suggestions on how to include interrupts to prevent the SX from having to wait on the BS2s all the time, as well as advice on better methods to
accomplish this without use of assembly.
I understand I'll eventually have to learn assembly, but for now I'd like to stick with SX/B... even if it is slow or takes a lot of work-arounds.
Thank you very much for the detailed explanation and the code!
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
I've attached an encoder demo that is from an old Parallax AppNote for the PIC -- I just coded it to my particular style. Since you want two encoders I wrote the program with two and have tested them using a parallel LCD. Works just hunky-dorey.
Notes:
* You can use INC x instead of x = x + 1 (same for DEC)
* When multiplying or dividing by powers of two (2, 4, 8, 16, ...) shift operators are more efficient
-- use x = x >> 1 instead of x = x / 2
* TASK is a keyword in SX/B 2.0 so you shouldn't use this as a variable name
* Even though the compiler is forgiving you should enclose subroutines in SUB/ENDSUB blocks and functions in FUNC/ENDFUNC blocks
Thanks for the input... the reason why I didn't use anything from your previous code samples is because I wanted to learn the actual SX/B language somewhat before I did the whole
copy and paste thing.
I will, of course, examine your samples closely and will most likely use elements from all of them.
I also appreciate the tips. I wasn't aware of SUB/ENDSUB and the like beforehand (I checked, the helpfile doesn't mention them).
Question... do you think I'm trying to do too much with the SX at my beginner stage? Should I offload the PWM of the four motors to one of the stamps?
Finally, I hope your not getting annoyed with me...
Most people would be quite happy just using your code and moving on to something else. I'm a little different, I want to UNDERSTAND every aspect of my project.
Including the code you've provided. So I guess you could say I'm doing it the hard way.
I guess what I'm saying is, I'll just have to crack down and learn a little assembly... if you and the others don't mind me annoying you with questions [noparse]:)[/noparse]
I can't seem to get one of the example programs you've provided to compile.
First, the Ugha_Robot.SXB you made for me has 27 errors... To start with is:
speed VAR pwmCtrl(0) ' 0 (stop) to 255 (full)
Is giving the error INVALID PARAMETER "("
Each line that has pwmCtrl(0) in it is also producing the same error. There are several other errors after it but I'm assuming it all has to do with the same thing.
At first I assumed my compiler was outdated, so I upgraded from v1.50.1 to v1.51.03 with no difference in the errors.
I honestly don't see what's wrong with that or the surrounding lines... therefore the problem must be something on my end.
Any idea what it might be?
Also related to the same area... I'm confused.
acc1 VAR pwmCtrl(9)
This aliases acc1 as the ninth element of pwmCtrl if I understand it correctly... but you only have pwmCtrl defined with 8 elements.
Am I missing something?
I'm very confused [noparse]:([/noparse]
Like you, I used code from others but spend time learning it, understanding it, then making it my own. The PWM code, for example. Guenther's original version presets the output then turns it right back off if it is not supposed to be on. I decided to add a few cycles to the routine to give absolutely clean output. Of course, I could only do this after understanding how the original version worked.
If you're in the gift-giving mood give yourself a Parallax Professional Development board and Guenther's book -- you'll be repaid in spades with the hours of fun you'll have and the ability to quickly develop a new project.
http://forums.parallax.com/showthread.php?p=780347
Whoops, there is an error; but I got away with something. In the original program I declared pwmCntrl as eight bytes but then defined 12 elements. Here's the rub: in the SX28 SX/B puts arrays of less than 16 elements into one contiguous bank, so the rxSerial array (defined after pwmCntrl) was set to the next physical bank in memory and didn't clobber the end of my pwm aliases.
Here's the compiler (2.00.08) output -- note how the aliases are correct despite my size error:
I fixed the program (with encoders folded in). Note that the speed is 50MHz. According to SX/Sim the ISR takes about 130 cycles (max) -- at 50MHz there 325 cycles between interrupts so there's plenty of time left for the foreground to do its thing.
Rip, tear, dissect, toss what doesn't work -- I offer this stuff only as help as you may choose to use it.
Post Edited (JonnyMac) : 2/5/2009 2:46:55 AM GMT
Other than these simple caveats, the technique Peter (PJV) showed is the best way to decode an encoder, and never worry about jitter screwing up the count. It's especially nice for mechanical encoders as they also have switch bounce which is automatically taken care of by this technique.
Thanks,
PeterM
I'm glad you brought that up.... you are abolutely correct.
As many on the forums here know, I generally run all my code in a multi-tasking manner under a very tight interrupt 1 to 10 uSec regime. I would therefore arrange to call this routine very cosistently at the appropriate speed, and never miss a pulse.
Given those real time techniques, I'm sure the things Ugha wants to do could all be readily handled by a single SX, and simultaneously to boot. Unfortunately (or, depending how you look at it, fortunately) he would have to learn assembler.
It's all a matter of learning, and to squeeze the most out of an SX, assembler is an absolute must...... but all in due time.
Cheers,
Peter (pjv)
Couldn't pjv's code be made into an interrupt service routine which would make itself known as often as necessary?
The use of the phrase "to call this routine very cosistently" is what confused me. It SEEMS to this newbie that you would not have to call it at all if it called you.
--Bill
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
I've been experimenting with this style folded into SX/B -- not there yet but having fun. For grins I wrote an SX/B program that uses a modified version of PJVs encoder routine and a simplistic version of his scheduler. Note that I'm not good enough with Assembly to know if RETW will work across page boundaries and as that could happen with the placement of the ENCODER subroutine I changed the table call to the READ+offset (I used the code generated by SX/B and tweaked it).
The attached program scans an encoder every 10us, updates eight LEDs with the position every 100us, and toggles and LED every 250ms. It's happily running on my PDB so while perhaps not a perfect implementation of PJV's scheduling strategy, it is working.
retw tables must be in the lower half of codepages.
Good place is directly after interrupt routine, or after
initial jump to interrupt routine.
See attachement. This version should work.
regards peter
Post Edited (JonnyMac) : 2/5/2009 6:49:52 PM GMT
Peter Verkaik... you are absolutely correct. Please do understand in my small example I was not trying to be perfect and deal with all issues and caveats, just the basic concepts for others to understand.
Jonny... nice going.·However, nowadays I prefer to use a counter in the interrupt that I empty in the base of the scheduler. Reason is that running multiple fast tasks, on occasion one could (but should not) overrun the base counter, and hence loose a tick. Obviously this would cause jitter, and loss of total tick counts. With a counter in the interrupt, such cannot happen as all ticks are accounted for, albeit a bit skewed in time but·a better solution because·long term timing stays perfect.
Please realize this snippet is not complete... counter definitions, initialization etc. are required. But it should show the intended operating concept. I have not tested it, but I believe it is correct.
Actually, I find that serial routines work·just fine·using a 20 uSec (5x oversampling at 9600) sample clock, and I can ·run multiple receivers and transmitters at the same time (something of apparent interest to some posters here). Of course those MUST be non-blocking. IE do a little bit of work in each as required, and return to the scheduler.
If enough others are interested I'd be happy to spend some time developing those and posting them here.
Cheers,
Peter (pjv)
I'm trying to hybridize your scheduler strategy with the way I've done things in the past, that is, UARTS in the interrupt. I study your state-driven UART and found that it uses fewer cycles per call (by design, of course) so I modified it a bit and stuck it back into the ISR. So far, so good. Working on the TX side now. My intent is to use the scheduler to handle the enqueuing and enqueuing of serial bytes.
I've tested the attached program at 38.4k serial RX which is the design max.