/* * geany_encoding=koi8-r * ssd1306.c * * Copyright 2018 Edward V. Emelianov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * */ #include "spi.h" #include "ssd1306.h" //#include "usart.h" #include "proto.h" #include // memset #define RST_PAUSE (10) #define BOOT_PAUSE (100) extern volatile uint32_t Tms; static uint32_t Tloc; static uint8_t curx, cury; typedef enum{ ST_UNINITIALIZED, ST_RSTSTART, ST_RESETED, ST_NEED4UPDATE, ST_IDLE } SSD1306_STATE; static SSD1306_STATE state = ST_UNINITIALIZED; static uint8_t SSD1306_Buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8]; static void ssd1306_WriteCommand(uint8_t byte){ CS_LO(); DC_LO(); spi_write_byte(byte); CS_HI(); } static void ssd1306_WriteData(uint8_t* buffer, uint16_t len){ CS_LO(); DC_HI(); spiWrite(buffer, len); CS_HI(); } // colr == 0 for black or 0xff for white or other values for vertical patterns void ssd1306_Fill(uint8_t colr){ memset(SSD1306_Buffer, colr, sizeof(SSD1306_Buffer)); curx = 0; cury = 0; } // Write ful; screenbuffer to the screen static void ssd1306_UpdateScreen_(){ uint8_t i; for(i = 0; i < SSD1306_HEIGHT/8; i++){ ssd1306_WriteCommand(0xB0 + i); ssd1306_WriteCommand(0x00); ssd1306_WriteCommand(0x10); ssd1306_WriteData(&SSD1306_Buffer[SSD1306_WIDTH*i], SSD1306_WIDTH); } } // change status to need4update, return 1 if can't uint8_t ssd1306_UpdateScreen(){ if(state != ST_IDLE) return 1; state = ST_NEED4UPDATE; return 0; } static void ssd1306_init(){ const uint8_t cmds[] = { 0xAE, // display off 0x20, // Set memory address 0x10, // 0x00: horizontal addressing mode, 0x01: vertical addressing mode // 0x10: Page addressing mode(RESET), 0x11: invalid 0xB0, // Set page start address for page addressing mode: 0 ~ 7 0xC8, // Set COM output scan direction 0x00, // Set low column address 0x10, // Set height column address 0x40, // Set start line address 0x81, // Set contrast control register 0xFF, 0xA1, // Set segment re-map 0 to 127 0xA6, // Set normal display 0xA8, // Set multiplex ratio(1 to 64) 0x3F, 0xA4, // 0xa4: ouput follows RAM content, 0xa5: ouput ignores RAM content 0xD3, // Set display offset 0x00, // Not offset 0xD5, // Set display clock divide ratio/oscillator frequency 0xF0, // Set divide ration 0xD9, // Set pre-charge period 0x22, 0xDA, // Set COM pins hardware configuration 0x12, 0xDB, // Set VCOMH 0x20, // 0x20: 0.77*Vcc 0x8D, // Set DC-DC enable 0x14, 0xAF, // turn on SSD1306panel }; uint8_t idx; for (idx = 0; idx < sizeof(cmds); ++idx){ ssd1306_WriteCommand(cmds[idx]); } } void ssd1306_Reset(){ state = ST_UNINITIALIZED; } void ssd1306_process(){ switch (state){ case ST_UNINITIALIZED: // reset screen //SEND("ST_UNINITIALIZED\n"); state = ST_RSTSTART; Tloc = Tms; // high CS, low RST CS_HI(); RST_LO(); break; case ST_RSTSTART: // reset procedure is over //SEND("ST_RSTSTART\n"); if(Tms - Tloc > RST_PAUSE){ state = ST_RESETED; RST_HI(); Tloc = Tms; } break; case ST_RESETED: // initialize screen //SEND("ST_RESETED\n"); if(Tms - Tloc > BOOT_PAUSE){ ssd1306_init(); state = ST_IDLE; } break; case ST_NEED4UPDATE: //SEND("ST_NEED4UPDATE\n"); ssd1306_UpdateScreen_(); state = ST_IDLE; break; case ST_IDLE: default: break; } ; } /** * Draw one pixel in the screenbuffer * @parameter x, y - coordinate (Y from top to bottom) * @parameter color == 0 for black */ void ssd1306_DrawPixel(uint8_t x, uint8_t y, uint8_t color){ if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) return; // Check if pixel should be inverted if(color){ SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8); }else{ SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8)); } } /** * Draw 1 char to the screen buffer * @parameter ch - given char * @parameter Font - selected font * @parameter color == 0 for black * @return 1 if all OK */ char ssd1306_WriteChar(char ch, FontDef Font, uint8_t color){ uint32_t i, b, j; // Check remaining space on current line if(SSD1306_WIDTH <= (curx + Font.FontWidth)){ if(SSD1306_HEIGHT <= (cury + 2*Font.FontHeight)) return 0; cury += Font.FontHeight; curx = 0; } if(SSD1306_HEIGHT <= (cury + Font.FontHeight)) return 0; if(ch == '\n'){ cury += Font.FontHeight; curx = 0; } if(ch < ' ' || ch > '~') return 0; // wrong symbol // Use the font to write for(i = 0; i < Font.FontHeight; i++) { b = Font.data[(ch - 32) * Font.FontHeight + i]; for(j = 0; j < Font.FontWidth; j++){ if((b << j) & 0x8000) { ssd1306_DrawPixel(curx + j, (cury + i), color); } else { ssd1306_DrawPixel(curx + j, (cury + i), !color); } } } curx += Font.FontWidth; return ch; } // Write full string to screenbuffer char ssd1306_WriteString(char* str, FontDef Font, uint8_t color){ // Write until null-byte while (*str){ if(!ssd1306_WriteChar(*str, Font, color)){ // Char could not be written return *str; } // Next char str++; } // Everything ok - return 0 return 0; } // Position the cursor void ssd1306_SetCursor(uint8_t x, uint8_t y){ if(x >= SSD1306_WIDTH || y > SSD1306_HEIGHT) return; curx = x; cury = y; }