Bad things happen when programmer finds a soldering iron

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.

CRC-8 Nibble Lookup Table

A few weeks ago I left you with the following CRC-8 code for microcontrollers:

uint8_t crc8(uint8_t* data, uint8_t length) {
    uint8_t crc = 0;
    while (length--) {
        crc ^= *data++;
        for (uint8_t i = 0; i < 8; i++) {
            if (crc & 0x80) {
                crc = (uint8_t)(crc << 1) ^ 0x2F;
            } else {
                crc <<= 1;
            }
        }
    }
    return crc;
}

However, this code is a bit lacking when it comes to the performance. And yes, adding a lookup table would speed things up by a lot since it would allow use of precalculated values instead of shifting stuff around.

uint8_t crc8(uint8_t* data, uint8_t length) {
    uint8_t crc = 0;
    while (length--) {
        crc ^= *data++;
        crc = Crc8Lut[crc];
    }
    return crc;
}

However, 256 bytes such lookup table requires is too much when dealing with a microcontroller that often has only that amount available for all its functionality. Some, like my favorite small micro PIC12F1501) might have as little as 64 bytes of RAM.

What we need is a smaller lookup table.

Fortunately, this is actually quite possible if we calculate lookup table independently for the high and low nibble.

for (uint8_t i = 0; i < 16; i++) {
    uint8_t crc = (uint8_t)i;
    for (uint8_t j = 1; j <= 8; j++) {
        if (crc & 0x80) {
            crc = (uint8_t)((crc << 1) ^ poly);
        } else {
            crc <<= 1;
        }
    }
    Crc8LutL[i] = crc;
}
for (uint8_t i = 0; i < 16; i++) {
    uint8_t crc = (uint8_t)i << 4;
    for (uint8_t j = 1; j <= 8; j++) {
        if (crc & 0x80) {
            crc = (uint8_t)((crc << 1) ^ poly);
        } else {
            crc <<= 1;
        }
    }
    Crc8LutH[i] = crc;
}

This shrinks our lookup table to 32 bytes and allows us to use shift-less CRC code. And yes, there is one shift operation in there but XC8 compiler should actually optimize that one away in most cases.

// Polynomial 0x2F
const uint8_t Crc8LutL[] = { 0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD, 0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A, };
const uint8_t Crc8LutH[] = { 0x00, 0xAE, 0x73, 0xDD, 0xE6, 0x48, 0x95, 0x3B, 0xE3, 0x4D, 0x90, 0x3E, 0x05, 0xAB, 0x76, 0xD8, };

uint8_t crc8(uint8_t* data, uint8_t length) {
    uint8_t crc = 0;
    while (length--) {
        crc ^= *data++;
        crc = Crc8LutL[crc & 0x0F] ^ Crc8LutH[crc >> 4];
    }
    return crc;
}

Implemented on a microcontroller using XC8 compiler, this code fits in 80 words of program memory and uses just 4 extra bytes of RAM (const means your table goes to program memory).

You might opt to go without const and in that case code needs 72 words of program memory and whooping 35 bytes of RAM. As I’m usually running out of RAM before I run out of program memory, I don’t find that particular compromise worth it.

And again, one could squeeze a few more bytes by implementing this in assembly, but not too many.

As always, the example code is available for download.


PS: For curious ones, a full CRC-8 lookup table implementation uses whooping 287 words of program memory but only 2 bytes of RAM.

Selecting 8-bit CRC

Data errors are a reality when communicating between electronic devices. While one can use various hashing algorithms in order to verify data, this is not a luxury easily afforded when working with microcontrollers. For such cases, one often uses just a simple parity, sum, or ideally a CRC algorithm.

I won’t go much into a history and general use of these algorithm as Wikipedia covers it nicely. I will just present you with a problem I had. Which of many CRC algorithms do I actually use for my Microchip PIC microcontroller project?

The first part of rough selection was easy. Due to the limited nature of my PIC microcontroller, the only realistic options were CRCs up to 16 bits in length. After discounting all CRCs that ended on non-byte boundaries, that left me with only CRC-16 and CRC-8.

While CRC-16 is superior in all manners, it’s not only more computationally intensive but it also requires more memory to implement. Both of which are notoriously absent from microcontrollers. While both of those might be a worthy compromise if I had long messages that needed checksumming, my messages were couple of bytes in length. So, CRC-8 it is.

However, CRC-8 has quite a few variants out there. Heck, my own C# library supports over 20 of them. What I needed was a logical way to select the best one. And that came in the form of the excellent “Cyclic Redundancy Code (CRC) Polynomial Selection For Embedded Networks” article.

However, what this article has shown me was that I was wrong in thinking there is a single optimal solution. For example, from charts its easy to see that polynomial 0xA6 is vastly superior when it comes to long messages. It will detect two bit errors (yes, simplifying here a bit) no matter what the length. However, if we want CRC to detect four bit errors, polynomial 0x97 will actually do better - albeit only up to 120 bits (15 bytes). Whether one is better than the other, is now function of message length.

And that sealed it for me. Since my messages were to be short, polynomial 0x97 was actually the right choice for me. Since CRC is defined by more than its polynomial, I went to the repository of CRC knowledge to see if someone implemented the same. And I couldn’t find it. Heck, I couldn’t find any of the polynomials discussed.

It took me a hot minute to see that whitepaper and CRC RevEng page were actually describing the same polynomials in a different way. CRC RevEng used their normal form while whitepaper decided to call them out in reversed reciprocal. My 0x97 was actually 0x2F.

After understanding that, I actually found two existing CRC-8 algorithms using my chosen polynomial: AutoSar and OpenSAFETY. Since AuroSar had an extra step of XORing the output with 0xFF, I went with a slightly shorter OpenSAFETY CRC-8 variant. Even better, I had that algorithm already implemented in C#. Converting it to C for my microcontroller would be a breeze.

However, since my C# algorithm used lookup table to speed things up, that made it really unsuitable for microcontroller implementation. How could I give 256 bytes to the lookup table when I had only 1024 to begin with? No, for this I needed a variant that uses as little memory as possible.

After a while, this is what I ended with:

uint8_t crc8(uint8_t* data, uint8_t length) {
    uint8_t crc = 0;
    while (length--) {
        crc ^= *data++;
        for (uint8_t i = 0; i &lt; 8; i++) {
            if (crc & 0x80) {
                crc = (uint8_t)(crc &lt;&lt; 1) ^ 0x2F;
            } else {
                crc &lt;&lt;= 1;
            }
        }
    }
    return crc;
}

Without any optimizations, this code needs only 3 bytes in RAM and 46 words of program memory (37 words with optimizations). And yes, speed does suck a bit but that’s the tradeoff. If speed was of essence, one could always go with lookup tables, no matter the RAM cost.

In any case, my problem was solved and algorithm selected.


PS: Yes, code could be optimized further if we used overflow from STATUS register as that would allow us to have only one crc << 1 but I opted not to do that as to keep code easily transportable between platforms.

PPS: And yes, if one is willing to go into the assembly, you can optimize it even more.

Really Prolific?

Illustration

Well, it’s been a while since the FTDI fuckup so I guess it was a time for another IC supplier to go bonkers. Yes, it’s again time for a chip manufacturer to mess with your computer drivers.

Story starts with me searching for 5V USB cable with a 3.3V signal. After finding a suitable device, I did what was needed and forgot about it for a while. A few days ago I needed USB type-A serial device to do a quick loopback test and grabber the same, previously working, device. While the serial port did appear, I couldn’t open it or send any data.

A quick trip to Device Manager has shown a problem: “THIS IS NOT PROLIFIC PL2303. PLEASE CONTACT YOUR SUPPLIER.” Yes, it’s the exact nonsense that FTDI pulled years ago - using Microsoft Windows Update mechanism for their authenticity enforcement.

Illustration

Now, you might thing this is their right. And I can see how they might be annoyed with fake chips using their drivers. However, their beef should be with fake chip suppliers and not with the end customer. For me the concept of bricking device owned by an unsuspected user is a bridge too far.

My case is probably the standard one. I bought device without knowing it has a fake chip in it. I paid the seller, he paid his supplied, his supplied paid the manufacturer and so on. Now my device stopped working. Money is long gone and so is the supplier of the fake chips. I might have lost that money. If I can ask for refund, the seller might be out of money. Manufacturer might be out of money (especially if they didn’t know they’re dealing with fakes). The only person not out of money is probably the guy selling fakes in the first place.

While Prolific might look at me as a potential new customer since I am in the market for a new cable, I believe that’s the wrong assumption. I am never going to knowingly buy a Prolific device again. Why? Because there is no way that I, as a customer, can check if device is indeed original or not.

What I do know is that Prolific is ready to play shenanigans with Microsoft update and brick my devices down the road. Since I cannot verify their authenticity myself, buying any Prolific device is something that might bite me in the ass. Unless something changes, I won’t buy a single Prolific cable ever again. Their product is nothing special and there are many other manufacturers happy to take my money.

I hope that Microsoft will rollback driver since it’s their update that’s causing issues for the customer. I also hope that Prolific will see the error of their ways and stop bricking customer devices. I am hoping, but not holding my breath for either.


PS: And yes, FTDI did say they saw the error of their ways back in 2014. Only to pull the same shit again in 2016. They learned nothing. Chances are neither will Prolific.

PPS: In meantime, you can download the older driver (v3.8.39.0 worked for me) and use it instead.

RS-232 Framework Expansion Card

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


A while ago I created a CAN-bus expansion card for Framework laptop. However, I consider this an utter failure. While the card did work, the lack of CAN-bus capable parts meant you cannot actually create one yourself. At least until this IC shortage comes to an end. However, experience was fun enough that I decided to make something else I do need and that one could actually manufacture in today’s world - a RS-232 expansion card.

For those uninitiated, RS-232 is an overall standard that defined connection between different devices for a long time. As many standards go, RS-232 is quite an expansive beast with many different signals. However, over the time, it became really common to have it boiled down to a three-wire setup: RXD (receive), TXD (transmit), and GND (ground). While RS-232 did disappear from desktops and laptops alike, it’s far from dead. Be it industrial equipment or a network switch, RS-232 still has its place.

Illustration

Design was easy enough. To get from USB to TTL UART, I used MCP2221, but any similar chip will work equally well. Resulting TTL signal is then fed into MAX232 where charge pump brings it to RS-232 voltage levels, about ±8 V. This is quite sufficient to be fully RS-232 compliant. Even better, MAX232 will allow for receiving a full RS-232 ±15V signal with some margin. The end result should be that you can interconnect with pretty much any RS-232 compliant device out there.

Unlike for my CAN-bus expansion card, here I went for a slightly bigger connector of JST XH variety. While this one actually required a bit of modification to the expansion card casing, it’s actually much more convenient than its 2.0 mm brethren. First of all, spacing is 2.5 mm which is close enough to 0.1" spacing that you can easily use a standard jumper wires to connect. This means you don’t need any specialized tools or bother with creating a cable yourself. Further more, if you plan to have something more permanent connected and you want the polarity protection you get from JST XH, it’s trivial to find many cables with 3-pin JST XH already present.

Illustration

Pinout was another difficult choice and in the end I decided onto RXD GND TXD. Benefits of this approach are mostly in the area of safety. If GND wire was at pin 1, there is some place for doubt whether you start counting on left or right. With it being in the middle, that doubt is removed. Further more, to do the self-test, one just needs to connect two outer pins together. Again, it describes connection uniquely without even mentioning pin numbers. Yes, you need pin numbers for RXD and TXD regardless but swapping those two will never cause any damage so the guessing game is less dangerous.

Illustration

As noted above, the boards were manufactured by PCBWay and there were a few things to watch out for. Due to the connector, these boards had to be 0.8mm. PCBWay does support this thickness without any extra cost and it’s only a click away.

Another thing to watch for, was that this board really does require 6/6 manufacturing process. Interestingly, I accidentally had one via that was too close but my board passed all the checks PCBWay had. When I got it back, I saw that via’s ring was adjusted to be slightly thinner on the offending side so everything ended up working just fine. While I appreciate such help, I would actually prefer the check to fail so I can correct the boards myself instead of silently fixing the issue.

One important thing that had me worried was if small PCB features needed for USB type-C edge connector would be routed correctly. What I discovered by accident was that some board houses do their routing with a 0.068" mill end which is a bit too large for these cutouts. If that happens, you simply cannot fit this connector onto it. What you want is 0.04" mill end to correctly handle this board outline. Either someone was paying attention or PCBWay does this by default. Either way, I got my boards perfectly milled.

If you look at pictures carefully, you will also notice something is off with the silk screen and here I must take a full fault for this. I do love Bahnschrift font and how well it renders at small sizes so I decided to use it for silk-screen markings. But I got greedy and went with its condensed variant that, in retrospective, had no chance of being readable. This one was purely on me.

In any case, boards were in my hands within 10 days. This also meant they arrived faster than I was ready for them but I cannot really hold this against PCBWay. :)

Illustration

As far as case goes, I toyed with an idea to fit it into the existing USB A case. While this might have been just possible, it also meant for each device one would need to dismantle a perfectly good official expansion card. Thus, I went with 3D printed case with slight modifications. When combined with friction-fit top, card actually doesn’t look half bad.

With everything else out of the way, now comes the part where I tell you about potential issues. The major one is that this is non-insulated interface. RS-322 driver can easily handle ±30 V so this would not be an issue normally. However, for signals to be measured, one needs to have both communicating devices on the same ground potential. And therein lies the major trouble. If you accidentally connect something that is not ground into this, you will pass a significant current and maybe burn down the transceiver. In the worst case you could also damage the laptop as all grounds are connected together.

All that said, as long as you connect ground to ground you should be fine. If you are not sure about the ground potential on the other side, a neat trick is to simply disconnect your laptop from the AC adapter. If you run your laptop of the battery, its ground will “float” and thus you again can rely on ±30 V range of MAX232 chip. This simple trick will give you enough leeway to make many mistakes.

Lastly, there is a slightly unconventional fast (20 ms) fuse on the ground path that might save your butt if things go really bad. Notice that I said “might” and not “will” here. Fuses are great but even a fast fuse will take a decent amount of time to break the connection. Fortunately, type-C ports are quite rugged so odds are decent the motherboard won’t be damaged. But I wouldn’t bet on it.

Illustration

My personal approach when connecting anything to this expansion card would be to make sure I’m connecting ground on both sides and, as an extra precaution, to test connection with laptop disconnected for the mains. If communication works fine, you can connect laptop back to the mains.

Also note this is nothing specific to this adapter - all non-insulated USB adapters (and most of them are) suffer from the same potential problems. However, due to the size of this expansion card, it’s much easier to misconnect wires then when you’re connecting to the DB-9.

In any case, as always, the design is freely available. If you don’t want to bother soldering one yourself, they are also available for purchase (USA only, at this time).

CyberCard

Illustration

As I got another CyberPower UPS for my devices, I again decided to use a custom card to connect it to my computer. However, using my existing PCB was a no-go as chip shortage meant my bellowed Si8621 was unobtanium again. Well, I guess it was time to have it redesigned anyhow…

For those joining the party late, this custom card is intended for the CyberPower expansion slot thus allowing for a more direct query and control interface than what you usually get from the 1U UPS range. Benefit of this approach is that you can script whatever support you want for you operating system without having to deal with CyberPower’s software (or, in case of Linux, lack there of).

Fortunate for me, PCBgogo was interesting in sponsoring some boards so I went onto designing the new version. This included cleaning board a bit, swapping controller into an easier-to-solder SOIC package, and lastly swapping the digital isolator for some optocouplers.

Optocouplers actually fit this project rather well as UART signal is inverted on UPS side and speed is low enough (2,400 baud) that selection process almost doesn’t matter. From previous projects I had some LTV-817S optocouplers that are still both cheap and easily obtainable. Even better, there are a few other optocouplers in the same 4-pin package so there is little risk of not finding a replacement if needed.

With all changes done, I went onto PCBgogo to order some boards and was greeted by an interface really similar to what I saw before with other PCB houses. It seems they all share the same web guy. :)

This board didn’t need much so I stayed with normal FR4 but those playing with high power LEDs might be interested in aluminium or copper substrate.

Color selection was reasonable with “normal” colors only affecting manufacturing time (green was the fastest) while there was an extra charge for matte variants. At least it was so at the time I did the original order. When I look at it now, there seems to be a premium for anything other than green (that has a $5 special price).

Illustration

Lead HASL finish comes standard while all other surfaces are available at extra expense. I might be spoiled brat but I would love to have ENIG. And, while ENIG is one of the options, paying $40+ is way too expensive in my opinion. And yes, for this board it really doesn’t matter. But if you go to SSOP and smaller, ENIG is god-given.

Once order was made, the gerber files were manually inspected by a real person. As it happens, my board actually had a paste mask incorrectly set above one pad and they actually caught it. All got sorted over email with PCBgogo manually applying corrections. Nice extra!

The finished PCB arrived reasonably quickly (especially since I wanted the cheapest shipping) and I have no complaints there. PCBs came vacuum packaged which was probably not necessary for their health but nice anyhow. The edges were properly cleaned up and board was quite leveled despite being only HASL. It seems to me that all PCB manufacturers pretty much solved HASL these days.

Interestingly I received one extra PCB in my package which reminded me of old times when manufacturers would do this just in case one of the boards was faulty (electronics version of baker’s dozen, I guess). With PCBgogo this wasn’t a case and all boards were in perfect working condition.

When looking boards sideways, I was surprised to see (at least) two slightly different FR4 substrates indicating either two manufacturing lines or two different manufacturers. Soldermask color was the same for all boards so I suspect it’s the former but I don’t recall ever noticing a substrate difference with any other board house. Regardless, if the exact substrate is important for your board, I suspect you would go for at least “impedance control” option.

Illustration

In addition to the PCB I also got cover and there lies my major complaint - the dreadful PCB order number. Instead of placing it on the back, it got silkscreened right on the front of the board. Since human was involved in the review, I hoped to a more sensible placement on something that’s obviously a front panel. In their defense, they do offer an option to remove it if you leave a note and this seems to be offered at no extra cost. Just be careful not to forget writing it in.

With all components soldered, I got the board into my new UPS. Worked like a charm.


Please note I have received PCBs for this project for free from PCBgogo. They didn’t request anything specific for me to say and all opinions given here are mine and mine alone.

PS: You can see the latest version of the board on GitHub. The latest version actually gets rid of voltage regulator too so it’s as simple as it gets now.

Cananka for Framework Laptop

Illustration

I find the Framework laptop an interesting concept and the only thing preventing me from buying one was the fact there’s no 15" model. As I use a laptop screen as my primary visual interface most of the time, I simply find 13" a tad bit too small. However, I couldn’t help but notice their developer program for expansion cards. And that peeked my interest. Can I fit a CAN bus interface there?

All resources for expansion cards are part of a reasonably structured GitHub repository. In there you’ll find KiCAD templates for creating your own PCB and a bunch of a mechanical drawings in addition to an enclosure suitable for 3D printing. Those using other PCB software are kinda out of luck as board description is not sufficient to replicate all the curves board has. Fortunately, due to KiCAD using a (relatively speaking) simple human-readable file format, I managed to create a DipTrace PCB template.

Since I already have a CAN bus USB controller, I figured to just make everything a bit smaller while retaining as much features I could. It was immediately clean that I couldn’t support a fully insulated interface as there was no chance to fit all components. Cananka mini was as good as it gets. While most of LEDs had to go due to the lack of space, the programmatically-controlled bus termination thankfully remained.

After much jiggling of components in order to fit them, all was done. A type-C connector was a pain in the butt to hand solder and 0.5 mm microcontroller and UART controller were close to follow. Even better, all passives retained their 0805 size. I did have to go with a full size PCB in order to fit everything onto a single PCB side but I figured 3D printed case would be ok.

Selecting a user-facing connector was a pure trouble and I went over multiple ideas to no avail. My favorite failure was using an edge connector with a Phoenix ZEC connector attached to it but I found it sticking from the side of laptop was a bit too much. At the end I went with a surprisingly well-fitting bodge. A vertical JST PH connector required just a minimal pin bending to fit highly restricted module height perfectly. Not only that but the connector is common enough that finding a premade cable (or making your own) will present no trouble.

Manufacturing board was actually a breeze as I modified PCB slightly as not to cause warnings with the Molex 105444-0001 connector. Any manufacturer capable of producing double-sided 0.8mm PCB at 6/6 should be just fine. And yes, you really need thinner than regular PCB in order to mount a type-C connector.

With PCB in hand and after a LONG time waiting for components (darn car manufacturers got all the good CAN bus stuff), I was ready to test the device. And here is the genius of Framework’s extension card design - it’s still just a type-C so I managed to test everything before ever needing a Framework device. Really easy R&D.

Illustration

However, with all electrical stuff out of way, it came time to see if my expansion card can actually fit. I contacted Framework for a loaner laptop to test it, got it in a few days, tried to push card in, and… the darn thing fitted like a glove. :)

While I would consider the whole adventure a success, I am not completely happy. First of all, a full size PCB was a mistake. I expected it to terminate 1 or 2 millimeters before module’s edge but in reality it’s flush with the frame. That means my connector is actually sticking out a bit. Probably not enough to matter for normal handling but any harder blow to the side (e.g. due to throwing a laptop into the bag) runs risk of just shearing the connector off. Also, while 3D printed case is fine for prototyping, having a better looking finish would go a long way.

Illustration

What’s next then? Well, the first step will be shortening PCB by a few millimeters as not to have connector sticking out from side. This change will be easily done as there is still enough space on a PCB. But that’s not where I’ll stop.

My thoughts go toward making the board even smaller as to fit into the existing Framework USB type-A enclosure. While this will mean I will have to go toward double-sided PCB load, it will look MUCH neater even though there will be some extra space. My thinking is to 3D print a transparent plug that would allow a status LED to shine through. That would avoid the “gaping hole” look while actually serving a purpose.

Timeline for this is a painful topic. While I managed to obtain some PIC18F25K80 chips, I am completely out of MCP2221A. And the soonest anybody seems to be able to get them is end of the year. And let’s not even talk about MCP2561 CAN bus controller. In short, I don’t expect anything new before the next year no matter how quickly I turn the PCBs around.

However, for now, I have the single Framework with a “native” CAN bus interface out there. :)


PS: While I did ask Framework for a loaner, I was told to just keep it once I queried how to send it back. I don’t believe my experience with developing and designing expansion card was affected by this as 95% of this work was completed way before a free Framework landed in my lap. I was never asked by a Framework team to write anything nor they conditioned sending me laptop to a positive coverage.

PPS: Yes, despite now having a 13" Framework in my possession, I still hope to purchase a 15" variant if it becomes available.

Longwave Fun (part 4; aka Failure)

This is a part 4 (out of 4) in my WWVB time signal series (part 3).


Illustration

While I had a tremendous amount of fun making PIC-based WWVB receiver and both hardware and software worked, it wasn’t a full success. Frankly, I would recommend anyone wanting time synchronization functionality to use a Raspberry Pi based version (also available for purchase). Reason is one and one only - operating frequency.

With Raspberry Pi working at 1 GHz+ it’s really easy to generate precise 60 kHz carrier wave and keep the timing straight. Microchip PIC16F1454 working at 48 MHz should be capable of giving a nice clean 60 KHz waveform too. In practice, it doesn’t. In order to allow the internal clock (a minor miracle, if you ask me) to work with USB, PIC will keep doing minor adjustments with every received packet. It will be awesome for USB but any external element will see this as a slight drift.

My Casio watch takes its time to synchronize to WWVB signal and, until done, it keeps comparing it to its internal quartz. And it has a damn good quartz so these micro-adjustment PIC makes really mess with it. Nine times our of ten, it would just abort synchronization since it thought signal was unreliable. In short, it was a pain in the butt.

And yes, it was possible to synchronize some clocks regardless of these imprecisions. Most clock actually do a much shorter synchronization cycle and without much verification. But the one I wanted to play with was being a smart alec. And in general, I actually found watches to be more picky than clocks - go figure.

Having an external crystal for microcontroller would help with stability. But it would need to be a full 48 kHz one. Guess what - the maximum supported frequency for external clock is 20 MHz. So, if we go with 16 MHz and 3x PLL, we’re in a slightly less leaky boat but still not out of woods (I just love mixed metaphors.)

Combine that with a general simplicity Raspberry Pi solution provides and the whole microcontroller-based solution was doomed to be inferior. But there are worse ways to spend a few weekends. :)

Longwave Fun (part 3; aka Firmware)

This is a part 3 in my WWVB time signal series (part 2, part 4).


Illustration

When connecting custom hardware to a computer, I love using a serial port interface. It’s widely supported and there are so many tools you can use to connect to it. While it’s common to use UART-USB interface chips (e.g. notorious FTDI or my current favorite MCP2221A), I chose to skip that part. As project was using PIC16F1454 that has USB capabilities, it seemed quite sensible to roll one’s own serial port interface.

The serial port code is based on Microchip’s CDC example but with all unnecessary parts removed. Since I wanted direct communication, all that baud rate stuff could go out. Yes, you can talk to the hardware at any baud rate you want - USB doesn’t care nor does my code. A bit unusually, I used polling instead of interrupts for USB. It actually doesn’t impact the USB communication speed for this use case and it leaves interrupts clean for timer code that’s much more sensitive to timings.

And this code is all about timing. The carrier signal is generated using PWM functionality. Based on the built-in frequency signal (48 MHz), it was actually possible to hit 40 kHz (JJY) and 60 kHz (WWVB, MSF, JJY) PWM frequency exactly. But this was not possible for 77.5 kHz (DCF77) so there we have about 0.1% error. Not great, not terrible.

Once we have the carrier sorted out, we need to have a separate time tracking for attenuation. This is in interrupt happening every 0.2 ms until we “collect” 100 ms. At that moment, a decision is made whether to keep the carrier signal as it is, turn it off, or just attenuate it a bit.

The actual data for all this logic is not done in microcontroller. While it would be possible to do for any of the protocols, this microcontroller simply doesn’t have enough programming space to do them all. Therefore, all necessary calculations are done in software and then transferred once a minute to the device.

And that’s about it. The software on a computer calculates bit-stream data for the next minute and sends it to the microcontroller. The microcontroller loads that data and keeps accounting for the current minute. Once minute is over, all repeats again. Yes, there are a few details more but you can discover those yourself.

Of course, the source code is available on GitHub.