ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_eink.c
Go to the documentation of this file.
1
29
30#include "board_periph_eink.h"
31
32#include <stdint.h>
33#include <stdio.h>
34
35#include "board_periph.h"
37
42typedef enum : uint16_t {
43 k_eink_pre_cmd = 0x6000U,
44 k_eink_pre_wr = 0x0000U,
45 k_eink_pre_rd = 0x1000U,
47
52typedef enum : uint16_t {
54 k_eink_cmd_sleep = 0x0003U,
60 k_eink_cmd_vcom = 0x0039U,
63
68typedef enum : uint16_t {
69 k_eink_vcom_get = 0x0000U,
70 k_eink_vcom_set = 0x0001U,
72
86typedef enum : uint16_t {
89
94typedef enum : uint16_t {
97
106typedef enum : uint16_t {
110
125
142
153
168
176typedef enum : uint8_t {
180
185typedef enum : uint8_t {
190
195typedef struct {
196 bool attached;
198 bool have_hi;
199 uint8_t hi;
200 uint16_t cur_cmd;
201 uint16_t datawr_idx;
202 uint16_t reg_addr;
203 uint16_t dev_idx;
204 uint16_t vcom_mv;
205 uint16_t vcom_dir;
206 uint16_t px_per_word;
207 bool reading;
208 uint8_t rd_pos;
209 uint8_t rd_len;
211 uint64_t pixels;
212 uint32_t refreshes;
213 uint16_t last_wf;
215
218
233RA8_INTERNAL static uint16_t internal_eink_reg_value(uint16_t reg)
234{
235 if (reg == (uint16_t)k_eink_reg_lutafsr) {
236 return 0U; /* LUT idle -> ra8_epaper_display_area poll exits. */
237 }
238 return 0U;
239}
240
252RA8_INTERNAL static uint16_t internal_eink_dev_info_word(uint16_t idx)
253{
254 if (idx == 0U) {
255 return (uint16_t)k_eink_panel_w;
256 }
257 if (idx == 1U) {
258 return (uint16_t)k_eink_panel_h;
259 }
260 return 0U;
261}
262
274RA8_INTERNAL static uint16_t internal_eink_px_per_word(uint16_t wire_pf)
275{
276 if (wire_pf == (uint16_t)k_eink_wire_pf_2bpp) {
277 return (uint16_t)((uint16_t)k_eink_word_bits / 2U);
278 }
279 if (wire_pf == (uint16_t)k_eink_wire_pf_3bpp) {
280 return (uint16_t)((uint16_t)k_eink_word_bits / 3U);
281 }
282 if (wire_pf == (uint16_t)k_eink_wire_pf_4bpp) {
283 return (uint16_t)((uint16_t)k_eink_word_bits / 4U);
284 }
285 return (uint16_t)((uint16_t)k_eink_word_bits / 8U); /* k_eink_wire_pf_8bpp */
286}
287
297{
298 uint16_t value = 0U;
299 if (s_eink.cur_cmd == (uint16_t)k_eink_cmd_reg_rd) {
300 value = internal_eink_reg_value(s_eink.reg_addr);
301 } else if (s_eink.cur_cmd == (uint16_t)k_eink_cmd_vcom) {
302 /* Only a "get" reads back; the driver never reads after a set. */
303 value = s_eink.vcom_mv;
304 } else if (s_eink.cur_cmd == (uint16_t)k_eink_cmd_get_dev_info) {
305 value = internal_eink_dev_info_word(s_eink.dev_idx);
306 s_eink.dev_idx = (uint16_t)(s_eink.dev_idx + 1U);
307 }
308 s_eink.rd_buf[k_eink_rd_dummy_hi] = (uint8_t)k_eink_idle_byte;
309 s_eink.rd_buf[k_eink_rd_dummy_lo] = (uint8_t)k_eink_idle_byte;
310 s_eink.rd_buf[k_eink_rd_val_hi] =
311 (uint8_t)((value >> (uint16_t)k_eink_byte_shift) & (uint16_t)k_eink_byte_mask);
312 s_eink.rd_buf[k_eink_rd_val_lo] = (uint8_t)(value & (uint16_t)k_eink_byte_mask);
313 s_eink.rd_len = (uint8_t)k_eink_read_bytes;
314 s_eink.rd_pos = 0U;
315 s_eink.reading = true;
316}
317
327RA8_INTERNAL static void internal_eink_consume_data(uint16_t word)
328{
329 if ((s_eink.cur_cmd == (uint16_t)k_eink_cmd_reg_rd) ||
330 (s_eink.cur_cmd == (uint16_t)k_eink_cmd_reg_wr)) {
331 if (s_eink.datawr_idx == (uint16_t)k_eink_idx_reg_addr) {
332 s_eink.reg_addr = word;
333 }
334 } else if (s_eink.cur_cmd == (uint16_t)k_eink_cmd_vcom) {
335 if (s_eink.datawr_idx == (uint16_t)k_eink_idx_vcom_dir) {
336 s_eink.vcom_dir = word;
337 } else if ((s_eink.datawr_idx == (uint16_t)k_eink_idx_vcom_val) &&
338 (s_eink.vcom_dir == (uint16_t)k_eink_vcom_set)) {
339 s_eink.vcom_mv = word;
340 } else {
341 /* A get takes no value word; nothing further to consume. */
342 }
343 } else if (s_eink.cur_cmd == (uint16_t)k_eink_cmd_ld_img_area) {
344 if (s_eink.datawr_idx == (uint16_t)k_eink_idx_ld_arg0) {
345 const uint16_t wire_pf =
346 (uint16_t)((word >> (uint16_t)k_eink_pf_shift) & (uint16_t)k_eink_pf_mask);
347 s_eink.px_per_word = internal_eink_px_per_word(wire_pf);
348 } else if (s_eink.datawr_idx >= (uint16_t)k_eink_idx_ld_px_start) {
349 s_eink.pixels += (uint64_t)s_eink.px_per_word;
350 } else {
351 /* Geometry args: no pixel accounting. */
352 }
353 } else if (s_eink.cur_cmd == (uint16_t)k_eink_cmd_dpy_area) {
354 if (s_eink.datawr_idx == (uint16_t)k_eink_idx_dpy_wf) {
355 s_eink.last_wf = word;
356 s_eink.refreshes = (uint32_t)(s_eink.refreshes + 1U);
357 }
358 }
359}
360
370RA8_INTERNAL static void internal_eink_consume_word(uint16_t word)
371{
372 if (s_eink.st == (eink_state_t)k_eink_st_cmd) {
373 s_eink.cur_cmd = word;
374 s_eink.datawr_idx = 0U;
375 if (word == (uint16_t)k_eink_cmd_get_dev_info) {
376 s_eink.dev_idx = 0U;
377 }
379 return;
380 }
383 s_eink.datawr_idx = (uint16_t)(s_eink.datawr_idx + 1U);
385 return;
386 }
387 /* k_eink_st_preamble: classify the preamble word. */
388 if (word == (uint16_t)k_eink_pre_cmd) {
390 } else if (word == (uint16_t)k_eink_pre_wr) {
392 } else if (word == (uint16_t)k_eink_pre_rd) {
393 internal_eink_begin_read(); /* subsequent dummy bytes are answered from rd_buf */
394 }
395}
396
398{
399 s_eink.attached = true;
401 return true;
402}
403
405{
406 return s_eink.attached;
407}
408
409uint8_t board_eink_exchange(uint8_t tx)
410{
411 if (!s_eink.attached) {
412 return (uint8_t)k_eink_idle_byte;
413 }
414 if (s_eink.reading) {
415 const uint8_t r =
416 (s_eink.rd_pos < s_eink.rd_len) ? s_eink.rd_buf[s_eink.rd_pos] : (uint8_t)k_eink_idle_byte;
417 s_eink.rd_pos = (uint8_t)(s_eink.rd_pos + 1U);
418 if (s_eink.rd_pos >= s_eink.rd_len) {
419 s_eink.reading = false;
420 }
421 return r;
422 }
423 if (!s_eink.have_hi) {
424 s_eink.hi = tx;
425 s_eink.have_hi = true;
426 return (uint8_t)k_eink_idle_byte;
427 }
428 const uint16_t word =
429 (uint16_t)(((uint16_t)s_eink.hi << (uint16_t)k_eink_byte_shift) | (uint16_t)tx);
430 s_eink.have_hi = false;
432 return (uint8_t)k_eink_idle_byte;
433}
434
436{
437 const bool was_attached = s_eink.attached;
438 const uint64_t pixels = s_eink.pixels;
439 const uint32_t refreshes = s_eink.refreshes;
440 const uint16_t vcom_mv = s_eink.vcom_mv;
441 s_eink = (eink_model_t){};
442 s_eink.attached = was_attached;
443 s_eink.pixels = pixels; /* keep run totals across a mid-run reset */
444 s_eink.refreshes = refreshes; /* (the SPI block resets framing, not the panel) */
446 /* VCOM is controller configuration, not bus framing: a mid-run SPI reset
447 * must not clear it, but a cold attach starts from the power-on value. */
448 s_eink.vcom_mv = (vcom_mv != 0U) ? vcom_mv : (uint16_t)k_eink_vcom_power_on_mv;
450}
451
453{
454 if (!s_eink.attached) {
455 return;
456 }
458}
459
461{
462 if (!s_eink.attached) {
463 return;
464 }
465 (void)priv_emu_io_errf(" IT8951 e-ink : %llu pixel(s) loaded, %u refresh(es), last wf=0x%X, "
466 "vcom=%umV\n",
467 (unsigned long long)s_eink.pixels,
468 s_eink.refreshes,
469 (unsigned)s_eink.last_wf,
470 (unsigned)s_eink.vcom_mv);
471}
Register-accurate peripheral-model framework for the board emulator.
void board_periph_gpio_set_input(uint8_t port, uint8_t pin, bool level)
Drive a GPIO pin's input level from outside the firmware.
eink_state_t
Host-word interpretation state.
@ k_eink_st_preamble
Next assembled word is a preamble.
@ k_eink_st_datawr
Next assembled word is data.
@ k_eink_st_cmd
Next assembled word is a command.
uint8_t board_eink_exchange(uint8_t tx)
Exchange one full-duplex SPI byte with the modelled controller.
static RA8_INTERNAL uint16_t internal_eink_reg_value(uint16_t reg)
Read-back value for a controller register.
eink_datawr_idx_t
Data-word positions within a command's argument stream.
@ k_eink_idx_ld_arg0
LD_IMG_AREA: endian/pf/rotate word.
@ k_eink_idx_vcom_dir
VCOM: direction word (get / set).
@ k_eink_idx_reg_addr
REG_RD/REG_WR: register address word.
@ k_eink_idx_vcom_val
VCOM: value word on a set.
@ k_eink_idx_ld_px_start
LD_IMG_AREA: first pixel word.
@ k_eink_idx_dpy_wf
DPY_AREA: waveform word (arg4).
@ k_eink_idx_ld_w
LD_IMG_AREA: width (arg3 after arg0).
@ k_eink_idx_ld_h
LD_IMG_AREA: height (arg4 after arg0).
static RA8_INTERNAL void internal_eink_begin_read(void)
Load rd_buf with [dummy word, value word] and open the read burst.
bool board_eink_attach(void)
Attach (arm) the modelled IT8951 e-paper controller.
void board_eink_reset(void)
Reset the controller's command / read framing to power-on.
static RA8_INTERNAL uint16_t internal_eink_dev_info_word(uint16_t idx)
Word i of the (discarded) GET_DEV_INFO block: W, H, then zeros.
static eink_model_t s_eink
The single modelled controller.
eink_preamble_t
SPI preamble words (IT8951 DS chapter 3.4 table 3-3).
@ k_eink_pre_cmd
Host -> command write.
@ k_eink_pre_wr
Host -> data write.
@ k_eink_pre_rd
Host <- data read.
eink_cmd_t
IT8951 user commands the ra8_epaper driver issues (DS chapter 4.2).
@ k_eink_cmd_vcom
Get / set VCOM bias.
@ k_eink_cmd_reg_rd
Register read.
@ k_eink_cmd_ld_img_end
End load.
@ k_eink_cmd_dpy_area
Refresh rectangle.
@ k_eink_cmd_reg_wr
Register write.
@ k_eink_cmd_sys_run
Wake from standby.
@ k_eink_cmd_sleep
Enter deep-sleep.
@ k_eink_cmd_ld_img_area
Begin load (rectangle).
@ k_eink_cmd_get_dev_info
40-byte info block.
eink_sizing_t
Byte-assembly + read-burst sizing constants (no magic numbers).
@ k_eink_dev_words
GET_DEV_INFO block length (40 / 2).
@ k_eink_byte_mask
Low-byte extraction mask.
@ k_eink_read_bytes
Bytes per read burst (dummy + value).
@ k_eink_idle_byte
Non-read response / idle byte.
@ k_eink_pf_mask
LD_IMG_AREA arg0 pixel-format mask.
@ k_eink_word_bits
Bits carried by one 16-bit word.
@ k_eink_pf_shift
LD_IMG_AREA arg0 pixel-format shift.
@ k_eink_byte_shift
Bits per byte.
eink_hrdy_pin_t
Panel HRDY "ready" GPIO on the modelled carrier.
@ k_eink_hrdy_port
HRDY on PORT4.
@ k_eink_hrdy_pin
HRDY on pin 1 (P4_01).
void board_eink_apply_gpio_defaults(void)
Re-arm the panel HRDY "ready" GPIO input high (if attached).
void board_eink_report(void)
Print the controller's end-of-run summary line (if attached).
eink_vcom_arg_t
Direction word of the VCOM command (DS user command set).
@ k_eink_vcom_set
Controller consumes the next word.
@ k_eink_vcom_get
Controller answers with its VCOM.
static RA8_INTERNAL void internal_eink_consume_word(uint16_t word)
Advance the state machine by one fully-assembled 16-bit word.
eink_vcom_default_t
VCOM the modelled controller powers up holding.
@ k_eink_vcom_power_on_mv
Reported VCOM magnitude (mV).
bool board_eink_attached(void)
Report whether the IT8951 controller is currently attached.
eink_wire_pf_t
LD_IMG_AREA arg0 pixel-format codes.
@ k_eink_wire_pf_2bpp
2 bits per pixel.
@ k_eink_wire_pf_8bpp
8 bits per pixel.
@ k_eink_wire_pf_3bpp
3 bits per pixel.
@ k_eink_wire_pf_4bpp
4 bits per pixel.
static RA8_INTERNAL uint16_t internal_eink_px_per_word(uint16_t wire_pf)
Pixels a 16-bit data word carries at the given wire pixel format.
static RA8_INTERNAL void internal_eink_consume_data(uint16_t word)
Interpret one data word against the command currently in flight.
eink_geom_t
Fixed panel geometry the model reports through GET_DEV_INFO.
@ k_eink_panel_h
Reported panel height (priv_px).
@ k_eink_panel_w
Reported panel width (priv_px).
eink_read_idx_t
Byte positions in the 4-byte (2-word) read-burst response buffer.
@ k_eink_rd_dummy_hi
High byte of the dummy word.
@ k_eink_rd_dummy_lo
Low byte of the dummy word.
@ k_eink_rd_val_lo
Low byte of the value word.
@ k_eink_rd_val_hi
High byte of the value word.
eink_reg_t
Controller registers whose read value the model must answer.
@ k_eink_reg_lutafsr
LUT busy status: 0 = idle (DS 4.2.4).
IT8951 e-paper SPI-device model for ra8_emulator (attached to SPI_B).
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
The modelled IT8951 controller's whole state.
bool have_hi
A high byte is latched in hi.
uint16_t vcom_dir
Direction word of the VCOM in flight.
uint16_t last_wf
Waveform of the last DPY_AREA.
uint32_t refreshes
DPY_AREA refreshes issued.
uint16_t reg_addr
Register address for a REG_RD/REG_WR.
uint8_t rd_len
Valid bytes in rd_buf.
uint8_t hi
Latched high byte of the pending word.
uint16_t dev_idx
Next GET_DEV_INFO word index.
uint16_t vcom_mv
Modelled VCOM magnitude in millivolts.
bool attached
–eink armed the controller.
uint16_t px_per_word
Pixels per data word at the loaded pf.
uint64_t pixels
Pixels streamed via LD_IMG_AREA.
uint8_t rd_pos
Cursor into rd_buf.
uint16_t cur_cmd
Command in flight (0 if none).
eink_state_t st
Host-word interpretation state.
uint8_t rd_buf[k_eink_read_bytes]
Read-burst response.
uint16_t datawr_idx
Data-word index within cur_cmd.
bool reading
A read burst is serving response bytes.