tag:blogger.com,1999:blog-30901190434586233422024-02-07T11:52:47.597-08:00Fix It Until It's BrokenMusings on hardware, software, life and stuff from a hardware/firmware engineer/entrepreneur.Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.comBlogger35125tag:blogger.com,1999:blog-3090119043458623342.post-81560668927799846112018-03-13T15:01:00.001-07:002018-03-13T15:01:38.434-07:00Altium Library and Directory SetupRecently I helped a client that was new to Altium get their libraries set up. They asked "why can't we just put all the parts in one library?" I've found that this presents several issues, mainly as you get larger:<br />
<br />
<ul>
<li>hard to for two people to work - you’ll have endless version conflicts</li>
<li>slower to compile</li>
<li>slower to find things - I have 281 components just in my LCR library - see attached</li>
<li>harder to use parameter manager to manage part parameters</li>
<li>all your eggs are in one basket</li>
</ul>
<span style="font-family: inherit;">Instead, I recommend that you divide parts into different libraries, mainly separated by manufacturer. For big manufacturers I separate it into a few sub-libraries, like TI-Analog, TI-LPRF, TI-MSP430, etc. </span>I also have a few generic libraries: LCR (resistors, caps, etc), Crystals, and Inductors. If I were setting it up new I’d make one library for resistors, one for capacitors, one for inductors. It makes it faster to find stuff when you have hundreds of parts in the library. I leave these libraries as generic because I don't care as much as to which manufacturer I use for these parts. I select passives mainly based on what is in stock.<br />
<br />
<br />
<b>Project Directory Structure</b><br />When I set up projects, I use the same directory structure for each client:<br />C:\AltiumDesigns<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>—Client1<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Project1<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Project2, etc.<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Released (stores tags of all released Altium files)<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— ManufacturingPackages (each release to a CM)<br class="" />When I send files to a CM, I do the following:<br />a. Check in all the project files (SchDoc, PcbDoc, PrjPcb, OutJob, etc)<br />b. Verify that nothing is checked in that shouldn’t be there.<br />c. Tag all the project files into the Released directory<br />d. Store the manufacturing files into the ManufacturingPackages directory.<br class="" /><br />
<b>Library Directory Structure</b><br />
For libraries, I use the following directory structure:<br />C:\AltiumLibraries<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Manufacturer1<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— History (created by Altium, SVN ignored)<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Project Outputs for XX (created by Altium, SVN ignored)<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Project Logs for XX (created by Altium, SVN ignored)<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Manufacturer1.LibPkg<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Manufacturer1.SchLib<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Manufacturer1.PcbLib<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— STEP (directory, contains STEP files for that manufacturer)<br /><span class="Apple-tab-span" style="white-space: pre;"> </span>— Manufacturer2, etc.<br class="" /><br />
<b>Version Control</b><br />
I store all libraries and project files in subversion. This makes it easier to undo changes, and also allows multiple engineers to work on a project. For Subversion, I use a cloud hosting provider, <a class="" href="http://svnrepository.com/">SVNrepository.com</a>, it costs about $72 per year. I have a ton of stuff in subversion - I check in all my hardware, firmware, and software projects. There's also more hardware centric Subversion provider, Assembla, but I don't prefer their approach because they structure the directories like software projects, not hardware projects.<br />
<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-78772568686304661022016-02-09T16:28:00.003-08:002016-02-09T16:43:05.031-08:00PCB Fabrication Notes for 2016, Part 2This is the second part of a series about PCB fabrication notes. As we mentioned previously, your requirements may be different, but these are based on what works best for our projects.<br />
<h2>
Finish</h2>
There are many different surface finishes, each with their own benefits and drawbacks. We use either:<br />
<br />
<ul>
<li>HASL for the few low cost non-lead-free boards where we don't have any fine pitch components</li>
<li>ENIG, for most of our projects</li>
</ul>
<br />
One of our CMs explained to me that they prefer ENIG because it is ruler flat and is easier to work with. In low to moderate quantities the cost difference between ENIG and HASL will not be much.<br />
<br />
We had one project where we specified "FINISH IS ENIG AS PER IPC-4552" and nothing else. The CM went to build the boards and was having a tough time getting the solder to adhere. It turns out that IPC-4552 has two acceptable plating thicknesses: a thinner plating for devices that will be soldered soon, and a thicker plating for devices that won't be soldered soon. The PCB Fabrication house used the thinner plating thickness but did not plate enough so the gold baked off and the solder was not able to adhere to the nickel below. So, now we specify the following:<br />
<br />
FINISH IS ENIG AS PER IPC-4552 WITH 2 TO 5 MICROINCHES OF GOLD OVER 120-230 MICROINCHES OF NICKEL.<br />
<h2>
Solder Mask & Silkscreen</h2>
The solder mask is the protective paint like layer that is on top of the copper traces, and is often green but is also available in red, blue, black, white, purple, etc. You need to specify the type of soldermask, the color, and how it shall be applied. If you are building a PCB that will operate at a high temperature (like an LED board) and you want a white soldermask then you should also specify a non-browning type.<br />
<br />
Silkscreen is the lettering and printing that is on the PCB, and is usually a contrasting color to the soldermask. So if the soldermask is red, green, or blue then the silkscreen should be white or yellow. Contrastingly, if the soldermask is white then the silkscreen should be black. While our design shouldn't have silkscreen on any component pad, we want to ensure that the PCB fabrication house will catch any errors, so we add the requirement below.<br />
<br />
SOLDERMASK SHALL MEET IPC-SM-840E, CLASS T REQUIREMENTS, COLOR BLUE. APPLY OVER BARE COPPER AS PER IPC-6012, CLASS 2.<br />
SILKSCREEN SHALL BE WHITE, PERMANENT, ORGANIC, NON-CONDUCTIVE INK. APPLY AS SHOWN IN FILES. THERE SHALL BE NO SILKSCREEN ON ANY SOLDERABLE COMPONENT PAD.<br />
<br />
<h2>
Bow and Twist</h2>
<div>
The most important part of specifying bow and twist is defining how it is tested. IPC-TM-650 is an IPC defined test method.</div>
<div>
<br /></div>
<div>
BOW AND TWIST SHALL NOT EXCEED 0.75% AS PER IPC-TM-650 2.4.22.</div>
<h2>
Cleanliness</h2>
<div>
Different boards will have different cleanliness requirements. If you are building low power devices that are designed to operate on a battery for a long time then you will want to ensure that the board is very clean, otherwise any residue can cause a current path and shorten battery life. There's considerable debate in the industry about how to measure cleanliness. IPC-6012 requires using Resistance of Solvent Extraction (ROSE) but <a href="http://blog.epectec.com/pcb-cleanliness-attention-to-details" target="_blank">many in the industry find issue with that</a>, and recommend using Ion Chromatography instead. Again, we want to reference a test method for how to measure cleanliness so we specify IPC-5704 which requires Ion Chromatography and defines the maximum amount of residue of various types.</div>
<div>
<br /></div>
<div>
PCB SHALL COMPLY WITH IPC-5704 CLEANLINESS REQUIREMENTS.</div>
<div>
<h2>
Electrical Test</h2>
<div>
Unless you are building a very low cost board, you should always have the bare boards electrically tested. This verifies that the fabricated PCB matches your netlist. This is done with very fast flying probe testers, which test for continuity between the pads.</div>
</div>
<div>
<br /></div>
<div>
ALL BOARDS ARE TO BE 100% ELECTRICALLY TESTED USING SUPPLIED IPC-D-356 NETLIST ACCORDING TO IPC-9252, CLASS 2.</div>
<h2>
UL Marking & Vendor Markings</h2>
<div>
It's fairly standard to require a UL marking on the PCB, to verify that there is traceability. The PCB fabricator will also typically want their own markings on the PCBA, like a job number or date code. We require all vendor markings to be in silkscreen (not metal) so that we can ensure that they don't put metal where it is not wanted, like beneath an antenna, or in a keep-out area.</div>
<div>
<div>
<br /></div>
<div>
FINISHED BOARD SHALL MEET THE REQUIREMENTS OF UL796 WITH A FLAMMABILITY RATING OF UL 94V-0 OR BETTER. VENDOR'S UL LOGO AND/OR DESIGNATION AND DATE REQUIRED, TO BE LOCATED IN SILKSCREEN ON THE SECONDARY (BOTTOM) SIDE OF PCB. ANY VENDOR MARKINGS ARE TO BE SILKSCREEN ONLY, BOTTOM SIDE. NO COPPER MARKINGS BY VENDOR ARE ALLOWED.</div>
</div>
<h2>
Miscellaneous</h2>
<div>
If you have internal cutouts then you should specify the maximum radius used in the cutouts. Otherwise your nice rectangular cutout will end up having significantly rounded corners and whatever needs to go in there may not fit.</div>
<div>
<br /></div>
<div>
<div>
MAXIMUM RADIUS ON ALL INSIDE SHARP CORNERS TO BE .045" (1.15MM) MAX. </div>
<div>
ALL DIMENSIONS ARE +/-0.010" (0.254MM)</div>
</div>
Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-87698424694000431192016-02-09T15:52:00.000-08:002016-02-09T15:52:00.355-08:00PCB Fabrication Notes for 2016Have you looked at your PCB fabrication notes lately? I recently went through our standard set of notes and did a ton of research to see what is best. This is the first of a two-part post about standard PCB fabrication notes, explaining the reasoning for each note. Your requirements may be different, but these are based on what works best for our projects. Most of our projects are fairly run of the mill, industrial type products, 2-4 layers, 6/6 width/spacing, etc. Nothing too fancy, but need to be decent quality and last a few years. Nowadays everything is going RoHS, so we include that into our standard drawing package too.<br />
<br />
When developing standard notes, be sure to talk to your CM and PCB vendor - they have lots of good advice since they've seen what works and doesn't work. But keep in mind that they're goals may not be the same as yours.<br />
<h2>
Standards</h2>
<span style="font-family: "times" , "times new roman" , serif;">The <span style="color: #252525;">Association Connecting Electronics Industries (aka IPC) has many standards that you can reference when specifying PCBs. There are many advantages of referencing a standard instead of specifying every little detail about a board:</span></span><br />
<br />
<ol>
<li><span style="color: #252525; font-family: "times" , "times new roman" , serif;">The standards reflect industry best practices, and are constantly being updated as technology changes</span></li>
<li><span style="color: #252525; font-family: "times" , "times new roman" , serif;">The vendor will be more familiar with a standard than your individual requirement</span></li>
<li><span style="color: #252525; font-family: "times" , "times new roman" , serif;">In the event of a dispute, you can have a 3rd party verify standard compliance easily</span></li>
</ol>
<br />
<span style="font-family: "times" , "times new roman" , serif;"><span style="color: #252525;">The current revision of standards can be checked on <a href="https://www.ipc.org/4.0_Knowledge/4.1_Standards/revstat1.htm" target="_blank">this page</a>. To ensure that your product is always being built with the most recent version, you can either manually update the note each time a standard is changed, or just add a note requiring the current version. Note that even in the standards, some specifications are "As Agreed Between User and Supplier", or AABUS for short.</span></span><br />
<h4>
IPC-A-600 vs. IPC-6012</h4>
<span style="font-family: "times" , "times new roman" , serif;"><span style="color: #252525;">There are two standards frequently specified when ordering PCBs: IPC-A-600 and IPC-6012. IPC-A-600 is an inspection criteria and defines how the board should look when it is complete. It is less stringent than IPC-6012, and is usually the governing specification for prototype PCB houses. On the other hand, IPC-6012 defines how the PCB is constructed, and goes into much greater detail. It also includes requirements for inspections and coupons. Inside IPC-6012, there are three main classes, depending on how stringent the standard should be:</span></span><br />
<ul>
<li><span style="color: #252525; font-family: "times" , "times new roman" , serif;">Class 1, low quality</span></li>
<li><span style="color: #252525; font-family: "times" , "times new roman" , serif;">Class 2, industrial quality</span></li>
<li><span style="color: #252525; font-family: "times" , "times new roman" , serif;">Class 3, high performance</span></li>
</ul>
<span style="color: #252525; font-family: "times" , "times new roman" , serif;">If you're not making cheap toys or avionics, then you'll probably want to use IPC-6012, Class 2. Refer to </span><a href="http://www.apctinc.com/images/designerpdf/IPC_6012C_Class_Chart_2011.pdf" style="font-family: Times, 'Times New Roman', serif;" target="_blank">this chart</a><span style="color: #252525; font-family: "times" , "times new roman" , serif;"> for a comparison between the three main classes.</span><br />
<h4>
Summary</h4>
<div>
The way that we specify the standard to use is:</div>
1. STANDARDS: USE CURRENT REVISION OF ALL STANDARDS. FABRICATE PCB ACCORDING TO IPC-6012, CLASS 2.<br />
<h2>
Material</h2>
Do you specify the PCB material using something like the following?<br />
<ul>
<li>"Hi-Temp FR-4, 170Tg"</li>
<li>"FR4, 135Tg"</li>
<li>"Rogers 4000"</li>
</ul>
If so, then you are making life more difficult for yourself. The IPC has developed standards for materials. There are many different types that are specified (called "slash sheets" in IPC parlance) but using these can make your life much easier for the following reasons:<br />
<ul>
<li>Exactly defines key specifications</li>
<li>Easier for PCB fabrication houses</li>
<li>No single-vendor lock-in</li>
</ul>
The IPC-4101 standard (Specification for Base Materials for Rigid and Multilayer Printed Circuit Boards) defines the various types very specifically. These define the characteristics of the material in great detail, including:<br />
<br />
<ul>
<li>Reinforcement (paper, fiberglass, etc.)</li>
<li>Resin (phenolic, epoxy, etc)</li>
<li>Copper peel strength</li>
<li>Moisture absorption</li>
<li>Tg, Td, CTE</li>
</ul>
<br />
<h4>
RoHS Compliance</h4>
When it comes to lead-free compliance for PCB material, things get confusing. A PCB type may in and of itself be lead-free, but it may not be able to survive the higher process temperatures of lead-free assembly. So it gets confusing, but suffice to say that we want a material that is both lead-free and is compatible with a lead-free process. There are many specifications to look at, but the most important are the glass transition temperature (Tg) and minimum decomposition temperature (Td).<br />
<br />
<a href="http://www.epotek.com/site/files/Techtips/pdfs/tip23.pdf" target="_blank">Glass transition temperature</a> is <span style="font-family: "times" , "times new roman" , serif;">defined in IPC-TM-650, 2.4.24C and is the temperature where the rigid PCB becomes flexible. Materials with a higher Td are more stable, but you don't get something for nothing: these materials have some drawbacks as I'll explain below.</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">The minimum decomposition temperature is defined as </span><span style="font-family: "times" , "times new roman" , serif;">the temperature at which point the weight of the PCB material changes by 5%, according to </span><span style="font-family: "times" , "times new roman" , serif;">IPC-TM-650, 2.4.24.6. These relate to the T260 or T288 characteristics, which is the time until delamination at 260C or 288C, respectively.</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">The higher-end phenolic materials (Tg > 170C & Td > 340C, aka IPC-4101/126 or /129 have great thermal properties but have poor moisture absorption and copper-to-laminate adhesion. So you can have your cake but you can't eat it too. Specifically:</span><br />
<ul>
<li><span style="font-family: "times" , "times new roman" , serif;">Moisture Absorption: Standard 130Tg FR-4 materials have moisture absorption of 0.20% vs. 0.45% for the fancier high Tg phenolics.</span></li>
<li><span style="font-family: "times" , "times new roman" , serif;">Peel Strength: Standard 130Tg FR-4 materials have peel strength of 8-10 lbs. vs. phenolics have a peel strength of 3-4 lbs.</span></li>
</ul>
For example, compare <a href="http://www.isola-group.com/wp-content/uploads/2013/04/DE104-Laminate-and-Prepreg-Data-Sheet-Isola.pdf" target="_blank">Isola DE104</a> (135Tg, 315Td, /21) and <a href="http://www.isola-group.com/wp-content/uploads/2015/12/370HR-Laminate-and-Prepreg-Data-Sheet-Isola.pdf" target="_blank">Isola 370HR</a> (180Tg, 340Td, /126): the former has peel strength of 9.0 lb/inch vs. 6.5 lb/inch for the higher temperature material. When it comes to IPC-4101 grades that are compliant with lead-free processing, there are generally four groups of material. Refer to the IPC standard for the details, or <a href="http://saturnelectronics.com/ipc_specs" target="_blank">this handy chart</a>.<br />
<ul>
<li>Low temp: /23, /24</li>
<li>Ok (110C Tg): /101, /121 - note, these have largely been ignored in US</li>
<li>Better (150C): /99, /124 (these are best for our applications)</li>
<li>High: (170C): /126, /129</li>
</ul>
Summary<br />
<div>
The way that we specify the PCB material is:</div>
<div>
2. MATERIAL: COPPER CLAD PLASTIC SHEET PER IPC-4101/99 OR IPC-4101/124 ROHS COMPLIANT.</div>
<h2>
Board Construction</h2>
Next, we have several requirements to ensure that the PCB is fabricated the way we want it:<br />
<h3>
RoHS Compliance Clause</h3>
We have an omnibus clause stating that everything must be RoHS compliant, and defining exactly what that means. This is good just as a double check to ensure that we don't miss anything. The vendor will alert us if we have something that is not RoHS compliant.<br />
<br />
ALL PARTS & MATERIALS SHALL MEET THE RoHS DIRECTIVE 2011/65/EU.<br />
<h4>
Board Stack-Up</h4>
Define the thickness of the PCB, if not defined in the layer stack-up. Try to avoid having non-stand thicknesses. Be sure to include a tolerance. Just to avoid confusion, I also like to define that the board stack-up should be according to the table in the PCB fabrication drawing:<br />
<br />
OVERALL BOARD THICKNESS TO BE 0.062 INCHES +/- 0.005 INCHES.<br />
REFER TO LAYER STACK-UP TABLE FOR LAYER CONFIGURATION. ALL LAYERS VIEWED FROM THE PRIMARY (COMPONENT) SIDE.<br />
<div>
<h3>
Hole Dimensions</h3>
</div>
Most of this is called out in IPC-6012, but we specify some here to be more exact. In IPC-6012 it sometimes says "this parameter is AABUS, but if not specified then standard X applies". When it comes to hole location tolerance, if none is specified then IPC-2221 applies. It requires 0.00984 for Type A, 0.0079" for Type B, and 0.00591 for Type C.<br />
<br />
UNLESS OTHERWISE NOTED ALL HOLE DIMENSIONS APPLY AFTER PLATING.<br />
ALL HOLES SHALL BE LOCATED WITHIN 0.006" OF TRUE POSITION AS SUPPLIED IN CAD DRILL DATA. LAYER TO LAYER REGISTRATION SHALL BE WITHIN 0.005".<br />
<h4>
Hole Wall Thickness</h4>
IPC-6012 Class 2 calls out 0.00071" minimum and 0.00079" standard for plating thickness, but we want to ensure that we have plenty of copper.<br />
<br />
ALL HOLES SHALL HAVE A WALL THICKNESS OF 0.0008".<br />
<br />
That's all for now, next we'll talk more about surface finish, cleanliness, and testing.<br />
<div>
<br /></div>
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-80539520664757273632015-12-23T20:54:00.004-08:002015-12-23T20:54:39.808-08:00MSP432 LaunchPad review<br />
The new <a href="http://www.ti.com/tool/msp-exp432p401r" target="_blank">MSP432 LaunchPad</a> is very cool. Here it is in all its glory. We got our hands on a pre-production unit in red, but it looks like the production units are all black.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://43oh.com/wp-content/uploads/2015/03/launchpad-msp432-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://43oh.com/wp-content/uploads/2015/03/launchpad-msp432-2.jpg" height="198" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2>
The MSP432 Processor</h2>
It is a 32-bit 48MHz Cortex-M4F processor with the MSP430 peripherals. Performance stated of 1.18f DMIPS/Mhz. At 48MHz it uses 7.6mA if using the LDO, 4.6mA if using the DC/DC converter. I'll explain more about that below. So it's pretty good.<br />
<br />
<ul>
<li>Memory - 256kB of flash, 16kB of INFO Memory, 64kB of SRAM (including 8kB of Backup Memory - we're not in the MSP430 land anymore), and 32KB of ROM preloaded with MSPWare Driver Libraries.</li>
<li>Voltage Range: 1.62V to 3.7V</li>
<li>Current consumption: Less than 1uA while running with RTC, Active is 90uA/MHz</li>
<li>Clocking: the flexible clocking that we know and love - internal DCO up to 48MHz, support for 32kHz xtal, a few different internal oscillators.</li>
<li>DMA - if you use DMA in a micro controller please comment below. I've always wondered who actually uses it.</li>
<li>Timers - 4x 16-bit timers, 2x 32-bit timers. Fewer than I would have thought.</li>
<li>Serial - 4x eUSCI_B modules with I2C/SPI and 4x eUSCI_A modules with UART/SPI/IrDA. Does anyone still use IrDA?</li>
<li>GPIO - All IOs can generate an Interrupt, like Tiva (yay!), all can drive up to 6mA, and 4 of them can drive up to 20mA. 24 of the pins support pin mapping - a very cool feature.</li>
<li>Analog - 14-bit 1MSPS ADC (ooh, that's cool) with (finally!) an internal voltage reference that doesn't suck (10ppm / degree C stability) and 2 comparators.</li>
<li>Hardware Accelerators for AES and CRC</li>
<li>Package - LQFP100, 80NFBGA, 60VQFN. The LQFP is pretty large, 14mm square. Same size as the TM4C129X in the debugger section of the Launchpad but with bigger pitch - should make soldering easier.</li>
<li>Built-In DC/DC converter or LDO option. This is new, never seen this on a processor before. The processor core operates at a lower voltage than the supply voltage. This voltage can be supplied by an LDO or DC/DC switching regulator. Most processors use an LDO. The MSP432 has an internal DC/DC regulator - you just need to attach an external inductor and a couple capacitors. Using the DC/DC regulator reduces operating and sleep current consumption by about 40%.</li>
</ul>
<h2>
The LaunchPad</h2>
The board is designed quite well. Like all Launchpads, it has an onboard debugger. Lets hope that it's more reliable than the MSP430G2 Launchpad debugger - that's the buggiest debugger I've ever used. The debugger on the '432 Launchpad can be electrically disconnected from the processor section via headers. That's very cool too. The headers that connect the two are well labeled:<br />
<br />
<ul>
<li>Power: GND, 3V3, 5V</li>
<li>Serial: RTS, CTS, RXD, TXD</li>
<li>JTAG: SWCLK, SWDIO, SWO, RST, TDI</li>
</ul>
<br />
There is a "Launchpad-XL" pin configuration (the 40 pin version with 35 GPIOs used on the '5529 LaunchPad) so it should support all the new BoosterPacks. Unfortunately they didn't say whether it would support the SensorHub - that would be a cool combination. They also pulled out the unused pin to a separate unpopulated header. There's 26 pins on that header. So there's a total of 35 + 26 = 61 GPIOs available.<br />
<br />
Peripherals - Similar to the TM4C123 LaunchPad, this board has 2x buttons + reset button, 1 red LED, and one RGB LED. I like the Launchpad style of not hogging up pins with peripherals that I don't need. The LED signals can be electrically disconnected from the processor using jumpers. The LED signals aren't available on any of the headers so it's questionable why someone would want to disconnect them. There are also a 32KHz crystal and a MHz crystal.Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-90151054248639837442015-05-21T16:52:00.001-07:002015-05-21T16:52:22.695-07:00BLE application development : Custom HardwareThis is the third post of a series on how to develop an application with Bluetooth Low Energy (BLE). If you haven't already, go back to the <a href="http://fixituntilitsbroken.blogspot.com/2014/05/get-started-with-bluetooth-low-energy.html" target="_blank">first post</a> in the series for information on how to get started. I'm working on a light bulb where the intensity and color temperature can be set via BLE. The application does a ton of stuff, and I'm grafting BLE support into the rest of the application. For projects like this, the Anaren module approach is great. <br />
<br />
A <a href="http://en.wikipedia.org/wiki/HAL_%28software%29" target="_blank">Hardware Abstraction Layer</a> is a programming <a href="http://en.wikipedia.org/wiki/Pattern_%28software%29" target="_blank">pattern </a>whereby you encapsulate all the functions that talk to the bare hardware into one file. This makes it easier to port the application to different hardware platforms.<br />
<br />
To complete this step, you'll need:<br />
a) have completed the previous two steps<br />
b) finished most of your schema with FirstApp, and made stubs for the callbacks<br />
c) the schematic<br />
d) hopefully a reference design close to your application. I'm using Tiva.<br />
<br />
Because this is my first project using the Anaren BLE module, I routed all my module connections to the same signals as those used on the Anaren example that uses the TI TM4C123GXL LaunchPad and BoosterPack. This makes porting a little easier.<br />
<br />
I recommend getting your schema and callback functions written while in the Em-Builder environment. Changing the schema after you move stuff over is a pain.<br />
<br />
I like to start with the Hardware Abstraction Layer because that's the lowest level, and once it is done the rest should be fairly straightforward. Start by creating a new directory called Hal. In it create two blank files, Hal.h and Hal.c. First, copy and paste over the contents of the FirstApp Hal.h file. Save it and compile. Be sure that you don't have errors. My general approach is to go step by step, copying over stuff and compiling as we go. That way we can solve problems early instead of getting to the end and discovering a huge pile of problems to wade through.<br />
<br />
<h2>
Porting Hal.c</h2>
I have Hal.c open in Em-Builder, and an empty file in my IDE named Hal.c. I've already copied over Hal.h. Now, to port Hal.c we're going to go step by step: start from the top of the file, copying over a little, modifying it as appropriate, and compiling.<br />
<h3>
#includes</h3>
First, copy over all the includes. The original Hal.c had two files:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#include "lm4f120h5qr_pin_map.h"</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#include "inc/lm4f120h5qr.h"</span></span><br />
... that can be replaced with the simpler driverlib one:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">#include "driverlib/pin_map.h"</span></span><br />
Now it should work better.<br />
<h3>
#defines</h3>
There are a couple of #defines that are rather perplexing. To clarify:<br />
EAP_RX_ACK_PIN - this is an input to the module, and an output from the MCU<br />
EAP_TX_ACK_PIN - this is an output from the module, and an input to the MCU<br />
Next you'll see the LED stuff. I ported over the LED_PIN and CONNECTED_LED stuff but I commented out the BLUE_LED stuff. I also left out the BUTTON_PIN and DEBUG1_PIN and DEBUG2_PIN stuff. One thing that isn't self-explanatory: the #define MSEC_CYCLES is the number of cycles in a MSEC, so if your clock frequency is 50MHz, this would be equal to 50000.<br />
<h3>
Functions</h3>
I did not port over the debug functions Hal_debugOn, Hal_debugOff, Hal_debugPulse.<br />
<br />
The Hal_Init() function is a big chunk of code, and should be done very carefully. Since I already have a hal_init function for the rest of the application, I renamed this BLE_Hal_init() so that it's clearer. I commented out a lot of the initialization stuff, but be sure to leave in the code that initializes the EAP TX & RX pins:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);<br /> /* These are the module's special flow-control functions */</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> GPIOPinTypeGPIOOutput(EAP_RX_ACK_PORT, EAP_RX_ACK_PIN);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> GPIOPinTypeGPIOInput(EAP_TX_ACK_PORT, EAP_TX_ACK_PIN);</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> IntEnable(INT_GPIOA);<br /> GPIOIntEnable(EAP_TX_ACK_PORT, EAP_TX_ACK_PIN);</span></span><br />
Also include the UART1 initialization, or whatever UART initialization you need. Be sure that it's configured properly.<br />
<br />
The last line of their Hal_init function won't allow us to compile. Temporarily comment it out; we'll come back to that later.<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">//FIXME: update this once the rest is working</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">//handlerTab[DISPATCH_HANDLER_ID] = Em_Message_dispatch;</span></span><br />
The FIXME tag will get automatically added to our list of tasks. This can be seen by adding the Tasks view. To do so navigate to: View : Other : General : Tasks and it should appear next to your problems tab.<br />
<br />
With that you should be able to compile the entire Hal.c file.<br />
<h3>
Event Handling</h3>
We're handling event handling a little differently. The Hal.c file has a function:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">void Hal_idleLoop(void)</span></span><br />
But we're doing it differently, so temporarily we skip over this.<br />
<h3>
LED Functions</h3>
Port the Hal_ledOn, off, read, toggle functions next. If you set up your #defines then you should just be able to copy/paste these functions.<br />
<h3>
Timer Function</h3>
The next function is a little funky:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">void Hal_tickStart(uint16_t msecs, void (*handler)(void))</span></span> <br />
The two parameters are:<br />
<ul>
<li>msecs - the number of milliseconds before the timer expires</li>
<li>handler - the function that will be called when the timer expires.</li>
</ul>
This works in conjunction with Hal_timerIsr(void) which is called by the timer when it expires. The first line in the function:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">handlerTab[TICK_HANDLER_ID] = handler;</span></span><br />
adds an entry to the event handler table that should be called when the timer expiry event is processed. For now I'm commenting it out so that I can complete the port of the rest of the file.<br />
<h3>
SRT-HAL Interface</h3>
I think the "SRT" refers to the module. I had to do a little bit of googling to find that it means "Schema Runtime" which is as self-explanatory as "SRT". Or not. At any rate, these functions are the ones that handle the serial communication. Here you can see the funky communications protocol that the module uses. When I copied these functions over, the compiler didn't like that the Em_Hal_reset function used Em_Hal_unlock before it was declared, so I moved Em_Hal_unlock above the Em_Hal_reset function. Then Em_Hal_startSend didn't like how Em_Message_startTx was used because I haven't yet included the other Emmoco files.<br />
<h3>
Interrupt Service Routines</h3>
The ISRs are short and to the point which is nice. I did not copy over the Hal_buttonIsr function since I have my own. The rest of these functions form the real meat and potatoes of the module interface. This includes the serial receive ISR as well as the ISR for the handshake from the module, Hal_txAckIsr. When I added these, it became clear that I needed to import the Emmoco files now.<br />
<h2>
Adding the Emmoco Files</h2>
Now that we have a compiling Hal file, lets import the Emmoco files. I simply copied over the Em directory from my FirstApp to my project directory. The first thing that I did was add in the line at the top of my Hal.c file:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#include "Em_Message.h"</span></span><br />
and tried to compile. Unfortunately it didn't work; the error<br />
<div style="text-align: center;">
<span style="font-family: inherit;"><span style="font-size: x-small;">cannot open source file "Em_Message.h"</span></span></div>
was displayed. Temporarily I fixed this by changing that line to:<br />
#include "../Em/Em_Message.h"<br />
But this is a hack. <br />
<br />
<h3>
Fixing the Interrupt Service Routines</h3>
When I ported the ISRs, there were a number of errors; some from the event handling, and one from an old Stellaris function that was used:<br />
GPIOPinIntClear(EAP_TX_ACK_PORT, EAP_TX_ACK_PIN);<br />
This has been replaced with:<br />
GPIOIntClear(EAP_TX_ACK_PORT, EAP_TX_ACK_PIN);<br />
<h3>
Should be able to compile now</h3>
Following the steps outlined above (and commenting out the few pieces of code) you should be able to compile now.<br />
<h2>
Copy over the Application Logic</h2>
Now, in your main.c file (or whatever file holds your application), add the folowing line:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">#include "Em/FirstApp.h"</span></span><br />
And compile again. You shouldn't have any errors.<br />
<br />
Over in Em-Browser, open your FirstApp-Prog.c file. We will be copying over some variables and functions from here. First, copy over the variables that are used:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">static FirstApp_psuTemperatureC_t psuTempC = 10;<br />static FirstApp_pcbTemperatureC_t pcbTempC = 10;<br />static FirstApp_level_t intensity = 10;<br />static FirstApp_colorTemperature_t colorTemperature = 5600;<br />static FirstApp_command_t powerState = FirstApp_TURN_OFF;</span></span><br />
Also, be sure to copy over the connectHandler and disconnectHandler functions; it turns out that the schema requires them.<br />
<h2>
Fix the Issues from Before</h2>
Previously we commented out some code. Now, go back in and uncomment each part, and be sure it compiles. Add the start() method - the last three lines of your main application should be something like below:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">/* The Hal_init() function for BLE stuff */ </span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> BLE_Hal_init();</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">/* Initialize the BLE module */<br /> FirstApp_start();</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">/* The event handler that never exits */<br /> eventHandler();</span></span><br />
<h2>
Add Interrupt Vectors</h2>
The Tiva line of microcontrollers uses a very cool interrupt vector table to tell the processor which function to call for a given interrupt. Open the startup code file that you're using (mine is startup_ccs.c) and add the following interrupt functions, replacing IntDefaultHandler with the name of the function.<br />
Hal_txAckIsr, // GPIO Port A <br />
Hal_rxIsr, // UART1 Rx and Tx <br />
Hal_timerIsr, // Timer 0 subtimer A<br />
<h2>
</h2>
<h2>
Troubleshooting</h2>
After I was able to compile the code it didn't work. So, to troubleshooting we go. First, double check your schematic connections, as far as what is an input and what is an output. Next hook up a logic analyzer and observe, and compare to the reference traces:<br />
http://wiki.em-hub.com/display/ED/Sample+Logic+Analyzer+Trace<br />
When I started, I wasn't seeing the RX_ACK pulses after each byte received from the module. Doing a bit of troubleshooting, I found that the uart receive interrupt was not properly configured. <br />
<br />
<br />
<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-54887420133525326512015-03-04T14:06:00.000-08:002015-03-04T14:06:00.544-08:00Fiducials in AltiumIn Printed Circuit Board design, <a href="https://en.wikipedia.org/wiki/Fiducial_marker#PCB" target="_blank">Fiducial</a> marks are used to align the pick and place machine with the PCB. A fiducial requires a circular area free of silkscreen and solderpaste, and in the middle of that area is a smaller circle exposing the top metal of the PCB.<br />
<br />
Creating these in Altium with keepouts can be problematic because it will create a keepout on both layers when typically you only want the keepout on the same layer as the fiducial.<br />
<br />
I recently <a href="http://www.eevblog.com/forum/altium/fiducials/" target="_blank">saw</a> a great solution for this: do it with a clearance rule. Create a Clearance design rule named "FiducialClearance" (or whatever you want to name it) that specifies the 26mil clearance
needed and is limited to just your fiducials. Create a new rule with the following:<br />
<span style="font-family: "Courier New",Courier,monospace;">HasFootprint('Fiducial-40mil') </span><br />Obviously, replace 'Fiducial-40mil' with the name of your fiducial footprint.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbT0r4CzPb3yEpk_7LHk1YzREqFHSwBXcOwnGkQBD9Ufczr9bMZ6-jsMzhYFdfvryz8bPdRojDr110NV2x3lXKnE6zHtMomBpayAdpzZYRaqz3G0JvQnwkJbqmSXzJ0KhRlANXM84KnB_w/s1600/Fiducial+Rule+Editor.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbT0r4CzPb3yEpk_7LHk1YzREqFHSwBXcOwnGkQBD9Ufczr9bMZ6-jsMzhYFdfvryz8bPdRojDr110NV2x3lXKnE6zHtMomBpayAdpzZYRaqz3G0JvQnwkJbqmSXzJ0KhRlANXM84KnB_w/s1600/Fiducial+Rule+Editor.PNG" height="256" width="320" /></a></div>
<br />
Finally change the priority of the "FiducialClearance" rule so that Priority=1.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDpIBEPkaRGbm554oVjUM_YU0qkf3IsGUgaDhw5FCnR-ObUAzk_AfHRjTCP_MnVmbi3Xd2g6sZP_wZnKXnZz2F6YkkdPZZ7SZ4nhQiyLjkQslWt8SbXM9MTeoaxBJ9lJafIzsoh7BVOM38/s1600/Fiducial+Rule+Priority.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDpIBEPkaRGbm554oVjUM_YU0qkf3IsGUgaDhw5FCnR-ObUAzk_AfHRjTCP_MnVmbi3Xd2g6sZP_wZnKXnZz2F6YkkdPZZ7SZ4nhQiyLjkQslWt8SbXM9MTeoaxBJ9lJafIzsoh7BVOM38/s1600/Fiducial+Rule+Priority.PNG" height="141" width="320" /></a></div>
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-6179422710326038982014-10-16T12:08:00.002-07:002014-10-16T12:08:42.438-07:00The Difference Between an Amateur Engineer and a Professional Engineer<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur grabs any old part from the parts bin and puts it into the board. A professional only uses parts that he has a valid part number for, since it will need to go into documentation somewhere.</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur assembles boards themselves. A professional only hand-builds if it will save money.</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur stores board revisions in zip files. A professional uses version control (like SVN).</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur doesn't pay attention to ESD when reworking boards. A professional uses proper grounding & wrist strap.</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur tries to solve all the problems himself. A professional coerces the manufacturer to help. :)</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur tests whether a design works. A professional tests how well the design works.</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur cares about the cost of the software tool. A professional cares about the productivity of the tool.</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur asks a question on a forum without RTFM. A professional asks a question on a forum after RTFM, usually when the documentation contradicts itself. :)</div>
<div style="background-color: white; color: #333333; font-family: Arial; font-size: 12px; line-height: 18px; top: 0px;">
An amateur doesn't care about software licensing. A professional can tell you when you can use GPL code vs. LGPL vs. BSD license.</div>
Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-17332203946502269162014-09-26T09:38:00.002-07:002018-03-14T11:56:11.099-07:00Demystifying Decoupling Capacitor PlacementDigital ICs require decoupling capacitors, to absorb the small current spikes from the switching behavior of the IC. Most of what we know about decoupling capacitor layout is "conventional wisdom", such as:<br />
<br />
1. Need a variety of capacitance values to decouple a wide frequency range<br />
2. Many capacitors of one value is better than many values<br />
3. Place caps close to ICs<br />
4. Location doesn't matter<br />
5. Spread caps across the entire board<br />
<br />
Most of our conventional wisdom is based on guidelines instead of empirical data. <br />
<br />
I found an excellent presentation which explains proper decoupling capacitor placement:<br />
https://ewh.ieee.org/r3/enc/emcs/archive/2012-10-10b_DecouplingMyths.pdfDerekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-55388563570612278362014-08-01T12:27:00.004-07:002014-08-01T12:27:53.802-07:00Using Texas Instruments Tiva Microcontroller Temperature SensorThe <a href="http://www.ti.com/tiva-c" target="_blank">Tiva</a> line of processors from <a href="http://www.ti.com/" target="_blank">Texas Instruments</a> has an internal temperature sensor. Code to do this is below. To use this,<br />
<ol>
<li>Call halTemperatureSensorInit() to initialize the sensor and ADC. Only needs to be done once.</li>
<li>Call halGetTemperature() to get the temperature in Celsius</li>
<li>Optionally, call halConvertTemperatureFromCtoF to convert it to Fahrenheit</li>
</ol>
Example code is below.<br />
<br />
/**<br />
* Tiva Microcontroller Internal Temperature Utility Functions<br />
*<br />
* @section license License<br />* This work is PUBLIC DOMAIN<br />* <br />* YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” <br />* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY <br />* WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO <br />* EVENT SHALL TESLA CONTROLS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, <br />* STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR <br />* INDIRECT DAMAGES OR EXPENSE INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, <br />* PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE <br />* GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY <br />* DEFENSE THEREOF), OR OTHER SIMILAR COSTS.<br />*/<br /><br />/**<br />
* Initialize the ADC for use by the temperature sensor.<br />
* @see TivaWare/examples/peripherals/adc/temperature_sensor.c<br />
* @post temperature sensor can be read with halGetTemperature()<br />
*/<br />
static void halTemperatureSensorInit()<br />
{<br />
/** Enable the ADC */<br />
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);<br />
<br />
#define ADC_SEQUENCE 3<br />
#define ADC_STEP 0<br />
<br />
/* Enable sample sequence 3 with a processor signal trigger.<br />
* Sequence 3 will do a single sample when the processor sends a signal to start the conversion. */<br />
ADCSequenceConfigure(ADC0_BASE, ADC_SEQUENCE, ADC_TRIGGER_PROCESSOR, 0);<br />
<br />
/* Configure step 0 on sequence 3:<br />
* - Sample the temperature sensor (ADC_CTL_TS) in single-ended mode (default)<br />
* - Configure the interrupt flag (ADC_CTL_IE) to be set when the sample is done<br />
* - Tell the ADC logic that this is the last conversion on sequence 3 (ADC_CTL_END). */<br />
ADCSequenceStepConfigure(ADC0_BASE, ADC_SEQUENCE, ADC_STEP, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);<br />
<br />
/* Enable the ADC Sequence we're using */<br />
ADCSequenceEnable(ADC0_BASE, ADC_SEQUENCE);<br />
<br />
/* Clear the interrupt status flag. This is done to make sure the interrupt flag is cleared before we sample. */<br />
ADCIntClear(ADC0_BASE, ADC_SEQUENCE);<br />
}<br />
<br />
<br />
/**<br />
* Read the processor's internal temperature sensor. Accuracy is +/-5C<br />
* @pre halTemperatureSensorInit() was called to initialize the ADC<br />
* @return temperature of processor in degrees Celsius<br />
* @note to convert: VTSENS = 2.7 - ((TEMP + 55) / 75)<br />
*/<br />
int32_t halGetTemperature()<br />
{<br />
/* This array is used for storing the data read from the ADC FIFO. It must be as large as the<br />
FIFO for the sequencer in use. This example uses sequence 3 which has a FIFO depth of 1. */<br />
uint32_t ulADC0_Value[1];<br />
<br />
/* Our output value */<br />
int32_t temperatureInDegreesC;<br />
<br />
/* Manually Trigger the ADC conversion */<br />
ADCProcessorTrigger(ADC0_BASE, 3);<br />
<br />
/* Wait for conversion to be completed */<br />
while(!ADCIntStatus(ADC0_BASE, 3, false))<br />
{<br />
}<br />
<br />
/* Clear the ADC interrupt flag. */<br />
ADCIntClear(ADC0_BASE, 3);<br />
<br />
/* Read ADC Value. */<br />
ADCSequenceDataGet(ADC0_BASE, 3, ulADC0_Value);<br />
<br />
//UARTprintf("ADC Value = %u\n", ulADC0_Value[0]);<br />
<br />
/* Reference voltage of 3.3V, in mV. */<br />
#define REFERENCE_VOLTAGE_MV (3300l)<br />
<br />
/* ADC is 12 bit resolution */<br />
#define NUMBER_OF_STEPS_12_BIT_RESOLUTION (4096l)<br />
<br />
/* The voltage from the ADC, in millivolts */<br />
uint32_t adcMv = (uint32_t) ((ulADC0_Value[0] * REFERENCE_VOLTAGE_MV) / NUMBER_OF_STEPS_12_BIT_RESOLUTION);<br />
<br />
/* Use non-calibrated conversion provided in the data sheet. Divide last to avoid dropout. */<br />
temperatureInDegreesC = (147500l - 75l * adcMv) / 1000;<br />
<br />
return temperatureInDegreesC;<br />
}<br />
<br />
<br />
/**<br />
* Utility function to convert a celsius temperature to fahrenheit.<br />
* @param temperatureInDegreesC the temp in C<br />
* @return the temp in F<br />
*/<br />
int32_t halConvertTemperatureFromCtoF(int32_t temperatureInDegreesC)<br />
{<br />
return ((temperatureInDegreesC * 9) + 160) / 5;<br />
}Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-61854535957315506392014-05-08T13:56:00.000-07:002014-05-08T13:56:17.854-07:00The Main Event - A Simple Event Handler for MicrocontrollersEvery application has some type of <a href="http://en.wikipedia.org/wiki/Event_handler" target="_blank">event handler</a>. Typically, an interrupt will occur, and the interrupt service routine (ISR) will then set a flag, and then the ISR will exit to return to the normal application. This approach is used to minimize the time spent in the ISR. Next, in the main application, the event flags are read, and if any were set then the application processes each event.<br />
<br />
I've been looking through the Anaren Bluetooth Low Energy software lately, and I saw this rather cool event handler. It uses a table of function pointers that are the functions to be called when the event occurs.<br />
<h2>
The Event Handler Functions</h2>
We will create a typedef for our function pointer. All functions in our Event Handler Table need to be this type. <br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">/* Typedef for the event handler functions that will go into our table */<br />typedef void (*Hal_Handler)(void);</span></span> <br />
Next, for convenience we also create a type for the event flag bitmask. For speed, make this the same as the processor's data size. <br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">/* The type of integer that we will use for our event bitflag. uint32 = 32 possible events */<br />typedef uint32_t EventFlags;</span></span><br />
<span style="font-size: small;"><span style="font-family: inherit;">We also create the global variable that will be the bitflag of any events that get set.</span></span><br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">/* Bitflag of the events that need to be processed */<br />static volatile EventFlags handlerEvents = 0;</span></span><br />
To complete the initialization, we define a few events, and create our event handler table.<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">#define NUM_HANDLERS 3<br />#define BUTTON_HANDLER_ID 0<br />#define TICK_HANDLER_ID 1<br />#define DISPATCH_HANDLER_ID 2</span></span><br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">#define KNOB_INCREMENT_HANDLER_ID 3</span></span><br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;"><span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">#define KNOB_DECREMENT_HANDLER_ID 4</span></span></span></span><br />
<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">/* Table of handler function pointers */<br />static Event_Handler handlerTable[NUM_HANDLERS];</span></span><br />
This table will contain function pointers for the function that gets called when the corresponding event gets called. For example, when the knob gets rotated clockwise, its ISR will set bit #3 in the handlerEvents function. Then the main loop will call the function that is located at handlerTable[3].<br />
<br />
We initialize our list by adding event handlers to the event handler table using the method below:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> int addEventHandler(uint8_t index, Event_Handler handler)<br /> {<br /> if (index > NUM_HANDLERS)<br /> {<br /> return -1;<br /> }<br /> handlerTable[index] = handler;<br /> return 0;<br /> }</span></span><br />
We then call this in our main() function, which we'll explain in a bit.<br />
<h2>
Set a flag when an event occurs</h2>
When an event happens, in the ISR we need to set the bitflag that corresponds to that event. This is done in the postEvent() method as shown below. This bitflag will be read later in our main event handling loop. For safety, we disable interrupts while we are modifying the <span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">handlerEvents </span></span>variable.<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">/** Set the corresponding bitflag for the eventId */<br />void postEvent(uint8_t handlerId) <br />{<br /> //Note: you should check that handlerId < NUM_HANDLERS here<br /> IntMasterDisable();<br /> handlerEvents |= 1 << handlerId;<br /> IntMasterEnable();<br />}</span></span><br />
In the ISRs, you will need to add a call to postEvent. For example, in a button ISR, add:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">postEvent(BUTTON_HANDLER_ID);</span></span><br />
<h2>
Configuring The Actions</h2>
When the bitflag for an event gets set, we want the event handler to do something. It does so by calling a function pointer that points to the function that we want to get called when that event happens. For example, when a button is pressed we just want to tell the user. Same for knob, as shown below:<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;">void buttonHandler(void)<br />{<br /> printf("Button Pressed!\r\n");<br />}<br />void knobIncrementHandler(void)<br />{<br /> printf("Knob Up!\r\n");<br />}<br />void knobDecrementHandler(void)<br />{<br /> printf("Knob Down!\r\n");<br />}</span></span><br />
Now, we need these in the Event Table so in main() we add them with the function we created earlier:<br />
<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"> addEventHandler(BUTTON_HANDLER_ID, buttonHandler);<br /> addEventHandler(KNOB_INCREMENT_HANDLER_ID, knobIncrementHandler);<br /> addEventHandler(KNOB_DECREMENT_HANDLER_ID, knobDecrementHandler);</span></span><br />
Now we've configured the event setting, and the event processing, so now we can implement the main event handler.<br />
<h2>
The Main Event (handler)</h2>
The Main Event Handler is fairly simple. It continually loops, and processes any events that were set by postEvent(). If a bitflag was set, then it calls the corresponding function pointer.<br />
<span style="font-family: "Courier New", Courier, monospace;"><span style="font-size: x-small;">void eventHandler(void)<br />{<br /> IntMasterEnable();<br /> for (;;)<br /> {<br /> /* First, disable interrupts so we don't get messed up while we are reading events */<br /> IntMasterDisable();<br /><br /> /* While interrupts are disabled, copy the current list of events that need to be processed */<br /> EventFlags events = handlerEvents;<br /><br /> /* ... and clear the master bitflag of events */<br /> handlerEvents = 0;<br /><br /> /* Now, process any events that need to be processed */<br /> if (events)<br /> { // dispatch all current events<br /> IntMasterEnable(); // Enable interrupts - note that we do this before calling each handler function<br /> /* mask will be the bitflag that we use to check the events bitflag. Starts at 0x1, then 0x2, 0x4, 0x8 etc. */<br /> uint16_t mask;<br /> /* The numeric index of the event - 0 through 31 */<br /> uint8_t id;<br /> /** Iterate through all possible events */<br /> for (id = 0, mask = 0x1; id < NUM_HANDLERS; id++, mask <<= 1)<br /> {<br /> if ((events & mask) && handlerTable[id]) // If we need to process the event<br /> { // AND there is an event handler for it<br /> handlerTable[id](); // ... Then process the event<br /> }<br /> }<br /> } else { // No events to handle, so wait for more<br /> /* If using a battery powered device, you could go back to sleep here. Just be sure to enable interrupts! */<br /> IntMasterEnable(); // Enable Interrupts<br /> }<br /> }<br />}</span></span><br />
<br />
<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-57371670671815696732014-05-02T16:18:00.003-07:002014-05-02T16:18:58.245-07:00Using Bluetooth Low Energy to Do Something<br />
In the <a href="http://fixituntilitsbroken.blogspot.com/2014/05/get-started-with-bluetooth-low-energy.html">previous post</a> we looked at how to get up and running with Bluetooth Low Energy. Now we're going to do something useful. This section assumes that you have completed the previous tutorial successfully.<br />
<h2>
Load on-line Help</h2>
First, we want to load the on-line help for the FirstApp example. Open Em-Builder and select Help > Em-Builder > Primer Contents and you'll see a number of lessons available:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjohOJVSN9o0VgvnW31XcR9QaUnNAbmVY4UlxcqHSOTuExithr92XGLKKP28ESf3zfsoAd2PjfRcH7ivboFHmYDnALJgMdC_dKVCMK2zvDsQhMUUKfZKGGp7WXsO_zU1BTkugm8w2dSAu-f/s1600/Primer+Contents.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjohOJVSN9o0VgvnW31XcR9QaUnNAbmVY4UlxcqHSOTuExithr92XGLKKP28ESf3zfsoAd2PjfRcH7ivboFHmYDnALJgMdC_dKVCMK2zvDsQhMUUKfZKGGp7WXsO_zU1BTkugm8w2dSAu-f/s1600/Primer+Contents.PNG" height="200" width="146" /></a></div>
Select "Lesson 01 - Your First App" and the workspace will change to load the on-line help:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQuQIBndV-CMoNgzH-r2smZCXXo46V_m0-vAQRqIWLVjv8P69OAoQEhf-DM8FWS_3CO47jcIRVY4cHPNw_eDIBwCIdWkxfAq_GlShTFn38FBoH9KUekWzfAwTjT763PFZkcP81mRqAmrC-/s1600/FirstApp.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQuQIBndV-CMoNgzH-r2smZCXXo46V_m0-vAQRqIWLVjv8P69OAoQEhf-DM8FWS_3CO47jcIRVY4cHPNw_eDIBwCIdWkxfAq_GlShTFn38FBoH9KUekWzfAwTjT763PFZkcP81mRqAmrC-/s1600/FirstApp.PNG" height="240" width="320" /></a></div>
<h2>
Run Unmodified FirstApp</h2>
Just like you did for Blinker, now right-click on the FirstApp project in the left hand column and select "Run Em-Builder Example". If you had an error, be sure that your development board is attached, and try the Em-blinker example too. Now open Em-Browser on your iPhone and connect to your development board. You should be able to read and write a variable with the unoriginal name of "data". Fun.<br />
<h2>
Customizing FirstApp</h2>
The first thing that we're going to do to get our feet wet with the Emmoco software is to do something very simple: change the name of the "data" variable to "pcbTemperature". This will give us a good exposure to what Emmoco uses to communicate. Make the change in the schema, FirstApp.ems.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgECJa601ZYmM5R-NY9NOi2bDDn-5G5K9vczn-vZ6L9EKehIsKUrpJRrJMm4hhWCsh9tcqVrYwvM4eQudFrsmDTAzTJPektRG_y9wM1_ycHuvRPC7f7S1YE0bij6oPXZ3IeWdD5jibY9DZD/s1600/FirstApp+schema+changing+name+of+data.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgECJa601ZYmM5R-NY9NOi2bDDn-5G5K9vczn-vZ6L9EKehIsKUrpJRrJMm4hhWCsh9tcqVrYwvM4eQudFrsmDTAzTJPektRG_y9wM1_ycHuvRPC7f7S1YE0bij6oPXZ3IeWdD5jibY9DZD/s1600/FirstApp+schema+changing+name+of+data.PNG" height="232" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmgD771KxI5yGCjfXMYpoMXUpOVoJb2_Gua22m1gkvVy3nrBPXamxf95BLaHiuiOY2m3LlsIFvrKpfdSQg1bnx1LyoSbUx-Qs-siwqczsZIVJl0UriH9qniKUaf8Uz6adI80Al5MGzJNK8/s1600/2Connecting+to+EDB.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
If you try to build it, then you will get an error, complaining of an unknown type name.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsrx7tGNC-4V8P201IX5uc3TQXZDdYOMGq17ZR9nJ6gArKbzuVOqphS3Nv8v5B83dBe2jUYrbuER2AIweC73mN207uk3BuG06Y1nTpmkjmrTe11htO0Y_etr2lG-3O6VLtPfLFPftfVB8j/s1600/FirstApp+-+Closeup+of+Changing+name+-+error+because+no+typename.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsrx7tGNC-4V8P201IX5uc3TQXZDdYOMGq17ZR9nJ6gArKbzuVOqphS3Nv8v5B83dBe2jUYrbuER2AIweC73mN207uk3BuG06Y1nTpmkjmrTe11htO0Y_etr2lG-3O6VLtPfLFPftfVB8j/s1600/FirstApp+-+Closeup+of+Changing+name+-+error+because+no+typename.png" height="98" width="320" /></a></div>
<br />
Now in the schema (FirstApp.ems) change the name of the variable "data". This will cause the build to fail, because we need to change all the other variables. If you go to FirstApp.h then you can see declarations of the new typedef and functions.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYjCuVJXXcJCvJqa7aWsgTzeE4L3l6oLTSaolRq4d2hSjuZ9uNz1buQBU9XQRldUkHxgUwZk1HTscsnAZEb5K-SZRGDX9bgyqpBtIvzcPjSN63GnTxZfKAvZUZw9P3zValVgUbH2uMy-nE/s1600/Firstapp+-+renamed+typedef+and+callback+functions.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYjCuVJXXcJCvJqa7aWsgTzeE4L3l6oLTSaolRq4d2hSjuZ9uNz1buQBU9XQRldUkHxgUwZk1HTscsnAZEb5K-SZRGDX9bgyqpBtIvzcPjSN63GnTxZfKAvZUZw9P3zValVgUbH2uMy-nE/s1600/Firstapp+-+renamed+typedef+and+callback+functions.png" height="57" width="320" /></a></div>
Now, make these changes to FirstApp.c<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsAh38yC6ADV31zdFK7ueZ5ELvWH-LczGi1GGNdbU1JXyvIOzz2toNtKRkbsrAioD5k479wrH2Yts_clE059cj6XGl7O9QGW1qSs9iLo5EO5dEWguHCKlZrD8cCt7cm7WitTTjBg2NHxfJ/s1600/FirstApp+after+renaming.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsAh38yC6ADV31zdFK7ueZ5ELvWH-LczGi1GGNdbU1JXyvIOzz2toNtKRkbsrAioD5k479wrH2Yts_clE059cj6XGl7O9QGW1qSs9iLo5EO5dEWguHCKlZrD8cCt7cm7WitTTjBg2NHxfJ/s1600/FirstApp+after+renaming.PNG" height="219" width="320" /></a></div>
Now that we've renamed the variables, we should be able to build successfully. Run the example (Right-click on project and select "Run Em-Builder Example") and it will be loaded on the development board. Using Em-Browser you should be able to read and write a resource named pcbTemperature.<br />
<h2>
Do Something When We Receive a New Value</h2>
Ok, now we're rolling.<br />
<br />
Now, just for fun, we modify the function pcbTemperatureC_store() as shown below to blink LED twice whenever we get a new value.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7DVkmGpiLw8oYiGOtIBnLO5OPD6iWcq41cq0tVbpsFHgMDDswSrc8JMTBbFg5zsFiJDUqZ3JlkP0D6O1Rjmf5YWNIUj4YCjmxhFMmIrAL86CK5BIQavSbUfXzFzildeDLHLkHwQt6z_qv/s1600/FirstApp-Prog-Modified+to+blink+LED+twice.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7DVkmGpiLw8oYiGOtIBnLO5OPD6iWcq41cq0tVbpsFHgMDDswSrc8JMTBbFg5zsFiJDUqZ3JlkP0D6O1Rjmf5YWNIUj4YCjmxhFMmIrAL86CK5BIQavSbUfXzFzildeDLHLkHwQt6z_qv/s1600/FirstApp-Prog-Modified+to+blink+LED+twice.PNG" height="232" width="320" /></a></div>
Now, in Em-Browser, write a value to pcbTemperature and you'll see the LED blink twice. Cool! Too bad we don't have printf() here though; it would be nice to be able to see it.<br />
<h2>
Adding Another Variable</h2>
Now we want to add another variable. I looked through the schema examples and saw that there's a 'num' type where you can set the range and interval. This is useful so that the phone application will only send us valid values. We want to control a light, so we add a variable, called 'level'. This can vary between 0% (totally off) and 100% (totally on), in steps of 1 percent each.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRC3KqFNf4eyLdwbHO4SaIs5g7s7XOqbzs72w2vCTiFReig0amhccNSWNiMDUwL4RgdFl_5TAob3dAMHohVz1-RToWonu3IYD1R6Rarxbf5XCyZGD1aSCUccPUxSiZ3b0Vodj-9bUTKuIF/s1600/Adding+another+variable+to+the+schema.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRC3KqFNf4eyLdwbHO4SaIs5g7s7XOqbzs72w2vCTiFReig0amhccNSWNiMDUwL4RgdFl_5TAob3dAMHohVz1-RToWonu3IYD1R6Rarxbf5XCyZGD1aSCUccPUxSiZ3b0Vodj-9bUTKuIF/s1600/Adding+another+variable+to+the+schema.PNG" height="232" width="320" /></a></div>
Now right click on the project and select"Build Project". It will report a few errors but more importantly it will automatically generate the typedefs and function declarations that we need. This will make our lives easier.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7HKO9Jh-ZJDV3wcAkX_dSWoMP23ngn4-gxSj9tueQn15NS92EOrWlLHAbSxBiCxlTXB9f6mK5F8099JNHNGDQ8xdZn4F4f_Tq3GdmsTl-izK3ASXfqTg9COcZiTLi4mTRp9p9XJaZ4PPx/s1600/FirstApp.h+automatically+generated+our+function+declarations+and+types.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7HKO9Jh-ZJDV3wcAkX_dSWoMP23ngn4-gxSj9tueQn15NS92EOrWlLHAbSxBiCxlTXB9f6mK5F8099JNHNGDQ8xdZn4F4f_Tq3GdmsTl-izK3ASXfqTg9COcZiTLi4mTRp9p9XJaZ4PPx/s1600/FirstApp.h+automatically+generated+our+function+declarations+and+types.PNG" height="232" width="320" /></a></div>
Here we can see that the Emmoco framework created a bunch of new stuff for us, including the following. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIng3Rm-W8Sbvx4vO0dHCCXb02y68kaHBEaTxLOFIPvSzt0Uthhe_5_2ZayUUHck9bSvmp-WS_zpV4gqoI-JKQODjlhDovpnOhhWs_ma2MohZR-bVxvDO11HvvGt2YQkbo_btZLCBIuxJ-/s1600/FirstApp.h+close+up+on+generated+types+and+function+declarations.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIng3Rm-W8Sbvx4vO0dHCCXb02y68kaHBEaTxLOFIPvSzt0Uthhe_5_2ZayUUHck9bSvmp-WS_zpV4gqoI-JKQODjlhDovpnOhhWs_ma2MohZR-bVxvDO11HvvGt2YQkbo_btZLCBIuxJ-/s1600/FirstApp.h+close+up+on+generated+types+and+function+declarations.png" height="92" width="320" /></a></div>
The min, max, step, and scale values are there because in the schema we
defined our variable as a number with range of 0 to 100 and in steps of
1. We can copy/paste the function declarations into our application to
make life easier. Now we need to add these to our application.<br />
<br />
In FirstApp-Prog.c, add a variable:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">static FirstApp_level_t intensity = 10;</span></span><br />
Also add two new functions:<br />
<span style="font-size: x-small;"><span style="font-family: "Courier New",Courier,monospace;">/* Send resource TO phone */<br />void FirstApp_level_fetch(FirstApp_level_t* output)<br />{<br /> *output = intensity;<br />}<br /><br />/* Receive new value FROM phone */<br />void FirstApp_level_store(FirstApp_level_t* input)<br />{<br /> intensity = *input;<br />}</span></span><br />
<h2>
Run the Example</h2>
Now, try building again - 'Run Em-Builder Example'. You shouldn't have any errors.<br />
Now open Em-Browser on the iPhone. You can now see that we have a new resource, called Level, and we can write to it and read it.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivVkdjFIQcIHbq7t3q_nm9WRGaoLbu6oZzN30CtiC7MnBgnLYXZk3PRVXc_I2dCXaPoz1GYDmUevxClAV2F_xavwT874020SiKwvQAs2Tndb2Suc6IPBMmiYXsSHQ2o9EOTvXrBhr_2Amn/s1600/FirstApp+resources.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivVkdjFIQcIHbq7t3q_nm9WRGaoLbu6oZzN30CtiC7MnBgnLYXZk3PRVXc_I2dCXaPoz1GYDmUevxClAV2F_xavwT874020SiKwvQAs2Tndb2Suc6IPBMmiYXsSHQ2o9EOTvXrBhr_2Amn/s1600/FirstApp+resources.png" height="200" width="133" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
Clicking on 'level' shows us more information about it. Note that if you try to write a level above 100 then it will be limited to 100. </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTJ2x8-dxCSjLqcNQkNnCyoDwBFMH0Y6mICL0NCWXwsAgToIYb7kgbuwDHCkG1fb875_v1HgJnnMpSCni5b9sCezggZc7UwV-u26mAwGOf2VjfI39KYxom37MGatz6drvV6IjLd5OwxV0B/s1600/level+resource+details.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTJ2x8-dxCSjLqcNQkNnCyoDwBFMH0Y6mICL0NCWXwsAgToIYb7kgbuwDHCkG1fb875_v1HgJnnMpSCni5b9sCezggZc7UwV-u26mAwGOf2VjfI39KYxom37MGatz6drvV6IjLd5OwxV0B/s1600/level+resource+details.png" height="200" width="133" /></a></div>
<br />
<br />
<h2>
Conclusion</h2>
In this tutorial we demonstrated how to modify one of their examples to do something useful and how to add another variable.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0San Diego, CA 92122, USA32.8563846 -117.2029363000000332.8030306 -117.28361730000003 32.9097386 -117.12225530000003tag:blogger.com,1999:blog-3090119043458623342.post-57257844094228895462014-05-02T15:17:00.000-07:002014-05-02T16:09:12.528-07:00Get Started with Bluetooth Low Energy This tutorial will show you how to develop an application with Bluetooth Low Energy and do something useful. Going through all the steps in this post will take between
30-60 minutes, depending on if you run into any issues.<a href="http://en.wikipedia.org/wiki/Bluetooth_low_energy" target="_blank"> Bluetooth Low Energy</a> (aka BLE) is all the rage right now, and rightfully so, as it is an easy to use low power RF communications protocol. All the new iPhones and some Android phones incorporate a BLE radio, so you can now easily design RF peripherals to communicate with the iPhone.<br />
<br />
I like to use a module for BLE because it is easier and less expensive for
quantities less than 10k. For this post I'm trying out the <a href="http://www.anaren.com/air/products/air-bluetooth-smart-apps" target="_blank">Anaren BLE Module</a> which is available on a Development kit from <a href="http://www.digikey.com/product-search/en/rf-if-and-rfid/rf-evaluation-and-development-kits-boards/3539644?k=A2541" target="_blank">Digikey</a> for $60. You will also need a Texas Instruments Launchpad. I'm using the TI <a href="http://www.ti.com/tool/ek-tm4c123gxl" target="_blank">Tiva Launchpad</a> which available from <a href="http://www.digikey.com/product-detail/en/EK-TM4C123GXL/296-35760-ND/3996736" target="_blank">Digikey</a> for about $14. These are so cheap that I recommend buying two. Below is a picture of the Anaren Board mounted on the Tiva Development Board.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb0uQ4n-a3PTualhhyImIfVvmCi-jKQClYOkxKicA_UlU-gTspphfEItN6IWCnkCfOJyapoqHjpLaf5B36EgXeW8rWAstq-UqjoYITxmOlMKuwubwM2GZiR-MyNlitmNqfxXCwH8EZi6QO/s1600/BLE+on+Tiva2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb0uQ4n-a3PTualhhyImIfVvmCi-jKQClYOkxKicA_UlU-gTspphfEItN6IWCnkCfOJyapoqHjpLaf5B36EgXeW8rWAstq-UqjoYITxmOlMKuwubwM2GZiR-MyNlitmNqfxXCwH8EZi6QO/s1600/BLE+on+Tiva2.jpg" height="291" width="320" /></a></div>
For this tutorial, you will need:<br />
<ul>
<li>Anaren BLE Development Kit</li>
<li>Mini-USB cable for above (comes with the kit) </li>
<li>Texas Instruments Tiva Launchpad</li>
<li>Micro-USB cable for above (comes with the kit)</li>
<li>iPhone, 4S or later to run phone examples </li>
<li>iPhone apps "Em-Blinker" & "Em-Browser", available for free from the App Store</li>
<li>Basic knowledge of C </li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
</div>
<h2 class="separator" style="clear: both; text-align: left;">
Hardware Connections</h2>
<div class="separator" style="clear: both; text-align: left;">
First, mate the two boards as shown above. Next, connect the Tiva's "Debug" USB port to your computer. Be sure that the slider switch is in the "DEBUG" position. Finally connect the "Admin UART" USB port on the Anaren Board to your computer.</div>
<h2 class="separator" style="clear: both; text-align: left;">
Get the Software</h2>
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlBWCXBUc7rE-jZ7kACHTZztlOdNAoko_niZHiq2tXd1Pt-mYPnvL0A-8lMkbnW6pSjZ-y0NjbXggFf6S6CMgiAhDxr6PaV2vMRwMrf1rUb8iyLB80V29omE3J4t4zxCyr_s3ZALdpiGV2/s1600/Em-Builder+Downloads.PNG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlBWCXBUc7rE-jZ7kACHTZztlOdNAoko_niZHiq2tXd1Pt-mYPnvL0A-8lMkbnW6pSjZ-y0NjbXggFf6S6CMgiAhDxr6PaV2vMRwMrf1rUb8iyLB80V29omE3J4t4zxCyr_s3ZALdpiGV2/s1600/Em-Builder+Downloads.PNG" height="129" width="320" /></a>The Anaren module uses a framework from Emmoco to make development easier. Go to <a href="http://www.em-hub.com/">www.em-hub.com</a>. You will need to create an account to be able to download the software. Click on the "Sign-Up" link. The access code that it wants is printed on a sticker on the Anaren board. It is on the black connector, on the far left of the above image. Enter that code and your other information. Next, click on the "Downloads" link and select v13, and then "Em-Builder IDE Container" for your operating system. </div>
<h2 class="separator" style="clear: both; text-align: left;">
Install the Software</h2>
After it downloads, install the software. It will also install some drivers too. When you first start the application it will look rather empty, like below.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJWpHCkKhcvn2AmeFexGLUetR0mq7bv5nOh2n3MZYfIbax8jK97VfZlHW-HsLNXRRu2vbpg5JRo0t3JxX_OqBoITy4HeZlBqN05WI6L8BxsMImFAF3jPegvrEcnHIqVahxA5pbrXC2PBGI/s1600/em-builder+start.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJWpHCkKhcvn2AmeFexGLUetR0mq7bv5nOh2n3MZYfIbax8jK97VfZlHW-HsLNXRRu2vbpg5JRo0t3JxX_OqBoITy4HeZlBqN05WI6L8BxsMImFAF3jPegvrEcnHIqVahxA5pbrXC2PBGI/s1600/em-builder+start.PNG" height="240" width="320" /></a></div>
Now we'll follow along the instructions in the left pane. Select Help > Em-Builder > Em-Hub Credentials and enter the same username and password you used to create your account.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtR71ZgVmI8cA3LSmEn47ffn409xm0kXrb2ThZgneeJEwjiupta8-MBKUl17Hv_SXHtY66UXWCKH4szjK5INiJ-RYeUILiaq0HgVhFebjLMOeYqTQrN1iiBaMO9GhuSQEWyCnPpf08bCJ4/s1600/em-builder+credentials.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtR71ZgVmI8cA3LSmEn47ffn409xm0kXrb2ThZgneeJEwjiupta8-MBKUl17Hv_SXHtY66UXWCKH4szjK5INiJ-RYeUILiaq0HgVhFebjLMOeYqTQrN1iiBaMO9GhuSQEWyCnPpf08bCJ4/s1600/em-builder+credentials.PNG" height="106" width="200" /></a></div>
<h2>
Update Development Board Firmware</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTwR4c5dpYeB43chLvvV_JI9a_hgUNqvmtOfer7uETTPXoOyIZcJQTPLwqAiaJKIXjSJIR0NeNf43M2qleJwc4cnywMiCQoYTzMWpIcfqgX1Ma6-KOfgiqXYNTQHwlWkd7x5V4gu7pIcwf/s1600/EDB+Administration.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTwR4c5dpYeB43chLvvV_JI9a_hgUNqvmtOfer7uETTPXoOyIZcJQTPLwqAiaJKIXjSJIR0NeNf43M2qleJwc4cnywMiCQoYTzMWpIcfqgX1Ma6-KOfgiqXYNTQHwlWkd7x5V4gu7pIcwf/s1600/EDB+Administration.PNG" height="278" width="320" /></a></div>
Next, you'll connect to the board and update the firmware. The hardest part on this step is figuring out which serial port is used for the "UART-ADMIN" port. I tried each of them but figured out finally that it was COM75 on mine. If the application is showing an error, then one thing to try is to press the "MCU Reset SOC" button on the Anaren Board; that allowed the two devices to connect. This took me awhile to figure out. <br />
<br />
Once you have updated the firmware, it will be happy:<br />
If you want, you can also change the name of the device here. Call it "SHAMU" or whatever you would like as long as it's all upper case. Now we're done updating the module firmware. If you want, you can remove the USB cable from the "ADMIN UART" port now.<br />
<h2>
Get the Example Projects</h2>
Now we need to import some examples so that we can start playing with them. GO to File > Import > Em-Builder > Example Projects, and select EK-TM4C123GXL as shown below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAC2kO3vrDVhvxfosP7qP8oN5wcNgcJY0FmHxwiHI-OoZCZGNpmABtDv5lIjhYTqggfydmmt57EkTjAyTY3sqWApioZLcrn0XVlRqkyJ82evMD5QkU4rSyL42X9HYFb0KMXWCO7c018DSc/s1600/import+Tiva+projects.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAC2kO3vrDVhvxfosP7qP8oN5wcNgcJY0FmHxwiHI-OoZCZGNpmABtDv5lIjhYTqggfydmmt57EkTjAyTY3sqWApioZLcrn0XVlRqkyJ82evMD5QkU4rSyL42X9HYFb0KMXWCO7c018DSc/s1600/import+Tiva+projects.PNG" height="200" width="190" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5_RQpD6uPc0nsMgrIbnszP76dynFTFaGgiN6iO4qb1RA0Rc3hMLQPnjg_avzTXOIK6genxpqfiLxvPIlbhHpogxG5otuUaJST0mZB0BuIRlR8lIFsmbmYojbOPygJDoMQU14mEDAZ_hT/s1600/import+projects.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5_RQpD6uPc0nsMgrIbnszP76dynFTFaGgiN6iO4qb1RA0Rc3hMLQPnjg_avzTXOIK6genxpqfiLxvPIlbhHpogxG5otuUaJST0mZB0BuIRlR8lIFsmbmYojbOPygJDoMQU14mEDAZ_hT/s1600/import+projects.PNG" height="200" width="190" /></a></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5_RQpD6uPc0nsMgrIbnszP76dynFTFaGgiN6iO4qb1RA0Rc3hMLQPnjg_avzTXOIK6genxpqfiLxvPIlbhHpogxG5otuUaJST0mZB0BuIRlR8lIFsmbmYojbOPygJDoMQU14mEDAZ_hT/s1600/import+projects.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"></a><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGe2-H5M4u2LEwHKOWJIKL5wLaetMdjfaSIbTO_55dVzeJWb1Rkb75UVYN4RLmhohaRK_DCT-VElhn31XNmE3THtXShjRwJlhNCOiD6pQmysV72Lyyu90qFKWjPld8KFU0IS8fAlk3r2eq/s1600/import+all+projects.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGe2-H5M4u2LEwHKOWJIKL5wLaetMdjfaSIbTO_55dVzeJWb1Rkb75UVYN4RLmhohaRK_DCT-VElhn31XNmE3THtXShjRwJlhNCOiD6pQmysV72Lyyu90qFKWjPld8KFU0IS8fAlk3r2eq/s1600/import+all+projects.PNG" height="200" width="190" /></a></div>
<br />
Now, the application should have a few example projects:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqra7dgWHQ3oYuXDroUqtMmkp_8menTozu5hiY0Akh21v-0vWNYaajq1bttOZRGnkFBdkWXeIvGvwg2X6nZGIcG2QHoWT3KdhUP56KrjJdL044kNCTTcqbIelHvEmog5eJMiDG9KXatlJy/s1600/All+projects.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqra7dgWHQ3oYuXDroUqtMmkp_8menTozu5hiY0Akh21v-0vWNYaajq1bttOZRGnkFBdkWXeIvGvwg2X6nZGIcG2QHoWT3KdhUP56KrjJdL044kNCTTcqbIelHvEmog5eJMiDG9KXatlJy/s1600/All+projects.PNG" height="240" width="320" /></a></div>
<h2>
Running an Example Application</h2>
Now we are ready to do something useful. Be sure that the USB cable to the Tiva board is connected to your computer. Right-click on the "Blinker-EK-TM4C123GXL" example and select "Run Em-Builder Example". It should compile and build, as shown below.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzSmHjOsWv7qw8u4k29rhA7nc_j71gcqdoM5dy7Ylkn9dsWvaf6fNi8BEFUQry9nl4PpV9EUSL4dk6wJQyHGcDiMOKkN6KmBmDfoYdHE9XNc_f8R5GuMc7YgllijEgesfbyYmcwIDGJKs2/s1600/Blinky+built.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzSmHjOsWv7qw8u4k29rhA7nc_j71gcqdoM5dy7Ylkn9dsWvaf6fNi8BEFUQry9nl4PpV9EUSL4dk6wJQyHGcDiMOKkN6KmBmDfoYdHE9XNc_f8R5GuMc7YgllijEgesfbyYmcwIDGJKs2/s1600/Blinky+built.PNG" height="193" width="320" /></a></div>
Now, open the "Em-Blinker" application on your phone. It should look something like below:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1BHoTVOkj7kLJoboAxcyqTMDHqSAFQvLjm4M16QDtDkPcUaAoBNXXRkUb8h6-fa2GbVx9baNVfu7twpPA0O9Ax-MeK_1Fq8zIMwm90VJs6kFDb3NI9yCKCw_egDRJte20zmsUEHDK3WWM/s1600/Em-Blinker.jpeg" height="320" width="180" /></div>
It doesn't do anything yet, you need to tell it to connect to your board. Press the button in the upper right corner to select the board. Previously we named our board "SHAMU" so we select that one. On the Tiva board, the Red LED should illuminate to indicate that it is connected to a phone.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgli1P9XY8m9n_hmKUOhb3XErfSh05ITjlIN4s7w-OspoThsKBECXij2uPcRNs-Bl0pIzKKecedN6vUIcXZ1Nfk1tUiLYMlTppJKZfI263b34Mst_I5ngFtA2pgPvIjmc0kwGmWmQ9xpsyf/s1600/image.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgli1P9XY8m9n_hmKUOhb3XErfSh05ITjlIN4s7w-OspoThsKBECXij2uPcRNs-Bl0pIzKKecedN6vUIcXZ1Nfk1tUiLYMlTppJKZfI263b34Mst_I5ngFtA2pgPvIjmc0kwGmWmQ9xpsyf/s1600/image.png" height="320" width="213" /></a></div>
Now, press "Start" or "On" depending on your version of Em-Blinker, and you should now see the blue LED on the Tiva board blink.<br />
<h2>
Conclusion</h2>
In this tutorial we started from zero and ended with a phone connected to our embedded device. Next time we'll look at making our own application.
<!-- Blogger automated replacement: "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5_RQpD6uPc0nsMgrIbnszP76dynFTFaGgiN6iO4qb1RA0Rc3hMLQPnjg_avzTXOIK6genxpqfiLxvPIlbhHpogxG5otuUaJST0mZB0BuIRlR8lIFsmbmYojbOPygJDoMQU14mEDAZ_hT/s1600/import+projects.PNG" with "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5_RQpD6uPc0nsMgrIbnszP76dynFTFaGgiN6iO4qb1RA0Rc3hMLQPnjg_avzTXOIK6genxpqfiLxvPIlbhHpogxG5otuUaJST0mZB0BuIRlR8lIFsmbmYojbOPygJDoMQU14mEDAZ_hT/s1600/import+projects.PNG" --><!-- Blogger automated replacement: "https://images-blogger-opensocial.googleusercontent.com/gadgets/proxy?url=http%3A%2F%2F3.bp.blogspot.com%2F-kClyi_Awa_4%2FU2KnvHFJffI%2FAAAAAAAAAi4%2Fyzr16bHuj3E%2Fs1600%2Fimport%2Bprojects.PNG&container=blogger&gadget=a&rewriteMime=image%2F*" with "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhL5_RQpD6uPc0nsMgrIbnszP76dynFTFaGgiN6iO4qb1RA0Rc3hMLQPnjg_avzTXOIK6genxpqfiLxvPIlbhHpogxG5otuUaJST0mZB0BuIRlR8lIFsmbmYojbOPygJDoMQU14mEDAZ_hT/s1600/import+projects.PNG" -->Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0San Diego, CA 92122, USA32.8563846 -117.2029363000000332.8030306 -117.28361730000003 32.9097386 -117.12225530000003tag:blogger.com,1999:blog-3090119043458623342.post-23083397225802417832014-04-30T10:28:00.001-07:002014-04-30T10:40:48.971-07:00The Easy Way to Use Thermistors<br />
A <a href="http://en.wikipedia.org/wiki/Thermistor" target="_blank">Thermistor </a>is a resistor that varies its values with temperature. These are fairly inexpensive and easy to use with a microcontroller that has an analog to digital converter (ADC). However, figuring out how to compute the exact temperature is not simple. Here I go through the process that I use. I call it "The Easy Way" because I use Excel and Word to do the heavy lifting for me.<br />
<br />
There are two types of thermistors, Negative Temperature Coefficient (NTC) and Positive Temperature Coefficient (PTC). Simply, for NTC thermistors, the resistance goes DOWN when the temperature goes UP.<br />
<br />
In a circuit, thermistors are typically half of a <a href="http://en.wikipedia.org/wiki/Voltage_divider" target="_blank">voltage divider</a>. In our example, the top half of the voltage divider is a 10k Ohm, 1% fixed resistor, powered by Vref, 2.5V. The bottom half of the voltage divider is a 10k Ohm 3% NTC thermistor.<br />
<h2>
Step 1: Get the temperature to resistance table</h2>
The math to convert from temperature to resistance is quite complex, so the manufacturers publish a table containing the resistance to temperature values. For example, see <a href="http://industrial.panasonic.com/www-data/pdf2/AUA0000/AUA0000AE233.pdf" target="_blank">this link</a>.<br />
<h2>
Step 2: Get them into Excel</h2>
<div style="text-align: left;">
For the table in the previous link the values were in a PDF sheet. But we need them in Excel. If you have a lot of time on your hands then you can manually retype them all. But I'm lazy, so I did some creative cut/paste from the PDF file into a Notepad file. You need a table consisting of many rows, each row containing the Temperature (in C) and also the Resistance (in Ohms). In Excel I also added another column containing the temperature in degrees Fahrenheit. This is just for convenience and not used in any calculations. The equation to convert from C to F in excel is the following, assuming the value in C is in cell A24:</div>
<div style="text-align: center;">
<i>=CONVERT(A24, "C", "F")</i></div>
<div style="text-align: left;">
Below we see what our table looks like after we have all our values. We only computed for the temperature range of 0C to 85C because that was our product requirements.<i><br /></i></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5vTcN4WjOnIYt_7qVk4VM-e-867ga9KWPmncco-xDxMX_OKtu7lRDa7e5h6mSD7X4FFwYX0vv0H6Be3melg6Y3mTCOObTkGmYh0uOaFg2dEGkqqNXQX34Dn5H0jLVJjxoIIJv8leT0fvS/s1600/Resistance+to+Temperature+Values.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5vTcN4WjOnIYt_7qVk4VM-e-867ga9KWPmncco-xDxMX_OKtu7lRDa7e5h6mSD7X4FFwYX0vv0H6Be3melg6Y3mTCOObTkGmYh0uOaFg2dEGkqqNXQX34Dn5H0jLVJjxoIIJv8leT0fvS/s1600/Resistance+to+Temperature+Values.PNG" height="266" width="320" /></a></div>
<h2>
Step 3: Get your ADC working</h2>
Use an oscilloscope to measure the output voltage of the voltage divider, while also measuring with the ADC. This will help you calibrate your calculations. Be sure that you know the resolution of your ADC to get the corresponding maximum counts. For example, a 12 bit ADC has 4096 possible values. Obviously to be able to measure temperature you will need to measure the voltage output from the thermistor voltage divider.<br />
<h2>
Step 4: Math Time</h2>
First, a few definitions: <br />
<ul>
<li>R1 = our "top" resistor, in our case the fixed 10kOhm value</li>
<li>Rt = the varying thermistor resistance</li>
<li>Vref = the voltage supply to the top of the voltage divider, in our case 2.5V</li>
<li>Vt = the output voltage of this voltage divider</li>
</ul>
The equation for the thermistor being on the BOTTOM leg of the voltage divider is:<br />
<div style="text-align: center;">
Rt Vt</div>
<div style="text-align: center;">
------- = ---------</div>
<div style="text-align: center;">
(R1 +Rt) Vref</div>
<br />
This can be rearranged to:<br />
<div style="text-align: center;">
Rt = R1 / ( ( Vref/Vt) - 1) </div>
Now, test this with your circuit, and verify that you are seeing reasonable values.<br />
Next, we need to convert this to ADC measurements. But first a couple more definitions:<br />
<ul>
<li>ADC= the value we measure from the ADC, in "counts"</li>
<li>Resolution = The resolution of the ADC. In our case, this is 4096 because we have a 12-bit ADC.</li>
</ul>
The ADC equation is:<br />
<div style="text-align: center;">
Vt = (ADC / Resolution) * Vref</div>
We can substitute this into the previous equation to get our magic equation:<br />
<div style="text-align: center;">
ADC = (4096) / ( (R1/Rt) + 1)</div>
<h2>
Step 5: Make a Table</h2>
We can use this equation to create a table in Excel that contains the following tuplets:<br />
<ul>
<li>Temperature (in C)</li>
<li>Resistance (in Ohms)</li>
<li>ADC Measurement</li>
</ul>
Using the Microsoft Excel Equation shown below. This assumes that our other resistor is 10k ohms, and our ADC has 4096 counts of resolution. In this case the raw resistance value is in cell C22. We <br />
<div style="text-align: center;">
=((4096)/((10000/C22)+1)) </div>
However, this will give us decimal values. We want to round this off, so we use the following:<br />
<div style="text-align: center;">
=ROUND(((4096)/((10000/C22)+1)), 0)</div>
<br />
Below shows the table with the ADC values added:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju3zwKeAlKhFZAnckgiaguaSG2hdOprOfp2S4ajArpkoWc12LPkgQYFR5F-PaNR2thah-KsYzzCQFzeRDYvZbYq5RtrA3KtdWXicprJlnJJQorOc4iDHwgWgKP3hnPceAAxv-KM_XJLrgN/s1600/Resistance+to+Temperature+Values+with+ADC.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEju3zwKeAlKhFZAnckgiaguaSG2hdOprOfp2S4ajArpkoWc12LPkgQYFR5F-PaNR2thah-KsYzzCQFzeRDYvZbYq5RtrA3KtdWXicprJlnJJQorOc4iDHwgWgKP3hnPceAAxv-KM_XJLrgN/s1600/Resistance+to+Temperature+Values+with+ADC.PNG" height="266" width="320" /></a></div>
<h2>
Step 5: Convert this into a C Array</h2>
Here's where our laziness comes into play. We're going to use Microsoft Word to convert the list of values into a C array. First, select only the ADC column, starting at the value for 0C. Paste this as plain text into a new Microsoft Word file. Be sure to paste as plain text.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnoObh1DzpaVbUAZfyV3hl2WQPnOn9SLGcHK9RQ8prbTpVlFQTYSfwwQXt4yy1TTMyK4N6vpEbArdQHG0gQ79jkPEAZZHzhGug_KXndKBaoUH-GbxwQOY6DIByDaZWHR3NVzv-PgfH4NXC/s1600/Word+File+-+be+sure+to+paste+as+plain+text.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnoObh1DzpaVbUAZfyV3hl2WQPnOn9SLGcHK9RQ8prbTpVlFQTYSfwwQXt4yy1TTMyK4N6vpEbArdQHG0gQ79jkPEAZZHzhGug_KXndKBaoUH-GbxwQOY6DIByDaZWHR3NVzv-PgfH4NXC/s1600/Word+File+-+be+sure+to+paste+as+plain+text.PNG" height="199" width="320" /></a></div>
Now we have a wonderful list of values. Next, use Word's find/replace option to replace the carriage return with a comma and a space, as shown below. Note the find string is "^p" without the quotes, and the replace string is ", " without the quotes.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQWR5bZcvaJTob3HfayM5RoOBHI8_9_ksob5LEKUeyDTK6RO09P1ZJC1_QCoxkp_TlTIn5RsMAh_IdEwYG9gtJ9Khixl7ouA29DETL9BdDgJgYEiuaF5hkjFdaAIm6bNINF2uJThb05DIp/s1600/Find+Replace.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQWR5bZcvaJTob3HfayM5RoOBHI8_9_ksob5LEKUeyDTK6RO09P1ZJC1_QCoxkp_TlTIn5RsMAh_IdEwYG9gtJ9Khixl7ouA29DETL9BdDgJgYEiuaF5hkjFdaAIm6bNINF2uJThb05DIp/s1600/Find+Replace.PNG" height="266" width="320" /></a></div>
This performs a bit of magic, turning our long list into something that's starting to look like an array:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZIg1Fk_TFMjWBo37vBYbgbjfbwapQR-kbWobZ74BEDuoAJIbRrDwhTSsiaqtHBxbomJ2bwgATz5HhhkfGukhR-Jg54Ez2X77_mFgBEjoByVg37f8sz18uUWCwcxXkOTHVcwAq-WX3D62z/s1600/Word+file+after+transformation.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZIg1Fk_TFMjWBo37vBYbgbjfbwapQR-kbWobZ74BEDuoAJIbRrDwhTSsiaqtHBxbomJ2bwgATz5HhhkfGukhR-Jg54Ez2X77_mFgBEjoByVg37f8sz18uUWCwcxXkOTHVcwAq-WX3D62z/s1600/Word+file+after+transformation.PNG" height="199" width="320" /></a></div>
Now, just wrap this with the variable definition and a comment explaining what it is, and we're done.<br />
<span style="font-family: "Courier New",Courier,monospace;"><span style="font-size: x-small;"><br /><span style="font-size: xx-small;">/** ADC values which correspond to temperature starting at 0C up to 85C*/<br />const uint16_t adcToTemperature[] = {3008, 2973, 2937, 2900, 2864, 2827, 2789, 2752,<br /> 2714, 2675, 2636, 2598, 2558, 2519, 2480, 2440, 2401, 2362, 2322, 2283, 2243, 2204,<br /> 2165, 2126, 2086, 2048, 2010, 1971, 1934, 1896, 1859, 1822, 1785, 1749, 1713, 1678,<br /> 1643, 1609, 1575, 1541, 1508, 1476, 1443, 1412, 1381, 1350, 1320, 1290, 1261, 1232,<br /> 1204, 1177, 1150, 1123, 1097, 1072, 1047, 1022, 998, 975, 952, 929, 907, 886, 865,<br /> 844, 824, 804, 785, 766, 748, 730, 712, 695, 679, 662, 646, 631, 616, 601, 587,<br /> 573, 559, 545, 532, 519};</span></span></span><br />
<h2>
Step 6: Get the Temperature for ADC Reading</h2>
We now have our array of values. To get the temperature we just iterate through this table, looking for the closest match. We're not doing any interpolation or anything fancy, just a straight look-up. If you need more accuracy then do something different.<br />
<span style="font-size: xx-small;"><span style="font-family: "Courier New",Courier,monospace;">/** Gets the temperature in degrees C for the specified ADC reading.<br /> * @param adc the reading from the ADC<br /> * @return the temperature in C, or ERROR_TEMPERATURE_NOT_FOUND if the value was not found.<br /> */<br />uint8_t getTemperatureForAdc(uint16_t adc)<br />{<br />#define ADC_RESOLUTION (4096)<br /><br /> if (adc > ADC_RESOLUTION)<br /> {<br /> return ERROR_TEMPERATURE_NOT_FOUND; //error - this should not happen<br /> }<br /><br /> /* Now iterate through the table, looking for the nearest match */<br /> uint32_t iterator = 0;<br /> while (adc < adcToTemperature[iterator])<br /> {<br /> iterator++;<br /> }<br /> if (iterator == ADC_TO_TEMP_LOOKUP_TABLE_ROWS)<br /> {<br /> // We've gone through the entire table but haven't found a match<br /> return ERROR_TEMPERATURE_NOT_FOUND; //error - this should not happen<br /> }<br /> return (iterator); // The temperature, in degrees C<br />}</span></span><br />
<h2>
Conclusion</h2>
We now have an easy and fast way to get the temperature from the thermistor.<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-39001810848917026942013-09-12T15:56:00.000-07:002013-09-12T15:56:20.896-07:00Syntax Highlighting for Custom Keywords in IAR<span style="font-family: inherit;">Syntax highlighting makes code much easier to read by parsing the various elements (function names, parameters, constants, etc.) and displaying each differently. Unfortunately IAR doesn't recognize the <a href="http://en.wikipedia.org/wiki/C_data_types#Fixed-width_integer_types" target="_blank">C99 Fixed-Width Integer Types</a> so it doesn't display them. For example see below. IAR doesn't display uint8_t any differently from the rest of the text.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUvopZUaW6xjoXLoIb1TD1izsMFkCuDtPeIOHfahVHJ9HiqCePL-Kj1z36poj8ak1IC2YQY2e5IB1a4kD-Xc4DbN3L8MVv7XnsF6spkOUNkp4T9cTxTTmByNa-edohokMWdEyRQX4mq5SX/s1600/iar_before_keyword_coloring.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUvopZUaW6xjoXLoIb1TD1izsMFkCuDtPeIOHfahVHJ9HiqCePL-Kj1z36poj8ak1IC2YQY2e5IB1a4kD-Xc4DbN3L8MVv7XnsF6spkOUNkp4T9cTxTTmByNa-edohokMWdEyRQX4mq5SX/s400/iar_before_keyword_coloring.PNG" width="400" /></a></div>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">To make IAR aware of these, you'll need to first create a custom keyword file, and then tell IAR to use it.</span><br />
<h2>
<span style="font-family: inherit;">Creating a custom keyword file </span></h2>
<span style="font-family: inherit;">To create a custom keyword file, open up your favorite text editor and insert the text below. Save it somewhere convenient. </span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><pre></pre>
</span><br />
<span style="font-family: inherit;">int8_t int16_t int32_t int64_t <br />int_fast8_t int_fast16_t int_fast32_t int_fast64_t <br />int_least8_t int_least16_t int_least32_t int_least64_t<br />uint8_t uint16_t uint32_t uint64_t <br />uint_fast8_t uint_fast16_t uint_fast32_t uint_fast64_t <br />uint_least8_t uint_least16_t uint_least32_t uint_least64_t<br />intmax_t intptr_t uintmax_t uintptr_t </span><br />
<span style="font-family: inherit;"></span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<h2>
<span style="font-family: inherit;">Tell IAR to use the custom keyword file</span></h2>
<span style="font-family: inherit;">Go to Tools : Options</span><br />
<span style="font-family: inherit;">Check the "Use Custom Keyword File" checkbox</span><br />
<span style="font-family: inherit;">Click on the Browse button labeled "..." and select the file you created above</span><br />
<span style="font-family: inherit;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_nV9F4h_CH7wb9VUVokkGE2Pn_9R8fJ364ZHB875PGfolaSOc1QQwaWLMQmlZgRAcRXUjbhkUiR56ocIpT3bp0OMkwznA2YQTvRksf6pk5O0b5lCYOVCPHOh0nnfHVGzsGYcKqrfv906-/s1600/IAR+Standard+Keywords+file.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_nV9F4h_CH7wb9VUVokkGE2Pn_9R8fJ364ZHB875PGfolaSOc1QQwaWLMQmlZgRAcRXUjbhkUiR56ocIpT3bp0OMkwznA2YQTvRksf6pk5O0b5lCYOVCPHOh0nnfHVGzsGYcKqrfv906-/s400/IAR+Standard+Keywords+file.PNG" width="400" /></a></div>
<br />
<h2>
<span style="font-family: inherit;">Tell IAR to use the custom keyword file</span></h2>
<span style="font-family: inherit;">Now, we'll change the color. </span><br />
<span style="font-family: inherit;">Go to Editor : Colors and Fonts</span><br />
<span style="font-family: inherit;">In the Syntax Coloring box, click on "User keyword"</span><br />
<span style="font-family: inherit;">Change the color and font to whatever you want. I chose green to make them stand out a little from everything else that is blue.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6_MRGyFE_X-UuqMzj8X7l1IADW52vfY3ZQ4tRl4X2XFSs8_GlAPSweLyIamDR4Ade8gbjjqgOsk9ohqqMG9k5J9ceX1hBlUHDf8IV6aetSzddoulD5VnWkdzyqN117iiMU_UOh7FrWn5I/s1600/IAR+custom+color+and+font.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6_MRGyFE_X-UuqMzj8X7l1IADW52vfY3ZQ4tRl4X2XFSs8_GlAPSweLyIamDR4Ade8gbjjqgOsk9ohqqMG9k5J9ceX1hBlUHDf8IV6aetSzddoulD5VnWkdzyqN117iiMU_UOh7FrWn5I/s400/IAR+custom+color+and+font.PNG" width="400" /></a></div>
<br />
<h2>
<span style="font-family: inherit;">Final Result </span></h2>
<span style="font-family: inherit;">Same as before, but now with color.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8reGJ8L4zFE2sR0Kw53jkKrNDteBrH3y6p2vAX7IDDZfmZHhqfbKHH8fe6Bag6eGA_Zk1bY1ME2NN0jOS30SysQs9oXzPgjm0Ph3tI8V5vODbrU6HrWsE7kJ10Eve1z2MVJsZxYOqWwIf/s1600/iar_after_keyword_coloring.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8reGJ8L4zFE2sR0Kw53jkKrNDteBrH3y6p2vAX7IDDZfmZHhqfbKHH8fe6Bag6eGA_Zk1bY1ME2NN0jOS30SysQs9oXzPgjm0Ph3tI8V5vODbrU6HrWsE7kJ10Eve1z2MVJsZxYOqWwIf/s400/iar_after_keyword_coloring.PNG" width="400" /></a></div>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><br /></span>Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-17747445115495823312013-09-02T14:36:00.002-07:002013-09-02T14:37:12.958-07:00API Design Technique for Web Application - Part 1We are in the process of releasing our new web application, <a href="http://demo.teslacontrols.com/" target="_blank">COIL</a>, and are working on how to allow users to get their data. COIL is a complete sensor to server development system that enables developers to display data from Zigbee devices in a web page. Users can create an account for free and customize the display of the data. On the back end, COIL receives the data from Gateways and communicates with the devices. We sell COIL as both a single tenant application (one server = one organization) and also as a <a href="https://en.wikipedia.org/wiki/Multitenancy" target="_blank">multi-tenant</a> application (one server supports thousands of users, each with their own data and configuration).<br />
<h3>
COIL System Architecture</h3>
We use a <a href="http://www.mysql.com/" target="_blank">MySQL</a> database instance on <a href="http://www.amazon.com/" target="_blank">Amazon</a> <a href="http://aws.amazon.com/rds" target="_blank">RDS</a> to hold our data. It's very cool. This is in a <a href="http://aws.amazon.com/vpc" target="_blank">Virtual Private Cloud</a> along with the application servers hosted on Amazon <a href="http://aws.amazon.com/ec2%E2%80%8E" target="_blank">EC2</a>.
So everything is behind one big firewall, with very limited access
in/out. For a single tenant system everything is dedicated to that
client; one cloud instance only has that one company's data. For multi-tenant systems everything is shared to support thousands of users. <br />
<h3>
API Goals</h3>
One of the goals of COIL is to allow users to access their data via an <a href="https://en.wikipedia.org/wiki/API" target="_blank">Application Programming Interface</a> (API). There are a few ways to implement this, all with their own challenges. The goals of our API are:<br />
<ul>
<li>Easy to use - easy to pull into an application.</li>
<li>Multitenancy support - support different users</li>
<li>Secure - one user should not see a different user's data</li>
<li>Implementation independent - allow us to change schema etc. without breaking API</li>
<li>Isolated - prevent a user from bogging down the database server.</li>
<li>Cross-platform - not tied to Windows, Mac, Linux, etc. </li>
<li>Scalable - handle millions of rows</li>
</ul>
<h3>
API Design Techniques</h3>
So we are evaluating different API approaches, and these have been mentioned:<br />
* <a href="https://en.wikipedia.org/wiki/Database" target="_blank">Database Access</a> - open up a port into our database <br />
* <a href="https://en.wikipedia.org/wiki/SOAP" target="_blank">SOAP</a> - Remote procedure calls<br />
* <a href="https://en.wikipedia.org/wiki/RESTful" target="_blank">REST</a> - the most popular approach right now<br />
* <a href="https://en.wikipedia.org/wiki/Flat_file_database" target="_blank">Flat Files</a> - like a good old fashioned CSV file<br />
<br />
Each of these has pros and cons, which we'll get into more in Part 2.Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-10575162651179311432012-10-13T11:22:00.004-07:002012-10-13T11:24:04.299-07:00Using a Wiki for Technical DocumentationIf you still produce technical documentation as PDF files then let me introduce a very cool tool to you. It's called <a href="http://www.mediawiki.org/">MediaWiki</a>. It's the engine behind a little site you might have heard of, <a href="http://www.wikipedia.org/">WikiPedia</a>. It's pretty easy to get up and running, and is totally free.<br />
<br />
Most people think of wikis as user generated content, but these are also handy for documentation projects where there are few authors but many readers. Most good documentation has two types of content: specifications and examples. The specifications explain how the thing works and are "locked down" as changes are restricted. The examples section is more free-flowing and it is desirable for users to add their own examples. The nice thing is that MediaWiki can be used for both. For specifications you can protect pages so that only a select group of users can make changes to them while at the same time leaving the example pages untouched.<br />
<h3>
<b>Using a Wiki to Reduce Support Costs</b></h3>
In any product, a support call or email is typically "how do I do X?" and the user may or may not have actually tried to research the problem first. When receiving a request like that the knee-jerk reaction is to explain to the user in a response how to solve the problem. But that only works that one time, and the next time that someone has the same question you'll have to answer the question again. Now many companies turn to user forums, hoping in vain that users will search through the forums for the answer before asking again. But few do, as forums are often full of questions and rarely full of answers.<br />
<br />
The better way to answer a support question is to answer the question by creating a Wiki page. This has several benefits. First, the next time that someone asks the same question you can answer the inquiry by sending the user a link to the article (whilst also kindly reminding him to <a href="http://en.wikipedia.org/wiki/Rtfm">RTFM</a> first). Second, it's a little easier to find a Wiki article due to the organization and searchability of a Wiki site. Finally, it encourages others to add on to the page, increasing the quality of the answer.<br />
<h3>
<b>Wiki Advantages</b></h3>
There are a few key advantages of using a Wiki to document a product rather than plain PDF files.<br />
<h4>
Hyperlinks</h4>
While you can add links in a PDF document, the Wiki format naturally lends itself to creating links, not just within the document but also to external links too. This can make it much easier for users. For a Zigbee Product Wiki, anytime I used a Zigbee term I made it a link to one page that explains what that term means. Hyperlinking can also reduce redundancy in documents.<br />
<h4>
Categorization</h4>
Another benefit of the wiki format is that you can assign one or more categories to a page. This makes it easy for users to find related content. It's quite easy to do, and allows users to navigate the site easier.<br />
<h4>
Easier to Maintain </h4>
One of the challenges of maintaining traditional documentation is often "who has the most recent word file" for the document. There is also a huge barrier to making any changes, as then the file has to be re-generated and uploaded somewhere. With a Wiki you always have the most recent version of the document on hand. You can create protected pages where users cannot modify the content, which is great for interface specifications.<br />
<h4>
User Generated Content</h4>
How often have you read through a datasheet and thought "why didn't they include more examples?" What would be better is for users to create examples and add it to the page. The best example of this is the <a href="http://dev.mysql.com/doc/refman/5.5/en/numeric-types.html">MySQL documentation</a> where the user section is as valuable (if not more so) than the official section.<br />
<h4>
Media</h4>
With PDF documentation of course you can include images, but with a Wiki you can include almost any type of media, including videos or other file formats. In the Zigbee Wiki I mentioned previously I wanted to explain to the reader how two ICs communicate. I included an image of a logic analyzer screenshot, but then I also included the logic analyzer capture file. Users can download this and then get in-depth information about how the ICs communicate.<br />
<h3>
<b>Wrapping Up</b></h3>
So, the next time you need to create documentation for your project, consider using a Wiki. It will take a bit of thought to get up and running but will be much easier to maintain, in addition to the other benefits I mentioned above.<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-53196198018910778062012-09-19T15:01:00.001-07:002012-09-19T15:01:03.465-07:00Testing Battery Pulse CharacteristicsLow power wireless devices typically sleep most of the time and then wake up to send a message and then go back to sleep again. In a <a href="http://fixituntilitsbroken.blogspot.com/2012/09/battery-selection-for-zigbee-and-low.html">previous post</a> I discussed battery selection for low power wireless devices. One issue with using coin cells is that they have poor pulse characteristics, and this performance varies across manufacturers.<br />
<br />
To figure out which battery will work for you (and get a feel for battery life) you will need to run a test. This test will involve pulsing the battery, waiting, and then pulsing it again; continuing until the battery voltage under load will no longer be sufficient for your product.<br />
<br />
<b>Pulse Waveform</b><br />
First, you need to get the waveform of what a typical pulse looks like. The easiest way is to supply your circuit through a one-ohm resistor and capture a typical pulse on an oscilloscope. Below is an example.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://ars.els-cdn.com/content/image/1-s2.0-S0026269210001163-gr2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="183" src="http://ars.els-cdn.com/content/image/1-s2.0-S0026269210001163-gr2.jpg" width="320" /></a></div>
To test battery life you will need to be able to reproduce this current draw. So, measure the current pulse. You don't need to get that fancy; just measure the width and height of the main pulse. For example, it may be 30mA for 5mSec. To reproduce the pulse you will need a suitable circuit. A simple MOSFET and load resistor will work. For 30mA at 3V use a 100 ohm resistor (V=IR).<br />
<br />
<b>Pulse Generation</b><br />
To generate the pulses you can use an arbitrary waveform generator, or if it's just simple on/off, you could even just write code to do it on a microcontroller development board if that's easier. For the most accurate estimate of battery life as measured by number of pulses you will want to allow as much "recovery time" as possible for the battery between pulses. Obviously you can't wait too long though; if you pulse every 6 seconds and your battery lasts for 45,000 pulses then this will take 3 days to measure. Typically we will run a "slow" test and "fast" test simultaneously to see if wait time has any affect.<br />
<br />
<b>Capture</b><br />
Remember that you need to measure the voltage of each pulse too to ensure that it it above your threshold voltage. This can be done in a few ways:<br />
<ul>
<li>Analog DAQ, like those from <a href="http://www.ni.com/products/usb-6008/">National Instruments</a>, although often these are not fast enough</li>
<li>Multimeter with output, like an <a href="http://www.home.agilent.com/agilent/product.jspx?cc=US&lc=eng&ckey=692834&nid=-33228.536908384.00&id=692834&cmpid=20051">Agilent 34410A DMM</a>. This is what we used with good results. Some custom processing of the output may be required though.</li>
<li>Custom code on a microcontroller. Come to think, it might just be the easiest way, especially if you are using the microcontroller to generate the pulses too.</li>
</ul>
Whatever option you select, record how many pulses it took before failing. It might also be good to record a typical voltage measurement (like voltage readings every millisecond of the pulse) so you can see how it fades.<br />
<br />
<b>Test Strategy </b><br />
For good measurement accuracy, perform the test on a sample of identical batteries, not just one. Five or so is sufficient. If you see lots of variability then you know to definitely not use that vendor. Test batteries from a few different vendors and use this information to determine which battery is best for you.<br />
<br />
You also now have a good indication of real battery life and which vendor's battery is best for you. When calculating total battery life be sure to include sleep current consumption as well as the current consumed by receiving any data too.<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-1060156101962657302012-09-17T14:47:00.000-07:002012-09-17T14:47:13.283-07:00Battery Selection for Zigbee and Low Power WirelessLow Power Wireless Standards include Zigbee, Bluetooth Low Energy, Dash7, you name it. Often times the newest wireless standard makes a claim like "lasts 4 years on a coin cell battery!" This is usually in the earliest days of the standard before anyone has actually tried it.<br />
<br />
Battery selection for low power wireless standards is very important. These systems typically need to run for a long time (months, if not years) and have many demands. Our ideal battery for these systems:<br />
* Lots of capacity (measured in amp-hours)<br />
* Small size<br />
* Excellent peak current capability (a wireless node can pulse up to 100mA)<br />
* Fairly flat voltage curve<br />
* Very low self-discharge<br />
* Dimensional stability (doesn't swell)<br />
* Low cost <br />
The challenge is that we're asking quite a lot from a battery. We want it to discharge very little while the device is sleeping but then also to discharge in large pulses when we transmit. Usually batteries that have excellent peak current capability have a higher self-discharge rate, and vice-versa. <br />
<br />
When starting a new project, people will often leap into creating a fancy battery lifetime spreadsheet, showing how based on estimated current consumption the battery will last something like 13.7 years or so. If only it were so! Unfortunately reality is quite a bit different. Battery lifetime is usually quite a bit shorter and often people don't quite know why.<br />
<br />
One of the biggest reasons why is <a href="http://en.wikipedia.org/wiki/Internal_resistance">internal resistance</a>. This causes the battery's output voltage to drop under load, limiting the effective amount of current that can be delivered. In general, the larger the battery, the smaller the internal resistance. This is one reason why <a href="http://en.wikipedia.org/wiki/Coin_cell">coin cell</a>s don't work well in low power wireless devices - they have too high of internal resistance and therefore cannot supply enough peak current. They'll work fine for data-logging or applications where the peak current is low (below 10mA) but for wireless devices that pulse at 30-100mA they will start to fade. Most coin cells are not specified at all for pulse current, let alone that much. Even worse, the pulse current capability will be different for different vendors' batteries. That means that Energizer may be able to handle a 15mA pulse but Maxell might be able to do 20mA pulses. It all depends on the coin cell size, geometry, etc.<br />
<br />
My favorite battery for low power wireless devices is the Energizer L91 (AA size) or L92 (AAA size). These lithium batteries have low self-discharge, nice voltage curve, good peak capability, and you can buy them in most grocery stores.<br />
<br />
Next I'll talk about how to test different batteries...Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com3tag:blogger.com,1999:blog-3090119043458623342.post-38823239981252208392012-09-14T13:03:00.000-07:002012-09-14T13:07:37.186-07:00The Joy of Bit-Banging, part 2: RXI previously discussed how to implement the Tx side of a bit-bang serial port, in <a href="http://fixituntilitsbroken.blogspot.com/2009/06/joy-of-bit-banging-part-1-tx.html">The Joy of Bit-Banging, part 1: TX</a>. This time I'll describe how to implement the receive side. This implementation uses blocking waits, as a result it should not be used unless you can tolerate delays.<br />
<br />
In this implementation the receive pin is P2.5<br />
<br />
<span style="font-weight: bold;">Required definitions, etc:</span><br />
<pre>#include "msp430x24x.h"
#define BIT_BANG_RX_PORT P2IN
#define BIT_BANG_RX_BIT BIT5
#define DISABLE_BIT_BANG_RX_INTERRUPT() (P2IE &= ~BIT5)
#define ENABLE_BIT_BANG_RX_INTERRUPT() (P2IE |= BIT5)
//Timings calculated using DCO_8_MHZ - modify if different oscillator speed or different hardware
#define BIT_LENGTH_9600 160
#define HALF_BIT_LENGTH_9600 76
#define BIT_LENGTH_19200 75
#define HALF_BIT_LENGTH_19200 27
/** Simple delay for bit width. Used in transmit and receive. */
void delayFullBit() {for (unsigned int i = 0; i < BIT_LENGTH_19200; i++) ; } //104uSec delay
/** Simple delay for 1/2 of bit width. Used in receive. */
void delayHalfBit() {for (unsigned int i = 0; i < HALF_BIT_LENGTH_19200; i++) ; } //104uSec delay
#define NUMBER_OF_DATA_BITS 8
#define TOGGLE_DEBUG_PIN() (P4OUT ^= BIT5) //debugging only</pre>
<br />
<span style="font-weight: bold;">Output function:</span><br />
<pre>/**
* Blocking UART receiver. Called by BIT_BANG_RX_PORT Interrupt Service Routine upon detection of start bit (high to low transition).
* BIT_BANG_RX_BIT should be configured as high-low edge triggered interrupt.
* Receives a byte and then processes it.
* @note BIT_BANG_RX_PORT, BIT_BANG_RX_BIT, DISABLE_BIT_BANG_RX_INTERRUPT(), and ENABLE_BIT_BANG_RX_INTERRUPT() must be #defined. Modify accordingly for other hardware.
* @pre Oscillator configured for 8MHz
* @pre A high-to-low transition has occurred on BIT_BANG_RX_BIT
* @return the byte received
*/
char bitBangInput()
{
unsigned char receivedBitIndex = 0;
unsigned char receivedByte = 0;
DISABLE_BIT_BANG_RX_INTERRUPT();
delayHalfBit(); //Since this method was called on detection of an edge, need to wait until we're in the middle of the bit width.
TOGGLE_DEBUG_PIN();
//Read this start bit - should be zero
if (BIT_BANG_RX_PORT & BIT_BANG_RX_BIT)
{
//error - re-enable interrupt and return
}
for (int i =0; i<number_of_data_bits bit="bit" br="br" each="each" for="for" i="i"> {
delayFullBit();
TOGGLE_DEBUG_PIN();
receivedByte |= (BIT_BANG_RX_PORT & BIT_BANG_RX_BIT) ? (1 << receivedBitIndex) : 0; //Sample the bit, and if '1' then increase receivedByte accordingly
receivedBitIndex++;
}
delayFullBit();
TOGGLE_DEBUG_PIN();
//THIS IS STOP BIT (line should be back high)
if (!(BIT_BANG_RX_PORT & BIT_BANG_RX_BIT))
{
//error - line is low; should be high because this is stop bit
}
//all bits have been received!
ENABLE_BIT_BANG_RX_INTERRUPT();
return receivedByte;
}
</number_of_data_bits></pre>
<br />
<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-51156910313432635282012-09-13T22:03:00.001-07:002012-09-13T22:06:04.955-07:00Implementing I2CLately I've been doing a lot of I2C development. In just about every way you can do it. Over the course of a month I've had to write code to communicate with an EEPROM and two sensors via:<br />
<ul>
<li>MSP430 using USCI</li>
<li>MSP430 using bit-bang</li>
<li>Stellaris using StellarisWare</li>
<li>Stellaris using bit-bang</li>
</ul>
Over the course of that experience I've gotten to know the I2C protocol very well. I won't go into the details of the protocol; there are plenty of sources for that.<br />
<br />
When you're first getting started, the best way to verify
the i2c interface is configured correctly is to not connect the
peripheral, but just watch the i2c signals to see that they are being
toggled correctly. By definition I2C is not a push-pull interface but
uses open drain I/O. It's really quite elegant, as it prevents a bus
conflict if one IC outputs a '1' and another outputs a '0'. As a result
if you want to see anything you will need external pull-up resistors. I
like to first develop code first without the peripheral ICs, just the
pull-up resistors. This way you can connect a logic analyzer to the two
signals and verify that your code is working correctly. This is
especially important if you are implementing a bit-bang solution and
need to check that the GPIOs are configured correctly.<br />
<br />
If you are using a hardware based solution then it is fairly straightforward; configure the baud rate, etc. and the hardware does all the signaling. The hard part is determining how to use the various hardware registers to get the desired output. This is much easier if you're using StellarisWare, as it handles all the register settings for you.<br />
<br />
If you're implementing a bit-bang solution, there are two ways of doing
this. The first is with simple delays; the second is using a timer
interrupt. I've done both. There both take about the same amount of time
to implement. The first way is simpler but requires hand-tuning the
amount of delay between bits to ensure the correct baud rate and a 50%
duty cycle. Using a timer is a bit more elegant as you're not waiting the processor but requires a state
machine to iterate through the various steps. If you're using a
StellarisWare it includes a nifty little softI2c implementation that can
be used decently easily.<br />
<br />
The first test you should do is to just do a simple write of 4 bytes or so, and observe it on the logic analyzer. Of course the ACK bit will not be pulled down since there's no peripheral IC but you'll at least be able to observe proper timing and framing behavior. After you get that working also verify that reads work too.<br />
<br />
After you get basic interfacing working, the next step is to write a simple address tester. This just writes to an address and checks to see if the write is acknowledged. This function is handy for verifying that the I2C peripheral is attached properly. This function can also be used for acknowledgment polling if you're implementing an EEPROM or FLASH interface. These types of memories take a few milliseconds to write and you must check that they are no longer in their write cycle before trying to access them again.<br />
<br />
Once basic functionality is working you can implement the basic read/write routines. Write is easier since it's a single step operation. Reading typically requires two steps: First writing an address (e.g. register or memory address), then doing a repeat start, and then doing a read. <br />
<br />
Since I2C has a fairly low bit rate (100kHz or 400 kHz under normal
conditions) it can take awhile to write or read a lot of bytes (6mSec to
read a full 64B page from an EEPROM at 100kHz). If you're using the I2C interface in a simple sensor it's fine to wait while communicating since you're not doing anything else until you receive the result. However, that would waste quite a few clock cycles; 160k if using a 25MHz clock. If you're using an RTOS or would otherwise like to minimize wait states then you'll want to implement it differently. In this case you should use DMA or at least an interrupt driven approach.<br />
<br />
A few miscellaneous I2C hints:<br />
<br />
Logic Analyzer<br />
I2C is much, much easier to troubleshoot with a <a href="http://en.wikipedia.org/wiki/Logic_analyzer">Logic Analyzer</a> since it will parse the serial data stream and show you the I2C start, stops, and data. I really like one from <a href="http://www.saleae.com/">Saleae</a>, they're USB based and great for microcontroller use.<br />
<br />
Mission Critical<br />
I've had occasions whereby one of the peripheral ICs would get into a funky state and cause the I2C bus to lock up. If you're dealing with mission critical application or you just have an extra GPIO pin then I recommend controlling the power of each peripheral IC from the GPIO. That way you can "reboot" a peripheral IC if there is an issue.<br />
<br />
Repeat Start<br />
Several peripherals require you to implement a repeat start condition. This isn't well documented in the processor's documentation and you may need to do a bit of research to find out how to do it.<br />
<br />Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-66128188674520230352012-08-07T10:37:00.003-07:002012-08-07T10:37:46.770-07:00Checking out the new Cortex-M4 ARM coreI've been using the Luminary Micro Cortex-M3 core for awhile now. It's pretty cool, and has some nice power to it for when you're running more advanced stacks. I also started developing on TI's new <a href="http://www.ti.com/cortexm4-b">Cortex-M4F Stellaris</a> processors. The benefits of the new processors:<br />
* Faster - standard up to 80MHz (though single-cycle flash only up to 40MHz)<br />
* Floating Point processing<br />
* Same or lower cost than the comparable Cortex-M3 processor<br />
* Lower power - uses <a href="http://www.eetimes.com/electronics-products/electronic-product-reviews/processors/4228114/TI-uses-65nm-process-for-M4F-based-MCUs">65nm process</a> instead of the 250nm process used on the M3 line<br />
<br />
The best part of Stellaris (both old and new) isn't the chip, it's the software. StellarisWare is a hardware abstraction library for these processors and makes firmware development MUCH faster. It is pretty comprehensive (very few things you can't do in stellarisWare) and reasonably well documented.<br />
<br />
The downsides of the Cortex-M4F:<br />
* No Ethernet support<br />
* Weird low power mode - "hibernation" but it doesn't keep the RAM alive, so you have to save your application variables<br />
* Higher power consumption than MSP430.Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com2tag:blogger.com,1999:blog-3090119043458623342.post-67145051626892213192011-11-21T11:08:00.000-08:002011-11-21T12:08:00.347-08:00Wireless Module vs. Custom ImplementationInterested in adding wireless functionality to your project?<br />
<br />
There's two main ways of doing this - with a premanufactured Module or a custom implementation. I've used both in the past and right now I'm working on a project right now where I need to use both approaches. There's a lot of hype out there with people arguing that one approach is better than the other but I thought it would be good to record exactly what I encounter with both approaches on this project. The technology used for both of these implementations is nearly the same which makes for a good comparison and both required internal antennas.<br />
<br />
<b>Custom Radio Implementation</b><br />
<br />
Design <b><br />
</b><br />
<ul><li>Figure out best antenna for the application: this depends on required performance as well as the enclosure used for the product.</li>
<li>Design matching circuit: While most RFICs and antennas have a 50 ohm impedance, some may differ and a small matching circuit is required. This needs to be done by an RF engineer. For most applications the RFIC manufacturer provides a reference design. For some reason that eludes me these always contain rare components that are a pain to source, and the reference design exhorts you not to change <i>anything</i> or else it will screw up your performance</li>
<li>Layout the PCB with the RFIC, antenna, and matching circuit. Most EDA packages have an option for automatically setting the width of the trace based on the desired impedance, as long as the PCBs are manufactured consistently each time. Heh.</li>
<li>It's a good idea to get the RF circuit reviewed by someone else, to help prevent stupid errors. And because it's "black magic" to most of us. This can be done by the RFIC manufacturer or the antenna manufacturer. Antenova (Antenna vendor) has a nice service whereby you give them some money and they design the matching circuit for you. </li>
<li>Firmware is lots of fun (sarcasm here) because you get to have to test that any change to your application may adversely affect the network stack. For simpler protocols this may not be much of an issue but if you're using more complex protocols (Zigbee, WirelessHART, etc.) this can get extremely time consuming.</li>
</ul> So now the PCB has been designed. What usually happens is that you go and fabricate the first few PCBs and find that performance doesn't match the reference board, so a few rounds of fixing is required. Next is manufacturing.<br />
<br />
Manufacturing<br />
<ul><li>Usually the reference designs use a 4-layer PCB, to get a good ground plane for RF performance. And as I mentioned above, life is hell if you try to modify the reference design, so you're pretty much stuck.</li>
<li><b> </b>Impedance Control - Life is sweet as long as the PCBs are manufactured consistently. However, that's never the case due to all kinds of process variations in the PCB manufacturing process. So this is that little checkbox you see when you get PCB quotes that makes everything more expensive, because the PCB manufacturer has to test and verify the characteristic impedance of each PCB panel, and if it's out of spec then they have to toss them out.</li>
<li>Source exotic RF components - as I mentioned previously, the reference designs often use components in the RF signal change that are extremely difficult to find. This becomes oh so much fun when you start production, as the CM will either complain or tell you that your lead time is very long (20+ weeks).</li>
<li>Design RF test fixture - Rarely if ever will a variation in the electronics manufacturing process produce <i>better</i> RF performance, almost always RF performance will suffer if something wasn't assembled correctly. So, you'd better test each device for RF performance. Fairly straightforward if the device has a connector but if it has an internal antenna then much thought needs to go into the RF test fixture as it needs to be shielded to prevent interfering with other devices under test. And you'd better design a system to record the RF performance of each device being tested, too, for SPC.</li>
<li>It's a minor issue, but if you're doing a cost analysis be sure that you look at what the cost of PCBs, manufacturing, components etc. is based on your actual volume. The module companies get components in 1M quantity discounts whereas you or I are stuck with whatever our actual volume is, usually lower. This can be an ugly surprise and make a custom radio implementation much more expensive down the road. </li>
</ul>Certification<br />
This is fairly straightforward if you're only doing FCC but can get expensive if you're targeting multiple countries. For FCC certification you should create a special version of your firmware that controls the radio so that their life is easier. I was working on one product that chirped out a small (20mSec) message every few minutes. That drove the test house crazy because it was very difficult to measure radiated emissions. So we modified the firmware to continuously send RF data. And there's nothing that firmware guys love to do more than stop writing code for The Next Best Thing so that they can create a custom spin of firmware just for RF testing. (sarcasm here). I've found that test houses always need to be "babysat" with someone from your company while the test is happening, that way it's easier to ensure that things are being done correctly, and that you can fix any problem that may arise. Getting FCC certification for a simple RF product usually costs around $9k in direct expenses (e.g. test house fees, FCC fees, etc.) and another $10k in indirect expenses (paperwork, creating test firmware, babysitting the test house etc.).<br />
<br />
<b>Module Implementation</b><br />
<br />
Design <b><br />
</b><br />
<ul><li>Figure out best module for the application: this depends on which protocol you're using and even more importantly, how easy it will be to integrate with the rest of the application. Examples are key here, as I'd rather take 10 good examples than 100 pages of specifications.</li>
<li>Writing the firmware is an order of magnitude easier with a module, because changes to your application can't break the network stack since the network stack is running on its own processor inside the module. Also makes for less code to maintain in my experience. (20kB vs. 240kB) </li>
<li>Since the module has all the RF stuff on it (RFIC, antenna, matching circuit, etc.) you just need to ensure that you pay attention to any ground plane requirements for the module. </li>
</ul>Manufacturing<br />
<ul><li>With a module your application will drive the PCB requirements, whereas with a custom radio implementation the RF section will limit your PCB options. So, for simple applications you can get by with a 2-layer non-impedance controlled PCB. Much cheaper and faster to prototype too.</li>
<li>You will need to ensure that you can source the module as needed. It's best to find a module that is carried by a few distributors so if one is out of stock then you can get it from someone else. </li>
</ul>Certification<br />
If you are using a pre-certified module then certification requirements are much less. Here's a good article about it from Digi (I don't like their modules but they have good information):<br />
http://www.digi.com/technology/rf-tips/2007/11<br />
<br />
<b>Conclusion - Which is the best approach?</b><br />
The answer probably comes as no surprise - it depends on a number of factors.<br />
<br />
Cost<br />
For low volume products (less than 10k per year) the module solution is almost always less expensive due to the high up-front costs of developing a custom radio solution. For higher volume products then you'll need to do a valid cost comparison. If it is unsure whether the product will become high volume then I recommend the module approach, as it will be much faster and less expensive up-front. Then later if it turns out that the product line is indeed high volume then you can do a cost comparison. However, if you're going high volume then oftentimes the module manufacturers will work with you on pricing so that it becomes a wash whether to continue using a module vs. re-engineering a custom solution.<br />
<br />
Expertise<br />
If your company lacks RF experience then you're probably better off going with the module approach, as it will be much easier than trying to learn RF. Most companies that choose the custom radio approach will end up outsourcing most of the RF work but your company will still need to learn about RF so that you can solve the inevitable manufacturing issues.<br />
<br />
Time to Market<br />
One aspect of time-to-market that oftentimes gets overlooked when comparing a modular approach with a custom radio is firmware development time. If the module comes with lots of relevant examples then life is good since you will be able to get up and going quickly and you won't have to learn the intricacies of the network stack. If you're developing a custom radio solution then you'll get to learn the network stack in-depth, which will obviously take much longer.Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-65896480809934585972011-08-05T20:03:00.000-07:002011-08-05T20:03:08.294-07:00Reading the supply voltage using the MSP430's internal Analog to Digital ConverterMany times you need to measure the supply voltage (Vdd) of the MSP430. The MSP430 has an ADC input channel which is connected to Vdd/2. The divide by two is required because the MSP430's internal reference is 2.5V, so Vdd must be divided by two for full resolution.<br />
<br />
Two simple routines for reading the supply voltage of the MSP430 family are shown below. Some MSP430 devices use the 10-bit ADC, e.g. the MSP430F2274, and some use the 12-bit ADC, e.g. the MSP430F248. Refer to the datasheet for your part to determine which is used. <br />
<br />
<span style="font-weight: bold;">Using the ADC10:</span><br />
<pre>/** Reads the MSP430 supply voltage using the Analog to Digital Converter (ADC).
On ez430 boards, this is approx. 3600mV
@return Vcc supply voltage, in millivolts
*/
unsigned int getVcc3()
{
ADC10CTL0 = SREF_1 + REFON + REF2_5V + ADC10ON + ADC10SHT_3; // use internal ref, turn on 2.5V ref, set samp time = 64 cycles
ADC10CTL1 = INCH_11;
delayMs(1); // Allow internal reference to stabilize
ADC10CTL0 |= ENC + ADC10SC; // Enable conversions
while (!(ADC10CTL0 & ADC10IFG)); // Conversion done?
unsigned long temp = (ADC10MEM * 5000l); // Convert raw ADC value to millivolts
return ((unsigned int) (temp / 1024l));
}
</pre><br />
<span style="font-weight: bold;">Using the ADC12:</span><br />
<pre>/** Private helper method to setup ADC for one-shot conversion and read out value according to registers.
Inserts a delay before beginning conversion if REFON
@return the raw ADC value with the specified commands.
@todo move the VREF warmup to startup and leave on to avoid 17mSec blocking delay each time?
*/
unsigned int getAnalogInput(unsigned int adc12ctl0, unsigned int adc12ctl1, unsigned char adc12mctl0)
{
#define ADC_VREF_DELAY_MS 17
ADC12CTL0 = adc12ctl0;
ADC12CTL1 = adc12ctl1;
ADC12MCTL0 = adc12mctl0;
if (adc12ctl0 & REFON) // if internal reference is used...
delayMs(ADC_VREF_DELAY_MS); // 17mSec delay required to Vref capacitors
ADC12CTL0 |= ENC; // Enable conversions
ADC12CTL0 |= ADC12SC; // Start conversions
while (!(ADC12IFG & 0x01)); // Conversion done?
return ADC12MEM0; // Read out 1st ADC value
}
/** Measures Vcc to the MSP430, nominally 3.3V
- ADC measures VCC/2 compared to 2.5V reference
- If Vcc = 3.3V, ADC output should be (1.65/2.5)*4095 = 2703
- Therefore (halfVcc/2.5)*4095 = ADC reading and (Vcc/2.5)*4095 = 2*ADC
- So Vcc*4096 = 5*ADC and VCC=5*ADC/4095
@return Vcc in millivolts
*/
unsigned int getVcc3()
{
unsigned int ctl0 = REFON + REF2_5V + ADC12ON + SHT0_15; // turn on 2.5V ref, set samp time=1024 cycles
unsigned int ctl1 = SHP; // Use sampling timer, internal ADC12OSC
unsigned char mctl0 = SREF_1 + INCH_11; // Channel A10, Vcc/2
unsigned long vcc = (unsigned long) getAnalogInput(ctl0, ctl1, mctl0);
unsigned long mult = vcc * 5000l;
return ((unsigned int)(mult / 4096l));
}
</pre>Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com1tag:blogger.com,1999:blog-3090119043458623342.post-39158009691199620262011-03-12T20:38:00.000-08:002011-03-12T20:38:35.351-08:00Making Development Boards That Don't SuckLately I've had to use a few microcontroller development kits from manufacturers that, well, suck. From unlabeled connectors/components to not bringing out the unused pins, I'm convinced that many designers of development boards never actually use them to design anything.<br />
<br />
When I'm trying out a new design, I frequently make my own development board. By "development board" I mean a schematic and layout that is the same circuit as the end product but optimized for firmware development and debugging. This allows a few things:<br />
<ul><li>Firmware development can start sooner - this is probably the most important since FW typically takes an order of magnitude longer than HW design</li>
<li>Verify PCB footprints for components - you can spend a month checking and double checking a PCB footprint, or just build it and verify it on a real PCB in a week.</li>
<li>Power Measurement</li>
<li>Fast layout. Since the point of a development board is to optimize for development not layout, you can make things a little larger and not worry about squeezing everything in.</li>
<li>Related to the previous, a development board helps you get a feel for how small you can make the design once everything is working.</li>
</ul><br />
Here are the a few rules for how to design development boards that don't suck:<br />
<ul><li>Expose all the pins of the components on test points or at least SMT pads. Soldering wires to fine pitch QFNs is a pain in the ass, and forget trying to do anything with BGAs. I recently helped out a major IC manufacturer and soldered some of their dev kits together because they didn't expose the unused pins.</li>
<li>Include a serial port (RS-232 is my preference). </li>
<li>Related to the above, bring out all signals that you can on easy 0.100" headers. This makes development much easier since you can attach logic analyzers, scopes, etc. Label them all with their signal name (more on this below).</li>
<li>Label liberally. Silkscreen is free, so on a development board label all components, as well as the signals on 0.100 headers, etc. Taking 5 minutes to label the signals on the PCB is faster than taking 5 minutes every time to look it up in documentation.</li>
<li>If it is a RF design with an onboard antenna, make an option for a connector. Can be SMA, U.FL or whatever, but something that you can connect to directly. I like to do this with a component in the signal chain that can be populated one way for the onboard antenna, or rotated 90 degrees to go to the connector instead.</li>
<li>Include a power connector and LDO to allow powering from a wall-wart. Makes it much easier to hand over to the firmware engineer and reduces unnecessary thought.</li>
<li>Enable power measurement: add a 0.100" header between LDO output and power input. This will have a shunt that can be removed and an ammeter attached instead. For extra credit, do this for the major power subsystems of the circuit. </li>
<li>Use full-sized JTAG connectors, and used shrouded (polarized) headers to prevent having to think about it.</li>
</ul><br />
Also, if this will be a commercially available development board (not just an intermediate step towards a final design) then there are a few more recommendations:<br />
<ul><li>Use larger packages to make it easy to assemble and modify. I like to use 0805s for all the non-RF passives, and use the largest package I can get for the micro. And label everything!</li>
<li>If the board has onboard peripherals (LEDs, buttons, etc.) then make them able to be disabled (disableable?). My favorite way to do this is with "Cuttable Jumpers", basically an 0805 footprint with a trace connecting the two pads. That way a user can cut the trace to disable the peripheral but can also re-enable it by soldering on a zero-ohm resistor across the pads.</li>
<li>Be sure that you include firmware drivers for all the peripherals. You have to develop them to test the board anyway, might as well provide them. </li>
</ul>The best development boards I've seen (besides the ones that I've designed of course) are the Stellaris development boards from Texas Instruments. Sadly, that company also makes some of the worst development boards too.Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0tag:blogger.com,1999:blog-3090119043458623342.post-70363005113001379812010-06-03T11:16:00.000-07:002010-06-03T11:33:39.631-07:00Calibrating the MSP430 Very Low Power OscillatorThe MSP430F2xxx contains an internal 12kHz very low power low frequency oscillator (VLO). The frequency varies by part, temperature, and supply voltage. On an MSP430F248 the VLO runs at approx. 9.4kHz and on an MSP430F2274 it's approx. 12kHz.<br />On the MSP430F2274 datasheet (page 38) the base frequency varies between 4kHz-20kHz and has a temp drift of 0.5%/degC and a supply voltage drift of 4%/V.<br /><br />So in order to make it useful we need to calibrate it. The method below will calibrate the VLO against the main oscillator. The accuracy of this calibration routine will only be as accurate as the main oscillator. If you're using one of the calibrated DCO frequencies (+/- 1%) then the end result will be the VLO within about 2% of actual. This will be more accurate if the main oscillator is sourced from a crystal.<br /><br />This calibration routine uses Timer A in the capture mode to capture the number of main clock cycles between subsequent ACLK cycles. This routine only counts one pulse, for more consistency you may want to count multiple ACLK cycles and average them.<br /><br /><pre><br />/** Calibrate VLO. Once this is done, the VLO can be used semi-accurately for timers etc.<br />Once calibrated, VLO is within ~2% of actual when using a 1% calibrated DCO frequency and temperature and supply voltage remain unchanged.<br />@return VLO frequency (number of VLO counts in 1sec)<br />@pre SMCLK is 4MHz<br />@pre MCLK is 8MHz<br />@pre ACLK sourced by VLO (BCSCTL3 = LFXT1S_2; in MSP430F2xxx)<br />@note Calibration is only as good as MCLK source. Obviously, if using the internal DCO (+/- 1%) then this value will only be as good as +/- 1%. YMMV.<br />@note On MSP430F248 or MSP430F22x2 or MSP430F22x4, must use TACCR2. On MSP430F20x2, must use TACCR0.<br />Check device-specific datasheet to see which module block has ACLK as a compare input.<br />For example, see page 23 of the MSP430F24x datasheet or page 17 of the MSP430F20x2 datasheet, or page 18 of the MSP430F22x4 datasheet.<br />@note If application will require accuracy over change in temperature or supply voltage, recommend calibrating VLO more often.<br />@post Timer A settings changed<br />@post ACLK divide by 8 bit cleared<br />*/<br />unsigned int calibrateVlo()<br />{<br /> WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer<br /> delayMs(1000);<br /> <br /> BCSCTL1 |= DIVA_3; // Divide ACLK by 8<br /> TACCTL2 = CM_1 + CCIS_1 + CAP; // Capture on ACLK<br /> TACTL = TASSEL_2 + MC_2 + TACLR; // Start TA, SMCLK(DCO), Continuous<br /> while ((TACCTL0 & CCIFG) == 0); // Wait until capture<br /> <br /> TACCR2 = 0; // Ignore first capture<br /> TACCTL2 &= ~CCIFG; // Clear CCIFG<br /> <br /> while ((TACCTL2 & CCIFG) == 0); // Wait for next capture<br /> unsigned int firstCapture = TACCR2; // Save first capture<br /> TACCTL2 &= ~CCIFG; // Clear CCIFG<br /> <br /> while ((TACCTL2 & CCIFG) ==0); // Wait for next capture<br /> <br /> unsigned long counts = (TACCR2 - firstCapture); // # of VLO clocks in 8Mhz<br /> BCSCTL1 &= ~DIVA_3; // Clear ACLK/8 settings<br /> <br /> vloFrequency = ((unsigned int) (32000000l / counts));<br /> return vloFrequency;<br />}<br /></pre>Derekhttp://www.blogger.com/profile/05693024174265961095noreply@blogger.com0