Seed I2C OLED library for mbed

Files at this revision

API Documentation at this revision

Comitter:
jacobjohnson
Date:
Wed Jan 25 18:33:22 2017 +0000
Commit message:
Seeed OLED Library for mbed;

Changed in this revision

SeeedGrayOLED.cpp Show annotated file Show diff for this revision Revisions of this file
SeeedGrayOLED.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r e14364aac2ca SeeedGrayOLED.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SeeedGrayOLED.cpp	Wed Jan 25 18:33:22 2017 +0000
@@ -0,0 +1,444 @@
+/*
+ * SeeedGrayOLED.cpp
+ * SSD1327 Gray OLED Driver Library for SSD1327
+ *
+ * Copyright (c) 2011 seeed technology inc.
+ * Author        :   Visweswara R
+ * Create Time   :   Dec 2011
+ * Change Log    :
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "mbed.h"
+#include "SeeedGrayOLED.h"
+#include <stdio.h>
+#include <string.h>
+
+// 8x8 Font ASCII 32 - 127 Implemented
+// Users can modify this to support more characters(glyphs)
+// BasicFont is placed in code memory.
+
+// This font can be freely used without any restriction(It is placed in public domain)
+// const unsigned char BasicFont[][8] PROGMEM=
+
+
+const uint8_t BasicFont[][8] = {
+  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
+  {0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00},
+  {0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00},
+  {0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00},
+  {0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00},
+  {0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00},
+  {0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00},
+  {0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00},
+  {0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00},
+  {0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00},
+  {0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00},
+  {0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00},
+  {0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00},
+  {0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
+  {0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
+  {0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00},
+  {0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00},
+  {0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00},
+  {0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00},
+  {0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00},
+  {0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00},
+  {0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00},
+  {0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00},
+  {0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00},
+  {0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00},
+  {0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00},
+  {0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00},
+  {0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00},
+  {0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00},
+  {0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00},
+  {0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00},
+  {0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00},
+  {0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00},
+  {0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00},
+  {0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00},
+  {0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00},
+  {0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00},
+  {0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00},
+  {0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00},
+  {0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00},
+  {0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00},
+  {0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00},
+  {0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00},
+  {0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00},
+  {0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00},
+  {0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00},
+  {0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00},
+  {0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00},
+  {0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00},
+  {0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00},
+  {0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00},
+  {0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00},
+  {0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00},
+  {0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00},
+  {0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00},
+  {0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00},
+  {0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00},
+  {0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00},
+  {0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00},
+  {0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00},
+  {0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00},
+  {0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00},
+  {0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00},
+  {0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00},
+  {0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00},
+  {0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00},
+  {0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00},
+  {0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00},
+  {0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00},
+  {0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00},
+  {0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00},
+  {0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00},
+  {0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00},
+  {0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00},
+  {0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00},
+  {0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00},
+  {0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00},
+  {0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00},
+  {0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00},
+  {0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00},
+  {0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00},
+  {0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00},
+  {0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00},
+  {0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00},
+  {0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00},
+  {0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00},
+  {0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00},
+  {0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00},
+  {0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00},
+  {0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00},
+  {0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00},
+  {0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00},
+  {0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00},
+  {0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00},
+  {0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00},
+  {0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00} 
+};
+
+
+SeeedGrayOLED::SeeedGrayOLED(PinName sda, PinName scl):
+    _device(sda, scl)
+{    
+}
+
+
+void SeeedGrayOLED::init(void)
+{
+    sendCommand(0xFD); // Unlock OLED driver IC MCU interface from entering command. i.e: Accept commands
+    sendCommand(0x12);
+    sendCommand(0xAE); // Set display off
+    sendCommand(0xA8); // set multiplex ratio
+    sendCommand(0x5F); // 96
+    sendCommand(0xA1); // set display start line
+    sendCommand(0x00);
+    sendCommand(0xA2); // set display offset
+    sendCommand(0x60);
+    sendCommand(0xA0); // set remap
+    sendCommand(0x46);
+    sendCommand(0xAB); // set vdd internal
+    sendCommand(0x01); //
+    sendCommand(0x81); // set contrasr
+    sendCommand(0x53); // 100 nit
+    sendCommand(0xB1); // Set Phase Length
+    sendCommand(0X51); //
+    sendCommand(0xB3); // Set Display Clock Divide Ratio/Oscillator Frequency
+    sendCommand(0x01);
+    sendCommand(0xB9); //
+    sendCommand(0xBC); // set pre_charge voltage/VCOMH
+    sendCommand(0x08); // (0x08);
+    sendCommand(0xBE); // set VCOMH
+    sendCommand(0X07); // (0x07);
+    sendCommand(0xB6); // Set second pre-charge period
+    sendCommand(0x01); //
+    sendCommand(0xD5); // enable second precharge and enternal vsl
+    sendCommand(0X62); // (0x62);
+    sendCommand(0xA4); // Set Normal Display Mode
+    sendCommand(0x2E); // Deactivate Scroll
+    sendCommand(0xAF); // Switch on display
+    wait_ms(100);
+
+    // Row Address
+    sendCommand(0x75);    // Set Row Address 
+    sendCommand(0x00);    // Start 0
+    sendCommand(0x5f);    // End 95 
+
+
+    // Column Address
+    sendCommand(0x15);    // Set Column Address 
+    sendCommand(0x08);    // Start from 8th Column of driver IC. This is 0th Column for OLED 
+    sendCommand(0x37);    // End at  (8 + 47)th column. Each Column has 2 pixels(segments)
+
+    // Init gray level for text. Default:Brightest White
+    grayH= 0xF0;
+    grayL= 0x0F;
+}
+
+void SeeedGrayOLED::sendCommand(unsigned char command)
+{
+    int transmissionSuccessful;
+    _device.start();
+    transmissionSuccessful  = _device.write(SeeedGrayOLED_Address << 1);
+    Thread::wait(5);
+    transmissionSuccessful |= _device.write(SeeedGrayOLED_Command_Mode);
+    Thread::wait(5);
+    transmissionSuccessful |= _device.write(command);
+    Thread::wait(5);
+    _device.stop();
+    Thread::wait(50);
+    // return (transmissionSuccessful);    
+}
+
+void SeeedGrayOLED::setContrastLevel(unsigned char ContrastLevel)
+{
+    sendCommand(SeeedGrayOLED_Set_ContrastLevel_Cmd);
+    sendCommand(ContrastLevel);
+}
+
+void SeeedGrayOLED::setHorizontalMode()
+{
+    sendCommand(0xA0); // remap to
+    sendCommand(0x42); // horizontal mode
+
+    // Row Address
+    sendCommand(0x75);    // Set Row Address 
+    sendCommand(0x00);    // Start 0
+    sendCommand(0x5f);    // End 95 
+
+    // Column Address
+    sendCommand(0x15);    // Set Column Address 
+    sendCommand(0x08);    // Start from 8th Column of driver IC. This is 0th Column for OLED 
+    sendCommand(0x37);    // End at  (8 + 47)th column. Each Column has 2 pixels(or segments)
+}
+
+void SeeedGrayOLED::setVerticalMode()
+{
+    sendCommand(0xA0); // remap to
+    sendCommand(0x46); // Vertical mode
+}
+
+void SeeedGrayOLED::setTextXY(unsigned char Row, unsigned char Column)
+{
+    //Column Address
+    sendCommand(0x15);             /* Set Column Address */
+    sendCommand(0x08+(Column*4));  /* Start Column: Start from 8 */
+    sendCommand(0x37);             /* End Column */
+    // Row Address
+    sendCommand(0x75);             /* Set Row Address */
+    sendCommand(0x00+(Row*8));     /* Start Row*/
+    sendCommand(0x07+(Row*8));     /* End Row*/
+}
+
+void SeeedGrayOLED::clearDisplay()
+{
+    unsigned char i,j;
+    for(j=0;j<48;j++)
+    {
+        for(i=0;i<96;i++)  //clear all columns
+        {
+            sendData(0x00);
+        }
+    }
+
+}
+
+void SeeedGrayOLED::sendData(unsigned char Data)
+{
+    int transmissionSuccessful;
+    _device.start();
+    transmissionSuccessful  = _device.write(SeeedGrayOLED_Address << 1);
+    transmissionSuccessful |= _device.write(SeeedGrayOLED_Data_Mode);
+    transmissionSuccessful |= _device.write(Data);
+    _device.stop();
+    // return (transmissionSuccessful);       
+}
+
+void SeeedGrayOLED::setGrayLevel(unsigned char grayLevel)
+{
+    grayH = (grayLevel << 4) & 0xF0;
+    grayL =  grayLevel & 0x0F;
+}
+
+void SeeedGrayOLED::putChar(unsigned char C)
+{
+    if(C < 32 || C > 127) //Ignore non-printable ASCII characters. This can be modified for multilingual font.
+    {
+        C=' '; //Space
+    }   
+
+    
+    for(char i=0;i<8;i=i+2)
+    {
+        for(char j=0;j<8;j++)
+        {
+            // Character is constructed two pixel at a time using vertical mode from the default 8x8 font
+            char c=0x00;
+            char bit1=(BasicFont[C-32][i]   >> j)  & 0x01;  
+            char bit2=(BasicFont[C-32][i+1] >> j)  & 0x01;
+            // Each bit is changed to a nibble
+            c|=(bit1)?grayH:0x00;
+            c|=(bit2)?grayL:0x00;
+            sendData(c);
+        }
+    }
+}
+
+void SeeedGrayOLED::putString(const char *String)
+{
+    unsigned char i=0;
+    while(String[i])
+    {
+        putChar(String[i]);     
+        i++;
+    }
+}
+
+unsigned char SeeedGrayOLED::putNumber(long long_num)
+{
+    unsigned char char_buffer[10]="";
+    unsigned char i = 0;
+    unsigned char f = 0;
+
+    if (long_num < 0)
+    {
+        f=1;
+        putChar('-');
+        long_num = -long_num;
+    }
+    else if (long_num == 0)
+    {
+        f=1;
+        putChar('0');
+        return f;
+    }
+
+    while (long_num > 0)
+    {
+        char_buffer[i++] = long_num % 10;
+        long_num /= 10;
+    }
+
+    f=f+i;
+    for(; i > 0; i--)
+    {
+        putChar('0'+ char_buffer[i - 1]);
+    }
+    return f;
+
+}
+
+
+
+void SeeedGrayOLED::drawBitmap(unsigned char *bitmaparray,int bytes)
+{
+    char localAddressMode = addressingMode;
+    if(addressingMode != HORIZONTAL_MODE)
+    {
+        //Bitmap is drawn in horizontal mode
+        setHorizontalMode();
+    }
+
+    for(int i=0;i<bytes;i++)
+    {
+
+    for(int j=0;j<8;j=j+2) 
+    {
+        char c=0x00;
+        char bit1=bitmaparray[i] <<  j    & 0x80;  
+        char bit2=bitmaparray[i] << (j+1) & 0x80;
+
+        // Each bit is changed to a nibble
+        c|=(bit1)?grayH:0x00;
+        // Each bit is changed to a nibble
+        c|=(bit2)?grayL:0x00;
+        sendData(c);
+     }
+    }
+    if(localAddressMode == VERTICAL_MODE)
+    {
+        //If Vertical Mode was used earlier, restore it.
+        setVerticalMode();
+    }
+
+}
+
+void SeeedGrayOLED::setHorizontalScrollProperties(bool direction,unsigned char startRow, unsigned char endRow,unsigned char startColumn, unsigned char endColumn, unsigned char scrollSpeed)
+{
+    /*
+Use the following defines for 'direction' :
+
+ Scroll_Left            
+ Scroll_Right           
+
+Use the following defines for 'scrollSpeed' :
+
+ Scroll_2Frames     
+ Scroll_3Frames
+ Scroll_4Frames
+ Scroll_5Frames 
+ Scroll_25Frames
+ Scroll_64Frames
+ Scroll_128Frames
+ Scroll_256Frames
+
+*/
+
+    if(Scroll_Right == direction)
+    {
+        //Scroll Right
+        sendCommand(0x27);
+    }
+    else
+    {
+        //Scroll Left  
+        sendCommand(0x26);
+    }
+    sendCommand(0x00);       //Dummmy byte
+    sendCommand(startRow);
+    sendCommand(scrollSpeed);
+    sendCommand(endRow);
+    sendCommand(startColumn+8);
+    sendCommand(endColumn+8);
+    sendCommand(0x00);      //Dummmy byte
+
+}
+
+void SeeedGrayOLED::activateScroll()
+{
+    sendCommand(SeeedGrayOLED_Activate_Scroll_Cmd);
+}
+
+void SeeedGrayOLED::deactivateScroll()
+{
+    sendCommand(SeeedGrayOLED_Dectivate_Scroll_Cmd);
+}
+
+void SeeedGrayOLED::setNormalDisplay()
+{
+    sendCommand(SeeedGrayOLED_Normal_Display_Cmd);
+}
+
+void SeeedGrayOLED::setInverseDisplay()
+{
+    sendCommand(SeeedGrayOLED_Inverse_Display_Cmd);
+}
+
+
diff -r 000000000000 -r e14364aac2ca SeeedGrayOLED.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SeeedGrayOLED.h	Wed Jan 25 18:33:22 2017 +0000
@@ -0,0 +1,117 @@
+/*
+ * SeeedGrayOLED.h
+ * SSD1327 Gray OLED Driver Library for SSD1327
+ *
+ * Copyright (c) 2011 seeed technology inc.
+ * Author        :   Visweswara R
+ * Create Time   :   Dec 2011
+ * Change Log    :
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef SeeedGrayOLED_data_H
+#define SeeedGrayOLED_data_H
+
+// SeeedGrayOLED Instruction set addresses
+
+// #include "Arduino.h"
+#include "mbed.h"
+#include <inttypes.h>
+
+
+#define VERTICAL_MODE                       01
+#define HORIZONTAL_MODE                     02
+
+#define SeeedGrayOLED_Address               0x3c
+#define SeeedGrayOLED_Command_Mode          0x80
+#define SeeedGrayOLED_Data_Mode             0x40
+
+#define SeeedGrayOLED_Display_Off_Cmd       0xAE
+#define SeeedGrayOLED_Display_On_Cmd        0xAF
+
+#define SeeedGrayOLED_Normal_Display_Cmd    0xA4
+#define SeeedGrayOLED_Inverse_Display_Cmd   0xA7
+#define SeeedGrayOLED_Activate_Scroll_Cmd   0x2F
+#define SeeedGrayOLED_Dectivate_Scroll_Cmd  0x2E
+#define SeeedGrayOLED_Set_ContrastLevel_Cmd 0x81
+
+#define Scroll_Left             0x00
+#define Scroll_Right            0x01
+
+#define Scroll_2Frames          0x7
+#define Scroll_3Frames          0x4
+#define Scroll_4Frames          0x5
+#define Scroll_5Frames          0x0
+#define Scroll_25Frames         0x6
+#define Scroll_64Frames         0x1
+#define Scroll_128Frames        0x2
+#define Scroll_256Frames        0x3
+
+
+class SeeedGrayOLED  {
+public:
+
+    /** Create a new TDA7419 communication interface
+    *
+    * @param sda is the pin for I2C SDA
+    * @param scl is the pin for I2C SCL
+    */
+    SeeedGrayOLED(PinName sda, PinName scl);
+
+
+    char addressingMode;
+
+    void init(void);
+
+    void setNormalDisplay();
+    void setInverseDisplay();
+
+    void sendCommand(unsigned char command);
+    void sendData(unsigned char Data);
+    void setGrayLevel(unsigned char grayLevel);
+
+    void setVerticalMode();
+    void setHorizontalMode();
+
+    void setTextXY(unsigned char Row, unsigned char Column);
+    void clearDisplay();
+    void setContrastLevel(unsigned char ContrastLevel);
+    void putChar(unsigned char c);
+    void putString(const char *String);
+    unsigned char putNumber(long n);
+    unsigned char putFloat(float floatNumber,unsigned char decimal);
+    unsigned char putFloat(float floatNumber);
+
+    void drawBitmap(unsigned char *bitmaparray,int bytes);
+
+    void setHorizontalScrollProperties(bool direction,unsigned char startRow, unsigned char endRow,unsigned char startColumn, unsigned char endColumn, unsigned char scrollSpeed);
+    void activateScroll();
+    void deactivateScroll();
+
+private:
+
+    // Signals related to I2C communication
+    I2C _device;
+
+    unsigned char grayH;
+    unsigned char grayL;
+
+
+};
+
+
+#endif
+