В этом году компания 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 по приему символа.
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.
Стоимость отладочной платы ATSAMD10-XMINI составляет 450 рублей.
ссылка на оригинал статьи http://geektimes.ru/post/267840/
Добавить комментарий