static void lm70_chipselect(struct spi_device *spi, int value) { struct spi_lm70llp *pp = spidev_to_pp(spi); if (value) assertCS(pp); else deassertCS(pp); }
template<typename SourceType> BMP_Status OLED::_displayBMP(SourceType &source, const int from_x, const int from_y, const int to_x, const int to_y) { SourceType &f = source; f.seek(0); // File header, check magic number 'BM' if(f.read() != 'B' || f.read() != 'M') return BMP_INVALID_FORMAT; // Read DIB header with image properties f.seek(OFFS_DIB_HEADER); uint16_t dib_headersize = readLong(f); uint16_t width, height, bpp, compression; bool v2header = (dib_headersize == 12); // BMPv2 header, no compression, no additional options if(v2header) { width = readShort(f); height = readShort(f); if(readShort(f) != 1) return BMP_UNSUPPORTED_HEADER; bpp = readShort(f); compression = BMP_NoCompression; } else { width = readLong(f); height = readLong(f); if(readShort(f) != 1) return BMP_UNSUPPORTED_HEADER; bpp = readShort(f); compression = readLong(f); } // Verify image properties from header if(bpp > 24) return BMP_TOO_MANY_COLOURS; if(bpp != 1 && bpp != 4 && bpp != 8 && bpp != 16 && bpp != 24) return BMP_UNSUPPORTED_COLOURS; if(!(compression == BMP_NoCompression || (compression == BMP_BITFIELDS && bpp == 16))) { return BMP_COMPRESSION_NOT_SUPPORTED; } // In case of the bitfields option, determine the pixel format. We support RGB565 & RGB555 only bool rgb565 = true; if(compression == BMP_BITFIELDS) { f.seek(0x36); uint16_t b = readLong(f); uint16_t g = readLong(f); uint16_t r = readLong(f); if(r == 0x001f && g == 0x07e0 && b == 0xf800) rgb565 = true; else if(r != 0x001f && g != 0x03e0 && b != 0x7c00) return BMP_UNSUPPORTED_COLOURS; // Not RGB555 either } if (width < from_x || height < from_y) return BMP_ORIGIN_OUTSIDE_IMAGE; // source in BMP is offscreen // Find the starting offset for the data in the first row f.seek(0x0a); uint32_t data_offs = readLong(f); assertCS(); // Trim height to 128, anything bigger gets cut off, then set up row span in memory height = height - from_y; if(to_y + height > 128) height = 128-to_y; // Calculate outputtable width and set up column span in memory uint16_t out_width = width - from_x; if(to_x + out_width > 128) out_width = 128-to_x; // Calculate the width in bits of each row (rounded up to nearest byte) uint16_t row_bits = (width*bpp + 7) & ~7; // Calculate width in bytes (4-byte boundary aligned) uint16_t row_bytes = (row_bits/8 + 3) & ~3; startWrite(to_x,to_y,to_x+out_width-1,to_y+height-1,false); releaseCS(); // Read colour palette to RAM. It's quite hefty to hold in RAM (512 bytes for a full 8-bit palette) // but don't have much choice as seeking back and forth on SD is painfully slow OLED_Colour *palette; if(bpp < 16) { uint16_t palette_size = 1<<bpp; palette = (OLED_Colour *)malloc(sizeof(OLED_Colour)*palette_size); f.seek(OFFS_DIB_HEADER + dib_headersize); for(uint16_t i = 0; i < palette_size; i++) { uint8_t pal[4]; f.read(pal, v2header ? 3 : 4); palette[i].blue = pal[0] >> 3; palette[i].green = pal[1] >> 2; palette[i].red = pal[2] >> 3; } }
int OLED::drawChar(const int x, const int y, const char letter, const OLED_Colour colour, const OLED_Colour background) { if (x <0 || y < 0 || x >= COLUMNS || y >= ROWS) return -1; struct FontHeader header; memcpy_P(&header, (void*)this->font, sizeof(FontHeader)); uint8_t c = letter; if (c == ' ') { int charWide = charWidth(' '); this->drawFilledBox(x, y-1, x + charWide, y + header.height, background); return charWide; } uint8_t width = 0; uint8_t bytes = (header.height + 7) / 8; // Number of bytes in a single column uint16_t index = 0; if (c < header.firstChar || c >= (header.firstChar + header.charCount)) return 0; c -= header.firstChar; if (header.size == 0) { // zero length is flag indicating fixed width font (array does not contain width data entries) width = header.fixedWidth; index = sizeof(FontHeader) + c * bytes * width; } else { // variable width font, read width data, to get the index for (uint8_t i = 0; i < c; i++) { index += pgm_read_byte(this->font + sizeof(FontHeader) + i); } index = index * bytes + sizeof(FontHeader) + header.charCount; width = pgm_read_byte(this->font + sizeof(FontHeader) + c); } if (x < -width || y < -header.height) return width; assertCS(); startWrite(x > 0 ? x : 0, y > 0 ? y : 0, x+width > 127 ? 127 : x+width-1, y+header.height > 127 ? 127 : y+header.height-1, true); /* Characters are stored as follows: * * Each byte is up to 8 vertical pixels (LSB @ top) * Each row of bytes is adjacent * For fonts >8 pixels high, the bytes are strided by width * * ie for a font 16 pixels high and 8 pixels wide: * * P(0,0) P(0,1) P(1,0) P(1,1) P(2,0) P(2,1) P(3,0) P(3,1) ... etc * * * Things are made more annoying for OLED because we need to write to the display * bottom-to-top for each column, so striding backwards instead of forwards... */ for(int16_t ox = 0; ox < width; ox++) { if(ox+x >= COLUMNS) break; int16_t oy = 0; for(int8_t byte_y = bytes-1; byte_y >= 0; byte_y--) { uint8_t data = pgm_read_byte(this->font + index + ox + byte_y * width); int8_t start_bit; int8_t end_bit; if(bytes == 1) { start_bit = header.height-1; end_bit = 0; } else { start_bit = 7; end_bit = (byte_y < bytes-1) ? 0: 7-((header.height-1)%8); } for(int8_t bit_y = start_bit; bit_y >= end_bit; bit_y--) { if(oy+y < ROWS && ox+x >= 0 && oy+y >= 0) { writeData( (data & 1<<bit_y) ? colour : background); } oy++; if(oy == header.height) break; } } } releaseCS(); return width; }