keskiviikko 29. huhtikuuta 2015

STM32F4 & USB HID - headbashing time

I briefly mentioned working with USB with little-to-none success on STM32F4 discovery board when I last wrote about this board. I wanted to use USB HS pins/core in device mode only (MicroC connector on Discovery board) but aside the hellishly ugly library there seemed to be nothing available.

As I don't have enough knowledge of USB on protocol level I finally relented and adapted the relevant parts of provided libraries (totalling 25 different files and ~250k of source) as a part of my own library. Except that it didn't work - I only got session request, reset, few SOF frames and eventual failure - Windows just couldn't identify my board at all.

I've spent more hours on figuring this out than I'd like to admit, and finally I found the problem. To minimize power usage I have most of the heavy lifting initiated by interrupts (external I/O, timers etc), then actual work is done in main and then processor is put in idle mode ("wfi" instruction - not even proper sleep mode, just "no-op until interrupt is received"). During typical use this cut the processor power usage to about third of normal, which is great indeed. Now side effect of this is that it apparently interferes with USB core somehow, preventing it from working properly. Removing this single instruction made the USB work immediately. D'oh! ...And I've been trying to figure out what is wrong with USB code. (insert bash-head-to-table moment here)

Since I'm using USB mainly to configure the board I then just changed system so that sleep instruction is only allowed when USB is not connected. Momentary added power consumption (of whopping few tens of milliamps!) is no problem there. However I somewhat suspect that USB is not the only place that will cause problems with power saving but we'll see...

Sources will be following after some cleanup.

Edit, the next day: Less than hour of writing above, after some trivial modifications that should affect nothing (mostly debug messages to figure out HID report contents) it stopped working again. I spent some time trying to figure out what I did, but day was already nearing end so I moved on, suspecting I knew the reason. (and as unrelated thing, the entire night my head was filled with orbital trajectories. Three words: Kerbal Space Program)

This morning I come in, turn computer on and plug discovery board in and it works. Again.

This isn't the first time this stupid pre-build Acer desktop fails with USB (first and last time I buy such, from now on it's selected components only and possibly assembly in shop). It has had habit of occasionally refusing to work with my (older, with different processor) boards until it's rebooted, and looks like that behavior continues with discovery boards too.

I have said this before it is worth repeating: Never, ever use USB for anything that needs to be reliable. USB certainly isn't. Even with better quality computers.


sunnuntai 12. huhtikuuta 2015

C startup code

In post about const/non-const data I mentioned that non-const data must be initialized on system startup. So when and where does this happen exactly?

The exact startup process of course varies from MCU to MCU and from compiler to another, but since I've been working with STM32F4 I'll use it's code as example. I'm not exactly sure what example code I got but they're mostly similar so it doesn't really matter that much, this should apply to most examples floating around the net.

To start from very beginning, when electrons start flowing on your board... No, scratch that, let's start from slightly level.

When processor is powered on, it is typically held in reset until things get stable enough to start actual startup process. In case of STM32F4 there is additional logic on board that immediately following power-on samples BOOT0 and BOOT1 pins and selects proper boot code from there; internal ROM, RAM or Flash. I'll skip ROM and RAM options here and assume that Flash is used as boot device, so MCU starts executing code at specific address that is often called reset vector. Different processors have different address, but usually it's either very first or very last instruction in memory space.

Now, C has no concept of such address, so where does that come in? If you take a look at source package (and makefile), you'll most likely find file named startup_stm32f4xx.s. In beginning of it you will find reference to Reset_Handler:

  .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:  

/* Copy the data segment initializers from flash to SRAM */
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4
    
LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
 
 /* Zero fill the bss segment. */ 
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4
    
LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss

/* Call the clock system intitialization function.*/
  bl  SystemInit   
/* Call static constructors */
  bl __libc_init_array  
/* Call the application's entry point.*/
  bl  main


There are handy commends already there telling you what is going on. All the basic stuff that needs to be done is here; first copying init data (that's your non-const data) from Flash to RAM, then clearing rest of used RAM. (and here is where my version of init code differs, I like that after short power outage I can resume where where I left off so neither init data copy nor clear sections exist; I recommend against this unless you know Damn Well what you are doing as this can result some really obscure bugs when things aren't handled properly).

After these there are three branches; to SystemInit, __libc_init_array and finally to your main (since main in case of embedded code isn't really expected to return I stop here).

SystemInit then can be found in system_stm32f4xx.c. In Discovery board examples this sets oscillators and clocks properly and initializes external memory controller. I won't go in details of those as STM32F4's clock setup is fairly complex and not really of interest unless you are designing your own board with specific criteria. For now it's sufficient to say that this is where you will find them if you are interested.

Then there is call to __libc_init_array, which... well, I haven't studied libc that much so I can't really say what it exactly does, but knowing that it does basic setup of stardard library is most of the time sufficient.

And finally  the main(). And this is the point where your code takes over.

Like I said, the low-level init process is specific for each MCU and board is different, but the basic structure remains. This same applies even to Arduino, although there the lower level complexity is hidden even more carefully by hiding the main() from sketches. For most simple applications these details are unnecessary, but it's always handy to know about them (or at least know of them) anyway.




tiistai 7. huhtikuuta 2015

Let the smoke out

My desk is often a mess. Especially when testing new boards, with wires going everywhere and all boards on the open. I've been fairly lucky, no major mishaps for a long time now, but it was bound to happen soon...


This was taken with my Jolla phone through x10 stereo microscope I use for most of fine work. Photo turned out surprisingly good, although finding the exact location where picture is best(ish) took some fine balancing.

See that dark weird thing circled with red? That's a crater. When I connected power there was pfft sound and a bit of smoke. Shit.

So, I've got two tri-color LEDs on the board for debugging/signalling purposes. I highly recommend you to place some LEDs on your prototype boards too, having nice slow "I'm alive" blink tell you that main() is, well, looping makes debugging way easier when nothing else works.

For now however I had connected some wires directly to pins driving one LED (well, to series resistor, off to left from this view), other end connected to scope. These were for timing tests, I wanted to see how quick power ramps I can generate. Turns out that fairly fast - output transistor isn't best (gotta try NFET instead of NPN I had to spare for first tests) but I still can drive output up very quickly (at 200mA) and even down relatively quickly, at rate of "just" 5v/ms. Perfect for simulating short power outages that are typical in cars (anyone want one? ;)

For a moment I had to do something else, disconnected this board and later connected it back. Pushing things around on table moved the board and one of the test wires moved to meet large-ish  thermal fuse that was directly connected to incoming 12v. And then damage happened. And that nice LED telling me software is running (that being the one LED I hadn't connected wires to) isn't flashing any more.

Ouch.

Taking that out and putting new one sucks. And see those wires at upper part? These are three address pins I stupidly forgot to route in first board version so I needed to manually solder them to processor pins. Processor itself is relatively easy to solder in (despite 0.5mm pin pitch), but those wires are more annoying, being annoyingly large and close together. Markings in wires (1, 2 and 3 dots, number 2 is not visible and third dot on 3 is also out of view) mark the pin position where I need to solder them back in.


Unfortunately I have no choice but to start repairing...