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

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



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


  • 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.


Внешний вид отладочной платы. Программатор на борту, подключение через разъем 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:






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

Комментарии (5)


  1. tnt23
    26.12.2015 10:41

    Старая добрая идея использовать ESC-последовательности для отрисовки сложных оконных интерфейсов, в свое время массово использовалась на BBS.

    А вот пример реализации тетриса на GSM модеме :)

    www.compel.ru/lib/ne/2007/19/9-primer-postroeniya-polzovatelskogo-interfeysa-v-prilozheniyah-open-at


  1. den1s1
    26.12.2015 11:28

    что-то по ссылке кроме мимолетного упоминания, о самом тетрисе ни слова. только о настройке режима увода модема.


    1. tnt23
      27.12.2015 13:34

      Там даже куски кода есть. (Что такое увод модема, не понял я?)

      Если же вы хотели получить разжёванную реализацию Тетриса, то это упражнение для студентов первого курса по специальности «Программирование на языке BASIC»


      1. marus-ka
        28.12.2015 10:33

        В приведенной вами по ссылке статье нет ни одного куска кода для тетриса. Мы не хотели получить разжеванную реализацию тетриса. В статье мы показали, что очень легко перейти с 8-битной меги на 32-битный samd10 с cortex-m0+.


        1. tnt23
          28.12.2015 11:23

          Что статья о легкости перехода с 8-битной меги на 32-битный кортекс, я понял с первого раза, прочитав статью. Замечу, что статья по ссылке также не о том, как написать собственно тетрис, а об использовании последовательного порта и ESC-последовательностей.