diff --git a/src/Makefile b/src/Makefile index 27d68da..9f51a53 100644 --- a/src/Makefile +++ b/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); diff --git a/src/engine/object.c b/src/engine/object.c index 261976d..11aaf01 100644 --- a/src/engine/object.c +++ b/src/engine/object.c @@ -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( - &object->points[object->edges[i][0]], screen, render_matrix); - ScreenPoint screen_point2 = point_to_screen_point( - &object->points[object->edges[i][1]], screen, render_matrix); - screen->draw_line(&screen_point1, &screen_point2, color); + 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 p2 = point_to_screen_point( + &object->points[object->edges[i][1]], screen, render_matrix); + screen->draw_line(&p1, &p2, color); + } } } \ No newline at end of file diff --git a/src/engine/object.h b/src/engine/object.h index ba150b6..f002e2b 100644 --- a/src/engine/object.h +++ b/src/engine/object.h @@ -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 \ No newline at end of file diff --git a/src/engine/point.c b/src/engine/point.c index 30cf6a4..7b59729 100644 --- a/src/engine/point.c +++ b/src/engine/point.c @@ -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++) diff --git a/src/engine/point.h b/src/engine/point.h index 233895a..66fa262 100644 --- a/src/engine/point.h +++ b/src/engine/point.h @@ -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); diff --git a/src/main.c b/src/main.c index 1b52c9e..371c7b9 100644 --- a/src/main.c +++ b/src/main.c @@ -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); } } diff --git a/src/objects.h b/src/objects.h index 9dd3bad..916a6cb 100644 --- a/src/objects.h +++ b/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}}; diff --git a/src/platforms/micro/lib/I2C/I2C.h b/src/platforms/micro/lib/I2C/I2C.h index e21724c..7e23e18 100644 --- a/src/platforms/micro/lib/I2C/I2C.h +++ b/src/platforms/micro/lib/I2C/I2C.h @@ -4,6 +4,7 @@ #include #define TWI_FREQ 400000UL // 400 kHz +// #define TWI_FREQ 100000UL // 400 kHz void i2c_init(void); uint8_t i2c_start(uint8_t address); diff --git a/src/platforms/micro/lib/MPU6050/MPU6050.c b/src/platforms/micro/lib/MPU6050/MPU6050.c index 46ddf71..b3b148c 100644 --- a/src/platforms/micro/lib/MPU6050/MPU6050.c +++ b/src/platforms/micro/lib/MPU6050/MPU6050.c @@ -1,5 +1,5 @@ -#include "../I2C/I2C.h" #include "MPU6050.h" +#include "../I2C/I2C.h" #include #include @@ -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); diff --git a/src/platforms/micro/lib/MPU6050/MPU6050.h b/src/platforms/micro/lib/MPU6050/MPU6050.h index 8e79ab5..ed9c9dc 100644 --- a/src/platforms/micro/lib/MPU6050/MPU6050.h +++ b/src/platforms/micro/lib/MPU6050/MPU6050.h @@ -3,6 +3,10 @@ #include +#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 \ No newline at end of file diff --git a/src/platforms/micro/lib/ST7789/ST7789.c b/src/platforms/micro/lib/ST7789/ST7789.c deleted file mode 100644 index d1f99a5..0000000 --- a/src/platforms/micro/lib/ST7789/ST7789.c +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/src/platforms/micro/lib/ST7789/ST7789.h b/src/platforms/micro/lib/ST7789/ST7789.h deleted file mode 100644 index 6a3c65d..0000000 --- a/src/platforms/micro/lib/ST7789/ST7789.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef ST7789_H -#define ST7789_H - -#include -#include -#include - -#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 \ No newline at end of file diff --git a/src/platforms/micro/lib/TVout/TVout.cpp b/src/platforms/micro/lib/TVout/TVout.cpp new file mode 100644 index 0000000..3d44fcf --- /dev/null +++ b/src/platforms/micro/lib/TVout/TVout.cpp @@ -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); + } +} diff --git a/src/platforms/micro/lib/TVout/TVout.h b/src/platforms/micro/lib/TVout/TVout.h new file mode 100644 index 0000000..34d28d2 --- /dev/null +++ b/src/platforms/micro/lib/TVout/TVout.h @@ -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 +#include + +#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 diff --git a/src/platforms/micro/lib/TVout/spec/asm_macros.h b/src/platforms/micro/lib/TVout/spec/asm_macros.h new file mode 100644 index 0000000..55b1b26 --- /dev/null +++ b/src/platforms/micro/lib/TVout/spec/asm_macros.h @@ -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 \ No newline at end of file diff --git a/src/platforms/micro/lib/TVout/spec/hardware_setup.h b/src/platforms/micro/lib/TVout/spec/hardware_setup.h new file mode 100644 index 0000000..f0134c7 --- /dev/null +++ b/src/platforms/micro/lib/TVout/spec/hardware_setup.h @@ -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 \ No newline at end of file diff --git a/src/platforms/micro/lib/TVout/spec/video_properties.h b/src/platforms/micro/lib/TVout/spec/video_properties.h new file mode 100644 index 0000000..7434906 --- /dev/null +++ b/src/platforms/micro/lib/TVout/spec/video_properties.h @@ -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 diff --git a/src/platforms/micro/lib/TVout/video_gen.cpp b/src/platforms/micro/lib/TVout/video_gen.cpp new file mode 100644 index 0000000..2a1bfb4 --- /dev/null +++ b/src/platforms/micro/lib/TVout/video_gen.cpp @@ -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 +} diff --git a/src/platforms/micro/lib/TVout/video_gen.h b/src/platforms/micro/lib/TVout/video_gen.h new file mode 100644 index 0000000..bd19659 --- /dev/null +++ b/src/platforms/micro/lib/TVout/video_gen.h @@ -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 +#include +#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 diff --git a/src/platforms/micro/micro.c b/src/platforms/micro/micro.c deleted file mode 100644 index 4630530..0000000 --- a/src/platforms/micro/micro.c +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/src/platforms/micro/micro.cpp b/src/platforms/micro/micro.cpp new file mode 100644 index 0000000..5157667 --- /dev/null +++ b/src/platforms/micro/micro.cpp @@ -0,0 +1,39 @@ + +#include "micro.h" +#include "../platform.h" + +#include "./lib/MPU6050/MPU6050.h" +#include "./lib/TVout/TVout.h" +#include + +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; +} \ No newline at end of file diff --git a/src/platforms/micro/micro.h b/src/platforms/micro/micro.h index 52c2cb6..d3bf083 100644 --- a/src/platforms/micro/micro.h +++ b/src/platforms/micro/micro.h @@ -1,7 +1,6 @@ #ifndef MICRO_H #define MICRO_H -#include "./lib/ST7789/ST7789.h" #include #endif \ No newline at end of file diff --git a/src/platforms/platform.h b/src/platforms/platform.h index 937e74e..1968aef 100644 --- a/src/platforms/platform.h +++ b/src/platforms/platform.h @@ -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