Last active
October 6, 2021 18:36
-
-
Save bellinitte/197142555ce78ee82c816c79941f351a to your computer and use it in GitHub Desktop.
Displaying an image on a Nokia 5510 LCD controlled by an ATmega328P
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define F_CPU 16000000UL | |
#include <avr/io.h> | |
#include <avr/pgmspace.h> | |
#include <util/delay.h> | |
#include "image.c" | |
#define LCD_RST PC1 | |
#define LCD_SCE PC2 | |
#define LCD_DC PC3 | |
#define LCD_DIN PC4 | |
#define LCD_CLK PC5 | |
volatile uint16_t image_pos = (uint16_t) image; | |
void lcd_write(uint8_t val, uint8_t as_data) { | |
PORTC &= ~_BV(LCD_SCE); | |
if (as_data) { | |
PORTC |= _BV(LCD_DC); | |
} else { | |
PORTC &= ~_BV(LCD_DC); | |
} | |
for (uint8_t i = 0; i < 8; i += 1) { | |
if ((val >> (7-i)) & 0x01) { | |
PORTC |= _BV(LCD_DIN); | |
} else { | |
PORTC &= ~_BV(LCD_DIN); | |
} | |
PORTC |= _BV(LCD_CLK); | |
PORTC &= ~_BV(LCD_CLK); | |
} | |
PORTC |= _BV(LCD_SCE); | |
} | |
void lcd_write_cmd(uint8_t val) { | |
lcd_write(val, 0); | |
} | |
void lcd_write_data(uint8_t val) { | |
lcd_write(val, 1); | |
} | |
void lcd_init() { | |
DDRC |= _BV(LCD_SCE); | |
DDRC |= _BV(LCD_RST); | |
DDRC |= _BV(LCD_DC); | |
DDRC |= _BV(LCD_DIN); | |
DDRC |= _BV(LCD_CLK); | |
PORTC |= _BV(LCD_RST); | |
PORTC |= _BV(LCD_SCE); | |
_delay_ms(10); | |
PORTC &= ~_BV(LCD_RST); | |
_delay_ms(70); | |
PORTC |= _BV(LCD_RST); | |
PORTC &= ~_BV(LCD_SCE); | |
lcd_write_cmd(0x21); | |
lcd_write_cmd(_BV(4) | 0); // Bias system (0-7) | |
lcd_write_cmd(_BV(2) | 0); // Temperature coefficient (0-3) | |
lcd_write_cmd(_BV(7) | 50); // V_OP (0-127) | |
lcd_write_cmd(0x20); | |
lcd_write_cmd(_BV(3) | _BV(2)); // D = 1, E = 0 | |
} | |
void lcd_render() { | |
lcd_write_cmd(0x80); // X = 0 | |
lcd_write_cmd(0x40); // Y = 0 | |
for (int i = 0; i < 504; ++i) { | |
lcd_write_data(pgm_read_byte(image_pos)); | |
++image_pos; | |
} | |
} | |
int main(void) { | |
lcd_init(); | |
lcd_render(); | |
while (1) {} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -ex | |
python3 preprocess.py image.png > image.c | |
avr-gcc -mmcu=atmega328p -Wall -Os -o main.elf main.c | |
avrdude -p m328p -c usbasp -e -V -U flash:w:main.elf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
from PIL import Image | |
image = Image.open(sys.argv[1]) | |
assert image.size == (84, 48), "image size is not 84x48" | |
def binarize(pixel): | |
r, g, b, a = pixel | |
assert a == 255, "image has a transparent pixel" | |
assert (r, g, b) == (0, 0, 0) or (r, g, b) == (255, 255, 255), "image is not a binary image" | |
return r // 255 | |
pixels = list(map(binarize, image.getdata())) | |
frame_buffer = [0] * 504 | |
def set_buffer(x, y, pixel): | |
global frame_buffer | |
p = x + y // 8 * 84 | |
if pixel: | |
frame_buffer[p] |= 2 ** (y % 8) | |
else: | |
frame_buffer[p] &= ~(2 ** (y % 8)) | |
for x in range(84): | |
for y in range(48): | |
i = y * 84 + x | |
set_buffer(83 - x, 47 - y, not pixels[i]) # Rotate 180 degrees and invert the color | |
print("const uint8_t image[504] PROGMEM = {") | |
for byte in frame_buffer: | |
print(f" 0x{byte:02x},") | |
print("};") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment