What you're going to need
• 1 pcs. - plastic enclosure, reuse an old multi-meter casing, or buy one like mine
• 1 pcs. - arduino, with all of its pins used, but with an active I²C bus - I borrowed an Arduino UNO
• 1 pcs. - ultrasonic ranging module - mine is HC-SR04
• 2 pcs. - PIC with MSSP module - I will use PIC12LF1840T39A
• 1 pcs. - common cathode display driver - as I already used this in other projects, I chose MAX7219
• 1 pcs. - common cathode display - Just pick one randomly, the one used by me is similar to this one
• 2 pcs. - right angled tactile switches - I de-soldered a few like these from an old CD-ROM
• 6 pcs. - 4.7 K resistors for pulling up the I²C lines, tactile switch inputs, and display driver current set, like these
• 1 pcs. - PC with MPLAB and Altium on it - I own a dell inspiron N5110 laptop
• 1 pcs. - Laser printer, I have a Brother HL2132
• 1 pcs. - Copper board that's large enough to fit your design
• 1 pcs. - 5 V low-dropout regulator, I used LM7805 in TO-252 package
• X pcs. - FeCl etchant, laser printer, iron, solder, soldering iron, drill, pliers, screwdrivers, screws
It means a great help to have
• A caliper - I have the old-school version without digital display
• A logic analyzer - mine is Scanalogic 2
• Steady hands, ambition - I think I got both :)
• A cup of tea - I drink English Breakfast
Extra components needed because of the 3.3 V - 5 V compatibility issue you will read about later
• 1 pcs. - bipolar transistor, I used BCR562 which has in-built bias resistors
• 1 pcs. - 10 K resistor to pull up the TRIG line
• 1 pcs. - 3.3 V zener diode
• 1 pcs. - 1 K current limiting resistor for the zener
• 1 pcs. - 3.3 V low-dropout regulator, like this
Step 1: Why Would One Build Such a Device?
We already had a motor controller, an LCD, a joystick and a Bluetooth transmitter hooked to the Arduino, but needed two more for opto-sensor feedback from the wheels. That left us with no pins for the ultrasonic ranging module. I think "no pins left" is the recurring sentence in every Arduino programmers nightmare, which - in most of the cases - leads them to surrender a functionality for the sake of another, or to abandon the project.
I was asked to design I²C interface for the HC-SR04, allowing the Arduino to ask for distance measurements via this two wire interface.
First thing I am going to show you is how to make an I²C sensor out of the HC-SR04. Once I'm done with that, I will go on with putting it into a custom project of mine: an ultrasonic measuring tape. My dad is an electrician, he needs precise measurements while working - my little project might be helping him with that.
There's probably going to be a lot of interesting ideas of where this sensor could be put, if you have a good one, share it!
Step 2: Designing a Board in Altium Designer
How to draw the board shape into Altium Designer
When you create a PCB file in Altium Designer, it opens with a random PCB size, which - I am sure - is bigger than we need it to be. To size a board precisely, I will use lines ('P' then 'L') after I selected the keep-out layer tab down at the bottom of the Altium window. I draw the first two, then double click each of them, and give them the values I just measured. Then I draw two lines again, this time there's no need to re-size them, you just have to start at the end of the two lines you already have. You can double check the lengths by pressing 'Ctrl' + 'M', and clicking on the edges.
Press 'Ctrl' + 'A' to select everything, then go to 'Design' -> 'Board shape' -> 'Define from selected objects'. This will snap the board limits to the lines you just defined on the keep-out layer. Use 'ALT + D' -> 'S' -> 'D' to do this quickly.
I measured the distance of the hole from the edges, and placed them on the PCB as multi-layer pads.
I shot a short movie clip that presents the definition of a board shape, nothing special, just a basic 80 x 25 mm board.
Editing libraries to have custom components in them
In case anyone decides to use a component that isn't in his schematic or/nor footprint libraries, I made a short instructional screen snip, in which I add a BTM-112 module to my libraries. You can see how the schematic symbol and land pattern is built up based on the datasheet specs. I usually group the pins that belong together (power rails, ports, USB, UART, ... ), when making the schematic symbol. It simplifies my schematic later on.
I suggest to use a snap grid, and pin lengths (schematic symbol) of 2.54 mm, to get all the symbol edges and pin-ends on grid.
I can't speak of this too closely, each schematic has its unique characteristics, components, buses, problems. Simple enough, placing a symbol is done by pressing 'P' -> 'P', which stands for Place Part. Then you have to dig down to your libraries to find the component you wish to add to the schematic file. Add them all, then press 'P' -> 'W' to Place Wires. To send the changes over to the PCB file, go to 'Design' -> 'Update PCB'.
Routing the wires and finishing the design phase
This is another thing you need to figure out yourself, the main key combo in PCB mode is 'P' -> 'T', this highlights the nets, making them easier to see. In addition, they move the un-routed line along, as you proceed with placing track segments. Do this for all your nets, then run an eventual design rule check, to look for design mistakes. Going into the design rules is way beyond the scope of my instructable, so I'll leave that out. If you have little or no knowledge about them, just use the DRC for checking against un-routed nets.
Create an Output Job file, and proceed with the settings explained here, to get a PDF file with your layout on it!
Cleaning a PCB and printing the artwork
Check out these photos I took before making some PCB-s for this, and some other projects:
These are two random boards I decided to etch for the sake of making an example. Both are break-out boards, one is for a school project (the SD card shaped), the other is the bluetooth module we just made a footprint + schematic symbol for. Through this instructable I already won myself a bluetooth module. I built it into a ball balancing robot, that will get its own instructable guide as soon as it starts up! Back to our stuff:
Next step is to put this into FeCl, to take off the excess copper. Then the toner needs to be washed off, the board has to be cut to size. Check these images to see my "end products".
An SD card shaped PCB, meant to replace the IMP from my motor controller board. There is a pin header on the bottom layer, I am using jumper cables to connect it with the rest of my board. This could have been avoided, since I did have I²C brought out on pins on the motor control board itself, but I wanted to take advantage of the on-board 3.3V/1A buck. Through this break-out board I can reach the 3.3V power rail, I am using it to power the rest of the peripherals: bluetooth module, gyro, pull-up resistors.
The BTM-112 soldered on-board. You can see how much solder got on the left side? It is because I used solder wider in diameter, and wasn't able to ration it well. The right side was soldered with solder of 0.3 mm in diameter, that side looks way better, not to mention the solder joints, which sit better on the pads.
Step 3: Sensor Board Manufacturing Process
Drawing the schematics
Once we're done with the definition of this board size constraint, a schematic must me drawn. Mine looks as simple as it can get, just the PIC, the headers and a pull-up resistor on the MCLR pin. You might experience problems with programming, when this resistor is in place, if you do, just take it off and disable MCLR function on the reset pin.
You'll have to install pull-ups too, if this is the only I²C device you use. In my case, I already had the gyroscope on the bus, which has on-board pull-up resistors - I didn't had to worry about that.
If schematics are all done, go to 'Design' -> 'Update PCB document ....' to make the components visible in the PCB file. Arrange them as you wish, I just threw them on, and aligned them to the lines I drew on the keep-out layer, so they get centered perfectly. You can do this by selecting both, and pressing the 'A' key. A menu appears, which allows you to align horizontally or vertically the selected components. Click the operation you want, then click the component you wish to hold as a reference - in my case: the line on the keep-out layer. If everything's arranged, just route your signals, put on polygon pours.
Altium Designer offers a great tool for visualizing the board while you're working on it. 3-D view is activated by pressing '3', one can switch back to 2-D view by pressing '2'. My board looks like this in the 3-D Altium rendering:
Exporting to PDF
If you wish, you can export your layout to PDF. Don't forget to do a mirroring on top layer, if you use toner transfer method! I already wrote a walk-through about this in one of my previous instructables.
Here's what I got when I exported my layout into PDF:
The text is mirrored, because all this goes onto the top layer.
I used toner transfer method to make this board,(like most of my boards in the past few months) it's way quicker than the UV exposure method. The UV method is something I only use when it's absolutely necessary, when the traces are very thin or the pads are super small.
Long story short - get the toner to stick to the PCB copper side, then wait until it cools off. Go to the sink, put the paper+board under water, wait until the paper gets soaked, then gently remove the paper. There's going to be some paper left on the board, you can rub those off with your finger. Put your board in ferric chloride, and wait until the copper is eaten away, leaving the board with the tracks, pads and the keep-out lines only. Then take some nail polish solvent, and wash off the toner. You will have the finished PCB in your hands, which only needs drilling, and cutting, eventually. Here's what mine looks like.
Cut the board to size, then drill the holes. I drill 0.7-0.8 holes first, then go over those which need to be wider (pin headers, in this case). Give the PCB another sanding, then clean it of with water, or solvent. What is a good idea at this point, is to take off the right-angle pin header that's mounted on the HC-SR04 by default, and replace it with a simple vertical one. This way the board you made will get right behind, and in line with the HC-SR04 ultrasonic ranging board.
Populate your board with the components: headers, PIC, HC-SR04. You will have a sensor board that is ready to be programmed! Here's what mine looks like now, that I'm finished - from the top:
the bottom side of my sensor looks like this:
We have the hardware ready, next step is to clarify the things we will have to do on software side. This leads us to the biggest (not too big, though) problem of ours: enabling the PIC to talk through I²C.
Step 4: A Few Words About I²C Bus
Brief description of the bus
The I²C bus is an 8 or 10 bit, bidirectional serial communication protocol, having transfer rates of 100 KHz, 400 KHz, or recently 1 MHz (Fast mode plus). The bus consists of two bidirectional signals (usually named SCL - serial clock, SDA - serial data). These signals connect to every I²C device in our circuit we wish to communicate with.
Thhe SDA line is used for serial data transfer, the SCL line assures that the communication is in sync, in both directions. Besides these two, we also need the I²C master and the I²C slaves to share a common ground.
The pins of the devices connected to an I²C bus are open-collector, or open-drain. This means, that they can bring the floating communication line to the ground, and they can release it to rise to the level to which they're pulled up.
It is very important to understand, that the term 'release' doesn't mean a '1', it only means, that the master or slave isn't driving the communication line. Having said that, we need to make sure, that the line isn't getting low, when it isn't supposed to. This can be done by installing a pull-up to the power rail of the circuit. We don't have to (or must not) put a pull-up to every slave in the circuit, that would mean a parallel connection, which - if too many resistors were installed - wouldn't allow the pin to bring the line down.
The value of the resistor used doesn't need to be a given value, usually a few k resistor will work fine.
The I²C protocol is master-slave based. A master decides when the slave devices communicate, through some data sent on the I²C bus. In our case, the PIC needs to be configured as a slave, since the Arduino will be the Master in the circuit. A slave is usually a sensor, a memory chip, or anything that is able to extend the features of a microcontroller.
All transmission are triggered by the master, the slaves don't communicate anything, unless the master is asking them to do so. Correct I²C bus operation assumes, that a single slave address is recognized only by a single slave. When a slave is selected, all other slaves must remain silent.
The master is responsible of all kind of timings - in most systems (single-master) the clock line is manipulated only by the master. This means, the speed of the transmission is dictated by the I²C master of the circuit.
• Whenever a master wishes to start a transmission, it executes a start sequence on the I²C bus. A start condition occurs, when the SDA line is brought to low level while the SCL line is high.
• When the master wants to signal, that the transmission is over, it sends a stop condition, which is a rising edge on SDA while the SCL is high.
• Data gets transmitted in the time-frame between these two events. For a data bit to be valid, the SDA must not change value, while the SCL is high. If it does, the bit sequence is misinterpreted as a stop or start condition, and leads to communication failure.
In my case, data transmission is done in 8 bit groups. The data - following the big endian pattern - is clocked out with the most significant bit going first. After every 8-th bit gets clocked out, the slave must signal back for the transfer to be considered a successful data exchange. This signal is called an 'acknowledge', and it assumes, that the slave brings the SDA line down at the 9-th clock signal on the SCL line.
As I mentioned before, each slave must have a unique slave address. This slave address is 7 bits long, meaning, that a total of 128 devices can be attached to the I²C bus, without any kind of tampering with the circuit.
When it comes to addressing, the master sends out 8 bits, 7 of which are the slave address bits, the last one determining whether the master wishes to read or write in the slave devices internal registers. This last bit is known as the R/W bit, a '0' on this bitfield means a write, a '1' on this bitfield means a read.
For example, if one wishes to write to a slave device with a slave address of 0b1010000, he must send 0b1010000 after the first start condition. If a master wishes to read from the same device, the first byte after the start condition must be 0b10100001. These, turned into hexadecimal give 0xA0 and 0xA1. Datasheets normally give the slave address in hexadecimal format.
In some of the cases, the manufacturer allows the user to add more of the same chip to the I²C bus: this is achieved by allowing the user to control a number of slave address bits. For example, an I²C EEPROM usually has the lower 3 bits of its slave address brought out on pins. Pulling these high means a '1' at the corresponding in the slave address byte, pulling them to ground means a '0' at that location.
There are situations, where the master is just too fast for the slave, it wants to read faster, than the slave could handle. To avoid these kind of situations, the I²C slave device manufacturers can build in a feature, that allows the slave to pull the SCL line low, while they're busy executing the commands of the master. When the slave is done with measuring, reading, sensing, etc. - it releases the SCL line, allowing the pull-ups to take the line high. The master will not initiate another transfer, unless it sees the SCL line high. This is often referred to as 'clock stretching'.
Example of a START condition followed by a read command from slave 0xA3. The read operation is valid, we now this, from the ACK (acknowledge) signal sent by the slave.
Unfortunately I wasn't able to capture clock stretching phases, but if I see one while working with the PIC I²C, I will make sure to show it to the readers.
So, we just learned this and that about the serial communications protocol we will use, now let's see it in action. First let me present you an example board, a PC tool and a microcontroller program.
Step 5: Eight EEPROM Chips on a Single I²C Bus? How?
I have a couple of 24LC128t EEPROM circuits in my drawers, I will put aside the main task a bit, and build up a test board, so I can show you some examples, waveform snips, behaviors. The processor which will read and write them will be an electric IMP, tucked in an April dev-board.
The datasheet of the EEPROM circuits I will be using can be found here.
As you can see on the pin diagrams, it has 3 of its slave addres bits brought out on pins 1, 2 and 3. If we connect A0 and A1 to ground, A2 to 5V, and look at Figure 5-1 of the dataheet, you can see how the first four bits are hard coded to 0101, the rest are controlled by the A0, A1, and A2 pins. If I connect the pins the way I said before, my slave address will be b1010100. The 8-th bit is the R/W bit.
Having three freely modifiable bits in the slave address allows you to connect 8 EEPROMS to the same bus without any conflicts. That means 8 times 16 = 128 kilobytes storage space. You can store variables, configuration words, images, anything you want. Our memory chip addresses will range from 0xA0 to 0xA7.
Interfacing other components is an easy stuff to do, once you have your I²C routines all worked out, working well. The datasheet always lists the registers that can be written or read. The same way you read from an EEPROM, you can read data from an I²C accelerometer, current measurement circuit and so on.
Designing and building a prototype/test board for the EEPROM test
Of course, I used Altium to design the board, then exported the layout as said before, ironed the artwork to a copper board, washed off the paper, threw the board into ferric chloride, sawed it, piled it and drilled it.
To make the interfacing easy, I used the exact dimensions of the April board, so my test board can snap into it real easy. Power is taken from the 500 mA DC-DC converter from the April, the SDA and SCL lines were traced to the I²C pins of the IMP. Since April is open source, the cool guys from the electric IMP uploaded the PCB files, I turned that into a step model, and included it in my board design - only on mechanical layer.
The layout spaghetti wasn't hard to be routed, it took me about 30 minutes.
Paper starts to soak in some water, it is this point, where I start to rub off paper.
Communicating with the monster memory we just created
Using the specific bit patterns with specific timings will allow you to access the memory of these I²C EEPROM-s with read or write procedures.
• If one desires to write data to a 24LC128t, his algorithm should follow this pattern:
1 - send start condition
2 - send slave address, with the 8-th bit low (signaling write) - 0xA8
3 - send slave register address, this is the "place" we want our data byte to be written. All register addresses and their names can be found in the device datasheet, along with their purpose.
4 - send the byte we wish to write on the address we specified before
5 - send stop condition
• If one desires to read from the 24LC128t, he/she should do something like this:
1 - send start condition
2 - send slave address, with the 8-th bit low (signaling write) - 0xA8
4 - send slave register address, from which data shall be read
5 - send start condition (restart)
6 - send slave address, with the 8-th bit high (signaling read) - 0xA9
7 - read data from bus
8 - send stop condition
I recorded some critical waveform segments, check them out! Everything is working as specified in the I²C specification.
Here you can see a read from 0x00FF address of the third EEPROM chip with slave address 0xA2. The returned data is 0xFF.
Let's see how I²C relates to our project in the next step.
Step 6: Configuring the PIC As an I²C Slave
In our case, we will need to configure the PIC12LF1840T39A to act as a slave device. First thing, that needs to be done, is to configure the MSSP module. The datasheet describes this procedure in detail, as well as the transactions.
1. Start bit detected.
2. S bit of SSP1STAT is set; SSP1IF is set if interrupt on Start detect is enabled.
3. Matching address with R/W bit clear is received.
4. The slave pulls SDA low sending an ACK to the master, and sets SSP1IF bit.
5. Software clears the SSP1IF bit.
6. Software reads received address from SSP1BUF clearing the BF flag.
7. If SEN = 1; Slave software sets CKP bit to release the SCL line.
8. The master clocks out a data byte.
9. Slave drives SDA low sending an ACK to the master, and sets SSP1IF bit.
10. Software clears SSP1IF.
11. Software reads the received byte from SSP1BUF clearing BF.
12. Steps 8-12 are repeated for all received bytes from the Master.
13. Master sends Stop condition, setting P bit of SSP1STAT, and the bus goes Idle.
Let's start writing our program. Aside of our usual configurations, like oscillator configuration, we will have to give values to the MSSP registers:
SSP1STAT = 0x80
1 = Slew rate control disabled for Standard Speed mode (100 kHz and 1 MHz)
0 = Disable SMBus specific inputs
0 = Indicates that the last byte received or transmitted was address
0 = Stop bit was not detected last
0 = Start bit was not detected last
0 = Write
0 = Address does not need to be updated
0 = Data transmit complete (does not include the ACK and Stop bits), SSP1BUF is empty
SSP1CON1 = 0x26
0 = No collision
0 = No overflow
1 = Enables the serial port and configures the SDA and SCL pins as the source of the serial port pins
1 = Disable clock
0110 = I2C Slave mode, 7-bit address
SSP1CON2 = 0x00
0 = General call address disabled
0 = Acknowledge was received
0 = Acknowledge
0 = Acknowledge sequence Idle
0 = Receive Idle
0 = Stop condition Idle
0 = Repeated Start condition Idle
0 = Clock stretching is disabled
SSP1ADD = 0x88
1000100 = 7-bit slave address
0 = Unused
SSP1MSK = 0x0F
Slave address masking, don't look for match in the first four bits
So these are the settings we need to apply to our PIC to make it work as we expect. This all goes into the initialization routine, before our infinite loop.
Once this is done, we have to write our write, read, acknowledge, negative acknowledge, idle, start and stop routines. It's always a nice thing to build something from scratch, but if someone worked with I²C before, and already has his favorite/optimized routines - just use those. I followed the instructions from the datasheet, even though I had already written I²C functions a while ago. I started from scratch for the sake of the instructable.
Step 7: Implementing the I²C Routines
It is very important to remember, that we're talking about a slave device here, let's keep that in mind.
If you decide to write functions for this, these are the functions you should implement.
void AckI2C( void )
This function is responsible of setting the ACK bit, and sending it. Acknowledge is sent for every byte, that was successfully received by our slave
void IdleI2C( void )
Makes the bus go idle
void CloseI2C( void )
Closes the I²C bus, no further transmission is made, not until an OpenI2C() is called
unsigned char ReadI2C( void )
Reads the bus, and returns the value it receives
unsigned char DataRdyI2C( void )
Signals a complete data transaction
unsigned char WriteI2C( unsigned char )
Writes a byte to the I²C bus
void OpenI2C( unsigned char, unsigned char )
Opens and configures the MSSP module for I²C communication
As I already said, I will be using the datasheet brief to make this work: I enable SSP1 interrupt, and treat everything I²C related in the interrupt service routine. Since this application isn't time critical, this won't give us too much problems. Check out my interrupt service routine that handles the I²C communication:
I made some communication snips with my logic analyzer:
Being happy with seeing this work, I sent the number 6500 through I²C to the other PIC, slashed into two pieces.
6500 is 1964h in hexadecimal, this is what we should see when the progressbar fills up. And it's a match!
This done, we have a PIC, that responds to I²C commands. The new peripheral which we just created will be used in our ultrasonic measurement tape.
Step 8: The PIC and the Ultrasonic Sensor
The HC-SR04 has four pins:
GND - common ground
This pin connects to the ground pins of the schematic
VCC - power rail
This pin must be tied to the power line of our circuit, which - in my case is provided by a 5V low drop-out regulator.
The regulator will take some heat, as the battery is a 9V square shaped one. This means a 4V drop, so we need to make sure we don't draw too much current through it. I haven't used a heatsink to protect it, the processors and the MAX7219, and the rest of the components shouldn't reach 200mA in current consumption. Since the LED-s are multiplexed, the LED current is taken into account only once.
TRIG - trigger pin
The user has to provide a microsecond long impulse on this pin, then wait for the echo to arrive on the echo pin. Note, that echo is high in the first tens of milliseconds, make sure you wait that out at initialization.
This pin is asserted, a timer is started, counting is stopped when the echo pulse is detected. From the number in the counter register we know the time needed for the sound to get back from an eventual obstacle. We know the speed of sound - we can calculate the distance!
ECHO - echo pin
This pin is used to detect the reflected sound waves. I connected it to a pin that has interrupt-on-change capability. A rising edge stops the previously mentioned counter, and saves it into a variable, which gets processed shortly after. For more info about IOC, check the datasheet of the processor.
A picture of my sensor, bottom side.
The timing of the pulses is shown in the datasheet of this module. As mentioned before, you need to wait out the initial ~100 ms, then give 10 microsecond trigger pulses on the TRIG pin. Giving such an impulse results in an 8 cycle burst coming from the transmitter 'speaker'. Then, we need to wait for an echo to come. The echo is captured by the receiver, and transformed to an impulse of variable length on the ECHO pin. The length of this echo impulse is directly proportional with the distance to the obstacle. The datasheet gives a simple formula to calculate this:
distance [cm] = t measured [microseconds] / 58
this will return the distance in centimeters. I only divide by 5.8, so the distance gets returned in millimeters. I will use four digits, that's the best for this range and resolution.
Let's take a look at this with a quicker eye, my logic analyzer:
How can we measure pulse length using a processor? With external interrupts. I will use a rising edge IOC to start a timer, and a falling edge to stop it. The data from the register tells us the time that passed between the two edges. We have that, we can calculate the distance, as instructed by the datasheet.
There is a timer in the background, when it detects a rising edge it starts up. A falling edge will trigger its stop, we are using the value from the timer registers to figure out the time that elapsed between the two edges. With other words: we're using a timer as a counter. The variable PulseLength is updated in the interrupt service routine.
WASTE_10_US(); is a macro I wrote, it consists of enough "nop" assembly instructions to waste a total time of 10 microseconds.
Fitting the sensor into the enclosure
Lucky me, the HC-SR04 perfectly fits into the upper part of my enclosure. I drilled two holes, than widened them up, piled them until I was able to push the "speakers" through the holes. Because I was tight on space in that area, I had to solder the oscillator from the top side to the bottom side. The useless four pin pin header was also gotten rid of.
When powering this up together with my main board, I experienced something I think I should mention here. The next step is about low drop-out regulators.
Step 9: Those Foxy LDO Regulators
A few words about LDO-s
Low drop-out regulators are probably the cheapest way for voltage regulation. Powered by a voltage input, they provide a stabilized rated output voltage on their output pins. From my experience, working with them is pretty straightforward, you just throw them into your design, add some filter caps eventually, and you're done.
The place where an LDO differs from standard linear regulators is the amount of drop-out voltage that is required to maintain a well regulated output. The drop-out voltage is the minimum voltage required across the regulator to maintain regulation. But what is the minimum voltage drop?
Standard regulators have a darlington NPN or PNP output stage as passing element.
A typical application
Let's check out some details about the LDO chain I decided to use in my design. This is a very basic representation of a linear LDO regulator.
The input voltage is applied to a pass element, which is an N or P channel FET (can be a bipolar transistor too). The pass element operates in the linear region to drop Vin down to Vout. Vout is then measured by the error amplifier and compared to a reference voltage. Based on the difference between Vin and Vout, the error amplifier drives the pass element’s gate to the appropriate voltage to ensure that the output is at the correct voltage. If large current draws try to pull the Vout lower, the error amplifier will react by raising the gate driving signal - which means an increase in Vout also. Under normal operation an LDO acts like a resistor.
My setup has these parameters: Vin = 5 V, Vout = 3.3 V, Iload = 100 mA.
In this case, my LDO operates as a (5 - 3.3) / 0.1 = 17 ohm resistor. I've never tried this before, but I've read before, that in this case, the LDO could be replaced by a 17 ohm resistor. Of course, this would demand a VERY steady load current. In most situations wou won't see stuff like this, there are current oscillations resulted by LED-s or other stuff turning on, so the parameters will be unlikely static. You'll be better off sticking with the LDO, which has a feedback loop in it.
That is done, now you only have to take care not to mess up the pin-out, as they differ from package to package, and from supplier to supplier.
That's enough for a side explanation about LDO-s, let's move on with the real thing!
Step 10: Okay, Slave I²C Sensor Is Built, What Do I Do With It?
If you're done with the previous steps, you can sit back, and watch it work with an arduino. If you want something cool, keep on working to get a cool measuring "tape"!
A few years ago I was working with my dad on a house, that needed its whole electric wiring network remade. While we were working on that, we had to measure whether the wires connect, whether the mains is present on the wires, etc. We forgot to switch back the multi-meter to high voltage measurement position: BOOM. I threw the guts out, but kept the enclosure, I thought, maybe I will use it one day. Originally I wanted to build my measuring device into the enclosure of that broken multi-meter, but I couldn't figure out what to put in the empty circle in the middle, where the rotary switch used to be. I looked on the web for something similar, and found the BOS501B enclosure.
It has space for a 9 V battery, a few screw holes, and a slot for the 4 LED displays I will use.
Get the caliper again, make some measurements of where the holes are, where the display slot is, so you can figure out your boards size. The work phases are the same as with the previous boards. Check back to the video where I showed you how to shape the board!
If I want more from my I²C sensor, (in my case to turn it into a measuring tool) I have to add a display to it and some user interface components, so my dad can control it. The easiest choice: use another identical PIC12LF1840T39A to do the I²C queries, and the SPI bit-banging for the display. I also added two buttons on the MCLR pins just in case I want some sort of input from the user. Here's my schematic, most of the components were added to my libraries during this project.
The two big rectangles in the middle are the two processors, U1 is the display driver (MAX7219), the one in the top middle is the display itself. P? is actually an LDO, i just didn't bother with drawing a special schematic symbol for it now. On the bottom there are two buttons with two pull-ups, which will be used for user input, and the programmer headers for the two PIC processors.
Transfer your changes to the PCB file, then place, replace, lock, route, until you're finished. Then create an "Output Job" file, add a "Documentation output" of the "PCB prints". Make sure it prints 1:1, you only enabled the layers you want and most important: make sure it won't be mirrored. Then tie it to a PDF output and generate the file by pressing F9.
I've had the bad luck to place the display on the top layer by mistake, so the one you see images of is actually the second iteration of this PCB. Now it should be okay. Version one even had some extra LED-s in it, but as the project evolved I realized I don't really need them. Let me show you some pics of my board!
A couple of details of the layout, in Altium, and in PDF format
Solder up the components, plug it in, and start programming! I recommend having flux and solder wick at hand when soldering these smaller components, along with very thin solder. This way you have greater control over the solder amount and where it jumps to!
Just when I was about to wire the thing together I came to realize the terrible thing, that just happened. Awful enough, I completely disregarded these two letters from the PIC ID number: "LF". This series of PIC processors work on 3.3 V, so at 5 V one will see smoke coming out of them instead of nice serial signals. So the 5 V regulator is off the table, from the PIC-s point of view, we need to add a 3.3 V regulator.
The 5 V regulator has to stick around, though, because the MAX232 of the ultrasonic sensor doesn't work with 3.3 V. I realized this, after I saw, that each time, the 10 microsecond trigger impulse was sent, a constant echo of 143.6 milliseconds is received.
Let's deal with this. The HC-SR04 board needs to be powered from 5 V, the trigger signal needs to be raised from the PIC 3.3 V to 5 V, the echo signal needs to be lowered from 5 V to 3.3 V. Let's design a simple level shifting mechanism, that fits in our enclosure:
Check out the schematics. Since each line is uni-directional, there's no need for complex level shifter IC-s, not even the for the cool & common double FET solution. This is simple enough, the 5 V signal is taken to 3.3 V by a zener diode voltage stabilizer circuit, the 3.3 V signal to 5 V switch is made with the help of a transistor and a pull-up resistor. Of course, this has to be small enough to fit in our enclosure.
If you're done with this, you can move on as you would've done anyway. If you started off with a 5V tolerant PIC, you're lucky - you can skip this side project. Mine came out pretty small, it has around 18 x 15 millimeters. Unfortunately I wasn't able to find a SMT zener, so the thing has some height too - because of the through-hole diode. Let's wrap it up to our system so we can move on with the cool stuff!
We're going to use this board to create a nice little tool for every crafty dad! I already told this and that about boards, schematics, but haven't said anything about SPI. It is a serial protocol which we're going to use to talk to our display through the display driver. But how do we communicate on SPI? Let's check it out!
Step 11: A Couple of Words About SPI Protocol and Its Position in This Project
An SPI (Serial Peripheral Interface) bus offers bidirectional communication between two devices. Similar to the previously used I²C protocol, this too is Master/Slave based. It is often called four wire interface; however, an SPI bus often uses 3, 5, 6 or even more wires to communicate. The SPI bus can be expanded by adding slaves to it: their MOSI, MISO, SCLK pins are wired together, their slave select pin needs to be pulled separately. The slave select pin for an SPI slave is like the slave address byte sequence for an I²C device.
The SPI protocol and the PIC12LF1840T39A
At the heart of a PIC SPI module is the SPIxSR shift register, the shift occurs with every clock signal on the SCLK pin. Similar to other serial peripherals, this too is double buffered, the SSP1SR content cannot be written, or read directly. The data needed to be sent must be written into SSP1BUF, this acts as a transmit register at write. During transmission, the data is automatically loaded in SSP1SR, and is clocked out from here.
Reception is done again via the internal shift register. When 8 bits are clocked into the SSP1SR, the byte is loaded intu SSP1BUF, the user can read it from there. This time, the SSPBUF acted as a receive register.
The SPI modules built in the PICs can work up to 10 MHz. In our case in particular, we will use ~10 KHz.
To our great sadness, the SPI and the I²C lines are multiplexed to the same pins. This means, we will have to bit-bang one of them. I wrote some routines for another instructable of mine, I copied it to this processor, but hit some difficulties, so I wrote them again, this time without any interrupts involved.
Because I am using bit banged SPI, I won't really go into details about it. I'll rather focus on analyzing the MAX7219 datasheet, to see what I have to put on the pins to make it work!
Here's an image from the MAX7219 datasheet:
You can see how the lines need to be toggled, as time passes. That is exactly what my signals have to look like on a scope, when I'm done. Look at the code sample and analyze what is written there, you'll see how it acts to generate the bit pattern we need.
Once the code you wrote generates the exact waveform you expect it to generate, you can move on to send commands to your display! There are plenty of ways to test your algorithm, there are simulator, analyzers, scopes that can help you with this.
This is a single word transmission, it's cropped out and zoomed from the next image with the data exchange
Okay, it seems we got ourselves through this display thing too. Now we need to configure this PIC to have its MSSP module configured as an I²C master.
Step 12: Configuring the PIC As I²C Master
void I2C_Init( void )
TRISA1 = 1;
TRISA2 = 1;
ANSELA = 0b00000000;
SSP1ADD = 0x03;
SSP1STAT = 0x80;
SSP1CON1 = 0x28;
SSP1CON2 = 0x00;
This is pretty much the same with what we did with the sensor, only this time we have to set the MSSP up to act as a master. I wrote some functions to do this, I will do some snips for you with sample code, to get you rolling. For some stupid reason I wasn't able to post this as an image, So here it is, a bit uglier, but at least it works.
The main routine
This is my main code and I²C code, some software de-bouncing is done for changing the screen luminosity, I²C commands are issued to collect distance data from the HC-SR04 through slave processor, then the numbers are parsed to be displayed on the 7 segment LED display. For the sake of easier routing I chose the digit cathodes to go randomly to the MAX7219, it is way easier to get around this in software then to do some fancy routing to get the tracks right.
The three transfers you've already seen when I presented you the slave transmission. The bus is the same, so I didn't bother to take new snips - the concept itself is all right, and that's what matters.
Having said this, the master PIC now talks to the slave PIC, asking for distance measurements. When data is received the display values are immediately modified to show the current distance measurement.
I also added a user interface to set the screen brightness. The master PIC has a button on the MCLR pin. By pressing it at least for half of second, the user can cycle through eight levels of brightness. I only put this in, because the MAX7219 has this feature in-built, and had that button lying around uselessly anyway.
I am still thinking of a good feature to put on the second button, which connects to the slave PIC's MCLR pin. I thought of using it to measure difference between the actual and the previous measurement, but the sensor only sees 400 centimeters, that's a bit short, so I didn't bother.
Suggestions are welcome!
Step 13: Wrapping It Up, Putting a Ribbon on It
I also want to design a sticker for this, I am doing this in Writer, text box sizes can be adjusted to match the enclosure dimensions, and button positions, so I don't really expect surprises while doing this.
I hope you'll be having fun while doing this last step of creating a useful tool, and a nice DIY gift!
There are still quite a few days until Christmas, so I will stash this in my workshop, so Dad won't find it! It will come with a set of replacement batteries, just in case he uses it too often!
I honestly hope you've had fun reading or building this, if it inspires at least one dude to after-build - it will make this instructable a great one.
A movie clip to prove that this is actually working: