Get the latest AI workflows to boost your productivity and business performance, delivered weekly by expert consultants. Enjoy step-by-step guides, weekly Q&A sessions, and full access to our AI workflow archive.
Summary
In the video, "How I Made Arduino Run 50x Faster," the creator Playduino explains how to significantly increase the speed of an Arduino by using direct port manipulation instead of the common 'digitalWrite' function. A problem with flickering was initially observed due to the 'millis' function, which was resolved by disabling interrupts. By leveraging direct port writes and bypassing Arduino's in-built functions, they achieved a drastic performance boost, hitting frequencies up to 8 MHz using SPI interface. This journey involved switching pins, adjusting duty cycles, and adopting bitwise operations, showcasing the dramatic improvements in speed and efficiency achievable through these techniques.
Highlights
At the start, Playduino identifies the common issue of slow processing with Arduino's 'digitalWrite'. π’β¨
Demonstrates the initial setup with a simple blink using oscilloscope to measure speed. π
Reveals how flickering caused by 'millis' can be removed by disabling interrupts. π«π‘
Achieves speed boosts by using direct port manipulation, bypassing Arduino's built-in functions. ππ
Demonstrates bitwise operations for selective pin controlling and achieving precise duty cycles. πβ¨
Introduces SPI interface to speed up pin toggling to an impressive 8 MHz frequency. βοΈπ
Explains why 'digitalWrite' is slow due to its comprehensive but time-consuming operations. β³π¬
Key Takeaways
Flicker-free with 'noInterrupts': Disable interrupts to eliminate unwanted flicker from the 'millis' timer. π«β¨
Fast pin toggling: Direct port manipulation can toggle pins significantly faster than 'digitalWrite'. π
Using assembly for precision: Introduce delay at the clock cycle level with assembly for precise duty cycle control. π οΈ
Leveraging SPI: Using the SPI interface achieves a massive speed boost by efficiently handling pin and data toggling. β‘
Trade-offs with 'digitalWrite': Convenient and versatile, but at a costβit's slower due to its broad functionality. π
Overview
In Playduino's latest video, they dive into the fascinating world of Arduino optimization. Initially, our host faced a frustrating performance issue with an LED matrix project, resulting in a flickering display rather than the intended animations. This problem was traced back to the standard 'digitalWrite' function used in Arduino programming, which, while easy to use, imposed limitations on speed. Playduino's journey to overcome this led to discovering techniques that increase the Arduino's performance by 50 times!
The solution began with addressing the flicker, caused by the 'millis' function, by using the 'noInterrupts' function to halt unnecessary interruptions, smoothing out operation. By shifting from standard functions to direct port manipulation, impressive frequency gains were made, showing just how much raw speed embedded systems can achieve. Using assembly commands like 'no operation', they fine-tuned timing down to the clock cycle, bringing precision control to the project's duty cycles.
To cap off these improvements, Playduino leveraged the power of SPI (Serial Peripheral Interface) which propelled pin toggling frequencies to a whopping 8 MHz. This exploration revealed not only how to maximize Arduino capabilities but also the trade-offs of popular Arduino functions like 'digitalWrite'. Viewers are left inspired to experiment with these techniques in their own projects, balancing easy-to-use functions with performance gains.
Chapters
00:00 - 00:30: Introduction and Initial Problem The chapter introduces a common issue faced by programmers: projects that don't perform as expected, using a personal anecdote. The author recounts working with an Arduino LED matrix display which resulted in a flickering mess instead of the desired animation. The problem stemmed from a frequently used function, digital operations, which will be explored further to highlight a solution.
00:30 - 01:00: Basic Oscilloscope Setup and Initial Test The chapter titled 'Basic Oscilloscope Setup and Initial Test' covers the initial steps for setting up an oscilloscope with a microcontroller. The narrator connects the oscilloscope to pin 13, which is also used for ISP programming. They demonstrate a basic blink program where an LED connected to pin 13 turns on and off at intervals, verifying the connection and setup of the oscilloscope.
01:00 - 02:00: Frequency and Flickering Explained This chapter explains the concepts of frequency and flickering using an Arduino as an example. It demonstrates what happens when the delay is removed from the Arduino's operation, allowing it to turn on and off repeatedly at a high speed. The demonstration reveals that the system operates at a frequency of around 150 kHz. The chapter aims to provide a clearer understanding of these technical concepts by showcasing their effects in a practical scenario.
02:00 - 02:30: Improvement Using No Interrupts This chapter discusses an issue observed with flickering due to the frequency and period of a signal, specifically at a frequency that translates to a period of around 6.7 microseconds. The flickering is further examined by zooming out. Although the problem is not visible in the code, it is identified that the issue might be related to the 'Millies' function, which could be affecting the timing and causing the flickering.
02:30 - 04:00: Switching Pins Faster Using Port Registers The chapter discusses the concept of switching pins faster using port registers in the context of programming microcontrollers. It touches upon the use of a timer zero interrupt service routine that operates approximately every millisecond, specifically 1.024 milliseconds. While the primary focus isn't on millisecond precision (like that of the 'millies' function), it aims to address the issue of removing flickering, possibly through efficient pin switching using the function no.
04:00 - 06:00: Fine-Tuning the Duty Cycle Using Assembly In this chapter, the focus is on fine-tuning the duty cycle using assembly language. The discussion begins by addressing the topic of interrupts, explaining that when a certain function is called, all interrupts are deactivated, including the millis function. This is initially demonstrated, and the result is the elimination of flickering, which is a positive outcome. The narrative then shifts back to the primary topic: toggling a pin at a frequency of 150 kHz, which amounts to toggling it 150,000 times per second. Although this might sound fast, it's considered slow compared to the Arduino's operation, which runs on a 16 MHz crystal, although it's noted that it's technically not a crystal. The chapter likely delves into how to optimize or adjust the duty cycle effectively using assembly, leveraging the high-speed capabilities of the Arduino.
06:00 - 08:00: Bitwise Operations for Pin Control This chapter explores the use of bitwise operations for controlling multiple pins simultaneously. The discussion includes the process of toggling two pins at the same time and the resulting effect on frequency, particularly pointing out that enabling two pins leads to a frequency drop.
08:00 - 10:00: Maximizing Speed with SPI The chapter discusses the configuration of pins as outputs to maximize speed when using SPI. It highlights why certain operations are slower due to the non-simultaneous switching of pins. Specifically, pin 11 and pin 13, which are both connected to port B, are used as an example. The process of configuring these pins to be outputs and how they can be turned on and off directly by writing to port B is explained.
10:00 - 11:00: Limitations of digitalWrite and Conclusion The chapter focuses on the limitations of the digitalWrite function in Arduino. It explains the inefficiencies and performance issues that can arise when using digitalWrite, especially in high-performance or time-critical applications. Instead, it suggests using direct port manipulation for better performance.
How I Made Arduino Run 50x Faster Transcription
00:00 - 00:30 Have you ever built a project that just wasn't fast enough no matter what you tried? I certainly have. Years ago, I was working on an LED matrix display with my Arduino. Excited to see it light up with animations. But instead of seeing the pattern on the screen that I expected, I got a flickering mess. Why? A single function most Arduino programmers use without thinking twice. Digital, right? Stick around to learn a simple change that made all the difference using the exact same
00:30 - 01:00 hardware. All right, so let's toggle a pin. I will connect my oscilloscope to pin 13. And the nice thing is pin 13 is also connected to this pin head here for ISP programming. Let's start with something very simple, which is blink. As you can see, blink works great. It turns on the LED pin, waits 1,000 milliseconds, and turns off the LED pin.
01:00 - 01:30 But sometimes we need to switch fast. Let's watch what happens if we remove the delay. So now Arduino turns on, immediately, turns off, and it does the same thing over and over and over again. So how fast will it actually run? Let's see. Well, it looks pretty impressive. We get a frequency of around 150 kohz as well as a duty cycle of
01:30 - 02:00 47.3%. And the frequency translates into a period of around 6.7 microsconds. You also see some flickering. What is going on there? Let's zoom out a bit. You see this flickering? Now if we stop at the right time, we see something like this. Why is this happening? We don't see it in our code here, right? Any idea what causes this? It's Millies. Millies relies on
02:00 - 02:30 the timer zero interrupt service routine approximately every millisecond. Yeah, this is actually pretty good. You can see that the difference between these two is a little bit more than 1 millisecond. In fact, it's 1.024 milliseconds. And you can watch this video if you want to know more. But millies is not the topic for this video. I just want to remove this flickering. How can I do that? We can do this with the function no
02:30 - 03:00 interrupts. If we call this function, we will deactivate all the interrupts, which also includes millies. So, let's do that. And suddenly the flickering is gone. Nice. But back to the topic, we toggle this pin with a frequency of 150 kohz, which is 150,000 times per second. And this is actually slow because our Arduino runs on a 16 megahertz crystal. It's actually not a crystal. It's a
03:00 - 03:30 ceramic resonator. Maybe a topic for a different video, but still it should be able to do more. And it is able to do more. What happens if we want to toggle two pins? If I want to also toggle pin 11, for example, at the same time, right? We we turn on both and then we turn off both. What happens to our frequency here? Well, let's have a look. Boom. As expected, the frequency drops. Let's
03:30 - 04:00 take a look at the second signal. Oh, yeah. We don't see a signal yet because pin 11 is not yet an output. Let's configure it as output. So here you can see why it's slower. It's not switching them at the same time. Pin 11 and pin 13 are both connected to port B. And what we can do is we can turn on and turn off all pins of this port B by directly writing a
04:00 - 04:30 value to the port register. So directly into the hardware bypassing all of the Arduino functions, we delete this digital write commands. So port B equals 0x FFF which is hexodimal and is basically the same thing as 0 B11 1. You can translate it into 255. I really like hexadesimal, so let's go
04:30 - 05:00 with that. Port B = 0x FF and port B = 0x 0. Let's upload and see what happens. As you can see, the speed increased dramatically. If you zoom in, the frequency increased to 2.66 MHz. The duty cycle also dropped to 16.6%. And we're switching all outputs at the same time because we turn on all pins of port B. And then we turn off all
05:00 - 05:30 pins of port B. There is no delay between the IO pins. Now how can we increase the on time to make it approximately 50/50? Technically what we need to do is we need to add a delay here. But we can't use the function delay because this adds a millisecond at least. And also we disabled the interrupt so it won't work anymore. Let's not use delay. Instead we can tell our Arduino wait one clock cycle. We
05:30 - 06:00 actually have to write assembly code for this. So how can we write assembly code? We use double underscore ASM double underscore. I also add volatile so that the compiler doesn't delete it. And then we write no operation. This is the assembly command for wait one clock cycle. So let's wait one clock cycle and see what happens. Right now we have a period time of
06:00 - 06:30 374 nanoconds and now we add one no operation and we slightly increase to 438 nconds and also the duty cycle as you can see increased to 28%. Now the difference between these two numbers is approximately 64 because it's jumping around like crazy. Now this should be the value of one clock cycle. So what's the value of one clock cycle?
06:30 - 07:00 1 divided by 16 MHz. So 16 * 10 to the power of 6. It should be 62.5 nconds. Let's add three. Let's add one more. Perfect. We have 50.5% duty cycle. That's actually better than using digital, right? And also way faster at 1.6 MHz. That's awesome. But it's not the whole truth. If I want to
07:00 - 07:30 turn on only one pin, then I can't do that. In order to just turn on PB5, we need some bitwise operation. And it works like this. Port B or equals. This is the mask that we can use to turn on certain pins. I will turn on PB5. And then we do something similar for turning it off again. Port Bend equals. And now I need an inverted
07:30 - 08:00 mask for turning it off PB 5 again. So let's see what this does to the speed. 1.33 MHz, but still pretty fast for turning on and off one specific pin. But what if I told you we can go even faster than that? In the case of my LED matrix, I had to shift bits as fast as possible into a shift
08:00 - 08:30 register. And so I don't need to toggle all of my pins. I just need to toggle the clock pin and the data pin. For something like this, we can use the SPI interface. It stands for serial peripheral interface include library SPI and I will remove this restart [Music] SPI and then we can set it to 8
08:30 - 09:00 MHz most significant bit first and SPI mode zero. This is just for clock polarity and phase. Instead of this, we can now SPI dot transfer 0x55 which is a nice on off on off on off on off pattern. Let's see what happens to the frequency with this high frequencies.
09:00 - 09:30 The signal is already getting very ugly. But we can clearly see that the clock signal, which is the yellow signal, runs at almost 8 MHz and the data signal runs at half that frequency because it's on, off, on, off, on, off. If I send a high signal all of the time, then it would just look like this. It would just stay high. So, this is the fastest way of
09:30 - 10:00 toggling a pin and transferring data out of your Arduino that I'm aware of. But why is digital right so slow? Well, if you look at the implementation of digital right, it needs to do a ton of different things every time you use digital right. I think most time is wasted with this stuff here because it needs to find out if there is a timer attached to this pin and it also needs to find out the bit number and the port. All of this needs
10:00 - 10:30 to be done every time you call digital write because it needs to be able to do this dynamically and that's why it takes so long. It's super versatile. It's platform independent. It works on all the different architectures but it's slow. That's the downside. All right. Did I miss something? Are you also using this hardware registers directly in your projects? If this video was helpful, please like and subscribe to this
10:30 - 11:00 channel. And if you're interested in millies, definitely watch this video. Thank you for watching. See you in the next video.