|
| 1 | +#include <lcddrvce.h> |
| 2 | +#include <graphx.h> |
| 3 | +#include <sys/lcd.h> |
| 4 | +#include <ti/getcsc.h> |
| 5 | +#include <stdint.h> |
| 6 | +#include <string.h> |
| 7 | + |
| 8 | +#define HALF_LCD_WIDTH (LCD_WIDTH / 2) |
| 9 | +#define HALF_LCD_HEIGHT (LCD_HEIGHT / 2) |
| 10 | + |
| 11 | +/* Function Prototypes */ |
| 12 | +void PrintCentered(const char *str, unsigned int border); |
| 13 | +void QuarterResFillScreen(uint8_t color); |
| 14 | +void QuarterResSwapDraw(void); |
| 15 | +void SetHalfResMode(bool enable); |
| 16 | + |
| 17 | +int main(void) |
| 18 | +{ |
| 19 | + /* Initialize GraphX first, because it sets LCD timing */ |
| 20 | + gfx_Begin(); |
| 21 | + |
| 22 | + /* Set half-resolution mode */ |
| 23 | + SetHalfResMode(true); |
| 24 | + |
| 25 | + /* Set clipping to an 160x120 area that draws to every second line of the half-res buffer */ |
| 26 | + gfx_SetClipRegion(0, 0, HALF_LCD_WIDTH - 1, HALF_LCD_HEIGHT - 1); |
| 27 | + gfx_SetDrawBuffer(); |
| 28 | + |
| 29 | + unsigned int border = 2; |
| 30 | + int dir = 1; |
| 31 | + |
| 32 | + /* Wait for a keypress */ |
| 33 | + while (!os_GetCSC()) |
| 34 | + { |
| 35 | + /* Clear the screen, gfx_FillScreen covers too much area so use this instead */ |
| 36 | + QuarterResFillScreen(255); |
| 37 | + |
| 38 | + /* Print the message on the screen */ |
| 39 | + PrintCentered("Hello, World!", border); |
| 40 | + |
| 41 | + /* Swap buffers and fill in every second line */ |
| 42 | + QuarterResSwapDraw(); |
| 43 | + |
| 44 | + /* Change the border size for the next frame */ |
| 45 | + border += dir; |
| 46 | + if (border <= 2 || border >= 25) |
| 47 | + { |
| 48 | + dir = -dir; |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + /* Ensure one empty frame is fully displayed to make the transition to full-res clean */ |
| 53 | + gfx_FillScreen(255); |
| 54 | + gfx_SwapDraw(); |
| 55 | + gfx_FillScreen(255); |
| 56 | + gfx_SwapDraw(); |
| 57 | + |
| 58 | + /* Restore normal resolution mode */ |
| 59 | + SetHalfResMode(false); |
| 60 | + |
| 61 | + /* Wait for the second empty frame to display */ |
| 62 | + gfx_Wait(); |
| 63 | + |
| 64 | + /* Deinitialize GraphX */ |
| 65 | + gfx_End(); |
| 66 | + |
| 67 | + return 0; |
| 68 | +} |
| 69 | + |
| 70 | +/* Prints a quarter-res screen centered string */ |
| 71 | +void PrintCentered(const char *str, unsigned int border) |
| 72 | +{ |
| 73 | + unsigned int width = gfx_GetStringWidth(str); |
| 74 | + gfx_PrintStringXY(str, |
| 75 | + (HALF_LCD_WIDTH - width) / 2, |
| 76 | + (HALF_LCD_HEIGHT - 8) / 2); |
| 77 | + gfx_Circle(HALF_LCD_WIDTH / 2, HALF_LCD_HEIGHT / 2, width / 2 + border); |
| 78 | +} |
| 79 | + |
| 80 | +/* Fills the drawable area of a quarter-res screen */ |
| 81 | +void QuarterResFillScreen(uint8_t color) |
| 82 | +{ |
| 83 | + uint8_t oldColor = gfx_SetColor(color); |
| 84 | + gfx_FillRectangle_NoClip(0, 0, HALF_LCD_WIDTH, HALF_LCD_HEIGHT); |
| 85 | + gfx_SetColor(oldColor); |
| 86 | +} |
| 87 | + |
| 88 | +/* Copies the rendered lines to the unrendered lines before swapping the buffer */ |
| 89 | +void QuarterResSwapDraw(void) |
| 90 | +{ |
| 91 | + gfx_CopyRectangle(gfx_buffer, gfx_buffer, 0, 0, HALF_LCD_WIDTH, 0, HALF_LCD_WIDTH, HALF_LCD_HEIGHT); |
| 92 | + gfx_SwapDraw(); |
| 93 | +} |
| 94 | + |
| 95 | +void SetHalfResMode(bool enable) |
| 96 | +{ |
| 97 | + typedef struct lcd_timing |
| 98 | + { |
| 99 | + uint32_t : 2; |
| 100 | + uint32_t PPL : 6; |
| 101 | + uint32_t HSW : 8; |
| 102 | + uint32_t HFP : 8; |
| 103 | + uint32_t HBP : 8; |
| 104 | + |
| 105 | + uint32_t LPP : 10; |
| 106 | + uint32_t VSW : 6; |
| 107 | + uint32_t VFP : 8; |
| 108 | + uint32_t VBP : 8; |
| 109 | + |
| 110 | + uint32_t PCD_LO : 5; |
| 111 | + uint32_t CLKSEL : 1; |
| 112 | + uint32_t ACB : 5; |
| 113 | + uint32_t IVS : 1; |
| 114 | + uint32_t IHS : 1; |
| 115 | + uint32_t IPC : 1; |
| 116 | + uint32_t IOE : 1; |
| 117 | + uint32_t : 1; |
| 118 | + uint32_t CPL : 10; |
| 119 | + uint32_t BCD : 1; |
| 120 | + uint32_t PCD_HI : 5; |
| 121 | + } lcd_timing_t; |
| 122 | + |
| 123 | + typedef struct res_settings |
| 124 | + { |
| 125 | + uint8_t frctrl2; |
| 126 | + uint8_t bpa; |
| 127 | + uint16_t xe; |
| 128 | + uint8_t tfa; |
| 129 | + uint8_t ramctrl1; |
| 130 | + lcd_timing_t timing; |
| 131 | + } res_settings_t; |
| 132 | + |
| 133 | + static res_settings_t settings[2] = |
| 134 | + { |
| 135 | + { |
| 136 | + /* Default ST7789 settings */ |
| 137 | + .frctrl2 = LCD_FRCTRL2_DEFAULT, |
| 138 | + .bpa = LCD_BPA_DEFAULT, |
| 139 | + .xe = LCD_WIDTH - 1, |
| 140 | + .tfa = 0, |
| 141 | + .ramctrl1 = LCD_RAMCTRL1_DEFAULT |
| 142 | + }, |
| 143 | + { |
| 144 | + /* With the following ST7789 timing: |
| 145 | + * Refreshes LCD in at most 16.51 ms after VSYNC, assuming worst case 9.5 MHz clock |
| 146 | + * Waits at least 3.42 ms after VSYNC to read LCD memory, assuming worst case 10.5 MHz clock |
| 147 | + */ |
| 148 | + .frctrl2 = 8, /* 378 clocks per line */ |
| 149 | + .bpa = 95, /* 95 lines of back porch */ |
| 150 | + .xe = HALF_LCD_WIDTH - 1, |
| 151 | + .tfa = HALF_LCD_WIDTH, |
| 152 | + .ramctrl1 = LCD_RAM_RGB | LCD_DM_VSYNC, |
| 153 | + /* With the following PL111 timing: |
| 154 | + * Refreshes LCD at 60 Hz = 24 MHz / (800*250*2), a VSYNC period of 16.67 ms |
| 155 | + * Outputs 38400 pixels to LCD memory within the first 3.40 ms after VSYNC |
| 156 | + */ |
| 157 | + .timing = |
| 158 | + { |
| 159 | + .PPL = 768 / 16 - 1, /* 768 pixels per line */ |
| 160 | + .HSW = 1 - 1, |
| 161 | + .HFP = (800 - 768 - 1 - 1) - 1, /* 800 total clocks per line */ |
| 162 | + .HBP = 1 - 1, |
| 163 | + .LPP = HALF_LCD_WIDTH * LCD_HEIGHT / 768, /* 50 lines */ |
| 164 | + .VSW = 1 - 1, |
| 165 | + .VFP = 250 - (HALF_LCD_WIDTH * LCD_HEIGHT / 768) - 1, /* 250 total lines */ |
| 166 | + .VBP = 0, |
| 167 | + .PCD_LO = (2 - 2) & 0x1F, /* clock divisor of 2 */ |
| 168 | + .CLKSEL = 0, |
| 169 | + .ACB = 0, |
| 170 | + .IVS = 1, |
| 171 | + .IHS = 1, |
| 172 | + .IPC = 1, |
| 173 | + .IOE = 1, |
| 174 | + .CPL = 768 - 1, |
| 175 | + .BCD = 0, |
| 176 | + .PCD_HI = (2 - 2) >> 5 |
| 177 | + } |
| 178 | + } |
| 179 | + }; |
| 180 | + |
| 181 | + const res_settings_t *p = &settings[enable]; |
| 182 | + |
| 183 | + /* Initialize LCD driver */ |
| 184 | + lcd_Init(); |
| 185 | + |
| 186 | + /* Set clocks per line */ |
| 187 | + lcd_SetNormalFrameRateControl(p->frctrl2); |
| 188 | + /* Set back porch */ |
| 189 | + lcd_SendCommand1(LCD_CMD_PORCTRL, p->bpa); |
| 190 | + /* Set horizontal output window */ |
| 191 | + lcd_SetColumnAddress(0, p->xe); |
| 192 | + /* Set fixed left scroll area */ |
| 193 | + lcd_SetScrollArea(p->tfa, LCD_WIDTH - p->tfa, 0); |
| 194 | + /* Set starting vertical scroll address to 0 */ |
| 195 | + lcd_SetScrollAddress(0); |
| 196 | + /* Set display mode */ |
| 197 | + lcd_SetRamInterface(p->ramctrl1); |
| 198 | + /* Set interlace mode */ |
| 199 | + lcd_SetInterlacedMode(enable); |
| 200 | + |
| 201 | + /* Save old display timing when enabling */ |
| 202 | + if (enable) |
| 203 | + { |
| 204 | + memcpy(&settings[0].timing, (const void *)&lcd_Timing0, sizeof(settings[0].timing)); |
| 205 | + } |
| 206 | + /* Set display timing */ |
| 207 | + memcpy((void *)&lcd_Timing0, &p->timing, sizeof(p->timing)); |
| 208 | + |
| 209 | + /* Cleanup LCD driver */ |
| 210 | + lcd_Cleanup(); |
| 211 | +} |
0 commit comments