Skip to content
This repository has been archived by the owner on Aug 27, 2023. It is now read-only.

Commit

Permalink
Display: introduce display queue.
Browse files Browse the repository at this point in the history
Now we shouldn't experience wait cycles in i2c_write() during
typical display writes any longer. It should also distribute CPU
load of display writes a lot better.

Previously writing a line of text to the display would take
almost as long as it took to actually send it to the display,
because the I2C queue could hold only one transmission, which
effectively meant only one character. This could hold the main
loop for several milliseconds.

Now we queue characters, send them one by one, and return to the
main loop in between.

This costs 160 bytes program memory. Only 18 bytes RAM, because
the I2C queue was reduced accordingly. Now:

  Program:  24456 bytes
     Data:   1543 bytes
   EEPROM:     32 bytes
  • Loading branch information
Traumflug committed Apr 27, 2016
1 parent aabce8e commit a13312d
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 31 deletions.
14 changes: 10 additions & 4 deletions clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,23 @@ static void clock_10ms(void) {
}
}

/*! do reoccuring stuff
/**
Do reoccuring stuff. Call it occasionally in busy loops.
call it occasionally in busy loops
Other than clock_tick() above, which is called at a constant interval, this
is called from the main loop. So it can be called very often on an idle
printer, but rather rarely on one running full speed.
*/
void clock() {
ifclock(clock_flag_10ms) {
clock_10ms();
}

#ifdef DISPLAY
display_tick();
#endif

#ifdef SIMULATOR
sim_time_warp();
#endif
}


37 changes: 36 additions & 1 deletion display.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,47 @@

#include "display.h"

#ifdef DISPLAY

// Ringbuffer logic for buffer 'display'.
#define BUFSIZE DISPLAY_BUFFER_SIZE

volatile uint8_t displayhead = 0;
volatile uint8_t displaytail = 0;
volatile uint8_t displaybuf[BUFSIZE];

#include "ringbuffer.h"


#define TEACUP_C_INCLUDE
#include "display_ssd1306.c"
#undef TEACUP_C_INCLUDE


#ifdef DISPLAY
#include "delay.h"

/**
Prints a character at the current cursor position.
\param data The character to be displayed.
This code is identical for all display buses and display types, because it
just queues up the character.
In case the buffer is full already it waits for a millisecond to allow
data to be sent to the display, then it tries again. If it still fails then,
it drops the character. This way we're fairly protected against data loss,
still we guarantee to not hang forever.
*/
void display_writechar(uint8_t data) {

if ( ! buf_canwrite(display)) {
delay_ms(1);
}
if (buf_canwrite(display)) {
buf_push(display, data);
}
}

void display_writestr_P(PGM_P data_P) {
uint8_t r, i = 0;
Expand Down
4 changes: 4 additions & 0 deletions display.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@
#endif /* DISPLAY_BUS */


#define DISPLAY_BUFFER_SIZE 128


void display_init(void);
void display_tick(void);
void display_clear(void);

void display_clock(void);
Expand Down
47 changes: 25 additions & 22 deletions display_ssd1306.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,31 +116,34 @@ void display_clock(void) {
}

/**
Prints a character at the current cursor position.
\param data The character to be displayed.
Forwards a character from the display queue to the I2C queue.
*/
void display_writechar(uint8_t data) {
uint8_t i, index = data - 0x20;

// Write pixels command.
displaybus_write(0x40, 0);
void display_tick() {
uint8_t i, data, index;

// Send the character bitmap.
#ifdef FONT_IS_PROPORTIONAL
for (i = 0; i < pgm_read_byte(&font[index].columns); i++) {
#else
for (i = 0; i < FONT_COLUMNS; i++) {
#endif
displaybus_write(pgm_read_byte(&font[index].data[i]), 0);
if (displaybus_busy()) {
return;
}
// Send space between characters.
for (i = 0; i < FONT_SYMBOL_SPACE; i++) {
// TODO: we finalise a I2C (or other) bus message after each character
// here because we have no idea on how many more are following. This
// is highly inefficient and makes the displaybus buffer almost
// pointless.
displaybus_write(0x00, (i == FONT_SYMBOL_SPACE - 1));

if (buf_canread(display)) {
buf_pop(display, data);
index = data - 0x20;

// Write pixels command.
displaybus_write(0x40, 0);

// Send the character bitmap.
#ifdef FONT_IS_PROPORTIONAL
for (i = 0; i < pgm_read_byte(&font[index].columns); i++) {
#else
for (i = 0; i < FONT_COLUMNS; i++) {
#endif
displaybus_write(pgm_read_byte(&font[index].data[i]), 0);
}
// Send space between characters.
for (i = 0; i < FONT_SYMBOL_SPACE; i++) {
displaybus_write(0x00, (i == FONT_SYMBOL_SPACE - 1));
}
}
}

Expand Down
12 changes: 8 additions & 4 deletions i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,15 @@
Size of send buffer. MUST be a \f$2^n\f$ value, maximum is 512.
A larger buffer allows to store more display data immediately, so it can
speed operations up. An exhaused buffer doesn't mean data gets lost, writing
to the buffer then waits until sufficient previous data is sent.
This buffer can be rather small, because there is another queue on the
display level. Transmissions can be large, e.g. 514 bytes when clearing the
display, but typically it's only some 3 to 10 bytes ( = sending one
character).
An exhausted buffer doesn't mean data loss, writing to the buffer then waits
until sufficient previous data is sent.
*/
#define I2C_BUFFER_SIZE 128
#define I2C_BUFFER_SIZE 16

#ifdef I2C_SLAVE_MODE
#define I2C_SLAVE_RX_BUFFER_SIZE 1
Expand Down

0 comments on commit a13312d

Please sign in to comment.