Experiences in product certification

24.10.2019 9:45

Yesterday I was invited to give a presentation at a seminar on procedures for product certification. My former colleagues at the Department of Communication Systems at the Jožef Stefan Institute invited three companies to share their experiences in getting electronics products to European market. We discussed compliance with the CE mark, testing for safety and electromagnetic compatibility standards and various other approvals that are required before you can put mass-produced electronics on the shelves.

"Lessons learned" slide from the certification seminar.

In my presentation (slides are here) I've discussed my view of the two certification cycles I've participated in at Klevio during roughly the last year and a half. I did a short intro about the company and products and then listed what we chose to certify and how. I didn't do any general intro into the certification procedures and individual measurements. This in the end turned out just fine, because others did it better than I could. Most of the time I spent talking about purely practical lessons we learned on how to best prepare for the task. Looking back at it now, I feel like most of my advice boils down to having a good understanding of what is involved and not basing your expectations purely on certification lab's sales pitches.

I wasn't sure how much time I would have and what the interest of the audience was. Because of that I've put the details on debugging specific compliance problems into a separate part of the presentation. I've ended up doing that part of the talk as well. I discussed a problem we had with an ESD test temporarily disabling an audio codec IC and a problem with radiated emissions that took 3 months to debug and led to some pretty significant changes to one of the DC-DC converters.

I wanted to talk more about some interesting home-brew methods of estimating radiated emissions. I spent a lot of time researching and experimenting with them when I was debugging Klevio's compliance problems. In the end I realized that I could spend a whole talk just on that topic. It also turned out that none of those methods were actually useful in finding a solution, so it didn't make much sense to do more than just list them out. For more info on that, here's an article I found useful on making magnetic loop probes. The idea for the common-mode current probe based on a ferrite ring is from the Application Note AN045 by Richtek. The SDR-based method was my own idea based on my previous research on spectrum sensing and I might eventually do a longer write up on that in the future.

In the end, it was interesting to compare notes and what others have learned solving similar problems. I found that even though our EMI problem took longer to solve than others presented at the seminar, Klevio's experience didn't differ much in regard to timelines or certification lab practicalities. It seems almost everyone stumbles upon some problems during certifications, and while specific issues are unique, the biggest obstacle to finding a solution seems pretty universal: reproducing the problem outside of the certification lab.

Posted by Tomaž | Categories: Life | Comments »

Some more notes on Denon RCD-M41DAB

19.10.2019 16:13

Back in January I bought a new micro Hi-Fi after the transformer in my old one failed and I couldn't repair it. Since then I've been pretty happy using Denon RCD-M41DAB for listening to music and radio. I previously did some measurements on its output amplifier and found it meets its ratings in regards to power and distortion. In any case, that was more to satisfy my electrical engineering curiosity and I'm not particularly sensitive to reproduction quality. However after extended use two problems did pop up. Since I see this model is still being sold, I thought I might do a short follow up.

Denon RCD-M41DAB

The first thing is that when streaming audio to the Hi-Fi over Bluetooth there are about 2 seconds of lag. It looks as if there's some kind of an audio processing buffer inside the RCD-M41DAB, or it might simply be the choice of the codec used for wireless transfer. When listening to music the delay isn't that annoying. For example, when clicking "next track" or changing volume on the music player on the phone it takes about 2 seconds before you can actually hear the change. However this lag does make a Bluetooth-connected RCD-M41DAB completely useless as an audio output when playing videos or anything interactive. The lag happens on iOS and Android and I'm pretty sure this is not something related to my smartphone, since when I'm using Bluetooth-connected headphones, there is no discernible lag there.

The other annoyance is that the CD player has problems reading some CDs in my collection. Apparently it reads the table of contents fine, because the number of tracks is displayed correctly. However it has problems seeking to tracks. For example, one disc won't play any tracks at all and I can just hear the head continuously seeking. Another disc won't start playing the first track, but if I skip it manually, player will find and play the second one just fine. All such discs play normally in the CD drive in my desktop computer and other CD players I've tried. It still might be that the discs have some kind of an error in them or have bad reflectivity (all of the problematic ones are kind of niche releases I bought from Bandcamp), but other players apparently are able to work around it.

Posted by Tomaž | Categories: Life | Comments »

Specializing C++ templates on AVR pins

09.10.2019 19:56

AVR is a popular processor core for microcontrollers, probably most well known from the ATmega chips on the classic Arduino boards. There's an excellent, free software C and C++ compiler toolchain available for them, which also usually sits behind the well-known Arduino IDE. AVR is a Harvard architecture, which means that code and data memory (i.e. flash and RAM) have separate address spaces. On these small, 8-bit chips there is very little RAM space (can be as little as 128 bytes on ATtiny devices). On the other hand, the amount of flash is usually much less restrictive.

Sometimes you want the microcontroller to drive multiple instances of the same external peripheral. Let's say for example this peripheral uses one GPIO pin and two instances differ only in the pin they are connected to. This means that the code in the microcontroller for peripheral 1 differs from code for peripheral 2 only by the address of the special function register (SFR) it uses and a bit mask. A naive way of implementing this in clean C++ would be something like the following:

#include <avr/io.h>

class DIO {
	public:
		PORT_t * const port;
		const int pin_bm;

		DIO(PORT_t* port_, int pin_bm_) 
			: port(port_), pin_bm(pin_bm_) {}

		void setup(void) const
		{
			port->DIR |= pin_bm;
		}

		// other methods would go here ...
};

DIO dio1(&PORTB, PIN4_bm);
DIO dio2(&PORTB, PIN5_bm);

int main(void)
{
	dio1.setup();
	dio2.setup();

	// other code would go here ...

	while (1);
}

Note that I'm using the nice PORT_t struct and the bit mask constants provided by avr-gcc headers instead of messing around with register addresses and such.

Unfortunately, this isn't optimal. Even though the class members are declared as const, compiler stores them in RAM. This means that each instance of DIO class costs 4 precious bytes of RAM that, baring a stray cosmic particle, will never ever change during the course of the program.

Compiling the code above for the ATtiny416 with avr-gcc 5.4.0 confirms this:

Program Memory Usage  : 204 bytes   5,0 % Full
Data Memory Usage     : 8 bytes   3,1 % Full

We could store member variables as data in program space. This is possible by using some special instructions and variable attributes (PROGMEM, pgm_read_byte() etc.), but it's kind of ugly and doesn't play nicely with the C language. Another way is to actually have copies of the binary code for each peripheral, but only change the constants in each copy.

Obviously we don't want to have multiple copies in the source as well. In C the usual approach is to do something rude with C preprocessor and macros. Luckily, C++ offers a nicer way via templates. We can make a template class that takes non-type parameters and specialize that class on the register and bitmap it should use. Ideally, we would use something simple like this:

template <PORT_t* port, int pin_bm> class DIO {
	public:
		void setup(void) {
			port->DIR |= pin_bm;
		}
};

DIO<(&PORTB), PIN4_bm> dio1;
DIO<(&PORTB), PIN5_bm> dio1;

Sadly, this does not work. Behind the scenes, PORTB is a macro that casts a constant integer to the PORT_t struct via a pointer:

#define PORTB    (*(PORT_t *) 0x0620)

&PORTB evaluates back to an integral constant, however the compiler isn't smart enough to realize that and balks at seeing a structure and a pointer dereference:

main.cpp(8,7): error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
DIO<(&PORTB), PIN4_bm> dio1;
main.cpp(8,7): error: '*' cannot appear in a constant-expression
main.cpp(8,7): error: '&' cannot appear in a constant-expression

I found a thread on the AVR freaks forum which talks about this exact problem. The discussion meanders around the usefulness of software abstractions and the ability of hardware engineers to follow orders, but fails to find a solution that does not involve a soup of C preprocessor macros.

Fortunately, there does seem to exist an elegant C++ solution. The compiler's restriction can be worked around without resorting to preprocessor magic and while still using the convenient constants defined in the avr-gcc headers. We only need to evaluate the macro in another expression and do the cast to PORT_t inside the template:

template <int port_addr, int pin_bm> class DIO {
	public:
		void setup(void) {
			port()->DIR |= pin_bm;

		}

		static PORT_t* port()
		{
			return (PORT_t*) port_addr;
		}
};

static const int PORTB_ADDR = (int) &PORTB; 
DIO<PORTB_ADDR, PIN4_bm> dio1;
DIO<PORTB_ADDR, PIN5_bm> dio2;

This compiles and works fine with avr-gcc. As intended, it uses zero bytes of RAM. Interestingly enough, it also significantly reduces the code size, something I did not expect:

Program Memory Usage  : 108 bytes   2,6 % Full
Data Memory Usage     : 0 bytes   0,0 % Full

I'm not sure why the first version using member variables produces so much more code. A brief glance at the disassembly suggests that the extra code comes from the constructor. I would guess that using a code-free constructor with initializer lists should generate minimal extra instructions. Apparently that is not so here. In any case, I'm not complaining. I'm also sure that in a non-toy example, the template version will use more flash space. After all, It has to duplicate the code for all methods.

You might also wonder how Arduino does this with the digitalWrite() function and friends. They store a number of data tables in program memory and then have preprocessor macros to read them out using pgm_read_byte(). As I mentioned above, this works, but isn't nicely structured and it's pretty far from clean, object-oriented C++ programming.

Posted by Tomaž | Categories: Code | Comments »