Design contest -> No prize, just gratitude

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

I need to write an assembly routine that reads a byte from a port and based on that byte sets or clears one of 64 port bits. This should run on a 640.

eg.
0b10000001 -> Would set BIT1 on PORTA
0b00000001 -> Would clear BIT1 on PORTA

0b10000100 -> Would set BIT4 on PORTA
0b00000100 -> Would clear BIT4 on PORTA

0b10001101 -> Would set BIT5 on PORTB
0b00001101 -> Would clear BIT5 on PORTB

0b10001111 -> Would set BIT7 on PORTB
0b00001111 -> Would clear BIT7 on PORTB

I have no problem using the "brne" test to make this happen, but with 64+ possible results I would like to know if there is a better way. Any ideas?

Thanks,

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

This is from an older thread here on freaks (I think glitch & zbaird were involved with this). Its the shortest, fastest way I have ever seen to convert 3 binary bits into a single 1 of 8 binary weighted bit position on an AVR. Its faster than a table lookup and takes less code space.

;
; this code gives the equivalent result of r17 = (1 << (r16 & 0x3))
;
ldi r17, 0x01 
sbrc r16, 1 
	ldi r17, 0x04 
sbrc r16, 0 
	lsl r17 
sbrc r16, 2 
	swap r17

All you have to do is add your port selection logic and on/off selection to this code.

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

The solution is in the pattern.

Quote:
0b10000001 -> Would set BIT1 on PORTA
0b00000001 -> Would clear BIT1 on PORTA

0b10000100 -> Would set BIT4 on PORTA
0b00000100 -> Would clear BIT4 on PORTA

0b10001101 -> Would set BIT5 on PORTB
0b00001101 -> Would clear BIT5 on PORTB

0b10001111 -> Would set BIT7 on PORTB
0b00001111 -> Would clear BIT7 on PORTB

Here is the pattern that I see.

Bit 7 = 1 = Set bit
Bit 7 = 0 = Clr bit

Bits 654 = x = don't care according to your example

Bit 3 = 0 = PORTA
Bit 3 = 1 = PORTB

Bits 210 = Bit #, 000=Bit0, 111=Bit7

Is this the pattern that you expected ?

It should be easy enough to break the control byte into bitfields, and write the code without doing lots of individual compares.

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

There may be up to 64 bits to manipulate. The missing bits are
just to show it would change from port to port.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

Port A, B, C, D, E, F, G (missing bits 6 and 7), H, J, K and L. This is 11 ports with 8 pins each (except for port G) for 86 pins total.

Do you actually mean there are 64 pins to manipulate? This many pins takes 8 ports, but the 7th port G is missing two bits. Will you skip port G and use ports H and J instead, or are you willing to use G and have 62 pins total?

See the 2549L–AVR–08/07 data sheet page 418 section 33 Register Summary. Ports H, J, K and L are on page 414.

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

Guys,

The part is ATmega640 100A pkg. The question regards the best method not the
exact number of pins to be manipulated. One port is used for data i/p the
other needed port bits (pins) total about 64. Yes there are 64 pins to manipulate.

I need to set or clear anyone of 64 port pins by decoding a control byte.

Thanks,

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

Your command bit format is like this:
0bsppppbbb

Where s = on/off, pppp = port, and bbb = bit weight

0bsppppbbb, pppp only needs to be a value from 0 to 7 (8 ports total) because 8 * 8 = 64

So, how about redefining the format like this (this makes the port map table smaller):
0bsxpppbbb

Where x = reserved (not used)

Here is a port map table that uses the actual I/O address of the port (for ports with an address lower than 0x40 the m640inc.def file cannot be used).

ppp = 0 decimal maps to 0x22 (the absolute I/O address of PORTA)
ppp = 1 decimal maps to 0x25 (PORTB)
ppp = 2 decimal maps to 0x28 (PORTC)
ppp = 3 decimal maps to 0x2B (PORTD)
ppp = 4 decimal maps to 0x2E (PORTE)
ppp = 5 decimal maps to 0x31 (PORTF)
ppp = 6 decimal maps to 0x102 (PORTH)
ppp = 7 decimal maps to 0x105 (PORTJ)

No matter how large the ppp table is, you must make sure it only maps valid port numbers. Otherwise, bad out of range ppp values could write to the totally wrong I/O register (major bugs). For example, if you used the original pppp with a 16 entry map (0 to 15), then make sure any unused port map values point at a valid I/O port even if you don't think you're going to use the extra values.

I would do the map with a lookup table (placing the map lookup result in either the X, Y or Z AVR register pair) and use the previous code to convert the bbb bit weight into the correct bit. Then using the correct LD instruction read the mapped PORT 8 bit value into a general purpose register, depending on the value of the s on/off bit use the appropriate OR / AND function on this general register with the bbb bit weight result, finally using the correct ST instruction to write the new value back to the mapped PORT. The mapping lookup table also allows you to assign which port actually get used in what order. If you want a certain physical pin layout order, it might be easier to use non-sequential ports.

If you want, I could whip up some concept code tomorrow.

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

Mike,

Thank you. Your description is approx. how I envisioned it. I was not sure of the best way to detect which port to target but your idea seems to fit the bill. Thanks again for offering to put "something down on paper" but this is something I should take care of myself. :wink:

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

Guys,

Just thought I would post my solution. It takes some time to parse through the code but it works. Any suggestions on speed improvement? It takes 10uS to flip a bit.

;*****
; Int2
;

WRITE_5:			; Bit manipulation
	;	
	push WREG_A
	push WREG_B

	mov WREG_A, PIN_MAN		; Move to a place we can work on this
	andi WREG_A, 0x80		; Is top bit high low?
	cpi WREG_A, 0x80		; Is it?
	brne BIT_IS_LOW
	jmp BIT_IS_HIGH

BIT_IS_LOW:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00111000	; Clear all but port address
	cpi	WREG_A, 0b00001000	; PORTC
	brne NEXT_L1
	jmp PORTC_L
NEXT_L1:
	cpi	WREG_A, 0b00010000	; PORTF
	brne NEXT_L2
	jmp PORTF_L
NEXT_L2:
	cpi	WREG_A, 0b00011000	; PORTH
	brne NEXT_L3
	jmp PORTH_L
NEXT_L3:
	cpi	WREG_A, 0b00100000	; PORTJ
	brne NEXT_L4
	jmp PORTJ_L
NEXT_L4:
	cpi	WREG_A, 0b00101000	; PORTK
	brne NEXT_L5
	jmp PORTK_L
NEXT_L5:
	cpi	WREG_A, 0b00110000	; PORTL
	brne NEXT_L6
	jmp PORTL_L
NEXT_L6:

	jmp	END_OF_BIT_M		; No port assigned


BIT_IS_HIGH:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00111000	; Clear all but port address
	cpi	WREG_A, 0b00001000	; PORTC
	brne NEXT_H1
	jmp PORTC_H
NEXT_H1:
	cpi	WREG_A, 0b00010000	; PORTF
	brne NEXT_H2
	jmp PORTF_H
NEXT_H2:
	cpi	WREG_A, 0b00011000	; PORTH
	brne NEXT_H3
	jmp PORTH_H
NEXT_H3:
	cpi	WREG_A, 0b00100000	; PORTJ
	brne NEXT_H4
	jmp PORTJ_H
NEXT_H4:
	cpi	WREG_A, 0b00101000	; PORTK
	brne NEXT_H5
	jmp PORTK_H
NEXT_H5:
	cpi	WREG_A, 0b00110000	; PORTL
	brne NEXT_H6
	jmp PORTL_H
NEXT_H6:

	jmp	END_OF_BIT_M		; No port assigned

	;Take care of each port
	; Port C bit clear
PORTC_L:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Clear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTC_L1
	in WREG_B, PINC
	andi WREG_B, 0b11111110
	out PORTC, WREG_B
PORTC_L1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTC_L2
	in WREG_B, PINC
	andi WREG_B, 0b11111101
	out PORTC, WREG_B
PORTC_L2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTC_L3
	in WREG_B, PINC
	andi WREG_B, 0b11111011
	out PORTC, WREG_B
PORTC_L3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTC_L4
	in WREG_B, PINC
	andi WREG_B, 0b11110111
	out PORTC, WREG_B
PORTC_L4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTC_L5
	in WREG_B, PINC
	andi WREG_B, 0b11101111
	out PORTC, WREG_B
PORTC_L5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTC_L6
	in WREG_B, PINC
	andi WREG_B, 0b11011111
	out PORTC, WREG_B
PORTC_L6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTC_L7
	in WREG_B, PINC
	andi WREG_B, 0b10111111
	out PORTC, WREG_B
PORTC_L7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTC_L8
	in WREG_B, PINC
	andi WREG_B, 0b01111111
	out PORTC, WREG_B
PORTC_L8:
	jmp END_OF_BIT_M

	; Port F bit clear
PORTF_L:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Flear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTF_L1
	in WREG_B, PINF
	andi WREG_B, 0b11111110
	out PORTF, WREG_B
PORTF_L1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTF_L2
	in WREG_B, PINF
	andi WREG_B, 0b11111101
	out PORTF, WREG_B
PORTF_L2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTF_L3
	in WREG_B, PINF
	andi WREG_B, 0b11111011
	out PORTF, WREG_B
PORTF_L3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTF_L4
	in WREG_B, PINF
	andi WREG_B, 0b11110111
	out PORTF, WREG_B
PORTF_L4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTF_L5
	in WREG_B, PINF
	andi WREG_B, 0b11101111
	out PORTF, WREG_B
PORTF_L5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTF_L6
	in WREG_B, PINF
	andi WREG_B, 0b11011111
	out PORTF, WREG_B
PORTF_L6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTF_L7
	in WREG_B, PINF
	andi WREG_B, 0b10111111
	out PORTF, WREG_B
PORTF_L7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTF_L8
	in WREG_B, PINF
	andi WREG_B, 0b01111111
	out PORTF, WREG_B
PORTF_L8:
	jmp END_OF_BIT_M

	; Port H bit clear
PORTH_L:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Clear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTH_L1
	lds WREG_B, PINH
	andi WREG_B, 0b11111110
	sts PORTH, WREG_B
PORTH_L1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTH_L2
	lds WREG_B, PINH
	andi WREG_B, 0b11111101
	sts PORTH, WREG_B
PORTH_L2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTH_L3
	lds WREG_B, PINH
	andi WREG_B, 0b11111011
	sts PORTH, WREG_B
PORTH_L3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTH_L4
	lds WREG_B, PINH
	andi WREG_B, 0b11110111
	sts PORTH, WREG_B
PORTH_L4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTH_L5
	lds WREG_B, PINH
	andi WREG_B, 0b11101111
	sts PORTH, WREG_B
PORTH_L5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTH_L6
	lds WREG_B, PINH
	andi WREG_B, 0b11011111
	sts PORTH, WREG_B
PORTH_L6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTH_L7
	lds WREG_B, PINH
	andi WREG_B, 0b10111111
	sts PORTH, WREG_B
PORTH_L7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTH_L8
	lds WREG_B, PINH
	andi WREG_B, 0b01111111
	sts PORTH, WREG_B
PORTH_L8:
	jmp END_OF_BIT_M

	; Port J bit clear
PORTJ_L:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Clear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTJ_L1
	lds WREG_B, PINJ
	andi WREG_B, 0b11111110
	sts PORTJ, WREG_B
PORTJ_L1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTJ_L2
	lds WREG_B, PINJ
	andi WREG_B, 0b11111101
	sts PORTJ, WREG_B
PORTJ_L2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTJ_L3
	lds WREG_B, PINJ
	andi WREG_B, 0b11111011
	sts PORTJ, WREG_B
PORTJ_L3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTJ_L4
	lds WREG_B, PINJ
	andi WREG_B, 0b11110111
	sts PORTJ, WREG_B
PORTJ_L4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTJ_L5
	lds WREG_B, PINJ
	andi WREG_B, 0b11101111
	sts PORTJ, WREG_B
PORTJ_L5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTJ_L6
	lds WREG_B, PINJ
	andi WREG_B, 0b11011111
	sts PORTJ, WREG_B
PORTJ_L6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTJ_L7
	lds WREG_B, PINJ
	andi WREG_B, 0b10111111
	sts PORTJ, WREG_B
PORTJ_L7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTJ_L8
	lds WREG_B, PINJ
	andi WREG_B, 0b01111111
	sts PORTJ, WREG_B
PORTJ_L8:
	jmp END_OF_BIT_M

	; Port K bit clear
PORTK_L:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Clear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTK_L1
	lds WREG_B, PINK
	andi WREG_B, 0b11111110
	sts PORTK, WREG_B
PORTK_L1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTK_L2
	lds WREG_B, PINK
	andi WREG_B, 0b11111101
	sts PORTK, WREG_B
PORTK_L2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTK_L3
	lds WREG_B, PINK
	andi WREG_B, 0b11111011
	sts PORTK, WREG_B
PORTK_L3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTK_L4
	lds WREG_B, PINK
	andi WREG_B, 0b11110111
	sts PORTK, WREG_B
PORTK_L4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTK_L5
	lds WREG_B, PINK
	andi WREG_B, 0b11101111
	sts PORTK, WREG_B
PORTK_L5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTK_L6
	lds WREG_B, PINK
	andi WREG_B, 0b11011111
	sts PORTK, WREG_B
PORTK_L6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTK_L7
	lds WREG_B, PINK
	andi WREG_B, 0b10111111
	sts PORTK, WREG_B
PORTK_L7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTK_L8
	lds WREG_B, PINK
	andi WREG_B, 0b01111111
	sts PORTK, WREG_B
PORTK_L8:
	jmp END_OF_BIT_M

	; Port L bit clear
PORTL_L:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Clear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTL_L1
	lds WREG_B, PINL
	andi WREG_B, 0b11111110
	sts PORTL, WREG_B
PORTL_L1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTL_L2
	lds WREG_B, PINL
	andi WREG_B, 0b11111101
	sts PORTL, WREG_B
PORTL_L2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTL_L3
	lds WREG_B, PINL
	andi WREG_B, 0b11111011
	sts PORTL, WREG_B
PORTL_L3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTL_L4
	lds WREG_B, PINL
	andi WREG_B, 0b11110111
	sts PORTL, WREG_B
PORTL_L4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTL_L5
	lds WREG_B, PINL
	andi WREG_B, 0b11101111
	sts PORTL, WREG_B
PORTL_L5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTL_L6
	lds WREG_B, PINL
	andi WREG_B, 0b11011111
	sts PORTL, WREG_B
PORTL_L6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTL_L7
	lds WREG_B, PINL
	andi WREG_B, 0b10111111
	sts PORTL, WREG_B
PORTL_L7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTL_L8
	lds WREG_B, PINL
	andi WREG_B, 0b01111111
	sts PORTL, WREG_B
PORTL_L8:
	jmp END_OF_BIT_M

	; Port C
	; Set bit stuff
PORTC_H:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Clear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTC_H1
	in WREG_B, PINC
	ori WREG_B, 0b00000001
	out PORTC, WREG_B
PORTC_H1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTC_H2
	in WREG_B, PINC
	ori WREG_B, 0b00000010
	out PORTC, WREG_B
PORTC_H2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTC_H3
	in WREG_B, PINC
	ori WREG_B, 0b00000100
	out PORTC, WREG_B
PORTC_H3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTC_H4
	in WREG_B, PINC
	ori WREG_B, 0b00001000
	out PORTC, WREG_B
PORTC_H4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTC_H5
	in WREG_B, PINC
	ori WREG_B, 0b00010000
	out PORTC, WREG_B
PORTC_H5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTC_H6
	in WREG_B, PINC
	ori WREG_B, 0b00100000
	out PORTC, WREG_B
PORTC_H6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTC_H7
	in WREG_B, PINC
	ori WREG_B, 0b01000000
	out PORTC, WREG_B
PORTC_H7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTC_H8
	in WREG_B, PINC
	ori WREG_B, 0b10000000
	out PORTC, WREG_B
PORTC_H8:
	jmp END_OF_BIT_M

	
	; Port F
PORTF_H:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Flear off non used stuff
	cpi WREG_A, 0b00000000	; Pin 0
	brne PORTF_H1
	in WREG_B, PINF
	ori WREG_B, 0b00000001
	out PORTF, WREG_B
PORTF_H1:
	cpi WREG_A, 0b00000001	; Pin 1
	brne PORTF_H2
	in WREG_B, PINF
	ori WREG_B, 0b00000010
	out PORTF, WREG_B
PORTF_H2:			
	cpi WREG_A, 0b00000010	; Pin 2
	brne PORTF_H3
	in WREG_B, PINF
	ori WREG_B, 0b00000100
	out PORTF, WREG_B
PORTF_H3:			
	cpi WREG_A, 0b00000011	; Pin 3
	brne PORTF_H4
	in WREG_B, PINF
	ori WREG_B, 0b00001000
	out PORTF, WREG_B
PORTF_H4:	
	cpi WREG_A, 0b00000100	; Pin 4
	brne PORTF_H5
	in WREG_B, PINF
	ori WREG_B, 0b00010000
	out PORTF, WREG_B
PORTF_H5:
	cpi WREG_A, 0b00000101	; Pin 5 
	brne PORTF_H6
	in WREG_B, PINF
	ori WREG_B, 0b00100000
	out PORTF, WREG_B
PORTF_H6:		
	cpi WREG_A, 0b00000110	; Pin 6 
	brne PORTF_H7
	in WREG_B, PINF
	ori WREG_B, 0b01000000
	out PORTF, WREG_B
PORTF_H7:
	cpi WREG_A, 0b00000111	; Pin 7 
	brne PORTF_H8
	in WREG_B, PINF
	ori WREG_B, 0b10000000
	out PORTF, WREG_B
PORTF_H8:
	jmp END_OF_BIT_M
	

	; Port H
PORTH_H:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Hlear off non used stuff
	cpi WREG_A, 0b00000000	; Plds 0
	brne PORTH_H1
	lds WREG_B, PINH
	ori WREG_B, 0b00000001
	sts PORTH, WREG_B
PORTH_H1:
	cpi WREG_A, 0b00000001	; Plds 1
	brne PORTH_H2
	lds WREG_B, PINH
	ori WREG_B, 0b00000010
	sts PORTH, WREG_B
PORTH_H2:			
	cpi WREG_A, 0b00000010	; Plds 2
	brne PORTH_H3
	lds WREG_B, PINH
	ori WREG_B, 0b00000100
	sts PORTH, WREG_B
PORTH_H3:			
	cpi WREG_A, 0b00000011	; Plds 3
	brne PORTH_H4
	lds WREG_B, PINH
	ori WREG_B, 0b00001000
	sts PORTH, WREG_B
PORTH_H4:	
	cpi WREG_A, 0b00000100	; Plds 4
	brne PORTH_H5
	lds WREG_B, PINH
	ori WREG_B, 0b00010000
	sts PORTH, WREG_B
PORTH_H5:
	cpi WREG_A, 0b00000101	; Plds 5 
	brne PORTH_H6
	lds WREG_B, PINH
	ori WREG_B, 0b00100000
	sts PORTH, WREG_B
PORTH_H6:		
	cpi WREG_A, 0b00000110	; Plds 6 
	brne PORTH_H7
	lds WREG_B, PINH
	ori WREG_B, 0b01000000
	sts PORTH, WREG_B
PORTH_H7:
	cpi WREG_A, 0b00000111	; Plds 7 
	brne PORTH_H8
	lds WREG_B, PINH
	ori WREG_B, 0b10000000
	sts PORTH, WREG_B
PORTH_H8:
	jmp END_OF_BIT_M

	; Port J
PORTJ_H:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Hlear off non used stuff
	cpi WREG_A, 0b00000000	; Plds 0
	brne PORTJ_H1
	lds WREG_B, PINJ
	ori WREG_B, 0b00000001
	sts PORTJ, WREG_B
PORTJ_H1:
	cpi WREG_A, 0b00000001	; Plds 1
	brne PORTJ_H2
	lds WREG_B, PINJ
	ori WREG_B, 0b00000010
	sts PORTJ, WREG_B
PORTJ_H2:			
	cpi WREG_A, 0b00000010	; Plds 2
	brne PORTJ_H3
	lds WREG_B, PINJ
	ori WREG_B, 0b00000100
	sts PORTJ, WREG_B
PORTJ_H3:			
	cpi WREG_A, 0b00000011	; Plds 3
	brne PORTJ_H4
	lds WREG_B, PINJ
	ori WREG_B, 0b00001000
	sts PORTJ, WREG_B
PORTJ_H4:	
	cpi WREG_A, 0b00000100	; Plds 4
	brne PORTJ_H5
	lds WREG_B, PINJ
	ori WREG_B, 0b00010000
	sts PORTJ, WREG_B
PORTJ_H5:
	cpi WREG_A, 0b00000101	; Plds 5 
	brne PORTJ_H6
	lds WREG_B, PINJ
	ori WREG_B, 0b00100000
	sts PORTJ, WREG_B
PORTJ_H6:		
	cpi WREG_A, 0b00000110	; Plds 6 
	brne PORTJ_H7
	lds WREG_B, PINJ
	ori WREG_B, 0b01000000
	sts PORTJ, WREG_B
PORTJ_H7:
	cpi WREG_A, 0b00000111	; Plds 7 
	brne PORTJ_H8
	lds WREG_B, PINJ
	ori WREG_B, 0b10000000
	sts PORTJ, WREG_B
PORTJ_H8:
	jmp END_OF_BIT_M

	; Port K
PORTK_H:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Hlear off non used stuff
	cpi WREG_A, 0b00000000	; Plds 0
	brne PORTK_H1
	lds WREG_B, PINK
	ori WREG_B, 0b00000001
	sts PORTK, WREG_B
PORTK_H1:
	cpi WREG_A, 0b00000001	; Plds 1
	brne PORTK_H2
	lds WREG_B, PINK
	ori WREG_B, 0b00000010
	sts PORTK, WREG_B
PORTK_H2:			
	cpi WREG_A, 0b00000010	; Plds 2
	brne PORTK_H3
	lds WREG_B, PINK
	ori WREG_B, 0b00000100
	sts PORTK, WREG_B
PORTK_H3:			
	cpi WREG_A, 0b00000011	; Plds 3
	brne PORTK_H4
	lds WREG_B, PINK
	ori WREG_B, 0b00001000
	sts PORTK, WREG_B
PORTK_H4:	
	cpi WREG_A, 0b00000100	; Plds 4
	brne PORTK_H5
	lds WREG_B, PINK
	ori WREG_B, 0b00010000
	sts PORTK, WREG_B
PORTK_H5:
	cpi WREG_A, 0b00000101	; Plds 5 
	brne PORTK_H6
	lds WREG_B, PINK
	ori WREG_B, 0b00100000
	sts PORTK, WREG_B
PORTK_H6:		
	cpi WREG_A, 0b00000110	; Plds 6 
	brne PORTK_H7
	lds WREG_B, PINK
	ori WREG_B, 0b01000000
	sts PORTK, WREG_B
PORTK_H7:
	cpi WREG_A, 0b00000111	; Plds 7 
	brne PORTK_H8
	lds WREG_B, PINK
	ori WREG_B, 0b10000000
	sts PORTK, WREG_B
PORTK_H8:
	jmp END_OF_BIT_M

	; Port L
PORTL_H:
	mov WREG_A, PIN_MAN
	andi WREG_A, 0b00000111	; Hlear off non used stuff
	cpi WREG_A, 0b00000000	; Plds 0
	brne PORTL_H1
	lds WREG_B, PINL
	ori WREG_B, 0b00000001
	sts PORTL, WREG_B
PORTL_H1:
	cpi WREG_A, 0b00000001	; Plds 1
	brne PORTL_H2
	lds WREG_B, PINL
	ori WREG_B, 0b00000010
	sts PORTL, WREG_B
PORTL_H2:			
	cpi WREG_A, 0b00000010	; Plds 2
	brne PORTL_H3
	lds WREG_B, PINL
	ori WREG_B, 0b00000100
	sts PORTL, WREG_B
PORTL_H3:			
	cpi WREG_A, 0b00000011	; Plds 3
	brne PORTL_H4
	lds WREG_B, PINL
	ori WREG_B, 0b00001000
	sts PORTL, WREG_B
PORTL_H4:	
	cpi WREG_A, 0b00000100	; Plds 4
	brne PORTL_H5
	lds WREG_B, PINL
	ori WREG_B, 0b00010000
	sts PORTL, WREG_B
PORTL_H5:
	cpi WREG_A, 0b00000101	; Plds 5 
	brne PORTL_H6
	lds WREG_B, PINL
	ori WREG_B, 0b00100000
	sts PORTL, WREG_B
PORTL_H6:		
	cpi WREG_A, 0b00000110	; Plds 6 
	brne PORTL_H7
	lds WREG_B, PINL
	ori WREG_B, 0b01000000
	sts PORTL, WREG_B
PORTL_H7:
	cpi WREG_A, 0b00000111	; Plds 7 
	brne PORTL_H8
	lds WREG_B, PINL
	ori WREG_B, 0b10000000
	sts PORTL, WREG_B
PORTL_H8:
	jmp END_OF_BIT_M

END_OF_BIT_M:

	pop WREG_B
	pop WREG_A

	reti

;
; End of Int2
;*****

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

The table approach would be much smaller, much faster, and adaptable to other processor models.

There are some macros in an AVR app note (AVR100?) that you can use to automagically take care of IN/OUT vs LDS/STS.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

The attached file is an example of what I had in mind. Please forgive the lack of proper documentation and sloppy coding. It was just thrown together.

[edit]The attachment was removed and replaced by a later post below.

Last Edited: Fri. Jan 9, 2009 - 03:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This post is not longer relevant.

Last Edited: Fri. Jan 9, 2009 - 03:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mike,
Cannot open your attachment but thanks for the effort.

Lee,
Do you have a sample of how to implement a table? Just some info on how it is done or some old code would help.

Thanks all,

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

It was a simple .txt format file. Here it is the long way.

.include "m640def.inc"
.include "macros.inc"		; AVR001
;
; you can get the AVR001 macros from the ATMEL AVR application notes web page

; an automated change of all the AVR PORT include addresses for use with LD and ST instructions
;	a macro wouldn't work when called from inside a .db or .equ
;	this could be moved to its own include file
;
.if PORTA < 0x40
	.equ PRTA = PORTA + 0x20
.else
	.equ PRTA = PORTA
.endif
.if PORTB < 0x40
	.equ PRTB = PORTB + 0x20
.else
	.equ PRTB = PORTB
.endif
.if PORTB < 0x40
	.equ PRTC = PORTC + 0x20
.else
	.equ PRTC = PORTC
.endif
.if PORTD < 0x40
	.equ PRTD = PORTD + 0x20
.else
	.equ PRTD = PORTD
.endif
.if PORTE < 0x40
	.equ PRTE = PORTE + 0x20
.else
	.equ PRTE = PORTE
.endif
.if PORTF < 0x40
	.equ PRTF = PORTF + 0x20
.else
	.equ PRTF = PORTF
.endif
.if PORTG < 0x40
	.equ PRTG = PORTG + 0x20
.else
	.equ PRTG = PORTG
.endif
.if PORTH < 0x40
	.equ PRTH = PORTH + 0x20
.else
	.equ PRTH = PORTH
.endif
.if PORTJ < 0x40
	.equ PRTJ = PORTJ + 0x20
.else
	.equ PRTJ = PORTJ
.endif
.if PORTK < 0x40
	.equ PRTK = PORTK + 0x20
.else
	.equ PRTK = PORTK
.endif
.if PORTL < 0x40
	.equ PRTL = PORTL + 0x20
.else
	.equ PRTL = PORTL
.endif

;
; r16 control bit map:
;	SxPPPBBB (MSb to LSb)
;
;		S = on/off, x = reserved (not used), PPP = AVR port, BBB = AVR port pin

.def control    = r16
.def bit_weight = r17
.def temp       = r18

.equ S = 7

; program start
;
.cseg
.org 0

  jmp reset
; any interrupt vectors you want to use go here

; define the PPP control address (0 through 7) and what physical AVR port they map to
;		these are only the actual I/O addresses for use with LD and ST instructions
;	skip port G because it has 2 missing bits
port_map:
	.dw PRTA, PRTB, PRTC, PRTD, PRTE, PRTF, PRTH, PRTJ

reset:
  ldi r16,high(RAMEND)		; Main program start
  STORE SPH,r16		; Set Stack Pointer to top of RAM
  ldi r16,low(RAMEND)
  STORE SPL,r16

; initialize: turn on all the output port pins used in port_map (above)
  ldi temp, 0xFF
  STORE DDRA, temp
  STORE DDRB, temp
  STORE DDRC, temp
  STORE DDRD, temp
  STORE DDRE, temp
  STORE DDRF, temp
  STORE DDRH, temp
  STORE DDRJ, temp

  ldi control, 0b10001101	; set bit 5 on PORT B (bits numbered from 0 to 7)

  call doit

  ldi control, 0bwww.avrfreaks.nett 5 on PORT B (bits numbered from 0 to 7)

  call doit

done:
  jmp done			; end of demo code, there is nothing else to do

; this is where all the work is done
;
doit:

; this code gives the equivalent result of: { bit_weight = (1 << ( control & 0x3)) }
;
  ldi bit_weight, 0x01
  sbrc control, 1
    ldi bit_weight, 0x04
  sbrc control, 0
    lsl bit_weight
  sbrc control, 2
    swap bit_weight

; Lookup port map using PPP as an index into port_map and get the AVR port address
;
  ldi zh, high(port_map * 2)
  ldi zl, low(port_map * 2)
  mov temp, control	; make a temp copy of the control value
  rol temp		; right justify the PPP bits so they are in the least significant position
  swap temp
  andi temp, 0x07	; limit the PPP value to 3 bits or the table lookup could return garbage
  lsl temp		; * 2 for 16 bit word size port_map table entries
  add zl, temp		; add the right adjusted 3 bit PPP * 2 to the Z register port_map table pointer
  clr temp
  adc zh, temp		; now the Z register pair is pointing at the correct port_map table entry
  lpm yl, z+		; get the port_map table value that points to the selected AVR PORT
  lpm yh, z

; clear or set the selected AVR PORT bit according to the control byte S bit
;
  ld temp, y		; get the current port value
  sbrc control, S		; skip if S = 0 (off) - otherwise we need to....
    or temp, bit_weight		; ...turn the port bit ON
  com bit_weight	; complement the bit weight result (may not actually be used)
  sbrs control, S		; skip if S = 1 (on) - otherwise we need to....
    and temp, bit_weight		; ...turn the port bit OFF
  st y, temp		; write the new port value (it might even be the same as the old port value)
  ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This time the correction for the text corruption bug is:
ldi control, 0b00001101 ; clear bit 5 on PORT B (bits numbered from 0 to 7)

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

Mike,

Thanks. I will go through your code and make sure that I understand what it does.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

Quote:

Do you have a sample of how to implement a table?

I was going to post a fragment earlier, but Mike described the whole thing. The actual implementation approach will depend on your design criteria--is this going to be done 50000 times a second so we have a time budget of a few us to work with? Then I'd build an SRAM table for speed of access. Is it going to be done every few ms when a packet comes in? Then I'd probably use an EEPROM or flash table and not worry about a few extra cycles each repetition.

A short version using an SRAM table is below, and the generated code. The reason I said "faster" is that it is deterministic. If gung-ho high-speed is needed then the table can be optimized like crazy in ASM to do the whole shebang in a few cycles by dedicating an index register pair to the table. Even better, put the table on a mod 256 boundary and then "theport" becomes XL or ZL.

// Use MikeB's research on the port addresses, A-x
#if 0 
ppp = 0 decimal maps to 0x22 (the absolute I/O address of PORTA)
ppp = 1 decimal maps to 0x25 (PORTB)
ppp = 2 decimal maps to 0x28 (PORTC)
ppp = 3 decimal maps to 0x2B (PORTD)
ppp = 4 decimal maps to 0x2E (PORTE)
ppp = 5 decimal maps to 0x31 (PORTF)
ppp = 6 decimal maps to 0x102 (PORTH)
ppp = 7 decimal maps to 0x105 (PORTJ)
#endif
unsigned char * portaddr[] =
{
(unsigned char *) 0x22,
(unsigned char *) 0x25,
(unsigned char *) 0x28,
(unsigned char *) 0x2b,
(unsigned char *) 0x2e,
(unsigned char *) 0x31,
(unsigned char *) 0x102,
(unsigned char *) 0x105
};

void setbit (unsigned char mask)
{
// mask = 0bsxpppbbb  for 8 ports.  Easily extensible to 16.
unsigned char theport;
unsigned char thebit;
unsigned char themask;  // working copy
unsigned char theSREG;

themask = mask;

theport = (themask & 0x38) >> 3;
thebit = themask & 0x07;
     
// About to do a read-modify-write critical section.
theSREG = SREG;
#asm ("cli")
if (themask & 0x80)
    {
    // Set bit
    *(portaddr[theport]) |= (1 << thebit);
    }
else
    {
    // Clear bit
    *(portaddr[theport]) &= ~(1 << thebit);
    }

SREG = theSREG;

}
                 ;      36 void setbit (unsigned char mask)
                 ;      37 {
                 
                 	.CSEG
                 _setbit:
                 ;      38 // mask = 0bsxpppbbb  for 8 ports.  Easily extensible to 16.
                 ;      39 unsigned char theport;
                 ;      40 unsigned char thebit;
                 ;      41 unsigned char themask;  // working copy
                 ;      42 unsigned char theSREG;
                 ;      43 
                 ;      44 themask = mask;
00005d d045      	RCALL __SAVELOCR4
                 ;	mask -> Y+4
                 ;	theport -> R17
                 ;	thebit -> R16
                 ;	themask -> R19
                 ;	theSREG -> R18
00005e 813c      	LDD  R19,Y+4
                 ;      45 
                 ;      46 theport = (themask & 0x38) >> 3;
00005f 2fe3      	MOV  R30,R19
000060 73e8      	ANDI R30,LOW(0x38)
000061 95e6      	LSR  R30
000062 95e6      	LSR  R30
000063 95e6      	LSR  R30
000064 2f1e      	MOV  R17,R30
                 ;      47 thebit = themask & 0x07;
000065 2fe3      	MOV  R30,R19
000066 70e7      	ANDI R30,LOW(0x7)
000067 2f0e      	MOV  R16,R30
                 ;      48 
                 ;      49 // About to do a read-modify-write critical section.
                 ;      50 theSREG = SREG;
000068 b72f      	IN   R18,63
                 ;      51 #asm ("cli")
000069 94f8      	cli
                 ;      52 if (themask & 0x80)
00006a ff37      	SBRS R19,7
00006b c010      	RJMP _0x4
                 ;      53     {
                 ;      54     // Set bit
                 ;      55     *(portaddr[theport]) |= (1 << thebit);
00006c 2fe1      	MOV  R30,R17
00006d e0a0      	LDI  R26,LOW(_portaddr)
00006e e0b2      	LDI  R27,HIGH(_portaddr)
00006f e0f0      	LDI  R31,0
000070 0fee      	LSL  R30
000071 1fff      	ROL  R31
000072 0fae      	ADD  R26,R30
000073 1fbf      	ADC  R27,R31
000074 d02a      	RCALL __GETW1P
000075 01bf      	MOVW R22,R30
000076 8010      	LD   R1,Z
000077 2fe0      	MOV  R30,R16
000078 e0a1      	LDI  R26,LOW(1)
000079 d01d      	RCALL __LSLB12
00007a 29e1      	OR   R30,R1
00007b c010      	RJMP _0xA
                 ;      56     }
                 ;      57 else
                 _0x4:
                 ;      58     {
                 ;      59     // Clear bit
                 ;      60     *(portaddr[theport]) &= ~(1 << thebit);
00007c 2fe1      	MOV  R30,R17
00007d e0a0      	LDI  R26,LOW(_portaddr)
00007e e0b2      	LDI  R27,HIGH(_portaddr)
00007f e0f0      	LDI  R31,0
000080 0fee      	LSL  R30
000081 1fff      	ROL  R31
000082 0fae      	ADD  R26,R30
000083 1fbf      	ADC  R27,R31
000084 d01a      	RCALL __GETW1P
000085 01bf      	MOVW R22,R30
000086 8010      	LD   R1,Z
000087 2fe0      	MOV  R30,R16
000088 e0a1      	LDI  R26,LOW(1)
000089 d00d      	RCALL __LSLB12
00008a 95e0      	COM  R30
00008b 21e1      	AND  R30,R1
                 _0xA:
00008c 01db      	MOVW R26,R22
00008d 93ec      	ST   X,R30
                 ;      61     }
                 ;      62 
                 ;      63 SREG = theSREG;
00008e bf2f      	OUT  0x3F,R18
                 ;      64 
                 ;      65 }
00008f d018      	RCALL __LOADLOCR4
000090 9625      	ADIW R28,5
000091 9508      	RET

Then you could tweak it. This is probably shorter but may not be faster.

unsigned char theport;
unsigned char thebit;
unsigned char themask;  // working copy
unsigned char theSREG;
unsigned char *theaddr;

themask = mask;

theport = (themask & 0x38) >> 3;
thebit = themask & 0x07;


theaddr = portaddr[theport];

// About to do a read-modify-write critical section.
theSREG = SREG;
#asm ("cli")

// Reuse "theport" 'cause I know it is a register variable
theport = *theaddr; // fetch
// Reuse "thebit" 'cause I know it is a register variable
thebit = 1 << thebit;

if (themask & 0x80)
    {
    // Set bit
    theport |= thebit;
    }
else
    {
    // Clear bit
    theport &= ~thebit;
    }
    
*theaddr = theport; // Store

SREG = theSREG;

Yep, 10 words shorter. It might be faster, too. It is about 40 cycles + the bit shift + the function call and stack manipulation for the local variables. Probably a little less than 100 cycles as written. If I move the locals to dedicated global register variables then it would be faster/shorter.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Lee,
The changing of a port bit should be quick, but anything under 10uS should be OK. I am using a 640 to replace some i/o on a PC104 bus. Currently the code is executed in an int but I am worried about the time needed to change two bits. My understanding is that the PC104 standard is about 8Mhz. This is way above the 50Khz I can get with the routine I have written. I may look for away to remove this process from an int.

Thanks for the input.(NPI)

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

Of course questions of speed will depend on your AVR clock rate.

Where are you going to get these "commands"? How often will they appear?

If this is the only thing that your app is doing and the commands "appear" on an I/O port, then you just sit in a loop looking for that to change. With a dedicated table and dedicated SRAM and table location on mod 256 and dedicated register variables, the limiting factor would be the port shift and the bit shift.

Now, you can sidestep the port bit shift by making a huge sparsely-populated table. Then you just have the mask. That problem is now solved.

There was a thread a while back on the fastest way to do (1 << bit). I don't remember the final result but I think it is constant-time.

If you don't have any other bit manipulation in ISRs then you can skip the critical-section stuff.

So then we are at maybe 20 cycles?

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Lee,
It is not the only thing this micro will be doing, but it is the most time consuming. The port that has the data is shared with other parts of the app, so I cannot rely on a change on the port always being a "port bit command". Its clocked at 16Mhz so I think I am at my limit for pratical purposes.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.include "m640def.inc"
.include "macros.inc"		; AVR001

;
; r16 control bit map:
;	xPPPBBBS (MSb to LSb)
;
;		x = reserved (not used), PPP = AVR port, BBB = AVR port pin, S = on/off

; you MUST create all 128 (64 pins) port off/on table addresses
;	failure to do so will cause a program crash when the ijmp executes an incorrect table entry
;	I don't think the .dw separate line syntax works as show below, but this is clearer
;
port_map:
.dw	bit00_off,	; address 1
	bit00_on,	; address 2
	bit01_off,	; address 3
	bit01_on,	; address 4
….			; addresses 5 through 126
	bit63_off,	; address 127
	bit63_on	; address 128

; Lookup port map using PPP as an index into port_map and get the AVR port address
;
    ldi zh, high(port_map * 2)
    ldi zl, low(port_map * 2)
    andi control, 0x7F	; always keep the table in bounds or you can crash
    lsl control		; * 2 for the 16 bit port_map table entries (this only works with a 7 bit control)
    add zl, temp		; add the right adjusted 7 bit control * 2 to the Z register port_map table pointer
    clr temp
    adc zh, temp		; now the Z register pair is pointing at the correct port_map table entry
    lpm yl, z+		; get the port_map table value that points to the port code
    lpm yh, z
    movw zh:zl, yh:yl	; ijmp uses the z registers and the pointer is in the y registers, until this move
    ijmp			; jump to the port off or on code

;
;
bit00_off:
    CLRB PORTA, 0	; these macros will automatically generate the most efficient code
    jmp done
bit00_on:
    SETB PORTA, 0
    jmp done
bit01_off:
    CLRB PORTA, 1
    jmp done
bit01_on:
    SETB PORTA, 1
    jmp done

….			; bit02 through bit62

bit63_off:
    CLRB PORTJ, 7
    jmp done
bit63_on:
    SETB PORTJ, 7
    jmp done

done:
; this does offer the advantage of being able to skip any AVR port pins you do not want to use or to have
;	any custom order of port pin arrangements:
;	bit00 can be port D bit 7, then bit01 can be port G bit 2, etc.
;	this could be a real advantage for laying out a PC board and it also allows you to selectively skip
;	port pins that you might want to use for a USART or whatever
;

This is an unoptimized example of in-line coding through a jump table. It takes lots of FLASH, but it can speed up execution and has some other possible advantages. You do have to be very careful creating the tables and bit routines. Coding mwww.avrfreaks.netbly cause instant crashes in the executable code.

The CLRB and SETB macros will automatically take advantage of the 0x00 to 0x1F, 0x20 to 0x3F port address range instruction optimizations. This does mean some port bits will change more quickly than others. If a deterministic execution speed is required, you could create a special macro that would not use the port address range instruction optimizations. This code is able to use the m640def.inc file PORT values without modification.

There is of course more room for speed optimization, but nothing to reduce FLASH usage. Reordering the control code bits just makes the table function faster. With a few more instructions you could use the original control code bit format.

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

The text corruption bug again. The correction is:
Coding mistakes here will probably cause instant crashes in the executable code.

Sorry, it looks like I didn't clean up all the comments from the borrowed code.

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

Mike,

Thanks again. I will chew on this solution for a while. At least I have a couple of options to look at that will improve my speed.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA

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

DUH! It can be fun just whipping up code off the top of your head. However, I wasn't careful enough. I forgot to change some temp registers into control registers :oops:. I fixed some of the comments too.

; Lookup port map using control as an index into port_map and get the address of the AVR port code
;
    ldi zh, high(port_map * 2)
    ldi zl, low(port_map * 2)
    andi control, 0x7F	;  always constrain the table index to be in bounds or you will crash
    lsl control		; * 2 for the 16 bit port_map table entries (this only works with up to a 7 bit control)
    add zl, control	;  add the right adjusted 7 bit control * 2 to the Z register port_map table pointer
    clr control
    adc zh, control	; now the Z register pair is pointing at the correct port_map table entry
    lpm yl, z+		; get the port_map table value that points to the port code executable address
    lpm yh, z
    movw zh:zl, yh:yl	; ijmp uses the z registers and the address is in the y registers, until this move
    ijmp			; jump to the port off or on code

You could get two CPU execution cycles back by reserving a register pair with the initial (port_map * 2) address saved as a constant value, then just use one MOVW instead of two LDI instructions. Then reserve a single register as a zero value constant (no CLR control would be needed and ADC zh, zero would be used instead). However, be very careful with these types of optimizations since anything that overwrites these reserved register constants will mess up the IJMP z pointer address. Messing up the IJMP address is at best a very bad bug (if it lands somewhere else in the correct word boundary of the code pointed to by port_map) and at worst its a total crash and burn. You can do it safely only if you always ensure those three registers with the constant values are never changed. That takes the above 16 instruction cycle table directed jump down to 14 instruction cycles.

The already optimized SETB and CLRB would be 1 instruction cycle (port address 0x00 to 0x1F), 3 instruction cycles (port address 0x20 to 0x3F) or 5 instruction cycles (port address 0x40 and higher). Since the Atmega640 doesn't have any port addresses in the 0x20 to 0x3F range, it would actually be 1 instruction cycle (ports A through G) or 5 instruction cycles (ports H, J, K and L). Finally using RJMP instead of JMP at the end (assuming its within range) would save another cycle. That would be 17 or 21 total optimized instruction cycles (YMMV).

I almost forgot, if you force the 256 byte port_map table to start at a perfect 256 byte boundary, you could get rid of the ADC instruction, zero constant and save another instruction cycle.

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

Last one, I just noticed the ANDI followed by the LSL on a 7 bit value makes the ANDI unnecessary (only because its a 7 bit value in an 8 bit register). It looks like you could knock this down to 15 or 19 instruction cycles depending on which port is used.

One word of advice. Any optimizations should be clearly documented in an easy to find place in the source code (like at the beginning). Especially any optimizations that makes the program function fragile with respect to any dependencies. This extra documentation will reveal these limitations in the optimized code and make it much easier to maintain or modify in the future.

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

Thanks Mike.

I will not have time to put some of this together untill later this week,
but I will post what I end up with.

A

AVR Studio 4 Ver. 4.18 684
avr-gcc Ver. 4.3.0
ISIS 7
ELECTRA