If only I had one more [insert function here]!

Introduction

There is one essential difference between embedded programmers and general computing programmers, and it has to do with the amazing variety of hardware we embedded programmers get to play with. I like to think of us as test pilots. While the larger community of pilots fly the same airplane and often the same routes every day, we enjoy a variety of machines and configurations that change often, searching for optimal cost, speed and utility in response to the market needs and/or our customers’ desires. As a result, our job is never boring – although, at times, we wish it was. Hence our somewhat bipolar approach to innovation: On one side, we try to find any excuse to play with the latest and greatest product/model prizing innovation and revolutionary advancements. On the other hand, we hate the learning curve that comes with it, and we wish everybody would use the same compiler, library and tools we just got familiar with and we learned to love… Only incremental changes, please…

Take Your Pick

We are in the year 2018, and when it comes to microcontrollers’ variety, there is no doubt in anyone’s mind that we have more than enough to choose from. We can pick any number of pins, any number of bits, any architecture and almost any combination of Flash and RAM memory. In fact, much of the competition among vendors in recent years has been focused on adding more of the above and expanding the resulting matrix of choices.

Some will argue that given sufficient clock speed and program memory space, any application can be written using only a bunch of general purpose I/Os (and I’ll throw in an ADC and a PWM for good measure). This sounds almost like a trivial corollary of Alan Turing’s original work on the General Purpose Computing Machine, extended to serve our embedded use case.

But, hopefully, we all agree that that this is not a sensible or practical strategy. After all, Alan Turing himself never built or used one of the machines that carry his name today.

Hopefully we all agree the embedded applications we develop today need a rich set of peripherals (functions) and this complicates matters, expanding the matrix, quite a bit.

Bait-and-Switch

So, even in 2018, in the world of ample embedded control choices, it is not uncommon for us designers to start working on a project using a given microcontroller model, only to realize we have to switch to a different one mid-project. Mind you, this is not for a lack of trying – it happens to lazy engineers who skipped proper analysis at the start and also to those who have done ample preparation and analysis. It is more often a matter of complexity — much like in a chess game where we can only play so many moves ahead in our mind. It can be hard to see how the peripheral functions interact with each other as we put them to use in our applications, or it may be hard to predict the evolution of the hardware solution as the project requirements drift (as they always do) and deadlines loom ahead.

At best, a microcontroller change can be a minor disruption, and we are lucky enough to find a close match in the same family of devices from the same vendor. Things get more difficult when that option is not available, forcing us to change the device family, which often produces a domino effect as pinout changes and possibly other peripheral functions follow.

Core Independent Peripherals to the Rescue

The best way to deal with these types of issues is to plan ahead for them. Most recent microcontroller families do provide smart ways to rearrange the pins for each of the peripherals offered. I am not referring to a single alternate pin function (that is so 90’s) – I am talking about full pinout remapping for analog and digital functions. For example, in the Microchip portfolio of 8-, 16- and 32-bit microcontrollers this is known as the Peripheral Pin Select (PPS), every pin on the device will be able to operate as an input for any number of peripherals (up to 64 different ones in some models), and any peripheral output can be published on up to 16 different pins (even 32 in some models). Analog inputs to the ADC will be similarly multiplexed among the vast majority of the device pins (up to 35 out of 40 pins, for example).

While this is already great insurance against future migration incompatibilities, the real game changers are more flexible approaches such as the Core Independent Peripherals (CIPs) in Microchip devices. Keep in mind, this is not the primary reason CIPs were created. The goal of Core Independent Peripherals was to reduce the workload of the microcontroller CPU by automating as many of the low level and coordination activities normally required by traditional peripheral functions as possible. But the extra connection available and the flexible nature of the CIPs make them a perfect tool to solve the mid-project crisis.

A practical example: creating a new PWM function out of thin air

For a practical example, I want to bring your attention to a case that occurred recently. We were designing an enhanced version of an automotive sensor, which involved the use of several timers, a pair of 10-bit Pulse Width Modulation (PWM) modules and an analog comparator, all easily found in a humble 28-pin PIC16F18855 microcontroller. We quickly realized that we reached a hard limit in the resolution of our current design, and if we proceeded any further, we would need a 12-bit or higher resolution PWM register. Unfortunately, that option was not on the menu. Any similar 8-bit PIC microcontroller that shared a compatible form factor (and pinout) was either offering too small a program memory or adding a much larger/richer set of peripherals and higher cost. All we needed was one more PWM register with 12-bit or better resolution.

Luckily, PWM peripheral functions are quite simple to synthetize on a chip if you have a few flexible blocks and a bit of glue logic. Any PWM can be assembled by combining two elements in particular: a periodic pulse and a limit counter. In Figure 1 you can see how the two can be combined to provide the period and the duty cycle of the resulting square wave.


Figure 1 – A PWM as the sum of a base timer and a limit counter (Source: Microchip Technology)

Setting the Time Base

The time base can be implemented with any standard timer as long as it can produce a periodic pulse with the desired frequency (~500 Hz in our case). For our application, the timer can be an 8-bit basic timer/counter and can be shared with other functions (PWMs or otherwise). In our application, we reused Timer2, a basic (8-bit) timer with a match register (T2PR). The configuration of the Timer2 module as the base timer requires only a pair of registers to be initialized as illustrated in Listing 1.

void TMR2_Initialize(void){    // T2CKPS 1:64; T2OUTPS 1:1; TMR2ON off    T2CON = 0x60;    // T2CS FOSC/4    T2CLKCON = 0x01;    // T2 period     T2PR = 0xFF;    // Clearing Interrupt flag    PIR4bits.TMR2IF = 0;    // Start TMR2    T2CONbits.TMR2ON = 1;}

Listing 1: Timer2 basic initialization 

The traditional way to develop such an initialization routine would require us to dig into the lengthy datasheet of the PIC microcontroller of choice and carefully identify the bit patterns required for each of the registers. This can be a lengthy and error-prone process, even for a simple peripheral. The better way to proceed nowadays is to use the MPLAB Code Configurator, a rapid development tool (plugin) integrated in the MPLAB X IDE that allows us to generate the same code (an initialization routine very similar to Listing 1) with only a few mouse clicks. In fact, the tool is often used to discover device features and experiment quickly with the many options offered by the Core Independent Peripherals.

In figure 2, you can see how all the options available for Timer2 are laid out for us to pick from multiple selection boxes and entry fields.

click for larger image

Figure 2 – MPLAB Code Configurator (MCC) Dialog Window for Timer 2 configuration (Source: Microchip Technology)

Configuring the Limit Counter

The limit counter is a little more sophisticated function, but not an unreasonably complex or rare one. Once triggered, this timer needs to be able to count up to a limit value (as the name implies), generate a pulse and stop. It will reset and start again only upon the next trigger input. This function is often referred to as a retriggerable monostable , or in the PIC literature it is known as a Hardware Limit Timer (HLT).

Unfortunately, if we desire a 12-bit or higher resolution on the PWM duty cycle, the typical HLT available on a PIC microcontroller is not going to cut it. On modern PIC microcontrollers, even numbered timers offer the HLT function but provide only an 8-bit counter. However, there is another less-known peripheral that can get us the resolution we crave.

click for larger image

Figure 3 – Block diagram of a Signal Measurement Timer (SMT) (Source: Microchip Technology)

This is the Signal Measurement Timer (SMT) represented in Figure 3.

If the HLT is little more than a timer with one extra trigger/reset input, the SMT is essentially a timer with two such inputs. It happens that there are a number of interesting ways to combine those two inputs to obtain a variety of time, frequency and duty cycle measurements — hence the name.

The device datasheet lists as many as 11 such modes, which is enough to scare most novice users and make the SMT the most misunderstood and under-utilized among the CIPs. I have no room here to dive into the details of all the modes, but it will suffice to mention that one of them – perhaps misleadingly named Window Measurement Mode – essentially turns the SMT into a HLT. In fact, it’s a very large HLT, as the SMT has a 24-bit counter and a period register of matching size.

Once again, the configuration of the Signal Measurement Timer (SMT1) can be significantly simplified by using the MCC tool, as illustrated in Figure 4.

click for larger image

Figure 4 – MCC Dialog Window for the SMT1 module (Source: Microchip Technology)

With a few mouse clicks, we can automatically generate an initialization function similar to Listing 2. In fact, that is precisely how this function was generated in the first place. A few comments were streamlined later for the sake of brevity and clarity.

void SMT1_Initialize(void){    // WPOL high/rising edge enabled; SMT1EN enabled; SMT1PS 1:1 Prescaler    SMT1CON0 = 0x80;    // SMT1MODE Windowed measure; SMT1GO disabled;    SMT1CON1 = 0x44;    // SMT1CSEL HFINTOSC    SMT1CLK = 0x02;    // SMT1WSEL TMR2_postscaled;    SMT1WIN = 0x05;}

Listing 2 – SMT1 module initialization function

Configuring the S/R Latch

Combining the base timer and the limit counter is the job of a set/reset (S/R) latch. This function is available on many PIC microcontrollers, but in the past five years it has been integrated as one of the eight functions provided by the Configurable Logic Cells (CLCs), a staple among the Core Independent Peripherals (CIPs).

Figure 5 illustrates how the two signals are fed to the S/R latch inputs. In fact, the image has been derived directly from the MPLAB Code Configurator dialog window for CLC modules.

click for larger image

Figure 5: Configuring the S/R Latch using MPLAB Code Configurator (Source: Microchip Technology)

Whether you are using the rapid development tool (MCC) or configuring the peripheral by hand, the result will be an equally small initialization function that contains only a few assignments to the corresponding registers of the peripheral (as in Listing 3).

Because the PIC16F188xx devices used in our application offer two SMT modules, we would be able to synthetize not one, but two new PWM modules with as much as double the resolution required.

void CLC1_Initialize(void){    // LC1D1S TMR2=T2PR    CLC1SEL0 = 0x0C;    // LC1D4S SMT1_OUT    CLC1SEL3 = 0x11;    // LC1G1D1T enabled    CLC1GLS0 = 0x02;    // LC1G4D4T enabled    CLC1GLS3 = 0x80;    // LC1EN enabled; MODE SR latch    CLC1CON = 0x83;} 

Listing 3: CLC1 module configuration as S/R latch

A few more lines of code will complete our new PWM configuration and allow us to conveniently control its duty cycle. In Listing 4, we implemented the peripheral initialization by simply stringing together the initialization functions of the three peripherals used. Starting and stopping of the new PWM is now controlled by the limit counter (SMT1), and as planned, the duty cycle is controlled by the 24-bit SMT period register. As a nice touch, a simple synchronization loop was added before the duty cycle value is modified to avoid output glitches.

Void PWM_Initialize(){    TMR2_Iniitialize();    SMT1_Initialize();    CLC1_Initialize();}void PWM_Start(){    SMT1CON1bits.SMT1GO = 1;}void PWM_Stop(){    SMT1CON1bits.SMT1GO = 0;}void PWM_DutyCycleSet(uint32_t dc){    while (CLC1_OutputStatusGet()==1);    SMT1_SetPeriod(dc);}

Listing 4: The new PWM module configuration and control functions

PWM Effective Resolution

It is a common misconception among novice users that the resolution of a PWM register is simply given by the nominal number of bits of the timer controlling the duration of the on pulse (duty cycle). By that definition, we would have just assembled a 24-bit PWM function – but that is not true.

The fact is, period and duty cycle of a PWM are not fully independent quantities: They are both controlled by timers/counters as we have seen above, and there is a common relationship with the peripheral clock of the microcontroller.

For any given peripheral clock, the counter that is responsible for the on time will only be able to count up to a MaxCount value before the time base period repeats. At that point the PWM register will have reached 100 percent duty cycle and no additional number of bits in its register will ever be used.  The formula below determines the effective resolution of the newly constructed PWM function in our previous example:

MaxCount =  Tperiod * Fclock

Or in our case for Tperiod = 2.048 ms and a peripheral clock frequency of 32 MHz

(Note that the SMT can be clocked directly with the maximum system oscillator frequency, and is not limited to the CPU instruction clock, which is divided by 4, as most other PIC peripherals)

Max Count = 65,356

The logarithm in base two of the MaxCount value is the effective resolution of our PWM in bits, which just happens to be exactly 16 bits.

This result is a confirmation that the new PWM module is indeed more accurate than the PIC standard 10-bit PWM modules, and in fact exceeds our original goal.

Summary

Notice how we assembled a new and effective 16-bit PWM function without using any additional external component(s). Instead, we simply configured, inter-connected and re-used a small number of functions available on the chip. Perhaps even more of note, we can replace the newly-constructed one in the pre-existing solution without modifying the board layout, as the Peripheral Pin Select (PPS) feature allows us to deploy the new function on any pin(s). Further, the newly constructed module is behaving as a fully autonomous function (the nature of CIPs), as no processor time is required to maintain its continuous operation aside from the initial set up and occasional duty cycle adjustment.

Additional Information

  1. 1.    PIC16F18855 datasheet. http://www.microchip.com/wwwproducts/en/PIC16F18855

  2. MPLAB Code Configurator. http://microchip.com/mcc

  3. CIPs: www.microchip.com/cip


Lucio Di Jasio is the EMEA Business Development Manager for Microchip Technology Inc. He has been covering various technical and marketing roles within the company’s 8-, 16- and 32-bit divisions for the past 18 years. As an opinionated and prolific technical author, Lucio has published numerous articles and several books on programming for embedded control applications. Following his passion for flying, he has achieved both FAA and EASA private pilot license certifications.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.