mixer-slider/firmware/display/main.c
2019-02-06 23:22:59 +01:00

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)
{
}
}