6 #include <hal_devices/spi_ili9341.h>
7 #include <satcat5/cfgbus_spi.h>
25 #ifndef SATCAT5_ILI9341_BURST
26 #define SATCAT5_ILI9341_BURST 32
34 static constexpr u16 BURST_PIXELS = SATCAT5_ILI9341_BURST;
35 static constexpr u16 BURST_CADDR = 5;
36 static constexpr u16 BURST_PADDR = 5;
37 static constexpr u16 BURST_RAMWR = 1 + 2*BURST_PIXELS;
38 static constexpr u16 BURST_BYTES = BURST_CADDR + BURST_PADDR + BURST_RAMWR;
39 static_assert(2*BURST_BYTES <= SATCAT5_SPI_TXBUFF);
43 static constexpr u16 TFT_WIDTH = 240;
44 static constexpr u16 TFT_HEIGHT = 320;
46 inline constexpr u16 effective_height(u8 madctl)
47 {
return (madctl & Ili9341::MADCTL_MV) ? TFT_WIDTH : TFT_HEIGHT; }
48 inline constexpr u16 effective_width(u8 madctl)
49 {
return (madctl & Ili9341::MADCTL_MV) ? TFT_HEIGHT : TFT_WIDTH; }
86 static constexpr u8 STARTUP[] = {
88 4, CMD_UNKNOWN, 0x03, 0x80, 0x02,
89 4, CMD_PWRCTRB, 0x00, 0xC1, 0x30,
90 5, CMD_PWRSEQ, 0x64, 0x03, 0x12, 0x81,
91 4, CMD_DRVTIMA, 0x85, 0x00, 0x78,
92 6, CMD_PWRCTRA, 0x39, 0x2C, 0x00, 0x34, 0x02,
94 3, CMD_DRVTIMB, 0x00, 0x00,
97 3, CMD_VCMCTR1, 0x3E, 0x28,
101 3, CMD_FRMCTR1, 0x00, 0x18,
102 4, CMD_DFUNCTR, 0x08, 0x82, 0x27,
105 16, CMD_GMCTRP1, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
106 16, CMD_GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
108 1, CMD_DISPON, 0, 150};
110 static constexpr
unsigned INIT_DONE =
sizeof(STARTUP) + 1;
113 :
Display(effective_height(madctl), effective_width(madctl))
135 return (m_init_step < INIT_DONE) || (m_draw_step < m_draw_done) || m_spi->
busy();
140 u8 cmd = inv ? CMD_INVON : CMD_INVOFF;
141 return spi_cmd(1, &cmd, 0);
159 return (m_init_step < INIT_DONE) || spi_vscrdef();
164 if (
busy())
return false;
183 m_draw_done = UINT16_MAX;
184 try_twidth(BURST_PIXELS);
185 for (u16 w = 1 ; w < BURST_PIXELS/2 ; ++w)
196 if (!m_viewsize)
return true;
198 u16 tmp = m_scroll + u16(rows);
199 if (rows < 0 && tmp >= m_viewsize) tmp += m_viewsize;
200 if (rows > 0 && tmp >= m_viewsize) tmp -= m_viewsize;
202 u8 cmd[3] = {CMD_VSCRSET};
203 write_be_u16(cmd + 1, m_viewtop + tmp);
204 bool ok = spi_cmd(
sizeof(cmd), cmd, 0);
206 if (ok) m_scroll = tmp;
210 void Ili9341::spi_done(
unsigned nread,
const u8* rbytes) {
211 if (m_init_step < INIT_DONE) init_next();
212 else if (m_draw_step < m_draw_done) draw_next();
216 if (m_init_step < INIT_DONE) init_next();
217 else if (m_draw_step < m_draw_done) draw_next();
220 bool Ili9341::in_viewport(u16 row)
const {
221 return (m_viewtop <= row) && (row < m_viewtop + m_viewsize);
228 void Ili9341::draw_next() {
231 u16 tile_width = min_u16(m_tile_width, m_draw_cmd.
width() - m_tile_col);
232 u16 tile_height = min_u16(m_tile_height, m_draw_cmd.
height() - m_tile_row);
235 write_be_u16(cmd + 1, m_cursor.
c + m_tile_col);
236 write_be_u16(cmd + 3, m_cursor.
c + m_tile_col + tile_width - 1);
239 write_be_u16(cmd + 6, m_cursor.
r + m_tile_row);
240 write_be_u16(cmd + 8, m_cursor.
r + m_tile_row + tile_height - 1);
244 for (u16 r = 0 ; r < tile_height ; ++r) {
245 for (u16 c = 0 ; c < tile_width ; ++c) {
246 u32 color = m_draw_cmd.
rc(m_tile_row + r, m_tile_col + c)
247 ? m_cursor.
fg : m_cursor.
bg;
248 write_be_u16(cmd + wrpos, u16(color));
254 bool ok = spi_cmd(5, cmd+0, 0)
255 && spi_cmd(5, cmd+5, 0)
256 && spi_cmd(wrpos-10, cmd+10,
this);
261 m_tile_col += tile_width;
262 if (m_tile_col >= m_draw_cmd.
width()) {
264 bool vp_old = in_viewport(m_cursor.
r + m_tile_row);
266 m_tile_row += tile_height;
267 bool vp_new = in_viewport(m_cursor.
r + m_tile_row);
269 if (vp_old && !vp_new) m_cursor.
r -= m_viewsize;
276 void Ili9341::init_next() {
277 if (m_init_step <
sizeof(STARTUP)) {
279 u8 len = STARTUP[m_init_step++];
282 u8 wait = STARTUP[m_init_step++];
284 }
else if (spi_cmd(len, STARTUP + m_init_step,
this)) {
294 if (spi_madctl() && spi_vscrdef()) {
296 m_init_step = INIT_DONE;
304 bool Ili9341::spi_cmd(u8 len,
const u8* cmd,
Ili9341* callback) {
305 return m_spi->
query(m_devidx, cmd, len, 0, callback);
308 bool Ili9341::spi_madctl() {
310 u8 cmd[2] = {CMD_MADCTL, m_madctl};
311 return spi_cmd(
sizeof(cmd), cmd, 0);
314 bool Ili9341::spi_vscrdef() {
316 u8 cmd[7] = {CMD_VSCRDEF};
319 write_be_u16(cmd + 5, TFT_HEIGHT - m_viewtop - m_viewsize);
320 return spi_cmd(
sizeof(cmd), cmd,
this);
323 void Ili9341::try_twidth(u16 w) {
325 u16 h = BURST_PIXELS / w;
327 u16 r = div_ceil<u16>(m_draw_cmd.
height(), h);
328 u16 c = div_ceil<u16>(m_draw_cmd.
width(), w);
330 if (r * c < m_draw_done) {
Polymorphic API for a generic SPI interface.
virtual bool busy()=0
Is the SPI controller currently busy?
virtual bool query(u8 devidx, const u8 *wrdata, u8 wrbytes, u8 rdbytes, satcat5::cfg::SpiEventListener *callback=0)=0
Queue a query transaction (write, read, or write-then-read).
Display device driver for the ILI9341.
bool draw(const satcat5::gui::Cursor &cursor, const satcat5::gui::DrawCmd &cmd) override
Draw pixels to the screen at the designated location.
static constexpr u8 MADCTL_MV
Constants for the MADCTL register (Section 8.2.29).
bool viewport(u16 top, u16 size)
Configure the scrolling viewport:
void timer_event() override
Child class MUST override this method.
bool busy() const
Busy with initialization or a previous command?
void reset()
Initiate software reset.
bool scroll(s16 rows) override
Optional: Advance the predefined viewport by N pixels.
bool invert(bool inv)
Invert entire display.
Required API for display devices.
Each "draw command" updates a rectangular region of pixels.
u16 width() const
Size of the rectangular update region.
u16 height() const
Size of the rectangular update region.
bool rc(u16 r, u16 c) const
Get the new pixel value at the designated row and column.
void timer_once(unsigned msec)
Configure a one-time notification after X milliseconds.
Cursor object tracks position and foreground/background colors.
u32 fg
Foreground color (format defined by display)
u16 r
Row coordinate (0 = top)
u32 bg
Background color (format defined by display)
u16 c
Column coordinate (0 = left)
Color parameters for the LogToDisplay class.
Miscellaneous mathematical utility functions.
void write_be_u16(u8 *dst, u16 val)
Store fields into a big-endian byte array.
constexpr u16 min_u16(u16 a, u16 b)
Min and max functions.
constexpr T div_ceil(T a, T b)
Integer division functions with various rounding options: