Tetris on a microcontroller in Tera Term

    This year, Atmel announced a line of “younger” M0 + cortexes of the SAM D09, SAM D10, SAM D11 family. These are not very "sophisticated" controllers have a low price and small enclosures. Moreover, the lineup contains stones in easily soldered SOIC-14 and SOIC-20 cases. To get acquainted with the capabilities of the controller, very cheap debugs from the Xplained mini series are available, which are compatible with shields from Arduino. These features may be of interest not only among professional developers, but also among amateur radio enthusiasts.

    When the debugging fell into our hands, I wanted to do something funny and creative instead of the “serious” demonstration task in honor of the approaching New Year. We scrapped through the guts and found an old projection - Tetris on MEGA168 through the terminal and decided to port it to a new stone and introduce it to the public. There is no practical sense in this, which is called Just for fun. To whom details are interesting, I ask under cat.



    Briefly about the new microcontrollers


    • SAM D09 is the youngest member of the SAM D family. It has an 8K or 16K flash and 4K SRAM. Housing options QFN-24 and SOIC-14. Onboard DMA and Event system. 2 SERCOM - universal communication modules that can be configured as USART, SPI or I2C. 5 or 10 channel 12 bit ADC.
    • SAM D10 - D09 upgrade in terms of adding additional timers, an analog comparator, a DAC and a touch button controller, as well as an additional SERCOM for some modifications. Housing options QFN-24, SOIC-14, SOIC-20.
    • SAM D11 is the same D10, but with the addition of a Full-Speed ​​USB Device.


    The appearance of the debug board. On-board programmer, connect via Micro USB connector.

    Now about Tetris himself


    The work of Tetris is based on several basic principles:
    • communication with the terminal is carried out according to the VT100 protocol,
    • the picture is updated by timer,
    • any figure fits into a square of certain sizes (4 by 4 characters).

    Tetris uses three commands from the VT100 protocol : clear the screen, move the cursor to the beginning and make the cursor invisible.
    To work on this protocol, you can use the Tera term terminal, for example.
    For control, 5 keyboard letters are used:
    • n - start a new game,
    • w or space - rotate a shape,
    • s - drop the figure,
    • d - move to the right,
    • a - move left.

    In the code, you can easily reassign control keys to other
    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();
    }	
    



    The game speed is set by a timer. For more experienced players, you can set the “tick” faster, then the pieces will fall faster.

    Of course, points are counted: for each line that disappears, 100 points are added. For each subsequent “disappeared” simultaneously with the first, two times more points are added than for the previous one.

    Porting from mega to samd10


    From the periphery of the controller, we need SERCOM in UART mode for directly transmitting figures and pictures, and a timer for counting the time for updating the picture.

    Instead of any programmer's 8-bit controllers, dear to the heart of any programmer, setting UART bits in registers:
    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);
    }
    

    we configure sercom to work in uart mode, not forgetting to allow interrupts and callback for receiving a character.
    Sercom configuration in uart mode
    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);
    }
    



    In the source code for mega, data on uart was received using putc, for samd10 we’ll make it simpler: let just by interrupt every received byte fall into a certain variable. This solution does not claim to be correct and safe, it is for ease of transition and accelerate it.
    In detail about how to defeat sometimes too “smart” ASF for receiving one byte by interrupt, we wrote in our article on we.easyelectronics.ru.

    Let's move on to the timers.
    Code for mega:
    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;
    }
    


    And the corresponding code for 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;
    }
    


    That's all. The rest of the code for handling the movement of the figures and the rest of the logic remains the same.
    The entire project for samd 10 lies on github .

    Settings for Tera Term:






    The cost of the ATSAMD10-XMINI debug board is 450 rubles.

    Also popular now: