I printed a body and added BLE. I’ll explain tomorrow after I get some rest, but the BLE was to allow me to test directed locomotion. I’ve also done some feature testing (load-sharing, charging circuit, “hunger” ADC), the board is actually a good design. Works well.
The BLE is the HM-11, itty-bitty BLE.
My goal is to test the physical and feature designs with the ATtiny84, and when Mr. Bdk6 releases his toolchain for the LPC1114, switch it as the controlling chip.
This is my version of Yahmez’ Baby Bot, the ATtiny84 Y-Baby (YB84). There are few differences from Yahmez’ version.
This version uses an ATtiny84.
It uses a LIR2032.
It has a charge circuit built in, using the MCP73831. This circuit has load-sharing capability so the baby can “feed” without sleeping.
The YB84 has two LED indicators.
One pin on the YB84 monitors the battery voltage, so it can tell how “hungry” it is.
This version came about because Yahmez’ Baby Bots were so damn cool I had to copy them. Here’s the node where I asked permission and added design notes. Also, I’ve wanted to make a small, cheap, small robot to couple with my Overlord projects in hope to develop an electronic lab-rat.
Note, I’ve not included the IR receiver or IR transmitters in the BOM. I’ve not tested the circuit yet, or sourced cheap parts. But I’m shooting to keep them under $10.
YB84 BOM Total: $7.70
Really, there wasn’t much to developing this little guy, Yahmez had done all the work. I simply selected an Atmel uC that I felt was cheap enough and provided enough pins to accomplish what I wanted to do with the little guy. The one problem I had was with the load-sharing circuit I tried to copy from Zak Kemble.
When I went to layout the load-circuit my mind got locked on the old thought, “MOSFET diodes go against the current.” This caused me to lay the DMP1045U down backwards, which essentially shorts the battery.
This took me a bit to figure out. I apparently wasn’t the only one that made the mistake, as a comment on Zak’s blog had a fellow asking my questions for me. In the end, I got the circuit sorted out and now the little guy works as intended.
That’s about it for now. I still have lots of testing to do on the little guy.
Motor placement for correct movements.
Leg angling for correct gait.
Currently, I have a pogo-pin programming header. But it is imperative to accomplish my goals for this little guy to make him programmable via IR. This should allow me to program a swarm of these little guys without manual interaction. I know the Kilotbot projects modified the Arduino code to do this very thing. My ideal setup is to add a mobile hanging over a swarm of these guys. On this mobile would be: IR-TX, IR-RX, and a camera. The camera would be using Overlord to track these guys and the IR to communicate with them in mass.
As always, thoughts, opinions, and critiques I welcome :)
In the end, I ripped maxEmbedded code and got my PB3 LED working in about 10 minutes. Then, I spent the next three evenings reading trying to figure out what maxEmbedded’s code was doing.
Really, it was the register and bit names that were tripping me up. Each had names like, “TCCROA1” and “OCR0A”, and so forth. Each is an initialism. This was a problem for me, I was soon lost in a jungle of intialisms, which represented abstract concepts, such as other intialisms. I felt as if I were bumbling through a George MacDonald dissertation on an orc language:
NOTE: Dear reader, I apologize if that video confused you more. Again, this is a journal so to help me remember what I learn. And I find adding a story to ridicules abstractions is necessary for me.
Alright, now that I had a little story in my head to handle the intialisms learning PWM on the AVR flowed a little easier.
Here is my reference list:
1. TCCR = Timer/Counter Control Register.
On the ATtiny1634 there are 4 control registers. One is 8-bit and the other is 16-bit. Though, this journal will stick with the Arduino standard, meaning, I’ll use my 16-bit as an 8-bit. Here are the four Timer/Counter Control Register names:
TCCROA (8-bit timer) and TCCROB (16-bit timer) control the PWM functions of pins.
TCCROA/B control pins PA5 and PA6.
TCCR1A/B control pins PC0 and PB3.
2. COM = Compare Output Mode
There are four bits per TCCR register that control the compare output of each pin. This is where the magic happens. If a pin is setup in compare output mode, it will only go high when the counter is equal to or higher than a number you provide. For instance,
Is timer greater than 100? Then go high.
This is the essence of PWM. You provide a number, in our case a number between 0-255 since it is an 8-bit counter, and if the timer is equal to or greater than your number, the pin will be HIGH. Then, the timer will continue to count up and will reset to 0 after 255 is reached. Mind you, the comparison is made every tick of the counter, so when it resets to 0 the comparison will be made and the pin will go LOW ago. Voila!
There are four COM bits in each TCCR register, two control the output of one pin.
Found in TCCR0A:
COM0A0 and COM0A0 control pin PC0.
COM0B0 and COM0B0 control pin PA5.
Found in TCCR1A:
COM1A0 and COM1A1 control pin PB3.
COM1B0 and COM1B1 control pin PA6.
Now, switching these bits can create many different types of output. But I stuck with Arduino standard.
3. WGM = Wave Form Generation (for 8-bit)
There are 3 bits that control the type of PWM we end up with. There are all sorts of wave-forms, but the main three are:
(Here is an Arduino article that explains them a bit.)
The one I’ll invoke is Fast PWM,
We select this by setting WGM00 and WGM01 bits.
4. How to set the TCCR registers.
So, setting things up, the code will look something like this,
I left the assignment of the TCCR registers in a binary format. This was just easier for me, but you could as easily use bitwise operations, e.g.,
You notice we set the COM0A1 or COM1A1 bits, but later I’ll change this so they are not set at initialization. I found if you connect the pins to the timers at the beginning, then they’ll constantly have a nominal voltage on them. This is made clearer if you have an LED on the pin. Therefore, unless you set the COM0A1 and COM1A1 bits low then the LED will never fully turn off.
Also, we have to set the data direction registers for the PWM pins to outputs.
Now, that the initialization is done, let’s look at the code I used to demonstrate PWM on the ATtiny1634.
You’ll notice this is a modified “Fade” sketch from the Arduino world.
The above code provided this output,
How the magic happens in AVR is around the output comparison registers,
OCR0A – controls PC0
OCR0B – controls PA5
OCR1A – controls PB3
OCR1B – controls PA6
Basically, the OCR registers flip the pin HIGH or LOW (as setup by the TCCR) based upon the number you assign to it. If you assign OCR0A a value you of 144, it’ll be LOW (0v) for 144 clock cycles (TCNT) and HIGH (5v) for 111 clock cycles. This gives us our PWM. Booyah!
OCROA = 127;
This sets PC0 to approximately 2.5v. (127/255bit * 5v = ~2.5v)
OCR1A = 255;
This sets PB3 to 5v. (255/255bit * 5v = 5v)
Ok, here’s the tricky one,
OCR0A = 0;
This should set PC0 to 0v, but that’s not the case. When we set the COM registers (COM0A1, etc.) there are internal pull-up resistors connected to the corsponding pin. This results in a constant nominal voltage unless the COM register is set low again.
This can be done using the XOR operator on the TCCR register,
TCCRO ^= (1«COM0A0)
This should set the PC0 pin to 0v.
It’s really that simple…well, unless you want to mess with the type of PWM you are creating. Ugh.
5. ATtiny1634 analogWrite.h
After I figured out how to use PWM on the ATtiny1634, I started thinking how nice it would be to re-create the Arduino library for it.
Being able to write,
had a lot of appeal to me.
I played with it a bit and ended up with the following,
A synopsis of the library,
Lines 1-2 and 90 make sure the library is only included once.
Lines 13-16 define the ATtiny1634 pins.
18-28 setup the TCCR registers (notice, the pins start out off to prevent nominal voltage).
41-42 makes sure our PWM value is in range.
46-85 control the PWM on each pin, with an else statement to gives us a true zero voltage in the case a PWM value of 0 is passed to the function.
I saved this as 1634analogWrite.h and then wrote a sketch to use
Ok. I’ll revisit this probably with a complete H-Bridge control library.
As always, please feel free to correct my f’ups.:)
Here are some random notes on working with the HM-10.
Working RX/TX LEDs
How upgrade the HM-10
Pseudo Star-Networking HM-10s
Working RX/TX LEDs
I spent some time trying to find a way to create RX/TX traffic LEDs for the HM-10 breakout. This might be easy for some, but it was a real thinker for me. The HM-10 is TTL, which as it was explained me, means that the line is high while idling and low to represent data. The problem here is the typical LED setup doesn’t work. Sadly, this is the setup I used in two of my fabricated boards.
Well, this stupid mistake has been staring me in the face. Especially since a few guys ordered my board and mentioned politely, “It only flashes when large amounts of data go through.”
After asking around I was told the best way to approach this problem was to use a PNP transistor. The guys at LMR pretty much gave me the solution here. The people here are brilliant. (Please don’t tell them I’m not, I want to hang with the cool-kids for a bit before they find out my mom still dresses me.)
But while LMR was helping me I found Sparkfun’s solution to the problem on one of their Xbee Explorer boards. In essence, the LEDs are wired from in between 3.3v and the TX/RX lines, this allows them to act as sinks whenever the data is coming through. As long as the LED is a green, blue or other with a 3.1v drop, then the voltage being sunk doesn’t interfere with the data.
Of course, this is risky. The CC2540 is a nifty chip, but it isn’t meant to be a high-current uC. I dug through the datasheet to see what the current sink rating of the RX/TX pins (2 & 3) were. Well…it’s not in the datasheet! But then I came across this post.
Well, there we go. So, I breadboarded a circuit and found it worked. But I was only brave enough to use a 1k resistor.
Eventually Brassfly helped me work through the math,
“Given that the voltage drop across the forward biased junction of a red LED is about 2 Vdc (we will call that Vf) the total current through the LED/Resistor is equal to Vsupply-Vf/R, or in this case 3.3V-2V/1000=.0013A=1.3mA….If they are bright enough then don’t worry about it. If not, you might then consider using a 470 ohm resistor (2.7mA) or even 330 ohm resistor (3.9mA).”
So, I made up a fresh version of the HM-10 breakout board (v.9.9) with this LED setup. I wired it up. And, it seems to be working.
Again, can’t use yellow or red LEDs on the RX/TX lines, since they do not create a high-enough voltage drop to signal the CC2540 low).
How upgrade the HM-10
(NOTE: You can only upgrade versions 507 or later, according to Jnhuamoa)
1. Visit the HM-10 website and learn all about the HM-10 upgrade process.
2. Download the lastest firmware version file. (Make sure your HM-10 is a CC2540, if it’s CC2541 use that file).
3. Before we proceed, here’s my bitchy disclaimer. Re-read the instructions and b_eware all ye who try to upload firmware and don’t blame me for problems, like the death of your HM-10 :P_
4. Open Realterm (or your preferred serial terminal).
6. You are about to cross the point of no return. Open a serial connection to the HM-10 and if you are prepared for the worst, type: “AT+SBLUP” and the module should reply, “SBLUP+OK.” Congratulations, you’ve bricked your HM-10–well, at least until the upgrade process is complete.
7. Now, CLOSE YOUR SERIAL CONNECTION in Realterm. Yes, I forgot to close the connection myself and was pissed because I thought I had bricked my HM-10 when I tried the next step. I’m a hack, hacking with a hacksaw.
8. Unzip the “HMSoft-10-2540-V522.zip” (or the latest version, respectively) into a folder. There should be several files in the folder, open HMSoft.exe
9. Now, click on the ellipsis and select “HMSoft.bin” file. Then, type the number of your com port. You don’t need to include anything but the number (e.g., COM34 becomes 34).
10. Pray (for the HM-10 this works best if to Nyarlathotep).
11. Click “Load Image.” The upload process should start pretty quick and should take approximately a minute and a half to complete. Do not remove power or screw with the wiring until you see the following:
12. Click “Ok” and open Realterm. Bring up a serial connection and type: “AT+VERS?” It should respond, “HMSoft v521.” That’s not a typo, even though we uploaded the V522 version it responds saying it is V521? You got me. But it works ok :)
Pseudo Star-Networking HM-10s
Setup up at least three units.
Unit #1 (“Master”)
Unit #2 (Slave #A)
AT+ADDR? Write the address down. Mine replied, “OK+ADDR:883314DD8015”
Unit #3 (Slave #B)
AT+ADDR? Write the address down.Mine replied, “OK+ADDR:883314DD8016”
Oh, one more bit. You need to wire pin 4 on the Arduino to the HM-10 Master’s reset line. Since there really isn’t anyway to reset the slave and the master at the same time. This gets us around the timeout for both, slave and master.
RESET<—-1k Resistor—–>D4 on Arduino
Then use the following code, changing your addresses respectively.
Not going to lie. I’m pretty dissapointed with how complicated the firmware makes it to switch between nodes. Although, I’ve only begun to play with firmware versions greater than V303.
I couldn’t do much better than about 2.84 seconds switching between nodes. This simply wont do for me. And, of course, this rate goes up with more nodes. So, a swarm of little robots controlled this way would be unmanagable.
I tried tweaking the timing of everything in the code. The HM-10 wasn’t having it. If I lowered the delay following an AT command it, the command wouldn’t take. This would often cause the nodes to become stuck (waiting for timeout, still connected, etc.) Also, the hardware reset on the HM-10 is said to be 100ms, but I’m finding it unhappy with much less than 250ms.
Anyway, if anyone else wants to carry the banner my suggestion is uping the buad rate and sticking with hardware resets, like in my ATtiny Bitsy Spider board.
Awhile back Sparkfun posted a new product, their MiniMoto breakout board. It breaks out the DRV8830 IC, which is a serially controlled (I2C) H-Bridge. I thought the chip was nifty. A few problems though,
Sparkfun’s breakout was 25x25mm for one bridge. If I added another and then an Arduino Pro Mini it’d lose smallness.
It’s not on a purple board :)
So, I set out to make a robot controller with it that was smaller than a Sparkfun breakout. What I ended up with is a little bitch I refer to a Kobold.
The board is pretty straightforward. It has an ATtiny 85 that acts as an I2C Master using the SoftI2CMaster library. This allows the Tiny 85 to control two motors using only two pins, leaving three for your pleasure.
My end goal will be to build a shield for it and hook up a HM-10 to make it a little wireless tethered bot. This would bring me down to one pin, which I’m sure will be some sort of range finder or feeling sensor.
I’m currently working on a second iteration to correct some problems. I’ll also add a few features, like my pogo-pin programming interface. The shield I have designed for it will also include a charging circuit and probably a SMD step-up circuit that should convert a LiPo to a nice 5v.
Anyway, work in progress…just thought I’d share.
If anyone is interested in this board, please wait a few iterations. I get worried I’m making blue-smoke with someone else’s money. :)
I thought I would journal my work as I begin to venture from the comfortable playground of Arduino C and IDE.
I’ve tried a few other IDEs and C++ to work with different chips. But each time I realize I don’t know enough about C/C++, tool-chains, and workspaces to make a robot with them.
Bdk6 gave me an LCP1114 and I was able to get a blink program to run on it, but it was from a .bin file that someone else compiled, I simply uploaded it. I tried to work through two walkthroughs to get a programming environment setup (mbed and LPC1114). Bust on both.
I spent some time Googling, trying to find out what I was doing wrong. I came to the conclusion there was not enough information for an ignorant like me to figure out how to set up the workspace; the LPC1114 is an intermediate chip and I’m a beginner.
I decided to pull up tutorials on AVR programming. I knew there was an entire community around the Atmel chips, a community besides the Arduino community, a bare-metal community. I figured that would be the “beginner” environment to work in, since there would be enough documentation for me to learn
So, that brings us to my journal. I’ve not written this out to impress anyone. I simply find I learn better if I force myself to document what I’m doing. It encourages me to understand the information, since I have to explain it to someone else. Learning by verbal-processing. That being said, I invite criticism. Anything I’ve written that is incorrect, feel free to yell at me; it’ll learn me. Also, a lot of this has been written; I know I’m not being original. I’m also not delusional in believing I explain it better, but I do plan on making this journal flow towards how AVR programming can help me build better robots. Ok. Disclaimer done.
What do you mean the silk-screen is mislabeled…shh.
After I got the pinout all figured out, I downloaded Atmel Studio 6.1. I know there are many different opinions about what IDE to use, and a lot of them seem to favor a text editor and AVRDude, for a change I wanted to go with a commercial IDE. Also, it seems like a lot of those in the AVRFreaks group actually like Atmel Studio.
It’s pretty easy to set up. You install it. Plug in your AVR ISP MKII. Then, File–>New Project. Select “GCC C Executable Project” and name your project. Hit “Ok.” Atmel Studio will bring up a list of supported chips. I scrolled down until I found my chip, “ATtiny1634.” You’ll notice the datasheet and supported programmers are to the side.
Alright, so, here I was. IDE all set up…um, now what do I do?
I began to Google until I found an AVR Tutorial Series that looked like it would bring me from the basics, well, up to Serial communication. Really, I figure if I could learn the following I could build whatever I wanted out of it:
Luckily, I found the series put together by Newbie Hack’s series.
So, I decided I would try to walk through all of his tutorials and see if I could apply them to the ATtiny1634. NOTE: The author of this series appears to be Patrick Hood-Daniel, a fellow Texan (don’t hold that against us :P).
I started Newbie Hack’s AVR Series after he had his programmer setup, his “First Program.” The uC’s version of the “hello world,” the LED blink. In Arduino I’d accomplish this like so,
I got Newbie Hack’s code going within 3 minutes
I looked down at my breakout board, I had some “PBx” pins, so I figured I had a Port B. I located pin PB0 (the the pin marked “1” in the above image) and I threw my LED and resistor on it.
I uploaded the program and got this:
But the purpose of this journal is to understand what I’m doing, not just copy it. So, the following is an explanation to myself, in case I forget stuff.
What’s Going on in the Blinkin LED Code
Atmel chips are amazing. They are versatile little uCs and the way this versatility is utilized from programming is by changing bits within a byte. For example, a pin can work as either a INPUT or OUPUT, but how does the chip know which one? You change the Digital Direction Registry (DDR) Bit of the pin. The DDR bit is a little switch that can be flipped either on or off through programming. When it is off (0) the pin acts as an INPUT, when it is set on, it acts an OUTPUT.
Now, Atmel chips are divided into ports, and thee ports have 8 pins (or less). This allows one byte to control alot of the functions of one port.
Newbie Hacks (NB) does a great job of explaining this. He even draws out a picture;I like pictures.
I like to think in images, so I see each bit as an LED. It makes sense, an LED has two-states ON/OFF; a bit has two states 0 or 1. So, a byte for me is 8 LEDs numbered 0-7.
The above image could represent the following code:
The Atmel chips have different ports, which usually have an array of pins. So, the DDR stands for digital direction registry of port B. Then, we assign a state to every pin on port B (PB0-PB7) by setting a bit to either 0 or 1. So, according to the image and our code, we are setting ports B1-7 as INPUTS (0 = INPUT) and PB0 as an OUTPUT.
But don’t be confused, the LED we have connected isn’t turned on yet. The voltage on PB0 still equals 0. But, we could now change the voltage from 0 to 5. We do this by changing another registry. The port state registry, PORTB.
Actually sets pin 0 (PB0) on port B to 5 volts. The other pins are still set as inputs, so you can’t change the voltage.
That simple folks? Well, we also have to insert a delay. Delay’s are really just telling the uC how many cycles to do nothing. There is some math involved that divides the clock speed to get the exact time delay you would like in finite-ish numbers. This math is locked away as a function in the <util/delay.h> file. So, for our sake, it is a simple matter of adding:
This tells our Tiny to sit idle for half a second, then continue. Also, there is a _delay_us() function that will delay microseconds.
Ok. Here are my study-notes
Note #1: Adjusting the Clock Fuse.
I did have one modification I had to make to NH’s program. He uploads his program using AVRDude. Atmel Studio actually has libraries to specific to the Tiny1634, these are included when you setup your project. One of these libraries require you tell the program how fast the CPU is going to be running. The ATtiny’s have an internal 8mhz oscillator, but there is a Atmel fuse that is burned at the factory to divide 8mhz by 8, giving you a run speed of 1mhz. I wanted to be working with my 1634 at the full 8mhz.
So, I went to Tools—>Device Programming. You then have to select your “Tool,” for me the AVR ISP MKII; your device, ATtiny1634 for me; interface, ISP. Then click “Apply.” Now, under “Device Signature” click “Read.” This should bring up the Device Programmer window. Now, make sure you have your programmer wired to your chip correctly and you have a stable connection (might throw a .1uF radial capacitor between VCC and GND on the Tiny1634). Click on the “Fuses” tab.
Uncheck the “CKDIV8.” It is the fuse that does exactly what it says, divides the clock by 8, reducing the chip from 8mhz to 1mhz. Don’t change any other fuses at this time. Hit the “Program” button. This will set the fuse. If all goes well we didn’t brick your chip.
While we are under the device programming window, click on the “Memories” tab. This is where you will actually upload your compiled program onto the chip. Here, if you click on the ellipsis you can select the code you would like to upload. This ends in “.elf” (unless you are using a chip besides an Tiny, then it’ll end in .troll, hehe). So, select your code and then hit “Program.”
Now, Atmel Studio is setup to automatically program your code using the last selected devices whenever you hit “F5.” Sadly, I found this rarely worked properly. But I’m betting it’s because I have no decoupling capacitor–yes, I confirmed this. I put a 1uF radial capacitor between VCC and GND, now the “F5” programming works as expected. I’ve no idea. Seems to work if I manually program it though. Shrug.
Ok. Now we have our Tiny running at 8mhz, we will need to adjust this in our code.
Here we are telling the program our chip is running at 8mhz (8,000,000 hertz).
After this, I uploaded the code and it ran just as I had hoped. The LED would be on for half a second, then off for half a second.
I will not go into much detail on Bitwise Operators because Newbie Hacks does an excellent job explaining them. And they are all over the internet. But for our purposes, building robots, it is good to know what they look like and what they’ll do to our bits (remember, your pins’ output are controlled by bit registries).
So, I’ll cover two that apply to our example and one bitwise helper: OR,XOR, and « (left shift).
The OR operator:
We learned that the following sets pin PB0 as an OUTPUT.
But we can use the bitwise operator, OR, to do the same,
The “|=” is an abbreviated operation that represents the following,
In English, “Whatever is in DDRB is equal to whatever is in DDRB OR 0b0000001.”
That looks like this,
Bitwise operators, like OR’ing, are done on the entire byte. That is, each bit, 0-7, are OR’d, so you have to keep in mind the operation isn’t done only on the bit you want to change. This is where truth tables come in. Below is a table describing the output of the OR operation.
In electronics registers usually have a fixed width. For example, the PORTB registry has the width of 8 bits (0-7). The left-shift operator («) allows you to address a specific bit in a registry, it does this by moving the bits in the registry to the left. The registry itself stays at a fixed width, so when this happens the new places introduced are zero. The bits that get shifted past the width of the registry get detroyed. Going back to the PORTB registry, you could address a different pin besides PB0 by using a shift-left operator. The left-shift operator allows us to quickly create a bit-mask from a byte. In code, this looks like the following:
The above takes the binary number assigned in DDRB and OR’s it with a bit mask that is exactly the same, except the third pin, that pin is equal to 1. Therefore, PORTB would look like this after the operation,
This seems more complex to me, but I understand it becomes very important when you start pulling apart the metal of an AVR.
One last thing, the <avr/io.h> contains defined pin constants. So, this operation,
Can be written like so,
They do exactly the same thing–and I guess the latter is easier to read. Pfft.
The XOR Operator:
The XOR (^) operator is much like the OR operation. Except, if A and B are equal the result is 0. They are not equal, the result is 1. Here’s the XOR truth table.
Going back to the LED example, here is the XOR operation.
We use the XOR operation to turn the LED off and on. Since a XOR operation on a bit is basically going to set it to the opposite it is in. Therefore, if it is 0 it becomes 1, if it is 1, it becomes 0. This means we can actually shorten or blink code using the operation.
So, our simplified code for Blinkin LED is,
And that’s it. That is as far as I’ve gotten. Now, before I move forward I plan attempting to interface a SN754410 and two small motors. I figure, we know how to perform digital pin control and this should allow use to control motor direction, even if we can’t yet control the speed.
Robot Application Note #1: SN754410 and ATtiny1634
First thing to do is wire up the H-Bridge.
You may notice we wired both “EN” pins to 5v, I don’t know how to generate PWM with AVR yet (but I hope to get there, so I’ll revisit this). Also, I used 5v as both my motor source and the Tiny1634 source. The motors I used were these little guys. I figured since the drop voltage of the SN754410 is around 1.4-2v then I’d be pretty close to the target voltage of my motors (5v - 1.4v = 3.6v). I kept everything approximate to the Tiny’s voltage rating; I figured if I wired something wrong I’d be safer keeping the voltage sources low.
And good call, this is the wiring mess I ended up with…I thought I would start with one motor and move to two.
I began thinking code. I know the basics of the SN754410 and I wanted to be able to hit all the functions of its truth-table.
So, I figured all I needed to do was get two of my IO pins to go HIGH and LOW to turn one direction, then switch them to go opposite. This reminded me of the XOR (^) operator, since it did exactly that, turn a bit to its opposite. This is the same operator we used to blink the LED. I ended up with the following code:
This code moved my motor one direction, then the other. But there was no pause between the changes of directions. I pulled it a part pretty quick, since I’ve generally had bad luck with instantaneous reversal of inductors.
Well, that wouldn’t do. But I realized another problem. The XOR operator would flip the the pins from high to low, and back again. But how would I set both pins to low? Or both to high? Now, in Arduino C it’s pretty easy, you just write digitalWrite(pin#, HIGH), but in AVR we are controlling bits.
I know I could accomplish this in long-hand, like so:
This gave me the output I wanted. The motor would turn one direction for 1.5 seconds, then stop, turn the other way, then stop, and start over. Like this:
This code felt bloated to me. And another problem was dawning: What if my LED was on PORTA? That means I would need to keep track the state of three bits (1) bit controlling motor connection A, (2) bit controlling motor connection B, and (3) LED. This means I would need to track 9 possible states (3 pins ^ 2 states = 9 pin states). Now, I might be able to do this mentally, but it would be taxing, especially if my code is dynamically modifying the PORTA registry. But what if all 8 pins were used? 8 pins ^ 2 sates = 64 pin states. Ummm…no. I can’t do it.
It hit me. This is why bitmasks and bitwise operators are so important; they dynamically change the states of one bit, while preserving the states of the rest of the registry. Nifty.
I spent some time with in Newbie Hack’s tutorial: MCU LED Blink tutorial. Specifically, the video there. In it he explains how to use bitwise operators and bit-masks to change the state of a pin while preserving all of the other pin states.
Now we’re cooking.
We already know the bitwise operator (and mask) to set one bit high: OR.
The OR (|)operator sets the a pin HIGH:
Sadly, clearing a bit while preserving the registry is slightly more complicated. To clear a bit we still use a bitmask, but we use two operators: AND (&) and NOT (~). Their truth-tables look like the following:
NOT (~) is unlike the other operators, it’s simple. It inverts the bit. If 0, it becomes 1, if 1, it turns to 0.
Instead of immediately modifying the PORT state we actually modify our bitmask with the NOT operator. This gives us the inverse mask (00000001 becomes 11111110). We then AND (&) the inverted mask with hte PORT’s original state to clear PIN0 while preserving the other bit’s state. Here’s the LED example for NOT and AND operation to clear a bit:
Ok. I could wrap my head around this. I developed the following code which did what I wanted:
Code to NOT a PIN looks like this PORTA &= ~ (1 << PINA1);. In plain English and in order of operation, “Set PORTA PIN1 to HIGH, create a bitmask of PORTA, then NOT that bitmask. After, take the NOT’ed bitmask and AND it with PORTA’s original state.”
Whew. I’m not sure I follow that even after writing it. But I understand it. Really, PORTA &= ~ (1 << PINA1) = Set PA1 LOW.
But this is good. We now can dynamically change the state of one PIN without destroying the state of the other PINs on the same port. Booyah!
Alright, let’s go for broke; now that I understand how to set pins HIGH or LOW, I wanted an easy way to control a motor with AVR. I wrote five functions. Four control the states of the motor (HH, LL, LH, HL) and one is a delay function that will accept a variable. The functions can be called from the main loop. Each one expects three parameters, two pin numbers and the number of milliseconds you wish the function to run.
Nifty, eh? Now all we need to do is add a second motor and then we can pass the functions the second motor pins and we can use the same set of code to control both motors. Aren’t we efficient!
Some time later…
Well, that didn’t not work as I wanted. I’m tired and didn’t think through how the delay would work in the function. The function would be called for motor A, but motor B wouldn’t be called until A was done. Doh.
Yes, this could be rewritten a hundred ways to salvage it. But! Right now I’m tired, so here’s our working code.
Something else I realized. I couldn’t wire motors to a different PORT (my schematic shows PB3 and PC0) since my functions call upon PORTA specifically. Eh, oh well. I’m tired. I’m sure I’ll clean this up over the next few days.