517 lines
12 KiB
ArmAsm
517 lines
12 KiB
ArmAsm
/* uart_i-asm.S */
|
|
/*
|
|
This file is part of the AVR-uart_i.
|
|
Copyright (C) 2009 Daniel Otte (daniel.otte@rub.de)
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* \file uart_i-asm.S
|
|
* \email daniel.otte@rub.de
|
|
* \author Daniel Otte
|
|
* \date 2009-07-24
|
|
* \license GPLv3 or later
|
|
* \ingroup uart_i
|
|
* \brief implementation of interrupt based uart
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#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 <util/setbaud.h>
|
|
* 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))<UART0_THRESH_LOW && !uart0_ctx.rxon){
|
|
* uart0_ctx.rxon = 1;
|
|
* circularbytebuffer_push(XON_VALUE, &(uart0_ctx.txb));
|
|
* UCSR0B |= (uint8_t)_BV(UDRIE0);
|
|
* }
|
|
* #endif
|
|
* }
|
|
*
|
|
*/
|
|
.global RX_ISR
|
|
RX_ISR:
|
|
push_range 0, 1
|
|
push_range 16, 31
|
|
in r16, _SFR_IO_ADDR(SREG)
|
|
clr r1
|
|
LOAD_IO r24, UDR0
|
|
#if UART0_SWFLOWCTRL
|
|
ldi r26, lo8(uart0_ctx+UART0_TXON_OFFSET)
|
|
ldi r27, hi8(uart0_ctx+UART0_TXON_OFFSET)
|
|
cpi r24, XON_VALUE
|
|
breq 11f
|
|
cpi r24, XOFF_VALUE
|
|
brne 12f
|
|
clr r24
|
|
11: st X, r24
|
|
rjmp 99f
|
|
12:
|
|
push r24
|
|
/* now the "sending" part*/
|
|
ldi r24, lo8(uart0_ctx+UART0_CBB_RX_OFFSET)
|
|
ldi r25, hi8(uart0_ctx+UART0_CBB_RX_OFFSET)
|
|
rcall circularbytebuffer_cnt
|
|
ldi r30, lo8(uart0_ctx+UART0_RXON_OFFSET)
|
|
ldi r31, hi8(uart0_ctx+UART0_RXON_OFFSET)
|
|
ld r18, Z
|
|
tst r18
|
|
breq 15f/* branch if rxon inactive -> 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 */
|