Bad things happen when programmer finds a soldering iron

Serial Number In Intel Hex - Bash Edition

When creating USB devices having an unique serial number for each is quite important. Yes, stuff will work with duplicate (or missing) serial number but you might notice Windows will get confused. With the serial devices the most noticeable effect will be a different port assignment on each plug. Not a breaking deal but a bit annoying.

The easiest way to deal with this is having a dummy serial number in code and then using post-build action to change it in the final Intel hex file. For TmpUsb project I even created a script to do exactly this. However, that script was written in PowerShell which until recently (March 2020) didn’t work on Linux and it definitely doesn’t come installed there by default. This and a few other reasons were why I decided to rewrite this in Bash for my CyberCard project.

The first step is setting up the serial number. The most crucial step here is __at macro that will ensure serial number is always at a predictable location. For my example, I selected location close to the end of addressable space (0x1F00). One could use special characters here but I would highly advise to have a valid USB serial number just in case script fails. That means at least 12 alphanumeric characters but I always go for numbers-only as it makes script a bit easier.

//Serial string descriptor
const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[13];}sd003 ^^__at(0x1F00)^^={
    sizeof(sd003),USB_DESCRIPTOR_STRING, {
        '1','9','7','9','0','1','2','8','1','8','1','5','0'
    }};

The final Intel hex file location will end up multiplied by 2 and thus at offset 0x3E00 further shifted by 4 hexadecimal characters. Script then needs to replace every 4th hexadecimal byte up to the count of 13 in my case (as my serial number is 13 digits long).

The only task remaining is calling it once project is done building. Previously I would do this using project properties dialog (Project Properties, Conf, Building, Execute this line after build) but that proved to be an issue if you want to compile on both Windows and Linux. While Linux setup was quite straightforward, on Windows it would get confused if you have any Linux-based toolset installed (e.g. Git Bash) and throw an error:

"User defined post-build step: [update-serial.sh]"
nbproject/Makefile-default.mk:105: recipe for target '.build-conf' failed
make[1]: Leaving directory 'Q:/Projects/Electronics/CyberCard/Firmware/Source'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
process_begin: CreateProcess(NULL, bash Q:\Projects\Electronics\CyberCard\Firmware\Source\update-serial.sh, ...) failed.
make (e=2): The system cannot find the file specified.

One workaround was to simply wrap call to this script in .cmd (or .bat) file but that would cause issues when compiling under Linux. Only if there was a way to execute one command for Windows and another one when running on Linux… Cue the Makefile.

While you cannot differentiate OS from GUI, you can go and edit MPLAB project Makefile directly. There we can point toward .cmd on Windows and toward .sh when running on Linux.

.build-post: .build-impl
# Add your post 'build' code here...
ifeq ($(OS),Windows_NT)
        update-serial.cmd
else
        ./update-serial.sh
endif

All that is left is actually placing the script(s) in the same directory the Makefile is situated (download here) and you’re good assuming you defined serial number as I did. If you didn’t, just adjust LOCATIONS variable inside.


PS: This assumes you have Git (or some other bash-capable environment) installed in Windows.

PPS: Don’t forget to use <Tab> in Makefile for indentation. Spaces won’t do here.

ResetBox

Illustration

It all started with an itch to scratch. My modem occasionally misbehaves and needs a reboot. Unfortunately, I have it mounted way back in the 19" rack and accessing it requires removing back panel. And no, I cannot easily unplug it’s connected to UPS and wires are all tucked in the back. What I needed was a button in one of rack spacers allowing for reboot of equipment in the back.

Since I couldn’t find something like this readily available, I decided to build it.

First I enumerated all the equipment I have. One modem (12 V), one NUC (19 V), and finally two wireless access points (48 V). I was at crossroads. As voltage range is quite wide I had to choose if I want to make device work of specific voltage (cheaper) or to have a single device covering all the bases (more expensive). While going cheaper was tempting, I decided to go with wide input after all. It meant I can design thing only once and use it later for some other project without having to check voltage rating each time.

This decision also made me select DC-DC converter module. Making my own wide range converter would be possible but getting it just right would take annoyingly long time and actual cost wouldn’t be much lower. Yes, at $5 R-78HE5.0-0.3 is not cheap but it’s simplicity is worth every penny. If I ever decided to produce this box in bigger quantities, this would probably be the first thing to go in order to lower expenses and simplify design.

While not necessarily mandatory, I also opted to include EMC filter for DC-DC converter. Considering low currents it’s probably an overkill but I really didn’t want to find out later that I’m spraying too much noise around my rack. Design improvement would be to either remove filter all together (probably possible due to low current requirements) or make it much simpler and hopefully omitting SMD inductor. I hate to solder that stuff.

It took me a while to find a button as I wanted momentary switch with LED (excellent for giving status) and it had to look nice in rack. I finally found Adafruit’s S 560. This button is awesome in person.

To connect button, I selected JST S3B-XH. I needed the minimum of three wires (GND, LED, and switch input) and I already had bunch of premade cables made in the course of another project. If I had only this project to worry about, I would probably select 4-wire connector as it would make soldering a bit easier. Not it’s too hard now - just wire GND to both LED and NC input and you’re golden.

Voltage connector choice was extremely easy as literally all devices I wanted to control already used barrel connector. Since 2.1 mm barrel had became a defacto standard for network cameras, this also meant I could count on connecting it all with factory cables making the whole setup a bit nicer.

Speaking of which, I mulled long time about whether I should support only barrel setup with positive voltage on tip and negative voltage on sleeve or the inverse one too. All network devices I ever owned have positive tip. However, I decided to allow negative tip setup to. I am 95% sure this was completely unnecessary precaution but at least I don’t need to worry about it.

As far as saving money goes, supporting only positive tip barrel connectors, would allow me to remove diode bridge on input and allow me to replace mechanical relay with MOSTFET. As I’m making only 3 devices, for now I value flexibility the most. But decision to include these still bothers me a bit.

Housing hardware fell to Hammond 1593KBK. This is a reasonably sized and more important reasonably priced case with side panels. While I usually replace such panels with custom PCB with cutouts in place of connectors, this time I opted for 3D printed version. It’s definitely cheaper than PCB but it does require you to have printer. And no, it’s not as nice as PCB panel but it looks good enough - especially since device will sit in the back of the rack.

Lastly, I had to select microprocessor that would handle all logic. As most of my devices, I simply went with selecting Microchip’s part. I needed two outputs (LED and relay) and a single input (switch). In order to avoid complications, I like to keep programming lines separate too (MCLR, PGD, and PGC). Yes, you can share these lines with logic if you’re careful but having them reserved is one way to make sure. Once you include power supply, there is variety of 8-pin microcontrollers to chose from.

Finally I went with PIC12F1501-I/SN. It has 8 pins which is what we needed. SOIC package makes it easy to solder and internal oscillator minimizes extra components needed.

Code running on chip is as easy as it gets. What follows is the main loop.

void main(void) {
    io_led_on();
    while(true) {
        if (io_switch()) {
            if (debounceCounter &lt; DEBOUNCE_MAX) {
                debounceCounter++;
            } else {
                intensityCounter++;
                if ((intensityCounter % 8) == 0) { io_led_on(); } else { io_led_off(); }
            }
        } else {
            if (debounceCounter == DEBOUNCE_MAX) {
                io_relay_on();
                for (int i = 0; i &lt; RESET_COUNTER; i++) {
                    io_led_on(); wait_short();
                    io_led_off(); wait_short();
                }
                io_relay_off();
            }
            io_led_on()
            debounceCounter = 0;
        }
    }
}

If button is pressed, counter (debounceCounter variable) gets increased while button is pressed and until it reaches maximum. Side effect of handling counter like this is automatic debouncing and making sure short, accidental, swipes don’t activate reboot functionality. Once at maximum, counter stops and we dim the LED so user knows reboot will follow once button is depressed. Dimming is actually done by poor man’s PWM - just turn LED manually on 1/8 of interval (when counter is divisible by 8).

Second path executes only when the button is not pressed. If debounce counter is at maximum, a simple reset sequence starts. Here we disconnect relay (ironically by turning it on as it’s normally-connected setup) and LED blinks for 4 seconds. Then we turn relay back off so that power returns to the device.

It’s probably the simplest code I’ve written in quite a while.

If you want to check it a bit more, feel free to look up both board and code at GitHub.

Why You Should Update GPS Firmware

Illustration

Assuming you survived the great GPS rollover, you might wonder if updating your GPS firmware makes any sense at all. It’s obvious you might want new maps. But firmware? Why do you need a new one if everything works just fine.

Real answer is that you probably need not bother. Again, assuming everything works fine, there is no navigation reason for firmware upgrade. Navigation will work equally well whether you have fresh firmware or one that’s a few years old.

However, if you care about time keeping, you will definitely need to update your GPS once in a while. And it’s not because of rollover - that issue is sorted now with the new 13-bit week counter and it’s up to your grandchildren to bother with that. Nope, it’s darn leap seconds.

Leap seconds were created to account for uneven rotation of this big rock we call Earth. As such they are currently beyond our capability to calculate in advance. Once in a while astronomers look upon the skies and decide if leap second is needed. One cannot know in advance when this will happen.

AS GPS time has no concept of a leap second, firmware is what adjusts GPS time to UTC and then later to time zones we all deal with. Guess what, if you have old firmware, your GPS will adjust wrongly and thus something you believe to be a correct time will be off by a second or two (or 27 if you were really lazy).

I know it’s not a breaking deal for a vast majority of population but I simply find it rather unnerving that you would intentionally make your GPS show the wrong time.

Update darn firmware to avoid having your existence out of sync with rest of the world. :)

El-Cheapo Power Supply Noise Measurement

If you are not doing power supply measurements every day, you probably don’t have any specialized probe for it. At best you might be using two probes and math channels to get a cheap differential-like probe as the only thing needed for this is your standard probes and a pair of pass-through 50 Ω terminators. Dave went over it on EEVBlog channel better than I ever could.

While this approach might not be perfect, it has advantage of a decent result with minimum parts needed. Assuming you already have the probes, only other parts needed are pass-through 50 Ω terminators that come in handy for other stuff too. If you are lucky, your scope might even have them built-in.

Due to low cost and ease of measurement I actually prefer it to more common single-ended measurements with short ground spring. Not only I find probing with two hands easier than dealing with springy ground wire but it also alleviates the need to worry as much about noise sources. With advent of switched LED lightning single-ended measurements just got too annoying.

Disadvantage, beside the precision, is that all your measurements need scaling as your probes and termination resistor make a voltage divider. Since we already know one side of voltage divider (50 Ω terminator), we need to measure probe’s resistance. From tip to BNC connector that will be in a few hundreds ohms range. Using the voltage divider formula, we get the ratio (for example I measured about 360 Ω):

ratio = (Rprobe + 50) / 50 = (360 + 50) / 50 = 8.2

While simply multiplying by ratio resulting numbers is not too much of a hassle and you could be done here, quite a few oscilloscopes will allow you to create a custom probe. As I usually use PicoTech 2206B, I will explain how to configure custom probe in PicoScope 6 so our on-screen result is already scaled correctly.

In Tools menu the first entry leads to Custom Probes window. There clicking on New Probe will start a wizard dialog. We use the standard volts unit and a linear equation. Our precalculated scaling ratio (7.6 in my case) goes under gradient and offset stays 0. Automatic range management is fine but software frequency filter we set to 20 MHz as PicoScope 2000-series has no hardware bandwidth selection. After naming the probe we can now select it in channel’s configuration.

When dealing with the “standard” probes, be aware that most x1 probes have around 10 MHz bandwidth. As power supply noise is usually measured with 20 MHz bandwidth limit this is something to be aware of. However, the whole measurement setup is on a cheap side and this will rarely be a deal breaker. Yes, you might not see the best fidelity but you’re gonna be in the ballpark regardless.

Further more, we are not interested in the DC component. If oscilloscope supports it, use AC coupling as we’re not interested in DC values - only noise. This will allow you to use all ADC bits for signal you want to see. If oscilloscope supports only DC coupling, you won’t be able to see much as, at ±5V, your 8 bits give you only 40 mV resolution and it goes worse from there. Unless you can get your analog offset configured (unlikely if your scope is so low end that AC coupling is unavailable), you can forget about any meaningful measurement.

Those doing it on cheap can check $140 PicoScope 2204A as it does support AC coupling. For example, assuming 100 mV range, you get quite adequate 0.4 mV resolution. As you go up in model selection you will get better bandwidth and bigger sample buffer but I would think even this works well for what we can get with this testing setup.

And I am mentioning PicoScope here specifically as other cheap scopes (whether PC-based or in MP3 player case) often don’t have AC coupling and lie about bandwidth on a grand scale at a comparable price. While you can get around missing AC coupling by placing capacitor in series, it is much harder to work around bandwidth restrictions and a missing analog frontend. PicoScope is not perfect (and it cannot be at that price point) but they don’t lie about their capabilities and their PC software is the best I’ve seen.

That aside, we’re not done yet. To see our “differential” signal noise, we have one more step. Under Tools there is an option called Math Channels. Here we can select already predefined A-B function and finally we can see the noise in all its glory.

PS: To automate measurements a bit, you can use Measurements, Add Measurement. Selecting A-B as a measurement channel will allow you to add both peak-to-peak and AC RMS as the most useful values.

PPS: The lowest PicoScope model where you can do these measurement comfortably is $350 2206B with 32 MS buffer. Lower models have only either 8 kS ($140 2204A) or 16kS ($225 2205A) making any analysis a bit annoying.

PPPS: Great thing about using PicoScope for measuring power supply is that it can be easily isolated. Just disconnect laptop from power supply and your USB (and thus PicoScope) is floating. :)

CAN Bus Decoding Using PicoScope

Illustration

When it comes to CAN bus protocol decoding, beating PicoScope is hard considering that even the cheapest 2000 series member has full support for it. Converting such scope to one with the floating ground is as easy as unplugging your laptop (or running your desktop of an UPS’ battery). However, there are a few issues you might want to avoid.

The very first step would be to properly trigger on the CAN bus message. Reasons for this are twofold. If there is any issue with your signal, this will allow you to catch it early so you can sort out basic electrical stuff before going on the higher level. Secondly, reliable triggering will help software decoder properly detect message start.

While exact trigger level might depend on the exact use case, in 95% of cases we are talking of 5V differential signal. Roughly speaking, your CAN L (low) signal will be either at 2.5 V or 0 V logical level and your CAN H (high) signal will be at either 2.5 V or 5 V level. Since signal between those two lines is mirrored, you only need to probe one of those two lines. I usually select L but that’s just because I already have cable made for it. All instructions are essentially the same whichever polarity you select. Good starting trigger level for low line is 2 V (3 V for high line). If your resting signal is further away from 2.5 V that would either point to termination issue or gross impedance mismatch and you should sort “electricals” first before continuing further.

Illustration

I like to set pre-trigger level to 5% as it allows me to use most of the screen for actual signal. To receive the maximum screen real estate you might be tempted to use 0% pre-triggering but I find seeing a bit of signal before my CAN message does help if you have a busy bus. Even better, this also allows you to ignore trigger edge setting completely (should be falling for low line).

Only once you can reliably catch your CAN signals, you can add serial decoder - Tools / Serial Decoding / Create / CAN. After selecting channel with your signal (named Data on this form), you will reap the benefits of getting triggers right. First and foremost, you should have bitrate set correctly as soon as channel is selected. More over, your threshold level should be automatically calculated from the signal. Yes, you can manually set it to 2 V but Murphy says you’ll forget it and then be confused as why decoding has errors or even misses a message completely. Lastly, you select High or Low setting appropriately depending on the line you are probing.

Congratulations! If all went good, you have just successfully decoded CAN bus message.

Ideal Multimeter

Illustration

If you are into electronics, probably the first test device you’ll own will be a multimeter. Not only you can get it for dirt cheap - my first multimeter was less than $10 in today’s money - but you can also do a lot in digital electronics with multimeter alone.

As I pawed my road to electronics with quite a few good and bad multimeters, I want to share my thoughts on what is important for a decent general purpose multimeter. I will stick to Agilent/Keysight U1232A and EEVBlog 121GW for most of my comparisons as these are two multimeters I use these days. However, principles are general and can be applied to any multimeter.

The very first thing I need of multimeter is to be safe. When I just started with electronics I didn’t care about that because I worked with low DC voltages only. However, sooner or later I would measure something on 240 V line. And not all multimeters would survive - some failing spectacularly. Having kids of curious age I like to have all my meters CAT rated and UL listed. And most of good meters will not die even if you do something utterly stupid like connecting to line voltage while in current range. Higher cost does offer higher survivability.

The next thing I find very important is speed. Having a high digit count is nice but it usually comes at the cost of speed. I find that 6000 count on my Agilent is much more comfortable for general measurement than 50000 counts EEVBlog has to offer. Yes, high digit count is important for certain scenarios and it’s not too bad to have one such meter available. But, if I had to choose one multimeter, I would go for a faster one. That said, don’t go under 6000 counts.

Illustration

In regards to speed, a special attention should be shown toward continuity check. Regardless of the display update speed, continuity tester must be fast and latched. Even the most minute contact between probes has to result in the (low volume, please) beep or flash. While most people prefer beep, I find backlight flash really great but that might be due to most of my work taking place during night when flash is easily visible and laud noises are not really desired. Speaking of flash, I love option to turn backlight with a single button. If you ask me, I would set all my multimeters to have backlight by default - battery life be damned - but I will be OK if I can turn light on easily.

I prefer when each measurement is a separate selection on a range switch as it makes switching between them a breeze. Multimeters with a lot of options tend to have just a few ranges but with Mode button switching between them. 121GW is specially nasty in this area as, not only it requires mode button but it also remembers the last mode selected. While this sounds like a good idea, it ensures that I need to check every time by looking onto display (that requires long press for backlight) before doing any work. This alone is probably the major reason why my Agilent sees much more use despite 121GW having more functionality and better specs.

Speaking about more functionality, I found 121GW’s mV range a real gem. If your multimeter gives you decent resolution it can pretty much substitute measuring current altogether. Just measure voltage drop over a resistor (or fuse) and you have all that’s needed to calculate current. Much easier than switching to current range and inserting multimeter into the circuit. Yes, this won’t work if you need precise and/or low-current measurement, but it works well to get ballpark figure and that’s often all that’s needed anyhow.

And order of measurements on the range switch is equally important. I prefer my order to be Low-Z voltage measurement, Off, DC voltage, AC voltage, other voltage related measurements, current, and finally the second off position. While this does prevent me from easily turning off multimeter by just moving switch into the far left position, it enables me to setup all on board and go into voltage measurement directly without traveling through Low-Z mode. While I love low-Z mode, I do not like when I have to go over it from off position (Agilent did this beautifully). Speaking of low-Z, idiotic restrictions like 12V minimum voltage for it to work 121GW has make it more nuisance than an useful range.

Illustration

If you are into temperature measurement, you will want support for standard K-type probes. Agilent here fails miserably as it requires special adapter to do it. My personal opinion is that, if any special adapter is required for measurement, that functionality might as well not exist.

Functionality I found awesome but it’s rarely implemented these days is a simple frequency counter. Yes, both U1232A and 121GW can measure frequency but range is laughably small (up to 100 kHz and 1 MHz respectively). When you deal with PWM circuits or you just want to check clock lines, a decent frequency measurement (like UT71C has - up to 40+ MHz) means you don’t need to pull out the scope most of the time.

Having the low burden voltage is an excellent thing in theory as it can mean a difference between being able to measure circuit’s current or not. In practice it depends on your use case. For me, working on 3.3V and 5V circuits, there is more than enough margin to handle any drop multimeter brings in.

Diode measurement should be a separate range so LED (or any other diode) polarity can be tested without much effort. If you are dealing with LEDs a lot, investing in multimeter that supports higher test voltage (like 15V on 121GW) does come in handy.

Other features worth mentioning include True RMS and auto-hold functionality. I found that not having either is not a breaking deal but they do come in useful once in a while.

Illustration

I do require support for either AA or AAA batteries in any multimeter I use. Not only 9V has generally inferior capacity but it’s also something I don’t ever have when I need it. There is simply no justification this day and age why standard batteries shouldn’t be used. And battery compartment should have a captive screw unless you want to spend 15 minutes searching for it every time you change the batteries.

Every decent multimeter will have a way of connecting to the computer for logging purposes. While bluetooth seems like a good idea, I found it lacking in practice as I would often find something would go wrong with it overnight. A nice physical cable is my preferred solution here.

In regards to the probes I find silicone is a must as stiff probe cables will be a hindrance. Tips have to be sharp and ideally their CAT III/IV insulation should be removable. Fully insulated probe is fine when dealing with high voltages or narrow spaces but often more exposed metal makes for easier measurement.

Unfortunately, finding the multimeter with all these features is hard if not impossible. Every multimeter - regarless of the price range - will have something missing. However, with two multimeters you can come darn close.

PS: Notice I haven’t spoke about precision at all as needs are highly dependent on the exact use case. For me 1% on DC is OK and this is something easily found in any decent multimeters.

PPS: One enjoyable thing you cannot really know until you see it also how multimeter fits in its carry case. Agilent is perfect here as you have range switch, all buttons, and probe holes reachable without ever getting it out.

Random on 8-bit PIC

When dealing with 8-bit microcontrollers one doesn’t always have all facilities taken for granted on a computer. One such example is a simple random number. While all computers can easily provide you with random number at will, unless you have a heavily specialized PIC, real random numbers are out of your reach. However, there is a way to get sufficiently close to make it work for most non-cryptographic purposes.

The answer is in linear-feedback shift registers. Assuming polynomials are carefully chosen, it is relatively easy to get non-repeating 255 byte sequences on 8-bit PIC. Yes, they are not random but for the most purposes they are random enough.

There are many types of LFSRs, Fibonacci’s being the most famous and probably the one implemented most often due to its simple hardware structure. However, for the PIC device, Galois has similar enough properties with a much simpler software structure.

General Galois function might looks like this:

#define GALOIS_POLYNOMIAL 0b11010100; //x^8+x^7+x^5+x^3+1
uint8_t randomState;

uint8_t getRandomByte() {
    unsigned bit = randomState & 0x01;
    randomState >>= 1;
    if (bit) { randomState ^= GALOIS_POLYNOMIAL; }
    return randomState;
}

This function essentially takes whatever data has in previous step and, using one of the maximum sequence length polynomials, calculates the next random byte. However, if you run it as such you might or might not get anything other than 0. Why? Well, pesky sequence has one blind spot - number 0. You can get any 255 byte sequence assuming it does not contain 0. If data becomes 0, it will stay 0.

Knowing this, one might be tempted to simply initialize data to some number and call it a day. Indeed, that would work for many applications that use 8-bit PIC. However, there are two things to have in mind: sequence will repeat every 255 bytes and random number sequence will be exactly the same on every startup.

To deal with the issue of repeating sequences, we need a counter that will simply reset the sequence after it “runs out of bytes”. As LFSR “circles” back to the original value after going thru all permutations (assuming polynomial is selected well), the easiest approach is to remember first value and reinitialize inner state once that value comes up again.

Second problem is a bit harder but not impossible. While the hardware random source would be nice, we can also do without it. My favorite approach is to have 8-bit timer running (e.g. TMR4) and then execute something that doesn’t have deterministic time - for example any user interaction (e.g. button press), writing to EEPROM (if PIC has one), or just waiting for PLL lock:

//setup unused timer that runs of the clock
T4CONbits.T4CKPS = 0; no prescale
T4CONbits.T4OUTPS = 0; //no postscale
T4CONbits.TMR4ON = 1; turn on timer 4 (or any other timer)

PLLEN = 1; //enable PLL
while (!OSCCONbits.OSTS); //wait for PLL lock

If function takes a slightly different amount of clock cycles every time, your timer will be in reasonably random state every time you check it and you can use it as a seed for generating random sequence - just don’t forget to ensure it is not zero.

Thus, our simple generator of random numbers could look something like this:

#define GALOIS_POLYNOMIAL 0b11010100; //x^8+x^7+x^5+x^3+1
uint8_t randomState = 0;
uint8_t randomStart = 0;

uint8_t getRandomByte() {
    if (randomState == randomStart) {
        randomState = (randomState ^ TMR4 ^ polynomial) | 0x80; //ensure non-zero at the cost of LSB
        randomStart = randomState;
    }

    unsigned bit = randomState & 0x01;
    randomState >>= 1;
    if (bit) { randomState ^= GALOIS_POLYNOMIAL; }
    return randomState;
}

If you run it for a while, you’ll notice something a bit disturbing. While start of the sequence is random, sequence is always the same. We need a way to make the sequence a bit more random. The cheap way to change this is to simply loop through all maximum length polynomial terms and thus extending our sequence from 255 to 4080 bytes.

Or, to break a pattern, you can even make switches in middle of sequence to make it a bit less obvious:

const uint8_t POLYNOMIALS[] = { 0x8E, 0x95, 0x96, 0xA6, 0xAF, 0xB1, 0xB2, 0xB4,
                                0xB8, 0xC3, 0xC6, 0xD4, 0xE1, 0xE7, 0xF3, 0xFA };
const uint8_t POLYNOMIALS_COUNT = 16;
uint8_t polynomial = 0xB8;

uint8_t randomState = 0;
uint8_t randomIndex = 0;

uint8_t getRandomByte() {
    if (randomIndex == 0) {
        randomIndex = (TMR4 >> 6) + 1; //generate number 1 to 4
        randomState = (randomState ^ TMR4 ^ polynomial) | 0x80; //ensure non-zero at the cost of LSB
        polynomial = POLYNOMIALS[TMR4 & 0x0F]; //select next polynomial
    }
    randomIndex--;

    unsigned bit = randomState & 0x01;
    randomState >>= 1;
    if (bit) { randomState ^= polynomial; }
    return randomState;
}

Realistically, there is only so much you can do without any random entropy while keeping function reasonably fast and I think this final function gives a reasonable result.


PS: Again, do not use this for anything security related - for that you need real random numbers, not pseudo-random sequences.

PPS: Jason Sachs has a nice article going a bit deeper in details about LFSRs.

PPPS: If you really want to expand random sequence, think about using 16-bit state and polynomial. Albeit the price of 16-bit computations on a 8-bit microprocessor is non-negligible (albeit not devastating), your use case might warrant such step.

EEPROM Access Routines For PIC18

While I was playing with CAN bus, I needed to write something to EEPROM memory - to keep it safe between reboots. Imagine my surprise when calls to eeprom_read and eeprom_write returned errors:

warning: unsupported: The Read_b_eep routine is no longer supported. Please use the MPLAB X MCC.
warning: unsupported: The Busy_eep routine is no longer supported. Please use the MPLAB X MCC.
warning: unsupported: The Write_b_eep routine is no longer supported. Please use the MPLAB X MCC.

It seems PIC18 series doesn’t support old EEPROM routines that worked on PIC16 but, at the same time, my PIC is old enough not to be properly supported in the suggested MCC. I guess it was time to write PIC18 version of these functions myself.

Reading over documentation for PIC18F25K80, I was pleasantly surprised that EEPROM writing hasn’t really changed. My assembly code I wrote back in PIC16F84 days was quite close to what’s needed these days too. The only significant change was expanding EEADR to allow for more than 256 bytes. Yes, there are some shenanigans with setting EEPGD/CFGS bits but that’s done only once.

My replacement for eeprom_read function was similarly named eeprom18_read and it’s really a drop-in replacement. Not to be the one to dwell in past, I opted to change my ASM code to XC8 flavored C. It’s essentially just setting up EEPROM address, followed by read request, a short pause to get the data, and then reading the data itself.

unsigned char eeprom18_read(unsigned int offset) {
  EECON1bits.EEPGD = 0; // accesses data EEPROM memory
  EECON1bits.CFGS = 0;  // accesses data EEPROM memory

  EEADRH = offset >> 8;
  EEADR = offset;

  EECON1bits.RD = 1; // initiates an EEPROM read
  Nop();             // it can be read after one NOP instruction

  return EEDATA; }

Writing was a bit more involved but not too much so. After setting EEPROM address and data, it was important to allow EEPROM write and disable interrupts as ancient EEPROM unlock sequence (0x55 0xAA) must not be interrupted. After write is initiated, code will wait for it to complete and then restore interrupts. For safety it will also disable further writes to EEPROM until it’s needed again.

void eeprom18_write(unsigned int offset, unsigned char value) {
  EECON1bits.EEPGD = 0;  // accesses data EEPROM memory
  EECON1bits.CFGS = 0;   // accesses data EEPROM memory

  EEADRH = offset >> 8;
  EEADR = offset;

  EEDATA = value;

  unsigned char oldGIE = GIE;  // interrupts be disabled during this code segment
  GIE = 0;

  EECON1bits.WREN = 1;  // allows write cycles

  EECON2 = 0x55;  // write sequence unlock
  EECON2 = 0xAA;  // write sequence unlock

  EECON1bits.WR = 1;     // initiates a data EEPROM erase/write cycle
  while(EECON1bits.WR);  // waits for write cycle to complete

  EECON1bits.WREN = 0;  // disable write
  GIE = oldGIE;         // restore interrupts
}

PCBWay

Illustration

Creating hardware projects usually involves a lot of time spent creating PCB design and then even more deciding which prototype service to use. When PCBWay approached me to consider their service, all I could say is heck yeah. :)

The first thing you’ll experience ordering boards from them is a page full of options. While you can select a few simplified “deals” from main page, they will all bring you to this page for customization. And you can customize a bunch. It’s like a prototype service on steroids.

You can select number of layers (up to 14), material (including flex construction), board thickness (0.4 to 2.4 mm), copper thickness (1 to 4 oz), and surface finish (including hard gold - great for edge connectors). Yes, not all combinations are supported at the same time - for example you cannot have 14-layer aluminum board) - but there is enough choices to make you happy and/or crazy.

You can also select track/spacing (up to 4/4 mil) and hole size specification (0.2 mm) with or without plugged vias (great for BGA work). If you click on special option you can further select castellations, edge plating, impedance control, blind vias, the list goes on and on. If you need it, chances are you can order it. A more “standard” customization of solder mask and silkscreen color almost goes unnoticed.

All these options do come at price. Not only the cost of board can go into hundreds of dollars but it also makes the whole process way more complicated than what you’ll need 99% of time. Clicking on various options can get you so sidelined that you’ll forget about the most basic stuff.

For example, I managed to order all my boards without ENIG finish (immersion gold) just because I somehow missed that option. I would kill for a few simple templates with the most logical options preselected (e.g. ENIG for surface-mount soldering) and with only limited customization allowed (e.g. solder mask and silkscreen color). Yes, there are “specials” available but they still end at the same overly-complicated page.

Upload process is rather pain-less and even supports my favorite RAR format. I used the exactly same files I usually use with OSH Park and PCB:NG service without any issue. Unfortunately, there is no gerber preview here so you might be up for a surprise if something goes wrong.

As one has to define size for board during upload, I intentionally selected a smaller size for one of them. Person doing verification caught it and corrected both board size and the price. This gives me hope that any gerber errors might be caught too but note that such manual review will probably happen a few hours after the upload. This means delay for any issue not easily correctable. I would consider first uploading gerbers to OSH Park and using their awesome preview to perform a sanity check.

Combined with overly extensive customizations, lack of preview makes service much harder for hobbyist (such as myself) to use and I believe even professionals don’t care/need about most of these options on a regular basis. Even when such extensive customization is needed, I doubt one would prefer to go from scratch each and every time.

It takes a few (five in my case) days for boards to get produced but then comes the longest part - shipping. As PCBWay is in China, this means that any cheap shipping will take a while. The cheapest option actually took 2 weeks to arrive, making it slightly slower than US-based competitors. Of course, this assumes you are in States. For international customers timeline might not differ as much. Moreover, there is option for courier shipping but at a significant premium.

Once boards arrived, I found a few things in common to all - most noticeably v-groove routing for external edge. It makes board much cleaner and without any sharp edges to deal with. Boards themselves come either vacuum packed or in a baggie for the small sizes. While vacuum packing might be a bit annoying to open, it does protect the boards quite well in transport.

I immediately hated the addition of order number to the silk screen. While it seems engineer took care to place it underneath big components where it wouldn’t be visible, it’s kinda lottery if you need a really clean look (e.g. customer facing panel). If you note where their number can and cannot go on the silk screen in the comment field, I am reasonably sure they will cautious. However, such instructions are an extra step you need to remember and there is still some risk the number will be written right where you don’t want it.

Testing consisted, as usually, of hand-soldering the boards using Hakko D-888 soldering iron. As I use lead-free solder, the temperature was set to (rather low) 350 °C most of the time. All fine-pitch soldering was done by flooding all pins with solder and then wicking the extra away.

Illustration

First board I got to solder was TmpUsb. It’s a small board with all SMD components and white solder mask did look awesome initially. As I went soldering, I noticed a residue forming whenever I touched the solder mask. Not only this sticky residue was ugly to see but it also stuck to the tip. As this was actually my first board with white solder mask, I am not sure if this is something common for that color or PCBWay uses a formula that’s not particularly heat resistant. In any case, not ideal for soldering iron work.

Second board was for Binarko. Not only this board has a fine-pitch component (0.2 mm TQFP-48) alongside quite a few 0603 passives but it’s intended to be press-fit mounted into the case. The good news is that fit is as close to perfect as it gets. Use of v-groove also means there are no breakout tabs to get rid of resulting in much cleaner look. Since I forgot the HASL coating, getting microcontroller aligned was a bit of challenge. Interestingly, the black solder mask handled high temperature much better than it’s white counterpart making much less mess.

Fine-pitched PIC meant I had to use solder wick a lot and that has shown another issue - it’s really easy to lift a trace. When it happened the first time I played around a bit and noted this seems to be related to the temperature. While I didn’t seem to have issues dragging 300 °C tip, at 350 °C dragging soldering tip was much more destructive.

The final board was of UsbAmps. With 0.25 mm pitch TQFP-44 package, it was a bit easier to solder. It’s green solder mask did its work well and I haven’t noticed any heat-related residue. However, sensitivity of traces to heat/dragging remained.

To conclude: If you are still using lead solder or you do reflow soldering, there are no major downfalls considering this service. While it might not be as cheap as OSH Park or PCB:NG for smaller boards (especially considering shipping is not free), it does get better with larger sizes. If board color or a bit more involved customization is necessary (think filled or buried vias), suddenly this service makes real sense. Yes, you are paying a bit (sometime quite a bit) extra but you do get something not commonly found in prototype market.

Default selection of lead HASL finish and paying extra for lead-free or ENIG is a bit of nuisance for me as I prefer poisoning my self with more fancy stuff but it’s probably not a problem for many considering the love hobbyist market still shows toward the good old 60/40.

There is quite restrictive service offered by OSH Park and PCB:NG where selection of anything other than 2 or 4 layer process is simply not possible but you get your board quickly and at a reasonable price. On the other hand there is almost infinite possibility of customization PCBWay embraces but not without the cost.

Now, only if they would stop adding their numbers to boards…

PS: I was given boards for free by PCBWay. I don’t believe that affected my review, but I believe it’s fair to disclose. :)

[2018-08-16: It seems that PCBWay decided to add extra $3 charge if you use PayPal and you will only see it on the last page. Not nice… :(]

[2019-11-24: At this time PCBWay asks you to pay extra $3 if you want your boards without their custom number. Essentially you are paying for them not to deface your board. Nickel and diming…]

Average Number of Lighted Segments on 7-Segment Display

For a project I am doing with kids we decided to use 3-digit 7-segment display. As we intended to video the project (eventually), it was easy to make decision that we’re gonna drive every digit fully - no multiplexing. Not only that simplifies programming logic but it also makes display rock steady during video - no flickering here.

However, with so many pins to drive, I got a bit scared about maximum power consumption MCU would need to deal with. Assuming we go with 10 mA per LED, what is actually the maximum current we’ll need?

The easy answer is 7x10 = 70 mA per digit, bringing it to 210 mA total. Assuming all digits are fully on (we haven’t used decimal point - otherwise it would be 8) we would indeed need that much current. However, realistically, are we always going to have number 8 on display?

For this purpose, we’ve gone and created a table - how many segments we actually use for each digit:

DigitSegmentsCurrent
0660 mA
1220 mA
2550 mA
3550 mA
4440 mA
5550 mA
6660 mA
7330 mA
8770 mA
9660 mA
Median550 mA

As you can see, the most common result ended up 5 segments being lighted. This tells we actually can usually count on 150 mA of precious current needed.

But wait, since our project will count from 0 to 255, we actually won’t have all our digits on all of the time. We need to calculate how many segments we actually have lighted for numbers that we need. An eternity in Excel later we’ve found that median current needed (assuming random distribution) was 120 mA. Maximum was 180 mA (darn 208!) and minimum was 20 mA.

While 180 mA is not trivial, it is definitely less than 210 mA we originally had to count.