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
|
||||
CC = gcc
|
||||
CFLAGS = -std=gnu23 -Wall -Wextra -O2
|
||||
CXXFLAGS = -std=gnu++17 -Wall -Wextra -O2 -fno-exceptions -fno-rtti
|
||||
LIBS = -lgdi32
|
||||
|
||||
# Micro options
|
||||
MCU = atmega328p
|
||||
F_CPU = 16000000
|
||||
PROGRAMMER = arduino
|
||||
PORT = COM4
|
||||
PORT = COM5
|
||||
MICRO_CC = avr-gcc
|
||||
MICRO_CXX = avr-g++
|
||||
OBJCOPY = avr-objcopy
|
||||
SIZE = avr-size
|
||||
AVRDUDE = avrdude
|
||||
MICRO_CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL $(CFLAGS)
|
||||
MICRO_CXXFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL $(CXXFLAGS)
|
||||
LDFLAGS = -mmcu=$(MCU)
|
||||
|
||||
TARGET = main
|
||||
@@ -26,8 +29,9 @@ UNIX_SOURCES = $(BASE_SOURCES) platforms/unix.c
|
||||
UNIX_OBJECTS = $(UNIX_SOURCES:.c=.uo)
|
||||
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_OBJECTS = $(MICRO_SOURCES:.c=.mo)
|
||||
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:.cpp=.mxxo)
|
||||
MICRO_OBJECTS := $(MICRO_OBJECTS:.c=.mo)
|
||||
MICRO_TARGET = $(TARGET)
|
||||
ELF = $(TARGET).elf
|
||||
HEX = $(TARGET).hex
|
||||
@@ -44,7 +48,7 @@ $(HEX): $(ELF)
|
||||
$(SIZE) $@
|
||||
|
||||
$(ELF): $(MICRO_OBJECTS)
|
||||
$(MICRO_CC) $(LDFLAGS) -o $@ $^
|
||||
$(MICRO_CXX) $(LDFLAGS) -o $@ $^
|
||||
|
||||
%.wo: %.c
|
||||
$(CC) $(CFLAGS) -DWIN -c $< -o $@
|
||||
@@ -52,9 +56,14 @@ $(ELF): $(MICRO_OBJECTS)
|
||||
%.uo: %.c
|
||||
$(CC) $(CFLAGS) -DUNIX -c $< -o $@
|
||||
|
||||
|
||||
%.mxxo: %.cpp
|
||||
$(MICRO_CXX) $(MICRO_CXXFLAGS) -DMICRO -c $< -o $@
|
||||
|
||||
%.mo: %.c
|
||||
$(MICRO_CC) $(MICRO_CFLAGS) -DMICRO -c $< -o $@
|
||||
|
||||
|
||||
run_win: $(WIN_TARGET)
|
||||
.\$(WIN_TARGET)
|
||||
|
||||
@@ -62,7 +71,7 @@ run_unix: $(UNIX_TARGET)
|
||||
.\$(UNIX_TARGET)
|
||||
|
||||
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:
|
||||
rm -f $(WIN_TARGET) $(WIN_OBJECTS) $(UNIX_TARGET) $(UNIX_OBJECTS) $(HEX) $(ELF) $(MICRO_OBJECTS);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "object.h"
|
||||
|
||||
#include "../utils/screen.h"
|
||||
#include "camera.h"
|
||||
#include "vector.h"
|
||||
|
||||
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,
|
||||
const float *render_matrix, const Color *const color) {
|
||||
for (int i = 0; i < object->number_of_edges; i++) {
|
||||
ScreenPoint screen_point1 = point_to_screen_point(
|
||||
const float *render_matrix, const Camera *const camera,
|
||||
const Color *const color) {
|
||||
// Массив для отслеживания, какие рёбра нужно нарисовать
|
||||
char edge_drawn[object->number_of_edges];
|
||||
for (int i = 0; i < object->number_of_edges; ++i) {
|
||||
edge_drawn[i] = 0;
|
||||
}
|
||||
|
||||
// Проходим по всем граням
|
||||
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 screen_point2 = point_to_screen_point(
|
||||
ScreenPoint p2 = point_to_screen_point(
|
||||
&object->points[object->edges[i][1]], screen, render_matrix);
|
||||
screen->draw_line(&screen_point1, &screen_point2, color);
|
||||
screen->draw_line(&p1, &p2, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,18 @@
|
||||
#define OBJECT_H
|
||||
|
||||
#include "../utils/screen.h"
|
||||
#include "camera.h"
|
||||
#include "point.h"
|
||||
|
||||
typedef struct Object {
|
||||
const char *const name;
|
||||
Point *const points;
|
||||
const int (*const edges)[2];
|
||||
const int *const *const faces;
|
||||
const int(*const face_sizes);
|
||||
const int number_of_points;
|
||||
const int number_of_edges;
|
||||
const int number_of_faces;
|
||||
Point *const position;
|
||||
const float (*const rotate_speed)[3];
|
||||
} Object;
|
||||
@@ -17,6 +21,7 @@ typedef struct Object {
|
||||
void object_transform(Object *const object, int size, float *translate_matrix);
|
||||
Point object_get_centroid(const Object *const object);
|
||||
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
|
||||
@@ -7,6 +7,10 @@ void point_add_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_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) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
|
||||
@@ -8,6 +8,7 @@ typedef struct Point {
|
||||
} 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_transform(Point *const point, int size,
|
||||
const float *translate_matrix);
|
||||
|
||||
@@ -47,7 +47,7 @@ void init_engine() {
|
||||
|
||||
void render(const Screen *const screen, const Color *const color) {
|
||||
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"
|
||||
|
||||
// == Cube ==
|
||||
|
||||
Point cube_points[] = {
|
||||
{.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 = {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}}};
|
||||
|
||||
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},
|
||||
{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",
|
||||
.points = cube_points,
|
||||
.edges = cube_edges,
|
||||
.faces = cube_faces,
|
||||
.face_sizes = cube_face_sizes,
|
||||
.number_of_points = 8,
|
||||
.number_of_edges = 12,
|
||||
.number_of_faces = 6,
|
||||
.position = &cube_position,
|
||||
.rotate_speed = &cube_speed};
|
||||
|
||||
Point pyramid_points[] = {{.coordinates = {-1.0f, 0.0f, -1.0f}},
|
||||
{.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};
|
||||
// // == Pyramid ==
|
||||
|
||||
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}}};
|
||||
Point prism_position = {.coordinates = {10.0f, 10.0f, 10.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 float prism_speed[] = {0.0f, 0.03f, 0.0f};
|
||||
const Object prism = {.name = "prism",
|
||||
.points = prism_points,
|
||||
.edges = prism_edges,
|
||||
.number_of_points = 6,
|
||||
.number_of_edges = 9,
|
||||
.position = &prism_position,
|
||||
.rotate_speed = &prism_speed};
|
||||
// Point pyramid_points[] = {{.coordinates = {-1.0f, 0.0f, -1.0f}},
|
||||
// {.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}}};
|
||||
// const int pyramid_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0},
|
||||
// {0, 4}, {1, 4}, {2, 4}, {3, 4}};
|
||||
// const int pyramid_face0[] = {0, 1, 2, 3};
|
||||
// const int pyramid_face1[] = {1, 0, 4};
|
||||
// const int pyramid_face2[] = {2, 1, 4};
|
||||
// const int pyramid_face3[] = {3, 2, 4};
|
||||
// const int pyramid_face4[] = {0, 3, 4};
|
||||
// const int *pyramid_faces[] = {pyramid_face0, pyramid_face1, pyramid_face2,
|
||||
// pyramid_face3, pyramid_face4};
|
||||
// const int pyramid_face_sizes[] = {4, 3, 3, 3, 3};
|
||||
|
||||
const int number_of_objects = 3;
|
||||
Object objects[] = {cube, pyramid, prism};
|
||||
// const float pyramid_speed[] = {0.03f, 0.03f, -0.03f};
|
||||
// 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_target = {.coordinates = {1.0f, 1.0f, 1.0f}};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define TWI_FREQ 400000UL // 400 kHz
|
||||
// #define TWI_FREQ 100000UL // 400 kHz
|
||||
|
||||
void i2c_init(void);
|
||||
uint8_t i2c_start(uint8_t address);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "../I2C/I2C.h"
|
||||
#include "MPU6050.h"
|
||||
#include "../I2C/I2C.h"
|
||||
#include <avr/io.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_z = z;
|
||||
}
|
||||
|
||||
void mpu6050_read_gyro(float *gx, float *gy, float *gz) {
|
||||
uint8_t buf[6];
|
||||
mpu6050_read_burst(MPU6050_REG_GYRO_XOUT_H, buf, 6);
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Адрес по умолчанию: AD0 = GND → 0x68 → сдвинутый = 0xD0
|
||||
// Если AD0 = VCC → 0x69 → 0xD2
|
||||
#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,
|
||||
int16_t *gz_offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#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
|
||||
#define MICRO_H
|
||||
|
||||
#include "./lib/ST7789/ST7789.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,18 @@
|
||||
#include "../utils/screen.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
|
||||
#include "win/win.h"
|
||||
|
||||
@@ -17,20 +29,14 @@
|
||||
|
||||
#ifdef MICRO
|
||||
|
||||
#define FPS 25
|
||||
#include "micro/micro.h"
|
||||
|
||||
#define DISPLAY_WIDTH 160
|
||||
#define DISPLAY_HEIGHT 128
|
||||
#define FPS 20
|
||||
#define DISPLAY_WIDTH 32
|
||||
#define DISPLAY_HEIGHT 24
|
||||
#define WINDOW_WIDTH DISPLAY_WIDTH
|
||||
#define WINDOW_HEIGHT DISPLAY_HEIGHT
|
||||
|
||||
#include "micro/micro.h"
|
||||
|
||||
#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
|
||||
|
||||
Reference in New Issue
Block a user