С stm32f103 на телевизор (полный TV сигнал)
- Войдите на сайт для отправки комментариев
Не то, чтобы я считаю эту тему актуальной, но ведь кому-то понадобилось делеть 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 (жирный).
Интересно, хотя и непонятно зачем :)
С интересом жду подробностей по реализации этого в коде.
О_О
Давно хотел реализовать на работу табло с текущими параметрами эксперимента на большом экране 40 дюймого телевизора притащенного аспирантами из дома за ненадобностью. Уже полгода лежит без дела. Присоединяюсь. Реализую в ближайшее время. Только шрифт будет большим, чтобы из далека было видно.
P.S. Они уже всё сделали :-(
https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F1/libraries/A_STM32_Examples/examples/Maple/CrudeVGA/CrudeVGA.ino
b707, я тоже не очень понимаю, зачем.
Ну, например, есть у меня Orange Zero. Непроверенный. Потому как без видеоконтроллера, но с TTL UART. Так что можно было бы использовать подобную конструкцию в качестве видеотерминала.
b707, я тоже не очень понимаю, зачем.
Ну, например, есть у меня Orange Zero. Непроверенный. Потому как без видеоконтроллера, но с TTL UART. Так что можно было бы использовать подобную конструкцию в качестве видеотерминала.
Очень напомнило:
Однажды Ходжу Насреддина спросили:
-- Как избавиться от крыс?
-- Очень просто! - сообщил Ходжа.
-- Нужно растолочь черепки в порошок и, когда поймаешь крысу, засыпать ей это порошок в ноздри. Крыса не сможет дышать и издохнет.
-- Но если я поймал крысу, не проще ли убить ее, ударив об стену?
-- Конечно проще! - ответил Ходжа.
P.S. Они уже всё сделали :-(
https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F1/libraries/A_STM32_Examples/examples/Maple/CrudeVGA/CrudeVGA.ino
Ну, этот исходник я, разумеется, изучил и даже кое-что из него замствовал.
То есть полезное там как бы есть, но использовать это на практике нельзя.
Похоже, выглядит это так:
1. Все это делается программно, причем, по горизонтали получается что-то чуть больше 20 "пикселов".
2. Есть один неприятный момент, который мне как раз хотелось бы обсудить.
Меня интересуют строки исходника 91 и 107. В первую очередь, конечно, 107, потому что 91, на мой взгляд, совершенно бесполезна.
Дело в том, что без этих строк (в том числе и у меня) на экране промелькивают помехи, а в результате Serial.end() эти помехи исчезают, но одновременно с этим "отваливается" USB порт. Т.е. перепрошить плату в дельнейшем удается только, нажав в нужный момент на RESET. Похоже, этот Serial.end() убивает как USB, так и милисекундные прерывания системы времени.
В общем, кто по этому поводу что может сказать?
Очень напомнило:
Однажды Ходжу Насреддина спросили:
-- Как избавиться от крыс?
-- Очень просто! - сообщил Ходжа.
-- Нужно растолочь черепки в порошок и, когда поймаешь крысу, засыпать ей это порошок в ноздри. Крыса не сможет дышать и издохнет.
-- Но если я поймал крысу, не проще ли убить ее, ударив об стену?
-- Конечно проще! - ответил Ходжа.
Вечер. Отец с сыном гуляют по берегу реки. На берегу сидит художник и рисует воду, мост через реку, закат...
Отец сыну(наставительно):
- Видишь, как дядя мучается без фотоаппарата?
В то же время ширина спектра сигнала изображения составляет 6.25 МГц, что соответствует пикселной частоте 12.5 МГц.
#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); } }#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 };Выяснил, что после отключения USB (Serial.end()) последовательный порт (Serial1) сохраняет работоспособность.
Значит, так и будет работать: USB выключаем дабы избавиться от помех на экране, а обмен ведем через COM-порт (PA9, PA10).
Видео одного из промежуточных вариантов: https://youtu.be/kdt_LOdkxfY
Выводит на экран текст, передаваемый по последовательному порту.
Имеет два режима экрана 480х240 и 512х240 пикселей, позволяет переключаться между ними в процессе работы посредством управляющих последовательностей.
Поддерживает два фонта: 6х8 и 8х8 пикселей, которые могут присутствовать на экране одновременно. Переключаются управляющими последовательностями.
Позволяет позиционировать место вывода.
Позволяет рисовать линии посредством управляющих последовательностей.
Пока:
- нет борьбы с помехами,
- не поддерживается кириллица,
- не поддерживаются аппаратные настройки (например, переключение кодовой страницы, что будет актуально после подключения кириллицы - планируется utf-8, 866, 1251 и КОИ8).
Для записи ролика был создан сценарий, т.е. последовательность символов, передаваемая в порт. В качестве источника данных была использована Мега. К проекту она никакого отношения не имеет, вместо нее можно было использовать ардуиновский монитор порта, но я, увы, не могу соревноваться с Мегой в скорости ввода символов.
Код будет выложен ближайшие день-два.
PS. Ни у кого нет на примете фонта 8х8 (желательно жирного) с украинско-белорусской кириллицей?
А вот и обещанные исходники:
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
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 и передавать команды и строки текста прямо из монитора порта.
Похожий проект "VGA на Arduino Due": https://github.com/stimmer/DueVGA/
Да, сходство есть.
Но мне хотелось:
1. Не менее 80 символов в строке (а в указанном проекте - до 53).
2. Обойтись контроллером, который почти на порядок дешевле.
При этих условиях единственный вариант - отказаться от цвета.
Но я сейчас продумываю вариант, при котором можно будет задавать цвет каждой строки целиком (из 7-255 цветов). Вот только думаю, может ли такое когда-нибудь кому-нибудь понадобиться. Тем более, что такой вариант однозначно предполагает переход с композитного сигнала на VGA.
Похожий проект "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
Да, посмотрел дэйташит на SAM8, там, в отличие от stm32, для SPI можно использовать практически любой коэффициент деления, тогда как для stm32 - только степени двойки. Наверное поэтому для sym32 и нет аналогов. Но я обошел это при помощи SPI slave.
А так - конечно, Due выглядит более многообещающим вариантом по сравнению с stm32f103 из-за впятеро большего объема памяти, на 17% более высокой тактовой частоты и большей свободы в выборе пиксельной частоты. Но, с другой стороны - неудобный формфактор и высокая цена.
PS. Естественно, проект не претендует на популярность, т.к. первый вопрос - "Зачем?". Но, все-таки, хотелось бы выслушать мнения по двум вопросам:
1. Какой интерфейс предпочтительнее: композитный TV-сигнал или VGA?
2. Имеет ли какой-то смысл предусмотреть возможность выводить каждую строку растра своим цветом?
Сделал поддержку кириллицы.
Поддерживаются 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:
файл библиотеки 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.
Вообще-то, смысл вашего проекта наверное в том, чтобы наложить надписи на видео от другого источника. Сразу приходят на ум: ОСД для дрона самопального, какой-то радар заднего хода автомобиля с выводом растояний и положения помехи на экран медиасистема, смешанный с видео от камеры заднего вида и т.п.
А вот как VGA не уверен, что кому-то будет интересно. Тем более что аналоговых телеков пока еще дофига, а вот монитор лишним не бывает, всегда куда-то нужнее, чем ардуино к нему подключать.
Только для реализации ОСД что-то нужна схематика микшера видео, и синхронизация от внешнего источника видео.
Вообще-то, я что-то похожее на ОСД на 328-ом видел, на форуме любителей радимоделей.
почему бы не использовать делитель на 5 (72/5=14.4, близко к 14.318), если используешь внешнюю синхронизацию, и тогда с строками длительностью в 63.5мкс, (NTSC timing)
можно будет как усеченный PAL-M или NTSC использовать.
отдельно буфер строки 904, 912 или 920 бит (113, 114 или 115 байт, 904 укладывается в стандарт, 912 чуть перебором, а 920 уже за пределами ntsc) и там как колорбурст так и цвет выводить.
пауза (0-ли), (после синхросигнала 5 байт 0xBB или 11001100), пауза (0-ли), потом цвет в виде декодированной последовательности (abcd)(efgh) - две тетрады.
чуть позже дополню с тетрадами, в ods посчитаю варианты как pal так и ntsc.
для пал подсчитал сочетания, если как у Вас 1 битное:
0000-0000
0001-0100
1001-1100
1110-1011
1011-1110
1111-1111
это стандрартные rgb, нечётный и четные строки, т.е. последовательно 16 цветов, но в 1битном кодировании PAL получилось только 6 независимых цветов.
по NTSC наверняка можно обыграть все 16 комбинаций 4битовых последовательностей, но какие они дадут цвета - не могу сказать, надо пробовать.
я попробую с использованием двух SPI каналов, двухбитные последовательности.
в виде двух буферов по 228 байт, и выдача параллельно с PA6 и PB9 через резисторы 580 и 240 Ом,
и 0 - на обоих бит=0, 1 на 1=1, на 2=0, 2 на 1=0, на 2=1, и 3 на 1=1, на 2=1.
и конденсатор там не нужен. выход нагружен входным сопротивлением в 75 ом.
тогда возможно будут такие комбинации:
всего 11 комбинаций.
отдельно буфер строки 904, 912 или 920 бит (113, 114 или 115 байт...
904*240/8=27120 байт, откуда в BluePill столько памяти?
Там на байт несколько точек. Вы же сами по spi выдаёте сигнал.
А тут сигнал на частоте цветоразностной сразу даёт цвет по pal или ntsc.
У меня указаны битовые последовательности в случае 1 спи (0/1)
Или при использовании 2 спи 0 (синхро), оба канала на 0, 1(с большим резистором в 1, 2 меньший резистор в1, больший в 0, 3 оба в единице.
Если использовать делитель 10, будет 4 точки на байт и на 1 спи.
И буфер не предлагаю сразу на весь экран, это буфер строки. Пока одна передается, другая формируется или от знакогенератора и текстового буфера, или с строки из графического фрейм буфера.
Другого простого варианта сделать унифицированный вывод видео при разных входных данных и двух немного разных методах кодирования цвета.
И буфер строки формируется на всю строку, на 64мксек. Тогда 20% неизменно, а 80% формируется из известных данных. Потом по таймеру запускаем спи и формируем четную строку, делаем другие задачи.
Тут можно любую ртос с переключением таймером в 15734/15625 раз. На таймере запуск спи и инициация заполнения 2 буфера.
Получаем при вашей схеме дополнительно 2*114 байт на 2 строки, если быстрый контроллер, то можно в онлайн формировать, 1 буфером.
Вам же и цвет и кучу всего захочется.
А тут сделать механизм формирования строки и не важно, какой буфер в целом и как им пользуются, лишь бы период формирования строки укладывался в 64 мксек.
Каков объем графического фрейм буфера 904х240 точек?
Чтобы "формировать" строку, надо откуда-то брать данные. Откуда брать данные для графического экрана? Вы знаете другой источник информации кроме фрейм буфера?
елы-палы. есть графический фрейм-буфер или текстовый фрейм-буфер, т.е. массив байт грубо говоря 80х24.
используем 2 буфера строк по 904..912 точек. ссылка на следующий буфер, номер строки, чет/нечет полукадр,
обычная задача по семафору, имея переменную номер строки, четный или нечетный полукадр, ссылку на строчный буфер и исходные данные - рисует как ей душа пожелает.
когда происходит вызов переполнения таймера строки (15625 или 15734 Гц) выбор:
ссылки на строковые буфера: новый=предыдущий, текущий=следующий, следующий=новый.
запускаем spi,
увеличиваем номер новой строки. если он больше>262+четный&1, то номер=1, четный=(++четный)&1.
(это я сразу пишу в с кодировании)
работа с новым буфером - если следующая с изображением - то семафор на формирование следующей строки изображения,
иначе какая там необходима строка - кадровый синхро-импульс, строковый, просто "чёрная" строка... можно взять заранее заготовленные.
в этих 904 строках 20% практически неизменны, это вне отображаемого пространства.
а около 704..720 точек - их сочетание по тетрадам, т.е. на 177..180 точек даст всевозможные состояния. выше - цветное разрешение сложно предсказать.
если будет ч/б изображение - после строкового импульса не выставлять color burst и будет чб сигнал высокого разрешения, до 704..720 точек разрешающей способности. и можно все поля сделать детально по стандарту.
тогда, если формирование буфера строки не прерываемо планировщиком - то в строках не будет помех, будет независимость от состояния исходного буфера.
если свести до графики CGA адаптера, т.е. 4 цвета 320х200, то можно использовать 1spi, и делитель на синхросигнал для spi будет равен 10, т.е. синхросихнал будет 7.2МГц, тогда строчный буфер можно в 2 раза уменьшить.
при использовании 1spi+1 вывод синхро, как у тебя, то при 3.3V, 2 резистора номиналом 560 ом на синхро, 220 ом на spi - получим при 75ом входной нагрузки 0, 0.3, 1.06 вольт. т.е. монохромный или в случае color burst цветной сигнал одной тональности.
я на выходных поищу bluepill и накарябаю код. заведу через rca или scart на дисплей, посмотрю что получится.
можно для расширения диапазона использовать 2spi и оба синхронизировать с делителя, тогда у нас получится 4 значения напряжений:
0, 0.3, 0.76, 1.06 вольт. - с ними вариантов будет больше.
"В огороде бузина, а в Киеве - дядька."
Я не спрашивал, как реализовать текстовый буфер.
И меня совершенно не интересует режим "как CGA" 320х200.
Тем более, я ничего не спрашивал про резисторы.
Меня интересует один вопрос: как разместить графический буфер 904х240 в памяти BluePill? Желательно в цветном варианте.
е-ма, не весь буфер, а только буфер строки!!!
Что "буфер строки"?
Зачем мне буфер строки?
Где хранить то, что нарисовано на экране?
Это вторичный буфер, который не используется для работы пользователем, он в количестве двух штук используется прерыванием для организации вывода видео, а формирование его уже идёт хоть от текстового буфера, хоть от графического, хоть налету, например при распознавании postscript языка. Чтобы мог организовать хоть как в спектруме графические атрибуты, хоть на каждую точку rgba, текст. Вывод будет отвязанный от реализации основного буфера, связь через 1 функцию конверсии с основного буфера в строковый. Сначала отработать что мы можем вообще выжать из твоего варианта выдачи видеосигнала, а потом уже на основном буфере уже определиться как кодировать для пользователя.
Ну опять, я про Фому, а Вы - про Ерему!
zelya, у меня к Вам просьба: либо ответьте на вопрос из 3-й строки сообщения №26, либо заведите тему и пишите туда все, что хотите. Но не нужно помещать в данную тему то, что к ней не имеет ни малейшего отношения.
у меня несколько вопросов, в начале красава, достойное решение.
1. к монитору прикрутили?
2.115200 это макс скорость порта?
3. можно ли прикрутить клаву и ответ по uart в обратку сканов клавы
1. К какому монитору?
2. Строго говоря, там чистый USB, поэтому на физическую скорость эта константа влияния не оказывает.
3. Если есть синяя изолента - почему нет!
А почему не на esp? Ща врде блюпилы как крыло самолета стоят, да и памяти побольше в есп.
прикрутил 230400, полет нормальный, все устраивает.
прикладываю результат
https://youtu.be/_e-hXnwsUxU
убрать бы мусор и немного стабильности, и буду пытаться запилить свой маленький не доретро компик
А почему не на esp? Ща врде блюпилы как крыло самолета стоят, да и памяти побольше в есп.
1. Когда я покупал, было 105-110р. Пока не закончились.
2. Я не встречал нормального дэйташита на esp. Зато где-то слышал, что его в природе не существует.
Я начал возиться с аппаратной реализацией (нужно добавить одну-две простейшие логические микрухи), но наткнулся на непонятки. Потом выяснил, что конкретный экземпляр stm выдает на одной из используемых ног ненормальное напряжение (больше вольта логического нуля). Но к тому моменту занялся другими проектами и возвращаться к этому уже не хотелось.
из всех таких устройств, отношение цена, производительность и разрешение, это лучшее, даже если прикрутить пару элементов логики, копейки
хотелось бы увидеть итог
может чем помогу
Мне одному заметно что это не 4К? И даже вроде не HD...
Мне одному заметно что это не 4К? И даже вроде не HD...
Вы сударь, часом не ошиблись ссайтом малинки, попали на сайт арду. :-)
Понял. Тут можна поговнячить.. :-)
Понял. Тут можна поговнячить.. :-)
шкодить добрым саморитянам, но для чего. если есть достойные предложения, - тогда можно поерничать.
А засрать мона все, даже себе на полу дома
выяснил, что конкретный экземпляр stm выдает на одной из используемых ног ненормальное напряжение (больше вольта логического нуля).
дай попробую угадать, уж не разделяется ли эта нога с SERIAL?
дай попробую угадать, уж не разделяется ли эта нога с SERIAL?
Да и какая связь с Сериал, колись
дай попробую угадать, уж не разделяется ли эта нога с SERIAL?
Да и какая связь с Сериал, колись
я в важгаде пост выкладывал, об использовании третьего таймера, с осциллограммами, реплика была про грань добра и зла )))
казалось бы какая связь... плата другая. пины другие, аддон другой...
казалось бы какая связь... плата другая. пины другие, аддон другой...
а только наружная обвязка может подтягивать уровень, это либо LED либо SERIAL других не знаю, может они и есть, нужен пин нужна схема, чудес не бывает...правда еще бывает вжаренная логика )))
PS я жеж говорил - попробую угадать...
Мне одному заметно что это не 4К? И даже вроде не HD...
Сэр что-нибудь слышал о такой науке как арифметика?
Для HD нужно 1920*1080/8=259200 байт видеопамяти, где столько взять в Блупилле?
я в важгаде пост выкладывал, об использовании третьего таймера, с осциллограммами, реплика была про грань добра и зла )))
а, если подвесить вч транзистор (чтоб небыло мусора и задержки при использовании логики, тот же старый и добрый 315 или 361, в литературе их ставят в генераторы с логикой, чтоб поднять уровень и снизить завалы) в импульсном режиме, и пару элементов, чтоб поднять уровень и сделать реку меандру. помнится в старых компах и в импульсных устройствах выравнивали (делали прямые углы) завалы, сдвиг на пару нс я думаю не сильно скажется
Мне одному заметно что это не 4К? И даже вроде не HD...
Сэр что-нибудь слышал о такой науке как арифметика?
Да сэр! Арихетика - моя слабость. Все больше интригалы, да интригалы.
Мне одному заметно что это не 4К? И даже вроде не HD...
Для HD нужно 1920*1080/8=259200 байт видеопамяти, где столько взять в Блупилле?
А вот проблемы индейцев шерифа не волнуют. Выбираем правильно кристалл под задачу.
с интрегалами разобрались?. Займемся нашими патифонами, у кого есть предложения?
реализовать триггер шмидта https://uk-parkovaya.ru/wp-content/uploads/1/1/6/11635e811bf116fadc250ba...
руководствуясь к примеру https://lib.qrz.ru/node/5480
или типо того https://forum.cxem.net/uploads/monthly_12_2014/post-186391-0-76944100-14...
просто у меня нет осциллографа для измерения форм сигналов с частотой более 20 Мгц, а то бы помог. не разжился я ещё
может за основу, для формирования _П_П_П_ взять входной каскад на полевике из схемы https://fornk.ru/2990-samodelnyj-chastotomer-na-attiny2313/
А в чем вопрос-то?
LM393 и голову не морочить