Firmware SDK
twr_ls013b7dh03.c
1 #include <twr_ls013b7dh03.h>
2 #include <twr_spi.h>
3 
4 #define _TWR_LS013B7DH03_VCOM_PERIOD 15000
5 
6 #define _TWR_LS013B7DH03_LINE_INCREMENT (TWR_LS013B7DH03_WIDTH / 8 + 2)
7 
8 static void _twr_ls013b7dh03_task(void *param);
9 static bool _twr_ls013b7dh03_spi_transfer(twr_ls013b7dh03_t *self, uint8_t *buffer, size_t length);
10 static void _twr_ls013b7dh03_spi_event_handler(twr_spi_event_t event, void *event_param);
11 static inline uint8_t _twr_ls013b7dh03_reverse(uint8_t b);
12 
13 void twr_ls013b7dh03_init(twr_ls013b7dh03_t *self, bool (*pin_cs_set)(bool state))
14 {
15  memset(self, 0xff, sizeof(*self));
16 
17  self->_vcom = 0;
18  self->_pin_cs_set = pin_cs_set;
19 
21 
22  // Address lines
23  uint8_t line;
24  uint32_t offs;
25  for (line = 0x01, offs = 1; line <= TWR_LS013B7DH03_HEIGHT; line++, offs += _TWR_LS013B7DH03_LINE_INCREMENT) // 128; 18
26  {
27  // Fill the gate line addresses on the exact place in the buffer
28  self->_framebuffer[offs] = _twr_ls013b7dh03_reverse(line);
29  }
30 
31  self->_pin_cs_set(1);
32 
33  self->_task_id = twr_scheduler_register(_twr_ls013b7dh03_task, self, _TWR_LS013B7DH03_VCOM_PERIOD);
34 }
35 
37 {
38  (void) self;
39 
40  static const twr_gfx_caps_t caps = { .width = TWR_LS013B7DH03_WIDTH, .height = TWR_LS013B7DH03_HEIGHT };
41 
42  return caps;
43 }
44 
46 {
47  (void) self;
48 
49  return twr_spi_is_ready();
50 }
51 
53 {
54  uint8_t line;
55  uint32_t offs;
56  uint8_t col;
57  for (line = 0x01, offs = 2; line <= TWR_LS013B7DH03_HEIGHT; line++, offs += _TWR_LS013B7DH03_LINE_INCREMENT)
58  {
59  for (col = 0; col < (TWR_LS013B7DH03_WIDTH / 8); col++)
60  {
61  self->_framebuffer[offs + col] = 0xff;
62  }
63  }
64 }
65 
66 void twr_ls013b7dh03_draw_pixel(twr_ls013b7dh03_t *self, int x, int y, uint32_t color)
67 {
68  // Skip mode byte + addr byte
69  uint32_t byteIndex = 2;
70  // Skip lines
71  byteIndex += y * _TWR_LS013B7DH03_LINE_INCREMENT;
72  // Select column byte
73  byteIndex += x / 8;
74 
75  uint8_t bitMask = 1 << (7 - (x % 8));
76 
77  if (color == 0)
78  {
79  self->_framebuffer[byteIndex] |= bitMask;
80  }
81  else
82  {
83  self->_framebuffer[byteIndex] &= ~bitMask;
84  }
85 }
86 
87 uint32_t twr_ls013b7dh03_get_pixel(twr_ls013b7dh03_t *self, int x, int y)
88 {
89  // Skip mode byte + addr byte
90  uint32_t byteIndex = 2;
91  // Skip lines
92  byteIndex += y * _TWR_LS013B7DH03_LINE_INCREMENT;
93  // Select column byte
94  byteIndex += x / 8;
95 
96  return (self->_framebuffer[byteIndex] >> (7 - (x % 8))) & 1 ? 0 : 1;
97 }
98 
99 /*
100 
101 Framebuffer format for updating multiple lines, ideal for later DMA TX:
102 
103 || Set MODE ||------18B for line---||--next 18B 2nd line--| ...
104 || 1B || 1B | 16B | 1B || 1B | 16B | 1B |
105 || M0 M1 M2 DUMMY || ADDR | DATA | DUMMY || ADDR | DATA | DUMMY |
106 
107 */
109 {
110  if (twr_spi_is_ready())
111  {
112  if (!self->_pin_cs_set(0))
113  {
114  return false;
115  }
116 
117  self->_framebuffer[0] = 0x80 | self->_vcom;
118 
119  if (!twr_spi_async_transfer(self->_framebuffer, NULL, TWR_LS013B7DH03_FRAMEBUFFER_SIZE, _twr_ls013b7dh03_spi_event_handler, self))
120  {
121  self->_pin_cs_set(1);
122 
123  return false;
124  }
125 
126  twr_scheduler_plan_relative(self->_task_id, _TWR_LS013B7DH03_VCOM_PERIOD);
127 
128  self->_vcom ^= 0x40;
129 
130  return true;
131  }
132 
133  return false;
134 }
135 
137 {
138  static const twr_gfx_driver_t driver =
139  {
140  .is_ready = (bool (*)(void *)) twr_ls013b7dh03_is_ready,
141  .clear = (void (*)(void *)) twr_ls013b7dh03_clear,
142  .draw_pixel = (void (*)(void *, int, int, uint32_t)) twr_ls013b7dh03_draw_pixel,
143  .get_pixel = (uint32_t (*)(void *, int, int)) twr_ls013b7dh03_get_pixel,
144  .update = (bool (*)(void *)) twr_ls013b7dh03_update,
145  .get_caps = (twr_gfx_caps_t (*)(void *)) twr_ls013b7dh03_get_caps
146  };
147 
148  return &driver;
149 }
150 
152 {
153  uint8_t spi_data[2] = { 0x20, 0x00 };
154 
155  return _twr_ls013b7dh03_spi_transfer(self, spi_data, sizeof(spi_data));
156 }
157 
158 static void _twr_ls013b7dh03_task(void *param)
159 {
160  twr_ls013b7dh03_t *self = (twr_ls013b7dh03_t *) param;
161 
162  uint8_t spi_data[2] = {self->_vcom, 0x00};
163 
164  if (_twr_ls013b7dh03_spi_transfer(self, spi_data, sizeof(spi_data)))
165  {
166  self->_vcom ^= 0x40;
167  }
168 
169  twr_scheduler_plan_current_from_now(_TWR_LS013B7DH03_VCOM_PERIOD);
170 }
171 
172 static bool _twr_ls013b7dh03_spi_transfer(twr_ls013b7dh03_t *self, uint8_t *buffer, size_t length)
173 {
174  if (!twr_spi_is_ready())
175  {
176  return false;
177  }
178 
179  if (!self->_pin_cs_set(0))
180  {
181  return false;
182  }
183 
184  bool spi_state = twr_spi_transfer(buffer, NULL, length);
185 
186  self->_pin_cs_set(1);
187 
188  return spi_state;
189 }
190 
191 static void _twr_ls013b7dh03_spi_event_handler(twr_spi_event_t event, void *event_param)
192 {
193  twr_ls013b7dh03_t *self = (twr_ls013b7dh03_t *) event_param;
194 
195  if (event == TWR_SPI_EVENT_DONE)
196  {
197  self->_pin_cs_set(1);
198  }
199 }
200 
201 static inline uint8_t _twr_ls013b7dh03_reverse(uint8_t b)
202 {
203  b = (b & 0xf0) >> 4 | (b & 0x0f) << 4;
204  b = (b & 0xcc) >> 2 | (b & 0x33) << 2;
205  b = (b & 0xaa) >> 1 | (b & 0x55) << 1;
206 
207  return b;
208 }
bool twr_ls013b7dh03_is_ready(twr_ls013b7dh03_t *self)
Check if lcd is ready for commands.
twr_gfx_caps_t twr_ls013b7dh03_get_caps(twr_ls013b7dh03_t *self)
Get capabilities.
const twr_gfx_driver_t * twr_ls013b7dh03_get_driver(void)
Get Lcd driver.
bool twr_ls013b7dh03_clear_memory_command(twr_ls013b7dh03_t *self)
Send Lcd clear memory command.
uint32_t twr_ls013b7dh03_get_pixel(twr_ls013b7dh03_t *self, int x, int y)
Lcd get pixel.
void twr_ls013b7dh03_clear(twr_ls013b7dh03_t *self)
Clear.
bool twr_ls013b7dh03_update(twr_ls013b7dh03_t *self)
Lcd update, send data.
void twr_ls013b7dh03_draw_pixel(twr_ls013b7dh03_t *self, int x, int y, uint32_t color)
Lcd draw pixel.
void twr_ls013b7dh03_init(twr_ls013b7dh03_t *self, bool(*pin_cs_set)(bool state))
Initialize lcd driver.
void twr_scheduler_plan_current_from_now(twr_tick_t tick)
Schedule current task to tick relative from now.
void twr_scheduler_plan_relative(twr_scheduler_task_id_t task_id, twr_tick_t tick)
Schedule specified task to tick relative from current spin.
twr_scheduler_task_id_t twr_scheduler_register(void(*task)(void *), void *param, twr_tick_t tick)
Register task in scheduler.
Definition: twr_scheduler.c:53
bool twr_spi_is_ready(void)
Check if is ready for transfer.
Definition: twr_spi.c:187
bool twr_spi_async_transfer(const void *source, void *destination, size_t length, void(*event_handler)(twr_spi_event_t event, void *event_param), void(*event_param))
Execute async SPI transfer.
Definition: twr_spi.c:267
bool twr_spi_transfer(const void *source, void *destination, size_t length)
Execute SPI transfer.
Definition: twr_spi.c:192
void twr_spi_init(twr_spi_speed_t speed, twr_spi_mode_t mode)
Initialize SPI channel.
Definition: twr_spi.c:64
twr_spi_event_t
SPI event.
Definition: twr_spi.h:61
@ TWR_SPI_SPEED_1_MHZ
SPI communication speed is 1 MHz.
Definition: twr_spi.h:24
@ TWR_SPI_MODE_0
SPI mode of operation is 0 (CPOL = 0, CPHA = 0)
Definition: twr_spi.h:45
@ TWR_SPI_EVENT_DONE
SPI event is completed.
Definition: twr_spi.h:63
Display size.
Definition: twr_gfx.h:14
Display driver interface.
Definition: twr_gfx.h:23