Design Solutions Research & Design Hub

Hacking MCU Pins

Written by Stuart Ball

Repurposing Peripherals

Today’s MCUs integrate a rich assortment of functionality. But sometimes there are one or more you wish you had that aren’t available on the device you’re using. Here, Stuart explains how to configure MCU pins to enable you to use on-chip peripherals to perform tasks for which they weren’t intended.

  • How to configure MCU pins to enable you to use on-chip peripherals to new tasks

  • How to use a UART for PWM

  • How to generate an interrupt using the UART input

  • How to use a UART or SPI as a timer

  • Texas Instruments (TI) TM4C1233H6 MCU

  • TI’s Code Composer Studio

  • TM4C TivaWare library from TI

Modern microcontrollers (MCUs) have a lot of functionality packed into a single IC. But sometimes you need one more function than the device has. You might have a simple 8-bit MCU, but it lacks pulse width modulation (PWM) output and you find you need that capability. Or maybe you need one more interrupt than the device provides on the available pins. It could be that you have an existing design and you don’t want to redesign it with a different device just to get some simple feature. In some cases, you can use the peripherals to do things for which they weren’t originally intended. I’ll look at a few of those here.


Most MCUs have one or more embedded UARTs. Usually, the UART pins can be used as general purpose I/O (GPIO) pins. But what if you need a PWM output instead of a GPIO pin? As it turns out, a UART can be used for a low-resolution PWM output.

Asynchronous output: Figure 1 shows the asynchronous waveform for a UART output. An asynchronous signal from a UART does not transmit a clock with the signal. Instead, the transmitter and receiver use the same fixed bit rate, and the transmission is framed by stop and start bits. The asynchronous signal starts in the high state when nothing is being transmitted. When a byte is written to the UART, a start bit of 0 is transmitted, followed by the 8 bits of the byte and then a stop bit of 1 returns the UART output to its original idle state. A second byte can be written to the UART while the first byte is being transmitted, and will transmit immediately after the first stop bit. UARTs send the least significant bit first, so if you send a byte of 0x01, then the one will follow the start bit.

Figure 1 Asynchronous serial timing. The byte being transmitted is 0x43, binary 01000011.
Figure 1
Asynchronous serial timing. The byte being transmitted is 0x43, binary 01000011.

Most MCUs allow you to program 5, 6, 7 or 8 data bits and 1 or 2 stop bits. The baud rate, which is the spacing between the individual bits, can also be programmed. Note that the baud rate and the data transfer bit rate are not the same. Sending one byte requires 8 data bits, a start bit and at least one stop bit. So, it takes a minimum of 10 bits (or baud) periods to send 8 bits of data.

Typical baud rates are 9600 (104µs per bit) to 115200 (8.7µs per bit). Although a word can be 5- to 8-bits long, I’ll stick with 8-bit words here. Also, most UARTs can be configured to send a parity bit. I’ll ignore that as well.

Generating PWM: If you send a zero to the UART, you get the waveform shown in Figure 2. This is 9 bits of zero (the start bit followed by 8 bits of zero), followed by the stop bit, which is 1. If the baud rate is 9600, then you have a low period of 937µs, followed by a high period of 104µs. This corresponds to a low time of 90% of the total 10-bit waveform, and a high time of 10%.

Figure 2 Asynchronous transmission of byte 00. Only the stop bit is high.
Figure 2
Asynchronous transmission of byte 00. Only the stop bit is high.

If you set the most significant bit of the transmitted byte to 1, you get the waveform shown in Figure 3. This results in 8 zero bits (start bit plus 7 data bits) followed by two bits of 1 (1 data bit plus the stop bit). The low period is now 80% of the total waveform. Similarly, we can send the values shown in Table 1 to get the corresponding duty cycles (where zero is “on”). Table 1 provides a fixed-frequency output with the total period always equal to 10-bit periods. The PWM duty cycle ranges from 10% to 90%. You can extend the range from 0% to 90% by stopping transmissions. If nothing is written to the UART, the output will remain in the idle state, which is high.

Figure 3 Asynchronous transmission of byte 0x80. The stop bit and the MS bit of the byte are high.
Figure 3
Asynchronous transmission of byte 0x80. The stop bit and the MS bit of the byte are high.
Figure 4 Schematic of the circuit used to demonstrate the principles described in the article
Figure 4
Schematic of the circuit used to demonstrate the principles described in the article

Figure 4 shows a circuit based on the Texas Instruments (TI) TM4C1233H6 MCU [1] that implements this technique to drive an LED. Transistor Q1 drives LED D3 from UART1 of the MCU. (This particular part has eight UARTs.) The software steps through the PWM values once per second. If you try this with the circuit shown, or any MCU with a UART output, you will see the LED slowly dim and then go to full brightness as the PWM cycle starts over.

Note that I have defined the low portion of the period as the “on” state, and the LED is driven by a PNP transistor (Figure 4). This is so the PWM output can be turned off by stopping transmission. If you will never need to turn it off, you could let the high state equal PWM “on,” and then your PWM would operate from 10% to 100% of the waveform period. (In this case, stopping transmission would be 100% on and 10% would result from sending all zeros.)

Figure 4 Schematic of the circuit used to demonstrate the principles described in the article
Figure 4
Schematic of the circuit used to demonstrate the principles described in the article

Applications: This is not a high-resolution PWM driver. But it would be suitable for controlling LED brightness, fan speed, a heater or similar applications. Higher PWM resolution can be approximated by alternating between two values. If you continuously send 0x80, then 0xC0, then 0x80, it will approximate a PWM value of 75% (halfway between 70% and 80%). This only works if whatever you are controlling will respond to that waveform. An LED, heater or fan will, but some other things won’t.

DMA: The TI TM4C1233H6 has an internal DMA controller that can transfer data to some of the internal UARTS. You could set up a memory area with a series of different PWM values, and sequentially send those to the UART to generate complex waveforms. Having two such buffers allows you to fill one while sending the other. This obviously only works on a device with a DMA controller that can interoperate with the UART.

Limitations: Using a UART for PWM does have some limitations. As mentioned, the resolution is limited to 10% steps (unless you use the approximation technique), and you can’t easily get 0% to 100% duty cycle; you’ll have to give up 10% at one end or the other. You are limited to the available baud rates. On some devices, you have to pick standard baud rates (1200, 2400, 4800, 9600 and so on). On others, the baud rate is selected as a binary division of the clock, so you can select any frequency up to the limit that the clock divisor will allow.

One significant difference between using a UART for PWM and using a dedicated PWM peripheral is that you have to keep feeding the UART with data. You can’t just set the PWM values and let it run. So, as long as you need to generate the PWM waveform, you need to be polling or using an interrupt to determine when the UART transmitter is ready for another byte. Using DMA, if provided, can relieve some of this workload by transferring a large block of bytes, but using a UART for PWM requires the software to interact with it periodically. Many MCU UARTs have FIFOs that will ameliorate this. UART FIFOs on the TM4C1233H6 are 16 bytes deep.


Similar to the UART, an SSI (synchronous serial interface) output can be configured to generate PWM. The outputs are called SSI on the TM4C1233H6, but they can generate the SPI (serial peripheral interface) or Microwire output waveform, which is how it is often named on other parts. Normally, an SSI or SPI output is accompanied by a clock signal to clock bits into some external device, such as a digital potentiometer, a flash memory or a digital-to-analog converter (ADC). One bit of the transmitted word is sent for each clock.

Terminology note: Although the terms “master” and “slave” have long be used in the electronics industry, those terms currently are discouraged for obvious and valid social reasons. The industry as a whole has not yet come to any agreement on replacement terms. For this article we’re using the term “controller” to replace “master,” and “remote” to replace “slave.”

The clock is generated when the SSI is in controller mode, but there is no actual requirement to connect the clock to anything. In my article “Build a Composite Video Text Terminal” (Circuit Cellar 356, March 2020) [2], I used an SSI output to generate a video signal. The SSI peripheral was happily generating a clock output that wasn’t connected to anything; only the data output was used.

If the clock is not connected, then the SSI output functionally looks like a UART output, except that the SSI output, normally being synchronous, doesn’t need the start and stop bits. In the TM4C1233H6, the SSI can be configured to be compatible with different SSI/SPI formats, including NXP’s (formerly Motorola) and TI’s. It can also be programmed for a word length from 4 to 16 bits. This allows for twice the resolution of the UART PWM signal, allowing words of 0x8000, 0xC000 up to 0xFFFFF to be transmitted, or a total of 16 values instead of 8. Therefore, the resolution is 1/16 of the total waveform time. Since there is no requirement for start or stop bits, the full duty cycle range of 0% to 100% can be used.

The basic principle of driving PWM with SSI is the same as using the UART. In Figure 4, transistor Q2 drives LED D4 using the output from the SSI0 output of the device. In this case, I made the “on” state a high voltage instead of low voltage. I did that because there is no need to return to an idle state of 1 when not sending. So, Q2 is an NPN transistor instead of PNP, and the collector goes low to turn on the LED.

Note that on the SSI output, the bits are sent MSB first instead of LSB first. Since there is no start or stop bit, the output word can be in either order. So, 1/16 duty cycle can be sent as either 0x8000 or 0x0001, 1/8 duty cycle can be 0xC000 or 0x0003 and so on.

Like the UART, some of the TM4C1233H6 SSI peripherals can interoperate with the DMA, allowing waveforms to be sent from memory. Also, like the UART, you can use the same approximation technique to simulate a higher resolution. You can also double the resolution by sending two 16-bit words for one PWM value (making a 32-bit PWM word). To keep the same frame rate, you would need to double the clock rate if you do this. But making the output 32-bits wide doubles the resolution.

Configuring the SSI/SPI will vary from one device to the next, but on the TM4C1233H6, the process is:

1) Enable SSI peripheral
2) Assign pins to the SSI functionality
3) Program clock rate, word width, select controller mode and SSI format
4) Enable SSI operation

If you use a different device, configuration will almost certainly be different. In all cases, the SSI has to be in controller mode for this to work, unless you have an external clock for SSI bit timing.

Limitations: If your MCU doesn’t support 16-bit SSI, you can still use this technique to generate 16-bit words. You just have to send two 8-bit values (or four 8-bit values, if you want the higher resolution of using a 32-bit PWM period).

Using SSI for PWM eliminates some of the limitations of the UART method. Because there is no requirement for a start or stop bit, you can make the transmitted words longer to get better resolution. Generally, you won’t be limited to specific baud rates, though some MCUs provide a limited range of SSI frequencies.

Like the UART method of generating PWM, using an SSI output requires the software to send data continuously as the transmitter is ready for it. Again, as with the UART method, FIFOs on the SSI, if available, will help. SSI FIFOs on the TM4C1233H6 are 8 words deep.


Many modern MCUs can designate many, if not most, of the GPIO pins as interrupts. But sometimes you need an extra interrupt you didn’t plan for, or, for some reason, you just don’t want to switch to a different device. If you have an extra UART (maybe you are using the transmit portion of the UART for a PWM output), you can generate an interrupt using the UART input.

To do this, you would enable the UART receiver, and configure it to generate an interrupt when a new byte is received. You would enable the interrupt and provide an ISR (interrupt service routine) to be executed when it triggers. You then drive the UART input with the signal that needs to generate an interrupt.

All UARTs use a start bit to indicate the start of transmission. So as long as the input stays low long enough to be recognized by the UART as a start bit, the UART will receive a byte whenever the input goes low, generating an interrupt. The faster you make the baud rate, the shorter the input signal can be, and still generate an interrupt. When the interrupt occurs, you would service it like any other interrupt, except that you will also need to read and discard the bogus incoming serial byte.

In Figure 4, the UART1 input is grounded with pushbutton switch S2. In this simple implementation, I didn’t generate an interrupt. I just detect the UART data-ready condition, flash an LED, and read the incoming byte. But the operation is the same if you use the input as an interrupt. You just have to set up the ISR and configure the UART to generate an interrupt when a byte is received.

Limitations: As shown in Figure 4, a switch generates the interrupt. Depending on how the UART in your device is configured, you might get 1 byte or a continuous stream of received bytes. Some UARTs will ignore the lack of a stop bit and just keep receiving, as long as the switch (or signal) is low. Others, such as the TM4C1233H6 that I used, will recognize the missing stop bit and ignore the continuous “start” bit until the switch or signal goes back high. In either event, you will get a framing error on the received byte, if the device UART supports that.

The interrupt won’t occur until the incoming byte is received. Accordingly, if you generate a signal and the baud rate is 9600, you won’t get the interrupt until about 1ms after the signal occurs (10 bits × 104µs). Obviously, higher baud rates will speed this up, but however you do it, there will be a delay. This limits the rate at which the input can interrupt the MCU, so this technique isn’t suitable for interrupts that occur any faster than the time it takes to receive a byte.

As mentioned, the signal has to be low long enough to be recognized as a start bit by the UART receiver. This is typically around 50% of the bit time, but can vary depending on what part you are using. This could also be used to filter a noisy input, as long as the noise never lasts long enough to trigger the input.


Usually, you will have plenty of timers. But in a case where all the timers are used, you can generate a periodic interrupt with a spare UART or SPI. For example, if you set a UART to 9600 baud, each byte transmitted takes just over 1ms, which could be used as an (almost) 1ms time base for a main timing loop or even a simple, real-time operating system (RTOS). You would be limited to available baud rates or SPI clock rates, and each time the interrupt occurs, servicing it requires sending another byte or word to generate the next interrupt. In the demo circuit (Figure 4), UART2 of the TM4C1233H6 is used to generate the 1ms timing. The demo code doesn’t use a UART interrupt; instead, it polls the UART transmitter once for every pass through the main loop. But an interrupt could certainly be used.

If you look at the code for the firmware (see Circuit Cellar’s article code and files download webpage), you will see that the output pins don’t even have to be configured for this to work; the UART Tx pin could be configured for some other function. The code to configure the UART2 output pin is included for reference, but commented out to demonstrate that it’s not needed. The demo circuit includes the code to use the TM4C1233H6’s SysTick (system tick) timer for the main timing loop, with a #define near the top of the file to select the SysTick or UART2 as the time base.

Limitations: You have to keep sending data to keep the “timer” running, and you will be limited to a timebase that is 10x one of the baud rate periods. However, you can reduce this by selecting 7-bit or 6-bit words. If doing this with an SSI output, if you may be able to get closer to even divisions like exactly 1,000Hz.


It’s tempting to ask if you could use a CAN (controller area network) or I2C interface for PWM. The answer is: Not very easily. The TM4C1233H6PM has both CAN and I2C interfaces. CAN was originally devised for communication between the various electronic modules in a vehicle. It is a message-based protocol, and it includes bus arbitration and other things built into the message format.

Similarly, I2C includes information such as addressing, and expects responses from the remote device when messages are sent. This makes it impractical to use for a PWM output.


The firmware for the demo circuit in Figure 4 is in one file, main.c (see Circuit Cellar’s article code and files download webpage). I did this for simplicity. The software just walks through the LED brightness generated by the UART PWM and the SSI PWM. LED blink timing is set by using UART2 as a 960Hz time base. Pressing the pushbutton also flashes the yellow diagnostic LED. Basically, the software continuously demonstrates the techniques described here. The green heartbeat LED blinks once per second to indicate the MCU is alive.

These concepts can be implemented on any MCU with suitable internal peripherals. The specifics of the device you use, such as availability of DMA, baud rate and SPI clock options, and other device-specific characteristics will determine how much of this you can use. But the basic principles apply to any device. The firmware was written using TI’s Code Composer Studio and the TM4C TivaWare library from TI [3]. The application uses only a fraction of the available memory in the device.

Transistors Q1 and Q2 are capable of driving the LEDs for the demo. Resistors R7 and R10 are sized to minimize the current in the bases of the transistors, not to maximize current capacity. If you were going to drive a heater or a fan, you would want a more sophisticated driver that can handle higher current.

The TM4C1233H6PM is programmed using serial port 0 via connector J2. Shorting W1 during reset or powerup puts the device in programming mode. This is the programming method I’ve used in previous articles, including my “Build a Composite Video Text Terminal” article mentioned earlier. Figure 5 is the schematic of the programming adapter that connects the board to a PC, in case you want to duplicate my circuit instead of using some other device.

Figure 5 Schematic of the programming adapter for the demonstration circuit
Figure 5
Schematic of the programming adapter for the demonstration circuit

Sometimes creative solutions will let you hammer a square peg into a round hole on your project. I hope these ideas are useful to you. 

Editor’s Note: Stuart Ball is a long time regular Circuit Cellar author, contributing excellent articles since the mid-‘90s. Check out some of his recent ones:


Advertise Here

“Understanding PID: Control Concepts,” (Issue #347, June 2019)
“Using Digital Potentiometers,“ (Issue #351, October 2019)
“Build a Composite Video Text Terminal” (Issue #356, March 2020)
“Calibrating an MCU’s RTC Using GPS,” (Issue #360, July 2020)

Go to our web shop at to find his articles in pdf form.


[1] TI TM4C1233H6PM datasheet:
[2] “Build a Composite Video Text Terminal” (Circuit Cellar 356, March 2020)
[3] TI Tivaware library manual:

Texas Instruments |


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Stuart Ball recently retired from a 40+ year career as an electrical engineer and engineering manager.  His most recent position was as a Principal Engineer at Seagate Technologies.

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Hacking MCU Pins

by Stuart Ball time to read: 15 min