Hand soldering small SMDs (LGA 28)

I am gearing up for another round of deployment on badgers and one of the big things to do was to stick down some LGA-28 chips. Land Grid Array is just a fancy way of saying that the chip manufacturers did their best to make things as difficult as they could for someone like me doing a small run (10 or 20 boards). LGAs and BGAs are great for miniaturization however, if you happen to have a pick-and-place machine knocking around. These chips are 5 mm x 5mm and have 28 pins that live on the underside of the chip, so you can’t just do drag soldering with an iron.  After some trawling through the interwebs, I found out that LGAs could indeed be soldered by hand using hot air. I almost went out and bought a stereo microscope, but I thought I would first try the African way (maak ‘n plan as they say in South Africa…), which is to make do with what you have.

The first couple of chips I tried to solder down were unmitigated disasters – charred boards, shorted pins, open pins – you name it, it happened. After a few more tries, I got the hang of it, so I thought I would put this up so that other folks need not be scared of LGAs. In fact, once you have it sorted, it is probably quicker to solder a batch of LGAs as opposed to PDIPs.

What you need:

  1. Solder paste (you could use wire, but I think paste is fantastic stuff)
  2. Fine tip iron (I have a “Tenma” which is Farnell’s brand which looks very much like an Aoyue clone)
  3. Hot air with a relatively small nozzle (again, I just use a Tenma SMD Rework Station)
  4. Flux (I use a no-clean pen)
  5. Panavise (really useful to hold things steady and it also brings things up to a comfortable working height)
  6. Wick (for mopping up all mistakes!)
  7. Tweezers (optional really, you could just use a small screwdriver to position chips)
  8. Patience

Note I don’t have any sort of magnification, but I guess it depends on your eyes – maybe a loupe would be helpful to see the end result properly.

The first thing to do is to put a (thin!) line of paste on the pads. The proper way to do it is with a stencil, but you can do just fine with a small amount of paste.

Next, tin the pads with your iron. The idea is to get a small amount of solder to sit nicely on each pad:

 Don’t worry too much about bridges – you can always remove them using a clean iron (i.e. clean your iron with the sponge and then it will suck up excess solder). The idea is to get some, but not too much solder on each pad – after a couple of attempts you will figure out when there is too much solder – it looks “blobby”. Once that is done, clean off any excess (unmelted) solder paste:

Next tin the pads of each chip. I am lazy, so I just put some paste in the center and scoot it around until all the pads are tinned. Again, you only want a little solder on each pad – less is more!

Here you can see a tinned chip and what we are up against:

Now that the chip is sorted out, we can turn our attention to the board. Pop it in the vice and apply flux to the tinned pads. The flux is to help the molten solder flow to the right places.

Place the chip in approximately the correct position. Make sure pin 1 (the dot) is aligned correctly with pin 1 on the board!

Fire up the hot air. I use a temperature of 303 deg C and an air flow of 40%. Why? Because that seemed to work. Whether this is optimal or not remains to be determined…

Use the hot air to melt the solder. The trick is to come in vertically above the chip and move in small circles to evenly distribute the heat. Start from about 15 cms above the board and gently warm it up. Slowly move the tip closer to the board until you are about 1 cm away, moving in little circles. Depending on your air flow, you might need to anchor the chip in place with tweezers until the paste melts. This is now the magical part, because as soon as the solder is melted, you simply will not be able to blow the chip off the board – it is held in place by surface tension. Now, use the tip of your tweezers to gently tap the chip into place. Give it a little tap on the top as well so that the chip is sitting flush with the board – after all, the tinning process was far from precise! You will know when the chip is correctly anchored when you give it a gentle sideways tap and it will “snap” back into place. Try it – once you have seen the magic of an LGA aligning itself with the pads, you realize just how easy it is to solder these beasties down. Once it is in place, gradually pull the hot air nozzle away from the board to cool the board down slowly – the idea is to avoid any thermal shocks, though I have found that these  chips are pretty resilient and immune to most forms of abuse – trust me, I did some terrible things to these chips and they all still seem to work fine.

Also, don’t worry if you get it all wrong. Just take a deep breath and try again. This chip below ended up not sitting properly (because I bumped the board while the paste was still molten). Just remelt the solder and start again. The only thing is to not keep the nozzle on the board too long (two minutes or so) or else you start cooking the board – it doesn’t seem to harm it, but it doesn’t look pretty any more. Rather cool the board down and try again.

Once you are done, marvel at the splendour of LGAs perfectly stuck down:

As I said above, I am pretty lazy, and the last thing I want to do is to populate a whole board and then find out that the first chip wasn’t soldered correctly. Using hot air on a populated board can be tricky as you start to unstick things you weren’t planning to unstick, unless you cover the other parts in foil or kapton tape. These chips are I2C, so with four wires (power, GND, SDA and SCL) I could query the WHOAMI register to see if they were playing ball. Using four little Kynar wires soldered to convenient pads, I had a quick and dirty test program running on an AVR that checked that everything was fine. And it was. 20 chips soldered down, not one failure.

And there you have it. Be afraid no more of LGAs, they are your friends 🙂 If you have some tips or similar experience, share below!

STM32F4: Generating a sine wave

Now that the basic peripherals of the STM32F4 have been configured, we can start to make them do useful things. The aim of this “mini-project” is to generate PSK31 modulated data. PSK31 is a very narrowband technique for sending data. Typically, it is used at VHF by ham radio operators, but we have adapted it in our recent IPSN2012 paper for use in underground mines using Magneto-Induction. At the moment, we are using the PWM to output analog values (after filtering), but it would be just as easy to use a DAC or the onboard CODEC.

We want to generate a sine wave at an arbitrary carrier frequency. The simplest way of doing this is to use a software Direct Digital Synthesis (DDS) technique. The basic concept is just to compute the wrapped phase at each sampling instant. In practice, what this means is that we use a free running sampling clock, say running at 100kHz to make life easy. Again, to make life easy, lets assume we wanted to generate a sine-wave with frequency 1kHz. 1kHz is equivalent to an angular frequency \omega=2000\pi radians per second. Our sampling period is 10 us, so in each discrete time step, the phase is advancing by \phi = 2000\pi \times 10^{-6} = 20 \pi \times 10^{-3} radians.

In general, for an arbitrary sampling frequency f_s and required frequency f_o, the phase must increase by R=2\pi \frac{f_o}{f_s} at each sampling instant. Because we are using fixed point numbers, and not reals (floating point), we let 2^{32} = 2\pi . If we were using a 16 bit phase accumulator, then obviously change 2^{32} to 2^{16}.  So all we need to calculate is:

R=2^{32}\frac{f_o}{f_s}

 

The overall procedure is quite simple:

  1. We keep a phase accumulator, which typically has a much higher resolution than the output DAC.
  2. At each sampling instant, we increment the phase accumulator by R.
  3. Sine of the current phase accumulator is then sent to the DAC.

The only “tricky” part in reality is scaling numbers from real (double) to integers to make things easier to compute. For speed, typically the angle-to-sine(angle) conversion is precalculated, in a lookup table (LUT). If you are really smart, you will note that only the first quadrant of the sine lookup needs to be specified, as the rest can be calculated through reflection or inversion. I am lazy, and the STM32 has tons of memory (it could actually easily calculate the true sine value on the fly), so I just precalculated the whole table.

The phase accumulator is stored as a 32 bit long. To get the 8 bit DAC value, the phase accumulator is shifted to the right by 24. This is then used as the index to the LUT.

The entire meat of the code lives in the Timer 2 ISR, which is called at a rate of 100 kHz. Code takes 300ns to execute, so it is taking about 3% of the STM’s brain power (clocked at 84MHz) to update this. This is obviously complete overkill just to generate a lowly sinewave, but it does it pretty well. Here is a screenshot of PD13 showing how long the processor takes in the ISR:

Measuring the ISR execution time
Measuring the ISR execution time

The code itself is simplicity itself, with the magic phase accumulator parameter R just being added on at each interval.

void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
GPIO_SetBits(GPIOD, GPIO_Pin_13);
PWM_SetDC(1,pulse_width);
// Calculate a new pulse width
phase_accumulator+=R;
angle=(uint8_t)(phase_accumulator>>24);
pulse_width = sinetable[angle];
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ResetBits(GPIOD, GPIO_Pin_13);
}
}

Before filtering, the PWM looks nothing like a sine wave!

PWM output – you can see the pulse width reducing towards the right. PWM frequency is ~ 325kHz.

After filtering with a single pole RC (R = 22k, C = 2.2nF), things look a whole lot better, and it looks like a pretty decent attempt at a sinewave. In this case, we are generating a 2500 Hz sinewave, so the magic number R = 107374182.

DDS generated sinewave at 2.5kHz

If we zoom in, we can see that the sinewave is not perfect. This is due to the RC filter not rejecting the high frequency PWM, leading to a slightly jagged waveform. This can be addressed using a higher order filter, such as cascaded LC sections. However, for our application of generating a low frequency magnetic field (where the coil itself acts as an LC tank), this is adequate for now. For a £10 dev board and a few hours work, I would say that this is quite successful!

Zoomed in sinewave, showing slightly rough edges due to inadequate filtering

Download the main listing.

 

STM32F4: USART

The nice thing about using the STLINK/V2 debugger inside IAR is that you can print values straight to the terminal watch screen, just by writing

printf("Ok\n");

. This works really well as a method to find out what is going on inside the mystery black box.

To interface to the real world however, we need to use the USART. Again, after googling a bit and reading the very thick manual, its not too difficult to figure out. The code below

void USART_Configuration(void){
// sort out clocks
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
/* Configure USART2 Tx (PA.02) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Map USART2 to A.02
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
// Initialize USART
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
/* Configure USART */
USART_Init(USART2, &USART_InitStructure);
/* Enable the USART */
USART_Cmd(USART2, ENABLE);
}

sets up USART2 to output data at 9600 bps. To make life easy, we can “overload” the printf primitive to redirect stdio to USART2:

/**
* @brief Function that printf uses to push characters to serial port
* @param ch: ascii character
* @retval character
*/
int putcharx(int ch)
{
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, (uint8_t)ch);
return ch;
}

Now, when we type “printf(“Hello World\n”);”, it appears on the serial port instead of the watch terminal. Simple 🙂

Get the main listing.

STM32F4: PWM

PWM output is great for things like motor control, dimming or in my case, a poor-man’s DAC. As long as the PWM frequency is significantly larger than the fundamental to be generated, a suitably filtered output is often good enough for many purposes. Here, we show how to implement a four channel PWM, driven from a common timebase provided by Timer 3.

First, the GPIOs are configured and assigned to the PWM OC (output compare) alternative functions. PWM is output on PC6, PC7, PB0 and PB1. The clock signal is also routed to Timer 3.

void TIM_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* TIM3 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

  /* GPIOC and GPIOB clock enable */
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB, ENABLE);
  
  /* GPIOC Configuration: TIM3 CH1 (PC6) and TIM3 CH2 (PC7) */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
  GPIO_Init(GPIOC, &GPIO_InitStructure); 
  
  /* GPIOB Configuration:  TIM3 CH3 (PB0) and TIM3 CH4 (PB1) */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
  GPIO_Init(GPIOB, &GPIO_InitStructure); 

  /* Connect TIM3 pins to AF2 */  
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3);
  GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_TIM3); 
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_TIM3);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_TIM3); 
}

Next, the PWM timer itself is configured and the duty cycle registers for each OC channel setup. The integer period passed to the function sets the overall period of the timer. The dutycycle is given as \frac{CCRn}{Period}.

void PWM_Config(int period)
{
  uint16_t PrescalerValue = 0;
  /* Compute the prescaler value */
  PrescalerValue = (uint16_t) ((SystemCoreClock /2) / 28000000) - 1;
  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = period;
  TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
  /* PWM1 Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);
  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
  /* PWM1 Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OC2Init(TIM3, &TIM_OCInitStructure);
  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
  /* PWM1 Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OC3Init(TIM3, &TIM_OCInitStructure);
  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
  /* PWM1 Mode configuration: Channel4 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 0;
  TIM_OC4Init(TIM3, &TIM_OCInitStructure);
  TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
  TIM_ARRPreloadConfig(TIM3, ENABLE);
  /* TIM3 enable counter */
  TIM_Cmd(TIM3, ENABLE);
}

Now the timer is setup and happily. To provide an easy way to modify the duty cycle for each channel, the following helper function is used. I am sure it could be made a lot cleaner, but as a first start, it works ok:

void PWM_SetDC(uint16_t channel,uint16_t dutycycle)
{
  if (channel == 1)
  {
    TIM3->CCR1 = dutycycle;
  }
  else if (channel == 2)
  {
    TIM3->CCR2 = dutycycle;
  }
  else if (channel == 3)
  {
    TIM3->CCR3 = dutycycle;
  }
  else 
  {
    TIM3->CCR4 = dutycycle;
  }
}

Fire it up and the duty cycle on Channel 1 ramps up from 0% to 100%, producing a triangular waveform.

Download the complete listing here.

STM32F4: Timer

This shows how to make a simple interval timer that toggles an LED (the orange LED on the discovery board) everytime the counter overflows. It uses Timer 2 and busy waits to see if the overflow flag has been set. If set, then it toggles the LED. Thus the frequency of the LED is half the frequency of overflow.

The main loop is straightforward: the timer is first started, then the LEDs on the GPIO pins. It then enters an infinite loop, toggling PD13 whenever the flag is raised.

int main(void)
{
  INTTIM_Config();
  /* LED Configuration */
  LED_Config();
  while (1)
  {
    if (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != RESET)
    {
      TIM_ClearFlag(TIM2, TIM_IT_Update);
      GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
    }
  }
}

Once you have had a go at reading the insanely long specification documents, it is quite easy to set up Timer2. The STM32 timers are incredibly flexible in terms of their prescaling – a lot of microcontrollers only give you a few options, whereas here you can divide by any integer (16bit?). You then set the period to dictate how often it rolls over – here we first divide down to 1MHz using the prescaler and then by using a period of 100, we get down to 10kHz. If you want to visibly see the LED flash, change this from 100 to 10000 or something similar. I was on the scope, so no issue with watching flashing lights.

void INTTIM_Config(void)
{
/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 100 - 1; // 1 MHz down to 10 KHz (0.1 ms)
TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; // Down to 1 MHz (adjust per your clock)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}

Download the complete file here.

STM32F4: Interrupt Timer

Rather than using a busy-wait approach to timing, as shown in a previous post here, it is much more efficient to make this interrupt driven. Adding an interrupt involves configuring the NVIC (Nested Vectored Interrupt Controller). In addition, the flags used are different – the IT flags are used instead. This is different to a lot of other micros, where the same flag is used to trigger and clear an interrupt.

The interrupt handler is straightforward – it checks for the interrupt source (TIM_IT_Update) and if that is SET, then it toggles the LED. Unlike other micros, the flag is not automatically cleared, so must be cleared manually (at least, this is my current view of things).

void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
}
}

The main loop is even simpler than before, it just configures the timer and ports and then spins around in an infinite loop. Configuring the timer is similar to before, but has a couple of extra steps to setup the interrupts and tie the timer to an interrupt:

void INTTIM_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the TIM2 gloabal Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

/* TIM2 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 1 MHz down to 1 KHz (1 ms)
TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1; // 24 MHz Clock down to 1 MHz (adjust per your clock)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* TIM IT enable */
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/* TIM2 enable counter */
TIM_Cmd(TIM2, ENABLE);
}

Download the complete listing here.

STM32F4 Discovery

I have a couple of the STM32F4 Discovery boards – they are cheap (£10 from Farnell) to the point of being free and pack an amazing amount of power into the demo board. It also comes with some useful peripherals like an accelerometer and audio CODEC. Possibly the most awesome thing about the F4 is the floating point unit (FPU). Being able to natively do floating point in the same amount of time it takes to do fixed point just moves these things from being pretty nice to rocket science. No more messing around with fixed point saturation and scaling issues…And algorithms can be seamlessly copied from desktop to DSP..

ARMs are quite a step up from the 8/16 bit microcontrollers that I have been using previously. Although ARMs are impressive, I don’t think they are going to entirely replace the lower end PIC and AVRs, mainly because of power consumption, which is a big deal for sensor networks. However, the whole power consumption thing is contentious, because it is not always easy to do a 1:1 comparison, and if you rely on vendor’s advertisements, you are going to be none the wiser.

Although the Discovery board comes with a lot of sample code, both for a demo application and also peripherals, the learning curve is just a lot steeper than other micros where you can just dive in and have something working quite quickly. There is a lot to do in terms of setting up registers and configuring pins/ports and clocks before it does anything useful. I have posted some short code snippets that illustrate some basic features of the device, such as PWM, USART and timers.

One gripe I have is the lack of decent tools for development in Ubuntu. I don’t see in this day and age of Android, iOS, linux etc, why tools should be constrained to Windows. Windows is just about the worst development environment I could think of. Nonetheless, for the sake of getting up and running with the STM32F4, I buckled and installed IAR kickstarter, which gives you 32kB free. I tried Atollic, but I didn’t like it so much. I also have an eclipse setup running on ubuntu – I can get it to debug and program, but its a bit flakey at the moment…

Anyway, enough talking, on to the content

Embedded Nature

Here you will find a collection of snippets, tutorials and explanations that will be useful to folks playing with sensor networks. A lot of it is just a reminder to myself of how to do things, but hopefully it will help you as well.

Research Overview

Here you can find information about current and past research projects. Click on the link to go to an overview of each project and relevant publications.

Magneto-Inductive Tracking of Animals Underground

dGRN: Discrete Gene Regulatory Networks

WildSensing: A Hybrid Framework of Mobile and Sensor Nodes for Wildlife Monitoring