for(embed)http://www.forembed.com/2020-05-27T23:30:00-04:00Replacing LabVIEW with Python, Part 22020-05-27T23:30:00-04:002020-05-27T23:30:00-04:00Jason Jonestag:www.forembed.com,2020-05-27:/replacing-labview-with-python-2.html<p><img src="http://www.forembed.com/images/2020/labview-vs-python.png" alt="LabVIEW vs. Python" class="img-responsive img-rounded" align="right" width="300px"></p>
<h1>Software Stacks</h1>
<p>Anyone already familiar with Python - particularly from the data science perspective - will already be familiar with the majority of the contents of this brief article. In short, this article will introduce some of the software that will be utilized during your test development.</p>
<ul>
<li>numpy</li>
<li>pandas</li>
<li>matplotlib</li>
<li>pyvisa / pyvisa-py …</li></ul><p><img src="http://www.forembed.com/images/2020/labview-vs-python.png" alt="LabVIEW vs. Python" class="img-responsive img-rounded" align="right" width="300px"></p>
<h1>Software Stacks</h1>
<p>Anyone already familiar with Python - particularly from the data science perspective - will already be familiar with the majority of the contents of this brief article. In short, this article will introduce some of the software that will be utilized during your test development.</p>
<ul>
<li>numpy</li>
<li>pandas</li>
<li>matplotlib</li>
<li>pyvisa / pyvisa-py</li>
<li>pyserial</li>
<li>threading / multiprocessing</li>
<li>GUI, tkinter / Qt</li>
</ul>
<h2>Plotting</h2>
<p><img src="http://www.forembed.com/images/2020/matplotlib-logo.svg" alt="matplotlib logo" class="img-responsive" align="right" width="300px"></p>
<p>Use <code>matplotlib</code>. There are other packages out there, but none have as much documentation or maturity and the publication quality is second to none. I prefer to use the object-oriented interface, but if you are coming from MATLAB, you may prefer the state-based plotting.</p>
<p>I haven't used <a href="https://doc.qt.io/qt-5/qtcharts-index.html">Qt charts</a> - or Qt for that matter - but I see that, in some cases, they may make a competent replacement for <code>matplotlib</code>.</p>
<h2>Data Processing</h2>
<p><img src="http://www.forembed.com/images/2020/numpy_logo.png" alt="numpy logo" class="img-responsive img-rounded" align="right" width="200px"></p>
<p>The standard packages for data processing are <code>numpy</code> and <code>pandas</code>. You can store millions of points along with gaining functionality such as fft and a host of other functions provided specifically for processing data quickly. Like <code>matplotlib</code>, <code>numpy</code> was originally created to replicate some of the array processing of MATLAB in an open-source package and, thus, has become the core of hundreds of packages. <code>pandas</code> can be thought of as a spreadsheet-like abstraction, but is actually much more powerful than spreadsheets in its capabilities.</p>
<h2>Serial Port Access</h2>
<p><img src="http://www.forembed.com/images/2020/pyserial.png" alt="pyserial logo" class="img-responsive" align="right" width="200px"></p>
<p>Try as they might, USB will never fully obsolete the ease of use of the good old serial port. Of course, it has been relegated - on most machines - to the virtual COM port, but its simplicity still reigns supreme in the hardware space.</p>
<p>I use <a href="https://pythonhosted.org/pyserial/"><code>pyserial</code></a> on nearly every project somewhere. The project is simple, useable, and mature. In most cases, code built on linux works just as well with windows, which is unusual in the hardware space.</p>
<p>The fact is that manufacturers continue to utilize the <a href="https://www.ftdichip.com/">FTDI</a> chips for USB serial interfaces, so knowledge of PySerial is simply required.</p>
<h2>Hardware</h2>
<p><img src="http://www.forembed.com/images/2020/pyvisa-logo.jpg" alt="pyvisa logo" class="img-responsive" align="right" width="200px"></p>
<p>As previously suggested, you should try to find hardware that has a good serial interface. If you find that you can't find a great serial interface for your chosen instrument, then it is likely supported through some VISA implementation. When I'm being perfectly honest, I usually struggle a bit to find the right instrument drivers. Sometimes the vendors drivers work quite well... and sometimes not. Either way, once you get the right drivers installed, you will want to use <code>pyvisa</code> to interface with the hardware.</p>
<p>There are two flavors of <code>pyvisa</code> available: <a href="https://pyvisa.readthedocs.io/en/latest/"><code>pyvisa</code></a> and <a href="https://pyvisa-py.readthedocs.io/en/latest/"><code>pyvisa-py</code></a>. There are some common authors between them. The <code>-py</code> flavor is a pure python implementation. Either should work for most instruments, but <code>pyvisa</code> is a bit more mature.</p>
<h2>Threading</h2>
<p>Of course, the <code>threading</code> and <code>multiprocessing</code> modules are part of the standard library. There are some cases in which the interface needs to be polled or maintained independently of any other process. Up to this point, the <code>threading</code> module has been up to the task of maintaining the test functionality. I expect that, one day, it won't be. When that day comes, I will push some of the high-demand tasks to <code>multiprocessing</code>, which will actually allocate an independent true thread to the task.</p>
<p>If you don't understand communications between threads, it is best to stick with <code>threading</code>.</p>
<p>One potentially significant advantage of LabVIEW is that it uses a data flow model, meaning that processes are not running until all inputs are satisfied. This data flow model means that users are rarely exposed to threading and processing realities since the LabVIEW runtime engine takes care of the processor load in most cases.</p>
<h2>GUI</h2>
<p>One aspect in which LabVIEW shines is in the ease of GUI creation. The drag-and-drop interface that was created for the user makes programming in LabVIEW deceptively easy. As a result, if you are looking for a replacement, you need to be familiar with some sort of framework for creating user interfaces.</p>
<p>My preference is <code>tkinter</code> primarily because I have clarity on the license. The <code>tkinter</code> packages comes bundled with Python and deployment is easy.</p>
<p>On the flip side, flavors of Qt, such as <code>Qt5</code> and <code>Pyside2</code> always make me feel nervous when I read their licensing terms.</p>
<p>As we will see later, there are ways to make your program create a majority of its own GUI so that the user only sees "pass" and "fail" blocks. This is the way that I was taught to write test programs and I am happy that I have gone down that route. Again, we will revisit GUIs later in the article stack as there is much more to discuss.</p>
<h2>Deployment</h2>
<p><img src="http://www.forembed.com/images/2020/pyinstaller-logo.png" alt="pyinstaller logo" class="img-responsive" align="right" width="400px"></p>
<p>As with any production test, deployment considerations take front and center as soon as your code moves from your lab to the production floor. With LabVIEW, you are required to install the correct runtime engine for the environment. With python, we can deploy the runtime engine with the executable as a package using <code>pyinstaller</code>. We will talk more about this at a future time.</p>
<h2>Bonus: Test Framework</h2>
<p>Early on in my test development efforts, I tried to come up with frameworks that made testing easier. Automated test frameworks such as <code>pytest</code> are great for hardware testing, but they don't really seem geared towards manufacturing tests, which are repetitive and tend to save the same data set each time around. In my efforts, I eventually came up with the <a href="https://github.com/slightlynybbled/mats">Manufacturing Automated Test Suite</a> (MATS). This is essentially a console tool that creates a test thread which executes and coordinates the test itself.</p>
<p>In addition, I created tkMATS which will automatically create the GUI and put a nice face on the front end.</p>
<p>We will discuss the test framework later, but this is one of the keys to developing production tests quickly since it removes the burden of boilerplate from the user while ensuring a safe and consistent test product.</p>Replacing LabVIEW with Python, Part 12020-03-08T23:30:00-04:002020-03-08T23:30:00-04:00Jason Jonestag:www.forembed.com,2020-03-08:/replacing-labview-with-python-1.html<p><img src="/images/2020/labview-vs-python.png" alt="LabVIEW vs. Python" class="img-responsive img-rounded" align="right" width="300px"></p>
<h1>Strategies for Choosing Hardware</h1>
<p>Much of this document is strategized to reduce your time developing harware APIs.</p>
<h2>Ask a Friend</h2>
<p>My strategy for choosing hardware: <em>ask a friend</em>! If your friend can save you a few hundred dollars and a couple of days of effort, go for it! Ask for …</p><p><img src="/images/2020/labview-vs-python.png" alt="LabVIEW vs. Python" class="img-responsive img-rounded" align="right" width="300px"></p>
<h1>Strategies for Choosing Hardware</h1>
<p>Much of this document is strategized to reduce your time developing harware APIs.</p>
<h2>Ask a Friend</h2>
<p>My strategy for choosing hardware: <em>ask a friend</em>! If your friend can save you a few hundred dollars and a couple of days of effort, go for it! Ask for a loaner and a working API!</p>
<p>There have been many times that a friend has solved the problem in the recent past and have a nearly ready solution already in production.</p>
<h2>Choose Hardware with Serial Ports</h2>
<p>One of the items that all of my preferred hardware has in common is having control via USB Virtual COM Port (USB Serial). Why?</p>
<ul>
<li>Driver availability - drivers often install automatically since many are <a href="https://www.ftdichip.com/">FTDI</a>-based</li>
<li>Well-documented APIs - often text-based (Python excels at text analysis and manipulation)</li>
</ul>
<h2>Standardize Your Hardware</h2>
<p>If you don't standardize on a few pieces of hardware, you will spend all of your time developing APIs rather than tests! If you have an application that requires a 5A power supply, it may be worth dedicating a 7A power supply if you have other applications that may use similar hardware. Have a few goto pieces of hardware that you always use and your development time will be minimized.</p>
<p>An additional benefit of standardized hardware is the ability to create a buffer stock from which you can pull more stock as more tests come online. I typically try to keep enough hardware to deploy 2-4 test setups based on standard hardware and requiring only the test-specific tooling to move on. This also reduces time to test since I can start working on the framework and initial test sequence while tooling is being made. At this point, I can put together a competent basic test sequence within half-an-hour, assuming standard hardware.</p>
<p>Shoe-ins for standardization:</p>
<ul>
<li>power supplies</li>
<li>data acquisition cards</li>
<li>temperature sensing</li>
<li>oscilloscopes</li>
</ul>
<p>Some equipment that I'm currently evaluating for standardization:</p>
<ul>
<li>HiPot machines</li>
<li>LCR meters</li>
</ul>
<p>When choosing candidates for standardization, buy one and give it a good shot. Prove it out. Make sure it works well. Time invested up front is painful to lose, but if you standardize on an awkward piece of hardware, you will find that you <em>always</em> have to deal with that awkward piece of hardware. Choose wisely!</p>
<h3>Bonus: Use the Same Manufacturer!</h3>
<p>Many manufacturers use the same API between instruments of different ratings. This means that you have minimum work required to bring up a new instrument. Less time dedicated to APIs. This is NOT universally true, so be sure to check!</p>
<h2>Use Inheritance/Interfaces in your APIs</h2>
<p>I usually write the API for the hardware <em>before</em> actually interacting with the hardware. In some languages, this is sometimes called writing an interface. In this way, I ensure that my actual implementation abstracts away much of the awkwardness that may be inherent in the instrument. Additionally, using inheritance, I can have more than one instrument utilize the exact same API. I have switched power supply models by simply changing the hardware acquisition line because both power supply classes inherited from the same abstract parent class.</p>
<p>My favorite abstract class is for power supplies.</p>
<div class="highlight"><pre><span></span><span class="kr">class</span> <span class="nx">Psu</span>:
<span class="kt">def</span> <span class="nx">set_voltage</span><span class="p">(</span><span class="nx">self</span><span class="p">,</span> <span class="nx">voltage</span><span class="o">:</span> <span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="kr">float</span><span class="p">,</span> <span class="kr">int</span><span class="p">))</span><span class="o">:</span>
<span class="nx">raise</span> <span class="nx">NotImplementedError</span>
<span class="nx">def</span> <span class="nx">set_current</span><span class="p">(</span><span class="nx">self</span><span class="p">,</span> <span class="nx">current</span><span class="o">:</span> <span class="p">(</span><span class="nx">str</span><span class="p">,</span> <span class="kr">float</span><span class="p">,</span> <span class="kr">int</span><span class="p">))</span><span class="o">:</span>
<span class="nx">raise</span> <span class="nx">NotImplementedError</span>
<span class="nx">def</span> <span class="nx">on</span><span class="p">(</span><span class="nx">self</span><span class="p">)</span><span class="o">:</span>
<span class="nx">raise</span> <span class="nx">NotImplementedError</span>
<span class="nx">def</span> <span class="nx">off</span><span class="p">(</span><span class="nx">self</span><span class="p">)</span><span class="o">:</span>
<span class="nx">raise</span> <span class="nx">NotImplementedError</span>
<span class="kd">@property</span>
<span class="nx">def</span> <span class="nx">voltage</span><span class="p">(</span><span class="nx">self</span><span class="p">)</span><span class="o">:</span>
<span class="nx">raise</span> <span class="nx">NotImplementedError</span>
<span class="kd">@property</span>
<span class="nx">def</span> <span class="nx">current</span><span class="p">(</span><span class="nx">self</span><span class="p">)</span><span class="o">:</span>
<span class="nx">raise</span> <span class="nx">NotImplementedError</span>
</pre></div>
<p>Starting with the above abstract class, I start with a complete target of functionality for a relatively simple set of concepts to implement before I can proceed to test development.</p>
<h2>My Favorite Hardware (thus far)</h2>
<h3>Data Acquisition</h3>
<p><img src="http://www.forembed.com/images/2020/di-2008.jpg" alt="DATAQ DI-2008" class="img-responsive img-rounded" align="right" width="300px"></p>
<p>My favorite DAQ is the DATAQ <a href="https://www.dataq.com/products/di-2008/">DI-2008</a>. When I'm being perfectly honest, the serial API is a bit awkward, but I took a lot of trouble to <a href="https://github.com/slightlynybbled/di2008">abstract the awkwardness away</a> with a <code>pip</code> installable package. Once past the API, the features more than make up for the awkwardness and I'm glad that I stuck it out with this one:</p>
<ul>
<li>configurable analog inputs from +/-10mV to +/-50V... how is this possible?!</li>
<li>analog inputs may be configured as thermocouple inputs... directly... up to 8 of them!</li>
<li>digital inputs/outputs - pretty standard</li>
<li>dedicated rate input</li>
<li>built-in analog filters</li>
</ul>
<p>The DI-2008 has more features, but I haven't used them all yet. Feel free to contribute to the <a href="https://github.com/slightlynybbled/di2008"><code>di2008</code></a> package!</p>
<p>There are only a couple of drawbacks to this device:</p>
<ul>
<li>speed isn't great at 200 samples/second</li>
<li>no analog output</li>
</ul>
<p>Thus far, these haven't hobbled my manufacturing-oriented test setups, but I'm sure that is a matter of time before I have one of these requirements bite me.</p>
<p>When not using the DI-2008, I like to use the National Instruments <a href="https://www.ni.com/en-us/shop/select/multifunction-io-device">USB-6001</a> or similar. The API isn't great since you have to drop down to the C documentation, but it is usable. Recommend spending some time to tune the API since it is somewhat non-intuitive.</p>
<h3>Power Supplies</h3>
<p><img src="http://www.forembed.com/images/2020/ex355p-usb.jpg" alt="AimTTI" class="img-responsive img-rounded" align="left" width="200px"></p>
<p>Thus far, I have worked exclusively with AimTTi power supplies. Both of the supplies that I have utilized started as a result of a recommendation from a friend.</p>
<p>The <a href="https://www.aimtti.com/product-category/dc-power-supplies/aim-cpxseries">CPX-series</a> of power supplies is available for 60V applications up to 20A. Supplies are easy to operate, have a remote-sense mode, and have exhibited ease of use and stability. I can now get one of these up and running in a couple of lines and I couldn't be happier. Be sure to look for models with USB option.</p>
<p>When I want a bit less voltage/current/dollars, I look to the <a href="https://www.aimtti.com/product-category/dc-power-supplies/aim-ex-rseries">EX-series</a>. Specifically, the EX355P-USB.</p>
<p>Both of these supplies are rock-solid choices for their range. I will <em>definitely</em> look to AimTTI for the next application that is beyond the range of these devices before looking anywhere else.</p>
<h3>Oscilloscopes</h3>
<p><img src="http://www.forembed.com/images/2020/ds1000Z.png" alt="Rigol DS1000Z" class="img-responsive img-rounded" align="right" width="300px"></p>
<p>Recently, I have been replacing setups which require an operator to evaluate and make judgements about oscilloscope waveforms, even for basic measurements. I have been quite happy to use the <a href="https://www.rigolna.com/products/digital-oscilloscopes/1000z/">Rigol DS1000Z series</a>.</p>
<p>Using an oscilloscope is likely overkill for many tests that we execute; however, I am often replacing legacy tests which require a user to make judgements based on the oscilloscope plots and replacing those with similar instrumentation tends to increase confidence in the methods.</p>
<h3>Your Hardware</h3>
<p>We have discussed "standard" lab equipment, such as power supplies and oscilloscopes, but haven't talked about your device! If your device has a communications interface, it is just as important to have a good API for controlling your hardware as it is to have a good API for your devices. For one, it makes tests written on your hardware much more readable, but it also allows the generation of multiple tests of the same product much easier. Don't neglect your device!</p>
<h1>Concluding...</h1>
<p>Having a few lego blocks from which to build tests will make your life easier. Again, spend time making your API simple and repeatable across multiple instruments and you will be rewarded with superpowers such as the ability to construct a competent test in hours instead of days.</p>Replacing LabVIEW with Python, Part 02020-03-01T23:30:00-05:002020-03-01T23:30:00-05:00Jason Jonestag:www.forembed.com,2020-03-01:/replacing-labview-with-python-0.html<p><img src="/images/2020/labview-vs-python.png" alt="LabVIEW vs. Python" class="img-responsive img-rounded" align="right" width="300px"></p>
<h1>Introduction</h1>
<p>The use case described herein involves prototype and manufacturing test applications almost exclusively. I do not intend to suggest that Python can or should replace LabVIEW in all applications, but I have found that, in this particular use case, Python is particularly well suited.</p>
<p>I am an electrical engineer …</p><p><img src="/images/2020/labview-vs-python.png" alt="LabVIEW vs. Python" class="img-responsive img-rounded" align="right" width="300px"></p>
<h1>Introduction</h1>
<p>The use case described herein involves prototype and manufacturing test applications almost exclusively. I do not intend to suggest that Python can or should replace LabVIEW in all applications, but I have found that, in this particular use case, Python is particularly well suited.</p>
<p>I am an electrical engineer. When I first started learning Python, I had already deployed a few LabVIEW programs to production. Initially, I really loved it, but as time passed, I found that the time dedicated to maintenance and refactoring was surprisingly high. As a result, I found myself looking for other options and it took me a while to realize that Python is a potential replacement.</p>
<p>This series of articles is going to show you how I have made the transition from LabVIEW to Python and have been able to successfully deploy low-maintenance, high-performing automated test programs to engineering and manufacturing environments.</p>
<h1>Overview</h1>
<ul>
<li>Why build with Python instead of Labview?</li>
<li><a href="http://www.forembed.com/replacing-labview-with-python-1.html">Hardware considerations</a></li>
<li><a href="http://www.forembed.com/replacing-labview-with-python-2.html">Software that you may want to get a handle on</a></li>
<li>Test methods and the need for a framework</li>
<li>User interaction</li>
<li>Deployment & Maintenance</li>
</ul>
<h1>Why Bother?</h1>
<p>Perhaps the best question is "Why would you want to replace LabVIEW with Python?"</p>
<p>Great question! First, let's take a look at the pros and cons. I'm assigning points based on the things that I value thate most and on the relative strength. Take the numbers with a pinch - bag? - of salt.</p>
<table class="table">
<tr>
<th>Attribute</th>
<th>Comparison</th>
<th>LabVIEW</th>
<th>Python</th>
</tr>
<tr>
<td>Cost</td>
<td>LabVIEW costs annually about $2k / license for each seat. Python sticker price is $0.</td>
<td></td>
<td>+2</td>
</tr>
<tr>
<td>Introductory GUI</td>
<td>LabVIEW's drag and drop GUI interface makes the first "hello, world" GUI a breeze. Programming your first GUI in Python requires quite a bit more training and knowledge.</td>
<td>+3</td>
<td></td>
</tr>
<tr>
<td>Standard Library</td>
<td>LabVIEW and Python both have very good standard libraries.</td>
<td>+1</td>
<td>+1</td>
</tr>
<tr>
<td>External Libraries</td>
<td>LabVIEW has some libraries out there, but a quick perusal of <a href="https://pypi.org">PyPI</a> shows 219k `pip`-installable libraries that have been community developed ready to be installed. Many more are available on <a href="https://github.com">GitHub</a>.</td>
<td></td>
<td>+5</td>
</tr>
<tr>
<td>Community</td>
<td>LabVIEW has some communities out there, but they tend to be relatively small and somewhat isolated. The ease of picking up Python has made it one of the easiest languages to extend and with tools such as `pip` coming on the scene in the last couple of decades, Python is a *strong* competitor.</td>
<td></td>
<td>+2</td>
</tr>
<tr>
<td>Ease of Deployment</td>
<td>LabVIEW has some cool installers available. They tend to be gigabytes, but they also tend to work pretty well.. Python itself doesn't have a lot of deployment strength on its own, but packages such as <a href="http://www.pyinstaller.org/">PyInstaller</a> have made deployment in linux and windows a breeze.</td>
<td></td>
<td>+2</td>
</tr>
<tr>
<td>Ease of Maintenance</td>
<td>With <a href="http://www.pyinstaller.org/">PyInstaller</a>, maintenance is a breeze. One can deploy multiple executables "compiled" with different versions of Python without them interfering with one another. I haven't had to actually install Python to any of my target machines because of the method that PyInstaller utilized to package the interpreter with the executable.</td>
<td></td>
<td>+3</td>
</tr>
<tr>
<td>Open Source</td>
<td>If you care about open source, Python is, LabVIEW isn't.</td>
<td></td>
<td>+1</td>
</tr>
<tr>
<td>Speed</td>
<td>I haven't done a speed comparison so I can't say what the relative speeds of performing the same task might be. Perhaps someone else could shed some light on this?</td>
<td>?</td>
<td>?</td>
</tr>
<tr>
<td>Platforms</td>
<td>I haven't tried LabVIEW on linux, but I know that Python works on all major platforms. There are instructions regarding the installation of LabVIEW available from National Instruments, so it appears that the platform is not a limitation with either.</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>Source Control</td>
<td>Python, being a text language, works quite well with Git, Subversion, etc. LabVIEW *can* work, but doesn't really shine since its VIs are binary files.</td>
<td></td>
<td>+2</td>
</tr>
<tr>
<td>Professional Image</td>
<td>Knowledge of Python transfers well to other problem domains, such as web development and data science. LabVIEW is used primarily to control and read instrumentation, limiting its broader appeal.</td>
<td></td>
<td>+3</td>
</tr>
<tr>
<td>Hardware Support</td>
<td>Since National Instruments makes a lot of hardware for LabVIEW, hardware support is definitely a strength. Community-contributed packages for Python such as pyvisa have closed the gap. Anything that has a C API (all NI hardware) also can be used in Python, but not as easily. As a result, I have started looking for instruments that have a text-based command and written classes to use them more intuitive. Because of the ease of use of the NI hardware, LabVIEW has a bit of an edge here.</td>
<td>+2</td>
<td></td>
</tr>
</table>
<p>Clearly, Python has some serious advantages! My take on this...</p>
<h1>Reasons to go with LabVIEW</h1>
<ul>
<li>You already have experience with LabVIEW and you've already paid for it</li>
<li>You like maintenence and fees</li>
<li>Easy GUIs! "Hello, World" is likely the easiest drag and drop of just about any GUI in any language</li>
<li>Quick access to hardware through well-defined virtual instruments</li>
</ul>
<h1>Reasons to go with Python</h1>
<ul>
<li>You're broke and don't have the option anyway</li>
<li>There is currently no established language or platform (new business)</li>
<li>Low maintence is a primary requirement</li>
<li>There is already some Python knowledge internally (data scientist, for instance)</li>
<li>Developer wants to have more transferrable skills</li>
<li>Open source matters</li>
<li>Community strength matters</li>
</ul>
<h1>How to make the Switch from LabVIEW to Python?</h1>
<p>The remainder of the articles will focus on different aspects of making the switch to Python.</p>Building a Pelican Blog with Bitbucket Pipelines2018-05-24T22:55:00-04:002018-05-24T22:55:00-04:00Jason Jonestag:www.forembed.com,2018-05-24:/building-a-pelican-blog-with-bitbucket-pipelines.html<p><img src="http://www.forembed.com/images/2018/pelican-atlassian-aws.png" alt="filter pic" class="img-responsive img-rounded"></p>
<p>I haven't exactly made it a secret that I use <a href="http://docs.getpelican.com/en/stable/">Pelican</a> to write this blog. Sometimes I'm active, and sometimes I'm not. As pelican is a static site generator based on templates and text files, I have been doing the build and deployment by hand. When I have been away …</p><p><img src="http://www.forembed.com/images/2018/pelican-atlassian-aws.png" alt="filter pic" class="img-responsive img-rounded"></p>
<p>I haven't exactly made it a secret that I use <a href="http://docs.getpelican.com/en/stable/">Pelican</a> to write this blog. Sometimes I'm active, and sometimes I'm not. As pelican is a static site generator based on templates and text files, I have been doing the build and deployment by hand. When I have been away for a while, I sometimes find that I have changed machines and have to spend some time setting up my local machine to release an article. This doesn't take too much time, but sometimes I just want to pound out an article and don't have the machine set up.</p>
<p>In the past, I had thought about setting up some sort of AWS lambda function, docker container, or even a dedicated server for auto-deployment... I just didn't want the hassle of operating yet another machine. I took a few minutes to investigate bitbucket pipelines and I am impressed! I have been using bitbucket to control the files for the blog for years already, but now I can simply commit to <code>master</code> to build and deploy the site to the production AWS server or commit to <code>test</code> to get a preview. Too easy! Why didn't I do this before!?</p>
<h1>The Steps</h1>
<h2>Step 1: Use bitbucket</h2>
<p>No need to go outside your of your normal repository control... no external service required... and it comes with 50 min of build time each month. That should be more than enough to get your your blog fix.</p>
<h2>Step 1.1 (optional): Create Build Environment on your Local Machine</h2>
<p>This step isn't actually necessary, but it will ensure that your pelican build environment is actually functional before trying to commit to bitbucket and wait for it to build.</p>
<div class="highlight"><pre><span></span>$ > virtualenv -p python3 venv
$ > venv/bin/pip install pelican markdown awscli
$ > venv/bin/pip freeze > requirements.txt
</pre></div>
<p>There is apparently a bit of a glitch with <code>pkg-resources</code> in debian-based environments. It is safe to delete the line in <code>requirements.txt</code> that specifies <code>pkg-resources</code>.</p>
<p>Add any <a href="https://github.com/getpelican/pelican-plugins">plugins</a> to your repository and configuration files.</p>
<p>This isn't intended to show you how to use pelican, but I generally execute <code>venv/bin/pelican -s pelicanconf.py</code> in order to build my site. My site is contained in <code>./output</code> when it is complete. We will see references to this path in the next step.</p>
<p>Now that I can build locally with relative paths in my configuration file, I can proceed...</p>
<h2>Step 2: Configure AWS S3</h2>
<p>You should have a bucket assigned to AWS. The bucket that hosts this site is called <code>forembed.com</code>. The bucket should be configured to host a site. There are numerous guides to this, but basically, you have to:</p>
<ul>
<li>make the bucket publically readable</li>
<li>configure to host a static site</li>
<li>set each file to be individually publically readable</li>
</ul>
<h2>Step 3: Create AWS User Credentails</h2>
<p>Here, you will create a 'user' that bitbucket will utilize to interact with your AWS S3 bucket.</p>
<ul>
<li>Create IAM user group such as <code>deployment-s3</code>. Be sure to attach the <code>AmazonS3FullAccess</code> policy to the group.</li>
<li>Create a user such as <code>bitbucket</code> and add the user to the <code>deployment-s3</code> group (unless you named it something else). Be sure to tick the "Programmatic Access" box when prompted.</li>
<li>Under the user, you will need to create credentails and you will need to copy the Access Key ID and Secret Access Key into bitbucket.</li>
</ul>
<p>There is a bit of a chicken-and-egg dilemma at this point. You have to enable bitbucket-pipelines to set environment variables for pipelines, but your deployment won't work without enviironment variables. Just enable the pipelines so that you can set your environment variables here.</p>
<ul>
<li>Settings -> Pipelines -> Environment variables</li>
<li>AWS_ACCESS_KEY_ID = {your access key id}</li>
<li>AWS_SECRET_ACCESS_KEY = {your secret key}</li>
</ul>
<h2>Step 4: Add bitbucket-pipelines.yml</h2>
<p>First, lets have a look at the <code>bitbucket-pipelines.yml</code> file:</p>
<div class="highlight"><pre><span></span><span class="l l-Scalar l-Scalar-Plain">image</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">python:3.5.1</span>
<span class="l l-Scalar l-Scalar-Plain">pipelines</span><span class="p p-Indicator">:</span>
<span class="c1"># create a full build that may be accessed at </span>
<span class="c1"># http://test-site-deployment.s3-website-us-east-1.amazonaws.com</span>
<span class="l l-Scalar l-Scalar-Plain">default</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">step</span><span class="p p-Indicator">:</span>
<span class="l l-Scalar l-Scalar-Plain">name</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">Build and deploy to test server</span>
<span class="l l-Scalar l-Scalar-Plain">deployment</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">test</span>
<span class="l l-Scalar l-Scalar-Plain">script</span><span class="p p-Indicator">:</span> <span class="c1"># Modify the commands below to build your repository.</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">pip3 install -r requirements.txt</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">pelican -s pelicanconf.py</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">aws s3 sync --delete ./output s3://test-site-deployment/ --acl public-read</span>
<span class="l l-Scalar l-Scalar-Plain">branches</span><span class="p p-Indicator">:</span>
<span class="c1"># commits to the master branch will deploy a new site at http://forembed.com</span>
<span class="l l-Scalar l-Scalar-Plain">master</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">step</span><span class="p p-Indicator">:</span>
<span class="l l-Scalar l-Scalar-Plain">name</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">Build and deploy to production server</span>
<span class="l l-Scalar l-Scalar-Plain">deployment</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">production</span>
<span class="l l-Scalar l-Scalar-Plain">script</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">pip3 install -r requirements.txt</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">pelican -s pelicanconf.py</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">aws s3 sync --delete ./output s3://forembed.com --acl public-read</span>
</pre></div>
<p>The file is fairly readable for a knowledgeable guy, but there are two nearly identical pipelines shown here, one for all branches not named and <code>master</code>. Three primary stages:</p>
<ul>
<li>environment setup (installing python packages)</li>
<li>build of static site, <code>pelican -s pelicanconf.py</code></li>
<li>AWS sync (last line)</li>
</ul>
<h3>Environment Notes</h3>
<p>You will need to specify <code>pip3</code> to use python 3. If you have a script that you want to execute using the python 3 environment, be sure to preface with <code>python3</code>. I don't know why python 2 was the default.</p>
<h3>AWS Notes</h3>
<ul>
<li><code>--delete</code> will delete the contents of the bucket</li>
<li><code>./output</code> specifies that only the <code>output</code> path should be uploaded, not the entire git repository</li>
<li><code>--acl public-read</code> sets the permissions so that the site is generally accessible</li>
</ul>
<h1>Coffee Break</h1>
<p>'Nuff said.</p>
<p>I will probably build some version of this into my other projects as well. Go Atlassian!</p>
<h1>Sources</h1>
<p>The AWS and Bitbucket documentation are pretty good, but I found a <a href="http://yup-the-website-domain-is.mindginative.com/post/setting-up-bitbucket-pipelines-and-aws-s3/">blog post</a> particularly helpful when it came to deployment to S3.</p>Reynolds Number with Python and Pint2017-11-16T00:00:00-05:002017-11-16T00:00:00-05:00Jason Jonestag:www.forembed.com,2017-11-16:/reynolds-number-with-python-and-pint.html<p><img alt="Reynolds Observations" src="http://www.forembed.com/images/2017/11/reynolds_observations_turbulence_1883.png"></p>
<p>I had an instance at work where I needed to quickly calculate an estimate for <a href="https://en.wikipedia.org/wiki/Reynolds_number">Reynolds Number</a> for 0 gallons/min to 10 gallons/min for several different pipe diameters.</p>
<div class="math">$$Re = \frac{QD}{vA}$$</div>
<ul>
<li><span class="math">\(Re\)</span> = Reynolds Number (dimensionless)</li>
<li><span class="math">\(Q\)</span> = volumetric flow rate (<span class="math">\(m^3/s\)</span>)</li>
<li><span class="math">\(v\)</span> = kinematic viscocity of the …</li></ul><p><img alt="Reynolds Observations" src="http://www.forembed.com/images/2017/11/reynolds_observations_turbulence_1883.png"></p>
<p>I had an instance at work where I needed to quickly calculate an estimate for <a href="https://en.wikipedia.org/wiki/Reynolds_number">Reynolds Number</a> for 0 gallons/min to 10 gallons/min for several different pipe diameters.</p>
<div class="math">$$Re = \frac{QD}{vA}$$</div>
<ul>
<li><span class="math">\(Re\)</span> = Reynolds Number (dimensionless)</li>
<li><span class="math">\(Q\)</span> = volumetric flow rate (<span class="math">\(m^3/s\)</span>)</li>
<li><span class="math">\(v\)</span> = kinematic viscocity of the fluid (<span class="math">\(m^2/s\)</span>)</li>
<li><span class="math">\(A\)</span> = cross sectional area (<span class="math">\(m^2\)</span>)</li>
</ul>
<p>At first, I tried this by hand. With various unit conversions, I got something wrong and ended up off by about 50%. Enter <a href="https://www.python.org/">Python</a> and <a href="https://pint.readthedocs.io/en/latest/">Pint</a>.</p>
<p>You probably already know about Python, but Pint is a very handy little package that keeps track of your units as you are going through, including through math operations. This keeps you from <a href="http://articles.latimes.com/1999/oct/01/news/mn-17288">messing things up due to a simple unit conversion error</a>.</p>
<h1>The Code</h1>
<p>First, we set ourselves up:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pint</span>
<span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">pi</span>
<span class="n">unit</span> <span class="o">=</span> <span class="n">pint</span><span class="o">.</span><span class="n">UnitRegistry</span><span class="p">()</span> <span class="c1"># create the unit registry</span>
</pre></div>
<p>We then define our central calculation, implementing the formula above. Note the <code>.to()</code> method that is being utilized to get the units consistent. Pint will often resolve simple operations itself - <code>unit.inch * unit.meter</code> will result in <code>inch ** 2</code> - but Pint is not a symbolic math engine, it just cancels the appropriate units that are obvious and leaves the rest out there. As a result, your answer may be technically correct, but useless with mixed units everywhere if you don't use the <code>.to()</code> method.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">calc_reynolds_number</span><span class="p">(</span><span class="n">volumetric_flow_rate</span><span class="p">,</span> <span class="n">hydraulic_diameter</span><span class="p">,</span> <span class="n">kinematic_viscocity</span><span class="p">,</span> <span class="n">cross_sectional_area</span><span class="p">):</span>
<span class="n">q</span> <span class="o">=</span> <span class="n">volumetric_flow_rate</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s1">'meter ** 3 / s'</span><span class="p">)</span>
<span class="n">dh</span> <span class="o">=</span> <span class="n">hydraulic_diameter</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s1">'meter'</span><span class="p">)</span>
<span class="n">v</span> <span class="o">=</span> <span class="n">kinematic_viscocity</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s1">'meter ** 2 / second'</span><span class="p">)</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">cross_sectional_area</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="s1">'meter ** 2'</span><span class="p">)</span>
<span class="n">re</span> <span class="o">=</span> <span class="p">(</span><span class="n">q</span> <span class="o">*</span> <span class="n">dh</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">v</span> <span class="o">*</span> <span class="n">a</span><span class="p">)</span>
<span class="k">return</span> <span class="n">re</span>
</pre></div>
<p>As it turns out, the presented example results in the same exact output if the function were re-written:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">calc_reynolds_number</span><span class="p">(</span><span class="n">volumetric_flow_rate</span><span class="p">,</span> <span class="n">hydraulic_diameter</span><span class="p">,</span> <span class="n">kinematic_viscocity</span><span class="p">,</span> <span class="n">cross_sectional_area</span><span class="p">):</span>
<span class="n">re</span> <span class="o">=</span> <span class="p">(</span><span class="n">volumetric_flow_rate</span> <span class="o">*</span> <span class="n">hydraulic_diameter</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">kinematic_viscocity</span> <span class="o">*</span> <span class="n">cross_sectional_area</span><span class="p">)</span>
<span class="k">return</span> <span class="n">re</span>
</pre></div>
<p>So there's one point for <code>Pint</code>. I have had the non-conversion bite me in the past, so I always convert to my preferred units.</p>
<p>Finally, we need to actually perform our calculations and show the results:</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">viscosity</span> <span class="o">=</span> <span class="mf">0.000001</span> <span class="o">*</span> <span class="n">unit</span><span class="o">.</span><span class="n">meter</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">/</span> <span class="n">unit</span><span class="o">.</span><span class="n">second</span>
<span class="n">cross_sectional_area</span> <span class="o">=</span> <span class="n">pi</span> <span class="o">*</span> <span class="p">(</span><span class="mf">0.5</span> <span class="o">*</span> <span class="n">unit</span><span class="o">.</span><span class="n">inch</span><span class="p">)</span> <span class="o">**</span> <span class="mi">2</span>
<span class="n">diameter</span> <span class="o">=</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="n">unit</span><span class="o">.</span><span class="n">inch</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">11</span><span class="p">):</span>
<span class="n">volumetric_flow_rate</span> <span class="o">=</span> <span class="n">i</span> <span class="o">*</span> <span class="n">unit</span><span class="o">.</span><span class="n">gallons</span> <span class="o">/</span> <span class="n">unit</span><span class="o">.</span><span class="n">minute</span>
<span class="n">re</span> <span class="o">=</span> <span class="n">calc_reynolds_number</span><span class="p">(</span><span class="n">volumetric_flow_rate</span><span class="p">,</span> <span class="n">diameter</span><span class="p">,</span> <span class="n">viscosity</span><span class="p">,</span> <span class="n">cross_sectional_area</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'{}</span><span class="se">\t</span><span class="s1">{}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">volumetric_flow_rate</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">re</span><span class="p">)))</span>
</pre></div>
<p>This results in an output of:</p>
<div class="highlight"><pre><span></span>0.0 gallon / minute 0
1.0 gallon / minute 6325
2.0 gallon / minute 12650
3.0 gallon / minute 18975
4.0 gallon / minute 25300
5.0 gallon / minute 31625
6.0 gallon / minute 37950
7.0 gallon / minute 44275
8.0 gallon / minute 50600
9.0 gallon / minute 56926
10.0 gallon / minute 63251
</pre></div>
<p>Which I can easily copy/paste into my <a href="https://www.libreoffice.org/">favorite spreadsheet editor</a>!</p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>KiCAD Copy and Paste2017-11-06T17:41:00-05:002017-11-06T17:41:00-05:00Jason Jonestag:www.forembed.com,2017-11-06:/kicad-copy-paste.html<h1>Introduction</h1>
<p>Throughout the last few years, KiCAD has been my goto tool as a hobbyist and as
a professional. I readily admit that KiCAD lacks some of the polish and features
found within Altium, Allegro, and PADS. Despite the lack of features, KiCAD has
one advantage that those packages did …</p><h1>Introduction</h1>
<p>Throughout the last few years, KiCAD has been my goto tool as a hobbyist and as
a professional. I readily admit that KiCAD lacks some of the polish and features
found within Altium, Allegro, and PADS. Despite the lack of features, KiCAD has
one advantage that those packages did not have, which is that it grew up in a
hobbyist's world. The primary result of this unique upbringing as been that KiCAD
is likely the easiest package to setup and use of all schematic-to-layout packages.</p>
<p>Unfortunately, there has been one glaring feature lacking in recent releases: copy
and paste. The copy/paste feature initially appears superficial, until the user wishes
to re-use a significant portion - including all archived part data - from one or more
additional schematics.</p>
<p>This article will present the current simplest method of gaining access to those juicy
schematic bits. We present this with the desire that this article quickly becomes outdated
by a true copy/paste functionality.</p>
<h1>Create your Circuit</h1>
<p>Open a new KiCAD instance and draw your ciruit. In our case, we are simply going to draw
a voltage divider. You could draw up something more useful, but I would rather
that this tutorial be very easy to follow.</p>
<p><img alt="voltage divider circuit" src="http://www.forembed.com/images/2017/11/voltage-divider.png"></p>
<h1>Layer it</h1>
<p>Next, we are going to create a heirarchical sheet. This will cause KiCAD to make a *.sch
file which represents your circuit. This command may be found on the right menu or by
using the <code>Place</code> -> <code>Hierarchical Sheet</code>. Draw the square on the page by clicking in the
upper left of where you want the circuit to start and the lower right. The exact location
doesn't matter and may be adjusted after initial creation.</p>
<p><img alt="circuit with sheet" src="http://www.forembed.com/images/2017/11/circuit-with-sheet.png"></p>
<p>When you have finished clicking, a window will appear asking what you want to name the <code>*.sch</code>
file and what you wish this particular instance of it to be called. Fill it out. The one
that we want to remember later is the `<em>.sch</em> name.</p>
<p><img alt="schematic sheet properties window" src="http://www.forembed.com/images/2017/11/schematic-sheet-properties.png"></p>
<p>You can verify that you now have a multi-sheet schematic by selecting the navigator menu button
<img alt="navigator menu button" src="http://www.forembed.com/images/2017/11/navigator.png"> and double-clicking on the
lower-level page. The page that you started in was the <code>Root</code>, the new page will bear the name
that you assigned in the <code>Schematic Sheet Properties</code> window.</p>
<p>You should also have an additional <code>*.sch</code> file within the project directory which bears the name
that you gave the sch file. This will be our source schematic! </p>
<h1>Move the Components Down</h1>
<p>Highlight all of the voltage divider circuit by dragging a selection box around it. Right-click
and select <code>Save Block</code>. Alternately, you could also use <code>CTRL-C</code>.</p>
<p>Navigate into the sheet using the navigator menu button.</p>
<p>Use the paste button <img alt="paste button" src="http://www.forembed.com/images/2017/11/kicad-paste.png"> to paste your
subcircuit into the schematic sheet.</p>
<p>This next part is going to be a bit weird, but just do it and you will see why in a moment.
Select all of the components and move them 'above' the sheet. When you import them into a
schematic in a moment, the location at which the components are placed is where they will be
imported next!</p>
<p><img alt="subcircuit above sheet" src="http://www.forembed.com/images/2017/11/subcircuit-above-sheet.png"></p>
<h1>Bring 'em In</h1>
<p>Create a new schematic or open another schematic into which the parts are to be imported.
<code>File</code> -> <code>Append Schematic Sheet</code>. Navigate to your circuit. In this cases, we navigated to
<code>voltage-divider.sch</code>. This operation will overwrite your sheet, but will bring in the
entire schematic contained in the <code>*.sch</code> file without overwriting your current schematic
page. </p>
<p><img alt="imported subcircuit" src="http://www.forembed.com/images/2017/11/imported-subcircuit.png"></p>
<p>Two things to note:</p>
<ol>
<li>Our circuit is in the <em>exact</em> same place that we left it, which is off-sheet. This is visually
displeasing, but easy enough to move.</li>
<li>The sheet changed! Manually change it back, using the sheet properties. Unfortunately, this is
not avoidable at this time.</li>
</ol>
<h1>Wrap-Up</h1>
<p>This isn't ideal. We would much rather copy-paste elements of schematics directly. In some ways,
defining a subcircuit is a bit like defining a component. It becomes part of your bag of tricks
and creates a way to easily create several 'boilerplate' sections of your circuitry with little
effort. How many times have you put that switching regulator on the page?</p>
<p>My suggestion is that you create a single schematic library in which you store several of your
most-used schematics into it... intelligently named, of course. When you have need of a circuit,
simply import it into your new schematic as a block. As your thoughts on a particular subcircuit
evolve - such as changing to sulfur-resistant components - then you can modify your subcircuits
and gain the advantages of having a single source for your circuits as opposed to multiple
sources (copy/paste errors finding their way into several schematics, even when the first one
was corrected, is common).</p>
<p>Good luck, and enjoy KiCAD!</p>Dispatch How-To2017-10-04T18:28:00-04:002017-10-04T18:28:00-04:00Jason Jonestag:www.forembed.com,2017-10-04:/dispatch-how-to.html<p><img src="http://www.forembed.com/images/logos/dispatch.png" alt="Dispatch Logo" height="100px" align="right"></p>
<h1>Purpose</h1>
<p>This document is intended to provide a guide regarding the use of Dispatch.</p>
<p>Source code may be found on <a href="https://github.com/slightlynybbled/Dispatch">github</a>.</p>
<h1>Concepts</h1>
<h2>Structure</h2>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/dispatch-architecture.png" alt="Dispatch architecture" align="right"></p>
<p>The Dispatch library consists of two primary components, the dispatch source and header and the
framing source and header. Dispatch acts as the intermediary between your application …</p><p><img src="http://www.forembed.com/images/logos/dispatch.png" alt="Dispatch Logo" height="100px" align="right"></p>
<h1>Purpose</h1>
<p>This document is intended to provide a guide regarding the use of Dispatch.</p>
<p>Source code may be found on <a href="https://github.com/slightlynybbled/Dispatch">github</a>.</p>
<h1>Concepts</h1>
<h2>Structure</h2>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/dispatch-architecture.png" alt="Dispatch architecture" align="right"></p>
<p>The Dispatch library consists of two primary components, the dispatch source and header and the
framing source and header. Dispatch acts as the intermediary between your application and the
remote application.</p>
<p>Your application publishes data to the subscribers using Dispatch and your subscribers consume data
from Dispatch. Your application will never have to interact directly with the hardware or the
framing libraries.</p>
<p>Dispatch is an <em>event-driven framework</em> which will execute your designated functions on receipt
of certain messages.</p>
<h2>Framing</h2>
<p>Framing happens, but - fortunately - you don't need to worry about it. Framing has
been completely abstracted!</p>
<h2>Publishing Data Flow</h2>
<p>A publish occurs when your application calls <code>publish("topic", data)</code>. The data is then passed to Dispatch,
through framing, and finally through the hardware drivers. The hardware drivers then pass the data through
the communications channel where it is received by the hardware on the receiving end, de-framed, and then
interpreted by the Dispatch library.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/publish-flow-through.png" alt="Publish flow"></p>
<p>Once the remote dispatch has received the message, it calls any subscribers to that data and allows them to
execute. </p>
<h2>Subscribing</h2>
<p>Subscribing is straightforward. Write your subscribing function and, then <code>subscribe("topic", &mySubscriber);</code>.
Dispatch will execute your function every time that message is received.</p>
<p>Note that more than one function may be subscribed to a particular topic!</p>
<h2>Retrieving Data</h2>
<p>Data retrieval must occur inside the subscribing function. If the data is not retrieved within the function,
then the data will be lost forever! Data retrieval is done within the subscribing function using
the <code>DIS_getElements()</code> function.</p>
<h1>Setting Up Dispatch</h1>
<h2>Hardware Interface</h2>
<p>In the off-chance that a driver exists in
<a href="https://github.com/slightlynybbled/Dispatch/tree/master/src/drivers">/src/drivers/</a>, then you are in luck!<br>
In the likely scenario that it is not, then you will have to find or write the hardware driver yourself. There
are four functions that need to be implemented: <code>readable</code>, <code>writeable</code>, <code>read</code>, and <code>write</code>. The function
names do not matter since they will be assigned during initialization.</p>
<p>Examples can be found in the <a href="https://github.com/slightlynybbled/Dispatch/tree/master/src/drivers">/src/drivers/</a>
directory.</p>
<h3>Buffers</h3>
<p>The drivers should have some sort of internal memory buffer that functions as a circular buffer. These buffers
are the ones accessed by the below functions. In this document, TX_BUF_LENGTH and RX_BUF_LENGTH are the
defines that will be utilized to control the sizes of this buffer at compile time. You can call them what you
wish.</p>
<h3>Readable</h3>
<p>The <code>readable()</code> function simply returns an unsigned 16-bit integer that indicates the amount of data than can
currently be read from the RX circular buffer of the hardware interface.</p>
<div class="highlight"><pre><span></span><span class="kt">uint16_t</span> <span class="nf">UART_readable</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</pre></div>
<h3>Read</h3>
<p>The <code>read()</code> function takes <code>length</code> amount of unsigned 8-bit data from the driver buffers and copies them
to the given buffer.</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">UART_read</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">);</span>
</pre></div>
<h3>Writeable</h3>
<p>The <code>writeable()</code> function returns the unsigned 16-bit integer than indicates the amount of data that can safely
be written to the circular buffer of the hardware interface.</p>
<div class="highlight"><pre><span></span><span class="kt">uint16_t</span> <span class="nf">UART_writeable</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</pre></div>
<h3>Write</h3>
<p>The <code>write()</code> function will write <code>length</code> amount of unsigned 8-bit data from the given buffer to the driver
buffer, which will be sent through the hardware interface.</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">UART_write</span><span class="p">(</span><span class="kt">void</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">)</span>
</pre></div>
<h2>Buffer Sizing</h2>
<p>I considered not including defines for buffer sizing, but I quickly realized that some applications would
be sending very modest amounts of data infrequenly and some could be sending significant amounts of data.
As a result, you are required to set up the defines. There are default values that should get you started,
but you should tune for your application.</p>
<h3>Transmission Buffers</h3>
<p>It is recommended that you write your UART TX driver to buffer at least 8 bytes. This gives enough room
to send modest messages without stalling the processor to wait for the UART to send data. This is
NOT a requirement! You can write your buffer to accept 1 byte at a time, but your processor will simply
stall on a transmission waiting for bytes to move out of the TX queue.</p>
<h3>Receive Buffers</h3>
<p>Due to the issue of having to calculate the checksum and verifying <em>before</em> accepting the entire message,
received messages MUST be buffered. There are three buffer levels that must be accounted for:</p>
<ol>
<li>RX Message Buffer</li>
<li>Framing Buffer</li>
<li>UART RX Driver Buffer</li>
</ol>
<h4>Dispatch RX Message Buffer</h4>
<p>The dispatch message buffer is determined by:</p>
<ol>
<li>The topic string length</li>
<li>The number of dimensions of the data</li>
<li>The dimensional data width (width of data for all dimensions)</li>
<li>The maximum length of all data to be received in a single reception</li>
</ol>
<p>Use the provided buffer calculator to determine your memory footprint required for Dispatch to send and
receive your data.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Description</th>
<th>Value</th>
<th>Unit</th>
<th>Notes</th>
</tr>
<tr>
<td>Maximum topic string length</td>
<td><input type="text" class="form-control" id="MAX_TOPIC_STR_LENGTH" value="8"></td>
<td>characters</td>
<td>Decrease this to decrease the recommended buffer size (1-to-1)</td>
</tr>
<tr>
<td>Number of dimensions of the data</td>
<td><input type="text" class="form-control" id="DIMENSIONS" value="2"></td>
<td>integer (1 to 15)</td>
<td>Decrease this to decrease the recommended buffer size</td>
</tr>
<tr>
<td>Dimensional Width</td>
<td><input type="text" class="form-control" id="DIM_WIDTH" value="2"></td>
<td>bytes</td>
<td>This is the number of bytes/element transmitted. For instance, if you have
two-dimensional data being sent, one of which is 1 byte wide and the other is 2
bytes wide, then this number should be 3. Decrease this to decrease the recommended
buffer size.</td>
</tr>
<tr>
<td>Maximum data length</td>
<td><input type="text" class="form-control" id="MAX_DATA_LENGTH" value="8"></td>
<td>unitless</td>
<td>This is the number of elements that will be transmitted (or the size of the array)</td>
</tr>
<tr>
<td>Dispatch RX Buffer recommended length</td>
<td><input type="text" class="form-control" id="RX_BUFFER_RECOMMENDED_LENGTH" value="" readonly></td>
<td>bytes</td>
<td>This is a minimum and is non-optional. This should always be a power of 2.</td>
</tr>
</table>
<script>
function setDispatchRxBufferLength(){
var topicStrLen = parseInt($('#MAX_TOPIC_STR_LENGTH').val()) + 1;
var dims = parseInt($('#DIMENSIONS').val());
var dimWidth = parseInt($('#DIM_WIDTH').val());
var dataLen = parseInt($('#MAX_DATA_LENGTH').val());
var buffer = topicStrLen + 4 + ((1 + dims) >> 1) + (dimWidth * dataLen);
/* ensure that the buffer is a power of 2 */
var i = 1;
while(Math.pow(2, i) < buffer){
console.log(i, buffer);
i++;
}
buffer = Math.pow(2, i);
$('#RX_BUFFER_RECOMMENDED_LENGTH').val(buffer);
var msgLength = parseInt($('#RX_BUFFER_RECOMMENDED_LENGTH').val());
var frameBufferLength = 2 * msgLength;
$('#RECOMMENDED_FRAME_BUFFER').val(frameBufferLength);
$('#MAX_NUM_OF_FORMAT_SPECIFIERS').val(dims);
$('#MAX_TOPIC_STR_LEN').val(topicStrLen);
$('#MAX_RECEIVE_MESSAGE_LEN').val(msgLength);
$('#RX_FRAME_LENGTH').val(frameBufferLength);
}
$('#MAX_TOPIC_STR_LENGTH').change(function(){setDispatchRxBufferLength();});
$('#DIMENSIONS').change(function(){setDispatchRxBufferLength();});
$('#DIM_WIDTH').change(function(){setDispatchRxBufferLength();});
$('#MAX_DATA_LENGTH').change(function(){setDispatchRxBufferLength();});
$(document).ready(function(){setDispatchRxBufferLength();});
</script>
<h4>Frame RX Buffer</h4>
<p>The simplest way to determine the framing RX buffer is to simply double the size of the Dispatch
message buffer. This will give a worst-case estimate that will ALWAYS be adequate under all
circumstances. The reason for this is that the framing process adds escape characters when
certain numbers are encountered. If the message consists of nearly all escape characters, then
the message simply doubles in size. This is an unlikey event in some applications, but entirely
plausible in others. For instance, it is not uncommon for sensor data to dwell on a particular
number for some time. Ultimately, you must make the decision regarding this.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Description</th>
<th>Value</th>
<th>Unit</th>
</tr>
<tr>
<td>Recommended Frame Buffer</td>
<td><input type="text" class="form-control" id="RECOMMENDED_FRAME_BUFFER" value="" readonly></td>
<td>bytes</td>
</tr>
</table>
<h4>UART RX Buffer</h4>
<p>The UART RX Buffer is where the hardware interfaces with the software. In some cases, there will be
hardware buffers on-die that will be adequate. On many microcontrollers, this will not be the case
and software buffers will need to be implemented. The size of this buffer is determined by two
factors: baud rate and the frequency that <code>DIS_process()</code> is called.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Description</th>
<th>Value</th>
<th>Unit</th>
<th>Notes</th>
</tr>
<tr>
<td>Interface Data Rate</td>
<td><input type="text" class="form-control" id="DATA_RATE" value="57600"></td>
<td>bits/s</td>
<td>Decrease this to decrease the recommended buffer size</td>
</tr>
<tr>
<td>Frequency of `DIS_process()`</td>
<td><input type="text" class="form-control" id="PROCESS_RATE" value="1000"></td>
<td>calls/s</td>
<td>Increase this to decrease the recommended buffer size</td>
</tr>
<tr>
<td>Recommended Driver Buffer Size</td>
<td><input type="text" class="form-control" id="DRIVER_BUFFER_SIZE" value="" readonly></td>
<td>bytes</td>
<td></td>
</tr>
</table>
<script>
function setDriverRxBufferLength(){
var dataRateBytes = parseInt($('#DATA_RATE').val())/8;
var processRate = parseInt($('#PROCESS_RATE').val());
var driverBuffer = Math.ceil(dataRateBytes/processRate);
$('#DRIVER_BUFFER_SIZE').val(driverBuffer);
}
$('#DATA_RATE').change(function(){setDriverRxBufferLength();});
$('#PROCESS_RATE').change(function(){setDriverRxBufferLength();});
$(document).ready(function(){setDriverRxBufferLength();});
</script>
<h4>Summary</h4>
<p>In the <a href="https://github.com/slightlynybbled/Dispatch/tree/master/examples">examples</a>
there is a file called 'dispatch_config.h' which contains some defines. Here, we will
boil the above entries into those defines just to go the extra mile to make your
experience a bit easier.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Define</th>
<th>Value</th>
</tr>
<tr>
<td>MAX_NUM_OF_FORMAT_SPECIFIERS</td>
<td><input type="text" class="form-control" id="MAX_NUM_OF_FORMAT_SPECIFIERS" value="" readonly></td>
</tr>
<tr>
<td>MAX_TOPIC_STR_LEN</td>
<td><input type="text" class="form-control" id="MAX_TOPIC_STR_LEN" value="" readonly></td>
</tr>
<tr>
<td>MAX_RECEIVE_MESSAGE_LEN</td>
<td><input type="text" class="form-control" id="MAX_RECEIVE_MESSAGE_LEN" value="" readonly></td>
</tr>
<tr>
<td>RX_FRAME_LENGTH</td>
<td><input type="text" class="form-control" id="RX_FRAME_LENGTH" value="" readonly></td>
</tr>
</table>
<h2>Initialization</h2>
<h3>Include the Source Files</h3>
<p>In our case, we are including our hardware source file, <code>uart.h</code>, along with the dispatch header file,
<code>dispatch.h</code>:</p>
<div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf">"uart.h"</span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf">"dispatch.h</span><span class="cp"></span>
</pre></div>
<h3>Initializing the Hardware</h3>
<p>It is usually necessary to initialize the hardware independently of Dispatch. For a serial module, this
configures the registers to communicate on the desired channel at a particular baud rate. We have called
our pin hardware initializer <code>DIO_init()</code> and the channel initializer <code>UART_init()</code>.</p>
<div class="highlight"><pre><span></span><span class="n">DIO_init</span><span class="p">();</span>
<span class="n">UART_init</span><span class="p">();</span>
</pre></div>
<h3>Initializing Dispatch</h3>
<p>There are two items that need to be tended to, initializing Dispatch itself and assigning the function that
Dispatch will utilize for communication.</p>
<div class="highlight"><pre><span></span><span class="cm">/* Assign the four necessary channel functions to Dispatch. */</span>
<span class="n">DIS_assignChannelReadable</span><span class="p">(</span><span class="o">&</span><span class="n">UART_readable</span><span class="p">);</span>
<span class="n">DIS_assignChannelWriteable</span><span class="p">(</span><span class="o">&</span><span class="n">UART_writeable</span><span class="p">);</span>
<span class="n">DIS_assignChannelRead</span><span class="p">(</span><span class="o">&</span><span class="n">UART_read</span><span class="p">);</span>
<span class="n">DIS_assignChannelWrite</span><span class="p">(</span><span class="o">&</span><span class="n">UART_write</span><span class="p">);</span>
<span class="cm">/* Initialize Dispatch */</span>
<span class="n">DIS_init</span><span class="p">();</span>
</pre></div>
<p>As you can see, our communication hardware functions were very similary named to the Dispatch functions so as to
make the assignments easier to identify.</p>
<h3>Processing</h3>
<p>After initialization, <code>DIS_process()</code> must be called periodically in order to process incoming data and
subscriptions. Generally, this is placed into the infinite <code>while(1)</code> loop, but it can be assigned to a task
or other periodic function.</p>
<div class="highlight"><pre><span></span><span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">){</span>
<span class="n">DIS_process</span><span class="p">();</span>
<span class="cm">/* ... other continually executing functions ... */</span>
<span class="p">}</span>
</pre></div>
<p>It is not recommended to place <code>DIS_process()</code> within a timer interrupt as it may block all of your other interrupts,
depending on architecture and configuration of your device.</p>
<h1>Using Dispatch</h1>
<h2>Publishing</h2>
<p>As described in <a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">the initial post</a>,
publishing to Dispatch is easy. We have renamed some of the functions to keep them from potentially colliding with
other functions, but the functionality has not changed. A quick summary:</p>
<div class="highlight"><pre><span></span><span class="cm">/* send "my string" to subscribers of "foo" */</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"foo"</span><span class="p">,</span> <span class="s">"my string"</span><span class="p">);</span>
<span class="cm">/* send first element of bar[] to subscribers of "foo" */</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"foo,u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
<span class="cm">/* send 10 elements of bar[] to subscribers of "foo" */</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"foo:10,u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
<span class="cm">/* send 10 elements of bar[] and baz[] to subscribers of "foo" */</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"foo:10,s16,s32"</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">baz</span><span class="p">);</span>
<span class="c1">// ^ ^ data sources</span>
<span class="c1">// ^ ^ format specifiers</span>
<span class="c1">// ^ number of elements to send</span>
<span class="c1">// ^ topic</span>
</pre></div>
<p>When not sending a string, the format specifiers must be in place for each array of data to be sent. Use the format
specifiers shown here:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Format Specifier</th>
<th>Signed/Unsigned</th>
<th>Width (bytes)</th>
</tr>
<tr>
<td>u8</td>
<td>unsigned</td>
<td>1</td>
</tr>
<tr>
<td>s8</td>
<td>signed</td>
<td>1</td>
</tr>
<tr>
<td>u16</td>
<td>unsigned</td>
<td>2</td>
</tr>
<tr>
<td>s16</td>
<td>signed</td>
<td>2</td>
</tr>
<tr>
<td>u32</td>
<td>unsigned</td>
<td>4</td>
</tr>
<tr>
<td>s32</td>
<td>signed</td>
<td>4</td>
</tr>
<tr>
<td>(str)</td>
<td>-</td>
<td>-</td>
</tr>
</table>
<h2>Alternate Publishing Methods</h2>
<p>In order to reduce the program memory footprint, we have introduced less dynamic publishing functions
which perform the same function as <code>DIS_publish()</code>, but simply use less memory by making the function
less generic.</p>
<p>For instance, the two publish functions will result in sending the same data:</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"foo:10,u8"</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
<span class="n">DIS_publish_u8</span><span class="p">(</span><span class="s">"foo:10"</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
</pre></div>
<p>We have reduce the amount of processing and program memory footprint necessary to send a transmission.
This approach is only recommended for situations in which the program memory is limited.</p>
<h2>Subscribing</h2>
<p>Subscribing usually happens before the infinite <code>while(1)</code>, but can happen at any time, even in
response to other events.</p>
<p>First, we must write the subscribing function. Our subscribing function is going to increment a
local counter and then publish that counter back to topic "i":</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriberFunction</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="kt">uint16_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="cm">/* publish i back to the sender to 'close the loop' */</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"i,u16"</span><span class="p">,</span> <span class="o">&</span><span class="n">i</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
<p>At some point, we must actually subscribe the function to the topic to create the association within
Dispatch:</p>
<div class="highlight"><pre><span></span><span class="n">DIS_subscribe</span><span class="p">(</span><span class="s">"foo"</span><span class="p">,</span> <span class="o">&</span><span class="n">mySubscriberFunction</span><span class="p">);</span>
</pre></div>
<p>Now, every time that the topic "foo" is received, Dispatch calls <code>mySubscriberFunction()</code> which
increments <code>i</code> and publishes it to topic "i".</p>
<h2>Retrieving Topical Data</h2>
<p>There is usually some payload sent with the topic. This can be string data or numeric, of 8, 16, or 32 bits.
It may be a single data point or consist of an array or multiple arrays of data. How do we get the approprate
data? In this, we will use the <code>DIS_getElements()</code> function.</p>
<p>Topical data should always have the same format. It is possible to send different data formats to the same topic,
but there is no way to distinguish one format from another. Best to stick with one format per topic.</p>
<p>The <code>DIS_getElements(uint16_t element, void* destArray)</code> function takes two arguments, <code>element</code> and <code>destArray</code>.
The <code>element</code> is the element number that is expected. In single dimensional data - such as strings, single numbers,
and singe arrays - this number will be <code>0</code>. In multidimensional data, this may be a different number in order to
retrieve different values. A few examples should clear it up.</p>
<h3>Retrieving a String</h3>
<p>For instance, if the topic is known to receive a string, then allocate string storage within the subscribing function
and use <code>DIS_getElements to retrieve it</code>:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriberFunction</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="kt">char</span> <span class="n">str</span><span class="p">[</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span> <span class="c1">// allocate your string array</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">str</span><span class="p">);</span> <span class="c1">// copy the received data into str</span>
<span class="cm">/* now use the data here */</span>
<span class="p">}</span>
</pre></div>
<h3>Retrieving a Number</h3>
<p>To retrieve a single number, we use a similar notation. When <code>mySubscriberFunction</code> is expecting a single unsigned
integer:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriberFunction</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="kt">uint16_t</span> <span class="n">v</span><span class="p">;</span> <span class="c1">// allocate memory</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">&</span><span class="n">v</span><span class="p">);</span> <span class="c1">// copy the received data into local variable</span>
<span class="cm">/* now use the data here */</span>
<span class="p">}</span>
</pre></div>
<h3>Multiple Dimensions</h3>
<p>To transmit more than one variable, multiple calls to <code>DIS_getElements()</code> will be necessary. Note that the
different calls will specify different <code>element</code> numbers. In the below function, we are mixing different
integer widths and signs and still receiving them appropriately, so long as the correct element is retrieved:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriberFunction</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="kt">uint16_t</span> <span class="n">a</span><span class="p">;</span> <span class="c1">// allocate memory</span>
<span class="kt">int16_t</span> <span class="n">b</span><span class="p">;</span>
<span class="kt">int32_t</span> <span class="n">c</span><span class="p">;</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">&</span><span class="n">a</span><span class="p">);</span> <span class="c1">// copy the received data into local variable</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&</span><span class="n">b</span><span class="p">);</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="o">&</span><span class="n">c</span><span class="p">);</span>
<span class="cm">/* now use the data here */</span>
<span class="p">}</span>
</pre></div>
<h3>Arrays</h3>
<p>It is also possible to retrieve arrays of data, so long as the array length is pre-determined. In fact,
transmitting data within an array is <em>much</em> more bandwidth-efficient and has a very similar syntactical
complexity:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriberFunction</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="kt">uint16_t</span> <span class="n">v</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// allocate memory</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> <span class="c1">// copy the received data into local array</span>
<span class="cm">/* now use the data here */</span>
<span class="p">}</span>
</pre></div>
<p>One may even retrieve a multi-dimensional array with similar effort, even with different data
widths:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriberFunction</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="kt">uint16_t</span> <span class="n">x</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// allocate memory</span>
<span class="kt">int8_t</span> <span class="n">y</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span> <span class="c1">// copy the received data into local array</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">y</span><span class="p">);</span>
<span class="cm">/* now use the data here */</span>
<span class="p">}</span>
</pre></div>
<p>And, just like that, 10 elements of <code>x</code> and <code>y</code> are received.</p>
<h1>Function Reference</h1>
<p>This document is intended to list all of the available functions of Dispatch. Move over
to the <a href="http://www.forembed.com/dispatch-how-to.html">Dispatch How-To</a> for a comprehensive
guide for using Dispatch.</p>
<p>Source code may be found on <a href="https://github.com/slightlynybbled/Dispatch">github</a>.</p>
<h2>Basic Functions</h2>
<p>This table contains the basic functions which make up the Dispatch library. Most application will use all
of these functions in one form or another.</p>
<h3>Channel Assignment and Initialization</h3>
<p>These functions should only be called at the beginning of program execution.</p>
<div class="highlight"><pre><span></span><span class="cm">/* Assigns the function which is used to determine how many bytes are currently </span>
<span class="cm"> readable from the UART RX buffer. */</span>
<span class="kt">void</span> <span class="nf">DIS_assignChannelReadable</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)());</span>
<span class="cm">/* Assigns the function which is used to determine how many bytes may be written to</span>
<span class="cm"> the UART TX buffer. */</span>
<span class="kt">void</span> <span class="nf">DIS_assignChannelWriteable</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)());</span>
<span class="cm">/* Assigns the function which is used to read from the UART RX buffer. */</span>
<span class="kt">void</span> <span class="nf">DIS_assignChannelRead</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">));</span>
<span class="cm">/* Assigns the function which is used to write to the UART TX buffer. */</span>
<span class="kt">void</span> <span class="nf">DIS_assignChannelWrite</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">));</span>
<span class="cm">/* Initializes Dispatch. Must be called after the `DIS_assignChannel` functions. */</span>
<span class="kt">void</span> <span class="nf">DIS_init</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</pre></div>
<h3>Normal Use</h3>
<h4>Subscribing/Unsubscribing</h4>
<p>Subscribing to a topic will likely only occur once during initialization, but it is possible
to dynamically subscribe and unsubscribe to topics. The <code>DIS_getElements</code> function is
utilized to retrieve data within the subscriber.</p>
<div class="highlight"><pre><span></span><span class="cm">/* Subscribes to a topic. Normally called one time at initialization, but</span>
<span class="cm"> may be called at any time during program execution */</span>
<span class="kt">void</span> <span class="nf">DIS_subscribe</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)());</span>
<span class="cm">/* Removes a subscriber from the subscription list. If the subscriber is not active, then</span>
<span class="cm"> there is no action. */</span>
<span class="kt">void</span> <span class="nf">DIS_unsubscribe</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)());</span>
</pre></div>
<h4>Retrieving Data</h4>
<p>Retrieving data is - hopefully - as simple as sending it. Note that it must be completed within
the subscribing function.</p>
<div class="highlight"><pre><span></span><span class="cm">/* Retrieve data received on the RX. */</span>
<span class="kt">uint16_t</span> <span class="nf">DIS_getElements</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">element</span><span class="p">,</span> <span class="kt">void</span><span class="o">*</span> <span class="n">destArray</span><span class="p">);</span>
</pre></div>
<p>The <code>element</code> is the element number which is to be retrieved. Generally, if it warrants a new variable,
then it will have its own element number within a particular topic.</p>
<p>The <code>destArray</code> is the address of a variable to which the data is to be stored.</p>
<h4>Publishing Data</h4>
<div class="highlight"><pre><span></span><span class="cm">/* Publishes data to a topic. This is the most generic publish function and has the most</span>
<span class="cm"> flexibility. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="p">...);</span>
</pre></div>
<h4>Processing Dispatch</h4>
<div class="highlight"><pre><span></span><span class="cm">/* Processes incoming messages and calls the appropriate subscribers. Must be called</span>
<span class="cm"> periodically. If this is called infrequently, then any subscriber functions are also called</span>
<span class="cm"> infrequently and may be missed. */</span>
<span class="kt">void</span> <span class="nf">DIS_process</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</pre></div>
<h2>Reduced Memory Publishing</h2>
<p>These functions are intended to replace the <code>DIS_publish()</code> function above with a specific variant in order
to reduce the program memory footprint of Dispatch in some applications. It is not necessary to utilize any of
these functions. All of these could be replaced by an appropriate call to <code>DIS_publish()</code>.</p>
<p>These functions are available in releases of Dispatch that are greater than v1.0.</p>
<div class="highlight"><pre><span></span><span class="cm">/* Publishes a string to a topic without having to use `stdarg.h`. If the user only needs to send a</span>
<span class="cm"> string using Dispatch, then this will be smaller and faster than `DIS_publish()` */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_str</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">str</span><span class="p">);</span>
<span class="cm">/* Publishes a single unsigned 8-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_u8</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">);</span>
<span class="cm">/* Publishes a single signed 8-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_s8</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">int8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">);</span>
<span class="cm">/* Publishes two unsigned 8-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_2u8</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data0</span><span class="p">,</span> <span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data1</span><span class="p">);</span>
<span class="cm">/* Publishes two signed 8-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_2s8</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">int8_t</span><span class="o">*</span> <span class="n">data0</span><span class="p">,</span> <span class="kt">int8_t</span><span class="o">*</span> <span class="n">data1</span><span class="p">);</span>
<span class="cm">/* Publishes a single unsigned 16-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_u16</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">uint16_t</span><span class="o">*</span> <span class="n">data</span><span class="p">);</span>
<span class="cm">/* Publishes a single signed 16-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_s16</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">int16_t</span><span class="o">*</span> <span class="n">data</span><span class="p">);</span>
<span class="cm">/* Publishes a single unsigned 32-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_u32</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">uint32_t</span><span class="o">*</span> <span class="n">data</span><span class="p">);</span>
<span class="cm">/* Publishes a single signed 32-bit array to the topic without using `stdarg.h`. If this user only</span>
<span class="cm"> needs to send an 8-bit array using Dispatch, then this will be smaller and faster than `DIS_publish()`. */</span>
<span class="kt">void</span> <span class="nf">DIS_publish_s32</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">topic</span><span class="p">,</span> <span class="kt">int32_t</span><span class="o">*</span> <span class="n">data</span><span class="p">);</span>
</pre></div>Load Time from a Browser to a Raspberry Pi2017-08-18T22:16:00-04:002017-08-18T22:16:00-04:00Jason Jonestag:www.forembed.com,2017-08-18:/load-time-from-browser-to-a-raspberry-pi.html<p><img class="img img-responsive" alt="Set Pi Time" src="http://www.forembed.com/images/2017/08/set-pi-time.png"></p>
<h1>Introduction</h1>
<p>I recently had a work move and wanted to make something of an impression. My
ambition to be, both, productive and cool has led me to the relatively simple
project of placing a coffee cam in the workplace.</p>
<p>Ideally, I could attach a pi with a camera to the …</p><p><img class="img img-responsive" alt="Set Pi Time" src="http://www.forembed.com/images/2017/08/set-pi-time.png"></p>
<h1>Introduction</h1>
<p>I recently had a work move and wanted to make something of an impression. My
ambition to be, both, productive and cool has led me to the relatively simple
project of placing a coffee cam in the workplace.</p>
<p>Ideally, I could attach a pi with a camera to the network and everyone could
just bookmark the IP address. Unfortunately, my IT department is unlikely to
approve this, so I am forced to keep my project offline. Using a wifi dongle,
I am using the pi as a hotspot that anyone could log in to with their phone and use a browser to view the camera.</p>
<p>As a side-part of this project, I would like to keep a few basic stats. The
pi - not having a real-time clock - is making this more difficult than I
initially thought it would be.</p>
<p>How should I load time on a non-internet connected raspberry pi without
having to log in on every reboot and set it?</p>
<h1>Architecture</h1>
<p>Though the purpose of this post isn't to document the coffee cam, it would
help to have some of the architecture sketched out.</p>
<p><img class="img img-responsive" alt="CoffeeCam Architecture" src="http://www.forembed.com/images/2017/08/coffeecam-arch.png"></p>
<p>The pi hosts its own local wifi network to which local clients can connect.</p>
<p>Simple, right?</p>
<h1>Solution</h1>
<h2>Client-Side</h2>
<p>In speaking with a colleague, the solution naturally arose that each client
that connects is likely to already have the local time. As it turns out,
accessing the <code>Date()</code> object in javascript gives you 80% of what you need
to set the time on your pi. Simply <code>POST</code> the time back to the web server,
add a bit of parsing, and Bob's your uncle! I prefer to use
<a href="https://jquery.com/">jquery</a> for DOM manipulation.</p>
<div class="highlight"><pre><span></span><span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">ready</span><span class="p">(</span>
<span class="kd">function</span><span class="p">(){</span>
<span class="cm">/* submit time using a post request */</span>
<span class="kd">var</span> <span class="nx">date</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span>
<span class="s1">'/set_time'</span><span class="p">,</span>
<span class="p">{</span>
<span class="s1">'date'</span><span class="o">:</span> <span class="nx">date</span><span class="p">.</span><span class="nx">getTime</span><span class="p">(),</span>
<span class="s1">'timezoneOffset'</span><span class="o">:</span> <span class="nx">date</span><span class="p">.</span><span class="nx">getTimezoneOffset</span><span class="p">()</span> <span class="o">*</span> <span class="mf">60.0</span>
<span class="p">}</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">);</span>
</pre></div>
<p>A couple of nuances to explain. The <code>.getTime()</code> method returns milliseconds
since epoch. Completely unambiguous. The <code>.getTimeZone()</code> method returns
the number of minutes offset from GMT. We multiply by 60 to get to seconds
on the client.</p>
<h2>Server-Side</h2>
<p>The server is running an instance of Python <a href="http://flask.pocoo.org/">Flask</a>.
We simply create a route to which to <code>POST</code>.</p>
<div class="highlight"><pre><span></span><span class="nd">@app.route</span><span class="p">(</span><span class="s1">'/set_time'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s1">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">set_time</span><span class="p">():</span>
<span class="n">date</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">flask</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">'date'</span><span class="p">])</span><span class="o">/</span><span class="mi">1000</span><span class="p">)</span>
<span class="n">timezone_offset</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">flask</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s1">'timezoneOffset'</span><span class="p">])</span>
<span class="n">dt</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">fromtimestamp</span><span class="p">(</span><span class="n">date</span><span class="p">)</span>
<span class="n">now</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span>
<span class="k">if</span> <span class="n">dt</span> <span class="o"><</span> <span class="n">now</span> <span class="o">+</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">hours</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="o">></span> <span class="n">dt</span> <span class="ow">or</span> <span class="n">dt</span> <span class="o">></span> <span class="n">now</span> <span class="o">-</span> <span class="n">datetime</span><span class="o">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">hours</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s1">'time already set, leaving it alone'</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">logger</span><span class="o">.</span><span class="n">warning</span><span class="p">(</span><span class="s1">'setting time to {}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">dt</span><span class="p">))</span>
<span class="n">new_dt</span> <span class="o">=</span> <span class="n">dt</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s1">'%m/</span><span class="si">%d</span><span class="s1">/%y %H:%M:%S'</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">system</span><span class="p">(</span><span class="s1">'hwclock --set --date="{}"'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">new_dt</span><span class="p">))</span>
<span class="k">return</span> <span class="s1">''</span><span class="p">,</span> <span class="mi">204</span>
</pre></div>
<p>The one mild complication. If every client that connected has a slightly
different time, then the local clock would be changed constantly. To keep
this from happening, a reported time is compared to the local time and, if
it is within an hour, then the time doesn't get set.</p>
<h1>Final Thoughts</h1>
<p>Cool solution, but it could use some refinement. If there is some guy who
just won't keep the right time, the current solution could result in frequent
time shifting. In the future, we may do some form of saving multiple times
and having some level of voting to set the time, but this refinement may be
a bit much for a coffeecam.</p>Curve Tracer Reference2017-06-13T20:10:00-04:002017-06-13T20:10:00-04:00Jason Jonestag:www.forembed.com,2017-06-13:/curve-tracer-reference.html<h1>Purpose</h1>
<p><img src="/images/2017/06/curve-tracer-screenshot-diode.png" alt="Curve Tracer Screenshot" class="img-responsive img-rounded"></p>
<p>This document is intended to document the curve tracer This document will be updated through
revisions and can be used as a persistent reference, regardless of the 'date posted'. As more
information becomes available, documentation will be added.</p>
<p>The original post for the curve tracer can be found <a href="http://www.forembed.com/project-curve-tracer-requirements.html">here …</a></p><h1>Purpose</h1>
<p><img src="/images/2017/06/curve-tracer-screenshot-diode.png" alt="Curve Tracer Screenshot" class="img-responsive img-rounded"></p>
<p>This document is intended to document the curve tracer This document will be updated through
revisions and can be used as a persistent reference, regardless of the 'date posted'. As more
information becomes available, documentation will be added.</p>
<p>The original post for the curve tracer can be found <a href="http://www.forembed.com/project-curve-tracer-requirements.html">here</a>.
Note that the specifications have changed somewhat since project inception based
on hardware limitations and other factors.</p>
<h1>Current Revision</h1>
<p>The current revision for the Curve Tracer is 1.2. The schematic and layout may have minor revisions released independently, depending
on the scope of the change.</p>
<table class="table table-striped table-bordered">
<tr>
<th>Document</th>
<th>Current Revision</th>
<th>Location</th>
</tr>
<tr>
<td>Curve Tracer Assembly</td>
<td>1.2</td>
<td><a href="https://github.com/slightlynybbled/curve_tracer_hardware" target="_blank">github</a></td>
</tr>
<tr>
<td>Schematic/BOM</td>
<td>1.2</td>
<td><a href="https://github.com/slightlynybbled/curve_tracer_hardware" target="_blank">github</a></td>
</tr>
<tr>
<td>Layout</td>
<td>1.2</td>
<td><a href="https://github.com/slightlynybbled/curve_tracer_hardware" target="_blank">github</a></td>
</tr>
<tr>
<td>Firmware</td>
<td>-unreleased-</td>
<td><a href="https://github.com/slightlynybbled/curve_tracer_firmware" target="_blank">github</a></td>
</tr>
<tr>
<td>Desktop GUI</td>
<td>0.1.0</td>
<td><a href="https://github.com/slightlynybbled/curve_tracer_gui" target="_blank">github</a></td>
</tr>
</table>
<p>A "working" revision of all of the above may be found in the <a href="https://github.com/slightlynybbled/project_curve_tracer">github repository</a>.</p>
<h1>Hardware</h1>
<p><img class="img-responsive" style="border-radius: 20px;" src="http://www.forembed.com/images/2016/08/curve-tracer-1v2-top.jpg" alt="Curve Tracer, Top"></p>
<table class="table table-striped table-bordered">
<tr>
<th>Connection</th>
<th>Description</th>
</tr>
<tr>
<td>P1</td>
<td>Header, 0.1", utilize for flash programming the microcontroller using a Microchip ICD3 or similar.</td>
</tr>
<tr>
<td>P2</td>
<td>USB Micro-B Connector utilized to create a Virtual COM Port for PC connection.</td>
</tr>
<tr>
<td>P3</td>
<td>XY header connection for probe tip terminals of an oscilloscope probe.</td>
</tr>
<tr>
<td>P4</td>
<td>Socket, 0.1", utilize to place a TO-220 or other through-hole components to be traced.</td>
</tr>
<tr>
<td>P8, P9</td>
<td>Pads for easy connection to the GND terminals of an oscilloscope probe.</td>
</tr>
</table>
<p><img class="img-responsive" style="border-radius: 20px;" src="http://www.forembed.com/images/2016/08/curve-tracer-1v2-bottom.jpg" alt="Curve Tracer, Bottom"></p>
<table class="table table-striped table-bordered">
<tr>
<th>Connection</th>
<th>Description</th>
</tr>
<tr>
<td>P6</td>
<td>Safety (multimeter) probe connector, 'positive'.</td>
</tr>
<tr>
<td>P7</td>
<td>Safety (multimeter) probe connector, 'positive'.</td>
</tr>
</table>
<h2>Connector Details</h2>
<p>Each multiple-contact connector is detailed in this section. Connectors with a single function -
such as USB - are not detailed.</p>
<h3>ICSP - P1</h3>
<table class="table table-striped table-bordered">
<tr>
<th>Pin</th>
<th>Description</th>
</tr>
<tr>
<td>1</td>
<td>~MCLR/Vpp</td>
</tr>
<tr>
<td>2</td>
<td>+5V</td>
</tr>
<tr>
<td>3</td>
<td>GND</td>
</tr>
<tr>
<td>4</td>
<td>PGD</td>
</tr>
<tr>
<td>5</td>
<td>PGC</td>
</tr>
</table>
<h3>Probe Connections - P6, P7</h3>
<table class="table table-striped table-bordered">
<tr>
<th>Pin</th>
<th>Description</th>
</tr>
<tr>
<td>P6</td>
<td>V+, positive probe connection.</td>
</tr>
<tr>
<td>V-</td>
<td>V-, negative probe connection.</td>
</tr>
</table>
<h3>MOSFET/Transistor Connection, P4</h3>
<table class="table table-striped table-bordered">
<tr>
<th>Pin</th>
<th>Description</th>
</tr>
<tr>
<td>1</td>
<td>A high-impedance voltage output intended to apply a voltage to a MOSFET or similar high-impedance device, also called the 'gate'</td>
</tr>
<tr>
<td>2</td>
<td>Positive probe connection, 'drain'</td>
</tr>
<tr>
<td>3</td>
<td>Negative probe connection, 'source'</td>
</tr>
<tr>
<td>4</td>
<td>'drain'</td>
</tr>
<tr>
<td>5</td>
<td>'source'</td>
</tr>
<tr>
<td>6</td>
<td>'gate'</td>
</tr>
</table>
<h2>Pin Specifications</h2>
<table class="table table-striped table-bordered">
<tr>
<th>Parameter</th>
<th>Value</th>
</tr>
<tr>
<td>Maximum Absolute Voltage, V+, V-</td>
<td>5.0V</td>
</tr>
<tr>
<td>Minimum Absolute Voltage, V+, V-</td>
<td>0.0V</td>
</tr>
<tr>
<td>Maximum Differential Voltage, V+, V-</td>
<td>5.0V</td>
</tr>
<tr>
<td>Minimum Differential Voltage, V+, V-</td>
<td>-5.0V</td>
</tr>
<tr>
<td>Maximum Current, V+, V-</td>
<td>+/-20mA</td>
</tr>
<tr>
<td>Maximum Absolute Voltage, Vgate</td>
<td>5.0V</td>
</tr>
<tr>
<td>Minimum Absolute Voltage, Vgate</td>
<td>0.0V</td>
</tr>
<tr>
<td>Maximum Current, Vgate</td>
<td>2.5mA</td>
</tr>
<tr>
<td>Output Impedance, Vgate</td>
<td>2kΩ</td>
</tr>
</table>
<h1>Firmware</h1>
<p>If you have received units from me, then you do not need to compile or program the board!
You may skip this section! If you have built the boards yourself, then you will
want to download, compile, and load the firmware.</p>
<p>In order to compile and load the firmware yourself, you will need
<a href="http://www.microchip.com/mplab/mplab-x-ide" target="_blank">MPLAB X</a> and the
<a href="http://www.microchip.com/mplab/compilers" target="_blank">XC16 Compiler</a> installed
on your machine.</p>
<ol>
<li>Create a new project in MPLAB X (File -> New Project)</li>
<li>Select 'standalone project'</li>
<li>Select the device - PIC24FV16KM202</li>
<li>Select the directory in which you would prefer to save your project</li>
<li>Select the XC16 compiler, then the debugging tool that you will be using for programming
(probably an ICD3 or PICkit)</li>
<li>Add all <code>*.h</code> files into the 'headers'.</li>
<li>Add all <code>*.c</code> and <code>*.s</code> files into the 'source'</li>
<li>Enter the compiler configuration and select optimization <code>-O1</code> or the compiler may not be able
to allocate enough program memory. I usually like to isolate each function into its own section
and tell the linker to remove unused sections in order to reduce memory usage.</li>
<li>Program your board!</li>
</ol>
<p>You will also need to change the configuration of the FT230X. Detailed instructions may be found
<a href="http://www.forembed.com/project-curve-tracer-configuring-the-oscillator.html">on the blog</a>.</p>
<h1>GUI</h1>
<p>The GUI is written in Python 3. Does this mean you need to install
Python or know how it works? No! There are three options for installation:</p>
<ol>
<li>native Python (pip-installed)</li>
<li>native Python (setup-installed)</li>
<li>download executable</li>
</ol>
<p>The GUI should work on all development environments that support python 3
(windows, linux, mac). Note that linux installations may require that certain
libraries be installed. On debian based systems, these would be installed
using <code>apt</code>:</p>
<div class="highlight"><pre><span></span>$> sudo apt install python3-dev python3-setuptools python-imaging python3-pil.imagetk
$> sudo apt install libjpeg-dev libtiff4-dev zlib1g-dev libfreetype6-dev libwebp-dev
$> sudo apt install tcl-dev tk-dev
</pre></div>
<p>User documentation will be available in a readthedocs format soon!</p>
<p><img src="/images/2017/06/curve-tracer-screenshot-diode.png" alt="Curve Tracer Screenshot" class="img-responsive img-rounded"></p>
<h2>Pip-Installed</h2>
<p>Execute <code>pip install curve_tracer</code> in your root Python or in your virtual
environment. Once it is installed, then simply <code>curve_tracer</code> on the
command line should open the GUI.</p>
<h2>Setup-Installed</h2>
<p>Clone the <a href="https://github.com/slightlynybbled/curve_tracer_gui">git repository</a>,
navigate to the <code>/curve_tracer_gui</code> directory, and <code>python3 setup.py install</code>.</p>
<p>Once installed, simply type <code>curve_tracer</code> in your command window to
begin the GUI.</p>
<h2>Executable (Windows)</h2>
<p>For the windows users, we have utilized
<a href="http://www.pyinstaller.org/">pyinstaller</a> to create a single-file
executable that does not require installation of the python
environment. Simply download the executable from the
<a href="https://github.com/slightlynybbled/curve_tracer_gui/releases">github releases</a>
and enjoy! Still working on linux, guys...</p>
<h1>User Notes</h1>
<p>When this equipment is utilized as a curve tracer, it <strong>MUST</strong> be utilized without power applied
to the unit under test (UUT). There are many varieties that emulate power off by going into a standby
state, but this is <strong>NOT</strong> power off! Be sure that the device is completely unplugged and leave
unplugged for several seconds to give any capacitors time to discharge.</p>
<p>Vgate is intended to hold a constant DC voltage across the gate of a MOSFET. Any resistive load placed
on Vgate will potentially load Vgate beyond its driving capacity.</p>Engineering Notation in Python2017-06-02T21:01:00-04:002017-06-02T21:01:00-04:00Jason Jonestag:www.forembed.com,2017-06-02:/engineering-notation-in-python.html<p><img src="/images/2017/06/eng-notation-screenshots.png" alt="Engineering Notation Screenshot" class="img-responsive img-rounded"></p>
<h1>Engineering Notation</h1>
<p>Anyone reading this will likely already have a background that covers
engineering notation, so I won't go into it deeply. Computer scientists
might also call engineering notation a 'human-readable' numbers. Instead
of <code>4096B</code> you might see <code>4kB</code> or <code>4.096kB</code>, depending on your precision.</p>
<p>There are <a href="https://en.wikipedia.org/wiki/Engineering_notation">more expansive …</a></p><p><img src="/images/2017/06/eng-notation-screenshots.png" alt="Engineering Notation Screenshot" class="img-responsive img-rounded"></p>
<h1>Engineering Notation</h1>
<p>Anyone reading this will likely already have a background that covers
engineering notation, so I won't go into it deeply. Computer scientists
might also call engineering notation a 'human-readable' numbers. Instead
of <code>4096B</code> you might see <code>4kB</code> or <code>4.096kB</code>, depending on your precision.</p>
<p>There are <a href="https://en.wikipedia.org/wiki/Engineering_notation">more expansive explanations</a>
all over the web if you want more information.</p>
<h1>Motivation</h1>
<h2>Explanation of Decimal Class</h2>
<p>The <code>Decimal</code> class is used when you want to do math the way that you
learned to do it in school. Numbers are not represented in the processor
as binary equivalents, they are represented as the exact value. Slower,
but more precise.</p>
<p>The python <code>Decimal</code> class actually has a built-in method <code>to_eng_string()</code>
appears to be written for this purpose, but actually doesn't work strictly
for all numbers, meaning that:</p>
<div class="highlight"><pre><span></span>>>> Decimal('1000000').to_eng_string()
1000000
</pre></div>
<p>This is not engineering notation! But it is in line with the standard
that is being implemented by <code>Decimal</code>. You have to actually give the
string value in scientific notation OR call the <code>normalize</code> method:</p>
<div class="highlight"><pre><span></span>>>> Decimal('1000000').normalize().to_eng_string()
100E+3
</pre></div>
<p>Unfortunately, it doesn't work as well for fractional numbers:</p>
<div class="highlight"><pre><span></span>>>> Decimal('0.01').to_eng_string()
0.01
>>> Decimal('0.01').normalize().to_eng_string()
0.01
</pre></div>
<p>This behavior isn't a fault in the library. As
<a href="https://stackoverflow.com/questions/12311148/print-number-in-engineering-format">some have explained</a>,
the library is doing exactly what it is written to do. It just isn't
exactly what I want.</p>
<h2>What Do I Want?</h2>
<p>Ideally, I want any number to always resolve into a human-readable form
presented in every ENG 101 class:</p>
<div class="highlight"><pre><span></span>>>> EngNumber('1000000')
1M
>>> EngNumber('0.1u')
100n
>>> EngNumber('1000m')
1
</pre></div>
<p>To get this behavior, consistently, it took writing a bit of code...</p>
<h1>Desired Features</h1>
<p>As explained above, the most fundamental features is the ability to resolve
any number into engineering notation the same way that you might on paper.
Some additional features that would add some usefulness:</p>
<ul>
<li>easy creation from floats/ints/strings</li>
<li>basic arithmetic</li>
<li>comparisons</li>
<li>proper parsing using <code>int()</code> and <code>float()</code></li>
<li>practical rounding strategy</li>
</ul>
<p>It would be value-added to be able to have units involved as well.</p>
<h2>Easy Variable Creation</h2>
<p>It should be <em>crazy</em> easy to create an <code>EngNumber</code>:</p>
<div class="highlight"><pre><span></span>>>> EngNumber('0.0102')
10.2m
>>> EngNumber('102m')
10.2m
>>> EngNumber('10.2e-2')
10.2m
>>> EngNumber(0.01)
10.2m
>>> EngNumber('10.2k')
10.2k
>>> EngNumber(10200)
10.2k
</pre></div>
<h2>Basic Arithmetic</h2>
<p>We should be able to do some easy math with the numbers as well:</p>
<div class="highlight"><pre><span></span>>>> EngNumber(10200) + EngNumber('0.4k')
10.6k
>>> EngNumber('10.2e3') - EngNumber(400)
9.8k
</pre></div>
<h2>Comparisons</h2>
<p>Basic comparisons should work out of the box, including comparisons with
floats, ints, and Decimals:</p>
<div class="highlight"><pre><span></span>>>> EngNumber(10200) == EngNumber('0.4k')
False
>>> EngNumber(10200) >= EngNumber('0.4k')
True
>>> EngNumber(10200) == 10200
True
</pre></div>
<h2>Proper Parsing using <code>int()</code> and <code>float()</code></h2>
<p>Does this need explanation?</p>
<div class="highlight"><pre><span></span>>>> n = EngNumber(10200)
>>> int(n)
10200
>>> float(n)
10200.0
</pre></div>
<h2>Practical Rounding Strategy</h2>
<p>Sometimes, adding numbers is just impractical:</p>
<div class="highlight"><pre><span></span>>>> EngNumber('10.2k') + EngNumber(1)
10.2k
</pre></div>
<h1>Source</h1>
<p>Source code may be found on
<a href="https://github.com/slightlynybbled/engineering_notation">github</a>,
including 100% test coverage.</p>
<p>Actually, using the <code>Decimal</code> class as a starting point works very
well since it implements 90% of the desired operations. All we have to
do is parse input strings for proper suffixes (<code>m</code>, <code>k</code>, etc.) and
replace <code>__repl__</code> and <code>__str__</code> with proper parsing to turn a <code>Decimal</code>
into a human-readable format.</p>
<div class="highlight"><pre><span></span><span class="n">_suffix_lookup</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'p'</span><span class="p">:</span> <span class="s1">'e-12'</span><span class="p">,</span>
<span class="s1">'n'</span><span class="p">:</span> <span class="s1">'e-9'</span><span class="p">,</span>
<span class="s1">'u'</span><span class="p">:</span> <span class="s1">'e-6'</span><span class="p">,</span>
<span class="s1">'m'</span><span class="p">:</span> <span class="s1">'e-3'</span><span class="p">,</span>
<span class="s1">''</span><span class="p">:</span> <span class="s1">'e0'</span><span class="p">,</span>
<span class="s1">'k'</span><span class="p">:</span> <span class="s1">'e3'</span><span class="p">,</span>
<span class="s1">'M'</span><span class="p">:</span> <span class="s1">'e6'</span><span class="p">,</span>
<span class="s1">'G'</span><span class="p">:</span> <span class="s1">'e9'</span><span class="p">,</span>
<span class="s1">'T'</span><span class="p">:</span> <span class="s1">'e12'</span>
<span class="p">}</span>
<span class="n">_exponent_lookup_scaled</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">'-36'</span><span class="p">:</span> <span class="s1">'p'</span><span class="p">,</span>
<span class="s1">'-33'</span><span class="p">:</span> <span class="s1">'n'</span><span class="p">,</span>
<span class="s1">'-30'</span><span class="p">:</span> <span class="s1">'u'</span><span class="p">,</span>
<span class="s1">'-27'</span><span class="p">:</span> <span class="s1">'m'</span><span class="p">,</span>
<span class="s1">'-24'</span><span class="p">:</span> <span class="s1">''</span><span class="p">,</span>
<span class="s1">'-21'</span><span class="p">:</span> <span class="s1">'k'</span><span class="p">,</span>
<span class="s1">'-18'</span><span class="p">:</span> <span class="s1">'M'</span><span class="p">,</span>
<span class="s1">'-15'</span><span class="p">:</span> <span class="s1">'G'</span><span class="p">,</span>
<span class="s1">'-12'</span><span class="p">:</span> <span class="s1">'T'</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">EngNumber</span><span class="p">:</span>
<span class="sd">"""</span>
<span class="sd"> Used for easy manipulation of numbers which use engineering notation</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">:</span> <span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">,</span> <span class="nb">float</span><span class="p">),</span> <span class="n">precision</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">2</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Initialize the class</span>
<span class="sd"> :param value: string, integer, or float representing the numeric value of the number</span>
<span class="sd"> :param precision: the precision past the decimal - default to 2</span>
<span class="sd"> """</span>
<span class="bp">self</span><span class="o">.</span><span class="n">precision</span> <span class="o">=</span> <span class="n">precision</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">suffix_keys</span> <span class="o">=</span> <span class="p">[</span><span class="n">key</span> <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">_suffix_lookup</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span> <span class="k">if</span> <span class="n">key</span> <span class="o">!=</span> <span class="s1">''</span><span class="p">]</span>
<span class="k">for</span> <span class="n">suffix</span> <span class="ow">in</span> <span class="n">suffix_keys</span><span class="p">:</span>
<span class="k">if</span> <span class="n">suffix</span> <span class="ow">in</span> <span class="n">value</span><span class="p">:</span>
<span class="n">value</span> <span class="o">=</span> <span class="n">value</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">_suffix_lookup</span><span class="p">[</span><span class="n">suffix</span><span class="p">]</span>
<span class="k">break</span>
<span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">int</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nb">float</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">=</span> <span class="n">Decimal</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Returns the string representation</span>
<span class="sd"> :return: a string representing the engineering number</span>
<span class="sd"> """</span>
<span class="c1"># since Decimal class only really converts number that are very small</span>
<span class="c1"># into engineering notation, then we will simply make all number a</span>
<span class="c1"># small number and take advantage of Decimal class</span>
<span class="n">num_str</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">*</span> <span class="n">Decimal</span><span class="p">(</span><span class="s1">'10e-25'</span><span class="p">)</span>
<span class="n">num_str</span> <span class="o">=</span> <span class="n">num_str</span><span class="o">.</span><span class="n">to_eng_string</span><span class="p">()</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
<span class="n">base</span><span class="p">,</span> <span class="n">exponent</span> <span class="o">=</span> <span class="n">num_str</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'e'</span><span class="p">)</span>
<span class="n">base</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="nb">round</span><span class="p">(</span><span class="n">Decimal</span><span class="p">(</span><span class="n">base</span><span class="p">),</span> <span class="bp">self</span><span class="o">.</span><span class="n">precision</span><span class="p">))</span>
<span class="k">if</span> <span class="s1">'.00'</span> <span class="ow">in</span> <span class="n">base</span><span class="p">:</span>
<span class="n">base</span> <span class="o">=</span> <span class="n">base</span><span class="p">[:</span><span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="k">return</span> <span class="n">base</span> <span class="o">+</span> <span class="n">_exponent_lookup_scaled</span><span class="p">[</span><span class="n">exponent</span><span class="p">]</span>
<span class="o">...</span>
<span class="o">...</span>
</pre></div>
<p>One trick that I used in the <code>__repr__</code> is to take advantage of the
<code>Decimal</code> class. Since it works well for very small numbers, then
simply multiplying the number by a very small number and looking up
the proper suffix based on the scaled exponent works quite well.</p>
<h1>Adding Units</h1>
<p>I also created an <code>EngUnit</code> class which builds on the <code>EngNumber</code> class.
It simply allows one to specify units that get parsed correctly. It also
does basic unit checking for addition, subtraction, and comparison:</p>
<div class="highlight"><pre><span></span>>>> EngUnit('10000ohm')
10kohm
>>> EngUnit('10ohm') + EngUnit(0.02)
AttributeError: units do not match
>>> EngUnit('10ohm') + EngUnit('20ohm')
30ohm
</pre></div>
<p>When a unit is not present, then <code>EngUnit</code> works basically like <code>EngNumber</code>
except the unit is treated as <code>None</code>.</p>
<p>Enjoy!</p>Review: Python Jumpstart by Building 10 Apps2016-08-11T23:30:00-04:002016-08-11T23:30:00-04:00Jason Jonestag:www.forembed.com,2016-08-11:/review-python-jumpstart-by-building-10-apps.html<h2>What is It</h2>
<p><a href="https://training.talkpython.fm/courses/details/python-language-jumpstart-building-10-apps">
<img class="img-responsive img-rounded" alt="Python Jumpstart Logo" src="http://www.forembed.com/images/2016/08/python-jumpstart.jpg" style="max-height:300px; float:right;">
</a></p>
<p><a href="https://training.talkpython.fm/courses/explore_python_jumpstart/python-language-jumpstart-building-10-apps">Python Jumpstart by Building 10 Apps</a>
is a course designed as an introduction to Python. This course is not your typical introduction found on <a href="https://www.coursera.org/">Coursera</a>
or <a href="https://www.edx.org/">EDX</a>, which are usually designed around some weeks-long introduction that get you to objects around week 8. This course -
which you …</p><h2>What is It</h2>
<p><a href="https://training.talkpython.fm/courses/details/python-language-jumpstart-building-10-apps">
<img class="img-responsive img-rounded" alt="Python Jumpstart Logo" src="http://www.forembed.com/images/2016/08/python-jumpstart.jpg" style="max-height:300px; float:right;">
</a></p>
<p><a href="https://training.talkpython.fm/courses/explore_python_jumpstart/python-language-jumpstart-building-10-apps">Python Jumpstart by Building 10 Apps</a>
is a course designed as an introduction to Python. This course is not your typical introduction found on <a href="https://www.coursera.org/">Coursera</a>
or <a href="https://www.edx.org/">EDX</a>, which are usually designed around some weeks-long introduction that get you to objects around week 8. This course -
which you can reasonably complete in a week if you are dedicated - will get you into the more advanced topics within <em>hours</em>, not weeks.</p>
<p>This is a 'learn by watching me do' course in which the author develops each application in front of your eyes. If you do the projects side-by-side,
you will learn a lot. You won't learn everything that he knows, especially as it pertains to specific library knowledge, but you will get the flavor
of the library and should have the proper vocabulary to explore <a href="http://stackoverflow.com/">StackOverflow</a> and the library documentation to figure
out the specifics.</p>
<p>There is a total of 7.2 hours of instruction for $69 with lifetime access. This doesn't sound like much course time, but it is quite information dense.</p>
<p><a href="https://talkpython.fm/">
<img class="img-responsive img-rounded" alt="Talk Python Podcast Logo" style="max-height:150px; float:left;" src="http://www.forembed.com/images/logos/talk_python_logo_mic.png">
</a></p>
<p>The author, Michael Kennedy, is an experienced Python developer who runs a weekly podcast <a href="https://talkpython.fm/">Talk Python to Me</a> in which
he speaks with contributing members of the Python community. I highly recommend giving this a listen.</p>
<h2>Who is it For?</h2>
<p>If you are new to Python or just haven't practiced for a while, this course is for you. Additionally, if you just missed some things along the way -
'lambdas' anyone? - you will find that the course material is organized in such a way that you can easily focus in on the particular topic that
you missed. I'm an embedded C guy with very hack-ish knowledge of Python, so this course has been very helpful to me.</p>
<h2>The Course</h2>
<h3>Write Each Program</h3>
<p>As an electronics guy, I didn't actually take any courses in programming beyond assembly. As a result, I was never exposed to actually building a
program as a professional might. Kennedy takes you through each application, first sketching out the rough program or file 'flow' and then adding
detail. By the third application, the viewer can predict the sequence of events that will start the course. The familiarity of format between
the modules gives the user a familiar hand-hold as they move on to each new application.</p>
<p>Kennedy supplies his code in the form of a <a href="https://github.com/mikeckennedy/python-jumpstart-course-demos">git repository</a>. This is a great
thing to reference from time-to-time, but <em>do not copy/paste</em> from his code. You will miss something. For instance, named tuples format:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">collections</span>
<span class="c1"># this one is wrong!</span>
<span class="n">MovieResult</span> <span class="o">=</span> <span class="n">collections</span><span class="o">.</span><span class="n">namedtuple</span><span class="p">(</span>
<span class="s1">'MovieResult'</span><span class="p">,</span>
<span class="s1">'Title'</span><span class="p">,</span> <span class="s1">'Poster'</span><span class="p">,</span> <span class="s1">'Type'</span><span class="p">,</span> <span class="s1">'imdbID'</span><span class="p">,</span> <span class="s1">'Year'</span>
<span class="p">)</span>
<span class="c1"># this is correct</span>
<span class="n">MovieResult</span> <span class="o">=</span> <span class="n">collections</span><span class="o">.</span><span class="n">namedtuple</span><span class="p">(</span>
<span class="s1">'MovieResult'</span><span class="p">,</span>
<span class="s1">'Title, Poster, Type, imdbID, Year'</span>
<span class="p">)</span>
<span class="c1"># see the difference?</span>
</pre></div>
<p>Because of a basic misunderstanding in format, I simply missed the nuance of the format of named tuples and - without typing it myself - I would
have had to find the issue on my own at some later time.</p>
<h3>Development/Learning Environment</h3>
<p><img class="img-responsive img-rounded" alt="Pycharm screenshot" src="http://www.forembed.com/images/2016/08/pycharm-screenshot.png" style="max-height:300px; float:right;"></p>
<p>Kennedy performs his development in <a href="https://www.jetbrains.com/pycharm/">PyCharm</a> - a Python IDE - and a significant amount of screen time is
dedicated to showing you how to get the most out of PyCharm. This was a bonus for me since I use PyCharm, but may be a drawback for you.<br>
If you are on the edge, the ease with which Kennedy performs many basic and more advanced tasks with the keyboard-only may convince you that
PyCharm is <em>The Way</em>. You should note that there is a 'Community Edition' of PyCharm that is free and is quite capable. For this series of
videos, you likely won't notice the difference between the 'Pro' and 'Community' editions. You would if you were developing for web applications
or other more advanced applications than just Python.</p>
<p>The lessons are still perfectly applicable to those who would prefer VIM or EMACS, but you might be annoyed by the continual references to how
PyCharm will help you out. Overall, the choice to use PyCharm should enable new users to move forward without too many issues, seeing results
quickly.</p>
<h3>Other Notes</h3>
<p>Kennedy has 7.2 hours in which to show you Python and he really pulls it off. This course covers what many 2-semester Python courses would cover.
Just <a href="https://training.talkpython.fm/courses/details/python-language-jumpstart-building-10-apps">look at the curriculum</a> to quell your doubts.
If you are an in-betweener - like me - who doesn't want to tackle beginner-style 'for' loops but does want to delve deeper into Python than you
have in the past, this is your guy.</p>
<p><img class="img img-responsive" alt="File Searcher App Video List" src="http://www.forembed.com/images/2016/08/file-searcher-app-video-list.png" style="float:right;"></p>
<p>To the right is one of the application outlines. The layout is very typical of each of the courses. The application is always introduced first.
It is then sketched out and a bit of code is written. Through the coding process, Kennedy moves through with a fluid train of thought, giving the
most obvious C-like solutions first, then refining to more "Pythonic" solutions. Somewhere just after introducing a new Python feature, Kennedy will
have a specific video just to elaborate on a 'Core Concept'. After the core concept, application development continues as if it were never interrupted.
I find this to be an effective teaching/learning format.</p>
<p>The course does start using file I/O in the last few applications and these files are not provided! One of them is a 2.5GB text file, so you
probably don't want to download it to test your program. You do want something, however, so I
<a href="https://github.com/slightlynybbled/python-jumpstart-course-demos">forked the repository</a> and added a couple of small files so that the bare bones
of the required files were present for a couple of applications. Look in /apps/<app_name>/you_try/ for the appropriate files for the <a href="https://github.com/slightlynybbled/python-jumpstart-course-demos/tree/master/apps/08_file_searcher/you_try">file
searcher</a> and
<a href="https://github.com/slightlynybbled/python-jumpstart-course-demos/tree/master/apps/09_real_estate_analyzer/you_try">real estate</a> applications.</p>
<p>Kennedy does touch on application structure, but doesn't go quite far enough. When looking around on github, I often see applications packaged
into a directory, then another application in another directory, with imports going from one to another in a fashion that I don't completely
understand. I feel like 10 minutes dedicated to this topic would really help the new and semi-seasoned Python developer in his/her jumpstart.</p>
<p>There are two topics that I would like to have seen covered that didn't receive a mention: testing and profiling. Testing
and profiling are an integral part of modern application development and merit a significant percentage of the
<a href="https://talkpython.fm/episodes/all">Talk Python to Me</a> air time. Since <a href="https://snap-ci.com/">snap ci</a> and other continuous integration services
are frequently sponsoring episodes of the podcast, I expect that a few videos focused on testing would give the author an opportunity for
sponsorship. I realize that testing in particular could easily be its own course, so I look forward to seeing this one on the
<a href="https://training.talkpython.fm/courses/all">course list</a> soon.</p>
<p><img class="img img-responsive" alt="PyCharm Profiler Screen Shot" src="http://www.forembed.com/images/2016/08/pycharm-profiling.png"></p>
<p>A few videos on string manipulation and searching - such as with regular expressions - would have been appropriate, but it doesn't feel like it
is <em>missing</em>. The course does cover using the CSV module, which accounts for half of the string search/manipulation that I perform. If you think
that you know the CSV module, you should watch anyway. There is a cool little trick that makes unpacking CSV data easy and robust.</p>
<p>I'm not advanced enough to know if application deployment should be a course of its own or get its own half-hour here. Kennedy does touch on
a method or two to get code to execute in Python2 and Python3 environments, but not the whole picture. A few minutes on application deployment
would be very helpful.</p>
<p>If you are looking for a modern GUI course, you might want to pass on this one. All user interaction is completed in the command line, allowing the
viewer to learn Python. Some courses/languages - I'm looking at you, Java - try to introduce the paradigm of GUI development to beginners still
trying to learn the syntax and concepts of the language. Kennedy's simple user interaction allows the user to focus on Python manipulations, not
frames and panes.</p>
<h3>Time Commitment</h3>
<p>Most videos are in the 3-6min range, with the longest video at 13:21. These are pretty bite-sized and you could easily spread the information-dense
7.2-hour course out over a more lengthy amount of time. If you watch these straight-through, you will miss out on half of the value of the course. Take
your time, pause the video, re-watch sections that you missed, and type out the examples yourself. Experienced programmers should expect to dedicated
12-ish hours to the task. Beginners should checkout some <a href="https://wiki.python.org/moin/PythonBooks">books</a> or
<a href="https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=python%20tutorial">tutorials</a> for when they don't fully understand an
example. I would expect new programmers to easily spend 30 hours or more learning the terminology and techniques.</p>
<h3>Summary Pros</h3>
<ul>
<li>Each application 'grows' with each video, much like real-world development</li>
<li>$69 gets you lifetime access</li>
<li>PyCharm lessons</li>
<li>Gets you started writing code <em>fast</em></li>
<li>Shows you most of the 'gotchas' that you will encounter when getting started</li>
<li>The applications - in most cases - resemble real-world applications and don't have that made-up and useless feeling that most
classroom applications have</li>
</ul>
<h3>Summary Cons</h3>
<ul>
<li>It costs money (you should get over this, Michael is doing good things for the world!)</li>
<li>There isn't a lot of time spent on each small topic, so if you want the in-depth on each library, read the docs</li>
<li>PyCharm lessons</li>
<li>Can't watch the videos at a faster rate</li>
<li>No testing/profiling/deployment</li>
</ul>
<h2>Recommendation?</h2>
<p>I came at this course having developed a few small applications for my own purposes. I have exposure to most of the material already, so I was able to do
about 3 applications/day without much of a problem.</p>
<p>There isn't anything in this course that isn't covered elsewhere for free. That being said, I have spent many hours learning the 'getting started gotchas'
one hard lesson at a time. Had I gone through this a few years back, I suspect that I wouldn't have had some of the headaches that I suffered through.</p>
<p>For a beginner, I would recommend trying to develop one app/day. That may be ambitious, particularly on the last few applications. Try not to move forward
without actually understanding what is being shown. The topics highlighted by the author as a 'Core Concept' should be understood thoroughly before advancing.</p>
<p>For the in-betweeners, I would still recommend watching this course through in its entirety and manually typing out the applications that have features that
you aren't familiar with. I feel like I could run through some portion of these videos every few months and learn a bit more every time.</p>
<p><img class="img-responsive" alt="thumbs up" style="float:right;" src="http://www.forembed.com/images/logos/thumbs-up.png"></p>
<p>For the seasoned Python developer, this is likely a pass. Having said that, I would be <em>very</em> surprised if anyone - including Michael! - wasn't able to pick
up a new trick or remember an old trick long-forgotten.</p>
<p>I highly recommend this course to developers of all vintages, but I suspect that it hits its sweetest spot with coders already familiar with at least one other
object-oriented language.</p>
<h2>Final Word</h2>
<p>My thanks to Michael Kennedy for publishing his video series and for permission to utilize his images and artwork. Keep up the great work!</p>Load Cell Sensor Introduction2016-08-02T07:35:00-04:002016-08-02T07:35:00-04:00Jason Jonestag:www.forembed.com,2016-08-02:/load-cell-introduction.html<p><img class="img-responsive" alt="Load Cells and Electronics" src="http://www.forembed.com/images/2016/08/load-cell-header.png"></p>
<h2>Load Cell Amplifier</h2>
<p>I was browsing <a href="https://www.sparkfun.com/">Sparkfun</a> a few days ago and came across their
<a href="https://www.sparkfun.com/products/13261">OpenScale</a> board. The board is basically a load-cell
amplifier with a microcontroller interface which will output data - via an FTDI chip - to the
PC or other UART at a fixed interval. The microcontroller also has …</p><p><img class="img-responsive" alt="Load Cells and Electronics" src="http://www.forembed.com/images/2016/08/load-cell-header.png"></p>
<h2>Load Cell Amplifier</h2>
<p>I was browsing <a href="https://www.sparkfun.com/">Sparkfun</a> a few days ago and came across their
<a href="https://www.sparkfun.com/products/13261">OpenScale</a> board. The board is basically a load-cell
amplifier with a microcontroller interface which will output data - via an FTDI chip - to the
PC or other UART at a fixed interval. The microcontroller also has the ability to read local and
- if present - remote temperature sensors. I was about to purchase the item when I realized that it
was $29.95... what!!!</p>
<p>That is when I remembered coming across a TI microcontroller, the
<a href="http://www.ti.com/product/MSP430AFE251/description">MSP430AFE251</a> which has an integrated 24-bit
sigma-delta ADC with a programmable gain amplifier. In addition, it has an on-board temperature
sensor. Basically, it has everything that the Sparkfun board has EXCEPT for a USB interface. It
looks like a new project time! It looks like I could make one of these for ~$10 in single-piece
quantities and quite a bit less for higher quantities.</p>
<h2>Load Cell Tutorial</h2>
<p>In electronics-speak, you want to have an amplifier that can read the voltage difference from a
<a href="https://en.wikipedia.org/wiki/Wheatstone_bridge">Wheatstone bridge</a> with enough resolutions to obtain
meaningful data.</p>
<p>Sparkfun's <a href="https://learn.sparkfun.com/tutorials/getting-started-with-load-cells">Getting Started with Load Cells</a>
tutorial does a pretty good job of going through the basics of load cells and how to use them, so - if you
have questions - go there. We are going to focus on the electronics required to get the job done.</p>
<h2>Electronics</h2>
<h3>Basic Requirements</h3>
<h4>Sigma-Delta Converter</h4>
<p>The most basic building block is the analog-to-digital converter (ADC). The sigma-delta ADC is the standard
in high-precision converters because you can get so many bits of resolution from them. If you don't need the
resolution, you can use the faster and more standard successive approximation ADCs which are standard on most
microcontrollers.</p>
<h4>Programmable Gain Amplifier (PGA)</h4>
<p>If you have a 64-bit converter, then you probably don't need the PGA. If you don't, then you will likely need
some analog interface which will allow you to increase the gain of the differential input before the ADC. The
amplifier can take the form of a fixed amplifier, but - with the microcontroller interface - it would be nice
to have the amplifier be programmable so that we can reduce the gain under saturation conditions and increase
the gain when the signal is too small.</p>
<h4>Microcontroller</h4>
<p>Of course, we are converting analog data to a digital format, so we need to have some method of translating. The
microcontroller - being programmable - will provide that interface. It is desireable - but not required - that the
microcontroller have an on-board UART to make the translation easier and more hardware-driven.</p>
<p><img class="img-responsive" alt="Requirement block diagram" src="http://www.forembed.com/images/2016/08/load-cell-req-diagram.png"></p>
<h3>Fulfilling the Requirements - Schematic</h3>
<p>Please refer to the <a href="http://www.forembed.com/docs/2016/08/load-cell-sensor-0v1.pdf">v0.1 schematic</a> for the remainder of this post.</p>
<p>We have chosen the MSP430AFE251 as our primary analog and digital interface. It has a 24-bit sigma-delta ADC
with a built-in PGA along with UART and SPI interfaces and a few timers that can be used as timers, PWM generators,
or input capture pins.</p>
<p>In addition to the requirements, we want a few other nice features:</p>
<ul>
<li>the ability to turn off the strain gauge network in order to conserve power</li>
<li>SPI output option (slave mode)</li>
<li>analog output option (PWM to analog)</li>
</ul>
<p>With these features, it should be easy to interface to most other system types.</p>
<p><img class="img-responsive" alt="Block diagram" src="http://www.forembed.com/images/2016/08/load-cell-diagram.png"></p>
<h4>Low-Power</h4>
<p>The ability to turn off the strain gauge network was implemented using an N-channel MOSFET on the low-side
which is driven directly by a microcontroller pin. The microcontroller pin also has an RC circuit directly
on its output in order to ensure that the analog side of the board is not 'contaminated' with conducted switching
from the digital side. This pin is not intended to be switched at high frequency, but I thought it best to
be cautious when dealing with 24 bits of resolutions since I don't have any experience beyond 12 bits.
<<img class="img-responsive" alt="MOSFET, Wheatstone bridge on/off" src="http://www.forembed.com/images/2016/08/nmos-on-off.png"></p>
<h4>SPI</h4>
<p>I thought that it would be nice if the interface supported SPI as well as the UART. The UART is great for general
purpose connections, but can be problematic if there are multiple sensors involved, which SPI can solve quite nicely.
In addition, the SPI interface can support much faster transfer rates, if required.</p>
<p>There may be a need to 'slow down' the SPI interface using a 'data ready' output or similar. The analog <code>SIGN</code> output
may be multiplexed for this purpose, if necessary, to give sufficient time for the SPI request to be received, processed,
and the appropriate data placed on the SPI bus for consumption.</p>
<h4>Analog</h4>
<p>The simplest options is - of course - a direct analog output. In truth, this wasn't a design goal at the outset,
but there were a couple of pins left, one of which could output PWM signals. I decided that a couple of RC pins would
be easy to put on the board to give an analog output option. Of course, this would be the lowest resolution output,
but might be pretty nice when viewing data using an oscilloscope.</p>
<p>The analog output is 'zero-referenced', meaning that any deviation from zero will be output as a positive number. I have
done this because I hate dealing with analog outputs and the errors caused by reading the A/D with 1.65V corresponding to
zero. To compensate, I have dedicated a pin to the analog 'sign' as well. This pin will be '0' when the analog voltage
is negative and '1' when it is positive.</p>
<h4>Low Noise</h4>
<p>In order to reduce the noise of the input, we have separated out the analog and digital supplies using an RC filter
on the power supply input leads:</p>
<p><img class="img-responsive" alt="Analog/Digital separation" src="http://www.forembed.com/images/2016/08/analog-digital-psu-separation.png"></p>
<p>This simple circuit has a cutoff frequency of ~79Hz. This may end up being modified to something less than 50Hz/60Hz so typical
in many operating environments, but only testing can tell. The best-case scenario would probably be to add a couple of inductors
in place of the resistors, but to get significant performance from an inductor requires a much larger package than a resistor, so
I would like to give this a try as a 'first time out' approach. If it isn't enough, then I will modify the R value or the component.</p>
<p>The SparkFun board uses an <a href="https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/hx711_english.pdf">HX711</a> chip, which claims
at the header "Simultaneous 50 and 60Hz supply rejection". I'm sure that anyone reading this has seen the effects of 50Hz and 60Hz
environmental noise, but I wasn't quite sure how to do this without implementing two <a href="https://en.wikipedia.org/wiki/Band-stop_filter">notch filters</a>,
one at 50Hz and one at 60Hz, which could turn out to be physically large at those low frequencies. As I am trying to minimize
component count, I decided to see how it goes without the filters for the first go-around. If it doesn't work out, then I will
first try to do it in software. If that doesn't work, then I may try to get it done with a special-footprint PSU.</p>
<h4>Crystal</h4>
<p>This microcontroller can operate at 12MIPS, so I opted to go for a small 12MHz crystal. Most of this series can operate at 16MIPS,
so this was somewhat of a surprise. I wanted to get the maximum speed possible since I am going to be attempting to implement an
SPI slave, which requires speed.</p>
<h3>Layout</h3>
<p>I actually spent a lot of time on figuring out the form factor for the layout. My first layout separated out each interface on its
own header, which is clean, but isn't compatible with any sort of standard. The layout was also quite small and breadboard-compatible,
but didn't have some other features that I liked.</p>
<p>I settled on the form factor used by the <a href="http://lowpowerlab.com/moteino/#pinout">Moteino</a>. There are a couple of reasons that I did
this:</p>
<ul>
<li>breadboard compatible</li>
<li>small form factor</li>
<li>anticipate a wireless application in the future</li>
</ul>
<p><img class="img-responsive" alt="Load cell sensor v0.1 layout - top" src="http://www.forembed.com/images/2016/08/load-cell-0v1-top.png"></p>
<p><img class="img-responsive" alt="Load cell sensor v0.1 layout - top" src="http://www.forembed.com/images/2016/08/load-cell-0v1-bot.png"></p>
<p>You can see that I added screw terminals to the left side (bottom pic) of the board for attachment. All SMT components are on
the 'top' while through-hole components are on the 'bottom'.</p>
<p>In truth, laying out the board in this fashion was actually quite challenging. I had some trouble keeping the analog and digital
domains far from one another and I am hoping that I didn't hobble the performance of the ADC. You can see - for instance - that
the SPI outputs are pretty close to the analog side. If I see that there is noise on the ADC for this reason, then I will probably
re-layout with separate interfaces and a much cleaner (electrically) layout.</p>
<p>For the moment, I haven't implemented any type of mounting features, but that may change in the future as well.</p>
<h3>Software</h3>
<p>There is currently no software written for the platform, but I am foreseeing something like a standard serial interface for configuration
and crude visualization, much like the <a href="https://www.sparkfun.com/products/13261">OpenScale</a> interface. I imagine that most
applications will use a low-speed UART to configure the board and use the SPI interface for high-speed access. I am considering making the
sign bit configurable as the sign bit for the analog output OR as a 'data ready' output for the SPI port. This seems like a good idea
since it can keep the host interrupt-driven.</p>
<p>Initially, I don't believe that I would like to implement an EEPROM-like ability to persist configurations through a reset, but that may
change. I anticipate that most applications involving this board - at least, anything I'm working with - will be software based and - thus -
easily configured on power-up. Of course, this could easily change.</p>
<p>I expect the software to be something like:</p>
<ol>
<li>initialize peripherals/hardware with defaults</li>
<li>wait for A/D time</li>
<li>turn on strain gauge, wait to stabilize, measure</li>
<li>turn off strain gauge</li>
<li>go back to '2'</li>
</ol>
<p>In parallel, the UART would be interrupt driven, being used for configuration and/or low-speed data transmission.</p>
<h3>Summing Up</h3>
<p>This should be a fun project that will have a lot of applications to play with. If we can get the interface right, it should be
quite easily integrated into other projects that I have in mind.</p>
<p>As always, check out our <a href="https://github.com/slightlynybbled/load-cell-sensor">github</a> repository.</p>Fan Controller Progress Update - July2016-07-21T11:05:00-04:002016-07-21T11:05:00-04:00Jason Jonestag:www.forembed.com,2016-07-21:/fan-controller-progress-update-july.html<p><img class="img-responsive img-rounded" alt="Rework" src="http://www.forembed.com/images/2016/07/fan-controller-with-scope-cropped.jpg"></p>
<h2>Hardware</h2>
<p>Hardware was received and built up quickly in the past couple of days. In fact, all has progressed
more quickly that I initially imagined. Osh Park - those loveable guys - bumped my order up to the
'premium' panel, so I received the boards a week earlier than anticipated!</p>
<h3>Rework</h3>
<p>That …</p><p><img class="img-responsive img-rounded" alt="Rework" src="http://www.forembed.com/images/2016/07/fan-controller-with-scope-cropped.jpg"></p>
<h2>Hardware</h2>
<p>Hardware was received and built up quickly in the past couple of days. In fact, all has progressed
more quickly that I initially imagined. Osh Park - those loveable guys - bumped my order up to the
'premium' panel, so I received the boards a week earlier than anticipated!</p>
<h3>Rework</h3>
<p>That isn't to say that there haven't been a couple of bumps along the road. Once again, I
apparently missed the capacitor pin on the microcontroller and assigned it to a function. This
required two reworks: (1) the addition of a capacitor and (2) moving of a sense trace to a
different pin. Additionally, I misread pin 14 on the microcontroller and thought that it was one
of the PWM outputs that I could use. Unfortunately, that particular PWM module was already being
utilized, so I had to move the PWM function for fan 3 to pin 18.</p>
<p><img class="img-responsive img-rounded" alt="Rework" src="http://www.forembed.com/images/2016/07/fan-controller-rework.jpg"></p>
<p>You can see that I soldered a small capacitor to the back of U2 (the DPAK) and soldered a lead to
the V<sub>cap</sub> pin of the microcontroller. You can also see pin 18 being shunted to pin 14 and
replacement of one of the tachometer signal traces. This rework job was minor and took just a few
minutes to do, so I am happy with the results.</p>
<h3>Hardware Functionality</h3>
<p>As the firmware has progressed, the functionality of several of the hardware blocks has been tested as well.
We have verified the functionality of the following bits of hardware:</p>
<ul>
<li>PWM modules</li>
<li>PWM outputs</li>
<li>TACH inputs</li>
<li>PWM input</li>
<li>TACH output</li>
<li>Rotary encoder/switch</li>
</ul>
<p>In addition, it was nice to hook up the programmer and have it simply work right off the bat. I
hadn't even cleaned flux off the board before I was flashing the microcontroller.</p>
<h3>PWM Input</h3>
<p>The only real issue that I had as I went through the firmware buildup involved measuring the input
pulses of PWM. The microcontroller - a <a href="http://www.microchip.com/wwwproducts/en/PIC24F16KM202">PIC24FV16KM202</a> -
has 5 modules which can be configured as PWM, input capture, or general-purpose timer. I am already
using all 5 of these for PWM and timer, so I decided that it would actually be better to convert the input
PWM to an analog signal using an RC circuit. We will implement this on v1.1 of the hardware.</p>
<p><img class="img-responsive img-rounded" alt="PWM-to-analog circuit" src="http://www.forembed.com/images/2016/07/pwm-to-analog.png"></p>
<h3>Part Flexibility</h3>
<p>I also decided that it would be a good idea to make the schematic/layout compatible with the 3.3V part in
case I ran into part compatibility issues down the road. As a result, several I/O were moved around a bit
in order to accomplish this goal.</p>
<h2>Firmware</h2>
<p>The firmware has been a bit more complex than I had initially thought it would be. Isn't that
always the case, though? The idea is to easily maintain control of the fans but to also have
the fans pulling minimal power at all times.</p>
<p>I'm still trying to decide if I should implement some sort of protection feature into the board. Fans are
supposed to have integrated protections against locked rotor and other similar types of failures, so I don't
know how much effort I should put into protection. Hit me in the comments if you have an opinion.</p>
<p>Again, a reminder of the state machine from the <a href="http://www.forembed.com/fan-controller-introduction.html">last article</a>:</p>
<p><img src="/images/2016/07/fan-controller-state-diagram.png" alt="Fan Controller State Diagram" class="img-responsive img-rounded"></p>
<p>We will bounce through some of the functionality of the firmware for the user, but the complete source is on
<a href="https://github.com/slightlynybbled/fan_controller">github</a>.</p>
<h3>Encoder</h3>
<p>The code for the encoder was a bit more difficult that I had anticipated. My hacked-together code didn't really
work very well and I did a quick search and found
<a href="https://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino">a really good article</a> - with sample code -
that worked quite well after a bit of adaptation.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">serviceEncoder</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="kt">int8_t</span> <span class="n">enc_states</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">};</span>
<span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">old_AB</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">old_AB</span> <span class="o"><<=</span> <span class="mi">2</span><span class="p">;</span>
<span class="n">old_AB</span> <span class="o">|=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)(</span><span class="n">PORTA</span> <span class="o">&</span> <span class="mh">0x0003</span><span class="p">);</span>
<span class="kt">int8_t</span> <span class="n">state</span> <span class="o">=</span> <span class="n">enc_states</span><span class="p">[(</span><span class="n">old_AB</span> <span class="o">&</span> <span class="mh">0x0f</span><span class="p">)];</span>
<span class="k">if</span><span class="p">(</span><span class="n">state</span> <span class="o">==</span> <span class="mi">1</span><span class="p">){</span>
<span class="cm">/* counter-clockwise */</span>
<span class="n">lastEncoderTime</span> <span class="o">=</span> <span class="n">TASK_getTime</span><span class="p">();</span>
<span class="n">encoderTurned</span><span class="o">--</span><span class="p">;</span> <span class="c1">// reverse the direction of CW and CCW</span>
<span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">state</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">){</span>
<span class="cm">/* clockwise */</span>
<span class="n">lastEncoderTime</span> <span class="o">=</span> <span class="n">TASK_getTime</span><span class="p">();</span>
<span class="n">encoderTurned</span><span class="o">++</span><span class="p">;</span> <span class="c1">// reverse the direction of CW and CCW</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>I decided to poll this function as a task every 1ms rather than use interrupts. This is mainly because the encoder
is relatively low priority and I don't want to potentially delay a different interrupt that may be more time-critical.</p>
<p>After user adjustments are made to the fan speed, the fan will wait 5s from the <code>lastEncoderTime</code> before saving the
values and returning to the normal state.</p>
<p>Periodically, <code>encoderTurned</code> is polled by a different function which determines how far the encoder has been turned
since the last read. This allows the higher level of software to have a crude sense of the speed of rotation of the
encoder, which turns out to be quite useful. We are using this knowledge to implement two modes of PWM adjustment, fine
and course adjustment. If <code>|encoderTurned|</code> is greater than some value, then we will add more adjustment. The user
will likely not even know the two modes since it is very natural to turn the knob quickly for large adjustments and
slowly for fine adjustments.</p>
<h3>EEPROM</h3>
<p>Working with EEPROM within the Microchip products is simple, but always requires a bit of tinkering on my part to get
it into an easily workable solution.</p>
<p>I implemented three functions to assist me in writing to any address within the EEPROM:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">EEPROM_erase</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">EEPROM_write</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">value</span><span class="p">);</span>
<span class="kt">uint16_t</span> <span class="nf">EEPROM_read</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">);</span>
</pre></div>
<p>The 'gotchas':</p>
<ul>
<li>Microchip on-chip EEPROMs are 16-bits wide and the address <em>must</em> be even so that you are addressing the lower byte.
Even addresses will simply not write.</li>
<li>Memory must be erased before it can be written - erase first!</li>
<li>Wait for <code>NVMCONbits.WR</code> to be cleared before continuing. If you don't, and you try another erase or write before it
has cleared, you will be sorry. Don't be sorry. Just plan to wait the proper time.</li>
</ul>
<p>My code is full of Microchip special functions that come with the XC16 environment. I wrote the core part of these functions
using Microchip example code with only slight modification to suit my purposes. An example using the <code>EEPROM_write</code>
function:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">uint16_t</span> <span class="nf">__attribute__</span><span class="p">((</span><span class="n">space</span><span class="p">(</span><span class="n">eedata</span><span class="p">)))</span> <span class="n">eedata</span><span class="p">[</span><span class="n">__EEDATA_LENGTH</span> <span class="o">>></span> <span class="mi">1</span><span class="p">];</span>
<span class="kt">void</span> <span class="nf">EEPROM_write</span><span class="p">(</span><span class="kt">uint16_t</span> <span class="n">address</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">value</span><span class="p">){</span>
<span class="n">EEPROM_erase</span><span class="p">(</span><span class="n">address</span><span class="p">);</span>
<span class="n">NVMCON</span> <span class="o">=</span> <span class="mh">0x4004</span><span class="p">;</span>
<span class="cm">/* Set up a pointer to the EEPROM location to be written */</span>
<span class="n">TBLPAG</span> <span class="o">=</span> <span class="n">__builtin_tblpage</span><span class="p">(</span><span class="n">eedata</span><span class="p">);</span>
<span class="kt">uint16_t</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">__builtin_tbloffset</span><span class="p">(</span><span class="n">eedata</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="n">address</span> <span class="o"><<</span> <span class="mi">1</span><span class="p">);</span>
<span class="n">__builtin_tblwtl</span><span class="p">(</span><span class="n">offset</span><span class="p">,</span> <span class="n">value</span><span class="p">);</span>
<span class="k">asm</span> <span class="k">volatile</span> <span class="p">(</span><span class="s">"disi #5"</span><span class="p">);</span>
<span class="n">__builtin_write_NVM</span><span class="p">();</span>
<span class="k">while</span><span class="p">(</span><span class="n">NVMCONbits</span><span class="p">.</span><span class="n">WR</span> <span class="o">==</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// wait for write sequence to complete</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Before writing any functions, we declare an array <code>eedata</code> which represents all of the EEPROM memory as an array. The
<code>__EEDATA_LENGTH</code> is a device header file define and is in bytes, so we have translated that into 16-bit values with
the declaration. As a result, we need to halve the <code>__EEDATA_LENGTH</code> or we will have a compiler memory allocation error.</p>
<p>Within the function, the first thing we do is erase the address before trying to write to it. Erasing sets all of the bits
of an address to <code>1</code>, since we can only actually write <code>0</code> values.</p>
<p>Next, we setup NVMCON appropriately to perform a write sequence. Consult the datasheet if you want more information. There
are different values required for read, erase, and write operations.</p>
<p>We set the 'page' and 'offset', which are really just a memory location. Microchip using paging - which can be very confusing -
but they do provide example code. Note that this is also where we add our address offset. If we write to location 0, then these
functions will write to the first location available in EEPROM memory. If we write to location 1, then the write will take place
in the second location, and so on...</p>
<p>There is an assembly unlock sequence that must occur in order to write to the EEPROM. This unlock sequence is contained within
<code>__builtin_write_NVM();</code>, but the sequence itself <em>must not</em> be interrupted, so we execute a <code>disi #5</code> assembly instruction, or
'disable instructions for 5 cycles'.</p>
<p>Once the write is initiated, we simply wait for the write bit to clear before proceeding. In some applications, the 4ms of write
time might be intolerable (think motor control), but our application has plenty of time to allow for a 4ms delay, so we have
no special considerations.</p>
<h3>Measuring Input PWM</h3>
<p>Measuring the input PWM command is a priority. Initially, I setup a couple of change notification interrupts, measured the input
pulse duration and period, and calculated a duty cycles. This worked fine for the middling values of PWM, but doesn't work at all
for 0% or 100% duty cycle since the change notice - by definition - requires a change in the pin state. After fiddling with the
logic for a bit, I decided that measuring the pulse width is <em>not</em> the best way to solve this problem. Converting the PWM to an
analog value is trivial in hardware and performs the logic required <em>perfectly</em>.</p>
<ul>
<li>When at 0% PWM, then the analog signal is 0V.</li>
<li>When at 100% PWM, then the analog signal is 5V.</li>
<li>When the input is disconnected, the analog signal is 5V.</li>
</ul>
<p>What more could you ask for?</p>
<h3>Tach Output</h3>
<p>Ultimately, one has to decide how to route the tach for four fans into one output. I decided that I would go for the simplest
option and simply route the tach input for Fan 0 to the tach output to the motherboard. To do this, I set up a change notification
interrupt that will read the tach input pin and reflect that on the output. It works perfectly.</p>
<h2>Next Version</h2>
<p>The version tested was v1.0 and the next version will be v1.1. I made a couple of decisions:</p>
<ul>
<li>It wouldn't be difficult to make the schematic compatible with the 3.3V drop-in replacement for this device, so v1.1 will be
able to use the 5V chip <em>or</em> the 3.3V chip with the same code. This requires moving a few pin functions around, but not much.</li>
<li>Change the form factor to something more conventional - I went with the 60mm x 37mm
<a href="http://dangerousprototypes.com/docs/Sick_of_Beige_standard_PCB_sizes_v1.0">Sick of Beige</a> form... because I like it.</li>
<li>Use the RC circuit to change PWM to an analog signal.</li>
</ul>
<p>The layout turned out well, but used a bit more board space than the last layout:</p>
<p><img class="img-responsive" alt="Rework" src="http://www.forembed.com/images/2016/07/fan-controller-layout-1v1-top.png"></p>
<p><img class="img-responsive" alt="Rework" src="http://www.forembed.com/images/2016/07/fan-controller-layout-1v1-bot.png"></p>
<p>I placed all SMT components on one side of the board and all through-hole components on the other side of the board. I also
took a bit of time to label each FAN and the input along with all of the input net names on the SMT side of the board. This
was mainly in case I found myself soldering leads to the board, but also serves as a nice visual aid.</p>
<p>With this nice form factor, I should be able to easily place an acrylic case around my electronics, making it a bit safer
to place within a PC.</p>
<h2>Future Applications</h2>
<p>With this hardware/firmware, it is possible to control four fans with or without a motherboard present. Simply set the
maximum speed and go.</p>
<p>I saw <a href="https://www.tindie.com/products/mux/open-source-programmable-fan-controller/?pt=full_prod_search">one application on Tindie</a>
that allows one to scale the output tach in order to fool the motherboard when installing more quiet fans. There is no
reason that this same thing couldn't be done with this hardware.</p>
<p>One might also use re-program this as a fail-safe. If one fan stops working, then the next fan is started and the new tach
signal is routed appropriately.</p>
<p>These are just a couple of ideas. If you get a couple of ideas, then hit me up in the comments.</p>Project Curve Tracer: XY Outputs2016-07-14T08:51:00-04:002016-07-14T08:51:00-04:00Jason Jonestag:www.forembed.com,2016-07-14:/project-curve-tracer-xy-outputs.html<p><img alt="curve trace, collage" class="img-responsive" src="http://www.forembed.com/images/2016/07/collage.png"></p>
<h2>Oscilloscope XY Outputs are Working!</h2>
<p>Using two PWM outputs and using a couple of simple RC circuits on the board, we have
managed to get the XY outputs working well enough to display on any oscilloscope! I
now feel that the hardware is feature-complete!</p>
<h2>Status</h2>
<p>The hardware in <a href="https://github.com/slightlynybbled/curve_tracer_hardware">github</a> is …</p><p><img alt="curve trace, collage" class="img-responsive" src="http://www.forembed.com/images/2016/07/collage.png"></p>
<h2>Oscilloscope XY Outputs are Working!</h2>
<p>Using two PWM outputs and using a couple of simple RC circuits on the board, we have
managed to get the XY outputs working well enough to display on any oscilloscope! I
now feel that the hardware is feature-complete!</p>
<h2>Status</h2>
<p>The hardware in <a href="https://github.com/slightlynybbled/curve_tracer_hardware">github</a> is up
to version 1.2, which is electrically identical to v1.0 and v1.1. The only changes have
been to the interfaces in order to make it easier for you to connect your devices to the
curve tracer.</p>
<h2>Setting Up</h2>
<p>You will be able to see the settings in our screenshots below, but it is always a good thing
to state some of them explicitly.</p>
<p>The vertical offset for each trace is 2.5V. You may recall from the <a href="http://www.forembed.com/docs/2016/07/curve-tracer-1v2-schematic.pdf">schematic</a>
that the voltage applied is 5V, so it stands to reason that the offset applied to each trace is half of that.</p>
<p>The X trace is set to 1V/division and the Y trace is set to 0.5V/division. This is somewhat
arbitrary, but not as critical as the vertical offset. I have found myself with a few different
settings for these values that work just fine.</p>
<p>Finally, the horizontal scale was adjusted so that at least one complete waveform is shown on
the screen, which corresponds to 2.0ms/division.</p>
<h2>Couple of Pics</h2>
<p>I have a Rigol DS1054, which is a great oscilloscope for the price. I would like to see
the XY on an old CRT screen for comparison. Perhaps I shouldn't have given my old scope away...</p>
<p>I inverted the display in the print settings, so it looks a bit odd. I may flip it back in
the future, I just wanted to try it out.</p>
<p>The display actually looks better than these screenshots. I suspect that inverting the shots
pushes the color data through some filter that causes visual artifacts.</p>
<h2>Dead Short</h2>
<p>A short-circuit condition is easily distinguished from all others.</p>
<p><img alt="curve trace, short" class="img-responsive" src="http://www.forembed.com/images/2016/07/short.png"></p>
<h3>Resistor, 100Ω</h3>
<p>As a resistor is defined by its voltage-vs-current slope, we expect a straight line here.</p>
<p><img alt="curve trace, 100ohm resistor" class="img-responsive" src="http://www.forembed.com/images/2016/07/100ohm.png"></p>
<h3>Resistor, 1kΩ</h3>
<p>Should be the same as the previous picture, but with a lower slope value.</p>
<p><img alt="curve trace, 1kohm resistor" class="img-responsive" src="http://www.forembed.com/images/2016/07/1kohm.png"></p>
<h3>Capacitor, Alum. Electrolytic, 4.7μF</h3>
<p>Reactance of a capacitor tends to create an ellipse.</p>
<p><img alt="curve trace, 4.7uF capacitor" class="img-responsive" src="http://www.forembed.com/images/2016/07/cap-4u7.png"></p>
<h3>Diode</h3>
<p>Standard <a href="http://www.digikey.com/product-detail/en/on-semiconductor/MBR1100RLG/MBR1100RLGOSTR-ND/822787">MBR1100</a>.
You can see that the component is an open when positively biased and an approximate short-circuit when the
component is negatively biased.</p>
<p><img alt="curve trace, diode" class="img-responsive" src="http://www.forembed.com/images/2016/07/diode.png"></p>
<h3>MOSFET, V<sub>g</sub> = 0.0V</h3>
<p>An <a href="http://www.digikey.com/product-detail/en/vishay-siliconix/IRF530PBF/IRF530PBF-ND/811781">IRF530</a>
MOSFET in a TO-220 package. The MOSFET with 0.0V<sub>g</sub> looks much like the diode.</p>
<p><img alt="curve trace, MOSFET, Vg=0.0V" class="img-responsive" src="http://www.forembed.com/images/2016/07/mosfet-0v0.png"></p>
<h3>MOSFET, V<sub>g</sub> = 4.0V</h3>
<p>As we increase V<sub>g</sub>, we can see that the MOSFET begins to conduct.</p>
<p><img alt="curve trace, MOSFET, Vg=4.0V" class="img-responsive" src="http://www.forembed.com/images/2016/07/mosfet-4v0.png"></p>
<h3>MOSFET, V<sub>g</sub> = 5.0V</h3>
<p>We can only go to 5.0V, but we can see that the component is approaching a short circuit as V<sub>g</sub> increases.</p>
<p><img alt="curve trace, MOSFET, Vg=5.0V" class="img-responsive" src="http://www.forembed.com/images/2016/07/mosfet-5v0.png"></p>Fan Controller Introduction2016-07-11T11:20:00-04:002016-07-11T11:20:00-04:00Jason Jonestag:www.forembed.com,2016-07-11:/fan-controller-introduction.html<p><img class="img-responsive" alt="PC Fan - One-to-Many" src="http://www.forembed.com/images/2016/07/fan-one-to-many.png"></p>
<p>Sorry that it has been so long since our last post. I have been out on vacation for a bit with some
family and am just getting back into the swing.</p>
<h2>PC Fan Controller</h2>
<p>I recently wished to add a fan to my PC and I realized that I only …</p><p><img class="img-responsive" alt="PC Fan - One-to-Many" src="http://www.forembed.com/images/2016/07/fan-one-to-many.png"></p>
<p>Sorry that it has been so long since our last post. I have been out on vacation for a bit with some
family and am just getting back into the swing.</p>
<h2>PC Fan Controller</h2>
<p>I recently wished to add a fan to my PC and I realized that I only had a couple of PC fan
case headers where I would have liked to have more. I ended up purchasing a set of fans
that can be daisy chained, but I don't actually like that solution very much.</p>
<p>I did a quick <a href="https://www.tindie.com/search/#q=fan">Tindie search</a> and I found that the
<a href="https://www.tindie.com/products/ICStation/fan-speed-controller-for-computer8062/?pt=full_prod_search">best candidate</a>
is a fan controller based on temperature and not on the motherboard command.</p>
<p>A github search shows several arduino fan controllers, but nothing that is a open-source <em>and</em> cost-effective.</p>
<p>After a bit of thinking about it, I sketched out some basic requirements for a simple fan controller.</p>
<h2>I Know This Has Been Done Before</h2>
<p>Before you go into the "hey, this has been done", I get it. It has. I want to do it and I think it is
still cool and fun. Get over it.</p>
<h2>Requirements</h2>
<p>The basic requirements for the PC fan controller are as follows:</p>
<ul>
<li>Must be easy for the user to adjust the speed of a particular fan</li>
<li>Must scale the input PWM value properly for each fan setting</li>
<li>Fan settings must be persistent through power cycling</li>
<li>Each fan will soft-start and in-sequence to reduce load on the motherboard</li>
<li>Must be directly pluggable</li>
</ul>
<p>I thought about adding serial ports and other features to the board, but decided against it in order
to reduce complication and - perhaps - speed up the design stage. The user interface
will be through a single knob, which is a quadrature encoder with a push function.</p>
<h2>State Diagram</h2>
<p>The <code>initialization</code> state will initialize all microcontroller values that need to be initialized.</p>
<p>The <code>start</code> state will go through a startup for each fan in a manner that reduces the overall current draw
of the fans.</p>
<p>The <code>run</code> state will read the motherboard command and scale the command to the other fans appropriately. For
instance, if the motherboard is commanding 50% duty cycle and the saved calibration value is 80% duty cycle
for FAN0, then the output value for FAN0 will be</p>
<div class="math">$$
(0.5)(0.8) = 0.4
$$</div>
<p>or 40% duty cycle.</p>
<p>In the absence of a command from the motherboard, the board will assume a command of 100% from the motherboard
and control the fans accordingly.</p>
<p>In order to reduce cost, it helps to only have one direct interface for the user which consists of a
rotary encoder and a switch. The switch is actually the same quadrature encoder, just with an integrated
switch that is actuated when the user pushes "down".</p>
<p>When the switch is pressed, the user will enter <code>calibration</code> mode. On each press of the switch, a different
fan is selected to calibrate and the others are all turned off. Once the user leaves the encoder and switch
alone for some number of seconds to be determined - or the timeout expires - then the values are saved and
the state moves back to <code>start</code>.</p>
<p><img src="/images/2016/07/fan-controller-state-diagram.png" alt="Fan Controller State Diagram" class="img-responsive img-rounded"></p>
<h2>Schematic and Layout</h2>
<p>Fortunately, PC fans are pretty simple devices, requiring open-collector outputs and pull-up inputs. Have a
look at the <a href="http://www.forembed.com/docs/2016/07/fan-controller-1v0-schematic.pdf">schematic</a>. Note that we have pull-up
resistors on all <code>sense</code> lines and open-collector outputs for all output PWMs. I know that the microcontroller
pins support open-collector outputs, but I wasn't sure that one might be able to use these open-collector outputs
in conjunction with the PWM module, so I made those outputs external. This also provides a level of isolation
to the microcontroller pins that gives me some comfort.</p>
<p>The microcontroller is the same as that used for the curve tracer, which is a 16MIPS, 16-bit microcontroller.
There are likely better choices for the microcontroller, but I have already used this one and I felt that I might
help myself by using the same microcontroller across projects.</p>
<p>LEDs were included for visual debugging, although they will likely not be populated. When used in PWM applications,
the brightness of the LED is an indication of the duty cycle.</p>
<p>The layout was similarly straightforward.</p>
<p><img class="img-responsive" alt="Fan Controller Layout - Top" src="http://www.forembed.com/images/2016/07/fan-controller-layout-top.png"></p>
<p>The primary fan connectors are all on the "top" side. This includes the <code>in</code>, which is where power and command
come from the motherboard and where the <code>sense</code> command from FAN0 is sent. Note that each fan connector is clearly
labeled and the small bus capacitor will be mounted to this side as well.</p>
<p>The schematic was created in a way that allows the encoder to be mounted on either side of the board.</p>
<p>A few test points - W1, W2, W3, and W4 - are visible from this side. These points are directly below the microcontroller and
are exclusively for debugging purposes.</p>
<p><img class="img-responsive" alt="Fan Controller Layout - Bottom" src="http://www.forembed.com/images/2016/07/fan-controller-layout-bottom.png"></p>
<p>All SMT components are on this side, including the microcontroller. It is possible to mount the encoder on this side of
the board. The majority of the schematic is located on this side.</p>
<p>As always, a <a href="https://github.com/slightlynybbled/fan_controller">github repository</a> has been created to manage the project. At this
time, I plan to keep the electronics and firmware in the same repository as the project is quite simple.</p>
<h2>Timeline</h2>
<p>I have placed the order from Digikey and Osh Park for the parts and boards. As this is a relatively simple application,
I don't expect it to take very long to reach full maturity.</p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>SolidWorks to Inkscape for Laser Cutting2016-06-23T11:15:00-04:002016-06-23T11:15:00-04:00Jason Jonestag:www.forembed.com,2016-06-23:/solidworks-to-inkscape-for-laser-cutting.html<p><img alt="SolidWorks/Inkscape/Ponoko" class="img-responsive" src="http://www.forembed.com/images/2016/06/solidworks-inkscape-ponoko.png"></p>
<h2>Intro</h2>
<p>In this article, I am going to guide you through an easy way to transfer your SolidWorks
design into Inkscape in preparation for laser cutting. There is already some information
out there, but the guides appear incomplete, so I'm tossing in my $0.02.</p>
<p>This will <em>not</em> be a …</p><p><img alt="SolidWorks/Inkscape/Ponoko" class="img-responsive" src="http://www.forembed.com/images/2016/06/solidworks-inkscape-ponoko.png"></p>
<h2>Intro</h2>
<p>In this article, I am going to guide you through an easy way to transfer your SolidWorks
design into Inkscape in preparation for laser cutting. There is already some information
out there, but the guides appear incomplete, so I'm tossing in my $0.02.</p>
<p>This will <em>not</em> be a SolidWorks or Inkscape tutorial.</p>
<p>We are taking advantage of the fact that SolidWorks will export a dimensionally correct
Adobe Illustrator file from a SolidWorks drawing and Inkscape will import this file
perfectly. There are other ways to do this! I spent several hours trying to do the same
sort of operation using DXF files, but I had to do a lot of cleanup and other manipulation
within Inkscape, which was frustrating.</p>
<h2>Laser Cutting Service</h2>
<p>We will be using <a href="http://www.ponoko.com/home">Ponoko</a> as our laser cutting service. They
are cheap, have several guides online, and - aside from generatign the inkscape files - there
is only a modest learning curve, mostly dealing with material selection.</p>
<p>One think that you should know is that, when you are using a service, you are purchasing an
entire sheet. Knowing the sheet dimensions is important and Ponoko supplies
<a href="http://www.ponoko.com/home/download-files">design templates</a> for Inkscape. We are going
to use the "P1" size, which is 180mm x 180mm. This is the smallest sheet, which is appropriate
for our initial prototype. Larger sheets will be cheaper per piece, but more expensive
up-front.</p>
<h2>Sheet Design in SolidWorks</h2>
<p>You may have experience with Inkscape, GIMP, or other graphics packages. Image manipulation
packages are designed to manipulate pixels. CAD packages are design to manipulate cohesive
models. This difference in design philosophy makes designing a sheet in Inkscape difficult
and error-prone. CAD packages will do the heavy lifting for you with very little effort, so
you will do nearly all of the design work within SolidWorks.</p>
<h3>Designing your Parts</h3>
<p>Design your parts normally in SolidWorks! Try to keep in mind that you will be laying the
parts out in 2D later, so shapes that naturally mate (such as chamfers) will be lower
cost than shapes that don't naturally mate so well.</p>
<p>Here, we have two designs that we would like to place on the same sheet. We will call these
the case 'top' and 'bottom':</p>
<p><img alt="case front" class="img-responsive" src="http://www.forembed.com/images/2016/06/case-chamfered-top.png"></p>
<p><img alt="case front" class="img-responsive" src="http://www.forembed.com/images/2016/06/case-chamfered-bottom.png"></p>
<p>I just grabbed a couple of screenshots. The case top and bottom are actually of the same dimension -
60mm x 37mm - but the pics don't show that clearly.</p>
<h3>Layout Out the Sheet</h3>
<p>First, take note of your sheet size! You must stay within that sheet size or your design will not
be manufacturable!</p>
<p>In SolidWorks, 'File -> Make Assembly from Part' to create an assembly. Use mates to place all of your
parts on a single layer and - where possible - join the parts so that a single cut is along a straight
line of two parts. You can space the parts out and make two cuts of it, but a single cut will reduce your
making costs by half.</p>
<p><img alt="case array layout" class="img-responsive" src="http://www.forembed.com/images/2016/06/case-layout-graphical.png"></p>
<p>A couple of tips that you see implemented above.</p>
<ol>
<li>Wherever possible, join two straight edges! One cut is half of the price of two.</li>
<li>Prefer chamfers over fillets. Rounded features slow the cutter down significantly. We have a combination
of round and chamfered features.</li>
<li>Nest parts wherever possible. You can see that we have nested the bottom two rows in order to fit a couple
more designs onto the board. If you can slightly change your design to take advantage of nesting, do it! You
are paying for a complete sheet of material and you should use as much of it as you can!</li>
</ol>
<h3>Create a SolidWorks Drawing</h3>
<p>You can do an Adobe Illustrator export from here, but it won't work correctly. You need to create a
Solidworks Drawing using 'File->Make Drawing from Assembly'.</p>
<p><img alt="new drawing window" class="img-responsive" src="http://www.forembed.com/images/2016/06/solidworks-new-drawing-window.png"></p>
<p>When you create the new drawing, us the 'custom sheet size'. You want the drawing to be blank - no title
block! What you see will be exported.</p>
<p><img alt="model view button" class="img-responsive" src="http://www.forembed.com/images/2016/06/solidworks-model-view.png"></p>
<p>'View Layout' tab, then 'Model View'. Once you are in the 'Model View', be sure to select the custom scale
and select 1:1.</p>
<p><img alt="model view, scale" class="img-responsive" src="http://www.forembed.com/images/2016/06/case-layout-drawing.png"></p>
<h3>Export from SolidWorks</h3>
<p>'File->Save As...' and be sure to select the file format as "Adobe Illustrator Files (*.ai)".</p>
<h2>Import Inkscape</h2>
<h3>Download Templates</h3>
<p>If you downloaded the templates, great! If you didn't, <a href="http://www.ponoko.com/home/download-files">go get them</a>!
We will be opening the "P1 Inkscape.svg" file and saving it as something else.</p>
<p><img alt="Ponoko P1 Template" class="img-responsive" src="http://www.forembed.com/images/2016/06/p1-template.png"></p>
<p>The file contains a lot of information. We will not be engraving, but know that if you need to engrave, you will
use 'red' or 'black'. We will be cutting, so we will use 'blue'. Red is for engraved lines, black is for engraved
areas, blue is for cuts.</p>
<h3>Importing</h3>
<p>'File->Import...' and navigate to the *.ai file that you saved from SolidWorks. I like to uncheck the lower two
boxes.</p>
<p><img alt="Inkscape AI Import Window" class="img-responsive" src="http://www.forembed.com/images/2016/06/inkscape-ai-import-window.png"></p>
<p>Now you should have a drawing that looks pretty similar to the drawing that you created in SolidWorks. Use the cursor
to center the entire panel on the P1 panel, staying within the orange template. <em>DO NOT</em> grab the little arrows! They
will drag only that node and distort your panel! If you accidently grap one of these and drag, be sure to undo it
or simply re-import.</p>
<p><img alt="Inkscape Imported" class="img-responsive" src="http://www.forembed.com/images/2016/06/inkscape-layout-on-drawing.png"></p>
<h3>Create Paths</h3>
<p>You might imagine that the laser tool head must travel and have a path to travel along. With your import selected, go
to 'Path->Object to Path'. This doesn't appear to change much, but it does change the underlying format with which the
file objects are saved.</p>
<h3>The Right Strokes</h3>
<p>To the right, there is a menu that may be minimized called "Fill and Stroke". Clik on it so that you can see the "Fill",
"Stroke Paint", and "Stroke Style" tabs.</p>
<p><img alt="Inkscape Fill and Stroke" class="img-responsive" src="http://www.forembed.com/images/2016/06/inkscape-fill-stroke-window.png"></p>
<p>Click the "Fill" tab and click on the "X". There should be no fill for the cutting outlines.</p>
<p>Click the "Stroke Paint" tab. Click "Flat color" just next to the "X", with color mode "RGB" and the values should be
0, 0, 255, 255. This defines "blue", which is the cutting color. If you wanted to engrave, you would use red or black.</p>
<p><img alt="Inkscape Stroke Paint Blue" class="img-responsive" src="http://www.forembed.com/images/2016/06/inkscape-stroke-paint-blue.png"></p>
<p>Finally, click on the "Stroke style" tab. The width should be set to 0.01mm, according to the Ponoko documentation.</p>
<p>Once you do all of this, your design has a <em>very</em> thin line, you may not even be able to see it! Zoom in (CTRL + mouse wheel)
to see the thin line.</p>
<h3>Engraving</h3>
<p>In some cases, you may wish to add an engraving to your item(s). I would suggest that you create the engraving and convert
it to a path in a separate Inkscape window, then copy/paste it into this window. There are lots of ways to do it, but I have
had the most success using that method.</p>
<h2>Finishing Up</h2>
<p>Before uploading your design, remove unnecessary layers using the layers dialog (usually to the right). Right-click on each
layer that is not called "Your Design" and remove it.</p>
<p><img alt="Inkscape Drawing Read" class="img-responsive" src="http://www.forembed.com/images/2016/06/inkscape-ready.png"></p>
<p>Now save your file in the default *.svg format and upload to Ponoko!</p>Project Curve Tracer - v1.1 Model2016-06-20T12:00:00-04:002016-06-20T12:00:00-04:00Jason Jonestag:www.forembed.com,2016-06-20:/project-curve-tracer-1v1-model.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/curve-tracer-1v1-cad.png" alt="CAD model, Solidworks"></p>
<h2>Modeling Software</h2>
<p>I am a big fan of <a href="http://www.solidworks.com/">SolidWorks</a> as a 3D design package. I am completely untrained, yet with
a few hours of practice, anyone can pick up the software and do something useful. After a while, you can start to do
some really nice things with it!</p>
<p>The …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/curve-tracer-1v1-cad.png" alt="CAD model, Solidworks"></p>
<h2>Modeling Software</h2>
<p>I am a big fan of <a href="http://www.solidworks.com/">SolidWorks</a> as a 3D design package. I am completely untrained, yet with
a few hours of practice, anyone can pick up the software and do something useful. After a while, you can start to do
some really nice things with it!</p>
<p>The biggest down side is the cost. The sticker price for one of these is $4000 (last I checked a few years back). I
purchased a copy at a significant discount for a project that I was working on, but I don't pay the maintenance on it,
so the software never gets updated.</p>
<p>I have looked into <a href="http://www.freecadweb.org/">FreeCAD</a> and other tools, but I have found that nothing comes close to
SolidWorks where my time is concerned, so I stick with it (for now).</p>
<h2>Modeling</h2>
<p>I only modeled the connectors. In this case, the resistors and ICs are simply too short to be concerned with. I have
had to model components in the past when there was limited headroom in a particular application, but this is not one of
those cases.</p>
<p>You may also note that I skipped modeling the hardware as well. I placed the components with plenty of room for english
or metric hardware. In english units:</p>
<ul>
<li>4x 4-40 threaded hex standoff, 0.75" long</li>
<li>4x 4-40 straight-through standoff, 0.25" long</li>
<li>8x 4-40 screw, pan-head, 0.5" long</li>
</ul>
<p>If you are into metric:</p>
<ul>
<li>4x M3 threaded hex standoff, 20mm long</li>
<li>4x M3 straight-through standoff, 6mm long</li>
<li>8x M3 screw, pan-head, 12mm long</li>
</ul>
<p>You should note that this is preliminary and I haven't tested the setup just yet... but I will!</p>
<h2>PCB Changes from v1.0 to v1.1</h2>
<p>There are no changes to the active components of the schematic, only the interfaces.</p>
<p>We removed two terminals from the screw terminal row and replaced them with the safety banana jacks. You can now use
your existing multimeter leads to interface with the curve tracer! In addition, any other banana jack equipped leads
will interface with the curve tracer as well. I feel that it is a safe assumption that anyone that might want one of
these would also have a set of multimeter leads laying around. You can see the two large barrel components out of the
bottom of the PCBA in the model. I initially wanted to include these on the top layer, but they are simply too large
to make this practical, so I moved them to the bottom.</p>
<p>Also, we moved the USB connector from the 'back' to the 'side'. This allows the user to place the curve tracer on its
'back' and have access to all of the terminals without having to re-orient the board.</p>
<p>All-in-all, very minor changes.</p>
<h2>Case Design</h2>
<p>You may remember that we based the footprint off of the <a href="http://dangerousprototypes.com/docs/Sick_of_Beige_basic_case_v1">Sick of Beige</a>
case design. Specifically, the 'golden rectangle' 60mm x 37mm board. We like this choice, but it has left us
with the requirement to cut out a couple of holes and go mildly into case design.</p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/06/curve-tracer-1v1-cad-top.png" alt="CAD model, Solidworks"></p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/06/curve-tracer-1v1-cad-bottom.png" alt="CAD model, Solidworks"></p>
<p>The cutouts are simple and should be quite easy to manage on any laser cutter. I'm considering a couple of places for the case
manufacturing, such as <a href="http://www.ponoko.com/">Ponoko</a>. First, I have to figure out how to generate vector graphics. If
you happen to know a quick and dirty way to get a sketch from SolidWorks into an SVG form, drop a line in the comments!</p>Project Curve Tracer - June Progress2016-06-14T16:10:00-04:002016-06-14T16:10:00-04:00Jason Jonestag:www.forembed.com,2016-06-14:/project-curve-tracer-june-progress.html<h2>Repository Adjustments</h2>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/repository_split.png" alt="Repository Split"></p>
<p>As the hardware is at the point of initial release, but the firmware and GUI aren't
quite there yet, I decided that it would be prudent to split off the firmware and GUI
components into their own GIT repositories. Tracking all files in the same repository
was convenient …</p><h2>Repository Adjustments</h2>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/repository_split.png" alt="Repository Split"></p>
<p>As the hardware is at the point of initial release, but the firmware and GUI aren't
quite there yet, I decided that it would be prudent to split off the firmware and GUI
components into their own GIT repositories. Tracking all files in the same repository
was convenient through the initial stages of project development, but splitting them
allows for more targeted releases and will likely make contributions easier. For instance,
if someone wished to write a Java client, then they could simply fork the GUI repository
and not worry about the hardware and firmware repositories.</p>
<p>As it stands, there are three repositories:</p>
<ul>
<li><a href="https://github.com/slightlynybbled/curve_tracer_hardware">hardware</a> - dedicated to the
KiCAD, gerber and BOM files</li>
<li><a href="https://github.com/slightlynybbled/curve_tracer_firmware">firmware</a> - dedicated to the
C files that comprise the firmware of the microcontroller</li>
<li><a href="https://github.com/slightlynybbled/curve_tracer_gui">GUI</a> - dedicated to the GUI (currently
Python)</li>
</ul>
<h2>Hardware Progress</h2>
<p><img class="img-responsive" style="border-radius: 20px;" src="http://www.forembed.com/images/2016/06/curve-tracer-layout-1v0-official.png" alt="Curve Tracer v1.0 layout"></p>
<p>We have released <a href="https://github.com/slightlynybbled/curve_tracer_hardware/releases/tag/v1.0">v1.0</a> of the hardware!
This release supports:</p>
<ul>
<li>Your choice of visualization, Oscilloscope or Desktop</li>
<li>Use of XY mode available on most oscilloscopes</li>
<li>Maximum of +/-5.0V on the probe outputs</li>
<li>Gate voltage application of up to 5.0V</li>
<li>Transistor base current of up to 10mA</li>
<li>Uses USB for the power supply (connects as Virtual COM Port)</li>
</ul>
<p>Once the firmware and GUI catch up wit the hardware in maturity, we will create a curve tracer page in the
references to get you up and running quickly!</p>
<h2>Firmware Progress</h2>
<p>Minor improvements and functionality have been added to the firmware. Specifically, two XY outputs were added to
support the oscilloscope XY mode. These are simply PWM outputs that reflect the measured component voltage and
current. There have been some small bug-fixes and improvements, but the firmware has been working well for some
time now and is nearing that 'v1.0' milestone.</p>
<h2>Client GUI Progress</h2>
<p>The Client has shown the most progress in recent weeks. We can save a live plot as a CSV file, recall that plot,
and erase it. We can also 'run' and 'pause' the live plot as needed.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/screenshot-3term-saved.png" alt="Curve Tracer v1.0 layout"></p>
<p>We have also filled out the menus for 'file', 'edit', and 'help', which contains all of the functionality of the
shortcut bar, plus a couple of extra less-used features. Oh, and take a look at the status bar! Lots of new
display features there, continually updated by the hardware.</p>
<p>In short, the GUI is already quite useful for live plotting and is getting features added as time goes on.</p>
<p>I have encountered an issue that I'm unsure of how to deal with. I am experiencing an optimization problem during
plotting that I have described on
<a href="http://stackoverflow.com/questions/37766482/python3-tkinter-canvas-create-image-and-create-oval-optimization/37766721">StackOverflow</a>.
It seems that plotting each point is taking more time than I thought it might. I don't see a quick and easy
fix for this at the moment, particularly since it is working well, but I am hoping that someone else might
have some insight into how to make this process a bit more efficient.</p>
<h2>Evolution of the Visualization</h2>
<p>I must admit that it was quite exciting to get the initial plots to the screen, but looking back a bit, it is
even more exciting to see where we have been on the hardware and software as it has evolved. I would like to
take a moment to look back on the screenshots from the time that we got to the first visualizations until the
present moment.</p>
<p>The first visualizations were crude, at best. In this first version, we found that we didn't even have all of the
points displayed on the screen, but we could see that the hardware was basically working, just not very well:</p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/04/curve-tracer-capacitor-10uF.png" alt="Curve tracer screenshot 0"></p>
<p>Firmware improvements got us a few more points on the screen and a complete waveform:</p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-100.png" alt="Curve tracer screenshot 1"></p>
<p>An improvement to the GUI added some functionality. In addition, the screen now looks a bit more like ye old
CRT:</p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/05/curve-tracer-visualization-active.png" alt="Curve tracer screenshot 2"></p>
<p>Hardware improvements greatly reduced the noise:</p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/05/curve-tracer-cap-50hz.png" alt="Curve tracer screenshot 3"></p>
<p>Finally, we doubled the resolution of each point along with doubling the number of points on the screen. We also added
adjustments to the waveform, gate voltage, two-terminal/three terminal modes, and status updates across the bottom:</p>
<p><img class="img-thumbnail" src="http://www.forembed.com/images/2016/06/capacitor-60hz.png" alt="Curve tracer screenshot 4"></p>
<p>We changed from lines to dots so that we would easily be able to see the data density. Using lines tended to obscure
that. We would also like to re-add the color coding so that we could see the directionality of the waveform, particularly
when there is reactance present. A capacitor and inductor will have opposite directions and that information can be
useful for troubleshooting.</p>
<p>We look forward to a complete v1.0 package!</p>Project Curve Tracer: Design Refinement2016-06-07T20:30:00-04:002016-06-07T20:30:00-04:00Jason Jonestag:www.forembed.com,2016-06-07:/project-curve-tracer-design-refinement.html<h2>Design Refinement</h2>
<p>There have been a couple more patch wires and a bit of scope creep, but no show-stoppers and a
fair amount of added functionality.</p>
<p>Since the last post, we have:</p>
<ul>
<li>Added an offset calibration mode which removes any voltage offset that may exist</li>
<li>Added a three-terminal mode which …</li></ul><h2>Design Refinement</h2>
<p>There have been a couple more patch wires and a bit of scope creep, but no show-stoppers and a
fair amount of added functionality.</p>
<p>Since the last post, we have:</p>
<ul>
<li>Added an offset calibration mode which removes any voltage offset that may exist</li>
<li>Added a three-terminal mode which is intended for driving MOSFETs and changes the display
to a 'drain current' view</li>
<li>Added a "gate drive" function which whill adjust a MOSFET gate (or transistor)
at a particular voltage.</li>
<li>Added a complete waveform adjustment window</li>
<li>Changed the display to points instead of lines (the lines were great, but they gave a false
impression of having a lot of data that may not have been there)</li>
<li>Added status data for each of the above functions</li>
<li>Added an XY mode so that you can use the curve tracer with an oscilloscope without having to
use your PC.</li>
</ul>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/screen-capture-overview.png" alt="Curve Tracer Annotated Screenshot"></p>
<p>Lets go through each of these functions and discuss.</p>
<h3>Offset Calibration</h3>
<p>When the vertical scale was 8-bit, there was a noticable 'stepping' that occured and the samples
didn't appear quit as close to '0mA' as they should have. As a result, I added an offset
calibration that should only be run when nothing is connected. This function removes the offset.</p>
<p>In truth, since increasing the data resolution, this functionality is not entirely necessary, but
having not tried a large number of units, we should leave it in for now.</p>
<h3>Gate Adjustment</h3>
<p>In order to check out a MOSFET or transistor, a third voltage or current must be available which
can drive the third leg. This is where the gate voltage adjustment comes into the picture. This
feature has been supported in hardware since the design inception but has not been utilized until
recently. As can be seen below, the </p>
<h3>2-Terminal/3-Termainal Modes</h3>
<p>This feature allows the user to switch between the standard two-terminal probe setup and the three-
terminal MOSFET/transistor mode.</p>
<p>When in two-terminal mode, a sinusoidal output - positive and negative - is placed across the probes.
This gives the 'standard' curve traces that are usually observed on curve traces with probes.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/MOSFET-mode-2-term.png" alt="MOSFET, two-terminal mode, Vg=5V"></p>
<p>The three-terminal mode will pull V- to ground and V+ will have a triangle waveform applied to it, so
the voltage applied across the D/S of the MOSFET is always greater than or equal to zero. The user may
then adjust the gate voltage as they see fit and observe the drain current. This will crudely reproduce
the curves that are so common in MOSFET/transistor datasheets.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/MOSFET-mode-3-term.png" alt="MOSFET, three-terminal mode, Vg=5V"></p>
<p>Note in the status that both shots have a gate voltage of 5Vg applied. For comparison, we have an extra
pic with 4.0V applied to the gate:</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/MOSFET-mode-3-term-4V.png" alt="MOSFET, three-terminal mode, Vg=4V"></p>
<p>I would like to keep the C code to a minimum on this project since the microcontroller only has 16kB
of program memory, so it would be great in the future to add a button on the GUI that will command
the microcontroller to go to different voltages and draw out the different curves all on one plot.
This wouldn't be a difficult task, but is still straying a bit from the core functionality that we
are trying to add.</p>
<h3>Waveform Adjustment Window</h3>
<p>Initialially, there were individual buttons for amplitude, frequency, and offset. Once it started to
look a bit crowded, I integrated these functions into a single window interface along with a little
graphic to assist the user.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/waveform-adjustment-window.png" alt="Waveform Adjustment Window"></p>
<p>I chose to use peak voltage instead of peak-to-peak voltage because many users may be interested in the
highest voltage that will be applied to their assembly. Some users may only wish a maximum of 1V be applied,
so this interface allows that adjustment with little thought. For those with more EE-fu, you can easily
double the number and know where you are going.</p>
<h3>Status</h3>
<p>For most of the above functions, we have added Dispatch messages to be sent to the GUI periodically so
that the GUI status may be updated. There is some code involved, but the
<a href="https://github.com/slightlynybbled/Dispatch">dispatch</a> and <a href="https://github.com/slightlynybbled/SerialDispatch">serialdispatch</a>
libraries have really helped with communication between the PC and board.</p>
<h3>XY Mode</h3>
<p>At some point today, it struck me that many people using this will probably already have an old oscilloscope
or two laying around and that I should place a couple of dedicated XY-mode outputs for that purpose. I
still feel a bit nostalgic when thinking back to my huntron tracker CRT screen. So I found a couple of unused
PWM outputs on the microcontroller that will mirror the differential voltage and current and filtered them
using a few extra RCs.</p>
<p>My initial thought was was just place the X probe on the positive output voltage and the Y probe on the output
of the current-to-voltage amplifier. When I mated these to the XY mode of my oscilloscope with a diode load,
I saw an unexpected waveform on the oscilloscope:</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/scope-xy-mode-diode.bmp" alt="Oscilloscope in XY mode, diode"></p>
<p>Note that I adjusted the vertical scale of the traces down by 2.5V in order to center the waveforms since the scope
is measuring absolute voltage rather than relative voltage.</p>
<p>What is that funny tail at the top right? My GUI showed a much more conventional trace:</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/screen-capture-diode.png" alt="GUI, diode"></p>
<p>After a few minutes of head-scratching, I realized that the curve tracer microcontroller is measuring the differential
voltage across the component while the oscilloscope was only measuring the positive. This is valid for
light loads, but anything that saturates the opamp is going to have the incorrect tails. A look at both voltage
traces reveals that the differential voltage is actually quite small during the near-short circuit caused by the
diode, but this is not taken into account when the scope is only attached to one output lead.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/06/diode-traces.bmp" alt="Oscilloscope traces, diode"></p>
<p>The solution is to pipe the differential voltage and the current through the microcontroller and to a couple of analog outputs.<br>
In this case, we don't have any more analog outputs, but we do have a couple of unused digital pins that we
can easily appropriate for the task.</p>
<p>This is the largest scope creep that has entered the project thus far, and really isn't that much. The microcontroller
limitation has been demenonstrated to be the ADC and not the level of processing, so adding a couple more peripheral
loads will impact our project minimally.</p>
<p>This has the added benefit of being able to operate the curve tracer in any standard lab without having to fire up a PC,
which is the primary reason that I added the feature. Sure, you won't be able to adjust the frequency or amplitude, but
95% of troubleshooting is done at the 60Hz, +/-5.0V setting anyway, so this is perfect.</p>
<p>As a result of modifying the schematic, we had to adjust our layout a bit to add the extra screw terminals:</p>
<p><img class="img-responsive" style="border-radius: 20px;" src="http://www.forembed.com/images/2016/06/curve-tracer-layout-1v0.png" alt="Curve Tracer v1.0 layout"></p>
<h2>Where To Next?</h2>
<p>We are working towards a complete version 1.0, but first, we need a couple more features to be fully set. I believe
that the hardware is already at that 1.0 mark, but the software is where we need a bit of work. I would like to have
the ability to put several 3-terminal voltage lines on the screen at one time. I would also like the ability to save
and recall waveforms as-needed.</p>
<p>I will give it a couple of days before calling the hardware complete. Once I am at the point that all hardware and software
is at the releasable level, then I will tag the repository.</p>Project Curve Tracer: Bringing Up the New Boards2016-05-31T08:21:00-04:002016-05-31T08:21:00-04:00Jason Jonestag:www.forembed.com,2016-05-31:/project-curve-tracer-bringing-up-the-new-boards.html<h1>Current Board Status</h1>
<p>The board is currently working and working well, but there were a few bumps along the way. There always
are, but - in this case - they were slightly more than expected.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-board-pic.jpg" alt="Curve Tracer PCBA"></p>
<h1>Surprises</h1>
<p>After the <a href="http://www.forembed.com/images/2016/03/curve_tracer_soldered_board.jpg">last revision</a>, we thought that the
kinks were worked out of the schematic. There …</p><h1>Current Board Status</h1>
<p>The board is currently working and working well, but there were a few bumps along the way. There always
are, but - in this case - they were slightly more than expected.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-board-pic.jpg" alt="Curve Tracer PCBA"></p>
<h1>Surprises</h1>
<p>After the <a href="http://www.forembed.com/images/2016/03/curve_tracer_soldered_board.jpg">last revision</a>, we thought that the
kinks were worked out of the schematic. There were still a couple of surprises left for us, but nothing
that couldn't be fixed with a couple of trace cuts and a patch wire.</p>
<h2>DAC Output Impedance</h2>
<p>This was the first surprise of the day. After going through a quick software port during which we disabled
the on-chip opamps and shifted the DACs directly to the pin outputs, the waveforms observed were very
distorted. It turns out that the DACs have a very high output impedance and are incapable of driving any
but the highest of impedances themselves. This led us to re-connect the opamps in software and to add a
patchwire. Fortunately, this is the only hardware patch we have had to do to the board. Unfortunately,
we had a hardware patch.</p>
<h2>Current-Sense Gain</h2>
<p>We started this schematic with a full range of -2.5mA to +2.5mA. During this testing, we have expanded that
range by reducing the current-sense gain of the differential opamp to 10 from 100. This should improve the
output response and brings the circuit closer to the current ranges of the Huntron Tracker 2000 that we are
targeting as a respacement. Now the full range is -25mA to +25mA.</p>
<h2>Adjusting Filters</h2>
<p>This one was not entirely unexpected as there is always a bit of tuning and adjustment. It turns out that the
noise that we had experienced on the previous revision was a result of PWM present on the
current-sense reference pin. The noise was still present on this iteration of the layout. A simple RC filter
adjustment from 1kΩ to 10kΩ squashed the problem. Since we had a patch wire on this board
anyway, we will probably add another stage to this filter to make it bulletproof.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/schematic-i-sense-reference.png" alt="Schematic - Current Sense Reference"></p>
<h2>DAC Update Rate</h2>
<p>In the midst of seeing what some components looked like on the curve tracer, we found one result that was a
bit startling at first:</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/scope-cap-peaky-current-1.png" alt="Scope Capture - Peaky Current Across a Capacitor"></p>
<p>In this scope capture, we had placed a 4.7μF capacitor on the curve tracer and just happended to be
monitoring the waveforms with the scope as well. Note tha the 'envelope' of the current waveform (yellow)
is roughly a lagging sine wave, which is what we would expect... but where is all of that choppiness
coming from?</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/scope-cap-peaky-current-2.png" alt="Scope Capture - Even More Peaky Current Across a Capacitor"></p>
<p>If you look closely at the waveform (blue), you can see the small stair-steps caused by the DAC update.<br>
On each update, the DAC voltage changed quickly and the current across the capacitor peaked. On observing this,
we made a software change that ensures that the DAC update is much faster.</p>
<h3>Software Strategy</h3>
<p>The GUI sends a calculated period based on the user-desired frequency. The period is based on the instruction cycle of
the microcontroller, which is 12MIPS. The GUI sends a period, in cycles, based on this instruction cycle.</p>
<p>After a bit of playing with the update rate, it was determined that a period of <code>1000</code> is adeqate. The period of <code>1000</code>
corresponds to 83.3μs. Whenever a period is requested that is greater than <code>2000</code>, the microcontroller will decrease
the period until it is less than <code>2000</code> and decrease the omega by the same increment. The GUI is unaware of this particular
calculation and translation is done completely on the microcontroller.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">changePeriod</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="kt">uint16_t</span> <span class="n">newPeriod</span><span class="p">;</span>
<span class="n">q16angle_t</span> <span class="n">newOmega</span> <span class="o">=</span> <span class="n">HIGH_SPEED_THETA_INCREMENT</span><span class="p">;</span>
<span class="n">dacSamplesPerAdcSamples</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">DIS_getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">&</span><span class="n">newPeriod</span><span class="p">);</span> <span class="c1">// retrieve the commanded period</span>
<span class="k">while</span><span class="p">(</span><span class="n">newPeriod</span> <span class="o">></span> <span class="mi">2000</span><span class="p">){</span>
<span class="n">newPeriod</span> <span class="o">>>=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">newOmega</span> <span class="o">>>=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">dacSamplesPerAdcSamples</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">omega</span> <span class="o">=</span> <span class="n">newOmega</span><span class="p">;</span>
<span class="n">theta</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">PR1</span> <span class="o">=</span> <span class="n">newPeriod</span><span class="p">;</span> <span class="c1">// PR1 is the TMR1 period register</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p><code>HIGH_SPEED_THETA_INCREMENT</code> is a constant that is based on the number of samples that are to be sent to the curve
tracer GUI. This is a constant because the angle progress per ADC sample is maintained as the same throughout the
operation of the device. Only the timer interrupt period changes. In this case, the <code>HIGH_SPEED_THETA_INCREMENT</code>
is calculated to <code>1024</code>, which translates to about 5.6°.</p>
<p>The code does a bit more than is described above. By changing the DAC update rate without changing the ADC update
rate, there needs to be some method to keep track of when each is intended to occur. We use <code>dacSamplesPerAdcSamples</code>
to keep track of this so that the period may be re-scaled when it is sent back to the GUI.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_T1Interrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">theta</span> <span class="o">+=</span> <span class="n">omega</span><span class="p">;</span>
<span class="n">DAC1DAT</span> <span class="o">=</span> <span class="n">q15_fast_sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">;</span>
<span class="n">DAC2DAT</span> <span class="o">=</span> <span class="n">q15_fast_sin</span><span class="p">(</span><span class="n">theta</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">;</span> <span class="c1">// theta + 180 deg</span>
<span class="cm">/* reset sampleIndex on every cycle */</span>
<span class="k">if</span><span class="p">(</span><span class="n">theta</span> <span class="o">==</span> <span class="mi">0</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">xmitActive</span> <span class="o">==</span> <span class="mi">0</span><span class="p">){</span>
<span class="n">sampleIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">SAMP</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">((</span><span class="n">theta</span> <span class="o">&</span> <span class="p">(</span><span class="n">HIGH_SPEED_THETA_INCREMENT</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span> <span class="o">==</span> <span class="mi">0</span><span class="p">){</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">SAMP</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">T1IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The T1 interrupt simply advances <code>theta</code> in increments of <code>omega</code>, both of which were set in <code>changePeriod()</code>. By
advancing <code>theta</code>, we can see in lines 4 and 5 that the DAC outputs are modified appropriately to generate the
sine wave. </p>
<p>The T1 interrupt is also responsible for starting the ADC at the appropriate time so that the samples are taken evenly
throughout the waveform. This is why the <code>ADC1CON1bits.SAMP</code> bit is being cleared under certain conditions.</p>
<h3>Results</h3>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/scope-cap-nonpeaky-current.png" alt="Scope Capture - Non-Peaky Current Across a Capacitor"></p>
<p>This picture also takes into account the gain adjustment of the current sensor circuit, mentioned previously</p>
<h1>Results</h1>
<p>The results are quite good compared to the <a href="http://www.forembed.com/images/2016/05/curve-tracer-visualization-active.png">previous results</a>. </p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-cap-10hz.png" alt="Curve Tracer - Cap at 10Hz"></p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-cap-50hz.png" alt="Curve Tracer - Cap at 50Hz"></p>
<p>There is still a small ripple observed on the waveform, which - I believe - is the result of the PWM filter on the current-sense
reference. The next revision will make this a 2-pole filter, which should remove that ripple quite well.</p>
<p>There is also a small vertical offset that can be observed. We will implement a software calibration that occurs at
startup or on external command to remove this offset.</p>
<p>It looks like one more round of hardware revision and a couple of tweaks to GUI software will put us ready to release!</p>
<p>As always, the most recent working data may be found on <a href="https://github.com/slightlynybbled/project_curve_tracer">github</a>.</p>Project Curve Tracer: Visualization Maturing2016-05-26T09:20:00-04:002016-05-26T09:20:00-04:00Jason Jonestag:www.forembed.com,2016-05-26:/project-curve-tracer-visualization-maturing.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-visualization-active.png" alt="Prototype tkinter frame layout"></p>
<h1>Making Progress on the GUI</h1>
<p>As you can see, the visualization tool is becoming more mature. We have taken the visualization
scaffolding <a href="http://www.forembed.com/project-curve-tracer-visualization-scaffolding.html">developed in the last post</a>
and added a few elements to each frame. It is amazing what a few buttons and menus will do for the
looks of …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-visualization-active.png" alt="Prototype tkinter frame layout"></p>
<h1>Making Progress on the GUI</h1>
<p>As you can see, the visualization tool is becoming more mature. We have taken the visualization
scaffolding <a href="http://www.forembed.com/project-curve-tracer-visualization-scaffolding.html">developed in the last post</a>
and added a few elements to each frame. It is amazing what a few buttons and menus will do for the
looks of an application. The visualization still has that 'Tk' look which, though dated, is quite
functional.</p>
<p>If you have been following along, you may note that the above is the curve trace of a capacitor and that we are still working
on the original hardware. We will be transitioning to the <a href="http://www.forembed.com/project-curve-tracer-another-layout.html">new layout</a>
soon. I have received the boards and am half-way through soldering the parts on. I am hoping that the improvements to the analog
section will yield a much higher quality of data. We shall see...</p>
<h1>Features</h1>
<h2>What Works</h2>
<p>At this point, the GUI may be used to:</p>
<ul>
<li>select the port</li>
<li>change the frequency</li>
<li>display the currently-selected serial port</li>
<li>display the communication status</li>
<li>display the frequency</li>
</ul>
<p>This list comprises all of the required functionality of the curve tracer, but we would like to add
a bit more. In my experience, the demonstrated capability of the current version of the
visualization will meet the requirements of 90% of those wishing to use the curve tracer as a
troubleshooting tool. When I was using a curve tracer for my job as a technician, this is the mode
that I operated in almost constantly.</p>
<h2>We Want More!</h2>
<p>Despite this, we have a lot of options to improve the tool and all of those options are software-driven
at this point... so why not? Specifically, we would like to eventually add support for:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
<tr>
<td>MOSFET/transistor mode</td>
<td>This mode is for analysis of a MOSFET or transistor forward curve based on the gate/base voltage/current</td>
</tr>
<tr>
<td>Take Screenshots</td>
<td>Ability to take screenshots and save them in a common format (PNG, GIF, etc.)</td>
</tr>
<tr>
<td>Recall Screenshots</td>
<td>Ability to recall screenshots</td>
</tr>
<tr>
<td>CSV</td>
<td>Save curve data to a CSV file</td>
</tr>
<tr>
<td>Recall/compare</td>
<td>Recall data from CSV to be displayed and compared to live traces</td>
</tr>
<tr>
<td>Scan mode</td>
<td>Scan through a low frequency to a higher frequency to highlight reactive components</td>
</tr>
</table>
<h1>Additional Screenshots</h1>
<h2>Port Selection Window</h2>
<p><img src="/images/2016/05/port-selector-window.png" alt="Port Selection Window" class="img-responsive img-rounded"></p>
<h2>Frequency Setting Window</h2>
<p><img src="/images/2016/05/frequency-setting-window.png" alt="Frequency Setting Window" class="img-responsive img-rounded"></p>
<h2>Inactive Window</h2>
<p>When the software is started and the ports are not selected nor connected.</p>
<p><img alt="Curve tracer visualization - inactive" src="http://www.forembed.com/images/2016/05/curve-tracer-visualization-inactive.png"></p>Dispatch Visualizer2016-05-24T13:19:00-04:002016-05-24T13:19:00-04:00Jason Jonestag:www.forembed.com,2016-05-24:/dispatch-visualizer.html<h1>Vision</h1>
<p>For some time, I have been working in small portions on Dispatch as a serial library for the
<a href="http://www.forembed.com/project-curve-tracer-requirements.html">curve tracer</a>. Initially,
I thought that the serial library would be limited in scope to that particular project, but I
began to see that the library could, potentially, be very useful …</p><h1>Vision</h1>
<p>For some time, I have been working in small portions on Dispatch as a serial library for the
<a href="http://www.forembed.com/project-curve-tracer-requirements.html">curve tracer</a>. Initially,
I thought that the serial library would be limited in scope to that particular project, but I
began to see that the library could, potentially, be very useful on many of my past microcontroller
projects.</p>
<p>As the C and Python modules have matured into <code>Dispatch</code> (C) and <code>serialdispatch</code> (Python), so,
too, has my vision of what this library could potentially achieve in the future with relatively
little effort. I will present this vision in this post.</p>
<h1>Dispatch and serialdispatch</h1>
<p>The Dispatch libraries, as they are, allow the user to define <em>any</em> topic to which data may be
published and consumed. This in itself is quite useful. Data may be sent and received easily
and simply for any purpose.</p>
<h1>What's Next?</h1>
<p>The next level will be to create standard topics to which to publish. When data is written to any
of these topics, then the module would automatically bring up the appropriate display type and,
then, display the data. We wish to turn standard C strings into a sort of scripting command for
the PC to interpret and display with no further user intervention!</p>
<h1>Why?</h1>
<p>Why do this? Couldn't anyone write any program in Python/Ruby/Java/<insert your favorite language here>
that would create an appropriate visualization? Well, yes, but - generally - they won't. Most
embedded C and C++ programmers know their chosen language quite well and will dabble in other
languages as necessary. As a result, they know best how to manipulate C and probably don't want
to bother with (or - more likely - don't have the time to bother with) creating the library that
would easily provide the visualization.</p>
<p>In essence, the visualization will be controlled by the C code that is publishing to it with the
end user not having to write a single line of Python or other in order to see his/her data.</p>
<h1>How?</h1>
<p>We will create a standard topic format which should - ideally - be extendable and easy to use.</p>
<h2>Modifications to serialdispatch</h2>
<p>Currently, subscriptions are expecting to find the exact string that is to be sent as the topic, nothing
more and nothing less. For instance, data sent to the topic <code>plot</code> will have different subscribers
from data sent to the topic <code>plot</code> (there is an extra space in there). As a result, we will need to
add a new type of subscriber to the currently existing serialdispatch which will subscribe if the first
few characters match a defined pattern. In this case, <code>plot</code>. I suspect that we will call this new
subscriber method something like <code>subscribe_to_prefix</code> or <code>subscribe_to_command</code>. This will not affect
the functionality of the normal <code>subscribe</code> and will be 100% backwards compatible.</p>
<h2>Format</h2>
<p>The standard format will be space-delimited (recall that we have already utilized commas and colons)
in the dispatch format). We will use the somewhat-familiar command-line syntax of higher-level OSs.
Fortunately, we won't have to interpret them in C but in the more dynamic Python code.</p>
<div class="highlight"><pre><span></span>DIS_publish("<command> <option1> <option2> ... <optionX>", &dataPtr1, &dataPtr2, ..., &dataPtrX)
</pre></div>
<h3>Commands</h3>
<p>At the moment, I can only think of a couple of commands that would be most useful. This list is
sure to expand:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Command</th>
<th>Description</th>
</tr>
<tr>
<td>plot</td>
<td>Plots the data according to its options.</td>
</tr>
<tr>
<td>log</td>
<td>Log the data according to its options.</td>
</tr>
<tr>
<td>csv</td>
<td>Save the data to a CSV file.</td>
</tr>
</table>
<h3>Plot Options</h3>
<table class="table table-bordered table-striped">
<tr>
<th>Option</th>
<th>Short Equivalent</th>
<th>Description</th>
</tr>
<tr>
<td>--name 'name in single quotes'</td>
<td>-n 'name in single quotes'</td>
<td>Applies the data to a named plot. If this is not present, the named plot will be 'default'. Writing data to the 'default' plot will have the same effect.</td>
</tr>
<tr>
<td>--dots</td>
<td>-d</td>
<td>The data will be plotted using dots.</td>
</tr>
<tr>
<td>--lines</td>
<td>-l</td>
<td>The data will be plotted using lines.</td>
</tr>
<tr>
<td>--polar</td>
<td>-p</td>
<td>The plot will be a polar plot. If the plot is currently a different type of plot with existing data on it, then that plot will be wiped and the new dataset will be applied.</td>
</tr>
<tr>
<td>--keep-data</td>
<td>-k</td>
<td>Append the data contained herein to the existing data set.</td>
</tr>
<tr>
<td>--xLabel 'x-axis label in single quotes'</td>
<td>-x 'x-axis label in single quotes'</td>
<td>The label for the x-axis.</td>
</tr>
<tr>
<td>--yLabel 'y-axis label in single quotes'</td>
<td>-y 'y-axis label in single quotes'</td>
<td>The label for the y-axis.</td>
</tr>
</table>
<h2>Examples</h2>
<p>We will begin with the most basic example and expand on that. If you haven't read the
<a href="http://www.forembed.com/introducing-dispatch.html">Dispatch introductory material</a> on Dispatch,
you may experience some confusion as this material will not be explained in detail here.</p>
<h3>Basic Plot</h3>
<p>These examples are provided as a vision of capability. As happened with Dispatch, this vision of
the implementation will likely morph in the future, leaving this blog post behind, but the goal will
remain the same.</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">xData</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">yData</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"plot,u8,u16"</span><span class="p">,</span> <span class="o">&</span><span class="n">xData</span><span class="p">,</span> <span class="o">&</span><span class="n">yData</span><span class="p">);</span>
<span class="c1">// ^ ^ data addresses</span>
<span class="c1">// ^ ^ format specifiers</span>
<span class="c1">// ^ command</span>
</pre></div>
<p>The simplest plot type. When no additional parameters are supplied, then the first data point is
plotted on x and the second data point is plotted on y. If data continues to be published in this
manner, the points will continually be added to the plot. If you don't understand the 'u8' and
'u16' notation,
<a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">go back to the vision posts on Dispatch</a>
for a review.</p>
<p>If the plot type is not specified using the <code>--dots</code>, <code>--lines</code>, or <code>--polar</code> notation, then the
plot type is assumed to be <code>--dots</code>.</p>
<h3>Plotting Multiple Parameters</h3>
<p>The above works great when only one plot is required... but what if there are multiple parameters
that need to be plotted?</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">xData</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">y1Data</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">y2Data</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"plot,u8,u16,u16"</span><span class="p">,</span> <span class="o">&</span><span class="n">xData</span><span class="p">,</span> <span class="o">&</span><span class="n">y1Data</span><span class="p">,</span> <span class="o">&</span><span class="n">y2Data</span><span class="p">);</span>
<span class="c1">// ^ ^ ^ data addresses</span>
<span class="c1">// ^ ^ ^ format specifiers</span>
</pre></div>
<p>It is always assumed that all scatter data has the first element along the x axis and all addional
elements on the y axis.</p>
<h3>Plotting Multi-Dimensional Data</h3>
<p>The primary reason for creating Dispatch in the first place and not simply using the
<a href="https://github.com/Overdrivr/Telemetry">Telemetry</a> library was so that we could send multi-dimensional
data of arbitrary length. In other words, we want to send arrays of data, not just single data
points:</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">xData</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y1Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y2Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">29</span><span class="p">};</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"plot:10,u8,u16,u16"</span><span class="p">,</span> <span class="n">xData</span><span class="p">,</span> <span class="n">y1Data</span><span class="p">,</span> <span class="n">y2Data</span><span class="p">);</span>
<span class="c1">// ^ ^ ^ data addresses</span>
<span class="c1">// ^ number of elements in each array</span>
</pre></div>
<p>Note that this is basically the same command with the <code>:10</code> attached and the <code>&</code> removed from the
data addresses since the addresses are now arrays (if you don't understand this, search 'C arrays
pointers', enjoy the reading).</p>
<h3>Labeling the Plot Axes</h3>
<p>In some cases, it is useful to name the data appropriately. In this case, we will use single-quotes
to surround the data label.</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">xData</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y1Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y2Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">29</span><span class="p">};</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"plot --x 'time' --y 'adc':10,u8,u16,u16"</span><span class="p">,</span> <span class="n">xData</span><span class="p">,</span> <span class="n">y1Data</span><span class="p">,</span> <span class="n">y2Data</span><span class="p">);</span>
<span class="c1">// ^ ^ axis labels</span>
<span class="c1">// ^ ^ axis label option</span>
</pre></div>
<p>The <code>--x</code> and <code>--y</code> stand for '--xLabel' and '--yLabel`, but the shortened notation allows for
somewhat shorter transmissions. Both are valid.</p>
<h3>Naming the Plot</h3>
<p>We can use the <code>--name</code> option to name the plot. If more than one named plot has data sent to it,
then each named plot will have its own plot window allocated to it.</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">time</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">adc1</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">samples</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">adc2</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"plot --name 'adc vs. time'"</span><span class="p">,</span> <span class="n">time</span><span class="p">,</span> <span class="n">adc1</span><span class="p">);</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"plot --name 'adc vs. samples'"</span><span class="p">,</span> <span class="n">samples</span><span class="p">,</span> <span class="n">adc2</span><span class="p">);</span>
<span class="c1">// ^ plot name in single quotes</span>
<span class="c1">// ^ plot name option</span>
</pre></div>
<h3>Keeping Old Plot Data</h3>
<p>The default behavior is that every time the new data is received for a particular plot, the old
data is deleted/overwritten. Using the <code>--keep-data</code> option will ensure that old data is
retained for display. It may be useful to have a 'clear' button on the GUI to clear the screen
when necessary.</p>
<h3>Logging the Data to the Console</h3>
<p>At times, the user may wish to simply log the data.</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">xData</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y1Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y2Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">29</span><span class="p">};</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"log:10,u8,u16,u16"</span><span class="p">,</span> <span class="n">xData</span><span class="p">,</span> <span class="n">y1Data</span><span class="p">,</span> <span class="n">y2Data</span><span class="p">);</span>
<span class="c1">// ^ log command</span>
</pre></div>
<p>The default behavior for <code>log</code> is to simply display the data to a console as a series of comma-
separated values.</p>
<h3>Saving Data to CSV File</h3>
<p>Sometimes it is most efficient to simply log data directly to a CSV file for later processing. This
is particularly true for higher-volume data. For this, we can supply a relative path for the data:</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">xData</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y1Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">19</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">y2Data</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">,</span> <span class="mi">21</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">26</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">29</span><span class="p">};</span>
<span class="n">DIS_publish</span><span class="p">(</span><span class="s">"csv:10,u8,u16,u16"</span><span class="p">,</span> <span class="n">xData</span><span class="p">,</span> <span class="n">y1Data</span><span class="p">,</span> <span class="n">y2Data</span><span class="p">);</span>
<span class="c1">// ^ csv command</span>
</pre></div>
<h1>Summing Up</h1>
<p>As you can see, we are basically attempting to turn our micro-controller into a plot-scripter
for the PC. I'm sure that getting all of this accomplished will take some real time, but some
of the effort is already in-progress within the curve-tracer project. With a nice debugging tool
like this, bringing up ADCs and troubleshooting waveforms should become much easier for the
embedded engineer. I will start work on this project as the curve-tracer becomes a more mature
platform, particularly as it will become a development platform for this effort.</p>
<p>Until next time!</p>SerialDispatch is now on PyPI2016-05-23T13:43:00-04:002016-05-23T13:43:00-04:00Jason Jonestag:www.forembed.com,2016-05-23:/serialdispatch-is-now-on-pypi.html<p><img class="img-responsive" src="http://www.forembed.com/images/logos/python.png" alt="Python Logo"></p>
<p>The title pretty much says it all! I have put my first package on <a href="https://pypi.python.org/pypi">PyPI</a>!
This is something that I have been curious about for quite some time, but never had the need to do until
this project.</p>
<p>I had to rename the python port to "serialdispatch" since "pydispatch" was …</p><p><img class="img-responsive" src="http://www.forembed.com/images/logos/python.png" alt="Python Logo"></p>
<p>The title pretty much says it all! I have put my first package on <a href="https://pypi.python.org/pypi">PyPI</a>!
This is something that I have been curious about for quite some time, but never had the need to do until
this project.</p>
<p>I had to rename the python port to "serialdispatch" since "pydispatch" was aleady taken by someone else.</p>
<p>Now, installing the library is as easy as <code>pip install serialdispatch</code>.</p>
<p>Go to the <a href="https://github.com/slightlynybbled/SerialDispatch">github repository</a> to see a more complete
example of the library in-use.</p>
<p>A more complete example of Dispatch can be found in the
<a href="https://github.com/slightlynybbled/project_curve_tracer">curve tracer project</a>, in which you can find the
C library and the Python library in use in a desktop application!</p>Project Curve Tracer: Visualization Scaffolding2016-05-20T16:54:00-04:002016-05-20T16:54:00-04:00Jason Jonestag:www.forembed.com,2016-05-20:/project-curve-tracer-visualization-scaffolding.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-visualization-frame.png" alt="Prototype tkinter frame layout" align="right"></p>
<h1>Window Layout</h1>
<h2>Scaffolding Appearance</h2>
<p>As you can see above, the scaffolding of a somewhat more sophisticated is complete and ready to
be populated. You may recall that the <a href="http://www.forembed.com/images/2016/05/curve-tracer-screenshot.png">original visualizer</a>
was adequate for the immediate task, but lacked a certain sophistication and control that would be
quite nice to have …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-visualization-frame.png" alt="Prototype tkinter frame layout" align="right"></p>
<h1>Window Layout</h1>
<h2>Scaffolding Appearance</h2>
<p>As you can see above, the scaffolding of a somewhat more sophisticated is complete and ready to
be populated. You may recall that the <a href="http://www.forembed.com/images/2016/05/curve-tracer-screenshot.png">original visualizer</a>
was adequate for the immediate task, but lacked a certain sophistication and control that would be
quite nice to have going forward... so we are going to add it!</p>
<p>There are five areas to focus on. In order to make the separation more clear, we have color-coded some areas
of the window so that they could be more easily distinguished at this stage. The color is a temporary measure
for the initial development that will not last long.</p>
<h3>Title Bar</h3>
<p>We have customized the title bar with the <a href="http://www.forembed.com/images/logos/forembed.png">for(embed) logo</a> and with
the name of the application. The standard tkinter feather is cute and all, but just not what we are looking
for... at least, so long as it is easy to change.</p>
<h3>Menu Bar</h3>
<p>The standard menu bar is the staple of any modern UI. We will put most of the options within these menus,
although they aren't actually filled out at this point.</p>
<h3>Button Bar</h3>
<p>Also quite common is a button bar, or 'shortcut' bar. See the blue section. This will be a collection of
buttons for quick tasks, such as screenshots or image recall. If anyone knows where I can find some nice
free icons - preferably with the MIT license - for the buttons, that would be very helpful. Post in the
comments!</p>
<h3>Plot Display</h3>
<p>This is the primary display area. In the past, this was the only section on the page. Now it will simply
be the area of focus with several other options and information surrounding it. We are going to use the <code>tk.Canvas</code>
class to create a plot. This is the same class that was used previously and, since it is a widget in itself,
it should easily integrate into the larger GUI.</p>
<h3>Status Bar</h3>
<p>This section will be used to display the serial port status, frequency, mode and perhaps other sections as well.</p>
<h2>Scaffolding Code</h2>
<p>Fortunately, tkinter allows us to create this layout fairly easily using frames and sizing them.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">tkinter</span> <span class="kn">as</span> <span class="nn">tk</span>
<span class="k">class</span> <span class="nc">CurveTracer</span><span class="p">(</span><span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">parent</span>
<span class="c1"># configure the menus</span>
<span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
<span class="c1"># create the file menu</span>
<span class="bp">self</span><span class="o">.</span><span class="n">file_menu</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">file_menu</span><span class="o">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Test'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">edit_menu</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">edit_menu</span><span class="o">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Select'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">help_menu</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">help_menu</span><span class="o">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'About'</span><span class="p">)</span>
<span class="c1"># add the menus to the window</span>
<span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="o">.</span><span class="n">add_cascade</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'File'</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">file_menu</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="o">.</span><span class="n">add_cascade</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Edit'</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">edit_menu</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="o">.</span><span class="n">add_cascade</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Help'</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">help_menu</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">config</span><span class="p">(</span><span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">)</span>
<span class="c1"># ----------------------------</span>
<span class="c1"># configure the high-level frames and add them to the root window</span>
<span class="bp">self</span><span class="o">.</span><span class="n">shortcut_frame</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s1">'blue'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">shortcut_frame</span><span class="o">.</span><span class="n">pack</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">plot_frame</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s1">'grey'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">plot_frame</span><span class="o">.</span><span class="n">pack</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status_frame</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s1">'green'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status_frame</span><span class="o">.</span><span class="n">pack</span><span class="p">()</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Tk</span><span class="p">()</span>
<span class="n">root</span><span class="o">.</span><span class="n">title</span><span class="p">(</span><span class="s2">"for(embed) - Curve Tracer"</span><span class="p">)</span>
<span class="n">root</span><span class="o">.</span><span class="n">iconbitmap</span><span class="p">(</span><span class="s1">'forembed.ico'</span><span class="p">)</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">CurveTracer</span><span class="p">(</span><span class="n">root</span><span class="p">)</span>
<span class="n">root</span><span class="o">.</span><span class="n">mainloop</span><span class="p">()</span>
</pre></div>
</td></tr></table>
<h3>Inline Code</h3>
<p>It probably helps to start at the bottom and then go into the <code>CurveTracer</code> class after that. In line 38, we
have a check to ensure that this is the file being run. If it is, then we will execute the remainder
of the lines.</p>
<p>Line 39, <code>root = tk.Tk()</code>, creates our primary window and saves the instance into <code>root</code>. Lines 40
and 41 set the decorations for that window, meaning the title and the window icon.</p>
<p>The next line, <code>app = CurveTracer(root)</code>, simply creates an instance of CurveTracer - which is a
<code>tk.Frame</code>, and saves it into <code>app</code>.</p>
<p>Finally, we run the <code>mainloop()</code> method of <code>root</code>, which is required for all tkinter programs.</p>
<h3>Classed Code</h3>
<p>Next, we will look at the <code>CurveTracer</code> class. Note that the class is a subclass of a <code>tk.Frame</code>.
This means that the CurveTracer class inherits all of the attributes of a <code>tk.Frame</code> right off the bat.
Our strategy is going to be to add the high-level areas to the <code>CurveTracer</code> class that we know that we
will need later. This will make it easier to organize starting out and easier to modify later.</p>
<p>The first think that we want to do is create a menu bar and to flesh out a couple of menus. Most of this
is just for the sake of creating the scaffolding for the application, so we are only going to create
three menus that we anticipate utilizing and will populate each of those with only one submenu.</p>
<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span>
</pre></div>
<p>First, we create the base menu bar and assign it to the 'parent', which is <code>root</code> (remember way down in line
42 when we supplied <code>root</code> as the parameter to <code>CurveTracer</code>?).</p>
<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">file_menu</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">file_menu</span><span class="o">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Test'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">edit_menu</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">edit_menu</span><span class="o">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Select'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">help_menu</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Menu</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">,</span> <span class="n">tearoff</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">help_menu</span><span class="o">.</span><span class="n">add_command</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'About'</span><span class="p">)</span>
</pre></div>
<p>Next, we create each menu - 'File', 'Edit', and 'Help' - as menus. We can then use the <code>add_command</code> to
attach submenus. Normally, the parameters for <code>add_command</code> would contain a <code>command=function_to_call</code>
parameter, but since we are only creating the visual and not the functional elements at this time, we are
going to leave the command blank for the moment. This just means that the menus won't actually do anything.</p>
<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="o">.</span><span class="n">add_cascade</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'File'</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">file_menu</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="o">.</span><span class="n">add_cascade</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Edit'</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">edit_menu</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="o">.</span><span class="n">add_cascade</span><span class="p">(</span><span class="n">label</span><span class="o">=</span><span class="s1">'Help'</span><span class="p">,</span> <span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">help_menu</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">config</span><span class="p">(</span><span class="n">menu</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">menu_bar</span><span class="p">)</span>
</pre></div>
<p>The last things to do with the menus is to add each 'cascade' to the <code>menu_bar</code>, complete with label using
the <code>add_cascade</code> method. Finally, we add the <code>menu_bar</code> itself to the <code>parent</code>, which is the <code>root</code> window.</p>
<p>If you have gotten through the menus, the frames should be relatively easy. We are going to create each new
frame and <code>pack()</code> that frame so that it is displayed on the parent. One thing to note, going forward. Frames
tend to take the size of their contents unless a <code>width</code> and <code>height</code> is specified. Since our frames are
going to be empty, the frames will automatically be of 0 size. We are going to adjust the colors and sizes in
this exercise, but we eventually expect the <code>height</code>, <code>width</code>, and <code>bg</code> attributes to be unecessary.</p>
<div class="highlight"><pre><span></span><span class="bp">self</span><span class="o">.</span><span class="n">shortcut_frame</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s1">'blue'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">shortcut_frame</span><span class="o">.</span><span class="n">pack</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">plot_frame</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s1">'grey'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">plot_frame</span><span class="o">.</span><span class="n">pack</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status_frame</span> <span class="o">=</span> <span class="n">tk</span><span class="o">.</span><span class="n">Frame</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">20</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">400</span><span class="p">,</span> <span class="n">bg</span><span class="o">=</span><span class="s1">'green'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">status_frame</span><span class="o">.</span><span class="n">pack</span><span class="p">()</span>
</pre></div>
<p>We create each frame of a particular size and cold and simply <code>pack()</code> it. By default, the pack attribute
will pack into the current window vertically. </p>
<h1>Why ...</h1>
<h2>... Tkinter?</h2>
<p>Doesn't Tk look bad? Why wouldn't you use something else? Anything else! There is Qt, WX, Kivy, ... why
Tkinter?</p>
<p>Well, the most important thing for me was that tkinter comes bundled with Python 3! When I am finished, I would
like to have a file that I can use a compiler on (yes, there are compilers that will compile Python code) and
I could simply provide a <code>*.exe</code> or similar for those who may not have the ability or desire to do a full Python
install just to get the curve tracer to work. I have done a couple of simple GUI interfaces using tkinter - mainly buttons -
that were able to be compiled and execute on platforms that didn't have Python installed.</p>
<h2>... not matplotlib?</h2>
<p>I like matplotlib. I don't like the install size. Also, I am not sure how well such a library might compile into a
native executable. As a result, I'm staying away from third-party libraries in this project as much as possible
so that the final result is small and portable. Tk is both.</p>
<h2>... are you doing things this way? This is bad software development ...</h2>
<p>I am not developing enterprise software with this effort. What I need is for the interface to work easily and be
usable by me and other electronics enthusiasts with very little setup time or knowledge required to get going. Additionally,
I am completely self-trained and have never worked on a team of software developers that are undergoing similar efforts.
The result is that my exposure to various development methods has been from podcasts and Google.</p>
<p>Having said all of that, I would be more than happy to have contributors join and show me all that I'm doing wrong :)</p>Interrupt Concurrency Issues2016-05-18T20:13:00-04:002016-05-18T20:13:00-04:00Jason Jonestag:www.forembed.com,2016-05-18:/interrupt-concurrency-issues.html<h1>The Symptoms</h1>
<p>I have spent my spare time in the past few days debugging a problem that simply
wouldn't go away. Every few seconds, there would be a random byte that was simply
skipped out of the UART. Fortunately, the checksum was protecting bad data from
getting through, but there …</p><h1>The Symptoms</h1>
<p>I have spent my spare time in the past few days debugging a problem that simply
wouldn't go away. Every few seconds, there would be a random byte that was simply
skipped out of the UART. Fortunately, the checksum was protecting bad data from
getting through, but there were times when more than 10% of the frames were being
corrupted!</p>
<p>What's more, the symptoms weren't very consistent with one another, leading me
down a few rabbit trails. For instance, compiling with a 64 byte UART buffer
was causing problems, but compiling with a 32 byte buffer was not. This lead me
to believe that the issue was related to memory map, which took a while to get
through before I was convinced that it wasn't a memory issue.</p>
<p>The full symptoms are documented in <a href="https://github.com/slightlynybbled/Dispatch/issues/7">Issue 7</a>
and <a href="https://github.com/slightlynybbled/Dispatch/issues/8">Issue 8</a> of
<a href="https://github.com/slightlynybbled/Dispatch">Dispatch</a>.</p>
<h1>The Problem</h1>
<p>After all of that messing around, it turns out that the UART driver had a concurrency
issue with buffer read/write access. This was a total amateur mistake which I should
have caught when writing the UART library, but I didn't.</p>
<p>Lets look at the problem code:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">UART_write</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">){</span>
<span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="n">length</span><span class="p">){</span>
<span class="k">while</span><span class="p">(</span><span class="n">UART_writeable</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> <span class="cm">/* wait for any current writes to clear */</span>
<span class="n">BUF_write8</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">,</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* if the transmit isn't active, then kick-start the</span>
<span class="cm"> * transmit; the interrupt routine will finish sending</span>
<span class="cm"> * the remainder of the buffer */</span>
<span class="k">while</span><span class="p">((</span><span class="n">U1STAbits</span><span class="p">.</span><span class="n">UTXBF</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&&</span> <span class="p">(</span><span class="n">BUF_status</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">)</span> <span class="o">!=</span> <span class="n">BUFFER_EMPTY</span><span class="p">)){</span>
<span class="n">U1TXREG</span> <span class="o">=</span> <span class="n">BUF_read8</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_U1TXInterrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* read the byte(s) to be transmitted from the tx circular</span>
<span class="cm"> * buffer and transmit using the hardware register */</span>
<span class="k">while</span><span class="p">((</span><span class="n">BUF_status</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">)</span> <span class="o">!=</span> <span class="n">BUFFER_EMPTY</span><span class="p">)</span>
<span class="o">&&</span> <span class="p">(</span><span class="n">U1STAbits</span><span class="p">.</span><span class="n">UTXBF</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)){</span>
<span class="n">U1TXREG</span> <span class="o">=</span> <span class="n">BUF_read8</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">U1TXIF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>We have two functions above, <code>UART_write()</code> and <code>_U1TXInterupt()</code>. The purpose of <code>UART_write()</code>
is to load the circular buffer and, if necessary, load the <code>U1TXREG</code> register to get transmit
interrupts started. The purpose of <code>_U1TXInterrupt()</code> is to empty the circular buffer and to
move the contents to the hardware UART transmit register.</p>
<p>For those that are experienced, you may have already spotted the problem. Both of these functions
have full and unblocked access to the same buffer! In other words, In the middle of a <code>UART_write()</code>
function, an interrupt could occur and the interrupt would modify the same variables <em>before</em> the
<code>UART_write()</code> was complete. This is <em>NOT</em> a thread-safe design and must be fixed.</p>
<h1>The Fix</h1>
<p>Fortunately, the fix is simple. We are going to introduce a locking mechanism that limits access
to the variable any time the non-interrupt code is modifying the circular buffer.</p>
<p>The fixed code:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="k">volatile</span> <span class="k">static</span> <span class="kt">uint8_t</span> <span class="n">writeLock</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">UART_write</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">){</span>
<span class="kt">uint32_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="n">length</span><span class="p">){</span>
<span class="k">while</span><span class="p">(</span><span class="n">UART_writeable</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span> <span class="cm">/* wait for any current writes to clear */</span>
<span class="n">writeLock</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">BUF_write8</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">,</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="n">writeLock</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* if the transmit isn't active, then kick-start the</span>
<span class="cm"> * transmit; the interrupt routine will finish sending</span>
<span class="cm"> * the remainder of the buffer */</span>
<span class="n">writeLock</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">while</span><span class="p">((</span><span class="n">U1STAbits</span><span class="p">.</span><span class="n">UTXBF</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&&</span> <span class="p">(</span><span class="n">BUF_status</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">)</span> <span class="o">!=</span> <span class="n">BUFFER_EMPTY</span><span class="p">)){</span>
<span class="n">U1TXREG</span> <span class="o">=</span> <span class="n">BUF_read8</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">writeLock</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_U1TXInterrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">writeLock</span> <span class="o">==</span> <span class="mi">0</span><span class="p">){</span>
<span class="cm">/* read the byte(s) to be transmitted from the tx circular</span>
<span class="cm"> * buffer and transmit using the hardware register */</span>
<span class="k">while</span><span class="p">((</span><span class="n">BUF_status</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">)</span> <span class="o">!=</span> <span class="n">BUFFER_EMPTY</span><span class="p">)</span>
<span class="o">&&</span> <span class="p">(</span><span class="n">U1STAbits</span><span class="p">.</span><span class="n">UTXBF</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)){</span>
<span class="n">U1TXREG</span> <span class="o">=</span> <span class="n">BUF_read8</span><span class="p">((</span><span class="n">Buffer</span><span class="o">*</span><span class="p">)</span><span class="o">&</span><span class="n">txBuf</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">U1TXIF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The <code>writeLock</code> variable is used to provide access to the buffer. Note that only non-interrupt
code will modify <code>writeLock</code> while the interrupt code will only read <code>writeLock</code>. Also note that
the writeLock is active for the shortest period of time possible, which is just before and just
after the buffer accesses.</p>
<p>This case has one additional wrinkle. The interrupt is the primary method of getting bytes to the
transmit register, but we have written the <code>writeLock</code> in such a way that the entire <code>_U1TXInterrupt()</code>
is basically skipped if <code>writeLock</code> is active... how do we know that everything will be transmitted?
The first <code>writeLock</code> occurs at lines 9-11, which is where the bulk of the parameter array is
shifted into the circular buffer. This is the likely place for a collision between the two buffer
accesses. If you look to lines 19-23, we have an additional load of the U1TXREG <em>if</em> the transmit
registers are empty and the circular buffer is not empty. In this way, we ensure that the process
continues even if an interrupt was skipped during the writeLock event.</p>
<h1>Summing Up</h1>
<p>It is not necessary to make all of your code thread-safe, but it is absolutely critical to make
any variables that are modified by both interrupt and main-line code thread-safe. If you have random
problems that don't seem very consistent, this is an area to focus on.</p>
<p>There are books written about this issue and the approach that I took to solve the problem - though
valid - is not very sophisticated. For more sophisticated methods, you might want to utilize an
RTOS or other similar architectures that support semaphores and limited access.</p>RSS and Atom Feeds!2016-05-09T14:33:00-04:002016-05-09T14:33:00-04:00Jason Jonestag:www.forembed.com,2016-05-09:/rss-and-atom-feeds.html<p><img src="/images/logos/rss.png" alt="RSS feed icon" class="img-responsive img-rounded"></p>
<h1>We have Added Feeds!</h1>
<p>If you look to the right on the sidebar, you will see that we have added RSS and Atom feeds!
This wasn't a critical update, but it was something that I have wanted to do for a bit. In
truth, I hadn't used feeds myself, so …</p><p><img src="/images/logos/rss.png" alt="RSS feed icon" class="img-responsive img-rounded"></p>
<h1>We have Added Feeds!</h1>
<p>If you look to the right on the sidebar, you will see that we have added RSS and Atom feeds!
This wasn't a critical update, but it was something that I have wanted to do for a bit. In
truth, I hadn't used feeds myself, so it took me a bit to even understand the reason to publish
a feed. After I realized what it was, then I decided that I wanted to create a feed.</p>
<p>As it happens, I am currently reading enough blogs that this sort of item on each blog would
help me sort through the old vs. new quite well, so I am happy to add to the potential productivity
boost for you guys as well.</p>
<h1>Other Changes</h1>
<h2>Disques Comments</h2>
<p>Each article now has a 'comments' section powered by <a href="https://disqus.com/">disqus</a>. I really like
having a comments section, but my readership is small enough that this probably won't matter for
the near future.</p>
<h2>Sidebar</h2>
<p>I have also been doing some other 'look and feel' changes to the blog. Some advice from a friend
suggested that I should alter the 'Profiles' on the sidebar to not be so prominent. Additionally,
I have altered the tag cloud so that tags with only one instance are not shown. I have also badged
each tag cloud item with a numeric indicator so that the user has an idea of what he is getting
into with each tag.</p>
<h2>Index Page</h2>
<p>Based on the advice from said friend, I also placed the 'summary' of each article inside the well
in order to better group the front-page articles. Hopefully, this will make it a bit easier for you
to see the separate content on each page.</p>
<h2>Navigation Bar</h2>
<p>Finally, I added some functionality to the navigation bar at the top so that the pages collapse when
the screen narrows. This functionality isn't perfect just yet, but should be enough so that the page
doesn't look too strange on phone and tablet-sized devices.</p>
<h1>Future</h1>
<p>In the future, I suspect that I will can the 'about' page and place all of that information on the
index page. Ultimately, the site is about projects and how they were done and the fewer clicks that
it takes a person to get from one place to another the better. I may also start adding 'breadcrumbs'
to show a person where they are in the hierarchy of the site. There is always more to do!</p>Project Curve Tracer - Another Layout2016-05-05T15:20:00-04:002016-05-05T15:20:00-04:00Jason Jonestag:www.forembed.com,2016-05-05:/project-curve-tracer-another-layout.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-top.png" alt="Top board render">
<img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-bottom.png" alt="Bottom board render"></p>
<p>As you can see, we have another layout completed and ordered from the guys
at OSH park. This time around, we took some time to place some nice mounting
holes and add rounded corners. We will be looking forward to assembling
this board!</p>
<p>In the mean time, we will be …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-top.png" alt="Top board render">
<img class="img-responsive" src="http://www.forembed.com/images/2016/05/curve-tracer-bottom.png" alt="Bottom board render"></p>
<p>As you can see, we have another layout completed and ordered from the guys
at OSH park. This time around, we took some time to place some nice mounting
holes and add rounded corners. We will be looking forward to assembling
this board!</p>
<p>In the mean time, we will be continuing to develop the software so that,
once the boards get here, we can continue with a minimum of fuss. Fortunately,
there is not much additional work to perform in order to get the boards working
on the new layout. With all of the extra room, test points, and signal
conditioning, we expect that the next layout will be easier to debug and
perform at a higher level.</p>
<p><img class="img-responsive img-rounded" src="http://www.forembed.com/images/2016/05/curve-tracer-top-kicad.png" alt="Top, KiCad"></p>
<p>As always, full source files can be found on the <a href="https://github.com/slightlynybbled/project_curve_tracer">github page</a>.</p>Project Curve Tracer - Next Revision2016-05-02T10:59:00-04:002016-05-02T10:59:00-04:00Jason Jonestag:www.forembed.com,2016-05-02:/project-curve-tracer-next-revision.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/recycle_schem.png" alt="Recycle Schematic"></p>
<h1>Project Status</h1>
<p>As you have seen with a few screenshots, we have made significant progress since the first post.
We have completed a schematic, layout, rework, and created a basic code framework. We have
proven that the hardware mostly works as-is, but would like to make some improvements. In this …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/05/recycle_schem.png" alt="Recycle Schematic"></p>
<h1>Project Status</h1>
<p>As you have seen with a few screenshots, we have made significant progress since the first post.
We have completed a schematic, layout, rework, and created a basic code framework. We have
proven that the hardware mostly works as-is, but would like to make some improvements. In this
post, we are stepping back from the software for a moment and heading back into hardware land
to address some of our concerns.</p>
<h1>Temptation</h1>
<p>After getting this far, I was actually tempted to change microcontroller series for something a
bit faster and more integrated. There are definitely processors out there - like the STM32F
series - that would be perfect for this application (high-resolution DAC, built-in USB, etc.).
I feel that I would lose some value to what work has been performed thus far and that the PIC24
series is adequate with some modifications, so I will complete this project with the PIC24
series and leave other processors for another day or another requirement.</p>
<h1>Communications</h1>
<p>The FT230X has been great. I went through a
<a href="http://www.forembed.com/project-curve-tracer-configuring-the-uart.html">bit of setup</a>,
but I haven't even thought about the part since then. It has been seemless. I will leave the
schematic for it and the USB connector itself alone.</p>
<h1>PIC24 Part Confusion</h1>
<p>In one of the <a href="http://www.forembed.com/project-curve-tracer-dac-initialization-and-utilization.html">first posts</a>,
we documented a couple of items:</p>
<ol>
<li>We should be using the PIC24FJ part, which requires a capacitor on pin 20 and cannot
be assigned to a pin function</li>
<li>We messed up the programming header pin numbers-to-labels associations</li>
</ol>
<p>Neither of these required major changes to the board and the reworks were fairly straightforward.
We definitely want to correct this on the next schematic..</p>
<h1>Analog</h1>
<p>On the analog side, we would like to make several changes. The first is that we want to use all
external amplifiers. I thought that I was being super clever using the internal opamps, and I still
believe that this would be the best solution, but the outputs of the internal opamps are not over-current
protected. This is the exact function that I require for this application, so I'm feeling forced
to use better external parts. This is unfortunate since it was a large part of the reason that
I had chosen this particular micro-controller, but such is life. Moving to an external part should
allow us to to a few helpful things on the analog side:</p>
<ol>
<li>Eliminate the current-limiting resistor</li>
<li>Reduce the value of the current-sensing resistor</li>
<li>Filter the DAC with an RC, which will help smooth the sine waveform</li>
<li>Further filter and buffer the current-sense reference output as the PWM source could be contributing
to the <a href="http://www.forembed.com/images/2016/04/curve-tracer-220n-480.png">small ripple that we are seeing on the current of the VI plots</a>.</li>
</ol>
<p>In addition to these goals, we also noted a small deficiency in the current-sensing amplifier. It appeared
that - at open leads - there was a small sinusoidal waveform shown on the current sensing output. This is,
I suspect, a result of a too-low value for common-mode rejection ratio (CMRR). The datasheet states that
the MCP6001 has a typical CMRR of 72dB at DC, but looking at the CMRR vs. Frequency chart, a 1k frequency
has a CMRR of ~40dB. We would like a bit more.</p>
<p><img src="/images/2016/05/cmrr-mcp6004.png" alt="CMRR" class="img-responsive img-rounded"></p>
<p>The <a href="http://ww1.microchip.com/downloads/en/DeviceDoc/22140b.pdf">MCP6L04</a> appears to be a nice compromise
between price and features. In truth, it is a very similar part to the MCP6004, just slightly better all
around. It wouldn't surprise me if these two parts came off of the same design and were simply sorted
into the proper part numbers.</p>
<p><img src="/images/2016/05/cmrr-mcp6l04.png" alt="CMRR" class="img-responsive img-rounded"></p>
<h1>Physical</h1>
<p>It would be really nice if the physical interface were a bit more user-friendly. On the first round, we actually
ignored this completely, knowing that getting a board in a reasonable amount of time for software development
and discovering the bugs that we had put in place would be worth the sacrifice. This time around, we want to
think about how the user might really want to interface with the board.</p>
<h2>Test Leads</h2>
<p>The typical curve-tracer is equipped with test lead jacks. This is a low-voltage device, so safety jacks aren't
necessary, but it certainly would be nice if your existing multimeter leads were compatible. Most multimeters
use standard 4mm 'safety' jacks. This would be great, but the majority of these jacks are of the 'panel mount'
type and are not at all easy to place on a board-only assembly. So for the moment, I will leave the screw
terminals on the board. In the future, if we box this project up, then we will likely put a large safety jack
on the front so that we can use standard multimeter leads.</p>
<h2>Three-Terminal Device Testing</h2>
<p>There still exists the need - although not the software - to test 3-lead components such as transistors and MOSFETs.
When we last visited this, I chose to place a 6-pin socket on the board since 3-pin sockets appeared to be so much
more expensive. I still believe this to be the right call as we can always toss a 3-pin socket or other similar
device in its place.</p>
<h1>Test Points</h1>
<p>Simply overlooked on the last round was a set of test points. As a result, there were several items
that were simply soldered on to the pins directly. This works, but isn't very user friendly. This time
around, we will spend a bit more time on test points. Places for test points:</p>
<ol>
<li>Ground</li>
<li>Some micro-controller pins</li>
<li>Current-sense output</li>
<li>Current-sense reference</li>
<li>Low-side output voltage</li>
</ol>
<h1>Mounting Considerations</h1>
<p>Again, it would be great to place this inside an enclosure of some sort. Since this is going to be for guys like
me - people who like to see the board components - I am opting to use one of the
<a href="http://dangerousprototypes.com/docs/Sick_of_Beige_compatible_cases">Sick of Beige</a> footprints. These cases are
clean looking and cheap. Since our board is a bit tight for the 50x31mm case, we will target the 60x37mm footprint
for our next layout.</p>
<h1>Wrapping Up</h1>
<p>I won't go through the block-by-block like <a href="http://www.forembed.com/project-curve-tracer-schematic-creation.html">last time</a>
since the target hasn't really changed very much. I will upload the new schematic and layout files to
<a href="https://github.com/slightlynybbled/project_curve_tracer/tree/master/electronics">github</a> for your viewing pleasure.</p>
<p>As a final note, take a look at the most recent version of the curve tracer screenshot:</p>
<p><img src="/images/2016/05/curve-tracer-screenshot.png" alt="Curve tracer screenshot" class="img-responsive img-rounded"></p>
<p>I had wanted to make it look like the old familiar green and grey of the huntron tracker 2000, but I got the idea to
color-code each segment around and I really liked the way it highlighted the start and end of the waveform.</p>Project Curve Tracer - Capacitor Shots2016-04-29T08:47:00-04:002016-04-29T08:47:00-04:00Jason Jonestag:www.forembed.com,2016-04-29:/project-curve-tracer-capacitor-shots.html<h1>Squashing Bugs</h1>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/vi-vs-freq-capacitor.png" alt="VI Curve - capacitor" align="right"></p>
<p>As you recall from the
<a href="http://www.forembed.com/first-vi-plots.html">last post</a>,
we were able to get a few screen captures of a few components under test, but the pictures
were a bit rough. Today, we still have rough pictures, but they are a lot better due
to a little effort in reducing …</p><h1>Squashing Bugs</h1>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/vi-vs-freq-capacitor.png" alt="VI Curve - capacitor" align="right"></p>
<p>As you recall from the
<a href="http://www.forembed.com/first-vi-plots.html">last post</a>,
we were able to get a few screen captures of a few components under test, but the pictures
were a bit rough. Today, we still have rough pictures, but they are a lot better due
to a little effort in reducing bugs and sampling intelligently.</p>
<p>Some of the bugs were found within <a href="http://www.forembed.com/introducing-dispatch.html">Dispatch</a>...
which deserves a post of its own, but each was dealt with during a major refactor and testing
was added to ensure that those bugs don't creep in again at a later time.</p>
<h1>Capacitive Reactance</h1>
<p>Today we were able to see some capacitive reactance as we changed the frequency. Note that
the screenshots last time were all done at a particular frequency. Now, we chose a 0.22uF
capacitor and varied the frequency. We can see the reactance go from nearly open-circuit to
nearly short circuit.</p>
<p>We are adjusting the frequency by adjusting 'omega', which is the change in theta over time.
As omega increases, so does frequency.</p>
<hr>
<h2>Capacitor, 0.22uF</h2>
<h3>Omega = 60, 11Hz</h3>
<p>There is only the smallest deviation from 0mA. As each vertical division is 1.11mA, we are looking
at much less than 1mA.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-220n-60.png" alt="11Hz trace"></p>
<h3>Omega = 240, 44Hz</h3>
<p>As we increase the frequency, we can see the ellipse beginning to emerge. Due to some hardware
choices made earlier in the project, one can see a fair amount of noise on the trace. We can
eliminate this in the next round.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-220n-240.png" alt="44Hz trace"></p>
<h3>Omega = 480, 88Hz</h3>
<p>Now we can clearly see a horizontal ellipse forming. The peak currents are ~1.1mA and the voltage is
still able to traverse the entire +/-5V range.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-220n-480.png" alt="88Hz trace"></p>
<h3>Omega = 1024, 188Hz</h3>
<p>Now we have an open 'eye' which is about as close to an ellipse as we have available to us.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-220n-1024.png" alt="188Hz trace"></p>
<h3>Omega = 2048, 376Hz</h3>
<p>And finally, we see the first signs of horizontal clipping. This is where the board can supply no more
current and the voltage isn't making it to the ends of the x-axis.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-220n-2048.png" alt="376Hz trace"></p>
<hr>
<h2>Capacitor, 4.7uF</h2>
<p>We expect the 4.7uF to behaving similarly to the 0.22uF, but we should see the behaviors at lower
frequencies. On this series, we are going to check a few more 'in-between' frequencies so that we can
see what the component looks like throughout the cycle.</p>
<h3>Omega = 60, 11Hz</h3>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-60.png" alt="11Hz trace"></p>
<h3>Omega = 100, 18Hz</h3>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-100.png" alt="18Hz trace"></p>
<h3>Omega = 120, 29Hz</h3>
<p>We are seeing the beginnings of the short circuit on the left and right edges of the trace. This
trend will continue as frequency increases.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-120.png" alt="29Hz trace"></p>
<h3>Omega = 160, 11Hz</h3>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-160.png" alt="11Hz trace"></p>
<h3>Omega = 240, 44Hz</h3>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-240.png" alt="44Hz trace"></p>
<h3>Omega = 2048, 376Hz</h3>
<p>Note that there is still a narrow opening, but that if we continue to increase the frequency, the
opening would continue to close until it was indistinguishable from a dead short.</p>
<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/curve-tracer-4u7-2048.png" alt="376Hz trace"></p>
<hr>
<h1>What did we Learn ?</h1>
<p>As our capacitors are not making true ellipses, particularly at lower frequencies, the 200Ω of
resistance in series with the path probably has something to do with it. It would be more ideal to
directly connect the opamp output to the device; however, Microchip warned me that there was no
current limiting protection on the internal opamps. That leaves me with having to limit the current
in other ways. I chose a resistor. The next layout will likely have a dedicated set of opamps for
the tasks.</p>
<p>We know that the Dispatch library is working well and that we can change the frequency on the fly by
sending the omega to the <code>omega</code> topic via the serial port. It appears that we are at a point at which
we should cut the next round of hardware. We can do more firmware development while waiting for hardware.
Aside from a few reworks here and there, the harware had definitely proven that it has the base
capability to perform the core operations of the curve tracer, but we have found some shortcomings
of the hardware along the way that we would like to address. The next post in the series will highlight
all of the changes that we wish to make to the form, fit, and function of the unit. After that, we
will have the next boards on the way.</p>
<p>Stay tuned!</p>Project Curve Tracer - First VI Plots!2016-04-23T11:19:00-04:002016-04-23T11:19:00-04:00Jason Jonestag:www.forembed.com,2016-04-23:/first-vi-plots.html<h1>The First Shots are In!</h1>
<p>After a few weeks of messing around on-and-off with the curve tracer, we finally have
some shots to show that the work has not been for nothing!</p>
<h1>The Tool</h1>
<p>I took the time to write a small TkInter-based plotting tool. I found a couple of …</p><h1>The First Shots are In!</h1>
<p>After a few weeks of messing around on-and-off with the curve tracer, we finally have
some shots to show that the work has not been for nothing!</p>
<h1>The Tool</h1>
<p>I took the time to write a small TkInter-based plotting tool. I found a couple of
examples out there, but nothing that seemed to fit the bill. I thought of using a
framework like PyQt or matplotlib, but I wanted to ensure that the final package
was completely compileable and in Python3, so I rolled my own.</p>
<p>Admittedly, the plot tool isn't very good right now, but I suspect that it will
get better. </p>
<h1>The Screenshots</h1>
<p>The plot consists of a grid of dotted lines and solid lines. The solid lines are
the '0' of each axis. On the horizontal axis, the scale goes from -5V to 5V. Each
dotted crossing represents 1V. On the vertical axis, the scale is:</p>
<p><span class="math">\(100\Omega \frac{24k\Omega}{2.67k\Omega}\)</span>
<span class="math">\(= 100\frac{V}{A} \frac{24}{2.67}\)</span>
<span class="math">\(= 899\frac{V}{A}\)</span></p>
<p>The vertical division is also in Volts, but converting that to current means that the
vertical divisions are:</p>
<p><span class="math">\(\frac{1}{899\frac{V}{A}}\)</span>
<span class="math">\(= 1.11\frac{mA}{V}\)</span></p>
<p>So the vertical divisions represent 1.11mA each, for a full-scale of 5.55mA. Our original
intent was to have the gain be "10" rather than "8.99", but you may recall that we
<a href="http://www.forembed.com/project-curve-tracer-setting-up-pwm-as-a-dac.html">didn't have any 10kΩ resistors in the SMT package</a>
and changed the ratio slightly. This will be corrected in the next schematic/layout session.</p>
<h2>Open Circuit</h2>
<p>The first picture is the standard 'open circuit'. The voltage traverses the entire scale while the current
remains at 0A.</p>
<p><img src="/images/2016/04/curve-tracer-open.png" alt="open circuit" class="img-responsive img-rounded"></p>
<h2>Short Circuit</h2>
<p>We connected the leads together and expect that the voltage will be very small and the current will
be traverse the vertical range...</p>
<p><img src="/images/2016/04/curve-tracer-short.png" alt="short circuit" class="img-responsive img-rounded"></p>
<p>Ok, so the dots are off-putting. I will work on the presentation, but it is working and that is what counts.</p>
<h2>Resistor, 100Ω</h2>
<p>Next, we try a 100Ω resistor. We expect a 'high' current to flow, but there should be some voltage applied.
Note that Ohm's Law is a linear equation, meaning that an ohm of resistance implies a Volts/Amp relationship.
Our curve tracer should reflect that since we are looking directly at the Volt-Amp relationship on a resistor.
We are actually seeing Ohm's Law in action!</p>
<p><img alt="100&Omega; resistor" src="http://www.forembed.com/images/2016/04/curve-tracer-resistor-100ohm.png"></p>
<p>There is a steep slope, but there is definitely a 'lean' to it that wasn't there on a short circuit. Success!</p>
<h2>Resistor, 1kΩ</h2>
<p>The 1kΩ resistor should have a much lower slope than the 100Ω resistor...</p>
<p><img alt="1k&Omega; resistor" src="http://www.forembed.com/images/2016/04/curve-tracer-resistor-1kohm.png"></p>
<p>Just as predicted! It seems that things are working.</p>
<h2>Capacitor, 10μF</h2>
<p>Now, lets introduce a bit of reactance into the equation by throwing a capacitor in the mix. With a capacitor,
the current leads the voltage, so we expect an ellipse.</p>
<p><img alt="10&mu;F capacitor" src="http://www.forembed.com/images/2016/04/curve-tracer-capacitor-10uF.png"></p>
<p>Well, it isn't a perfect ellipse, but - at this point - we know that the concepts are serving us well.</p>
<h2>Diode</h2>
<p>Finally, one of the most iconic VI curves is that of the diode, which is "open" in one polarity and "short" in
the other polarity. We have an <a href="http://www.onsemi.com/pub_link/Collateral/MBR1100-D.PDF">MBR1100</a>, which is a
Schottky diode.</p>
<p><img src="/images/2016/04/curve-tracer-diode.png" alt="diode" class="img-responsive img-rounded"></p>
<p>Perfect!</p>
<h1>Next Steps</h1>
<p>During this testing, we found a glitch somewhere in the communications channel. We will get all of the tested
code from <a href="http://www.forembed.com/introducing-dispatch.html">Dispatch</a> working rather than using the
implementation that was ad-hoc written here. We will also attempt to firm up the Python library in the process.
After that, some solid work on the visuallization and data rate to get more points would probably be in order.</p>
<p>After all of the posts, it is nice to see it coming together!</p>
<script type="text/javascript">if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Introducing Dispatch2016-04-17T19:15:00-04:002016-04-17T19:15:00-04:00Jason Jonestag:www.forembed.com,2016-04-17:/introducing-dispatch.html<p><img class="img-responsive" src="http://www.forembed.com/images/logos/dispatch.png" alt="Dispatch Logo" align="right"></p>
<h1>Intro</h1>
<p>Dispatch is a library designed to operate on any byte-aligned communication channel. Dispatch
has 'endpoints' or 'topics' to which data is written and 'subscriber' or 'consumer' functions
to which the data is sent. At this time, Dispatch is primarily written in C, although a Python
port does exist in …</p><p><img class="img-responsive" src="http://www.forembed.com/images/logos/dispatch.png" alt="Dispatch Logo" align="right"></p>
<h1>Intro</h1>
<p>Dispatch is a library designed to operate on any byte-aligned communication channel. Dispatch
has 'endpoints' or 'topics' to which data is written and 'subscriber' or 'consumer' functions
to which the data is sent. At this time, Dispatch is primarily written in C, although a Python
port does exist in the testing phase. Dispatch is written to operate in a large number of
environments and should be able to compile on any platform.</p>
<h1>The Name</h1>
<p>I have been listening to an <a href="http://civilwarpodcast.org/">American Civil War podcast</a> and I am
a bit fascinated with the tremendous influence that communications tend to have on the outcomes
of some engagements. The messengers between units were 'dispatched' and pertinent messages went
to a specific individual. I decided to name the library 'dispatch' because that metaphor very
nearly models the way Dispatch works. A message is sent out to a certain subscriber. If there
is no subscriber, the message is simply lost.</p>
<h1>History</h1>
<p>Some time ago, we began work on the
<a href="http://www.forembed.com/project-curve-tracer-requirements.html">curve tracer project</a>
knowning that - at some point - we would be implementing a serial library of some sort. At
the time, I thought that I would just find something laying around on github that fulfilled
the project requirements without writing a library from scratch.</p>
<p>I ran across the <a href="https://github.com/Overdrivr/Telemetry">Telemetry</a> library - which is a great
library - but it didn't quite meet the potential data rate that I was looking for. After some
attempts to patch the capability to send arrays through the Telemetry, I decided that the effort
required would vastly modify the library itself and that Remi - the author of Telemetry - would
likely not accept the massive changes to the base level protocol.</p>
<p>I took inspriation from the simplicity and elegance of Telemetry and created the first crude
version of Dispatch on a PIC24 processor for the curve tracer. After several successful tests,
I have concluded that the serial library is worrh making a project of its own and I decided to
call it Dispatch.</p>
<h1>First Posts</h1>
<p>As this began as part of another project, there is some history that isn't part of this project.
Before moving to the <a href="https://github.com/slightlynybbled/Dispatch">Dispatch repository</a>, it may
be helpful to read the following posts from the curve tracer project:</p>
<ul>
<li><a href="http://www.forembed.com/project-curve-tracer-framing-serial-data.html">Framing Serial Data</a></li>
<li><a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">Serial Protocol Part 1: How to Publish Data</a></li>
<li><a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-2.html">Serial Protocol Part 2: Converting the Published Data To Byte Streams</a></li>
<li><a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-3.html">Serial Protocol Part 3: Subscribing to a Topic</a></li>
</ul>
<p>Although the function naming conventions will be a bit different, the concepts for understanding
the library concepts and syntax are laid out quite clearly in those posts.</p>
<h1>Project Maturity</h1>
<p><img src="https://travis-ci.org/slightlynybbled/Dispatch.svg?branch=master" alt="travisci" class="img-responsive img-rounded"></p>
<p>The project is not mature and is mostly untested! I have set up a
<a href="https://travis-ci.org/">TravisCI</a> test, so any new commits will be able to have the testing
run on them immediately. Right now, there is only about 15% test coverage, so don't use this
in your new project unless you want to do some debugging!</p>
<h1>Future</h1>
<p>At the time of this writing, the
<a href="https://github.com/slightlynybbled/Dispatch">Dispatch github repository</a> contains all of the files
that are required to use and build, but there isn't much in the way of instructions or other
guidance. I am working on implementing unit tests and, once those are complete, I will also
attempt to optimize for RAM and/or speed.</p>
<p>Thanks for checking in!</p>Bootstrap 3 Theme2016-04-17T12:14:00-04:002016-04-17T12:14:00-04:00Jason Jonestag:www.forembed.com,2016-04-17:/bootstrap-3-theme.html<p>Ok, so I realize that <a href="http://getbootstrap.com/">Bootstrap</a> is going on version 4, but
I decided to update from Bootstrap2 to Bootstrap3 anyway.</p>
<p>I wanted to do this for three reasons:</p>
<ol>
<li>To learn a bit more about the Jinja2 templating engine</li>
<li>To learn a bit more about Pelican and the access to …</li></ol><p>Ok, so I realize that <a href="http://getbootstrap.com/">Bootstrap</a> is going on version 4, but
I decided to update from Bootstrap2 to Bootstrap3 anyway.</p>
<p>I wanted to do this for three reasons:</p>
<ol>
<li>To learn a bit more about the Jinja2 templating engine</li>
<li>To learn a bit more about Pelican and the access to variables</li>
<li>Fun!</li>
</ol>
<p>I had been patching the default bootstrap2 theme that is 'stock' with Pelican for a
while now. As it happens, the theme didn't work out-of-the-box since Pelican itself
has moved ahead of most of the stock themes. That patching process is what gave me the
bug to create my own theme. I must admit, it is heavily influenced by the stock theme,
just updated a bit to add my particular tastes.</p>
<h1>Impressions</h1>
<h2>Boostrap</h2>
<p>Overall, I'm quite impressed with Bootstrap. it is easy to pick up and use as-is. For
anyone learning out there, I highly suggest doing a quick perusal through the
<a href="http://www.w3schools.com/bootstrap/">w3schools tutorial</a>
and the
<a href="http://www.tutorialrepublic.com/twitter-bootstrap-tutorial/">tutorial republic tutorial</a>.</p>
<p>While you are going through the tutorials, don't try to memorize any code or anything
like that, but focus on what types of items are available. For instance, I was particularly
impressed with the 'wells' like the sidebar is in.</p>
<p>There is one section that you should attempt to become quite familiar with and that is the
grid and layout system. If you build your site heirarchically, then your layout will be
in your 'base' template and you won't ever have to worry about the layout on any other page.</p>
<h2>Jinja2</h2>
<p>I have played with Jinja2 in the past and I am continually impressed with its capabilities.
I suspect that any html work that I do will be using a templating engine and I like the
Jinja2 syntax and capabilities, so I will likely stay in this space whenever possible.</p>
<h2>Pelican</h2>
<p>One shortcoming that I find with Pelican is the inability to place a class on all common
elements generated from a markdown file. For instance, to apply a bootstrap class to a
table, I have to create the table in html. This isn't unworkable, it just means that I
can't type the pretty markdown tables. Same issue with images.</p>
<h1>Future</h1>
<p>Future work on the site will probably involve adding a comments section. I feel that this
is a serious shortcoming on a technical blog. I will likely use <a href="https://disqus.com/">disqus</a>
or something similar for the sake of simplicy.</p>
<p>When Bootstrap4 becomes mainstream (it is in beta now), I will likely move over to it as well.
I don't feel that I need any new capability, but the process of using the more recentl libs
will keep me up-to-date.</p>Project Curve Tracer - Serial Protocol Part 32016-04-11T05:54:00-04:002016-04-11T05:54:00-04:00Jason Jonestag:www.forembed.com,2016-04-11:/project-curve-tracer-serial-prototol-part-3.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/mail.png" alt="mail"></p>
<p>So, the graphic above looks a lot like something from the 80's. I wanted to put something
up since this page was going to be nearly all text, so there ya go.</p>
<p>In <a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">Part 1</a>
we described the 'vision' of the protocol that we wish to use, placing a lot …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/mail.png" alt="mail"></p>
<p>So, the graphic above looks a lot like something from the 80's. I wanted to put something
up since this page was going to be nearly all text, so there ya go.</p>
<p>In <a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">Part 1</a>
we described the 'vision' of the protocol that we wish to use, placing a lot of emphasis on
the <code>publish</code> methods.</p>
<p>In <a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">Part 2</a>
we described how that vision translates into a byte stream.</p>
<p>One item that we have neglected is actually consuming the data. For data consumption, we are
going to use a 'subscriber' function and a callback function.</p>
<h1>Subscribing to a Topic</h1>
<h2>The Simple Approach</h2>
<p>We must first 'subscribe' to a topic using the 'subscribe' function. Below is the simplest
complete subscription that we can execute:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriber</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span> <span class="c1">// declare your subscriber function</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">subscribe</span><span class="p">(</span><span class="s">"my topic"</span><span class="p">,</span> <span class="o">&</span><span class="n">mySubscriber</span><span class="p">);</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">){</span>
<span class="n">process</span><span class="p">();</span> <span class="c1">// must be called continually for subscriptions to work</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">mySubscriber</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Going through the above lines, first we must declare our callback function just as we declare
any other function. In main, we subscribe to the topic "my topic" using the <code>subscribe</code>
function and, as a parameter, the callback function.</p>
<p>The <code>process</code> function must be called continually as it takes care of calling the subscriber
when the topic is received. When the topic "my_topic" is received then <code>mySubscriber</code> is called.
In this case, <code>mySubscriber</code> will simply increment a variable. That is all there is to
subscribing! In its current form, <code>mySubscriber</code> isn't very helpful. It might be useful for
toggling a pin on receipt of a particular topic, but not much more than that. We want the
subscriber to consume actual data. For that, we must use the <code>getElements</code> function.</p>
<h2>The Slightly More Complex Approach</h2>
<p>I like the use the <a href="https://github.com/slightlynybbled/embedded_task_manager">task manager</a> for
items that need to be executed repeatedly, such as <code>process()</code>. For this project, I would make
a small alteration to the above code:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriber</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span> <span class="c1">// declare your subscriber function</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">TASK_init</span><span class="p">();</span>
<span class="n">subscribe</span><span class="p">(</span><span class="s">"my topic"</span><span class="p">,</span> <span class="o">&</span><span class="n">mySubscriber</span><span class="p">);</span>
<span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">process</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span> <span class="c1">// execute 'process()' every 10ms</span>
<span class="n">TASK_manage</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">mySubscriber</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>This has the same effect as the previous code block with two exceptions:</p>
<ol>
<li>I can add other tasks with several different timings. Each of these will appear to execute
in parallel so long as each doesn't unduly tax the processor.</li>
<li>Task timing is much more deterministic - if a function needs to execute every 10ms, it simply
executes when it is ready to execute.</li>
</ol>
<p>I generally implement a task manager early in each project so that I can use it to its greatest
advantage. In many cases, the task manager can take the place of several timer interrupts without
dedicating hardware timers to each or having to create a separate global variable to keep track
of different time intervals.</p>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- center-article -->
<p><ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-9588877918195509"
data-ad-slot="8072568671"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></p>
<h1>Retrieving Data</h1>
<p>A subscriber retrieves data using <code>getElements</code>:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">mySubscriber</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* declare a place for the data to go */</span>
<span class="kt">uint16_t</span> <span class="n">myData0</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
<span class="kt">int16_t</span> <span class="n">myData1</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
<span class="kt">uint16_t</span> <span class="n">dataLength</span><span class="p">;</span>
<span class="n">dataLength</span> <span class="o">=</span> <span class="n">getElements</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">myData0</span><span class="p">);</span> <span class="c1">// element(s)0 into myData0[]</span>
<span class="n">getElements</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">myData1</span><span class="p">);</span> <span class="c1">// element(s)1 into myData1[]</span>
<span class="p">}</span>
</pre></div>
<p>In the above sequence, a two-dimensional dataset with up to 10 elements each was retrieved
and saved into <code>myData0</code> and <code>myData1</code>. Recall from previous posts that the array length is
the same for both, which is why <code>dataLength</code> was only retrieved on the first call to
<code>getElements</code>.</p>
<p>The arrays MUST be long enough to hold the data as there is no protection from overflowing
the data destination array. Additionally, the arrays MUST be of the proper type. If the
publisher of the data is sending <code>int32</code> data and the subscriber is retrieving <code>int16</code> data,
then the data retrieved by the <code>getElements</code> will be garbage.</p>
<h1>Notes</h1>
<p>There are no program-imposed limitations on what functions can use the <code>getElements</code>
function; however, the function returned data is <em>only</em> valid within a subscriber. If
<code>getElements</code> is called outside a subscriber, it will only return garbage.</p>
<p>As mentioned before, the data format must exactly match between publisher and subscriber.
If the formats do not match, then garbage will be returned!</p>
<h1>Future of the Protocol</h1>
<p>At this time, the protocol is being developed as part of the curve tracer project. I believe
that it is quite useful and worth moving into its own repository. I will create similar
functionality using Python in order to complete the communications loop between the curve
tracer and the PC. Python will also enable the data visualization.</p>
<p>For source code for the protocol as it exists, visit the
<a href="https://github.com/slightlynybbled/project_curve_tracer">github</a> repository.</p>Project Curve Tracer - Serial Protocol Part 22016-04-08T08:47:00-04:002016-04-08T08:47:00-04:00Jason Jonestag:www.forembed.com,2016-04-08:/project-curve-tracer-serial-prototol-part-2.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/dispatch_icon.jpg" alt="truck icon"></p>
<h1>Before we Get Started...</h1>
<p>After conferring with the author of the <a href="https://github.com/Overdrivr/Telemetry">Telemetry</a> library,
I am convinced that it will not meet my requirements at this time. I am currently writing this
library as part of the curve tracer project, but I feel that it would be best to publish it …</p><p><img class="img-responsive" src="http://www.forembed.com/images/2016/04/dispatch_icon.jpg" alt="truck icon"></p>
<h1>Before we Get Started...</h1>
<p>After conferring with the author of the <a href="https://github.com/Overdrivr/Telemetry">Telemetry</a> library,
I am convinced that it will not meet my requirements at this time. I am currently writing this
library as part of the curve tracer project, but I feel that it would be best to publish it to the
community as its own project. I will fork the project as soon as it fits the needs of this project
and is mature enough to stand on its own. Before forking the project, I do feel that I should come
up with some project name. I'm thinking something like "Dispatch" or something along those lines.</p>
<p>In the <a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">last post</a>,
we discussed some ways that we would like to interface with the serial library. In this post, we
would like to translate some of those interfaces into bytes. We are going to go through most of the
publish examples from the last post and translate that into fields, then decompose each field into
bytes. We will refrain from sending negative and large numbers so that the user can visually see
the translation from fields into bytes.</p>
<p>In all cases, the left-most field or byte is transmitted first.</p>
<h1>The Format</h1>
<p>The 'publish' function must have some translation to bits within a frame. Our overall organization of the frame will be:</p>
<table class="table table-bordered table-striped">
<tr>
<td>"topic\0" (string, length not fixed)</td>
<td>reserved</td>
<td>dimensions (u4, 1 nybble)</td>
<td>length (u16, 2 bytes)</td>
<td>fs (u4, 1 nybble/dimension)</td>
<td>payload (series of bytes, length based on dimension, length, fs, and payload)</td>
</tr>
</table>
<p>We should take a look at each of these in more detail:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Parameter</th>
<th>Description</th>
</tr>
<tr>
<td>"topic\0"</td>
<td>The 'topic' is a zero-terminated string. The topic is transmitted across the medium
as-is, so keep the topic as short as reasonably possible.</td>
</tr>
<tr>
<td>reserved</td>
<td>There are 4 bits that are reserved for future use on the first byte. These are the
upper bits in the byte. The 'dimensions' is in the lower nybble of this byte.</td>
</tr>
<tr>
<td>dimensions</td>
<td>Strings always have a dimension of 1. The number of dimensions references the number
of variables that are being sent within this transmission. For instance, if (x,y) were
being sent as a single transmission, then the transmission has two dimensions. The maximum
number of dimensions is 15.</td>
</tr>
<tr>
<td>length</td>
<td>The length is the number of characters if the transmission is a string. Otherwise, it
is the length of the data being sent. Note that it is the length of a single data set. For
instance, if a set of (x,y) variables were being sent in a single transmission, and each
variable consisted of a 10-element array, the length would be 10 and not 20.</td>
</tr>
<tr>
<td>format specifiers</td>
<td>The format specifier is a way for the transmitter to communicate to the receiver how the
data is encoded. For each dimension, there will be one format specifier. For instance, a
string has one dimension and, thus, will have one format specifier. The (x,y) data will have
two dimensions and, thus, will have two format specifiers. The format specifiers are n nybble
wide. When a transmision is of one dimension, the format specifier will lay on the lower nybble
and the upper nybble will be unused.</td>
</tr>
<tr>
<td>payload</td>
<td>The payload is simply a series of bytes. In a string, the payload contains a series of ASCII
characters. In a numeric example, the payload contains the data. Multidimensional data is kept
intact as a block and not mixed. Meaning x<sub>0</sub>x<sub>1</sub>x<sub>2</sub>...x<sub>n</sub>
then y<sub>0</sub>y<sub>1</sub>y<sub>2</sub>...y<sub>n</sub>.</td>
</tr>
</table>
<h2>Format Specifier Assignments</h2>
<p>In the previous post, we outlined the format specifiers. In this post, we will assign numbers to the
format specifier so that we can write and read the format specifier field numerically. The format
specifier bit values will correspond to the "Enumerated Value" column, meaning that if a type "u8"
is the format specifier, the value that will actually be sent is "2".</p>
<table class="table table-bordered table-striped">
<tr>
<th>Format Specifier</th>
<th>Signed/Unsigned</th>
<th>Width (bytes)</th>
<th>Enumerated Value</th>
</tr>
<tr>
<td>(str)</td>
<td>-</td>
<td>-</td>
<td>1</td>
</tr>
<tr>
<td>u8</td>
<td>unsigned</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>s8</td>
<td>signed</td>
<td>1</td>
<td>3</td>
</tr>
<tr>
<td>u16</td>
<td>unsigned</td>
<td>2</td>
<td>4</td>
</tr>
<tr>
<td>s16</td>
<td>signed</td>
<td>2</td>
<td>5</td>
</tr>
<tr>
<td>u32</td>
<td>unsigned</td>
<td>4</td>
<td>6</td>
</tr>
<tr>
<td>s32</td>
<td>signed</td>
<td>4</td>
<td>7</td>
</tr>
</table>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- center-article -->
<p><ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-9588877918195509"
data-ad-slot="8072568671"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></p>
<h1>Publishing Data</h1>
<p>In this section, we are going to follow the format of the
<a href="http://www.forembed.com/project-curve-tracer-serial-prototol-part-1.html">previous post</a> so that
the user can easily look back and forth.</p>
<h2>The Simplest Example</h2>
<div class="highlight"><pre><span></span><span class="n">publish</span><span class="p">(</span><span class="s">"foo"</span><span class="p">,</span> <span class="s">"my string"</span><span class="p">)</span>
</pre></div>
<p>Should translate into the below fields. Each field is also broken down into its
bytes. Note that the sequence is using 'little-endian' format. For instance, the
'length' field consists of 2 bytes. The lower index is the LSB while the higher
index is the MSB. The same is true throughout the message.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Fields:</th>
<td colspan="4">"foo\0"</td>
<td>dim = 1</td>
<td colspan="2">length = 9</td>
<td>fs = STR</td>
<td colspan="9">"my string\0"</td>
</tr>
<tr>
<th>Bytes:</th>
<td>'f'</td>
<td>'o'</td>
<td>'o'</td>
<td>'\0'</td>
<td>1</td>
<td>9</td>
<td>0</td>
<td>1</td>
<td>'m'</td>
<td>'y'</td>
<td>' '</td>
<td>'s'</td>
<td>'t'</td>
<td>'r'</td>
<td>'i'</td>
<td>'n'</td>
<td>'g'</td>
</tr>
</table>
<h2>The Simplest Numeric Example</h2>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">bar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">100</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo, u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
</pre></div>
<p>Breaks down into:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Fields:</th>
<td colspan="4">"foo\0"</td>
<td>dim = 1</td>
<td colspan="2">length = 1</td>
<td>fs = U8</td>
<td>Payload = {100}</td>
</tr>
<tr>
<th>Bytes:</th>
<td>'f'</td>
<td>'o'</td>
<td>'o'</td>
<td>'\0'</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>2</td>
<td>100</td>
</tr>
</table>
<p>Something to take note of, there is a lot of overhead to sending
a single byte of data per transmission! This transmission is 9
bytes long, but only actually transmits 1 byte of data! That is
a transmission efficiency of 11%, very bad. That isn't entirely
fair, as the topic is also part of the valid data, but you should
come away knowing that sending a single piece of data per transmission
represents an inefficient use of available bandwidth which may
be acceptable or even necessary in your application.</p>
<h2>Arrays</h2>
<p>A more efficient transmission method is to send an entire array of data:</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">bar</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">30</span><span class="p">,</span><span class="mi">40</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo:4,u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
</pre></div>
<p>Breaks down into:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Fields:</th>
<td colspan="4">"foo\0"</td>
<td>dim = 1</td>
<td colspan="2">length = 4</td>
<td>fs = U8</td>
<td colspan="4">Payload = {10,20,30,40}</td>
</tr>
<tr>
<th>Bytes:</th>
<td>'f'</td>
<td>'o'</td>
<td>'o'</td>
<td>'\0'</td>
<td>1</td>
<td>4</td>
<td>0</td>
<td>1</td>
<td>10</td>
<td>20</td>
<td>30</td>
<td>40</td>
</tr>
</table>
<p>By adding 3 more elements, our transmission efficiency increases to 33.3%,
which isn't great, but is 3x better than sending only 1 element of data.
As you can see, as one sends an infinitely larger set of data, the data
efficiency will approach 100%. For microcontrollers, a reasonable limit
to set would be 128 elements of 8-bit data (92.1%). The library itself can
currently support a limit of 65535 elements (based on the 16-bit 'length'
field), which would result in a 99.986% efficiency, but this is impractical
on the devices we are working with and is included in the library so that
transmissions aren't limited to 255 element transmission.</p>
<h2>Format Specifier</h2>
<p>The format specifier determines the width of each element within the payload:</p>
<div class="highlight"><pre><span></span><span class="kt">int8_t</span> <span class="n">bar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo,s16"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
</pre></div>
<table class="table table-bordered table-striped">
<tr>
<th>Fields:</th>
<td colspan="4">"foo\0"</td>
<td>dim = 1</td>
<td colspan="2">length = 1</td>
<td>fs = U16</td>
<td colspan="2">Payload = {10}</td>
</tr>
<tr>
<th>Bytes:</th>
<td>'f'</td>
<td>'o'</td>
<td>'o'</td>
<td>'\0'</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td>4</td>
<td>10</td>
<td>0</td>
</tr>
</table>
<p>A string/u8/s8 format specifier implies one byte/element. A u16/s16 implies two
bytes/element. A u32/s32 implies four bytes/element.</p>
<h2>Multi-Dimensional Data</h2>
<p>Multi-dimensional data must be able to be transmitted across the channel as a
single transmission as well.</p>
<div class="highlight"><pre><span></span><span class="kt">int16_t</span> <span class="n">bar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">baz</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo,s16,u16"</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">baz</span><span class="p">);</span>
</pre></div>
<table class="table table-bordered table-striped">
<tr>
<th>Fields:</th>
<td colspan="4">"foo\0"</td>
<td>dim = 2</td>
<td colspan="2">length = 1</td>
<td>fs[0] = S16, fs[1] = U16</td>
<td colspan="2">Payload[0] = {10}</td>
<td colspan="2">Payload[1] = {20}</td>
</tr>
<tr>
<th>Bytes:</th>
<td>'f'</td>
<td>'o'</td>
<td>'o'</td>
<td>'\0'</td>
<td>2</td>
<td>1</td>
<td>0</td>
<td>0x45</td>
<td>10</td>
<td>0</td>
<td>20</td>
<td>0</td>
</tr>
</table>
<h2>Putting it All Together</h2>
<div class="highlight"><pre><span></span><span class="kt">int16_t</span> <span class="n">bar</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">30</span><span class="p">,</span><span class="mi">40</span><span class="p">};</span>
<span class="kt">uint8_t</span> <span class="n">baz</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">100</span><span class="p">,</span><span class="mi">90</span><span class="p">,</span><span class="mi">80</span><span class="p">,</span><span class="mi">70</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo:100,s16,u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">baz</span><span class="p">);</span>
</pre></div>
<table class="table table-bordered table-striped">
<tr>
<th>Fields:</th>
<td colspan="4">"foo\0"</td>
<td>dim = 2</td>
<td colspan="2">length = 4</td>
<td>fs[0] = S16, fs[1] = U8</td>
<td colspan="8">Payload[0] = {10,20,30,40}</td>
<td colspan="4">Payload[1] = {100,90,80,70}</td>
</tr>
<tr>
<th>Bytes:</th>
<td>'f'</td>
<td>'o'</td>
<td>'o'</td>
<td>'\0'</td>
<td>2</td>
<td>1</td>
<td>0</td>
<td>0x25</td>
<td>10</td>
<td>0</td>
<td>20</td>
<td>0</td>
<td>30</td>
<td>0</td>
<td>40</td>
<td>0</td>
<td>100</td>
<td>90</td>
<td>80</td>
<td>70</td>
</tr>
</table>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- center-article -->
<p><ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-9588877918195509"
data-ad-slot="8072568671"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></p>
<h1>Next Up...</h1>
<p>Up to this point, we have talked about publishing data to a particular
topic, but how would the receiver get access to that data? That is the
topic of Part 3 of our series!</p>Project Curve Tracer - Serial Protocol Part 12016-04-05T08:47:00-04:002016-04-05T08:47:00-04:00Jason Jonestag:www.forembed.com,2016-04-05:/project-curve-tracer-serial-prototol-part-1.html<p><img alt="how to connect?" src="http://www.forembed.com/images/2016/03/connection-question.png"></p>
<h1>Looking for a Serial Protocol</h1>
<p>As you know if you have been reading the other articles in this series,
the end goal is to display the curve tracer data on a PC screen for the
user to consume and to provide controls for the user to interact with
the hardware …</p><p><img alt="how to connect?" src="http://www.forembed.com/images/2016/03/connection-question.png"></p>
<h1>Looking for a Serial Protocol</h1>
<p>As you know if you have been reading the other articles in this series,
the end goal is to display the curve tracer data on a PC screen for the
user to consume and to provide controls for the user to interact with
the hardware.</p>
<p>I have looked around the interwebs for a serial protocol that is easy to
use, lightweight, and has the ability to send multi-dimensional data, but
I haven't found it. The closest think that I have found is the
<a href="https://github.com/Overdrivr/Telemetry">Telemetry</a> library. This is an
outstanding project in that it provides you with a serial protocol along
with a graphical python script which can be used to plot the data coming
out of the microcontroller.</p>
<p>Unfortunately, the library doesn't have the ability to send multi-dimensional
data (x,y coordinates, for instance) nor the ability to send bulk data, such
as an array.</p>
<p>So we will engage in that time-honored tradition of building up a serial
protocol. There are hundreds - maybe thousands - out there, so the right one
probably exists, but it might take us longer to find and customize it than
to just write it and be done.</p>
<h1>Goals</h1>
<p>The current goals are:</p>
<ul>
<li>Data Integrity</li>
<li>End-point delivery</li>
</ul>
<p>At this time, there is no vision for ensuring content delivery (ACK, retransmit,
etc.) at this level in the protocol. This means that the application layer will
have to take care of ensuring content delivery, if necessary. It is possible
that we will implement delivery assurance at a later time and have reserved a few
bits so that we can take care of this aspect of transmission later.</p>
<p>The protocol described herein is a <em>vision</em>, not a final document. Issues brought
about during implementation may drive some minor changes, although the spirit of
the article should still be maintained.</p>
<h1>Physical Medium</h1>
<p>This library will be written using a UART with a focus on 8-bit point-to-point
applications, but I see no reason that it wouldn't work on just about any
point-to-point connection that is byte-aligned.</p>
<p>For the remainder of this article, we want to focus on the vision of the use
model and won't get bogged down in the physical medium, but that is something
that will need to be addressed at some point.</p>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- center-article -->
<p><ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-9588877918195509"
data-ad-slot="8072568671"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></p>
<h1>Publish/Subscribe Model</h1>
<p>I must be honest, I am going to template my library as much as I can from
Telemetry. I will just hack in those features that are required and I may
end up with my own library out there on github. For now, it is just part
of this project.</p>
<p>I wanted to start this series of posts with a quick vision. As I stated,
I'm largely templating this from Telemetry, so much of this is available there.
I will use a 'publish-subscribe' model in which two (or more on some types
of buses) parties will have access to a serial channel and the data will
be 'published' to a topic. Any parties that are interested in that topic
will receive the data. If there are no subscribers, the data is simply sent,
but not received.</p>
<p><img alt="publish-subscribe" src="http://www.forembed.com/images/2016/03/publish-subscribe.png"></p>
<p>Note that the picture shows all messages going through one channel and being
automatically separated out on the other side. This is the basic idea of
the publish/subscribe model that will be utilized. The image depicts one
channel of publisher/subscription. In the UART, there are two channels, one
for each direction. Each endpoint will have at least one publisher and at
least one subscriber each in our application.</p>
<h1>High-Level Publish-Subscribe Workflow</h1>
<p>The library - above all - must be easy to port and easy to use! I want to
focus on the usability part first simply because porting is a matter of
coding it correctly. Usability has to come up front and doesn't just happen
without some thought.</p>
<h2>Publishing Data</h2>
<h3>The Simplest Example</h3>
<p>The easiest thing to do with the pubserial library is to simply publish
a string:</p>
<div class="highlight"><pre><span></span><span class="n">publish</span><span class="p">(</span><span class="s">"foo"</span><span class="p">,</span> <span class="s">"my string"</span><span class="p">);</span>
<span class="c1">// ^ string to be published, in quotes</span>
<span class="c1">// ^ topic</span>
</pre></div>
<p>The above code is somewhat equivalent to <code>printf</code> in that it will simply
send the string specified to the particular topic.</p>
<h3>The Simplest Numeric Example</h3>
<p>The simplest possible publish would be to publish a single data point to
a topic.</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">bar</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo,u8"</span><span class="p">,</span> <span class="o">&</span><span class="n">bar</span><span class="p">);</span>
<span class="c1">// ^ source of data</span>
<span class="c1">// ^ format specifier</span>
<span class="c1">// ^ topic</span>
</pre></div>
<p>Note that there are three elements:s topic, format specifier, and data. The portion
after the comma in the string is known as the 'format specifier' and will be explained in
further detail later. If the data width is not specified, then the
assumption is that the data in bar is a string.</p>
<p>The same thing could have been accomplished using an array notation for
the variable declaration:</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">bar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo,u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
<span class="c1">// ^source of data</span>
<span class="c1">// ^topic</span>
</pre></div>
<p>Which notation is utilized for single data points doesn't matter, but at
a point later in the article, this notation will become the norm, so it
helps to see it in both forms here on a single point of data.</p>
<h3>Arrays</h3>
<p>Next, we will likely want to post several related bits of data to a topic.
We can do this using the colon ':' as the delimiter, similar to Python and
JavaScript.</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">bar</span><span class="p">[</span><span class="mi">20</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">11</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span><span class="mi">13</span><span class="p">,</span><span class="mi">14</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span><span class="mi">16</span><span class="p">,</span><span class="mi">17</span><span class="p">,</span><span class="mi">18</span><span class="p">,</span><span class="mi">19</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo:20,u8"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
<span class="c1">// ^ number of elements</span>
</pre></div>
<p>Again, the width is not specified, so the assumption is that bar contains 8-bit
data. The above would send 20 elements of the data contained within <code>bar</code> to all
subscribers of the topic <code>foo</code>.</p>
<h3>Format Specifier</h3>
<p>Up to this point, we have been sending unsigned, 8-bit data. What if we want to
send a signed 16-bit integer?</p>
<div class="highlight"><pre><span></span><span class="kt">int8_t</span> <span class="n">bar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo,s16"</span><span class="p">,</span> <span class="n">bar</span><span class="p">);</span>
<span class="c1">// ^ format specifier</span>
</pre></div>
<p>Now we have introduced the comma as a format specifier. Format specifiers can
assign the data type to be sent. Valid values for the format specifier are
listed in the table:</p>
<table class="table table-bordered table-striped">
<tr>
<th>Format Specifier</th>
<th>Signed/Unsigned</th>
<th>Width (bytes)</th>
</tr>
<tr>
<td>u8</td>
<td>unsigned</td>
<td>1</td>
</tr>
<tr>
<td>s8</td>
<td>signed</td>
<td>1</td>
</tr>
<tr>
<td>u16</td>
<td>unsigned</td>
<td>2</td>
</tr>
<tr>
<td>s16</td>
<td>signed</td>
<td>2</td>
</tr>
<tr>
<td>u32</td>
<td>unsigned</td>
<td>4</td>
</tr>
<tr>
<td>s32</td>
<td>signed</td>
<td>4</td>
</tr>
<tr>
<td>(str)</td>
<td>-</td>
<td>-</td>
</tr>
</table>
<h3>Multi-Dimensional Data</h3>
<p>There are many times in which we wish to publish x and y at the same time
for plotting or other purposes. It would be very nice to be able to do that
with this model.</p>
<div class="highlight"><pre><span></span><span class="kt">int16_t</span> <span class="n">bar</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">baz</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo,s16,u16"</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">baz</span><span class="p">);</span>
<span class="c1">// ^ second format specifier</span>
<span class="c1">// ^ first format specifier</span>
</pre></div>
<p>When multiple format specifiers are present in the topic string, then there
should be multiple pieces of data to be sent with this transmission. In the
above, a single datapoint is being sent that has two dimensions. You might
think of this as a single data point in a scatter plot.</p>
<p>Note that strings <em>cannot</em> be sent with other data. If you need to send multiple
strings, then that must occur as multiple publish sequences.</p>
<h3>Putting it All Together</h3>
<p>Finally, we get to the part where the real power of the library is flexed. We
can use all of the above conventions together to send multi-dimensional data as
an array for maximum transmission efficiency:</p>
<div class="highlight"><pre><span></span><span class="kt">int16_t</span> <span class="n">bar</span><span class="p">[</span><span class="mi">100</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">baz</span><span class="p">[</span><span class="mi">100</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">20</span><span class="p">};</span>
<span class="n">publish</span><span class="p">(</span><span class="s">"foo:100,s16,u16"</span><span class="p">,</span> <span class="n">bar</span><span class="p">,</span> <span class="n">baz</span><span class="p">);</span>
</pre></div>
<p>The above format would send the data contained in <code>bar[0]</code> through <code>bar[99]</code>, <code>baz[0]</code>
through <code>baz[99]</code> and publish the data to <code>foo[0]</code> through <code>foo[99]</code>.</p>
<p>Admittedly, this isn't the simplest possible solution for a single application. The
simplest solution would probably be to write a single function for your needs, maybe
a publish16 and not have to worry about the rest. The extra complication of adding
the string formatter is to add flexibility to the library. Using the string formatter
allows the library to play well in a huge variety of applications and even allow run-
time creation of the formatting string.</p>
<h3>Publishing Limitations</h3>
<p>The primary limitation is that string data and numeric data may not be mixed:</p>
<div class="highlight"><pre><span></span><span class="n">publish</span><span class="p">(</span><span class="s">"foo,s16"</span><span class="p">,</span> <span class="s">"my string"</span><span class="p">,</span> <span class="o">&</span><span class="n">bar</span><span class="p">);</span>
<span class="c1">// ^ ^ mixed string and numeric in one publish - invalid!</span>
</pre></div>
<p>Additionally, RAM limitations may keep messages relatively small along with the
number of dimensions. In our implementation, it seems reasonable to limit the
dimensionality to '3' and the length to '128'.</p>
<h2>Subscribing to Data</h2>
<p>Again, assuming a C development environment, subscription should be as simple
as specifying a topic and a function to execute when that topic is received.</p>
<div class="highlight"><pre><span></span><span class="cm">/* write the function to execute when a topic is received */</span>
<span class="kt">void</span> <span class="n">function</span> <span class="nf">myFooReceiver</span><span class="p">(</span><span class="n">PubSer</span><span class="o">*</span> <span class="n">pubSerStruct</span><span class="p">){</span>
<span class="cm">/* do something with the pubSerStruct data */</span>
<span class="p">}</span>
<span class="cm">/* .... somewhere in the code .... */</span>
<span class="n">subscribe</span><span class="p">(</span><span class="s">"foo"</span><span class="p">,</span> <span class="o">&</span><span class="n">myFunction</span><span class="p">);</span>
</pre></div>
<p>Note that the subscription specifies the topic ONLY. It does not specify data widths,
array indices, or anything of the sort. All of this information will be conatined in
the PubSer structure.</p>
<h2>Additional Notes</h2>
<p>The data model does not protect you from errors! For instance, if you specified
that u16 data is to be sent, but specify that that it is to go into a s32 register
in your subscriber call back, the misinterpretation will likely result in errors
at run time, not at compile time.</p>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- center-article -->
<p><ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-9588877918195509"
data-ad-slot="8072568671"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></p>
<h1>Hardware Agnosticism</h1>
<p>The library should also be able to utilize any byte-aligned hardware or communications
channel as simply as possible.</p>
<p>Again, taking a cue from the Telemetry library, we would like to provide a function on
which a library may be built. This will likely be provided in the form of a structure
to initialize which points to the correct functions to execute for byte-aligned hardware
access.</p>
<h1>Use in the Curve Tracer Project</h1>
<p>Once we get a nice little library going, we will be able to easily publish (x,y) data
to a particular topic for our primary data transfer:</p>
<div class="highlight"><pre><span></span><span class="n">publish</span><span class="p">(</span><span class="s">"curvetrace:128,s16,s16"</span><span class="p">,</span> <span class="n">volts</span><span class="p">,</span> <span class="n">amps</span><span class="p">);</span>
</pre></div>
<p>Additionally, the microcontroller will be able to subscribe to feeds such as 'frequency'
and 'waveshape', making modification of the operating characteristics of the software trivial.</p>
<p>In part 2, we will translate all of the above into a formatted stream of bytes so that
we can think about sending data in a byte-aligned chunk across an interface!</p>Current Sensor Datasheet2016-03-29T00:01:00-04:002016-03-29T00:01:00-04:00Jason Jonestag:www.forembed.com,2016-03-29:/current-sensor-datasheet.html<p><img src="/images/2016/02/sensor_board_render.png" alt="current sensor board render" class="img-responsive img-rounded"></p>
<h1>Purpose</h1>
<p>The current sensor breakout is a breadboard compatible breakout module for the
<a href="http://www.ti.com/product/ina139/description?keyMatch=INA139&tisearch=Search-EN-Everything">INA139</a>
series of high-side current sensor ICs. In addition to providing a mount point for the
INA139 part and the shunt resistor, the module also includes a low-impedance output suitable
for driving an analog-to-digital converter.</p>
<h1>Pinout</h1>
<p>The …</p><p><img src="/images/2016/02/sensor_board_render.png" alt="current sensor board render" class="img-responsive img-rounded"></p>
<h1>Purpose</h1>
<p>The current sensor breakout is a breadboard compatible breakout module for the
<a href="http://www.ti.com/product/ina139/description?keyMatch=INA139&tisearch=Search-EN-Everything">INA139</a>
series of high-side current sensor ICs. In addition to providing a mount point for the
INA139 part and the shunt resistor, the module also includes a low-impedance output suitable
for driving an analog-to-digital converter.</p>
<h1>Pinout</h1>
<p>The potentially high-current pins - Vps and Vload - have two pins each dedicated to their
functionality. It is not necessary to connect both pins.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Pin Number</th>
<th>Pin Label</th>
<th>Description</th>
</tr>
<tr>
<td>1</td>
<td>V<sub>ps</sub></td>
<td>Power supply pin 1 - connect to the incoming power supply</td>
</tr>
<tr>
<td>2</td>
<td>V<sub>ps</sub></td>
<td>Power supply pin 2 - connect to the incoming power supply</td>
</tr>
<tr>
<td>3</td>
<td>V<sub>load</sub></td>
<td>Load pin 1 - connect to the load</td>
</tr>
<tr>
<td>4</td>
<td>V<sub>load</sub></td>
<td>Load pin 2 - connect to the load</td>
</tr>
<tr>
<td>5</td>
<td>V<sub>cc</sub></td>
<td>Logic-level Voltage Supply</td>
</tr>
<tr>
<td>6</td>
<td>Gnd</td>
<td>Ground Pin</td>
</tr>
<tr>
<td>7</td>
<td>V<sub>sns</sub></td>
<td>Sense voltage pin - leave unconnected in most applications unless filtering is desired</td>
</tr>
<tr>
<td>8</td>
<td>V<sub>buf</sub></td>
<td>Voltage buffer output - connect this to the ADC or other measurement device</td>
</tr>
</table>
<h1>Ratings</h1>
<h2>Sustained Ratings</h2>
<p>Each part is designed so that no more than 150mV is dropped across the part at the rated current.
It is possible to run a multitude of settings on this part, but I have found that the most common
combinations are targeted for 3.3V and 5V applications. Below you will find common configurations
for measuring currents between 3mA to 3.0A.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Part Number</th>
<th>Rated Current</th>
<th>Transimpedance</th>
<th>Volts Out @ Irated</th>
</tr>
<tr>
<td>ELAS00011100</td>
<td>3.0A</td>
<td>1.65V/A</td>
<td>4.95V</td>
</tr>
<tr>
<td>ELAS00011101</td>
<td>300mA</td>
<td>16.5V/A</td>
<td>4.95V</td>
</tr>
<tr>
<td>ELAS00011102</td>
<td>30mA</td>
<td>155.1V/A</td>
<td>4.95V</td>
</tr>
<tr>
<td>ELAS00011103</td>
<td>3mA</td>
<td>1551V/A</td>
<td>4.95V</td>
</tr>
<tr>
<td>ELAS00011104</td>
<td>3.0A</td>
<td>1.1V/A</td>
<td>3.3V</td>
</tr>
<tr>
<td>ELAS00011105</td>
<td>300mA</td>
<td>11V/A</td>
<td>3.3V</td>
</tr>
<tr>
<td>ELAS00011106</td>
<td>30mA</td>
<td>103.4V/A</td>
<td>3.3V</td>
</tr>
<tr>
<td>ELAS00011107</td>
<td>3mA</td>
<td>1034V/A</td>
<td>3.3V</td>
</tr>
</table>
<h2>Common</h2>
<p>These values are common to all part variants.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Parameter</th>
<th>Value</th>
</tr>
<tr>
<td>Frequency</td>
<td>200kHz</td>
</tr>
<tr>
<td>Accuracy</td>
<td>2%</td>
</tr>
<tr>
<td>Recommended Minimum V<sub>cc</sub></td>
<td>3.0V</td>
</tr>
<tr>
<td>Recommended Maximum V<sub>cc</sub></td>
<td>5.0V</td>
</tr>
<tr>
<td>Recommended Minimum V<sub>ps</sub></td>
<td>3.0V</td>
</tr>
<tr>
<td>Recommended Maximum V<sub>ps</sub></td>
<td>35.0V</td>
</tr>
</table>
<h1>Absolute Maximum Ratings</h1>
<table class="table table-bordered table-striped">
<tr>
<th>Parameter</th>
<th>Maximum</th>
</tr>
<tr>
<td>V<sub>ps</sub> to GND</td>
<td>40.0V</td>
</tr>
<tr>
<td>V<sub>cc</sub></td>
<td>5.5V</td>
</tr>
</table>
<h1>Schematic</h1>
<p>The schematic has three sections:</p>
<ol>
<li>Shunt Resistor</li>
<li>High-Side Amplifier</li>
<li>Voltage Follower</li>
</ol>
<p>The shunt resistor is the resistor through which all current flows in order to
create a differential voltage.</p>
<p>The high-side amplifier circuit is the circuit which measures the differential
voltage across the shunt resistor and amplifies the signal to a more easily
measured value.</p>
<p>The voltage follower takes the high-impedance output of the high-side amplifier
and outputs a low-impedance voltage suitable for driving a multitude of input
types, such as analog-to-digital converters.</p>
<p><img src="http://www.forembed.com/images/2016/03/current-sensor-schematic.png" width="800"></p>
<h1>Connection Diagrams</h1>
<p>De-coupling capacitors are not necessary as they are integrated next to the part,
although extra capacitance on the inputs of the pins would not compromise the
performance of the part in any way.</p>
<h2>Common Supply</h2>
<p>In application in which the Vps is between 3.0V and 5.0V, Vcc and Vps may be
connected. This is the simplest method of connection. It is possible that
this method of connection may induce noise on the V<sub>cc</sub> pin, depending
on the load noise characteristics.</p>
<p><img src="/images/2016/03/common-supply-connection.png" alt="Common Supply" class="img-responsive img-rounded"></p>
<h2>Split Supply</h2>
<p>In application in which the Vps is to operate above 5.0V, Vcc should
be supplied with its own voltage source between 3.0V and 5.0V.</p>
<p><img src="/images/2016/03/split-supply-connection.png" alt="Split Supply Connection" class="img-responsive img-rounded"></p>
<h2>Filtered</h2>
<p>In some situations, it may be desireable to filter the output waveform, reducing
the frequency content of the output signal while increasing the 'averaging' effect.
When this is desired, a capacitor may be populated between Vsense and GND.</p>
<p><img src="/images/2016/03/split-supply-connection-filter.png" alt="Split Supply with Filter" class="img-responsive img-rounded"></p>Project Curve Tracer - Framing Serial Data2016-03-28T07:49:00-04:002016-03-28T07:49:00-04:00Jason Jonestag:www.forembed.com,2016-03-28:/project-curve-tracer-framing-serial-data.html<p><img src="/images/2016/03/framed.png" alt="Framed" class="img-responsive img-rounded"></p>
<p>In transferring data, there are many layers that one can add in order to ensure that
the communication has certain properties. For instance, up to this point, we have
implemented the physical layer and part of the the data layer. The physical layer
specifies thevoltages we are permitted to operate …</p><p><img src="/images/2016/03/framed.png" alt="Framed" class="img-responsive img-rounded"></p>
<p>In transferring data, there are many layers that one can add in order to ensure that
the communication has certain properties. For instance, up to this point, we have
implemented the physical layer and part of the the data layer. The physical layer
specifies thevoltages we are permitted to operate on and the data layer ensures that
each element transferred is a byte.</p>
<p>In this post, we are going to elaborate on the data layer and enable arbitrary transfers
that contain a series of bytes or a block of data. What those bytes mean won't be won't
matter at this level, we just need to ensure that the data that is received is actually
the data that was intended.</p>
<h1>Framing Elements</h1>
<p>Framing is a method of sending a coherent block of data and verifying that the received
data is exactly the intended data.</p>
<h2>Payload</h2>
<p>The first element of the frame is the payload. In
our data model, the frame payload has a minimum size of 1 byte.</p>
<h2>Payload Integrity Verification</h2>
<p>Once the sender knows the contents of the frame payload, it can calculate a CRC or
checksum of the data and append that verification code to the end of the data. This
verification code allows the receiver to verify that the payload is unchanged between
sender and receiver.</p>
<h2>Start-of-Frame (SOF) Flag</h2>
<p>The Start-of-Frame (SOF) flag is a specific value that indicates that the frame is
about to begin. The SOF will not occur at any place within the frame (see 'Escape
Sequences' below) and will not occur again until the next frame starts. The SOF
itself is discarded by the receiver as it is not valid data in itself.</p>
<h2>End-of-Frame (EOF) Flag</h2>
<p>The End-of-Frame (EOF) flag is exactly analagous to the SOF flag except that it
delimits the end of a frame. All data between the SOF and EOF are data to be
consumed by the receiver while the SOF and EOF flags are discarded by the receiver.</p>
<h2>Escape Sequences</h2>
<p>One might imagine that the payload itself might contain a byte of data that happens
to be the of the same value as the SOF or EOF. What are we to do when this occurs?
This is where we escape the character and switch it out for another so that the
entire sequence between SOF and EOF remains free of SOF and EOF characters. Thus,
we find ourselves with a third special character called the ESC character. As the
data is being framed, each byte is examined to ensure that it doesn't correspond to
the SOF, EOF, or ESC characters. If it does, then the ESC character is inserted and
the offending character is XORed with a value that changes its transmitted value.
When the receiver encounters the ESC character, the ESC character is discarded and
the very next character in the sequence is XORed with the same value, returning
the string of characters to its original value.</p>
<p>The numbers that were chosen for the escape sequences are as follows:</p>
<div class="highlight"><pre><span></span><span class="cp">#define SOF 0xf7</span>
<span class="cp">#define EOF 0x7f</span>
<span class="cp">#define ESC 0xf6</span>
<span class="cp">#define ESC_XOR 0x20</span>
</pre></div>
<p>There is nothing special about these numbers. They are simply chosen based on
observation of other similar implementations.</p>
<h1>Example</h1>
<p>Assume, for a moment, that we have a sequence of data that is to be transmitted.<br>
The data is the payload, so we will designate it PL[0] through PL[4] for a 5-byte
sequence that must be transmitted.
The first step in framing is to simply calculate the verification sequence. In
this case, we will be calculating a fletcher16 checksum and append the value to
the end as CK[0] and CK[1]:</p>
<p><img alt="Frame, non-escaped" src="http://www.forembed.com/images/2016/03/frame_non_escaped.png"></p>
<p>We append the checksum to the end of the data. Next, we must add any escaping
that is necessary. In this sequence, lets say that PL[2] corresponds to the
value of the SOF, EOF, or ESC characters. Note that the checksum is subject
to escaping as well.</p>
<p><img alt="Frame, escaped" src="http://www.forembed.com/images/2016/03/frame_escaped.png"></p>
<p>Once all of the SOF, EOF, and ESC characters have been removed by the escaping
process, we can add our SOF and EOF secure in the knowledge that there is no data
in between that will coincide with the SOF, EOF, or ESC that isn't supposed to
be there.</p>
<p><img alt="Frame, complete" src="http://www.forembed.com/images/2016/03/frame_complete.png"></p>
<p>The receiver now re-executes this sequence in reverse. It first removes the
SOF and EOF flags and discards them. The receiver then goes through the sequence
identifying ESC characters and, where found, discards the ESC character, XORs
the next character with the proper escape value, and saves that value as the data.
Once the data has been un-framed and un-escaped, the receiver then calculates the
checksum on the data and compares it with the checksum appended to the end of
the data. If the checksum matches, then the data is accepted as a frame. If
the checksum does not match, the entire data set is discarded.</p>
<h1>Framing Bandwidth Efficiency</h1>
<p>One thing to take note of. As the features of the network increase, so, too, does
the amount of metadata required. For instance, in our simple framing example
above, it took 10 characters to transmit a single 5 characters. This is an abysmal
50% transmission efficiency just to frame the characters! The advantage is that
we can send messages that are far longer than 5 bytes. If we were to send a similar
message of a length of 100 bytes with a 1.2% escape occurance, then we would end up
adding 6 bytes for framing (2 checksum, 1 SOF, 1 EOF, and 1.2 rounded up to 2 for
escaping). This would result in 94% efficiency. For this reason, it is critical
for bandwidth that higher-level protocols transmit as many bytes in a single frame
as possible in order to better leverage the available channels.</p>
<h1>C Framing Implementation</h1>
<h2>Pre-requisites</h2>
<p>In order to implement framing, we must have two byte-aligned circular buffers in
the software layer just below our framing layer. The software layer must have
available four functions:</p>
<table class="table table-bordered table-striped">
<tr>
<th>function</th>
<th>description</th>
</tr>
<tr>
<td>UART_readable()</td>
<td>returns the number of full slots in the receive buffer</td>
</tr>
<tr>
<td>UART_writeable()</td>
<td>returns the number of empty slots in the send buffer</td>
</tr>
<tr>
<td>UART_read()</td>
<td>reads a number of bytes from the receive circular buffer</td>
</tr>
<tr>
<td>UART_write()</td>
<td>writes a number of bytes to the send circular buffer</td>
</tr>
</table>
<p><br>
The underlying UART library must take care of sending and receiving all bytes
written to and received from the circular buffers. In our case, we are using
the <a href="http://www.forembed.com/circular-buffers-part-two.html">circular buffers written about in a previous article</a>
to store UART data in both directions.</p>
<h2>Framing Functions</h2>
<p>We are going to write three functions, FRM_push(), FRM_pull(), and FRM_fletcher16().</p>
<table class="table table-bordered table-striped">
<tr>
<th>function</th>
<th>description</th>
</tr>
<tr>
<td>FRM_push()</td>
<td>"pushes" data to the UART for sending after framing</td>
</tr>
<tr>
<td>FRM_pull()</td>
<td>"pulls" data from the UART for receiving and de-frames</td>
</tr>
<tr>
<td>FRM_fletcher16()</td>
<td>calculates the Fletcher16 checksum of the data</td>
</tr>
</table>
<h3>Pushing Data</h3>
<p>Data is 'pushed' out from higher level software using the FRM_push
function:</p>
<div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">FRM_push</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">)</span>
</pre></div>
<p>An array of data and the data length are supplied to the function. This
data corresponds to our payload from the initial framing discussions. At
this point, there is no SOF, EOF, checksum, or ESC applied to the payload.</p>
<p>The C code is as follows:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">FRM_push</span><span class="p">(</span><span class="kt">uint8_t</span><span class="o">*</span> <span class="n">data</span><span class="p">,</span> <span class="kt">uint16_t</span> <span class="n">length</span><span class="p">){</span>
<span class="kt">uint8_t</span> <span class="n">txFrame</span><span class="p">[</span><span class="n">TX_FRAME_LENGTH</span><span class="p">];</span>
<span class="kt">int16_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">txIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">check</span> <span class="o">=</span> <span class="n">FRM_fletcher16</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
<span class="kt">uint8_t</span> <span class="n">check0</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)(</span><span class="n">check</span> <span class="o">&</span> <span class="mh">0x00ff</span><span class="p">);</span>
<span class="kt">uint8_t</span> <span class="n">check1</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)((</span><span class="n">check</span> <span class="o">&</span> <span class="mh">0xff00</span><span class="p">)</span> <span class="o">>></span> <span class="mi">8</span><span class="p">);</span>
<span class="cm">/* start the frame */</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">SOF</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="cm">/* copy the data from data to the txFrame */</span>
<span class="k">while</span><span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="n">length</span><span class="p">){</span>
<span class="cm">/* add proper escape sequences */</span>
<span class="k">if</span><span class="p">((</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">SOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">ESC</span><span class="p">)){</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">ESC</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^</span> <span class="n">ESC_XOR</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* append the checksum ensuring that the values are properly escaped */</span>
<span class="k">if</span><span class="p">((</span><span class="n">check0</span> <span class="o">==</span> <span class="n">SOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check0</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check0</span> <span class="o">==</span> <span class="n">ESC</span><span class="p">)){</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">ESC</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check0</span> <span class="o">^</span> <span class="n">ESC_XOR</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check0</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">((</span><span class="n">check1</span> <span class="o">==</span> <span class="n">SOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check1</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check1</span> <span class="o">==</span> <span class="n">ESC</span><span class="p">)){</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">ESC</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check1</span> <span class="o">^</span> <span class="n">ESC_XOR</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check1</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* end the frame */</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">EOF</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="cm">/* wait for the UART circular buffer to clear from</span>
<span class="cm"> * any currently in-progress transmissions */</span>
<span class="k">while</span><span class="p">(</span><span class="n">UART_writeable</span><span class="p">()</span> <span class="o"><</span> <span class="n">txIndex</span><span class="p">);</span>
<span class="n">UART_write</span><span class="p">(</span><span class="n">txFrame</span><span class="p">,</span> <span class="n">txIndex</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The comments explain reasonably well what is going on, but lets take
a few lines at a time anyway</p>
<div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">txFrame</span><span class="p">[</span><span class="n">TX_FRAME_LENGTH</span><span class="p">];</span>
<span class="kt">int16_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">txIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</pre></div>
<p>These lines simply declare some useful variables. The <code>txFrame[]</code> array is
the place in which we will build the frame to be transmitted. The <code>txIndex</code>
variable will act as an index for txFrame as the frame is being constructed.
The <code>i</code> variable will be used throughout the function for looping and is
declared here for convenience.</p>
<div class="highlight"><pre><span></span><span class="kt">uint16_t</span> <span class="n">check</span> <span class="o">=</span> <span class="n">FRM_fletcher16</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">length</span><span class="p">);</span>
<span class="kt">uint8_t</span> <span class="n">check0</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)(</span><span class="n">check</span> <span class="o">&</span> <span class="mh">0x00ff</span><span class="p">);</span>
<span class="kt">uint8_t</span> <span class="n">check1</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint8_t</span><span class="p">)((</span><span class="n">check</span> <span class="o">&</span> <span class="mh">0xff00</span><span class="p">)</span> <span class="o">>></span> <span class="mi">8</span><span class="p">);</span>
</pre></div>
<p>First, we calculate the fletcher checksum and store the 16-bit results into
two 8-bit registers, <code>check0</code> and <code>check1</code>. This calculation is done on the
raw data before any data is stored on the frame.</p>
<div class="highlight"><pre><span></span><span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">SOF</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
</pre></div>
<p>Next, we start the frame. Based on previous discussions, this should be
clear, but note that we increment the <code>txIndex</code>. Each write to the <code>txFrame[]</code>
must be accompanied by an increment of the <code>txIndex</code> so that data doesn't
get overwritten.</p>
<div class="highlight"><pre><span></span><span class="k">while</span><span class="p">(</span><span class="n">i</span> <span class="o"><</span> <span class="n">length</span><span class="p">){</span>
<span class="cm">/* add proper escape sequences */</span>
<span class="k">if</span><span class="p">((</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">SOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">ESC</span><span class="p">)){</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">ESC</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">^</span> <span class="n">ESC_XOR</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">i</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>We now copy the majority of the data into the <code>txFrame[]</code> array. Note that
we have an <code>if</code> condition that ensures that SOF, EOF, and ESC characters are
identified and properly escaped.</p>
<div class="highlight"><pre><span></span><span class="k">if</span><span class="p">((</span><span class="n">check0</span> <span class="o">==</span> <span class="n">SOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check0</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check0</span> <span class="o">==</span> <span class="n">ESC</span><span class="p">)){</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">ESC</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check0</span> <span class="o">^</span> <span class="n">ESC_XOR</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check0</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">((</span><span class="n">check1</span> <span class="o">==</span> <span class="n">SOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check1</span> <span class="o">==</span> <span class="n">EOF</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">check1</span> <span class="o">==</span> <span class="n">ESC</span><span class="p">)){</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">ESC</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check1</span> <span class="o">^</span> <span class="n">ESC_XOR</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">check1</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>Nearing the end now, each <code>checkx</code> byte that was saved previously is also
checked to ensure that it doesn't coincide with the SOF, EOF, or ESC characters
before saving on the <code>txFrame[]</code> array.</p>
<div class="highlight"><pre><span></span><span class="n">txFrame</span><span class="p">[</span><span class="n">txIndex</span><span class="p">]</span> <span class="o">=</span> <span class="n">EOF</span><span class="p">;</span>
<span class="n">txIndex</span><span class="o">++</span><span class="p">;</span>
</pre></div>
<p>Of course, the final step to constructing the frame is saving the EOF character.
At this point, the frame is completely constructed and ready to be written
to the UART using <code>UART_write()</code>. It is prudent to ensure that the number of
bytes to be written will actually fit in the transmit buffer, so we place a
check and wait statement. The software waits for <code>UART_writeable()</code> to return
a value less than the length of the frame before writing to it.</p>
<div class="highlight"><pre><span></span><span class="k">while</span><span class="p">(</span><span class="n">UART_writeable</span><span class="p">()</span> <span class="o"><</span> <span class="n">txIndex</span><span class="p">);</span>
<span class="n">UART_write</span><span class="p">(</span><span class="n">txFrame</span><span class="p">,</span> <span class="n">txIndex</span><span class="p">);</span>
</pre></div>
<p>Note that this method would still allow multiple frames to stack up in the outbound
circular buffer, as long as those frames were relatively short. This effectively
stalls the processor and keeps it from losing data. Care must be taking to write
a reaonable amount of data through this interface or the processor will bottleneck
at this interface.</p>
<p>I will leave <code>FRM_pull()</code> as an exercise for the reader. One solution can be found
in the project <a href="https://github.com/slightlynybbled/project_curve_tracer">github repository</a>.</p>
<h1>Python Framing Implementation</h1>
<p>For the Python code, we will take a closer look at the de-framing mechanism. I
should include the caveat that all code is subject to change, depending on what
sorts of bugs are found. The presented code is what is working today.</p>
<p>First off, a class called <code>Frame</code> is created, which creates a thread called <code>run</code>.
This thread will be run continually in order to extract frames into a list of
lists <code>rx_messages</code>, which is part of the Frame object.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">port</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="c1"># pull out the frames</span>
<span class="k">while</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">SOF</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">EOF</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">):</span>
<span class="c1"># find the SOF by removing bytes in front of it</span>
<span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">SOF</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1"># find the EOF</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">element</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">):</span>
<span class="k">if</span> <span class="n">element</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">EOF</span><span class="p">:</span>
<span class="n">end_char_index</span> <span class="o">=</span> <span class="n">i</span>
<span class="k">break</span>
<span class="n">frame</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">[:</span><span class="n">end_char_index</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">[</span><span class="n">end_char_index</span><span class="p">:]</span>
<span class="c1"># remove the SOF and EOF from the frame</span>
<span class="n">frame</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">escape_flag</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="n">frame</span><span class="p">:</span>
<span class="k">if</span> <span class="n">escape_flag</span> <span class="ow">is</span> <span class="bp">False</span><span class="p">:</span>
<span class="k">if</span> <span class="n">element</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">ESC</span><span class="p">:</span>
<span class="n">escape_flag</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span> <span class="o">^</span> <span class="bp">self</span><span class="o">.</span><span class="n">ESC_XOR</span><span class="p">)</span>
<span class="n">escape_flag</span> <span class="o">=</span> <span class="bp">False</span>
<span class="c1"># remove the fletcher16 checksum</span>
<span class="n">f16_check</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">256</span>
<span class="n">f16_check</span> <span class="o">+=</span> <span class="n">message</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="c1"># calculate the checksum</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fletcher16_checksum</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">==</span> <span class="n">f16_check</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rx_messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
<span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
</pre></div>
</td></tr></table>
<p>Again, a few lines at a time...</p>
<div class="highlight"><pre><span></span> <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">port</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
</pre></div>
<p>The code will be executed for as long as the <code>Frame</code> object is in memory.
Also, the code will continually read up to 1000 bytes from the serial port
<code>self.port</code> and append to <code>self.raw</code> in preparation for processing.</p>
<div class="highlight"><pre><span></span> <span class="k">while</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">SOF</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">EOF</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">):</span>
</pre></div>
<p>When <code>self.raw</code> contains at least one SOF and one EOF chracter, process the
detected frame.</p>
<div class="highlight"><pre><span></span> <span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="n">SOF</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
</pre></div>
<p>These lines remove any residual data from in front of <code>self.raw</code> up to the
first detected SOF.</p>
<div class="highlight"><pre><span></span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">element</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">):</span>
<span class="k">if</span> <span class="n">element</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">EOF</span><span class="p">:</span>
<span class="n">end_char_index</span> <span class="o">=</span> <span class="n">i</span>
<span class="k">break</span>
<span class="n">frame</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">[:</span><span class="n">end_char_index</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">raw</span><span class="p">[</span><span class="n">end_char_index</span><span class="p">:]</span>
</pre></div>
<p>The <code>frame</code> list is created from the values between the SOF and EOF of
<code>self.raw</code>. This swath is then removed from <code>self.raw</code> so that it doesn't
get re-processed later. From this point on, the data of concern is within
<code>frame</code>.</p>
<div class="highlight"><pre><span></span> <span class="n">frame</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">message</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">escape_flag</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="n">frame</span><span class="p">:</span>
<span class="k">if</span> <span class="n">escape_flag</span> <span class="ow">is</span> <span class="bp">False</span><span class="p">:</span>
<span class="k">if</span> <span class="n">element</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">ESC</span><span class="p">:</span>
<span class="n">escape_flag</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">message</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">element</span> <span class="o">^</span> <span class="bp">self</span><span class="o">.</span><span class="n">ESC_XOR</span><span class="p">)</span>
<span class="n">escape_flag</span> <span class="o">=</span> <span class="bp">False</span>
</pre></div>
<p>The <code>message</code> list will store the final de-framed data. Note that any
escaped variables are caught before appending data to <code>message</code>. This
is a stateful execution, meaning that the state of this code is either
'escaped' or 'not escaped' using <code>escape_flag</code>.</p>
<div class="highlight"><pre><span></span> <span class="c1"># remove the fletcher16 checksum</span>
<span class="n">f16_check</span> <span class="o">=</span> <span class="n">message</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">256</span>
<span class="n">f16_check</span> <span class="o">+=</span> <span class="n">message</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="c1"># calculate the checksum</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">fletcher16_checksum</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="o">==</span> <span class="n">f16_check</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">rx_messages</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">message</span><span class="p">)</span>
</pre></div>
<p>The last two bytes of the received data contain the Fletcher16 value.
This value is first removed from the message and integrated back to
<code>f16_check</code>. If the calculated checksum value is the same as
<code>f16_check</code>, then the complete processed data is placed into
<code>self.rx_messages</code> for later processing. If the check fails, the
data is discarded.</p>
<h1>Testing the Framing Implementations</h1>
<p>For a test of our implementation, we are sending a short frame from the
micrcontroller to the PC using the presented code.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">txData</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span><span class="mh">0xf7</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mh">0x7f</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mh">0xf6</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">};</span>
<span class="kt">uint16_t</span> <span class="n">txLength</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span>
<span class="n">FRM_push</span><span class="p">(</span><span class="n">txData</span><span class="p">,</span> <span class="n">txLength</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>Note that we chose values in <code>txData</code> so that the escape sequences would
be necessary in order to transmit the data. We displayed the data on the
console using the print statement:</p>
<div class="highlight"><pre><span></span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">247</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">127</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">246</span><span class="p">,</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">]</span>
</pre></div>
<p>Which is exactly what we intended to send, except as a list rather than
an array.</p>
<p>For full implementations, be sure to visit the
<a href="https://github.com/slightlynybbled/project_curve_tracer">github repository</a>.</p>MicroSD Holder2016-03-27T22:55:00-04:002016-03-27T22:55:00-04:00Jason Jonestag:www.forembed.com,2016-03-27:/microsd-holder.html<p><img alt="micro sd case, open" src="http://www.forembed.com/images/2016/03/open_case.jpg" style="height:200px;"></p>
<p>It has been a bit a couple of weeks since I last made a post. I am working on the
next curve tracer post, but the next post covers a fair amount of material and it
takes several decisions to get some of these things going.</p>
<p>I had found myself …</p><p><img alt="micro sd case, open" src="http://www.forembed.com/images/2016/03/open_case.jpg" style="height:200px;"></p>
<p>It has been a bit a couple of weeks since I last made a post. I am working on the
next curve tracer post, but the next post covers a fair amount of material and it
takes several decisions to get some of these things going.</p>
<p>I had found myself gathering MicroSD cards on my desktop for a while and I decided
at some point this week to take a look at <a href="http://www.thingiverse.com">Thingiverse</a>
to find a nice little organizer or holder for MicroSD cards. To my surprise, I
didn't find anything even close to what I was looking for. I did find some holders,
but they were all either mixed with a different type of storage (USB flash, full-
sized SD cards, etc.) or didn't really have a look and feel that I was looking for.</p>
<p>As you probably know at this point, I decided to take a moment to design a MicroSD
card holder that fit my requirements/goals. I wanted a holder that:</p>
<ol>
<li>Holds several cards</li>
<li>Is expandable</li>
<li>Is a small and easy print</li>
</ol>
<p>In the end, I came up with <a href="http://www.thingiverse.com/thing:1447037">a design</a>
that holds up to 5 cards and can have either a lid or another holder stacked on
it to add capacity.</p>
<p><img alt="micro sd case, closed" src="http://www.forembed.com/images/2016/03/closed_case.jpg" style="height:200px;"></p>
<p>I printed this on a Makerbot Thing-O-Matic that has been lightly modified over the
years. Any printer on earth that can be considered a 3D printer should be able to
easily print this part, even with slight overhangs.</p>
<p>In the future, I may take a moment to add slots so that the box may easily be
secured for travel without fear of the lid sliding off. There isn't a large chance
for that to occur, but some would like the extra bit of security.</p>Circular Buffers, Part Two!2016-03-21T19:01:00-04:002016-03-21T19:01:00-04:00Jason Jonestag:www.forembed.com,2016-03-21:/circular-buffers-part-two.html<p><img src="/images/2016/03/circ_buffer.gif" alt="circular buffer animation" class="img-responsive img-rounded"></p>
<p>For some background, go back to read the
<a href="http://www.forembed.com/circular-buffers.html">previous post on circular buffers</a>.</p>
<h1>Mistakes were made</h1>
<p>So, I was implementing a circular buffer on a different project and - lo
and behold - I have made a mistake in the implementation of the circular
buffer! We last visited the circular buffer in …</p><p><img src="/images/2016/03/circ_buffer.gif" alt="circular buffer animation" class="img-responsive img-rounded"></p>
<p>For some background, go back to read the
<a href="http://www.forembed.com/circular-buffers.html">previous post on circular buffers</a>.</p>
<h1>Mistakes were made</h1>
<p>So, I was implementing a circular buffer on a different project and - lo
and behold - I have made a mistake in the implementation of the circular
buffer! We last visited the circular buffer in a
<a href="http://www.forembed.com/circular-buffers.html">post</a> a few weeks ago.
The implementation in github at that time
was fully functional, but it did not use all of the slots available
as was advertised by the last entry. The next version of code has
corrected this mistake.</p>
<h1>The Problem</h1>
<p>The root of the problem is demonstrated by the graphic above. The last
implementation was 'stateless', meaning that it didn't keep track of the
state of the circular buffer. There are two states - BUFFER_EMPTY and
BUFFER_FULL - which look exactly the same on the linear array on which
the circular array is constructed!</p>
<h1>The Solution</h1>
<p>We simply added a state to the circular buffer. If the code is
reading the buffer and the indexes become equal, then the new buffer state
is 'empty'. If you were writing to the buffer and the indexes become
equal, then the new buffer state is 'full'. Easy enough.</p>
<h1>Empty Slots vs. Full Slots</h1>
<p>The library into which I was implementing the circular buffer also had an
additional requirement of knowing how many slots were 'readable' and how
many slots were writeable. We added the functions BUF_emptySlots() and
BUF_fullSlots() so that we could easily extract this information from the
buffer.</p>
<p>Finally, the BUF_read() function was a bit clunky to use since it required
the creation of an element, then the pass-by-reference, and so on. I found
it much easier to understand the BUF_read() function when it simply returned
the next value in the series rather than the buffer status. There is a
function that returns the buffer status if the application requires it, so
there is little need for the read function to return the status.</p>Switched Blog Engines2016-03-18T16:05:00-04:002016-03-18T16:05:00-04:00Jason Jonestag:www.forembed.com,2016-03-18:/switched-blog-engines.html<p>Aaaannnd we're back!</p>
<p>So I have spent some time converting my posts from wordpress to Pelican
and I'm pretty happy with the way things are turning out. There are
still some items that need to be gone through. For instance, you can
still see the marks from the changeover in …</p><p>Aaaannnd we're back!</p>
<p>So I have spent some time converting my posts from wordpress to Pelican
and I'm pretty happy with the way things are turning out. There are
still some items that need to be gone through. For instance, you can
still see the marks from the changeover in the template, but that will
change with a bit of time.</p>
<h1>Why Change the Blog Engine?</h1>
<p>Wordpress is great. For my sister-in-law who also blogs a bit, there is
nothing better out there. I am a bit of a command-line kind of guy. The
truth is that I have been nervous about losing a lot of real work because
of some power blip in india that took down a MySQL server or some hacker
found a security loophole that gave him command of all of WordPress...
including someething of mine.</p>
<p>When I saw at one point that I had over 25 posts, I thought
to myself "I need to change over quickly or it won't even be an option". </p>
<p>It has been bothering me.</p>
<p>I started looking around and found <a href="http://docs.getpelican.com/en/3.6.3/">pelican</a>. I
could go on for a while about the benefits of using a 'static' site,
but here are the things that make me more comfortable:</p>
<ol>
<li>I have complete control over every bracket/period/space in every source file</li>
<li>Does not use a database</li>
<li>Since nearly all content is text-based, it is easy to control and archive using git</li>
<li>Security - what's to secure? Its a bunch of text files and images.</li>
<li>I have complete control (yeah, that is worth mentioning twice)</li>
</ol>
<p>Again, I'm not bagging on WordPress, I just found a continuing unease with the
the vulnerability to a particular platform. In fact, I'm giving some things up
to go to a text format.</p>
<ol>
<li>The Wordpress workflow is a breeze to set up a post</li>
<li>Plugins! Wow, the plugins!</li>
<li>Native user stats are just par for the course</li>
<li>Comment areas built-in</li>
</ol>
<p>When it comes down to it, I just like the control.</p>
<h1>So What's Next</h1>
<p>I am going to continue to improve the 'new' static site. In particular, I want
to pull the categories off of the top row and just leave them to the right. I
also want to remove some of the templating to the right - such as the 'Blogroll' -
and some of the items at the bottom.</p>
<p>I need to fix the tags to the right... apparently the theme is not picking them up. </p>
<p>I just made my <a href="http://www.forembed.com/current-sensor-board-testing-with-a-dc-motor.html">first post since making the changeover</a> today, so
we are moving ahead!</p>
<p>Lots to do, better get to it!</p>Current Sensor Board - Testing with a DC Motor2016-03-18T14:00:00-04:002016-03-18T14:00:00-04:00Jason Jonestag:www.forembed.com,2016-03-18:/current-sensor-board-testing-with-a-dc-motor.html<p>In <a href="http://www.forembed.com/current-sensor-board-testing.html">previous posts</a>,
we tested the DC performance of the current sensor board. In this post,
we are going to test the current sensor board using a typical toy DC
motor as the load.</p>
<h1>DC Motors</h1>
<p>A <a href="https://en.wikipedia.org/wiki/DC_motor">DC motor</a> runs from a DC power
supply, but the motor currents are …</p><p>In <a href="http://www.forembed.com/current-sensor-board-testing.html">previous posts</a>,
we tested the DC performance of the current sensor board. In this post,
we are going to test the current sensor board using a typical toy DC
motor as the load.</p>
<h1>DC Motors</h1>
<p>A <a href="https://en.wikipedia.org/wiki/DC_motor">DC motor</a> runs from a DC power
supply, but the motor currents are typically not very flat due to primarily
to the motor commutating (when the brush moves to a different coil on the
armature). Electrical and magnetic variances play a role as well.</p>
<p>As the motor rotates, the brushes of the motor contact the same points over
and over again. Given a constant load, the variances in the current
waveform are very consistent over a motor rotation, which means that
we could potentially use the motor current frequency to measure speed,
but I digress. We will take a peek into this later.</p>
<p>The point I'm really getting at is that DC motors do not draw a constant
current, regardless of what your multimeter tells you. The current waveform
is very peaky. This is what we want to see with our current sensor.</p>
<h1>Our Setup</h1>
<h2>Board Mod</h2>
<p>The first test that I ran revealed that the gain was a bit high. We had a
33kΩ resistor and the peaks were being cut off. We modified the
resistor to a 22kΩ, which allowed us to complete our test.</p>
<p>Using a 22kΩ resistor, our transimpedance - or volts per amp - is now
11Volts/Amp.</p>
<h2>Circuit</h2>
<p><img src="/images/2016/03/current_sense_dc_schematic.png" alt="dc motor and current sensor schematic" class="img-responsive img-rounded"></p>
<h3>C1 on Vps</h3>
<p>The first test that we will run will be with C1 placed between the power
supply voltage and ground. This test will show the motor peak currents
very clearly since all motor current will be going through the shunt
resistor on the current sensor board.</p>
<h3>C1 across Motor</h3>
<p>The second test we will run is with C1 across the motor. In this
situation, we expect C1 to supply a majority of the instantaneous
current requirements of the motor and the current sensor will
detect a much more average value of current.</p>
<h3>Breadboard Setup</h3>
<p>For ease, we ran this experiment on a typical breadboard. Our
typical expected currents are less than 300mA, so the board contact
resistances shouldn't interfere with the result.</p>
<p><img src="/images/2016/03/breadboard_dc_motor_test.jpg" alt="dc motor on breadboard circuit" class="img-responsive img-rounded"></p>
<h1>Results</h1>
<h2>C1 on Vps</h2>
<p>Our first picture reveals a high peak-to-peak current (blue) and a
slightly wavy voltage across the motor terminals (yellow). The
voltage difference is due to the peak currents through the motor.<br>
Since we have a resisor in line with the motor, as the current
increases, the resistor voltage increases, decreasing the voltage
across the motor. Notice how the peaks in current correspond to
the dips in the voltage.</p>
<p><img alt="scope capture, C1 on Vps" src="http://www.forembed.com/images/2016/03/dc_motor_vi.png"></p>
<p>Our next picture has some measurements. Earlier, we stated that the
current waveforms are repetitive. You can see that in these scope
captures. With a 2-pole, 3-slot configuration (most common in toy
motors), the cycle repeats every 6 peaks. Each peak corresponds to
one commutation of the motor. Six commutations result in one revolution
of the motor.</p>
<p><img src="/images/2016/03/dc_motor_current_ipp.png" alt="DC motor current waveform" class="img-responsive img-rounded"></p>
<p>The mechanical period of this motor is 7.14ms. Thus the speed is:</p>
<p>frequency = 1/7.14ms = 140Hz
speed_in_rpm = 140rotations/sec * 60sec/min = 8403rotations/min</p>
<p>Not bad! You could do the same math on the period of the current
peaks, but these peaks aren't nearly as consistent as the rotational
period. If you measure the motor period, just count out 6 peaks and
you will get a much more consistent value. Otherwise, your PID
loop will get wrong input values for the speed and you will notice
a vibration in your motor that you are actually causing!</p>
<p>Finally, our highest and lowest voltages measured are 4.0V and 960mV
for a peak-to-peak voltage of 3.04V. Translating to current:</p>
<p>Ihigh = 364mA</p>
<p>Ilow = 87mA</p>
<p>Ipp = 277mA</p>
<p>That is quite a variation in current! If you are running your
arduino on the same circuit as this motor, you'd better do something
about that!</p>
<h2>C1 across Motor</h2>
<p>We have made one change to our schematic. Instead of C1 being placed
across Vps and ground, we have placed it directly across the motor.</p>
<p><img src="/images/2016/03/dc_motor_cap_across_load.png" alt="DC motor current waveform" class="img-responsive img-rounded"></p>
<p>You can see a dramatic change in the current waveform. Note that the
rotational variation still repeats, but our current is much closer to
the average current.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Parameter</th>
<th>C<sub>1</sub> on V<sub>ps</sub></th>
<th>C<sub>1</sub> on Motor</th>
</tr>
<tr>
<td>I<sub>high</sub></td>
<td>364mA</td>
<td>221mA</td>
</tr>
<tr>
<td>I<sub>low</sub></td>
<td>87mA</td>
<td>160mA</td>
</tr>
<tr>
<td>I<sub>pp</sub></td>
<td>277mA</td>
<td>61mA</td>
</tr>
</table>
<p>What a difference!</p>
<p>Notice also that the voltage across the motor (yellow) is nearly constant.</p>
<h1>Conclusions</h1>
<p>We can easily measure motor current with the current sensor board.
Additionally, the characteristic waveforms of the motor current can
reveal aspects of motor operation that may not even be apparent to
the user.</p>
<p>It is also important to note the importance of capacitance in
protecting your cirucit from the large noise spikes caused by the
typical DC motor. One need only look at the plots to see the
huge differences between the two configurations.</p>Project Curve Tracer: Configuring the UART2016-03-11T16:13:00-05:002016-03-11T16:13:00-05:00Jason Jonestag:www.forembed.com,2016-03-11:/project-curve-tracer-configuring-the-uart.html<h1>Intro</h1>
<p><img src="/images/2016/03/ft230x_schem_capture.png" alt="FT230X Schematic Capture" class="img-responsive img-rounded"></p>
<p>We have the most basic elements of the curve tracer working, but we have no
way to interface with it other than hooking up the Microchip debugger
and downloading array variables. In this installment, we are going to
make the first steps toward getting data to the PC using …</p><h1>Intro</h1>
<p><img src="/images/2016/03/ft230x_schem_capture.png" alt="FT230X Schematic Capture" class="img-responsive img-rounded"></p>
<p>We have the most basic elements of the curve tracer working, but we have no
way to interface with it other than hooking up the Microchip debugger
and downloading array variables. In this installment, we are going to
make the first steps toward getting data to the PC using the UART. You
may know this better as a USB serial port.</p>
<h1>Data Requirements</h1>
<p>We are operating on a human time scale, so our data requirements are
likely not very high. Going through a few data points to get us started
on a requirements calculation:</p>
<ul>
<li>It takes two bytes/sample</li>
<li>Both current and voltage must be sampled</li>
<li>The lowest frequency setting will probably be around 60Hz</li>
<li>The waveform generator is operating with a period of 83.2μs</li>
</ul>
<p>The lowest frequency will have the most points to transfer. Our 60Hz
signal has a period of 16.7ms. This means that there are</p>
<p>16.7ms/83.2μs = 200.7 points to save</p>
<p>With 200 points, 2 bytes/point, and both current and voltage to save:</p>
<p>200points * 2bytes/point * 2terms * 8bits/byte = 6400 bits</p>
<p>We have 6400 bits to send per waveform. If we wanted to send all 6400
bits on every waveform, we would be sending:</p>
<p>6400bits/waveform * 1/16.7ms = 400000bits/s</p>
<p>That is quite a number! We will likely do some averaging and filtering
on the uC, and a person doesn't need a refresh rate higher than 8 times
per second on a periodic waveform, so lets make that assumption:</p>
<p>6400bits/waveform * 8 = 51200bits/s</p>
<p>As it happens, there is a 'standard' value that is very close to this
value at 57600 bits/s. We will select this for now, knowing that we may
need or want to modify it later.</p>
<h1>Soldering the Parts</h1>
<p><img src="/images/2016/03/curve_tracer_soldered_board.jpg" alt="curve tracer soldered board" class="img-responsive img-rounded"></p>
<p>We haven't bothered populating the FT230X part until now largely because it
wasn't necessary and might actually end up compromising our ability to
debug the board and software. We now wish to populate the FT230X, the
USB connector, R9 and R10. The schematic says that we will populate R9
and R10 with 27Ω resistors, but I didn't have any, so I populated with
0Ω resistors. That seems to have worked out OK.</p>
<h1>Setting up the Registers</h1>
<h2>Baud Rate Generator</h2>
<p><img src="/images/2016/03/uxbrg_equation.png" alt="equation for BRG" class="img-responsive img-rounded"></p>
<p>The baud rate generator for this device operates in two modes, 'standard'
and 'high-speed'. Generally, we want standard whenever possible because
high-speed mode is prone to more error. We get the above equation from
the datasheet. Using 12MIPS as the FCY and 57600 as the baud rate, we
calculate 12.02 as the value for U1BRG. Since this is nearly an exact
integer, we will have very little error. If the value had been 12.5, we
may have wanted to select a different baud rate that fell closer to an
integer value to reduce the error.</p>
<h2>Setup of Registers</h2>
<p>In most peripherals, the setup order doesn't matter. With this
particular peripheral, the <strong>setup order matters</strong>. I spent an hour
trying to figure out why my setup wasn't working and it turns out that
it was the order that I had executed the statements in. Here is the
order you should follow for the UART peripheral:</p>
<ol>
<li>Setup the ANSB bits to a digital state</li>
<li>Setup the core UART registers with the UART disabled and UTXEN
disabled</li>
<li>Setup interrupts</li>
<li>Enable UART with UARTEN</li>
<li>Enable UTXEN</li>
</ol>
<p>If you don't follow this order, the peripheral won't function! I'm not
sure why, but it doesn't. Resulting code:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">initUart</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB2</span> <span class="o">=</span> <span class="n">DIO_DIGITAL</span><span class="p">;</span>
<span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB7</span> <span class="o">=</span> <span class="n">DIO_DIGITAL</span><span class="p">;</span>
<span class="cm">/* baud rate = 57600bps </span>
<span class="cm"> * U1BRG = (12000000/(16*57600)) - 1 = 12.02 = 12</span>
<span class="cm"> */</span>
<span class="n">U1BRG</span> <span class="o">=</span> <span class="mi">12</span><span class="p">;</span>
<span class="n">U1MODE</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span> <span class="c1">// TX/RX only, standard mode</span>
<span class="n">U1STA</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span> <span class="c1">// enable TX</span>
<span class="cm">/* uart interrupts */</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">U1TXIF</span> <span class="o">=</span> <span class="n">IFS0bits</span><span class="p">.</span><span class="n">U1RXIF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">IEC0bits</span><span class="p">.</span><span class="n">U1TXIE</span> <span class="o">=</span> <span class="n">IEC0bits</span><span class="p">.</span><span class="n">U1RXIE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">U1MODEbits</span><span class="p">.</span><span class="n">UARTEN</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">U1STAbits</span><span class="p">.</span><span class="n">UTXEN</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>For testing purposes, I initially configured the receive interrupt to
echo its value through the transmit register. This way, when I type a
character into the terminal (we will get to that), it will be displayed
on the screen.</p>
<p>The test environment tends to 'skid' in MPLAB X, so setting a break
point at a particular line is not very reliable. I added several Nop()
function (no operation) and set a break point on the top. This allowed
me to test the transmit interrupt as well.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_U1TXInterrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">Nop</span><span class="p">();</span>
<span class="n">Nop</span><span class="p">();</span>
<span class="n">Nop</span><span class="p">();</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">U1TXIF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_U1RXInterrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">U1TXREG</span> <span class="o">=</span> <span class="n">U1RXREG</span><span class="p">;</span> <span class="c1">// echo</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">U1RXIF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<h1>Connecting to the Terminal</h1>
<h2>Terminal Download</h2>
<p>If you don't currently have a terminal program, there are several out
there. My favorite for windows is
<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html">PuTTY</a>.
When I'm on Linux, I use minicom (apt-get install minicom), but I have
to look things up every time to use it. You won't have to install
PuTTY, just download it and execute.</p>
<h2>Terminal Setup</h2>
<p><img src="/images/2016/03/putty_config_57600.png" alt="PuTTY Configuration" class="img-responsive img-rounded"></p>
<p>Open PUTTY.EXE and enter the appropriate values under the 'Session' tab. My
PC has populated the USB serial port as 'COM9', but yours will likely be
different. Press the 'Open' button at the bottom and you should see a
black box with a small green square in it. If everything is working
correctly, you should be able to type in that box. What is actually
happening:</p>
<ol>
<li>You type a letter into the terminal</li>
<li>The terminal converts that letter into its numeric equivalent and
sends that to your board</li>
<li>Your board generates an interrupt, executing the _U1RXInterrupt()
function</li>
<li>The interrupt function reads the byte from the receive register and
sends it to the transmit register</li>
<li>The terminal receives the number and displays it to the user</li>
</ol>
<p><img src="/images/2016/03/putty_working.png" alt="PuTTY working confirmation" class="img-responsive img-rounded"></p>
<p>You now have the capability to send any 8-bit number between the PC and
the microcontroller! Pretty cool, huh?</p>
<h1>Next Steps</h1>
<p>This post marks the end of hardware verification. We have soldered all
components to the board and verified basic functionality of all
elements. In our next post, we will review what we have done thus far
and make the minor corrections to the schematic and layout that have
been noted throughout the project, but haven't fully been collected to
this point.</p>
<p>After completing the next round of schematic and layout, we will move
almost exclusively to the software realm. We still have some work to do
with the serial interface.</p>Project Curve Tracer: Configuring the Oscillator2016-03-10T17:15:00-05:002016-03-10T17:15:00-05:00Jason Jonestag:www.forembed.com,2016-03-10:/project-curve-tracer-configuring-the-oscillator.html<p>If you have no idea what this is about, go back to the <a href="http://www.forembed.com/project-curve-tracer-requirements.html">Curve Tracer
Requirements
Post</a>!</p>
<h1>Intro</h1>
<p><img src="/images/2016/03/clock.gif" alt="clock signal" class="img-responsive img-rounded"></p>
<p>In previous posts, we have been using the maximum speed 32MHz (16MIPS)
oscillator. This oscillator is prone to some error and we would like to
eliminate that error. In this post, we …</p><p>If you have no idea what this is about, go back to the <a href="http://www.forembed.com/project-curve-tracer-requirements.html">Curve Tracer
Requirements
Post</a>!</p>
<h1>Intro</h1>
<p><img src="/images/2016/03/clock.gif" alt="clock signal" class="img-responsive img-rounded"></p>
<p>In previous posts, we have been using the maximum speed 32MHz (16MIPS)
oscillator. This oscillator is prone to some error and we would like to
eliminate that error. In this post, we are going to use the FTDI
calibrated oscillator at 24MHz to drive our chips clock input.</p>
<h1>Setting up FTDI Chip</h1>
<h2>Desired FTDI Setup</h2>
<p><img src="/images/2016/03/ft230x_cbus_pins.png" alt="FT230X CBUS pins" class="img-responsive img-rounded"></p>
<p>The first thing that you must do is set up the FTDI chip itself so that it
outputs the 24MHz waveform desired. The part we are using is the
<a href="http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT230X.pdf">FT230X</a> (pdf
warning). We must download the <a href="http://www.ftdichip.com/Support/Utilities.htm#FT_PROG">FT_PROG
utility</a> from the
FTDI site before going too far as well. This utility allows us to flash
the EEPROM of the FTDI board, which sets up the FTDI pins. The default
configuration is:</p>
<ul>
<li>CBUS0 = TXDEN</li>
<li>CBUS1 = RXLED</li>
<li>CBUS2 = TXLED</li>
<li>CBSU3 = SLEEP</li>
</ul>
<p>We aren't using RXLED or TXLED, so those outputs defaults are correct
as-is. Additionally, the SLEEP pin is correct by default as well. The
only pin that we need to change is CBUS0.</p>
<h2>Changing the EEPROM</h2>
<p>Plug the USB device into your PC. Allow the drivers to install.</p>
<p>The FT_PROG utility appears to only work in Windows. I didn't try it
in Linux. When it first opens, it is pretty blank. Go to DEVICES ->
Scan and Parse in order to have a look at your device options.</p>
<p><img src="/images/2016/03/ftprog_screen_capture.png" alt="FTPROG screen capture" class="img-responsive img-rounded"></p>
<p>My scan resulted in two chips. Only one of the two devices was of the
FT230X type, so that was easy enough to identify. I verified by
unplugging, scanning, and re-plugging and re-scanning.</p>
<p>Click down into the tree, Hardware Specific -> CBUS Signals. When you
click on 'CBUS Signals', the right pane will display the Cx signals
under 'Property' and their current values under 'Value'. Click the
drop-down box by 'C0' and select the 'CLK24MHz' option. Then click the
lightning bolt shortcut at the top of the window to program.</p>
<p>In the 'Program Devices' dialog box that comes up, ensure that the
proper device is selected and click the 'Program' button.</p>
<h2>Verifying Output Frequency</h2>
<p>Of course, we can't just flash the EEPROM and call it done, we have to
verify that it worked! Using the oscilloscope, we can see the the pin
is outputting the expected 24MHz frequency.</p>
<p><img src="/images/2016/03/osc_24mhz.png" alt="24MHz oscillator scope capture" class="img-responsive img-rounded"></p>
<h1>Micro-Controller Code</h1>
<h2>Before you get started...</h2>
<p>You are using some tool to power your board. Up to this point, I have
been using the MPLAB ICD3 to power the board. We are about to change to
USB power, so before you connect your programmer, go to the ICD3 options
and turn off the ICD3 power supply.</p>
<h2>Its all in the Configuration</h2>
<p>Up to this point, we have been using the internal oscillator to perform
operations on the uC. We would like to use the clock input pin instead.
We should only need to change the configuration bits. FNOSC should be
changed to 'PRI' to select the primary oscillator (instead of the
internal oscillator). POSCMOD should be changed to 'EC' in order to
select the external clock as the primary oscillator.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="c1">// FOSCSEL</span>
<span class="cp">#pragma config FNOSC = PRI </span><span class="c1">//(changed to PRI) </span>
<span class="cp">#pragma config SOSCSRC = ANA </span>
<span class="cp">#pragma config LPRCSEL = LP </span>
<span class="cp">#pragma config IESO = ON</span>
<span class="c1">// FOSC</span>
<span class="cp">#pragma config POSCMOD = EC </span><span class="c1">//(changed to EC)</span>
<span class="cp">#pragma config OSCIOFNC = CLKO </span>
<span class="cp">#pragma config POSCFREQ = HS </span>
<span class="cp">#pragma config SOSCSEL = SOSCHP </span>
<span class="cp">#pragma config FCKSM = CSECME</span>
</pre></div>
</td></tr></table>
<p>Window -> PIC Memory Views -> Configuration bits will get you to a window
which will help you select the options using drop-down menus. Once your
options are selected, click 'Generate Souce Code to Output' and
copy/paste as necessary into 'main.c'.</p>
<h2>Verification</h2>
<p>Using the oscillocope, we can verify that the frequency has changed by
simply looking at the period at which the output is updated. Measuring
pre-configuration change yields an update of the output waveform every
62.8μs. After the change, the update occurs every 83.2μs. This holds
the same ratio as 12/16, so we are good. We are now connected to USB
power and are using the FTDI 24MHz calibrated clock as our clock input!</p>
<p>Our next post will (probably) be setting up the serial port function so
we can finally begin speaking with the PC.</p>Project Curve Tracer - Implementing the Task Manager2016-03-04T00:44:00-05:002016-03-04T00:44:00-05:00Jason Jonestag:www.forembed.com,2016-03-04:/project-curve-tracer-implementing-the-task-manager.html<h1>Intro</h1>
<p><img src="/images/2016/03/task_list.png" alt="task list graphic" class="img-responsive img-rounded"></p>
<p>From this point on, there are various tasks that need to occur
deterministically, but not necessarily via an interrupt. We are going
to implement a task manager for this purpose. For readers, this may be
familiar. We already implemented the <a href="http://www.forembed.com/the-not-quite-rtos-a-task-manager.html">task manager for the
MSP430</a> a
few weeks ago …</p><h1>Intro</h1>
<p><img src="/images/2016/03/task_list.png" alt="task list graphic" class="img-responsive img-rounded"></p>
<p>From this point on, there are various tasks that need to occur
deterministically, but not necessarily via an interrupt. We are going
to implement a task manager for this purpose. For readers, this may be
familiar. We already implemented the <a href="http://www.forembed.com/the-not-quite-rtos-a-task-manager.html">task manager for the
MSP430</a> a
few weeks ago in a much simpler application. In this article, we are
going to be putting several 'tasks' which will function independently,
deterministically, and at different intervals.</p>
<h1>Timing</h1>
<p>There are several types of applications that require processing, but
infrequent processing at fixed intervals. A PID loop is a perfect
example. When the number of tasks is very low, then this type of
processing is usually done using a dedicated timer interrupt. When the
number escalates, then we find that we run out of timers quickly on most
processors. This is the problem that the task manager was created to
solve.</p>
<p>I typically only use the task manager for tasks that need to execute
less frequently than 1kHz. This is my desired 'sytem tick'. Faster
tasks should get their own timer interrupt.</p>
<h2>Task Manager</h2>
<p>To execute timing to 1ms precision, we are going to use the <a href="http://www.forembed.com/the-not-quite-rtos-a-task-manager.html">embedded
task
manager</a>.
Specifically, we are going to copy the 'task.h' and 'task.c' files from
the <a href="https://github.com/slightlynybbled/embedded_task_manager">github
repository</a>
and modify them somewhat to use the PIC24 timer peripheral instead of
the MSP430.</p>
<p>We are using timer 1 already for sine wave generation and CCP timers 1
and 2 for PWM generation, so we will be forced to use CCT3 as the time
base for our timer interrupt. Note that there are multiple interrupts
associated with the CCP/CCT module, you want the CCT interrupt! Also
note, neither of these is documented, I found it by guessing and
checking. In the below code, the green indicates where code has been
added:</p>
<p>Change the include from 'lib430.h' to</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><xc.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf">"task.c"</span><span class="cp"></span>
</pre></div>
</td></tr></table>
<p>Adding a 'TMR_init()' function specific to the PIC24 which takes a
function pointer as a parameter. We are going to simply add this
function at the bottom of the 'task.c'</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">TMR_init</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)()){</span>
<span class="n">TMR_timedFunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span>
<span class="cm">/* period registers */</span>
<span class="n">CCP3PRH</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">CCP3PRL</span> <span class="o">=</span> <span class="mi">16000</span><span class="p">;</span>
<span class="n">CCP3CON1L</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span> <span class="c1">// timer mode</span>
<span class="n">CCP3CON1H</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">CCP3CON2L</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">CCP3CON2H</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">CCP3CON3L</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">CCP3CON3H</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">IFS1bits</span><span class="p">.</span><span class="n">CCT3IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">IEC1bits</span><span class="p">.</span><span class="n">CCT3IE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">CCP3CON1Lbits</span><span class="p">.</span><span class="n">CCPON</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Adding the 'TMR_disableInterrupt()' function specific to the PIC24.
Again, we will simply include this at the bottom of the 'task.c'</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">TMR_disableInterrupt</span><span class="p">(){</span>
<span class="n">IEC1bits</span><span class="p">.</span><span class="n">CCT3IE</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Adding the PIC24-specific interrupt</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_CCT3Interrupt</span><span class="p">(){</span>
<span class="k">if</span><span class="p">(</span><span class="n">TMR_timedFunctPtr</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="o">*</span><span class="n">TMR_timedFunctPtr</span><span class="p">)();</span>
<span class="n">IFS1bits</span><span class="p">.</span><span class="n">CCT3IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Adding a ClrWdt() to the 'TASK_manage()' function since it never
returns. This clears the watchdog timer, which is something we have
been doing in 'main()' of our current code
base.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">TASK_manage</span><span class="p">(){</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">){</span>
<span class="kt">uint16_t</span> <span class="n">i</span><span class="p">;</span>
<span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">MAX_NUM_OF_TASKS</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
<span class="kt">uint32_t</span> <span class="n">time</span> <span class="o">=</span> <span class="n">TASK_getTime</span><span class="p">();</span>
<span class="k">if</span><span class="p">(</span><span class="n">task</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">taskFunctPtr</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">time</span> <span class="o">>=</span> <span class="n">task</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">nextExecutionTime</span><span class="p">){</span>
<span class="n">task</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">nextExecutionTime</span> <span class="o">=</span> <span class="n">task</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">period</span> <span class="o">+</span> <span class="n">time</span><span class="p">;</span>
<span class="p">(</span><span class="n">task</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">taskFunctPtr</span><span class="p">)();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">ClrWdt</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Now that we have modified 'task.c' as required, we need to modify our
'main()' function so that it properly starts the task manager. Again,
green indicates additions to the code, blue indicates changes:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">initOsc</span><span class="p">();</span>
<span class="n">initLowZAnalogOut</span><span class="p">();</span>
<span class="n">initInterrupts</span><span class="p">();</span>
<span class="n">initPwm</span><span class="p">();</span>
<span class="n">initAdc</span><span class="p">();</span>
<span class="n">initUart</span><span class="p">();</span>
<span class="n">TASK_init</span><span class="p">();</span>
<span class="cm">/* set the initial duty cycles */</span>
<span class="n">setDutyCycleHZ1</span><span class="p">(</span><span class="mi">16384</span><span class="p">);</span>
<span class="n">setDutyCycleHZ2</span><span class="p">(</span><span class="mi">8192</span><span class="p">);</span>
<span class="n">TASK_manage</span><span class="p">();</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The task manager setup is complete! Now, all we have to do is create a
function and add it to the task manager!</p>
<h2>Adding a Task</h2>
<p>Adding a task is almost as simple as adding a function. We are going to
add two tasks:</p>
<ol>
<li>One task to control the HZ1 voltage, and</li>
<li>Another task to control the HZ2 voltage</li>
</ol>
<p>We could place both of these into a single task, but the time may come
when we wish to change the frequency of each of them independently.
Besides, doing it this way allows us to focus on one task at a time.</p>
<p>Declare the function like any other function.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">hz1Control</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">hz2Control</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>Write the function implementation(s):</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8
9</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">hz1Control</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">hz2Control</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Add the function in 'main()' with the proper timing specified, 20ms
in this case. The tasks must be added after 'TASK_init()' and can
be added or removed at any time after that.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">initOsc</span><span class="p">();</span>
<span class="n">initLowZAnalogOut</span><span class="p">();</span>
<span class="n">initInterrupts</span><span class="p">();</span>
<span class="n">initPwm</span><span class="p">();</span>
<span class="n">initAdc</span><span class="p">();</span>
<span class="n">initUart</span><span class="p">();</span>
<span class="n">TASK_init</span><span class="p">();</span>
<span class="cm">/* set the initial duty cycles */</span>
<span class="n">setDutyCycleHZ1</span><span class="p">(</span><span class="mi">16384</span><span class="p">);</span>
<span class="n">setDutyCycleHZ2</span><span class="p">(</span><span class="mi">8192</span><span class="p">);</span>
<span class="cm">/* add tasks */</span>
<span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">hz1Control</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
<span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">hz2Control</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
<span class="n">TASK_manage</span><span class="p">();</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>At this point, we have two tasks that are doing nothing. We will go
just a bit further to verify the functionality of our task manager. We
will toggle a pin within 'hz1Control()':</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">hz1Control</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">LATBbits</span><span class="p">.</span><span class="n">LATB7</span> <span class="o">^</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Resulting in the scope capture:</p>
<p><img src="/images/2016/03/interrupt_timing_20ms.png" alt="pin toggle every 20ms capture" class="img-responsive img-rounded"></p>
<p>We measure a near-perfect 20ms between pin toggles, which is exactly
what we were expecting.</p>Current Sensor Board - Testing2016-03-03T14:17:00-05:002016-03-03T14:17:00-05:00Jason Jonestag:www.forembed.com,2016-03-03:/current-sensor-board-testing.html<p>For a project introduction, go back to the <a href="http://www.forembed.com/creating-the-current-sensor-board.html">first
post</a>!</p>
<h1>Intro</h1>
<p>So, if you are already familiar with the project, you know that I was
looking for a quick general-purpose current-to-voltage converter with a
low-impedance output. I received boards this weekend and was able to
solder together the module today …</p><p>For a project introduction, go back to the <a href="http://www.forembed.com/creating-the-current-sensor-board.html">first
post</a>!</p>
<h1>Intro</h1>
<p>So, if you are already familiar with the project, you know that I was
looking for a quick general-purpose current-to-voltage converter with a
low-impedance output. I received boards this weekend and was able to
solder together the module today. Doesn't look like a million bucks,
but works just fine.</p>
<p><img src="/images/2016/03/sensor_board_picture.jpg" alt="current sensor breadboard picture" class="img-responsive img-rounded"></p>
<p>All files may be found on
<a href="https://github.com/slightlynybbled/unidir_current_sense_module">github</a>. There are no
patch wires on this version, so files within the master branch should be pretty safe. I
may do a release tag at some point to indicate as much.</p>
<h1>Module Rating and Gain Settings</h1>
<p>When I went to order parts, I realized that I should order parts for a
variety of current ranges.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Rsense</th>
<th>Rgain</th>
<th>Rated Current</th>
<th>Transimpedance</th>
</tr>
<tr>
<td>0.05Ω</td>
<td>33kΩ</td>
<td>3.0A</td>
<td>1.65V/A</td>
</tr>
<tr>
<td>0.5Ω</td>
<td>33kΩ</td>
<td>300mA</td>
<td>16.5V/A</td>
</tr>
<tr>
<td>4.7Ω</td>
<td>22kΩ</td>
<td>30mA</td>
<td>103.4V/A</td>
</tr>
<tr>
<td>47Ω</td>
<td>22kΩ</td>
<td>3mA</td>
<td>1034V/A</td>
</tr>
</table>
<p>The gain resistor should be set to 33kΩ for 5V devices and 22kΩ for 3.3V
devices. This table simply summarizes a few valid configurations that
the user might find necessary. If you would like to suggest a
combination, leave a comment.</p>
<p>I am choosing the second combination for my initial tests. We will be
testing more configurations later! The transimpedance is 16.5V/A and
the rating is 0.3A, which should place many small devices within its
range.</p>
<h1>Testing</h1>
<p><img src="/images/2016/03/current_sensor_module.png" alt="current sensor module" class="img-responsive img-rounded"></p>
<p>A quick explanation of module functionality is in order. There are two
pins dedicated to power supply (Vps) and two pins dedicated to the load
(Vld). Current should flow from Vps to Vld. Reverse current will not
damage the device, but the sense current will not be accurate either.</p>
<p>Vcc should be the same voltage as your sense circuit. Generally, this
will be 3.3V or 5V. This voltage can be anything between 2V and 5.5V.</p>
<p>Vsns is a high-impedance output. The only use for this pin is to add a
filter capacitor. In most applications, this pin should be left
floating.</p>
<p>Vbuf is the low-impedance, buffered output that is suitable for most
ADCs. Vsns and Vbuf should reflect the same voltage at all times.</p>
<h2>DC Testing</h2>
<p><img src="/images/2016/03/current_sensor_module_schem.png" alt="current sensor module schematic symbol" class="img-responsive img-rounded"></p>
<p>The first and easiest test is to apply a known resistance load to the module
and to calculate the current that would flow through that resistor,
comparing the calculated current with the measured current. We will
apply 5V to Vcc and to the power supply pin (Vps) of the module. Our
load will be a nominal 100Ω load. Measuring the resistor with a
multimeter actually shows a 98.4Ω load. Measuring the power supply
yields a 5.05V PSU. We will use these numbers instead of the nominal
values.</p>
<p>5.05V/98.4Ω = 51.32mA</p>
<p>51.32mA * 16.5V/A = 846.8V</p>
<p><img src="/images/2016/03/scope_capture_0A.png" alt="scope capture 0A" class="img-responsive img-rounded"></p>
<p>At 0A (no resistor load), the voltage is very low, but there is an
oscillation visible on the output of the opamp. This oscillation is
also visible on the sensor output and is NOT present on the power supply
voltage. As a result, I can only conclude that the oscillation is
caused by the internals of the INA139.</p>
<p>The addition of a 0.1μF capacitor to the Vsns output greatly attenuates
this noise. We could add more capacitance and squelch the noise
completely; however, this would reduce our circuit responsiveness.</p>
<p><img src="/images/2016/03/scope_capture_0A_with_cap.png" alt="scope capture 0A with cap" class="img-responsive img-rounded"></p>
<p>Now we apply our resistor to the circuit.</p>
<p><img src="/images/2016/03/scope_capture_51mA.png" alt="scope capture 51mA" class="img-responsive img-rounded"></p>
<p>The average voltage out with the 98.4Ω load is 863mV. When compared with
our expected 846.8V, this value is 1.9% high. Some portion of this
inaccuracy is expected since our device is a 0.5% device, the sense
resistor is a 1%, and the gain resistor is also 1%. When the tolerances
are stacked up, the circuit should perform to +/- 2.5%. The circuit is
behaving within tolerance on this application.</p>
<h2>Step Response Testing</h2>
<p>The datasheet shows a nice step response, with the INA139 nearly to full
voltage within \~20μs of the change in current. The TI test is at 12V,
which may explain the difference, but I see no characterization of
output frequency vs. input voltage. This would be nice to have!</p>
<p><img src="/images/2016/03/datasheet_step_response.png" alt="datasheet step response" class="img-responsive img-rounded"></p>
<p>Just another quick note, I did check both Vsns and Vbuf outputs.</p>
<p><img alt="positive step response 2.96ms response" src="http://www.forembed.com/images/2016/03/pos_step_response.png"></p>
<p><img alt="negative step response 3.0ms response" src="http://www.forembed.com/images/2016/03/neg_step_response.png"></p>
<p>The positive step response is measured at 2.96ms and the negative step
response is measured at 3.0ms.</p>
<h1>Summary</h1>
<p>The breakout module is suitable for measuring positive shunt currents at
relatively low frequency. The datasheet indicates a faster frequency
response than we observed, therefore, it is possible that the specific
resistor combinations that were chosen are limiting the frequency
somehow. Using the rule-of-thumb:</p>
<p>freq = 0.35/riseTime</p>
<p>It was found that our cutoff frequency is ~116Hz. As a result, I would
recommend this module with its current resistor configuration for
applications that need performance up to 100Hz.</p>
<h1>I Want One!</h1>
<p>I have several of these boards. If you want one, just <a href="http://www.forembed.com/pages/contact-us.html">drop me a
line</a> and I'm sure that we can work
something out for a parts kit or soldered module.</p>Project Curve Tracer - ADC Input2016-03-02T18:56:00-05:002016-03-02T18:56:00-05:00Jason Jonestag:www.forembed.com,2016-03-02:/project-curve-tracer-adc-input.html<h1>Intro</h1>
<p>Up to this point, we have focused on generating signals without
necessarily observing those signals. In this post, we will begin to
give the microcontroller the capacity to observe those signals using the
Analog-to-Digital Converter (ADC).</p>
<h1>ADC Requirements</h1>
<p>There are 5 signals to be observed:</p>
<ol>
<li>LD_VOLTAGE_0 - this is the …</li></ol><h1>Intro</h1>
<p>Up to this point, we have focused on generating signals without
necessarily observing those signals. In this post, we will begin to
give the microcontroller the capacity to observe those signals using the
Analog-to-Digital Converter (ADC).</p>
<h1>ADC Requirements</h1>
<p>There are 5 signals to be observed:</p>
<ol>
<li>LD_VOLTAGE_0 - this is the 'high-side' voltage of the load</li>
<li>LD_VOLTAGE_1 - this is the 'low-side' voltage of the load</li>
<li>CURRENT - this is the output of the U1 circuit, which is a voltage
proportional to the load current</li>
<li>HZ_OUT_1 - this is the bias voltage of the differential opamp</li>
<li>HC_OUT_2 - this is currently unused, but will eventually be the
bias voltage for the base/gate drive circuit</li>
</ol>
<p>Ideally, all of these signals would be simultaneously sampled.
Unfortunately, we don't have that capability, but we do have a
reasonably fast A/D converter at 100k samples/s. If we run the A/D at
maximum speed, we can achieve 10μs spacing between each sample and have
a complete dataset every 50μs, which limits our maximum possible sample
rate to 20kHz. The highest sample rate that we require is at the Timer
1 frequency used to generate the DAC signal. In the
<a href="http://www.forembed.com/project-curve-tracer-dac-initialization-and-utilization.html">DAC post</a>,
we found that the frequency of T1 which generates a pretty good waveform
is 1.3kHz, so this is well within the capabilities of a 20kHz ADC.</p>
<h1>Setting Up</h1>
<h2>Following the Example</h2>
<p>I like to start at a 'known good' place. I went to the <a href="http://www.microchip.com/wwwproducts/en/PIC24F16KM202">Microchip site
for this
processor</a>, down
to documentation, and looked at the <a href="http://ww1.microchip.com/downloads/en/DeviceDoc/39739b.pdf">A/D
document</a>.
They had Example 51-6, which is supposed to set up the converter to
sample/convert a single channel continually:</p>
<p><img alt="A/D Microchip example code" src="http://www.forembed.com/images/2016/03/microchip_sample_adc_code.png"></p>
<p>Unfortunately, the example doesn't work. In AD1CON1, they have a value
of 0x00E0 whereas a value of 0x0070 is what you require in order to use
the internal counter to trigger the conversion.</p>
<p>I started using the sample code for setup, but instead of placing all of
the garbage that they have in the 'while' loop, I placed the portion
that I require into an interrupt. I also set up the initialization to
start on channel 1 instead of channel 0. I wrapped all of this up and
placed it into a function 'initAdc()'.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">initAdc</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* set up the analog pins as analog inputs */</span>
<span class="n">TRISAbits</span><span class="p">.</span><span class="n">TRISA1</span> <span class="o">=</span>
<span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB0</span> <span class="o">=</span>
<span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB4</span> <span class="o">=</span>
<span class="n">TRISAbits</span><span class="p">.</span><span class="n">TRISA4</span> <span class="o">=</span>
<span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB6</span> <span class="o">=</span> <span class="n">DIO_INPUT</span><span class="p">;</span>
<span class="n">ANSAbits</span><span class="p">.</span><span class="n">ANSA1</span> <span class="o">=</span>
<span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB0</span> <span class="o">=</span>
<span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB4</span> <span class="o">=</span>
<span class="n">ANSAbits</span><span class="p">.</span><span class="n">ANSA4</span> <span class="o">=</span>
<span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB6</span> <span class="o">=</span> <span class="n">DIO_ANALOG</span><span class="p">;</span>
<span class="n">AD1CON1</span> <span class="o">=</span> <span class="mh">0x0270</span><span class="p">;</span> <span class="cm">/* Internal counter triggers conversion</span>
<span class="cm"> * FORM = left justified */</span>
<span class="n">AD1CON2</span> <span class="o">=</span> <span class="mh">0x003C</span><span class="p">;</span> <span class="cm">/* Set AD1IF after every 1 samples */</span>
<span class="n">AD1CON3</span> <span class="o">=</span> <span class="mh">0x0107</span><span class="p">;</span> <span class="cm">/* Sample time = 1Tad, Tad = 8 * Tcy */</span>
<span class="n">AD1CHS</span> <span class="o">=</span> <span class="n">CURRENT_VOLTAGE_AN</span><span class="p">;</span> <span class="cm">/* AN1 */</span>
<span class="n">AD1CSSL</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">ADON</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// turn ADC ON</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">ASAM</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// auto-sample</span>
<span class="cm">/* analog-to-digital interrupts */</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">AD1IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">IEC0bits</span><span class="p">.</span><span class="n">AD1IE</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>As I mentioned, I also enabled interrupts in order to refrain from
polling the interrupt flag. I looked up the function for ADC interrupts
in the Microchip docs and cleared the interrupt flag within it:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_ADC1Interrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* clear the flag */</span>
<span class="n">IF0bits</span><span class="p">.</span><span class="n">AD1IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>We will do more than this in the future, but it is a good start. Once
we get here, we can set a breakpoint at the only line within the
function and run the processor using the debugger. If all is well, we
should hit the breakpoint every time we press 'play'.</p>
<h2>Calculating Values</h2>
<p>At this point, we should diverge a bit to talk about the calculated
values that we wish to see from the A/D. Our A/D is configured to be a
left-aligned unsigned value. To become a signed value, we can simply
shift the value by one to the right in order to leave room for a sign
bit. This will be useful for calculations later on.</p>
<p>First off, we want to know the differential voltage across the load
<em>and</em> across time. This involves measuring the low-side voltage of the
load and subtracting it from the high-side voltage of the load and then
saving that value into an array.</p>
<ol>
<li>loadVoltageL = AdcLoadL >> 1</li>
<li>loadVoltage[sampleIndex] = (AdcLoadH >> 1) - loadVoltageL</li>
</ol>
<p>The next value that we want to save is the current value across time.
The value of the current is measured instantaneously and requires
subtraction of the HZ1 voltage as the reference of the opamp circuit.
The time will be saved as an array that is indexed in the same way that
the load voltage is indexed.</p>
<ol>
<li>loadCurrent[sampleIndex++] = (AdcLoadCurrent >> 1) - hz1Value</li>
</ol>
<p>Finally, the two HZ outputs must be measured and saved.</p>
<h2>Moving Through the Inputs</h2>
<p>Unfortunately, there is no mode that will automatically load the A/D
values into their proper places and then trigger the interrupt in this
processor. There are other chips in this line that will, but we don't
have one. As a result, we will have to manually multiplex the A/D to
the proper channels.</p>
<p>Since we can't simultaneously sample the channels, we would like to
sample the high-side voltage, low-side voltage, and current as closely
together as possible. We will then sample the HZ outputs.</p>
<p>We must change the 'trigger' source to when the SAMP bit is cleared
instead of on a timer in order to remain synchronous. At the end of the
T1 interrupt, we will clear the SAMP bit. On each source, we will
change the source and, again, clear the SAMP bit. At the end of the
chain, we will not clear the SAMP bit again until the next T1 interrupt.
Only three lines change significantly in the initAdc() function:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">AD1CON1</span> <span class="o">=</span> <span class="mh">0x0200</span><span class="p">;</span>
<span class="n">AD1CON2</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">AD1CON3</span> <span class="o">=</span> <span class="mh">0x0007</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>The _T1Interrupt() function gets one line added to the end of it to
clear the SAMP bit:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_T1Interrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="n">q16angle_t</span> <span class="n">theta</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">theta</span> <span class="o">+=</span> <span class="n">omega</span><span class="p">;</span>
<span class="n">DAC1DAT</span> <span class="o">=</span> <span class="n">q15_fast_sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">;</span>
<span class="n">DAC2DAT</span> <span class="o">=</span> <span class="n">q15_fast_sin</span><span class="p">(</span><span class="n">theta</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">;</span> <span class="c1">// theta + 180 deg</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">T1IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">SAMP</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// <= Added this line</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Finally, a switch/case statement gets added inside the A/D interrupt in
order to change the input channel being sampled and to properly clear
the SAMP bit:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_ADC1Interrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">switch</span><span class="p">(</span><span class="n">AD1CHS</span><span class="p">){</span>
<span class="k">case</span> <span class="nl">LD_VOLTAGE_1_AN</span><span class="p">:</span>
<span class="p">{</span>
<span class="n">loadVoltageL</span> <span class="o">=</span> <span class="p">(</span><span class="n">q15_t</span><span class="p">)(</span><span class="n">ADC1BUF0</span> <span class="o">>></span> <span class="mi">1</span><span class="p">);</span>
<span class="n">AD1CHS</span> <span class="o">=</span> <span class="n">LD_VOLTAGE_0_AN</span><span class="p">;</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">SAMP</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">case</span> <span class="nl">LD_VOLTAGE_0_AN</span><span class="p">:</span>
<span class="p">{</span>
<span class="n">loadVoltage</span><span class="p">[</span><span class="n">sampleIndex</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">q15_t</span><span class="p">)(</span><span class="n">ADC1BUF0</span> <span class="o">>></span> <span class="mi">1</span><span class="p">)</span> <span class="o">-</span> <span class="n">loadVoltageL</span><span class="p">;</span>
<span class="n">AD1CHS</span> <span class="o">=</span> <span class="n">CURRENT_VOLTAGE_AN</span><span class="p">;</span>
<span class="n">AD1CON1bits</span><span class="p">.</span><span class="n">SAMP</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">case</span> <span class="nl">CURRENT_VOLTAGE_AN</span><span class="p">:</span>
<span class="cm">/* ... more code follows... */</span>
</pre></div>
</td></tr></table>
<p>Of course, more case statements follow, but they all look nearly
identical except for the last one, which does not clear the SAMP bit.</p>
<h2>Adjusting T1 Frequency</h2>
<p>I would like for all ADCs to be sampled after the T1 interrupt. As a
result, The period of the T1 interrupt must be longer than the time to
sample and convert 5 A/D cycles. As each A/D cycle takes \~10μs and our
current T1 period of '250' corresponds to 15.625μs, we will have to
increase our T1 period to '1000' in order to pack all A/D sample and
conversion cycles into the span of a single T1 interrupt.</p>
<h2>Results</h2>
<p>After going through all of this setup and playing, it is time to test.
I have set the LATB7 bit at the beginning of the A/D interrupt and
cleared it at the end of the interrupt in order to get a visualization
of when the A/D interrupt is executing relative to the T1 interrupt.</p>
<p><img src="/images/2016/03/adc_interrupt_capture.png" alt="ADC interrupt sequence capture" class="img-responsive img-rounded"></p>
<p>In the scope capture, you can see in blue the output waveform. Each
transition of that waveform to a different level marks the T1 interrupt
and, thus, the clearing of the SAMP bit. Each yellow positive
transition marks the 5 A/D interrupts that are triggered by the T1
interrupt.</p>
<p>After running the code and plotting the load voltage and the current
arrays:</p>
<p><img alt="current and voltage vs. sample number" src="http://www.forembed.com/images/2016/03/i_and_v_vs_sample.png"></p>
<p>The discontinuity is where the index was loading values, so the most
recent value loaded was actually sample 20. We will make efforts to fix
this in later segments.</p>
<h1>Summary</h1>
<p>Now we have most of the blocks of the core waveform generator and
A/D functioning as expected. We will probably tighten up a couple of
blocks in preparation for UART transmission of data in the next segment.</p>
<p>As always, you can find our the code in our <a href="https://github.com/slightlynybbled/project_curve_tracer">github
repository</a>.</p>Project Curve Tracer - Setting Up PWM as a DAC2016-02-29T12:00:00-05:002016-02-29T12:00:00-05:00Jason Jonestag:www.forembed.com,2016-02-29:/project-curve-tracer-setting-up-pwm-as-a-dac.html<h1>Intro</h1>
<p><img src="/images/2016/02/pwm_output_schematic.png" alt="PWM output schematic" class="img-responsive img-rounded"></p>
<p>In the initial schematic, you will recall that U1 is an opamp in the
differential configuration. This means that the voltage at the
reference input will be the 'center' or '0' voltage of the output. We
decided to place a small RC filter at the input of that opamp …</p><h1>Intro</h1>
<p><img src="/images/2016/02/pwm_output_schematic.png" alt="PWM output schematic" class="img-responsive img-rounded"></p>
<p>In the initial schematic, you will recall that U1 is an opamp in the
differential configuration. This means that the voltage at the
reference input will be the 'center' or '0' voltage of the output. We
decided to place a small RC filter at the input of that opamp so that we
could adjust the center of the output as needed. We are using the DACs
available for a different portion of the project, so we are going to do
this using the PWM through an RC circuit in order to generate the DC
reference.</p>
<h1>More Rework</h1>
<h2>Opamp Gain</h2>
<p>Originally, I had the gain setting resistors of the opamp at 1kΩ and
10kΩ. As it turns out, I didn't have any 10k resistors in this package
laying around. I modified the schematic with a ratio that was close,
going with 2.67kΩ and 24kΩ for a gain of 8.99. We will probably modify
this on the next round so that the resistors on the board have more
common values, but this will do for now.</p>
<h2>High-Impedance Output</h2>
<p><img src="/images/2016/02/board_pwm_patches.jpg" alt="board with PWM pin patch wires" class="img-responsive img-rounded"></p>
<p>Originally, we had pin 20 generating the DC bias PWM for the current sense opamp.<br>
Since our pin 20 was taken up by a capacitor due to a misread, we had
to cut a trace and re-route to the analog out that is labeled
'HZ_OUT_1'. Fortunately, we weren't using this for anything else, so
that worked perfectly.</p>
<h2>Opamp Output</h2>
<p>Finally, due to the same misread, we had to cut a trace to pin 7 (see
previous post) and re-route the opamp output to a different analog
input. We chose pin 17.</p>
<h1>RC Filter</h1>
<p><img src="/images/2016/02/rc_filter.png" alt="RC Filter" class="img-responsive img-rounded"></p>
<p>We are using one of the most basic elements of electronics, the RC
filter, to generate a DC signal using a variable AC waveform. By tuning
the values of R, C, and the PWM frequency, we can ensure that the output
is a nearly perfect DC value. In this case, we have chosen a 1kΩ
resistor and a 1μF capacitor in the 'low-pass' configuration, meaning
that the filter will pass the low frequency components of the input
waveform. The 'cutoff' frequency occurs at:</p>
<p>fc = 1/(2 * pi * R * C)</p>
<p>In our case, this works out to 159Hz. Generally, if you want to filter
all if your AC components, you stay well above the cutoff frequency, so
above 1.59kHz should do quite well.</p>
<p>Our PWM requirement, which will go into Vin of the low-pass filter, is
to have a frequency higher than 1.59kHz.</p>
<h1>Coding</h1>
<p>This PWM module is quite flexible, which means that it can be complex to
set up. Couple that with a somewhat vague datasheet and you can spend
hours setting it up. Fortunately, there are some things that are common
to all PWM modules that make it fairly straightforward once you identify
the registers to modify.</p>
<ol>
<li>Setup DIO</li>
<li>Identify the PWM module and registers to modify</li>
<li>Set up the time base</li>
<li>Set the PWM mode</li>
<li>Turn on the module</li>
<li>Modify the duty cycle as required</li>
</ol>
<h2>Setup DIO</h2>
<p>The digital pins RB10 and RB11 must be setup as digital outputs using
the TRISB bits.</p>
<h2>Identify PWM/CCP Modules</h2>
<p>In our case, we are using pins 21 and 22 of the microcontroller. This
means that we have access to PWM pins OC1C and OC2A. Re-interpreting
these values, we have Output Compare on PWM Module 1, output C and
Output Compare on PWM module 2, output A. This identifies that we will
be setting up two different output compare modules, OC1 and OC2. This
will guide us through the rest of our setup.</p>
<p>When we are setting up OC1 and we see in the datasheet "CCPxCON1L",
substitute the number '1' for the 'x'. For our application, nearly all
registers are the same for the two modules.</p>
<h2>Time Base</h2>
<p>The timer in this PWM module is capable operating in 16-bit or 32-bit
mode. We only need 16-bits for our time base, so we clear the T32 bit
in the CCP1CON1L and CCP2CON1L. We have no need to adjust the prescaler
for the timer, so leave the prescaler at '1'.</p>
<p>For our period register, CCPxPRL, we only need to stay above 1.59kHz.
Additionally, we would like to have the ability to adjust with a
reasonable degree of precision - say, 10 bits - so we will set this
value to 1024 (2 \^ 10).</p>
<p>Tcycle = 1/16000000 = 62.5ns</p>
<p>Tpwm = Tcycle * 1024 = 64μs</p>
<p>Fpwm = 1/Tpwm = 15.625kHz</p>
<p>This is well above our minimum desired frequency and will make a great
starting point.</p>
<h2>PWM Mode</h2>
<p>The datasheet can be confusing on some of the specifics of the PWM
module, but just remember that the 'default' operation is often close to
where you want to be.</p>
<p>In our case, the mode we want is 'Dual Edge Compare Mode, buffered'.
What this means is that, as the counter counts up, it will at some
point encounter the value stored in CCPxRA at which time the pin value
will go 'high'. When the timer value reaches the value stored
in CCPxRB, the pin will go 'low'. We don't need to adjust both edges,
so we will leave CCPxRA = 0 and adjust CCPxRB when we want to change the
duty cycle. More on that later.</p>
<h2>Turn On Module</h2>
<p>Once all of the registers are set up, turn on the module!</p>
<h2>Complete Module Setup</h2>
<p>The complete module setup code is:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">initPwm</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="c1">// use OC1C (pin 21, RB10) and OC2A (pin 22, RB11)</span>
<span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB10</span> <span class="o">=</span> <span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB11</span> <span class="o">=</span> <span class="n">DIO_DIGITAL</span><span class="p">;</span>
<span class="cm">/* Initialize MCCP module</span>
<span class="cm"> * */</span>
<span class="cm">/* period registers */</span>
<span class="n">CCP1PRH</span> <span class="o">=</span> <span class="n">CCP2PRH</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">CCP1PRL</span> <span class="o">=</span> <span class="n">CCP2PRL</span> <span class="o">=</span> <span class="mi">1024</span><span class="p">;</span>
<span class="n">CCP1CON1L</span> <span class="o">=</span> <span class="n">CCP2CON1L</span> <span class="o">=</span> <span class="mh">0x0005</span><span class="p">;</span>
<span class="n">CCP1CON1H</span> <span class="o">=</span> <span class="n">CCP2CON1H</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">CCP1CON2L</span> <span class="o">=</span> <span class="n">CCP2CON2L</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">CCP1CON2H</span> <span class="o">=</span> <span class="mh">0x8400</span><span class="p">;</span> <span class="c1">// enable output OC1C</span>
<span class="n">CCP2CON2H</span> <span class="o">=</span> <span class="mh">0x8100</span><span class="p">;</span> <span class="c1">// enable output 0C2A</span>
<span class="n">CCP1CON3L</span> <span class="o">=</span> <span class="n">CCP2CON3L</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// dead time disabled</span>
<span class="n">CCP1CON3H</span> <span class="o">=</span> <span class="n">CCP2CON3H</span> <span class="o">=</span> <span class="mh">0x0000</span><span class="p">;</span>
<span class="n">CCP1CON1Lbits</span><span class="p">.</span><span class="n">CCPON</span> <span class="o">=</span> <span class="n">CCP2CON1Lbits</span><span class="p">.</span><span class="n">CCPON</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="cm">/* duty cycle registers */</span>
<span class="n">CCP1RA</span> <span class="o">=</span> <span class="n">CCP2RA</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">CCP1RB</span> <span class="o">=</span> <span class="n">CCP2RB</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Note that the vast majority of bits are '0'. Also note that the only
difference betweeen CCP1 and CCP2 is with regard to the specific output
number - A on CCP2 and C on CCP1 - on register CCPxCON2H. All other
registers are identical.</p>
<h2>Modify Duty Cycle</h2>
<p>To modify the duty cycle, we only need to modify the CC2RB value. For
this type of application, I generally like to abstract the interface a
bit and use Q1.15 as the input to the function abstraction. This makes
other operations later much simpler, such as implementing a PID loop.</p>
<p>We will call our new functions 'setDutyCycleHZ1' and 'setDutyCycleHZ2'.
The functions are nearly identical and will take a Q1.15 number as the
sole parameter and, based on the contents of the period register, setup
the CCPxRB register appropriately.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8
9</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">setDutyCycleHZ1</span><span class="p">(</span><span class="n">q15_t</span> <span class="n">dutyCycle</span><span class="p">){</span>
<span class="n">CCP1RB</span> <span class="o">=</span> <span class="n">q15_mul</span><span class="p">(</span><span class="n">dutyCycle</span><span class="p">,</span> <span class="n">CCP1PRL</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">setDutyCycleHZ2</span><span class="p">(</span><span class="n">q15_t</span> <span class="n">dutyCycle</span><span class="p">){</span>
<span class="n">CCP2RB</span> <span class="o">=</span> <span class="n">q15_mul</span><span class="p">(</span><span class="n">dutyCycle</span><span class="p">,</span> <span class="n">CCP2PRL</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<h1>Testing</h1>
<p>In main, we initialize HZ1 with a 50% duty cycle and HZ2 with a 25% duty
cycle in Q1.15 format:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cm">/* set the initial duty cycle */</span>
<span class="n">setDutyCycleHZ1</span><span class="p">(</span><span class="mi">16384</span><span class="p">);</span>
<span class="n">setDutyCycleHZ2</span><span class="p">(</span><span class="mi">8192</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>Looking at the HZ2 output:</p>
<p><img alt="25% duty cycle scope capture" src="http://www.forembed.com/images/2016/02/pwm_250.png"></p>
<p>The yellow trace is the PWM and the blue trace is the output of the RC
filter. Note the slight fluctuation on the DC output waveform. We can
set our scope to AC coupled and measure that small fluctuation more
closely:</p>
<p><img alt="25% duty cycle scope capture, Vpp" src="http://www.forembed.com/images/2016/02/pwm_250_ac.png"></p>
<p>According to our measurements of the output when we AC-couple the signal
and change the vertical scale to 20mV/division, we can measure the
peak-to-peak voltage to be (35.2mV - (-32.0mV)) = <strong>67.2mV</strong>. This is
acceptable for the moment. We know that we can reduce from here by one
of three strategies:</p>
<ol>
<li>Increase the resistance of the RC network</li>
<li>Increase the capacitance of the RC network</li>
<li>Increase the PWM frequency</li>
</ol>
<p>As we move through the project, we may notice that 67.2mV is simply too
much fluctuation enough for whatever reason.</p>Project Curve Tracer - Using the On-Die Opamp2016-02-26T00:07:00-05:002016-02-26T00:07:00-05:00Jason Jonestag:www.forembed.com,2016-02-26:/project-curve-tracer-using-the-on-die-opamp.html<h1>Intro</h1>
<p><img src="/images/2016/02/sine_thumbnail.png" alt="sine capture thumbnail" class="img-responsive img-rounded"></p>
<p>In the previous post code-oriented post, we got the DAC generating
waveforms of reasonable frequencies. Unfortunately, a DAC usually has a
high output impedance, which will distort our signals if we place it in
the wrong circuit. Fortunately, we can make up for this using the
on-board operational amplifiers …</p><h1>Intro</h1>
<p><img src="/images/2016/02/sine_thumbnail.png" alt="sine capture thumbnail" class="img-responsive img-rounded"></p>
<p>In the previous post code-oriented post, we got the DAC generating
waveforms of reasonable frequencies. Unfortunately, a DAC usually has a
high output impedance, which will distort our signals if we place it in
the wrong circuit. Fortunately, we can make up for this using the
on-board operational amplifiers as 'buffers' or 'voltage followers'.
Their job is to simply take the input voltage and reflect it on the
output with a low output impedance. This post will focus on getting the
op-amps running in this mode.</p>
<h1>Board Patches</h1>
<p>As mentioned in the last post, there were some schematic errors. We
aren't going to go into all of the changes, just the necessary ones to
make this board work. We did 3 things:</p>
<ol>
<li>Cut the trace from U2, pin 7 to U1, pin1.</li>
<li>Applied a patch wire (in red) between U2, pin 7 and the output on
R11.</li>
<li>Used a solder-bridge to short U2 pins 25 and 26.</li>
</ol>
<p><img src="/images/2016/02/partially_populated_board.jpg" alt="partially populated board" class="img-responsive img-rounded"></p>
<p>Performing these operations shunts our opamp outputs to the desired
outputs. More changes will be made later.</p>
<h1>Buffer Amplifier</h1>
<h2>Desired Circuit</h2>
<p>The <a href="https://en.wikipedia.org/wiki/Operational_amplifier_applications">buffer amplifier</a>
is likely the simplest opamp circuit out there. If you
don't understand how it works, follow the link. The simplest idea is that
Vout always equals Vin.</p>
<p><img src="/images/2016/02/voltage_follower.png" alt="voltage follower" class="img-responsive img-rounded"></p>
<h2>On-Chip Opamp</h2>
<p>The on-chip amplifier is very configurable. We are going to use the
PINSEL and NINSEL registers to make our on-chip amplifier conform to the
buffer connections above. See the blue lines below. The OAxOUT pins
are our pins 7 and 26 on our device.</p>
<p><img src="/images/2016/02/dac_connections.png" alt="DAC connections" class="img-responsive img-rounded"></p>
<h2>Code</h2>
<p>We can use just a couple of lines of code to configure the opamp pins.
Looking at the datasheet AMPxCON:</p>
<ul>
<li>AMPEN = 0 ( opamp disabled)</li>
<li>AMPSIDL = 0 (idle mode doesn't matter)</li>
<li>AMPSLP = 0 (sleep mode doesn't matter)</li>
<li>SPDSEL = 0 (low bandwidth)</li>
<li>NINSEL = 101 (voltage follower config)</li>
<li>PINSEL = 101 (DAC input)</li>
</ul>
<p>We could load these all at once, but I am not certain that the compiler
would recognize and optimize sequential register manipulations, so we
will simply look at all of the 1's and calculate the hex value, which is
0x002d.</p>
<p>The final statement is to turn the opamps on using the AMPEN bit.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB3</span> <span class="o">=</span> <span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB15</span> <span class="o">=</span> <span class="n">TRISBbits</span><span class="p">.</span><span class="n">TRISB14</span> <span class="o">=</span> <span class="n">DIO_INPUT</span><span class="p">;</span>
<span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB3</span> <span class="o">=</span> <span class="n">ANSBbits</span><span class="p">.</span><span class="n">ANSB15</span> <span class="o">=</span> <span class="n">DIO_ANALOG</span><span class="p">;</span>
<span class="n">AMP1CON</span> <span class="o">=</span> <span class="n">AMP2CON</span> <span class="o">=</span> <span class="mh">0x002D</span><span class="p">;</span>
<span class="n">AMP1CONbits</span><span class="p">.</span><span class="n">AMPEN</span> <span class="o">=</span> <span class="n">AMP2CONbits</span><span class="p">.</span><span class="n">AMPEN</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<h2>Test</h2>
<p>The scope output shows that our buffer amplifiers are working perfectly!</p>
<p><img alt="opamp_output" src="http://www.forembed.com/images/2016/02/sine_1k3.png"></p>Project Curve Tracer - Schematic Errors2016-02-25T00:00:00-05:002016-02-25T00:00:00-05:00Jason Jonestag:www.forembed.com,2016-02-25:/project-curve-tracer-schematic-errors.html<p><img class="img-responsive" src="http://www.forembed.com/images/2016/02/schematic_revisions.png" alt="schematic revisions"></p>
<p>As always, there are some schematic errors. This is one of the issues
with highly integrated ICs, it is so easy to mix up the pins! Maybe I
should stop doing schematic and layout so late at night...</p>
<ul>
<li>Vcap should be on pin 20, nothing else. This error arose from …</li></ul><p><img class="img-responsive" src="http://www.forembed.com/images/2016/02/schematic_revisions.png" alt="schematic revisions"></p>
<p>As always, there are some schematic errors. This is one of the issues
with highly integrated ICs, it is so easy to mix up the pins! Maybe I
should stop doing schematic and layout so late at night...</p>
<ul>
<li>Vcap should be on pin 20, nothing else. This error arose from me
reading the wrong columns in the data sheet. We simply have to
solder a 4.7uF cap across R8 and short C2. We also have to cut the
trace going to the U1 circuit and attach that trace to the identical
circuit on R7.</li>
<li>LZ_OUT_x pins were intended to be the operational amplifier
outputs. Instead, they are the DAC outputs. The opamp outputs are
on pins 7 and 26. Pin 7 currently holds the current sense A/D input
and will have to have the trace cut and a patch wire added to
another A/D. Pins 25 and 26 can probably be easily jumpered with a
solder bridge if pin 25 is made to be an input.</li>
<li>The programming header has pins 1 and 2 swapped.</li>
</ul>
<p>This is much more rework than is typical for my layouts, but with boards
costing less than the parts on it, it isn't a big deal. We will
continue to patch the boards we have - and post embarassing pictures -
until we have a fully functioning layout! I'm sure that more problems
are waiting to be found.</p>Project Curve Tracer - DAC Initialization and Utilization2016-02-23T07:49:00-05:002016-02-23T07:49:00-05:00Jason Jonestag:www.forembed.com,2016-02-23:/project-curve-tracer-dac-initialization-and-utilization.html<p>Go <a href="http://www.forembed.com/project-curve-tracer-requirements.html">back to the first
post</a> to get
some background on this project!</p>
<h1>Hardware Patch</h1>
<p><img src="/images/2016/02/curve_tracer_board_pic.jpg" alt="Curve Tracer board pic" class="img-responsive img-rounded"></p>
<p>Just a couple of notes before we begin coding. I was using the pinout for
the "PIC24F" part on the datasheet whereas I should have been using the
"PIC24FJ" part. This will involve a small …</p><p>Go <a href="http://www.forembed.com/project-curve-tracer-requirements.html">back to the first
post</a> to get
some background on this project!</p>
<h1>Hardware Patch</h1>
<p><img src="/images/2016/02/curve_tracer_board_pic.jpg" alt="Curve Tracer board pic" class="img-responsive img-rounded"></p>
<p>Just a couple of notes before we begin coding. I was using the pinout for
the "PIC24F" part on the datasheet whereas I should have been using the
"PIC24FJ" part. This will involve a small patch wire, but nothing
major. There is supposed to be a capacitor for an on-die voltage
regulator. For the moment, populating R8 with a 4.7uF ceramic capacitor
and shorting C2 will do the trick.</p>
<p>Additionally, I wasn't paying close attention when I placed the
programming header on the schematic and pins 1 and 2 got mixed up. We
will fix this in the next layout.</p>
<p>I'll update the schematic when I know what all of the patch wires are
going to be :).</p>
<h1>Short Term Goals</h1>
<p>The first goal will be to simply get the DAC functioning well enough to
output a couple of sine waves at reasonable frequencies, maybe in the
1kHz range. That's it, simple enough! The short version is that a
timer interrupt will occur which will add 'omega' to 'theta' at periodic
intervals. We will adjust 'omega' in order to adjust the frequency.</p>
<h2>Libraries</h2>
<p>For this application, we are going to use
<a href="https://github.com/slightlynybbled/libmathq15">libmathq15</a>, available
on github. It has a nice balance of memory usage and accuracy and has
been well-tested. It is likely possible that we could do better using a
custom lookup table or function, but the testing and overhead -
especially at this beginning stage - is not worth the extra hassle.</p>
<p>Compiler Settings</p>
<p>The primary setting that you must enable is optimization level. Go to
the settings and setup the compiler to -O1 optimization (maximum 'free'
optimization level).</p>
<h2>Initialization</h2>
<p>Any microcontroller must be initialized, and the PIC24 series is no
exception. For the moment, we aren't going to worry about external
clock sources, so we will use the configuration #pragma statements to
setup the internal FRC with 4x PLL, which will set us up for 16MIPS.</p>
<p>Next, we initialize the DACs through the initLowZAnalogOut() function.
Just a few register operations to:</p>
<ul>
<li>Set the tri-state (TRISB) and analog-select (ANSB) pins</li>
<li>Enable the DAC for left-aligned input (our library uses all 16-bits
and the DAC only uses 8 bits)</li>
</ul>
<p>The final initialization involves the timer interrupt. For the moment,
we will configure the timer interrupt to fire every 250cycles using PR4.
This may end up too low a number, but should give us a good start at a
high operating frequency as 250cycles at 16MIPS will result in 15.625us
for each sample. Assuming a 24-sample sinusoidal waveform, we will have
a period of 375us, which would result in 2.67kHz. This could be better,
but isn't bad. At our final instruction frequency of 12MIPS, our max
frequency becomes 2kHz.</p>
<p>I won't repeat the source code for this portion here, but you can find
it on
<a href="https://github.com/slightlynybbled/project_curve_tracer/tree/master/code">github</a>.</p>
<h2>Generating a Waveform</h2>
<p>Now we get to the meat of the entire post, the generation of the sine
wave. In order to get a +/- 5V output, we will actually be generating
two sine waveforms that are 180 degrees out of phase. We will take our
angle, 'theta', add the new offset 'omega', then load DAC1 with the sine
of theta and load DAC2 with the sine of 'theta + 180º'. Since this is
an interrupt function that takes place periodically, then we will have
to clear the interrupt flag.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="n">_ISR</span> <span class="nf">_T1Interrupt</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="n">q16angle_t</span> <span class="n">theta</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">theta</span> <span class="o">+=</span> <span class="n">omega</span><span class="p">;</span>
<span class="n">DAC1DAT</span> <span class="o">=</span> <span class="n">q15_fast_sin</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">;</span>
<span class="n">DAC2DAT</span> <span class="o">=</span> <span class="n">q15_fast_sin</span><span class="p">(</span><span class="n">theta</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32768</span><span class="p">;</span> <span class="c1">// theta + 180 deg</span>
<span class="n">IFS0bits</span><span class="p">.</span><span class="n">T1IF</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Each waveform has an 'offset' of 32768. This must occur because we
cannot generate negative voltages with our DAC. All voltages generated
must be positive. Additionally, the second waveform has a +32768 offset
to theta. This represents a 180° phase relationship between the two
waveforms. This is how we are going to apply both a positive and
negative voltage to the device-under-test.</p>
<p>Simulation of the timer interrupt reveals that it takes \~60 cycles to
execute the interrupt. This doesn't include the \~10 cycles of context
saving and restoring that are performed, so \~70 cycles is a reasonably
safe bet. Our period of 250 cycles will probably remain exactly where
it is since we have other things to do with the extra cycles.</p>
<h1>Evaluation</h1>
<p>How does the code perform? Lets take a look at the waveforms!</p>
<p><img alt="1.3kHz sine wave" src="http://www.forembed.com/images/2016/02/sine_1k3.png"></p>
<p>At 1.3kHz, we can see the high-frequency DAC loads when we look closely,
but the waveform appears adequate. We may employ a bit of averaging in
order to get the benefit of multiple sequential waveforms, but it
doesn't appear that averaging would be necessary.</p>
<p><img alt="2.7kHz sine wave" src="http://www.forembed.com/images/2016/02/sine_2k7.png"></p>
<p>On the other hand, at 2.7kHz, we can clearly see the stair-stepping
caused by the DAC loads. Again, for our application, this could
probably be overcome with some averaging techniques, but at some point,
we would be interfering with our data.</p>
<p>Our target of 1.0kHz appears to be sufficiently proven to continue with
that goal in mind.</p>
<h1>What Next?</h1>
<p>Next post, we will connect the internal opamp to the DAC in the voltage
follower configuration. This will translate the relatively high
impedance DAC voltage into a much lower impedance, which is what we
desire for signal integrity. Stay close!</p>A Low-RAM Averaging Technique2016-02-19T22:55:00-05:002016-02-19T22:55:00-05:00Jason Jonestag:www.forembed.com,2016-02-19:/a-low-ram-averaging-technique.html<p><img src="/images/2016/02/filled-filter-xxl-150x150.png" alt="filter pic" class="img-responsive img-rounded"></p>
<h1>Intro</h1>
<p>You have all had to implement a moving average algorithm from
time-to-time. Moving averages make life easier, especially for
mixed-signal applications in which you have a few extra A/D cycles
available.</p>
<p>This article is intended for a fixed-point audience; however, the
techniques shown can apply to floating point …</p><p><img src="/images/2016/02/filled-filter-xxl-150x150.png" alt="filter pic" class="img-responsive img-rounded"></p>
<h1>Intro</h1>
<p>You have all had to implement a moving average algorithm from
time-to-time. Moving averages make life easier, especially for
mixed-signal applications in which you have a few extra A/D cycles
available.</p>
<p>This article is intended for a fixed-point audience; however, the
techniques shown can apply to floating point data as well, with slight
modifications.</p>
<p>The remainder of the article will assume that the user is working with
signed 16-bit numbers, which have a range of -32768 to +32767. This is
just for illustration and doesn't in any way impose an upper or lower
limit on the technique.</p>
<h1>Typical Moving Average</h1>
<p>The typical takes some number of elements and averages them, then takes
the next group of elements, then averages those and so on. This is
simple and highly effective. If you only require a two-element moving
average, this is the way to go. If you require a 16-element moving
average, then the process of creating a moving average involves:</p>
<ul>
<li>Saving 16 elements of data, probably in an array (32 bytes of RAM)</li>
<li>Adding all of these elements up</li>
<li>Dividing the elements by 16</li>
</ul>
<p>We can optimize a couple of these processes. For one, if you create an
extra 'accumulator' value, then you can simply add the newest value to
it and subtract the oldest value from it instead of adding all elements
up.</p>
<p>Additionally, if you make your array length a power of 2, you can
'divide' by simple bit shifting.</p>
<h2>RAM Breakdown</h2>
<p>Assuming your accumulator is twice the sample width, your RAM usage in
this instance is:</p>
<ul>
<li>32 bytes for your data array</li>
<li>1 byte for your array index counter</li>
<li>4 bytes for your accumulator</li>
</ul>
<p>For a total of 37 bytes utilized. This won't break the bank on your
standard smartphone or x64, but this is nearly half of your RAM on an
MSP430!</p>
<h2>Cycles Breakdown</h2>
<ul>
<li>1 add operation</li>
<li>1 subtract operation</li>
<li>1 divide/shift operation</li>
</ul>
<p>Assuming that we are shifting and not dividing, this isn't too bad a
penalty to pay for the moving average.</p>
<h1>Modified Moving Average</h1>
<p>Lets just get right into a bit of pseudocode:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">avg_accum</span> <span class="o">+=</span> <span class="n">new_sample</span><span class="p">;</span>
<span class="n">avg_accum</span> <span class="o">=-</span> <span class="n">sample_average</span><span class="p">;</span>
<span class="n">sample_average</span> <span class="o">=</span> <span class="n">avg_accum</span> <span class="o">>></span> <span class="n">x</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>The above pseudocode is executed on every new sample. The 'accumulator'
adds a 'new_sample' and subtracts an 'sample_average' on each run
around. The new 'sample_average' is then calculated by shifting the
bits by 'x', which will usually be a static value. A higher number
creates a stronger filter.</p>
<p>Effectively, this technique works by giving weight to older data by
reducing the weight of newer data by the amount shifted, 'x'.</p>
<h2>RAM breakdown</h2>
<ul>
<li>4 bytes for the accumulator</li>
<li>2 bytes for the average (it is persistent, cycle-to-cycle)</li>
</ul>
<p>For a total of 6 bytes.</p>
<h2>Cycles Breakdown</h2>
<ul>
<li>1 add operation</li>
<li>1 subtract operation</li>
<li>1 divide/shift operation</li>
</ul>
<p>Identical to the moving average, so this is a wash.</p>
<h1>Comparisons</h1>
<p>So lets run these filters through some rudimentary testing through a
16-sample moving average filter and then match that with our 'lean'
filter.</p>
<h2>Impulse Response</h2>
<p><img src="/images/2016/02/impulse.png" alt="impulse" class="img-responsive img-rounded"></p>
<p>The left-most sample number of the chart is actually a value of '15' at
a value of '0' so that the moving average filter could get enough
samples to be 'primed'. At sample 16, the sample impulses to 32767 and
the filter responses are shown. I realize that a typical moving average
filter would not have the phase delay, but that is a choice that I made
to ease coding.</p>
<p>The moving average filter will simply average the group of 16 samples
until it gets to the maximum, resulting in the linear ramp to maximum
shown.</p>
<p>The reader familiar with electronics will note the similarity between
the 'lean' response and the exponential response common in RC and RL
filters. I don't know if they are actually equivalent, but the
resemblance is striking. Perhaps a gifted reader could tackle this
question?</p>
<p>We adjusted the shift operation until the plotted response of the two
methods were reasonably close and ended up with a shift of '3', which is
effectively division by 8.</p>
<h2>Random Noise</h2>
<p>The impulse response is cool and shows that there is some real filtering
going on in the lean version, but what about random data? We took
random numbers between 0 and 32767 and ran them through each filter.
Lets take a look:</p>
<p><img src="/images/2016/02/random.png" alt="random noise plot" class="img-responsive img-rounded"></p>
<p>It is clear from the two plots that both datasets demonstrate effective
filtering of the random data. There are places in which each seems to
work more effectively, but both are holding the value near the 16383
average value.</p>
<h2>Noisy Waveform</h2>
<p>Placing the filters on an actual wave shape will help the reader
visualize the effectiveness of the filter as well:</p>
<p><img src="/images/2016/02/noisy_sine.png" alt="noisy sine plot" class="img-responsive img-rounded"></p>
<h1>Summary</h1>
<p>If you are looking for a simple and effective filter for your
low-resource project, this is it. Adjustment is simple and, unlike the
standard moving average filter, doesn't require any more RAM to keep the
older samples.</p>
<p>The filter simulation was run using Python. Source code is on
<a href="https://github.com/slightlynybbled/lean_moving_avg">github</a>.</p>Creating the Current Sensor Board2016-02-16T21:44:00-05:002016-02-16T21:44:00-05:00Jason Jonestag:www.forembed.com,2016-02-16:/creating-the-current-sensor-board.html<h1>Intro</h1>
<p><img src="/images/2016/02/part_to_adc.png" alt="Interfacing INA139 with an ADC" class="img-responsive img-rounded"></p>
<p>There have been several times that I have had to sense current in an
application for one reason or another. I was working on just such an
application recently when I thought to myself, "Hey, why don't I just
make a quick solderable module and just always use that …</p><h1>Intro</h1>
<p><img src="/images/2016/02/part_to_adc.png" alt="Interfacing INA139 with an ADC" class="img-responsive img-rounded"></p>
<p>There have been several times that I have had to sense current in an
application for one reason or another. I was working on just such an
application recently when I thought to myself, "Hey, why don't I just
make a quick solderable module and just always use that". So I did.</p>
<h1>Requirements</h1>
<p>I want a current-sensor board that will give me unidirectional current
sense output that I can run straight into an A/D converter. I am
looking for a maximum sense current around 3A, and the ability to handle
a voltage of at least 30V.</p>
<h1>The Market</h1>
<p>SparkFun has their <a href="https://www.sparkfun.com/products/12040">current sensor breakout
board</a> for the
<a href="http://www.ti.com/product/ina169">INA169</a>. It is a very flexible
layout with four nice mounting holes and is about the size of a quarter
(as shown on their website), which I believe is 1" x 1". In truth, it
is a really nice board and there is no recommendation that I can make
against it. I just wanted something a bit more specific. Here are the
items that I wish to address:</p>
<ul>
<li>The first is that the breakout board is so generalized that it
wastes a lot of space on components that will never be populated. I
want something small and purpose-built.</li>
<li>The output is a relatively high-impedance output, meaning that if
you want to run it straight into your ADC, then you may need to roll
an opamp into your circuit.</li>
<li>Lower-voltage part (the <a href="http://www.ti.com/product/ina139">INA139</a>
is a bit cheaper and still goes up to 40V).</li>
<li>In stock.</li>
</ul>
<p>The 'in stock' part is probably what got me going down this path, but
the rest of the items are things that I decided that I wanted along the
way.</p>
<p>I looked at other options as well. For instance, the "AttoPilot boards
are not bad, but they are scaled for currents greater than 10x the range
I am looking for and cost \$20/board. The boards based on the ACS712
chip are really great, but they have a quiescent current in the 10mA
range. I am doing a project for a battery-powered application, so this
will not do.</p>
<h1>Schematic</h1>
<p>Building the schematic was probably the easiest part. TI basically
gives you the schematic in their datasheet, you just have to pick parts
and place them.</p>
<p>I chose a 0.05ohm resistor as my sense resistor. I was trying to
balance sense voltage with power loss. At 0.05ohm, there will be a
150mV differential voltage across the sense resistor which is directed
into the INA139 IC.</p>
<p>I chose a 33kohm resistor as the 'gain' resistor. I am expecting about
2A nominal in my circuit and I want to be able to sense up to 3A. At
3A, there is a 150mV differential (as previously descussed), which -
when amplified by 33 - will result in a 4.95V output. Since I am using
a 5V microcontroller for the application, 33kohm is about right.</p>
<p><img src="/images/2016/02/ina139_schem.png" alt="INA139 schematic" class="img-responsive img-rounded"></p>
<p>At this point, the circuit looks a lot like the Sparkfun schematic. But
wait, there's more!</p>
<p>The IC output in combination with the resistor form a high-impedance
output. The TI datasheet recommends a buffer on this before utilizing
it on any A/D converter, so I took the trouble to place a small voltage
follower on the board to make it A/D compatible:</p>
<p><img src="/images/2016/02/ina139_buffer.png" alt="INA139 buffer schematic" class="img-responsive img-rounded"></p>
<p>The last point on the schematic is to place some sort of external
connector on the board. I prefer 0.1" headers whenever possible since
they are universally available, so I placed an 8-pin header with the
power-supply and load pins doubled-up for more current-carrying
capacity.</p>
<p><img src="/images/2016/02/sensor_board_pinout.png" alt="current sensor board pinout" class="img-responsive img-rounded"></p>
<p>Through the header, we can supply the current to be measured by running a
current from Vps to Vload. We can supply the ICs with the power through
Vcc and GND. Finally, we can access the current sensor directly through
Vsense or the buffer through Vbuf. Perfect!</p>
<h1>Layout</h1>
<p>As us usual with layouts, there isn't much to say about the actual
process. We placed the header on the edge of our board and crammed
everything into as small a space as was reasonable. At the end, we
ended up with a board that is 1.1" x 0.5", whoohoo! Mission
accomplished!</p>
<p><img src="/images/2016/02/sensor_board_layout.png" alt="current sensor board layout" class="img-responsive img-rounded"></p>
<p>There are layers two cutouts which are to 'bottom' against a PCB when this
board is inserted into another board.</p>
<p>The sense resistor is also connected to planes which have vias. This
provides a small amount of heatsinking for the potentially hot
component.</p>
<p>Finally, we added labels to the bottom side to help the user (probably
just me). There is a line drawn that indicates the direction of current
if it isn't clear by the lettering. The KiCad view is a bit busy. The
OSH Park view is much better (below). You can see the vias much more
clearly on the OSH Park render.</p>
<p><img src="/images/2016/02/sensor_board_render.png" alt="current sensor board render" class="img-responsive img-rounded"></p>
<p>So, we have already ordered boards from OSH Park, next we need to order
parts and get them soldered up and ready-to-roll!</p>
<p>As usual, all files can be found on
<a href="https://github.com/slightlynybbled/unidir_current_sense_module">github</a>.</p>Avoiding Division and Modulo Operations2016-02-15T16:07:00-05:002016-02-15T16:07:00-05:00Jason Jonestag:www.forembed.com,2016-02-15:/avoiding-division-and-modulo-operations.html<p>You may also be interested in our article regarding <a href="http://www.forembed.com/how-fixed-point-math-works.html">fixed-point
math</a>!</p>
<h1>Intro</h1>
<p>Division and modulo operations should be avoided on 8- and 16-bit
hardware. We would like to present you with a few techniques that can
help you avoid the cycle-heavy division operations by replacing your
modulo operators with bit …</p><p>You may also be interested in our article regarding <a href="http://www.forembed.com/how-fixed-point-math-works.html">fixed-point
math</a>!</p>
<h1>Intro</h1>
<p>Division and modulo operations should be avoided on 8- and 16-bit
hardware. We would like to present you with a few techniques that can
help you avoid the cycle-heavy division operations by replacing your
modulo operators with bit masks and your division operations with bit
shifts. I have focused on 'C' for this article, but they are just as
valid in all programming languages.</p>
<h1>Powers of 2</h1>
<p>You will see a recurring theme in this article, powers of 2 make your
life easier. This is for the same reason that powers of 10 make your
life easier in the metric system. Math just tends to work out easily.
Whenever possible, make your multiplication, division, length, whatever
a power of 2 and you will thank yourself later.</p>
<h1>Look-up Tables</h1>
<p>The processors that we are accustomed to working with on this
publication are lumbering, slow devices that can often take microseconds
to perform the simplest operations (that is a long time, btw). As a
result, designers often place look-up tables (LuTs) within the processor
to replace potentially time-consuming
maths.</p>
<p><img src="/images/2016/02/rollover_animation.gif" alt="angle rollover animation" class="img-responsive img-rounded"></p>
<p>When choosing the length of your table, you should generally choose a
length that is a power of 2, especially for periodic waveforms. Why?
Lets take the example of a repeating waveform, 'sine'. As you know, a
sine wave repeats every rotation. Once it gets to its maximum value, it
simply repeats again. We can take advantage of this using a modulo
technique that can be completed very quickly on most processors without
doing the first divide, but <em>only</em> if the length of the table is a power
of 2. Keep reading... we come back to this in a moment.</p>
<h1>To Roll or Not to Roll</h1>
<p>The most common experience for most people involving a fixed number of
digits rolling over is the odometer in a typical car. If you can find
an old Bronco 2 from the early eighties, you will see that they only had
5 digits to work with. When the old car got to '99999' and then went
one more mile, the miles were suddenly '00000' again. This is exactly
the modulo operation. In this case:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">odometer</span> <span class="o">=</span> <span class="n">miles</span> <span class="o">%</span> <span class="mi">100000</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>We have similarly easy operations on our simple processors, but we try
to implement them in the easiest and most straightforward way. Just as
the odometer in the base 10 example was very easy to modulo with a power
of 10, it is just as easy to modulo a binary number with a power of 2;</p>
<p>Going back to our sine LuT, we can traverse through our table by adding
1 and looking up the next value.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="n">sineTable</span><span class="p">[</span><span class="n">sineIndex</span><span class="p">];</span>
<span class="n">sineIndex</span><span class="o">++</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>There will be a point when 'sineIndex' will step beyond the bounds of
the lookup table. This is where our modulo operation can help. By
using the modulo operator, we cause the lower order bits to repeat
continually, just as the above animation illustrates. Assuming a
256-element lookup table:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="n">sineTable</span><span class="p">[</span><span class="n">sineIndex</span> <span class="o">%</span> <span class="mi">256</span><span class="p">];</span>
<span class="n">sineIndex</span><span class="o">++</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>There are several compilers that will pick this up as a modulo operator
on a power of two and insert the appropriate optimization. It is this
author's opinion that the programmer should be able to recognize the
optimization himself and state his desire more clearly as all compilers
do not recognize the optimization:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="n">sineTable</span> <span class="p">[</span><span class="n">sineIndex</span> <span class="o">&</span> <span class="mh">0x00ff</span><span class="p">];</span>
<span class="n">sineIndex</span><span class="o">++</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>This set of statements has the exact same effect as the previous set of
statements and completes the argument on why your LuT length should be a
power of two. If your sine-table length would have been anything other
than a power of two length, then this optimization would not have been
possible.</p>
<p>Alternatively, for very special cases which happen to line up with
compiler defined lengths, you can forego the mask and the rollover will
occur automatically for the same reason that it occurred on your
odometer:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">uint8_t</span> <span class="n">sineIndex</span><span class="p">;</span> <span class="c1">// this is only an 8-bit number</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">sineTable</span><span class="p">[</span><span class="n">sineIndex</span><span class="o">++</span><span class="p">];</span> <span class="c1">// no mask required</span>
</pre></div>
</td></tr></table>
<h1>Feeling Shifty</h1>
<p>Division by any power of two can take place using the simple shift
operation '>>':</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">>></span> <span class="mi">1</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>is the same as:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>Most of you know this, but we can take it further. This division is the
same as multiplication by 1/2:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">*</span> <span class="mi">1</span><span class="o">/</span><span class="mi">2</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<p>What if you wanted to multiply by 3/4? As long as your divisor is a
power of two or can be broken down into a sequence of powers of two, you
can use the shift operation to your advantage. Below, each expression
is exactly equivalent to the others:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">output</span> <span class="o">=</span> <span class="mi">12</span> <span class="o">*</span> <span class="mi">3</span><span class="o">/</span><span class="mi">4</span><span class="p">;</span>
<span class="n">output</span> <span class="o">=</span> <span class="p">(</span><span class="mi">12</span> <span class="o">*</span> <span class="mi">1</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">12</span> <span class="o">*</span> <span class="mi">1</span><span class="o">/</span><span class="mi">4</span><span class="p">);</span>
<span class="n">output</span> <span class="o">=</span> <span class="p">(</span><span class="mi">12</span> <span class="o">>></span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">12</span> <span class="o">>></span> <span class="mi">2</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>On some compilers, the upper most expression would institute a
multiplication then a division operation, which will cost you tens of
cycles (hundreds on some). The bottom expression will always end up
being a couple of bit shifts and an add.</p>
<h1>Summary</h1>
<p>To perform modulo operations, use bit masks or use the maximum value
allowed for that variable type. To perform divisions, use bit shifts as
much as possible. Remember to use powers of 2 throughout your code. As
always, have fun!</p>Project Curve Tracer - Boards on the Way!2016-02-12T20:57:00-05:002016-02-12T20:57:00-05:00Jason Jonestag:www.forembed.com,2016-02-12:/project-curve-tracer-boards-on-the-way.html<p><a href="https://oshpark.com/shared_projects/dK8zQVI0">Boards</a> are on the way
from OSH Park, along with parts from Digikey!</p>
<p><img src="/images/2016/02/curve_tracer_board_render.png" alt="curve tracer board render" class="img-responsive img-rounded"></p>
<p>We should be able to start coding sometime next week - I <em>always</em> take
the free shipping.</p>
<p>I will likely have them soldered up the day they get here since they
have so few parts.</p>
<p>As mentioned in …</p><p><a href="https://oshpark.com/shared_projects/dK8zQVI0">Boards</a> are on the way
from OSH Park, along with parts from Digikey!</p>
<p><img src="/images/2016/02/curve_tracer_board_render.png" alt="curve tracer board render" class="img-responsive img-rounded"></p>
<p>We should be able to start coding sometime next week - I <em>always</em> take
the free shipping.</p>
<p>I will likely have them soldered up the day they get here since they
have so few parts.</p>
<p>As mentioned in the <a href="http://www.forembed.com/project-curve-tracer-requirements.html">requirements
post</a>, I
will probably start with coding up the waveform generator. I may do a
bit of pre-emptive coding to get a jump start...</p>Testing the Accuracy of libmathq15 on the PIC242016-02-11T21:04:00-05:002016-02-11T21:04:00-05:00Jason Jonestag:www.forembed.com,2016-02-11:/testing-the-accuracy-of-libmathq15-on-the-pic24.html<p>For a quick primer on libmathq15, check out <a href="http://www.forembed.com/how-fixed-point-math-works.html">How Fixed-Point Math
Works</a>.</p>
<h1>Intro</h1>
<p>Recently, I have been curious as to the accuracy of libmathq15 over<br>
the entire sine range, which is 0 to 65535 for trigonometric functions
and -32768 to 32767 for the standard Q1.15 functions. Realizing quickly
that …</p><p>For a quick primer on libmathq15, check out <a href="http://www.forembed.com/how-fixed-point-math-works.html">How Fixed-Point Math
Works</a>.</p>
<h1>Intro</h1>
<p>Recently, I have been curious as to the accuracy of libmathq15 over<br>
the entire sine range, which is 0 to 65535 for trigonometric functions
and -32768 to 32767 for the standard Q1.15 functions. Realizing quickly
that this range is very reasonable to check, I went to work using the
PIC24 simulator to get my results.</p>
<p>This is a long post, grab a cup of coffee!</p>
<h2>Version?</h2>
<p>The accuracy tests would not be complete without some sort of version
information. All testing was run against git commit
hash '5519ce5f96f2daa412882503473527f218e3f31e' on
<a href="https://github.com/slightlynybbled/libmathq15">github</a>.</p>
<h1>From Simulator to File</h1>
<p>The first thing that I want to do is save data to a file. I don't want
to copy/paste 65536 entries into a file from the console. Fortunately,
the device simulator that comes with the MPLAB X environment has the
option of exporting 'printf' statements to the console or to a file:</p>
<ol>
<li>File -> Project Properties</li>
<li>Under 'Categories', click 'Simulator'</li>
<li>Under 'Option Categories' drop down, click 'Uart1 IO Options'</li>
<li>Check the checkbox, choose your output type (console or file)</li>
<li>If 'file' is the desired output, choose a file name</li>
</ol>
<p>In the file that needs to contain printf statements, you need to include
<stdio.h> as well.</p>
<p>Quick note about simulation, if you want to repeat this testing, all
files are in github, but I would recommend running it on the silicon and
using the UART to send the data back into PuTTy or something similar.
The simulation took entirely too long for my liking and the hardware
probably would have blown the sim out of the water.</p>
<h1>The Program Unfolds</h1>
<h2>The First Draft</h2>
<p>We are going to use float types and math.h as our 'standard' by which to
judge error.</p>
<p>For the remainder of this section, I am going to use the testing for the
sine function as the 'sample', but you should know that the results will
include multiplication, division, etc.</p>
<p>For the sine function, we will start at a fixed-point angle of '0' and
convert that to the floating-point equivalent. We will then execute
both fixed-point and floating-point sine functions. Finally, we will
scale the floating-point value to fixed-point to determine the error in
'bits'.</p>
<p>This thought process resulted in the below program:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* print column headers */</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"float_val, fixed_val, float_sine, fixed_sine, fixed_err"</span><span class="p">);</span>
<span class="n">q16angle_t</span> <span class="n">q16_angle_num</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">q16_angle_num</span> <span class="o"><</span> <span class="mi">65535</span><span class="p">){</span>
<span class="cm">/* convert fixed-point to radians */</span>
<span class="kt">float</span> <span class="n">float_val</span> <span class="o">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">q16_angle_num</span> <span class="o">*</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="mf">3.14159</span> <span class="o">/</span> <span class="mf">65536.0</span><span class="p">;</span>
<span class="cm">/* calculate sine of float and of fixed */</span>
<span class="kt">float</span> <span class="n">float_sine</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">float_val</span><span class="p">);</span>
<span class="n">q15_t</span> <span class="n">fixed_sine</span> <span class="o">=</span> <span class="n">q15_sin</span><span class="p">(</span><span class="n">q16_angle_num</span><span class="p">);</span>
<span class="cm">/* calculate the error in points */</span>
<span class="kt">float</span> <span class="n">fixed_err</span> <span class="o">=</span> <span class="p">(</span><span class="n">float_sine</span> <span class="o">*</span> <span class="mf">32768.0</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">fixed_sine</span><span class="p">;</span>
<span class="cm">/* print relevant values to the console */</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%f,%d,%f,%d,%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">float_val</span><span class="p">,</span>
<span class="n">q16_angle_num</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">float_sine</span><span class="p">,</span>
<span class="n">fixed_sine</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">fixed_err</span><span class="p">);</span>
<span class="n">q16_angle_num</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Our goal is to generate a nice CSV file which can easily be parsed by
most plotting tools, including <a href="http://www.gnuplot.info/">gnuplot</a>.</p>
<h2>The Wrinkles</h2>
<p>While running this program, I noticed that it was taking a really long
time and I decided to take a peek at the results. I saw a repeating
pattern, the program was continually 'resetting' at the beginning of
program execution! What was going on here?</p>
<h2>Wrinkle 1: The Watchdog Timer</h2>
<p>There is a function of the microcontroller that exists solely to reset
the microcontroller periodically if it is not 'serviced': the WatchDog
timer. The job of the watchdog timer is to reset the microcontroller
when the code goes out of bounds due to anything from cosmic rays to bad
programming practices. In many cases, you might not even notice a
watchdog timer reset. I have seen it happen on an assembly that was
spinning at 50000 RPM and the only way that I knew that something odd
was happening was an odd 'click' every now and then. Simply insert the
below line at the top of your program and the watchdog timer should be
disabled. Alternatively, you could add a 'ClrWdt()' call to your while
loop and get the same effect.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#pragma config FWDTEN = OFF</span>
</pre></div>
</td></tr></table>
<p>After inserting the configuration bit that turns the watchdog timer off,
all went well. Our program takes several minutes to execute - maybe
half an hour? Only after it finished did I stop to think about all of
the processes that are working together here - PIC24 machine
instructions running on a device simulator which is running on Java,
then on my hardware. No wonder it took several minutes. If you decide
to run this yourself, hit 'go', make sure it is working, then go get
lunch.</p>
<h2>Wrinkle 2: printf Integer Formatting</h2>
<p>It turns out that the 'printf' function used always sees 16-bit numbers
as signed, so when the number is of type uint16_t and is a value of
'32768', printf will output this as '-32768'. This inspired us to do a
'float' conversion right in the printf to keep the float and fixed point
comparisons on the same horizontal scale.</p>
<h2>Wrinkle 3: No 'equivalent' Column</h2>
<p>Our program above did a conversion just before an error check, but there
was no conversion archived. It would be convenient to have the
fixed-point scaled value archived in the CSV file so that we can overlay
the two plots during the analysis stage.</p>
<p>The final program executed is below, which is very similar, with the
'wrinkles' ironed out:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* print column headers */</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"float_val, fixed_val, float_sine, fixed_sine, scaled_float, fixed_err"</span><span class="p">);</span>
<span class="n">q16angle_t</span> <span class="n">q16_angle_num</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">q16_angle_num</span> <span class="o"><</span> <span class="mi">65535</span><span class="p">){</span>
<span class="cm">/* convert fixed-point to radians */</span>
<span class="kt">float</span> <span class="n">float_val</span> <span class="o">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">q16_angle_num</span> <span class="o">*</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="mf">3.14159</span> <span class="o">/</span> <span class="mf">65536.0</span><span class="p">;</span>
<span class="cm">/* calculate sine of float and of fixed */</span>
<span class="kt">float</span> <span class="n">float_sine</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">float_val</span><span class="p">);</span>
<span class="n">q15_t</span> <span class="n">fixed_sine</span> <span class="o">=</span> <span class="n">q15_sin</span><span class="p">(</span><span class="n">q16_angle_num</span><span class="p">);</span>
<span class="cm">/* scale the floating-point to fixed-point for comparison */</span>
<span class="kt">float</span> <span class="n">scaled_sine</span> <span class="o">=</span> <span class="n">float_sine</span> <span class="o">*</span> <span class="mf">32768.0</span><span class="p">;</span>
<span class="cm">/* calculate the error in points */</span>
<span class="kt">float</span> <span class="n">fixed_err</span> <span class="o">=</span> <span class="p">(</span><span class="n">float_sine</span> <span class="o">*</span> <span class="mf">32768.0</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">fixed_sine</span><span class="p">;</span>
<span class="cm">/* print relevant values to the console */</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"%f,%d,%d,%d,%f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">float_val</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">q16_angle_num</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">float_sine</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">fixed_sine</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">scaled_sine</span><span class="p">,</span>
<span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">fixed_err</span><span class="p">);</span>
<span class="n">q16_angle_num</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>We will execute similar programs for the other functions under test as
well. There are some functions that require two parameters. When this
is the case, we will simply choose a set of values to run instead of
attempting to run all possible input values. We are comfortable with
this approach since unit testing covers the edge cases. Here are some
quick details:</p>
<ul>
<li>q15_mul() - tested with 24567 and -1024 as the multiplier</li>
<li>q15_div() - tested with 24567 and -1024 as the dividend</li>
<li>q15_add() - tested with 24567 and -1024 as the adder</li>
<li>q15_abs() - tested full range</li>
<li>q15_sqrt() - tested full range</li>
<li>q15_sin() - tested full range, 8-bit table</li>
<li>q15_cos() - tested full range, 8-bit table</li>
</ul>
<h1>Analysis</h1>
<p>Finally, we have data! My less-than-humble opinion on data is that if
you can't see it, then you probably don't understand it. Humans are
visual creatures above all else, so plots are extremely important to
gaining a full understanding of any data set. You can find the datasets
on github.</p>
<p>Having said that, <a href="http://www.gnuplot.info/">gnuplot</a> is my favorite
plotting tool for quickly plotting nice little graphs from CSV files.
All plots shown were generated using this a <a href="https://github.com/slightlynybbled/libmathq15/blob/master/accuracy/xc16/gnuplot_script.txt">gnuplot
script</a>.
To execute the script, you must have gnuplot installed. Navigate to
the directory with the data and the script. On the command line, type:</p>
<blockquote>
<p>gnuplot gnuplot_script.txt</p>
</blockquote>
<p>All of your PNGs will be generated from the data. There are lots of
gnuplot tutorials out there, but feel free to copy/paste mine.
Remember, when you are customizing the script, the order of the
commands matters!</p>
<h1>Results</h1>
<p>The results are specific to the operation, so we will break them out
here. This is a summary of plots created. The full data set can be
found on the <a href="https://github.com/slightlynybbled/libmathq15/tree/master/accuracy/xc16">github
repository</a>.</p>
<h2>Multiplication</h2>
<p>The q15_mul multiplication function is dead-on across the range tested.
As we are using multiplication hardware available on the device, this
is no surprise. Errors are less than 1 bit.</p>
<p><img src="/images/2016/02/mul_24576_error.png" alt="multiplication error" class="img-responsive img-rounded"></p>
<p><img src="/images/2016/02/mul_24576_overlay.png" alt="multiplication overlay" class="img-responsive img-rounded"></p>
<h2>Division</h2>
<p>The q15_div function operates perfectly until the magnitude of the
divisor (denominator) becomes equal to or greater than the magnitude of
the dividend. As mentioned in the past, in Q1.15 math, the magnitude of
the divisor MUST be less than the magnitude of the dividend OR the
result will saturate.</p>
<p><img src="/images/2016/02/div_n1024_error.png" alt="division error" class="img-responsive img-rounded"></p>
<p>Note the saturation (below) when the magnitude of the divisor becomes
less than the magnitude of the dividend. On all other values, the error
is less than 1 bit.</p>
<p><img src="/images/2016/02/div_n1024_overlay.png" alt="division overlay" class="img-responsive img-rounded"></p>
<h2>Addition</h2>
<p>To test addition, we simply add a static number to every possible
number. In this case, that number is '-1024'. Note that the error is 0
everywhere except in the region in which the result saturates.</p>
<p><img src="/images/2016/02/add_n1024_error.png" alt="addition error" class="img-responsive img-rounded"></p>
<p>In the overlay, you can see a small 'tail' at the bottom left where the
result saturates.</p>
<p><img src="/images/2016/02/add_n1024_overlay.png" alt="addition overlay" class="img-responsive img-rounded"></p>
<h2>Absolute Value</h2>
<p>No surprise, the absolute value function displays no error relative to
the floating-point function over the entire range.</p>
<p><img src="/images/2016/02/abs_error.png" alt="absolute value error" class="img-responsive img-rounded"></p>
<p><img src="/images/2016/02/abs_overlay.png" alt="absolute value overlay" class="img-responsive img-rounded"></p>
<h2>Square Root</h2>
<p>The Q1.15 square root is very accurate except when the number is very
small. I have included two plots of the error, one that runs from 0 to
100 and the other that runs from 101 on. Note that the error below an
input of \~40 is greater than 20 bits.</p>
<p><img src="/images/2016/02/sqrt_error_0to100.png" alt="sqrt error 0 to 100" class="img-responsive img-rounded"></p>
<p>The error quickly drops to less than 10 bits and stays below 2 bits for
the majority of the range of the function.</p>
<p><img alt="sqrt error, 101 up" src="http://www.forembed.com/images/2016/02/sqrt_error_101up.png"></p>
<p><img src="/images/2016/02/sqrt_overlay.png" alt="sqrt overlay" class="img-responsive img-rounded"></p>
<h2>Sine</h2>
<p>Just for clarification, this code was executed on the '8-bit' table,
which is the highest resolution table. The lowest resolution table is
the 4-bit table, which likely has much worse errors.</p>
<p>The error in every odd input were observed to be worse than the even
data points. On closer inspection, it appears that the odd and even
numbers that are adjacent (0 and 1, 2 and 3, etc.) are actually the same
number across the range. Regardless, the error is very low - certainly
good enough for the majority of applications. Future library
improvements will likely target this function first since the previous
functions have all behaved with lower error</p>
<p><img src="/images/2016/02/sine_8bit_error.png" alt="sine error" class="img-responsive img-rounded"></p>
<p>On the overlay, you still can't even see the two different data sets.</p>
<p><img src="/images/2016/02/sine_8bit_overlay.png" alt="sine overlay" class="img-responsive img-rounded"></p>
<h2>Fast Sine</h2>
<p>The fast sine is simply a lookup table within the code with no
interpolation. As a result, it gives up accuracy for the sake of speed.
Fortunately, this is often plenty of accuracy to run a motor or meet
other functional requirements.</p>
<p><img src="/images/2016/02/fast_sine_8bit_error.png" alt="fast sine error" class="img-responsive img-rounded"></p>
<p>Since the lookup table is only implemented for 90 degrees and calculated
for the other quadrants, it may be more appropriate to just look at the
first quadrant.</p>
<p><img src="/images/2016/02/fast_sine_8bit_to16384_error.png" alt="fast sine error to 16384" class="img-responsive img-rounded"></p>
<p>Even with this amount of error, the overlay still looks quite adequate
for most tasks.</p>
<p><img src="/images/2016/02/fast_sine_8bit_overlay.png" alt="fast sine overlay" class="img-responsive img-rounded"></p>
<h2>Cosine</h2>
<p>Results look very similar to the sine function, just shifted 90 degrees.
Again, small room for improvement, but the results are definitely very
usable in the majority of applications.</p>
<p><img src="/images/2016/02/cos_8bit_error.png" alt="cosine error" class="img-responsive img-rounded"></p>
<p><img src="/images/2016/02/cos_8bit_overlay.png" alt="cosine overlay" class="img-responsive img-rounded"></p>
<h2>Tangent</h2>
<p>A tangent function is included in the q15 library for completeness, but
its functionality is severely limited by the inability of the format to
represent numbers greater in magnitude than 1.0. In the ranges of 57344
(-45 degrees) to 8192 (45 degrees) and 24575 (135 degrees) to 40959 (225
degrees), the results are valid with errors never going beyond +/- 10
bits.</p>
<p><img src="/images/2016/02/tan_8bit_error.png" alt="tangent error" class="img-responsive img-rounded"></p>
<p>The below plot shows the saturation that occurs as a result of the Q1.15
format, which causes the large errors. Note that the overlay is nearly
perfect in the valid regions.</p>
<p><img src="/images/2016/02/tan_8bit_overlay.png" alt="tangent overlay" class="img-responsive img-rounded"></p>
<p>The other two 'fast' trigonometric functions were omitted from this
study since they will show similar results to the fast sine.</p>
<h1>Conclusion</h1>
<p>On the whole, the library is fully functional on the XC16 platform.
There could be some other details addressed and there is room for
improvement, but the library is ready for production as is.</p>Project Curve Tracer - Layout2016-02-11T12:00:00-05:002016-02-11T12:00:00-05:00Jason Jonestag:www.forembed.com,2016-02-11:/project-curve-tracer-layout.html<p>If you haven't read the project documentation up to this point, you may
want to go <a href="http://www.forembed.com/project-curve-tracer-requirements.html">back to the
beginning</a>.</p>
<p>If you are ready to dive into the layout, then lets get started!</p>
<p>This will probably be a short post. The layout process was fairly
straightforward and resulted in a …</p><p>If you haven't read the project documentation up to this point, you may
want to go <a href="http://www.forembed.com/project-curve-tracer-requirements.html">back to the
beginning</a>.</p>
<p>If you are ready to dive into the layout, then lets get started!</p>
<p>This will probably be a short post. The layout process was fairly
straightforward and resulted in a board 1.1" x 1.5", 2-layer. I did
make a couple of funny design choices that should probably be explained.
Lets take a look at the board:</p>
<p><img src="/images/2016/02/curve_tracer_layout.png" alt="Curve tracer layout" class="img-responsive img-rounded"></p>
<h1>Component Sizing</h1>
<p>I have chosen the smallest reasonable packages available for the active
parts and 0603 components for the passives. I really like 0603. I can
solder 0402, but it gets really annoying. The 0805 package just seems
too large.</p>
<h1>I/O</h1>
<p>P3 is a screw terminal. This makes it so that we can attach any sort of
wire we like to the outputs while we are prototyping.</p>
<p>P4 is a 3-pin device on the schematic, so what happened here? As it
turns out, a 3-pin spring loaded socket for TO-220 packages was either
too expensive or out of stock, but 6-pin sockets are quite readily
available and cheap. The result is the P4 footprint.</p>
<p>The USB connector is at the top left on the bottom side of the board. I
haven't done many USB connectors, so I am sure to have gotten something
wrong, but that is what this revision is all about. All-in-all, I'm
happy with the way the board turned out in layout, particularly the
size. Perhaps the next revision will incorporate mounting provisions,
but we can learn that sort of thing later.</p>
<h1>Test Points</h1>
<p>One thing that I forgot that you shouldn't - put some test points on
your board! I banged this out in an hour and completely missed test
points! I'm sure that I can hack a test point on there, but it is nice
to be delivered with them!</p>
<p>Electronic files can be found on
<a href="https://github.com/slightlynybbled/project_curve_tracer">github</a>.</p>
<p><img src="/images/2016/02/curve_tracer_bottom_copper.png" alt="curve tracer bottom copper" class="img-responsive img-rounded"></p>
<p><img src="/images/2016/02/curve_tracer_top_copper.png" alt="curve tracer top copper" class="img-responsive img-rounded"></p>Project Curve Tracer - Schematic Creation2016-02-10T12:00:00-05:002016-02-10T12:00:00-05:00Jason Jonestag:www.forembed.com,2016-02-10:/project-curve-tracer-schematic-creation.html<p>In the <a href="http://www.forembed.com/project-curve-tracer-schematic-part-selection.html">last
post</a>,
we talked about the component selection. In this post, we will make all
connections of the schematic and fill in the details that we skipped.</p>
<h1>High-Level Design</h1>
<p>I believe that it is often instructive to make a high-level block
diagram:</p>
<p><img src="/images/2016/02/curve_tracer_block_diagram.png" alt="curve tracer block diagram" class="img-responsive img-rounded"></p>
<p>There is obviously going to be …</p><p>In the <a href="http://www.forembed.com/project-curve-tracer-schematic-part-selection.html">last
post</a>,
we talked about the component selection. In this post, we will make all
connections of the schematic and fill in the details that we skipped.</p>
<h1>High-Level Design</h1>
<p>I believe that it is often instructive to make a high-level block
diagram:</p>
<p><img src="/images/2016/02/curve_tracer_block_diagram.png" alt="curve tracer block diagram" class="img-responsive img-rounded"></p>
<p>There is obviously going to be more detail than this, but this reveals
our high-level design. There will be a PIC24F16KM202 which takes care
of all user I/O and transmits the results over USB. The USB module will
supply a 24MHz clock to the PIC. Two of the amplifiers shown will
reside within the PIC and the current-sense amplifier will reside
outside of that.</p>
<h2>Output Stage</h2>
<p>The two internal amplifiers will be connected in the voltage follower
configuration and will serve to output the signal that is to go to the
device under test. Vout(high) and Vout(low) will be measured using the
PIC ADC in order to determine the differential voltage across the device
under test.</p>
<p>The low-side will actually be run through what is known as a current
sense resistor. Remember back to Ohm's Law:</p>
<p>V = IR</p>
<p>When we are trying to measure current, one technique is to take
advantage of Ohm's Law by placing a known resistance in series with the
return current. The voltage across that resistor is directly
proportional to the current flowing through it:</p>
<p><img src="/images/2016/02/current_sense_block_diagram.png" alt="current sense block diagram" class="img-responsive img-rounded"></p>
<p>The diagram above shows the direction of current flow when the
differential voltage is positive using the dotted lines. When the
differential voltage is negative, the current will change directions in
the dotted line, but still follow the same path.</p>
<h2>Current-Sense Strategy</h2>
<p>Sensing current can be very difficult, especially small currents.
Fortunately, we should be able to amplify/attenuate the signal without
too much impact using Rsense and the gain on the differential amplifier.</p>
<p><img src="/images/2016/02/current_sense_amplifier.png" alt="current sense amplifier" class="img-responsive img-rounded"></p>
<p>The gain is adjusted by adjusting the ratio of R4:R3 and R5:R2. It is
best to make R3 = R2 and R4 = R5. Once this balance is achieved, it is
a simple matter of choosing the gain value. In this case, my gain is:</p>
<p>R3/R4 = 10k/1k = 10</p>
<p>Any voltage coming in on the input lines (from the right) will be
amplified by a factor of 10 and pass out to the left.</p>
<p>One unexplained node is the upper node going off screen from R3. This
is the 'reference' voltage. In most circuits, this node is simply
grounded in order to make the reference ground. in our case, we want to
sense current in both positive and negative directions. If we ground
this node, we will not be able to sense currents below 0. We will raise
this node to a different voltage for this purpose. Even better, we will
raise this node using an extra PWM output as a DAC so that we can
precisely tune the reference voltage.</p>
<h2>USB Connection</h2>
<p>The USB connection will be nearly identical to that recommended by the
FT230X datasheet. Our only modification is that we will change the
CBUS0 output so that it will output a 24MHz precision clock for our
microcontroller.</p>
<p><img src="/images/2016/02/usb_conn_ft230x.png" alt="usb connector and FT230X" class="img-responsive img-rounded"></p>
<p>No surprises here.</p>
<h2>Programming Header</h2>
<p>The standard Microchip programming header uses an RJ12 connector
(phone). This is large and relatively expensive. We are going to
change this to a 5-pin single row header that we don't have to populate
in any production order. This is simple and easy.</p>
<p><img src="/images/2016/02/pic24_prog_header.png" alt="PIC24 programming header" class="img-responsive img-rounded"></p>
<h2>The Microcontroller</h2>
<p>This is where all of the magic happens. There are some conventions I
used that you should be aware of:</p>
<ul>
<li>HZ = High-Z, or 'high impedance'</li>
<li>LZ = Low-X, or 'low impedance'</li>
<li>LD - load</li>
</ul>
<p>The rest of the labels should make some level of sense.</p>
<p><img src="/images/2016/02/pic24_connections.png" alt="pic24 connections" class="img-responsive img-rounded"></p>
<p>There are three RC filters to the right. These are PWM outputs.
HZ_OUT_0 will go into the reference of our current-sense differential
amplifier. HZ_OUT_1 will be unused (at this time) and HZ_OUT_2 will
be used for biasing transistors and MOSFETs for testing. We had the
extra outputs, may as well connect them to something. Note also that
the HX_OUT_x pins are all going into A/D converters on the right side.
This allows us to close the loop around a particular voltage and adjust
as needed in order to get higher precision from these outputs.</p>
<p>The LZ_OUT_x pins are the voltage follower pins. They will go through
some small protective resistance before driving the load.</p>
<h1>Full Schematic</h1>
<p>To see a full schematic with all of the pieces connected, checkout the
<a href="https://github.com/slightlynybbled/project_curve_tracer">github repository</a>.</p>
<p>Some of the resistance values are very likely to change and some of them
aren't as likely. This is a decent first guess and we can adjust
resistance values as we learn more about our application!</p>
<p>Our next post will be with the layout. With so few components, that
should be a breeze. At that point, we will also upload something
semi-useful to github so that you can download and criticize. Enjoy!</p>Project Curve Tracer - Schematic Part Selection2016-02-09T08:47:00-05:002016-02-09T08:47:00-05:00Jason Jonestag:www.forembed.com,2016-02-09:/project-curve-tracer-schematic-part-selection.html<p>You may want to go back to <a href="http://www.forembed.com/project-curve-tracer-requirements.html">Project Curve Tracer -
Requirements</a>
in order to gain some background on this project.</p>
<h1>Shield or Custom?</h1>
<p>After thinking about this for a bit, I decided to go against making this
a shield for the MSP430 launchpad or Arduino. This decision is
primarily based …</p><p>You may want to go back to <a href="http://www.forembed.com/project-curve-tracer-requirements.html">Project Curve Tracer -
Requirements</a>
in order to gain some background on this project.</p>
<h1>Shield or Custom?</h1>
<p>After thinking about this for a bit, I decided to go against making this
a shield for the MSP430 launchpad or Arduino. This decision is
primarily based on cost. Even the cheapest launchpad - which probably
wouldn't fulfill my requirements - is $10, which is most of my $15
budget. Not to mention that the board size to fit over the Launchpad or
Arduino is large enough to eat most - if not all - of my budget as well.</p>
<h1>I/O Requirements</h1>
<p>We visited the high-level requirements in the last post. Here, we need
to get down into IC requirements. The ideal single-chip solution would
have:</p>
<ul>
<li>2-4 Digital-to-Analog Converters (DACs)</li>
<li>2-4 PWM outputs - 10-bit minumum</li>
<li>2-4 On-die opamps</li>
<li>1 USB peripheral</li>
<li>4-8 Analog-to-Digital Converters (ADCs) - 10-bit minumum</li>
<li>Less than $2.00/part</li>
<li>Up to 5V operation</li>
<li>Be very small but solderable - we don't want to pay for board space
that we won't use, but we also can't hand-solder BGAs.</li>
</ul>
<p>So, in order to get a Vpp = 5V, the module must have two analog outputs
with complimentary sine waves. This will place a full +5V across the
part at the positive peak and a full -5V across the part at the negative
peak. This is what the DACs and PWMs would be used for. DACs are
preferred.</p>
<p>Two of the opamps are basically buffer amplifiers taking the DAC signal
into the input and providing a low-impedance output signal. A typical
<a href="https://en.wikipedia.org/wiki/Operational_amplifier_applications">voltage-follower</a>
circuit should do nicely. One of the opamps must be able to amplify a
small voltage across a current-sense resistor, so there are at least 3
opamps required in the design. If one or two are on-die, then that
would help a great deal.</p>
<p>The USB peripheral is optional. I know as much as the next guy about
USB - which isn't much - so I'm leaning toward using an FTDI chip for
this portion of the project anyway. We will call this 'optional'.</p>
<p>ADCs are a must. We must be able to read the voltage at the load -
high-side and low-side - and also read the current through the load.
Ideally, this would occur using a simultaneously sampled A/D, but that
isn't very likely. We must be sure that these three samples are read
within - say - 1/100th of the waveform? This is somewhat arbitrary, but
needs to be taken into account. If our waveform max frequency is 500Hz,
then the period is 2ms. If we are to sample in 1/100th of 2ms, then
that leads us to having at least 3 samples in 20us, which is one sample
for every 6.7us. Taking the inverse of 6.7us gives us a minimum
sampling frequency of 150kHz. Since this value is somewhat arbitrarily
chosen, we will consider it flexible, but it is good to have an idea of
where we want to be. If we run across an A/D that can sample at 100kHz,
that would probably do. If we run into one that can sample at 10kHz,
then it simply will not do.</p>
<h1>MicroController Selection</h1>
<p>This leaves me looking around at manufacturer data sheets. I went
through several and I believe that I found a great candidate for my
project, the
<a href="http://www.microchip.com/wwwproducts/Devices.aspx?product=PIC24F16KM202">PIC24F16KM202</a>.
Amongst the requirements that mention '2 to 4' peripherals, it has 2 of
almost all of them. Conspicously lacking is USB, so we will go with the
FTDI chip with UART. Amongst the features we are interested in:</p>
<ul>
<li>2 DACs (8-bit)</li>
<li>4+ PWMs - up to 16 bit</li>
<li>2 Opamps - code configurable as voltage followers with the DACs as
inputs</li>
<li>Up to 5V operation</li>
<li>Several 12-bit A/D inputs at 100ksps, but can go slightly higher for
10-bit A/D results</li>
<li>2 UART modules</li>
<li>$2.50/pc</li>
</ul>
<p>To ensure that the microcontroller is a good choice for the application,
I like to go through the datasheet with a spreadsheet and associate each
function with a pin. It always seems like I miss something or that the
final schematic doesn't quite agree with my initial pin assignments, but
this is a great tool for development.</p>
<p><img src="/images/2016/02/pin_assignment.png" alt="PIC24 pin assignments" class="img-responsive img-rounded"></p>
<p>The only thing that is certain is that this will change somewhat, but it
is a great start.</p>
<h1>External OpAmp</h1>
<p>With all of this put together, we only need purchase a single additional
opamp. Since the requirement for this opamp is not a tight one, it is
desireable to purchase a cheap opamp that has a common footprint.
Again, small is essential. The MCP6001 in the SOT-23-5 is a pretty
good first approximation. I have used this opamp before and have been
happy with its overall performance in a higher-frequency application.
It should do just fine as our current amplifier. It will be placed in
the <a href="https://en.wikipedia.org/wiki/Operational_amplifier_applications">differential
mode</a>
so that it can amplify positive and negative currents through the sense
resistor. We will use one of the PWM outputs as the reference so that
we can adjust the reference as required by the application in real time.</p>
<h1>USB Interface</h1>
<p>Finally, the last major component is the USB interface in the form of an
FTDI chip. The FT230X meets all of our requirements and has a precision
trimmed output pin option which will give the microcontroller a good
oscillator without having to purchase a crystal. This comes at the cost
of a few MIPS, from 16 to 12 MIPS. At this time, it is believed that
this will be adequate for the application. This part comes in at
~$2.38. This is more than we would like, but probably worth it for
the moment in order to move forward with our application.</p>
<h1>Misc Parts</h1>
<p>There remains a slew of resistors, capacitors, and connectors to choose
as well. I won't bore you with the selection process of each resistor
or connector, but any PWM outputs must have RC filters, amplifiers have
resistors, all components have bypass caps. With two SSOP style
packages and one SOT-23-5 package along with a USB connector, this board
should be smaller than 1.5" x 1.5". That really helps, going a long way
towards keeping our boards under the target $15.</p>
<h1>Wrapping Up</h1>
<p>This post has gone longer than I anticipated, so I will wait until next
time to flesh out the actual schematic.</p>Project Curve Tracer - Requirements2016-02-08T12:00:00-05:002016-02-08T12:00:00-05:00Jason Jonestag:www.forembed.com,2016-02-08:/project-curve-tracer-requirements.html<h1>Intro</h1>
<p>My first job out of school was as an engineering technician repairing
medium to large UPS systems. I only worked there for 6 months, but I
learned a lot about the 'real world' and about how to troubleshoot
circuits. In my time there, I encountered a piece of equipment …</p><h1>Intro</h1>
<p>My first job out of school was as an engineering technician repairing
medium to large UPS systems. I only worked there for 6 months, but I
learned a lot about the 'real world' and about how to troubleshoot
circuits. In my time there, I encountered a piece of equipment called a
<a href="http://www.huntron.com/products/tracker2000.htm">Huntron Tracker 2000</a>.
At first, I was dubious, but after just a short time I could often find
the problem with a circuit in seconds using this marvelous device.</p>
<p>What is a Huntron Tracker 2000? It is a curve tracer. The Tracker
outputs a sinusoidal (or other, based on settings) waveform and plots
voltage on the horizontal axis and current on the vertical axis. This
sounds cryptic, but look at a typical VI curve for a diode:</p>
<p><img src="/images/2016/02/vi-diode.png" alt="diode VI curve" class="img-responsive img-rounded"></p>
<p>When looking at a typical diode, you would see exactly this curve. A
dead short appears as a vertical line (low voltage, high current), an
open appears as a horizontal line (high voltage, low current) and so on.
Capacitors and inductors have reactance, so they tend to draw ellipses
based on the frequency.</p>
<p>At any rate, a Huntron Tracker 2000 costs a few hundred dollars. I
believe that - with the right design - one could get 90% of the
functionality of a Tracker for less than $15, which brings us to our
project: to build a curve tracer for less than $15.</p>
<h1>Requirements</h1>
<p>I have thought about this a lot recently and - in order to get the cost
down - the unit must have a cheap or available power supply and must
coopt the screen for another device. The first and easiest thing that
comes to mind is to simply use USB for the power supply and use your PC
screen as the screen. This is the direction that we will go for the
moment in order to keep things simple. Up to this point, we have a
screen and power supply without even starting the design.</p>
<p>This brings us to our first real limitation, which is that we will have
access to a 5V power supply whereas the Tracker can go up to +/-20V on a
particular waveform. As the majority of troubleshooting is done on the
5V setting anyway, this is an acceptable limitation.</p>
<p>Since we are connecting via USB, it is probably necessary to get a
microcontroller with a USB peripheral built-in or to use a simple FTDI
chip. This decision is somewhat arbitrary. If the right
microcontroller can be found with all of the peripherals except the USB,
then the FTDI route is acceptable. If the microcontroller can be found
that has integrated DACs and opamps and the USB module, that would be
ideal.</p>
<p>The 'nominal' frequency of operation is 60Hz, but the unit must be able
to output 'complete' waveforms up to 500Hz. It would be nice if we
could get to a higher frequency while maintaining signal integrity.</p>
<p>For the moment, we won't worry too much about the actual connectors used
for the probes during the prototype stage. I don't want to get into the
trap of not moving forward at all because of getting hung up on a
connection point for two weeks. Once the first prototype is working, we
can work on the next rev of the electronics.</p>
<h1>Planning</h1>
<p>Any project must have some sort of plan, and any plan must be broken at
some point. Here is the rough outline, which I am certain will not be
fully followed:</p>
<ol>
<li>Design the schematic, including BOM for major items</li>
<li>Design the board</li>
<li>Order parts - samples where possible</li>
<li>Solder parts together</li>
<li>Code up the waveform generator</li>
<li>Test the waveform generator along with ensuring the analog signal
integrity</li>
<li>Code up on-board VI curve generation</li>
<li>Code up USB communications</li>
<li>Code up PC graphics with USB comms</li>
</ol>
<p>So, now that I'm looking at the list, it seems like a good idea to just
plan on doing at least one post for each of the design items. I'm sure
that you don't want to read about 'ordering the parts' and 'soldering',
but most of those points look like pretty decent topics.</p>
<p>I'll be hitting the schematic soon, so take notice! More posts are
coming!</p>The Not-Quite RTOS - A Task Manager2016-02-07T12:00:00-05:002016-02-07T12:00:00-05:00Jason Jonestag:www.forembed.com,2016-02-07:/the-not-quite-rtos-a-task-manager.html<p><em>Simplicity is the ultimate sophistication - Leonardo da Vinci</em></p>
<h1>Intro</h1>
<p>There was a time sometime long ago when I was looking for an RTOS or
something similar which would help me easily schedule multiple tasks to
execute deterministically, but not require a lot of overhead. Of
course, I came across all …</p><p><em>Simplicity is the ultimate sophistication - Leonardo da Vinci</em></p>
<h1>Intro</h1>
<p>There was a time sometime long ago when I was looking for an RTOS or
something similar which would help me easily schedule multiple tasks to
execute deterministically, but not require a lot of overhead. Of
course, I came across all of the big guys out there with some very cool
capabilities. Unfortunately, most of them were simply too much... too
much RAM, too much time, and too much to learn about for each RTOS.</p>
<p>It was at the point that I reading into the source code for some RTOS or
another that I came across the function pointer code. After some
inspection, I realized that this was the answer to what I was looking
for... and I'm going to share it with you.</p>
<h1>The Task Manager Overview</h1>
<p>I have written a simple task manager which allows the coder to easily
add and remove tasks from the execution environment. This seems like a
place for some bullet points:</p>
<ul>
<li>Non-preemptive</li>
<li>Non-priority driven</li>
<li>Has no semaphores, mutexes, etc.</li>
<li>Interruptable and works well with interrupt! I have had this task
manager working with several tasks on a motor drive with 50kHz
interrupts with no hiccups!</li>
<li>Simple</li>
<li>Tasks may be added or removed at will</li>
<li>Portable to ALL devices that can compile C code (I have used it on
PIC18s and on the Raspberry Pi to schedule tasks within a program
with zero change to the source code)</li>
<li>Easy to understand - the code consists of 6 functions -
TASK_init(), TASK_add(), TASK_remove(), TASK_manage(),
TASK_getTime(), and TASK_resetTime()</li>
<li>Generally does not hinder sleep modes</li>
<li>Requires a single timer interrupt</li>
</ul>
<h1>Setting Up</h1>
<p>The task manager requires you to do one thing: set up a timer interrupt
to occur at the 'system tick' frequency that you desire. I generally
choose 1ms since most of the systems that I have worked with require
higher frequency timing than a keypad poll. Having said that, there is
no reason that you couldn't use 10ms or 100ms (or 1 min if your timer
will go that long before interrupting).</p>
<p>The timer that you choose should be rock solid. Measure it on the
oscilloscope with an I/O pin. Make sure that it recovers if you change
the clock frequency. Once you have gotten this ready, then and only
then, you may proceed.</p>
<p>A quick personal note about portability, you should try to make the
timer initialization function the same between implementation in order
to improve code portability. For instance, in the example below, I
refer to my timer initialization as TMR_init(), and I supply a function
pointer to the function. This ensures that my task.h and task.c files
remain unchanged between architectures (ARM, MSP430, PIC24), but I only
change my underlying TMR_init() based on the architecture.</p>
<h2>The System Tick</h2>
<p>There is one interrupt in the task manager and it has a single purpose:
keeping the system time. The system time is in the form of a global
variable (file scope) called 'milliSeconds'. When an interrupt occurs,
'milliSeconds' is incremented by one. That is it. The frequency that
you choose will also determine the smallest amount of time that you can
resolve to (on system tasks - you can still configure another timer to
operate at a higher frequency for a particular interrupt function).</p>
<h2>Configure Task Header</h2>
<p>In the 'task.h' file, there is a MAX_NUM_OF_TASKS define. Configure
it for the maximum number of tasks that you expect to have in the queue
at one time. On the MSP430, it takes about 11 bytes of RAM for each
task. This might change a bit on different architectures, depending on
the width of function pointers.</p>
<h1>Using the Task Manager</h1>
<h2>Initialization</h2>
<p>On device power up, the task manager should be initialized directly
after the oscillator initialization. Initialization goes through the
task queue and ensures that all tasks are cleared out and ready to
accept tasks into the queue.</p>
<h2>Adding Tasks</h2>
<p>Adding a task is simply adding a normal function that will execute at
intervals when it is 'due'. The first thing that you need to do is
create the function.... any function. There is only one qualification
that the function needs - it needs to be able to fully execute in a
fraction of the system tick. Why? Because the task manager is
non-preemptive, so it won't interrupt a task that is resource hungry,
and you may have multiple tasks ready to execute at the same time.
Actually adding a task is as simple as:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">myFunction</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>The above code will add the function to the task manager to execute
every 3 system ticks. A couple of notes about 'myFunction':</p>
<ul>
<li>return type must be 'void'</li>
<li>parameters must be 'void'</li>
</ul>
<p>Simple enough.</p>
<p>If you attempt to add a task twice, the task manager - as currently
written - will simply update the period of the existing task. For
instance, if you executed the above task adder, then executed:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">myFunction</span><span class="p">,</span> <span class="mi">5</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>You would change the period from 3 system ticks to 5 system ticks.</p>
<h2>Removing Tasks</h2>
<p>Removing tasks is a simple as adding them:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">TASK_remove</span><span class="p">(</span><span class="o">&</span><span class="n">myFunction</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<h2>Reading/Writing System Time</h2>
<p>For various reasons, you may want to have your software read the system
time. I have used this to coordinate a network of wireless nodes. Each
node had its own scheduled time slot within which to transmit and - thus
- there existed a global 'network time' that the nodes shared. If a
node's local time drifted a bit, it could use the TASK_resetTime()
function to reset its time to the network time.</p>
<p>Additionally, there are processes that often expire and require some
amount of time to lapse before they do. For instance, in that network
example if a node dropped off the network, it remained active on the
network until it timed out. This timeout was kept track of by simply
polling TASK_getTime() every now and then and comparing the results to
the TASK_getTime() of the last reception from that node. Once the time
had grown large enough, then the node was dropped from the network.</p>
<h2>Managing Tasks</h2>
<p>Alright, you've initialized your task manager and you have added your
tasks. What now? The final think that you will do in your main.c is
call TASK_manage(), which will NEVER return. Don't place any
statements that you want to execute after that function call. That's
it!</p>
<h1>Summary</h1>
<p>A quick run down of the things you need to do to get the task manager
running:</p>
<ol>
<li>Setup your oscillator</li>
<li>Setup your TMR_init() function for a system tick</li>
<li>Configure 'task.h' file for the max number of tasks that can be
running</li>
<li>Call TASK_init() to initialize all of the tasks AFTER the
oscillator has been setup.</li>
<li>Add your initial tasks to the queue using TASK_add()</li>
<li>Call the TASK_manage() function</li>
</ol>
<h1>Example</h1>
<p>Lets put all of this to use! I am going to use an MSP430 launchpad
because it is what I have laying around. The launchpad has two LEDs and
a button right on the board. Our task is to blink one LED until the
button is pushed, then blink the other LED until the button is pushed.
Sounds simple enough, so lets start planning out the project
architecture:</p>
<ul>
<li>Button needs debouncing and to control the overall state between
LED1 and LED2</li>
<li>LED1 needs to flash</li>
<li>LED2 needs to flash</li>
</ul>
<h2>Setting up, Again</h2>
<p>Before we go too deeply into the problem domain, we need to set up a
couple of basic routines. In the sample project, these are located in
'lib430.h' and 'lib430.c'.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><msp430.h></span><span class="cp"></span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">TMR_timedFunctPtr</span><span class="p">)();</span>
<span class="n">__interrupt</span> <span class="kt">void</span> <span class="nf">TMR_intFunct</span><span class="p">();</span>
<span class="kt">void</span> <span class="nf">TMR_init</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)()){</span>
<span class="n">TMR_timedFunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span>
<span class="n">TA0CTL</span> <span class="o">|=</span> <span class="n">TASSEL1</span><span class="p">;</span> <span class="cm">/* SMCLK */</span>
<span class="n">TACCR0</span> <span class="o">=</span> <span class="mi">1000</span><span class="p">;</span> <span class="cm">/* timer period - sets the 'system tick' to ~1ms*/</span>
<span class="n">TA0CTL</span> <span class="o">|=</span> <span class="n">MC0</span><span class="p">;</span>
<span class="n">TA0CTL</span> <span class="o">&=</span> <span class="o">~</span><span class="n">MC1</span><span class="p">;</span>
<span class="n">TA0CCTL0</span> <span class="o">|=</span> <span class="n">CCIE</span><span class="p">;</span>
<span class="n">__bis_SR_register</span><span class="p">(</span><span class="n">GIE</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">TMR_disableInterrupt</span><span class="p">(){</span>
<span class="n">TA0CCTL0</span> <span class="o">&=</span> <span class="o">~</span><span class="n">CCIE</span><span class="p">;</span>
<span class="p">}</span>
<span class="cp">#pragma vector=TIMER0_A0_VECTOR</span>
<span class="n">__interrupt</span> <span class="kt">void</span> <span class="nf">TMR_intFunct</span><span class="p">(){</span>
<span class="k">if</span><span class="p">(</span><span class="n">TMR_timedFunctPtr</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="o">*</span><span class="n">TMR_timedFunctPtr</span><span class="p">)();</span>
<span class="n">TA0IV</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The functions that we need to set up are TMR_init(),
TMR_disableInterrupt(), and TMR_intFunct().</p>
<p>Write the TMR_init() routine based on your architecture, oscillator
settings, and desired system tick. The default oscillator settings for
the MSP430 launchpad will set up the oscillator for 1MHz operation,
which is passed into the SMCLK, which my timer is assigned to. As a
result, I am setting the period register to '1000' in order to get a
system tick at 1kHz, or every 1ms. Additionally, you will need to setup
your timed function pointer to the functPtr value (first line in
TMR_init()).</p>
<p>The TMR_disableInterrupt() simply disables interrupts. It is used by
the task manager.</p>
<p>The interrupt function itself has different syntax in different
architectures, but the same purpose. It will call the function that the
task manager requires for its primary system tick.</p>
<h2>Back to the Problem</h2>
<p>Each of the above bullet points will each get their own task. The task
that reads the button will execute continually while turning on and off
the LED1 and LED2 tasks as required.</p>
<p>The code for this can be found on
<a href="https://github.com/slightlynybbled/embedded_task_manager">github</a>.</p>
<p>First, we create our 'blink' functions. Notice that they are 'void'
return and take no parameters. These are just normal functions. They
don't have a 'yield' statement or anything like that. The functionality
should be pretty self-explanatory.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">blinkLed1</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">LED1_IS_ON</span><span class="p">){</span>
<span class="n">LED1_OFF</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">LED1_ON</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">blinkLed2</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">LED2_IS_ON</span><span class="p">){</span>
<span class="n">LED2_OFF</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">LED2_ON</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The 'debounceButton' task is just a bit more complex. The purpose of
this task is to read the button and ensure that one button press will
result in one event. Once this event is detected (see blue highlighted
line below), the appropriate task is added, the opposing task is
removed, and the state machine is changed (ledState). All of the rest
of the code is only there to ensure that the button bounce is
eliminated. For more information, see <a href="http://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-i/">Elliot Williams'
articles</a>
about button debounce. I'm not advertising this as the 'best' button
debouncer, but it works well and was fast to code.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">debounceButton</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="k">static</span> <span class="kt">bool</span> <span class="n">btnState</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">static</span> <span class="kt">int8_t</span> <span class="n">btnCounter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">int8_t</span> <span class="n">btnCounterMax</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span>
<span class="k">static</span> <span class="kt">bool</span> <span class="n">ledState</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="cm">/* when switch is on, increment btnCounter, else decrement */</span>
<span class="k">if</span><span class="p">(</span><span class="n">SWITCH_IS_ON</span><span class="p">){</span>
<span class="n">btnCounter</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">btnCounter</span><span class="o">--</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">((</span><span class="n">btnCounter</span> <span class="o">==</span> <span class="n">btnCounterMax</span><span class="p">)</span> <span class="o">&&</span> <span class="p">(</span><span class="n">btnState</span> <span class="o">==</span> <span class="nb">false</span><span class="p">)){</span>
<span class="cm">/* the btnCounter just counted up to this point and this</span>
<span class="cm"> * is the positive going edge - do something here */</span>
<span class="n">btnState</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">ledState</span><span class="p">){</span>
<span class="n">TASK_remove</span><span class="p">(</span><span class="o">&</span><span class="n">blinkLed2</span><span class="p">);</span>
<span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">blinkLed1</span><span class="p">,</span> <span class="n">LED_1_BLINK_PERIOD</span><span class="p">);</span>
<span class="n">LED2_OFF</span><span class="p">;</span>
<span class="n">ledState</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">TASK_remove</span><span class="p">(</span><span class="o">&</span><span class="n">blinkLed1</span><span class="p">);</span>
<span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">blinkLed2</span><span class="p">,</span> <span class="n">LED_2_BLINK_PERIOD</span><span class="p">);</span>
<span class="n">LED1_OFF</span><span class="p">;</span>
<span class="n">ledState</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">btnCounter</span> <span class="o">></span> <span class="n">btnCounterMax</span><span class="p">){</span>
<span class="n">btnState</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
<span class="n">btnCounter</span> <span class="o">=</span> <span class="n">btnCounterMax</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">btnCounter</span> <span class="o"><</span> <span class="o">-</span><span class="n">btnCounterMax</span><span class="p">){</span>
<span class="n">btnState</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
<span class="n">btnCounter</span> <span class="o">=</span> <span class="o">-</span><span class="n">btnCounterMax</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>At this point, we have written the pieces that we need, but still
haven't gotten into main.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">WDTCTL</span> <span class="o">=</span> <span class="n">WDTPW</span> <span class="o">|</span> <span class="n">WDTHOLD</span><span class="p">;</span> <span class="c1">// Stop watchdog timer</span>
<span class="n">TASK_init</span><span class="p">();</span>
<span class="cm">/* setup the LED pins and switch */</span>
<span class="n">P1DIR</span> <span class="o">&=</span> <span class="o">~</span><span class="n">BIT3</span><span class="p">;</span>
<span class="n">P1DIR</span> <span class="o">|=</span> <span class="p">(</span><span class="n">BIT0</span> <span class="o">|</span> <span class="n">BIT6</span><span class="p">);</span>
<span class="n">LED1_OFF</span><span class="p">;</span>
<span class="n">LED2_OFF</span><span class="p">;</span>
<span class="cm">/* add the debounce task */</span>
<span class="n">TASK_add</span><span class="p">(</span><span class="o">&</span><span class="n">debounceButton</span><span class="p">,</span> <span class="n">BTN_POLL_PERIOD</span><span class="p">);</span>
<span class="cm">/* enter the task execute - this will never return! */</span>
<span class="n">TASK_manage</span><span class="p">();</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The first statement is specific to the MSP430 architecture and simply
turns off the watchdog timer.</p>
<p>The next statement initializes the task manager, just as we mentioned
before.</p>
<p>The third block of statements sets up the hardware registers so that the
LED pins and switch are in the correct input/output configuration and
that the LEDs are off.</p>
<p>The next block adds the 'debounce' task to the task queue to execute
every 'BTN_POLL_PERIOD' system ticks. I have set this particular
project up so that a system tick is 1ms, so the value of
BTN_POLL_PERIOD will also be in milliseconds. Notice the use of the
'&' character. This indicates that I am passing a function pointer and
is very important to the functionality of the task manager. If you
don't use a function pointer, then this will not work and you will
likely get compiler warnings and errors.</p>
<p>Finally, we call TASK_manage(), which we never expect to return.</p>
<p>That's it!</p>
<h1>Conclusions</h1>
<p>The task manager is perfect for functions that need to be executed at
specific intervals, such as PID loops or button polls, but don't
necessarily need to be interrupts. This allows you to pull all of that
code that you once placed into an interrupt and place it outside of the
interrupt sequence so that code that truly needs to interrupt can do its
thing and not be blocked by an unnecessary interrupt code.</p>
<p>Again, a sample project using the MSP430 can be found on github.</p>Circular Buffers!2016-02-05T19:01:00-05:002016-02-05T19:01:00-05:00Jason Jonestag:www.forembed.com,2016-02-05:/circular-buffers.html<p>A note from the future: This article is a bit out-dated. Please visit
the <a href="http://www.forembed.com/circular-buffers-part-two.html">next entry</a> for the updated
entries after you have read this article!</p>
<h1>Intro</h1>
<p>Inspired a bit my the 'Embed with Elliot' article <a href="https://hackaday.com/2015/10/29/embed-with-elliot-going-round-with-circular-buffers/">Going 'Round with
Circular
Buffers</a>,
I decided to make a contribution. Read the article …</p><p>A note from the future: This article is a bit out-dated. Please visit
the <a href="http://www.forembed.com/circular-buffers-part-two.html">next entry</a> for the updated
entries after you have read this article!</p>
<h1>Intro</h1>
<p>Inspired a bit my the 'Embed with Elliot' article <a href="https://hackaday.com/2015/10/29/embed-with-elliot-going-round-with-circular-buffers/">Going 'Round with
Circular
Buffers</a>,
I decided to make a contribution. Read the article, it is pretty solid.</p>
<p>After reading the article, I went over to the source code expecting to
have a *.h and *.c file that implemented the concepts from the
article. Instead, I found that the concepts from the article were
implemented in the same file as the main.c. Feeling like I had to
separate them out and make them somewhat more dynamic, I did a pull of
his repository and - more or less - copied his code with a few changes
to make the library more portable and more dynamic.</p>
<h2>Changes</h2>
<ul>
<li>I took the implementation that Elliot created and moved it into its
own C and H files in order to make it a bit more friendly for
importing into your project.</li>
<li>Added one 'slot' to the buffer. The original implementation would
use one less than the actual buffer size, so if your buffer was 16
bytes long, then you could only use 15 slots.</li>
<li>Added ability to declare more than one buffer using
pass-by-reference functions.</li>
<li>Added the ability to change the element width using a header file
define.</li>
<li>Added checks to ensure that the buffer length is a power of 2 (keeps
code executing without EVER using the modulo operation).</li>
</ul>
<h2>Differences in Use</h2>
<ul>
<li>You must 'initialize' the buffer using the 'BUF_init()' function.
This was originally taken care of in the declaration.</li>
<li>You must now pass the buffer by reference in each function call.</li>
<li>You must include 'cbuffer.h' instead of having all of the code
copied/pasted at the top.</li>
<li>Function calls are slightly different. Each is prefixed with a
'BUF_', but otherwise very similar.</li>
</ul>
<p>For anyone who started with the original code and has already
copied/pasted and all is working, there is no reason to port. If you
find that you wanted to use a second (or third..., or fourth...) buffer,
then you should take the 10 min to convert over.</p>
<h1>Example</h1>
<p>A quick code sample is in order.</p>
<p>First, the buffer is declared. There is no reason that this couldn't be
declared as a global, but it isn't necessary for this sample. When
using in interrupts, be sure to use 'volatile'!</p>
<p>Next, the buffer is initialized. Once it is initialized, the buffer is
'empty'. We can now write to the buffer and read from it.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">Buffer</span> <span class="n">b</span><span class="p">;</span>
<span class="n">BUF_init</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">);</span>
<span class="kt">uint8_t</span> <span class="n">a</span><span class="p">;</span>
<span class="cm">/* write to the buffer too many times */</span>
<span class="kt">uint8_t</span> <span class="n">i</span><span class="p">;</span>
<span class="k">for</span><span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
<span class="n">BUF_write</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span>
<span class="p">}</span>
<span class="cm">/* read one value */</span>
<span class="n">BUF_read</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span> <span class="o">&</span><span class="n">a</span><span class="p">);</span>
<span class="cm">/* write one value */</span>
<span class="n">a</span><span class="o">++</span><span class="p">;</span>
<span class="n">BUF_write</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
<span class="cm">/* read 3 values into a */</span>
<span class="n">BUF_read</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span> <span class="o">&</span><span class="n">a</span><span class="p">);</span>
<span class="n">BUF_read</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span> <span class="o">&</span><span class="n">a</span><span class="p">);</span>
<span class="n">BUF_read</span><span class="p">(</span><span class="o">&</span><span class="n">b</span><span class="p">,</span> <span class="o">&</span><span class="n">a</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>One key thing to note is that the 'write' passes elements into the
buffer by value while the 'read' passes elements out of the buffer by
reference. This is really important to note!</p>
<p>Additionally, I took Elliot's original example and re-worked it mildly
to function identically with this cbuffer library instead of the inline
code. It only took a few minutes and it is now its own stand-alone
library. Thank you for your contribution, Elliot!</p>
<p>As always, code can be found on
<a href="https://github.com/slightlynybbled/embed_with_elliot-circular_buffer">github</a>.</p>
<p>A note from the future: This article is a bit out-dated. Please visit
the <a href="http://www.forembed.com/circular-buffers-part-two.html">next entry</a> for the updated
entries after you have read this article!</p>MSP430 vs. PIC242016-02-04T16:43:00-05:002016-02-04T16:43:00-05:00Jason Jonestag:www.forembed.com,2016-02-04:/msp430-vs-pic24.html<h1>Intro</h1>
<p>I believe that I should start out saying that I like both the MSP430 AND
the PIC24 series. I was doing some assembly coding on the MSP430 close
to the time that I was doing some assembly work on the PIC24 and I
noticed that things were taking significantly …</p><h1>Intro</h1>
<p>I believe that I should start out saying that I like both the MSP430 AND
the PIC24 series. I was doing some assembly coding on the MSP430 close
to the time that I was doing some assembly work on the PIC24 and I
noticed that things were taking significantly longer to code on the
MSP430. It just seems like the PIC24 and dsPIC series simply have more
instructions available.</p>
<p>Inspired by this thought, I consulted <a href="https://www.google.com/">The
Oracle</a> and found this
<a href="http://www.ti.com/lit/an/slaa205c/slaa205c.pdf">paper</a> from 2006
(revisited in 2009) comparing the MSP430 to several other competitive
architectures.</p>
<h1>Flash Memory Usage</h1>
<p>In all tests, the PIC24 and dsPIC had nearly twice the flash memory used
for program space. This is not surprising as I know that the PIC24 has
a 24-bit instruction width while (I think but could not confirm) the
MSP430 has a 16-bit instruction width.</p>
<p>This instruction width is a double-edged sword. By making their
instructions wider, Microchip has made more instructions available to
their users. Even the lowliest PIC24 has a multiply and div instruction
- not the case on the MSP430!</p>
<h1>Execution Time</h1>
<p>Unless I'm reading the paper wrong, it looks like the PIC24 wins on
execution time while the MSP430 wins on code density. There are some
caveats to mention. The first is that the dsPIC didn't handily trounce
the PIC24 and the MSP430 in the FIR filter benchmark. This tells me
that the IAR compiler that they are using probably doesn't take into
account the dsPIC's DSP engine. If that is factored in, MAC operations
become very fast and benchmarks such as FIR filters will often half the
execution cycles.</p>
<p>An aside regarding raw speed for a moment. The MSP430 tops out at
25MIPS while the PIC24 and dsPIC33 series both top out at 70MIPS (look
at the 'E' series, as in PIC24E). The ability to scale the clock up to
that point can be seriously advantageous if speed is what the
application requires.</p>
<h1>Peripherals</h1>
<p>The other distinguishing thing about the dsPIC and PIC24 series is the
'completeness' and selection of the peripherals. The Microchip
offerings tend to have more transistors dedicated to making coding
easier.</p>
<p>A quick for-instance regarding a recent SPI project in which I was
writing code for an MSP430FR SPI slave. I was working with a high-end
MSP430FR series microcontroller that cost me nearly \$10/part and I am
dealing with SPI transfers that only have a 1-deep buffer. This doesn't
sound so bad, but the Microchip offerings tend to have 4-deep FIFOs on
this sort of thing, which gives you more luxury than you might imagine.
If you only have a 1-deep FIFO and you are trying to code a slave
device to be responsive at 1MHz, you have to tread very carefully. If
another interrupt is active at the time, forget about it. The 4-deep
FIFO greatly reduces the strain on the code.</p>
<h1>Memory Capacity</h1>
<p>That said, the MSP430FR series uses ferromagnetic technology that allows
you to use the program memory just like you would use SRAM and <em>at full
speed</em>. I have never had so much potential RAM available and that
aspect has definitely added some 'slack' in the form of buffers. I can
set back and let the buffers fill and then respond to them when I can
process multiple buffers at once. I don't see anything on Microchip's
offerings that seem to be heading in this direction.</p>
<h1>Summary</h1>
<p>In the future, I am likely to use both TI and Microchip offerings for
different applications. When the application demands a lot of
peripherals, I will probably lean towards Microchip. When it needs to
be single-tasked and cheap OR needs lots of RAM (ferromagnetic memory),
I will lean towards TI.</p>Simple Embedded Unit Testing2016-02-03T23:08:00-05:002016-02-03T23:08:00-05:00Jason Jonestag:www.forembed.com,2016-02-03:/simple-embedded-unit-testing.html<h1>Intro</h1>
<p>All of the guys who are software gurus do some sort of unit testing...
and you should too. There are lots of tools out there, from the very
slimmest to some very expensive packages. I'm going to show you the
quick and dirty method that will cover 80% of …</p><h1>Intro</h1>
<p>All of the guys who are software gurus do some sort of unit testing...
and you should too. There are lots of tools out there, from the very
slimmest to some very expensive packages. I'm going to show you the
quick and dirty method that will cover 80% of your low-level
functionality without breaking a sweat. Note that these techniques will
<em>ONLY</em> test a certain class of functions and will not test whole-program
execution. If your function takes parameters and returns values based
SOLELY on those parameters (think math or algorithms), then read on! If
your program reads 10 bytes from the SPI port and processes that and
returns it back to some other hardware, you should look elsewhere.</p>
<h1>Test Framework</h1>
<p>For this tutorial, I am not using a test framework! But you should know
that there are test frameworks out there that will go through your test
code and generate automated test reports. These test frameworks are
geared towards x86/x64 architectures, so you won't be able to easily
just drop most of them into your project and simulator and hit the road.
My favorite test framework thus far is
<a href="http://www.throwtheswitch.org/unity/">Unity</a>. I will probably look for
a good way to integrate this into MPLAB X and Code Composer Studio at
some point because running automated tests on your code is AWESOME! You
find so many bugs and, when a bug gets fixed, you can integrate the
scenario that revealed your bug into the permanent test suite.</p>
<h1>Non Test Framework</h1>
<p>We are going to emulate the functionality of the test framework in the
simulator. There is nothing that would keep you from doing the same
thing with a debugger on the silicon as well.</p>
<p>Fire up your project and create a 'main' file. Include the file that
you are testing. This sounds obvious, but it is easy to forget. Be
sure that you can compile code before you move on. You should have a
main function and all of the standard jazz that allows you to compile
before proceeding.</p>
<h2>Declare Assert Functions</h2>
<p>Just above your 'main' function, declare your 'ASSERT' functions. In a
normal test framework, these are usually macros rather than functions.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cm">/* variables */</span>
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">tests_executed</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">tests_passed</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">tests_failed</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">TEST_ASSERT_FLOAT_WITHIN</span><span class="p">(</span><span class="kt">float</span> <span class="n">tolerance</span><span class="p">,</span> <span class="kt">float</span> <span class="n">desiredValue</span><span class="p">,</span> <span class="kt">float</span> <span class="n">testValue</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">TEST_ASSERT_EQUAL_INT16</span><span class="p">(</span><span class="kt">int16_t</span> <span class="n">n0</span><span class="p">,</span> <span class="kt">int16_t</span> <span class="n">n1</span><span class="p">);</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<h2>Write Test Function Implementations</h2>
<p>Next, write your test function implementations. In my case, I have two
types of tests that I'm running. The first is a check for float
equality and the second is a check for integer equality. Since checking
float equality is always a bad idea, this ASSERT has a tolerance
associated with it.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">TEST_ASSERT_FLOAT_WITHIN</span><span class="p">(</span><span class="kt">float</span> <span class="n">tolerance</span><span class="p">,</span> <span class="kt">float</span> <span class="n">desiredValue</span><span class="p">,</span> <span class="kt">float</span> <span class="n">testValue</span><span class="p">){</span>
<span class="kt">float</span> <span class="n">maxValue</span> <span class="o">=</span> <span class="n">desiredValue</span> <span class="o">+</span> <span class="n">tolerance</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">minValue</span> <span class="o">=</span> <span class="n">desiredValue</span> <span class="o">-</span> <span class="n">tolerance</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">pass</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">testValue</span> <span class="o">></span> <span class="n">maxValue</span><span class="p">){</span>
<span class="n">pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">testValue</span> <span class="o"><</span> <span class="n">minValue</span><span class="p">){</span>
<span class="n">pass</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">pass</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Test Failure</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\t</span><span class="s">Expected Value: %f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">desiredValue</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\t</span><span class="s">Test Value: %f</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">double</span><span class="p">)</span><span class="n">testValue</span><span class="p">);</span>
<span class="n">tests_failed</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">tests_passed</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">tests_executed</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">TEST_ASSERT_EQUAL_INT16</span><span class="p">(</span><span class="kt">int16_t</span> <span class="n">n0</span><span class="p">,</span> <span class="kt">int16_t</span> <span class="n">n1</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">n0</span> <span class="o">!=</span> <span class="n">n1</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"Test Failure</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\t</span><span class="s">Expected Value: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">n0</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\t</span><span class="s">Test Value: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">n1</span><span class="p">);</span>
<span class="n">tests_failed</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">tests_passed</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">tests_executed</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>I am using MPLAB X, so I am using printf statements to allow me to view
the tests that failed directly on the console within the simulator.</p>
<h2>Write Tests</h2>
<p>This seems obvious, but the mechanics may be a bit mysterious. Simply
use your test functions to check the functionality of your test
functions.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">TEST_ASSERT_FLOAT_WITHIN</span><span class="p">(</span><span class="mf">0.000031</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">q15_to_dbl</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
<span class="n">TEST_ASSERT_EQUAL_INT16</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">q15_to_int</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>I have one instance of each type of test above. I have made this
intentionally small for the sake of illustration. You should probably
have 4 to 10 tests for each of your functions. Try to think of edge
cases and be sadistic! Always test the '0' values along with the max
and mins. The while loop is just to 'catch' my debugger. I usually
place a breakpoint here so that my tests execute and then notify
me when they are complete.</p>
<h2>Execute Tests</h2>
<p>Finally, build and execute. At the completion of each test run, I audit
the console output and determine which tests failed, make the appropriate
corrections, and re-run the tests.</p>
<p>For a more complete version of this, take a look at the
<a href="https://github.com/slightlynybbled/libmathq15">Q1.15</a> library and its
associated <a href="https://github.com/slightlynybbled/libmathq15/tree/master/test/xc16-test/test_libmathq15.X">test
project</a>.</p>How to Emulate Multiple Interrupt Vectors with a Single Interrupt2016-02-03T17:41:00-05:002016-02-03T17:41:00-05:00Jason Jonestag:www.forembed.com,2016-02-03:/how-to-emulate-multiple-interrupt-vectors-with-a-single-interrupt.html<h1>Intro</h1>
<p>There are some really great chips out there, many with almost
unbelievable numbers of interrupt vectors. Unfortunately, it always
seems to come up that you have a single interrupt vector that has to be
split between two or more different events.</p>
<p>So you have been an embedded engineering for …</p><h1>Intro</h1>
<p>There are some really great chips out there, many with almost
unbelievable numbers of interrupt vectors. Unfortunately, it always
seems to come up that you have a single interrupt vector that has to be
split between two or more different events.</p>
<p>So you have been an embedded engineering for a while, you know the
drill... on interrupt, check the flag, execute the proper subtroutine,
clear the flag, etc.</p>
<p>I am going to introduce you to a technique that I like to code into my
APIs that emulates separate interrupt vectors, even when only one is
being used. This technique is a bit more complex at first glance, but
it makes your application more dynamic and adaptable to modification,
contributing to code reuse and stability.</p>
<h1>Setup</h1>
<p>I am going to demonstrate this using an MSP430G2231 from the TI
launchpad, but I have used this technique across manufacturers with no
variation on the basic process. For simplicity, I will simply attempt
to emulate multiple interrupt vectors on the port 1 interrupt pins since
this requires the simplest level of understanding.</p>
<p>As you may know, each pin on port 1 has the ability to interrupt on a
rising or falling edge. Unfortunately, each pin doesn't have its own
interrupt vector, so - if you have multiple pins that need to interrupt
the processor - you will need to check which pin caused the interrupt
within the interrupt service routine. This causes you to write and
re-write the interrupt routine as the application progresses towards the
finish line and is not a very dynamic approach in the long run.</p>
<p>We are going to write lower-level functions that will always take care
of the details of adding and removing interrupts for us, then associate
higher-level functions using a defined API in order to made adding and
removing interrupt functions easy and quick.</p>
<h2>Create your Library</h2>
<p>You don't have to separate this into its own file, but I do recommend
it. Organizing your project into different files allows you to focus on
the task at hand. In this case, I am going to create a library called
'dio' which consists of 'dio.h' and 'dio.c'.</p>
<p>Add the Header</p>
<p>By creating your header file first, you are forcing yourself to define
the API. In our case, our header - dio.h - only contains four
functions:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">DIO_makeOutput</span><span class="p">(</span><span class="kt">int</span> <span class="n">port</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pin</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">DIO_makeInput</span><span class="p">(</span><span class="kt">int</span> <span class="n">port</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pin</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">DIO_enableInterrupt</span><span class="p">(</span><span class="kt">int</span> <span class="n">port</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pin</span><span class="p">,</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)());</span>
<span class="kt">void</span> <span class="nf">DIO_disableInterrupt</span><span class="p">(</span><span class="kt">int</span> <span class="n">port</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pin</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>We could easily add more functionality to this library, but that would
distract from the goal of the article.</p>
<p>The 'makeInput' and 'makeOutput' functions are pretty easy to
understand. They make a pin an input or output pin.</p>
<p>The 'enableInterrupt' function takes three arguments: port, pin, and
functPtr. This last one is simply a function that you wish to be
executed when the interrupt occurs.</p>
<p>The 'disableInterrupt' simply disables interrupts on that pin.</p>
<h2>Declare Emulated Functions</h2>
<p>As hinted in the sample header file, function pointers are going to be
utilized to accomplish interrupt vector multiplexing. In your C file,
you will need to declare each interrupt for which you wish to emulate a
vector.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7
8</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p10FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p11FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p12FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p13FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p14FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p15FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p16FunctPtr</span><span class="p">)();</span>
<span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">DIO_p17FunctPtr</span><span class="p">)();</span>
</pre></div>
</td></tr></table>
<p>We are declaring variables, but this notation tells the compiler that we
expect these to operate like function pointers. There are other ways
that we can do this, but this also makes it clear to the reader that the
intent is for these to be function pointers.</p>
<h2>Write the ISR</h2>
<p>Now we get into the meat of the library. We are to write the ISR using
the interrupt vector given to us in 'msp430.h'.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#pragma vector=PORT2_VECTOR </span>
<span class="n">__interrupt</span> <span class="kt">void</span> <span class="nf">DIO_port2IntFunct</span><span class="p">(){</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>All of the actual interrupt code will be executed within this interrupt.
Finally, the interesting bit where your pin-specific code gets
executed:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#pragma vector=PORT1_VECTOR</span>
<span class="n">__interrupt</span> <span class="kt">void</span> <span class="nf">DIO_port1IntFunct</span><span class="p">(){</span>
<span class="cm">/* find out which pin triggered the interrupt */</span>
<span class="k">if</span><span class="p">(</span><span class="n">P1IFG</span> <span class="o">&</span> <span class="n">BIT0</span><span class="p">){</span>
<span class="cm">/* if the function pointer points to an address,</span>
<span class="cm"> * then execute that function call */</span>
<span class="k">if</span><span class="p">(</span><span class="n">DIO_p10FunctPtr</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">){</span>
<span class="p">(</span><span class="o">*</span><span class="n">DIO_p10FunctPtr</span><span class="p">)();</span>
<span class="p">}</span>
<span class="n">P1IFG</span> <span class="o">&=</span> <span class="o">~</span><span class="n">BIT0</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">P1IFG</span> <span class="o">&</span> <span class="n">BIT1</span><span class="p">){</span>
<span class="c1">// ... more code follows...</span>
</pre></div>
</td></tr></table>
<p>For each available interrupt flag, the code must check the flag, execute
the function point (only if there is one assigned!), and clear the
hardware interrupt flag. The actual function is pretty long, so I'm not
going to finish it here. You can find it on
<a href="https://github.com/slightlynybbled/msp430_multiplexed_interrupts/blob/master/dio.c">github</a>.</p>
<p>Quick aside for those of you that aren't familiar with the notation (I'm
looking at the Microchip guys!). Defines like 'BIT0' are simply
hard-coded numbers. In the case of 'BIT0', it translates to 0b00000001.
BIT1 would translate to 0b00000010. AVR and MSP430 series use this
notation pretty heavily for most SFR interactions.</p>
<h2>Implement Enablers</h2>
<p>Up to thing point things make sense, but we haven't implemented the
interrupt enabling function just yet.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">DIO_enableInterrupt</span><span class="p">(</span><span class="kt">int</span> <span class="n">port</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pin</span><span class="p">,</span> <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functPtr</span><span class="p">)()){</span>
<span class="cm">/* pins that interrupt should generally be inputs */</span>
<span class="n">DIO_makeInput</span><span class="p">(</span><span class="n">port</span><span class="p">,</span> <span class="n">pin</span><span class="p">);</span>
<span class="k">switch</span><span class="p">(</span><span class="n">port</span><span class="p">){</span>
<span class="k">case</span> <span class="mi">1</span><span class="o">:</span>
<span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">mask</span> <span class="o">=</span> <span class="mi">1</span> <span class="o"><<</span> <span class="n">pin</span><span class="p">;</span>
<span class="n">P1IFG</span> <span class="o">&=</span> <span class="o">~</span><span class="n">mask</span><span class="p">;</span> <span class="c1">// clear the interrupt flag</span>
<span class="n">P1IE</span> <span class="o">|=</span> <span class="n">mask</span><span class="p">;</span> <span class="c1">// enable interupts on this pin</span>
<span class="n">__bis_SR_register</span><span class="p">(</span><span class="n">GIE</span><span class="p">);</span> <span class="c1">// enable global interrupts</span>
<span class="cm">/* associate the function pointer */</span>
<span class="k">switch</span><span class="p">(</span><span class="n">pin</span><span class="p">){</span>
<span class="k">case</span> <span class="mi">0</span><span class="o">:</span> <span class="n">DIO_p10FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">1</span><span class="o">:</span> <span class="n">DIO_p11FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">2</span><span class="o">:</span> <span class="n">DIO_p12FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">3</span><span class="o">:</span> <span class="n">DIO_p13FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">4</span><span class="o">:</span> <span class="n">DIO_p14FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">5</span><span class="o">:</span> <span class="n">DIO_p15FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">6</span><span class="o">:</span> <span class="n">DIO_p16FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">7</span><span class="o">:</span> <span class="n">DIO_p17FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">default</span><span class="o">:</span> <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">case</span> <span class="mi">2</span><span class="o">:</span>
<span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">mask</span> <span class="o">=</span> <span class="mi">1</span> <span class="o"><<</span> <span class="n">pin</span><span class="p">;</span>
<span class="n">P2IFG</span> <span class="o">&=</span> <span class="o">~</span><span class="n">mask</span><span class="p">;</span> <span class="c1">// clear the interrupt flag</span>
<span class="n">P2IE</span> <span class="o">|=</span> <span class="n">mask</span><span class="p">;</span> <span class="c1">// enable interupts on this pin</span>
<span class="n">__bis_SR_register</span><span class="p">(</span><span class="n">GIE</span><span class="p">);</span> <span class="c1">// enable global interrupts</span>
<span class="cm">/* associate the function pointer */</span>
<span class="k">switch</span><span class="p">(</span><span class="n">pin</span><span class="p">){</span>
<span class="k">case</span> <span class="mi">6</span><span class="o">:</span> <span class="n">DIO_p26FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="mi">7</span><span class="o">:</span> <span class="n">DIO_p27FunctPtr</span> <span class="o">=</span> <span class="n">functPtr</span><span class="p">;</span> <span class="k">break</span><span class="p">;</span>
<span class="k">default</span><span class="o">:</span> <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">default</span><span class="o">:</span>
<span class="p">{</span>
<span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// programmer's trap</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>First, a mask is created that is useful for setting the correct
interrupt bits. The interrupt flag is cleared, interrupts enabled, and
the global interrupt bit is set.</p>
<p>Finally, depending on the pin that was selected, the proper function
pointer is loaded with the function pointer that was supplied in the
parameters. This is the final link that we need to get interrupts up
and going on this processor.</p>
<p>The 'default: while(1);' is a programmer's trap that I always place in
switch/case statements. If I type an invalid value into the parameters,
the program will get stuck right here when I'm debugging. When I pause
the debugger, I will see immediately where I went wrong.</p>
<h2>Implement Disablers</h2>
<p>Why would you want to disable interrupts? I admit, this doesn't come up
often, but adding the functionality is trivial once you have coded up to
this point and it can add an interesting dynamism. For instance, you
can have two interrupts 'competing' and one of them disable the other.
This might be useful in a Jeopardy-style hand buzzer.</p>
<p>You should try to figure this one out yourself, but the source is
available
on <a href="https://github.com/slightlynybbled/msp430_multiplexed_interrupts/blob/master/dio.c">github</a> if
you'd like to take a look.</p>
<h1>Use the Function, Luke</h1>
<p>So, up to this point you haven't done anything on the processor! Open
your 'main.c' file and we will write a quick code that pits two
interrupts against one another!</p>
<p>The goal is to write two 'interrupt' functions which - when executed -
will disable the other interrupt function.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#define LED1 BIT0</span>
<span class="cp">#define LED2 BIT6</span>
<span class="kt">void</span> <span class="nf">p13Int</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
<span class="kt">void</span> <span class="nf">p17Int</span><span class="p">(</span><span class="kt">void</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>First, we add a couple of defines that will make our lives easier when
setting LEDs. Next, we declare our interrupt functions. I am
associating these functions with pins 1.3 and 1.7, which explains the
cryptic names. I usually try to add a more descriptive name that deals
with the functionality, maybe 'deathToP17' might have been more
appropriate?</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="n">WDTCTL</span> <span class="o">=</span> <span class="n">WDTPW</span> <span class="o">|</span> <span class="n">WDTHOLD</span><span class="p">;</span> <span class="c1">// Stop watchdog timer</span>
<span class="cm">/* adding interrupt is as simple as a function</span>
<span class="cm"> * call with port, pin, and function pointer */</span>
<span class="n">DIO_enableInterrupt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="o">&</span><span class="n">p13Int</span><span class="p">);</span>
<span class="n">DIO_enableInterrupt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="o">&</span><span class="n">p17Int</span><span class="p">);</span>
<span class="n">P1DIR</span> <span class="o">|=</span> <span class="p">(</span><span class="n">LED1</span> <span class="o">|</span> <span class="n">LED2</span><span class="p">);</span> <span class="c1">// use to light the LEDs</span>
<span class="n">P1OUT</span> <span class="o">|=</span> <span class="p">(</span><span class="n">LED1</span> <span class="o">|</span> <span class="n">LED2</span><span class="p">);</span> <span class="c1">// turn the LEDs on</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>The 'main' function is incredibly simple (my apologies for the spacing).
We execute the 'DIO_enableInterrupt' function twice, one for each pin
and function. Now <em>this </em>is why we went through the trouble of coding
all of the ISR setting code back there. Using the ISR is now incredibly
simple and - fortunately for us - the MSP430 series is very consistent
on its interrupts, so you may never need to hand-code a DIO ISR again!</p>
<p>The LED setup are just for my own debugging, but I'm leaving it in
there.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">void</span> <span class="nf">p13Int</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="cm">/* using the 'dio' files, we can easily</span>
<span class="cm"> * manipulate this interrupt or other interrupts */</span>
<span class="n">DIO_disableInterrupt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">);</span>
<span class="n">P1OUT</span> <span class="o">^=</span> <span class="n">LED1</span><span class="p">;</span> <span class="c1">// toggle LED1</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">p17Int</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
<span class="n">DIO_disableInterrupt</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="n">P1OUT</span> <span class="o">^=</span> <span class="n">LED2</span><span class="p">;</span> <span class="c1">// toggle LED2</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p>Finally, the interrupt functions themselves! Each function simply
disables the other function and toggles the LED.</p>
<p>Remember, if you place any variables in here, they should still be
declared 'volatile'!</p>
<p>The complete source code can be found on
<a href="https://github.com/slightlynybbled/msp430_multiplexed_interrupts/">github</a>.
Just place the files into your CCS project.</p>MSP430 - Writing Assembly for use in C2016-02-02T20:21:00-05:002016-02-02T20:21:00-05:00Jason Jonestag:www.forembed.com,2016-02-02:/msp430-writing-assembly-for-use-in-c.html<h1>Intro</h1>
<p>This tutorial is geared towards those who want to write hand-optimized
assembly code for the MSP430, but don't quite know how to get started.
If you are interested in writing assembly on the PIC24 or dsPIC33
series, see our other tutorial on <a href="http://www.forembed.com/xc16-writing-assembly-for-use-in-c.html">writing assembly for use in C using …</a></p><h1>Intro</h1>
<p>This tutorial is geared towards those who want to write hand-optimized
assembly code for the MSP430, but don't quite know how to get started.
If you are interested in writing assembly on the PIC24 or dsPIC33
series, see our other tutorial on <a href="http://www.forembed.com/xc16-writing-assembly-for-use-in-c.html">writing assembly for use in C using
the XC16</a>
compiler!</p>
<p>This tutorial assumes that you are using TI's Code Composer Studio,
which is the 'default' IDE for the MSP430 series. It would help to be
reasonably familiar with Eclipse, but not totally necessary.</p>
<h1>The Basics</h1>
<h2>Create your project</h2>
<p>This is similar to most other IDE's. I had an old launchpad around, so
I used the MSP430G2231 that came with it to configure my project around.
I created my project with an empty main.c file. This isn't necessary
since the <a href="https://github.com/slightlynybbled/msp430_mixed">github page</a>
that I'm supplying to you already has a main.c, but if you are creating
your own blank project, you will need to create main.c (or whatever you
name your highest level file) at some point. Note the directory that
your project is saved in, you will need it later!</p>
<h2>Create header file</h2>
<p>Create a header file in which the assembly file and any associated C
functions are declared. In the example project, we simply named this
‘mylib.h’. Note that this declaration is like any other function
declaration – no underscores, return type, parameter, etc.</p>
<h2>Create assembly file</h2>
<p>The basics are in the compiler documentation, but looking at the source
will still help you out. You must create the file with a ‘.s’
extension. Additionally, DO NOT name the file the same as your C file.
For instance, if you are writing a library called ‘mylib.c’, then do
not name your assembly file ‘mylib.s’. Both of these potentially create
a ‘mylib.o’ object file, which will confuse the linker. Stick with
‘mylib_msp430.s’, or something like that. Besides, if you port to the
another processor achitecture, you can always write that assembly code
into ‘mylib_xc16.s’ or similar.</p>
<p>The MSP430 compiler handles include files assembly in a strange way.
You must type:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="nf">.cdecls</span> <span class="nv">C</span><span class="p">,</span> <span class="nv">list</span><span class="p">,</span> <span class="err">“</span><span class="nv">msp430.h</span><span class="err">“</span>
</pre></div>
</td></tr></table>
<p>This allows you to include a standard C header, 'msp430.h', into an
assembly file. Normally, you would include a '.inc' file into assembly.
This allows your C and assembly source to share the same file during
compilation. If you don't understand this, you can read the compiler
documentation or simply accept it. In truth, it doesn't have a lot of
bearing once you are elbow-deep in assembly.</p>
<p>In your header, your function might be called ‘add’. You should also
name your function within the assembler 'add'. When the compiler looks
at your code, it is only looking for ‘global’ symbols to link with your
C code. As a result, you need to add an additional declaration to the
top of your file: ‘.global add’. This must be exactly the same as the
assembly 'function' is named within your assembly file.</p>
<h2>Write assembly</h2>
<p>Write the assembly code into your function. Feel free to name sections
of your code anything you like. As long as they are not declared
‘.global’, then the compiler won’t see them as C functions.</p>
<p>In this architecture, parameter passing is accomplished using the
working registers w12-w15. Remember that these registers are 16-bit
registers. If a function takes two 16-bit parameters, they will be
located in w12 and w13. If a function takes a single 32-bit parameter,
that parameter is located in [w13:w12] with w12 being the low word.
There are lots of details to this, so it would really help to read the
docs!</p>
<p>You can add other functions into this file as well, just be sure to
perform the same steps so that the compiler knows the declared global
symbols.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="nf">.cdecls</span> <span class="nv">C</span><span class="p">,</span> <span class="nv">list</span><span class="p">,</span> <span class="s">"msp430.h"</span> <span class="c1">; this allows us to use C headers</span>
<span class="nf">.text</span> <span class="c1">; locates code in 'text' section</span>
<span class="nf">.global</span> <span class="nv">add</span> <span class="c1">; declares 'add' as global</span>
<span class="nl">add:</span> <span class="c1">; this is the function itself</span>
<span class="c1">; if bit 15 is set, then jump to add_neg</span>
<span class="nf">bit.w</span> <span class="err">#</span><span class="mi">32768</span><span class="p">,</span> <span class="nv">r12</span>
<span class="nf">jc</span> <span class="nv">add_neg</span>
<span class="nl">add_pos:</span>
<span class="nf">bit.w</span> <span class="err">#</span><span class="mi">32768</span><span class="p">,</span> <span class="nv">r13</span>
<span class="nf">jc</span> <span class="nv">add_pos_neg</span>
<span class="nl">add_pos_pos:</span> <span class="c1">; this label isn't declared global, so it is only available locally</span>
<span class="nf">add.w</span> <span class="nv">r13</span><span class="p">,</span> <span class="nv">r12</span>
<span class="nf">jn</span> <span class="nv">add_pos_pos_sat</span>
<span class="nf">ret</span>
<span class="nl">add_neg:</span>
<span class="nf">bit.w</span> <span class="err">#</span><span class="mi">32768</span><span class="p">,</span> <span class="nv">r13</span>
<span class="nf">jc</span> <span class="nv">add_neg_neg</span>
<span class="nl">add_pos_neg:</span>
<span class="nf">add.w</span> <span class="nv">r13</span><span class="p">,</span> <span class="nv">r12</span>
<span class="nf">ret</span>
<span class="nl">add_pos_pos_sat:</span>
<span class="nf">mov</span> <span class="err">#</span><span class="mi">32767</span><span class="p">,</span> <span class="nv">r12</span>
<span class="nf">ret</span>
<span class="nl">add_neg_neg:</span>
<span class="nf">add.w</span> <span class="nv">r13</span><span class="p">,</span> <span class="nv">r12</span>
<span class="c1">; result should be negative</span>
<span class="nf">bit.w</span> <span class="err">#</span><span class="mi">32768</span><span class="p">,</span> <span class="nv">r12</span>
<span class="nf">jnc</span> <span class="nv">add_neg_neg_sat</span>
<span class="nf">ret</span>
<span class="nl">add_neg_neg_sat:</span>
<span class="nf">mov</span> <span class="err">#</span><span class="mi">32768</span><span class="p">,</span> <span class="nv">r12</span>
<span class="nf">ret</span>
<span class="nf">.end</span>
</pre></div>
</td></tr></table>
<h2>Prepare to Compile</h2>
<p>If all of these files are within the project directory, then you should
be ready to rock!</p>
<p>If the files are somewhere else in your directory tree, you may have to
add the source files to your project and include directories to your gcc
options.</p>
<p>As mentioned previously, there is a <a href="https://github.com/slightlynybbled/msp430_mixed">starter file set in
github</a> to help you get
started. Just download and execute!</p>How Fixed-Point Math Works2016-02-01T06:35:00-05:002016-02-01T06:35:00-05:00Jason Jonestag:www.forembed.com,2016-02-01:/how-fixed-point-math-works.html<h1>What is Fixed-Point Math?</h1>
<p>Fixed-point math is math in which the decimal doesn’t move relative to
the digits composing the number. Some call this ‘integer math’, which is
a reference to the integer form that numbers normally take on the
processor in fixed-point notations. Others call this ‘q math …</p><h1>What is Fixed-Point Math?</h1>
<p>Fixed-point math is math in which the decimal doesn’t move relative to
the digits composing the number. Some call this ‘integer math’, which is
a reference to the integer form that numbers normally take on the
processor in fixed-point notations. Others call this ‘q math’ for the
common q notation that this method has become known by – more on this
later.</p>
<h1>Why would you want to Use Fixed-Point Math?</h1>
<p>There is one overriding reason to use fixed-point math: speed. Depending
on the processor, floating-point math can take many times more cycles to
execute than fixed-point math. The key is to know your processor’s
capabilities. If you have a floating-point unit (FPU) on the processor,
then the speed comparison of fixed-point vs. floating-point will be much
more closely aligned. The list of processors without FPUs is long, so
some general guidelines:</p>
<ul>
<li>8-bit and 16-bit processors don’t have floating-point units. This is
not a limitation of technology, just the way things tend to shake
out when manufacturers are designing the technology</li>
<li>Microchip’s PICxx – including dsPIC – don’t have a floating point
unit until you get to the PIC32 series</li>
<li>Atmel’s ATTINY and ATMEGA don’t have them</li>
<li>STMicro’s STM32Fx series doesn’t include them until the ‘x’ gets to
4 or larger</li>
<li>TI’s MSP430 series generally don’t have FPUs.</li>
</ul>
<p>You must consult the datasheet to be certain!</p>
<p>There is an additional reason to use fixed-point math instead of
floating-point math even when an FPU is available and that is that
fixed-point math tends to saturate much like real-world behavior. For
instance, PID loops written in fixed-point math have a maximum value
they can represent. In the real world, PWM has a maximum duty cycle
(100%) that it can apply. This works out very well. A floating point
implementation of a PID loop often has a statement that is something
like (pseudocode) ‘if output is greater than 100%, set output equal to
100%’. Fixed-point implementations naturally saturate, emulating the
hardware’s inherent limitations.</p>
<p>Usually, the decision to use fixed point notation is a combination of
processor capability, speed requirement, and frequency. For instance, if
you are executing a temperature PID loop, this loop probably execute at
a maximum of 1Hz. Even a long series of calculations will take a small
percentage of the processor’s time. On the other hand, a current control
algorithm for motor phase currents might execution 50000 times per
second. In this case, you probably want to go the fixed-point route.</p>
<h1>Why wouldn’t you want to use Fixed-Point Math?</h1>
<p>Fixed-point math has one limitation that can be severe: loss of
resolution. In most cases, the loss of resolution involved is trivial or
can be mitigated. In precision applications – such as measurement of a
voltage across a strain gauge arranged across a Wheatstone bridge – the
resolution loss would likely be intolerable and floating-point math
should probably be utilized. Additionally, if you have an on-die FPU,
your first draft should probably be floating point. Despite the loss in
precision, one may utilize fixed-point math and – a majority of the time
– have results that are good enough for the application.</p>
<h1>Fixed-Point Concepts</h1>
<h2>Fixed-Point Notation and Basic Operations</h2>
<p>I will not go into great depth when talking about floating-point
notation, but I would be remiss if I didn’t at least mention it. Before
writing the first line of code, you should know how many digits you have
and where the decimal is. For more in-depth regarding floating-point
notation, check out the Wikipedia article.<br>
The short version: start with the capital letter ‘Q’. Easy enough.
Next, the number of digits to the left of the decimal. Next, the
decimal. Then the number of digits to the right of the decimal.
Generally, the uppermost digit is considered a sign bit. As the number
of digits to the right of the decimal increases, the precision of the
format increases.</p>
<p>A Q16.16 number has twice the ‘resolution’ as a Q1.15, because the
number to the right of the decimal has one more bit. It has 32768 times
the range (the upper bit in each is a sign bit. A Q8.8 has a larger
range than a Q1.15 number, but has a much more limited resolution. You
may find that your application does not have enough resolution and/or
range in one fixed-point format, but does in another. I have found that
Q1.15 is one of the best first-draft formats on 16-bit devices. If you
are rocking a 32-bit device, Q16.16 or Q1.31 might be more appropriate.</p>
<p>One more quick note regarding notation… notations that have a ‘1’ next
to the ‘Q’ are also commonly referred to as ‘fractional’ since they can
only represent numbers between -1.0 and +0.999. This appears to be a
severe limitation but – as we will see – this notation often emulates
the way we think about processes (think max speed) and – even better –
the way things often work in the real world.</p>
<p>One of the most common formats to use on 16-bit processors is Q1.15,
which we will use for the remainder of this article. Note that the
number of digits adds up to ‘16’, which is a nice convenient value that
is well-defined in C with ‘int16_t’. Note also that there is one digit
to the left of the decimal and that this digit is a sign bit. A 16-bit
signed value in integer math would have a range of -32768 to +32767. In
Q1.15 math, this same range is mapped to -1.0 to +0.9999. Note that
‘Q1.15’ is often shortened to ‘Q15’.</p>
<p>For illustration, we should examine the behavior of Q1.15 math just to
ensure that some of the implications are clear and to assist in
debugging later. You – the user – will never see a value of 0.5 or any
other decimal-like notation in the registers of your device. You will
only see the integer representation of that value. Therefore, you need
to know the basic logic. Presented here are some comparisons of generic
operations to get you thinking.</p>
<table class="table table-bordered table-striped">
<tr>
<th>Floating-point Operation</th>
<th>Q1.15 Equivalent</th>
<th>Remarks</th>
</tr>
<tr>
<td>0.5 × 0.5 = 0.25</td>
<td>16384 × 16384 = 8192</td>
<td>May take some getting used to, but makes perfect sense…</td>
</tr>
<tr>
<td>0.25/0.50 = 0.50</td>
<td>8192 / 16384 = 16384</td>
<td>…again, makes sense…</td>
</tr>
<tr>
<td>0.5 + 0.25 = 0.75</td>
<td>16384 + 8192 = 24576</td>
<td>…on a roll…</td>
</tr>
<tr>
<td>0.75 + 0.5 = 1.25</td>
<td>24576 + 16384 = 32767</td>
<td>…wait, what? Remember, the largest value that can be represented in Q1.15 is 32767, which is roughly equivalent to 0.9999. This demonstrates the saturating nature of Q math.</td>
</tr>
<tr>
<td>-0.75 – 0.5 = -1.25</td>
<td>-24576 – 16384 = -32768</td>
<td>Again, the value saturates negatively the same as it does positively. The only difference is that we can represent -1.0.</td>
</tr>
<tr>
<td>0.5 / 0.25 = 2.0</td>
<td>16384/8192=undefined</td>
<td>Remember, the largest value we can represent is 0.9999, so we cannot represent 2.0. Technically this value is undefined, but it always makes sense to me to implement as a saturated value.</td>
</tr>
</table>
<h2>Q1.15 Implementations</h2>
<p>Manufacturers will sometimes include ‘.a’ files with headers with their
embedded compilers. Often, these libraries are good and work adequately,
but do not always work consistently across implementations. In other
words, Microchip’s libraries won’t necessarily work the same around the
edge cases nor use the same names as Atmel’s libraries.</p>
<p>Understand that there are fixed-point libraries out there – many of them
higher precision. Additionally, there are more functions than those
below – such as trigonometric functions. I am covering the basic
addition/multiplication/division to illustrate the concepts. Once you
get the basic functions, you should be able to apply the concepts to any
fixed-point library. A quick GitHub search reveals several libraries,
but most are geared towards Q16.16 formatted numbers.</p>
<p>These basic routines should work on all implementations, but – to be
sure – they are not the most efficient implementation on most
processors! For instance, in the dsPIC series, the on-chip DSP has a
mode which operates in Q1.15 mode natively. Not to mention that
floating-point operations have been highly optimized on most compilers
and operate very well. As a result, the below ‘pure-C’ functions are not
likely to outperform a hand-tuned assembly function in most cases… but
they can be illustrative of the concepts. Take a look at the <a href="https://github.com/slightlynybbled/libmathq15/blob/master/readme.md">github
page</a>
I created for this project for a more detailed implementation.</p>
<h3>Type Definitions</h3>
<p>From an implementation standpoint, it doesn’t matter whether you create
a type definition or not. This just makes your code more readable.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="k">typedef</span> <span class="kt">int16_t</span> <span class="n">q15_t</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<h3>Addition/Subtraction</h3>
<p>In this section of code, note the manual saturation that occurs. This
is a 'correct' implementation, but shifting bits around is not the most
efficient method on almost any platform. Hand-tuned assembly can handle
this in just a few cycles.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5
6
7</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">q15_t</span> <span class="nf">q15_add</span><span class="p">(</span><span class="n">q15_t</span> <span class="n">addend</span><span class="p">,</span> <span class="n">q15_t</span> <span class="n">adder</span><span class="p">){</span>
<span class="kt">int32_t</span> <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)</span><span class="n">addend</span> <span class="o">+</span> <span class="p">(</span><span class="kt">uint32_t</span><span class="p">)</span><span class="n">adder</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">result</span> <span class="o">></span> <span class="mi">32767</span><span class="p">)</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">32767</span><span class="p">;</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">result</span> <span class="o"><</span> <span class="o">-</span><span class="mi">32768</span><span class="p">)</span> <span class="n">result</span> <span class="o">=</span> <span class="o">-</span><span class="mi">32768</span><span class="p">;</span>
<span class="k">return</span> <span class="p">(</span><span class="n">q15_t</span><span class="p">)</span><span class="n">result</span><span class="p">;</span>
</pre></div>
</td></tr></table>
<h3>Multiplication</h3>
<p>The multiplication routine is surprisingly simple. There are no
'if-then' statements because all numbers are in the range of -1.0 to
+0.99997, therefore, there is no opportunity for saturation.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">q15_t</span> <span class="nf">q15_mul</span><span class="p">(</span><span class="n">q15_t</span> <span class="n">multiplicand</span><span class="p">,</span> <span class="n">q15_t</span> <span class="n">multiplier</span><span class="p">){</span>
<span class="kt">int32_t</span> <span class="n">product</span> <span class="o">=</span> <span class="p">((</span><span class="kt">int32_t</span><span class="p">)</span><span class="n">multiplicand</span> <span class="o">*</span> <span class="p">(</span><span class="kt">int32_t</span><span class="p">)</span><span class="n">multiplier</span><span class="p">)</span> <span class="o">>></span> <span class="mi">15</span><span class="p">;</span>
<span class="k">return</span> <span class="p">(</span><span class="n">q15_t</span><span class="p">)</span><span class="n">product</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<h3>Division</h3>
<p>Unfortunately, division presents us with a couple of edge cases. These
aren't difficult to deal with, but they do eat cycles.</p>
<p>The most obvious is that the divisor must not be '0'. As you learned in
math class, this is either infinity or undefined (depending on who is
teaching, from what I recall).</p>
<p>A more subtle - but just as important! - aspect is that the magnitude
(some of you say 'absolute value') of the dividend must be smaller than
the divisor. Go back to the table if you don't see this.</p>
<p>In either case, we will simply saturate in the appropriate direction by
XORing the most significant bit in each and deciding - based on that -
what the saturated value should be.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">q15_t</span> <span class="nf">q15_div</span><span class="p">(</span><span class="n">q15_t</span> <span class="n">dividend</span><span class="p">,</span> <span class="n">q15_t</span> <span class="n">divisor</span><span class="p">){</span>
<span class="n">q15_t</span> <span class="n">quotient</span><span class="p">;</span>
<span class="cm">/* check to ensure dividend is smaller in magnitude</span>
<span class="cm"> * than the divisor */</span>
<span class="k">if</span><span class="p">((</span><span class="n">q15_abs</span><span class="p">(</span><span class="n">divisor</span><span class="p">)</span> <span class="o"><</span> <span class="n">q15_abs</span><span class="p">(</span><span class="n">dividend</span><span class="p">))</span> <span class="o">||</span> <span class="p">(</span><span class="n">divisor</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)){</span>
<span class="cm">/* saturation: if signs are different,</span>
<span class="cm"> * then saturate negative */</span>
<span class="k">if</span><span class="p">((</span><span class="n">divisor</span> <span class="o">&</span> <span class="mh">0x8000</span><span class="p">)</span> <span class="o">^</span> <span class="p">(</span><span class="n">dividend</span> <span class="o">&</span> <span class="mh">0x8000</span><span class="p">)){</span>
<span class="n">quotient</span> <span class="o">=</span> <span class="o">-</span><span class="mi">32768</span><span class="p">;</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">quotient</span> <span class="o">=</span> <span class="mi">32767</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span><span class="k">else</span><span class="p">{</span>
<span class="n">quotient</span> <span class="o">=</span> <span class="mi">32768</span> <span class="o">*</span> <span class="n">dividend</span><span class="o">/</span><span class="n">divisor</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">quotient</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<h2>Using Q1.15</h2>
<p>With Q1.15 math, it is useful to begin thinking of variables as
percentages of maximum rather than absolute values. For instance, you
might say that the voltage of an A/D is 1.65V of a 3.3V max or you could
say that it is at 50%. Another example, you can say that the speed of a
motor is at 2700RPM of a maximum of 3000RPM or you could say that the
motor speed is at 90%. I try to implement Q1.15 conversions at all of
the ‘edges’ of my program while all of the internals work natively in
Q1.15. This contributes to code reuse and – in many cases – the code
auto-scales when I change peripheral resolution. A couple of useful
examples might be in order…</p>
<h3>Scaling a Voltage</h3>
<p>You have a 10-bit A/D converter. Again, you don’t care so much about the
particular number so much as the percentage of maximum that the A/D is
set to. We know that our maximum value is 1023, but we have to keep the
divisor larger than the dividend in Q1.15 math, so we will use 1024 as
the divisor.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#define AD_DIVISOR 1024 </span>
<span class="n">q15_t</span> <span class="n">scaledAD</span> <span class="o">=</span> <span class="n">q15_div</span><span class="p">((</span><span class="n">q15_t</span><span class="p">)</span><span class="n">rawADValue</span><span class="p">,</span> <span class="p">(</span><span class="n">q15_t</span><span class="p">)</span><span class="n">AD_DIVISOR</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>Now the ‘scaledAD’ variable contains the value of the A/D converter as a
fixed-point value. It is a good idea to scale ALL parameters to Q1.15
notation if ANY of them are to be in Q1.15 calculations. This ensures
that all variables are on the same scaling at all times. I don’t even
keep ‘native’ values in memory anymore. I simply calculate the
fixed-point equivalent and save that value instead.</p>
<h3>Scaling a PWM Value</h3>
<p>So you have your A/D reading in memory as a Q1.15 value and now you need
to scale that to a duty cycle register within a PWM module. PWM modules
usually have a dedicated timer and those timers have period registers
associated with them. One quick and easy way to scale your desired PWM
value to your period register is to use the multiply routine:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="kt">int16_t</span> <span class="n">dutyCycleRegister</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int16_t</span><span class="p">)</span><span class="n">q15_mul</span><span class="p">(</span><span class="n">scaledAD</span><span class="p">,</span> <span class="n">periodRegister</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>Now, you can change your PWM frequency at will using the period register
and the output duty cycle will always scale appropriately (within
resolution, of course… if your period register is ‘2’ then you will
still get very bad resolution no matter how precise the intermediate
maths).</p>
<h3>Scaling a Speed</h3>
<p>How would you go about calculating the motor speed in Q1.15? This is
more complicated than the previous examples, but is easier than you
might expect. First, you should know the maximum speed of the motor. In
this case, 3000RPM. You should be able to calculate the motor period
from this, which is</p>
<blockquote>
<p>1min/3000rotations × 60s/1min = 20ms/rotation</p>
</blockquote>
<p>Now we know that 20ms/rotation is the maximum speed of the motor. We can
use a timer to measure the motor period. This will be different in each
implementation, but for the sake of explanation, lets say that a timer
value of ‘500’ corresponds to 20ms/rotation. Use a define or constant in
your code:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#define MIN_ROTATIONAL_PERIOD 500</span>
</pre></div>
</td></tr></table>
<p>Then you can use the current motor period that was most easily measured
to easily calculate the speed:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="n">q15_t</span> <span class="n">rotationalSpeed</span> <span class="o">=</span> <span class="n">q15_div</span><span class="p">((</span><span class="n">Q15</span><span class="p">)</span><span class="n">MIN_ROTATIONAL_PERIOD</span><span class="p">,</span> <span class="p">(</span><span class="n">q15_t</span><span class="p">)</span><span class="n">measuredPeriod</span><span class="p">);</span>
</pre></div>
</td></tr></table>
<p>Lets say that the measuredPeriod was ‘1000’:</p>
<blockquote>
<p>500/1000=16384</p>
</blockquote>
<p>Remember, ‘16384’ corresponds to 50%, so now we know the speed as a
percentage of maximum speed!</p>
<h1>Summary</h1>
<p>Proper utilization of fixed-point math in embedded applications can
encourage code reuse, application flexibility, and provide the
appropriate resolution for the application.In a future post, we will
examine an application and use Q1.15 libraries to implement a PID loop!
Don't forget to check out the (more) <a href="https://github.com/slightlynybbled/libmathq15">complete source
code</a>!</p>
<p>You may also be interested in our article <a href="http://www.forembed.com/avoiding-division-and-modulo-operations.html">Avoiding Division and Modulo
Operations</a>.</p>XC16 - Writing Assembly for Use in C2016-02-01T02:28:00-05:002016-02-01T02:28:00-05:00Jason Jonestag:www.forembed.com,2016-02-01:/xc16-writing-assembly-for-use-in-c.html<h1>Intro</h1>
<p>So I have been playing with writing assembly again within the context of
C and I have run into the usual issues. I have decided to create a
quick little 'getting started' tutorial.</p>
<p>If you are interested in the same type of writeup regarding TI's MSP430
series, <a href="http://www.forembed.com/msp430-writing-assembly-for-use-in-c.html">head over …</a></p><h1>Intro</h1>
<p>So I have been playing with writing assembly again within the context of
C and I have run into the usual issues. I have decided to create a
quick little 'getting started' tutorial.</p>
<p>If you are interested in the same type of writeup regarding TI's MSP430
series, <a href="http://www.forembed.com/msp430-writing-assembly-for-use-in-c.html">head over to the
tutorial</a>!</p>
<p>For those who just want to see the results, skip on over to the <a href="https://github.com/slightlynybbled/xc16_mixed">github
repository</a>, download the
MPLAB X project and source files and get started. For those who would
like a gentler introduction, read on!</p>
<p>This tutorial assumes that you are already pretty familiar with MPLAB X
and writing programs in C or assembly and are just looking for the next
steps. I am using version 3.20 of the MPLAB X IDE, and version 1.25 of
XC16, but my experience is that the tips here will port across a wide
range of versions.</p>
<h1>The How</h1>
<h2>Create your Project</h2>
<p>Create your MPLAB X project (File -> New Project...). You will be
presented with a series of choices which I can't really guide you
through. You should have an idea of the processor that you are using
and the location that you want to store your project, so just follow the
steps with what you know. If you are just exploring, then use the
PIC24FJ32GA102. It is a good basic processor that doesn't have all of
the bells and whistles, but shares a core with all PIC24 and dsPIC33
series.</p>
<h2>Create main.c</h2>
<p>Create your main.c file where your application code will reside. For
you guys already creating C projects, this is no different from any
other C project.</p>
<h2>Create addition C files (optional)</h2>
<p>Your implementation may have one or morel C files that are part of the
same library. Create these functions in the usual way.</p>
<h2>Create Header</h2>
<p>Create a header file in which the assembly file and any associated C
functions are declared. In the example project, we simply named this
'mylib.h'. Note that this declaration is like any other function
declaration - no underscores, return type, parameter, etc.</p>
<h2>Create assembly file</h2>
<p>This is the part that will trick most of us up. The basics are in the
compiler documentation, but there is some real information missing. You
must create the file with a '.s' extension. Additionally, DO NOT name
the file the same as your C file. For instance, if you are writing a
library called 'mylib.c', then do not name your assembly file 'mylib.s'.
Both of these create a 'mylib.o' object file, which will confuse the
linker. Stick with 'mylib_xc16.s', or something like that. Besides,
if you port to the another processor achitecture, you can always write
that assembly code into 'mylib_msp430.s' or similar.</p>
<p>In your header, your function might be called 'add'. When this is
compiled, it gets an underscore added to it. As a result, when you are
writing your assembly function, you need to append an underscore at the
beginning of the name. For instance '_add'.</p>
<p>When the compiler looks at your code, it is only looking for 'global'
symbols to link with your C code. As a result, you need to add an
additional declaration to the top of your file: '.global _add'. This
must be exactly the same as the function is named within your
header file, but with an underscore.</p>
<h2>Write assembly</h2>
<p>Write the assembly code into your function. Feel free to name sections
of your code anything you like. As long as they are not declared
'.global', then the compiler won't see them as C functions.</p>
<p>In this architecture, parameter passing is accomplished using the
working registers w0-w7. Remember that these registers are 16-bit
registers. If a function takes two 16-bit parameters, they will be
located in w0 and w1. If a function takes a single 32-bit parameter,
that parameter is located in [w1:w0] with w0 being the low word. There
are lots of details to this, so it would really help to read the docs!</p>
<p>You can add other functions into this file as well, just be sure to
perform the same steps so that the compiler knows the declared global
symbols.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35</pre></div></td><td class="code"><div class="highlight"><pre><span></span> <span class="nf">.include</span> <span class="s">"xc.inc"</span>
<span class="nf">.text</span>
<span class="c1">; variables and functions that are to be used in the C</span>
<span class="c1">; domain must be declared 'global'. Additionally, an</span>
<span class="c1">; underscore must be placed in front of it since the </span>
<span class="c1">; compiler places an underscore in front of code to be</span>
<span class="c1">; assembled.</span>
<span class="nf">.global</span> <span class="nv">_add</span>
<span class="nl">_add:</span>
<span class="nf">add</span> <span class="nv">w0</span><span class="p">,</span> <span class="nv">w1</span><span class="p">,</span> <span class="nv">w2</span>
<span class="nf">btsc</span> <span class="nv">SR</span><span class="p">,</span> <span class="err">#</span><span class="mi">2</span> <span class="c1">; check the overflow bit</span>
<span class="nf">goto</span> <span class="nv">_add_sat</span>
<span class="nf">mov</span> <span class="nv">w2</span><span class="p">,</span> <span class="nv">w0</span>
<span class="nf">return</span>
<span class="c1">; if the code gets here, then an overflow occured, need to saturate</span>
<span class="c1">; mask = 0x8000</span>
<span class="nl">_add_sat:</span>
<span class="c1">; if w0 is negative, then saturate negative, otherwise positive</span>
<span class="nf">btsc</span> <span class="nv">w0</span><span class="p">,</span> <span class="err">#</span><span class="mi">15</span>
<span class="nf">goto</span> <span class="nv">_add_neg_sat</span>
<span class="nl">_add_pos_sat:</span>
<span class="nf">mov</span> <span class="err">#</span><span class="mi">32767</span><span class="p">,</span> <span class="nv">w0</span>
<span class="nf">return</span>
<span class="nl">_add_neg_sat:</span>
<span class="nf">mov</span> <span class="err">#</span><span class="mi">32768</span><span class="p">,</span> <span class="nv">w0</span>
<span class="nf">return</span>
<span class="nf">.end</span>
</pre></div>
</td></tr></table>
<h2>Prepare to Compile</h2>
<p>If all of these files are within the project.X directory, then you
should be able to add the *.c and *.s files to the 'source' directory
and the *.h files to the 'header' directory.</p>
<p>If the files are somewhere else in your directory tree, you may have to
add the include directories to your gcc options.</p>
<p>As mentioned previously, there is a <a href="https://github.com/slightlynybbled/xc16_mixed">starter project in
github</a> to help you out.
Just download and execute!</p>Fixed Point Library Creation2016-01-31T21:11:00-05:002016-01-31T21:11:00-05:00Jason Jonestag:www.forembed.com,2016-01-31:/fixed-point-libraries.html<p>So I have been doing some reading and looking around recently regarding
Q1.15 math and embedded C and I am surprised at the lack of libraries
out there. I remember implementing some fixed-point math in my motor
control days, but I would have thought that there would be an …</p><p>So I have been doing some reading and looking around recently regarding
Q1.15 math and embedded C and I am surprised at the lack of libraries
out there. I remember implementing some fixed-point math in my motor
control days, but I would have thought that there would be an
easy-to-install and easy-to-use library out there.</p>
<p>There has definitely been some work done in the Q16.16 space (see
<a href="https://github.com/PetteriAimonen/libfixmath">libfixmath</a>), and the gcc
toolchain has begun to support the _Fract type - with all of its
subtypes - but it doesn't seem quite uniform as yet, particularly across
manufacturers.</p>
<p>As a result, I decided to create a github repository with <a href="https://github.com/slightlynybbled/libmathq15">my small
contributions</a> to Q1.15
math. It isn't mature, by any means, but the end goal would be to have
a library that works well with a range of 8-bit and 16-bit processors.</p>
<p>I have implemented - and tested! - 90% of the functions that I find most
useful in embedded applications. I have also added assembly
optimizations for the Microchip 16-bit line, but I could use some help
porting to other processors. Any help would be appreciated!</p>
<p>Want to learn <a href="http://www.forembed.com/how-fixed-point-math-works.html">how fixed-point math
works</a>?</p>Launch!2016-01-31T20:51:00-05:002016-01-31T20:51:00-05:00Jason Jonestag:www.forembed.com,2016-01-31:/launch.html<p>Greetings!</p>
<p>After a number of years of working in the embedded world, I realized
that I really enjoy sharing cool things with cool people. This is my
attempt to further that goal!</p>
<p>j</p><p>Greetings!</p>
<p>After a number of years of working in the embedded world, I realized
that I really enjoy sharing cool things with cool people. This is my
attempt to further that goal!</p>
<p>j</p>