Thursday 20 February 2014

The STM32F103 Audio Test Board

Allow me to introduce my DIY ARM based Audio Test Board. On the following pages you'll find a description of the board, how I came to it, what tools I used and in the end, all available downloads.

Brothers in ARM's
The idea for this board came to me a little over two years ago. I was working on my DIY guitar amp, and I was having trouble getting the right levels out of the guitar, into the preamp, the level of distortion, etc. Basically, the preamp was a big question mark on the design of the amp.

Then it came to me, just like Doc Brown when he fell off he toilet and invented the flux capacitor! What if I replace the preamp with a DSP?! I could do EQ and some distortion and change it on the fly (or with a few lines of code) if I wasn't satisfied. I had also read a book called "Digital Audio Effects" by Zölzer where some examples of overdrive and tube simulation algorithms where shown.

However, a DSP was not meant to be. I work home with Linux, and use Windows only if I have to do my tax returns. Development tools for TI or ADC on Linux were not well known to me at the time. What if I used a microcontroller instead? I had an Atmel lying around on a dev board, 66 MHz or so I think, but adding hardware to it would prove too complicated. Finally, after a conversation with a work mate, he recommended an ARM processor from STM to me, they ran at 72 MHz, and gave me an USB dev board from Olimex, since he didn't have the time to play around with it.

After a bit of fiddling on the dev board for some time, adding ADC's, DAC's and a serial interface, I decided it was time to build my own audio processing board.

I laid following requirements on the device:
  • At least 8 IO ports
  • At least 8 integrated ADCs (2 on board)
  • RS232 interface
  • External RAM memory (size to be defined)
  • AC or DC powered
  • JTAG interface
  • Reset and boot mode buttons
  • Stereo in / stereo out over the I2S interface
  • Burr Brown (TI) ADC and DAC for audio
That's it, there it was. On paper. No more and no less.

Audio Board Overview

Now that the concept was ready, I had to put it in practice. What follows is a detailed description of the schematics diagram. I tried to keep the schematics simple, in the end, I tried to separate the schematic pages by function blocks.

The Micro Controller

ST Microelectronics, or STM as I shall call them from now, have a nice range of ARM based microcontrollers, from 8 to 32 bits, from 32 to 168 MHz. After some tests with my mate's dev board, I realised that 72 MHz for some simple audio algorithms would do. 32 bits integer operations would be enough, provided care is taken when multiplying and avoiding overloads.

No USB or Ethernet communications were in the plan, that ruled out the STM32F105 and 107 series. In the end I decided for the STM32F103 Hi Density series, with 64 Kbytes RAM and 512 KBytes Flash memory, in case I ever decided to try with a real time operative system (FreeRTOS has a port for STM). A 144 pin TQFP package would be enough for interfacing an external memory module, and just enough to be able to solder it manually to the board.

Another cool aspect of the STM32 processors is the fact that the F1 (72 MHz), F2 (120 MHz) and F4 (168 MHz) families are pin compatible. Ok, that's not entirely correct, because you do need to pay attention on the power pins. Depending on the family, some resistors and capacitors are required or not. This is nicely explained on STM's Application Note regarding compatibility (AN3364: Migration and compatibility guidelines for STM32 microcontroller applications). In that case I could build two boards, one STM32F1 and one STM32F2. On the schematics there is a comment in blue around components that are (or not) necessary for each family.

Two oscillators are provided, a 12.288 MHz oscillator for the processor and a 32.768 KHz oscillator for the real time clock. The 12.288 MHz frequency allows the processor to run up to 67.584 MHz, with overclocking up to 73.728 Mhz, but that's not recommended by STM. True, the processor doesn't run at full speed, but as the processor is the master clock source for both ADC and DAC, it's possible to set sample rates of 48 and 96 KHz precisely.

The pushbuttons are the "Reset" and "Boot0". "Reset" has an internal pull up, so it just uses a cap for debouncing. "Boot0", when pressed or released after "Reset" release, sets the boot mode. This is important when you don't have a JTAG debugger/programmer, you can still flash the processor using the serial interface. In that case both "Reset" and "Boot0" are pushed, and "Reset" is released first, and the processor starts in boot mode. That was very useful for me, since I started my first test applications programming the controller using the serial interface. More about boot modes can be found here: AN2606: STM32 microcontroller system memory boot mode.

A JTAG interface is available as well, and it allows debugging and flashing using JTAG or SWD protocols.

Finally, an inductor provides extra filtering for the internal ADC's power supply.

Microcontroller Schematics (Click to download PDF)

Power Supply

To power the board 7V DC are necessary. I've tested it up to 14V DC, but I specified the maximum voltage at 12V. The board accepts AC power too, it uses a very simple regulator and diode circuit, also to avoid damage if the polarity is swapped. I'll be very honest with you, the board does work well under AC power (I used an old modified Nokia charger), but I did not try changing polarity.

The board has two power sources: 5V for the analog part, and 3.3V for the digital part. The layout includes two separate ground planes, connected to the main power supply ground.

The 5V come from a TS1117-CW50 linear regulator. The 3.3V are sourced from a L5970D step down converter. Both power supplies have an LED indicator, that does help a lot!

As you can see, the power section is kept pretty simple, and it works. I measured the consumption of the board at 12V DC and it's about 100mA with all interfaces working.
Power Supply Schematics (Click for PDF)


Serial Interfaces, ADC & DAC (I2S)

These interface provide the main interfaces to the test board, audio ADC and DAC. I chose what I think are very decent converters, PCM1802 ADC and PCM1781 DAC from TI (formerly Burr Brown), plus the fact that they have simple hardware interfaces for configuring the operation modes. Each device operates as a slave to the microcontroller using the I2S interface, and requires:
  • Master clock (MCLK), provided by the controller (e.g.: 24.576 MHz)
  • Left/Right clock (LRCK), provided by the controller (e.g.: 48 KHz)
  • Bit clock (BCLK or SCLK), provided by the controller (e.g.: 3.072 MHz)
  • Data (SD), provided by the ADC to the controller, or by the controller to the DAC.
There are some format at configuration pins, set dynamically by the controller or fix. For more information on the functions you can check the datasheets. Additionally, an LED shows the "Zero" status when the DAC is fed with zeros as data.

The analog data from the DAC is fed to a dual opamp, the LM49721. I had the OPA2134 on the original design, however, I failed to notice that the minimum rail to rail voltage is 5 V. Once the board was built, the power supply was not enough for the OPA2134. Lucky for me, the LM49721 is a pin compatible replacement to the OPA2134, with a supply range from 2.5 to 5 V rail to rail. After replacing the output op amps, the output was very satisfying.

A MAX3232 from Maxim (no, not the magazine) Integrated Products provides a serial interface to connect the board directly to a PC. I found there is nothing more frustrating than having a development board with a serial interface which you cannot connect to the PC directly, but have to build your own converter from scrap material. The serial port is kept as simple as possible, just transmit, receive and ground signals (3, 2 and 5 on the PCs serial port respectively). The MAX3232, as opposed to the MAX232 is 3.3V compatible. Two LEDs connected between the controller and the MAX3232 indicate if there is some activity on the serial lines.
ADC, DAC, Opamps & Serial Interface Schematics (Click for PDF)


ADCs (Internal) & GPIOs (General Purpose Inputs/Outputs)

All of the above is functional and very nice, but there is no point in going further if I cannot control the board in some way. Here come the GPIOs and the internal ADCs. There is a choice of 16 GPIOs + 4 LEDs, plus 2 on board potentiometers and the possibility to connect 8 external sources.

The GPIOs are configured with a weak pull-down 47K resistor, so it will not affect too much if the pin is used as output. Each GPIO input can be driven high by the boards 3.3V supply, if so chosen. Care must be taken if the input is driven exclusively by an external component, not to exceed the 3.3V.

Each ADC input can use the 3.3V power supply (filtered additionally). Both on board pots are 10K pots, and the maximum value that can be fed to the ADCs is ~1% less than the supply voltage due to the resistor in series. I've tested the board with several 10K pots connected, and the values read by the ADCs were fairly accurate. Again, care should be taken if the ADCs are driven by external sources not to exceed 3.3V. The sampling speed and operation of the ADC is configured on the controller.

The four available LEDs are fixed and very, very helpful! Of course the first application I tried was the blinking led program (the equivalent to "Hello World"). If you get the LED on any development board to blink, the rest is usually easy.
Internal ADCs and General Purpose IOs Schematics (Click for PDF)


SRAM

The external memory is a K6R4016V1D SRAM from Samsung. As far as I read, it's cheap, easily available and compatible to some other brands. It's a 4Mbit device with 16 bit access, 512 KBytes additionally to the controller's 64KBytes.

The STM32 controller offers a dedicated controller for external memories, that's why it's very important to connect the memory address and data lines correctly, as well as the chip select, output enable and write signals. On a first version of the board I made a mistake, a pin was connected wrong. This meant, the controller worked properly and was setting the correct signals, but no data could be written to the SRAM.
Static Memory (SRAM) Schematics (Click for PDF)

The Layout


Below you'll see a finished version of the board in relative detail, with all interfaces highlighted.

Laying out a board is an art, people say. Now, I don't consider myself an artist, but I'm really happy with the result (after 2 previous tries). It provides easy access to all interfaces.

There is a v0.3 of the layout (which is the one you can download), and the main differences between the image (v0.2) and v0.3 are:
  • The JTAG connector is the actual JTAG connector and not a pin header
  • Some texts are not etched on the top copper layer, but on the silk layer
  • The audio signals follow a slightly different route.

Top View


Bottom view

Below is a printout of the boards top view from Kicad

Current layout

Hands on experience and final words

After many hours patient soldering, I had the board ready. The only part I soldered first was the power supply and checked the voltage. Once that was taken care of I proceded with the rest. Needless to say, the hardest part was to solder the controller, but with a steady hand and lots of patience and some cursing it turned ok.

I can't really say that the board worked flawlessly from the first moment, I had to re check solder joints in different places I was able to flash it. Once the flashing worked and my first "blinky" was application ready, I had to check the solder on the LEDs, because guess what, it didn't work either!

But, and this is the good part, whatever didn't work at the beginning turned out to be my lack of experience in soldering, because at one point in time, all interfaces were working ok. To test everything a wrote a program that uses all interfaces. I will not go into software detail on this post, it's already long enough (if you made it so far, many thanks!). I also did some measurements using an audio analyzer, just to have some values. Sadly, I never stored those values, but the board measures OK.

And I'm happy with OK, because the idea behind all of this is to have a test board, not a Hi-fi product. One can certainly develop ideas on this test board, and design a proper Hi-fi device using a smaller micro, in case the external memory were not required or only a few GPIOs were needed. The function would not change, still the peripheral components could be improved.

At the moment one version of this board is working as a guitar pre-amplifier in my DIY guitar amp. It does a great job at that, and it gives me the flexibility to change or reprogram parameters if I wish to do so.

Another project I have for the future basted on the STM32F2 family is a phono preamp. I'll test the RIAA filter with this test board, and then move on to a custom design.

It was a fun project to do, it took quite some time, it was a bit of a sweat, but in the end I'm pretty happy with it.

Downloads

  • The Kicad Project (zip)
  • Gerber files (zip)
  • Schematics (pdf)

4 comments:

  1. Dear Betocool,

    I wonder how did you set it up the micro to get the 67.584Mhz.

    Will be this correct:
    /* PLL configuration: PLLCLK = HSE/2 * 11 = 67.584 MHz
    PLLCLK = HSE/RCC_CFGR_PLLSRC * RCC_CFGR_PLLMULL11 = 67.584 MHz*/
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
    RCC_CFGR_PLLMULL));
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL11 | RCC_CFGR_PLLSRC);

    or have you done it in a different way?

    Many thanks

    ReplyDelete
    Replies
    1. Hi,

      I used this code:

      void initClocks(void){

      /** Initialize clock to external clock (12.288 MHz) **/
      RCC->CR |= RCC_CR_HSEON;
      while(!(RCC->CR & RCC_CR_HSERDY)){
      ;
      }

      /** Set PLLXTPRE to '1' (/2) **/
      RCC->CFGR |= RCC_CFGR_PLLXTPRE;
      /** Set PLLMUL to 0x8 (x10) **/
      RCC->CFGR |= (0x8 << 18);

      /** Set PLLSRC to 1 (HSE) **/
      RCC->CFGR |= RCC_CFGR_PLLSRC;
      while(!(RCC->CFGR & RCC_CFGR_PLLSRC)){
      ;
      }

      /** Enable PLL **/
      RCC->CR |= RCC_CR_PLLON;
      while(!(RCC->CR & RCC_CR_PLLRDY)){
      ;
      }

      /** Set SW to PLL **/
      RCC->CFGR |= RCC_CFGR_SW_PLL;
      while(!(RCC->CFGR & RCC_CFGR_SW_PLL)){
      ;
      }
      }

      Hope it helps!

      Delete
    2. thanks for your help, i will tried that to see if it works!!!

      Delete
    3. Hi, do you use DMA for i2s data transfer ?
      Do you publish the example code somewhere .../
      ?

      Thank you

      Delete

Note: only a member of this blog may post a comment.