/* * Copyright (C) 2019 Max Regan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "DisplayDriver.h" #include "macros.h" #include "font.h" namespace BSP { using Common::Schedule::NextTime; using Common::ReturnCode; DisplayDriver::DisplayDriver(Common::Schedule::TaskScheduler &scheduler, SpiDriver &spi) : m_scheduler(scheduler) , m_spi(spi) , m_is_dirty(true) , m_dirty_line_min(0) , m_dirty_line_max(0) { buffer_init(); } ReturnCode DisplayDriver::init() { /** Enable Port A,B clock */ SET(RCC->IOPENR, RCC_IOPENR_IOPBEN); /** Enable pin P3 for output */ SET_TO(GPIOB->MODER, GPIO_MODER_MODE3, GPIO_MODER_MODE3_0); CLR(GPIOB->OTYPER, GPIO_OTYPER_OT_3); CLR(GPIOB->PUPDR, GPIO_PUPDR_PUPD3); return Common::ReturnCode::OK; return ReturnCode::OK; } NextTime DisplayDriver::execute() { return NextTime::never(); } void DisplayDriver::buffer_init() { for (size_t i = 0; i < ARRAY_SIZE(m_buffer.lines); i++) { struct display_line *line = &m_buffer.lines[i]; line->mode = 1; // Update display line->line = i; for (size_t j = 0; j < ARRAY_SIZE(line->data); j++) { line->data[j] = 0xFF; } } m_buffer.dummy = 0; } void DisplayDriver::set_dirty(unsigned int y) { if (!m_is_dirty) { m_is_dirty = true; m_dirty_line_min = y; m_dirty_line_max = y; } else { m_dirty_line_min = MIN(y, m_dirty_line_min); m_dirty_line_max = MAX(y, m_dirty_line_max); } } void DisplayDriver::set_bit(unsigned int x, unsigned int y, uint8_t val) { if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) { return; } struct display_line *line = &m_buffer.lines[y]; uint8_t *byte = &line->data[x >> 3]; if (val) { CLR_POS(*byte, x & 7); } else { SET_POS(*byte, x & 7); } set_dirty(y); } void DisplayDriver::set_byte(unsigned int x, unsigned int y, uint8_t val) { if (x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) { return; } if (x & 7) { return; } struct display_line *line = &m_buffer.lines[y]; line->data[x >> 3] = val; set_dirty(y); } void DisplayDriver::char_at(int *x_off, int y_off, char c, const struct font *font) { const struct glyph *g = glyph_for_char(font, c); if (g == NULL) { return; } // TODO: Don't hardcode this int byte_cols = (g->cols / 8); if (g->cols & 7) { byte_cols++; } if (byte_cols & 1) { byte_cols++; } for (size_t x = 0; x < g->cols; x++) { for (size_t y = 0; y < g->rows; y++) { int byte_x = x >> 3; int byte_y = y; uint8_t bit = (g->bitmap[byte_y * byte_cols + byte_x] >> (7 - (x & 7))) & 1; /* 16 is font max height */ set_bit(g->left + *x_off + x, y_off + y + 16 - g->top, bit); } } *x_off += g->advance; } void DisplayDriver::string_at(int x_off, int y_off, const char *string, const struct font *font) { int i = 0; while (string[i]) { char_at(&x_off, y_off, string[i], font); i++; } } void DisplayDriver::refresh() { if (!m_is_dirty) { return; } uint8_t *start = (uint8_t *) &m_buffer.lines[m_dirty_line_min]; // Data size size_t size = sizeof(m_buffer.lines[0]) * (m_dirty_line_max - m_dirty_line_min + 1); // Trailer dummy data size += 2; m_spi.tx_blocking(start, size); m_is_dirty = false; } void DisplayDriver::clear() { buffer_init(); m_is_dirty = true; m_dirty_line_min = 0; m_dirty_line_max = DISPLAY_HEIGHT - 1; } //TODO: put me somewhere fonty const struct glyph *DisplayDriver::glyph_for_char(const struct font *font, char c) { // TODO: This is almost the least efficient way imaginable to implement this for (int i = 0; i < font->max; i++) { const struct glyph *g = font->glyphs[i]; if (g == NULL) { continue; } if (g->glyph == c) { return g; } } return NULL; } }