[CODE][ASM][ATTINY] Bit-bang UART, number conversion, and debugging routines

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I come from an assembly language 8051 family background, so when I started working with some ATTINY chips I wrote the following code which I had already written many years ago for the 8051.

1. The bit-bang UART code will be helpful mainly to those working with the smaller ATTINY chips that do not have a hardware UART.

2. It also includes utility routines that display the contents of registers, RAM, and EEPROM. These can be useful with most AVRs if you don't use other debugging methods.

3. It also includes a "project" of sorts to demonstrate the utilities, a temperature logger, but the hardware is a minimum, (really minimum!)

4. It is specific to the ATTINY85 but in general, only the port bits and memory sizes need to be changed for other chips.

It is listed below for quick viewing and copying, and is attached as a file.

Note: Dec 13th update. Oops, I used the temperature A/D routine from the ATTINY44 I was testing with.

; ************************************************************************
; ATTINY85DEMO.ASM     written by Gary Peek    last updated Dec 13, 2012
;
; Contains:
; 1. ATTINY AVR bit-bang UART routines to create a serial port
; 2. AVR routines for displaying values of registers and memory
; 3. AVR routines for displaying decimal and hex values, and strings
; 4. AVR routines for looped delays
; 5. Demo temperature data logger code to demonstrate the above routines
;
; ************************************************************************
;
;                          Minimum hardware
;
;                     MAX232               ATTINY85
;                  or equivalent        +-----------+
;       PC                        ___  1|           |8
;     Serial             +5       RST --|PB5     VCC|-----------+--- +5
;      Port              |              |           |           |
;     +---+        +-------------+     2|           |7        --+--  .1
; RX  | 2 |--<-----| RS-232      |--<---|PB3     PB2|-- SCK   --+--  uF
; TX  | 3 |-->--+  | transmitter |      |           |           |
; GND | 5 |--+  |  | RS-232      |     3|           |6         GND
;     +---+  |  +--| Receiver    |-->---|PB4     PB1|-- MISO
;            |     +-------------+      |           |
;            |           |             4|           |5
;           GND         GND       GND --|GND     PB0|-- MOSI
;                                       +-----------+
;
; The default internal 8MHZ oscillator is VCC and temperature dependent,
; therefore this circuit is useful only as a demo, since using it at
; widely varying temperatures or with battery power will affect timing.
;
; ************************************************************************

.device ATtiny85

; Including "tn85def.inc" can replace some of the following equates and defines 

.equ	FLASHEND=$fff	;words
.equ	E2END   =$1ff
.equ	RAMBEG  =$60
.equ	RAMEND  =$25f

; CPU and I/O registers

.equ    SREG    =$3f    ;$5f RAM
.equ    SPL     =$3d
.equ    SPH     =$3e
.equ    MCUCR   =$35

.equ    EEARH   =$1F
.equ    EEARL   =$1E
.equ    EEDR    =$1D
.equ    EECR    =$1C

.equ    PORTB   =$18
.equ    DDRB    =$17
.equ    PINB    =$16

.equ	ADMUX	=$07
.equ	ADCSRA	=$06
.equ	ADCH	=$05
.equ	ADCL	=$04
.equ	ADCSRB	=$03

;               =$00    ;$20 RAM

; General purpose registers
; Immediate instructions only available on R16-R31

.def	ZH	=r31
.def	ZL	=r30
.def	YH	=r29
.def	YL	=r28
.def	XH	=r27
.def	XL	=r26

.def	calHI	=R25		;for record routine
.def	calLO	=R24

.def	loopD	=R23		;delay countdown and loop registers
.def	loopC	=R22
.def	loopB	=R21
.def	loopA	=R20

.def	regD	=R19		;temporary registers
.def	regC	=R18
.def	regB	=R17
.def	regA	=R16

.def	dispMN	=R6		;for disptemps routine
.def	dispLN	=R5		;for dispee/dispram/record routines
.def	dispLOC	=R4		;for dispee/dispram/record routines
.def	rbdHI	=R3		;bit delay x 1.5 for rxchar routine
.def	rbdLO	=R2		;bit delay x 1.5 for rxchar routine
.def	bdHI	=R1		;bit delay for txchar and rxchar routines
.def	bdLO	=R0		;bit delay for txchar and rxchar routines

;-----------------------------------------------------------------------------

.CSEG
.ORG	0

init:				;default reset pin left as low going reset
                                ;default internal 8MHZ clock, startup time, and system clock prescaler

        ldi	regA,low(RAMEND)	;get end of RAM location
        ldi	regB,high(RAMEND)
        out	SPL,regA		;set stack pointer
        out	SPH,regB

                                ;clear RAM
clrram:	ldi	ZH,0		;start of RAM in ATTINY85
        ldi	ZL,$60		;start of RAM in ATTINY85
        ldi	regA,0		;value to clear
        ldi	regC,$02	;number of bytes to clear, MSB
clrrm2:	ldi	regB,$00	;number of bytes to clear, LSB
clrrm1:	st	Z+,regA		;store value in ram, post increment index register
        dec	regB		;next location
        brne	clrrm1
        dec	regC		;next location
        brne	clrrm2

        ldi	regA,$02	;pullups not disabled, sleep mode disabled, interrupt sense on falling edge
        out	MCUCR,regA	;MCU control register

        rcall	inittx		;initialize serial tx pin and tx/rx bit timing values
        rcall	initrx		;initialize serial rx pin and rx 1.5 bit timing values

        rcall	delts		;delay 1/10 second

;-----------------------------------------------------------------------------

txmenu:	ldi	ZH,high(2*menmsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*menmsg)	;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory
        rjmp	main

menmsg:	.db 13,10,13,10,"ATTiny85 Temperature Logger/Demo",13,10,13,10
        .db "c  calibrate temp sensor at 25 celcius",13,10
        .db "s  start recording temperature every 10 seconds ",13,10
        .db "t  display recorded temperatures in EEPROM",13,10
        .db "g  display contents of registers (in RAM) in hex",13,10
        .db "r  display RAM in hex ",13,10
        .db "e  display EEPROM in hex ",13,10,0

main:	rcall	rxchar		;check for and receive character into regA
        brcc	main		;carry not set means no character available

m1:	cpi	regA,'c'
        brne	m2
        rcall	calib		;calibrate temperature sensor
        rjmp	main

m2:	cpi	regA,'s'
        brne	m3
        rcall	record		;start recording temperatures
        rjmp	main

m3:	cpi	regA,'t'
        brne	m4
        rcall	disptemps	;display recorded temperatures
        rjmp	main

m4:	cpi	regA,'g'
        brne	m5
        rcall	dispreg		;display contents of registers (in RAM)
        rjmp	main

m5:	cpi	regA,'r'
        brne	m6
        rcall	dispram		;display contents of RAM
        rjmp	main

m6:	cpi	regA,'e'
        brne	m7
        rcall	dispee		;display contents of EEPROM
        rjmp	main

m7:	rjmp	txmenu		;back to the menu if not a valid character

;-----------------------------------------------------------------------------

calib:	rcall	rdtad		;read temperature into regB:regA
        mov	regC,regA	;save A/D LSB
        mov	regD,regB	;save A/D MSB
        ldi	ZH,$00		;address of calibration byte
        ldi	ZL,$00		;address of calibration byte
        mov	regA,regD	;get A/D MSB
        rcall	eewr1		;write one byte to EEPROM
        ldi	ZH,$00		;address of calibration byte
        ldi	ZL,$01		;address of calibration byte
        mov	regA,regC	;get A/D LSB
        rcall	eewr1		;write one byte to EEPROM
        ldi	ZH,high(2*calmsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*calmsg)	;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory
        ret

calmsg:	.db 13,10,"Calibration done",13,10,0,0

;--------------------

recmsg:	.db 13,10,"Recording  ",0

record:	ldi	ZH,high(2*recmsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*recmsg)	;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory

                                ;get temperature sensor calibration word
        ldi	regA,0		;starting address
        out	EEARH,regA	;address MSB
        ldi	regA,0		;starting address
        out	EEARL,regA	;address LSB
        sbi	EECR,0		;set read enable
        in	calHI,EEDR	;get read data, calibration MSB
        ldi	regA,0		;starting address
        out	EEARH,regA	;address MSB
        ldi	regA,1		;starting address
        out	EEARL,regA	;address LSB
        sbi	EECR,0		;set read enable
        in	calLO,EEDR	;get read data, calibration LSB	

        subi	calLO,25	;subtract 25 (degrees celcius) where calibration is done
        sbci	calHI,0		;high byte should always result in zero

        ldi	ZH,$00		;starting EEPROM address of temperatures
        ldi	ZL,$02		;starting EEPROM address of temperatures
        ldi	regA,18		;number of minutes recorded
                                ;(limited only to keep display on one screen)
        mov	dispLN,regA

rechour:ldi	regA,6		;number of readings per minute
        mov	dispLOC,regA

rec1:	rcall	rdtad		;read temperature into regB:regA
                                ;calculate temperature, put in regA
        sub	regA,calLO	;subtract calibration value from reading
        sbc	regB,calHI	;should always result in zero

        out	EEARH,ZH	;put address in EEPROM registers
        out	EEARL,ZL	;put address in EEPROM registers
        rcall	eewr1		;write one byte to EEPROM

        ldi	regA,'.'	;send a period
        rcall	txchar		;transmit character in regA

                                ;wait until next record period
        ldi	loopD,10	;10 x 1/10 second = 10 seconds
rec2:	rcall	del1s		;1 second delay
        dec	loopD		;10 seconds
        brne	rec2
        ld	regA,Z+		;do this only to post increment index register
        dec	dispLOC		;next location (reading)
        brne	rec1
        dec	dispLN		;next line (minute)
        brne	rechour
        ret

;--------------------

rdtad:				;read ATTINY85 temperature A/D word into regB:regA
        ldi	regA,0b10001111	;1.1V reference, channel 4 (temperature)
        out	ADMUX,regA	;Bits: ;REFS1, REFS0, ADLAR, REFS2, MUX3, MUX2, MUX1, MUX0
        ldi	regA,0b11000111	;enable ADC, start conversion, no auto trigger,
                                ;clear interrupt flag, disable interrupt, prescaller=128
        out	ADCSRA,regA	;Bits: Enable, Start Conversion, Auto Trigger Enable,
                                ;Interrupt Flag, Interrupt Enable, Prescaler bits 2,1,0
        ldi	regA,0b00000000	;unipolar, no reversal, free running
        out	ADCSRB,regA	;Bits: BIN, ACME, IPR, n/a, n/a, ADTS2, ADTS1, ADTS0

waitr1:	sbic	ADCSRA,6	;skip over if conversion is complete
        rjmp	waitr1		;conversion not done, keep waiting
        in	regA,ADCL	;A/D LSB should be read first
        in	regB,ADCH	;A/D MSB
        ret

;--------------------

                                ;write one byte to EEPROM
                                ;enter with address in ZH:ZL, data in regA
eewr1:	sbic	EECR,1		;wait for completion of any previous write
        rjmp	eewr1

        out	EEARH,ZH	;EEPROM address MSB
        out	EEARL,ZL	;EEPROM address LSB
        out	EEDR,regA	;data to write
                                ;EECR Bits: n/a, n/a, EEPM1, EEPM0, EERIE, EEMPE, EEPE, EERE
        cbi	EECR,5		;set programming mode to erase/write
        cbi	EECR,4		;set programming mode to erase/write
        cbi	EECR,3		;no interrupt
        cbi	EECR,0		;no read enable
        sbi	EECR,2		;master enable
        sbi	EECR,1		;program enable
        ret

;--------------------

tmpmsg:	.db 13,10,"Temperatures: ",13,10
        .db "Mins 00s 10s 20s 30s 40s 50s ",13,10,0

disptemps:				;display recorded temperatures in EEPROM
        ldi	ZH,high(2*tmpmsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*tmpmsg)	;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory
        ldi	regA,0		;starting minute
        mov	dispMN,regA
        ldi	ZH,$00		;starting EEPROM address of temperatures
        ldi	ZL,$02		;starting EEPROM address of temperatures
        ldi	regA,18		;number of minutes recorded
        mov	dispLN,regA
dtln1:	ldi	regA,6		;number of readings per minute
        mov	dispLOC,regA
        mov	regA,dispMN	;get hour
        rcall	txdec3		;transmit decimal value in regA as 3 digits
        ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
dispt1:	sbic	EECR,1		;wait for completion of any previous write
        rjmp	dispt1
        ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
        out	EEARH,ZH	;put address in EEPROM registers
        out	EEARL,ZL	;put address in EEPROM registers
        sbi	EECR,0		;set read enable
        in	regA,EEDR	;get read data
        rcall	txdec3		;transmit decimal value in regA as 3 digits
        ld	regA,Z+		;do this only to post increment index register
        dec	dispLOC		;next location
        brne	dispt1
        rcall	txcrlf		;transmit carriage return and line feed
        inc	dispMN		;next hour
        dec	dispLN		;next line
        brne	dtln1
        ret

;-----------------------------------------------------------------------------

initled:
        sbi	DDRB,1		;PB1 output for serial out
        sbi	PORTB,1		;PB1 initially high
        ret

ledon:	cbi	PORTB,1		;PB1 low, LED on
        ret

ledoff:	sbi	PORTB,1		;PB1 high, LED off
        ret

;-----------------------------------------------------------------------------

; AVR utility routines for looped delays
; Timing values are based on using the default clock source of the internal
; 8MHZ oscillator and the default system clock prescaler value of 8,
; resulting in a 1MHZ system clock. The timing values are also based on
; 5 volts power and the reference temperature of 68 degrees F.

;--------------------

del1s:	ldi	loopA,10	;1 second delay
        rjmp	dellp1

delts:	ldi	loopA,1		;1/10th second delay

                                ;delay inner loops
dellp1:	ldi	loopB,185	;coarse adjustment
dellp2:	ldi	loopC,185	;fine adjustment
dellp3:	dec	loopC
        brne	dellp3
        dec	loopB
        brne	dellp2
        dec	loopA
        brne	dellp1
        ret

;-----------------------------------------------------------------------------

; AVR utility routines for displaying values of registers and memory in hex.
; Requires the routines txmsg, txchar, txhex, and txcrlf below.

;--------------------

regmsg:	.db 13,10,"Registers:",13,10,0,0

dispreg:			;display contents of registers (in RAM) in hexadecimal
        ldi	ZH,high(2*regmsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*regmsg)	;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory
        ldi	ZH,$00		;start of RAM in ATTINY85
        ldi	ZL,$00		;start of RAM in ATTINY85
        ldi	regA,6		;number of lines for ATTINY85
        mov	dispLN,regA
rgln1:	ldi	regA,16		;number of locations per line
        mov	dispLOC,regA
        mov	regA,ZH		;get address
        rcall	txhex		;transmit hex value in regA
        mov	regA,ZL		;get address
        rcall	txhex		;transmit hex value in regA
        ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
rgrd1:	ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
        sbi	EECR,0		;set read enable
        ld	regA,Z+		;get value from ram, post increment index register
        rcall	txhex		;transmit hex value in regA
        dec	dispLOC		;next location
        brne	rgrd1
        rcall	txcrlf		;transmit carriage return and line feed
        dec	dispLN		;next line
        brne	rgln1
        ret

;--------------------

rammsg:	.db 13,10,"RAM:",13,10,0,0

dispram:			;display contents of RAM in hexadecimal
        ldi	ZH,high(2*rammsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*rammsg)	;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory
        ldi	ZH,0		;start of RAM in ATTINY85
        ldi	ZL,$60		;start of RAM in ATTINY85
        ldi	regA,32		;number of lines for ATTINY85
        mov	dispLN,regA
rmln1:	ldi	regA,16		;number of locations per line
        mov	dispLOC,regA
        mov	regA,ZH		;get address
        rcall	txhex		;transmit hex value in regA
        mov	regA,ZL		;get address
        rcall	txhex		;transmit hex value in regA
        ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
rmrd1:	ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
        sbi	EECR,0		;set read enable
        ld	regA,Z+		;get value from ram, post increment index register
        rcall	txhex		;transmit hex value in regA
        dec	dispLOC		;next location
        brne	rmrd1
        rcall	txcrlf		;transmit carriage return and line feed
        dec	dispLN		;next line
        brne	rmln1
        ret

;--------------------

eemsg:	.db 13,10,"EEPROM:",13,10,0

dispee:				;display contents of EEPROM in hexadecimal
        ldi	ZH,high(2*eemsg)	;load high part of byte address into ZH
        ldi	ZL,low(2*eemsg)		;load low part of byte address into ZL
        rcall	txmsg		;transmit message in code memory
        ldi	ZH,$00		;start of EEPROM in ATTINY85
        ldi	ZL,$00		;start of EEPROM in ATTINY85
        ldi	regA,32		;number of lines for ATTINY85
        mov	dispLN,regA	;line counter
eeln1:	ldi	regA,16		;number of locations per line
        mov	dispLOC,regA	;location counter
        mov	regA,ZH		;get address
        rcall	txhex		;transmit hex value in regA
        mov	regA,ZL		;get address
        rcall	txhex		;transmit hex value in regA
        ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
eerd1:	sbic	EECR,1		;wait for completion of any previous write
        rjmp	eerd1
        ldi	regA,' '	;space
        rcall	txchar		;transmit character in regA
        out	EEARH,ZH	;put address in EEPROM registers
        out	EEARL,ZL	;put address in EEPROM registers
        sbi	EECR,0		;set read enable
        in	regA,EEDR	;get read data
        rcall	txhex		;transmit hex value in regA
        ld	regA,Z+		;do this only to post increment index register
        dec	dispLOC		;next location
        brne	eerd1
        rcall	txcrlf		;transmit carriage return and line feed
        dec	dispLN		;next line
        brne	eeln1
        ret

;-----------------------------------------------------------------------------

; AVR utility routines for displaying values and strings. Requires the
; routine txchar which transmits the character in regA out the serial
; port. The ATTINY AVR bit-bang UART routines includes this routine, but if
; the AVR being used has a hardware UART, txchar can be created for it.

;--------------------

                                ;transmit message in code memory, ending with a NULL
                                ;pointed to by Z register
txmsg:	push	regA		;used here, save it
txmsg2:	lpm	regA,Z		;get next character in string
        tst	regA		;set bits in SREG
        breq	txmsg1		;zero, past end of string
        rcall	txchar		;transmit character in regA
        adiw	ZL,1		;point to next character
        rjmp	txmsg2		;transmit next character
txmsg1:	pop	regA		;restore it
        ret

;--------------------

                                ;transmit carriage return and line feed
txcrlf:	ldi	regA,13		;carriage return
        rcall	txchar		;transmit character in regA
        ldi	regA,10		;line feed
        rcall	txchar		;transmit character in regA
        ret

;--------------------

                                ;transmit regA as 2 hexadecimal digits
txhex:	push	regA		;save lower nibble
        swap	regA		;move upper nibble to lower
        rcall	htoa		;convert hex to ascii character
        rcall	txchar		;transmit character in regA
        pop	regA		;restore lower nibble
        rcall	htoa		;convert hex to ascii character
        rcall	txchar		;transmit character in regA
        ret	

                                ;convert hex to ascii character
htoa:	push	regB		;used here, save it
        andi	regA,$0f	;use only lower nibble
        cpi	regA,$0a	;compare with 10
        brlo	htoa1		;lower, skip
        ldi	regB,55		;convert A-F
        rjmp	htoa2
htoa1:	ldi	regB,48		;convert 0-9
htoa2:	add	regA,regB	;add offset to character
        pop	regB		;restore it
        ret

;--------------------

                                ;transmit regA as 1-3 digits, no leading zeros
txdec:	push	regB		;save regB which is used here
        mov	regB,regA	;remainder of value will be here
        clt			;clear flag to indicate no hundreds digit
        clr	regA		;zero the digit which will be created here
txde2:	cpi	regB,100	;see if value is at least 100
        brlo	txde1		;lower than 100, no need to subtract
        subi	regB,100	;subtract 100
        inc	regA		;increase hundreds count
        rjmp	txde2		;check again
txde1:	cpi	regA,0		;see if hundreds digit is zero
        breq	txde5		;skip if zero
        call	txasc		;transmit hundreds count in regA
        set			;flag a hundreds digit
txde5:	clr	regA		;zero the digit which will be created here
        mov	regC,regB	;save remainder in case we subtract too much
txde4:	cpi	regB,10		;see if value is at least 10
        brlo	txde3		;lower than 10, no need to subtract
        subi	regB,10		;subtract 10
        inc	regA		;increase tens count
        rjmp	txde4		;check again
txde3:	brts	txde7		;force a tens digit if there is a hundreds digit
        cpi	regA,0		;see if tens digit is zero
        breq	txde6		;skip if zero
txde7:	call	txasc		;transmit tens count in regA
txde6:	mov	regA,regB	;remainder is the ones count
        call	txasc		;transmit ones count in regA
        pop	regB		;restore regB
        ret

                                ;transmit regA as 3 digits
txdec3:	push	regB		;save regB which is used here
        mov	regB,regA	;remainder of value will be here
        clr	regA		;zero the digit which will be created here
txd32:	cpi	regB,100	;see if value is at least 100
        brlo	txd31		;lower than 100, no need to subtract
        subi	regB,100	;subtract 100
        inc	regA		;increase hundreds count
        rjmp	txd32		;check again
txd31:	call	txasc		;transmit hundreds count in regA
        clr	regA		;zero the digit which will be created here
        mov	regC,regB	;save remainder in case we subtract too much
txd34:	cpi	regB,10		;see if value is at least 10
        brlo	txd33		;lower than 10, no need to subtract
        subi	regB,10		;subtract 10
        inc	regA		;increase tens count
        rjmp	txd34		;check again
txd33:	call	txasc		;transmit tens count in regA
        mov	regA,regB	;remainder is the ones count
        call	txasc		;transmit ones count in regA
        pop	regB		;restore regB
        ret

                                ;add ASCII offset to digit, then jump to txchar
txasc:	push	regB		;save it
        ldi	regB,$30	;add the ASCII offset
        add	regA,regB	;to create character 0-9
        pop	regB		;restore it
        rjmp	txchar		;continue to txchar to transmit number

;-----------------------------------------------------------------------------

; AVR bit-bang UART routines. 8 bits, no parity. Assumes 8 MHZ internal clock,
; RS-232 driver on both signals. 8 bits, no parity. Baud rates set in inittx
; and initrx. Port bits are specified in inittx, initrx, txchar, rxchar.

; The transmit routine can be used alone, but the receive routine requires
; the registers and bit time values set in the transmit init routine.

; Since the receive routine is not interrupt based, it must be called in
; the main program often enough to see when the character start bit occurs.
; If it cannot be called often enough the baud rate will need to be lower.
; At 9600 baud the timing is close. The receive routine is suitable for
; entering single character with time in between them, such as typing at
; a terminal program. It is not suitable for receiving a fast data stream.

; Timing values are based on using the default clock source of the internal
; 8MHZ oscillator and the default system clock prescaler value of 8,
; resulting in a 1MHZ system clock. The timing values are also based on
; 5 volts power and the reference temperature of 68 degrees F.

; Internal 8MHZ oscillator is 10% uncalibrated, 1% calibrated
; with 5 volts power, if 8 MHZ at 25C, 8.25 MHZ at 85C, 7.7 MHZ at -40C
; 85C, 185F, 3.1% fast, 8.25 MHZ
; 45C, 113F, 1.25% fast, 8.1 MHZ
; 40C, 104F, 1% fast, 8.08 MHZ
; 35C, 95F, 0.625% fast, 8.05 MHZ
; 30C, 86F, 0.5% fast, 8.04 MHZ
; 20C, 68F, reference (8 MHZ)
; 15C, 59F, 0.5% slow, 7.96 MHZ
; 10C, 50F, 0.625% slow, 7.95 MHZ
; 0C, 32F, 1% slow, 7.92 MHZ
; -3C, 26F, 1.25% slow, 7.9 MHZ
; -40C, -40F, 3.7% slow, 7.7 MHZ

; values used in       inittx/rx     initrx
; baud   bit time uS   bdHI  bdLO    rbdHI rbdLO
; 9600   104           01H   1DH     01H   2DH
; 4800   208           01H   40H     01H   5EH
; 2400   416.6         01H   85H     02H   62H
; 1200   833           02H   86H     03h   87H
; 600    1666.6        04H   87H     06H   86H
; 300    3333          08H   87H     0CH   EAH

;--------------------

inittx:	sbi	DDRB,3		;PB3 output for serial out
        sbi	PORTB,3		;PB3 initially high

        ldi	regA,$01	;coarse bit timing value
        mov	bdHI,regA
        ldi	regA,$1D	;fine bit timing value
        mov	bdLO,regA
        ret

;--------------------

txchar:				;transmit character in regA out the port bit
        cbi	PORTB,3		;begin start bit
        rcall	txrxbd		;bit time delay
        ldi	loopC,8		;8 data bits, no parity
txc1:	ror	regA		;rotate bit 0 into carry
        brcs	txc2		;branch if bit is high
        cbi	PORTB,3
        rjmp	txc3
txc2:	sbi	PORTB,3
txc3:	rcall	txrxbd		;bit time delay
        dec	loopC
        brne	txc1		;loop until all bits done
        sbi	PORTB,3		;begin the stop bit
        rcall	txrxbd		;bit time delay
                                ;wait another bit time before next character
                                ;(sends 2 stop bits)
        rcall	txrxbd		;bit time delay
        ret

;--------------------
                                ;bit delay, common to tx and rx routines
txrxbd:	mov	loopA,bdHI	;coarse bit timing value
txrx5:	mov	loopB,bdLO	;fine bit timing value
txrx4:	dec	loopB		;inner loop
        brne	txrx4
        dec	loopA		;outer loop
        brne	txrx5
        ret

;--------------------

initrx:	cbi	DDRB,4		;PB4 input for serial in
        cbi	PORTB,4		;PB4 no internal pullup

        ldi	regA,$01	;x1.5 coarse bit timing value
        mov	rbdHI,regA
        ldi	regA,$2D	;x1.5 fine bit timing value
        mov	rbdLO,regA
        ret

;--------------------

; Check for start bit, receive character at port bit if asserted. Exit with
; carry set if character received or carry cleared if no character is ready.

rxchar:	clc			;carry clear means no character ready
        sbic	PINB,4		;skip if serial receive pin is low
        ret			;start bit not asserted

                                ;receive character at the port bit into regA
                                ;wait 1.5 bit times (center of first bit)
        mov	loopA,rbdHI	;coarse bit timing value
rx5:	mov	loopB,rbdLO	;fine bit timing value
rx4:	dec	loopB		;inner loop
        brne	rx4
        dec	loopA		;outer loop
        brne	rx5
        ldi	loopC,8		;8 data bits, no parity
rxc3:	clc			;start with carry clear
        sbic	PINB,4		;skip if port bit low
        sec			;set carry high to match port bit
        ror	regA		;rotate carry into bit 7 of regA
        rcall	txrxbd		;bit time delay
        dec	loopC		;do next bit
        brne	rxc3		;loop until all bits done
        rcall	txrxbd		;bit time delay                 (comment out?)
                                ;wait until 1/2 bit time past the stop bit
                                ;before allowing looking for next character
        sec			;carry set means character ready
        ret

; Posted by Dave Lundquist: Sat. Sep 1, 2018 - 09:33 AM
; Just a quick point for anyone using this. It works fine, but I did find one
; minor but important issue. The rxchar routine is really setup for 2 stop bits.
; With one stop bit that last call to txrxbd puts you half way into the start bit
; of the next character if you have back to back 8N1 traffic. Your timing is now
; screwed up for the next character since you miss the low going edge of the next
; start bit. Simply commenting out that line fixes it. You return from the routine
; in the middle of the stop bit, not the middle of the bit after the stop bit.

Attachment(s): 

Last Edited: Tue. Mar 12, 2019 - 02:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great work Gary,

 

The ATtiny85 is just being fully appreciated in 2018 not so much by the professional but the many amateurs,

which have the desire to connect their devices to the Arduino processor with USB connection, whilst loosing

in the effort 3 K of Flash memory.

 

Your ATtiny85  resource is an example of a fine Assembler code, to those who can rejoice on your fine

programming craft.

 

Thanking  you

 

Kind regards

 

Alfons:

...........

Variety is the spice of live.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just a quick point for anyone using this.  It works fine, but I did find one minor but important issue.  The rxchar routine is really setup for 2 stop bits.  With one stop bit that last call to txrxbd puts you half way into the start bit of the next character if you have back to back 8N1 traffic.   Your timing is now screwed up for the next character since you miss the low going edge of the next start bit.  Simply commenting out that line fixes it.  You return from the routine in the middle of the stop bit, not the middle of the bit after the stop bit.

 

Dave

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dave, thank you very much for that correction!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you so much for sharing the above code example. it is something i have been looking for to help connect the missing dots im experiencing in learning assembly language for avr.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're welcome. Here is a couple more resources that I have found handy. Old but good:

http://www.avr-asm-tutorial.net/...

http://www.avrbeginners.net/

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Gary did you ever work with the Signetics 2650? (looooooong time ago) I do remember having some stuff from someone with a name similar or the same as yours.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, that was not me.