С 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 аватар
Онлайн
Зарегистрирован: 25.01.2017

О_О

nik182
Offline
Зарегистрирован: 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. Имеет ли какой-то смысл предусмотреть возможность выводить каждую строку растра своим цветом?