Monday, September 24, 2012

Routing Differential Signals

Differential Signalling uses a pair of complementary signals to cary data, that is each wire is carrying the opposite signal, those signals are subtracted at the receiving end and the result is either 0 for noise or 1 for a valid signal. This technique is used with high speed signals to reduce crosstalk and EMI, examples of differential signalling include the twisted pairs inside Ethernet cables, HDMI and USB.
When routing differential signals, each pair should have the exact same length, because the signals are subtracted from each other so they need to arrive at the exact same time, otherwise it's useless, the same requirement applies to other high-speed non-deferential signals such as the signals of this DRAM, note the wiggly tracks on the PCB which are used to make the tracks of equal length:

Eagle 6 has some facilities to help with differential routing, if you use an older version you could still do it manually. For Eagle 6, first give both signals the same name with one ending with _N and the other with _P, so for example USB_N and USB_P, now when you start routing either one of the signals they both will be routed together as you can see: 

If you can't route both signals together, if there's not enough space, then one will be shorter than the other, and that's when you will need the wiggly traces, or formally the Meander function. After you finish routing the signals as usual, run the meander function with the length you wish to add to the shorter trace, then click on the trace and move the mouse it will start adding wiggles to the trace:

You can also use the length.ulp to find out the difference between the tracks before using the meander function:
run length USB*
Read more ...

Saturday, September 1, 2012

STM32F4 Discovery Quick Start Guide

I finally got my hands on an STM32F4 Discovery board, those are super cheap STM32F407 ARM Cortex-M4 boards from ST. The Cortex-M4 is perfect for DSP applications as it has SIMD and an FPU and it also runs at 168Mhz. As for the board, it seems okay given its price, I really hate the headers though and it only has a few components namely, an audio DAC, a chip microphone, an accelerometer and a few LEDs.


There's also an STLINK debugger on board, which means you don't need anything else to program and debug the chip. This is a quick tutorial on how to setup a toolchain and the necessary software.

Setting up a toolchain
First thing you need to do is build or install a toolchain, I couldn't get myself to use any of the toolchains/IDEs that work with the board, as they are all propriety and they all have some sort of limitations on the free editions.

There's always the CodeSourcry toolchain, while it works fine, however, it was not built with hard-float enabled only soft and softfp, which means you have a choice between slow FP and really slow FP emulation. I also tried the summon-arm-toolchain, but it seems to have the same problem no hard-float...

I then accidentally stumbled upon a gcc toolchain which seems to be maintained by ARM and it supports soft, softfp and hard-float, I think I will be using this toolchain for every ARM Cortex I have from now on, anyway, now that you have a working toolchain you can start compiling programs but you still need drivers.

Building the drivers library:
ST provides a package that includes drivers for all the peripherals, a DSP library, API documentation and lots of examples, if you look at the examples you will see that you have to copy all the *.c files you need to your project directory along with the linker script, system initialization and startup code, personally, I like to build a monolithic library and just link with that, so you can skip this step if you want:

First download and extract the STM32F4 DSP and standard peripherals Library then copy the startup and initialization code to the src directory:
$ cp Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c Libraries/STM32F4xx_StdPeriph_Driver/src/ 
$ cp Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/TrueSTUDIO/startup_stm32f4xx.s Libraries/STM32F4xx_StdPeriph_Driver/src/
Before you build the library, note that most of the drivers use a macro called assert_param, this macro is defined in stm32fxx_conf.h when you build a project you can enable/disable assertions, but since we're compiling this into a library the macro has to be seen at compile time, otherwise the compiler will just assume it's an external symbol and you'll get undefined references later, so you need to add this macro to the stm32f4xx header
#Libraries/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h
#ifdef  USE_FULL_ASSERT
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */
if you #define USE_FULL_ASSERT it will enable assertions or you can compile two libraries one for normal use and the other for debugging.

Finally, copy this Makefile into the src directory and compile the library, I'm assuming here that you have the new toolchain in the path:
#Libraries/STM32F4xx_StdPeriph_Driver/src/Makefile
LIB     =  libcm4.a 
SRCS    = $(wildcard *.c) 
LIB_OBJS= $(SRCS:.c=.o)
AFLAGS  = -mcpu=cortex-m4 -mthumb -mthumb-interwork -mlittle-endian -mfloat-abi=hard -mfpu=fpv4-sp-d16 
CFLAGS  = -mcpu=cortex-m4 -mthumb -mthumb-interwork -mlittle-endian -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O2 \
-I../inc -I../../CMSIS/Include/ -I../../CMSIS/Device/ST/STM32F4xx/Include/

CC = arm-none-eabi-gcc
AS = arm-none-eabi-as
LD = arm-none-eabi-ld
AR = arm-none-eabi-ar

all:: $(LIB)

$(LIB): $(LIB_OBJS)
    $(AR) -r $(LIB) $(LIB_OBJS)
    echo $(LIB_OBJ)

clean:
    $(RM) *.o *.a

.c.o :
    $(CC) $(CFLAGS) -c $<

.s.o :
    $(AS) $(AFLAGS) $< -o $@
When it's done, copy all the headers (including the CMSIS headers) and the library to somewhere like /opt or /usr/local. There's just one last piece of the puzzle missing, the linker script, you can find one in the many templates included with the library or use the one here, it also includes a Makefile template.

Blinky... Well not exactly!
To keep this simple, this example just lights up one of the LEDs on the board

#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
GPIO_InitTypeDef  GPIO_InitStructure;
int main(void)
{
    /* GPIOG Periph clock enable */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    /* Configure PD12 in output mode */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    /* Set PD12 high */
    GPIO_SetBits(GPIOD, GPIO_Pin_12);

    /* Do nothing */
    while (1) {
    }
}
Programming and debugging
There's no "official" stlink utility for Linux, so I used this one instead:
$ git clone https://github.com/texane/stlink.git
$ cd stlink
$ ./autogen.sh
$ ./configure
$ make
This will build two binaries, the st-util a gdb server, and st-flash which is supposed to let you write the binary to flash but it doesn't really work! anyway, the project includes a udev rules, you should use that instead of sudo each time:
$ cp 49-stlinkv2.rules /etc/udev/rules.d/
$ udevadm control --reload-rules

Now just run st-util and in another terminal run gdb:
$ arm-none-eabi-gdb blinky.elf
$ tar ext :4242
$ load
$ continue

Notes
Take care not to remap the USB pins used by the STLINK debugger, if you do so the debugger won't be able to talk to the chip and you could actually brick the board :) if you do find yourself in this situation, pull the BOOT0 pin high, there's a VDD pin right next to it so just place a jumper on those pins, when you do that, the board will run the bootloader instead and you can erase the entire flash.

The library is configured for a 25MHz oscillator and the Discovery board has an 8MHz oscillator, you will need to change two values to fix that, the HSE_VALUE in stm32f4xx.h set that to (8000000) and the PLL_M in system_stm32f4xx.c to (8).
Read more ...