mirror of
https://github.com/eddyem/tsys01.git
synced 2025-12-06 18:45:15 +03:00
234 lines
6.7 KiB
C
234 lines
6.7 KiB
C
/*
|
|
* geany_encoding=koi8-r
|
|
* ssd1306.c
|
|
*
|
|
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
|
*
|
|
* 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 <string.h> // 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;
|
|
}
|