404 lines
7.8 KiB
C
404 lines
7.8 KiB
C
|
/*
|
||
|
* DisplayBoardFirmware.c
|
||
|
*
|
||
|
* Created: 08.01.2019 19:46:33
|
||
|
* Author : julian
|
||
|
*/
|
||
|
|
||
|
#include <avr/io.h>
|
||
|
#include <avr/interrupt.h>
|
||
|
#include <util/delay.h>
|
||
|
#include <stddef.h> // for size_t
|
||
|
|
||
|
#define DISPLAY_PORT PORTA
|
||
|
#define DISPLAY_PIN PINA
|
||
|
#define DISPLAY_DDR DDRA
|
||
|
#define DISPLAY_SCL (1<<PA3)
|
||
|
#define DISPLAY_SDA (1<<PA2)
|
||
|
|
||
|
#define DISPLAY_SET(pin) do { DISPLAY_PORT &= ~(pin); DISPLAY_DDR |= (pin); } while(0)
|
||
|
#define DISPLAY_PIN_CLEAR(pin) do { DISPLAY_DDR &= ~(pin); DISPLAY_PORT |= (pin); } while(0)
|
||
|
|
||
|
#define DISPLAY_HALFCLK 1
|
||
|
|
||
|
// display commands
|
||
|
enum {
|
||
|
DISPLAY_CMD_CLK_DIV = 0xD5,
|
||
|
DISPLAY_CMD_MULTIPLEX = 0xA8,
|
||
|
DISPLAY_CMD_CHARGEPUMP = 0x8D,
|
||
|
|
||
|
DISPLAY_CMD_COMM_VOLTAGE_DETECT = 0xDB,
|
||
|
|
||
|
DISPLAY_CMD_LINE_OFFSET = 0xD3,
|
||
|
DISPLAY_CMD_LINE_START = 0x40,
|
||
|
DISPLAY_CMD_ENABLE = 0xAE,
|
||
|
DISPLAY_CMD_INVERT = 0xA6,
|
||
|
DISPLAY_CMD_TEST = 0xA4,
|
||
|
DISPLAY_CMD_CONTRAST = 0x81,
|
||
|
|
||
|
DISPLAY_CMD_ADDR_MODE = 0x20,
|
||
|
DISPLAY_CMD_COLUMN_ADDR = 0x21,
|
||
|
DISPLAY_CMD_PAGE_ADDR = 0x22,
|
||
|
DISPLAY_CMD_SCANMODE_POS = 0xC8,
|
||
|
DISPLAY_CMD_SCANMODE_NEG = 0xC0,
|
||
|
};
|
||
|
|
||
|
|
||
|
/* Display driver */
|
||
|
typedef struct {
|
||
|
volatile uint8_t current_buffer:1;
|
||
|
} display_t;
|
||
|
|
||
|
display_t display;
|
||
|
|
||
|
static inline void display_start() {
|
||
|
DISPLAY_SET(DISPLAY_SDA);
|
||
|
_delay_us(DISPLAY_HALFCLK);
|
||
|
DISPLAY_SET(DISPLAY_SCL);
|
||
|
}
|
||
|
|
||
|
static inline void display_stop() {
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SCL);
|
||
|
_delay_us(DISPLAY_HALFCLK);
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SDA);
|
||
|
}
|
||
|
|
||
|
const uint8_t display_addr = 0x78;
|
||
|
//const uint8_t display_addr = 0x1E;
|
||
|
|
||
|
// returns 1 on NACK
|
||
|
int display_send(uint8_t b) {
|
||
|
int i = 0;
|
||
|
while(i < 8)
|
||
|
{
|
||
|
if (b&(0x80>>i))
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SDA);
|
||
|
else
|
||
|
DISPLAY_SET(DISPLAY_SDA);
|
||
|
|
||
|
_delay_us(1);
|
||
|
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SCL);
|
||
|
_delay_us(DISPLAY_HALFCLK);
|
||
|
DISPLAY_SET(DISPLAY_SCL);
|
||
|
_delay_us(DISPLAY_HALFCLK-2);
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SDA);
|
||
|
_delay_us(DISPLAY_HALFCLK);
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SCL);
|
||
|
|
||
|
_delay_us(DISPLAY_HALFCLK);
|
||
|
// look for ACK
|
||
|
int result = DISPLAY_PIN & DISPLAY_SDA;
|
||
|
DISPLAY_SET(DISPLAY_SCL);
|
||
|
|
||
|
// ack means 0 during clk period
|
||
|
return result != 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
int display_ncmd(uint8_t *cmd, uint8_t len) {
|
||
|
int result = 1;
|
||
|
display_start();
|
||
|
|
||
|
if (display_send(display_addr)) goto fail;
|
||
|
|
||
|
uint8_t prefix = 0x80;
|
||
|
while(len--)
|
||
|
{
|
||
|
if (display_send(prefix) || display_send(*cmd))
|
||
|
goto fail;
|
||
|
|
||
|
if (len-1) {
|
||
|
prefix = 0x00;
|
||
|
}
|
||
|
++cmd;
|
||
|
}
|
||
|
|
||
|
result = 0;
|
||
|
fail:
|
||
|
display_stop();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int display_cmd(uint8_t cmd) {
|
||
|
return display_ncmd(&cmd, 1);
|
||
|
}
|
||
|
|
||
|
int display_data_chunk(uint8_t ** data, size_t *len) {
|
||
|
int result = 1;
|
||
|
uint8_t count = 16;
|
||
|
|
||
|
display_start();
|
||
|
|
||
|
if (display_send(display_addr)) {
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
if (display_send(0x40))
|
||
|
goto fail;
|
||
|
|
||
|
while(count && *len && !display_send(**data)) {
|
||
|
count--;
|
||
|
|
||
|
(*len)--;
|
||
|
(*data)++;
|
||
|
}
|
||
|
|
||
|
result = 0;
|
||
|
fail:
|
||
|
|
||
|
display_stop();
|
||
|
return result;
|
||
|
}
|
||
|
int display_data(uint8_t * data, size_t len) {
|
||
|
int result = 1;
|
||
|
|
||
|
while(len && !(result = display_data_chunk(&data, &len))) {}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void display_copy(int x, int y, int w, int h, uint8_t *data, size_t len) {
|
||
|
display_cmd(DISPLAY_CMD_COLUMN_ADDR);
|
||
|
display_cmd(y);
|
||
|
display_cmd(y + h);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_PAGE_ADDR);
|
||
|
display_cmd(x + 4*display.current_buffer);
|
||
|
display_cmd(x + w + 4*display.current_buffer -1);
|
||
|
|
||
|
display_data(data, len);
|
||
|
}
|
||
|
|
||
|
void display_clear() {
|
||
|
static uint8_t chunk[8] = {0xFF};
|
||
|
size_t len = 0;
|
||
|
size_t c = 8;
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_COLUMN_ADDR);
|
||
|
display_cmd(0);
|
||
|
display_cmd(127);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_PAGE_ADDR);
|
||
|
display_cmd(display.current_buffer*4);
|
||
|
display_cmd(display.current_buffer*4+3);
|
||
|
|
||
|
uint8_t * current = chunk;
|
||
|
while(len < 128*4) {
|
||
|
display_start();
|
||
|
display_send(display_addr);
|
||
|
display_send(0x40);
|
||
|
for (uint8_t i = 0; i < 16; i++) {
|
||
|
display_send(0x00);
|
||
|
}
|
||
|
|
||
|
display_stop();
|
||
|
len += 16;
|
||
|
}
|
||
|
}
|
||
|
#include "font.h"
|
||
|
|
||
|
static inline void pgm_memcpy(uint8_t * dest, const uint8_t * src, size_t s) {
|
||
|
while(s--) {
|
||
|
*dest = pgm_read_byte(src);
|
||
|
|
||
|
++src;
|
||
|
++dest;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void display_font_copy(char c, int x, int y) {
|
||
|
static uint8_t buffer[FONT_HEIGHT*2];
|
||
|
|
||
|
if (c < FONT_START || c > FONT_START + FONT_END)
|
||
|
return;
|
||
|
|
||
|
int offset = c - FONT_START;
|
||
|
|
||
|
uint16_t index = pgm_read_word(Fonttable + offset);
|
||
|
uint8_t w = pgm_read_byte(Fonttable + offset +2);
|
||
|
uint8_t h = pgm_read_byte(Fonttable + offset +3);
|
||
|
|
||
|
const uint8_t * current = Fonttable + index + FONT_END;
|
||
|
uint8_t linebuffer = pgm_read_byte(current++);
|
||
|
uint8_t count = 8;
|
||
|
|
||
|
for (int y = 0; y < h; y++) {
|
||
|
for (int x = 0; x < w; x++) {
|
||
|
count--;
|
||
|
|
||
|
if (!count) {
|
||
|
count = 8;
|
||
|
linebuffer = pgm_read_byte(current++);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// pgm_memcpy((uint8_t*)&glyph_buffer, (const uint8_t*)Glyphtable, sizeof(glyph_buffer));
|
||
|
// pgm_memcpy(buffer, Fonttable + glyph_buffer.index, glyph_buffer.w);
|
||
|
|
||
|
// display_copy(x, y, 1, FONT_HEIGHT, buffer, FONT_HEIGHT);
|
||
|
}
|
||
|
|
||
|
|
||
|
// initializes display
|
||
|
// setup parameters for normal operation
|
||
|
void display_init()
|
||
|
{
|
||
|
display.current_buffer = 1;
|
||
|
|
||
|
DISPLAY_PIN_CLEAR(DISPLAY_SCL | DISPLAY_SDA);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_ENABLE);
|
||
|
display_cmd(DISPLAY_CMD_CLK_DIV);
|
||
|
display_cmd(0x80);
|
||
|
display_cmd(DISPLAY_CMD_MULTIPLEX);
|
||
|
display_cmd(63);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_CHARGEPUMP);
|
||
|
display_cmd(0x14);
|
||
|
|
||
|
display_cmd(0xDA);
|
||
|
display_cmd(0x02);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_LINE_OFFSET);
|
||
|
display_cmd(0);
|
||
|
display_cmd(DISPLAY_CMD_LINE_START | 0);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_COMM_VOLTAGE_DETECT);
|
||
|
display_cmd(0x40);
|
||
|
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_ADDR_MODE);
|
||
|
display_cmd(0x01);
|
||
|
display_cmd(DISPLAY_CMD_SCANMODE_POS);
|
||
|
|
||
|
display_cmd(DISPLAY_CMD_TEST); // no testing mode
|
||
|
|
||
|
display_clear();
|
||
|
display_cmd(DISPLAY_CMD_ENABLE|1);
|
||
|
}
|
||
|
|
||
|
void display_swap() {
|
||
|
display.current_buffer = !display.current_buffer;
|
||
|
display_cmd(DISPLAY_CMD_LINE_START | 32*display.current_buffer);
|
||
|
|
||
|
}
|
||
|
|
||
|
// TODO: find out what display is capable of
|
||
|
void display_write(const char * str)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
// should be called after every change to display content is made
|
||
|
// TODO: check if this is possible to implement
|
||
|
void display_present() {
|
||
|
|
||
|
}
|
||
|
|
||
|
/* encoder driver */
|
||
|
typedef enum {
|
||
|
ENC_DIR_NONE,
|
||
|
ENC_DIR_CW,
|
||
|
ENC_DIR_CCW,
|
||
|
} encoder_dir_t;
|
||
|
|
||
|
struct encoder;
|
||
|
typedef void (*encoder_callback_t)(struct encoder * encoder);
|
||
|
|
||
|
typedef struct encoder {
|
||
|
encoder_dir_t dir;
|
||
|
uint16_t velo;
|
||
|
int16_t pos;
|
||
|
|
||
|
encoder_callback_t callback;
|
||
|
} encoder_t;
|
||
|
|
||
|
encoder_t encoder;
|
||
|
|
||
|
// encoder can only be at the INT0/INT1 lines
|
||
|
// maybe pushbutton on pinchange
|
||
|
void encoder_init(encoder_t * encoder) {
|
||
|
*encoder = (encoder_t){};
|
||
|
|
||
|
// TODO: configure PCINT0 interrupt for direction
|
||
|
// TODO: configure PCINT1 interrupt for push button
|
||
|
}
|
||
|
|
||
|
volatile encoder_dir_t first;
|
||
|
|
||
|
#include <avr/interrupt.h>
|
||
|
|
||
|
// turning interrupt
|
||
|
ISR(PCINT0_vect) {
|
||
|
// find edge type
|
||
|
if (first == ENC_DIR_NONE) {
|
||
|
first = ENC_DIR_CCW;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
encoder.dir = first;
|
||
|
encoder.pos -= 1;
|
||
|
if (encoder.callback)
|
||
|
encoder.callback(&encoder);
|
||
|
first = ENC_DIR_NONE;
|
||
|
}
|
||
|
|
||
|
// push interrupt
|
||
|
ISR(PCINT1_vect) {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
/* main application */
|
||
|
typedef struct menuitem {
|
||
|
struct menuitem * root;
|
||
|
const char * text;
|
||
|
|
||
|
struct menuitem * children;
|
||
|
uint8_t children_count;
|
||
|
} menuitem;
|
||
|
|
||
|
#include <stddef.h>
|
||
|
|
||
|
menuitem root = {.root = NULL,
|
||
|
.text = NULL,
|
||
|
|
||
|
};
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
//_delay_ms(100);
|
||
|
display_init();
|
||
|
|
||
|
uint8_t face[8] = {
|
||
|
0b11111111,
|
||
|
0b11111110,
|
||
|
0b11111100,
|
||
|
0b11111000,
|
||
|
0b11110000,
|
||
|
0b11100000,
|
||
|
0b11000000,
|
||
|
0b10000000,
|
||
|
};
|
||
|
|
||
|
for (int j = 0; j< 16; j++)
|
||
|
for (int i = 0; i<4; i++)
|
||
|
{
|
||
|
display_copy(i,j*8, 1,8, face, 8);
|
||
|
display_font_copy('a', i,j*8);
|
||
|
}
|
||
|
display_swap();
|
||
|
|
||
|
/* Replace with your application code */
|
||
|
while (1)
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
|