Graduating From Arduino: How to Select and Add a Microcontroller to Your Custom PCB

by markahofmeister in Circuits > Microcontrollers

55 Views, 0 Favorites, 0 Comments

Graduating From Arduino: How to Select and Add a Microcontroller to Your Custom PCB

pcb-back.png

Arduino is a simple microcontroller development platform that allows wicked-fast prototyping. However, Arduino's basic hardware and abstracted programming language limit the ability of makers once they go beyond basic prototyping. I'm going to open the door to the vast world of microcontrollers and empower you to choose one, add it to a custom PCB, and interface it with an incredible range of other devices. I'll be using some of the modules on my very own STM32 audio development panel to demonstrate.

What Actually IS a Microcontroller?

arduino-annotated.png
ATMEGA328P-PU.jpg

First of all, I must clear up a common confusion of terminology among makers. "Arduino" is not a microcontroller. "Arduino" is a microcontroller development platform. Many folks call Arduino a "microcontroller" when, in reality, it is a PCB with a microcontroller on it. The PCB "breaks out" all of the GPIO pins to the user in the form of big, fat male/female header pins, includes some LEDs to flash, and does all of the power management/delivery for us. Taking a look at the canonical Arduino Uno, we see that the actual microcontroller is the black rectangle towards the center of the board.


The black rectangle on the board is the ATMEGA328P, which is a specific flavor of the ATMEGA microcontroller (MCU) product line. It's an integrated circuit (IC) that comes in a DIP16 package. Arduino chose to use this MCU for their applications, but we don't have to.


There are so many MCUs on the market to choose from, each with different functionalities, manufacturer qualities, and development environments. Popular names that you'll hear include:

Each company has a massive bank of MCUs with similar architecture but variances in the exact peripherals and supported functionality. As opposed to Arduino, this allows us to select an MCU tailored to our needs. I'll be using an STM32 microcontroller from STMicroelectronics in this tutorial because they have a great development environment that is a bridge between Arduino's programming language and bare-metal. Next, let's check out what they have to offer product-wise.

Choosing a Microcontroller

mcu-prod-select.png
mcu-datasheet.png

If we check out the STM32 MCU Product Selector Page, we'll see that they have 1300 options for us to choose from. Luckily, we can narrow our search down a bit by filtering the parameters in the product selector tool.


First, let's understand the naming convention of STM32 MCUs. All STM32 MCUs have a product code that is a series of letters and numbers, which we can break down into something more intelligible. Each product code is a concatenation of these things:

  • Family (this will always be STM32)
  • Type
  • Core
  • Line
  • Number of Pins
  • Flash Size
  • Package
  • Temperature Range

Of particular interest to us in this tutorial are the type, core, number of pins, and package.


The type will always be represented by a single letter and tells us the general application of a specific microcontroller. For example:

  • F - foundation, or sometimes high-performance
  • G - Mainstream
  • L - Low-Power
  • H - High-performance
  • W - Wireless

For our application, we don't need low-power or wireless functionality. We're also not using this for very intense DSP, so we don't need high performance, either. Therefore, I'll stick to a type "F" MCU.


The core is represented by a single integer, representing the power of the ARM Cortex CPU in the MCU:

  • 0 - ARM Cortex M0
  • 1 - ARM Cortex M3
  • 2 - ARM Cortex M3
  • 3 - ARM Cortex M4
  • 4 - ARM Cortex M4
  • 7 - ARM Cortex M7

A more powerful core means a more expensive MCU, and ARM Cortex M3 will do just fine for us. Therefore, we'll probably be looking for an "F1" or "F2" MCU.


The number of pins is represented by a single letter:

  • F = 20 pins
  • G = 28 pins
  • K = 32 pins
  • T = 36 pins
  • C = 48 pins
  • R = 64 pins
  • V = 100 pins
  • Z = 144 pins
  • I = 176 pins

There will be a lot of peripherals on our board, so we'll probably need at least 48 pins. Therefore, we'll likely be looking at a "C" or "R" number of pins.


Further transparency into the MCU package is given by the package code:

  • P = TSOOP Package
  • H = BGA Package
  • U = VFQFPN Package
  • T = LQFP Package

QFN and BGA packages allow very tight pin density for applications requiring lots of IO or a small footprint. They are a bit tougher to route and have lower PCB assembly yield rates, and we don't have any extreme space or IO requirements, so we'll likely be looking for "P" or "T" packages.


Let's jump back to the STM32 MCU Product Selector. We'll use the following filters based on our previous analysis:

  • Status & Availability - Active
  • Package - all LQFP Options
  • Core - Arm Cortex-M3

There are a few other things that I want for this project, so I'll use those to further filter the results:

  • Flash Size (kB) - Min. 512 kB, as I may use this to store some audio data
  • Additional Interfaces - SD/MMC, as I'd like to interface my MCU on an SD card

After browsing through the options, I'll settle on the STM32F103RET6. I made sure to check that it has an I2S peripheral, as that's something that I know I'll want to interface with an audio amplifier.

(Optional) Planning Pinout With STM32 Cube IDE

mcu-select.png
ioc-pin.png
ioc-menu.png
ioc-clock.png
ioc-final.png

***NOTE: This section is not required if you are using a non-STM32 MCU or you choose not to use the STM32 Cube IDE. The concepts in all following sections can be generalized to all MCUs. All pin configuration options can be found in your MCU's datasheet. Make sure to read application notes for each peripheral to make sure you're using them correctly.***


Before we start connecting our MCU with other components on our PCB, we need to determine which pins will go where. STM32 has a very helpful tool to allow us to configure the functionality of each pin on our specific MCU model, which is called STM32 Cube IDE.

After downloading STM32 Cube IDE, create a new STM32 project and select the STM32F103RET6 as our target. Choose a name for your project that you fancy and click Finish; you'll be greeted with a bare MCU. This is the I/O configuration file with a .ioc extension, which you'll see contained in your project file list.

There are two ways to configure the pins on the MCU:

  1. Click on a single pin of the MCU diagram - you'll notice that a list of different functions pops up. You can select one of the pin functionalities from this chart, such as GPIO output, GPIO input, GPIO external interrupt, ADC channel, timer output, or other communication protocols. Configuring pin-by-pin is useful for selecting which pins you'd like to use as GPIOs.
  2. Click on one of the folded menus in the column to the left of the MCU diagram. These contain all of the supported hardware configurations on our MCU. This menu is useful for selecting which serial interfaces you'd like to use, as enabling multi-pin functionalities in this menu will automatically configure all required pins.

I'll be configuring just a few things in this example:

  1. GPIO Output:
  2. Click on whichever pin you'd like to serve as this output. I'm using pin PC5.
  3. Select "GPIO_Output".
  4. Right click again on the pin.
  5. Select "Enter User Label."
  6. Enter a convenient name for this pin. I'll call this "DEBUG_LED".
  7. HSE Crystal:
  8. In the left column, select "System Core" --> "RCC".
  9. In the "High-Speed Clock (HSE)" field, select "Crystal/Ceramic Resonator." You'll notice that two pins on the MCU have automatically been configured for this function.
  10. Click the "Clock Configuration" tab.
  11. Configure the system clock source to be the PLL and the PLL source to be the high-speed external crystal. Set the HSE crystal input as 12 MHz. Though the MCU doesn't need an external crystal to run, crystal oscillators are much more accurate than the MCU's internal oscillator and can support high-speed peripherals like USB.
  12. Set the prescalars to produce the clock values required for your application, as shown in the image at the top of this section.
  13. SWD:
  14. In the left column, select "System Core" --> "SYS."
  15. In the debug field, select "Serial Wire." Like the HSE crystal, you'll notice that two pins on the MCU have automatically been configured for this function.

Save the .ioc file and STM32 Cube IDE will write a bunch of code to configure these peripherals for you. This is an incredible time-saver.

Adding Your MCU to PCB EDA

mcu=bare.png

Find a schematic, footprint, and 3D model for your MCU. My 3 favorite web sources for these files are:

Additionally, manufacturer models can often be found on vendor sites like Mouser and Digikey. Simply enter the product number in the search bar to see if the sites have them.


I'll be using Altium in this tutorial, but you can find models for any EDA software like KiCAD, EAGLE, or Cadence. Make sure to verify that both the schematic and footprint are correct!

Powering Your MCU

vdd-recommended.png
schem.png
pcb.png
ind.png

Powering an Arduino board is as simple as plugging it in, as Arduino's hardware engineers have already implemented all of the necessary circuitry to do so. We, however, need to do this ourselves.

Determine what supply voltage your MCU uses. For the STM32F103RET6, I found that the operating voltage is 2V - 3.6V, which makes sense, as this MCU runs on 3.3V logic levels. I got this from the "General Operating Conditions" section 5.3.1 of the datasheet.

Important: Determine operating parameters from the "General Operating Conditions" section, not the "Absolute Maximum Ratings" section. Always operate devices within the manufacturer's recommended parameters - they usually know best.

Therefore, our "VDD" supply voltage should be 3.3V - but we're not done. We need to ensure that this stays 3.3V during operation, or at least doesn't stray outside of recommended operating conditions.

Let me explain - any power supply line will have parasitic inductance along its length, and the voltage across an inductor is given by (v = L * di/dt). Therefore, rapid current changes caused by transistors in the MCU rapidly switching on or off result in a voltage drop across the power supply line's parasitic inductance. This can sag the voltage on the power supply line considerably, which can result in your MCU spontaneously resetting or in unexpected behavior. This occurs even when my 3.3V source is regulated.

We remedy this problem with capacitors. If we place a capacitor really close to a voltage supply pin on an MCU, it will serve as a little localized charge source and filter out any high-frequency changes in the power supply line before they hit the MCU's supply pin.

My MCU has 4 separate VDD pins and a VBAT pin, so I'll include 5 capacitors of a small value, like 10nF - one for each pin. I'll also include a single 2.2uF "bulk" capacitor to act as a bulk charge source for all pins. This comprises the digital circuitry supply.

For the analog power supply, I'll apply even more filtering, as this powers some peripherals that are very sensitive to changes in VDD. I'm using a pi network with 2 capacitors and a ferrite bead to provide power that is as pure as possible.

When it comes to routing, it is essential that the capacitors be placed as close to the MCU pin as possible. Tight routing minimizes stray inductances between the capacitor and the input pin.

Implementing an External Crystal Oscillator

mcu-cap.png
pcb.png
saturn.png
schem.png

Select a crystal oscillator that you'd like to use. My MCU can take a HSE crystal oscillator with an output frequency in the range 8 - 16 MHz; I chose to use a 12 MHz crystal.

Your crystal has an important parameter called the "load capacitance." To function properly, we need to add two external capacitors to the crystal to meet its load requirements. Unfortunately, the value of the capacitors is not simply equal to the crystal's load capacitance. We must take into account the capacitance of the MCU pins and the capacitance of the PCB traces.

The "rule of thumb" calculation for capacitor values is 2 * [ (Crystal load capacitance) - (Stray Parasitic Capacitance)]. The Saturn PCB Toolkit has a tool to help us calculate this. The MCU's datasheet shows 5pF of input capacitance on the HSE crystal pin, and I routed my crystal very tightly, so I predict only about 2pF of trace capacitance. Using these values, we find a "Rule of thumb" value of 18pF for our capacitors. Make sure to annotate your calculations on the schematic!

Adding a Programming Interface

connector.png
mcu-swd.png
pcb-test-points.png
boot0.png

Arduino allows us to simply connect a USB cable from the board to our computer and upload code, but microcontrollers usually don't take native USB, and it's not necessary to flash code to them. Our STM32 MCU uses a protocol called Serial Wire Debug (SWD) to do this, and we already enabled it in the software in Step 3. We can use tools like an ST-Link to convert USB data from our computer to SWD data that the MCU likes. If you're not using an STM32, your MCU's manufacturer will have similar tools to allow interfacing.

We'll add a connector with 5 pins:

  • Serial Wire Data (SWD_IO)
  • Serial Wire Clock (SWD_CLK)
  • MCU Reset Pin (NRST)
  • MCU Power (VDD)
  • Ground (GND)

***Important: The ST-Link does not power the MCU - your board will need an external power source. The VDD connection is a reference voltage for the ST-Link to communicate with the proper logic levels.***

Technically, you're able to simply connect the pins of the header to their corresponding MCU pins. I'm going to add a bit of extra circuitry, however:

  • TVS Diodes: Since I'll be touching this header with my fingers when plugging/unplugging the SWD connector, there's a chance that I transfer an electrostatic charge into the PCB. These charge releases can be thousands of volts, which will damage sensitive circuitry on the board. These diodes serve to short those spikes to ground in the event of a voltage spike greater than my 3.3V logic level.
  • Series Resistors: If I happen to short one line to ground or another line while I'm probing around during debugging, these small series resistors will limit the current rush through this short.
  • Reset Line Capacitor: This filters out noise on the reset line and prevents spurious MCU resets.
  • Test Points - These are useful to hook an oscilloscope probe onto to examine what is happening on a SWD pin.

Luckily, SWD communicates at low speeds (~4MHz clock frequency), so you won't need any impedance matching when routing these signal lines.

Finally, my MCU has a BOOT0 pin (and some MCUs have multiple BOOT pins.) For STM32, the BOOT pins should be pulled LOW for SWD programming. BOOT pins can be pulled high for things like USB interfacing, which we're not covering in our tutorial. I included a DIP switch to allow me to switch to BOOT mode if needed, but you don't have to if you'll only be using SWD. Make sure, however, to pull this pin to ground using a pull-down resistor.

"Quality of Life" Improvements

led.png
tps.png

This step adds components that are not necessary for functionality but make our lives much, much easier.

  1. Debug LED - Add an LED in series with a resistor connected to one MCU pin. The resistor is a relatively large 1k ohm since this LED need not be very bright. I added my LED to pin PC5, which we configured in the Cube IDE in step 3. Flashing this LED is a very simple test to make sure that your MCU is alive and well.
  2. Test points - Be liberal with your inclusion of test points. Include test points for power, multiple ground points, and any signals that you might need to examine. You can see that I'll include ~30 test points for a simple audio application. These pads don't even need to be populated - even the exposed copper pad is helpful.

Add Other Peripherals

We've covered the basics for adding and interfacing with an MCU. At this point, you're free to add other circuitry to your design like buttons, switches, LEDs, amplifiers, sensors, etc. Be sure to follow manufacturer recommendations.

Order Your PCB to Be Manufactured

My favorite PCB Manufacturers:

(If Needed) Talk to Your Manufacturer!

Remember: Your MCU manufacturer wants you to use their products. It is in their interest for them to help you and provide you with the tools you need to make a successful application. If you're having trouble with their datasheet or don't know how to implement something, talk to them. A company with a healthy MCU line will have application engineers ready to help you out, and they know their products best. Send their tech support an email, phone call, or message.

"Hello, World" Your MCU

connect.png

Once you receive your PCBA, or once you receive your PCB and assemble it yourself, power your PCB, wire up the programming interface, and do a quick test to make sure you can flash the debug LED.

For those using STM32: When you download STM32 Cube IDE, ST downloads another utility for you called "STM32 ST-Link Utility." It's found in C:\Program Files (x86)\STMicroelectronics\STM32 ST-LINK Utility\ST-LINK Utility (if you're on Windows.) Open this .exe file and click "Target" ---> "Connect." If your PCB is powered and your programming interface is set up correctly, the ST-Link Utility should detect what line of MCU you are using and read the memory successfully.

The World Is Your Oyster

Once you've confirmed that you can talk to your MCU, you're free to write whatever code is necessary for your application. Have fun!

Resources

If you'd like, check out my GitHub page, where I've stored all of the Altium files for this project:

https://github.com/Markahofmeister/Christmas-2023

References

  • https://commons.wikimedia.org/wiki/File:ATMEGA328P-PU.jpg
  • https://store.arduino.cc/products/arduino-uno-rev3
  • https://www.digikey.com/en/maker/tutorials/2020/understanding-stm32-naming-conventions
  • https://wiki.st.com/stm32mpu/wiki/How_to_get_started_with_STM32CubeIDE_from_scratch
  • https://www.st.com/resource/en/application_note/an4989-stm32-microcontroller-debug-toolbox-stmicroelectronics.pdf