1 /*
2 * Copyright 2023-2024 NXP
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include "fsl_st7796s.h"
9
10 #define ST7796S_ERROR_CHECK(x) \
11 do \
12 { \
13 status_t ret = (x); \
14 if (ret != kStatus_Success) \
15 { \
16 return ret; \
17 } \
18 } while (false)
19
20 #define ST7796S_CMD_SWRESET (0x01U)
21 #define ST7796S_CMD_SLPIN (0x10U)
22 #define ST7796S_CMD_SLPOUT (0x11U)
23 #define ST7796S_CMD_INVOFF (0x20U)
24 #define ST7796S_CMD_INVON (0x21U)
25 #define ST7796S_CMD_DISPOFF (0x28U)
26 #define ST7796S_CMD_DISPON (0x29U)
27 #define ST7796S_CMD_CASET (0x2AU)
28 #define ST7796S_CMD_RASET (0x2BU)
29 #define ST7796S_CMD_RAMWR (0x2CU)
30 #define ST7796S_CMD_TEOFF (0x34U)
31 #define ST7796S_CMD_TEON (0x35U)
32 #define ST7796S_CMD_MADCTL (0x36U)
33 #define ST7796S_CMD_COLMOD (0x3AU)
34 #define ST7796S_CMD_CSCON (0xF0U)
35 #define ST7796S_CMD_INVTR (0xB4U)
36 #define ST7796S_CMD_FRMCTR1 (0xB1U)
37 #define ST7796S_CMD_BPC (0xB5U)
38 #define ST7796S_CMD_DFC (0xB6U)
39 #define ST7796S_CMD_PWR1 (0xC0U)
40 #define ST7796S_CMD_PWR2 (0xC1U)
41 #define ST7796S_CMD_PWR3 (0xC2U)
42 #define ST7796S_CMD_VCMPCTL (0xC5U)
43 #define ST7796S_CMD_DOCA (0xE8U)
44 #define ST7796S_CMD_PGC (0xE0U)
45 #define ST7796S_CMD_NGC (0xE1U)
46
47 /*
48 * This is the panel-specific initialization parameters for driver section,
49 * including analog power/voltage control, gamma correction, scan timing, etc.
50 * Data format: | 1 Byte parameter length | 1 Byte command byte | N Bytes command parameter |
51 */
52 static const uint8_t s_st7796s_driver_preset_pars035[] = {
53 /* clang-format off */
54 0x01, ST7796S_CMD_CSCON, 0xC3, // Enable command part 1
55 0x01, ST7796S_CMD_CSCON, 0x96, // Enable command part 2
56 0x01, ST7796S_CMD_INVTR, 0x01, // Display inversion
57 0x02, ST7796S_CMD_FRMCTR1, 0x80, 0x10, // Frame rate control 1
58 0x04, ST7796S_CMD_BPC, 0x1F, 0x50, 0x00, 0x20, // Blanking porch control
59 0x03, ST7796S_CMD_DFC, 0x8A, 0x07, 0x3B, // Display function control
60 0x02, ST7796S_CMD_PWR1, 0x80, 0x64, // Power control 1
61 0x01, ST7796S_CMD_PWR2, 0x13, // Power control 2
62 0x01, ST7796S_CMD_PWR3, 0xA7, // Power control 3
63 0x01, ST7796S_CMD_VCMPCTL, 0x09, // VCOM control
64 0x08, ST7796S_CMD_DOCA, 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33, // DOCA
65 0x0E, ST7796S_CMD_PGC, 0xF0, 0x06, 0x0B, 0x07, 0x06, 0x05, 0x2E, 0x33, 0x47, 0x3A, 0x17, 0x16, 0x2E, 0x31, // PGC
66 0x0E, ST7796S_CMD_NGC, 0xF0, 0x09, 0x0D, 0x09, 0x08, 0x23, 0x2E, 0x33, 0x46, 0x38, 0x13, 0x13, 0x2C, 0x32, // NGC
67 0x01, ST7796S_CMD_CSCON, 0x3C, // Disable command part 1
68 0x01, ST7796S_CMD_CSCON, 0x69, // Disable command part 2
69 /* clang-format on */
70 };
71
ST7796S_WriteCommand(st7796s_handle_t * handle,uint8_t command,const uint8_t * params,uint8_t param_len)72 static status_t ST7796S_WriteCommand(st7796s_handle_t *handle,
73 uint8_t command,
74 const uint8_t *params,
75 uint8_t param_len)
76 {
77 #if MCUX_DBI_LEGACY
78 ST7796S_ERROR_CHECK(handle->xferOps->writeCommand(handle->xferOpsData, command));
79
80 for (uint8_t i = 0; i < param_len; i++)
81 {
82 uint16_t param_data = params[i];
83
84 ST7796S_ERROR_CHECK(handle->xferOps->writeData(handle->xferOpsData, ¶m_data, 2U));
85 }
86
87 return kStatus_Success;
88 #else
89 return DBI_IFACE_WriteCmdData(handle->dbiIface, command, params, param_len);
90 #endif
91 }
92
ST7796S_PresetDriver(st7796s_handle_t * handle,st7796s_driver_preset_t preset)93 static status_t ST7796S_PresetDriver(st7796s_handle_t *handle, st7796s_driver_preset_t preset)
94 {
95 const uint8_t *preset_ptr;
96 uint16_t preset_len;
97
98 if (kST7796S_DriverPresetLCDPARS035 == preset)
99 {
100 preset_ptr = s_st7796s_driver_preset_pars035;
101 preset_len = ARRAY_SIZE(s_st7796s_driver_preset_pars035);
102 }
103 else
104 {
105 return kStatus_InvalidArgument;
106 }
107
108 uint16_t i = 0;
109
110 while (i < preset_len)
111 {
112 uint8_t param_len = preset_ptr[i];
113
114 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, preset_ptr[i + 1u], &preset_ptr[i + 2u], param_len));
115 i += ((uint16_t)param_len + 2u); /* Next = parameter length + command byte + length itself */
116 }
117
118 return kStatus_Success;
119 }
120
ST7796S_SoftwareReset(st7796s_handle_t * handle)121 static status_t ST7796S_SoftwareReset(st7796s_handle_t *handle)
122 {
123 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_SWRESET, NULL, 0));
124 SDK_DelayAtLeastUs(5 * 1000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
125
126 return kStatus_Success;
127 }
128
ST7796S_SleepMode(st7796s_handle_t * handle,bool sleep)129 static status_t ST7796S_SleepMode(st7796s_handle_t *handle, bool sleep)
130 {
131 uint8_t slp_cmd;
132 if (sleep)
133 {
134 slp_cmd = ST7796S_CMD_SLPIN;
135 }
136 else
137 {
138 slp_cmd = ST7796S_CMD_SLPOUT;
139 }
140
141 return ST7796S_WriteCommand(handle, slp_cmd, NULL, 0);
142 }
143
144 #if MCUX_DBI_LEGACY
ST7796S_Init(st7796s_handle_t * handle,const st7796s_config_t * config,const dbi_xfer_ops_t * xferOps,void * xferOpsData)145 status_t ST7796S_Init(st7796s_handle_t *handle,
146 const st7796s_config_t *config,
147 const dbi_xfer_ops_t *xferOps,
148 void *xferOpsData)
149 #else
150 status_t ST7796S_Init(st7796s_handle_t *handle, const st7796s_config_t *config, dbi_iface_t *dbiIface)
151 #endif
152 {
153 #if MCUX_DBI_LEGACY
154 handle->xferOps = xferOps;
155 handle->xferOpsData = xferOpsData;
156 #else
157 handle->dbiIface = dbiIface;
158 #endif
159
160 ST7796S_ERROR_CHECK(ST7796S_SoftwareReset(handle));
161 ST7796S_ERROR_CHECK(ST7796S_PresetDriver(handle, config->driverPreset));
162 ST7796S_ERROR_CHECK(ST7796S_SleepMode(handle, false));
163 ST7796S_ERROR_CHECK(ST7796S_Config(handle, config));
164
165 return kStatus_Success;
166 }
167
ST7796S_InvertDisplay(st7796s_handle_t * handle,bool invert)168 status_t ST7796S_InvertDisplay(st7796s_handle_t *handle, bool invert)
169 {
170 uint8_t inv_cmd;
171 if (invert)
172 {
173 inv_cmd = ST7796S_CMD_INVON;
174 }
175 else
176 {
177 inv_cmd = ST7796S_CMD_INVOFF;
178 }
179
180 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, inv_cmd, NULL, 0U));
181
182 return kStatus_Success;
183 }
184
ST7796S_EnableDisplay(st7796s_handle_t * handle,bool enable)185 status_t ST7796S_EnableDisplay(st7796s_handle_t *handle, bool enable)
186 {
187 uint8_t disp_cmd;
188 if (enable)
189 {
190 disp_cmd = ST7796S_CMD_DISPON;
191 }
192 else
193 {
194 disp_cmd = ST7796S_CMD_DISPOFF;
195 }
196
197 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, disp_cmd, NULL, 0U));
198
199 return kStatus_Success;
200 }
201
ST7796S_SetPixelFormat(st7796s_handle_t * handle,st7796s_pixel_format_t pixelFormat)202 status_t ST7796S_SetPixelFormat(st7796s_handle_t *handle, st7796s_pixel_format_t pixelFormat)
203 {
204 uint8_t pixel_fmt = (uint8_t)pixelFormat;
205 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_COLMOD, &pixel_fmt, 0x01U));
206
207 return kStatus_Success;
208 }
209
ST7796S_SetTEConfig(st7796s_handle_t * handle,st7796s_te_config_t teConfig)210 status_t ST7796S_SetTEConfig(st7796s_handle_t *handle, st7796s_te_config_t teConfig)
211 {
212 if (teConfig == kST7796S_TEDisabled)
213 {
214 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_TEOFF, NULL, 0U));
215 }
216 else
217 {
218 uint8_t te_cfg = 0x00U;
219 if (teConfig == kST7796S_TEHVSync)
220 {
221 te_cfg |= 0x01U; /* Set TEM bit */
222 }
223
224 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_TEON, &te_cfg, 1U));
225 }
226
227 return kStatus_Success;
228 }
229
ST7796S_SelectArea(st7796s_handle_t * handle,uint16_t startX,uint16_t startY,uint16_t endX,uint16_t endY)230 status_t ST7796S_SelectArea(st7796s_handle_t *handle, uint16_t startX, uint16_t startY, uint16_t endX, uint16_t endY)
231 {
232 uint8_t tx_buf[4];
233
234 tx_buf[0] = (uint8_t)(startX >> 0x08U) & 0xFFU;
235 tx_buf[1] = (uint8_t)startX & 0xFFU;
236 tx_buf[2] = (uint8_t)(endX >> 0x08U) & 0xFFU;
237 tx_buf[3] = (uint8_t)endX & 0xFFU;
238
239 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_CASET, tx_buf, 4U));
240
241 tx_buf[0] = (uint8_t)(startY >> 0x08U) & 0xFFU;
242 tx_buf[1] = (uint8_t)startY & 0xFFU;
243 tx_buf[2] = (uint8_t)(endY >> 0x08U) & 0xFFU;
244 tx_buf[3] = (uint8_t)endY & 0xFFU;
245
246 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_RASET, tx_buf, 4U));
247
248 return kStatus_Success;
249 }
250
ST7796S_Config(st7796s_handle_t * handle,const st7796s_config_t * config)251 status_t ST7796S_Config(st7796s_handle_t *handle, const st7796s_config_t *config)
252 {
253 ST7796S_ERROR_CHECK(ST7796S_InvertDisplay(handle, config->invertDisplay));
254 ST7796S_ERROR_CHECK(ST7796S_SetPixelFormat(handle, config->pixelFormat));
255 ST7796S_ERROR_CHECK(ST7796S_SetTEConfig(handle, config->teConfig));
256
257 uint8_t tx_buf[1];
258
259 tx_buf[0] = (uint8_t)config->orientationMode;
260 if (!config->bgrFilter)
261 {
262 tx_buf[0] &= (uint8_t)(~0x08U);
263 }
264
265 if (config->flipDisplay)
266 {
267 if ((config->orientationMode == kST7796S_Orientation90) || (config->orientationMode == kST7796S_Orientation270))
268 {
269 tx_buf[0] ^= 0x80U;
270 }
271 else
272 {
273 tx_buf[0] ^= 0x40U;
274 }
275 }
276
277 ST7796S_ERROR_CHECK(ST7796S_WriteCommand(handle, ST7796S_CMD_MADCTL, tx_buf, 1U));
278
279 handle->orientationMode = config->orientationMode;
280
281 return kStatus_Success;
282 }
283
ST7796S_WritePixels(st7796s_handle_t * handle,uint16_t * pixels,uint32_t len)284 status_t ST7796S_WritePixels(st7796s_handle_t *handle, uint16_t *pixels, uint32_t len)
285 {
286 #if MCUX_DBI_LEGACY
287 ST7796S_ERROR_CHECK(handle->xferOps->writeMemory(handle->xferOpsData, ST7796S_CMD_RAMWR, pixels, len * 2u));
288 #else
289 ST7796S_ERROR_CHECK(DBI_IFACE_WriteMemory(handle->dbiIface, (const uint8_t *)pixels, len * 2u));
290 #endif
291
292 return kStatus_Success;
293 }
294
295 #if MCUX_DBI_LEGACY
ST7796S_SetMemoryDoneCallback(st7796s_handle_t * handle,dbi_mem_done_callback_t callback,void * userData)296 void ST7796S_SetMemoryDoneCallback(st7796s_handle_t *handle, dbi_mem_done_callback_t callback, void *userData)
297 {
298 handle->xferOps->setMemoryDoneCallback(handle->xferOpsData, callback, userData);
299 }
300 #endif
301