1 /*
2 * Copyright (c) 2006-2021, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-11-08 bigmagic first version
9 */
10 #include <rtthread.h>
11 #include <rtdevice.h>
12
13 #include <raspi4.h>
14 #include <drv_spi.h>
15 #include "drv_ili9486.h"
16
17 #ifdef USING_LCD_CONSOLE
18 #include "lcd_console.h"
19 #endif
20
21 //http://www.lcdwiki.com/MHS-3.5inch_RPi_Display
22 #define LCD_DEVICE_NAME ("spi0.0")
23
24 #define LCD_SPI_SEND_FAST
25 //waveshare
26 #define LCD_SCREEN_WIDTH (320)
27 #define LCD_SCREEN_HEIGHT (480)
28
29 #define LCD_RESET_PIN (25)
30 #define LCD_RS_PIN (24)
31
32 #define LCD_SPI_FREQ_MAX (125*1000*1000)
33
34 uint16_t LCD_HEIGHT = LCD_SCREEN_HEIGHT;
35 uint16_t LCD_WIDTH = LCD_SCREEN_WIDTH;
36
37 #define SCREEN_VERTICAL_1 (0)
38 #define SCREEN_HORIZONTAL_1 (1)
39 #define SCREEN_VERTICAL_2 (2)
40 #define SCREEN_HORIZONTAL_2 (3)
41
42 struct rt_semaphore lcd_spi_lock;
43 struct rt_semaphore lcd_lock;
44
45 //rgb565 lcd buffer
46 uint16_t _lcd_buffer[LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT];
47 uint16_t send_buffer[LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT];
48
49 static struct rt_spi_device *lcd_dev;
50
send_cmd(void)51 static inline void send_cmd(void)
52 {
53 rt_pin_write(LCD_RS_PIN, PIN_LOW);
54 }
55
send_data(void)56 static inline void send_data(void)
57 {
58 rt_pin_write(LCD_RS_PIN, PIN_HIGH);
59 }
60
writeData16(rt_uint16_t data)61 void writeData16(rt_uint16_t data)
62 {
63 rt_uint8_t send_data[2];
64 send_data[1] = data & 0x00FF;
65 send_data[0] = ((data >> 8) & 0x00FF);
66 rt_spi_transfer(lcd_dev, &send_data[0], RT_NULL, 2);
67 }
68
writeData(void * dev,rt_uint8_t data)69 void writeData(void* dev,rt_uint8_t data)
70 {
71 writeData16((rt_uint16_t)(data));
72 }
73
writeCommand(void * dev,rt_uint8_t cmd)74 void writeCommand(void* dev, rt_uint8_t cmd)
75 {
76 send_cmd();
77 writeData16((rt_uint16_t)(cmd));
78 send_data();
79 }
80
lcd_write_commmand(rt_uint8_t cmd)81 void lcd_write_commmand(rt_uint8_t cmd)
82 {
83 writeCommand(lcd_dev, cmd);
84 }
85
lcd_write_data(rt_uint8_t data)86 void lcd_write_data(rt_uint8_t data)
87 {
88 writeData(lcd_dev, data);
89 }
90
91 /*Ser rotation of the screen - changes x0 and y0*/
lcd_set_rotation(uint8_t rotation)92 static inline void lcd_set_rotation(uint8_t rotation)
93 {
94 writeCommand(lcd_dev, 0x36);
95 rt_thread_mdelay(100);
96
97 switch(rotation) {
98 case SCREEN_VERTICAL_1:
99 writeData(lcd_dev, 0x48);
100 LCD_WIDTH = 320;
101 LCD_HEIGHT = 480;
102 break;
103 case SCREEN_HORIZONTAL_1:
104 writeData(lcd_dev, 0x28);
105 LCD_WIDTH = 480;
106 LCD_HEIGHT = 320;
107 break;
108 case SCREEN_VERTICAL_2:
109 writeData(lcd_dev, 0x98);
110 LCD_WIDTH = 320;
111 LCD_HEIGHT = 480;
112 break;
113 case SCREEN_HORIZONTAL_2:
114 writeData(lcd_dev, 0xF8);
115 LCD_WIDTH = 480;
116 LCD_HEIGHT = 320;
117 break;
118 default:
119 //EXIT IF SCREEN ROTATION NOT VALID!
120 break;
121 }
122
123 if((rotation == SCREEN_VERTICAL_1) || (rotation == SCREEN_VERTICAL_2))
124 {
125 lcd_write_commmand(0x2A);
126 lcd_write_data(0x00);
127 lcd_write_data(0x00);
128 lcd_write_data(0x01);
129 lcd_write_data(0x3F);
130
131 lcd_write_commmand(0x2B);
132 lcd_write_data(0x00);
133 lcd_write_data(0x00);
134 lcd_write_data(0x01);
135 lcd_write_data(0xE0);
136 }
137
138 if((rotation == SCREEN_HORIZONTAL_1) || (rotation == SCREEN_HORIZONTAL_2))
139 {
140 lcd_write_commmand(0x2B);
141 lcd_write_data(0x00);
142 lcd_write_data(0x00);
143 lcd_write_data(0x01);
144 lcd_write_data(0x3F);
145
146 lcd_write_commmand(0x2A);
147 lcd_write_data(0x00);
148 lcd_write_data(0x00);
149 lcd_write_data(0x01);
150 lcd_write_data(0xE0);
151 }
152 }
153
fast_send_data(void)154 static inline void fast_send_data(void)
155 {
156 rt_uint32_t ii = 0;
157 rt_uint32_t tx_index = 0;
158 char *tx_data = (char *)send_buffer;
159 rt_sem_take(&lcd_spi_lock, RT_WAITING_FOREVER);
160
161 SPI_REG_CS(SPI_0_BASE) &= (~(3 << 0));
162 SPI_REG_CLK(SPI_0_BASE) = 4;
163 SPI_REG_CS(SPI_0_BASE) |= SPI_CS_TA;
164 for(tx_index=0;tx_index<(LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT) * 2;tx_index++)
165 {
166 for(ii = 0; ii < 32; ii = ii + 2)
167 {
168 SPI_REG_FIFO(SPI_0_BASE) = tx_data[tx_index + ii + 1];
169 SPI_REG_FIFO(SPI_0_BASE) = tx_data[tx_index + ii];
170 }
171 while (!(SPI_REG_CS(SPI_0_BASE) & SPI_CS_DONE));
172 SPI_REG_CS(SPI_0_BASE) |= (SPI_CS_CLEAR_TX) | (SPI_CS_CLEAR_RX);
173 tx_index = tx_index + 31;
174 }
175 SPI_REG_CS(SPI_0_BASE) |= (SPI_CS_CLEAR_TX) | (SPI_CS_CLEAR_RX);
176 SPI_REG_CS(SPI_0_BASE) &= (~SPI_CS_TA);
177 rt_sem_release(&lcd_spi_lock);
178 }
179
lcd_show(void)180 static inline void lcd_show(void)
181 {
182
183 lcd_write_commmand(0x2C); // Memory write?
184
185 //rt_thread_mdelay(150);
186
187 #ifdef LCD_SPI_SEND_FAST
188 fast_send_data();
189 #else
190 int i, j;
191 for (i = 0 ; i < 30 ; i ++)
192 {
193 uint16_t *tx_data = (uint16_t*)&send_buffer[5120* i];
194 int32_t data_sz = 5120;
195 for( j=0; j<data_sz; j++)
196 {
197 writeData16(tx_data[j]);
198 }
199 }
200 #endif
201
202 }
203
lcd_init(void)204 static void lcd_init(void)
205 {
206 writeCommand(lcd_dev, 0x28);
207 rt_thread_mdelay(150);
208
209 writeCommand(lcd_dev, 0x3A); // Interface Pixel Format
210 writeData(lcd_dev, 0x55); // 16 bit/pixe
211
212 writeCommand(lcd_dev, 0xC2); // Interface Pixel Format
213 writeData(lcd_dev, 0x44);
214
215 writeCommand(lcd_dev, 0xC5); // VCOM Control
216 writeData(lcd_dev, 0x00);
217 writeData(lcd_dev, 0x00);
218 writeData(lcd_dev, 0x00);
219 writeData(lcd_dev, 0x00);
220
221 writeCommand(lcd_dev, 0xE0); // PGAMCTRL(Positive Gamma Control)
222 writeData(lcd_dev, 0x0F);
223 writeData(lcd_dev, 0x1F);
224 writeData(lcd_dev, 0x1C);
225 writeData(lcd_dev, 0x0C);
226 writeData(lcd_dev, 0x0F);
227 writeData(lcd_dev, 0x08);
228 writeData(lcd_dev, 0x48);
229 writeData(lcd_dev, 0x98);
230 writeData(lcd_dev, 0x37);
231 writeData(lcd_dev, 0x0A);
232 writeData(lcd_dev, 0x13);
233 writeData(lcd_dev, 0x04);
234 writeData(lcd_dev, 0x11);
235 writeData(lcd_dev, 0x0D);
236 writeData(lcd_dev, 0x00);
237
238 writeCommand(lcd_dev, 0xE1); // NGAMCTRL (Negative Gamma Correction)
239 writeData(lcd_dev, 0x0F);
240 writeData(lcd_dev, 0x32);
241 writeData(lcd_dev, 0x2E);
242 writeData(lcd_dev, 0x0B);
243 writeData(lcd_dev, 0x0D);
244 writeData(lcd_dev, 0x05);
245 writeData(lcd_dev, 0x47);
246 writeData(lcd_dev, 0x75);
247 writeData(lcd_dev, 0x37);
248 writeData(lcd_dev, 0x06);
249 writeData(lcd_dev, 0x10);
250 writeData(lcd_dev, 0x03);
251 writeData(lcd_dev, 0x24);
252 writeData(lcd_dev, 0x20);
253 writeData(lcd_dev, 0x00);
254
255 writeCommand(lcd_dev, 0x11); // Sleep out, also SW reset
256 rt_thread_mdelay(150);
257
258 writeCommand(lcd_dev, 0x20); // Display Inversion OFF RPi LCD (A)
259 //writeCommand(lcd_dev, 0x21); // Display Inversion ON RPi LCD (B)
260
261 lcd_set_rotation(SCREEN_VERTICAL_2);
262 writeCommand(lcd_dev, 0x29); // Display ON
263 rt_thread_mdelay(150);
264 }
265
lcd_reset(void)266 static inline void lcd_reset(void)
267 {
268 //Reset signal, low reset (pin22)
269 rt_pin_mode(LCD_RESET_PIN,PIN_MODE_OUTPUT);
270
271 rt_pin_write(LCD_RESET_PIN, PIN_HIGH);
272 rt_thread_mdelay(100);
273 rt_pin_write(LCD_RESET_PIN, PIN_LOW);
274 rt_thread_mdelay(100);
275 rt_pin_write(LCD_RESET_PIN, PIN_HIGH);
276 }
277
278
ili9486_open(rt_device_t dev,rt_uint16_t oflag)279 rt_err_t ili9486_open(rt_device_t dev, rt_uint16_t oflag)
280 {
281 return RT_EOK;
282 }
283
ili9486_close(rt_device_t dev)284 rt_err_t ili9486_close(rt_device_t dev)
285 {
286 return RT_EOK;
287 }
288
ili9486_read(rt_device_t dev,rt_off_t pos,void * buf,rt_size_t size)289 rt_size_t ili9486_read(rt_device_t dev, rt_off_t pos, void *buf, rt_size_t size)
290 {
291 return 0;
292 }
293
ili9486_write(rt_device_t dev,rt_off_t pos,const void * buffer,rt_size_t size)294 rt_size_t ili9486_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
295 {
296 #ifdef USING_LCD_CONSOLE
297 fb_print((char*)buffer);
298 #endif
299 return size;
300 }
301
ili9486_control(rt_device_t dev,int cmd,void * args)302 rt_err_t ili9486_control(rt_device_t dev, int cmd, void *args)
303 {
304 rt_sem_take(&lcd_lock, RT_WAITING_FOREVER);
305 switch (cmd)
306 {
307 case RTGRAPHIC_CTRL_RECT_UPDATE:
308 {
309 struct rt_device_rect_info *info = (struct rt_device_rect_info*)args;
310 info = info;
311 rt_memcpy(send_buffer, _lcd_buffer, LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT * 2);
312 lcd_show();
313 }
314 break;
315
316 case RTGRAPHIC_CTRL_GET_INFO:
317 {
318 struct rt_device_graphic_info* info = (struct rt_device_graphic_info*)args;
319
320 RT_ASSERT(info != RT_NULL);
321 info->pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565;
322 info->bits_per_pixel= 16;
323 info->width = LCD_WIDTH;
324 info->height = LCD_HEIGHT;
325 info->framebuffer = (void *)_lcd_buffer;//lcd->fb;
326 }
327 break;
328 }
329 rt_sem_release(&lcd_lock);
330 return RT_EOK;
331 }
332
333 #ifdef RT_USING_DEVICE_OPS
334 const static struct rt_device_ops ili9486_ops =
335 {
336 RT_NULL,
337 ili9486_open,
338 ili9486_close,
339 ili9486_read,
340 ili9486_write,
341 ili9486_control,
342 };
343 #endif
344
hw_ili9486_lcd_init(void)345 static int hw_ili9486_lcd_init(void)
346 {
347 struct rt_device *device;
348 device = rt_malloc(sizeof(struct rt_device));
349 rt_memset(device, 0, sizeof(struct rt_device));
350
351 lcd_reset();
352 rt_pin_mode(LCD_RS_PIN, PIN_MODE_OUTPUT);
353 lcd_dev = (struct rt_spi_device *)rt_device_find(LCD_DEVICE_NAME);
354 if (!lcd_dev)
355 {
356 rt_kprintf("no %s!\n", LCD_DEVICE_NAME);
357 }
358 lcd_dev->config.max_hz = LCD_SPI_FREQ_MAX;//125M
359 lcd_init();
360
361 rt_sem_init(&lcd_spi_lock, "lcd_spi_lock", 1, RT_IPC_FLAG_FIFO);
362 rt_sem_init(&lcd_lock, "lcd_spi_lock", 1, RT_IPC_FLAG_FIFO);
363 /* set device type */
364 device->type = RT_Device_Class_Graphic;
365 /* initialize device interface */
366 #ifdef RT_USING_DEVICE_OPS
367 device->ops = &ili9486_ops;
368 #else
369 device->init = RT_NULL;
370 device->open = ili9486_open;
371 device->close = ili9486_close;
372 device->read = ili9486_read;
373 device->write = ili9486_write;
374 device->control = ili9486_control;
375 #endif
376 /* register to device manager */
377 rt_device_register(device, "lcd", RT_DEVICE_FLAG_RDWR);
378
379 return RT_EOK;
380 }
381 INIT_DEVICE_EXPORT(hw_ili9486_lcd_init);
382