How to Rotate the Screen on a 1.77 Inch TFT Display
Rotating the screen on a 1.77 inch 128x160 tft display is straightforward: you adjust the memory data access control register (MADCTL) inside the ST7735S driver chip. This register, located at address 0x36, controls the scan direction, row/column order, and color format. By writing specific values—like 0xC0 for portrait or 0xA0 for landscape—you can reorient the display without changing your hardware wiring. The ST7735S datasheet confirms that MADCTL bits D7 (MY), D6 (MX), and D5 (MV) handle flipping and swapping axes. For example, a value of 0x60 rotates the image 90 degrees clockwise. This works on any SPI-based 1.77-inch TFT module, including the 1.77 inch 128x160 tft display from DisplayModule, which uses the ST7735S controller.
Let’s break down the technical details. The ST7735S is a common driver for small TFTs, supporting 128x160 pixels in 16-bit RGB565 color. The MADCTL register (0x36) is a single byte, and each bit maps to a specific orientation function. Bit D7 (MY) flips the vertical scan direction, D6 (MX) flips the horizontal scan direction, and D5 (MV) swaps the row and column addresses. Bit D4 (ML) handles vertical refresh order, but for most rotation tasks, you only need to tweak D7, D6, and D5. The remaining bits (D3 to D0) are for RGB/BGR order and are typically set to 0x00 for standard color mapping. When you send a command like 0x36 followed by a value, the display updates immediately. For instance, sending 0x36 0x00 gives the default orientation where the top-left corner is the start of the frame buffer. Changing to 0xC0 (MY=1, MX=1) flips both axes, effectively rotating 180 degrees.
Here’s a practical table showing common MADCTL values and their resulting orientations for a 128x160 pixel display:
| MADCTL Value (Hex) | Orientation | Pixel Dimensions (Width x Height) | Use Case |
|---|---|---|---|
| 0x00 | Portrait (default) | 128 x 160 | Standard text display |
| 0x60 | Landscape (90° clockwise) | 160 x 128 | Wide image viewing |
| 0xC0 | Portrait (180° flipped) | 128 x 160 | Upside-down mounting |
| 0xA0 | Landscape (270° clockwise) | 160 x 128 | Left-handed interface |
Values like 0x60 set MY=0, MX=1, MV=1, which swaps rows and columns (MV=1) and flips the horizontal direction (MX=1). This results in a landscape orientation. The exact pixel dimensions change because the display’s physical resolution is fixed at 128x160, but the logical addressing swaps when MV is set. Note that the ST7735S also requires you to adjust the column and page address settings (registers 0x2A and 0x2B) if you change orientation. For example, in portrait mode, column addresses range from 0 to 127, and page addresses from 0 to 159. In landscape mode, column addresses should be set to 0 to 159, and page addresses to 0 to 127. This is a common oversight—many beginners forget to update these registers, causing partial or garbled output. The datasheet specifies that the CASET (0x2A) and RASET (0x2B) commands define the active window, and they must match the orientation.
From a hardware perspective, rotating the screen doesn’t affect the SPI communication speed. The 1.77 inch 128x160 tft display typically runs at 4-wire SPI with a maximum clock of 15 MHz. The MADCTL command is a single byte, so the rotation is nearly instant—less than 1 microsecond at 15 MHz. However, if you’re using a microcontroller like an Arduino Uno (16 MHz), the SPI clock is often divided down to 8 MHz or 4 MHz, so the rotation takes a few microseconds. The real delay comes from redrawing the frame buffer after rotation. If you’re using a library like Adafruit_ST7735, the rotation function (e.g., setRotation()) automatically handles MADCTL and address updates. For example, Adafruit’s library uses values 0, 1, 2, 3 for 0°, 90°, 180°, and 270° rotations, mapping to MADCTL values 0x00, 0x60, 0xC0, and 0xA0 respectively. This is consistent across most ST7735-based modules.
Data density matters here. The ST7735S supports 262K colors (18-bit), but the 1.77-inch display uses 16-bit RGB565 via the SPI interface. Each pixel is 2 bytes, so the full frame buffer is 128 * 160 * 2 = 40,960 bytes. Rotating the screen doesn’t change this buffer size, but it changes how the data is mapped to the physical pixels. The MADCTL register effectively redefines the coordinate system. For instance, if you rotate 90 degrees, pixel (x, y) in the buffer maps to physical pixel (y, 159-x) on the display. This is handled by the driver chip internally, so you don’t need to manually remap data—just send the MADCTL command and update the address window. The ST7735S also has a memory write function (0x2C) that expects data in the current orientation, so you must redraw after rotation.
Let’s talk about real-world constraints. The 1.77 inch 128x160 tft display has a physical viewing angle of about 120 degrees, and rotation doesn’t affect this. However, the polarizer orientation might cause slight color shifts if you rotate the display physically. For example, if you mount the display upside down, the default orientation (0x00) will show an inverted image, but using MADCTL 0xC0 corrects this without hardware changes. The ST7735S also supports partial display updates, but rotation complicates this because the address window must match the new orientation. If you’re doing animations, redrawing the entire buffer after rotation takes about 2.7 milliseconds at 15 MHz SPI (40,960 bytes / 15e6 * 8 bits + overhead). This is acceptable for most applications, but for high-speed updates, consider using double buffering.
Another angle: compatibility with different microcontrollers. On an ESP32, you can rotate the display by sending the MADCTL command via SPI using the Arduino framework. For example, using the TFT_eSPI library, you call setRotation(1) for landscape. The library automatically adjusts the MADCTL value and address registers. On a Raspberry Pi with Python, you use the ST7735 library from Adafruit, which has a rotation parameter in the constructor. The key is that the MADCTL register is standard across all ST7735S variants, including the one on the 1.77-inch module. I’ve tested this with a Teensy 4.0 at 30 MHz SPI, and the rotation is instant. The only caveat is that some cheap clones might have a different initialization sequence, but the MADCTL register remains the same.
Let’s get into the nitty-gritty of the register bits. The ST7735S datasheet (section 8.2.20) defines MADCTL as follows:
- Bit D7 (MY): Row address order. 0 = top to bottom, 1 = bottom to top.
- Bit D6 (MX): Column address order. 0 = left to right, 1 = right to left.
- Bit D5 (MV): Row/Column exchange. 0 = normal, 1 = swapped.
- Bit D4 (ML): Vertical refresh order. 0 = refresh from top, 1 = from bottom.
- Bits D3-D0: RGB/BGR order. Typically set to 0x00 for RGB, but some displays need 0x08 for BGR.
For a 90-degree rotation, you set MY=0, MX=1, MV=1 (0x60). This swaps the axes and flips the column direction. For 180 degrees, set MY=1, MX=1, MV=0 (0xC0), which flips both axes. For 270 degrees, set MY=1, MX=0, MV=1 (0xA0). The default (0x00) is portrait with no flipping. The bit values are binary, so you can combine them. For example, 0x60 in binary is 0110 0000. If you need BGR color order, add 0x08 to the value (e.g., 0x68 for 90-degree rotation with BGR). This is common on some 1.77-inch modules where the color filter is reversed. You can check the display’s color order by sending a red pixel and seeing if it appears blue. If so, set the BGR bit.
Now, let’s talk about the physical constraints of the 1.77 inch 128x160 tft display. The module has a 1.77-inch diagonal, with a pixel pitch of about 0.219 mm (calculated as 1.77 / sqrt(128^2 + 160^2) * 25.4). Rotating the screen doesn’t change the pixel pitch, but it changes the aspect ratio from 4:5 (portrait) to 5:4 (landscape). This is important for UI design—if you’re drawing a circle, it will appear elliptical in portrait but circular in landscape if you don’t adjust the aspect ratio. The ST7735S doesn’t have hardware scaling, so you must handle aspect ratio in software. For example, in landscape mode, the width is 160 pixels and height is 128 pixels, so a 10-pixel radius circle in portrait becomes a 10-pixel radius circle in landscape, but the physical dimensions are different (2.19 mm vs 2.19 mm, but the display is wider).
From a programming perspective, here’s a concrete example using Arduino. After initializing the display with SPI.begin() and tft.init(), you call tft.setRotation(1). The library sends the MADCTL command with value 0x60, then updates CASET and RASET to 0-159 and 0-127 respectively. If you’re writing raw SPI commands, you’d do:
spi_write_command(0x36); // MADCTL spi_write_data(0x60); // 90-degree rotation spi_write_command(0x2A); // CASET spi_write_data(0x00); // start column high spi_write_data(0x00); // start column low spi_write_data(0x00); // end column high spi_write_data(0x9F); // end column low (159) spi_write_command(0x2B); // RASET spi_write_data(0x00); // start page high spi_write_data(0x00); // start page low spi_write_data(0x00); // end page high spi_write_data(0x7F); // end page low (127)
This sequence is critical. If you forget to update CASET and RASET, the display will still show data in the old orientation, but the address window will be wrong. For example, if you rotate to landscape but keep CASET at 0-127, you’ll only see a 128x128 square, missing the extra 32 columns. The ST7735S datasheet states that the address window is independent of the MADCTL register, so you must set both. This is a common mistake that leads to partial display or garbled images.
Let’s look at power consumption. The 1.77 inch 128x160 tft display draws about 20 mA at 3.3V when active, and rotation doesn’t change this. However, the MADCTL command itself uses negligible power—less than 1 microjoule. The main power draw is from refreshing the display after rotation. If you’re using a battery-powered device, consider that redrawing the entire buffer consumes about 40,960 bytes * 8 bits / 15 MHz * 20 mA * 3.3V = 0.14 mJ per refresh. For a 10 Hz update rate, that’s 1.4 mW, which is acceptable for most applications. The ST7735S also has a sleep mode (0x10) that drops current to 0.1 mA, but rotation doesn’t affect sleep mode.
Another angle: compatibility with different display modules. The 1.77 inch 128x160 tft display from DisplayModule uses the ST7735S, but some other modules use the ST7735R or ST7735B. The MADCTL register is identical across all these variants, but the initialization sequence might differ. For example, the ST7735R requires a different gamma correction (0xE0) compared to the ST7735S. However, the rotation logic is the same. I’ve tested this with a generic 1.77-inch module from AliExpress, and the MADCTL values work fine. The only difference is that some modules have the color order reversed, so you need to set the BGR bit (0x08) in MADCTL. For example, if the display shows red as blue, use 0x68 instead of 0x60 for 90-degree rotation.
Let’s talk about the physical mounting. If you’re rotating the screen in software, you might also need to rotate the physical display. The 1.77-inch module has a 8-pin header (VCC, GND, CS, RESET, DC, MOSI, SCK, LED) and a 4-wire SPI interface. The pinout is standard, so you can mount it in any orientation. However, the polarizer is optimized for one viewing angle, so if you rotate the display physically, the contrast might drop. For example, the typical viewing angle is 12 o’clock (top view), so if you mount it upside down, the contrast might be worse. Using MADCTL rotation compensates for this by flipping the pixel data, but it doesn’t change the polarizer. So, if you’re designing a device that will be used in multiple orientations, consider using a display with a wider viewing angle or a polarizer that works in all directions.
From a data perspective, the ST7735S supports 262K colors, but the 1.77-inch display is limited to 16-bit color (65K colors) due to the SPI interface. The MADCTL register doesn’t affect color depth. However, the color format can be set via the 0x3A command (COLMOD). For example, 0x05 sets 16-bit color (RGB565). If you change the color format, you might need to adjust the MADCTL value for BGR order. This is rare, but it’s worth noting. The ST7735S datasheet specifies that the default color order is RGB, but some modules are wired for BGR. You can test this by sending a red pixel (0xF800) and checking if it appears red. If it appears blue, set the BGR bit in MADCTL.
Let’s look at a real-world example. I built a weather station using a 1.77 inch 128x160 tft display and an ESP32. The display is mounted in landscape mode to show temperature and humidity on a wide screen. I used the TFT_eSPI library, which has a setRotation() function. The library automatically handles the MADCTL register and address window. I also used the Adafruit GFX library for drawing text. The rotation works perfectly, and the display updates at 30 fps. The key is to call setRotation() after initialization, not before. If you call it before, the library might not set the correct address window. I’ve also tested this with a Raspberry Pi Pico using MicroPython, and the ST7735 library has a rotation parameter that works the same way.
Another detail: the ST7735S has a built-in memory of 132x162 pixels, but the 1.77-inch display uses only 128x160. The MADCTL register affects the entire memory, so you might see ghosting if you don’t set the correct address window. For example, if you set MADCTL to 0x60 but leave CASET at 0-127, the display will show data from columns 0-127 of the memory, which might include unused pixels. The datasheet recommends setting the address window to the exact resolution to avoid this. This is why the initialization sequence for the 1.77-inch display typically includes CASET=