Random thoughts about software, hardware and electronics. And other things too...
torstai 19. toukokuuta 2016
Cars and electrical faults
My car (Skoda Octavia Combi) is now 5 years old and first electrical problems started appearing. Oh joy. In this case, it was the trunk opening button that went faulty. The lock there is fully electronic, the button is literally just that; button that signals the computer that someone wants to open the trunk. Opening itself is controlled by the computer.
So few months ago the button started acting up, not opening the trunk occasionally. Fortunately the car key (remote) has specific "open trunk" button that opens the trunk anyway so this wasn't acute problem as it was pretty cold out yet - working with cars in freezing conditions isn't exactly the most fun one can have outside. And remote button working indicated that it's the trunk button that failed, not the lock itself.
A few weeks ago now the trunk button stopped working completely, and since it had gotten warmer outside it was time to tear the trunk lid open and fix the problem. (or I could have taken the car to the shop, they would've fixed it and charged me something 200€..)
So I opened it. Unfortunately I didn't take any photos so text will have to suffice.
I found out that the button is a separate module, on first glance quite nice and tight-looking (IP44 or so on first estimate) package. The connector (two wires) however had oxidized pretty badly. So as a first resort I cleaned it up and put it back in. And it worked!
After a week I put the inner covering of the trunk lid back in.
And the damn think failed again on the very next day. Fuck.
At this point this would get very interesting with photos, but alas, still none...
On round 1 I was hoping to avoid any major rework of systems, but deep down I knew that oxidization usually can't be fixed that easily, which is the reason I kept the inner cover off for that long - I expected it to fail. So first thing was to scrap the oxidized connector completely and solder the wires directly to the button module.
No good. Button's completely dead.
So a closer examination of the button module itself was in order. And on closer examination I found out that the button isn't even close to IP rated as a module. Don't get me wrong - when properly placed in the trunk lid it still is protected from occasional splashes by its rubbery covering, but some moisture can still get in when in car wash or such. And when the button normally is enclosed within the lid the moisture will stay in and eventually start doing some damage (as a side note, I wonder if a small heater, in range of 2-5 watts, active when driving, would help there...)
After some careful destructive deconstruction I found that the button inside (fairly bulky) module is actually tiny, 15x5x10mm rectangle-shaped microswitch. I didn't have anything close to it handy, so I pretty much destroyed the plastic enclosure and replaced it with round one with about 1cm diameter I found from my dad's junk bin (main reason for not having photos; I wasn't at home.) New button had to be glued and soldered in, of course. New button unfortunately isn't tactile so there is no "click" anymore, but who cares, at least it works! (for now, new button isn't any better protected against moisture)
Now, assuming the car still is in working condition years from now, I can only imagine some sorry future owner opening the trunk to figure out why button doesn't work and finding the -- umm -- innovative ways I fixed this. And I imagine that there will be some chosen expletives spoken that day...
sunnuntai 15. toukokuuta 2016
Geiger counter from the 50s
Some time ago I managed to get my hands on an old geiger counter. For no reason, really, I just happen to like vintage tech stuff like this. Although this unit had been modified by someone at some point in the most unpleasant fashion.
The manufacturer is Atomic Research Corporation, and readout says "Model 521" on it. I can't find any absolutely certain information about this (the 521 models I see in net are slightly different, with hand-held radiation sensor; this one has the sensor fixed at the bottom), but so far the best guess places manufacturing date to mid-50s.
Does it work? I don't know. It takes few C-size (I think; might be D-size too) batteries which are no problem, but it additionally needs two 65-volt batteries of type I've never seen that - really - aren't exactly commonly available these days. This is where the modification done by someone comes in play; they attached a box full of 9v batteries in series to generate the high voltage needed by tubes. I consider that modification nothing but crude and vulgar and completely out of spirit of this vintage design and immediately removed it, without even testing if this thing works first.
I've been thinking about building a boost or flyback transformer to boost lower voltage to more suitable levels, but I don't really have experience building such things (at this high boost factor, I've done boost converters with voltage factor of about 2 before) and so far all my attempts have been miserable failures.
Here is the insides. The large open area is for two 65v-batteries and on right you can see slots for two smaller batteries. Electronics on top is constructed in a fashion fairly typical of the era; everything just wired directly together. Not really most elegant or easiest to work with, but gets the job done. This isn't nuclear scien... oh, wait, it kinda is. Nevermind. Not complicated science though.
The sensing element (I think there is more accurate name for it but I'm too lazy to check now) is fixed on bottom, and there are few openings in the case for it. The crudely cut slots you can see there are for the wires to the battery box attachment. Ugly, ugly.
It even comes with full schematics! Unfortunately they are glued inside of the outer cover and have been somewhat destroyed by repeated removals and insertions of (fairly tight-fitting) insides so I can't get much better picture of them than this. Not very complicated thing as you can see. Unfortunately I have absolutely no knowledge about tubes and their operation so I can't even try to decipher how this is supposed to work. Maybe when (okay: if) I ever get my boost converted design working properly I can try to figure out more about the operation by trial and error, but for now it has to wait, just sitting there on my shelf on display.
torstai 12. toukokuuta 2016
Android 6 permission stupidity
Rant ahead.
Some time ago I got a call from a colleague who had problems trying to connect to our meters with a new phone; our app couldn't find any bluetooth devices anymore. During the discussion it became apparent that the new phone had Android 6 installed, first one I've encountered so far. So I started to suspect that something in new Android version might have changed.
Phone in question was across the country at the time so I had to resort to some internet searches by intuition instead. After a short search session it became apparent that to be able to find bluetooth devices apps now need (in addition to old BLUETOOTH_ADMIN permission that explicitly deals with bluetooth discovery) also ACCESS_COARSE_LOCATION permission. Now, this is just about the stu-- no, this is the stupidest thing I've heard this week. Hell, even this month.. Actually, make that two months, as this text has been sitting in my publishing queue for some time now.
Remember, like I just said there is already permission for Bluetooth discovery and pairing, the BLUETOOTH_ADMIN, so for limiting damage a rogue app could do adding this other does absolutely nothing.
The reasoning for this change apparently is that mapping out bluetooth devices nearby would allow rough geolocation, similarly to WiFi. Which is pretty stupid already - unlike WiFi, Bluetooth has very short range - 10 metres or so (granted, I've heard that newer (low energy) versions have improved the range somewhat - no idea how much really). Unless the surroundings is pretty much littered with searchable unmoving devices the search will be pretty much useless.
Most bluetooth devices are actually mobile and most of the time not searchable, so they aren't very useful for location purposes. You might argue that this is a way to inform user about this obscure positioning method, but I don't buy that argument.
But wait, it gets even worse. My app has absolutely no need for location information, ever. I haven't used any Android 6 devices (or even emulators) myself so I haven't seen the new permission system in action, but based on what I've read it asks for permission when it's used (although it seems that for apps targeting older APIs (read: vast majority of them, mine included) the system works as before - permissions are given on install time.) As far as excuses for a features go, this is a pretty sorry one.
But for new apps -- I imagine that when user clicks "Find BT device" button a permission request for location pops up! User goes "WTF?" and clicks deny. And nothing works. And user has no frickin' idea what just happened (after all, he denied location, not bluetooth!), he only sees that the app can't find any any devices. Yes, the permission can be granted later (via app permissions menu), but first user needs to know that this menu exist and then also why that permission is needed, and I'd imagine that explaining why bluetooth need location permission won't be accepted easily by quite a few end users...
On the other hand, now a rogue Bluetooth-using app (that shouldn't need the information about location) can silently gather information about its (coarse) location any time it wants! After all, now pairing a bluetooth device within app explicitly requires location, so essentially this change made permissions worse. If there were an option to "allow these permissions for 10 minutes" (for the duration of pairing) this just might be acceptable, but it seems to be all-or-nothing (not many people go around changing app permissions after pairing, so that app permission menu doesn't count.)
One use case that might apply would be in-shop beacons (think of targeted advertising - when you're at product aisle you start getting ads for kumquats and whatnot) , but with all the other downsides listed here the new permission is much a failure with more drawbacks than help.
Overall this change is stupid, stupid, stupid. Android permissions have been way too vague for a long time, and this change only waters down existing permissions by shoving even more irrelevant crud to their coverage. What the hell were you thinking there, Google?
maanantai 9. toukokuuta 2016
Soldering a DFN package
Many of the newest and coolest chips come in annoyingly small and difficult to (manually) solder packages, like DFN. For production runs this is no problem, but I typically prefer to assemble first boards manually - this allows me to catch stupid mistakes (like incorrectly set up regulator) without blowing up too many expensive components in the process. Manual assembly of course takes time, but at least you aren't paying 200€ each for five boards you immediately find out don't work at all due some stupid mistake that should've been caught at layout.
But back to DFN assembly. The following pictures are really crappy - I have to take them through my microscope's eyepiece, making it very difficult to focus them properly. I'll really need to get a microscope with camera one of these days...
So here is the situation. Chip in question is simple USB/serial converter. This one chip I salvaged from older board - finding problems is much easier when you have known-good (and properly programmed) chip already, allowing you to focus on problems on board itself. And there were a few such problems, as expected. But those are not today's topic either.
Like I said in previous post, I prefer to use leaded solder for difficult jobs. This counts as one. So I got a small syringe of solder paste from local electronics shop (note; you absolutely must keep solder paste in cold, like fridge - it will be quickly ruined in room temperature!) and apply some on the board. The amount seen below is actually more than enough, about half of that would be sufficient. Adding to much paste will only result in shorts under the chip.
Then place the chip on top of the solder paste. Positioning doesn't need to be exact; surface tension will move it to correct place during soldering. I have cheap Chinese hot air soldering station with adjustable temperature and air flow. I typically set temperature to slightly below 300C and air flow to low value (about 20 on scale 0-100). Even then the air flow will easily blow chip away, so start from a longer distance and gradually get closer with the nozzle. Oh, and you absolutely want to have a microscope (or at least some magnification) for this - seeing what is going on makes the entire process so much easier.
After solder starts to melt, the chip will stay put and reflow properly. Don't try to hold the chip with a tool - this prevents it from settling properly during the reflow process.
No picture of the process, sorry (too busy soldering). Just the end result. Not the prettiest solder job, and needs some cleaning, but it works - which is all I need from a prototype board.
perjantai 6. toukokuuta 2016
Wanted: Sharp multimeter probes
It seems that these days getting usable (read: thin and sharp) multimeter probes is almost impossible. All probe points are think, dull and sometimes insulated almost all the way to the tip. Apparently due to some stupid safety regulation or whatnot. If you only want to measure large objects this is (kinda) fine, but when doing prototyping or rework I often want to check shorts and connection of MCU or connector pins - and these days those tend to be with quite a fine pitch - 0.5mm or even smaller. For that these thick probes are completely useless.
But as usual, need is the driving force of invention: Thumb tacks to the rescue!
A bit unwieldy, yes, but at least now I can measure MCU pins without shorting them to neighboring ones.
Yet still, I'd still take proper long, thin and sharp meter probes any day. If I just could fine them somewhere...
sunnuntai 1. toukokuuta 2016
Sometimes tools can be surprisingly expensive
Sometimes people being salaried (note; in Finland this means that monthly salary and work time are fixed; overtime is still compensated) seems to seriously twist the peoples' idea of how expensive time spent can be.
For example, at one time an engineer wanted to buy a small torque wrench for some reason but it definitely was work-related and justified (in my opinion at least). I don't remember the exact cost of the tool but it wasn't particularly high - something like 20eur or so.
So this engineer asked his boss for this wrench. Boss said sure, but he'd need to ask his boss in turn. So he did.
As a result there was a 30-minute ad-hoc meeting (not including above preliminaries) about whether this wrench was useful enough to be bought. Attended by three highly paid engineers (me included) and two even higher paid managers. If this had been billable meeting the cost would have been in neighborhood of 300€ (just a quick estimate, I don't know the exact costs involved).
That must have been one of the most expensive wrenches that were never bought.
torstai 28. huhtikuuta 2016
STM32F4xx display code, refined version
Okay, ages ago I promised improved version of the original code for using STM32F429/STM32F439 LCD controller. Like previously mentioned same code works with newer STM32F469/STM32F479 too and most likely on almost all other chips on the family.
So here it goes. Sorry this isn't a drop-in project, taking all code out from my libraries (with all supporting code included) would take a bit too much of time, so you'll have to settle for generalized version, you'll need to adapt it to your specific display; mostly with defines listed at the top. Note that I use the pin mappings for the 429 discovery board, but display size does not match it so this won't work there directly. I left also out the SPI initialization as most display modules don't either have it or use very different settings.
Main change here (compared to previous one) are the defines; now they are defined in same way they typically are in datasheets so adaptation for different displays should be easier.
#include "stm32f4_discovery.h" #include "stm32f4xx_conf.h" // Display module timing configuration. These are different for // each display module; see module's datasheet for details.
#define DISP_WIDTH (320) /* active area width */ #define DISP_HEIGHT (240) /* active area height */ #define DISP_HSYNC_W (3) /* HSYNC width, in clocks */ #define DISP_VSYNC_H (3) /* VSYNC height, in rows */ #define DISP_HORIZ_BACKPORCH (65) /* Horizontal backporch, excluding HSYNC. Data starts after backporch. */ #define DISP_VERT_BACKPORCH (15) /* Vertical backporch, excluding VSYNC */ #define DISP_HORIZ_FRONTPORCH (22) /* Horizontal frontporch (after data on scanline) */ #define DISP_VERT_FRONTPORCH (42) /* Vertical frontporch */
unsigned char dispFrameBuff[DISP_WIDTH * DISP_HEIGHT];
#define LTDC_CFG_PINCOUNT 22
const int ltdc_lcd_pins[] =
// port AF pin. For some reason LCD has pins in both AF9 (alternate function 9)
// and AF14 slots.
{ PORTA | 3 | (14<<8), PORTA | 4 | (14<<8), PORTA | 6 | (14<<8),
PORTA | 11 | (14<<8), PORTA | 12 | (14<<8),
PORTB | 0 | (9<<8), PORTB | 1 | (9<<8),
PORTB | 8 | (14<<8), PORTB | 9 | (14<<8), PORTB | 10 | (14<<8),
PORTB | 11 | (14<<8),PORTC | 6 | (14<<8), PORTC | 7 | (14<<8),
PORTC | 10 | (14<<8),PORTD | 3 | (14<<8), PORTD | 6 | (14<<8),
PORTF | 10 | (14<<8), PORTG | 6 | (14<<8), PORTG | 7 | (14<<8),
PORTG | 10 | (9<<8), PORTG | 11 | (14<<8), PORTG | 12 | (9<<8) };
// some pins for easier probing;
// PC2 = CS
// PD13 = CMD/DATA
// PF9 = SDA
// PF7 = CLK
// PA4 = vsync (active low)
// PC6 = hsync (active low)
// PF10 = DE (active high)
// PG7 = dotclk
void dispInit()
{
// Enable clocks for GPIOs (pins) and devices. Last is DMA2D (Chrom-Art)
unsigned int i;
RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN |
RCC_AHB1ENR_GPIODEN | RCC_AHB1ENR_GPIOFEN | RCC_AHB1ENR_GPIOGEN | (1<<23) );
// Initialize LTDC pins in AF mode.
for (i = 0; i < LTDC_CFG_PINCOUNT; ++i)
{ unsigned int af = (ltdc_lcd_pins[i] >> 8) & 15;
// Set pin (3) in (PORTA) to (AF14) through GPIO controller
ioPinSetAF(ltdc_lcd_pins[i] & (~0xff00), 0, af);
} dispLTDCInit(); }
/* -----------------------------------------------------------------
* Init internal LTDC controller.
*/
void dispLTDCInit()
{
RCC->APB2ENR |= (1 << 26); // Enable LTDC clock.
/* Clock setup: LCD pixel clock = HSE / M * N / R / DIVR (see your
* display module specs to see what it should be) Most discovery board
* templates has HSE 8MHz and M at 8. This gives PLL input clock of 1 MHz.
*
* Then we use values:
* N = 192, R = 4, DIVR = 8 so output freq is 6 MHz
*
* PLL has also Q divider, for SAI (audio), but we do not use it so it
* it just set to something here.
*/
#define PLLSAI_N 192 /* Multiplier for input clock (which is HSE/M) */
#define PLLSAI_R 4 /* Divider 1; /4 */
#define PLLSAI_DIVR 2 /* Divider 2; 0=/2, 1=/4, 2=/8, 3=/16 */
RCC->PLLSAICFGR = (PLLSAI_R << 28) | (4 << 24) | (PLLSAI_N << 6);
RCC->DCKCFGR = (RCC->DCKCFGR & (~0x30000)) | (PLLSAI_DIVR << 16);
RCC->CR |= (1<<28); // enable PLL SAI and wait until it is ready
unsigned int n = 0;
while (!(RCC->CR & (1<<29)))
{ if (++n > 2000000)
{ serialWrite((unsigned char*)"pllsai fail\r"); // PLL clock did not start; failure somewhere.
return;
}
}
LTDC->GCR = 0; // disable LCD-TFT controller for re-initialisation
// Set up LCD timing variables. At first these values may seem like black magic
// but trust me, if you spend some time with display datasheet they become clear eventually.
// Or just trust me on these :)
LTDC->SSCR = ((DISP_HSYNC_W-1) << 16) | (DISP_VSYNC_H-1);
LTDC->BPCR = ((DISP_HSYNC_W+DISP_HORIZ_BACKPORCH-1) << 16) | ((DISP_VSYNC_H+DISP_VERT_BACKPORCH-1));
LTDC->AWCR = ((DISP_HSYNC_W+DISP_HORIZ_BACKPORCH+DISP_WIDTH) << 16) | (DISP_VSYNC_H+DISP_VERT_BACKPORCH+DISP_HEIGHT-1);
LTDC->TWCR = ((DISP_HSYNC_W+DISP_HORIZ_BACKPORCH+DISP_WIDTH+DISP_HORIZ_FRONTPORCH-1) << 16) | (DISP_VSYNC_H+DISP_VERT_BACKPORCH+DISP_HEIGHT+DISP_VERT_FRONTPORCH-1);
// background color (23:0 : R8G8B8). Purple-ish for testing.
LTDC->BCCR = 0xff00ff;
// Enable layer 1. We have 320x240 (76800) bytes in internal memory in L8 (with color LUT) mode
// Discovery board has also external memory that could be used for bigger
// display sizes, color depths or multiple display pages.
// From the display controller's point of view the image is 240x320 pixels, portrait mode.
// Entire area configured for layer 1.
LTDC->L1CR = 0; // disable for reprogramming.
LTDC->L1WHPCR = ((DISP_WIDTH+DISP_HSYNC_W+DISP_HORIZ_BACKPORCH+2) << 16) | (DISP_HSYNC_W+DISP_HORIZ_BACKPORCH);
LTDC->L1WVPCR = ((DISP_HEIGHT+DISP_VSYNC_H+DISP_VERT_BACKPORCH) << 16) | (DISP_VSYNC_H+DISP_VERT_BACKPORCH);
LTDC->L1PFCR = 5; // Pixel format: 5=L8. 2= RGB565
LTDC->L1CFBAR = (unsigned int)dispFrameBuff;
LTDC->L1CFBLR = ((DISP_WIDTH) << 16) | (DISP_WIDTH+6); // hi:line pitch in bytes; lo:(width_in_bytes+3). Note that L8 has just 1 byte per pixels!
// docs say that CFBLR loword should be W+3 but that doesn't seem to work, at least in L8 mode. So +6 instead.
LTDC->L1CFBLNR = DISP_HEIGHT;
LTDC->L1CKCR = 0x000; // Black data is transparent, allowing background to be shown.
// Set color look-up tables. Here we fill only first 128 entries with black-to-full color values.
unsigned int i;
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+0) << 24) + (i*0x101010); // 0-15: 16 greyscales
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+16) << 24) + (i*0x100000); // 16-31: 16 reds
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+32) << 24) + (i*0x001000); // 32-47: 16 greens
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+48) << 24) + (i*0x000010); // 48-63: 16 blues
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+64) << 24) + (i*0x101000); // 64-79: 16 yellows
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+80) << 24) + (i*0x100010); // 80-95: 16 purples
for (i = 0; i < 16; ++i)
LTDC->L1CLUTWR = ((i+96) << 24) + (i*0x001010); // 96-112: 16 cyans
LTDC->L1CR = 0x13; // Enable layer1 (0x01), CLUT (0x10) and color key (0x02)
// Fill display buffer with some data.
for (i = 0; i < sizeof(dispFrameBuff); ++i)
{ dispFrameBuff[i] = 7;
}
// White borders
for (i = 0; i < DISP_WIDTH; ++i)
{
dispFrameBuff[i] = 15;
dispFrameBuff[i+DISP_WIDTH] = 15;
dispFrameBuff[i+(DISP_HEIGHT-1)*DISP_WIDTH] = 15;
dispFrameBuff[i+(DISP_HEIGHT-2)*DISP_WIDTH] = 15;
}
for (i = 0; i < DISP_HEIGHT; ++i)
{
dispFrameBuff[i*DISP_WIDTH] = 15;
dispFrameBuff[i*DISP_WIDTH+1] = 15;
dispFrameBuff[i*DISP_WIDTH+DISP_WIDTH-1] = 15;
dispFrameBuff[i*DISP_WIDTH+DISP_WIDTH-2] = 15;
}
// Color square at upper left corner
for (i = 0; i < 16; ++i)
{ unsigned int j;
for (j = 0; j < 16; ++j)
{ dispFrameBuff[DISP_WIDTH*2+2+ i*DISP_WIDTH*2+j*2] = i+j*16;
dispFrameBuff[DISP_WIDTH*2+2+ i*DISP_WIDTH*2+1+j*2] = i+j*16;
dispFrameBuff[DISP_WIDTH*3+2+ i*DISP_WIDTH*2+j*2] = i+j*16;
dispFrameBuff[DISP_WIDTH*3+2+ i*DISP_WIDTH*2+1+j*2] = i+j*16;
}
}
// Reload shadow registers to active in SRCR
LTDC->SRCR = 1; // 1=reload immeiately; 2=reload on vertical blank
// Enable controller.
LTDC->GCR = 1;
}
So there.
Tilaa:
Blogitekstit (Atom)








