Compare commits
No commits in common. "42eda85fd084bcc650e89453077aec0e448685aa" and "b3bf3d1f04a58f2fb04350dc76c8c9c75ec5d9a7" have entirely different histories.
42eda85fd0
...
b3bf3d1f04
12 changed files with 12 additions and 1217 deletions
11
Makefile
Normal file
11
Makefile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
all : flash
|
||||||
|
|
||||||
|
TARGET:=oled_gol
|
||||||
|
CH32V003FUN:=../ch32v003fun/ch32v003fun
|
||||||
|
|
||||||
|
include ../ch32v003fun/ch32v003fun/ch32v003fun.mk
|
||||||
|
|
||||||
|
flash : build
|
||||||
|
gdb-multiarch -x gdbinit -ex 'load' -ex 'detach' -ex 'quit' $(TARGET).elf
|
||||||
|
clean : cv_clean
|
||||||
|
|
11
gol/Makefile
11
gol/Makefile
|
@ -1,11 +0,0 @@
|
||||||
all : flash
|
|
||||||
|
|
||||||
TARGET:=gol
|
|
||||||
CH32V003FUN:=../../ch32v003fun/ch32v003fun
|
|
||||||
|
|
||||||
include ../../ch32v003fun/ch32v003fun/ch32v003fun.mk
|
|
||||||
|
|
||||||
flash : build
|
|
||||||
gdb-multiarch -x ../gdbinit -ex 'load' -ex 'detach' -ex 'quit' $(TARGET).elf
|
|
||||||
clean : cv_clean
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
all : flash
|
|
||||||
|
|
||||||
TARGET:=soweli
|
|
||||||
CH32V003FUN:=../../ch32v003fun/ch32v003fun
|
|
||||||
|
|
||||||
include ../../ch32v003fun/ch32v003fun/ch32v003fun.mk
|
|
||||||
|
|
||||||
flash : build
|
|
||||||
gdb-multiarch -x ../gdbinit -ex 'load' -ex 'detach' -ex 'quit' $(TARGET).elf
|
|
||||||
clean : cv_clean
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
#define OBSTACLE_TYPES 5
|
|
||||||
|
|
||||||
const unsigned char soweli_a[8] = {
|
|
||||||
0b01111111,
|
|
||||||
0b10000000,
|
|
||||||
0b10101000,
|
|
||||||
0b10101000,
|
|
||||||
0b10000000,
|
|
||||||
0b01010101,
|
|
||||||
0b01010000,
|
|
||||||
0b01010000,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char soweli_b[8] = {
|
|
||||||
0b01111111,
|
|
||||||
0b10000000,
|
|
||||||
0b10101000,
|
|
||||||
0b10101000,
|
|
||||||
0b10000000,
|
|
||||||
0b01010101,
|
|
||||||
0b00000101,
|
|
||||||
0b00000101,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char obstacle_sprites[OBSTACLE_TYPES][8] = {
|
|
||||||
{
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b01010101,
|
|
||||||
0b01010100,
|
|
||||||
0b11111110,
|
|
||||||
0b01010100,
|
|
||||||
0b01010101,
|
|
||||||
0b00000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
|
|
||||||
0b00111100,
|
|
||||||
0b01100110,
|
|
||||||
0b11000011,
|
|
||||||
0b10000001,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b11111111,
|
|
||||||
0b01000010,
|
|
||||||
0b01000010,
|
|
||||||
0b01000010,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b01000010,
|
|
||||||
0b00011000,
|
|
||||||
0b00100100,
|
|
||||||
0b01111110,
|
|
||||||
0b00100100,
|
|
||||||
0b01111110,
|
|
||||||
0b00100100,
|
|
||||||
0b00011000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b00000000,
|
|
||||||
0b00011000,
|
|
||||||
0b00011000,
|
|
||||||
0b00011000,
|
|
||||||
0b00011000,
|
|
||||||
0b00111100,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char obstacle_heights[OBSTACLE_TYPES] = {6, 4, 4, 8, 5};
|
|
||||||
const unsigned char obstacle_widths[OBSTACLE_TYPES] = {8, 8, 8, 7, 5};
|
|
|
@ -1,7 +0,0 @@
|
||||||
#ifndef _FUNCONFIG_H
|
|
||||||
#define _FUNCONFIG_H
|
|
||||||
|
|
||||||
#define CH32V003 1
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
255
soweli/soweli.c
255
soweli/soweli.c
|
@ -1,255 +0,0 @@
|
||||||
|
|
||||||
#define SSD1306_128X32
|
|
||||||
#include "ch32v003fun.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "ssd1306_i2c.h"
|
|
||||||
#include "ssd1306.h"
|
|
||||||
|
|
||||||
#include "data.h"
|
|
||||||
|
|
||||||
#define PIN_LED PC4
|
|
||||||
#define PIN_BTN PC7
|
|
||||||
|
|
||||||
// #define JUMP_POWER 48
|
|
||||||
// #define GRAVITY 3
|
|
||||||
#define JUMP_POWER 36
|
|
||||||
#define GRAVITY 2
|
|
||||||
#define SCROLL_SPEED 1
|
|
||||||
#define FRAME_DELAY 1000 / 60
|
|
||||||
#define SCORE_DIGITS 4
|
|
||||||
#define RUN_ANIM_SPEED 4
|
|
||||||
#define PLAYER_X 16
|
|
||||||
#define Y_PRECISION 4
|
|
||||||
|
|
||||||
u32 time = 0;
|
|
||||||
u16 score = 0;
|
|
||||||
u16 highscore = 0;
|
|
||||||
s16 player_y = 0;
|
|
||||||
s16 player_vel = 0;
|
|
||||||
#define MAX_OBSTACLES 16
|
|
||||||
u8 obstacle_positions[MAX_OBSTACLES];
|
|
||||||
u8 obstacle_types[MAX_OBSTACLES]; // todo
|
|
||||||
u8 obstacle_count = 0;
|
|
||||||
u8 is_running = 1;
|
|
||||||
u8 time_to_obstacle = 0;
|
|
||||||
|
|
||||||
uint32_t lfsr = 1; // PRNG state
|
|
||||||
|
|
||||||
u8 rand8();
|
|
||||||
void update();
|
|
||||||
void render();
|
|
||||||
void restart();
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
SystemInit();
|
|
||||||
funGpioInitAll();
|
|
||||||
funPinMode(PIN_LED, GPIO_CFGLR_OUT_10Mhz_PP);
|
|
||||||
funPinMode(PIN_BTN, GPIO_CFGLR_IN_PUPD);
|
|
||||||
|
|
||||||
ssd1306_i2c_init();
|
|
||||||
ssd1306_init();
|
|
||||||
|
|
||||||
restart();
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
ssd1306_refresh();
|
|
||||||
|
|
||||||
if (is_running)
|
|
||||||
update();
|
|
||||||
render();
|
|
||||||
time += 1;
|
|
||||||
|
|
||||||
if (funDigitalRead(PIN_BTN))
|
|
||||||
{
|
|
||||||
if (is_running)
|
|
||||||
{
|
|
||||||
if (player_y == 0 && player_vel == 0)
|
|
||||||
{
|
|
||||||
player_vel = JUMP_POWER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
restart();
|
|
||||||
}
|
|
||||||
if (is_running == 0)
|
|
||||||
{
|
|
||||||
funDigitalWrite(PIN_LED, (time >> 2) & 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
funDigitalWrite(PIN_LED, FUN_LOW);
|
|
||||||
}
|
|
||||||
Delay_Ms(FRAME_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void restart()
|
|
||||||
{
|
|
||||||
lfsr = SysTick->CNT;
|
|
||||||
is_running = 1;
|
|
||||||
player_vel = 0;
|
|
||||||
player_y = 0;
|
|
||||||
obstacle_count = 0;
|
|
||||||
time_to_obstacle = 0;
|
|
||||||
if (highscore < score)
|
|
||||||
highscore = score;
|
|
||||||
score = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_obstacle()
|
|
||||||
{
|
|
||||||
if (obstacle_count == 0)
|
|
||||||
return;
|
|
||||||
for (u8 i = 1; i < obstacle_count; i++)
|
|
||||||
{
|
|
||||||
obstacle_positions[i - 1] = obstacle_positions[i];
|
|
||||||
obstacle_types[i - 1] = obstacle_types[i];
|
|
||||||
}
|
|
||||||
obstacle_count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void spawn_obstacle(u8 pos)
|
|
||||||
{
|
|
||||||
if (obstacle_count >= MAX_OBSTACLES)
|
|
||||||
return;
|
|
||||||
obstacle_positions[obstacle_count] = pos;
|
|
||||||
obstacle_types[obstacle_count] = rand8() % OBSTACLE_TYPES;
|
|
||||||
obstacle_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update()
|
|
||||||
{
|
|
||||||
score += (time & 4) != 0;
|
|
||||||
if (player_vel != 0 || player_y != 0)
|
|
||||||
{
|
|
||||||
player_vel -= GRAVITY;
|
|
||||||
player_y += player_vel;
|
|
||||||
if (player_y < 0)
|
|
||||||
{
|
|
||||||
player_y = 0;
|
|
||||||
player_vel = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (u8 i = 0; i < obstacle_count; i++)
|
|
||||||
{
|
|
||||||
obstacle_positions[i] -= SCROLL_SPEED;
|
|
||||||
u8 type = obstacle_types[i];
|
|
||||||
u8 dist_x = gfx_abs((s16)obstacle_positions[i] - (s16)PLAYER_X);
|
|
||||||
if (dist_x < obstacle_widths[type] && (player_y >> Y_PRECISION) < obstacle_heights[type])
|
|
||||||
{
|
|
||||||
is_running = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (obstacle_count > 0 && obstacle_positions[0] == 0)
|
|
||||||
{
|
|
||||||
remove_obstacle();
|
|
||||||
}
|
|
||||||
if (time_to_obstacle < 5)
|
|
||||||
{
|
|
||||||
u8 count = 1 + (rand8() & rand8() & 1) + (rand8() & rand8() & rand8() & rand8() & 1);
|
|
||||||
for (u8 i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
spawn_obstacle(128 + i * 8);
|
|
||||||
}
|
|
||||||
time_to_obstacle = (rand8() & 31) + 50;
|
|
||||||
}
|
|
||||||
time_to_obstacle -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_digit(u8 digit, u8 x, u8 y)
|
|
||||||
{
|
|
||||||
const u16 digit_font[] = {
|
|
||||||
0b111101101101111, // 0
|
|
||||||
0b010110010010111, // 1
|
|
||||||
0b111001111100111, // 2
|
|
||||||
0b111001111001111, // 3
|
|
||||||
0b101101111001001, // 4
|
|
||||||
0b111100111001111, // 5
|
|
||||||
0b111100111101111, // 6
|
|
||||||
0b111001001001001, // 7
|
|
||||||
0b111101111101111, // 8
|
|
||||||
0b111101111001111, // 9
|
|
||||||
};
|
|
||||||
u16 pixels = digit_font[digit];
|
|
||||||
|
|
||||||
ssd1306_drawPixel(x + 0, y + 0, (pixels >> 14) & 1);
|
|
||||||
ssd1306_drawPixel(x + 1, y + 0, (pixels >> 13) & 1);
|
|
||||||
ssd1306_drawPixel(x + 2, y + 0, (pixels >> 12) & 1);
|
|
||||||
ssd1306_drawPixel(x + 0, y + 1, (pixels >> 11) & 1);
|
|
||||||
ssd1306_drawPixel(x + 1, y + 1, (pixels >> 10) & 1);
|
|
||||||
ssd1306_drawPixel(x + 2, y + 1, (pixels >> 9) & 1);
|
|
||||||
ssd1306_drawPixel(x + 0, y + 2, (pixels >> 8) & 1);
|
|
||||||
ssd1306_drawPixel(x + 1, y + 2, (pixels >> 7) & 1);
|
|
||||||
ssd1306_drawPixel(x + 2, y + 2, (pixels >> 6) & 1);
|
|
||||||
ssd1306_drawPixel(x + 0, y + 3, (pixels >> 5) & 1);
|
|
||||||
ssd1306_drawPixel(x + 1, y + 3, (pixels >> 4) & 1);
|
|
||||||
ssd1306_drawPixel(x + 2, y + 3, (pixels >> 3) & 1);
|
|
||||||
ssd1306_drawPixel(x + 0, y + 4, (pixels >> 2) & 1);
|
|
||||||
ssd1306_drawPixel(x + 1, y + 4, (pixels >> 1) & 1);
|
|
||||||
ssd1306_drawPixel(x + 2, y + 4, (pixels >> 0) & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render_number(u32 s, u8 x, u8 y, u8 digits)
|
|
||||||
{
|
|
||||||
u8 painter_x = x + digits * 4;
|
|
||||||
u32 val = s;
|
|
||||||
|
|
||||||
for (int i = 0; i < digits; i++)
|
|
||||||
{
|
|
||||||
u8 digit = val % 10;
|
|
||||||
render_digit(digit, painter_x, y);
|
|
||||||
val /= 10;
|
|
||||||
painter_x -= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void render()
|
|
||||||
{
|
|
||||||
ssd1306_setbuf(0);
|
|
||||||
ssd1306_drawFastHLine(0, 31, 128, 1);
|
|
||||||
|
|
||||||
render_number(score, 0, 0, SCORE_DIGITS);
|
|
||||||
render_number(highscore, 0, 6, SCORE_DIGITS);
|
|
||||||
// render_number(obstacle_count, 20, 0, 2);
|
|
||||||
// render_number(abs(player_vel), 30, 0, 2);
|
|
||||||
// render_number(time_to_obstacle, 20, 6, 3);
|
|
||||||
|
|
||||||
for (u8 i = 0; i < obstacle_count; i++)
|
|
||||||
{
|
|
||||||
u8 type = obstacle_types[i];
|
|
||||||
ssd1306_drawImage(obstacle_positions[i], 31 - 8, obstacle_sprites[type], 8, 8, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (time & RUN_ANIM_SPEED && is_running)
|
|
||||||
ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_a, 8, 8, 0);
|
|
||||||
else
|
|
||||||
ssd1306_drawImage(PLAYER_X, 31 - (player_y >> Y_PRECISION) - 8, soweli_b, 8, 8, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* White Noise Generator State */
|
|
||||||
#define NOISE_BITS 8
|
|
||||||
#define NOISE_MASK ((1 << NOISE_BITS) - 1)
|
|
||||||
#define NOISE_POLY_TAP0 31
|
|
||||||
#define NOISE_POLY_TAP1 21
|
|
||||||
#define NOISE_POLY_TAP2 1
|
|
||||||
#define NOISE_POLY_TAP3 0
|
|
||||||
/*
|
|
||||||
* random byte generator, taken from ch32v003fun examples
|
|
||||||
*/
|
|
||||||
uint8_t rand8(void)
|
|
||||||
{
|
|
||||||
uint8_t bit;
|
|
||||||
uint32_t new_data;
|
|
||||||
|
|
||||||
for (bit = 0; bit < NOISE_BITS; bit++)
|
|
||||||
{
|
|
||||||
new_data = ((lfsr >> NOISE_POLY_TAP0) ^
|
|
||||||
(lfsr >> NOISE_POLY_TAP1) ^
|
|
||||||
(lfsr >> NOISE_POLY_TAP2) ^
|
|
||||||
(lfsr >> NOISE_POLY_TAP3));
|
|
||||||
lfsr = (lfsr << 1) | (new_data & 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lfsr & NOISE_MASK;
|
|
||||||
}
|
|
489
soweli/ssd1306.h
489
soweli/ssd1306.h
|
@ -1,489 +0,0 @@
|
||||||
/*
|
|
||||||
* Single-File-Header for using SPI OLED
|
|
||||||
* 05-05-2023 E. Brombaugh
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SSD1306_H
|
|
||||||
#define _SSD1306_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// comfortable packet size for this OLED
|
|
||||||
#define SSD1306_PSZ 32
|
|
||||||
|
|
||||||
// characteristics of each type
|
|
||||||
#if !defined(SSD1306_64X32) && !defined(SSD1306_128X32) && !defined(SSD1306_128X64)
|
|
||||||
#error "Please define the SSD1306_WXH resolution used in your application"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SSD1306_64X32
|
|
||||||
#define SSD1306_W 64
|
|
||||||
#define SSD1306_H 32
|
|
||||||
#define SSD1306_FULLUSE
|
|
||||||
#define SSD1306_OFFSET 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SSD1306_128X32
|
|
||||||
#define SSD1306_W 128
|
|
||||||
#define SSD1306_H 32
|
|
||||||
#define SSD1306_OFFSET 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SSD1306_128X64
|
|
||||||
#define SSD1306_W 128
|
|
||||||
#define SSD1306_H 64
|
|
||||||
#define SSD1306_FULLUSE
|
|
||||||
#define SSD1306_OFFSET 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* send OLED command byte
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_cmd(uint8_t cmd)
|
|
||||||
{
|
|
||||||
ssd1306_pkt_send(&cmd, 1, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* send OLED data packet (up to 32 bytes)
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_data(uint8_t *data, uint8_t sz)
|
|
||||||
{
|
|
||||||
ssd1306_pkt_send(data, sz, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SSD1306_SETCONTRAST 0x81
|
|
||||||
#define SSD1306_SEGREMAP 0xA0
|
|
||||||
#define SSD1306_DISPLAYALLON_RESUME 0xA4
|
|
||||||
#define SSD1306_DISPLAYALLON 0xA5
|
|
||||||
#define SSD1306_NORMALDISPLAY 0xA6
|
|
||||||
#define SSD1306_INVERTDISPLAY 0xA7
|
|
||||||
#define SSD1306_DISPLAYOFF 0xAE
|
|
||||||
#define SSD1306_DISPLAYON 0xAF
|
|
||||||
#define SSD1306_SETDISPLAYOFFSET 0xD3
|
|
||||||
#define SSD1306_SETCOMPINS 0xDA
|
|
||||||
#define SSD1306_SETVCOMDETECT 0xDB
|
|
||||||
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
|
|
||||||
#define SSD1306_SETPRECHARGE 0xD9
|
|
||||||
#define SSD1306_SETMULTIPLEX 0xA8
|
|
||||||
#define SSD1306_SETLOWCOLUMN 0x00
|
|
||||||
#define SSD1306_SETHIGHCOLUMN 0x10
|
|
||||||
#define SSD1306_SETSTARTLINE 0x40
|
|
||||||
#define SSD1306_MEMORYMODE 0x20
|
|
||||||
#define SSD1306_COLUMNADDR 0x21
|
|
||||||
#define SSD1306_PAGEADDR 0x22
|
|
||||||
#define SSD1306_COMSCANINC 0xC0
|
|
||||||
#define SSD1306_COMSCANDEC 0xC8
|
|
||||||
#define SSD1306_CHARGEPUMP 0x8D
|
|
||||||
#define SSD1306_EXTERNALVCC 0x1
|
|
||||||
#define SSD1306_SWITCHCAPVCC 0x2
|
|
||||||
#define SSD1306_TERMINATE_CMDS 0xFF
|
|
||||||
|
|
||||||
/* choose VCC mode */
|
|
||||||
#define SSD1306_EXTERNALVCC 0x1
|
|
||||||
#define SSD1306_SWITCHCAPVCC 0x2
|
|
||||||
// #define vccstate SSD1306_EXTERNALVCC
|
|
||||||
#define vccstate SSD1306_SWITCHCAPVCC
|
|
||||||
|
|
||||||
// OLED initialization commands for 128x32
|
|
||||||
const uint8_t ssd1306_init_array[] =
|
|
||||||
{
|
|
||||||
SSD1306_DISPLAYOFF, // 0xAE
|
|
||||||
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
|
|
||||||
0x80, // the suggested ratio 0x80
|
|
||||||
SSD1306_SETMULTIPLEX, // 0xA8
|
|
||||||
#ifdef SSD1306_64X32
|
|
||||||
0x1F, // for 64-wide displays
|
|
||||||
#else
|
|
||||||
0x3F, // for 128-wide displays
|
|
||||||
#endif
|
|
||||||
SSD1306_SETDISPLAYOFFSET, // 0xD3
|
|
||||||
0x00, // no offset
|
|
||||||
SSD1306_SETSTARTLINE | 0x0, // 0x40 | line
|
|
||||||
SSD1306_CHARGEPUMP, // 0x8D
|
|
||||||
0x14, // enable?
|
|
||||||
SSD1306_MEMORYMODE, // 0x20
|
|
||||||
0x00, // 0x0 act like ks0108
|
|
||||||
SSD1306_SEGREMAP | 0x1, // 0xA0 | bit
|
|
||||||
SSD1306_COMSCANDEC,
|
|
||||||
SSD1306_SETCOMPINS, // 0xDA
|
|
||||||
0x12, //
|
|
||||||
SSD1306_SETCONTRAST, // 0x81
|
|
||||||
0x8F,
|
|
||||||
SSD1306_SETPRECHARGE, // 0xd9
|
|
||||||
0xF1,
|
|
||||||
SSD1306_SETVCOMDETECT, // 0xDB
|
|
||||||
0x40,
|
|
||||||
SSD1306_DISPLAYALLON_RESUME, // 0xA4
|
|
||||||
SSD1306_NORMALDISPLAY, // 0xA6
|
|
||||||
SSD1306_DISPLAYON, // 0xAF --turn on oled panel
|
|
||||||
SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end
|
|
||||||
};
|
|
||||||
|
|
||||||
// the display buffer
|
|
||||||
uint8_t ssd1306_buffer[SSD1306_W * SSD1306_H / 8];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* set the buffer to a color
|
|
||||||
*/
|
|
||||||
void ssd1306_setbuf(uint8_t color)
|
|
||||||
{
|
|
||||||
memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef SSD1306_FULLUSE
|
|
||||||
/*
|
|
||||||
* expansion array for OLED with every other row unused
|
|
||||||
*/
|
|
||||||
const uint8_t expand[16] =
|
|
||||||
{
|
|
||||||
0x00,
|
|
||||||
0x02,
|
|
||||||
0x08,
|
|
||||||
0x0a,
|
|
||||||
0x20,
|
|
||||||
0x22,
|
|
||||||
0x28,
|
|
||||||
0x2a,
|
|
||||||
0x80,
|
|
||||||
0x82,
|
|
||||||
0x88,
|
|
||||||
0x8a,
|
|
||||||
0xa0,
|
|
||||||
0xa2,
|
|
||||||
0xa8,
|
|
||||||
0xaa,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send the frame buffer
|
|
||||||
*/
|
|
||||||
void ssd1306_refresh(void)
|
|
||||||
{
|
|
||||||
uint16_t i;
|
|
||||||
|
|
||||||
ssd1306_cmd(SSD1306_COLUMNADDR);
|
|
||||||
ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset)
|
|
||||||
ssd1306_cmd(SSD1306_OFFSET + SSD1306_W - 1); // Column end address (127 = reset)
|
|
||||||
|
|
||||||
ssd1306_cmd(SSD1306_PAGEADDR);
|
|
||||||
ssd1306_cmd(0); // Page start address (0 = reset)
|
|
||||||
ssd1306_cmd(7); // Page end address
|
|
||||||
|
|
||||||
#ifdef SSD1306_FULLUSE
|
|
||||||
/* for fully used rows just plow thru everything */
|
|
||||||
for (i = 0; i < sizeof(ssd1306_buffer); i += SSD1306_PSZ)
|
|
||||||
{
|
|
||||||
/* send PSZ block of data */
|
|
||||||
ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* for displays with odd rows unused expand bytes */
|
|
||||||
uint8_t tbuf[SSD1306_PSZ], j, k;
|
|
||||||
for (i = 0; i < sizeof(ssd1306_buffer); i += 128) // for each page
|
|
||||||
{
|
|
||||||
/* low nybble */
|
|
||||||
for (j = 0; j < 128; j += SSD1306_PSZ)
|
|
||||||
{
|
|
||||||
for (k = 0; k < SSD1306_PSZ; k++)
|
|
||||||
tbuf[k] = expand[ssd1306_buffer[i + j + k] & 0xf];
|
|
||||||
|
|
||||||
/* send PSZ block of data */
|
|
||||||
ssd1306_data(tbuf, SSD1306_PSZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* high nybble */
|
|
||||||
for (j = 0; j < 128; j += SSD1306_PSZ)
|
|
||||||
{
|
|
||||||
for (k = 0; k < SSD1306_PSZ; k++)
|
|
||||||
tbuf[k] = expand[(ssd1306_buffer[i + j + k] >> 4) & 0xf];
|
|
||||||
|
|
||||||
/* send PSZ block of data */
|
|
||||||
ssd1306_data(tbuf, SSD1306_PSZ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* plot a pixel in the buffer
|
|
||||||
*/
|
|
||||||
void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color)
|
|
||||||
{
|
|
||||||
uint16_t addr;
|
|
||||||
|
|
||||||
/* clip */
|
|
||||||
if (x >= SSD1306_W)
|
|
||||||
return;
|
|
||||||
if (y >= SSD1306_H)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* compute buffer address */
|
|
||||||
addr = x + SSD1306_W * (y / 8);
|
|
||||||
|
|
||||||
/* set/clear bit in buffer */
|
|
||||||
if (color)
|
|
||||||
ssd1306_buffer[addr] |= (1 << (y & 7));
|
|
||||||
else
|
|
||||||
ssd1306_buffer[addr] &= ~(1 << (y & 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fast vert line
|
|
||||||
*/
|
|
||||||
void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color)
|
|
||||||
{
|
|
||||||
// clipping
|
|
||||||
if ((x >= SSD1306_W) || (y >= SSD1306_H))
|
|
||||||
return;
|
|
||||||
if ((y + h - 1) >= SSD1306_H)
|
|
||||||
h = SSD1306_H - y;
|
|
||||||
while (h--)
|
|
||||||
{
|
|
||||||
ssd1306_drawPixel(x, y++, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fast horiz line
|
|
||||||
*/
|
|
||||||
void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color)
|
|
||||||
{
|
|
||||||
// clipping
|
|
||||||
if ((x >= SSD1306_W) || (y >= SSD1306_H))
|
|
||||||
return;
|
|
||||||
if ((x + w - 1) >= SSD1306_W)
|
|
||||||
w = SSD1306_W - x;
|
|
||||||
|
|
||||||
while (w--)
|
|
||||||
{
|
|
||||||
ssd1306_drawPixel(x++, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* abs() helper function for line drawing
|
|
||||||
*/
|
|
||||||
int16_t gfx_abs(int16_t x)
|
|
||||||
{
|
|
||||||
return (x < 0) ? -x : x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* swap() helper function for line drawing
|
|
||||||
*/
|
|
||||||
void gfx_swap(uint16_t *z0, uint16_t *z1)
|
|
||||||
{
|
|
||||||
uint16_t temp = *z0;
|
|
||||||
*z0 = *z1;
|
|
||||||
*z1 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bresenham line draw routine swiped from Wikipedia
|
|
||||||
*/
|
|
||||||
void ssd1306_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color)
|
|
||||||
{
|
|
||||||
int16_t steep;
|
|
||||||
int16_t deltax, deltay, error, ystep, x, y;
|
|
||||||
|
|
||||||
/* flip sense 45deg to keep error calc in range */
|
|
||||||
steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0));
|
|
||||||
|
|
||||||
if (steep)
|
|
||||||
{
|
|
||||||
gfx_swap(&x0, &y0);
|
|
||||||
gfx_swap(&x1, &y1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* run low->high */
|
|
||||||
if (x0 > x1)
|
|
||||||
{
|
|
||||||
gfx_swap(&x0, &x1);
|
|
||||||
gfx_swap(&y0, &y1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set up loop initial conditions */
|
|
||||||
deltax = x1 - x0;
|
|
||||||
deltay = gfx_abs(y1 - y0);
|
|
||||||
error = deltax / 2;
|
|
||||||
y = y0;
|
|
||||||
if (y0 < y1)
|
|
||||||
ystep = 1;
|
|
||||||
else
|
|
||||||
ystep = -1;
|
|
||||||
|
|
||||||
/* loop x */
|
|
||||||
for (x = x0; x <= x1; x++)
|
|
||||||
{
|
|
||||||
/* plot point */
|
|
||||||
if (steep)
|
|
||||||
/* flip point & plot */
|
|
||||||
ssd1306_drawPixel(y, x, color);
|
|
||||||
else
|
|
||||||
/* just plot */
|
|
||||||
ssd1306_drawPixel(x, y, color);
|
|
||||||
|
|
||||||
/* update error */
|
|
||||||
error = error - deltay;
|
|
||||||
|
|
||||||
/* update y */
|
|
||||||
if (error < 0)
|
|
||||||
{
|
|
||||||
y = y + ystep;
|
|
||||||
error = error + deltax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* draws a circle
|
|
||||||
*/
|
|
||||||
void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
|
|
||||||
{
|
|
||||||
/* Bresenham algorithm */
|
|
||||||
int16_t x_pos = -radius;
|
|
||||||
int16_t y_pos = 0;
|
|
||||||
int16_t err = 2 - 2 * radius;
|
|
||||||
int16_t e2;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ssd1306_drawPixel(x - x_pos, y + y_pos, color);
|
|
||||||
ssd1306_drawPixel(x + x_pos, y + y_pos, color);
|
|
||||||
ssd1306_drawPixel(x + x_pos, y - y_pos, color);
|
|
||||||
ssd1306_drawPixel(x - x_pos, y - y_pos, color);
|
|
||||||
e2 = err;
|
|
||||||
if (e2 <= y_pos)
|
|
||||||
{
|
|
||||||
err += ++y_pos * 2 + 1;
|
|
||||||
if (-x_pos == y_pos && e2 <= x_pos)
|
|
||||||
{
|
|
||||||
e2 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e2 > x_pos)
|
|
||||||
{
|
|
||||||
err += ++x_pos * 2 + 1;
|
|
||||||
}
|
|
||||||
} while (x_pos <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* draws a filled circle
|
|
||||||
*/
|
|
||||||
void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color)
|
|
||||||
{
|
|
||||||
/* Bresenham algorithm */
|
|
||||||
int16_t x_pos = -radius;
|
|
||||||
int16_t y_pos = 0;
|
|
||||||
int16_t err = 2 - 2 * radius;
|
|
||||||
int16_t e2;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ssd1306_drawPixel(x - x_pos, y + y_pos, color);
|
|
||||||
ssd1306_drawPixel(x + x_pos, y + y_pos, color);
|
|
||||||
ssd1306_drawPixel(x + x_pos, y - y_pos, color);
|
|
||||||
ssd1306_drawPixel(x - x_pos, y - y_pos, color);
|
|
||||||
ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color);
|
|
||||||
ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color);
|
|
||||||
e2 = err;
|
|
||||||
if (e2 <= y_pos)
|
|
||||||
{
|
|
||||||
err += ++y_pos * 2 + 1;
|
|
||||||
if (-x_pos == y_pos && e2 <= x_pos)
|
|
||||||
{
|
|
||||||
e2 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e2 > x_pos)
|
|
||||||
{
|
|
||||||
err += ++x_pos * 2 + 1;
|
|
||||||
}
|
|
||||||
} while (x_pos <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* draw a rectangle
|
|
||||||
*/
|
|
||||||
void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
|
|
||||||
{
|
|
||||||
ssd1306_drawFastVLine(x, y, h, color);
|
|
||||||
ssd1306_drawFastVLine(x + w - 1, y, h, color);
|
|
||||||
ssd1306_drawFastHLine(x, y, w, color);
|
|
||||||
ssd1306_drawFastHLine(x, y + h - 1, w, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* fill a rectangle
|
|
||||||
*/
|
|
||||||
void ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
|
|
||||||
{
|
|
||||||
uint8_t m, n = y, iw = w;
|
|
||||||
|
|
||||||
/* scan vertical */
|
|
||||||
while (h--)
|
|
||||||
{
|
|
||||||
m = x;
|
|
||||||
w = iw;
|
|
||||||
/* scan horizontal */
|
|
||||||
while (w--)
|
|
||||||
{
|
|
||||||
/* invert pixels */
|
|
||||||
ssd1306_drawPixel(m++, n, color);
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* invert a rectangle in the buffer
|
|
||||||
*/
|
|
||||||
void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
|
|
||||||
{
|
|
||||||
uint8_t m, n = y, iw = w;
|
|
||||||
|
|
||||||
/* scan vertical */
|
|
||||||
while (h--)
|
|
||||||
{
|
|
||||||
m = x;
|
|
||||||
w = iw;
|
|
||||||
/* scan horizontal */
|
|
||||||
while (w--)
|
|
||||||
{
|
|
||||||
/* invert pixels */
|
|
||||||
ssd1306_xorPixel(m++, n);
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* initialize I2C and OLED
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_init(void)
|
|
||||||
{
|
|
||||||
// pulse reset
|
|
||||||
ssd1306_rst();
|
|
||||||
|
|
||||||
// initialize OLED
|
|
||||||
uint8_t *cmd_list = (uint8_t *)ssd1306_init_array;
|
|
||||||
while (*cmd_list != SSD1306_TERMINATE_CMDS)
|
|
||||||
{
|
|
||||||
if (ssd1306_cmd(*cmd_list++))
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear display
|
|
||||||
ssd1306_setbuf(0);
|
|
||||||
ssd1306_refresh();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,363 +0,0 @@
|
||||||
/*
|
|
||||||
* Single-File-Header for SSD1306 I2C interface
|
|
||||||
* 05-07-2023 E. Brombaugh
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SSD1306_I2C_H
|
|
||||||
#define _SSD1306_I2C_H
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// SSD1306 I2C address
|
|
||||||
#define SSD1306_I2C_ADDR 0x3c
|
|
||||||
|
|
||||||
// I2C Bus clock rate - must be lower the Logic clock rate
|
|
||||||
#define SSD1306_I2C_CLKRATE 1000000
|
|
||||||
|
|
||||||
// I2C Logic clock rate - must be higher than Bus clock rate
|
|
||||||
#define SSD1306_I2C_PRERATE 2000000
|
|
||||||
|
|
||||||
// uncomment this for high-speed 36% duty cycle, otherwise 33%
|
|
||||||
#define SSD1306_I2C_DUTY
|
|
||||||
|
|
||||||
// I2C Timeout count
|
|
||||||
#define TIMEOUT_MAX 100000
|
|
||||||
|
|
||||||
// uncomment this to enable IRQ-driven operation
|
|
||||||
//#define SSD1306_I2C_IRQ
|
|
||||||
|
|
||||||
#ifdef SSD1306_I2C_IRQ
|
|
||||||
// some stuff that IRQ mode needs
|
|
||||||
volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c_send_sz, ssd1306_i2c_irq_state;
|
|
||||||
|
|
||||||
// uncomment this to enable time diags in IRQ
|
|
||||||
//#define IRQ_DIAG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* init just I2C
|
|
||||||
*/
|
|
||||||
void ssd1306_i2c_setup(void)
|
|
||||||
{
|
|
||||||
uint16_t tempreg;
|
|
||||||
|
|
||||||
// Reset I2C1 to init all regs
|
|
||||||
RCC->APB1PRSTR |= RCC_APB1Periph_I2C1;
|
|
||||||
RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1;
|
|
||||||
|
|
||||||
// set freq
|
|
||||||
tempreg = I2C1->CTLR2;
|
|
||||||
tempreg &= ~I2C_CTLR2_FREQ;
|
|
||||||
tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK/SSD1306_I2C_PRERATE)&I2C_CTLR2_FREQ;
|
|
||||||
I2C1->CTLR2 = tempreg;
|
|
||||||
|
|
||||||
// Set clock config
|
|
||||||
tempreg = 0;
|
|
||||||
#if (SSD1306_I2C_CLKRATE <= 100000)
|
|
||||||
// standard mode good to 100kHz
|
|
||||||
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(2*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR;
|
|
||||||
#else
|
|
||||||
// fast mode over 100kHz
|
|
||||||
#ifndef SSD1306_I2C_DUTY
|
|
||||||
// 33% duty cycle
|
|
||||||
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(3*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR;
|
|
||||||
#else
|
|
||||||
// 36% duty cycle
|
|
||||||
tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(25*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR;
|
|
||||||
tempreg |= I2C_CKCFGR_DUTY;
|
|
||||||
#endif
|
|
||||||
tempreg |= I2C_CKCFGR_FS;
|
|
||||||
#endif
|
|
||||||
I2C1->CKCFGR = tempreg;
|
|
||||||
|
|
||||||
#ifdef SSD1306_I2C_IRQ
|
|
||||||
// enable IRQ driven operation
|
|
||||||
NVIC_EnableIRQ(I2C1_EV_IRQn);
|
|
||||||
|
|
||||||
// initialize the state
|
|
||||||
ssd1306_i2c_irq_state = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Enable I2C
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_PE;
|
|
||||||
|
|
||||||
// set ACK mode
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_ACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* error descriptions
|
|
||||||
*/
|
|
||||||
char *errstr[] =
|
|
||||||
{
|
|
||||||
"not busy",
|
|
||||||
"master mode",
|
|
||||||
"transmit mode",
|
|
||||||
"tx empty",
|
|
||||||
"transmit complete",
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* error handler
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_error(uint8_t err)
|
|
||||||
{
|
|
||||||
// report error
|
|
||||||
printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]);
|
|
||||||
|
|
||||||
// reset & initialize I2C
|
|
||||||
ssd1306_i2c_setup();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// event codes we use
|
|
||||||
#define SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
|
|
||||||
#define SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */
|
|
||||||
#define SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check for 32-bit event codes
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask)
|
|
||||||
{
|
|
||||||
/* read order matters here! STAR1 before STAR2!! */
|
|
||||||
uint32_t status = I2C1->STAR1 | (I2C1->STAR2<<16);
|
|
||||||
return (status & event_mask) == event_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SSD1306_I2C_IRQ
|
|
||||||
/*
|
|
||||||
* packet send for IRQ-driven operation
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
|
|
||||||
{
|
|
||||||
int32_t timeout;
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(3));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// error out if buffer under/overflow
|
|
||||||
if((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
// wait for previous packet to finish
|
|
||||||
while(ssd1306_i2c_irq_state);
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(16+3));
|
|
||||||
GPIOC->BSHR = (1<<(4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// init buffer for sending
|
|
||||||
ssd1306_i2c_send_sz = sz;
|
|
||||||
ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer;
|
|
||||||
memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz);
|
|
||||||
|
|
||||||
// wait for not busy
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(0);
|
|
||||||
|
|
||||||
// Set START condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_START;
|
|
||||||
|
|
||||||
// wait for master mode select
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(1);
|
|
||||||
|
|
||||||
// send 7-bit address + write flag
|
|
||||||
I2C1->DATAR = addr<<1;
|
|
||||||
|
|
||||||
// wait for transmit condition
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(2);
|
|
||||||
|
|
||||||
// Enable TXE interrupt
|
|
||||||
I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN;
|
|
||||||
ssd1306_i2c_irq_state = 1;
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(16+4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// exit
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IRQ handler for I2C events
|
|
||||||
*/
|
|
||||||
void I2C1_EV_IRQHandler(void) __attribute__((interrupt));
|
|
||||||
void I2C1_EV_IRQHandler(void)
|
|
||||||
{
|
|
||||||
uint16_t STAR1, STAR2 __attribute__((unused));
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// read status, clear any events
|
|
||||||
STAR1 = I2C1->STAR1;
|
|
||||||
STAR2 = I2C1->STAR2;
|
|
||||||
|
|
||||||
/* check for TXE */
|
|
||||||
if(STAR1 & I2C_STAR1_TXE)
|
|
||||||
{
|
|
||||||
/* check for remaining data */
|
|
||||||
if(ssd1306_i2c_send_sz--)
|
|
||||||
I2C1->DATAR = *ssd1306_i2c_send_ptr++;
|
|
||||||
|
|
||||||
/* was that the last byte? */
|
|
||||||
if(!ssd1306_i2c_send_sz)
|
|
||||||
{
|
|
||||||
// disable TXE interrupt
|
|
||||||
I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN);
|
|
||||||
|
|
||||||
// reset IRQ state
|
|
||||||
ssd1306_i2c_irq_state = 0;
|
|
||||||
|
|
||||||
// wait for tx complete
|
|
||||||
while(!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED));
|
|
||||||
|
|
||||||
// set STOP condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
GPIOC->BSHR = (1<<(16+4));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* low-level packet send for blocking polled operation via i2c
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz)
|
|
||||||
{
|
|
||||||
int32_t timeout;
|
|
||||||
|
|
||||||
// wait for not busy
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(0);
|
|
||||||
|
|
||||||
// Set START condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_START;
|
|
||||||
|
|
||||||
// wait for master mode select
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(1);
|
|
||||||
|
|
||||||
// send 7-bit address + write flag
|
|
||||||
I2C1->DATAR = addr<<1;
|
|
||||||
|
|
||||||
// wait for transmit condition
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(2);
|
|
||||||
|
|
||||||
// send data one byte at a time
|
|
||||||
while(sz--)
|
|
||||||
{
|
|
||||||
// wait for TX Empty
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(3);
|
|
||||||
|
|
||||||
// send command
|
|
||||||
I2C1->DATAR = *data++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for tx complete
|
|
||||||
timeout = TIMEOUT_MAX;
|
|
||||||
while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--));
|
|
||||||
if(timeout==-1)
|
|
||||||
return ssd1306_i2c_error(4);
|
|
||||||
|
|
||||||
// set STOP condition
|
|
||||||
I2C1->CTLR1 |= I2C_CTLR1_STOP;
|
|
||||||
|
|
||||||
// we're happy
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* high-level packet send for I2C
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_pkt_send(uint8_t *data, uint8_t sz, uint8_t cmd)
|
|
||||||
{
|
|
||||||
uint8_t pkt[33];
|
|
||||||
|
|
||||||
/* build command or data packets */
|
|
||||||
if(cmd)
|
|
||||||
{
|
|
||||||
pkt[0] = 0;
|
|
||||||
pkt[1] = *data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pkt[0] = 0x40;
|
|
||||||
memcpy(&pkt[1], data, sz);
|
|
||||||
}
|
|
||||||
return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* init I2C and GPIO
|
|
||||||
*/
|
|
||||||
uint8_t ssd1306_i2c_init(void)
|
|
||||||
{
|
|
||||||
// Enable GPIOC and I2C
|
|
||||||
RCC->APB2PCENR |= RCC_APB2Periph_GPIOC;
|
|
||||||
RCC->APB1PCENR |= RCC_APB1Periph_I2C1;
|
|
||||||
|
|
||||||
// PC1 is SDA, 10MHz Output, alt func, open-drain
|
|
||||||
GPIOC->CFGLR &= ~(0xf<<(4*1));
|
|
||||||
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1);
|
|
||||||
|
|
||||||
// PC2 is SCL, 10MHz Output, alt func, open-drain
|
|
||||||
GPIOC->CFGLR &= ~(0xf<<(4*2));
|
|
||||||
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2);
|
|
||||||
|
|
||||||
#ifdef IRQ_DIAG
|
|
||||||
// GPIO diags on PC3/PC4
|
|
||||||
GPIOC->CFGLR &= ~(0xf<<(4*3));
|
|
||||||
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3);
|
|
||||||
GPIOC->BSHR = (1<<(16+3));
|
|
||||||
GPIOC->CFGLR &= ~(0xf<<(4*4));
|
|
||||||
GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
|
|
||||||
GPIOC->BSHR = (1<<(16+4));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// load I2C regs
|
|
||||||
ssd1306_i2c_setup();
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// test if SSD1306 is on the bus by sending display off command
|
|
||||||
uint8_t command = 0xAF;
|
|
||||||
return ssd1306_pkt_send(&command, 1, 1);
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* reset is not used for SSD1306 I2C interface
|
|
||||||
*/
|
|
||||||
void ssd1306_rst(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -480,7 +480,7 @@ uint8_t ssd1306_init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear display
|
// clear display
|
||||||
ssd1306_setbuf(0);
|
// ssd1306_setbuf(0);
|
||||||
ssd1306_refresh();
|
ssd1306_refresh();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
Loading…
Reference in a new issue