From a13312d9a925bced156062e2aacaf83cdc034934 Mon Sep 17 00:00:00 2001 From: Markus Hitter Date: Tue, 26 Apr 2016 18:52:14 +0200 Subject: [PATCH] Display: introduce display queue. 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 --- clock.c | 14 ++++++++++---- display.c | 37 ++++++++++++++++++++++++++++++++++++- display.h | 4 ++++ display_ssd1306.c | 47 +++++++++++++++++++++++++---------------------- i2c.h | 12 ++++++++---- 5 files changed, 83 insertions(+), 31 deletions(-) diff --git a/clock.c b/clock.c index f0ca122..ef0ef1b 100644 --- a/clock.c +++ b/clock.c @@ -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 } - - diff --git a/display.c b/display.c index 0cca508..b98b271 100644 --- a/display.c +++ b/display.c @@ -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; diff --git a/display.h b/display.h index 0680b92..25b5951 100644 --- a/display.h +++ b/display.h @@ -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); diff --git a/display_ssd1306.c b/display_ssd1306.c index 3472ce3..3b4f62d 100644 --- a/display_ssd1306.c +++ b/display_ssd1306.c @@ -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; +void display_tick() { + uint8_t i, data, index; - // 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); + 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)); + } } } diff --git a/i2c.h b/i2c.h index 8bb6960..f363cd1 100644 --- a/i2c.h +++ b/i2c.h @@ -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