mirror of
https://github.com/StepanovPlaton/C3DGraphicEngine.git
synced 2026-04-05 21:30:44 +04:00
Add TV output
This commit is contained in:
19
src/Makefile
19
src/Makefile
@@ -1,18 +1,21 @@
|
|||||||
# Desktop options
|
# Desktop options
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -std=gnu23 -Wall -Wextra -O2
|
CFLAGS = -std=gnu23 -Wall -Wextra -O2
|
||||||
|
CXXFLAGS = -std=gnu++17 -Wall -Wextra -O2 -fno-exceptions -fno-rtti
|
||||||
LIBS = -lgdi32
|
LIBS = -lgdi32
|
||||||
|
|
||||||
# Micro options
|
# Micro options
|
||||||
MCU = atmega328p
|
MCU = atmega328p
|
||||||
F_CPU = 16000000
|
F_CPU = 16000000
|
||||||
PROGRAMMER = arduino
|
PROGRAMMER = arduino
|
||||||
PORT = COM4
|
PORT = COM5
|
||||||
MICRO_CC = avr-gcc
|
MICRO_CC = avr-gcc
|
||||||
|
MICRO_CXX = avr-g++
|
||||||
OBJCOPY = avr-objcopy
|
OBJCOPY = avr-objcopy
|
||||||
SIZE = avr-size
|
SIZE = avr-size
|
||||||
AVRDUDE = avrdude
|
AVRDUDE = avrdude
|
||||||
MICRO_CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL $(CFLAGS)
|
MICRO_CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL $(CFLAGS)
|
||||||
|
MICRO_CXXFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL $(CXXFLAGS)
|
||||||
LDFLAGS = -mmcu=$(MCU)
|
LDFLAGS = -mmcu=$(MCU)
|
||||||
|
|
||||||
TARGET = main
|
TARGET = main
|
||||||
@@ -26,8 +29,9 @@ UNIX_SOURCES = $(BASE_SOURCES) platforms/unix.c
|
|||||||
UNIX_OBJECTS = $(UNIX_SOURCES:.c=.uo)
|
UNIX_OBJECTS = $(UNIX_SOURCES:.c=.uo)
|
||||||
UNIX_TARGET = $(TARGET)
|
UNIX_TARGET = $(TARGET)
|
||||||
|
|
||||||
MICRO_SOURCES = $(BASE_SOURCES) platforms/micro/micro.c platforms/micro/lib/ST7789/ST7789.c platforms/micro/lib/MPU6050/MPU6050.c platforms/micro/lib/I2C/I2C.c
|
MICRO_SOURCES = $(BASE_SOURCES) platforms/micro/micro.cpp platforms/micro/lib/TVout/TVout.cpp platforms/micro/lib/TVout/video_gen.cpp platforms/micro/lib/MPU6050/MPU6050.c platforms/micro/lib/I2C/I2C.c
|
||||||
MICRO_OBJECTS = $(MICRO_SOURCES:.c=.mo)
|
MICRO_OBJECTS = $(MICRO_SOURCES:.cpp=.mxxo)
|
||||||
|
MICRO_OBJECTS := $(MICRO_OBJECTS:.c=.mo)
|
||||||
MICRO_TARGET = $(TARGET)
|
MICRO_TARGET = $(TARGET)
|
||||||
ELF = $(TARGET).elf
|
ELF = $(TARGET).elf
|
||||||
HEX = $(TARGET).hex
|
HEX = $(TARGET).hex
|
||||||
@@ -44,7 +48,7 @@ $(HEX): $(ELF)
|
|||||||
$(SIZE) $@
|
$(SIZE) $@
|
||||||
|
|
||||||
$(ELF): $(MICRO_OBJECTS)
|
$(ELF): $(MICRO_OBJECTS)
|
||||||
$(MICRO_CC) $(LDFLAGS) -o $@ $^
|
$(MICRO_CXX) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
%.wo: %.c
|
%.wo: %.c
|
||||||
$(CC) $(CFLAGS) -DWIN -c $< -o $@
|
$(CC) $(CFLAGS) -DWIN -c $< -o $@
|
||||||
@@ -52,9 +56,14 @@ $(ELF): $(MICRO_OBJECTS)
|
|||||||
%.uo: %.c
|
%.uo: %.c
|
||||||
$(CC) $(CFLAGS) -DUNIX -c $< -o $@
|
$(CC) $(CFLAGS) -DUNIX -c $< -o $@
|
||||||
|
|
||||||
|
|
||||||
|
%.mxxo: %.cpp
|
||||||
|
$(MICRO_CXX) $(MICRO_CXXFLAGS) -DMICRO -c $< -o $@
|
||||||
|
|
||||||
%.mo: %.c
|
%.mo: %.c
|
||||||
$(MICRO_CC) $(MICRO_CFLAGS) -DMICRO -c $< -o $@
|
$(MICRO_CC) $(MICRO_CFLAGS) -DMICRO -c $< -o $@
|
||||||
|
|
||||||
|
|
||||||
run_win: $(WIN_TARGET)
|
run_win: $(WIN_TARGET)
|
||||||
.\$(WIN_TARGET)
|
.\$(WIN_TARGET)
|
||||||
|
|
||||||
@@ -62,7 +71,7 @@ run_unix: $(UNIX_TARGET)
|
|||||||
.\$(UNIX_TARGET)
|
.\$(UNIX_TARGET)
|
||||||
|
|
||||||
upload_micro: $(HEX)
|
upload_micro: $(HEX)
|
||||||
$(AVRDUDE) -p $(MCU) -c $(PROGRAMMER) -P $(PORT) -b 115200 -U flash:w:$(HEX):i
|
$(AVRDUDE) -p $(MCU) -c $(PROGRAMMER) -P $(PORT) -b 57600 -U flash:w:$(HEX):i
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(WIN_TARGET) $(WIN_OBJECTS) $(UNIX_TARGET) $(UNIX_OBJECTS) $(HEX) $(ELF) $(MICRO_OBJECTS);
|
rm -f $(WIN_TARGET) $(WIN_OBJECTS) $(UNIX_TARGET) $(UNIX_OBJECTS) $(HEX) $(ELF) $(MICRO_OBJECTS);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
|
||||||
#include "../utils/screen.h"
|
#include "../utils/screen.h"
|
||||||
|
#include "camera.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
void object_transform(Object *const object, int size, float *translate_matrix) {
|
void object_transform(Object *const object, int size, float *translate_matrix) {
|
||||||
@@ -27,12 +28,84 @@ Point object_get_centroid(const Object *const object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void object_draw(const Object *object, const Screen *const screen,
|
void object_draw(const Object *object, const Screen *const screen,
|
||||||
const float *render_matrix, const Color *const color) {
|
const float *render_matrix, const Camera *const camera,
|
||||||
for (int i = 0; i < object->number_of_edges; i++) {
|
const Color *const color) {
|
||||||
ScreenPoint screen_point1 = point_to_screen_point(
|
// Массив для отслеживания, какие рёбра нужно нарисовать
|
||||||
&object->points[object->edges[i][0]], screen, render_matrix);
|
char edge_drawn[object->number_of_edges];
|
||||||
ScreenPoint screen_point2 = point_to_screen_point(
|
for (int i = 0; i < object->number_of_edges; ++i) {
|
||||||
&object->points[object->edges[i][1]], screen, render_matrix);
|
edge_drawn[i] = 0;
|
||||||
screen->draw_line(&screen_point1, &screen_point2, color);
|
}
|
||||||
|
|
||||||
|
// Проходим по всем граням
|
||||||
|
for (int face_idx = 0; face_idx < object->number_of_faces; ++face_idx) {
|
||||||
|
const int *face = object->faces[face_idx];
|
||||||
|
|
||||||
|
// Берём первые три вершины для вычисления нормали (достаточно для плоской
|
||||||
|
// грани)
|
||||||
|
Point p0 = object->points[face[0]];
|
||||||
|
Point p1 = object->points[face[1]];
|
||||||
|
Point p2 = object->points[face[2]];
|
||||||
|
|
||||||
|
// Векторы по ребрам грани
|
||||||
|
float v1[3] = {p1.coordinates[0] - p0.coordinates[0],
|
||||||
|
p1.coordinates[1] - p0.coordinates[1],
|
||||||
|
p1.coordinates[2] - p0.coordinates[2]};
|
||||||
|
float v2[3] = {p2.coordinates[0] - p0.coordinates[0],
|
||||||
|
p2.coordinates[1] - p0.coordinates[1],
|
||||||
|
p2.coordinates[2] - p0.coordinates[2]};
|
||||||
|
|
||||||
|
// Нормаль к грани (внешняя, если winding order — CCW при взгляде снаружи)
|
||||||
|
float normal[3];
|
||||||
|
vector_cross_product(v1, v2, normal);
|
||||||
|
|
||||||
|
// Вычисляем центр грани (для направления к камере)
|
||||||
|
float w[3] = {0.0f, 0.0f, 0.0f};
|
||||||
|
for (int i = 0; i < object->face_sizes[face_idx]; ++i) {
|
||||||
|
const Point *pt = &object->points[object->faces[face_idx][i]];
|
||||||
|
w[0] += pt->coordinates[0];
|
||||||
|
w[1] += pt->coordinates[1];
|
||||||
|
w[2] += pt->coordinates[2];
|
||||||
|
}
|
||||||
|
w[0] /= (float)object->face_sizes[face_idx];
|
||||||
|
w[1] /= (float)object->face_sizes[face_idx];
|
||||||
|
w[2] /= (float)object->face_sizes[face_idx];
|
||||||
|
|
||||||
|
// Вектор от центра грани к позиции камеры
|
||||||
|
float to_camera[3] = {camera->position->coordinates[0] - w[0],
|
||||||
|
camera->position->coordinates[1] - w[1],
|
||||||
|
camera->position->coordinates[2] - w[2]};
|
||||||
|
|
||||||
|
// Проверка видимости: если грань смотрит от камеры — пропускаем
|
||||||
|
float dot = vector_dot_product(normal, to_camera);
|
||||||
|
if (dot <= 0.0f) {
|
||||||
|
continue; // back-face: невидима
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отмечаем все рёбра этой грани как "нужно нарисовать"
|
||||||
|
for (int j = 0; j < object->face_sizes[face_idx]; ++j) {
|
||||||
|
int v_start = face[j];
|
||||||
|
int v_end = face[(j + 1) % object->face_sizes[face_idx]];
|
||||||
|
|
||||||
|
for (int edge_idx = 0; edge_idx < object->number_of_edges; ++edge_idx) {
|
||||||
|
int e0 = object->edges[edge_idx][0];
|
||||||
|
int e1 = object->edges[edge_idx][1];
|
||||||
|
|
||||||
|
if ((e0 == v_start && e1 == v_end) || (e0 == v_end && e1 == v_start)) {
|
||||||
|
edge_drawn[edge_idx] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рисуем только отмеченные рёбра
|
||||||
|
for (int i = 0; i < object->number_of_edges; ++i) {
|
||||||
|
if (edge_drawn[i]) {
|
||||||
|
ScreenPoint p1 = point_to_screen_point(
|
||||||
|
&object->points[object->edges[i][0]], screen, render_matrix);
|
||||||
|
ScreenPoint p2 = point_to_screen_point(
|
||||||
|
&object->points[object->edges[i][1]], screen, render_matrix);
|
||||||
|
screen->draw_line(&p1, &p2, color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,14 +2,18 @@
|
|||||||
#define OBJECT_H
|
#define OBJECT_H
|
||||||
|
|
||||||
#include "../utils/screen.h"
|
#include "../utils/screen.h"
|
||||||
|
#include "camera.h"
|
||||||
#include "point.h"
|
#include "point.h"
|
||||||
|
|
||||||
typedef struct Object {
|
typedef struct Object {
|
||||||
const char *const name;
|
const char *const name;
|
||||||
Point *const points;
|
Point *const points;
|
||||||
const int (*const edges)[2];
|
const int (*const edges)[2];
|
||||||
|
const int *const *const faces;
|
||||||
|
const int(*const face_sizes);
|
||||||
const int number_of_points;
|
const int number_of_points;
|
||||||
const int number_of_edges;
|
const int number_of_edges;
|
||||||
|
const int number_of_faces;
|
||||||
Point *const position;
|
Point *const position;
|
||||||
const float (*const rotate_speed)[3];
|
const float (*const rotate_speed)[3];
|
||||||
} Object;
|
} Object;
|
||||||
@@ -17,6 +21,7 @@ typedef struct Object {
|
|||||||
void object_transform(Object *const object, int size, float *translate_matrix);
|
void object_transform(Object *const object, int size, float *translate_matrix);
|
||||||
Point object_get_centroid(const Object *const object);
|
Point object_get_centroid(const Object *const object);
|
||||||
void object_draw(const Object *object, const Screen *const screen,
|
void object_draw(const Object *object, const Screen *const screen,
|
||||||
const float *render_matrix, const Color *const color);
|
const float *render_matrix, const Camera *const camera,
|
||||||
|
const Color *const color);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -7,6 +7,10 @@ void point_add_point(Point *const point, const Point *const other_point) {
|
|||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
point->coordinates[i] += other_point->coordinates[i];
|
point->coordinates[i] += other_point->coordinates[i];
|
||||||
}
|
}
|
||||||
|
void point_substact_point(Point *const point, const Point *const other_point) {
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
point->coordinates[i] -= other_point->coordinates[i];
|
||||||
|
}
|
||||||
|
|
||||||
void point_mult_number(Point *const point, const int k) {
|
void point_mult_number(Point *const point, const int k) {
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ typedef struct Point {
|
|||||||
} Point;
|
} Point;
|
||||||
|
|
||||||
void point_add_point(Point *const point, const Point *const other_point);
|
void point_add_point(Point *const point, const Point *const other_point);
|
||||||
|
void point_substact_point(Point *const point, const Point *const other_point);
|
||||||
void point_mult_number(Point *const point, const int k);
|
void point_mult_number(Point *const point, const int k);
|
||||||
void point_transform(Point *const point, int size,
|
void point_transform(Point *const point, int size,
|
||||||
const float *translate_matrix);
|
const float *translate_matrix);
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ void init_engine() {
|
|||||||
|
|
||||||
void render(const Screen *const screen, const Color *const color) {
|
void render(const Screen *const screen, const Color *const color) {
|
||||||
for (int i = 0; i < number_of_objects; i++) {
|
for (int i = 0; i < number_of_objects; i++) {
|
||||||
object_draw(&(objects[i]), screen, render_matrix, color);
|
object_draw(&(objects[i]), screen, render_matrix, &camera, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
118
src/objects.h
118
src/objects.h
@@ -3,59 +3,105 @@
|
|||||||
|
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
|
||||||
|
// == Cube ==
|
||||||
|
|
||||||
Point cube_points[] = {
|
Point cube_points[] = {
|
||||||
{.coordinates = {0.0f, 0.0f, 0.0f}}, {.coordinates = {1.0f, 0.0f, 0.0f}},
|
{.coordinates = {0.0f, 0.0f, 0.0f}}, {.coordinates = {1.0f, 0.0f, 0.0f}},
|
||||||
{.coordinates = {1.0f, 1.0f, 0.0f}}, {.coordinates = {0.0f, 1.0f, 0.0f}},
|
{.coordinates = {1.0f, 1.0f, 0.0f}}, {.coordinates = {0.0f, 1.0f, 0.0f}},
|
||||||
{.coordinates = {0.0f, 0.0f, 1.0f}}, {.coordinates = {1.0f, 0.0f, 1.0f}},
|
{.coordinates = {0.0f, 0.0f, 1.0f}}, {.coordinates = {1.0f, 0.0f, 1.0f}},
|
||||||
{.coordinates = {1.0f, 1.0f, 1.0f}}, {.coordinates = {0.0f, 1.0f, 1.0f}}};
|
{.coordinates = {1.0f, 1.0f, 1.0f}}, {.coordinates = {0.0f, 1.0f, 1.0f}}};
|
||||||
|
|
||||||
Point cube_position = {.coordinates = {3.0f, 6.0f, 5.0f}};
|
|
||||||
const int cube_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6},
|
const int cube_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6},
|
||||||
{6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
|
{6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
|
||||||
const float cube_speed[] = {0.03f, 0.0f, 0.0f};
|
int cube_face0[] = {3, 2, 1, 0};
|
||||||
|
int cube_face1[] = {4, 5, 6, 7};
|
||||||
|
int cube_face2[] = {0, 4, 7, 3};
|
||||||
|
int cube_face3[] = {2, 6, 5, 1};
|
||||||
|
int cube_face4[] = {1, 5, 4, 0};
|
||||||
|
int cube_face5[] = {3, 7, 6, 2};
|
||||||
|
const int *cube_faces[] = {cube_face0, cube_face1, cube_face2,
|
||||||
|
cube_face3, cube_face4, cube_face5};
|
||||||
|
const int cube_face_sizes[] = {4, 4, 4, 4, 4, 4};
|
||||||
|
|
||||||
|
const float cube_speed[] = {0.03f, -0.03f, 0.03f};
|
||||||
|
Point cube_position = {.coordinates = {2.0f, 2.0f, 2.0f}};
|
||||||
|
// Point cube_position = {.coordinates = {3.0f, 6.0f, 5.0f}};
|
||||||
const Object cube = {.name = "cube",
|
const Object cube = {.name = "cube",
|
||||||
.points = cube_points,
|
.points = cube_points,
|
||||||
.edges = cube_edges,
|
.edges = cube_edges,
|
||||||
|
.faces = cube_faces,
|
||||||
|
.face_sizes = cube_face_sizes,
|
||||||
.number_of_points = 8,
|
.number_of_points = 8,
|
||||||
.number_of_edges = 12,
|
.number_of_edges = 12,
|
||||||
|
.number_of_faces = 6,
|
||||||
.position = &cube_position,
|
.position = &cube_position,
|
||||||
.rotate_speed = &cube_speed};
|
.rotate_speed = &cube_speed};
|
||||||
|
|
||||||
Point pyramid_points[] = {{.coordinates = {-1.0f, 0.0f, -1.0f}},
|
// // == Pyramid ==
|
||||||
{.coordinates = {1.0f, 0.0f, -1.0f}},
|
|
||||||
{.coordinates = {1.0f, 0.0f, 1.0f}},
|
|
||||||
{.coordinates = {-1.0f, 0.0f, 1.0f}},
|
|
||||||
{.coordinates = {0.0f, 1.5f, 0.0f}}};
|
|
||||||
Point pyramid_position = {.coordinates = {6.0f, 3.0f, 5.0f}};
|
|
||||||
const int pyramid_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0},
|
|
||||||
{0, 4}, {1, 4}, {2, 4}, {3, 4}};
|
|
||||||
const float pyramid_speed[] = {0.0f, 0.0f, 0.03f};
|
|
||||||
const Object pyramid = {.name = "pyramid",
|
|
||||||
.points = pyramid_points,
|
|
||||||
.edges = pyramid_edges,
|
|
||||||
.number_of_points = 5,
|
|
||||||
.number_of_edges = 8,
|
|
||||||
.position = &pyramid_position,
|
|
||||||
.rotate_speed = &pyramid_speed};
|
|
||||||
|
|
||||||
Point prism_points[] = {
|
// Point pyramid_points[] = {{.coordinates = {-1.0f, 0.0f, -1.0f}},
|
||||||
{.coordinates = {0.0f, 0.0f, 0.0f}}, {.coordinates = {1.0f, 0.0f, 0.0f}},
|
// {.coordinates = {1.0f, 0.0f, -1.0f}},
|
||||||
{.coordinates = {0.5f, 0.0f, 1.0f}}, {.coordinates = {0.0f, 1.0f, 0.0f}},
|
// {.coordinates = {1.0f, 0.0f, 1.0f}},
|
||||||
{.coordinates = {1.0f, 1.0f, 0.0f}}, {.coordinates = {0.5f, 1.0f, 1.0f}}};
|
// {.coordinates = {-1.0f, 0.0f, 1.0f}},
|
||||||
Point prism_position = {.coordinates = {10.0f, 10.0f, 10.0f}};
|
// {.coordinates = {0.0f, 1.5f, 0.0f}}};
|
||||||
const int prism_edges[][2] = {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5},
|
// const int pyramid_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0},
|
||||||
{5, 3}, {0, 3}, {1, 4}, {2, 5}};
|
// {0, 4}, {1, 4}, {2, 4}, {3, 4}};
|
||||||
const float prism_speed[] = {0.0f, 0.03f, 0.0f};
|
// const int pyramid_face0[] = {0, 1, 2, 3};
|
||||||
const Object prism = {.name = "prism",
|
// const int pyramid_face1[] = {1, 0, 4};
|
||||||
.points = prism_points,
|
// const int pyramid_face2[] = {2, 1, 4};
|
||||||
.edges = prism_edges,
|
// const int pyramid_face3[] = {3, 2, 4};
|
||||||
.number_of_points = 6,
|
// const int pyramid_face4[] = {0, 3, 4};
|
||||||
.number_of_edges = 9,
|
// const int *pyramid_faces[] = {pyramid_face0, pyramid_face1, pyramid_face2,
|
||||||
.position = &prism_position,
|
// pyramid_face3, pyramid_face4};
|
||||||
.rotate_speed = &prism_speed};
|
// const int pyramid_face_sizes[] = {4, 3, 3, 3, 3};
|
||||||
|
|
||||||
const int number_of_objects = 3;
|
// const float pyramid_speed[] = {0.03f, 0.03f, -0.03f};
|
||||||
Object objects[] = {cube, pyramid, prism};
|
// Point pyramid_position = {.coordinates = {6.0f, 3.0f, 5.0f}};
|
||||||
|
// const Object pyramid = {.name = "pyramid",
|
||||||
|
// .points = pyramid_points,
|
||||||
|
// .edges = pyramid_edges,
|
||||||
|
// .faces = pyramid_faces,
|
||||||
|
// .face_sizes = pyramid_face_sizes,
|
||||||
|
// .number_of_points = 5,
|
||||||
|
// .number_of_edges = 8,
|
||||||
|
// .number_of_faces = 5,
|
||||||
|
// .position = &pyramid_position,
|
||||||
|
// .rotate_speed = &pyramid_speed};
|
||||||
|
|
||||||
|
// // == Prism ==
|
||||||
|
|
||||||
|
// Point prism_points[] = {
|
||||||
|
// {.coordinates = {0.0f, 0.0f, 0.0f}}, {.coordinates = {1.0f, 0.0f, 0.0f}},
|
||||||
|
// {.coordinates = {0.5f, 0.0f, 1.0f}}, {.coordinates = {0.0f, 1.0f, 0.0f}},
|
||||||
|
// {.coordinates = {1.0f, 1.0f, 0.0f}}, {.coordinates =
|
||||||
|
// {0.5f, 1.0f, 1.0f}}};
|
||||||
|
// const int prism_edges[][2] = {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5},
|
||||||
|
// {5, 3}, {0, 3}, {1, 4}, {2, 5}};
|
||||||
|
// const int prism_face0[] = {0, 1, 2};
|
||||||
|
// const int prism_face1[] = {3, 5, 4};
|
||||||
|
// const int prism_face2[] = {0, 3, 4, 1};
|
||||||
|
// const int prism_face3[] = {1, 4, 5, 2};
|
||||||
|
// const int prism_face4[] = {2, 5, 3, 0};
|
||||||
|
// const int *prism_faces[] = {prism_face0, prism_face1, prism_face2,
|
||||||
|
// prism_face3,
|
||||||
|
// prism_face4};
|
||||||
|
// const int prism_face_sizes[] = {3, 3, 4, 4, 4};
|
||||||
|
|
||||||
|
// const float prism_speed[] = {-0.03f, 0.03f, 0.03f};
|
||||||
|
// Point prism_position = {.coordinates = {10.0f, 10.0f, 10.0f}};
|
||||||
|
// const Object prism = {.name = "prism",
|
||||||
|
// .points = prism_points,
|
||||||
|
// .edges = prism_edges,
|
||||||
|
// .faces = prism_faces,
|
||||||
|
// .face_sizes = prism_face_sizes,
|
||||||
|
// .number_of_points = 6,
|
||||||
|
// .number_of_edges = 9,
|
||||||
|
// .number_of_faces = 5,
|
||||||
|
// .position = &prism_position,
|
||||||
|
// .rotate_speed = &prism_speed};
|
||||||
|
|
||||||
|
const int number_of_objects = 1;
|
||||||
|
// Object objects[] = {cube, pyramid, prism};
|
||||||
|
Object objects[] = {cube};
|
||||||
|
|
||||||
Point camera_position = {.coordinates = {0.0f, 0.0f, 0.0f}};
|
Point camera_position = {.coordinates = {0.0f, 0.0f, 0.0f}};
|
||||||
Point camera_target = {.coordinates = {1.0f, 1.0f, 1.0f}};
|
Point camera_target = {.coordinates = {1.0f, 1.0f, 1.0f}};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define TWI_FREQ 400000UL // 400 kHz
|
#define TWI_FREQ 400000UL // 400 kHz
|
||||||
|
// #define TWI_FREQ 100000UL // 400 kHz
|
||||||
|
|
||||||
void i2c_init(void);
|
void i2c_init(void);
|
||||||
uint8_t i2c_start(uint8_t address);
|
uint8_t i2c_start(uint8_t address);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "../I2C/I2C.h"
|
|
||||||
#include "MPU6050.h"
|
#include "MPU6050.h"
|
||||||
|
#include "../I2C/I2C.h"
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <util/delay.h>
|
#include <util/delay.h>
|
||||||
|
|
||||||
@@ -76,7 +76,6 @@ void mpu6050_set_gyro_offsets(int16_t x, int16_t y, int16_t z) {
|
|||||||
gyro_offset_y = y;
|
gyro_offset_y = y;
|
||||||
gyro_offset_z = z;
|
gyro_offset_z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mpu6050_read_gyro(float *gx, float *gy, float *gz) {
|
void mpu6050_read_gyro(float *gx, float *gy, float *gz) {
|
||||||
uint8_t buf[6];
|
uint8_t buf[6];
|
||||||
mpu6050_read_burst(MPU6050_REG_GYRO_XOUT_H, buf, 6);
|
mpu6050_read_burst(MPU6050_REG_GYRO_XOUT_H, buf, 6);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
// Адрес по умолчанию: AD0 = GND → 0x68 → сдвинутый = 0xD0
|
// Адрес по умолчанию: AD0 = GND → 0x68 → сдвинутый = 0xD0
|
||||||
// Если AD0 = VCC → 0x69 → 0xD2
|
// Если AD0 = VCC → 0x69 → 0xD2
|
||||||
#define MPU6050_ADDR 0xD0
|
#define MPU6050_ADDR 0xD0
|
||||||
@@ -18,4 +22,8 @@ void mpu6050_set_gyro_offsets(int16_t x, int16_t y, int16_t z);
|
|||||||
void mpu6050_calibrate_gyro(int16_t *gx_offset, int16_t *gy_offset,
|
void mpu6050_calibrate_gyro(int16_t *gx_offset, int16_t *gy_offset,
|
||||||
int16_t *gz_offset);
|
int16_t *gz_offset);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
#include "ST7789.h"
|
|
||||||
|
|
||||||
#define DC_HIGH() (ST7789_PORT |= (1 << DC_PIN))
|
|
||||||
#define DC_LOW() (ST7789_PORT &= ~(1 << DC_PIN))
|
|
||||||
#define RESET_HIGH() (ST7789_PORT |= (1 << RESET_PIN))
|
|
||||||
#define RESET_LOW() (ST7789_PORT &= ~(1 << RESET_PIN))
|
|
||||||
#define CS_HIGH() (ST7789_PORT |= (1 << CS_PIN))
|
|
||||||
#define CS_LOW() (ST7789_PORT &= ~(1 << CS_PIN))
|
|
||||||
|
|
||||||
static void _spi_init(void) {
|
|
||||||
// Настройка пинов SPI: MOSI (PB3), SCK (PB5) как выходы
|
|
||||||
DDRB |= (1 << PB3) | (1 << PB5);
|
|
||||||
|
|
||||||
// Включение SPI: Master, режим 0, частота F_CPU/4
|
|
||||||
SPCR = (1 << SPE) | (1 << MSTR);
|
|
||||||
SPSR = (1 << SPI2X); // Удвоение скорости (F_CPU/2)
|
|
||||||
}
|
|
||||||
static void _spi_write(uint8_t data) {
|
|
||||||
SPDR = data;
|
|
||||||
while (!(SPSR & (1 << SPIF)))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _st7789_write_command(uint8_t cmd) {
|
|
||||||
DC_LOW();
|
|
||||||
CS_LOW();
|
|
||||||
_spi_write(cmd);
|
|
||||||
CS_HIGH();
|
|
||||||
}
|
|
||||||
void _st7789_write_data(uint8_t data) {
|
|
||||||
DC_HIGH();
|
|
||||||
CS_LOW();
|
|
||||||
_spi_write(data);
|
|
||||||
CS_HIGH();
|
|
||||||
}
|
|
||||||
void _st7789_write_data_16(uint16_t data) {
|
|
||||||
DC_HIGH();
|
|
||||||
CS_LOW();
|
|
||||||
_spi_write(data >> 8); // Старший байт
|
|
||||||
_spi_write(data & 0xFF); // Младший байт
|
|
||||||
CS_HIGH();
|
|
||||||
}
|
|
||||||
void _st7789_set_address_window(uint16_t x0, uint16_t y0, uint16_t x1,
|
|
||||||
uint16_t y1) {
|
|
||||||
// Установка столбцов (CASET)
|
|
||||||
_st7789_write_command(0x2A);
|
|
||||||
_st7789_write_data(x0 >> 8);
|
|
||||||
_st7789_write_data(x0 & 0xFF);
|
|
||||||
_st7789_write_data(x1 >> 8);
|
|
||||||
_st7789_write_data(x1 & 0xFF);
|
|
||||||
|
|
||||||
// Установка строк (RASET)
|
|
||||||
_st7789_write_command(0x2B);
|
|
||||||
_st7789_write_data(y0 >> 8);
|
|
||||||
_st7789_write_data(y0 & 0xFF);
|
|
||||||
_st7789_write_data(y1 >> 8);
|
|
||||||
_st7789_write_data(y1 & 0xFF);
|
|
||||||
|
|
||||||
// Команда записи в память (RAMWR)
|
|
||||||
_st7789_write_command(0x2C);
|
|
||||||
}
|
|
||||||
|
|
||||||
void st7789_init(void) {
|
|
||||||
// Настройка пинов управления
|
|
||||||
ST7789_DDR |= (1 << DC_PIN) | (1 << RESET_PIN) | (1 << CS_PIN);
|
|
||||||
|
|
||||||
// Инициализация SPI
|
|
||||||
_spi_init();
|
|
||||||
|
|
||||||
// Процедура сброса дисплея
|
|
||||||
RESET_HIGH();
|
|
||||||
_delay_ms(10);
|
|
||||||
RESET_LOW();
|
|
||||||
_delay_ms(10);
|
|
||||||
RESET_HIGH();
|
|
||||||
_delay_ms(150);
|
|
||||||
|
|
||||||
// Последовательность инициализации ST7789
|
|
||||||
_st7789_write_command(0x01); // SWRESET: программный сброс
|
|
||||||
_delay_ms(150);
|
|
||||||
|
|
||||||
_st7789_write_command(0x11); // SLPOUT: выход из спящего режима
|
|
||||||
_delay_ms(255);
|
|
||||||
|
|
||||||
_st7789_write_command(0x3A); // COLMOD: установка формата цвета
|
|
||||||
_st7789_write_data(0x05); // 16 бит на пиксель (RGB565)
|
|
||||||
|
|
||||||
// ВАЖНО: Установка горизонтальной ориентации
|
|
||||||
_st7789_write_command(0x36); // MADCTL: управление ориентацией
|
|
||||||
_st7789_write_data(MADCTL_LANDSCAPE); // Горизонтальная ориентация
|
|
||||||
|
|
||||||
// Дополнительные настройки для лучшего отображения
|
|
||||||
_st7789_write_command(0xB2); // PORCTRL: настройка porch
|
|
||||||
_st7789_write_data(0x0C);
|
|
||||||
_st7789_write_data(0x0C);
|
|
||||||
_st7789_write_data(0x00);
|
|
||||||
_st7789_write_data(0x33);
|
|
||||||
_st7789_write_data(0x33);
|
|
||||||
|
|
||||||
_st7789_write_command(0xB7); // GCTRL: настройка gate control
|
|
||||||
_st7789_write_data(0x35);
|
|
||||||
|
|
||||||
_st7789_write_command(0xBB); // VCOMS: настройка VCOM
|
|
||||||
_st7789_write_data(0x2B);
|
|
||||||
|
|
||||||
_st7789_write_command(0xC0); // LCMCTRL: настройка LCM
|
|
||||||
_st7789_write_data(0x2C);
|
|
||||||
|
|
||||||
_st7789_write_command(0xC2); // VDVVRHEN: настройка VDV и VRH
|
|
||||||
_st7789_write_data(0x01);
|
|
||||||
_st7789_write_data(0xFF);
|
|
||||||
|
|
||||||
_st7789_write_command(0xC3); // VRHS: настройка VRH
|
|
||||||
_st7789_write_data(0x11);
|
|
||||||
|
|
||||||
_st7789_write_command(0xC4); // VDVS: настройка VDV
|
|
||||||
_st7789_write_data(0x20);
|
|
||||||
|
|
||||||
_st7789_write_command(0xC6); // FRCTRL2: настройка частоты
|
|
||||||
_st7789_write_data(0x0F);
|
|
||||||
|
|
||||||
_st7789_write_command(0xD0); // PWCTRL1: настройка питания
|
|
||||||
_st7789_write_data(0xA4);
|
|
||||||
_st7789_write_data(0xA1);
|
|
||||||
|
|
||||||
// Включение нормального режима и дисплея
|
|
||||||
_st7789_write_command(0x21); // INVON: инверсия цветов (опционально)
|
|
||||||
_st7789_write_command(0x13); // NORON: нормальный режим
|
|
||||||
_delay_ms(10);
|
|
||||||
|
|
||||||
_st7789_write_command(0x29); // DISPON: включение дисплея
|
|
||||||
_delay_ms(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void st7789_fill_screen(uint16_t color) {
|
|
||||||
// Установка окна на весь экран
|
|
||||||
_st7789_set_address_window(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
|
|
||||||
|
|
||||||
// Быстрая заливка через непрерывную передачу
|
|
||||||
DC_HIGH();
|
|
||||||
CS_LOW();
|
|
||||||
|
|
||||||
uint32_t total_pixels = (uint32_t)DISPLAY_WIDTH * DISPLAY_HEIGHT;
|
|
||||||
|
|
||||||
// Оптимизированная заливка экрана
|
|
||||||
for (uint32_t i = 0; i < total_pixels; i++) {
|
|
||||||
_spi_write(color >> 8); // Старший байт цвета
|
|
||||||
_spi_write(color & 0xFF); // Младший байт цвета
|
|
||||||
}
|
|
||||||
|
|
||||||
CS_HIGH();
|
|
||||||
}
|
|
||||||
|
|
||||||
void st7789_draw_hline(uint16_t x, uint16_t y, uint16_t length,
|
|
||||||
uint16_t color) {
|
|
||||||
// Проверка границ
|
|
||||||
if (y >= DISPLAY_HEIGHT)
|
|
||||||
return;
|
|
||||||
if (x >= DISPLAY_WIDTH)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint16_t x_end = x + length - 1;
|
|
||||||
if (x_end >= DISPLAY_WIDTH)
|
|
||||||
x_end = DISPLAY_WIDTH - 1;
|
|
||||||
|
|
||||||
_st7789_set_address_window(x, y, x_end, y);
|
|
||||||
|
|
||||||
DC_HIGH();
|
|
||||||
CS_LOW();
|
|
||||||
|
|
||||||
for (uint16_t i = x; i <= x_end; i++) {
|
|
||||||
_spi_write(color >> 8);
|
|
||||||
_spi_write(color & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
CS_HIGH();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Рисование вертикальной линии
|
|
||||||
* @param x X координата
|
|
||||||
* @param y Начальная Y координата
|
|
||||||
* @param length Длина линии
|
|
||||||
* @param color Цвет линии
|
|
||||||
*/
|
|
||||||
void st7789_draw_vline(uint16_t x, uint16_t y, uint16_t length,
|
|
||||||
uint16_t color) {
|
|
||||||
// Проверка границ
|
|
||||||
if (x >= DISPLAY_WIDTH)
|
|
||||||
return;
|
|
||||||
if (y >= DISPLAY_HEIGHT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint16_t y_end = y + length - 1;
|
|
||||||
if (y_end >= DISPLAY_HEIGHT)
|
|
||||||
y_end = DISPLAY_HEIGHT - 1;
|
|
||||||
|
|
||||||
_st7789_set_address_window(x, y, x, y_end);
|
|
||||||
|
|
||||||
DC_HIGH();
|
|
||||||
CS_LOW();
|
|
||||||
|
|
||||||
for (uint16_t i = y; i <= y_end; i++) {
|
|
||||||
_spi_write(color >> 8);
|
|
||||||
_spi_write(color & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
CS_HIGH();
|
|
||||||
}
|
|
||||||
|
|
||||||
void st7789_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
|
||||||
uint16_t color) {
|
|
||||||
// Оптимизация для горизонтальных и вертикальных линий
|
|
||||||
if (y0 == y1) {
|
|
||||||
if (x0 < x1)
|
|
||||||
st7789_draw_hline(x0, y0, x1 - x0 + 1, color);
|
|
||||||
else
|
|
||||||
st7789_draw_hline(x1, y0, x0 - x1 + 1, color);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x0 == x1) {
|
|
||||||
if (y0 < y1)
|
|
||||||
st7789_draw_vline(x0, y0, y1 - y0 + 1, color);
|
|
||||||
else
|
|
||||||
st7789_draw_vline(x0, y1, y0 - y1 + 1, color);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Алгоритм Брезенхэма для произвольных линий
|
|
||||||
int16_t dx = (x1 > x0) ? (x1 - x0) : (x0 - x1);
|
|
||||||
int16_t dy = (y1 > y0) ? (y1 - y0) : (y0 - y1);
|
|
||||||
int16_t sx = (x0 < x1) ? 1 : -1;
|
|
||||||
int16_t sy = (y0 < y1) ? 1 : -1;
|
|
||||||
int16_t err = dx - dy;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
// Рисование пикселя (с проверкой границ)
|
|
||||||
if (x0 < DISPLAY_WIDTH && y0 < DISPLAY_HEIGHT) {
|
|
||||||
_st7789_set_address_window(x0, y0, x0, y0);
|
|
||||||
_st7789_write_data_16(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x0 == x1 && y0 == y1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
int16_t e2 = 2 * err;
|
|
||||||
if (e2 > -dy) {
|
|
||||||
err -= dy;
|
|
||||||
x0 += sx;
|
|
||||||
}
|
|
||||||
if (e2 < dx) {
|
|
||||||
err += dx;
|
|
||||||
y0 += sy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#ifndef ST7789_H
|
|
||||||
#define ST7789_H
|
|
||||||
|
|
||||||
#include <avr/io.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <util/delay.h>
|
|
||||||
|
|
||||||
#define RGB565(r, g, b) \
|
|
||||||
((((255 - r) & 0xF8) << 8) | (((255 - g) & 0xFC) << 3) | ((255 - b) >> 3))
|
|
||||||
|
|
||||||
#define COLOR_BLACK RGB565(0, 0, 0)
|
|
||||||
#define COLOR_WHITE RGB565(255, 255, 255)
|
|
||||||
|
|
||||||
#define DISPLAY_WIDTH 160
|
|
||||||
#define DISPLAY_HEIGHT 128
|
|
||||||
#define ST7789_PORT PORTB
|
|
||||||
#define ST7789_DDR DDRB
|
|
||||||
#define DC_PIN PB0 // Data/Command
|
|
||||||
#define RESET_PIN PB1 // Reset
|
|
||||||
#define CS_PIN PB2 // Chip Select
|
|
||||||
|
|
||||||
#define MADCTL_MY 0x80 // Page Address Order (обратный по Y)
|
|
||||||
#define MADCTL_MX 0x40 // Column Address Order (обратный по X)
|
|
||||||
#define MADCTL_MV 0x20 // Page/Column Order (перестановка X/Y)
|
|
||||||
#define MADCTL_ML 0x10 // Line Address Order (обратный порядок строк)
|
|
||||||
#define MADCTL_RGB 0x00 // RGB порядок цветов
|
|
||||||
#define MADCTL_BGR 0x08 // BGR порядок цветов
|
|
||||||
#define MADCTL_MH 0x04 // Display Data Latch Order
|
|
||||||
#define MADCTL_LANDSCAPE (MADCTL_MV | MADCTL_MX)
|
|
||||||
|
|
||||||
void st7789_init(void);
|
|
||||||
|
|
||||||
void st7789_fill_screen(uint16_t color);
|
|
||||||
void st7789_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
|
||||||
uint16_t color);
|
|
||||||
|
|
||||||
void st7789_draw_hline(uint16_t x, uint16_t y, uint16_t length, uint16_t color);
|
|
||||||
void st7789_draw_vline(uint16_t x, uint16_t y, uint16_t length, uint16_t color);
|
|
||||||
|
|
||||||
// void _st7789_write_command(uint8_t cmd);
|
|
||||||
// void _st7789_write_data(uint8_t data);
|
|
||||||
// void _st7789_write_data_16(uint16_t data);
|
|
||||||
// void _st7789_set_address_window(uint16_t x0, uint16_t y0, uint16_t x1,
|
|
||||||
// uint16_t y1);
|
|
||||||
|
|
||||||
#endif // ST7789_H
|
|
||||||
732
src/platforms/micro/lib/TVout/TVout.cpp
Normal file
732
src/platforms/micro/lib/TVout/TVout.cpp
Normal file
@@ -0,0 +1,732 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010 Myles Metzer
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
This library provides a simple method for outputting data to a tv
|
||||||
|
from a frame buffer stored in sram. This implementation is done
|
||||||
|
completly by interupt and will return give as much cpu time to the
|
||||||
|
application as possible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TVout.h"
|
||||||
|
|
||||||
|
/* call this to start video output with the default resolution.
|
||||||
|
Arguments:
|
||||||
|
mode: The video standard to follow:
|
||||||
|
PAL = 1 = _PAL
|
||||||
|
NTSC = 0 = _NTSC
|
||||||
|
Returns:
|
||||||
|
0 if no error.
|
||||||
|
4 if there is not enough memory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t TVout::begin(uint8_t mode) { return begin(mode, 128, 96); }
|
||||||
|
|
||||||
|
/* call this to start video output with a specified resolution.
|
||||||
|
Arguments:
|
||||||
|
mode: The video standard to follow:
|
||||||
|
PAL = 1 = _PAL
|
||||||
|
NTSC = 0 = _NTSC
|
||||||
|
x: Horizonal resolution must be divisable by 8.
|
||||||
|
y: Vertical resolution.
|
||||||
|
Returns:
|
||||||
|
0 if no error.
|
||||||
|
1 if x is not divisable by 8.
|
||||||
|
2 if y is to large (NTSC only cannot fill PAL vertical resolution by
|
||||||
|
8bit limit) 4 if there is not enough memory for the frame buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint8_t TVout::begin(uint8_t mode, uint8_t x, uint8_t y) {
|
||||||
|
// check if x is divisable by 8
|
||||||
|
if (x % 8) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
x = x / 8;
|
||||||
|
screen = (unsigned char *)malloc(x * y * sizeof(unsigned char));
|
||||||
|
if (screen == NULL) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
_cursorX = 0;
|
||||||
|
_cursorY = 0;
|
||||||
|
render_setup(mode, x, y, screen);
|
||||||
|
clearScreen();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop video render and free the used memory
|
||||||
|
void TVout::end() {
|
||||||
|
TIMSK1 = 0;
|
||||||
|
free(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the Horizontal resolution of the screen
|
||||||
|
Returns: The horizonal resolution.
|
||||||
|
*/
|
||||||
|
unsigned int TVout::hres() { return display.hres * 8; }
|
||||||
|
|
||||||
|
/* Gets the Vertical resolution of the screen
|
||||||
|
Returns: The vertical resolution
|
||||||
|
*/
|
||||||
|
unsigned int TVout::vres() { return display.vres; }
|
||||||
|
|
||||||
|
/* Return the number of characters that will fit on a line
|
||||||
|
Returns: The number of characters that will fit on a text line starting
|
||||||
|
from x = 0. Will return -1 for dynamic width fonts as this cannot be
|
||||||
|
determined.
|
||||||
|
*/
|
||||||
|
/* Delay for x ms
|
||||||
|
The resolution is 16ms for NTSC and 20ms for PAL
|
||||||
|
Arguments:
|
||||||
|
x: The number of ms this function should consume.
|
||||||
|
*/
|
||||||
|
void TVout::delay(unsigned int x) {
|
||||||
|
unsigned long time = millis() + x;
|
||||||
|
while (millis() < time)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delay for x frames, exits at the end of the last display line.
|
||||||
|
delayFrame(1) is useful prior to drawing so there is little/no flicker.
|
||||||
|
Arguments:
|
||||||
|
x: The number of frames to delay for.
|
||||||
|
*/
|
||||||
|
void TVout::delayFrame(unsigned int x) {
|
||||||
|
int stop_line = (int)(display.start_render +
|
||||||
|
(display.vres * (display.vscale_const + 1))) +
|
||||||
|
1;
|
||||||
|
while (x) {
|
||||||
|
while (display.scanLine != stop_line)
|
||||||
|
;
|
||||||
|
while (display.scanLine == stop_line)
|
||||||
|
;
|
||||||
|
x--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the time in ms since begin was called.
|
||||||
|
The resolution is 16ms for NTSC and 20ms for PAL
|
||||||
|
Returns: The time in ms since video generation has started.
|
||||||
|
*/
|
||||||
|
unsigned long TVout::millis() {
|
||||||
|
if (display.lines_frame == _NTSC_LINE_FRAME) {
|
||||||
|
return display.frames * _NTSC_TIME_SCANLINE * _NTSC_LINE_FRAME / 1000;
|
||||||
|
} else {
|
||||||
|
return display.frames * _PAL_TIME_SCANLINE * _PAL_LINE_FRAME / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TVout::clearScreen() { fill(0); }
|
||||||
|
|
||||||
|
void TVout::invert() { fill(2); }
|
||||||
|
|
||||||
|
/* Fill the screen with some color.
|
||||||
|
Arguments:
|
||||||
|
color: The color to fill the screen with
|
||||||
|
*/
|
||||||
|
void TVout::fill(uint8_t color) {
|
||||||
|
switch (color) {
|
||||||
|
case BLACK:
|
||||||
|
_cursorX = 0;
|
||||||
|
_cursorY = 0;
|
||||||
|
for (int i = 0; i < (display.hres) * display.vres; i++)
|
||||||
|
display.screen[i] = 0;
|
||||||
|
break;
|
||||||
|
case WHITE:
|
||||||
|
_cursorX = 0;
|
||||||
|
_cursorY = 0;
|
||||||
|
for (int i = 0; i < (display.hres) * display.vres; i++)
|
||||||
|
display.screen[i] = 0xFF;
|
||||||
|
break;
|
||||||
|
case INVERT:
|
||||||
|
for (int i = 0; i < display.hres * display.vres; i++)
|
||||||
|
display.screen[i] = ~display.screen[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the color of a pixel
|
||||||
|
Arguments:
|
||||||
|
x: The x coordinate of the pixel.
|
||||||
|
y: The y coordinate of the pixel.
|
||||||
|
c: The color of the pixel
|
||||||
|
*/
|
||||||
|
void TVout::setPixel(uint8_t x, uint8_t y, char c) {
|
||||||
|
if (x >= display.hres * 8 || y >= display.vres)
|
||||||
|
return;
|
||||||
|
sp(x, y, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the color of the pixel at x, y
|
||||||
|
Arguments:
|
||||||
|
x: The x coordinate of the pixel.
|
||||||
|
y: The y coordinate of the pixel.
|
||||||
|
Returns:
|
||||||
|
The color of the pixel
|
||||||
|
*/
|
||||||
|
bool TVout::getPixel(uint8_t x, uint8_t y) {
|
||||||
|
if (x >= display.hres * 8 || y >= display.vres)
|
||||||
|
return false;
|
||||||
|
if (display.screen[x / 8 + y * display.hres] & (0x80 >> (x & 7)))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw a line from one point to another
|
||||||
|
Arguments:
|
||||||
|
x0: The x coordinate of point 0.
|
||||||
|
y0: The y coordinate of point 0.
|
||||||
|
x1: The x coordinate of point 1.
|
||||||
|
y1: The y coordinate of point 1.
|
||||||
|
c: The color of the line.
|
||||||
|
*/
|
||||||
|
void TVout::drawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, char c) {
|
||||||
|
if (x0 > display.hres * 8 || y0 > display.vres || x1 > display.hres * 8 ||
|
||||||
|
y1 > display.vres)
|
||||||
|
return;
|
||||||
|
if (x0 == x1)
|
||||||
|
drawColumn(x0, y0, y1, c);
|
||||||
|
else if (y0 == y1)
|
||||||
|
drawRow(y0, x0, x1, c);
|
||||||
|
else {
|
||||||
|
int e;
|
||||||
|
int dx, dy, j, temp;
|
||||||
|
char s1, s2, xchange;
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
x = x0;
|
||||||
|
y = y0;
|
||||||
|
|
||||||
|
// take absolute value
|
||||||
|
if (x1 < x0) {
|
||||||
|
dx = x0 - x1;
|
||||||
|
s1 = -1;
|
||||||
|
} else if (x1 == x0) {
|
||||||
|
dx = 0;
|
||||||
|
s1 = 0;
|
||||||
|
} else {
|
||||||
|
dx = x1 - x0;
|
||||||
|
s1 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y1 < y0) {
|
||||||
|
dy = y0 - y1;
|
||||||
|
s2 = -1;
|
||||||
|
} else if (y1 == y0) {
|
||||||
|
dy = 0;
|
||||||
|
s2 = 0;
|
||||||
|
} else {
|
||||||
|
dy = y1 - y0;
|
||||||
|
s2 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xchange = 0;
|
||||||
|
|
||||||
|
if (dy > dx) {
|
||||||
|
temp = dx;
|
||||||
|
dx = dy;
|
||||||
|
dy = temp;
|
||||||
|
xchange = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = ((int)dy << 1) - dx;
|
||||||
|
|
||||||
|
for (j = 0; j <= dx; j++) {
|
||||||
|
sp(x, y, c);
|
||||||
|
|
||||||
|
if (e >= 0) {
|
||||||
|
if (xchange == 1) {
|
||||||
|
x = x + s1;
|
||||||
|
} else {
|
||||||
|
y = y + s2;
|
||||||
|
}
|
||||||
|
e = e - ((int)dx << 1);
|
||||||
|
}
|
||||||
|
if (xchange == 1) {
|
||||||
|
y = y + s2;
|
||||||
|
} else {
|
||||||
|
x = x + s1;
|
||||||
|
}
|
||||||
|
e = e + ((int)dy << 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill a row from one point to another
|
||||||
|
Argument:
|
||||||
|
line: The row that fill will be performed on.
|
||||||
|
x0: edge 0 of the fill.
|
||||||
|
x1: edge 1 of the fill.
|
||||||
|
c: the color of the fill.
|
||||||
|
*/
|
||||||
|
void TVout::drawRow(uint8_t line, uint16_t x0, uint16_t x1, uint8_t c) {
|
||||||
|
uint8_t lbit, rbit;
|
||||||
|
|
||||||
|
if (x0 == x1) {
|
||||||
|
setPixel(x0, line, c);
|
||||||
|
} else {
|
||||||
|
if (x0 > x1) {
|
||||||
|
lbit = x0;
|
||||||
|
x0 = x1;
|
||||||
|
x1 = lbit;
|
||||||
|
}
|
||||||
|
lbit = 0xff >> (x0 & 7);
|
||||||
|
x0 = x0 / 8 + display.hres * line;
|
||||||
|
rbit = ~(0xff >> (x1 & 7));
|
||||||
|
x1 = x1 / 8 + display.hres * line;
|
||||||
|
if (x0 == x1) {
|
||||||
|
lbit = lbit & rbit;
|
||||||
|
rbit = 0;
|
||||||
|
}
|
||||||
|
if (c == WHITE) {
|
||||||
|
screen[x0++] |= lbit;
|
||||||
|
while (x0 < x1) {
|
||||||
|
screen[x0++] = 0xff;
|
||||||
|
}
|
||||||
|
screen[x0] |= rbit;
|
||||||
|
} else if (c == BLACK) {
|
||||||
|
screen[x0++] &= ~lbit;
|
||||||
|
while (x0 < x1) {
|
||||||
|
screen[x0++] = 0;
|
||||||
|
}
|
||||||
|
screen[x0] &= ~rbit;
|
||||||
|
} else if (c == INVERT) {
|
||||||
|
screen[x0++] ^= lbit;
|
||||||
|
while (x0 < x1) {
|
||||||
|
screen[x0++] ^= 0xff;
|
||||||
|
}
|
||||||
|
screen[x0] ^= rbit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill a column from one point to another
|
||||||
|
Argument:
|
||||||
|
row: The row that fill will be performed on.
|
||||||
|
y0: edge 0 of the fill.
|
||||||
|
y1: edge 1 of the fill.
|
||||||
|
c: the color of the fill.
|
||||||
|
*/
|
||||||
|
void TVout::drawColumn(uint8_t row, uint16_t y0, uint16_t y1, uint8_t c) {
|
||||||
|
unsigned char bit;
|
||||||
|
int byte;
|
||||||
|
|
||||||
|
if (y0 == y1) {
|
||||||
|
setPixel(row, y0, c);
|
||||||
|
} else {
|
||||||
|
if (y1 < y0) {
|
||||||
|
bit = y0;
|
||||||
|
y0 = y1;
|
||||||
|
y1 = bit;
|
||||||
|
}
|
||||||
|
bit = 0x80 >> (row & 7);
|
||||||
|
byte = row / 8 + y0 * display.hres;
|
||||||
|
if (c == WHITE) {
|
||||||
|
while (y0 <= y1) {
|
||||||
|
screen[byte] |= bit;
|
||||||
|
byte += display.hres;
|
||||||
|
y0++;
|
||||||
|
}
|
||||||
|
} else if (c == BLACK) {
|
||||||
|
while (y0 <= y1) {
|
||||||
|
screen[byte] &= ~bit;
|
||||||
|
byte += display.hres;
|
||||||
|
y0++;
|
||||||
|
}
|
||||||
|
} else if (c == INVERT) {
|
||||||
|
while (y0 <= y1) {
|
||||||
|
screen[byte] ^= bit;
|
||||||
|
byte += display.hres;
|
||||||
|
y0++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw a rectangle at x,y with a specified width and height
|
||||||
|
Arguments:
|
||||||
|
x0: The x coordinate of upper left corner of the rectangle.
|
||||||
|
y0: The y coordinate of upper left corner of the rectangle.
|
||||||
|
w: The widht of the rectangle.
|
||||||
|
h: The height of the rectangle.
|
||||||
|
c: The color of the rectangle.
|
||||||
|
fc: The fill color of the rectangle.
|
||||||
|
*/
|
||||||
|
void TVout::drawRect(uint8_t x0, uint8_t y0, uint8_t w, uint8_t h, char c,
|
||||||
|
char fc) {
|
||||||
|
if (fc != -1) {
|
||||||
|
for (unsigned char i = y0; i < y0 + h; i++) {
|
||||||
|
drawRow(i, x0, x0 + w, fc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawLine(x0, y0, x0 + w, y0, c);
|
||||||
|
drawLine(x0, y0, x0, y0 + h, c);
|
||||||
|
drawLine(x0 + w, y0, x0 + w, y0 + h, c);
|
||||||
|
drawLine(x0, y0 + h, x0 + w, y0 + h, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw a circle given a coordinate x,y and radius both filled and non filled.
|
||||||
|
Arguments:
|
||||||
|
x0: The x coordinate of the center of the circle.
|
||||||
|
y0: The y coordinate of the center of the circle.
|
||||||
|
radius: The radius of the circle.
|
||||||
|
c: The color of the circle.
|
||||||
|
fc: The color to fill the circle.
|
||||||
|
*/
|
||||||
|
void TVout::drawCircle(uint8_t x0, uint8_t y0, uint8_t radius, char c,
|
||||||
|
char fc) {
|
||||||
|
int f = 1 - radius;
|
||||||
|
int ddF_x = 1;
|
||||||
|
int ddF_y = -2 * radius;
|
||||||
|
int x = 0;
|
||||||
|
int y = radius;
|
||||||
|
uint8_t pyy = y, pyx = x;
|
||||||
|
|
||||||
|
// there is a fill color
|
||||||
|
if (fc != -1) {
|
||||||
|
drawRow(y0, x0 - radius, x0 + radius, fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
sp(x0, y0 + radius, c);
|
||||||
|
sp(x0, y0 - radius, c);
|
||||||
|
sp(x0 + radius, y0, c);
|
||||||
|
sp(x0 - radius, y0, c);
|
||||||
|
|
||||||
|
while (x < y) {
|
||||||
|
if (f >= 0) {
|
||||||
|
y--;
|
||||||
|
ddF_y += 2;
|
||||||
|
f += ddF_y;
|
||||||
|
}
|
||||||
|
x++;
|
||||||
|
ddF_x += 2;
|
||||||
|
f += ddF_x;
|
||||||
|
|
||||||
|
// there is a fill color
|
||||||
|
if (fc != -1) {
|
||||||
|
// prevent double draws on the same rows
|
||||||
|
if (pyy != y) {
|
||||||
|
drawRow(y0 + y, x0 - x, x0 + x, fc);
|
||||||
|
drawRow(y0 - y, x0 - x, x0 + x, fc);
|
||||||
|
}
|
||||||
|
if (pyx != x && x != y) {
|
||||||
|
drawRow(y0 + x, x0 - y, x0 + y, fc);
|
||||||
|
drawRow(y0 - x, x0 - y, x0 + y, fc);
|
||||||
|
}
|
||||||
|
pyy = y;
|
||||||
|
pyx = x;
|
||||||
|
}
|
||||||
|
sp(x0 + x, y0 + y, c);
|
||||||
|
sp(x0 - x, y0 + y, c);
|
||||||
|
sp(x0 + x, y0 - y, c);
|
||||||
|
sp(x0 - x, y0 - y, c);
|
||||||
|
sp(x0 + y, y0 + x, c);
|
||||||
|
sp(x0 - y, y0 + x, c);
|
||||||
|
sp(x0 + y, y0 - x, c);
|
||||||
|
sp(x0 - y, y0 - x, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* place a bitmap at x,y where the bitmap is defined as
|
||||||
|
{width,height,imagedata....} Arguments: x: The x coordinate of the upper
|
||||||
|
left corner. y: The y coordinate of the upper left corner. bmp: The
|
||||||
|
bitmap data to print. i: The offset into the image data to start at.
|
||||||
|
This is mainly used for fonts default = 0 width: Override the bitmap width.
|
||||||
|
This is mainly used for fonts. default = 0 (do not override) height: Override
|
||||||
|
the bitmap height. This is mainly used for fonts. default = 0 (do not
|
||||||
|
override)
|
||||||
|
*/
|
||||||
|
void TVout::bitmap(uint8_t x, uint8_t y, const unsigned char *bmp, uint16_t i,
|
||||||
|
uint8_t width, uint8_t lines) {
|
||||||
|
uint8_t temp, lshift, rshift, save, xtra;
|
||||||
|
uint16_t si = 0;
|
||||||
|
|
||||||
|
rshift = x & 7;
|
||||||
|
lshift = 8 - rshift;
|
||||||
|
if (width == 0) {
|
||||||
|
width = pgm_read_byte((uint32_t)(bmp) + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (lines == 0) {
|
||||||
|
lines = pgm_read_byte((uint32_t)(bmp) + i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width & 7) {
|
||||||
|
xtra = width & 7;
|
||||||
|
width = width / 8;
|
||||||
|
width++;
|
||||||
|
} else {
|
||||||
|
xtra = 8;
|
||||||
|
width = width / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t l = 0; l < lines; l++) {
|
||||||
|
si = (y + l) * display.hres + x / 8;
|
||||||
|
if (width == 1) {
|
||||||
|
temp = 0xff >> rshift + xtra;
|
||||||
|
} else {
|
||||||
|
temp = 0;
|
||||||
|
}
|
||||||
|
save = screen[si];
|
||||||
|
screen[si] &= ((0xff << lshift) | temp);
|
||||||
|
temp = pgm_read_byte((uint32_t)(bmp) + i++);
|
||||||
|
screen[si++] |= temp >> rshift;
|
||||||
|
for (uint16_t b = i + width - 1; i < b; i++) {
|
||||||
|
save = screen[si];
|
||||||
|
screen[si] = temp << lshift;
|
||||||
|
temp = pgm_read_byte((uint32_t)(bmp) + i);
|
||||||
|
screen[si++] |= temp >> rshift;
|
||||||
|
}
|
||||||
|
if (rshift + xtra < 8) {
|
||||||
|
screen[si - 1] |= (save & (0xff >> rshift + xtra));
|
||||||
|
}
|
||||||
|
if (rshift + xtra - 8 > 0) {
|
||||||
|
screen[si] &= (0xff >> rshift + xtra - 8);
|
||||||
|
}
|
||||||
|
screen[si] |= temp << lshift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shift the pixel buffer in any direction
|
||||||
|
This function will shift the screen in a direction by any distance.
|
||||||
|
Arguments:
|
||||||
|
distance: The distance to shift the screen
|
||||||
|
direction: The direction to shift the screen the direction and the
|
||||||
|
integer values: UP = 0 DOWN = 1 LEFT = 2 RIGHT = 3
|
||||||
|
*/
|
||||||
|
|
||||||
|
void TVout::shift(uint8_t distance, uint8_t direction) {
|
||||||
|
uint8_t *src;
|
||||||
|
uint8_t *dst;
|
||||||
|
uint8_t *end;
|
||||||
|
uint8_t shift;
|
||||||
|
uint8_t tmp;
|
||||||
|
switch (direction) {
|
||||||
|
case UP:
|
||||||
|
dst = display.screen;
|
||||||
|
src = display.screen + distance * display.hres;
|
||||||
|
end = display.screen + display.vres * display.hres;
|
||||||
|
while (src <= end) {
|
||||||
|
*dst = *src;
|
||||||
|
*src = 0;
|
||||||
|
dst++;
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DOWN:
|
||||||
|
dst = display.screen + display.vres * display.hres;
|
||||||
|
src = dst - distance * display.hres;
|
||||||
|
end = display.screen;
|
||||||
|
while (src >= end) {
|
||||||
|
*dst = *src;
|
||||||
|
*src = 0;
|
||||||
|
dst--;
|
||||||
|
src--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LEFT:
|
||||||
|
shift = distance & 7;
|
||||||
|
for (uint8_t line = 0; line < display.vres; line++) {
|
||||||
|
dst = display.screen + display.hres * line;
|
||||||
|
src = dst + distance / 8;
|
||||||
|
end = dst + display.hres - 2;
|
||||||
|
while (src <= end) {
|
||||||
|
tmp = 0;
|
||||||
|
tmp = *src << shift;
|
||||||
|
*src = 0;
|
||||||
|
src++;
|
||||||
|
tmp |= *src >> (8 - shift);
|
||||||
|
*dst = tmp;
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
tmp = 0;
|
||||||
|
tmp = *src << shift;
|
||||||
|
*src = 0;
|
||||||
|
*dst = tmp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RIGHT:
|
||||||
|
shift = distance & 7;
|
||||||
|
for (uint8_t line = 0; line < display.vres; line++) {
|
||||||
|
dst = display.screen + display.hres - 1 + display.hres * line;
|
||||||
|
src = dst - distance / 8;
|
||||||
|
end = dst - display.hres + 2;
|
||||||
|
while (src >= end) {
|
||||||
|
tmp = 0;
|
||||||
|
tmp = *src >> shift;
|
||||||
|
*src = 0;
|
||||||
|
src--;
|
||||||
|
tmp |= *src << (8 - shift);
|
||||||
|
*dst = tmp;
|
||||||
|
dst--;
|
||||||
|
}
|
||||||
|
tmp = 0;
|
||||||
|
tmp = *src >> shift;
|
||||||
|
*src = 0;
|
||||||
|
*dst = tmp;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple tone generation
|
||||||
|
Arguments:
|
||||||
|
frequency: the frequency of the tone courtesy of adamwwolf
|
||||||
|
*/
|
||||||
|
void TVout::tone(unsigned int frequency) { tone(frequency, 0); }
|
||||||
|
|
||||||
|
/* Simple tone generation
|
||||||
|
Arguments:
|
||||||
|
frequency: the frequency of the tone
|
||||||
|
durationMS: the duration to play the tone in ms courtesy of adamwwolf
|
||||||
|
*/
|
||||||
|
void TVout::tone(unsigned int frequency, unsigned long durationMS) {
|
||||||
|
if (frequency == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TIMER 2
|
||||||
|
// this is init code
|
||||||
|
TCCR2A = 0;
|
||||||
|
TCCR2B = 0;
|
||||||
|
TCCR2A |= _BV(WGM21);
|
||||||
|
TCCR2B |= _BV(CS20);
|
||||||
|
// end init code
|
||||||
|
|
||||||
|
// most of this is taken from Tone.cpp from Arduino
|
||||||
|
uint8_t prescalarbits = 0b001;
|
||||||
|
uint32_t ocr = 0;
|
||||||
|
|
||||||
|
// set pb3 (digital pin 11) to output
|
||||||
|
DDR_SND |= _BV(SND_PIN);
|
||||||
|
|
||||||
|
// we are using an 8 bit timer, scan through prescalars to find the best fit
|
||||||
|
ocr = F_CPU / frequency / 2 - 1;
|
||||||
|
// ck/1: same for both timers
|
||||||
|
prescalarbits = 0b001;
|
||||||
|
if (ocr > 255) {
|
||||||
|
ocr = F_CPU / frequency / 2 / 8 - 1;
|
||||||
|
// ck/8: same for both timers
|
||||||
|
prescalarbits = 0b010;
|
||||||
|
|
||||||
|
if (ocr > 255) {
|
||||||
|
ocr = F_CPU / frequency / 2 / 32 - 1;
|
||||||
|
prescalarbits = 0b011;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ocr > 255) {
|
||||||
|
ocr = F_CPU / frequency / 2 / 64 - 1;
|
||||||
|
prescalarbits = TIMER == 0 ? 0b011 : 0b100;
|
||||||
|
if (ocr > 255) {
|
||||||
|
ocr = F_CPU / frequency / 2 / 128 - 1;
|
||||||
|
prescalarbits = 0b101;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ocr > 255) {
|
||||||
|
ocr = F_CPU / frequency / 2 / 256 - 1;
|
||||||
|
prescalarbits = TIMER == 0 ? 0b100 : 0b110;
|
||||||
|
if (ocr > 255) {
|
||||||
|
// can't do any better than / 1024
|
||||||
|
ocr = F_CPU / frequency / 2 / 1024 - 1;
|
||||||
|
prescalarbits = TIMER == 0 ? 0b101 : 0b111;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TCCR2B = prescalarbits;
|
||||||
|
|
||||||
|
if (durationMS > 0) {
|
||||||
|
// 60 here represents the framerate
|
||||||
|
remainingToneVsyncs = durationMS * 60 / 1000;
|
||||||
|
} else {
|
||||||
|
remainingToneVsyncs = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the OCR for the given timer,
|
||||||
|
OCR2A = ocr;
|
||||||
|
// set it to toggle the pin by itself
|
||||||
|
// set COM2A1 to 0
|
||||||
|
TCCR2A &= ~(_BV(COM2A1));
|
||||||
|
TCCR2A |= _BV(COM2A0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stops tone generation
|
||||||
|
*/
|
||||||
|
void TVout::noTone() {
|
||||||
|
TCCR2B = 0;
|
||||||
|
// set pin 11 to 0
|
||||||
|
PORT_SND &= ~(_BV(SND_PIN));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the vertical blank function call
|
||||||
|
The function passed to this function will be called one per frame. The
|
||||||
|
function should be quickish. Arguments: func: The function to call
|
||||||
|
*/
|
||||||
|
void TVout::setVBIHook(void (*func)()) { vbi_hook = func; }
|
||||||
|
|
||||||
|
/* set the horizonal blank function call
|
||||||
|
This function passed to this function will be called one per scan line.
|
||||||
|
The function MUST be VERY FAST(~2us max).
|
||||||
|
Arguments:
|
||||||
|
funct: The function to call
|
||||||
|
*/
|
||||||
|
void TVout::setHBIHook(void (*func)()) { hbi_hook = func; }
|
||||||
|
|
||||||
|
/* force the number of times to display each line
|
||||||
|
Arguments:
|
||||||
|
sfactor: The scale number of times to repeate each line.
|
||||||
|
*/
|
||||||
|
void TVout::forceVscale(char sfactor) {
|
||||||
|
delayFrame(1);
|
||||||
|
display.vscale_const = sfactor - 1;
|
||||||
|
display.vscale = sfactor - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* force the output start time of a scanline in micro seconds
|
||||||
|
Arguments:
|
||||||
|
time: The new output start time in micro seconds.
|
||||||
|
*/
|
||||||
|
void TVout::forceOutStart(uint8_t time) {
|
||||||
|
delayFrame(1);
|
||||||
|
display.output_delay = ((time * _CYCLES_PER_US) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* force the start line for active video
|
||||||
|
Arguments:
|
||||||
|
line: The new active video output start line
|
||||||
|
*/
|
||||||
|
void TVout::forceLineStart(uint8_t line) {
|
||||||
|
delayFrame(1);
|
||||||
|
display.start_render = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline version of setPixel that does not perform a bounds check
|
||||||
|
*/
|
||||||
|
static void inline sp(uint8_t x, uint8_t y, char c) {
|
||||||
|
if (c == 1) {
|
||||||
|
display.screen[(x / 8) + (y * display.hres)] |= 0x80 >> (x & 7);
|
||||||
|
} else if (c == 0) {
|
||||||
|
display.screen[(x / 8) + (y * display.hres)] &= ~0x80 >> (x & 7);
|
||||||
|
} else {
|
||||||
|
display.screen[(x / 8) + (y * display.hres)] ^= 0x80 >> (x & 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/platforms/micro/lib/TVout/TVout.h
Normal file
103
src/platforms/micro/lib/TVout/TVout.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010 Myles Metzer
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TVOUT_H
|
||||||
|
#define TVOUT_H
|
||||||
|
|
||||||
|
#include "video_gen.h"
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define PAL 1
|
||||||
|
#define NTSC 0
|
||||||
|
#define _PAL 1
|
||||||
|
#define _NTSC 0
|
||||||
|
|
||||||
|
#define WHITE 1
|
||||||
|
#define BLACK 0
|
||||||
|
#define INVERT 2
|
||||||
|
|
||||||
|
#define UP 0
|
||||||
|
#define DOWN 1
|
||||||
|
#define LEFT 2
|
||||||
|
#define RIGHT 3
|
||||||
|
|
||||||
|
#define DEC 10
|
||||||
|
#define HEX 16
|
||||||
|
#define OCT 8
|
||||||
|
#define BIN 2
|
||||||
|
#define BYTE 0
|
||||||
|
|
||||||
|
class TVout {
|
||||||
|
public:
|
||||||
|
uint8_t *screen;
|
||||||
|
uint8_t begin(uint8_t mode);
|
||||||
|
uint8_t begin(uint8_t mode, uint8_t x, uint8_t y);
|
||||||
|
void end();
|
||||||
|
|
||||||
|
// accessor functions
|
||||||
|
unsigned int hres();
|
||||||
|
unsigned int vres();
|
||||||
|
char charLine();
|
||||||
|
|
||||||
|
// flow control functions
|
||||||
|
void delay(unsigned int x);
|
||||||
|
void delayFrame(unsigned int x);
|
||||||
|
unsigned long millis();
|
||||||
|
|
||||||
|
// basic rendering functions
|
||||||
|
void clearScreen();
|
||||||
|
void invert();
|
||||||
|
void fill(uint8_t color);
|
||||||
|
void setPixel(uint8_t x, uint8_t y, char c);
|
||||||
|
bool getPixel(uint8_t x, uint8_t y);
|
||||||
|
void drawLine(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, char c);
|
||||||
|
void drawRow(uint8_t line, uint16_t x0, uint16_t x1, uint8_t c);
|
||||||
|
void drawColumn(uint8_t row, uint16_t y0, uint16_t y1, uint8_t c);
|
||||||
|
void drawRect(uint8_t x0, uint8_t y0, uint8_t w, uint8_t h, char c,
|
||||||
|
char fc = -1);
|
||||||
|
void drawCircle(uint8_t x0, uint8_t y0, uint8_t radius, char c, char fc = -1);
|
||||||
|
void bitmap(uint8_t x, uint8_t y, const unsigned char *bmp, uint16_t i = 0,
|
||||||
|
uint8_t width = 0, uint8_t lines = 0);
|
||||||
|
void shift(uint8_t distance, uint8_t direction);
|
||||||
|
|
||||||
|
// tone functions
|
||||||
|
void tone(unsigned int frequency, unsigned long durationMS);
|
||||||
|
void tone(unsigned int frequency);
|
||||||
|
void noTone();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// hook setup functions
|
||||||
|
void setVBIHook(void (*func)());
|
||||||
|
void setHBIHook(void (*func)());
|
||||||
|
|
||||||
|
// override setup functions
|
||||||
|
void forceVscale(char sfactor);
|
||||||
|
void forceOutStart(uint8_t time);
|
||||||
|
void forceLineStart(uint8_t line);
|
||||||
|
void incTxtLine();
|
||||||
|
|
||||||
|
uint8_t _cursorX, _cursorY;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void inline sp(uint8_t x, uint8_t y, char c);
|
||||||
|
#endif
|
||||||
119
src/platforms/micro/lib/TVout/spec/asm_macros.h
Normal file
119
src/platforms/micro/lib/TVout/spec/asm_macros.h
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include "hardware_setup.h"
|
||||||
|
|
||||||
|
#ifndef ASM_MACROS_H
|
||||||
|
#define ASM_MACROS_H
|
||||||
|
|
||||||
|
// delay macros
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
// delay 1 clock cycle.
|
||||||
|
".macro delay1\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 2 clock cycles
|
||||||
|
".macro delay2\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 3 clock cyles
|
||||||
|
".macro delay3\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 4 clock cylces
|
||||||
|
".macro delay4\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 5 clock cylces
|
||||||
|
".macro delay5\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 6 clock cylces
|
||||||
|
".macro delay6\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 7 clock cylces
|
||||||
|
".macro delay7\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 8 clock cylces
|
||||||
|
".macro delay8\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 9 clock cylces
|
||||||
|
".macro delay9\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// delay 10 clock cylces
|
||||||
|
".macro delay10\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"nop\n"
|
||||||
|
".endm\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
// common output macros, specific output macros at top of file
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
|
||||||
|
// save port 16 and clear the video bit
|
||||||
|
".macro svprt p\n\t"
|
||||||
|
"in r16,\\p\n\t"
|
||||||
|
ANDI_HWS
|
||||||
|
".endm\n"
|
||||||
|
|
||||||
|
// ouput 1 bit port safe
|
||||||
|
".macro o1bs p\n\t"
|
||||||
|
BLD_HWS
|
||||||
|
"out \\p,r16\n"
|
||||||
|
".endm\n"
|
||||||
|
);
|
||||||
|
#endif
|
||||||
146
src/platforms/micro/lib/TVout/spec/hardware_setup.h
Normal file
146
src/platforms/micro/lib/TVout/spec/hardware_setup.h
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
// sound is output on OC2A
|
||||||
|
// sync output is on OC1A
|
||||||
|
|
||||||
|
// ENABLE_FAST_OUTPUT chooses the highest bit of a port over the original output method
|
||||||
|
// comment out this line to switch back to the original output pins.
|
||||||
|
#define ENABLE_FAST_OUTPUT
|
||||||
|
|
||||||
|
#ifndef HARDWARE_SETUP_H
|
||||||
|
#define HARDWARE_SETUP_H
|
||||||
|
|
||||||
|
// device specific settings.
|
||||||
|
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
|
||||||
|
#if defined(ENABLE_FAST_OUTPUT)
|
||||||
|
#define PORT_VID PORTA
|
||||||
|
#define DDR_VID DDRA
|
||||||
|
#define VID_PIN 7
|
||||||
|
#else
|
||||||
|
//video
|
||||||
|
#define PORT_VID PORTB
|
||||||
|
#define DDR_VID DDRB
|
||||||
|
#define VID_PIN 6
|
||||||
|
#endif
|
||||||
|
//sync
|
||||||
|
#define PORT_SYNC PORTB
|
||||||
|
#define DDR_SYNC DDRB
|
||||||
|
#define SYNC_PIN 5
|
||||||
|
//sound
|
||||||
|
#define PORT_SND PORTB
|
||||||
|
#define DDR_SND DDRB
|
||||||
|
#define SND_PIN 4
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
|
||||||
|
//video
|
||||||
|
#if defined(ENABLE_FAST_OUTPUT)
|
||||||
|
#define PORT_VID PORTA
|
||||||
|
#define DDR_VID DDRA
|
||||||
|
#define VID_PIN 7
|
||||||
|
#else
|
||||||
|
#define PORT_VID PORTD
|
||||||
|
#define DDR_VID DDRD
|
||||||
|
#define VID_PIN 4
|
||||||
|
#endif
|
||||||
|
//sync
|
||||||
|
#define PORT_SYNC PORTD
|
||||||
|
#define DDR_SYNC DDRD
|
||||||
|
#define SYNC_PIN 5
|
||||||
|
//sound
|
||||||
|
#define PORT_SND PORTD
|
||||||
|
#define DDR_SND DDRD
|
||||||
|
#define SND_PIN 7
|
||||||
|
|
||||||
|
// Arduino UNO
|
||||||
|
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
|
||||||
|
//video
|
||||||
|
#if defined(ENABLE_FAST_OUTPUT)
|
||||||
|
#define PORT_VID PORTD
|
||||||
|
#define DDR_VID DDRD
|
||||||
|
#define VID_PIN 7
|
||||||
|
#else
|
||||||
|
#define PORT_VID PORTB
|
||||||
|
#define DDR_VID DDRB
|
||||||
|
#define VID_PIN 0
|
||||||
|
#endif
|
||||||
|
//sync
|
||||||
|
#define PORT_SYNC PORTB
|
||||||
|
#define DDR_SYNC DDRB
|
||||||
|
#define SYNC_PIN 1
|
||||||
|
//sound
|
||||||
|
#define PORT_SND PORTB
|
||||||
|
#define DDR_SND DDRB
|
||||||
|
#define SND_PIN 3
|
||||||
|
|
||||||
|
// Arduino DUE
|
||||||
|
#elif defined (__AVR_AT90USB1286__)
|
||||||
|
//video
|
||||||
|
#define PORT_VID PORTF
|
||||||
|
#define DDR_VID DDRF
|
||||||
|
#define VID_PIN 7
|
||||||
|
//sync
|
||||||
|
#define PORT_SYNC PORTB
|
||||||
|
#define DDR_SYNC DDRB
|
||||||
|
#define SYNC_PIN 5
|
||||||
|
//sound
|
||||||
|
#define PORT_SND PORTB
|
||||||
|
#define DDR_SND DDRB
|
||||||
|
#define SND_PIN 4
|
||||||
|
|
||||||
|
// Arduino Leonardo
|
||||||
|
#elif defined(__AVR_ATmega32U4__)
|
||||||
|
// video arduino pin 8
|
||||||
|
#define PORT_VID PORTB
|
||||||
|
#define DDR_VID DDRB
|
||||||
|
#define VID_PIN 4
|
||||||
|
// sync arduino pin 9
|
||||||
|
#define PORT_SYNC PORTB
|
||||||
|
#define DDR_SYNC DDRB
|
||||||
|
#define SYNC_PIN 5
|
||||||
|
// sound arduino pin 11
|
||||||
|
#define PORT_SND PORTB
|
||||||
|
#define DDR_SND DDRB
|
||||||
|
#define SND_PIN 7
|
||||||
|
#define TCCR2A TCCR0A
|
||||||
|
#define TCCR2B TCCR0B
|
||||||
|
#define OCR2A OCR0A
|
||||||
|
#define OCR2B OCR0B
|
||||||
|
#define COM2A0 COM0A0
|
||||||
|
#define COM2A1 COM0A1
|
||||||
|
#define CS20 CS00
|
||||||
|
#define WGM21 WGM01
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//automatic BST/BLD/ANDI macro definition
|
||||||
|
#if VID_PIN == 0
|
||||||
|
#define BLD_HWS "bld r16,0\n\t"
|
||||||
|
#define BST_HWS "bst r16,0\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xFE\n"
|
||||||
|
#elif VID_PIN == 1
|
||||||
|
#define BLD_HWS "bld r16,1\n\t"
|
||||||
|
#define BST_HWS "bst r16,1\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xFD\n"
|
||||||
|
#elif VID_PIN == 2
|
||||||
|
#define BLD_HWS "bld r16,2\n\t"
|
||||||
|
#define BST_HWS "bst r16,2\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xFB\n"
|
||||||
|
#elif VID_PIN == 3
|
||||||
|
#define BLD_HWS "bld r16,3\n\t"
|
||||||
|
#define BST_HWS "bst r16,3\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xF7\n"
|
||||||
|
#elif VID_PIN == 4
|
||||||
|
#define BLD_HWS "bld r16,4\n\t"
|
||||||
|
#define BST_HWS "bst r16,4\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xEF\n"
|
||||||
|
#elif VID_PIN == 5
|
||||||
|
#define BLD_HWS "bld r16,5\n\t"
|
||||||
|
#define BST_HWS "bst r16,5\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xDF\n"
|
||||||
|
#elif VID_PIN == 6
|
||||||
|
#define BLD_HWS "bld r16,6\n\t"
|
||||||
|
#define BST_HWS "bst r16,6\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0xBF\n"
|
||||||
|
#elif VID_PIN == 7
|
||||||
|
#define BLD_HWS "bld r16,7\n\t"
|
||||||
|
#define BST_HWS "bst r16,7\n\t"
|
||||||
|
#define ANDI_HWS "andi r16,0x7F\n"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
41
src/platforms/micro/lib/TVout/spec/video_properties.h
Normal file
41
src/platforms/micro/lib/TVout/spec/video_properties.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* This File contains the timing definitions for the TVout AVR composite video
|
||||||
|
generation Library
|
||||||
|
*/
|
||||||
|
#ifndef VIDEO_TIMING_H
|
||||||
|
#define VIDEO_TIMING_H
|
||||||
|
|
||||||
|
#define _CYCLES_PER_US (F_CPU / 1000000)
|
||||||
|
|
||||||
|
#define _TIME_HORZ_SYNC 4.7
|
||||||
|
#define _TIME_VIRT_SYNC 58.85
|
||||||
|
#define _TIME_ACTIVE 46
|
||||||
|
#define _CYCLES_VIRT_SYNC ((_TIME_VIRT_SYNC * _CYCLES_PER_US) - 1)
|
||||||
|
#define _CYCLES_HORZ_SYNC ((_TIME_HORZ_SYNC * _CYCLES_PER_US) - 1)
|
||||||
|
|
||||||
|
// Timing settings for NTSC
|
||||||
|
#define _NTSC_TIME_SCANLINE 63.55
|
||||||
|
#define _NTSC_TIME_OUTPUT_START 12
|
||||||
|
|
||||||
|
#define _NTSC_LINE_FRAME 262
|
||||||
|
#define _NTSC_LINE_START_VSYNC 0
|
||||||
|
#define _NTSC_LINE_STOP_VSYNC 3
|
||||||
|
#define _NTSC_LINE_DISPLAY 216
|
||||||
|
#define _NTSC_LINE_MID ((_NTSC_LINE_FRAME - _NTSC_LINE_DISPLAY) / 2 + _NTSC_LINE_DISPLAY / 2)
|
||||||
|
|
||||||
|
#define _NTSC_CYCLES_SCANLINE ((_NTSC_TIME_SCANLINE * _CYCLES_PER_US) - 1)
|
||||||
|
#define _NTSC_CYCLES_OUTPUT_START ((_NTSC_TIME_OUTPUT_START * _CYCLES_PER_US) - 1)
|
||||||
|
|
||||||
|
// Timing settings for PAL
|
||||||
|
#define _PAL_TIME_SCANLINE 64
|
||||||
|
#define _PAL_TIME_OUTPUT_START 12.5
|
||||||
|
|
||||||
|
#define _PAL_LINE_FRAME 312
|
||||||
|
#define _PAL_LINE_START_VSYNC 0
|
||||||
|
#define _PAL_LINE_STOP_VSYNC 7
|
||||||
|
#define _PAL_LINE_DISPLAY 260
|
||||||
|
#define _PAL_LINE_MID ((_PAL_LINE_FRAME - _PAL_LINE_DISPLAY) / 2 + _PAL_LINE_DISPLAY / 2)
|
||||||
|
|
||||||
|
#define _PAL_CYCLES_SCANLINE ((_PAL_TIME_SCANLINE * _CYCLES_PER_US) - 1)
|
||||||
|
#define _PAL_CYCLES_OUTPUT_START ((_PAL_TIME_OUTPUT_START * _CYCLES_PER_US) - 1)
|
||||||
|
|
||||||
|
#endif
|
||||||
517
src/platforms/micro/lib/TVout/video_gen.cpp
Normal file
517
src/platforms/micro/lib/TVout/video_gen.cpp
Normal file
@@ -0,0 +1,517 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010 Myles Metzer
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "video_gen.h"
|
||||||
|
#include "spec/asm_macros.h"
|
||||||
|
|
||||||
|
//#define REMOVE6C
|
||||||
|
//#define REMOVE5C
|
||||||
|
//#define REMOVE4C
|
||||||
|
//#define REMOVE3C
|
||||||
|
|
||||||
|
int renderLine;
|
||||||
|
TVout_vid display;
|
||||||
|
void (*render_line)();
|
||||||
|
void (*line_handler)();
|
||||||
|
void (*hbi_hook)() = ∅
|
||||||
|
void (*vbi_hook)() = ∅
|
||||||
|
|
||||||
|
// sound properties
|
||||||
|
volatile long remainingToneVsyncs;
|
||||||
|
|
||||||
|
void empty() {
|
||||||
|
|
||||||
|
}
|
||||||
|
// start timer
|
||||||
|
void render_setup(uint8_t mode, uint8_t x, uint8_t y, uint8_t *scrnptr) {
|
||||||
|
display.screen = scrnptr;
|
||||||
|
display.hres = x;
|
||||||
|
display.vres = y;
|
||||||
|
display.frames = 0;
|
||||||
|
|
||||||
|
if (mode) {
|
||||||
|
display.vscale_const = _PAL_LINE_DISPLAY / display.vres - 1;
|
||||||
|
} else {
|
||||||
|
display.vscale_const = _NTSC_LINE_DISPLAY / display.vres - 1;
|
||||||
|
}
|
||||||
|
display.vscale = display.vscale_const;
|
||||||
|
|
||||||
|
// selects the widest render method that fits in 46us
|
||||||
|
// as of 9/16/10 rendermode 3 will not work for resolutions lower than
|
||||||
|
// 192(display.hres lower than 24)
|
||||||
|
unsigned char rmethod = (_TIME_ACTIVE*_CYCLES_PER_US) / (display.hres * 8);
|
||||||
|
switch(rmethod) {
|
||||||
|
case 6:
|
||||||
|
render_line = &render_line6c;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
render_line = &render_line5c;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
render_line = &render_line4c;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
render_line = &render_line3c;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (rmethod > 6) {
|
||||||
|
render_line = &render_line6c;
|
||||||
|
} else {
|
||||||
|
render_line = &render_line3c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DDR_VID |= _BV(VID_PIN);
|
||||||
|
DDR_SYNC |= _BV(SYNC_PIN);
|
||||||
|
PORT_VID &= ~_BV(VID_PIN);
|
||||||
|
PORT_SYNC |= _BV(SYNC_PIN);
|
||||||
|
// for tone generation
|
||||||
|
DDR_SND |= _BV(SND_PIN);
|
||||||
|
|
||||||
|
// inverted fast pwm mode on timer 1
|
||||||
|
TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);
|
||||||
|
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
|
||||||
|
|
||||||
|
if (mode) {
|
||||||
|
display.start_render = _PAL_LINE_MID - ((display.vres * (display.vscale_const + 1)) / 2);
|
||||||
|
display.output_delay = _PAL_CYCLES_OUTPUT_START;
|
||||||
|
display.vsync_end = _PAL_LINE_STOP_VSYNC;
|
||||||
|
display.lines_frame = _PAL_LINE_FRAME;
|
||||||
|
ICR1 = _PAL_CYCLES_SCANLINE;
|
||||||
|
OCR1A = _CYCLES_HORZ_SYNC;
|
||||||
|
} else {
|
||||||
|
display.start_render = _NTSC_LINE_MID - ((display.vres * (display.vscale_const+1))/2) + 8;
|
||||||
|
display.output_delay = _NTSC_CYCLES_OUTPUT_START;
|
||||||
|
display.vsync_end = _NTSC_LINE_STOP_VSYNC;
|
||||||
|
display.lines_frame = _NTSC_LINE_FRAME;
|
||||||
|
ICR1 = _NTSC_CYCLES_SCANLINE;
|
||||||
|
OCR1A = _CYCLES_HORZ_SYNC;
|
||||||
|
}
|
||||||
|
display.scanLine = display.lines_frame + 1;
|
||||||
|
line_handler = &vsync_line;
|
||||||
|
TIMSK1 = _BV(TOIE1);
|
||||||
|
sei();
|
||||||
|
}
|
||||||
|
|
||||||
|
// render a line
|
||||||
|
ISR(TIMER1_OVF_vect) {
|
||||||
|
hbi_hook();
|
||||||
|
line_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
// вызываем синхронизацию или прорисовку строки
|
||||||
|
void blank_line() {
|
||||||
|
if (display.scanLine == display.start_render) {
|
||||||
|
renderLine = 0;
|
||||||
|
display.vscale = display.vscale_const;
|
||||||
|
line_handler = &active_line;
|
||||||
|
} else if (display.scanLine == display.lines_frame) {
|
||||||
|
line_handler = &vsync_line;
|
||||||
|
vbi_hook();
|
||||||
|
}
|
||||||
|
display.scanLine++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void active_line() {
|
||||||
|
wait_until(display.output_delay);
|
||||||
|
render_line();
|
||||||
|
if (!display.vscale) {
|
||||||
|
display.vscale = display.vscale_const;
|
||||||
|
renderLine += display.hres;
|
||||||
|
} else {
|
||||||
|
display.vscale--;
|
||||||
|
}
|
||||||
|
if ((display.scanLine + 1) == (int)(display.start_render + (display.vres * (display.vscale_const + 1)))) {
|
||||||
|
line_handler = &blank_line;
|
||||||
|
}
|
||||||
|
display.scanLine++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// генерируем синхроимпульс
|
||||||
|
void vsync_line() {
|
||||||
|
if (display.scanLine >= display.lines_frame) {
|
||||||
|
OCR1A = _CYCLES_VIRT_SYNC;
|
||||||
|
display.scanLine = 0;
|
||||||
|
display.frames++;
|
||||||
|
if (remainingToneVsyncs != 0) {
|
||||||
|
if (remainingToneVsyncs > 0) {
|
||||||
|
remainingToneVsyncs--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// stop the tone
|
||||||
|
TCCR2B = 0;
|
||||||
|
PORTB &= ~(_BV(SND_PIN));
|
||||||
|
}
|
||||||
|
} else if (display.scanLine == display.vsync_end) {
|
||||||
|
OCR1A = _CYCLES_HORZ_SYNC;
|
||||||
|
line_handler = &blank_line;
|
||||||
|
}
|
||||||
|
display.scanLine++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void inline wait_until(uint8_t time) {
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"subi %[time], 10\n"
|
||||||
|
"sub %[time], %[tcnt1l]\n\t"
|
||||||
|
"100:\n\t"
|
||||||
|
"subi %[time], 3\n\t"
|
||||||
|
"brcc 100b\n\t"
|
||||||
|
"subi %[time], 0-3\n\t"
|
||||||
|
"breq 101f\n\t"
|
||||||
|
"dec %[time]\n\t"
|
||||||
|
"breq 102f\n\t"
|
||||||
|
"rjmp 102f\n"
|
||||||
|
"101:\n\t"
|
||||||
|
"nop\n"
|
||||||
|
"102:\n"
|
||||||
|
:
|
||||||
|
: [time] "a" (time),
|
||||||
|
[tcnt1l] "a" (TCNT1L)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_line6c() {
|
||||||
|
#ifndef REMOVE6C
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ADD r26,r28\n\t"
|
||||||
|
"ADC r27,r29\n\t"
|
||||||
|
// save PORTB
|
||||||
|
"svprt %[port]\n\t"
|
||||||
|
"rjmp enter6\n"
|
||||||
|
"loop6:\n\t"
|
||||||
|
// 8
|
||||||
|
"bst __tmp_reg__,0\n\t"
|
||||||
|
"o1bs %[port]\n"
|
||||||
|
"enter6:\n\t"
|
||||||
|
// 1
|
||||||
|
"LD __tmp_reg__,X+\n\t"
|
||||||
|
"delay1\n\t"
|
||||||
|
"bst __tmp_reg__,7\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 2
|
||||||
|
"delay3\n\t"
|
||||||
|
"bst __tmp_reg__,6\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 3
|
||||||
|
"delay3\n\t"
|
||||||
|
"bst __tmp_reg__,5\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 4
|
||||||
|
"delay3\n\t"
|
||||||
|
"bst __tmp_reg__,4\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 5
|
||||||
|
"delay3\n\t"
|
||||||
|
"bst __tmp_reg__,3\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 6
|
||||||
|
"delay3\n\t"
|
||||||
|
"bst __tmp_reg__,2\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 7
|
||||||
|
"delay3\n\t"
|
||||||
|
"bst __tmp_reg__,1\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
"dec %[hres]\n\t"
|
||||||
|
// go too loopsix
|
||||||
|
"brne loop6\n\t"
|
||||||
|
"delay2\n\t"
|
||||||
|
// 8
|
||||||
|
"bst __tmp_reg__,0\n\t"
|
||||||
|
"o1bs %[port]\n"
|
||||||
|
"svprt %[port]\n\t"
|
||||||
|
BST_HWS
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
:
|
||||||
|
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||||
|
"x" (display.screen),
|
||||||
|
"y" (renderLine),
|
||||||
|
[hres] "d" (display.hres)
|
||||||
|
: "r16" // try to remove this clobber later...
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_line5c() {
|
||||||
|
#ifndef REMOVE5C
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ADD r26,r28\n\t"
|
||||||
|
"ADC r27,r29\n\t"
|
||||||
|
// save PORTB
|
||||||
|
"svprt %[port]\n\t"
|
||||||
|
"rjmp enter5\n"
|
||||||
|
"loop5:\n\t"
|
||||||
|
// 8
|
||||||
|
"bst __tmp_reg__,0\n\t"
|
||||||
|
"o1bs %[port]\n"
|
||||||
|
"enter5:\n\t"
|
||||||
|
// 1
|
||||||
|
"LD __tmp_reg__,X+\n\t"
|
||||||
|
"bst __tmp_reg__,7\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 2
|
||||||
|
"delay2\n\t"
|
||||||
|
"bst __tmp_reg__,6\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 3
|
||||||
|
"delay2\n\t"
|
||||||
|
"bst __tmp_reg__,5\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 4
|
||||||
|
"delay2\n\t"
|
||||||
|
"bst __tmp_reg__,4\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 5
|
||||||
|
"delay2\n\t"
|
||||||
|
"bst __tmp_reg__,3\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 6
|
||||||
|
"delay2\n\t"
|
||||||
|
"bst __tmp_reg__,2\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// 7
|
||||||
|
"delay1\n\t"
|
||||||
|
"dec %[hres]\n\t"
|
||||||
|
"bst __tmp_reg__,1\n\t"
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
// go too loop5
|
||||||
|
"brne loop5\n\t"
|
||||||
|
"delay1\n\t"
|
||||||
|
// 8
|
||||||
|
"bst __tmp_reg__,0\n\t"
|
||||||
|
"o1bs %[port]\n"
|
||||||
|
"svprt %[port]\n\t"
|
||||||
|
BST_HWS
|
||||||
|
"o1bs %[port]\n\t"
|
||||||
|
:
|
||||||
|
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||||
|
"x" (display.screen),
|
||||||
|
"y" (renderLine),
|
||||||
|
[hres] "d" (display.hres)
|
||||||
|
: "r16" // try to remove this clobber later...
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_line4c() {
|
||||||
|
#ifndef REMOVE4C
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
"ADD r26,r28\n\t"
|
||||||
|
"ADC r27,r29\n\t"
|
||||||
|
"rjmp enter4\n"
|
||||||
|
"loop4:\n\t"
|
||||||
|
// 8
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"enter4:\n\t"
|
||||||
|
// 1
|
||||||
|
"LD __tmp_reg__,X+\n\t"
|
||||||
|
"delay1\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
// 2
|
||||||
|
"delay2\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
// 3
|
||||||
|
"delay2\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
// 4
|
||||||
|
"delay2\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"delay2\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
// 6
|
||||||
|
"delay2\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
// 7
|
||||||
|
"delay1\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"dec %[hres]\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
// go too loop4
|
||||||
|
"brne loop4\n\t"
|
||||||
|
// 8
|
||||||
|
"delay1\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"delay3\n\t"
|
||||||
|
"cbi %[port],7\n\t"
|
||||||
|
:
|
||||||
|
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||||
|
"x" (display.screen),
|
||||||
|
"y" (renderLine),
|
||||||
|
[hres] "d" (display.hres)
|
||||||
|
: "r16" // try to remove this clobber later...
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// only 16mhz right now!!!
|
||||||
|
void render_line3c() {
|
||||||
|
#ifndef REMOVE3C
|
||||||
|
__asm__ __volatile__ (
|
||||||
|
".macro byteshift\n\t"
|
||||||
|
"LD __tmp_reg__,X+\n\t"
|
||||||
|
// 0
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 1
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 2
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 3
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 4
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 5
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 6
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
"nop\n\t"
|
||||||
|
"lsl __tmp_reg__\n\t"
|
||||||
|
// 7
|
||||||
|
"out %[port],__tmp_reg__\n\t"
|
||||||
|
".endm\n\t"
|
||||||
|
"ADD r26,r28\n\t"
|
||||||
|
"ADC r27,r29\n\t"
|
||||||
|
// 615
|
||||||
|
"cpi %[hres],30\n\t"
|
||||||
|
"breq skip0\n\t"
|
||||||
|
"cpi %[hres],29\n\t"
|
||||||
|
"breq jumpto1\n\t"
|
||||||
|
"cpi %[hres],28\n\t"
|
||||||
|
"breq jumpto2\n\t"
|
||||||
|
"cpi %[hres],27\n\t"
|
||||||
|
"breq jumpto3\n\t"
|
||||||
|
"cpi %[hres],26\n\t"
|
||||||
|
"breq jumpto4\n\t"
|
||||||
|
"cpi %[hres],25\n\t"
|
||||||
|
"breq jumpto5\n\t"
|
||||||
|
"cpi %[hres],24\n\t"
|
||||||
|
"breq jumpto6\n\t"
|
||||||
|
"jumpto1:\n\t"
|
||||||
|
"rjmp skip1\n\t"
|
||||||
|
"jumpto2:\n\t"
|
||||||
|
"rjmp skip2\n\t"
|
||||||
|
"jumpto3:\n\t"
|
||||||
|
"rjmp skip3\n\t"
|
||||||
|
"jumpto4:\n\t"
|
||||||
|
"rjmp skip4\n\t"
|
||||||
|
"jumpto5:\n\t"
|
||||||
|
"rjmp skip5\n\t"
|
||||||
|
"jumpto6:\n\t"
|
||||||
|
"rjmp skip6\n\t"
|
||||||
|
"skip0:\n\t"
|
||||||
|
//1 643
|
||||||
|
"byteshift\n\t"
|
||||||
|
"skip1:\n\t"
|
||||||
|
// 2
|
||||||
|
"byteshift\n\t"
|
||||||
|
"skip2:\n\t"
|
||||||
|
// 3
|
||||||
|
"byteshift\n\t"
|
||||||
|
"skip3:\n\t"
|
||||||
|
// 4
|
||||||
|
"byteshift\n\t"
|
||||||
|
"skip4:\n\t"
|
||||||
|
// 5
|
||||||
|
"byteshift\n\t"
|
||||||
|
"skip5:\n\t"
|
||||||
|
// 6
|
||||||
|
"byteshift\n\t"
|
||||||
|
"skip6:\n\t"
|
||||||
|
// 7
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 8
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 9
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 10
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 11
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 12
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 13
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 14
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 15
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 16
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 17
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 18
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 19
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 20
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 21
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 22
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 23
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 24
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 25
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 26
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 27
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 28
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 29
|
||||||
|
"byteshift\n\t"
|
||||||
|
// 30
|
||||||
|
"byteshift\n\t"
|
||||||
|
"delay2\n\t"
|
||||||
|
"cbi %[port],7\n\t"
|
||||||
|
:
|
||||||
|
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||||
|
"x" (display.screen),
|
||||||
|
"y" (renderLine),
|
||||||
|
[hres] "d" (display.hres)
|
||||||
|
: "r16" // try to remove this clobber later...
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
71
src/platforms/micro/lib/TVout/video_gen.h
Normal file
71
src/platforms/micro/lib/TVout/video_gen.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010 Myles Metzer
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VIDEO_GEN_H
|
||||||
|
#define VIDEO_GEN_H
|
||||||
|
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include "spec/video_properties.h"
|
||||||
|
#include "spec/hardware_setup.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
volatile int scanLine;
|
||||||
|
volatile unsigned long frames;
|
||||||
|
unsigned char start_render;
|
||||||
|
// remove me
|
||||||
|
int lines_frame;
|
||||||
|
uint8_t vres;
|
||||||
|
uint8_t hres;
|
||||||
|
// remove me
|
||||||
|
uint8_t output_delay;
|
||||||
|
// combine me with status switch
|
||||||
|
char vscale_const;
|
||||||
|
// combine me too.
|
||||||
|
char vscale;
|
||||||
|
// remove me
|
||||||
|
char vsync_end;
|
||||||
|
uint8_t* screen;
|
||||||
|
} TVout_vid;
|
||||||
|
|
||||||
|
extern TVout_vid display;
|
||||||
|
|
||||||
|
extern void (*hbi_hook)();
|
||||||
|
extern void (*vbi_hook)();
|
||||||
|
|
||||||
|
void render_setup(uint8_t mode, uint8_t x, uint8_t y, uint8_t *scrnptr);
|
||||||
|
|
||||||
|
void blank_line();
|
||||||
|
void active_line();
|
||||||
|
void vsync_line();
|
||||||
|
void empty();
|
||||||
|
|
||||||
|
// tone generation properties
|
||||||
|
extern volatile long remainingToneVsyncs;
|
||||||
|
|
||||||
|
// 6cycles functions
|
||||||
|
void render_line6c();
|
||||||
|
void render_line5c();
|
||||||
|
void render_line4c();
|
||||||
|
void render_line3c();
|
||||||
|
static void inline wait_until(uint8_t time);
|
||||||
|
#endif
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#include "./lib/MPU6050/MPU6050.h"
|
|
||||||
#include "./lib/ST7789/ST7789.h"
|
|
||||||
|
|
||||||
#include "../platform.h"
|
|
||||||
#include "micro.h"
|
|
||||||
|
|
||||||
void draw_line(const ScreenPoint *sp1, const ScreenPoint *sp2,
|
|
||||||
const Color *const color) {
|
|
||||||
st7789_draw_line(sp1->coordinates[0], sp1->coordinates[1],
|
|
||||||
sp2->coordinates[0], sp2->coordinates[1],
|
|
||||||
RGB565(color->red, color->green, color->blue));
|
|
||||||
};
|
|
||||||
const Screen screen = {
|
|
||||||
.width = DISPLAY_WIDTH, .height = DISPLAY_HEIGHT, .draw_line = &draw_line};
|
|
||||||
|
|
||||||
const Color white = {255, 255, 255};
|
|
||||||
const Color black = {0, 0, 0};
|
|
||||||
|
|
||||||
float gx, gy, gz;
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
st7789_init();
|
|
||||||
mpu6050_init();
|
|
||||||
|
|
||||||
init_engine();
|
|
||||||
|
|
||||||
st7789_fill_screen(RGB565(0, 0, 0));
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
render(&screen, &black);
|
|
||||||
|
|
||||||
mpu6050_read_gyro(&gx, &gy, &gz);
|
|
||||||
float angles_speed[3] = {gx / 255.0f, gy / 255.0f, gz / 255.0f};
|
|
||||||
rotate(angles_speed);
|
|
||||||
|
|
||||||
render(&screen, &white);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
39
src/platforms/micro/micro.cpp
Normal file
39
src/platforms/micro/micro.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
#include "micro.h"
|
||||||
|
#include "../platform.h"
|
||||||
|
|
||||||
|
#include "./lib/MPU6050/MPU6050.h"
|
||||||
|
#include "./lib/TVout/TVout.h"
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
TVout TV;
|
||||||
|
|
||||||
|
void draw_line(const ScreenPoint *const sp1, const ScreenPoint *const sp2,
|
||||||
|
const Color *const color) {
|
||||||
|
TV.drawLine(sp1->coordinates[0], sp1->coordinates[1], sp2->coordinates[0],
|
||||||
|
sp2->coordinates[1], 1);
|
||||||
|
}
|
||||||
|
Screen screen = {.width = 128, .height = 64, .draw_line = &draw_line};
|
||||||
|
Color white = {.red = (char)255, .green = (char)255, .blue = (char)255};
|
||||||
|
|
||||||
|
float gx, gy, gz;
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
TV.begin(NTSC, 128, 64);
|
||||||
|
mpu6050_init();
|
||||||
|
|
||||||
|
init_engine();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
TV.clearScreen();
|
||||||
|
|
||||||
|
mpu6050_read_gyro(&gx, &gy, &gz);
|
||||||
|
float angles[3] = {gx / 250.0f, gy / 250.0f, gz / 250.0f};
|
||||||
|
rotate(angles);
|
||||||
|
|
||||||
|
render(&screen, &white);
|
||||||
|
|
||||||
|
// _delay_ms(1000 / FPS);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#ifndef MICRO_H
|
#ifndef MICRO_H
|
||||||
#define MICRO_H
|
#define MICRO_H
|
||||||
|
|
||||||
#include "./lib/ST7789/ST7789.h"
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -4,6 +4,18 @@
|
|||||||
#include "../utils/screen.h"
|
#include "../utils/screen.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define EXTERNC extern "C"
|
||||||
|
#else
|
||||||
|
#define EXTERNC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXTERNC void init_engine();
|
||||||
|
EXTERNC void render(const Screen *const screen, const Color *const color);
|
||||||
|
EXTERNC void tic();
|
||||||
|
EXTERNC void rotate(const float angles_speed[3]);
|
||||||
|
EXTERNC void destroy();
|
||||||
|
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
#include "win/win.h"
|
#include "win/win.h"
|
||||||
|
|
||||||
@@ -17,20 +29,14 @@
|
|||||||
|
|
||||||
#ifdef MICRO
|
#ifdef MICRO
|
||||||
|
|
||||||
#define FPS 25
|
#define FPS 20
|
||||||
#include "micro/micro.h"
|
#define DISPLAY_WIDTH 32
|
||||||
|
#define DISPLAY_HEIGHT 24
|
||||||
#define DISPLAY_WIDTH 160
|
|
||||||
#define DISPLAY_HEIGHT 128
|
|
||||||
#define WINDOW_WIDTH DISPLAY_WIDTH
|
#define WINDOW_WIDTH DISPLAY_WIDTH
|
||||||
#define WINDOW_HEIGHT DISPLAY_HEIGHT
|
#define WINDOW_HEIGHT DISPLAY_HEIGHT
|
||||||
|
|
||||||
|
#include "micro/micro.h"
|
||||||
|
|
||||||
#endif // MICRO
|
#endif // MICRO
|
||||||
|
|
||||||
void init_engine();
|
|
||||||
void render(const Screen *const screen, const Color *const color);
|
|
||||||
void tic();
|
|
||||||
void rotate(const float angles_speed[3]);
|
|
||||||
void destroy();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user