Bad things happen when programmer finds a soldering iron

Overthinking the LED Blinking

The very first thing I add to every piece of hardware I make is an LED. It not only signals that the device is on, but you can also use one to show processing or even do some simple debugging. What I often go for is a simple resistor+LED pair. And here is where the first decision comes - do I go for high-side or low-side drive?

Illustration

High side drive is when your output pin goes into the anode of the LED. The exact resistor location doesn’t really matter, and either before or after the LED results in the same behavior. When your pin goes high, the LED lights up. When the pin goes low, the LED turns off.

Illustration

Low side drive is pretty much the same idea but with logic reversed. When the pin goes low, the LED turns on, while the pin going high turns it off.

With both being essentially the same, why would you select one over the other? Honestly, there is no difference worth mentioning. It all goes to personal preference and/or what is easier to route on the PCB.

Illustration

For open-drain LED control we make use of the tri-state output available on most microcontrollers (high-Z state simulated using a switch). In addition to the standard low and high setup, we have an additional one sometimes called high-Z (or high impedance). In Microchip PIC, this is done by manipulating the TRIS pin. If the output TRIS is 1 (high-Z), the LED will be off. If TRIS is 0 and the output is low (the only valid open-drain state), the LED has a path to the ground and lights up.

This is quite similar to the previously mentioned low-side drive and has pretty much the same effect but with a slightly different way of controlling the pin. I would argue that both are essentially the same if your circuit uses just a single voltage. Where things change is when you have different voltages around as open-drain will work for driving LED on any rail (ok, some limits apply) while low side drive will not turn off fully if, e.g., you are controlling 12V LED with a 5V microcontroller.

But this open-drain setup got me thinking whether I can have use something similar to have LED be on by default? Why, you wonder? Well, my first troubleshooting step is to see if input voltage is present. My second troubleshooting step is to see if microcontroller is booting. Quite often, those tasks fall onto two different LEDs. If I could have LED on by default, that would tell me if there’s any voltage to start with and then blinking boot sequence would tell me microcontroller is alive and programmed.

Illustration

Well, there is one way to do it, and I call it default on open-drain. Unprogrammed Microchip PIC has pins in tri-state (high-Z) configuration. Thus, current will flow through LED as soon as voltage is applied. However, if pin goes low, current will flow to the pin and thus LED will go off. Exactly what we wanted.

Albeit, nothing comes without the cost and here “cost” is twofold. The first issue is current consumption. While our “on” state is comparable to what we had with other setups, our “off” state uses even more current. For blinking it might not matter but it should be accounted for if you have it off for longer duration.

Second potential issue comes if our ping ever goes high since that will have LED pull as much current as pin can provide. While Microchip’s current limiting is usually quite capable, you are exceeding current limit and that’s never good for a long-term PIC health. And chips that have no current limiting are going to fry outright. It is critical for functioning of this LED setup that the only two states are ever present: either low or high-Z.

Illustration

To avoid this danger, we can do the same setup using two resistor split. Assuming both resistors together are about the same overall value, in “on” state this circuit matches what we had before. In “off” state, we unfortunately increased current even further but, if we ever switch pin to high, we are fully protected. Even better, this setup allows for three LED states: “on”, “off”, and “bright”. This feature alone might be worth increased current consumption.

In order to deal with higher current consumption, we could reorganize resistors a bit. This circuit behaves the same as the previous one if microcontroller’s pin is high-Z (or not present). If microcontroller pin goes high, “bright” state gets even brighter as you’re essentially putting two resistors in parallel.

Illustration

When pin goes low, we have those resistors in series and thus current consumption, while still present, is not exceedingly higher than what “on” state requires. And yes, having made what’s essentially a voltage divider does mean that LED is not really fully off. However, it goes dark enough that nobody will be able to tell the difference when blinking starts.

Which one is best?

Well, I still use a bog-standard resistor-LED drive the most (either low, high, or open-drain). It’s simple, it’s fool-proof and definitely has the lowest current consumption among examples given. However, for boards where remote debugging is a premium but space doesn’t allow for bigger display, I find that having three brightness settings at a cost of a single resistor is quite a useful bargain.

Type-C Power Delivery as Passive PoE Source - Firmware

This is part 3 out of 3 in the series. PCBs were sponsored by PCBWay.


The firmware for the old ResetBox had a simple setup. You press the button shortly, it gets ignored; you press it for a second or two, and you reboot the devices. However, the introduction of the PD negotiation required a way to configure the voltage.

The easy way would be simply to use the existing external button for configuration. And that would work just fine. However, this would also mean that one could accidentally (or more likely by messing with the front panel) change the voltage. I didn’t want that. Thus the second, internal, button was born.

With it, the second question popped - why not use the internal button to set the voltage and the external button to change the setting. Since two buttons are there, both can be used. Except there aren’t two buttons available at all times. If we go with my rack-mounting case, the button is mounted in the rack using a rather short cable. If we get the device on the table, we don’t want to unmount the button. So, it has to be a single button setup.

Thus, the first thing that firmware checks during boot time is the state of the button. If the button is pressed, the boot process waits for either 3 seconds to elapse or for the button to be released. If the button is released within that time, just proceed with the normal boot. If the button is still pressed, go into configuration.

While I use TIMER0 for time management, I avoid using interrupts for just measuring the duration of time presses. The reason behind this is that I like to have interrupts available for the stuff that matters (e.g., change on the external pin). It doesn’t matter if the press is 3 or 3.01 seconds long, and thus, just checking the timer state is good enough.

Essentially, all time loops are as follows:

while {
    if (timer0_wasTriggered()) {
        counter++;
        if (counter == COUNTER_MAX) {  // held it long enough
            // do something for holding
        }
    }
}

As I set up timer pre/post-scalers in such a manner that I have exactly 24 “triggers” per second, an 8-bit counter variable allows for 10 seconds to pass. If a longer duration is needed, you can use 16-bit integers, but I usually find 10 seconds to be plenty.

Another change I made to the firmware was the ability to cancel the reset. In the old version, once you press the button for 3 seconds, it will reset the device when you release the button. Well, it happened at least once that I pressed the button for 3 seconds and then discovered that I pressed the wrong one. However, there was no way to go back.

In the new firmware, I lowered the duration needed to enter into the reboot mode to 2 seconds, but I also allowed for cancel if one holds it for an additional 10 seconds. As before, one can know if the release will reset the device by the LED brightness. If the LED gets dim, the reset is imminent.

For a simple device like this one, this is more than enough flexibility.

Source code is available on GitHub. And you can also check previous posts about design and its fixes.

Type-C Power Delivery as Passive PoE Source - Fixes

This is part 2 out of 3 in the series. PCBs were sponsored by PCBWay.


The first version of any project is rarely without faults. And this one is not an exception.

Illustration

The simplest issue has been me simply forgetting to wire up enable lines on 74HC238D decoder. I assumed, for no good reason, that lines were enabled by default. Thus, no matter what my code did, LEDs stayed dark. After a bit of probing and seeing correct voltage on input, I decided to check the datasheet again and saw the issue almost immediately. A fix required two wires though, as G2A and G2B lines required pull-down while pull-up was required for G1.

The next issue I noticed was that my internal button wouldn’t work. I knew of this risk since, due to pin shortage on PIC12F1501, I ended up using this pin as both input and output. As the microcontroller was booting, I wanted to detect button presses so I can put it into configuration mode. If the button wasn’t pressed during configuration, I would simply use the same pin as power-on signal input towards the transistor. Well, in theory, that should have worked. In practice, I had it pull to ground via an external base resistor. In order to keep the transistor at a known level when the line is input, I used my bodge powers to make the button pull the line high when pressed. Here is where my habit of connecting only two legs of the four-legged button actually helped as I was able to simply snip off one leg and solder 3V3 to another.

With the configuration mode sorted out, I started switching voltages only to find out that I am maxing at 9 V. I could select 5 V just fine but no matter what higher voltage I selected, CH224K would give me 9 V. It took me a while and a lot of datasheet deciphering (Chinese-only datasheet didn’t help) to understand what was happening.

You see, there are two ways you can configure CH224K. You can either use 3 CFG lines to set the desired input state in binary or you can use a resistor on CFG1 line alone. My 10K series resistor made the chip think I was using a single-resistor setting. 10K was close enough to 6.8K so that’s where 9 V came from. In 5 V mode, I had that pin pulled high, and that was enough to stop the single-resistor setup. The solution was to remove series resistors altogether. Ironically, I didn’t actually need them but decided to place them anyhow to help with troubleshooting.

One thing that definitely did help with this troubleshooting was the quality of the PCB. For example, while trying to troubleshoot my voltage settings, I soldered and resoldered CH221K multiple times. Since ground is on the pad that is inaccessible to a soldering iron, I had to reheat the board multiple times. I honestly expected to damage the solder mask after the second time getting it to 300 °C. And while the smell wasn’t the best, the PCB handled it without problems. Yes, you can damage the solder mask if you really want to but it will take a lot more than heavy-handed rework to do so.

With bodges sorted out, the next step will be to update the firmware.

Gone Cold

On paper, there’s little not to love about Pinecil v2. It’s a nice portable soldering iron, it uses low-ohm tips allowing for quick heating, uses the sweet type-C power, and at $25 it’s quite a deal. Do I need another soldering iron? Not really. But such deal was simply too tempting to pass.

As it often happens, my enthusiasm led me to buy two irons, some extra tips, nice silicone type-C cables, and even the darn replacement tip contacts. And no, I didn’t buy both for myself - one I figured was a perfect present for a friend.

The shipment arrived reasonably fast and I immediately tested it. I still preferred my KSGER but the Pinecil seemed quite usable when I needed to solder something quickly without getting the big guns out. I definitely believe there is a place in my arsenal for something like this. However, in a practical sense, it would always be my second choice. Thus, over a 3-month period, I found myself using the Pinecil only a few times.

And then it came time for some microsoldering on Framework HDMI Expansion Card. It was a single-wire mod but QFN package made it ideal work for the Pinecil. So, for maybe the 10th time in 3 months, I inserted its soldering tip, plugged it into a type-C charger, and… nothing. It was bereft of life.

After trying a fAfter attempting several solutions suggested online, I finnally contacted Pine64 support. They responded almost immediately, asked me for order number and requested pictures of my power supply, only to follow up with an extended silence. After a week or so, I got bored and pinged them to see what the status is, only to be informed that the warranty for the Pinecil was merely one month and there was nothing more they could do.

I don’t care how cheap the product is but 1 month warranty is not acceptable. Either you stand behind your product or you don’t. By having warranty match no-questions-asked-return period offered by most retailers, it’s clear that they’re doing the bare minimum. I would argue that a one-month warranty equates to virtually no warranty at all.

Should have I known that before I made the purchase? Maybe. Maybe not. But as I am in the market for a new portable soldering iron, one thing is certain: my new portable iron will not be a Pinecil. Not because it died - that happens. It’s purely because I know I cannot count on company for any support. And if they’re not willing to support their customers, it’s reasonable to assume they’re doing the bare minimum with their manufacturing process as well.

Are other portable soldering iron companies the same? Possibly, but that remains to be seen. However, with the Pinecil, the adage “buy cheap, buy twice” certainly proved true. And at $50, it’s not exactly a budget device anymore.


PS: My friend’s Pinecil still works. Alternatively, maybe it died but he’s too thoughtful to tell me. :)

Type-C Power Delivery as Passive PoE Source

This is a part 1 out of 3. PCBs were sponsored by PCBWay.


A long time ago I decided upon passive 48 V PoE for my network. Choice was made due to both hAP ac and Audience access points supporting up to 57 V. Thus 48 V was closest lower standard value with a reasonably priced power supplies. However, this decision came to bite me in the ass with my new hAP ax³ supports going only up to 28 V. Placing it into my network would lead to a lot of sparking fun. What I needed was a lower voltage.

Well, the next logical step was switching to 24 V. However, buying two 24 V power supplies (one to use, and one for backup) allowing for 60 W was actually not that cheap. Since I had bunch of leftover power supplies, I started to wonder if I could use something I already have.

Yes, I could have used one of vendor-specific laptop power supplies but that would involve cutting cables as their connectors were anything but standard. The only standard connector I had was type-C. And then it hit me. Why not simply use power delivery to get 20 V out of it?

Realistically, probably the easiest in-place replacement was one of many prebuilt cables out there. And I think this is the best way to go if you just need power. But, for my home setup I connected PoE over ResetBox device which allows for an easy power reset. Wouldn’t it be really nice if I updated ResetBox to directly use type-C?

And no, ResetBox is not just a simple switch albeit it offers essential functionality of one. Its a bit smarter than that as it contains microcontroller that gets to control a relay. For example, it will ignore short, accidental, presses and allow reset only when held for 3 seconds. Ideal for controlling things you don’t want to reset by accident - for example your home wireless network.

For this update it would be ideal to switch input to type-C, allow for on-board voltage selection (5 V, 9 V, 12 V, 15 V, and 20 V), and lastly have a nice way to indicate the selected voltage.

With that in mind, the first task became selection of PD chip. It is possible to control PD on your own but I quickly decided against it as setting it to function right with various PD and non-PD type-C devices would take ages. No, I wanted something standard.

Quick search quickly pointed toward IP2721 as a reasonably easy to (hand)solder device used in many existing triggers. However, its package was a bit on a large side and voltage output was quite limited since it only supported 5 V, 15 V, and 20 V operation. Definitely not a full set of voltages I wanted. And yes, you could get lower voltages by using IP2721_MAX12 variant but then you lose the higher voltage options.

Second device I found was HUSB238 and I immediately liked its SOT33-6L variant. Unfortunately, this easy-to-solder variant was nowhere to be found for purchase. Even worse, the chip didn’t support the full PD 3.0 5A operation. Yes, probably not a deal-breaker for this particular scenario as 60 W was plenty but still no ideal for a new design.

After quite a lot of additional search I stumbled across CH224K. Not only it came in (seemingly) easy to solder ESSOP-10 package but it also supported easy control. Based on a few examples I found it seemed possible to tease the full 100 W with it. The datasheet also mentions an even more appealing CH221K in the SOT23-6L package, but I had difficulty finding it in the market. On the other hand, the ESSOP-10 variant was readily available all over AliExpress.

With chip selected, the second order of business became figuring out how to connect all this. With only 6 I/O pins available, the current PIC12F1501 was a bit crowded. In addition to 1 button input, 1 LED output, and a 1 relay output it already handled, I would add 3 outputs for voltage control (also used to show LEDs status). Total of 1 input and 5 outputs. Not comfortable but just enough.

Originally, ResetBox had the option to handle AC input. However, with type-C, this is no longer necessary, so we can replace the relay with a small P-MOSFET and completely bypass the diode bridge. We don’t control this MOSFET directly, but rather via a small NPN transistor. This approach serves two purposes: first, it prevents our PIC from seeing 20 V at any given time, and second, it allows the PG signal to override the output (i.e., no PG, no voltage).

While originally ResetBox had option to hadnle AC input, with type-C we don’t need this anymore and thus we can replace relay with a small P-MOSFET and omit diode bridge altogether. We don’t control this MOSFET directly but over a small NPN transistor and that’s for two reasons. First one is to avoid our PIC seeing 20V at any point in time and the second reason is allowing PG signal to override output (i.e., no PG, no voltage).

The power supply for the main PIC is managed by an LDO. While this approach does waste some energy, it’s not too significant as the PIC won’t need more than roughly 20 mA. For such a small current, it simply wasn’t worth opting for a switched-mode regulation. I considered using a 5 V LDO despite the input being at 5 V, but chose to make the smarter decision and go with 3.3 V instead. And yes, I did test the 5 V LDO in the same circuit and it worked, albeit with a slight voltage drop.

Illustration

For PCB creation I chose PCBWay and they generously provided me with free PCBs. As it’s common with my projects, I don’t really push the boundary of what’s possible but there were a few non-standard things with this PCB. The first one was its thickness as I needed 0.8 mm due to type-C connector. The second one were really small holes for the same. And yes, this was quite well within the specification but I actually had issues with this when it came to other PCB manufacturers.

But more about that the next time when I go over all the things I’ve botched during the PCB design.


Latest design is available on GitHub

RS-485 Framework Expansion Card (FTDI Edition)

Boards for this project were sponsored by PCBWay (affiliate link).


Illustration

Playing with electronics means dealing with a lot of protocols. Be it UART, RS-232, I2C, or CAN-bus, I have made a version to fit my Framework laptop. However, one popular protocol was strangely absent - RS-485. Well, it’s time to correct that mistake.

Since I successfully used JST XH with many Framework expansion cards before, connector decision was easy. However, deciding which pinout to use was a bit more troublesome as I needed 3 pins and common wisdom was that order is GND-A-B. However, my UART cards were using 3-pin setup with GND in middle. It would be too easy to forget which card was plugged in and get the most crucial pin wrong.

So, the only remaining choice was to have A-GND-B order but I really didn’t like it and honestly I am not sure why. It’s a good pinout allowing for excellent routability (since protection diodes have this pinout already). But no other RS-485 device has it. While moving GND pin to center would alleviate error rate among my framework expansion cards, I could see even myself accidentally connecting ground to the first pin.

At the end I decided to use 4-pin socket with the last pin pulled out. This would allow for standard GND-A-B setup while making connector distinctively different from my cards that use center pin for GND. Even better, GND signal here actually matches my CAN-bus card where that last pin is populated (albeit not used).

Since I still had bunch of SP485EN chips I used for another project, it was easy to select RS-485 transceiver. And honestly, while aged, it’s not a bad chip for the purpose. It’s available from multiple sources, it has low pin count in easily hand-solderable package, and at 5 Mbps it’s way faster than UART I was planning to pair it with.

Speaking of UARTs, there is one that you will find mentioned time and time again when it comes to RS-485 - FTDI FT232RL. And that’s for a good reason. It’s a single chip solution without the need for crystal, allows for reasonably high baud rates, and it has specialized TXDEN pin allowing for automatic flow control. While its pin count might be on a high side and comes with a bit of baggage, it really fits nicely for the purpose.

While FT232 does offer reasonably full RS-485 schematics, I added a few things to spice it all up. First of all, I added a MOSFET to keep RS-485 transceiver fully off when in USB suspend. I later did find this was not really necessary as SP485EN has a suspend mode that FT232 can nicely trigger, it does offer me a bit of flexibility for further testing.

Another thing I added were biasing resistors. In my own setups I found that this increases bus resiliency a lot and it only costs you 3 resistors. What value resistors? Well, that’s where it gets tricky. There are so many ways to calculate these resistor values and you need to know a lot about the exact setup in order to make it perfect. Since I wanted to use this device in many different networks, the best I could hope for is to get close enough. In this case this meant 130/750Ω combination. When combined with 120Ω termination on the other side of the bus, it should handle many scenarios.

For protection, I decided to use good old SM712 TVS diodes to keep SP485EN safe and a few fast fuses on both data lines and on the ground for catastrophe prevention. Ideally you might want to have an isolated interface between your laptop and any bus. However, I simply didn’t have enough space on board for discrete solution and solutions with integrated DC-DC converter were either way to expensive or unobtainable.

While fuses are not ideal replacement for isolation since bad things can still happen if you deal with wildly different potentials, things are not as bleak as it’s easy to insulate your laptop. Simply unplugging it from its power supply and running it of battery will do the trick and allow you safe interfacing.

With the overall design done, I turned to PCB fabrication and PCBWay. I decided to go with them due to previous good experiences when it came to Framework expansion boards. While they are simple enough, routing them properly is a bit tricky. Only once I already made an order did PCBWay come back and proposed a sponsorship in the form of free PCBs. And that jinxed it, I guess, since for the first time routing was not ok.

Illustration

Per my understanding, most commonly fabs do board edge routing with a 1.6 mm or larger mill end. This is done for both speed and durability as smaller bits do tend to break more. Routing boards intended for type-C connector used here requires 1.0 mm or smaller bit in order to properly do the notches. If you check the bad board (green) and compare it to the good board (red), you’ll see how little difference there is.

In any case, that gave me an opportunity to deal with their helpdesk for the first time. After writing a ticket, I got a response in less than 24 hours despite them being in holiday time. After a short back-and-forth where I got to explain the exact problem, I had my updated order with extra notes attached. And yes, I selected a different color so I don’t mix them up.

Illustration

When it comes to the PCB quality, there isn’t much I can say I haven’t said before. A default HASL finish is good enough and there were no leveling issues. If you want ENIG, that is an option albeit quoted price skyrockets when selected. Soldermask is decent and can handle lead-free soldering temperatures without issues. I usually go fora green color as it has the shortest lead time, but you can select other 5 colors at no extra cost (purple and matte colors are extra).

Silk screen is of decent quality and resolution even for small font sizes. And yes, PCBWay still adds their identification number on board by default with option to remove it requiring $1.50. For these boards I don’t really care since they won’t be visible to the end user but if you want to do user-facing panels, you might want to pay extra.

With all components soldered, I connected it to a Framework laptop and the experience was as good as you can get. One great advantage of FTDI chips is that they work flawlessly. I tested it on a few small RS-485 busses at varying speeds (up to 1 Mbaud) without any communication issues. Even under scope things were looking good.

But, is there a way to get rid of FTDI? Well, we can discuss that in the next instalment of the RS-485 Framework expansion card saga. :)


As always, you can find project on GitHub

Reading 74HC165 with Reduced Pin Count

For a project of mine, I had to read an 8-bit DIP switch. Easy solution was to use 74HC165 but there I had a problem. Fully interfacing with it requires 4 lines: Shift/Load, Clock, Clock Inhibit, and Q7. However, I only had 3 lines available.

Obvious one was getting rid of Clock Inhibit as that one can be tied to low permanently. This leaves only 3 lines but with a slightly unclear handling as official datasheet uses Clock Inhibit in all examples.

After a few tests with chips from different manufacturers, I found the following code works with all.

bitbang_cp_low();  __delay_us(1);  // bring clock down
bitbang_pl_low();  __delay_us(1);  // perform Load
bitbang_pl_high(); __delay_us(1);  // load done

uint8_t value = !bitbang_so_get(); // get the first bit as that one is already latched
for (uint8_t i = 0; i < 7; i++) {  // do the next 7 bits
    bitbang_cp_high(); __delay_us(1);  // clock goes high
    value <<= 1;                       // shift value 1 bit to have empty bit for the next load
    if (!bitbang_so_get()) { value |= 0x01; }  // read serial output
    bitbang_cp_low(); __delay_us(1);  // clock goes low for the next round
}

return value;

One can argue that 1µs delay is a bit too much but I actually found a few chips obtained from eBay that positively needed it to function properly. One could argue that those chips should be excluded as all reliable manufacturers specify it at 250ns or less. However, quadrupling that value in my use case (reading DIP switch on startup) wasn’t that important.

Regardless, adjust overall code as needed.

CRC-16 Nibble Lookup Table

I already wrote about using smaller-than-256-byte lookup table. What we ended up with was a smaller 32 byte lookup setup for CRC-8. However, if you’re sending a lot of data, CRC-8 won’t do. Can one make a nibble lookup table for CRC-16 too?

Of course one can. It’s actually exactly the same - just expand all variables to 16 bytes. In order to calculate lookup table, one can just use this code:

for (uint8_t i = 0; i < 16; i++) {
    uint16_t crc = (uint16_t)i;
    for (uint8_t j = 1; j <= 16; j++) {
        if (crc & 0x8000) {
            crc = (uint16_t)((crc << 1) ^ poly);
        } else {
            crc <<= 1;
        }
    }
    Crc16LutL[i] = crc;
}
for (uint8_t i = 0; i < 16; i++) {
    uint16_t crc = (uint16_t)i << 4;
    for (uint8_t j = 1; j <= 16; j++) {
        if (crc & 0x8000) {
            crc = (uint16_t)((crc << 1) ^ poly);
        } else {
            crc <<= 1;
        }
    }
    Crc16LutH[i] = crc;
}

I will be using polynomial 0xA2EB (0xD175 in Koopman’s notation) since that one seems to be the best at this time. Mind you, in year from now, one might find another polynomial with better characteristics yet.

// Polynomial 0xA2EB
const uint16_t Crc16LutL[] = { 0x0000, 0xA2EB, 0xE73D, 0x45D6, 0x6C91, 0xCE7A, 0x8BAC, 0x2947, 0xD922, 0x7BC9, 0x3E1F, 0x9CF4, 0xB5B3, 0x1758, 0x528E, 0xF065, };
const uint16_t Crc16LutH[] = { 0x0000, 0x10AF, 0x215E, 0x31F1, 0x42BC, 0x5213, 0x63E2, 0x734D, 0x8578, 0x95D7, 0xA426, 0xB489, 0xC7C4, 0xD76B, 0xE69A, 0xF635, };

uint16_t crc16(uint8_t* data, uint8_t length) {
    uint16_t crc = 0;
    while (length--) {
        uint8_t combo = (crc >> 8) ^ *data++;
        crc = (crc << 8) ^ Crc16LutL[combo & 0x0F] ^ Crc16LutH[combo >> 4];
    }
    return crc;
}

These extra 64 bytes (two lookup tables, 32 bytes each) allows us to use shift-less CRC code. And yes, there will one shift operation in there but XC8 compiler should actually optimize that one away in most cases.

Since we do have a bit more complicated data handling, implemented on a microcontroller using XC8 compiler, this code needs 141 words of program memory (was 80 for CRC-8) and uses 16 bytes of RAM (was 4 for CRC-8). All in all, pretty handleable by almost any PIC microcontroller.

Interestingly, here you cannot use trick of removing const in order to move usage more toward memory. While RAM usage for such case indeed increases to 76 bytes, program memory usage doesn’t go down at all.

As always, the example code is available for download.

Mystery of a High ADC Reading

Microchip makes reading ADC easy enough. Select a channel, start measurement, read a registed - doesn’t get much easier than that. However, in one project of mine I got a bit stumped. While most of my readings were spot on, one was stubornly too high.

As I went over PIC16F15386 documentation a bit I saw a following note: “It is recommended that … the user selects Vss channel before connecting to the channel with the lower voltage.” Whether due to a high channel count of some other pecularity of this PIC, capacitance was strong with this one. One of the rare times when reading instructions actually solved an issue.

Well, solved might have been too optimistic of a statement. While my low voltage ADC channel now read a correct value, my higher voltage inputs read slightly too low. Yes, I am aware I sound like a daddy bear looking at his bed but I too wanted my readings to be just right and for the darn Goldilocks to leave my home.

What I found working for me is doing the following: switch channel to GND, (dummy) read ADC, switch to the correct channel, do the first (proper) reading, do the second reading, average the two readings. In code it would be something like this:

ADCON0bits.CHS = 0b011011;    // select Vss channel
ADCON0bits.GO_nDONE = 1;      // start an A/D conversion cycle
while (ADCON0bits.GO_nDONE);  // wait for conversion to complete

ADCON0bits.CHS = channel;     // select channel

ADCON0bits.GO_nDONE = 1;      // start an A/D conversion cycle
while (ADCON0bits.GO_nDONE);  // wait for conversion to complete
uint16_t value1 = ADRES;      // read value

ADCON0bits.GO_nDONE = 1;      // start an A/D conversion cycle
while (ADCON0bits.GO_nDONE);  // wait for conversion to complete
uint16_t value2 = ADRES;      // read value

ADCON0bits.CHS = 0b011011;    // select Vss channel

return (value1 + value2 + 1) / 2;

Now, the obvious issue here is that three readings are done when only one is needed. Since we want to do averaging, there is nothing that can be done about reading it twice. However, if you are not pooling it all the time, you can pretty much skip the first (dummy) reading as switching to Vss channel at the end of routine does the trick.

While 16-bit operations are not the most pleasant thing 8-bit PIC can do, it’s actually not too bad as we’re talking about only 3 additions and one right shift (division by 2 gets optimized). Not good, but not terrible.

Even better, this works flawlessly with ADCs that have no issues with reading being too high or too low. This means I don’t need to worry if I use routine with a different PIC microcontroller.

All in all, it’s a cheap way to get a correct reading.

I2C Framework Expansion Card

Boards for this project were sponsored by PCBWay (affiliate link).


Illustration

Some time ago I created an UART expansion card for Framework laptop. While card served me well, but MCP2221 around which the whole circuit was created has more to give - I2C. But for that, a bit of rethinking was needed.

The first order of business was a connector. UART realistically requires only 3 wires and thus connector was reasonably small. But, if one wants to express both UART and I2C, at least 5 wires are needed. Still manaegable but not sufficient for what I had in mind.

As I wanted this card to help me test and troubleshoot standalone I2C devices, I also needed to source power. Well, easy enough to expose my internal regulator output on another pin. And that gives us total of 6 pins.

However, if you count pins on my finialized device you will see a 7-pin connector. In order to minimize risk and keep UART connections straight in the middle I decided to add an empty pin with a nice side effect of isolating power pin from other thus making accidental connection less likely.

Illustration

Since I already had UART PCB as a template, creating a new one was easy enough so in no time I uploaded it to PCBWay for manufacturing. 15 minutes later I got a message that something is wrong.

Failure was due to “non-plated slots size should be greater than 0.8mm” and it included a nice picture showing the issue. In a moment I figured an issue - my wide connector was using enough space to leave only slivers of PCB material remaining. Since I was always looking at it on the screen, I never got the feeling how thin PCB that was. However, my reviewer (Elena) did.

After looking into a few different solutions, I decided to maintain PCB’s shape and just cut the bottom off. If you look into PCB carefully, you will see just a sliver of the slot remaining. While it might look like an accident, it actually helps with positioning around the stub in case.

Illustration

Courtesy of fast shipping, PCBs were in my hand in less than two weeks. First thing I noticed was subpar silk screen printing. Yes, I pushed things a bit using such a small lettering but PCBWay usually does better job. It seems that truetype font I used is simply not compatible with their silkscreening process. I know for a fact that vector fonts normally used by PCB tools work flawlessly as I use PCBWay for a lot of non-sponsored content. But truetype font seems to be their cryptonite.

Boards themselves are what you would expect from HASL finish. As you can see on the picture, surface is not as leveled as you would get with ENIG but I found no issues positioning narrow-pitch type-C connector and soldering it in place.

While PCB outline is not really complicated, it’s not trivial either. I had other manufacturers mess board outline routing by using mill end that’s a bit too big. But PCBWay always routed it perfectly (and not just for sponsored boards). I pretty much consider them a first choice when it comes to framework expansion cards.

In any case, a short soldering session later and I had my device ready for testing. UART portion works as you would expect it. System sees it as a serial port and with correct baud rate world is your oyster.

I2C side requires download of a I2C/SMBus Terminal utility. Utility is easy enough that anyone familiar with I2C will quickly get the hang of it. Also you can look in other downloads if you want to create something custom.

Just keep in mind that I2C is not relly designed to be used via USB and inherent latency makes any high-speed communication impossible. Yes, your device might work at 400 kHz, but you still need to wait after every transfer for reply to come back. Inneficient at best.

Regardless, for testing, this is a decent addition for my Framework laptop toolbox.


PS: I got information on the silk screen from PCBWay and issue stems from how truetype font got rendered into gerber on my side. The end result is bunch of small lines that are way too short. In order to make each of those lines visible, they get expanded a bit and thus the result ends up looking too thick and blury. Now, some houses won’t have issue with this as they might drop the lines instead of widening them up but that will probably cause issue with some other truetype font rendering. There is big enough “impedance mismatch” between how fonts and how gerbers work that I would recommend staying clear away from them completely as result will differ both from font to font and fab to fab. Every PCB tool has a vector font that’s properly setup for usage in gerber and you should stick with that one. Lesson learned. :)

PPS: Source files are available on GitHub.