Making Miss Princess Dance: Basic Propeller Programming
This is part 0 of a multi-part series. The other parts are here.
Miss Princess is a pretty simple robot to build and was simple to program. I figured it would be helpful to walk thru how I did it to give you an idea of basic Propeller Programming
Theory of operation
When turned on, Miss Princess does a brief wakeup animation and waits until the 'GO' button is pushed. While waiting, she does an idle animation, too.
Once the GO button is pushed, she reads the states of the jumpers and plays out the animations.
Here's what (most) of the program looks like. Next, I'll go through each part of it.
You're looking at the first lines of code for Miss Princess. Spin (the most common programming language for the Propeller) is organized into blocks and the IDE colorcodes them to make things easier.
The CON(stants) block lets me define constants I'll later refer to in the program. I've made the motor pins constants so I can easily modify the code if I use different hardware.
the first two constants are special:
How fast should the propeller run? The Prop can run from 20kHz all the way to 80MHz. The mode I've selected here is: "Take the crystal and multiply it by 16."
I just need to let the propeller know how fast the crystal is. You can use a range of crystals, I'm using a 5MHz crystal.
This setting puts my clockspeed at 80MHz, a 5MHz crystal * 16. If you don't specify anything, the Propeller will use it's internal clock to run @ 12MHz.
the next block is OBJ(ects). It tells the software that I'm going to be referring another object (a.k.a. file) from this program. The object I"m going to use is "PWMMotorDriver.spin". I got it from the Propeller Object Exchange and it makes it super easy to do pwm motor control. I just downloaded the code to the same directory that my Miss Pricess code is in.
One cool thing I've done here - I want to control 2 motors, so I can create multiple instances of the object just by changing the name on the left. I've created 2 instances, "lmotor" and "rmotor". Don't worry, I'll show you how this works in just a bit.
That's it for setup, now the programming starts.
- The CON(stants) block holds program constants.
- The OBJ block tells the IDE that I'll be referring to code in another file.
Two blocks are available to hold Spin code. PUB(lic) and PRI(vate). Program execution begins with the first listed PUB Block. I called mine 'Main', but it can be called anything. If you've programmed in another language, PUB and PRI work just like functions (although they're called methods in Spin). You can pass values to them, and you can get return values.
Here's Miss Princess' main program
This is the first method listed in this program, so it's what runs first.
First, It runs another PRI method called 'inithardware'. This method just sets up the directions on the I/O pins. Once the cog completes that method, it goes the next line and runs another PRI method, 'wakeupanimate'. This is a brief whistle, wheel shake, and eye blink.
The repeat loop below continues eternally.
- it calls a PRI method called idleloop, which randomly does an idle animation.
- it checks to see if the pin connected to the GO button has been connected to the ground (button has been pushed). The states of all the pins are in the array 'ina'. to check on the state of a pin, just get the value of ina[pin_number]. On startup, all pins are set as inputs.
- If the button is being pushed, it runs the getstates PRI method. This reads where the wires have been inserted. Once the 'playlist' is loaded, it runs the runstates method
Once the runstates method has completed the repeat loop starts all over again.
The difference between PRI and PUB
Why have two blocks to hold code? The difference between PUB and PRI is subtle - PUB methods can be accessed from another object while PRI methods cannot. It's not really important unless you're building objects for other people to use.
So far, everything I've talked about is happening in a single cog (core) The other 7 cogs have been smoking cigarettes out back. But what if I want to make Miss Princess spin around? Maybe spin in circles and whistle at the same time? I can ask the other cogs to help.
Take a look at the inithardware method
The first line calls the Start method in the lmotor object. You can see I've also passed along some values, already defined in the CON(stants) section, to get things going. If you open up the pwm motor object, usage instructions are right on top.
The lmotor.start fires up a new cog and tells it how to control the motor at the pin locations specified.
The next line takes a copy of the same object and gets it starting in another cog with a different set of pins.
The original cog has now started up 2 of his buddies, completing the inithardware method and it goes back to the main program
I'm skipping the parts on reading states and playing back the animation - I just want to show you one of the animations, Dancing.
Here's the dance method
The first line tells the cog running lmotor to run the motor at 50% clockwise. Setduty is documented in the object, but it'd dead simple to use. setduty(100) means turn the motor 100% to clockwise. setduty (-100) means 100% to counter-clockwise.
the left motor is turning 50% clockwise, now I'll tell the cog running rmotor to turn 50% counter-clockwise with the next line.
Miss Princess is now spinning. I let everything run for an 1/8 of a second with waitcnt. waitcnt pauses cog execution until the system clock reaches the number specified.
- clkfreq = number of ticks in one second
- cnt = current system clock
So waitcnt(clkfreq / 8 + cnt) translates to: find out how many ticks are in 1/8th of a second. add that to the current system clock and wait until the system clock reaches that value.
Once an 1/8th of a second has gone by, I flip the motor directions. Then I wait for another 1/8th of a second. Then I let both motors coast.
That's it - If I've missed anything, let me know!