/* tutorial TinyAVR 1-Series * UART 2 - příjem řetězců s argumentem * PA1 - Tx, PA2 - Rx * PB5 - LEDka (zapojená proti VCC) * (pozor na chyby baudrate mEDBG) */ /* Ve fuses zvolen 20MHz oscilátor (lze volit ještě 16MHz) */ #define F_CPU 20000000UL #include #include #include // kvůli printf_P a scanf #include // kvůli strncpy, strncmp #include // kvůli makru PSTR (v printf_P) #include #define MAX_STRLEN 16 #define BAUDRATE 38400UL // aktuální baudrate (maximum baudrate F_CPU/16, resp. F_CPU/8 s CLK2X) #define LED_ON PORTB.OUTCLR = PIN5_bm // rozsviť LED na PB5 (připojená proti VCC) #define LED_OFF PORTB.OUTSET = PIN5_bm // zhasni LED na PB5 (připojená proti VCC) // Makro na výpočet hodnoty BAUD registeru (BAUDVAL) #if BAUDRATE < F_CPU/16 // pokud není baudrate větší než maximum #if (((10*4*F_CPU)/BAUDRATE) % 10) > 4 // zaokrouhlování (round) #define BAUDVAL (((4*F_CPU)/BAUDRATE)+1) // zaokrouhlíme nahoru (round up) #else #define BAUDVAL ((4*F_CPU)/BAUDRATE) // zaokrouhlíme dolů (floor) #endif #else #warning "Baudrate is out of range ! (max baudrate without CLK2X is F_CPU/16 (1.25Mb/s if F_CPU=20MHz)" #define BAUDVAL 0 #endif // fce k odeslání jednoho znaku po UARTu ve formátu nutném pro přesměrování printf na UART int usart_putchar(char var, FILE *stream); void clock_20MHz(void); void usart_init(void); // kouzelná formule, pro přesměrování printf na UART static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE); uint16_t tmp; // pomocná proměnná (aby bylo co posílat) volatile char command[MAX_STRLEN]; // řetězec s příkazem volatile uint8_t num_commands=0; // příznak že dorazila celá zpráva int main(void){ clock_20MHz(); // taktujeme na 20MHz PORTB.DIRSET = PIN5_bm; // PB5 - výstup (LEDka) usart_init(); // konfigurace UARTu stdout = &mystdout; // přesměrujeme Printf na UART sei(); // globální povolení přerušení while (1){ if(num_commands){ // dorazil nový příkaz ? // poslal uživatel příkaz "zapni" ? if(strncmp(command,"zapni",MAX_STRLEN)==0){ LED_ON; // zapni LED a odpověz printf_P(PSTR("LED zapnuta\n\r")); } // poslal uživatel příkaz "vypni" ? else if(strncmp(command,"vypni",MAX_STRLEN)==0){ LED_OFF; // zhasni LED a odpověz printf_P(PSTR("LED vypnuta\n\r")); } // poslal uživatel číslo ? else if(sscanf_P(command,PSTR("x=%u"),&tmp) > 0){ // odpověz a předveď že je to opravdu číslo printf_P(PSTR("x=%u, 2x=%u\n\r"),tmp, 2*tmp); } // jiný příkaz... else{ printf_P(PSTR("nerozumim\n\r")); } num_commands=0; // příkaz zpracován, čekáme na nový } } } ISR(USART0_RXC_vect){ static char rx_string[MAX_STRLEN]; // zde dočasně ukládáme přicházející znaky static uint8_t cnt = 0; // počítadlo přijatých znaků char znak; // pomocná proměnná //err = USART0.RXDATAH; // případná kontrola chyb znak = USART0.RXDATAL; // vyčteme přijatý znak // není to "konec příkazu" a máme ještě kam data ukládat ? ... if((znak!='\r') && (znak!='\n') && (cnt < (MAX_STRLEN-1))){ rx_string[cnt] = znak; // ulož znak do pole cnt++; // inkrementuj počítadlo znaků } // ... jinak je příkaz celý a je čas ho zpracovat else{ if(cnt>0){ // pokud není příkaz prázdný memcpy(command,rx_string, cnt); // zkopírujeme řetězec do pole "command" command[cnt]= '\0'; // připojíme na konec ukončovací znak 0 num_commands++; // dáme vědět do "main" že je tu nový kompletní příkaz } cnt = 0; // nulujeme počítadlo znaků (abychom mohli přijímat další příkazy) } } // odešle jeden znak (formát pro "printf" implementaci) int usart_putchar(char var, FILE *stream){ while(!(USART0.STATUS & USART_DREIF_bm)){} // počkat než bude místo v odesílacím bufferu USART0.TXDATAL = var; // naložit data k odeslání return 0 ; } void usart_init(void){ PORTA.OUTSET = PIN1_bm; // log.1 PA1 (Tx) - neutrální hodnota na UARTu PORTA.DIRSET = PIN1_bm; // PA1 (Tx) - je výstup PORTA.DIRCLR = PIN2_bm; // PA2 (Rx) - je vstup PORTMUX.CTRLB = PORTMUX_USART0_bm; // Remapujeme USART (Tx na PA1 a Rx na PA2) USART0.BAUD = BAUDVAL; // nastavit baudrate USART0.CTRLB |= USART_TXEN_bm | USART_RXEN_bm; // zapnout přijímač a vysílač USART0.CTRLA |= USART_RXCIE_bm; // zapnout přerušení od Rx } // Nastaví clock 20MHz (z interního 20MHz bez děličky) void clock_20MHz(void){ // v případě potřeby zde dočasně vypněte přerušení CCP = CCP_IOREG_gc; // odemyká zápis do chráněného registru CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSC20M_gc; // vybírá 20MHz oscilátor CCP = CCP_IOREG_gc; // odemyká zápis do chráněného registru CLKCTRL.MCLKCTRLB = 0; // vypne prescaler (děličku) }