Тетрис на микроконтроллере в Tera Term

в 12:04, , рубрики: arduino, atmel, cortex-m0, samd10, tetris, Блог компании Rainbow, программирование микроконтроллеров, Электроника для начинающих, метки:

В этом году компания Atmel анонсировала линейку «младших» кортексов М0+ семейства SAM D09, SAM D10, SAM D11. Эти не сильно «навороченные» контроллеры имеют низкую цену и небольшие корпуса. Причем в линейке присутствуют камни в легкопаяемых корпусах SOIC-14 и SOIC-20. Для ознакомления с возможностями контроллера доступны очень дешевые отладки из серии Xplained mini, которые совместимы с шилдами от Arduino. Эти особенности, возможно, вызовут интерес не только среди профессиональных разработчиков, но и у радиолюбителей.

Когда отладки попали к нам в руки, захотелось вместо «серьёзной» демонстрационной задачи в честь приближающегося Нового года сделать что-нибудь забавное и креативное. Мы поскребли по сусекам и нашли старенький проектик — тетрис на MEGA168 через терминалку и решили портировать его на новый камень и представить общественности. Практического смысла в этом никакого, что называется Just for fun. Кому интересны подробности, прошу под кат.

Тетрис на микроконтроллере в Tera Term - 1

Кратко о новых микроконтроллерах

  • SAM D09 — младший представитель семейства SAM D. Имеет 8К или 16К флеша и 4К SRAM. Варианты корпусов QFN-24 и SOIC-14. На борту DMA и Event system. 2 SERCOM — универсальных коммуникационных модулей, которые могут конфигурироваться как USART, SPI или I2C. 5-ти или 10-ти канальный 12-ти битный АЦП.
  • SAM D10 — апгрейд D09 в части добавления дополнительных таймеров, аналогового компаратора, ЦАП и контроллера сенсорных кнопок, а так же дополнительного SERCOM для некоторых модификаций. Варианты корпусов QFN-24, SOIC-14, SOIC-20.
  • SAM D11 — тот же D10, но с добавлением Full-Speed USB Device.

Тетрис на микроконтроллере в Tera Term - 2
Внешний вид отладочной платы. Программатор на борту, подключение через разъем Micro USB.

Теперь про сам тетрис

Работа тетриса основана на нескольких базовых принципах:

  • общение с терминалкой осуществляется по протоколу VT100,
  • обновление картинки происходит по таймеру,
  • любая фигура вписывается в квадрат определенных размеров (4 на 4 символа).

Тетрис использует три команды из протокола VT100: очистка экрана, перемещение курсора в начало и сделать курсор невидимым.
Для работы по этому протоколу можно использовать терминалку Tera term, например.
Для управления используются 5 клавиш-букв клавиатуры:

  • n – начать новую игру,
  • w или space – повернуть фигуру,
  • s – уронить фигуру,
  • d – переместить вправо,
  • a – переместить влево.

В коде можно легко переназначить клавиши управления на другие
switch (c) 
{
	case 'w':
	case ' ':
	//ROTATE
	 tetris_rotate();
	break;
	case 's':
	//DOWN
	tetris_gravity();	
	break;
	case 'd':
	 //RIGHT
	 tetris_move_right();
	break;
	case 'a':
	 //LEFT
	tetris_move_left();
	break;
	default: break;
}

if (c == 'n') 
{
	c=0;
	//Seed random function so we do not get same start condition
	//for each new game. In essence we will not start a new game
	//exactly at the same time.
	srand(tick);
					
	//New Game
	is_running = true;
	terminal_cursor_off();
	terminal_clear();
	tetris_init();
	tetris_new_block();
	terminal_cursor_home();
	tetris_print();
}	

Скорость игры устанавливается таймером. Для более опытных игроков можно задать «тиканье» быстрее, тогда и фигуры будут падать быстрее.

Конечно же, подсчитываются очки: за каждую исчезнувшую строку добавляется 100 очков. За каждую следующую «исчезнувшую» одновременно с первой, добавляется в два раза больше очков, чем за предыдущую.

Портируем с mega на samd10

Из периферии контролера нам нужен SERCOM в режиме UART для непосредственной передачи фигурок и картинки, и таймер для отсчета времени обновления картинки.

Вместо милой сердцу любого программиста 8-битных контроллеров настройки UART битами в регистрах:

static void board_init(void)
{	
	/*Configure IO pins:
	 * - UART pins
	 * - SW pin
	 * - LED pin
	 */
	DDRD &= ~USART_RX_PIN_bm;
	DDRD |= USART_TX_PIN_bm;
	PORTD |= USART_TX_PIN_bm;	
	PORTB |= SW_PIN_bm;
	DDRB &= ~SW_PIN_bm;
	/*Disable all modules we will not use*/
	PRR = (1 << PRTWI) | (1 << PRTIM2) | (1 << PRTIM0) | (1 << PRSPI) | (1 << PRADC);
}

конфигурируем sercom для работы в режиме uart, не забывая разрешить прерывания и callback по приему символа.

Конфигурация Sercom в режиме uart
static void configure_console(void)
{
	struct usart_config usart_conf;

	usart_get_config_defaults(&usart_conf);
	usart_conf.mux_setting = CONF_STDIO_MUX_SETTING;
	usart_conf.pinmux_pad0 = CONF_STDIO_PINMUX_PAD0;
	usart_conf.pinmux_pad1 = CONF_STDIO_PINMUX_PAD1;
	usart_conf.pinmux_pad2 = CONF_STDIO_PINMUX_PAD2;
	usart_conf.pinmux_pad3 = CONF_STDIO_PINMUX_PAD3;
	usart_conf.baudrate    = CONF_STDIO_BAUDRATE;

	stdio_serial_init(&cdc_uart_module, CONF_STDIO_USART_MODULE, &usart_conf);
}

enum status_code usart_enable_rx_interrupt(	struct usart_module *const module,	uint8_t *rx_data)
{
	// Sanity check arguments
	Assert(module);
	Assert(rx_data);

	// Issue internal asynchronous read
	// Get a pointer to the hardware module instance
	SercomUsart *const usart_hw = &(module->hw->USART);
	module->rx_buffer_ptr = rx_data;
	// Enable the RX Complete Interrupt
	usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC;
	return STATUS_OK;
}
void configure_usart_callbacks(void)
{
	usart_register_callback(&cdc_uart_module, USART_RX_callback, USART_CALLBACK_BUFFER_RECEIVED);
	usart_enable_callback(&cdc_uart_module, USART_CALLBACK_BUFFER_RECEIVED);
}

В исходном коде для меги данные по uart принимались с помощью putc, для samd10 сделаем проще: пусть просто по прерыванию каждый полученный байт сваливается в определенную переменную. Это решение не претендует на правильность и безопасность, оно для простоты перехода и ускорения его.
Подробно про то, как победить порой слишком «умную» ASF для приема одного байта по прерываниям, мы писали в нашей статье на сайте we.easyelectronics.ru.

Перейдем к таймерам.
Код для меги:

void init_timer(void)
{
	/*Start timer used to iterate game and seed random function*/
	TIFR1 = 1 << OCF1A;
	TIMSK1 = 1 << OCIE1A;
	OCR1A = TIMER_TOP_VALUE;
	TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
}
ISR(TIMER1_COMPA_vect, ISR_BLOCK)
{
	++tick;
	iterate_game = true;
}

И соответствующий код для samd10

/** Configures  TC function with the  driver.
 */
static void configure_tc(void)
{
	struct tc_config config_tc;

	tc_get_config_defaults(&config_tc);
	config_tc.counter_size    = TC_COUNTER_SIZE_16BIT;
	config_tc.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ;
	config_tc.counter_16_bit.compare_capture_channel[0] = 2000; 
	config_tc.clock_prescaler=TC_CLOCK_PRESCALER_DIV1024;

	tc_init(&tc_instance, CONF_TC_INSTANCE, &config_tc);
	tc_enable(&tc_instance);
}

/** Registers TC callback function with the  driver.
 */
static void configure_tc_callbacks(void)
{
	tc_register_callback(&tc_instance,	tc_callback_to_counter,	TC_CALLBACK_CC_CHANNEL0);
	tc_enable_callback(&tc_instance, TC_CALLBACK_CC_CHANNEL0);
}

static void tc_callback_to_counter(	struct tc_module *const module_inst)
{
	++tick;
	iterate_game = true;
	
}

Вот и все. Весь остальной код для обработки движения фигур и всей остальной логики остается таким же.
Полностью проект для samd 10 лежит на github.

Настройки для Tera Term:

Тетрис на микроконтроллере в Tera Term - 3
Тетрис на микроконтроллере в Tera Term - 4
Тетрис на микроконтроллере в Tera Term - 5

Стоимость отладочной платы ATSAMD10-XMINI составляет 450 рублей.

Автор: Rainbow

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js