Однажды один мой друг спросил, на чем бы я сделал таймер обратного отсчета, чтобы на телевизоре показывал большие цифры. Понятно, что можно подключить ноутбук / iPad / Android и написать приложение, только ноутбук — громоздко, а написанием мобильных приложений ни друг, ни я никогда не занимались.
И тут я вспомнил, что видел в сети проекты тв-терминалов на микроконтроллере AVR. В голове сразу появилась идея объединить маленькие символы в большие и мы решили попробовать. Как-то само собой получилось, что основную работу пришлось делать мне.
Конечно, небольшой опыт разработки устройств на МК у меня есть, но всегда проще взять готовое, поэтому я начал с активного поиска готового решения вывода на телевизор. Основным критерием поиска стала, в первую очередь, простота, по возможности, использование языка С без ассемблерных вставок, высокое качество изображения.
Найдено было много проектов, но оказалось, что большинство из них критериям не особо соответствуют. Впоследствии стало ясно, что главное — понять принцип формирования видеосигнала, а дальше дело пойдет. Но на данном этапе безусловным фаворитом стал проект Максима Ибрагимова «Простой VGA/видео адаптер», он и лег в основу моей поделки. Однако, в процессе работы от него осталась только структура, реализацию пришлось переделать практически полностью.
Дополнительной задачей, которую я практически сам себе придумал, стало задание начального времени с ИК-пульта.
В качестве основного контроллера я решил использовать ATMega168, работающий на 20МГц. Аппаратная часть формирователя видеосигнала выглядит так:
Начал я с того, что выкинул из проекта все, что касается VGA, так как его делать не планировал. Попутно изучал стандарты кодирования видеосигнала, наиболее доступной мне показалась картинка с сайта Мартина Хиннера:
.
По этой картинке делал генератор сигнала синхронизации.
В основе генератора — Timer1 в режиме fastPWM. Дополнительно глобальной переменной организован счетчик синхроимпульсов. По каждому прерыванию переполнения таймера происходит проверка номера синхроимпульса на ключевое значение, изменение длительности следующего синхроимпульса и период следующего синхроимпульса (полная строка / половина строки). Если не требуется изменений, делаются стандартные действия — увеличивается счетчик синхроимпульсов, изменяются другие переменные.
// 2. System definitions
#define Timer_WholeLine F_CPU/15625 //One PAL line 64us
#define Timer_HalfLine Timer_WholeLine/2 //Half PAL line = 32us
#define Timer_ShortSync Timer_WholeLine/32 //2us
#define Timer_LongSync Timer_ShortSync*15 //30us
#define Timer_NormalSync Timer_WholeLine/16 //4us
#define Timer_blank Timer_WholeLine/8 //8us
//Global definitions for render PAL
#define PAL_FPS 50
#define pal_first_visible_line1 40
#define pal_last_visible_line1 290 //pal_first_visible_line1+pal_row_count*pal_symbol_height
#define horiz_shift_delay 15
// Initialize Sync for PAL
synccount = 1;
VIDEO_DDR |= (1<<SYNC_PIN);
OCR1B = Timer_LongSync;
TCCR1A = (1<<COM1B1)|(1<<COM1B0)|(0<<WGM10)|(1<<WGM11); //Fast PWM,Set OC1B on Compare Match,
// clear OC1B at BOTTOM (inverting mode)
TCCR1B = (1<<WGM12)|(1<<WGM13)|(1<<CS10); //full speed;TOP = ICR1
ICR1 = Timer_HalfLine; //Начинаем с двух прерываний на строку.
TIMSK1 = (1<<OCIE1B); //enable interrupt from
row_render=0;
y_line_render=0;
//генератор синхросигнала
volatile unsigned int synccount; // счетчик импульсов синхронизации
EMPTY_INTERRUPT (TIMER1_COMPB_vect);
void MakeSync(void)
{
switch (synccount)
{
case 5://++++++++++++++++++++++++++++++++++++++++++++++++++++++++=
Sync=Timer_ShortSync;
synccount++;
break;
case 10://++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ICR1 = Timer_WholeLine;
Sync= Timer_NormalSync;
synccount++;
break;
case 315://++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ICR1 = Timer_HalfLine;
Sync= Timer_ShortSync;
synccount++;
break;
case 321://++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Sync=Timer_LongSync;
synccount=1;
framecount++;
linecount = 0;
break;
default://++++++++++++++++++++++++++++++++++++++++++++++++++++++++
synccount++;
video_enable_flg = ((synccount>pal_first_visible_line1)&&(synccount<pal_last_visible_line1));
break;
}
}
В конце каждой строки контроллер вгоняется в сон, по прерыванию по переполнению таймера просыпается, после чего сразу вызывается функция MakeSync(), задающая настройки таймера на следующий период синхронизации, после чего, если номер синхросчетчика попадает в видимую область, начинается вывод видеосигнала.
Вывод видеосигнала организован через SPI, работающий на максимальной частоте, равной половине частоты тактового сигнала.
#define SPI_PORT PORTB
#define SPI_DDR DDRB
#define MOSI PORTB3
#define MISO PORTB4
#define SCK PORTB5
//Вывод видео
#define VIDEO_PORT SPI_PORT
#define VIDEO_DDR SPI_DDR
#define VIDEO_PIN MOSI
#define VIDEO_OFF DDRB=0b00100100;
#define VIDEO_ON DDRB=0b00101100;
//Set SPI PORT DDR bits
VIDEO_DDR |= (1<<MOSI)|(1<<SCK)|(0<<MISO);
SPSR = 1 << SPI2X;
SPCR = (1 << SPE) | (1 << MSTR); //SPI enable as master ,FREQ = fclk/2
Сам процесс вывода осуществляется в каждой строке функцией DrawString, которой в качестве параметров передается указатель на массив цифр для вывода, указатель на используемый шрифт и количество выводимых символов. Также при выводе используются глобальные переменные номера выводимой строки в каждом шрифте и номера символа. Внутри каждого символа, в цикле с количеством итераций, равному ширине данного символа в байтах, эти байты шрифта передаются в регистр SPDR.
Кроме того, аппаратная реализация SPI в контроллере AVR не может передавать несколько байт данных подряд. После каждого байта один бит пропускается, из-за чего возникают разрывы на изображении.
Чтобы победить этот недостаток, пришлось воспользоваться трюком, предложенным в проекте TellyMate, который заключается в переключении ножки вывода видео в высокоимпедансное состояние, когда нужно, и таким образом повторять последний бит в выводимом байте. Эта часть функции очень критична по времени и отказ от ассемблера привел к необходимости
inline void DrawString (unsigned char *str_buffer[], struct FONT_INFO *font, unsigned char str_symbols)
{
unsigned char symbol_width;
unsigned char i;
unsigned char * _ptr;
unsigned char * _ptr1;
y_line_render++;
//Set pointer for render line (display buffer)
_ptr = &str_buffer[row_render * str_symbols];
unsigned char j;
register unsigned char _S;
unsigned char _S1;
//Cycle for render line
i = str_symbols;
while(i--)
{
symbol_width = font->width[(* _ptr)];
//Set pointer for render line (character generator)
_ptr1 = &font->bitmap[font->offset[* _ptr]+y_line_render*symbol_width];
_S1 = 0; //предыдущий байт
_S = pgm_read_byte(_ptr1); //текущий байт
_ptr1++;
j=symbol_width; //вывод одного символа
while (1)
{
if (_S1 & 0b1)
{
goto matr;
}
VIDEO_OFF;
matr: NOP;
SPDR = _S;
VIDEO_ON;
_S1 = _S;
_S = pgm_read_byte(_ptr1++);
NOP;
NOP;
if (!--j) break;
}
_ptr++;
VIDEO_OFF;
}
}
После того, как изображение было получено, стало ясно, что ни о каком приеме и разборе ИК-посылок с пульта не может идти речи, просто не хватит скорости, поэтому оставил прием команд по UART. Приемом ИК займется другой микроконтроллер.
Также добавил второй буфер, который нужен для отображения часов. Соответственно, шрифтов будет тоже два. Структура файла шрифта состоит из собственно, битмапов символов, константы высоты шрифта и массивов смещений каждого символа и ширины каждого символа.
Также имеется структура, описывающая шрифт, для более простого доступа из программы.
// Character bitmaps for Digital-7 Mono 120pt
const unsigned char PROGMEM Digital7_Bitmaps[] =
{
// @0 '0' (71 pixels wide)
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x80, // ############################################# #
0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0xE0, // ############################################### ###
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF0, // ############################################### #####
0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xF8, // ################################################ ######
...
...
}
const unsigned char Digital7_Height = 105;
const unsigned char Digital7_Width[] =
{
9, /* 0 */
9, /* 1 */
9, /* 2 */
9, /* 3 */
9, /* 4 */
9, /* 5 */
9, /* 6 */
9, /* 7 */
9, /* 8 */
9, /* 9 */
3 /* : */
};
const unsigned int Digital7_Offset[] =
{
0 , /* 0 */
945, /* 1 */
1890, /* 2 */
2835, /* 3 */
3780, /* 4 */
4725, /* 5 */
5670, /* 6 */
6615, /* 7 */
7560, /* 8 */
8505, /* 9 */
9450 /* : */
};
Шрифты генерировал программой DotFactory.
Во время невидимой части кадра делается ход часов и таймера, а также реакция на команды, полученные по UART.
unsigned char clock_left;
bool clock_set;
volatile unsigned char MinTens, MinOnes;
volatile unsigned char SecTens, SecOnes;
static void pal_terminal_handle(void)
{
unsigned char received_symbol = 0;
// Parser received symbols from UART
while(UCSR0A & (1<<RXC0))
{
received_symbol = UDR0;
if (received_symbol=='#')
{
clock_left=5;
clock_set = true;
}
if ((received_symbol>0x2F)&&(received_symbol<0x3A))
{
if (clock_set)
{
time_array[5-clock_left] = received_symbol - 0x30;
clock_left--;
if (clock_left==3)
{
clock_left--;
}
if (clock_left==0)
{
time_array[6] = 0;
time_array[7] = 0;
clock_set = false;
}
}
else
{
if ((pause==0)||_Stop)
{
MinTens = 0;
}
else
{
MinTens = MinOnes;
}
MinOnes = received_symbol - 0x30;
SecTens = 0;
SecOnes = 0;
pause = 4;
_Stop = false;
str_array[0] = MinTens;
str_array[1] = MinOnes;
str_array[2] = 0x0A;
str_array[3] = SecTens;
str_array[4] = SecOnes;
}
//time_array[] = {1, 2, 10, 5, 5};
}
}
}
volatile bool _Stop;
struct FONT_INFO
{
unsigned char height;
unsigned char * bitmap;
unsigned int * offset;
unsigned char * width;
} Digital7, comdot;
int main(void)
{
avr_init();
//fonts
Digital7.bitmap = &Digital7_Bitmaps;
Digital7.height = Digital7_Height;
Digital7.offset = &Digital7_Offset;
Digital7.width = &Digital7_Width;
comdot.bitmap = &comdotshadow_Bitmaps;
comdot.height = comdotshadow_Height;
comdot.offset = &comdotshadow_Offset;
comdot.width = &comdotshadow_Width;
MinTens = 0;
MinOnes = 0;
SecTens = 0;
SecOnes = 0;
str_array[0] = MinTens;
str_array[1] = MinOnes;
str_array[2] = 0x0A;
str_array[3] = SecTens;
str_array[4] = SecOnes;
unsigned char *semicolon = &time_array[2];
sei();
while (1)
{
sleep_mode();
MakeSync();
if (UCSR0A & (1<<RXC0))
{
//Parse received symbol
pal_terminal_handle();
//Can easealy add here RX polling buffer
//to avoid display flickering
continue;
}
//Check visible field
if(video_enable_flg)
{
linecount++;
//OK, visible
//Main render routine
#define firstline 36
#define secondline 200
//To make horizontal shift rendered image
unsigned char k;
for (k=horiz_shift_delay; k>0; k--)
{
NOP;
}
if ((linecount == firstline)||(linecount == secondline))
{
row_render = 0;
y_line_render = 0;
}
if ((linecount> firstline) && (linecount< firstline+(Digital7.height)))
{
DrawString(&str_array, &Digital7, 5);
}
if ((linecount> secondline) && (linecount< secondline+(comdot.height)))
{
DrawString(&time_array, &comdot, 5);
}
}
else
{
//Not visible
//Can do something else..
//You can add here your own handlers..
// VIDEO_OFF;
if (framecount==PAL_FPS)
{
framecount=0;
//=========================================
if (*semicolon== 11)
{
*semicolon=10;
}
else
{
*semicolon=11;
}
if (++time_array[7] == 10)
{
framecount = 1;// коррекция секунд
time_array[7]=0;
if (++time_array[6]==6)
{
framecount = 3; // коррекция секунд
time_array[6]=0;
if (++time_array[4]==10)
{
time_array[4]=0;
if (++time_array[3]==6)
{
time_array[3]=0;
if ((++time_array[1]==4) && (time_array[0]==2))
{
time_array[0]=0;
time_array[1]=0;
}
if (time_array[1]== 9)
{
time_array[1]=0;
time_array[0]++;
}
}
}
}
}
//=========================================
if ((pause==0)&&(_Stop==false))
{
if ((SecOnes--)==0)
{
SecOnes=9;
if ((SecTens--) == 0)
{
SecTens = 5;
if ((MinOnes--) == 0)
{
MinOnes = 9;
if (MinTens == 0)
{
_Stop = true;
}
else
{
MinTens--;
}
}
}
}
if (!_Stop)
{
str_array[0] = MinTens;
str_array[1] = MinOnes;
str_array[2] = 0x0A;
str_array[3] = SecTens;
str_array[4] = SecOnes;
}
}
else
{
pause--;
}
}
}
}
}
В качестве контроллера, декодирующего ИК-пульт и отправляющего команды по UART, я взял ATTiny45. Поскольку у него нет аппаратного UART, на просторах интернета была найдена очень компактная функция программного UART, работающего только на отправку, а также простая функция чтения команд с пульта (без декодирования).
Все это было быстренько собрано в кучу и откомпилировано. Коды кнопок пульта жестко прошиты в коде. Дополнительно сделал мигание светодиода при приеме команды.
* Tiny85_UART.c
*
* Created: 19.04.2016 21:22:52
* Author: Antonio
*/
#include <avr/io.h>
#include «dbg_putchar.h»
#include <avr/interrupt.h>
//#include <stdlib.h>
#include <stdbool.h>
// пороговое значение для сравнения длинн импульсов и пауз
static const char IrPulseThershold = 9;// 1024/8000 * 9 = 1.152 msec
// определяет таймаут приема посылки
// и ограничивает максимальную длину импульса и паузы
static const uint8_t TimerReloadValue = 100;
static const uint8_t TimerClock = (1 << CS02) | (1 << CS00);// 8 MHz / 1024
volatile unsigned char blink = 0;
#define blink_delay 3;
volatile struct ir_t
{
// флаг начала приема полылки
uint8_t rx_started;
// принятый код
uint32_t code,
// буфер приёма
rx_buffer;
} ir;
static void ir_start_timer()
{
TCNT0 = 0;
TCCR0B = TimerClock;
}
// когда таймер переполнится, считаем, что посылка принята
// копируем принятый код из буфера
// сбрасываем флаги и останавливаем таймер
ISR(TIMER0_OVF_vect)
{
ir.code = ir.rx_buffer;
ir.rx_buffer = 0;
ir.rx_started = 0;
if(ir.code == 0)
TCCR0B = 0;
TCNT0 = TimerReloadValue;
}
ISR(TIMER1_OVF_vect)
{
if (blink==0)
{
OCR1B = 0;
}
else
{
OCR1B = 200;
blink--;
}
}
// внешнее прерывание по фронту и спаду
ISR(INT0_vect)
{
uint8_t delta;
if(ir.rx_started)
{
// если длительность импульса/паузы больше пороговой
// сдвигаем в буфер единицу иначе ноль.
delta = TCNT0 — TimerReloadValue;
ir.rx_buffer <<= 1;
if(delta > IrPulseThershold) ir.rx_buffer |= 1;
}
else{
ir.rx_started = 1;
ir_start_timer();
}
TCNT0 = TimerReloadValue;
}
void dbg_puts(char *s)
{
while(*s) dbg_putchar(*s++);
}
int main(void)
{
GIMSK |= _BV(INT0);
MCUCR |= (1 << ISC00) | (0 <<ISC01);
TIMSK = (1 << TOIE0)|(1<<TOIE1);
ir_start_timer();
dbg_tx_init();
DDRB|=_BV(PB4);
TCCR1 |= (1<<CS13)|(1<<CS12)|(0<<CS11)|(0<<CS10);
GTCCR |= (1<<COM1B1)|(0<<COM1B0)|(1<<PWM1B);
OCR1C = 255;
OCR1B = 0;
blink=0;
sei();
//dbg_puts(&HelloWorld);
while (1)
{
// если ir.code не ноль, значит мы приняли новую комманду
if(ir.code)
{
// конвертируем код в строку
//ultoa(ir.code, buf, 16);
// dbg_puts(buf); //и выводим в порт
//==================================================================
switch (ir.code)
{
case 0x2880822a: blink=blink_delay; dbg_putchar('1'); break;
case 0x8280282a: blink=blink_delay; dbg_putchar('2'); break;
case 0x8a0020aa: blink=blink_delay; dbg_putchar('3'); break;
case 0x0a00a0aa: blink=blink_delay; dbg_putchar('4'); break;
case 0x0280a82a: blink=blink_delay; dbg_putchar('5'); break;
case 0x2a888022: blink=blink_delay; dbg_putchar('6'); break;
case 0x0200a8aa: blink=blink_delay; dbg_putchar('7'); break;
case 0x0a80a02a: blink=blink_delay; dbg_putchar('8'); break;
case 0x22888822: blink=blink_delay; dbg_putchar('9'); break;
case 0x20888a22: blink=blink_delay; dbg_putchar('0'); break;
case 0x0008aaa2: blink=blink_delay; dbg_putchar('O'); break;
case 0x280882a2: blink=blink_delay; dbg_putchar('U'); break;
case 0x8880222a: blink=blink_delay; dbg_putchar('D'); break;
case 0x0808a2a2: blink=blink_delay; dbg_putchar('L'); break;
case 0xa0080aa2: blink=blink_delay; dbg_putchar('R'); break;
case 0x20088aa2: blink=blink_delay; dbg_putchar('*'); break;
case 0x220888a2: blink=blink_delay; dbg_putchar('#'); break;
default: break;
}
ir.code = 0;
//===================================================================
}
}
}
Итоговая схема получилась такая:
Первую версию собрал на макетной плате с использованием кусков оргстекла в качестве корпуса.
Блок питания купил самый простой на 12В 500мА в местном магазине.
Пультик заказывал на ebay.
Вот результат:
Таймер используется для информирования говорящего с кафедры об отведенном времени.
В планах — переделать на stm32, уместить в один контроллер, оформить в корпус покрасивее.
Спасибо за внимание.
Комментарии (21)
lorc
24.05.2016 18:33Я когда-то делал нечто похожее на AT91SAM7S, только для VGA. Хорошо что там был DMA и более вменяемый SPI.
Но мне пришлось выделить под frame buffer бОльшую часть доступной RAM, потому что в frame buffer пришлось хранить не только само изображение, но и невидимые части (все четыре porch'а). Только таким образом удалось избежать срывов синхронизации.antonluba
24.05.2016 19:04Простым решением было бы переписать вывод видео на STM8. Он по функциям почти аналогичен, но SPI непрерывный. Я даже получил лицензию на компилятор COSMIC.
Но потом понял, что лучше сразу сделать на STM32. На данный момент сделал вывод синхросигнала и немного исследовал SPI, DMA и контроллер прерываний.
Выглядит так, что тайминги можно выдерживать с помощью таймера синхронизации (с его 4 каналами), один канал вырабатывает сами синхроимпульсы, второй — настроен на backporch и пинает DMA на вывод строки. Или запускает второй таймер, который, в свою очередь, несколько раз пинает DMA c периодом, равным ширине каждого символа в строке.
В деталях еще не продумал, периферия очень мощная, изучаю потихоньку.lorc
24.05.2016 19:28Да, чем хороши STM32, так это развитой периферией. Она там намного лучше чем в тех же AT91SAM7x, и уж явно намного круче AVRок. И это при сопоставимой с AVRками цене.
Думаю, на STM32 выйдет реализовать вменяемый frame buffer. А если бы там было три SPI или один quad SPI, то можно было бы сделать даже трехбитный цвет.antonluba
24.05.2016 19:47Цвет я бы сделал 8-битный, внешним кодеком, даже заказал пару микросхем.
Можно так же через DMA выводить байты в порт, к которому подключен простой R2R ЦАП.
Только для хранения цветного изображения нужно гораздо больший объем флеша, тут бы сжатие не помешало какое-нибудь быстрое.
Другой вариант — сделать мультиплексор и подавать черно-белый сигнал на разные входы кодера цвета.
Только в практическом устройстве это вряд ли нужно.
antonluba
24.05.2016 23:29И кстати, frame buffer как таковой в этом таймере отсутствует.
Есть буфер, где хранятся значения символов — 5 байт верхней строки и 8 — нижней (только второе двоеточие и секунды не отображаются, они нужны для хода часов).
Для верхней строки символы могут принимать значения цифр от 0 до 9 и двоеточия. Для нижней строки добавляется пустое двоеточие, то есть символ такого же размера, как двоеточие, но пустой, для гашения двоеточия. Битмапы этих символов хранятся во флеше (см. описание шрифтов).
На каждом цикле вывода видео-строки есть вложенный цикл вывода линии текущего символа. В этом цикле по цифре в буфере и номеру линии (y-координата символа) вычисляется начальный адрес, с которого начинается вывод, а дальше просто в SPI кидаются байты, пока вся ширина символа не выведется.
Таким образом расход ОЗУ вообще минимален, а флеш забит огромным шрифтом.
andrewsh
24.05.2016 19:01Совет: используйте GIMP и формат XBM для графики. Очень помогает (я делал похожий проект на AVR, потом на STM32).
mmMike
25.05.2016 07:03Ностальгия…
Как то подобное и на рассыпухе (обычная K155 серии «логика» + ПЗУ) один мой знакомый делал. Вот это был мазохизм.
Еще один кусочек который можно добавить — это передатчик. Хоть на одном транзисторе (на метровый диапазон).
Тогда даже кабеля к телевизору не надо. В радиусе 10-20 метров нормально сигнал ловится.
Armleo
25.05.2016 14:39Рекомендую еще попробовать сделать то же самое на FPGA. Опыта и удовольствия получите в разы больше. Кстати, я тоже делал похожую штуку только под VGA. Внешний ПЛИС генерировал синхроимпульсы, и выводил данные с SRAM чипа, а МК на порчах записовал.
antonluba
25.05.2016 19:38Обязательно попробую. Я даже заказал пару микросхем sram по 128 кБайт каждая, флешек от биосов у меня и так полно, есть кодеры цвета, есть макетка на Altera MAX2 и Циклоне 1.
Хочу еще с какой-нибудь AVR с интерфейсом внешней памяти поиграться.
Времени только мало.Armleo
26.05.2016 16:11Можно информацию о SRAM чипе? А что за макетки Altera MAX II и Cyclone I? самодельные?
«Хочу еще с какой-нибудь AVR с интерфейсом внешней памяти поиграться.»
Не совсем понял. Что вы имеете ввиду? чипы памяти подключить к AVR или с интерфесом внутри AVR поиграться? (видимо пора спать).antonluba
26.05.2016 20:35Память взял первую попавшуюся.
Макетка MAX2
Макетки с циклоном такие, как у меня, уже не продаются. Вот здесь описание.
К интерфейсу внешней памяти (а это шина адреса и шина данных) хочу подключить плис и сделать какое-нибудь внешнее устройство. Тот же видеоадаптер или вывод звука.Armleo
28.05.2016 09:43Насколько мне известно в AVR8 нет апаратной шины адреса или данных.
Лучше сделать все в плисе. Ядро аврок есть, возможно придется доделать, но они рабочие. С внутренним ядром можно всякое творить, например много много pwm или вывести его вообще.
Чип от итачи не рекомендую поскольку они не дадут вывести тру 640 на 480 при 60 фпс 8 бит цвет. У них скорость ~14MHz, а с кешом это 14МегаБайт в секунду а надо 18 МегаБайт с кешом или 25 МегаБайт без кеша. Можно конечно уменшить разрешение, скажем 320 на 240 с 60 фпс, но это не ахти.
В MAX II авр не поместить, но VGA поместится.
Поидее можно вывести видеопамять на внутренний авр. Только там 64кб макс, а с перефирией 60кб максимум.Armleo
28.05.2016 09:56Эх забыл сказать. У чипов итачи ножки 5V и питание соответсвенно. А у плис только 3.3V. Будут проблемы с уровнями.
impetus
И, хотя сама работа отличная и полезная, и автору респект, как за изделие, так и за нормальное его описание — определить теперь где хабр где ГТ уже само НЛО наверное не в состоянии
safari2012
думаю как-то так:
arduino — geektimes
arv (чистый) — habr
abrakada
Думаю что нет, на ардуино полно весьма приличных проектов и решений. Прошли те времена, когда только светодиодом там мигали.
custos
Тем не менее, раздел DIY на Geektimes, и не понятно что делать, когда DIY касается микроконтроллера, поскольку хаб о его программировании на Habrahabr. Кто бы разъяснил...
Joric
Да сразу было понятно, что разделение это абсолютно бессмысленное (аудитория там одна и та же), недавняя смерть «мегамозга» это только потдвердила. Я думаю, скоро гигтаймс сольется с хабром (или наоборот, пофигу). Имхо, НЛО — дура!
Janom
Ну, тут неоднозначно… Я рад, что всякий мусор, который ежедневно, тоннами сваливают всем известные персонажи, стало проще фильтровать, персонажи переехали на гт, а сюда все больше в гости заходят. И в тоже время, как-то не хватает нормального DIY (не диодом конечно мигать, но просто прикольные железки с достойным описанием как она работает, будь-то хоть сколько оригинально, а не перепост любой ссылки с первой страницы гугла, по правильному запросу) и науч-поп хабов, типа Космонавтика…
Я бы оставил как было изначально — все на хабре, но дал пользователям возможность настроить фильтр ленты как ему удобно.