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 radians per second. Our sampling period is 10 us, so in each discrete time step, the phase is advancing by radians.

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

The overall procedure is quite simple:

- We keep a phase accumulator, which typically has a much higher resolution than the output DAC.
- At each sampling instant, we increment the phase accumulator by .
- 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:

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!

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.

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!

Download the main listing.