{"id":4335,"date":"2019-02-06T20:00:03","date_gmt":"2019-02-06T19:00:03","guid":{"rendered":"https:\/\/msalamon.pl\/?p=4335"},"modified":"2025-12-27T19:59:34","modified_gmt":"2025-12-27T18:59:34","slug":"ssd1306-oled-displays-i2c-or-spi","status":"publish","type":"post","link":"https:\/\/msalamon.pl\/en\/ssd1306-oled-displays-i2c-or-spi\/","title":{"rendered":"SSD1306 OLED Displays: I2C or SPI?"},"content":{"rendered":"\n<p class=\"has-text-align-left\">Many people rave about the beauty of OLED displays because there\u2019s something amazing about them. Infinite contrast and high refresh rate are incredible advantages of this technology. On the market you can find plenty of tiny monochrome OLEDs in sizes from about 0.49\u2033 up to roughly 4\u2033. Their price has dropped significantly in recent years, making them wildly popular. Let\u2019s see how to tame an SSD1306 OLED using an STM32!<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">OLED technology<\/h2>\n\n\n\n<p class=\"has-text-align-left\">Probably the most important feature of this technology is the fact that every pixel is an individual organic light-emitting diode. This means there\u2019s no need for backlighting the matrix, so the display is very thin and essentially made of glass alone. Thanks to this, these displays can theoretically achieve infinite contrast. In short \u2013 a black pixel emits nothing, and white is white.<\/p>\n\n\n\n<p class=\"has-text-align-left\">The lack of power-hungry backlighting means energy efficiency, as this is the component that consumes the most power in classic LCD displays.<\/p>\n\n\n\n<p class=\"has-text-align-left\">But nothing comes for free\u2026 Because the pixels are diodes, they can suffer from burn-in. Over time, diodes lose brightness. Depending on the color, the drop to half the initial brightness may take about 20\u2013100 thousand hours. This is stated in every datasheet. That seems like a large value, and it actually is. Unfortunately, the human eye can perceive relative brightness differences after just a few percent drop. Hence we see burn-in in areas where the OLED shows a static image. You\u2019ll notice this in a year or two (if you don\u2019t already) on early OLED TVs, and it will be quite a big problem in the first models.<\/p>\n\n\n\n<p>A very good article about OLEDs was once written by a colleague of mine. I invite you to read it (<a href=\"https:\/\/ep.com.pl\/files\/11867.pdf\" target=\"_blank\" rel=\"noopener\">link<\/a>).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SSD1306 OLED controller<\/h2>\n\n\n\n<p class=\"has-text-align-left\">We never have access to a bare matrix. There\u2019s always some controller in between. This is the case with alphanumeric displays (HD44780), TFTs, and OLEDs. The controller\u2019s job is to receive instructions from the host on how to set the pixels on the matrix. They often feature additional built-in functions.<\/p>\n\n\n\n<p class=\"has-text-align-left\">A popular manufacturer of OLED display controllers is Solomon Systech Limited. They produce very good, popular controllers marked SSD. They offer a dozen or so chips capable of driving OLED matrices. They differ in the sizes of supported matrices, interfaces, or available grayscale levels. For those interested, see the brochure \u2013 <a data-e-disable-page-transition=\"true\" class=\"download-link\" title=\"\" href=\"https:\/\/msalamon.pl\/download\/512\/?tmstv=1766859693\" rel=\"nofollow\" id=\"download-link-512\" data-redirect=\"false\"><br>SSD_OLED_IC_Catalog<\/a><br>. Undoubtedly, the most popular on the market is the SSD1306. It\u2019s relatively simple. It supports matrices up to 128\u00d764 pixels. It has a 256-step brightness scale and note, this is not grayscale. It simply dims\/brightens the entire matrix. It has built-in RAM for the displayed image. You can talk to it via an 8-bit parallel interface, I\u00b2C, or SPI (3\/4-wire), with the parallel interface rarely implemented on Chinese modules. And that\u2019s good because I believe I\u00b2C and SPI are fast enough. An important feature is the built-in voltage converter, since OLED panels require about 12 V to operate. Thanks to this converter you don\u2019t have to worry about it, although supposedly they can whine. Luckily, I\u2019ve never had one do that.<\/p>\n\n\n\n<p>This controller has a few built-in effects:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\n<ul class=\"wp-block-list\">\n<li>Screen scrolling, e.g. for a simple screensaver \u2013 even pixel wear.<\/li>\n\n\n\n<li>Fade Out and Blinking function<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Zoom<\/li>\n<\/ul>\n\n\n\n<p class=\"has-text-align-left\">You\u2019ll find more valuable information in the chip\u2019s datasheet \u2013 <a data-e-disable-page-transition=\"true\" class=\"download-link\" title=\"\" href=\"https:\/\/msalamon.pl\/download\/515\/?tmstv=1766859693\" rel=\"nofollow\" id=\"download-link-515\" data-redirect=\"false\"><br>SSD1306 Datasheet<\/a><br>. I will focus on a display based on this controller. It will be 0.96\u2033.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/produkt\/wyswietlacz-oled-096\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=oled096&amp;utm_content=Text\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"341\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-1024x341.jpg\" alt=\"\" class=\"wp-image-952\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-768x256.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-24x8.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-36x12.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner-160x53.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/02\/OLED_baner.jpg 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Control<\/h2>\n\n\n\n<p class=\"has-text-align-left\">Chinese modules offer two interfaces \u2013 I\u00b2C and SPI. Communication is split into <i>commands<\/i> and <i>data<\/i>. The choice of the type of information transmitted over I\u00b2C is made by writing to the appropriate register (0x00 for data and 0x40 for commands). In SPI it\u2019s a bit different because you can choose 3- or 4-wire communication. With four wires, in addition to the standard SPI signals, there is also a D\/C signal that determines what is being sent to the display. If 3-wire mode is selected, you need to send an extra bit at the beginning of each byte. This makes the communication 9-bit. So, shall we? Let\u2019s get cracking!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Library<\/h2>\n\n\n\n<p class=\"has-text-align-left\">The code provides several definitions you can change, such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>interface selection and its settings, e.g. the I2C address<\/li>\n\n\n\n<li>matrix resolution<\/li>\n\n\n\n<li>selection of graphic functions offered by the display controller<\/li>\n<\/ul>\n\n\n\n<p class=\"has-text-align-left\">The rest of the control is done through appropriate functions. Image creation is based on an RAM buffer in the MCU. It contains information about each pixel \u2013 the so-called frame. This is a monochrome display, so the entire buffer occupies only 1 kB of RAM. That\u2019s not much for an STM32, and using such a buffer offers great convenience. Thanks to it, it\u2019s easy to implement image overlaying (the <i>transparency<\/i> argument).<\/p>\n\n\n\n<p class=\"has-text-align-left\">The basic function is initialization. Depending on the interface, it takes as an argument a pointer to the appropriate I\u00b2C or SPI structure. <strong>Note.<\/strong> This function sets, among other things, matrix size, COM voltage, and drawing directions, which can differ especially with other resolutions and sizes. <strong>Make sure you have the right ones and, if necessary, change them to those recommended by your display\u2019s manufacturer.<\/strong> Smaller resolutions may have a different pixel-to-memory organization. However, for most cases, these settings should be correct.<\/p>\n\n\n\n<p>Four configuration functions allow you to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Enable\/disable pixels.<\/li>\n\n\n\n<li>Invert colors<\/li>\n\n\n\n<li>Rotate the display by 180\u00b0<\/li>\n\n\n\n<li>Set contrast \u2013 256-step display brightness<\/li>\n<\/ul>\n\n\n\n<p>Next, the most important display functions.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_DrawPixel(int16_t x, int16_t y, uint8_t Color);<\/pre>\n\n\n\n<p class=\"has-text-align-left\">Drawing a single pixel in the RAM buffer. This function draws a pixel with the given coordinates and color in RAM. It is not sent to the display. This allows you to prepare all graphics before they are sent to the OLED\u2019s RAM.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_Clear(uint8_t Color);\n<\/pre>\n\n\n\n<p class=\"has-text-align-left\">Fill the RAM buffer with the selected color. Only WHITE and BLACK are available. Any other value will be ignored. It\u2019s used to clear the frame contents.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_Display(void);<\/pre>\n\n\n\n<p class=\"has-text-align-left\">Send the entire buffer to the display\u2019s RAM. Only when this function is called is the previously prepared buffer sent to the display and the final image appears on the matrix.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_Bitmap(uint8_t *bitmap);<\/pre>\n\n\n\n<p class=\"has-text-align-left\">This function is similar to <i>SSD1306_Display<\/i>, except that we can send an image that is outside the buffer, e.g. in Flash memory. <strong>It must be the same size as the matrix<\/strong>. Otherwise some garbage from memory will be sent or a HardFault will occur.<\/p>\n\n\n\n<p class=\"has-text-align-left\">Next, I implemented the graphics functions offered by the controller. There is content scrolling in different directions.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_StartScrollRight(uint8_t StartPage, uint8_t EndPage, scroll_horizontal_speed Speed);\nvoid SSD1306_StartScrollLeft(uint8_t StartPage, uint8_t EndPage, scroll_horizontal_speed Speed);\nvoid SSD1306_StartScrollRightUp(uint8_t StartPage, uint8_t EndPage, scroll_horizontal_speed HorizontalSpeed, uint8_t VerticalOffset);\nvoid SSD1306_StartScrollLeftUp(uint8_t StartPage, uint8_t EndPage, scroll_horizontal_speed HorizontalSpeed, uint8_t VerticalOffset);\nvoid SSD1306_StopScroll(void);<\/pre>\n\n\n\n<p class=\"has-text-align-left\">Arguments of type <i>scroll_horizontal_speed<\/i> decide how many frames apart the animation advances. <strong>This is not the number of frames you send to the controller.<\/strong> These are the controller\u2019s refresh frames, based, among other things, on <i>Display Clock<\/i> and <i>Multiplex Ratio<\/i> values provided during initialization. For convenience, they are in enumerated form. I encourage you to experiment with these functions.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">typedef enum\n{\n\tSCROLL_EVERY_5_FRAMES,\n\tSCROLL_EVERY_64_FRAMES,\n\tSCROLL_EVERY_128_FRAMES,\n\tSCROLL_EVERY_256_FRAMES,\n\tSCROLL_EVERY_3_FRAMES,\n\tSCROLL_EVERY_4_FRAMES,\n\tSCROLL_EVERY_25_FRAMES,\n\tSCROLL_EVERY_2_FRAMES\n} scroll_horizontal_speed;<\/pre>\n\n\n\n<p class=\"has-text-align-left\">The last section is the \u201cadvanced\u201d graphic commands. The first is <i>Fade Out<\/i>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_StartFadeOut(uint8_t Interval);<\/pre>\n\n\n\n<p class=\"has-text-align-left\">It gradually dims the display, i.e. decreases the contrast. The change goes from the given <i>Interval<\/i> down to zero and remains there until canceled.<\/p>\n\n\n\n<p class=\"has-text-align-left\">A similar effect is blinking.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_StartBlinking(uint8_t Interval);<\/pre>\n\n\n\n<p class=\"has-text-align-left\">The effect is similar to <i>Fade Out<\/i>, except that the display returns to the <i>Interval<\/i> value and \u201cblinks\u201d in a loop.<\/p>\n\n\n\n<p class=\"has-text-align-left\">After dimming or setting blinking, the display does not return to the \u201cnormal\u201d state. You need to pull it out with<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_StopFadeOutOrBlinking(void);<\/pre>\n\n\n\n<p>The last, most pointless function for me is <i>Zoom In.<\/i><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void SSD1306_ZoomIn(uint8_t Zoom);\n<\/pre>\n\n\n\n<p class=\"has-text-align-left\">It only works with the full possible matrix, i.e. 128\u00d764, and the idea is that the top half of the display (128\u00d732) is stretched downwards. See how it works for yourself. Will it be useful?<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"960\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal.jpg\" alt=\"\" class=\"wp-image-536\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal-300x300.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal-150x150.jpg 150w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal-768x768.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal-24x24.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal-36x36.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_normal-80x80.jpg 80w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"960\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom.jpg\" alt=\"\" class=\"wp-image-537\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom-300x300.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom-150x150.jpg 150w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom-768x768.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom-24x24.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom-36x36.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_zoom-80x80.jpg 80w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Graphics library<\/h2>\n\n\n\n<p class=\"has-text-align-left\">The graphics library I use is based on various examples from the Internet. It can be used with both STM32 and AVR. It requires passing three values to work correctly. These are the function that draws a single pixel and the display dimensions in pixels.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#define GFX_DrawPixel(x,y,color) SSD1306_DrawPixel(x,y,color)\n#define WIDTH SSD1306_LCDWIDTH\n#define HEIGHT SSD1306_LCDHEIGHT<\/pre>\n\n\n\n<p class=\"has-text-align-left\">In my example, the library will draw in the RAM buffer because that\u2019s what the pixel function does. To send this to the OLED controller, you\u2019ll need to use SSD1306_Display().<\/p>\n\n\n\n<p class=\"has-text-align-left\">I also included a few switches to decide which drawing functions will be needed. You can save some Flash space by skipping compilation of unnecessary things. What gets compiled is determined by USING_XXX-style switches (#define). Setting zero removes the given function, while one adds it to the code. Some functions require others, and I added a small \u201cautomaton\u201d to handle that. Available are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Strings in various sizes<\/li>\n\n\n\n<li>Images<\/li>\n\n\n\n<li>Rotated images in 1\u00b0 steps \u2013 a very primitive function that can leave empty pixels during rotation<\/li>\n\n\n\n<li>Drawing geometric shapes (squares, circles, triangles \u2013 empty, filled, rounded or not)<\/li>\n<\/ul>\n\n\n\n<p class=\"has-text-align-left\">I encourage you to play with the library and report bugs or modification proposals to me. Now I\u2019ll move on to the main part of the post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">I\u00b2C<\/h2>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/dev-boardy\/stm32-nucleo\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=mlx90614&amp;utm_content=Text\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"400\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg\" alt=\"\" class=\"wp-image-1593\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-768x256.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">I\u2019ll start with I\u00b2C. With an STM32 I only need two wires for this interface, so it\u2019s a tempting option for default use. The SSD1306 datasheet says that the maximum supported I\u00b2C clock is 400 kHz. For today\u2019s tests I\u2019ll use a <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/nucleo-l476rg\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=oled096&amp;utm_content=Text\"><strong>Nucleo with STM32L476RG<\/strong><\/a> on board. It supports I\u00b2C communication in Standard Mode (100 bit\/s), Fast Mode (400 bit\/s), and Fast Mode Plus (1 Mbit\/s). I\u2019ll try to overclock the SSD1306 controller\u2019s I\u00b2C clock, why not! Let\u2019s see what happens.<\/p>\n\n\n\n<p class=\"has-text-align-left\">Since I used a ready-made module with the display, the schematic to connect it to I2C1 is trivially simple.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"838\" height=\"520\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic.jpg\" alt=\"\" class=\"wp-image-517\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic.jpg 838w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic-300x186.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic-768x477.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic-24x15.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic-36x22.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_schematic-129x80.jpg 129w\" sizes=\"auto, (max-width: 838px) 100vw, 838px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">First I\u2019ll test a \u201csafe\u201d clock value \u2013 100 kHz. Cube configuration is trivial.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"728\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1.jpg\" alt=\"\" class=\"wp-image-519\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1-300x228.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1-768x582.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1-24x18.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1-36x27.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/11\/oled_i2c_cube_conf_100k-1-105x80.jpg 105w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">In later steps I\u2019ll change the speed via the <i>I2C Speed Mode<\/i> menu, but I won\u2019t show that on screenshots. Sending a complete frame at an I\u00b2C clock frequency of 100 kHz looks like this:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"280\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k.jpg\" alt=\"\" class=\"wp-image-527\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k-300x88.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k-768x224.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k-24x7.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k-36x11.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k-160x47.jpg 160w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">The time to send all pixels is about 103 ms. That\u2019s quite a lot given that sending frame after frame yields only 10 frames per second with 100% MCU load. I\u2019ll increase the speed a bit.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"280\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k.jpg\" alt=\"\" class=\"wp-image-528\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k-300x88.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k-768x224.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k-24x7.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k-36x11.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k-160x47.jpg 160w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">It\u2019s definitely better. 27.4 ms is already a nice value that gives about 36 frames per second. Even though the SSD1306 datasheet states that the maximum I\u00b2C clock frequency can be 400 kHz, I\u2019ll try to overclock communication a bit. The STM32L476RG offers Fast Mode Plus. Here\u2019s the effect.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"280\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k.jpg\" alt=\"\" class=\"wp-image-529\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k-300x88.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k-768x224.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k-24x7.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k-36x11.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k-160x47.jpg 160w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">Frame transfer time is only 12.38 ms. This value allows about <strong>80 FPS<\/strong>. That\u2019s quite a lot. Usually such a high FPS isn\u2019t needed, so you can limit it, freeing up CPU for other operations. Speaking of which, can we ease the processor a bit during transfer? Of course! With DMA \ud83d\ude42 I\u2019ll perform the same operation now by sending the entire buffer via DMA. See how much the CPU usage drops.<\/p>\n\n\n\n<p>100 kHz:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"280\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA.jpg\" alt=\"\" class=\"wp-image-538\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA-300x88.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA-768x224.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA-24x7.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA-36x11.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_100k_DMA-160x47.jpg 160w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">As you can see, the processor is busy for just one millisecond. That\u2019s the time I send over I2C to the display the information that I\u2019m going to send a frame. You can shorten this time a bit.<\/p>\n\n\n\n<p>400 kHz:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"280\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA.jpg\" alt=\"\" class=\"wp-image-539\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA-300x88.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA-768x224.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA-24x7.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA-36x11.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_400k_DMA-160x47.jpg 160w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>0.3 ms of CPU occupancy at 400 kHz is an excellent result. There\u2019s still 1000 kHz left.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"280\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA.jpg\" alt=\"\" class=\"wp-image-540\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA-300x88.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA-768x224.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA-24x7.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA-36x11.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frametime_1000k_DMA-160x47.jpg 160w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">In practice, the STM32L476RG sets the I2C clock to 800 kHz even though 1000 is set in Cube. Hence the \u201conly\u201d twice lower result \u2013 0.15 ms. How fast is that? Very.<\/p>\n\n\n\n<p class=\"has-text-align-left\">If you still don\u2019t know the benefits of using DMA, let me show how many main loops the CPU completes each second while working with the display. These will be results with DMA off and on. The first photos concern blocking transfer, i.e. without DMA.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"200\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-300x200.jpg\" alt=\"\" class=\"wp-image-530\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-300x200.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-1024x683.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-768x512.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-360x240.jpg 360w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-24x16.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-36x24.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k-120x80.jpg 120w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k.jpg 1440w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p class=\"has-text-align-left\"><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"200\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-300x200.jpg\" alt=\"\" class=\"wp-image-532\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-300x200.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-1024x683.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-768x512.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-360x240.jpg 360w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-24x16.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-36x24.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k-120x80.jpg 120w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k.jpg 1440w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"200\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-300x200.jpg\" alt=\"\" class=\"wp-image-534\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-300x200.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-1024x683.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-768x512.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-360x240.jpg 360w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-24x16.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-36x24.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k-120x80.jpg 120w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k.jpg 1440w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">As you can see, the program will execute only as many main loops as frames per second the display is able to show. That\u2019s logical, because the CPU waits until the OLED transfer finishes. Now results with DMA. The transfer of the next frame happens only if the previous transfer has finished. At the same time, the main while loop keeps running all the time.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"200\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-300x200.jpg\" alt=\"\" class=\"wp-image-531\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-300x200.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-1024x683.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-768x512.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-360x240.jpg 360w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-24x16.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-36x24.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA-120x80.jpg 120w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_100k_DMA.jpg 1440w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"200\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-300x200.jpg\" alt=\"\" class=\"wp-image-533\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-300x200.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-1024x683.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-768x512.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-360x240.jpg 360w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-24x16.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-36x24.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA-120x80.jpg 120w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_400k_DMA.jpg 1440w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"200\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-300x200.jpg\" alt=\"\" class=\"wp-image-535\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-300x200.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-1024x683.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-768x512.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-360x240.jpg 360w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-24x16.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-36x24.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA-120x80.jpg 120w, https:\/\/msalamon.pl\/wp-content\/uploads\/2018\/12\/oled_i2c_frames_1000k_DMA.jpg 1440w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">Not only did the FPS count go up, but the <strong>program executes about 300 thousand iterations of the while loop per second<\/strong>. While the frame is being transferred by DMA, the CPU is idle and can, for example, receive data from sensors. You can go for double buffering and smoothly prepare data for the display.<\/p>\n\n\n\n<p class=\"has-text-align-left\">Choosing how to handle it \u2013 DMA or not \u2013 besides the configuration in Cube is done in the library with the <i>#define SSD1306_I2C_DMA_ENABLE<\/i> definition.<\/p>\n\n\n\n<p>So how does this all look when working with the SPI interface?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SPI<\/h2>\n\n\n\n<p class=\"has-text-align-left\">The SSD1306 controller allows the SPI interface to operate with three or four connections. The difference is in connecting the DC pin, which tells the chip whether data or commands are being sent. Communication in 4-wire mode is 8-bit, while 3-wire requires sending 9 bits over SPI each time (one informational bit instead of a dedicated wire). My display does not allow me to test 3-wire mode, so I\u2019ll limit myself to the available four connections. Besides, 9-bit SPI seems rather unnatural\u2026 The module I have does not have the MISO pin brought out, but there\u2019s no need to read anything from the display anyway. In such a case, I\u2019ll set SPI to Half-Duplex. SPI + DC equals 4 pins, and there\u2019s also the RESET pin. It\u2019s not mandatory, but if we have free pins in the system, why not use it. It\u2019s enabled conveniently in the library with one \u2018define\u2019 \u2013 <i>#define SSD1306_RESET_USE.<\/i> Then this pin should be connected to 3.3 V.<\/p>\n\n\n\n<p>I\u2019ll connect all possible pins to the MCU. The schematic looks like this.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"523\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic.jpg\" alt=\"\" class=\"wp-image-671\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic-300x163.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic-768x418.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic-36x20.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_schematic-147x80.jpg 147w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">The CS pin strayed a bit from the rest of the group, but there\u2019s a reason for that, which I\u2019ll explain in a moment.<\/p>\n\n\n\n<p class=\"has-text-align-left\">The SSD1306 datasheet says the maximum SPI clock frequency can be 10 MHz. The STM32L476RG runs at 80 MHz, so the prescaler should be set to 8. As with I\u00b2C, I can overclock the communication. For SPI, I can raise the SCK line frequency up to 40 MHz. I\u2019ll test it, of course \ud83d\ude42<\/p>\n\n\n\n<p class=\"has-text-align-left\">Initially, however, I\u2019ll set a reasonably safe value. A prescaler of 64 gives me a clock of ~1.25 MHz. Later it\u2019s enough to just decrease this value. The I\u00b2C pins are remnants from the previous mode and are not needed at this point.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"960\" height=\"512\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf.jpg\" alt=\"\" class=\"wp-image-672\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf.jpg 960w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf-300x160.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf-768x410.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf-36x19.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_cube_conf-150x80.jpg 150w\" sizes=\"auto, (max-width: 960px) 100vw, 960px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">Time to see what the analyzer shows. At 1.25 MHz we already get a good result. Sending the whole frame takes 8.25 ms. Definitely better than the maximum I\u00b2C frequency, and this is only 12% of the controller\u2019s SPI capability.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-1024x203.jpg\" alt=\"\" class=\"wp-image-673\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>For comparison, 5 MHz \u2013 2.07 ms.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-1024x203.jpg\" alt=\"\" class=\"wp-image-674\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>And 10 MHz \u2013 1.04 ms. Very fast!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-1024x203.jpg\" alt=\"\" class=\"wp-image-675\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">Alright, but what about overclocking? \ud83d\ude42 I checked what the prescaler allows, i.e. 20 MHz and 40 MHz. As with I\u00b2C, the controller didn\u2019t even flinch. I didn\u2019t see any random or missing pixels on the screen. Everything goes smoothly. And the times? Regardless of what the analyzer interprets on MOSI and SCK (16 MHz sampling), the test tells the truth.<\/p>\n\n\n\n<p>Frame transfer at 20 MHz takes just 0.53 ms.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-1024x203.jpg\" alt=\"\" class=\"wp-image-676\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_20000k.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">And at 40 MHz\u2026 0.27 ms! What a speed. At such a rate you could skip DMA \ud83d\ude42<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-1024x203.jpg\" alt=\"\" class=\"wp-image-677\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_40000k.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p class=\"has-text-align-left\">However, remember this is 4 times the maximum value given by the manufacturer. The fact that I managed it on a desk doesn\u2019t mean the display will work flawlessly at 40 MHz in a production environment! It\u2019s better to stick to a maximum of 10 MHz and use DMA.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SPI DMA<\/h3>\n\n\n\n<p class=\"has-text-align-left\">Of course, I anticipated this option in the library. Enable DMA the same way as for I\u00b2C and use the <i>#define SSD1306_SPI_DMA_ENABLE<\/i> definition to switch the code to DMA transmission. Unfortunately, that\u2019s not all. As you know, with SPI you also have to control the CS pin. You need to return it to the high state after the DMA transmission is complete. I\u2019m not going to wait for the transfer to finish just to toggle a pin. I didn\u2019t configure DMA to wait like with a regular transfer\u2026 There are two approaches that I implemented in the library:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><i>void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)<\/i> interrupt, i.e. DMA transfer completion. This is a good solution, but remember to enable the interrupt in Cube and place the <i>void SSD1306_DmaEndCallback(SPI_HandleTypeDef *hspi)<\/i> function in its handler. It has the advantage that any pin can be used as CS.<\/li>\n\n\n\n<li>The second way is to set the CS pin to hardware control. The MCU can toggle it by itself when needed. Then you can forget about the DMA transfer completion interrupt. The downside is that it has to be a dedicated pin. For SPI1, that\u2019s PA4, hence the \u201codd\u201d choice from the start. Another downside is that you can\u2019t connect another device to this SPI because the display\u2019s CS will activate whenever you start a transfer on that SPI.<\/li>\n<\/ol>\n\n\n\n<p class=\"has-text-align-left\">You have to decide what\u2019s right for you. What are the MCU occupancy times with SPI DMA like? 1.25 MHz \u2013 38.37 \u00b5s.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-1024x203.jpg\" alt=\"\" class=\"wp-image-678\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_1250k_DMA.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-1024x203.jpg\" alt=\"\" class=\"wp-image-679\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_5000k_DMA.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>5 MHz \u2013 28.81 \u00b5s.<\/p>\n\n\n\n<p class=\"has-text-align-left\">10 MHz \u2013 21.81 \u00b5s.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"203\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-1024x203.jpg\" alt=\"\" class=\"wp-image-680\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-1024x203.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-300x59.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-768x152.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-1536x304.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-24x5.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-36x7.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA-160x32.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/01\/oled_spi_frametime_10000k_DMA.jpg 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><br>It won\u2019t get better than this. At 20 and 40 MHz this time is the same. Most likely the surrounding overhead operations like function returns or HAL SPI handling take much longer than transferring the three commands themselves. Nevertheless, the result is impressive.<\/p>\n\n\n\n<p class=\"has-text-align-left\">What remains is the FPS count and the number of while-loop iterations with DMA. Allow me to skip the photos of the display showing the results. I\u2019ll present them in a table together with I\u00b2C.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">So which interface to choose?<\/h2>\n\n\n\n<figure class=\"wp-block-table tablepress tablepress-id-11\"><table class=\"has-fixed-layout\"><thead><tr><td><\/td><th>CPU time Poll(ms)<\/th><th>FPS Poll<\/th><th>CPU time DMA<\/th><th>FPS DMA<\/th><th>Loops DMA<\/th><\/tr><\/thead><tbody><tr><td>I2C 100 kHz<\/td><td>109<\/td><td>10<\/td><td>1.08 ms<\/td><td>11<\/td><td>331655<\/td><\/tr><tr><td>I2C 400 kHz<\/td><td>27.4<\/td><td>36<\/td><td>0.3 ms<\/td><td>38<\/td><td>317300<\/td><\/tr><tr><td>I2C 800 kHz<\/td><td>12.38<\/td><td>80<\/td><td>0.15 ms<\/td><td>81<\/td><td>293911<\/td><\/tr><tr><td>SPI 1,25 MHz<\/td><td>8.25<\/td><td>104<\/td><td>38.37 us<\/td><td>102<\/td><td>2368545<\/td><\/tr><tr><td>SPI 5 MHz<\/td><td>2.07<\/td><td>285<\/td><td>28.81 us<\/td><td>272<\/td><td>1585956<\/td><\/tr><tr><td>SPI 10 MHz<\/td><td>1.04<\/td><td>403<\/td><td>21.81 us<\/td><td>378<\/td><td>1098468<\/td><\/tr><tr><td>SPI 20 MHz<\/td><td>0.53<\/td><td>507<\/td><td>21.81 us<\/td><td>477<\/td><td>691864<\/td><\/tr><tr><td>SPI 40 MHz<\/td><td>0.27<\/td><td>583<\/td><td>21.81 us<\/td><td>544<\/td><td>393303<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>What conclusions can be drawn? Which interface to use in a project? For tinkering or a simple device, it won\u2019t matter much which interface you choose. However, if:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\n<ol class=\"wp-block-list\">\n<li>You have few pins, system size doesn\u2019t matter \u2013 you can use I\u00b2C.<\/li>\n\n\n\n<li>You have few pins, and the system is time-critical (has many elements and a ton of code executing at once) \u2013 take SPI without reset in 3-wire mode. This will require a slight rebuild of the library.<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>Any number of pins and any complexity \u2013 SPI is definitely the better choice. Unless it\u2019s already taken.<\/li>\n<\/ol>\n\n\n\n<p class=\"has-text-align-left\">Should you care about the maximum FPS? The only \u201cthreat\u201d to animation smoothness is I\u00b2C at 100 kHz. Everything else will handle 30 frames for smoothness. It\u2019s also better to programmatically limit FPS to 30 or 60. This isn\u2019t Counter\u2011Strike.<\/p>\n\n\n\n<p class=\"has-text-align-left\">And DMA? In my opinion, it\u2019s a must have. Life\u2019s too short to wait for the MCU to send data \u201cby hand.\u201d You can really do other things during that time, like data acquisition from all sensors. Operation will be smooth, and the display will please the eye with a consistently high frame rate.<\/p>\n\n\n\n<p class=\"has-text-align-left\">I mentioned double buffering earlier. If you want to send frames at equal time intervals, for example using a <i>timer<\/i> interrupt, it may happen that the <i>timer<\/i> triggers the DMA transfer when the MCU is writing some data to the display buffer. Then at least one frame will be corrupted, which a keen eye may notice. You can implement double buffering to solve this. What is it? How to do it? Another time \ud83d\ude09<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"has-text-align-left\">OLEDs are great displays. Using them is very enjoyable and I\u2019m eager to use them in my devices. I think I\u2019ve shown the difference between the interfaces well and you\u2019ll choose the right control more consciously.<\/p>\n\n\n\n<p>If you liked OLED displays, you can get them in various variants <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/wyswietlacz-oled-096\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=oled096&amp;utm_content=Text\">in my shop<\/a>.<\/p>\n\n\n\n<p>The project with code is on my GitHub: <a href=\"https:\/\/github.com\/lamik\/OLED_SSD1306_STM32_HAL\" target=\"_blank\" rel=\"noopener\">link<\/a><\/p>\n\n\n\n<p><span>If you noticed any error, disagree with something, would like to add something important, or simply want to discuss this topic, write a comment. Remember that the discussion should be polite and in accordance with the rules of the Polish language.<\/span><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;4335&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;0&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;0&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;0&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;0\\\/5 - (0 votes)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;title&quot;:&quot;SSD1306 OLED Displays: I2C or SPI?&quot;,&quot;width&quot;:&quot;0&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 0px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 19.2px;\">\n            <span class=\"kksr-muted\"><\/span>\n    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Many people rave about the beauty of OLED displays because there\u2019s something amazing about them. Infinite contrast and high refresh rate are incredible advantages of this technology. On the market you can find plenty of tiny monochrome OLEDs in sizes from about 0.49\u2033 up to roughly 4\u2033. Their price has [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3122,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[160],"tags":[176,174,177],"class_list":["post-4335","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-stm32","tag-programming","tag-stm32","tag-stm32cubemx"],"_links":{"self":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4335","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/comments?post=4335"}],"version-history":[{"count":3,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4335\/revisions"}],"predecessor-version":[{"id":4441,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4335\/revisions\/4441"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media\/3122"}],"wp:attachment":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media?parent=4335"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/categories?post=4335"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/tags?post=4335"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}