2009-09-03

ROMfoolery

"I spy, with my little eye, a ROM address beginning with the sequence 0001010000011001..."

I've recently been playing with DS18B20 One-Wire digital temperature sensors (from Sparkfun -- big surprise there). They're fun little devices -- accurate and cheap. They're also insanely easy to wire up, having only three leads (they look just like 2N2222 transistors).

When Maxim says "one wire," they mean it (not counting ground); you can even power it using the data line if you're careful about timings and provide a strong pull-up when needed. Developing microcontroller code to drive them is nontrivial, though (various layers include bit timings, commands and ROM codes etc), but once it's working, they're very reliable.

Temperature A-to-D conversion takes up to 750ms, but you can issue a broadcast command to get all devices on the line to start a conversion at once, then read the results back at your leisure.

Here's the first interesting part.

With a one-wire interface, timing and coordination of reliable two-way communications gets tricky. There's no synchronization line -- and from the description in the datasheet, the timing accuracy of these sensors isn't all that wonderful, anyway. The scheme that Maxim worked out was that a short low pulse represents a one, and a longer low pulse represents a zero. A really long (more than 480 microseconds long) pulse resets the bus altogether.

But wait, there's more.

The interface is specified so that multiple slave devices (I.E. multiple sensors) can share the bus. This takes not only coordination, but implementing the bus with an open-drain configuration (in other words, you use a fairly weak resistor to put 5V on the bus -- and devices are only allowed to short the bus to ground, not pull it high.) This keeps communication collisions between devices on the bus from causing short circuits and burning up output transistors -- but it also complicates timings and the basic communications protocol.

Now for the really interesting part.

Each sensor has a unique 64-bit serial number, which you need to know in order to interface with it. It's not marked on the package (too small) or in the literature you get when you order the part (that would make too much sense), so Maxim provides a "Search ROM" feature, which uses a bizarre take on Manchester coding to send the address back. Two bits are read out at a time for each bit position in the 64-digit ROM code. 01 means the bit is a 0; 10 means it's a 1; 00 means a conflict (someone on the bus sent a 1 and someone else -- maybe a lot of someone elses -- sent a 0); and 11 means that nobody is listening (check the circuit?). By going through bit by bit (with a lot of backtracking, if addressing a lot of these on one line), you can eventually determine the ROM codes for all devices on the line. It's very Byzantine -- but when you think about it, that's really a result of the one-wire interface.

Here's an example of how it would work with simpler devices that only had 4-bit ROM codes.

Suppose you had three devices on the bus, with ROM codes 0110, 1110, and 0101.

The least-significant bit is sent first; this comes back in two-bit Manchester coding as an "00." (I.E. there's at least one of each variety.)

The master (microcontroller) makes a decision at this point, and sends a one or zero. Any devices which match this bit in the first position keep listening; all others go idle and wait for a bus reset. In this case, we'll default to zeros first. The master notes a conflict here, and sends a zero.

Sensor 0101 reads the zero and goes idle. Sensors 0110 and 1110 read the zero and continue, since it matches the least-significant bit in their ROM code.

The master then continues with reading the second bit. This comes back as a "10" -- apparently all remaining devices agree that there is a "1" here. The master records a "1" and continues.

The master reads a third bit, which comes back as another 10. Another "1" is recorded and transmitted.

The master reads a fourth bit, which reads "00". (Another conflict.) The master sends a zero; device 1110 goes idle, while 0110 is made active (having passed all the way through its ROM code). The master records 0110 as the ROM code of one of the devices on the bus -- and records 1110, since it can deduce that; this being the last bit, that's the only possibility.

The master then resets the bus, starts the ROM search process again, but sends a "1" this time when it reaches the conflict in the first bit. Device 0101 remains active this time while 1110 and 0110 go idle. The master then continues with the process; since 0101 is the only device left on the bus, no more conflicts occur.

Think of this process, expanded to 64 bits, and you have an idea of the situation. And not only that -- you then have to figure out which sensor is which, if they are in different positions!

The amazing part is, once the code for this is written, it's all over in a few milliseconds, and the ROM codes appear on the LCD...

Labels: , , , , , , , , ,

2009-08-26

PIC programming, Cadillac style!

I've been working with the PIC18F4620, and am impressed by how easy it is to program, compared to the smaller PICs. I think I'm in love. Or at least in lust.

Here are some of the improvements from an embedded developer's point of view, as contrasted to the (still very cool) 16F887.

32MHz max internal clock speed instead of 8MHz
This alone would be worth it -- a PIC running at 8MIPS! It will go to 10MIPS with an external 10MHz clock, but 32MHz with no external parts needed is very cool. For us PIC16 afficionados, it's like finding out that your new car can go 400km/hr on the freeway, legally, when the old one only did 100.

MULWF and MULLW instructions
You can tell the PIC16 programmers in the audience; they just started drooling. Hardware multiply -- and not only that, but hardware multiply that executes in a single instruction cycle. I see a re-writing of some of my math libraries in the cards!

(almost) NO MORE PCLATH!
Ding, dong, the witch is (almost) dead! GOTOs and CALLs on PIC18s access the entire memory space directly. So, unless you're doing a computed GOTO, you don't have to worry anymore about the value of the high bits of the program counter. No more save-the-old-PCLATH-value-then-set-the-new-one-then-call-the-routine-then-return-then-restore-the-old-PCLATH business. Just GOTO or CALL your routine! And with PIC18s, you really don't want to do computed GOTOs anyway, because now, you have...


TBLRD* and TBLWRT* instructions
PICs are Harvard architecture parts -- meaning they have separate memory spaces for instructions and data. Before the PIC18s, there was no way to access the program memory at runtime -- lookup tables were handled by a series of RETLW statements, whether specific or produced by the assembler from "dt" statements. The TBLRD* and TBLWRT* instructions allow PIC18s to read from and write to program memory. One use of this is to use the (relatively huge) 64kB of program space Flash memory (32 kWords, but addressable as bytes for data use). Large, verbose diagnostic messages can now be programmed relatively easily, with just a little more work than calling printf() from C.

ADDWFC
Add W to F with Carry. This is a bigger deal than it sounds. It will speed up the ADD32 and SUB32 libraries quite a bit, I think.

MOVFF instruction
With MOVFF (which takes two instruction cycles), you can copy a byte directly from one memory location to another, without going through the W register. This doesn't always save time (although it's always at least as fast as the old way), but not having to go through the W register can be very helpful.

Branch instructions
Branch-if-zero. Branch-if-not-zero. Branch-if-carry. Branch-if-not-carry. Branch-if-the-moon-is-in-the-seventh-house, for all I know. Well, maybe not that last, but PICs now have more branch statements than just BTFSS and BTFSC. This doesn't quite rank up there with MULWF or TBLRD as far as I'm concerned, but I'm told it makes the compiler guys happy.

RRNCF and RLNCF
Rotates that don't go through carry. Huh. And RRF/RLF are now RRCF/RLCF, just to break older code, I suppose.

TSTFSZ
Test F, Skip if Zero. Sounds like it could be useful.

DCFSNZ and INFSNZ
Now you can increment or decrement and skip if *not* zero. I'm confused now; this is backwards from how these are normally used. This smells like something a compiler would like to have available.

CPFSEQ/CPFSGT/CPFSLT
Compare F with W, Skip if equal, greater-than, or less-than. Hmm. Equality tests sound useful.

BTG
Bit Toggle, to go with BSF and BCF. Well, I guess it was the missing option...

DAW
Decimal Adjust W. Something to do with BCD math. Sounds possibly useful.

PUSH and POP
More to the point, there's a real, honest-to-goodness stack now! This is a good thing. Low-range and mid-range 8-bit PICs have an 8-entry hardware stack. Except in simulation, it was anybody's guess just how deep into the stack a program already was when a particular subroutine was called. This made for very conservative use of the CALL statement -- and has prompted me more than once to do things with macros that I really shouldn't have.

RCALL
Relative Call. This sounds like a segfault waiting to happen. I hope it's not what I think it is...

Two NOP opcodes
To quote Bob from Frontier: First Encounters, "My, oh my, what happened here?"

RESET
There's probably a legit reason for this, but it escapes me at the moment. Why not just GOTO 0x0000?

MOVLB

Move Literal to BSR? *looks up what BSR is, anyway*

LFSR
Move Literal to FSR? *looks up what FSR is, too*



OK, now for some of the drawbacks:
Nonhomogenous instruction size
In other words, some instructions are two words long, and others are one. This means that you can't just cavalierly do a GOTO $-5 to jump back five instructions anymore, without at least thinking about which instructions are which. If you inadvertently point to a location in the middle of an instruction, the assembler will tell you. If it happens to line up on another one, it won't know anything is wrong until you run it and it doesn't work as intended. Best is to come up with a consistent labeling scheme and make liberal use of good, mnemonic labels, aiming the GOTOs at them instead of using relative jumps. This will also stop you from breaking the code when you add more lines in the middle of the loop, later. (Anyone else been bit by this one?)

They're a bit more expensive
Waah. They're worth it.

Labels: , , , , , , ,

2009-08-11

Digital blasphemy?

When is a 555 not a 555?

When it's really a PIC!

The design of the DrACo/Z80 computer calls for a 555 timer (a great piece of paleotech, even if it still isn't sure after 38 years whether it's really a digital or analog part.) However, for running higher-speed programs on the DrACo, I wanted to have the option to, as Emeril might say, "kick it up a notch." I figured that a PIC12F683 would do nicely, since it could be programmed to provide a nice, stable TTL clock output without any external components.

The only problem was, the PIC's ground lead is on Pin 8 and its power lead is on Pin 1; the 555 has the opposite configuration. The solution? Possibly the single ugliest soldering job I've done in a long time (and that's saying something, folks.)



Yet, it works -- and is a nice, drop-in replacement for the 555, swapping the astable oscillator mode for a much faster 2MHz clock output.

This is also the single shortest program I've ever written for a PIC:

   org 0x00
   banksel OSCCON
   movlw 0x70
   movwf OSCCON

   goto $


It sets the clock to 8MHz, then sits in a single-instruction loop and contemplates its navel. So, for that matter, when is a PIC not a PIC? When it's not doing any computing at all -- but just providing a 2MHz CLKOUT signal.

But sometimes, the smaller the MCU, the more creative the applications. I've heard of people using microcontrollers in fireworks to provide split-second timing for the pyrotechnics. At 30 cents each for something like the 10F200, it only sounds crazy until you think about it.

...and yeah, this project could have been very easily done with a less expensive PIC -- but 12F683s are so versatile that I keep a couple dozen of them on hand for various things. They're like digital LEGO...

Labels: , ,

2009-07-24

A step in the right direction

Well, that would explain it...

The reason that the quadruped robot was doing the "funky chicken" instead of walking smoothly was mostly due to a major bug in the gait angle calculations. The angle of the leg is the sum of the "shoulder" and "knee" joint angles -- but the angle commands to the knee servos weren't taking the shoulder angles into account. This caused a really inefficient, jerking motion, rather than the relatively smooth gait that was intended.

Oops.

It's fixed now (and has a new chassis and circuit board). The video shows it moving more-or-less in a straight line, but with a few modifications to the gait tables, it can turn, at least in shallow circles.

Next on the list:

  • Rubber tips for the feet

  • Improvements to the turning routines

  • Turn-in-place functionality

  • Reverse (basically, just run the gait tables in reverse order)

  • Wireless control

  • A second microcontroller to handle control and communications



Labels: , , , , ,

2009-07-15

Walker robot progress

Well, it moves. The gait algorithm is still handcoded instead of evolved (I'm working on finding a physics sim I can use for that), and is less than efficient -- but at least it's going in the right direction. The plan is for it to eventually be a hexapod -- maybe even with three degrees of freedom per leg -- but for now, it's a 2DOF quaduped. Once it's walking reasonably smoothly and efficiently, the plan is to make it remotely controllable and embed a video camera -- probably using XBeePro modules (from SparkFun, where else?).

The version in this video is still based on a cutaway plastic project box; it has since been moved to a proper chassis produced on the 3D printer. This doesn't yet do much for the gait, though.

More about the robot as the design evolves...

Labels: , , , , ,

2008-10-04

Z80 computer LED display

The Z80 computer now has its name in lights! (See video; I apologize in advance for the horrible low resolution and mp4 artifacts.)

Dr. Rosen and I got approval to buy an Alpha 215R single-line LED display as a demo for the Z80 computer. While it was on the way, I built a second peripheral for the Z80: a specialized RS232 port that accepts strings and reformats them for output to the LED display. (It's an interesting challenge to build a peripheral based solely on information from a datasheet, without having the actual device available.)

When I finally got to work with the display on Thursday, the interface actually worked -- but various electrical gremlins were causing all sorts of problems, when the Z80, interface, control panel, and sign were all connected. The problems eventually turned out to be mostly due to insufficient voltage -- as it turns out, 5.5V (rather hot for TTL circuits) at the power supply ends up being a nice, clean 5.0V to 5.1V at the Z80. Those 40-pin ribbon cables keep finding new ways in which to be a Dumb Idea(tm)...

At any rate, it started running very reliably Friday afternoon. The bad news was that this meant that it was time to start programming it. Writing Z80 code to do a demonstration script isn't too bad; a bit tedious to enter the strings, but still fun.

Toggling some 200 bytes of machine code in, byte by byte, however, is mind-numbingly boring -- even if (like me) you actually enjoy programming in assembler. (Note to self: there has GOT to be some source for knobs for those rotary switches SOMEWHERE!)

The script is running, though. Hopefully it will prove inspiring to the EET325 students. (How many college courses out there lead you through building your own computer, chip by chip and wire by wire?)

Labels: , , , , , , ,

2008-09-28

Much Ado About Nothing

Sometimes, nothing is exactly what you need. A specific amount of nothing, to be precise.

Here is a delay library, written in assembly, for PIC microcontrollers running at 8MHZ. If you find it useful, please let me know. Share and enjoy!

8MHz delay library

Labels: , , , , ,

2008-08-31

First peripheral



The first peripheral for the Z80 is working (although still somewhat alpha at this point): a two-line LCD text display. It's mapped as I/O ports 0x00 and 0x01, with control commands being sent to 0x00 and data to 0x01.

It's been tested with a "Hello, World!" program written in Z80 assembler. (The current version of the program is very inefficient; the ideal way to handle it would be to write the "Hello, World!" data into memory and then clock it out to the I/O port automatically (I believe the Z80 can do this in a single instruction, once the registers are set up.)

Here is the "Hello, World!" assembly code. The C register is loaded with 0x01, then the A register is loaded with the ASCII code for each character, which is output to the port.

In related news, I think I've found how to turn off all the peripherals on the PIC16F887. It's a good replacement for the '877A -- with an internal 8MHz clock, plus a complete 8-bit PORTA -- but it does seem to power up with a lot of extraneous analog options turned on. The MPU for the text display is an '887.

Edit: Here is an updated version of the "Hello, World!" app -- using a single OTIR (Output/Incrementing/Repeat) instruction to do the dirty work, once the registers are all updated. Apparently it works by not incrementing the program counter, so the same instruction is executed over and over until B counts down to zero. Whatever the mechanism, it works as advertised, and the program now takes up only 27 instead of 60 bytes of memory!

Labels: , , , , ,

2008-08-21

It works!

After a bit of debugging (some PIC code corrections, some VB code corrections, and finally tracking down a missing direction-pin wire), the Z80 computer is executing code! I wrote a short test program to compute Fibonacci numbers, and it ran correctly.

It still could use quite a few features -- such as an internal clear-all-memory routine, program save and load functionality, program trace output, etc. But for now, it's a working Z80 computer, accessed via RS232.

Labels: , , , , ,

2008-07-17

Plus ça change...

...plus ça ne change pas. Or so they say, anyway -- but the design of this computer has certainly changed. The changes are for the better, though: the core (which will be constructed by the students) has been greatly simplified, with as much functionality as possible having been collected into a control panel unit.

Here is the schematic for the "core." Some more minor changes may yet happen (perhaps another 74LS245 for the control lines etc), but the core design is essentially finalized. The idea is that the core system can run as a "headless" unit, without a control panel: with the addition of 24 LEDs to show the status of the address and data lines, it should look like a real "Hollywood" computer, complete with blinking lights! I'm still skeptical about putting LEDs directly onto the busses, but we'll see how that works out. If nothing else, they can be driven by three more '245s.

The control panel (still under construction) will include:
  • Rotary hex switches to enter addresses and data;
  • An LCD panel to read addresses and data (and perhaps other information);
  • Run/Stop, Single-Step, Manual/Auto, Reset, and Write switches;
  • An SD card slot and Load/Save switches to back up programs to an SD card; and
  • Three PIC microcontrollers to run all of this.
Right now, I'm creating a set of inter-MPU commands, to keep everything in sync. Complete details will be available here once it's all finished.

Labels: , , , , ,

2008-05-05

VSI gauge progress

The first CHComm-based instrument is starting to take shape. A Futaba servo connected to a PIC16F877a (yeah, I know -- gross overkill -- but it was handy) to make a VSI gauge. At this point, it works intermittently; it seems to only register changes of the hundreds' place; I really need to get an LCD on it to help diagnose what's going on.

On the bright side, it's up to 9600 baud, from 2400, so that helps.

Getting the PIC to properly multitask while communicating with the serial port is an interesting proposition. Right now, the main loop provides constant position pulses to the servo while polling the serial port line every 500us or so, to see if a start bit has been received. I'm leaning towards a 2-MCU solution for each gauge, though -- one to handle comms and one to run the servo. That way, the servo MCU would be guaranteed a very short data-update cycle, rather than allowing for a potential stoppage should something happen to the serial line. (Servos act strange when their position pulses are interrupted.)

Labels: , , , ,