Showing posts with label MCU. Show all posts
Showing posts with label MCU. Show all posts

Monday, February 12, 2018

SLab First Release

This article is also available, in spanish, in the companion AIM65 blog.

This is the first release of Small Lab (SLab), a project I have been working on for a long time.


SLab is a project to develop a tool for hands-on electronics learning on a minimum budget. The idea is to provide, to anyone who wants it, a system capable to inject excitations on a circuit and obtain measurements so that the circuit operation can be understand by practice.

Low cost means really low cost, below 40€ in my rough calculations for a full system with instrumentation and components to build the circuits.

SLab System (and disclaimer)

The SLab system is composed of a chain of components as shown in the figure below:

First we have our Circuit Under Test (CUT). This is a circuit that enables us to learn somethig about electronics. Then we have a hardware board that is able to inject signals on the circuit and measure the voltage on some of its nodes. The board connects to a PC where some Python modules interact with the board by sending command throug a serial over USB COM link. You are at the right end of the chain sending high level commands for the SLab system to execute. Those commands, if needed, can be written on Python scripts to ease repetitive tasks. You can also create your own libraries that connect with the SLab ones and extend the system capabilities.

Note the red isolation zone in the previous drawing. You are only supposed to connect the Circuit Under Test (CUT) to the hardware board. No electrical connection, except the USB cable, shall go outside of the protection zone. The CUT shall also be powered only by the hardware board.

If you want an additional layer of protection, or you are not sure of your own actions, you can use an isolated USB cable together with a powered HUB to run the harware board like in the figure below. That way you could damage the hardware board or the HUB but the PC will be more secure.

Either way the following disclaimer apply:

The author of this document takes no responsibility for any damage to you, your properties or equipment due to the use of the SLab system. This is true either if you are following the proper procedures or not.

The hardware

In order to keep costs as low as possible, the main hardware element of the system is a microcontroller demonstration board. The chosen board for this first release is the STM32 Nucleo64 F303RE board.

Nucleo F303RE Board

Why this board?. Well, it is cheap, currently only 9€ at farnell and it is quite capable. It includes the STM32F303RE microcontroller that features two 12 bit DACs, four 12 bit ADCs, 512KB of flash and 80KB of SRAM. It also is MBED enabled. That means that installing a binary firmware is just drag&drop. Moreover, the USB communication includes a serial over USB Com link that installs the driver as soon as you connect the board to a computer. Finally, as MBED is so easy to use, and the board is much more powerful than most Arduinos, having this board also enables you to play with firmware developement in your spare time if you want to.
 
Using the proper software on the board, you can instruct it to send signals out of the DACs and read signals with the ADC inputs. The problem is that you usually need some buffering to prevent loading effects when you connect the ADCs and DACs to a Circuit Under Test (CUT).
The following figure shows a suitable solution based on opamp voltage followers for all DAC outputs and ADC inputs. See that it also adds a LED and a resistor so that we can see that the CUT is properly powered.

Drivers to interface the circuit

Note that DAC2, associated to pin D13, has a non inverter amplifier instead of a follower. This is needed because, in the F303RE board, the D13 pin is also connected to a LED, that prevents us to use the full range of the DAC and we are limited only to le lower half of the full range. This is no problem, however, as we will see later.

You can implement the driver circuit in a solderless breadboard and you have plenty of space left to put a circuit to be measured. The following figure shows the F303RE board, the driver circuit and a test circuit composed of a resistor and a diode.



If you have the capabilities to solder you can implement the driver circuit in a shield. The following figure shows the drivers on a shield board designed for Arduino. This is ok because F303RE board features Arduino compatible connectors.

Driver Shield

If you have option to build your own PCBs, you can create a better shield. The following figure shows the "Long Board". It includes the F303RE board, a milled PCB shield with the drivers, a solderless breadboard for the test circuit and an aluminium clad to join it all. A small electronics lab in a 28 x 8 cm form factor. The details are in the SLab documentation.

Long Board
The following image shows the shield diagram wit its 17 male pins. In includes two GND pins, a 3.3V Vdd pin, the two buffered DAC outputs, the four buffered ADC inputs and eight digital I/O lines. All of that can be controlled from Python code.

Depending on your resources and capabilities you can use the any of the hardware options to mix together the demonstration MCU board, the drivers, and space to mount a circit to test. Either way, in the end, you will have the elements shown in the following figure: A Vdd supply, two DAC outputs and four ADC inputs. You can also add the digital I/O but this is not needed for most analog circuits.

SLab elements


By using the proposed MCP6002 and MCP6004 operational amplifier chips, you get very high input impedance on the ADCs, and nearly full rail operation inside the 3.3V range of the board. The current capabilities of the opamps are not so great, but, as we are powering the system from the USB port, we are quite limited in the currents anyway.

It would be possible to design a driver interface circuit with more voltage and current range. And it will be done in the future, but, for now, the idea is to keep the system as cheap and simple as possible. Making a better interface circuit is not difficult at all, the problem is making it available, at low cost, for everyone.

The software

Bad software can make a low budget instrumentation device a nightmare to operate, and developing good software is difficult and time consuming. For this project, I have taken profit of the huge Python library and, in particular, of some SciPy libraries (NumPy and Matplotlib).

But Python is not easy to use on the F303RE board, and also, it would be very inefficient for performing timed measurement. So,the full system needs an additional element: the board firmware that is the middle element between the board ADCs and DACs and the Python code in the PC. It acts as a server for commands from a client PC.

The software structure somewhat resembles the firmata protocol with a server on the hardware board and a client on the PC. But it is not firmata. The main difference, apart from the fact that the communication is not MIDI, is that commands can include waveform generation and the reading of sequences of voltage values at constant rates (up to about 80kHz with the current firmware). Some functionalities, like the DC ones, could be performed adding a Python layer to firmata, but not all of them, specially for the AC ones. In general, the SLab system, it is more oriented towards analog signals than digital ones.


The board firmware

As the F303RE board is MBED enabled, the first version of the firmware has been developed in the MBED cloud compiler. I plan on designing a future version of the firmware, probably without MBED, to optimize the code as close as the board limits as possible. On the following table you can see that, when using only one channel, sampling time in transient measurements can be as low as 13 microseconds. This is not too bad for a microcontroller board, but far from the F303RE capabilities that can get sample rates in the MHz range. Most of the problems when using more than one channel are due to the fact that MBED uses only one ADC for all analog inputs son all four channels cannot be read at the same time. There is always room for improvement, and the firmware will be sure improved. When I have time, that's it.

Current firmware sample time transient capabilities
The board firmware communicates with the PC and basically receives commands, interfaces with the circuit through the DACs and ADCs, and reply a response to the PC. Some examples of  commands are:
  • Set a DAC voltage value
  • Read the voltage on an ADC input
  • Read a sequence of ADC values with at a constant sample rate
  • Store on the board SRAM waveforms to be generated later on the DACs
  • Generate waveforms and read the circuit response using at a constant sample rate
The most complex thing the board firmware can do is to generate two different arbitrary waveforms on the two DAC outputs and simultaneously read the four ADC inputs. After generatings the waves and collecting data for some time, the data is sent to the PC for processing.
The communication protocol between the board and the PC is fully documented so, if you have a board with at least two DACs and four ADC inputs and you know about firmware developement, you can create your own firmware to interface with the SLab system. The communication link implements CRC checking, but, as USB communication is quite reliable at short distances, the main purpose of the CRC is to detect software bugs.
The board capabilities are read from the board uppon connection, so no modifications need to be performed at the PC side if you develop a firmware for a new board.

Current board firmware is about 2500 lines of "C" code. You can find it in the SLab Github project. But, if you only want to use it, just download the binnary file and drag&drop it on the board to program it.

The firmware code is also available on MBED.

The SLab Python code
The board firmware interfaces with the circuit and needs to be as optimized as possible because it is what set the limits of the full system. But one important part of the software is Python code. Currently the SLab project has next to 6000 lines of Python code. It really is not that much because of comments and blank lines. Although comments are also important because they generate the SLab help subsystem. No exactly doxygen but is the same idea.

In order to work with a circuit, you open a Python interpreter, import the SLab module, then call SLab functions that communicate with the board, perform excitations and/or measurements and return the results. Thanks to Matplotlib the results can be shown on the screen, and, thanks to NumPy, the results can be manipulated to your heart's content.
All the SLab Python code is included in the same Github project as the firmware code. 

The first layer of the Python code is the calibration code. How do you obtain good measuments from instruments? By proper calibration, off course. Before using the board you should calibrate it. From the calibration procedure you obtain a set of curves, one for each DAC and ADC that describe how the real values relate to the inputs or outputs of those devices. As an example, the following figure shows the DAC ratiometric calibration curves that include the drivers indicated on the previous schematic. Note how DAC2 has a slope of two due to the non inverting amplifier operation. But you don't need to bother as calibration takes care of that. Once the calibration is completed, you can forget about it, SLab takes care of the details internally on each measurement.

DAC Calibration Curves
The last layer of the SLab code are the functions that the user can call. Those functions indicate the board to do something and return a result, if available. It can be as simple as setting one DAC to a given voltage value or as complex as measuring the distortion that a circuit induces on a sinewave or obtaining a bode plot by obtaining the response to several sinewaves. The simple commands are just directly sent to the board after calibration. The more complex commands require more Python code work do do their magic from the basic board firmware commands.


Some examples

Now, some examples of what can be done in SLab. Refer to the reference documentation for much more examples. Let's start playing with a simple low pass filter:

Low Pass Filter

You connect the buffered DAC1 output to the input of the filter and the buffered ADC1 input to the output of the filter. Then, you input the following Python code. You don't need to use interactive mode, but this is always an option.

The first six lines calculate the ideal response of a low pass filter using the NumPy library. The sixth line measures the real frequency response of the circuit connected between DAC1 and ADC1 for the same set of frequencies used on the previous calculations. You will obtain the following graph that compares the idel and real responses right after the last command.

Bode Plot
As the SLab system, to keep costs down, features no amplification stages , the dynamic range is not so great and bode plots start to get ugly, specially in the phase graph, after 30 dB atenuation levels.
The circuits can be much more complicated. The following circuit uses DAC1 to generate a sinewave and DAC2 to generate a random noise signal. From those signals, two differential signals with injected common noise are generated on V1 and V2. Then, a differential amplifier is used to reject the noise and recover the signal. On the image, unity gain amplifier are opamp followers and the inverting amplifier is an opamp inverting amplifier.
Common Noise Reject Circuit
The circuit has been mounted on a breadboard and tested using the following code that generates the signals, performs the measurement and plots the results. In this case, althoug SLab includes gaussian and uniform noise generation, we use numpy calls to generate the noise.

The following graph shows the differential signal, with common noise, at ADC1 and ADC2. It also shows the noise at ADC4 and how the signal is recovered without noise at ADC3. It looks like a simulation but they are real measurements.



You don't need to provide inputs to the circuit. The following example is an astable circuit, based on a hysteresis comparator that generates a square wave.

Astable Circuit
 The measurements of some circuit nodes are shown below. The code is here.

Astable transient operation

You don't always need to operate with time varying signals. You can also perform DC calculations. The following figure shows the collector current for various base currents on a bipolar transistor. Each point on the curve is an independent DC reading with some time given between the setting the input and reading the output. You can find the associated code here.


In the stock SLab system you are limited to the 3.3V range of the Nucleo board. But using the two DACs in bridge configuration can give a -3.3V to +3.3V range in some circuits. This is the case of the full bridge rectifier:

Full Bridge Circuit
The 100k R2 resistor provides some bias on the outputs when no diode is conducting. You can neglect it to ease the understanding of the circuit. The measured input to output voltage response is shown in the following figure:

Bridge Voltage Curve
You can find the code here.



The documentation

We have a hardware board. We have the firmware on the board and the Python code on the PC. If you know enough about electronics and want to use the SLab system, you only need to add in the SLab reference documentation and you are on the track for messing with circuits.

The current documentation to setup the hardware and the software and to reference all the SLab functions (there are a lot of them) is 239 pages in several PDF files. The Python code folder, also includes a folder with 31 examples in self contained Python code files. 

But one of the SLab project objectives was about learning electronics. That's why the SLab project also contains 222 pages of tutorials about several devices and circuits. Currenty the subjets are:
  • Linear Operational Amplifier circuits (7 files)
  • Diode circuits (4 files)
  • BJT circuits (2 files)
I will keep adding files when I have time. Perhaps I will also add some experiments on the blog.

Last words (for now)

SLab is an ongoing project. The board firware will be improved in the future and more tutorials about circuits will be added. Now we are on the first release version 1.2. All the files can be obtained in Github or in a Zip file, also on Github.

I want to end this blog entry just showing a dual wave arbitrary waveform generated with the SLab system just for fun.


Isn't it pretty? Wanna see the code?

Article updates


19/2/2018
Updated the RC filter code. AC functions now are inside the slab_ac module and not in the main slab Python module.
Added a new system description together with security recommendations and a disclaimer.

Saturday, January 17, 2015

Fast ADC on Arduino Leonardo

Last December I wrote about generating fast PWM on the Arduino Leonardo board.
This time I will follow my ideas of going beyond stock vanilla Arduino talking about the ADC.


The problem

You can use the Arduino analogRead function to read an analog value on one of the analog enabled pins. On the Arduino Leonardo there are 12 analog inputs. The typical A0 to A5 six standard Arduino inputs and the additional six lines A6 to A11 that correspond to D4, D6, D8, D9, D10 and D12. 
The ADCs contained in the Atmega MCUs used in Arduino boards are 10 bit converters. To do one conversion the analogRead function takes 100us. It is supposed that the MCU needs that much time to give a 10 bit precision conversion. But 100us, for some applications, can be too much time. It limits, in fact, the maximum analog sample frequency to 10kHz and this doesn't take into account the time needed to process the read data.

You can do better. You can do faster. But, as is normal in life, there must be some trade offs. So, if you want to do faster conversions you need to know what are you trading for them.


The ATmega 32u4 ADC

I suspect that other ATmega based Arduinos include a similar ADC, but I don't know for sure. The ADC uses an internal clock that is obtained from the system clock (16MHz on Arduino Leonardo) which is divided by factor between 2 and 128 using an special register setting. 

On the Arduino Leonardo, the divider is set to the maximum 128 value so, at 16MHz master clock frequency we get a 125kHz ADC clock frequency.
To obtain the time needed to do a conversion from the ADC clock frequency we can refer to one table in the ATmega 42u4 datasheet.

Number of cycles to do a conversion
There are three kinds of conversion: First Conversion, Normal Conversions and Auto Triggered Conversion. The ones that apply to analogRead are the two first ones, but the first only applies to the first conversion so we can use the central column (Normal Conversion) as the typical case.

The ADC needs to do two things to provide a conversion: Sample and Hold and SAR Conversion. In the table we find that those needs 1.5 and 13 cycles for a total of 14.4 cycles. As the ADC frequency is 125kHz, the total ADC time is 14.5/125kHz  =  116 us. That's where the about 100us analogRead time comes from.

The MCU datasheet states that we need an ADC frequency between 50kHz and 200kHz to get the maximum 10 bit resolution. With a 200kHz frequency we could shorten the total ADC time to 72,5us. Unfortunately the clock divider option next to 128 is 64 so, we cannot select 200kHz. We can only select 125kHz, 250kHz... Any option beyond 125kHz, using a 16MHz master clock, goes beyond the Atmel recommendations for the MCU.

Separating the two things the ADC does we get 12us Sample and Hold time and 104us SAR Conversion time. If we increase the ADC frequency we could shorten those times but. We know that we won't get maximum resolution but, ¿What are exactly the side effecs?


Sample and Hold

To understand the Sample and Hold we can use one of the figures in the ATmega 32u4 datasheet:

The Sample and Hold (S&H) circuit is composed on a switch, a resistor and a capacitor. At the start of the conversion the switch is closed during the sample time (1.5 ADC clock cycles). After that, the switch is open and the ADC SAR circuit does the real conversion. The S&H circuit is important because the signal should not change during the SAR conversion. That is guaranteed by the fact that the switch is open during the conversion.

One fundamental point about S&H circuits is that the capacitor need to be charged to the input voltage, during the sample time, before the conversion starts. As this is an RC circuit, in theory, the capacitor voltage will never reach the input one. In practice we only need to get as close to the input voltage as is needed for the converter resolution. For a 10 bit ADC, in order to get a negligible error we need to get to 1/2^11 of the ADC reference value that is normally 5V for Arduino boards.

The RC charging, assuming the worst case of capacitor at zero will be modelled by:


In the worst case, the input voltage will be 5V and to meet the required 10 bit precision the input value capacitor value should get to 1/2^11 of the input value.

That will get:


At 125kHz ADC clock operation we know that the sample time is 12us, so the time constant should be 1,57us or less. As the capacitance is 14pF, the total resistance should be below 112 kOhm. As the ADC internal resistance can go up to 100k, that limits the external resistance to about 12kOhm. That is consistent with the fact that the MCU datasheet recommends external resistances to the ADC of 10 kOhm or  less.

The above formula is quite important. When using the ADC. At a 16MHz master frequency the sample time cannot be bigger than 12us so you should not use a total external resistance to the ADC greater than 12 kOhm. Using a greater resistance could limit the ADC resolution lowering the effective number of bits you get from the ADC.

If, for instance, you only need 8 bits because you are discarding the 4 lower bits resulting from the conversion, you only need to have a 5,4 us time constant (sampling time divided by ln(9) ) so, with the same 14pF ADC capacitance you will only need a total resistance below 386k Ohm. Considering the maximum 100k internal ADC resistance you only need to guarantee a maximum of 286k Ohm external resistance.


SAR Conversion

The ATmega 32u4 MCU uses a Succesive Approximation Register (SAR) to do the conversion. This is the most usual kind of converter that can be found on MCUs. These converters get one bit at a time at each clock cycle. So, in order to get a 10bit value, we need 10 clock cycles. From the table found on the datasheet we know that the actual conversion takes 13 cycles. The extra 3 cycles are probably for synchronization tasks.

Increasing the clock frequency during the conversion operation will affect the ADC resolution but the effect is not easy to model although it can be measured. Fortunately someone has done this measurements.


On the Open Music Labs website you can find this two articles:



They are a quite interesting read and they explain more details on the Sample and Hold operation. The links apply to the ATmega 328p but I don't think that the ATmega 32u4 ADC should be much different.

The result is that increasing the ADC frequency reduces the equivalent number of bits (ENOB) of the conversion. Observe that at the minimum stock 125 kHz frequency the ENOB is not 10 bits but something above 9. Also, at 250 and 500 kHz the ENOB is also over 9 but decaying. At 1MHz the ENOB is over 8. For frequencies greater than that the ENOB falls below 8 bits.

Finally we can find the trade offs of using a bigger ADC frequency:
  • The resolution (number of bits) could be limited by the S&H
  • The resolution (number of bits) will be limited during the conversion
The number of bits of an ADC is an integer number, but the Equivalent Number of Bits (ENOB) is not. At 125kHz, if the S&H is not the limiting factor, you will get about 9,5 bits worth of data. That is, the ratio between the maximum value you can convert and the conversion noise will be limited to a 2^9,5 ratio. If you don't need more than 9 bits of real resolution, you can increase the ADC clock frequency up to 500kHz but you must verify that the S&H is also capable to keep with that speed for your ADC external resistance values. For 8 bit operation you could go to 1MHz. That is eight times the original conversion speed.


Example Program

In order to change the ADC speed clock you need to change the ADC Control and Status Register A (ADCSRA).

The 3 lower bits of this register determine the divider used to obtain the ADC clock from the MCU master frequency.


For 111 we get a 128 prescaler that corresponds to the 125 kHz stock Arduino frequency.
For 110 we get a 64 divider that gets a 250kHz clock. For 101 we get 500kHz and for 100 we get 1MHz.

The following example program just does 4 conversions in a row. The first at the stock 125kHz, the second at 250kHz, the third at 500kHz and the last one at 1MHz.



The program also toggles the Arduino pin 3 before and after each call to analogRead so that the total time associated to this function can be measured.

Output at pin 3
From the mesurement we can see how the analogRead function time halves on each call as the ADCSRA register is changed to obtain greater ADC frequencies.

Fortunately the analogRead function itself doesn't modify the ADCSRA register. If that was the case we could not hack the ADC speed without creating a new analogRead function.


Code on Github (11/02/2018)

The  code is now on Github:

https://github.com/R6500/Leonardo/blob/master/FastAdcTest.ino


Monday, December 22, 2014

Fast PWM on Arduino Leonardo

Today I will talk about PWM generation.
The Arduino boards provide pseudo analog outputs using the analogWrite( ) function. This function is not available on all pins, only the ones marked with the ~ symbol. The Analog Write function doesn't provide a real analog output but a PWM signal instead.

Arduino Leonardo Board
A PWM (Pulse Width Modulation) signal is a pulsed binary signal. As it is binary it can only have two output states "HIGH" and "LOW". The analog information is not on the signal levels but on the width of the generated pulses.We define Pulse Width as the width of the HIGH pulses and Duty Cycle, represented with a lower case delta letter, as the fraction of the Pulse Width to the total period T of the signal. The frequency of the PWM signal is defined as the inverse of the period.
PWM Signal

The mean value of the PWM signal depends on the Duty Cycle and the voltage values associated to the HIGH and LOW levels:
If the LOW level is zero, the the mean value of the PWM signal is:

That way, the mean value of the signal is proportional to the Duty Cycle.



Arduino analogWrite( ) function maps a 0 to 255 input value to a 0% to 100% Duty Cycle. As the HIGH level is about 5V and the LOW level is near to zero, the mean value of the signal generated using  analogWrite(pin,x) is:
 
If the device connected to the pin has lower bandwidth than the frequency of the PWM signal, it will work as low pass to the signal an only will see the mean value. Arduino Leonardo PWM pins use frequencies of  488Hz or 976Hz. When you use analogWrite on a LED, as LEDS have usually higher than 1kHz bandwidth, the LED will turn full ON and OFF as indicated on the PWM signal. Our eye, however, has a bandwidth (fusion frequency) up to about 100 Hz depending on the ambient ligh levels. As the LED pulses at a higher frequency than the eye bandwidth, we only see the mean value and seems that the LED lights at intermediate levels between ON and OFF.
The same applies if we use a PWM signal to drive a motor. Normal DC motors, due to their inertia, have bandwidths near or below the Arduino PWM frequency, so the motor works as if a constant variable voltage where applied. In fact, PWM operation at low frequency can make the motor work better at low speeds.

All in all, the 500Hz to 1kHz PWM frequency in stock Arduino is adequate to drive motors. If we want, however, to generate an audio signal, the analogWrite function does not work.The hearing bandwidth in humans is arround 20kHz, much higher than the Arduino PWM frequency. Typical speakers are usually designed inside the human hearing bandwith so applying a PWM signal to them will produce an audible tone at the PWM frequency.

One of the utilities to generate a high frequency PWM signal is going beyond the speaker and hearing bandwidths so that we can use an PWM signal to generate an audio signal.


Stock PWM on Arduino Leonardo

First we start explaining how the PWM is implementated on the Arduino Leonardo board. In most Arduino boards the PWM signals are generated using timers. Timer peripheras provide hardware PWM generation so that the CPU don't need to use any of it's execution resources to generate the signals. Each timer has a limited number of PWM signals than can be generated by hardware. 

The Arduino Leonardo, as the Micro and Esplora use the ATmega42u4 MCU. It includes four timers: Timer 0, Timer 1, Timer 3 and Timer 4. Timers 1 and 3 are equal, but the rest of timers are quite different. Other Arduino Boards make use of other timers provided by the MCUs they use.

The following table shows the timers usage to generate PWM on the Arduino Leonardo board. You can see it and more information on my Arduino Leonardo spreadsheet.

PWM Timers in Arduino Leonardo
Arduino uses the timers for other functionalities. For instance, Timer 0 is also used to record the pass of time as is needed for the functions millis( ), micros( ), delay( ) and delayMicroseconds( ). You don't wanna mess with Timer 0 operation as it will disturb all timing functions. 
Some Arduino libraries make also use of timers. The Servo library uses Timer 1 on the Leonardo while the MsTimer2 library uses Timer 4. You should know that using any library that needs a timer will affect any PWM pin associated to that timer.

There are several two basic ways to create a PWM signal using a timer.

Single Slope PWM

The timers are typically based on a Counter. The counter uses a clock input and, at each active clock edge, the counter changes state. One typical mode of operation for a timer involves increasing the counter at clock each clock edge until it reaches a maximum value. Once this value is reached, the counter returns to zero and the process repeats.That gives a sawtooth waveform on the counter values as is depicted in the figure below. One full cycle of the sawtooth will need Count Max + 1 clock cycles.

Single slope PWM

Timer peripherals usually include several capture/compare registers. In order to create a PWM signal, a compare register is linked to a hardware output of the MCU in a way that makes this output high if the counter is below or equal to the compare register value. That will give, for each compare register, a PWM signal whose duty cycle depends on the compare value. 
All PWM signals generated by the same timer, will have the same frequency as the start of the generated pulses will allways be at the falling edge of the sawtooth signal.
If we have several PWM signals regenerated with different compare registers at the same timer, all of them will have the rising edge at the same times.

The Arduino analogWrite( ) functions operate the ATmega 32u4 Timer 0 at single slope. The 250kHz clock used is obtained dividing by 64 the 16MHz system clock. The final PWM frequency will the be 976Hz.
Timer 0 has two compare channels A and B associated to two compare registers OCR0A and OCR0B. The two channels are linked to two hardware outputs OC0A and OC0B that are used as Arduino pins numbers 11 and 3.

Dual Slope PWM

Another way to generate a PWM signal configures the counter to increase at each clock edge, and when it reaches the maximum value, start decreasing one number each clock cycle until it reaches zero and the patter repeats again. That will give a triangle waveform instead of the sawtooth one.

Dual Slope PWM
We can also configure a capture register so that a generated output signal is high when the counter is below this register. This will generate a PWM signal as in the single slope case. The difference is that, if we have several compare registers, the PWM signal generated with each one will be synchronized at the center of the generated pulses.
This is handy in some kinds of motor control but the details are beyond this article.
In any case, the PWM frequency will be the same for all compare channels and will also be half of the value we would obtain if we used a Single Slope counter.


The Arduino analogWrite( ) functions operate the ATmega 32u4 MCU Timers 1, 3 and 4 in Dual Slope mode. A possible reason why Timer 0 is not operated also in Dual Slope can probably be due to the fact that Timer 0 is used to record the pass of time and, this use, is difficult to be implemented on a dual slope timer. Working on dual slope, all PWM pins associated to those pins use half the Timer 0 frequency working at 488Hz.
Timer 1 includes three compare channels A, B and C associated to compare registers OCR1A, OCR1B, OCR1C linked to hardware outputs OC1A, OC1B and OC1C. The Arduino software only uses the channels A and B that are related to Arduino pins 9 and 10. Channel C output OC1C is associated to the same 11 Arduino Pin driven by Timer 0. For whatever reason Arduino designers preferred to drive pin 11 using Timer 0 at single slope than use Timer 1 at dual slope.
Timer 3 only includes one hardware output OC3A at Arduino pin 5, associated to the compare register OCR3A.
Timer 4 is a special timer, but respect to PWM, it operates as Timers 1 and 3 in dual slope mode. It includes three hardware outputs OC4A, OC4B and OC4D associated to registers OCR4A, OCR4B and OCR4D. Only channels A and D are used in stock Arduino for pins 13 and 6. Channel B is associated to the same pin driven by OC1B so Timer 1, instead of Timer 4, is used for this pin.
All of that gives the table of the 7 PWM enabled Arduino Pins.


Fast PWM on Timer 1

If the maximum 976 Hz that stock Arduino analogWrite is not enough, we need to develop our own PWM functionality. Timer 1 is a good candidate as it has three available compare channels and don't mess with Arduino delay functions. You should take care if you use the Servo library because it also uses Timer 1.

Timer 1 is based arround a 16 bit counter. That means that it can count from 0 up to 65535 before overflowing. The timer has several modes of operations that include 12 PWM modes. The fastest PWM mode available is single slope 8 bits counting between 0 and 255. As single slope is faster that dual slope, it is also called Fast PWM mode.
You can also have 9 bits and 10 bits PWM modes with 511 and 1023 terminal counts that can operate on single and dual slope modes. Three bit modes 8, 9 and 10 for single and dual slope PWM gives a total of 6 PWM modes. The other 6 additional PWM modes use programmable terminal counts that can be any 16 bit value and is not restricted to 255, 511 or 1023.

The timer gets its clock from the global MCU prescaler that provides five frequencies from the system clock. The divide ratios are 1, 8, 64, 256 and 1024. That gives, using the 16MHz Arduino Leonardo system clock, the fiveclock and single slope frequencies shown below for standard 8, 9 and 19 bit modes:

Prescaler and PWM options
Using dual slope the maximum frequency will be half. We see that the maximum available PWM frequency on Timer 1 is 62.5kHz. That's enough to generate some sort of audio signal as it is beyond the audible range of frequencies.

In order to configure the timer we must program the Timer 1 registers TCCR1A and TCCR1B.


Bits 0, 1 and 2 of TCCR1B (CS10, CS11 and CS12) configure the clock options acording to the following table.


Bit 1 of TCCR1A (WGM11) and bits 3 and 4 of TCCR1B configure the waveform for the timer acording to the table:


Modes  1, 2, 3, 5, 6 and 7 correspond to the 6 standard PWM modes.

Once the timer is configured on one PWM mode, each of the three compare channels A, B and C can be enabled to generate a PWM signal. To do that, the COM1x0 an COM1x1 bits on register TCCR1A associated to a particular x channel (A, B, C) need to be configured according to the table:


The PWM value for each channel should be programmed in each channel compare register OCR1A, OCR1B and OCR1C.

All the above tables habe been taken from the ATmega32u4 datasheet.

Configuring the TCCR1A bits is not enough to generate the PWM signal at the output pins. We also need to configure them in output mode. From the ATmega32u4 we can see that the timer 1 outputs OC1A, OC1B and OC1C  are associated to port lines PB5, PB6 and PB7 (Arduino Pins 9, 10 and 11). We need to set those pins in output mode using the Port B Data Direction Register DDRB.


Fast PWM on Timer 4

Using Timers 0, 1 or 3  we can have up to 62,5kHz PWM signals, but the maximum possible PWM frequency is only available on Timer 4.
Timer 4 is a 10 bit timer that can operate at very fast speed due to its clock source options.
The ATmega32u4 MCU includes an USB peripheral. This peripheral needs a 48MHz clock frequency that goes beyond the maximum 16MHz system clock. In order to generate the USB frequency the MCU incorporates an internal PLL. In the Arduino Leonardo the PLL takes as input the 16MHz system clock and multiplies it by 6 to generate a 96MHz output frequency.
You don't want to mess with the PLL configuration on the Arduino Leonarda as it will break the USB communications. The only need you need to know about the PLL is that its output can source the Timer 4 peripheral.

The PLL register PLLFRQ includes among other things, two bits PLLTM0 and PLLTM1 that determine the input clock to Timer 4.


There are four options, don't use the PLL to source the timer (it will be sourced by system clock) or to use the PLL output divided by 1, 1.5 or 2. That will give 96MHz, 64MHz or 48MHz clock frequencies.


At the time of this writting the above table was missing from the official ATmega32u4 current datasheet. I needed to find an older datasheet to obtain the full PLL configuration register contents.

Timer 4 has an additional divider configured with bits CS40 to CS43 of the TCCR4B configuration register.

The available options are:


 That way, using the PLL and Timer 4 dividers you can have input frequencies betwen 96MHz and 5859Hz.

In a similar way than Timer 1, Timer 4 must select a waveform operation mode using bits WGM40 and WGM41 of the TCCR4D register.

The available options are:


Mode 00 is Single Slope while mode 01 is Dual Slope.

Observe that the PWM signal has a maximum count value defined in OCR4C register. As compare channel C register is used for the terminal count, there is no independent channel C PWM output.

PWM6 is an special PWM operation mode that uses all three available channels A, B and D to drive a motor. The details, that are beyond the scope of this document, can be found on the MCU datasheet.

After configuring the clock input, each channel x = A, B and D of Timer 4, can be configured with its own bits COM4x0, COM4x1 and PWM4x at registers TCCR4A and TCCR4C.
As we have explained channel C, used as terminal count, has no output unit so it cannot be used to generate PWM.

To operate one x channel in PWM mode you need to set to "1" the  corresponding PWM4x bit. After that, the COM4x0 and COM4x1 determine the mode of PWM operation. In the case of channel A in fast PWM mode we can choose:


Normal PWM operation corresponds to mode 10. Mode 11 will give a complementary output whereas mode 01 gives two complementary PWM outputs at different pins. The complementary signals can have some deadband between them so that one signal, and its complement are never active at the same time.
Similar tables can be found for channels B and D.

Fast PWM Test Code

I have put together all the above methods to generate PWM in a FastPWM Arduino sketch.



The sketch includes the needed code to operate 5 pins associated to Timers 1 and 4 in fast PWM modes.

Timer 1 Code

The code associated to timer 1 includes 4 functions and several defines. It can uses all three Timer 1 compare channels to generate PWM signals at Arduino pins 9, 10 and 11.

The pwm91011configure function must be called previously to the call of any other function associated to this timer. It configures the timer to operate in single slope fast PWM mode and sets the prescaler to the mode indicated on the function argument.
The five possible modes are  PWM62k,  PWM8k, PWM1k, PWM244 and PWM61 and are associated to the four available frequencies in single slope 8 bit PWM modes.

Functions pwmSet9, pwmSet10 and pwmSet11 configure the Timer 1 channels associated to pins 9, 10 and 11 to work in PWM mode and set the given PWM value (0 to 255).

After one of the three above functions is called, the PWM value can be fast changed using a direct access to the corresponding compare register. To ease the access, three definitions PWM9, PWM10 and PWM11 are associated to compare registers OCR1A, OCR1B and OCR1C.


Timer 4 Code
The code associated to timer 4 includes 3 functions and several defines also. It uses two of the three available compare channels A and D associted to Arduino pins 13 and 6. It doesn't use channel B because its Arduino pin 10 conflicts with the Timer 1 channel B previously used.
The pwm613configure function is similar to the one defined for Timer 1. It sets the PLL and PWM modes to properly configure Timer 4 in high speed mode from the 48MHz PLL tap and sets the terminal count in OCR4C to 8 bits (255). 
The input argument sets one of seven available prescaler frequencies for a 48MHz timer 4 clock input: PWM187k (187500 Hz), PWM94k  (93750 Hz), PWM47k  (46875 Hz), PWM23k  (23437 Hz), PWM12k  (11719 Hz), PWM6k   (5859 Hz) and  PWM3k  (2930 Hz). If you remenber the Timer4 options there where 15 clock prescaler options. The lower frequency ones are not implemented in the defines as they provide no advantage over stock analogWrite functionality.

Functions pwmSet13 and pwmSet6 configure the Timer 4 channels associated to pins 13, 6 to work in PWM mode and set the given PWM value (0 to 255).
In a similar way than timer 1, after one of the two above functions is called, the PWM value can be fast changed using a direct access to the corresponding compare register. Two definitions PWM13, and PWM6 are associated to compare registers OCR4A and OCR4D. An aditional PWM6_13_MAX definition has been added to access the OCR4C register that sets the PWM terminal count that is, by default, set to 255 (8 bits).


Setup and Loop Code

To check all the above functions Timer 1 is configured in setup to generate 62,5kHz PWM signals.  Timer 4 is configured for 187kHz. 4 signals are generated. At pins 11 and 13 11% and 75% fixed PWM values are set.  For pins 6 and 9, a variable 0% to 100% PWM value is programmed inside the loop function.

Generated Waveforms

The following figure shows the generated waveforms captured with the Logic Analizer of the Analog Discovery scope.


Timer 1 signals at pins 9 and 11 have the same risign edge as they are in single ramp PWM mode. The same applies to signals at pins 6 and 13 that depend on Timer 4. Signals at different timers are at different frequency so the rising edges don't usually coincide for both timer signals.

That's all for now. Timers are very usefull peripherals. PWM is only one of the timer applications. In the future I plan to talk also about generation of periodic events.


Code on Github (11/02/2018)

The code is now on Github:

https://github.com/R6500/Leonardo/blob/master/FastPWM.ino