/* uart_i-asm.S */ /* This file is part of the AVR-uart_i. Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * \file uart_i-asm.S * \email bg@nerilex.org * \author Daniel Otte * \date 2009-07-24 * \license GPLv3 or later * \ingroup uart_i * \brief implementation of interrupt based uart */ #include #include "config.h" #include "avr-asm-macros.S" #include "uart_defs.h" #define XON_VALUE 0x11 #define XOFF_VALUE 0x13 #if UART0_I #ifndef UART0_PARATY # warning "UART0: using default paraty: 'none'" # define UART0_PARATY UART_PARATY_NONE #endif #ifndef UART0_STOPBITS # warning "UART0: using default ammount of stop bits: '1'" # define UART0_STOPBITS UART_STOPBITS_1 #endif #ifndef UART0_DATABITS # warning "UART0: using default ammount of data bits: '8'" # define UART0_DATABITS UART_DATABITS_8 #endif #ifdef UDR # define OLD_UART # ifdef UDR0 # error "can not decide which registernames to use, UDR and UDR0 are defined" # endif #endif #ifdef OLD_UART # define UCSR0A UCSRA # define UCSR0B UCSRB # define UCSR0C UCSRC # define UBRR0H UBRRH # define UBRR0L UBRRL # define UDR0 UDR # define TXEN0 TXEN # define RXEN0 RXEN # define UDRE0 UDRE # define RXC0 RXC # define TXB80 TXB8 # define RXB80 RXB8 # define U2X0 U2X # define UDRIE0 UDRIE # define RXCIE0 RXCIE #endif #ifdef USART0_RX_vect # define RX_ISR USART0_RX_vect #endif #ifdef USART_RXC_vect # define RX_ISR USART_RXC_vect #endif #ifdef USART0_UDRE_vect # define TX_ISR USART0_UDRE_vect #endif #ifdef USART_UDRE_vect # define TX_ISR USART_UDRE_vect #endif #define CBB_SIZE 10 #define UART0_CBB_RX_OFFSET 0 #define UART0_CBB_TX_OFFSET 10 #define CTX_BASE_SIZE (2*(CBB_SIZE)) #if UART0_HOOK # if UART0_SWFLOWCTRL # define UART0_CTX_SIZE (3+2+CTX_BASE_SIZE) # define UART0_HOOK_OFFSET 20 # define UART0_HOOKR_OFFSET 22 # define UART0_TXON_OFFSET 23 # define UART0_RXON_OFFSET 24 # else # define UART0_CTX_SIZE (3+0+CTX_BASE_SIZE) # define UART0_HOOK_OFFSET 20 # define UART0_HOOKR_OFFSET 22 # endif #else # if UART0_SWFLOWCTRL # define UART0_CTX_SIZE (0+2+CTX_BASE_SIZE) # define UART0_TXON_OFFSET 20 # define UART0_RXON_OFFSET 21 # else # define UART0_CTX_SIZE (0+0+CTX_BASE_SIZE) # endif #endif .section .bss .global uart0_rxbuffer uart0_rxbuffer: .fill UART0_RXBUFFER_SIZE, 1, 0 .global uart0_txbuffer uart0_txbuffer: .fill UART0_TXBUFFER_SIZE, 1, 0 .global uart0_ctx uart0_ctx: .fill UART0_CTX_SIZE, 1, 0 /******************************************************************************/ /* Baudrate calculation */ #ifdef BAUD #undef BAUD #endif #define BAUD UART0_BAUD_RATE #include "setbaud_asm.inc" .section .text /******************************************************************************/ /* * void uart0_init(void){ * circularbytebuffer_init2(UART0_RXBUFFER_SIZE, &(uart0_ctx.rxb), uart0_rxbuffer); * circularbytebuffer_init2(UART0_TXBUFFER_SIZE, &(uart0_ctx.txb), uart0_txbuffer); * #if UART0_HOOK * uart0_ctx.hook = NULL; * uart0_ctx.hook_running = 0; * #endif * #if UART0_SWFLOWCTRL * uart0_ctx.txon = 1; * uart0_ctx.rxon = 1; * #endif * #define BAUD UART0_BAUD_RATE * #include * UBRR0H = UBRRH_VALUE; * UBRR0L = UBRRL_VALUE; * #if USE_2X * UCSR0A |= _BV(U2X0); * #else * UCSR0A &= ~_BV(U2X0); * #endif * UCSR0C = (UART0_PARATY<<4)|(UART0_STOPBITS<<3)|((UART0_DATABITS&3)<<1); * UCSR0B = _BV(RXCIE0) | _BV(UDRIE0) | _BV(RXEN0) | _BV(TXEN0) ; / * enable TX and RX and interrupts * / * sei(); * } * */ .global uart0_init uart0_init: ldi r24, UART0_RXBUFFER_SIZE clr r25 ldi r22, lo8(uart0_ctx+UART0_CBB_RX_OFFSET) ldi r23, hi8(uart0_ctx+UART0_CBB_RX_OFFSET) ldi r20, lo8(uart0_rxbuffer) ldi r21, hi8(uart0_rxbuffer) rcall circularbytebuffer_init2 ldi r24, UART0_TXBUFFER_SIZE clr r25 ldi r22, lo8(uart0_ctx+UART0_CBB_TX_OFFSET) ldi r23, hi8(uart0_ctx+UART0_CBB_TX_OFFSET) ldi r20, lo8(uart0_txbuffer) ldi r21, hi8(uart0_txbuffer) rcall circularbytebuffer_init2 #if UART0_SWFLOWCTRL ldi r30, lo8(uart0_ctx) ldi r31, hi8(uart0_ctx) ldi r24, 1 std Z+UART0_TXON_OFFSET, r24 std Z+UART0_RXON_OFFSET, r24 #endif #if UART0_HOOK std Z+UART0_HOOK_OFFSET, r1 std Z+UART0_HOOK_OFFSET+1, r1 std Z+UART0_HOOKR_OFFSET, r1 #endif ldi r24, UBRRH_VALUE STORE_IO UBRR0H, r24 ldi r24, UBRRL_VALUE STORE_IO UBRR0L, r24 #if USE_2X SET_BIT_IO UCSR0A, U2X0, r24 #else CLEAR_BIT_IO UCSR0A, U2X0, r24 #endif ldi r24, (UART0_PARATY<<4)|(UART0_STOPBITS<<3)|((UART0_DATABITS&3)<<1) STORE_IO UCSR0C, r24 ldi r24, _BV(RXCIE0) | _BV(UDRIE0) | _BV(RXEN0) | _BV(TXEN0) STORE_IO UCSR0B, r24 sei ret /******************************************************************************/ /* * ISR(USART0_UDRE_vect){ * uint16_t x; * x = circularbytebuffer_get_fifo(&(uart0_ctx.txb)); * if(x==0xffff){ * / * the transmit buffer is empty, disable interrupt * / * UCSR0B &= (uint8_t)~_BV(UDRIE0); * return; * } * #if UART0_SWFLOWCTRL * while(!uart0_ctx.txon) * ; * #endif * UDR0 = x; * } */ .global TX_ISR TX_ISR: push r1 push r21 push r22 in r21, _SFR_IO_ADDR(SREG) CLEAR_BIT_IO UCSR0B, UDRIE0, r22 sei push_range 23, 27 push_range 30, 31 clr r1 ldi r24, lo8(uart0_ctx+UART0_CBB_TX_OFFSET) ldi r25, hi8(uart0_ctx+UART0_CBB_TX_OFFSET) rcall circularbytebuffer_get_fifo cpi r25, 0xff brne 20f CLEAR_BIT_IO UCSR0B, UDRIE0, r24 rjmp 99f 20: #if UART0_SWFLOWCTRL ldi r30, lo8(uart0_ctx+UART0_TXON_OFFSET) ldi r31, hi8(uart0_ctx+UART0_TXON_OFFSET) 30: ld r22, Z tst r22 breq 30b #endif STORE_IO UDR0, r24 SET_BIT_IO UCSR0B, UDRIE0, r22 99: ori r21, 0x80 /* set I bit */ out _SFR_IO_ADDR(SREG), r21 pop_range 30, 31 pop_range 21, 27 pop r1 ret /******************************************************************************/ /* * void uart0_putc (uint16_t c){ * #if UART0_SWFLOWCTRL * while(!uart0_ctx.txon) * ; * #endif * while(circularbytebuffer_cnt(&(uart0_ctx.txb))==UART0_TXBUFFER_SIZE) * ; * cli(); * circularbytebuffer_append((uint8_t)c, &(uart0_ctx.txb)); * sei(); * UCSR0B |= (uint8_t)_BV(UDRIE0); * } * * param c: r24:r25 */ .global uart0_putc uart0_putc: mov r18, r24 #if UART0_SWFLOWCTRL ldi r30, lo8(uart0_ctx+UART0_TXON_OFFSET) ldi r31, hi8(uart0_ctx+UART0_TXON_OFFSET) 10: ld r22, Z tst r22 breq 10b #endif ldi r26, lo8(uart0_ctx+UART0_CBB_TX_OFFSET) ldi r27, hi8(uart0_ctx+UART0_CBB_TX_OFFSET) 20: movw r24, r26 cli rcall circularbytebuffer_cnt sei cpi r24, UART0_TXBUFFER_SIZE breq 20b movw r22, r26 mov r24, r18 clr r25 cli rcall circularbytebuffer_append SET_BIT_IO UCSR0B, UDRIE0, r24 reti /******************************************************************************/ /* * ISR(USART0_RX_vect){ * uint16_t c; * c = UDR0; * #if UART0_SWFLOWCTRL * if(c==XON_VALUE){ * uart0_ctx.txon = 1; * return; * } * if(c==XOFF_VALUE){ * uart0_ctx.txon = 0; * return; * } * #endif * #if UART0_HOOK * if((!uart0_ctx.hook_running) && uart0_ctx.hook){ * uart0_ctx.hook_running=1; * sei(); * do{ * uart0_ctx.hook(c); * }while((c=circularbytebuffer_get_fifo(&(uart0_ctx.rxb)))!=0xffff); * uart0_ctx.hook_running=0; * return; * } * #endif * if(circularbytebuffer_cnt(&(uart0_ctx.rxb))==UART0_RXBUFFER_SIZE) * return; * circularbytebuffer_append(c, &(uart0_ctx.rxb)); * #if UART0_SWFLOWCTRL * if(circularbytebuffer_cnt(&(uart0_ctx.rxb))>UART0_THRESH_HIGH && uart0_ctx.rxon){ * uart0_ctx.rxon = 0; * circularbytebuffer_push(XOFF_VALUE, &(uart0_ctx.txb)); * UCSR0B |= (uint8_t)_BV(UDRIE0); * } * if(circularbytebuffer_cnt(&(uart0_ctx.rxb)) we had send an XOFF earlier */ /* ok, we did not have send an XOFF, should we? */ cpi r24, UART0_THRESH_HIGH brlo 90f /* ok, nothing critical, go on */ st Z, r1 ldi r24, XOFF_VALUE ; sbi _SFR_IO_ADDR(PORTD), 5 rjmp 16f 15: cpi r24, UART0_THRESH_LOW brsh 90f /* nothing has changed */ /* if we get here, we had send an XOFF and are now below threshold */ /* so we sen an XON */ ldi r24, XON_VALUE cbi _SFR_IO_ADDR(PORTD), 5 st Z, r24 16: ldi r22, lo8(uart0_ctx+UART0_CBB_TX_OFFSET) ldi r23, hi8(uart0_ctx+UART0_CBB_TX_OFFSET) rcall circularbytebuffer_push SET_BIT_IO UCSR0B, UDRIE0, r24 90: pop r24 #endif /* UART0_SWFLOWCTRL */ 20: #if UART0_HOOK ldi r30, lo8(uart0_ctx) ldi r31, hi8(uart0_ctx) ldd r22, Z+UART0_HOOKR_OFFSET tst r22 brne 50f ldd r26, Z+UART0_HOOK_OFFSET ldd r27, Z+UART0_HOOK_OFFSET+1 adiw r26, 0 breq 50f movw r28, r26 movw r16, r30 sei 30: /* now we can run the hook */ movw r30, r28 clr r25 icall movw r24, r16 rcall circularbytebuffer_get_fifo cpi r25, 0xff brne 30b clr r24 st -Y, r24 /* write 0 to uart0_hook_running */ rjmp 99f #endif /* UART0_HOOK */ 50: ldi r22, lo8(uart0_ctx+UART0_CBB_RX_OFFSET) ldi r23, hi8(uart0_ctx+UART0_CBB_RX_OFFSET) clr r25 ; sbi _SFR_IO_ADDR(PORTD), 6 rcall circularbytebuffer_append 99: out _SFR_IO_ADDR(SREG), r16 pop_range 16, 31 pop_range 0, 1 reti /******************************************************************************/ /* * uint16_t uart0_getc(void){ * uint8_t ret; * while(circularbytebuffer_cnt(&(uart0_ctx.rxb))==0) * ; * cli(); * ret = circularbytebuffer_get_fifo(&(uart0_ctx.rxb)); * sei(); * return ret; * } */ .global uart0_getc uart0_getc: ldi r22, lo8(uart0_ctx+UART0_CBB_RX_OFFSET) ldi r23, hi8(uart0_ctx+UART0_CBB_RX_OFFSET) 10: movw r24, r22 rcall circularbytebuffer_cnt tst r24 breq 10b movw r24, r22 cli rcall circularbytebuffer_get_fifo clr r25 reti /******************************************************************************/ /* * uint8_t uart0_dataavail(void){ * return circularbytebuffer_cnt(&(uart0_ctx.rxb)); * } */ .global uart0_dataavail uart0_dataavail: ldi r24, lo8(uart0_ctx+UART0_CBB_RX_OFFSET) ldi r25, hi8(uart0_ctx+UART0_CBB_RX_OFFSET) rjmp circularbytebuffer_cnt /******************************************************************************/ #if UART0_HOOK /* * void uart0_sethook(void(*fpt)(uint8_t)){ * uart0_ctx.hook = fpt; * } */ .global uart0_sethook uart0_sethook: ldi r26, lo8(uart0_ctx+UART0_HOOK_OFFSET) ldi r27, hi8(uart0_ctx+UART0_HOOK_OFFSET) st X+, r24 st X+, r25 ret #endif .global uart0_flush uart0_flush: 10: ldi r24, lo8(uart0_ctx+UART0_CBB_TX_OFFSET) ldi r25, hi8(uart0_ctx+UART0_CBB_TX_OFFSET) rcall circularbytebuffer_cnt tst r24 brne 10b ret #endif /* UART0_I */