Свое OSD и проблемы графического video overlay
Пересмотрел много open source кода по OSD, video overlay и пробовал делать по аналогии (часть кода и алгоритм был взят из проекта osxosd) вывод графики через spi интерфейс. Но вывод в spi байта 0b10000000 приводит к отрисовке не небольшого участка - пиксела, а небольшой линии, тоже с задержками какая-то борода.
Конечная цель понять алгоритмику рисования и отрисовывать графику приблизительно как Smalltim, PitLab, Oleg70. Понятно дело что у них используются серьезные камни, но на данном этапе мне хотя бы понять как они это делают, алгоритм скорее всего один.
Может кто знает какие методы у них используются для video overlay? spi? просто порты и задержки?
#define F_CPU 16000000UL // CPU speed
#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdbool.h>
#include "timings.h"
// swith pin 0 port B
#define NORM DDRB &=~(1<<DDB0)
#define DIM DDRB |=(1<<DDB0)
// wait SPI transfer
#define WAIT while(!(SPSR & (1<<SPIF)))
// graphic size
#define GRAPHICS_HEIGHT 80
#define GRAPHICS_WIDTH GRAPHICS_HEIGHT/8
// buffer
uint8_t screen[GRAPHICS_WIDTH][GRAPHICS_HEIGHT];
// line counter
static uint16_t line = 1;
void setPixel(uint8_t x, uint8_t y, uint8_t state) {
uint8_t bitPos = 7-(x%8);
uint8_t temp = screen[x/8][y];
if (state == 0) {
temp &= ~(1<<bitPos);
}
else if (state == 1) {
temp |= (1<<bitPos);
}
else {
temp ^= (1<<bitPos);
}
screen[x/8][y] = temp;
}
int main(void)
{
// set white color to 7 bit on buffer array screen 0,0
setPixel(0,0,1);
//
DDRB = 0b00111100; // SPI master output
PORTB = 0b00000100; // SS=1
DDRD = 0b00000000; // INT0:1 as input
DDRC = 0b00000000; // PC0 & PC1 as input
// INT1 on falling; INT0 on any change
MCUCR = (1<<ISC01) | (1<<ISC11); /* Set interrupt on falling edge */
GICR = (1<<INT0)|(1<<INT1); // enable INT0:1 interrupts
// SPI enable | master mode | phase shift | invert polarity
SPCR =(1<<SPE)|(1<<MSTR)|(1<<CPHA)|(1<<CPOL); // CPOL - ???
// set interrupt enable
sei();
while (1)
{
}
}
// VSYNC interrupt
ISR(INT1_vect)
{
line = 1;
}
// HSYNC interrupt
ISR(INT0_vect)
{
// reset color
NORM;
if ((line >= 60) && (line < (60 + GRAPHICS_HEIGHT))){
// skip begin pulse
delay4700ns;
delay5700ns;
// dim color
DIM;
// speed spi 2x faster
SPSR = (1<<SPI2X);
// out current line from screen array
for (uint8_t i = 0; i < GRAPHICS_WIDTH; ++i) {
SPDR = screen[i][line-60];
WAIT;
}
// reset color
NORM;
SPDR=0x00;
}
++line;
}
Из за жестких таймингов надо писать не асме, хотя бы формирование одной строки. Си делает много лишнего уходя в прерывание. Не всегда используют SPI можно и регистр крутить и выводить его.
Из за жестких таймингов надо писать не асме, хотя бы формирование одной строки. Си делает много лишнего уходя в прерывание. Не всегда используют SPI можно и регистр крутить и выводить его.
провел эксперемент:
- Вывожу в прерывании INT0 (начало новой строки) через SPI следующим образом при размере графики 80x80, т.е. массив screen[10][80]
#define WAIT while(!(SPSR & (1<<SPIF)))
//
for (uint8_t i = 0; i < GRAPHICS_WIDTH; ++i) {
SPDR = screen[i][currentline];
WAIT;
}
80x80 результат
32x32 результат
- Все тоже что и в первом эксперименте, не не пользуюсь макрос WAIT, т.е. просто кидаю в регистр SPDR байт и NOP-ами делаю задержки. Какие NOP-ы и где подсмотрел в cl-osd.
for (uint8_t i = 0; i < GRAPHICS_WIDTH; ++i) {
DELAY_9_NOP();
SPDR = screen[i][currentline];
DELAY_9_NOP();
DELAY_1_NOP();
}
DELAY_10_NOP ();
DELAY_7_NOP ();
SPDR = 0x00;
получаю допустимый 80x80 результат
---------- резюме -------------
1 эксперимент своего рода tcp (гарантия проталкивания SPDR), 2 эксперимент второй udp (без гарантии проталкивания SPDR), т.е. гарантия проталкивания SPDR приводит к “конским” задержкам.
Вопрос по теме несколько сужается: как рассчитываются данные NOP-ы? каждый NOP - это NoOPeration один такт, в моем случае 1/16Мгц=62.5us. Длина строки 64us. Таким образом перед внесением в регистр SPDR байта заставляем сделать 9 NOP-ов, т.е. 62.5*9=562.5us и столько же после - почему? Как рассчитать какие NOP-ы использовать? И где их делать - сколько до внесения в SPDR, сколько после, сколько после всей строки?
SPI интерфейс - это единственное решение?
Нет не единственное, но самое выгодное, т.к. это фактически аппаратный сдвиговый регистр.
(сдвигает биты байта памяти на одну ногу по сигналу тактирования, а это как раз то что нужно…)
алгоритм скорее всего один
не совсем так, подходов к отрисовке строки и кадра может быть много, в зависимости от желания и от возможностей конкретного чипа.
Конечная цель понять алгоритмику рисования
Для меги8 вся алгоритмика сводится к выводу по строчному синхроимпульсу (int0 у Вас) расчетного количества байт в строку,
похоже у Вас уже это работает, в чём тогда вопрос (?)…
У stm32 всё одновременно и сложней и проще, там DMA есть, но куча других нюансов, требующих отдельного разговора…
Большое спасибо за ответы!!!
Для меги8 вся алгоритмика сводится к выводу по строчному синхроимпульсу (int0 у Вас) расчетного количества байт в строку, похоже у Вас уже это работает, в чём тогда вопрос (?)…
Вы не могли бы пояснить по NOP-ам в конце моего поста не совсем понял как правильно их считать (то что у меня работает я подсмотрел в cl-osd) и как грамотно использовать, чтобы четко попадать во время.
У stm32 всё одновременно и сложней и проще, там DMA есть, но куча других нюансов, требующих отдельного разговора…
Скажите в stm32 Вы рисуете задержками или каким-то образом используете SPI? Здесь на форуме видел схему Вашего OSD и изучал конфигурацию stm-а, конечно большого опята нет, у Вас там TIM1,TIM2,TIM9 и SPI1 используется по схеме и судя по точным задержкам таймеров они Вам нужны для отрисовки строк. Интересуюсь чисто алгоритмически.
SPI при таком использовании очень удобен, закинул байт и забыл, но в как быть с обводкой черным (или градацией черного) белого цвета. Меняя направление порта черного цвета (в моем случае DDB0) можно сделать все нулевые биты данным цветом, т.е. получается черный фон, вопрос как сделать именно окаймление черным. На данном форуме читал, что для этого на stm-ах используют SPI1,SPI2 и получается выводят два слоя черный и белый?
На STM32 Вы контролируете вывод каждого пиксела?
как правильно их считать
Считать - дело неблагодарное, проще подогнать экспериментально, смысл простой:
после строчного прерывания необходимо сделать некоторую задержку NOPами до вывода первого байта (левый отступ) и затем в цикле “кидать” в SPDR необходимое количество байт (в зависимости от горизонтального разрешения)… Ну и далее аналогично следующую строку и т.д. до окончания кадра. всё…
Вы рисуете задержками или каким-то образом используете SPI?
Тот же SPI, но только через DMA, и с внешним тактированием от таймера.
но в как быть с обводкой черным (или градацией черного) белого цвета.
У меня работает синхронно два SPI, т.е. один выводит белый цвет другой чёрный, естественно оперативки надо в два раза больше, ну и символы соответственно хранятся в двухцветном виде…
судя по точным задержкам таймеров они Вам нужны для отрисовки строк. Интересуюсь чисто алгоритмически.
Здесь задача такая: максимально избавиться от программного переключения между строками и между полукадрами, (при разрешении выше чем 320х240 уже необходимо контролировать и полукадры у видеосигнала) поэтому куча таймеров и т.д., всё фактически выводится аппаратно… Был у меня вариант (рабочий) где вообще вся отрисовка картинки была аппаратной, т.е. ядро контроллера вообще не участвовало в работе, но там маленькая обвязочка из одной микрухи всёж понадобилась…
Естественно всё это возможно только на “продвинутых” камнях с мощной аппаратной периферией, типа stm…
вопрос как сделать именно окаймление черным.
на Меге8 реально никак, можно только сделать белый символ на черном фоне (целое знакоместо)
Oleg70, спасибо!!!
Получил ответы на все вопросы, будем искать. По результатам, обязательно отчитаюсь здесь.
или градацией черного
Да кстати, градации серого делаются (даже на Меге) по другому принципу, уже не через SPI, а параллельным выводом байта в порт ввода/вывода, тогда отдельные ноги порта (биты) при помощи внешнего резистивного делителя смешиваются, и получается нечто вроде ЦАП и полученное напряжение уже рисует пиксель… Сам я такой изврат не пробовал, но пробовал использовать аппаратный ЦАП stm, получается круто, только вот за пикселями “тянется хвост” из-за высокоомного выхода самого порта, короче побаловался и забил…
посмотрите как у hydra или как его там сделано на stm32f4, кроме дма spi еще пара резисторов на подтяжку уровней белого и черного.
видел в cleanflight вроде.