С stm32f103 на телевизор (полный TV сигнал)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Не то, чтобы я считаю эту тему актуальной, но ведь кому-то понадобилось делеть TV-выход на Nano даже при том, что там отображается всего 20 символов в строке...

Опять же, не прошло и месяца, как аналоговое телевидение вышло из моды. ))) А аналоговые телевизоры остались...

Ну и на Ардуино выбор экранов (с разрешением от 128 по горизнтали) обычно ограничивается двумя непересекающимися диапазонами: 

- от менее дюйма до примерно 4 юймов,

- от 20-40 дюймов и больше.

А промежуточный диапазон перекрывают как раз аналоговые TV-приемники.

В общем, первые прикидки показали, что можно сделать на stm32 формирователь полного телевизионного сигнала с разрешением экрана 512х240 пикселей, что дает 64 символа в строке для символов 8х8 или 8х12, а также до 85 символов в строке для достаточно стандартного для Ардуино фонта 6х8 (5х7).

Первая попытка:

Здесь шахматное поле с клеткой 16х16, поверх которого нарисована одниписельная прямая из левого верхнего угла вправо вниз, а также надписи шрифтами 6х8, 8х12 (зернкально) и 8х8 (жирный).

b707
Offline
Зарегистрирован: 26.05.2017

Интересно, хотя и непонятно зачем :)

С интересом жду подробностей по реализации этого в коде.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

О_О

nik182
Онлайн
Зарегистрирован: 04.05.2015

Давно хотел реализовать на работу табло с текущими параметрами эксперимента на большом экране 40 дюймого телевизора притащенного аспирантами из дома за ненадобностью. Уже полгода лежит без дела. Присоединяюсь. Реализую в ближайшее время. Только шрифт будет большим, чтобы из далека было видно.

P.S. Они уже всё сделали :-(  

https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F1/libraries/A_STM32_Examples/examples/Maple/CrudeVGA/CrudeVGA.ino

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

b707, я тоже не очень понимаю, зачем.

Ну, например, есть у меня Orange Zero. Непроверенный. Потому как без видеоконтроллера, но с TTL UART. Так что можно было бы использовать подобную конструкцию в качестве видеотерминала.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

andriano пишет:

b707, я тоже не очень понимаю, зачем.

Ну, например, есть у меня Orange Zero. Непроверенный. Потому как без видеоконтроллера, но с TTL UART. Так что можно было бы использовать подобную конструкцию в качестве видеотерминала.

Очень напомнило:

Однажды Ходжу Насреддина спросили: 

-- Как избавиться от крыс?

-- Очень просто! - сообщил  Ходжа.

-- Нужно растолочь черепки в порошок и, когда поймаешь крысу, засыпать ей это порошок в ноздри. Крыса не сможет дышать и издохнет.

-- Но если я поймал крысу, не проще ли убить ее, ударив об стену?

-- Конечно проще! - ответил Ходжа.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Ну, этот исходник я, разумеется, изучил и даже кое-что из него замствовал.

То есть полезное там как бы есть, но использовать это на практике нельзя.

Похоже, выглядит это так:

1. Все это делается программно, причем, по горизонтали получается что-то чуть больше 20 "пикселов".

2. Есть один неприятный момент, который мне как раз хотелось бы обсудить.

Меня интересуют строки исходника 91 и 107. В первую очередь, конечно, 107, потому что 91, на мой взгляд, совершенно бесполезна.

Дело в том, что без этих строк (в том числе и у меня) на экране промелькивают помехи, а в результате Serial.end() эти помехи исчезают, но одновременно с этим "отваливается" USB порт. Т.е. перепрошить плату в дельнейшем удается только, нажав в нужный момент на RESET. Похоже, этот Serial.end() убивает как USB, так и милисекундные прерывания системы времени.

В общем, кто по этому поводу что может сказать?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

wdrakula пишет:

Очень напомнило:

Однажды Ходжу Насреддина спросили: 

-- Как избавиться от крыс?

-- Очень просто! - сообщил  Ходжа.

-- Нужно растолочь черепки в порошок и, когда поймаешь крысу, засыпать ей это порошок в ноздри. Крыса не сможет дышать и издохнет.

-- Но если я поймал крысу, не проще ли убить ее, ударив об стену?

-- Конечно проще! - ответил Ходжа.

 

Вечер. Отец с сыном гуляют по берегу реки. На берегу сидит художник и рисует воду, мост через реку, закат...

Отец сыну(наставительно):

- Видишь, как дядя мучается без фотоаппарата?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015
Собственно, идея использовать Ардуино в качестве видеокарты не нова: https://www.instructables.com/id/TV-Out-with-Arduino/
Но в данном проекте:
- слишком мало пикселей в строке - порядка 20,
- насколько мне известно, проект не собирается в современных версиях среды (могу и ошибаться - сам не пробовал ввиду отсутствия интереса).
 
В общем-то, хотелось бы, чтобы экран был более или менее полноценным и позволял разместить не менее сраницы текста (29 строк примерно по 55-58 символов). А еще лучше - если стандартные экранные 80 символов.
Считаем: при ширине знакоместа 6 пикселей это соответствует 360-480 пикселей, а при знакоместе 8 пикселей - всего 480-640 пискелей в строке.
Длина строки - 63.5-64 мкс (в зависимости от системы вещания).
Длина видимой части строки по ГОСТ 18471 - 52 мкс, но известно, что телевизор обрезает рая изображения, что для "компьютерного" применения абсолютно неприемлемо. У имеющегося у меня 8" телевизора видимая часть строки составляет примерно 47 мкс. Исходя из этого составим табличку:
 
количество    пиксельная 
 пикселей      частота,
 в строке        МГц
   360          7.66 МГц
   480         10.2  МГц
   512         10.9  МГц
   640         13.6  МГц

В то же время ширина спектра сигнала изображения составляет 6.25 МГц, что соответствует пикселной частоте 12.5 МГц.

Понятно, что на частоте 72 МГц обеспечить программный вывод пикселей на указанных частотах (т.е. по 6-7 тактов на пиксель) невозможно. Остается только аппаратный. А наиболее приемлемый вариант - интерфейс SPI.
 
Правда, SPI на stm32 может работать только на частотах 18 МГц, 9 МГц и ниже. В первом случае сигнал оказывется далеко за пределами полосы видеосигнала, а потому не будет адакватно отображаться на телевизоре, а во втором - в строке окажется только 423 пикселя, что соответствует 52-70 символов в зависимости от ширины знакоместа. Хотелось бы немного больше.
 
В то же время таймеры 72-МГц процессора могут генерировать сигнал с частотами 10.3 (коэффициент деления 7) и 12(коэффициент деления 6) МГц. Но как получить эти частоты от SPI?
 
Единственный вариант, который пришел мне в голову, - выдавать таймером такой сигнал наружу, а SPI заставить работать в режиме slave. Ну и, чтобы не особо напрягать процессор, поручить DMAС взаимодействие с SPI.
 
К сожалению, в Гугле примеров организации такого режима мне раскопать не удалось, поэтому пришлось заглянуть в дэйташит ))).
 
"О сколько нам открытий чудных готовит datasheet!" Во-первых, при экспериментах с SPI master я конфигурировал SCK в "альтернативный" режим. Но оказалось (наверное, для кого-то это очевидно, но я узнал только сейчас), что альтернативный бывает только выход, а вход нужно оставлять сконфигурированным по умолчанию, даже если он используется для специальной функции. Далее: дэйташит настоятельно рекомендует по окончании обмена останавлвать SPI записью в соответствующий бит 0. Оказалось, делать это не обязательно: достаточно просто еще раз записать в этот бит 1. Т.е. раньше мы уже писали туда 1, мы уверенно считываем оттуда 1, но SPI не работает. А если мы повторно запишем 1, интерфейс будет работать. Правда, в процессе работы SPI сохраняет фазу сигнала и, если длина строки окажется не кратной произведению коэффициента деления на размер байта, то на экране вместо шахматного поля мы увидим примерно следующее. И то в лучшем случае - в худшем картинка вообще не будет стационарной.
У меня получилось, что если для коэффициента деления 6 (частота 12 МГц) длина строки может быть равной ровно 64 мкс, то единственный вариант пригодный для коэффициента 7 (частота 10.3 МГц) - длина строки 63.777777... мкс.
 
В дальнейшем я думаю сделать возможность как аппаратного так и программного переключения между пиксельными частотами 10.3 и 12 МГц, пока же, если на фото в 0-м сообщении темы используется частота 12 МГц, то в скетче ниже - 10.3
#define TICK_PIX 7 // тиков (72 МГц) на пиксель, пиксельная частота =72000000/TICK_PIX
#define HS_PERIOD 4592 // 63.5-64 мс, 4572-4608 тактов, такты должны дельтся на TICK_PIX, для 7 оптимально 4592, для 6 - 4608
#define HS_START 1 // точка отсчета, условный 0, - начало импульса вертикальной синхронизации
#define HS_SYN_END 340 // конец импульса синхронизации 4.7 мкс
#define HS_SCREEN_START 837 // начало видимой части экрана 11.53 мс (831)
#define SCREEN_TICK_WIDTH (SCREEN_WIDTH*TICK_PIX) // =3360 - ширина видимой части экрана
#define HS_SCREEN_END (SCREEN_TICK_WIDTH + HS_SCREEN_START) // =4191 - примерная точка конца видимой части экрана 58.17 мкс
#define NUM_SCAN_LINES 240 // количество линий растра
#define SCREEN_WIDTH 480 // ширина видимой части экрана в пискелях
#define SCR_BUFFER_WIDTH 65 // длина строки видеобуфера в байтах (64 байта = 512 пикселей макс. + 1 байт-бордюр, обеспечивающий гашение луча)

#include <SPI.h>
#include "a_Small_Rus.c"
#include "font_c.c"

#define ph0 0 //LOW
#define ph1 1 //HIGH

#define PB00_Out  (*((volatile unsigned long *) 0x42218180 )) // 
#define PB01_Out  (*((volatile unsigned long *) 0x42218184 )) // 
#define PB02_Out  (*((volatile unsigned long *) 0x42218188 )) // 
#define PB03_Out  (*((volatile unsigned long *) 0x4221818C )) // 
#define PB04_Out  (*((volatile unsigned long *) 0x42218190 )) // 
#define PB05_Out  (*((volatile unsigned long *) 0x42218194 )) // 
#define PB06_Out  (*((volatile unsigned long *) 0x42218198 )) // 
#define PB07_Out  (*((volatile unsigned long *) 0x4221819C )) // 
#define PB08_Out  (*((volatile unsigned long *) 0x422181A0 )) // 
#define PB09_Out  (*((volatile unsigned long *) 0x422181A4 )) // 
#define PB10_Out  (*((volatile unsigned long *) 0x422181A8 )) // 
#define PB11_Out  (*((volatile unsigned long *) 0x422181AC )) // 
#define PB12_Out  (*((volatile unsigned long *) 0x422181B0 )) // 
#define PB13_Out  (*((volatile unsigned long *) 0x422181B4 )) // 
#define PB14_Out  (*((volatile unsigned long *) 0x422181B8 )) // 
#define PB15_Out  (*((volatile unsigned long *) 0x422181BC )) // 

#define pin_B6    PB06_Out // PB6
#define pin_video PB07_Out // PB7
#define pin_gash  PB08_Out // PB8
#define pin_sync  PB09_Out // PB9

#define NOP __asm__ __volatile__ ("nop\n\t")

byte buf[SCR_BUFFER_WIDTH*NUM_SCAN_LINES]; // 65*240=15600 экранный буфер
int lineNumber = 0; // текущий номер строки при сканировании видеопамяти
int toHS = ph1;     // ячейка для запоминания состояния HS - сразу по прерыванию заносится в порт, а потом вычисляется значение для следующей строки
uint32_t lenLine = 64; // длина видимой части строки в байтах, зависит только от режима: 60 при 480 пикселях и 64 - при 512
int deltaLine = 0;                         // ячейка для хранения смещения строки для очередной строки растра
int Xchar, Ychar; // пиксельные координаты верхнего левого угла текущего знакоместа (куда выводим символ)
void (*putChar)(unsigned char); // текущая процедура вывода символа на экран

inline void startHS() {   // начало импульса HS, переводится из высокого состояния в низкое
  pin_sync = ph0;
}

inline void endHS() {   // конец импульса HS, остается низким во время кадровой синхронизации
  pin_sync = toHS;
  toHS = (lineNumber <= 247) || (lineNumber >= 255);  // импульс VS  262-275
}

inline void endScreen() {   // конец видимой строки экрана, пока ничего не делаем, но вдруг понадибится...
}

inline void startScreen() {
  if(lineNumber < NUM_SCAN_LINES) {
    pin_B6 = ph0;
    DMA1_BASE->CCR3 &= 0xfffe; //|= 0;
    DMA1_BASE->CMAR3 = (uint32)&buf[deltaLine];
    DMA1_BASE->CNDTR3 = lenLine+1;
    DMA1_BASE->CCR3  = 0x3091; // 0x3093
    SPI1_BASE->CR1   = 0xC2C8;  // 0xffbf; // 0x021C;
    pin_B6 = ph1;
    TIMER2_BASE->CCER = 0x0010;  // CC2 enable    
  }
  lineNumber++;
  deltaLine += SCR_BUFFER_WIDTH;
  if (lineNumber >= 292) { // 288 конец кадра
    lineNumber = 0;
    deltaLine = 0;
  }
}

void inline setPixel(int x, int y) {
  buf[x/8 + y*SCR_BUFFER_WIDTH] |= 1 << (x%8);
}

void inline clearPixel(int x, int y) {
  buf[x/8 + y*SCR_BUFFER_WIDTH] &= ~(1 << (x%8));
}

void putChar6x8(unsigned char ch) {
  for(int i = 0; i < 5; i++) {
    byte b = SmallFont6[(ch - ' ')*5 + 4 + i];
    for(int j = 0; j < 8; j++) {
      if(b & 1) setPixel(Xchar, Ychar+j);
      else    clearPixel(Xchar, Ychar+j);
      b = b >> 1;
    }
    Xchar++;
  }
  for(int j = 0; j < 8; j++)
    clearPixel(Xchar, Ychar+j);
  Xchar++;
}

void putChar8x8(unsigned char ch) {
  for(int i = 0; i < 8; i++) {
    buf[Xchar/8 + (Ychar + i)*SCR_BUFFER_WIDTH] = font_c[(ch - ' ')*8 + i];
  }
  Xchar += 8;
}

void scrollUp() {
  memcpy(&buf[0], &buf[8*SCR_BUFFER_WIDTH], (NUM_SCAN_LINES - 8)*SCR_BUFFER_WIDTH);
  memset(&buf[(NUM_SCAN_LINES - 8)*SCR_BUFFER_WIDTH], 0, 8*SCR_BUFFER_WIDTH);
  Xchar = 0;
  Ychar = (NUM_SCAN_LINES - 8);
}

void chessField() {
  for(int i = 0; i < (15600); i++) {
    int L = i/SCR_BUFFER_WIDTH;
    int J = i%SCR_BUFFER_WIDTH;
    int L0 = L/16&1;
    int J0 = J/2&1;
    buf[i] = 255*((L0+J0)&1);
  }
  for(int i = 0; i < NUM_SCAN_LINES; i++) if(buf[i*SCR_BUFFER_WIDTH] == 0) buf[i*SCR_BUFFER_WIDTH] = 2; else buf[i*SCR_BUFFER_WIDTH] = 253;  // 
}

void screenInit() {
  for(int i = 0; i < NUM_SCAN_LINES; i++) buf[i*SCR_BUFFER_WIDTH+lenLine] = 0; // заполнение последнего байта в строке нулями - гашение луча

  GPIOB_BASE->CRL = 0x33444444;     // пины PB7,PB6 - выход, остальные - вход (не исп.)
  GPIOB_BASE->CRH = 0x44444433;     // пины PB9-PB8 - выход, остальные - вход (не исп.)
  GPIOA_BASE->CRL = 0xbb4444b4;     // пины MOSI(pa7), MISO(pa6), SCK(pa5), T2C2(PA1) - выход таймера
//  GPIOA_BASE->CRL = 0xbb4444b4;     // пины MOSI(pa7), MISO(pa6), SCK(pa5), T2C2(PA1) - выход таймера

  RCC_BASE->AHBENR |= 0x00000001; // включение тактового сигнала DMA
  RCC_BASE->APB2ENR |= (1 << 12); // включение тактового сигнала SPI
  DMA1_BASE->CPAR3 = 0x4001300C;
  DMA1_BASE->CMAR3 = (uint32)buf;
  DMA1_BASE->CNDTR3 = 13;
  SPI1_BASE->CR1   = 0xC288;  // 0xffbf; // 0x021C;
  SPI1_BASE->CR2   = 0x0082; // 0x0082
  DMA1_BASE->CCR3  = 0x3090; // 0x3092
  
    Timer4.pause(); // while we configure
    Timer4.setPrescaleFactor(1);     // Full speed
    Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setChannel2Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setChannel3Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setChannel4Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setOverflow(HS_PERIOD - 1); // 4591 : 63.7 ms, для делителя 7 оптимально 4592-1

    Timer4.setCompare1(HS_START);    // 1 - начало отсчета
    Timer4.attachCompare1Interrupt(startHS); // начало HS
    Timer4.setCompare2(HS_SYN_END);    // 339 - конец импульса синхронизации 4.7 мкс
    Timer4.attachCompare2Interrupt(endHS); // конец HS
    Timer4.setCompare3(HS_SCREEN_START);   // 831 - начало видимой части экрана 11.53 мс
    Timer4.attachCompare3Interrupt(startScreen); // начало видимой строки
    Timer4.setCompare4(HS_SCREEN_END);      // =4191 - примерная точка конца видимой части экрана 58.17 мкс
    Timer4.attachCompare4Interrupt(endScreen);
    
    Timer4.setCount(0);         // Ready...
    Timer4.resume();            // Go! 

    TIMER2_BASE->PSC = 0;            // prescaler 
    TIMER2_BASE->ARR = TICK_PIX - 1; // 6=7-1, где 7 - количество тиков (72МГц) на пиксель
    TIMER2_BASE->CCR2 = TICK_PIX/2 - 1; // 2
    TIMER2_BASE->CCER = 0x0010;  // CC2 enable    
  delayMicroseconds(40);
}

void setup() {
  Serial.begin(115200); // Ignored by Maple. But needed by boards using hardware serial via a USB to Serial adaptor ]'
  while(!Serial) ;

  uint32_t t0 = micros();
  scrollUp();

  uint32_t t1 = micros();
  chessField();
  
  uint32_t t2 = micros();
  for(int i = 0; i < 240; i++) setPixel(i, i);       // диагональная линия
  
  uint32_t t3 = micros();
  Xchar = 0;
  Ychar = 36;
  putChar = putChar6x8;
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);

  uint32_t t4 = micros();
  Xchar = 0;
  Ychar = 76;
  putChar = putChar8x8;
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);

  uint32_t t5 = micros();

  Serial.print("Scroll Up    ");
  Serial.println(t1-t0);
  Serial.print("Chess Field  ");
  Serial.println(t2-t1);
  Serial.print("240 pixels   ");
  Serial.println(t3-t2);
  Serial.print("60 chars 6x8 ");
  Serial.println(t4-t3);
  Serial.print("60 chars 8x8 ");
  Serial.println(t5-t4);

  Xchar = 0;
  Ychar = 207;
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);
  Xchar = 0;
  Ychar = 215;
  for(int i = 59; i < 119; i++) putChar(' ' + i + 1);
  Xchar = 0;
  Ychar = 223;
  for(int i = 118; i < 163; i++) putChar(' ' + i + 1);
  
  putChar = putChar6x8;
  Xchar = 0;
  Ychar = 183;
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);
  Xchar = 0;
  Ychar = 191;
  for(int i = 59; i < 119; i++) putChar(' ' + i + 1);
  Xchar = 0;
  Ychar = 199;
  for(int i = 118; i < 176; i++) putChar(' ' + i + 1);

  screenInit();
  
     // This gets rid of the majority of the interrupt artifacts;
    // a SysTick.end() is required as well
//    Serial.end();

  putChar = putChar8x8;
  Xchar = 480;
  Ychar = 239;
}

void loop() {
  if(Serial.available()) {
    unsigned char ch = Serial.read();
    Serial.println(ch);
    if(ch == 13) {
      Xchar = 0;
      Ychar += 8;
      if(Ychar > (NUM_SCAN_LINES-8)) {
        scrollUp();
      }
      ch = 0;
    }
    if(Xchar > 472) {
      scrollUp();
    }
    if(ch != 0) putChar(ch);
  }
}

 

Пока программа находится в очень сыром виде, и даже больше - в эклектичном, например, инициализация одного таймера произведена через класс (заимствование из чужого проекта), то другого - через регистры. Ну и т.д...
для работы скетча нужны файлы знакогенератора для двух фонтов - 6х8 и 8х8:
#if defined(__AVR__)
  #include <avr/pgmspace.h>
  #define fontdatatype const uint8_t
#elif defined(__PIC32MX__)
  #define PROGMEM
  #define fontdatatype const unsigned char
#elif defined(__arm__)
  #define PROGMEM
  #define fontdatatype const unsigned char
#endif

// New font 177 symbols 5x8 = 885+4 bytes

fontdatatype SmallFont6[] PROGMEM =
{
0x05, 0x08, 0x20, 0xb1,
0x00, 0x00, 0x00, 0x00, 0x00,   // sp
0x00, 0x00, 0x2f, 0x00, 0x00,   // !
0x00, 0x07, 0x00, 0x07, 0x00,   // "
0x14, 0x7f, 0x14, 0x7f, 0x14,   // #
0x24, 0x2a, 0x7f, 0x2a, 0x12,   // $
0x23, 0x13, 0x08, 0x64, 0x62,   // %
0x36, 0x49, 0x55, 0x22, 0x50,   // &
0x00, 0x05, 0x03, 0x00, 0x00,   // '
0x00, 0x1c, 0x22, 0x41, 0x00,   // (
0x00, 0x41, 0x22, 0x1c, 0x00,   // )
0x14, 0x08, 0x3E, 0x08, 0x14,   // *
0x08, 0x08, 0x3E, 0x08, 0x08,   // +
0x00, 0x00, 0xA0, 0x60, 0x00,   // ,
0x08, 0x08, 0x08, 0x08, 0x08,   // -
0x00, 0x60, 0x60, 0x00, 0x00,   // .
0x20, 0x10, 0x08, 0x04, 0x02,   // /

0x3E, 0x51, 0x49, 0x45, 0x3E,   // 0
0x00, 0x42, 0x7F, 0x40, 0x00,   // 1
0x42, 0x61, 0x51, 0x49, 0x46,   // 2
0x21, 0x41, 0x45, 0x4B, 0x31,   // 3
0x18, 0x14, 0x12, 0x7F, 0x10,   // 4
0x27, 0x45, 0x45, 0x45, 0x39,   // 5
0x3C, 0x4A, 0x49, 0x49, 0x30,   // 6
0x01, 0x71, 0x09, 0x05, 0x03,   // 7
0x36, 0x49, 0x49, 0x49, 0x36,   // 8
0x06, 0x49, 0x49, 0x29, 0x1E,   // 9
0x00, 0x36, 0x36, 0x00, 0x00,   // :
0x00, 0x56, 0x36, 0x00, 0x00,   // ;
0x08, 0x14, 0x22, 0x41, 0x00,   // <
0x14, 0x14, 0x14, 0x14, 0x14,   // =
0x00, 0x41, 0x22, 0x14, 0x08,   // >
0x02, 0x01, 0x51, 0x09, 0x06,   // ?

0x32, 0x49, 0x59, 0x51, 0x3E,   // @
0x7C, 0x12, 0x11, 0x12, 0x7C,   // A
0x7F, 0x49, 0x49, 0x49, 0x36,   // B
0x3E, 0x41, 0x41, 0x41, 0x22,   // C
0x7F, 0x41, 0x41, 0x22, 0x1C,   // D
0x7F, 0x49, 0x49, 0x49, 0x41,   // E
0x7F, 0x09, 0x09, 0x09, 0x01,   // F
0x3E, 0x41, 0x49, 0x49, 0x7A,   // G
0x7F, 0x08, 0x08, 0x08, 0x7F,   // H
0x00, 0x41, 0x7F, 0x41, 0x00,   // I
0x20, 0x40, 0x41, 0x3F, 0x01,   // J
0x7F, 0x08, 0x14, 0x22, 0x41,   // K
0x7F, 0x40, 0x40, 0x40, 0x40,   // L
0x7F, 0x02, 0x0C, 0x02, 0x7F,   // M
0x7F, 0x04, 0x08, 0x10, 0x7F,   // N
0x3E, 0x41, 0x41, 0x41, 0x3E,   // O

0x7F, 0x09, 0x09, 0x09, 0x06,   // P
0x3E, 0x41, 0x51, 0x21, 0x5E,   // Q
0x7F, 0x09, 0x19, 0x29, 0x46,   // R
0x46, 0x49, 0x49, 0x49, 0x31,   // S
0x01, 0x01, 0x7F, 0x01, 0x01,   // T
0x3F, 0x40, 0x40, 0x40, 0x3F,   // U
0x1F, 0x20, 0x40, 0x20, 0x1F,   // V
0x3F, 0x40, 0x38, 0x40, 0x3F,   // W
0x63, 0x14, 0x08, 0x14, 0x63,   // X
0x07, 0x08, 0x70, 0x08, 0x07,   // Y
0x61, 0x51, 0x49, 0x45, 0x43,   // Z
0x00, 0x7F, 0x41, 0x41, 0x00,   // [
0x01, 0x06, 0x08, 0x30, 0x40,   // Backslash
0x00, 0x41, 0x41, 0x7F, 0x00,   // ]
0x04, 0x02, 0x01, 0x02, 0x04,   // ^
0x40, 0x40, 0x40, 0x40, 0x40,   // _

0x00, 0x03, 0x05, 0x00, 0x00,   // `
0x20, 0x54, 0x54, 0x54, 0x78,   // a
0x7F, 0x48, 0x44, 0x44, 0x38,   // b
0x38, 0x44, 0x44, 0x44, 0x20,   // c
0x38, 0x44, 0x44, 0x48, 0x7F,   // d
0x38, 0x54, 0x54, 0x54, 0x18,   // e
0x08, 0x7E, 0x09, 0x01, 0x02,   // f
0x18, 0xA4, 0xA4, 0xA4, 0x7C,   // g
0x7F, 0x08, 0x04, 0x04, 0x78,   // h
0x00, 0x44, 0x7D, 0x40, 0x00,   // i
0x40, 0x80, 0x84, 0x7D, 0x00,   // j
0x7F, 0x10, 0x28, 0x44, 0x00,   // k
0x00, 0x41, 0x7F, 0x40, 0x00,   // l
0x7C, 0x04, 0x18, 0x04, 0x78,   // m
0x7C, 0x08, 0x04, 0x04, 0x78,   // n
0x38, 0x44, 0x44, 0x44, 0x38,   // o

0xFC, 0x24, 0x24, 0x24, 0x18,   // p
0x18, 0x24, 0x24, 0x18, 0xFC,   // q
0x7C, 0x08, 0x04, 0x04, 0x08,   // r
0x48, 0x54, 0x54, 0x54, 0x20,   // s
0x04, 0x3F, 0x44, 0x40, 0x20,   // t
0x3C, 0x40, 0x40, 0x20, 0x7C,   // u
0x1C, 0x20, 0x40, 0x20, 0x1C,   // v
0x3C, 0x40, 0x30, 0x40, 0x3C,   // w
0x44, 0x28, 0x10, 0x28, 0x44,   // x
0x1C, 0xA0, 0xA0, 0xA0, 0x7C,   // y
0x44, 0x64, 0x54, 0x4C, 0x44,   // z
0x00, 0x10, 0x7C, 0x82, 0x00,   // {
0x00, 0x00, 0xFF, 0x00, 0x00,   // |
0x00, 0x82, 0x7C, 0x10, 0x00,   // }
0x08, 0x04, 0x08, 0x10, 0x08,   // 7E    126   ~
0x7C, 0x12, 0x11, 0x12, 0x7C,   // А

0x7F, 0x49, 0x49, 0x49, 0x31,   // Б
0x7F, 0x45, 0x45, 0x45, 0x3A,   // В
0x7F, 0x01, 0x01, 0x01, 0x03,   // Г
0x60, 0x3F, 0x21, 0x3F, 0x60,   // Д
0x7F, 0x49, 0x49, 0x49, 0x41,   // Е
0x73, 0x0C, 0x7F, 0x0C, 0x73,   // Ж
0x21, 0x41, 0x49, 0x4D, 0x33,   // З
0x7F, 0x10, 0x08, 0x04, 0x7F,   // И
0x7E, 0x20, 0x11, 0x08, 0x7E,   // Й
0x7F, 0x08, 0x14, 0x22, 0x41,   // К
0x40, 0x3F, 0x01, 0x01, 0x7F,   // Л
0x7F, 0x06, 0x08, 0x06, 0x7F,   // М
0x7F, 0x08, 0x08, 0x08, 0x7F,   // Н
0x3E, 0x41, 0x41, 0x41, 0x3E,   // О
0x7F, 0x01, 0x01, 0x01, 0x7F,   // П
0x7F, 0x09, 0x09, 0x09, 0x06,   // Р

0x3E, 0x41, 0x41, 0x41, 0x22,   // С
0x03, 0x01, 0x7F, 0x01, 0x03,   // Т
0x61, 0x26, 0x18, 0x06, 0x01,   // У
0x1C, 0x22, 0x7F, 0x22, 0x1C,   // Ф
0x63, 0x14, 0x08, 0x14, 0x63,   // Х
0x3F, 0x20, 0x20, 0x3F, 0x60,   // Ц
0x07, 0x08, 0x08, 0x08, 0x7F,   // Ч
0x7F, 0x40, 0x7F, 0x40, 0x7F,   // Ш
0x3F, 0x20, 0x3F, 0x20, 0x7F,   // Щ
0x01, 0x7F, 0x48, 0x48, 0x30,   // Ъ
0x7F, 0x48, 0x78, 0x00, 0x7F,   // Ы
0x7F, 0x48, 0x48, 0x30, 0x00,   // Ь
0x41, 0x49, 0x49, 0x2A, 0x1C,   // Э
0x7F, 0x10, 0x3E, 0x41, 0x3E,   // Ю
0x66, 0x19, 0x09, 0x09, 0x7F,   // Я
0x20, 0x54, 0x54, 0x78, 0x40,   // а

0x3E, 0x49, 0x45, 0x45, 0x38,   // б
0x7E, 0x4A, 0x4A, 0x34, 0x00,   // в
0x7C, 0x04, 0x04, 0x0C, 0x00,   // г
0x38, 0x45, 0x45, 0x49, 0x3E,   // д
0x38, 0x54, 0x54, 0x54, 0x18,   // е
0x4C, 0x30, 0x7C, 0x30, 0x4C,   // ж
0x24, 0x42, 0x4A, 0x34, 0x00,   // з
0x7C, 0x20, 0x10, 0x7C, 0x00,   // и
0x7C, 0x21, 0x11, 0x7C, 0x00,   // й
0x7C, 0x10, 0x28, 0x44, 0x00,   // к
0x40, 0x3C, 0x04, 0x04, 0x7C,   // л
0x7C, 0x08, 0x10, 0x08, 0x7C,   // м
0x7C, 0x10, 0x10, 0x7C, 0x00,   // н
0x38, 0x44, 0x44, 0x44, 0x38,   // о
0x7C, 0x04, 0x04, 0x7C, 0x00,   // п
0xFC, 0x18, 0x24, 0x24, 0x18,   // р

0x38, 0x44, 0x44, 0x44, 0x28,   // с
0x04, 0x04, 0x7C, 0x04, 0x04,   // т
0x4C, 0x90, 0x90, 0x90, 0x7C,   // у
0x18, 0x24, 0x7E, 0x24, 0x18,   // ф
0x44, 0x28, 0x10, 0x28, 0x44,   // х
0x3C, 0x20, 0x20, 0x3C, 0x60,   // ц
0x1C, 0x10, 0x10, 0x7C, 0x00,   // ч
0x7C, 0x40, 0x7C, 0x40, 0x7C,   // ш
0x3C, 0x20, 0x3C, 0x20, 0x7C,   // щ
0x04, 0x7C, 0x50, 0x70, 0x00,   // ъ
0x7C, 0x50, 0x70, 0x00, 0x7C,   // ы
0x7C, 0x50, 0x70, 0x00, 0x00,   // ь
0x42, 0x42, 0x52, 0x52, 0x3C,   // э
0x7C, 0x10, 0x38, 0x44, 0x38,   // ю
0x40, 0x2C, 0x12, 0x7E, 0x00,   // я
0x7E, 0x4B, 0x4A, 0x4B, 0x42,   //            Ё   D0 81

0x38, 0x55, 0x54, 0x55, 0x18,   //            ё   D1 91
0x7C, 0x04, 0x05, 0x04, 0x00,   //81    129   Ѓ   D0 83
0x00, 0x78, 0x0A, 0x09, 0x00,   //83    131   ѓ   D1 93
0x3E, 0x49, 0x49, 0x41, 0x22,   //AA    170   Є   D0 84
0x38, 0x54, 0x54, 0x44, 0x28,   //BA    186   є   D1 94
0x00, 0x41, 0x7F, 0x41, 0x00,   //B2    178   І   D0 86
0x00, 0x44, 0x7D, 0x40, 0x00,   //B3    179   і   D1 96
0x00, 0x45, 0x7C, 0x45, 0x00,   //AF    175   Ї   D0 87
0x00, 0x45, 0x7C, 0x41, 0x00,   //BF    191   ї   D1 97
0x23, 0x44, 0x39, 0x04, 0x03,   //A1    161   Ў   D0 8E
0x24, 0x49, 0x32, 0x09, 0x04,   //A2    162   ў   D1 9E
0x7E, 0x02, 0x02, 0x02, 0x01,   //A5    165   Ґ   D2 90
0x7C, 0x04, 0x04, 0x02, 0x00,   //B4    180   ґ   D2 91
0x00, 0x4A, 0x55, 0x29, 0x00,   //A7    167   §   C2 A7
0x00, 0x06, 0x09, 0x09, 0x06,   //            °   C2 B0
0x44, 0x44, 0x5F, 0x44, 0x44,   //B1    177   ±   C2 B1

0x7C, 0x10, 0x10, 0x3C, 0x40,   //B5    181   µ   C2 B5
};

#include <avr/pgmspace.h>

const unsigned char font_c[] PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0,  // 32   
24, 60, 60, 24, 24, 0, 24, 0,  // 33  !
54, 54, 20, 0, 0, 0, 0, 0,  // 34  "
54, 54, 127, 54, 127, 54, 54, 0,  // 35  #
8, 60, 2, 28, 32, 30, 8, 0,  // 36  $
6, 102, 48, 24, 12, 102, 96, 0,  // 37  %
30, 51, 30, 10, 83, 51, 126, 0,  // 38  &
24, 24, 24, 12, 0, 0, 0, 0,  // 39  '
48, 24, 12, 12, 12, 24, 48, 0,  // 40  (
12, 24, 48, 48, 48, 24, 12, 0,  // 41  )
0, 54, 28, 127, 28, 54, 0, 0,  // 42  *
0, 8, 8, 62, 8, 8, 0, 0,  // 43  +
0, 0, 0, 24, 24, 24, 12, 0,  // 44  ,
0, 0, 0, 60, 0, 0, 0, 0,  // 45  -
0, 0, 0, 0, 0, 24, 24, 0,  // 46  .
0, 96, 48, 24, 12, 6, 0, 0,  // 47  /
60, 102, 118, 110, 102, 102, 60, 0,  // 48  0
24, 24, 28, 24, 24, 24, 126, 0,  // 49  1
60, 102, 96, 48, 12, 6, 126, 0,  // 50  2
60, 102, 96, 56, 96, 102, 60, 0,  // 51  3
48, 56, 52, 50, 126, 48, 48, 0,  // 52  4
126, 6, 62, 96, 96, 102, 60, 0,  // 53  5
60, 102, 6, 62, 102, 102, 60, 0,  // 54  6
126, 102, 48, 48, 24, 24, 24, 0,  // 55  7
60, 102, 102, 60, 102, 102, 60, 0,  // 56  8
60, 102, 102, 124, 96, 102, 60, 0,  // 57  9
0, 24, 24, 0, 24, 24, 0, 0,  // 58  :
0, 24, 24, 0, 24, 24, 12, 0,  // 59  ;
48, 24, 12, 6, 12, 24, 48, 0,  // 60  <
0, 0, 60, 0, 60, 0, 0, 0,  // 61  =
6, 12, 24, 48, 24, 12, 6, 0,  // 62  >
60, 102, 96, 56, 24, 0, 24, 0,  // 63  ?
28, 34, 58, 26, 66, 60, 0, 0,  // 64  @
60, 102, 102, 126, 102, 102, 102, 0,  // 65  A
62, 102, 102, 62, 102, 102, 62, 0,  // 66  B
60, 102, 6, 6, 6, 102, 60, 0,  // 67  C
62, 102, 102, 102, 102, 102, 62, 0,  // 68  D
126, 6, 6, 62, 6, 6, 126, 0,  // 69  E
126, 6, 6, 62, 6, 6, 6, 0,  // 70  F
60, 102, 6, 6, 118, 102, 60, 0,  // 71  G
102, 102, 102, 126, 102, 102, 102, 0,  // 72  H
60, 24, 24, 24, 24, 24, 60, 0,  // 73  I
120, 48, 48, 48, 54, 54, 28, 0,  // 74  J
102, 54, 30, 14, 30, 54, 102, 0,  // 75  K
6, 6, 6, 6, 6, 6, 126, 0,  // 76  L
99, 119, 127, 107, 99, 99, 99, 0,  // 77  M
99, 103, 111, 123, 115, 99, 99, 0,  // 78  N
60, 102, 102, 102, 102, 102, 60, 0,  // 79  O
62, 102, 102, 102, 62, 6, 6, 0,  // 80  P
60, 102, 102, 102, 118, 60, 96, 0,  // 81  Q
62, 102, 102, 62, 30, 54, 102, 0,  // 82  R
60, 102, 6, 60, 96, 102, 60, 0,  // 83  S
126, 90, 24, 24, 24, 24, 24, 0,  // 84  T
102, 102, 102, 102, 102, 102, 124, 0,  // 85  U
102, 102, 102, 102, 102, 60, 24, 0,  // 86  V
99, 99, 99, 107, 127, 119, 99, 0,  // 87  W
99, 99, 54, 28, 54, 99, 99, 0,  // 88  X
102, 102, 102, 60, 24, 24, 24, 0,  // 89  Y
126, 96, 48, 24, 12, 6, 126, 0,  // 90  Z
60, 12, 12, 12, 12, 12, 60, 0,  // 91  [
0, 6, 12, 24, 48, 96, 0, 0,  // 92  BackSlash
60, 48, 48, 48, 48, 48, 60, 0,  // 93  ]
8, 20, 34, 65, 0, 0, 0, 0,  // 94  ^
0, 0, 0, 0, 0, 0, 60, 0,  // 95  _
12, 12, 24, 0, 0, 0, 0, 0,  // 96  `
0, 0, 60, 96, 124, 102, 124, 0,  // 97  a
6, 6, 6, 62, 102, 102, 62, 0,  // 98  b
0, 0, 60, 102, 6, 102, 60, 0,  // 99  c
96, 96, 96, 124, 102, 102, 124, 0,  // 100  d
0, 0, 60, 102, 126, 6, 60, 0,  // 101  e
56, 108, 12, 12, 62, 12, 12, 0,  // 102  f
0, 124, 102, 102, 124, 96, 60, 0,  // 103  g
6, 6, 6, 62, 102, 102, 102, 0,  // 104  h
0, 6, 0, 6, 6, 6, 79, 0,  // 105  i
48, 0, 48, 48, 54, 54, 28, 0,  // 106  j
6, 6, 102, 54, 30, 54, 102, 0,  // 107  k
24, 24, 24, 24, 24, 24, 24, 0,  // 108  l
0, 0, 99, 119, 127, 107, 107, 0,  // 109  m
0, 0, 62, 126, 102, 102, 102, 0,  // 110  n
0, 0, 60, 102, 102, 102, 60, 0,  // 111  o
0, 62, 102, 102, 62, 6, 6, 0,  // 112  p
0, 30, 27, 27, 30, 88, 120, 0,  // 113  q
0, 0, 62, 102, 102, 6, 6, 0,  // 114  r
0, 0, 124, 2, 60, 64, 62, 0,  // 115  s
0, 24, 24, 126, 24, 24, 24, 0,  // 116  t
0, 0, 102, 102, 102, 102, 124, 0,  // 117  u
0, 0, 0, 102, 102, 60, 24, 0,  // 118  v
0, 0, 99, 107, 107, 107, 62, 0,  // 119  w
0, 0, 102, 60, 24, 60, 102, 0,  // 120  x
0, 0, 102, 102, 124, 96, 60, 0,  // 121  y
0, 0, 60, 48, 24, 12, 60, 0,  // 122  z
56, 12, 12, 6, 12, 12, 56, 0,  // 123  {
8, 8, 8, 8, 8, 8, 8, 0,  // 124  |
14, 24, 24, 48, 24, 24, 14, 0,  // 125  }
0, 0, 92, 54, 0, 0, 0, 0,  // 126  ~
0, 0, 0, 0, 0, 0, 0, 0,  // 127  BackSpace
0, 62, 102, 102, 62, 6, 6, 0,  // 128  Р
0, 0, 60, 102, 6, 102, 60, 0,  // 129  с
0, 0, 126, 24, 24, 24, 24, 0,  // 130  т
0, 0, 102, 102, 124, 96, 60, 0,  // 131  у
0, 0, 60, 90, 90, 90, 60, 24,  // 132  ф
0, 0, 102, 60, 24, 60, 102, 0,  // 133  х
0, 0, 51, 51, 51, 51, 127, 96,  // 134  ц
0, 0, 102, 102, 126, 96, 96, 0,  // 135  ч
0, 0, 99, 107, 107, 107, 127, 0,  // 136  ш
0, 0, 99, 107, 107, 107, 255, 192,  // 137  щ
0, 0, 7, 62, 102, 102, 62, 0,  // 138  ъ
0, 0, 195, 207, 219, 219, 207, 0,  // 139  ы
0, 0, 6, 62, 102, 102, 62, 0,  // 140  ь
0, 0, 60, 98, 120, 98, 60, 0,  // 141  э
0, 0, 115, 219, 223, 219, 115, 0,  // 142  ю
0, 0, 124, 102, 126, 108, 102, 0,  // 143  я
60, 102, 102, 126, 102, 102, 102, 0,  // 144  А
126, 6, 62, 102, 102, 102, 62, 0,  // 145  Б
62, 102, 102, 62, 102, 102, 62, 0,  // 146  В
126, 6, 6, 6, 6, 6, 6, 0,  // 147  Г
56, 52, 54, 54, 54, 54, 127, 99,  // 148  Д
126, 6, 6, 62, 6, 6, 126, 0,  // 149  Е
219, 219, 90, 188, 90, 219, 219, 0,  // 150  Ж
60, 102, 96, 56, 96, 102, 60, 0,  // 151  З
102, 102, 102, 102, 118, 110, 102, 0,  // 152  И
24, 0, 102, 102, 118, 110, 102, 0,  // 153  Й
102, 54, 30, 14, 30, 54, 102, 0,  // 154  К
112, 104, 108, 108, 108, 108, 110, 0,  // 155  Л
99, 119, 127, 107, 99, 99, 99, 0,  // 156  М
102, 102, 102, 126, 102, 102, 102, 0,  // 157  Н
60, 102, 102, 102, 102, 102, 60, 0,  // 158  О
126, 102, 102, 102, 102, 102, 102, 0,  // 159  П
62, 102, 102, 102, 62, 6, 6, 0,  // 160  Р
60, 102, 6, 6, 6, 102, 60, 0,  // 161  С
126, 90, 24, 24, 24, 24, 24, 0,  // 162  Т
102, 102, 102, 124, 96, 102, 60, 0,  // 163  У
126, 219, 219, 219, 254, 24, 24, 0,  // 164  Ф
99, 99, 54, 28, 54, 99, 99, 0,  // 165  Х
51, 51, 51, 51, 51, 51, 127, 96,  // 166  Ц
102, 102, 102, 126, 96, 96, 96, 0,  // 167  Ч
99, 99, 107, 107, 107, 107, 127, 0,  // 168  Ш
99, 99, 107, 107, 107, 107, 255, 192,  // 169  Щ
7, 7, 62, 102, 230, 102, 62, 0,  // 170  Ъ
195, 195, 207, 219, 219, 219, 207, 0,  // 171  Ы
6, 6, 62, 102, 102, 102, 62, 0,  // 172  Ь
60, 102, 96, 120, 96, 102, 60, 0,  // 173  Э
115, 219, 219, 223, 219, 219, 115, 0,  // 174  Ю
124, 102, 102, 102, 120, 108, 102, 0,  // 175  Я
0, 0, 60, 96, 124, 102, 124, 0,  // 176  а
64, 60, 6, 62, 102, 102, 60, 0,  // 177  б
12, 22, 22, 14, 62, 102, 60, 0,  // 178  в
0, 0, 126, 6, 6, 6, 6, 0,  // 179  г
0, 0, 60, 54, 54, 54, 127, 99,  // 180  д
0, 0, 60, 102, 126, 6, 60, 0,  // 181  е
0, 0, 219, 90, 60, 90, 219, 0,  // 182  ж
0, 0, 60, 102, 48, 102, 60, 0,  // 183  з
0, 0, 102, 102, 118, 110, 102, 0,  // 184  и
24, 0, 102, 102, 118, 110, 102, 0,  // 185  й
0, 0, 102, 54, 30, 54, 102, 0,  // 186  к
0, 0, 112, 104, 108, 108, 110, 0,  // 187  л
0, 0, 99, 247, 235, 99, 99, 0,  // 188  м
0, 0, 102, 102, 126, 102, 102, 0,  // 189  н
0, 0, 60, 102, 102, 102, 60, 0,  // 190  о
0, 0, 126, 102, 102, 102, 102, 0,  // 191  п
102, 0, 126, 6, 62, 6, 126, 0,  // 192  Ё
36, 0, 60, 102, 126, 6, 60, 0,  // 193   ё
0, 0, 0, 0, 0, 0, 0, 0,  // 194  
0, 0, 0, 0, 0, 0, 0, 0  // 195  
};

 

 
Сразу отмечу, что пока КИРИЛЛИЦА НЕ ПОДДЕРЖИВАЕТСЯ. То, что видно на экране - лишь содержимое файлов знакогенераторов, чтобы появилась полноценная кириллица, необходима соответствующая программная поддержка, которой я пока не делал.
 
Скетч взаимодействет с монитором порта: введенное в строку монитора отображается на телевизоре (помните, что я писал выше про кириллицу). Поэтому без открытого монитора порта скетч не запускается.
Кроме того, на экране присутствуют помехи, о чем я писал ранее.
 
Ну и краткое описание схемы подключения (в дальнейшем она, думаю, будет изменена).
Важно !!! - подключить выход таймера PA1 ко входу SCK PA5.
Сигнал Video снимается с выхода MISO (PA6) через резистор 430 Ом.
Сигнал Sync снимается с выхода PB9 через резистор 820 Ом.
Еще несколько выводов PB использовались мною для отладки логическим анализатором, к ним нежелательно ничего подключать или замыкать между собой.
Полный телевизионный сигнал снимается с общей точки двух указанных резисторов через керамический конденсатор 4.7 мкФ.
 
 
andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Выяснил, что после отключения USB (Serial.end()) последовательный порт (Serial1) сохраняет работоспособность.

Значит, так и будет работать: USB выключаем дабы избавиться от помех на экране, а обмен ведем через COM-порт (PA9, PA10).

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Видео одного из промежуточных вариантов: https://youtu.be/kdt_LOdkxfY

Выводит на экран текст, передаваемый по последовательному порту.

Имеет два режима экрана 480х240 и 512х240 пикселей, позволяет переключаться между ними в процессе работы посредством управляющих последовательностей.

Поддерживает два фонта: 6х8 и 8х8 пикселей, которые могут присутствовать на экране одновременно. Переключаются управляющими последовательностями.

Позволяет позиционировать место вывода.

Позволяет рисовать линии посредством управляющих последовательностей.

Пока:

- нет борьбы с помехами,

- не поддерживается кириллица,

- не поддерживаются аппаратные настройки (например, переключение кодовой страницы, что будет актуально после подключения кириллицы - планируется utf-8, 866, 1251 и КОИ8).

Для записи ролика был создан сценарий, т.е. последовательность символов, передаваемая в порт. В качестве источника данных была использована Мега. К проекту она никакого отношения не имеет, вместо нее можно было использовать ардуиновский монитор порта, но я, увы, не могу соревноваться с Мегой в скорости ввода символов.

Код будет выложен ближайшие день-два.

PS. Ни у кого нет на примете фонта 8х8 (желательно жирного) с украинско-белорусской кириллицей?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

А вот и обещанные исходники:

stm_strl.ino

#include <SPI.h>
#include "screen.h"

void setup() {
  Serial.begin(115200); // Ignored by Maple. But needed by boards using hardware serial via a USB to Serial adaptor ]'
  while(!Serial) ;

  uint32_t t0 = micros();
  scrollUp();

  uint32_t t1 = micros();
  chessField();
  
  uint32_t t2 = micros();
  setXY(0, 0);
  lineTo(239, 239);
  
  uint32_t t3 = micros();
  setXY(0, 36);
  setCharSize6x8();
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);

  uint32_t t4 = micros();
  setXY(0, 76);
  setCharSize8x8();
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);

  uint32_t t5 = micros();

  setXY(0, 207);
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);
  setXY(0, 215);
  for(int i = 59; i < 119; i++) putChar(' ' + i + 1);
  setXY(0, 223);
  for(int i = 118; i < 163; i++) putChar(' ' + i + 1);
  
  setCharSize6x8();
  setXY(0, 183);
  for(int i = 0; i < 60; i++) putChar(' ' + i + 1);
  setXY(0, 191);
  for(int i = 59; i < 119; i++) putChar(' ' + i + 1);
  setXY(0, 199);
  for(int i = 118; i < 176; i++) putChar(' ' + i + 1);

  Serial1.begin(9600);
  delay(1);

  setScreenMode(SCREEN_MODE_480);
  
     // This gets rid of the majority of the interrupt artifacts;
    // a SysTick.end() is required as well
  //  Serial.end();


  Serial.print("Scroll Up    ");
  Serial.println(t1-t0);
  Serial.print("Chess Field  ");
  Serial.println(t2-t1);
  Serial.print("Line 240 pix.");
  Serial.println(t3-t2);
  Serial.print("60 chars 6x8 ");
  Serial.println(t4-t3);
  Serial.print("60 chars 8x8 ");
  Serial.println(t5-t4);

  setCharSize8x8;

  setXY(240+45, 120);
  lineTo(240, 120+85);
  lineTo(240-200, 120);
  lineTo(240, 120-100);
  lineTo(240, 120);
  lineTo(240+45, 120);
  
  setXY(0, 232);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  static int ledState = LOW; // blink
  static unsigned long previousMillis = 0;
  static long interval = 20;
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    interval = 20 + digitalRead(LED_BUILTIN)*980;
  }  
  if(Serial1.available()) { // обработка ввода
    unsigned char ch = Serial1.read();
#ifdef DEBUG    
    if(ch >= ' ') {
      Serial.print((char)ch);
    } else {
      Serial.print('^');
      Serial.print((char)(ch + 'A' - 1));
    }
    Serial.print(" 0x");
    Serial.println((byte)ch,HEX);
#endif    
    printChar(ch);
  }
}

screen.h

#ifndef SCREEN_H
#define SCREEN_H 

// Библиотечка использует stm32f103 для формирования полного TV-сигнала,
// позволяет выводить на экран текст и рисовать линии.
//    Возможности:
// Позволяет установить один из двух видеорежимов: 480х240 или 512х240 пикселей,
// Позволяет выводить текст фонтами размером 6х8 и 8х8 (жирный) пикселей, могут присутствовать на экране одновременно,
// Позволяет рисовать линии при помощи управляющих последовательностей,
// При выводе текста последний переносится на следующую строку по достижении правого края экрана, 
// При достижении нижнего края - происходит скроллинг текста.
//    Поддерживаются слежующие управляющие последовательности
// ]~4 - переключение в режим 480 пикселей
// ]~5 - переключение в режим 512 пикселей
// ]~6 - переключение в режим узкого шрифта 6х8
// ]~8 - переключение в режим широкого шрифта 8х8
// ]~C- очистка экрана
// ]~LXXXxYYY$ - рисование линии из текущей точки в указанную
// ]~MXXXxYYY$ - перемещение текущей позиции в указанную точку
//    где XXX и YYY - десятичная запись одно-, двух- или трехзначных координат.
//    управляющая последовательность может (но не обязана) заканчиваться символом перевода строки, который в этом случае опускается,
//    в тексте символ 0x13 обрабатывается как конец строки, другие однобайтовые управляющие символы пока не поддерживаются.
//
//     !!!  пока не предпринимались попытки борьбы с помехами на экране !!!
//     !!!  кириллица пока не поддерживается !!!

#define SCREEN_MODE_480 0
#define SCREEN_MODE_512 1

extern void (*putChar)(unsigned char); // текущая процедура вывода символа на экран
extern void scrollUp();              // пролистывание экрана на одну строку (8 пикселей) вверх и очистка последней строки
extern void chessField();            // рисует на экране "шахматное" поле с "квадратиком" 16х16 пикселей
extern void setScreenMode(int mode); // установка одного из двух режимов 480х240 или 512х240 пикселей - см. определенные выше константы
extern void setCharSize6x8();        // выбор фонта 6х8 пикселей
extern void setCharSize8x8();        // выбор "жирного" фонта 8х8 пикселей
extern void setXY(uint32_t x, uint32_t y); // устанавливает текущую позицию вывода
extern void printChar(unsigned char ch); // вывод символа в текущую позицию
extern void lineTo(uint32_t x1, uint32_t y1);  // рисует линию из текущей позиции в указанную (вместо moveTo(); следует использовать setXY(); )

#endif

screen.cpp

#define NUM_SCAN_LINES 240 // количество линий растра экрана (видимых)
#define SCR_BUFFER_WIDTH 65 // длина строки видеобуфера в байтах (64 байта = 512 пикселей макс. + 1 байт-бордюр, обеспечивающий гашение луча)

#include <Arduino.h>   // SCREEN_WIDTH*TICK_PIX + HS_SCREEN_START
#include <SPI.h>
#include "a_Small_Rus.c"
#include "font_c.c"

#define ph0 0 //LOW
#define ph1 1 //HIGH

#define pin_sync  (*((volatile unsigned long *) 0x422181A4 )) // PB9

#include "screen.h"

struct tv_screen { // HS отсчитывается от начала синхроимпульса, VS - от начала первой видимой строки экрана
  uint32_t tick_pix;         // 6 или 7 тактов (72 МГц) на пиксель, пиксельная частота =72000000/tick_pix
  uint32_t HS_period;        // 63.5-64 мс, 4572-4608 тактов, такты кратны TICK_PIX, для tick_pix=7 - HS_period=4592, для tick_pix=6 - HS_period=4608
  uint32_t HS_syn_end;       // 4.7 мкс - конец импульса синхронизации, в тактах
  uint32_t HS_screen_start;  // 11.61 мкс - начало видимой части экрана (по ГОСТ 10.5 мкс), в тактах
  uint32_t screen_width;     // 480 или 512 - ширина экрана (3072 или 3360 тактов), в пикселях
  uint32_t VS_num_lines;     // полное количество линий развертки в кадре (288-312)
  uint32_t VS_sync_start;    // первая строка импульса VS
  uint32_t VS_sync_end;      // последняя строка импульса VS
};

uint32_t VS_num_lines;   // текущее количество линий вертикальной развертки
uint32_t VS_sync_start;  // текущее начало синхроимпульса вертикальной развертки, номер линии
uint32_t VS_sync_end;    // текущий конец синхроимпульса вертикальной развертки, номер линии

//                            tick, period, hsyne, hss,  sw,  nl, vss, vse
const struct tv_screen tv60 = { 7,   4592,   340,  837, 480, 302, 253, 261};
const struct tv_screen tv64 = { 6,   4608,   397,  981, 512, 302, 253, 261};

byte buf[SCR_BUFFER_WIDTH*NUM_SCAN_LINES]; // 65*240=15600 экранный буфер (ориентировочно 0x200008B0)
uint32_t* buf_BB = (uint32_t*)(((uint32_t)buf - 0x20000000)*32 + 0x22000000); // псевдоним экр. буфера в bit banding (~0x22011600)
int lineNumber = 0; // текущий номер строки при сканировании видеопамяти
int toHS = ph1;     // ячейка для запоминания состояния HS - сразу по прерыванию заносится в порт, а потом вычисляется значение для следующей строки
uint32_t lenLine;   // длина видимой части строки в байтах, зависит только от режима: 60 при 480 пикселях и 64 - при 512
int deltaLine = 0;  // ячейка для хранения смещения строки для очередной строки растра
int Xchar, Ychar;   // пиксельные координаты верхнего левого угла текущего знакоместа (куда выводим символ)
void (*putChar)(unsigned char); // текущая процедура вывода символа на экран
int newX, newY;     // новые координаты при рисовании и перемещении
int charWidth;      // 6 или 8 - текущая ширина символа в пикселях

inline void startHS() {   // начало импульса HS, переводится из высокого состояния в низкое
  pin_sync = ph0;
}

inline void endHS() {   // конец импульса HS, остается низким во время кадровой синхронизации
  pin_sync = toHS;
  toHS = (lineNumber <= VS_sync_start) || (lineNumber >= VS_sync_end);  // импульс VS  262-275
}

inline void endScreen() {   // конец видимой строки экрана, пока ничего не делаем, но вдруг понадибится...
}

inline void startScreen() {
  if(lineNumber < NUM_SCAN_LINES) {
    DMA1_BASE->CCR3 &= 0xfffe; //|= 0;
    DMA1_BASE->CMAR3 = (uint32)&buf[deltaLine];
    DMA1_BASE->CNDTR3 = lenLine+1;
    DMA1_BASE->CCR3  = 0x3091; // 0x3093
    SPI1_BASE->CR1   = 0xC2C8;  // 0xffbf; // 0x021C;
    TIMER2_BASE->CCER = 0x0010;  // CC2 enable    
  }
  lineNumber++;
  deltaLine += SCR_BUFFER_WIDTH;
  if (lineNumber >= VS_num_lines) { // 292 288 конец кадра
    lineNumber = 0;
    deltaLine = 0;
  }
}

void inline setPixel(int x, int y) {
  buf_BB[x + y*SCR_BUFFER_WIDTH*8] = 1;
}

void inline clearPixel(int x, int y) {
  buf_BB[x + y*SCR_BUFFER_WIDTH*8] = 0;
}

void putChar6x8(unsigned char ch) {
  for(int i = 0; i < 5; i++) {
    byte b = SmallFont6[(ch - ' ')*5 + 4 + i];
    for(int j = 0; j < 8; j++) {
      buf_BB[Xchar + (Ychar+j)*SCR_BUFFER_WIDTH*8] = b & 1;
      b = b >> 1;
    }
    Xchar++;
  }
  for(int j = 0; j < 8; j++)
    clearPixel(Xchar, Ychar+j);
  Xchar++;
}

void putChar8x8(unsigned char ch) {
  for(int i = 0; i < 8; i++) {
    buf[Xchar/8 + (Ychar + i)*SCR_BUFFER_WIDTH] = font_c[(ch - ' ')*8 + i];
  }
  Xchar += 8;
}

void scrollUp() {
  memcpy(&buf[0], &buf[8*SCR_BUFFER_WIDTH], (NUM_SCAN_LINES - 8)*SCR_BUFFER_WIDTH);
  memset(&buf[(NUM_SCAN_LINES - 8)*SCR_BUFFER_WIDTH], 0, 8*SCR_BUFFER_WIDTH);
  Xchar = 0;
  Ychar = (NUM_SCAN_LINES - 8);
}

void chessField() {
  for(int i = 0; i < (SCR_BUFFER_WIDTH*NUM_SCAN_LINES); i++) {
    int L = i/SCR_BUFFER_WIDTH;
    int J = i%SCR_BUFFER_WIDTH;
    int L0 = L/16&1;
    int J0 = J/2&1;
    buf[i] = 255*((L0+J0)&1);
  }
  for(int i = 0; i < NUM_SCAN_LINES; i++) if(buf[i*SCR_BUFFER_WIDTH] == 0) buf[i*SCR_BUFFER_WIDTH] = 2; else buf[i*SCR_BUFFER_WIDTH] = 253;  // 
}

void setCharSize6x8() {
  putChar = putChar6x8;
  charWidth = 6;
}

void setCharSize8x8() {
  putChar = putChar8x8;
  charWidth = 8;
}

void setXY(uint32_t x, uint32_t y) {
  Xchar = x;
  Ychar = y;
}

void screenInit(const struct tv_screen &SCR) {
  uint32_t HS_screen_end = SCR.screen_width*SCR.tick_pix + SCR.HS_screen_start;
  lenLine = SCR.screen_width / 8;
  for(int i = 0; i < NUM_SCAN_LINES; i++) buf[i*SCR_BUFFER_WIDTH+lenLine] = 0; // заполнение последнего байта в строке нулями - гашение луча

  GPIOB_BASE->CRL = 0x33444444;     // пины PB7,PB6 - выход, остальные - вход (не исп.)
  GPIOB_BASE->CRH = 0x44444433;     // пины PB9-PB8 - выход, остальные - вход (не исп.)
  GPIOA_BASE->CRL = 0xbb4444b4;     // пины MOSI(pa7), MISO(pa6), SCK(pa5), T2C2(PA1) - выход таймера

  RCC_BASE->AHBENR |= 0x00000001; // включение тактового сигнала DMA
  RCC_BASE->APB2ENR |= (1 << 12); // включение тактового сигнала SPI
  DMA1_BASE->CPAR3 = 0x4001300C;
  DMA1_BASE->CMAR3 = (uint32)buf;
  DMA1_BASE->CNDTR3 = 13;
  SPI1_BASE->CR1   = 0xC288;  // 0xffbf; // 0x021C;
  SPI1_BASE->CR2   = 0x0082; // 0x0082
  DMA1_BASE->CCR3  = 0x3090; // 0x3092
  
    Timer4.pause(); // while we configure
    delayMicroseconds(50);

    VS_num_lines  = SCR.VS_num_lines;
    VS_sync_start = SCR.VS_sync_start;
    VS_sync_end   = SCR.VS_sync_end;
    
    Timer4.setPrescaleFactor(1);     // Full speed
    Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setChannel2Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setChannel3Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setChannel4Mode(TIMER_OUTPUTCOMPARE);
    Timer4.setOverflow(SCR.HS_period - 1); // 4591 : 63.7 ms, для делителя 7 оптимально 4592-1

    Timer4.setCompare1(1);    // 1 - точка отсчета, условный 0, - начало импульса вертикальной синхронизации
    Timer4.attachCompare1Interrupt(startHS); // начало HS
    Timer4.setCompare2(SCR.HS_syn_end);    // 339 - конец импульса синхронизации 4.7 мкс
    Timer4.attachCompare2Interrupt(endHS); // конец HS
    Timer4.setCompare3(SCR.HS_screen_start);   // 831 - начало видимой части экрана 11.53 мс
    Timer4.attachCompare3Interrupt(startScreen); // начало видимой строки
    Timer4.setCompare4(HS_screen_end);      // =4191 - примерная точка конца видимой части экрана 58.17 мкс
    Timer4.attachCompare4Interrupt(endScreen);
    
    Timer4.setCount(0);         // Ready...
    Timer4.resume();            // Go! 

    TIMER2_BASE->PSC = 0;            // prescaler 
    TIMER2_BASE->ARR = SCR.tick_pix - 1; // 6=7-1, где 7 - количество тиков (72МГц) на пиксель
    TIMER2_BASE->CCR2 = SCR.tick_pix/2 - 1; // 2
    TIMER2_BASE->CCER = 0x0010;  // CC2 enable    
  delayMicroseconds(40);
}

void setScreenMode(int mode) {
  if(mode == SCREEN_MODE_480) screenInit(tv60);
  else                        screenInit(tv64);
}

void printCoord(int x, int y) {
  Serial.print('(');
  Serial.print(x);
  Serial.print(',');
  Serial.print(y);
  Serial.println(')');
}

void lineTo(uint32_t x1, uint32_t y1) {
  int dx = (int)x1 - Xchar;
  int deltax = abs(dx);
  int dy = (int)y1 - Ychar;
  int deltay = abs(dy);
  int error = 0;
  int y = Ychar;
  if (dy > 0) dy = 1;
  if (dy < 0) dy = -1;
  int x = Xchar;
  if (dx > 0) dx = 1;
  if (dx < 0) dx = -1;
  if(deltax > deltay) {
    int deltaerr = deltay;
    for (int i = 0; i <= deltax; i++) {
      setPixel(x,y);
      error += deltaerr;
      if (2 * error >= deltax) {
        y += dy;
        error -= deltax;
      }
      x += dx;
    }
  } else {
    int deltaerr = deltax;
    for (int i = 0; i <= deltay; i++) {
      setPixel(x,y);
      error += deltaerr;
      if (2 * error >= deltay) {
        x += dx;
        error -= deltay;
      }
      y += dy;
    }
  }
  Xchar = x1;
  Ychar = y1;
}

void nextLine() { // перевод строки
  Xchar = 0;
  Ychar += 8;
  if(Ychar > (NUM_SCAN_LINES-8)) {
    scrollUp();
  }
}

void printChar(unsigned char ch) {
  static int mode = 0; // 0-обычный, 1-введен ']', 2-введено "]~", 3-линия, ввод X, 4-линии, ввод Y, 5-6-moveTo, 9-пропускаем CR
  switch (mode) {
    case 0: // режим ввода однобайтных символов
    case 9: // то же самое, но пропустить CR
      if(ch < ' ') {  // подблок однобайтных управляющих символов
        if(ch == 13) { // обработка CR
          if (mode == 0) nextLine();
        }
      } else {  // ch >= ' ' подблок обычных символов
        if(ch == 127) { // <BackSpace
          if(Xchar >= charWidth)
            Xchar -= charWidth;
          else
            Xchar = 0;
        } else { // все обычные символы (32-126), включая начало упр.посл.
          if(ch == ']') {
            mode = 1;  // начало управляющей последовательности
          } else { // все обычные символы
            if(Xchar > (lenLine*8 - charWidth)) {
              nextLine();
            }
            /*if(ch != 0)*/ putChar(ch);
          }
        }
      }
      if (mode == 9) mode = 0;
    break;
    case 1:
      if(ch == '~') mode = 2;
      else {
        putChar(']');
      mode = 0;
    }
    break;
    case 2:
      switch (ch) {
        case '4': setScreenMode(SCREEN_MODE_480);   mode = 9;
        break;
        case '5': setScreenMode(SCREEN_MODE_512);   mode = 9;
        break;
        case '6': setCharSize6x8();   mode = 9;
        break;
        case '8': setCharSize8x8();   mode = 9;
        break;
        case 'C': memset(buf, 0, SCR_BUFFER_WIDTH*NUM_SCAN_LINES);   Xchar = 0;   Ychar = 0;   mode = 9;
        break;
        case 'L': newX = 0;   mode = 3;
        break;
        case 'M': newX = 0;   mode = 5;
        break;
        default: 
          putChar(']');
          putChar('~');
          mode = 0;
      }
    break;
    case 3: // линия, ввод X
      if((ch >= '0') && (ch <= '9')) newX = newX*10 + (ch - '0');
      else
      if(ch == 'x') {
        newY = 0;
        mode = 4;
      } else mode = 0;
    break;
    case 4: // линия, ввод Y
      if((ch >= '0') && (ch <= '9')) newY = newY*10 + (ch - '0');
      else
      if(ch == '$') {
        if(newX >= lenLine*8) newX = lenLine*8-1;
        if(newY >= NUM_SCAN_LINES) newY = NUM_SCAN_LINES-1;
        lineTo(newX, newY);
        Xchar = newX;
        Ychar = newY;
        mode = 9;
      } else mode = 0;
    break;
    case 5: // перемещение, ввод X
      if((ch >= '0') && (ch <= '9')) newX = newX*10 + (ch - '0');
      else
      if(ch == 'x') {
        newY = 0;
        mode = 6;
      } else mode = 0;
    break;
    case 6: // перемещение, ввод Y
      if((ch >= '0') && (ch <= '9')) newY = newY*10 + (ch - '0');
      else
      if(ch == '$') {
        if(newX >= lenLine*8) newX = lenLine*8-1;
        if(newY >= NUM_SCAN_LINES) newY = NUM_SCAN_LINES-1;
        Xchar = newX;
        Ychar = newY;
        mode = 9;
      } else mode = 0;
    break;
  }
}

И демонстрационный код на Меге:

test.ino

HardwareSerial * S = &Serial3;

void sendSeq() {
  S->println("test");
  S->print("]~4");
  S->print("]~C]~M200x100$]~8H e l l o !]~M160x140$]~6Serial Terminal Display Test]~M140x80$]~L340x80$]~L340x160$]~L140x160$]~L140x80$");
  Serial.println("OK!");
  delay(4000);

  S->print("]~C");
  
  S->print("]~C");
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~5");
  delay(4000);
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~8");
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~4");
  delay(4000);
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~5");
  delay(4000);
  S->print("]~C");
  for(int i = 0; i < 1024; i++) {
    S->print("]~L");
    S->print(random(512));
    S->print("x");
    S->print(random(240));
    S->print("$");
  }
  S->print("]~M228x108$       ");
  S->print("]~M228x116$ E N D ");
  S->print("]~M228x124$       ");
  delay(4000);
}

void setup() {
  S->begin(9600);
  Serial.begin(115200);
  sendSeq();
}

void loop() {
}  

Выход TX3 Меги нужно подключить ко входу RX1 stm32.

Напоминаю, что:

кириллица пока не поддерживается,

борьба с помехами пока не ведется.

Управляющие коды описаны в screen.h. Можно подключить к stm32 вместо Меги ПК через USB-UART и передавать команды и строки текста прямо из монитора порта.

arduinec
Offline
Зарегистрирован: 01.09.2015

Похожий проект "VGA на Arduino Due": https://github.com/stimmer/DueVGA/

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Да, сходство есть.

Но мне хотелось:

1. Не менее 80 символов в строке (а в указанном проекте - до 53).

2. Обойтись контроллером, который почти на порядок дешевле.

При этих условиях единственный вариант - отказаться от цвета.

Но я сейчас продумываю вариант, при котором можно будет задавать цвет каждой строки целиком (из 7-255 цветов). Вот только думаю, может ли такое когда-нибудь кому-нибудь понадобиться. Тем более, что такой вариант однозначно предполагает переход с композитного сигнала на VGA.

arduinec
Offline
Зарегистрирован: 01.09.2015

arduinec пишет:

Похожий проект "VGA на Arduino Due": https://github.com/stimmer/DueVGA/

Схемы здесь (для VGA и TV): http://stimmer.github.io/DueVGA/
http://stimmer.github.io/DueVGA/breadboard.html
http://stimmer.github.io/DueVGA/tvout.html
 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Да, посмотрел дэйташит на SAM8, там, в отличие от stm32, для SPI можно использовать практически любой коэффициент деления, тогда как для stm32 - только степени двойки. Наверное поэтому для sym32 и нет аналогов. Но я обошел это при помощи SPI slave.

А так - конечно, Due выглядит более многообещающим вариантом по сравнению с stm32f103 из-за впятеро большего объема памяти, на 17% более высокой тактовой частоты и большей свободы в выборе пиксельной частоты. Но, с другой стороны - неудобный формфактор и высокая цена.

 

PS. Естественно, проект не претендует на популярность, т.к. первый вопрос - "Зачем?". Но, все-таки, хотелось бы выслушать мнения по двум вопросам:

1. Какой интерфейс предпочтительнее: композитный TV-сигнал или VGA?

2. Имеет ли какой-то смысл предусмотреть возможность выводить каждую строку растра своим цветом?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Сделал поддержку кириллицы.

Поддерживаются 4 кодировки: UTF-8, KOI8R, CP-1251 (Windows), CP-866 (Альтернативная ГОСТ). По умолчанию включена UTF-8, но управляющими последовательностями их можно переключать.

В дальнейшем планируется сделать настройки по умолчанию при помощи dip-переключателя, тобы устройство могло работать в любом из поддерживаемых режимов совместно с устройством, которое ничего не знает об управляющих последовательностях.

Но, пожалуй, данный вариант устройства приобрел более или менее законченные очертания - дальнейшая работа предполагается в расчете на VGA разъем и с ограниченной поддержкой цвета.

Поэтому публикую его полностью.

Схема:

Основной файл:

#include <SPI.h>
#include "screen.h"

void printStr(const char* ch) {
  while(*ch) { /*Serial.println((byte)(*ch));*/ printChar((unsigned char)*ch++); /*ch++;*/ }
}

void printCharSet(int x, int y, unsigned char b, const char* ch) {
  setXYchar(x,y);
  printStr(ch);
  int jj = b > 127 ? 4 : 8;
  for(int j = 0; j < jj; j++) {
    setXYchar(x,y+(j+1)*8);
    for(int i = 0; i < 16; i++) {
      if (b > 127) printChar(b);
      printChar((unsigned char)(128 + j*16 + i));
    }
  }
}

void setup() {
  Serial.begin(115200); // Ignored by Maple. But needed by boards using hardware serial via a USB to Serial adaptor ]'
  while(!Serial) ;
//Serial.println("start");
  uint32_t t0 = micros();
  scrollUp();

  uint32_t t1 = micros();
  chessField();
  
  uint32_t t2 = micros();
  setXYpix(0, 0);
  lineTo(239, 239);

  uint32_t t3 = micros();
  setXYchar(0, 2);
  setCharSize(CHAR_SIZE_6x8);
  for(int i = 0; i < 60; i++) printChar(' ' + i + 1);

  uint32_t t4 = micros();
  setXYchar(0, 12);
  setCharSize(CHAR_SIZE_8x8);
  for(int i = 0; i < 60; i++) printChar(' ' + i + 1);

  uint32_t t5 = micros();

  setXYchar(0, 22);
  for(int i = 0; i < 60; i++) printChar(' ' + i + 1);
  setXYchar(0, 30);
  for(int i = 59; i < 93; i++) printChar(' ' + i + 1);

  setCharSize(CHAR_SIZE_6x8);
  setXYchar(0, 40);
  for(int i = 0; i < 60; i++) printChar(' ' + i + 1);
  setXYchar(0, 48);
  for(int i = 59; i < 93; i++) printChar(' ' + i + 1);

  Serial1.begin(9600);
  delay(1);
  setScreenMode(SCREEN_MODE_480);
  
  const char *cyr_1 = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяЃѓЄєІіЇїЎўҐґ§°±µ0123456789";
  
  setXYchar(0, 58);
  for(int i = 0; i < 184-10; i++) { // 10 однобайтных цифр
    printChar(cyr_1[i]);
  }

  setCharSize(CHAR_SIZE_8x8);
  setXYchar(0, 76);
  for(int i = 0; i < 184-10; i++) {
    printChar(cyr_1[i]);
  }

  setXYchar(0, 94);
  printStr("Кодировка UTF-8. Российская Федерация, Україна, Рэспубліка Беларусь");

  
  printCharSet(24, 114, 0xD0, "UTF-8 D0");
  printCharSet(176, 114, 0xD1, "UTF-8 D1");
  printCharSet(328, 114, 0xC2, "UTF-8 C2");
//  printCharSet(24, 164, 0xC2, "UTF-8 C2");
  setCodePage(CP_1251);
  printCharSet(176, 164, 2, "CP-1251");
  setCodePage(CP_866);
  printCharSet(328, 164, 2, "CP-866");
  setCodePage(CP_KOI8);
  printCharSet(24, 164, 2, "CP-KOI8R");

  Serial.print("Scroll Up    ");
  Serial.println(t1-t0);
  Serial.print("Chess Field  ");
  Serial.println(t2-t1);
  Serial.print("Line 240 pix.");
  Serial.println(t3-t2);
  Serial.print("60 chars 6x8 ");
  Serial.println(t4-t3);
  Serial.print("60 chars 8x8 ");
  Serial.println(t5-t4);

  setCharSize(CHAR_SIZE_8x8);

  setXYpix(240+45, 120);
  lineTo(240, 120+85);
  lineTo(240-200, 120);
  lineTo(240, 120-100);
  lineTo(240, 120);
  lineTo(240+45, 120);
  
  setXYchar(0, 232);
  pinMode(LED_BUILTIN, OUTPUT);

     // This gets rid of the majority of the interrupt artifacts;
    // a SysTick.end() is required as well
    Serial.end();

}

// #define DEBUG
void loop() {
  static int ledState = LOW; // blink
  static unsigned long previousMillis = 0;
  static long interval = 20;
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    interval = 20 + digitalRead(LED_BUILTIN)*980;
  }  
  if(Serial1.available()) { // обработка ввода
    unsigned char ch = Serial1.read();
#ifdef DEBUG    
    if(ch >= ' ') {
      Serial.print((char)ch);
    } else {
      Serial.print('^');
      Serial.print((char)(ch + 'A' - 1));
    }
    Serial.print(" 0x");
    Serial.println((byte)ch,HEX);
#endif    
    printChar(ch);
  }
}

Заголовочный файл библиотеки screen.h:

#ifndef SCREEN_H
#define SCREEN_H 

// Библиотечка использует stm32f103 для формирования полного TV-сигнала,
// позволяет выводить на экран текст и рисовать линии.
//    Возможности:
// Позволяет установить один из двух видеорежимов: 480х240 или 512х240 пикселей,
// Позволяет выводить текст фонтами размером 6х8 и 8х8 (жирный) пикселей, могут присутствовать на экране одновременно,
// Позволяет рисовать линии при помощи управляющих последовательностей,
// Автоматический перенос текста по достижении правого края экрана
// Автоматический скроллинг по достижении нижнего края экрана
//    Поддерживаются слежующие управляющие последовательности
// ]~4 - переключение в режим 480 пикселей
// ]~5 - переключение в режим 512 пикселей
// ]~6 - переключение в режим узкого шрифта 6х8
// ]~8 - переключение в режим широкого шрифта 8х8
// ]~C- очистка экрана
// ]~LXXXxYYY$ - рисование линии из текущей точки в указанную
// ]~MXXXxYYY$ - перемещение текущей позиции рисования линии в указанную точку
// ]~PXXXxYYY$ - перемещение текущей позиции печати текста в указанную точку
//    где XXX и YYY - десятичная запись одно-, двух- или трехзначных координат.
// ]~U - переключение в кодировку UTF-8
// ]~1 - переключение в кодировку 1251
// ]~A - переключение в кодировку 866 (альтернативная ГОСТ)
// ]~K - переключение в кодировку KOI-8
//    управляющая последовательность может (но не обязана) заканчиваться символом перевода строки, который в этом случае опускается,
//    в тексте символ 0x13 обрабатывается как конец строки, другие однобайтовые управляющие символы пока не поддерживаются.
//
//     !!!  пока не предпринимались попытки борьбы с помехами на экране !!!

#define SCREEN_MODE_480 0  // режим дисплея разрешением 480х240
#define SCREEN_MODE_512 1  // режим дисплея разрешением 512х240
#define CHAR_SIZE_6x8 0  // режим узких символов 6х8
#define CHAR_SIZE_8x8 1  // режим широких символов 8х8
#define CP_UTF8 0  // режим кодировки UTF-8
#define CP_1251 1  // режим кодировки CP-1251
#define CP_866 2   // режим кодировки CP-866 (Альтернативная ГОСТ)
#define CP_KOI8 3  // режим кодировки KOI8R

extern void scrollUp();              // пролистывание экрана на одну строку (8 пикселей) вверх и очистка последней строки
extern void chessField();            // рисует на экране "шахматное" поле с "квадратиком" 16х16 пикселей
extern void setScreenMode(int mode); // установка одного из двух режимов 480х240 или 512х240 пикселей
extern void setCharSize(int size);        // выбор фонта 6х8 или 8х8 пикселей
extern void setCodePage(int page);        // выбор кодовой страницы
extern void setXYchar(uint32_t x, uint32_t y); // устанавливает текущую позицию вывода символа
extern void setXYpix(uint32_t x, uint32_t y); // устанавливает текущую позицию вывода линии
extern void printChar(unsigned char ch); // вывод символа в текущую позицию
extern void lineTo(uint32_t x1, uint32_t y1);  // рисует линию из текущей позиции в указанную (вместо moveTo(); следует использовать setXY(); )

#endif

файл библиотеки screen.cpp:

#define NUM_SCAN_LINES 240 // количество линий растра экрана (видимых)
#define SCR_BUFFER_WIDTH 65 // длина строки видеобуфера в байтах (64 байта = 512 пикселей макс. + 1 байт-бордюр, обеспечивающий гашение луча)

#include <Arduino.h>   // SCREEN_WIDTH*TICK_PIX + HS_SCREEN_START
#include <SPI.h>
#include "a_Small_Rus.c"
#include "font_c.c"

#define ph0 0 //LOW
#define ph1 1 //HIGH

#define pin_sync  (*((volatile unsigned long *) 0x422181A4 )) // PB9

#include "screen.h"

struct tv_screen { // HS отсчитывается от начала синхроимпульса, VS - от начала первой видимой строки экрана
  uint32_t tick_pix;         // 6 или 7 тактов (72 МГц) на пиксель, пиксельная частота =72000000/tick_pix
  uint32_t HS_period;        // 63.5-64 мс, 4572-4608 тактов, такты кратны TICK_PIX, для tick_pix=7 - HS_period=4592, для tick_pix=6 - HS_period=4608
  uint32_t HS_syn_end;       // 4.7 мкс - конец импульса синхронизации, в тактах
  uint32_t HS_screen_start;  // 11.61 мкс - начало видимой части экрана (по ГОСТ 10.5 мкс), в тактах
  uint32_t screen_width;     // 480 или 512 - ширина экрана (3072 или 3360 тактов), в пикселях
  uint32_t VS_num_lines;     // полное количество линий развертки в кадре (288-312)
  uint32_t VS_sync_start;    // первая строка импульса VS
  uint32_t VS_sync_end;      // последняя строка импульса VS
};

uint32_t VS_num_lines;   // текущее количество линий вертикальной развертки
uint32_t VS_sync_start;  // текущее начало синхроимпульса вертикальной развертки, номер линии
uint32_t VS_sync_end;    // текущий конец синхроимпульса вертикальной развертки, номер линии

//                            tick, period, hsyne, hss,  sw,  nl, vss, vse
const struct tv_screen tv480 = { 7,   4592,   340,  837, 480, 302, 253, 261};
const struct tv_screen tv512 = { 6,   4608,   397,  981, 512, 302, 253, 261};

unsigned char recodeCharUTF(unsigned char ch); // !!! предварительное объявление - потом убрать !!!

byte buf[SCR_BUFFER_WIDTH*NUM_SCAN_LINES]; // 65*240=15600 экранный буфер (ориентировочно 0x200008B0)
uint32_t* buf_BB = (uint32_t*)(((uint32_t)buf - 0x20000000)*32 + 0x22000000); // псевдоним экр. буфера в bit banding (~0x22011600)
int lineNumber = 0; // текущий номер строки при сканировании видеопамяти
int toHS = ph1;     // ячейка для запоминания состояния HS - сразу по прерыванию заносится в порт, а потом вычисляется значение для следующей строки
uint32_t lenLine;   // длина видимой части строки в байтах, зависит только от режима: 60 при 480 пикселях и 64 - при 512
int Xchar, Ychar;   // пиксельные координаты верхнего левого угла текущего знакоместа (куда выводим символ)
int Xpix, Ypix; // координаты для рисования линии
void (*drawChar)(unsigned char); // текущая процедура вывода символа на экран
unsigned char (*recodeChar)(unsigned char) = recodeCharUTF; // текущая процедура перекодировки символа
int charWidth;      // 6 или 8 - текущая ширина символа в пикселях

inline void startHS() {   // начало импульса HS, переводится из высокого состояния в низкое
  pin_sync = ph0;
}

inline void endHS() {   // конец импульса HS, остается низким во время кадровой синхронизации
  pin_sync = toHS;
  toHS = (lineNumber <= VS_sync_start) || (lineNumber >= VS_sync_end);  // импульс VS  262-275
}

inline void endScreen() {   // конец видимой строки экрана, пока ничего не делаем, но вдруг понадибится...
}

inline void startScreen() {
static int deltaLine = 0;  // ячейка для хранения смещения строки для очередной строки растра
  if(lineNumber < NUM_SCAN_LINES) {
    DMA1_BASE->CCR3 &= 0xfffe; //|= 0;
    DMA1_BASE->CMAR3 = (uint32)&buf[deltaLine];
    DMA1_BASE->CNDTR3 = lenLine+1;
    DMA1_BASE->CCR3  = 0x3091; // 0x3093
    SPI1_BASE->CR1   = 0xC2C8;  // 0xffbf; // 0x021C;
  //  TIMER2_BASE->CCER = 0x0010;  // CC2 enable    
  }
  lineNumber++;
  deltaLine += SCR_BUFFER_WIDTH;
  if (lineNumber >= VS_num_lines) { // 292 288 конец кадра
    lineNumber = 0;
    deltaLine = 0;
  }
}

void setXYchar(uint32_t x, uint32_t y) {
  Xchar = x;
  Ychar = y;
}

void setXYpix(uint32_t x, uint32_t y) {
  Xpix = x;
  Ypix = y;
}

void inline setPixel(int x, int y) {
  buf_BB[x + y*SCR_BUFFER_WIDTH*8] = 1;
}

void inline clearPixel(int x, int y) {
  buf_BB[x + y*SCR_BUFFER_WIDTH*8] = 0;
}

void scrollUp() {
  memcpy(&buf[0], &buf[8*SCR_BUFFER_WIDTH], (NUM_SCAN_LINES - 8)*SCR_BUFFER_WIDTH);
  memset(&buf[(NUM_SCAN_LINES - 8)*SCR_BUFFER_WIDTH], 0, 8*SCR_BUFFER_WIDTH);
  setXYchar(0, NUM_SCAN_LINES - 8);
}

void nextLine() { // перевод строки
  setXYchar(0, Ychar + 8);
  if(Ychar > (NUM_SCAN_LINES-8)) {
    scrollUp();
  }
}

void drawChar6x8(unsigned char ch) {
  if(ch == 255) return; // ничего не печатать (вероятно, 1-й байт многобайтового символа)
//  Serial.print("ch: ");
//  Serial.print(ch);
//  Serial.print(", Xchar: ");
//  Serial.print(Xchar);
//  Serial.print(", Ychar: ");
//  Serial.println(Ychar);
//  Serial.print("ch:");
//  Serial.print("ch:");
  if(Xchar > (lenLine*8 - charWidth)) {
    nextLine();
  }
  for(int i = 0; i < 5; i++) {
    byte b = SmallFont6[(ch /*- ' '*/)*5 + 4 + i];
    for(int j = 0; j < 8; j++) {
      buf_BB[Xchar + (Ychar+j)*SCR_BUFFER_WIDTH*8] = b & 1;
      b = b >> 1;
    }
    Xchar++;
  }
  for(int j = 0; j < 8; j++)
//    clearPixel(Xchar, Ychar+j);
    buf_BB[Xchar + (Ychar+j)*SCR_BUFFER_WIDTH*8] = 0;
  Xchar++;
}

void drawChar8x8(unsigned char ch) {
  if(ch == 255) return; // ничего не печатать (вероятно, 1-й байт многобайтового символа)
  if(Xchar > (lenLine*8 - charWidth)) {
    nextLine();
  }
  for(int i = 0; i < 8; i++) {
    buf[Xchar/8 + (Ychar + i)*SCR_BUFFER_WIDTH] = font_c[(ch /*- ' '*/)*8 + i];
  }
  Xchar += 8;
}

const unsigned char utf_D0[] = {
 37, 141, 14, 143, 145, 51, 41, 147, 42, 14, 14, 72, 43, 101, 149, 14,        // .Ё.ЃЄSIЇJ.....Ў. 
 33, 95, 34, 96, 97, 37, 98, 99, 100, 101, 43, 102, 45, 40, 47, 103,          // АБВГДЕЖЗИЙКЛМНОП
 48, 35, 52, 104, 105, 56, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,  // РСТУФХЦЧШЩЪЫЬЭЮЯ
 65, 116, 117, 118, 119, 69, 120, 121, 122, 123, 124, 125, 126, 127, 79, 128  // абвгдежзийклмноп
};

const unsigned char utf_D1[] = {
 80, 67, 129, 89, 130, 88, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,  // рстуфхцчшщъыьэюя
 69, 142, 14, 144, 146, 83, 73, 148, 74, 14, 14, 72, 124, 123, 150, 14        // .ё.ѓєsiїj.....ў.
};

const unsigned char ansi1251[] = {
 14, 143, 12, 144, 2, 14, 14, 14, 146, 14, 14, 28, 14, 43, 72, 14,            // .Ѓ,ѓ"...є..<.Кh.
 14, 64, 7, 2, 2, 14, 13, 13, 0, 14, 14, 30, 14, 124, 72, 14,                 // .`'...--...>.кh.
 0, 149, 150, 42, 14, 151, 92, 153, 141, 14, 145, 14, 14, 0, 14, 147,         //  ЎўJ.Ґ|§Ё.Є.. .Ї
 154, 155, 41, 73, 152, 156, 14, 14, 142, 46, 146, 14, 74, 51, 83, 148,       // °±Iiґµ..ё№є.jSsї
 33, 95, 34, 96, 97, 37, 98, 99, 100, 101, 43, 102, 45, 40, 47, 103,          // АБВГДЕЖЗИЙКЛМНОП
 48, 35, 52, 104, 105, 56, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,  // РСТУФХЦЧШЩЪЫЬЭЮЯ
 65, 116, 117, 118, 119, 69, 120, 121, 122, 123, 124, 125, 126, 127, 79, 128, // абвгдежзийклмноп
 80, 67, 129, 89, 130, 88, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140   // рстуфхцчшщъыьэюя
};

const unsigned char oem866[] = {
 33, 95, 34, 96, 97, 37, 98, 99, 100, 101, 43, 102, 45, 40, 47, 103,          // АБВГДЕЖЗИЙКЛМНОП
 48, 35, 52, 104, 105, 56, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,  // РСТУФХЦЧШЩЪЫЬЭЮЯ
 65, 116, 117, 118, 119, 69, 120, 121, 122, 123, 124, 125, 126, 127, 79, 128, // абвгдежзийклмноп
 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,              //   псевдографика
 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,              //   псевдографика
 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,              //   псевдографика
 80, 67, 129, 89, 130, 88, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,  // рстуфхцчшщъыьэюя
 141, 142, 145, 145, 147, 148, 149, 150, 154, 14, 14, 14, 46, 14, 14, 14      // ЁёЄєЇїЎў°...№...
};

const unsigned char koi8r[] = {
 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,              // ................
 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 154, 14, 14, 14,              // .......... .°...
 14, 14, 14, 142, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,             // ...ё............
 14, 14, 14, 141, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,             // ...Ё............
 139, 65, 116, 131, 119, 69, 130, 118, 88, 122, 123, 124, 125, 126, 127, 79,  // юабцдефгхийклмно
 128, 140, 80, 67, 129, 89, 120, 117, 137, 136, 121, 133, 138, 134, 132, 135, // пярстужвьызшэщчъ
 114, 33, 95, 106, 97, 37, 105, 96, 56, 100, 101, 43, 102, 45, 40, 47,        // ЮАБЦДЕФГХИЙКЛМНО
 103, 115, 48, 35, 52, 104, 98, 34, 112, 111, 99, 108, 113, 109, 107, 110     // ПЯРСТУЖВЬЫЗШЭЩЧЪ
};

unsigned char recodeChar1251(unsigned char ch) {
  return ansi1251[ch-128];
}

unsigned char recodeChar866(unsigned char ch) {
  return oem866[ch-128];
}

unsigned char recodeCharKOI8(unsigned char ch) {
  return koi8r[ch - 128];
}

unsigned char recodeCharUTF(unsigned char ch) {
  static byte firstByte = 0;
  if(firstByte == 0) {
    firstByte = ch;
    return 255;
  } else {
    switch (firstByte) {
      case (unsigned char)0xD0: firstByte = 0;   
        if(ch < 0xC0)         {   return utf_D0[ch - 128];
        } else                {   return 14;  }
      case (unsigned char)0xD1: firstByte = 0;   
        if(ch < 0xA0)         {   return utf_D1[ch - 128];
        } else                {   return 14;  }
      case (unsigned char)0xD2: firstByte = 0;         
        if        (ch == 144) {   return 151;  // Ґ
        } else if (ch == 145) {   return 152;  // ґ
        } else                {   return 14;  }
      break;
      case (unsigned char)0xC2:  firstByte = 0;         
        if        (ch == 167) {   return 153;  // §
        } else if (ch == 176) {   return 154;  // °
        } else if (ch == 177) {   return 155;  // ±
        } else if (ch == 181) {   return 156;  // µ
        } else                {   return 14;  }      
      break;
      default:  firstByte = 0;    return 14;   break;
    }
  }
}

void setCodePage(int page) {        // выбор кодовой страницы
  switch (page) {
    case (CP_UTF8): recodeChar = recodeCharUTF;    break;
    case (CP_1251): recodeChar = recodeChar1251;   break;
    case (CP_866):  recodeChar = recodeChar866;    break;
    case (CP_KOI8): recodeChar = recodeCharKOI8;   break;
  }
}

void putChar(unsigned char ch) {
  if(ch <= 126) drawChar(ch - 32);
  else          drawChar(recodeChar(ch));
}

void chessField() {
  for(int i = 0; i < (SCR_BUFFER_WIDTH*NUM_SCAN_LINES); i++) {
    int L = i/SCR_BUFFER_WIDTH;
    int J = i%SCR_BUFFER_WIDTH;
    int L0 = L/16&1;
    int J0 = J/2&1;
    buf[i] = 255*((L0+J0)&1);
  }
  for(int i = 0; i < NUM_SCAN_LINES; i++) if(buf[i*SCR_BUFFER_WIDTH] == 0) buf[i*SCR_BUFFER_WIDTH] = 2; else buf[i*SCR_BUFFER_WIDTH] = 253;  // 
}

void setCharSize(int size) {
  switch (size) {
    case (CHAR_SIZE_6x8): {
      drawChar = drawChar6x8;
      charWidth = 6;
      break;
    }
    case (CHAR_SIZE_8x8): {
      drawChar = drawChar8x8;
      charWidth = 8;
      break;
    }
  }
}

void screenInit(const struct tv_screen &SCR) {
//  Serial.println("scrInit");
  static bool first = true;
  noInterrupts();
//  while(!(SPI1_BASE->SR) | 1);
 // while((!(SPI1_BASE->SR & 2)) && !first); // TXE (Transmit buffer Empty)
 // while(SPI1_BASE->SR & 0x0080); // BSY
//  int rg = SPI1_BASE->SR;
  uint32_t HS_screen_end = SCR.screen_width*SCR.tick_pix + SCR.HS_screen_start;
  lenLine = SCR.screen_width / 8;
  for(int i = 0; i < NUM_SCAN_LINES; i++) buf[i*SCR_BUFFER_WIDTH+lenLine] = 0; // заполнение последнего байта в строке нулями - гашение луча

  if(first) {
    recodeChar = recodeCharUTF;
    GPIOB_BASE->CRL = 0x33444444;     // пины PB7,PB6 - выход, остальные - вход (не исп.)
    GPIOB_BASE->CRH = 0x44444433;     // пины PB9-PB8 - выход, остальные - вход (не исп.)
    GPIOA_BASE->CRL = 0xbb4444b4;     // пины MOSI(pa7), MISO(pa6), SCK(pa5), T2C2(PA1) - выход таймера

    RCC_BASE->AHBENR |= 0x00000001; // включение тактового сигнала DMA
    RCC_BASE->APB2ENR |= (1 << 12); // включение тактового сигнала SPI
  
//  SPI1_BASE->CR1  &= ~0x0040; // выключаем SPI
//  DMA1_BASE->CCR3  &= ~0x0001; // выключаем DMA
  
    DMA1_BASE->CPAR3 = 0x4001300C;
    DMA1_BASE->CMAR3 = (uint32)buf;
    DMA1_BASE->CNDTR3 = 13;
    SPI1_BASE->CR1   = 0xC288;  // 0xffbf; // 0x021C;
    SPI1_BASE->CR2   = 0x0082; // 0x0082
    DMA1_BASE->CCR3  = 0x3090; // 0x3092
  }
    Timer4.pause(); // while we configure
    delayMicroseconds(50);

    VS_num_lines  = SCR.VS_num_lines;
    VS_sync_start = SCR.VS_sync_start;
    VS_sync_end   = SCR.VS_sync_end;

    if(first){
      Timer4.setPrescaleFactor(1);     // Full speed
      Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE);
      Timer4.setChannel2Mode(TIMER_OUTPUTCOMPARE);
      Timer4.setChannel3Mode(TIMER_OUTPUTCOMPARE);
      Timer4.setChannel4Mode(TIMER_OUTPUTCOMPARE);
      Timer4.setCompare1(1);    // 1 - точка отсчета, условный 0, - начало импульса вертикальной синхронизации
      Timer4.attachCompare1Interrupt(startHS); // начало HS
      Timer4.attachCompare2Interrupt(endHS); // конец HS
      Timer4.attachCompare3Interrupt(startScreen); // начало видимой строки
      Timer4.attachCompare4Interrupt(endScreen);
    }
    Timer4.setOverflow(SCR.HS_period - 1); // 4591 : 63.7 ms, для делителя 7 оптимально 4592-1
    Timer4.setCompare2(SCR.HS_syn_end);    // 339 - конец импульса синхронизации 4.7 мкс
    Timer4.setCompare3(SCR.HS_screen_start);   // 831 - начало видимой части экрана 11.53 мс
    Timer4.setCompare4(HS_screen_end);      // =4191 - примерная точка конца видимой части экрана 58.17 мкс
    
    Timer4.setCount(0);         // Ready...
    Timer4.resume();            // Go! 

    TIMER2_BASE->CR1 &= ~1;        // выключаем
    if(first) {
      TIMER2_BASE->PSC = 0;            // prescaler 
      TIMER2_BASE->CCER = 0x0010;  // CC2 enable    
      first = false;
    }
    TIMER2_BASE->ARR = SCR.tick_pix - 1; // 6=7-1, где 7 - количество тиков (72МГц) на пиксель
    TIMER2_BASE->CCR2 = SCR.tick_pix/2 - 1; // 2
    TIMER2_BASE->CR1 |= 1;        // выключаем
   interrupts();
  delayMicroseconds(40);
//  Serial.println(rg, HEX);
}

void setScreenMode(int mode) {
  if(mode == SCREEN_MODE_480) screenInit(tv480);
  else                        screenInit(tv512);
}

void printCoord(int x, int y) {
  Serial.print('(');
  Serial.print(x);
  Serial.print(',');
  Serial.print(y);
  Serial.println(')');
}

void lineTo(uint32_t x1, uint32_t y1) {
  int dx = (int)x1 - Xpix;
  int deltax = abs(dx);
  int dy = (int)y1 - Ypix;
  int deltay = abs(dy);
  int error = 0;
  int y = Ypix;
  if (dy > 0) dy = 1;
  if (dy < 0) dy = -1;
  int x = Xpix;
  if (dx > 0) dx = 1;
  if (dx < 0) dx = -1;
  if(deltax > deltay) {
    int deltaerr = deltay;
    for (int i = 0; i <= deltax; i++) {
      setPixel(x,y);
      error += deltaerr;
      if (2 * error >= deltax) {
        y += dy;
        error -= deltax;
      }
      x += dx;
    }
  } else {
    int deltaerr = deltax;
    for (int i = 0; i <= deltay; i++) {
      setPixel(x,y);
      error += deltaerr;
      if (2 * error >= deltay) {
        x += dx;
        error -= deltay;
      }
      y += dy;
    }
  }
  setXYpix(x1, y1);
}

void printChar(unsigned char ch) {
/*    Serial.print("print ");
    if(ch >= ' ') {
      Serial.print((char)ch);
    } else {
      Serial.print('^');
      Serial.print((char)(ch + 'A' - 1));
    }
    Serial.print(" 0x");
    Serial.println((byte)ch,HEX);
*/
  static int mode = 0; // 0-обычный, 1-введен ']', 2-введено "]~", 3-линия, ввод X, 4-линии, ввод Y, 5-6-moveTo, 7-8-textTo, 9-пропускаем CR
  static int newX, newY;     // новые координаты при рисовании и перемещении
  switch (mode) {
    case 0: // режим ввода символов
    case 9: // то же самое, но пропустить CR (после ввода управляющих символов)
      if(ch < ' ') {  // подблок однобайтных управляющих символов
        if(ch == 13) { // обработка CR
          if (mode == 0) nextLine();
        }
      } else {  // ch >= ' ' подблок обычных символов
        if(ch == 127) { // <BackSpace
          if(Xchar >= charWidth)
            Xchar -= charWidth;
          else
            Xchar = 0;
        } else { // все обычные символы (32-126, 128-255), включая начало упр.посл.
          if(ch == ']') {
            mode = 1;  // начало управляющей последовательности
          } else { // все обычные символы
            putChar(ch);
          }
        }
      }
      if (mode == 9) mode = 0;
    break;
    case 1:       // перед этим был введен ]
      if(ch == '~') mode = 2;
      else {
        putChar(']');
        mode = 0;
        printChar(ch);
      }
    break;
    case 2:        // перед этим было введено ]~
      switch (ch) {
        case '4': setScreenMode(SCREEN_MODE_480);   mode = 9;
        break;
        case '5': setScreenMode(SCREEN_MODE_512);   mode = 9;
        break;
        case '6': setCharSize(CHAR_SIZE_6x8);   mode = 9;
        break;
        case '8': setCharSize(CHAR_SIZE_8x8);   mode = 9;
        break;
        case 'C': memset(buf, 0, SCR_BUFFER_WIDTH*NUM_SCAN_LINES);   setXYchar(0, 0);   mode = 9;
        break;
        case 'L': newX = 0;   mode = 3;
        break;
        case 'M': newX = 0;   mode = 5;
        break;
        case 'P': newX = 0;   mode = 7;
        break;

        case 'U': setCodePage(CP_UTF8);   mode = 9;
        break;
        case '1': setCodePage(CP_1251);   mode = 9;
        break;
        case 'A': setCodePage(CP_866);    mode = 9;
        break;
        case 'K': setCodePage(CP_KOI8);   mode = 9;
        break;

        default: 
          putChar(']');
          putChar('~');
          mode = 0;
      }
    break;
    case 3: // линия, ввод X (введено ]~L)
      if((ch >= '0') && (ch <= '9')) newX = newX*10 + (ch - '0');
      else
      if(ch == 'x') {
        newY = 0;
        mode = 4;
      } else mode = 0;
    break;
    case 4: // линия, ввод Y (введено ]~LXXXx)
      if((ch >= '0') && (ch <= '9')) newY = newY*10 + (ch - '0');
      else
      if(ch == '$') {
        if(newX >= lenLine*8) newX = lenLine*8-1;
        if(newY >= NUM_SCAN_LINES) newY = NUM_SCAN_LINES-1;
        lineTo(newX, newY);
        mode = 9;
      } else mode = 0;
    break;
    case 5: // перемещение, ввод X (введено ]~M)
      if((ch >= '0') && (ch <= '9')) newX = newX*10 + (ch - '0');
      else
      if(ch == 'x') {
        newY = 0;
        mode = 6;
      } else mode = 0;
    break;
    case 6: // перемещение позиции рисования, ввод Y (введено ]~MXXXx)
      if((ch >= '0') && (ch <= '9')) newY = newY*10 + (ch - '0');
      else
      if(ch == '$') {
        if(newX >= lenLine*8) newX = lenLine*8-1;
        if(newY >= NUM_SCAN_LINES) newY = NUM_SCAN_LINES-1;
        setXYpix(newX, newY);
        mode = 9;
      } else mode = 0;
    break;
    case 7: // перемещение позиции вывода текста, ввод X (введено ]~P)
      if((ch >= '0') && (ch <= '9')) newX = newX*10 + (ch - '0');
      else
      if(ch == 'x') {
        newY = 0;
        mode = 8;
      } else mode = 0;
    break;
    case 8: // перемещение, ввод Y (введено ]~PXXXx)
      if((ch >= '0') && (ch <= '9')) newY = newY*10 + (ch - '0');
      else
      if(ch == '$') {
        if(newX >= (lenLine*8 - charWidth)) newX = lenLine*8 - charWidth - 1;
        if(newY >= (NUM_SCAN_LINES-8)) newY = NUM_SCAN_LINES-9;
        setXYchar(newX, newY);
        mode = 9;
      } else mode = 0;
    break;
  }
}

файлы фонтов:

a_Small_Rus.c

#if defined(__AVR__)
  #include <avr/pgmspace.h>
  #define fontdatatype const uint8_t
#elif defined(__PIC32MX__)
  #define PROGMEM
  #define fontdatatype const unsigned char
#elif defined(__arm__)
  #define PROGMEM
  #define fontdatatype const unsigned char
#endif

// New font 177 symbols 5x8 = 885+4 bytes

fontdatatype SmallFont6[] PROGMEM =
{
0x05, 0x08, 0x20, 0xb1,
0x00, 0x00, 0x00, 0x00, 0x00,   //  32 sp
0x00, 0x00, 0x2f, 0x00, 0x00,   //  33 !
0x00, 0x07, 0x00, 0x07, 0x00,   //  34 "
0x14, 0x7f, 0x14, 0x7f, 0x14,   //  35 #
0x24, 0x2a, 0x7f, 0x2a, 0x12,   //  36 $
0x23, 0x13, 0x08, 0x64, 0x62,   //  37 %
0x36, 0x49, 0x55, 0x22, 0x50,   //  38 &
0x00, 0x05, 0x03, 0x00, 0x00,   //  39 '
0x00, 0x1c, 0x22, 0x41, 0x00,   //  40 (
0x00, 0x41, 0x22, 0x1c, 0x00,   //  41 )
0x14, 0x08, 0x3E, 0x08, 0x14,   //  42 *
0x08, 0x08, 0x3E, 0x08, 0x08,   //  43 +
0x00, 0x00, 0xA0, 0x60, 0x00,   //  44 ,
0x08, 0x08, 0x08, 0x08, 0x08,   //  45 -
0x00, 0x60, 0x60, 0x00, 0x00,   //  46 .
0x20, 0x10, 0x08, 0x04, 0x02,   //  47 /

0x3E, 0x51, 0x49, 0x45, 0x3E,   //  48 0
0x00, 0x42, 0x7F, 0x40, 0x00,   //  49 1
0x42, 0x61, 0x51, 0x49, 0x46,   //  50 2
0x21, 0x41, 0x45, 0x4B, 0x31,   //  51 3
0x18, 0x14, 0x12, 0x7F, 0x10,   //  52 4
0x27, 0x45, 0x45, 0x45, 0x39,   //  53 5
0x3C, 0x4A, 0x49, 0x49, 0x30,   //  54 6
0x01, 0x71, 0x09, 0x05, 0x03,   //  55 7
0x36, 0x49, 0x49, 0x49, 0x36,   //  56 8
0x06, 0x49, 0x49, 0x29, 0x1E,   //  57 9
0x00, 0x36, 0x36, 0x00, 0x00,   //  58 :
0x00, 0x56, 0x36, 0x00, 0x00,   //  59 ;
0x08, 0x14, 0x22, 0x41, 0x00,   //  60 <
0x14, 0x14, 0x14, 0x14, 0x14,   //  61 =
0x00, 0x41, 0x22, 0x14, 0x08,   //  62 >
0x02, 0x01, 0x51, 0x09, 0x06,   //  63 ?

0x32, 0x49, 0x59, 0x51, 0x3E,   //  64 @
0x7C, 0x12, 0x11, 0x12, 0x7C,   //  65 A
0x7F, 0x49, 0x49, 0x49, 0x36,   //  66 B
0x3E, 0x41, 0x41, 0x41, 0x22,   //  67 C
0x7F, 0x41, 0x41, 0x22, 0x1C,   //  68 D
0x7F, 0x49, 0x49, 0x49, 0x41,   //  69 E
0x7F, 0x09, 0x09, 0x09, 0x01,   //  70 F
0x3E, 0x41, 0x49, 0x49, 0x7A,   //  71 G
0x7F, 0x08, 0x08, 0x08, 0x7F,   //  72 H
0x00, 0x41, 0x7F, 0x41, 0x00,   //  73 I
0x20, 0x40, 0x41, 0x3F, 0x01,   //  74 J
0x7F, 0x08, 0x14, 0x22, 0x41,   //  75 K
0x7F, 0x40, 0x40, 0x40, 0x40,   //  76 L
0x7F, 0x02, 0x0C, 0x02, 0x7F,   //  77 M
0x7F, 0x04, 0x08, 0x10, 0x7F,   //  78 N
0x3E, 0x41, 0x41, 0x41, 0x3E,   //  79 O

0x7F, 0x09, 0x09, 0x09, 0x06,   //  80 P
0x3E, 0x41, 0x51, 0x21, 0x5E,   //  81 Q
0x7F, 0x09, 0x19, 0x29, 0x46,   //  82 R
0x46, 0x49, 0x49, 0x49, 0x31,   //  83 S
0x01, 0x01, 0x7F, 0x01, 0x01,   //  84 T
0x3F, 0x40, 0x40, 0x40, 0x3F,   //  85 U
0x1F, 0x20, 0x40, 0x20, 0x1F,   //  86 V
0x3F, 0x40, 0x38, 0x40, 0x3F,   //  87 W
0x63, 0x14, 0x08, 0x14, 0x63,   //  88 X
0x07, 0x08, 0x70, 0x08, 0x07,   //  89 Y
0x61, 0x51, 0x49, 0x45, 0x43,   //  90 Z
0x00, 0x7F, 0x41, 0x41, 0x00,   //  91 [
0x01, 0x06, 0x08, 0x30, 0x40,   //  92 Backslash
0x00, 0x41, 0x41, 0x7F, 0x00,   //  93 ]
0x04, 0x02, 0x01, 0x02, 0x04,   //  94 ^
0x40, 0x40, 0x40, 0x40, 0x40,   //  95 _

0x00, 0x03, 0x05, 0x00, 0x00,   //  96 `
0x20, 0x54, 0x54, 0x54, 0x78,   //  97 a
0x7F, 0x48, 0x44, 0x44, 0x38,   //  98 b
0x38, 0x44, 0x44, 0x44, 0x20,   //  99 c
0x38, 0x44, 0x44, 0x48, 0x7F,   // 100 d
0x38, 0x54, 0x54, 0x54, 0x18,   // 101 e
0x08, 0x7E, 0x09, 0x01, 0x02,   // 102 f
0x18, 0xA4, 0xA4, 0xA4, 0x7C,   // 103 g
0x7F, 0x08, 0x04, 0x04, 0x78,   // 104 h
0x00, 0x44, 0x7D, 0x40, 0x00,   // 105 i
0x40, 0x80, 0x84, 0x7D, 0x00,   // 106 j
0x7F, 0x10, 0x28, 0x44, 0x00,   // 107 k
0x00, 0x41, 0x7F, 0x40, 0x00,   // 108 l
0x7C, 0x04, 0x18, 0x04, 0x78,   // 109 m
0x7C, 0x08, 0x04, 0x04, 0x78,   // 110 n
0x38, 0x44, 0x44, 0x44, 0x38,   // 111 o

0xFC, 0x24, 0x24, 0x24, 0x18,   // 112 p
0x18, 0x24, 0x24, 0x18, 0xFC,   // 113 q
0x7C, 0x08, 0x04, 0x04, 0x08,   // 114 r
0x48, 0x54, 0x54, 0x54, 0x20,   // 115 s
0x04, 0x3F, 0x44, 0x40, 0x20,   // 116 t
0x3C, 0x40, 0x40, 0x20, 0x7C,   // 117 u
0x1C, 0x20, 0x40, 0x20, 0x1C,   // 118 v
0x3C, 0x40, 0x30, 0x40, 0x3C,   // 119 w
0x44, 0x28, 0x10, 0x28, 0x44,   // 120 x
0x1C, 0xA0, 0xA0, 0xA0, 0x7C,   // 121 y
0x44, 0x64, 0x54, 0x4C, 0x44,   // 122 z
0x00, 0x10, 0x7C, 0x82, 0x00,   // 123 {
0x00, 0x00, 0xFF, 0x00, 0x00,   // 124 |
0x00, 0x82, 0x7C, 0x10, 0x00,   // 125 }
0x08, 0x04, 0x08, 0x10, 0x08,   // 126 ~
//0x7C, 0x12, 0x11, 0x12, 0x7C,   // 127 А ->33

0x7F, 0x49, 0x49, 0x49, 0x31,   // 128 Б  95
//0x7F, 0x45, 0x45, 0x45, 0x3A,   // 129 В ->34
0x7F, 0x01, 0x01, 0x01, 0x03,   // 130 Г  96
0x60, 0x3F, 0x21, 0x3F, 0x60,   // 131 Д  97
//0x7F, 0x49, 0x49, 0x49, 0x41,   // 132 Е ->37
0x73, 0x0C, 0x7F, 0x0C, 0x73,   // 133 Ж  98
0x21, 0x41, 0x49, 0x4D, 0x33,   // 134 З  99
0x7F, 0x10, 0x08, 0x04, 0x7F,   // 135 И 100
0x7E, 0x20, 0x11, 0x08, 0x7E,   // 136 Й 101
//0x7F, 0x08, 0x14, 0x22, 0x41,   // 137 К ->43
0x40, 0x3F, 0x01, 0x01, 0x7F,   // 138 Л 102
//0x7F, 0x06, 0x08, 0x06, 0x7F,   // 139 М ->45
//0x7F, 0x08, 0x08, 0x08, 0x7F,   // 140 Н ->40
//0x3E, 0x41, 0x41, 0x41, 0x3E,   // 141 О ->47
0x7F, 0x01, 0x01, 0x01, 0x7F,   // 142 П 103
//0x7F, 0x09, 0x09, 0x09, 0x06,   // 143 Р ->48

//0x3E, 0x41, 0x41, 0x41, 0x22,   // 144 С ->35
//0x03, 0x01, 0x7F, 0x01, 0x03,   // 145 Т ->52
0x61, 0x26, 0x18, 0x06, 0x01,   // 146 У 104
0x1C, 0x22, 0x7F, 0x22, 0x1C,   // 147 Ф 105
//0x63, 0x14, 0x08, 0x14, 0x63,   // 148 Х ->56
0x3F, 0x20, 0x20, 0x3F, 0x60,   // 149 Ц 106
0x07, 0x08, 0x08, 0x08, 0x7F,   // 150 Ч 107
0x7F, 0x40, 0x7F, 0x40, 0x7F,   // 151 Ш 108
0x3F, 0x20, 0x3F, 0x20, 0x7F,   // 152 Щ 109
0x01, 0x7F, 0x48, 0x48, 0x30,   // 153 Ъ 110
0x7F, 0x48, 0x78, 0x00, 0x7F,   // 154 Ы 111
0x7F, 0x48, 0x48, 0x30, 0x00,   // 155 Ь 112
0x41, 0x49, 0x49, 0x2A, 0x1C,   // 156 Э 113
0x7F, 0x10, 0x3E, 0x41, 0x3E,   // 157 Ю 114
0x66, 0x19, 0x09, 0x09, 0x7F,   // 158 Я 115
//0x20, 0x54, 0x54, 0x78, 0x40,   // 159 а ->65

0x3E, 0x49, 0x45, 0x45, 0x38,   // 160 б 116
0x7E, 0x4A, 0x4A, 0x34, 0x00,   // 161 в 117
0x7C, 0x04, 0x04, 0x0C, 0x00,   // 162 г 118
0x38, 0x45, 0x45, 0x49, 0x3E,   // 163 д 119
//0x38, 0x54, 0x54, 0x54, 0x18,   // 164 е ->69
0x4C, 0x30, 0x7C, 0x30, 0x4C,   // 165 ж 120
0x24, 0x42, 0x4A, 0x34, 0x00,   // 166 з 121
0x7C, 0x20, 0x10, 0x7C, 0x00,   // 167 и 122
0x7C, 0x21, 0x11, 0x7C, 0x00,   // 168 й 123
0x7C, 0x10, 0x28, 0x44, 0x00,   // 169 к 124
0x40, 0x3C, 0x04, 0x04, 0x7C,   // 170 л 125
0x7C, 0x08, 0x10, 0x08, 0x7C,   // 171 м 126
0x7C, 0x10, 0x10, 0x7C, 0x00,   // 172 н 127
//0x38, 0x44, 0x44, 0x44, 0x38,   // 173 о ->79
0x7C, 0x04, 0x04, 0x7C, 0x00,   // 174 п 128
//0xFC, 0x18, 0x24, 0x24, 0x18,   // 175 р ->80

//0x38, 0x44, 0x44, 0x44, 0x28,   // 176 с ->67
0x04, 0x04, 0x7C, 0x04, 0x04,   // 177 т 129
//0x4C, 0x90, 0x90, 0x90, 0x7C,   // 178 у ->89
0x18, 0x24, 0x7E, 0x24, 0x18,   // 179 ф 130
//0x44, 0x28, 0x10, 0x28, 0x44,   // 180 х ->88
0x3C, 0x20, 0x20, 0x3C, 0x60,   // 181 ц 131
0x1C, 0x10, 0x10, 0x7C, 0x00,   // 182 ч 132
0x7C, 0x40, 0x7C, 0x40, 0x7C,   // 183 ш 133
0x3C, 0x20, 0x3C, 0x20, 0x7C,   // 184 щ 134
0x04, 0x7C, 0x50, 0x70, 0x00,   // 185 ъ 135
0x7C, 0x50, 0x70, 0x00, 0x7C,   // 186 ы 136
0x7C, 0x50, 0x70, 0x00, 0x00,   // 187 ь 137
0x42, 0x42, 0x52, 0x52, 0x3C,   // 188 э 138
0x7C, 0x10, 0x38, 0x44, 0x38,   // 189 ю 139
0x40, 0x2C, 0x12, 0x7E, 0x00,   // 190 я 140
0x7E, 0x4B, 0x4A, 0x4B, 0x42,   // 191 Ё   D0 81              141

0x38, 0x55, 0x54, 0x55, 0x18,   // 192 ё   D1 91              142
0x7C, 0x04, 0x05, 0x04, 0x00,   // 193 81    129   Ѓ   D0 83  143
0x00, 0x78, 0x0A, 0x09, 0x00,   // 194 83    131   ѓ   D1 93  144
0x3E, 0x49, 0x49, 0x41, 0x22,   // 195 AA    170   Є   D0 84  145
0x38, 0x54, 0x54, 0x44, 0x28,   // 196 BA    186   є   D1 94  146
//0x00, 0x41, 0x7F, 0x41, 0x00,   // 197 B2    178   І   D0 86 ->41
//0x00, 0x44, 0x7D, 0x40, 0x00,   // 198 B3    179   і   D1 96 ->73
0x00, 0x45, 0x7C, 0x45, 0x00,   // 199 AF    175   Ї   D0 87  147
0x00, 0x45, 0x7C, 0x41, 0x00,   // 200 BF    191   ї   D1 97  148
0x23, 0x44, 0x39, 0x04, 0x03,   // 201 A1    161   Ў   D0 8E  149
0x24, 0x49, 0x32, 0x09, 0x04,   // 202 A2    162   ў   D1 9E  150
0x7E, 0x02, 0x02, 0x02, 0x01,   // 203 A5    165   Ґ   D2 90  151
0x7C, 0x04, 0x04, 0x02, 0x00,   // 204 B4    180   ґ   D2 91  152
0x00, 0x4A, 0x55, 0x29, 0x00,   // 205 A7    167   §   C2 A7  153
0x00, 0x06, 0x09, 0x09, 0x06,   // 206             °   C2 B0  154
0x44, 0x44, 0x5F, 0x44, 0x44,   // 207 B1    177   ±   C2 B1  155

0x7C, 0x10, 0x10, 0x3C, 0x40,   // 208 B5    181   µ   C2 B5  156
};


и font_c.c:

#include <avr/pgmspace.h>

const unsigned char font_c[] PROGMEM = { // "жирный" фонт 8х8
0, 0, 0, 0, 0, 0, 0, 0,                // 32       0
24, 60, 60, 24, 24, 0, 24, 0,          // 33  !    1
54, 54, 20, 0, 0, 0, 0, 0,             // 34  "    2
54, 54, 127, 54, 127, 54, 54, 0,       // 35  #    3
8, 60, 2, 28, 32, 30, 8, 0,            // 36  $    4
6, 102, 48, 24, 12, 102, 96, 0,        // 37  %    5
30, 51, 30, 10, 83, 51, 126, 0,        // 38  &    6
24, 24, 24, 12, 0, 0, 0, 0,            // 39  '    7
48, 24, 12, 12, 12, 24, 48, 0,         // 40  (    8
12, 24, 48, 48, 48, 24, 12, 0,         // 41  )    9
0, 54, 28, 127, 28, 54, 0, 0,          // 42  *   10
0, 8, 8, 62, 8, 8, 0, 0,               // 43  +   11
0, 0, 0, 24, 24, 24, 12, 0,            // 44  ,   12
0, 0, 0, 60, 0, 0, 0, 0,               // 45  -   13
0, 0, 0, 0, 0, 24, 24, 0,              // 46  .   14
0, 96, 48, 24, 12, 6, 0, 0,            // 47  /   15
60, 102, 118, 110, 102, 102, 60, 0,    // 48  0   16
24, 24, 28, 24, 24, 24, 126, 0,        // 49  1   17
60, 102, 96, 48, 12, 6, 126, 0,        // 50  2   18
60, 102, 96, 56, 96, 102, 60, 0,       // 51  3   19
48, 56, 52, 50, 126, 48, 48, 0,        // 52  4   20
126, 6, 62, 96, 96, 102, 60, 0,        // 53  5   21
60, 102, 6, 62, 102, 102, 60, 0,       // 54  6   22
126, 102, 48, 48, 24, 24, 24, 0,       // 55  7   23
60, 102, 102, 60, 102, 102, 60, 0,     // 56  8   24
60, 102, 102, 124, 96, 102, 60, 0,     // 57  9   25
0, 24, 24, 0, 24, 24, 0, 0,            // 58  :   26
0, 24, 24, 0, 24, 24, 12, 0,           // 59  ;   27
48, 24, 12, 6, 12, 24, 48, 0,          // 60  <   28
0, 0, 60, 0, 60, 0, 0, 0,              // 61  =   29
6, 12, 24, 48, 24, 12, 6, 0,           // 62  >   30
60, 102, 96, 56, 24, 0, 24, 0,         // 63  ?   31
28, 34, 58, 26, 66, 60, 0, 0,          // 64  @   32
60, 102, 102, 126, 102, 102, 102, 0,   // 65  A   33
62, 102, 102, 62, 102, 102, 62, 0,     // 66  B   34
60, 102, 6, 6, 6, 102, 60, 0,          // 67  C   35
62, 102, 102, 102, 102, 102, 62, 0,    // 68  D   36
126, 6, 6, 62, 6, 6, 126, 0,           // 69  E   37
126, 6, 6, 62, 6, 6, 6, 0,             // 70  F   38
60, 102, 6, 6, 118, 102, 60, 0,        // 71  G   39
102, 102, 102, 126, 102, 102, 102, 0,  // 72  H   40
60, 24, 24, 24, 24, 24, 60, 0,         // 73  I   41
120, 48, 48, 48, 54, 54, 28, 0,        // 74  J   42
102, 54, 30, 14, 30, 54, 102, 0,       // 75  K   43
6, 6, 6, 6, 6, 6, 126, 0,              // 76  L   44
99, 119, 127, 107, 99, 99, 99, 0,      // 77  M   45
99, 103, 111, 123, 115, 99, 99, 0,     // 78  N   46
60, 102, 102, 102, 102, 102, 60, 0,    // 79  O   47
62, 102, 102, 102, 62, 6, 6, 0,        // 80  P   48
60, 102, 102, 102, 118, 60, 96, 0,     // 81  Q   49
62, 102, 102, 62, 30, 54, 102, 0,      // 82  R   50
60, 102, 6, 60, 96, 102, 60, 0,        // 83  S   51
126, 90, 24, 24, 24, 24, 24, 0,        // 84  T   52
102, 102, 102, 102, 102, 102, 60, 0,   // 85  U   53  (124->60)
102, 102, 102, 102, 102, 60, 24, 0,    // 86  V   54
99, 99, 99, 107, 127, 119, 99, 0,      // 87  W   55
99, 99, 54, 28, 54, 99, 99, 0,         // 88  X   56
102, 102, 102, 60, 24, 24, 24, 0,      // 89  Y   57
126, 96, 48, 24, 12, 6, 126, 0,        // 90  Z   58
60, 12, 12, 12, 12, 12, 60, 0,         // 91  [   59
0, 6, 12, 24, 48, 96, 0, 0,            // 92      60  BackSlash
60, 48, 48, 48, 48, 48, 60, 0,         // 93  ]   61
8, 20, 34, 65, 0, 0, 0, 0,             // 94  ^   62
0, 0, 0, 0, 0, 0, 60, 0,               // 95  _   63
12, 12, 24, 0, 0, 0, 0, 0,             // 96  `   64
0, 0, 60, 96, 124, 102, 124, 0,        // 97  a   65
6, 6, 6, 62, 102, 102, 62, 0,          // 98  b   66
0, 0, 60, 102, 6, 102, 60, 0,          // 99  c   67
96, 96, 96, 124, 102, 102, 124, 0,     // 100  d   68
0, 0, 60, 102, 126, 6, 60, 0,          // 101  e   69
56, 108, 12, 62, 12, 12, 12, 0,        // 102  f   70
0, 0, 124, 102, 102, 124, 96, 60,      // 103  g   71
6, 6, 6, 62, 102, 102, 102, 0,         // 104  h   72
0, 24, 0, 24, 24, 24, 60, 0,           // 105  i   73  (0, 6, 0, 6, 6, 6, 79, 0,)
48, 0, 48, 48, 54, 54, 28, 0,          // 106  j   74
6, 6, 102, 54, 30, 54, 102, 0,         // 107  k   75
24, 24, 24, 24, 24, 24, 24, 0,         // 108  l   76
0, 0, 99, 119, 127, 107, 107, 0,       // 109  m   77
0, 0, 62, 126, 102, 102, 102, 0,       // 110  n   78
0, 0, 60, 102, 102, 102, 60, 0,        // 111  o   79
0, 0, 62, 102, 102, 62, 6, 6,          // 112  p   80
0, 0, 30, 27, 27, 30, 88, 120,         // 113  q   81
0, 0, 62, 102, 102, 6, 6, 0,           // 114  r   82
0, 0, 124, 6, 60, 96, 62, 0,           // 115  s   83
0, 24, 24, 126, 24, 24, 24, 0,         // 116  t   84
0, 0, 102, 102, 102, 102, 124, 0,      // 117  u   85
0, 0, 102, 102, 102, 60, 24, 0,        // 118  v   86
0, 0, 99, 107, 107, 107, 62, 0,        // 119  w   87
0, 0, 102, 60, 24, 60, 102, 0,         // 120  x   88
0, 0, 102, 102, 124, 96, 60, 0,        // 121  y   89
0, 0, 60, 48, 24, 12, 60, 0,           // 122  z   90
56, 12, 12, 6, 12, 12, 56, 0,          // 123  {   91
8, 8, 8, 8, 8, 8, 8, 0,                // 124  |   92
14, 24, 24, 48, 24, 24, 14, 0,         // 125  }   93
0, 0, 92, 54, 0, 0, 0, 0,              // 126  ~   94
//0, 0, 0, 0, 0, 0, 0, 0,                // 127  BackSpace
//60, 102, 102, 126, 102, 102, 102, 0,    // 127  А
126, 6, 62, 102, 102, 102, 62, 0,       // 128  Б   95
//62, 102, 102, 62, 102, 102, 62, 0,      // 129  В
126, 6, 6, 6, 6, 6, 6, 0,               // 130  Г   96
56, 52, 54, 54, 54, 54, 127, 99,        // 131  Д   97
//126, 6, 6, 62, 6, 6, 126, 0,            // 132  Е
219, 219, 90, 188, 90, 219, 219, 0,     // 133  Ж   98
60, 102, 96, 56, 96, 102, 60, 0,        // 134  З   99
102, 102, 102, 102, 118, 110, 102, 0,   // 135  И  100
24, 0, 102, 102, 118, 110, 102, 0,      // 136  Й  101
//102, 54, 30, 14, 30, 54, 102, 0,        // 137  К
112, 104, 108, 108, 108, 108, 110, 0,   // 138  Л  102
//99, 119, 127, 107, 99, 99, 99, 0,       // 139  М
//102, 102, 102, 126, 102, 102, 102, 0,   // 140  Н
//60, 102, 102, 102, 102, 102, 60, 0,     // 141  О
126, 102, 102, 102, 102, 102, 102, 0,   // 142  П  103
//62, 102, 102, 102, 62, 6, 6, 0,         // 143  Р
//60, 102, 6, 6, 6, 102, 60, 0,           // 144  С
//126, 90, 24, 24, 24, 24, 24, 0,         // 145  Т
102, 102, 102, 124, 96, 102, 60, 0,     // 146  У  104
126, 219, 219, 219, 126, 24, 24, 0,     // 147  Ф  105  (254-126)
//99, 99, 54, 28, 54, 99, 99, 0,          // 148  Х
51, 51, 51, 51, 51, 51, 127, 96,        // 149  Ц  106
102, 102, 102, 126, 96, 96, 96, 0,      // 150  Ч  107
99, 99, 107, 107, 107, 107, 127, 0,     // 151  Ш  108
99, 99, 107, 107, 107, 107, 255, 192,   // 152  Щ  109
7, 7, 62, 102, 230, 102, 62, 0,         // 153  Ъ  110
195, 195, 207, 219, 219, 219, 207, 0,   // 154  Ы  111
6, 6, 62, 102, 102, 102, 62, 0,         // 155  Ь  112
60, 102, 96, 120, 96, 102, 60, 0,       // 156  Э  113
115, 219, 219, 223, 219, 219, 115, 0,   // 157  Ю  114
124, 102, 102, 102, 120, 108, 102, 0,   // 158  Я  115
//0, 0, 60, 96, 124, 102, 124, 0,         // 159  а
64, 60, 6, 62, 102, 102, 60, 0,         // 160  б  116
12, 22, 22, 14, 62, 102, 60, 0,         // 161  в  117
0, 0, 126, 6, 6, 6, 6, 0,               // 162  г  118
0, 0, 60, 54, 54, 54, 127, 99,          // 163  д  119
//0, 0, 60, 102, 126, 6, 60, 0,           // 164  е
0, 0, 219, 90, 60, 90, 219, 0,          // 165  ж  120
0, 0, 60, 102, 48, 102, 60, 0,          // 166  з  121
0, 0, 102, 102, 118, 110, 102, 0,       // 167  и  122
24, 0, 102, 102, 118, 110, 102, 0,      // 168  й  123
0, 0, 102, 54, 30, 54, 102, 0,          // 169  к  124
0, 0, 112, 104, 108, 108, 110, 0,       // 170  л  125
0, 0, 99, 119, 107, 99, 99, 0,          // 171  м  126   (247,235-119,107)
0, 0, 102, 102, 126, 102, 102, 0,       // 172  н  127
//0, 0, 60, 102, 102, 102, 60, 0,         // 173  о
0, 0, 126, 102, 102, 102, 102, 0,       // 174  п  128
//0, 0, 62, 102, 102, 62, 6, 6,           // 175  Р
//0, 0, 60, 102, 6, 102, 60, 0,           // 176  с
0, 0, 126, 24, 24, 24, 24, 0,           // 177  т  129
//0, 0, 102, 102, 124, 96, 60, 0,         // 178  у
0, 0, 60, 90, 90, 90, 60, 24,           // 179  ф  130
//0, 0, 102, 60, 24, 60, 102, 0,          // 180  х
0, 0, 51, 51, 51, 51, 127, 96,          // 181  ц  131
0, 0, 102, 102, 126, 96, 96, 0,         // 182  ч  132
0, 0, 99, 107, 107, 107, 127, 0,        // 183  ш  133
0, 0, 99, 107, 107, 107, 255, 192,      // 184  щ  134
0, 0, 7, 62, 102, 102, 62, 0,           // 185  ъ  135
0, 0, 195, 207, 219, 219, 207, 0,       // 186  ы  136
0, 0, 6, 62, 102, 102, 62, 0,           // 187  ь  137
0, 0, 60, 98, 120, 98, 60, 0,           // 188  э  138
0, 0, 115, 219, 223, 219, 115, 0,       // 189  ю  139
0, 0, 124, 102, 126, 108, 102, 0,       // 190  я  140
102, 0, 126, 6, 62, 6, 126, 0,          // 191  Ё  141
36, 0, 60, 102, 126, 6, 60, 0,          // 192  ё  142
48, 0, 126, 6, 6, 6, 6, 0,              // 193  Ѓ  143
96, 24, 0, 126, 6, 6, 6, 0,             // 194  ѓ  144
60, 102, 6, 30, 6, 102, 60, 0,          // 195  Є  145
0, 0, 60, 70, 30, 70, 60, 0,            // 196  є  146
//60, 24, 24, 24, 24, 24, 60, 0,          // 197  І
//0, 24, 0, 24, 24, 24, 60, 0,            // 198  і
54, 0, 60, 24, 24, 24, 60, 0,           // 199  Ї  147
0, 54, 0, 24, 24, 24, 60, 0,            // 200  ї  148
90, 102, 102, 124, 96, 102, 60, 0,      // 201  Ў  149
36, 24, 102, 102, 124, 96, 60, 0,       // 202  ў  150
32, 62, 6, 6, 6, 6, 6, 0,               // 203  Ґ  151
0, 32, 62, 6, 6, 6, 6, 0,               // 204  ґ  152
24, 6, 24, 102, 24, 96, 24, 0,          // 205  §  153
0, 28, 54, 54, 28, 0, 0, 0,             // 206  °  154
0, 24, 24, 126, 24, 0, 126, 0,          // 207  ±  155
0, 0, 102, 102, 126, 102, 198, 6,       // 208  µ  156
};

На всякий случай напомню, что данный контроллер должен отображать на экране то, что передается ему извне, т.е.  другого контроллера. Для проверки была использованка Mega со следующим скетчем:

#define SCREEN_MODE_480 0  // режим дисплея разрешением 480х240
#define SCREEN_MODE_512 1  // режим дисплея разрешением 512х240
#define CHAR_SIZE_6x8 0  // режим узких символов 6х8
#define CHAR_SIZE_8x8 1  // режим широких символов 8х8
#define CP_UTF8 0  // режим кодировки UTF-8
#define CP_1251 1  // режим кодировки CP-1251
#define CP_866 2   // режим кодировки CP-866 (Альтернативная ГОСТ)
#define CP_KOI8 3  // режим кодировки KOI8R

HardwareSerial * S = &Serial3;

void printStr(const char* ch) {
  while(*ch) { S->print((char)*ch++); }
}

void setXYchar(int x, int y) {
  S->print("]~P");
  S->print(x);
  S->print('x');
  S->print(y);
  S->print('$');
}

void setCodePage(int page) {        // выбор кодовой страницы
  const char m[4] = {'U', '1', 'A', 'K'};
  S->print("]~");
  S->print(m[page]);
}

void printCharSet(int x, int y, unsigned char b, const char* ch) {
  setXYchar(x,y);
  printStr(ch);
  int jj = b > 127 ? 4 : 8;
  int ii = b == 0 ? 0 : 128;
  for(int j = 0; j < jj; j++) {
    setXYchar(x,y+(j+1)*8);
    for(int i = 0; i < 16; i++) {
      if (b > 127) S->write(b);
      S->print((char)(ii + j*16 + i));
    }
  }
}


void sendSeq() {
//  S->println("test");
  S->print("]~4");           // mode 480x240
  S->print("]~C");           // clear screen
  S->print("]~P200x100$]~8H e l l o !");
  S->print("]~P160x140$]~6Serial Terminal Display Test");
  S->print("]~M140x80$]~L340x80$]~L340x160$]~L140x160$]~L140x80$"); // draw frame
  Serial.println("OK!");
  delay(4000);

  S->print("]~C");
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~5");
  delay(4000);
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~8");
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~4");
  delay(4000);
  for(int i = 0; i < 128; i++) {
    S->print("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz..");
  }
  delay(4000);
  S->print("]~5");
  delay(4000);
  S->print("]~C");
  for(int i = 0; i < 1024; i++) {
    S->print("]~L");
    S->print(random(512));
    S->print("x");
    S->print(random(240));
    S->print("$");
  }
//  delay(4000);
  S->print("]~P228x108$       ");
  S->print("]~P228x116$ E N D ");
  S->print("]~P228x124$       ");
  delay(4000);

  S->print("]~C");
  S->print("]~8");
  setCodePage(CP_UTF8);
  S->print("]~P50x20$Work with different cyrillic codepages");
  S->print("]~P50x30$Работа с различными кодировками кириллицы");

  printCharSet(24, 54, 0, "ASCII");

  printCharSet(176, 54, 0xD0, "UTF-8 D0");
  printCharSet(328, 54, 0xD1, "UTF-8 D1");
  printCharSet(176, 104, 0xD2, "UTF-8 D2");
  printCharSet(328, 104, 0xC2, "UTF-8 C2");
  setCodePage(CP_1251);
  printCharSet(176, 154, 2, "CP-1251");
  setCodePage(CP_866);
  printCharSet(328, 154, 2, "CP-866");
  setCodePage(CP_KOI8);
  printCharSet(24, 154, 2, "CP-KOI8R");
  delay(4000);

}

void setup() {
  // put your setup code here, to run once:
//  pinMode(2,OUTPUT);
//  tone(2, 4000);
  S->begin(9600);
  Serial.begin(115200);
  sendSeq();
}

void loop() {
  // put your main code here, to run repeatedly:

}

Для того, чтобы добиться желаемого результата вход контроллера stm32 Serial1 должен быть подключен к выходу Меги Serial3.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Вообще-то, смысл вашего проекта наверное в том, чтобы наложить надписи на видео от другого источника. Сразу приходят на ум: ОСД для дрона самопального, какой-то радар заднего хода автомобиля с выводом растояний и положения помехи на экран медиасистема, смешанный с видео от камеры заднего вида и т.п.

А вот как VGA не уверен, что кому-то будет интересно. Тем более что аналоговых телеков пока еще дофига, а вот монитор лишним не бывает, всегда куда-то нужнее, чем ардуино к нему подключать.

Только для реализации ОСД что-то нужна схематика микшера видео, и синхронизация от внешнего источника видео.

Вообще-то, я что-то похожее на ОСД на 328-ом видел, на форуме любителей радимоделей.