diff --git a/docs/images/ssd1351.jpg b/docs/images/ssd1351.jpg new file mode 100644 index 00000000..541d97d1 Binary files /dev/null and b/docs/images/ssd1351.jpg differ diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 7bfa702c..b2277035 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -10,7 +10,7 @@ macro(get_module_name example_name module_name) elseif ((${example_name} MATCHES "^mq" AND ${length} EQUAL 3) OR ${example_name} STREQUAL "tp401") set (${module_name} "gas") else() - set(${module_name} ${example_name}) + set(${module_name} ${example_name}) endif() endmacro() @@ -25,25 +25,25 @@ macro(add_custom_example example_bin example_src example_module_list) if (MODULE_LIST) list(FIND MODULE_LIST ${module} index) if (${index} EQUAL -1) - set(found_all_modules FALSE) + set(found_all_modules FALSE) endif() endif() endforeach() if (found_all_modules) add_executable (${example_bin} ${example_src}) - target_link_libraries (${example_bin} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries (${example_bin} ${CMAKE_THREAD_LIBS_INIT}) foreach (module ${example_module_list}) set(module_dir "${PROJECT_SOURCE_DIR}/src/${module}") include_directories (${module_dir}) if (${module} STREQUAL "lcd") - set(module "i2clcd") + set(module "i2clcd") endif() - target_link_libraries (${example_bin} ${module}) + target_link_libraries (${example_bin} ${module}) endforeach() else() MESSAGE(INFO " Ignored ${example_bin}") set (example_bin "") - endif() + endif() endmacro() @@ -56,13 +56,13 @@ macro(add_example example_name) set(module_dir "${PROJECT_SOURCE_DIR}/src/${module_name}") if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${example_src}" AND EXISTS ${module_dir} - AND IS_DIRECTORY ${module_dir}) + AND IS_DIRECTORY ${module_dir}) add_custom_example(${example_bin} ${example_src} ${module_name}) if ((NOT ${example_bin} STREQUAL "") AND (${module_name} STREQUAL "grove")) set(grove_module_path "${PROJECT_SOURCE_DIR}/src/${example_name}") if (EXISTS ${grove_module_path}) include_directories(${grove_module_path}) - target_link_libraries (${example_bin} ${example_name}) + target_link_libraries (${example_bin} ${example_name}) endif() endif() else() @@ -239,6 +239,7 @@ add_example (hdxxvxta) add_example (rhusb) add_example (apds9930) add_example (kxcjk1013) +add_example (ssd1351) # These are special cases where you specify example binary, source file and module(s) include_directories (${PROJECT_SOURCE_DIR}/src) diff --git a/examples/c++/ssd1351.cxx b/examples/c++/ssd1351.cxx new file mode 100644 index 00000000..c0de509f --- /dev/null +++ b/examples/c++/ssd1351.cxx @@ -0,0 +1,61 @@ +#include "mraa.hpp" +#include +#include + +#include "ssd1351.h" + +#define BLACK 0x0000 +#define WHITE 0xFFFF +#define INTEL_BLUE 0x0BF8 + +int main(int argc, char **argv) +{ + // Define colors (16-bit RGB on 5/6/5 bits) + int colors[] = {0x0000, 0x000F, 0x03E0, 0x03EF, + 0x7800, 0x780F, 0x7BE0, 0xC618, + 0x7BEF, 0x001F, 0x07E0, 0x07FF, + 0xF800, 0xF81F, 0xFFE0, 0xFFFF}; +//! [Interesting] + // Initialize display with pins + // oc = 0, dc = 1, r = 2, si = 11, cl = 13 + upm::SSD1351* display = new upm::SSD1351(0, 1, 2); + + // Test lines pixel by pixel + for(int i = 0; i < SSD1351HEIGHT; i++) { + for(int j = 0; j < SSD1351WIDTH; j++) { + display->drawPixel(i, j, colors[i/8]); + } + } + display->refresh(); + sleep(5); + + // Test rectangles + for(int i = 0; i < SSD1351HEIGHT/32; i++) { + for (int j = 0; j < SSD1351WIDTH/32; j++) { + display->fillRect(i * 32, j * 32, 32, 32, colors[i * 4 + j]); + } + } + display->refresh(); + sleep(5); + + // Test circles + display->fillScreen(0x2104); + for(int i = 0; i < SSD1351HEIGHT/32; i++) { + for (int j = 0; j < SSD1351WIDTH/32; j++) { + display->drawCircle(i * 32 + 15, j * 32 + 15, 15, colors[i * 4 + j]); + } + } + display->refresh(); + sleep(5); + + // Test Text + display->fillScreen(INTEL_BLUE); + display->setTextColor(WHITE, INTEL_BLUE); + display->setTextSize(4); + display->setCursor(7, 30); + display->print("Intel"); + display->setCursor(5, 70); + display->print("IoTDK"); + display->refresh(); +//! [Interesting] +} diff --git a/src/ssd1351/CMakeLists.txt b/src/ssd1351/CMakeLists.txt new file mode 100644 index 00000000..ee75b73a --- /dev/null +++ b/src/ssd1351/CMakeLists.txt @@ -0,0 +1,5 @@ +set (libname "ssd1351") +set (libdescription "libupm SSD1351 SPI LCD") +set (module_src gfx.cxx ssd1351.cxx) +set (module_h gfx.h ssd1351.h) +upm_module_init() diff --git a/src/ssd1351/gfx.cxx b/src/ssd1351/gfx.cxx new file mode 100644 index 00000000..443932e0 --- /dev/null +++ b/src/ssd1351/gfx.cxx @@ -0,0 +1,218 @@ +/* + * Authors: Yevgeniy Kiveisha + * Mihai Tudor Panu + * + * Copyright (c) 2016 Intel Corporation. + * + * 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 +#include +#include + +#include "gfx.h" + +using namespace upm; + +GFX::GFX (int width, int height) : m_width(width), m_height(height), + m_textSize(1), m_textColor(0xFFFF), m_textBGColor(0x0000), + m_cursorX(0), m_cursorY(0), m_font(font) { +} + +GFX::~GFX () { +} + +void +GFX::fillScreen (uint16_t color) { + fillRect(0, 0, m_width, m_height, color); +} + +void +GFX::fillRect (int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + for (int16_t i=x; i abs(x1 - x0); + + if (steep) { + swap(x0, y0); + swap(x1, y1); + } + + if (x0 > x1) { + swap(x0, x1); + swap(y0, y1); + } + + int16_t dx, dy; + dx = x1 - x0; + dy = abs (y1 - y0); + + int16_t err = dx / 2; + int16_t ystep; + + if (y0 < y1) { + ystep = 1; + } else { + ystep = -1; + } + + for (; x0 <= x1; x0++) { + if (steep) { + drawPixel(y0, x0, color); + } else { + drawPixel(x0, y0, color); + } + err -= dy; + if (err < 0) { + y0 += ystep; + err += dx; + } + } +} + +void +GFX::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color) { + drawLine(x0, y0, x1, y1, color); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x0, y0, color); +} + +void +GFX::drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color) { + int16_t f = 1 - r; + int16_t ddF_x = 1; + int16_t ddF_y = -2 * r; + int16_t x = 0; + int16_t y = r; + + drawPixel(x0 , y0+r, color); + drawPixel(x0 , y0-r, color); + drawPixel(x0+r, y0 , color); + drawPixel(x0-r, y0 , color); + + while (x= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + + ddF_x += 2; + f += ddF_x; + + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + y, y0 + x, color); + drawPixel(x0 - y, y0 + x, color); + drawPixel(x0 + y, y0 - x, color); + drawPixel(x0 - y, y0 - x, color); + } +} + +void +GFX::setCursor (int16_t x, int16_t y) { + m_cursorX = x; + m_cursorY = y; +} + +void +GFX::setTextColor (uint16_t textColor, uint16_t textBGColor) { + m_textColor = textColor; + m_textBGColor = textBGColor; +} + +void +GFX::setTextSize (uint8_t size) { + m_textSize = (size > 0) ? size : 1; +} + +void +GFX::setTextWrap (uint8_t wrap) { + m_wrap = wrap; +} + +void +GFX::drawChar (int16_t x, int16_t y, uint8_t data, uint16_t color, uint16_t bg, uint8_t size) { + if( (x >= m_width) || // Clip right + (y >= m_height) || // Clip bottom + ((x + 6 * size - 1) < 0) || // Clip left + ((y + 8 * size - 1) < 0)) // Clip top + return; + + for (int8_t i=0; i<6; i++ ) { + uint8_t line; + if (i == 5) { + line = 0x0; + } else { + line = *(m_font+(data * 5)+i); + for (int8_t j = 0; j<8; j++) { + if (line & 0x1) { + if (size == 1) // default size + drawPixel (x+i, y+j, color); + else { // big size + fillRect (x+(i*size), y+(j*size), size, size, color); + } + } else if (bg != color) { + if (size == 1) // default size + drawPixel (x+i, y+j, bg); + else { // big size + fillRect (x+i*size, y+j*size, size, size, bg); + } + } + line >>= 1; + } + } + } +} + +void +GFX::print (std::string msg) { + int len = msg.length(); + + for (int idx = 0; idx < len; idx++) { + if (msg[idx] == '\n') { + m_cursorY += m_textSize * 8; + m_cursorX = 0; + } else if (msg[idx] == '\r') { + // skip em + } else { + drawChar(m_cursorX, m_cursorY, msg[idx], m_textColor, m_textBGColor, m_textSize); + m_cursorX += m_textSize * 6; + if (m_wrap && ((m_cursorX + m_textSize * 6) >= m_width)) { + m_cursorY += m_textSize * 8; + m_cursorX = 0; + } + } + } +} diff --git a/src/ssd1351/gfx.h b/src/ssd1351/gfx.h new file mode 100644 index 00000000..e199cd2f --- /dev/null +++ b/src/ssd1351/gfx.h @@ -0,0 +1,453 @@ +/* + * Authors: Yevgeniy Kiveisha + * Mihai Tudor Panu + * + * Copyright (c) 2016 Intel Corporation. + * + * 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. + */ +#pragma once + +#include +#include +#include +#include + +#define swap(a, b) { int16_t t = a; a = b; b = t; } + +namespace upm { + +const unsigned char font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, + 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, + 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, + 0x18, 0x3C, 0x7E, 0x3C, 0x18, + 0x1C, 0x57, 0x7D, 0x57, 0x1C, + 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, + 0x00, 0x18, 0x3C, 0x18, 0x00, + 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, + 0x00, 0x18, 0x24, 0x18, 0x00, + 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, + 0x30, 0x48, 0x3A, 0x06, 0x0E, + 0x26, 0x29, 0x79, 0x29, 0x26, + 0x40, 0x7F, 0x05, 0x05, 0x07, + 0x40, 0x7F, 0x05, 0x25, 0x3F, + 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, + 0x7F, 0x3E, 0x1C, 0x1C, 0x08, + 0x08, 0x1C, 0x1C, 0x3E, 0x7F, + 0x14, 0x22, 0x7F, 0x22, 0x14, + 0x5F, 0x5F, 0x00, 0x5F, 0x5F, + 0x06, 0x09, 0x7F, 0x01, 0x7F, + 0x00, 0x66, 0x89, 0x95, 0x6A, + 0x60, 0x60, 0x60, 0x60, 0x60, + 0x94, 0xA2, 0xFF, 0xA2, 0x94, + 0x08, 0x04, 0x7E, 0x04, 0x08, + 0x10, 0x20, 0x7E, 0x20, 0x10, + 0x08, 0x08, 0x2A, 0x1C, 0x08, + 0x08, 0x1C, 0x2A, 0x08, 0x08, + 0x1E, 0x10, 0x10, 0x10, 0x10, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, + 0x30, 0x38, 0x3E, 0x38, 0x30, + 0x06, 0x0E, 0x3E, 0x0E, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x07, 0x00, + 0x14, 0x7F, 0x14, 0x7F, 0x14, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, + 0x23, 0x13, 0x08, 0x64, 0x62, + 0x36, 0x49, 0x56, 0x20, 0x50, + 0x00, 0x08, 0x07, 0x03, 0x00, + 0x00, 0x1C, 0x22, 0x41, 0x00, + 0x00, 0x41, 0x22, 0x1C, 0x00, + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, + 0x08, 0x08, 0x3E, 0x08, 0x08, + 0x00, 0x80, 0x70, 0x30, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x60, 0x60, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x02, + 0x3E, 0x51, 0x49, 0x45, 0x3E, + 0x00, 0x42, 0x7F, 0x40, 0x00, + 0x72, 0x49, 0x49, 0x49, 0x46, + 0x21, 0x41, 0x49, 0x4D, 0x33, + 0x18, 0x14, 0x12, 0x7F, 0x10, + 0x27, 0x45, 0x45, 0x45, 0x39, + 0x3C, 0x4A, 0x49, 0x49, 0x31, + 0x41, 0x21, 0x11, 0x09, 0x07, + 0x36, 0x49, 0x49, 0x49, 0x36, + 0x46, 0x49, 0x49, 0x29, 0x1E, + 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x40, 0x34, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x00, 0x41, 0x22, 0x14, 0x08, + 0x02, 0x01, 0x59, 0x09, 0x06, + 0x3E, 0x41, 0x5D, 0x59, 0x4E, + 0x7C, 0x12, 0x11, 0x12, 0x7C, + 0x7F, 0x49, 0x49, 0x49, 0x36, + 0x3E, 0x41, 0x41, 0x41, 0x22, + 0x7F, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x49, 0x49, 0x49, 0x41, + 0x7F, 0x09, 0x09, 0x09, 0x01, + 0x3E, 0x41, 0x41, 0x51, 0x73, + 0x7F, 0x08, 0x08, 0x08, 0x7F, + 0x00, 0x41, 0x7F, 0x41, 0x00, + 0x20, 0x40, 0x41, 0x3F, 0x01, + 0x7F, 0x08, 0x14, 0x22, 0x41, + 0x7F, 0x40, 0x40, 0x40, 0x40, + 0x7F, 0x02, 0x1C, 0x02, 0x7F, + 0x7F, 0x04, 0x08, 0x10, 0x7F, + 0x3E, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x09, 0x09, 0x09, 0x06, + 0x3E, 0x41, 0x51, 0x21, 0x5E, + 0x7F, 0x09, 0x19, 0x29, 0x46, + 0x26, 0x49, 0x49, 0x49, 0x32, + 0x03, 0x01, 0x7F, 0x01, 0x03, + 0x3F, 0x40, 0x40, 0x40, 0x3F, + 0x1F, 0x20, 0x40, 0x20, 0x1F, + 0x3F, 0x40, 0x38, 0x40, 0x3F, + 0x63, 0x14, 0x08, 0x14, 0x63, + 0x03, 0x04, 0x78, 0x04, 0x03, + 0x61, 0x59, 0x49, 0x4D, 0x43, + 0x00, 0x7F, 0x41, 0x41, 0x41, + 0x02, 0x04, 0x08, 0x10, 0x20, + 0x00, 0x41, 0x41, 0x41, 0x7F, + 0x04, 0x02, 0x01, 0x02, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x03, 0x07, 0x08, 0x00, + 0x20, 0x54, 0x54, 0x78, 0x40, + 0x7F, 0x28, 0x44, 0x44, 0x38, + 0x38, 0x44, 0x44, 0x44, 0x28, + 0x38, 0x44, 0x44, 0x28, 0x7F, + 0x38, 0x54, 0x54, 0x54, 0x18, + 0x00, 0x08, 0x7E, 0x09, 0x02, + 0x18, 0xA4, 0xA4, 0x9C, 0x78, + 0x7F, 0x08, 0x04, 0x04, 0x78, + 0x00, 0x44, 0x7D, 0x40, 0x00, + 0x20, 0x40, 0x40, 0x3D, 0x00, + 0x7F, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, + 0x7C, 0x04, 0x78, 0x04, 0x78, + 0x7C, 0x08, 0x04, 0x04, 0x78, + 0x38, 0x44, 0x44, 0x44, 0x38, + 0xFC, 0x18, 0x24, 0x24, 0x18, + 0x18, 0x24, 0x24, 0x18, 0xFC, + 0x7C, 0x08, 0x04, 0x04, 0x08, + 0x48, 0x54, 0x54, 0x54, 0x24, + 0x04, 0x04, 0x3F, 0x44, 0x24, + 0x3C, 0x40, 0x40, 0x20, 0x7C, + 0x1C, 0x20, 0x40, 0x20, 0x1C, + 0x3C, 0x40, 0x30, 0x40, 0x3C, + 0x44, 0x28, 0x10, 0x28, 0x44, + 0x4C, 0x90, 0x90, 0x90, 0x7C, + 0x44, 0x64, 0x54, 0x4C, 0x44, + 0x00, 0x08, 0x36, 0x41, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, + 0x00, 0x41, 0x36, 0x08, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x02, + 0x3C, 0x26, 0x23, 0x26, 0x3C, + 0x1E, 0xA1, 0xA1, 0x61, 0x12, + 0x3A, 0x40, 0x40, 0x20, 0x7A, + 0x38, 0x54, 0x54, 0x55, 0x59, + 0x21, 0x55, 0x55, 0x79, 0x41, + 0x21, 0x54, 0x54, 0x78, 0x41, + 0x21, 0x55, 0x54, 0x78, 0x40, + 0x20, 0x54, 0x55, 0x79, 0x40, + 0x0C, 0x1E, 0x52, 0x72, 0x12, + 0x39, 0x55, 0x55, 0x55, 0x59, + 0x39, 0x54, 0x54, 0x54, 0x59, + 0x39, 0x55, 0x54, 0x54, 0x58, + 0x00, 0x00, 0x45, 0x7C, 0x41, + 0x00, 0x02, 0x45, 0x7D, 0x42, + 0x00, 0x01, 0x45, 0x7C, 0x40, + 0xF0, 0x29, 0x24, 0x29, 0xF0, + 0xF0, 0x28, 0x25, 0x28, 0xF0, + 0x7C, 0x54, 0x55, 0x45, 0x00, + 0x20, 0x54, 0x54, 0x7C, 0x54, + 0x7C, 0x0A, 0x09, 0x7F, 0x49, + 0x32, 0x49, 0x49, 0x49, 0x32, + 0x32, 0x48, 0x48, 0x48, 0x32, + 0x32, 0x4A, 0x48, 0x48, 0x30, + 0x3A, 0x41, 0x41, 0x21, 0x7A, + 0x3A, 0x42, 0x40, 0x20, 0x78, + 0x00, 0x9D, 0xA0, 0xA0, 0x7D, + 0x39, 0x44, 0x44, 0x44, 0x39, + 0x3D, 0x40, 0x40, 0x40, 0x3D, + 0x3C, 0x24, 0xFF, 0x24, 0x24, + 0x48, 0x7E, 0x49, 0x43, 0x66, + 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, + 0xFF, 0x09, 0x29, 0xF6, 0x20, + 0xC0, 0x88, 0x7E, 0x09, 0x03, + 0x20, 0x54, 0x54, 0x79, 0x41, + 0x00, 0x00, 0x44, 0x7D, 0x41, + 0x30, 0x48, 0x48, 0x4A, 0x32, + 0x38, 0x40, 0x40, 0x22, 0x7A, + 0x00, 0x7A, 0x0A, 0x0A, 0x72, + 0x7D, 0x0D, 0x19, 0x31, 0x7D, + 0x26, 0x29, 0x29, 0x2F, 0x28, + 0x26, 0x29, 0x29, 0x29, 0x26, + 0x30, 0x48, 0x4D, 0x40, 0x20, + 0x38, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x38, + 0x2F, 0x10, 0xC8, 0xAC, 0xBA, + 0x2F, 0x10, 0x28, 0x34, 0xFA, + 0x00, 0x00, 0x7B, 0x00, 0x00, + 0x08, 0x14, 0x2A, 0x14, 0x22, + 0x22, 0x14, 0x2A, 0x14, 0x08, + 0xAA, 0x00, 0x55, 0x00, 0xAA, + 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x10, 0x10, 0x10, 0xFF, 0x00, + 0x14, 0x14, 0x14, 0xFF, 0x00, + 0x10, 0x10, 0xFF, 0x00, 0xFF, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x14, 0x14, 0x14, 0xFC, 0x00, + 0x14, 0x14, 0xF7, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x14, 0x14, 0xF4, 0x04, 0xFC, + 0x14, 0x14, 0x17, 0x10, 0x1F, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0x1F, 0x00, + 0x10, 0x10, 0x10, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0xF0, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xFF, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x14, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x1F, 0x10, 0x17, + 0x00, 0x00, 0xFC, 0x04, 0xF4, + 0x14, 0x14, 0x17, 0x10, 0x17, + 0x14, 0x14, 0xF4, 0x04, 0xF4, + 0x00, 0x00, 0xFF, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0xF7, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x17, 0x14, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0xF4, 0x14, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x00, 0x00, 0x1F, 0x10, 0x1F, + 0x00, 0x00, 0x00, 0x1F, 0x14, + 0x00, 0x00, 0x00, 0xFC, 0x14, + 0x00, 0x00, 0xF0, 0x10, 0xF0, + 0x10, 0x10, 0xFF, 0x10, 0xFF, + 0x14, 0x14, 0x14, 0xFF, 0x14, + 0x10, 0x10, 0x10, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x10, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x38, 0x44, 0x44, 0x38, 0x44, + 0x7C, 0x2A, 0x2A, 0x3E, 0x14, + 0x7E, 0x02, 0x02, 0x06, 0x06, + 0x02, 0x7E, 0x02, 0x7E, 0x02, + 0x63, 0x55, 0x49, 0x41, 0x63, + 0x38, 0x44, 0x44, 0x3C, 0x04, + 0x40, 0x7E, 0x20, 0x1E, 0x20, + 0x06, 0x02, 0x7E, 0x02, 0x02, + 0x99, 0xA5, 0xE7, 0xA5, 0x99, + 0x1C, 0x2A, 0x49, 0x2A, 0x1C, + 0x4C, 0x72, 0x01, 0x72, 0x4C, + 0x30, 0x4A, 0x4D, 0x4D, 0x30, + 0x30, 0x48, 0x78, 0x48, 0x30, + 0xBC, 0x62, 0x5A, 0x46, 0x3D, + 0x3E, 0x49, 0x49, 0x49, 0x00, + 0x7E, 0x01, 0x01, 0x01, 0x7E, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x44, 0x44, 0x5F, 0x44, 0x44, + 0x40, 0x51, 0x4A, 0x44, 0x40, + 0x40, 0x44, 0x4A, 0x51, 0x40, + 0x00, 0x00, 0xFF, 0x01, 0x03, + 0xE0, 0x80, 0xFF, 0x00, 0x00, + 0x08, 0x08, 0x6B, 0x6B, 0x08, + 0x36, 0x12, 0x36, 0x24, 0x36, + 0x06, 0x0F, 0x09, 0x0F, 0x06, + 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x00, + 0x30, 0x40, 0xFF, 0x01, 0x01, + 0x00, 0x1F, 0x01, 0x01, 0x1E, + 0x00, 0x19, 0x1D, 0x17, 0x12, + 0x00, 0x3C, 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** + * @brief GFX helper class + * + * This file is used by the screen. + */ +class GFX { + public: + /** + * Instantiates a GFX object + * + * @param width Screen width + * @param height Screen height + */ + GFX (int width, int height); + + /** + * GFX object destructor + */ + ~GFX (); + + /** + * Sends a pixel color (RGB) to the chip. Must be implemented by the + * inherited class. + * + * @param x Axis on the horizontal scale + * @param y Axis on the vertical scale + * @param color RGB value + */ + virtual void drawPixel (int16_t x, int16_t y, uint16_t color) = 0; + + /** + * Copies the buffer to the chip via the SPI. + */ + virtual void refresh () = 0; + + /** + * + * + * @param x Axis on the horizontal scale + * @param y Axis on the vertical scale + * @param data Character to write + * @param color Character color + * @param bg Character background color + * @param size Size of the font + */ + void drawChar (int16_t x, int16_t y, uint8_t data, uint16_t color, uint16_t bg, uint8_t size); + + /** + * Prints a message on the screen + * + * @param msg Message to print + */ + void print (std::string msg); + + /** + * Fills the screen with a selected color + * + * @param color Selected color + */ + void fillScreen (uint16_t color); + + /** + * Fills a rectangle with a selected color + * + * @param x Axis on the horizontal scale (top-left corner) + * @param y Axis on the vertical scale (top-left corner) + * @param w Distanse from x + * @param h Distanse from y + * @param color Selected color + */ + void fillRect (int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + + /** + * Draws a line on the vertical scale + * + * @param x Axis on the horizontal scale + * @param y Axis on the vertical scale + * @param h Distanse from y + * @param color Selected color + */ + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); + + /** + * Draws a line from coordinate C0 to coordinate C1 + * + * @param x0 First coordinate + * @param y0 First coordinate + * @param x1 Second coordinate + * @param y1 Second coordinate + * @param color selected color + */ + void drawLine (int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); + + /** + * Draws a triangle + * + * @param x0 First coordinate + * @param y0 First coordinate + * @param x1 Second coordinate + * @param y1 Second coordinate + * @param x2 Third coordinate + * @param y2 Third coordinate + * @param color Selected color + */ + void drawTriangle (int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color); + + /** + * Draws a circle + * + * @param x Center of the circle on the horizontal scale + * @param y Center of the circle on the vertical scale + * @param r Radius of the circle + * @param color Color of the circle + */ + void drawCircle (int16_t x, int16_t y, int16_t r, uint16_t color); + + /** + * Sets the cursor for a text message + * + * @param x Axis on the horizontal scale + * @param y Axis on the vertical scale + */ + void setCursor (int16_t x, int16_t y); + + /** + * Sets a text color for a message + * + * @param textColor Font color + * @param textBGColor Background color + */ + void setTextColor (uint16_t textColor, uint16_t textBGColor); + + /** + * Sets the size of the font + * + * @param size Font size + */ + void setTextSize (uint8_t size); + + /** + * Wraps a printed message + * + * @param wrap True (0x1) or false (0x0) + */ + void setTextWrap (uint8_t wrap); + + protected: + int m_width; /**< Screen width */ + int m_height; /**< Screen height */ + int m_textSize; /**< Printed text size */ + int m_textColor; /**< Printed text color */ + int m_textBGColor; /**< Printed text background color */ + int m_cursorX; /**< Cursor X coordinate */ + int m_cursorY; /**< Cursor Y coordinate */ + int m_wrap; /**< Wrapper flag (true or false) */ + + const unsigned char * m_font; + }; +} diff --git a/src/ssd1351/javaupm_ssd1351.i b/src/ssd1351/javaupm_ssd1351.i new file mode 100644 index 00000000..7adb59a7 --- /dev/null +++ b/src/ssd1351/javaupm_ssd1351.i @@ -0,0 +1,28 @@ +%module javaupm_ssd1351 +%include "../upm.i" +%include "typemaps.i" +%include "stdint.i" + +%ignore m_map; +%ignore font; + +%include "gfx.h" +%{ + #include "gfx.h" +%} + +%include "ssd1351.h" +%{ + #include "ssd1351.h" +%} + +%pragma(java) jniclasscode=%{ + static { + try { + System.loadLibrary("javaupm_ssd1351"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. \n" + e); + System.exit(1); + } + } +%} \ No newline at end of file diff --git a/src/ssd1351/jsupm_ssd1351.i b/src/ssd1351/jsupm_ssd1351.i new file mode 100644 index 00000000..b0961519 --- /dev/null +++ b/src/ssd1351/jsupm_ssd1351.i @@ -0,0 +1,12 @@ +%module jsupm_ssd1351 +%include "../upm.i" + +%include "gfx.h" +%{ + #include "gfx.h" +%} + +%include "ssd1351.h" +%{ + #include "ssd1351.h" +%} diff --git a/src/ssd1351/pyupm_ssd1351.i b/src/ssd1351/pyupm_ssd1351.i new file mode 100644 index 00000000..fee480d2 --- /dev/null +++ b/src/ssd1351/pyupm_ssd1351.i @@ -0,0 +1,17 @@ +// Include doxygen-generated documentation +%include "pyupm_doxy2swig.i" +%module pyupm_ssd1351 +%include "../upm.i" + +%feature("autodoc", "3"); +%rename("printString") print(std::string msg); + +%include "gfx.h" +%{ + #include "gfx.h" +%} + +%include "ssd1351.h" +%{ + #include "ssd1351.h" +%} diff --git a/src/ssd1351/ssd1351.cxx b/src/ssd1351/ssd1351.cxx new file mode 100644 index 00000000..2103603b --- /dev/null +++ b/src/ssd1351/ssd1351.cxx @@ -0,0 +1,225 @@ +/* + * Author: Mihai Tudor Panu + * Copyright (c) 2016 Intel Corporation. + * + * Based on Adafruit SSD1351 library. + * + * 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 +#include +#include +#include +#include + +#include "ssd1351.h" + +using namespace upm; +using namespace std; + +SSD1351::SSD1351 (uint8_t oc, uint8_t dc, uint8_t rst) : + GFX(SSD1351WIDTH, SSD1351HEIGHT), + m_spi(0), m_oc(oc), m_dc(dc), m_rst(rst) { + + m_name = "SSD1351"; + m_usemap = true; + + // Setup SPI bus + m_spi.frequency(8 * 1000000); + m_spi.mode(mraa::SPI_MODE3); + m_spi.writeByte(0x00); // Need to bring clk high before init + + // Init pins + if (m_oc.dir(mraa::DIR_OUT) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Could not initialize CS pin"); + return; + } + m_oc.useMmap(true); + if (m_dc.dir(mraa::DIR_OUT) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Could not initialize data/cmd pin"); + return; + } + m_dc.useMmap(true); + if (m_rst.dir(mraa::DIR_OUT) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Could not initialize reset pin"); + return; + } + + // Toggle reset pin + ocLow(); + m_rst.write(1); + usleep(500000); + m_rst.write(0); + usleep(500000); + m_rst.write(1); + usleep(500000); + + // Configure and init display + writeCommand(SSD1351_CMD_COMMANDLOCK); + writeData(0x12); + + writeCommand(SSD1351_CMD_COMMANDLOCK); + writeData(0xB1); + + writeCommand(SSD1351_CMD_DISPLAYOFF); + + writeCommand(SSD1351_CMD_CLOCKDIV); + writeCommand(0xF1); + + writeCommand(SSD1351_CMD_MUXRATIO); + writeData(127); + + writeCommand(SSD1351_CMD_SETREMAP); + writeData(0x74); + + writeCommand(SSD1351_CMD_SETCOLUMN); + writeData(0x00); + writeData(0x7F); + + writeCommand(SSD1351_CMD_SETROW); + writeData(0x00); + writeData(0x7F); + + writeCommand(SSD1351_CMD_STARTLINE); + if (SSD1351HEIGHT == 96) { + writeData(96); + } else { + writeData(0); + } + + writeCommand(SSD1351_CMD_DISPLAYOFFSET); + writeData(0x0); + + writeCommand(SSD1351_CMD_SETGPIO); + writeData(0x00); + + writeCommand(SSD1351_CMD_FUNCTIONSELECT); + writeData(0x01); + + writeCommand(SSD1351_CMD_PRECHARGE); + writeCommand(0x32); + + writeCommand(SSD1351_CMD_VCOMH); + writeCommand(0x05); + + writeCommand(SSD1351_CMD_NORMALDISPLAY); + + writeCommand(SSD1351_CMD_CONTRASTABC); + writeData(0xC8); + writeData(0x80); + writeData(0xC8); + + writeCommand(SSD1351_CMD_CONTRASTMASTER); + writeData(0x0F); + + writeCommand(SSD1351_CMD_SETVSL ); + writeData(0xA0); + writeData(0xB5); + writeData(0x55); + + writeCommand(SSD1351_CMD_PRECHARGE2); + writeData(0x01); + + writeCommand(SSD1351_CMD_DISPLAYON); +} + +SSD1351::~SSD1351() { +} + +void +SSD1351::writeCommand (uint8_t value) { + dcLow(); + m_spi.writeByte(value); +} + +void +SSD1351::writeData (uint8_t value) { + dcHigh (); + m_spi.writeByte(value); +} + +void +SSD1351::drawPixel(int16_t x, int16_t y, uint16_t color) { + if ((x < 0) || (y < 0) || (x >= SSD1351WIDTH) || (y >= SSD1351HEIGHT)) + return; + + if(m_usemap) { + int index = (y * SSD1351WIDTH + x) * 2; + m_map[index] = color >> 8; + m_map[index + 1] = color; + } else { + writeCommand(SSD1351_CMD_SETCOLUMN); + writeData(x); + writeData(SSD1351WIDTH-1); + + writeCommand(SSD1351_CMD_SETROW); + writeData(y); + writeData(SSD1351HEIGHT-1); + + writeCommand(SSD1351_CMD_WRITERAM); + writeData(color >> 8); + writeData(color); + } +} +void +SSD1351::refresh () { + writeCommand(SSD1351_CMD_WRITERAM); + int blockSize = SSD1351HEIGHT * SSD1351WIDTH * 2 / BLOCKS; + dcHigh(); + for (int block = 0; block < BLOCKS; block++) { + m_spi.write(&m_map[block * blockSize], blockSize); + } +} +void +SSD1351::ocLow() { + if (m_oc.write(LOW) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Failed to write CS pin"); + } +} +void +SSD1351::ocHigh() { + if (m_oc.write(HIGH) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Failed to write CS pin"); + } +} +void +SSD1351::dcLow() { + if (m_dc.write(LOW) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Failed to write data/cmd pin"); + } +} +void +SSD1351::dcHigh() { + if (m_dc.write(HIGH) != mraa::SUCCESS) { + throw std::runtime_error(string(__FUNCTION__) + + ": Failed to write data/cmd pin"); + } +} +void +upm::SSD1351::useMemoryMap(bool var) { + m_usemap = var; +} diff --git a/src/ssd1351/ssd1351.h b/src/ssd1351/ssd1351.h new file mode 100644 index 00000000..6f23c66e --- /dev/null +++ b/src/ssd1351/ssd1351.h @@ -0,0 +1,188 @@ +/* + * Author: Mihai Tudor Panu + * Copyright (c) 2016 Intel Corporation. + * + * Based on Adafruit SSD1351 library. + * + * 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. + */ +#pragma once + +#include + +#include +#include +#include +#include "gfx.h" + +// Display Size +#define SSD1351WIDTH 128 +#define SSD1351HEIGHT 128 // Set this to 96 for 1.27" + +// SSD1351 Commands +#define SSD1351_CMD_SETCOLUMN 0x15 +#define SSD1351_CMD_SETROW 0x75 +#define SSD1351_CMD_WRITERAM 0x5C +#define SSD1351_CMD_READRAM 0x5D +#define SSD1351_CMD_SETREMAP 0xA0 +#define SSD1351_CMD_STARTLINE 0xA1 +#define SSD1351_CMD_DISPLAYOFFSET 0xA2 +#define SSD1351_CMD_DISPLAYALLOFF 0xA4 +#define SSD1351_CMD_DISPLAYALLON 0xA5 +#define SSD1351_CMD_NORMALDISPLAY 0xA6 +#define SSD1351_CMD_INVERTDISPLAY 0xA7 +#define SSD1351_CMD_FUNCTIONSELECT 0xAB +#define SSD1351_CMD_DISPLAYOFF 0xAE +#define SSD1351_CMD_DISPLAYON 0xAF +#define SSD1351_CMD_PRECHARGE 0xB1 +#define SSD1351_CMD_DISPLAYENHANCE 0xB2 +#define SSD1351_CMD_CLOCKDIV 0xB3 +#define SSD1351_CMD_SETVSL 0xB4 +#define SSD1351_CMD_SETGPIO 0xB5 +#define SSD1351_CMD_PRECHARGE2 0xB6 +#define SSD1351_CMD_SETGRAY 0xB8 +#define SSD1351_CMD_USELUT 0xB9 +#define SSD1351_CMD_PRECHARGELEVEL 0xBB +#define SSD1351_CMD_VCOMH 0xBE +#define SSD1351_CMD_CONTRASTABC 0xC1 +#define SSD1351_CMD_CONTRASTMASTER 0xC7 +#define SSD1351_CMD_MUXRATIO 0xCA +#define SSD1351_CMD_COMMANDLOCK 0xFD +#define SSD1351_CMD_HORIZSCROLL 0x96 +#define SSD1351_CMD_STOPSCROLL 0x9E +#define SSD1351_CMD_STARTSCROLL 0x9F + +#define HIGH 1 +#define LOW 0 + +// Number of blocks for SPI transfer of buffer +#define BLOCKS 8 + +namespace upm { +/** + * @brief SSD1351 OLED library + * @defgroup ssd1351 libupm-ssd1351 + * @ingroup adafruit spi display + */ +/** + * @library ssd1351 + * @sensor ssd1351 + * @comname SSD1351 OLED + * @type display + * @man adafruit + * @web http://www.adafruit.com/products/1431 + * @con spi + * + * @brief API for SSD1351 OLED displays + * + * This module defines the interface for the SSD1351 display library + * + * @image html ssd1351.jpg + * @snippet ssd1351.cxx Interesting + */ +class SSD1351 : public GFX{ + public: + /** + * Instantiates an SSD1351 object + * + * @param oc LCD chip select pin + * @param dc Data/command pin + * @param rst Reset pin + */ + SSD1351 (uint8_t oc, uint8_t dc, uint8_t rst); + + /** + * SSD1351 object destructor + */ + ~SSD1351(); + + /** + * Returns the name of the component + */ + std::string name() + { + return m_name; + } + + /** + * Sends a command to an SPI bus + * + * @param value Command + */ + void writeCommand (uint8_t value); + + /** + * Sends data to an SPI bus + * + * @param value Data + */ + void writeData (uint8_t value); + /** + * Sends a pixel color (RGB) to the display buffer or chip + * + * @param x Axis on the horizontal scale + * @param y Axis on the vertical scale + * @param color RGB (16-bit) color (R[0-4], G[5-10], B[11-15]) + */ + void drawPixel (int16_t x, int16_t y, uint16_t color); + + /** + * Copies the buffer to the chip via the SPI bus + */ + void refresh (); + + /** + * Set OLED chip select LOW + */ + void ocLow (); + + /** + * Set OLED chip select HIGH + */ + void ocHigh (); + + /** + * Data select LOW + */ + void dcLow (); + + /** + * Data select HIGH + */ + void dcHigh (); + + /** + * Use memory mapped (buffered) writes to the display + * + * @param var true for yes (default), false for no + */ + void useMemoryMap (bool var); + private: + mraa::Spi m_spi; + uint8_t m_map[SSD1351HEIGHT * SSD1351WIDTH * 2]; /**< Screen buffer */ + bool m_usemap; + + mraa::Gpio m_oc; + mraa::Gpio m_dc; + mraa::Gpio m_rst; + + std::string m_name; +}; +}