keskiviikko 29. heinäkuuta 2015

Pay attention to your linker scripts!


If you are like me with GCC, your project most likely is based on some example that you found somewhere in the interned, with everything including makefiles, system sources and some mysterious files you haven't checked out included.

All models of STM32F4 have total of 256kB of memory included; 192kB of this is "normal" bus-accessible (S)RAM and 64kB is special Core Coupled Memory (CCM) that is not bus-accessible. For now you only need to know that if peripheral needs to access memory, you can't use CCM for that data, but you can use it for data that is accessed by core alone (i.e. your software), and specifically if you want that part of program to be as fast as possible (as access to CCM has kind of priority).

Anyway, back to the story. So there I am, happily writing code and consuming available SRAM of the MCU until suddenly the project started crashing. It didn't take me long to figure out that the reason was stack corruption; program never returned from a certain function, which just happened to be one that went through few fairly big chunks of data. And I just done some major rewriting on that function.

I then spent some time going through that function, only to find out that there were no overflows there - program stayed strictly withing data bounds.

At this point I remembered linker scripts GCC uses to actually place all code, data and other pieces into the final binary. And so there, linker script I used was originally for some lesser chip if I remember correctly, with just 128kB of SRAM included. As the data used my program grew towards this, the extra space allocated to stack (typically placed at the top of memory, in this case starting at 128kB and "growing" down) eventually ran out when it and data (starting at zero and growing up) overlapped. Causing data to overwrite stack and to cause crash.

As you can guess, this wasn't first time I have dealt with this issue, and I have no reason to expect this to be last time either, which is why it didn't take longer to track out this problem.

So you might want to check your linker scripts too, especially if you are using example I linked in earlier post. File is stm32_flash.ld and beginning looks somewhat like this:

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of 128K RAM on AHB bus*/

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 192K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
}

_estack is is the problem; it sets stack pointer to 128kB. With STM32F4 you want it to be set at 0x20030000, i.e. 192kB so you have (almost) everything below for you program to use as it wishes (and just admit it, if you use LCD_TFT without external memory, it all is gone in a flash).
And although STM32F4 has 256kB of RAM, only 192kB is to be listed on RAM row here as general purpose memory; 64kB should appear as its own row as CCM. This script doesn't include 64kB of CCM memory but that is topic for another time.

And mandatory disclaimer; there is loads of magic going on in linker files I still haven't figured out so when I discuss this, so there is bound to be errors. All I can say as an excuse is that it seems to work for me -- for now.





sunnuntai 26. heinäkuuta 2015

Building an (unintentional) radio jammer


Long time ago, when I was just getting started with switch-mode DC-DC regulators my first design proved to be somewhat bad. Specifically, when I turned it on (without any load, mind you), FM radio two meters away lost reception.

Oops.

Cut power and radio reception returns. So, I managed to build an interference generator.  After some tweaking the design started behaving better, and next designs didn't produce practically any noise anymore (granted, most of the designs are fairly low-power and thus relatively easy to make quiet).

Now, what went wrong with the first one?

Being first experiment, I built it on breadboard, placing controller (with built-in switching transistor), diode and coil fairly far away from each other, thus unintentionally making a really big current loop that essentially works as an loop antenna. Typical switching regulators run at several hundred kiloherz, and with all harmonics that antenna will spew out a lot of wide-band noise, in this case enough to block out FM radio signal at around 90MHz.

Running that thing for extended periods might have earned a visit from military or civilian authorities, who in general are not very happy (putting it mildly) when you emit all kinds of garbage over all over RF spectrum.

Just placing components better reduced noise significantly, and properly designed PCB reduced emitted noise even further. Problem solved and lesson learned.



perjantai 10. heinäkuuta 2015

Should we use water cooling?


Just a short anecdote this time about cooling.

Long time ago I worked for a device manufacturer that dealt with fairly spesialized and as a result fairly expensive devices. Work was quite interesting and I also learned a lot about things that I wouldn't have in some other line of work ever touched as a software developer.

While some software people seem to have serious attitude issues with dealing with hardware, I have always preferred to be generalist myself. After all, when you know what is going on "under the hood" figuring out why the software doesn't (or does) work becomes much, much easier. And you don't have to ask about every detail from hardware folks, which they will appreciate too. As a result of this I eventually found myself dealing with, well, almost everything related to devices I worked with.

At some point we had some serious issues with main processor overheating due to insufficient air flow. As usual, this was after some changes (previous vendor EOL'd their processor and we had to source replacement with a very short notice). So here we are, handful of engineers trying to figure out how to solve this issue, with typical constraints of costs and schedules (after all, we had devices already sold that needed to be delivered soon).

So someone mentioned that we could build a liquid (water) cooling system to transfer heat from main processor to a location where it could be dissipated more easily.

At this point the person who was responsible for customer contact said (to paraphrase a bit); "I do not want to receive a call at 3 AM from customer saying that our device just wet itself."

Water cooling was never mentioned again.


sunnuntai 5. heinäkuuta 2015

Using serial (I2C) memories


Occasionally you will need some small-ish amount of non-volatile memory (that is, memory contents are retained even after power outage). If the amount of memory needed isn't huge, small EEPROMs with I2C interface might be useful - they're small (SO8 package being fairly typical, also DIP8 packages should be available if you are more comfortable with them), cheap (few euros in low quantities, depending mainly on memory size and type) and easy(-ish) to use - just two wires needed. For example Microchip's 24AA256, but there are numerous others too.

History of PROMs, EPROMs and EEPROMs - and especially development towards more user-friendly (err, in this context "developer-friendly" might be better phrase) is fairly interesting topic, but I'll skip that for now.

For now it's sufficient to know the drawbacks; EEPROMs have finite amount of write cycles - typical nowadays is around 1 million writes (that is, each byte of memory can be overwritten about 1 million times before becoming defuct). There are other pin-compatible memory types that don't have that issue, like FRAMs (link as an example) that can be written over practically infinitely but often those have been smaller (in memory density) and more expensive (5 eur instead of 50 cents, so not earth-shattering but still).
Another small drawback is that EEPROMs typically require that writes are done in 64-byte page boundaries, and after write there is a small delay (several milliseconds) before memory can be accessed again. This is really no issue and is easy to work around as long as you are aware of it. (FRAMs don't have these drawback either. Do I need to say I like FRAMs a lot, price aside?)

So, how you use one if these then? I borrowed below figure from datasheet of FM24C256 - 256kbit (32kByte) EEPROM from Fairchild. Mostly this because it just happened to be first datasheet I found from my library, but they're pretty much same so no matter.
This device is old - datasheet is from 2001 - so it seems a bit more limited; only 100k writes, but it also works with 3,3v and 5v voltages, which is nice. Some chips are less forgiving, so be careful when selecting part you wish to use. (let's just say that I wasn't very happy when I found out that somehow 5v EEPROM was placed on board that operated with 3,3v. It worked - mostly - due to low clock speed used, but exhibited some very mysterious failures on field...)

So what's needed to use these? SDA and SCL are data and clock lines, respectively. SDA is bi-directional line so optimally you'd want to use open drain output on MCU with pull-up resistor (4k7 is often recommended; internal pull-ups in MCU are often in 20k range which can work too, just don't try to use maximum clock speed then). If your MCU doesn't have open drain capability you'll need to fiddle between output and input mode; it's doable, but it's very easy to cause some glitches during switch that will interfere with communication.

VCC and VSS, supply and ground, of course are needed. WP, write protect, will prevent writes if pulled high. Most of the time it's easier to just connect it to GND (so no protection) unless you have good reason to do otherwise.

Address (Ax) modify the device address used to communicate with it, allowing you to connect multiple chips in same data lines in parallel. All chips must of course have different bus addresses, set by wiring these pins either high or low. For single-chip case it's easiest to wire them all to GND.

I2C bus is strictly master-slave bus, which means that the master (usually MCU) must initiate and control all data transfers. Some types of devices have interrupt lines that are used to signal MCU that they need to communicate with it ("hey I got a new result for you" or whatever). Memories just sit there until needed so they don't have that.

And that's it on hardware front.

I won't go deeply in software here, as writing your own I2C library might be tricky .. Nay, scratch that, it will be tricky. How thicky exactly depends on MCU and its pin capabilities. Instead I'll just point towards Wire library of Arduino that has I2C capabilities built in. I haven't used it myself so I don't have an example of it but there seem to be plenty of example around already.

The above example doesn't really explain the reasons why it does what it does so I'll explain a bit;

All communications with I2C device start with Start Bit and end with Stop Bit. These are mostly hidden behind beginTransmission() and endTransmission() functions.

After starting transmission, first byte written is the device address. Devices have often their own 7-bit address, possibly modified with Ax pins (in software I use often shifted 8-bit form where lowest bit of address is always zero; in case of above FM24C256 address is in binary 1010000 (7bit) or 0xA0 (8bit; shifted one left). In 8-bit form the lowest bit indicates read (1) or write (0) operation. When starting new operation you always specify write operation first to write data address:

After device address with write comes the data address. Depending on chip it can be one or two (or three) bytes. Number of bytes depends on size of memory; if 256 bytes or less, there is one address byte; for 512..65536 sizes two bytes and so on.

If you are writing to the memory, the data address is immediately followed by data itself, terminated by stop bit after which write operation starts on chip (remember that you must respect 64-byte pages and wait a bit for this to finish!).

If you are reading from memory it's a bit different though; after data address comes immediately new start bit (and no, there should not be stop bit first!) and device address again, only with Read operation set (bit 0 set to 1, so 0xA1).

Here the example linked above looks very wrong to me; it seems to be sending stop bit, and only then new start bit. Seems Very Wrong to me, but I don't know enough of internals of wire library to definitely call it an error. Even if it does stop bit first it actually might work on many chips, but technically that is violation of I2C spec so some chips might actually not work properly (for example if chip goes to low-power more after stop bit, forgetting previously set address).

After device address with Read operation is sent you can start reading data out. Read operation is nicer than write in the sense that you do not need to care about delays or page boundaries; if you want to, you can read contents of entire chip at one go. Or send NAK and stop bit to end transfer when you have received enough data (this part looks a bit wrong to me too, specifically the NAK sending part, but stop bit will stop operation anyway so that's less serious).

So, just to open up the read operation a bit, it should look a bit like this (with lots of error checking added, of course):

startBit();
writeByte(deviceAddress | 0); // 0xA0 - write operation
writeByte(byteAddressHigh);
writeByte(byteAddressLow);
startBit();
writeByte(deviceAddress | 1); // 0xA1 - read operation
while (bytesToRead--)
  { data[ofs++] = readByte();
  }
stopBit();

This doesn't check ACKs when writing bytes, nor does it send NAK properly at the end of transfer,  but I'm justifying this by claiming that leaving those out makes this example easier to understand.