Recently I bought an HC-SR04 Ultrasonic Range Sensor from Amazon.com. I thought this would be interesting and fun to play with. The first challenge was writing the code to interface with it. The range sensor is triggered by a pulse of at least 10us on the "trigger" line. The distance is provided as the length of the pulse on the "echo" line, which is actually the time it takes the ultrasonic sound to travel from the sensor to the object and bounce back to the sensor. Therefore the length of the pulse on the "echo" line represents twice the distance between the sensor and the object.
I am writing in assembly. The biggest constraint is that I do not have floating point. Also, the PIC module that will measure the pulse width is dependent on the oscillator speed. So I need to account for that in the conversion. I know that the pulse width will be a 16-bit value. I use the 16/16 divide routine from Microchip's math library to handle that.
As for goals, I want the code to detect error conditions (such as a missing or broken sensor) and indicate out-of-range. I also want to be able to specify an arbitrary "unit", besides inches and centimeters. The conversion should be done in one step: directly from pulse width to desired units.
The distance to the object is half the length of the pulse, measured in seconds, multiplied by the speed of sound, 340.29 meters/sec:
d{m} = t{s} * 340.29{m/s} * 1/2
This is all well and good if you have floating point variables and math and can measure in microseconds, but I am writing in assembly on a PIC and will be using the Capture/Compare/PWM (CCP) module.
The CCP module uses the oscillator to measure time. The base rate is Fosc/4. This rate can be scaled by the prescaler, which can be 1:1 (no prescale), 2:1, 4:1 or 8:1. I am going to use the CCP capture to detect the rising edge of the echo pulse and then the falling edge. The capture module captures the time of the "event" by copying the current TMR1 value to the capture registers. This time is in what I am going to call "ticks". The duration, in seconds, of a "tick" is (prescale * 4 / Fosc), where Fosc is in Hz. To convert ticks to seconds, we can use
t{s} = n * prescale * 4 / Fosc{Hz}
It is a lot more convenient to work in microseconds (us). To rewrite this to give the time in microseconds, we only needs to change the Hz to MHz:
t{us} = n * prescale * 4 / Fosc{Mhz}
If we are working in microseconds, then we need to convert the speed of sound the same way. Also, it helps to change from meters to millimeters, and to round the 340.29 to 340
340.29{m/s} = 340 * 1000 {mm}/ 1000000 {us} = 34{mm}/100 {us}
Now, we can take the ticks we get from the capture module and convert this to millimeters:
d{mm} = n * prescale * 4 / (Fosc{MHz} ) * (1/2) * 34{mm}/100{us}
Finally, we can convert the millimeters to any units that we need. For inches we could multiply by 10{in}/254{mm}. For centimeters, it would be 1{cm}/10{mm}. Note that we select values that are integers. We generalize this with
N_Units{u}/M_MM{mm}
The final equation, before simplifying is
d{u} = n * prescale * 4 / (Fosc{MHz} ) * (1/2) * 34{mm}/100{us} * N_units{u} / M_MM{mm}
We can rearrange this and get
d{u} = n * (17 * prescale * N_units)/(25 * Fosc * M_MM)
The only problem remaining is that the conversion factor (everything after n) is less than zero. Quite a bit less than zero. The easy way to solve this is to divide by the reciprocal:
d{u} = n / ((25 * Fosc * M_MM)/(17 * prescale * N_units))
So now we have a way to calculate a divisor that can convert our pulse width into a distance.
The HCSR04_MeasureDistance routine does the following:
The code uses a register labelled HCSR04_STATUS to track if there was an error, if the result is out-of-bounds, and to pass a rising/falling edge flag.
I wanted to have some error checking in the code. That is, I did not want to assume that everything would work every time. This is why I check to make sure that the input (echo) line is low before starting. If it is not low, then there is no way to even take a measurement. If there is a problem, then I set the "error" bit in my HCSR04_STATUS register. This error is "returned" to the caller. That is, the HCSR04_STATUS_Error bit of HCSR04_STATUS register can be tested to determine if an error occurred.
To trigger the sensor, I bring up the trigger line, delay for at least 10us, and then bring the line down. I have only tested this with a 20MHz clock (ceramic resonator). I expect it will work at slower clock speeds.
I use the capture module to monitor for the rising and falling edges. This is done with HCSR04_CaptureEchoTransition. This routine sets up the CCP to in capture mode and tells it to look for the either the next rising or falling transition. The caller sets the HCSR04_STATUS_Rising bit of the HCSR04_STATUS register to indicate which edge to look for. This routine resets the timer to zero before starting the capture. The HCSR_MonitorEchoEnd copies the CCPR1L/H registers to the HCSR04_WIDTH_L/H registers if the capture was successful. Note that the HCSR04_CaptureEchoTransition also monitors the TMR1. If the timer rolls-over, then this is considered an error condition and sets the HCSR04_STATUS_Error bit.
To compute the distance, I divide the pulse width by the "divisor" described above. I used the Microchip math library described in application note AN526. The final value is placed in HCSR04_DIST_L/H registers.
I just learned about the TLC5940 from Texas Instruments. They are available at SparkFun. Normally I like to do everything myself, including write my own multi-channel PWM code for the Microchip PICs. But, at some point, I am just going to want to control the brightness of a lot of LEDs. This chip looks like it will do that just fine.
From the TI website:
The TLC5940 is a 16-channel, constant-current sink LED driver. Each channel has an individually adjustable 4096-step grayscale PWM brightness control and a 64-step, constant-current sink (dot correction). The dot correction adjusts the brightness variations between LED channels and other LED drivers. The dot correction data is stored in an integrated EEPROM. Both grayscale control and dot correction are accessible via a serial interface. A single external resistor sets the maximum current value of all 16 channels.
This looks like a fun chip to play with. What do I consider the nicest feature of this chip? No resistors and not transistors needed. This chip can directly drive the LEDs.
A comment on the SparkFun pointed out that TI offers free samples. So that is what I did -- headed to the TI website, created an account, and requested three TL5940 chips.
I will put up another post on this topic once I actually do something with them.
I have been intrigued by the idea of using UV LEDs to "write" on a phosphorescent surface ever since I saw this posted on the Make: blog. I already had a bunch of UV LEDs that I received when I thought I was ordering purple LEDs. And I previously built one of my POV boards with these UV LEDs. It worked great when swiping across the various glow-in-the-dark material samples I had been collected over the years. [Tokyu Hands in Shibuya, Tokyo, has a great selection.] But instead of me moving the LEDs, I wanted to move the vinyl. And I wanted to find an interesting way to do this.
A few months ago I purchased a roll of phosphorescent vinyl from H&H Sign Supply. One of the challenges is that the vinyl is not really that flexible. It is flexible enough to apply (it has an adhesive back), but you would not want to continuously bend it. This means a "conveyor belt" approach would probably wear out or be problematic in one way or another.
One day it dawned on me to use a ring. With a ring I could suspend it on the shaft of a motor. And a ring with a large diameter would give me a long surface. This length would give the glow time to fade before it comes around again.
So I took a 24" square of the vinyl and used a string and a pencil to trace two circles. I planned on sticking the vinyl on a piece of posterboard I had, but the posterboard was 22". So the outer diameter was 22" and the inner diameter was 14". The vinyl actually has a metal backing to reflect the glow, but this made it a little difficult to cut cleanly around the circle. Normally that would not matter too much, but you might notice in the video where the circle slows down. This is at the spot where there is a bump in the circle.
I had a motor from a cassette player I took apart long ago. Conveniently this motor had both a pulley on the shaft and two mounting screws attached to the motor. I drilled a hole in a 24"x12" board for the motor pulley and two more holes for the screws. (It may look like I planned to have a 22" circle on a 24" board, but that was just luck.) I attached the motor to the board so that pulley was coming through the hole and was now in front of the board.
On the front of the board I mounted the UV POV board I made. This was the board I made earlier for my hand-held UV POV tests. To mount it, I removed one of the little legs and used a spacer on the top of the board to give me the distance I needed. The goal was to get the LEDs close enough to the vinyl but press against it. Otherwise it would affect the rotation. Thankfully the board was light enough that only a single mounting screw was needed.
The board and the motor are powered separately. For the first attempt, I used two AA batteries to power the motor. This made the ring turn a little two fast. The meant that the glow was not fading enough before the ring made a full rotation. In the Flickr set there is a picture where you might be able to see this. (Also you notice that the images are closer together.) In the second attempt I used only a single AA battery. This made the motor turn rather slowly, which was what I wanted.
The board uses a PIC16F628 and a dozen LEDs. For the current tests, only 8 LEDs are used. The board is powered with 3 AAA batteries. In the picture below you can see a binder clip. This is keeping the battery holder from falling to the floor.
The designs are all created using the Animator LED Animation Application I wrote some time ago.
In the first test I thought the images were too close, and it was hard to figure out what was what. I used the editor to add space after each of the images. I also modified the program to run at about a third of the speed. Before I did this all of the glowing images were too skinny, since the ring was turning too slowly.
Overall I am pleased with the outcome. This was just my first prototype. Ideally I would like to find a way to build a more permanent version, especially one where I could display messages. Also it would be useful to control the motor with the microcontroller.
View all of the photos on Flicker TheVaporTrail
As covered on Hack A Day
Looking for something to do with the thousand LEDs I bought from Electronic Goldmine, I decided to make a small pyramid. It reminds me of the Sol LeWitt sculpture, but on a much smaller scale. The LEDs are nearly cubes, so they stack nicely. The twenty-five LEDs in the pyramid are wired as a 5x5 matrix, and each LED can be controlled individually using a PIC16F648. I also extended Animator, my animation editor application, so it can be used to edit the animations.
Details and downloads available on TheVaporTrail website.
Larger photos available on Flickr
The other day I bought a bag of one thousand green LEDs from Electronic Goldmine. The bag was sitting on my desk and every once in a while I would grab some of the LEDs and wonder what to do with them. I was arranging them in a perf board (from Radio Shack), when I thought it would be nice to arrange them in a diamond. This would highlight what I thought was the most interesting aspect of these LEDs, that view looking at the corner. The problem, for me, was that I did not feel adding a wire to each LED so I could plug each LED into a breadboard.
Read more and see the pictures on TheVaporTrail site or at Flickr
Some time last year ThinkGeek gave HacDC the left over neon digits that never sold. They used to carry a 'DIY Neon Kit', similar to this one, but they discontinued it. The digits did not come with any power supplies, just bags of digits, and perhaps a 'Q' or two. I had been wondering for some time about how to light them up.
I know nothing of neon or fluorescent lighting, both of which require transformers or inverters of some sort. A friend at HacDC mentioned that he was able to use an inverter that he bought from Electronic Goldmine, until it burnt out. From what I understood trying to light the neon digit might have been too much for it. I was still interested, but found out that Electronic Goldmine no longer carried it. I had seen what looked like a similar part ("5V Input inverter CCFL (Cold cathode Fluorescent Lighting) Back-lit graphic module ES5843") at Excess Solutions, which was really what started this renewed interest. I was not sure I wanted to spend $25 (the minimum order amount) at Excess Solutions, and I missed the opportunity to get a driver/inverter from Electronic Goldmine. Imagine my surprise when I opened a drawer in my office/workshop and found I already had an inverter, and had actually ordered it over six years ago from, wait for it, Electronic Goldmine! And, not just one, but four. I wasted no time grabbing a few wires with alligator clips giving it a try. It worked! Although not completely -- the digit did not fully light up -- but that was still fine with me.
Tonight I decided to try various combinations of digits (that is, series or parallel) and to open the cases to get out the neon. You can find all of the pictures on Flickr.
A few months ago I found some web pages describing the physical and signalling protocols between a PS/2 mouse and a PC. It was a lot simpler than I expected. Even though there are six pins on a PS/2 mouse, only two of them are part of the protocol: one for clock and one for data. "Data and clock? That's all?" I thought. So I decided to write an program for a PIC so it could communicate with a mouse.
When I was done I had a set of PS/2 communications routines and a small program running on a PIC16F726 that worked with a PS/2 mouse plugged into a breadboard. I can move a "cursor" and toggle elements on the 9x11 LED display. I can clear the display using the right button and load a pattern by pressing the right and left buttons at the same time. Also, the application can detect when the mouse is unplugged.
Tonight I took it one step further and added a USB-to-PS/2 adapter, and then plugged in a Bluetooth-to-USB dongle with a Bluetooth mouse. This worked, much to my surprise, especially since the USB mouse I tried did not work.
Update on Aug 12, 2011: Fixed PIC chip name. Chip is actually PIC16F726, and not the PIC16F648.
Downloads are available at the bottom of the project page.
Larger photos available on Flickr
The Electronic Goldmine email arrived a few weeks ago, and one of the items was a bag of one thousand LEDs (part G18232: Square Green LEDs (Factory Bag of 1000)). The price, at the time, was $15.00, on 1.5 cents per LED. I thought, "what a great price!" and "I'm sure I'll think of something to do with them."
So, after a few days of not being able to convince myself I didn't need them, I ordered them. They arrived in a bag just like the one on website. Wow! One thousand LEDs! (More pictures on Flickr)
OK, now what do I do with them? I guess I have to think about that some.
One thing I notice is that they look the most interesting viewed looking down from the corner, like in the picture at the left. You can see both the glow through the top, and the die glowing through two sides.
Recently, the item has been on sale for $9.95, but I really don't need any more.
TL;DR: Don't enable the 'brown-out reset' configuration bit if you are using the PIC at less than 5 volts.
I have been playing with Microchip PICs for a number of years. Mostly I use something in the PIC16F628 family. At some point I ordered the LF (low voltage) version, but I still used them with either a 5v power supply or with 3 AA batteries. Every once in a while, since I had the LF version, I would try to use 2 AA batteries or even a 3v coin cell, but the circuit never worked. I wondered why, but I never gave it too much thought. Yet it still nagged me. The specs said the chip should work down to 2 volts, but it was not working for me.
The other night I finally sat down to try it again. This time, instead of starting with a complete project designed for 5 volts, I decided to start with a blank chip, blank project, and blank bread board. I copied the header from one of my other projects, since it set-up the chip and configuration I usually use, and wrote a little code to blink one LED. I programmed the chip, added an LED with a 10 ohm resistor and added a CR2032 coin cell -- and nothing happened. I double checked the wiring, but did not find anything wrong. I removed the battery, then set the programmer to power the chip at 3 volts (thinking that the coin cell did not have enough power) and reconnected it to the breadboard. Still nothing.
I thought that maybe I wired the circuit wrong or had a problem with the program. To rule out these possibilities, I decided to power the circuit at 5 volts (and use a larger resistor). I found a few problems and fixed them. Now the LED was blinking. Again I tried the project at 3 volts, and again, nothing.
When all else fails, double-check the spec. So now I look at the spec and check the chart that shows the supported voltage range. It says it works as low as 2 volts, but my LED is not blinking. I pull out the oscilloscope. Maybe, I think, that the port pin is going high and low, but does not have enough current to drive the LED. The scope shows nothing. I try the circuit at 5 volts again. The LED works and, of course, I see the trace on the scope. I switch back to 3 volts and nothing.
At this point I am really wondering what the problem could be. I start looking through the spec again, but there is no mention of anything special regarding using the chip at 3 volts. I check the settings in MPLAB, the Microchip development environment, and I do not find anything about configuring a chip for low voltage usage. I look at the configuration bits I have set in the program. None of them seem to be related to my problem. There is something called 'low voltage programming', but, I figured, this is not a programming problem. I do not think it is the watchdog timeout.
I notice that I have 'brown out reset' (BOD) enabled. In the spec, there is no mention of a dependency or relationship between the brown out reset voltage and the chip voltage. What I mean is that there is not a sentence that says "do not use brown-out reset for low voltages" or "the brown-out reset voltage level is 80% of the supply voltage". I continue to read the spec hoping to find something useful, something that explains why I can't run the chip at 3v.
Somewhere I notice the term V[BOR], which I take to mean "voltage: brown out reset". In particular, the sentence say "If V[DD] falls below V[BOR] ... the brown-out situation will reset the chip." I also notice the condition in one of tables describing a brown out event that says V[DD] < V[BOR]. There is also a diagram showing the trigger of a brown-own reset when V[DD] dips below V[BOR]. All of a sudden V[BOR] seems important.
A little more searching and I find that V[BOR] is typically 4.0v. I wonder, how can the chip even work if the brown-out reset voltage is 4v, but the power supply is 3v? The answer is, of course it can not. This is why the chip never works for me on 3v. I have the brown-out detect configuration bit set.
I disable the brown-out detect bit and reprogram the chip. All of a sudden, it just works. I can now run the chip on a coin cell.
I realize that I always "brought this problem with me" from project to project because I always copied the top part of the assembly file from one project to another, just as I did for this test project. Some time ago, for some project, I must have thought that I needed the brown-out reset enabled, and it has been set since then.
One more note on this: For some reason the PicKit 2 programmer has a problem programming the PIC16LF648a when the programmer voltage is set to 3v. I don't have a solution to this, just a work-around. I set the voltage of the PicKit 2 to 5v and unplug the LEDs when I am programming the chip.